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

Generated on Mon Jul 10 19:11:40 2006 for gnome-mag by  doxygen 1.4.4