/* $Id: keyboard.c,v 1.30 2012/05/11 08:57:42 khorben Exp $ */ /* Copyright (c) 2011 Pierre Pronchery */ /* This file is part of DeforaOS Desktop Keyboard */ /* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3 of the License. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* TODO: * - see if XKB could be used to define the keyboard */ #include #include #include #include #include #define XK_LATIN1 #define XK_MISCELLANY #include #include #include "callbacks.h" #include "layout.h" #include "keyboard.h" /* Keyboard */ /* private */ /* types */ struct _Keyboard { /* preferences */ int embedded; KeyboardLayout ** layouts; size_t layouts_cnt; PangoFontDescription * font; GtkWidget * window; GdkRectangle geometry; int width; int height; int x; int y; }; typedef struct _KeyboardKeyDefinition { unsigned int row; unsigned int width; unsigned int modifier; unsigned int keysym; char const * label; } KeyboardKeyDefinition; typedef enum _KeyboardLayoutSection { KLS_LETTERS = 0, KLS_KEYPAD, KLS_SPECIAL } KeyboardLayoutSection; #define KLS_LAST KLS_SPECIAL #define KLS_COUNT (KLS_LAST + 1) typedef struct _KeyboardLayoutDefinition { char const * label; KeyboardKeyDefinition const * keys; } KeyboardLayoutDefinition; /* variables */ static KeyboardKeyDefinition const _keyboard_layout_letters[] = { { 0, 2, 0, XK_q, "q" }, { 0, 0, XK_Shift_L, XK_Q, "Q" }, { 0, 2, 0, XK_w, "w" }, { 0, 0, XK_Shift_L, XK_W, "W" }, { 0, 2, 0, XK_e, "e" }, { 0, 0, XK_Shift_L, XK_E, "E" }, { 0, 2, 0, XK_r, "r" }, { 0, 0, XK_Shift_L, XK_R, "R" }, { 0, 2, 0, XK_t, "t" }, { 0, 0, XK_Shift_L, XK_T, "T" }, { 0, 2, 0, XK_y, "y" }, { 0, 0, XK_Shift_L, XK_Y, "Y" }, { 0, 2, 0, XK_u, "u" }, { 0, 0, XK_Shift_L, XK_U, "U" }, { 0, 2, 0, XK_i, "i" }, { 0, 0, XK_Shift_L, XK_I, "I" }, { 0, 2, 0, XK_o, "o" }, { 0, 0, XK_Shift_L, XK_O, "O" }, { 0, 2, 0, XK_p, "p" }, { 0, 0, XK_Shift_L, XK_P, "P" }, { 1, 1, 0, 0, NULL }, { 1, 2, 0, XK_a, "a" }, { 1, 0, XK_Shift_L, XK_A, "A" }, { 1, 2, 0, XK_s, "s" }, { 1, 0, XK_Shift_L, XK_S, "S" }, { 1, 2, 0, XK_d, "d" }, { 1, 0, XK_Shift_L, XK_D, "D" }, { 1, 2, 0, XK_f, "f" }, { 1, 0, XK_Shift_L, XK_F, "F" }, { 1, 2, 0, XK_g, "g" }, { 1, 0, XK_Shift_L, XK_G, "G" }, { 1, 2, 0, XK_h, "h" }, { 1, 0, XK_Shift_L, XK_H, "H" }, { 1, 2, 0, XK_j, "j" }, { 1, 0, XK_Shift_L, XK_J, "J" }, { 1, 2, 0, XK_k, "k" }, { 1, 0, XK_Shift_L, XK_K, "K" }, { 1, 2, 0, XK_l, "l" }, { 1, 0, XK_Shift_L, XK_L, "L" }, { 2, 2, 0, XK_Shift_L, "\xe2\x87\xa7" }, { 2, 2, 0, XK_z, "z" }, { 2, 0, XK_Shift_L, XK_Z, "Z" }, { 2, 2, 0, XK_x, "x" }, { 2, 0, XK_Shift_L, XK_X, "X" }, { 2, 2, 0, XK_c, "c" }, { 2, 0, XK_Shift_L, XK_C, "C" }, { 2, 2, 0, XK_v, "v" }, { 2, 0, XK_Shift_L, XK_V, "V" }, { 2, 2, 0, XK_b, "b" }, { 2, 0, XK_Shift_L, XK_B, "B" }, { 2, 2, 0, XK_n, "n" }, { 2, 0, XK_Shift_L, XK_N, "N" }, { 2, 2, 0, XK_m, "m" }, { 2, 0, XK_Shift_L, XK_M, "M" }, { 2, 2, 0, XK_comma, "," }, { 2, 0, XK_Shift_L, XK_comma, "<" }, { 2, 2, 0, XK_period, "." }, { 2, 0, XK_Shift_L, XK_period, ">" }, { 3, 3, 0, 0, NULL }, { 3, 3, 0, XK_Control_L, "Ctrl" }, { 3, 3, 0, XK_Alt_L, "Alt" }, { 3, 5, 0, XK_space, " " }, { 3, 0, XK_Shift_L, XK_space, " " }, { 3, 3, 0, XK_Return, "\xe2\x86\xb2" }, { 3, 3, 0, XK_BackSpace, "\xe2\x8c\xab" }, { 0, 0, 0, 0, NULL } }; static KeyboardKeyDefinition const _keyboard_layout_keypad[] = { { 0, 3, 0, XK_Num_Lock, "Num" }, { 0, 1, 0, 0, NULL }, { 0, 4, 0, XK_KP_Home, "\xe2\x86\x96" }, { 0, 0, XK_Num_Lock, XK_7, "7" }, { 0, 4, 0, XK_KP_Up, "\xe2\x86\x91" }, { 0, 0, XK_Num_Lock, XK_8, "8" }, { 0, 4, 0, XK_KP_Page_Up, "\xe2\x87\x9e" }, { 0, 0, XK_Num_Lock, XK_9, "9" }, { 0, 1, 0, 0, NULL }, { 0, 3, 0, XK_KP_Subtract, "-" }, { 1, 3, 0, XK_KP_Divide, "/" }, { 1, 1, 0, 0, NULL }, { 1, 4, 0, XK_KP_Left, "\xe2\x86\x90" }, { 1, 0, XK_Num_Lock, XK_4, "4" }, { 1, 4, 0, XK_5, "5" }, { 1, 0, XK_Num_Lock, XK_5, "5" }, { 1, 4, 0, XK_KP_Right, "\xe2\x86\x92" }, { 1, 0, XK_Num_Lock, XK_6, "6" }, { 1, 1, 0, 0, NULL }, { 1, 3, 0, XK_KP_Add, "+" }, { 2, 3, 0, XK_KP_Multiply, "*" }, { 2, 1, 0, 0, NULL }, { 2, 4, 0, XK_KP_End, "\xe2\x86\x99" }, { 2, 0, XK_Num_Lock, XK_1, "1" }, { 2, 4, 0, XK_KP_Down, "\xe2\x86\x93" }, { 2, 0, XK_Num_Lock, XK_2, "2" }, { 2, 4, 0, XK_KP_Page_Down, "\xe2\x87\x9f" }, { 2, 0, XK_Num_Lock, XK_3, "3" }, { 2, 1, 0, 0, NULL }, { 2, 3, 0, XK_KP_Enter, "\xe2\x86\xb2" }, { 3, 3, 0, 0, NULL }, { 3, 1, 0, 0, NULL }, { 3, 8, 0, XK_KP_Insert, "Ins" }, { 3, 0, XK_Num_Lock, XK_0, "0" }, { 3, 4, 0, XK_KP_Delete, "Del" }, { 3, 0, XK_Num_Lock, XK_KP_Decimal, "." }, { 3, 1, 0, 0, NULL }, { 3, 3, 0, XK_BackSpace, "\xe2\x8c\xab" }, { 0, 0, 0, 0, NULL } }; static KeyboardKeyDefinition const _keyboard_layout_special[] = { { 0, 3, 0, XK_Escape, "Esc" }, { 0, 2, 0, XK_F1, "F1" }, { 0, 2, 0, XK_F2, "F2" }, { 0, 2, 0, XK_F3, "F3" }, { 0, 2, 0, XK_F4, "F4" }, { 0, 1, 0, 0, NULL }, { 0, 2, 0, XK_F5, "F5" }, { 0, 0, XK_Shift_L, XK_F9, "F9" }, { 0, 2, 0, XK_F6, "F6" }, { 0, 0, XK_Shift_L, XK_F10, "F10" }, { 0, 2, 0, XK_F7, "F7" }, { 0, 0, XK_Shift_L, XK_F11, "F11" }, { 0, 2, 0, XK_F8, "F8" }, { 0, 0, XK_Shift_L, XK_F12, "F12" }, { 1, 2, 0, XK_1, "1" }, { 1, 0, XK_Shift_L, XK_1, "!" }, { 1, 2, 0, XK_2, "2" }, { 1, 0, XK_Shift_L, XK_2, "@" }, { 1, 2, 0, XK_3, "3" }, { 1, 0, XK_Shift_L, XK_3, "#" }, { 1, 2, 0, XK_4, "4" }, { 1, 0, XK_Shift_L, XK_4, "$" }, { 1, 2, 0, XK_5, "5" }, { 1, 0, XK_Shift_L, XK_5, "%" }, { 1, 2, 0, XK_6, "6" }, { 1, 0, XK_Shift_L, XK_6, "^" }, { 1, 2, 0, XK_7, "7" }, { 1, 0, XK_Shift_L, XK_7, "&" }, { 1, 2, 0, XK_8, "8" }, { 1, 0, XK_Shift_L, XK_8, "*" }, { 1, 2, 0, XK_9, "9" }, { 1, 0, XK_Shift_L, XK_9, "(" }, { 1, 2, 0, XK_0, "0" }, { 1, 0, XK_Shift_L, XK_0, ")" }, { 2, 3, 0, XK_Tab, "\xe2\x86\xb9" }, { 2, 2, 0, XK_grave, "`" }, { 2, 0, XK_Shift_L, XK_grave, "~" }, { 2, 2, 0, XK_minus, "-" }, { 2, 0, XK_Shift_L, XK_minus, "_" }, { 2, 2, 0, XK_equal, "=" }, { 2, 0, XK_Shift_L, XK_equal, "+" }, { 2, 2, 0, XK_backslash, "\\" }, { 2, 0, XK_Shift_L, XK_backslash, "|" }, { 2, 2, 0, XK_bracketleft, "[" }, { 2, 0, XK_Shift_L, XK_bracketleft, "{" }, { 2, 2, 0, XK_bracketright, "]" }, { 2, 0, XK_Shift_L, XK_bracketright, "}" }, { 2, 2, 0, XK_semicolon, ";" }, { 2, 0, XK_Shift_L, XK_semicolon, ":" }, { 2, 2, 0, XK_apostrophe, "'" }, { 2, 0, XK_Shift_L, XK_apostrophe, "\"" }, { 3, 3, 0, 0, NULL }, { 3, 2, 0, XK_Shift_L, "\xe2\x87\xa7" }, { 3, 3, 0, XK_space, " " }, { 3, 0, XK_Shift_L, XK_space, " " }, { 3, 2, 0, XK_comma, "," }, { 3, 0, XK_Shift_L, XK_comma, "<" }, { 3, 2, 0, XK_period, "." }, { 2, 0, XK_Shift_L, XK_period, ">" }, { 3, 2, 0, XK_slash, "/" }, { 3, 0, XK_Shift_L, XK_slash, "?" }, { 3, 3, 0, XK_Return, "\xe2\x86\xb2" }, { 3, 3, 0, XK_BackSpace, "\xe2\x8c\xab" }, { 0, 0, 0, 0, NULL } }; static KeyboardLayoutDefinition _keyboard_layout[KLS_COUNT] = { { "Abc", _keyboard_layout_letters }, { "123", _keyboard_layout_keypad }, { ",./", _keyboard_layout_special } }; /* prototypes */ static GtkWidget * _keyboard_add_layout(Keyboard * keyboard, KeyboardLayoutDefinition * definitions, size_t definitions_cnt, KeyboardLayoutSection section); /* public */ /* functions */ /* keyboard_new */ Keyboard * keyboard_new(KeyboardPrefs * prefs) { Keyboard * keyboard; GdkScreen * screen; GtkWidget * vbox; GtkWidget * widget; PangoFontDescription * bold; GdkColor gray = { 0x90909090, 0x9090, 0x9090, 0x9090 }; #ifdef DEBUG fprintf(stderr, "DEBUG: %s()\n", __func__); #endif if((keyboard = malloc(sizeof(*keyboard))) == NULL) return NULL; keyboard->embedded = prefs->embedded; keyboard->layouts = NULL; keyboard->layouts_cnt = 0; screen = gdk_screen_get_default(); if(prefs != NULL && prefs->monitor > 0 && prefs->monitor < gdk_screen_get_n_monitors(screen)) gdk_screen_get_monitor_geometry(screen, prefs->monitor, &keyboard->geometry); else gdk_screen_get_monitor_geometry(screen, 0, &keyboard->geometry); /* window */ if(prefs->embedded != 0) { keyboard->window = gtk_plug_new(0); keyboard->width = 0; keyboard->height = 0; keyboard->x = 0; keyboard->y = 0; g_signal_connect_swapped(G_OBJECT(keyboard->window), "embedded", G_CALLBACK(on_keyboard_embedded), keyboard); } else { keyboard->window = gtk_window_new(GTK_WINDOW_POPUP); gtk_container_set_border_width(GTK_CONTAINER(keyboard->window), 4); gtk_window_set_accept_focus(GTK_WINDOW(keyboard->window), FALSE); gtk_window_set_focus_on_map(GTK_WINDOW(keyboard->window), FALSE); keyboard->width = keyboard->geometry.width; keyboard->height = (keyboard->geometry.width / 11) * 3; keyboard->x = keyboard->geometry.x; keyboard->y = keyboard->geometry.y + keyboard->geometry.height - keyboard->height; gtk_window_move(GTK_WINDOW(keyboard->window), keyboard->x, keyboard->y); gtk_widget_set_size_request(keyboard->window, keyboard->width, keyboard->height); g_signal_connect_swapped(G_OBJECT(keyboard->window), "delete-event", G_CALLBACK(on_keyboard_delete_event), keyboard); } gtk_widget_modify_bg(keyboard->window, GTK_STATE_NORMAL, &gray); /* fonts */ if(prefs->font != NULL) keyboard->font = pango_font_description_from_string( prefs->font); else { keyboard->font = pango_font_description_new(); pango_font_description_set_weight(keyboard->font, PANGO_WEIGHT_BOLD); } bold = pango_font_description_new(); pango_font_description_set_weight(bold, PANGO_WEIGHT_BOLD); /* layouts */ vbox = gtk_vbox_new(TRUE, 4); gtk_widget_show(vbox); if((widget = _keyboard_add_layout(keyboard, _keyboard_layout, KLS_COUNT, KLS_LETTERS)) != NULL) gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 0); if((widget = _keyboard_add_layout(keyboard, _keyboard_layout, KLS_COUNT, KLS_KEYPAD)) != NULL) gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 0); if((widget = _keyboard_add_layout(keyboard, _keyboard_layout, KLS_COUNT, KLS_SPECIAL)) != NULL) gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 0); gtk_container_add(GTK_CONTAINER(keyboard->window), vbox); if(prefs->embedded == 0) gtk_widget_show(keyboard->window); else { printf("%u\n", gtk_plug_get_id(GTK_PLUG(keyboard->window))); fclose(stdout); } keyboard_set_layout(keyboard, KLS_LETTERS); pango_font_description_free(bold); /* messages */ desktop_message_register(KEYBOARD_CLIENT_MESSAGE, on_keyboard_message, keyboard); return keyboard; } /* keyboard_delete */ void keyboard_delete(Keyboard * keyboard) { #ifdef DEBUG fprintf(stderr, "DEBUG: %s()\n", __func__); #endif gtk_widget_destroy(keyboard->window); pango_font_description_free(keyboard->font); free(keyboard); } /* accessors */ /* keyboard_set_layout */ void keyboard_set_layout(Keyboard * keyboard, unsigned int which) { size_t i; GtkWidget * widget; for(i = 0; i < keyboard->layouts_cnt; i++) if((widget = keyboard_layout_get_widget(keyboard->layouts[i])) == NULL) continue; else if(i == which) gtk_widget_show_all(widget); else gtk_widget_hide(widget); } /* keyboard_set_modifier */ void keyboard_set_modifier(Keyboard * keyboard, unsigned int modifier) { size_t i; for(i = 0; i < keyboard->layouts_cnt; i++) keyboard_layout_apply_modifier(keyboard->layouts[i], modifier); } /* keyboard_set_page */ void keyboard_set_page(Keyboard * keyboard, KeyboardPage page) { /* FIXME really implement */ switch(page) { case KEYBOARD_PAGE_DEFAULT: keyboard_set_layout(keyboard, 0); break; case KEYBOARD_PAGE_KEYPAD: keyboard_set_layout(keyboard, 1); break; } } /* useful */ /* keyboard_show */ void keyboard_show(Keyboard * keyboard, gboolean show) { #ifdef DEBUG fprintf(stderr, "DEBUG: %s(%s)\n", __func__, show ? "TRUE" : "FALSE"); #endif if(show == TRUE) { gtk_window_get_size(GTK_WINDOW(keyboard->window), &keyboard->width, &keyboard->height); gtk_widget_show(keyboard->window); gtk_window_get_position(GTK_WINDOW(keyboard->window), &keyboard->x, &keyboard->y); #ifdef DEBUG fprintf(stderr, "DEBUG: %s() width=%d, height=%d\n", __func__, keyboard->width, keyboard->height); fprintf(stderr, "DEBUG: %s() x=%d, y=%d\n", __func__, keyboard->x, keyboard->y); #endif } else if(keyboard->embedded == 0) gtk_widget_hide(keyboard->window); } /* private */ /* keyboard_add_layout */ static void _layout_clicked(GtkWidget * widget, gpointer data); static GtkWidget * _keyboard_add_layout(Keyboard * keyboard, KeyboardLayoutDefinition * definitions, size_t definitions_cnt, KeyboardLayoutSection section) { KeyboardLayout ** p; KeyboardLayout * layout; KeyboardKeyDefinition const * keys; size_t i; KeyboardKey * key; GtkWidget * label; GtkWidget * widget; unsigned long l; GdkColor black = { 0x00000000, 0x0000, 0x0000, 0x0000 }; GdkColor white = { 0xffffffff, 0xffff, 0xffff, 0xffff }; GdkColor gray = { 0xd0d0d0d0, 0xd0d0, 0xd0d0, 0xd0d0 }; if((p = realloc(keyboard->layouts, sizeof(*p) * (keyboard->layouts_cnt + 1))) == NULL) return NULL; keyboard->layouts = p; if((layout = keyboard_layout_new()) == NULL) return NULL; keyboard->layouts[keyboard->layouts_cnt++] = layout; keys = definitions[section].keys; for(i = 0; keys[i].width != 0; i++) { key = keyboard_layout_add(layout, keys[i].row, keys[i].width, keys[i].keysym, keys[i].label); if(key == NULL) continue; keyboard_key_set_background(key, &gray); keyboard_key_set_foreground(key, &black); keyboard_key_set_font(key, keyboard->font); for(; keys[i + 1].width == 0 && keys[i + 1].modifier != 0; i++) { keyboard_key_set_background(key, &white); keyboard_key_set_modifier(key, keys[i + 1].modifier, keys[i + 1].keysym, keys[i + 1].label); } } l = (section + 1) % definitions_cnt; label = gtk_label_new(definitions[l].label); gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &black); gtk_widget_modify_font(label, keyboard->font); widget = gtk_button_new(); gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, &white); gtk_container_add(GTK_CONTAINER(widget), label); g_object_set_data(G_OBJECT(widget), "layout", (void *)l); g_signal_connect(widget, "clicked", G_CALLBACK(_layout_clicked), keyboard); keyboard_layout_add_widget(layout, 3, 0, 3, widget); return keyboard_layout_get_widget(layout); } static void _layout_clicked(GtkWidget * widget, gpointer data) { Keyboard * keyboard = data; unsigned long d; KeyboardLayoutSection section; d = (unsigned long)g_object_get_data(G_OBJECT(widget), "layout"); section = d; switch(section) { case KLS_LETTERS: case KLS_KEYPAD: case KLS_SPECIAL: keyboard_set_layout(keyboard, section); break; } }