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);

Generated on Wed Aug 13 20:09:18 2008 for gnome-mag by  doxygen 1.5.4