zoom-region.c

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

Generated on Wed May 23 10:47:18 2007 for gnome-mag by  doxygen 1.5.1