2002-03-27 Michael Meeks <michael@ximian.com>
[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       return 0;
462     }
463   else
464     {
465       return -1;
466     }
467 }
468
469 static CORBA_boolean
470 impl_magnifier_get_zoom_region_params (PortableServer_Servant servant,
471                                        const CORBA_short zoom_region,
472                                        CORBA_float * zx,
473                                        CORBA_float * zy, CORBA_long * x1,
474                                        CORBA_long * y1, CORBA_long * x2,
475                                        CORBA_long * y2,
476                                        CORBA_Environment * ev)
477 {
478   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
479   if (zoom_region == (CORBA_short) 0)
480   {
481     *zx = magnifier->mag_data->factor_x;
482     *zy = magnifier->mag_data->factor_y;
483     *x1 = 0;
484     *y1 = 0;
485     *x2 = *x1 + magnifier->mag_data->mag_width;
486     *y2 = *y1 + magnifier->mag_data->mag_height;
487     return CORBA_TRUE;    
488   }
489   else return CORBA_FALSE;
490 }
491
492 static void
493 impl_magnifier_resize_zoom_region (PortableServer_Servant servant,
494                                    const CORBA_short zoom_region,
495                                    const CORBA_long x1, const CORBA_long y1,
496                                    const CORBA_long x2, const CORBA_long y2,
497                                    CORBA_Environment * ev)
498 {
499   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
500   if (zoom_region == 0)
501     {
502       magnifier->mag_data->mag_x = x1;
503       magnifier->mag_data->mag_y = y1;
504       magnifier->mag_data->mag_width = (x2 - x1);
505       magnifier->mag_data->mag_height = (y2 - y1);
506       magnifier_pack_regions (magnifier);
507     }
508 }
509
510 static void
511 impl_magnifier_destroy_zoom_region (PortableServer_Servant servant,
512                                     const CORBA_short zoom_region,
513                                     CORBA_Environment * ev)
514 {
515   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
516   if (zoom_region == 0)
517     {
518       g_list_free (magnifier->mag_data->zoom_regions);
519     }
520 }
521
522 static void
523 impl_magnifier_clear_all_zoom_regions (PortableServer_Servant servant,
524                                        CORBA_Environment * ev)
525 {
526   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
527   g_list_free (magnifier->mag_data->zoom_regions);
528   magnifier->mag_data->zoom_regions = NULL;
529 }
530
531 static void
532 impl_magnifier_exit (PortableServer_Servant servant, CORBA_Environment *ev)
533 {
534         ;
535 }
536
537 static void
538 magnifier_class_init (MagnifierClass *klass)
539 {
540         GObjectClass * object_class = (GObjectClass *) klass;
541         POA_Accessibility_Magnifier__epv *epv = &klass->epv;
542 /*
543         object_class->finalize = magnifier_finalize;
544 */
545         epv->_set_SourceDisplay = impl_magnifier_set_source_display;
546         epv->_set_TargetDisplay = impl_magnifier_set_target_display;
547         epv->setROI = impl_magnifier_set_roi;
548         epv->setMagFactor = impl_magnifier_set_mag_factor;
549         epv->markDirty = impl_magnifier_mark_dirty;
550         epv->markUnmanaged = impl_magnifier_mark_unmanaged;
551         epv->createZoomRegion = impl_magnifier_create_zoom_region;
552         epv->getZoomRegionParams = impl_magnifier_get_zoom_region_params;
553         epv->resizeZoomRegion = impl_magnifier_resize_zoom_region;
554         epv->destroyZoomRegion = impl_magnifier_destroy_zoom_region;
555         epv->clearAllZoomRegions = impl_magnifier_clear_all_zoom_regions;
556         epv->exit = impl_magnifier_exit;
557 }
558
559 static void
560 magnifier_init (Magnifier *magnifier)
561 {
562   magnifier->mag_data = (MagnifierData *) g_new0 (MagnifierData, 1);
563   magnifier->mag_data->factor_x = 2;
564   magnifier->mag_data->factor_y = 2;
565   magnifier->mag_data->contrast = 0;
566   magnifier->mag_data->color_inverted = FALSE;
567   magnifier->mag_data->fast_rgb_convert = FALSE;
568   magnifier->mag_data->center.x = 0;
569   magnifier->mag_data->center.y = 0;
570   magnifier->mag_data->zoom_regions = NULL;
571 }
572
573 GType
574 magnifier_get_type (void)
575 {
576         static GType type = 0;
577
578         if (!type) {
579                 static const GTypeInfo tinfo = {
580                         sizeof (MagnifierClass),
581                         (GBaseInitFunc) NULL,
582                         (GBaseFinalizeFunc) NULL,
583                         (GClassInitFunc) magnifier_class_init,
584                         (GClassFinalizeFunc) NULL,
585                         NULL, /* class data */
586                         sizeof (Magnifier),
587                         0, /* n preallocs */
588                         (GInstanceInitFunc) magnifier_init,
589                         NULL /* value table */
590                 };
591                 /*
592                  *   Here we use bonobo_type_unique instead of
593                  * gtk_type_unique, this auto-generates a load of
594                  * CORBA structures for us. All derived types must
595                  * use bonobo_type_unique.
596                  */
597                 type = bonobo_type_unique (
598                         PARENT_TYPE,
599                         POA_Accessibility_Magnifier__init,
600                         NULL,
601                         G_STRUCT_OFFSET (MagnifierClass, epv),
602                         &tinfo,
603                         "Magnifier");
604         }
605
606         return type;
607 }
608
609 Magnifier *
610 magnifier_new (int argc, char **argv)
611 {
612   poptContext ctx;      
613   Magnifier *magnifier =
614           MAGNIFIER (g_object_new (magnifier_get_type(), NULL));
615   ctx = poptGetContext ("magnifier",
616                         argc,
617                         (const char **)argv,
618                         magnifier_options,
619                         0);
620
621   while (poptGetNextOpt (ctx) >= 0)
622         /**/;
623
624   return magnifier;
625 }