gnome-mag: zoom-region.c Source File

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