/* * Copyright (C) 2006 Sergey V. Udaltsov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define noKBDRAW_DEBUG #define INVALID_KEYCODE ((guint)(-1)) #define GTK_RESPONSE_PRINT 2 #define KEY_FONT_SIZE 12 enum { BAD_KEYCODE = 0, NUM_SIGNALS }; static guint matekbd_keyboard_drawing_signals[NUM_SIGNALS] = { 0 }; static void matekbd_keyboard_drawing_set_mods (MatekbdKeyboardDrawing * drawing, guint mods); extern gboolean xkl_xkb_config_native_prepare (XklEngine * engine, const XklConfigRec * data, gpointer component_names); extern void xkl_xkb_config_native_cleanup (XklEngine * engine, gpointer component_names); static gint xkb_to_pixmap_coord (MatekbdKeyboardDrawingRenderContext * context, gint n) { return n * context->scale_numerator / context->scale_denominator; } static gdouble xkb_to_pixmap_double (MatekbdKeyboardDrawingRenderContext * context, gdouble d) { return d * context->scale_numerator / context->scale_denominator; } /* angle is in tenths of a degree; coordinates can be anything as (xkb, * pixels, pango) as long as they are all the same */ static void rotate_coordinate (gint origin_x, gint origin_y, gint x, gint y, gint angle, gint * rotated_x, gint * rotated_y) { *rotated_x = origin_x + (x - origin_x) * cos (M_PI * angle / 1800.0) - (y - origin_y) * sin (M_PI * angle / 1800.0); *rotated_y = origin_y + (x - origin_x) * sin (M_PI * angle / 1800.0) + (y - origin_y) * cos (M_PI * angle / 1800.0); } static gdouble length (gdouble x, gdouble y) { return sqrt (x * x + y * y); } static gdouble point_line_distance (gdouble ax, gdouble ay, gdouble nx, gdouble ny) { return ax * nx + ay * ny; } static void normal_form (gdouble ax, gdouble ay, gdouble bx, gdouble by, gdouble * nx, gdouble * ny, gdouble * d) { gdouble l; *nx = by - ay; *ny = ax - bx; l = length (*nx, *ny); *nx /= l; *ny /= l; *d = point_line_distance (ax, ay, *nx, *ny); } static void inverse (gdouble a, gdouble b, gdouble c, gdouble d, gdouble * e, gdouble * f, gdouble * g, gdouble * h) { gdouble det; det = a * d - b * c; *e = d / det; *f = -b / det; *g = -c / det; *h = a / det; } static void multiply (gdouble a, gdouble b, gdouble c, gdouble d, gdouble e, gdouble f, gdouble * x, gdouble * y) { *x = a * e + b * f; *y = c * e + d * f; } static void intersect (gdouble n1x, gdouble n1y, gdouble d1, gdouble n2x, gdouble n2y, gdouble d2, gdouble * x, gdouble * y) { gdouble e, f, g, h; inverse (n1x, n1y, n2x, n2y, &e, &f, &g, &h); multiply (e, f, g, h, d1, d2, x, y); } /* draw an angle from the current point to b and then to c, * with a rounded corner of the given radius. */ static void rounded_corner (cairo_t * cr, gdouble bx, gdouble by, gdouble cx, gdouble cy, gdouble radius) { gdouble ax, ay; gdouble n1x, n1y, d1; gdouble n2x, n2y, d2; gdouble pd1, pd2; gdouble ix, iy; gdouble dist1, dist2; gdouble nx, ny, d; gdouble a1x, a1y, c1x, c1y; gdouble phi1, phi2; cairo_get_current_point (cr, &ax, &ay); #ifdef KBDRAW_DEBUG printf (" current point: (%f, %f), radius %f:\n", ax, ay, radius); #endif /* make sure radius is not too large */ dist1 = length (bx - ax, by - ay); dist2 = length (cx - bx, cy - by); radius = MIN (radius, MIN (dist1, dist2)); /* construct normal forms of the lines */ normal_form (ax, ay, bx, by, &n1x, &n1y, &d1); normal_form (bx, by, cx, cy, &n2x, &n2y, &d2); /* find which side of the line a,b the point c is on */ if (point_line_distance (cx, cy, n1x, n1y) < d1) pd1 = d1 - radius; else pd1 = d1 + radius; /* find which side of the line b,c the point a is on */ if (point_line_distance (ax, ay, n2x, n2y) < d2) pd2 = d2 - radius; else pd2 = d2 + radius; /* intersect the parallels to find the center of the arc */ intersect (n1x, n1y, pd1, n2x, n2y, pd2, &ix, &iy); nx = (bx - ax) / dist1; ny = (by - ay) / dist1; d = point_line_distance (ix, iy, nx, ny); /* a1 is the point on the line a-b where the arc starts */ intersect (n1x, n1y, d1, nx, ny, d, &a1x, &a1y); nx = (cx - bx) / dist2; ny = (cy - by) / dist2; d = point_line_distance (ix, iy, nx, ny); /* c1 is the point on the line b-c where the arc ends */ intersect (n2x, n2y, d2, nx, ny, d, &c1x, &c1y); /* determine the first angle */ if (a1x - ix == 0) phi1 = (a1y - iy > 0) ? M_PI_2 : 3 * M_PI_2; else if (a1x - ix > 0) phi1 = atan ((a1y - iy) / (a1x - ix)); else phi1 = M_PI + atan ((a1y - iy) / (a1x - ix)); /* determine the second angle */ if (c1x - ix == 0) phi2 = (c1y - iy > 0) ? M_PI_2 : 3 * M_PI_2; else if (c1x - ix > 0) phi2 = atan ((c1y - iy) / (c1x - ix)); else phi2 = M_PI + atan ((c1y - iy) / (c1x - ix)); /* compute the difference between phi2 and phi1 mod 2pi */ d = phi2 - phi1; while (d < 0) d += 2 * M_PI; while (d > 2 * M_PI) d -= 2 * M_PI; #ifdef KBDRAW_DEBUG printf (" line 1 to: (%f, %f):\n", a1x, a1y); #endif if (!(isnan (a1x) || isnan (a1y))) cairo_line_to (cr, a1x, a1y); /* pick the short arc from phi1 to phi2 */ if (d < M_PI) cairo_arc (cr, ix, iy, radius, phi1, phi2); else cairo_arc_negative (cr, ix, iy, radius, phi1, phi2); #ifdef KBDRAW_DEBUG printf (" line 2 to: (%f, %f):\n", cx, cy); #endif cairo_line_to (cr, cx, cy); } static void rounded_polygon (cairo_t * cr, gboolean filled, gdouble radius, GdkPoint * points, gint num_points) { gint i, j; cairo_move_to (cr, (gdouble) (points[num_points - 1].x + points[0].x) / 2, (gdouble) (points[num_points - 1].y + points[0].y) / 2); #ifdef KBDRAW_DEBUG printf (" rounded polygon of radius %f:\n", radius); #endif for (i = 0; i < num_points; i++) { j = (i + 1) % num_points; rounded_corner (cr, (gdouble) points[i].x, (gdouble) points[i].y, (gdouble) (points[i].x + points[j].x) / 2, (gdouble) (points[i].y + points[j].y) / 2, radius); #ifdef KBDRAW_DEBUG printf (" corner (%d, %d) -> (%d, %d):\n", points[i].x, points[i].y, points[j].x, points[j].y); #endif }; cairo_close_path (cr); if (filled) cairo_fill (cr); else cairo_stroke (cr); } static void draw_polygon (MatekbdKeyboardDrawingRenderContext * context, GdkRGBA * fill_color, gint xkb_x, gint xkb_y, XkbPointRec * xkb_points, guint num_points, gdouble radius) { GdkPoint *points; gboolean filled; gint i; if (fill_color) { filled = TRUE; } else { fill_color = &context->dark_color; filled = FALSE; } gdk_cairo_set_source_rgba (context->cr, fill_color); points = g_new (GdkPoint, num_points); #ifdef KBDRAW_DEBUG printf (" Polygon points:\n"); #endif for (i = 0; i < num_points; i++) { points[i].x = xkb_to_pixmap_coord (context, xkb_x + xkb_points[i].x); points[i].y = xkb_to_pixmap_coord (context, xkb_y + xkb_points[i].y); #ifdef KBDRAW_DEBUG printf (" %d, %d\n", points[i].x, points[i].y); #endif } rounded_polygon (context->cr, filled, xkb_to_pixmap_double (context, radius), points, num_points); g_free (points); } static void curve_rectangle (cairo_t * cr, gdouble x0, gdouble y0, gdouble width, gdouble height, gdouble radius) { gdouble x1, y1; if (!width || !height) return; x1 = x0 + width; y1 = y0 + height; radius = MIN (radius, MIN (width / 2, height / 2)); cairo_move_to (cr, x0, y0 + radius); cairo_arc (cr, x0 + radius, y0 + radius, radius, M_PI, 3 * M_PI / 2); cairo_line_to (cr, x1 - radius, y0); cairo_arc (cr, x1 - radius, y0 + radius, radius, 3 * M_PI / 2, 2 * M_PI); cairo_line_to (cr, x1, y1 - radius); cairo_arc (cr, x1 - radius, y1 - radius, radius, 0, M_PI / 2); cairo_line_to (cr, x0 + radius, y1); cairo_arc (cr, x0 + radius, y1 - radius, radius, M_PI / 2, M_PI); cairo_close_path (cr); } static void draw_curve_rectangle (cairo_t * cr, gboolean filled, GdkRGBA * fill_color, gint x, gint y, gint width, gint height, gint radius) { curve_rectangle (cr, x, y, width, height, radius); gdk_cairo_set_source_rgba (cr, fill_color); if (filled) cairo_fill (cr); else cairo_stroke (cr); } /* x, y, width, height are in the xkb coordinate system */ static void draw_rectangle (MatekbdKeyboardDrawingRenderContext * context, GdkRGBA * fill_color, gint angle, gint xkb_x, gint xkb_y, gint xkb_width, gint xkb_height, gint radius) { if (angle == 0) { gint x, y, width, height; gboolean filled; if (fill_color) { filled = TRUE; } else { fill_color = &context->dark_color; filled = FALSE; } x = xkb_to_pixmap_coord (context, xkb_x); y = xkb_to_pixmap_coord (context, xkb_y); width = xkb_to_pixmap_coord (context, xkb_x + xkb_width) - x; height = xkb_to_pixmap_coord (context, xkb_y + xkb_height) - y; draw_curve_rectangle (context->cr, filled, fill_color, x, y, width, height, xkb_to_pixmap_double (context, radius)); } else { XkbPointRec points[4]; gint x, y; points[0].x = xkb_x; points[0].y = xkb_y; rotate_coordinate (xkb_x, xkb_y, xkb_x + xkb_width, xkb_y, angle, &x, &y); points[1].x = x; points[1].y = y; rotate_coordinate (xkb_x, xkb_y, xkb_x + xkb_width, xkb_y + xkb_height, angle, &x, &y); points[2].x = x; points[2].y = y; rotate_coordinate (xkb_x, xkb_y, xkb_x, xkb_y + xkb_height, angle, &x, &y); points[3].x = x; points[3].y = y; /* the points we've calculated are relative to 0,0 */ draw_polygon (context, fill_color, 0, 0, points, 4, radius); } } static void draw_outline (MatekbdKeyboardDrawingRenderContext * context, XkbOutlineRec * outline, GdkRGBA * color, gint angle, gint origin_x, gint origin_y) { #ifdef KBDRAW_DEBUG printf (" num_points in %p: %d\n", outline, outline->num_points); #endif if (outline->num_points == 1) { if (color) draw_rectangle (context, color, angle, origin_x, origin_y, outline->points[0].x, outline->points[0].y, outline->corner_radius); #ifdef KBDRAW_DEBUG printf ("pointsxy:%d %d %d\n", outline->points[0].x, outline->points[0].y, outline->corner_radius); #endif draw_rectangle (context, NULL, angle, origin_x, origin_y, outline->points[0].x, outline->points[0].y, outline->corner_radius); } else if (outline->num_points == 2) { gint rotated_x0, rotated_y0; rotate_coordinate (origin_x, origin_y, origin_x + outline->points[0].x, origin_y + outline->points[0].y, angle, &rotated_x0, &rotated_y0); if (color) draw_rectangle (context, color, angle, rotated_x0, rotated_y0, outline->points[1].x, outline->points[1].y, outline->corner_radius); draw_rectangle (context, NULL, angle, rotated_x0, rotated_y0, outline->points[1].x, outline->points[1].y, outline->corner_radius); } else { if (color) draw_polygon (context, color, origin_x, origin_y, outline->points, outline->num_points, outline->corner_radius); draw_polygon (context, NULL, origin_x, origin_y, outline->points, outline->num_points, outline->corner_radius); } } /* see PSColorDef in xkbprint */ static gboolean parse_xkb_color_spec (gchar * colorspec, GdkRGBA * color) { glong level; color->alpha = 1.0; if (g_ascii_strcasecmp (colorspec, "black") == 0) { color->red = 0; color->green = 0; color->blue = 0; } else if (g_ascii_strcasecmp (colorspec, "white") == 0) { color->red = 1.0; color->green = 1.0; color->blue = 1.0; } else if (g_ascii_strncasecmp (colorspec, "grey", 4) == 0 || g_ascii_strncasecmp (colorspec, "gray", 4) == 0) { level = strtol (colorspec + 4, NULL, 10); color->red = 1.0 - 1.0 * level / 100.0; color->green = 1.0 - 1.0 * level / 100.0; color->blue = 1.0 - 1.0 * level / 100.0; } else if (g_ascii_strcasecmp (colorspec, "red") == 0) { color->red = 1.0; color->green = 0; color->blue = 0; } else if (g_ascii_strcasecmp (colorspec, "green") == 0) { color->red = 0; color->green = 1.0; color->blue = 0; } else if (g_ascii_strcasecmp (colorspec, "blue") == 0) { color->red = 0; color->green = 0; color->blue = 1.0; } else if (g_ascii_strncasecmp (colorspec, "red", 3) == 0) { level = strtol (colorspec + 3, NULL, 10); color->red = 1.0 * level / 100.0; color->green = 0; color->blue = 0; } else if (g_ascii_strncasecmp (colorspec, "green", 5) == 0) { level = strtol (colorspec + 5, NULL, 10); color->red = 0; color->green = 1.0 * level / 100.0; color->blue = 0; } else if (g_ascii_strncasecmp (colorspec, "blue", 4) == 0) { level = strtol (colorspec + 4, NULL, 10); color->red = 0; color->green = 0; color->blue = 1.0 * level / 100.0; } else return FALSE; return TRUE; } static guint find_keycode (MatekbdKeyboardDrawing * drawing, gchar * key_name) { #define KEYSYM_NAME_MAX_LENGTH 4 guint keycode; gint i, j; XkbKeyNamePtr pkey; XkbKeyAliasPtr palias; guint is_name_matched; gchar *src, *dst; if (!drawing->xkb) return INVALID_KEYCODE; #ifdef KBDRAW_DEBUG printf (" looking for keycode for (%c%c%c%c)\n", key_name[0], key_name[1], key_name[2], key_name[3]); #endif pkey = drawing->xkb->names->keys + drawing->xkb->min_key_code; for (keycode = drawing->xkb->min_key_code; keycode <= drawing->xkb->max_key_code; keycode++) { is_name_matched = 1; src = key_name; dst = pkey->name; for (i = KEYSYM_NAME_MAX_LENGTH; --i >= 0;) { if ('\0' == *src) break; if (*src++ != *dst++) { is_name_matched = 0; break; } } if (is_name_matched) { #ifdef KBDRAW_DEBUG printf (" found keycode %u\n", keycode); #endif return keycode; } pkey++; } palias = drawing->xkb->names->key_aliases; for (j = drawing->xkb->names->num_key_aliases; --j >= 0;) { is_name_matched = 1; src = key_name; dst = palias->alias; for (i = KEYSYM_NAME_MAX_LENGTH; --i >= 0;) { if ('\0' == *src) break; if (*src++ != *dst++) { is_name_matched = 0; break; } } if (is_name_matched) { keycode = find_keycode (drawing, palias->real); #ifdef KBDRAW_DEBUG printf ("found alias keycode %u\n", keycode); #endif return keycode; } palias++; } return INVALID_KEYCODE; } static void set_markup (MatekbdKeyboardDrawingRenderContext * context, gchar *txt) { PangoLayout *layout = context->layout; txt = strcmp ("<", txt) ? txt : "<"; txt = strcmp ("&", txt) ? txt : "&"; if (g_utf8_strlen (txt, -1) > 1) { gchar* buf = g_strdup_printf ("%s", txt); pango_layout_set_markup (layout, buf, -1); g_free (buf); } else { pango_layout_set_markup (layout, txt, -1); } } static void set_key_label_in_layout (MatekbdKeyboardDrawingRenderContext * context, guint keyval) { gchar buf[5]; gunichar uc; switch (keyval) { case GDK_KEY_Scroll_Lock: set_markup (context, "Scroll\nLock"); break; case GDK_KEY_space: set_markup (context, ""); break; case GDK_KEY_Sys_Req: set_markup (context, "Sys Rq"); break; case GDK_KEY_Page_Up: set_markup (context, "Page\nUp"); break; case GDK_KEY_Page_Down: set_markup (context, "Page\nDown"); break; case GDK_KEY_Num_Lock: set_markup (context, "Num\nLock"); break; case GDK_KEY_KP_Page_Up: set_markup (context, "Pg Up"); break; case GDK_KEY_KP_Page_Down: set_markup (context, "Pg Dn"); break; case GDK_KEY_KP_Home: set_markup (context, "Home"); break; case GDK_KEY_KP_Left: set_markup (context, "Left"); break; case GDK_KEY_KP_End: set_markup (context, "End"); break; case GDK_KEY_KP_Up: set_markup (context, "Up"); break; case GDK_KEY_KP_Begin: set_markup (context, "Begin"); break; case GDK_KEY_KP_Right: set_markup (context, "Right"); break; case GDK_KEY_KP_Enter: set_markup (context, "Enter"); break; case GDK_KEY_KP_Down: set_markup (context, "Down"); break; case GDK_KEY_KP_Insert: set_markup (context, "Ins"); break; case GDK_KEY_KP_Delete: set_markup (context, "Del"); break; case GDK_KEY_dead_grave: set_markup (context, "ˋ"); break; case GDK_KEY_dead_acute: set_markup (context, "ˊ"); break; case GDK_KEY_dead_circumflex: set_markup (context, "ˆ"); break; case GDK_KEY_dead_tilde: set_markup (context, "~"); break; case GDK_KEY_dead_macron: set_markup (context, "ˉ"); break; case GDK_KEY_dead_breve: set_markup (context, "˘"); break; case GDK_KEY_dead_abovedot: set_markup (context, "˙"); break; case GDK_KEY_dead_diaeresis: set_markup (context, "¨"); break; case GDK_KEY_dead_abovering: set_markup (context, "˚"); break; case GDK_KEY_dead_doubleacute: set_markup (context, "˝"); break; case GDK_KEY_dead_caron: set_markup (context, "ˇ"); break; case GDK_KEY_dead_cedilla: set_markup (context, "¸"); break; case GDK_KEY_dead_ogonek: set_markup (context, "˛"); break; /* case GDK_KEY_dead_iota: * case GDK_KEY_dead_voiced_sound: * case GDK_KEY_dead_semivoiced_sound: */ case GDK_KEY_dead_belowdot: set_markup (context, " ̣"); break; case GDK_KEY_horizconnector: set_markup (context, "horiz\nconn"); break; case GDK_KEY_Mode_switch: set_markup (context, "AltGr"); break; case GDK_KEY_Multi_key: set_markup (context, "Compose"); break; default: uc = gdk_keyval_to_unicode (keyval); if (uc != 0 && g_unichar_isgraph (uc)) { buf[g_unichar_to_utf8 (uc, buf)] = '\0'; set_markup (context, buf); } else { gchar *name = gdk_keyval_name (keyval); if (name) { set_markup (context, name); } else set_markup (context, ""); } } } static void draw_pango_layout (MatekbdKeyboardDrawingRenderContext * context, MatekbdKeyboardDrawing * drawing, gint angle, gint x, gint y) { PangoLayout *layout = context->layout; GdkRGBA *color; PangoLayoutLine *line; gint x_off, y_off; gint i; color = drawing->colors + (drawing->xkb->geom->label_color - drawing->xkb->geom->colors); if (angle != context->angle) { PangoMatrix matrix = PANGO_MATRIX_INIT; pango_matrix_rotate (&matrix, -angle / 10.0); pango_context_set_matrix (pango_layout_get_context (layout), &matrix); pango_layout_context_changed (layout); context->angle = angle; } i = 0; y_off = 0; for (line = pango_layout_get_line (layout, i); line != NULL; line = pango_layout_get_line (layout, ++i)) { GSList *runp; PangoRectangle line_extents; x_off = 0; for (runp = line->runs; runp != NULL; runp = runp->next) { PangoGlyphItem *run = runp->data; gint j; for (j = 0; j < run->glyphs->num_glyphs; j++) { PangoGlyphGeometry *geometry; geometry = &run->glyphs->glyphs[j].geometry; x_off += geometry->width; } } pango_layout_line_get_extents (line, NULL, &line_extents); y_off += line_extents.height + pango_layout_get_spacing (layout); } cairo_move_to (context->cr, x, y); gdk_cairo_set_source_rgba (context->cr, color); pango_cairo_show_layout (context->cr, layout); } static void draw_key_label_helper (MatekbdKeyboardDrawingRenderContext * context, MatekbdKeyboardDrawing * drawing, KeySym keysym, gint angle, MatekbdKeyboardDrawingGroupLevelPosition glp, gint x, gint y, gint width, gint height, gint padding) { gint label_x, label_y, label_max_width, ycell; if (keysym == 0) return; #ifdef KBDRAW_DEBUG printf ("keysym: %04X(%c) at glp: %d\n", (unsigned) keysym, (char) keysym, (int) glp); #endif switch (glp) { case MATEKBD_KEYBOARD_DRAWING_POS_TOPLEFT: case MATEKBD_KEYBOARD_DRAWING_POS_BOTTOMLEFT: { ycell = glp == MATEKBD_KEYBOARD_DRAWING_POS_BOTTOMLEFT; rotate_coordinate (x, y, x + padding, y + padding + (height - 2 * padding) * ycell * 4 / 7, angle, &label_x, &label_y); label_max_width = PANGO_SCALE * (width - 2 * padding); break; } case MATEKBD_KEYBOARD_DRAWING_POS_TOPRIGHT: case MATEKBD_KEYBOARD_DRAWING_POS_BOTTOMRIGHT: { ycell = glp == MATEKBD_KEYBOARD_DRAWING_POS_BOTTOMRIGHT; rotate_coordinate (x, y, x + padding + (width - 2 * padding) * 4 / 7, y + padding + (height - 2 * padding) * ycell * 4 / 7, angle, &label_x, &label_y); label_max_width = PANGO_SCALE * ((width - 2 * padding) - (width - 2 * padding) * 4 / 7); break; } default: return; } set_key_label_in_layout (context, keysym); pango_layout_set_width (context->layout, label_max_width); label_y -= (pango_layout_get_line_count (context->layout) - 1) * (pango_font_description_get_size (context->font_desc) / PANGO_SCALE); cairo_save (context->cr); cairo_rectangle (context->cr, x + padding / 2, y + padding / 2, width - padding, height - padding); cairo_clip (context->cr); draw_pango_layout (context, drawing, angle, label_x, label_y); cairo_restore (context->cr); } static void draw_key_label (MatekbdKeyboardDrawingRenderContext * context, MatekbdKeyboardDrawing * drawing, guint keycode, gint angle, gint xkb_origin_x, gint xkb_origin_y, gint xkb_width, gint xkb_height) { gint x, y, width, height; gint padding; gint g, l, glp; if (!drawing->xkb) return; padding = 23 * context->scale_numerator / context->scale_denominator; /* 2.3mm */ x = xkb_to_pixmap_coord (context, xkb_origin_x); y = xkb_to_pixmap_coord (context, xkb_origin_y); width = xkb_to_pixmap_coord (context, xkb_origin_x + xkb_width) - x; height = xkb_to_pixmap_coord (context, xkb_origin_y + xkb_height) - y; for (glp = MATEKBD_KEYBOARD_DRAWING_POS_TOPLEFT; glp < MATEKBD_KEYBOARD_DRAWING_POS_TOTAL; glp++) { if (drawing->groupLevels[glp] == NULL) continue; g = drawing->groupLevels[glp]->group; l = drawing->groupLevels[glp]->level; if (g < 0 || g >= XkbKeyNumGroups (drawing->xkb, keycode)) continue; if (l < 0 || l >= XkbKeyGroupWidth (drawing->xkb, keycode, g)) continue; /* Skip "exotic" levels like the "Ctrl" level in PC_SYSREQ */ if (l > 0) { guint mods = XkbKeyKeyType (drawing->xkb, keycode, g)->mods.mask; if ((mods & (ShiftMask | drawing->l3mod)) == 0) continue; } if (drawing->track_modifiers) { guint mods_rtrn; KeySym keysym; if (XkbTranslateKeyCode (drawing->xkb, keycode, XkbBuildCoreState (drawing->mods, g), &mods_rtrn, &keysym)) { draw_key_label_helper (context, drawing, keysym, angle, glp, x, y, width, height, padding); /* reverse y order */ } } else { KeySym keysym; keysym = XkbKeySymEntry (drawing->xkb, keycode, l, g); draw_key_label_helper (context, drawing, keysym, angle, glp, x, y, width, height, padding); /* reverse y order */ } } } /* * The x offset is calculated for complex shapes. It is the rightmost of the vertical lines in the outline */ static gint calc_origin_offset_x (XkbOutlineRec * outline) { gint rv = 0; gint i; XkbPointPtr point = outline->points; if (outline->num_points < 3) return 0; for (i = outline->num_points; --i > 0;) { gint x1 = point->x; gint y1 = point++->y; gint x2 = point->x; gint y2 = point->y; /*vertical, bottom to top (clock-wise), on the left */ if ((x1 == x2) && (y1 > y2) && (x1 > rv)) { rv = x1; } } return rv; } /* groups are from 0-3 */ static void draw_key (MatekbdKeyboardDrawingRenderContext * context, MatekbdKeyboardDrawing * drawing, MatekbdKeyboardDrawingKey * key) { XkbShapeRec *shape; GtkStyleContext *style_context; GdkRGBA color; XkbOutlineRec *outline; int origin_offset_x; /* gint i; */ if (!drawing->xkb) return; #ifdef KBDRAW_DEBUG printf ("shape: %p (base %p, index %d)\n", drawing->xkb->geom->shapes + key->xkbkey->shape_ndx, drawing->xkb->geom->shapes, key->xkbkey->shape_ndx); #endif shape = drawing->xkb->geom->shapes + key->xkbkey->shape_ndx; if (key->pressed) { style_context = gtk_widget_get_style_context (GTK_WIDGET (drawing)); gtk_style_context_save (style_context); gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_VIEW); gtk_style_context_get_background_color (style_context, GTK_STATE_FLAG_SELECTED, &color); gtk_style_context_restore (style_context); } else color = *(drawing->colors + key->xkbkey->color_ndx); #ifdef KBDRAW_DEBUG printf (" outlines base in the shape: %p (total: %d), origin: (%d, %d), angle %d\n", shape->outlines, shape->num_outlines, key->origin_x, key->origin_y, key->angle); #endif /* draw the primary outline */ outline = shape->primary ? shape->primary : shape->outlines; draw_outline (context, outline, &color, key->angle, key->origin_x, key->origin_y); #if 0 /* don't draw other outlines for now, since * the text placement does not take them into account */ for (i = 0; i < shape->num_outlines; i++) { if (shape->outlines + i == shape->approx || shape->outlines + i == shape->primary) continue; draw_outline (context, shape->outlines + i, NULL, key->angle, key->origin_x, key->origin_y); } #endif origin_offset_x = calc_origin_offset_x (outline); draw_key_label (context, drawing, key->keycode, key->angle, key->origin_x + origin_offset_x, key->origin_y, shape->bounds.x2, shape->bounds.y2); } static void invalidate_region (MatekbdKeyboardDrawing * drawing, gdouble angle, gint origin_x, gint origin_y, XkbShapeRec * shape) { GdkPoint points[4]; gint x_min, x_max, y_min, y_max; gint x, y, width, height; gint xx, yy; rotate_coordinate (0, 0, 0, 0, angle, &xx, &yy); points[0].x = xx; points[0].y = yy; rotate_coordinate (0, 0, shape->bounds.x2, 0, angle, &xx, &yy); points[1].x = xx; points[1].y = yy; rotate_coordinate (0, 0, shape->bounds.x2, shape->bounds.y2, angle, &xx, &yy); points[2].x = xx; points[2].y = yy; rotate_coordinate (0, 0, 0, shape->bounds.y2, angle, &xx, &yy); points[3].x = xx; points[3].y = yy; x_min = MIN (MIN (points[0].x, points[1].x), MIN (points[2].x, points[3].x)); x_max = MAX (MAX (points[0].x, points[1].x), MAX (points[2].x, points[3].x)); y_min = MIN (MIN (points[0].y, points[1].y), MIN (points[2].y, points[3].y)); y_max = MAX (MAX (points[0].y, points[1].y), MAX (points[2].y, points[3].y)); x = xkb_to_pixmap_coord (drawing->renderContext, origin_x + x_min) - 6; y = xkb_to_pixmap_coord (drawing->renderContext, origin_y + y_min) - 6; width = xkb_to_pixmap_coord (drawing->renderContext, x_max - x_min) + 12; height = xkb_to_pixmap_coord (drawing->renderContext, y_max - y_min) + 12; gtk_widget_queue_draw_area (GTK_WIDGET (drawing), x, y, width, height); } static void invalidate_indicator_doodad_region (MatekbdKeyboardDrawing * drawing, MatekbdKeyboardDrawingDoodad * doodad) { if (!drawing->xkb) return; invalidate_region (drawing, doodad->angle, doodad->origin_x + doodad->doodad->indicator.left, doodad->origin_y + doodad->doodad->indicator.top, &drawing->xkb->geom->shapes[doodad-> doodad->indicator.shape_ndx]); } static void invalidate_key_region (MatekbdKeyboardDrawing * drawing, MatekbdKeyboardDrawingKey * key) { if (!drawing->xkb) return; invalidate_region (drawing, key->angle, key->origin_x, key->origin_y, &drawing->xkb->geom->shapes[key-> xkbkey->shape_ndx]); } static void draw_text_doodad (MatekbdKeyboardDrawingRenderContext * context, MatekbdKeyboardDrawing * drawing, MatekbdKeyboardDrawingDoodad * doodad, XkbTextDoodadRec * text_doodad) { gint x, y; if (!drawing->xkb) return; x = xkb_to_pixmap_coord (context, doodad->origin_x + text_doodad->left); y = xkb_to_pixmap_coord (context, doodad->origin_y + text_doodad->top); set_markup (context, text_doodad->text); draw_pango_layout (context, drawing, doodad->angle, x, y); } static void draw_indicator_doodad (MatekbdKeyboardDrawingRenderContext * context, MatekbdKeyboardDrawing * drawing, MatekbdKeyboardDrawingDoodad * doodad, XkbIndicatorDoodadRec * indicator_doodad) { GdkRGBA *color; XkbShapeRec *shape; gint i; if (!drawing->xkb) return; shape = drawing->xkb->geom->shapes + indicator_doodad->shape_ndx; color = drawing->colors + (doodad->on ? indicator_doodad->on_color_ndx : indicator_doodad->off_color_ndx); for (i = 0; i < 1; i++) draw_outline (context, shape->outlines + i, color, doodad->angle, doodad->origin_x + indicator_doodad->left, doodad->origin_y + indicator_doodad->top); } static void draw_shape_doodad (MatekbdKeyboardDrawingRenderContext * context, MatekbdKeyboardDrawing * drawing, MatekbdKeyboardDrawingDoodad * doodad, XkbShapeDoodadRec * shape_doodad) { XkbShapeRec *shape; GdkRGBA *color; gint i; if (!drawing->xkb) return; shape = drawing->xkb->geom->shapes + shape_doodad->shape_ndx; color = drawing->colors + shape_doodad->color_ndx; /* draw the primary outline filled */ draw_outline (context, shape->primary ? shape->primary : shape->outlines, color, doodad->angle, doodad->origin_x + shape_doodad->left, doodad->origin_y + shape_doodad->top); /* stroke the other outlines */ for (i = 0; i < shape->num_outlines; i++) { if (shape->outlines + i == shape->approx || shape->outlines + i == shape->primary) continue; draw_outline (context, shape->outlines + i, NULL, doodad->angle, doodad->origin_x + shape_doodad->left, doodad->origin_y + shape_doodad->top); } } static void draw_doodad (MatekbdKeyboardDrawingRenderContext * context, MatekbdKeyboardDrawing * drawing, MatekbdKeyboardDrawingDoodad * doodad) { switch (doodad->doodad->any.type) { case XkbOutlineDoodad: case XkbSolidDoodad: draw_shape_doodad (context, drawing, doodad, &doodad->doodad->shape); break; case XkbTextDoodad: draw_text_doodad (context, drawing, doodad, &doodad->doodad->text); break; case XkbIndicatorDoodad: draw_indicator_doodad (context, drawing, doodad, &doodad->doodad->indicator); break; case XkbLogoDoodad: /* g_print ("draw_doodad: logo: %s\n", doodad->doodad->logo.logo_name); */ /* XkbLogoDoodadRec is essentially a subclass of XkbShapeDoodadRec */ draw_shape_doodad (context, drawing, doodad, &doodad->doodad->shape); break; } } typedef struct { MatekbdKeyboardDrawing *drawing; MatekbdKeyboardDrawingRenderContext *context; } DrawKeyboardItemData; static void redraw_overlapping_doodads (MatekbdKeyboardDrawingRenderContext * context, MatekbdKeyboardDrawing * drawing, MatekbdKeyboardDrawingKey * key) { GList *list; gboolean do_draw = FALSE; for (list = drawing->keyboard_items; list; list = list->next) { MatekbdKeyboardDrawingItem *item = list->data; if (do_draw && item->type == MATEKBD_KEYBOARD_DRAWING_ITEM_TYPE_DOODAD) draw_doodad (context, drawing, (MatekbdKeyboardDrawingDoodad *) item); if (list->data == key) do_draw = TRUE; } } static void draw_keyboard_item (MatekbdKeyboardDrawingItem * item, DrawKeyboardItemData * data) { MatekbdKeyboardDrawing *drawing = data->drawing; MatekbdKeyboardDrawingRenderContext *context = data->context; if (!drawing->xkb) return; switch (item->type) { case MATEKBD_KEYBOARD_DRAWING_ITEM_TYPE_INVALID: break; case MATEKBD_KEYBOARD_DRAWING_ITEM_TYPE_KEY: case MATEKBD_KEYBOARD_DRAWING_ITEM_TYPE_KEY_EXTRA: draw_key (context, drawing, (MatekbdKeyboardDrawingKey *) item); break; case MATEKBD_KEYBOARD_DRAWING_ITEM_TYPE_DOODAD: draw_doodad (context, drawing, (MatekbdKeyboardDrawingDoodad *) item); break; } } static void draw_keyboard_to_context (MatekbdKeyboardDrawingRenderContext * context, MatekbdKeyboardDrawing * drawing) { DrawKeyboardItemData data = { drawing, context }; #ifdef KBDRAW_DEBUG printf ("mods: %d\n", drawing->mods); #endif g_list_foreach (drawing->keyboard_items, (GFunc) draw_keyboard_item, &data); } static gboolean create_cairo (MatekbdKeyboardDrawing * drawing) { GtkStyleContext *style_context = NULL; GtkStateFlags state; GdkRGBA dark_color; if (drawing == NULL) return FALSE; if (drawing->surface == NULL) return FALSE; drawing->renderContext->cr = cairo_create (drawing->surface); style_context = gtk_widget_get_style_context (GTK_WIDGET (drawing)); state = gtk_style_context_get_state (style_context); gtk_style_context_get_background_color (style_context, state, &dark_color); /* make dark background by making regular background darker */ dark_color.red *= 0.7; dark_color.green *= 0.7; dark_color.blue *= 0.7; drawing->renderContext->dark_color = dark_color; return TRUE; } static void destroy_cairo (MatekbdKeyboardDrawing * drawing) { cairo_destroy (drawing->renderContext->cr); drawing->renderContext->cr = NULL; } static void draw_keyboard (MatekbdKeyboardDrawing * drawing) { GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET (drawing)); GtkStateFlags state = gtk_style_context_get_state (context); GdkRGBA color; GtkAllocation allocation; if (!drawing->xkb) return; gtk_widget_get_allocation (GTK_WIDGET (drawing), &allocation); drawing->surface = gdk_window_create_similar_surface (gtk_widget_get_window (GTK_WIDGET (drawing)), CAIRO_CONTENT_COLOR, allocation.width, allocation.height); if (create_cairo (drawing)) { /* blank background */ gtk_style_context_save (context); gtk_style_context_add_class (context, GTK_STYLE_CLASS_VIEW); gtk_style_context_get_background_color (context, state, &color); gtk_style_context_restore (context); gdk_cairo_set_source_rgba (drawing->renderContext->cr, &color); cairo_paint (drawing->renderContext->cr); draw_keyboard_to_context (drawing->renderContext, drawing); destroy_cairo (drawing); } } static void alloc_render_context (MatekbdKeyboardDrawing * drawing) { MatekbdKeyboardDrawingRenderContext *context = drawing->renderContext = g_new0 (MatekbdKeyboardDrawingRenderContext, 1); PangoContext *pangoContext = gtk_widget_get_pango_context (GTK_WIDGET (drawing)); GtkStyleContext *style_context = gtk_widget_get_style_context (GTK_WIDGET (drawing)); PangoFontDescription *fd = NULL; gtk_style_context_get (style_context, gtk_style_context_get_state (style_context), GTK_STYLE_PROPERTY_FONT, &fd, NULL); context->layout = pango_layout_new (pangoContext); pango_layout_set_ellipsize (context->layout, PANGO_ELLIPSIZE_END); context->font_desc = pango_font_description_copy (fd); context->angle = 0; context->scale_numerator = 1; context->scale_denominator = 1; } static void free_render_context (MatekbdKeyboardDrawing * drawing) { MatekbdKeyboardDrawingRenderContext *context = drawing->renderContext; g_object_unref (G_OBJECT (context->layout)); pango_font_description_free (context->font_desc); g_free (drawing->renderContext); drawing->renderContext = NULL; } static gboolean draw (GtkWidget *widget, cairo_t *cr, MatekbdKeyboardDrawing *drawing) { if (!drawing->xkb) return FALSE; if (drawing->surface == NULL) return FALSE; cairo_set_source_surface (cr, drawing->surface, 0, 0); cairo_paint (cr); return FALSE; } static gboolean idle_redraw (gpointer user_data) { MatekbdKeyboardDrawing *drawing = user_data; drawing->idle_redraw = 0; draw_keyboard (drawing); gtk_widget_queue_draw (GTK_WIDGET (drawing)); return FALSE; } static gboolean context_setup_scaling (MatekbdKeyboardDrawingRenderContext * context, MatekbdKeyboardDrawing * drawing, gdouble width, gdouble height, gdouble dpi_x, gdouble dpi_y) { if (!drawing->xkb) return FALSE; if (drawing->xkb->geom->width_mm <= 0 || drawing->xkb->geom->height_mm <= 0) { g_critical ("keyboard geometry reports width or height as zero!"); return FALSE; } if (width * drawing->xkb->geom->height_mm < height * drawing->xkb->geom->width_mm) { context->scale_numerator = width; context->scale_denominator = drawing->xkb->geom->width_mm; } else { context->scale_numerator = height; context->scale_denominator = drawing->xkb->geom->height_mm; } pango_font_description_set_size (context->font_desc, 72 * KEY_FONT_SIZE * dpi_x * context->scale_numerator / context->scale_denominator); pango_layout_set_spacing (context->layout, -160 * dpi_y * context->scale_numerator / context->scale_denominator); pango_layout_set_font_description (context->layout, context->font_desc); return TRUE; } static void size_allocate (GtkWidget * widget, GtkAllocation * allocation, MatekbdKeyboardDrawing * drawing) { MatekbdKeyboardDrawingRenderContext *context = drawing->renderContext; if (drawing->surface) { cairo_surface_destroy (drawing->surface); drawing->surface = NULL; } if (!context_setup_scaling (context, drawing, allocation->width, allocation->height, 50, 50)) return; if (!drawing->idle_redraw) drawing->idle_redraw = g_idle_add (idle_redraw, drawing); } static gint key_event (GtkWidget * widget, GdkEventKey * event, MatekbdKeyboardDrawing * drawing) { MatekbdKeyboardDrawingKey *key; if (!drawing->xkb) return FALSE; key = drawing->keys + event->hardware_keycode; if (event->hardware_keycode > drawing->xkb->max_key_code || event->hardware_keycode < drawing->xkb->min_key_code || key->xkbkey == NULL) { g_signal_emit (drawing, matekbd_keyboard_drawing_signals[BAD_KEYCODE], 0, event->hardware_keycode); return TRUE; } if ((event->type == GDK_KEY_PRESS && key->pressed) || (event->type == GDK_KEY_RELEASE && !key->pressed)) return TRUE; /* otherwise this event changes the state we believed we had before */ key->pressed = (event->type == GDK_KEY_PRESS); if (create_cairo (drawing)) { draw_key (drawing->renderContext, drawing, key); redraw_overlapping_doodads (drawing->renderContext, drawing, key); destroy_cairo (drawing); } invalidate_key_region (drawing, key); return TRUE; } static gint button_press_event (GtkWidget * widget, GdkEventButton * event, MatekbdKeyboardDrawing * drawing) { if (!drawing->xkb) return FALSE; gtk_widget_grab_focus (widget); return FALSE; } static gboolean unpress_keys (MatekbdKeyboardDrawing * drawing) { gint i; drawing->timeout = 0; if (!drawing->xkb) return FALSE; if (create_cairo (drawing)) { for (i = drawing->xkb->min_key_code; i <= drawing->xkb->max_key_code; i++) if (drawing->keys[i].pressed) { drawing->keys[i].pressed = FALSE; draw_key (drawing->renderContext, drawing, drawing->keys + i); invalidate_key_region (drawing, drawing->keys + i); } destroy_cairo (drawing); } return FALSE; } static gint focus_event (GtkWidget * widget, GdkEventFocus * event, MatekbdKeyboardDrawing * drawing) { if (event->in && drawing->timeout > 0) { g_source_remove (drawing->timeout); drawing->timeout = 0; } else if (drawing->timeout == 0) drawing->timeout = g_timeout_add (120, (GSourceFunc) unpress_keys, drawing); return FALSE; } static gint compare_keyboard_item_priorities (MatekbdKeyboardDrawingItem * a, MatekbdKeyboardDrawingItem * b) { if (a->priority > b->priority) return 1; else if (a->priority < b->priority) return -1; else return 0; } static void init_indicator_doodad (MatekbdKeyboardDrawing * drawing, XkbDoodadRec * xkbdoodad, MatekbdKeyboardDrawingDoodad * doodad) { if (!drawing->xkb) return; if (xkbdoodad->any.type == XkbIndicatorDoodad) { gint index; Atom iname = 0; Atom sname = xkbdoodad->indicator.name; unsigned long phys_indicators = drawing->xkb->indicators->phys_indicators; Atom *pind = drawing->xkb->names->indicators; #ifdef KBDRAW_DEBUG printf ("Looking for %d[%s]\n", (int) sname, XGetAtomName (drawing->display, sname)); #endif for (index = 0; index < XkbNumIndicators; index++) { iname = *pind++; /* name matches and it is real */ if (iname == sname && (phys_indicators & (1 << index))) break; if (iname == 0) break; } if (iname == 0) g_warning ("Could not find indicator %d [%s]\n", (int) sname, XGetAtomName (drawing->display, sname)); else { #ifdef KBDRAW_DEBUG printf ("Found in xkbdesc as %d\n", index); #endif drawing->physical_indicators[index] = doodad; /* Trying to obtain the real state, but if fail - just assume OFF */ if (!XkbGetNamedIndicator (drawing->display, sname, NULL, &doodad->on, NULL, NULL)) doodad->on = 0; } } } static void init_keys_and_doodads (MatekbdKeyboardDrawing * drawing) { gint i, j, k; gint x, y; if (!drawing->xkb) return; for (i = 0; i < drawing->xkb->geom->num_doodads; i++) { XkbDoodadRec *xkbdoodad = drawing->xkb->geom->doodads + i; MatekbdKeyboardDrawingDoodad *doodad = g_new (MatekbdKeyboardDrawingDoodad, 1); doodad->type = MATEKBD_KEYBOARD_DRAWING_ITEM_TYPE_DOODAD; doodad->origin_x = 0; doodad->origin_y = 0; doodad->angle = 0; doodad->priority = xkbdoodad->any.priority * 256 * 256; doodad->doodad = xkbdoodad; init_indicator_doodad (drawing, xkbdoodad, doodad); drawing->keyboard_items = g_list_append (drawing->keyboard_items, doodad); } for (i = 0; i < drawing->xkb->geom->num_sections; i++) { XkbSectionRec *section = drawing->xkb->geom->sections + i; guint priority; #ifdef KBDRAW_DEBUG printf ("initing section %d containing %d rows\n", i, section->num_rows); #endif x = section->left; y = section->top; priority = section->priority * 256 * 256; for (j = 0; j < section->num_rows; j++) { XkbRowRec *row = section->rows + j; #ifdef KBDRAW_DEBUG printf (" initing row %d\n", j); #endif x = section->left + row->left; y = section->top + row->top; for (k = 0; k < row->num_keys; k++) { XkbKeyRec *xkbkey = row->keys + k; MatekbdKeyboardDrawingKey *key; XkbShapeRec *shape = drawing->xkb->geom->shapes + xkbkey->shape_ndx; guint keycode = find_keycode (drawing, xkbkey-> name.name); if (keycode == INVALID_KEYCODE) continue; #ifdef KBDRAW_DEBUG printf (" initing key %d, shape: %p(%p + %d), code: %u\n", k, shape, drawing->xkb->geom->shapes, xkbkey->shape_ndx, keycode); #endif if (row->vertical) y += xkbkey->gap; else x += xkbkey->gap; if (keycode >= drawing->xkb->min_key_code && keycode <= drawing->xkb->max_key_code) { key = drawing->keys + keycode; if (key->type == MATEKBD_KEYBOARD_DRAWING_ITEM_TYPE_INVALID) { key->type = MATEKBD_KEYBOARD_DRAWING_ITEM_TYPE_KEY; } else { /* duplicate key for the same keycode, already defined as MATEKBD_KEYBOARD_DRAWING_ITEM_TYPE_KEY */ key = g_new0 (MatekbdKeyboardDrawingKey, 1); key->type = MATEKBD_KEYBOARD_DRAWING_ITEM_TYPE_KEY_EXTRA; } } else { g_warning ("key %4.4s: keycode = %u; not in range %d..%d\n", xkbkey->name.name, keycode, drawing->xkb->min_key_code, drawing->xkb->max_key_code); key = g_new0 (MatekbdKeyboardDrawingKey, 1); key->type = MATEKBD_KEYBOARD_DRAWING_ITEM_TYPE_KEY_EXTRA; } key->xkbkey = xkbkey; key->angle = section->angle; rotate_coordinate (section->left, section->top, x, y, section->angle, &key->origin_x, &key->origin_y); key->priority = priority; key->keycode = keycode; drawing->keyboard_items = g_list_append (drawing->keyboard_items, key); if (row->vertical) y += shape->bounds.y2; else x += shape->bounds.x2; priority++; } } for (j = 0; j < section->num_doodads; j++) { XkbDoodadRec *xkbdoodad = section->doodads + j; MatekbdKeyboardDrawingDoodad *doodad = g_new (MatekbdKeyboardDrawingDoodad, 1); doodad->type = MATEKBD_KEYBOARD_DRAWING_ITEM_TYPE_DOODAD; doodad->origin_x = x; doodad->origin_y = y; doodad->angle = section->angle; doodad->priority = priority + xkbdoodad->any.priority; doodad->doodad = xkbdoodad; init_indicator_doodad (drawing, xkbdoodad, doodad); drawing->keyboard_items = g_list_append (drawing->keyboard_items, doodad); } } drawing->keyboard_items = g_list_sort (drawing->keyboard_items, (GCompareFunc) compare_keyboard_item_priorities); } static void init_colors (MatekbdKeyboardDrawing * drawing) { gboolean result; gint i; if (!drawing->xkb) return; drawing->colors = g_new (GdkRGBA, drawing->xkb->geom->num_colors); for (i = 0; i < drawing->xkb->geom->num_colors; i++) { result = parse_xkb_color_spec (drawing->xkb->geom-> colors[i].spec, drawing->colors + i); if (!result) g_warning ("init_colors: unable to parse color %s\n", drawing->xkb->geom->colors[i].spec); } } static void free_cdik ( /*colors doodads indicators keys */ MatekbdKeyboardDrawing * drawing) { GList *itemp; if (!drawing->xkb) return; for (itemp = drawing->keyboard_items; itemp; itemp = itemp->next) { MatekbdKeyboardDrawingItem *item = itemp->data; switch (item->type) { case MATEKBD_KEYBOARD_DRAWING_ITEM_TYPE_INVALID: case MATEKBD_KEYBOARD_DRAWING_ITEM_TYPE_KEY: break; case MATEKBD_KEYBOARD_DRAWING_ITEM_TYPE_KEY_EXTRA: case MATEKBD_KEYBOARD_DRAWING_ITEM_TYPE_DOODAD: g_free (item); break; } } g_list_free (drawing->keyboard_items); drawing->keyboard_items = NULL; g_free (drawing->keys); g_free (drawing->colors); } static void alloc_cdik (MatekbdKeyboardDrawing * drawing) { if (!drawing->xkb) return; drawing->physical_indicators_size = drawing->xkb->indicators->phys_indicators + 1; drawing->physical_indicators = g_new0 (MatekbdKeyboardDrawingDoodad *, drawing->physical_indicators_size); drawing->keys = g_new0 (MatekbdKeyboardDrawingKey, drawing->xkb->max_key_code + 1); } static void process_indicators_state_notify (XkbIndicatorNotifyEvent * iev, MatekbdKeyboardDrawing * drawing) { /* Good question: should we track indicators when the keyboard is NOT really taken from the screen */ gint i; for (i = 0; i <= drawing->xkb->indicators->phys_indicators; i++) if (drawing->physical_indicators[i] != NULL && (iev->changed & 1 << i)) { gint state = (iev->state & 1 << i) != FALSE; if ((state && !drawing->physical_indicators[i]->on) || (!state && drawing->physical_indicators[i]->on)) { drawing->physical_indicators[i]->on = state; if (create_cairo (drawing)) { draw_doodad (drawing->renderContext, drawing, drawing->physical_indicators [i]); destroy_cairo (drawing); } invalidate_indicator_doodad_region (drawing, drawing->physical_indicators[i]); } } } static GdkFilterReturn xkb_state_notify_event_filter (GdkXEvent * gdkxev, GdkEvent * event, MatekbdKeyboardDrawing * drawing) { #define group_change_mask (XkbGroupStateMask | XkbGroupBaseMask | XkbGroupLatchMask | XkbGroupLockMask) #define modifier_change_mask (XkbModifierStateMask | XkbModifierBaseMask | XkbModifierLatchMask | XkbModifierLockMask) if (!drawing->xkb) return GDK_FILTER_CONTINUE; if (((XEvent *) gdkxev)->type == drawing->xkb_event_type) { XkbEvent *kev = (XkbEvent *) gdkxev; GtkAllocation allocation; switch (kev->any.xkb_type) { case XkbStateNotify: if (((kev->state.changed & modifier_change_mask) && drawing->track_modifiers)) { free_cdik (drawing); if (drawing->track_modifiers) matekbd_keyboard_drawing_set_mods (drawing, kev->state.compat_state); drawing->keys = g_new0 (MatekbdKeyboardDrawingKey, drawing->xkb->max_key_code + 1); gtk_widget_get_allocation (GTK_WIDGET (drawing), &allocation); size_allocate (GTK_WIDGET (drawing), &allocation, drawing); init_keys_and_doodads (drawing); init_colors (drawing); } break; case XkbIndicatorStateNotify: { process_indicators_state_notify (& ((XkbEvent *) gdkxev)->indicators, drawing); } break; case XkbIndicatorMapNotify: case XkbControlsNotify: case XkbNamesNotify: case XkbNewKeyboardNotify: { XkbStateRec state; memset (&state, 0, sizeof (state)); XkbGetState (drawing->display, XkbUseCoreKbd, &state); if (drawing->track_modifiers) matekbd_keyboard_drawing_set_mods (drawing, state.compat_state); if (drawing->track_config) matekbd_keyboard_drawing_set_keyboard (drawing, NULL); } break; } } return GDK_FILTER_CONTINUE; } static void destroy (MatekbdKeyboardDrawing * drawing) { free_render_context (drawing); gdk_window_remove_filter (NULL, (GdkFilterFunc) xkb_state_notify_event_filter, drawing); if (drawing->timeout > 0) { g_source_remove (drawing->timeout); drawing->timeout = 0; } if (drawing->idle_redraw > 0) { g_source_remove (drawing->idle_redraw); drawing->idle_redraw = 0; } if (drawing->surface != NULL) { cairo_surface_destroy (drawing->surface); } } static void style_changed (MatekbdKeyboardDrawing * drawing) { pango_layout_context_changed (drawing->renderContext->layout); } static void matekbd_keyboard_drawing_init (MatekbdKeyboardDrawing * drawing) { gint opcode = 0, error = 0, major = 1, minor = 0; gint mask; drawing->display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); printf ("dpy: %p\n", (void *) drawing->display); if (!XkbQueryExtension (drawing->display, &opcode, &drawing->xkb_event_type, &error, &major, &minor)) g_critical ("XkbQueryExtension failed! Stuff probably won't work."); printf ("evt/error/major/minor: %d/%d/%d/%d\n", drawing->xkb_event_type, error, major, minor); /* XXX: this stuff probably doesn't matter.. also, gdk_screen_get_default can fail */ if (gtk_widget_has_screen (GTK_WIDGET (drawing))) drawing->screen_num = gdk_screen_get_number (gtk_widget_get_screen (GTK_WIDGET (drawing))); else drawing->screen_num = gdk_screen_get_number (gdk_screen_get_default ()); drawing->surface = NULL; alloc_render_context (drawing); drawing->keyboard_items = NULL; drawing->colors = NULL; drawing->track_modifiers = 0; drawing->track_config = 0; /* XXX: XkbClientMapMask | XkbIndicatorMapMask | XkbNamesMask | XkbGeometryMask */ drawing->xkb = XkbGetKeyboard (drawing->display, XkbGBN_GeometryMask | XkbGBN_KeyNamesMask | XkbGBN_OtherNamesMask | XkbGBN_SymbolsMask | XkbGBN_IndicatorMapMask, XkbUseCoreKbd); if (drawing->xkb) { XkbGetNames (drawing->display, XkbAllNamesMask, drawing->xkb); XkbSelectEventDetails (drawing->display, XkbUseCoreKbd, XkbIndicatorStateNotify, drawing->xkb->indicators->phys_indicators, drawing->xkb->indicators->phys_indicators); } drawing->l3mod = XkbKeysymToModifiers (drawing->display, GDK_KEY_ISO_Level3_Shift); drawing->xkbOnDisplay = TRUE; alloc_cdik (drawing); mask = (XkbStateNotifyMask | XkbNamesNotifyMask | XkbControlsNotifyMask | XkbIndicatorMapNotifyMask | XkbNewKeyboardNotifyMask); XkbSelectEvents (drawing->display, XkbUseCoreKbd, mask, mask); mask = XkbGroupStateMask | XkbModifierStateMask; XkbSelectEventDetails (drawing->display, XkbUseCoreKbd, XkbStateNotify, mask, mask); mask = (XkbGroupNamesMask | XkbIndicatorNamesMask); XkbSelectEventDetails (drawing->display, XkbUseCoreKbd, XkbNamesNotify, mask, mask); init_keys_and_doodads (drawing); init_colors (drawing); /* required to get key events */ gtk_widget_set_can_focus (GTK_WIDGET (drawing), TRUE); gtk_widget_set_events (GTK_WIDGET (drawing), GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_FOCUS_CHANGE_MASK); g_signal_connect (G_OBJECT (drawing), "draw", G_CALLBACK (draw), drawing); g_signal_connect_after (G_OBJECT (drawing), "key-press-event", G_CALLBACK (key_event), drawing); g_signal_connect_after (G_OBJECT (drawing), "key-release-event", G_CALLBACK (key_event), drawing); g_signal_connect (G_OBJECT (drawing), "button-press-event", G_CALLBACK (button_press_event), drawing); g_signal_connect (G_OBJECT (drawing), "focus-out-event", G_CALLBACK (focus_event), drawing); g_signal_connect (G_OBJECT (drawing), "focus-in-event", G_CALLBACK (focus_event), drawing); g_signal_connect (G_OBJECT (drawing), "size-allocate", G_CALLBACK (size_allocate), drawing); g_signal_connect (G_OBJECT (drawing), "destroy", G_CALLBACK (destroy), drawing); g_signal_connect (G_OBJECT (drawing), "style-set", G_CALLBACK (style_changed), drawing); gdk_window_add_filter (NULL, (GdkFilterFunc) xkb_state_notify_event_filter, drawing); } GtkWidget * matekbd_keyboard_drawing_new (void) { return GTK_WIDGET (g_object_new (matekbd_keyboard_drawing_get_type (), NULL)); } static void matekbd_keyboard_drawing_class_init (MatekbdKeyboardDrawingClass * klass) { #if GTK_CHECK_VERSION (3, 20, 0) GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); gtk_widget_class_set_css_name (widget_class, "matekbd-keyboard-drawing"); #endif klass->bad_keycode = NULL; matekbd_keyboard_drawing_signals[BAD_KEYCODE] = g_signal_new ("bad-keycode", matekbd_keyboard_drawing_get_type (), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MatekbdKeyboardDrawingClass, bad_keycode), NULL, NULL, matekbd_keyboard_drawing_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); } GType matekbd_keyboard_drawing_get_type (void) { static GType matekbd_keyboard_drawing_type = 0; if (!matekbd_keyboard_drawing_type) { static const GTypeInfo matekbd_keyboard_drawing_info = { sizeof (MatekbdKeyboardDrawingClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) matekbd_keyboard_drawing_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (MatekbdKeyboardDrawing), 0, /* n_preallocs */ (GInstanceInitFunc) matekbd_keyboard_drawing_init, }; matekbd_keyboard_drawing_type = g_type_register_static (GTK_TYPE_DRAWING_AREA, "MatekbdKeyboardDrawing", &matekbd_keyboard_drawing_info, 0); } return matekbd_keyboard_drawing_type; } void matekbd_keyboard_drawing_set_mods (MatekbdKeyboardDrawing * drawing, guint mods) { #ifdef KBDRAW_DEBUG printf ("set_mods: %d\n", mods); #endif if (mods != drawing->mods) { drawing->mods = mods; gtk_widget_queue_draw (GTK_WIDGET (drawing)); } } /** * matekbd_keyboard_drawing_render: * @kbdrawing: keyboard layout to render * @cr: Cairo context to render to * @layout: Pango layout to use to render text * @x: left coordinate (pixels) of region to render in * @y: top coordinate (pixels) of region to render in * @width: width (pixels) of region to render in * @height: height (pixels) of region to render in * * Renders a keyboard layout to a cairo_t context. @cr and @layout can be got * from e.g. a GtkWidget or a GtkPrintContext. @cr and @layout may be modified * by the function but will not be unreffed. * * Returns: %TRUE on success, %FALSE on failure */ gboolean matekbd_keyboard_drawing_render (MatekbdKeyboardDrawing * kbdrawing, cairo_t * cr, PangoLayout * layout, double x, double y, double width, double height, double dpi_x, double dpi_y) { GtkStyleContext *style_context = gtk_widget_get_style_context (GTK_WIDGET (kbdrawing)); GdkRGBA dark_color; PangoFontDescription *fd = NULL; gtk_style_context_get_background_color (style_context, gtk_style_context_get_state (style_context), &dark_color); /* make dark background by making regular background darker */ dark_color.red *= 0.7; dark_color.green *= 0.7; dark_color.blue *= 0.7; gtk_style_context_get (style_context, gtk_style_context_get_state (style_context), GTK_STYLE_PROPERTY_FONT, &fd, NULL); fd = pango_font_description_copy (fd); MatekbdKeyboardDrawingRenderContext context = { cr, kbdrawing->renderContext->angle, layout, pango_font_description_copy (fd), 1, 1, dark_color }; if (!context_setup_scaling (&context, kbdrawing, width, height, dpi_x, dpi_y)) return FALSE; cairo_translate (cr, x, y); draw_keyboard_to_context (&context, kbdrawing); pango_font_description_free (fd); return TRUE; } /** * matekbd_keyboard_drawing_set_keyboard: (skip) */ gboolean matekbd_keyboard_drawing_set_keyboard (MatekbdKeyboardDrawing * drawing, XkbComponentNamesRec * names) { GtkAllocation allocation; free_cdik (drawing); if (drawing->xkb) XkbFreeKeyboard (drawing->xkb, 0, TRUE); /* free_all = TRUE */ drawing->xkb = NULL; if (names) { drawing->xkb = XkbGetKeyboardByName (drawing->display, XkbUseCoreKbd, names, 0, XkbGBN_GeometryMask | XkbGBN_KeyNamesMask | XkbGBN_OtherNamesMask | XkbGBN_ClientSymbolsMask | XkbGBN_IndicatorMapMask, FALSE); drawing->xkbOnDisplay = FALSE; } else { drawing->xkb = XkbGetKeyboard (drawing->display, XkbGBN_GeometryMask | XkbGBN_KeyNamesMask | XkbGBN_OtherNamesMask | XkbGBN_SymbolsMask | XkbGBN_IndicatorMapMask, XkbUseCoreKbd); XkbGetNames (drawing->display, XkbAllNamesMask, drawing->xkb); drawing->xkbOnDisplay = TRUE; } if (drawing->xkb) { XkbSelectEventDetails (drawing->display, XkbUseCoreKbd, XkbIndicatorStateNotify, drawing->xkb->indicators->phys_indicators, drawing->xkb->indicators->phys_indicators); } alloc_cdik (drawing); init_keys_and_doodads (drawing); init_colors (drawing); gtk_widget_get_allocation (GTK_WIDGET (drawing), &allocation); size_allocate (GTK_WIDGET (drawing), &allocation, drawing); gtk_widget_queue_draw (GTK_WIDGET (drawing)); return TRUE; } const gchar* matekbd_keyboard_drawing_get_keycodes(MatekbdKeyboardDrawing* drawing) { if (!drawing->xkb || drawing->xkb->names->keycodes <= 0) { return NULL; } else { return XGetAtomName(drawing->display, drawing->xkb->names->keycodes); } } const gchar* matekbd_keyboard_drawing_get_geometry(MatekbdKeyboardDrawing* drawing) { if (!drawing->xkb || drawing->xkb->names->geometry <= 0) { return NULL; } else { return XGetAtomName(drawing->display, drawing->xkb->names->geometry); } } const gchar* matekbd_keyboard_drawing_get_symbols(MatekbdKeyboardDrawing* drawing) { if (!drawing->xkb || drawing->xkb->names->symbols <= 0) { return NULL; } else { return XGetAtomName(drawing->display, drawing->xkb->names->symbols); } } const gchar* matekbd_keyboard_drawing_get_types(MatekbdKeyboardDrawing* drawing) { if (!drawing->xkb || drawing->xkb->names->types <= 0) { return NULL; } else { return XGetAtomName(drawing->display, drawing->xkb->names->types); } } const gchar* matekbd_keyboard_drawing_get_compat(MatekbdKeyboardDrawing* drawing) { if (!drawing->xkb || drawing->xkb->names->compat <= 0) { return NULL; } else { return XGetAtomName(drawing->display, drawing->xkb->names->compat); } } void matekbd_keyboard_drawing_set_track_modifiers (MatekbdKeyboardDrawing * drawing, gboolean enable) { if (enable) { XkbStateRec state; drawing->track_modifiers = 1; memset (&state, 0, sizeof (state)); XkbGetState (drawing->display, XkbUseCoreKbd, &state); matekbd_keyboard_drawing_set_mods (drawing, state.compat_state); } else drawing->track_modifiers = 0; } void matekbd_keyboard_drawing_set_track_config (MatekbdKeyboardDrawing * drawing, gboolean enable) { if (enable) drawing->track_config = 1; else drawing->track_config = 0; } void matekbd_keyboard_drawing_set_groups_levels (MatekbdKeyboardDrawing * drawing, MatekbdKeyboardDrawingGroupLevel * groupLevels[]) { #ifdef KBDRAW_DEBUG printf ("set_group_levels [topLeft]: %d %d \n", groupLevels[MATEKBD_KEYBOARD_DRAWING_POS_TOPLEFT]->group, groupLevels[MATEKBD_KEYBOARD_DRAWING_POS_TOPLEFT]->level); printf ("set_group_levels [topRight]: %d %d \n", groupLevels[MATEKBD_KEYBOARD_DRAWING_POS_TOPRIGHT]->group, groupLevels[MATEKBD_KEYBOARD_DRAWING_POS_TOPRIGHT]->level); printf ("set_group_levels [bottomLeft]: %d %d \n", groupLevels[MATEKBD_KEYBOARD_DRAWING_POS_BOTTOMLEFT]->group, groupLevels[MATEKBD_KEYBOARD_DRAWING_POS_BOTTOMLEFT]->level); printf ("set_group_levels [bottomRight]: %d %d \n", groupLevels[MATEKBD_KEYBOARD_DRAWING_POS_BOTTOMRIGHT]->group, groupLevels[MATEKBD_KEYBOARD_DRAWING_POS_BOTTOMRIGHT]->level); #endif drawing->groupLevels = groupLevels; gtk_widget_queue_draw (GTK_WIDGET (drawing)); } typedef struct { MatekbdKeyboardDrawing *drawing; const gchar *description; } XkbLayoutPreviewPrintData; static void matekbd_keyboard_drawing_begin_print (GtkPrintOperation * operation, GtkPrintContext * context, XkbLayoutPreviewPrintData * data) { /* We always print single-page documents */ GtkPrintSettings *settings = gtk_print_operation_get_print_settings (operation); gtk_print_operation_set_n_pages (operation, 1); if (!gtk_print_settings_has_key (settings, GTK_PRINT_SETTINGS_ORIENTATION)) gtk_print_settings_set_orientation (settings, GTK_PAGE_ORIENTATION_LANDSCAPE); } static void matekbd_keyboard_drawing_draw_page (GtkPrintOperation * operation, GtkPrintContext * context, gint page_nr, XkbLayoutPreviewPrintData * data) { cairo_t *cr = gtk_print_context_get_cairo_context (context); PangoLayout *layout = gtk_print_context_create_pango_layout (context); PangoFontDescription *desc = pango_font_description_from_string ("sans 8"); gdouble width = gtk_print_context_get_width (context); gdouble height = gtk_print_context_get_height (context); gdouble dpi_x = gtk_print_context_get_dpi_x (context); gdouble dpi_y = gtk_print_context_get_dpi_y (context); gchar *header; gtk_print_operation_set_unit (operation, GTK_UNIT_PIXEL); header = g_strdup_printf (_("Keyboard layout \"%s\"\n" "Copyright © X.Org Foundation and " "XKeyboardConfig contributors\n" "For licensing see package metadata"), data->description); pango_layout_set_markup (layout, header, -1); pango_layout_set_font_description (layout, desc); pango_font_description_free (desc); pango_layout_set_width (layout, pango_units_from_double (width)); pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER); cairo_set_source_rgb (cr, 0, 0, 0); cairo_move_to (cr, 0, 0); pango_cairo_show_layout (cr, layout); matekbd_keyboard_drawing_render (MATEKBD_KEYBOARD_DRAWING (data->drawing), cr, layout, 0.0, 0.0, width, height, dpi_x, dpi_y); g_object_unref (layout); } void matekbd_keyboard_drawing_print (MatekbdKeyboardDrawing * drawing, GtkWindow * parent_window, const gchar * description) { GtkPrintOperation *print; GtkPrintOperationResult res; static GtkPrintSettings *settings = NULL; XkbLayoutPreviewPrintData data = { drawing, description }; print = gtk_print_operation_new (); if (settings != NULL) gtk_print_operation_set_print_settings (print, settings); g_signal_connect (print, "begin_print", G_CALLBACK (matekbd_keyboard_drawing_begin_print), &data); g_signal_connect (print, "draw_page", G_CALLBACK (matekbd_keyboard_drawing_draw_page), &data); res = gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, parent_window, NULL); if (res == GTK_PRINT_OPERATION_RESULT_APPLY) { if (settings != NULL) g_object_unref (settings); settings = gtk_print_operation_get_print_settings (print); g_object_ref (settings); } g_object_unref (print); } static void show_layout_response (GtkWidget * dialog, gint resp) { GdkRectangle rect; GtkWidget *kbdraw; const gchar *groupName; switch (resp) { case GTK_RESPONSE_CLOSE: gtk_window_get_position (GTK_WINDOW (dialog), &rect.x, &rect.y); gtk_window_get_size (GTK_WINDOW (dialog), &rect.width, &rect.height); matekbd_preview_save_position (&rect); gtk_widget_destroy (dialog); break; case GTK_RESPONSE_PRINT: kbdraw = GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), "kbdraw")); groupName = (const gchar *) g_object_get_data (G_OBJECT (dialog), "groupName"); matekbd_keyboard_drawing_print (MATEKBD_KEYBOARD_DRAWING (kbdraw), GTK_WINDOW (dialog), groupName ? groupName : _("Unknown")); } } GtkWidget * matekbd_keyboard_drawing_new_dialog (gint group, gchar * group_name) { static MatekbdKeyboardDrawingGroupLevel groupsLevels[] = { { 0, 1}, { 0, 3}, { 0, 0}, { 0, 2} }; static MatekbdKeyboardDrawingGroupLevel *pGroupsLevels[] = { groupsLevels, groupsLevels + 1, groupsLevels + 2, groupsLevels + 3 }; GtkBuilder *builder; GtkWidget *dialog, *kbdraw; XkbComponentNamesRec component_names; XklConfigRec *xkl_data; GdkRectangle *rect; GError *error = NULL; char title[128] = ""; XklEngine* engine = xkl_engine_get_instance(GDK_DISPLAY_XDISPLAY(gdk_display_get_default())); builder = gtk_builder_new (); gtk_builder_add_from_file (builder, UIDIR "/show-layout.ui", &error); if (error) { g_error ("building ui from %s failed: %s", UIDIR "/show-layout.ui", error->message); g_clear_error (&error); } dialog = GTK_WIDGET (gtk_builder_get_object (builder, "gswitchit_layout_view")); kbdraw = matekbd_keyboard_drawing_new (); gtk_widget_set_vexpand (kbdraw, TRUE); snprintf (title, sizeof (title), _("Keyboard Layout \"%s\""), group_name); gtk_window_set_title (GTK_WINDOW (dialog), title); g_object_set_data_full (G_OBJECT (dialog), "group_name", g_strdup (group_name), g_free); matekbd_keyboard_drawing_set_groups_levels (MATEKBD_KEYBOARD_DRAWING (kbdraw), pGroupsLevels); xkl_data = xkl_config_rec_new (); if (xkl_config_rec_get_from_server (xkl_data, engine)) { int num_layouts = g_strv_length (xkl_data->layouts); int num_variants = g_strv_length (xkl_data->variants); if (group >= 0 && group < num_layouts && group < num_variants) { char *l = g_strdup (xkl_data->layouts[group]); char *v = g_strdup (xkl_data->variants[group]); char **p; int i; if ((p = xkl_data->layouts) != NULL) for (i = num_layouts; --i >= 0;) g_free (*p++); if ((p = xkl_data->variants) != NULL) for (i = num_variants; --i >= 0;) g_free (*p++); xkl_data->layouts = g_realloc (xkl_data->layouts, sizeof (char *) * 2); xkl_data->variants = g_realloc (xkl_data->variants, sizeof (char *) * 2); xkl_data->layouts[0] = l; xkl_data->variants[0] = v; xkl_data->layouts[1] = xkl_data->variants[1] = NULL; } if (xkl_xkb_config_native_prepare(engine, xkl_data, &component_names)) { matekbd_keyboard_drawing_set_keyboard (MATEKBD_KEYBOARD_DRAWING (kbdraw), &component_names); xkl_xkb_config_native_cleanup (engine, &component_names); } } g_object_unref (G_OBJECT (xkl_data)); g_object_set_data (G_OBJECT (dialog), "builderData", builder); g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (show_layout_response), NULL); rect = matekbd_preview_load_position (); if (rect != NULL) { gtk_window_move (GTK_WINDOW (dialog), rect->x, rect->y); gtk_window_resize (GTK_WINDOW (dialog), rect->width, rect->height); g_free (rect); } else { gtk_window_resize (GTK_WINDOW (dialog), 700, 400); } gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE); gtk_container_add (GTK_CONTAINER (gtk_builder_get_object (builder, "preview_vbox")), kbdraw); g_object_set_data (G_OBJECT (dialog), "kbdraw", kbdraw); g_signal_connect_swapped(dialog, "destroy", G_CALLBACK(g_object_unref), g_object_get_data(G_OBJECT(dialog), "builderData")); gtk_widget_show_all(dialog); return dialog; }