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