2001-12-10 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         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   
131   x_cmap = NULL;
132
133   size_client = sizeof(client_sockaddr);
134
135   image = NULL;
136
137   if (!bonobo_init (&argc, argv))
138     {
139       g_error ("Could not initialize Bonobo");
140     }
141
142   magnifier = magnifier_new (argc, argv);
143   
144   magnifier->mag_data->follow_mouse =
145           global_options.mouse_follow;
146   magnifier->mag_data->color_inverted =
147           global_options.invert_image;
148   magnifier->mag_data->factor_x =
149     (int) global_options.zoom_factor; 
150   magnifier->mag_data->factor_y =
151     (int) global_options.zoom_factor;
152   
153   /* TODO: enable fractional magnifications ? */
154   if (global_options.target_display) {
155     snprintf (env_string, (size_t) (ENV_STRING_MAX_SIZE-1), "DISPLAY=%s", global_options.target_display);
156     putenv (env_string);
157   }
158   gtk_init (&argc, &argv);
159
160   window = g_object_connect (gtk_widget_new (gtk_window_get_type (),
161                                              "user_data", NULL,
162                                              "can_focus", FALSE,
163                                              "type", GTK_WINDOW_TOPLEVEL,
164                                              "title", "magnifier",
165                                              "allow_grow", FALSE,
166                                              "allow_shrink", FALSE,
167                                              "border_width", 10,
168                                              NULL),
169                              "signal::realize", magnifier_realize, NULL,
170                              "signal::destroy", magnifier_exit, NULL,
171                              NULL);
172   
173   drawing_area = gtk_drawing_area_new();
174   gtk_container_add (GTK_CONTAINER (window),drawing_area);
175   gtk_widget_add_events(GTK_WIDGET(drawing_area),GDK_BUTTON_PRESS_MASK);
176   gtk_signal_connect (GTK_OBJECT (drawing_area),"expose_event",
177                       GTK_SIGNAL_FUNC(expose_event),NULL);
178   
179   /* Init socket */
180   if (global_options.no_bonobo) {
181     if((desc = socket(AF_UNIX,SOCK_STREAM,0)) == -1){
182       perror("Socket");exit(1);
183     }
184     unlink("/tmp/magnifier_socket");
185
186     if(bind(desc,(struct sockaddr*)&mag_server,sizeof(mag_server)) == -1){
187        perror("Bind");exit(1);
188     }
189
190     if(listen(desc,100) == -1){
191        perror("Listen");exit(1);
192     }
193     mag_channel = g_io_channel_unix_new(desc);
194     g_io_add_watch(mag_channel,
195                    G_IO_IN | G_IO_ERR,
196                    get_commands,
197                    magnifier->mag_data);
198
199   }
200   
201   /* init image information */
202   if (!global_options.source_display) global_options.source_display = gdk_get_display();
203   dpyname = global_options.source_display + (strlen(global_options.source_display) - 1);
204   screen_num = atoi(dpyname);
205   if (!global_options.target_display) {
206     if((screen_num == 0) && global_options.dual_head)
207       screen_num++;
208     else if (global_options.dual_head)
209       screen_num--;
210     global_options.target_display =
211             (char *) malloc (strlen (global_options.source_display));
212     strncpy (global_options.target_display,
213              global_options.source_display,
214              strlen(global_options.source_display)-2);
215     global_options.target_display[strlen(global_options.source_display)-2] = 0;
216     sprintf(global_options.target_display, "%s.%d",
217             global_options.target_display, screen_num);
218   }
219
220   printf("displays: source=%s; target=%s\n",
221          global_options.source_display,
222          global_options.target_display);
223   
224   magnifier->mag_data->source_display = XOpenDisplay (global_options.source_display);
225   magnifier->mag_data->target_display = GDK_DISPLAY();
226
227   spi_image_root_window = RootWindow(magnifier->mag_data->source_display, screen_num);
228   gdk_pixbuf_xlib_init (magnifier->mag_data->source_display, screen_num);
229   image = gdk_pixbuf_new        (GDK_COLORSPACE_RGB,FALSE, 8,
230                                 DisplayWidth (magnifier->mag_data->source_display,screen_num)/2,
231                                 DisplayHeight(magnifier->mag_data->source_display,screen_num)/2);
232   scaled_image = gdk_pixbuf_new (GDK_COLORSPACE_RGB,FALSE, 8,
233                                 DisplayWidth (magnifier->mag_data->target_display,screen_num),
234                                 DisplayHeight(magnifier->mag_data->target_display,screen_num));
235   if (global_options.vertical_split)
236           magnifier->mag_data->mag_width = DisplayWidth (magnifier->mag_data->target_display,screen_num)/2;
237   else
238           magnifier->mag_data->mag_width = DisplayWidth (magnifier->mag_data->target_display, screen_num);
239   if (global_options.horizontal_split)
240           magnifier->mag_data->mag_height = DisplayHeight (magnifier->mag_data->target_display,screen_num)/2;
241   else magnifier->mag_data->mag_height = DisplayHeight (magnifier->mag_data->target_display, screen_num);
242   gtk_window_set_decorated(GTK_WINDOW (window), FALSE);
243   gtk_widget_show_all (window);
244
245   gdk_window_move(window->window,
246                   gdk_screen_width() - magnifier->mag_data->mag_width,
247                   gdk_screen_height() - magnifier->mag_data->mag_height);
248   gdk_window_resize (window->window, magnifier->mag_data->mag_width, magnifier->mag_data->mag_height);
249   magnifier->mag_data->output_window = window;
250   if (global_options.fullscreen) gdk_window_stick (window->window);
251   gdk_window_set_functions(window->window, 0);
252   gdk_window_raise(window->window);
253   
254   gtk_timeout_add(global_options.min_refresh_time, display_image, magnifier->mag_data);
255
256   obj_id = "OAFIID:Accessibility_Util_Magnifier:proto0.1";
257
258   if (! global_options.no_bonobo)
259     {     
260       int ret = bonobo_activation_active_server_register (
261         obj_id, BONOBO_OBJREF (magnifier));
262
263       if (ret == Bonobo_ACTIVATION_REG_SUCCESS)
264         {
265           bonobo_main ();
266         }
267     }
268   else
269     {
270       gtk_main ();
271     }
272
273   unlink("magnifier_socket");
274   return 0;
275 }
276
277 static void
278 impl_magnifier_fullscreen (PortableServer_Servant servant,
279                            CORBA_Environment *ev)
280 {
281   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
282   magnifier->mag_data->mag_width = DisplayWidth (magnifier->mag_data->target_display, screen_num);
283   magnifier->mag_data->mag_height = DisplayHeight (magnifier->mag_data->target_display, screen_num);
284 }
285                                    
286 static void
287 impl_magnifier_set_extents (PortableServer_Servant servant,
288                             CORBA_long x1,
289                             CORBA_long y1,
290                             CORBA_long x2,
291                             CORBA_long y2,
292                             CORBA_Environment *ev)
293 {
294   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
295   magnifier->mag_data->mag_width = x2 - x1;
296   magnifier->mag_data->mag_height = y2 - y1;
297   gdk_window_move(window->window, x1, y1);
298 }
299
300 static void
301 impl_magnifier_set_follow_mouse (PortableServer_Servant servant,
302                                  const CORBA_boolean follow_mouse,
303                                  CORBA_Environment *ev)
304 {
305   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
306   magnifier->mag_data->follow_mouse = (int) follow_mouse;
307 }
308
309 static void
310 impl_magnifier_set_contrast (PortableServer_Servant servant,
311                              const CORBA_short contrast,
312                              CORBA_Environment *ev)
313 {
314   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
315   magnifier->mag_data->contrast = (int) contrast;
316 }
317
318 static void
319 impl_magnifier_set_source_display (PortableServer_Servant servant,
320                                    const CORBA_char *display,
321                                    CORBA_Environment *ev)
322 {
323   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
324   magnifier->mag_data->source_display =
325           XOpenDisplay (global_options.source_display);
326 }
327
328 static void
329 impl_magnifier_set_target_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->target_display = GDK_DISPLAY();
335 }
336
337 static void
338 impl_magnifier_goto (PortableServer_Servant servant,
339                      const CORBA_long x,
340                      const CORBA_long y,
341                      CORBA_Environment *ev)
342 {
343   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
344   magnifier->mag_data->center.x = (int) x;
345   magnifier->mag_data->center.y = (int) y;
346 }
347
348 static void
349 impl_magnifier_set_roi (PortableServer_Servant servant,
350                         const CORBA_short zoom_region,
351                         const CORBA_long x1,
352                         const CORBA_long y1,
353                         const CORBA_long x2,
354                         const CORBA_long y2,
355                         CORBA_Environment *ev)
356 {
357   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
358   magnifier->mag_data->center.x = (int) ((x1 + x2)/2);
359   magnifier->mag_data->center.y = (int) ((y1 + y2)/2);
360 }
361
362 static void
363 impl_magnifier_set_mag_factor (PortableServer_Servant servant,
364                                const CORBA_short zoom_region,
365                                const CORBA_float mag_factor_x,
366                                const CORBA_float mag_factor_y,
367                                CORBA_Environment *ev)
368 {
369   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
370   if (zoom_region == (CORBA_short) 0) /* TODO: fix for multi-zoom-regions */
371     {
372       magnifier->mag_data->factor_x = (float) mag_factor_x;
373       magnifier->mag_data->factor_y = (float) mag_factor_y;
374     }
375 }
376
377 static void
378 impl_magnifier_mark_dirty (PortableServer_Servant servant,
379                            const CORBA_short zoom_region,
380                            CORBA_Environment *ev)
381 {
382   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
383   /* TODO: implement */
384 }
385
386 static void
387 impl_magnifier_mark_unmanaged (PortableServer_Servant servant,
388                                const CORBA_short zoom_region,
389                                CORBA_Environment *ev)
390 {
391   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
392   /* TODO: implement */
393 }
394
395 static CORBA_short
396 impl_magnifier_create_zoom_region (PortableServer_Servant servant,
397                                    const CORBA_float zx,
398                                    const CORBA_float zy,
399                                    const CORBA_long x1,
400                                    const CORBA_long y1,
401                                    const CORBA_long x2,
402                                    const CORBA_long y2,
403                                    CORBA_Environment *ev)
404 {
405   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
406   return -1;
407 }
408
409 static CORBA_boolean
410 impl_magnifier_get_zoom_region_params (PortableServer_Servant servant,
411                                        const CORBA_short zoom_region,
412                                        CORBA_float * zx,
413                                        CORBA_float * zy, CORBA_long * x1,
414                                        CORBA_long * y1, CORBA_long * x2,
415                                        CORBA_long * y2,
416                                        CORBA_Environment * ev)
417 {
418   Magnifier *magnifier = MAGNIFIER (bonobo_object_from_servant (servant));
419   if (zoom_region == (CORBA_short) 0)
420   {
421     *zx = magnifier->mag_data->factor_x;
422     *zy = magnifier->mag_data->factor_y;
423     *x1 = 0;
424     *y1 = 0;
425     *x2 = *x1 + magnifier->mag_data->mag_width;
426     *y2 = *y1 + magnifier->mag_data->mag_height;
427     return CORBA_TRUE;    
428   }
429   else return CORBA_FALSE;
430 }
431
432 static void
433 impl_magnifier_resize_zoom_region (PortableServer_Servant _servant,
434                                    const CORBA_short zoom_region,
435                                    const CORBA_long x1, const CORBA_long y1,
436                                    const CORBA_long x2, const CORBA_long y2,
437                                    CORBA_Environment * ev)
438 {
439 }
440
441 static void
442 impl_magnifier_destroy_zoom_region (PortableServer_Servant _servant,
443                                     const CORBA_short zoom_region,
444                                     CORBA_Environment * ev)
445 {
446 }
447
448 static void
449 impl_magnifier_clear_all_zoom_regions (PortableServer_Servant _servant,
450                                        CORBA_Environment * ev)
451 {
452 }
453
454 static void
455 impl_magnifier_exit (PortableServer_Servant servant, CORBA_Environment *ev)
456 {
457         ;
458 }
459
460 static void
461 magnifier_class_init (MagnifierClass *klass)
462 {
463         GObjectClass * object_class = (GObjectClass *) klass;
464         POA_Accessibility_Magnifier__epv *epv = &klass->epv;
465 /*
466         object_class->finalize = magnifier_finalize;
467 */
468         epv->_set_SourceDisplay = impl_magnifier_set_source_display;
469         epv->_set_TargetDisplay = impl_magnifier_set_target_display;
470         epv->setROI = impl_magnifier_set_roi;
471         epv->setMagFactor = impl_magnifier_set_mag_factor;
472         epv->markDirty = impl_magnifier_mark_dirty;
473         epv->markUnmanaged = impl_magnifier_mark_unmanaged;
474         epv->createZoomRegion = impl_magnifier_create_zoom_region;
475         epv->getZoomRegionParams = impl_magnifier_get_zoom_region_params;
476         epv->resizeZoomRegion = impl_magnifier_resize_zoom_region;
477         epv->destroyZoomRegion = impl_magnifier_destroy_zoom_region;
478         epv->clearAllZoomRegions = impl_magnifier_clear_all_zoom_regions;
479         epv->exit = impl_magnifier_exit;
480 }
481
482 static void
483 magnifier_init (Magnifier *magnifier)
484 {
485   magnifier->mag_data = (MagnifierData *) g_new0 (MagnifierData, 1);
486   magnifier->mag_data->factor_x = 2;
487   magnifier->mag_data->factor_y = 2;
488   magnifier->mag_data->contrast = 0;
489   magnifier->mag_data->color_inverted = FALSE;
490   magnifier->mag_data->fast_rgb_convert = FALSE;
491   magnifier->mag_data->center.x = 0;
492   magnifier->mag_data->center.y = 0;
493 }
494
495 GType
496 magnifier_get_type (void)
497 {
498         static GType type = 0;
499
500         if (!type) {
501                 static const GTypeInfo tinfo = {
502                         sizeof (MagnifierClass),
503                         (GBaseInitFunc) NULL,
504                         (GBaseFinalizeFunc) NULL,
505                         (GClassInitFunc) magnifier_class_init,
506                         (GClassFinalizeFunc) NULL,
507                         NULL, /* class data */
508                         sizeof (Magnifier),
509                         0, /* n preallocs */
510                         (GInstanceInitFunc) magnifier_init,
511                         NULL /* value table */
512                 };
513                 /*
514                  *   Here we use bonobo_type_unique instead of
515                  * gtk_type_unique, this auto-generates a load of
516                  * CORBA structures for us. All derived types must
517                  * use bonobo_type_unique.
518                  */
519                 type = bonobo_type_unique (
520                         PARENT_TYPE,
521                         POA_Accessibility_Magnifier__init,
522                         NULL,
523                         G_STRUCT_OFFSET (MagnifierClass, epv),
524                         &tinfo,
525                         "Magnifier");
526         }
527
528         return type;
529 }
530
531 Magnifier *
532 magnifier_new (int argc, char **argv)
533 {
534   poptContext ctx;      
535   Magnifier *magnifier =
536           MAGNIFIER (g_object_new (magnifier_get_type(), NULL));
537   ctx = poptGetContext ("magnifier",
538                         argc,
539                         (const char **)argv,
540                         magnifier_options,
541                         0);
542
543   while (poptGetNextOpt (ctx) >= 0)
544         /**/;
545
546   return magnifier;
547 }