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,
00922                                   &zoom_region->priv->cursor_backing_rect);
00923 }
00924 
00925 static void
00926 zoom_region_paint_cursor (ZoomRegion *zoom_region,
00927                           GdkRectangle *clip_rect)
00928 {
00929         GdkGCValues values;
00930         GdkRectangle rect, intersct;
00931         GdkRectangle fullscreen;
00932         Magnifier *magnifier = zoom_region->priv->parent;
00933         rect = zoom_region_cursor_rect (zoom_region);
00934 #ifdef ZOOM_REGION_DEBUG
00935         g_assert (zoom_region->alive);
00936 #endif
00937         if (clip_rect == NULL)
00938         {
00939                 fullscreen = zoom_region_rect_from_bounds (zoom_region,
00940                                                            &zoom_region->viewport);
00941                 clip_rect = &fullscreen;
00942         }
00943         /* save the unclipped cursor pos for 'undrawing' the crosswire, the clipped one is no good */
00944         zoom_region->priv->last_drawn_crosswire_pos.x = rect.x + magnifier->cursor_hotspot.x;
00945         zoom_region->priv->last_drawn_crosswire_pos.y = rect.y + magnifier->cursor_hotspot.y;
00946 
00947         if (gdk_rectangle_intersect (clip_rect, &rect, &intersct))
00948         {
00949                 int width = 0, height = 0;
00950                 
00951                 GdkDrawable *cursor = magnifier_get_cursor (magnifier);
00952                 if (!cursor)
00953                         return;
00954                 else if (!GDK_IS_DRAWABLE (cursor)) g_message ("cursor isn't DRAWABLE!");
00955                 zoom_region->priv->cursor_backing_rect = rect;
00956                 if (zoom_region->priv->cursor_backing_pixels) {
00957                         gdk_drawable_get_size (zoom_region->priv->cursor_backing_pixels,
00958                                                &width, &height);
00959                 }
00960                 if (rect.width != width || rect.height != height)
00961                 {
00962                         if (zoom_region->priv->cursor_backing_pixels) {
00963                                 g_object_unref (zoom_region->priv->cursor_backing_pixels);
00964                         }
00965                         zoom_region->priv->cursor_backing_pixels =
00966                                 gdk_pixmap_new (zoom_region->priv->w->window,
00967                                                 rect.width,
00968                                                 rect.height,
00969                                                 -1);
00970                 }
00971                 if (zoom_region->priv->w->window != NULL)
00972                 {
00973                         if (zoom_region->priv->default_gc == NULL) 
00974                                 zoom_region->priv->default_gc = gdk_gc_new(zoom_region->priv->w->window);
00975                         gdk_draw_drawable (zoom_region->priv->cursor_backing_pixels,
00976                                      zoom_region->priv->default_gc,
00977                                      zoom_region->priv->w->window,
00978                                      rect.x,
00979                                      rect.y,
00980                                      0, 0,
00981                                      rect.width,
00982                                      rect.height);
00983                 }
00984                 DEBUG_RECT ("painting", rect);
00985                 if (cursor && zoom_region->priv->w && GDK_IS_DRAWABLE (zoom_region->priv->w->window))
00986                 {
00987                     if (zoom_region->priv->paint_cursor_gc == NULL)
00988                                 zoom_region->priv->paint_cursor_gc = gdk_gc_new (zoom_region->priv->w->window);
00989 
00990                         gdk_gc_set_clip_rectangle (zoom_region->priv->paint_cursor_gc, clip_rect);
00991                         values.clip_x_origin = rect.x;
00992                         values.clip_y_origin = rect.y;
00993                         values.clip_mask = magnifier->priv->cursor_mask;
00994                         gdk_gc_set_values(zoom_region->priv->paint_cursor_gc, &values, GDK_GC_CLIP_X_ORIGIN |
00995                                           GDK_GC_CLIP_Y_ORIGIN  | GDK_GC_CLIP_MASK);
00996 
00997                         gdk_draw_rectangle (zoom_region->priv->w->window,
00998                                            zoom_region->priv->paint_cursor_gc,
00999                                            TRUE,
01000                                            rect.x, rect.y, rect.width, rect.height);
01001 
01002                         gdk_draw_drawable (zoom_region->priv->w->window,
01003                                            zoom_region->priv->paint_cursor_gc,
01004                                            cursor,
01005                                            0, 0,
01006                                            rect.x,
01007                                            rect.y,
01008                                            rect.width,
01009                                            rect.height);
01010                 }
01011         }
01012 }
01013 
01018 static void
01019 zoom_region_coalesce_updates (ZoomRegion *zoom_region)
01020 {
01021         /* TODO: lock the queue ? */
01022         GList *q;
01023         int lookahead_n = 4; /* 'distance' to look ahead in queue */
01024         int max_qlen = 50;
01025 
01026         if (zoom_region->priv && zoom_region->priv->q && g_list_length (zoom_region->priv->q) > max_qlen)
01027         {
01028                 g_list_free (zoom_region->priv->q);
01029                 zoom_region->priv->q = NULL; /* just discard and update everything */
01030                 /* CAUTION: this can be an expensive operation! */
01031                 zoom_region_queue_update (zoom_region, zoom_region_rect_from_bounds
01032                                           (zoom_region, &zoom_region->priv->source_area));
01033         }
01034         else 
01035 
01036         if (zoom_region->priv && zoom_region->priv->q && 
01037             (g_list_length (zoom_region->priv->q) > 1) && can_coalesce)
01038         {               
01039                 q = g_list_reverse (g_list_copy (zoom_region->priv->q));
01040                 if (q)
01041                 {
01042                         GList *coalesce_copy;
01043                         if (zoom_region->coalesce_func)
01044                         {
01045                                 GList *new;
01046                                 coalesce_copy = (*zoom_region->coalesce_func) (q, lookahead_n);
01047                                 new = g_list_reverse (coalesce_copy);
01048                                 g_list_free (zoom_region->priv->q);
01049                                 zoom_region->priv->q = new;
01050                         }
01051                         g_list_free (q);
01052                 }
01053         }
01054 }
01055 
01056 
01057 static void
01058 zoom_region_paint_border (ZoomRegion *zoom_region)
01059 {
01060         GdkColor color;
01061 
01062 #ifdef ZOOM_REGION_DEBUG
01063         g_assert (zoom_region->alive);
01064 #endif
01065         if ((zoom_region->border_size > 0) &&
01066             (zoom_region->priv->border->window)) {
01067                 color.red = (((zoom_region->border_color & 0xFF0000) >> 16) *
01068                              65535) / 255;
01069                 color.green = (((zoom_region->border_color & 0xFF00) >> 8) *
01070                                65535) / 255;
01071                 color.blue = ((zoom_region->border_color & 0xFF) * 65535) /
01072                         255;
01073 
01074 #ifdef DEBUG_BORDER
01075                 fprintf (stderr, "border color triple RGB=%d|%d|%d\n",
01076                          color.red, color.green, color.blue);
01077 #endif
01078 
01079                 gtk_widget_modify_bg (zoom_region->priv->border,
01080                                       GTK_STATE_NORMAL, &color);
01081         }
01082 }
01083 
01084 static void
01085 zoom_region_paint_pixmap (ZoomRegion *zoom_region,
01086                           GdkRectangle *area)
01087 {
01088 #ifdef ZOOM_REGION_DEBUG
01089         g_assert (zoom_region->alive);
01090 #endif
01091         g_assert (zoom_region->priv);
01092         g_assert (zoom_region->priv->w);
01093 
01094         if (!GDK_IS_DRAWABLE (zoom_region->priv->w->window)) return;
01095         if (zoom_region->priv->default_gc == NULL) 
01096                 zoom_region->priv->default_gc = gdk_gc_new (zoom_region->priv->w->window);
01097 
01098         if (zoom_region->priv->pixmap && GDK_IS_DRAWABLE (zoom_region->priv->w->window))
01099         {
01100                 gdk_draw_drawable (zoom_region->priv->w->window,
01101                                    zoom_region->priv->default_gc,
01102                                    zoom_region->priv->pixmap,
01103                                    area->x + zoom_region->priv->exposed_bounds.x1 - zoom_region->priv->source_area.x1 * zoom_region->xscale,
01104                                    area->y + zoom_region->priv->exposed_bounds.y1 - zoom_region->priv->source_area.y1 * zoom_region->yscale,
01105                                    area->x,
01106                                    area->y,
01107                                    area->width,
01108                                    area->height);
01109         }
01110 }
01111 
01115 static void
01116 zoom_region_paint (ZoomRegion *zoom_region,
01117                    GdkRectangle *area)
01118 {
01119         GdkRectangle paint_area;
01120 
01121 #ifdef ZOOM_REGION_DEBUG
01122         g_assert (zoom_region->alive);
01123 #endif
01124         DEBUG_RECT ("painting (clipped)", *area);
01125         paint_area = zoom_region_clip_to_window (zoom_region, *area);
01126         zoom_region_paint_pixmap (zoom_region, &paint_area);
01127         zoom_region_paint_cursor (zoom_region, &paint_area);
01128         zoom_region_paint_crosswire_cursor (zoom_region, &paint_area);
01129 }
01130 
01131 static ZoomRegionPixmapCreationError
01132 zoom_region_create_pixmap (ZoomRegion *zoom_region)
01133 {
01134 #ifdef ZOOM_REGION_DEBUG
01135         g_assert (zoom_region->alive);
01136 #endif
01137         if (zoom_region->priv->w && GDK_IS_DRAWABLE (zoom_region->priv->w->window))
01138         {
01139                 long width = (zoom_region->priv->source_area.x2 -
01140                               zoom_region->priv->source_area.x1) * zoom_region->xscale;
01141                 long height = (zoom_region->priv->source_area.y2 -
01142                                zoom_region->priv->source_area.y1) * zoom_region->yscale;
01143                 zoom_region->priv->pixmap =
01144                         gdk_pixmap_new (
01145                                 zoom_region->priv->w->window,
01146                                 width,
01147                                 height,
01148                                 gdk_drawable_get_depth (
01149                                         zoom_region->priv->w->window));
01150 
01151                 if (magnifier_error_check ()) {
01152                         zoom_region->priv->pixmap = NULL;
01153                         return ZOOM_REGION_ERROR_TOO_BIG;
01154                 }
01155 
01156                 DEBUG_RECT("viewport", zoom_region_source_rect_from_view_bounds
01157                            (zoom_region, &zoom_region->viewport));
01158                 DEBUG_RECT("source", zoom_region_rect_from_bounds
01159                            (zoom_region, &((Magnifier*)zoom_region->priv->parent)->source_bounds));
01160 
01161                 zoom_region_update (zoom_region,
01162 /*                                  zoom_region_source_rect_from_view_bounds (
01163                                     zoom_region,
01164                                     &zoom_region->viewport));
01165 */
01166                                     zoom_region_rect_from_bounds 
01167                                     (zoom_region, 
01168                                      &((Magnifier *)zoom_region->priv->parent)->source_bounds));
01169                 return ZOOM_REGION_ERROR_NONE;
01170         }
01171         
01172         return ZOOM_REGION_ERROR_NO_TARGET_DRAWABLE;
01173 }
01174 
01175 static void
01176 zoom_region_expose_handler (GtkWindow * w,
01177                             GdkEventExpose *event,
01178                             gpointer data)
01179 {
01180         ZoomRegion *zoom_region = data;
01181         DEBUG_RECT ("expose", event->area);
01182 
01183 #ifdef ZOOM_REGION_DEBUG
01184         g_assert (zoom_region->alive);
01185 #endif
01186         if (zoom_region->priv->pixmap == NULL)
01187         {
01188                 ZoomRegionPixmapCreationError ret; 
01189                 /* TODO: scale down if this fails here */
01190                 while ((ret = zoom_region_create_pixmap (zoom_region)) ==
01191                     ZOOM_REGION_ERROR_TOO_BIG) {
01192                         zoom_region->xscale -= 1.0;
01193                         zoom_region->yscale -= 1.0;
01194                         zoom_region->priv->pixmap = NULL;
01195                         g_warning ("Scale factor too big to fit in memory; shrinking.");
01196                 }
01197                 if (ret == ZOOM_REGION_ERROR_NO_TARGET_DRAWABLE) 
01198                     g_warning ("create-pixmap: no target drawable");
01199         }
01200         zoom_region_paint (zoom_region, &event->area);
01201 }
01202 
01203 static void
01204 zoom_region_update_cursor (ZoomRegion *zoom_region, int dx, int dy,
01205                            GdkRectangle *clip_rect)
01206 {
01207 #ifdef ZOOM_REGION_DEBUG
01208         g_assert (zoom_region->alive);
01209 #endif
01210         zoom_region_unpaint_crosswire_cursor (zoom_region, clip_rect);
01211         zoom_region_unpaint_cursor (zoom_region, clip_rect);
01212         zoom_region->priv->cursor_backing_rect.x += dx;
01213         zoom_region->priv->cursor_backing_rect.y += dy;
01214         zoom_region->priv->last_drawn_crosswire_pos.x += dx;
01215         zoom_region->priv->last_drawn_crosswire_pos.y += dy;
01216         zoom_region_paint_cursor (zoom_region, clip_rect);
01217         zoom_region_paint_crosswire_cursor (zoom_region, clip_rect);
01218         if (GTK_IS_WIDGET (zoom_region->priv->w) &&
01219             GDK_IS_WINDOW (zoom_region->priv->w->window))
01220                 gdk_display_sync (gdk_drawable_get_display (
01221                                           zoom_region->priv->w->window));
01222 }
01223 
01224 static gboolean
01225 zoom_region_calculate_scroll_rects (ZoomRegion *zoom_region,
01226                                     int dx, int dy,
01227                                     GdkRectangle *scroll_rect,
01228                                     GdkRectangle *expose_rect_h,
01229                                     GdkRectangle *expose_rect_v)
01230 {
01231         GdkWindow *window = NULL;
01232         GdkRectangle rect = {0, 0, 0, 0};
01233         gboolean retval = TRUE;
01234 
01235 #ifdef ZOOM_REGION_DEBUG
01236         g_assert (zoom_region->alive);
01237 #endif
01238         rect.x = 0;
01239         rect.y = 0;
01240         if (zoom_region && zoom_region->priv->w &&
01241             zoom_region->priv->w->window)
01242                 window = zoom_region->priv->w->window;
01243         else
01244                 retval = FALSE;
01245         if (!window)
01246                 retval = FALSE;
01247 
01248         if (window != NULL)
01249           gdk_drawable_get_size (GDK_DRAWABLE (window),
01250                                  &rect.width,
01251                                  &rect.height);
01252 
01253         if ((ABS (dx) >= rect.width) || (ABS (dy) >= rect.height)) {
01254                 *scroll_rect = rect;
01255                 DBG(fprintf (stderr, "deltas too big to scroll\n"));
01256                 retval = FALSE;
01257         }
01258         else {
01259             scroll_rect->x = MAX (0, dx);
01260             scroll_rect->y = MAX (0, dy);
01261             scroll_rect->width = MIN (rect.width + dx, rect.width - dx);
01262             scroll_rect->height = MIN (rect.height + dy, rect.height - dy);
01263         }
01264 
01265         expose_rect_h->x = 0;
01266         expose_rect_h->y = (scroll_rect->y == 0) ? scroll_rect->height : 0;
01267         expose_rect_h->width = rect.width;
01268         expose_rect_h->height = rect.height - scroll_rect->height;
01269 
01270         expose_rect_v->x = (scroll_rect->x == 0) ? scroll_rect->width : 0;
01271         expose_rect_v->y = scroll_rect->y;
01272         expose_rect_v->width = rect.width - scroll_rect->width;
01273         expose_rect_v->height = scroll_rect->height;
01274 
01275         return retval;
01276 }
01277 
01278 static void
01279 zoom_region_scroll_fast (ZoomRegion *zoom_region, int dx, int dy,
01280                          GdkRectangle *scroll_rect,
01281                          GdkRectangle *expose_rect_h,
01282                          GdkRectangle *expose_rect_v)
01283 {
01284         GdkWindow *window;
01285 
01286 #ifdef ZOOM_REGION_DEBUG
01287         g_assert (zoom_region->alive);
01288 #endif
01289         if (zoom_region->priv->w && zoom_region->priv->w->window)
01290                 window = zoom_region->priv->w->window;
01291         else {
01292                 processing_updates = FALSE;
01293                 return;
01294         }
01295         zoom_region_unpaint_crosswire_cursor (zoom_region, scroll_rect);
01296         zoom_region_unpaint_cursor (zoom_region, scroll_rect);
01297         gdk_window_scroll (window, dx, dy);
01298         zoom_region_paint_cursor (zoom_region, scroll_rect);
01299         zoom_region_paint_crosswire_cursor (zoom_region, scroll_rect);
01300         gdk_window_process_updates (window, FALSE);
01301         /* sync reduces cursor flicker, but slows things down */
01302         if (zoom_region->smooth_scroll_policy >
01303             GNOME_Magnifier_ZoomRegion_SCROLL_FASTEST)
01304                 gdk_display_sync (gdk_drawable_get_display (window)); 
01305 }
01306 
01307 static void
01308 zoom_region_scroll_smooth (ZoomRegion *zoom_region, int dx, int dy,
01309                            GdkRectangle *scroll_rect,
01310                            GdkRectangle *expose_rect_h,
01311                            GdkRectangle *expose_rect_v)
01312 {
01313         GdkWindow *window = NULL;
01314         GdkRectangle window_rect;
01315 
01316 #ifdef ZOOM_REGION_DEBUG
01317         g_assert (zoom_region->alive);
01318 #endif
01319         if (zoom_region->priv->w && GDK_IS_DRAWABLE (zoom_region->priv->w->window))
01320                 window = zoom_region->priv->w->window;
01321         else
01322                 return;
01323         window_rect.x = 0;
01324         window_rect.y = 0;
01325         gdk_drawable_get_size (GDK_DRAWABLE (window),
01326                                &window_rect.width, &window_rect.height);
01327         gdk_window_begin_paint_rect (window, &window_rect);
01328         gdk_window_invalidate_rect (window, &window_rect, FALSE);
01329         gdk_window_process_updates (window, FALSE); 
01330         gdk_window_end_paint (window);
01331 }
01332 
01333 static void
01334 zoom_region_scroll (ZoomRegion *zoom_region, int dx, int dy)
01335 {
01336         GdkRectangle scroll_rect, expose_rect_h, expose_rect_v;
01337         gboolean can_scroll;
01338 
01339 #ifdef ZOOM_REGION_DEBUG
01340         g_assert (zoom_region->alive);
01341 #endif
01342         if (timing_test) {
01343                 mag_timing.num_line_samples++;
01344                 mag_timing.dx = abs(dx);
01345                 mag_timing.dy = abs(dy);
01346                 mag_timing.dx_total += mag_timing.dx;
01347                 mag_timing.dy_total += mag_timing.dy;
01348                 if (zoom_region->timing_output) {
01349                         fprintf(stderr, "  Panning Increment (x)    = %d (avg. %f) lines/frame\n",
01350                                 mag_timing.dx, (float)mag_timing.dx_total / (float)mag_timing.num_line_samples);
01351                         fprintf(stderr, "  Panning Increment (y)    = %d (avg. %f) lines/frame\n",
01352                                 mag_timing.dy, (float)mag_timing.dy_total / (float)mag_timing.num_line_samples);
01353                 }
01354         }
01355 
01356     /*
01357      * Currently processing a screen update.  This flag used to disallow
01358      * other updates to occur until this one finishes
01359      */
01360     processing_updates = TRUE;
01361 
01362         can_scroll = zoom_region_calculate_scroll_rects (zoom_region, dx, dy,
01363                                                          &scroll_rect,
01364                                                          &expose_rect_h,
01365                                                          &expose_rect_v);
01366         
01367         if (can_scroll) {
01368                 zoom_region_update_pixmap (zoom_region, zoom_region_source_rect_from_view_rect (zoom_region, expose_rect_h), NULL);
01369                 zoom_region_update_pixmap (zoom_region, zoom_region_source_rect_from_view_rect (zoom_region, expose_rect_v), NULL);
01370 
01371                 if (zoom_region->smooth_scroll_policy > GNOME_Magnifier_ZoomRegion_SCROLL_FAST) {
01372                         zoom_region_scroll_smooth (zoom_region, dx, dy,
01373                                                    &scroll_rect,
01374                                                    &expose_rect_h,
01375                                                    &expose_rect_v);
01376                 } else {
01377                         zoom_region_scroll_fast (zoom_region, dx, dy,
01378                                                  &scroll_rect,
01379                                                  &expose_rect_h,
01380                                                  &expose_rect_v);
01381                 }
01382         } else {
01383                 zoom_region_queue_update (zoom_region, zoom_region_source_rect_from_view_rect (zoom_region, scroll_rect));
01384         }
01385 }
01386 
01387 static void
01388 zoom_region_recompute_exposed_bounds (ZoomRegion *zoom_region)
01389 {
01390         zoom_region->priv->exposed_bounds.x2 = zoom_region->priv->exposed_bounds.x1
01391                 + (zoom_region->viewport.x2 - zoom_region->viewport.x1);
01392         zoom_region->priv->exposed_bounds.y2 = zoom_region->priv->exposed_bounds.y1
01393                 + (zoom_region->viewport.y2 - zoom_region->viewport.y1);
01394 }
01395 
01396 static void
01397 zoom_region_set_cursor_pos (ZoomRegion *zoom_region, int x, int y)
01398 {
01399         if (zoom_region->priv)
01400         {
01401                 zoom_region->priv->last_cursor_pos.x = x;
01402                 zoom_region->priv->last_cursor_pos.y = y;
01403         }
01404 }
01405 
01406 static gboolean
01407 zoom_region_update_pointer (ZoomRegion *zoom_region, gboolean draw_cursor)
01408 {
01409         Magnifier *magnifier;
01410         gint mouse_x_return, mouse_y_return;
01411         guint mask_return;
01412 
01413 #ifdef ZOOM_REGION_DEBUG
01414         g_assert (zoom_region->alive);
01415 #endif
01416         if (!zoom_region->priv || !zoom_region->priv->parent 
01417             || !zoom_region->poll_mouse)
01418               return FALSE; 
01419 
01420         magnifier = zoom_region->priv->parent;
01421 
01422         /* TODO: there's really no reason we should be using magnifier->priv->root here */
01423         if (magnifier && magnifier->priv && magnifier_get_root (magnifier))
01424         {
01425                 gdk_window_get_pointer (
01426                         magnifier_get_root (magnifier),
01427                         &mouse_x_return,
01428                         &mouse_y_return,
01429                         &mask_return);
01430                 
01431                 if (zoom_region->priv->last_cursor_pos.x != mouse_x_return
01432                     || zoom_region->priv->last_cursor_pos.y != mouse_y_return)
01433                 {
01434                         zoom_region_set_cursor_pos (zoom_region,
01435                                                     mouse_x_return, mouse_y_return);
01436                         if (draw_cursor)
01437                         {
01438                                 GdkRectangle paint_area, *clip = NULL;
01439 
01440                                 if (GTK_IS_WIDGET (zoom_region->priv->w) && 
01441                                     GDK_IS_DRAWABLE (zoom_region->priv->w->window))
01442                                 {
01443                                         gdk_drawable_get_size (GDK_DRAWABLE (zoom_region->priv->w->window), &paint_area.width, &paint_area.height);
01444                                         paint_area.x = 0;
01445                                         paint_area.y = 0;
01446                                         clip = &paint_area;
01447                                         paint_area = 
01448                                                 zoom_region_clip_to_source (
01449                                                         zoom_region,
01450                                                         paint_area);
01451                                 }
01452                                 zoom_region_update_cursor (zoom_region, 0, 0,
01453                                                            clip);
01454                         }
01455                         return TRUE;
01456                 }
01457         }       
01458         return FALSE;
01459 }
01460 
01461 static int
01462 zoom_region_update_pointer_idle (gpointer data)
01463 {
01464         ZoomRegion *zoom_region = (ZoomRegion *) data;
01465 
01466         if (zoom_region_update_pointer (zoom_region, TRUE))
01467                 return TRUE;
01468         else {
01469                 if (zoom_region->priv)
01470                         zoom_region->priv->update_pointer_id =
01471                             g_timeout_add_full (G_PRIORITY_DEFAULT,
01472                                                 100,
01473                                                 zoom_region_update_pointer_timeout,
01474                                                 zoom_region,
01475                                                 NULL);
01476                 return FALSE;
01477         }
01478 }
01479 
01480 static int
01481 zoom_region_update_pointer_timeout (gpointer data)
01482 {
01483         ZoomRegion *zoom_region = data;
01484 
01485         if (zoom_region->priv && zoom_region_update_pointer (zoom_region, TRUE)) {
01486             zoom_region->priv->update_pointer_id =
01487                 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
01488                                  zoom_region_update_pointer_idle,
01489                                  data,
01490                                  NULL);
01491                 return FALSE;
01492         } else 
01493                 return TRUE;
01494 }
01495 
01496 static void
01497 zoom_region_moveto (ZoomRegion *zoom_region,
01498                     const long x, const long y)
01499 {
01500         long dx = x * zoom_region->xscale - zoom_region->priv->exposed_bounds.x1;
01501         long dy = y * zoom_region->yscale - zoom_region->priv->exposed_bounds.y1;
01502 #ifdef ZOOM_REGION_DEBUG
01503         g_assert (zoom_region->alive);
01504 #endif
01505 /* fprintf (stderr, "moveto %ld %ld\n", x, y); */
01506 
01507         mag_timing.dx = 0;
01508         mag_timing.dy = 0;
01509 
01510         if ((dx != 0) || (dy != 0)) {
01511                 zoom_region_update_pointer (zoom_region, FALSE);
01512                 zoom_region->priv->exposed_bounds.x1 = x * zoom_region->xscale;
01513                 zoom_region->priv->exposed_bounds.y1 = y * zoom_region->yscale;
01514                 zoom_region_recompute_exposed_bounds (zoom_region);
01515                 zoom_region_scroll (zoom_region,
01516                                     -dx, -dy);
01517         }
01518 }
01519 
01520 /*
01521  * Process that must be made in-line in the current pixbuf.
01522  */
01523 static void
01524 zoom_region_process_pixbuf (ZoomRegion *zoom_region, GdkPixbuf *pixbuf)
01525 {
01526         int rowstride = gdk_pixbuf_get_rowstride (pixbuf);
01527         int i, j, t;
01528         int w = gdk_pixbuf_get_width (pixbuf);
01529         int h = gdk_pixbuf_get_height (pixbuf);
01530         int n_channels = gdk_pixbuf_get_n_channels (pixbuf);
01531         guchar *pixels = gdk_pixbuf_get_pixels (pixbuf);
01532         guchar *pixels_row;
01533 #ifdef HAVE_COLORBLIND
01534         COLORBLIND_RUNTIME *cbr;
01535         COLORBLIND_XCOLOR *color;
01536 #endif /* HAVE_COLORBLIND */
01537 
01538         gboolean manipulate_contrast = FALSE;
01539         gboolean manipulate_brightness = FALSE;
01540         gboolean color_blind_filter = FALSE;
01541 
01542         if (zoom_region->contrast_r != 0 || zoom_region->contrast_g != 0 ||
01543             zoom_region->contrast_b != 0) {
01544                 manipulate_contrast = TRUE;
01545         }
01546 
01547         if (zoom_region->bright_r != 0 || zoom_region->bright_g != 0 ||
01548             zoom_region->bright_b != 0) {
01549                 manipulate_brightness = TRUE;
01550         }
01551 
01552 #ifdef HAVE_COLORBLIND
01553         if (zoom_region->color_blind_filter !=
01554             GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_NO_FILTER) {
01555                 color_blind_filter = TRUE;
01556                 cbr = colorblind_create ();
01557                 color = malloc (sizeof (COLORBLIND_XCOLOR));
01558                 switch (zoom_region->color_blind_filter) {
01559                 case GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_NO_FILTER:
01560                         break; /* This entry is only to avoid a warning */
01561                 case GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_SELECTIVE_SATURATE_RED:
01562                         colorblind_set_filter_type (cbr, colorblind_filter_t_selective_saturate_red);
01563                         break;
01564                 case GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_SELECTIVE_SATURATE_GREEN:
01565                         colorblind_set_filter_type (cbr, colorblind_filter_t_selective_saturate_green);
01566                         break;
01567                 case GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_SELECTIVE_SATURATE_BLUE:
01568                         colorblind_set_filter_type (cbr, colorblind_filter_t_selective_saturate_blue);
01569                         break;
01570                 case GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_SELECTIVE_DESSATURATE_RED:
01571                         colorblind_set_filter_type (cbr, colorblind_filter_t_selective_dessaturate_red);
01572                         break;
01573                 case GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_SELECTIVE_DESSATURATE_GREEN:
01574                         colorblind_set_filter_type (cbr, colorblind_filter_t_selective_dessaturate_green);
01575                         break;
01576                 case GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_SELECTIVE_DESSATURATE_BLUE:
01577                         colorblind_set_filter_type (cbr, colorblind_filter_t_selective_dessaturate_blue);
01578                         break;
01579                 case GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_HUE_SHIFT_POSITIVE:
01580                         colorblind_set_filter_type (cbr, colorblind_filter_t_hue_shift_positive);
01581                         break;
01582                 case GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_HUE_SHIFT_NEGATIVE:
01583                         colorblind_set_filter_type (cbr, colorblind_filter_t_hue_shift_negative);
01584                         break;
01585                 case GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_SELECTIVE_SATURATE:
01586                         colorblind_set_filter_type (cbr, colorblind_filter_t_selective_saturate);
01587                         break;
01588                 case GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_SELECTIVE_DESSATURATE:
01589                         colorblind_set_filter_type (cbr, colorblind_filter_t_selective_dessaturate);
01590                         break;
01591                 case GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_MONOCHRONE_OTHERS:
01592                         colorblind_set_filter_type (cbr, colorblind_filter_t_monochrome_others);
01593                         break;
01594                 }
01595         }
01596 #endif /* HAVE_COLORBLIND */
01597 
01598         if (!manipulate_contrast && !zoom_region->invert &&
01599             !manipulate_brightness && !color_blind_filter)
01600                 return;
01601 
01602 #define CLAMP_UCHAR(v) (t = (v), CLAMP (t, 0, 255))
01603 #define CLAMP_LOW_MID(v) (t = (v), CLAMP (t, 0, 127))
01604 #define CLAMP_MID_HIGH(v) (t = (v), CLAMP (t, 127, 255))
01605 
01606         for (j = 0; j < h; ++j) {
01607                 pixels_row = pixels;
01608                 for (i = 0; i < w; ++i) {
01609                         if (manipulate_contrast) {
01610                                 /* Set the RED contrast */
01611                                 if (pixels_row[0] <= 127)
01612                                         pixels_row[0] = CLAMP_LOW_MID (pixels_row[0] - zoom_region->contrast_r * 127);
01613                                 else
01614                                         pixels_row[0] = CLAMP_MID_HIGH (pixels_row[0] + zoom_region->contrast_r * 127);
01615 
01616                                 /* Set the GREEN contrast */
01617                                 if (pixels_row[1] <= 127)
01618                                         pixels_row[1] = CLAMP_LOW_MID (pixels_row[1] - zoom_region->contrast_g * 127);
01619                                 else
01620                                         pixels_row[1] = CLAMP_MID_HIGH (pixels_row[1] + zoom_region->contrast_g * 127);
01621 
01622                                 /* Set the BLUE contrast */
01623                                 if (pixels_row[2] <= 127)
01624                                         pixels_row[2] = CLAMP_LOW_MID (pixels_row[2] - zoom_region->contrast_b * 127);
01625                                 else
01626                                         pixels_row[2] = CLAMP_MID_HIGH (pixels_row[2] + zoom_region->contrast_b * 127);
01627                         }
01628 
01629                         if (manipulate_brightness) {
01630                                 /* Set the RED brightness */
01631                                 pixels_row[0] = CLAMP_UCHAR (pixels_row[0] + zoom_region->bright_r * 255);
01632                                 
01633                                 /* Set the GREEN brightness */
01634                                 pixels_row[1] = CLAMP_UCHAR (pixels_row[1] + zoom_region->bright_g * 255);
01635 
01636                                 /* Set the BLUE brightness */
01637                                 pixels_row[2] = CLAMP_UCHAR (pixels_row[2] + zoom_region->bright_b * 255);
01638                         }
01639 
01640                         if (zoom_region->invert) {
01641                                 pixels_row[0] = ~(pixels_row[0]);
01642                                 pixels_row[1] = ~(pixels_row[1]);
01643                                 pixels_row[2] = ~(pixels_row[2]);
01644                         }
01645 
01646 #ifdef HAVE_COLORBLIND
01647                         if (color_blind_filter) {
01648                                 color->red   = pixels_row[0];
01649                                 color->green = pixels_row[1];
01650                                 color->blue  = pixels_row[2];
01651                                 if (colorblind_filter (cbr, color)) {
01652                                         pixels_row[0] = color->red;
01653                                         pixels_row[1] = color->green;
01654                                         pixels_row[2] = color->blue;
01655                                 }
01656                         }
01657 #endif /* HAVE_COLORBLIND */
01658                         
01659                         pixels_row += n_channels;
01660                 }
01661                 pixels += rowstride;
01662         }
01663 }
01664 
01665 static void
01666 zoom_region_post_process_pixbuf (ZoomRegion *zoom_region,
01667                                  GdkPixbuf *subimage,
01668                                  GdkPixbuf *scaled_image)
01669 {
01670         /* nothing yet */
01680 }
01681 
01682 static GdkPixbuf *
01683 zoom_region_get_source_subwindow (ZoomRegion *zoom_region,
01684                                   const GdkRectangle bounds)
01685 {
01686         int i, j, width, height;
01687         Magnifier *magnifier = zoom_region->priv->parent;
01688         GdkPixbuf *subimage = NULL;
01689 
01690 #ifdef ZOOM_REGION_DEBUG
01691         g_assert (zoom_region->alive);
01692 #endif
01693         width = gdk_screen_get_width (
01694                 gdk_display_get_screen (magnifier->source_display,
01695                                         magnifier->source_screen_num));
01696         height = gdk_screen_get_height (
01697                 gdk_display_get_screen (magnifier->source_display,
01698                                         magnifier->source_screen_num));
01699 
01700         if ((bounds.width <= 0) || (bounds.height <= 0))
01701         {
01702                 return NULL;
01703         }
01704         
01705         if (!zoom_region->priv->source_drawable)
01706         {
01707                 /* TESTING ONLY */
01708                 if (zoom_region->priv->test) {
01709                         GdkImage *test_image = NULL;
01710 
01711                         test_image = gdk_image_new (GDK_IMAGE_FASTEST,
01712                                                     gdk_visual_get_system (),
01713                                                     width,
01714                                                     height);
01715 
01716                         for (i = 0; i < width; ++i)
01717                                 for (j = 0; j < height; ++j)
01718                                         gdk_image_put_pixel (test_image, i, j, i*j);
01719 
01720                         zoom_region->priv->source_drawable = gdk_pixmap_new (zoom_region->priv->w->window, width, height, -1);
01721 
01722                         if (zoom_region->priv->default_gc == NULL)
01723                                 zoom_region->priv->default_gc = gdk_gc_new(zoom_region->priv->w->window);
01724 
01725                         gdk_draw_image (zoom_region->priv->source_drawable,
01726                                         zoom_region->priv->default_gc,
01727                                         test_image,
01728                                         0, 0,
01729                                         0, 0,
01730                                         width, height);
01731                 }
01732                 else
01733                 {
01734                         if (magnifier->priv->source_drawable) {
01735                                 zoom_region->priv->source_drawable =
01736                                         magnifier->priv->source_drawable;
01737                         } else
01738                                 zoom_region->priv->source_drawable = gdk_screen_get_root_window (gdk_display_get_screen (magnifier->source_display, magnifier->source_screen_num));
01739                 }
01740                 if (zoom_region->cache_source)
01741                 {
01742                         zoom_region->priv->source_pixbuf_cache =
01743                                 gdk_pixbuf_new (GDK_COLORSPACE_RGB,
01744                                                 FALSE,
01745                                                 8, /* FIXME: not always 8? */
01746                                                 width, height);
01747                 }
01748         }
01749         DEBUG_RECT ("getting subimage from ", bounds);
01750 
01751         subimage = gdk_pixbuf_get_from_drawable (NULL, zoom_region->priv->source_drawable,
01752                                                  gdk_colormap_get_system (),
01753                                                  bounds.x,
01754                                                  bounds.y,
01755                                                  0,
01756                                                  0,
01757                                                  bounds.width,
01758                                                  bounds.height);
01759 
01760         /* TODO: blank the region overlapped by the target display if source == target */
01761         
01762         if (!subimage)
01763                 _debug_announce_rect ("update of invalid subregion!\n", bounds);
01764 
01765         /* if this zoom-region keeps a cache, do a diff to see if update is necessary */
01766         if (zoom_region->cache_source && subimage) {
01767                 GdkPixbuf *cache_subpixbuf =
01768                         gdk_pixbuf_new_subpixbuf (zoom_region->priv->source_pixbuf_cache,
01769                                                   bounds.x, bounds.y, bounds.width, bounds.height);
01770                 if (_diff_pixbufs (subimage, cache_subpixbuf)) {
01771                         gdk_pixbuf_copy_area (subimage, 0, 0, bounds.width, bounds.height,
01772                                               zoom_region->priv->source_pixbuf_cache,
01773                                               bounds.x, bounds.y);
01774                 }
01775                 else
01776                 {
01777                         if (subimage)
01778                                 g_object_unref (subimage);
01779                         subimage = NULL;
01780                 }
01781                 g_object_unref (cache_subpixbuf);
01782         }
01783         return subimage;
01784 }
01785 
01786 static GdkRectangle
01787 zoom_region_update_pixmap (ZoomRegion *zoom_region,
01788                            const GdkRectangle update_rect,
01789                            GdkRectangle *p_rect)
01790 {
01791         GdkPixbuf *subimage;
01792         GdkRectangle source_rect;
01793 
01794 #ifdef ZOOM_REGION_DEBUG
01795         g_assert (zoom_region->alive);
01796 #endif
01797         DEBUG_RECT ("unclipped update rect", update_rect);
01798         source_rect = zoom_region_clip_to_source (zoom_region, update_rect);
01799         DEBUG_RECT ("clipped to source", source_rect);
01800         source_rect = zoom_region_clip_to_exposed_target (zoom_region, source_rect);
01801         DEBUG_RECT ("update rect clipped to exposed target", source_rect); 
01802 
01803         subimage = zoom_region_get_source_subwindow (zoom_region, source_rect);
01804 
01805         if (subimage)
01806         {
01807                 GdkRectangle paint_rect;
01808                 g_timer_start (mag_timing.scale);
01809                 DEBUG_RECT ("source rect", source_rect);
01810                 paint_rect = zoom_region_view_rect_from_source_rect (zoom_region, source_rect);
01811                 if (p_rect) {
01812                         *p_rect = paint_rect;
01813                 }
01814                 /* paint_rect = zoom_region_clip_to_scaled_pixmap (zoom_region, paint_rect); */
01815                 DEBUG_RECT ("paint rect", paint_rect);
01816 
01817                 zoom_region_process_pixbuf (zoom_region, subimage);
01818 
01823                 gdk_pixbuf_scale (subimage,
01824                                   zoom_region->priv->scaled_pixbuf,
01825                                   0,
01826                                   0,
01827                                   paint_rect.width,
01828                                   paint_rect.height,
01829                                   0,
01830                                   0,
01831                                   zoom_region->xscale,
01832                                   zoom_region->yscale,
01833                                   zoom_region->priv->gdk_interp_type);
01834 
01835                 zoom_region_post_process_pixbuf (zoom_region, subimage,
01836                                                  zoom_region->priv->scaled_pixbuf);
01837                 if (zoom_region->priv->default_gc == NULL)
01838                         zoom_region->priv->default_gc = gdk_gc_new(zoom_region->priv->w->window);
01839 
01840 #ifndef USE_GDK_PIXBUF_RENDER_TO_DRAWABLE 
01841                 if (GDK_IS_DRAWABLE (zoom_region->priv->pixmap))
01842                     gdk_draw_pixbuf (zoom_region->priv->pixmap,
01843                                      zoom_region->priv->default_gc,
01844                                      zoom_region->priv->scaled_pixbuf,
01845                                      0,
01846                                      0,
01847                                      paint_rect.x + zoom_region->priv->exposed_bounds.x1 - zoom_region->priv->source_area.x1 * zoom_region->xscale,
01848                                      paint_rect.y + zoom_region->priv->exposed_bounds.y1 - zoom_region->priv->source_area.y1 * zoom_region->yscale,
01849                                      paint_rect.width,
01850                                      paint_rect.height,
01851                                      GDK_RGB_DITHER_NONE,
01852                                      0,
01853                                      0);
01854                 else
01855                     g_warning ("updating non-drawable pixmap: region %p", zoom_region);
01856 #else
01857                 gdk_pixbuf_render_to_drawable (zoom_region->priv->scaled_pixbuf,
01858                                                zoom_region->priv->pixmap,
01859                                                zoom_region->priv->default_gc,
01860                                                0,
01861                                                0,
01862                                                paint_rect.x + zoom_region->priv->exposed_bounds.x1,
01863                                                paint_rect.y + zoom_region->priv->exposed_bounds.y1,
01864                                                paint_rect.width,
01865                                                paint_rect.height,
01866                                                GDK_RGB_DITHER_NONE,
01867                                                0,
01868                                                0);
01869 #endif
01870                 if (magnifier_error_check ())
01871                         g_warning ("Could not render scaled image to drawable; out of memory!\n");
01872                 g_object_unref (subimage);
01873 
01874                 g_timer_stop (mag_timing.scale);
01875         }
01876         return source_rect;
01877 }
01878 
01885 static void
01886 zoom_region_update (ZoomRegion *zoom_region,
01887                     const GdkRectangle update_rect)
01888 {
01889         GdkRectangle paint_rect = {0, 0, 0, 0};
01890         if (zoom_region->priv->w && zoom_region->priv->w->window) {
01891                 GdkRectangle source_rect = zoom_region_update_pixmap (zoom_region, update_rect, &paint_rect);
01892                 if (paint_rect.x != 0 || paint_rect.y != 0 ||
01893                     paint_rect.width != 0 || paint_rect.height != 0) {
01894                         gdk_window_begin_paint_rect (
01895                                 zoom_region->priv->w->window, &paint_rect);
01896                         zoom_region_paint (zoom_region, &paint_rect);
01897                         gdk_window_end_paint (zoom_region->priv->w->window);
01898                 }
01899                 if (timing_test) {
01900                         mag_timing.num_scale_samples++;
01901                         
01902                         gulong microseconds;
01903 
01904                         mag_timing.scale_val =
01905                                 g_timer_elapsed (mag_timing.scale,
01906                                                  &microseconds);
01907                         mag_timing.scale_total += mag_timing.scale_val;
01908 
01909                         if (mag_timing.scale_val != 0 && (timing_scale_max == 0 ||
01910                            (1.0/(float)mag_timing.scale_val) > (1.0/(float)timing_scale_max)))
01911                                 timing_scale_max = mag_timing.scale_val;
01912                         if ((source_rect.height * source_rect.width / mag_timing.scale_val) > update_nrr_max)
01913                                 update_nrr_max = source_rect.height * source_rect.width / mag_timing.scale_val;
01914 
01915                         mag_timing.update_pixels_total += source_rect.height * source_rect.width;
01916 
01917                         if (zoom_region->timing_output) {
01918                                 fprintf(stderr, "  Update Duration          = %f (avg. %f) (max. %f) (tot. %f) seconds\n",
01919                                         mag_timing.scale_val, (mag_timing.scale_total / 
01920                                         mag_timing.num_scale_samples), timing_scale_max, mag_timing.scale_total);
01921                                 fprintf(stderr, "    Update Pixels          = %ld (avg. %ld) pixels/frame\n",
01922                                         (long) source_rect.height * source_rect.width,
01923                                         mag_timing.update_pixels_total / mag_timing.num_scale_samples);
01924                                 fprintf(stderr, "    Update Rate            = (avg. %f) (max. %f) updates/second\n",
01925                                         1.0/(mag_timing.scale_total / mag_timing.num_scale_samples), 1.0/(float)timing_scale_max);
01926                                 fprintf(stderr, "    Net Update Rate        = (avg. %f) (max. %f) Mpex/second\n",
01927                                         ((float)mag_timing.update_pixels_total / (float)mag_timing.scale_total) / 1000000.0,
01928                                         update_nrr_max / 1000000.0);
01929                         }
01930                 }
01931         } else {
01932                 fprintf (stderr, "update on uninitialized zoom region!\n");
01933         }
01934 }
01935 
01936 static void
01937 zoom_region_init_window (ZoomRegion *zoom_region)
01938 {
01939         GtkFixed *parent;
01940         GtkWidget *zoomer, *border;
01941         DBG(fprintf (stderr, "window not yet created...\n"));
01942         parent = GTK_FIXED (
01943                 ((Magnifier *)zoom_region->priv->parent)->priv->canvas);
01944         zoomer = gtk_drawing_area_new ();
01945         border = gtk_drawing_area_new ();
01946         zoom_region->priv->border = border;
01947         zoom_region->priv->w = zoomer;
01948 
01949 #ifdef ZOOM_REGION_DEBUG
01950         g_assert (zoom_region->alive);
01951 #endif
01952         gtk_widget_set_size_request (GTK_WIDGET (border),
01953                                      zoom_region->viewport.x2 -
01954                                      zoom_region->viewport.x1,
01955                                      zoom_region->viewport.y2 -
01956                                      zoom_region->viewport.y1);
01957         gtk_widget_set_size_request (GTK_WIDGET (zoomer),
01958                                      zoom_region->viewport.x2 -
01959                                      zoom_region->viewport.x1 -
01960                                      zoom_region->border_size * 2,
01961                                      zoom_region->viewport.y2 -
01962                                      zoom_region->viewport.y1 -
01963                                      zoom_region->border_size * 2);
01964         gtk_fixed_put (parent, border,
01965                        zoom_region->viewport.x1,
01966                        zoom_region->viewport.y1);
01967         gtk_fixed_put (parent, zoomer,
01968                        zoom_region->viewport.x1 + zoom_region->border_size,
01969                        zoom_region->viewport.y1 + zoom_region->border_size);
01970         gtk_widget_show (GTK_WIDGET (border));
01971         gtk_widget_show (GTK_WIDGET (zoomer));
01972         gtk_widget_show (GTK_WIDGET (parent));
01973         zoom_region->priv->expose_handler_id =
01974                 g_signal_connect (G_OBJECT (zoom_region->priv->w),
01975                                   "expose_event",
01976                                   G_CALLBACK (zoom_region_expose_handler),
01977                                   zoom_region);
01978         DBG(fprintf (stderr, "New window created\n"));
01979 }
01980 
01981 static int
01982 zoom_region_process_updates (gpointer data)
01983 {
01984         ZoomRegion *zoom_region = (ZoomRegion *) data;
01985 
01986         /* TODO: lock the queue when copying it? */
01987         zoom_region_coalesce_updates (zoom_region);
01988 
01989         if (zoom_region->priv->q != NULL) {
01990                 GList *last = g_list_last (zoom_region->priv->q);
01991 #ifdef ZOOM_REGION_DEBUG
01992                 fprintf (stderr, "qlen=%d\n", g_list_length (zoom_region->priv->q));
01993 #endif
01994                 if (last) {
01995                         zoom_region->priv->q = g_list_remove_link (zoom_region->priv->q,
01996                                                                    last);
01997                         zoom_region_update (zoom_region,
01998                                             * (GdkRectangle *) last->data);
01999                         g_list_free (last);
02000 #ifdef DEBUG
02001                         fputs (".\n", stderr); /* debug output, means we actually did something. */
02002 #endif
02003                 }
02004                 return TRUE;
02005         }
02006         else 
02007         {
02008                 if (zoom_region->priv) 
02009                         zoom_region->priv->update_handler_id = 0;
02010                 return FALSE;
02011         }
02012 }
02013 
02014 void
02015 timing_report(ZoomRegion *zoom_region)
02016 {
02017         float frame_avg;
02018         float x_scroll_incr, y_scroll_incr;
02019         int width, height, x, y;
02020 
02021         if (timing_test) {
02022                 width = (zoom_region->viewport.x2 -
02023                         zoom_region->viewport.x1) / zoom_region->xscale;
02024                 height = (zoom_region->viewport.y2 -
02025                         zoom_region->viewport.y1) / zoom_region->yscale;
02026 
02027                 frame_avg = mag_timing.frame_total / mag_timing.num_frame_samples;
02028 
02029                 x_scroll_incr = (float)mag_timing.dx_total / (float)mag_timing.num_line_samples;
02030                 y_scroll_incr = (float)mag_timing.dy_total / (float)mag_timing.num_line_samples;
02031 
02032                 gdk_drawable_get_size (GDK_DRAWABLE (zoom_region->priv->w->window),
02033                         &x, &y);
02034 
02035                 fprintf(stderr, "  Frames Processed         = %ld\n", 
02036                         mag_timing.num_frame_samples + 1);
02037                 fprintf(stderr, "  Width/Height/Depth       = %d/%d/%d\n", x, y,
02038                         gdk_drawable_get_depth (zoom_region->priv->w->window));
02039                 fprintf(stderr, "  Zoom Factor (x/y)        = %f/%f\n", zoom_region->xscale,
02040                         zoom_region->yscale);
02041                 if (mag_timing.num_scale_samples != 0) {
02042                         fprintf(stderr, "  Update Duration          = (avg. %f) (max. %f) (tot. %f) seconds\n",
02043                                 (mag_timing.scale_total / mag_timing.num_scale_samples), timing_scale_max, mag_timing.scale_total);
02044                         fprintf(stderr, "    Update Pixels          = (avg. %ld) pixels/frame\n",
02045                                 mag_timing.update_pixels_total / mag_timing.num_scale_samples);
02046                         fprintf(stderr, "    Update Rate            = (avg. %f) (max. %f) updates/second\n",
02047                                 1.0/((float)mag_timing.scale_total / (float)mag_timing.num_scale_samples),
02048                                 1.0/(float)timing_scale_max);
02049                         fprintf(stderr, "    Net Update Rate        = (avg. %f) (max. %f) Mpex/second\n",
02050                                 ((float)mag_timing.update_pixels_total / (float)mag_timing.scale_total) / 1000000.0,
02051                                 update_nrr_max / 1000000.0);
02052                 }
02053                 fprintf(stderr, "  Pan Latency              = (avg. %f) (max. %f) seconds\n",
02054                         (mag_timing.idle_total / mag_timing.num_idle_samples), timing_idle_max);
02055                 fprintf(stderr, "  Total Frame Duration     = (avg. %f) (max. %f) (tot. %f) seconds\n",
02056                         frame_avg, timing_frame_max, mag_timing.frame_total);
02057                 fprintf(stderr, "  Frame Rate               = (avg. %f) (max. %f) frames/second\n",
02058                         1.0 / (mag_timing.frame_total / mag_timing.num_frame_samples), cps_max);
02059                 fprintf(stderr, "  Scroll Delta (x)         = (avg. %f) (tot. %d) lines\n",
02060                         x_scroll_incr, mag_timing.dx_total);
02061                 fprintf(stderr, "  Scroll Delta (y)         = (avg. %f) (tot. %d) lines\n",
02062                         y_scroll_incr, mag_timing.dy_total);
02063                 fprintf(stderr, "  Scroll Rate (x)          = (avg. %f) lines/second\n",
02064                         x_scroll_incr / frame_avg);
02065                 fprintf(stderr, "  Scroll Rate (y)          = (avg. %f) lines/second\n",
02066                         y_scroll_incr / frame_avg);
02067 
02068                 fprintf(stderr, "  Net Render Rate          = (avg. %f) (max. %f) Mpex/second\n\n",
02069                         (height * width *
02070                         ((float)mag_timing.num_frame_samples / (float)mag_timing.frame_total)) / 1000000.0,
02071                         nrr_max / 1000000.0);
02072         }
02073 }
02074 
02075 static void
02076 zoom_region_time_frame(ZoomRegion *zoom_region, Magnifier *magnifier)
02077 {
02078         float frame_avg;
02079         float x_scroll_incr, y_scroll_incr;
02080         int width = magnifier->target_bounds.x2 - magnifier->target_bounds.x1;
02081         int height = magnifier->target_bounds.y2 - magnifier->target_bounds.y1;
02082 
02083         mag_timing.num_frame_samples++;
02084         g_timer_stop (mag_timing.frame);
02085 
02086         gulong microseconds;
02087 
02088         mag_timing.frame_val = g_timer_elapsed (mag_timing.frame,
02089                                                 &microseconds);
02090 
02091         mag_timing.frame_total += mag_timing.frame_val;
02092         if (mag_timing.frame_val > timing_frame_max)
02093                 timing_frame_max = mag_timing.frame_val;
02094         if (mag_timing.frame_val != 0 && 1.0/mag_timing.frame_val > cps_max)
02095                 cps_max = 1.0/mag_timing.frame_val;
02096 
02097         frame_avg = mag_timing.frame_total / mag_timing.num_frame_samples;
02098 
02099         x_scroll_incr = (float)mag_timing.dx_total / (float)mag_timing.num_line_samples;
02100         y_scroll_incr = (float)mag_timing.dy_total / (float)mag_timing.num_line_samples;
02101 
02102         if ((height * width / mag_timing.frame_val) > nrr_max)
02103                 nrr_max = height * width / mag_timing.frame_val;
02104 
02105         if (zoom_region->timing_output) {
02106                 fprintf(stderr, "  Total Frame Duration     = %f (avg. %f) (max. %f) (tot. %f) seconds\n",
02107                         mag_timing.frame_val, frame_avg, timing_frame_max, mag_timing.frame_total);
02108                 fprintf(stderr, "  Frame Rate               = (avg. %f) (max. %f) frames/second\n",
02109                         1.0 /frame_avg, cps_max);
02110                 fprintf(stderr, "  Scroll Delta (x)         = (avg. %f) (tot. %d) lines\n",
02111                         x_scroll_incr, mag_timing.dx_total);
02112                 fprintf(stderr, "  Scroll Delta (y)         = (avg. %f) (tot. %d) lines\n",
02113                         y_scroll_incr, mag_timing.dy_total);
02114                 fprintf(stderr, "  Scroll Rate (x)          = (avg. %f) lines/second\n",
02115                         x_scroll_incr / frame_avg);
02116                 fprintf(stderr, "  Scroll Rate (y)          = (avg. %f) lines/second\n",
02117                         y_scroll_incr / frame_avg);
02118 
02119                 fprintf(stderr, "  Net Render Rate          = (avg. %f) (max. %f) Mpex/second\n",
02120                         (height * width *
02121                         ((float)mag_timing.num_frame_samples / (float)mag_timing.frame_total)) / 1000000.0,
02122                         nrr_max / 1000000.0);
02123         }
02124 
02125         mag_timing.last_frame_val = mag_timing.frame_val;
02126         mag_timing.last_dy        = mag_timing.dy;
02127 
02128         if (reset_timing) {
02129                 fprintf(stderr, "\n### Updates summary:\n\n");
02130                 timing_report (zoom_region);
02131                 fprintf(stderr, "\n### Updates finished, starting panning test\n");
02132                 reset_timing_stats();
02133                 reset_timing = FALSE;
02134         }
02135 }
02136 
02137 static void
02138 zoom_region_sync (ZoomRegion *zoom_region)
02139 {
02140         while (zoom_region->priv->q)
02141                 zoom_region_process_updates (zoom_region);
02142 }
02143 
02144 static gboolean
02145 gdk_timing_idle (gpointer data)
02146 {
02147         ZoomRegion *zoom_region = data;
02148 
02149         /* Now update has finished, reset processing_updates */
02150         processing_updates = FALSE;
02151         g_timer_stop (mag_timing.idle);
02152 
02153         if (timing_test) {
02154                 mag_timing.num_idle_samples++;
02155 
02156                 gulong microseconds;
02157 
02158                 mag_timing.idle_val = g_timer_elapsed (mag_timing.idle,
02159                                                        &microseconds);
02160                 mag_timing.idle_total += mag_timing.idle_val;
02161 
02162                 if (mag_timing.idle_val > timing_idle_max)
02163                         timing_idle_max = mag_timing.idle_val;
02164 
02165                 if (zoom_region->timing_output) {
02166                         fprintf(stderr, "  Pan Latency              = %f (avg. %f) (max. %f) seconds\n",
02167                                 mag_timing.idle_val, (mag_timing.idle_total /
02168                                 mag_timing.num_idle_samples), timing_idle_max);
02169                 }
02170         }
02171 
02172         return FALSE;
02173 }
02174 
02175 static void
02176 zoom_region_align (ZoomRegion *zoom_region)
02177 {
02178         Magnifier *magnifier = zoom_region->priv->parent;
02179         long x = 0, y = 0;
02180         long width, height;
02181 
02182         if (timing_start)
02183                 zoom_region_time_frame(zoom_region, magnifier);
02184 
02185         if (timing_test) {
02186                 g_timer_start (mag_timing.frame);
02187 
02188                 if (zoom_region->timing_output) {
02189                         gint x, y;
02190 
02191                         gdk_drawable_get_size (GDK_DRAWABLE (zoom_region->priv->w->window),
02192                                 &x, &y);
02193 
02194                         fprintf(stderr, "\nTiming Information - ROI   = (%d, %d) (%d, %d):\n",
02195                                 zoom_region->roi.x1, zoom_region->roi.y1, zoom_region->roi.x2,
02196                                 zoom_region->roi.y2);
02197                         fprintf(stderr, "  Frame Number             = %ld\n", 
02198                                 mag_timing.num_frame_samples + 1);
02199                         fprintf(stderr, "  Width/Height/Depth       = %d/%d/%d\n", x, y,
02200                                 gdk_drawable_get_depth (zoom_region->priv->w->window));
02201                 }
02202 
02203                 /*
02204                  * The timing_start flag makes sure that we don't start displaying output
02205                  * until we have processed an entire frame.
02206                  */
02207                 if (!timing_start)
02208                         g_timer_start (mag_timing.process);
02209 
02210                 timing_start = TRUE;
02211         }
02212 
02213         g_timer_start (mag_timing.idle);
02214 
02215         /*
02216          * zoom_region_align calls
02217          *   zoom_region_moveto calls
02218          *     zoom_region_scroll calls
02219          *        zoom_region_scroll_fast or zoom_region_scroll_smooth calls
02220          *           gdk_window_scroll or gdk_window_invalidate_rect calls
02221          *              gdk_window_invalidate_region calls
02222          *                 gdk_window_invalidate_maybe_recurse
02223          * 
02224          * The last function in the stack will set up an idle handler of
02225          * priority GDK_PRIORITY_REDRAW (gdk_window_update_idle) to be called
02226          * to handle the work of updateing the screen.
02227          *
02228          * By setting up an idle handler of priority GDK_PRIORITY_REDRAW + 1,
02229          * it will be called immediately after and we can determine when GTK+
02230          * is finished with the update.
02231          */
02232         g_idle_add_full (GDK_PRIORITY_REDRAW + 1,
02233                 gdk_timing_idle, zoom_region, NULL);
02234 
02235         width = (zoom_region->viewport.x2 -
02236                 zoom_region->viewport.x1) / zoom_region->xscale;
02237         height = (zoom_region->viewport.y2 -
02238                 zoom_region->viewport.y1) / zoom_region->yscale;
02239 
02240         switch (zoom_region->x_align_policy) {
02241         case GNOME_Magnifier_ZoomRegion_ALIGN_MAX:
02242                 x = zoom_region->roi.x2 - width;
02243                 break;
02244         case GNOME_Magnifier_ZoomRegion_ALIGN_MIN:
02245                 x = zoom_region->roi.x1;
02246                 break;
02247         case GNOME_Magnifier_ZoomRegion_ALIGN_CENTER:
02248         default:
02249                 x = ((zoom_region->roi.x1 + zoom_region->roi.x2) - width ) / 2;
02250         }
02251 
02252         switch (zoom_region->y_align_policy) {
02253         case GNOME_Magnifier_ZoomRegion_ALIGN_MAX:
02254                 y = zoom_region->roi.y2 - height;
02255                 break;
02256         case GNOME_Magnifier_ZoomRegion_ALIGN_MIN:
02257                 y = zoom_region->roi.y1;
02258                 break;
02259         case GNOME_Magnifier_ZoomRegion_ALIGN_CENTER:
02260         default:
02261                 y = ((zoom_region->roi.y1 + zoom_region->roi.y2) - height ) / 2;
02262         }
02263 
02264         zoom_region_moveto (zoom_region, x, y);
02265 }
02266 
02267 static void
02268 zoom_region_set_viewport (ZoomRegion *zoom_region,
02269                           const GNOME_Magnifier_RectBounds *viewport)
02270 {
02271 #ifdef ZOOM_REGION_DEBUG
02272         g_assert (zoom_region->alive);
02273 #endif
02274         if (zoom_region->viewport.x1 == viewport->x1 &&
02275             zoom_region->viewport.y1 == viewport->y1 &&
02276             zoom_region->viewport.x2 == viewport->x2 &&
02277             zoom_region->viewport.y2 == viewport->y2) {
02278                 return;
02279         }
02280         zoom_region->viewport = *viewport;
02281 #ifdef DEBUG
02282         fprintf (stderr, "Setting viewport %d,%d - %d,%d\n",
02283                  (int) viewport->x1, (int) viewport->y1,
02284                  (int) viewport->x2, (int) viewport->y2);
02285 #endif
02286         zoom_region_align (zoom_region);
02287         if (!zoom_region->priv->w) {
02288                 zoom_region_init_window (zoom_region);
02289         } else {
02290                 CORBA_any *any;
02291                 CORBA_Environment ev;
02292                 Bonobo_PropertyBag properties;
02293                 Magnifier *magnifier = (Magnifier *) zoom_region->priv->parent;
02294                 GtkFixed *fixed = GTK_FIXED (magnifier->priv->canvas);
02295                 gtk_fixed_move (fixed,
02296                                 zoom_region->priv->border,
02297                                 zoom_region->viewport.x1,
02298                                 zoom_region->viewport.y1);
02299                 gtk_fixed_move (fixed,
02300                                 zoom_region->priv->w,
02301                                 zoom_region->viewport.x1 +
02302                                 zoom_region->border_size,
02303                                 zoom_region->viewport.y1 +
02304                                 zoom_region->border_size);
02305                 gtk_widget_set_size_request (
02306                         GTK_WIDGET (zoom_region->priv->border),
02307                         zoom_region->viewport.x2 - zoom_region->viewport.x1,
02308                         zoom_region->viewport.y2 - zoom_region->viewport.y1);
02309                 gtk_widget_set_size_request (GTK_WIDGET (zoom_region->priv->w),
02310                                              zoom_region->viewport.x2 -
02311                                              zoom_region->viewport.x1 -
02312                                              zoom_region->border_size * 2,
02313                                              zoom_region->viewport.y2 -
02314                                              zoom_region->viewport.y1 -
02315                                              zoom_region->border_size * 2);
02316                 CORBA_exception_init (&ev);
02317                 properties = 
02318                         GNOME_Magnifier_Magnifier_getProperties(
02319                                 BONOBO_OBJREF (
02320                                         (Magnifier *) zoom_region->priv->parent), &ev);
02321                 if (!BONOBO_EX (&ev))
02322                         any = Bonobo_PropertyBag_getValue (
02323                                 properties, "source-display-bounds", &ev);
02324                 if (!BONOBO_EX (&ev))
02325                         zoom_region->priv->source_area =
02326                                 *((GNOME_Magnifier_RectBounds *) any->_value);
02327                 if (zoom_region->priv->pixmap) 
02328                         g_object_unref (zoom_region->priv->pixmap);
02329                 zoom_region_create_pixmap (zoom_region);
02330                 if (zoom_region->priv->scaled_pixbuf)
02331                         g_object_unref (zoom_region->priv->scaled_pixbuf);
02332 
02333                 zoom_region->priv->scaled_pixbuf = 
02334                   gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
02335                                   (zoom_region->priv->source_area.x2 -
02336                                    zoom_region->priv->source_area.x1) * zoom_region->xscale + 1,
02337                                   (zoom_region->priv->source_area.y2 -
02338                                    zoom_region->priv->source_area.y1) * zoom_region->yscale + 1);
02339         }
02340         zoom_region_queue_update (zoom_region,
02341                                   zoom_region_source_rect_from_view_bounds (
02342                                           zoom_region, &zoom_region->viewport));
02343 }
02344 
02345 static void
02346 zoom_region_get_property (BonoboPropertyBag *bag,
02347                           BonoboArg *arg,
02348                           guint arg_id,
02349                           CORBA_Environment *ev,
02350                           gpointer user_data)
02351 {
02352         ZoomRegion *zoom_region = user_data;
02353 
02354 #ifdef ZOOM_REGION_DEBUG
02355         g_assert (zoom_region->alive);
02356 #endif
02357         DBG (fprintf (stderr, "Get zoom-region property: %s\n", prop_names[arg_id]));
02358 
02359         switch (arg_id) {
02360         case ZOOM_REGION_MANAGED_PROP:
02361                 BONOBO_ARG_SET_BOOLEAN (arg, zoom_region->is_managed);
02362                 break;
02363         case ZOOM_REGION_POLL_MOUSE_PROP:
02364                 BONOBO_ARG_SET_BOOLEAN (arg, zoom_region->poll_mouse);
02365                 break;
02366         case ZOOM_REGION_INVERT_PROP:
02367                 BONOBO_ARG_SET_BOOLEAN (arg, zoom_region->invert);
02368                 break;
02369         case ZOOM_REGION_SMOOTHSCROLL_PROP:
02370                 BONOBO_ARG_SET_SHORT (arg, zoom_region->smooth_scroll_policy);
02371                 break;
02372         case ZOOM_REGION_COLORBLIND_PROP:
02373                 BONOBO_ARG_SET_SHORT (arg, zoom_region->color_blind_filter);
02374                 break;
02375         case ZOOM_REGION_TESTPATTERN_PROP:
02376                 BONOBO_ARG_SET_BOOLEAN (arg, zoom_region->priv->test);
02377                 break;
02378         case ZOOM_REGION_SMOOTHING_PROP:
02379                 BONOBO_ARG_SET_STRING (arg, zoom_region->smoothing);
02380                 break;
02381         case ZOOM_REGION_CONTRASTR_PROP:
02382                 BONOBO_ARG_SET_FLOAT (arg, zoom_region->contrast_r);
02383                 break;
02384         case ZOOM_REGION_CONTRASTG_PROP:
02385                 BONOBO_ARG_SET_FLOAT (arg, zoom_region->contrast_g);
02386                 break;
02387         case ZOOM_REGION_CONTRASTB_PROP:
02388                 BONOBO_ARG_SET_FLOAT (arg, zoom_region->contrast_b);
02389                 break;
02390         case ZOOM_REGION_BRIGHTR_PROP:
02391                 BONOBO_ARG_SET_FLOAT (arg, zoom_region->bright_r);
02392                 break;
02393         case ZOOM_REGION_BRIGHTG_PROP:
02394                 BONOBO_ARG_SET_FLOAT (arg, zoom_region->bright_g);
02395                 break;
02396         case ZOOM_REGION_BRIGHTB_PROP:
02397                 BONOBO_ARG_SET_FLOAT (arg, zoom_region->bright_b);
02398                 break;
02399         case ZOOM_REGION_XSCALE_PROP:
02400                 BONOBO_ARG_SET_FLOAT (arg, zoom_region->xscale);
02401                 break;
02402         case ZOOM_REGION_YSCALE_PROP:
02403                 BONOBO_ARG_SET_FLOAT (arg, zoom_region->yscale);
02404                 break;
02405         case ZOOM_REGION_BORDERSIZE_PROP:
02406                 BONOBO_ARG_SET_LONG (arg, zoom_region->border_size);
02407                 break;
02408         case ZOOM_REGION_XALIGN_PROP:
02409                 /* TODO: enums here */
02410                 BONOBO_ARG_SET_INT (arg, zoom_region->x_align_policy);
02411                 break;
02412         case ZOOM_REGION_YALIGN_PROP:
02413                 BONOBO_ARG_SET_INT (arg, zoom_region->y_align_policy);
02414                 break;
02415         case ZOOM_REGION_BORDERCOLOR_PROP:
02416                 BONOBO_ARG_SET_LONG (arg,
02417                                      zoom_region->border_color);
02418                 break;
02419         case ZOOM_REGION_VIEWPORT_PROP:
02420                 BONOBO_ARG_SET_GENERAL (arg, zoom_region->viewport,
02421                                         TC_GNOME_Magnifier_RectBounds,
02422                                         GNOME_Magnifier_RectBounds,
02423                                         NULL);
02424                 break;
02425         case ZOOM_REGION_TIMING_TEST_PROP:
02426                 BONOBO_ARG_SET_INT (arg, zoom_region->timing_iterations);
02427                 break;
02428         case ZOOM_REGION_TIMING_OUTPUT_PROP:
02429                 BONOBO_ARG_SET_BOOLEAN (arg, zoom_region->timing_output);
02430                 break;
02431         case ZOOM_REGION_TIMING_PAN_RATE_PROP:
02432                 BONOBO_ARG_SET_INT (arg, zoom_region->timing_pan_rate);
02433                 break;
02434         case ZOOM_REGION_EXIT_MAGNIFIER:
02435                 BONOBO_ARG_SET_BOOLEAN (arg, zoom_region->exit_magnifier);
02436                 break;
02437         default:
02438                 bonobo_exception_set (ev, ex_Bonobo_PropertyBag_NotFound);
02439         };
02440 }
02441 
02442 static void
02443 zoom_region_set_property (BonoboPropertyBag *bag,
02444                           BonoboArg *arg,
02445                           guint arg_id,
02446                           CORBA_Environment *ev,
02447                           gpointer user_data)
02448 {
02449         ZoomRegion *zoom_region = user_data;
02450         GNOME_Magnifier_RectBounds bounds;
02451         gfloat t;
02452 
02453 #ifdef ZOOM_REGION_DEBUG
02454         g_assert (zoom_region->alive);
02455 #endif
02456         DBG (fprintf (stderr, "Set zoom-region property: %s\n", prop_names[arg_id]));
02457 
02458         switch (arg_id) {
02459         case ZOOM_REGION_MANAGED_PROP:
02460                 zoom_region->is_managed = BONOBO_ARG_GET_BOOLEAN (arg);
02461                 break;
02462         case ZOOM_REGION_POLL_MOUSE_PROP:
02463                 zoom_region->poll_mouse = BONOBO_ARG_GET_BOOLEAN (arg);
02464                 if (zoom_region->poll_mouse)
02465                 {
02466                     g_message ("Adding polling timer");
02467                     zoom_region->priv->update_pointer_id =
02468                         g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
02469                                             200,
02470                                             zoom_region_update_pointer_timeout,
02471                                             zoom_region,
02472                                             NULL);
02473                 }
02474                 else if (zoom_region->priv->update_pointer_id)
02475                 {
02476                     g_message ("Removing polling timer");
02477                     g_source_remove (zoom_region->priv->update_pointer_id);
02478                     zoom_region->priv->update_pointer_id = 0;
02479                 }
02480                 break;
02481         case ZOOM_REGION_INVERT_PROP:
02482                 zoom_region->invert = BONOBO_ARG_GET_BOOLEAN (arg);
02483                 zoom_region_update_current (zoom_region);
02484                 break;
02485         case ZOOM_REGION_SMOOTHSCROLL_PROP:
02486                 zoom_region->smooth_scroll_policy = BONOBO_ARG_GET_SHORT (arg);
02487                 break;
02488         case ZOOM_REGION_COLORBLIND_PROP:
02489                 zoom_region->color_blind_filter = BONOBO_ARG_GET_SHORT (arg);
02490                 zoom_region_update_current (zoom_region);
02491                 break;
02492         case ZOOM_REGION_SMOOTHING_PROP:
02493                 zoom_region->smoothing = BONOBO_ARG_GET_STRING (arg);
02494                 if (!strncmp (zoom_region->smoothing, "bilinear", 8))
02495                         zoom_region->priv->gdk_interp_type = GDK_INTERP_BILINEAR;
02496                 else 
02497                         zoom_region->priv->gdk_interp_type = GDK_INTERP_NEAREST;
02498                 zoom_region_update_current (zoom_region);
02499                 break;
02500         case ZOOM_REGION_TESTPATTERN_PROP:
02501                 zoom_region->priv->test = BONOBO_ARG_GET_BOOLEAN (arg);
02502                 if (zoom_region->priv->source_drawable) {
02503                         g_object_unref (zoom_region->priv->source_drawable);
02504                         zoom_region->priv->source_drawable = NULL;
02505                 }
02506                 zoom_region_update_current (zoom_region);
02507                 break;
02508 #define CLAMP_B_C(v) (t = (v), CLAMP (t, -1, 1));
02509         case ZOOM_REGION_CONTRASTR_PROP:
02510                 zoom_region->contrast_r =
02511                         CLAMP_B_C (BONOBO_ARG_GET_FLOAT (arg));
02512                 zoom_region_update_current (zoom_region);
02513                 break;
02514         case ZOOM_REGION_CONTRASTG_PROP:
02515                 zoom_region->contrast_g =
02516                         CLAMP_B_C (BONOBO_ARG_GET_FLOAT (arg));
02517                 zoom_region_update_current (zoom_region);
02518                 break;
02519         case ZOOM_REGION_CONTRASTB_PROP:
02520                 zoom_region->contrast_b =
02521                         CLAMP_B_C (BONOBO_ARG_GET_FLOAT (arg));
02522                 zoom_region_update_current (zoom_region);
02523                 break;
02524         case ZOOM_REGION_BRIGHTR_PROP:
02525                 zoom_region->bright_r =
02526                         CLAMP_B_C (BONOBO_ARG_GET_FLOAT (arg));
02527                 zoom_region_update_current (zoom_region);
02528                 break;
02529         case ZOOM_REGION_BRIGHTG_PROP:
02530                 zoom_region->bright_g =
02531                         CLAMP_B_C (BONOBO_ARG_GET_FLOAT (arg));
02532                 zoom_region_update_current (zoom_region);
02533                 break;
02534         case ZOOM_REGION_BRIGHTB_PROP:
02535                 zoom_region->bright_b =
02536                         CLAMP_B_C (BONOBO_ARG_GET_FLOAT (arg));
02537                 zoom_region_update_current (zoom_region);
02538                 break;
02539         case ZOOM_REGION_XSCALE_PROP:
02540                 (void) zoom_region_update_scale (zoom_region,
02541                                                  BONOBO_ARG_GET_FLOAT (arg),
02542                                                  zoom_region->yscale);
02543                 zoom_region_update_current (zoom_region);
02544                 break;
02545         case ZOOM_REGION_YSCALE_PROP:
02546                 (void) zoom_region_update_scale (zoom_region,
02547                                                  zoom_region->xscale,
02548                                                  BONOBO_ARG_GET_FLOAT (arg));
02549                 zoom_region_update_current (zoom_region);
02550                 break;
02551         case ZOOM_REGION_BORDERSIZE_PROP:
02552                 zoom_region->border_size = BONOBO_ARG_GET_LONG (arg);
02553                 gtk_widget_set_size_request (
02554                         GTK_WIDGET (zoom_region->priv->border),
02555                         zoom_region->viewport.x2 -
02556                         zoom_region->viewport.x1,
02557                         zoom_region->viewport.y2 -
02558                         zoom_region->viewport.y1);
02559                 gtk_widget_set_size_request (GTK_WIDGET (zoom_region->priv->w),
02560                                              zoom_region->viewport.x2 -
02561                                              zoom_region->viewport.x1 -
02562                                              zoom_region->border_size * 2,
02563                                              zoom_region->viewport.y2 -
02564                                              zoom_region->viewport.y1 -
02565                                              zoom_region->border_size * 2);
02566                 gtk_fixed_move (GTK_FIXED (((Magnifier *)zoom_region->priv->parent)->priv->canvas), zoom_region->priv->border, zoom_region->viewport.x1, zoom_region->viewport.y1);
02567                 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);
02568                 break;
02569         case ZOOM_REGION_BORDERCOLOR_PROP:
02570                 zoom_region->border_color =
02571                         BONOBO_ARG_GET_LONG (arg);
02572                 zoom_region_paint_border (zoom_region);
02573                 break;
02574         case ZOOM_REGION_XALIGN_PROP:
02575                 zoom_region->x_align_policy = BONOBO_ARG_GET_INT (arg);
02576                 zoom_region_align (zoom_region);
02577                 break;
02578         case ZOOM_REGION_YALIGN_PROP:
02579                 /* TODO: enums here */
02580                 zoom_region->y_align_policy = BONOBO_ARG_GET_INT (arg);
02581                 zoom_region_align (zoom_region);
02582                 break;
02583         case ZOOM_REGION_VIEWPORT_PROP:
02584                 bounds = BONOBO_ARG_GET_GENERAL (arg,
02585                                                  TC_GNOME_Magnifier_RectBounds,
02586                                                  GNOME_Magnifier_RectBounds,
02587                                                  NULL);
02588                 zoom_region_set_viewport (zoom_region, &bounds);
02589                 break;
02590         case ZOOM_REGION_TIMING_TEST_PROP:
02591                 zoom_region->timing_iterations = BONOBO_ARG_GET_INT (arg);
02592                 timing_test = TRUE;
02593                 break;
02594         case ZOOM_REGION_TIMING_OUTPUT_PROP:
02595                 zoom_region->timing_output = BONOBO_ARG_GET_BOOLEAN (arg);
02596                 break;
02597         case ZOOM_REGION_TIMING_PAN_RATE_PROP:
02598                 zoom_region->timing_pan_rate = BONOBO_ARG_GET_INT (arg);
02599                 timing_test = TRUE;
02600                 break;
02601         case ZOOM_REGION_EXIT_MAGNIFIER:
02602                 zoom_region->exit_magnifier = BONOBO_ARG_GET_BOOLEAN (arg);
02603                 break;
02604         default:
02605                 bonobo_exception_set (ev, ex_Bonobo_PropertyBag_NotFound);
02606         };
02607 }
02608 
02609 static int
02610 zoom_region_process_pending (gpointer data)
02611 {
02612         ZoomRegion *zoom_region = (ZoomRegion *) data;
02613 
02614 #ifdef ZOOM_REGION_DEBUG
02615         g_assert (zoom_region->alive);
02616 #endif
02617         zoom_region_align (zoom_region);
02618         return FALSE;
02619 }
02620 
02621 static int
02622 zoom_region_pan_test (gpointer data)
02623 {
02624         ZoomRegion *zoom_region = (ZoomRegion *) data;
02625         Magnifier *magnifier = zoom_region->priv->parent; 
02626         GNOME_Magnifier_ZoomRegionList *zoom_regions;
02627         GNOME_Magnifier_RectBounds roi;
02628         CORBA_Environment ev;
02629         static int counter = 0;
02630         static gboolean finished_update = !TRUE;
02631         static float last_pixels_at_speed = -1;
02632         float pixels_at_speed;
02633         float total_time;
02634         int screen_height, height;
02635         int pixel_position;
02636         int pixel_direction;
02637 
02638         screen_height = gdk_screen_get_height (
02639                 gdk_display_get_screen (magnifier->source_display,
02640                  magnifier->source_screen_num));
02641 
02642         height = (zoom_region->viewport.y2 -
02643                 zoom_region->viewport.y1) / zoom_region->yscale;
02644 
02645         roi.x1 = zoom_region->roi.x1;
02646         roi.x2 = zoom_region->roi.x2;
02647 
02648         g_timer_stop (mag_timing.process);
02649 
02650         gulong microseconds;
02651 
02652         total_time = g_timer_elapsed (mag_timing.process, &microseconds);
02653 
02654         if (mag_timing.frame_total != 0.0)
02655                 pixels_at_speed = total_time * zoom_region->timing_pan_rate;
02656         else
02657                 pixels_at_speed = 0.0;
02658 
02659     /* Wait until it is actually necessary to update the screen */
02660     if ((int)(last_pixels_at_speed) == (int)(pixels_at_speed))
02661         return TRUE;
02662 
02663         pixel_position = (int)(pixels_at_speed) % (screen_height - height);
02664         counter = (int)(pixels_at_speed) / (screen_height - height);
02665         pixel_direction = counter % 2;
02666 
02667         if (!finished_update) {
02668                 if ((int)(pixels_at_speed) > (zoom_region->roi.y1 + height))
02669                         roi.y1 = zoom_region->roi.y1 + height;
02670                 else
02671                         roi.y1 = (int)(pixels_at_speed);
02672 
02673                 if (roi.y1 >= screen_height - height) {
02674                         roi.y1 = screen_height - height;
02675                 }
02676         } else {
02677                 if (pixel_direction == 0)
02678                         roi.y1 = screen_height - height - pixel_position;
02679                 else
02680                         roi.y1 = pixel_position;
02681         }
02682 
02683         roi.y2 = roi.y1 + height;
02684         magnifier->priv->cursor_x = (roi.x2 + roi.x1) / 2;
02685         magnifier->priv->cursor_y = (roi.y2 + roi.y1) / 2;
02686 
02687         /* Add one since in first loop we call zoom_region_process_updates */
02688         if (counter > zoom_region->timing_iterations - 1)
02689                 zoom_region->exit_magnifier = TRUE;
02690 
02691         zoom_regions = GNOME_Magnifier_Magnifier_getZoomRegions (
02692                 BONOBO_OBJREF (magnifier), &ev);
02693 
02694         if (zoom_regions && (zoom_regions->_length > 0)) {
02695                 GNOME_Magnifier_ZoomRegion_setROI (
02696                         zoom_regions->_buffer[0], &roi, &ev);
02697         }
02698 
02699         if (!finished_update) {
02700                 zoom_region_process_updates(zoom_region);
02701                 if (roi.y1 == screen_height - height) {
02702                         finished_update = TRUE;
02703                         reset_timing = TRUE;
02704                 }
02705         }
02706 
02707     last_pixels_at_speed = pixels_at_speed;
02708 
02709         return FALSE;
02710 }
02711 
02712 static void
02713 impl_zoom_region_set_pointer_pos (PortableServer_Servant servant,
02714                                   const CORBA_long mouse_x,
02715                                   const CORBA_long mouse_y,
02716                                   CORBA_Environment *ev)
02717 {
02718         ZoomRegion *zoom_region =
02719                 ZOOM_REGION (bonobo_object_from_servant (servant));
02720         GdkRectangle paint_area, *clip = NULL;
02721 
02722 #ifdef ZOOM_REGION_DEBUG
02723         g_assert (zoom_region->alive);
02724 #endif
02725         DBG (fprintf (stderr, "Set Pointer: \t%ld,%ld\n", 
02726                       (long) mouse_x, (long) mouse_y));
02727 
02728         fprintf (stderr, "Set Pointer: \t%ld,%ld\n", 
02729                       (long) mouse_x, (long) mouse_y);
02730 
02731         zoom_region_set_cursor_pos (zoom_region, (int) mouse_x, (int) mouse_y);
02732 
02733         if (GTK_IS_WIDGET (zoom_region->priv->w) && 
02734             GDK_IS_DRAWABLE (zoom_region->priv->w->window))
02735         {
02736             gdk_drawable_get_size (
02737                 GDK_DRAWABLE (
02738                     zoom_region->priv->w->window),
02739                 &paint_area.width, &paint_area.height);
02740             paint_area.x = 0;
02741             paint_area.y = 0;
02742             clip = &paint_area;
02743             paint_area = zoom_region_clip_to_source (
02744                 zoom_region, paint_area);
02745         }
02746         /* 
02747          * if we update the cursor now, it causes flicker if the client 
02748          * subsequently calls setROI, so we wait for a redraw.
02749          * Perhaps we should cue a redraw on idle instead?
02750          */
02751 }
02752 
02753 static void
02754 impl_zoom_region_set_contrast (PortableServer_Servant servant,
02755                                const CORBA_float R,
02756                                const CORBA_float G,
02757                                const CORBA_float B,
02758                                CORBA_Environment *ev)
02759 {
02760         ZoomRegion *zoom_region =
02761                 ZOOM_REGION (bonobo_object_from_servant (servant));
02762 
02763 #ifdef ZOOM_REGION_DEBUG
02764         g_assert (zoom_region->alive);
02765 #endif
02766         DBG (fprintf (stderr, "Set contrast: \t%f,%f %f\n", R, G, B));
02767 
02768         /* if the contrast values are the same, this is a NOOP */
02769         if (zoom_region->contrast_r == R &&
02770             zoom_region->contrast_g == G &&
02771             zoom_region->contrast_b == B)
02772                 return;
02773 
02774         zoom_region->contrast_r = R;
02775         zoom_region->contrast_g = G;
02776         zoom_region->contrast_b = B;
02777 
02778         zoom_region_update_current (zoom_region);
02779 }
02780 
02781 static void
02782 impl_zoom_region_get_contrast (PortableServer_Servant servant,
02783                                CORBA_float *R,
02784                                CORBA_float *G,
02785                                CORBA_float *B,
02786                                CORBA_Environment *ev)
02787 {
02788         ZoomRegion *zoom_region =
02789                 ZOOM_REGION (bonobo_object_from_servant (servant));
02790 
02791 #ifdef ZOOM_REGION_DEBUG
02792         g_assert (zoom_region->alive);
02793 #endif
02794 
02795         *R = zoom_region->contrast_r;
02796         *G = zoom_region->contrast_g;
02797         *B = zoom_region->contrast_b;
02798 }
02799 
02800 static void
02801 impl_zoom_region_set_brightness (PortableServer_Servant servant,
02802                                  const CORBA_float R,
02803                                  const CORBA_float G,
02804                                  const CORBA_float B,
02805                                  CORBA_Environment *ev)
02806 {
02807         ZoomRegion *zoom_region =
02808                 ZOOM_REGION (bonobo_object_from_servant (servant));
02809 
02810 #ifdef ZOOM_REGION_DEBUG
02811         g_assert (zoom_region->alive);
02812 #endif
02813         DBG (fprintf (stderr, "Set brightness: \t%f,%f %f\n", R, G, B));
02814 
02815         /* if the contrast values are the same, this is a NOOP */
02816         if (zoom_region->bright_r == R &&
02817             zoom_region->bright_g == G &&
02818             zoom_region->bright_b == B)
02819                 return;
02820 
02821         zoom_region->bright_r = R;
02822         zoom_region->bright_g = G;
02823         zoom_region->bright_b = B;
02824 
02825         zoom_region_update_current (zoom_region);
02826 }
02827 
02828 static void
02829 impl_zoom_region_get_brightness (PortableServer_Servant servant,
02830                                  CORBA_float *R,
02831                                  CORBA_float *G,
02832                                  CORBA_float *B,
02833                                  CORBA_Environment *ev)
02834 {
02835         ZoomRegion *zoom_region =
02836                 ZOOM_REGION (bonobo_object_from_servant (servant));
02837 
02838 #ifdef ZOOM_REGION_DEBUG
02839         g_assert (zoom_region->alive);
02840 #endif
02841 
02842         *R = zoom_region->bright_r;
02843         *G = zoom_region->bright_g;
02844         *B = zoom_region->bright_b;
02845 }
02846 
02847 static void
02848 impl_zoom_region_set_roi (PortableServer_Servant servant,
02849                           const GNOME_Magnifier_RectBounds *bounds,
02850                           CORBA_Environment *ev)
02851 {
02852         ZoomRegion *zoom_region =
02853                 ZOOM_REGION (bonobo_object_from_servant (servant));
02854 
02855 #ifdef ZOOM_REGION_DEBUG
02856         g_assert (zoom_region->alive);
02857 #endif
02858         DBG (fprintf (stderr, "Set ROI: \t%d,%d %d,%d\n", 
02859                       bounds->x1, bounds->y1, bounds->x2, bounds->y2));
02860 
02861         if ((zoom_region->roi.x1 == bounds->x1) &&
02862             (zoom_region->roi.x2 == bounds->x2) &&
02863             (zoom_region->roi.y1 == bounds->y1) &&
02864             (zoom_region->roi.y2 == bounds->y2)) {
02865             return;
02866         }
02867 
02868         /* if these bounds are clearly bogus, warn and ignore */
02869         if (!bounds || (bounds->x2 <= bounds->x1)
02870             || (bounds->y2 < bounds->y1) || 
02871             ((bounds->x1 + bounds->x2)/2 < 0) || 
02872             ((bounds->y1 + bounds->y2)/2 < 0))
02873         {
02874             g_warning ("Bad bounds request (%d,%d to %d,%d), ignoring.\n",
02875                        bounds->x1, bounds->y1, bounds->x2, bounds->y2);
02876             return;
02877         }
02878 
02879         zoom_region->roi = *bounds;
02880 
02881         if (zoom_region->timing_pan_rate > 0) {
02882                 /* Set idle handler to do panning test */
02883                 g_idle_add_full (GDK_PRIORITY_REDRAW + 3, 
02884                         zoom_region_pan_test, zoom_region, NULL);
02885         }
02886 
02887         if (zoom_region->exit_magnifier) {
02888                 if (timing_test) {
02889                         fprintf(stderr, "\n### Timing Summary:\n\n");
02890                         if (zoom_region->timing_pan_rate)
02891                                 fprintf(stderr, "  Pan Rate                 = %d\n", zoom_region->timing_pan_rate);
02892                         timing_report(zoom_region);
02893                 }
02894                 exit(0);
02895         }
02896 
02897         /*
02898          * Do not bother trying to update the screen if the last
02899          * screen update has not had time to complete.
02900          */
02901         if (processing_updates) {
02902                 /* Remove any previous idle handler */
02903                 if (pending_idle_handler != 0) {
02904                         g_source_remove(pending_idle_handler);
02905                         pending_idle_handler = 0;
02906                 }
02907 
02908                 /* Set idle handler to process this pending update when possible */
02909 
02910                 pending_idle_handler = g_idle_add_full (GDK_PRIORITY_REDRAW + 2,
02911                         zoom_region_process_pending, zoom_region, NULL);
02912 
02913                 if (zoom_region->timing_output) {
02914                         fprintf(stderr,
02915                                 "\n  [Last update not finished, pending - ROI=(%d, %d) (%d, %d)]\n\n",
02916                                 zoom_region->roi.x1, zoom_region->roi.y1, zoom_region->roi.x2,
02917                                 zoom_region->roi.y2);
02918                 }
02919         } else {
02920                 zoom_region_align (zoom_region);
02921         }
02922 }
02923 
02924 static CORBA_boolean
02925 impl_zoom_region_set_mag_factor (PortableServer_Servant servant,
02926                                  const CORBA_float mag_factor_x,
02927                                  const CORBA_float mag_factor_y,
02928                                  CORBA_Environment *ev)
02929 {
02930         ZoomRegion *zoom_region =
02931                 ZOOM_REGION (bonobo_object_from_servant (servant));
02932 
02933 #ifdef ZOOM_REGION_DEBUG
02934         g_assert (zoom_region->alive);
02935 #endif
02936         CORBA_any *any;
02937         CORBA_boolean retval = CORBA_TRUE;
02938 
02939         if ((zoom_region->xscale == mag_factor_x) &&
02940             (zoom_region->yscale == mag_factor_y)) {
02941                 return retval;
02942         }
02943 
02944         /* TODO: assert that parent is magnifier object */
02945         Bonobo_PropertyBag properties =
02946                 GNOME_Magnifier_Magnifier_getProperties(
02947                         BONOBO_OBJREF (
02948                                 (Magnifier *) zoom_region->priv->parent), ev);
02949         any = Bonobo_PropertyBag_getValue (
02950                 properties, "source-display-bounds", ev);
02951         if (!BONOBO_EX (ev))
02952                 zoom_region->priv->source_area =
02953                         *((GNOME_Magnifier_RectBounds *) any->_value);
02954         else
02955                 retval = CORBA_FALSE;
02956 
02957         retval = zoom_region_update_scale (zoom_region,
02958                                            mag_factor_x, mag_factor_y);
02959         
02960         zoom_region_update_current (zoom_region);
02961         zoom_region_sync (zoom_region);
02962 
02963         bonobo_object_release_unref (properties, NULL);
02964         return retval;
02965 }
02966 
02967 static void
02968 impl_zoom_region_get_mag_factor (PortableServer_Servant servant,
02969                                  CORBA_float *mag_factor_x,
02970                                  CORBA_float *mag_factor_y,
02971                                  CORBA_Environment *ev)
02972 {
02973         ZoomRegion *zoom_region =
02974                 ZOOM_REGION (bonobo_object_from_servant (servant));
02975 
02976 #ifdef ZOOM_REGION_DEBUG
02977         g_assert (zoom_region->alive);
02978 #endif
02979         *mag_factor_x = zoom_region->xscale;
02980         *mag_factor_y = zoom_region->yscale;
02981 }
02982 
02983 static Bonobo_PropertyBag
02984 impl_zoom_region_get_properties (PortableServer_Servant servant,
02985                                  CORBA_Environment *ev)
02986 {
02987         ZoomRegion *zoom_region =
02988                 ZOOM_REGION (bonobo_object_from_servant (servant));
02989 
02990 #ifdef ZOOM_REGION_DEBUG
02991         g_assert (zoom_region->alive);
02992 #endif
02993         return bonobo_object_dup_ref (
02994                 BONOBO_OBJREF (zoom_region->properties), ev);
02995 }
02996 
02997 static void
02998 impl_zoom_region_mark_dirty (PortableServer_Servant servant,
02999                              const GNOME_Magnifier_RectBounds *roi_dirty,
03000                              CORBA_Environment *ev)
03001 {
03002         ZoomRegion *zoom_region =
03003                 ZOOM_REGION (bonobo_object_from_servant (servant));
03004 
03005 #ifdef ZOOM_REGION_DEBUG
03006         g_assert (zoom_region->alive);
03007 #endif
03008         DEBUG_RECT ("mark dirty", zoom_region_rect_from_bounds (
03009                             zoom_region, roi_dirty) );
03010 
03011         zoom_region_update_pointer (zoom_region, TRUE);
03012         /* XXX ? should we clip here, or wait till process_updates? */
03013         zoom_region_queue_update (zoom_region, 
03014           zoom_region_clip_to_source (zoom_region, 
03015               zoom_region_rect_from_bounds (zoom_region, roi_dirty)));
03016 }
03017 
03018 static GNOME_Magnifier_RectBounds
03019 impl_zoom_region_get_roi (PortableServer_Servant servant,
03020                           CORBA_Environment     *ev)
03021 {
03022         ZoomRegion *zoom_region =
03023                 ZOOM_REGION (bonobo_object_from_servant (servant));
03024 
03025 #ifdef ZOOM_REGION_DEBUG
03026         g_assert (zoom_region->alive);
03027 #endif
03028         return zoom_region->roi;
03029 }
03030 
03031 static void
03032 impl_zoom_region_move_resize (PortableServer_Servant            servant,
03033                               const GNOME_Magnifier_RectBounds *viewport_bounds,
03034                               CORBA_Environment                *ev)
03035 {
03036         ZoomRegion *zoom_region =
03037                 ZOOM_REGION (bonobo_object_from_servant (servant));
03038 
03039 #ifdef ZOOM_REGION_DEBUG
03040         g_assert (zoom_region->alive);
03041 #endif
03042         zoom_region_set_viewport (zoom_region, viewport_bounds);
03043 }
03044 
03045 /* could be called multiple times... */
03046 static void
03047 zoom_region_do_dispose (ZoomRegion *zoom_region)
03048 {
03049         DBG(g_message ("disposing region %p", zoom_region));
03050         if (zoom_region->priv && zoom_region->priv->expose_handler_id && 
03051             GTK_IS_WIDGET (zoom_region->priv->w)) {
03052                 g_signal_handler_disconnect (
03053                         zoom_region->priv->w,
03054                         zoom_region->priv->expose_handler_id);
03055                 zoom_region->priv->expose_handler_id = 0;
03056         }
03057         if (zoom_region->priv && zoom_region->priv->update_pointer_id)
03058             g_source_remove (zoom_region->priv->update_pointer_id);
03059         if (zoom_region->priv && zoom_region->priv->update_handler_id)
03060             g_source_remove (zoom_region->priv->update_handler_id);
03061         g_idle_remove_by_data (zoom_region);
03062         
03063 #ifdef ZOOM_REGION_DEBUG
03064         zoom_region->alive = FALSE;
03065 #endif
03066 }
03067 
03068 static void
03069 impl_zoom_region_dispose (PortableServer_Servant servant,
03070                           CORBA_Environment     *ev)
03071 {
03072         ZoomRegion *zoom_region =
03073                 ZOOM_REGION (bonobo_object_from_servant (servant));
03074         zoom_region_do_dispose (zoom_region);
03075 }
03076 
03077 
03078 /* could be called multiple times */
03079 static void
03080 zoom_region_dispose (GObject *object)
03081 {
03082         ZoomRegion *zoom_region = ZOOM_REGION (object);
03083 
03084         zoom_region_do_dispose (zoom_region);
03085 
03086         BONOBO_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
03087 }
03088 
03089 static void
03090 zoom_region_class_init (ZoomRegionClass *klass)
03091 {
03092         GObjectClass * object_class = (GObjectClass *) klass;
03093         POA_GNOME_Magnifier_ZoomRegion__epv *epv = &klass->epv;
03094         parent_class = g_type_class_peek (BONOBO_TYPE_OBJECT); /* needed by BONOBO_CALL_PARENT! */
03095 
03096         object_class->dispose = zoom_region_dispose;
03097         object_class->finalize = zoom_region_finalize;
03098         
03099         epv->setMagFactor = impl_zoom_region_set_mag_factor;
03100         epv->getMagFactor = impl_zoom_region_get_mag_factor;
03101         epv->getProperties = impl_zoom_region_get_properties;
03102         epv->setROI = impl_zoom_region_set_roi;
03103         epv->setPointerPos = impl_zoom_region_set_pointer_pos;
03104         epv->markDirty = impl_zoom_region_mark_dirty;
03105         epv->getROI = impl_zoom_region_get_roi;
03106         epv->moveResize = impl_zoom_region_move_resize;
03107         epv->dispose = impl_zoom_region_dispose;
03108         epv->setContrast = impl_zoom_region_set_contrast;
03109         epv->getContrast = impl_zoom_region_get_contrast;
03110         epv->setBrightness = impl_zoom_region_set_brightness;
03111         epv->getBrightness = impl_zoom_region_get_brightness;
03112 
03113         reset_timing_stats();
03114 #ifdef DEBUG_CLIENT_CALLS
03115         client_debug = (g_getenv ("MAG_CLIENT_DEBUG") != NULL);
03116 #endif
03117 }
03118 
03119 static void
03120 zoom_region_properties_init (ZoomRegion *zoom_region)
03121 {
03122         BonoboArg *def;
03123         
03124         zoom_region->properties =
03125                 bonobo_property_bag_new_closure (
03126                         g_cclosure_new_object (
03127                                 G_CALLBACK (zoom_region_get_property),
03128                                 G_OBJECT (zoom_region)),
03129                         g_cclosure_new_object (
03130                                 G_CALLBACK (zoom_region_set_property),
03131                                 G_OBJECT (zoom_region)));
03132 
03133         def = bonobo_arg_new (BONOBO_ARG_BOOLEAN);
03134         BONOBO_ARG_SET_BOOLEAN (def, TRUE);
03135         
03136         bonobo_property_bag_add (zoom_region->properties,
03137                                  "is-managed",
03138                                  ZOOM_REGION_MANAGED_PROP,
03139                                  BONOBO_ARG_BOOLEAN,
03140                                  def,
03141                                  "If false, zoom region does not auto-update, but is drawn into directly by the client",
03142                                  Bonobo_PROPERTY_READABLE |
03143                                  Bonobo_PROPERTY_WRITEABLE);
03144 
03145         bonobo_arg_release (def);
03146         def = bonobo_arg_new (BONOBO_ARG_BOOLEAN);
03147         BONOBO_ARG_SET_BOOLEAN (def, TRUE);
03148         
03149         bonobo_property_bag_add (zoom_region->properties,
03150                                  "poll-mouse",
03151                                  ZOOM_REGION_POLL_MOUSE_PROP,
03152                                  BONOBO_ARG_BOOLEAN,
03153                                  NULL,
03154                                  "If false, zoom region does not poll for pointer location, but is (exclusively) given it by the client",
03155                                  Bonobo_PROPERTY_READABLE |
03156                                  Bonobo_PROPERTY_WRITEABLE);
03157 
03158         bonobo_arg_release (def);
03159         def = bonobo_arg_new (BONOBO_ARG_SHORT);
03160         BONOBO_ARG_SET_SHORT (def, GNOME_Magnifier_ZoomRegion_SCROLL_FASTEST);
03161         
03162         bonobo_property_bag_add (zoom_region->properties,
03163                                  "smooth-scroll-policy",
03164                                  ZOOM_REGION_SMOOTHSCROLL_PROP,
03165                                  BONOBO_ARG_SHORT,
03166                                  def,
03167                                  "scrolling policy, slower versus faster",
03168                                  Bonobo_PROPERTY_READABLE |
03169                                  Bonobo_PROPERTY_WRITEABLE);
03170 
03171         bonobo_arg_release (def);
03172         def = bonobo_arg_new (BONOBO_ARG_SHORT);
03173         BONOBO_ARG_SET_SHORT (
03174                 def,
03175                 GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_NO_FILTER);
03176         
03177         bonobo_property_bag_add (zoom_region->properties,
03178                                  "color-blind-filter",
03179                                  ZOOM_REGION_COLORBLIND_PROP,
03180                                  BONOBO_ARG_SHORT,
03181                                  def,
03182                                  "color blind filter to apply in an image",
03183                                  Bonobo_PROPERTY_READABLE |
03184                                  Bonobo_PROPERTY_WRITEABLE);
03185 
03186         bonobo_arg_release (def);
03187         def = bonobo_arg_new (BONOBO_ARG_BOOLEAN);
03188         BONOBO_ARG_SET_BOOLEAN (def, FALSE);
03189 
03190         bonobo_property_bag_add (zoom_region->properties,
03191                                  "use-test-pattern",
03192                                  ZOOM_REGION_TESTPATTERN_PROP,
03193                                  BONOBO_ARG_BOOLEAN,
03194                                  def,
03195                                  "use test pattern for source",
03196                                  Bonobo_PROPERTY_READABLE |
03197                                  Bonobo_PROPERTY_WRITEABLE);
03198 
03199         bonobo_arg_release (def);
03200         def = bonobo_arg_new (BONOBO_ARG_BOOLEAN);
03201         BONOBO_ARG_SET_BOOLEAN (def, TRUE);
03202         
03203         bonobo_property_bag_add (zoom_region->properties,
03204                                  "inverse-video",
03205                                  ZOOM_REGION_INVERT_PROP,
03206                                  BONOBO_ARG_BOOLEAN,
03207                                  def,
03208                                  "inverse video display",
03209                                  Bonobo_PROPERTY_READABLE |
03210                                  Bonobo_PROPERTY_WRITEABLE);
03211 
03212         bonobo_arg_release (def);
03213 
03214         bonobo_property_bag_add (zoom_region->properties,
03215                                  "smoothing-type",
03216                                  ZOOM_REGION_SMOOTHING_PROP,
03217                                  BONOBO_ARG_STRING,
03218                                  NULL,
03219                                  "image smoothing algorithm used",
03220                                  Bonobo_PROPERTY_READABLE |
03221                                  Bonobo_PROPERTY_WRITEABLE);
03222 
03223         def = bonobo_arg_new (BONOBO_ARG_FLOAT);
03224         BONOBO_ARG_SET_FLOAT (def, 0.0);
03225 
03226         bonobo_property_bag_add (zoom_region->properties,
03227                                  "red-contrast",
03228                                  ZOOM_REGION_CONTRASTR_PROP,
03229                                  BONOBO_ARG_FLOAT,
03230                                  def,
03231                                  "red image contrast ratio",
03232                                  Bonobo_PROPERTY_READABLE |
03233                                  Bonobo_PROPERTY_WRITEABLE);
03234         bonobo_arg_release (def);
03235 
03236         def = bonobo_arg_new (BONOBO_ARG_FLOAT);
03237         BONOBO_ARG_SET_FLOAT (def, 0.0);
03238 
03239         bonobo_property_bag_add (zoom_region->properties,
03240                                  "green-contrast",
03241                                  ZOOM_REGION_CONTRASTG_PROP,
03242                                  BONOBO_ARG_FLOAT,
03243                                  def,
03244                                  "green image contrast ratio",
03245                                  Bonobo_PROPERTY_READABLE |
03246                                  Bonobo_PROPERTY_WRITEABLE);
03247         bonobo_arg_release (def);
03248 
03249         def = bonobo_arg_new (BONOBO_ARG_FLOAT);
03250         BONOBO_ARG_SET_FLOAT (def, 0.0);
03251 
03252         bonobo_property_bag_add (zoom_region->properties,
03253                                  "blue-contrast",
03254                                  ZOOM_REGION_CONTRASTB_PROP,
03255                                  BONOBO_ARG_FLOAT,
03256                                  def,
03257                                  "blue image contrast ratio",
03258                                  Bonobo_PROPERTY_READABLE |
03259                                  Bonobo_PROPERTY_WRITEABLE);
03260         bonobo_arg_release (def);
03261 
03262         def = bonobo_arg_new (BONOBO_ARG_FLOAT);
03263         BONOBO_ARG_SET_FLOAT (def, 0.0);
03264 
03265         bonobo_property_bag_add (zoom_region->properties,
03266                                  "red-brightness",
03267                                  ZOOM_REGION_BRIGHTR_PROP,
03268                                  BONOBO_ARG_FLOAT,
03269                                  def,
03270                                  "red image brightness ratio",
03271                                  Bonobo_PROPERTY_READABLE |
03272                                  Bonobo_PROPERTY_WRITEABLE);
03273         bonobo_arg_release (def);
03274 
03275         def = bonobo_arg_new (BONOBO_ARG_FLOAT);
03276         BONOBO_ARG_SET_FLOAT (def, 0.0);
03277 
03278         bonobo_property_bag_add (zoom_region->properties,
03279                                  "green-brightness",
03280                                  ZOOM_REGION_BRIGHTG_PROP,
03281                                  BONOBO_ARG_FLOAT,
03282                                  def,
03283                                  "green image brightness ratio",
03284                                  Bonobo_PROPERTY_READABLE |
03285                                  Bonobo_PROPERTY_WRITEABLE);
03286         bonobo_arg_release (def);
03287 
03288         def = bonobo_arg_new (BONOBO_ARG_FLOAT);
03289         BONOBO_ARG_SET_FLOAT (def, 0.0);
03290 
03291         bonobo_property_bag_add (zoom_region->properties,
03292                                  "blue-brightness",
03293                                  ZOOM_REGION_BRIGHTB_PROP,
03294                                  BONOBO_ARG_FLOAT,
03295                                  def,
03296                                  "blue image brightness ratio",
03297                                  Bonobo_PROPERTY_READABLE |
03298                                  Bonobo_PROPERTY_WRITEABLE);
03299         bonobo_arg_release (def);
03300 
03301         def = bonobo_arg_new (BONOBO_ARG_FLOAT);
03302         BONOBO_ARG_SET_FLOAT (def, 2.0);
03303 
03304         bonobo_property_bag_add (zoom_region->properties,
03305                                  "mag-factor-x",
03306                                  ZOOM_REGION_XSCALE_PROP,
03307                                  BONOBO_ARG_FLOAT,
03308                                  def,
03309                                  "x scale factor",
03310                                  Bonobo_PROPERTY_READABLE |
03311                                  Bonobo_PROPERTY_WRITEABLE);
03312 
03313         bonobo_arg_release (def);
03314         def = bonobo_arg_new (BONOBO_ARG_FLOAT);
03315         BONOBO_ARG_SET_FLOAT (def, 2.0);
03316 
03317         bonobo_property_bag_add (zoom_region->properties,
03318                                  "mag-factor-y",
03319                                  ZOOM_REGION_YSCALE_PROP,
03320                                  BONOBO_ARG_FLOAT,
03321                                  def,
03322                                  "y scale factor",
03323                                  Bonobo_PROPERTY_READABLE |
03324                                  Bonobo_PROPERTY_WRITEABLE);
03325 
03326         bonobo_arg_release (def);
03327         def = bonobo_arg_new (BONOBO_ARG_LONG);
03328         BONOBO_ARG_SET_LONG (def, 0);
03329         
03330         bonobo_property_bag_add (zoom_region->properties,
03331                                  "border-size",
03332                                  ZOOM_REGION_BORDERSIZE_PROP,
03333                                  BONOBO_ARG_LONG,
03334                                  def,
03335                                  "size of zoom-region borders, in pixels",
03336                                  Bonobo_PROPERTY_READABLE |
03337                                  Bonobo_PROPERTY_WRITEABLE);
03338 
03339         bonobo_arg_release (def);
03340         def = bonobo_arg_new (BONOBO_ARG_LONG);
03341         BONOBO_ARG_SET_LONG (def, 0x00000000);
03342         
03343         bonobo_property_bag_add (zoom_region->properties,
03344                                  "border-color",
03345                                  ZOOM_REGION_BORDERCOLOR_PROP,
03346                                  BONOBO_ARG_LONG,
03347                                  def,
03348                                  "border color, as RGBA32",
03349                                  Bonobo_PROPERTY_READABLE |
03350                                  Bonobo_PROPERTY_WRITEABLE);
03351 
03352         bonobo_arg_release (def);
03353         def = bonobo_arg_new (BONOBO_ARG_INT);
03354         BONOBO_ARG_SET_INT (def, 0);
03355 
03356         bonobo_property_bag_add (zoom_region->properties,
03357                                  "x-alignment",
03358                                  ZOOM_REGION_XALIGN_PROP,
03359                                  BONOBO_ARG_INT,
03360                                  def,
03361                                  "x-alignment policy for this region",
03362                                  Bonobo_PROPERTY_READABLE |
03363                                  Bonobo_PROPERTY_WRITEABLE);
03364 
03365         bonobo_arg_release (def);
03366         def = bonobo_arg_new (BONOBO_ARG_INT);
03367         BONOBO_ARG_SET_INT (def, 0);
03368 
03369         bonobo_property_bag_add (zoom_region->properties,
03370                                  "y-alignment",
03371                                  ZOOM_REGION_YALIGN_PROP,
03372                                  BONOBO_ARG_INT,
03373                                  def,
03374                                  "y-alignment policy for this region",
03375                                  Bonobo_PROPERTY_READABLE |
03376                                  Bonobo_PROPERTY_WRITEABLE);
03377         bonobo_arg_release (def);
03378 
03379         bonobo_property_bag_add (zoom_region->properties,
03380                                  "viewport",
03381                                  ZOOM_REGION_VIEWPORT_PROP,
03382                                  TC_GNOME_Magnifier_RectBounds,
03383                                  NULL,
03384                                  "viewport bounding box",
03385                                  Bonobo_PROPERTY_READABLE |
03386                                  Bonobo_PROPERTY_WRITEABLE);
03387 
03388         def = bonobo_arg_new (BONOBO_ARG_INT);
03389         BONOBO_ARG_SET_INT (def, 0);
03390 
03391         bonobo_property_bag_add (zoom_region->properties,
03392                                  "timing-iterations",
03393                                  ZOOM_REGION_TIMING_TEST_PROP,
03394                                  BONOBO_ARG_INT,
03395                                  def,
03396                                  "timing iterations",
03397                                  Bonobo_PROPERTY_READABLE |
03398                                  Bonobo_PROPERTY_WRITEABLE);
03399         bonobo_arg_release (def);
03400 
03401         def = bonobo_arg_new (BONOBO_ARG_BOOLEAN);
03402         BONOBO_ARG_SET_BOOLEAN (def, FALSE);
03403         
03404         bonobo_property_bag_add (zoom_region->properties,
03405                                  "timing-output",
03406                                  ZOOM_REGION_TIMING_OUTPUT_PROP,
03407                                  BONOBO_ARG_BOOLEAN,
03408                                  def,
03409                                  "timing output",
03410                                  Bonobo_PROPERTY_READABLE |
03411                                  Bonobo_PROPERTY_WRITEABLE);
03412 
03413         bonobo_arg_release (def);
03414 
03415         def = bonobo_arg_new (BONOBO_ARG_INT);
03416         BONOBO_ARG_SET_INT (def, 0);
03417 
03418         bonobo_property_bag_add (zoom_region->properties,
03419                                  "timing-pan-rate",
03420                                  ZOOM_REGION_TIMING_PAN_RATE_PROP,
03421                                  BONOBO_ARG_INT,
03422                                  def,
03423                                  "timing pan rate",
03424                                  Bonobo_PROPERTY_READABLE |
03425                                  Bonobo_PROPERTY_WRITEABLE);
03426         bonobo_arg_release (def);
03427 
03428         def = bonobo_arg_new (BONOBO_ARG_BOOLEAN);
03429         BONOBO_ARG_SET_BOOLEAN (def, FALSE);
03430         
03431         bonobo_property_bag_add (zoom_region->properties,
03432                                  "exit-magnifier",
03433                                  ZOOM_REGION_EXIT_MAGNIFIER,
03434                                  BONOBO_ARG_BOOLEAN,
03435                                  def,
03436                                  "timing output",
03437                                  Bonobo_PROPERTY_READABLE |
03438                                  Bonobo_PROPERTY_WRITEABLE);
03439 
03440         bonobo_arg_release (def);
03441 
03442 }
03443 
03444 static void
03445 zoom_region_private_init (ZoomRegionPrivate *priv)
03446 {
03447         GdkRectangle rect = {0, 0, 0, 0};
03448         GNOME_Magnifier_RectBounds rectbounds = {0, 0, 0, 0};
03449         priv->parent = NULL;
03450         priv->w = NULL;
03451         priv->default_gc = NULL;
03452         priv->paint_cursor_gc = NULL;
03453         priv->crosswire_gc = NULL;
03454         priv->q = NULL;
03455         priv->scaled_pixbuf = NULL;
03456         priv->source_pixbuf_cache = NULL;
03457         priv->source_drawable = NULL;
03458         priv->pixmap = NULL;
03459         priv->cursor_backing_rect = rect;
03460         priv->cursor_backing_pixels = NULL;
03461         priv->gdk_interp_type = GDK_INTERP_NEAREST;
03462         priv->expose_handler_id = 0;
03463         priv->test = FALSE;
03464         priv->last_cursor_pos.x = 0;
03465         priv->last_cursor_pos.y = 0;
03466         priv->last_drawn_crosswire_pos.x = 0;
03467         priv->last_drawn_crosswire_pos.y = 0;
03468         priv->exposed_bounds = rectbounds;
03469         priv->source_area = rectbounds;
03470         priv->update_pointer_id = 0;
03471         priv->update_handler_id = 0;
03472 }
03473 
03474 static void
03475 zoom_region_init (ZoomRegion *zoom_region)
03476 {
03477         DBG(g_message ("initializing region %p", zoom_region));
03478 
03479         zoom_region_properties_init (zoom_region);
03480         zoom_region->smooth_scroll_policy =
03481                 GNOME_Magnifier_ZoomRegion_SCROLL_SMOOTH;
03482         zoom_region->color_blind_filter =
03483                 GNOME_Magnifier_ZoomRegion_COLORBLIND_FILTER_T_NO_FILTER;
03484         zoom_region->contrast_r = 0.0;
03485         zoom_region->contrast_g = 0.0;
03486         zoom_region->contrast_b = 0.0;
03487         zoom_region->bright_r = 0.0;
03488         zoom_region->bright_g = 0.0;
03489         zoom_region->bright_b = 0.0;
03490         zoom_region->invert = FALSE;
03491         zoom_region->cache_source = FALSE;
03492         zoom_region->border_size = 0;
03493         zoom_region->border_color = 0;
03494         zoom_region->roi.x1 = 0;
03495         zoom_region->roi.x1 = 0;
03496         zoom_region->roi.x2 = 1;
03497         zoom_region->roi.x2 = 1;
03498         zoom_region->x_align_policy = GNOME_Magnifier_ZoomRegion_ALIGN_CENTER;
03499         zoom_region->y_align_policy = GNOME_Magnifier_ZoomRegion_ALIGN_CENTER;
03500         zoom_region->coalesce_func = _coalesce_update_rects;
03501         zoom_region->poll_mouse = TRUE; 
03502         zoom_region->priv = g_malloc (sizeof (ZoomRegionPrivate));
03503         zoom_region_private_init (zoom_region->priv);
03504         bonobo_object_add_interface (BONOBO_OBJECT (zoom_region),
03505                                      BONOBO_OBJECT (zoom_region->properties));
03506         zoom_region->timing_output = FALSE;
03507 #ifdef ZOOM_REGION_DEBUG
03508         zoom_region->alive = TRUE;
03509 #endif
03510         zoom_region->priv->update_pointer_id =
03511             g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
03512                                 200,
03513                                 zoom_region_update_pointer_timeout,
03514                                 zoom_region,
03515                                 NULL);
03516 }
03517 
03518 ZoomRegion *
03519 zoom_region_new (void)
03520 {
03521         return g_object_new (zoom_region_get_type(), NULL);
03522 }
03523 
03524 /* this one really shuts down the object - called once only */
03525 static void
03526 zoom_region_finalize (GObject *region)
03527 {
03528         ZoomRegion *zoom_region = (ZoomRegion *) region;
03529 
03530         DBG(g_message ("finalizing region %p", zoom_region));
03531 
03532         if (zoom_region->priv && zoom_region->priv->q) 
03533         {
03534                 g_list_free (zoom_region->priv->q);
03535                 zoom_region->priv->q = NULL;
03536         }
03537         if (GTK_IS_WIDGET (zoom_region->priv->w))
03538                 gtk_container_remove (GTK_CONTAINER (((Magnifier *) zoom_region->priv->parent)->priv->canvas), GTK_WIDGET (zoom_region->priv->w));
03539         if (GTK_IS_WIDGET (zoom_region->priv->border))
03540                 gtk_container_remove (GTK_CONTAINER (((Magnifier *) zoom_region->priv->parent)->priv->canvas), GTK_WIDGET (zoom_region->priv->border));
03541         if (zoom_region->priv->source_pixbuf_cache) 
03542             g_object_unref (zoom_region->priv->source_pixbuf_cache);
03543         if (zoom_region->priv->scaled_pixbuf) 
03544             g_object_unref (zoom_region->priv->scaled_pixbuf);
03545         if (zoom_region->priv->pixmap) 
03546             g_object_unref (zoom_region->priv->pixmap);
03547         zoom_region->priv->pixmap = NULL;
03548         zoom_region->priv->parent = NULL;
03549         if (zoom_region->priv->cursor_backing_pixels) 
03550             g_object_unref (zoom_region->priv->cursor_backing_pixels);
03551         g_free (zoom_region->priv);
03552         zoom_region->priv = NULL;
03553 #ifdef ZOOM_REGION_DEBUG
03554         zoom_region->alive = FALSE;
03555 #endif
03556         BONOBO_CALL_PARENT (G_OBJECT_CLASS, finalize, (region));
03557 }
03558 
03559 BONOBO_TYPE_FUNC_FULL (ZoomRegion, 
03560                        GNOME_Magnifier_ZoomRegion,
03561                        BONOBO_TYPE_OBJECT,
03562                        zoom_region);

Generated on Thu Jan 25 16:23:23 2007 for gnome-mag by  doxygen 1.4.7