lyx_mirror/development/autotests/xvkbd/xvkbd.c
Tommaso Cucinotta 252606610c Added -wait_idle option to wait for the target process to idle at each key press.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@37519 a592a061-630c-0410-9148-cb99ea01b6c8
2011-02-06 22:47:33 +00:00

3712 lines
123 KiB
C

/*
* xvkbd - Virtual Keyboard for X Window System
* (Version 3.2, 2010-03-14)
*
* Copyright (C) 2000-2010 by Tom Sato <VEF00200@nifty.ne.jp>
* 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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <time.h>
#include <fnmatch.h>
#include <limits.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/keysym.h>
#include <X11/cursorfont.h>
#include <X11/Xproto.h> /* to get request code */
#include <X11/Xaw/Box.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Repeater.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/MenuButton.h>
#include <X11/Xaw/SimpleMenu.h>
#include <X11/Xaw/SmeBSB.h>
#include <X11/Xaw/SmeLine.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/Viewport.h>
#include <X11/Xaw/List.h>
#include <X11/Xaw/Toggle.h>
#include <X11/Xmu/WinUtil.h>
#include <X11/Xatom.h>
#ifdef USE_I18N
# include <X11/Xlocale.h>
#endif
#ifdef USE_XTEST
# include <X11/extensions/XTest.h>
#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 <VEF00200@nifty.ne.jp>\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 */