2 * xvkbd - Virtual Keyboard for X Window System
3 * (Version 3.2, 2010-03-14)
5 * Copyright (C) 2000-2010 by Tom Sato <VEF00200@nifty.ne.jp>
6 * http://homepage3.nifty.com/tsato/xvkbd/
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 * See the GNU General Public License for more details.
29 #include <X11/Intrinsic.h>
30 #include <X11/StringDefs.h>
31 #include <X11/Shell.h>
32 #include <X11/keysym.h>
33 #include <X11/cursorfont.h>
34 #include <X11/Xproto.h> /* to get request code */
35 #include <X11/Xaw/Box.h>
36 #include <X11/Xaw/Form.h>
37 #include <X11/Xaw/Command.h>
38 #include <X11/Xaw/Repeater.h>
39 #include <X11/Xaw/Label.h>
40 #include <X11/Xaw/MenuButton.h>
41 #include <X11/Xaw/SimpleMenu.h>
42 #include <X11/Xaw/SmeBSB.h>
43 #include <X11/Xaw/SmeLine.h>
44 #include <X11/Xaw/AsciiText.h>
45 #include <X11/Xaw/Viewport.h>
46 #include <X11/Xaw/List.h>
47 #include <X11/Xaw/Toggle.h>
48 #include <X11/Xmu/WinUtil.h>
49 #include <X11/Xatom.h>
52 # include <X11/Xlocale.h>
56 # include <X11/extensions/XTest.h>
59 #include "resources.h"
60 #define PROGRAM_NAME_WITH_VERSION "xvkbd (v3.2)"
63 * Default keyboard layout is hardcoded here.
64 * Layout of the main keyboard can be redefined by resources.
66 #define NUM_KEY_ROWS 25
67 #define NUM_KEY_COLS 25
69 static char *keys_normal[NUM_KEY_ROWS][NUM_KEY_COLS] = {
70 { "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "BackSpace" },
71 { "Escape", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\\", "`" },
72 { "Tab", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "Delete" },
73 { "Control_L", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "Return" },
74 { "Shift_L", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "Multi_key", "Shift_R" },
75 { "MainMenu", "Caps_Lock", "Alt_L", "Meta_L", "space", "Meta_R", "Alt_R",
76 "Left", "Right", "Up", "Down", "Focus" },
78 static char *keys_shift[NUM_KEY_ROWS][NUM_KEY_COLS] = {
79 { "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "BackSpace" },
80 { "Escape", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "|", "~" },
81 { "Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{", "}", "Delete" },
82 { "Control_L", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "Return" },
83 { "Shift_L", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "Multi_key", "Shift_R" },
84 { "MainMenu", "Caps_Lock", "Alt_L", "Meta_L", "space", "Meta_R", "Alt_R",
85 "Left", "Right", "Up", "Down", "Focus" },
87 static char *keys_altgr[NUM_KEY_ROWS][NUM_KEY_COLS] = { { NULL } };
88 static char *keys_shift_altgr[NUM_KEY_ROWS][NUM_KEY_COLS] = { { NULL } };
90 static char *key_labels[NUM_KEY_ROWS][NUM_KEY_COLS] = {
91 { "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "Backspace" },
92 { "Esc", "!\n1", "@\n2", "#\n3", "$\n4", "%\n5", "^\n6",
93 "&\n7", "*\n8", "(\n9", ")\n0", "_\n-", "+\n=", "|\n\\", "~\n`" },
94 { "Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{\n[", "}\n]", "Del" },
95 { "Control", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":\n;", "\"\n'", "Return" },
96 { "Shift", "Z", "X", "C", "V", "B", "N", "M", "<\n,", ">\n.", "?\n/", "Com\npose", "Shift" },
97 { "MainMenu", "Caps\nLock", "Alt", "Meta", "", "Meta", "Alt",
98 "left", "right", "up", "down", "Focus" },
100 static char *normal_key_labels[NUM_KEY_ROWS][NUM_KEY_COLS] = {
101 { "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "Backspace" },
102 { "Esc", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "\\", "`" },
103 { "Tab", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "Del" },
104 { "Ctrl", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "Return" },
105 { "Shift", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "Comp", "Shift" },
106 { "MainMenu", "Caps", "Alt", "Meta", "", "Meta", "Alt",
107 "left", "right", "up", "down", "Focus" },
109 static char *shift_key_labels[NUM_KEY_ROWS][NUM_KEY_COLS] = {
110 { "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "Backspace" },
111 { "Esc", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "|", "~" },
112 { "Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{", "}", "Del" },
113 { "Ctrl", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "Return" },
114 { "Shift", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "Comp", "Shift" },
115 { "MainMenu", "Caps", "Alt", "Meta", "", "Meta", "Alt",
116 "left", "right", "up", "down", "Focus" },
118 static char *altgr_key_labels[NUM_KEY_ROWS][NUM_KEY_COLS] = { { NULL } };
119 static char *shift_altgr_key_labels[NUM_KEY_ROWS][NUM_KEY_COLS] = { { NULL } };
122 #define NUM_KEYPAD_ROWS NUM_KEY_ROWS
123 #define NUM_KEYPAD_COLS NUM_KEY_COLS
125 static char *keypad[NUM_KEYPAD_ROWS][NUM_KEYPAD_COLS] = {
126 { "Num_Lock", "KP_Divide", "KP_Multiply", "Focus" },
127 { "Home", "Up", "Page_Up", "KP_Add" },
128 { "Left", "5", "Right", "KP_Subtract" },
129 { "End", "Down", "Page_Down", "KP_Enter" },
130 { "Insert", "Delete" },
132 static char *keypad_shift[NUM_KEYPAD_ROWS][NUM_KEYPAD_COLS] = {
133 { "Num_Lock", "KP_Divide", "KP_Multiply", "Focus" },
134 { "KP_7", "KP_8", "KP_9", "KP_Add" },
135 { "KP_4", "KP_5", "KP_6", "KP_Subtract" },
136 { "KP_1", "KP_2", "KP_3", "KP_Enter" },
139 static char *keypad_label[NUM_KEYPAD_ROWS][NUM_KEYPAD_COLS] = {
140 { "Num\nLock", "/", "*", "Focus" },
141 { "7\nHome", "8\nUp ", "9\nPgUp", "+" },
142 { "4\nLeft", "5\n ", "6\nRight", "-" },
143 { "1\nEnd ", "2\nDown", "3\nPgDn", "Enter" },
144 { "0\nIns ", ".\nDel " },
147 #define NUM_SUN_FKEY_ROWS 6
148 #define NUM_SUN_FKEY_COLS 3
150 static char *sun_fkey[NUM_SUN_FKEY_ROWS][NUM_SUN_FKEY_COLS] = {
158 static char *sun_fkey_label[NUM_SUN_FKEY_ROWS][NUM_SUN_FKEY_COLS] = {
159 { "Stop \nL1", "Again\nL2" },
160 { "Props\nL3", "Undo \nL4" },
161 { "Front\nL5", "Copy \nL6" },
162 { "Open \nL7", "Paste\nL8" },
163 { "Find \nL9", "Cut \nL10" },
168 * Image for arrow keys
172 static unsigned char up_bits[] = {
173 0x08, 0x1c, 0x1c, 0x3e, 0x2a, 0x49, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
177 #define down_height 13
178 static unsigned char down_bits[] = {
179 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x49, 0x2a, 0x3e, 0x1c, 0x1c,
182 #define left_width 13
183 #define left_height 13
184 static unsigned char left_bits[] = {
185 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x18, 0x00, 0x0e, 0x00,
186 0xff, 0x1f, 0x0e, 0x00, 0x18, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
189 #define right_width 13
190 #define right_height 13
191 static unsigned char right_bits[] = {
192 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x03, 0x00, 0x0e,
193 0xff, 0x1f, 0x00, 0x0e, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
196 #define check_width 16
197 #define check_height 16
198 static unsigned char check_bits[] = {
199 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1e, 0x08, 0x0f,
200 0x8c, 0x07, 0xde, 0x03, 0xfe, 0x03, 0xfc, 0x01, 0xf8, 0x00, 0xf0, 0x00,
201 0x70, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00};
203 #define back_width 18
204 #define back_height 13
205 static unsigned char back_bits[] = {
206 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00,
207 0x78, 0x00, 0x00, 0xfe, 0xff, 0x03, 0xff, 0xff, 0x03, 0xfe, 0xff, 0x03,
208 0x78, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
212 * Resources and options
214 #define Offset(entry) XtOffset(struct appres_struct *, entry)
215 static XtResource application_resources[] = {
216 { "description", "Description", XtRString, sizeof(char *),
217 Offset(description), XtRImmediate,
218 PROGRAM_NAME_WITH_VERSION " - virtual keyboard for X window system\n\n"
219 "Copyright (C) 2000-2010 by Tom Sato <VEF00200@nifty.ne.jp>\n"
220 "http://homepage3.nifty.com/tsato/xvkbd/\n\n"
221 "This program is free software with ABSOLUTELY NO WARRANTY,\n"
222 "distributed under the terms of the GNU General Public License.\n" },
223 { "showManualCommand", "ShowManualCommand", XtRString, sizeof(char *),
224 Offset(show_manual_command), XtRImmediate, "xterm -e man xvkbd &" },
226 { "windowGeometry", "Geometry", XtRString, sizeof(char *),
227 Offset(geometry), XtRImmediate, "" },
228 { "inheritGeoemetry", "Inherit", XtRBoolean, sizeof(Boolean),
229 Offset(inherit_geometry), XtRImmediate, (XtPointer)TRUE },
230 { "debug", "Debug", XtRBoolean, sizeof(Boolean),
231 Offset(debug), XtRImmediate, (XtPointer)FALSE },
232 { "version", "Version", XtRBoolean, sizeof(Boolean),
233 Offset(version), XtRImmediate, (XtPointer)FALSE },
235 { "xtest", "XTest", XtRBoolean, sizeof(Boolean),
236 Offset(xtest), XtRImmediate, (XtPointer)TRUE },
238 { "xtest", "XTest", XtRBoolean, sizeof(Boolean),
239 Offset(xtest), XtRImmediate, (XtPointer)FALSE },
241 { "noSync", "NoSync", XtRBoolean, sizeof(Boolean),
242 Offset(no_sync), XtRImmediate, (XtPointer)FALSE },
243 { "alwaysOnTop", "AlwaysOnTop", XtRBoolean, sizeof(Boolean),
244 Offset(always_on_top), XtRImmediate, (XtPointer)FALSE },
245 { "wmToolbar", "WmToolbar", XtRBoolean, sizeof(Boolean),
246 Offset(wm_toolbar), XtRImmediate, (XtPointer)FALSE },
247 { "jumpPointer", "JumpPointer", XtRBoolean, sizeof(Boolean),
248 Offset(jump_pointer), XtRImmediate, (XtPointer)TRUE },
249 { "jumpPointerAlways", "JumpPointer", XtRBoolean, sizeof(Boolean),
250 Offset(jump_pointer_always), XtRImmediate, (XtPointer)TRUE },
251 { "jumpPointerBack", "JumpPointer", XtRBoolean, sizeof(Boolean),
252 Offset(jump_pointer_back), XtRImmediate, (XtPointer)TRUE },
253 { "quickModifiers", "QuickModifiers", XtRBoolean, sizeof(Boolean),
254 Offset(quick_modifiers), XtRImmediate, (XtPointer)TRUE },
255 { "altgrLock", "ModifiersLock", XtRBoolean, sizeof(Boolean),
256 Offset(altgr_lock), XtRImmediate, (XtPointer)FALSE },
257 { "shiftLock", "ModifiersLock", XtRBoolean, sizeof(Boolean),
258 Offset(shift_lock), XtRImmediate, (XtPointer)FALSE },
259 { "modifiersLock", "ModifiersLock", XtRBoolean, sizeof(Boolean),
260 Offset(modifiers_lock), XtRImmediate, (XtPointer)FALSE },
261 { "numLockState", "NumLockState", XtRBoolean, sizeof(Boolean),
262 Offset(num_lock_state), XtRImmediate, (XtPointer)TRUE },
263 { "autoRepeat", "AutoRepeat", XtRBoolean, sizeof(Boolean),
264 Offset(auto_repeat), XtRImmediate, (XtPointer)TRUE },
265 { "modalKeytop", "ModalKeytop", XtRBoolean, sizeof(Boolean),
266 Offset(modal_keytop), XtRImmediate, (XtPointer)FALSE },
267 { "minimizable", "Minimizable", XtRBoolean, sizeof(Boolean),
268 Offset(minimizable), XtRImmediate, (XtPointer)FALSE },
269 { "secure", "Secure", XtRBoolean, sizeof(Boolean),
270 Offset(secure), XtRImmediate, (XtPointer)FALSE },
271 { "no_root", "NoRoot", XtRBoolean, sizeof(Boolean),
272 Offset(no_root), XtRImmediate, (XtPointer)FALSE },
273 { "wait_idle", "Text", XtRString, sizeof(char *),
274 Offset(wait_idle), XtRImmediate, "" },
275 { "nonexitable", "Secure", XtRBoolean, sizeof(Boolean),
276 Offset(nonexitable), XtRImmediate, (XtPointer)FALSE },
277 { "modalKeytop", "ModalKeytop", XtRBoolean, sizeof(Boolean),
278 Offset(modal_keytop), XtRImmediate, (XtPointer)FALSE },
279 { "modalThreshold", "ModalThreshold", XtRInt, sizeof(int),
280 Offset(modal_threshold), XtRImmediate, (XtPointer)150 },
281 { "keypad", "Keypad", XtRBoolean, sizeof(Boolean),
282 Offset(keypad), XtRImmediate, (XtPointer)TRUE },
283 { "functionkey", "FunctionKey", XtRBoolean, sizeof(Boolean),
284 Offset(function_key), XtRImmediate, (XtPointer)TRUE },
285 { "compact", "Compact", XtRBoolean, sizeof(Boolean),
286 Offset(compact), XtRImmediate, (XtPointer)FALSE },
287 { "keypadOnly", "KeypadOnly", XtRBoolean, sizeof(Boolean),
288 Offset(keypad_only), XtRImmediate, (XtPointer)FALSE },
289 { "keypadKeysym", "KeypadKeysym", XtRBoolean, sizeof(Boolean),
290 Offset(keypad_keysym), XtRImmediate, (XtPointer)FALSE },
291 { "autoAddKeysym", "AutoAddKeysym", XtRBoolean, sizeof(Boolean),
292 Offset(auto_add_keysym), XtRImmediate, (XtPointer)TRUE },
293 { "listWidgets", "Debug", XtRBoolean, sizeof(Boolean),
294 Offset(list_widgets), XtRImmediate, (XtPointer)FALSE },
295 { "positiveModifiers", "PositiveModifiers", XtRString, sizeof(char *),
296 Offset(positive_modifiers), XtRImmediate, "" },
297 { "text", "Text", XtRString, sizeof(char *),
298 Offset(text), XtRImmediate, "" },
299 { "file", "File", XtRString, sizeof(char *),
300 Offset(file), XtRImmediate, "" },
301 { "window", "Window", XtRString, sizeof(char *),
302 Offset(window), XtRImmediate, "" },
303 { "instance", "Instance", XtRString, sizeof(char *),
304 Offset(instance), XtRImmediate, "" },
305 { "widget", "Widget", XtRString, sizeof(char *),
306 Offset(widget), XtRImmediate, "" },
307 { "generalFont", XtCFont, XtRFontStruct, sizeof(XFontStruct *),
308 Offset(general_font), XtRString, XtDefaultFont},
309 { "letterFont", XtCFont, XtRFontStruct, sizeof(XFontStruct *),
310 Offset(letter_font), XtRString, XtDefaultFont},
311 { "specialFont", XtCFont, XtRFontStruct, sizeof(XFontStruct *),
312 Offset(special_font), XtRString, XtDefaultFont},
313 { "keypadFont", XtCFont, XtRFontStruct, sizeof(XFontStruct *),
314 Offset(keypad_font), XtRString, XtDefaultFont},
315 { "generalBackground", XtCBackground, XtRPixel, sizeof(Pixel),
316 Offset(general_background), XtRString, "gray" },
317 { "specialBackground", XtCBackground, XtRPixel, sizeof(Pixel),
318 Offset(special_background), XtRString, "gray" },
319 { "specialForeground", XtCForeground, XtRPixel, sizeof(Pixel),
320 Offset(special_foreground), XtRString, "black" },
322 { "specialFontSet", XtCFontSet, XtRFontSet, sizeof(XFontSet),
323 Offset(special_fontset), XtRString, XtDefaultFontSet},
325 { "highlightBackground", XtCBackground, XtRPixel, sizeof(Pixel),
326 Offset(highlight_background), XtRString, "gray" },
327 { "highlightForeground", XtCForeground, XtRPixel, sizeof(Pixel),
328 Offset(highlight_foreground), XtRString, "forestgreen" },
329 { "focusBackground", XtCBackground, XtRPixel, sizeof(Pixel),
330 Offset(focus_background), XtRString, "gray" },
331 { "remoteFocusBackground", XtCBackground, XtRPixel, sizeof(Pixel),
332 Offset(remote_focus_background), XtRString, "cyan" },
333 { "balloonBackground", XtCBackground, XtRPixel, sizeof(Pixel),
334 Offset(balloon_background), XtRString, "LightYellow1" },
335 { "launchBalloonBackground", XtCBackground, XtRPixel, sizeof(Pixel),
336 Offset(launch_balloon_background), XtRString, "SkyBlue1" },
338 { "normalkeys", "NormalKeys", XtRString, sizeof(char *),
339 Offset(keys_normal), XtRImmediate, "" },
340 { "shiftkeys", "ShiftKeys", XtRString, sizeof(char *),
341 Offset(keys_shift), XtRImmediate, "" },
342 { "altgrkeys", "AltgrKeys", XtRString, sizeof(char *),
343 Offset(keys_altgr), XtRImmediate, "" },
344 { "shiftaltgrkeys", "ShiftAltgrKeys", XtRString, sizeof(char *),
345 Offset(keys_shift_altgr), XtRImmediate, "" },
346 { "keylabels", "KeyLabels", XtRString, sizeof(char *),
347 Offset(key_labels), XtRImmediate, "" },
348 { "normalkeylabels", "NormalKeyLabels", XtRString, sizeof(char *),
349 Offset(normal_key_labels), XtRImmediate, "" },
350 { "shiftkeylabels", "ShiftKeyLabels", XtRString, sizeof(char *),
351 Offset(shift_key_labels), XtRImmediate, "" },
352 { "altgrkeylabels", "AltgrKeyLabels", XtRString, sizeof(char *),
353 Offset(altgr_key_labels), XtRImmediate, "" },
354 { "shiftaltgrkeylabels", "ShiftAltgrKeyLabels", XtRString, sizeof(char *),
355 Offset(shift_altgr_key_labels), XtRImmediate, "" },
357 { "normalkeypad", "NormalKeypad", XtRString, sizeof(char *),
358 Offset(keypad_normal), XtRImmediate, "" },
359 { "shiftkeypad", "ShiftKeypad", XtRString, sizeof(char *),
360 Offset(keypad_shift), XtRImmediate, "" },
361 { "keypad_labels", "KeypadLabels", XtRString, sizeof(char *),
362 Offset(keypad_labels), XtRImmediate, "" },
364 { "deadkeys", "DeadKeys", XtRString, sizeof(char *),
365 Offset(deadkeys), XtRImmediate, "" },
366 { "altgrKeycode", "AltgrKeycode", XtRInt, sizeof(int),
367 Offset(altgr_keycode), XtRImmediate, (XtPointer)0 },
369 { "keyFile", "KeyFile", XtRString, sizeof(char *),
370 Offset(key_file), XtRImmediate, ".xvkbd" },
371 { "dictFile", "DictFile", XtRString, sizeof(char *),
372 Offset(dict_file), XtRImmediate, "/usr/share/dict/words" },
373 { "customizations", "Customizations", XtRString, sizeof(char *),
374 Offset(customizations), XtRImmediate, "default" },
375 { "editableFunctionKeys", "EditableFunctionKeys", XtRInt, sizeof(int),
376 Offset(editable_function_keys), XtRImmediate, (XtPointer)12 },
378 { "maxWidthRatio", "MaxRatio", XtRFloat, sizeof(float),
379 Offset(max_width_ratio), XtRString, "0.9" },
380 { "maxHeightRatio", "MaxRatio", XtRFloat, sizeof(float),
381 Offset(max_height_ratio), XtRString, "0.5" },
382 { "textDelay", "TextDelay", XtRInt, sizeof(int),
383 Offset(text_delay), XtRImmediate, (XtPointer)0 },
385 { "keyClickPitch", "KeyClickPitch", XtRInt, sizeof(int),
386 Offset(key_click_pitch), XtRImmediate, (XtPointer)1000 },
387 { "keyClickDuration", "KeyClickDuration", XtRInt, sizeof(int),
388 Offset(key_click_duration), XtRImmediate, (XtPointer)1 },
389 { "autoClickDelay", "AutoClickDelay", XtRInt, sizeof(int),
390 Offset(autoclick_delay), XtRImmediate, (XtPointer)0 },
394 static XrmOptionDescRec options[] = {
395 { "-geometry", ".windowGeometry", XrmoptionSepArg, NULL },
396 { "-windowgeometry", ".windowGeometry", XrmoptionSepArg, NULL },
397 { "-debug", ".debug", XrmoptionNoArg, "True" },
399 { "-xtest", ".xtest", XrmoptionNoArg, "True" },
400 { "-xsendevent", ".xtest", XrmoptionNoArg, "False" },
401 { "-no-jump-pointer", ".jumpPointer", XrmoptionNoArg, "False" },
402 { "-no-back-pointer", ".jumpPointerBack", XrmoptionNoArg, "False" },
404 { "-no-sync", ".noSync", XrmoptionNoArg, "True" },
405 { "-always-on-top", ".alwaysOnTop", XrmoptionNoArg, "True" }, /* EXPERIMENTAL */
406 { "-quick", ".quickModifiers", XrmoptionNoArg, "True" },
407 { "-modifiers", ".positiveModifiers", XrmoptionSepArg, NULL },
408 { "-text", ".text", XrmoptionSepArg, NULL },
409 { "-file", ".file", XrmoptionSepArg, NULL },
410 { "-delay", ".textDelay", XrmoptionSepArg, NULL },
411 { "-window", ".window", XrmoptionSepArg, NULL },
412 { "-instance", ".instance", XrmoptionSepArg, NULL },
413 { "-widget", ".widget", XrmoptionSepArg, NULL },
414 { "-altgr-lock", ".altgrLock", XrmoptionNoArg, "True" },
415 { "-no-altgr-lock", ".altgrLock", XrmoptionNoArg, "False" },
416 { "-no-repeat", ".autoRepeat", XrmoptionNoArg, "False" },
417 { "-norepeat", ".autoRepeat", XrmoptionNoArg, "False" },
418 { "-no-keypad", ".keypad", XrmoptionNoArg, "False" },
419 { "-nokeypad", ".keypad", XrmoptionNoArg, "False" },
420 { "-no-functionkey", ".functionkey", XrmoptionNoArg, "False" },
421 { "-nofunctionkey", ".functionkey", XrmoptionNoArg, "False" },
422 { "-highlight", ".highlightForeground", XrmoptionSepArg, NULL },
423 { "-compact", ".compact", XrmoptionNoArg, "True" },
424 { "-keypad", ".keypadOnly", XrmoptionNoArg, "True" },
425 { "-true-keypad", ".keypadKeysym", XrmoptionNoArg, "True" },
426 { "-truekeypad", ".keypadKeysym", XrmoptionNoArg, "True" },
427 { "-no-add-keysym", ".autoAddKeysym", XrmoptionNoArg, "False" },
428 { "-altgr-keycode", ".altgrKeycode", XrmoptionSepArg, NULL },
429 { "-list", ".listWidgets", XrmoptionNoArg, "True" },
430 { "-modal", ".modalKeytop", XrmoptionNoArg, "True" },
431 { "-minimizable", ".minimizable", XrmoptionNoArg, "True" },
432 { "-secure", ".secure", XrmoptionNoArg, "True" },
433 { "-no_root", ".no_root", XrmoptionNoArg, "True" },
434 { "-wait_idle", ".wait_idle", XrmoptionSepArg, NULL },
435 { "-nonexitable", ".nonexitable", XrmoptionNoArg, "True" },
436 { "-xdm", ".Secure", XrmoptionNoArg, "True" },
437 { "-dict", ".dictFile", XrmoptionSepArg, NULL },
438 { "-keyfile", ".keyFile", XrmoptionSepArg, NULL },
439 { "-customizations", ".customizations", XrmoptionSepArg, NULL },
440 { "-version", ".version", XrmoptionNoArg, "True" },
441 { "-help", ".version", XrmoptionNoArg, "True" },
447 static char dict_filename[PATH_MAX] = "";
452 static XtAppContext app_con;
453 static Widget toplevel;
454 static Widget key_widgets[NUM_KEY_ROWS][NUM_KEY_COLS];
455 static Widget main_menu = None;
457 static Dimension toplevel_height = 1000;
460 static Atom wm_delete_window = None;
462 static KeySym *keysym_table = NULL;
463 static int min_keycode, max_keycode;
464 static int keysym_per_keycode;
465 static Boolean error_detected;
467 static int alt_mask = 0;
468 static int meta_mask = 0;
469 static int altgr_mask = 0;
470 static KeySym altgr_keysym = NoSymbol;
472 static int shift_state = 0;
473 static int mouse_shift = 0;
475 static Display *target_dpy = NULL;
477 static Window toplevel_parent = None;
478 static Window focused_window = None;
479 static Window focused_subwindow = None;
481 static Pixmap xvkbd_pixmap = None;
483 static int AddKeysym(KeySym keysym, Boolean top); /* forward */
484 static void SendString(const unsigned char *str);
485 static void MakeKeyboard(Boolean remake);
486 static void MakeKeypad(Widget form, Widget from_vert, Widget from_horiz);
487 static void MakeSunFunctionKey(Widget form, Widget from_vert, Widget from_horiz);
488 static void MakeDeadkeyPanel(Widget form);
489 static void RefreshMainMenu(void);
490 static void PopupFunctionKeyEditor(void);
491 static void DeleteWindowProc(Widget w, XEvent *event, String *pars, Cardinal *n_pars);
494 * Search for window which has specified instance name (WM_NAME)
495 * or class name (WM_CLASS).
497 static Window FindWindow(Window top, char *name)
500 Window *children, dummy;
501 unsigned int nchildren;
508 if (appres.debug) fprintf(stderr, "FindWindow: id=0x%lX", (long)top);
510 if (XGetClassHint(target_dpy, top, &hint)) {
512 if (appres.debug) fprintf(stderr, " instance=\"%s\"", hint.res_name);
513 if ((strlen(name) > 0 && fnmatch(name, hint.res_name, 0) == 0)
514 || (strlen(appres.instance) > 0 && fnmatch(appres.instance, hint.res_name, 0) == 0)) w = top;
515 XFree(hint.res_name);
517 if (strlen(name) > 0 && hint.res_class) {
518 if (appres.debug) fprintf(stderr, " class=\"%s\"", hint.res_class);
519 if (strlen(name) > 0 && fnmatch(name, hint.res_class, 0) == 0) w = top;
520 XFree(hint.res_class);
523 if (XFetchName(target_dpy, top, &win_name)) { /* window title */
524 if (appres.debug) fprintf(stderr, " title=\"%s\"", win_name);
525 if (strlen(name) > 0 && fnmatch(name, win_name, 0) == 0) w = top;
529 if (appres.debug) fprintf(stderr, "%s\n", (w == None) ? "" : " [matched]");
532 XQueryTree(target_dpy, top, &dummy, &dummy, &children, &nchildren)) {
533 for (i = 0; i < nchildren; i++) {
534 w = FindWindow(children[i], name);
535 if (w != None) break;
537 if (children) XFree((char *)children);
544 * This will be called to get window to set input focus,
545 * when user pressed the "Focus" button.
547 static void GetFocusedWindow(void)
551 Window target_root, child;
556 int cur_x, cur_y, last_x, last_y;
557 double x_ratio, y_ratio;
560 target_root = RootWindow(target_dpy, DefaultScreen(target_dpy));
562 cursor = XCreateFontCursor(dpy, (target_dpy == dpy) ? XC_crosshair : XC_dot);
563 if (XGrabPointer(dpy, RootWindow(dpy, DefaultScreen(dpy)), False, ButtonPressMask,
564 GrabModeSync, GrabModeAsync, None,
565 cursor, CurrentTime) == 0) {
567 fprintf(stderr, "Grab pointer - waiting for button press\n");
570 x_ratio = ((double)WidthOfScreen(DefaultScreenOfDisplay(target_dpy))
571 / WidthOfScreen(XtScreen(toplevel)));
572 y_ratio = ((double)HeightOfScreen(DefaultScreenOfDisplay(target_dpy))
573 / HeightOfScreen(XtScreen(toplevel)));
575 XAllowEvents(dpy, SyncPointer, CurrentTime);
576 if (target_dpy == dpy) {
577 XNextEvent(dpy, &event);
579 XCheckTypedEvent(dpy, ButtonPress, &event);
580 if (XQueryPointer(dpy, RootWindow(dpy, DefaultScreen(dpy)), &junk_w, &junk_w,
581 &cur_x, &cur_y, &junk_i, &junk_i, &junk_u)) {
582 cur_x = cur_x * x_ratio;
583 cur_y = cur_y * y_ratio;
585 if (cur_x != last_x || cur_y != last_y) {
586 if (appres.debug) fprintf(stderr, "Moving pointer to (%d, %d) on %s\n",
587 cur_x, cur_y, XDisplayString(target_dpy));
588 XWarpPointer(target_dpy, None, target_root, 0, 0, 0, 0, cur_x, cur_y);
592 XQueryPointer(target_dpy, target_root, &junk_w, &child,
593 &cur_x, &cur_y, &junk_i, &junk_i, &junk_u);
599 } while (event.type != ButtonPress);
600 XUngrabPointer(dpy, CurrentTime);
602 focused_window = None;
603 if (target_dpy == dpy) focused_window = event.xbutton.subwindow;
604 if (focused_window == None) {
606 for (scrn = 0; scrn < ScreenCount(target_dpy); scrn++) {
607 if (XQueryPointer(target_dpy, RootWindow(target_dpy, scrn), &junk_w, &child,
608 &junk_i, &junk_i, &junk_i, &junk_i, &junk_u)) {
610 fprintf(stderr, "Window on the other display/screen (screen #%d of %s) focused\n",
611 scrn, XDisplayString(target_dpy));
612 target_root = RootWindow(target_dpy, scrn);
613 focused_window = child;
618 if (focused_window == None) focused_window = target_root;
619 else focused_window = XmuClientWindow(target_dpy, focused_window);
620 if (appres.debug) fprintf(stderr, "Selected window is: 0x%lX on %s\n",
621 focused_window, XDisplayString(target_dpy));
623 if (target_dpy == dpy && XtWindow(toplevel) == focused_window) {
624 focused_window = None;
625 focused_subwindow = focused_window;
629 focused_subwindow = focused_window;
630 do { /* search the child window */
631 XQueryPointer(target_dpy, focused_subwindow, &junk_w, &child,
632 &junk_i, &junk_i, &junk_i, &junk_i, &junk_u);
634 focused_subwindow = child;
635 if (appres.debug) fprintf(stderr, " going down: 0x%lX\n", focused_subwindow);
637 } while (child != None);
638 if (appres.list_widgets || strlen(appres.widget) != 0) {
639 child = FindWidget(toplevel, focused_window, appres.widget);
640 if (child != None) focused_subwindow = child;
643 fprintf(stderr, "%s: cannot grab pointer\n", PROGRAM_NAME);
648 * Read keyboard mapping and modifier mapping.
649 * Keyboard mapping is used to know what keys are in shifted position.
650 * Modifier mapping is required because we should know Alt and Meta
651 * key are used as which modifier.
653 static void Highlight(char *name, Boolean state);
655 static void ReadKeymap(void)
658 int keycode, inx, pos;
660 XModifierKeymap *modifiers;
664 XDisplayKeycodes(target_dpy, &min_keycode, &max_keycode);
665 if (keysym_table != NULL) XFree(keysym_table);
666 keysym_table = XGetKeyboardMapping(target_dpy,
667 min_keycode, max_keycode - min_keycode + 1,
668 &keysym_per_keycode);
669 for (keycode = min_keycode; keycode <= max_keycode; keycode++) {
670 /* if the first keysym is alphabet and the second keysym is NoSymbol,
671 it is equivalent to pair of lowercase and uppercase alphabet */
672 inx = (keycode - min_keycode) * keysym_per_keycode;
673 if (keysym_table[inx + 1] == NoSymbol
674 && ((XK_A <= keysym_table[inx] && keysym_table[inx] <= XK_Z)
675 || (XK_a <= keysym_table[inx] && keysym_table[inx] <= XK_z))) {
676 if (XK_A <= keysym_table[inx] && keysym_table[inx] <= XK_Z)
677 keysym_table[inx] = keysym_table[inx] - XK_A + XK_a;
678 keysym_table[inx + 1] = keysym_table[inx] - XK_a + XK_A;
682 last_altgr_mask = altgr_mask;
686 altgr_keysym = NoSymbol;
687 modifiers = XGetModifierMapping(target_dpy);
688 for (i = 0; i < 8; i++) {
689 for (pos = 0; pos < modifiers->max_keypermod; pos++) {
690 keycode = modifiers->modifiermap[i * modifiers->max_keypermod + pos];
691 if (keycode < min_keycode || max_keycode < keycode) continue;
693 keysym = keysym_table[(keycode - min_keycode) * keysym_per_keycode];
694 if (keysym == XK_Alt_L || keysym == XK_Alt_R) {
696 } else if (keysym == XK_Meta_L || keysym == XK_Meta_R) {
698 } else if (keysym == XK_Mode_switch) {
700 fprintf(stderr, "%s: found Mode_switch at %dth modifier\n", PROGRAM_NAME, i);
701 if (altgr_keysym == XK_ISO_Level3_Shift) {
703 fprintf(stderr, "%s: both ISO_Level3_Shift and Mode_switch found - ISO_Level3_Shift prefered\n", PROGRAM_NAME);
705 altgr_mask = 0x0101 << i;
706 /* I don't know why, but 0x2000 was required for mod3 on my Linux box */
707 altgr_keysym = keysym;
709 } else if (keysym == XK_ISO_Level3_Shift) {
710 /* if no Mode_switch, try to use ISO_Level3_Shift instead */
711 /* however, it may not work as intended - I don't know why */
713 fprintf(stderr, "%s: found ISO_Level3_Shift at %dth modifier\n", PROGRAM_NAME, i);
714 if (altgr_keysym == XK_Mode_switch) {
716 fprintf(stderr, "%s: both ISO_Level3_Shift and Mode_switch found - ISO_Level3_Shift prefered\n", PROGRAM_NAME);
719 altgr_keysym = keysym;
723 XFreeModifiermap(modifiers);
725 if (altgr_keysym != XK_Mode_switch) {
726 fprintf(stderr, "%s: Mode_switch not available as a modifier\n", PROGRAM_NAME);
727 if (altgr_keysym == XK_ISO_Level3_Shift)
728 fprintf(stderr, "%s: although ISO_Level3_Shift is used instead, AltGr may not work correctly\n", PROGRAM_NAME);
730 fprintf(stderr, "%s: AltGr can't be used\n", PROGRAM_NAME);
733 w = XtNameToWidget(toplevel, "*Multi_key");
735 if (XKeysymToKeycode(target_dpy, XK_Multi_key) == NoSymbol) {
736 if (!appres.auto_add_keysym || AddKeysym(XK_Multi_key, FALSE) == NoSymbol)
737 XtSetSensitive(w, FALSE);
740 w = XtNameToWidget(toplevel, "*Mode_switch");
742 if (appres.xtest && 0 < appres.altgr_keycode) {
743 XtSetSensitive(w, TRUE);
745 fprintf(stderr, "%s: keycode %d will be used for AltGr - it was specified with altgrKeycode\n",
746 PROGRAM_NAME, appres.altgr_keycode);
747 } else if (altgr_mask) {
748 XtSetSensitive(w, TRUE);
750 XtSetSensitive(w, FALSE);
751 if (shift_state & last_altgr_mask) {
752 shift_state &= ~last_altgr_mask;
753 Highlight("Mode_switch", FALSE);
760 * This will called when X error is detected when attempting to
761 * send a event to a client window; this will normally caused
762 * when the client window is destroyed.
764 static int MyErrorHandler(Display *my_dpy, XErrorEvent *event)
768 error_detected = TRUE;
769 if (event->error_code == BadWindow) {
771 fprintf(stderr, "%s: BadWindow - couldn't find target window 0x%lX (destroyed?)\n",
772 PROGRAM_NAME, (long)focused_window);
775 XGetErrorText(my_dpy, event->error_code, msg, sizeof(msg) - 1);
776 fprintf(stderr, "X error trapped: %s, request-code=%d\n", msg, event->request_code);
781 * Send event to the focused window.
782 * If input focus is specified explicitly, select the window
783 * before send event to the window.
785 static void SendEvent(XKeyEvent *event)
787 static Boolean first = TRUE;
789 if (!appres.no_sync) {
790 XSync(event->display, FALSE);
791 XSetErrorHandler(MyErrorHandler);
794 error_detected = FALSE;
795 if (focused_window != None) {
796 /* set input focus if input focus is set explicitly */
798 fprintf(stderr, "Set input focus to window 0x%lX (0x%lX)\n",
799 (long)focused_window, (long)event->window);
800 XSetInputFocus(event->display, focused_window, RevertToParent, CurrentTime);
801 if (!appres.no_sync) XSync(event->display, FALSE);
803 if (!error_detected) {
807 fprintf(stderr, "XTestFakeKeyEvent(0x%lx, %ld, %d)\n",
808 (long)event->display, (long)event->keycode, event->type == KeyPress);
809 if (appres.jump_pointer) {
810 Window root, child, w;
811 int root_x, root_y, x, y;
816 if (first || strlen(appres.text) == 0 || appres.jump_pointer_back) {
819 w = focused_subwindow;
820 if (w == None && appres.jump_pointer_always)
821 XGetInputFocus(event->display, &w, &revert_to);
825 fprintf(stderr, "SendEvent: jump pointer to window 0x%lx\n", (long)w);
827 XQueryPointer(event->display, w,
828 &root, &child, &root_x, &root_y, &x, &y, &mask);
829 XWarpPointer(event->display, None, w, 0, 0, 0, 0, 1, 1);
830 XFlush(event->display);
834 XTestFakeKeyEvent(event->display, event->keycode, event->type == KeyPress, 0);
835 XFlush(event->display);
837 if (w != None && appres.jump_pointer_back) {
838 XWarpPointer(event->display, None, root, 0, 0, 0, 0, root_x, root_y);
839 XFlush(event->display);
842 XTestFakeKeyEvent(event->display, event->keycode, event->type == KeyPress, 0);
843 XFlush(event->display);
846 fprintf(stderr, "%s: this binary is compiled without XTEST support\n",
850 XSendEvent(event->display, event->window, TRUE, KeyPressMask, (XEvent *)event);
851 if (!appres.no_sync) XSync(event->display, FALSE);
854 && (focused_subwindow != None) && (focused_subwindow != event->window)) {
855 error_detected = FALSE;
856 event->window = focused_subwindow;
858 fprintf(stderr, " retry: send event to window 0x%lX (0x%lX)\n",
859 (long)focused_window, (long)event->window);
860 XSendEvent(event->display, event->window, TRUE, KeyPressMask, (XEvent *)event);
861 if (!appres.no_sync) XSync(event->display, FALSE);
866 if (error_detected) {
867 /* reset focus because focused window is (probably) no longer exist */
869 focused_window = None;
870 focused_subwindow = None;
873 XSetErrorHandler(NULL);
877 * Insert a specified keysym to unused position in the keymap table.
878 * This will be called to add required keysyms on-the-fly.
879 * if the second parameter is TRUE, the keysym will be added to the
880 * non-shifted position - this may be required for modifier keys
881 * (e.g. Mode_switch) and some special keys (e.g. F20).
883 static int AddKeysym(KeySym keysym, Boolean top)
885 int keycode, pos, max_pos, inx, phase;
890 max_pos = keysym_per_keycode - 1;
891 if (4 <= max_pos) max_pos = 3;
892 if (2 <= max_pos && altgr_keysym != XK_Mode_switch) max_pos = 1;
895 for (phase = 0; phase < 2; phase++) {
896 for (keycode = max_keycode; min_keycode <= keycode; keycode--) {
897 for (pos = max_pos; 0 <= pos; pos--) {
898 inx = (keycode - min_keycode) * keysym_per_keycode;
899 if ((phase != 0 || keysym_table[inx] == NoSymbol) && keysym_table[inx] < 0xFF00) {
900 /* In the first phase, to avoid modifing existing keys, */
901 /* add the keysym only to the keys which has no keysym in the first position. */
902 /* If no place fuond in the first phase, add the keysym for any keys except */
903 /* for modifier keys and other special keys */
904 if (keysym_table[inx + pos] == NoSymbol) {
906 fprintf(stderr, "*** Adding keysym \"%s\" at keycode %d position %d/%d\n",
907 XKeysymToString(keysym), keycode, pos, keysym_per_keycode);
908 keysym_table[inx + pos] = keysym;
909 XChangeKeyboardMapping(target_dpy, keycode, keysym_per_keycode, &keysym_table[inx], 1);
917 fprintf(stderr, "%s: couldn't add \"%s\" to keymap\n",
918 PROGRAM_NAME, XKeysymToString(keysym));
924 * Add the specified key as a new modifier.
925 * This is used to use Mode_switch (AltGr) as a modifier.
927 static void AddModifier(KeySym keysym)
929 XModifierKeymap *modifiers;
932 keycode = XKeysymToKeycode(target_dpy, keysym);
933 if (keycode == NoSymbol) keycode = AddKeysym(keysym, TRUE);
935 modifiers = XGetModifierMapping(target_dpy);
936 for (i = 7; 3 < i; i--) {
937 if (modifiers->modifiermap[i * modifiers->max_keypermod] == NoSymbol
938 || ((keysym_table[(modifiers->modifiermap[i * modifiers->max_keypermod]
939 - min_keycode) * keysym_per_keycode]) == XK_ISO_Level3_Shift
940 && keysym == XK_Mode_switch)) {
941 for (pos = 0; pos < modifiers->max_keypermod; pos++) {
942 if (modifiers->modifiermap[i * modifiers->max_keypermod + pos] == NoSymbol) {
944 fprintf(stderr, "Adding modifier \"%s\" as %dth modifier\n",
945 XKeysymToString(keysym), i);
946 modifiers->modifiermap[i * modifiers->max_keypermod + pos] = keycode;
947 XSetModifierMapping(target_dpy, modifiers);
953 fprintf(stderr, "%s: couldn't add \"%s\" as modifier\n",
954 PROGRAM_NAME, XKeysymToString(keysym));
959 * Send sequence of KeyPressed/KeyReleased events to the focused
960 * window to simulate keyboard. If modifiers (shift, control, etc)
961 * are set ON, many events will be sent.
963 static void SendKeyPressedEvent(KeySym keysym, unsigned int shift)
969 Window root, *children;
970 unsigned int n_children;
974 if (focused_subwindow != None)
975 cur_focus = focused_subwindow;
977 XGetInputFocus(target_dpy, &cur_focus, &revert_to);
981 if ((keysym & ~0x7f) == 0 && isprint(keysym)) ch = keysym;
982 fprintf(stderr, "SendKeyPressedEvent: focus=0x%lX, key=0x%lX (%c), shift=0x%lX\n",
983 (long)cur_focus, (long)keysym, ch, (long)shift);
986 if (XtWindow(toplevel) != None) {
987 if (toplevel_parent == None) {
988 XQueryTree(target_dpy, RootWindow(target_dpy, DefaultScreen(target_dpy)),
989 &root, &toplevel_parent, &children, &n_children);
992 if (cur_focus == None || cur_focus == PointerRoot
993 || cur_focus == XtWindow(toplevel) || cur_focus == toplevel_parent) {
994 /* notice user when no window focused or the xvkbd window is focused */
1002 if (keysym != NoSymbol) {
1003 for (phase = 0; phase < 2; phase++) {
1004 for (keycode = min_keycode; !found && (keycode <= max_keycode); keycode++) {
1005 /* Determine keycode for the keysym: we use this instead
1006 of XKeysymToKeycode() because we must know shift_state, too */
1007 inx = (keycode - min_keycode) * keysym_per_keycode;
1008 if (keysym_table[inx] == keysym) {
1009 shift &= ~altgr_mask;
1010 if (keysym_table[inx + 1] != NoSymbol) shift &= ~ShiftMask;
1013 } else if (keysym_table[inx + 1] == keysym) {
1014 shift &= ~altgr_mask;
1020 if (!found && altgr_mask && 3 <= keysym_per_keycode) {
1021 for (keycode = min_keycode; !found && (keycode <= max_keycode); keycode++) {
1022 inx = (keycode - min_keycode) * keysym_per_keycode;
1023 if (keysym_table[inx + 2] == keysym) {
1024 shift &= ~ShiftMask;
1025 shift |= altgr_mask;
1028 } else if (4 <= keysym_per_keycode && keysym_table[inx + 3] == keysym) {
1029 shift |= ShiftMask | altgr_mask;
1035 if (found || !appres.auto_add_keysym) break;
1037 if (0xF000 <= keysym) {
1038 /* for special keys such as function keys,
1039 first try to add it in the non-shifted position of the keymap */
1040 if (AddKeysym(keysym, TRUE) == NoSymbol) AddKeysym(keysym, FALSE);
1042 AddKeysym(keysym, FALSE);
1047 fprintf(stderr, "SendKeyPressedEvent: keysym=0x%lx, keycode=%ld, shift=0x%lX\n",
1048 (long)keysym, (long)keycode, (long)shift);
1050 fprintf(stderr, "SendKeyPressedEvent: keysym=0x%lx - keycode not found\n",
1055 event.display = target_dpy;
1056 event.window = cur_focus;
1057 event.root = RootWindow(event.display, DefaultScreen(event.display));
1058 event.subwindow = None;
1059 event.time = CurrentTime;
1064 event.same_screen = TRUE;
1069 int root_x, root_y, x, y;
1072 XQueryPointer(target_dpy, event.root, &root, &child, &root_x, &root_y, &x, &y, &mask);
1074 event.type = KeyRelease;
1076 if (mask & ControlMask) {
1077 event.keycode = XKeysymToKeycode(target_dpy, XK_Control_L);
1080 if (mask & alt_mask) {
1081 event.keycode = XKeysymToKeycode(target_dpy, XK_Alt_L);
1084 if (mask & meta_mask) {
1085 event.keycode = XKeysymToKeycode(target_dpy, XK_Meta_L);
1088 if (mask & altgr_mask) {
1089 if (0 < appres.altgr_keycode)
1090 event.keycode = appres.altgr_keycode;
1092 event.keycode = XKeysymToKeycode(target_dpy, altgr_keysym);
1095 if (mask & ShiftMask) {
1096 event.keycode = XKeysymToKeycode(target_dpy, XK_Shift_L);
1099 if (mask & LockMask) {
1100 event.keycode = XKeysymToKeycode(target_dpy, XK_Caps_Lock);
1106 event.type = KeyPress;
1108 if (shift & ControlMask) {
1109 event.keycode = XKeysymToKeycode(target_dpy, XK_Control_L);
1111 event.state |= ControlMask;
1113 if (shift & alt_mask) {
1114 event.keycode = XKeysymToKeycode(target_dpy, XK_Alt_L);
1116 event.state |= alt_mask;
1118 if (shift & meta_mask) {
1119 event.keycode = XKeysymToKeycode(target_dpy, XK_Meta_L);
1121 event.state |= meta_mask;
1123 if (shift & altgr_mask) {
1124 if (0 < appres.altgr_keycode)
1125 event.keycode = appres.altgr_keycode;
1127 event.keycode = XKeysymToKeycode(target_dpy, altgr_keysym);
1129 event.state |= altgr_mask;
1131 if (shift & ShiftMask) {
1132 event.keycode = XKeysymToKeycode(target_dpy, XK_Shift_L);
1134 event.state |= ShiftMask;
1137 if (keysym != NoSymbol) { /* send event for the key itself */
1138 event.keycode = found ? keycode : XKeysymToKeycode(target_dpy, keysym);
1139 if (event.keycode == NoSymbol) {
1140 if ((keysym & ~0x7f) == 0 && isprint(keysym))
1141 fprintf(stderr, "%s: no such key: %c\n",
1142 PROGRAM_NAME, (char)keysym);
1143 else if (XKeysymToString(keysym) != NULL)
1144 fprintf(stderr, "%s: no such key: keysym=%s (0x%lX)\n",
1145 PROGRAM_NAME, XKeysymToString(keysym), (long)keysym);
1147 fprintf(stderr, "%s: no such key: keysym=0x%lX\n",
1148 PROGRAM_NAME, (long)keysym);
1152 event.type = KeyRelease;
1157 event.type = KeyRelease;
1158 if (shift & ShiftMask) {
1159 event.keycode = XKeysymToKeycode(target_dpy, XK_Shift_L);
1161 event.state &= ~ShiftMask;
1163 if (shift & altgr_mask) {
1164 if (0 < appres.altgr_keycode)
1165 event.keycode = appres.altgr_keycode;
1167 event.keycode = XKeysymToKeycode(target_dpy, altgr_keysym);
1169 event.state &= ~altgr_mask;
1171 if (shift & meta_mask) {
1172 event.keycode = XKeysymToKeycode(target_dpy, XK_Meta_L);
1174 event.state &= ~meta_mask;
1176 if (shift & alt_mask) {
1177 event.keycode = XKeysymToKeycode(target_dpy, XK_Alt_L);
1179 event.state &= ~alt_mask;
1181 if (shift & ControlMask) {
1182 event.keycode = XKeysymToKeycode(target_dpy, XK_Control_L);
1184 event.state &= ~ControlMask;
1187 if (appres.no_sync) XFlush(dpy);
1191 * Word completion - list of words which match the prefix entered
1192 * via xvkbd can be listed, and choosing one of them will send the
1193 * suffix to the clients.
1194 * Words for completion will be read from dictionary file specified
1195 * with xvkbd.dictFile resource, such as /usr/dict/words.
1197 static Widget completion_panel = None;
1198 static Widget completion_entry = None;
1199 static Widget completion_list = None;
1201 static Widget props_dict_entry = None;
1203 static char completion_text[100] = "";
1205 #define HASH_SIZE 100
1207 #define Hash(str) ((toupper(str[0]) * 26 + toupper(str[1])) % HASH_SIZE)
1209 static struct WORDLIST {
1210 struct WORDLIST *next;
1212 } completion_words[HASH_SIZE];
1213 static int n_completion_words = 0;
1215 #define MAX_WORDS 200
1217 static String word_list[MAX_WORDS + 1];
1218 static int n_word_list = 0;
1220 static void SetDefaultDictionary(void)
1222 strncpy(dict_filename, appres.dict_file, sizeof(dict_filename));
1223 XtVaSetValues(props_dict_entry, XtNstring, dict_filename, NULL);
1226 static void ReadCompletionDictionary(void)
1228 static Boolean first = TRUE;
1229 static char cur_dict_filename[PATH_MAX] = "";
1231 struct WORDLIST *node_ptr;
1236 if (strcmp(cur_dict_filename, dict_filename) == 0) return;
1237 strcpy(cur_dict_filename, dict_filename);
1241 for (i = 0; i < HASH_SIZE; i++) {
1242 while (completion_words[i].next != NULL) {
1243 p = completion_words[i].next;
1244 completion_words[i].next = p->next;
1250 fprintf(stderr, "ReadCompletionDictionary: %d words freed\n", cnt);
1254 for (i = 0; i < HASH_SIZE; i++) {
1255 completion_words[i].next = NULL;
1256 completion_words[i].word = NULL;
1259 n_completion_words = 0;
1260 fp = fopen(dict_filename, "r");
1262 fprintf(stderr, "%s: can't read dictionary file %s: %s\n",
1263 PROGRAM_NAME, dict_filename, strerror(errno));
1265 while (fgets(str, sizeof(str) - 1, fp)) {
1266 if (3 < strlen(str)) {
1267 str[strlen(str) - 1] = '\0';
1268 node_ptr = &completion_words[Hash(str)];
1269 while (node_ptr->word != NULL) node_ptr = node_ptr->next;
1271 node_ptr->word = XtNewString(str);
1272 node_ptr->next = malloc(sizeof(struct WORDLIST));
1273 node_ptr->next->next = NULL;
1274 node_ptr->next->word = NULL;
1275 n_completion_words++;
1281 fprintf(stderr, "ReadCompletionDictionary: %d words allocated\n", n_completion_words);
1285 static void AddToCompletionText(KeySym keysym)
1288 struct WORDLIST *node_ptr;
1290 if (completion_entry != None) {
1292 if (n_completion_words == 0) {
1293 XtVaSetValues(completion_entry, XtNlabel, "Couldn't read dictionary file", NULL);
1297 len = strlen(completion_text);
1298 if (keysym == XK_BackSpace || keysym == XK_Delete) {
1299 if (0 < len) completion_text[len - 1] = '\0';
1300 } else if (keysym != NoSymbol
1301 && !ispunct((char)keysym) && !isspace((char)keysym)) {
1302 if (len < sizeof(completion_text) - 2) {
1303 completion_text[len + 1] = '\0';
1304 completion_text[len] = keysym;
1307 completion_text[0] = '\0';
1309 XtVaSetValues(completion_entry, XtNlabel, completion_text, NULL);
1312 if (2 <= strlen(completion_text)) {
1313 node_ptr = &completion_words[Hash(completion_text)];
1314 while (node_ptr->word != NULL && n_word_list < MAX_WORDS) {
1315 if (strlen(completion_text) + 1 < strlen(node_ptr->word) &&
1316 strncasecmp(node_ptr->word, completion_text, strlen(completion_text)) == 0) {
1317 word_list[n_word_list] = node_ptr->word;
1318 n_word_list = n_word_list + 1;
1320 node_ptr = node_ptr->next;
1323 word_list[n_word_list] = NULL;
1324 XawListChange(completion_list, word_list, 0, 0, TRUE);
1328 static void CompletionWordSelected(Widget w, XtPointer client_data, XtPointer call_data)
1334 n = ((XawListReturnStruct *)call_data)->list_index;
1335 if (0 <= n && n < n_word_list) {
1337 for (i = 0; completion_text[i] != '\0'; i++) {
1338 if (islower(completion_text[i])) capitalize = FALSE;
1340 for (i = strlen(completion_text); word_list[n][i] != '\0'; i++) {
1341 ch = word_list[n][i];
1342 if (capitalize) ch = toupper(ch);
1343 SendKeyPressedEvent(ch, 0);
1346 AddToCompletionText(NoSymbol);
1349 static void PopupCompletionPanel(void)
1351 Widget form, label, view;
1354 if (completion_panel == None) {
1355 completion_panel = XtVaCreatePopupShell("completion_panel", transientShellWidgetClass,
1357 form = XtVaCreateManagedWidget("form", formWidgetClass, completion_panel, NULL);
1358 label = XtVaCreateManagedWidget("label", labelWidgetClass, form, NULL);
1359 completion_entry = XtVaCreateManagedWidget("entry", labelWidgetClass, form,
1360 XtNfromHoriz, label, NULL);
1361 view = XtVaCreateManagedWidget("view", viewportWidgetClass, form,
1362 XtNfromVert, label, NULL);
1363 completion_list = XtVaCreateManagedWidget("list", listWidgetClass, view, NULL);
1364 XtAddCallback(completion_list, XtNcallback, CompletionWordSelected, NULL);
1365 XtRealizeWidget(completion_panel);
1366 XSetWMProtocols(dpy, XtWindow(completion_panel), &wm_delete_window, 1);
1368 XtPopup(completion_panel, XtGrabNone);
1369 AddToCompletionText(NoSymbol);
1372 XtPopup(completion_panel, XtGrabNone);
1375 ReadCompletionDictionary();
1377 sprintf(msg, "%d words in the dictionary", n_completion_words);
1378 XtVaSetValues(completion_entry, XtNlabel, msg, NULL);
1380 completion_text[0] = '\0';
1382 word_list[n_word_list] = NULL;
1383 XawListChange(completion_list, word_list, 0, 0, TRUE);
1387 * Send given string to the focused window as if the string
1388 * is typed from a keyboard.
1390 static void KeyPressed(Widget w, char *key, char *data);
1392 static void SendString(const unsigned char *str)
1394 const unsigned char *cp, *cp2; /* I remember "unsigned" might be required for some systems */
1398 Window target_root, child, junk_w;
1404 for (cp = str; *cp != '\0'; cp++) {
1405 if (0 < appres.text_delay)
1406 usleep(appres.text_delay * 1000);
1407 if (appres.wait_idle && strlen(appres.wait_idle) > 0) {
1408 int pid = atoi(appres.wait_idle);
1412 snprintf(cmd, sizeof(cmd), "/proc/%d/status", pid);
1413 FILE *f = fopen(cmd, "r");
1415 fprintf(stderr, "Process not found: %d\n", pid);
1419 snprintf(cmd, sizeof(cmd), "grep 'State.*running' /proc/%d/status", pid);
1429 fprintf(stderr, "%s: missing character after \"\\\"\n",
1432 case '[': /* we can write any keysym as "\[the-keysym]" here */
1433 cp2 = strchr(cp, ']');
1435 fprintf(stderr, "%s: no closing \"]\" after \"\\[\"\n",
1439 if (sizeof(key) <= len) len = sizeof(key) - 1;
1440 strncpy(key, cp + 1, len);
1442 KeyPressed(None, key, NULL);
1446 case 'S': shift_state |= ShiftMask; break;
1447 case 'C': shift_state |= ControlMask; break;
1448 case 'A': shift_state |= alt_mask; break;
1449 case 'M': shift_state |= meta_mask; break;
1450 case 'b': SendKeyPressedEvent(XK_BackSpace, shift_state); shift_state = 0; break;
1451 case 't': SendKeyPressedEvent(XK_Tab, shift_state); shift_state = 0; break;
1452 case 'n': SendKeyPressedEvent(XK_Linefeed, shift_state); shift_state = 0; break;
1453 case 'r': SendKeyPressedEvent(XK_Return, shift_state); shift_state = 0; break;
1454 case 'e': SendKeyPressedEvent(XK_Escape, shift_state); shift_state = 0; break;
1455 case 'd': SendKeyPressedEvent(XK_Delete, shift_state); shift_state = 0; break;
1456 case 'D': /* delay */
1458 if ('1' <= *cp && *cp <= '9') {
1459 usleep((*cp - '0') * 100000);
1461 fprintf(stderr, "%s: no digit after \"\\m\"\n",
1465 case 'm': /* simulate click mouse button */
1467 if ('1' <= *cp && *cp <= '9') {
1468 if (appres.debug) fprintf(stderr, "XTestFakeButtonEvent(%d)\n", *cp - '0');
1469 XTestFakeButtonEvent(target_dpy, *cp - '0', True, CurrentTime);
1470 XTestFakeButtonEvent(target_dpy, *cp - '0', False, CurrentTime);
1473 fprintf(stderr, "%s: no digit after \"\\m\"\n",
1478 case 'y': /* move mouse pointer */
1479 sscanf(cp + 1, "%d", &val);
1480 target_root = RootWindow(target_dpy, DefaultScreen(target_dpy));
1481 XQueryPointer(target_dpy, target_root, &junk_w, &child,
1482 &cur_x, &cur_y, &junk_i, &junk_i, &junk_u);
1484 if (isdigit(*(cp + 1))) cur_x = val;
1487 if (isdigit(*(cp + 1))) cur_y = val;
1490 XWarpPointer(target_dpy, None, target_root, 0, 0, 0, 0, cur_x, cur_y);
1493 while (isdigit(*(cp + 1)) || *(cp + 1) == '+' || *(cp + 1) == '-') cp++;
1496 SendKeyPressedEvent(*cp, shift_state);
1501 SendKeyPressedEvent(*cp, shift_state);
1508 * Send content of the file as if the it is typed from a keyboard.
1510 static void SendFileContent(const char *file)
1516 if (strcmp(file, "-") != 0) fp = fopen(file, "r");
1519 fprintf(stderr, "%s: can't read the file: %s\n", PROGRAM_NAME, file);
1522 while ((ch = fgetc(fp)) != EOF) {
1523 if (0 < appres.text_delay) usleep(appres.text_delay * 1000);
1524 if (ch == '\n') { /* newline - send Return instead */
1525 SendKeyPressedEvent(XK_Return, 0);
1526 } else if (ch == '\033') { /* ESC */
1527 SendKeyPressedEvent(XK_Escape, 0);
1528 } else if (ch == '\177') { /* DEL */
1529 SendKeyPressedEvent(XK_Delete, 0);
1530 } else if (1 <= ch && ch <= 26) { /* Ctrl-x */
1531 SendKeyPressedEvent('a' + ch - 1, ControlMask);
1532 } else { /* normal characters */
1533 SendKeyPressedEvent(ch, 0);
1536 if (strcmp(file, "-") != 0) fclose(fp);
1540 * Highlight/unhighligh spcified modifier key on the screen.
1542 static void Highlight(char *name, Boolean state)
1547 sprintf(name1, "*%s", name);
1548 w = XtNameToWidget(toplevel, name1);
1550 if (strstr(name, "Focus") != NULL) {
1551 if (target_dpy == dpy)
1552 XtVaSetValues(w, XtNbackground, appres.focus_background, NULL);
1554 XtVaSetValues(w, XtNbackground, appres.remote_focus_background, NULL);
1556 XtVaSetValues(w, XtNforeground, appres.highlight_foreground, NULL);
1558 XtVaSetValues(w, XtNforeground, appres.special_foreground, NULL);
1561 XtVaSetValues(w, XtNbackground, appres.highlight_background,
1562 XtNforeground, appres.highlight_foreground, NULL);
1564 XtVaSetValues(w, XtNbackground, appres.special_background,
1565 XtNforeground, appres.special_foreground, NULL);
1571 * Highlight/unhighligh keys on the screen to reflect the state.
1573 static void RefreshShiftState(Boolean force)
1575 static Boolean first = TRUE;
1576 static int last_shift_state = 0;
1577 static int last_mouse_shift = 0;
1578 static int last_num_lock_state = FALSE;
1579 static Display *last_target_dpy = NULL;
1580 static long last_focus = 0;
1583 int first_row, row, col;
1588 cur_shift = shift_state | mouse_shift;
1589 changed = cur_shift ^ (last_shift_state | last_mouse_shift);
1590 if (first || force) changed = 0xffff;
1592 if (changed & ShiftMask) {
1593 Highlight("Shift_L", cur_shift & ShiftMask);
1594 Highlight("Shift_R", cur_shift & ShiftMask);
1596 if (changed & ControlMask) {
1597 Highlight("Control_L", cur_shift & ControlMask);
1598 Highlight("Control_R", cur_shift & ControlMask);
1600 if (changed & alt_mask) {
1601 Highlight("Alt_L", cur_shift & alt_mask);
1602 Highlight("Alt_R", cur_shift & alt_mask);
1604 if (changed & meta_mask) {
1605 Highlight("Meta_L", cur_shift & meta_mask);
1606 Highlight("Meta_R", cur_shift & meta_mask);
1608 if (changed & LockMask) {
1609 Highlight("Caps_Lock", cur_shift & LockMask);
1611 if (changed & altgr_mask) {
1612 Highlight("Mode_switch", cur_shift & altgr_mask);
1614 if (last_num_lock_state != appres.num_lock_state) {
1615 Highlight("Num_Lock", appres.num_lock_state);
1616 Highlight("keypad_panel*Num_Lock", appres.num_lock_state);
1618 if (last_target_dpy != target_dpy || last_focus != focused_window) {
1619 Highlight("Focus", focused_window != 0);
1620 Highlight("keypad*Focus", focused_window != 0);
1621 Highlight("keypad_panel*Focus", focused_window != 0);
1622 last_target_dpy = target_dpy;
1623 last_focus = focused_window;
1626 mask = ShiftMask | LockMask | altgr_mask;
1627 changed = (shift_state & mask) ^ (last_shift_state & mask);
1628 if (first || force) changed = TRUE;
1629 if (changed && !appres.keypad_only
1630 && (appres.modal_keytop || toplevel_height < appres.modal_threshold)) {
1631 first_row = appres.function_key ? 0 : 1;
1632 for (row = first_row; row < NUM_KEY_ROWS; row++) {
1633 for (col = 0; col < NUM_KEY_COLS; col++) {
1634 shifted = (shift_state & ShiftMask);
1635 if (key_widgets[row][col] != None) {
1636 if ((shift_state & altgr_mask) && altgr_key_labels[row][col] != NULL) {
1637 if (shifted && shift_altgr_key_labels[row][col] != NULL)
1638 label = shift_altgr_key_labels[row][col];
1640 label = altgr_key_labels[row][col];
1642 if ((shift_state & LockMask)
1643 && isalpha(keys_normal[row][col][0]) && keys_normal[row][col][1] == '\0')
1645 if (shifted && shift_key_labels[row][col] != NULL)
1646 label = shift_key_labels[row][col];
1648 label = normal_key_labels[row][col];
1650 if (label == NULL) {
1651 fprintf(stderr, "%s: no label for key %d,%d\n", PROGRAM_NAME, row, col);
1654 if (strcmp(label, "space") == 0) label = "";
1655 XtVaSetValues(key_widgets[row][col], XtNlabel, label, NULL);
1661 last_shift_state = shift_state;
1662 last_mouse_shift = mouse_shift;
1663 last_num_lock_state = appres.num_lock_state;
1667 if (appres.xtest && strlen(appres.positive_modifiers) != 0) {
1668 /* modifiers specified in positiveModifiers resouce will be hold down
1669 so that it can be used with, for example, mouse operations */
1672 int root_x, root_y, x, y;
1677 event.display = target_dpy;
1678 event.window = RootWindow(event.display, DefaultScreen(event.display));
1679 event.root = event.window;
1680 event.subwindow = None;
1681 event.time = CurrentTime;
1686 event.same_screen = TRUE;
1689 XQueryPointer(target_dpy, event.root, &root, &child, &root_x, &root_y, &x, &y, &mask);
1691 if (strstr(appres.positive_modifiers, "shift") != NULL
1692 && (shift_state & ShiftMask) != (mask & ShiftMask)) {
1693 event.keycode = XKeysymToKeycode(target_dpy, XK_Shift_L);
1694 event.type = (shift_state & ShiftMask) ? KeyPress : KeyRelease;
1697 if (strstr(appres.positive_modifiers, "control") != NULL
1698 && (shift_state & ControlMask) != (mask & ControlMask)) {
1699 event.keycode = XKeysymToKeycode(target_dpy, XK_Control_L);
1700 event.type = (shift_state & ControlMask) ? KeyPress : KeyRelease;
1703 if (strstr(appres.positive_modifiers, "alt") != NULL
1704 && (shift_state & alt_mask) != (mask & alt_mask)) {
1705 event.keycode = XKeysymToKeycode(target_dpy, XK_Alt_L);
1706 event.type = (shift_state & alt_mask) ? KeyPress : KeyRelease;
1709 if (strstr(appres.positive_modifiers, "meta") != NULL
1710 && (shift_state & meta_mask) != (mask & meta_mask)) {
1711 event.keycode = XKeysymToKeycode(target_dpy, XK_Meta_L);
1712 event.type = (shift_state & meta_mask) ? KeyPress : KeyRelease;
1721 * This function will be called when mouse button is pressed on a key
1722 * on the screen. Most operation will be performed in KeyPressed()
1723 * which will be called as callback for the Command widgets, and we
1724 * only need to check which mouse button is pressed here.
1726 static unsigned int n_key_repeat;
1728 static void ButtonDownAction(Widget w, XEvent *event, String *pars, Cardinal *n_pars)
1732 switch (event->xbutton.button) {
1734 mouse_shift |= ControlMask;
1738 mouse_shift |= ShiftMask;
1741 RefreshShiftState(FALSE);
1745 * This function will be called when mouse button is released on a key
1746 * on the screen, after callback is called.
1748 static void ButtonUpAction(Widget w, XEvent *event, String *pars, Cardinal *n_pars)
1750 if (appres.quick_modifiers) {
1751 if (n_key_repeat == 1) XtCallCallbacks(w, XtNcallback, NULL);
1754 RefreshShiftState(FALSE);
1758 * Get the geometry of the base window.
1760 static char *GetWindowGeometry(Widget w)
1762 static char geom[50];
1767 unsigned int wd, ht, bd, dp;
1769 XtVaGetValues(w, XtNx, &x0, XtNy, &y0, NULL);
1770 XGetGeometry(dpy, XtWindow(w), &root, &x1, &y1, &wd, &ht, &bd, &dp);
1771 sprintf(geom, "%dx%d+%d+%d", wd, ht, (int)(x0 - x1), (int)(y0 - y1));
1777 * Set window manager hint.
1778 * ("Extended Window Manager Hints", http://standards.freedesktop.org/wm-spec/)
1780 static void SetWindowManagerHint(Boolean initial)
1783 if (appres.wm_toolbar) {
1784 Atom net_wm_window_type = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
1785 Atom net_wm_window_type_toolbar = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_TOOLBAR", False);
1786 XChangeProperty(dpy, XtWindow(toplevel),
1787 net_wm_window_type, XA_ATOM, 32,
1789 (unsigned char *) &net_wm_window_type_toolbar, 1);
1791 fprintf(stderr, "SetWindowManagerHint: set _NET_WM_WINDOW_TYPE_TOOLBAR\n");
1795 if (!initial || appres.always_on_top) {
1796 const int net_wm_state_remove = 0;
1797 const int net_wm_state_add = 1;
1798 Atom net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
1799 Atom net_wm_state_above = XInternAtom(dpy, "_NET_WM_STATE_ABOVE", False);
1800 XClientMessageEvent ev;
1801 ev.type = ClientMessage;
1803 ev.window = XtWindow(toplevel);
1804 ev.message_type = net_wm_state;
1806 ev.data.l[0] = appres.always_on_top ? net_wm_state_add : net_wm_state_remove;
1807 ev.data.l[1] = net_wm_state_above;
1809 XSendEvent(dpy, RootWindow(dpy, DefaultScreen(dpy)),
1810 FALSE, SubstructureNotifyMask | SubstructureRedirectMask,
1813 fprintf(stderr, "SetWindowManagerHint: _NET_WM_STATE_ABOVE = %d\n", ev.data.l[0]);
1818 * Restart the program to (possibly) change the keyboard layout,
1819 * by loading the app-default file for the selected "customization".
1821 static void LayoutSelected(Widget w, char *key, char *data)
1823 static char *env_lang, *env_xenv;
1825 char customization[30] = "", lang[30] = "C";
1831 if (strcmp(key, "default") != 0) {
1832 sscanf(key, "%29[^/]/%29s", customization, lang);
1833 sprintf(name, "XVkbd-%s", customization);
1834 xenv = XtResolvePathname(dpy, "app-defaults", name, NULL, NULL, NULL, 0, NULL);
1836 fprintf(stderr, "%s: app-default file \"%s\" not installed\n",
1837 PROGRAM_NAME, name);
1841 env_lang = malloc(strlen("LC_ALL=") + strlen(lang) + 1);
1842 sprintf(env_lang, "LC_ALL=%s", lang);
1845 env_xenv = malloc(strlen("XENVIRONMENT=") + strlen(xenv) + 1);
1846 sprintf(env_xenv, "XENVIRONMENT=%s", xenv);
1848 } else if (getenv("XENVIRONMENT") != NULL) {
1849 putenv("XENVIRONMENT=");
1853 for (i = 1; i < argc1; i++) {
1854 if (strncmp(argv1[i], "-geom", strlen("-geom")) == 0) {
1855 if (appres.inherit_geometry) {
1856 argv1[i + 1] = GetWindowGeometry(toplevel);
1857 } else if (i + 2 == argc1) {
1864 if (i == argc1 && appres.inherit_geometry) {
1865 argv1[argc1++] = "-geometry";
1866 argv1[argc1++] = GetWindowGeometry(toplevel);
1867 argv1[argc1] = NULL;
1871 fprintf(stderr, "XENVIRONMENT=%s, LC_ALL=%s\n", (xenv != NULL) ? xenv : "", lang);
1872 fprintf(stderr, "Exec:");
1873 for (i = 0; i < argc1; i++) fprintf(stderr, " %s", argv1[i]);
1874 fprintf(stderr, "\n");
1877 execvp(argv1[0], argv1);
1881 * Popup a window to select the (possibly) keyboard layout.
1882 * The "XVkbd.customizations" resource will define the list,
1883 * such as "default,german,swissgerman,french,latin1,jisx6004/ja".
1884 * For example, "german" here will make this program to load
1885 * "XVkbd-german" app-default file. Locale for each configuration
1886 * can be specified by putting the locale name after "/".
1888 static void PopupLayoutPanel(void)
1890 static Widget layout_panel = None;
1892 char *customizations;
1896 if (layout_panel == None) {
1897 layout_panel = XtVaCreatePopupShell("layout_panel", transientShellWidgetClass,
1899 box = XtVaCreateManagedWidget("box", boxWidgetClass, layout_panel, NULL);
1901 customizations = XtNewString(appres.customizations);
1902 cp = strtok(customizations, " \t,");
1903 while (cp != NULL) {
1904 cp2 = strchr(cp, '/');
1905 if (cp2 != NULL) *cp2 = '\0'; /* temporary remove '/' */
1906 button = XtVaCreateManagedWidget(cp, commandWidgetClass, box, NULL);
1907 if (cp2 != NULL) *cp2 = '/';
1908 XtAddCallback(button, XtNcallback, (XtCallbackProc)LayoutSelected, XtNewString(cp));
1909 cp = strtok(NULL, " \t,");
1911 XtRealizeWidget(layout_panel);
1912 XSetWMProtocols(dpy, XtWindow(layout_panel), &wm_delete_window, 1);
1914 XtFree(customizations);
1917 XtPopup(layout_panel, XtGrabNone);
1923 static void SaveFunctionKey(void); /* forward */
1925 static Widget props_panel = None;
1926 static Widget autoclick_buttons = None;
1927 static Widget click_buttons = None;
1928 static Boolean props_panel_active = FALSE;
1930 static void PropsItemToggled(Widget w, char *key, char *data)
1932 Boolean last_wm_toolbar = appres.wm_toolbar;
1934 if (!props_panel_active) return;
1937 XtVaGetValues(XtNameToWidget(props_panel, "*use_xtest"),
1938 XtNstate, &appres.xtest, NULL);
1940 XtVaGetValues(XtNameToWidget(props_panel, "*quick_modifiers"),
1941 XtNstate, &appres.quick_modifiers, NULL);
1942 XtVaGetValues(XtNameToWidget(props_panel, "*shift_lock"),
1943 XtNstate, &appres.shift_lock, NULL);
1944 XtVaGetValues(XtNameToWidget(props_panel, "*altgr_lock"),
1945 XtNstate, &appres.altgr_lock, NULL);
1946 XtVaGetValues(XtNameToWidget(props_panel, "*modifiers_lock"),
1947 XtNstate, &appres.modifiers_lock, NULL);
1948 XtVaGetValues(XtNameToWidget(props_panel, "*always_on_top"),
1949 XtNstate, &appres.always_on_top, NULL);
1950 XtVaGetValues(XtNameToWidget(props_panel, "*wm_toolbar"),
1951 XtNstate, &appres.wm_toolbar, NULL);
1952 XtVaGetValues(XtNameToWidget(props_panel, "*jump_pointer"),
1953 XtNstate, &appres.jump_pointer, NULL);
1955 appres.key_click_duration = (int)XawToggleGetCurrent(click_buttons);
1956 appres.autoclick_delay = (int)XawToggleGetCurrent(autoclick_buttons);
1959 SetWindowManagerHint(FALSE);
1961 if (appres.wm_toolbar != last_wm_toolbar) LayoutSelected(None, NULL, NULL);
1964 static void PropsSetState(void)
1967 XtVaSetValues(XtNameToWidget(props_panel, "*use_xtest"),
1968 XtNstate, appres.xtest, NULL);
1970 XtVaSetValues(XtNameToWidget(props_panel, "*quick_modifiers"),
1971 XtNstate, appres.quick_modifiers, NULL);
1972 XtVaSetValues(XtNameToWidget(props_panel, "*shift_lock"),
1973 XtNstate, appres.shift_lock, NULL);
1974 if (XtNameToWidget(toplevel, "*Mode_switch") == None) {
1975 XtSetSensitive(XtNameToWidget(props_panel, "*altgr_lock"), FALSE);
1976 XtVaSetValues(XtNameToWidget(props_panel, "*altgr_lock"),
1977 XtNstate, FALSE, NULL);
1979 XtVaSetValues(XtNameToWidget(props_panel, "*altgr_lock"),
1980 XtNstate, appres.altgr_lock, NULL);
1982 XtVaSetValues(XtNameToWidget(props_panel, "*modifiers_lock"),
1983 XtNstate, appres.modifiers_lock, NULL);
1984 XtVaSetValues(XtNameToWidget(props_panel, "*always_on_top"),
1985 XtNstate, appres.always_on_top, NULL);
1986 XtVaSetValues(XtNameToWidget(props_panel, "*wm_toolbar"),
1987 XtNstate, appres.wm_toolbar, NULL);
1989 XtVaSetValues(XtNameToWidget(props_panel, "*jump_pointer"),
1990 XtNstate, appres.jump_pointer, NULL);
1992 XawToggleSetCurrent(click_buttons, (XtPointer)appres.key_click_duration);
1993 XawToggleSetCurrent(autoclick_buttons, (XtPointer)appres.autoclick_delay);
1996 static void ClosePropsPanel(void)
1998 XtPopdown(props_panel);
2002 if (completion_panel != None) XtPopdown(completion_panel);
2005 static void PopupPropsPanel(void)
2007 static char *props_items[] = {
2009 "shift_lock", "altgr_lock", "modifiers_lock",
2018 if (props_panel == None) {
2019 Widget label, button;
2024 props_panel = XtVaCreatePopupShell("props_panel", transientShellWidgetClass,
2026 form = XtVaCreateManagedWidget("form", formWidgetClass, props_panel, NULL);
2029 for (i = 0; i < XtNumber(props_items); i++) {
2030 w = XtVaCreateManagedWidget(props_items[i], toggleWidgetClass,
2031 form, XtNfromVert, w, NULL);
2032 XtAddCallback(w, XtNcallback, (XtCallbackProc)PropsItemToggled,
2033 (XtPointer)props_items[i]);
2036 label = XtVaCreateManagedWidget("click", labelWidgetClass,
2037 form, XtNfromVert, w, NULL);
2038 button = XtVaCreateManagedWidget("OFF", toggleWidgetClass,
2039 form, XtNfromVert, w, XtNfromHoriz, label,
2040 XtNwidth, 0, XtNhorizDistance, 0, NULL);
2041 XtVaSetValues(button, XtNradioGroup, button, XtNradioData, (XtPointer)0, NULL);
2042 XtAddCallback(button, XtNcallback, (XtCallbackProc)PropsItemToggled,
2044 click_buttons = button;
2045 for (val = 1; val <= 50; val *= 2) {
2047 sprintf(s1, "%dms", val);
2048 button = XtVaCreateManagedWidget(s1, toggleWidgetClass,
2049 form, XtNfromVert, w, XtNfromHoriz, button,
2050 XtNradioData, (XtPointer)val,
2051 XtNradioGroup, click_buttons,
2052 XtNwidth, 0, XtNhorizDistance, 0, NULL);
2053 XtAddCallback(button, XtNcallback, (XtCallbackProc)PropsItemToggled,
2058 label = XtVaCreateManagedWidget("autoclick", labelWidgetClass,
2059 form, XtNfromVert, w, NULL);
2060 button = XtVaCreateManagedWidget("OFF", toggleWidgetClass,
2061 form, XtNfromVert, w, XtNfromHoriz, label,
2062 XtNwidth, 0, XtNhorizDistance, 0, NULL);
2063 XtVaSetValues(button, XtNradioGroup, button, XtNradioData, (XtPointer)0, NULL);
2064 XtAddCallback(button, XtNcallback, (XtCallbackProc)PropsItemToggled,
2066 autoclick_buttons = button;
2067 for (val = 500; val <= 1000; val += 100) {
2069 sprintf(s1, "%dms", val);
2070 button = XtVaCreateManagedWidget(s1, toggleWidgetClass,
2071 form, XtNfromVert, w, XtNfromHoriz, button,
2072 XtNradioData, (XtPointer)val,
2073 XtNradioGroup, autoclick_buttons,
2074 XtNwidth, 0, XtNhorizDistance, 0, NULL);
2075 XtAddCallback(button, XtNcallback, (XtCallbackProc)PropsItemToggled,
2080 label = XtVaCreateManagedWidget("dict_entry_label", labelWidgetClass,
2081 form, XtNfromVert, w, NULL);
2082 props_dict_entry = XtVaCreateManagedWidget("dict_entry", asciiTextWidgetClass, form,
2083 XtNfromVert, w, XtNfromHoriz, label,
2084 XtNuseStringInPlace, True,
2085 XtNstring, dict_filename,
2086 XtNeditType, XawtextEdit,
2087 XtNlength, sizeof(dict_filename) - 1,
2089 button = XtVaCreateManagedWidget("dict_default_button", commandWidgetClass,
2090 form, XtNfromVert, w, XtNfromHoriz, props_dict_entry,
2092 XtAddCallback(button, XtNcallback, (XtCallbackProc)SetDefaultDictionary, NULL);
2095 w = XtVaCreateManagedWidget("dismiss", commandWidgetClass, form,
2096 XtNfromVert, label, NULL);
2097 XtAddCallback(w, XtNcallback, (XtCallbackProc)ClosePropsPanel, NULL);
2099 XtRealizeWidget(props_panel);
2100 XSetWMProtocols(dpy, XtWindow(props_panel), &wm_delete_window, 1);
2102 XtPopup(props_panel, XtGrabNone);
2105 props_panel_active = TRUE;
2107 if (completion_panel != None) XtPopdown(completion_panel);
2111 * Callback for main menu (activated from "xvkbd" logo).
2113 static Widget about_panel = None;
2114 static Widget keypad_panel = None;
2115 static Widget sun_fkey_panel = None;
2116 static Widget deadkey_panel = None;
2117 static Widget display_panel = None;
2118 static Widget display_status = None;
2120 #define DISPLAY_NAME_LENGTH 50
2122 static void OpenRemoteDisplay(Widget w, char *display_name, char *data)
2124 static char name[DISPLAY_NAME_LENGTH + 10];
2127 focused_window = None;
2128 focused_subwindow = None;
2129 if (target_dpy != NULL && target_dpy != dpy) XCloseDisplay(target_dpy);
2131 strncpy(name, (display_name == NULL) ? "" : display_name, sizeof(name));
2132 for (cp = name; isascii(*cp) && isprint(*cp); cp++) ;
2135 if (strlen(name) == 0) {
2137 XtVaSetValues(display_status, XtNlabel, "Disconnected - local display selected", NULL);
2138 XtPopdown(display_panel);
2140 if (strchr(name, ':') == NULL) strcat(name, ":0");
2141 target_dpy = XOpenDisplay(name);
2142 if (target_dpy == NULL) {
2143 XtVaSetValues(display_status, XtNlabel, "Couldn't connect to the display", NULL);
2147 XtVaSetValues(display_status, XtNlabel, "Connected", NULL);
2148 XtPopdown(display_panel);
2153 if (!altgr_mask && appres.auto_add_keysym) AddModifier(XK_Mode_switch);
2156 RefreshShiftState(FALSE);
2159 static void MenuSelected(Widget w, char *key)
2163 if (strcmp(key, "man") == 0) {
2164 if (!appres.secure) system(appres.show_manual_command);
2165 } else if (strcmp(key, "about") == 0) {
2166 if (about_panel == None) {
2167 about_panel = XtVaCreatePopupShell("about_panel", transientShellWidgetClass,
2169 XtVaCreateManagedWidget("message", labelWidgetClass, about_panel,
2170 XtNlabel, appres.description, NULL);
2171 XtRealizeWidget(about_panel);
2172 XSetWMProtocols(dpy, XtWindow(about_panel), &wm_delete_window, 1);
2174 XtPopup(about_panel, XtGrabNone);
2175 } else if (strcmp(key, "keypad") == 0) {
2176 if (keypad_panel == None) {
2177 keypad_panel = XtVaCreatePopupShell("keypad_panel", transientShellWidgetClass,
2179 form = XtVaCreateManagedWidget("form", formWidgetClass, keypad_panel, NULL);
2180 MakeKeypad(form, None, None);
2181 XtRealizeWidget(keypad_panel);
2182 XSetWMProtocols(dpy, XtWindow(keypad_panel), &wm_delete_window, 1);
2184 XtPopup(keypad_panel, XtGrabNone);
2185 } else if (strcmp(key, "sun_fkey") == 0) {
2186 if (sun_fkey_panel == None) {
2187 sun_fkey_panel = XtVaCreatePopupShell("sun_fkey_panel", transientShellWidgetClass,
2189 form = XtVaCreateManagedWidget("form", formWidgetClass, sun_fkey_panel, NULL);
2190 MakeSunFunctionKey(form, None, None);
2191 XtRealizeWidget(sun_fkey_panel);
2192 XSetWMProtocols(dpy, XtWindow(sun_fkey_panel), &wm_delete_window, 1);
2194 XtPopup(sun_fkey_panel, XtGrabNone);
2195 } else if (strcmp(key, "deadkey") == 0) {
2196 if (deadkey_panel == None) {
2197 deadkey_panel = XtVaCreatePopupShell("deadkey_panel", transientShellWidgetClass,
2199 form = XtVaCreateManagedWidget("form", formWidgetClass, deadkey_panel, NULL);
2200 MakeDeadkeyPanel(form);
2201 XtRealizeWidget(deadkey_panel);
2202 XSetWMProtocols(dpy, XtWindow(deadkey_panel), &wm_delete_window, 1);
2204 XtPopup(deadkey_panel, XtGrabNone);
2205 } else if (strcmp(key, "completion") == 0) {
2206 PopupCompletionPanel();
2207 } else if (strcmp(key, "select_layout") == 0) {
2209 } else if (strcmp(key, "edit_fkey") == 0) {
2210 PopupFunctionKeyEditor();
2211 } else if (strcmp(key, "show_keypad") == 0
2212 || strcmp(key, "show_functionkey") == 0) {
2213 if (strcmp(key, "show_keypad") == 0) appres.keypad = !appres.keypad;
2214 else appres.function_key = !appres.function_key;
2216 } else if (strcmp(key, "props") == 0) {
2218 } else if (strcmp(key, "open_display") == 0) {
2219 if (display_panel == None) {
2220 Widget label, entry, button;
2221 static char display_name[DISPLAY_NAME_LENGTH] = "";
2222 display_panel = XtVaCreatePopupShell("display_panel", transientShellWidgetClass,
2224 form = XtVaCreateManagedWidget("form", formWidgetClass, display_panel, NULL);
2225 label = XtVaCreateManagedWidget("label", labelWidgetClass, form, NULL);
2226 entry = XtVaCreateManagedWidget("entry", asciiTextWidgetClass, form,
2227 XtNfromHoriz, label,
2228 XtNuseStringInPlace, True,
2229 XtNeditType, XawtextEdit,
2230 XtNstring, display_name,
2231 XtNlength, sizeof(display_name) - 1,
2234 button = XtVaCreateManagedWidget("ok", commandWidgetClass, form,
2235 XtNfromHoriz, entry, NULL);
2236 XtAddCallback(button, XtNcallback, (XtCallbackProc)OpenRemoteDisplay, (XtPointer)display_name);
2238 display_status = XtVaCreateManagedWidget("status", labelWidgetClass, form,
2240 XtNlabel, "", NULL);
2241 XtRealizeWidget(display_panel);
2242 XSetWMProtocols(dpy, XtWindow(display_panel), &wm_delete_window, 1);
2244 XtSetKeyboardFocus(display_panel, entry);
2246 XtPopup(display_panel, XtGrabNone);
2247 } else if (strcmp(key, "close_display") == 0) {
2248 OpenRemoteDisplay(None, NULL, NULL);
2249 } else if (strcmp(key, "quit") == 0) {
2250 DeleteWindowProc(None, NULL, NULL, NULL);
2254 static void ClosePopupPanel(Widget w)
2256 if (w == keypad_panel) {
2258 keypad_panel = None;
2259 } else if (w == props_panel) {
2267 * Iconify/uniconify the xvkbd window even if window manager is not
2270 static void IconifyWindow(Widget w, Boolean iconify)
2272 static Widget iconified_window = None;
2273 static Widget uniconify_button = None;
2274 static Position x0, y0;
2276 static unsigned int wd, ht, bd, dp;
2282 XUnmapWindow(dpy, XtWindow(toplevel));
2284 if (iconified_window == None) {
2287 iconified_window = XtVaCreatePopupShell("iconified_window", transientShellWidgetClass,
2288 toplevel, XtNoverrideRedirect, TRUE, NULL);
2289 box = XtVaCreateManagedWidget("form", boxWidgetClass, iconified_window, NULL);
2290 uniconify_button = XtVaCreateManagedWidget("uniconify_button", commandWidgetClass, box,
2291 XtNbitmap, xvkbd_pixmap,
2292 XtNhorizDistance, 10, XtNvertDistance, 0,
2294 XtAddCallback(uniconify_button, XtNcallback, (XtCallbackProc)IconifyWindow, FALSE);
2296 XtRealizeWidget(iconified_window);
2297 XSetWMProtocols(dpy, XtWindow(iconified_window), &wm_delete_window, 1);
2300 XtVaGetValues(toplevel, XtNx, &x0, XtNy, &y0, NULL);
2301 XGetGeometry(dpy, XtWindow(toplevel), &root, &x1, &y1, &wd, &ht, &bd, &dp);
2303 XMoveResizeWindow(dpy, XtWindow(iconified_window), x0 + bd, y0 + bd, wd, ht);
2304 XtPopup(iconified_window, XtGrabNone);
2305 for (i = 9; 0 < i; i--) {
2306 Dimension btn_wd, btn_ht;
2311 XtVaGetValues(uniconify_button, XtNwidth, &btn_wd, XtNheight, &btn_ht, NULL);
2312 if (i == 1 || wd1 < btn_wd) wd1 = btn_wd;
2313 if (i == 1 || ht1 < btn_ht) ht1 = btn_ht;
2314 XMoveResizeWindow(dpy, XtWindow(iconified_window), x0 + bd, y0 + (ht - ht1) + bd, wd1, ht1);
2319 if (iconified_window != None) XtPopdown(iconified_window);
2320 XMapWindow(dpy, XtWindow(toplevel));
2324 static void SignalUser1(void)
2326 XWindowAttributes attr;
2327 XGetWindowAttributes(dpy, XtWindow(toplevel), &attr);
2328 IconifyWindow(None, attr.map_state != IsUnmapped);
2333 * This will be called when user pressed a key on the screen.
2335 static const char *FindFunctionKeyValue(const char *key, Boolean shiftable);
2336 static void ShowBalloon(Widget w, XEvent *event, String *pars, Cardinal *n_pars);
2337 static void KeyClick(void);
2338 static void StopAutoclick(void);
2340 static void KeyPressed(Widget w, char *key, char *data)
2350 if (appres.debug) fprintf(stderr, "KeyPressed: key=%s, widget=%lx\n", key, (long)w);
2352 value = FindFunctionKeyValue(key, TRUE);
2353 if (value != NULL) {
2354 if (appres.debug) fprintf(stderr, "Assigned string: %s\n", value);
2355 if (value[0] == '!') {
2356 if (appres.debug) fprintf(stderr, "Launching: %s\n", value + 1);
2357 if (!appres.secure) system(value + 1);
2359 if (value[0] == '\\') value = value + 1;
2360 if (appres.debug) fprintf(stderr, "Sending: %s\n", value);
2363 ShowBalloon(w, NULL, NULL, NULL);
2367 if (strncmp(key, "Shift", strlen("Shift")) == 0) {
2368 if (shift_state & ShiftMask) SendKeyPressedEvent(NoSymbol, shift_state);
2369 shift_state ^= ShiftMask;
2370 } else if (strncmp(key, "Control", strlen("Control")) == 0) {
2371 if (shift_state & ControlMask) SendKeyPressedEvent(NoSymbol, shift_state);
2372 shift_state ^= ControlMask;
2373 } else if (strncmp(key, "Alt", strlen("Alt")) == 0) {
2374 if (shift_state & alt_mask) SendKeyPressedEvent(NoSymbol, shift_state);
2375 shift_state ^= alt_mask;
2376 } else if (strncmp(key, "Meta", strlen("Meta")) == 0) {
2377 if (shift_state & meta_mask) SendKeyPressedEvent(NoSymbol, shift_state);
2378 shift_state ^= meta_mask;
2379 } else if (strcmp(key, "Caps_Lock") == 0) {
2380 if (shift_state & LockMask) SendKeyPressedEvent(NoSymbol, shift_state);
2381 shift_state ^= LockMask;
2382 } else if (strcmp(key, "Mode_switch") == 0) {
2383 if (shift_state & altgr_mask) SendKeyPressedEvent(NoSymbol, shift_state);
2384 shift_state ^= altgr_mask;
2385 } else if (strcmp(key, "Num_Lock") == 0) {
2386 appres.num_lock_state = !appres.num_lock_state;
2387 } else if (strcmp(key, "Focus") == 0) {
2388 cur_shift = shift_state | mouse_shift;
2389 if (cur_shift & ShiftMask) {
2390 focused_window = None;
2391 focused_subwindow = None;
2396 if (appres.quick_modifiers && mouse_shift == 0 && w != None) {
2401 Dimension btn_wd, btn_ht;
2403 n_key_repeat = n_key_repeat + 1;
2404 if (n_key_repeat == 1) return;
2406 XtVaGetValues(w, XtNwidth, &btn_wd, XtNheight, &btn_ht, NULL);
2407 XQueryPointer(dpy, XtWindow(w), &junk_w, &junk_w,
2408 &junk_i, &junk_i, &cur_x, &cur_y, &junk_u);
2411 if (cur_x < 0 && btn_ht < cur_y) {
2412 mouse_shift |= alt_mask; /* left-down */
2414 if (cur_y < 0) mouse_shift |= ShiftMask; /* up */
2415 else if (btn_ht < cur_y) mouse_shift |= meta_mask; /* down */
2416 if (cur_x < 0) mouse_shift |= ControlMask; /* left */
2417 else if (btn_wd < cur_x) mouse_shift |= altgr_mask; /* right */
2420 cur_shift = shift_state | mouse_shift;
2421 shifted = (cur_shift & ShiftMask);
2424 if (sscanf(key, "pad%d,%d", &row, &col) == 2) {
2425 if (appres.num_lock_state) shifted = !shifted;
2426 key1 = shifted ? keypad_shift[row][col]: keypad[row][col];
2429 if (sscanf(key, "%d,%d", &row, &col) == 2) {
2431 } else if (w != None) {
2432 int first_row = appres.function_key ? 0 : 1;
2433 for (row = first_row; row < NUM_KEY_ROWS; row++) {
2434 for (col = 0; col < NUM_KEY_COLS; col++) {
2435 if (key_widgets[row][col] == w) {
2440 if (col < NUM_KEY_COLS) break;
2444 if ((cur_shift & LockMask)
2445 && isalpha(keys_normal[row][col][0]) && keys_normal[row][col][1] == '\0')
2447 if ((cur_shift & altgr_mask) && keys_altgr[row][col] != NULL) {
2448 if (shifted && keys_shift_altgr[row][col] != NULL) {
2449 key1 = keys_shift_altgr[row][col];
2450 if (strcmp(keys_altgr[row][col], keys_shift_altgr[row][col]) != 0)
2451 cur_shift &= ~ShiftMask;
2453 key1 = keys_altgr[row][col];
2456 if (shifted && keys_shift[row][col] != NULL) {
2457 key1 = keys_shift[row][col];
2458 if (strcmp(keys_normal[row][col], keys_shift[row][col]) != 0)
2459 cur_shift &= ~ShiftMask;
2461 key1 = keys_normal[row][col];
2464 } /* if (found) ... */
2465 } /* if (sscanf(key, "pad%d,%d", ... */
2466 } /* if (w != None) ... */
2467 if (strlen(key1) == 1) {
2468 SendKeyPressedEvent((KeySym)*key1 & 0xff, cur_shift);
2469 AddToCompletionText((KeySym)*key1);
2471 if (islower(key1[0]) && key1[1] == ':') {
2473 case 's': cur_shift |= ShiftMask; break;
2474 case 'c': cur_shift |= ControlMask; break;
2475 case 'a': cur_shift |= alt_mask; break;
2476 case 'm': cur_shift |= meta_mask; break;
2477 default: fprintf(stderr, "%s: unknown modidier: %s\n",
2478 PROGRAM_NAME, key1); break;
2482 keysym = XStringToKeysym(key1);
2483 if ((!appres.keypad_keysym && strncmp(key1, "KP_", 3) == 0)
2484 || XKeysymToKeycode(target_dpy, keysym) == NoSymbol) {
2485 switch ((unsigned)keysym) {
2486 case XK_KP_Equal: keysym = XK_equal; break;
2487 case XK_KP_Divide: keysym = XK_slash; break;
2488 case XK_KP_Multiply: keysym = XK_asterisk; break;
2489 case XK_KP_Add: keysym = XK_plus; break;
2490 case XK_KP_Subtract: keysym = XK_minus; break;
2491 case XK_KP_Enter: keysym = XK_Return; break;
2492 case XK_KP_1: keysym = XK_1; break;
2493 case XK_KP_2: keysym = XK_2; break;
2494 case XK_KP_3: keysym = XK_3; break;
2495 case XK_KP_4: keysym = XK_4; break;
2496 case XK_KP_5: keysym = XK_5; break;
2497 case XK_KP_6: keysym = XK_6; break;
2498 case XK_KP_7: keysym = XK_7; break;
2499 case XK_KP_8: keysym = XK_8; break;
2500 case XK_KP_9: keysym = XK_9; break;
2501 case XK_Shift_L: keysym = XK_Shift_R; break;
2502 case XK_Shift_R: keysym = XK_Shift_L; break;
2503 case XK_Control_L: keysym = XK_Control_R; break;
2504 case XK_Control_R: keysym = XK_Control_L; break;
2505 case XK_Alt_L: keysym = XK_Alt_R; break;
2506 case XK_Alt_R: keysym = XK_Alt_L; break;
2507 case XK_Meta_L: keysym = XK_Meta_R; break;
2508 case XK_Meta_R: keysym = XK_Meta_L; break;
2510 if (keysym == NoSymbol || !appres.auto_add_keysym)
2511 fprintf(stderr, "%s: no such key: %s\n",
2512 PROGRAM_NAME, key1); break;
2515 SendKeyPressedEvent(keysym, cur_shift);
2516 AddToCompletionText(keysym);
2518 if ((cur_shift & ControlMask) && (cur_shift & alt_mask)) {
2519 if (strstr(XServerVendor(dpy), "XFree86") != NULL) {
2520 if (strcmp(key1, "KP_Add") == 0) {
2521 if (!appres.secure) system("xvidtune -next");
2522 } else if (strcmp(key1, "KP_Subtract") == 0) {
2523 if (!appres.secure) system("xvidtune -prev");
2528 if (!appres.shift_lock)
2529 shift_state &= ~ShiftMask;
2530 if (!appres.modifiers_lock)
2531 shift_state &= ~(ControlMask | alt_mask | meta_mask);
2532 if (!appres.altgr_lock)
2533 shift_state &= ~altgr_mask;
2535 RefreshShiftState(FALSE);
2539 /* StopAutoclick(); */
2544 * Redefine keyboard layout.
2545 * "spec" is a sequence of words separated with spaces, and it is
2546 * usally specified in app-defaults file, as:
2548 * xvkbd.AltGrKeys: \
2549 * F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 BackSpace \n\
2550 * Escape \271 \262 \263 \243 \254 \251 { [ ] } \\ ' ^ ' \n\
2553 * White spaces separate the keys, and " \n" (note that white space
2554 * before the \n) separate the rows of keys.
2556 static void RedefineKeys(char *array[NUM_KEY_ROWS][NUM_KEY_COLS], const char *spec)
2558 char *s = XtNewString(spec);
2561 int key_rows = NUM_KEY_ROWS;
2562 int key_cols = NUM_KEY_COLS;
2564 for (row = 0; row < key_rows; row++) {
2565 for (col = 0; col < key_cols; col++) array[row][col] = NULL;
2569 cp = strtok(s, " ");
2570 while (cp != NULL) {
2577 if (key_rows <= row) {
2578 fprintf(stderr, "%s: too many key rows: \"%s\" ignored\n",
2580 } else if (key_cols <= col) {
2581 fprintf(stderr, "%s: too many keys in a row: \"%s\" ignored\n",
2584 array[row][col] = XtNewString(cp);
2588 cp = strtok(NULL, " ");
2594 * Create keyboard on the screen.
2596 static Widget MakeKey(Widget parent, const char *name, const char *label, Pixel color)
2598 static Pixmap up_pixmap = None;
2599 static Pixmap down_pixmap = None;
2600 static Pixmap left_pixmap = None;
2601 static Pixmap right_pixmap = None;
2602 static Pixmap back_pixmap = None;
2604 Window scr = RootWindow(dpy, DefaultScreen(dpy));
2608 if (!appres.auto_repeat
2609 || strncmp(name, "Shift", strlen("Shift")) == 0
2610 || strncmp(name, "Control", strlen("Control")) == 0
2611 || strncmp(name, "Alt", strlen("Alt")) == 0
2612 || strncmp(name, "Meta", strlen("Meta")) == 0
2613 || strcmp(name, "Caps_Lock") == 0
2614 || strcmp(name, "Mode_switch") == 0
2615 || strcmp(name, "Num_Lock") == 0
2616 || strcmp(name, "Focus") == 0) {
2617 w = XtVaCreateManagedWidget(name, commandWidgetClass, parent,
2618 XtNbackground, color, NULL);
2620 w = XtVaCreateManagedWidget(name, repeaterWidgetClass, parent,
2621 XtNbackground, color, NULL);
2623 XtAddCallback(w, XtNcallback, (XtCallbackProc)KeyPressed, (XtPointer)name);
2625 if (label != NULL) {
2626 strncpy(str, label, sizeof(str) - 1);
2627 if (strcmp(str, "space") == 0) strcpy(str, "");
2630 if (str[1] == '_') str[1] = ' ';
2631 if (str[len - 2] == '_') str[len - 2] = ' ';
2633 XtVaSetValues(w, XtNlabel, str, NULL);
2635 if (strcmp(label, "up") == 0) {
2636 if (up_pixmap == None)
2637 up_pixmap = XCreateBitmapFromData(dpy, scr,
2638 (char *)up_bits, up_width, up_height);
2639 XtVaSetValues(w, XtNbitmap, up_pixmap, NULL);
2640 } else if (strcmp(label, "down") == 0) {
2641 if (down_pixmap == None)
2642 down_pixmap = XCreateBitmapFromData(dpy, scr,
2643 (char *)down_bits, down_width, down_height);
2644 XtVaSetValues(w, XtNbitmap, down_pixmap, NULL);
2645 } else if (strcmp(label, "left") == 0) {
2646 if (left_pixmap == None)
2647 left_pixmap = XCreateBitmapFromData(dpy, scr,
2648 (char *)left_bits, left_width, left_height);
2649 XtVaSetValues(w, XtNbitmap, left_pixmap, NULL);
2650 } else if (strcmp(label, "right") == 0) {
2651 if (right_pixmap == None)
2652 right_pixmap = XCreateBitmapFromData(dpy, scr,
2653 (char *)right_bits, right_width, right_height);
2654 XtVaSetValues(w, XtNbitmap, right_pixmap, NULL);
2655 } else if (strcmp(label, "back") == 0) {
2656 if (back_pixmap == None)
2657 back_pixmap = XCreateBitmapFromData(dpy, scr,
2658 (char *)back_bits, back_width, back_height);
2659 XtVaSetValues(w, XtNbitmap, back_pixmap, NULL);
2666 static void MakeKeypad(Widget form, Widget from_vert, Widget from_horiz)
2668 Widget key, left, upper;
2673 Widget keypad_row[NUM_KEYPAD_ROWS];
2676 keypad_box = XtVaCreateManagedWidget("keypad", formWidgetClass, form, NULL);
2677 if (from_horiz != None)
2678 XtVaSetValues(keypad_box, XtNfromHoriz, from_horiz, NULL);
2680 XtVaSetValues(keypad_box, XtNhorizDistance, 0, NULL);
2681 if (from_vert != None)
2682 XtVaSetValues(keypad_box, XtNfromVert, from_vert, NULL);
2684 XtVaSetValues(keypad_box, XtNvertDistance, 0, NULL);
2686 for (row = 0; row < NUM_KEYPAD_ROWS; row++) {
2688 for (col = 0; keypad[row][col] != NULL; col++) {
2689 font = appres.keypad_font;
2690 if (strlen(keypad_label[row][col]) == 1) font = appres.letter_font;
2691 color = appres.special_background;
2692 if (strcmp(keypad[row][col], "Focus") == 0)
2693 color = appres.focus_background;
2694 else if (strcmp(keypad_shift[row][col], ".") == 0
2695 || (strncmp(keypad_shift[row][col], "KP_", 3) == 0
2696 && isdigit(keypad_shift[row][col][3])))
2697 color = appres.general_background;
2698 strcpy(name, keypad[row][col]);
2699 if (strcmp(name, "Focus") != 0 && strcmp(name, "Num_Lock") != 0)
2700 sprintf(name, "pad%d,%d", row, col);
2701 key = MakeKey(keypad_box, XtNewString(name),
2702 keypad_label[row][col], color);
2703 XtVaSetValues(key, XtNfont, font, NULL);
2705 XtVaSetValues(key, XtNfromVert, keypad_row[row - 1], NULL);
2707 XtVaSetValues(key, XtNfromHoriz, left, NULL);
2709 keypad_row[row] = key;
2715 static void MakeSunFunctionKey(Widget form, Widget from_vert, Widget from_horiz)
2717 Widget key, left, upper;
2720 Widget fkey_row[NUM_SUN_FKEY_ROWS];
2722 fkey_box = XtVaCreateManagedWidget("fkey", formWidgetClass, form, NULL);
2723 if (from_horiz != None)
2724 XtVaSetValues(fkey_box, XtNfromHoriz, from_horiz, NULL);
2726 XtVaSetValues(fkey_box, XtNhorizDistance, 0, NULL);
2727 if (from_vert != None)
2728 XtVaSetValues(fkey_box, XtNfromVert, from_vert, NULL);
2730 XtVaSetValues(fkey_box, XtNvertDistance, 0, NULL);
2732 for (row = 0; row < NUM_SUN_FKEY_ROWS; row++) {
2734 for (col = 0; sun_fkey[row][col] != NULL; col++) {
2735 key = MakeKey(fkey_box, sun_fkey[row][col],
2736 sun_fkey_label[row][col], appres.special_background);
2737 XtVaSetValues(key, XtNfont, appres.keypad_font, NULL);
2739 XtVaSetValues(key, XtNfromVert, fkey_row[row - 1], NULL);
2741 XtVaSetValues(key, XtNfromHoriz, left, NULL);
2743 fkey_row[row] = key;
2749 static void MakeDeadkeyPanel(Widget form)
2751 Widget deadkey_box, left, key;
2752 char *deadkeys, *cp, *cp2;
2754 deadkeys = XtNewString(appres.deadkeys);
2756 deadkey_box = XtVaCreateManagedWidget("deadkey", formWidgetClass, form, NULL);
2758 cp = strtok(deadkeys, " \t,");
2759 while (cp != NULL) {
2760 cp2 = XtNewString(cp);
2761 key = MakeKey(deadkey_box, cp2, NULL, appres.general_background);
2762 if (left != None) XtVaSetValues(key, XtNfromHoriz, left, NULL);
2764 cp = strtok(NULL, " \t,");
2769 static void RefreshMainMenu(void)
2771 static Pixmap check_pixmap = None;
2773 if (check_pixmap == None) {
2774 check_pixmap = XCreateBitmapFromData(dpy, RootWindow(dpy, DefaultScreen(dpy)),
2775 (char *)check_bits, check_width, check_height);
2777 XtVaSetValues(XtNameToWidget(main_menu, "*show_keypad"),
2778 XtNrightBitmap, appres.keypad ? check_pixmap : None, NULL);
2779 XtVaSetValues(XtNameToWidget(main_menu, "*show_functionkey"),
2780 XtNrightBitmap, appres.function_key ? check_pixmap : None, NULL);
2782 XtSetSensitive(XtNameToWidget(main_menu, "*edit_fkey"), appres.function_key);
2783 XtSetSensitive(XtNameToWidget(main_menu, "*close_display"), target_dpy != dpy);
2786 static void MakeKeyboard(Boolean remake)
2788 static char *main_menu_items[] = {
2789 "about", "man", "keypad", "sun_fkey", "deadkey", "completion", "",
2796 "open_display", "close_display", "",
2799 Widget form, key, left;
2802 Dimension wd, max_wd;
2803 int row, col, first_row;
2804 char name[50], *label;
2805 Widget key_box[NUM_KEY_ROWS];
2809 #include "xvkbd.xbm"
2810 #include "iconify.xbm"
2813 appres.geometry = GetWindowGeometry(toplevel);
2814 XtUnrealizeWidget(toplevel);
2815 XtDestroyWidget(XtNameToWidget(toplevel, "form"));
2818 form = XtVaCreateManagedWidget("form", formWidgetClass, toplevel, NULL);
2822 first_row = appres.function_key ? 0 : 1;
2823 if (!appres.keypad_only) {
2824 for (row = first_row; row < NUM_KEY_ROWS; row++) {
2825 if (keys_normal[row][0] == NULL) continue;
2827 sprintf(name, "row%d", row);
2828 key_box[row] = XtVaCreateManagedWidget(name, formWidgetClass, form, NULL);
2829 key_box[row + 1] = None;
2830 if (row != first_row)
2831 XtVaSetValues(key_box[row], XtNfromVert, key_box[row - 1], NULL);
2832 else if (!appres.function_key)
2833 XtVaSetValues(key_box[row], XtNvertDistance, 0, NULL);
2836 for (col = 0; keys_normal[row][col] != NULL; col++) {
2837 strcpy(name, keys_normal[row][col]);
2838 if (strcmp(name, "MainMenu") == 0) {
2839 Widget iconify_button = None;
2841 if (appres.minimizable) {
2842 Pixmap iconify_pixmap = XCreateBitmapFromData(dpy, RootWindow(dpy, DefaultScreen(dpy)),
2843 (char *)iconify_bits, iconify_width, iconify_height);
2844 iconify_button = XtVaCreateManagedWidget("Iconify", commandWidgetClass, key_box[row],
2845 XtNbitmap, iconify_pixmap, NULL);
2846 XtAddCallback(iconify_button, XtNcallback, (XtCallbackProc)IconifyWindow, (void *)TRUE);
2849 xvkbd_pixmap = XCreateBitmapFromData(dpy, RootWindow(dpy, DefaultScreen(dpy)),
2850 (char *)xvkbd_bits, xvkbd_width, xvkbd_height);
2851 key = XtVaCreateManagedWidget("MainMenu", menuButtonWidgetClass, key_box[row],
2852 XtNbitmap, xvkbd_pixmap, XtNfromHoriz, iconify_button, NULL);
2853 main_menu = XtVaCreatePopupShell("menu", simpleMenuWidgetClass, key, NULL);
2854 for (i = 0; i < XtNumber(main_menu_items); i++) {
2855 if (strlen(main_menu_items[i]) == 0) {
2856 XtVaCreateManagedWidget("separator", smeLineObjectClass, main_menu, NULL);
2858 menu_entry = XtVaCreateManagedWidget(main_menu_items[i], smeBSBObjectClass,
2860 XtAddCallback(menu_entry, XtNcallback, (XtCallbackProc)MenuSelected,
2861 (XtPointer)main_menu_items[i]);
2865 label = appres.modal_keytop ? normal_key_labels[row][col] : key_labels[row][col];
2866 if (isascii(name[0]) && isupper(name[0])) {
2867 if (strcmp(name, "Focus") == 0) {
2868 color = appres.focus_background;
2869 font = appres.keypad_font;
2871 color = appres.special_background;
2872 if (label != NULL && strchr(label, '\n') != NULL) font = appres.keypad_font;
2873 else font = appres.special_font;
2876 color = appres.general_background;
2877 font = appres.general_font;
2878 if (isalpha(name[0])) font = appres.letter_font;
2879 if (strcmp(name, "space") != 0) sprintf(name, "%d,%d", row, col);
2881 key = MakeKey(key_box[row], XtNewString(name), label, color);
2882 XtVaGetValues(key, XtNwidth, &wd, NULL);
2884 /* keys can be removed by setting its width to 1 */
2885 XtDestroyWidget(key);
2888 XtVaSetValues(key, XtNfont, font, NULL);
2890 if (font == appres.special_font || font == appres.keypad_font)
2891 XtVaSetValues(key, XtNfontSet, appres.special_fontset, NULL);
2896 if (left != None) XtVaSetValues(key, XtNfromHoriz, left, NULL);
2899 key_widgets[row][col] = key;
2904 if (appres.keypad) MakeKeypad(form, key_box[0], key_box[1]);
2906 if (!appres.keypad_only && appres.function_key && appres.keypad) {
2907 XtVaCreateManagedWidget("banner", labelWidgetClass, form,
2908 XtNfromHoriz, key_box[1],
2909 XtNlabel, PROGRAM_NAME_WITH_VERSION, NULL);
2913 XtRealizeWidget(toplevel);
2914 SetWindowManagerHint(TRUE);
2916 if (!remake && strlen(appres.geometry) == 0) {
2919 unsigned int wd, ht, bd, dp;
2922 XGetGeometry(dpy, XtWindow(toplevel), &root, &x1, &y1, &wd, &ht, &bd, &dp);
2923 max_wd = XtScreen(toplevel)->width * appres.max_width_ratio;
2924 max_ht = XtScreen(toplevel)->height * appres.max_height_ratio;
2926 fprintf(stderr, "window size: %dx%d, max size: %dx%d\n", wd, ht, max_wd, max_ht);
2927 if (max_wd < wd || max_ht < ht) {
2928 if (max_wd < wd) wd = max_wd;
2929 if (max_ht < ht) ht = max_ht;
2931 fprintf(stderr, "setting window size: %dx%d\n", wd, ht);
2932 XResizeWindow(dpy, XtWindow(toplevel), wd, ht);
2936 if (!appres.debug && key_box[first_row] != None) {
2937 if (appres.keypad) {
2938 XtVaGetValues(key_box[1], XtNwidth, &max_wd, NULL);
2941 for (row = first_row; row < NUM_KEY_ROWS && key_box[row] != None; row++) {
2942 XtVaGetValues(key_box[row], XtNwidth, &wd, NULL);
2943 if (max_wd < wd) max_wd = wd;
2946 for (row = first_row; row < NUM_KEY_ROWS && key_box[row] != None; row++) {
2947 XtVaSetValues(key_box[row], XtNwidth, max_wd, NULL);
2950 if (0 < strlen(appres.geometry)) {
2951 if (appres.wm_toolbar) {
2953 fprintf(stderr, "window fgeometry ignored; _NET_WM_WINDOW_TYPE_TOOLBAR set on\n");
2956 fprintf(stderr, "setting window geometry: %s\n", appres.geometry);
2957 XtVaSetValues(toplevel, XtNgeometry, appres.geometry, NULL);
2958 XtUnrealizeWidget(toplevel);
2959 XtRealizeWidget(toplevel);
2964 if (main_menu != None) RefreshMainMenu();
2965 RefreshShiftState(FALSE);
2967 XtMapWidget(toplevel);
2969 if (wm_delete_window == None)
2970 wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", FALSE);
2971 XSetWMProtocols(dpy, XtWindow(toplevel), &wm_delete_window, 1);
2973 XtVaGetValues(toplevel, XtNheight, &toplevel_height, NULL);
2977 * WM_DELETE_WINDOW has been sent - terminate the program.
2979 static void DeleteWindowProc(Widget w, XEvent *event,
2980 String *pars, Cardinal *n_pars)
2982 if (appres.nonexitable) {
2986 RefreshShiftState(TRUE);
2987 XtDestroyApplicationContext(XtWidgetToApplicationContext(toplevel));
2993 * Callback for ConfigureNotify event, which will be invoked when
2994 * the toplevel window is resized.
2995 * We may need to switch the keytop labels when window becomes
2996 * smaller than appres.modal_threshold, and vice versa.
2998 static void WindowResized(Widget w, XEvent *event,
2999 String *pars, Cardinal *n_pars)
3003 XtVaGetValues(toplevel, XtNheight, &ht, NULL);
3004 if (appres.modal_threshold <= ht) {
3005 if (toplevel_height < appres.modal_threshold) MakeKeyboard(TRUE);
3007 toplevel_height = ht;
3009 RefreshShiftState(TRUE);
3013 * Load list of text to be assigned to function keys.
3014 * Each line contains name of the key (with optional modifier)
3015 * and the text to be assigned to the key, as:
3018 * s:F2 text for Shift-F2
3021 # define PATH_MAX 300
3024 static char fkey_filename[PATH_MAX] = "";
3026 static struct fkey_struct {
3027 struct fkey_struct *next;
3029 } *fkey_list = NULL;
3031 static void ReadFuncionKeys(void)
3034 char str[200], key[200];
3035 struct fkey_struct *sp = NULL, *new_node;
3040 /* If KeyFile is not started with "/", consider the filename is relative to $HOME */
3041 /* and put value of the $HOME environment variable before the KeyFile. */
3042 /* To avoid possible buffer overflow, $HOME will not be added when resulting filename */
3044 home = getenv("HOME");
3045 if (appres.key_file[0] != '/' && home != NULL
3046 && strlen(home) + strlen(appres.key_file) + 1 < sizeof(fkey_filename))
3047 sprintf(fkey_filename, "%s/%s", home, appres.key_file);
3049 strncpy(fkey_filename, appres.key_file, sizeof(fkey_filename));
3051 strncpy(dict_filename, appres.dict_file, sizeof(dict_filename));
3053 fp = fopen(fkey_filename, "r");
3054 if (fp == NULL) return;
3056 while (fgets(str, sizeof(str) - 1, fp)) {
3057 if (str[0] == '#') {
3058 sscanf(&str[1], "%s %d", key, &val);
3059 if (strcmp(key, "quick_modifiers") == 0)
3060 appres.quick_modifiers = val;
3061 else if (strcmp(key, "shift_lock") == 0)
3062 appres.shift_lock = val;
3063 else if (strcmp(key, "altgr_lock") == 0)
3064 appres.altgr_lock = val;
3065 else if (strcmp(key, "modifiers_lock") == 0)
3066 appres.modifiers_lock = val;
3067 else if (strcmp(key, "key_click") == 0)
3068 appres.key_click_duration = val;
3069 else if (strcmp(key, "autoclick") == 0)
3070 appres.autoclick_delay = val;
3071 else if (strcmp(key, "always_on_top") == 0)
3072 appres.always_on_top = val;
3073 else if (strcmp(key, "wm_toolbar") == 0)
3074 appres.wm_toolbar = val;
3075 else if (strcmp(key, "jump_pointer") == 0)
3076 appres.jump_pointer = val;
3077 else if (strcmp(key, "dict_file") == 0) {
3078 sscanf(&str[1], "%*s %s", &key);
3079 strncpy(dict_filename, key, sizeof(dict_filename));
3081 } else if (isalpha(str[0])) {
3083 if (str[len - 1] == '\n') str[len - 1] = '\0';
3085 new_node = malloc(sizeof(struct fkey_struct));
3086 if (fkey_list == NULL) fkey_list = new_node;
3087 else sp->next = new_node;
3091 sp->value = XtNewString(str);
3098 * Edit string assigned for function keys.
3099 * Modifiers (Shift, Ctrl, etc.) can't be handled here.
3101 static Widget edit_fkey_panel = None;
3102 static Widget fkey_menu_button = None;
3103 static Widget fkey_value_menu_button = None;
3104 static Widget fkey_value_entry = None;
3105 static char fkey_value[100] = "";
3106 static char cur_fkey[20] = "";
3107 static char *cur_fkey_value_mode = "";
3109 static void FKeyValueMenuSelected(Widget w, char *key)
3113 if (key[0] == 'c') {
3114 cur_fkey_value_mode = "command";
3117 cur_fkey_value_mode = "string";
3120 XtVaGetValues(XtNameToWidget(fkey_value_menu_button, key1), XtNlabel, &cp, NULL);
3121 XtVaSetValues(fkey_value_menu_button, XtNlabel, cp, NULL);
3124 static void FKeyMenuSelected(Widget w, char *key)
3126 struct fkey_struct *sp, *sp2;
3128 const char *value, *prefix;
3133 else if (strncmp(key, "Shift-", strlen("Shift-")) == 0)
3134 sprintf(key2, "s:%s", &key[strlen("Shift-")]);
3138 if (strcmp(cur_fkey, key2) != 0) {
3139 if (strlen(cur_fkey) != 0) {
3140 len = strlen(cur_fkey);
3142 for (sp = fkey_list; sp != NULL; sp = sp->next) {
3143 if (strncmp(sp->value, cur_fkey, len) == 0 && isspace(sp->value[len]))
3147 if (strlen(fkey_value) != 0) { /* assign new string for the function key */
3148 if (sp == NULL) { /* it was not defined before now */
3149 sp = malloc(sizeof(struct fkey_struct));
3150 if (fkey_list == NULL) fkey_list = sp;
3151 else sp2->next = sp;
3155 sp->value = realloc(sp->value, len + strlen(fkey_value) + 5);
3157 if (cur_fkey_value_mode[0] == 'c') prefix = "!";
3158 else if (fkey_value[0] == '!') prefix = "\\";
3159 sprintf(sp->value, "%s %s%s", cur_fkey, prefix, fkey_value);
3160 } else { /* empty string - remove the entry for the function key */
3162 if (sp2 != NULL) sp2->next = sp->next;
3163 else fkey_list = sp->next;
3171 XtVaSetValues(fkey_menu_button, XtNlabel, key, NULL);
3173 value = FindFunctionKeyValue(key2, FALSE);
3174 if (value == NULL) value = "";
3176 FKeyValueMenuSelected(None, (value[0] == '!') ? "command" : "string");
3178 if (value[0] == '!' || value[0] == '\\') value = value + 1;
3179 strncpy(fkey_value, value, sizeof(fkey_value) - 1);
3180 XtVaSetValues(fkey_value_entry, XtNstring, fkey_value, NULL);
3182 strcpy(cur_fkey, key2);
3187 static void CloseFunctionKeyPanel(void)
3189 XtPopdown(edit_fkey_panel);
3192 static void SaveFunctionKey(void)
3194 struct fkey_struct *sp;
3197 if (appres.debug) fprintf(stderr, "SaveFunctionKey\n");
3199 if (edit_fkey_panel != None) FKeyMenuSelected(None, NULL);
3201 fp = fopen(fkey_filename, "w");
3203 fprintf(stderr, "%s: can't open \"%s\": %s\n",
3204 PROGRAM_NAME, fkey_filename, strerror(errno));
3207 fprintf(fp, "#quick_modifiers %d\n", appres.quick_modifiers);
3208 fprintf(fp, "#shift_lock %d\n", appres.shift_lock);
3209 fprintf(fp, "#altgr_lock %d\n", appres.altgr_lock);
3210 fprintf(fp, "#modifiers_lock %d\n", appres.modifiers_lock);
3211 fprintf(fp, "#key_click %d\n", appres.key_click_duration);
3212 fprintf(fp, "#autoclick %d\n", appres.autoclick_delay);
3213 fprintf(fp, "#always_on_top %d\n", appres.always_on_top);
3214 fprintf(fp, "#wm_toolbar %d\n", appres.wm_toolbar);
3215 fprintf(fp, "#jump_pointer %d\n", appres.jump_pointer);
3216 fprintf(fp, "#dict_file %s\n", dict_filename);
3217 for (sp = fkey_list; sp != NULL; sp = sp->next) {
3218 fprintf(fp, "%s\n", sp->value);
3222 if (edit_fkey_panel != None) CloseFunctionKeyPanel();
3225 static void PopupFunctionKeyEditor(void)
3227 Widget form, form2, menu, menu_entry, button;
3232 if (edit_fkey_panel == None) {
3233 edit_fkey_panel = XtVaCreatePopupShell("edit_fkey_panel", transientShellWidgetClass,
3235 form = XtVaCreateManagedWidget("form", formWidgetClass, edit_fkey_panel, NULL);
3237 form2 = XtVaCreateManagedWidget("form2", formWidgetClass, form, NULL);
3238 XtVaCreateManagedWidget("fkey_label", labelWidgetClass, form2, NULL);
3239 fkey_menu_button = XtVaCreateManagedWidget("fkey_menu", menuButtonWidgetClass,
3241 menu = XtVaCreatePopupShell("menu", simpleMenuWidgetClass, fkey_menu_button, NULL);
3242 for (j = 0; j <= 1; j++) {
3243 for (i = 1; i <= appres.editable_function_keys; i++) {
3245 sprintf(label, "F%d", i);
3247 sprintf(label, "Shift-F%d", i);
3248 key = XtNewString(label);
3249 menu_entry = XtVaCreateManagedWidget(key, smeBSBObjectClass, menu, NULL);
3250 XtAddCallback(menu_entry, XtNcallback, (XtCallbackProc)FKeyMenuSelected,
3255 fkey_value_menu_button = XtVaCreateManagedWidget("fkey_value_menu", menuButtonWidgetClass,
3257 menu = XtVaCreatePopupShell("menu", simpleMenuWidgetClass, fkey_value_menu_button, NULL);
3258 menu_entry = XtVaCreateManagedWidget("string", smeBSBObjectClass, menu, NULL);
3259 XtAddCallback(menu_entry, XtNcallback, (XtCallbackProc)FKeyValueMenuSelected,
3260 (XtPointer)"string");
3261 menu_entry = XtVaCreateManagedWidget("command", smeBSBObjectClass, menu, NULL);
3262 XtAddCallback(menu_entry, XtNcallback, (XtCallbackProc)FKeyValueMenuSelected,
3263 (XtPointer)"command");
3265 XtVaCreateManagedWidget("fkey_value_sep", labelWidgetClass, form2, NULL);
3267 fkey_value_entry = XtVaCreateManagedWidget("fkey_value", asciiTextWidgetClass, form2,
3268 XtNuseStringInPlace, True,
3269 XtNeditType, XawtextEdit,
3270 XtNstring, fkey_value,
3271 XtNlength, sizeof(fkey_value) - 1,
3274 button = XtVaCreateManagedWidget("save_button", commandWidgetClass, form, NULL);
3275 XtAddCallback(button, XtNcallback, (XtCallbackProc)SaveFunctionKey, NULL);
3277 button = XtVaCreateManagedWidget("close_button", commandWidgetClass, form, NULL);
3278 XtAddCallback(button, XtNcallback, (XtCallbackProc)CloseFunctionKeyPanel, NULL);
3280 XtRealizeWidget(edit_fkey_panel);
3281 XSetWMProtocols(dpy, XtWindow(edit_fkey_panel), &wm_delete_window, 1);
3283 XtSetKeyboardFocus(edit_fkey_panel, fkey_value_entry);
3285 FKeyMenuSelected(None, "F1");
3288 XtPopup(edit_fkey_panel, XtGrabNone);
3292 * If text is assigned to the specified function key,
3293 * return the text. Otherwise, return NULL.
3295 static const char *FindFunctionKeyValue(const char *key, Boolean shiftable)
3299 struct fkey_struct *sp;
3304 if (shift_state & meta_mask) prefix = 'm';
3305 else if (shift_state & alt_mask) prefix = 'a';
3306 else if (shift_state & ControlMask) prefix = 'c';
3307 else if (shift_state & ShiftMask) prefix = 's';
3309 if (prefix == '\0') sprintf(label, "%s", key);
3310 else sprintf(label, "%c:%s", prefix, key);
3311 len = strlen(label);
3313 for (sp = fkey_list; sp != NULL; sp = sp->next) {
3314 if (strncmp(sp->value, label, len) == 0 && isspace(sp->value[len]))
3315 return &(sp->value[len + 1]);
3326 XKeyboardControl kc;
3328 if (0 < appres.key_click_duration) {
3329 XGetKeyboardControl(dpy, &ks);
3331 kc.bell_duration = ks.bell_duration;
3332 kc.bell_pitch = appres.key_click_pitch;
3333 kc.bell_duration = appres.key_click_duration;
3334 XChangeKeyboardControl(dpy, KBBellPitch | KBBellDuration, &kc);
3338 kc.bell_pitch = ks.bell_pitch;
3339 kc.bell_duration = ks.bell_duration;
3340 XChangeKeyboardControl(dpy, KBBellPitch | KBBellDuration, &kc);
3346 * Display balloon message for the function keys,
3347 * if text is assigned to the key.
3349 static Boolean balloon_panel_open = FALSE;
3350 static Widget balloon_panel = None;
3352 static XtIntervalId autoclick_id = (XtIntervalId)0;
3354 static void StopAutoclick(void)
3356 if (autoclick_id != (XtIntervalId)0) {
3357 if (appres.debug) fprintf(stderr, "StopAutoclick: %lx\n", (long)autoclick_id);
3359 XtRemoveTimeOut(autoclick_id);
3360 autoclick_id = (XtIntervalId)0;
3364 static void Autoclick(void)
3368 XTestFakeButtonEvent(target_dpy, 1, True, CurrentTime);
3369 XTestFakeButtonEvent(target_dpy, 1, False, CurrentTime);
3372 static void ShowBalloon(Widget w, XEvent *event, String *pars, Cardinal *n_pars)
3374 static Widget message;
3379 if (strcmp(XtName(w), "MainMenu") == 0) {
3380 value = "Main menu";
3382 if (0 < appres.autoclick_delay) {
3383 autoclick_id = XtAppAddTimeOut(app_con, (long)appres.autoclick_delay,
3384 (XtTimerCallbackProc)Autoclick, (XtPointer)w);
3386 if (appres.debug) fprintf(stderr, "ShowBalloon: auto click triggerd: %lx, %d\n",
3387 (long)autoclick_id, appres.autoclick_delay);
3389 value = FindFunctionKeyValue(XtName(w), TRUE);
3390 if (value == NULL) return;
3393 if (balloon_panel == None) {
3394 balloon_panel = XtVaCreatePopupShell("balloon_panel", transientShellWidgetClass, toplevel,
3395 XtNoverrideRedirect, TRUE, NULL);
3396 message = XtVaCreateManagedWidget("message", labelWidgetClass, balloon_panel, NULL);
3398 XtVaGetValues(w, XtNheight, &ht, NULL);
3399 XtTranslateCoords(w, 6, ht + 2, &x, &y);
3400 XtVaSetValues(balloon_panel, XtNx, x, XtNy, y, NULL);
3401 if (value[0] == '!') {
3402 if (appres.secure) return;
3403 XtVaSetValues(message, XtNlabel, value + 1,
3404 XtNbackground, appres.launch_balloon_background, NULL);
3406 if (value[0] == '\\') value = value + 1;
3407 XtVaSetValues(message, XtNlabel, value,
3408 XtNbackground, appres.balloon_background, NULL);
3410 XtPopup(balloon_panel, XtGrabNone);
3412 balloon_panel_open = TRUE;
3415 static void CloseBalloon(Widget w, XEvent *event, String *pars, Cardinal *n_pars)
3418 if (balloon_panel_open) {
3419 XtPopdown(balloon_panel);
3420 balloon_panel_open = FALSE;
3427 static void SetIconBitmap(Widget w)
3429 #include "xvkbd_icon.xbm"
3430 #include "xvkbd_iconmask.xbm"
3432 Pixmap icon_pixmap, icon_mask;
3434 icon_pixmap = XCreateBitmapFromData(XtDisplay(w), XtWindow(w),
3435 (char *)xvkbd_icon_bits,
3436 xvkbd_icon_width, xvkbd_icon_height);;
3437 icon_mask = XCreateBitmapFromData(XtDisplay(w), XtWindow(w),
3438 (char *)xvkbd_iconmask_bits,
3439 xvkbd_iconmask_width, xvkbd_iconmask_height);
3440 XtVaSetValues(w, XtNiconPixmap, icon_pixmap, XtNiconMask, icon_mask, NULL);
3444 * Callback for VisibilityChanged event, which will be invoked
3445 * when xvkbd window is hidden by other window. ** EXPERIMENTAL **
3447 static void VisibilityChanged(Widget w, XEvent *event,
3448 String *pars, Cardinal *n_pars)
3451 static time_t t1 = 0;
3454 if (!appres.always_on_top) return;
3456 if (balloon_panel_open) return;
3458 if (main_menu != None && XtWindow(main_menu) != None) {
3459 XWindowAttributes attr;
3460 XGetWindowAttributes(dpy, XtWindow(main_menu), &attr);
3461 if (attr.map_state != IsUnmapped) return;
3465 if (t1 != t2) cnt = 0;
3469 fprintf(stderr, "%s: visibility of the window changed (cnt = %d)\n", PROGRAM_NAME, cnt);
3471 XRaiseWindow(XtDisplay(toplevel), XtWindow(toplevel));
3477 int main(int argc, char *argv[])
3479 static XtActionsRec actions[] = {
3480 { "DeleteWindowProc", DeleteWindowProc },
3481 { "WindowResized", WindowResized },
3482 { "VisibilityChanged", VisibilityChanged },
3483 { "ReadKeymap", (XtActionProc)ReadKeymap },
3484 { "ButtonDownAction", ButtonDownAction },
3485 { "ButtonUpAction", ButtonUpAction },
3486 { "ShowBalloon", ShowBalloon },
3487 { "CloseBalloon", CloseBalloon },
3488 { "ClosePopupPanel", (XtActionProc)ClosePopupPanel },
3490 static String fallback_resources[] = {
3491 #include "XVkbd-common.h"
3495 Boolean open_keypad_panel = FALSE;
3501 argv1 = malloc(sizeof(char *) * (argc1 + 5));
3502 memcpy(argv1, argv, sizeof(char *) * argc1);
3503 argv1[argc1] = NULL;
3506 XtSetLanguageProc(NULL, NULL, NULL);
3509 toplevel = XtVaAppInitialize(NULL, "XVkbd",
3510 options, XtNumber(options),
3511 &argc, argv, fallback_resources, NULL);
3512 dpy = XtDisplay(toplevel);
3513 app_con = XtWidgetToApplicationContext(toplevel);
3514 XtAppAddActions(app_con, actions, XtNumber(actions));
3519 fprintf(stderr, "%s: illegal option: %s\n\n", PROGRAM_NAME, argv[1]);
3522 XtGetApplicationResources(toplevel, &appres,
3523 application_resources, XtNumber(application_resources),
3525 if (appres.version) {
3526 fprintf(stdout, "%s\n", appres.description);
3530 if (appres.compact) {
3531 appres.keypad = FALSE;
3532 appres.function_key = FALSE;
3534 if (appres.keypad_only && !appres.keypad) {
3535 appres.keypad_only = FALSE;
3536 open_keypad_panel = TRUE;
3539 if (appres.no_sync) {
3541 XSetErrorHandler(MyErrorHandler);
3544 if (0 < strlen(appres.window) || 0 < strlen(appres.instance)) {
3545 if (strcmp(appres.window, "root") == 0) {
3546 focused_window = RootWindow(dpy, DefaultScreen(dpy));
3547 } else if (sscanf(appres.window, "0x%lX%c", &focused_window, &ch) != 1) {
3548 if (sscanf(appres.window, "%ld%c", &focused_window, &ch) != 1) {
3549 focused_window = FindWindow(RootWindow(dpy, DefaultScreen(dpy)),
3551 if (focused_window == None) {
3552 fprintf(stderr, "%s: no such window: window=%s and class=%s\n", PROGRAM_NAME, appres.window, appres.instance);
3559 focused_subwindow = focused_window;
3562 if (!altgr_mask && appres.auto_add_keysym) AddModifier(XK_Mode_switch);
3564 if (strlen(appres.text) != 0 || strlen(appres.file) != 0) {
3565 appres.keypad_keysym = TRUE;
3566 if (focused_window != None &&
3567 (appres.list_widgets || strlen(appres.widget) != 0)) {
3568 XtVaSetValues(toplevel, XtNwidth, 1, XtNheight, 1, NULL);
3569 XtRealizeWidget(toplevel);
3570 child = FindWidget(toplevel, focused_window, appres.widget);
3571 if (child != None) focused_subwindow = child;
3573 if (strlen(appres.text) != 0)
3574 SendString(appres.text);
3576 SendFileContent(appres.file);
3581 if (0 < strlen(appres.keys_normal))
3582 RedefineKeys(keys_normal, appres.keys_normal);
3583 if (0 < strlen(appres.keys_shift))
3584 RedefineKeys(keys_shift, appres.keys_shift);
3585 if (0 < strlen(appres.keys_altgr))
3586 RedefineKeys(keys_altgr, appres.keys_altgr);
3587 if (0 < strlen(appres.keys_shift_altgr))
3588 RedefineKeys(keys_shift_altgr, appres.keys_shift_altgr);
3590 if (0 < strlen(appres.key_labels))
3591 RedefineKeys(key_labels, appres.key_labels);
3592 if (0 < strlen(appres.normal_key_labels))
3593 RedefineKeys(normal_key_labels, appres.normal_key_labels);
3594 if (0 < strlen(appres.shift_key_labels))
3595 RedefineKeys(shift_key_labels, appres.shift_key_labels);
3596 if (0 < strlen(appres.altgr_key_labels))
3597 RedefineKeys(altgr_key_labels, appres.altgr_key_labels);
3598 if (0 < strlen(appres.shift_altgr_key_labels))
3599 RedefineKeys(shift_altgr_key_labels, appres.shift_altgr_key_labels);
3601 if (0 < strlen(appres.keypad_normal)) {
3602 RedefineKeys(keypad, appres.keypad_normal);
3603 RedefineKeys(keypad_shift, appres.keypad_normal);
3604 RedefineKeys(keypad_label, appres.keypad_normal);
3606 if (0 < strlen(appres.keypad_shift))
3607 RedefineKeys(keypad_shift, appres.keypad_shift);
3608 if (0 < strlen(appres.keypad_labels))
3609 RedefineKeys(keypad_label, appres.keypad_labels);
3611 MakeKeyboard(FALSE);
3613 if (focused_window != None &&
3614 (appres.list_widgets || strlen(appres.widget) != 0)) {
3615 child = FindWidget(toplevel, focused_window, appres.widget);
3616 if (child != None) focused_subwindow = child;
3619 if (main_menu != None) {
3620 if (strlen(dict_filename) == 0)
3621 XtSetSensitive(XtNameToWidget(main_menu, "*completion"), FALSE);
3622 if (strlen(appres.customizations) == 0)
3623 XtSetSensitive(XtNameToWidget(main_menu, "*select_layout"), FALSE);
3624 if (appres.nonexitable)
3625 XtSetSensitive(XtNameToWidget(main_menu, "*quit"), FALSE);
3626 if (appres.secure) {
3627 XtSetSensitive(XtNameToWidget(main_menu, "*man"), FALSE);
3628 XtSetSensitive(XtNameToWidget(main_menu, "*open_display"), FALSE);
3633 if (!XQueryExtension(dpy, "XTEST", &op, &ev, &err)) {
3635 fprintf(stderr, "%s: XTEST extension is not supported by the X server\n",
3637 fprintf(stderr, "%s: XSendEvent will be used instead\n",
3639 appres.xtest = FALSE;
3641 if (main_menu != None) {
3642 XtSetSensitive(XtNameToWidget(main_menu, "*use_xtest"), FALSE);
3648 if (!appres.debug) {
3650 signal(SIGINT, SIG_IGN);
3651 signal(SIGQUIT, SIG_IGN);
3653 struct sigaction sigact;
3654 sigact.sa_handler = SIG_IGN;
3655 sigemptyset(&sigact.sa_mask);
3656 sigact.sa_flags = 0;
3657 sigaction(SIGINT, &sigact, NULL);
3658 sigaction(SIGQUIT, &sigact, NULL);
3664 signal(SIGUSR1, SignalUser1);
3666 struct sigaction sigact;
3667 sigact.sa_handler = SignalUser1;
3668 sigemptyset(&sigact.sa_mask);
3669 sigact.sa_flags = 0;
3670 sigaction(SIGUSR1, &sigact, NULL);
3674 SetIconBitmap(toplevel);
3676 if (open_keypad_panel) MenuSelected(None, "keypad");
3679 XtAppMainLoop(app_con);
3685 * Replace setlocale() in the standard library here, because
3686 * it may not support some locales used for localized keyboards.
3688 #if defined(USE_I18N) && !defined(HAVE_SETLOCALE)
3690 char *setlocale(int category, const char *locale)
3692 static char old_locale[100] = "C";
3693 static char cur_locale[100] = "C";
3695 if (locale == NULL) {
3697 } else if (category == LC_ALL) {
3698 strcpy(old_locale, cur_locale);
3699 if (locale[0] == '\0') {
3700 s = getenv("LC_ALL");
3701 if (s == NULL) s = "C"; /* LC_ALL not defined */
3705 strncpy(cur_locale, s, sizeof(cur_locale) - 1);
3711 #endif /* HAVE_SETLOCALE */