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 : "<";
657 txt = strcmp ("&", txt) ? txt : "&";
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 © 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