gnome-mag
magnifier-main.c
Go to the documentation of this file.
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4  *
5  * Copyright 2001 Sun Microsystems Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22 
23 #include "config.h"
24 #include "magnifier.h"
25 #include "magnifier-private.h"
26 #include "zoom-region.h"
27 #include "gmag-graphical-server.h"
28 #include "GNOME_Magnifier.h"
29 
30 #include <unistd.h>
31 
32 #include <string.h>
33 #include <stdlib.h>
34 #include <sys/time.h>
35 
36 #include <gdk/gdk.h>
37 #include <gdk/gdkx.h>
38 #include <gtk/gtk.h>
39 
40 #include <libbonobo.h>
41 
42 #define ENV_STRING_MAX_SIZE 128
43 
45 
46 typedef struct {
49  gchar *cursor_set;
51  gdouble zoom_factor;
52  gdouble zoom_factor_x;
53  gdouble zoom_factor_y;
58  gint64 cursor_color;
59  gboolean vertical_split;
60  gboolean horizontal_split;
61  gboolean fullscreen;
62  gboolean mouse_follow;
63  gboolean invert_image;
66  gboolean timing_output;
70  gboolean smooth_scroll;
72  gint64 border_color;
73  gboolean test_pattern;
75  gboolean ignore_damage;
76 #ifdef HAVE_COMPOSITE
77  gboolean ignore_composite;
78 #endif /* HAVE_COMPOSITE */
79  gboolean print_version;
80  gboolean hide_pointer;
81  gboolean show_crosswires;
83 
84 static MagnifierOptions global_options = { NULL, /* target_display */
85  NULL, /* source_display */
86  "default", /* cursor_set */
87  "none", /* smoothing_type */
88  2.0, /* zoom_factor */
89  0.0, /* zoom_factor_x */
90  0.0, /* zoom_factor_y */
91  500, /* refresh_time */
92  50, /* mouse_poll_time */
93  0, /* cursor_size */
94  0.0F, /* cursor_scale_factor */
95  0xFF000000,/* cursor_color */
96  0, /* vertical_split */
97  0, /* horizontal_split */
98  0, /* fullscreen */
99  0, /* mouse_follow */
100  0, /* invert_image */
101  0, /* no_initial_region */
102  0, /* timing_iterations */
103  0, /* timing_output */
104  10, /* timing_delta_x */
105  10, /* timing_delat_y */
106  0, /* timing_pan_rate */
107  1, /* smooth_scroll */
108  0, /* border_width */
109  0, /* border_color */
110  0, /* test_pattern */
111  0, /* is_override_redirect*/
112  0 /* ignore_damage */
113 #ifdef HAVE_COMPOSITE
114  ,0 /* ignore_composite */
115 #endif /* HAVE_COMPOSITE */
116  ,0, /* print_version */
117  0, /* hide_pointer */
118  0 /* show_crosswires */
119  };
120 
121 static GOptionEntry magnifier_options [] = {
122  {"target-display", 't', 0, G_OPTION_ARG_STRING, &global_options.target_display, "specify display on which to show magnified view", NULL},
123  {"source-display", 's', 0, G_OPTION_ARG_STRING, &global_options.source_display, "specify display to magnify", NULL},
124  {"cursor-set", 0, 0, G_OPTION_ARG_STRING, &global_options.cursor_set, "cursor set to use in target display", NULL},
125  {"cursor-size", 0, 0, G_OPTION_ARG_INT, &global_options.cursor_size, "cursor size to use (overrides cursor-scale-factor)", NULL},
126  {"cursor-scale-factor", 0, 0, G_OPTION_ARG_DOUBLE, &global_options.cursor_scale_factor, "cursor scale factor", NULL},
127  {"cursor-color", 0, 0, G_OPTION_ARG_INT64, &global_options.cursor_color, "cursor color (applied to \'black\' pixels)", NULL},
128  {"vertical", 'v', 0, G_OPTION_ARG_NONE, &global_options.vertical_split, "split screen vertically (if target display = source display)", NULL},
129  {"horizontal", 'h', 0, G_OPTION_ARG_NONE, &global_options.horizontal_split, "split screen horizontally (if target display = source display)", NULL},
130  {"mouse-follow", 'm', 0, G_OPTION_ARG_NONE, &global_options.mouse_follow, "track mouse movements", NULL},
131  {"refresh-time", 'r', 0, G_OPTION_ARG_INT, &global_options.refresh_time, "minimum refresh time for idle, in ms", NULL},
132  {"mouse-latency", 0, 0, G_OPTION_ARG_INT, &global_options.mouse_poll_time, "maximum mouse latency time, in ms", NULL},
133  {"zoom-factor", 'z', 0, G_OPTION_ARG_DOUBLE, &global_options.zoom_factor, "zoom (scale) factor used to magnify source display", NULL},
134  {"invert-image", 'i', 0, G_OPTION_ARG_NONE, &global_options.invert_image, "invert the image colormap", NULL},
135  {"no-initial-region", 0, 0, G_OPTION_ARG_NONE, &global_options.no_initial_region, "don't create an initial zoom region", NULL},
136  {"timing-iterations", 0, 0, G_OPTION_ARG_INT, &global_options.timing_iterations, "iterations to run timing benchmark test (0=continuous)", NULL},
137  {"timing-output", 0, 0, G_OPTION_ARG_NONE, &global_options.timing_output, "display performance ouput", NULL},
138  {"timing-pan-rate", 0, 0, G_OPTION_ARG_INT, &global_options.timing_pan_rate, "timing pan rate in lines per frame", NULL},
139  {"timing-delta-x", 0, 0, G_OPTION_ARG_INT, &global_options.timing_delta_x, "pixels to pan in x-dimension each frame in timing update test", NULL},
140  {"timing-delta-y", 0, 0, G_OPTION_ARG_INT, &global_options.timing_delta_y, "pixels to pan in y-dimension each frame in timing update test", NULL},
141  {"smoothing-type", 0, 0, G_OPTION_ARG_STRING, &global_options.smoothing_type, "image smoothing algorithm to apply (bilinear-interpolation | none)", NULL},
142  {"fullscreen", 'f', 0, G_OPTION_ARG_NONE, &global_options.fullscreen, "fullscreen magnification, covers entire target display [REQUIRES --source-display and --target-display]", NULL},
143  {"smooth-scrolling", 0, 0, G_OPTION_ARG_NONE, &global_options.smooth_scroll, "use smooth scrolling", NULL},
144  {"border-size", 'b', 0, G_OPTION_ARG_INT, &global_options.border_width, "width of border", NULL},
145  {"border-color", 'c', 0, G_OPTION_ARG_INT64, &global_options.border_color, "border color specified as (A)RGB 23-bit value, Alpha-MSB", NULL},
146  {"hide-pointer", 0, 0, G_OPTION_ARG_NONE, &global_options.hide_pointer, "hide magnifier pointer when passed", NULL},
147  {"show-crosswires", 0, 0, G_OPTION_ARG_NONE, &global_options.show_crosswires, "show crosswires when passed", NULL},
148  {"use-test-pattern", 0, 0, G_OPTION_ARG_NONE, &global_options.test_pattern, "use test pattern as source", NULL},
149  {"override-redirect", 0, 0, G_OPTION_ARG_NONE, &global_options.is_override_redirect, "make the magnifier window totally unmanaged by the window manager", NULL},
150  {"ignore-damage", 0, 0, G_OPTION_ARG_NONE, &global_options.ignore_damage, "ignore the X server DAMAGE extension, if present", NULL},
151 #ifdef HAVE_COMPOSITE
152  {"ignore-composite", 0, 0, G_OPTION_ARG_NONE, &global_options.ignore_composite, "ignore the X server COMPOSITE extension, if present", NULL},
153 #endif /* HAVE_COMPOSITE */
154  {"version", 0, 0, G_OPTION_ARG_NONE, &global_options.print_version, "print version", NULL},
155  {NULL}
156 };
157 
158 static void
160  long x1, long y1, long x2, long y2)
161 {
162  bounds->x1 = x1;
163  bounds->y1 = y1;
164  bounds->x2 = x2;
165  bounds->y2 = y2;
166 }
167 
169 
170 static int
172 {
173  static long timing_counter = 0;
174  static int timing_x_pos = 0;
175  static int timing_y_pos = 0;
176  static int x_direction = 1;
177  static int y_direction = 1;
178  Magnifier *magnifier = (Magnifier *) data;
179  GNOME_Magnifier_ZoomRegionList *zoom_regions;
180  Bonobo_PropertyBag properties;
181  CORBA_Environment ev;
183  int x_roi, y_roi;
184 
185  /* Only iterate the number of times specified */
186  if (global_options.timing_iterations > 0) {
187  if (timing_counter > global_options.timing_iterations) {
188  CORBA_exception_init (&ev);
190  if (BONOBO_EX (&ev))
191  fprintf (stderr, "EXCEPTION\n");
192 
193  bonobo_pbclient_set_boolean (properties, "exit-magnifier",
194  TRUE, &ev);
195  }
196  }
197 
198  CORBA_exception_init (&ev);
199 
200  x_roi = global_options.timing_delta_x * timing_x_pos;
201  roi.x1 = x_roi;
202  roi.x2 = (target_width / global_options.zoom_factor) + roi.x1;
203  x_roi = global_options.timing_delta_x * (timing_x_pos + x_direction);
204 
205  /* Determine if magnifier hit an edge and should reverse direction */
206  if (x_roi + (target_width / global_options.zoom_factor) > target_width)
207  x_direction = -1;
208  else if (x_roi < 0)
209  x_direction = 1;
210 
211  timing_x_pos += x_direction;
212 
213  y_roi = global_options.timing_delta_y * timing_y_pos;
214 
215  /* Calculate size of screen not covered by magnifier */
216  if (global_options.horizontal_split)
217  roi.y1 = y_roi + target_height;
218  else
219  roi.y1 = y_roi;
220  roi.y2 = (target_height / global_options.zoom_factor) + roi.y1;
221 
222  y_roi = global_options.timing_delta_y * (timing_y_pos + y_direction);
223 
224  /* The counter is increased each time the y-direction changes */
225  if (y_roi + (target_height / global_options.zoom_factor) >
226  target_height) {
227  timing_counter++;
228  y_direction = -1;
229  }
230  else if (y_roi < 0) {
231  timing_counter++;
232  y_direction = 1;
233  }
234 
235  timing_y_pos += y_direction;
236 
237  if (!IS_MAGNIFIER (magnifier))
238  return FALSE;
239 
240  magnifier->priv->cursor_x = (roi.x2 + roi.x1) / 2;
241  magnifier->priv->cursor_y = (roi.y2 + roi.y1) / 2;
242 
243  zoom_regions =
245  BONOBO_OBJREF (magnifier),
246  &ev);
247 
248  if (zoom_regions && (zoom_regions->_length > 0)) {
249 
251  zoom_regions->_buffer[0], &roi, &ev);
252  }
253 
254  return TRUE;
255 }
256 
257 static int last_x = 0, last_y = 0;
258 
259 static int
261 {
262  Magnifier *magnifier = (Magnifier *) data;
263  GNOME_Magnifier_ZoomRegionList *zoom_regions;
265  CORBA_Environment ev;
267  int mouse_x_return, mouse_y_return;
268  int w, h;
269  GdkModifierType mask_return;
270 
271  CORBA_exception_init (&ev);
272 
273  if (global_options.mouse_follow && IS_MAGNIFIER (magnifier))
274  {
275  gdk_window_get_pointer (
276  magnifier_get_root (magnifier),
277  &mouse_x_return,
278  &mouse_y_return,
279  &mask_return);
280 
281  if (last_x != mouse_x_return || last_y != mouse_y_return)
282  {
283  last_x = mouse_x_return;
284  last_y = mouse_y_return;
285  w = (magnifier->target_bounds.x2 - magnifier->target_bounds.x1);
286  h = (magnifier->target_bounds.y2 - magnifier->target_bounds.y1);
287  roi.x1 = mouse_x_return;
288  roi.y1 = mouse_y_return;
289  roi.x2 = roi.x1 + 1;
290  roi.y2 = roi.y1 + 1;
291 
292  zoom_regions =
294  BONOBO_OBJREF (magnifier),
295  &ev);
296  if (zoom_regions && (zoom_regions->_length > 0))
297  {
298  int i;
299  for (i = 0; i < zoom_regions->_length; ++i)
300  {
301  /* fprintf (stderr, "panning region %d\n", i);*/
302  zoom_region =
303  CORBA_Object_duplicate (
304  ( (CORBA_Object *)
305  (zoom_regions->_buffer))[i], &ev);
306  if (zoom_region != CORBA_OBJECT_NIL) {
308  &roi,
309  &ev);
310  } else fprintf (stderr, "nil region!\n");
311  }
312  }
313  }
314  return TRUE;
315  }
316 
317  return FALSE;
318 }
319 
320 static int
322 {
323  int i;
324  Magnifier *magnifier = data;
325  CORBA_any *dirty_bounds_any;
326  CORBA_Environment ev;
327  Bonobo_PropertyBag properties;
328  GNOME_Magnifier_RectBounds *dirty_bounds;
330 
331  CORBA_exception_init (&ev);
332 
333  if (!IS_MAGNIFIER (magnifier))
334  return FALSE;
335 
337  BONOBO_OBJREF (magnifier),
338  &ev);
339 
340 #ifdef DEBUG_REFRESH
341  fprintf (stderr, "refreshing %d regions\n", regions->_length);
342 #endif
343 
344  properties = GNOME_Magnifier_Magnifier_getProperties (BONOBO_OBJREF (magnifier), &ev);
345 
346  dirty_bounds_any = Bonobo_PropertyBag_getValue (properties, "source-display-bounds", &ev);
347  if (BONOBO_EX (&ev)) {
348  g_warning ("Error getting source-display-bounds");
349  bonobo_main_quit ();
350  return FALSE;
351  }
352 
353  dirty_bounds = (GNOME_Magnifier_RectBounds *) dirty_bounds_any->_value;
354 
355  fprintf (stderr, "region to update: %d %d %d %d\n",
356  dirty_bounds->x1, dirty_bounds->y1, dirty_bounds->x2, dirty_bounds->y2);
357 
358  for (i = 0; i < regions->_length; ++i)
360  regions->_buffer [i], dirty_bounds, &ev);
361 
362  bonobo_object_release_unref (properties, NULL);
363 
364  return TRUE;
365 }
366 
367 int
368 main (int argc, char** argv)
369 {
370  GOptionContext *context;
373  CORBA_any *viewport_any;
374  int x = 0, y = 0, src_width, src_height;
375  guint pan_handle = 0, refresh_handle = 0;
376  CORBA_Environment ev;
377  Bonobo_PropertyBag properties;
378 
379  Magnifier *magnifier;
380 
381  if (!bonobo_init (&argc, argv)) {
382  g_error ("Could not initialize Bonobo");
383  }
384  CORBA_exception_init (&ev);
385 
386  context = g_option_context_new ("- a screen magnifier for Gnome");
387  g_option_context_set_description (context, "Report bugs to http://bugzilla.gnome.org\n");
388  g_option_context_add_main_entries (context, magnifier_options, "main options");
389  g_option_context_set_ignore_unknown_options (context, TRUE);
390  g_option_context_parse(context, &argc, &argv, NULL);
391  g_option_context_free(context);
392 
393  if (global_options.print_version) {
394  g_print ("%s\n", VERSION);
395  return 0;
396  }
397 
403  if (global_options.target_display) {
404  gchar *string;
405  string = g_strconcat ("DISPLAY=", global_options.target_display, NULL);
406  putenv (string);
407  } else {
408  global_options.target_display = g_getenv ("DISPLAY");
409  if (!global_options.target_display) {
410  fprintf (stderr, _("Can't open display: DISPLAY is not set"));
411  exit (1);
412  }
413  }
414 
415  if (!global_options.source_display) {
416  global_options.source_display = global_options.target_display;
417  }
418 
419  if (global_options.timing_pan_rate && global_options.timing_iterations == 0)
420  {
421  g_error ("Must specify timing_iterations when running pan test");
422  }
423 
424  /* FIXME */
425  gtk_init (&argc, &argv);
426 
427  if (global_options.ignore_damage)
428  {
429  g_setenv ("MAGNIFIER_IGNORE_DAMAGE", "1", TRUE);
430  }
431 #ifdef HAVE_COMPOSITE
432  if (global_options.ignore_composite)
433  g_setenv ("MAGNIFIER_IGNORE_COMPOSITE", "1", TRUE);
434 #endif /* HAVE_COMPOSITE */
435 
436  magnifier = magnifier_new (global_options.is_override_redirect);
437 
439  BONOBO_OBJREF (magnifier), &ev);
440  if (ev._major != CORBA_NO_EXCEPTION) fprintf (stderr, "EXCEPTION\n");
441 
442  if (global_options.target_display)
443  bonobo_pbclient_set_string (properties, "target-display-screen",
444  global_options.target_display, NULL);
445 
446  if (global_options.source_display)
447  bonobo_pbclient_set_string (properties, "source-display-screen",
448  global_options.source_display, NULL);
449 
450  if (global_options.cursor_set)
451  bonobo_pbclient_set_string (properties, "cursor-set",
452  global_options.cursor_set, NULL);
453 
454  if (global_options.cursor_size)
455  bonobo_pbclient_set_long (properties, "cursor-size",
456  global_options.cursor_size, NULL);
457 
458  else if (global_options.cursor_scale_factor != 0.0F)
459  bonobo_pbclient_set_float (properties, "cursor-scale-factor",
460  global_options.cursor_scale_factor, NULL);
461  else
462  bonobo_pbclient_set_float (properties, "cursor-scale-factor",
463  global_options.zoom_factor, NULL);
464 
465  if (global_options.cursor_color)
466  bonobo_pbclient_set_ulong (properties, "cursor-color",
467  global_options.cursor_color,
468  NULL);
469 
470  if (!global_options.show_crosswires)
471  bonobo_pbclient_set_long (properties, "crosswire-size", 0, NULL);
472 
473  src_width = gdk_screen_get_width (gdk_display_get_screen (
474  magnifier->source_display,
475  magnifier->source_screen_num));
476  src_height = gdk_screen_get_height (gdk_display_get_screen (
477  magnifier->source_display,
478  magnifier->source_screen_num));
479 
480  target_width = gdk_screen_get_width (gdk_display_get_screen (
481  magnifier->target_display,
482  magnifier->target_screen_num));
483  target_height = gdk_screen_get_height (
484  gdk_display_get_screen (magnifier->target_display,
485  magnifier->target_screen_num));
486 
487  if (global_options.vertical_split) {
488  target_width /= 2;
489  x = target_width;
490  }
491  if (global_options.horizontal_split) {
492  target_height /= 2;
493  y = target_height;
494  }
495 
496  fprintf (stderr, "initial viewport %d %d\n", (int) target_width,
497  (int) target_height);
498 
499 
500  if (global_options.vertical_split || global_options.horizontal_split ||
501  global_options.fullscreen) {
502  init_rect_bounds (viewport, x, y, x + target_width,
503  y + target_height);
504  viewport_any = bonobo_arg_new_from (TC_GNOME_Magnifier_RectBounds,
505  viewport);
506 
507  bonobo_pbclient_set_value (properties, "target-display-bounds",
508  viewport_any, &ev);
509  bonobo_arg_release (viewport_any);
510  }
511 
512  if (global_options.vertical_split || global_options.horizontal_split)
513  {
514 #ifdef HAVE_COMPOSITE
515  if (!g_getenv ("MAGNIFIER_IGNORE_COMPOSITE"))
516  init_rect_bounds (viewport, 0, 0,
517  src_width, src_height);
518  else
519 #endif /* HAVE_COMPOSITE */
520  init_rect_bounds (viewport, 0, 0,
521  src_width-x, src_height-y);
522  viewport_any = bonobo_arg_new_from (TC_GNOME_Magnifier_RectBounds,
523  viewport);
524  bonobo_pbclient_set_value (properties, "source-display-bounds",
525  viewport_any,
526  &ev);
527 
528  bonobo_arg_release (viewport_any);
529  } else if (global_options.fullscreen) {
530  init_rect_bounds (viewport, 0, 0, src_width, src_height);
531  viewport_any = bonobo_arg_new_from (TC_GNOME_Magnifier_RectBounds,
532  viewport);
533  bonobo_pbclient_set_value (properties, "source-display-bounds",
534  viewport_any,
535  &ev);
536  bonobo_arg_release (viewport_any);
537  }
538 
539  bonobo_object_release_unref (properties, NULL);
540  properties = NULL;
541 
542  if (global_options.vertical_split ||
543  global_options.horizontal_split ||
544  global_options.fullscreen)
545  {
546  int scroll_policy;
547 
548  init_rect_bounds (roi, 0, 0,
549  target_width / global_options.zoom_factor,
550  target_height / global_options.zoom_factor);
551  init_rect_bounds (viewport, 0, 0, target_width, target_height);
552  zoom_region =
554  BONOBO_OBJREF (magnifier),
555  global_options.zoom_factor,
556  global_options.zoom_factor,
557  roi,
558  viewport,
559  &ev);
560 
562  if (BONOBO_EX (&ev))
563  fprintf (stderr, "EXCEPTION\n");
564 
565  scroll_policy = global_options.smooth_scroll ?
568 
569  bonobo_pbclient_set_long (properties, "timing-iterations",
570  global_options.timing_iterations, &ev);
571  bonobo_pbclient_set_boolean (properties, "timing-output",
572  global_options.timing_output, &ev);
573  bonobo_pbclient_set_long (properties, "timing-pan-rate",
574  global_options.timing_pan_rate, &ev);
575  bonobo_pbclient_set_long (properties, "border-size",
576  global_options.border_width, &ev);
577  bonobo_pbclient_set_long (properties, "border-color",
578  global_options.border_color, &ev);
579  bonobo_pbclient_set_short (properties, "smooth-scroll-policy",
580  (short) scroll_policy, &ev);
581  bonobo_pbclient_set_boolean (properties, "use-test-pattern",
582  global_options.test_pattern, &ev);
583  bonobo_pbclient_set_boolean (properties, "draw-cursor",
584  !global_options.hide_pointer, &ev);
585 
586  if (strcmp (global_options.smoothing_type, "none"))
587  bonobo_pbclient_set_string (properties, "smoothing-type",
588  global_options.smoothing_type, &ev);
589 
590  if (global_options.invert_image)
591  bonobo_pbclient_set_boolean (properties, "inverse-video",
592  global_options.invert_image, NULL);
593 
595  BONOBO_OBJREF (magnifier),
596  zoom_region,
597  &ev);
598 
599  bonobo_object_release_unref (properties, &ev);
600  properties = NULL;
601  }
602 
603  if (global_options.timing_pan_rate)
604  {
605  GNOME_Magnifier_ZoomRegionList *zoom_regions;
607  roi.x1 = 100;
608  roi.x2 = 100 + (target_width / global_options.zoom_factor);
609  roi.y1 = 0;
610  roi.y2 = target_height / global_options.zoom_factor;
611 
613  BONOBO_OBJREF (magnifier), &ev);
614 
615  if (zoom_regions && (zoom_regions->_length > 0))
616  {
618  zoom_regions->_buffer[0], &roi, &ev);
619  }
620  }
621  else if (global_options.timing_iterations)
622  {
623  refresh_handle = g_timeout_add (global_options.refresh_time,
625  magnifier);
626  }
627  else
628  {
629  if (global_options.ignore_damage ||
630  !gmag_gs_source_has_damage_extension (magnifier))
631  {
632  refresh_handle = g_timeout_add (
633  global_options.refresh_time,
634  magnifier_main_refresh_all, magnifier);
635  }
636 
637  pan_handle = g_timeout_add (
638  global_options.mouse_poll_time,
639  magnifier_main_pan_image, magnifier);
640  }
641 
642  bonobo_main ();
643 
644  if (refresh_handle)
645  g_source_remove (refresh_handle);
646 
647  if (pan_handle)
648  g_source_remove (pan_handle);
649 
650  return 0;
651 }