zoom-region.c

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

Generated on Sun Aug 9 20:57:32 2009 for gnome-mag by  doxygen 1.5.4