1 /*
2  * Gromit-MPX -- a program for painting on the screen
3  *
4  * Gromit Copyright (C) 2000 Simon Budig <Simon.Budig@unix-ag.org>
5  *
6  * Gromit-MPX Copyright (C) 2009,2010 Christian Beier <dontmind@freeshell.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21  *
22  */
23 
24 #include <string.h>
25 #include <math.h>
26 #include <stdlib.h>
27 
28 #include "callbacks.h"
29 #include "config.h"
30 #include "input.h"
31 #include "gromit-mpx.h"
32 #include "build-config.h"
33 
34 #include "paint_cursor.xpm"
35 #include "erase_cursor.xpm"
36 
37 
38 
paint_context_new(GromitData * data,GromitPaintType type,GdkRGBA * paint_color,guint width,guint arrowsize,guint minwidth)39 GromitPaintContext *paint_context_new (GromitData *data,
40 				       GromitPaintType type,
41 				       GdkRGBA *paint_color,
42 				       guint width,
43 				       guint arrowsize,
44 				       guint minwidth)
45 {
46   GromitPaintContext *context;
47 
48   context = g_malloc (sizeof (GromitPaintContext));
49 
50   context->type = type;
51   context->width = width;
52   context->arrowsize = arrowsize;
53   context->minwidth = minwidth;
54   context->paint_color = paint_color;
55 
56 
57   context->paint_ctx = cairo_create (data->backbuffer);
58 
59   gdk_cairo_set_source_rgba(context->paint_ctx, paint_color);
60   if(!data->composited)
61     cairo_set_antialias(context->paint_ctx, CAIRO_ANTIALIAS_NONE);
62   cairo_set_line_width(context->paint_ctx, width);
63   cairo_set_line_cap(context->paint_ctx, CAIRO_LINE_CAP_ROUND);
64   cairo_set_line_join(context->paint_ctx, CAIRO_LINE_JOIN_ROUND);
65 
66   if (type == GROMIT_ERASER)
67     cairo_set_operator(context->paint_ctx, CAIRO_OPERATOR_CLEAR);
68   else
69     if (type == GROMIT_RECOLOR)
70       cairo_set_operator(context->paint_ctx, CAIRO_OPERATOR_ATOP);
71     else /* GROMIT_PEN */
72       cairo_set_operator(context->paint_ctx, CAIRO_OPERATOR_OVER);
73 
74   return context;
75 }
76 
77 
paint_context_print(gchar * name,GromitPaintContext * context)78 void paint_context_print (gchar *name,
79 			  GromitPaintContext *context)
80 {
81   g_printerr ("Tool name: \"%-20s\": ", name);
82   switch (context->type)
83   {
84     case GROMIT_PEN:
85       g_printerr ("Pen,     "); break;
86     case GROMIT_ERASER:
87       g_printerr ("Eraser,  "); break;
88     case GROMIT_RECOLOR:
89       g_printerr ("Recolor, "); break;
90     default:
91       g_printerr ("UNKNOWN, "); break;
92   }
93 
94   g_printerr ("width: %3d, ", context->width);
95   g_printerr ("minwidth: %3d, ", context->minwidth);
96   g_printerr ("arrowsize: %.2f, ", context->arrowsize);
97   g_printerr ("color: %s\n", gdk_rgba_to_string(context->paint_color));
98 }
99 
100 
paint_context_free(GromitPaintContext * context)101 void paint_context_free (GromitPaintContext *context)
102 {
103   cairo_destroy(context->paint_ctx);
104   g_free (context);
105 }
106 
107 
coord_list_prepend(GromitData * data,GdkDevice * dev,gint x,gint y,gint width)108 void coord_list_prepend (GromitData *data,
109 			 GdkDevice* dev,
110 			 gint x,
111 			 gint y,
112 			 gint width)
113 {
114   /* get the data for this device */
115   GromitDeviceData *devdata = g_hash_table_lookup(data->devdatatable, dev);
116 
117   GromitStrokeCoordinate *point;
118 
119   point = g_malloc (sizeof (GromitStrokeCoordinate));
120   point->x = x;
121   point->y = y;
122   point->width = width;
123 
124   devdata->coordlist = g_list_prepend (devdata->coordlist, point);
125 }
126 
127 
coord_list_free(GromitData * data,GdkDevice * dev)128 void coord_list_free (GromitData *data,
129 		      GdkDevice* dev)
130 {
131   /* get the data for this device */
132   GromitDeviceData *devdata = g_hash_table_lookup(data->devdatatable, dev);
133 
134   GList *ptr;
135   ptr = devdata->coordlist;
136 
137   while (ptr)
138     {
139       g_free (ptr->data);
140       ptr = ptr->next;
141     }
142 
143   g_list_free (devdata->coordlist);
144 
145   devdata->coordlist = NULL;
146 }
147 
148 
coord_list_get_arrow_param(GromitData * data,GdkDevice * dev,gint search_radius,gint * ret_width,gfloat * ret_direction)149 gboolean coord_list_get_arrow_param (GromitData *data,
150 				     GdkDevice  *dev,
151 				     gint        search_radius,
152 				     gint       *ret_width,
153 				     gfloat     *ret_direction)
154 {
155   gint x0, y0, r2, dist;
156   gboolean success = FALSE;
157   GromitStrokeCoordinate  *cur_point, *valid_point;
158   /* get the data for this device */
159   GromitDeviceData *devdata = g_hash_table_lookup(data->devdatatable, dev);
160   GList *ptr = devdata->coordlist;
161   gfloat width;
162 
163   valid_point = NULL;
164 
165   if (ptr)
166     {
167       cur_point = ptr->data;
168       x0 = cur_point->x;
169       y0 = cur_point->y;
170       r2 = search_radius * search_radius;
171       dist = 0;
172 
173       while (ptr && dist < r2)
174         {
175           ptr = ptr->next;
176           if (ptr)
177             {
178               cur_point = ptr->data;
179               dist = (cur_point->x - x0) * (cur_point->x - x0) +
180                      (cur_point->y - y0) * (cur_point->y - y0);
181               width = cur_point->width * devdata->cur_context->arrowsize;
182               if (width * 2 <= dist &&
183                   (!valid_point || valid_point->width < cur_point->width))
184                 valid_point = cur_point;
185             }
186         }
187 
188       if (valid_point)
189         {
190           *ret_width = MAX (valid_point->width * devdata->cur_context->arrowsize,
191                             2);
192           *ret_direction = atan2 (y0 - valid_point->y, x0 - valid_point->x);
193           success = TRUE;
194         }
195     }
196 
197   return success;
198 }
199 
200 
hide_window(GromitData * data)201 void hide_window (GromitData *data)
202 {
203   if (!data->hidden)
204     {
205       /* save grab state of each device */
206       GHashTableIter it;
207       gpointer value;
208       GromitDeviceData* devdata;
209       g_hash_table_iter_init (&it, data->devdatatable);
210       while (g_hash_table_iter_next (&it, NULL, &value))
211         {
212           devdata = value;
213           devdata->was_grabbed = devdata->is_grabbed;
214         }
215       data->hidden = 1;
216       release_grab (data, NULL); /* release all */
217       gtk_widget_hide (data->win);
218 
219       if(data->debug)
220         g_printerr ("DEBUG: Hiding window.\n");
221     }
222 }
223 
224 
show_window(GromitData * data)225 void show_window (GromitData *data)
226 {
227   if (data->hidden)
228     {
229       gtk_widget_show (data->win);
230       data->hidden = 0;
231 
232       /* restore grab state of each device */
233       GHashTableIter it;
234       gpointer value;
235       GromitDeviceData* devdata;
236       g_hash_table_iter_init (&it, data->devdatatable);
237       while (g_hash_table_iter_next (&it, NULL, &value))
238         {
239           devdata = value;
240           if(devdata->was_grabbed)
241             acquire_grab (data, devdata->device);
242         }
243       if(data->debug)
244         g_printerr ("DEBUG: Showing window.\n");
245     }
246   gdk_window_raise (gtk_widget_get_window(data->win));
247 }
248 
249 
250 
toggle_visibility(GromitData * data)251 void toggle_visibility (GromitData *data)
252 {
253   if (data->hidden)
254     show_window (data);
255   else
256     hide_window (data);
257 }
258 
259 
260 
clear_screen(GromitData * data)261 void clear_screen (GromitData *data)
262 {
263   cairo_t *cr = cairo_create(data->backbuffer);
264   cairo_set_source_rgba(cr, 0, 0, 0, 0);
265   cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
266   cairo_paint (cr);
267   cairo_destroy(cr);
268 
269   GdkRectangle rect = {0, 0, data->width, data->height};
270   gdk_window_invalidate_rect(gtk_widget_get_window(data->win), &rect, 0);
271 
272   if(!data->composited)
273     {
274       cairo_region_t* r = gdk_cairo_region_create_from_surface(data->backbuffer);
275       gtk_widget_shape_combine_region(data->win, r);
276       cairo_region_destroy(r);
277       // try to set transparent for input
278       r =  cairo_region_create();
279       gtk_widget_input_shape_combine_region(data->win, r);
280       cairo_region_destroy(r);
281     }
282 
283   data->painted = 0;
284 
285   if(data->debug)
286     g_printerr ("DEBUG: Cleared screen.\n");
287 }
288 
289 
reshape(gpointer user_data)290 gint reshape (gpointer user_data)
291 {
292   GromitData *data = (GromitData *) user_data;
293 
294   if (data->modified && !data->composited)
295     {
296       if (gtk_events_pending () && data->delayed < 5)
297         {
298           data->delayed++ ;
299         }
300       else
301         {
302 	  cairo_region_t* r = gdk_cairo_region_create_from_surface(data->backbuffer);
303 	  gtk_widget_shape_combine_region(data->win, r);
304 	  cairo_region_destroy(r);
305 	  // try to set transparent for input
306 	  r =  cairo_region_create();
307 	  gtk_widget_input_shape_combine_region(data->win, r);
308 	  cairo_region_destroy(r);
309 
310 	  /*
311 	    this is not needed when there is user input, i.e. when drawing,
312 	    but needed when uing the undo functionality.
313 	  */
314 	  gtk_widget_queue_draw(data->win);
315 
316           data->modified = 0;
317           data->delayed = 0;
318         }
319     }
320   return 1;
321 }
322 
323 
select_tool(GromitData * data,GdkDevice * device,GdkDevice * slave_device,guint state)324 void select_tool (GromitData *data,
325 		  GdkDevice *device,
326 		  GdkDevice *slave_device,
327 		  guint state)
328 {
329   guint buttons = 0, modifier = 0, slave_len = 0, len = 0, default_len = 0;
330   guint req_buttons = 0, req_modifier = 0;
331   guint i, j, success = 0;
332   GromitPaintContext *context = NULL;
333   guchar *slave_name;
334   guchar *name;
335   guchar *default_name;
336 
337   /* get the data for this device */
338   GromitDeviceData *devdata = g_hash_table_lookup(data->devdatatable, device);
339 
340   if (device)
341     {
342       slave_len = strlen (gdk_device_get_name(slave_device));
343       slave_name = (guchar*) g_strndup (gdk_device_get_name(slave_device), slave_len + 3);
344       len = strlen (gdk_device_get_name(device));
345       name = (guchar*) g_strndup (gdk_device_get_name(device), len + 3);
346       default_len = strlen(DEFAULT_DEVICE_NAME);
347       default_name = (guchar*) g_strndup (DEFAULT_DEVICE_NAME, default_len + 3);
348 
349 
350       /* Extract Button/Modifiers from state (see GdkModifierType) */
351       req_buttons = (state >> 8) & 31;
352 
353       req_modifier = (state >> 1) & 7;
354       if (state & GDK_SHIFT_MASK) req_modifier |= 1;
355 
356       slave_name [slave_len] = 124;
357       slave_name [slave_len+3] = 0;
358       name [len] = 124;
359       name [len+3] = 0;
360       default_name [default_len] = 124;
361       default_name [default_len+3] = 0;
362 
363       /*
364 	Iterate i up until <= req_buttons.
365 	For each i, find out if bits of i are _all_ in `req_buttons`.
366 	- If yes, lookup if there is tool and select if there is.
367 	- If no, try next i, no tool lookup.
368       */
369       context = NULL;
370       i=-1;
371       do
372         {
373           i++;
374 
375 	  /*
376 	    For all i > 0, find out if _all_ bits representing the iterator 'i'
377 	    are present in req_buttons as well. If not (none or only some are),
378 	    then go on.
379 	    The condition i==0 handles the config cases where no button is given.
380 	  */
381 	  buttons = i & req_buttons;
382 	  if(i > 0 && (buttons == 0 || buttons != i))
383 	      continue;
384 
385           j=-1;
386           do
387             {
388               j++;
389               modifier = req_modifier & ((1 << j)-1);
390               slave_name [slave_len+1] = buttons + 64;
391               slave_name [slave_len+2] = modifier + 48;
392               name [len+1] = buttons + 64;
393               name [len+2] = modifier + 48;
394               default_name [default_len+1] = buttons + 64;
395               default_name [default_len+2] = modifier + 48;
396 
397 	            if(data->debug)
398                 g_printerr("DEBUG: select_tool looking up context for '%s' attached to '%s'\n", slave_name, name);
399 
400               context = g_hash_table_lookup (data->tool_config, slave_name);
401               if(context) {
402                   if(data->debug)
403                     g_printerr("DEBUG: select_tool set context for '%s'\n", slave_name);
404                   devdata->cur_context = context;
405                   success = 1;
406               }
407               else /* try master name */
408               if ((context = g_hash_table_lookup (data->tool_config, name)))
409                 {
410                   if(data->debug)
411                     g_printerr("DEBUG: select_tool set context for '%s'\n", name);
412                   devdata->cur_context = context;
413                   success = 1;
414                 }
415               else /* try default_name */
416                 if((context = g_hash_table_lookup (data->tool_config, default_name)))
417                   {
418                     if(data->debug)
419                       g_printerr("DEBUG: select_tool set default context '%s' for '%s'\n", default_name, name);
420                     devdata->cur_context = context;
421                     success = 1;
422                   }
423 
424             }
425           while (j<=3 && req_modifier >= (1u << j));
426         }
427       while (i < req_buttons);
428 
429       g_free (name);
430       g_free (default_name);
431 
432       if (!success)
433         {
434           if (gdk_device_get_source(device) == GDK_SOURCE_ERASER)
435             devdata->cur_context = data->default_eraser;
436           else
437             devdata->cur_context = data->default_pen;
438 
439 	  if(data->debug)
440 	      g_printerr("DEBUG: select_tool set fallback context for '%s'\n", name);
441         }
442     }
443   else
444     g_printerr ("ERROR: select_tool attempted to select nonexistent device!\n");
445 
446   GdkCursor *cursor;
447   if(devdata->cur_context && devdata->cur_context->type == GROMIT_ERASER)
448     cursor = data->erase_cursor;
449   else
450     cursor = data->paint_cursor;
451 
452 
453   if(data->debug)
454     g_printerr("DEBUG: select_tool setting cursor %p\n",cursor);
455 
456 
457   //FIXME!  Should be:
458   //gdk_window_set_cursor(gtk_widget_get_window(data->win), cursor);
459   // doesn't work during a grab?
460   gdk_device_grab(device,
461   		  gtk_widget_get_window(data->win),
462   		  GDK_OWNERSHIP_NONE,
463   		  FALSE,
464   		  GROMIT_MOUSE_EVENTS,
465   		  cursor,
466   		  GDK_CURRENT_TIME);
467 
468   devdata->state = state;
469   devdata->lastslave = slave_device;
470 }
471 
472 
473 
snap_undo_state(GromitData * data)474 void snap_undo_state (GromitData *data)
475 {
476   if(data->debug)
477     g_printerr ("DEBUG: Snapping undo buffer %d.\n", data->undo_head);
478 
479   copy_surface(data->undobuffer[data->undo_head], data->backbuffer);
480 
481   // Increment head position
482   data->undo_head++;
483   if(data->undo_head >= GROMIT_MAX_UNDO)
484     data->undo_head -= GROMIT_MAX_UNDO;
485   data->undo_depth++;
486   // See if we ran out of undo levels with oldest undo overwritten
487   if(data->undo_depth > GROMIT_MAX_UNDO)
488     data->undo_depth = GROMIT_MAX_UNDO;
489   // Invalidate any redo from this position
490   data->redo_depth = 0;
491 }
492 
493 
494 
copy_surface(cairo_surface_t * dst,cairo_surface_t * src)495 void copy_surface (cairo_surface_t *dst, cairo_surface_t *src)
496 {
497   cairo_t *cr = cairo_create(dst);
498   cairo_set_source_surface(cr, src, 0, 0);
499   cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
500   cairo_paint (cr);
501   cairo_destroy(cr);
502 }
503 
504 
505 
swap_surfaces(cairo_surface_t * a,cairo_surface_t * b)506 void swap_surfaces (cairo_surface_t *a, cairo_surface_t *b)
507 {
508   int width = cairo_image_surface_get_width(a);
509   int height = cairo_image_surface_get_height(a);
510   cairo_surface_t *temp = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
511   copy_surface(temp, a);
512   copy_surface(a, b);
513   copy_surface(b, temp);
514   cairo_surface_destroy(temp);
515 }
516 
517 
518 
undo_drawing(GromitData * data)519 void undo_drawing (GromitData *data)
520 {
521   /* Swap undobuffer[head-1]->backbuffer */
522   if(data->undo_depth <= 0)
523     return;
524   data->undo_depth--;
525   data->redo_depth++;
526   if(data->redo_depth > GROMIT_MAX_UNDO)
527     data->redo_depth -= GROMIT_MAX_UNDO;
528   data->undo_head--;
529   if(data->undo_head < 0)
530     data->undo_head += GROMIT_MAX_UNDO;
531 
532   swap_surfaces(data->backbuffer, data->undobuffer[data->undo_head]);
533 
534   GdkRectangle rect = {0, 0, data->width, data->height};
535   gdk_window_invalidate_rect(gtk_widget_get_window(data->win), &rect, 0);
536 
537   data->modified = 1;
538 
539   if(data->debug)
540     g_printerr ("DEBUG: Undo drawing %d.\n", data->undo_head);
541 }
542 
543 
544 
redo_drawing(GromitData * data)545 void redo_drawing (GromitData *data)
546 {
547   if(data->redo_depth <= 0)
548     return;
549 
550   swap_surfaces(data->backbuffer, data->undobuffer[data->undo_head]);
551 
552   data->redo_depth--;
553   data->undo_depth++;
554   data->undo_head++;
555   if(data->undo_head >= GROMIT_MAX_UNDO)
556     data->undo_head -= GROMIT_MAX_UNDO;
557 
558   GdkRectangle rect = {0, 0, data->width, data->height};
559   gdk_window_invalidate_rect(gtk_widget_get_window(data->win), &rect, 0);
560 
561   data->modified = 1;
562 
563   if(data->debug)
564     g_printerr("DEBUG: Redo drawing.\n");
565 }
566 
567 
568 
draw_line(GromitData * data,GdkDevice * dev,gint x1,gint y1,gint x2,gint y2)569 void draw_line (GromitData *data,
570 		GdkDevice *dev,
571 		gint x1, gint y1,
572 		gint x2, gint y2)
573 {
574   GdkRectangle rect;
575   GromitDeviceData *devdata = g_hash_table_lookup(data->devdatatable, dev);
576 
577   rect.x = MIN (x1,x2) - data->maxwidth / 2;
578   rect.y = MIN (y1,y2) - data->maxwidth / 2;
579   rect.width = ABS (x1-x2) + data->maxwidth;
580   rect.height = ABS (y1-y2) + data->maxwidth;
581 
582   if(data->debug)
583     g_printerr("DEBUG: draw line from %d %d to %d %d\n", x1, y1, x2, y2);
584 
585   if (devdata->cur_context->paint_ctx)
586     {
587       cairo_set_line_width(devdata->cur_context->paint_ctx, data->maxwidth);
588       cairo_set_line_cap(devdata->cur_context->paint_ctx, CAIRO_LINE_CAP_ROUND);
589       cairo_set_line_join(devdata->cur_context->paint_ctx, CAIRO_LINE_JOIN_ROUND);
590 
591       cairo_move_to(devdata->cur_context->paint_ctx, x1, y1);
592       cairo_line_to(devdata->cur_context->paint_ctx, x2, y2);
593       cairo_stroke(devdata->cur_context->paint_ctx);
594 
595       data->modified = 1;
596 
597       gdk_window_invalidate_rect(gtk_widget_get_window(data->win), &rect, 0);
598     }
599 
600   data->painted = 1;
601 }
602 
603 
draw_arrow(GromitData * data,GdkDevice * dev,gint x1,gint y1,gint width,gfloat direction)604 void draw_arrow (GromitData *data,
605 		 GdkDevice *dev,
606 		 gint x1, gint y1,
607 		 gint width,
608 		 gfloat direction)
609 {
610   GdkRectangle rect;
611   GdkPoint arrowhead [4];
612 
613   /* get the data for this device */
614   GromitDeviceData *devdata = g_hash_table_lookup(data->devdatatable, dev);
615 
616   width = width / 2;
617 
618   /* I doubt that calculating the boundary box more exact is very useful */
619   rect.x = x1 - 4 * width - 1;
620   rect.y = y1 - 4 * width - 1;
621   rect.width = 8 * width + 2;
622   rect.height = 8 * width + 2;
623 
624   arrowhead [0].x = x1 + 4 * width * cos (direction);
625   arrowhead [0].y = y1 + 4 * width * sin (direction);
626 
627   arrowhead [1].x = x1 - 3 * width * cos (direction)
628                        + 3 * width * sin (direction);
629   arrowhead [1].y = y1 - 3 * width * cos (direction)
630                        - 3 * width * sin (direction);
631 
632   arrowhead [2].x = x1 - 2 * width * cos (direction);
633   arrowhead [2].y = y1 - 2 * width * sin (direction);
634 
635   arrowhead [3].x = x1 - 3 * width * cos (direction)
636                        - 3 * width * sin (direction);
637   arrowhead [3].y = y1 + 3 * width * cos (direction)
638                        - 3 * width * sin (direction);
639 
640   if (devdata->cur_context->paint_ctx)
641     {
642       cairo_set_line_width(devdata->cur_context->paint_ctx, 1);
643       cairo_set_line_cap(devdata->cur_context->paint_ctx, CAIRO_LINE_CAP_ROUND);
644       cairo_set_line_join(devdata->cur_context->paint_ctx, CAIRO_LINE_JOIN_ROUND);
645 
646       cairo_move_to(devdata->cur_context->paint_ctx, arrowhead[0].x, arrowhead[0].y);
647       cairo_line_to(devdata->cur_context->paint_ctx, arrowhead[1].x, arrowhead[1].y);
648       cairo_line_to(devdata->cur_context->paint_ctx, arrowhead[2].x, arrowhead[2].y);
649       cairo_line_to(devdata->cur_context->paint_ctx, arrowhead[3].x, arrowhead[3].y);
650       cairo_fill(devdata->cur_context->paint_ctx);
651 
652       gdk_cairo_set_source_rgba(devdata->cur_context->paint_ctx, data->black);
653 
654       cairo_move_to(devdata->cur_context->paint_ctx, arrowhead[0].x, arrowhead[0].y);
655       cairo_line_to(devdata->cur_context->paint_ctx, arrowhead[1].x, arrowhead[1].y);
656       cairo_line_to(devdata->cur_context->paint_ctx, arrowhead[2].x, arrowhead[2].y);
657       cairo_line_to(devdata->cur_context->paint_ctx, arrowhead[3].x, arrowhead[3].y);
658       cairo_line_to(devdata->cur_context->paint_ctx, arrowhead[0].x, arrowhead[0].y);
659       cairo_stroke(devdata->cur_context->paint_ctx);
660 
661       gdk_cairo_set_source_rgba(devdata->cur_context->paint_ctx, devdata->cur_context->paint_color);
662 
663       data->modified = 1;
664 
665       gdk_window_invalidate_rect(gtk_widget_get_window(data->win), &rect, 0);
666     }
667 
668   data->painted = 1;
669 }
670 
671 
672 
673 
674 /*
675  * Functions for handling various (GTK+)-Events
676  */
677 
678 
main_do_event(GdkEventAny * event,GromitData * data)679 void main_do_event (GdkEventAny *event,
680 		    GromitData  *data)
681 {
682   guint keycode = ((GdkEventKey *) event)->hardware_keycode;
683   if ((event->type == GDK_KEY_PRESS ||
684        event->type == GDK_KEY_RELEASE) &&
685       event->window == data->root &&
686       (keycode == data->hot_keycode || keycode == data->undo_keycode))
687     {
688       /* redirect the event to our main window, so that GTK+ doesn't
689        * throw it away (there is no GtkWidget for the root window...)
690        */
691       event->window = gtk_widget_get_window(data->win);
692       g_object_ref (gtk_widget_get_window(data->win));
693     }
694 
695   gtk_main_do_event ((GdkEvent *) event);
696 }
697 
698 
699 
700 
701 
setup_main_app(GromitData * data,int argc,char ** argv)702 void setup_main_app (GromitData *data, int argc, char ** argv)
703 {
704   gboolean activate;
705 
706   if(getenv("GDK_CORE_DEVICE_EVENTS")) {
707       g_printerr("GDK is set to not use the XInput extension, Gromit-MPX can not work this way.\n"
708 		 "Probably the GDK_CORE_DEVICE_EVENTS environment variable is set, try to start Gromit-MPX with this variable unset.\n");
709       exit(1);
710   }
711 
712   /*
713     HOT KEYS
714   */
715   data->hot_keyval = DEFAULT_HOTKEY;
716   data->hot_keycode = 0;
717 
718   data->undo_keyval = DEFAULT_UNDOKEY;
719   data->undo_keycode = 0;
720 
721   char *xdg_current_desktop = getenv("XDG_CURRENT_DESKTOP");
722   if (xdg_current_desktop && strcmp(xdg_current_desktop, "XFCE") == 0) {
723       /*
724 	XFCE per default grabs Ctrl-F1 to Ctrl-F12 (switch to workspace 1-12)
725 	and Alt-F9 (minimize window) which renders Gromit-MPX's default hotkey
726 	mapping unusable. Provide different defaults for that desktop environment.
727       */
728       data->hot_keyval = DEFAULT_HOTKEY_XFCE;
729       data->undo_keyval = DEFAULT_UNDOKEY_XFCE;
730       g_print("Detected XFCE, changing default hot keys to '" DEFAULT_HOTKEY_XFCE "' and '" DEFAULT_UNDOKEY_XFCE "'\n");
731   }
732 
733   /* COLOURS */
734   g_free(data->white);
735   g_free(data->black);
736   g_free(data->red);
737   data->white = g_malloc (sizeof (GdkRGBA));
738   data->black = g_malloc (sizeof (GdkRGBA));
739   data->red   = g_malloc (sizeof (GdkRGBA));
740   gdk_rgba_parse(data->white, "#FFFFFF");
741   gdk_rgba_parse(data->black, "#000000");
742   gdk_rgba_parse(data->red, "#FF0000");
743 
744 
745   /*
746      CURSORS
747   */
748   GdkPixbuf* paint_cursor_pixbuf = gdk_pixbuf_new_from_xpm_data(paint_cursor_xpm);
749   data->paint_cursor = gdk_cursor_new_from_pixbuf(data->display,
750 						  paint_cursor_pixbuf,
751 						  paint_cursor_x_hot,
752 						  paint_cursor_y_hot);
753   g_object_unref (paint_cursor_pixbuf);
754 
755   GdkPixbuf* erase_cursor_pixbuf = gdk_pixbuf_new_from_xpm_data(erase_cursor_xpm);
756   data->erase_cursor = gdk_cursor_new_from_pixbuf(data->display,
757 						  erase_cursor_pixbuf,
758 						  erase_cursor_x_hot,
759 						  erase_cursor_y_hot);
760   g_object_unref (erase_cursor_pixbuf);
761 
762 
763   /*
764     DRAWING AREA
765   */
766   /* SHAPE SURFACE*/
767   cairo_surface_destroy(data->backbuffer);
768   data->backbuffer = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, data->width, data->height);
769 
770   /*
771     UNDO STATE
772   */
773   data->undo_head = 0;
774   data->undo_depth = 0;
775   data->redo_depth = 0;
776   int i;
777   for (i = 0; i < GROMIT_MAX_UNDO; i++)
778     {
779       cairo_surface_destroy(data->undobuffer[i]);
780       data->undobuffer[i] = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, data->width, data->height);
781     }
782 
783 
784 
785   /* EVENTS */
786   gtk_widget_add_events (data->win, GROMIT_WINDOW_EVENTS);
787   g_signal_connect (data->win, "draw",
788 		    G_CALLBACK (on_expose), data);
789   g_signal_connect (data->win,"configure_event",
790 		    G_CALLBACK (on_configure), data);
791   g_signal_connect (data->win,"screen-changed",
792 		    G_CALLBACK (on_screen_changed), data);
793   g_signal_connect (data->screen,"monitors_changed",
794 		    G_CALLBACK (on_monitors_changed), data);
795   g_signal_connect (data->screen,"composited-changed",
796 		    G_CALLBACK (on_composited_changed), data);
797   g_signal_connect (gdk_display_get_device_manager (data->display), "device-added",
798                     G_CALLBACK (on_device_added), data);
799   g_signal_connect (gdk_display_get_device_manager (data->display), "device-removed",
800                     G_CALLBACK (on_device_removed), data);
801   g_signal_connect (data->win, "motion_notify_event",
802 		    G_CALLBACK (on_motion), data);
803   g_signal_connect (data->win, "button_press_event",
804 		    G_CALLBACK (on_buttonpress), data);
805   g_signal_connect (data->win, "button_release_event",
806 		    G_CALLBACK (on_buttonrelease), data);
807   /* disconnect previously defined selection handlers */
808   g_signal_handlers_disconnect_by_func (data->win,
809 					G_CALLBACK (on_clientapp_selection_get),
810 					data);
811   g_signal_handlers_disconnect_by_func (data->win,
812 					G_CALLBACK (on_clientapp_selection_received),
813 					data);
814   /* and re-connect them to mainapp functions */
815   g_signal_connect (data->win, "selection_get",
816 		    G_CALLBACK (on_mainapp_selection_get), data);
817   g_signal_connect (data->win, "selection_received",
818 		    G_CALLBACK (on_mainapp_selection_received), data);
819 
820 
821   if(!data->composited) // set initial shape
822     {
823       cairo_region_t* r = gdk_cairo_region_create_from_surface(data->backbuffer);
824       gtk_widget_shape_combine_region(data->win, r);
825       cairo_region_destroy(r);
826     }
827 
828 
829   /* reset settings from client setup */
830   gtk_selection_remove_all (data->win);
831   gtk_selection_owner_set (data->win, GA_CONTROL, GDK_CURRENT_TIME);
832 
833   gtk_selection_add_target (data->win, GA_CONTROL, GA_STATUS, 0);
834   gtk_selection_add_target (data->win, GA_CONTROL, GA_QUIT, 1);
835   gtk_selection_add_target (data->win, GA_CONTROL, GA_ACTIVATE, 2);
836   gtk_selection_add_target (data->win, GA_CONTROL, GA_DEACTIVATE, 3);
837   gtk_selection_add_target (data->win, GA_CONTROL, GA_TOGGLE, 4);
838   gtk_selection_add_target (data->win, GA_CONTROL, GA_VISIBILITY, 5);
839   gtk_selection_add_target (data->win, GA_CONTROL, GA_CLEAR, 6);
840   gtk_selection_add_target (data->win, GA_CONTROL, GA_RELOAD, 7);
841   gtk_selection_add_target (data->win, GA_CONTROL, GA_UNDO, 8);
842   gtk_selection_add_target (data->win, GA_CONTROL, GA_REDO, 9);
843 
844 
845 
846 
847   /*
848    * Parse Config file
849    */
850   data->tool_config = g_hash_table_new (g_str_hash, g_str_equal);
851   parse_config (data);
852   g_hash_table_foreach (data->tool_config, parse_print_help, NULL);
853 
854   /*
855     parse key file
856   */
857   read_keyfile(data);
858 
859   /*
860     parse cmdline
861   */
862   activate = app_parse_args (argc, argv, data);
863 
864   // might have been in key file
865   gtk_widget_set_opacity(data->win, data->opacity);
866 
867   /*
868      FIND HOTKEY KEYCODE
869   */
870   if (data->hot_keyval)
871     {
872       GdkKeymap    *keymap;
873       GdkKeymapKey *keys;
874       gint          n_keys;
875       guint         keyval;
876 
877       if (strlen (data->hot_keyval) > 0 &&
878           strcasecmp (data->hot_keyval, "none") != 0)
879         {
880           keymap = gdk_keymap_get_for_display (data->display);
881           keyval = gdk_keyval_from_name (data->hot_keyval);
882 
883           if (!keyval || !gdk_keymap_get_entries_for_keyval (keymap, keyval,
884                                                              &keys, &n_keys))
885             {
886               g_printerr ("cannot find the key \"%s\"\n", data->hot_keyval);
887               exit (1);
888             }
889 
890           data->hot_keycode = keys[0].keycode;
891           g_free (keys);
892         }
893     }
894 
895   /*
896      FIND UNDOKEY KEYCODE
897   */
898   if (data->undo_keyval)
899     {
900       GdkKeymap    *keymap;
901       GdkKeymapKey *keys;
902       gint          n_keys;
903       guint         keyval;
904 
905       if (strlen (data->undo_keyval) > 0 &&
906           strcasecmp (data->undo_keyval, "none") != 0)
907         {
908           keymap = gdk_keymap_get_for_display (data->display);
909           keyval = gdk_keyval_from_name (data->undo_keyval);
910 
911           if (!keyval || !gdk_keymap_get_entries_for_keyval (keymap, keyval,
912                                                              &keys, &n_keys))
913             {
914               g_printerr ("cannot find the key \"%s\"\n", data->undo_keyval);
915               exit (1);
916             }
917 
918           data->undo_keycode = keys[0].keycode;
919           g_free (keys);
920         }
921     }
922 
923 
924   /*
925      INPUT DEVICES
926   */
927   data->devdatatable = g_hash_table_new(NULL, NULL);
928   setup_input_devices (data);
929 
930 
931 
932   gtk_widget_show_all (data->win);
933 
934   data->painted = 0;
935   hide_window (data);
936 
937   data->timeout_id = g_timeout_add (20, reshape, data);
938 
939   data->modified = 0;
940 
941   data->default_pen = paint_context_new (data, GROMIT_PEN,
942 					 data->red, 7, 0, 1);
943   data->default_eraser = paint_context_new (data, GROMIT_ERASER,
944 					    data->red, 75, 0, 1);
945 
946 
947 
948   gdk_event_handler_set ((GdkEventFunc) main_do_event, data, NULL);
949   gtk_key_snooper_install (snoop_key_press, data);
950 
951   if (activate)
952     acquire_grab (data, NULL); /* grab all */
953 
954   /*
955      TRAY ICON
956   */
957   data->trayicon = app_indicator_new (PACKAGE_NAME,
958 				      "net.christianbeier.Gromit-MPX",
959 				      APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
960 
961   app_indicator_set_status (data->trayicon, APP_INDICATOR_STATUS_ACTIVE);
962 
963 
964 
965   /* create the menu */
966   GtkWidget *menu = gtk_menu_new ();
967 
968   char labelBuf[128];
969   /* Create the menu items */
970   snprintf(labelBuf, sizeof(labelBuf), "Toggle Painting (%s)", data->hot_keyval);
971   GtkWidget* toggle_paint_item = gtk_menu_item_new_with_label (labelBuf);
972   snprintf(labelBuf, sizeof(labelBuf), "Clear Screen (SHIFT-%s)", data->hot_keyval);
973   GtkWidget* clear_item = gtk_menu_item_new_with_label (labelBuf);
974   snprintf(labelBuf, sizeof(labelBuf), "Toggle Visibility (CTRL-%s)", data->hot_keyval);
975   GtkWidget* toggle_vis_item = gtk_menu_item_new_with_label (labelBuf);
976   GtkWidget* thicker_lines_item = gtk_menu_item_new_with_label ("Thicker Lines");
977   GtkWidget* thinner_lines_item = gtk_menu_item_new_with_label ("Thinner Lines");
978   GtkWidget* opacity_bigger_item = gtk_menu_item_new_with_label ("Bigger Opacity");
979   GtkWidget* opacity_lesser_item = gtk_menu_item_new_with_label ("Lesser Opacity");
980   snprintf(labelBuf, sizeof(labelBuf), "Undo (%s)", data->undo_keyval);
981   GtkWidget* undo_item = gtk_menu_item_new_with_label (labelBuf);
982   snprintf(labelBuf, sizeof(labelBuf), "Redo (SHIFT-%s)", data->undo_keyval);
983   GtkWidget* redo_item = gtk_menu_item_new_with_label (labelBuf);
984 
985   GtkWidget* sep_item = gtk_separator_menu_item_new();
986   GtkWidget* intro_item = gtk_menu_item_new_with_mnemonic("_Introduction");
987   GtkWidget* support_item = gtk_menu_item_new_with_mnemonic("_Support Gromit-MPX");
988   GtkWidget* about_item = gtk_menu_item_new_with_mnemonic("_About");
989   snprintf(labelBuf, sizeof(labelBuf), "_Quit (ALT-%s)", data->hot_keyval);
990   GtkWidget* quit_item = gtk_menu_item_new_with_mnemonic(labelBuf);
991 
992 
993   /* Add them to the menu */
994   gtk_menu_shell_append (GTK_MENU_SHELL (menu), toggle_paint_item);
995   gtk_menu_shell_append (GTK_MENU_SHELL (menu), clear_item);
996   gtk_menu_shell_append (GTK_MENU_SHELL (menu), toggle_vis_item);
997   gtk_menu_shell_append (GTK_MENU_SHELL (menu), thicker_lines_item);
998   gtk_menu_shell_append (GTK_MENU_SHELL (menu), thinner_lines_item);
999   gtk_menu_shell_append (GTK_MENU_SHELL (menu), opacity_bigger_item);
1000   gtk_menu_shell_append (GTK_MENU_SHELL (menu), opacity_lesser_item);
1001   gtk_menu_shell_append (GTK_MENU_SHELL (menu), undo_item);
1002   gtk_menu_shell_append (GTK_MENU_SHELL (menu), redo_item);
1003 
1004   gtk_menu_shell_append (GTK_MENU_SHELL (menu), sep_item);
1005   gtk_menu_shell_append (GTK_MENU_SHELL (menu), intro_item);
1006   gtk_menu_shell_append (GTK_MENU_SHELL (menu), support_item);
1007   gtk_menu_shell_append (GTK_MENU_SHELL (menu), about_item);
1008   gtk_menu_shell_append (GTK_MENU_SHELL (menu), quit_item);
1009 
1010 
1011   /* Find out if the D-Bus name org.kde.StatusNotifierWatcher is owned. */
1012   GDBusConnection *dbus_conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
1013   GVariant *is_status_notifier_watcher_owned = g_dbus_connection_call_sync(dbus_conn,
1014 						 "org.freedesktop.DBus",
1015 						 "/org/freedesktop/DBus",
1016 						 "org.freedesktop.DBus",
1017 						 "GetConnectionUnixProcessID",
1018 						 g_variant_new("(s)", "org.kde.StatusNotifierWatcher"),
1019 						 G_VARIANT_TYPE("(u)"),
1020 						 G_DBUS_CALL_FLAGS_NONE,
1021 						 -1,
1022 						 NULL,
1023 						 NULL);
1024   if(data->debug)
1025       g_printerr("DEBUG: org.kde.StatusNotifierWatcher is %s\n", is_status_notifier_watcher_owned ? "owned" : "not owned");
1026 
1027   /* Attach the callback functions to the respective activate signal */
1028   if (is_status_notifier_watcher_owned) {
1029       // KStatusNotifier does not handle the device-specific "button-press-event" from a menu
1030       g_signal_connect(G_OBJECT (toggle_paint_item), "activate",
1031 		       G_CALLBACK (on_toggle_paint_all),
1032 		       data);
1033   } else {
1034       g_signal_connect(toggle_paint_item, "button-press-event",
1035 		       G_CALLBACK(on_toggle_paint), data);
1036   }
1037   g_signal_connect(G_OBJECT (clear_item), "activate",
1038 		   G_CALLBACK (on_clear),
1039 		   data);
1040   g_signal_connect(G_OBJECT (toggle_vis_item), "activate",
1041 		   G_CALLBACK (on_toggle_vis),
1042 		   data);
1043   g_signal_connect(G_OBJECT (thicker_lines_item), "activate",
1044 		   G_CALLBACK (on_thicker_lines),
1045 		   data);
1046   g_signal_connect(G_OBJECT (thinner_lines_item), "activate",
1047 		   G_CALLBACK (on_thinner_lines),
1048 		   data);
1049   g_signal_connect(G_OBJECT (opacity_bigger_item), "activate",
1050 		   G_CALLBACK (on_opacity_bigger),
1051 		   data);
1052   g_signal_connect(G_OBJECT (opacity_lesser_item), "activate",
1053 		   G_CALLBACK (on_opacity_lesser),
1054 		   data);
1055   g_signal_connect(G_OBJECT (undo_item), "activate",
1056 		   G_CALLBACK (on_undo),
1057 		   data);
1058   g_signal_connect(G_OBJECT (redo_item), "activate",
1059 		   G_CALLBACK (on_redo),
1060 		   data);
1061 
1062   g_signal_connect(G_OBJECT (intro_item), "activate",
1063 		   G_CALLBACK (on_intro),
1064 		   data);
1065   g_signal_connect(G_OBJECT (about_item), "activate",
1066 		   G_CALLBACK (on_about),
1067 		   NULL);
1068   g_signal_connect(G_OBJECT (quit_item), "activate",
1069 		   G_CALLBACK (gtk_main_quit),
1070 		   NULL);
1071 
1072 
1073   /* We do need to show menu items */
1074   gtk_widget_show (toggle_paint_item);
1075   gtk_widget_show (clear_item);
1076   gtk_widget_show (toggle_vis_item);
1077   gtk_widget_show (thicker_lines_item);
1078   gtk_widget_show (thinner_lines_item);
1079   gtk_widget_show (opacity_bigger_item);
1080   gtk_widget_show (opacity_lesser_item);
1081   gtk_widget_show (undo_item);
1082   gtk_widget_show (redo_item);
1083 
1084   gtk_widget_show (sep_item);
1085   gtk_widget_show (intro_item);
1086   gtk_widget_show (support_item);
1087   gtk_widget_show (about_item);
1088   gtk_widget_show (quit_item);
1089 
1090 
1091   app_indicator_set_menu (data->trayicon, GTK_MENU(menu));
1092 
1093   /*
1094     Build the support menu
1095    */
1096   GtkWidget *support_menu = gtk_menu_new ();
1097   gtk_menu_item_set_submenu(GTK_MENU_ITEM(support_item), support_menu);
1098 
1099   GtkWidget* support_liberapay_item = gtk_menu_item_new_with_label("Via LiberaPay");
1100   GtkWidget* support_patreon_item = gtk_menu_item_new_with_label("Via Patreon");
1101   GtkWidget* support_paypal_item = gtk_menu_item_new_with_label("Via PayPal");
1102 
1103   gtk_menu_shell_append (GTK_MENU_SHELL (support_menu), support_liberapay_item);
1104   gtk_menu_shell_append (GTK_MENU_SHELL (support_menu), support_patreon_item);
1105   gtk_menu_shell_append (GTK_MENU_SHELL (support_menu), support_paypal_item);
1106 
1107   g_signal_connect(G_OBJECT (support_liberapay_item), "activate",
1108 		   G_CALLBACK (on_support_liberapay),
1109 		   data);
1110   g_signal_connect(G_OBJECT (support_patreon_item), "activate",
1111 		   G_CALLBACK (on_support_patreon),
1112 		   data);
1113   g_signal_connect(G_OBJECT (support_paypal_item), "activate",
1114 		   G_CALLBACK (on_support_paypal),
1115 		   data);
1116 
1117   gtk_widget_show(support_liberapay_item);
1118   gtk_widget_show(support_patreon_item);
1119   gtk_widget_show(support_paypal_item);
1120 
1121 
1122   if(data->show_intro_on_startup)
1123       on_intro(NULL, data);
1124 }
1125 
1126 
parse_print_help(gpointer key,gpointer value,gpointer user_data)1127 void parse_print_help (gpointer key, gpointer value, gpointer user_data)
1128 {
1129   paint_context_print ((gchar *) key, (GromitPaintContext *) value);
1130 }
1131 
1132 
app_parse_args(int argc,char ** argv,GromitData * data)1133 int app_parse_args (int argc, char **argv, GromitData *data)
1134 {
1135    gint      i;
1136    gchar    *arg;
1137    gboolean  wrong_arg = FALSE;
1138    gboolean  activate = FALSE;
1139 
1140    for (i=1; i < argc ; i++)
1141      {
1142        arg = argv[i];
1143        if (strcmp (arg, "-a") == 0 ||
1144            strcmp (arg, "--active") == 0)
1145          {
1146            activate = TRUE;
1147          }
1148        else if (strcmp (arg, "-d") == 0 ||
1149                 strcmp (arg, "--debug") == 0)
1150          {
1151            data->debug = 1;
1152          }
1153        else if (strcmp (arg, "-k") == 0 ||
1154                 strcmp (arg, "--key") == 0)
1155          {
1156            if (i+1 < argc)
1157              {
1158                data->hot_keyval = argv[i+1];
1159                data->hot_keycode = 0;
1160                i++;
1161              }
1162            else
1163              {
1164                g_printerr ("-k requires an Key-Name as argument\n");
1165                wrong_arg = TRUE;
1166              }
1167          }
1168        else if (strcmp (arg, "-K") == 0 ||
1169                 strcmp (arg, "--keycode") == 0)
1170          {
1171            if (i+1 < argc && atoi (argv[i+1]) > 0)
1172              {
1173                data->hot_keyval = NULL;
1174                data->hot_keycode = atoi (argv[i+1]);
1175                i++;
1176              }
1177            else
1178              {
1179                g_printerr ("-K requires an keycode > 0 as argument\n");
1180                wrong_arg = TRUE;
1181              }
1182          }
1183       else if (strcmp (arg, "-o") == 0 ||
1184                 strcmp (arg, "--opacity") == 0)
1185          {
1186            if (i+1 < argc && strtod (argv[i+1], NULL) >= 0.0 && strtod (argv[i+1], NULL) <= 1.0)
1187              {
1188                data->opacity = strtod (argv[i+1], NULL);
1189                g_printerr ("Opacity set to: %.2f\n", data->opacity);
1190                gtk_widget_set_opacity(data->win, data->opacity);
1191                i++;
1192              }
1193            else
1194              {
1195                g_printerr ("-o requires an opacity >=0 and <=1 as argument\n");
1196                wrong_arg = TRUE;
1197              }
1198          }
1199        else if (strcmp (arg, "-u") == 0 ||
1200                 strcmp (arg, "--undo-key") == 0)
1201          {
1202            if (i+1 < argc)
1203              {
1204                data->undo_keyval = argv[i+1];
1205                data->undo_keycode = 0;
1206                i++;
1207              }
1208            else
1209              {
1210                g_printerr ("-u requires an Key-Name as argument\n");
1211                wrong_arg = TRUE;
1212              }
1213          }
1214        else if (strcmp (arg, "-U") == 0 ||
1215                 strcmp (arg, "--undo-keycode") == 0)
1216          {
1217            if (i+1 < argc && atoi (argv[i+1]) > 0)
1218              {
1219                data->undo_keyval = NULL;
1220                data->undo_keycode = atoi (argv[i+1]);
1221                i++;
1222              }
1223            else
1224              {
1225                g_printerr ("-U requires an keycode > 0 as argument\n");
1226                wrong_arg = TRUE;
1227              }
1228          }
1229        else if (strcmp (arg, "-V") == 0 ||
1230 		strcmp (arg, "--version") == 0)
1231          {
1232 	     g_print("Gromit-MPX " VERSION "\n");
1233 	     exit(0);
1234          }
1235        else
1236          {
1237            g_printerr ("Unknown Option for Gromit-MPX startup: \"%s\"\n", arg);
1238            wrong_arg = TRUE;
1239          }
1240 
1241        if (wrong_arg)
1242          {
1243            g_printerr ("Please see the Gromit-MPX manpage for the correct usage\n");
1244            exit (1);
1245          }
1246      }
1247 
1248    return activate;
1249 }
1250 
1251 
1252 /*
1253  * Main programs
1254  */
1255 
main_client(int argc,char ** argv,GromitData * data)1256 int main_client (int argc, char **argv, GromitData *data)
1257 {
1258    GdkAtom   action = GDK_NONE;
1259    gint      i;
1260    gchar    *arg;
1261    gboolean  wrong_arg = FALSE;
1262 
1263    for (i=1; i < argc ; i++)
1264      {
1265        arg = argv[i];
1266        action = GDK_NONE;
1267        if (strcmp (arg, "-d") == 0 ||
1268            strcmp (arg, "--debug") == 0)
1269          {
1270            data->debug = 1;
1271          }
1272        else if (strcmp (arg, "-t") == 0 ||
1273            strcmp (arg, "--toggle") == 0)
1274          {
1275            action = GA_TOGGLE;
1276            if (i+1 < argc && argv[i+1][0] != '-') /* there is an id supplied */
1277              {
1278                data->clientdata  = argv[i+1];
1279                ++i;
1280              }
1281            else
1282              data->clientdata = "-1"; /* default to grab all */
1283          }
1284        else if (strcmp (arg, "-v") == 0 ||
1285                 strcmp (arg, "--visibility") == 0)
1286          {
1287            action = GA_VISIBILITY;
1288          }
1289        else if (strcmp (arg, "-q") == 0 ||
1290                 strcmp (arg, "--quit") == 0)
1291          {
1292            action = GA_QUIT;
1293          }
1294        else if (strcmp (arg, "-c") == 0 ||
1295                 strcmp (arg, "--clear") == 0)
1296          {
1297            action = GA_CLEAR;
1298          }
1299        else if (strcmp (arg, "-r") == 0 ||
1300                 strcmp (arg, "--reload") == 0)
1301          {
1302            action = GA_RELOAD;
1303          }
1304        else if (strcmp (arg, "-z") == 0 ||
1305                 strcmp (arg, "--undo") == 0)
1306          {
1307            action = GA_UNDO;
1308          }
1309        else if (strcmp (arg, "-y") == 0 ||
1310                 strcmp (arg, "--redo") == 0)
1311          {
1312            action = GA_REDO;
1313          }
1314        else
1315          {
1316            g_printerr ("Unknown Option to control a running Gromit-MPX process: \"%s\"\n", arg);
1317            wrong_arg = TRUE;
1318          }
1319 
1320        if (!wrong_arg && action != GDK_NONE)
1321          {
1322            gtk_selection_convert (data->win, GA_CONTROL,
1323                                   action, GDK_CURRENT_TIME);
1324            gtk_main ();  /* Wait for the response */
1325          }
1326        else if(wrong_arg)
1327          {
1328            g_printerr ("Please see the Gromit-MPX manpage for the correct usage\n");
1329            return 1;
1330          }
1331      }
1332 
1333 
1334    return 0;
1335 }
1336 
main(int argc,char ** argv)1337 int main (int argc, char **argv)
1338 {
1339   GromitData *data;
1340 
1341   /*
1342       we run okay under XWayland, but not native Wayland
1343   */
1344   gdk_set_allowed_backends ("x11");
1345 
1346   gtk_init (&argc, &argv);
1347   data = g_malloc0(sizeof (GromitData));
1348 
1349   /*
1350      init basic stuff
1351   */
1352   data->display = gdk_display_get_default ();
1353   data->screen = gdk_display_get_default_screen (data->display);
1354   data->xinerama = gdk_screen_get_n_monitors (data->screen) > 1;
1355   data->composited = gdk_screen_is_composited (data->screen);
1356   data->root = gdk_screen_get_root_window (data->screen);
1357   data->width = gdk_screen_get_width (data->screen);
1358   data->height = gdk_screen_get_height (data->screen);
1359   data->opacity = DEFAULT_OPACITY;
1360 
1361   /*
1362     init our window
1363   */
1364   data->win = gtk_window_new (GTK_WINDOW_POPUP);
1365   // this trys to set an alpha channel
1366   on_screen_changed(data->win, NULL, data);
1367 
1368   gtk_window_fullscreen(GTK_WINDOW(data->win));
1369   gtk_window_set_skip_taskbar_hint(GTK_WINDOW(data->win), TRUE);
1370   gtk_widget_set_opacity(data->win, data->opacity);
1371   gtk_widget_set_app_paintable (data->win, TRUE);
1372   gtk_window_set_decorated (GTK_WINDOW (data->win), FALSE);
1373 
1374   gtk_widget_set_size_request (GTK_WIDGET (data->win), data->width, data->height-1);
1375 
1376   gtk_widget_add_events (data->win, GROMIT_WINDOW_EVENTS);
1377 
1378   g_signal_connect (data->win, "delete-event", gtk_main_quit, NULL);
1379   g_signal_connect (data->win, "destroy", gtk_main_quit, NULL);
1380   /* the selection event handlers will be overwritten if we become a mainapp */
1381   g_signal_connect (data->win, "selection_received",
1382 		    G_CALLBACK (on_clientapp_selection_received), data);
1383   g_signal_connect (data->win, "selection_get",
1384 		    G_CALLBACK (on_clientapp_selection_get), data);
1385 
1386   gtk_widget_realize (data->win);
1387 
1388   // try to set transparent for input
1389   cairo_region_t* r =  cairo_region_create();
1390   gtk_widget_input_shape_combine_region(data->win, r);
1391   cairo_region_destroy(r);
1392 
1393   gtk_selection_owner_set (data->win, GA_DATA, GDK_CURRENT_TIME);
1394   gtk_selection_add_target (data->win, GA_DATA, GA_TOGGLEDATA, 1007);
1395 
1396 
1397 
1398   /* Try to get a status message. If there is a response gromit
1399    * is already active.
1400    */
1401 
1402   gtk_selection_convert (data->win, GA_CONTROL, GA_STATUS,
1403                          GDK_CURRENT_TIME);
1404   gtk_main ();  /* Wait for the response */
1405 
1406   if (data->client)
1407     return main_client (argc, argv, data);
1408 
1409   /* Main application */
1410   setup_main_app (data, argc, argv);
1411   gtk_main ();
1412   shutdown_input_devices(data);
1413   write_keyfile(data); // save keyfile config
1414   g_free (data);
1415   return 0;
1416 }
1417 
indicate_active(GromitData * data,gboolean YESNO)1418 void indicate_active(GromitData *data, gboolean YESNO)
1419 {
1420     if(YESNO)
1421 	app_indicator_set_icon(data->trayicon, "net.christianbeier.Gromit-MPX.active");
1422     else
1423 	app_indicator_set_icon(data->trayicon, "net.christianbeier.Gromit-MPX");
1424 }
1425