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

Generated on Mon Jan 29 13:08:27 2007 for gnome-mag by  doxygen 1.4.7