Main Page | Namespace List | Data Structures | Directories | File List | Namespace Members | Data Fields | Globals

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

Generated on Mon May 15 16:14:02 2006 for gnome-mag by  doxygen 1.4.4