From c4d4854798b0db73376f23a3e70a8a75909a4620 Mon Sep 17 00:00:00 2001 From: billh Date: Mon, 20 May 2002 20:54:57 +0000 Subject: [PATCH] Added screen review test sample file. git-svn-id: http://svn.gnome.org/svn/at-spi/trunk@299 e2bd861d-eb25-0410-b326-f6ed22b6b98c --- test/Makefile.am | 4 +- test/screen-review-test.c | 863 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 866 insertions(+), 1 deletion(-) create mode 100644 test/screen-review-test.c diff --git a/test/Makefile.am b/test/Makefile.am index 49020da..1fc1fd1 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,11 +1,13 @@ NULL= -noinst_PROGRAMS = test-simple at app simple-at keysynth-demo accessx-gui key-listener-test event-listener-test +noinst_PROGRAMS = test-simple at app simple-at keysynth-demo accessx-gui key-listener-test event-listener-test screen-review-test key_listener_test_SOURCES = key-listener-test.c event_listener_test_SOURCES = event-listener-test.c +screen_review_test_SOURCES = screen-review-test.c + at_SOURCES = at.c app_SOURCES = app.c diff --git a/test/screen-review-test.c b/test/screen-review-test.c new file mode 100644 index 0000000..070af60 --- /dev/null +++ b/test/screen-review-test.c @@ -0,0 +1,863 @@ +/* + * AT-SPI - Assistive Technology Service Provider Interface + * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap) + * + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "../cspi/spi-private.h" + +#define CLIP_DEBUG + +#define BOUNDS_CONTAIN_X(b, p) (((p)>=(b)->x) && ((p)<=((b)->x + (b)->width))\ + && ((b)->width > 0) && ((b)->height > 0)) + +#define BOUNDS_CONTAIN_Y(b, p) (((p)>=(b)->y) && ((p)<=((b)->y + (b)->height))\ + && ((b)->width > 0) && ((b)->height > 0)) + +#define CHUNK_BOUNDS_BEFORE_START(c, i) ((i) && ((c)->clip_bounds.x < \ + (i)->clip_bounds.x)) + +#define CHUNK_BOUNDS_END_BEFORE_START(c, i) ((i) && \ + (((c)->clip_bounds.x + \ + (c)->clip_bounds.width) < \ + (i)->clip_bounds.x)) + +#define CHUNK_BOUNDS_AFTER_END(c, i) ((i) && ((c)->clip_bounds.x >= \ + ((i)->clip_bounds.x + \ + (i)->clip_bounds.width))) + +#define CHUNK_BOUNDS_SPANS_END(c, i) ((i) && (((c)->clip_bounds.x + \ + (c)->clip_bounds.width) >= \ + ((i)->clip_bounds.x + \ + (i)->clip_bounds.width))) + +//#define CHUNK_BOUNDS_WITHIN(c, i) ((i) && ((c)->clip_bounds.x >= \ +// (i)->text_bounds.x) && \ +// (((c)->clip_bounds.x + (c)->clip_bounds.width) \ +// <= ((i)->text_bounds.x + (i)->text_bounds.width))) + +static void report_screen_review_line (const AccessibleEvent *event, void *user_data); + +static gint n_elements_traversed = 0; +static AccessibleEventListener *mouseclick_listener; + +typedef struct _BoundaryRect { + long int x; + long int y; + long int width; + long int height; + AccessibleRole role; /* role of last clipping element */ + gboolean isClipped; + gboolean isEmpty; +} BoundaryRect; + +typedef struct _TextChunk { + char *string; + AccessibleText *source; + int start_offset; + int end_offset; + BoundaryRect clip_bounds; + BoundaryRect text_bounds; + BoundaryRect start_char_bounds; + BoundaryRect end_char_bounds; +} TextChunk; + +typedef struct _ScreenReviewBuffer { /* TODO: implement */ + GList *text_chunks; +} ScreenReviewBuffer; + +static gboolean isJava = FALSE; + +int +main (int argc, char **argv) +{ + int i, j; + int n_desktops; + int n_apps; + char *s; + GTimer *timer; + gdouble elapsed_time; + Accessible *desktop; + Accessible *application; + const char *modules; + + SPI_init (); + + mouseclick_listener = SPI_createAccessibleEventListener ( + report_screen_review_line, NULL); + + SPI_registerGlobalEventListener (mouseclick_listener, + "Gtk:GtkWidget:button-press-event"); +#define JAVA_TEST_HACK +#ifdef JAVA_TEST_HACK + SPI_registerGlobalEventListener (mouseclick_listener, + "object:text-caret-moved"); + isJava = TRUE; +#endif + SPI_event_main (); + + putenv ("AT_BRIDGE_SHUTDOWN=1"); + + /* + * TODO: Add a key event listener that calls test_exit, to + * deregister and cleanup appropriately. + */ + + return SPI_exit (); +} + +static inline gboolean +bounds_contain_y (BoundaryRect *bounds, int y) +{ + return (y > bounds->y && y <= (bounds->y + bounds->height) + && bounds->width && bounds->height); +} + + +static inline gboolean +chunk_bounds_within (TextChunk *chunk, TextChunk *test_chunk) +{ + int x1, x2, tx1, tx2; + gboolean gtx1, ltx2; + + x1 = chunk->clip_bounds.x; + x2 = x1 + chunk->clip_bounds.width; + tx1 = test_chunk->clip_bounds.x; + tx2 = tx1 + test_chunk->clip_bounds.width; + gtx1 = (chunk->clip_bounds.x >= test_chunk->clip_bounds.x); + ltx2 = (chunk->clip_bounds.x + chunk->clip_bounds.width + <= test_chunk->clip_bounds.x + test_chunk->clip_bounds.width); + +// fprintf (stderr, "testing BOUNDS %d-%d WITHIN %d-%d: %s\n", +// x1, x2, tx1, tx2, ((gtx1 && ltx2) ? "T" : "F")); + + return gtx1 && ltx2; +} + +#define CHUNK_BOUNDS_WITHIN(a, b) chunk_bounds_within(a, b) + +static BoundaryRect ** +clip_bounds_clone (BoundaryRect *bounds[]) +{ + int i; + BoundaryRect **bounds_clone; + bounds_clone = (BoundaryRect **) + g_new0 (gpointer, SPI_LAYER_LAST_DEFINED); + for (i = 0; i < SPI_LAYER_LAST_DEFINED; ++i) { + bounds_clone[i] = g_new0 (BoundaryRect, 1); + *bounds_clone[i] = *bounds[i]; + } + return bounds_clone; +} + +static void +boundary_clip (BoundaryRect *bounds, BoundaryRect *clipBounds) +{ + int x2 = bounds->x + bounds->width; + int y2 = bounds->y + bounds->height; +#ifdef CLIP_DEBUG + fprintf (stderr, "bclip %d-%d, %d-%d; ", + bounds->x, x2, + clipBounds->x, clipBounds->x+clipBounds->width); +#endif + bounds->x = MAX (bounds->x, clipBounds->x); + bounds->y = MAX (bounds->y, clipBounds->y); + x2 = MIN (x2, clipBounds->x + clipBounds->width); + y2 = MIN (y2, clipBounds->y + clipBounds->height); + bounds->width = MAX (x2 - bounds->x, 0); + bounds->height = MAX (y2 - bounds->y, 0); + if (!bounds->width || !bounds->height) + bounds->isEmpty = TRUE; +#ifdef CLIP_DEBUG + fprintf (stderr, "%d-%d\n", + bounds->x, bounds->x+bounds->width); +#endif +} + +static void +boundary_xclip_head (BoundaryRect *bounds, BoundaryRect *clipBounds) +{ + int x2; + int cx2 = clipBounds->x + clipBounds->width; + if (cx2 < bounds->x) return; + x2 = bounds->x + bounds->width; + if (cx2 < x2) bounds->x = cx2; + bounds->width = MAX (0, x2 - cx2); +} + +static void +boundary_xclip_tail (BoundaryRect *bounds, BoundaryRect *clipBounds) +{ + int x2 = bounds->x + bounds->width; + if (clipBounds->x > x2) return; + bounds->width = MAX (0, clipBounds->x - bounds->x); +} + +static TextChunk* +text_chunk_copy (TextChunk *chunk) +{ + TextChunk *copy = g_new0 (TextChunk, 1); + *copy = *chunk; + if (chunk->string) copy->string = g_strdup (chunk->string); + if (copy->source) AccessibleText_ref (copy->source); + return copy; +} + +static void +text_chunk_tail_clip (TextChunk *bottom, TextChunk *top) +{ +#ifdef CLIP_DEBUG + fprintf (stderr, "bottom %d-%d, top %d-%d;", + bottom->clip_bounds.x, + bottom->clip_bounds.x + bottom->clip_bounds.width, + top->clip_bounds.x, + top->clip_bounds.x + top->clip_bounds.width); +#endif + boundary_xclip_tail (&bottom->text_bounds, &top->clip_bounds); + boundary_xclip_tail (&bottom->clip_bounds, &top->clip_bounds); + bottom->text_bounds.isClipped = TRUE; + bottom->clip_bounds.isClipped = TRUE; +#ifdef CLIP_DEBUG + fprintf (stderr, "result %d-%d\n", + bottom->clip_bounds.x, + bottom->clip_bounds.x + bottom->clip_bounds.width); +#endif +} + +static void +text_chunk_head_clip (TextChunk *bottom, TextChunk *top) +{ +#ifdef CLIP_DEBUG + fprintf (stderr, "bottom %d-%d, top %d-%d;", + bottom->clip_bounds.x, + bottom->clip_bounds.x + bottom->clip_bounds.width, + top->clip_bounds.x, + top->clip_bounds.x + top->clip_bounds.width); +#endif + boundary_xclip_head (&bottom->text_bounds, &top->clip_bounds); + boundary_xclip_head (&bottom->clip_bounds, &top->clip_bounds); + bottom->text_bounds.isClipped = TRUE; + bottom->clip_bounds.isClipped = TRUE; +#ifdef CLIP_DEBUG + fprintf (stderr, "result %d-%d\n", + bottom->clip_bounds.x, + bottom->clip_bounds.x + bottom->clip_bounds.width); +#endif +} + +static GList * +text_chunk_split_insert (GList *chunk_list, GList *iter, TextChunk *chunk) +{ + TextChunk *iter_chunk = iter->data; + TextChunk *iter_copy = text_chunk_copy (iter_chunk); + /* TODO: FIXME something is wrong here */ +#ifdef CLIP_DEBUG + fprintf (stderr, "***clip insert of %s into %s\n", chunk->string, + iter_chunk->string); +#endif + chunk_list = g_list_insert_before (chunk_list, iter, iter_copy); + text_chunk_tail_clip (iter_copy, chunk); + chunk_list = g_list_insert_before (chunk_list, iter, chunk); + text_chunk_head_clip (iter_chunk, chunk); + return chunk_list; +} + +/* #define PRINT_CHUNK_DEBUG(a, b, c, d) print_chunk_debug(a, b, c, d) */ + +#define PRINT_CHUNK_DEBUG(a, b, c, d) + +#ifdef PRINT_CHUNK_DEBUG +static void +print_chunk_debug (TextChunk *chunk, char *opname, GList *prev, GList *next) +{ + fprintf (stderr, "%sing chunk %s between %s and %s; %d-%d\n", + opname, + chunk->string, + (prev ? ((TextChunk *) prev->data)->string : ""), + (next ? ((TextChunk *) next->data)->string : ""), + chunk->clip_bounds.x, + chunk->text_bounds.x + chunk->text_bounds.width); +} +#endif + +static GList * +text_chunk_list_head_clip (GList *text_chunk_list, + TextChunk *chunk, + GList *next) +{ + GList *target, *iter = next, *prev; + prev = iter->prev; +// if (chunk->string && strlen (chunk->string)) { + text_chunk_list = + g_list_insert_before (text_chunk_list, next, chunk); +// } + while (iter) { + if (CHUNK_BOUNDS_SPANS_END (chunk, (TextChunk *)iter->data)) { +#ifdef CLIP_DEBUG + fprintf (stderr, "deleting %s\n", + ((TextChunk *)iter->data)->string); +#endif + target = iter; + iter = iter->next; + text_chunk_list = + g_list_delete_link (text_chunk_list, target); + } else { + if (!CHUNK_BOUNDS_END_BEFORE_START (chunk, + (TextChunk *)iter->data)) { + text_chunk_head_clip ((TextChunk *)iter->data, + chunk); + } + if (prev && + !CHUNK_BOUNDS_AFTER_END ( + chunk, + (TextChunk *)prev->data)) { + text_chunk_tail_clip ( + (TextChunk *)prev->data, + chunk); + } + break; + } + } + + return text_chunk_list; +} + +static GList * +text_chunk_list_clip_and_insert (GList *text_chunk_list, + TextChunk *chunk, + GList *prev, + GList *next) +{ +#ifdef CLIP_DEBUG + fprintf (stderr, "clip-and-insert for %s, between %s and %s\n", + chunk->string, + (prev ? ((TextChunk *)prev->data)->string : ""), + (next ? ((TextChunk *)next->data)->string : "")); +#endif + /* cases: */ + if (!prev && !next) { /* first element in, no clip needed */ +// if (chunk->string && strlen (chunk->string)) { + text_chunk_list = + g_list_append (text_chunk_list, chunk); + PRINT_CHUNK_DEBUG (chunk, "append", + prev, + NULL); +// } + } else { /* check for clip with prev */ + /* if we split the prev */ + if (prev && + CHUNK_BOUNDS_WITHIN (chunk, (TextChunk *) prev->data)) { + text_chunk_list = + text_chunk_split_insert ( + text_chunk_list, + prev, chunk); + } else if (next) { + /* we split the 'next' element */ + if (CHUNK_BOUNDS_WITHIN (chunk, (TextChunk *)next->data)) { + text_chunk_list = + text_chunk_split_insert (text_chunk_list, + next, chunk); + } else { + /* do an insert + head clip */ + text_chunk_list = + text_chunk_list_head_clip ( + text_chunk_list, + chunk, + next); + } + } else { + if (!CHUNK_BOUNDS_AFTER_END (chunk, + (TextChunk *) prev->data)) { + text_chunk_tail_clip (prev->data, chunk); + } +// if (chunk->string && strlen (chunk->string)) { + text_chunk_list = + g_list_append (text_chunk_list, chunk); +// } + } + } + return text_chunk_list; +} + +static GList * +text_chunk_list_insert_chunk (GList *text_chunk_list, TextChunk *chunk) +{ + GList *iter = g_list_first (text_chunk_list); + TextChunk *iter_chunk = NULL; + do { + if (iter) iter_chunk = (TextChunk *) iter->data; + /* if we're ahead of the current element */ + if (!iter) { + text_chunk_list = + text_chunk_list_clip_and_insert (text_chunk_list, + chunk, + iter, + NULL); + break; + } else if (CHUNK_BOUNDS_BEFORE_START (chunk, iter_chunk)) { + text_chunk_list = + text_chunk_list_clip_and_insert (text_chunk_list, + chunk, + iter->prev, + iter); + break; + } else if (!iter->next ) { + text_chunk_list = + text_chunk_list_clip_and_insert (text_chunk_list, + chunk, + iter, + NULL); + break; + } + if (iter) iter = iter->next; + } while (iter); + return text_chunk_list; +} + +static TextChunk * +review_buffer_get_text_chunk (ScreenReviewBuffer *reviewBuffer, + Accessible *accessible, BoundaryRect *bounds, + int screen_x, int screen_y) +{ + AccessibleText *text = NULL; + AccessibleRole role; + TextChunk *text_chunk; + BoundaryRect start_char_bounds, end_char_bounds; + char *s = NULL; + int offset; + long start, end; + long x2, y2; + + role = Accessible_getRole (accessible); + text_chunk = g_new0 (TextChunk, 1); + text_chunk->clip_bounds = *bounds; + if (Accessible_isText (accessible)) { + text = Accessible_getText (accessible); + offset = AccessibleText_getOffsetAtPoint (text, + screen_x, + screen_y, + SPI_COORD_TYPE_SCREEN); + s = AccessibleText_getTextAtOffset (text, offset, + SPI_TEXT_BOUNDARY_LINE_START, + &start, &end); + if (end > start) { + AccessibleText_getCharacterExtents ( + text, start, + &text_chunk->start_char_bounds.x, + &text_chunk->start_char_bounds.y, + &text_chunk->start_char_bounds.width, + &text_chunk->start_char_bounds.height, + SPI_COORD_TYPE_SCREEN); +#ifdef CLIP_DEBUG + fprintf (stderr, "%s: start char (%d) x, width %d %d;", + s, + start, + text_chunk->start_char_bounds.x, + text_chunk->start_char_bounds.width); +#endif + end--; /* XXX: bug workaround */ + AccessibleText_getCharacterExtents ( + text, end--, + &text_chunk->end_char_bounds.x, + &text_chunk->end_char_bounds.y, + &text_chunk->end_char_bounds.width, + &text_chunk->end_char_bounds.height, + SPI_COORD_TYPE_SCREEN); +#ifdef CLIP_DEBUG + fprintf (stderr, "end char (%d) x, width %d %d\n", + end, + text_chunk->end_char_bounds.x, + text_chunk->end_char_bounds.width); +#endif + } + text_chunk->text_bounds.x = MIN (text_chunk->start_char_bounds.x, + text_chunk->end_char_bounds.x); + text_chunk->text_bounds.y = MIN (text_chunk->start_char_bounds.y, + text_chunk->end_char_bounds.y); + x2 = MAX (text_chunk->start_char_bounds.x + + text_chunk->start_char_bounds.width, + text_chunk->end_char_bounds.x + + text_chunk->end_char_bounds.width); + text_chunk->text_bounds.width = x2 - text_chunk->text_bounds.x; + y2 = MAX (text_chunk->start_char_bounds.y + + text_chunk->start_char_bounds.height, + text_chunk->end_char_bounds.y + + text_chunk->end_char_bounds.height); + text_chunk->text_bounds.height = y2 - text_chunk->text_bounds.y; + text_chunk->start_offset = start; + text_chunk->end_offset = end; + if ((role != SPI_ROLE_TABLE_CELL) /* XXX bug workaround */ + && !bounds_contain_y (&text_chunk->text_bounds, + screen_y)) { + s = NULL; + } + } else { + if (role == SPI_ROLE_PUSH_BUTTON || + role == SPI_ROLE_CHECK_BOX || + role == SPI_ROLE_MENU || + role == SPI_ROLE_MENU_ITEM) { /* don't like this + special casing :-( */ + s = Accessible_getName (accessible); + /* use name instead */ + text_chunk->text_bounds = text_chunk->clip_bounds; + text_chunk->start_offset = 0; + text_chunk->end_offset = strlen (s); + } + } + if (s && strlen (s)) { + if (s[strlen(s)-1] == '\n') s[strlen(s)-1] = ' '; + /* XXX: if last char is newline, aren't its bounds wrong now? */ + text_chunk->string = s; + text_chunk->source = text; + if (text) AccessibleText_ref (text); +#ifdef CLIP_DEBUG + fprintf (stderr, "%s, bounds %d-%d; clip %d-%d\n", + s, + text_chunk->text_bounds.x, + text_chunk->text_bounds.x+text_chunk->text_bounds.width, + text_chunk->clip_bounds.x, + text_chunk->clip_bounds.x+text_chunk->clip_bounds.width); +#endif + } else { + text_chunk->string = NULL; + text_chunk->source = NULL; + } + if (text) AccessibleText_unref (text); + return text_chunk; +} + +static void +debug_chunk_list (GList *iter) +{ + TextChunk *chunk; + while (iter) { + chunk = (TextChunk *)iter->data; + fprintf (stderr, "Chunk %s, clip %d-%d, text %d-%d\n", + chunk->string, + chunk->clip_bounds.x, + chunk->clip_bounds.x + chunk->clip_bounds.width, + chunk->text_bounds.x, + chunk->text_bounds.x + chunk->text_bounds.width); + iter = iter->next; + } +} + +static void +clip_into_buffers (Accessible *accessible, BoundaryRect* parentClipBounds[], + ScreenReviewBuffer *reviewBuffers[], + int screen_x, int screen_y) +{ + int n_children, child_n; + Accessible *child; + BoundaryRect bounds; + BoundaryRect** clip_bounds; + TextChunk *text_chunk; + AccessibleComponent *component; + int layer; + + clip_bounds = clip_bounds_clone (parentClipBounds); + if (Accessible_isComponent (accessible)) { + component = Accessible_getComponent (accessible); + layer = AccessibleComponent_getLayer (component); + bounds = *clip_bounds[layer]; + if (!bounds.isEmpty || 1) { + AccessibleComponent_getExtents (component, + &bounds.x, + &bounds.y, + &bounds.width, + &bounds.height, + SPI_COORD_TYPE_SCREEN); + if (clip_bounds[layer]) + boundary_clip (&bounds, clip_bounds[layer]); + if (bounds_contain_y (&bounds, screen_y)) { + text_chunk = review_buffer_get_text_chunk ( + reviewBuffers[layer], accessible, &bounds, + screen_x, screen_y); + reviewBuffers[layer]->text_chunks = + text_chunk_list_insert_chunk ( + reviewBuffers[layer]->text_chunks, + text_chunk); + } else { + clip_bounds[layer]->isEmpty = TRUE; + } + } + Accessible_unref (component); + } + /* + * we always descend into children in case they are in a higher layer + * this can of course be optimized for the topmost layer... + * but nobody uses that one! (SPI_LAYER_OVERLAY) + */ + n_children = Accessible_getChildCount (accessible); + for (child_n = 0; child_n < n_children; ++child_n) { + child = Accessible_getChildAtIndex (accessible, child_n); + clip_into_buffers (child, clip_bounds, reviewBuffers, screen_x, screen_y); + Accessible_unref (child); + } + /* TODO: free the parent clip bounds */ +} + +static char* +text_chunk_get_clipped_string (TextChunk *chunk) +{ + char *s; + int i; + int len; + gunichar c; + GString *string = g_string_new (""); + BoundaryRect char_bounds; + if (!chunk->text_bounds.isClipped) + s = chunk->string; + else if (chunk->source) { + len = chunk->end_offset - chunk->start_offset; +#ifdef CLIP_DEBUG + fprintf (stderr, "clipping %s\n", chunk->string); +#endif + for (i = 0; i < len; ++i) { + AccessibleText_getCharacterExtents (chunk->source, + i, + &char_bounds.x, + &char_bounds.y, + &char_bounds.width, + &char_bounds.height, + SPI_COORD_TYPE_SCREEN); +#ifdef CHARACTER_CLIP_DEBUG + fprintf (stderr, "testing %d-%d against %d-%d\n", + char_bounds.x, char_bounds.x+char_bounds.width, + chunk->text_bounds.x, + chunk->text_bounds.x + chunk->text_bounds.width); +#endif + if (BOUNDS_CONTAIN_X (&chunk->text_bounds, + char_bounds.x)) { + c = AccessibleText_getCharacterAtOffset ( + chunk->source, i); +#ifdef CLIP_DEBUG + fprintf (stderr, "[%c]", c); +#endif + g_string_append_unichar (string, c); + } + } + s = string->str; + } else { /* we're clipped, but don't implement AccessibleText :-( */ + /* punt for now, maybe we can do betterc someday */ + s = chunk->string; + } + g_string_free (string, FALSE); + return s; +} + +static char* +text_chunk_list_to_string (GList *iter) +{ + char *s = ""; + char *string; + TextChunk *chunk = NULL; + while (iter) { + chunk = (TextChunk *)iter->data; + if (chunk /* && chunk->string */) { + string = text_chunk_get_clipped_string (chunk); + if (string) + s = g_strconcat (s, "|", string, NULL); + else /* XXX test */ + s = g_strconcat (s, ":", NULL); + } + iter = iter->next; + } + s = g_strconcat (s, "|", NULL); + return s; +} + +static char* +review_buffer_composite (ScreenReviewBuffer *buffers[]) +{ + int i; + GList *chunk_list, *iter; + TextChunk *chunk; + chunk_list = buffers[0]->text_chunks; + for (i = 1; i < SPI_LAYER_LAST_DEFINED; ++i) { + iter = buffers[i]->text_chunks; + fprintf (stderr, "layer %d has %d chunks\n", + i, g_list_length (iter)); + while (iter) { + chunk = (TextChunk *) iter->data; + if (chunk) { + fprintf (stderr, "inserting chunk <%s>\n", + chunk->string); + chunk_list = + text_chunk_list_insert_chunk (chunk_list, + chunk); + } + iter = iter->next; + } + } + chunk_list = buffers[SPI_LAYER_WIDGET]->text_chunks; + return text_chunk_list_to_string (chunk_list); +} + +static char * +get_screen_review_line_at (int x, int y) +{ + char *string; + Accessible *desktop, *app, *toplevel, *child; + AccessibleComponent *component; + AccessibleStateSet *states; + GList *toplevels = NULL, *actives = NULL, *iter; + ScreenReviewBuffer* reviewBuffers[SPI_LAYER_LAST_DEFINED]; + BoundaryRect* clip_bounds[SPI_LAYER_LAST_DEFINED]; + BoundaryRect toplevel_bounds; + int n_apps, n_toplevels, n_children, app_n, toplevel_n, child_n; + GTimer *timer = g_timer_new (); + int i; + + for (i = 0; i < SPI_LAYER_LAST_DEFINED; ++i) { + reviewBuffers[i] = g_new0 (ScreenReviewBuffer, 1); + clip_bounds[i] = g_new0 (BoundaryRect, 1); + clip_bounds[i]->isClipped = FALSE; + clip_bounds[i]->isEmpty = FALSE; + } + + /* how do we decide which desktop ? */ + desktop = SPI_getDesktop (0); + + /* for each app */ + n_apps = Accessible_getChildCount (desktop); + for (app_n = 0; app_n < n_apps; ++app_n) { + /* for each toplevel in app */ + app = Accessible_getChildAtIndex (desktop, app_n); + n_toplevels = Accessible_getChildCount (app); + for (toplevel_n = 0; toplevel_n < n_toplevels; ++toplevel_n) { + Accessible *toplevel = Accessible_getChildAtIndex (app, toplevel_n); + if (Accessible_isComponent (toplevel)) + toplevels = g_list_prepend (toplevels, toplevel); + else { + Accessible_unref (toplevel); + fprintf (stderr, "warning, app toplevel not a component.\n"); + } + } + } + + /* sort: at the moment we don't have a good way to sort except to put actives on top */ + for (iter = g_list_first (toplevels); iter; iter = iter->next) { + Accessible *toplevel = + (Accessible *) iter->data; + if (AccessibleStateSet_contains (Accessible_getStateSet (toplevel), + SPI_STATE_ACTIVE)) { + actives = g_list_prepend (actives, toplevel); + } + } + + for (iter = g_list_first (actives); iter; iter = actives->next) { + toplevels = g_list_remove (toplevels, iter->data); /* place at end */ + toplevels = g_list_append (toplevels, iter->data); + } + g_list_free (actives); + + /* for each toplevel, ending with the active one(s), + * clip against children, putting results into appropriate charBuffer. + */ + for (iter = g_list_first (toplevels); iter; iter = iter->next) { + toplevel = (Accessible *) iter->data; + if (Accessible_isComponent (toplevel)) { + /* make sure toplevel is visible and not iconified or shaded */ + states = Accessible_getStateSet (toplevel); + if (AccessibleStateSet_contains (states, SPI_STATE_VISIBLE) + && !AccessibleStateSet_contains (states, SPI_STATE_ICONIFIED) + || isJava) { /* isJava hack! */ + component = Accessible_getComponent (toplevel); + AccessibleComponent_getExtents (component, + &toplevel_bounds.x, + &toplevel_bounds.y, + &toplevel_bounds.width, + &toplevel_bounds.height, + SPI_COORD_TYPE_SCREEN); + toplevel_bounds.isEmpty = FALSE; + for (i = 0; i < SPI_LAYER_LAST_DEFINED; ++i) { + *clip_bounds[i] = toplevel_bounds; + } +#ifdef CLIP_DEBUG + fprintf (stderr, "toplevel clip starting\n"); + debug_chunk_list (reviewBuffers[SPI_LAYER_WIDGET]->text_chunks); +#endif + clip_into_buffers (toplevel, clip_bounds, + reviewBuffers, x, y); + } + } + Accessible_unref (toplevel); + } + + string = review_buffer_composite (reviewBuffers); + + /* SIMPLE SINGLE-PASS ALGORITHM:*/ + /* traverse the tree: + * keep a pointer to outermost instance of each layer + * clip against outermost in same layer + * when this clip occurs, store outermost clipped string in 2d string buffer. + * string buffer may have attributes to mark component bounds, line art, + * or attributes of text being reviewed. + * composite the layers, ignoring NULL chars in the string buffers. + * + * Limitations: + * sibling clip not correct, text may overwrite if siblings intersect onscreen + * length of resulting text buffer may vary! + * + * Technical issues: + * no API for ordering toplevels yet, other than knowing which is ACTIVE. + * not much implementation for the LAYER API yet, other than menus. + */ + g_timer_stop (timer); + fprintf (stderr, "elapsed time = %f s\n", g_timer_elapsed (timer, NULL)); + + return string; +} + +void +report_screen_review_line (const AccessibleEvent *event, void *user_data) +{ + static Display *display = NULL; + int x, y, win_x, win_y; + Window root_return, child_return; + unsigned int mask_return; + + if (!display) display = XOpenDisplay (getenv ("DISPLAY")); + /* + * we would prefer to get the x,y info in the above event. + * At the moment we don't get detail params for "toolkit" events, + * so for testing purposes we use XQueryPointer. Actual apps + * probably shouldn't do this. + */ + XQueryPointer (display, + DefaultRootWindow (display), + &root_return, &child_return, + &x, &y, + &win_x, &win_y, + &mask_return); + + fprintf (stderr, "screen review event %s at %d, %d\n", event->type, + x, y); + fprintf (stderr, "[%s]\n", + get_screen_review_line_at (x, y)); +} + +void +test_exit () +{ + SPI_deregisterGlobalEventListenerAll (mouseclick_listener); + AccessibleEventListener_unref (mouseclick_listener); +} -- 2.7.4