1 /*
2  * florence - Florence is a simple virtual keyboard for Gnome.
3 
4  * Copyright (C) 2012 François Agrech
5 
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10 
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15 
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 
20 */
21 
22 #include "system.h"
23 #include "view.h"
24 #include "trace.h"
25 #include "settings.h"
26 #include "keyboard.h"
27 #include "tools.h"
28 #include <gtk/gtk.h>
29 #include <gdk/gdkx.h>
30 #include <cairo/cairo-xlib.h>
31 #include <X11/Xlib.h>
32 #include <X11/Xutil.h>
33 #include <X11/extensions/shape.h>
34 #include <X11/extensions/Xcomposite.h>
35 
36 
37 /* Show the view next to the accessible object if specified. */
38 #ifdef AT_SPI
39 #ifdef ENABLE_AT_SPI2
view_show(struct view * view,AtspiAccessible * object)40 void view_show (struct view *view, AtspiAccessible *object)
41 #else
42 void view_show (struct view *view, Accessible *object)
43 #endif
44 #else
45 void view_show (struct view *view)
46 #endif
47 {
48 	START_FUNC
49 	gtk_widget_show(GTK_WIDGET(view->window));
50 	/* Some winwow managers forget it */
51 	gtk_window_set_keep_above(view->window, TRUE);
52 	gtk_window_set_urgency_hint(view->window, TRUE);
53 	/* reposition the window */
54 	gtk_window_move(view->window, settings_get_int(SETTINGS_XPOS), settings_get_int(SETTINGS_YPOS));
55 #ifdef AT_SPI
56 	/* positionnement intelligent */
57 	if (settings_get_bool(SETTINGS_AUTO_HIDE) &&
58 		settings_get_bool(SETTINGS_MOVE_TO_WIDGET) && object) {
59 		tools_window_move(view->window, object);
60 	}
61 #endif
62 	END_FUNC
63 }
64 
65 /* Hides the view */
view_hide(struct view * view)66 void view_hide (struct view *view)
67 {
68 	START_FUNC
69 	gtk_widget_hide(GTK_WIDGET(view->window));
70 	END_FUNC
71 }
72 
view_on_destroy(gpointer user_data)73 void view_on_destroy(gpointer user_data)
74 {
75 	START_FUNC
76 	struct view *view=(struct view *)user_data;
77 	view->window=NULL;
78 	END_FUNC
79 }
80 
81 /* destroy the view */
view_destroy(struct view * view)82 void view_destroy(struct view *view)
83 {
84 	START_FUNC
85 	if (view->window) {
86 		GtkWidget *window=GTK_WIDGET(view->window);
87 		view->window=NULL;
88 		gtk_widget_destroy(window);
89 	}
90 	END_FUNC
91 }
92 
93 /* resize the window */
view_resize(struct view * view)94 void view_resize (struct view *view)
95 {
96 	START_FUNC
97 	GdkRectangle rect;
98 	GdkGeometry hints;
99 	hints.win_gravity=GDK_GRAVITY_NORTH_WEST;
100 	if (settings_get_bool(SETTINGS_RESIZABLE)) {
101 		gtk_window_set_resizable(view->window, TRUE);
102 		if (settings_get_bool(SETTINGS_KEEP_RATIO)) {
103 			hints.min_aspect=view->vwidth/view->vheight;
104 			hints.max_aspect=view->vwidth/view->vheight;
105 			gtk_window_set_geometry_hints(view->window, NULL, &hints,
106 				GDK_HINT_ASPECT|GDK_HINT_WIN_GRAVITY);
107 		} else {
108 			gtk_window_set_geometry_hints(view->window, NULL, &hints,
109 				GDK_HINT_WIN_GRAVITY);
110 		}
111 		/* Do not call configure signal handler */
112 		if (view->configure_handler) g_signal_handler_disconnect(G_OBJECT(view->window), view->configure_handler);
113 		view->configure_handler=0;
114 		gtk_window_resize(view->window, view->width, view->height);
115 	} else {
116 		gtk_window_set_geometry_hints(view->window, NULL, &hints,
117 			GDK_HINT_WIN_GRAVITY);
118 		gtk_window_set_resizable(view->window, FALSE);
119 		gtk_widget_set_size_request(GTK_WIDGET(view->window),
120 			view->width, view->height);
121 	}
122 	/* refresh the view */
123 	if (view->window && gtk_widget_get_window(GTK_WIDGET(view->window))) {
124 		rect.x=0; rect.y=0;
125 		rect.width=view->width; rect.height=view->height;
126 		gdk_window_invalidate_rect(gtk_widget_get_window(GTK_WIDGET(view->window)), &rect, TRUE);
127 	}
128 	END_FUNC
129 }
130 
131 /* draws the background of florence */
view_draw(struct view * view,cairo_t * cairoctx,cairo_surface_t ** surface,enum style_class class)132 void view_draw (struct view *view, cairo_t *cairoctx, cairo_surface_t **surface, enum style_class class)
133 {
134 	START_FUNC
135 	GSList *list=view->keyboards;
136 	struct keyboard *keyboard;
137 	cairo_t *offscreen;
138 
139 	/* create the surface */
140 	if (!*surface) *surface=cairo_surface_create_similar(cairo_get_target(cairoctx),
141 		CAIRO_CONTENT_COLOR_ALPHA, view->width, view->height);
142 	offscreen=cairo_create(*surface);
143 	cairo_set_source_rgba(offscreen, 0.0, 0.0, 0.0, 0.0);
144 	cairo_set_operator(offscreen, CAIRO_OPERATOR_SOURCE);
145 	cairo_paint(offscreen);
146 	cairo_set_operator(offscreen, CAIRO_OPERATOR_OVER);
147 
148 	/* browse the keyboards */
149 	cairo_save(offscreen);
150 	cairo_scale(offscreen, view->scalex, view->scaley);
151 	while (list)
152 	{
153 		keyboard=(struct keyboard *)list->data;
154 		if (keyboard_activated(keyboard)) {
155 			/* actual draw */
156 			switch(class) {
157 				case STYLE_SHAPE:
158 					keyboard_background_draw(keyboard, offscreen, view->style, view->status);
159 					if (keyboard->under) {
160 						cairo_set_source_rgba(offscreen, 0.0, 0.0, 0.0, 0.75);
161 						cairo_set_operator(offscreen, CAIRO_OPERATOR_OVER);
162 						cairo_rectangle(offscreen, keyboard->xpos, keyboard->ypos,
163 							keyboard_get_width(keyboard), keyboard_get_height(keyboard));
164 						cairo_fill(offscreen);
165 					}
166 					break;
167 				case STYLE_SYMBOL:
168 					keyboard_symbols_draw(keyboard, offscreen, view->style, view->status);
169 					break;
170 			}
171 		}
172 		list=list->next;
173 	}
174 	cairo_destroy(offscreen);
175 	END_FUNC
176 }
177 
178 /* draws the background of florence */
view_background_draw(struct view * view,cairo_t * cairoctx)179 void view_background_draw (struct view *view, cairo_t *cairoctx)
180 {
181 	START_FUNC
182 	view_draw(view, cairoctx, &(view->background), STYLE_SHAPE);
183 	END_FUNC
184 }
185 
186 /* draws the symbols */
view_symbols_draw(struct view * view,cairo_t * cairoctx)187 void view_symbols_draw (struct view *view, cairo_t *cairoctx)
188 {
189 	START_FUNC
190 	view_draw(view, cairoctx, &(view->symbols), STYLE_SYMBOL);
191 	END_FUNC
192 }
193 
194 /* update the keyboard positions */
view_keyboards_set_pos(struct view * view,struct keyboard * over)195 void view_keyboards_set_pos(struct view *view, struct keyboard *over)
196 {
197 	START_FUNC
198 	GSList *list=view->keyboards;
199 	struct keyboard *keyboard;
200 	gdouble width=0.0, height=0.0, xoffset=0.0, yoffset=0.0;
201 	gdouble x=0.0, y=0.0;
202 
203 	/* browse the keyboards */
204 	while (list)
205 	{
206 		keyboard=(struct keyboard *)list->data;
207 		if (keyboard_activated(keyboard)) {
208 			/* get the position to draw the keyboard */
209 			switch (keyboard_get_placement(keyboard)) {
210 				case LAYOUT_VOID:
211 					width=keyboard_get_width(keyboard);
212 					height=keyboard_get_height(keyboard);
213 					xoffset=yoffset=0.0;
214 					x=y=0.0;
215 					if (over) keyboard_set_under(keyboard); else keyboard_set_over(keyboard);
216 					break;
217 				case LAYOUT_TOP:
218 					yoffset+=keyboard_get_height(keyboard);
219 					x=0.0; y=-yoffset;
220 					if (over) keyboard_set_under(keyboard); else keyboard_set_over(keyboard);
221 					break;
222 				case LAYOUT_BOTTOM:
223 					x=0.0; y=height;
224 					height+=keyboard_get_height(keyboard);
225 					if (over) keyboard_set_under(keyboard); else keyboard_set_over(keyboard);
226 					break;
227 				case LAYOUT_LEFT:
228 					xoffset+=keyboard_get_width(keyboard);
229 					x=-xoffset; y=0.0;
230 					if (over) keyboard_set_under(keyboard); else keyboard_set_over(keyboard);
231 					break;
232 				case LAYOUT_RIGHT:
233 					x=width; y=0.0;
234 					width+=keyboard_get_width(keyboard);
235 					if (over) keyboard_set_under(keyboard); else keyboard_set_over(keyboard);
236 					break;
237 				case LAYOUT_OVER:
238 					if (keyboard_get_width(keyboard)>width) width=keyboard_get_width(keyboard);
239 					if (keyboard_get_height(keyboard)>height) height=keyboard_get_height(keyboard);
240 					x=(width-view->xoffset-keyboard_get_width(keyboard))/2.0;
241 					y=(height-view->yoffset-keyboard_get_height(keyboard))/2.0;
242 					if (over==keyboard) keyboard_set_over(keyboard); else keyboard_set_under(keyboard);
243 					break;
244 			}
245 			keyboard_set_pos(keyboard, x+view->xoffset, y+view->yoffset);
246 		}
247 		list = list->next;
248 	}
249 	END_FUNC
250 }
251 
252 /* calculate the dimensions of Florence */
view_set_dimensions(struct view * view)253 void view_set_dimensions(struct view *view)
254 {
255 	START_FUNC
256 	GSList *list=view->keyboards;
257 	struct keyboard *keyboard;
258 	struct keyboard *over=NULL;
259 
260 	while (list)
261 	{
262 		keyboard=(struct keyboard *)list->data;
263 		if (keyboard_activated(keyboard)) {
264 			switch (keyboard_get_placement(keyboard)) {
265 				case LAYOUT_VOID:
266 					view->vwidth=keyboard_get_width(keyboard);
267 					view->vheight=keyboard_get_height(keyboard);
268 					view->xoffset=view->yoffset=0;
269 					break;
270 				case LAYOUT_TOP:
271 					view->vheight+=(view->yoffset+=keyboard_get_height(keyboard));
272 					break;
273 				case LAYOUT_BOTTOM:
274 					view->vheight+=keyboard_get_height(keyboard);
275 					break;
276 				case LAYOUT_LEFT:
277 					view->vwidth+=(view->xoffset+=keyboard_get_width(keyboard));
278 					break;
279 				case LAYOUT_RIGHT:
280 					view->vwidth+=keyboard_get_width(keyboard);
281 					break;
282 				case LAYOUT_OVER:
283 					if (keyboard_get_width(keyboard)>view->vwidth) view->vwidth=keyboard_get_width(keyboard);
284 					if (keyboard_get_height(keyboard)>view->vheight) view->vheight=keyboard_get_height(keyboard);
285 					over=keyboard;
286 					break;
287 			}
288 		}
289 		list = list->next;
290 	}
291 	view->width=(guint)(view->vwidth*view->scalex);
292 	view->height=(guint)(view->vheight*view->scaley);
293 	view_keyboards_set_pos(view, over);
294 	END_FUNC
295 }
296 
297 /* get the key at position */
298 #ifdef ENABLE_RAMBLE
view_hit_get(struct view * view,gint x,gint y,enum key_hit * hit)299 struct key *view_hit_get (struct view *view, gint x, gint y, enum key_hit *hit)
300 #else
301 struct key *view_hit_get (struct view *view, gint x, gint y)
302 #endif
303 {
304 	START_FUNC
305 	GSList *list=view->keyboards;
306 	struct keyboard *keyboard=NULL;
307 	struct key *key;
308 	gint kx=0.0, ky=0.0, kw=0.0, kh=0.0;
309 
310 	/* find the hit keyboard */
311 	while (list)
312 	{
313 		keyboard=(struct keyboard *)list->data;
314 		/* TODO: record in pixel
315 		 * and move that to keyboard_test */
316 		kx=keyboard->xpos*view->scalex;
317 		ky=keyboard->ypos*view->scaley;
318 		kw=keyboard->width*view->scalex;
319 		kh=keyboard->height*view->scaley;
320 		if (keyboard_activated(keyboard) && (!keyboard->under) && (x>=kx) && (x<=(kx+kw)) && (y>=ky) && y<=(ky+kh)) {
321 			list=NULL;
322 		}
323 		else list = list->next;
324 	}
325 #ifdef ENABLE_RAMBLE
326 	key=keyboard_hit_get(keyboard, x-kx, y-ky, view->scalex, view->scaley, hit);
327 #else
328 	key=keyboard_hit_get(keyboard, x-kx, y-ky, view->scalex, view->scaley);
329 #endif
330 
331 	END_FUNC
332 	return key;
333 }
334 
335 /* Create a window mask for transparent window for non-composited screen */
336 /* For composited screen, this function is useless, use alpha channel instead. */
view_create_window_mask(struct view * view)337 void view_create_window_mask(struct view *view)
338 {
339 	START_FUNC
340 	Pixmap shape;
341 	cairo_surface_t *mask=NULL;
342 	cairo_t *cairoctx=NULL;
343 	Display *disp=(Display *)gdk_x11_get_default_xdisplay();
344 
345 	if (settings_get_bool(SETTINGS_TRANSPARENT) && (!view->composite)) {
346 		shape=XCreatePixmap(disp, GDK_WINDOW_XID(gtk_widget_get_window(GTK_WIDGET(view->window))),
347 			view->width, view->height, 1);
348 		mask=cairo_xlib_surface_create_for_bitmap(disp, shape,
349 			DefaultScreenOfDisplay(disp), view->width, view->height);
350 		cairoctx=cairo_create(mask);
351 		view_background_draw(view, cairoctx);
352 		cairo_set_source_rgba(cairoctx, 0.0, 0.0, 0.0, 0.0);
353 		cairo_set_operator(cairoctx, CAIRO_OPERATOR_SOURCE);
354 		cairo_paint(cairoctx);
355 		cairo_set_source_surface(cairoctx, view->background, 0, 0);
356 		cairo_paint(cairoctx);
357 		XShapeCombineMask(disp, GDK_WINDOW_XID(gtk_widget_get_window(GTK_WIDGET(view->window))),
358 			ShapeBounding, 0, 0, cairo_xlib_surface_get_drawable(mask), ShapeSet);
359 		cairo_destroy(cairoctx);
360 		cairo_surface_destroy(view->background);
361 		view->background=NULL;
362 		cairo_surface_destroy(mask);
363 		status_focus_zoom_set(view->status, FALSE);
364 	} else {
365 		XShapeCombineMask(disp, GDK_WINDOW_XID(gtk_widget_get_window(GTK_WIDGET(view->window))),
366 			ShapeBounding, 0, 0, 0, ShapeSet);
367 		status_focus_zoom_set(view->status, TRUE);
368 	}
369 	gtk_widget_queue_draw(GTK_WIDGET(view->window));
370 	END_FUNC
371 }
372 
373 /* Triggered by gconf when the "transparent" parameter is changed. Calls view_create_window_mask */
view_set_transparent(GSettings * settings,gchar * key,gpointer user_data)374 void view_set_transparent(GSettings *settings, gchar *key, gpointer user_data)
375 {
376 	START_FUNC
377 	struct view *view=(struct view *)user_data;
378 	gboolean shown=gtk_widget_get_visible(GTK_WIDGET(view->window));
379 	gtk_widget_show(GTK_WIDGET(view->window));
380 	view_create_window_mask(view);
381 	if (!shown) gtk_widget_hide(GTK_WIDGET(view->window));
382 	END_FUNC
383 }
384 
385 /* Triggered by gconf when the "decorated" parameter is changed. Decorates or undecorate the window. */
view_set_decorated(GSettings * settings,gchar * key,gpointer user_data)386 void view_set_decorated(GSettings *settings, gchar *key, gpointer user_data)
387 {
388 	START_FUNC
389 	struct view *view=(struct view *)user_data;
390 	gtk_window_set_decorated(view->window, settings_get_bool(SETTINGS_DECORATED));
391 	gtk_window_move(view->window, settings_get_int(SETTINGS_XPOS), settings_get_int(SETTINGS_YPOS));
392 	END_FUNC
393 }
394 
395 /* Triggered by gconf when the "always_on_top" parameter is changed.
396    Change the window property to be always on top or not to be. */
view_set_always_on_top(GSettings * settings,gchar * key,gpointer user_data)397 void view_set_always_on_top(GSettings *settings, gchar *key, gpointer user_data)
398 {
399 	START_FUNC
400 	struct view *view=(struct view *)user_data;
401 	gtk_window_set_keep_above(view->window, settings_get_bool(SETTINGS_ALWAYS_ON_TOP));
402 	END_FUNC
403 }
404 
405 /* Triggered by gconf when the "task_bar" parameter is changed.
406    Change the window hint to appear in the task bar or not. */
view_set_task_bar(GSettings * settings,gchar * key,gpointer user_data)407 void view_set_task_bar(GSettings *settings, gchar *key, gpointer user_data)
408 {
409 	START_FUNC
410 	struct view *view=(struct view *)user_data;
411 	gtk_window_set_skip_taskbar_hint(view->window, !settings_get_bool(SETTINGS_TASK_BAR));
412 	END_FUNC
413 }
414 
415 /* Triggered by gconf when the "resizable" parameter is changed.
416    makes the window (not)resizable the window. */
view_set_resizable(GSettings * settings,gchar * key,gpointer user_data)417 void view_set_resizable(GSettings *settings, gchar *key, gpointer user_data)
418 {
419 	START_FUNC
420 	struct view *view=(struct view *)user_data;
421 	if (settings_get_bool(SETTINGS_RESIZABLE)) {
422 		gtk_widget_set_size_request(GTK_WIDGET(view->window), view->vwidth, view->vheight);
423 	}
424 	view_resize(view);
425 	END_FUNC
426 }
427 
428 /* Triggered by gconf when a color parameter is changed. */
view_redraw(GSettings * settings,gchar * key,gpointer user_data)429 void view_redraw(GSettings *settings, gchar *key, gpointer user_data)
430 {
431 	START_FUNC
432 	struct view *view=(struct view *)user_data;
433 	style_update_colors(view->style);
434 	if ((!strcmp(key, "key")) || (!strcmp(key, "outline"))) {
435 		if (view->background) cairo_surface_destroy(view->background);
436 		view->background=NULL;
437 	} else if (!strncmp(key, "label", 5) || (!strcmp(key, "font")) || (!strcmp(key, "system_font"))) {
438 		if (view->symbols) cairo_surface_destroy(view->symbols);
439 		view->symbols=NULL;
440 	}
441 	gtk_widget_queue_draw(GTK_WIDGET(view->window));
442 	END_FUNC
443 }
444 
445 /* Triggered by gconf when the "resizable" parameter is changed.
446    makes the window (not)resizable the window. */
view_set_keep_ratio(GSettings * settings,gchar * key,gpointer user_data)447 void view_set_keep_ratio(GSettings *settings, gchar *key, gpointer user_data)
448 {
449 	START_FUNC
450 	struct view *view=(struct view *)user_data;
451 	if (settings_get_bool(SETTINGS_KEEP_RATIO)) {
452 		view->scaley=view->scalex;
453 	}
454 	view_resize(view);
455 	view_redraw(settings, key, user_data);
456 	END_FUNC
457 }
458 
459 /* Redraw the key to the window */
view_update(struct view * view,struct key * key,gboolean statechange)460 void view_update(struct view *view, struct key *key, gboolean statechange)
461 {
462 	START_FUNC
463 	GdkRectangle *rect;
464 	GdkCursor *cursor;
465 
466 	if (!view->window) return;
467 	if (key) {
468 		if (statechange) {
469 			if (view->symbols) cairo_surface_destroy(view->symbols);
470 			view->symbols=NULL;
471 			gtk_widget_queue_draw(GTK_WIDGET(view->window));
472 		} else {
473 			rect=keyboard_key_getrect((struct keyboard *)key_get_keyboard(key),
474 				key, status_focus_zoom_get(view->status));
475 			gdk_window_invalidate_rect(
476 				gtk_widget_get_window(GTK_WIDGET(view->window)),
477 				rect, TRUE);
478 		}
479 	}
480 	if (status_focus_get(view->status)) {
481 		if (!view->hand_cursor) {
482 			cursor=gdk_cursor_new(GDK_HAND2);
483 			gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(view->window)), cursor);
484 			view->hand_cursor=TRUE;
485 		}
486 	} else if (view->hand_cursor) {
487 		gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(view->window)), NULL);
488 		view->hand_cursor=FALSE;
489 	}
490 	END_FUNC
491 }
492 
493 /* on screen change event: check for composite extension */
view_screen_changed(GtkWidget * widget,GdkScreen * old_screen,struct view * view)494 void view_screen_changed (GtkWidget *widget, GdkScreen *old_screen, struct view *view)
495 {
496 	START_FUNC
497 	GdkVisual *visual;
498 	if (gtk_widget_is_composited(widget)) {
499 		flo_info(_("X11 composite extension detected. Semi-transparency is enabled."));
500 		if (view) view->composite=TRUE;
501 		visual=gdk_screen_get_rgba_visual(gdk_screen_get_default());
502 		if (visual==NULL) visual=gdk_screen_get_system_visual(gdk_screen_get_default());
503 		gtk_widget_set_visual(widget, visual);
504 	} else {
505 		flo_info(_("Your screen does not support alpha channel. Semi-transparency is disabled"));
506 		if (view) view->composite=FALSE;
507 	}
508 	END_FUNC
509 }
510 
511 /* on configure events: record window position */
view_configure(GtkWidget * window,GdkEventConfigure * pConfig,struct view * view)512 void view_configure (GtkWidget *window, GdkEventConfigure* pConfig, struct view *view)
513 {
514 	START_FUNC
515 	GdkRectangle rect;
516 	gint xpos, ypos;
517 	if ((!view->window)||(!gtk_widget_get_visible(window))) return;
518 
519 	/* record window position */
520 	if (gtk_window_get_decorated(GTK_WINDOW(view->window)))
521 		gtk_window_get_position(GTK_WINDOW(view->window), &xpos, &ypos);
522 	else { xpos=pConfig->x; ypos=pConfig->y; }
523 	if (settings_get_int(SETTINGS_XPOS)!=xpos)
524 		settings_set_int(SETTINGS_XPOS, xpos);
525 	if (settings_get_int(SETTINGS_YPOS)!=ypos)
526 		settings_set_int(SETTINGS_YPOS, ypos);
527 
528 	/* handle resize events */
529 	if ((pConfig->width!=view->width) || (pConfig->height!=view->height)) {
530 		if (settings_get_bool(SETTINGS_KEEP_RATIO)) {
531 			view->scalex=view->scaley=(gdouble)pConfig->width/view->vwidth;
532 		} else {
533 			view->scalex=(gdouble)pConfig->width/view->vwidth;
534 			view->scaley=(gdouble)pConfig->height/view->vheight;
535 		}
536 		if ((view->scalex>200.0)||(view->scaley>200.0))
537 			flo_warn(_("Window size out of range :%d, %d"), view->scalex, view->scaley);
538 		else {
539 			settings_set_double(SETTINGS_SCALEX, view->scalex, FALSE);
540 			settings_set_double(SETTINGS_SCALEY, view->scaley, FALSE);
541 		}
542 		view->width=pConfig->width; view->height=pConfig->height;
543 		if (view->background) cairo_surface_destroy(view->background);
544 		view->background=NULL;
545 		if (view->symbols) cairo_surface_destroy(view->symbols);
546 		view->symbols=NULL;
547 		view_create_window_mask(view);
548 		rect.x=0; rect.y=0;
549 		rect.width=pConfig->width; rect.height=pConfig->height;
550 		gtk_widget_size_allocate(GTK_WIDGET(view->window), &rect);
551 		gdk_window_invalidate_rect(gtk_widget_get_window(GTK_WIDGET(view->window)), &rect, TRUE);
552 		gdk_window_process_updates(gtk_widget_get_window(GTK_WIDGET(view->window)), FALSE);
553 	}
554 
555 	END_FUNC
556 }
557 
558 /* draw the background of the keyboard */
view_draw_background(struct view * view,cairo_t * context)559 void view_draw_background (struct view *view, cairo_t *context)
560 {
561 	START_FUNC
562 	/* prepare the background */
563 	if (!view->background) {
564 		view_background_draw(view, context);
565 	}
566 
567 	/* paint the background */
568 	cairo_set_operator(context, CAIRO_OPERATOR_OVER);
569 	cairo_set_source_surface(context, view->background, 0, 0);
570 	cairo_paint(context);
571 	END_FUNC
572 }
573 
574 /* draw a list of keys (latched or locked keys) */
view_draw_list(struct view * view,cairo_t * context,GList * list)575 void view_draw_list (struct view *view, cairo_t *context, GList *list)
576 {
577 	START_FUNC
578 	struct keyboard *keyboard;
579 	struct key *key;
580 	while (list) {
581 		key=(struct key *)list->data;
582 		keyboard=(struct keyboard *)key_get_keyboard(key);
583 		keyboard_press_draw(keyboard, context, view->style, key, view->status);
584 		list=list->next;
585 	}
586 	END_FUNC
587 }
588 
589 /* draw a single key (pressed or focused) */
view_draw_key(struct view * view,cairo_t * context,struct key * key)590 void view_draw_key (struct view *view, cairo_t *context, struct key *key)
591 {
592 	START_FUNC
593 	struct keyboard *keyboard;
594 	if (key) {
595 		keyboard=(struct keyboard *)key_get_keyboard(key);
596 		keyboard_focus_draw(keyboard, context,
597 			(gdouble)cairo_xlib_surface_get_width(view->background),
598 			(gdouble)cairo_xlib_surface_get_height(view->background),
599 			view->style, key, view->status);
600 	}
601 	END_FUNC
602 }
603 
604 /* on draw event: draws the keyboards to the window */
view_expose(GtkWidget * window,cairo_t * context,struct view * view)605 void view_expose (GtkWidget *window, cairo_t* context, struct view *view)
606 {
607 	START_FUNC
608 	enum key_state state;
609 
610 	/* clear the area */
611 	if (settings_get_bool(SETTINGS_TRANSPARENT)) {
612 		cairo_set_source_rgba(context, 0.0, 0.0, 0.0, 0.0);
613 		cairo_set_operator(context, CAIRO_OPERATOR_SOURCE);
614 		cairo_paint(context);
615 	}
616 
617 	view_draw_background(view, context);
618 
619 	/* draw the symbols */
620 	if (!view->symbols) {
621 		view_symbols_draw(view, context);
622 	}
623 	cairo_set_source_surface(context, view->symbols, 0, 0);
624 	cairo_paint(context);
625 
626 	/* handle composited transparency */
627 	/* TODO: check for transparency support in WM */
628 	if (view->composite && settings_get_double(SETTINGS_OPACITY)!=100.0) {
629 		if (settings_get_double(SETTINGS_OPACITY)>100.0 ||
630 			settings_get_double(SETTINGS_OPACITY)<1.0) {
631 			flo_error(_("Window opacity out of range (1.0 to 100.0): %f"),
632 				settings_get_double(SETTINGS_OPACITY));
633 		}
634 		cairo_set_source_rgba(context, 0.0, 0.0, 0.0,
635 			(100.0-settings_get_double(SETTINGS_OPACITY))/100.0);
636 		cairo_set_operator(context, CAIRO_OPERATOR_DEST_OUT);
637 		cairo_paint(context);
638 		cairo_set_operator(context, CAIRO_OPERATOR_OVER);
639 	}
640 
641 	cairo_save(context);
642 	cairo_scale(context, view->scalex, view->scaley);
643 
644 	/* draw highlights (pressed keys) */
645 	view_draw_list(view, context, status_list_latched(view->status));
646 	view_draw_list(view, context, status_list_locked(view->status));
647 
648 	/* pressed and focused key */
649 	view_draw_key(view, context, status_focus_get(view->status));
650 	if (status_pressed_get(view->status)) {
651 		state=status_pressed_get(view->status)->state;
652 		key_state_set(status_pressed_get(view->status), KEY_PRESSED);
653 		view_draw_key(view, context, status_pressed_get(view->status));
654 		key_state_set(status_pressed_get(view->status), state);
655 	}
656 
657 	cairo_restore(context);
658 
659 #ifdef ENABLE_RAMBLE
660 	if (view->ramble) ramble_draw(view->ramble, context);
661 #endif
662 
663 	/* restore configure event handler. */
664 	if (!view->configure_handler)
665 		view->configure_handler=g_signal_connect(G_OBJECT(view->window), "configure-event",
666 			G_CALLBACK(view_configure), view);
667 	END_FUNC
668 }
669 
670 /* on keys changed events */
view_on_keys_changed(gpointer user_data)671 void view_on_keys_changed(gpointer user_data)
672 {
673 	START_FUNC
674 	struct view *view=(struct view *)user_data;
675 	if (view->symbols) cairo_surface_destroy(view->symbols);
676 	view->symbols=NULL;
677 	if (view->window) gtk_widget_queue_draw(GTK_WIDGET(view->window));
678 	END_FUNC
679 }
680 
681 /* track the windows state changes */
view_window_state(GtkWidget * window,GdkEventWindowState * event,struct view * view)682 void view_window_state (GtkWidget *window, GdkEventWindowState *event, struct view *view)
683 {
684 	START_FUNC
685 	gint is_iconified=gdk_window_get_state(gtk_widget_get_window(window))&GDK_WINDOW_STATE_ICONIFIED;
686 	if (is_iconified) {
687 		view_hide(view);
688 		gtk_window_deiconify(GTK_WINDOW(view->window));
689 	}
690 	END_FUNC
691 }
692 
693 
694 /* Triggered by gconf when the "extensions" parameter is changed. */
view_update_extensions(GSettings * settings,gchar * key,gpointer user_data)695 void view_update_extensions(GSettings *settings, gchar *key, gpointer user_data)
696 {
697 	START_FUNC
698 	struct view *view=(struct view *)user_data;
699 	GSList *list=view->keyboards;
700 	struct keyboard *keyboard;
701 
702 	/* Do not call configure signal handler */
703 	if (view->configure_handler) g_signal_handler_disconnect(G_OBJECT(view->window), view->configure_handler);
704 	view->configure_handler=0;
705 
706 	while (list)
707 	{
708 		keyboard=(struct keyboard *)list->data;
709 		keyboard_status_update(keyboard, view->status);
710 		list=list->next;
711 	}
712 
713 	view_set_dimensions(view);
714 	view_resize(view);
715 	if (view->background) cairo_surface_destroy(view->background);
716 	view->background=NULL;
717 	if (view->symbols) cairo_surface_destroy(view->symbols);
718 	view->symbols=NULL;
719 	view_create_window_mask(view);
720 	status_focus_set(view->status, NULL);
721 	gtk_widget_queue_draw(GTK_WIDGET(view->window));
722 	END_FUNC
723 }
724 
725 /* Triggered by gconf when the "zoom" parameter is changed. */
view_set_scalex(GSettings * settings,gchar * key,gpointer user_data)726 void view_set_scalex(GSettings *settings, gchar *key, gpointer user_data)
727 {
728 	START_FUNC
729 	struct view *view=(struct view *)user_data;
730 	/* Do not call configure signal handler */
731 	if (view->configure_handler) g_signal_handler_disconnect(G_OBJECT(view->window), view->configure_handler);
732 	view->configure_handler=0;
733 	view->scalex=settings_get_double(SETTINGS_SCALEX);
734 	if (settings_get_bool(SETTINGS_KEEP_RATIO)) view->scaley=view->scalex;
735 	view_update_extensions(settings, key, user_data);
736 	END_FUNC
737 }
738 
739 /* Triggered by gconf when the "zoom" parameter is changed. */
view_set_scaley(GSettings * settings,gchar * key,gpointer user_data)740 void view_set_scaley(GSettings *settings, gchar *key, gpointer user_data)
741 {
742 	START_FUNC
743 	struct view *view=(struct view *)user_data;
744 	/* Do not call configure signal handler */
745 	if (view->configure_handler) g_signal_handler_disconnect(G_OBJECT(view->window), view->configure_handler);
746 	view->configure_handler=0;
747 	view->scaley=settings_get_double(SETTINGS_SCALEY);
748 	if (settings_get_bool(SETTINGS_KEEP_RATIO)) view->scalex=view->scaley;
749 	view_update_extensions(settings, key, user_data);
750 	END_FUNC
751 }
752 
753 /* Triggered by gconf when the "opacity" parameter is changed. */
view_set_opacity(GSettings * settings,gchar * key,gpointer user_data)754 void view_set_opacity(GSettings *settings, gchar *key, gpointer user_data)
755 {
756 	START_FUNC
757 	struct view *view=(struct view *)user_data;
758 	gtk_widget_queue_draw(GTK_WIDGET(view->window));
759 	END_FUNC
760 }
761 
762 /* get gtk window of the view */
view_window_get(struct view * view)763 GtkWindow *view_window_get (struct view *view)
764 {
765 	START_FUNC
766 	END_FUNC
767 	return view->window;
768 }
769 
770 /* get gtk window of the view */
view_status_set(struct view * view,struct status * status)771 void view_status_set (struct view *view, struct status *status)
772 {
773 	START_FUNC
774 	view->status=status;
775 	END_FUNC
776 }
777 
778 /* liberate all the memory used by the view */
view_free(struct view * view)779 void view_free(struct view *view)
780 {
781 	START_FUNC
782 	if (view->background) cairo_surface_destroy(view->background);
783 	if (view->symbols) cairo_surface_destroy(view->symbols);
784 	g_free(view);
785 	END_FUNC
786 }
787 
788 /* create a view of florence */
view_new(struct status * status,struct style * style,GSList * keyboards)789 struct view *view_new (struct status *status, struct style *style, GSList *keyboards)
790 {
791 	START_FUNC
792 	struct view *view=g_malloc(sizeof(struct view));
793 	if (!view) flo_fatal(_("Unable to allocate memory for view"));
794 	memset(view, 0, sizeof(struct view));
795 
796 	view->status=status;
797 	view->style=style;
798 	view->keyboards=keyboards;
799 	view->scalex=settings_get_double(SETTINGS_SCALEX);
800 	view->scaley=settings_get_double(SETTINGS_SCALEY);
801 	view_set_dimensions(view);
802 	view->window=GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
803 	gtk_window_set_keep_above(view->window, settings_get_bool(SETTINGS_ALWAYS_ON_TOP));
804  	gtk_window_set_accept_focus(view->window, FALSE);
805 	gtk_window_set_skip_taskbar_hint(view->window, !settings_get_bool(SETTINGS_TASK_BAR));
806 	/* Remove resize grip since it is buggy */
807 	gtk_window_set_has_resize_grip(view->window, FALSE);
808 	view_resize(view);
809 	gtk_container_set_border_width(GTK_CONTAINER(view->window), 0);
810 	gtk_widget_set_events(GTK_WIDGET(view->window),
811 		GDK_EXPOSURE_MASK|GDK_POINTER_MOTION_HINT_MASK|GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK|
812 		GDK_ENTER_NOTIFY_MASK|GDK_LEAVE_NOTIFY_MASK|GDK_STRUCTURE_MASK|GDK_POINTER_MOTION_MASK);
813 	gtk_widget_set_app_paintable(GTK_WIDGET(view->window), TRUE);
814 	gtk_window_set_decorated(view->window, settings_get_bool(SETTINGS_DECORATED));
815 	gtk_window_move(view->window, settings_get_int(SETTINGS_XPOS), settings_get_int(SETTINGS_YPOS));
816 	/*g_signal_connect(gdk_keymap_get_default(), "keys-changed", G_CALLBACK(view_on_keys_changed), view);*/
817 	xkeyboard_register_events(status->xkeyboard, view_on_keys_changed, (gpointer)view);
818 	g_signal_connect(G_OBJECT(view->window), "screen-changed", G_CALLBACK(view_screen_changed), view);
819 	view->configure_handler=g_signal_connect(G_OBJECT(view->window), "configure-event",
820 		G_CALLBACK(view_configure), view);
821 	g_signal_connect(G_OBJECT(view->window), "draw", G_CALLBACK(view_expose), view);
822 	g_signal_connect(G_OBJECT(view->window), "window-state-event", G_CALLBACK(view_window_state), view);
823 	view_screen_changed(GTK_WIDGET(view->window), NULL, view);
824 	g_signal_connect(G_OBJECT(view->window), "destroy", G_CALLBACK(view_on_destroy), view);
825 	gtk_widget_show(GTK_WIDGET(view->window));
826 	view_create_window_mask(view);
827 
828 	/* register settings callbacks */
829 	settings_changecb_register(SETTINGS_TRANSPARENT, view_set_transparent, view);
830 	settings_changecb_register(SETTINGS_DECORATED, view_set_decorated, view);
831 	settings_changecb_register(SETTINGS_RESIZABLE, view_set_resizable, view);
832 	settings_changecb_register(SETTINGS_ALWAYS_ON_TOP, view_set_always_on_top, view);
833 	settings_changecb_register(SETTINGS_TASK_BAR, view_set_task_bar, view);
834 	settings_changecb_register(SETTINGS_KEEP_RATIO, view_set_keep_ratio, view);
835 	settings_changecb_register(SETTINGS_SCALEX, view_set_scalex, view);
836 	settings_changecb_register(SETTINGS_SCALEY, view_set_scaley, view);
837 	settings_changecb_register(SETTINGS_OPACITY, view_set_opacity, view);
838 	settings_changecb_register(SETTINGS_EXTENSIONS, view_update_extensions, view);
839 	settings_changecb_register(SETTINGS_KEY, view_redraw, view);
840 	settings_changecb_register(SETTINGS_OUTLINE, view_redraw, view);
841 	settings_changecb_register(SETTINGS_LABEL, view_redraw, view);
842 	settings_changecb_register(SETTINGS_LABEL_OUTLINE, view_redraw, view);
843 	settings_changecb_register(SETTINGS_ACTIVATED, view_redraw, view);
844 	settings_changecb_register(SETTINGS_LATCHED, view_redraw, view);
845 	settings_changecb_register(SETTINGS_SYSTEM_FONT, view_redraw, view);
846 	settings_changecb_register(SETTINGS_FONT, view_redraw, view);
847 
848 	/* set the window icon */
849 	tools_set_icon(view->window);
850 	END_FUNC
851 	return view;
852 }
853 
854 /* Change the layout and style of the view and redraw */
view_update_layout(struct view * view,struct style * style,GSList * keyboards)855 void view_update_layout(struct view *view, struct style *style, GSList *keyboards)
856 {
857 	START_FUNC
858 	view->style=style;
859 	view->keyboards=keyboards;
860 	xkeyboard_register_events(view->status->xkeyboard, view_on_keys_changed, (gpointer)view);
861 	view_update_extensions(NULL, NULL, (gpointer)view);
862 	END_FUNC
863 }
864 
865