# zoom-region.c

Go to the documentation of this file.

    00001 /*
    00002  * AT-SPI - Assistive Technology Service Provider Interface
    00003  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
    00004  *
    00005  * Copyright 2001 Sun Microsystems Inc.
    00006  *
    00007  * This library is free software; you can redistribute it and/or
    00008  * modify it under the terms of the GNU Library General Public
    00009  * License as published by the Free Software Foundation; either
    00010  * version 2 of the License, or (at your option) any later version.
    00011  *
    00012  * This library is distributed in the hope that it will be useful,
    00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    00015  * Library General Public License for more details.
    00016  *
    00017  * You should have received a copy of the GNU Library General Public
    00018  * License along with this library; if not, write to the
    00019  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    00020  * Boston, MA 02111-1307, USA.
    00021  */
    00022
    00023 #include "config.h"
    00024 #include "gmag-graphical-server.h"
    00025
    00026 #include <stdlib.h>
    00027 #include <string.h>
    00028 #include <popt.h>
    00029
    00030 #ifdef HAVE_COLORBLIND
    00031 #include <colorblind.h>
    00032 #endif /* HAVE_COLORBLIND */
    00033
    00034 #include <gdk/gdk.h>
    00035 #include <gtk/gtk.h>
    00036
    00037 #ifdef USE_GDKPIXBUF_RENDER_TO_DRAWABLE
    00038 #include <gdk/gdkpixbuf.h>
    00039 #endif
    00040
    00041 #include <gdk/gdkx.h>
    00042 #include <libbonobo.h>
    00043 #include <math.h>
    00044
    00045 #undef ZOOM_REGION_DEBUG
    00046
    00047 #include "zoom-region.h"
    00048 #include "zoom-region-private.h"
    00049 #include "magnifier.h" /* needed to access parent data */
    00050 #include "magnifier-private.h" /* needed to access parent data */
    00051
    00052 #define DEBUG_CLIENT_CALLS
    00053
    00054 #ifdef DEBUG_CLIENT_CALLS
    00055 static gboolean client_debug = FALSE;
    00056 #define DBG(a) if (client_debug) { (a); }
    00057 #else
    00058 #define DBG(a)
    00059 #endif
    00060
    00061 static GObjectClass *parent_class = NULL;
    00062
    00063 enum {
    00064         ZOOM_REGION_MANAGED_PROP,
    00065         ZOOM_REGION_POLL_MOUSE_PROP,
    00066         ZOOM_REGION_DRAW_CURSOR_PROP,
    00067         ZOOM_REGION_SMOOTHSCROLL_PROP,
    00068         ZOOM_REGION_COLORBLIND_PROP,
    00069         ZOOM_REGION_INVERT_PROP,
    00070         ZOOM_REGION_SMOOTHING_PROP,
    00071         ZOOM_REGION_CONTRASTR_PROP,
    00072         ZOOM_REGION_CONTRASTG_PROP,
    00073         ZOOM_REGION_CONTRASTB_PROP,
    00074         ZOOM_REGION_BRIGHTR_PROP,
    00075         ZOOM_REGION_BRIGHTG_PROP,
    00076         ZOOM_REGION_BRIGHTB_PROP,
    00077         ZOOM_REGION_XSCALE_PROP,
    00078         ZOOM_REGION_YSCALE_PROP,
    00079         ZOOM_REGION_BORDERSIZE_PROP,
    00080         ZOOM_REGION_BORDERSIZETOP_PROP,
    00081         ZOOM_REGION_BORDERSIZELEFT_PROP,
    00082         ZOOM_REGION_BORDERSIZERIGHT_PROP,
    00083         ZOOM_REGION_BORDERSIZEBOTTOM_PROP,
    00084         ZOOM_REGION_BORDERCOLOR_PROP,
    00085         ZOOM_REGION_XALIGN_PROP,
    00086         ZOOM_REGION_YALIGN_PROP,
    00087         ZOOM_REGION_VIEWPORT_PROP,
    00088         ZOOM_REGION_TESTPATTERN_PROP,
    00089         ZOOM_REGION_TIMING_TEST_PROP,
    00090         ZOOM_REGION_TIMING_OUTPUT_PROP,
    00091         ZOOM_REGION_TIMING_PAN_RATE_PROP,
    00092         ZOOM_REGION_EXIT_MAGNIFIER
    00093 } PropIdx;
    00094
    00095 #ifdef DEBUG_CLIENT_CALLS
    00096 gchar* prop_names[ZOOM_REGION_EXIT_MAGNIFIER + 1] =
    00097 {
    00098         "MANAGED",
    00099         "POLLMOUSE",
    00100         "DRAWCURSOR",
    00101         "SMOOTHSCROLL",
    00102         "COLORBLIND",
    00103         "INVERT",
    00104         "SMOOTHING",
    00105         "CONTRASTR",
    00106         "CONTRASTG",
    00107         "CONTRASTB",
    00108         "BRIGHTR",
    00109         "BRIGHTG",
    00110         "BRIGHTB",
    00111         "XSCALE",
    00112         "YSCALE",
    00113         "BORDERSIZE",
    00114         "BORDERSIZETOP",
    00115         "BORDERSIZELEFT",
    00116         "BORDERSIZERIGHT",
    00117         "BORDERSIZEBOTTOM",
    00118         "BORDERCOLOR",
    00119         "XALIGN",
    00120         "YALIGN",
    00121         "VIEWPORT",
    00122         "TESTPATTERN",
    00123         "TIMING_TEST",
    00124         "TIMING_OUTPUT",
    00125         "TIMING_PAN_RATE",
    00126         "EXIT_MAGNIFIER"
    00127 };
    00128 #endif
    00129
    00130 typedef enum {
    00131         ZOOM_REGION_ERROR_NONE,
    00132         ZOOM_REGION_ERROR_NO_TARGET_DRAWABLE,
    00133         ZOOM_REGION_ERROR_TOO_BIG
    00134 } ZoomRegionPixmapCreationError;
    00135
    00136 static float timing_scale_max  = 0;
    00137 static float timing_idle_max   = 0;
    00138 static float timing_frame_max  = 0;
    00139 static float cps_max           = 0;
    00140 static float nrr_max           = 0;
    00141 static float update_nrr_max    = 0;
    00142 static gboolean reset_timing   = FALSE;
    00143 static gboolean timing_test    = FALSE;
    00144
    00145 static guint pending_idle_handler = 0;
    00146 static gboolean processing_updates = FALSE;
    00147 static gboolean timing_start = FALSE;
    00148
    00149 static gboolean can_coalesce = TRUE ; /* change this when event coalescing is working */
    00150
    00151 #define CLAMP_B_C(v) (t = (v), CLAMP (t, -1, 1));
    00152
    00153 static void zoom_region_sync (ZoomRegion *region);
    00154 static void zoom_region_finalize (GObject *object);
    00155 static void zoom_region_update (ZoomRegion *zoom_region,
    00156                                 const GdkRectangle rect);
    00157 static void zoom_region_queue_update (ZoomRegion *zoom_region,
    00158                                       const GdkRectangle rect);
    00159
    00160 static int  zoom_region_process_updates (gpointer data);
    00161 static void zoom_region_paint (ZoomRegion *zoom_region, GdkRectangle *rect);
    00162 static void zoom_region_paint_pixmap (ZoomRegion *zoom_region, GdkRectangle *rect);
    00163 static int  zoom_region_update_pointer_timeout (gpointer data);
    00164 static GdkRectangle zoom_region_rect_from_bounds (ZoomRegion *zoom_region,
    00165                                                   const GNOME_Magnifier_RectBounds *bounds);
    00166 static ZoomRegionPixmapCreationError zoom_region_create_pixmap (ZoomRegion *zoom_region);
    00167 static GdkRectangle zoom_region_update_pixmap (ZoomRegion *zoom_region, const GdkRectangle update_rect, GdkRectangle *paint_rect);
    00168 static void zoom_region_get_move_x_y (ZoomRegion *zoom_region, long *x, long *y);
    00169 static void zoom_region_recompute_exposed_bounds (ZoomRegion *zoom_region);
    00170 static void zoom_region_update_current (ZoomRegion *zoom_region);
    00171
    00172 void
    00173 reset_timing_stats()
    00174 {
    00175         timing_scale_max               = 0;
    00176         timing_idle_max                = 0;
    00177         timing_frame_max               = 0;
    00178         cps_max                        = 0;
    00179         nrr_max                        = 0;
    00180         update_nrr_max                 = 0;
    00181         mag_timing.num_scale_samples   = 0;
    00182         mag_timing.num_idle_samples    = 0;
    00183         mag_timing.num_frame_samples   = 0;
    00184         mag_timing.num_line_samples    = 0;
    00185         mag_timing.scale_total         = 0;
    00186         mag_timing.idle_total          = 0;
    00187         mag_timing.frame_total         = 0;
    00188         mag_timing.update_pixels_total = 0;
    00189         mag_timing.update_pixels_total = 0;
    00190         mag_timing.dx_total            = 0;
    00191         mag_timing.dy_total            = 0;
    00192         mag_timing.last_frame_val      = 0;
    00193         mag_timing.last_dy             = 0;
    00194         g_timer_start (mag_timing.process);
    00195 }
    00196
    00199 #undef DEBUG
    00200 #ifdef DEBUG
    00201 #define DEBUG_RECT(a, b) _debug_announce_rect (a, b)
    00202 #else
    00203 #define DEBUG_RECT(a, b)
    00204 #endif
    00205 static void
    00206 _debug_announce_rect (char *msg, GdkRectangle rect)
    00207 {
    00208         fprintf (stderr, "%s: (%d,%d - %d,%d)\n",
    00209                  msg, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height);
    00210 }
    00211
    00212 static gboolean
    00213 _diff_pixbufs (const GdkPixbuf *a, const GdkPixbuf *b)
    00214 {
    00215         long i, j;
    00216         int bits_per_byte = 8; /* always true? */
    00217         guchar *pa = gdk_pixbuf_get_pixels (a);
    00218         guchar *pb = gdk_pixbuf_get_pixels (b);
    00219         guchar *cpa, *cpb;
    00220         long rsa = gdk_pixbuf_get_rowstride (a);
    00221         long rsb = gdk_pixbuf_get_rowstride (b);
    00222         long rowbytes = gdk_pixbuf_get_width (a) *
    00223                 gdk_pixbuf_get_bits_per_sample (a) *
    00224                 gdk_pixbuf_get_n_channels (a)/ bits_per_byte;
    00225         long n_rows = gdk_pixbuf_get_height (a);
    00226
    00227         if (gdk_pixbuf_get_height (b) != n_rows)
    00228                 return TRUE;
    00229         if (gdk_pixbuf_get_width (b) != gdk_pixbuf_get_width (a))
    00230                 return TRUE;
    00231         for (j = 0; j < n_rows; ++j)
    00232         {
    00233                 cpa = pa + j * rsa;
    00234                 cpb = pb + j * rsb;
    00235                 for (i = 0; i < rowbytes; ++i)
    00236                 {
    00237                         if (*cpa != *cpb)
    00238                         {
    00239                                 return TRUE;
    00240                         }
    00241                         cpa++;
    00242                         cpb++;
    00243                 }
    00244         }
    00245         return FALSE;
    00246 }
    00247
    00250 #ifdef BROKEN_COALESCE_STUFF_GETS_FIXED
    00251
    00260 static gboolean
    00261 _combine_rects (GdkRectangle *a, GdkRectangle *b)
    00262 {
    00263         gboolean can_combine = FALSE;
    00264         if ((a->x == b->x) && (a->x + a->width == b->x + b->width))
    00265         {
    00266                 can_combine = TRUE;
    00267         }
    00268         else if ((a->y == b->y) && (a->y + a->height == b->y + b->height))
    00269         {
    00270                 can_combine = TRUE;
    00271         }
    00272         if (can_combine)
    00273         {
    00274                 GdkRectangle c;
    00275                 /* TODO: check and fix this */
    00276                 if (gdk_rectangle_intersect (a, b, &c))
    00277                 {
    00278                         gdk_rectangle_union (a, b, &c);
    00279                         *a = c;
    00280                         can_combine = TRUE;
    00281                 }
    00282                 else
    00283                 {
    00284                         can_combine = FALSE;
    00285                 }
    00286         }
    00287         return can_combine;
    00288 }
    00289
    00303 static gboolean
    00304 _refactor_rects (GdkRectangle *p, GdkRectangle *n)
    00305 {
    00306         gboolean refactored = FALSE;
    00307         GdkRectangle *a, *b;
    00308         if (p->x == n->x)
    00309         {
    00310                 if (p->width < n->width)
    00311                 {
    00312                         a = p;
    00313                         b = n;
    00314                 }
    00315                 else
    00316                 {
    00317                         a = n;
    00318                         b = p;
    00319                 }
    00320                 if (a->y == b->y + b->height)
    00321                 {
    00322                         a->y -= b->height;
    00323                         a->height += b->height;
    00324                         b->x += a->width;
    00325                         b->width -= a->width;
    00326                         refactored = TRUE;
    00327                 }
    00328                 else if (a->y + a->height == b->y)
    00329                 {
    00330                         a->height += b->height;
    00331                         b->x += a->width;
    00332                         b->width -= a->width;
    00333                         refactored = TRUE;
    00334                 }
    00335                 if (refactored) fprintf (stderr, "REFACTOR 1\n");
    00336         }
    00337         else if (p->y == n->y)
    00338         {
    00339                 if (p->height < n->height)
    00340                 {
    00341                         a = p;
    00342                         b = n;
    00343                 }
    00344                 else
    00345                 {
    00346                         a = n;
    00347                         b = p;
    00348                 }
    00349                 if (a->x == b->x + b->width)
    00350                 {
    00351                         a->x -= b->width;
    00352                         a->width += b->width;
    00353                         b->y += a->height;
    00354                         b->height -= a->height;
    00355                         refactored = TRUE;
    00356                 }
    00357                 else if (a->x + a->width == b->x)
    00358                 {
    00359                         a->width += b->width;
    00360                         b->y += a->height;
    00361                         b->height -= a->height;
    00362                         refactored = TRUE;
    00363                 }
    00364                 if (refactored) fprintf (stderr, "REFACTOR 2\n");
    00365         }
    00366         else if (p->x + p->width == n->x + n->width)
    00367         {
    00368                 if (p->width < n->width)
    00369                 {
    00370                         a = p;
    00371                         b = n;
    00372                 }
    00373                 else
    00374                 {
    00375                         a = n;
    00376                         b = p;
    00377                 }
    00378                 if (a->y == b->y + b->height)
    00379                 {
    00380                         a->y -= b->height;
    00381                         a->height += b->height;
    00382                         b->width -= a->width;
    00383                         refactored = TRUE;
    00384                 }
    00385                 else if (a->y + a->height == b->y)
    00386                 {
    00387                         a->height += b->height;
    00388                         b->width -= a->width;
    00389                         refactored = TRUE;
    00390                 }
    00391                 if (refactored) fprintf (stderr, "REFACTOR 3\n");
    00392         }
    00393         else if (p->y + p->height == n->y + n->height)
    00394         {
    00395                 if (p->height < n->height)
    00396                 {
    00397                         a = p;
    00398                         b = n;
    00399                 }
    00400                 else
    00401                 {
    00402                         a = n;
    00403                         b = p;
    00404                 }
    00405                 if (a->x == b->x + b->width)
    00406                 {
    00407                         a->x -= b->width;
    00408                         a->width += b->width;
    00409                         b->height -= a->height;
    00410                         refactored = TRUE;
    00411                 }
    00412                 else if (a->x + a->width == b->x)
    00413                 {
    00414                         a->width += b->width;
    00415                         b->height -= a->height;
    00416                         refactored = TRUE;
    00417                 }
    00418                 if (refactored) fprintf (stderr, "REFACTOR 4\n");
    00419         }
    00420         return refactored;
    00421 }
    00422
    00423 static GList*
    00424 _combine_update_rects (GList *q, int lookahead_n)
    00425 {
    00426         int i = 0;
    00427         GdkRectangle *a = q->data;
    00428         GList *p = q;
    00429         while (i < lookahead_n && p && p->next)
    00430         {
    00431                 if (_combine_rects (a, q->next->data))
    00432                 {
    00433                         q = g_list_delete_link (q, p->next);
    00434                 }
    00435                 else
    00436                 {
    00437                         p = p->next;
    00438                         ++i;
    00439                 }
    00440         }
    00441         return q;
    00442 }
    00443 #endif
    00444
    00445 /*#define _is_horizontal_rect(r)   (((2 * (r)->width / 3 * (r)->height)) > 1)*/
    00446 /*#define _is_vertical_rect(r)   (((2 * (r)->height / 3 * (r)->width)) > 1)*/
    00447 #define _is_horizontal_rect(r) ((r)->width > (r)->height)
    00448 #define _is_vertical_rect(r)   ((r)->height > (r)->width)
    00449
    00456 static GList *
    00457 _coalesce_update_rects (GList *q, int min_coalesce_length)
    00458 {
    00459         GdkRectangle *v = NULL, *h = NULL;
    00460         GList *compact_queue = NULL;
    00461 /*      fprintf (stderr, "starting queue length = %d\n", g_list_length (q)); */
    00462         if (g_list_length (q) < min_coalesce_length)
    00463                 return g_list_copy (q);
    00464         while (q)
    00465         {
    00466                 if (_is_vertical_rect ((GdkRectangle *) (q->data)))
    00467                 {
    00468                         if (v) gdk_rectangle_union (v, q->data, v);
    00469                         else
    00470                         {
    00471                                 v = g_new0 (GdkRectangle, 1);
    00472                                 *v = *(GdkRectangle *)q->data;
    00473                         }
    00474                 }
    00475                 else if (_is_horizontal_rect ((GdkRectangle *) (q->data)))
    00476                 {
    00477                         if (h) gdk_rectangle_union (h, q->data, h);
    00478                         else
    00479                         {
    00480                                 h = g_new0 (GdkRectangle, 1);
    00481                                 *h = *(GdkRectangle *)q->data;
    00482                         }
    00483                 }
    00484                 else
    00485                         compact_queue = g_list_prepend (compact_queue, q->data);
    00486                 q = q->next;
    00487         };
    00488         if (v)
    00489                 compact_queue = g_list_prepend (compact_queue, v);
    00490         if (h)
    00491                 compact_queue = g_list_prepend (compact_queue, h);
    00492 /*      fprintf (stderr, "ending queue length = %d\n", g_list_length (compact_queue));*/
    00493         /* don't free the original queue, that's the caller's responsibility */
    00494         return compact_queue;
    00495 }
    00496
    00497 #ifdef BROKEN_COALESCE_STUFF_GETS_FIXED
    00498 static GList *
    00499 _smartbutbroken_coalesce_update_rects (GList *q, int lookahead_n)
    00500 {
    00501         int i = 0, len;
    00502         fprintf (stderr, "starting queue length = %d\n", g_list_length (q));
    00503         do {
    00504                 GdkRectangle *a;
    00505                 len = g_list_length (q);
    00506                 q = _combine_update_rects (q, lookahead_n);
    00507                 a = q->data;
    00508                 while (i < lookahead_n && q && q->next)
    00509                 {
    00510                         if (_refactor_rects (a, q->next->data))
    00511                                 break;
    00512                         else
    00513                                 ++i;
    00514                 }
    00515                 q = _combine_update_rects (q, lookahead_n);
    00516         } while (g_list_length (q) < len);
    00517         fprintf (stderr, "ending queue length = %d\n", g_list_length (q));
    00518         return q;
    00519 }
    00520 #endif
    00521
    00525 static GdkRectangle
    00526 _rectangle_clip_to_rectangle (GdkRectangle area,
    00527                               GdkRectangle clip_rect)
    00528 {
    00529         GdkRectangle clipped;
    00530         clipped.x = MAX (area.x, clip_rect.x);
    00531         clipped.y = MAX (area.y, clip_rect.y);
    00532         clipped.width = MIN ((area.x + area.width), (clip_rect.x + clip_rect.width)) - clipped.x;
    00533         clipped.height = MIN ((area.y + area.height), (clip_rect.y + clip_rect.height)) - clipped.y;
    00534         return clipped;
    00535 }
    00536
    00537 static GdkRectangle
    00538 _rectangle_clip_to_bounds (GdkRectangle area,
    00539                            GNOME_Magnifier_RectBounds *clip_bounds)
    00540 {
    00541         area.x = MAX (area.x, clip_bounds->x1);
    00542         area.x = MIN (area.x, clip_bounds->x2);
    00543         area.width = MIN (area.width, clip_bounds->x2 - area.x);
    00544         area.y = MAX (area.y, clip_bounds->y1);
    00545         area.y = MIN (area.y, clip_bounds->y2);
    00546         area.height = MIN (area.height, clip_bounds->y2 - area.y);
    00547         return area;
    00548 }
    00549
    00550 static GdkRectangle
    00551 zoom_region_clip_to_source (ZoomRegion *zoom_region,
    00552                             GdkRectangle area)
    00553 {
    00554     GNOME_Magnifier_RectBounds *source_rect_ptr;
    00555     if (zoom_region && zoom_region->priv && zoom_region->priv->parent)
    00556     {
    00557         source_rect_ptr = &((Magnifier *)zoom_region->priv->parent)->source_bounds;
    00558         DEBUG_RECT ("clipping to source bounds", zoom_region_rect_from_bounds (zoom_region, source_rect_ptr));
    00559         return _rectangle_clip_to_bounds (area, source_rect_ptr);
    00560     }
    00561     return area;
    00562 }
    00563
    00564 static GdkRectangle
    00565 zoom_region_clip_to_exposed_target (ZoomRegion *zoom_region,
    00566                                     GdkRectangle area)
    00567 {
    00568         GNOME_Magnifier_RectBounds onscreen_target, *source_area;
    00569         source_area = &zoom_region->priv->source_area;
    00570
    00571         onscreen_target.x1 = MAX (floor (zoom_region->priv->exposed_bounds.x1
    00572                                          / zoom_region->xscale),
    00573                                          source_area->x1);
    00574         onscreen_target.y1 = MAX (floor (zoom_region->priv->exposed_bounds.y1
    00575                                          / zoom_region->yscale),
    00576                                          source_area->y1);
    00577         onscreen_target.x2 = MIN (ceil (zoom_region->priv->exposed_bounds.x2
    00578                                         / zoom_region->xscale),
    00579                                         source_area->x2);
    00580         onscreen_target.y2 = MIN (ceil (zoom_region->priv->exposed_bounds.y2
    00581                                         / zoom_region->yscale),
    00582                                         source_area->y2);
    00583
    00584         return _rectangle_clip_to_bounds (area, &onscreen_target);
    00585 }
    00586
    00587 static GdkRectangle
    00588 zoom_region_clip_to_scaled_pixmap (ZoomRegion *zoom_region,
    00589                                    GdkRectangle area)
    00590 {
    00591         GdkRectangle pixmap_area = {0, 0, 0, 0};
    00592         if (zoom_region->priv && zoom_region->priv->pixmap)
    00593         {
    00594             gdk_drawable_get_size (zoom_region->priv->pixmap, &pixmap_area.width, &pixmap_area.height);
    00595             return _rectangle_clip_to_rectangle (area, pixmap_area);
    00596         }
    00597         else
    00598             return area;
    00599 }
    00600
    00601 static GdkRectangle
    00602 zoom_region_clip_to_window (ZoomRegion *zoom_region,
    00603                             GdkRectangle area)
    00604 {
    00605         GdkRectangle window_rect;
    00606
    00607         /* we can just return ATM because _rectangle_clip_to_rectangle is unimplemented now */
    00608
    00609         return area;
    00610
    00611         if (zoom_region->priv->w->window)
    00612                 gdk_drawable_get_size (GDK_DRAWABLE (zoom_region->priv->w->window),
    00613                                        &window_rect.x,
    00614                                        &window_rect.y);
    00615         else
    00616         {
    00617                 window_rect.x = 0;
    00618                 window_rect.y = 0;
    00619         }
    00620         return _rectangle_clip_to_rectangle (area, window_rect);
    00621 }
    00622
    00623 static GdkRectangle
    00624 zoom_region_source_rect_from_view_bounds (ZoomRegion *zoom_region,
    00625                                           const GNOME_Magnifier_RectBounds *view_bounds)
    00626 {
    00627         GdkRectangle source_rect;
    00628         source_rect.x = floor ((view_bounds->x1 + zoom_region->priv->exposed_bounds.x1)
    00629                                / zoom_region->xscale);
    00630         source_rect.y = floor ((view_bounds->y1 + zoom_region->priv->exposed_bounds.y1)
    00631                                 / zoom_region->yscale);
    00632         source_rect.width = ceil ((view_bounds->x2 - view_bounds->x1) / zoom_region->xscale) + 1;
    00633         source_rect.height = ceil ((view_bounds->y2 - view_bounds->y1) / zoom_region->yscale) + 1;
    00634         return source_rect;
    00635 }
    00636
    00637 static GdkRectangle
    00638 zoom_region_view_rect_from_source_rect (ZoomRegion *zoom_region,
    00639                                         const GdkRectangle source_rect)
    00640 {
    00641         GdkRectangle view_rect;
    00642         view_rect.x = source_rect.x * zoom_region->xscale - zoom_region->priv->exposed_bounds.x1;
    00643         view_rect.y = source_rect.y * zoom_region->yscale - zoom_region->priv->exposed_bounds.y1;
    00644         view_rect.width = source_rect.width * zoom_region->xscale;
    00645         view_rect.height = source_rect.height * zoom_region->yscale;
    00646         DEBUG_RECT ("source", source_rect);
    00647         DEBUG_RECT ("converted to view-rect", view_rect);
    00648         return view_rect;
    00649 }
    00650
    00651 static GdkRectangle
    00652 zoom_region_source_rect_from_view_rect (ZoomRegion *zoom_region,
    00653                                         const GdkRectangle view_rect)
    00654 {
    00655         GdkRectangle source_rect;
    00656         source_rect.x = floor ((view_rect.x + zoom_region->priv->exposed_bounds.x1)
    00657                                / zoom_region->xscale);
    00658         source_rect.y = floor ((view_rect.y + zoom_region->priv->exposed_bounds.y1)
    00659                                 / zoom_region->yscale);
    00660         source_rect.width = ceil (view_rect.width / zoom_region->xscale) + 1;
    00661         source_rect.height = ceil (view_rect.height / zoom_region->yscale) + 1;
    00662         return source_rect;
    00663 }
    00664
    00665 static GdkRectangle
    00666 zoom_region_rect_from_bounds (ZoomRegion *zoom_region,
    00667                               const GNOME_Magnifier_RectBounds *bounds)
    00668 {
    00669         GdkRectangle rect;
    00670         rect.x = bounds->x1;
    00671         rect.y = bounds->y1;
    00672         rect.width = bounds->x2 - bounds->x1;
    00673         rect.height = bounds->y2 - bounds->y1;
    00674         return rect;
    00675 }
    00676
    00679 static CORBA_boolean
    00680 zoom_region_update_scale (ZoomRegion *zoom_region, gdouble x, gdouble y)
    00681 {
    00682         gdouble x_old = zoom_region->xscale;
    00683         gdouble y_old = zoom_region->yscale;
    00684         long x_move, y_move;
    00685
    00686         zoom_region->xscale = x;
    00687         zoom_region->yscale = y;
    00688
    00689         if (zoom_region->priv->scaled_pixbuf)
    00690                 g_object_unref (zoom_region->priv->scaled_pixbuf);
    00691         zoom_region->priv->scaled_pixbuf =
    00692                 gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, (zoom_region->priv->source_area.x2 - zoom_region->priv->source_area.x1) * zoom_region->xscale + 1, (zoom_region->priv->source_area.y2 - zoom_region->priv->source_area.y1) * zoom_region->yscale + 1);
    00693
    00694         if (zoom_region->priv->pixmap)
    00695                 g_object_unref (zoom_region->priv->pixmap);
    00696
    00697         if (zoom_region_create_pixmap (zoom_region) ==
    00698             ZOOM_REGION_ERROR_TOO_BIG) {
    00699                 zoom_region->xscale = x_old;
    00700                 zoom_region->yscale = y_old;
    00701                 zoom_region_create_pixmap (zoom_region);
    00702                 g_object_unref (zoom_region->priv->scaled_pixbuf);
    00703
    00704                 /* only create a scaled image big enough for the target
    00705                  * display, for now */
    00706                 zoom_region->priv->scaled_pixbuf = gdk_pixbuf_new (
    00707                         GDK_COLORSPACE_RGB, FALSE, 8, (zoom_region->priv->source_area.x2 - zoom_region->priv->source_area.x1) * zoom_region->xscale + 1, (zoom_region->priv->source_area.y2 - zoom_region->priv->source_area.y1) * zoom_region->yscale + 1);
    00708
    00709                 return CORBA_FALSE;
    00710         }
    00711
    00712         zoom_region_get_move_x_y (zoom_region, &x_move, &y_move);
    00713         zoom_region->priv->exposed_bounds.x1 = x_move * zoom_region->xscale;
    00714         zoom_region->priv->exposed_bounds.y1 = y_move * zoom_region->yscale;
    00715         zoom_region_recompute_exposed_bounds (zoom_region);
    00716         zoom_region_update_current (zoom_region);
    00717
    00718         return CORBA_TRUE;
    00719 }
    00720
    00721 static void
    00722 zoom_region_queue_update (ZoomRegion *zoom_region,
    00723                           const GdkRectangle update_rect)
    00724 {
    00725         GdkRectangle *rect =
    00726                 g_new0 (GdkRectangle, 1);
    00727         *rect = update_rect;
    00728
    00729 #ifdef ZOOM_REGION_DEBUG
    00730         g_assert (zoom_region->alive);
    00731 #endif
    00732         DEBUG_RECT ("queueing update", *rect);
    00733
    00734         zoom_region->priv->q =
    00735                 g_list_prepend (zoom_region->priv->q, rect);
    00736         if (zoom_region->priv && zoom_region->priv->update_handler_id == 0)
    00737                 zoom_region->priv->update_handler_id =
    00738                         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
    00739                                          zoom_region_process_updates,
    00740                                          zoom_region,
    00741                                          NULL);
    00742 }
    00743
    00744 static void
    00745 zoom_region_update_current (ZoomRegion *zoom_region)
    00746 {
    00747 #ifdef ZOOM_REGION_DEBUG
    00748         g_assert (zoom_region->alive);
    00749 #endif
    00750         if (zoom_region->priv)
    00751         {
    00752                 gboolean pixmap_valid = GDK_IS_DRAWABLE (zoom_region->priv->pixmap);
    00753                 if (!pixmap_valid)
    00754                         pixmap_valid = (zoom_region_create_pixmap (zoom_region) == ZOOM_REGION_ERROR_NONE);
    00755                 if (pixmap_valid)
    00756                         zoom_region_update (zoom_region,
    00757                                             zoom_region_source_rect_from_view_bounds (
    00758                                                     zoom_region,
    00759                                                     &zoom_region->viewport));
    00760         }
    00761 }
    00762
    00763 static GdkRectangle
    00764 zoom_region_cursor_rect (ZoomRegion *zoom_region)
    00765 {
    00766         GdkRectangle rect = {0, 0, 0, 0};
    00767         Magnifier *magnifier = zoom_region->priv->parent;
    00768         GdkDrawable *cursor = NULL;
    00769         if (magnifier)
    00770                 cursor = magnifier_get_cursor (magnifier);
    00771         if (cursor)
    00772         {
    00773                 rect.x = zoom_region->priv->last_cursor_pos.x;
    00774                 rect.y = zoom_region->priv->last_cursor_pos.y;
    00775                 rect = zoom_region_view_rect_from_source_rect (zoom_region, rect);
    00776                 rect.x -= magnifier->cursor_hotspot.x;
    00777                 rect.y -= magnifier->cursor_hotspot.y;
    00778                 gdk_drawable_get_size (cursor, &rect.width, &rect.height);
    00779         }
    00780         return rect;
    00781 }
    00782
    00783 static void
    00784 zoom_region_unpaint_crosswire_cursor (ZoomRegion *zoom_region,
    00785                                       GdkRectangle *clip_rect)
    00786 {
    00787         Magnifier *magnifier = zoom_region->priv->parent;
    00788         GdkRectangle vline_rect, hline_rect;
    00789         GdkPoint cursor_pos;
    00790
    00791 #ifdef ZOOM_REGION_DEBUG
    00792         g_assert (zoom_region->alive);
    00793 #endif
    00794         if (!magnifier || magnifier->crosswire_size <= 0) return;
    00795
    00796         cursor_pos = zoom_region->priv->last_drawn_crosswire_pos;
    00797         vline_rect.x = cursor_pos.x - magnifier->crosswire_size/2;
    00798         vline_rect.y = clip_rect ? clip_rect->y : 0;
    00799         vline_rect.width = MAX (magnifier->crosswire_size, 1);
    00800         vline_rect.height = clip_rect ? clip_rect->height : 4096;
    00801         hline_rect.x = clip_rect ? clip_rect->x : 0;
    00802         hline_rect.y = cursor_pos.y - magnifier->crosswire_size/2;
    00803         hline_rect.width = clip_rect ? clip_rect->width : 4096;
    00804         hline_rect.height = MAX (magnifier->crosswire_size, 1);
    00805
    00806         zoom_region_paint_pixmap (zoom_region, &vline_rect);
    00807         zoom_region_paint_pixmap (zoom_region, &hline_rect);
    00808 }
    00809
    00810 static void
    00811 zoom_region_paint_crosswire_cursor (ZoomRegion *zoom_region, GdkRectangle *clip_rect)
    00812 {
    00813         Magnifier *magnifier = zoom_region->priv->parent;
    00814         static GdkColormap *cmap;
    00815         static GdkColor last_color;
    00816         static gboolean last_color_init = FALSE;
    00817         GdkGCValues values;
    00818         GdkRectangle rect;
    00819         GdkDrawable *cursor;
    00820         GdkColor color = {0, 0, 0, 0};
    00821         int x_start = 0, y_start = 0, x_end = 4096, y_end = 4096;
    00822         int x_left_clip = 0, x_right_clip = 0, y_top_clip = 0, y_bottom_clip = 0;
    00823         int csize = 0;
    00824
    00825 #ifdef ZOOM_REGION_DEBUG
    00826         g_assert (zoom_region->alive);
    00827 #endif
    00828         if (!(magnifier &&
    00829               zoom_region->priv->w->window &&
    00830               GDK_IS_DRAWABLE (zoom_region->priv->w->window) &&
    00831               magnifier->crosswire_size > 0)) return;
    00832
    00833         if (zoom_region->priv->crosswire_gc == NULL)
    00834         {
    00835                 zoom_region->priv->crosswire_gc = gdk_gc_new (zoom_region->priv->w->window);
    00836                 cmap = gdk_gc_get_colormap(zoom_region->priv->crosswire_gc);
    00837                 last_color_init = FALSE;
    00838         }
    00839
    00840         if (magnifier->crosswire_color == 0)
    00841         {
    00842                 color.red = 0xFFFF;
    00843                 color.blue = 0xFFFF;
    00844                 color.green = 0xFFFF;
    00845                 values.function = GDK_INVERT;
    00846         }
    00847         else
    00848         {
    00849                 color.red = (magnifier->crosswire_color & 0xFF0000) >> 8;
    00850                 color.green = (magnifier->crosswire_color & 0xFF00);
    00851                 color.blue = (magnifier->crosswire_color & 0xFF) << 8;
    00852                 values.function = GDK_COPY;
    00853         }
    00854
    00855         values.foreground = color;
    00856
    00857         /* Only reset colors if they have changed */
    00858     if (!last_color_init || color.red != last_color.red ||
    00859             color.blue != last_color.blue || color.green != last_color.green)
    00860         {
    00861                 if (cmap)
    00862                 {
    00863                         gdk_rgb_find_color (cmap, &(values.foreground));
    00864                         gdk_gc_set_values(zoom_region->priv->crosswire_gc, &values, GDK_GC_FUNCTION | GDK_GC_FOREGROUND);
    00865                 }
    00866                 else
    00867                 {
    00868                         gdk_gc_set_values(zoom_region->priv->crosswire_gc, &values, GDK_GC_FUNCTION);
    00869                 }
    00870
    00871                 last_color.red   = color.red;
    00872                 last_color.blue  = color.blue;
    00873                 last_color.green = color.green;
    00874                 last_color_init  = TRUE;
    00875         }
    00876
    00877         rect.x = zoom_region->priv->last_cursor_pos.x;
    00878         rect.y = zoom_region->priv->last_cursor_pos.y;
    00879         rect.width = 0;
    00880         rect.height = 0;
    00881         rect = zoom_region_view_rect_from_source_rect (zoom_region, rect);
    00882         if (clip_rect) gdk_gc_set_clip_rectangle (zoom_region->priv->crosswire_gc, clip_rect);
    00883         else gdk_gc_set_clip_rectangle (zoom_region->priv->crosswire_gc, NULL);
    00884
    00885         if ((cursor = magnifier_get_cursor (magnifier))) {
    00886                 gdk_drawable_get_size (cursor, &csize, &csize);
    00887         }
    00888
    00889         if (magnifier->crosswire_length) {
    00890                 if (magnifier->crosswire_clip) {
    00891                         x_start = rect.x - magnifier->cursor_hotspot.x -
    00892                                 magnifier->crosswire_length;
    00893                         x_end = rect.x +
    00894                                 (csize - magnifier->cursor_hotspot.x) +
    00895                                 magnifier->crosswire_length;
    00896                         y_start = rect.y - magnifier->cursor_hotspot.y -
    00897                                 magnifier->crosswire_length;
    00898                         y_end = rect.y +
    00899                                 (csize - magnifier->cursor_hotspot.y) +
    00900                                 magnifier->crosswire_length;
    00901                 } else {
    00902                         x_start = rect.x - magnifier->crosswire_length;
    00903                         x_end = rect.x + magnifier->crosswire_length;
    00904                         y_start = rect.y - magnifier->crosswire_length;
    00905                         y_end = rect.y + magnifier->crosswire_length;
    00906                 }
    00907         }
    00908
    00909         if (magnifier->crosswire_clip)
    00910         {
    00911                 y_top_clip = rect.y - magnifier->cursor_hotspot.y -
    00912                         magnifier->crosswire_size;
    00913                 y_bottom_clip = rect.y +
    00914                         (csize - magnifier->cursor_hotspot.y) +
    00915                         magnifier->crosswire_size;
    00916                 x_left_clip = rect.x - magnifier->cursor_hotspot.x -
    00917                         magnifier->crosswire_size;
    00918                 x_right_clip = rect.x +
    00919                         (csize - magnifier->cursor_hotspot.x) +
    00920                         magnifier->crosswire_size;
    00921
    00922         }
    00923         if (magnifier->crosswire_size == 1) {
    00924                 if (magnifier->crosswire_clip) {
    00925                         gdk_draw_line (zoom_region->priv->w->window,
    00926                                        zoom_region->priv->crosswire_gc,
    00927                                        rect.x, y_top_clip, rect.x,
    00928                                        y_bottom_clip);
    00929                         gdk_draw_line (zoom_region->priv->w->window,
    00930                                        zoom_region->priv->crosswire_gc,
    00931                                        x_left_clip, rect.y, x_right_clip,
    00932                                        rect.y);
    00933                 }
    00934                 gdk_draw_line (zoom_region->priv->w->window,
    00935                                zoom_region->priv->crosswire_gc,
    00936                                rect.x, y_start, rect.x, y_end);
    00937                 gdk_draw_line (zoom_region->priv->w->window,
    00938                                zoom_region->priv->crosswire_gc,
    00939                                x_start, rect.y, x_end, rect.y);
    00940         }
    00941         else {
    00942                 if (magnifier->crosswire_clip ) {
    00943                         gdk_draw_rectangle (
    00944                                 zoom_region->priv->w->window,
    00945                                 zoom_region->priv->crosswire_gc, TRUE,
    00946                                 rect.x - magnifier->crosswire_size / 2,
    00947                                 y_top_clip, magnifier->crosswire_size,
    00948                                 y_bottom_clip - y_top_clip);
    00949                         gdk_draw_rectangle (
    00950                                 zoom_region->priv->w->window,
    00951                                 zoom_region->priv->crosswire_gc, TRUE,
    00952                                 x_left_clip,
    00953                                 rect.y - magnifier->crosswire_size / 2,
    00954                                 x_right_clip - x_left_clip,
    00955                                 magnifier->crosswire_size);
    00956                 }
    00957                 gdk_draw_rectangle (
    00958                         zoom_region->priv->w->window,
    00959                         zoom_region->priv->crosswire_gc, TRUE,
    00960                         rect.x - magnifier->crosswire_size / 2, y_start,
    00961                         magnifier->crosswire_size, y_end - y_start);
    00962                 gdk_draw_rectangle (
    00963                         zoom_region->priv->w->window,
    00964                         zoom_region->priv->crosswire_gc, TRUE,
    00965                         x_start, rect.y - magnifier->crosswire_size / 2,
    00966                         x_end - x_start, magnifier->crosswire_size);
    00967         }
    00968 }
    00969
    00970 static void
    00971 zoom_region_unpaint_cursor (ZoomRegion *zoom_region, GdkRectangle *clip_rect)
    00972 {
    00973 #ifdef ZOOM_REGION_DEBUG
    00974         g_assert (zoom_region->alive);
    00975 #endif
    00976         zoom_region_paint_pixmap (zoom_region,
    00977                                   &zoom_region->priv->cursor_backing_rect);
    00978 }
    00979
    00980
    00981 static void
    00982 zoom_region_paint_cursor (ZoomRegion *zoom_region,
    00983                           GdkRectangle *clip_rect)
    00984 {
    00985         GdkGCValues values;
    00986         GdkRectangle rect, intersct;
    00987         GdkRectangle fullscreen;
    00988         Magnifier *magnifier = zoom_region->priv->parent;
    00989         rect = zoom_region_cursor_rect (zoom_region);
    00990 #ifdef ZOOM_REGION_DEBUG
    00991         g_assert (zoom_region->alive);
    00992 #endif
    00993         if (!zoom_region->draw_cursor)
    00994                 return;
    00995
    00996         if (clip_rect == NULL)
    00997         {
    00998                 fullscreen = zoom_region_rect_from_bounds (zoom_region,
    00999                                                            &zoom_region->viewport);
    01000                 clip_rect = &fullscreen;
    01001         }
    01002         /* save the unclipped cursor pos for 'undrawing' the crosswire, the clipped one is no good */
    01003         zoom_region->priv->last_drawn_crosswire_pos.x = rect.x + magnifier->cursor_hotspot.x;
    01004         zoom_region->priv->last_drawn_crosswire_pos.y = rect.y + magnifier->cursor_hotspot.y;
    01005
    01006         if (gdk_rectangle_intersect (clip_rect, &rect, &intersct))
    01007         {
    01008                 int width = 0, height = 0;
    01009
    01010                 GdkDrawable *cursor = magnifier_get_cursor (magnifier);
    01011                 if (!cursor)
    01012                         return;
    01013                 else if (!GDK_IS_DRAWABLE (cursor)) g_message ("cursor isn't DRAWABLE!");
    01014                 zoom_region->priv->cursor_backing_rect = rect;
    01015                 if (zoom_region->priv->cursor_backing_pixels) {
    01016                         gdk_drawable_get_size (zoom_region->priv->cursor_backing_pixels,
    01017                                                &width, &height);
    01018                 }
    01019                 if (rect.width != width || rect.height != height)
    01020                 {
    01021                         if (zoom_region->priv->cursor_backing_pixels) {
    01022                                 g_object_unref (zoom_region->priv->cursor_backing_pixels);
    01023                         }
    01024                         zoom_region->priv->cursor_backing_pixels =
    01025                                 gdk_pixmap_new (zoom_region->priv->w->window,
    01026                                                 rect.width,
    01027                                                 rect.height,
    01028                                                 -1);
    01029                 }
    01030                 if (zoom_region->priv->w->window != NULL)
    01031                 {
    01032                         if (zoom_region->priv->default_gc == NULL)
    01033                                 zoom_region->priv->default_gc = gdk_gc_new(zoom_region->priv->w->window);
    01034                         gdk_draw_drawable (zoom_region->priv->cursor_backing_pixels,
    01035                                      zoom_region->priv->default_gc,
    01036                                      zoom_region->priv->w->window,
    01037                                      rect.x,
    01038                                      rect.y,
    01039                                      0, 0,
    01040                                      rect.width,
    01041                                      rect.height);
    01042                 }
    01043                 DEBUG_RECT ("painting", rect);
    01044                 if (cursor && zoom_region->priv->w && GDK_IS_DRAWABLE (zoom_region->priv->w->window))
    01045                 {
    01046                     if (zoom_region->priv->paint_cursor_gc == NULL)
    01047                                 zoom_region->priv->paint_cursor_gc = gdk_gc_new (zoom_region->priv->w->window);
    01048
    01049                         gdk_gc_set_clip_rectangle (zoom_region->priv->paint_cursor_gc, clip_rect);
    01050                         values.clip_x_origin = rect.x;
    01051                         values.clip_y_origin = rect.y;
    01052                         values.clip_mask = magnifier->priv->cursor_mask;
    01053                         gdk_gc_set_values(zoom_region->priv->paint_cursor_gc, &values, GDK_GC_CLIP_X_ORIGIN |
    01054                                           GDK_GC_CLIP_Y_ORIGIN  | GDK_GC_CLIP_MASK);
    01055
    01056                         gdk_draw_rectangle (zoom_region->priv->w->window,
    01057                                            zoom_region->priv->paint_cursor_gc,
    01058                                            TRUE,
    01059                                            rect.x, rect.y, rect.width, rect.height);
    01060
    01061                         gdk_draw_drawable (zoom_region->priv->w->window,
    01062                                            zoom_region->priv->paint_cursor_gc,
    01063                                            cursor,
    01064                                            0, 0,
    01065                                            rect.x,
    01066                                            rect.y,
    01067                                            rect.width,
    01068                                            rect.height);
    01069                 }
    01070         }
    01071 }
    01072
    01077 static void
    01078 zoom_region_coalesce_updates (ZoomRegion *zoom_region)
    01079 {
    01080         /* TODO: lock the queue ? */
    01081         GList *q;
    01082         int lookahead_n = 4; /* 'distance' to look ahead in queue */
    01083         int max_qlen = 50;
    01084
    01085         if (zoom_region->priv && zoom_region->priv->q && g_list_length (zoom_region->priv->q) > max_qlen)
    01086         {
    01087                 g_list_free (zoom_region->priv->q);
    01088                 zoom_region->priv->q = NULL; /* just discard and update everything */
    01089                 /* CAUTION: this can be an expensive operation! */
    01090                 zoom_region_queue_update (zoom_region, zoom_region_rect_from_bounds
    01091                                           (zoom_region, &zoom_region->priv->source_area));
    01092         }
    01093         else
    01094
    01095         if (zoom_region->priv && zoom_region->priv->q &&
    01096             (g_list_length (zoom_region->priv->q) > 1) && can_coalesce)
    01097         {
    01098                 q = g_list_reverse (g_list_copy (zoom_region->priv->q));
    01099                 if (q)
    01100                 {
    01101                         GList *coalesce_copy;
    01102                         if (zoom_region->coalesce_func)
    01103                         {
    01104                                 GList *new;
    01105                                 coalesce_copy = (*zoom_region->coalesce_func) (q, lookahead_n);
    01106                                 new = g_list_reverse (coalesce_copy);
    01107                                 g_list_free (zoom_region->priv->q);
    01108                                 zoom_region->priv->q = new;
    01109                         }
    01110                         g_list_free (q);
    01111                 }
    01112         }
    01113 }
    01114
    01115
    01116 static void
    01117 zoom_region_paint_border (ZoomRegion *zoom_region)
    01118 {
    01119         GdkColor color;
    01120
    01121 #ifdef ZOOM_REGION_DEBUG
    01122         g_assert (zoom_region->alive);
    01123 #endif
    01124         if ((zoom_region->border_size_left > 0 ||
    01125              zoom_region->border_size_top > 0 ||
    01126              zoom_region->border_size_right > 0 ||
    01127              zoom_region->border_size_bottom > 0) &&
    01128             (zoom_region->priv->border->window)) {
    01129                 color.red = (((zoom_region->border_color & 0xFF0000) >> 16) *
    01130                              65535) / 255;
    01131                 color.green = (((zoom_region->border_color & 0xFF00) >> 8) *
    01132                                65535) / 255;
    01133                 color.blue = ((zoom_region->border_color & 0xFF) * 65535) /
    01134                         255;
    01135
    01136 #ifdef DEBUG_BORDER
    01137                 fprintf (stderr, "border color triple RGB=%d|%d|%d\n",
    01138                          color.red, color.green, color.blue);
    01139 #endif
    01140
    01141                 gtk_widget_modify_bg (zoom_region->priv->border,
    01142                                       GTK_STATE_NORMAL, &color);
    01143         }
    01144 }
    01145
    01146 static void
    01147 zoom_region_paint_pixmap (ZoomRegion *zoom_region,
    01148                           GdkRectangle *area)
    01149 {
    01150 #ifdef ZOOM_REGION_DEBUG
    01151         g_assert (zoom_region->alive);
    01152 #endif
    01153         g_assert (zoom_region->priv);
    01154         g_assert (zoom_region->priv->w);
    01155
    01156         if (!GDK_IS_DRAWABLE (zoom_region->priv->w->window)) return;
    01157         if (zoom_region->priv->default_gc == NULL)
    01158                 zoom_region->priv->default_gc = gdk_gc_new (zoom_region->priv->w->window);
    01159
    01160         if (zoom_region->priv->pixmap && GDK_IS_DRAWABLE (zoom_region->priv->w->window))
    01161         {
    01162                 gdk_draw_drawable (zoom_region->priv->w->window,
    01163                                    zoom_region->priv->default_gc,
    01164                                    zoom_region->priv->pixmap,
    01165                                    area->x + zoom_region->priv->exposed_bounds.x1 - zoom_region->priv->source_area.x1 * zoom_region->xscale,
    01166                                    area->y + zoom_region->priv->exposed_bounds.y1 - zoom_region->priv->source_area.y1 * zoom_region->yscale,
    01167                                    area->x,
    01168                                    area->y,
    01169                                    area->width,
    01170                                    area->height);
    01171         }
    01172 }
    01173
    01177 static void
    01178 zoom_region_paint (ZoomRegion *zoom_region,
    01179                    GdkRectangle *area)
    01180 {
    01181         GdkRectangle paint_area;
    01182
    01183 #ifdef ZOOM_REGION_DEBUG
    01184         g_assert (zoom_region->alive);
    01185 #endif
    01186         DEBUG_RECT ("painting (clipped)", *area);
    01187         paint_area = zoom_region_clip_to_window (zoom_region, *area);
    01188         zoom_region_paint_pixmap (zoom_region, &paint_area);
    01189         zoom_region_paint_cursor (zoom_region, &paint_area);
    01190         zoom_region_paint_crosswire_cursor (zoom_region, &paint_area);
    01191 }
    01192
    01193 static ZoomRegionPixmapCreationError
    01194 zoom_region_create_pixmap (ZoomRegion *zoom_region)
    01195 {
    01196 #ifdef ZOOM_REGION_DEBUG
    01197         g_assert (zoom_region->alive);
    01198 #endif
    01199         if (zoom_region->priv->w && GDK_IS_DRAWABLE (zoom_region->priv->w->window))
    01200         {
    01201                 long width = (zoom_region->priv->source_area.x2 -
    01202                               zoom_region->priv->source_area.x1) * zoom_region->xscale;
    01203                 long height = (zoom_region->priv->source_area.y2 -
    01204                                zoom_region->priv->source_area.y1) * zoom_region->yscale;
    01205                 zoom_region->priv->pixmap =
    01206                         gdk_pixmap_new (
    01207                                 zoom_region->priv->w->window,
    01208                                 width,
    01209                                 height,
    01210                                 gdk_drawable_get_depth (
    01211                                         zoom_region->priv->w->window));
    01212
    01213                 if (gmag_gs_error_check ()) {
    01214                         zoom_region->priv->pixmap = NULL;
    01215                         return ZOOM_REGION_ERROR_TOO_BIG;
    01216                 }
    01217
    01218                 DEBUG_RECT("viewport", zoom_region_source_rect_from_view_bounds
    01219                            (zoom_region, &zoom_region->viewport));
    01220                 DEBUG_RECT("source", zoom_region_rect_from_bounds
    01221                            (zoom_region, &((Magnifier*)zoom_region->priv->parent)->source_bounds));
    01222
    01223                 return ZOOM_REGION_ERROR_NONE;
    01224         }
    01225
    01226         return ZOOM_REGION_ERROR_NO_TARGET_DRAWABLE;
    01227 }
    01228
    01229 static void
    01230 zoom_region_expose_handler (GtkWindow * w,
    01231                             GdkEventExpose *event,
    01232                             gpointer data)
    01233 {
    01234         ZoomRegion *zoom_region = data;
    01235         DEBUG_RECT ("expose", event->area);
    01236
    01237 #ifdef ZOOM_REGION_DEBUG
    01238         g_assert (zoom_region->alive);
    01239 #endif
    01240         if (zoom_region->priv->pixmap == NULL)
    01241         {
    01242                 ZoomRegionPixmapCreationError ret;
    01243                 /* TODO: scale down if this fails here */
    01244                 while ((ret = zoom_region_create_pixmap (zoom_region)) ==
    01245                     ZOOM_REGION_ERROR_TOO_BIG) {
    01246                         zoom_region->xscale -= 1.0;
    01247                         zoom_region->yscale -= 1.0;
    01248                         zoom_region->priv->pixmap = NULL;
    01249                         g_warning ("Scale factor too big to fit in memory; shrinking.");
    01250                 }
    01251                 if (ret == ZOOM_REGION_ERROR_NO_TARGET_DRAWABLE)
    01252                         g_warning ("create-pixmap: no target drawable");
    01253                 else
    01254                         zoom_region_update_pixmap (zoom_region, event->area,
    01255                                                    NULL);
    01256         }
    01257         zoom_region_paint (zoom_region, &event->area);
    01258 }
    01259
    01260 static void
    01261 zoom_region_update_cursor (ZoomRegion *zoom_region, int dx, int dy,
    01262                            GdkRectangle *clip_rect)
    01263 {
    01264 #ifdef ZOOM_REGION_DEBUG
    01265         g_assert (zoom_region->alive);
    01266 #endif
    01267         zoom_region_unpaint_crosswire_cursor (zoom_region, clip_rect);
    01268         zoom_region_unpaint_cursor (zoom_region, clip_rect);
    01269         zoom_region->priv->cursor_backing_rect.x += dx;
    01270         zoom_region->priv->cursor_backing_rect.y += dy;
    01271         zoom_region->priv->last_drawn_crosswire_pos.x += dx;
    01272         zoom_region->priv->last_drawn_crosswire_pos.y += dy;
    01273         zoom_region_paint_cursor (zoom_region, clip_rect);
    01274         zoom_region_paint_crosswire_cursor (zoom_region, clip_rect);
    01275         if (GTK_IS_WIDGET (zoom_region->priv->w) &&
    01276             GDK_IS_WINDOW (zoom_region->priv->w->window))
    01277                 gdk_display_sync (gdk_drawable_get_display (
    01278                                           zoom_region->priv->w->window));
    01279 }
    01280
    01281 static gboolean
    01282 zoom_region_calculate_scroll_rects (ZoomRegion *zoom_region,
    01283                                     int dx, int dy,
    01284                                     GdkRectangle *scroll_rect,
    01285                                     GdkRectangle *expose_rect_h,
    01286                                     GdkRectangle *expose_rect_v)
    01287 {
    01288         GdkWindow *window = NULL;
    01289         GdkRectangle rect = {0, 0, 0, 0};
    01290         gboolean retval = TRUE;
    01291
    01292 #ifdef ZOOM_REGION_DEBUG
    01293         g_assert (zoom_region->alive);
    01294 #endif
    01295         rect.x = 0;
    01296         rect.y = 0;
    01297         if (zoom_region && zoom_region->priv->w &&
    01298             zoom_region->priv->w->window)
    01299                 window = zoom_region->priv->w->window;
    01300         else
    01301                 retval = FALSE;
    01302         if (!window)
    01303                 retval = FALSE;
    01304
    01305         if (window != NULL)
    01306           gdk_drawable_get_size (GDK_DRAWABLE (window),
    01307                                  &rect.width,
    01308                                  &rect.height);
    01309
    01310         if ((ABS (dx) >= rect.width) || (ABS (dy) >= rect.height)) {
    01311                 *scroll_rect = rect;
    01312                 DBG(fprintf (stderr, "deltas too big to scroll\n"));
    01313                 retval = FALSE;
    01314         }
    01315         else {
    01316             scroll_rect->x = MAX (0, dx);
    01317             scroll_rect->y = MAX (0, dy);
    01318             scroll_rect->width = MIN (rect.width + dx, rect.width - dx);
    01319             scroll_rect->height = MIN (rect.height + dy, rect.height - dy);
    01320         }
    01321
    01322         expose_rect_h->x = 0;
    01323         expose_rect_h->y = (scroll_rect->y == 0) ? scroll_rect->height : 0;
    01324         expose_rect_h->width = rect.width;
    01325         expose_rect_h->height = rect.height - scroll_rect->height;
    01326
    01327         expose_rect_v->x = (scroll_rect->x == 0) ? scroll_rect->width : 0;
    01328         expose_rect_v->y = scroll_rect->y;
    01329         expose_rect_v->width = rect.width - scroll_rect->width;
    01330         expose_rect_v->height = scroll_rect->height;
    01331
    01332         return retval;
    01333 }
    01334
    01335 static void
    01336 zoom_region_scroll_fast (ZoomRegion *zoom_region, int dx, int dy,
    01337                          GdkRectangle *scroll_rect,
    01338                          GdkRectangle *expose_rect_h,
    01339                          GdkRectangle *expose_rect_v)
    01340 {
    01341         GdkWindow *window;
    01342
    01343 #ifdef ZOOM_REGION_DEBUG
    01344         g_assert (zoom_region->alive);
    01345 #endif
    01346         if (zoom_region->priv->w && zoom_region->priv->w->window)
    01347                 window = zoom_region->priv->w->window;
    01348         else {
    01349                 processing_updates = FALSE;
    01350                 return;
    01351         }
    01352         zoom_region_unpaint_crosswire_cursor (zoom_region, scroll_rect);
    01353         zoom_region_unpaint_cursor (zoom_region, scroll_rect);
    01354         gdk_window_scroll (window, dx, dy);
    01355         zoom_region_paint_cursor (zoom_region, scroll_rect);
    01356         zoom_region_paint_crosswire_cursor (zoom_region, scroll_rect);
    01357         gdk_window_process_updates (window, FALSE);
    01358         /* sync reduces cursor flicker, but slows things down */
    01359         if (zoom_region->smooth_scroll_policy >
    01360             GNOME_Magnifier_ZoomRegion_SCROLL_FASTEST)
    01361                 gdk_display_sync (gdk_drawable_get_display (window));
    01362 }
    01363
    01364 static void
    01365 zoom_region_scroll_smooth (ZoomRegion *zoom_region, int dx, int dy,
    01366                            GdkRectangle *scroll_rect,
    01367                            GdkRectangle *expose_rect_h,
    01368                            GdkRectangle *expose_rect_v)
    01369 {
    01370         GdkWindow *window = NULL;
    01371         GdkRectangle window_rect;
    01372
    01373 #ifdef ZOOM_REGION_DEBUG
    01374         g_assert (zoom_region->alive);
    01375 #endif
    01376         if (zoom_region->priv->w && GDK_IS_DRAWABLE (zoom_region->priv->w->window))
    01377                 window = zoom_region->priv->w->window;
    01378         else
    01379                 return;
    01380         window_rect.x = 0;
    01381         window_rect.y = 0;
    01382         gdk_drawable_get_size (GDK_DRAWABLE (window),
    01383                                &window_rect.width, &window_rect.height);
    01384         gdk_window_begin_paint_rect (window, &window_rect);
    01385         gdk_window_invalidate_rect (window, &window_rect, FALSE);
    01386         gdk_window_process_updates (window, FALSE);
    01387         gdk_window_end_paint (window);
    01388 }
    01389
    01390 static void
    01391 zoom_region_scroll (ZoomRegion *zoom_region, int dx, int dy)
    01392 {
    01393         GdkRectangle scroll_rect, expose_rect_h, expose_rect_v;
    01394         gboolean can_scroll;
    01395
    01396 #ifdef ZOOM_REGION_DEBUG
    01397         g_assert (zoom_region->alive);
    01398 #endif
    01399         if (timing_test) {
    01400                 mag_timing.num_line_samples++;
    01401                 mag_timing.dx = abs(dx);
    01402                 mag_timing.dy = abs(dy);
    01403                 mag_timing.dx_total += mag_timing.dx;
    01404                 mag_timing.dy_total += mag_timing.dy;
    01405                 if (zoom_region->timing_output) {
    01406                         fprintf(stderr, "  Panning Increment (x)    = %d (avg. %f) lines/frame\n",
    01407                                 mag_timing.dx, (float)mag_timing.dx_total / (float)mag_timing.num_line_samples);
    01408                         fprintf(stderr, "  Panning Increment (y)    = %d (avg. %f) lines/frame\n",
    01409                                 mag_timing.dy, (float)mag_timing.dy_total / (float)mag_timing.num_line_samples);
    01410                 }
    01411         }
    01412
    01413     /*
    01414      * Currently processing a screen update.  This flag used to disallow
    01415      * other updates to occur until this one finishes
    01416      */
    01417     processing_updates = TRUE;
    01418
    01419         can_scroll = zoom_region_calculate_scroll_rects (zoom_region, dx, dy,
    01420                                                          &scroll_rect,
    01421                                                          &expose_rect_h,
    01422                                                          &expose_rect_v);
    01423
    01424         if (can_scroll) {
    01425                 zoom_region_update_pixmap (zoom_region, zoom_region_source_rect_from_view_rect (zoom_region, expose_rect_h), NULL);
    01426                 zoom_region_update_pixmap (zoom_region, zoom_region_source_rect_from_view_rect (zoom_region, expose_rect_v), NULL);
    01427
    01428                 if (zoom_region->smooth_scroll_policy > GNOME_Magnifier_ZoomRegion_SCROLL_FAST) {
    01429                         zoom_region_scroll_smooth (zoom_region, dx, dy,
    01430                                                    &scroll_rect,
    01431                                                    &expose_rect_h,
    01432                                                    &expose_rect_v);
    01433                 } else {
    01434                         zoom_region_scroll_fast (zoom_region, dx, dy,
    01435                                                  &scroll_rect,
    01436                                                  &expose_rect_h,
    01437                                                  &expose_rect_v);
    01438                 }
    01439         } else {
    01440                 zoom_region_queue_update (zoom_region, zoom_region_source_rect_from_view_rect (zoom_region, scroll_rect));
    01441         }
    01442 }
    01443
    01444 static void
    01445 zoom_region_recompute_exposed_bounds (ZoomRegion *zoom_region)
    01446 {
    01447         zoom_region->priv->exposed_bounds.x2 = zoom_region->priv->exposed_bounds.x1
    01448                 + (zoom_region->viewport.x2 - zoom_region->viewport.x1);
    01449         zoom_region->priv->exposed_bounds.y2 = zoom_region->priv->exposed_bounds.y1
    01450                 + (zoom_region->viewport.y2 - zoom_region->viewport.y1);
    01451 }
    01452
    01453 static void
    01454 zoom_region_set_cursor_pos (ZoomRegion *zoom_region, int x, int y)
    01455 {
    01456         if (zoom_region->priv)
    01457         {
    01458                 zoom_region->priv->last_cursor_pos.x = x;
    01459                 zoom_region->priv->last_cursor_pos.y = y;
    01460         }
    01461 }
    01462
    01463 static gboolean
    01464 zoom_region_update_pointer (ZoomRegion *zoom_region, gboolean draw_cursor)
    01465 {
    01466         Magnifier *magnifier;
    01467         gint mouse_x_return, mouse_y_return;
    01468         guint mask_return;
    01469
    01470 #ifdef ZOOM_REGION_DEBUG
    01471         g_assert (zoom_region->alive);
    01472 #endif
    01473         if (!zoom_region->priv || !zoom_region->priv->parent
    01474             || !zoom_region->poll_mouse)
    01475               return FALSE;
    01476
    01477         magnifier = zoom_region->priv->parent;
    01478
    01479         /* TODO: there's really no reason we should be using magnifier->priv->root here */
    01480         if (magnifier && magnifier->priv && magnifier_get_root (magnifier))
    01481         {
    01482                 gdk_window_get_pointer (
    01483                         magnifier_get_root (magnifier),
    01484                         &mouse_x_return,
    01485                         &mouse_y_return,
    01486                         &mask_return);
    01487
    01488                 if (zoom_region->priv->last_cursor_pos.x != mouse_x_return
    01489                     || zoom_region->priv->last_cursor_pos.y != mouse_y_return)
    01490                 {
    01491                         zoom_region_set_cursor_pos (zoom_region,
    01492                                                     mouse_x_return,
    01493                                                     mouse_y_return);
    01494                         if (draw_cursor)
    01495                                 zoom_region_update_cursor (zoom_region, 0, 0,
    01496                                                            NULL);
    01497
    01498                         return TRUE;
    01499                 }
    01500         }
    01501         return FALSE;
    01502 }
    01503
    01504 static int
    01505 zoom_region_update_pointer_idle (gpointer data)
    01506 {
    01507         ZoomRegion *zoom_region = (ZoomRegion *) data;
    01508
    01509         if (zoom_region_update_pointer (zoom_region, TRUE))
    01510                 return TRUE;
    01511         else {
    01512                 if (zoom_region->priv)
    01513                         zoom_region->priv->update_pointer_id =
    01514                             g_timeout_add_full (G_PRIORITY_DEFAULT,
    01515                                                 100,
    01516                                                 zoom_region_update_pointer_timeout,
    01517                                                 zoom_region,
    01518                                                 NULL);
    01519                 return FALSE;
    01520         }
    01521 }
    01522
    01523 static int
    01524 zoom_region_update_pointer_timeout (gpointer data)
    01525 {
    01526         ZoomRegion *zoom_region = data;
    01527
    01528         if (zoom_region->priv && zoom_region_update_pointer (zoom_region,
    01529                                                              TRUE)) {
    01530             zoom_region->priv->update_pointer_id =
    01531                 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
    01532                                  zoom_region_update_pointer_idle,
    01533                                  data,
    01534                                  NULL);
    01535                 return FALSE;
    01536         } else
    01537                 return TRUE;
    01538 }
    01539
    01540 static void
    01541 zoom_region_moveto (ZoomRegion *zoom_region,
    01542                     const long x, const long y)
    01543 {
    01544         long dx = x * zoom_region->xscale - zoom_region->priv->exposed_bounds.x1;
    01545         long dy = y * zoom_region->yscale - zoom_region->priv->exposed_bounds.y1;
    01546 #ifdef ZOOM_REGION_DEBUG
    01547         g_assert (zoom_region->alive);
    01548 #endif
    01549 /* fprintf (stderr, "moveto %ld %ld\n", x, y); */
    01550
    01551         mag_timing.dx = 0;
    01552         mag_timing.dy = 0;
    01553
    01554         if ((dx != 0) || (dy != 0)) {
    01555                 zoom_region_update_pointer (zoom_region, FALSE);
    01556                 zoom_region->priv->exposed_bounds.x1 = x * zoom_region->xscale;
    01557                 zoom_region->priv->exposed_bounds.y1 = y * zoom_region->yscale;
    01558                 zoom_region_recompute_exposed_bounds (zoom_region);
    01559                 zoom_region_scroll (zoom_region,
    01560                                     -dx, -dy);
    01561         }
    01562 }
    01563
    01564 /*
    01565  * Process that must be made in-line in the current pixbuf.
    01566  */
    01567 static void
    01568 zoom_region_process_pixbuf (ZoomRegion *zoom_region, GdkPixbuf *pixbuf)
    01569 {
    01570         int rowstride = gdk_pixbuf_get_rowstride (pixbuf);
    01571         int i, j, t;
    01572         int w = gdk_pixbuf_get_width (pixbuf);
    01573         int h = gdk_pixbuf_get_height (pixbuf);
    01574         int n_channels = gdk_pixbuf_get_n_channels (pixbuf);
    01575         guchar *pixels = gdk_pixbuf_get_pixels (pixbuf);
    01576         guchar *pixels_row;
    01577 #ifdef HAVE_COLORBLIND
    01578         COLORBLIND_RUNTIME *cbr;
    01579         COLORBLIND_XCOLOR *color;
    01580 #endif /* HAVE_COLORBLIND */
    01581
    01582         gboolean manipulate_contrast = FALSE;
    01583         gboolean manipulate_brightness = FALSE;
    01584         gboolean color_blind_filter = FALSE;
    01585
    01586         if (zoom_region->contrast_r != 0 || zoom_region->contrast_g != 0 ||
    01587             zoom_region->contrast_b != 0) {
    01588                 manipulate_contrast = TRUE;
    01589         }
    01590
    01591         if (zoom_region->bright_r != 0 || zoom_region->bright_g != 0 ||
    01592             zoom_region->bright_b != 0) {
    01593                 manipulate_brightness = TRUE;
    01594         }
    01595
    01596 #ifdef HAVE_COLORBLIND
    01597         if (zoom_region->color_blind_filter !=
    01598             GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_NO_FILTER) {
    01599                 color_blind_filter = TRUE;
    01600                 cbr = colorblind_create ();
    01601                 color = malloc (sizeof (COLORBLIND_XCOLOR));
    01602                 switch (zoom_region->color_blind_filter) {
    01603                 case GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_NO_FILTER:
    01604                         break; /* This entry is only to avoid a warning */
    01605                 case GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_SELECTIVE_SATURATE_RED:
    01606                         colorblind_set_filter_type (cbr, colorblind_filter_t_selective_saturate_red);
    01607                         break;
    01608                 case GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_SELECTIVE_SATURATE_GREEN:
    01609                         colorblind_set_filter_type (cbr, colorblind_filter_t_selective_saturate_green);
    01610                         break;
    01611                 case GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_SELECTIVE_SATURATE_BLUE:
    01612                         colorblind_set_filter_type (cbr, colorblind_filter_t_selective_saturate_blue);
    01613                         break;
    01614                 case GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_SELECTIVE_DESSATURATE_RED:
    01615                         colorblind_set_filter_type (cbr, colorblind_filter_t_selective_dessaturate_red);
    01616                         break;
    01617                 case GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_SELECTIVE_DESSATURATE_GREEN:
    01618                         colorblind_set_filter_type (cbr, colorblind_filter_t_selective_dessaturate_green);
    01619                         break;
    01620                 case GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_SELECTIVE_DESSATURATE_BLUE:
    01621                         colorblind_set_filter_type (cbr, colorblind_filter_t_selective_dessaturate_blue);
    01622                         break;
    01623                 case GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_HUE_SHIFT_POSITIVE:
    01624                         colorblind_set_filter_type (cbr, colorblind_filter_t_hue_shift_positive);
    01625                         break;
    01626                 case GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_HUE_SHIFT_NEGATIVE:
    01627                         colorblind_set_filter_type (cbr, colorblind_filter_t_hue_shift_negative);
    01628                         break;
    01629                 case GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_SELECTIVE_SATURATE:
    01630                         colorblind_set_filter_type (cbr, colorblind_filter_t_selective_saturate);
    01631                         break;
    01632                 case GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_SELECTIVE_DESSATURATE:
    01633                         colorblind_set_filter_type (cbr, colorblind_filter_t_selective_dessaturate);
    01634                         break;
    01635                 case GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_MONOCHRONE_OTHERS:
    01636                         colorblind_set_filter_type (cbr, colorblind_filter_t_monochrome_others);
    01637                         break;
    01638                 }
    01639         }
    01640 #endif /* HAVE_COLORBLIND */
    01641
    01642         if (!manipulate_contrast && !zoom_region->invert &&
    01643             !manipulate_brightness && !color_blind_filter)
    01644                 return;
    01645
    01646 #define CLAMP_UCHAR(v) (t = (v), CLAMP (t, 0, 255))
    01647 #define CLAMP_LOW_MID(v) (t = (v), CLAMP (t, 0, 127))
    01648 #define CLAMP_MID_HIGH(v) (t = (v), CLAMP (t, 127, 255))
    01649
    01650         for (j = 0; j < h; ++j) {
    01651                 pixels_row = pixels;
    01652                 for (i = 0; i < w; ++i) {
    01653                         if (manipulate_contrast) {
    01654                                 /* Set the RED contrast */
    01655                                 if (pixels_row[0] <= 127)
    01656                                         pixels_row[0] = CLAMP_LOW_MID (pixels_row[0] - zoom_region->contrast_r * 127);
    01657                                 else
    01658                                         pixels_row[0] = CLAMP_MID_HIGH (pixels_row[0] + zoom_region->contrast_r * 127);
    01659
    01660                                 /* Set the GREEN contrast */
    01661                                 if (pixels_row[1] <= 127)
    01662                                         pixels_row[1] = CLAMP_LOW_MID (pixels_row[1] - zoom_region->contrast_g * 127);
    01663                                 else
    01664                                         pixels_row[1] = CLAMP_MID_HIGH (pixels_row[1] + zoom_region->contrast_g * 127);
    01665
    01666                                 /* Set the BLUE contrast */
    01667                                 if (pixels_row[2] <= 127)
    01668                                         pixels_row[2] = CLAMP_LOW_MID (pixels_row[2] - zoom_region->contrast_b * 127);
    01669                                 else
    01670                                         pixels_row[2] = CLAMP_MID_HIGH (pixels_row[2] + zoom_region->contrast_b * 127);
    01671                         }
    01672
    01673                         if (manipulate_brightness) {
    01674                                 /* Set the RED brightness */
    01675                                 pixels_row[0] = CLAMP_UCHAR (pixels_row[0] + zoom_region->bright_r * 255);
    01676
    01677                                 /* Set the GREEN brightness */
    01678                                 pixels_row[1] = CLAMP_UCHAR (pixels_row[1] + zoom_region->bright_g * 255);
    01679
    01680                                 /* Set the BLUE brightness */
    01681                                 pixels_row[2] = CLAMP_UCHAR (pixels_row[2] + zoom_region->bright_b * 255);
    01682                         }
    01683
    01684                         if (zoom_region->invert) {
    01685                                 pixels_row[0] = ~(pixels_row[0]);
    01686                                 pixels_row[1] = ~(pixels_row[1]);
    01687                                 pixels_row[2] = ~(pixels_row[2]);
    01688                         }
    01689
    01690 #ifdef HAVE_COLORBLIND
    01691                         if (color_blind_filter) {
    01692                                 color->red   = pixels_row[0];
    01693                                 color->green = pixels_row[1];
    01694                                 color->blue  = pixels_row[2];
    01695                                 if (colorblind_filter (cbr, color)) {
    01696                                         pixels_row[0] = color->red;
    01697                                         pixels_row[1] = color->green;
    01698                                         pixels_row[2] = color->blue;
    01699                                 }
    01700                         }
    01701 #endif /* HAVE_COLORBLIND */
    01702
    01703                         pixels_row += n_channels;
    01704                 }
    01705                 pixels += rowstride;
    01706         }
    01707 }
    01708
    01709 static void
    01710 zoom_region_post_process_pixbuf (ZoomRegion *zoom_region,
    01711                                  GdkPixbuf *subimage,
    01712                                  GdkPixbuf *scaled_image)
    01713 {
    01714         /* nothing yet */
    01724 }
    01725
    01726 static GdkPixbuf *
    01727 zoom_region_get_source_subwindow (ZoomRegion *zoom_region,
    01728                                   const GdkRectangle bounds)
    01729 {
    01730         int i, j, width, height;
    01731         Magnifier *magnifier = zoom_region->priv->parent;
    01732         GdkPixbuf *subimage = NULL;
    01733
    01734 #ifdef ZOOM_REGION_DEBUG
    01735         g_assert (zoom_region->alive);
    01736 #endif
    01737         width = gdk_screen_get_width (
    01738                 gdk_display_get_screen (magnifier->source_display,
    01739                                         magnifier->source_screen_num));
    01740         height = gdk_screen_get_height (
    01741                 gdk_display_get_screen (magnifier->source_display,
    01742                                         magnifier->source_screen_num));
    01743
    01744         if ((bounds.width <= 0) || (bounds.height <= 0))
    01745         {
    01746                 return NULL;
    01747         }
    01748
    01749         if (!zoom_region->priv->source_drawable)
    01750         {
    01751                 /* TESTING ONLY */
    01752                 if (zoom_region->priv->test) {
    01753                         GdkImage *test_image = NULL;
    01754
    01755                         test_image = gdk_image_new (GDK_IMAGE_FASTEST,
    01756                                                     gdk_visual_get_system (),
    01757                                                     width,
    01758                                                     height);
    01759
    01760                         for (i = 0; i < width; ++i)
    01761                                 for (j = 0; j < height; ++j)
    01762                                         gdk_image_put_pixel (test_image, i, j, i*j);
    01763
    01764                         zoom_region->priv->source_drawable = gdk_pixmap_new (zoom_region->priv->w->window, width, height, -1);
    01765
    01766                         if (zoom_region->priv->default_gc == NULL)
    01767                                 zoom_region->priv->default_gc = gdk_gc_new(zoom_region->priv->w->window);
    01768
    01769                         gdk_draw_image (zoom_region->priv->source_drawable,
    01770                                         zoom_region->priv->default_gc,
    01771                                         test_image,
    01772                                         0, 0,
    01773                                         0, 0,
    01774                                         width, height);
    01775                 }
    01776                 else
    01777                 {
    01778                         if (magnifier->priv->source_drawable) {
    01779                                 zoom_region->priv->source_drawable =
    01780                                         magnifier->priv->source_drawable;
    01781                         } else
    01782                                 zoom_region->priv->source_drawable = gdk_screen_get_root_window (gdk_display_get_screen (magnifier->source_display, magnifier->source_screen_num));
    01783                 }
    01784                 if (zoom_region->cache_source)
    01785                 {
    01786                         zoom_region->priv->source_pixbuf_cache =
    01787                                 gdk_pixbuf_new (GDK_COLORSPACE_RGB,
    01788                                                 FALSE,
    01789                                                 8, /* FIXME: not always 8? */
    01790                                                 width, height);
    01791                 }
    01792         }
    01793         DEBUG_RECT ("getting subimage from ", bounds);
    01794
    01795         subimage = gdk_pixbuf_get_from_drawable (NULL, zoom_region->priv->source_drawable,
    01796                                                  gdk_colormap_get_system (),
    01797                                                  bounds.x,
    01798                                                  bounds.y,
    01799                                                  0,
    01800                                                  0,
    01801                                                  bounds.width,
    01802                                                  bounds.height);
    01803
    01804         /* TODO: blank the region overlapped by the target display if source == target */
    01805
    01806         if (!subimage)
    01807                 _debug_announce_rect ("update of invalid subregion!\n", bounds);
    01808
    01809         /* if this zoom-region keeps a cache, do a diff to see if update is necessary */
    01810         if (zoom_region->cache_source && subimage) {
    01811                 GdkPixbuf *cache_subpixbuf =
    01812                         gdk_pixbuf_new_subpixbuf (zoom_region->priv->source_pixbuf_cache,
    01813                                                   bounds.x, bounds.y, bounds.width, bounds.height);
    01814                 if (_diff_pixbufs (subimage, cache_subpixbuf)) {
    01815                         gdk_pixbuf_copy_area (subimage, 0, 0, bounds.width, bounds.height,
    01816                                               zoom_region->priv->source_pixbuf_cache,
    01817                                               bounds.x, bounds.y);
    01818                 }
    01819                 else
    01820                 {
    01821                         if (subimage)
    01822                                 g_object_unref (subimage);
    01823                         subimage = NULL;
    01824                 }
    01825                 g_object_unref (cache_subpixbuf);
    01826         }
    01827         return subimage;
    01828 }
    01829
    01830 static GdkRectangle
    01831 zoom_region_update_pixmap (ZoomRegion *zoom_region,
    01832                            const GdkRectangle update_rect,
    01833                            GdkRectangle *p_rect)
    01834 {
    01835         GdkPixbuf *subimage;
    01836         GdkRectangle source_rect;
    01837
    01838 #ifdef ZOOM_REGION_DEBUG
    01839         g_assert (zoom_region->alive);
    01840 #endif
    01841         DEBUG_RECT ("unclipped update rect", update_rect);
    01842         source_rect = zoom_region_clip_to_source (zoom_region, update_rect);
    01843         DEBUG_RECT ("clipped to source", source_rect);
    01844         source_rect = zoom_region_clip_to_exposed_target (zoom_region, source_rect);
    01845         DEBUG_RECT ("update rect clipped to exposed target", source_rect);
    01846
    01847         subimage = zoom_region_get_source_subwindow (zoom_region, source_rect);
    01848
    01849         if (subimage)
    01850         {
    01851                 GdkRectangle paint_rect;
    01852                 g_timer_start (mag_timing.scale);
    01853                 DEBUG_RECT ("source rect", source_rect);
    01854                 paint_rect = zoom_region_view_rect_from_source_rect (zoom_region, source_rect);
    01855                 if (p_rect) {
    01856                         *p_rect = paint_rect;
    01857                 }
    01858                 /* paint_rect = zoom_region_clip_to_scaled_pixmap (zoom_region, paint_rect); */
    01859                 DEBUG_RECT ("paint rect", paint_rect);
    01860
    01861                 zoom_region_process_pixbuf (zoom_region, subimage);
    01862
    01867                 gdk_pixbuf_scale (subimage,
    01868                                   zoom_region->priv->scaled_pixbuf,
    01869                                   0,
    01870                                   0,
    01871                                   paint_rect.width,
    01872                                   paint_rect.height,
    01873                                   0,
    01874                                   0,
    01875                                   zoom_region->xscale,
    01876                                   zoom_region->yscale,
    01877                                   zoom_region->priv->gdk_interp_type);
    01878
    01879                 zoom_region_post_process_pixbuf (zoom_region, subimage,
    01880                                                  zoom_region->priv->scaled_pixbuf);
    01881                 if (zoom_region->priv->default_gc == NULL)
    01882                         zoom_region->priv->default_gc = gdk_gc_new(zoom_region->priv->w->window);
    01883
    01884 #ifndef USE_GDK_PIXBUF_RENDER_TO_DRAWABLE
    01885                 if (GDK_IS_DRAWABLE (zoom_region->priv->pixmap))
    01886                     gdk_draw_pixbuf (zoom_region->priv->pixmap,
    01887                                      zoom_region->priv->default_gc,
    01888                                      zoom_region->priv->scaled_pixbuf,
    01889                                      0,
    01890                                      0,
    01891                                      paint_rect.x + zoom_region->priv->exposed_bounds.x1 - zoom_region->priv->source_area.x1 * zoom_region->xscale,
    01892                                      paint_rect.y + zoom_region->priv->exposed_bounds.y1 - zoom_region->priv->source_area.y1 * zoom_region->yscale,
    01893                                      paint_rect.width,
    01894                                      paint_rect.height,
    01895                                      GDK_RGB_DITHER_NONE,
    01896                                      0,
    01897                                      0);
    01898                 else
    01899                     g_warning ("updating non-drawable pixmap: region %p", zoom_region);
    01900 #else
    01901                 gdk_pixbuf_render_to_drawable (zoom_region->priv->scaled_pixbuf,
    01902                                                zoom_region->priv->pixmap,
    01903                                                zoom_region->priv->default_gc,
    01904                                                0,
    01905                                                0,
    01906                                                paint_rect.x + zoom_region->priv->exposed_bounds.x1,
    01907                                                paint_rect.y + zoom_region->priv->exposed_bounds.y1,
    01908                                                paint_rect.width,
    01909                                                paint_rect.height,
    01910                                                GDK_RGB_DITHER_NONE,
    01911                                                0,
    01912                                                0);
    01913 #endif
    01914                 if (gmag_gs_error_check ())
    01915                         g_warning ("Could not render scaled image to drawable; out of memory!\n");
    01916                 g_object_unref (subimage);
    01917
    01918                 g_timer_stop (mag_timing.scale);
    01919         }
    01920         return source_rect;
    01921 }
    01922
    01929 static void
    01930 zoom_region_update (ZoomRegion *zoom_region,
    01931                     const GdkRectangle update_rect)
    01932 {
    01933         GdkRectangle paint_rect = {0, 0, 0, 0};
    01934         if (zoom_region->priv->w && zoom_region->priv->w->window) {
    01935                 GdkRectangle source_rect = zoom_region_update_pixmap (zoom_region, update_rect, &paint_rect);
    01936                 if (paint_rect.x != 0 || paint_rect.y != 0 ||
    01937                     paint_rect.width != 0 || paint_rect.height != 0) {
    01938                         gdk_window_begin_paint_rect (
    01939                                 zoom_region->priv->w->window, &paint_rect);
    01940                         zoom_region_paint (zoom_region, &paint_rect);
    01941                         gdk_window_end_paint (zoom_region->priv->w->window);
    01942                 }
    01943                 if (timing_test) {
    01944                         mag_timing.num_scale_samples++;
    01945
    01946                         gulong microseconds;
    01947
    01948                         mag_timing.scale_val =
    01949                                 g_timer_elapsed (mag_timing.scale,
    01950                                                  &microseconds);
    01951                         mag_timing.scale_total += mag_timing.scale_val;
    01952
    01953                         if (mag_timing.scale_val != 0 && (timing_scale_max == 0 ||
    01954                            (1.0/(float)mag_timing.scale_val) > (1.0/(float)timing_scale_max)))
    01955                                 timing_scale_max = mag_timing.scale_val;
    01956                         if ((source_rect.height * source_rect.width / mag_timing.scale_val) > update_nrr_max)
    01957                                 update_nrr_max = source_rect.height * source_rect.width / mag_timing.scale_val;
    01958
    01959                         mag_timing.update_pixels_total += source_rect.height * source_rect.width;
    01960
    01961                         if (zoom_region->timing_output) {
    01962                                 fprintf(stderr, "  Update Duration          = %f (avg. %f) (max. %f) (tot. %f) seconds\n",
    01963                                         mag_timing.scale_val, (mag_timing.scale_total /
    01964                                         mag_timing.num_scale_samples), timing_scale_max, mag_timing.scale_total);
    01965                                 fprintf(stderr, "    Update Pixels          = %ld (avg. %ld) pixels/frame\n",
    01966                                         (long) source_rect.height * source_rect.width,
    01967                                         mag_timing.update_pixels_total / mag_timing.num_scale_samples);
    01968                                 fprintf(stderr, "    Update Rate            = (avg. %f) (max. %f) updates/second\n",
    01969                                         1.0/(mag_timing.scale_total / mag_timing.num_scale_samples), 1.0/(float)timing_scale_max);
    01970                                 fprintf(stderr, "    Net Update Rate        = (avg. %f) (max. %f) Mpex/second\n",
    01971                                         ((float)mag_timing.update_pixels_total / (float)mag_timing.scale_total) / 1000000.0,
    01972                                         update_nrr_max / 1000000.0);
    01973                         }
    01974                 }
    01975         } else {
    01976                 fprintf (stderr, "update on uninitialized zoom region!\n");
    01977         }
    01978 }
    01979
    01980 static void
    01981 zoom_region_init_window (ZoomRegion *zoom_region)
    01982 {
    01983         GtkFixed *parent;
    01984         GtkWidget *zoomer, *border;
    01985         DBG(fprintf (stderr, "window not yet created...\n"));
    01986         parent = GTK_FIXED (
    01987                 ((Magnifier *)zoom_region->priv->parent)->priv->canvas);
    01988         zoomer = gtk_drawing_area_new ();
    01989         border = gtk_drawing_area_new ();
    01990         zoom_region->priv->border = border;
    01991         zoom_region->priv->w = zoomer;
    01992
    01993 #ifdef ZOOM_REGION_DEBUG
    01994         g_assert (zoom_region->alive);
    01995 #endif
    01996         gtk_widget_set_size_request (GTK_WIDGET (border),
    01997                                      zoom_region->viewport.x2 -
    01998                                      zoom_region->viewport.x1,
    01999                                      zoom_region->viewport.y2 -
    02000                                      zoom_region->viewport.y1);
    02001         gtk_widget_set_size_request (GTK_WIDGET (zoomer),
    02002                                      zoom_region->viewport.x2 -
    02003                                      zoom_region->viewport.x1 -
    02004                                      (zoom_region->border_size_right +
    02005                                       zoom_region->border_size_left),
    02006                                      zoom_region->viewport.y2 -
    02007                                      zoom_region->viewport.y1 -
    02008                                      (zoom_region->border_size_bottom +
    02009                                       zoom_region->border_size_top));
    02010         gtk_fixed_put (parent, border,
    02011                        zoom_region->viewport.x1,
    02012                        zoom_region->viewport.y1);
    02013         gtk_fixed_put (parent, zoomer,
    02014                        zoom_region->viewport.x1 +
    02015                        zoom_region->border_size_left,
    02016                        zoom_region->viewport.y1 +
    02017                        zoom_region->border_size_top);
    02018         gtk_widget_show (GTK_WIDGET (border));
    02019         gtk_widget_show (GTK_WIDGET (zoomer));
    02020         gtk_widget_show (GTK_WIDGET (parent));
    02021         zoom_region->priv->expose_handler_id =
    02022                 g_signal_connect (G_OBJECT (zoom_region->priv->w),
    02023                                   "expose_event",
    02024                                   G_CALLBACK (zoom_region_expose_handler),
    02025                                   zoom_region);
    02026         DBG(fprintf (stderr, "New window created\n"));
    02027 }
    02028
    02029 static int
    02030 zoom_region_process_updates (gpointer data)
    02031 {
    02032         ZoomRegion *zoom_region = (ZoomRegion *) data;
    02033
    02034         /* TODO: lock the queue when copying it? */
    02035         zoom_region_coalesce_updates (zoom_region);
    02036
    02037         if (zoom_region->priv->q != NULL) {
    02038                 GList *last = g_list_last (zoom_region->priv->q);
    02039 #ifdef ZOOM_REGION_DEBUG
    02040                 fprintf (stderr, "qlen=%d\n", g_list_length (zoom_region->priv->q));
    02041 #endif
    02042                 if (last) {
    02043                         zoom_region->priv->q = g_list_remove_link (zoom_region->priv->q,
    02044                                                                    last);
    02045                         zoom_region_update (zoom_region,
    02046                                             * (GdkRectangle *) last->data);
    02047                         g_list_free (last);
    02048 #ifdef DEBUG
    02049                         fputs (".\n", stderr); /* debug output, means we actually did something. */
    02050 #endif
    02051                 }
    02052                 return TRUE;
    02053         }
    02054         else
    02055         {
    02056                 if (zoom_region->priv)
    02057                         zoom_region->priv->update_handler_id = 0;
    02058                 return FALSE;
    02059         }
    02060 }
    02061
    02062 void
    02063 timing_report(ZoomRegion *zoom_region)
    02064 {
    02065         float frame_avg;
    02066         float x_scroll_incr, y_scroll_incr;
    02067         int width, height, x, y;
    02068
    02069         if (timing_test) {
    02070                 width = (zoom_region->viewport.x2 -
    02071                         zoom_region->viewport.x1) / zoom_region->xscale;
    02072                 height = (zoom_region->viewport.y2 -
    02073                         zoom_region->viewport.y1) / zoom_region->yscale;
    02074
    02075                 frame_avg = mag_timing.frame_total / mag_timing.num_frame_samples;
    02076
    02077                 x_scroll_incr = (float)mag_timing.dx_total / (float)mag_timing.num_line_samples;
    02078                 y_scroll_incr = (float)mag_timing.dy_total / (float)mag_timing.num_line_samples;
    02079
    02080                 gdk_drawable_get_size (GDK_DRAWABLE (zoom_region->priv->w->window),
    02081                         &x, &y);
    02082
    02083                 fprintf(stderr, "  Frames Processed         = %ld\n",
    02084                         mag_timing.num_frame_samples + 1);
    02085                 fprintf(stderr, "  Width/Height/Depth       = %d/%d/%d\n", x, y,
    02086                         gdk_drawable_get_depth (zoom_region->priv->w->window));
    02087                 fprintf(stderr, "  Zoom Factor (x/y)        = %f/%f\n", zoom_region->xscale,
    02088                         zoom_region->yscale);
    02089                 if (mag_timing.num_scale_samples != 0) {
    02090                         fprintf(stderr, "  Update Duration          = (avg. %f) (max. %f) (tot. %f) seconds\n",
    02091                                 (mag_timing.scale_total / mag_timing.num_scale_samples), timing_scale_max, mag_timing.scale_total);
    02092                         fprintf(stderr, "    Update Pixels          = (avg. %ld) pixels/frame\n",
    02093                                 mag_timing.update_pixels_total / mag_timing.num_scale_samples);
    02094                         fprintf(stderr, "    Update Rate            = (avg. %f) (max. %f) updates/second\n",
    02095                                 1.0/((float)mag_timing.scale_total / (float)mag_timing.num_scale_samples),
    02096                                 1.0/(float)timing_scale_max);
    02097                         fprintf(stderr, "    Net Update Rate        = (avg. %f) (max. %f) Mpex/second\n",
    02098                                 ((float)mag_timing.update_pixels_total / (float)mag_timing.scale_total) / 1000000.0,
    02099                                 update_nrr_max / 1000000.0);
    02100                 }
    02101                 fprintf(stderr, "  Pan Latency              = (avg. %f) (max. %f) seconds\n",
    02102                         (mag_timing.idle_total / mag_timing.num_idle_samples), timing_idle_max);
    02103                 fprintf(stderr, "  Total Frame Duration     = (avg. %f) (max. %f) (tot. %f) seconds\n",
    02104                         frame_avg, timing_frame_max, mag_timing.frame_total);
    02105                 fprintf(stderr, "  Frame Rate               = (avg. %f) (max. %f) frames/second\n",
    02106                         1.0 / (mag_timing.frame_total / mag_timing.num_frame_samples), cps_max);
    02107                 fprintf(stderr, "  Scroll Delta (x)         = (avg. %f) (tot. %d) lines\n",
    02108                         x_scroll_incr, mag_timing.dx_total);
    02109                 fprintf(stderr, "  Scroll Delta (y)         = (avg. %f) (tot. %d) lines\n",
    02110                         y_scroll_incr, mag_timing.dy_total);
    02111                 fprintf(stderr, "  Scroll Rate (x)          = (avg. %f) lines/second\n",
    02112                         x_scroll_incr / frame_avg);
    02113                 fprintf(stderr, "  Scroll Rate (y)          = (avg. %f) lines/second\n",
    02114                         y_scroll_incr / frame_avg);
    02115
    02116                 fprintf(stderr, "  Net Render Rate          = (avg. %f) (max. %f) Mpex/second\n\n",
    02117                         (height * width *
    02118                         ((float)mag_timing.num_frame_samples / (float)mag_timing.frame_total)) / 1000000.0,
    02119                         nrr_max / 1000000.0);
    02120         }
    02121 }
    02122
    02123 static void
    02124 zoom_region_time_frame(ZoomRegion *zoom_region, Magnifier *magnifier)
    02125 {
    02126         float frame_avg;
    02127         float x_scroll_incr, y_scroll_incr;
    02128         int width = magnifier->target_bounds.x2 - magnifier->target_bounds.x1;
    02129         int height = magnifier->target_bounds.y2 - magnifier->target_bounds.y1;
    02130
    02131         mag_timing.num_frame_samples++;
    02132         g_timer_stop (mag_timing.frame);
    02133
    02134         gulong microseconds;
    02135
    02136         mag_timing.frame_val = g_timer_elapsed (mag_timing.frame,
    02137                                                 &microseconds);
    02138
    02139         mag_timing.frame_total += mag_timing.frame_val;
    02140         if (mag_timing.frame_val > timing_frame_max)
    02141                 timing_frame_max = mag_timing.frame_val;
    02142         if (mag_timing.frame_val != 0 && 1.0/mag_timing.frame_val > cps_max)
    02143                 cps_max = 1.0/mag_timing.frame_val;
    02144
    02145         frame_avg = mag_timing.frame_total / mag_timing.num_frame_samples;
    02146
    02147         x_scroll_incr = (float)mag_timing.dx_total / (float)mag_timing.num_line_samples;
    02148         y_scroll_incr = (float)mag_timing.dy_total / (float)mag_timing.num_line_samples;
    02149
    02150         if ((height * width / mag_timing.frame_val) > nrr_max)
    02151                 nrr_max = height * width / mag_timing.frame_val;
    02152
    02153         if (zoom_region->timing_output) {
    02154                 fprintf(stderr, "  Total Frame Duration     = %f (avg. %f) (max. %f) (tot. %f) seconds\n",
    02155                         mag_timing.frame_val, frame_avg, timing_frame_max, mag_timing.frame_total);
    02156                 fprintf(stderr, "  Frame Rate               = (avg. %f) (max. %f) frames/second\n",
    02157                         1.0 /frame_avg, cps_max);
    02158                 fprintf(stderr, "  Scroll Delta (x)         = (avg. %f) (tot. %d) lines\n",
    02159                         x_scroll_incr, mag_timing.dx_total);
    02160                 fprintf(stderr, "  Scroll Delta (y)         = (avg. %f) (tot. %d) lines\n",
    02161                         y_scroll_incr, mag_timing.dy_total);
    02162                 fprintf(stderr, "  Scroll Rate (x)          = (avg. %f) lines/second\n",
    02163                         x_scroll_incr / frame_avg);
    02164                 fprintf(stderr, "  Scroll Rate (y)          = (avg. %f) lines/second\n",
    02165                         y_scroll_incr / frame_avg);
    02166
    02167                 fprintf(stderr, "  Net Render Rate          = (avg. %f) (max. %f) Mpex/second\n",
    02168                         (height * width *
    02169                         ((float)mag_timing.num_frame_samples / (float)mag_timing.frame_total)) / 1000000.0,
    02170                         nrr_max / 1000000.0);
    02171         }
    02172
    02173         mag_timing.last_frame_val = mag_timing.frame_val;
    02174         mag_timing.last_dy        = mag_timing.dy;
    02175
    02176         if (reset_timing) {
    02177                 fprintf(stderr, "\n### Updates summary:\n\n");
    02178                 timing_report (zoom_region);
    02179                 fprintf(stderr, "\n### Updates finished, starting panning test\n");
    02180                 reset_timing_stats();
    02181                 reset_timing = FALSE;
    02182         }
    02183 }
    02184
    02185 static void
    02186 zoom_region_sync (ZoomRegion *zoom_region)
    02187 {
    02188         while (zoom_region->priv->q)
    02189                 zoom_region_process_updates (zoom_region);
    02190 }
    02191
    02192 static gboolean
    02193 gdk_timing_idle (gpointer data)
    02194 {
    02195         ZoomRegion *zoom_region = data;
    02196
    02197         /* Now update has finished, reset processing_updates */
    02198         processing_updates = FALSE;
    02199         g_timer_stop (mag_timing.idle);
    02200
    02201         if (timing_test) {
    02202                 mag_timing.num_idle_samples++;
    02203
    02204                 gulong microseconds;
    02205
    02206                 mag_timing.idle_val = g_timer_elapsed (mag_timing.idle,
    02207                                                        &microseconds);
    02208                 mag_timing.idle_total += mag_timing.idle_val;
    02209
    02210                 if (mag_timing.idle_val > timing_idle_max)
    02211                         timing_idle_max = mag_timing.idle_val;
    02212
    02213                 if (zoom_region->timing_output) {
    02214                         fprintf(stderr, "  Pan Latency              = %f (avg. %f) (max. %f) seconds\n",
    02215                                 mag_timing.idle_val, (mag_timing.idle_total /
    02216                                 mag_timing.num_idle_samples), timing_idle_max);
    02217                 }
    02218         }
    02219
    02220         return FALSE;
    02221 }
    02222
    02223 static void
    02224 zoom_region_get_move_x_y (ZoomRegion *zoom_region, long *x, long *y)
    02225 {
    02226         long width, height;
    02227
    02228         width = (zoom_region->viewport.x2 - zoom_region->viewport.x1) /
    02229                 zoom_region->xscale;
    02230         height = (zoom_region->viewport.y2 - zoom_region->viewport.y1) /
    02231                 zoom_region->yscale;
    02232
    02233         switch (zoom_region->x_align_policy) {
    02234         case GNOME_Magnifier_ZoomRegion_ALIGN_MAX:
    02235                 *x = zoom_region->roi.x2 - width;
    02236                 break;
    02237         case GNOME_Magnifier_ZoomRegion_ALIGN_MIN:
    02238                 *x = zoom_region->roi.x1;
    02239                 break;
    02240         case GNOME_Magnifier_ZoomRegion_ALIGN_CENTER:
    02241         default:
    02242                 *x = ((zoom_region->roi.x1 + zoom_region->roi.x2) - width ) /
    02243                         2;
    02244         }
    02245
    02246         switch (zoom_region->y_align_policy) {
    02247         case GNOME_Magnifier_ZoomRegion_ALIGN_MAX:
    02248                 *y = zoom_region->roi.y2 - height;
    02249                 break;
    02250         case GNOME_Magnifier_ZoomRegion_ALIGN_MIN:
    02251                 *y = zoom_region->roi.y1;
    02252                 break;
    02253         case GNOME_Magnifier_ZoomRegion_ALIGN_CENTER:
    02254         default:
    02255                 *y = ((zoom_region->roi.y1 + zoom_region->roi.y2) - height ) /
    02256                         2;
    02257         }
    02258 }
    02259
    02260 static void
    02261 zoom_region_align (ZoomRegion *zoom_region)
    02262 {
    02263         Magnifier *magnifier = zoom_region->priv->parent;
    02264         long x = 0, y = 0;
    02265
    02266         if (timing_start)
    02267                 zoom_region_time_frame(zoom_region, magnifier);
    02268
    02269         if (timing_test) {
    02270                 g_timer_start (mag_timing.frame);
    02271
    02272                 if (zoom_region->timing_output) {
    02273                         gint x, y;
    02274
    02275                         gdk_drawable_get_size (GDK_DRAWABLE (zoom_region->priv->w->window),
    02276                                 &x, &y);
    02277
    02278                         fprintf(stderr, "\nTiming Information - ROI   = (%d, %d) (%d, %d):\n",
    02279                                 zoom_region->roi.x1, zoom_region->roi.y1, zoom_region->roi.x2,
    02280                                 zoom_region->roi.y2);
    02281                         fprintf(stderr, "  Frame Number             = %ld\n",
    02282                                 mag_timing.num_frame_samples + 1);
    02283                         fprintf(stderr, "  Width/Height/Depth       = %d/%d/%d\n", x, y,
    02284                                 gdk_drawable_get_depth (zoom_region->priv->w->window));
    02285                 }
    02286
    02287                 /*
    02288                  * The timing_start flag makes sure that we don't start displaying output
    02289                  * until we have processed an entire frame.
    02290                  */
    02291                 if (!timing_start)
    02292                         g_timer_start (mag_timing.process);
    02293
    02294                 timing_start = TRUE;
    02295         }
    02296
    02297         g_timer_start (mag_timing.idle);
    02298
    02299         /*
    02300          * zoom_region_align calls
    02301          *   zoom_region_moveto calls
    02302          *     zoom_region_scroll calls
    02303          *        zoom_region_scroll_fast or zoom_region_scroll_smooth calls
    02304          *           gdk_window_scroll or gdk_window_invalidate_rect calls
    02305          *              gdk_window_invalidate_region calls
    02306          *                 gdk_window_invalidate_maybe_recurse
    02307          *
    02308          * The last function in the stack will set up an idle handler of
    02309          * priority GDK_PRIORITY_REDRAW (gdk_window_update_idle) to be called
    02310          * to handle the work of updateing the screen.
    02311          *
    02312          * By setting up an idle handler of priority GDK_PRIORITY_REDRAW + 1,
    02313          * it will be called immediately after and we can determine when GTK+
    02314          * is finished with the update.
    02315          */
    02316         g_idle_add_full (GDK_PRIORITY_REDRAW + 1,
    02317                 gdk_timing_idle, zoom_region, NULL);
    02318
    02319         zoom_region_get_move_x_y (zoom_region, &x, &y);
    02320
    02321         zoom_region_moveto (zoom_region, x, y);
    02322 }
    02323
    02324 static void
    02325 zoom_region_set_viewport (ZoomRegion *zoom_region,
    02326                           const GNOME_Magnifier_RectBounds *viewport)
    02327 {
    02328 #ifdef ZOOM_REGION_DEBUG
    02329         g_assert (zoom_region->alive);
    02330 #endif
    02331         if (zoom_region->viewport.x1 == viewport->x1 &&
    02332             zoom_region->viewport.y1 == viewport->y1 &&
    02333             zoom_region->viewport.x2 == viewport->x2 &&
    02334             zoom_region->viewport.y2 == viewport->y2) {
    02335                 return;
    02336         }
    02337         zoom_region->viewport = *viewport;
    02338 #ifdef DEBUG
    02339         fprintf (stderr, "Setting viewport %d,%d - %d,%d\n",
    02340                  (int) viewport->x1, (int) viewport->y1,
    02341                  (int) viewport->x2, (int) viewport->y2);
    02342 #endif
    02343         zoom_region_align (zoom_region);
    02344         if (!zoom_region->priv->w) {
    02345                 zoom_region_init_window (zoom_region);
    02346         } else {
    02347                 CORBA_any *any;
    02348                 CORBA_Environment ev;
    02349                 Bonobo_PropertyBag properties;
    02350                 Magnifier *magnifier = (Magnifier *) zoom_region->priv->parent;
    02351                 GtkFixed *fixed = GTK_FIXED (magnifier->priv->canvas);
    02352                 gtk_fixed_move (fixed,
    02353                                 zoom_region->priv->border,
    02354                                 zoom_region->viewport.x1,
    02355                                 zoom_region->viewport.y1);
    02356                 gtk_fixed_move (fixed,
    02357                                 zoom_region->priv->w,
    02358                                 zoom_region->viewport.x1 +
    02359                                 zoom_region->border_size_left,
    02360                                 zoom_region->viewport.y1 +
    02361                                 zoom_region->border_size_top);
    02362                 gtk_widget_set_size_request (
    02363                         GTK_WIDGET (zoom_region->priv->border),
    02364                         zoom_region->viewport.x2 - zoom_region->viewport.x1,
    02365                         zoom_region->viewport.y2 - zoom_region->viewport.y1);
    02366                 gtk_widget_set_size_request (GTK_WIDGET (zoom_region->priv->w),
    02367                                              zoom_region->viewport.x2 -
    02368                                              zoom_region->viewport.x1 -
    02369                                              (zoom_region->border_size_right +
    02370                                               zoom_region->border_size_left),
    02371                                              zoom_region->viewport.y2 -
    02372                                              zoom_region->viewport.y1 -
    02373                                              (zoom_region->border_size_bottom +
    02374                                               zoom_region->border_size_top));
    02375                 CORBA_exception_init (&ev);
    02376                 properties =
    02377                         GNOME_Magnifier_Magnifier_getProperties(
    02378                                 BONOBO_OBJREF (
    02379                                         (Magnifier *) zoom_region->priv->parent), &ev);
    02380                 if (!BONOBO_EX (&ev))
    02381                         any = Bonobo_PropertyBag_getValue (
    02382                                 properties, "source-display-bounds", &ev);
    02383                 if (!BONOBO_EX (&ev))
    02384                         zoom_region->priv->source_area =
    02385                                 *((GNOME_Magnifier_RectBounds *) any->_value);
    02386                 if (zoom_region->priv->pixmap)
    02387                         g_object_unref (zoom_region->priv->pixmap);
    02388                 zoom_region_create_pixmap (zoom_region);
    02389                 if (zoom_region->priv->scaled_pixbuf)
    02390                         g_object_unref (zoom_region->priv->scaled_pixbuf);
    02391
    02392                 zoom_region->priv->scaled_pixbuf =
    02393                   gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
    02394                                   (zoom_region->priv->source_area.x2 -
    02395                                    zoom_region->priv->source_area.x1) * zoom_region->xscale + 1,
    02396                                   (zoom_region->priv->source_area.y2 -
    02397                                    zoom_region->priv->source_area.y1) * zoom_region->yscale + 1);
    02398         }
    02399         zoom_region_queue_update (zoom_region,
    02400                                   zoom_region_source_rect_from_view_bounds (
    02401                                           zoom_region, &zoom_region->viewport));
    02402 }
    02403
    02404 static void
    02405 zoom_region_get_property (BonoboPropertyBag *bag,
    02406                           BonoboArg *arg,
    02407                           guint arg_id,
    02408                           CORBA_Environment *ev,
    02409                           gpointer user_data)
    02410 {
    02411         ZoomRegion *zoom_region = user_data;
    02412
    02413 #ifdef ZOOM_REGION_DEBUG
    02414         g_assert (zoom_region->alive);
    02415 #endif
    02416         DBG (fprintf (stderr, "Get zoom-region property: %s\n", prop_names[arg_id]));
    02417
    02418         switch (arg_id) {
    02419         case ZOOM_REGION_MANAGED_PROP:
    02420                 BONOBO_ARG_SET_BOOLEAN (arg, zoom_region->is_managed);
    02421                 break;
    02422         case ZOOM_REGION_POLL_MOUSE_PROP:
    02423                 BONOBO_ARG_SET_BOOLEAN (arg, zoom_region->poll_mouse);
    02424                 break;
    02425         case ZOOM_REGION_DRAW_CURSOR_PROP:
    02426                 BONOBO_ARG_SET_BOOLEAN (arg, zoom_region->draw_cursor);
    02427                 break;
    02428         case ZOOM_REGION_INVERT_PROP:
    02429                 BONOBO_ARG_SET_BOOLEAN (arg, zoom_region->invert);
    02430                 break;
    02431         case ZOOM_REGION_SMOOTHSCROLL_PROP:
    02432                 BONOBO_ARG_SET_SHORT (arg, zoom_region->smooth_scroll_policy);
    02433                 break;
    02434         case ZOOM_REGION_COLORBLIND_PROP:
    02435                 BONOBO_ARG_SET_SHORT (arg, zoom_region->color_blind_filter);
    02436                 break;
    02437         case ZOOM_REGION_TESTPATTERN_PROP:
    02438                 BONOBO_ARG_SET_BOOLEAN (arg, zoom_region->priv->test);
    02439                 break;
    02440         case ZOOM_REGION_SMOOTHING_PROP:
    02441                 BONOBO_ARG_SET_STRING (arg, zoom_region->smoothing);
    02442                 break;
    02443         case ZOOM_REGION_CONTRASTR_PROP:
    02444                 BONOBO_ARG_SET_FLOAT (arg, zoom_region->contrast_r);
    02445                 break;
    02446         case ZOOM_REGION_CONTRASTG_PROP:
    02447                 BONOBO_ARG_SET_FLOAT (arg, zoom_region->contrast_g);
    02448                 break;
    02449         case ZOOM_REGION_CONTRASTB_PROP:
    02450                 BONOBO_ARG_SET_FLOAT (arg, zoom_region->contrast_b);
    02451                 break;
    02452         case ZOOM_REGION_BRIGHTR_PROP:
    02453                 BONOBO_ARG_SET_FLOAT (arg, zoom_region->bright_r);
    02454                 break;
    02455         case ZOOM_REGION_BRIGHTG_PROP:
    02456                 BONOBO_ARG_SET_FLOAT (arg, zoom_region->bright_g);
    02457                 break;
    02458         case ZOOM_REGION_BRIGHTB_PROP:
    02459                 BONOBO_ARG_SET_FLOAT (arg, zoom_region->bright_b);
    02460                 break;
    02461         case ZOOM_REGION_XSCALE_PROP:
    02462                 BONOBO_ARG_SET_FLOAT (arg, zoom_region->xscale);
    02463                 break;
    02464         case ZOOM_REGION_YSCALE_PROP:
    02465                 BONOBO_ARG_SET_FLOAT (arg, zoom_region->yscale);
    02466                 break;
    02467         case ZOOM_REGION_BORDERSIZE_PROP:
    02468                 BONOBO_ARG_SET_LONG (
    02469                         arg, (zoom_region->border_size_top +
    02470                               zoom_region->border_size_left +
    02471                               zoom_region->border_size_right +
    02472                               zoom_region->border_size_bottom) / 4);
    02473                 break;
    02474         case ZOOM_REGION_BORDERSIZETOP_PROP:
    02475                 BONOBO_ARG_SET_LONG (arg, zoom_region->border_size_top);
    02476                 break;
    02477         case ZOOM_REGION_BORDERSIZELEFT_PROP:
    02478                 BONOBO_ARG_SET_LONG (arg, zoom_region->border_size_left);
    02479                 break;
    02480         case ZOOM_REGION_BORDERSIZERIGHT_PROP:
    02481                 BONOBO_ARG_SET_LONG (arg, zoom_region->border_size_right);
    02482                 break;
    02483         case ZOOM_REGION_BORDERSIZEBOTTOM_PROP:
    02484                 BONOBO_ARG_SET_LONG (arg, zoom_region->border_size_bottom);
    02485                 break;
    02486         case ZOOM_REGION_XALIGN_PROP:
    02487                 /* TODO: enums here */
    02488                 BONOBO_ARG_SET_INT (arg, zoom_region->x_align_policy);
    02489                 break;
    02490         case ZOOM_REGION_YALIGN_PROP:
    02491                 BONOBO_ARG_SET_INT (arg, zoom_region->y_align_policy);
    02492                 break;
    02493         case ZOOM_REGION_BORDERCOLOR_PROP:
    02494                 BONOBO_ARG_SET_LONG (arg,
    02495                                      zoom_region->border_color);
    02496                 break;
    02497         case ZOOM_REGION_VIEWPORT_PROP:
    02498                 BONOBO_ARG_SET_GENERAL (arg, zoom_region->viewport,
    02499                                         TC_GNOME_Magnifier_RectBounds,
    02500                                         GNOME_Magnifier_RectBounds,
    02501                                         NULL);
    02502                 break;
    02503         case ZOOM_REGION_TIMING_TEST_PROP:
    02504                 BONOBO_ARG_SET_INT (arg, zoom_region->timing_iterations);
    02505                 break;
    02506         case ZOOM_REGION_TIMING_OUTPUT_PROP:
    02507                 BONOBO_ARG_SET_BOOLEAN (arg, zoom_region->timing_output);
    02508                 break;
    02509         case ZOOM_REGION_TIMING_PAN_RATE_PROP:
    02510                 BONOBO_ARG_SET_INT (arg, zoom_region->timing_pan_rate);
    02511                 break;
    02512         case ZOOM_REGION_EXIT_MAGNIFIER:
    02513                 BONOBO_ARG_SET_BOOLEAN (arg, zoom_region->exit_magnifier);
    02514                 break;
    02515         default:
    02516                 bonobo_exception_set (ev, ex_Bonobo_PropertyBag_NotFound);
    02517         };
    02518 }
    02519
    02520 static void
    02521 zoom_region_update_borders (ZoomRegion *zoom_region)
    02522 {
    02523         gtk_widget_set_size_request (GTK_WIDGET (zoom_region->priv->border),
    02524                                      zoom_region->viewport.x2 -
    02525                                      zoom_region->viewport.x1,
    02526                                      zoom_region->viewport.y2 -
    02527                                      zoom_region->viewport.y1);
    02528         gtk_widget_set_size_request (GTK_WIDGET (zoom_region->priv->w),
    02529                                      zoom_region->viewport.x2 -
    02530                                      zoom_region->viewport.x1 -
    02531                                      (zoom_region->border_size_right +
    02532                                       zoom_region->border_size_left),
    02533                                      zoom_region->viewport.y2 -
    02534                                      zoom_region->viewport.y1 -
    02535                                      (zoom_region->border_size_bottom +
    02536                                       zoom_region->border_size_top));
    02537         gtk_fixed_move (GTK_FIXED (((Magnifier *)zoom_region->priv->parent)->priv->canvas), zoom_region->priv->border, zoom_region->viewport.x1, zoom_region->viewport.y1);
    02538         gtk_fixed_move (GTK_FIXED (((Magnifier *)zoom_region->priv->parent)->priv->canvas), zoom_region->priv->w, zoom_region->viewport.x1 + zoom_region->border_size_left, zoom_region->viewport.y1 + zoom_region->border_size_top);
    02539 }
    02540
    02541 static void
    02542 zoom_region_set_property (BonoboPropertyBag *bag,
    02543                           BonoboArg *arg,
    02544                           guint arg_id,
    02545                           CORBA_Environment *ev,
    02546                           gpointer user_data)
    02547 {
    02548         ZoomRegion *zoom_region = user_data;
    02549         GNOME_Magnifier_RectBounds bounds;
    02550         gfloat t;
    02551
    02552 #ifdef ZOOM_REGION_DEBUG
    02553         g_assert (zoom_region->alive);
    02554 #endif
    02555         DBG (fprintf (stderr, "Set zoom-region property: %s\n", prop_names[arg_id]));
    02556
    02557         switch (arg_id) {
    02558         case ZOOM_REGION_MANAGED_PROP:
    02559                 zoom_region->is_managed = BONOBO_ARG_GET_BOOLEAN (arg);
    02560                 break;
    02561         case ZOOM_REGION_POLL_MOUSE_PROP:
    02562                 zoom_region->poll_mouse = BONOBO_ARG_GET_BOOLEAN (arg);
    02563                 if (zoom_region->poll_mouse)
    02564                 {
    02565                     g_message ("Adding polling timer");
    02566                     zoom_region->priv->update_pointer_id =
    02567                         g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
    02568                                             200,
    02569                                             zoom_region_update_pointer_timeout,
    02570                                             zoom_region,
    02571                                             NULL);
    02572                 }
    02573                 else if (zoom_region->priv->update_pointer_id)
    02574                 {
    02575                     g_message ("Removing polling timer");
    02576                     g_source_remove (zoom_region->priv->update_pointer_id);
    02577                     zoom_region->priv->update_pointer_id = 0;
    02578                 }
    02579                 break;
    02580         case ZOOM_REGION_DRAW_CURSOR_PROP:
    02581                 zoom_region->draw_cursor = BONOBO_ARG_GET_BOOLEAN (arg);
    02582                 if (!zoom_region->draw_cursor)
    02583                         zoom_region_unpaint_cursor (zoom_region, NULL);
    02584                 break;
    02585         case ZOOM_REGION_INVERT_PROP:
    02586                 zoom_region->invert = BONOBO_ARG_GET_BOOLEAN (arg);
    02587                 zoom_region_update_current (zoom_region);
    02588                 break;
    02589         case ZOOM_REGION_SMOOTHSCROLL_PROP:
    02590                 zoom_region->smooth_scroll_policy = BONOBO_ARG_GET_SHORT (arg);
    02591                 break;
    02592         case ZOOM_REGION_COLORBLIND_PROP:
    02593                 zoom_region->color_blind_filter = BONOBO_ARG_GET_SHORT (arg);
    02594                 zoom_region_update_current (zoom_region);
    02595                 break;
    02596         case ZOOM_REGION_SMOOTHING_PROP:
    02597                 zoom_region->smoothing = BONOBO_ARG_GET_STRING (arg);
    02598                 if (!strncmp (zoom_region->smoothing, "bilinear", 8))
    02599                         zoom_region->priv->gdk_interp_type = GDK_INTERP_BILINEAR;
    02600                 else
    02601                         zoom_region->priv->gdk_interp_type = GDK_INTERP_NEAREST;
    02602                 zoom_region_update_current (zoom_region);
    02603                 break;
    02604         case ZOOM_REGION_TESTPATTERN_PROP:
    02605                 zoom_region->priv->test = BONOBO_ARG_GET_BOOLEAN (arg);
    02606                 if (zoom_region->priv->source_drawable) {
    02607                         g_object_unref (zoom_region->priv->source_drawable);
    02608                         zoom_region->priv->source_drawable = NULL;
    02609                 }
    02610                 zoom_region_update_current (zoom_region);
    02611                 break;
    02612         case ZOOM_REGION_CONTRASTR_PROP:
    02613                 zoom_region->contrast_r =
    02614                         CLAMP_B_C (BONOBO_ARG_GET_FLOAT (arg));
    02615                 zoom_region_update_current (zoom_region);
    02616                 break;
    02617         case ZOOM_REGION_CONTRASTG_PROP:
    02618                 zoom_region->contrast_g =
    02619                         CLAMP_B_C (BONOBO_ARG_GET_FLOAT (arg));
    02620                 zoom_region_update_current (zoom_region);
    02621                 break;
    02622         case ZOOM_REGION_CONTRASTB_PROP:
    02623                 zoom_region->contrast_b =
    02624                         CLAMP_B_C (BONOBO_ARG_GET_FLOAT (arg));
    02625                 zoom_region_update_current (zoom_region);
    02626                 break;
    02627         case ZOOM_REGION_BRIGHTR_PROP:
    02628                 zoom_region->bright_r =
    02629                         CLAMP_B_C (BONOBO_ARG_GET_FLOAT (arg));
    02630                 zoom_region_update_current (zoom_region);
    02631                 break;
    02632         case ZOOM_REGION_BRIGHTG_PROP:
    02633                 zoom_region->bright_g =
    02634                         CLAMP_B_C (BONOBO_ARG_GET_FLOAT (arg));
    02635                 zoom_region_update_current (zoom_region);
    02636                 break;
    02637         case ZOOM_REGION_BRIGHTB_PROP:
    02638                 zoom_region->bright_b =
    02639                         CLAMP_B_C (BONOBO_ARG_GET_FLOAT (arg));
    02640                 zoom_region_update_current (zoom_region);
    02641                 break;
    02642         case ZOOM_REGION_XSCALE_PROP:
    02643                 (void) zoom_region_update_scale (zoom_region,
    02644                                                  BONOBO_ARG_GET_FLOAT (arg),
    02645                                                  zoom_region->yscale);
    02646                 break;
    02647         case ZOOM_REGION_YSCALE_PROP:
    02648                 (void) zoom_region_update_scale (zoom_region,
    02649                                                  zoom_region->xscale,
    02650                                                  BONOBO_ARG_GET_FLOAT (arg));
    02651
    02652                 break;
    02653         case ZOOM_REGION_BORDERSIZE_PROP:
    02654                 zoom_region->border_size_left =
    02655                         zoom_region->border_size_top =
    02656                         zoom_region->border_size_right =
    02657                         zoom_region->border_size_bottom =
    02658                         BONOBO_ARG_GET_LONG (arg);
    02659                 zoom_region_update_borders (zoom_region);
    02660                 break;
    02661         case ZOOM_REGION_BORDERSIZELEFT_PROP:
    02662                 zoom_region->border_size_left = BONOBO_ARG_GET_LONG (arg);
    02663                 zoom_region_update_borders (zoom_region);
    02664                 break;
    02665         case ZOOM_REGION_BORDERSIZETOP_PROP:
    02666                 zoom_region->border_size_top = BONOBO_ARG_GET_LONG (arg);
    02667                 zoom_region_update_borders (zoom_region);
    02668                 break;
    02669         case ZOOM_REGION_BORDERSIZERIGHT_PROP:
    02670                 zoom_region->border_size_right = BONOBO_ARG_GET_LONG (arg);
    02671                 zoom_region_update_borders (zoom_region);
    02672                 break;
    02673         case ZOOM_REGION_BORDERSIZEBOTTOM_PROP:
    02674                 zoom_region->border_size_bottom = BONOBO_ARG_GET_LONG (arg);
    02675                 zoom_region_update_borders (zoom_region);
    02676                 break;
    02677         case ZOOM_REGION_BORDERCOLOR_PROP:
    02678                 zoom_region->border_color =
    02679                         BONOBO_ARG_GET_LONG (arg);
    02680                 zoom_region_paint_border (zoom_region);
    02681                 break;
    02682         case ZOOM_REGION_XALIGN_PROP:
    02683                 zoom_region->x_align_policy = BONOBO_ARG_GET_INT (arg);
    02684                 zoom_region_align (zoom_region);
    02685                 break;
    02686         case ZOOM_REGION_YALIGN_PROP:
    02687                 /* TODO: enums here */
    02688                 zoom_region->y_align_policy = BONOBO_ARG_GET_INT (arg);
    02689                 zoom_region_align (zoom_region);
    02690                 break;
    02691         case ZOOM_REGION_VIEWPORT_PROP:
    02692                 bounds = BONOBO_ARG_GET_GENERAL (arg,
    02693                                                  TC_GNOME_Magnifier_RectBounds,
    02694                                                  GNOME_Magnifier_RectBounds,
    02695                                                  NULL);
    02696                 zoom_region_set_viewport (zoom_region, &bounds);
    02697                 break;
    02698         case ZOOM_REGION_TIMING_TEST_PROP:
    02699                 zoom_region->timing_iterations = BONOBO_ARG_GET_INT (arg);
    02700                 timing_test = TRUE;
    02701                 break;
    02702         case ZOOM_REGION_TIMING_OUTPUT_PROP:
    02703                 zoom_region->timing_output = BONOBO_ARG_GET_BOOLEAN (arg);
    02704                 break;
    02705         case ZOOM_REGION_TIMING_PAN_RATE_PROP:
    02706                 zoom_region->timing_pan_rate = BONOBO_ARG_GET_INT (arg);
    02707                 timing_test = TRUE;
    02708                 break;
    02709         case ZOOM_REGION_EXIT_MAGNIFIER:
    02710                 zoom_region->exit_magnifier = BONOBO_ARG_GET_BOOLEAN (arg);
    02711                 break;
    02712         default:
    02713                 bonobo_exception_set (ev, ex_Bonobo_PropertyBag_NotFound);
    02714         };
    02715 }
    02716
    02717 static int
    02718 zoom_region_process_pending (gpointer data)
    02719 {
    02720         ZoomRegion *zoom_region = (ZoomRegion *) data;
    02721
    02722 #ifdef ZOOM_REGION_DEBUG
    02723         g_assert (zoom_region->alive);
    02724 #endif
    02725         zoom_region_align (zoom_region);
    02726         return FALSE;
    02727 }
    02728
    02729 static int
    02730 zoom_region_pan_test (gpointer data)
    02731 {
    02732         ZoomRegion *zoom_region = (ZoomRegion *) data;
    02733         Magnifier *magnifier = zoom_region->priv->parent;
    02734         GNOME_Magnifier_ZoomRegionList *zoom_regions;
    02735         GNOME_Magnifier_RectBounds roi;
    02736         CORBA_Environment ev;
    02737         static int counter = 0;
    02738         static gboolean finished_update = !TRUE;
    02739         static float last_pixels_at_speed = -1;
    02740         float pixels_at_speed;
    02741         float total_time;
    02742         int screen_height, height;
    02743         int pixel_position;
    02744         int pixel_direction;
    02745
    02746         screen_height = gdk_screen_get_height (
    02747                 gdk_display_get_screen (magnifier->source_display,
    02748                  magnifier->source_screen_num));
    02749
    02750         height = (zoom_region->viewport.y2 -
    02751                 zoom_region->viewport.y1) / zoom_region->yscale;
    02752
    02753         roi.x1 = zoom_region->roi.x1;
    02754         roi.x2 = zoom_region->roi.x2;
    02755
    02756         g_timer_stop (mag_timing.process);
    02757
    02758         gulong microseconds;
    02759
    02760         total_time = g_timer_elapsed (mag_timing.process, &microseconds);
    02761
    02762         if (mag_timing.frame_total != 0.0)
    02763                 pixels_at_speed = total_time * zoom_region->timing_pan_rate;
    02764         else
    02765                 pixels_at_speed = 0.0;
    02766
    02767     /* Wait until it is actually necessary to update the screen */
    02768     if ((int)(last_pixels_at_speed) == (int)(pixels_at_speed))
    02769         return TRUE;
    02770
    02771         pixel_position = (int)(pixels_at_speed) % (screen_height - height);
    02772         counter = (int)(pixels_at_speed) / (screen_height - height);
    02773         pixel_direction = counter % 2;
    02774
    02775         if (!finished_update) {
    02776                 if ((int)(pixels_at_speed) > (zoom_region->roi.y1 + height))
    02777                         roi.y1 = zoom_region->roi.y1 + height;
    02778                 else
    02779                         roi.y1 = (int)(pixels_at_speed);
    02780
    02781                 if (roi.y1 >= screen_height - height) {
    02782                         roi.y1 = screen_height - height;
    02783                 }
    02784         } else {
    02785                 if (pixel_direction == 0)
    02786                         roi.y1 = screen_height - height - pixel_position;
    02787                 else
    02788                         roi.y1 = pixel_position;
    02789         }
    02790
    02791         roi.y2 = roi.y1 + height;
    02792         magnifier->priv->cursor_x = (roi.x2 + roi.x1) / 2;
    02793         magnifier->priv->cursor_y = (roi.y2 + roi.y1) / 2;
    02794
    02795         /* Add one since in first loop we call zoom_region_process_updates */
    02796         if (counter > zoom_region->timing_iterations - 1)
    02797                 zoom_region->exit_magnifier = TRUE;
    02798
    02799         zoom_regions = GNOME_Magnifier_Magnifier_getZoomRegions (
    02800                 BONOBO_OBJREF (magnifier), &ev);
    02801
    02802         if (zoom_regions && (zoom_regions->_length > 0)) {
    02803                 GNOME_Magnifier_ZoomRegion_setROI (
    02804                         zoom_regions->_buffer[0], &roi, &ev);
    02805         }
    02806
    02807         if (!finished_update) {
    02808                 zoom_region_process_updates(zoom_region);
    02809                 if (roi.y1 == screen_height - height) {
    02810                         finished_update = TRUE;
    02811                         reset_timing = TRUE;
    02812                 }
    02813         }
    02814
    02815     last_pixels_at_speed = pixels_at_speed;
    02816
    02817         return FALSE;
    02818 }
    02819
    02820 static void
    02821 impl_zoom_region_set_pointer_pos (PortableServer_Servant servant,
    02822                                   const CORBA_long mouse_x,
    02823                                   const CORBA_long mouse_y,
    02824                                   CORBA_Environment *ev)
    02825 {
    02826         ZoomRegion *zoom_region =
    02827                 ZOOM_REGION (bonobo_object_from_servant (servant));
    02828         GdkRectangle paint_area, *clip = NULL;
    02829
    02830 #ifdef ZOOM_REGION_DEBUG
    02831         g_assert (zoom_region->alive);
    02832 #endif
    02833         DBG (fprintf (stderr, "Set Pointer: \t%ld,%ld\n",
    02834                       (long) mouse_x, (long) mouse_y));
    02835
    02836         fprintf (stderr, "Set Pointer: \t%ld,%ld\n",
    02837                       (long) mouse_x, (long) mouse_y);
    02838
    02839         zoom_region_set_cursor_pos (zoom_region, (int) mouse_x, (int) mouse_y);
    02840
    02841         if (GTK_IS_WIDGET (zoom_region->priv->w) &&
    02842             GDK_IS_DRAWABLE (zoom_region->priv->w->window))
    02843         {
    02844             gdk_drawable_get_size (
    02845                 GDK_DRAWABLE (
    02846                     zoom_region->priv->w->window),
    02847                 &paint_area.width, &paint_area.height);
    02848             paint_area.x = 0;
    02849             paint_area.y = 0;
    02850             clip = &paint_area;
    02851             paint_area = zoom_region_clip_to_source (
    02852                 zoom_region, paint_area);
    02853         }
    02854         /*
    02855          * if we update the cursor now, it causes flicker if the client
    02856          * subsequently calls setROI, so we wait for a redraw.
    02857          * Perhaps we should cue a redraw on idle instead?
    02858          */
    02859 }
    02860
    02861 static void
    02862 impl_zoom_region_set_contrast (PortableServer_Servant servant,
    02863                                const CORBA_float R,
    02864                                const CORBA_float G,
    02865                                const CORBA_float B,
    02866                                CORBA_Environment *ev)
    02867 {
    02868         ZoomRegion *zoom_region =
    02869                 ZOOM_REGION (bonobo_object_from_servant (servant));
    02870         gfloat t;
    02871
    02872 #ifdef ZOOM_REGION_DEBUG
    02873         g_assert (zoom_region->alive);
    02874 #endif
    02875         DBG (fprintf (stderr, "Set contrast: \t%f,%f %f\n", R, G, B));
    02876
    02877         /* if the contrast values are the same, this is a NOOP */
    02878         if (zoom_region->contrast_r == R &&
    02879             zoom_region->contrast_g == G &&
    02880             zoom_region->contrast_b == B)
    02881                 return;
    02882
    02883         zoom_region->contrast_r = CLAMP_B_C (R);
    02884         zoom_region->contrast_g = CLAMP_B_C (G);
    02885         zoom_region->contrast_b = CLAMP_B_C (B);
    02886
    02887         zoom_region_update_current (zoom_region);
    02888 }
    02889
    02890 static void
    02891 impl_zoom_region_get_contrast (PortableServer_Servant servant,
    02892                                CORBA_float *R,
    02893                                CORBA_float *G,
    02894                                CORBA_float *B,
    02895                                CORBA_Environment *ev)
    02896 {
    02897         ZoomRegion *zoom_region =
    02898                 ZOOM_REGION (bonobo_object_from_servant (servant));
    02899
    02900 #ifdef ZOOM_REGION_DEBUG
    02901         g_assert (zoom_region->alive);
    02902 #endif
    02903
    02904         *R = zoom_region->contrast_r;
    02905         *G = zoom_region->contrast_g;
    02906         *B = zoom_region->contrast_b;
    02907 }
    02908
    02909 static void
    02910 impl_zoom_region_set_brightness (PortableServer_Servant servant,
    02911                                  const CORBA_float R,
    02912                                  const CORBA_float G,
    02913                                  const CORBA_float B,
    02914                                  CORBA_Environment *ev)
    02915 {
    02916         ZoomRegion *zoom_region =
    02917                 ZOOM_REGION (bonobo_object_from_servant (servant));
    02918         gfloat t;
    02919
    02920 #ifdef ZOOM_REGION_DEBUG
    02921         g_assert (zoom_region->alive);
    02922 #endif
    02923         DBG (fprintf (stderr, "Set brightness: \t%f,%f %f\n", R, G, B));
    02924
    02925         /* if the contrast values are the same, this is a NOOP */
    02926         if (zoom_region->bright_r == R &&
    02927             zoom_region->bright_g == G &&
    02928             zoom_region->bright_b == B)
    02929                 return;
    02930
    02931         zoom_region->bright_r = CLAMP_B_C (R);
    02932         zoom_region->bright_g = CLAMP_B_C (G);
    02933         zoom_region->bright_b = CLAMP_B_C (B);
    02934
    02935         zoom_region_update_current (zoom_region);
    02936 }
    02937
    02938 static void
    02939 impl_zoom_region_get_brightness (PortableServer_Servant servant,
    02940                                  CORBA_float *R,
    02941                                  CORBA_float *G,
    02942                                  CORBA_float *B,
    02943                                  CORBA_Environment *ev)
    02944 {
    02945         ZoomRegion *zoom_region =
    02946                 ZOOM_REGION (bonobo_object_from_servant (servant));
    02947
    02948 #ifdef ZOOM_REGION_DEBUG
    02949         g_assert (zoom_region->alive);
    02950 #endif
    02951
    02952         *R = zoom_region->bright_r;
    02953         *G = zoom_region->bright_g;
    02954         *B = zoom_region->bright_b;
    02955 }
    02956
    02957 static void
    02958 impl_zoom_region_set_roi (PortableServer_Servant servant,
    02959                           const GNOME_Magnifier_RectBounds *bounds,
    02960                           CORBA_Environment *ev)
    02961 {
    02962         ZoomRegion *zoom_region =
    02963                 ZOOM_REGION (bonobo_object_from_servant (servant));
    02964
    02965 #ifdef ZOOM_REGION_DEBUG
    02966         g_assert (zoom_region->alive);
    02967 #endif
    02968         DBG (fprintf (stderr, "Set ROI: \t%d,%d %d,%d\n",
    02969                       bounds->x1, bounds->y1, bounds->x2, bounds->y2));
    02970
    02971         if ((zoom_region->roi.x1 == bounds->x1) &&
    02972             (zoom_region->roi.x2 == bounds->x2) &&
    02973             (zoom_region->roi.y1 == bounds->y1) &&
    02974             (zoom_region->roi.y2 == bounds->y2)) {
    02975             return;
    02976         }
    02977
    02978         /* if these bounds are clearly bogus, warn and ignore */
    02979         if (!bounds || (bounds->x2 <= bounds->x1)
    02980             || (bounds->y2 < bounds->y1) ||
    02981             ((bounds->x1 + bounds->x2)/2 < 0) ||
    02982             ((bounds->y1 + bounds->y2)/2 < 0))
    02983         {
    02984             g_warning ("Bad bounds request (%d,%d to %d,%d), ignoring.\n",
    02985                        bounds->x1, bounds->y1, bounds->x2, bounds->y2);
    02986             return;
    02987         }
    02988
    02989         zoom_region->roi = *bounds;
    02990
    02991         if (zoom_region->timing_pan_rate > 0) {
    02992                 /* Set idle handler to do panning test */
    02993                 g_idle_add_full (GDK_PRIORITY_REDRAW + 3,
    02994                         zoom_region_pan_test, zoom_region, NULL);
    02995         }
    02996
    02997         if (zoom_region->exit_magnifier) {
    02998                 if (timing_test) {
    02999                         fprintf(stderr, "\n### Timing Summary:\n\n");
    03000                         if (zoom_region->timing_pan_rate)
    03001                                 fprintf(stderr, "  Pan Rate                 = %d\n", zoom_region->timing_pan_rate);
    03002                         timing_report(zoom_region);
    03003                 }
    03004                 exit(0);
    03005         }
    03006
    03007         /*
    03008          * Do not bother trying to update the screen if the last
    03009          * screen update has not had time to complete.
    03010          */
    03011         if (processing_updates) {
    03012                 /* Remove any previous idle handler */
    03013                 if (pending_idle_handler != 0) {
    03014                         g_source_remove(pending_idle_handler);
    03015                         pending_idle_handler = 0;
    03016                 }
    03017
    03018                 /* Set idle handler to process this pending update when possible */
    03019
    03020                 pending_idle_handler = g_idle_add_full (GDK_PRIORITY_REDRAW + 2,
    03021                         zoom_region_process_pending, zoom_region, NULL);
    03022
    03023                 if (zoom_region->timing_output) {
    03024                         fprintf(stderr,
    03025                                 "\n  [Last update not finished, pending - ROI=(%d, %d) (%d, %d)]\n\n",
    03026                                 zoom_region->roi.x1, zoom_region->roi.y1, zoom_region->roi.x2,
    03027                                 zoom_region->roi.y2);
    03028                 }
    03029         } else {
    03030                 zoom_region_align (zoom_region);
    03031         }
    03032 }
    03033
    03034 static CORBA_boolean
    03035 impl_zoom_region_set_mag_factor (PortableServer_Servant servant,
    03036                                  const CORBA_float mag_factor_x,
    03037                                  const CORBA_float mag_factor_y,
    03038                                  CORBA_Environment *ev)
    03039 {
    03040         ZoomRegion *zoom_region =
    03041                 ZOOM_REGION (bonobo_object_from_servant (servant));
    03042
    03043 #ifdef ZOOM_REGION_DEBUG
    03044         g_assert (zoom_region->alive);
    03045 #endif
    03046         CORBA_any *any;
    03047         CORBA_boolean retval = CORBA_TRUE;
    03048
    03049         if ((zoom_region->xscale == mag_factor_x) &&
    03050             (zoom_region->yscale == mag_factor_y)) {
    03051                 return retval;
    03052         }
    03053
    03054         /* TODO: assert that parent is magnifier object */
    03055         Bonobo_PropertyBag properties =
    03056                 GNOME_Magnifier_Magnifier_getProperties(
    03057                         BONOBO_OBJREF (
    03058                                 (Magnifier *) zoom_region->priv->parent), ev);
    03059         any = Bonobo_PropertyBag_getValue (
    03060                 properties, "source-display-bounds", ev);
    03061         if (!BONOBO_EX (ev))
    03062                 zoom_region->priv->source_area =
    03063                         *((GNOME_Magnifier_RectBounds *) any->_value);
    03064         else
    03065                 retval = CORBA_FALSE;
    03066
    03067         retval = zoom_region_update_scale (zoom_region,
    03068                                            mag_factor_x, mag_factor_y);
    03069         zoom_region_sync (zoom_region);
    03070
    03071         bonobo_object_release_unref (properties, NULL);
    03072         return retval;
    03073 }
    03074
    03075 static void
    03076 impl_zoom_region_get_mag_factor (PortableServer_Servant servant,
    03077                                  CORBA_float *mag_factor_x,
    03078                                  CORBA_float *mag_factor_y,
    03079                                  CORBA_Environment *ev)
    03080 {
    03081         ZoomRegion *zoom_region =
    03082                 ZOOM_REGION (bonobo_object_from_servant (servant));
    03083
    03084 #ifdef ZOOM_REGION_DEBUG
    03085         g_assert (zoom_region->alive);
    03086 #endif
    03087         *mag_factor_x = zoom_region->xscale;
    03088         *mag_factor_y = zoom_region->yscale;
    03089 }
    03090
    03091 static Bonobo_PropertyBag
    03092 impl_zoom_region_get_properties (PortableServer_Servant servant,
    03093                                  CORBA_Environment *ev)
    03094 {
    03095         ZoomRegion *zoom_region =
    03096                 ZOOM_REGION (bonobo_object_from_servant (servant));
    03097
    03098 #ifdef ZOOM_REGION_DEBUG
    03099         g_assert (zoom_region->alive);
    03100 #endif
    03101         return bonobo_object_dup_ref (
    03102                 BONOBO_OBJREF (zoom_region->properties), ev);
    03103 }
    03104
    03105 static void
    03106 impl_zoom_region_update_pointer (PortableServer_Servant servant,
    03107                                  CORBA_Environment *ev)
    03108 {
    03109         ZoomRegion *zoom_region =
    03110                 ZOOM_REGION (bonobo_object_from_servant (servant));
    03111
    03112 #ifdef ZOOM_REGION_DEBUG
    03113         g_assert (zoom_region->alive);
    03114 #endif
    03115
    03116         zoom_region_update_cursor (zoom_region, 0, 0, NULL);
    03117 }
    03118
    03119 static void
    03120 impl_zoom_region_mark_dirty (PortableServer_Servant servant,
    03121                              const GNOME_Magnifier_RectBounds *roi_dirty,
    03122                              CORBA_Environment *ev)
    03123 {
    03124         ZoomRegion *zoom_region =
    03125                 ZOOM_REGION (bonobo_object_from_servant (servant));
    03126
    03127 #ifdef ZOOM_REGION_DEBUG
    03128         g_assert (zoom_region->alive);
    03129 #endif
    03130         DEBUG_RECT ("mark dirty", zoom_region_rect_from_bounds (
    03131                             zoom_region, roi_dirty) );
    03132
    03133         zoom_region_update_pointer (zoom_region, TRUE);
    03134         /* XXX ? should we clip here, or wait till process_updates? */
    03135         zoom_region_queue_update (zoom_region,
    03136           zoom_region_clip_to_source (zoom_region,
    03137               zoom_region_rect_from_bounds (zoom_region, roi_dirty)));
    03138 }
    03139
    03140 static GNOME_Magnifier_RectBounds
    03141 impl_zoom_region_get_roi (PortableServer_Servant servant,
    03142                           CORBA_Environment     *ev)
    03143 {
    03144         ZoomRegion *zoom_region =
    03145                 ZOOM_REGION (bonobo_object_from_servant (servant));
    03146
    03147 #ifdef ZOOM_REGION_DEBUG
    03148         g_assert (zoom_region->alive);
    03149 #endif
    03150         return zoom_region->roi;
    03151 }
    03152
    03153 static void
    03154 impl_zoom_region_move_resize (PortableServer_Servant            servant,
    03155                               const GNOME_Magnifier_RectBounds *viewport_bounds,
    03156                               CORBA_Environment                *ev)
    03157 {
    03158         ZoomRegion *zoom_region =
    03159                 ZOOM_REGION (bonobo_object_from_servant (servant));
    03160
    03161 #ifdef ZOOM_REGION_DEBUG
    03162         g_assert (zoom_region->alive);
    03163 #endif
    03164         zoom_region_set_viewport (zoom_region, viewport_bounds);
    03165 }
    03166
    03167 /* could be called multiple times... */
    03168 static void
    03169 zoom_region_do_dispose (ZoomRegion *zoom_region)
    03170 {
    03171         DBG(g_message ("disposing region %p", zoom_region));
    03172         if (zoom_region->priv && zoom_region->priv->expose_handler_id &&
    03173             GTK_IS_WIDGET (zoom_region->priv->w)) {
    03174                 g_signal_handler_disconnect (
    03175                         zoom_region->priv->w,
    03176                         zoom_region->priv->expose_handler_id);
    03177                 zoom_region->priv->expose_handler_id = 0;
    03178         }
    03179         if (zoom_region->priv && zoom_region->priv->update_pointer_id)
    03180             g_source_remove (zoom_region->priv->update_pointer_id);
    03181         if (zoom_region->priv && zoom_region->priv->update_handler_id)
    03182             g_source_remove (zoom_region->priv->update_handler_id);
    03183         g_idle_remove_by_data (zoom_region);
    03184
    03185 #ifdef ZOOM_REGION_DEBUG
    03186         zoom_region->alive = FALSE;
    03187 #endif
    03188 }
    03189
    03190 static void
    03191 impl_zoom_region_dispose (PortableServer_Servant servant,
    03192                           CORBA_Environment     *ev)
    03193 {
    03194         ZoomRegion *zoom_region =
    03195                 ZOOM_REGION (bonobo_object_from_servant (servant));
    03196         zoom_region_do_dispose (zoom_region);
    03197 }
    03198
    03199
    03200 /* could be called multiple times */
    03201 static void
    03202 zoom_region_dispose (GObject *object)
    03203 {
    03204         ZoomRegion *zoom_region = ZOOM_REGION (object);
    03205
    03206         zoom_region_do_dispose (zoom_region);
    03207
    03208         BONOBO_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
    03209 }
    03210
    03211 static void
    03212 zoom_region_class_init (ZoomRegionClass *klass)
    03213 {
    03214         GObjectClass * object_class = (GObjectClass *) klass;
    03215         POA_GNOME_Magnifier_ZoomRegion__epv *epv = &klass->epv;
    03216         parent_class = g_type_class_peek (BONOBO_TYPE_OBJECT); /* needed by BONOBO_CALL_PARENT! */
    03217
    03218         object_class->dispose = zoom_region_dispose;
    03219         object_class->finalize = zoom_region_finalize;
    03220
    03221         epv->setMagFactor = impl_zoom_region_set_mag_factor;
    03222         epv->getMagFactor = impl_zoom_region_get_mag_factor;
    03223         epv->getProperties = impl_zoom_region_get_properties;
    03224         epv->setROI = impl_zoom_region_set_roi;
    03225         epv->setPointerPos = impl_zoom_region_set_pointer_pos;
    03226         epv->updatePointer = impl_zoom_region_update_pointer;
    03227         epv->markDirty = impl_zoom_region_mark_dirty;
    03228         epv->getROI = impl_zoom_region_get_roi;
    03229         epv->moveResize = impl_zoom_region_move_resize;
    03230         epv->dispose = impl_zoom_region_dispose;
    03231         epv->setContrast = impl_zoom_region_set_contrast;
    03232         epv->getContrast = impl_zoom_region_get_contrast;
    03233         epv->setBrightness = impl_zoom_region_set_brightness;
    03234         epv->getBrightness = impl_zoom_region_get_brightness;
    03235
    03236         reset_timing_stats();
    03237 #ifdef DEBUG_CLIENT_CALLS
    03238         client_debug = (g_getenv ("MAG_CLIENT_DEBUG") != NULL);
    03239 #endif
    03240 }
    03241
    03242 static void
    03243 zoom_region_properties_init (ZoomRegion *zoom_region)
    03244 {
    03245         BonoboArg *def;
    03246
    03247         zoom_region->properties =
    03248                 bonobo_property_bag_new_closure (
    03249                         g_cclosure_new_object (
    03250                                 G_CALLBACK (zoom_region_get_property),
    03251                                 G_OBJECT (zoom_region)),
    03252                         g_cclosure_new_object (
    03253                                 G_CALLBACK (zoom_region_set_property),
    03254                                 G_OBJECT (zoom_region)));
    03255
    03256         def = bonobo_arg_new (BONOBO_ARG_BOOLEAN);
    03257         BONOBO_ARG_SET_BOOLEAN (def, TRUE);
    03258
    03259         bonobo_property_bag_add (zoom_region->properties,
    03260                                  "is-managed",
    03261                                  ZOOM_REGION_MANAGED_PROP,
    03262                                  BONOBO_ARG_BOOLEAN,
    03263                                  def,
    03264                                  "If false, zoom region does not auto-update, but is drawn into directly by the client",
    03265                                  Bonobo_PROPERTY_READABLE |
    03266                                  Bonobo_PROPERTY_WRITEABLE);
    03267
    03268         bonobo_arg_release (def);
    03269         def = bonobo_arg_new (BONOBO_ARG_BOOLEAN);
    03270         BONOBO_ARG_SET_BOOLEAN (def, TRUE);
    03271
    03272         bonobo_property_bag_add (zoom_region->properties,
    03273                                  "poll-mouse",
    03274                                  ZOOM_REGION_POLL_MOUSE_PROP,
    03275                                  BONOBO_ARG_BOOLEAN,
    03276                                  NULL,
    03277                                  "If false, zoom region does not poll for pointer location, but is (exclusively) given it by the client",
    03278                                  Bonobo_PROPERTY_READABLE |
    03279                                  Bonobo_PROPERTY_WRITEABLE);
    03280
    03281         bonobo_arg_release (def);
    03282         def = bonobo_arg_new (BONOBO_ARG_BOOLEAN);
    03283         BONOBO_ARG_SET_BOOLEAN (def, TRUE);
    03284
    03285         bonobo_property_bag_add (zoom_region->properties,
    03286                                  "draw-cursor",
    03287                                  ZOOM_REGION_DRAW_CURSOR_PROP,
    03288                                  BONOBO_ARG_BOOLEAN,
    03289                                  NULL,
    03290                                  "If false, zoom region does not draw the cursor.",
    03291                                  Bonobo_PROPERTY_READABLE |
    03292                                  Bonobo_PROPERTY_WRITEABLE);
    03293
    03294         bonobo_arg_release (def);
    03295         def = bonobo_arg_new (BONOBO_ARG_SHORT);
    03296         BONOBO_ARG_SET_SHORT (def, GNOME_Magnifier_ZoomRegion_SCROLL_FASTEST);
    03297
    03298         bonobo_property_bag_add (zoom_region->properties,
    03299                                  "smooth-scroll-policy",
    03300                                  ZOOM_REGION_SMOOTHSCROLL_PROP,
    03301                                  BONOBO_ARG_SHORT,
    03302                                  def,
    03303                                  "scrolling policy, slower versus faster",
    03304                                  Bonobo_PROPERTY_READABLE |
    03305                                  Bonobo_PROPERTY_WRITEABLE);
    03306
    03307         bonobo_arg_release (def);
    03308         def = bonobo_arg_new (BONOBO_ARG_SHORT);
    03309         BONOBO_ARG_SET_SHORT (
    03310                 def,
    03311                 GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_NO_FILTER);
    03312
    03313         bonobo_property_bag_add (zoom_region->properties,
    03314                                  "color-blind-filter",
    03315                                  ZOOM_REGION_COLORBLIND_PROP,
    03316                                  BONOBO_ARG_SHORT,
    03317                                  def,
    03318                                  "color blind filter to apply in an image",
    03319                                  Bonobo_PROPERTY_READABLE |
    03320                                  Bonobo_PROPERTY_WRITEABLE);
    03321
    03322         bonobo_arg_release (def);
    03323         def = bonobo_arg_new (BONOBO_ARG_BOOLEAN);
    03324         BONOBO_ARG_SET_BOOLEAN (def, FALSE);
    03325
    03326         bonobo_property_bag_add (zoom_region->properties,
    03327                                  "use-test-pattern",
    03328                                  ZOOM_REGION_TESTPATTERN_PROP,
    03329                                  BONOBO_ARG_BOOLEAN,
    03330                                  def,
    03331                                  "use test pattern for source",
    03332                                  Bonobo_PROPERTY_READABLE |
    03333                                  Bonobo_PROPERTY_WRITEABLE);
    03334
    03335         bonobo_arg_release (def);
    03336         def = bonobo_arg_new (BONOBO_ARG_BOOLEAN);
    03337         BONOBO_ARG_SET_BOOLEAN (def, TRUE);
    03338
    03339         bonobo_property_bag_add (zoom_region->properties,
    03340                                  "inverse-video",
    03341                                  ZOOM_REGION_INVERT_PROP,
    03342                                  BONOBO_ARG_BOOLEAN,
    03343                                  def,
    03344                                  "inverse video display",
    03345                                  Bonobo_PROPERTY_READABLE |
    03346                                  Bonobo_PROPERTY_WRITEABLE);
    03347
    03348         bonobo_arg_release (def);
    03349
    03350         bonobo_property_bag_add (zoom_region->properties,
    03351                                  "smoothing-type",
    03352                                  ZOOM_REGION_SMOOTHING_PROP,
    03353                                  BONOBO_ARG_STRING,
    03354                                  NULL,
    03355                                  "image smoothing algorithm used",
    03356                                  Bonobo_PROPERTY_READABLE |
    03357                                  Bonobo_PROPERTY_WRITEABLE);
    03358
    03359         def = bonobo_arg_new (BONOBO_ARG_FLOAT);
    03360         BONOBO_ARG_SET_FLOAT (def, 0.0);
    03361
    03362         bonobo_property_bag_add (zoom_region->properties,
    03363                                  "red-contrast",
    03364                                  ZOOM_REGION_CONTRASTR_PROP,
    03365                                  BONOBO_ARG_FLOAT,
    03366                                  def,
    03367                                  "red image contrast ratio",
    03368                                  Bonobo_PROPERTY_READABLE |
    03369                                  Bonobo_PROPERTY_WRITEABLE);
    03370         bonobo_arg_release (def);
    03371
    03372         def = bonobo_arg_new (BONOBO_ARG_FLOAT);
    03373         BONOBO_ARG_SET_FLOAT (def, 0.0);
    03374
    03375         bonobo_property_bag_add (zoom_region->properties,
    03376                                  "green-contrast",
    03377                                  ZOOM_REGION_CONTRASTG_PROP,
    03378                                  BONOBO_ARG_FLOAT,
    03379                                  def,
    03380                                  "green image contrast ratio",
    03381                                  Bonobo_PROPERTY_READABLE |
    03382                                  Bonobo_PROPERTY_WRITEABLE);
    03383         bonobo_arg_release (def);
    03384
    03385         def = bonobo_arg_new (BONOBO_ARG_FLOAT);
    03386         BONOBO_ARG_SET_FLOAT (def, 0.0);
    03387
    03388         bonobo_property_bag_add (zoom_region->properties,
    03389                                  "blue-contrast",
    03390                                  ZOOM_REGION_CONTRASTB_PROP,
    03391                                  BONOBO_ARG_FLOAT,
    03392                                  def,
    03393                                  "blue image contrast ratio",
    03394                                  Bonobo_PROPERTY_READABLE |
    03395                                  Bonobo_PROPERTY_WRITEABLE);
    03396         bonobo_arg_release (def);
    03397
    03398         def = bonobo_arg_new (BONOBO_ARG_FLOAT);
    03399         BONOBO_ARG_SET_FLOAT (def, 0.0);
    03400
    03401         bonobo_property_bag_add (zoom_region->properties,
    03402                                  "red-brightness",
    03403                                  ZOOM_REGION_BRIGHTR_PROP,
    03404                                  BONOBO_ARG_FLOAT,
    03405                                  def,
    03406                                  "red image brightness ratio",
    03407                                  Bonobo_PROPERTY_READABLE |
    03408                                  Bonobo_PROPERTY_WRITEABLE);
    03409         bonobo_arg_release (def);
    03410
    03411         def = bonobo_arg_new (BONOBO_ARG_FLOAT);
    03412         BONOBO_ARG_SET_FLOAT (def, 0.0);
    03413
    03414         bonobo_property_bag_add (zoom_region->properties,
    03415                                  "green-brightness",
    03416                                  ZOOM_REGION_BRIGHTG_PROP,
    03417                                  BONOBO_ARG_FLOAT,
    03418                                  def,
    03419                                  "green image brightness ratio",
    03420                                  Bonobo_PROPERTY_READABLE |
    03421                                  Bonobo_PROPERTY_WRITEABLE);
    03422         bonobo_arg_release (def);
    03423
    03424         def = bonobo_arg_new (BONOBO_ARG_FLOAT);
    03425         BONOBO_ARG_SET_FLOAT (def, 0.0);
    03426
    03427         bonobo_property_bag_add (zoom_region->properties,
    03428                                  "blue-brightness",
    03429                                  ZOOM_REGION_BRIGHTB_PROP,
    03430                                  BONOBO_ARG_FLOAT,
    03431                                  def,
    03432                                  "blue image brightness ratio",
    03433                                  Bonobo_PROPERTY_READABLE |
    03434                                  Bonobo_PROPERTY_WRITEABLE);
    03435         bonobo_arg_release (def);
    03436
    03437         def = bonobo_arg_new (BONOBO_ARG_FLOAT);
    03438         BONOBO_ARG_SET_FLOAT (def, 2.0);
    03439
    03440         bonobo_property_bag_add (zoom_region->properties,
    03441                                  "mag-factor-x",
    03442                                  ZOOM_REGION_XSCALE_PROP,
    03443                                  BONOBO_ARG_FLOAT,
    03444                                  def,
    03445                                  "x scale factor",
    03446                                  Bonobo_PROPERTY_READABLE |
    03447                                  Bonobo_PROPERTY_WRITEABLE);
    03448
    03449         bonobo_arg_release (def);
    03450         def = bonobo_arg_new (BONOBO_ARG_FLOAT);
    03451         BONOBO_ARG_SET_FLOAT (def, 2.0);
    03452
    03453         bonobo_property_bag_add (zoom_region->properties,
    03454                                  "mag-factor-y",
    03455                                  ZOOM_REGION_YSCALE_PROP,
    03456                                  BONOBO_ARG_FLOAT,
    03457                                  def,
    03458                                  "y scale factor",
    03459                                  Bonobo_PROPERTY_READABLE |
    03460                                  Bonobo_PROPERTY_WRITEABLE);
    03461
    03462         bonobo_arg_release (def);
    03463         def = bonobo_arg_new (BONOBO_ARG_LONG);
    03464         BONOBO_ARG_SET_LONG (def, 0);
    03465
    03466         bonobo_property_bag_add (zoom_region->properties,
    03467                                  "border-size",
    03468                                  ZOOM_REGION_BORDERSIZE_PROP,
    03469                                  BONOBO_ARG_LONG,
    03470                                  def,
    03471                                  "size of zoom-region borders, in pixels",
    03472                                  Bonobo_PROPERTY_READABLE |
    03473                                  Bonobo_PROPERTY_WRITEABLE);
    03474
    03475         bonobo_arg_release (def);
    03476         def = bonobo_arg_new (BONOBO_ARG_LONG);
    03477         BONOBO_ARG_SET_LONG (def, 0);
    03478
    03479         bonobo_property_bag_add (zoom_region->properties,
    03480                                  "border-size-left",
    03481                                  ZOOM_REGION_BORDERSIZELEFT_PROP,
    03482                                  BONOBO_ARG_LONG,
    03483                                  def,
    03484                                  "size of left zoom-region border, in pixels",
    03485                                  Bonobo_PROPERTY_READABLE |
    03486                                  Bonobo_PROPERTY_WRITEABLE);
    03487
    03488         bonobo_arg_release (def);
    03489         def = bonobo_arg_new (BONOBO_ARG_LONG);
    03490         BONOBO_ARG_SET_LONG (def, 0);
    03491
    03492         bonobo_property_bag_add (zoom_region->properties,
    03493                                  "border-size-top",
    03494                                  ZOOM_REGION_BORDERSIZETOP_PROP,
    03495                                  BONOBO_ARG_LONG,
    03496                                  def,
    03497                                  "size of top zoom-region border, in pixels",
    03498                                  Bonobo_PROPERTY_READABLE |
    03499                                  Bonobo_PROPERTY_WRITEABLE);
    03500
    03501         bonobo_arg_release (def);
    03502         def = bonobo_arg_new (BONOBO_ARG_LONG);
    03503         BONOBO_ARG_SET_LONG (def, 0);
    03504
    03505         bonobo_property_bag_add (zoom_region->properties,
    03506                                  "border-size-right",
    03507                                  ZOOM_REGION_BORDERSIZERIGHT_PROP,
    03508                                  BONOBO_ARG_LONG,
    03509                                  def,
    03510                                  "size of right zoom-region border, in pixels",
    03511                                  Bonobo_PROPERTY_READABLE |
    03512                                  Bonobo_PROPERTY_WRITEABLE);
    03513
    03514         bonobo_arg_release (def);
    03515         def = bonobo_arg_new (BONOBO_ARG_LONG);
    03516         BONOBO_ARG_SET_LONG (def, 0);
    03517
    03518         bonobo_property_bag_add (zoom_region->properties,
    03519                                  "border-size-bottom",
    03520                                  ZOOM_REGION_BORDERSIZEBOTTOM_PROP,
    03521                                  BONOBO_ARG_LONG,
    03522                                  def,
    03523                                  "size of bottom zoom-region border, in "
    03524                                  "pixels",
    03525                                  Bonobo_PROPERTY_READABLE |
    03526                                  Bonobo_PROPERTY_WRITEABLE);
    03527
    03528         bonobo_arg_release (def);
    03529         def = bonobo_arg_new (BONOBO_ARG_LONG);
    03530         BONOBO_ARG_SET_LONG (def, 0x00000000);
    03531
    03532         bonobo_property_bag_add (zoom_region->properties,
    03533                                  "border-color",
    03534                                  ZOOM_REGION_BORDERCOLOR_PROP,
    03535                                  BONOBO_ARG_LONG,
    03536                                  def,
    03537                                  "border color, as RGBA32",
    03538                                  Bonobo_PROPERTY_READABLE |
    03539                                  Bonobo_PROPERTY_WRITEABLE);
    03540
    03541         bonobo_arg_release (def);
    03542         def = bonobo_arg_new (BONOBO_ARG_INT);
    03543         BONOBO_ARG_SET_INT (def, 0);
    03544
    03545         bonobo_property_bag_add (zoom_region->properties,
    03546                                  "x-alignment",
    03547                                  ZOOM_REGION_XALIGN_PROP,
    03548                                  BONOBO_ARG_INT,
    03549                                  def,
    03550                                  "x-alignment policy for this region",
    03551                                  Bonobo_PROPERTY_READABLE |
    03552                                  Bonobo_PROPERTY_WRITEABLE);
    03553
    03554         bonobo_arg_release (def);
    03555         def = bonobo_arg_new (BONOBO_ARG_INT);
    03556         BONOBO_ARG_SET_INT (def, 0);
    03557
    03558         bonobo_property_bag_add (zoom_region->properties,
    03559                                  "y-alignment",
    03560                                  ZOOM_REGION_YALIGN_PROP,
    03561                                  BONOBO_ARG_INT,
    03562                                  def,
    03563                                  "y-alignment policy for this region",
    03564                                  Bonobo_PROPERTY_READABLE |
    03565                                  Bonobo_PROPERTY_WRITEABLE);
    03566         bonobo_arg_release (def);
    03567
    03568         bonobo_property_bag_add (zoom_region->properties,
    03569                                  "viewport",
    03570                                  ZOOM_REGION_VIEWPORT_PROP,
    03571                                  TC_GNOME_Magnifier_RectBounds,
    03572                                  NULL,
    03573                                  "viewport bounding box",
    03574                                  Bonobo_PROPERTY_READABLE |
    03575                                  Bonobo_PROPERTY_WRITEABLE);
    03576
    03577         def = bonobo_arg_new (BONOBO_ARG_INT);
    03578         BONOBO_ARG_SET_INT (def, 0);
    03579
    03580         bonobo_property_bag_add (zoom_region->properties,
    03581                                  "timing-iterations",
    03582                                  ZOOM_REGION_TIMING_TEST_PROP,
    03583                                  BONOBO_ARG_INT,
    03584                                  def,
    03585                                  "timing iterations",
    03586                                  Bonobo_PROPERTY_READABLE |
    03587                                  Bonobo_PROPERTY_WRITEABLE);
    03588         bonobo_arg_release (def);
    03589
    03590         def = bonobo_arg_new (BONOBO_ARG_BOOLEAN);
    03591         BONOBO_ARG_SET_BOOLEAN (def, FALSE);
    03592
    03593         bonobo_property_bag_add (zoom_region->properties,
    03594                                  "timing-output",
    03595                                  ZOOM_REGION_TIMING_OUTPUT_PROP,
    03596                                  BONOBO_ARG_BOOLEAN,
    03597                                  def,
    03598                                  "timing output",
    03599                                  Bonobo_PROPERTY_READABLE |
    03600                                  Bonobo_PROPERTY_WRITEABLE);
    03601
    03602         bonobo_arg_release (def);
    03603
    03604         def = bonobo_arg_new (BONOBO_ARG_INT);
    03605         BONOBO_ARG_SET_INT (def, 0);
    03606
    03607         bonobo_property_bag_add (zoom_region->properties,
    03608                                  "timing-pan-rate",
    03609                                  ZOOM_REGION_TIMING_PAN_RATE_PROP,
    03610                                  BONOBO_ARG_INT,
    03611                                  def,
    03612                                  "timing pan rate",
    03613                                  Bonobo_PROPERTY_READABLE |
    03614                                  Bonobo_PROPERTY_WRITEABLE);
    03615         bonobo_arg_release (def);
    03616
    03617         def = bonobo_arg_new (BONOBO_ARG_BOOLEAN);
    03618         BONOBO_ARG_SET_BOOLEAN (def, FALSE);
    03619
    03620         bonobo_property_bag_add (zoom_region->properties,
    03621                                  "exit-magnifier",
    03622                                  ZOOM_REGION_EXIT_MAGNIFIER,
    03623                                  BONOBO_ARG_BOOLEAN,
    03624                                  def,
    03625                                  "timing output",
    03626                                  Bonobo_PROPERTY_READABLE |
    03627                                  Bonobo_PROPERTY_WRITEABLE);
    03628
    03629         bonobo_arg_release (def);
    03630
    03631 }
    03632
    03633 static void
    03634 zoom_region_private_init (ZoomRegionPrivate *priv)
    03635 {
    03636         GdkRectangle rect = {0, 0, 0, 0};
    03637         GNOME_Magnifier_RectBounds rectbounds = {0, 0, 0, 0};
    03638         priv->parent = NULL;
    03639         priv->w = NULL;
    03640         priv->default_gc = NULL;
    03641         priv->paint_cursor_gc = NULL;
    03642         priv->crosswire_gc = NULL;
    03643         priv->q = NULL;
    03644         priv->scaled_pixbuf = NULL;
    03645         priv->source_pixbuf_cache = NULL;
    03646         priv->source_drawable = NULL;
    03647         priv->pixmap = NULL;
    03648         priv->cursor_backing_rect = rect;
    03649         priv->cursor_backing_pixels = NULL;
    03650         priv->gdk_interp_type = GDK_INTERP_NEAREST;
    03651         priv->expose_handler_id = 0;
    03652         priv->test = FALSE;
    03653         priv->last_cursor_pos.x = 0;
    03654         priv->last_cursor_pos.y = 0;
    03655         priv->last_drawn_crosswire_pos.x = 0;
    03656         priv->last_drawn_crosswire_pos.y = 0;
    03657         priv->exposed_bounds = rectbounds;
    03658         priv->source_area = rectbounds;
    03659         priv->update_pointer_id = 0;
    03660         priv->update_handler_id = 0;
    03661 }
    03662
    03663 static void
    03664 zoom_region_init (ZoomRegion *zoom_region)
    03665 {
    03666         DBG(g_message ("initializing region %p", zoom_region));
    03667
    03668         zoom_region_properties_init (zoom_region);
    03669         zoom_region->draw_cursor = TRUE;
    03670         zoom_region->smooth_scroll_policy =
    03671                 GNOME_Magnifier_ZoomRegion_SCROLL_SMOOTH;
    03672         zoom_region->color_blind_filter =
    03673                 GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_NO_FILTER;
    03674         zoom_region->contrast_r = 0.0;
    03675         zoom_region->contrast_g = 0.0;
    03676         zoom_region->contrast_b = 0.0;
    03677         zoom_region->bright_r = 0.0;
    03678         zoom_region->bright_g = 0.0;
    03679         zoom_region->bright_b = 0.0;
    03680         zoom_region->invert = FALSE;
    03681         zoom_region->cache_source = FALSE;
    03682         zoom_region->border_size_left = 0;
    03683         zoom_region->border_size_top = 0;
    03684         zoom_region->border_size_right = 0;
    03685         zoom_region->border_size_bottom = 0;
    03686         zoom_region->border_color = 0;
    03687         zoom_region->roi.x1 = 0;
    03688         zoom_region->roi.x1 = 0;
    03689         zoom_region->roi.x2 = 1;
    03690         zoom_region->roi.x2 = 1;
    03691         zoom_region->x_align_policy = GNOME_Magnifier_ZoomRegion_ALIGN_CENTER;
    03692         zoom_region->y_align_policy = GNOME_Magnifier_ZoomRegion_ALIGN_CENTER;
    03693         zoom_region->coalesce_func = _coalesce_update_rects;
    03694         zoom_region->poll_mouse = TRUE;
    03695         zoom_region->priv = g_malloc (sizeof (ZoomRegionPrivate));
    03696         zoom_region_private_init (zoom_region->priv);
    03697         bonobo_object_add_interface (BONOBO_OBJECT (zoom_region),
    03698                                      BONOBO_OBJECT (zoom_region->properties));
    03699         zoom_region->timing_output = FALSE;
    03700 #ifdef ZOOM_REGION_DEBUG
    03701         zoom_region->alive = TRUE;
    03702 #endif
    03703         zoom_region->priv->update_pointer_id =
    03704             g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
    03705                                 200,
    03706                                 zoom_region_update_pointer_timeout,
    03707                                 zoom_region,
    03708                                 NULL);
    03709 }
    03710
    03711 ZoomRegion *
    03712 zoom_region_new (void)
    03713 {
    03714         return g_object_new (zoom_region_get_type(), NULL);
    03715 }
    03716
    03717 /* this one really shuts down the object - called once only */
    03718 static void
    03719 zoom_region_finalize (GObject *region)
    03720 {
    03721         ZoomRegion *zoom_region = (ZoomRegion *) region;
    03722
    03723         DBG(g_message ("finalizing region %p", zoom_region));
    03724
    03725         if (zoom_region->priv && zoom_region->priv->q)
    03726         {
    03727                 g_list_free (zoom_region->priv->q);
    03728                 zoom_region->priv->q = NULL;
    03729         }
    03730         if (GTK_IS_WIDGET (zoom_region->priv->w))
    03731                 gtk_container_remove (GTK_CONTAINER (((Magnifier *) zoom_region->priv->parent)->priv->canvas), GTK_WIDGET (zoom_region->priv->w));
    03732         if (GTK_IS_WIDGET (zoom_region->priv->border))
    03733                 gtk_container_remove (GTK_CONTAINER (((Magnifier *) zoom_region->priv->parent)->priv->canvas), GTK_WIDGET (zoom_region->priv->border));
    03734         if (zoom_region->priv->source_pixbuf_cache)
    03735             g_object_unref (zoom_region->priv->source_pixbuf_cache);
    03736         if (zoom_region->priv->scaled_pixbuf)
    03737             g_object_unref (zoom_region->priv->scaled_pixbuf);
    03738         if (zoom_region->priv->pixmap)
    03739             g_object_unref (zoom_region->priv->pixmap);
    03740         zoom_region->priv->pixmap = NULL;
    03741         zoom_region->priv->parent = NULL;
    03742         if (zoom_region->priv->cursor_backing_pixels)
    03743             g_object_unref (zoom_region->priv->cursor_backing_pixels);
    03744         g_free (zoom_region->priv);
    03745         zoom_region->priv = NULL;
    03746 #ifdef ZOOM_REGION_DEBUG
    03747         zoom_region->alive = FALSE;
    03748 #endif
    03749         BONOBO_CALL_PARENT (G_OBJECT_CLASS, finalize, (region));
    03750 }
    03751
    03752 BONOBO_TYPE_FUNC_FULL (ZoomRegion,
    03753                        GNOME_Magnifier_ZoomRegion,
    03754                        BONOBO_TYPE_OBJECT,
    03755                        zoom_region);