mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-11-25 02:49:46 +00:00
252606610c
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@37519 a592a061-630c-0410-9148-cb99ea01b6c8
3712 lines
123 KiB
C
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 */
|