Magnification utility enhancements.
[platform/core/uifw/at-spi2-atk.git] / util / magnifier.c
1 #include <popt.h>
2 #include <gdk/gdkwindow.h>
3 #include <libbonobo.h>
4 #include "magnifier.h"
5 #include "mag_image.h"
6
7 #define ENV_STRING_MAX_SIZE 128
8
9 /*
10  * Our parent GObject type
11  */
12 #define PARENT_TYPE BONOBO_OBJECT_TYPE
13
14 struct sockaddr_un mag_server = { AF_UNIX ,  "/tmp/magnifier_socket" };
15
16 typedef struct {
17         gchar *target_display;
18         gchar *source_display;
19         int   vertical_split;
20         int   horizontal_split;
21         int   dual_head;
22         int   fullscreen;
23         int   mouse_follow;
24         int   invert_image;
25         float zoom_factor;
26         int   min_refresh_time;
27         int   no_bonobo;
28         int   fast_cmap_convert;
29         int   no_initial_region;
30 } MagnifierOptions;
31
32 static MagnifierOptions global_options = { ":0.0",
33                                            ":0.0",
34                                            0,
35                                            0,
36                                            0,
37                                            0,
38                                            0,
39                                            0,
40                                            2.0,
41                                            200,
42                                            0,
43                                            0,
44                                            0
45                                          };
46
47 struct poptOption magnifier_options [] = {
48         {"target_display", 't', POPT_ARG_STRING, &global_options.target_display, 't', "specify display on which to show magnified view", NULL},
49         {"source_display", 's', POPT_ARG_STRING, &global_options.source_display, 's', "specify display to magnify", NULL},
50         {"vertical", 'v', POPT_ARG_NONE, &global_options.vertical_split, 'v', "split screen vertically (if target display = source display)", NULL},
51         {"horizontal", 'h', POPT_ARG_NONE, &global_options.horizontal_split, 'h', "split screen horizontally (if target display = source display)", NULL},
52         {"dual-head", 'd', POPT_ARG_NONE, &global_options.dual_head, 'd', "dual-head display mode (maps magnifier to second display)", NULL},
53         {"mouse follow", 'm', POPT_ARG_NONE, &global_options.mouse_follow, 'm', "track mouse movements", NULL},
54         {"refresh time", 'r', POPT_ARG_NONE, &global_options.min_refresh_time, 'r', "minimum refresh time for mouse follow and idle, in ms", NULL},
55         {"zoom (scale) factor", 'z', POPT_ARG_FLOAT, &global_options.zoom_factor, 'z', "zoom (scale) factor used to magnify source display", NULL}, 
56 /*      {"invert image", 'i', POPT_ARG_NONE, &global_options.invert_image, 'i', "invert the image colormap", NULL}, */
57         {"fast-colormap-conversion", 'c', POPT_ARG_NONE, &global_options.fast_cmap_convert, 'c', "use faster colormap conversion algorithm (fails for 6 bit color)", NULL}, 
58         {"no-bonobo", '\0', POPT_ARG_NONE, &global_options.no_bonobo, '\0', "don't use bonobo for controls, use sockets", NULL},
59         {"no-initial-region", '\0', POPT_ARG_NONE, &global_options.no_initial_region, '\0', "don't create an initial zoom region", NULL},
60         {NULL, 0, 0, NULL, 0, 0}
61 };
62
63 #define MSG_LEN 80
64
65 static GtkWidget *window; /* TODO: clean up, with accessor func? */
66
67 static void
68 magnifier_realize (GtkWidget *widget)
69 {
70   XWMHints wm_hints;
71   Atom wm_window_protocols[2];
72   static gboolean initialized = FALSE;
73   
74   if (!initialized)
75     {
76       wm_window_protocols[0] = gdk_x11_get_xatom_by_name ("WM_DELETE_WINDOW");
77       wm_window_protocols[1] = gdk_x11_get_xatom_by_name ("_NET_WM_PING");
78     }
79   
80   wm_hints.flags = InputHint;
81   wm_hints.input = False;
82   
83   XSetWMHints (GDK_WINDOW_XDISPLAY (widget->window),
84                GDK_WINDOW_XWINDOW (widget->window), &wm_hints);
85   
86   XSetWMProtocols (GDK_WINDOW_XDISPLAY (widget->window),
87                    GDK_WINDOW_XWINDOW (widget->window), wm_window_protocols, 2);
88 }
89
90 static void
91 magnifier_exit()
92 {
93   gtk_exit(0);
94 }
95
96 static gboolean get_commands(GIOChannel* client,
97                              GIOCondition condition,
98                              gpointer data){
99   char msg[MSG_LEN];
100   int desc_client = 0;
101   
102   memset(msg,0,MSG_LEN);
103   
104   if((desc_client = accept(desc,(struct sockaddr*)&client_sockaddr,&size_client)) == -1){
105     perror("Could connect to client");exit(1);
106   }
107   
108   read(desc_client,msg,sizeof(msg));
109   parse_message(msg, data);
110   return TRUE;
111
112 }
113
114 static void
115 magnifier_pack_regions (Magnifier *magnifier)
116 {
117   /* 
118    * this call prevents resizing, which is a bother, but required to 
119    * work around dtwm incompatibilities.  Perhaps a better workaround will
120    * be found, or we can make this a runtime option.
121    */ 
122   gtk_widget_set_size_request (magnifier->mag_data->output_window, 
123                                magnifier->mag_data->mag_width, 
124                                magnifier->mag_data->mag_height);
125
126   gdk_window_move(magnifier->mag_data->output_window->window,
127                   magnifier->mag_data->mag_x,
128                   magnifier->mag_data->mag_y);
129
130 }
131
132 int main (int argc, char** argv){
133   GIOChannel *mag_channel;
134   char *dpyname;
135   char env_string[ENV_STRING_MAX_SIZE];
136
137   /* TODO: finish replacing socket connection IPC with bonobo service. */
138   Magnifier *magnifier;
139   char * obj_id;
140   GdkGeometry geometry;
141   
142   x_cmap = NULL;
143
144   size_client = sizeof(client_sockaddr);
145
146   image = NULL;
147
148   if (!bonobo_init (&argc, argv))
149     {
150       g_error ("Could not initialize Bonobo");
151     }
152
153   magnifier = magnifier_new (argc, argv);
154   
155   magnifier->mag_data->follow_mouse =
156           global_options.mouse_follow;
157   magnifier->mag_data->color_inverted =
158           global_options.invert_image;
159   if (!global_options.no_initial_region) 
160     {
161       magnifier->mag_data->zoom_regions =
162               g_list_prepend (magnifier->mag_data->zoom_regions,
163                               g_new0 (ZoomRegionData, 1));
164       magnifier->mag_data->factor_x = (int) global_options.zoom_factor; 
165       magnifier->mag_data->factor_y = (int) global_options.zoom_factor;
166     }
167   else {
168       g_print ("starting magnifier with no initial zoom region\n");
169       magnifier->mag_data->mag_width = 0; 
170       magnifier->mag_data->mag_height = 0;
171   }
172       /* TODO: enable fractional magnifications option? */
173   if (global_options.target_display) {
174     snprintf (env_string, (size_t) (ENV_STRING_MAX_SIZE-1), "DISPLAY=%s", global_options.target_display);
175     putenv (env_string);
176   }
177   gtk_init (&argc, &argv);
178
179   window = g_object_connect (gtk_widget_new (gtk_window_get_type (),
180                                              "user_data", NULL,
181                                              "can_focus", FALSE,
182                                              "type", GTK_WINDOW_TOPLEVEL,
183                                              "title", "magnifier",
184                                              "allow_grow", FALSE,
185                                              "allow_shrink", FALSE,
186                                              "border_width", 10,
187                                              NULL),
188                              "signal::realize", magnifier_realize, NULL,
189                              "signal::destroy", magnifier_exit, NULL,
190                              NULL);
191   
192   drawing_area = gtk_drawing_area_new();
193   gtk_container_add (GTK_CONTAINER (window),drawing_area);
194   gtk_widget_add_events(GTK_WIDGET(drawing_area),GDK_BUTTON_PRESS_MASK);
195   gtk_signal_connect (GTK_OBJECT (drawing_area),"expose_event",
196                       GTK_SIGNAL_FUNC(expose_event),NULL);
197   
198   /* Init socket */
199   if (global_options.no_bonobo) {
200     if((desc = socket(AF_UNIX,SOCK_STREAM,0)) == -1){
201       perror("Socket");exit(1);
202     }
203     unlink("/tmp/magnifier_socket");
204
205     if(bind(desc,(struct sockaddr*)&mag_server,sizeof(mag_server)) == -1){
206        perror("Bind");exit(1);
207     }
208
209     if(listen(desc,100) == -1){
210        perror("Listen");exit(1);
211     }
212     mag_channel = g_io_channel_unix_new(desc);
213     g_io_add_watch(mag_channel,
214                    G_IO_IN | G_IO_ERR,
215                    get_commands,
216                    magnifier->mag_data);
217
218   }
219   
220   /* init image information */
221   if (!global_options.source_display) global_options.source_display = gdk_get_display();
222   dpyname = global_options.source_display + (strlen(global_options.source_display) - 1);
223   screen_num = atoi(dpyname);
224   if (!global_options.target_display) {
225     if((screen_num == 0) && global_options.dual_head)
226       screen_num++;
227     else if (global_options.dual_head)
228       screen_num--;
229     global_options.target_display =
230             (char *) malloc (strlen (global_options.source_display));
231     strncpy (global_options.target_display,
232              global_options.source_display,
233              strlen(global_options.source_display)-2);
234     global_options.target_display[strlen(global_options.source_display)-2] = 0;
235     sprintf(global_options.target_display, "%s.%d",
236             global_options.target_display, screen_num);
237   }
238
239   printf("displays: source=%s; target=%s\n",
240          global_options.source_display,
241          global_options.target_display);
242   
243   magnifier->mag_data->source_display = XOpenDisplay (global_options.source_display);
244   magnifier->mag_data->target_display = GDK_DISPLAY();
245
246   spi_image_root_window = RootWindow(magnifier->mag_data->source_display, screen_num);
247   gdk_pixbuf_xlib_init (magnifier->mag_data->source_display, screen_num);
248   image = gdk_pixbuf_new        (GDK_COLORSPACE_RGB,FALSE, 8,
249                                 DisplayWidth (magnifier->mag_data->source_display,screen_num)/2,
250                                 DisplayHeight(magnifier->mag_data->source_display,screen_num)/2);
251   scaled_image = gdk_pixbuf_new (GDK_COLORSPACE_RGB,FALSE, 8,
252                                 DisplayWidth (magnifier->mag_data->target_display,screen_num),
253                                 DisplayHeight(magnifier->mag_data->target_display,screen_num));
254   if (global_options.vertical_split)
255     {
256       magnifier->mag_data->mag_width =
257               DisplayWidth (magnifier->mag_data->target_display,screen_num)/2;
258       magnifier->mag_data->mag_height =
259               DisplayHeight (magnifier->mag_data->target_display, screen_num);
260     }
261   else if (global_options.horizontal_split)
262     {
263       magnifier->mag_data->mag_width =
264             DisplayWidth (magnifier->mag_data->target_display, screen_num);
265       magnifier->mag_data->mag_height =
266             DisplayHeight (magnifier->mag_data->target_display,screen_num)/2;
267     }
268   else if (global_options.fullscreen)
269     {
270       magnifier->mag_data->mag_width =
271             DisplayWidth (magnifier->mag_data->target_display, screen_num);
272       magnifier->mag_data->mag_height =
273             DisplayHeight (magnifier->mag_data->target_display,screen_num);
274     }
275   magnifier->mag_data->mag_x =
276           DisplayWidth (magnifier->mag_data->target_display, screen_num)
277           - magnifier->mag_data->mag_width;
278   magnifier->mag_data->mag_y =
279           DisplayHeight (magnifier->mag_data->target_display, screen_num)
280           - magnifier->mag_data->mag_height;
281
282   magnifier->mag_data->output_window = window;
283   gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
284   gtk_widget_show_all (window);
285
286   magnifier_pack_regions (magnifier);
287   
288   /* if (global_options.fullscreen) */
289   gdk_window_stick (window->window);
290   gdk_window_set_functions(window->window, 0);
291   gdk_window_raise(window->window);
292   
293   gtk_timeout_add(global_options.min_refresh_time, display_image, magnifier->mag_data);
294
295   obj_id = "OAFIID:Accessibility_Util_Magnifier:proto0.1";
296
297   if (! global_options.no_bonobo)
298     {     
299       int ret = bonobo_activation_active_server_register (
300         obj_id, BONOBO_OBJREF (magnifier));
301
302       if (ret == Bonobo_ACTIVATION_REG_SUCCESS)
303         {
304           bonobo_main ();
305         }
306     }
307   else
308     {
309       gtk_main ();
310     }
311
312   unlink("magnifier_socket");
313   return 0;
314 }
315
316 static void
317 impl_magnifier_fullscreen (PortableServer_Servant servant,
318                            CORBA_Environment *ev)
319 {
320   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
321   magnifier->mag_data->mag_width = DisplayWidth (magnifier->mag_data->target_display, screen_num);
322   magnifier->mag_data->mag_height = DisplayHeight (magnifier->mag_data->target_display, screen_num);
323 }
324                                    
325 static void
326 impl_magnifier_set_extents (PortableServer_Servant servant,
327                             CORBA_long x1,
328                             CORBA_long y1,
329                             CORBA_long x2,
330                             CORBA_long y2,
331                             CORBA_Environment *ev)
332 {
333   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
334   magnifier->mag_data->mag_width = x2 - x1;
335   magnifier->mag_data->mag_height = y2 - y1;
336   gdk_window_move(window->window, x1, y1);
337 }
338
339 static void
340 impl_magnifier_set_follow_mouse (PortableServer_Servant servant,
341                                  const CORBA_boolean follow_mouse,
342                                  CORBA_Environment *ev)
343 {
344   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
345   magnifier->mag_data->follow_mouse = (int) follow_mouse;
346 }
347
348 static void
349 impl_magnifier_set_contrast (PortableServer_Servant servant,
350                              const CORBA_short contrast,
351                              CORBA_Environment *ev)
352 {
353   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
354   magnifier->mag_data->contrast = (int) contrast;
355 }
356
357 static void
358 impl_magnifier_set_source_display (PortableServer_Servant servant,
359                                    const CORBA_char *display,
360                                    CORBA_Environment *ev)
361 {
362   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
363   magnifier->mag_data->source_display =
364           XOpenDisplay (global_options.source_display);
365 }
366
367 static void
368 impl_magnifier_set_target_display (PortableServer_Servant servant,
369                                    const CORBA_char *display,
370                                    CORBA_Environment *ev)
371 {
372   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
373   magnifier->mag_data->target_display = GDK_DISPLAY();
374 }
375
376 static void
377 impl_magnifier_goto (PortableServer_Servant servant,
378                      const CORBA_long x,
379                      const CORBA_long y,
380                      CORBA_Environment *ev)
381 {
382   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
383   magnifier->mag_data->center.x = (int) x;
384   magnifier->mag_data->center.y = (int) y;
385 }
386
387 static void
388 impl_magnifier_set_roi (PortableServer_Servant servant,
389                         const CORBA_short zoom_region,
390                         const CORBA_long x1,
391                         const CORBA_long y1,
392                         const CORBA_long x2,
393                         const CORBA_long y2,
394                         CORBA_Environment *ev)
395 {
396   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
397   magnifier->mag_data->center.x = (int) ((x1 + x2)/2);
398   magnifier->mag_data->center.y = (int) ((y1 + y2)/2);
399 }
400
401 static void
402 impl_magnifier_set_mag_factor (PortableServer_Servant servant,
403                                const CORBA_short zoom_region,
404                                const CORBA_float mag_factor_x,
405                                const CORBA_float mag_factor_y,
406                                CORBA_Environment *ev)
407 {
408   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
409   if (zoom_region == (CORBA_short) 0) /* TODO: fix for multi-zoom-regions */
410     {
411       magnifier->mag_data->factor_x = (float) mag_factor_x;
412       magnifier->mag_data->factor_y = (float) mag_factor_y;
413     }
414 }
415
416 static void
417 impl_magnifier_mark_dirty (PortableServer_Servant servant,
418                            const CORBA_short zoom_region,
419                            const CORBA_long x1,
420                            const CORBA_long y1,
421                            const CORBA_long x2,
422                            const CORBA_long y2,
423                            CORBA_Environment *ev)
424 {
425   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
426   /* TODO: implement */
427 }
428
429 static void
430 impl_magnifier_mark_unmanaged (PortableServer_Servant servant,
431                                const CORBA_short zoom_region,
432                                CORBA_Environment *ev)
433 {
434   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
435   /* TODO: implement */
436 }
437
438 static CORBA_short
439 impl_magnifier_create_zoom_region (PortableServer_Servant servant,
440                                    const CORBA_float zx,
441                                    const CORBA_float zy,
442                                    const CORBA_long x1,
443                                    const CORBA_long y1,
444                                    const CORBA_long x2,
445                                    const CORBA_long y2,
446                                    CORBA_Environment *ev)
447 {
448   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
449   if (magnifier->mag_data->zoom_regions == NULL)
450     {
451       magnifier->mag_data->zoom_regions =
452               g_list_prepend (magnifier->mag_data->zoom_regions,
453                               g_new0 (ZoomRegionData, 1));
454       magnifier->mag_data->factor_x = (int) zx; 
455       magnifier->mag_data->factor_y = (int) zy;
456       magnifier->mag_data->mag_x = x1;
457       magnifier->mag_data->mag_y = y1;
458       magnifier->mag_data->mag_width = (x2 - x1);
459       magnifier->mag_data->mag_height = (y2 - y1);
460       magnifier_pack_regions (magnifier);
461     }
462   else
463     {
464       return -1;
465     }
466 }
467
468 static CORBA_boolean
469 impl_magnifier_get_zoom_region_params (PortableServer_Servant servant,
470                                        const CORBA_short zoom_region,
471                                        CORBA_float * zx,
472                                        CORBA_float * zy, CORBA_long * x1,
473                                        CORBA_long * y1, CORBA_long * x2,
474                                        CORBA_long * y2,
475                                        CORBA_Environment * ev)
476 {
477   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
478   if (zoom_region == (CORBA_short) 0)
479   {
480     *zx = magnifier->mag_data->factor_x;
481     *zy = magnifier->mag_data->factor_y;
482     *x1 = 0;
483     *y1 = 0;
484     *x2 = *x1 + magnifier->mag_data->mag_width;
485     *y2 = *y1 + magnifier->mag_data->mag_height;
486     return CORBA_TRUE;    
487   }
488   else return CORBA_FALSE;
489 }
490
491 static void
492 impl_magnifier_resize_zoom_region (PortableServer_Servant servant,
493                                    const CORBA_short zoom_region,
494                                    const CORBA_long x1, const CORBA_long y1,
495                                    const CORBA_long x2, const CORBA_long y2,
496                                    CORBA_Environment * ev)
497 {
498   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
499   if (zoom_region == 0)
500     {
501       magnifier->mag_data->mag_x = x1;
502       magnifier->mag_data->mag_y = y1;
503       magnifier->mag_data->mag_width = (x2 - x1);
504       magnifier->mag_data->mag_height = (y2 - y1);
505       magnifier_pack_regions (magnifier);
506     }
507 }
508
509 static void
510 impl_magnifier_destroy_zoom_region (PortableServer_Servant servant,
511                                     const CORBA_short zoom_region,
512                                     CORBA_Environment * ev)
513 {
514   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
515   if (zoom_region == 0)
516     {
517       g_list_free (magnifier->mag_data->zoom_regions);
518     }
519 }
520
521 static void
522 impl_magnifier_clear_all_zoom_regions (PortableServer_Servant servant,
523                                        CORBA_Environment * ev)
524 {
525   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
526   g_list_free (magnifier->mag_data->zoom_regions);
527   magnifier->mag_data->zoom_regions = NULL;
528 }
529
530 static void
531 impl_magnifier_exit (PortableServer_Servant servant, CORBA_Environment *ev)
532 {
533         ;
534 }
535
536 static void
537 magnifier_class_init (MagnifierClass *klass)
538 {
539         GObjectClass * object_class = (GObjectClass *) klass;
540         POA_Accessibility_Magnifier__epv *epv = &klass->epv;
541 /*
542         object_class->finalize = magnifier_finalize;
543 */
544         epv->_set_SourceDisplay = impl_magnifier_set_source_display;
545         epv->_set_TargetDisplay = impl_magnifier_set_target_display;
546         epv->setROI = impl_magnifier_set_roi;
547         epv->setMagFactor = impl_magnifier_set_mag_factor;
548         epv->markDirty = impl_magnifier_mark_dirty;
549         epv->markUnmanaged = impl_magnifier_mark_unmanaged;
550         epv->createZoomRegion = impl_magnifier_create_zoom_region;
551         epv->getZoomRegionParams = impl_magnifier_get_zoom_region_params;
552         epv->resizeZoomRegion = impl_magnifier_resize_zoom_region;
553         epv->destroyZoomRegion = impl_magnifier_destroy_zoom_region;
554         epv->clearAllZoomRegions = impl_magnifier_clear_all_zoom_regions;
555         epv->exit = impl_magnifier_exit;
556 }
557
558 static void
559 magnifier_init (Magnifier *magnifier)
560 {
561   magnifier->mag_data = (MagnifierData *) g_new0 (MagnifierData, 1);
562   magnifier->mag_data->factor_x = 2;
563   magnifier->mag_data->factor_y = 2;
564   magnifier->mag_data->contrast = 0;
565   magnifier->mag_data->color_inverted = FALSE;
566   magnifier->mag_data->fast_rgb_convert = FALSE;
567   magnifier->mag_data->center.x = 0;
568   magnifier->mag_data->center.y = 0;
569   magnifier->mag_data->zoom_regions = NULL;
570 }
571
572 GType
573 magnifier_get_type (void)
574 {
575         static GType type = 0;
576
577         if (!type) {
578                 static const GTypeInfo tinfo = {
579                         sizeof (MagnifierClass),
580                         (GBaseInitFunc) NULL,
581                         (GBaseFinalizeFunc) NULL,
582                         (GClassInitFunc) magnifier_class_init,
583                         (GClassFinalizeFunc) NULL,
584                         NULL, /* class data */
585                         sizeof (Magnifier),
586                         0, /* n preallocs */
587                         (GInstanceInitFunc) magnifier_init,
588                         NULL /* value table */
589                 };
590                 /*
591                  *   Here we use bonobo_type_unique instead of
592                  * gtk_type_unique, this auto-generates a load of
593                  * CORBA structures for us. All derived types must
594                  * use bonobo_type_unique.
595                  */
596                 type = bonobo_type_unique (
597                         PARENT_TYPE,
598                         POA_Accessibility_Magnifier__init,
599                         NULL,
600                         G_STRUCT_OFFSET (MagnifierClass, epv),
601                         &tinfo,
602                         "Magnifier");
603         }
604
605         return type;
606 }
607
608 Magnifier *
609 magnifier_new (int argc, char **argv)
610 {
611   poptContext ctx;      
612   Magnifier *magnifier =
613           MAGNIFIER (g_object_new (magnifier_get_type(), NULL));
614   ctx = poptGetContext ("magnifier",
615                         argc,
616                         (const char **)argv,
617                         magnifier_options,
618                         0);
619
620   while (poptGetNextOpt (ctx) >= 0)
621         /**/;
622
623   return magnifier;
624 }