gmag-events.c

Go to the documentation of this file.
00001 /*
00002  * GNOME-MAG Magnification service for GNOME
00003  *
00004  * Copyright 2006 Carlos Eduardo Rodrigues Diógenes
00005  * Copyright 2004 Sun Microsystems Inc. (damage-client.c)
00006  * Copyright 2001 Sun Microsystems Inc. (magnifier.c)
00007  *
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Library General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2 of the License, or (at your option) any later version.
00012  *
00013  * This library is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016  * Library General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Library General Public
00019  * License along with this library; if not, write to the
00020  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00021  * Boston, MA 02111-1307, USA.
00022  */
00023 
00024 #include "config.h"
00025 #include "magnifier.h"
00026 #include "magnifier-private.h"
00027 #include "gmag-events.h"
00028 
00029 #include <stdlib.h>
00030 
00031 #include <X11/Xlib.h>
00032 #include <X11/extensions/Xfixes.h>
00033 #include <X11/extensions/Xdamage.h>
00034 #ifdef HAVE_COMPOSITE
00035 #include <X11/extensions/Xrender.h>
00036 #include <X11/extensions/Xcomposite.h>
00037 #endif /* HAVE_COMPOSITE */
00038 
00039 #include <glib.h>
00040 
00041 #include <gdk/gdkx.h>
00042 #include <gtk/gtk.h>
00043 
00044 static Display       *dpy_conn = NULL;
00045 static guint          dpy_gsource = 0;
00046 static Window         root_window, mag_window;
00047 
00048 static gboolean       use_damage, use_composite;
00049 
00050 gint                  fixes_event_base = 0, fixes_error_base;
00051 static gint           damage_event_base, damage_error_base;
00052 static Damage         root_window_damage;
00053 static XserverRegion  gmag_events_tmp_region;
00054 #ifdef HAVE_COMPOSITE
00055 static GQueue        *mag_windows_list;
00056 static Damage         off_screen_damage;
00057 static Picture        off_screen_picture;
00058 static XserverRegion  off_screen_region;
00059 static XserverRegion  tmp_region, new_region, old_region, exp_region;
00060 #endif /* HAVE_COMPOSITE */
00061 
00062 #define EVENTS_DEBUG
00063 #undef  EVENTS_DEBUG
00064 
00065 #ifdef  EVENTS_DEBUG
00066 
00067 #define DAMAGE_DEBUG
00068 #undef  DAMAGE_DEBUG
00069 
00070 #ifdef HAVE_COMPOSITE
00071 #define COMPOSITE_DEBUG
00072 #undef  COMPOSITE_DEBUG
00073 #endif /* HAVE_COMPOSITE */
00074 
00075 #define CURSOR_DEBUG
00076 #undef  CURSOR_DEBUG
00077 #define XFIXES_DEBUG
00078 #undef  XFIXES_DEBUG
00079 
00080 #endif /* EVENTS_DEBUG */
00081 
00082 #ifdef HAVE_COMPOSITE
00083 
00084 /*
00085  * GCompareFunc used in g_queue_find_custom to find windows in
00086  * mag_windows_list.
00087  */
00088 static gint
00089 gmag_events_g_compare_func (GmagWinPtr pgmag_win, Window xwin)
00090 {
00091         if (pgmag_win->xwin == xwin)
00092                 return 0;
00093 
00094         return 1;
00095 }
00096 
00097 /*
00098  * Calculates the clip for all windows in mag_windows_list.
00099  */
00100 static void
00101 gmag_events_calculate_windows_clip ()
00102 {
00103         GList         *elem = NULL;
00104         XserverRegion clipSum;
00105 
00106         clipSum = XFixesCreateRegion (dpy_conn, 0, 0);
00107         elem = g_queue_peek_tail_link (mag_windows_list);
00108         if (!elem) {
00109                 XFixesDestroyRegion (dpy_conn, clipSum);
00110                 return;
00111         }
00112         do {
00113                 GmagWinPtr pgmag_win = (GmagWinPtr) elem->data;
00114                 if (pgmag_win->pic)
00115                         if (pgmag_win->attr.map_state == IsViewable) {
00116                                 XFixesCopyRegion (
00117                                         dpy_conn,
00118                                         pgmag_win->clip,
00119                                         XFixesCreateRegionFromWindow (
00120                                                 dpy_conn,
00121                                                 pgmag_win->xwin,
00122                                                 WindowRegionBounding));
00123                                 XFixesTranslateRegion (
00124                                         dpy_conn,
00125                                         pgmag_win->clip, pgmag_win->attr.x,
00126                                         pgmag_win->attr.y);
00127                                 XFixesCopyRegion (
00128                                         dpy_conn,
00129                                         pgmag_win->win_region,
00130                                         pgmag_win->clip);
00131                                 XFixesSubtractRegion (
00132                                         dpy_conn,
00133                                         pgmag_win->clip, pgmag_win->clip,
00134                                         clipSum);
00135                                 XFixesUnionRegion (
00136                                         dpy_conn,
00137                                         clipSum, clipSum,
00138                                         pgmag_win->win_region);
00139                         }
00140         } while ((elem = g_list_previous (elem)));
00141         XFixesDestroyRegion (dpy_conn, clipSum);
00142 }
00143 
00144 /*
00145  * Calculates the clip of a single window in mag_windows_list.
00146  */
00147 static void
00148 gmag_events_calculate_window_clip (GmagWinPtr pgmag_win_newclip)
00149 {
00150         GList         *elem = NULL;
00151         XserverRegion clipSum;
00152 
00153         clipSum = XFixesCreateRegion (dpy_conn, 0, 0);
00154         elem = g_queue_peek_tail_link (mag_windows_list);
00155         if (!elem) {
00156                 XFixesDestroyRegion (dpy_conn, clipSum);
00157                 return;
00158         }
00159         do {
00160                 GmagWinPtr pgmag_win = (GmagWinPtr) elem->data;
00161                 if (pgmag_win->xwin == pgmag_win_newclip->xwin) {
00162                         /* the window that must have a new clip was founded */
00163                         XFixesCopyRegion (
00164                                 dpy_conn,
00165                                 pgmag_win->clip,
00166                                 XFixesCreateRegionFromWindow (
00167                                         dpy_conn,
00168                                         pgmag_win->xwin,
00169                                         WindowRegionBounding));
00170                         XFixesTranslateRegion (dpy_conn,
00171                                                pgmag_win->clip,
00172                                                pgmag_win->attr.x,
00173                                                pgmag_win->attr.y);
00174                         XFixesCopyRegion (dpy_conn,
00175                                           pgmag_win->win_region,
00176                                           pgmag_win->clip);
00177                         XFixesSubtractRegion (dpy_conn,
00178                                               pgmag_win->clip, pgmag_win->clip,
00179                                               clipSum);
00180                         break;
00181                 }
00182                 if (pgmag_win->pic)
00183                         if (pgmag_win->attr.map_state == IsViewable) {
00184                                 XFixesUnionRegion (
00185                                         dpy_conn,
00186                                         clipSum, clipSum,
00187                                         pgmag_win->win_region);
00188                         }
00189         } while ((elem = g_list_previous (elem)));
00190         XFixesDestroyRegion (dpy_conn, clipSum);
00191 }
00192 
00193 /*
00194  * Paint a window. If region is None, the window clip region is painted, else
00195  * the intersection of the window clip region and region is painted.
00196  */
00197 static void
00198 gmag_events_paint_window (GmagWinPtr pgmag_win, XserverRegion region)
00199 {
00200         static XserverRegion final_clip = None;
00201 
00202         if (!pgmag_win->damaged && !region)
00203                 return;
00204         if (!pgmag_win->pic)
00205                 return;
00206         if (pgmag_win->attr.map_state != IsViewable)
00207                 return;
00208 
00209         if (!final_clip)
00210                 final_clip = XFixesCreateRegion (
00211                         dpy_conn, 0, 0);
00212 
00213         XFixesSetRegion (dpy_conn, final_clip, 0, 0);
00214 
00215         if (region) {
00216                 XFixesIntersectRegion (dpy_conn,
00217                                        final_clip, region, pgmag_win->clip);
00218                 XFixesSetPictureClipRegion (dpy_conn,
00219                                             pgmag_win->pic,
00220                                             -(pgmag_win->attr.x),
00221                                             -(pgmag_win->attr.y), final_clip);
00222         } else
00223                 XFixesSetPictureClipRegion (dpy_conn,
00224                                             pgmag_win->pic,
00225                                             -(pgmag_win->attr.x),
00226                                             -(pgmag_win->attr.y),
00227                                             pgmag_win->clip);
00228         XRenderComposite (dpy_conn, PictOpSrc,
00229                           pgmag_win->pic, None, off_screen_picture,
00230                           0, 0, 0, 0, pgmag_win->attr.x, pgmag_win->attr.y,
00231                           pgmag_win->attr.width, pgmag_win->attr.height);
00232 }
00233 
00234 /*
00235  * Paint all the windows in mag_windows_list with the specified
00236  * exposedRegion.
00237  */
00238 static void
00239 gmag_events_paint_windows (XserverRegion exposedRegion)
00240 {
00241         GList      *elem;
00242         GmagWinPtr  pgmag_win;
00243 
00244         elem = g_queue_peek_head_link (mag_windows_list);
00245 
00246         while (elem) {
00247                 pgmag_win = (GmagWinPtr) elem->data;
00248                 gmag_events_paint_window (pgmag_win, exposedRegion);
00249                 elem = g_list_next (elem);
00250         }
00251 }
00252 
00253 /*
00254  * Sometimes XGetWindowAttributes fail (when the window is destroied), so we
00255  * put default values in it to not have problems in other parts of the program.
00256  * I think that only some ones need to be setted, but this was copied from
00257  * Compiz, so...
00258  */
00259 static void
00260 gmag_events_set_default_window_attributes (XWindowAttributes *wa)
00261 {
00262         wa->x                     = 0;
00263         wa->y                     = 0;
00264         wa->width                 = 1;
00265         wa->height                = 1;
00266         wa->border_width          = 0;
00267         wa->depth                 = 0;
00268         wa->visual                = NULL;
00269         wa->root                  = None;
00270         wa->class                 = InputOnly;
00271         wa->bit_gravity           = NorthWestGravity;
00272         wa->win_gravity           = NorthWestGravity;
00273         wa->backing_store         = NotUseful;
00274         wa->backing_planes        = 0;
00275         wa->backing_pixel         = 0;
00276         wa->save_under            = FALSE;
00277         wa->colormap              = None;
00278         wa->map_installed         = FALSE;
00279         wa->map_state             = IsUnviewable;
00280         wa->all_event_masks       = 0;
00281         wa->your_event_mask       = 0;
00282         wa->do_not_propagate_mask = 0;
00283         wa->override_redirect     = TRUE;
00284         wa->screen                = NULL;
00285 }
00286 
00287 /*
00288  * Creates the necessary information of a redirected window and add it to
00289  * mag_windows_list.
00290  */
00291 static void
00292 gmag_events_add_window (Window xwin)
00293 {
00294         GmagWinPtr                new;
00295         XRenderPictureAttributes  pic_attr;
00296         XRenderPictFormat        *format;
00297 
00298         new = (GmagWinPtr) malloc (sizeof (GmagWin));
00299         if (!new)
00300                 g_error ("can't allocate GmagWin (struct _GmagWin)");
00301 
00302         if (!XGetWindowAttributes (dpy_conn, xwin,
00303                                    &new->attr))
00304                 gmag_events_set_default_window_attributes (&new->attr);
00305 
00306         new->xwin = xwin;
00307 
00308         if (new->attr.class == InputOnly) {
00309                 new->pic = None;
00310                 new->damage = None;
00311                 new->damaged = FALSE;
00312                 new->damaged_region = None;
00313         } else {
00314                 format = XRenderFindVisualFormat (
00315                         dpy_conn, new->attr.visual);
00316                 pic_attr.subwindow_mode = IncludeInferiors;
00317                 new->pic = XRenderCreatePicture (
00318                         dpy_conn, xwin, format,
00319                         CPSubwindowMode, &pic_attr);
00320                 new->damage = XDamageCreate (dpy_conn, xwin,
00321                                              XDamageReportDeltaRectangles);
00322                 new->damaged = TRUE;
00323                 new->damaged_region = XFixesCreateRegion (dpy_conn, 0, 0);
00324                 new->clip = XFixesCreateRegion (dpy_conn, 0, 0);
00325                 new->win_region = XFixesCreateRegion (dpy_conn, 0, 0);
00326         }
00327         
00328         g_queue_push_tail (mag_windows_list, new);
00329 }
00330 
00331 /*
00332  * Create the mag_windows_list querying the xserver for the actual
00333  * windows and adding them to the windows list.
00334  */
00335 static void
00336 gmag_events_create_windows_list ()
00337 {
00338         Window  root_return, parent_return, *children;
00339         guint   nchildren;
00340         gint    i;
00341 
00342         if (!mag_windows_list)
00343                 mag_windows_list = g_queue_new ();
00344 
00345         XGrabServer (dpy_conn);
00346         XSelectInput (dpy_conn, root_window,
00347                       SubstructureNotifyMask);
00348         XQueryTree (dpy_conn, root_window,
00349                     &root_return, &parent_return, &children, &nchildren);
00350         for (i = 0; i < nchildren; i++)
00351                 gmag_events_add_window (children[i]);
00352         XFree (children);
00353         XUngrabServer (dpy_conn);
00354 }
00355 
00356 /*
00357  * Destroy the window resources and remove it from the
00358  * mag_windows_list.
00359  */
00360 static void
00361 gmag_events_remove_window (Window xwin)
00362 {
00363         GList     *elem = NULL;
00364         GmagWinPtr pgmag_win;
00365 
00366         elem = g_queue_find_custom (mag_windows_list,
00367                                     (gconstpointer) xwin,
00368                                     (GCompareFunc) gmag_events_g_compare_func);
00369         if (elem) {
00370                 pgmag_win = (GmagWinPtr) elem->data;
00371                 g_queue_remove (mag_windows_list, pgmag_win);
00372                 XFixesDestroyRegion (dpy_conn,
00373                                      pgmag_win->clip);
00374                 XFixesDestroyRegion (dpy_conn,
00375                                      pgmag_win->win_region);
00376                 free (pgmag_win);
00377         }
00378 }
00379 
00380 /*
00381  * Add a window damaged region, making a union with the actual damaged region,
00382  * to the window in mag_windows_list.
00383  */
00384 static void
00385 gmag_events_add_win_damaged_region (Window xwin, XserverRegion region)
00386 {
00387         GList      *elem;
00388         GmagWinPtr  pgmag_win;
00389 
00390         elem = g_queue_find_custom (mag_windows_list,
00391                                     (gconstpointer) xwin,
00392                                     (GCompareFunc) gmag_events_g_compare_func);
00393         if (elem) {
00394                 pgmag_win = (GmagWinPtr) elem->data;
00395                 XFixesTranslateRegion (dpy_conn, region,
00396                                        pgmag_win->attr.x, pgmag_win->attr.y);
00397                 XFixesUnionRegion (dpy_conn,
00398                                    pgmag_win->damaged_region,
00399                                    pgmag_win->damaged_region, region);
00400                 pgmag_win->damaged = TRUE;
00401         }
00402 }
00403 
00404 /*
00405  * Paint all the windows that have some damage.
00406  */
00407 static void
00408 gmag_events_paint_damaged_windows ()
00409 {
00410         GList      *elem;
00411         GmagWinPtr  pgmag_win;
00412 
00413         elem = g_queue_peek_head_link (mag_windows_list);
00414         while (elem) {
00415                 pgmag_win = (GmagWinPtr) elem->data;
00416                 if (pgmag_win->damaged) {
00417                         gmag_events_paint_window (pgmag_win,
00418                                                   pgmag_win->damaged_region);
00419                         XFixesSetRegion (dpy_conn,
00420                                          pgmag_win->damaged_region, 0, 0);
00421                         pgmag_win->damaged = FALSE;
00422                 }
00423 
00424                 elem = g_list_next (elem);
00425         }
00426 }
00427 
00428 static void
00429 gmag_events_circulate_notify_handler (XEvent *ev)
00430 {
00431         GList      *elem;
00432         GmagWinPtr  pgmag_win;
00433 
00434 #ifdef COMPOSITE_DEBUG
00435         printf ("Received CirculateNotify event: 0x%x\n",
00436                 (guint) ev->xcirculate.window);
00437 #endif /* COMPOSITE_DEBUG */
00438         if (ev->xcirculate.window == mag_window) {
00439 #ifdef HAVE_OVERLAY
00440 #ifdef COMPOSITE_DEBUG
00441                 printf ("Overlay window = 0x%x\n",
00442                         (guint) gmag_events_overlay_window);
00443 #endif /* COMPOSITE_DEBUG */
00444 #endif /* HAVE_OVERLAY */
00445                 return;
00446         }
00447         elem = g_queue_find_custom (mag_windows_list,
00448                                     (gconstpointer) ev->xcirculate.window,
00449                                     (GCompareFunc) gmag_events_g_compare_func);
00450         if (elem) {
00451                 pgmag_win = (GmagWinPtr) elem->data;
00452                 g_queue_remove (mag_windows_list, pgmag_win);
00453                 if (ev->xcirculate.place == PlaceOnTop) {
00454                         g_queue_push_tail (mag_windows_list,
00455                                            pgmag_win);
00456                         if (pgmag_win->attr.map_state == IsViewable) {
00457                                 XFixesSubtractRegion (
00458                                         dpy_conn,
00459                                         tmp_region, pgmag_win->win_region,
00460                                         pgmag_win->clip);
00461                                 XFixesUnionRegion (
00462                                         dpy_conn,
00463                                         exp_region, exp_region, tmp_region);
00464                         }
00465                 } else {
00466                         g_queue_push_head (mag_windows_list,
00467                                            pgmag_win);
00468                         if (pgmag_win->attr.map_state == IsViewable)
00469                                 XFixesUnionRegion (
00470                                         dpy_conn,
00471                                         exp_region, exp_region,
00472                                         pgmag_win->clip);
00473                 }
00474         }
00475 }
00476 
00477 static void
00478 gmag_events_configure_notify_handler (XEvent *ev)
00479 {
00480         GList      *elem;
00481         GmagWinPtr  pgmag_win;
00482         
00483 #ifdef COMPOSITE_DEBUG
00484         printf ("Received ConfigureNotify event: 0x%x\n",
00485                 (guint) ev->xconfigure.window);
00486 #endif /* COMPOSITE_DEBUG */
00487         if (ev->xconfigure.window == mag_window) {
00488 #ifdef HAVE_OVERLAY
00489 #ifdef COMPOSITE_DEBUG
00490                 printf ("Overlay window = 0x%x\n",
00491                         (guint) gmag_events_overlay_window);
00492 #endif /* COMPOSITE_DEBUG */
00493 #endif /* HAVE_OVERLAY */
00494                 return;
00495         }
00496         elem = g_queue_find_custom (mag_windows_list,
00497                                     (gconstpointer) ev->xconfigure.window,
00498                                     (GCompareFunc) gmag_events_g_compare_func);
00499         if (elem) {
00500                 pgmag_win = (GmagWinPtr) elem->data;
00501                 if ((pgmag_win->attr.x != ev->xconfigure.x) ||
00502                     (pgmag_win->attr.y != ev->xconfigure.y) ||
00503                     (pgmag_win->attr.width != ev->xconfigure.width) ||
00504                     (pgmag_win->attr.height != ev->xconfigure.height) ||
00505                     (pgmag_win->attr.border_width !=
00506                      ev->xconfigure.border_width)) {
00507                         /* If an attribute of the window has changed we could
00508                          * have an exposed area that is not reported due to the
00509                          * overlay window. So we subtract the new region, from
00510                          * the old one, and we have the value of the exposed
00511                          * region that must be repainted.
00512                          */
00513                         pgmag_win->attr.x = ev->xconfigure.x;
00514                         pgmag_win->attr.y = ev->xconfigure.y;
00515                         pgmag_win->attr.width = ev->xconfigure.width;
00516                         pgmag_win->attr.height = ev->xconfigure.height;
00517                         pgmag_win->attr.border_width =
00518                                 ev->xconfigure.border_width;
00519                               
00520                         if (pgmag_win->attr.map_state == IsViewable) {
00521                                 XFixesCopyRegion (
00522                                         dpy_conn,
00523                                         old_region, pgmag_win->clip);
00524                                 gmag_events_calculate_window_clip (pgmag_win);
00525                                 XFixesCopyRegion (
00526                                         dpy_conn,
00527                                         new_region, pgmag_win->clip);
00528                                 XFixesUnionRegion (
00529                                         dpy_conn,
00530                                         exp_region, exp_region, old_region);
00531                                 XFixesUnionRegion (
00532                                         dpy_conn,
00533                                         exp_region, exp_region, new_region);
00534                         }
00535                 }
00536                 if (!ev->xconfigure.above) {
00537                         g_queue_remove (mag_windows_list, pgmag_win);
00538                         g_queue_push_head (mag_windows_list,
00539                                            pgmag_win);
00540                         if (pgmag_win->attr.map_state == IsViewable) {
00541                                 XFixesUnionRegion (
00542                                         dpy_conn,
00543                                         exp_region, exp_region,
00544                                         pgmag_win->win_region);
00545                         }
00546                 } else {
00547                         elem = g_queue_find_custom (
00548                                 mag_windows_list,
00549                                 (gconstpointer) ev->xconfigure.above,
00550                                 (GCompareFunc) gmag_events_g_compare_func);
00551                         if (elem) {
00552                                 g_queue_remove (mag_windows_list,
00553                                                 pgmag_win);
00554                                 g_queue_insert_after (mag_windows_list,
00555                                                       elem, pgmag_win);
00556                                 if (pgmag_win->attr.map_state == IsViewable) {
00557                                         XFixesUnionRegion (
00558                                                 dpy_conn,
00559                                                 exp_region, exp_region,
00560                                                 pgmag_win->win_region);
00561                                 }
00562                         }
00563                 }
00564         }
00565 }
00566 
00567 static void
00568 gmag_events_create_notify_handler (XEvent *ev)
00569 {
00570         GList      *elem;
00571         GmagWinPtr  pgmag_win;
00572 
00573 #ifdef COMPOSITE_DEBUG
00574         printf ("Received CreateNotify event: 0x%x\n",
00575                 (guint) ev->xcreatewindow.window);
00576 #endif /* COMPOSITE_DEBUG */
00577         if (ev->xcreatewindow.window == mag_window) {
00578 #ifdef HAVE_OVERLAY
00579 #ifdef COMPOSITE_DEBUG
00580                 printf ("Overlay window = 0x%x\n",
00581                         (guint) gmag_events_overlay_window);
00582 #endif /* COMPOSITE_DEBUG */
00583 #endif /* HAVE_OVERLAY */
00584                 return;
00585         }
00586         gmag_events_add_window (ev->xcreatewindow.window);
00587         elem = g_queue_find_custom (mag_windows_list,
00588                                     (gconstpointer) ev->xcreatewindow.window,
00589                                     (GCompareFunc) gmag_events_g_compare_func);
00590         if (elem) {
00591                 pgmag_win = (GmagWinPtr) elem->data;
00592                 if (pgmag_win->attr.map_state == IsViewable) {
00593                         gmag_events_calculate_window_clip (pgmag_win);
00594                         XFixesUnionRegion (dpy_conn,
00595                                            exp_region, exp_region,
00596                                            pgmag_win->clip);
00597                 }
00598         }
00599 }
00600 
00601 static void
00602 gmag_events_destroy_notify_handler (XEvent *ev)
00603 {
00604         GList      *elem;
00605         GmagWinPtr  pgmag_win;
00606 
00607 #ifdef COMPOSITE_DEBUG
00608         printf ("Received DestroyNotify event: 0x%x\n",
00609                 (guint) ev->xdestroywindow.window);
00610 #endif /* COMPOSITE_DEBUG */
00611         if (ev->xdestroywindow.window == mag_window) {
00612 #ifdef HAVE_OVERLAY
00613 #ifdef COMPOSITE_DEBUG
00614                 printf ("Overlay window = 0x%x\n",
00615                         (guint) gmag_events_overlay_window);
00616 #endif /* COMPOSITE_DEBUG */
00617 #endif /* HAVE_OVERLAY */
00618                 return;
00619         }
00620         elem = g_queue_find_custom (mag_windows_list,
00621                                     (gconstpointer) ev->xdestroywindow.window,
00622                                     (GCompareFunc) gmag_events_g_compare_func);
00623         if (elem) {
00624                 pgmag_win = (GmagWinPtr) elem->data;
00625                 if (pgmag_win->attr.map_state == IsViewable)
00626                         XFixesUnionRegion (dpy_conn,
00627                                            exp_region, exp_region,
00628                                            pgmag_win->clip);
00629                 gmag_events_remove_window (ev->xdestroywindow.window);
00630         }
00631 }
00632 
00633 static void
00634 gmag_events_map_notify_handler (XEvent *ev)
00635 {
00636         GList      *elem;
00637         GmagWinPtr  pgmag_win;
00638 
00639 #ifdef COMPOSITE_DEBUG
00640         printf ("Received MapNotify event: 0x%x\n",
00641                 (guint) ev->xmap.window);
00642 #endif /* COMPOSITE_DEBUG */
00643         if (ev->xmap.window == mag_window) {
00644 #ifdef HAVE_OVERLAY
00645 #ifdef COMPOSITE_DEBUG
00646                 printf ("Overlay window = 0x%x\n",
00647                         (guint) gmag_events_overlay_window);
00648 #endif /* COMPOSITE_DEBUG */
00649 #endif /* HAVE_OVERLAY */
00650                 return;
00651         }
00652         elem = g_queue_find_custom (mag_windows_list,
00653                                     (gconstpointer) ev->xmap.window,
00654                                     (GCompareFunc) gmag_events_g_compare_func);
00655         if (elem) {
00656                 pgmag_win = (GmagWinPtr) elem->data;
00657                 pgmag_win->attr.map_state = IsViewable;
00658                 gmag_events_calculate_window_clip (pgmag_win);
00659                 XFixesUnionRegion (dpy_conn, exp_region,
00660                                    exp_region, pgmag_win->clip);
00661         }
00662 }
00663 
00664 static void
00665 gmag_events_unmap_notify_handler (XEvent *ev)
00666 {
00667         GList      *elem;
00668         GmagWinPtr  pgmag_win;
00669 
00670 #ifdef COMPOSITE_DEBUG
00671         printf ("Received UnmapNotify event: 0x%x\n",
00672                 (guint) ev->xunmap.window);
00673 #endif /* COMPOSITE_DEBUG */
00674         if (ev->xunmap.window == mag_window) {
00675 #ifdef HAVE_OVERLAY
00676 #ifdef COMPOSITE_DEBUG
00677                 printf ("Overlay window = 0x%x\n",
00678                         (guint) gmag_events_overlay_window);
00679 #endif /* COMPOSITE_DEBUG */
00680 #endif /* HAVE_OVERLAY */
00681                 return;
00682         }
00683         elem = g_queue_find_custom (mag_windows_list,
00684                                     (gconstpointer) ev->xunmap.window,
00685                                     (GCompareFunc) gmag_events_g_compare_func);
00686         if (elem) {
00687                 pgmag_win = (GmagWinPtr) elem->data;
00688                 pgmag_win->attr.map_state = IsUnmapped;
00689                 XFixesUnionRegion (dpy_conn, exp_region,
00690                                    exp_region, pgmag_win->clip);
00691         }
00692 }
00693 
00694 static void
00695 gmag_events_reparent_notify_handler (XEvent *ev)
00696 {
00697         GList      *elem;
00698         GmagWinPtr  pgmag_win;
00699 
00700 #ifdef COMPOSITE_DEBUG
00701         printf ("Received ReparentNotify event: 0x%x (Window), 0x%x (Parent)\n", (guint) ev->xreparent.window, (guint) ev->xreparent.parent);
00702 #endif /* COMPOSITE_DEBUG */
00703         if  (ev->xreparent.window == mag_window) {
00704 #ifdef HAVE_OVERLAY
00705 #ifdef COMPOSITE_DEBUG
00706                 printf ("Overlay window = 0x%x\n",
00707                         (guint) gmag_events_overlay_window);
00708 #endif /* COMPOSITE_DEBUG */
00709 #endif /* HAVE_OVERLAY */
00710                 return;
00711         }
00712         if (ev->xreparent.parent != root_window) {
00713                 gmag_events_remove_window (ev->xreparent.window);
00714         } else {
00715                 gmag_events_add_window (ev->xreparent.window);
00716                 elem = g_queue_find_custom (
00717                         mag_windows_list,
00718                         (gconstpointer) ev->xreparent.window,
00719                         (GCompareFunc) gmag_events_g_compare_func);
00720                 if (elem) {
00721                         pgmag_win = (GmagWinPtr) elem->data;
00722                         if (pgmag_win->attr.map_state == IsViewable) {
00723                                 gmag_events_calculate_window_clip (pgmag_win);
00724                                 XFixesUnionRegion (
00725                                         dpy_conn,
00726                                         exp_region, exp_region,
00727                                         pgmag_win->clip);
00728                         }
00729                 }
00730         }
00731 }
00732 
00733 #endif /* HAVE_COMPOSITE */
00734 
00735 static void
00736 gmag_events_damage_notify_handler (XEvent *ev)
00737 {
00738         XDamageNotifyEvent *dev = (XDamageNotifyEvent *) ev;
00739 #ifdef DAMAGE_DEBUG
00740         g_message ("Damage area %3d, %3d x %3d, %3d",
00741                    (int) dev->area.x, (int) dev->area.x + dev->area.width,
00742                    (int) dev->area.y, (int) dev->area.y + dev->area.height);
00743         g_message ("Damage geometry %3d, %3d x %3d, %3d",
00744                    (int) dev->geometry.x,
00745                    (int) dev->geometry.x + dev->geometry.width,
00746                    (int) dev->geometry.y,
00747                    (int) dev->geometry.y + dev->geometry.height);
00748 #endif /* DAMAGE_DEBUG */
00749 
00750 #ifdef HAVE_COMPOSITE
00751         if (use_composite) {
00752                 if (dev->damage == off_screen_damage) {
00753 #ifdef DAMAGE_DEBUG
00754                         g_message ("off_screen_damage damaged");
00755 #endif /* DAMAGE_DEBUG */
00756                         XDamageSubtract (dpy_conn, dev->damage, None,
00757                                          gmag_events_tmp_region);
00758                         XFixesUnionRegion (dpy_conn,
00759                                            off_screen_region,
00760                                            off_screen_region,
00761                                            gmag_events_tmp_region);
00762                 } else {
00763 #ifdef DAMAGE_DEBUG
00764                         g_message ("Window with damage: 0x%x", dev->drawable);
00765 #endif /* DAMAGE_DEBUG */
00766                         XDamageSubtract (dpy_conn, dev->damage, None,
00767                                          gmag_events_tmp_region);
00768                         gmag_events_add_win_damaged_region (
00769                                 dev->drawable, gmag_events_tmp_region);
00770                 }
00771         }
00772 #endif /* HAVE_COMPOSITE */
00773 }
00774 
00775 static void
00776 gmag_events_cursor_convert_to_rgba (Magnifier *magnifier,
00777                                     XFixesCursorImage *cursor_image)
00778 {
00779         int i, count = cursor_image->width * cursor_image->height;
00780         for (i = 0; i < count; ++i) {
00781                 guint32 pixval = GUINT_TO_LE (cursor_image->pixels[i]);
00782                 cursor_image->pixels[i] = pixval;
00783         }
00784 }
00785 
00786 static void
00787 gmag_events_free_cursor_pixels (guchar *pixels, gpointer data)
00788 {
00789     /* XFree (data); FIXME why doesn't this work properly? */
00790 }
00791 
00792 GdkPixbuf *
00793 gmag_events_get_source_pixbuf (Magnifier *magnifier)
00794 {
00795         XFixesCursorImage *cursor_image = XFixesGetCursorImage (
00796                 dpy_conn);
00797         GdkPixbuf *cursor_pixbuf = NULL;
00798         gchar s[6];
00799         if (cursor_image)
00800         {
00801                 gmag_events_cursor_convert_to_rgba (magnifier, cursor_image);
00802                 cursor_pixbuf = gdk_pixbuf_new_from_data (
00803                         (guchar *) cursor_image->pixels, GDK_COLORSPACE_RGB,
00804                         TRUE, 8, cursor_image->width, cursor_image->height,
00805                         cursor_image->width * 4,
00806                         gmag_events_free_cursor_pixels, cursor_image);
00807                 gdk_pixbuf_set_option (cursor_pixbuf, "x_hot", 
00808                                        g_ascii_dtostr (
00809                                                s, 6,
00810                                                (gdouble) cursor_image->xhot));
00811                 gdk_pixbuf_set_option (cursor_pixbuf, "y_hot", 
00812                                        g_ascii_dtostr (
00813                                                s, 6,
00814                                                (gdouble) cursor_image->yhot));
00815         }
00816         return cursor_pixbuf;
00817 }
00818 
00819 gboolean
00820 gmag_events_source_has_damage_extension (Magnifier *magnifier)
00821 {
00822         gint event_base, error_base;
00823         Display *dpy;
00824         g_assert (magnifier);
00825         dpy = GDK_DISPLAY_XDISPLAY (magnifier->source_display);
00826         if (g_getenv ("MAGNIFIER_IGNORE_DAMAGE"))
00827                 return FALSE;
00828         if (XDamageQueryExtension (dpy, &event_base, &error_base))
00829                 return TRUE;
00830         return FALSE;
00831 }
00832 
00833 static gboolean
00834 gmag_events_handler (GIOChannel *source, GIOCondition condition, gpointer data)
00835 {
00836         XEvent                   ev;
00837         XFixesCursorNotifyEvent *cev = NULL;
00838         gboolean                 cursor_changed = FALSE;
00839         Magnifier               *magnifier = (Magnifier *) data;
00840         XRectangle              *rectlist;
00841 #ifdef HAVE_COMPOSITE
00842         gboolean                 calc_clip = FALSE;
00843 #endif /* HAVE_COMPOSITE */
00844 
00845 #ifdef HAVE_OVERLAY
00846         if (magnifier->priv->overlay)
00847                 mag_window = GDK_WINDOW_XID (magnifier->priv->overlay);
00848 #else
00849         if (magnifier->priv->w && magnifier->priv->w->window)
00850                 mag_window = GDK_WINDOW_XID (magnifier->priv->w->window);
00851 #endif /* HAVE_OVERLAY */
00852 
00853         do
00854         {
00855                 XNextEvent(dpy_conn, &ev);
00856 
00857 #ifdef HAVE_COMPOSITE
00858                 if (use_composite) {
00859                         switch (ev.type) {
00860                         case CirculateNotify:
00861                                 gmag_events_circulate_notify_handler (&ev);
00862                                 calc_clip = TRUE;
00863                                 break;
00864                         case ConfigureNotify:
00865                                 gmag_events_configure_notify_handler (&ev);
00866                                 calc_clip = TRUE;
00867                                 break;
00868                         case CreateNotify:
00869                                 gmag_events_create_notify_handler (&ev);
00870                                 calc_clip = TRUE;
00871                                 break;
00872                         case DestroyNotify:
00873                                 gmag_events_destroy_notify_handler (&ev);
00874                                 calc_clip = TRUE;
00875                                 break;
00876                         case MapNotify:
00877                                 gmag_events_map_notify_handler (&ev);
00878                                 calc_clip = TRUE;
00879                                 break;
00880                         case UnmapNotify:
00881                                 gmag_events_unmap_notify_handler (&ev);
00882                                 calc_clip = TRUE;
00883                                 break;
00884                         case ReparentNotify:
00885                                 gmag_events_reparent_notify_handler (&ev);
00886                                 calc_clip = TRUE;
00887                                 break;
00888                         }
00889                 }
00890 #endif /* HAVE_COMPOSITE */
00891 
00892                 if (use_damage) {
00893                         if (ev.type == damage_event_base + XDamageNotify) {
00894                                 gmag_events_damage_notify_handler (&ev);
00895                         }
00896                 }
00897 
00898                 if (ev.type == fixes_event_base + XFixesCursorNotify) {
00899                         cursor_changed = TRUE;
00900                         cev = (XFixesCursorNotifyEvent *) &ev;
00901                 }
00902 
00903         } while (XPending (dpy_conn));
00904 
00905 #ifndef HAVE_OVERLAY
00906         if (use_composite && mag_window) {
00907                 XRaiseWindow (dpy_conn, mag_window);
00908         }
00909 #endif /* HAVE_OVERLAY */
00910 
00911         if (!use_composite) {
00912                 XDamageSubtract (dpy_conn, root_window_damage, None,
00913                                  gmag_events_tmp_region);
00914         }
00915 
00916         if (use_damage) {
00917                 if (magnifier) {
00918                         int i, howmany;
00919                         /* TODO: maintain this list on the client instead, to
00920                          * avoid the roundtrip below */
00921 #ifdef HAVE_COMPOSITE
00922                         if (use_composite) {
00923                                 rectlist = XFixesFetchRegion (
00924                                         dpy_conn,
00925                                         off_screen_region,
00926                                         &howmany);
00927                         } else {
00928 #endif /* HAVE_COMPOSITE */
00929                                 rectlist = XFixesFetchRegion (
00930                                         dpy_conn, gmag_events_tmp_region,
00931                                         &howmany);
00932 #ifdef HAVE_COMPOSITE
00933                         }
00934 #endif /* HAVE_COMPOSITE */
00935                         if (rectlist == NULL) /* no reply from fetch */
00936                                 return TRUE;
00937                         for (i=0; i < howmany; ++i) {
00938                                 magnifier_notify_damage (magnifier,
00939                                                          &rectlist[i]);
00940                         }
00941                         XFree (rectlist);
00942                 }
00943         }
00944 
00945 #ifdef HAVE_COMPOSITE
00946         if (use_composite) {
00947                 if (calc_clip) {
00948                         gmag_events_calculate_windows_clip ();
00949                         gmag_events_paint_windows (exp_region);
00950                 }
00951                 gmag_events_paint_damaged_windows ();
00952         }
00953 #endif /* HAVE_COMPOSITE */
00954 
00955         if (cursor_changed) {
00956                 if (magnifier->priv->use_source_cursor) {
00957                         GdkPixbuf *cursor_pixbuf =
00958                                 gmag_events_get_source_pixbuf (magnifier);
00959                         magnifier_set_cursor_from_pixbuf (magnifier,
00960                                                           cursor_pixbuf);
00961                         if (cursor_pixbuf) g_object_unref (cursor_pixbuf);
00962                 } else {
00963                         magnifier_set_cursor_pixmap_by_name (magnifier, cev ? gdk_x11_get_xatom_name (cev->cursor_name) : "default", TRUE);
00964                 }
00965           
00966                 magnifier_transform_cursor (magnifier);
00967 #ifdef CURSOR_DEBUG
00968                 if (cev)
00969                         g_message ("cursor changed: subtype=%d, " \
00970                                    "cursor_serial=%lu, name=[%x] %s\n",
00971                                    (int) cev->subtype, cev->cursor_serial,
00972                                    (int) cev->cursor_name,
00973                                    gdk_x11_get_xatom_name (cev->cursor_name));
00974 #endif /* CURSOR_DEBUG */
00975                 cursor_changed = FALSE;
00976         }
00977 
00978 #ifdef HAVE_COMPOSITE
00979         if (use_composite) {
00980                 XFixesSetRegion (dpy_conn, tmp_region, 0, 0);
00981                 XFixesSetRegion (dpy_conn, new_region, 0, 0);
00982                 XFixesSetRegion (dpy_conn, old_region, 0, 0);
00983                 XFixesSetRegion (dpy_conn, exp_region, 0, 0);
00984                 XFixesSetRegion (dpy_conn, off_screen_region, 0, 0);
00985         }
00986 #endif /* HAVE_COMPOSITE */
00987 
00988         XFlush (dpy_conn);
00989         return TRUE;
00990 }
00991 
00992 static gboolean
00993 gmag_events_use_damage ()
00994 {
00995         gint major, event, error;
00996         if (XQueryExtension (dpy_conn, "DAMAGE", &major, &event, &error) &&
00997             !g_getenv ("MAGNIFIER_IGNORE_DAMAGE"))
00998                 return TRUE;
00999         return FALSE;
01000 }
01001 
01002 static gboolean
01003 gmag_events_use_composite ()
01004 {
01005         if (!gmag_events_use_damage ()) {
01006                 return FALSE;
01007         }
01008 #ifdef HAVE_COMPOSITE
01009         gint major, event, error;
01010         if (XQueryExtension (dpy_conn, "Composite", &major, &event, &error) &&
01011             !g_getenv ("MAGNIFIER_IGNORE_COMPOSITE"))
01012                 return TRUE;
01013         return FALSE;
01014 #else
01015         return FALSE;
01016 #endif /* HAVE_COMPOSITE */
01017 }
01018 
01019 void
01020 gmag_events_client_init (Magnifier *magnifier)
01021 {
01022         GIOChannel               *ioc;
01023         gint                      fd;
01024         gint                      event_base, error_base;
01025 #ifdef HAVE_COMPOSITE
01026         XRenderPictureAttributes  pic_attr;
01027         XRenderPictFormat        *format;
01028         GdkDisplay               *gdk_display_connection;
01029         GdkScreen                *gdkscr;
01030         gint                      scr = 0, root_w, root_h;
01031 #endif /* HAVE_COMPOSITE */
01032 
01033         if (dpy_conn) {
01034                 /* remove the old watch */
01035                 if (dpy_gsource) 
01036                         g_source_remove (dpy_gsource);
01037                 XCloseDisplay (dpy_conn);
01038         }
01039 
01040         if (magnifier) {
01041                 /* we need our own connection here to keep from gumming up the
01042                  * works */
01043                 dpy_conn = XOpenDisplay (magnifier->source_display_name); 
01044                 root_window = GDK_WINDOW_XWINDOW (magnifier->priv->root);
01045         } else {
01046                 dpy_conn = XOpenDisplay (NULL);
01047                 root_window = RootWindow (dpy_conn, DefaultScreen (dpy_conn));
01048                 g_message ("warning - using DefaultScreen for X connection.");
01049         }
01050 
01051 #ifdef EVENTS_DEBUG
01052         XSynchronize (dpy_conn, True);
01053 #endif /* EVENTS_DEBUG */
01054 
01055         fd = ConnectionNumber (dpy_conn);
01056         ioc = g_io_channel_unix_new (fd);
01057         dpy_gsource = g_io_add_watch (ioc,
01058                                       G_IO_IN | G_IO_HUP | G_IO_PRI | G_IO_ERR,
01059                                       gmag_events_handler, magnifier);
01060         g_io_channel_unref (ioc); 
01061 
01062         use_damage = gmag_events_use_damage ();
01063         use_composite = gmag_events_use_composite ();
01064 
01065         if (!XFixesQueryExtension (dpy_conn, &fixes_event_base,
01066                                    &fixes_error_base)) {
01067                 g_warning ("XFixes extension not currently active.\n");
01068         } else {
01069                 XFixesSelectCursorInput (dpy_conn, root_window,
01070                                          XFixesDisplayCursorNotifyMask);
01071                 g_message ("added event source to xfixes cursor-notify " \
01072                            "connection");
01073         }
01074 
01075         if (!XDamageQueryExtension (dpy_conn, &damage_event_base,
01076                                     &damage_error_base)) {
01077                 g_warning ("Damage extension not currently active.\n");
01078         } else if (g_getenv ("MAGNIFIER_IGNORE_DAMAGE")) {
01079                 g_warning ("Damage extension being ignored at user request.");
01080         } else {
01081                 gmag_events_tmp_region = XFixesCreateRegion (dpy_conn, 0, 0);
01082                 if (!use_composite) {
01083                         root_window_damage = XDamageCreate (
01084                                 dpy_conn, root_window,
01085                                 XDamageReportDeltaRectangles);
01086                         /* I don't know why, but without this XDamageSubtract
01087                          * call below the damage events aren't hanled normally.
01088                          * They start to be handled normally, without the call
01089                          * below, only after you move your mouse.
01090                          */
01091                         XDamageSubtract (dpy_conn, root_window_damage, None,
01092                                          None);
01093                 }
01094                 g_message ("added event source to damage connection");
01095         }
01096 
01097 #ifdef HAVE_COMPOSITE
01098         if (!XCompositeQueryExtension (dpy_conn, &event_base, &error_base)) {
01099                 g_warning ("Composite extension not currently active.\n");
01100         } else if (g_getenv ("MAGNIFIER_IGNORE_COMPOSITE")) {
01101                 g_warning ("Composite extension being ignored at user " \
01102                            "request.");
01103         } else if (!use_damage) {
01104                 g_setenv ("MAGNIFIER_IGNORE_COMPOSITE", "1", TRUE);
01105                 g_warning ("Composite extension being ignored due Damage " \
01106                            "is not actived.");
01107         } else {
01108 #ifndef HAVE_OVERLAY
01109                 g_warning ("update composite to version 0.3 or higher to " \
01110                            "have overlay window support.\n");
01111 #endif /* HAVE_OVERLAY */
01112 
01113                 gdk_drawable_get_size (magnifier->priv->root, &root_w,
01114                                        &root_h);
01115                 magnifier->priv->source_drawable = gdk_pixmap_new (
01116                         magnifier->priv->root, root_w, root_h, -1);
01117                 /* GTK+ uses it's own connection with X, so we must flush that
01118                  * to not receive a BadDrawable when creating a picture of this
01119                  * drawable below. */
01120                 gdk_flush ();
01121 
01122                 gdk_display_connection = gdk_drawable_get_display (
01123                         magnifier->priv->root);
01124                 gdkscr = gdk_display_get_default_screen (
01125                         gdk_display_connection);
01126             
01127                 scr = GDK_SCREEN_XNUMBER (gdkscr);
01128 
01129                 XCompositeRedirectSubwindows (dpy_conn, root_window,
01130                                               CompositeRedirectAutomatic);
01131                 off_screen_region = XFixesCreateRegion (
01132                         dpy_conn, 0, 0);
01133                 tmp_region = XFixesCreateRegion (dpy_conn, 0, 0);
01134                 new_region = XFixesCreateRegion (dpy_conn, 0, 0);
01135                 old_region = XFixesCreateRegion (dpy_conn, 0, 0);
01136                 exp_region = XFixesCreateRegion (dpy_conn, 0, 0);
01137                 off_screen_damage = XDamageCreate (
01138                                 dpy_conn, 
01139                                 GDK_DRAWABLE_XID (
01140                                         magnifier->priv->source_drawable),
01141                                 XDamageReportDeltaRectangles);
01142 
01143                 format = XRenderFindVisualFormat (
01144                         dpy_conn,
01145                         DefaultVisual (dpy_conn, scr));
01146                 pic_attr.subwindow_mode = IncludeInferiors;
01147                 off_screen_picture = XRenderCreatePicture (
01148                         dpy_conn,
01149                         GDK_DRAWABLE_XID (magnifier->priv->source_drawable),
01150                         format, CPSubwindowMode, &pic_attr);
01151 
01152                 gmag_events_create_windows_list (gdk_display_connection,
01153                                                  gdkscr);
01154                 gmag_events_calculate_windows_clip ();
01155                 g_message ("added event source to composite connection");
01156         }
01157 #else
01158         g_warning ("this copy of gnome-mag was built without composite " \
01159                    "extension support.\n");
01160 #endif /* HAVE_COMPOSITE */
01161 
01162         XFlush (dpy_conn);
01163 }

Generated on Tue Apr 24 15:39:22 2007 for gnome-mag by  doxygen 1.5.1