/* * xvkbd - Virtual Keyboard for X Window System * (Version 3.2, 2010-03-14) * * Copyright (C) 2000-2010 by Tom Sato * http://homepage3.nifty.com/tsato/xvkbd/ * * 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; either version 2 * of the License, or any later version. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* to get request code */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_I18N # include #endif #ifdef USE_XTEST # include #endif #include "resources.h" #define PROGRAM_NAME_WITH_VERSION "xvkbd (v3.2)" /* * Default keyboard layout is hardcoded here. * Layout of the main keyboard can be redefined by resources. */ #define NUM_KEY_ROWS 25 #define NUM_KEY_COLS 25 static char *keys_normal[NUM_KEY_ROWS][NUM_KEY_COLS] = { { "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "BackSpace" }, { "Escape", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\\", "`" }, { "Tab", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "Delete" }, { "Control_L", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "Return" }, { "Shift_L", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "Multi_key", "Shift_R" }, { "MainMenu", "Caps_Lock", "Alt_L", "Meta_L", "space", "Meta_R", "Alt_R", "Left", "Right", "Up", "Down", "Focus" }, }; static char *keys_shift[NUM_KEY_ROWS][NUM_KEY_COLS] = { { "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "BackSpace" }, { "Escape", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "|", "~" }, { "Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{", "}", "Delete" }, { "Control_L", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "Return" }, { "Shift_L", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "Multi_key", "Shift_R" }, { "MainMenu", "Caps_Lock", "Alt_L", "Meta_L", "space", "Meta_R", "Alt_R", "Left", "Right", "Up", "Down", "Focus" }, }; static char *keys_altgr[NUM_KEY_ROWS][NUM_KEY_COLS] = { { NULL } }; static char *keys_shift_altgr[NUM_KEY_ROWS][NUM_KEY_COLS] = { { NULL } }; static char *key_labels[NUM_KEY_ROWS][NUM_KEY_COLS] = { { "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "Backspace" }, { "Esc", "!\n1", "@\n2", "#\n3", "$\n4", "%\n5", "^\n6", "&\n7", "*\n8", "(\n9", ")\n0", "_\n-", "+\n=", "|\n\\", "~\n`" }, { "Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{\n[", "}\n]", "Del" }, { "Control", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":\n;", "\"\n'", "Return" }, { "Shift", "Z", "X", "C", "V", "B", "N", "M", "<\n,", ">\n.", "?\n/", "Com\npose", "Shift" }, { "MainMenu", "Caps\nLock", "Alt", "Meta", "", "Meta", "Alt", "left", "right", "up", "down", "Focus" }, }; static char *normal_key_labels[NUM_KEY_ROWS][NUM_KEY_COLS] = { { "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "Backspace" }, { "Esc", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\\", "`" }, { "Tab", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "Del" }, { "Ctrl", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "Return" }, { "Shift", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "Comp", "Shift" }, { "MainMenu", "Caps", "Alt", "Meta", "", "Meta", "Alt", "left", "right", "up", "down", "Focus" }, }; static char *shift_key_labels[NUM_KEY_ROWS][NUM_KEY_COLS] = { { "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "Backspace" }, { "Esc", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "|", "~" }, { "Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{", "}", "Del" }, { "Ctrl", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "Return" }, { "Shift", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "Comp", "Shift" }, { "MainMenu", "Caps", "Alt", "Meta", "", "Meta", "Alt", "left", "right", "up", "down", "Focus" }, }; static char *altgr_key_labels[NUM_KEY_ROWS][NUM_KEY_COLS] = { { NULL } }; static char *shift_altgr_key_labels[NUM_KEY_ROWS][NUM_KEY_COLS] = { { NULL } }; #define NUM_KEYPAD_ROWS NUM_KEY_ROWS #define NUM_KEYPAD_COLS NUM_KEY_COLS static char *keypad[NUM_KEYPAD_ROWS][NUM_KEYPAD_COLS] = { { "Num_Lock", "KP_Divide", "KP_Multiply", "Focus" }, { "Home", "Up", "Page_Up", "KP_Add" }, { "Left", "5", "Right", "KP_Subtract" }, { "End", "Down", "Page_Down", "KP_Enter" }, { "Insert", "Delete" }, }; static char *keypad_shift[NUM_KEYPAD_ROWS][NUM_KEYPAD_COLS] = { { "Num_Lock", "KP_Divide", "KP_Multiply", "Focus" }, { "KP_7", "KP_8", "KP_9", "KP_Add" }, { "KP_4", "KP_5", "KP_6", "KP_Subtract" }, { "KP_1", "KP_2", "KP_3", "KP_Enter" }, { "KP_0", "." }, }; static char *keypad_label[NUM_KEYPAD_ROWS][NUM_KEYPAD_COLS] = { { "Num\nLock", "/", "*", "Focus" }, { "7\nHome", "8\nUp ", "9\nPgUp", "+" }, { "4\nLeft", "5\n ", "6\nRight", "-" }, { "1\nEnd ", "2\nDown", "3\nPgDn", "Enter" }, { "0\nIns ", ".\nDel " }, }; #define NUM_SUN_FKEY_ROWS 6 #define NUM_SUN_FKEY_COLS 3 static char *sun_fkey[NUM_SUN_FKEY_ROWS][NUM_SUN_FKEY_COLS] = { { "L1", "L2" }, { "L3", "L4" }, { "L5", "L6" }, { "L7", "L8" }, { "L9", "L10" }, { "Help" }, }; static char *sun_fkey_label[NUM_SUN_FKEY_ROWS][NUM_SUN_FKEY_COLS] = { { "Stop \nL1", "Again\nL2" }, { "Props\nL3", "Undo \nL4" }, { "Front\nL5", "Copy \nL6" }, { "Open \nL7", "Paste\nL8" }, { "Find \nL9", "Cut \nL10" }, { "Help" }, }; /* * Image for arrow keys */ #define up_width 7 #define up_height 13 static unsigned char up_bits[] = { 0x08, 0x1c, 0x1c, 0x3e, 0x2a, 0x49, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}; #define down_width 7 #define down_height 13 static unsigned char down_bits[] = { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x49, 0x2a, 0x3e, 0x1c, 0x1c, 0x08}; #define left_width 13 #define left_height 13 static unsigned char left_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x18, 0x00, 0x0e, 0x00, 0xff, 0x1f, 0x0e, 0x00, 0x18, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; #define right_width 13 #define right_height 13 static unsigned char right_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x03, 0x00, 0x0e, 0xff, 0x1f, 0x00, 0x0e, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; #define check_width 16 #define check_height 16 static unsigned char check_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1e, 0x08, 0x0f, 0x8c, 0x07, 0xde, 0x03, 0xfe, 0x03, 0xfc, 0x01, 0xf8, 0x00, 0xf0, 0x00, 0x70, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00}; #define back_width 18 #define back_height 13 static unsigned char back_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x78, 0x00, 0x00, 0xfe, 0xff, 0x03, 0xff, 0xff, 0x03, 0xfe, 0xff, 0x03, 0x78, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; /* * Resources and options */ #define Offset(entry) XtOffset(struct appres_struct *, entry) static XtResource application_resources[] = { { "description", "Description", XtRString, sizeof(char *), Offset(description), XtRImmediate, PROGRAM_NAME_WITH_VERSION " - virtual keyboard for X window system\n\n" "Copyright (C) 2000-2010 by Tom Sato \n" "http://homepage3.nifty.com/tsato/xvkbd/\n\n" "This program is free software with ABSOLUTELY NO WARRANTY,\n" "distributed under the terms of the GNU General Public License.\n" }, { "showManualCommand", "ShowManualCommand", XtRString, sizeof(char *), Offset(show_manual_command), XtRImmediate, "xterm -e man xvkbd &" }, { "windowGeometry", "Geometry", XtRString, sizeof(char *), Offset(geometry), XtRImmediate, "" }, { "inheritGeoemetry", "Inherit", XtRBoolean, sizeof(Boolean), Offset(inherit_geometry), XtRImmediate, (XtPointer)TRUE }, { "debug", "Debug", XtRBoolean, sizeof(Boolean), Offset(debug), XtRImmediate, (XtPointer)FALSE }, { "version", "Version", XtRBoolean, sizeof(Boolean), Offset(version), XtRImmediate, (XtPointer)FALSE }, #ifdef USE_XTEST { "xtest", "XTest", XtRBoolean, sizeof(Boolean), Offset(xtest), XtRImmediate, (XtPointer)TRUE }, #else { "xtest", "XTest", XtRBoolean, sizeof(Boolean), Offset(xtest), XtRImmediate, (XtPointer)FALSE }, #endif { "noSync", "NoSync", XtRBoolean, sizeof(Boolean), Offset(no_sync), XtRImmediate, (XtPointer)FALSE }, { "alwaysOnTop", "AlwaysOnTop", XtRBoolean, sizeof(Boolean), Offset(always_on_top), XtRImmediate, (XtPointer)FALSE }, { "wmToolbar", "WmToolbar", XtRBoolean, sizeof(Boolean), Offset(wm_toolbar), XtRImmediate, (XtPointer)FALSE }, { "jumpPointer", "JumpPointer", XtRBoolean, sizeof(Boolean), Offset(jump_pointer), XtRImmediate, (XtPointer)TRUE }, { "jumpPointerAlways", "JumpPointer", XtRBoolean, sizeof(Boolean), Offset(jump_pointer_always), XtRImmediate, (XtPointer)TRUE }, { "jumpPointerBack", "JumpPointer", XtRBoolean, sizeof(Boolean), Offset(jump_pointer_back), XtRImmediate, (XtPointer)TRUE }, { "quickModifiers", "QuickModifiers", XtRBoolean, sizeof(Boolean), Offset(quick_modifiers), XtRImmediate, (XtPointer)TRUE }, { "altgrLock", "ModifiersLock", XtRBoolean, sizeof(Boolean), Offset(altgr_lock), XtRImmediate, (XtPointer)FALSE }, { "shiftLock", "ModifiersLock", XtRBoolean, sizeof(Boolean), Offset(shift_lock), XtRImmediate, (XtPointer)FALSE }, { "modifiersLock", "ModifiersLock", XtRBoolean, sizeof(Boolean), Offset(modifiers_lock), XtRImmediate, (XtPointer)FALSE }, { "numLockState", "NumLockState", XtRBoolean, sizeof(Boolean), Offset(num_lock_state), XtRImmediate, (XtPointer)TRUE }, { "autoRepeat", "AutoRepeat", XtRBoolean, sizeof(Boolean), Offset(auto_repeat), XtRImmediate, (XtPointer)TRUE }, { "modalKeytop", "ModalKeytop", XtRBoolean, sizeof(Boolean), Offset(modal_keytop), XtRImmediate, (XtPointer)FALSE }, { "minimizable", "Minimizable", XtRBoolean, sizeof(Boolean), Offset(minimizable), XtRImmediate, (XtPointer)FALSE }, { "secure", "Secure", XtRBoolean, sizeof(Boolean), Offset(secure), XtRImmediate, (XtPointer)FALSE }, { "no_root", "NoRoot", XtRBoolean, sizeof(Boolean), Offset(no_root), XtRImmediate, (XtPointer)FALSE }, { "wait_idle", "Text", XtRString, sizeof(char *), Offset(wait_idle), XtRImmediate, "" }, { "nonexitable", "Secure", XtRBoolean, sizeof(Boolean), Offset(nonexitable), XtRImmediate, (XtPointer)FALSE }, { "modalKeytop", "ModalKeytop", XtRBoolean, sizeof(Boolean), Offset(modal_keytop), XtRImmediate, (XtPointer)FALSE }, { "modalThreshold", "ModalThreshold", XtRInt, sizeof(int), Offset(modal_threshold), XtRImmediate, (XtPointer)150 }, { "keypad", "Keypad", XtRBoolean, sizeof(Boolean), Offset(keypad), XtRImmediate, (XtPointer)TRUE }, { "functionkey", "FunctionKey", XtRBoolean, sizeof(Boolean), Offset(function_key), XtRImmediate, (XtPointer)TRUE }, { "compact", "Compact", XtRBoolean, sizeof(Boolean), Offset(compact), XtRImmediate, (XtPointer)FALSE }, { "keypadOnly", "KeypadOnly", XtRBoolean, sizeof(Boolean), Offset(keypad_only), XtRImmediate, (XtPointer)FALSE }, { "keypadKeysym", "KeypadKeysym", XtRBoolean, sizeof(Boolean), Offset(keypad_keysym), XtRImmediate, (XtPointer)FALSE }, { "autoAddKeysym", "AutoAddKeysym", XtRBoolean, sizeof(Boolean), Offset(auto_add_keysym), XtRImmediate, (XtPointer)TRUE }, { "listWidgets", "Debug", XtRBoolean, sizeof(Boolean), Offset(list_widgets), XtRImmediate, (XtPointer)FALSE }, { "positiveModifiers", "PositiveModifiers", XtRString, sizeof(char *), Offset(positive_modifiers), XtRImmediate, "" }, { "text", "Text", XtRString, sizeof(char *), Offset(text), XtRImmediate, "" }, { "file", "File", XtRString, sizeof(char *), Offset(file), XtRImmediate, "" }, { "window", "Window", XtRString, sizeof(char *), Offset(window), XtRImmediate, "" }, { "instance", "Instance", XtRString, sizeof(char *), Offset(instance), XtRImmediate, "" }, { "widget", "Widget", XtRString, sizeof(char *), Offset(widget), XtRImmediate, "" }, { "generalFont", XtCFont, XtRFontStruct, sizeof(XFontStruct *), Offset(general_font), XtRString, XtDefaultFont}, { "letterFont", XtCFont, XtRFontStruct, sizeof(XFontStruct *), Offset(letter_font), XtRString, XtDefaultFont}, { "specialFont", XtCFont, XtRFontStruct, sizeof(XFontStruct *), Offset(special_font), XtRString, XtDefaultFont}, { "keypadFont", XtCFont, XtRFontStruct, sizeof(XFontStruct *), Offset(keypad_font), XtRString, XtDefaultFont}, { "generalBackground", XtCBackground, XtRPixel, sizeof(Pixel), Offset(general_background), XtRString, "gray" }, { "specialBackground", XtCBackground, XtRPixel, sizeof(Pixel), Offset(special_background), XtRString, "gray" }, { "specialForeground", XtCForeground, XtRPixel, sizeof(Pixel), Offset(special_foreground), XtRString, "black" }, #ifdef USE_I18N { "specialFontSet", XtCFontSet, XtRFontSet, sizeof(XFontSet), Offset(special_fontset), XtRString, XtDefaultFontSet}, #endif { "highlightBackground", XtCBackground, XtRPixel, sizeof(Pixel), Offset(highlight_background), XtRString, "gray" }, { "highlightForeground", XtCForeground, XtRPixel, sizeof(Pixel), Offset(highlight_foreground), XtRString, "forestgreen" }, { "focusBackground", XtCBackground, XtRPixel, sizeof(Pixel), Offset(focus_background), XtRString, "gray" }, { "remoteFocusBackground", XtCBackground, XtRPixel, sizeof(Pixel), Offset(remote_focus_background), XtRString, "cyan" }, { "balloonBackground", XtCBackground, XtRPixel, sizeof(Pixel), Offset(balloon_background), XtRString, "LightYellow1" }, { "launchBalloonBackground", XtCBackground, XtRPixel, sizeof(Pixel), Offset(launch_balloon_background), XtRString, "SkyBlue1" }, { "normalkeys", "NormalKeys", XtRString, sizeof(char *), Offset(keys_normal), XtRImmediate, "" }, { "shiftkeys", "ShiftKeys", XtRString, sizeof(char *), Offset(keys_shift), XtRImmediate, "" }, { "altgrkeys", "AltgrKeys", XtRString, sizeof(char *), Offset(keys_altgr), XtRImmediate, "" }, { "shiftaltgrkeys", "ShiftAltgrKeys", XtRString, sizeof(char *), Offset(keys_shift_altgr), XtRImmediate, "" }, { "keylabels", "KeyLabels", XtRString, sizeof(char *), Offset(key_labels), XtRImmediate, "" }, { "normalkeylabels", "NormalKeyLabels", XtRString, sizeof(char *), Offset(normal_key_labels), XtRImmediate, "" }, { "shiftkeylabels", "ShiftKeyLabels", XtRString, sizeof(char *), Offset(shift_key_labels), XtRImmediate, "" }, { "altgrkeylabels", "AltgrKeyLabels", XtRString, sizeof(char *), Offset(altgr_key_labels), XtRImmediate, "" }, { "shiftaltgrkeylabels", "ShiftAltgrKeyLabels", XtRString, sizeof(char *), Offset(shift_altgr_key_labels), XtRImmediate, "" }, { "normalkeypad", "NormalKeypad", XtRString, sizeof(char *), Offset(keypad_normal), XtRImmediate, "" }, { "shiftkeypad", "ShiftKeypad", XtRString, sizeof(char *), Offset(keypad_shift), XtRImmediate, "" }, { "keypad_labels", "KeypadLabels", XtRString, sizeof(char *), Offset(keypad_labels), XtRImmediate, "" }, { "deadkeys", "DeadKeys", XtRString, sizeof(char *), Offset(deadkeys), XtRImmediate, "" }, { "altgrKeycode", "AltgrKeycode", XtRInt, sizeof(int), Offset(altgr_keycode), XtRImmediate, (XtPointer)0 }, { "keyFile", "KeyFile", XtRString, sizeof(char *), Offset(key_file), XtRImmediate, ".xvkbd" }, { "dictFile", "DictFile", XtRString, sizeof(char *), Offset(dict_file), XtRImmediate, "/usr/share/dict/words" }, { "customizations", "Customizations", XtRString, sizeof(char *), Offset(customizations), XtRImmediate, "default" }, { "editableFunctionKeys", "EditableFunctionKeys", XtRInt, sizeof(int), Offset(editable_function_keys), XtRImmediate, (XtPointer)12 }, { "maxWidthRatio", "MaxRatio", XtRFloat, sizeof(float), Offset(max_width_ratio), XtRString, "0.9" }, { "maxHeightRatio", "MaxRatio", XtRFloat, sizeof(float), Offset(max_height_ratio), XtRString, "0.5" }, { "textDelay", "TextDelay", XtRInt, sizeof(int), Offset(text_delay), XtRImmediate, (XtPointer)0 }, { "keyClickPitch", "KeyClickPitch", XtRInt, sizeof(int), Offset(key_click_pitch), XtRImmediate, (XtPointer)1000 }, { "keyClickDuration", "KeyClickDuration", XtRInt, sizeof(int), Offset(key_click_duration), XtRImmediate, (XtPointer)1 }, { "autoClickDelay", "AutoClickDelay", XtRInt, sizeof(int), Offset(autoclick_delay), XtRImmediate, (XtPointer)0 }, }; #undef Offset static XrmOptionDescRec options[] = { { "-geometry", ".windowGeometry", XrmoptionSepArg, NULL }, { "-windowgeometry", ".windowGeometry", XrmoptionSepArg, NULL }, { "-debug", ".debug", XrmoptionNoArg, "True" }, #ifdef USE_XTEST { "-xtest", ".xtest", XrmoptionNoArg, "True" }, { "-xsendevent", ".xtest", XrmoptionNoArg, "False" }, { "-no-jump-pointer", ".jumpPointer", XrmoptionNoArg, "False" }, { "-no-back-pointer", ".jumpPointerBack", XrmoptionNoArg, "False" }, #endif { "-no-sync", ".noSync", XrmoptionNoArg, "True" }, { "-always-on-top", ".alwaysOnTop", XrmoptionNoArg, "True" }, /* EXPERIMENTAL */ { "-quick", ".quickModifiers", XrmoptionNoArg, "True" }, { "-modifiers", ".positiveModifiers", XrmoptionSepArg, NULL }, { "-text", ".text", XrmoptionSepArg, NULL }, { "-file", ".file", XrmoptionSepArg, NULL }, { "-delay", ".textDelay", XrmoptionSepArg, NULL }, { "-window", ".window", XrmoptionSepArg, NULL }, { "-instance", ".instance", XrmoptionSepArg, NULL }, { "-widget", ".widget", XrmoptionSepArg, NULL }, { "-altgr-lock", ".altgrLock", XrmoptionNoArg, "True" }, { "-no-altgr-lock", ".altgrLock", XrmoptionNoArg, "False" }, { "-no-repeat", ".autoRepeat", XrmoptionNoArg, "False" }, { "-norepeat", ".autoRepeat", XrmoptionNoArg, "False" }, { "-no-keypad", ".keypad", XrmoptionNoArg, "False" }, { "-nokeypad", ".keypad", XrmoptionNoArg, "False" }, { "-no-functionkey", ".functionkey", XrmoptionNoArg, "False" }, { "-nofunctionkey", ".functionkey", XrmoptionNoArg, "False" }, { "-highlight", ".highlightForeground", XrmoptionSepArg, NULL }, { "-compact", ".compact", XrmoptionNoArg, "True" }, { "-keypad", ".keypadOnly", XrmoptionNoArg, "True" }, { "-true-keypad", ".keypadKeysym", XrmoptionNoArg, "True" }, { "-truekeypad", ".keypadKeysym", XrmoptionNoArg, "True" }, { "-no-add-keysym", ".autoAddKeysym", XrmoptionNoArg, "False" }, { "-altgr-keycode", ".altgrKeycode", XrmoptionSepArg, NULL }, { "-list", ".listWidgets", XrmoptionNoArg, "True" }, { "-modal", ".modalKeytop", XrmoptionNoArg, "True" }, { "-minimizable", ".minimizable", XrmoptionNoArg, "True" }, { "-secure", ".secure", XrmoptionNoArg, "True" }, { "-no_root", ".no_root", XrmoptionNoArg, "True" }, { "-wait_idle", ".wait_idle", XrmoptionSepArg, NULL }, { "-nonexitable", ".nonexitable", XrmoptionNoArg, "True" }, { "-xdm", ".Secure", XrmoptionNoArg, "True" }, { "-dict", ".dictFile", XrmoptionSepArg, NULL }, { "-keyfile", ".keyFile", XrmoptionSepArg, NULL }, { "-customizations", ".customizations", XrmoptionSepArg, NULL }, { "-version", ".version", XrmoptionNoArg, "True" }, { "-help", ".version", XrmoptionNoArg, "True" }, }; /* * Global variables */ static char dict_filename[PATH_MAX] = ""; static int argc1; static char **argv1; static XtAppContext app_con; static Widget toplevel; static Widget key_widgets[NUM_KEY_ROWS][NUM_KEY_COLS]; static Widget main_menu = None; static Dimension toplevel_height = 1000; static Display *dpy; static Atom wm_delete_window = None; static KeySym *keysym_table = NULL; static int min_keycode, max_keycode; static int keysym_per_keycode; static Boolean error_detected; static int alt_mask = 0; static int meta_mask = 0; static int altgr_mask = 0; static KeySym altgr_keysym = NoSymbol; static int shift_state = 0; static int mouse_shift = 0; static Display *target_dpy = NULL; static Window toplevel_parent = None; static Window focused_window = None; static Window focused_subwindow = None; static Pixmap xvkbd_pixmap = None; static int AddKeysym(KeySym keysym, Boolean top); /* forward */ static void SendString(const unsigned char *str); static void MakeKeyboard(Boolean remake); static void MakeKeypad(Widget form, Widget from_vert, Widget from_horiz); static void MakeSunFunctionKey(Widget form, Widget from_vert, Widget from_horiz); static void MakeDeadkeyPanel(Widget form); static void RefreshMainMenu(void); static void PopupFunctionKeyEditor(void); static void DeleteWindowProc(Widget w, XEvent *event, String *pars, Cardinal *n_pars); /* * Search for window which has specified instance name (WM_NAME) * or class name (WM_CLASS). */ static Window FindWindow(Window top, char *name) { Window w; Window *children, dummy; unsigned int nchildren; int i; XClassHint hint; char *win_name; w = None; if (appres.debug) fprintf(stderr, "FindWindow: id=0x%lX", (long)top); if (XGetClassHint(target_dpy, top, &hint)) { if (hint.res_name) { if (appres.debug) fprintf(stderr, " instance=\"%s\"", hint.res_name); if ((strlen(name) > 0 && fnmatch(name, hint.res_name, 0) == 0) || (strlen(appres.instance) > 0 && fnmatch(appres.instance, hint.res_name, 0) == 0)) w = top; XFree(hint.res_name); } if (strlen(name) > 0 && hint.res_class) { if (appres.debug) fprintf(stderr, " class=\"%s\"", hint.res_class); if (strlen(name) > 0 && fnmatch(name, hint.res_class, 0) == 0) w = top; XFree(hint.res_class); } } if (XFetchName(target_dpy, top, &win_name)) { /* window title */ if (appres.debug) fprintf(stderr, " title=\"%s\"", win_name); if (strlen(name) > 0 && fnmatch(name, win_name, 0) == 0) w = top; XFree(win_name); } if (appres.debug) fprintf(stderr, "%s\n", (w == None) ? "" : " [matched]"); if (w == None && XQueryTree(target_dpy, top, &dummy, &dummy, &children, &nchildren)) { for (i = 0; i < nchildren; i++) { w = FindWindow(children[i], name); if (w != None) break; } if (children) XFree((char *)children); } return(w); } /* * This will be called to get window to set input focus, * when user pressed the "Focus" button. */ static void GetFocusedWindow(void) { Cursor cursor; XEvent event; Window target_root, child; int junk_i; unsigned junk_u; Window junk_w; int scrn; int cur_x, cur_y, last_x, last_y; double x_ratio, y_ratio; XFlush(target_dpy); target_root = RootWindow(target_dpy, DefaultScreen(target_dpy)); cursor = XCreateFontCursor(dpy, (target_dpy == dpy) ? XC_crosshair : XC_dot); if (XGrabPointer(dpy, RootWindow(dpy, DefaultScreen(dpy)), False, ButtonPressMask, GrabModeSync, GrabModeAsync, None, cursor, CurrentTime) == 0) { if (appres.debug) fprintf(stderr, "Grab pointer - waiting for button press\n"); last_x = -1; last_y = -1; x_ratio = ((double)WidthOfScreen(DefaultScreenOfDisplay(target_dpy)) / WidthOfScreen(XtScreen(toplevel))); y_ratio = ((double)HeightOfScreen(DefaultScreenOfDisplay(target_dpy)) / HeightOfScreen(XtScreen(toplevel))); do { XAllowEvents(dpy, SyncPointer, CurrentTime); if (target_dpy == dpy) { XNextEvent(dpy, &event); } else { XCheckTypedEvent(dpy, ButtonPress, &event); if (XQueryPointer(dpy, RootWindow(dpy, DefaultScreen(dpy)), &junk_w, &junk_w, &cur_x, &cur_y, &junk_i, &junk_i, &junk_u)) { cur_x = cur_x * x_ratio; cur_y = cur_y * y_ratio; } if (cur_x != last_x || cur_y != last_y) { if (appres.debug) fprintf(stderr, "Moving pointer to (%d, %d) on %s\n", cur_x, cur_y, XDisplayString(target_dpy)); XWarpPointer(target_dpy, None, target_root, 0, 0, 0, 0, cur_x, cur_y); XFlush(target_dpy); last_x = cur_x; last_y = cur_y; XQueryPointer(target_dpy, target_root, &junk_w, &child, &cur_x, &cur_y, &junk_i, &junk_i, &junk_u); usleep(10000); } else { usleep(100000); } } } while (event.type != ButtonPress); XUngrabPointer(dpy, CurrentTime); focused_window = None; if (target_dpy == dpy) focused_window = event.xbutton.subwindow; if (focused_window == None) { XFlush(target_dpy); for (scrn = 0; scrn < ScreenCount(target_dpy); scrn++) { if (XQueryPointer(target_dpy, RootWindow(target_dpy, scrn), &junk_w, &child, &junk_i, &junk_i, &junk_i, &junk_i, &junk_u)) { if (appres.debug) fprintf(stderr, "Window on the other display/screen (screen #%d of %s) focused\n", scrn, XDisplayString(target_dpy)); target_root = RootWindow(target_dpy, scrn); focused_window = child; break; } } } if (focused_window == None) focused_window = target_root; else focused_window = XmuClientWindow(target_dpy, focused_window); if (appres.debug) fprintf(stderr, "Selected window is: 0x%lX on %s\n", focused_window, XDisplayString(target_dpy)); if (target_dpy == dpy && XtWindow(toplevel) == focused_window) { focused_window = None; focused_subwindow = focused_window; return; } focused_subwindow = focused_window; do { /* search the child window */ XQueryPointer(target_dpy, focused_subwindow, &junk_w, &child, &junk_i, &junk_i, &junk_i, &junk_i, &junk_u); if (child != None) { focused_subwindow = child; if (appres.debug) fprintf(stderr, " going down: 0x%lX\n", focused_subwindow); } } while (child != None); if (appres.list_widgets || strlen(appres.widget) != 0) { child = FindWidget(toplevel, focused_window, appres.widget); if (child != None) focused_subwindow = child; } } else { fprintf(stderr, "%s: cannot grab pointer\n", PROGRAM_NAME); } } /* * Read keyboard mapping and modifier mapping. * Keyboard mapping is used to know what keys are in shifted position. * Modifier mapping is required because we should know Alt and Meta * key are used as which modifier. */ static void Highlight(char *name, Boolean state); static void ReadKeymap(void) { int i; int keycode, inx, pos; KeySym keysym; XModifierKeymap *modifiers; Widget w; int last_altgr_mask; XDisplayKeycodes(target_dpy, &min_keycode, &max_keycode); if (keysym_table != NULL) XFree(keysym_table); keysym_table = XGetKeyboardMapping(target_dpy, min_keycode, max_keycode - min_keycode + 1, &keysym_per_keycode); for (keycode = min_keycode; keycode <= max_keycode; keycode++) { /* if the first keysym is alphabet and the second keysym is NoSymbol, it is equivalent to pair of lowercase and uppercase alphabet */ inx = (keycode - min_keycode) * keysym_per_keycode; if (keysym_table[inx + 1] == NoSymbol && ((XK_A <= keysym_table[inx] && keysym_table[inx] <= XK_Z) || (XK_a <= keysym_table[inx] && keysym_table[inx] <= XK_z))) { if (XK_A <= keysym_table[inx] && keysym_table[inx] <= XK_Z) keysym_table[inx] = keysym_table[inx] - XK_A + XK_a; keysym_table[inx + 1] = keysym_table[inx] - XK_a + XK_A; } } last_altgr_mask = altgr_mask; alt_mask = 0; meta_mask = 0; altgr_mask = 0; altgr_keysym = NoSymbol; modifiers = XGetModifierMapping(target_dpy); for (i = 0; i < 8; i++) { for (pos = 0; pos < modifiers->max_keypermod; pos++) { keycode = modifiers->modifiermap[i * modifiers->max_keypermod + pos]; if (keycode < min_keycode || max_keycode < keycode) continue; keysym = keysym_table[(keycode - min_keycode) * keysym_per_keycode]; if (keysym == XK_Alt_L || keysym == XK_Alt_R) { alt_mask = 1 << i; } else if (keysym == XK_Meta_L || keysym == XK_Meta_R) { meta_mask = 1 << i; } else if (keysym == XK_Mode_switch) { if (appres.debug) fprintf(stderr, "%s: found Mode_switch at %dth modifier\n", PROGRAM_NAME, i); if (altgr_keysym == XK_ISO_Level3_Shift) { if (appres.debug) fprintf(stderr, "%s: both ISO_Level3_Shift and Mode_switch found - ISO_Level3_Shift prefered\n", PROGRAM_NAME); } else { altgr_mask = 0x0101 << i; /* I don't know why, but 0x2000 was required for mod3 on my Linux box */ altgr_keysym = keysym; } } else if (keysym == XK_ISO_Level3_Shift) { /* if no Mode_switch, try to use ISO_Level3_Shift instead */ /* however, it may not work as intended - I don't know why */ if (appres.debug) fprintf(stderr, "%s: found ISO_Level3_Shift at %dth modifier\n", PROGRAM_NAME, i); if (altgr_keysym == XK_Mode_switch) { if (appres.debug) fprintf(stderr, "%s: both ISO_Level3_Shift and Mode_switch found - ISO_Level3_Shift prefered\n", PROGRAM_NAME); } altgr_mask = 1 << i; altgr_keysym = keysym; } } } XFreeModifiermap(modifiers); if (altgr_keysym != XK_Mode_switch) { fprintf(stderr, "%s: Mode_switch not available as a modifier\n", PROGRAM_NAME); if (altgr_keysym == XK_ISO_Level3_Shift) fprintf(stderr, "%s: although ISO_Level3_Shift is used instead, AltGr may not work correctly\n", PROGRAM_NAME); else fprintf(stderr, "%s: AltGr can't be used\n", PROGRAM_NAME); } w = XtNameToWidget(toplevel, "*Multi_key"); if (w != None) { if (XKeysymToKeycode(target_dpy, XK_Multi_key) == NoSymbol) { if (!appres.auto_add_keysym || AddKeysym(XK_Multi_key, FALSE) == NoSymbol) XtSetSensitive(w, FALSE); } } w = XtNameToWidget(toplevel, "*Mode_switch"); if (w != None) { if (appres.xtest && 0 < appres.altgr_keycode) { XtSetSensitive(w, TRUE); if (appres.debug) fprintf(stderr, "%s: keycode %d will be used for AltGr - it was specified with altgrKeycode\n", PROGRAM_NAME, appres.altgr_keycode); } else if (altgr_mask) { XtSetSensitive(w, TRUE); } else { XtSetSensitive(w, FALSE); if (shift_state & last_altgr_mask) { shift_state &= ~last_altgr_mask; Highlight("Mode_switch", FALSE); } } } } /* * This will called when X error is detected when attempting to * send a event to a client window; this will normally caused * when the client window is destroyed. */ static int MyErrorHandler(Display *my_dpy, XErrorEvent *event) { char msg[200]; error_detected = TRUE; if (event->error_code == BadWindow) { if (appres.debug) fprintf(stderr, "%s: BadWindow - couldn't find target window 0x%lX (destroyed?)\n", PROGRAM_NAME, (long)focused_window); return 0; } XGetErrorText(my_dpy, event->error_code, msg, sizeof(msg) - 1); fprintf(stderr, "X error trapped: %s, request-code=%d\n", msg, event->request_code); return 0; } /* * Send event to the focused window. * If input focus is specified explicitly, select the window * before send event to the window. */ static void SendEvent(XKeyEvent *event) { static Boolean first = TRUE; if (!appres.no_sync) { XSync(event->display, FALSE); XSetErrorHandler(MyErrorHandler); } error_detected = FALSE; if (focused_window != None) { /* set input focus if input focus is set explicitly */ if (appres.debug) fprintf(stderr, "Set input focus to window 0x%lX (0x%lX)\n", (long)focused_window, (long)event->window); XSetInputFocus(event->display, focused_window, RevertToParent, CurrentTime); if (!appres.no_sync) XSync(event->display, FALSE); } if (!error_detected) { if (appres.xtest) { #ifdef USE_XTEST if (appres.debug) fprintf(stderr, "XTestFakeKeyEvent(0x%lx, %ld, %d)\n", (long)event->display, (long)event->keycode, event->type == KeyPress); if (appres.jump_pointer) { Window root, child, w; int root_x, root_y, x, y; unsigned int mask; int revert_to; w = None; if (first || strlen(appres.text) == 0 || appres.jump_pointer_back) { first = FALSE; w = focused_subwindow; if (w == None && appres.jump_pointer_always) XGetInputFocus(event->display, &w, &revert_to); if (w != None) { if (appres.debug) fprintf(stderr, "SendEvent: jump pointer to window 0x%lx\n", (long)w); XQueryPointer(event->display, w, &root, &child, &root_x, &root_y, &x, &y, &mask); XWarpPointer(event->display, None, w, 0, 0, 0, 0, 1, 1); XFlush(event->display); } } XTestFakeKeyEvent(event->display, event->keycode, event->type == KeyPress, 0); XFlush(event->display); if (w != None && appres.jump_pointer_back) { XWarpPointer(event->display, None, root, 0, 0, 0, 0, root_x, root_y); XFlush(event->display); } } else { XTestFakeKeyEvent(event->display, event->keycode, event->type == KeyPress, 0); XFlush(event->display); } #else fprintf(stderr, "%s: this binary is compiled without XTEST support\n", PROGRAM_NAME); #endif } else { XSendEvent(event->display, event->window, TRUE, KeyPressMask, (XEvent *)event); if (!appres.no_sync) XSync(event->display, FALSE); if (error_detected && (focused_subwindow != None) && (focused_subwindow != event->window)) { error_detected = FALSE; event->window = focused_subwindow; if (appres.debug) fprintf(stderr, " retry: send event to window 0x%lX (0x%lX)\n", (long)focused_window, (long)event->window); XSendEvent(event->display, event->window, TRUE, KeyPressMask, (XEvent *)event); if (!appres.no_sync) XSync(event->display, FALSE); } } } if (error_detected) { /* reset focus because focused window is (probably) no longer exist */ XBell(dpy, 0); focused_window = None; focused_subwindow = None; } XSetErrorHandler(NULL); } /* * Insert a specified keysym to unused position in the keymap table. * This will be called to add required keysyms on-the-fly. * if the second parameter is TRUE, the keysym will be added to the * non-shifted position - this may be required for modifier keys * (e.g. Mode_switch) and some special keys (e.g. F20). */ static int AddKeysym(KeySym keysym, Boolean top) { int keycode, pos, max_pos, inx, phase; if (top) { max_pos = 0; } else { max_pos = keysym_per_keycode - 1; if (4 <= max_pos) max_pos = 3; if (2 <= max_pos && altgr_keysym != XK_Mode_switch) max_pos = 1; } for (phase = 0; phase < 2; phase++) { for (keycode = max_keycode; min_keycode <= keycode; keycode--) { for (pos = max_pos; 0 <= pos; pos--) { inx = (keycode - min_keycode) * keysym_per_keycode; if ((phase != 0 || keysym_table[inx] == NoSymbol) && keysym_table[inx] < 0xFF00) { /* In the first phase, to avoid modifing existing keys, */ /* add the keysym only to the keys which has no keysym in the first position. */ /* If no place fuond in the first phase, add the keysym for any keys except */ /* for modifier keys and other special keys */ if (keysym_table[inx + pos] == NoSymbol) { if (appres.debug) fprintf(stderr, "*** Adding keysym \"%s\" at keycode %d position %d/%d\n", XKeysymToString(keysym), keycode, pos, keysym_per_keycode); keysym_table[inx + pos] = keysym; XChangeKeyboardMapping(target_dpy, keycode, keysym_per_keycode, &keysym_table[inx], 1); XFlush(target_dpy); return keycode; } } } } } fprintf(stderr, "%s: couldn't add \"%s\" to keymap\n", PROGRAM_NAME, XKeysymToString(keysym)); XBell(dpy, 0); return NoSymbol; } /* * Add the specified key as a new modifier. * This is used to use Mode_switch (AltGr) as a modifier. */ static void AddModifier(KeySym keysym) { XModifierKeymap *modifiers; int keycode, i, pos; keycode = XKeysymToKeycode(target_dpy, keysym); if (keycode == NoSymbol) keycode = AddKeysym(keysym, TRUE); modifiers = XGetModifierMapping(target_dpy); for (i = 7; 3 < i; i--) { if (modifiers->modifiermap[i * modifiers->max_keypermod] == NoSymbol || ((keysym_table[(modifiers->modifiermap[i * modifiers->max_keypermod] - min_keycode) * keysym_per_keycode]) == XK_ISO_Level3_Shift && keysym == XK_Mode_switch)) { for (pos = 0; pos < modifiers->max_keypermod; pos++) { if (modifiers->modifiermap[i * modifiers->max_keypermod + pos] == NoSymbol) { if (appres.debug) fprintf(stderr, "Adding modifier \"%s\" as %dth modifier\n", XKeysymToString(keysym), i); modifiers->modifiermap[i * modifiers->max_keypermod + pos] = keycode; XSetModifierMapping(target_dpy, modifiers); return; } } } } fprintf(stderr, "%s: couldn't add \"%s\" as modifier\n", PROGRAM_NAME, XKeysymToString(keysym)); XBell(dpy, 0); } /* * Send sequence of KeyPressed/KeyReleased events to the focused * window to simulate keyboard. If modifiers (shift, control, etc) * are set ON, many events will be sent. */ static void SendKeyPressedEvent(KeySym keysym, unsigned int shift) { Window cur_focus; int revert_to; XKeyEvent event; int keycode; Window root, *children; unsigned int n_children; int phase, inx; Boolean found; if (focused_subwindow != None) cur_focus = focused_subwindow; else XGetInputFocus(target_dpy, &cur_focus, &revert_to); if (appres.debug) { char ch = '?'; if ((keysym & ~0x7f) == 0 && isprint(keysym)) ch = keysym; fprintf(stderr, "SendKeyPressedEvent: focus=0x%lX, key=0x%lX (%c), shift=0x%lX\n", (long)cur_focus, (long)keysym, ch, (long)shift); } if (XtWindow(toplevel) != None) { if (toplevel_parent == None) { XQueryTree(target_dpy, RootWindow(target_dpy, DefaultScreen(target_dpy)), &root, &toplevel_parent, &children, &n_children); XFree(children); } if (cur_focus == None || cur_focus == PointerRoot || cur_focus == XtWindow(toplevel) || cur_focus == toplevel_parent) { /* notice user when no window focused or the xvkbd window is focused */ XBell(dpy, 0); return; } } found = FALSE; keycode = 0; if (keysym != NoSymbol) { for (phase = 0; phase < 2; phase++) { for (keycode = min_keycode; !found && (keycode <= max_keycode); keycode++) { /* Determine keycode for the keysym: we use this instead of XKeysymToKeycode() because we must know shift_state, too */ inx = (keycode - min_keycode) * keysym_per_keycode; if (keysym_table[inx] == keysym) { shift &= ~altgr_mask; if (keysym_table[inx + 1] != NoSymbol) shift &= ~ShiftMask; found = TRUE; break; } else if (keysym_table[inx + 1] == keysym) { shift &= ~altgr_mask; shift |= ShiftMask; found = TRUE; break; } } if (!found && altgr_mask && 3 <= keysym_per_keycode) { for (keycode = min_keycode; !found && (keycode <= max_keycode); keycode++) { inx = (keycode - min_keycode) * keysym_per_keycode; if (keysym_table[inx + 2] == keysym) { shift &= ~ShiftMask; shift |= altgr_mask; found = TRUE; break; } else if (4 <= keysym_per_keycode && keysym_table[inx + 3] == keysym) { shift |= ShiftMask | altgr_mask; found = TRUE; break; } } } if (found || !appres.auto_add_keysym) break; if (0xF000 <= keysym) { /* for special keys such as function keys, first try to add it in the non-shifted position of the keymap */ if (AddKeysym(keysym, TRUE) == NoSymbol) AddKeysym(keysym, FALSE); } else { AddKeysym(keysym, FALSE); } } if (appres.debug) { if (found) fprintf(stderr, "SendKeyPressedEvent: keysym=0x%lx, keycode=%ld, shift=0x%lX\n", (long)keysym, (long)keycode, (long)shift); else fprintf(stderr, "SendKeyPressedEvent: keysym=0x%lx - keycode not found\n", (long)keysym); } } event.display = target_dpy; event.window = cur_focus; event.root = RootWindow(event.display, DefaultScreen(event.display)); event.subwindow = None; event.time = CurrentTime; event.x = 1; event.y = 1; event.x_root = 1; event.y_root = 1; event.same_screen = TRUE; #ifdef USE_XTEST if (appres.xtest) { Window root, child; int root_x, root_y, x, y; unsigned int mask; XQueryPointer(target_dpy, event.root, &root, &child, &root_x, &root_y, &x, &y, &mask); event.type = KeyRelease; event.state = 0; if (mask & ControlMask) { event.keycode = XKeysymToKeycode(target_dpy, XK_Control_L); SendEvent(&event); } if (mask & alt_mask) { event.keycode = XKeysymToKeycode(target_dpy, XK_Alt_L); SendEvent(&event); } if (mask & meta_mask) { event.keycode = XKeysymToKeycode(target_dpy, XK_Meta_L); SendEvent(&event); } if (mask & altgr_mask) { if (0 < appres.altgr_keycode) event.keycode = appres.altgr_keycode; else event.keycode = XKeysymToKeycode(target_dpy, altgr_keysym); SendEvent(&event); } if (mask & ShiftMask) { event.keycode = XKeysymToKeycode(target_dpy, XK_Shift_L); SendEvent(&event); } if (mask & LockMask) { event.keycode = XKeysymToKeycode(target_dpy, XK_Caps_Lock); SendEvent(&event); } } #endif event.type = KeyPress; event.state = 0; if (shift & ControlMask) { event.keycode = XKeysymToKeycode(target_dpy, XK_Control_L); SendEvent(&event); event.state |= ControlMask; } if (shift & alt_mask) { event.keycode = XKeysymToKeycode(target_dpy, XK_Alt_L); SendEvent(&event); event.state |= alt_mask; } if (shift & meta_mask) { event.keycode = XKeysymToKeycode(target_dpy, XK_Meta_L); SendEvent(&event); event.state |= meta_mask; } if (shift & altgr_mask) { if (0 < appres.altgr_keycode) event.keycode = appres.altgr_keycode; else event.keycode = XKeysymToKeycode(target_dpy, altgr_keysym); SendEvent(&event); event.state |= altgr_mask; } if (shift & ShiftMask) { event.keycode = XKeysymToKeycode(target_dpy, XK_Shift_L); SendEvent(&event); event.state |= ShiftMask; } if (keysym != NoSymbol) { /* send event for the key itself */ event.keycode = found ? keycode : XKeysymToKeycode(target_dpy, keysym); if (event.keycode == NoSymbol) { if ((keysym & ~0x7f) == 0 && isprint(keysym)) fprintf(stderr, "%s: no such key: %c\n", PROGRAM_NAME, (char)keysym); else if (XKeysymToString(keysym) != NULL) fprintf(stderr, "%s: no such key: keysym=%s (0x%lX)\n", PROGRAM_NAME, XKeysymToString(keysym), (long)keysym); else fprintf(stderr, "%s: no such key: keysym=0x%lX\n", PROGRAM_NAME, (long)keysym); XBell(dpy, 0); } else { SendEvent(&event); event.type = KeyRelease; SendEvent(&event); } } event.type = KeyRelease; if (shift & ShiftMask) { event.keycode = XKeysymToKeycode(target_dpy, XK_Shift_L); SendEvent(&event); event.state &= ~ShiftMask; } if (shift & altgr_mask) { if (0 < appres.altgr_keycode) event.keycode = appres.altgr_keycode; else event.keycode = XKeysymToKeycode(target_dpy, altgr_keysym); SendEvent(&event); event.state &= ~altgr_mask; } if (shift & meta_mask) { event.keycode = XKeysymToKeycode(target_dpy, XK_Meta_L); SendEvent(&event); event.state &= ~meta_mask; } if (shift & alt_mask) { event.keycode = XKeysymToKeycode(target_dpy, XK_Alt_L); SendEvent(&event); event.state &= ~alt_mask; } if (shift & ControlMask) { event.keycode = XKeysymToKeycode(target_dpy, XK_Control_L); SendEvent(&event); event.state &= ~ControlMask; } if (appres.no_sync) XFlush(dpy); } /* * Word completion - list of words which match the prefix entered * via xvkbd can be listed, and choosing one of them will send the * suffix to the clients. * Words for completion will be read from dictionary file specified * with xvkbd.dictFile resource, such as /usr/dict/words. */ static Widget completion_panel = None; static Widget completion_entry = None; static Widget completion_list = None; static Widget props_dict_entry = None; static char completion_text[100] = ""; #define HASH_SIZE 100 #define Hash(str) ((toupper(str[0]) * 26 + toupper(str[1])) % HASH_SIZE) static struct WORDLIST { struct WORDLIST *next; char *word; } completion_words[HASH_SIZE]; static int n_completion_words = 0; #define MAX_WORDS 200 static String word_list[MAX_WORDS + 1]; static int n_word_list = 0; static void SetDefaultDictionary(void) { strncpy(dict_filename, appres.dict_file, sizeof(dict_filename)); XtVaSetValues(props_dict_entry, XtNstring, dict_filename, NULL); } static void ReadCompletionDictionary(void) { static Boolean first = TRUE; static char cur_dict_filename[PATH_MAX] = ""; FILE *fp; struct WORDLIST *node_ptr; char str[50]; int i; struct WORDLIST *p; if (strcmp(cur_dict_filename, dict_filename) == 0) return; strcpy(cur_dict_filename, dict_filename); if (!first) { int cnt = 0; for (i = 0; i < HASH_SIZE; i++) { while (completion_words[i].next != NULL) { p = completion_words[i].next; completion_words[i].next = p->next; free(p); cnt++; } } if (appres.debug) fprintf(stderr, "ReadCompletionDictionary: %d words freed\n", cnt); } first = FALSE; for (i = 0; i < HASH_SIZE; i++) { completion_words[i].next = NULL; completion_words[i].word = NULL; } n_completion_words = 0; fp = fopen(dict_filename, "r"); if (fp == NULL) { fprintf(stderr, "%s: can't read dictionary file %s: %s\n", PROGRAM_NAME, dict_filename, strerror(errno)); } else { while (fgets(str, sizeof(str) - 1, fp)) { if (3 < strlen(str)) { str[strlen(str) - 1] = '\0'; node_ptr = &completion_words[Hash(str)]; while (node_ptr->word != NULL) node_ptr = node_ptr->next; node_ptr->word = XtNewString(str); node_ptr->next = malloc(sizeof(struct WORDLIST)); node_ptr->next->next = NULL; node_ptr->next->word = NULL; n_completion_words++; } } fclose(fp); if (appres.debug) fprintf(stderr, "ReadCompletionDictionary: %d words allocated\n", n_completion_words); } } static void AddToCompletionText(KeySym keysym) { int len; struct WORDLIST *node_ptr; if (completion_entry != None) { if (n_completion_words == 0) { XtVaSetValues(completion_entry, XtNlabel, "Couldn't read dictionary file", NULL); return; } len = strlen(completion_text); if (keysym == XK_BackSpace || keysym == XK_Delete) { if (0 < len) completion_text[len - 1] = '\0'; } else if (keysym != NoSymbol && !ispunct((char)keysym) && !isspace((char)keysym)) { if (len < sizeof(completion_text) - 2) { completion_text[len + 1] = '\0'; completion_text[len] = keysym; } } else { completion_text[0] = '\0'; } XtVaSetValues(completion_entry, XtNlabel, completion_text, NULL); n_word_list = 0; if (2 <= strlen(completion_text)) { node_ptr = &completion_words[Hash(completion_text)]; while (node_ptr->word != NULL && n_word_list < MAX_WORDS) { if (strlen(completion_text) + 1 < strlen(node_ptr->word) && strncasecmp(node_ptr->word, completion_text, strlen(completion_text)) == 0) { word_list[n_word_list] = node_ptr->word; n_word_list = n_word_list + 1; } node_ptr = node_ptr->next; } } word_list[n_word_list] = NULL; XawListChange(completion_list, word_list, 0, 0, TRUE); } } static void CompletionWordSelected(Widget w, XtPointer client_data, XtPointer call_data) { Boolean capitalize; unsigned char ch; int n, i; n = ((XawListReturnStruct *)call_data)->list_index; if (0 <= n && n < n_word_list) { capitalize = TRUE; for (i = 0; completion_text[i] != '\0'; i++) { if (islower(completion_text[i])) capitalize = FALSE; } for (i = strlen(completion_text); word_list[n][i] != '\0'; i++) { ch = word_list[n][i]; if (capitalize) ch = toupper(ch); SendKeyPressedEvent(ch, 0); } } AddToCompletionText(NoSymbol); } static void PopupCompletionPanel(void) { Widget form, label, view; char msg[100]; if (completion_panel == None) { completion_panel = XtVaCreatePopupShell("completion_panel", transientShellWidgetClass, toplevel, NULL); form = XtVaCreateManagedWidget("form", formWidgetClass, completion_panel, NULL); label = XtVaCreateManagedWidget("label", labelWidgetClass, form, NULL); completion_entry = XtVaCreateManagedWidget("entry", labelWidgetClass, form, XtNfromHoriz, label, NULL); view = XtVaCreateManagedWidget("view", viewportWidgetClass, form, XtNfromVert, label, NULL); completion_list = XtVaCreateManagedWidget("list", listWidgetClass, view, NULL); XtAddCallback(completion_list, XtNcallback, CompletionWordSelected, NULL); XtRealizeWidget(completion_panel); XSetWMProtocols(dpy, XtWindow(completion_panel), &wm_delete_window, 1); XtPopup(completion_panel, XtGrabNone); AddToCompletionText(NoSymbol); XFlush(dpy); } else { XtPopup(completion_panel, XtGrabNone); } ReadCompletionDictionary(); sprintf(msg, "%d words in the dictionary", n_completion_words); XtVaSetValues(completion_entry, XtNlabel, msg, NULL); completion_text[0] = '\0'; n_word_list = 0; word_list[n_word_list] = NULL; XawListChange(completion_list, word_list, 0, 0, TRUE); } /* * Send given string to the focused window as if the string * is typed from a keyboard. */ static void KeyPressed(Widget w, char *key, char *data); static void SendString(const unsigned char *str) { const unsigned char *cp, *cp2; /* I remember "unsigned" might be required for some systems */ char key[50]; int len; int val; Window target_root, child, junk_w; int junk_i; unsigned junk_u; int cur_x, cur_y; shift_state = 0; for (cp = str; *cp != '\0'; cp++) { if (0 < appres.text_delay) usleep(appres.text_delay * 1000); if (appres.wait_idle && strlen(appres.wait_idle) > 0) { int pid = atoi(appres.wait_idle); int ret; do { char cmd[80]; snprintf(cmd, sizeof(cmd), "/proc/%d/status", pid); FILE *f = fopen(cmd, "r"); if (f == NULL) { fprintf(stderr, "Process not found: %d\n", pid); exit(-1); } fclose(f); snprintf(cmd, sizeof(cmd), "grep 'State.*running' /proc/%d/status", pid); ret = system(cmd); if (ret == 0) usleep(50); } while (ret == 0); } if (*cp == '\\') { cp++; switch (*cp) { case '\0': fprintf(stderr, "%s: missing character after \"\\\"\n", PROGRAM_NAME); return; case '[': /* we can write any keysym as "\[the-keysym]" here */ cp2 = strchr(cp, ']'); if (cp2 == NULL) { fprintf(stderr, "%s: no closing \"]\" after \"\\[\"\n", PROGRAM_NAME); } else { len = cp2 - cp - 1; if (sizeof(key) <= len) len = sizeof(key) - 1; strncpy(key, cp + 1, len); key[len] = '\0'; KeyPressed(None, key, NULL); cp = cp2; } break; case 'S': shift_state |= ShiftMask; break; case 'C': shift_state |= ControlMask; break; case 'A': shift_state |= alt_mask; break; case 'M': shift_state |= meta_mask; break; case 'b': SendKeyPressedEvent(XK_BackSpace, shift_state); shift_state = 0; break; case 't': SendKeyPressedEvent(XK_Tab, shift_state); shift_state = 0; break; case 'n': SendKeyPressedEvent(XK_Linefeed, shift_state); shift_state = 0; break; case 'r': SendKeyPressedEvent(XK_Return, shift_state); shift_state = 0; break; case 'e': SendKeyPressedEvent(XK_Escape, shift_state); shift_state = 0; break; case 'd': SendKeyPressedEvent(XK_Delete, shift_state); shift_state = 0; break; case 'D': /* delay */ cp++; if ('1' <= *cp && *cp <= '9') { usleep((*cp - '0') * 100000); } else { fprintf(stderr, "%s: no digit after \"\\m\"\n", PROGRAM_NAME); } break; case 'm': /* simulate click mouse button */ cp++; if ('1' <= *cp && *cp <= '9') { if (appres.debug) fprintf(stderr, "XTestFakeButtonEvent(%d)\n", *cp - '0'); XTestFakeButtonEvent(target_dpy, *cp - '0', True, CurrentTime); XTestFakeButtonEvent(target_dpy, *cp - '0', False, CurrentTime); XFlush(dpy); } else { fprintf(stderr, "%s: no digit after \"\\m\"\n", PROGRAM_NAME); } break; case 'x': case 'y': /* move mouse pointer */ sscanf(cp + 1, "%d", &val); target_root = RootWindow(target_dpy, DefaultScreen(target_dpy)); XQueryPointer(target_dpy, target_root, &junk_w, &child, &cur_x, &cur_y, &junk_i, &junk_i, &junk_u); if (*cp == 'x') { if (isdigit(*(cp + 1))) cur_x = val; else cur_x += val; } else { if (isdigit(*(cp + 1))) cur_y = val; else cur_y += val; } XWarpPointer(target_dpy, None, target_root, 0, 0, 0, 0, cur_x, cur_y); XFlush(dpy); cp++; while (isdigit(*(cp + 1)) || *(cp + 1) == '+' || *(cp + 1) == '-') cp++; break; default: SendKeyPressedEvent(*cp, shift_state); shift_state = 0; break; } } else { SendKeyPressedEvent(*cp, shift_state); shift_state = 0; } } } /* * Send content of the file as if the it is typed from a keyboard. */ static void SendFileContent(const char *file) { FILE *fp; int ch; fp = stdin; if (strcmp(file, "-") != 0) fp = fopen(file, "r"); if (fp == NULL) { fprintf(stderr, "%s: can't read the file: %s\n", PROGRAM_NAME, file); exit(1); } while ((ch = fgetc(fp)) != EOF) { if (0 < appres.text_delay) usleep(appres.text_delay * 1000); if (ch == '\n') { /* newline - send Return instead */ SendKeyPressedEvent(XK_Return, 0); } else if (ch == '\033') { /* ESC */ SendKeyPressedEvent(XK_Escape, 0); } else if (ch == '\177') { /* DEL */ SendKeyPressedEvent(XK_Delete, 0); } else if (1 <= ch && ch <= 26) { /* Ctrl-x */ SendKeyPressedEvent('a' + ch - 1, ControlMask); } else { /* normal characters */ SendKeyPressedEvent(ch, 0); } } if (strcmp(file, "-") != 0) fclose(fp); } /* * Highlight/unhighligh spcified modifier key on the screen. */ static void Highlight(char *name, Boolean state) { char name1[50]; Widget w; sprintf(name1, "*%s", name); w = XtNameToWidget(toplevel, name1); if (w != None) { if (strstr(name, "Focus") != NULL) { if (target_dpy == dpy) XtVaSetValues(w, XtNbackground, appres.focus_background, NULL); else XtVaSetValues(w, XtNbackground, appres.remote_focus_background, NULL); if (state) XtVaSetValues(w, XtNforeground, appres.highlight_foreground, NULL); else XtVaSetValues(w, XtNforeground, appres.special_foreground, NULL); } else { if (state) XtVaSetValues(w, XtNbackground, appres.highlight_background, XtNforeground, appres.highlight_foreground, NULL); else XtVaSetValues(w, XtNbackground, appres.special_background, XtNforeground, appres.special_foreground, NULL); } } } /* * Highlight/unhighligh keys on the screen to reflect the state. */ static void RefreshShiftState(Boolean force) { static Boolean first = TRUE; static int last_shift_state = 0; static int last_mouse_shift = 0; static int last_num_lock_state = FALSE; static Display *last_target_dpy = NULL; static long last_focus = 0; int cur_shift; int changed; int first_row, row, col; Boolean shifted; char *label; int mask; cur_shift = shift_state | mouse_shift; changed = cur_shift ^ (last_shift_state | last_mouse_shift); if (first || force) changed = 0xffff; if (changed & ShiftMask) { Highlight("Shift_L", cur_shift & ShiftMask); Highlight("Shift_R", cur_shift & ShiftMask); } if (changed & ControlMask) { Highlight("Control_L", cur_shift & ControlMask); Highlight("Control_R", cur_shift & ControlMask); } if (changed & alt_mask) { Highlight("Alt_L", cur_shift & alt_mask); Highlight("Alt_R", cur_shift & alt_mask); } if (changed & meta_mask) { Highlight("Meta_L", cur_shift & meta_mask); Highlight("Meta_R", cur_shift & meta_mask); } if (changed & LockMask) { Highlight("Caps_Lock", cur_shift & LockMask); } if (changed & altgr_mask) { Highlight("Mode_switch", cur_shift & altgr_mask); } if (last_num_lock_state != appres.num_lock_state) { Highlight("Num_Lock", appres.num_lock_state); Highlight("keypad_panel*Num_Lock", appres.num_lock_state); } if (last_target_dpy != target_dpy || last_focus != focused_window) { Highlight("Focus", focused_window != 0); Highlight("keypad*Focus", focused_window != 0); Highlight("keypad_panel*Focus", focused_window != 0); last_target_dpy = target_dpy; last_focus = focused_window; } mask = ShiftMask | LockMask | altgr_mask; changed = (shift_state & mask) ^ (last_shift_state & mask); if (first || force) changed = TRUE; if (changed && !appres.keypad_only && (appres.modal_keytop || toplevel_height < appres.modal_threshold)) { first_row = appres.function_key ? 0 : 1; for (row = first_row; row < NUM_KEY_ROWS; row++) { for (col = 0; col < NUM_KEY_COLS; col++) { shifted = (shift_state & ShiftMask); if (key_widgets[row][col] != None) { if ((shift_state & altgr_mask) && altgr_key_labels[row][col] != NULL) { if (shifted && shift_altgr_key_labels[row][col] != NULL) label = shift_altgr_key_labels[row][col]; else label = altgr_key_labels[row][col]; } else { if ((shift_state & LockMask) && isalpha(keys_normal[row][col][0]) && keys_normal[row][col][1] == '\0') shifted = !shifted; if (shifted && shift_key_labels[row][col] != NULL) label = shift_key_labels[row][col]; else label = normal_key_labels[row][col]; } if (label == NULL) { fprintf(stderr, "%s: no label for key %d,%d\n", PROGRAM_NAME, row, col); label = ""; } if (strcmp(label, "space") == 0) label = ""; XtVaSetValues(key_widgets[row][col], XtNlabel, label, NULL); } } } } last_shift_state = shift_state; last_mouse_shift = mouse_shift; last_num_lock_state = appres.num_lock_state; first = FALSE; #ifdef USE_XTEST if (appres.xtest && strlen(appres.positive_modifiers) != 0) { /* modifiers specified in positiveModifiers resouce will be hold down so that it can be used with, for example, mouse operations */ Window root, child; int root_x, root_y, x, y; unsigned int mask; XKeyEvent event; event.display = target_dpy; event.window = RootWindow(event.display, DefaultScreen(event.display)); event.root = event.window; event.subwindow = None; event.time = CurrentTime; event.x = 1; event.y = 1; event.x_root = 1; event.y_root = 1; event.same_screen = TRUE; event.state = 0; XQueryPointer(target_dpy, event.root, &root, &child, &root_x, &root_y, &x, &y, &mask); if (strstr(appres.positive_modifiers, "shift") != NULL && (shift_state & ShiftMask) != (mask & ShiftMask)) { event.keycode = XKeysymToKeycode(target_dpy, XK_Shift_L); event.type = (shift_state & ShiftMask) ? KeyPress : KeyRelease; SendEvent(&event); } if (strstr(appres.positive_modifiers, "control") != NULL && (shift_state & ControlMask) != (mask & ControlMask)) { event.keycode = XKeysymToKeycode(target_dpy, XK_Control_L); event.type = (shift_state & ControlMask) ? KeyPress : KeyRelease; SendEvent(&event); } if (strstr(appres.positive_modifiers, "alt") != NULL && (shift_state & alt_mask) != (mask & alt_mask)) { event.keycode = XKeysymToKeycode(target_dpy, XK_Alt_L); event.type = (shift_state & alt_mask) ? KeyPress : KeyRelease; SendEvent(&event); } if (strstr(appres.positive_modifiers, "meta") != NULL && (shift_state & meta_mask) != (mask & meta_mask)) { event.keycode = XKeysymToKeycode(target_dpy, XK_Meta_L); event.type = (shift_state & meta_mask) ? KeyPress : KeyRelease; SendEvent(&event); } } #endif } /* * This function will be called when mouse button is pressed on a key * on the screen. Most operation will be performed in KeyPressed() * which will be called as callback for the Command widgets, and we * only need to check which mouse button is pressed here. */ static unsigned int n_key_repeat; static void ButtonDownAction(Widget w, XEvent *event, String *pars, Cardinal *n_pars) { n_key_repeat = 0; switch (event->xbutton.button) { case Button2: mouse_shift |= ControlMask; break; case Button3: case Button4: mouse_shift |= ShiftMask; break; } RefreshShiftState(FALSE); } /* * This function will be called when mouse button is released on a key * on the screen, after callback is called. */ static void ButtonUpAction(Widget w, XEvent *event, String *pars, Cardinal *n_pars) { if (appres.quick_modifiers) { if (n_key_repeat == 1) XtCallCallbacks(w, XtNcallback, NULL); } mouse_shift = 0; RefreshShiftState(FALSE); } /* * Get the geometry of the base window. */ static char *GetWindowGeometry(Widget w) { static char geom[50]; Position x0, y0; Window root; int x1, y1; unsigned int wd, ht, bd, dp; XtVaGetValues(w, XtNx, &x0, XtNy, &y0, NULL); XGetGeometry(dpy, XtWindow(w), &root, &x1, &y1, &wd, &ht, &bd, &dp); sprintf(geom, "%dx%d+%d+%d", wd, ht, (int)(x0 - x1), (int)(y0 - y1)); return geom; } /* * Set window manager hint. * ("Extended Window Manager Hints", http://standards.freedesktop.org/wm-spec/) */ static void SetWindowManagerHint(Boolean initial) { if (initial) { if (appres.wm_toolbar) { Atom net_wm_window_type = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); Atom net_wm_window_type_toolbar = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_TOOLBAR", False); XChangeProperty(dpy, XtWindow(toplevel), net_wm_window_type, XA_ATOM, 32, PropModeReplace, (unsigned char *) &net_wm_window_type_toolbar, 1); if (appres.debug) fprintf(stderr, "SetWindowManagerHint: set _NET_WM_WINDOW_TYPE_TOOLBAR\n"); } } if (!initial || appres.always_on_top) { const int net_wm_state_remove = 0; const int net_wm_state_add = 1; Atom net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False); Atom net_wm_state_above = XInternAtom(dpy, "_NET_WM_STATE_ABOVE", False); XClientMessageEvent ev; ev.type = ClientMessage; ev.display = dpy; ev.window = XtWindow(toplevel); ev.message_type = net_wm_state; ev.format = 32; ev.data.l[0] = appres.always_on_top ? net_wm_state_add : net_wm_state_remove; ev.data.l[1] = net_wm_state_above; ev.data.l[2] = 0; XSendEvent(dpy, RootWindow(dpy, DefaultScreen(dpy)), FALSE, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent *)&ev); if (appres.debug) fprintf(stderr, "SetWindowManagerHint: _NET_WM_STATE_ABOVE = %d\n", ev.data.l[0]); } } /* * Restart the program to (possibly) change the keyboard layout, * by loading the app-default file for the selected "customization". */ static void LayoutSelected(Widget w, char *key, char *data) { static char *env_lang, *env_xenv; char name[50]; char customization[30] = "", lang[30] = "C"; char *xenv = NULL; int i; if (key != NULL) { if (strcmp(key, "default") != 0) { sscanf(key, "%29[^/]/%29s", customization, lang); sprintf(name, "XVkbd-%s", customization); xenv = XtResolvePathname(dpy, "app-defaults", name, NULL, NULL, NULL, 0, NULL); if (xenv == NULL) { fprintf(stderr, "%s: app-default file \"%s\" not installed\n", PROGRAM_NAME, name); } } env_lang = malloc(strlen("LC_ALL=") + strlen(lang) + 1); sprintf(env_lang, "LC_ALL=%s", lang); putenv(env_lang); if (xenv != NULL) { env_xenv = malloc(strlen("XENVIRONMENT=") + strlen(xenv) + 1); sprintf(env_xenv, "XENVIRONMENT=%s", xenv); putenv(env_xenv); } else if (getenv("XENVIRONMENT") != NULL) { putenv("XENVIRONMENT="); } } for (i = 1; i < argc1; i++) { if (strncmp(argv1[i], "-geom", strlen("-geom")) == 0) { if (appres.inherit_geometry) { argv1[i + 1] = GetWindowGeometry(toplevel); } else if (i + 2 == argc1) { argv1[i] = NULL; argc1 = i; } break; } } if (i == argc1 && appres.inherit_geometry) { argv1[argc1++] = "-geometry"; argv1[argc1++] = GetWindowGeometry(toplevel); argv1[argc1] = NULL; } if (appres.debug) { fprintf(stderr, "XENVIRONMENT=%s, LC_ALL=%s\n", (xenv != NULL) ? xenv : "", lang); fprintf(stderr, "Exec:"); for (i = 0; i < argc1; i++) fprintf(stderr, " %s", argv1[i]); fprintf(stderr, "\n"); } execvp(argv1[0], argv1); } /* * Popup a window to select the (possibly) keyboard layout. * The "XVkbd.customizations" resource will define the list, * such as "default,german,swissgerman,french,latin1,jisx6004/ja". * For example, "german" here will make this program to load * "XVkbd-german" app-default file. Locale for each configuration * can be specified by putting the locale name after "/". */ static void PopupLayoutPanel(void) { static Widget layout_panel = None; char *customizations; char *cp, *cp2; Widget box, button; if (layout_panel == None) { layout_panel = XtVaCreatePopupShell("layout_panel", transientShellWidgetClass, toplevel, NULL); box = XtVaCreateManagedWidget("box", boxWidgetClass, layout_panel, NULL); customizations = XtNewString(appres.customizations); cp = strtok(customizations, " \t,"); while (cp != NULL) { cp2 = strchr(cp, '/'); if (cp2 != NULL) *cp2 = '\0'; /* temporary remove '/' */ button = XtVaCreateManagedWidget(cp, commandWidgetClass, box, NULL); if (cp2 != NULL) *cp2 = '/'; XtAddCallback(button, XtNcallback, (XtCallbackProc)LayoutSelected, XtNewString(cp)); cp = strtok(NULL, " \t,"); } XtRealizeWidget(layout_panel); XSetWMProtocols(dpy, XtWindow(layout_panel), &wm_delete_window, 1); XtFree(customizations); } XtPopup(layout_panel, XtGrabNone); } /* * Property panel */ static void SaveFunctionKey(void); /* forward */ static Widget props_panel = None; static Widget autoclick_buttons = None; static Widget click_buttons = None; static Boolean props_panel_active = FALSE; static void PropsItemToggled(Widget w, char *key, char *data) { Boolean last_wm_toolbar = appres.wm_toolbar; if (!props_panel_active) return; #ifdef USE_XTEST XtVaGetValues(XtNameToWidget(props_panel, "*use_xtest"), XtNstate, &appres.xtest, NULL); #endif XtVaGetValues(XtNameToWidget(props_panel, "*quick_modifiers"), XtNstate, &appres.quick_modifiers, NULL); XtVaGetValues(XtNameToWidget(props_panel, "*shift_lock"), XtNstate, &appres.shift_lock, NULL); XtVaGetValues(XtNameToWidget(props_panel, "*altgr_lock"), XtNstate, &appres.altgr_lock, NULL); XtVaGetValues(XtNameToWidget(props_panel, "*modifiers_lock"), XtNstate, &appres.modifiers_lock, NULL); XtVaGetValues(XtNameToWidget(props_panel, "*always_on_top"), XtNstate, &appres.always_on_top, NULL); XtVaGetValues(XtNameToWidget(props_panel, "*wm_toolbar"), XtNstate, &appres.wm_toolbar, NULL); XtVaGetValues(XtNameToWidget(props_panel, "*jump_pointer"), XtNstate, &appres.jump_pointer, NULL); appres.key_click_duration = (int)XawToggleGetCurrent(click_buttons); appres.autoclick_delay = (int)XawToggleGetCurrent(autoclick_buttons); SaveFunctionKey(); SetWindowManagerHint(FALSE); if (appres.wm_toolbar != last_wm_toolbar) LayoutSelected(None, NULL, NULL); } static void PropsSetState(void) { #ifdef USE_XTEST XtVaSetValues(XtNameToWidget(props_panel, "*use_xtest"), XtNstate, appres.xtest, NULL); #endif XtVaSetValues(XtNameToWidget(props_panel, "*quick_modifiers"), XtNstate, appres.quick_modifiers, NULL); XtVaSetValues(XtNameToWidget(props_panel, "*shift_lock"), XtNstate, appres.shift_lock, NULL); if (XtNameToWidget(toplevel, "*Mode_switch") == None) { XtSetSensitive(XtNameToWidget(props_panel, "*altgr_lock"), FALSE); XtVaSetValues(XtNameToWidget(props_panel, "*altgr_lock"), XtNstate, FALSE, NULL); } else { XtVaSetValues(XtNameToWidget(props_panel, "*altgr_lock"), XtNstate, appres.altgr_lock, NULL); } XtVaSetValues(XtNameToWidget(props_panel, "*modifiers_lock"), XtNstate, appres.modifiers_lock, NULL); XtVaSetValues(XtNameToWidget(props_panel, "*always_on_top"), XtNstate, appres.always_on_top, NULL); XtVaSetValues(XtNameToWidget(props_panel, "*wm_toolbar"), XtNstate, appres.wm_toolbar, NULL); XtVaSetValues(XtNameToWidget(props_panel, "*jump_pointer"), XtNstate, appres.jump_pointer, NULL); XawToggleSetCurrent(click_buttons, (XtPointer)appres.key_click_duration); XawToggleSetCurrent(autoclick_buttons, (XtPointer)appres.autoclick_delay); } static void ClosePropsPanel(void) { XtPopdown(props_panel); XFlush(dpy); SaveFunctionKey(); if (completion_panel != None) XtPopdown(completion_panel); } static void PopupPropsPanel(void) { static char *props_items[] = { "quick_modifiers", "shift_lock", "altgr_lock", "modifiers_lock", "always_on_top", "wm_toolbar", #ifdef USE_XTEST "use_xtest", #endif "jump_pointer", }; if (props_panel == None) { Widget label, button; Widget form, w; int i; int val; props_panel = XtVaCreatePopupShell("props_panel", transientShellWidgetClass, toplevel, NULL); form = XtVaCreateManagedWidget("form", formWidgetClass, props_panel, NULL); w = None; for (i = 0; i < XtNumber(props_items); i++) { w = XtVaCreateManagedWidget(props_items[i], toggleWidgetClass, form, XtNfromVert, w, NULL); XtAddCallback(w, XtNcallback, (XtCallbackProc)PropsItemToggled, (XtPointer)props_items[i]); } label = XtVaCreateManagedWidget("click", labelWidgetClass, form, XtNfromVert, w, NULL); button = XtVaCreateManagedWidget("OFF", toggleWidgetClass, form, XtNfromVert, w, XtNfromHoriz, label, XtNwidth, 0, XtNhorizDistance, 0, NULL); XtVaSetValues(button, XtNradioGroup, button, XtNradioData, (XtPointer)0, NULL); XtAddCallback(button, XtNcallback, (XtCallbackProc)PropsItemToggled, (XtPointer)0); click_buttons = button; for (val = 1; val <= 50; val *= 2) { char s1[10]; sprintf(s1, "%dms", val); button = XtVaCreateManagedWidget(s1, toggleWidgetClass, form, XtNfromVert, w, XtNfromHoriz, button, XtNradioData, (XtPointer)val, XtNradioGroup, click_buttons, XtNwidth, 0, XtNhorizDistance, 0, NULL); XtAddCallback(button, XtNcallback, (XtCallbackProc)PropsItemToggled, NULL); } w = label; label = XtVaCreateManagedWidget("autoclick", labelWidgetClass, form, XtNfromVert, w, NULL); button = XtVaCreateManagedWidget("OFF", toggleWidgetClass, form, XtNfromVert, w, XtNfromHoriz, label, XtNwidth, 0, XtNhorizDistance, 0, NULL); XtVaSetValues(button, XtNradioGroup, button, XtNradioData, (XtPointer)0, NULL); XtAddCallback(button, XtNcallback, (XtCallbackProc)PropsItemToggled, (XtPointer)0); autoclick_buttons = button; for (val = 500; val <= 1000; val += 100) { char s1[10]; sprintf(s1, "%dms", val); button = XtVaCreateManagedWidget(s1, toggleWidgetClass, form, XtNfromVert, w, XtNfromHoriz, button, XtNradioData, (XtPointer)val, XtNradioGroup, autoclick_buttons, XtNwidth, 0, XtNhorizDistance, 0, NULL); XtAddCallback(button, XtNcallback, (XtCallbackProc)PropsItemToggled, (XtPointer)val); } w = label; label = XtVaCreateManagedWidget("dict_entry_label", labelWidgetClass, form, XtNfromVert, w, NULL); props_dict_entry = XtVaCreateManagedWidget("dict_entry", asciiTextWidgetClass, form, XtNfromVert, w, XtNfromHoriz, label, XtNuseStringInPlace, True, XtNstring, dict_filename, XtNeditType, XawtextEdit, XtNlength, sizeof(dict_filename) - 1, NULL); button = XtVaCreateManagedWidget("dict_default_button", commandWidgetClass, form, XtNfromVert, w, XtNfromHoriz, props_dict_entry, NULL); XtAddCallback(button, XtNcallback, (XtCallbackProc)SetDefaultDictionary, NULL); w = XtVaCreateManagedWidget("dismiss", commandWidgetClass, form, XtNfromVert, label, NULL); XtAddCallback(w, XtNcallback, (XtCallbackProc)ClosePropsPanel, NULL); XtRealizeWidget(props_panel); XSetWMProtocols(dpy, XtWindow(props_panel), &wm_delete_window, 1); } XtPopup(props_panel, XtGrabNone); PropsSetState(); props_panel_active = TRUE; if (completion_panel != None) XtPopdown(completion_panel); } /* * Callback for main menu (activated from "xvkbd" logo). */ static Widget about_panel = None; static Widget keypad_panel = None; static Widget sun_fkey_panel = None; static Widget deadkey_panel = None; static Widget display_panel = None; static Widget display_status = None; #define DISPLAY_NAME_LENGTH 50 static void OpenRemoteDisplay(Widget w, char *display_name, char *data) { static char name[DISPLAY_NAME_LENGTH + 10]; char *cp; focused_window = None; focused_subwindow = None; if (target_dpy != NULL && target_dpy != dpy) XCloseDisplay(target_dpy); strncpy(name, (display_name == NULL) ? "" : display_name, sizeof(name)); for (cp = name; isascii(*cp) && isprint(*cp); cp++) ; *cp = '\0'; if (strlen(name) == 0) { target_dpy = dpy; XtVaSetValues(display_status, XtNlabel, "Disconnected - local display selected", NULL); XtPopdown(display_panel); } else { if (strchr(name, ':') == NULL) strcat(name, ":0"); target_dpy = XOpenDisplay(name); if (target_dpy == NULL) { XtVaSetValues(display_status, XtNlabel, "Couldn't connect to the display", NULL); target_dpy = dpy; XBell(dpy, 0); } else { XtVaSetValues(display_status, XtNlabel, "Connected", NULL); XtPopdown(display_panel); } } ReadKeymap(); if (!altgr_mask && appres.auto_add_keysym) AddModifier(XK_Mode_switch); RefreshMainMenu(); RefreshShiftState(FALSE); } static void MenuSelected(Widget w, char *key) { Widget form; if (strcmp(key, "man") == 0) { if (!appres.secure) system(appres.show_manual_command); } else if (strcmp(key, "about") == 0) { if (about_panel == None) { about_panel = XtVaCreatePopupShell("about_panel", transientShellWidgetClass, toplevel, NULL); XtVaCreateManagedWidget("message", labelWidgetClass, about_panel, XtNlabel, appres.description, NULL); XtRealizeWidget(about_panel); XSetWMProtocols(dpy, XtWindow(about_panel), &wm_delete_window, 1); } XtPopup(about_panel, XtGrabNone); } else if (strcmp(key, "keypad") == 0) { if (keypad_panel == None) { keypad_panel = XtVaCreatePopupShell("keypad_panel", transientShellWidgetClass, toplevel, NULL); form = XtVaCreateManagedWidget("form", formWidgetClass, keypad_panel, NULL); MakeKeypad(form, None, None); XtRealizeWidget(keypad_panel); XSetWMProtocols(dpy, XtWindow(keypad_panel), &wm_delete_window, 1); } XtPopup(keypad_panel, XtGrabNone); } else if (strcmp(key, "sun_fkey") == 0) { if (sun_fkey_panel == None) { sun_fkey_panel = XtVaCreatePopupShell("sun_fkey_panel", transientShellWidgetClass, toplevel, NULL); form = XtVaCreateManagedWidget("form", formWidgetClass, sun_fkey_panel, NULL); MakeSunFunctionKey(form, None, None); XtRealizeWidget(sun_fkey_panel); XSetWMProtocols(dpy, XtWindow(sun_fkey_panel), &wm_delete_window, 1); } XtPopup(sun_fkey_panel, XtGrabNone); } else if (strcmp(key, "deadkey") == 0) { if (deadkey_panel == None) { deadkey_panel = XtVaCreatePopupShell("deadkey_panel", transientShellWidgetClass, toplevel, NULL); form = XtVaCreateManagedWidget("form", formWidgetClass, deadkey_panel, NULL); MakeDeadkeyPanel(form); XtRealizeWidget(deadkey_panel); XSetWMProtocols(dpy, XtWindow(deadkey_panel), &wm_delete_window, 1); } XtPopup(deadkey_panel, XtGrabNone); } else if (strcmp(key, "completion") == 0) { PopupCompletionPanel(); } else if (strcmp(key, "select_layout") == 0) { PopupLayoutPanel(); } else if (strcmp(key, "edit_fkey") == 0) { PopupFunctionKeyEditor(); } else if (strcmp(key, "show_keypad") == 0 || strcmp(key, "show_functionkey") == 0) { if (strcmp(key, "show_keypad") == 0) appres.keypad = !appres.keypad; else appres.function_key = !appres.function_key; MakeKeyboard(TRUE); } else if (strcmp(key, "props") == 0) { PopupPropsPanel(); } else if (strcmp(key, "open_display") == 0) { if (display_panel == None) { Widget label, entry, button; static char display_name[DISPLAY_NAME_LENGTH] = ""; display_panel = XtVaCreatePopupShell("display_panel", transientShellWidgetClass, toplevel, NULL); form = XtVaCreateManagedWidget("form", formWidgetClass, display_panel, NULL); label = XtVaCreateManagedWidget("label", labelWidgetClass, form, NULL); entry = XtVaCreateManagedWidget("entry", asciiTextWidgetClass, form, XtNfromHoriz, label, XtNuseStringInPlace, True, XtNeditType, XawtextEdit, XtNstring, display_name, XtNlength, sizeof(display_name) - 1, NULL); button = XtVaCreateManagedWidget("ok", commandWidgetClass, form, XtNfromHoriz, entry, NULL); XtAddCallback(button, XtNcallback, (XtCallbackProc)OpenRemoteDisplay, (XtPointer)display_name); display_status = XtVaCreateManagedWidget("status", labelWidgetClass, form, XtNfromVert, label, XtNlabel, "", NULL); XtRealizeWidget(display_panel); XSetWMProtocols(dpy, XtWindow(display_panel), &wm_delete_window, 1); XtSetKeyboardFocus(display_panel, entry); } XtPopup(display_panel, XtGrabNone); } else if (strcmp(key, "close_display") == 0) { OpenRemoteDisplay(None, NULL, NULL); } else if (strcmp(key, "quit") == 0) { DeleteWindowProc(None, NULL, NULL, NULL); } } static void ClosePopupPanel(Widget w) { if (w == keypad_panel) { XtDestroyWidget(w); keypad_panel = None; } else if (w == props_panel) { ClosePropsPanel(); } else { XtPopdown(w); } } /* * Iconify/uniconify the xvkbd window even if window manager is not * available. */ static void IconifyWindow(Widget w, Boolean iconify) { static Widget iconified_window = None; static Widget uniconify_button = None; static Position x0, y0; static int x1, y1; static unsigned int wd, ht, bd, dp; if (iconify) { Window root; int i; XUnmapWindow(dpy, XtWindow(toplevel)); if (iconified_window == None) { Widget box; iconified_window = XtVaCreatePopupShell("iconified_window", transientShellWidgetClass, toplevel, XtNoverrideRedirect, TRUE, NULL); box = XtVaCreateManagedWidget("form", boxWidgetClass, iconified_window, NULL); uniconify_button = XtVaCreateManagedWidget("uniconify_button", commandWidgetClass, box, XtNbitmap, xvkbd_pixmap, XtNhorizDistance, 10, XtNvertDistance, 0, NULL); XtAddCallback(uniconify_button, XtNcallback, (XtCallbackProc)IconifyWindow, FALSE); XtRealizeWidget(iconified_window); XSetWMProtocols(dpy, XtWindow(iconified_window), &wm_delete_window, 1); } XtVaGetValues(toplevel, XtNx, &x0, XtNy, &y0, NULL); XGetGeometry(dpy, XtWindow(toplevel), &root, &x1, &y1, &wd, &ht, &bd, &dp); XMoveResizeWindow(dpy, XtWindow(iconified_window), x0 + bd, y0 + bd, wd, ht); XtPopup(iconified_window, XtGrabNone); for (i = 9; 0 < i; i--) { Dimension btn_wd, btn_ht; Dimension wd1, ht1; wd1 = wd * i / 10; ht1 = ht * i / 10; XtVaGetValues(uniconify_button, XtNwidth, &btn_wd, XtNheight, &btn_ht, NULL); if (i == 1 || wd1 < btn_wd) wd1 = btn_wd; if (i == 1 || ht1 < btn_ht) ht1 = btn_ht; XMoveResizeWindow(dpy, XtWindow(iconified_window), x0 + bd, y0 + (ht - ht1) + bd, wd1, ht1); XFlush(dpy); usleep(10000); } } else { if (iconified_window != None) XtPopdown(iconified_window); XMapWindow(dpy, XtWindow(toplevel)); } } static void SignalUser1(void) { XWindowAttributes attr; XGetWindowAttributes(dpy, XtWindow(toplevel), &attr); IconifyWindow(None, attr.map_state != IsUnmapped); XSync(dpy, FALSE); } /* * This will be called when user pressed a key on the screen. */ static const char *FindFunctionKeyValue(const char *key, Boolean shiftable); static void ShowBalloon(Widget w, XEvent *event, String *pars, Cardinal *n_pars); static void KeyClick(void); static void StopAutoclick(void); static void KeyPressed(Widget w, char *key, char *data) { int row, col; int cur_shift; char *key1; KeySym keysym; Boolean shifted; const char *value; Boolean found; if (appres.debug) fprintf(stderr, "KeyPressed: key=%s, widget=%lx\n", key, (long)w); value = FindFunctionKeyValue(key, TRUE); if (value != NULL) { if (appres.debug) fprintf(stderr, "Assigned string: %s\n", value); if (value[0] == '!') { if (appres.debug) fprintf(stderr, "Launching: %s\n", value + 1); if (!appres.secure) system(value + 1); } else { if (value[0] == '\\') value = value + 1; if (appres.debug) fprintf(stderr, "Sending: %s\n", value); SendString(value); } ShowBalloon(w, NULL, NULL, NULL); return; } if (strncmp(key, "Shift", strlen("Shift")) == 0) { if (shift_state & ShiftMask) SendKeyPressedEvent(NoSymbol, shift_state); shift_state ^= ShiftMask; } else if (strncmp(key, "Control", strlen("Control")) == 0) { if (shift_state & ControlMask) SendKeyPressedEvent(NoSymbol, shift_state); shift_state ^= ControlMask; } else if (strncmp(key, "Alt", strlen("Alt")) == 0) { if (shift_state & alt_mask) SendKeyPressedEvent(NoSymbol, shift_state); shift_state ^= alt_mask; } else if (strncmp(key, "Meta", strlen("Meta")) == 0) { if (shift_state & meta_mask) SendKeyPressedEvent(NoSymbol, shift_state); shift_state ^= meta_mask; } else if (strcmp(key, "Caps_Lock") == 0) { if (shift_state & LockMask) SendKeyPressedEvent(NoSymbol, shift_state); shift_state ^= LockMask; } else if (strcmp(key, "Mode_switch") == 0) { if (shift_state & altgr_mask) SendKeyPressedEvent(NoSymbol, shift_state); shift_state ^= altgr_mask; } else if (strcmp(key, "Num_Lock") == 0) { appres.num_lock_state = !appres.num_lock_state; } else if (strcmp(key, "Focus") == 0) { cur_shift = shift_state | mouse_shift; if (cur_shift & ShiftMask) { focused_window = None; focused_subwindow = None; } else { GetFocusedWindow(); } } else { if (appres.quick_modifiers && mouse_shift == 0 && w != None) { Window junk_w; int junk_i; unsigned junk_u; int cur_x, cur_y; Dimension btn_wd, btn_ht; n_key_repeat = n_key_repeat + 1; if (n_key_repeat == 1) return; XtVaGetValues(w, XtNwidth, &btn_wd, XtNheight, &btn_ht, NULL); XQueryPointer(dpy, XtWindow(w), &junk_w, &junk_w, &junk_i, &junk_i, &cur_x, &cur_y, &junk_u); mouse_shift = 0; if (cur_x < 0 && btn_ht < cur_y) { mouse_shift |= alt_mask; /* left-down */ } else { if (cur_y < 0) mouse_shift |= ShiftMask; /* up */ else if (btn_ht < cur_y) mouse_shift |= meta_mask; /* down */ if (cur_x < 0) mouse_shift |= ControlMask; /* left */ else if (btn_wd < cur_x) mouse_shift |= altgr_mask; /* right */ } } cur_shift = shift_state | mouse_shift; shifted = (cur_shift & ShiftMask); key1 = key; if (w != None) { if (sscanf(key, "pad%d,%d", &row, &col) == 2) { if (appres.num_lock_state) shifted = !shifted; key1 = shifted ? keypad_shift[row][col]: keypad[row][col]; } else { found = FALSE; if (sscanf(key, "%d,%d", &row, &col) == 2) { found = TRUE; } else if (w != None) { int first_row = appres.function_key ? 0 : 1; for (row = first_row; row < NUM_KEY_ROWS; row++) { for (col = 0; col < NUM_KEY_COLS; col++) { if (key_widgets[row][col] == w) { found = TRUE; break; } } if (col < NUM_KEY_COLS) break; } } if (found) { if ((cur_shift & LockMask) && isalpha(keys_normal[row][col][0]) && keys_normal[row][col][1] == '\0') shifted = !shifted; if ((cur_shift & altgr_mask) && keys_altgr[row][col] != NULL) { if (shifted && keys_shift_altgr[row][col] != NULL) { key1 = keys_shift_altgr[row][col]; if (strcmp(keys_altgr[row][col], keys_shift_altgr[row][col]) != 0) cur_shift &= ~ShiftMask; } else { key1 = keys_altgr[row][col]; } } else { if (shifted && keys_shift[row][col] != NULL) { key1 = keys_shift[row][col]; if (strcmp(keys_normal[row][col], keys_shift[row][col]) != 0) cur_shift &= ~ShiftMask; } else { key1 = keys_normal[row][col]; } } } /* if (found) ... */ } /* if (sscanf(key, "pad%d,%d", ... */ } /* if (w != None) ... */ if (strlen(key1) == 1) { SendKeyPressedEvent((KeySym)*key1 & 0xff, cur_shift); AddToCompletionText((KeySym)*key1); } else { if (islower(key1[0]) && key1[1] == ':') { switch (key1[0]) { case 's': cur_shift |= ShiftMask; break; case 'c': cur_shift |= ControlMask; break; case 'a': cur_shift |= alt_mask; break; case 'm': cur_shift |= meta_mask; break; default: fprintf(stderr, "%s: unknown modidier: %s\n", PROGRAM_NAME, key1); break; } key1 = key1 + 2; } keysym = XStringToKeysym(key1); if ((!appres.keypad_keysym && strncmp(key1, "KP_", 3) == 0) || XKeysymToKeycode(target_dpy, keysym) == NoSymbol) { switch ((unsigned)keysym) { case XK_KP_Equal: keysym = XK_equal; break; case XK_KP_Divide: keysym = XK_slash; break; case XK_KP_Multiply: keysym = XK_asterisk; break; case XK_KP_Add: keysym = XK_plus; break; case XK_KP_Subtract: keysym = XK_minus; break; case XK_KP_Enter: keysym = XK_Return; break; case XK_KP_1: keysym = XK_1; break; case XK_KP_2: keysym = XK_2; break; case XK_KP_3: keysym = XK_3; break; case XK_KP_4: keysym = XK_4; break; case XK_KP_5: keysym = XK_5; break; case XK_KP_6: keysym = XK_6; break; case XK_KP_7: keysym = XK_7; break; case XK_KP_8: keysym = XK_8; break; case XK_KP_9: keysym = XK_9; break; case XK_Shift_L: keysym = XK_Shift_R; break; case XK_Shift_R: keysym = XK_Shift_L; break; case XK_Control_L: keysym = XK_Control_R; break; case XK_Control_R: keysym = XK_Control_L; break; case XK_Alt_L: keysym = XK_Alt_R; break; case XK_Alt_R: keysym = XK_Alt_L; break; case XK_Meta_L: keysym = XK_Meta_R; break; case XK_Meta_R: keysym = XK_Meta_L; break; default: if (keysym == NoSymbol || !appres.auto_add_keysym) fprintf(stderr, "%s: no such key: %s\n", PROGRAM_NAME, key1); break; } } SendKeyPressedEvent(keysym, cur_shift); AddToCompletionText(keysym); if ((cur_shift & ControlMask) && (cur_shift & alt_mask)) { if (strstr(XServerVendor(dpy), "XFree86") != NULL) { if (strcmp(key1, "KP_Add") == 0) { if (!appres.secure) system("xvidtune -next"); } else if (strcmp(key1, "KP_Subtract") == 0) { if (!appres.secure) system("xvidtune -prev"); } } } } if (!appres.shift_lock) shift_state &= ~ShiftMask; if (!appres.modifiers_lock) shift_state &= ~(ControlMask | alt_mask | meta_mask); if (!appres.altgr_lock) shift_state &= ~altgr_mask; } RefreshShiftState(FALSE); if (w != None) { KeyClick(); /* StopAutoclick(); */ } } /* * Redefine keyboard layout. * "spec" is a sequence of words separated with spaces, and it is * usally specified in app-defaults file, as: * * xvkbd.AltGrKeys: \ * F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 BackSpace \n\ * Escape \271 \262 \263 \243 \254 \251 { [ ] } \\ ' ^ ' \n\ * ... * * White spaces separate the keys, and " \n" (note that white space * before the \n) separate the rows of keys. */ static void RedefineKeys(char *array[NUM_KEY_ROWS][NUM_KEY_COLS], const char *spec) { char *s = XtNewString(spec); char *cp; int row, col; int key_rows = NUM_KEY_ROWS; int key_cols = NUM_KEY_COLS; for (row = 0; row < key_rows; row++) { for (col = 0; col < key_cols; col++) array[row][col] = NULL; } row = 0; col = 0; cp = strtok(s, " "); while (cp != NULL) { if (*cp == '\n') { row = row + 1; col = 0; cp = cp + 1; } if (*cp != '\0') { if (key_rows <= row) { fprintf(stderr, "%s: too many key rows: \"%s\" ignored\n", PROGRAM_NAME, cp); } else if (key_cols <= col) { fprintf(stderr, "%s: too many keys in a row: \"%s\" ignored\n", PROGRAM_NAME, cp); } else { array[row][col] = XtNewString(cp); col = col + 1; } } cp = strtok(NULL, " "); } XtFree(s); } /* * Create keyboard on the screen. */ static Widget MakeKey(Widget parent, const char *name, const char *label, Pixel color) { static Pixmap up_pixmap = None; static Pixmap down_pixmap = None; static Pixmap left_pixmap = None; static Pixmap right_pixmap = None; static Pixmap back_pixmap = None; Widget w; Window scr = RootWindow(dpy, DefaultScreen(dpy)); char str[50]; int len; if (!appres.auto_repeat || strncmp(name, "Shift", strlen("Shift")) == 0 || strncmp(name, "Control", strlen("Control")) == 0 || strncmp(name, "Alt", strlen("Alt")) == 0 || strncmp(name, "Meta", strlen("Meta")) == 0 || strcmp(name, "Caps_Lock") == 0 || strcmp(name, "Mode_switch") == 0 || strcmp(name, "Num_Lock") == 0 || strcmp(name, "Focus") == 0) { w = XtVaCreateManagedWidget(name, commandWidgetClass, parent, XtNbackground, color, NULL); } else { w = XtVaCreateManagedWidget(name, repeaterWidgetClass, parent, XtNbackground, color, NULL); } XtAddCallback(w, XtNcallback, (XtCallbackProc)KeyPressed, (XtPointer)name); if (label != NULL) { strncpy(str, label, sizeof(str) - 1); if (strcmp(str, "space") == 0) strcpy(str, ""); len = strlen(str); if (3 <= len) { if (str[1] == '_') str[1] = ' '; if (str[len - 2] == '_') str[len - 2] = ' '; } XtVaSetValues(w, XtNlabel, str, NULL); if (strcmp(label, "up") == 0) { if (up_pixmap == None) up_pixmap = XCreateBitmapFromData(dpy, scr, (char *)up_bits, up_width, up_height); XtVaSetValues(w, XtNbitmap, up_pixmap, NULL); } else if (strcmp(label, "down") == 0) { if (down_pixmap == None) down_pixmap = XCreateBitmapFromData(dpy, scr, (char *)down_bits, down_width, down_height); XtVaSetValues(w, XtNbitmap, down_pixmap, NULL); } else if (strcmp(label, "left") == 0) { if (left_pixmap == None) left_pixmap = XCreateBitmapFromData(dpy, scr, (char *)left_bits, left_width, left_height); XtVaSetValues(w, XtNbitmap, left_pixmap, NULL); } else if (strcmp(label, "right") == 0) { if (right_pixmap == None) right_pixmap = XCreateBitmapFromData(dpy, scr, (char *)right_bits, right_width, right_height); XtVaSetValues(w, XtNbitmap, right_pixmap, NULL); } else if (strcmp(label, "back") == 0) { if (back_pixmap == None) back_pixmap = XCreateBitmapFromData(dpy, scr, (char *)back_bits, back_width, back_height); XtVaSetValues(w, XtNbitmap, back_pixmap, NULL); } } return w; } static void MakeKeypad(Widget form, Widget from_vert, Widget from_horiz) { Widget key, left, upper; Pixel color; XFontStruct *font; int row, col; Widget keypad_box; Widget keypad_row[NUM_KEYPAD_ROWS]; char name[50]; keypad_box = XtVaCreateManagedWidget("keypad", formWidgetClass, form, NULL); if (from_horiz != None) XtVaSetValues(keypad_box, XtNfromHoriz, from_horiz, NULL); else XtVaSetValues(keypad_box, XtNhorizDistance, 0, NULL); if (from_vert != None) XtVaSetValues(keypad_box, XtNfromVert, from_vert, NULL); else XtVaSetValues(keypad_box, XtNvertDistance, 0, NULL); upper = None; for (row = 0; row < NUM_KEYPAD_ROWS; row++) { left = None; for (col = 0; keypad[row][col] != NULL; col++) { font = appres.keypad_font; if (strlen(keypad_label[row][col]) == 1) font = appres.letter_font; color = appres.special_background; if (strcmp(keypad[row][col], "Focus") == 0) color = appres.focus_background; else if (strcmp(keypad_shift[row][col], ".") == 0 || (strncmp(keypad_shift[row][col], "KP_", 3) == 0 && isdigit(keypad_shift[row][col][3]))) color = appres.general_background; strcpy(name, keypad[row][col]); if (strcmp(name, "Focus") != 0 && strcmp(name, "Num_Lock") != 0) sprintf(name, "pad%d,%d", row, col); key = MakeKey(keypad_box, XtNewString(name), keypad_label[row][col], color); XtVaSetValues(key, XtNfont, font, NULL); if (row != 0) XtVaSetValues(key, XtNfromVert, keypad_row[row - 1], NULL); if (left != None) XtVaSetValues(key, XtNfromHoriz, left, NULL); if (col == 0) keypad_row[row] = key; left = key; } } } static void MakeSunFunctionKey(Widget form, Widget from_vert, Widget from_horiz) { Widget key, left, upper; int row, col; Widget fkey_box; Widget fkey_row[NUM_SUN_FKEY_ROWS]; fkey_box = XtVaCreateManagedWidget("fkey", formWidgetClass, form, NULL); if (from_horiz != None) XtVaSetValues(fkey_box, XtNfromHoriz, from_horiz, NULL); else XtVaSetValues(fkey_box, XtNhorizDistance, 0, NULL); if (from_vert != None) XtVaSetValues(fkey_box, XtNfromVert, from_vert, NULL); else XtVaSetValues(fkey_box, XtNvertDistance, 0, NULL); upper = None; for (row = 0; row < NUM_SUN_FKEY_ROWS; row++) { left = None; for (col = 0; sun_fkey[row][col] != NULL; col++) { key = MakeKey(fkey_box, sun_fkey[row][col], sun_fkey_label[row][col], appres.special_background); XtVaSetValues(key, XtNfont, appres.keypad_font, NULL); if (row != 0) XtVaSetValues(key, XtNfromVert, fkey_row[row - 1], NULL); if (left != None) XtVaSetValues(key, XtNfromHoriz, left, NULL); if (col == 0) fkey_row[row] = key; left = key; } } } static void MakeDeadkeyPanel(Widget form) { Widget deadkey_box, left, key; char *deadkeys, *cp, *cp2; deadkeys = XtNewString(appres.deadkeys); deadkey_box = XtVaCreateManagedWidget("deadkey", formWidgetClass, form, NULL); left = None; cp = strtok(deadkeys, " \t,"); while (cp != NULL) { cp2 = XtNewString(cp); key = MakeKey(deadkey_box, cp2, NULL, appres.general_background); if (left != None) XtVaSetValues(key, XtNfromHoriz, left, NULL); left = key; cp = strtok(NULL, " \t,"); } XtFree(deadkeys); } static void RefreshMainMenu(void) { static Pixmap check_pixmap = None; if (check_pixmap == None) { check_pixmap = XCreateBitmapFromData(dpy, RootWindow(dpy, DefaultScreen(dpy)), (char *)check_bits, check_width, check_height); } XtVaSetValues(XtNameToWidget(main_menu, "*show_keypad"), XtNrightBitmap, appres.keypad ? check_pixmap : None, NULL); XtVaSetValues(XtNameToWidget(main_menu, "*show_functionkey"), XtNrightBitmap, appres.function_key ? check_pixmap : None, NULL); XtSetSensitive(XtNameToWidget(main_menu, "*edit_fkey"), appres.function_key); XtSetSensitive(XtNameToWidget(main_menu, "*close_display"), target_dpy != dpy); } static void MakeKeyboard(Boolean remake) { static char *main_menu_items[] = { "about", "man", "keypad", "sun_fkey", "deadkey", "completion", "", "select_layout", "edit_fkey", "show_keypad", "show_functionkey", "props", "", "open_display", "close_display", "", "quit" }; Widget form, key, left; Pixel color; XFontStruct *font; Dimension wd, max_wd; int row, col, first_row; char name[50], *label; Widget key_box[NUM_KEY_ROWS]; Widget menu_entry; int i; #include "xvkbd.xbm" #include "iconify.xbm" if (remake) { appres.geometry = GetWindowGeometry(toplevel); XtUnrealizeWidget(toplevel); XtDestroyWidget(XtNameToWidget(toplevel, "form")); } form = XtVaCreateManagedWidget("form", formWidgetClass, toplevel, NULL); key_box[0] = None; key_box[1] = None; first_row = appres.function_key ? 0 : 1; if (!appres.keypad_only) { for (row = first_row; row < NUM_KEY_ROWS; row++) { if (keys_normal[row][0] == NULL) continue; sprintf(name, "row%d", row); key_box[row] = XtVaCreateManagedWidget(name, formWidgetClass, form, NULL); key_box[row + 1] = None; if (row != first_row) XtVaSetValues(key_box[row], XtNfromVert, key_box[row - 1], NULL); else if (!appres.function_key) XtVaSetValues(key_box[row], XtNvertDistance, 0, NULL); left = None; for (col = 0; keys_normal[row][col] != NULL; col++) { strcpy(name, keys_normal[row][col]); if (strcmp(name, "MainMenu") == 0) { Widget iconify_button = None; if (appres.minimizable) { Pixmap iconify_pixmap = XCreateBitmapFromData(dpy, RootWindow(dpy, DefaultScreen(dpy)), (char *)iconify_bits, iconify_width, iconify_height); iconify_button = XtVaCreateManagedWidget("Iconify", commandWidgetClass, key_box[row], XtNbitmap, iconify_pixmap, NULL); XtAddCallback(iconify_button, XtNcallback, (XtCallbackProc)IconifyWindow, (void *)TRUE); } xvkbd_pixmap = XCreateBitmapFromData(dpy, RootWindow(dpy, DefaultScreen(dpy)), (char *)xvkbd_bits, xvkbd_width, xvkbd_height); key = XtVaCreateManagedWidget("MainMenu", menuButtonWidgetClass, key_box[row], XtNbitmap, xvkbd_pixmap, XtNfromHoriz, iconify_button, NULL); main_menu = XtVaCreatePopupShell("menu", simpleMenuWidgetClass, key, NULL); for (i = 0; i < XtNumber(main_menu_items); i++) { if (strlen(main_menu_items[i]) == 0) { XtVaCreateManagedWidget("separator", smeLineObjectClass, main_menu, NULL); } else { menu_entry = XtVaCreateManagedWidget(main_menu_items[i], smeBSBObjectClass, main_menu, NULL); XtAddCallback(menu_entry, XtNcallback, (XtCallbackProc)MenuSelected, (XtPointer)main_menu_items[i]); } } } else { label = appres.modal_keytop ? normal_key_labels[row][col] : key_labels[row][col]; if (isascii(name[0]) && isupper(name[0])) { if (strcmp(name, "Focus") == 0) { color = appres.focus_background; font = appres.keypad_font; } else { color = appres.special_background; if (label != NULL && strchr(label, '\n') != NULL) font = appres.keypad_font; else font = appres.special_font; } } else { color = appres.general_background; font = appres.general_font; if (isalpha(name[0])) font = appres.letter_font; if (strcmp(name, "space") != 0) sprintf(name, "%d,%d", row, col); } key = MakeKey(key_box[row], XtNewString(name), label, color); XtVaGetValues(key, XtNwidth, &wd, NULL); if (wd <= 1) { /* keys can be removed by setting its width to 1 */ XtDestroyWidget(key); key = None; } else { XtVaSetValues(key, XtNfont, font, NULL); #ifdef USE_I18N if (font == appres.special_font || font == appres.keypad_font) XtVaSetValues(key, XtNfontSet, appres.special_fontset, NULL); #endif } } if (key != None) { if (left != None) XtVaSetValues(key, XtNfromHoriz, left, NULL); left = key; } key_widgets[row][col] = key; } } } if (appres.keypad) MakeKeypad(form, key_box[0], key_box[1]); if (!appres.keypad_only && appres.function_key && appres.keypad) { XtVaCreateManagedWidget("banner", labelWidgetClass, form, XtNfromHoriz, key_box[1], XtNlabel, PROGRAM_NAME_WITH_VERSION, NULL); } XtRealizeWidget(toplevel); SetWindowManagerHint(TRUE); if (!remake && strlen(appres.geometry) == 0) { Window root; int x1, y1; unsigned int wd, ht, bd, dp; int max_wd, max_ht; XGetGeometry(dpy, XtWindow(toplevel), &root, &x1, &y1, &wd, &ht, &bd, &dp); max_wd = XtScreen(toplevel)->width * appres.max_width_ratio; max_ht = XtScreen(toplevel)->height * appres.max_height_ratio; if (appres.debug) fprintf(stderr, "window size: %dx%d, max size: %dx%d\n", wd, ht, max_wd, max_ht); if (max_wd < wd || max_ht < ht) { if (max_wd < wd) wd = max_wd; if (max_ht < ht) ht = max_ht; if (appres.debug) fprintf(stderr, "setting window size: %dx%d\n", wd, ht); XResizeWindow(dpy, XtWindow(toplevel), wd, ht); } } if (!appres.debug && key_box[first_row] != None) { if (appres.keypad) { XtVaGetValues(key_box[1], XtNwidth, &max_wd, NULL); } else { max_wd = 0; for (row = first_row; row < NUM_KEY_ROWS && key_box[row] != None; row++) { XtVaGetValues(key_box[row], XtNwidth, &wd, NULL); if (max_wd < wd) max_wd = wd; } } for (row = first_row; row < NUM_KEY_ROWS && key_box[row] != None; row++) { XtVaSetValues(key_box[row], XtNwidth, max_wd, NULL); } } if (0 < strlen(appres.geometry)) { if (appres.wm_toolbar) { if (appres.debug) fprintf(stderr, "window fgeometry ignored; _NET_WM_WINDOW_TYPE_TOOLBAR set on\n"); } else { if (appres.debug) fprintf(stderr, "setting window geometry: %s\n", appres.geometry); XtVaSetValues(toplevel, XtNgeometry, appres.geometry, NULL); XtUnrealizeWidget(toplevel); XtRealizeWidget(toplevel); } } ReadKeymap(); if (main_menu != None) RefreshMainMenu(); RefreshShiftState(FALSE); XtMapWidget(toplevel); if (wm_delete_window == None) wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", FALSE); XSetWMProtocols(dpy, XtWindow(toplevel), &wm_delete_window, 1); XtVaGetValues(toplevel, XtNheight, &toplevel_height, NULL); } /* * WM_DELETE_WINDOW has been sent - terminate the program. */ static void DeleteWindowProc(Widget w, XEvent *event, String *pars, Cardinal *n_pars) { if (appres.nonexitable) { XBell(dpy, 0); } else { shift_state = 0; RefreshShiftState(TRUE); XtDestroyApplicationContext(XtWidgetToApplicationContext(toplevel)); exit(0); } } /* * Callback for ConfigureNotify event, which will be invoked when * the toplevel window is resized. * We may need to switch the keytop labels when window becomes * smaller than appres.modal_threshold, and vice versa. */ static void WindowResized(Widget w, XEvent *event, String *pars, Cardinal *n_pars) { Dimension ht; XtVaGetValues(toplevel, XtNheight, &ht, NULL); if (appres.modal_threshold <= ht) { if (toplevel_height < appres.modal_threshold) MakeKeyboard(TRUE); } else { toplevel_height = ht; } RefreshShiftState(TRUE); } /* * Load list of text to be assigned to function keys. * Each line contains name of the key (with optional modifier) * and the text to be assigned to the key, as: * * F1 text for F1 * s:F2 text for Shift-F2 */ #ifndef PATH_MAX # define PATH_MAX 300 #endif static char fkey_filename[PATH_MAX] = ""; static struct fkey_struct { struct fkey_struct *next; char *value; } *fkey_list = NULL; static void ReadFuncionKeys(void) { FILE *fp; char str[200], key[200]; struct fkey_struct *sp = NULL, *new_node; char len; int val; const char *home; /* If KeyFile is not started with "/", consider the filename is relative to $HOME */ /* and put value of the $HOME environment variable before the KeyFile. */ /* To avoid possible buffer overflow, $HOME will not be added when resulting filename */ /* is too long. */ home = getenv("HOME"); if (appres.key_file[0] != '/' && home != NULL && strlen(home) + strlen(appres.key_file) + 1 < sizeof(fkey_filename)) sprintf(fkey_filename, "%s/%s", home, appres.key_file); else strncpy(fkey_filename, appres.key_file, sizeof(fkey_filename)); strncpy(dict_filename, appres.dict_file, sizeof(dict_filename)); fp = fopen(fkey_filename, "r"); if (fp == NULL) return; while (fgets(str, sizeof(str) - 1, fp)) { if (str[0] == '#') { sscanf(&str[1], "%s %d", key, &val); if (strcmp(key, "quick_modifiers") == 0) appres.quick_modifiers = val; else if (strcmp(key, "shift_lock") == 0) appres.shift_lock = val; else if (strcmp(key, "altgr_lock") == 0) appres.altgr_lock = val; else if (strcmp(key, "modifiers_lock") == 0) appres.modifiers_lock = val; else if (strcmp(key, "key_click") == 0) appres.key_click_duration = val; else if (strcmp(key, "autoclick") == 0) appres.autoclick_delay = val; else if (strcmp(key, "always_on_top") == 0) appres.always_on_top = val; else if (strcmp(key, "wm_toolbar") == 0) appres.wm_toolbar = val; else if (strcmp(key, "jump_pointer") == 0) appres.jump_pointer = val; else if (strcmp(key, "dict_file") == 0) { sscanf(&str[1], "%*s %s", &key); strncpy(dict_filename, key, sizeof(dict_filename)); } } else if (isalpha(str[0])) { len = strlen(str); if (str[len - 1] == '\n') str[len - 1] = '\0'; new_node = malloc(sizeof(struct fkey_struct)); if (fkey_list == NULL) fkey_list = new_node; else sp->next = new_node; sp = new_node; sp->next = NULL; sp->value = XtNewString(str); } } fclose(fp); } /* * Edit string assigned for function keys. * Modifiers (Shift, Ctrl, etc.) can't be handled here. */ static Widget edit_fkey_panel = None; static Widget fkey_menu_button = None; static Widget fkey_value_menu_button = None; static Widget fkey_value_entry = None; static char fkey_value[100] = ""; static char cur_fkey[20] = ""; static char *cur_fkey_value_mode = ""; static void FKeyValueMenuSelected(Widget w, char *key) { char *key1, *cp; if (key[0] == 'c') { cur_fkey_value_mode = "command"; key1 = "*command"; } else { cur_fkey_value_mode = "string"; key1 = "*string"; } XtVaGetValues(XtNameToWidget(fkey_value_menu_button, key1), XtNlabel, &cp, NULL); XtVaSetValues(fkey_value_menu_button, XtNlabel, cp, NULL); } static void FKeyMenuSelected(Widget w, char *key) { struct fkey_struct *sp, *sp2; int len; const char *value, *prefix; char key2[20]; if (key == NULL) strcpy(key2, ""); else if (strncmp(key, "Shift-", strlen("Shift-")) == 0) sprintf(key2, "s:%s", &key[strlen("Shift-")]); else strcpy(key2, key); if (strcmp(cur_fkey, key2) != 0) { if (strlen(cur_fkey) != 0) { len = strlen(cur_fkey); sp2 = NULL; for (sp = fkey_list; sp != NULL; sp = sp->next) { if (strncmp(sp->value, cur_fkey, len) == 0 && isspace(sp->value[len])) break; sp2 = sp; } if (strlen(fkey_value) != 0) { /* assign new string for the function key */ if (sp == NULL) { /* it was not defined before now */ sp = malloc(sizeof(struct fkey_struct)); if (fkey_list == NULL) fkey_list = sp; else sp2->next = sp; sp->next = NULL; sp->value = NULL; } sp->value = realloc(sp->value, len + strlen(fkey_value) + 5); prefix = ""; if (cur_fkey_value_mode[0] == 'c') prefix = "!"; else if (fkey_value[0] == '!') prefix = "\\"; sprintf(sp->value, "%s %s%s", cur_fkey, prefix, fkey_value); } else { /* empty string - remove the entry for the function key */ if (sp != NULL) { if (sp2 != NULL) sp2->next = sp->next; else fkey_list = sp->next; free(sp->value); free(sp); } } } if (key != NULL) { XtVaSetValues(fkey_menu_button, XtNlabel, key, NULL); value = FindFunctionKeyValue(key2, FALSE); if (value == NULL) value = ""; FKeyValueMenuSelected(None, (value[0] == '!') ? "command" : "string"); if (value[0] == '!' || value[0] == '\\') value = value + 1; strncpy(fkey_value, value, sizeof(fkey_value) - 1); XtVaSetValues(fkey_value_entry, XtNstring, fkey_value, NULL); strcpy(cur_fkey, key2); } } } static void CloseFunctionKeyPanel(void) { XtPopdown(edit_fkey_panel); } static void SaveFunctionKey(void) { struct fkey_struct *sp; FILE *fp; if (appres.debug) fprintf(stderr, "SaveFunctionKey\n"); if (edit_fkey_panel != None) FKeyMenuSelected(None, NULL); fp = fopen(fkey_filename, "w"); if (fp == NULL) { fprintf(stderr, "%s: can't open \"%s\": %s\n", PROGRAM_NAME, fkey_filename, strerror(errno)); return; } fprintf(fp, "#quick_modifiers %d\n", appres.quick_modifiers); fprintf(fp, "#shift_lock %d\n", appres.shift_lock); fprintf(fp, "#altgr_lock %d\n", appres.altgr_lock); fprintf(fp, "#modifiers_lock %d\n", appres.modifiers_lock); fprintf(fp, "#key_click %d\n", appres.key_click_duration); fprintf(fp, "#autoclick %d\n", appres.autoclick_delay); fprintf(fp, "#always_on_top %d\n", appres.always_on_top); fprintf(fp, "#wm_toolbar %d\n", appres.wm_toolbar); fprintf(fp, "#jump_pointer %d\n", appres.jump_pointer); fprintf(fp, "#dict_file %s\n", dict_filename); for (sp = fkey_list; sp != NULL; sp = sp->next) { fprintf(fp, "%s\n", sp->value); } fclose(fp); if (edit_fkey_panel != None) CloseFunctionKeyPanel(); } static void PopupFunctionKeyEditor(void) { Widget form, form2, menu, menu_entry, button; char label[20]; char *key; int i, j; if (edit_fkey_panel == None) { edit_fkey_panel = XtVaCreatePopupShell("edit_fkey_panel", transientShellWidgetClass, toplevel, NULL); form = XtVaCreateManagedWidget("form", formWidgetClass, edit_fkey_panel, NULL); form2 = XtVaCreateManagedWidget("form2", formWidgetClass, form, NULL); XtVaCreateManagedWidget("fkey_label", labelWidgetClass, form2, NULL); fkey_menu_button = XtVaCreateManagedWidget("fkey_menu", menuButtonWidgetClass, form2, NULL); menu = XtVaCreatePopupShell("menu", simpleMenuWidgetClass, fkey_menu_button, NULL); for (j = 0; j <= 1; j++) { for (i = 1; i <= appres.editable_function_keys; i++) { if (j == 0) sprintf(label, "F%d", i); else sprintf(label, "Shift-F%d", i); key = XtNewString(label); menu_entry = XtVaCreateManagedWidget(key, smeBSBObjectClass, menu, NULL); XtAddCallback(menu_entry, XtNcallback, (XtCallbackProc)FKeyMenuSelected, (XtPointer)key); } } fkey_value_menu_button = XtVaCreateManagedWidget("fkey_value_menu", menuButtonWidgetClass, form2, NULL); menu = XtVaCreatePopupShell("menu", simpleMenuWidgetClass, fkey_value_menu_button, NULL); menu_entry = XtVaCreateManagedWidget("string", smeBSBObjectClass, menu, NULL); XtAddCallback(menu_entry, XtNcallback, (XtCallbackProc)FKeyValueMenuSelected, (XtPointer)"string"); menu_entry = XtVaCreateManagedWidget("command", smeBSBObjectClass, menu, NULL); XtAddCallback(menu_entry, XtNcallback, (XtCallbackProc)FKeyValueMenuSelected, (XtPointer)"command"); XtVaCreateManagedWidget("fkey_value_sep", labelWidgetClass, form2, NULL); fkey_value_entry = XtVaCreateManagedWidget("fkey_value", asciiTextWidgetClass, form2, XtNuseStringInPlace, True, XtNeditType, XawtextEdit, XtNstring, fkey_value, XtNlength, sizeof(fkey_value) - 1, NULL); button = XtVaCreateManagedWidget("save_button", commandWidgetClass, form, NULL); XtAddCallback(button, XtNcallback, (XtCallbackProc)SaveFunctionKey, NULL); button = XtVaCreateManagedWidget("close_button", commandWidgetClass, form, NULL); XtAddCallback(button, XtNcallback, (XtCallbackProc)CloseFunctionKeyPanel, NULL); XtRealizeWidget(edit_fkey_panel); XSetWMProtocols(dpy, XtWindow(edit_fkey_panel), &wm_delete_window, 1); XtSetKeyboardFocus(edit_fkey_panel, fkey_value_entry); FKeyMenuSelected(None, "F1"); } XtPopup(edit_fkey_panel, XtGrabNone); } /* * If text is assigned to the specified function key, * return the text. Otherwise, return NULL. */ static const char *FindFunctionKeyValue(const char *key, Boolean shiftable) { char label[50]; char prefix; struct fkey_struct *sp; int len; prefix = '\0'; if (shiftable) { if (shift_state & meta_mask) prefix = 'm'; else if (shift_state & alt_mask) prefix = 'a'; else if (shift_state & ControlMask) prefix = 'c'; else if (shift_state & ShiftMask) prefix = 's'; } if (prefix == '\0') sprintf(label, "%s", key); else sprintf(label, "%c:%s", prefix, key); len = strlen(label); for (sp = fkey_list; sp != NULL; sp = sp->next) { if (strncmp(sp->value, label, len) == 0 && isspace(sp->value[len])) return &(sp->value[len + 1]); } return NULL; } /* * Key click */ void KeyClick(void) { XKeyboardState ks; XKeyboardControl kc; if (0 < appres.key_click_duration) { XGetKeyboardControl(dpy, &ks); kc.bell_duration = ks.bell_duration; kc.bell_pitch = appres.key_click_pitch; kc.bell_duration = appres.key_click_duration; XChangeKeyboardControl(dpy, KBBellPitch | KBBellDuration, &kc); XBell(dpy, 0); XSync(dpy, FALSE); kc.bell_pitch = ks.bell_pitch; kc.bell_duration = ks.bell_duration; XChangeKeyboardControl(dpy, KBBellPitch | KBBellDuration, &kc); XSync(dpy, FALSE); } } /* * Display balloon message for the function keys, * if text is assigned to the key. */ static Boolean balloon_panel_open = FALSE; static Widget balloon_panel = None; static XtIntervalId autoclick_id = (XtIntervalId)0; static void StopAutoclick(void) { if (autoclick_id != (XtIntervalId)0) { if (appres.debug) fprintf(stderr, "StopAutoclick: %lx\n", (long)autoclick_id); XtRemoveTimeOut(autoclick_id); autoclick_id = (XtIntervalId)0; } } static void Autoclick(void) { StopAutoclick(); XTestFakeButtonEvent(target_dpy, 1, True, CurrentTime); XTestFakeButtonEvent(target_dpy, 1, False, CurrentTime); } static void ShowBalloon(Widget w, XEvent *event, String *pars, Cardinal *n_pars) { static Widget message; Position x, y; Dimension ht; const char *value; if (strcmp(XtName(w), "MainMenu") == 0) { value = "Main menu"; } else { if (0 < appres.autoclick_delay) { autoclick_id = XtAppAddTimeOut(app_con, (long)appres.autoclick_delay, (XtTimerCallbackProc)Autoclick, (XtPointer)w); if (appres.debug) fprintf(stderr, "ShowBalloon: auto click triggerd: %lx, %d\n", (long)autoclick_id, appres.autoclick_delay); } value = FindFunctionKeyValue(XtName(w), TRUE); if (value == NULL) return; } if (balloon_panel == None) { balloon_panel = XtVaCreatePopupShell("balloon_panel", transientShellWidgetClass, toplevel, XtNoverrideRedirect, TRUE, NULL); message = XtVaCreateManagedWidget("message", labelWidgetClass, balloon_panel, NULL); } XtVaGetValues(w, XtNheight, &ht, NULL); XtTranslateCoords(w, 6, ht + 2, &x, &y); XtVaSetValues(balloon_panel, XtNx, x, XtNy, y, NULL); if (value[0] == '!') { if (appres.secure) return; XtVaSetValues(message, XtNlabel, value + 1, XtNbackground, appres.launch_balloon_background, NULL); } else { if (value[0] == '\\') value = value + 1; XtVaSetValues(message, XtNlabel, value, XtNbackground, appres.balloon_background, NULL); } XtPopup(balloon_panel, XtGrabNone); balloon_panel_open = TRUE; } static void CloseBalloon(Widget w, XEvent *event, String *pars, Cardinal *n_pars) { StopAutoclick(); if (balloon_panel_open) { XtPopdown(balloon_panel); balloon_panel_open = FALSE; } } /* * Set icon image. */ static void SetIconBitmap(Widget w) { #include "xvkbd_icon.xbm" #include "xvkbd_iconmask.xbm" Pixmap icon_pixmap, icon_mask; icon_pixmap = XCreateBitmapFromData(XtDisplay(w), XtWindow(w), (char *)xvkbd_icon_bits, xvkbd_icon_width, xvkbd_icon_height);; icon_mask = XCreateBitmapFromData(XtDisplay(w), XtWindow(w), (char *)xvkbd_iconmask_bits, xvkbd_iconmask_width, xvkbd_iconmask_height); XtVaSetValues(w, XtNiconPixmap, icon_pixmap, XtNiconMask, icon_mask, NULL); } /* * Callback for VisibilityChanged event, which will be invoked * when xvkbd window is hidden by other window. ** EXPERIMENTAL ** */ static void VisibilityChanged(Widget w, XEvent *event, String *pars, Cardinal *n_pars) { static cnt = 0; static time_t t1 = 0; time_t t2; if (!appres.always_on_top) return; if (balloon_panel_open) return; if (main_menu != None && XtWindow(main_menu) != None) { XWindowAttributes attr; XGetWindowAttributes(dpy, XtWindow(main_menu), &attr); if (attr.map_state != IsUnmapped) return; } t2 = time(NULL); if (t1 != t2) cnt = 0; t1 = t2; cnt = cnt + 1; if (appres.debug) fprintf(stderr, "%s: visibility of the window changed (cnt = %d)\n", PROGRAM_NAME, cnt); if (cnt < 5) XRaiseWindow(XtDisplay(toplevel), XtWindow(toplevel)); } /* * The main program. */ int main(int argc, char *argv[]) { static XtActionsRec actions[] = { { "DeleteWindowProc", DeleteWindowProc }, { "WindowResized", WindowResized }, { "VisibilityChanged", VisibilityChanged }, { "ReadKeymap", (XtActionProc)ReadKeymap }, { "ButtonDownAction", ButtonDownAction }, { "ButtonUpAction", ButtonUpAction }, { "ShowBalloon", ShowBalloon }, { "CloseBalloon", CloseBalloon }, { "ClosePopupPanel", (XtActionProc)ClosePopupPanel }, }; static String fallback_resources[] = { #include "XVkbd-common.h" NULL, }; Boolean open_keypad_panel = FALSE; char ch; Window child; int op, ev, err; argc1 = argc; argv1 = malloc(sizeof(char *) * (argc1 + 5)); memcpy(argv1, argv, sizeof(char *) * argc1); argv1[argc1] = NULL; #ifdef USE_I18N XtSetLanguageProc(NULL, NULL, NULL); #endif toplevel = XtVaAppInitialize(NULL, "XVkbd", options, XtNumber(options), &argc, argv, fallback_resources, NULL); dpy = XtDisplay(toplevel); app_con = XtWidgetToApplicationContext(toplevel); XtAppAddActions(app_con, actions, XtNumber(actions)); target_dpy = dpy; if (1 < argc) { fprintf(stderr, "%s: illegal option: %s\n\n", PROGRAM_NAME, argv[1]); } XtGetApplicationResources(toplevel, &appres, application_resources, XtNumber(application_resources), NULL, 0); if (appres.version) { fprintf(stdout, "%s\n", appres.description); exit(1); } if (appres.compact) { appres.keypad = FALSE; appres.function_key = FALSE; } if (appres.keypad_only && !appres.keypad) { appres.keypad_only = FALSE; open_keypad_panel = TRUE; } if (appres.no_sync) { XSync(dpy, FALSE); XSetErrorHandler(MyErrorHandler); } if (0 < strlen(appres.window) || 0 < strlen(appres.instance)) { if (strcmp(appres.window, "root") == 0) { focused_window = RootWindow(dpy, DefaultScreen(dpy)); } else if (sscanf(appres.window, "0x%lX%c", &focused_window, &ch) != 1) { if (sscanf(appres.window, "%ld%c", &focused_window, &ch) != 1) { focused_window = FindWindow(RootWindow(dpy, DefaultScreen(dpy)), appres.window); if (focused_window == None) { fprintf(stderr, "%s: no such window: window=%s and class=%s\n", PROGRAM_NAME, appres.window, appres.instance); if (appres.no_root) exit(-1); } } } } focused_subwindow = focused_window; ReadKeymap(); if (!altgr_mask && appres.auto_add_keysym) AddModifier(XK_Mode_switch); if (strlen(appres.text) != 0 || strlen(appres.file) != 0) { appres.keypad_keysym = TRUE; if (focused_window != None && (appres.list_widgets || strlen(appres.widget) != 0)) { XtVaSetValues(toplevel, XtNwidth, 1, XtNheight, 1, NULL); XtRealizeWidget(toplevel); child = FindWidget(toplevel, focused_window, appres.widget); if (child != None) focused_subwindow = child; } if (strlen(appres.text) != 0) SendString(appres.text); else SendFileContent(appres.file); exit(0); } else { ReadFuncionKeys(); if (0 < strlen(appres.keys_normal)) RedefineKeys(keys_normal, appres.keys_normal); if (0 < strlen(appres.keys_shift)) RedefineKeys(keys_shift, appres.keys_shift); if (0 < strlen(appres.keys_altgr)) RedefineKeys(keys_altgr, appres.keys_altgr); if (0 < strlen(appres.keys_shift_altgr)) RedefineKeys(keys_shift_altgr, appres.keys_shift_altgr); if (0 < strlen(appres.key_labels)) RedefineKeys(key_labels, appres.key_labels); if (0 < strlen(appres.normal_key_labels)) RedefineKeys(normal_key_labels, appres.normal_key_labels); if (0 < strlen(appres.shift_key_labels)) RedefineKeys(shift_key_labels, appres.shift_key_labels); if (0 < strlen(appres.altgr_key_labels)) RedefineKeys(altgr_key_labels, appres.altgr_key_labels); if (0 < strlen(appres.shift_altgr_key_labels)) RedefineKeys(shift_altgr_key_labels, appres.shift_altgr_key_labels); if (0 < strlen(appres.keypad_normal)) { RedefineKeys(keypad, appres.keypad_normal); RedefineKeys(keypad_shift, appres.keypad_normal); RedefineKeys(keypad_label, appres.keypad_normal); } if (0 < strlen(appres.keypad_shift)) RedefineKeys(keypad_shift, appres.keypad_shift); if (0 < strlen(appres.keypad_labels)) RedefineKeys(keypad_label, appres.keypad_labels); MakeKeyboard(FALSE); if (focused_window != None && (appres.list_widgets || strlen(appres.widget) != 0)) { child = FindWidget(toplevel, focused_window, appres.widget); if (child != None) focused_subwindow = child; } if (main_menu != None) { if (strlen(dict_filename) == 0) XtSetSensitive(XtNameToWidget(main_menu, "*completion"), FALSE); if (strlen(appres.customizations) == 0) XtSetSensitive(XtNameToWidget(main_menu, "*select_layout"), FALSE); if (appres.nonexitable) XtSetSensitive(XtNameToWidget(main_menu, "*quit"), FALSE); if (appres.secure) { XtSetSensitive(XtNameToWidget(main_menu, "*man"), FALSE); XtSetSensitive(XtNameToWidget(main_menu, "*open_display"), FALSE); } } #ifdef USE_XTEST if (!XQueryExtension(dpy, "XTEST", &op, &ev, &err)) { if (appres.xtest) { fprintf(stderr, "%s: XTEST extension is not supported by the X server\n", PROGRAM_NAME); fprintf(stderr, "%s: XSendEvent will be used instead\n", PROGRAM_NAME); appres.xtest = FALSE; } if (main_menu != None) { XtSetSensitive(XtNameToWidget(main_menu, "*use_xtest"), FALSE); RefreshMainMenu(); } } #endif if (!appres.debug) { #ifdef SYSV signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); #else struct sigaction sigact; sigact.sa_handler = SIG_IGN; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigaction(SIGINT, &sigact, NULL); sigaction(SIGQUIT, &sigact, NULL); #endif } { #ifdef SYSV signal(SIGUSR1, SignalUser1); #else struct sigaction sigact; sigact.sa_handler = SignalUser1; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigaction(SIGUSR1, &sigact, NULL); #endif } SetIconBitmap(toplevel); if (open_keypad_panel) MenuSelected(None, "keypad"); XtAppMainLoop(app_con); } exit(0); } /* * Replace setlocale() in the standard library here, because * it may not support some locales used for localized keyboards. */ #if defined(USE_I18N) && !defined(HAVE_SETLOCALE) char *setlocale(int category, const char *locale) { static char old_locale[100] = "C"; static char cur_locale[100] = "C"; const char *s; if (locale == NULL) { return cur_locale; } else if (category == LC_ALL) { strcpy(old_locale, cur_locale); if (locale[0] == '\0') { s = getenv("LC_ALL"); if (s == NULL) s = "C"; /* LC_ALL not defined */ } else { s = locale; } strncpy(cur_locale, s, sizeof(cur_locale) - 1); return old_locale; } else { return cur_locale; } } #endif /* HAVE_SETLOCALE */