zoom-region.c

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

Generated on Wed May 28 12:38:58 2008 for gnome-mag by  doxygen 1.5.5