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

Generated on Sun Jan 7 13:09:54 2007 for gnome-mag by  doxygen 1.4.7