zoom-region.c

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

Generated on Sat Jan 12 14:30:52 2008 for gnome-mag by  doxygen 1.5.2