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

Generated on Tue Apr 24 15:39:22 2007 for gnome-mag by  doxygen 1.5.1