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