2 * AT-SPI - Assistive Technology Service Provider Interface
3 * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
5 * Copyright 2001 Sun Microsystems Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
24 #include "../cspi/spi-private.h"
28 #define BOUNDS_CONTAIN_X(b, p) (((p)>=(b)->x) && ((p)<=((b)->x + (b)->width))\
29 && ((b)->width > 0) && ((b)->height > 0))
31 #define BOUNDS_CONTAIN_Y(b, p) (((p)>=(b)->y) && ((p)<=((b)->y + (b)->height))\
32 && ((b)->width > 0) && ((b)->height > 0))
34 #define CHUNK_BOUNDS_BEFORE_START(c, i) ((i) && ((c)->clip_bounds.x < \
37 #define CHUNK_BOUNDS_END_BEFORE_START(c, i) ((i) && \
38 (((c)->clip_bounds.x + \
39 (c)->clip_bounds.width) < \
42 #define CHUNK_BOUNDS_AFTER_END(c, i) ((i) && ((c)->clip_bounds.x >= \
43 ((i)->clip_bounds.x + \
44 (i)->clip_bounds.width)))
46 #define CHUNK_BOUNDS_SPANS_END(c, i) ((i) && (((c)->clip_bounds.x + \
47 (c)->clip_bounds.width) >= \
48 ((i)->clip_bounds.x + \
49 (i)->clip_bounds.width)))
51 //#define CHUNK_BOUNDS_WITHIN(c, i) ((i) && ((c)->clip_bounds.x >= \
52 // (i)->text_bounds.x) && \
53 // (((c)->clip_bounds.x + (c)->clip_bounds.width) \
54 // <= ((i)->text_bounds.x + (i)->text_bounds.width)))
56 static void report_screen_review_line (const AccessibleEvent *event, void *user_data);
58 static gint n_elements_traversed = 0;
59 static AccessibleEventListener *mouseclick_listener;
61 typedef struct _BoundaryRect {
66 AccessibleRole role; /* role of last clipping element */
71 typedef struct _TextChunk {
73 AccessibleText *source;
76 BoundaryRect clip_bounds;
77 BoundaryRect text_bounds;
78 BoundaryRect start_char_bounds;
79 BoundaryRect end_char_bounds;
82 typedef struct _ScreenReviewBuffer { /* TODO: implement */
86 static gboolean isJava = FALSE;
89 main (int argc, char **argv)
98 Accessible *application;
103 mouseclick_listener = SPI_createAccessibleEventListener (
104 report_screen_review_line, NULL);
106 SPI_registerGlobalEventListener (mouseclick_listener,
107 "Gtk:GtkWidget:button-press-event");
108 #define JAVA_TEST_HACK
109 #ifdef JAVA_TEST_HACK
110 SPI_registerGlobalEventListener (mouseclick_listener,
111 "object:text-caret-moved");
116 putenv ("AT_BRIDGE_SHUTDOWN=1");
119 * TODO: Add a key event listener that calls test_exit, to
120 * deregister and cleanup appropriately.
126 static inline gboolean
127 bounds_contain_y (BoundaryRect *bounds, int y)
129 return (y > bounds->y && y <= (bounds->y + bounds->height)
130 && bounds->width && bounds->height);
134 static inline gboolean
135 chunk_bounds_within (TextChunk *chunk, TextChunk *test_chunk)
137 int x1, x2, tx1, tx2;
140 x1 = chunk->clip_bounds.x;
141 x2 = x1 + chunk->clip_bounds.width;
142 tx1 = test_chunk->clip_bounds.x;
143 tx2 = tx1 + test_chunk->clip_bounds.width;
144 gtx1 = (chunk->clip_bounds.x >= test_chunk->clip_bounds.x);
145 ltx2 = (chunk->clip_bounds.x + chunk->clip_bounds.width
146 <= test_chunk->clip_bounds.x + test_chunk->clip_bounds.width);
148 // fprintf (stderr, "testing BOUNDS %d-%d WITHIN %d-%d: %s\n",
149 // x1, x2, tx1, tx2, ((gtx1 && ltx2) ? "T" : "F"));
154 #define CHUNK_BOUNDS_WITHIN(a, b) chunk_bounds_within(a, b)
156 static BoundaryRect **
157 clip_bounds_clone (BoundaryRect *bounds[])
160 BoundaryRect **bounds_clone;
161 bounds_clone = (BoundaryRect **)
162 g_new0 (gpointer, SPI_LAYER_LAST_DEFINED);
163 for (i = 0; i < SPI_LAYER_LAST_DEFINED; ++i) {
164 bounds_clone[i] = g_new0 (BoundaryRect, 1);
165 *bounds_clone[i] = *bounds[i];
171 boundary_clip (BoundaryRect *bounds, BoundaryRect *clipBounds)
173 int x2 = bounds->x + bounds->width;
174 int y2 = bounds->y + bounds->height;
176 fprintf (stderr, "bclip %d-%d, %d-%d; ",
178 clipBounds->x, clipBounds->x+clipBounds->width);
180 bounds->x = MAX (bounds->x, clipBounds->x);
181 bounds->y = MAX (bounds->y, clipBounds->y);
182 x2 = MIN (x2, clipBounds->x + clipBounds->width);
183 y2 = MIN (y2, clipBounds->y + clipBounds->height);
184 bounds->width = MAX (x2 - bounds->x, 0);
185 bounds->height = MAX (y2 - bounds->y, 0);
186 if (!bounds->width || !bounds->height)
187 bounds->isEmpty = TRUE;
189 fprintf (stderr, "%d-%d\n",
190 bounds->x, bounds->x+bounds->width);
195 boundary_xclip_head (BoundaryRect *bounds, BoundaryRect *clipBounds)
198 int cx2 = clipBounds->x + clipBounds->width;
199 if (cx2 < bounds->x) return;
200 x2 = bounds->x + bounds->width;
201 if (cx2 < x2) bounds->x = cx2;
202 bounds->width = MAX (0, x2 - cx2);
206 boundary_xclip_tail (BoundaryRect *bounds, BoundaryRect *clipBounds)
208 int x2 = bounds->x + bounds->width;
209 if (clipBounds->x > x2) return;
210 bounds->width = MAX (0, clipBounds->x - bounds->x);
214 text_chunk_copy (TextChunk *chunk)
216 TextChunk *copy = g_new0 (TextChunk, 1);
218 if (chunk->string) copy->string = g_strdup (chunk->string);
219 if (copy->source) AccessibleText_ref (copy->source);
224 text_chunk_tail_clip (TextChunk *bottom, TextChunk *top)
227 fprintf (stderr, "bottom %d-%d, top %d-%d;",
228 bottom->clip_bounds.x,
229 bottom->clip_bounds.x + bottom->clip_bounds.width,
231 top->clip_bounds.x + top->clip_bounds.width);
233 boundary_xclip_tail (&bottom->text_bounds, &top->clip_bounds);
234 boundary_xclip_tail (&bottom->clip_bounds, &top->clip_bounds);
235 bottom->text_bounds.isClipped = TRUE;
236 bottom->clip_bounds.isClipped = TRUE;
238 fprintf (stderr, "result %d-%d\n",
239 bottom->clip_bounds.x,
240 bottom->clip_bounds.x + bottom->clip_bounds.width);
245 text_chunk_head_clip (TextChunk *bottom, TextChunk *top)
248 fprintf (stderr, "bottom %d-%d, top %d-%d;",
249 bottom->clip_bounds.x,
250 bottom->clip_bounds.x + bottom->clip_bounds.width,
252 top->clip_bounds.x + top->clip_bounds.width);
254 boundary_xclip_head (&bottom->text_bounds, &top->clip_bounds);
255 boundary_xclip_head (&bottom->clip_bounds, &top->clip_bounds);
256 bottom->text_bounds.isClipped = TRUE;
257 bottom->clip_bounds.isClipped = TRUE;
259 fprintf (stderr, "result %d-%d\n",
260 bottom->clip_bounds.x,
261 bottom->clip_bounds.x + bottom->clip_bounds.width);
266 text_chunk_split_insert (GList *chunk_list, GList *iter, TextChunk *chunk)
268 TextChunk *iter_chunk = iter->data;
269 TextChunk *iter_copy = text_chunk_copy (iter_chunk);
270 /* TODO: FIXME something is wrong here */
272 fprintf (stderr, "***clip insert of %s into %s\n",
273 chunk->string ? chunk->string : "<null>",
274 iter_chunk->string ? iter_chunk->string : "<null>");
276 chunk_list = g_list_insert_before (chunk_list, iter, iter_copy);
277 text_chunk_tail_clip (iter_copy, chunk);
278 chunk_list = g_list_insert_before (chunk_list, iter, chunk);
279 text_chunk_head_clip (iter_chunk, chunk);
283 /* #define PRINT_CHUNK_DEBUG(a, b, c, d) print_chunk_debug(a, b, c, d) */
285 #define PRINT_CHUNK_DEBUG(a, b, c, d)
287 #ifdef PRINT_CHUNK_DEBUG
289 print_chunk_debug (TextChunk *chunk, char *opname, GList *prev, GList *next)
291 fprintf (stderr, "%sing chunk %s between %s and %s; %d-%d\n",
294 (prev ? ((TextChunk *) prev->data)->string : "<null>"),
295 (next ? ((TextChunk *) next->data)->string : "<null>"),
296 chunk->clip_bounds.x,
297 chunk->text_bounds.x + chunk->text_bounds.width);
302 text_chunk_list_head_clip (GList *text_chunk_list,
306 GList *target, *iter = next, *prev;
308 // if (chunk->string && strlen (chunk->string)) {
310 g_list_insert_before (text_chunk_list, next, chunk);
313 if (CHUNK_BOUNDS_SPANS_END (chunk, (TextChunk *)iter->data)) {
315 fprintf (stderr, "deleting %s\n",
316 ((TextChunk *)iter->data)->string);
321 g_list_delete_link (text_chunk_list, target);
323 if (!CHUNK_BOUNDS_END_BEFORE_START (chunk,
324 (TextChunk *)iter->data)) {
325 text_chunk_head_clip ((TextChunk *)iter->data,
329 !CHUNK_BOUNDS_AFTER_END (
331 (TextChunk *)prev->data)) {
332 text_chunk_tail_clip (
333 (TextChunk *)prev->data,
340 return text_chunk_list;
344 text_chunk_list_clip_and_insert (GList *text_chunk_list,
351 fprintf (stderr, "clip-and-insert for %s, between %s and %s\n",
353 (prev && ((TextChunk *)prev->data)->string ? ((TextChunk *)prev->data)->string : "<null>"),
354 (next && ((TextChunk *)next->data)->string ? ((TextChunk *)next->data)->string : "<null>"));
357 if (!prev && !next) { /* first element in, no clip needed */
358 // if (chunk->string && strlen (chunk->string)) {
360 g_list_append (text_chunk_list, chunk);
361 PRINT_CHUNK_DEBUG (chunk, "append",
365 } else { /* check for clip with prev */
366 /* if we split the prev */
368 CHUNK_BOUNDS_WITHIN (chunk, (TextChunk *) prev->data)) {
370 text_chunk_split_insert (
374 /* we split the 'next' element */
375 if (CHUNK_BOUNDS_WITHIN (chunk, (TextChunk *)next->data)) {
377 text_chunk_split_insert (text_chunk_list,
380 /* do an insert + head clip */
382 text_chunk_list_head_clip (
388 if (!CHUNK_BOUNDS_AFTER_END (chunk,
389 (TextChunk *) prev->data)) {
390 text_chunk_tail_clip (prev->data, chunk);
392 // if (chunk->string && strlen (chunk->string)) {
394 g_list_append (text_chunk_list, chunk);
398 return text_chunk_list;
402 text_chunk_list_insert_chunk (GList *text_chunk_list, TextChunk *chunk)
404 GList *iter = g_list_first (text_chunk_list);
405 TextChunk *iter_chunk = NULL;
407 if (iter) iter_chunk = (TextChunk *) iter->data;
408 /* if we're ahead of the current element */
411 text_chunk_list_clip_and_insert (text_chunk_list,
416 } else if (CHUNK_BOUNDS_BEFORE_START (chunk, iter_chunk)) {
418 text_chunk_list_clip_and_insert (text_chunk_list,
423 } else if (!iter->next ) {
425 text_chunk_list_clip_and_insert (text_chunk_list,
431 if (iter) iter = iter->next;
433 return text_chunk_list;
437 review_buffer_get_text_chunk (ScreenReviewBuffer *reviewBuffer,
438 Accessible *accessible, BoundaryRect *bounds,
439 int screen_x, int screen_y)
441 AccessibleText *text = NULL;
443 TextChunk *text_chunk;
444 BoundaryRect start_char_bounds, end_char_bounds;
450 role = Accessible_getRole (accessible);
451 text_chunk = g_new0 (TextChunk, 1);
452 text_chunk->clip_bounds = *bounds;
453 if (Accessible_isText (accessible)) {
454 text = Accessible_getText (accessible);
455 offset = AccessibleText_getOffsetAtPoint (text,
458 SPI_COORD_TYPE_SCREEN);
459 s = AccessibleText_getTextAtOffset (text, offset,
460 SPI_TEXT_BOUNDARY_LINE_START,
463 AccessibleText_getCharacterExtents (
465 &text_chunk->start_char_bounds.x,
466 &text_chunk->start_char_bounds.y,
467 &text_chunk->start_char_bounds.width,
468 &text_chunk->start_char_bounds.height,
469 SPI_COORD_TYPE_SCREEN);
471 fprintf (stderr, "%s: start char (%d) x, width %d %d;",
474 text_chunk->start_char_bounds.x,
475 text_chunk->start_char_bounds.width);
477 end--; /* XXX: bug workaround */
478 AccessibleText_getCharacterExtents (
480 &text_chunk->end_char_bounds.x,
481 &text_chunk->end_char_bounds.y,
482 &text_chunk->end_char_bounds.width,
483 &text_chunk->end_char_bounds.height,
484 SPI_COORD_TYPE_SCREEN);
486 fprintf (stderr, "end char (%d) x, width %d %d\n",
488 text_chunk->end_char_bounds.x,
489 text_chunk->end_char_bounds.width);
492 text_chunk->text_bounds.x = MIN (text_chunk->start_char_bounds.x,
493 text_chunk->end_char_bounds.x);
494 text_chunk->text_bounds.y = MIN (text_chunk->start_char_bounds.y,
495 text_chunk->end_char_bounds.y);
496 x2 = MAX (text_chunk->start_char_bounds.x +
497 text_chunk->start_char_bounds.width,
498 text_chunk->end_char_bounds.x +
499 text_chunk->end_char_bounds.width);
500 text_chunk->text_bounds.width = x2 - text_chunk->text_bounds.x;
501 y2 = MAX (text_chunk->start_char_bounds.y +
502 text_chunk->start_char_bounds.height,
503 text_chunk->end_char_bounds.y +
504 text_chunk->end_char_bounds.height);
505 text_chunk->text_bounds.height = y2 - text_chunk->text_bounds.y;
506 text_chunk->start_offset = start;
507 text_chunk->end_offset = end;
508 if ((role != SPI_ROLE_TABLE_CELL) /* XXX bug workaround */
509 && !bounds_contain_y (&text_chunk->text_bounds,
514 if (role == SPI_ROLE_PUSH_BUTTON ||
515 role == SPI_ROLE_CHECK_BOX ||
516 role == SPI_ROLE_MENU ||
517 role == SPI_ROLE_MENU_ITEM) { /* don't like this
518 special casing :-( */
519 s = Accessible_getName (accessible);
520 /* use name instead */
521 text_chunk->text_bounds = text_chunk->clip_bounds;
522 text_chunk->start_offset = 0;
523 text_chunk->end_offset = strlen (s);
526 if (s && strlen (s)) {
527 if (s[strlen(s)-1] == '\n') s[strlen(s)-1] = ' ';
528 /* XXX: if last char is newline, aren't its bounds wrong now? */
529 text_chunk->string = s;
530 text_chunk->source = text;
531 if (text) AccessibleText_ref (text);
533 fprintf (stderr, "%s, bounds %d-%d; clip %d-%d\n",
535 text_chunk->text_bounds.x,
536 text_chunk->text_bounds.x+text_chunk->text_bounds.width,
537 text_chunk->clip_bounds.x,
538 text_chunk->clip_bounds.x+text_chunk->clip_bounds.width);
541 text_chunk->string = NULL;
542 text_chunk->source = NULL;
544 if (text) AccessibleText_unref (text);
549 debug_chunk_list (GList *iter)
553 chunk = (TextChunk *)iter->data;
554 fprintf (stderr, "Chunk %s, clip %d-%d, text %d-%d\n",
556 chunk->clip_bounds.x,
557 chunk->clip_bounds.x + chunk->clip_bounds.width,
558 chunk->text_bounds.x,
559 chunk->text_bounds.x + chunk->text_bounds.width);
565 clip_into_buffers (Accessible *accessible, BoundaryRect* parentClipBounds[],
566 ScreenReviewBuffer *reviewBuffers[],
567 int screen_x, int screen_y)
569 int n_children, child_n;
572 BoundaryRect** clip_bounds;
573 TextChunk *text_chunk;
574 AccessibleComponent *component;
577 clip_bounds = clip_bounds_clone (parentClipBounds);
578 if (Accessible_isComponent (accessible)) {
579 component = Accessible_getComponent (accessible);
580 layer = AccessibleComponent_getLayer (component);
581 bounds = *clip_bounds[layer];
582 if (!bounds.isEmpty || 1) {
583 AccessibleComponent_getExtents (component,
588 SPI_COORD_TYPE_SCREEN);
589 if (clip_bounds[layer])
590 boundary_clip (&bounds, clip_bounds[layer]);
591 if (bounds_contain_y (&bounds, screen_y)) {
592 text_chunk = review_buffer_get_text_chunk (
593 reviewBuffers[layer], accessible, &bounds,
595 reviewBuffers[layer]->text_chunks =
596 text_chunk_list_insert_chunk (
597 reviewBuffers[layer]->text_chunks,
600 clip_bounds[layer]->isEmpty = TRUE;
603 Accessible_unref (component);
606 * we always descend into children in case they are in a higher layer
607 * this can of course be optimized for the topmost layer...
608 * but nobody uses that one! (SPI_LAYER_OVERLAY)
610 n_children = Accessible_getChildCount (accessible);
611 for (child_n = 0; child_n < n_children; ++child_n) {
612 child = Accessible_getChildAtIndex (accessible, child_n);
613 clip_into_buffers (child, clip_bounds, reviewBuffers, screen_x, screen_y);
614 Accessible_unref (child);
616 /* TODO: free the parent clip bounds */
620 text_chunk_get_clipped_string (TextChunk *chunk)
626 GString *string = g_string_new ("");
627 BoundaryRect char_bounds;
628 if (!chunk->text_bounds.isClipped)
630 else if (chunk->source) {
631 len = chunk->end_offset - chunk->start_offset;
633 fprintf (stderr, "clipping %s\n", chunk->string);
635 for (i = 0; i < len; ++i) {
636 AccessibleText_getCharacterExtents (chunk->source,
642 SPI_COORD_TYPE_SCREEN);
643 #ifdef CHARACTER_CLIP_DEBUG
644 fprintf (stderr, "testing %d-%d against %d-%d\n",
645 char_bounds.x, char_bounds.x+char_bounds.width,
646 chunk->text_bounds.x,
647 chunk->text_bounds.x + chunk->text_bounds.width);
649 if (BOUNDS_CONTAIN_X (&chunk->text_bounds,
651 c = AccessibleText_getCharacterAtOffset (
654 fprintf (stderr, "[%c]", c);
656 g_string_append_unichar (string, c);
660 } else { /* we're clipped, but don't implement AccessibleText :-( */
661 /* punt for now, maybe we can do betterc someday */
664 g_string_free (string, FALSE);
669 text_chunk_list_to_string (GList *iter)
673 TextChunk *chunk = NULL;
675 chunk = (TextChunk *)iter->data;
676 if (chunk /* && chunk->string */) {
677 string = text_chunk_get_clipped_string (chunk);
679 s = g_strconcat (s, "|", string, NULL);
681 s = g_strconcat (s, ":", NULL);
685 s = g_strconcat (s, "|", NULL);
690 review_buffer_composite (ScreenReviewBuffer *buffers[])
693 GList *chunk_list, *iter;
695 chunk_list = buffers[0]->text_chunks;
696 for (i = 1; i < SPI_LAYER_LAST_DEFINED; ++i) {
697 iter = buffers[i]->text_chunks;
698 fprintf (stderr, "layer %d has %d chunks\n",
699 i, g_list_length (iter));
701 chunk = (TextChunk *) iter->data;
703 fprintf (stderr, "inserting chunk <%s>\n",
704 chunk->string ? chunk->string : "<null>");
706 text_chunk_list_insert_chunk (chunk_list,
712 chunk_list = buffers[SPI_LAYER_WIDGET]->text_chunks;
713 return text_chunk_list_to_string (chunk_list);
717 get_screen_review_line_at (int x, int y)
720 Accessible *desktop, *app, *toplevel, *child;
721 AccessibleComponent *component;
722 AccessibleStateSet *states;
723 GList *toplevels = NULL, *actives = NULL, *iter;
724 ScreenReviewBuffer* reviewBuffers[SPI_LAYER_LAST_DEFINED];
725 BoundaryRect* clip_bounds[SPI_LAYER_LAST_DEFINED];
726 BoundaryRect toplevel_bounds;
727 int n_apps, n_toplevels, n_children, app_n, toplevel_n, child_n;
728 GTimer *timer = g_timer_new ();
731 for (i = 0; i < SPI_LAYER_LAST_DEFINED; ++i) {
732 reviewBuffers[i] = g_new0 (ScreenReviewBuffer, 1);
733 clip_bounds[i] = g_new0 (BoundaryRect, 1);
734 clip_bounds[i]->isClipped = FALSE;
735 clip_bounds[i]->isEmpty = FALSE;
738 /* how do we decide which desktop ? */
739 desktop = SPI_getDesktop (0);
742 n_apps = Accessible_getChildCount (desktop);
743 for (app_n = 0; app_n < n_apps; ++app_n) {
744 /* for each toplevel in app */
745 app = Accessible_getChildAtIndex (desktop, app_n);
746 n_toplevels = Accessible_getChildCount (app);
747 for (toplevel_n = 0; toplevel_n < n_toplevels; ++toplevel_n) {
748 Accessible *toplevel = Accessible_getChildAtIndex (app, toplevel_n);
749 if (Accessible_isComponent (toplevel))
750 toplevels = g_list_prepend (toplevels, toplevel);
752 Accessible_unref (toplevel);
753 fprintf (stderr, "warning, app toplevel not a component.\n");
758 /* sort: at the moment we don't have a good way to sort except to put actives on top */
759 for (iter = g_list_first (toplevels); iter; iter = iter->next) {
760 Accessible *toplevel =
761 (Accessible *) iter->data;
762 if (AccessibleStateSet_contains (Accessible_getStateSet (toplevel),
764 actives = g_list_prepend (actives, toplevel);
768 for (iter = g_list_first (actives); iter; iter = actives->next) {
769 toplevels = g_list_remove (toplevels, iter->data); /* place at end */
770 toplevels = g_list_append (toplevels, iter->data);
772 g_list_free (actives);
774 /* for each toplevel, ending with the active one(s),
775 * clip against children, putting results into appropriate charBuffer.
777 for (iter = g_list_first (toplevels); iter; iter = iter->next) {
778 toplevel = (Accessible *) iter->data;
779 if (Accessible_isComponent (toplevel)) {
780 /* make sure toplevel is visible and not iconified or shaded */
781 states = Accessible_getStateSet (toplevel);
782 if (AccessibleStateSet_contains (states, SPI_STATE_VISIBLE)
783 && !AccessibleStateSet_contains (states, SPI_STATE_ICONIFIED)
784 || isJava) { /* isJava hack! */
785 component = Accessible_getComponent (toplevel);
786 AccessibleComponent_getExtents (component,
789 &toplevel_bounds.width,
790 &toplevel_bounds.height,
791 SPI_COORD_TYPE_SCREEN);
792 toplevel_bounds.isEmpty = FALSE;
793 for (i = 0; i < SPI_LAYER_LAST_DEFINED; ++i) {
794 *clip_bounds[i] = toplevel_bounds;
797 fprintf (stderr, "toplevel clip starting\n");
798 debug_chunk_list (reviewBuffers[SPI_LAYER_WIDGET]->text_chunks);
800 clip_into_buffers (toplevel, clip_bounds,
801 reviewBuffers, x, y);
804 Accessible_unref (toplevel);
807 string = review_buffer_composite (reviewBuffers);
809 /* SIMPLE SINGLE-PASS ALGORITHM:*/
810 /* traverse the tree:
811 * keep a pointer to outermost instance of each layer
812 * clip against outermost in same layer
813 * when this clip occurs, store outermost clipped string in 2d string buffer.
814 * string buffer may have attributes to mark component bounds, line art,
815 * or attributes of text being reviewed.
816 * composite the layers, ignoring NULL chars in the string buffers.
819 * sibling clip not correct, text may overwrite if siblings intersect onscreen
820 * length of resulting text buffer may vary!
823 * no API for ordering toplevels yet, other than knowing which is ACTIVE.
824 * not much implementation for the LAYER API yet, other than menus.
826 g_timer_stop (timer);
827 fprintf (stderr, "elapsed time = %f s\n", g_timer_elapsed (timer, NULL));
833 report_screen_review_line (const AccessibleEvent *event, void *user_data)
835 static Display *display = NULL;
836 int x, y, win_x, win_y;
837 Window root_return, child_return;
838 unsigned int mask_return;
840 if (!display) display = XOpenDisplay (getenv ("DISPLAY"));
842 * we would prefer to get the x,y info in the above event.
843 * At the moment we don't get detail params for "toolkit" events,
844 * so for testing purposes we use XQueryPointer. Actual apps
845 * probably shouldn't do this.
847 XQueryPointer (display,
848 DefaultRootWindow (display),
849 &root_return, &child_return,
854 fprintf (stderr, "screen review event %s at %d, %d\n", event->type,
856 fprintf (stderr, "[%s]\n",
857 get_screen_review_line_at (x, y));
863 SPI_deregisterGlobalEventListenerAll (mouseclick_listener);
864 AccessibleEventListener_unref (mouseclick_listener);