1 /*
2  * Copyright (C) 2006 Sergey V. Udaltsov <svu@gnome.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 
20 #include <config.h>
21 #include <gtk/gtk.h>
22 #include <gdk/gdkx.h>
23 #include <gdk/gdkkeysyms.h>
24 #include <X11/XKBlib.h>
25 #include <X11/extensions/XKBgeom.h>
26 #include <stdlib.h>
27 #include <memory.h>
28 #include <math.h>
29 #include <glib/gi18n-lib.h>
30 
31 #include <gkbd-keyboard-config.h>
32 #include <gkbd-keyboard-drawing.h>
33 #include <gkbd-keyboard-drawing-marshal.h>
34 #include <gkbd-util.h>
35 
36 #define noKBDRAW_DEBUG
37 
38 #define INVALID_KEYCODE ((guint)(-1))
39 
40 #define GTK_RESPONSE_PRINT 2
41 
42 #define CAIRO_LINE_WIDTH 1.0
43 
44 #define KEY_FONT_SIZE 12
45 
46 enum {
47 	BAD_KEYCODE = 0,
48 	NUM_SIGNALS
49 };
50 
51 static guint gkbd_keyboard_drawing_signals[NUM_SIGNALS] = { 0 };
52 
53 static GkbdKeyboardDrawingGroupLevel defaultGroupsLevels[] = {
54 	{0, 1},
55 	{0, 3},
56 	{0, 0},
57 	{0, 2}
58 };
59 
60 static GkbdKeyboardDrawingGroupLevel *pGroupsLevels[] = {
61 	defaultGroupsLevels,
62 	defaultGroupsLevels + 1,
63 	defaultGroupsLevels + 2,
64 	defaultGroupsLevels + 3
65 };
66 
67 static void gkbd_keyboard_drawing_set_mods (GkbdKeyboardDrawing * drawing,
68 					    guint mods);
69 
70 extern gboolean xkl_xkb_config_native_prepare (XklEngine * engine,
71 					       const XklConfigRec * data,
72 					       gpointer component_names);
73 
74 extern void xkl_xkb_config_native_cleanup (XklEngine * engine,
75 					   gpointer component_names);
76 
77 static gint
xkb_to_pixmap_coord(GkbdKeyboardDrawingRenderContext * context,gint n)78 xkb_to_pixmap_coord (GkbdKeyboardDrawingRenderContext * context, gint n)
79 {
80 	return n * context->scale_numerator / context->scale_denominator;
81 }
82 
83 static gdouble
xkb_to_pixmap_double(GkbdKeyboardDrawingRenderContext * context,gdouble d)84 xkb_to_pixmap_double (GkbdKeyboardDrawingRenderContext * context,
85 		      gdouble d)
86 {
87 	return d * context->scale_numerator / context->scale_denominator;
88 }
89 
90 
91 /* angle is in tenths of a degree; coordinates can be anything as (xkb,
92  * pixels, pango) as long as they are all the same */
93 static void
rotate_coordinate(gint origin_x,gint origin_y,gint x,gint y,gint angle,gint * rotated_x,gint * rotated_y)94 rotate_coordinate (gint origin_x,
95 		   gint origin_y,
96 		   gint x,
97 		   gint y, gint angle, gint * rotated_x, gint * rotated_y)
98 {
99 	*rotated_x =
100 	    origin_x + (x - origin_x) * cos (M_PI * angle / 1800.0) - (y -
101 								       origin_y)
102 	    * sin (M_PI * angle / 1800.0);
103 	*rotated_y =
104 	    origin_y + (x - origin_x) * sin (M_PI * angle / 1800.0) + (y -
105 								       origin_y)
106 	    * cos (M_PI * angle / 1800.0);
107 }
108 
109 static gdouble
length(gdouble x,gdouble y)110 length (gdouble x, gdouble y)
111 {
112 	return sqrt (x * x + y * y);
113 }
114 
115 static gdouble
point_line_distance(gdouble ax,gdouble ay,gdouble nx,gdouble ny)116 point_line_distance (gdouble ax, gdouble ay, gdouble nx, gdouble ny)
117 {
118 	return ax * nx + ay * ny;
119 }
120 
121 static void
normal_form(gdouble ax,gdouble ay,gdouble bx,gdouble by,gdouble * nx,gdouble * ny,gdouble * d)122 normal_form (gdouble ax, gdouble ay,
123 	     gdouble bx, gdouble by,
124 	     gdouble * nx, gdouble * ny, gdouble * d)
125 {
126 	gdouble l;
127 
128 	*nx = by - ay;
129 	*ny = ax - bx;
130 
131 	l = length (*nx, *ny);
132 
133 	*nx /= l;
134 	*ny /= l;
135 
136 	*d = point_line_distance (ax, ay, *nx, *ny);
137 }
138 
139 static void
inverse(gdouble a,gdouble b,gdouble c,gdouble d,gdouble * e,gdouble * f,gdouble * g,gdouble * h)140 inverse (gdouble a, gdouble b, gdouble c, gdouble d,
141 	 gdouble * e, gdouble * f, gdouble * g, gdouble * h)
142 {
143 	gdouble det;
144 
145 	det = a * d - b * c;
146 
147 	*e = d / det;
148 	*f = -b / det;
149 	*g = -c / det;
150 	*h = a / det;
151 }
152 
153 static void
multiply(gdouble a,gdouble b,gdouble c,gdouble d,gdouble e,gdouble f,gdouble * x,gdouble * y)154 multiply (gdouble a, gdouble b, gdouble c, gdouble d,
155 	  gdouble e, gdouble f, gdouble * x, gdouble * y)
156 {
157 	*x = a * e + b * f;
158 	*y = c * e + d * f;
159 }
160 
161 static void
intersect(gdouble n1x,gdouble n1y,gdouble d1,gdouble n2x,gdouble n2y,gdouble d2,gdouble * x,gdouble * y)162 intersect (gdouble n1x, gdouble n1y, gdouble d1,
163 	   gdouble n2x, gdouble n2y, gdouble d2, gdouble * x, gdouble * y)
164 {
165 	gdouble e, f, g, h;
166 
167 	inverse (n1x, n1y, n2x, n2y, &e, &f, &g, &h);
168 	multiply (e, f, g, h, d1, d2, x, y);
169 }
170 
171 
172 /* draw an angle from the current point to b and then to c,
173  * with a rounded corner of the given radius.
174  */
175 static void
rounded_corner(cairo_t * cr,gdouble bx,gdouble by,gdouble cx,gdouble cy,gdouble radius)176 rounded_corner (cairo_t * cr,
177 		gdouble bx, gdouble by,
178 		gdouble cx, gdouble cy, gdouble radius)
179 {
180 	gdouble ax, ay;
181 	gdouble n1x, n1y, d1;
182 	gdouble n2x, n2y, d2;
183 	gdouble pd1, pd2;
184 	gdouble ix, iy;
185 	gdouble dist1, dist2;
186 	gdouble nx, ny, d;
187 	gdouble a1x, a1y, c1x, c1y;
188 	gdouble phi1, phi2;
189 
190 	cairo_get_current_point (cr, &ax, &ay);
191 #ifdef KBDRAW_DEBUG
192 	printf ("        current point: (%f, %f), radius %f:\n", ax, ay,
193 		radius);
194 #endif
195 
196 	/* make sure radius is not too large */
197 	dist1 = length (bx - ax, by - ay);
198 	dist2 = length (cx - bx, cy - by);
199 
200 	radius = MIN (radius, MIN (dist1, dist2));
201 
202 	/* construct normal forms of the lines */
203 	normal_form (ax, ay, bx, by, &n1x, &n1y, &d1);
204 	normal_form (bx, by, cx, cy, &n2x, &n2y, &d2);
205 
206 	/* find which side of the line a,b the point c is on */
207 	if (point_line_distance (cx, cy, n1x, n1y) < d1)
208 		pd1 = d1 - radius;
209 	else
210 		pd1 = d1 + radius;
211 
212 	/* find which side of the line b,c the point a is on */
213 	if (point_line_distance (ax, ay, n2x, n2y) < d2)
214 		pd2 = d2 - radius;
215 	else
216 		pd2 = d2 + radius;
217 
218 	/* intersect the parallels to find the center of the arc */
219 	intersect (n1x, n1y, pd1, n2x, n2y, pd2, &ix, &iy);
220 
221 	nx = (bx - ax) / dist1;
222 	ny = (by - ay) / dist1;
223 	d = point_line_distance (ix, iy, nx, ny);
224 
225 	/* a1 is the point on the line a-b where the arc starts */
226 	intersect (n1x, n1y, d1, nx, ny, d, &a1x, &a1y);
227 
228 	nx = (cx - bx) / dist2;
229 	ny = (cy - by) / dist2;
230 	d = point_line_distance (ix, iy, nx, ny);
231 
232 	/* c1 is the point on the line b-c where the arc ends */
233 	intersect (n2x, n2y, d2, nx, ny, d, &c1x, &c1y);
234 
235 	/* determine the first angle */
236 	if (a1x - ix == 0)
237 		phi1 = (a1y - iy > 0) ? M_PI_2 : 3 * M_PI_2;
238 	else if (a1x - ix > 0)
239 		phi1 = atan ((a1y - iy) / (a1x - ix));
240 	else
241 		phi1 = M_PI + atan ((a1y - iy) / (a1x - ix));
242 
243 	/* determine the second angle */
244 	if (c1x - ix == 0)
245 		phi2 = (c1y - iy > 0) ? M_PI_2 : 3 * M_PI_2;
246 	else if (c1x - ix > 0)
247 		phi2 = atan ((c1y - iy) / (c1x - ix));
248 	else
249 		phi2 = M_PI + atan ((c1y - iy) / (c1x - ix));
250 
251 	/* compute the difference between phi2 and phi1 mod 2pi */
252 	d = phi2 - phi1;
253 	while (d < 0)
254 		d += 2 * M_PI;
255 	while (d > 2 * M_PI)
256 		d -= 2 * M_PI;
257 
258 #ifdef KBDRAW_DEBUG
259 	printf ("        line 1 to: (%f, %f):\n", a1x, a1y);
260 #endif
261 	if (!(isnan (a1x) || isnan (a1y)))
262 		cairo_line_to (cr, a1x, a1y);
263 
264 	/* pick the short arc from phi1 to phi2 */
265 	if (d < M_PI)
266 		cairo_arc (cr, ix, iy, radius, phi1, phi2);
267 	else
268 		cairo_arc_negative (cr, ix, iy, radius, phi1, phi2);
269 
270 #ifdef KBDRAW_DEBUG
271 	printf ("        line 2 to: (%f, %f):\n", cx, cy);
272 #endif
273 	cairo_line_to (cr, cx, cy);
274 }
275 
276 static void
rounded_polygon(cairo_t * cr,gboolean filled,gdouble radius,GdkPoint * points,gint num_points)277 rounded_polygon (cairo_t * cr,
278 		 gboolean filled,
279 		 gdouble radius, GdkPoint * points, gint num_points)
280 {
281 	gint i, j;
282 
283 	cairo_move_to (cr,
284 		       (gdouble) (points[num_points - 1].x +
285 				  points[0].x) / 2,
286 		       (gdouble) (points[num_points - 1].y +
287 				  points[0].y) / 2);
288 
289 
290 #ifdef KBDRAW_DEBUG
291 	printf ("    rounded polygon of radius %f:\n", radius);
292 #endif
293 	for (i = 0; i < num_points; i++) {
294 		j = (i + 1) % num_points;
295 		rounded_corner (cr, (gdouble) points[i].x,
296 				(gdouble) points[i].y,
297 				(gdouble) (points[i].x + points[j].x) / 2,
298 				(gdouble) (points[i].y + points[j].y) / 2,
299 				radius);
300 #ifdef KBDRAW_DEBUG
301 		printf ("      corner (%d, %d) -> (%d, %d):\n",
302 			points[i].x, points[i].y, points[j].x,
303 			points[j].y);
304 #endif
305 	};
306 	cairo_close_path (cr);
307 
308 	if (filled)
309 		cairo_fill (cr);
310 	else {
311 		cairo_set_line_width (cr, CAIRO_LINE_WIDTH);
312 		cairo_stroke (cr);
313 	}
314 }
315 
316 static void
draw_polygon(GkbdKeyboardDrawingRenderContext * context,GdkRGBA * fill_color,gint xkb_x,gint xkb_y,XkbPointRec * xkb_points,guint num_points,gdouble radius)317 draw_polygon (GkbdKeyboardDrawingRenderContext * context,
318 	      GdkRGBA * fill_color,
319 	      gint xkb_x,
320 	      gint xkb_y, XkbPointRec * xkb_points, guint num_points,
321 	      gdouble radius)
322 {
323 	GdkPoint *points;
324 	gboolean filled;
325 	gint i;
326 
327 	if (fill_color) {
328 		filled = TRUE;
329 	} else {
330 		fill_color = &context->dark_color;
331 		filled = FALSE;
332 	}
333 
334 	gdk_cairo_set_source_rgba (context->cr, fill_color);
335 
336 	points = g_new (GdkPoint, num_points);
337 
338 #ifdef KBDRAW_DEBUG
339 	printf ("    Polygon points:\n");
340 #endif
341 	for (i = 0; i < num_points; i++) {
342 		points[i].x =
343 		    xkb_to_pixmap_coord (context, xkb_x + xkb_points[i].x);
344 		points[i].y =
345 		    xkb_to_pixmap_coord (context, xkb_y + xkb_points[i].y);
346 #ifdef KBDRAW_DEBUG
347 		printf ("      %d, %d\n", points[i].x, points[i].y);
348 #endif
349 	}
350 
351 	rounded_polygon (context->cr, filled,
352 			 xkb_to_pixmap_double (context, radius),
353 			 points, num_points);
354 
355 	g_free (points);
356 }
357 
358 static void
curve_rectangle(cairo_t * cr,gdouble x0,gdouble y0,gdouble width,gdouble height,gdouble radius)359 curve_rectangle (cairo_t * cr,
360 		 gdouble x0,
361 		 gdouble y0, gdouble width, gdouble height, gdouble radius)
362 {
363 	gdouble x1, y1;
364 
365 	if (!width || !height)
366 		return;
367 
368 	x1 = x0 + width;
369 	y1 = y0 + height;
370 
371 	radius = MIN (radius, MIN (width / 2, height / 2));
372 
373 	cairo_move_to (cr, x0, y0 + radius);
374 	cairo_arc (cr, x0 + radius, y0 + radius, radius, M_PI,
375 		   3 * M_PI / 2);
376 	cairo_line_to (cr, x1 - radius, y0);
377 	cairo_arc (cr, x1 - radius, y0 + radius, radius, 3 * M_PI / 2,
378 		   2 * M_PI);
379 	cairo_line_to (cr, x1, y1 - radius);
380 	cairo_arc (cr, x1 - radius, y1 - radius, radius, 0, M_PI / 2);
381 	cairo_line_to (cr, x0 + radius, y1);
382 	cairo_arc (cr, x0 + radius, y1 - radius, radius, M_PI / 2, M_PI);
383 
384 	cairo_close_path (cr);
385 }
386 
387 static void
draw_curve_rectangle(cairo_t * cr,gboolean filled,GdkRGBA * color,gint x,gint y,gint width,gint height,gint radius)388 draw_curve_rectangle (cairo_t * cr,
389 		      gboolean filled,
390 		      GdkRGBA * color,
391 		      gint x, gint y, gint width, gint height, gint radius)
392 {
393 	curve_rectangle (cr, x, y, width, height, radius);
394 
395 	gdk_cairo_set_source_rgba (cr, color);
396 
397 	if (filled)
398 		cairo_fill (cr);
399 	else {
400 		cairo_set_line_width (cr, CAIRO_LINE_WIDTH);
401 		cairo_stroke (cr);
402 	}
403 }
404 
405 /* x, y, width, height are in the xkb coordinate system */
406 static void
draw_rectangle(GkbdKeyboardDrawingRenderContext * context,GdkRGBA * color,gint angle,gint xkb_x,gint xkb_y,gint xkb_width,gint xkb_height,gint radius)407 draw_rectangle (GkbdKeyboardDrawingRenderContext * context,
408 		GdkRGBA * color,
409 		gint angle,
410 		gint xkb_x, gint xkb_y, gint xkb_width, gint xkb_height,
411 		gint radius)
412 {
413 	if (angle == 0) {
414 		gint x, y, width, height;
415 		gboolean filled;
416 
417 		if (color) {
418 			filled = TRUE;
419 		} else {
420 			color = &context->dark_color;
421 			filled = FALSE;
422 		}
423 
424 		x = xkb_to_pixmap_coord (context, xkb_x);
425 		y = xkb_to_pixmap_coord (context, xkb_y);
426 		width =
427 		    xkb_to_pixmap_coord (context, xkb_x + xkb_width) - x;
428 		height =
429 		    xkb_to_pixmap_coord (context, xkb_y + xkb_height) - y;
430 
431 		draw_curve_rectangle (context->cr, filled, color,
432 				      x, y, width, height,
433 				      xkb_to_pixmap_double (context,
434 							    radius));
435 	} else {
436 		XkbPointRec points[4];
437 		gint x, y;
438 
439 		points[0].x = xkb_x;
440 		points[0].y = xkb_y;
441 		rotate_coordinate (xkb_x, xkb_y, xkb_x + xkb_width, xkb_y,
442 				   angle, &x, &y);
443 		points[1].x = x;
444 		points[1].y = y;
445 		rotate_coordinate (xkb_x, xkb_y, xkb_x + xkb_width,
446 				   xkb_y + xkb_height, angle, &x, &y);
447 		points[2].x = x;
448 		points[2].y = y;
449 		rotate_coordinate (xkb_x, xkb_y, xkb_x, xkb_y + xkb_height,
450 				   angle, &x, &y);
451 		points[3].x = x;
452 		points[3].y = y;
453 
454 		/* the points we've calculated are relative to 0,0 */
455 		draw_polygon (context, color, 0, 0, points, 4, radius);
456 	}
457 }
458 
459 static void
draw_outline(GkbdKeyboardDrawingRenderContext * context,XkbOutlineRec * outline,GdkRGBA * color,gint angle,gint origin_x,gint origin_y)460 draw_outline (GkbdKeyboardDrawingRenderContext * context,
461 	      XkbOutlineRec * outline,
462 	      GdkRGBA * color, gint angle, gint origin_x, gint origin_y)
463 {
464 #ifdef KBDRAW_DEBUG
465 	printf ("origin: %d, %d, num_points in %p: %d\n", origin_x,
466 		origin_y, outline, outline->num_points);
467 #endif
468 
469 	if (outline->num_points == 1) {
470 #ifdef KBDRAW_DEBUG
471 		printf
472 		    ("1 point (rectangle): width, height: %d, %d, radius %d\n",
473 		     outline->points[0].x, outline->points[0].y,
474 		     outline->corner_radius);
475 #endif
476 		if (color)
477 			draw_rectangle (context, color, angle, origin_x,
478 					origin_y, outline->points[0].x,
479 					outline->points[0].y,
480 					outline->corner_radius);
481 
482 		draw_rectangle (context, NULL, angle, origin_x,
483 				origin_y, outline->points[0].x,
484 				outline->points[0].y,
485 				outline->corner_radius);
486 	} else if (outline->num_points == 2) {
487 		gint rotated_x0, rotated_y0;
488 
489 		rotate_coordinate (origin_x, origin_y,
490 				   origin_x + outline->points[0].x,
491 				   origin_y + outline->points[0].y,
492 				   angle, &rotated_x0, &rotated_y0);
493 #ifdef KBDRAW_DEBUG
494 		printf
495 		    ("2 points (rectangle): from %d, %d, width, height: %d, %d, radius %d\n",
496 		     rotated_x0, rotated_y0, outline->points[1].x,
497 		     outline->points[1].y, outline->corner_radius);
498 #endif
499 		if (color)
500 			draw_rectangle (context, color, angle, rotated_x0,
501 					rotated_y0, outline->points[1].x,
502 					outline->points[1].y,
503 					outline->corner_radius);
504 		draw_rectangle (context, NULL, angle, rotated_x0,
505 				rotated_y0, outline->points[1].x,
506 				outline->points[1].y,
507 				outline->corner_radius);
508 	} else {
509 #ifdef KBDRAW_DEBUG
510 		printf ("multiple points (%d) from %d %d, radius %d\n",
511 			outline->num_points, origin_x, origin_y,
512 			outline->corner_radius);
513 #endif
514 		if (color)
515 			draw_polygon (context, color, origin_x, origin_y,
516 				      outline->points, outline->num_points,
517 				      outline->corner_radius);
518 		draw_polygon (context, NULL, origin_x, origin_y,
519 			      outline->points, outline->num_points,
520 			      outline->corner_radius);
521 	}
522 }
523 
524 /* see PSColorDef in xkbprint */
525 static gboolean
parse_xkb_color_spec(gchar * colorspec,GdkRGBA * color)526 parse_xkb_color_spec (gchar * colorspec, GdkRGBA * color)
527 {
528 	glong level;
529 
530 	color->alpha = 1;
531 	if (g_ascii_strcasecmp (colorspec, "black") == 0) {
532 		color->red = 0;
533 		color->green = 0;
534 		color->blue = 0;
535 	} else if (g_ascii_strcasecmp (colorspec, "white") == 0) {
536 		color->red = 1.0;
537 		color->green = 1.0;
538 		color->blue = 1.0;
539 	} else if (g_ascii_strncasecmp (colorspec, "grey", 4) == 0 ||
540 		   g_ascii_strncasecmp (colorspec, "gray", 4) == 0) {
541 		level = strtol (colorspec + 4, NULL, 10);
542 
543 		color->red = 1.0 - level / 100.0;
544 		color->green = 1.0 - level / 100.0;
545 		color->blue = 1.0 - level / 100.0;
546 	} else if (g_ascii_strcasecmp (colorspec, "red") == 0) {
547 		color->red = 1.0;
548 		color->green = 0;
549 		color->blue = 0;
550 	} else if (g_ascii_strcasecmp (colorspec, "green") == 0) {
551 		color->red = 0;
552 		color->green = 1.0;
553 		color->blue = 0;
554 	} else if (g_ascii_strcasecmp (colorspec, "blue") == 0) {
555 		color->red = 0;
556 		color->green = 0;
557 		color->blue = 1.0;
558 	} else if (g_ascii_strncasecmp (colorspec, "red", 3) == 0) {
559 		level = strtol (colorspec + 3, NULL, 10);
560 
561 		color->red = level / 100.0;
562 		color->green = 0;
563 		color->blue = 0;
564 	} else if (g_ascii_strncasecmp (colorspec, "green", 5) == 0) {
565 		level = strtol (colorspec + 5, NULL, 10);
566 
567 		color->red = 0;
568 		color->green = level / 100.0;
569 		color->blue = 0;
570 	} else if (g_ascii_strncasecmp (colorspec, "blue", 4) == 0) {
571 		level = strtol (colorspec + 4, NULL, 10);
572 
573 		color->red = 0;
574 		color->green = 0;
575 		color->blue = level / 100.0;
576 	} else
577 		return FALSE;
578 
579 	return TRUE;
580 }
581 
582 
583 static guint
find_keycode(GkbdKeyboardDrawing * drawing,gchar * key_name)584 find_keycode (GkbdKeyboardDrawing * drawing, gchar * key_name)
585 {
586 #define KEYSYM_NAME_MAX_LENGTH 4
587 	guint keycode;
588 	gint i, j;
589 	XkbKeyNamePtr pkey;
590 	XkbKeyAliasPtr palias;
591 	guint is_name_matched;
592 	gchar *src, *dst;
593 
594 	if (!drawing->xkb)
595 		return INVALID_KEYCODE;
596 
597 #ifdef KBDRAW_DEBUG
598 	printf ("    looking for keycode for (%c%c%c%c)\n",
599 		key_name[0], key_name[1], key_name[2], key_name[3]);
600 #endif
601 
602 	pkey = drawing->xkb->names->keys + drawing->xkb->min_key_code;
603 	for (keycode = drawing->xkb->min_key_code;
604 	     keycode <= drawing->xkb->max_key_code; keycode++) {
605 		is_name_matched = 1;
606 		src = key_name;
607 		dst = pkey->name;
608 		for (i = KEYSYM_NAME_MAX_LENGTH; --i >= 0;) {
609 			if ('\0' == *src)
610 				break;
611 			if (*src++ != *dst++) {
612 				is_name_matched = 0;
613 				break;
614 			}
615 		}
616 		if (is_name_matched) {
617 #ifdef KBDRAW_DEBUG
618 			printf ("      found keycode %u\n", keycode);
619 #endif
620 			return keycode;
621 		}
622 		pkey++;
623 	}
624 
625 	palias = drawing->xkb->names->key_aliases;
626 	for (j = drawing->xkb->names->num_key_aliases; --j >= 0;) {
627 		is_name_matched = 1;
628 		src = key_name;
629 		dst = palias->alias;
630 		for (i = KEYSYM_NAME_MAX_LENGTH; --i >= 0;) {
631 			if ('\0' == *src)
632 				break;
633 			if (*src++ != *dst++) {
634 				is_name_matched = 0;
635 				break;
636 			}
637 		}
638 
639 		if (is_name_matched) {
640 			keycode = find_keycode (drawing, palias->real);
641 #ifdef KBDRAW_DEBUG
642 			printf ("found alias keycode %u\n", keycode);
643 #endif
644 			return keycode;
645 		}
646 		palias++;
647 	}
648 
649 	return INVALID_KEYCODE;
650 }
651 
652 static void
set_markup(GkbdKeyboardDrawingRenderContext * context,gchar * txt)653 set_markup (GkbdKeyboardDrawingRenderContext * context, gchar * txt)
654 {
655 	PangoLayout *layout = context->layout;
656 	txt = strcmp ("<", txt) ? txt : "&lt;";
657 	txt = strcmp ("&", txt) ? txt : "&amp;";
658 	if (g_utf8_strlen (txt, -1) > 1) {
659 		gchar *buf =
660 		    g_strdup_printf ("<span size=\"x-small\">%s</span>",
661 				     txt);
662 		pango_layout_set_markup (layout, buf, -1);
663 		g_free (buf);
664 	} else {
665 		pango_layout_set_markup (layout, txt, -1);
666 	}
667 }
668 
669 static void
set_key_label_in_layout(GkbdKeyboardDrawingRenderContext * context,guint keyval)670 set_key_label_in_layout (GkbdKeyboardDrawingRenderContext * context,
671 			 guint keyval)
672 {
673 	gchar buf[5];
674 	gunichar uc;
675 
676 	switch (keyval) {
677 	case GDK_KEY_Scroll_Lock:
678 		set_markup (context, "Scroll\nLock");
679 		break;
680 
681 	case GDK_KEY_space:
682 		set_markup (context, "");
683 		break;
684 
685 	case GDK_KEY_Sys_Req:
686 		set_markup (context, "Sys Rq");
687 		break;
688 
689 	case GDK_KEY_Page_Up:
690 		set_markup (context, "Page\nUp");
691 		break;
692 
693 	case GDK_KEY_Page_Down:
694 		set_markup (context, "Page\nDown");
695 		break;
696 
697 	case GDK_KEY_Num_Lock:
698 		set_markup (context, "Num\nLock");
699 		break;
700 
701 	case GDK_KEY_KP_Page_Up:
702 		set_markup (context, "Pg Up");
703 		break;
704 
705 	case GDK_KEY_KP_Page_Down:
706 		set_markup (context, "Pg Dn");
707 		break;
708 
709 	case GDK_KEY_KP_Home:
710 		set_markup (context, "Home");
711 		break;
712 
713 	case GDK_KEY_KP_Left:
714 		set_markup (context, "Left");
715 		break;
716 
717 	case GDK_KEY_KP_End:
718 		set_markup (context, "End");
719 		break;
720 
721 	case GDK_KEY_KP_Up:
722 		set_markup (context, "Up");
723 		break;
724 
725 	case GDK_KEY_KP_Begin:
726 		set_markup (context, "Begin");
727 		break;
728 
729 	case GDK_KEY_KP_Right:
730 		set_markup (context, "Right");
731 		break;
732 
733 	case GDK_KEY_KP_Enter:
734 		set_markup (context, "Enter");
735 		break;
736 
737 	case GDK_KEY_KP_Down:
738 		set_markup (context, "Down");
739 		break;
740 
741 	case GDK_KEY_KP_Insert:
742 		set_markup (context, "Ins");
743 		break;
744 
745 	case GDK_KEY_KP_Delete:
746 		set_markup (context, "Del");
747 		break;
748 
749 	case GDK_KEY_dead_grave:
750 		set_markup (context, "ˋ");
751 		break;
752 
753 	case GDK_KEY_dead_acute:
754 		set_markup (context, "ˊ");
755 		break;
756 
757 	case GDK_KEY_dead_circumflex:
758 		set_markup (context, "ˆ");
759 		break;
760 
761 	case GDK_KEY_dead_tilde:
762 		set_markup (context, "~");
763 		break;
764 
765 	case GDK_KEY_dead_macron:
766 		set_markup (context, "ˉ");
767 		break;
768 
769 	case GDK_KEY_dead_breve:
770 		set_markup (context, "˘");
771 		break;
772 
773 	case GDK_KEY_dead_abovedot:
774 		set_markup (context, "˙");
775 		break;
776 
777 	case GDK_KEY_dead_diaeresis:
778 		set_markup (context, "¨");
779 		break;
780 
781 	case GDK_KEY_dead_abovering:
782 		set_markup (context, "˚");
783 		break;
784 
785 	case GDK_KEY_dead_doubleacute:
786 		set_markup (context, "˝");
787 		break;
788 
789 	case GDK_KEY_dead_caron:
790 		set_markup (context, "ˇ");
791 		break;
792 
793 	case GDK_KEY_dead_cedilla:
794 		set_markup (context, "¸");
795 		break;
796 
797 	case GDK_KEY_dead_ogonek:
798 		set_markup (context, "˛");
799 		break;
800 
801 		/* case GDK_KEY_dead_iota:
802 		 * case GDK_KEY_dead_voiced_sound:
803 		 * case GDK_KEY_dead_semivoiced_sound: */
804 
805 	case GDK_KEY_dead_belowdot:
806 		set_markup (context, " ̣");
807 		break;
808 
809 	case GDK_KEY_horizconnector:
810 		set_markup (context, "horiz\nconn");
811 		break;
812 
813 	case GDK_KEY_Mode_switch:
814 		set_markup (context, "AltGr");
815 		break;
816 
817 	case GDK_KEY_Multi_key:
818 		set_markup (context, "Compose");
819 		break;
820 
821 	case GDK_KEY_VoidSymbol:
822 		set_markup (context, "");
823 		break;
824 
825 	default:
826 		uc = gdk_keyval_to_unicode (keyval);
827 		if (uc != 0 && g_unichar_isgraph (uc)) {
828 			buf[g_unichar_to_utf8 (uc, buf)] = '\0';
829 			set_markup (context, buf);
830 		} else {
831 			gchar *name = gdk_keyval_name (keyval);
832 			if (name) {
833 				gchar *fixed_name = g_strdup (name), *p;
834 				/* Replace underscores with spaces */
835 				for (p = fixed_name; *p; p++)
836 					if (*p == '_')
837 						*p = ' ';
838 				/* Get rid of scary ISO_ prefix */
839 				if (g_strstr_len (fixed_name, -1, "ISO "))
840 					set_markup (context,
841 						    fixed_name + 4);
842 				else
843 					set_markup (context, fixed_name);
844 				g_free (fixed_name);
845 			} else
846 				set_markup (context, "");
847 		}
848 	}
849 }
850 
851 
852 static void
draw_pango_layout(GkbdKeyboardDrawingRenderContext * context,GkbdKeyboardDrawing * drawing,gint angle,gint x,gint y,gboolean is_pressed)853 draw_pango_layout (GkbdKeyboardDrawingRenderContext * context,
854 		   GkbdKeyboardDrawing * drawing,
855 		   gint angle, gint x, gint y, gboolean is_pressed)
856 {
857 	PangoLayout *layout = context->layout;
858 	GdkRGBA *pcolor, color;
859 	PangoLayoutLine *line;
860 	gint x_off, y_off;
861 	gint i;
862 
863 	if (is_pressed) {
864 		GtkStyleContext *style_context =
865 		    gtk_widget_get_style_context (GTK_WIDGET (drawing));
866 		pcolor = &color;
867 		gtk_style_context_get_color (style_context,
868 					     GTK_STATE_FLAG_SELECTED,
869 					     pcolor);
870 	} else {
871 		pcolor =
872 		    drawing->colors + (drawing->xkb->geom->label_color -
873 				       drawing->xkb->geom->colors);
874 	}
875 
876 	if (angle != context->angle) {
877 		PangoMatrix matrix = PANGO_MATRIX_INIT;
878 		pango_matrix_rotate (&matrix, -angle / 10.0);
879 		pango_context_set_matrix (pango_layout_get_context
880 					  (layout), &matrix);
881 		pango_layout_context_changed (layout);
882 		context->angle = angle;
883 	}
884 
885 	i = 0;
886 	y_off = 0;
887 	for (line = pango_layout_get_line (layout, i);
888 	     line != NULL; line = pango_layout_get_line (layout, ++i)) {
889 		GSList *runp;
890 		PangoRectangle line_extents;
891 
892 		x_off = 0;
893 
894 		for (runp = line->runs; runp != NULL; runp = runp->next) {
895 			PangoGlyphItem *run = runp->data;
896 			gint j;
897 
898 			for (j = 0; j < run->glyphs->num_glyphs; j++) {
899 				PangoGlyphGeometry *geometry;
900 
901 				geometry =
902 				    &run->glyphs->glyphs[j].geometry;
903 
904 				x_off += geometry->width;
905 			}
906 		}
907 
908 		pango_layout_line_get_extents (line, NULL, &line_extents);
909 		y_off +=
910 		    line_extents.height +
911 		    pango_layout_get_spacing (layout);
912 	}
913 
914 	cairo_move_to (context->cr, x, y);
915 	gdk_cairo_set_source_rgba (context->cr, pcolor);
916 	pango_cairo_show_layout (context->cr, layout);
917 }
918 
919 static void
draw_key_label_helper(GkbdKeyboardDrawingRenderContext * context,GkbdKeyboardDrawing * drawing,KeySym keysym,gint angle,GkbdKeyboardDrawingGroupLevelPosition glp,gint x,gint y,gint width,gint height,gint padding,gboolean is_pressed)920 draw_key_label_helper (GkbdKeyboardDrawingRenderContext * context,
921 		       GkbdKeyboardDrawing * drawing,
922 		       KeySym keysym,
923 		       gint angle,
924 		       GkbdKeyboardDrawingGroupLevelPosition glp,
925 		       gint x,
926 		       gint y, gint width, gint height, gint padding,
927 		       gboolean is_pressed)
928 {
929 	gint label_x, label_y, label_max_width, ycell;
930 
931 	if (keysym == 0)
932 		return;
933 #ifdef KBDRAW_DEBUG
934 	printf ("keysym: %04X(%c) at glp: %d\n",
935 		(unsigned) keysym, (char) keysym, (int) glp);
936 #endif
937 
938 	switch (glp) {
939 	case GKBD_KEYBOARD_DRAWING_POS_TOPLEFT:
940 	case GKBD_KEYBOARD_DRAWING_POS_BOTTOMLEFT:
941 		{
942 			ycell =
943 			    glp == GKBD_KEYBOARD_DRAWING_POS_BOTTOMLEFT;
944 
945 			rotate_coordinate (x, y, x + padding,
946 					   y + padding + (height -
947 							  2 * padding) *
948 					   ycell * 4 / 7, angle, &label_x,
949 					   &label_y);
950 			label_max_width =
951 			    PANGO_SCALE * (width - 2 * padding);
952 			break;
953 		}
954 	case GKBD_KEYBOARD_DRAWING_POS_TOPRIGHT:
955 	case GKBD_KEYBOARD_DRAWING_POS_BOTTOMRIGHT:
956 		{
957 			ycell =
958 			    glp == GKBD_KEYBOARD_DRAWING_POS_BOTTOMRIGHT;
959 
960 			rotate_coordinate (x, y,
961 					   x + padding + (width -
962 							  2 * padding) *
963 					   4 / 7,
964 					   y + padding + (height -
965 							  2 * padding) *
966 					   ycell * 4 / 7, angle, &label_x,
967 					   &label_y);
968 			label_max_width =
969 			    PANGO_SCALE * ((width - 2 * padding) -
970 					   (width - 2 * padding) * 4 / 7);
971 			break;
972 		}
973 	default:
974 		return;
975 	}
976 	set_key_label_in_layout (context, keysym);
977 	pango_layout_set_width (context->layout, label_max_width);
978 	label_y -= (pango_layout_get_line_count (context->layout) - 1) *
979 	    (pango_font_description_get_size (context->font_desc) /
980 	     PANGO_SCALE);
981 	cairo_save (context->cr);
982 	cairo_rectangle (context->cr, x + padding / 2, y + padding / 2,
983 			 width - padding, height - padding);
984 	cairo_clip (context->cr);
985 	draw_pango_layout (context, drawing, angle, label_x, label_y,
986 			   is_pressed);
987 	cairo_restore (context->cr);
988 }
989 
990 static void
draw_key_label(GkbdKeyboardDrawingRenderContext * context,GkbdKeyboardDrawing * drawing,guint keycode,gint angle,gint xkb_origin_x,gint xkb_origin_y,gint xkb_width,gint xkb_height,gboolean is_pressed)991 draw_key_label (GkbdKeyboardDrawingRenderContext * context,
992 		GkbdKeyboardDrawing * drawing,
993 		guint keycode,
994 		gint angle,
995 		gint xkb_origin_x,
996 		gint xkb_origin_y, gint xkb_width, gint xkb_height,
997 		gboolean is_pressed)
998 {
999 	gint x, y, width, height;
1000 	gint padding;
1001 	gint g, l, glp;
1002 
1003 	if (!drawing->xkb || !drawing->groupLevels || keycode == INVALID_KEYCODE)
1004 		return;
1005 
1006 	padding = 23 * context->scale_numerator / context->scale_denominator;	/* 2.3mm */
1007 
1008 	x = xkb_to_pixmap_coord (context, xkb_origin_x);
1009 	y = xkb_to_pixmap_coord (context, xkb_origin_y);
1010 	width =
1011 	    xkb_to_pixmap_coord (context, xkb_origin_x + xkb_width) - x;
1012 	height =
1013 	    xkb_to_pixmap_coord (context, xkb_origin_y + xkb_height) - y;
1014 
1015 	for (glp = GKBD_KEYBOARD_DRAWING_POS_TOPLEFT;
1016 	     glp < GKBD_KEYBOARD_DRAWING_POS_TOTAL; glp++) {
1017 		if (drawing->groupLevels[glp] == NULL)
1018 			continue;
1019 		g = drawing->groupLevels[glp]->group;
1020 		l = drawing->groupLevels[glp]->level;
1021 
1022 		if (g < 0 || g >= XkbKeyNumGroups (drawing->xkb, keycode))
1023 			continue;
1024 		if (l < 0
1025 		    || l >= XkbKeyGroupWidth (drawing->xkb, keycode, g))
1026 			continue;
1027 
1028 		/* Skip "exotic" levels like the "Ctrl" level in PC_SYSREQ */
1029 		if (l > 0) {
1030 			guint mods = XkbKeyKeyType (drawing->xkb, keycode,
1031 						    g)->mods.mask;
1032 			if ((mods & (ShiftMask | drawing->l3mod)) == 0)
1033 				continue;
1034 		}
1035 
1036 		if (drawing->track_modifiers) {
1037 			guint mods_rtrn;
1038 			KeySym keysym;
1039 
1040 			if (XkbTranslateKeyCode (drawing->xkb, keycode,
1041 						 XkbBuildCoreState
1042 						 (drawing->mods, g),
1043 						 &mods_rtrn, &keysym)) {
1044 				draw_key_label_helper (context, drawing,
1045 						       keysym, angle, glp,
1046 						       x, y, width, height,
1047 						       padding,
1048 						       is_pressed);
1049 				/* reverse y order */
1050 			}
1051 		} else {
1052 			KeySym keysym;
1053 
1054 			keysym =
1055 			    XkbKeySymEntry (drawing->xkb, keycode, l, g);
1056 
1057 			draw_key_label_helper (context, drawing, keysym,
1058 					       angle, glp, x, y, width,
1059 					       height, padding,
1060 					       is_pressed);
1061 			/* reverse y order */
1062 		}
1063 	}
1064 }
1065 
1066 /*
1067  * The x offset is calculated for complex shapes. It is the rightmost of the vertical lines in the outline
1068  */
1069 static gint
calc_shape_origin_offset_x(XkbOutlineRec * outline)1070 calc_shape_origin_offset_x (XkbOutlineRec * outline)
1071 {
1072 	gint rv = 0;
1073 	gint i;
1074 	XkbPointPtr point = outline->points;
1075 	if (outline->num_points < 3)
1076 		return 0;
1077 	for (i = outline->num_points; --i > 0;) {
1078 		gint x1 = point->x;
1079 		gint y1 = point++->y;
1080 		gint x2 = point->x;
1081 		gint y2 = point->y;
1082 
1083 		/*vertical, bottom to top (clock-wise), on the left */
1084 		if ((x1 == x2) && (y1 > y2) && (x1 > rv)) {
1085 			rv = x1;
1086 		}
1087 	}
1088 	return rv;
1089 }
1090 
1091 /* groups are from 0-3 */
1092 static void
draw_key(GkbdKeyboardDrawingRenderContext * context,GkbdKeyboardDrawing * drawing,GkbdKeyboardDrawingKey * key)1093 draw_key (GkbdKeyboardDrawingRenderContext * context,
1094 	  GkbdKeyboardDrawing * drawing, GkbdKeyboardDrawingKey * key)
1095 {
1096 	XkbShapeRec *shape;
1097 	GdkRGBA *pcolor, color;
1098 	XkbOutlineRec *outline;
1099 	int origin_offset_x;
1100 	/* gint i; */
1101 
1102 	if (!drawing->xkb)
1103 		return;
1104 
1105 #ifdef KBDRAW_DEBUG
1106 	printf ("shape: %p (base %p, index %d)\n",
1107 		drawing->xkb->geom->shapes + key->xkbkey->shape_ndx,
1108 		drawing->xkb->geom->shapes, key->xkbkey->shape_ndx);
1109 #endif
1110 
1111 	shape = drawing->xkb->geom->shapes + key->xkbkey->shape_ndx;
1112 
1113 	if (key->pressed) {
1114 		GtkStyleContext *style_context =
1115 		    gtk_widget_get_style_context (GTK_WIDGET (drawing));
1116 		pcolor = &color;
1117 		gtk_style_context_get_background_color (style_context,
1118 							GTK_STATE_FLAG_SELECTED,
1119 							pcolor);
1120 	} else
1121 		pcolor = drawing->colors + key->xkbkey->color_ndx;
1122 
1123 #ifdef KBDRAW_DEBUG
1124 	printf
1125 	    (" outlines base in the shape: %p (total: %d), origin: (%d, %d), angle %d, colored: %s\n",
1126 	     shape->outlines, shape->num_outlines, key->origin_x,
1127 	     key->origin_y, key->angle, pcolor ? "yes" : "no");
1128 #endif
1129 
1130 	/* draw the primary outline */
1131 	outline = shape->primary ? shape->primary : shape->outlines;
1132 	draw_outline (context, outline, pcolor, key->angle, key->origin_x,
1133 		      key->origin_y);
1134 #if 0
1135 	/* don't draw other outlines for now, since
1136 	 * the text placement does not take them into account
1137 	 */
1138 	for (i = 0; i < shape->num_outlines; i++) {
1139 		if (shape->outlines + i == shape->approx ||
1140 		    shape->outlines + i == shape->primary)
1141 			continue;
1142 		draw_outline (context, shape->outlines + i, NULL,
1143 			      key->angle, key->origin_x, key->origin_y);
1144 	}
1145 #endif
1146 	origin_offset_x = calc_shape_origin_offset_x (outline);
1147 	draw_key_label (context, drawing, key->keycode, key->angle,
1148 			key->origin_x + origin_offset_x, key->origin_y,
1149 			shape->bounds.x2, shape->bounds.y2, key->pressed);
1150 }
1151 
1152 static void
invalidate_region(GkbdKeyboardDrawing * drawing,gdouble angle,gint origin_x,gint origin_y,XkbShapeRec * shape)1153 invalidate_region (GkbdKeyboardDrawing * drawing,
1154 		   gdouble angle,
1155 		   gint origin_x, gint origin_y, XkbShapeRec * shape)
1156 {
1157 	GdkPoint points[4];
1158 	GtkAllocation alloc;
1159 	gint x_min, x_max, y_min, y_max;
1160 	gint x, y, width, height;
1161 	gint xx, yy;
1162 
1163 	rotate_coordinate (0, 0, 0, 0, angle, &xx, &yy);
1164 	points[0].x = xx;
1165 	points[0].y = yy;
1166 	rotate_coordinate (0, 0, shape->bounds.x2, 0, angle, &xx, &yy);
1167 	points[1].x = xx;
1168 	points[1].y = yy;
1169 	rotate_coordinate (0, 0, shape->bounds.x2, shape->bounds.y2, angle,
1170 			   &xx, &yy);
1171 	points[2].x = xx;
1172 	points[2].y = yy;
1173 	rotate_coordinate (0, 0, 0, shape->bounds.y2, angle, &xx, &yy);
1174 	points[3].x = xx;
1175 	points[3].y = yy;
1176 
1177 	x_min =
1178 	    MIN (MIN (points[0].x, points[1].x),
1179 		 MIN (points[2].x, points[3].x));
1180 	x_max =
1181 	    MAX (MAX (points[0].x, points[1].x),
1182 		 MAX (points[2].x, points[3].x));
1183 	y_min =
1184 	    MIN (MIN (points[0].y, points[1].y),
1185 		 MIN (points[2].y, points[3].y));
1186 	y_max =
1187 	    MAX (MAX (points[0].y, points[1].y),
1188 		 MAX (points[2].y, points[3].y));
1189 
1190 	x = xkb_to_pixmap_coord (drawing->renderContext,
1191 				 origin_x + x_min) - 6;
1192 	y = xkb_to_pixmap_coord (drawing->renderContext,
1193 				 origin_y + y_min) - 6;
1194 	width =
1195 	    xkb_to_pixmap_coord (drawing->renderContext,
1196 				 x_max - x_min) + 12;
1197 	height =
1198 	    xkb_to_pixmap_coord (drawing->renderContext,
1199 				 y_max - y_min) + 12;
1200 
1201 	gtk_widget_get_allocation (GTK_WIDGET (drawing), &alloc);
1202 	gtk_widget_queue_draw_area (GTK_WIDGET (drawing),
1203 				    x + alloc.x, y + alloc.y,
1204 				    width, height);
1205 }
1206 
1207 static void
invalidate_indicator_doodad_region(GkbdKeyboardDrawing * drawing,GkbdKeyboardDrawingDoodad * doodad)1208 invalidate_indicator_doodad_region (GkbdKeyboardDrawing * drawing,
1209 				    GkbdKeyboardDrawingDoodad * doodad)
1210 {
1211 	if (!drawing->xkb)
1212 		return;
1213 
1214 	invalidate_region (drawing,
1215 			   doodad->angle,
1216 			   doodad->origin_x +
1217 			   doodad->doodad->indicator.left,
1218 			   doodad->origin_y +
1219 			   doodad->doodad->indicator.top,
1220 			   &drawing->xkb->geom->shapes[doodad->doodad->
1221 						       indicator.
1222 						       shape_ndx]);
1223 }
1224 
1225 static void
invalidate_key_region(GkbdKeyboardDrawing * drawing,GkbdKeyboardDrawingKey * key)1226 invalidate_key_region (GkbdKeyboardDrawing * drawing,
1227 		       GkbdKeyboardDrawingKey * key)
1228 {
1229 	if (!drawing->xkb)
1230 		return;
1231 
1232 	invalidate_region (drawing,
1233 			   key->angle,
1234 			   key->origin_x,
1235 			   key->origin_y,
1236 			   &drawing->xkb->geom->shapes[key->xkbkey->
1237 						       shape_ndx]);
1238 }
1239 
1240 static void
draw_text_doodad(GkbdKeyboardDrawingRenderContext * context,GkbdKeyboardDrawing * drawing,GkbdKeyboardDrawingDoodad * doodad,XkbTextDoodadRec * text_doodad)1241 draw_text_doodad (GkbdKeyboardDrawingRenderContext * context,
1242 		  GkbdKeyboardDrawing * drawing,
1243 		  GkbdKeyboardDrawingDoodad * doodad,
1244 		  XkbTextDoodadRec * text_doodad)
1245 {
1246 	gint x, y;
1247 	if (!drawing->xkb)
1248 		return;
1249 
1250 	x = xkb_to_pixmap_coord (context,
1251 				 doodad->origin_x + text_doodad->left);
1252 	y = xkb_to_pixmap_coord (context,
1253 				 doodad->origin_y + text_doodad->top);
1254 
1255 	set_markup (context, text_doodad->text);
1256 	draw_pango_layout (context, drawing, doodad->angle, x, y, FALSE);
1257 }
1258 
1259 static void
draw_indicator_doodad(GkbdKeyboardDrawingRenderContext * context,GkbdKeyboardDrawing * drawing,GkbdKeyboardDrawingDoodad * doodad,XkbIndicatorDoodadRec * indicator_doodad)1260 draw_indicator_doodad (GkbdKeyboardDrawingRenderContext * context,
1261 		       GkbdKeyboardDrawing * drawing,
1262 		       GkbdKeyboardDrawingDoodad * doodad,
1263 		       XkbIndicatorDoodadRec * indicator_doodad)
1264 {
1265 	GdkRGBA *color;
1266 	XkbShapeRec *shape;
1267 	gint i;
1268 
1269 	if (!drawing->xkb)
1270 		return;
1271 
1272 	shape = drawing->xkb->geom->shapes + indicator_doodad->shape_ndx;
1273 
1274 	color = drawing->colors + (doodad->on ?
1275 				   indicator_doodad->on_color_ndx :
1276 				   indicator_doodad->off_color_ndx);
1277 
1278 	for (i = 0; i < 1; i++)
1279 		draw_outline (context, shape->outlines + i, color,
1280 			      doodad->angle,
1281 			      doodad->origin_x + indicator_doodad->left,
1282 			      doodad->origin_y + indicator_doodad->top);
1283 }
1284 
1285 static void
draw_shape_doodad(GkbdKeyboardDrawingRenderContext * context,GkbdKeyboardDrawing * drawing,GkbdKeyboardDrawingDoodad * doodad,XkbShapeDoodadRec * shape_doodad)1286 draw_shape_doodad (GkbdKeyboardDrawingRenderContext * context,
1287 		   GkbdKeyboardDrawing * drawing,
1288 		   GkbdKeyboardDrawingDoodad * doodad,
1289 		   XkbShapeDoodadRec * shape_doodad)
1290 {
1291 	XkbShapeRec *shape;
1292 	GdkRGBA *color;
1293 	gint i;
1294 
1295 	if (!drawing->xkb)
1296 		return;
1297 
1298 	shape = drawing->xkb->geom->shapes + shape_doodad->shape_ndx;
1299 	color = drawing->colors + shape_doodad->color_ndx;
1300 
1301 	/* draw the primary outline filled */
1302 	draw_outline (context,
1303 		      shape->primary ? shape->primary : shape->outlines,
1304 		      color, doodad->angle,
1305 		      doodad->origin_x + shape_doodad->left,
1306 		      doodad->origin_y + shape_doodad->top);
1307 
1308 	/* stroke the other outlines */
1309 	for (i = 0; i < shape->num_outlines; i++) {
1310 		if (shape->outlines + i == shape->approx ||
1311 		    shape->outlines + i == shape->primary)
1312 			continue;
1313 		draw_outline (context, shape->outlines + i, NULL,
1314 			      doodad->angle,
1315 			      doodad->origin_x + shape_doodad->left,
1316 			      doodad->origin_y + shape_doodad->top);
1317 	}
1318 }
1319 
1320 static void
draw_doodad(GkbdKeyboardDrawingRenderContext * context,GkbdKeyboardDrawing * drawing,GkbdKeyboardDrawingDoodad * doodad)1321 draw_doodad (GkbdKeyboardDrawingRenderContext * context,
1322 	     GkbdKeyboardDrawing * drawing,
1323 	     GkbdKeyboardDrawingDoodad * doodad)
1324 {
1325 	switch (doodad->doodad->any.type) {
1326 	case XkbOutlineDoodad:
1327 	case XkbSolidDoodad:
1328 		draw_shape_doodad (context, drawing, doodad,
1329 				   &doodad->doodad->shape);
1330 		break;
1331 
1332 	case XkbTextDoodad:
1333 		draw_text_doodad (context, drawing, doodad,
1334 				  &doodad->doodad->text);
1335 		break;
1336 
1337 	case XkbIndicatorDoodad:
1338 		draw_indicator_doodad (context, drawing, doodad,
1339 				       &doodad->doodad->indicator);
1340 		break;
1341 
1342 	case XkbLogoDoodad:
1343 		/* g_print ("draw_doodad: logo: %s\n", doodad->doodad->logo.logo_name); */
1344 		/* XkbLogoDoodadRec is essentially a subclass of XkbShapeDoodadRec */
1345 		draw_shape_doodad (context, drawing, doodad,
1346 				   &doodad->doodad->shape);
1347 		break;
1348 	}
1349 }
1350 
1351 typedef struct {
1352 	GkbdKeyboardDrawing *drawing;
1353 	GkbdKeyboardDrawingRenderContext *context;
1354 } DrawKeyboardItemData;
1355 
1356 static void
draw_keyboard_item(GkbdKeyboardDrawingItem * item,DrawKeyboardItemData * data)1357 draw_keyboard_item (GkbdKeyboardDrawingItem * item,
1358 		    DrawKeyboardItemData * data)
1359 {
1360 	GkbdKeyboardDrawing *drawing = data->drawing;
1361 	GkbdKeyboardDrawingRenderContext *context = data->context;
1362 
1363 	if (!drawing->xkb)
1364 		return;
1365 
1366 	switch (item->type) {
1367 	case GKBD_KEYBOARD_DRAWING_ITEM_TYPE_INVALID:
1368 		break;
1369 
1370 	case GKBD_KEYBOARD_DRAWING_ITEM_TYPE_KEY:
1371 	case GKBD_KEYBOARD_DRAWING_ITEM_TYPE_KEY_EXTRA:
1372 		draw_key (context, drawing,
1373 			  (GkbdKeyboardDrawingKey *) item);
1374 		break;
1375 
1376 	case GKBD_KEYBOARD_DRAWING_ITEM_TYPE_DOODAD:
1377 		draw_doodad (context, drawing,
1378 			     (GkbdKeyboardDrawingDoodad *) item);
1379 		break;
1380 	}
1381 }
1382 
1383 static void
draw_keyboard_to_context(GkbdKeyboardDrawingRenderContext * context,GkbdKeyboardDrawing * drawing)1384 draw_keyboard_to_context (GkbdKeyboardDrawingRenderContext * context,
1385 			  GkbdKeyboardDrawing * drawing)
1386 {
1387 	DrawKeyboardItemData data = { drawing, context };
1388 #ifdef KBDRAW_DEBUG
1389 	printf ("mods: %d\n", drawing->mods);
1390 #endif
1391 	g_list_foreach (drawing->keyboard_items,
1392 			(GFunc) draw_keyboard_item, &data);
1393 }
1394 
1395 static gboolean
prepare_cairo(GkbdKeyboardDrawing * drawing,cairo_t * cr)1396 prepare_cairo (GkbdKeyboardDrawing * drawing, cairo_t * cr)
1397 {
1398 	GtkStateFlags state;
1399 	GtkStyleContext *style_context;
1400 	if (drawing == NULL)
1401 		return FALSE;
1402 
1403 	style_context =
1404 	    gtk_widget_get_style_context (GTK_WIDGET (drawing));
1405 	drawing->renderContext->cr = cr;
1406 	state = gtk_widget_get_state_flags (GTK_WIDGET (drawing));
1407 	gtk_style_context_get_background_color (style_context, state,
1408 						&drawing->
1409 						renderContext->dark_color);
1410 
1411 	/* same approach as gtk - dark color = background color * 0.7 */
1412 	drawing->renderContext->dark_color.red *= 0.7;
1413 	drawing->renderContext->dark_color.green *= 0.7;
1414 	drawing->renderContext->dark_color.blue *= 0.7;
1415 	return TRUE;
1416 }
1417 
1418 static void
draw_keyboard(GkbdKeyboardDrawing * drawing,cairo_t * cr)1419 draw_keyboard (GkbdKeyboardDrawing * drawing, cairo_t * cr)
1420 {
1421 	GtkStateFlags state =
1422 	    gtk_widget_get_state_flags (GTK_WIDGET (drawing));
1423 	GtkStyleContext *style_context =
1424 	    gtk_widget_get_style_context (GTK_WIDGET (drawing));
1425 	GtkAllocation allocation;
1426 
1427 	if (!drawing->xkb)
1428 		return;
1429 
1430 	gtk_widget_get_allocation (GTK_WIDGET (drawing), &allocation);
1431 
1432 	if (prepare_cairo (drawing, cr)) {
1433 		/* blank background */
1434 		GdkRGBA color;
1435 		gtk_style_context_get_background_color (style_context,
1436 							state, &color);
1437 		gdk_cairo_set_source_rgba (cr, &color);
1438 		cairo_paint (cr);
1439 #ifdef KBDRAW_DEBUG
1440 		GdkRGBA yellow = { 1.0, 1.0, 0, 1.0 };
1441 		gdk_cairo_set_source_rgba (cr, &yellow);
1442 
1443 		cairo_move_to (cr, 0, 0);
1444 		cairo_line_to (cr, allocation.width, 0);
1445 		cairo_line_to (cr, allocation.width, allocation.height);
1446 		cairo_line_to (cr, 0, allocation.height);
1447 		cairo_close_path (cr);
1448 
1449 		cairo_stroke (cr);
1450 #endif
1451 
1452 		draw_keyboard_to_context (drawing->renderContext, drawing);
1453 	}
1454 }
1455 
1456 static void
alloc_render_context(GkbdKeyboardDrawing * drawing)1457 alloc_render_context (GkbdKeyboardDrawing * drawing)
1458 {
1459 	GkbdKeyboardDrawingRenderContext *context =
1460 	    drawing->renderContext =
1461 	    g_new0 (GkbdKeyboardDrawingRenderContext, 1);
1462 
1463 	PangoContext *pangoContext =
1464 	    gtk_widget_get_pango_context (GTK_WIDGET (drawing));
1465 	context->layout = pango_layout_new (pangoContext);
1466 	pango_layout_set_ellipsize (context->layout, PANGO_ELLIPSIZE_END);
1467 
1468 	context->font_desc =
1469 		pango_font_description_copy (pango_context_get_font_description (pangoContext));
1470 	context->angle = 0;
1471 	context->scale_numerator = 1;
1472 	context->scale_denominator = 1;
1473 }
1474 
1475 static void
free_render_context(GkbdKeyboardDrawing * drawing)1476 free_render_context (GkbdKeyboardDrawing * drawing)
1477 {
1478 	GkbdKeyboardDrawingRenderContext *context = drawing->renderContext;
1479 	g_object_unref (G_OBJECT (context->layout));
1480 	pango_font_description_free (context->font_desc);
1481 
1482 	g_free (drawing->renderContext);
1483 	drawing->renderContext = NULL;
1484 }
1485 
1486 static gboolean
draw(GtkWidget * widget,cairo_t * cr,GkbdKeyboardDrawing * drawing)1487 draw (GtkWidget * widget, cairo_t * cr, GkbdKeyboardDrawing * drawing)
1488 {
1489 	if (!drawing->xkb)
1490 		return FALSE;
1491 
1492 	draw_keyboard (drawing, cr);
1493 	return FALSE;
1494 }
1495 
1496 static gboolean
context_setup_scaling(GkbdKeyboardDrawingRenderContext * context,GkbdKeyboardDrawing * drawing,gdouble width,gdouble height,gdouble dpi_x,gdouble dpi_y)1497 context_setup_scaling (GkbdKeyboardDrawingRenderContext * context,
1498 		       GkbdKeyboardDrawing * drawing,
1499 		       gdouble width, gdouble height,
1500 		       gdouble dpi_x, gdouble dpi_y)
1501 {
1502 	if (!drawing->xkb)
1503 		return FALSE;
1504 
1505 	if (drawing->xkb->geom->width_mm <= 0
1506 	    || drawing->xkb->geom->height_mm <= 0) {
1507 		g_critical
1508 		    ("keyboard geometry reports width or height as zero!");
1509 		return FALSE;
1510 	}
1511 
1512 	if (width * drawing->xkb->geom->height_mm <
1513 	    height * drawing->xkb->geom->width_mm) {
1514 		context->scale_numerator = width;
1515 		context->scale_denominator = drawing->xkb->geom->width_mm;
1516 	} else {
1517 		context->scale_numerator = height;
1518 		context->scale_denominator = drawing->xkb->geom->height_mm;
1519 	}
1520 
1521 	pango_font_description_set_size (context->font_desc,
1522 					 72 * KEY_FONT_SIZE * dpi_x *
1523 					 context->scale_numerator /
1524 					 context->scale_denominator);
1525 	pango_layout_set_spacing (context->layout,
1526 				  -160 * dpi_y * context->scale_numerator /
1527 				  context->scale_denominator);
1528 	pango_layout_set_font_description (context->layout,
1529 					   context->font_desc);
1530 
1531 	return TRUE;
1532 }
1533 
1534 static void
size_allocate(GtkWidget * widget,GtkAllocation * allocation,GkbdKeyboardDrawing * drawing)1535 size_allocate (GtkWidget * widget,
1536 	       GtkAllocation * allocation, GkbdKeyboardDrawing * drawing)
1537 {
1538 	GkbdKeyboardDrawingRenderContext *context = drawing->renderContext;
1539 
1540 	context_setup_scaling (context, drawing,
1541 			       allocation->width, allocation->height,
1542 			       50, 50);
1543 }
1544 
1545 static gint
key_event(GtkWidget * widget,GdkEventKey * event,GkbdKeyboardDrawing * drawing)1546 key_event (GtkWidget * widget,
1547 	   GdkEventKey * event, GkbdKeyboardDrawing * drawing)
1548 {
1549 	GkbdKeyboardDrawingKey *key;
1550 	if (!drawing->xkb)
1551 		return FALSE;
1552 
1553 	key = drawing->keys + event->hardware_keycode;
1554 
1555 	if (event->hardware_keycode > drawing->xkb->max_key_code ||
1556 	    event->hardware_keycode < drawing->xkb->min_key_code ||
1557 	    key->xkbkey == NULL) {
1558 		g_signal_emit (drawing,
1559 			       gkbd_keyboard_drawing_signals[BAD_KEYCODE],
1560 			       0, event->hardware_keycode);
1561 		return TRUE;
1562 	}
1563 
1564 	if ((event->type == GDK_KEY_PRESS && key->pressed) ||
1565 	    (event->type == GDK_KEY_RELEASE && !key->pressed))
1566 		return TRUE;
1567 	/* otherwise this event changes the state we believed we had before */
1568 
1569 	key->pressed = (event->type == GDK_KEY_PRESS);
1570 
1571 	invalidate_key_region (drawing, key);
1572 	return TRUE;
1573 }
1574 
1575 static gint
button_press_event(GtkWidget * widget,GdkEventButton * event,GkbdKeyboardDrawing * drawing)1576 button_press_event (GtkWidget * widget,
1577 		    GdkEventButton * event, GkbdKeyboardDrawing * drawing)
1578 {
1579 	if (!drawing->xkb)
1580 		return FALSE;
1581 
1582 	gtk_widget_grab_focus (widget);
1583 	return FALSE;
1584 }
1585 
1586 static gboolean
unpress_keys(GkbdKeyboardDrawing * drawing)1587 unpress_keys (GkbdKeyboardDrawing * drawing)
1588 {
1589 	gint i;
1590 
1591 	drawing->timeout = 0;
1592 
1593 	if (!drawing->xkb)
1594 		return FALSE;
1595 
1596 	for (i = drawing->xkb->min_key_code;
1597 	     i <= drawing->xkb->max_key_code; i++)
1598 		if (drawing->keys[i].pressed) {
1599 			drawing->keys[i].pressed = FALSE;
1600 			draw_key (drawing->renderContext, drawing,
1601 				  drawing->keys + i);
1602 			invalidate_key_region (drawing, drawing->keys + i);
1603 		}
1604 
1605 	return FALSE;
1606 }
1607 
1608 static gint
focus_event(GtkWidget * widget,GdkEventFocus * event,GkbdKeyboardDrawing * drawing)1609 focus_event (GtkWidget * widget,
1610 	     GdkEventFocus * event, GkbdKeyboardDrawing * drawing)
1611 {
1612 	if (event->in && drawing->timeout > 0) {
1613 		g_source_remove (drawing->timeout);
1614 		drawing->timeout = 0;
1615 	} else if (!drawing->timeout)
1616 		drawing->timeout =
1617 		    g_timeout_add (120, (GSourceFunc) unpress_keys,
1618 				   drawing);
1619 
1620 	return FALSE;
1621 }
1622 
1623 static gint
compare_keyboard_item_priorities(GkbdKeyboardDrawingItem * a,GkbdKeyboardDrawingItem * b)1624 compare_keyboard_item_priorities (GkbdKeyboardDrawingItem * a,
1625 				  GkbdKeyboardDrawingItem * b)
1626 {
1627 	if (a->priority > b->priority)
1628 		return 1;
1629 	else if (a->priority < b->priority)
1630 		return -1;
1631 	else
1632 		return 0;
1633 }
1634 
1635 static void
init_indicator_doodad(GkbdKeyboardDrawing * drawing,XkbDoodadRec * xkbdoodad,GkbdKeyboardDrawingDoodad * doodad)1636 init_indicator_doodad (GkbdKeyboardDrawing * drawing,
1637 		       XkbDoodadRec * xkbdoodad,
1638 		       GkbdKeyboardDrawingDoodad * doodad)
1639 {
1640 	if (!drawing->xkb)
1641 		return;
1642 
1643 	if (xkbdoodad->any.type == XkbIndicatorDoodad) {
1644 		gint index;
1645 		Atom iname = 0;
1646 		Atom sname = xkbdoodad->indicator.name;
1647 		unsigned long phys_indicators =
1648 		    drawing->xkb->indicators->phys_indicators;
1649 		Atom *pind = drawing->xkb->names->indicators;
1650 
1651 #ifdef KBDRAW_DEBUG
1652 		printf ("Looking for %d[%s]\n",
1653 			(int) sname, XGetAtomName (drawing->display,
1654 						   sname));
1655 #endif
1656 
1657 		for (index = 0; index < XkbNumIndicators; index++) {
1658 			iname = *pind++;
1659 			/* name matches and it is real */
1660 			if (iname == sname
1661 			    && (phys_indicators & (1 << index)))
1662 				break;
1663 			if (iname == 0)
1664 				break;
1665 		}
1666 		if (iname == 0)
1667 			g_warning ("Could not find indicator %d [%s]\n",
1668 				   (int) sname,
1669 				   XGetAtomName (drawing->display, sname));
1670 		else {
1671 #ifdef KBDRAW_DEBUG
1672 			printf ("Found in xkbdesc as %d\n", index);
1673 #endif
1674 			drawing->physical_indicators[index] = doodad;
1675 			/* Trying to obtain the real state, but if fail - just assume OFF */
1676 			if (!XkbGetNamedIndicator
1677 			    (drawing->display, sname, NULL, &doodad->on,
1678 			     NULL, NULL))
1679 				doodad->on = 0;
1680 		}
1681 	}
1682 }
1683 
1684 static void
init_keys_and_doodads(GkbdKeyboardDrawing * drawing)1685 init_keys_and_doodads (GkbdKeyboardDrawing * drawing)
1686 {
1687 	gint i, j, k;
1688 	gint x, y;
1689 
1690 	if (!drawing->xkb)
1691 		return;
1692 
1693 	for (i = 0; i < drawing->xkb->geom->num_doodads; i++) {
1694 		XkbDoodadRec *xkbdoodad = drawing->xkb->geom->doodads + i;
1695 		GkbdKeyboardDrawingDoodad *doodad =
1696 		    g_new (GkbdKeyboardDrawingDoodad, 1);
1697 
1698 		doodad->type = GKBD_KEYBOARD_DRAWING_ITEM_TYPE_DOODAD;
1699 		doodad->origin_x = 0;
1700 		doodad->origin_y = 0;
1701 		doodad->angle = 0;
1702 		doodad->priority = xkbdoodad->any.priority * 256 * 256;
1703 		doodad->doodad = xkbdoodad;
1704 
1705 		init_indicator_doodad (drawing, xkbdoodad, doodad);
1706 
1707 		drawing->keyboard_items =
1708 		    g_list_append (drawing->keyboard_items, doodad);
1709 	}
1710 
1711 	for (i = 0; i < drawing->xkb->geom->num_sections; i++) {
1712 		XkbSectionRec *section = drawing->xkb->geom->sections + i;
1713 		guint priority;
1714 
1715 #ifdef KBDRAW_DEBUG
1716 		printf ("initing section %d containing %d rows\n", i,
1717 			section->num_rows);
1718 #endif
1719 		x = section->left;
1720 		y = section->top;
1721 		priority = section->priority * 256 * 256;
1722 
1723 		for (j = 0; j < section->num_rows; j++) {
1724 			XkbRowRec *row = section->rows + j;
1725 
1726 #ifdef KBDRAW_DEBUG
1727 			printf ("  initing row %d\n", j);
1728 #endif
1729 			x = section->left + row->left;
1730 			y = section->top + row->top;
1731 
1732 			for (k = 0; k < row->num_keys; k++) {
1733 				XkbKeyRec *xkbkey = row->keys + k;
1734 				GkbdKeyboardDrawingKey *key;
1735 				XkbShapeRec *shape =
1736 				    drawing->xkb->geom->shapes +
1737 				    xkbkey->shape_ndx;
1738 				guint keycode = find_keycode (drawing,
1739 							      xkbkey->name.
1740 							      name);
1741 
1742 #ifdef KBDRAW_DEBUG
1743 				printf
1744 				    ("    initing key %d, shape: %p(%p + %d), code: %u\n",
1745 				     k, shape, drawing->xkb->geom->shapes,
1746 				     xkbkey->shape_ndx, keycode);
1747 #endif
1748 				if (row->vertical)
1749 					y += xkbkey->gap;
1750 				else
1751 					x += xkbkey->gap;
1752 
1753 				if (keycode >= drawing->xkb->min_key_code
1754 				    && keycode <=
1755 				    drawing->xkb->max_key_code) {
1756 					key = drawing->keys + keycode;
1757 					if (key->type ==
1758 					    GKBD_KEYBOARD_DRAWING_ITEM_TYPE_INVALID)
1759 					{
1760 						key->type =
1761 						    GKBD_KEYBOARD_DRAWING_ITEM_TYPE_KEY;
1762 					} else {
1763 						/* duplicate key for the same keycode,
1764 						   already defined as GKBD_KEYBOARD_DRAWING_ITEM_TYPE_KEY */
1765 						key =
1766 						    g_new0
1767 						    (GkbdKeyboardDrawingKey,
1768 						     1);
1769 						key->type =
1770 						    GKBD_KEYBOARD_DRAWING_ITEM_TYPE_KEY_EXTRA;
1771 					}
1772 				} else {
1773 					g_warning
1774 					    ("key %4.4s: keycode = %u; not in range %d..%d\n",
1775 					     xkbkey->name.name, keycode,
1776 					     drawing->xkb->min_key_code,
1777 					     drawing->xkb->max_key_code);
1778 
1779 					key =
1780 					    g_new0 (GkbdKeyboardDrawingKey,
1781 						    1);
1782 					key->type =
1783 					    GKBD_KEYBOARD_DRAWING_ITEM_TYPE_KEY_EXTRA;
1784 				}
1785 
1786 				key->xkbkey = xkbkey;
1787 				key->angle = section->angle;
1788 				rotate_coordinate (section->left,
1789 						   section->top, x, y,
1790 						   section->angle,
1791 						   &key->origin_x,
1792 						   &key->origin_y);
1793 				key->priority = priority;
1794 				key->keycode = keycode;
1795 
1796 				drawing->keyboard_items =
1797 				    g_list_append (drawing->keyboard_items,
1798 						   key);
1799 
1800 				if (row->vertical)
1801 					y += shape->bounds.y2;
1802 				else
1803 					x += shape->bounds.x2;
1804 
1805 				priority++;
1806 			}
1807 		}
1808 
1809 		for (j = 0; j < section->num_doodads; j++) {
1810 			XkbDoodadRec *xkbdoodad = section->doodads + j;
1811 			GkbdKeyboardDrawingDoodad *doodad =
1812 			    g_new (GkbdKeyboardDrawingDoodad, 1);
1813 
1814 			doodad->type =
1815 			    GKBD_KEYBOARD_DRAWING_ITEM_TYPE_DOODAD;
1816 			doodad->origin_x = x;
1817 			doodad->origin_y = y;
1818 			doodad->angle = section->angle;
1819 			doodad->priority =
1820 			    priority + xkbdoodad->any.priority;
1821 			doodad->doodad = xkbdoodad;
1822 
1823 			init_indicator_doodad (drawing, xkbdoodad, doodad);
1824 
1825 			drawing->keyboard_items =
1826 			    g_list_append (drawing->keyboard_items,
1827 					   doodad);
1828 		}
1829 	}
1830 
1831 	drawing->keyboard_items = g_list_sort (drawing->keyboard_items,
1832 					       (GCompareFunc)
1833 					       compare_keyboard_item_priorities);
1834 }
1835 
1836 static void
init_colors(GkbdKeyboardDrawing * drawing)1837 init_colors (GkbdKeyboardDrawing * drawing)
1838 {
1839 	gboolean result;
1840 	gint i;
1841 
1842 	if (!drawing->xkb)
1843 		return;
1844 
1845 	drawing->colors = g_new (GdkRGBA, drawing->xkb->geom->num_colors);
1846 
1847 	for (i = 0; i < drawing->xkb->geom->num_colors; i++) {
1848 		result =
1849 		    parse_xkb_color_spec (drawing->xkb->geom->colors[i].
1850 					  spec, drawing->colors + i);
1851 
1852 		if (!result)
1853 			g_warning
1854 			    ("init_colors: unable to parse color %s\n",
1855 			     drawing->xkb->geom->colors[i].spec);
1856 	}
1857 }
1858 
1859 static void
free_cdik(GkbdKeyboardDrawing * drawing)1860 free_cdik (			/*colors doodads indicators keys */
1861 		  GkbdKeyboardDrawing * drawing)
1862 {
1863 	GList *itemp;
1864 
1865 	if (!drawing->xkb)
1866 		return;
1867 
1868 	for (itemp = drawing->keyboard_items; itemp; itemp = itemp->next) {
1869 		GkbdKeyboardDrawingItem *item = itemp->data;
1870 
1871 		switch (item->type) {
1872 		case GKBD_KEYBOARD_DRAWING_ITEM_TYPE_INVALID:
1873 		case GKBD_KEYBOARD_DRAWING_ITEM_TYPE_KEY:
1874 			break;
1875 
1876 		case GKBD_KEYBOARD_DRAWING_ITEM_TYPE_KEY_EXTRA:
1877 		case GKBD_KEYBOARD_DRAWING_ITEM_TYPE_DOODAD:
1878 			g_free (item);
1879 			break;
1880 		}
1881 	}
1882 
1883 	g_list_free (drawing->keyboard_items);
1884 	drawing->keyboard_items = NULL;
1885 
1886 	g_free (drawing->keys);
1887 	g_free (drawing->colors);
1888 }
1889 
1890 static void
alloc_cdik(GkbdKeyboardDrawing * drawing)1891 alloc_cdik (GkbdKeyboardDrawing * drawing)
1892 {
1893 	if (!drawing->xkb)
1894 		return;
1895 
1896 	drawing->physical_indicators_size =
1897 	    drawing->xkb->indicators->phys_indicators + 1;
1898 	drawing->physical_indicators =
1899 	    g_new0 (GkbdKeyboardDrawingDoodad *,
1900 		    drawing->physical_indicators_size);
1901 	drawing->keys =
1902 	    g_new0 (GkbdKeyboardDrawingKey,
1903 		    drawing->xkb->max_key_code + 1);
1904 }
1905 
1906 static void
process_indicators_state_notify(XkbIndicatorNotifyEvent * iev,GkbdKeyboardDrawing * drawing)1907 process_indicators_state_notify (XkbIndicatorNotifyEvent * iev,
1908 				 GkbdKeyboardDrawing * drawing)
1909 {
1910 	/* Good question: should we track indicators when the keyboard is
1911 	   NOT really taken from the screen */
1912 	gint i;
1913 
1914 	for (i = 0; i <= drawing->xkb->indicators->phys_indicators; i++)
1915 		if (drawing->physical_indicators[i] != NULL
1916 		    && (iev->changed & 1 << i)) {
1917 			gint state = (iev->state & 1 << i) != FALSE;
1918 
1919 			if ((state && !drawing->physical_indicators[i]->on)
1920 			    || (!state
1921 				&& drawing->physical_indicators[i]->on)) {
1922 				drawing->physical_indicators[i]->on =
1923 				    state;
1924 				invalidate_indicator_doodad_region
1925 				    (drawing,
1926 				     drawing->physical_indicators[i]);
1927 			}
1928 		}
1929 }
1930 
1931 static GdkFilterReturn
xkb_state_notify_event_filter(GdkXEvent * gdkxev,GdkEvent * event,GkbdKeyboardDrawing * drawing)1932 xkb_state_notify_event_filter (GdkXEvent * gdkxev,
1933 			       GdkEvent * event,
1934 			       GkbdKeyboardDrawing * drawing)
1935 {
1936 #define group_change_mask (XkbGroupStateMask | XkbGroupBaseMask | XkbGroupLatchMask | XkbGroupLockMask)
1937 #define modifier_change_mask (XkbModifierStateMask | XkbModifierBaseMask | XkbModifierLatchMask | XkbModifierLockMask)
1938 
1939 	if (!drawing->xkb)
1940 		return GDK_FILTER_CONTINUE;
1941 
1942 	if (((XEvent *) gdkxev)->type == drawing->xkb_event_type) {
1943 		XkbEvent *kev = (XkbEvent *) gdkxev;
1944 		GtkAllocation allocation;
1945 		switch (kev->any.xkb_type) {
1946 		case XkbStateNotify:
1947 			if (((kev->state.changed & modifier_change_mask) &&
1948 			     drawing->track_modifiers))
1949 				gkbd_keyboard_drawing_set_mods
1950 				    (drawing,
1951 				     kev->state.compat_state);
1952 			break;
1953 
1954 		case XkbIndicatorStateNotify:
1955 			{
1956 				process_indicators_state_notify (&
1957 								 ((XkbEvent
1958 								   *)
1959 								  gdkxev)->
1960 indicators, drawing);
1961 			}
1962 			break;
1963 
1964 		case XkbIndicatorMapNotify:
1965 		case XkbControlsNotify:
1966 		case XkbNamesNotify:
1967 		case XkbNewKeyboardNotify:
1968 			{
1969 				XkbStateRec state;
1970 				memset (&state, 0, sizeof (state));
1971 				XkbGetState (drawing->display,
1972 					     XkbUseCoreKbd, &state);
1973 				if (drawing->track_modifiers)
1974 					gkbd_keyboard_drawing_set_mods
1975 					    (drawing, state.compat_state);
1976 				if (drawing->track_config)
1977 					gkbd_keyboard_drawing_set_keyboard
1978 					    (drawing, NULL);
1979 			}
1980 			break;
1981 		}
1982 	}
1983 
1984 	return GDK_FILTER_CONTINUE;
1985 }
1986 
1987 static void
destroy(GkbdKeyboardDrawing * drawing)1988 destroy (GkbdKeyboardDrawing * drawing)
1989 {
1990 	free_render_context (drawing);
1991 	gdk_window_remove_filter (NULL, (GdkFilterFunc)
1992 				  xkb_state_notify_event_filter, drawing);
1993 	if (drawing->timeout > 0) {
1994 		g_source_remove (drawing->timeout);
1995 		drawing->timeout = 0;
1996 	}
1997 }
1998 
1999 static void
style_changed(GkbdKeyboardDrawing * drawing)2000 style_changed (GkbdKeyboardDrawing * drawing)
2001 {
2002 	pango_layout_context_changed (drawing->renderContext->layout);
2003 }
2004 
2005 static void
gkbd_keyboard_drawing_init(GkbdKeyboardDrawing * drawing)2006 gkbd_keyboard_drawing_init (GkbdKeyboardDrawing * drawing)
2007 {
2008 	gint opcode = 0, error = 0, major = 1, minor = 0;
2009 	gint mask;
2010 
2011 	drawing->display =
2012 	    GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
2013 
2014 	if (!XkbQueryExtension
2015 	    (drawing->display, &opcode, &drawing->xkb_event_type, &error,
2016 	     &major, &minor))
2017 		g_critical
2018 		    ("XkbQueryExtension failed! Stuff probably won't work.");
2019 
2020 	/* XXX: this stuff probably doesn't matter.. also, gdk_screen_get_default can fail */
2021 	if (gtk_widget_has_screen (GTK_WIDGET (drawing)))
2022 		drawing->screen_num =
2023 		    gdk_screen_get_number (gtk_widget_get_screen
2024 					   (GTK_WIDGET (drawing)));
2025 	else
2026 		drawing->screen_num =
2027 		    gdk_screen_get_number (gdk_screen_get_default ());
2028 
2029 	alloc_render_context (drawing);
2030 
2031 	drawing->keyboard_items = NULL;
2032 	drawing->colors = NULL;
2033 
2034 	drawing->track_modifiers = 0;
2035 	drawing->track_config = 0;
2036 
2037 	gtk_widget_set_has_window (GTK_WIDGET (drawing), FALSE);
2038 
2039 	/* XXX: XkbClientMapMask | XkbIndicatorMapMask | XkbNamesMask | XkbGeometryMask */
2040 	drawing->xkb = XkbGetKeyboard (drawing->display,
2041 				       XkbGBN_GeometryMask |
2042 				       XkbGBN_KeyNamesMask |
2043 				       XkbGBN_OtherNamesMask |
2044 				       XkbGBN_SymbolsMask |
2045 				       XkbGBN_IndicatorMapMask,
2046 				       XkbUseCoreKbd);
2047 	if (drawing->xkb) {
2048 		XkbGetNames (drawing->display, XkbAllNamesMask, drawing->xkb);
2049 		XkbSelectEventDetails (drawing->display, XkbUseCoreKbd,
2050 				       XkbIndicatorStateNotify,
2051 				       drawing->xkb->indicators->phys_indicators,
2052 				       drawing->xkb->indicators->phys_indicators);
2053 	}
2054 
2055 	drawing->l3mod = XkbKeysymToModifiers (drawing->display,
2056 					       GDK_KEY_ISO_Level3_Shift);
2057 
2058 	drawing->xkbOnDisplay = TRUE;
2059 
2060 	alloc_cdik (drawing);
2061 
2062 	mask =
2063 	    (XkbStateNotifyMask | XkbNamesNotifyMask |
2064 	     XkbControlsNotifyMask | XkbIndicatorMapNotifyMask |
2065 	     XkbNewKeyboardNotifyMask);
2066 	XkbSelectEvents (drawing->display, XkbUseCoreKbd, mask, mask);
2067 
2068 	mask = XkbGroupStateMask | XkbModifierStateMask;
2069 	XkbSelectEventDetails (drawing->display, XkbUseCoreKbd,
2070 			       XkbStateNotify, mask, mask);
2071 
2072 	mask = (XkbGroupNamesMask | XkbIndicatorNamesMask);
2073 	XkbSelectEventDetails (drawing->display, XkbUseCoreKbd,
2074 			       XkbNamesNotify, mask, mask);
2075 	init_keys_and_doodads (drawing);
2076 	init_colors (drawing);
2077 
2078 	/* required to get key events */
2079 	gtk_widget_set_can_focus (GTK_WIDGET (drawing), TRUE);
2080 
2081 	gtk_widget_set_events (GTK_WIDGET (drawing),
2082 			       GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK |
2083 			       GDK_KEY_RELEASE_MASK | GDK_BUTTON_PRESS_MASK
2084 			       | GDK_FOCUS_CHANGE_MASK);
2085 	g_signal_connect (G_OBJECT (drawing), "draw",
2086 			  G_CALLBACK (draw), drawing);
2087 	g_signal_connect_after (G_OBJECT (drawing), "key-press-event",
2088 				G_CALLBACK (key_event), drawing);
2089 	g_signal_connect_after (G_OBJECT (drawing), "key-release-event",
2090 				G_CALLBACK (key_event), drawing);
2091 	g_signal_connect (G_OBJECT (drawing), "button-press-event",
2092 			  G_CALLBACK (button_press_event), drawing);
2093 	g_signal_connect (G_OBJECT (drawing), "focus-out-event",
2094 			  G_CALLBACK (focus_event), drawing);
2095 	g_signal_connect (G_OBJECT (drawing), "focus-in-event",
2096 			  G_CALLBACK (focus_event), drawing);
2097 	g_signal_connect (G_OBJECT (drawing), "size-allocate",
2098 			  G_CALLBACK (size_allocate), drawing);
2099 	g_signal_connect (G_OBJECT (drawing), "destroy",
2100 			  G_CALLBACK (destroy), drawing);
2101 	g_signal_connect (G_OBJECT (drawing), "style-set",
2102 			  G_CALLBACK (style_changed), drawing);
2103 
2104 	gdk_window_add_filter (NULL, (GdkFilterFunc)
2105 			       xkb_state_notify_event_filter, drawing);
2106 }
2107 
2108 GtkWidget *
gkbd_keyboard_drawing_new(void)2109 gkbd_keyboard_drawing_new (void)
2110 {
2111 	return
2112 	    GTK_WIDGET (g_object_new
2113 			(gkbd_keyboard_drawing_get_type (), NULL));
2114 }
2115 
2116 static GtkSizeRequestMode
get_request_mode(GtkWidget * widget)2117 get_request_mode (GtkWidget * widget)
2118 {
2119 	return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
2120 }
2121 
2122 static void
get_preferred_width(GtkWidget * widget,gint * minimum_width,gint * natural_width)2123 get_preferred_width (GtkWidget * widget,
2124 		     gint * minimum_width, gint * natural_width)
2125 {
2126 	GdkRectangle rect;
2127 	gint w, monitor;
2128 	GdkScreen *scr = NULL;
2129 
2130 	scr = gdk_screen_get_default ();
2131 	monitor = gdk_screen_get_primary_monitor (scr);
2132 
2133 	gdk_screen_get_monitor_geometry (scr, monitor, &rect);
2134 	w = rect.width;
2135 	*minimum_width = *natural_width = w - (w >> 2);
2136 }
2137 
2138 static void
get_preferred_height_for_width(GtkWidget * widget,gint width,gint * minimum_height,gint * natural_height)2139 get_preferred_height_for_width (GtkWidget * widget,
2140 				gint width,
2141 				gint * minimum_height,
2142 				gint * natural_height)
2143 {
2144 	GkbdKeyboardDrawing *drawing = GKBD_KEYBOARD_DRAWING (widget);
2145 	*minimum_height = *natural_height =
2146 	    width * drawing->xkb->geom->height_mm /
2147 	    drawing->xkb->geom->width_mm;
2148 }
2149 
2150 static void
gkbd_keyboard_drawing_class_init(GkbdKeyboardDrawingClass * klass)2151 gkbd_keyboard_drawing_class_init (GkbdKeyboardDrawingClass * klass)
2152 {
2153 	klass->bad_keycode = NULL;
2154 	GTK_WIDGET_CLASS (klass)->get_preferred_height_for_width =
2155 	    get_preferred_height_for_width;
2156 	GTK_WIDGET_CLASS (klass)->get_preferred_width =
2157 	    get_preferred_width;
2158 	GTK_WIDGET_CLASS (klass)->get_request_mode = get_request_mode;
2159 
2160 	gkbd_keyboard_drawing_signals[BAD_KEYCODE] =
2161 	    g_signal_new ("bad-keycode", gkbd_keyboard_drawing_get_type (),
2162 			  G_SIGNAL_RUN_FIRST,
2163 			  G_STRUCT_OFFSET (GkbdKeyboardDrawingClass,
2164 					   bad_keycode), NULL, NULL,
2165 			  gkbd_keyboard_drawing_VOID__UINT, G_TYPE_NONE, 1,
2166 			  G_TYPE_UINT);
2167 }
2168 
2169 GType
gkbd_keyboard_drawing_get_type(void)2170 gkbd_keyboard_drawing_get_type (void)
2171 {
2172 	static GType gkbd_keyboard_drawing_type = 0;
2173 
2174 	if (!gkbd_keyboard_drawing_type) {
2175 		static const GTypeInfo gkbd_keyboard_drawing_info = {
2176 			sizeof (GkbdKeyboardDrawingClass),
2177 			NULL,	/* base_init */
2178 			NULL,	/* base_finalize */
2179 			(GClassInitFunc) gkbd_keyboard_drawing_class_init,
2180 			NULL,	/* class_finalize */
2181 			NULL,	/* class_data */
2182 			sizeof (GkbdKeyboardDrawing),
2183 			0,	/* n_preallocs */
2184 			(GInstanceInitFunc) gkbd_keyboard_drawing_init,
2185 		};
2186 
2187 		gkbd_keyboard_drawing_type =
2188 		    g_type_register_static (GTK_TYPE_DRAWING_AREA,
2189 					    "GkbdKeyboardDrawing",
2190 					    &gkbd_keyboard_drawing_info,
2191 					    0);
2192 	}
2193 
2194 	return gkbd_keyboard_drawing_type;
2195 }
2196 
2197 void
gkbd_keyboard_drawing_set_mods(GkbdKeyboardDrawing * drawing,guint mods)2198 gkbd_keyboard_drawing_set_mods (GkbdKeyboardDrawing * drawing, guint mods)
2199 {
2200 #ifdef KBDRAW_DEBUG
2201 	printf ("set_mods: %d\n", mods);
2202 #endif
2203 	if (mods != drawing->mods) {
2204 		drawing->mods = mods;
2205 		gtk_widget_queue_draw (GTK_WIDGET (drawing));
2206 	}
2207 }
2208 
2209 /**
2210  * gkbd_keyboard_drawing_render:
2211  * @kbdrawing: keyboard layout to render
2212  * @cr:        Cairo context to render to
2213  * @layout:    Pango layout to use to render text
2214  * @x:         left coordinate (pixels) of region to render in
2215  * @y:         top coordinate (pixels) of region to render in
2216  * @width:     width (pixels) of region to render in
2217  * @height:    height (pixels) of region to render in
2218  *
2219  * Renders a keyboard layout to a cairo_t context.  @cr and @layout can be got
2220  * from e.g. a GtkWidget or a GtkPrintContext.  @cr and @layout may be modified
2221  * by the function but will not be unreffed.
2222  *
2223  * Returns: %TRUE on success, %FALSE on failure
2224  */
2225 gboolean
gkbd_keyboard_drawing_render(GkbdKeyboardDrawing * kbdrawing,cairo_t * cr,PangoLayout * layout,double x,double y,double width,double height,double dpi_x,double dpi_y)2226 gkbd_keyboard_drawing_render (GkbdKeyboardDrawing * kbdrawing,
2227 			      cairo_t * cr,
2228 			      PangoLayout * layout,
2229 			      double x, double y,
2230 			      double width, double height,
2231 			      double dpi_x, double dpi_y)
2232 {
2233 	GtkStateFlags state =
2234 	    gtk_widget_get_state_flags (GTK_WIDGET (kbdrawing));
2235 	GtkStyleContext *style_context =
2236 	    gtk_widget_get_style_context (GTK_WIDGET (kbdrawing));
2237 	PangoContext *pangoContext =
2238 		gtk_widget_get_pango_context (GTK_WIDGET (kbdrawing));
2239 	GkbdKeyboardDrawingRenderContext context = {
2240 		cr,
2241 		kbdrawing->renderContext->angle,
2242 		layout,
2243 		pango_font_description_copy (pango_context_get_font_description (pangoContext)),
2244 		1, 1
2245 	};
2246 
2247 	gtk_style_context_get_background_color (style_context, state,
2248 						&context.dark_color);
2249 
2250 	if (!context_setup_scaling (&context, kbdrawing, width, height,
2251 				    dpi_x, dpi_y))
2252 		return FALSE;
2253 	cairo_translate (cr, x, y);
2254 
2255 	draw_keyboard_to_context (&context, kbdrawing);
2256 
2257 	pango_font_description_free (context.font_desc);
2258 
2259 	return TRUE;
2260 }
2261 
2262 /**
2263  * gkbd_keyboard_drawing_set_keyboard: (skip)
2264  */
2265 gboolean
gkbd_keyboard_drawing_set_keyboard(GkbdKeyboardDrawing * drawing,XkbComponentNamesRec * names)2266 gkbd_keyboard_drawing_set_keyboard (GkbdKeyboardDrawing * drawing,
2267 				    XkbComponentNamesRec * names)
2268 {
2269 	GtkAllocation allocation;
2270 
2271 	free_cdik (drawing);
2272 	if (drawing->xkb)
2273 		XkbFreeKeyboard (drawing->xkb, 0, TRUE);	/* free_all = TRUE */
2274 	drawing->xkb = NULL;
2275 
2276 	if (names) {
2277 		drawing->xkb =
2278 		    XkbGetKeyboardByName (drawing->display, XkbUseCoreKbd,
2279 					  names, 0,
2280 					  XkbGBN_GeometryMask |
2281 					  XkbGBN_KeyNamesMask |
2282 					  XkbGBN_OtherNamesMask |
2283 					  XkbGBN_ClientSymbolsMask |
2284 					  XkbGBN_IndicatorMapMask, FALSE);
2285 		drawing->xkbOnDisplay = FALSE;
2286 	} else {
2287 		drawing->xkb = XkbGetKeyboard (drawing->display,
2288 					       XkbGBN_GeometryMask |
2289 					       XkbGBN_KeyNamesMask |
2290 					       XkbGBN_OtherNamesMask |
2291 					       XkbGBN_SymbolsMask |
2292 					       XkbGBN_IndicatorMapMask,
2293 					       XkbUseCoreKbd);
2294 		XkbGetNames (drawing->display, XkbAllNamesMask,
2295 			     drawing->xkb);
2296 		drawing->xkbOnDisplay = TRUE;
2297 	}
2298 
2299 	if (drawing->xkb) {
2300 		XkbSelectEventDetails (drawing->display, XkbUseCoreKbd,
2301 				       XkbIndicatorStateNotify,
2302 				       drawing->xkb->indicators->phys_indicators,
2303 				       drawing->xkb->indicators->phys_indicators);
2304 	}
2305 
2306 	alloc_cdik (drawing);
2307 
2308 	init_keys_and_doodads (drawing);
2309 	init_colors (drawing);
2310 
2311 	gtk_widget_get_allocation (GTK_WIDGET (drawing), &allocation);
2312 	size_allocate (GTK_WIDGET (drawing), &allocation, drawing);
2313 	gtk_widget_queue_draw (GTK_WIDGET (drawing));
2314 
2315 	return TRUE;
2316 }
2317 
2318 G_CONST_RETURN gchar *
gkbd_keyboard_drawing_get_keycodes(GkbdKeyboardDrawing * drawing)2319 gkbd_keyboard_drawing_get_keycodes (GkbdKeyboardDrawing * drawing)
2320 {
2321 	if (!drawing->xkb || drawing->xkb->names->keycodes <= 0)
2322 		return NULL;
2323 	else
2324 		return XGetAtomName (drawing->display,
2325 				     drawing->xkb->names->keycodes);
2326 }
2327 
2328 G_CONST_RETURN gchar *
gkbd_keyboard_drawing_get_geometry(GkbdKeyboardDrawing * drawing)2329 gkbd_keyboard_drawing_get_geometry (GkbdKeyboardDrawing * drawing)
2330 {
2331 	if (!drawing->xkb || drawing->xkb->names->geometry <= 0)
2332 		return NULL;
2333 	else
2334 		return XGetAtomName (drawing->display,
2335 				     drawing->xkb->names->geometry);
2336 }
2337 
2338 G_CONST_RETURN gchar *
gkbd_keyboard_drawing_get_symbols(GkbdKeyboardDrawing * drawing)2339 gkbd_keyboard_drawing_get_symbols (GkbdKeyboardDrawing * drawing)
2340 {
2341 	if (!drawing->xkb || drawing->xkb->names->symbols <= 0)
2342 		return NULL;
2343 	else
2344 		return XGetAtomName (drawing->display,
2345 				     drawing->xkb->names->symbols);
2346 }
2347 
2348 G_CONST_RETURN gchar *
gkbd_keyboard_drawing_get_types(GkbdKeyboardDrawing * drawing)2349 gkbd_keyboard_drawing_get_types (GkbdKeyboardDrawing * drawing)
2350 {
2351 	if (!drawing->xkb || drawing->xkb->names->types <= 0)
2352 		return NULL;
2353 	else
2354 		return XGetAtomName (drawing->display,
2355 				     drawing->xkb->names->types);
2356 }
2357 
2358 G_CONST_RETURN gchar *
gkbd_keyboard_drawing_get_compat(GkbdKeyboardDrawing * drawing)2359 gkbd_keyboard_drawing_get_compat (GkbdKeyboardDrawing * drawing)
2360 {
2361 	if (!drawing->xkb || drawing->xkb->names->compat <= 0)
2362 		return NULL;
2363 	else
2364 		return XGetAtomName (drawing->display,
2365 				     drawing->xkb->names->compat);
2366 }
2367 
2368 void
gkbd_keyboard_drawing_set_track_modifiers(GkbdKeyboardDrawing * drawing,gboolean enable)2369 gkbd_keyboard_drawing_set_track_modifiers (GkbdKeyboardDrawing * drawing,
2370 					   gboolean enable)
2371 {
2372 	if (enable) {
2373 		XkbStateRec state;
2374 		drawing->track_modifiers = 1;
2375 		memset (&state, 0, sizeof (state));
2376 		XkbGetState (drawing->display, XkbUseCoreKbd, &state);
2377 		gkbd_keyboard_drawing_set_mods (drawing,
2378 						state.compat_state);
2379 	} else
2380 		drawing->track_modifiers = 0;
2381 }
2382 
2383 void
gkbd_keyboard_drawing_set_track_config(GkbdKeyboardDrawing * drawing,gboolean enable)2384 gkbd_keyboard_drawing_set_track_config (GkbdKeyboardDrawing * drawing,
2385 					gboolean enable)
2386 {
2387 	if (enable)
2388 		drawing->track_config = 1;
2389 	else
2390 		drawing->track_config = 0;
2391 }
2392 
2393 void
gkbd_keyboard_drawing_set_groups_levels(GkbdKeyboardDrawing * drawing,GkbdKeyboardDrawingGroupLevel * groupLevels[])2394 gkbd_keyboard_drawing_set_groups_levels (GkbdKeyboardDrawing * drawing,
2395 					 GkbdKeyboardDrawingGroupLevel *
2396 					 groupLevels[])
2397 {
2398 #ifdef KBDRAW_DEBUG
2399 	printf ("set_group_levels [topLeft]: %d %d \n",
2400 		groupLevels[GKBD_KEYBOARD_DRAWING_POS_TOPLEFT]->group,
2401 		groupLevels[GKBD_KEYBOARD_DRAWING_POS_TOPLEFT]->level);
2402 	printf ("set_group_levels [topRight]: %d %d \n",
2403 		groupLevels[GKBD_KEYBOARD_DRAWING_POS_TOPRIGHT]->group,
2404 		groupLevels[GKBD_KEYBOARD_DRAWING_POS_TOPRIGHT]->level);
2405 	printf ("set_group_levels [bottomLeft]: %d %d \n",
2406 		groupLevels[GKBD_KEYBOARD_DRAWING_POS_BOTTOMLEFT]->group,
2407 		groupLevels[GKBD_KEYBOARD_DRAWING_POS_BOTTOMLEFT]->level);
2408 	printf ("set_group_levels [bottomRight]: %d %d \n",
2409 		groupLevels[GKBD_KEYBOARD_DRAWING_POS_BOTTOMRIGHT]->group,
2410 		groupLevels[GKBD_KEYBOARD_DRAWING_POS_BOTTOMRIGHT]->level);
2411 #endif
2412 	drawing->groupLevels = groupLevels;
2413 
2414 	gtk_widget_queue_draw (GTK_WIDGET (drawing));
2415 }
2416 
2417 typedef struct {
2418 	GkbdKeyboardDrawing *drawing;
2419 	const gchar *description;
2420 } XkbLayoutPreviewPrintData;
2421 
2422 static void
gkbd_keyboard_drawing_begin_print(GtkPrintOperation * operation,GtkPrintContext * context,XkbLayoutPreviewPrintData * data)2423 gkbd_keyboard_drawing_begin_print (GtkPrintOperation * operation,
2424 				   GtkPrintContext * context,
2425 				   XkbLayoutPreviewPrintData * data)
2426 {
2427 	/* We always print single-page documents */
2428 	GtkPrintSettings *settings =
2429 	    gtk_print_operation_get_print_settings (operation);
2430 	gtk_print_operation_set_n_pages (operation, 1);
2431 	if (!gtk_print_settings_has_key
2432 	    (settings, GTK_PRINT_SETTINGS_ORIENTATION))
2433 		gtk_print_settings_set_orientation (settings,
2434 						    GTK_PAGE_ORIENTATION_LANDSCAPE);
2435 }
2436 
2437 static void
gkbd_keyboard_drawing_draw_page(GtkPrintOperation * operation,GtkPrintContext * context,gint page_nr,XkbLayoutPreviewPrintData * data)2438 gkbd_keyboard_drawing_draw_page (GtkPrintOperation * operation,
2439 				 GtkPrintContext * context,
2440 				 gint page_nr,
2441 				 XkbLayoutPreviewPrintData * data)
2442 {
2443 	cairo_t *cr = gtk_print_context_get_cairo_context (context);
2444 	PangoLayout *layout =
2445 	    gtk_print_context_create_pango_layout (context);
2446 	PangoFontDescription *desc =
2447 	    pango_font_description_from_string ("sans 8");
2448 	gdouble width = gtk_print_context_get_width (context);
2449 	gdouble height = gtk_print_context_get_height (context);
2450 	gdouble dpi_x = gtk_print_context_get_dpi_x (context);
2451 	gdouble dpi_y = gtk_print_context_get_dpi_y (context);
2452 	gchar *header;
2453 
2454 	gtk_print_operation_set_unit (operation, GTK_UNIT_PIXEL);
2455 
2456 	header = g_strdup_printf
2457 	    (_("Keyboard layout \"%s\"\n"
2458 	       "Copyright &#169; X.Org Foundation and "
2459 	       "XKeyboardConfig contributors\n"
2460 	       "For licensing see package metadata"), data->description);
2461 	pango_layout_set_markup (layout, header, -1);
2462 	pango_layout_set_font_description (layout, desc);
2463 	pango_font_description_free (desc);
2464 	pango_layout_set_width (layout, pango_units_from_double (width));
2465 	pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
2466 	cairo_set_source_rgb (cr, 0, 0, 0);
2467 	cairo_move_to (cr, 0, 0);
2468 	pango_cairo_show_layout (cr, layout);
2469 
2470 	gkbd_keyboard_drawing_render (GKBD_KEYBOARD_DRAWING
2471 				      (data->drawing), cr, layout, 0.0,
2472 				      0.0, width, height, dpi_x, dpi_y);
2473 
2474 	g_object_unref (layout);
2475 }
2476 
2477 void
gkbd_keyboard_drawing_print(GkbdKeyboardDrawing * drawing,GtkWindow * parent_window,const gchar * description)2478 gkbd_keyboard_drawing_print (GkbdKeyboardDrawing * drawing,
2479 			     GtkWindow * parent_window,
2480 			     const gchar * description)
2481 {
2482 	GtkPrintOperation *print;
2483 	GtkPrintOperationResult res;
2484 	static GtkPrintSettings *settings = NULL;
2485 	XkbLayoutPreviewPrintData data = { drawing, description };
2486 
2487 	print = gtk_print_operation_new ();
2488 
2489 	if (settings != NULL)
2490 		gtk_print_operation_set_print_settings (print, settings);
2491 
2492 	g_signal_connect (print, "begin_print",
2493 			  G_CALLBACK (gkbd_keyboard_drawing_begin_print),
2494 			  &data);
2495 	g_signal_connect (print, "draw_page",
2496 			  G_CALLBACK (gkbd_keyboard_drawing_draw_page),
2497 			  &data);
2498 
2499 	res = gtk_print_operation_run (print,
2500 				       GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
2501 				       parent_window, NULL);
2502 
2503 	if (res == GTK_PRINT_OPERATION_RESULT_APPLY) {
2504 		if (settings != NULL)
2505 			g_object_unref (settings);
2506 		settings = gtk_print_operation_get_print_settings (print);
2507 		g_object_ref (settings);
2508 	}
2509 
2510 	g_object_unref (print);
2511 }
2512 
2513 void
gkbd_keyboard_drawing_set_layout(GkbdKeyboardDrawing * drawing,const gchar * id)2514 gkbd_keyboard_drawing_set_layout (GkbdKeyboardDrawing * drawing,
2515 				  const gchar * id)
2516 {
2517 	XklConfigRec *data;
2518 	char **p, *layout, *variant;
2519 	XkbComponentNamesRec component_names;
2520 	XklEngine *engine;
2521 
2522 	if (drawing == NULL)
2523 		return;
2524 
2525 	if (id == NULL) {
2526 		gkbd_keyboard_drawing_set_keyboard (drawing, NULL);
2527 		return;
2528 	}
2529 
2530 	engine = xkl_engine_get_instance (GDK_DISPLAY_XDISPLAY
2531 					  (gdk_display_get_default ()));
2532 
2533 	data = xkl_config_rec_new ();
2534 	if (xkl_config_rec_get_from_server (data, engine)) {
2535 		if ((p = data->layouts) != NULL)
2536 			g_strfreev (data->layouts);
2537 
2538 		if ((p = data->variants) != NULL)
2539 			g_strfreev (data->variants);
2540 
2541 		data->layouts = g_new0 (char *, 2);
2542 		data->variants = g_new0 (char *, 2);
2543 		if (gkbd_keyboard_config_split_items
2544 		    (id, &layout, &variant)
2545 		    && variant != NULL) {
2546 			data->layouts[0] =
2547 			    (layout == NULL) ? NULL : g_strdup (layout);
2548 			data->variants[0] =
2549 			    (variant == NULL) ? NULL : g_strdup (variant);
2550 		} else {
2551 			data->layouts[0] =
2552 			    (id == NULL) ? NULL : g_strdup (id);
2553 			data->variants[0] = NULL;
2554 		}
2555 
2556 		if (xkl_xkb_config_native_prepare
2557 		    (engine, data, &component_names)) {
2558 			if (!gkbd_keyboard_drawing_set_keyboard
2559 			    (drawing, &component_names))
2560 				gkbd_keyboard_drawing_set_keyboard
2561 				    (drawing, NULL);
2562 
2563 			xkl_xkb_config_native_cleanup
2564 			    (engine, &component_names);
2565 		} else {
2566 			xkl_debug (0, "Could not find the keyboard\n");
2567 		}
2568 	}
2569 	g_object_unref (G_OBJECT (data));
2570 }
2571 
2572 static void
gkbd_keyboard_drawing_dialog_set_layout_name(GtkWidget * dialog,const gchar * layout_name)2573 gkbd_keyboard_drawing_dialog_set_layout_name (GtkWidget * dialog,
2574 					      const gchar * layout_name)
2575 {
2576 	gtk_window_set_title (GTK_WINDOW (dialog), layout_name);
2577 	g_object_set_data_full (G_OBJECT (dialog), "layout_name",
2578 				g_strdup (layout_name), g_free);
2579 }
2580 
2581 static void
gkbd_keyboard_drawing_dialog_response(GtkWidget * dialog,gint resp)2582 gkbd_keyboard_drawing_dialog_response (GtkWidget * dialog, gint resp)
2583 {
2584 	GdkRectangle rect;
2585 	GtkWidget *kbdraw;
2586 	const gchar *groupName;
2587 
2588 	switch (resp) {
2589 	case GTK_RESPONSE_CLOSE:
2590 		gtk_window_get_position (GTK_WINDOW (dialog), &rect.x,
2591 					 &rect.y);
2592 		gtk_window_get_size (GTK_WINDOW (dialog), &rect.width,
2593 				     &rect.height);
2594 		gkbd_preview_save_position (&rect);
2595 		gtk_widget_destroy (dialog);
2596 		break;
2597 	case GTK_RESPONSE_PRINT:
2598 		kbdraw =
2599 		    GTK_WIDGET (g_object_get_data
2600 				(G_OBJECT (dialog), "kbdraw"));
2601 		groupName =
2602 		    (const gchar *) g_object_get_data (G_OBJECT (dialog),
2603 						       "groupName");
2604 		gkbd_keyboard_drawing_print (GKBD_KEYBOARD_DRAWING
2605 					     (kbdraw), GTK_WINDOW (dialog),
2606 					     groupName ? groupName :
2607 					     _("Unknown"));
2608 	}
2609 }
2610 
2611 void
gkbd_keyboard_drawing_dialog_set_group(GtkWidget * dialog,XklConfigRegistry * registry,gint group)2612 gkbd_keyboard_drawing_dialog_set_group (GtkWidget * dialog,
2613 					XklConfigRegistry * registry,
2614 					gint group)
2615 {
2616 	XkbComponentNamesRec component_names;
2617 	XklConfigRec *xkl_data;
2618 	XklEngine *engine =
2619 	    xkl_engine_get_instance (GDK_DISPLAY_XDISPLAY
2620 				     (gdk_display_get_default ()));
2621 
2622 	xkl_data = xkl_config_rec_new ();
2623 	if (xkl_config_rec_get_from_server (xkl_data, engine)) {
2624 		int num_layouts = g_strv_length (xkl_data->layouts);
2625 		int num_variants = g_strv_length (xkl_data->variants);
2626 		if (group >= 0 && group < num_layouts
2627 		    && group < num_variants) {
2628 			XklConfigItem *xki = xkl_config_item_new ();
2629 			gchar *l = g_strdup (xkl_data->layouts[group]);
2630 			gchar *v = g_strdup (xkl_data->variants[group]);
2631 			const gchar *layout_name = NULL;
2632 			gchar **p;
2633 			int i;
2634 
2635 			if ((p = xkl_data->layouts) != NULL)
2636 				for (i = num_layouts; --i >= 0;)
2637 					g_free (*p++);
2638 
2639 			if ((p = xkl_data->variants) != NULL)
2640 				for (i = num_variants; --i >= 0;)
2641 					g_free (*p++);
2642 
2643 			xkl_data->layouts =
2644 			    g_realloc (xkl_data->layouts,
2645 				       sizeof (char *) * 2);
2646 			xkl_data->variants =
2647 			    g_realloc (xkl_data->variants,
2648 				       sizeof (char *) * 2);
2649 			xkl_data->layouts[0] = l;
2650 			xkl_data->variants[0] = v;
2651 			xkl_data->layouts[1] = xkl_data->variants[1] =
2652 			    NULL;
2653 
2654 			if (v[0] != 0) {
2655 				strncpy (xki->name, v,
2656 					 XKL_MAX_CI_NAME_LENGTH);
2657 				xki->name[XKL_MAX_CI_NAME_LENGTH - 1] = 0;
2658 				if (xkl_config_registry_find_variant
2659 				    (registry, l, xki))
2660 					layout_name = xki->description;
2661 			} else {
2662 				strncpy (xki->name, l,
2663 					 XKL_MAX_CI_NAME_LENGTH);
2664 				xki->name[XKL_MAX_CI_NAME_LENGTH - 1] = 0;
2665 				if (xkl_config_registry_find_layout
2666 				    (registry, xki))
2667 					layout_name = xki->description;
2668 			}
2669 			gkbd_keyboard_drawing_dialog_set_layout_name
2670 			    (dialog, layout_name);
2671 			g_object_unref (xki);
2672 		}
2673 
2674 		if (xkl_xkb_config_native_prepare
2675 		    (engine, xkl_data, &component_names)) {
2676 			GtkWidget *kbdraw =
2677 			    g_object_get_data (G_OBJECT (dialog),
2678 					       "kbdraw");
2679 			if (!gkbd_keyboard_drawing_set_keyboard
2680 			    (GKBD_KEYBOARD_DRAWING (kbdraw),
2681 			     &component_names))
2682 				gkbd_keyboard_drawing_set_keyboard
2683 				    (GKBD_KEYBOARD_DRAWING (kbdraw), NULL);
2684 			xkl_xkb_config_native_cleanup (engine,
2685 						       &component_names);
2686 		}
2687 	}
2688 
2689 	g_object_unref (G_OBJECT (xkl_data));
2690 }
2691 
2692 GtkWidget *
gkbd_keyboard_drawing_dialog_new()2693 gkbd_keyboard_drawing_dialog_new ()
2694 {
2695 	GtkBuilder *builder;
2696 	GtkWidget *dialog, *kbdraw;
2697 	GdkRectangle *rect;
2698 	GError *error = NULL;
2699 
2700 	builder = gtk_builder_new ();
2701 	gtk_builder_add_from_file (builder, UIDIR "/show-layout.ui",
2702 				   &error);
2703 
2704 	if (error) {
2705 		g_error ("building ui from %s failed: %s",
2706 			 UIDIR "/show-layout.ui", error->message);
2707 		g_clear_error (&error);
2708 	}
2709 
2710 	dialog =
2711 	    GTK_WIDGET (gtk_builder_get_object
2712 			(builder, "gswitchit_layout_view"));
2713 	kbdraw = gkbd_keyboard_drawing_new ();
2714 
2715 	gkbd_keyboard_drawing_set_groups_levels (GKBD_KEYBOARD_DRAWING
2716 						 (kbdraw), pGroupsLevels);
2717 
2718 	g_object_set_data (G_OBJECT (dialog), "builderData", builder);
2719 	g_signal_connect (G_OBJECT (dialog), "response",
2720 			  G_CALLBACK
2721 			  (gkbd_keyboard_drawing_dialog_response), NULL);
2722 
2723 	gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
2724 
2725 	gtk_box_pack_start (GTK_BOX
2726 			    (gtk_builder_get_object
2727 			     (builder, "preview_vbox")), kbdraw, TRUE,
2728 			    TRUE, 0);
2729 
2730 	g_object_set_data (G_OBJECT (dialog), "kbdraw", kbdraw);
2731 
2732 	g_signal_connect_swapped (dialog, "destroy",
2733 				  G_CALLBACK (g_object_unref),
2734 				  g_object_get_data (G_OBJECT (dialog),
2735 						     "builderData"));
2736 
2737 	rect = gkbd_preview_load_position ();
2738 	if (rect != NULL) {
2739 		gtk_window_move (GTK_WINDOW (dialog), rect->x, rect->y);
2740 		g_free (rect);
2741 	}
2742 
2743 	return dialog;
2744 }
2745 
2746 void
gkbd_keyboard_drawing_dialog_set_layout(GtkWidget * dialog,XklConfigRegistry * registry,const gchar * full_layout)2747 gkbd_keyboard_drawing_dialog_set_layout (GtkWidget * dialog,
2748 					 XklConfigRegistry * registry,
2749 					 const gchar * full_layout)
2750 {
2751 	const gchar *layout_name = "?";
2752 	XklConfigItem *xki = xkl_config_item_new ();
2753 	gchar *layout = NULL, *variant = NULL;
2754 
2755 	GkbdKeyboardDrawing *kbdraw =
2756 	    GKBD_KEYBOARD_DRAWING (g_object_get_data
2757 				   (G_OBJECT (dialog), "kbdraw"));
2758 
2759 	if (full_layout == NULL || full_layout[0] == 0)
2760 		return;
2761 
2762 	gkbd_keyboard_drawing_set_layout (kbdraw, full_layout);
2763 
2764 	if (gkbd_keyboard_config_split_items
2765 	    (full_layout, &layout, &variant)) {
2766 		if (variant != NULL) {
2767 			strncpy (xki->name, variant,
2768 				 XKL_MAX_CI_NAME_LENGTH);
2769 			xki->name[XKL_MAX_CI_NAME_LENGTH - 1] = 0;
2770 			if (xkl_config_registry_find_variant
2771 			    (registry, layout, xki))
2772 				layout_name = xki->description;
2773 		} else {
2774 			strncpy (xki->name, layout,
2775 				 XKL_MAX_CI_NAME_LENGTH);
2776 			xki->name[XKL_MAX_CI_NAME_LENGTH - 1] = 0;
2777 			if (xkl_config_registry_find_layout
2778 			    (registry, xki))
2779 				layout_name = xki->description;
2780 		}
2781 	}
2782 
2783 	gkbd_keyboard_drawing_dialog_set_layout_name (dialog, layout_name);
2784 	g_object_unref (xki);
2785 }
2786