Revved to 1.0.0; at-spi 1.0 is now API frozen.
authorbillh <billh@e2bd861d-eb25-0410-b326-f6ed22b6b98c>
Mon, 3 Jun 2002 14:44:55 +0000 (14:44 +0000)
committerbillh <billh@e2bd861d-eb25-0410-b326-f6ed22b6b98c>
Mon, 3 Jun 2002 14:44:55 +0000 (14:44 +0000)
Revved registry activation (OAFIID) version to 1.0.

API changes:

Accessibility_Registry.idl: registerKeystrokeListener now returns a boolean.

Enhancements to the screen review demo/test code.

git-svn-id: http://svn.gnome.org/svn/at-spi/trunk@309 e2bd861d-eb25-0410-b326-f6ed22b6b98c

13 files changed:
ChangeLog
atk-bridge/bridge.c
configure.in
cspi/bonobo/cspi-bonobo.c
idl/Accessibility_Registry.idl
registryd/Accessibility_Registry.server.in.in
registryd/deviceeventcontroller.c
registryd/registry-main.c
registryd/registry.c
registryd/registryd.c
test/app.c
test/at.c
test/screen-review-test.c

index 42439fc..c70fa91 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,72 @@
+2002-06-02  Bill Haneman  <bill.haneman@sun.com>
+
+       AT-SPI 1.0 API FINAL: at-spi 1.0 is now
+       API frozen.
+       
+       * configure.in: Revved to 1.0.0.
+
+       * idl/Accessibility_Registry.idl:
+       (registerKeystrokeListener):
+       Added boolean return value.
+       
+       * registryd/registry.c:
+       (notify_listeners_cb): Minor fix to debug output.
+
+       * registryd/Accessibility_Registry.server.in.in:
+       Revved version number in OAFIID to 1.0.
+
+       * registryd/registryd.c:
+       (main):
+       Use new OAFIID version.
+
+       * cspi/bonobo/cspi-bonobo.c:
+       (cspi_init):
+       Use new OAFIID version.
+
+       * test/at.c:
+       (main):
+       * test/app.c:
+       (main):
+       * atk-bridge/bridge.c:
+       (atk_bridge_init):
+       Use new OAFIID version.
+
+       * registryd/deviceeventcontroller.c:
+       (impl_register_keystroke_listener):
+       Added CORBA_boolean return value.
+       (spi_controller_register_device_listener):
+       Added gboolean return value.
+       (spi_controller_register_global_keygrabs):
+       Added gboolean return value.
+       (spi_key_set_contains_key):
+       Added implementation for many more control keys,
+       for instance F1-F12, arrow keys, End, Home, Page_Up,
+       Page_Down, Escape.
+       [TODO: some still not implemented]. 
+       
+       * text/screen-review-test.c:
+       (text_chunk_pad_string):
+       New function: it provides mapping between coordinate
+       positions of text chunks and character positions in the
+       screen-review-line output string.
+       (text_chunk_to_string):
+       New function, calls text_chunk_pad_string with various
+       pad/delimiter characters. Pushbuttons are delimited with
+       square brackets, Frames with vertical 'pipe' lines, and 
+       other text with double quotes.
+       (text_chunk_list_to_string):
+       Calls new function text_chunk_to_string.
+       (toplevel_composite):
+       New function to composite layers CANVAS through
+       POPUP in each toplevel (other layers are composited
+       across toplevels, i.e. BACKGROUND and OVERLAY).
+       (review_buffer_composite):
+       Revise to use new methods.
+       
+       
+       
+       
+
 2002-05-31  Laszlo Peter  <laca@sun.com>
 
        * configure.in: add the Xtst libdir to the runpath on Solaris,
@@ -9,6 +78,15 @@
 
 2002-05-29  Bill Haneman  <bill.haneman@sun.com>
 
+       * test/screen-review-test.c:
+       (text_chunk_pad_string):
+       Added method, which pads the string according to the 
+       text bounds of the chunk.  It also takes a 3-character
+       string as a param which indicates the characters to be 
+       used for start, padding, and end delimitation of the chunk.
+       (text_chunk_to_string):
+       Changed to use text_chunk_pad_string.
+       
        * configure.in: Fixed bug in AC_OUTPUT that was
         causing path substitution in Accessibility_Registry.server
        to fail.
index 91f8749..974b483 100644 (file)
@@ -110,7 +110,7 @@ atk_bridge_init (gint *argc, gchar **argv[])
   CORBA_exception_init(&ev);
 
   registry = bonobo_activation_activate_from_id (
-         "OAFIID:Accessibility_Registry:proto0.1", 0, NULL, &ev);
+         "OAFIID:Accessibility_Registry:1.0", 0, NULL, &ev);
   
   if (ev._major != CORBA_NO_EXCEPTION)
     {
index 5749679..7a75488 100644 (file)
@@ -1,8 +1,8 @@
 AC_INIT(idl/Accessibility.idl)
 
-AT_SPI_MAJOR_VERSION=0
-AT_SPI_MINOR_VERSION=13
-AT_SPI_MICRO_VERSION=1
+AT_SPI_MAJOR_VERSION=1
+AT_SPI_MINOR_VERSION=0
+AT_SPI_MICRO_VERSION=0
 AT_SPI_INTERFACE_AGE=0
 AT_SPI_BINARY_AGE=0
 AT_SPI_VERSION="$AT_SPI_MAJOR_VERSION.$AT_SPI_MINOR_VERSION.$AT_SPI_MICRO_VERSION"
index c5bd670..eb51feb 100644 (file)
@@ -86,7 +86,7 @@ cspi_init (void)
       g_error ("Could not initialize Bonobo");
     }
 
-  obj_id = "OAFIID:Accessibility_Registry:proto0.1";
+  obj_id = "OAFIID:Accessibility_Registry:1.0";
 
   CORBA_exception_init (&ev);
 
index 6e8e554..74165cf 100644 (file)
@@ -226,13 +226,13 @@ module Accessibility {
         *            receive the events synchronously, potentially consuming them,
         *            or just be notified asynchronously of those events that have
         *            been generated.
-        * Returns: void
+        * Returns: %true if successful, %false if not
          *
          * Register to intercept keyboard events, and either pass them on or
          * consume them. 
          *
         **/
-        void registerKeystrokeListener (in DeviceEventListener listener,
+        boolean registerKeystrokeListener (in DeviceEventListener listener,
                                        in KeySet keys,
                                        in ControllerEventMask mask,
                                        in KeyEventTypeSeq type,
index b5e70fe..368186b 100644 (file)
@@ -1,5 +1,5 @@
 <oaf_info>
-       <oaf_server iid="OAFIID:Accessibility_Registry:proto0.1"
+       <oaf_server iid="OAFIID:Accessibility_Registry:1.0"
                    type="exe" location="@REGISTRYD_PATH@/at-spi-registryd">
                <oaf_attribute name="repo_ids" type="stringv">
                        <item value="IDL:Bonobo/Unknown:1.0"/>
index bad5705..c0bc354 100644 (file)
@@ -84,7 +84,7 @@ typedef struct {
 static void     spi_controller_register_with_devices          (SpiDEController           *controller);
 static gboolean spi_controller_update_key_grabs               (SpiDEController           *controller,
                                                               Accessibility_DeviceEvent *recv);
-static void     spi_controller_register_device_listener       (SpiDEController           *controller,
+static gboolean spi_controller_register_device_listener       (SpiDEController           *controller,
                                                               DEControllerListener      *l,
                                                               CORBA_Environment         *ev);
 static void     spi_device_event_controller_forward_key_event (SpiDEController           *controller,
@@ -239,6 +239,9 @@ handle_keygrab (SpiDEController         *controller,
   if (key_listener->keys->_length == 0) /* special case means AnyKey/AllKeys */
     {
       grab_mask.key_val = AnyKey;
+#ifdef SPI_DEBUG
+      fprintf (stderr, "AnyKey grab!"); */
+#endif
       process_cb (controller, &grab_mask);
     }
   else
@@ -268,12 +271,12 @@ handle_keygrab (SpiDEController         *controller,
     }
 }
 
-static void
+static gboolean
 spi_controller_register_global_keygrabs (SpiDEController         *controller,
                                         DEControllerKeyListener *key_listener)
 {
   handle_keygrab (controller, key_listener, _register_keygrab);
-  spi_controller_update_key_grabs (controller, NULL);
+  return spi_controller_update_key_grabs (controller, NULL);
 }
 
 static void
@@ -284,7 +287,7 @@ spi_controller_deregister_global_keygrabs (SpiDEController         *controller,
   spi_controller_update_key_grabs (controller, NULL);
 }
 
-static void
+static gboolean
 spi_controller_register_device_listener (SpiDEController      *controller,
                                         DEControllerListener *listener,
                                         CORBA_Environment    *ev)
@@ -299,12 +302,15 @@ spi_controller_register_device_listener (SpiDEController      *controller,
                                                  key_listener);
       if (key_listener->mode->global)
         {
-         spi_controller_register_global_keygrabs (controller, key_listener);   
+         return spi_controller_register_global_keygrabs (controller, key_listener);    
        }
+      else
+             return TRUE;
       break;
     default:
       break;
   }
+  return FALSE; 
 }
 
 static GdkFilterReturn
@@ -355,6 +361,7 @@ spi_controller_register_with_devices (SpiDEController *controller)
   x_default_error_handler = XSetErrorHandler (_spi_controller_device_error_handler);
 }
 
+#define SPI_KEYEVENT_DEBUG
 static gboolean
 spi_key_set_contains_key (Accessibility_KeySet            *key_set,
                          const Accessibility_DeviceEvent *key_event)
@@ -560,6 +567,69 @@ spi_keystroke_from_x_key_event (XKeyEvent *x_key_event)
       case XK_Return:
         key_event.event_string = CORBA_string_dup ("Return");
        break;
+      case XK_Home:
+        key_event.event_string = CORBA_string_dup ("Home");
+       break;
+      case XK_Page_Down:
+        key_event.event_string = CORBA_string_dup ("Page_Down");
+       break;
+      case XK_Page_Up:
+        key_event.event_string = CORBA_string_dup ("Page_Up");
+       break;
+      case XK_F1:
+        key_event.event_string = CORBA_string_dup ("F1");
+       break;
+      case XK_F2:
+        key_event.event_string = CORBA_string_dup ("F2");
+       break;
+      case XK_F3:
+        key_event.event_string = CORBA_string_dup ("F3");
+       break;
+      case XK_F4:
+        key_event.event_string = CORBA_string_dup ("F4");
+       break;
+      case XK_F5:
+        key_event.event_string = CORBA_string_dup ("F5");
+       break;
+      case XK_F6:
+        key_event.event_string = CORBA_string_dup ("F6");
+       break;
+      case XK_F7:
+        key_event.event_string = CORBA_string_dup ("F7");
+       break;
+      case XK_F8:
+        key_event.event_string = CORBA_string_dup ("F8");
+       break;
+      case XK_F9:
+        key_event.event_string = CORBA_string_dup ("F9");
+       break;
+      case XK_F10:
+        key_event.event_string = CORBA_string_dup ("F10");
+       break;
+      case XK_F11:
+        key_event.event_string = CORBA_string_dup ("F11");
+       break;
+      case XK_F12:
+        key_event.event_string = CORBA_string_dup ("F12");
+       break;
+      case XK_End:
+        key_event.event_string = CORBA_string_dup ("End");
+       break;
+      case XK_Escape:
+        key_event.event_string = CORBA_string_dup ("Escape");
+       break;
+      case XK_Up:
+        key_event.event_string = CORBA_string_dup ("Up");
+       break;
+      case XK_Down:
+        key_event.event_string = CORBA_string_dup ("Down");
+       break;
+      case XK_Left:
+        key_event.event_string = CORBA_string_dup ("Left");
+       break;
+      case XK_Right:
+        key_event.event_string = CORBA_string_dup ("Right");
+       break;
       default:
         if (XLookupString (x_key_event, cbuf, cbuf_bytes, &keysym, NULL) > 0)
           {
@@ -706,7 +776,7 @@ spi_device_event_controller_object_finalize (GObject *object)
  * CORBA Accessibility::DEController::registerKeystrokeListener
  *     method implementation
  */
-static void
+static CORBA_boolean
 impl_register_keystroke_listener (PortableServer_Servant                  servant,
                                  const Accessibility_DeviceEventListener l,
                                  const Accessibility_KeySet             *keys,
@@ -723,8 +793,8 @@ impl_register_keystroke_listener (PortableServer_Servant                  servan
           (void *) l, (unsigned long) mask);
 #endif
   dec_listener = spi_dec_key_listener_new (l, keys, mask, type, mode, ev);
-  spi_controller_register_device_listener (
-    controller, (DEControllerListener *) dec_listener, ev);
+  return spi_controller_register_device_listener (
+         controller, (DEControllerListener *) dec_listener, ev);
 }
 
 
index 47ac78b..71b86f6 100644 (file)
@@ -41,7 +41,7 @@ main (int argc, char **argv)
       g_error ("Could not initialize oaf / Bonobo");
     }
 
-  obj_id = "OAFIID:Accessibility_Registry:proto0.1";
+  obj_id = "OAFIID:Accessibility_Registry:1.0";
 
   registry = spi_registry_new ();
 
index c4e93d9..d74fc8c 100644 (file)
@@ -23,6 +23,7 @@
 /* registry.c: the main accessibility service registry implementation */
 
 #undef SPI_LISTENER_DEBUG
+#undef SPI_DEBUG
 
 #include <config.h>
 #ifdef SPI_DEBUG
@@ -351,12 +352,13 @@ impl_accessibility_registry_register_global_event_listener (
        const CORBA_char           *event_name,
        CORBA_Environment          *ev)
 {
-  SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant));
+  SpiRegistry *registry = SPI_REGISTRY (bonobo_object_from_servant (servant)); 
   SpiListenerStruct *ls = spi_listener_struct_new (listener, ev);
   EventTypeStruct etype;
   GList          **list;
 
 #ifdef SPI_DEBUG
+  fprintf (stderr, "registering");
   fprintf (stderr, "registering for events of type %s\n", event_name);
 #endif
 
@@ -560,8 +562,9 @@ notify_listeners_cb (GList * const *list, gpointer user_data)
       (ls->event_type_quark == ctx->etype.minor))
     {
 #ifdef SPI_DEBUG
-      fprintf (stderr, "notifying listener %d\n", g_list_index (listeners, l->data));
-      s = Accessibility_Accessible__get_name (ctx->source, ev);
+      fprintf (stderr, "notifying listener %d\n", 0);
+/* g_list_index (list, l->data)); */
+      s = Accessibility_Accessible__get_name (ctx->source, ctx->ev);
       fprintf (stderr, "event source name %s\n", s);
       CORBA_free (s);
 #endif
index 228b474..f5ab438 100644 (file)
@@ -42,7 +42,7 @@ main (int argc, char **argv)
       g_error ("Could not initialize oaf / Bonobo");
     }
 
-  obj_id = "OAFIID:Accessibility_Registry:proto0.1";
+  obj_id = "OAFIID:Accessibility_Registry:1.0";
 
   registry = spi_registry_new ();
 
index c11b29e..9bf749d 100644 (file)
@@ -67,7 +67,7 @@ main(int argc, char **argv)
         e.source = bonobo_object_corba_objref ( bonobo_object (accessible));
         e.type = CORBA_string_dup ("focus:");
 
-        obj_id = "OAFIID:Accessibility_Registry:proto0.1";
+        obj_id = "OAFIID:Accessibility_Registry:1.0";
 
         oclient = bonobo_activation_activate_from_id (obj_id, 0, NULL, &ev);
         if (ev._major != CORBA_NO_EXCEPTION) {
index 1c2c185..418ce81 100644 (file)
--- a/test/at.c
+++ b/test/at.c
@@ -62,7 +62,7 @@ main(int argc, char **argv)
             g_error ("Could not initialize Bonobo");
           }
 
-        obj_id = "OAFIID:Accessibility_Registry:proto0.1";
+        obj_id = "OAFIID:Accessibility_Registry:1.0";
 
         oclient = bonobo_activation_activate_from_id (obj_id, 0, NULL, &ev);
         if (ev._major != CORBA_NO_EXCEPTION) {
index af19d52..7cde0ec 100644 (file)
@@ -30,8 +30,7 @@
  *
  * Issues:
  *
- * * bounds now fail to include window border decoration
- *         (which we can't really know about yet).
+ * * there are bugs in the compositing code.
  *
  * * brute-force algorithm uses no client-side cache; performance mediocre.
  *
  *          the active window is not always on top (i.e. autoraise is
  *          not enabled).
  *
- * * text-bearing objects that don't implement AccessibleText (such as buttons)
- *          don't get their text clipped since we don't know the character
- *          bounding boxes.
- *
  * * can't know about "inaccessible" objects that may be obscuring the
  *          accessible windows (inherent to API-based approach).
  *
- * * this implementation doesn't worry about text column-alignment, it generates
- *          review lines of more-or-less arbitrary length.  The x-coordinate
- *          info is preserved in the reviewBuffers list structures, but the
- *          "buffer-to-string" algorithm (currently) ignores it.
+ * * text column alignment is crude since it relies on a hard-coded
+ *          (and arbitrary) ratio of horizontal pixels to character columns.
+ *          For small proportional fonts, overruns are likely to result in
+ *          occasional review lines that exceed the standard length.
  *
  * * (others).
  */
@@ -116,7 +111,7 @@ typedef struct _TextChunk {
        BoundaryRect    end_char_bounds;
 } TextChunk;
 
-typedef struct _ScreenReviewBuffer { /* TODO: implement */
+typedef struct _ScreenReviewBuffer { 
        GList *text_chunks;
 } ScreenReviewBuffer;
 
@@ -185,7 +180,7 @@ clip_bounds_clone (BoundaryRect *bounds[])
        BoundaryRect **bounds_clone;
        bounds_clone = (BoundaryRect **)
                g_new0 (gpointer, SPI_LAYER_LAST_DEFINED);
-       for (i = 0; i < SPI_LAYER_LAST_DEFINED; ++i) {
+       for (i = 1; i < SPI_LAYER_LAST_DEFINED; ++i) {
                bounds_clone[i] = g_new0 (BoundaryRect, 1);
                *bounds_clone[i] = *bounds[i];
        }
@@ -746,7 +741,6 @@ string_guess_clip (TextChunk *chunk)
                                                        &b.x, &b.y,
                                                        &b.width, &b.height,
                                                        SPI_COORD_TYPE_SCREEN);
-                       /* TODO: finish this! */
                        start_offset = len * (chunk->text_bounds.x - b.x) / b.width;
                        end_offset = len * (chunk->text_bounds.x +
                                            chunk->text_bounds.width - b.x) / b.width;
@@ -829,20 +823,82 @@ text_chunk_get_clipped_string (TextChunk *chunk)
        return string;
 }
 
+
+static char*
+text_chunk_pad_string (TextChunk *chunk, char *string, glong offset, const char *pad_chars)
+{
+       char *s = "";
+       char *cp;
+       char startbuf[6], padbuf[6], endbuf[6];
+       int pixels_per_column = 6;
+        /* this is an arbitrary pixel-to-textcolumn mapping at present */
+       glong end_padding;
+       gint howmany;
+       howmany = g_unichar_to_utf8 (g_utf8_get_char (pad_chars), startbuf);
+       startbuf[howmany] = '\0';
+       g_assert (howmany < 7 && howmany > 0);
+       cp = g_utf8_find_next_char (pad_chars, NULL);
+       howmany = g_unichar_to_utf8 (g_utf8_get_char (cp), padbuf);
+       padbuf[howmany] = '\0';
+       g_assert (howmany < 7 && howmany > 0);
+       cp = g_utf8_find_next_char (cp, NULL);
+       howmany = g_unichar_to_utf8 (g_utf8_get_char (cp), endbuf);
+       endbuf[howmany] = '\0';
+       g_assert (howmany < 7 && howmany > 0);
+       end_padding = chunk->clip_bounds.x / pixels_per_column; 
+       while (offset < end_padding - 1) {
+               s = g_strconcat (s, padbuf, NULL); /* could be more efficient */
+               ++offset;
+       }
+       s = g_strconcat (s, startbuf, string, NULL);
+       offset += g_utf8_strlen (string, -1) + 1;
+       end_padding = chunk->text_bounds.x / pixels_per_column; 
+       while (offset < end_padding) {
+               s = g_strconcat (s, padbuf, NULL); /* could be more efficient */
+               ++offset;
+       }
+       end_padding = (chunk->clip_bounds.x + chunk->clip_bounds.width) /
+               pixels_per_column;
+       while (offset < end_padding - 1) {
+               s = g_strconcat (s, padbuf, NULL); /* could be more efficient */
+               ++offset;
+       }
+       s = g_strconcat (s, endbuf, NULL);
+       return s;
+}
+
+static char*
+text_chunk_to_string (TextChunk *chunk, glong offset)
+{
+       char *s = NULL;
+       if (chunk->string) {
+               s = text_chunk_get_clipped_string (chunk);
+               if (chunk->clip_bounds.role == SPI_ROLE_PUSH_BUTTON) {
+                       s = text_chunk_pad_string (chunk, s, offset, "[ ]");
+               } else if (chunk->clip_bounds.role == SPI_ROLE_FRAME) {
+                       s = text_chunk_pad_string (chunk, s, offset, "| |");
+               } else if (chunk->clip_bounds.role == SPI_ROLE_TEXT) {
+                       s = text_chunk_pad_string (chunk, s, offset, "\" \"");
+               } else {
+                       s = text_chunk_pad_string (chunk, s, offset, "   ");
+               }
+       }
+       return s;
+}
+
 static char*
 text_chunk_list_to_string (GList *iter)
 {
        char *s = "";
        char *string;
        TextChunk *chunk = NULL;
+       int offset = 0;
        while (iter) {
                chunk = (TextChunk *)iter->data;
-               if (chunk /* && chunk->string */) {
-                       string = text_chunk_get_clipped_string (chunk);
+               if (chunk) {
+                       string = text_chunk_to_string (chunk, g_utf8_strlen (s, -1));
                        if (string)
-                               s = g_strconcat (s, "|", string, NULL);
-                       else /* XXX test */
-                               s = g_strconcat (s, ":", NULL);
+                               s = g_strconcat (s, string, NULL);
                }
                iter = iter->next;
        }
@@ -850,15 +906,54 @@ text_chunk_list_to_string (GList *iter)
        return s;
 }
 
+#define COMPOSITE_DEBUG
+
+static void
+toplevel_composite (ScreenReviewBuffer *buffers[])
+{
+       int i;
+       GList *chunk_list, *iter;
+       TextChunk *chunk;
+
+       chunk_list = buffers[SPI_LAYER_CANVAS]->text_chunks;
+       for (i = SPI_LAYER_MDI; i < SPI_LAYER_OVERLAY; ++i) {
+               iter = buffers[i]->text_chunks;
+#ifdef COMPOSITE_DEBUG
+               fprintf (stderr, "layer %d has %d chunks\n",
+                        i, g_list_length (iter));
+#endif         
+               while (iter) {
+                       chunk = (TextChunk *) iter->data;
+                       if (chunk) {
+#ifdef COMPOSITE_DEBUG
+                               fprintf (stderr, "inserting chunk <%s>\n",
+                                        chunk->string ? chunk->string : "<null>");
+#endif
+                               chunk_list =
+                                       text_chunk_list_insert_chunk (chunk_list,
+                                                                     chunk);
+                       }
+                       iter = iter->next;
+               }
+       }
+}
+
 static char*
 review_buffer_composite (ScreenReviewBuffer *buffers[])
 {
+       /* TODO: FIXME: something is wrong here, compositing fails */
        int i;
        GList *chunk_list, *iter;
        TextChunk *chunk;
-#ifdef NEED_TO_FIX_THIS        
-       chunk_list = buffers[0]->text_chunks;
-       for (i = 1; i < SPI_LAYER_LAST_DEFINED; ++i) {
+       chunk_list = buffers[SPI_LAYER_BACKGROUND]->text_chunks;
+       for (i = 2; i < SPI_LAYER_LAST_DEFINED; ++i) {
+               if (i == SPI_LAYER_WIDGET) i = SPI_LAYER_OVERLAY;
+               /*
+                * Q: why skip these layers ?
+                * A: since layers WIDGET, MDI, and POPUP have already been
+                *  composited into layer CANVAS for each toplevel before this
+                *  routine is called.
+                */
                iter = buffers[i]->text_chunks;
 #ifdef CLIP_DEBUG
                fprintf (stderr, "layer %d has %d chunks\n",
@@ -878,7 +973,7 @@ review_buffer_composite (ScreenReviewBuffer *buffers[])
                        iter = iter->next;
                }
        }
-#endif
+       
        chunk_list = buffers[SPI_LAYER_WIDGET]->text_chunks;
        return text_chunk_list_to_string (chunk_list);
 }
@@ -898,7 +993,7 @@ get_screen_review_line_at (int x, int y)
   GTimer *timer = g_timer_new ();
   int i;
 
-  for (i = 0; i < SPI_LAYER_LAST_DEFINED; ++i) {
+  for (i = 1; i < SPI_LAYER_LAST_DEFINED; ++i) {
          reviewBuffers[i] = g_new0 (ScreenReviewBuffer, 1);
          clip_bounds[i] = g_new0 (BoundaryRect, 1);
          clip_bounds[i]->isClipped = FALSE;
@@ -960,11 +1055,13 @@ get_screen_review_line_at (int x, int y)
                                                      &toplevel_bounds.height,
                                                      SPI_COORD_TYPE_SCREEN);
                      toplevel_bounds.isEmpty = FALSE;
-                     for (i = 0; i < SPI_LAYER_LAST_DEFINED; ++i) {
+                     for (i = 1; i < SPI_LAYER_LAST_DEFINED; ++i) {
                              *clip_bounds[i] = toplevel_bounds;
                      }
                      clip_into_buffers (toplevel, clip_bounds,
                                     reviewBuffers, x, y);
+
+                     toplevel_composite (reviewBuffers);
 #ifdef CHUNK_LIST_DEBUG
                      fprintf (stderr, "toplevel clip done\n");
                      debug_chunk_list (reviewBuffers[SPI_LAYER_WIDGET]->text_chunks);