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 { "nonexitable", "Secure", XtRBoolean, sizeof(Boolean),
274 Offset(nonexitable), XtRImmediate, (XtPointer)FALSE },
275 { "modalKeytop", "ModalKeytop", XtRBoolean, sizeof(Boolean),
276 Offset(modal_keytop), XtRImmediate, (XtPointer)FALSE },
277 { "modalThreshold", "ModalThreshold", XtRInt, sizeof(int),
278 Offset(modal_threshold), XtRImmediate, (XtPointer)150 },
279 { "keypad", "Keypad", XtRBoolean, sizeof(Boolean),
280 Offset(keypad), XtRImmediate, (XtPointer)TRUE },
281 { "functionkey", "FunctionKey", XtRBoolean, sizeof(Boolean),
282 Offset(function_key), XtRImmediate, (XtPointer)TRUE },
283 { "compact", "Compact", XtRBoolean, sizeof(Boolean),
284 Offset(compact), XtRImmediate, (XtPointer)FALSE },
285 { "keypadOnly", "KeypadOnly", XtRBoolean, sizeof(Boolean),
286 Offset(keypad_only), XtRImmediate, (XtPointer)FALSE },
287 { "keypadKeysym", "KeypadKeysym", XtRBoolean, sizeof(Boolean),
288 Offset(keypad_keysym), XtRImmediate, (XtPointer)FALSE },
289 { "autoAddKeysym", "AutoAddKeysym", XtRBoolean, sizeof(Boolean),
290 Offset(auto_add_keysym), XtRImmediate, (XtPointer)TRUE },
291 { "listWidgets", "Debug", XtRBoolean, sizeof(Boolean),
292 Offset(list_widgets), XtRImmediate, (XtPointer)FALSE },
293 { "positiveModifiers", "PositiveModifiers", XtRString, sizeof(char *),
294 Offset(positive_modifiers), XtRImmediate, "" },
295 { "text", "Text", XtRString, sizeof(char *),
296 Offset(text), XtRImmediate, "" },
297 { "file", "File", XtRString, sizeof(char *),
298 Offset(file), XtRImmediate, "" },
299 { "window", "Window", XtRString, sizeof(char *),
300 Offset(window), XtRImmediate, "" },
301 { "instance", "Instance", XtRString, sizeof(char *),
302 Offset(instance), XtRImmediate, "" },
303 { "widget", "Widget", XtRString, sizeof(char *),
304 Offset(widget), XtRImmediate, "" },
305 { "generalFont", XtCFont, XtRFontStruct, sizeof(XFontStruct *),
306 Offset(general_font), XtRString, XtDefaultFont},
307 { "letterFont", XtCFont, XtRFontStruct, sizeof(XFontStruct *),
308 Offset(letter_font), XtRString, XtDefaultFont},
309 { "specialFont", XtCFont, XtRFontStruct, sizeof(XFontStruct *),
310 Offset(special_font), XtRString, XtDefaultFont},
311 { "keypadFont", XtCFont, XtRFontStruct, sizeof(XFontStruct *),
312 Offset(keypad_font), XtRString, XtDefaultFont},
313 { "generalBackground", XtCBackground, XtRPixel, sizeof(Pixel),
314 Offset(general_background), XtRString, "gray" },
315 { "specialBackground", XtCBackground, XtRPixel, sizeof(Pixel),
316 Offset(special_background), XtRString, "gray" },
317 { "specialForeground", XtCForeground, XtRPixel, sizeof(Pixel),
318 Offset(special_foreground), XtRString, "black" },
320 { "specialFontSet", XtCFontSet, XtRFontSet, sizeof(XFontSet),
321 Offset(special_fontset), XtRString, XtDefaultFontSet},
323 { "highlightBackground", XtCBackground, XtRPixel, sizeof(Pixel),
324 Offset(highlight_background), XtRString, "gray" },
325 { "highlightForeground", XtCForeground, XtRPixel, sizeof(Pixel),
326 Offset(highlight_foreground), XtRString, "forestgreen" },
327 { "focusBackground", XtCBackground, XtRPixel, sizeof(Pixel),
328 Offset(focus_background), XtRString, "gray" },
329 { "remoteFocusBackground", XtCBackground, XtRPixel, sizeof(Pixel),
330 Offset(remote_focus_background), XtRString, "cyan" },
331 { "balloonBackground", XtCBackground, XtRPixel, sizeof(Pixel),
332 Offset(balloon_background), XtRString, "LightYellow1" },
333 { "launchBalloonBackground", XtCBackground, XtRPixel, sizeof(Pixel),
334 Offset(launch_balloon_background), XtRString, "SkyBlue1" },
336 { "normalkeys", "NormalKeys", XtRString, sizeof(char *),
337 Offset(keys_normal), XtRImmediate, "" },
338 { "shiftkeys", "ShiftKeys", XtRString, sizeof(char *),
339 Offset(keys_shift), XtRImmediate, "" },
340 { "altgrkeys", "AltgrKeys", XtRString, sizeof(char *),
341 Offset(keys_altgr), XtRImmediate, "" },
342 { "shiftaltgrkeys", "ShiftAltgrKeys", XtRString, sizeof(char *),
343 Offset(keys_shift_altgr), XtRImmediate, "" },
344 { "keylabels", "KeyLabels", XtRString, sizeof(char *),
345 Offset(key_labels), XtRImmediate, "" },
346 { "normalkeylabels", "NormalKeyLabels", XtRString, sizeof(char *),
347 Offset(normal_key_labels), XtRImmediate, "" },
348 { "shiftkeylabels", "ShiftKeyLabels", XtRString, sizeof(char *),
349 Offset(shift_key_labels), XtRImmediate, "" },
350 { "altgrkeylabels", "AltgrKeyLabels", XtRString, sizeof(char *),
351 Offset(altgr_key_labels), XtRImmediate, "" },
352 { "shiftaltgrkeylabels", "ShiftAltgrKeyLabels", XtRString, sizeof(char *),
353 Offset(shift_altgr_key_labels), XtRImmediate, "" },
355 { "normalkeypad", "NormalKeypad", XtRString, sizeof(char *),
356 Offset(keypad_normal), XtRImmediate, "" },
357 { "shiftkeypad", "ShiftKeypad", XtRString, sizeof(char *),
358 Offset(keypad_shift), XtRImmediate, "" },
359 { "keypad_labels", "KeypadLabels", XtRString, sizeof(char *),
360 Offset(keypad_labels), XtRImmediate, "" },
362 { "deadkeys", "DeadKeys", XtRString, sizeof(char *),
363 Offset(deadkeys), XtRImmediate, "" },
364 { "altgrKeycode", "AltgrKeycode", XtRInt, sizeof(int),
365 Offset(altgr_keycode), XtRImmediate, (XtPointer)0 },
367 { "keyFile", "KeyFile", XtRString, sizeof(char *),
368 Offset(key_file), XtRImmediate, ".xvkbd" },
369 { "dictFile", "DictFile", XtRString, sizeof(char *),
370 Offset(dict_file), XtRImmediate, "/usr/share/dict/words" },
371 { "customizations", "Customizations", XtRString, sizeof(char *),
372 Offset(customizations), XtRImmediate, "default" },
373 { "editableFunctionKeys", "EditableFunctionKeys", XtRInt, sizeof(int),
374 Offset(editable_function_keys), XtRImmediate, (XtPointer)12 },
376 { "maxWidthRatio", "MaxRatio", XtRFloat, sizeof(float),
377 Offset(max_width_ratio), XtRString, "0.9" },
378 { "maxHeightRatio", "MaxRatio", XtRFloat, sizeof(float),
379 Offset(max_height_ratio), XtRString, "0.5" },
380 { "textDelay", "TextDelay", XtRInt, sizeof(int),
381 Offset(text_delay), XtRImmediate, (XtPointer)0 },
383 { "keyClickPitch", "KeyClickPitch", XtRInt, sizeof(int),
384 Offset(key_click_pitch), XtRImmediate, (XtPointer)1000 },
385 { "keyClickDuration", "KeyClickDuration", XtRInt, sizeof(int),
386 Offset(key_click_duration), XtRImmediate, (XtPointer)1 },
387 { "autoClickDelay", "AutoClickDelay", XtRInt, sizeof(int),
388 Offset(autoclick_delay), XtRImmediate, (XtPointer)0 },
392 static XrmOptionDescRec options[] = {
393 { "-geometry", ".windowGeometry", XrmoptionSepArg, NULL },
394 { "-windowgeometry", ".windowGeometry", XrmoptionSepArg, NULL },
395 { "-debug", ".debug", XrmoptionNoArg, "True" },
397 { "-xtest", ".xtest", XrmoptionNoArg, "True" },
398 { "-xsendevent", ".xtest", XrmoptionNoArg, "False" },
399 { "-no-jump-pointer", ".jumpPointer", XrmoptionNoArg, "False" },
400 { "-no-back-pointer", ".jumpPointerBack", XrmoptionNoArg, "False" },
402 { "-no-sync", ".noSync", XrmoptionNoArg, "True" },
403 { "-always-on-top", ".alwaysOnTop", XrmoptionNoArg, "True" }, /* EXPERIMENTAL */
404 { "-quick", ".quickModifiers", XrmoptionNoArg, "True" },
405 { "-modifiers", ".positiveModifiers", XrmoptionSepArg, NULL },
406 { "-text", ".text", XrmoptionSepArg, NULL },
407 { "-file", ".file", XrmoptionSepArg, NULL },
408 { "-delay", ".textDelay", XrmoptionSepArg, NULL },
409 { "-window", ".window", XrmoptionSepArg, NULL },
410 { "-instance", ".instance", XrmoptionSepArg, NULL },
411 { "-widget", ".widget", XrmoptionSepArg, NULL },
412 { "-altgr-lock", ".altgrLock", XrmoptionNoArg, "True" },
413 { "-no-altgr-lock", ".altgrLock", XrmoptionNoArg, "False" },
414 { "-no-repeat", ".autoRepeat", XrmoptionNoArg, "False" },
415 { "-norepeat", ".autoRepeat", XrmoptionNoArg, "False" },
416 { "-no-keypad", ".keypad", XrmoptionNoArg, "False" },
417 { "-nokeypad", ".keypad", XrmoptionNoArg, "False" },
418 { "-no-functionkey", ".functionkey", XrmoptionNoArg, "False" },
419 { "-nofunctionkey", ".functionkey", XrmoptionNoArg, "False" },
420 { "-highlight", ".highlightForeground", XrmoptionSepArg, NULL },
421 { "-compact", ".compact", XrmoptionNoArg, "True" },
422 { "-keypad", ".keypadOnly", XrmoptionNoArg, "True" },
423 { "-true-keypad", ".keypadKeysym", XrmoptionNoArg, "True" },
424 { "-truekeypad", ".keypadKeysym", XrmoptionNoArg, "True" },
425 { "-no-add-keysym", ".autoAddKeysym", XrmoptionNoArg, "False" },
426 { "-altgr-keycode", ".altgrKeycode", XrmoptionSepArg, NULL },
427 { "-list", ".listWidgets", XrmoptionNoArg, "True" },
428 { "-modal", ".modalKeytop", XrmoptionNoArg, "True" },
429 { "-minimizable", ".minimizable", XrmoptionNoArg, "True" },
430 { "-secure", ".secure", XrmoptionNoArg, "True" },
431 { "-no_root", ".no_root", XrmoptionNoArg, "True" },
432 { "-nonexitable", ".nonexitable", XrmoptionNoArg, "True" },
433 { "-xdm", ".Secure", XrmoptionNoArg, "True" },
434 { "-dict", ".dictFile", XrmoptionSepArg, NULL },
435 { "-keyfile", ".keyFile", XrmoptionSepArg, NULL },
436 { "-customizations", ".customizations", XrmoptionSepArg, NULL },
437 { "-version", ".version", XrmoptionNoArg, "True" },
438 { "-help", ".version", XrmoptionNoArg, "True" },
444 static char dict_filename[PATH_MAX] = "";
449 static XtAppContext app_con;
450 static Widget toplevel;
451 static Widget key_widgets[NUM_KEY_ROWS][NUM_KEY_COLS];
452 static Widget main_menu = None;
454 static Dimension toplevel_height = 1000;
457 static Atom wm_delete_window = None;
459 static KeySym *keysym_table = NULL;
460 static int min_keycode, max_keycode;
461 static int keysym_per_keycode;
462 static Boolean error_detected;
464 static int alt_mask = 0;
465 static int meta_mask = 0;
466 static int altgr_mask = 0;
467 static KeySym altgr_keysym = NoSymbol;
469 static int shift_state = 0;
470 static int mouse_shift = 0;
472 static Display *target_dpy = NULL;
474 static Window toplevel_parent = None;
475 static Window focused_window = None;
476 static Window focused_subwindow = None;
478 static Pixmap xvkbd_pixmap = None;
480 static int AddKeysym(KeySym keysym, Boolean top); /* forward */
481 static void SendString(const unsigned char *str);
482 static void MakeKeyboard(Boolean remake);
483 static void MakeKeypad(Widget form, Widget from_vert, Widget from_horiz);
484 static void MakeSunFunctionKey(Widget form, Widget from_vert, Widget from_horiz);
485 static void MakeDeadkeyPanel(Widget form);
486 static void RefreshMainMenu(void);
487 static void PopupFunctionKeyEditor(void);
488 static void DeleteWindowProc(Widget w, XEvent *event, String *pars, Cardinal *n_pars);
491 * Search for window which has specified instance name (WM_NAME)
492 * or class name (WM_CLASS).
494 static Window FindWindow(Window top, char *name)
497 Window *children, dummy;
498 unsigned int nchildren;
505 if (appres.debug) fprintf(stderr, "FindWindow: id=0x%lX", (long)top);
507 if (XGetClassHint(target_dpy, top, &hint)) {
509 if (appres.debug) fprintf(stderr, " instance=\"%s\"", hint.res_name);
510 if ((strlen(name) > 0 && fnmatch(name, hint.res_name, 0) == 0)
511 || (strlen(appres.instance) > 0 && fnmatch(appres.instance, hint.res_name, 0) == 0)) w = top;
512 XFree(hint.res_name);
514 if (strlen(name) > 0 && hint.res_class) {
515 if (appres.debug) fprintf(stderr, " class=\"%s\"", hint.res_class);
516 if (strlen(name) > 0 && fnmatch(name, hint.res_class, 0) == 0) w = top;
517 XFree(hint.res_class);
520 if (XFetchName(target_dpy, top, &win_name)) { /* window title */
521 if (appres.debug) fprintf(stderr, " title=\"%s\"", win_name);
522 if (strlen(name) > 0 && fnmatch(name, win_name, 0) == 0) w = top;
526 if (appres.debug) fprintf(stderr, "%s\n", (w == None) ? "" : " [matched]");
529 XQueryTree(target_dpy, top, &dummy, &dummy, &children, &nchildren)) {
530 for (i = 0; i < nchildren; i++) {
531 w = FindWindow(children[i], name);
532 if (w != None) break;
534 if (children) XFree((char *)children);
541 * This will be called to get window to set input focus,
542 * when user pressed the "Focus" button.
544 static void GetFocusedWindow(void)
548 Window target_root, child;
553 int cur_x, cur_y, last_x, last_y;
554 double x_ratio, y_ratio;
557 target_root = RootWindow(target_dpy, DefaultScreen(target_dpy));
559 cursor = XCreateFontCursor(dpy, (target_dpy == dpy) ? XC_crosshair : XC_dot);
560 if (XGrabPointer(dpy, RootWindow(dpy, DefaultScreen(dpy)), False, ButtonPressMask,
561 GrabModeSync, GrabModeAsync, None,
562 cursor, CurrentTime) == 0) {
564 fprintf(stderr, "Grab pointer - waiting for button press\n");
567 x_ratio = ((double)WidthOfScreen(DefaultScreenOfDisplay(target_dpy))
568 / WidthOfScreen(XtScreen(toplevel)));
569 y_ratio = ((double)HeightOfScreen(DefaultScreenOfDisplay(target_dpy))
570 / HeightOfScreen(XtScreen(toplevel)));
572 XAllowEvents(dpy, SyncPointer, CurrentTime);
573 if (target_dpy == dpy) {
574 XNextEvent(dpy, &event);
576 XCheckTypedEvent(dpy, ButtonPress, &event);
577 if (XQueryPointer(dpy, RootWindow(dpy, DefaultScreen(dpy)), &junk_w, &junk_w,
578 &cur_x, &cur_y, &junk_i, &junk_i, &junk_u)) {
579 cur_x = cur_x * x_ratio;
580 cur_y = cur_y * y_ratio;
582 if (cur_x != last_x || cur_y != last_y) {
583 if (appres.debug) fprintf(stderr, "Moving pointer to (%d, %d) on %s\n",
584 cur_x, cur_y, XDisplayString(target_dpy));
585 XWarpPointer(target_dpy, None, target_root, 0, 0, 0, 0, cur_x, cur_y);
589 XQueryPointer(target_dpy, target_root, &junk_w, &child,
590 &cur_x, &cur_y, &junk_i, &junk_i, &junk_u);
596 } while (event.type != ButtonPress);
597 XUngrabPointer(dpy, CurrentTime);
599 focused_window = None;
600 if (target_dpy == dpy) focused_window = event.xbutton.subwindow;
601 if (focused_window == None) {
603 for (scrn = 0; scrn < ScreenCount(target_dpy); scrn++) {
604 if (XQueryPointer(target_dpy, RootWindow(target_dpy, scrn), &junk_w, &child,
605 &junk_i, &junk_i, &junk_i, &junk_i, &junk_u)) {
607 fprintf(stderr, "Window on the other display/screen (screen #%d of %s) focused\n",
608 scrn, XDisplayString(target_dpy));
609 target_root = RootWindow(target_dpy, scrn);
610 focused_window = child;
615 if (focused_window == None) focused_window = target_root;
616 else focused_window = XmuClientWindow(target_dpy, focused_window);
617 if (appres.debug) fprintf(stderr, "Selected window is: 0x%lX on %s\n",
618 focused_window, XDisplayString(target_dpy));
620 if (target_dpy == dpy && XtWindow(toplevel) == focused_window) {
621 focused_window = None;
622 focused_subwindow = focused_window;
626 focused_subwindow = focused_window;
627 do { /* search the child window */
628 XQueryPointer(target_dpy, focused_subwindow, &junk_w, &child,
629 &junk_i, &junk_i, &junk_i, &junk_i, &junk_u);
631 focused_subwindow = child;
632 if (appres.debug) fprintf(stderr, " going down: 0x%lX\n", focused_subwindow);
634 } while (child != None);
635 if (appres.list_widgets || strlen(appres.widget) != 0) {
636 child = FindWidget(toplevel, focused_window, appres.widget);
637 if (child != None) focused_subwindow = child;
640 fprintf(stderr, "%s: cannot grab pointer\n", PROGRAM_NAME);
645 * Read keyboard mapping and modifier mapping.
646 * Keyboard mapping is used to know what keys are in shifted position.
647 * Modifier mapping is required because we should know Alt and Meta
648 * key are used as which modifier.
650 static void Highlight(char *name, Boolean state);
652 static void ReadKeymap(void)
655 int keycode, inx, pos;
657 XModifierKeymap *modifiers;
661 XDisplayKeycodes(target_dpy, &min_keycode, &max_keycode);
662 if (keysym_table != NULL) XFree(keysym_table);
663 keysym_table = XGetKeyboardMapping(target_dpy,
664 min_keycode, max_keycode - min_keycode + 1,
665 &keysym_per_keycode);
666 for (keycode = min_keycode; keycode <= max_keycode; keycode++) {
667 /* if the first keysym is alphabet and the second keysym is NoSymbol,
668 it is equivalent to pair of lowercase and uppercase alphabet */
669 inx = (keycode - min_keycode) * keysym_per_keycode;
670 if (keysym_table[inx + 1] == NoSymbol
671 && ((XK_A <= keysym_table[inx] && keysym_table[inx] <= XK_Z)
672 || (XK_a <= keysym_table[inx] && keysym_table[inx] <= XK_z))) {
673 if (XK_A <= keysym_table[inx] && keysym_table[inx] <= XK_Z)
674 keysym_table[inx] = keysym_table[inx] - XK_A + XK_a;
675 keysym_table[inx + 1] = keysym_table[inx] - XK_a + XK_A;
679 last_altgr_mask = altgr_mask;
683 altgr_keysym = NoSymbol;
684 modifiers = XGetModifierMapping(target_dpy);
685 for (i = 0; i < 8; i++) {
686 for (pos = 0; pos < modifiers->max_keypermod; pos++) {
687 keycode = modifiers->modifiermap[i * modifiers->max_keypermod + pos];
688 if (keycode < min_keycode || max_keycode < keycode) continue;
690 keysym = keysym_table[(keycode - min_keycode) * keysym_per_keycode];
691 if (keysym == XK_Alt_L || keysym == XK_Alt_R) {
693 } else if (keysym == XK_Meta_L || keysym == XK_Meta_R) {
695 } else if (keysym == XK_Mode_switch) {
697 fprintf(stderr, "%s: found Mode_switch at %dth modifier\n", PROGRAM_NAME, i);
698 if (altgr_keysym == XK_ISO_Level3_Shift) {
700 fprintf(stderr, "%s: both ISO_Level3_Shift and Mode_switch found - ISO_Level3_Shift prefered\n", PROGRAM_NAME);
702 altgr_mask = 0x0101 << i;
703 /* I don't know why, but 0x2000 was required for mod3 on my Linux box */
704 altgr_keysym = keysym;
706 } else if (keysym == XK_ISO_Level3_Shift) {
707 /* if no Mode_switch, try to use ISO_Level3_Shift instead */
708 /* however, it may not work as intended - I don't know why */
710 fprintf(stderr, "%s: found ISO_Level3_Shift at %dth modifier\n", PROGRAM_NAME, i);
711 if (altgr_keysym == XK_Mode_switch) {
713 fprintf(stderr, "%s: both ISO_Level3_Shift and Mode_switch found - ISO_Level3_Shift prefered\n", PROGRAM_NAME);
716 altgr_keysym = keysym;
720 XFreeModifiermap(modifiers);
722 if (altgr_keysym != XK_Mode_switch) {
723 fprintf(stderr, "%s: Mode_switch not available as a modifier\n", PROGRAM_NAME);
724 if (altgr_keysym == XK_ISO_Level3_Shift)
725 fprintf(stderr, "%s: although ISO_Level3_Shift is used instead, AltGr may not work correctly\n", PROGRAM_NAME);
727 fprintf(stderr, "%s: AltGr can't be used\n", PROGRAM_NAME);
730 w = XtNameToWidget(toplevel, "*Multi_key");
732 if (XKeysymToKeycode(target_dpy, XK_Multi_key) == NoSymbol) {
733 if (!appres.auto_add_keysym || AddKeysym(XK_Multi_key, FALSE) == NoSymbol)
734 XtSetSensitive(w, FALSE);
737 w = XtNameToWidget(toplevel, "*Mode_switch");
739 if (appres.xtest && 0 < appres.altgr_keycode) {
740 XtSetSensitive(w, TRUE);
742 fprintf(stderr, "%s: keycode %d will be used for AltGr - it was specified with altgrKeycode\n",
743 PROGRAM_NAME, appres.altgr_keycode);
744 } else if (altgr_mask) {
745 XtSetSensitive(w, TRUE);
747 XtSetSensitive(w, FALSE);
748 if (shift_state & last_altgr_mask) {
749 shift_state &= ~last_altgr_mask;
750 Highlight("Mode_switch", FALSE);
757 * This will called when X error is detected when attempting to
758 * send a event to a client window; this will normally caused
759 * when the client window is destroyed.
761 static int MyErrorHandler(Display *my_dpy, XErrorEvent *event)
765 error_detected = TRUE;
766 if (event->error_code == BadWindow) {
768 fprintf(stderr, "%s: BadWindow - couldn't find target window 0x%lX (destroyed?)\n",
769 PROGRAM_NAME, (long)focused_window);
772 XGetErrorText(my_dpy, event->error_code, msg, sizeof(msg) - 1);
773 fprintf(stderr, "X error trapped: %s, request-code=%d\n", msg, event->request_code);
778 * Send event to the focused window.
779 * If input focus is specified explicitly, select the window
780 * before send event to the window.
782 static void SendEvent(XKeyEvent *event)
784 static Boolean first = TRUE;
786 if (!appres.no_sync) {
787 XSync(event->display, FALSE);
788 XSetErrorHandler(MyErrorHandler);
791 error_detected = FALSE;
792 if (focused_window != None) {
793 /* set input focus if input focus is set explicitly */
795 fprintf(stderr, "Set input focus to window 0x%lX (0x%lX)\n",
796 (long)focused_window, (long)event->window);
797 XSetInputFocus(event->display, focused_window, RevertToParent, CurrentTime);
798 if (!appres.no_sync) XSync(event->display, FALSE);
800 if (!error_detected) {
804 fprintf(stderr, "XTestFakeKeyEvent(0x%lx, %ld, %d)\n",
805 (long)event->display, (long)event->keycode, event->type == KeyPress);
806 if (appres.jump_pointer) {
807 Window root, child, w;
808 int root_x, root_y, x, y;
813 if (first || strlen(appres.text) == 0 || appres.jump_pointer_back) {
816 w = focused_subwindow;
817 if (w == None && appres.jump_pointer_always)
818 XGetInputFocus(event->display, &w, &revert_to);
822 fprintf(stderr, "SendEvent: jump pointer to window 0x%lx\n", (long)w);
824 XQueryPointer(event->display, w,
825 &root, &child, &root_x, &root_y, &x, &y, &mask);
826 XWarpPointer(event->display, None, w, 0, 0, 0, 0, 1, 1);
827 XFlush(event->display);
831 XTestFakeKeyEvent(event->display, event->keycode, event->type == KeyPress, 0);
832 XFlush(event->display);
834 if (w != None && appres.jump_pointer_back) {
835 XWarpPointer(event->display, None, root, 0, 0, 0, 0, root_x, root_y);
836 XFlush(event->display);
839 XTestFakeKeyEvent(event->display, event->keycode, event->type == KeyPress, 0);
840 XFlush(event->display);
843 fprintf(stderr, "%s: this binary is compiled without XTEST support\n",
847 XSendEvent(event->display, event->window, TRUE, KeyPressMask, (XEvent *)event);
848 if (!appres.no_sync) XSync(event->display, FALSE);
851 && (focused_subwindow != None) && (focused_subwindow != event->window)) {
852 error_detected = FALSE;
853 event->window = focused_subwindow;
855 fprintf(stderr, " retry: send event to window 0x%lX (0x%lX)\n",
856 (long)focused_window, (long)event->window);
857 XSendEvent(event->display, event->window, TRUE, KeyPressMask, (XEvent *)event);
858 if (!appres.no_sync) XSync(event->display, FALSE);
863 if (error_detected) {
864 /* reset focus because focused window is (probably) no longer exist */
866 focused_window = None;
867 focused_subwindow = None;
870 XSetErrorHandler(NULL);
874 * Insert a specified keysym to unused position in the keymap table.
875 * This will be called to add required keysyms on-the-fly.
876 * if the second parameter is TRUE, the keysym will be added to the
877 * non-shifted position - this may be required for modifier keys
878 * (e.g. Mode_switch) and some special keys (e.g. F20).
880 static int AddKeysym(KeySym keysym, Boolean top)
882 int keycode, pos, max_pos, inx, phase;
887 max_pos = keysym_per_keycode - 1;
888 if (4 <= max_pos) max_pos = 3;
889 if (2 <= max_pos && altgr_keysym != XK_Mode_switch) max_pos = 1;
892 for (phase = 0; phase < 2; phase++) {
893 for (keycode = max_keycode; min_keycode <= keycode; keycode--) {
894 for (pos = max_pos; 0 <= pos; pos--) {
895 inx = (keycode - min_keycode) * keysym_per_keycode;
896 if ((phase != 0 || keysym_table[inx] == NoSymbol) && keysym_table[inx] < 0xFF00) {
897 /* In the first phase, to avoid modifing existing keys, */
898 /* add the keysym only to the keys which has no keysym in the first position. */
899 /* If no place fuond in the first phase, add the keysym for any keys except */
900 /* for modifier keys and other special keys */
901 if (keysym_table[inx + pos] == NoSymbol) {
903 fprintf(stderr, "*** Adding keysym \"%s\" at keycode %d position %d/%d\n",
904 XKeysymToString(keysym), keycode, pos, keysym_per_keycode);
905 keysym_table[inx + pos] = keysym;
906 XChangeKeyboardMapping(target_dpy, keycode, keysym_per_keycode, &keysym_table[inx], 1);
914 fprintf(stderr, "%s: couldn't add \"%s\" to keymap\n",
915 PROGRAM_NAME, XKeysymToString(keysym));
921 * Add the specified key as a new modifier.
922 * This is used to use Mode_switch (AltGr) as a modifier.
924 static void AddModifier(KeySym keysym)
926 XModifierKeymap *modifiers;
929 keycode = XKeysymToKeycode(target_dpy, keysym);
930 if (keycode == NoSymbol) keycode = AddKeysym(keysym, TRUE);
932 modifiers = XGetModifierMapping(target_dpy);
933 for (i = 7; 3 < i; i--) {
934 if (modifiers->modifiermap[i * modifiers->max_keypermod] == NoSymbol
935 || ((keysym_table[(modifiers->modifiermap[i * modifiers->max_keypermod]
936 - min_keycode) * keysym_per_keycode]) == XK_ISO_Level3_Shift
937 && keysym == XK_Mode_switch)) {
938 for (pos = 0; pos < modifiers->max_keypermod; pos++) {
939 if (modifiers->modifiermap[i * modifiers->max_keypermod + pos] == NoSymbol) {
941 fprintf(stderr, "Adding modifier \"%s\" as %dth modifier\n",
942 XKeysymToString(keysym), i);
943 modifiers->modifiermap[i * modifiers->max_keypermod + pos] = keycode;
944 XSetModifierMapping(target_dpy, modifiers);
950 fprintf(stderr, "%s: couldn't add \"%s\" as modifier\n",
951 PROGRAM_NAME, XKeysymToString(keysym));
956 * Send sequence of KeyPressed/KeyReleased events to the focused
957 * window to simulate keyboard. If modifiers (shift, control, etc)
958 * are set ON, many events will be sent.
960 static void SendKeyPressedEvent(KeySym keysym, unsigned int shift)
966 Window root, *children;
967 unsigned int n_children;
971 if (focused_subwindow != None)
972 cur_focus = focused_subwindow;
974 XGetInputFocus(target_dpy, &cur_focus, &revert_to);
978 if ((keysym & ~0x7f) == 0 && isprint(keysym)) ch = keysym;
979 fprintf(stderr, "SendKeyPressedEvent: focus=0x%lX, key=0x%lX (%c), shift=0x%lX\n",
980 (long)cur_focus, (long)keysym, ch, (long)shift);
983 if (XtWindow(toplevel) != None) {
984 if (toplevel_parent == None) {
985 XQueryTree(target_dpy, RootWindow(target_dpy, DefaultScreen(target_dpy)),
986 &root, &toplevel_parent, &children, &n_children);
989 if (cur_focus == None || cur_focus == PointerRoot
990 || cur_focus == XtWindow(toplevel) || cur_focus == toplevel_parent) {
991 /* notice user when no window focused or the xvkbd window is focused */
999 if (keysym != NoSymbol) {
1000 for (phase = 0; phase < 2; phase++) {
1001 for (keycode = min_keycode; !found && (keycode <= max_keycode); keycode++) {
1002 /* Determine keycode for the keysym: we use this instead
1003 of XKeysymToKeycode() because we must know shift_state, too */
1004 inx = (keycode - min_keycode) * keysym_per_keycode;
1005 if (keysym_table[inx] == keysym) {
1006 shift &= ~altgr_mask;
1007 if (keysym_table[inx + 1] != NoSymbol) shift &= ~ShiftMask;
1010 } else if (keysym_table[inx + 1] == keysym) {
1011 shift &= ~altgr_mask;
1017 if (!found && altgr_mask && 3 <= keysym_per_keycode) {
1018 for (keycode = min_keycode; !found && (keycode <= max_keycode); keycode++) {
1019 inx = (keycode - min_keycode) * keysym_per_keycode;
1020 if (keysym_table[inx + 2] == keysym) {
1021 shift &= ~ShiftMask;
1022 shift |= altgr_mask;
1025 } else if (4 <= keysym_per_keycode && keysym_table[inx + 3] == keysym) {
1026 shift |= ShiftMask | altgr_mask;
1032 if (found || !appres.auto_add_keysym) break;
1034 if (0xF000 <= keysym) {
1035 /* for special keys such as function keys,
1036 first try to add it in the non-shifted position of the keymap */
1037 if (AddKeysym(keysym, TRUE) == NoSymbol) AddKeysym(keysym, FALSE);
1039 AddKeysym(keysym, FALSE);
1044 fprintf(stderr, "SendKeyPressedEvent: keysym=0x%lx, keycode=%ld, shift=0x%lX\n",
1045 (long)keysym, (long)keycode, (long)shift);
1047 fprintf(stderr, "SendKeyPressedEvent: keysym=0x%lx - keycode not found\n",
1052 event.display = target_dpy;
1053 event.window = cur_focus;
1054 event.root = RootWindow(event.display, DefaultScreen(event.display));
1055 event.subwindow = None;
1056 event.time = CurrentTime;
1061 event.same_screen = TRUE;
1066 int root_x, root_y, x, y;
1069 XQueryPointer(target_dpy, event.root, &root, &child, &root_x, &root_y, &x, &y, &mask);
1071 event.type = KeyRelease;
1073 if (mask & ControlMask) {
1074 event.keycode = XKeysymToKeycode(target_dpy, XK_Control_L);
1077 if (mask & alt_mask) {
1078 event.keycode = XKeysymToKeycode(target_dpy, XK_Alt_L);
1081 if (mask & meta_mask) {
1082 event.keycode = XKeysymToKeycode(target_dpy, XK_Meta_L);
1085 if (mask & altgr_mask) {
1086 if (0 < appres.altgr_keycode)
1087 event.keycode = appres.altgr_keycode;
1089 event.keycode = XKeysymToKeycode(target_dpy, altgr_keysym);
1092 if (mask & ShiftMask) {
1093 event.keycode = XKeysymToKeycode(target_dpy, XK_Shift_L);
1096 if (mask & LockMask) {
1097 event.keycode = XKeysymToKeycode(target_dpy, XK_Caps_Lock);
1103 event.type = KeyPress;
1105 if (shift & ControlMask) {
1106 event.keycode = XKeysymToKeycode(target_dpy, XK_Control_L);
1108 event.state |= ControlMask;
1110 if (shift & alt_mask) {
1111 event.keycode = XKeysymToKeycode(target_dpy, XK_Alt_L);
1113 event.state |= alt_mask;
1115 if (shift & meta_mask) {
1116 event.keycode = XKeysymToKeycode(target_dpy, XK_Meta_L);
1118 event.state |= meta_mask;
1120 if (shift & altgr_mask) {
1121 if (0 < appres.altgr_keycode)
1122 event.keycode = appres.altgr_keycode;
1124 event.keycode = XKeysymToKeycode(target_dpy, altgr_keysym);
1126 event.state |= altgr_mask;
1128 if (shift & ShiftMask) {
1129 event.keycode = XKeysymToKeycode(target_dpy, XK_Shift_L);
1131 event.state |= ShiftMask;
1134 if (keysym != NoSymbol) { /* send event for the key itself */
1135 event.keycode = found ? keycode : XKeysymToKeycode(target_dpy, keysym);
1136 if (event.keycode == NoSymbol) {
1137 if ((keysym & ~0x7f) == 0 && isprint(keysym))
1138 fprintf(stderr, "%s: no such key: %c\n",
1139 PROGRAM_NAME, (char)keysym);
1140 else if (XKeysymToString(keysym) != NULL)
1141 fprintf(stderr, "%s: no such key: keysym=%s (0x%lX)\n",
1142 PROGRAM_NAME, XKeysymToString(keysym), (long)keysym);
1144 fprintf(stderr, "%s: no such key: keysym=0x%lX\n",
1145 PROGRAM_NAME, (long)keysym);
1149 event.type = KeyRelease;
1154 event.type = KeyRelease;
1155 if (shift & ShiftMask) {
1156 event.keycode = XKeysymToKeycode(target_dpy, XK_Shift_L);
1158 event.state &= ~ShiftMask;
1160 if (shift & altgr_mask) {
1161 if (0 < appres.altgr_keycode)
1162 event.keycode = appres.altgr_keycode;
1164 event.keycode = XKeysymToKeycode(target_dpy, altgr_keysym);
1166 event.state &= ~altgr_mask;
1168 if (shift & meta_mask) {
1169 event.keycode = XKeysymToKeycode(target_dpy, XK_Meta_L);
1171 event.state &= ~meta_mask;
1173 if (shift & alt_mask) {
1174 event.keycode = XKeysymToKeycode(target_dpy, XK_Alt_L);
1176 event.state &= ~alt_mask;
1178 if (shift & ControlMask) {
1179 event.keycode = XKeysymToKeycode(target_dpy, XK_Control_L);
1181 event.state &= ~ControlMask;
1184 if (appres.no_sync) XFlush(dpy);
1188 * Word completion - list of words which match the prefix entered
1189 * via xvkbd can be listed, and choosing one of them will send the
1190 * suffix to the clients.
1191 * Words for completion will be read from dictionary file specified
1192 * with xvkbd.dictFile resource, such as /usr/dict/words.
1194 static Widget completion_panel = None;
1195 static Widget completion_entry = None;
1196 static Widget completion_list = None;
1198 static Widget props_dict_entry = None;
1200 static char completion_text[100] = "";
1202 #define HASH_SIZE 100
1204 #define Hash(str) ((toupper(str[0]) * 26 + toupper(str[1])) % HASH_SIZE)
1206 static struct WORDLIST {
1207 struct WORDLIST *next;
1209 } completion_words[HASH_SIZE];
1210 static int n_completion_words = 0;
1212 #define MAX_WORDS 200
1214 static String word_list[MAX_WORDS + 1];
1215 static int n_word_list = 0;
1217 static void SetDefaultDictionary(void)
1219 strncpy(dict_filename, appres.dict_file, sizeof(dict_filename));
1220 XtVaSetValues(props_dict_entry, XtNstring, dict_filename, NULL);
1223 static void ReadCompletionDictionary(void)
1225 static Boolean first = TRUE;
1226 static char cur_dict_filename[PATH_MAX] = "";
1228 struct WORDLIST *node_ptr;
1233 if (strcmp(cur_dict_filename, dict_filename) == 0) return;
1234 strcpy(cur_dict_filename, dict_filename);
1238 for (i = 0; i < HASH_SIZE; i++) {
1239 while (completion_words[i].next != NULL) {
1240 p = completion_words[i].next;
1241 completion_words[i].next = p->next;
1247 fprintf(stderr, "ReadCompletionDictionary: %d words freed\n", cnt);
1251 for (i = 0; i < HASH_SIZE; i++) {
1252 completion_words[i].next = NULL;
1253 completion_words[i].word = NULL;
1256 n_completion_words = 0;
1257 fp = fopen(dict_filename, "r");
1259 fprintf(stderr, "%s: can't read dictionary file %s: %s\n",
1260 PROGRAM_NAME, dict_filename, strerror(errno));
1262 while (fgets(str, sizeof(str) - 1, fp)) {
1263 if (3 < strlen(str)) {
1264 str[strlen(str) - 1] = '\0';
1265 node_ptr = &completion_words[Hash(str)];
1266 while (node_ptr->word != NULL) node_ptr = node_ptr->next;
1268 node_ptr->word = XtNewString(str);
1269 node_ptr->next = malloc(sizeof(struct WORDLIST));
1270 node_ptr->next->next = NULL;
1271 node_ptr->next->word = NULL;
1272 n_completion_words++;
1278 fprintf(stderr, "ReadCompletionDictionary: %d words allocated\n", n_completion_words);
1282 static void AddToCompletionText(KeySym keysym)
1285 struct WORDLIST *node_ptr;
1287 if (completion_entry != None) {
1289 if (n_completion_words == 0) {
1290 XtVaSetValues(completion_entry, XtNlabel, "Couldn't read dictionary file", NULL);
1294 len = strlen(completion_text);
1295 if (keysym == XK_BackSpace || keysym == XK_Delete) {
1296 if (0 < len) completion_text[len - 1] = '\0';
1297 } else if (keysym != NoSymbol
1298 && !ispunct((char)keysym) && !isspace((char)keysym)) {
1299 if (len < sizeof(completion_text) - 2) {
1300 completion_text[len + 1] = '\0';
1301 completion_text[len] = keysym;
1304 completion_text[0] = '\0';
1306 XtVaSetValues(completion_entry, XtNlabel, completion_text, NULL);
1309 if (2 <= strlen(completion_text)) {
1310 node_ptr = &completion_words[Hash(completion_text)];
1311 while (node_ptr->word != NULL && n_word_list < MAX_WORDS) {
1312 if (strlen(completion_text) + 1 < strlen(node_ptr->word) &&
1313 strncasecmp(node_ptr->word, completion_text, strlen(completion_text)) == 0) {
1314 word_list[n_word_list] = node_ptr->word;
1315 n_word_list = n_word_list + 1;
1317 node_ptr = node_ptr->next;
1320 word_list[n_word_list] = NULL;
1321 XawListChange(completion_list, word_list, 0, 0, TRUE);
1325 static void CompletionWordSelected(Widget w, XtPointer client_data, XtPointer call_data)
1331 n = ((XawListReturnStruct *)call_data)->list_index;
1332 if (0 <= n && n < n_word_list) {
1334 for (i = 0; completion_text[i] != '\0'; i++) {
1335 if (islower(completion_text[i])) capitalize = FALSE;
1337 for (i = strlen(completion_text); word_list[n][i] != '\0'; i++) {
1338 ch = word_list[n][i];
1339 if (capitalize) ch = toupper(ch);
1340 SendKeyPressedEvent(ch, 0);
1343 AddToCompletionText(NoSymbol);
1346 static void PopupCompletionPanel(void)
1348 Widget form, label, view;
1351 if (completion_panel == None) {
1352 completion_panel = XtVaCreatePopupShell("completion_panel", transientShellWidgetClass,
1354 form = XtVaCreateManagedWidget("form", formWidgetClass, completion_panel, NULL);
1355 label = XtVaCreateManagedWidget("label", labelWidgetClass, form, NULL);
1356 completion_entry = XtVaCreateManagedWidget("entry", labelWidgetClass, form,
1357 XtNfromHoriz, label, NULL);
1358 view = XtVaCreateManagedWidget("view", viewportWidgetClass, form,
1359 XtNfromVert, label, NULL);
1360 completion_list = XtVaCreateManagedWidget("list", listWidgetClass, view, NULL);
1361 XtAddCallback(completion_list, XtNcallback, CompletionWordSelected, NULL);
1362 XtRealizeWidget(completion_panel);
1363 XSetWMProtocols(dpy, XtWindow(completion_panel), &wm_delete_window, 1);
1365 XtPopup(completion_panel, XtGrabNone);
1366 AddToCompletionText(NoSymbol);
1369 XtPopup(completion_panel, XtGrabNone);
1372 ReadCompletionDictionary();
1374 sprintf(msg, "%d words in the dictionary", n_completion_words);
1375 XtVaSetValues(completion_entry, XtNlabel, msg, NULL);
1377 completion_text[0] = '\0';
1379 word_list[n_word_list] = NULL;
1380 XawListChange(completion_list, word_list, 0, 0, TRUE);
1384 * Send given string to the focused window as if the string
1385 * is typed from a keyboard.
1387 static void KeyPressed(Widget w, char *key, char *data);
1389 static void SendString(const unsigned char *str)
1391 const unsigned char *cp, *cp2; /* I remember "unsigned" might be required for some systems */
1395 Window target_root, child, junk_w;
1401 for (cp = str; *cp != '\0'; cp++) {
1402 if (0 < appres.text_delay) usleep(appres.text_delay * 1000);
1407 fprintf(stderr, "%s: missing character after \"\\\"\n",
1410 case '[': /* we can write any keysym as "\[the-keysym]" here */
1411 cp2 = strchr(cp, ']');
1413 fprintf(stderr, "%s: no closing \"]\" after \"\\[\"\n",
1417 if (sizeof(key) <= len) len = sizeof(key) - 1;
1418 strncpy(key, cp + 1, len);
1420 KeyPressed(None, key, NULL);
1424 case 'S': shift_state |= ShiftMask; break;
1425 case 'C': shift_state |= ControlMask; break;
1426 case 'A': shift_state |= alt_mask; break;
1427 case 'M': shift_state |= meta_mask; break;
1428 case 'b': SendKeyPressedEvent(XK_BackSpace, shift_state); shift_state = 0; break;
1429 case 't': SendKeyPressedEvent(XK_Tab, shift_state); shift_state = 0; break;
1430 case 'n': SendKeyPressedEvent(XK_Linefeed, shift_state); shift_state = 0; break;
1431 case 'r': SendKeyPressedEvent(XK_Return, shift_state); shift_state = 0; break;
1432 case 'e': SendKeyPressedEvent(XK_Escape, shift_state); shift_state = 0; break;
1433 case 'd': SendKeyPressedEvent(XK_Delete, shift_state); shift_state = 0; break;
1434 case 'D': /* delay */
1436 if ('1' <= *cp && *cp <= '9') {
1437 usleep((*cp - '0') * 100000);
1439 fprintf(stderr, "%s: no digit after \"\\m\"\n",
1443 case 'm': /* simulate click mouse button */
1445 if ('1' <= *cp && *cp <= '9') {
1446 if (appres.debug) fprintf(stderr, "XTestFakeButtonEvent(%d)\n", *cp - '0');
1447 XTestFakeButtonEvent(target_dpy, *cp - '0', True, CurrentTime);
1448 XTestFakeButtonEvent(target_dpy, *cp - '0', False, CurrentTime);
1451 fprintf(stderr, "%s: no digit after \"\\m\"\n",
1456 case 'y': /* move mouse pointer */
1457 sscanf(cp + 1, "%d", &val);
1458 target_root = RootWindow(target_dpy, DefaultScreen(target_dpy));
1459 XQueryPointer(target_dpy, target_root, &junk_w, &child,
1460 &cur_x, &cur_y, &junk_i, &junk_i, &junk_u);
1462 if (isdigit(*(cp + 1))) cur_x = val;
1465 if (isdigit(*(cp + 1))) cur_y = val;
1468 XWarpPointer(target_dpy, None, target_root, 0, 0, 0, 0, cur_x, cur_y);
1471 while (isdigit(*(cp + 1)) || *(cp + 1) == '+' || *(cp + 1) == '-') cp++;
1474 SendKeyPressedEvent(*cp, shift_state);
1479 SendKeyPressedEvent(*cp, shift_state);
1486 * Send content of the file as if the it is typed from a keyboard.
1488 static void SendFileContent(const char *file)
1494 if (strcmp(file, "-") != 0) fp = fopen(file, "r");
1497 fprintf(stderr, "%s: can't read the file: %s\n", PROGRAM_NAME, file);
1500 while ((ch = fgetc(fp)) != EOF) {
1501 if (0 < appres.text_delay) usleep(appres.text_delay * 1000);
1502 if (ch == '\n') { /* newline - send Return instead */
1503 SendKeyPressedEvent(XK_Return, 0);
1504 } else if (ch == '\033') { /* ESC */
1505 SendKeyPressedEvent(XK_Escape, 0);
1506 } else if (ch == '\177') { /* DEL */
1507 SendKeyPressedEvent(XK_Delete, 0);
1508 } else if (1 <= ch && ch <= 26) { /* Ctrl-x */
1509 SendKeyPressedEvent('a' + ch - 1, ControlMask);
1510 } else { /* normal characters */
1511 SendKeyPressedEvent(ch, 0);
1514 if (strcmp(file, "-") != 0) fclose(fp);
1518 * Highlight/unhighligh spcified modifier key on the screen.
1520 static void Highlight(char *name, Boolean state)
1525 sprintf(name1, "*%s", name);
1526 w = XtNameToWidget(toplevel, name1);
1528 if (strstr(name, "Focus") != NULL) {
1529 if (target_dpy == dpy)
1530 XtVaSetValues(w, XtNbackground, appres.focus_background, NULL);
1532 XtVaSetValues(w, XtNbackground, appres.remote_focus_background, NULL);
1534 XtVaSetValues(w, XtNforeground, appres.highlight_foreground, NULL);
1536 XtVaSetValues(w, XtNforeground, appres.special_foreground, NULL);
1539 XtVaSetValues(w, XtNbackground, appres.highlight_background,
1540 XtNforeground, appres.highlight_foreground, NULL);
1542 XtVaSetValues(w, XtNbackground, appres.special_background,
1543 XtNforeground, appres.special_foreground, NULL);
1549 * Highlight/unhighligh keys on the screen to reflect the state.
1551 static void RefreshShiftState(Boolean force)
1553 static Boolean first = TRUE;
1554 static int last_shift_state = 0;
1555 static int last_mouse_shift = 0;
1556 static int last_num_lock_state = FALSE;
1557 static Display *last_target_dpy = NULL;
1558 static long last_focus = 0;
1561 int first_row, row, col;
1566 cur_shift = shift_state | mouse_shift;
1567 changed = cur_shift ^ (last_shift_state | last_mouse_shift);
1568 if (first || force) changed = 0xffff;
1570 if (changed & ShiftMask) {
1571 Highlight("Shift_L", cur_shift & ShiftMask);
1572 Highlight("Shift_R", cur_shift & ShiftMask);
1574 if (changed & ControlMask) {
1575 Highlight("Control_L", cur_shift & ControlMask);
1576 Highlight("Control_R", cur_shift & ControlMask);
1578 if (changed & alt_mask) {
1579 Highlight("Alt_L", cur_shift & alt_mask);
1580 Highlight("Alt_R", cur_shift & alt_mask);
1582 if (changed & meta_mask) {
1583 Highlight("Meta_L", cur_shift & meta_mask);
1584 Highlight("Meta_R", cur_shift & meta_mask);
1586 if (changed & LockMask) {
1587 Highlight("Caps_Lock", cur_shift & LockMask);
1589 if (changed & altgr_mask) {
1590 Highlight("Mode_switch", cur_shift & altgr_mask);
1592 if (last_num_lock_state != appres.num_lock_state) {
1593 Highlight("Num_Lock", appres.num_lock_state);
1594 Highlight("keypad_panel*Num_Lock", appres.num_lock_state);
1596 if (last_target_dpy != target_dpy || last_focus != focused_window) {
1597 Highlight("Focus", focused_window != 0);
1598 Highlight("keypad*Focus", focused_window != 0);
1599 Highlight("keypad_panel*Focus", focused_window != 0);
1600 last_target_dpy = target_dpy;
1601 last_focus = focused_window;
1604 mask = ShiftMask | LockMask | altgr_mask;
1605 changed = (shift_state & mask) ^ (last_shift_state & mask);
1606 if (first || force) changed = TRUE;
1607 if (changed && !appres.keypad_only
1608 && (appres.modal_keytop || toplevel_height < appres.modal_threshold)) {
1609 first_row = appres.function_key ? 0 : 1;
1610 for (row = first_row; row < NUM_KEY_ROWS; row++) {
1611 for (col = 0; col < NUM_KEY_COLS; col++) {
1612 shifted = (shift_state & ShiftMask);
1613 if (key_widgets[row][col] != None) {
1614 if ((shift_state & altgr_mask) && altgr_key_labels[row][col] != NULL) {
1615 if (shifted && shift_altgr_key_labels[row][col] != NULL)
1616 label = shift_altgr_key_labels[row][col];
1618 label = altgr_key_labels[row][col];
1620 if ((shift_state & LockMask)
1621 && isalpha(keys_normal[row][col][0]) && keys_normal[row][col][1] == '\0')
1623 if (shifted && shift_key_labels[row][col] != NULL)
1624 label = shift_key_labels[row][col];
1626 label = normal_key_labels[row][col];
1628 if (label == NULL) {
1629 fprintf(stderr, "%s: no label for key %d,%d\n", PROGRAM_NAME, row, col);
1632 if (strcmp(label, "space") == 0) label = "";
1633 XtVaSetValues(key_widgets[row][col], XtNlabel, label, NULL);
1639 last_shift_state = shift_state;
1640 last_mouse_shift = mouse_shift;
1641 last_num_lock_state = appres.num_lock_state;
1645 if (appres.xtest && strlen(appres.positive_modifiers) != 0) {
1646 /* modifiers specified in positiveModifiers resouce will be hold down
1647 so that it can be used with, for example, mouse operations */
1650 int root_x, root_y, x, y;
1655 event.display = target_dpy;
1656 event.window = RootWindow(event.display, DefaultScreen(event.display));
1657 event.root = event.window;
1658 event.subwindow = None;
1659 event.time = CurrentTime;
1664 event.same_screen = TRUE;
1667 XQueryPointer(target_dpy, event.root, &root, &child, &root_x, &root_y, &x, &y, &mask);
1669 if (strstr(appres.positive_modifiers, "shift") != NULL
1670 && (shift_state & ShiftMask) != (mask & ShiftMask)) {
1671 event.keycode = XKeysymToKeycode(target_dpy, XK_Shift_L);
1672 event.type = (shift_state & ShiftMask) ? KeyPress : KeyRelease;
1675 if (strstr(appres.positive_modifiers, "control") != NULL
1676 && (shift_state & ControlMask) != (mask & ControlMask)) {
1677 event.keycode = XKeysymToKeycode(target_dpy, XK_Control_L);
1678 event.type = (shift_state & ControlMask) ? KeyPress : KeyRelease;
1681 if (strstr(appres.positive_modifiers, "alt") != NULL
1682 && (shift_state & alt_mask) != (mask & alt_mask)) {
1683 event.keycode = XKeysymToKeycode(target_dpy, XK_Alt_L);
1684 event.type = (shift_state & alt_mask) ? KeyPress : KeyRelease;
1687 if (strstr(appres.positive_modifiers, "meta") != NULL
1688 && (shift_state & meta_mask) != (mask & meta_mask)) {
1689 event.keycode = XKeysymToKeycode(target_dpy, XK_Meta_L);
1690 event.type = (shift_state & meta_mask) ? KeyPress : KeyRelease;
1699 * This function will be called when mouse button is pressed on a key
1700 * on the screen. Most operation will be performed in KeyPressed()
1701 * which will be called as callback for the Command widgets, and we
1702 * only need to check which mouse button is pressed here.
1704 static unsigned int n_key_repeat;
1706 static void ButtonDownAction(Widget w, XEvent *event, String *pars, Cardinal *n_pars)
1710 switch (event->xbutton.button) {
1712 mouse_shift |= ControlMask;
1716 mouse_shift |= ShiftMask;
1719 RefreshShiftState(FALSE);
1723 * This function will be called when mouse button is released on a key
1724 * on the screen, after callback is called.
1726 static void ButtonUpAction(Widget w, XEvent *event, String *pars, Cardinal *n_pars)
1728 if (appres.quick_modifiers) {
1729 if (n_key_repeat == 1) XtCallCallbacks(w, XtNcallback, NULL);
1732 RefreshShiftState(FALSE);
1736 * Get the geometry of the base window.
1738 static char *GetWindowGeometry(Widget w)
1740 static char geom[50];
1745 unsigned int wd, ht, bd, dp;
1747 XtVaGetValues(w, XtNx, &x0, XtNy, &y0, NULL);
1748 XGetGeometry(dpy, XtWindow(w), &root, &x1, &y1, &wd, &ht, &bd, &dp);
1749 sprintf(geom, "%dx%d+%d+%d", wd, ht, (int)(x0 - x1), (int)(y0 - y1));
1755 * Set window manager hint.
1756 * ("Extended Window Manager Hints", http://standards.freedesktop.org/wm-spec/)
1758 static void SetWindowManagerHint(Boolean initial)
1761 if (appres.wm_toolbar) {
1762 Atom net_wm_window_type = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
1763 Atom net_wm_window_type_toolbar = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_TOOLBAR", False);
1764 XChangeProperty(dpy, XtWindow(toplevel),
1765 net_wm_window_type, XA_ATOM, 32,
1767 (unsigned char *) &net_wm_window_type_toolbar, 1);
1769 fprintf(stderr, "SetWindowManagerHint: set _NET_WM_WINDOW_TYPE_TOOLBAR\n");
1773 if (!initial || appres.always_on_top) {
1774 const int net_wm_state_remove = 0;
1775 const int net_wm_state_add = 1;
1776 Atom net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
1777 Atom net_wm_state_above = XInternAtom(dpy, "_NET_WM_STATE_ABOVE", False);
1778 XClientMessageEvent ev;
1779 ev.type = ClientMessage;
1781 ev.window = XtWindow(toplevel);
1782 ev.message_type = net_wm_state;
1784 ev.data.l[0] = appres.always_on_top ? net_wm_state_add : net_wm_state_remove;
1785 ev.data.l[1] = net_wm_state_above;
1787 XSendEvent(dpy, RootWindow(dpy, DefaultScreen(dpy)),
1788 FALSE, SubstructureNotifyMask | SubstructureRedirectMask,
1791 fprintf(stderr, "SetWindowManagerHint: _NET_WM_STATE_ABOVE = %d\n", ev.data.l[0]);
1796 * Restart the program to (possibly) change the keyboard layout,
1797 * by loading the app-default file for the selected "customization".
1799 static void LayoutSelected(Widget w, char *key, char *data)
1801 static char *env_lang, *env_xenv;
1803 char customization[30] = "", lang[30] = "C";
1809 if (strcmp(key, "default") != 0) {
1810 sscanf(key, "%29[^/]/%29s", customization, lang);
1811 sprintf(name, "XVkbd-%s", customization);
1812 xenv = XtResolvePathname(dpy, "app-defaults", name, NULL, NULL, NULL, 0, NULL);
1814 fprintf(stderr, "%s: app-default file \"%s\" not installed\n",
1815 PROGRAM_NAME, name);
1819 env_lang = malloc(strlen("LC_ALL=") + strlen(lang) + 1);
1820 sprintf(env_lang, "LC_ALL=%s", lang);
1823 env_xenv = malloc(strlen("XENVIRONMENT=") + strlen(xenv) + 1);
1824 sprintf(env_xenv, "XENVIRONMENT=%s", xenv);
1826 } else if (getenv("XENVIRONMENT") != NULL) {
1827 putenv("XENVIRONMENT=");
1831 for (i = 1; i < argc1; i++) {
1832 if (strncmp(argv1[i], "-geom", strlen("-geom")) == 0) {
1833 if (appres.inherit_geometry) {
1834 argv1[i + 1] = GetWindowGeometry(toplevel);
1835 } else if (i + 2 == argc1) {
1842 if (i == argc1 && appres.inherit_geometry) {
1843 argv1[argc1++] = "-geometry";
1844 argv1[argc1++] = GetWindowGeometry(toplevel);
1845 argv1[argc1] = NULL;
1849 fprintf(stderr, "XENVIRONMENT=%s, LC_ALL=%s\n", (xenv != NULL) ? xenv : "", lang);
1850 fprintf(stderr, "Exec:");
1851 for (i = 0; i < argc1; i++) fprintf(stderr, " %s", argv1[i]);
1852 fprintf(stderr, "\n");
1855 execvp(argv1[0], argv1);
1859 * Popup a window to select the (possibly) keyboard layout.
1860 * The "XVkbd.customizations" resource will define the list,
1861 * such as "default,german,swissgerman,french,latin1,jisx6004/ja".
1862 * For example, "german" here will make this program to load
1863 * "XVkbd-german" app-default file. Locale for each configuration
1864 * can be specified by putting the locale name after "/".
1866 static void PopupLayoutPanel(void)
1868 static Widget layout_panel = None;
1870 char *customizations;
1874 if (layout_panel == None) {
1875 layout_panel = XtVaCreatePopupShell("layout_panel", transientShellWidgetClass,
1877 box = XtVaCreateManagedWidget("box", boxWidgetClass, layout_panel, NULL);
1879 customizations = XtNewString(appres.customizations);
1880 cp = strtok(customizations, " \t,");
1881 while (cp != NULL) {
1882 cp2 = strchr(cp, '/');
1883 if (cp2 != NULL) *cp2 = '\0'; /* temporary remove '/' */
1884 button = XtVaCreateManagedWidget(cp, commandWidgetClass, box, NULL);
1885 if (cp2 != NULL) *cp2 = '/';
1886 XtAddCallback(button, XtNcallback, (XtCallbackProc)LayoutSelected, XtNewString(cp));
1887 cp = strtok(NULL, " \t,");
1889 XtRealizeWidget(layout_panel);
1890 XSetWMProtocols(dpy, XtWindow(layout_panel), &wm_delete_window, 1);
1892 XtFree(customizations);
1895 XtPopup(layout_panel, XtGrabNone);
1901 static void SaveFunctionKey(void); /* forward */
1903 static Widget props_panel = None;
1904 static Widget autoclick_buttons = None;
1905 static Widget click_buttons = None;
1906 static Boolean props_panel_active = FALSE;
1908 static void PropsItemToggled(Widget w, char *key, char *data)
1910 Boolean last_wm_toolbar = appres.wm_toolbar;
1912 if (!props_panel_active) return;
1915 XtVaGetValues(XtNameToWidget(props_panel, "*use_xtest"),
1916 XtNstate, &appres.xtest, NULL);
1918 XtVaGetValues(XtNameToWidget(props_panel, "*quick_modifiers"),
1919 XtNstate, &appres.quick_modifiers, NULL);
1920 XtVaGetValues(XtNameToWidget(props_panel, "*shift_lock"),
1921 XtNstate, &appres.shift_lock, NULL);
1922 XtVaGetValues(XtNameToWidget(props_panel, "*altgr_lock"),
1923 XtNstate, &appres.altgr_lock, NULL);
1924 XtVaGetValues(XtNameToWidget(props_panel, "*modifiers_lock"),
1925 XtNstate, &appres.modifiers_lock, NULL);
1926 XtVaGetValues(XtNameToWidget(props_panel, "*always_on_top"),
1927 XtNstate, &appres.always_on_top, NULL);
1928 XtVaGetValues(XtNameToWidget(props_panel, "*wm_toolbar"),
1929 XtNstate, &appres.wm_toolbar, NULL);
1930 XtVaGetValues(XtNameToWidget(props_panel, "*jump_pointer"),
1931 XtNstate, &appres.jump_pointer, NULL);
1933 appres.key_click_duration = (int)XawToggleGetCurrent(click_buttons);
1934 appres.autoclick_delay = (int)XawToggleGetCurrent(autoclick_buttons);
1937 SetWindowManagerHint(FALSE);
1939 if (appres.wm_toolbar != last_wm_toolbar) LayoutSelected(None, NULL, NULL);
1942 static void PropsSetState(void)
1945 XtVaSetValues(XtNameToWidget(props_panel, "*use_xtest"),
1946 XtNstate, appres.xtest, NULL);
1948 XtVaSetValues(XtNameToWidget(props_panel, "*quick_modifiers"),
1949 XtNstate, appres.quick_modifiers, NULL);
1950 XtVaSetValues(XtNameToWidget(props_panel, "*shift_lock"),
1951 XtNstate, appres.shift_lock, NULL);
1952 if (XtNameToWidget(toplevel, "*Mode_switch") == None) {
1953 XtSetSensitive(XtNameToWidget(props_panel, "*altgr_lock"), FALSE);
1954 XtVaSetValues(XtNameToWidget(props_panel, "*altgr_lock"),
1955 XtNstate, FALSE, NULL);
1957 XtVaSetValues(XtNameToWidget(props_panel, "*altgr_lock"),
1958 XtNstate, appres.altgr_lock, NULL);
1960 XtVaSetValues(XtNameToWidget(props_panel, "*modifiers_lock"),
1961 XtNstate, appres.modifiers_lock, NULL);
1962 XtVaSetValues(XtNameToWidget(props_panel, "*always_on_top"),
1963 XtNstate, appres.always_on_top, NULL);
1964 XtVaSetValues(XtNameToWidget(props_panel, "*wm_toolbar"),
1965 XtNstate, appres.wm_toolbar, NULL);
1967 XtVaSetValues(XtNameToWidget(props_panel, "*jump_pointer"),
1968 XtNstate, appres.jump_pointer, NULL);
1970 XawToggleSetCurrent(click_buttons, (XtPointer)appres.key_click_duration);
1971 XawToggleSetCurrent(autoclick_buttons, (XtPointer)appres.autoclick_delay);
1974 static void ClosePropsPanel(void)
1976 XtPopdown(props_panel);
1980 if (completion_panel != None) XtPopdown(completion_panel);
1983 static void PopupPropsPanel(void)
1985 static char *props_items[] = {
1987 "shift_lock", "altgr_lock", "modifiers_lock",
1996 if (props_panel == None) {
1997 Widget label, button;
2002 props_panel = XtVaCreatePopupShell("props_panel", transientShellWidgetClass,
2004 form = XtVaCreateManagedWidget("form", formWidgetClass, props_panel, NULL);
2007 for (i = 0; i < XtNumber(props_items); i++) {
2008 w = XtVaCreateManagedWidget(props_items[i], toggleWidgetClass,
2009 form, XtNfromVert, w, NULL);
2010 XtAddCallback(w, XtNcallback, (XtCallbackProc)PropsItemToggled,
2011 (XtPointer)props_items[i]);
2014 label = XtVaCreateManagedWidget("click", labelWidgetClass,
2015 form, XtNfromVert, w, NULL);
2016 button = XtVaCreateManagedWidget("OFF", toggleWidgetClass,
2017 form, XtNfromVert, w, XtNfromHoriz, label,
2018 XtNwidth, 0, XtNhorizDistance, 0, NULL);
2019 XtVaSetValues(button, XtNradioGroup, button, XtNradioData, (XtPointer)0, NULL);
2020 XtAddCallback(button, XtNcallback, (XtCallbackProc)PropsItemToggled,
2022 click_buttons = button;
2023 for (val = 1; val <= 50; val *= 2) {
2025 sprintf(s1, "%dms", val);
2026 button = XtVaCreateManagedWidget(s1, toggleWidgetClass,
2027 form, XtNfromVert, w, XtNfromHoriz, button,
2028 XtNradioData, (XtPointer)val,
2029 XtNradioGroup, click_buttons,
2030 XtNwidth, 0, XtNhorizDistance, 0, NULL);
2031 XtAddCallback(button, XtNcallback, (XtCallbackProc)PropsItemToggled,
2036 label = XtVaCreateManagedWidget("autoclick", 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 autoclick_buttons = button;
2045 for (val = 500; val <= 1000; val += 100) {
2047 sprintf(s1, "%dms", val);
2048 button = XtVaCreateManagedWidget(s1, toggleWidgetClass,
2049 form, XtNfromVert, w, XtNfromHoriz, button,
2050 XtNradioData, (XtPointer)val,
2051 XtNradioGroup, autoclick_buttons,
2052 XtNwidth, 0, XtNhorizDistance, 0, NULL);
2053 XtAddCallback(button, XtNcallback, (XtCallbackProc)PropsItemToggled,
2058 label = XtVaCreateManagedWidget("dict_entry_label", labelWidgetClass,
2059 form, XtNfromVert, w, NULL);
2060 props_dict_entry = XtVaCreateManagedWidget("dict_entry", asciiTextWidgetClass, form,
2061 XtNfromVert, w, XtNfromHoriz, label,
2062 XtNuseStringInPlace, True,
2063 XtNstring, dict_filename,
2064 XtNeditType, XawtextEdit,
2065 XtNlength, sizeof(dict_filename) - 1,
2067 button = XtVaCreateManagedWidget("dict_default_button", commandWidgetClass,
2068 form, XtNfromVert, w, XtNfromHoriz, props_dict_entry,
2070 XtAddCallback(button, XtNcallback, (XtCallbackProc)SetDefaultDictionary, NULL);
2073 w = XtVaCreateManagedWidget("dismiss", commandWidgetClass, form,
2074 XtNfromVert, label, NULL);
2075 XtAddCallback(w, XtNcallback, (XtCallbackProc)ClosePropsPanel, NULL);
2077 XtRealizeWidget(props_panel);
2078 XSetWMProtocols(dpy, XtWindow(props_panel), &wm_delete_window, 1);
2080 XtPopup(props_panel, XtGrabNone);
2083 props_panel_active = TRUE;
2085 if (completion_panel != None) XtPopdown(completion_panel);
2089 * Callback for main menu (activated from "xvkbd" logo).
2091 static Widget about_panel = None;
2092 static Widget keypad_panel = None;
2093 static Widget sun_fkey_panel = None;
2094 static Widget deadkey_panel = None;
2095 static Widget display_panel = None;
2096 static Widget display_status = None;
2098 #define DISPLAY_NAME_LENGTH 50
2100 static void OpenRemoteDisplay(Widget w, char *display_name, char *data)
2102 static char name[DISPLAY_NAME_LENGTH + 10];
2105 focused_window = None;
2106 focused_subwindow = None;
2107 if (target_dpy != NULL && target_dpy != dpy) XCloseDisplay(target_dpy);
2109 strncpy(name, (display_name == NULL) ? "" : display_name, sizeof(name));
2110 for (cp = name; isascii(*cp) && isprint(*cp); cp++) ;
2113 if (strlen(name) == 0) {
2115 XtVaSetValues(display_status, XtNlabel, "Disconnected - local display selected", NULL);
2116 XtPopdown(display_panel);
2118 if (strchr(name, ':') == NULL) strcat(name, ":0");
2119 target_dpy = XOpenDisplay(name);
2120 if (target_dpy == NULL) {
2121 XtVaSetValues(display_status, XtNlabel, "Couldn't connect to the display", NULL);
2125 XtVaSetValues(display_status, XtNlabel, "Connected", NULL);
2126 XtPopdown(display_panel);
2131 if (!altgr_mask && appres.auto_add_keysym) AddModifier(XK_Mode_switch);
2134 RefreshShiftState(FALSE);
2137 static void MenuSelected(Widget w, char *key)
2141 if (strcmp(key, "man") == 0) {
2142 if (!appres.secure) system(appres.show_manual_command);
2143 } else if (strcmp(key, "about") == 0) {
2144 if (about_panel == None) {
2145 about_panel = XtVaCreatePopupShell("about_panel", transientShellWidgetClass,
2147 XtVaCreateManagedWidget("message", labelWidgetClass, about_panel,
2148 XtNlabel, appres.description, NULL);
2149 XtRealizeWidget(about_panel);
2150 XSetWMProtocols(dpy, XtWindow(about_panel), &wm_delete_window, 1);
2152 XtPopup(about_panel, XtGrabNone);
2153 } else if (strcmp(key, "keypad") == 0) {
2154 if (keypad_panel == None) {
2155 keypad_panel = XtVaCreatePopupShell("keypad_panel", transientShellWidgetClass,
2157 form = XtVaCreateManagedWidget("form", formWidgetClass, keypad_panel, NULL);
2158 MakeKeypad(form, None, None);
2159 XtRealizeWidget(keypad_panel);
2160 XSetWMProtocols(dpy, XtWindow(keypad_panel), &wm_delete_window, 1);
2162 XtPopup(keypad_panel, XtGrabNone);
2163 } else if (strcmp(key, "sun_fkey") == 0) {
2164 if (sun_fkey_panel == None) {
2165 sun_fkey_panel = XtVaCreatePopupShell("sun_fkey_panel", transientShellWidgetClass,
2167 form = XtVaCreateManagedWidget("form", formWidgetClass, sun_fkey_panel, NULL);
2168 MakeSunFunctionKey(form, None, None);
2169 XtRealizeWidget(sun_fkey_panel);
2170 XSetWMProtocols(dpy, XtWindow(sun_fkey_panel), &wm_delete_window, 1);
2172 XtPopup(sun_fkey_panel, XtGrabNone);
2173 } else if (strcmp(key, "deadkey") == 0) {
2174 if (deadkey_panel == None) {
2175 deadkey_panel = XtVaCreatePopupShell("deadkey_panel", transientShellWidgetClass,
2177 form = XtVaCreateManagedWidget("form", formWidgetClass, deadkey_panel, NULL);
2178 MakeDeadkeyPanel(form);
2179 XtRealizeWidget(deadkey_panel);
2180 XSetWMProtocols(dpy, XtWindow(deadkey_panel), &wm_delete_window, 1);
2182 XtPopup(deadkey_panel, XtGrabNone);
2183 } else if (strcmp(key, "completion") == 0) {
2184 PopupCompletionPanel();
2185 } else if (strcmp(key, "select_layout") == 0) {
2187 } else if (strcmp(key, "edit_fkey") == 0) {
2188 PopupFunctionKeyEditor();
2189 } else if (strcmp(key, "show_keypad") == 0
2190 || strcmp(key, "show_functionkey") == 0) {
2191 if (strcmp(key, "show_keypad") == 0) appres.keypad = !appres.keypad;
2192 else appres.function_key = !appres.function_key;
2194 } else if (strcmp(key, "props") == 0) {
2196 } else if (strcmp(key, "open_display") == 0) {
2197 if (display_panel == None) {
2198 Widget label, entry, button;
2199 static char display_name[DISPLAY_NAME_LENGTH] = "";
2200 display_panel = XtVaCreatePopupShell("display_panel", transientShellWidgetClass,
2202 form = XtVaCreateManagedWidget("form", formWidgetClass, display_panel, NULL);
2203 label = XtVaCreateManagedWidget("label", labelWidgetClass, form, NULL);
2204 entry = XtVaCreateManagedWidget("entry", asciiTextWidgetClass, form,
2205 XtNfromHoriz, label,
2206 XtNuseStringInPlace, True,
2207 XtNeditType, XawtextEdit,
2208 XtNstring, display_name,
2209 XtNlength, sizeof(display_name) - 1,
2212 button = XtVaCreateManagedWidget("ok", commandWidgetClass, form,
2213 XtNfromHoriz, entry, NULL);
2214 XtAddCallback(button, XtNcallback, (XtCallbackProc)OpenRemoteDisplay, (XtPointer)display_name);
2216 display_status = XtVaCreateManagedWidget("status", labelWidgetClass, form,
2218 XtNlabel, "", NULL);
2219 XtRealizeWidget(display_panel);
2220 XSetWMProtocols(dpy, XtWindow(display_panel), &wm_delete_window, 1);
2222 XtSetKeyboardFocus(display_panel, entry);
2224 XtPopup(display_panel, XtGrabNone);
2225 } else if (strcmp(key, "close_display") == 0) {
2226 OpenRemoteDisplay(None, NULL, NULL);
2227 } else if (strcmp(key, "quit") == 0) {
2228 DeleteWindowProc(None, NULL, NULL, NULL);
2232 static void ClosePopupPanel(Widget w)
2234 if (w == keypad_panel) {
2236 keypad_panel = None;
2237 } else if (w == props_panel) {
2245 * Iconify/uniconify the xvkbd window even if window manager is not
2248 static void IconifyWindow(Widget w, Boolean iconify)
2250 static Widget iconified_window = None;
2251 static Widget uniconify_button = None;
2252 static Position x0, y0;
2254 static unsigned int wd, ht, bd, dp;
2260 XUnmapWindow(dpy, XtWindow(toplevel));
2262 if (iconified_window == None) {
2265 iconified_window = XtVaCreatePopupShell("iconified_window", transientShellWidgetClass,
2266 toplevel, XtNoverrideRedirect, TRUE, NULL);
2267 box = XtVaCreateManagedWidget("form", boxWidgetClass, iconified_window, NULL);
2268 uniconify_button = XtVaCreateManagedWidget("uniconify_button", commandWidgetClass, box,
2269 XtNbitmap, xvkbd_pixmap,
2270 XtNhorizDistance, 10, XtNvertDistance, 0,
2272 XtAddCallback(uniconify_button, XtNcallback, (XtCallbackProc)IconifyWindow, FALSE);
2274 XtRealizeWidget(iconified_window);
2275 XSetWMProtocols(dpy, XtWindow(iconified_window), &wm_delete_window, 1);
2278 XtVaGetValues(toplevel, XtNx, &x0, XtNy, &y0, NULL);
2279 XGetGeometry(dpy, XtWindow(toplevel), &root, &x1, &y1, &wd, &ht, &bd, &dp);
2281 XMoveResizeWindow(dpy, XtWindow(iconified_window), x0 + bd, y0 + bd, wd, ht);
2282 XtPopup(iconified_window, XtGrabNone);
2283 for (i = 9; 0 < i; i--) {
2284 Dimension btn_wd, btn_ht;
2289 XtVaGetValues(uniconify_button, XtNwidth, &btn_wd, XtNheight, &btn_ht, NULL);
2290 if (i == 1 || wd1 < btn_wd) wd1 = btn_wd;
2291 if (i == 1 || ht1 < btn_ht) ht1 = btn_ht;
2292 XMoveResizeWindow(dpy, XtWindow(iconified_window), x0 + bd, y0 + (ht - ht1) + bd, wd1, ht1);
2297 if (iconified_window != None) XtPopdown(iconified_window);
2298 XMapWindow(dpy, XtWindow(toplevel));
2302 static void SignalUser1(void)
2304 XWindowAttributes attr;
2305 XGetWindowAttributes(dpy, XtWindow(toplevel), &attr);
2306 IconifyWindow(None, attr.map_state != IsUnmapped);
2311 * This will be called when user pressed a key on the screen.
2313 static const char *FindFunctionKeyValue(const char *key, Boolean shiftable);
2314 static void ShowBalloon(Widget w, XEvent *event, String *pars, Cardinal *n_pars);
2315 static void KeyClick(void);
2316 static void StopAutoclick(void);
2318 static void KeyPressed(Widget w, char *key, char *data)
2328 if (appres.debug) fprintf(stderr, "KeyPressed: key=%s, widget=%lx\n", key, (long)w);
2330 value = FindFunctionKeyValue(key, TRUE);
2331 if (value != NULL) {
2332 if (appres.debug) fprintf(stderr, "Assigned string: %s\n", value);
2333 if (value[0] == '!') {
2334 if (appres.debug) fprintf(stderr, "Launching: %s\n", value + 1);
2335 if (!appres.secure) system(value + 1);
2337 if (value[0] == '\\') value = value + 1;
2338 if (appres.debug) fprintf(stderr, "Sending: %s\n", value);
2341 ShowBalloon(w, NULL, NULL, NULL);
2345 if (strncmp(key, "Shift", strlen("Shift")) == 0) {
2346 if (shift_state & ShiftMask) SendKeyPressedEvent(NoSymbol, shift_state);
2347 shift_state ^= ShiftMask;
2348 } else if (strncmp(key, "Control", strlen("Control")) == 0) {
2349 if (shift_state & ControlMask) SendKeyPressedEvent(NoSymbol, shift_state);
2350 shift_state ^= ControlMask;
2351 } else if (strncmp(key, "Alt", strlen("Alt")) == 0) {
2352 if (shift_state & alt_mask) SendKeyPressedEvent(NoSymbol, shift_state);
2353 shift_state ^= alt_mask;
2354 } else if (strncmp(key, "Meta", strlen("Meta")) == 0) {
2355 if (shift_state & meta_mask) SendKeyPressedEvent(NoSymbol, shift_state);
2356 shift_state ^= meta_mask;
2357 } else if (strcmp(key, "Caps_Lock") == 0) {
2358 if (shift_state & LockMask) SendKeyPressedEvent(NoSymbol, shift_state);
2359 shift_state ^= LockMask;
2360 } else if (strcmp(key, "Mode_switch") == 0) {
2361 if (shift_state & altgr_mask) SendKeyPressedEvent(NoSymbol, shift_state);
2362 shift_state ^= altgr_mask;
2363 } else if (strcmp(key, "Num_Lock") == 0) {
2364 appres.num_lock_state = !appres.num_lock_state;
2365 } else if (strcmp(key, "Focus") == 0) {
2366 cur_shift = shift_state | mouse_shift;
2367 if (cur_shift & ShiftMask) {
2368 focused_window = None;
2369 focused_subwindow = None;
2374 if (appres.quick_modifiers && mouse_shift == 0 && w != None) {
2379 Dimension btn_wd, btn_ht;
2381 n_key_repeat = n_key_repeat + 1;
2382 if (n_key_repeat == 1) return;
2384 XtVaGetValues(w, XtNwidth, &btn_wd, XtNheight, &btn_ht, NULL);
2385 XQueryPointer(dpy, XtWindow(w), &junk_w, &junk_w,
2386 &junk_i, &junk_i, &cur_x, &cur_y, &junk_u);
2389 if (cur_x < 0 && btn_ht < cur_y) {
2390 mouse_shift |= alt_mask; /* left-down */
2392 if (cur_y < 0) mouse_shift |= ShiftMask; /* up */
2393 else if (btn_ht < cur_y) mouse_shift |= meta_mask; /* down */
2394 if (cur_x < 0) mouse_shift |= ControlMask; /* left */
2395 else if (btn_wd < cur_x) mouse_shift |= altgr_mask; /* right */
2398 cur_shift = shift_state | mouse_shift;
2399 shifted = (cur_shift & ShiftMask);
2402 if (sscanf(key, "pad%d,%d", &row, &col) == 2) {
2403 if (appres.num_lock_state) shifted = !shifted;
2404 key1 = shifted ? keypad_shift[row][col]: keypad[row][col];
2407 if (sscanf(key, "%d,%d", &row, &col) == 2) {
2409 } else if (w != None) {
2410 int first_row = appres.function_key ? 0 : 1;
2411 for (row = first_row; row < NUM_KEY_ROWS; row++) {
2412 for (col = 0; col < NUM_KEY_COLS; col++) {
2413 if (key_widgets[row][col] == w) {
2418 if (col < NUM_KEY_COLS) break;
2422 if ((cur_shift & LockMask)
2423 && isalpha(keys_normal[row][col][0]) && keys_normal[row][col][1] == '\0')
2425 if ((cur_shift & altgr_mask) && keys_altgr[row][col] != NULL) {
2426 if (shifted && keys_shift_altgr[row][col] != NULL) {
2427 key1 = keys_shift_altgr[row][col];
2428 if (strcmp(keys_altgr[row][col], keys_shift_altgr[row][col]) != 0)
2429 cur_shift &= ~ShiftMask;
2431 key1 = keys_altgr[row][col];
2434 if (shifted && keys_shift[row][col] != NULL) {
2435 key1 = keys_shift[row][col];
2436 if (strcmp(keys_normal[row][col], keys_shift[row][col]) != 0)
2437 cur_shift &= ~ShiftMask;
2439 key1 = keys_normal[row][col];
2442 } /* if (found) ... */
2443 } /* if (sscanf(key, "pad%d,%d", ... */
2444 } /* if (w != None) ... */
2445 if (strlen(key1) == 1) {
2446 SendKeyPressedEvent((KeySym)*key1 & 0xff, cur_shift);
2447 AddToCompletionText((KeySym)*key1);
2449 if (islower(key1[0]) && key1[1] == ':') {
2451 case 's': cur_shift |= ShiftMask; break;
2452 case 'c': cur_shift |= ControlMask; break;
2453 case 'a': cur_shift |= alt_mask; break;
2454 case 'm': cur_shift |= meta_mask; break;
2455 default: fprintf(stderr, "%s: unknown modidier: %s\n",
2456 PROGRAM_NAME, key1); break;
2460 keysym = XStringToKeysym(key1);
2461 if ((!appres.keypad_keysym && strncmp(key1, "KP_", 3) == 0)
2462 || XKeysymToKeycode(target_dpy, keysym) == NoSymbol) {
2463 switch ((unsigned)keysym) {
2464 case XK_KP_Equal: keysym = XK_equal; break;
2465 case XK_KP_Divide: keysym = XK_slash; break;
2466 case XK_KP_Multiply: keysym = XK_asterisk; break;
2467 case XK_KP_Add: keysym = XK_plus; break;
2468 case XK_KP_Subtract: keysym = XK_minus; break;
2469 case XK_KP_Enter: keysym = XK_Return; break;
2470 case XK_KP_1: keysym = XK_1; break;
2471 case XK_KP_2: keysym = XK_2; break;
2472 case XK_KP_3: keysym = XK_3; break;
2473 case XK_KP_4: keysym = XK_4; break;
2474 case XK_KP_5: keysym = XK_5; break;
2475 case XK_KP_6: keysym = XK_6; break;
2476 case XK_KP_7: keysym = XK_7; break;
2477 case XK_KP_8: keysym = XK_8; break;
2478 case XK_KP_9: keysym = XK_9; break;
2479 case XK_Shift_L: keysym = XK_Shift_R; break;
2480 case XK_Shift_R: keysym = XK_Shift_L; break;
2481 case XK_Control_L: keysym = XK_Control_R; break;
2482 case XK_Control_R: keysym = XK_Control_L; break;
2483 case XK_Alt_L: keysym = XK_Alt_R; break;
2484 case XK_Alt_R: keysym = XK_Alt_L; break;
2485 case XK_Meta_L: keysym = XK_Meta_R; break;
2486 case XK_Meta_R: keysym = XK_Meta_L; break;
2488 if (keysym == NoSymbol || !appres.auto_add_keysym)
2489 fprintf(stderr, "%s: no such key: %s\n",
2490 PROGRAM_NAME, key1); break;
2493 SendKeyPressedEvent(keysym, cur_shift);
2494 AddToCompletionText(keysym);
2496 if ((cur_shift & ControlMask) && (cur_shift & alt_mask)) {
2497 if (strstr(XServerVendor(dpy), "XFree86") != NULL) {
2498 if (strcmp(key1, "KP_Add") == 0) {
2499 if (!appres.secure) system("xvidtune -next");
2500 } else if (strcmp(key1, "KP_Subtract") == 0) {
2501 if (!appres.secure) system("xvidtune -prev");
2506 if (!appres.shift_lock)
2507 shift_state &= ~ShiftMask;
2508 if (!appres.modifiers_lock)
2509 shift_state &= ~(ControlMask | alt_mask | meta_mask);
2510 if (!appres.altgr_lock)
2511 shift_state &= ~altgr_mask;
2513 RefreshShiftState(FALSE);
2517 /* StopAutoclick(); */
2522 * Redefine keyboard layout.
2523 * "spec" is a sequence of words separated with spaces, and it is
2524 * usally specified in app-defaults file, as:
2526 * xvkbd.AltGrKeys: \
2527 * F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 BackSpace \n\
2528 * Escape \271 \262 \263 \243 \254 \251 { [ ] } \\ ' ^ ' \n\
2531 * White spaces separate the keys, and " \n" (note that white space
2532 * before the \n) separate the rows of keys.
2534 static void RedefineKeys(char *array[NUM_KEY_ROWS][NUM_KEY_COLS], const char *spec)
2536 char *s = XtNewString(spec);
2539 int key_rows = NUM_KEY_ROWS;
2540 int key_cols = NUM_KEY_COLS;
2542 for (row = 0; row < key_rows; row++) {
2543 for (col = 0; col < key_cols; col++) array[row][col] = NULL;
2547 cp = strtok(s, " ");
2548 while (cp != NULL) {
2555 if (key_rows <= row) {
2556 fprintf(stderr, "%s: too many key rows: \"%s\" ignored\n",
2558 } else if (key_cols <= col) {
2559 fprintf(stderr, "%s: too many keys in a row: \"%s\" ignored\n",
2562 array[row][col] = XtNewString(cp);
2566 cp = strtok(NULL, " ");
2572 * Create keyboard on the screen.
2574 static Widget MakeKey(Widget parent, const char *name, const char *label, Pixel color)
2576 static Pixmap up_pixmap = None;
2577 static Pixmap down_pixmap = None;
2578 static Pixmap left_pixmap = None;
2579 static Pixmap right_pixmap = None;
2580 static Pixmap back_pixmap = None;
2582 Window scr = RootWindow(dpy, DefaultScreen(dpy));
2586 if (!appres.auto_repeat
2587 || strncmp(name, "Shift", strlen("Shift")) == 0
2588 || strncmp(name, "Control", strlen("Control")) == 0
2589 || strncmp(name, "Alt", strlen("Alt")) == 0
2590 || strncmp(name, "Meta", strlen("Meta")) == 0
2591 || strcmp(name, "Caps_Lock") == 0
2592 || strcmp(name, "Mode_switch") == 0
2593 || strcmp(name, "Num_Lock") == 0
2594 || strcmp(name, "Focus") == 0) {
2595 w = XtVaCreateManagedWidget(name, commandWidgetClass, parent,
2596 XtNbackground, color, NULL);
2598 w = XtVaCreateManagedWidget(name, repeaterWidgetClass, parent,
2599 XtNbackground, color, NULL);
2601 XtAddCallback(w, XtNcallback, (XtCallbackProc)KeyPressed, (XtPointer)name);
2603 if (label != NULL) {
2604 strncpy(str, label, sizeof(str) - 1);
2605 if (strcmp(str, "space") == 0) strcpy(str, "");
2608 if (str[1] == '_') str[1] = ' ';
2609 if (str[len - 2] == '_') str[len - 2] = ' ';
2611 XtVaSetValues(w, XtNlabel, str, NULL);
2613 if (strcmp(label, "up") == 0) {
2614 if (up_pixmap == None)
2615 up_pixmap = XCreateBitmapFromData(dpy, scr,
2616 (char *)up_bits, up_width, up_height);
2617 XtVaSetValues(w, XtNbitmap, up_pixmap, NULL);
2618 } else if (strcmp(label, "down") == 0) {
2619 if (down_pixmap == None)
2620 down_pixmap = XCreateBitmapFromData(dpy, scr,
2621 (char *)down_bits, down_width, down_height);
2622 XtVaSetValues(w, XtNbitmap, down_pixmap, NULL);
2623 } else if (strcmp(label, "left") == 0) {
2624 if (left_pixmap == None)
2625 left_pixmap = XCreateBitmapFromData(dpy, scr,
2626 (char *)left_bits, left_width, left_height);
2627 XtVaSetValues(w, XtNbitmap, left_pixmap, NULL);
2628 } else if (strcmp(label, "right") == 0) {
2629 if (right_pixmap == None)
2630 right_pixmap = XCreateBitmapFromData(dpy, scr,
2631 (char *)right_bits, right_width, right_height);
2632 XtVaSetValues(w, XtNbitmap, right_pixmap, NULL);
2633 } else if (strcmp(label, "back") == 0) {
2634 if (back_pixmap == None)
2635 back_pixmap = XCreateBitmapFromData(dpy, scr,
2636 (char *)back_bits, back_width, back_height);
2637 XtVaSetValues(w, XtNbitmap, back_pixmap, NULL);
2644 static void MakeKeypad(Widget form, Widget from_vert, Widget from_horiz)
2646 Widget key, left, upper;
2651 Widget keypad_row[NUM_KEYPAD_ROWS];
2654 keypad_box = XtVaCreateManagedWidget("keypad", formWidgetClass, form, NULL);
2655 if (from_horiz != None)
2656 XtVaSetValues(keypad_box, XtNfromHoriz, from_horiz, NULL);
2658 XtVaSetValues(keypad_box, XtNhorizDistance, 0, NULL);
2659 if (from_vert != None)
2660 XtVaSetValues(keypad_box, XtNfromVert, from_vert, NULL);
2662 XtVaSetValues(keypad_box, XtNvertDistance, 0, NULL);
2664 for (row = 0; row < NUM_KEYPAD_ROWS; row++) {
2666 for (col = 0; keypad[row][col] != NULL; col++) {
2667 font = appres.keypad_font;
2668 if (strlen(keypad_label[row][col]) == 1) font = appres.letter_font;
2669 color = appres.special_background;
2670 if (strcmp(keypad[row][col], "Focus") == 0)
2671 color = appres.focus_background;
2672 else if (strcmp(keypad_shift[row][col], ".") == 0
2673 || (strncmp(keypad_shift[row][col], "KP_", 3) == 0
2674 && isdigit(keypad_shift[row][col][3])))
2675 color = appres.general_background;
2676 strcpy(name, keypad[row][col]);
2677 if (strcmp(name, "Focus") != 0 && strcmp(name, "Num_Lock") != 0)
2678 sprintf(name, "pad%d,%d", row, col);
2679 key = MakeKey(keypad_box, XtNewString(name),
2680 keypad_label[row][col], color);
2681 XtVaSetValues(key, XtNfont, font, NULL);
2683 XtVaSetValues(key, XtNfromVert, keypad_row[row - 1], NULL);
2685 XtVaSetValues(key, XtNfromHoriz, left, NULL);
2687 keypad_row[row] = key;
2693 static void MakeSunFunctionKey(Widget form, Widget from_vert, Widget from_horiz)
2695 Widget key, left, upper;
2698 Widget fkey_row[NUM_SUN_FKEY_ROWS];
2700 fkey_box = XtVaCreateManagedWidget("fkey", formWidgetClass, form, NULL);
2701 if (from_horiz != None)
2702 XtVaSetValues(fkey_box, XtNfromHoriz, from_horiz, NULL);
2704 XtVaSetValues(fkey_box, XtNhorizDistance, 0, NULL);
2705 if (from_vert != None)
2706 XtVaSetValues(fkey_box, XtNfromVert, from_vert, NULL);
2708 XtVaSetValues(fkey_box, XtNvertDistance, 0, NULL);
2710 for (row = 0; row < NUM_SUN_FKEY_ROWS; row++) {
2712 for (col = 0; sun_fkey[row][col] != NULL; col++) {
2713 key = MakeKey(fkey_box, sun_fkey[row][col],
2714 sun_fkey_label[row][col], appres.special_background);
2715 XtVaSetValues(key, XtNfont, appres.keypad_font, NULL);
2717 XtVaSetValues(key, XtNfromVert, fkey_row[row - 1], NULL);
2719 XtVaSetValues(key, XtNfromHoriz, left, NULL);
2721 fkey_row[row] = key;
2727 static void MakeDeadkeyPanel(Widget form)
2729 Widget deadkey_box, left, key;
2730 char *deadkeys, *cp, *cp2;
2732 deadkeys = XtNewString(appres.deadkeys);
2734 deadkey_box = XtVaCreateManagedWidget("deadkey", formWidgetClass, form, NULL);
2736 cp = strtok(deadkeys, " \t,");
2737 while (cp != NULL) {
2738 cp2 = XtNewString(cp);
2739 key = MakeKey(deadkey_box, cp2, NULL, appres.general_background);
2740 if (left != None) XtVaSetValues(key, XtNfromHoriz, left, NULL);
2742 cp = strtok(NULL, " \t,");
2747 static void RefreshMainMenu(void)
2749 static Pixmap check_pixmap = None;
2751 if (check_pixmap == None) {
2752 check_pixmap = XCreateBitmapFromData(dpy, RootWindow(dpy, DefaultScreen(dpy)),
2753 (char *)check_bits, check_width, check_height);
2755 XtVaSetValues(XtNameToWidget(main_menu, "*show_keypad"),
2756 XtNrightBitmap, appres.keypad ? check_pixmap : None, NULL);
2757 XtVaSetValues(XtNameToWidget(main_menu, "*show_functionkey"),
2758 XtNrightBitmap, appres.function_key ? check_pixmap : None, NULL);
2760 XtSetSensitive(XtNameToWidget(main_menu, "*edit_fkey"), appres.function_key);
2761 XtSetSensitive(XtNameToWidget(main_menu, "*close_display"), target_dpy != dpy);
2764 static void MakeKeyboard(Boolean remake)
2766 static char *main_menu_items[] = {
2767 "about", "man", "keypad", "sun_fkey", "deadkey", "completion", "",
2774 "open_display", "close_display", "",
2777 Widget form, key, left;
2780 Dimension wd, max_wd;
2781 int row, col, first_row;
2782 char name[50], *label;
2783 Widget key_box[NUM_KEY_ROWS];
2787 #include "xvkbd.xbm"
2788 #include "iconify.xbm"
2791 appres.geometry = GetWindowGeometry(toplevel);
2792 XtUnrealizeWidget(toplevel);
2793 XtDestroyWidget(XtNameToWidget(toplevel, "form"));
2796 form = XtVaCreateManagedWidget("form", formWidgetClass, toplevel, NULL);
2800 first_row = appres.function_key ? 0 : 1;
2801 if (!appres.keypad_only) {
2802 for (row = first_row; row < NUM_KEY_ROWS; row++) {
2803 if (keys_normal[row][0] == NULL) continue;
2805 sprintf(name, "row%d", row);
2806 key_box[row] = XtVaCreateManagedWidget(name, formWidgetClass, form, NULL);
2807 key_box[row + 1] = None;
2808 if (row != first_row)
2809 XtVaSetValues(key_box[row], XtNfromVert, key_box[row - 1], NULL);
2810 else if (!appres.function_key)
2811 XtVaSetValues(key_box[row], XtNvertDistance, 0, NULL);
2814 for (col = 0; keys_normal[row][col] != NULL; col++) {
2815 strcpy(name, keys_normal[row][col]);
2816 if (strcmp(name, "MainMenu") == 0) {
2817 Widget iconify_button = None;
2819 if (appres.minimizable) {
2820 Pixmap iconify_pixmap = XCreateBitmapFromData(dpy, RootWindow(dpy, DefaultScreen(dpy)),
2821 (char *)iconify_bits, iconify_width, iconify_height);
2822 iconify_button = XtVaCreateManagedWidget("Iconify", commandWidgetClass, key_box[row],
2823 XtNbitmap, iconify_pixmap, NULL);
2824 XtAddCallback(iconify_button, XtNcallback, (XtCallbackProc)IconifyWindow, (void *)TRUE);
2827 xvkbd_pixmap = XCreateBitmapFromData(dpy, RootWindow(dpy, DefaultScreen(dpy)),
2828 (char *)xvkbd_bits, xvkbd_width, xvkbd_height);
2829 key = XtVaCreateManagedWidget("MainMenu", menuButtonWidgetClass, key_box[row],
2830 XtNbitmap, xvkbd_pixmap, XtNfromHoriz, iconify_button, NULL);
2831 main_menu = XtVaCreatePopupShell("menu", simpleMenuWidgetClass, key, NULL);
2832 for (i = 0; i < XtNumber(main_menu_items); i++) {
2833 if (strlen(main_menu_items[i]) == 0) {
2834 XtVaCreateManagedWidget("separator", smeLineObjectClass, main_menu, NULL);
2836 menu_entry = XtVaCreateManagedWidget(main_menu_items[i], smeBSBObjectClass,
2838 XtAddCallback(menu_entry, XtNcallback, (XtCallbackProc)MenuSelected,
2839 (XtPointer)main_menu_items[i]);
2843 label = appres.modal_keytop ? normal_key_labels[row][col] : key_labels[row][col];
2844 if (isascii(name[0]) && isupper(name[0])) {
2845 if (strcmp(name, "Focus") == 0) {
2846 color = appres.focus_background;
2847 font = appres.keypad_font;
2849 color = appres.special_background;
2850 if (label != NULL && strchr(label, '\n') != NULL) font = appres.keypad_font;
2851 else font = appres.special_font;
2854 color = appres.general_background;
2855 font = appres.general_font;
2856 if (isalpha(name[0])) font = appres.letter_font;
2857 if (strcmp(name, "space") != 0) sprintf(name, "%d,%d", row, col);
2859 key = MakeKey(key_box[row], XtNewString(name), label, color);
2860 XtVaGetValues(key, XtNwidth, &wd, NULL);
2862 /* keys can be removed by setting its width to 1 */
2863 XtDestroyWidget(key);
2866 XtVaSetValues(key, XtNfont, font, NULL);
2868 if (font == appres.special_font || font == appres.keypad_font)
2869 XtVaSetValues(key, XtNfontSet, appres.special_fontset, NULL);
2874 if (left != None) XtVaSetValues(key, XtNfromHoriz, left, NULL);
2877 key_widgets[row][col] = key;
2882 if (appres.keypad) MakeKeypad(form, key_box[0], key_box[1]);
2884 if (!appres.keypad_only && appres.function_key && appres.keypad) {
2885 XtVaCreateManagedWidget("banner", labelWidgetClass, form,
2886 XtNfromHoriz, key_box[1],
2887 XtNlabel, PROGRAM_NAME_WITH_VERSION, NULL);
2891 XtRealizeWidget(toplevel);
2892 SetWindowManagerHint(TRUE);
2894 if (!remake && strlen(appres.geometry) == 0) {
2897 unsigned int wd, ht, bd, dp;
2900 XGetGeometry(dpy, XtWindow(toplevel), &root, &x1, &y1, &wd, &ht, &bd, &dp);
2901 max_wd = XtScreen(toplevel)->width * appres.max_width_ratio;
2902 max_ht = XtScreen(toplevel)->height * appres.max_height_ratio;
2904 fprintf(stderr, "window size: %dx%d, max size: %dx%d\n", wd, ht, max_wd, max_ht);
2905 if (max_wd < wd || max_ht < ht) {
2906 if (max_wd < wd) wd = max_wd;
2907 if (max_ht < ht) ht = max_ht;
2909 fprintf(stderr, "setting window size: %dx%d\n", wd, ht);
2910 XResizeWindow(dpy, XtWindow(toplevel), wd, ht);
2914 if (!appres.debug && key_box[first_row] != None) {
2915 if (appres.keypad) {
2916 XtVaGetValues(key_box[1], XtNwidth, &max_wd, NULL);
2919 for (row = first_row; row < NUM_KEY_ROWS && key_box[row] != None; row++) {
2920 XtVaGetValues(key_box[row], XtNwidth, &wd, NULL);
2921 if (max_wd < wd) max_wd = wd;
2924 for (row = first_row; row < NUM_KEY_ROWS && key_box[row] != None; row++) {
2925 XtVaSetValues(key_box[row], XtNwidth, max_wd, NULL);
2928 if (0 < strlen(appres.geometry)) {
2929 if (appres.wm_toolbar) {
2931 fprintf(stderr, "window fgeometry ignored; _NET_WM_WINDOW_TYPE_TOOLBAR set on\n");
2934 fprintf(stderr, "setting window geometry: %s\n", appres.geometry);
2935 XtVaSetValues(toplevel, XtNgeometry, appres.geometry, NULL);
2936 XtUnrealizeWidget(toplevel);
2937 XtRealizeWidget(toplevel);
2942 if (main_menu != None) RefreshMainMenu();
2943 RefreshShiftState(FALSE);
2945 XtMapWidget(toplevel);
2947 if (wm_delete_window == None)
2948 wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", FALSE);
2949 XSetWMProtocols(dpy, XtWindow(toplevel), &wm_delete_window, 1);
2951 XtVaGetValues(toplevel, XtNheight, &toplevel_height, NULL);
2955 * WM_DELETE_WINDOW has been sent - terminate the program.
2957 static void DeleteWindowProc(Widget w, XEvent *event,
2958 String *pars, Cardinal *n_pars)
2960 if (appres.nonexitable) {
2964 RefreshShiftState(TRUE);
2965 XtDestroyApplicationContext(XtWidgetToApplicationContext(toplevel));
2971 * Callback for ConfigureNotify event, which will be invoked when
2972 * the toplevel window is resized.
2973 * We may need to switch the keytop labels when window becomes
2974 * smaller than appres.modal_threshold, and vice versa.
2976 static void WindowResized(Widget w, XEvent *event,
2977 String *pars, Cardinal *n_pars)
2981 XtVaGetValues(toplevel, XtNheight, &ht, NULL);
2982 if (appres.modal_threshold <= ht) {
2983 if (toplevel_height < appres.modal_threshold) MakeKeyboard(TRUE);
2985 toplevel_height = ht;
2987 RefreshShiftState(TRUE);
2991 * Load list of text to be assigned to function keys.
2992 * Each line contains name of the key (with optional modifier)
2993 * and the text to be assigned to the key, as:
2996 * s:F2 text for Shift-F2
2999 # define PATH_MAX 300
3002 static char fkey_filename[PATH_MAX] = "";
3004 static struct fkey_struct {
3005 struct fkey_struct *next;
3007 } *fkey_list = NULL;
3009 static void ReadFuncionKeys(void)
3012 char str[200], key[200];
3013 struct fkey_struct *sp = NULL, *new_node;
3018 /* If KeyFile is not started with "/", consider the filename is relative to $HOME */
3019 /* and put value of the $HOME environment variable before the KeyFile. */
3020 /* To avoid possible buffer overflow, $HOME will not be added when resulting filename */
3022 home = getenv("HOME");
3023 if (appres.key_file[0] != '/' && home != NULL
3024 && strlen(home) + strlen(appres.key_file) + 1 < sizeof(fkey_filename))
3025 sprintf(fkey_filename, "%s/%s", home, appres.key_file);
3027 strncpy(fkey_filename, appres.key_file, sizeof(fkey_filename));
3029 strncpy(dict_filename, appres.dict_file, sizeof(dict_filename));
3031 fp = fopen(fkey_filename, "r");
3032 if (fp == NULL) return;
3034 while (fgets(str, sizeof(str) - 1, fp)) {
3035 if (str[0] == '#') {
3036 sscanf(&str[1], "%s %d", key, &val);
3037 if (strcmp(key, "quick_modifiers") == 0)
3038 appres.quick_modifiers = val;
3039 else if (strcmp(key, "shift_lock") == 0)
3040 appres.shift_lock = val;
3041 else if (strcmp(key, "altgr_lock") == 0)
3042 appres.altgr_lock = val;
3043 else if (strcmp(key, "modifiers_lock") == 0)
3044 appres.modifiers_lock = val;
3045 else if (strcmp(key, "key_click") == 0)
3046 appres.key_click_duration = val;
3047 else if (strcmp(key, "autoclick") == 0)
3048 appres.autoclick_delay = val;
3049 else if (strcmp(key, "always_on_top") == 0)
3050 appres.always_on_top = val;
3051 else if (strcmp(key, "wm_toolbar") == 0)
3052 appres.wm_toolbar = val;
3053 else if (strcmp(key, "jump_pointer") == 0)
3054 appres.jump_pointer = val;
3055 else if (strcmp(key, "dict_file") == 0) {
3056 sscanf(&str[1], "%*s %s", &key);
3057 strncpy(dict_filename, key, sizeof(dict_filename));
3059 } else if (isalpha(str[0])) {
3061 if (str[len - 1] == '\n') str[len - 1] = '\0';
3063 new_node = malloc(sizeof(struct fkey_struct));
3064 if (fkey_list == NULL) fkey_list = new_node;
3065 else sp->next = new_node;
3069 sp->value = XtNewString(str);
3076 * Edit string assigned for function keys.
3077 * Modifiers (Shift, Ctrl, etc.) can't be handled here.
3079 static Widget edit_fkey_panel = None;
3080 static Widget fkey_menu_button = None;
3081 static Widget fkey_value_menu_button = None;
3082 static Widget fkey_value_entry = None;
3083 static char fkey_value[100] = "";
3084 static char cur_fkey[20] = "";
3085 static char *cur_fkey_value_mode = "";
3087 static void FKeyValueMenuSelected(Widget w, char *key)
3091 if (key[0] == 'c') {
3092 cur_fkey_value_mode = "command";
3095 cur_fkey_value_mode = "string";
3098 XtVaGetValues(XtNameToWidget(fkey_value_menu_button, key1), XtNlabel, &cp, NULL);
3099 XtVaSetValues(fkey_value_menu_button, XtNlabel, cp, NULL);
3102 static void FKeyMenuSelected(Widget w, char *key)
3104 struct fkey_struct *sp, *sp2;
3106 const char *value, *prefix;
3111 else if (strncmp(key, "Shift-", strlen("Shift-")) == 0)
3112 sprintf(key2, "s:%s", &key[strlen("Shift-")]);
3116 if (strcmp(cur_fkey, key2) != 0) {
3117 if (strlen(cur_fkey) != 0) {
3118 len = strlen(cur_fkey);
3120 for (sp = fkey_list; sp != NULL; sp = sp->next) {
3121 if (strncmp(sp->value, cur_fkey, len) == 0 && isspace(sp->value[len]))
3125 if (strlen(fkey_value) != 0) { /* assign new string for the function key */
3126 if (sp == NULL) { /* it was not defined before now */
3127 sp = malloc(sizeof(struct fkey_struct));
3128 if (fkey_list == NULL) fkey_list = sp;
3129 else sp2->next = sp;
3133 sp->value = realloc(sp->value, len + strlen(fkey_value) + 5);
3135 if (cur_fkey_value_mode[0] == 'c') prefix = "!";
3136 else if (fkey_value[0] == '!') prefix = "\\";
3137 sprintf(sp->value, "%s %s%s", cur_fkey, prefix, fkey_value);
3138 } else { /* empty string - remove the entry for the function key */
3140 if (sp2 != NULL) sp2->next = sp->next;
3141 else fkey_list = sp->next;
3149 XtVaSetValues(fkey_menu_button, XtNlabel, key, NULL);
3151 value = FindFunctionKeyValue(key2, FALSE);
3152 if (value == NULL) value = "";
3154 FKeyValueMenuSelected(None, (value[0] == '!') ? "command" : "string");
3156 if (value[0] == '!' || value[0] == '\\') value = value + 1;
3157 strncpy(fkey_value, value, sizeof(fkey_value) - 1);
3158 XtVaSetValues(fkey_value_entry, XtNstring, fkey_value, NULL);
3160 strcpy(cur_fkey, key2);
3165 static void CloseFunctionKeyPanel(void)
3167 XtPopdown(edit_fkey_panel);
3170 static void SaveFunctionKey(void)
3172 struct fkey_struct *sp;
3175 if (appres.debug) fprintf(stderr, "SaveFunctionKey\n");
3177 if (edit_fkey_panel != None) FKeyMenuSelected(None, NULL);
3179 fp = fopen(fkey_filename, "w");
3181 fprintf(stderr, "%s: can't open \"%s\": %s\n",
3182 PROGRAM_NAME, fkey_filename, strerror(errno));
3185 fprintf(fp, "#quick_modifiers %d\n", appres.quick_modifiers);
3186 fprintf(fp, "#shift_lock %d\n", appres.shift_lock);
3187 fprintf(fp, "#altgr_lock %d\n", appres.altgr_lock);
3188 fprintf(fp, "#modifiers_lock %d\n", appres.modifiers_lock);
3189 fprintf(fp, "#key_click %d\n", appres.key_click_duration);
3190 fprintf(fp, "#autoclick %d\n", appres.autoclick_delay);
3191 fprintf(fp, "#always_on_top %d\n", appres.always_on_top);
3192 fprintf(fp, "#wm_toolbar %d\n", appres.wm_toolbar);
3193 fprintf(fp, "#jump_pointer %d\n", appres.jump_pointer);
3194 fprintf(fp, "#dict_file %s\n", dict_filename);
3195 for (sp = fkey_list; sp != NULL; sp = sp->next) {
3196 fprintf(fp, "%s\n", sp->value);
3200 if (edit_fkey_panel != None) CloseFunctionKeyPanel();
3203 static void PopupFunctionKeyEditor(void)
3205 Widget form, form2, menu, menu_entry, button;
3210 if (edit_fkey_panel == None) {
3211 edit_fkey_panel = XtVaCreatePopupShell("edit_fkey_panel", transientShellWidgetClass,
3213 form = XtVaCreateManagedWidget("form", formWidgetClass, edit_fkey_panel, NULL);
3215 form2 = XtVaCreateManagedWidget("form2", formWidgetClass, form, NULL);
3216 XtVaCreateManagedWidget("fkey_label", labelWidgetClass, form2, NULL);
3217 fkey_menu_button = XtVaCreateManagedWidget("fkey_menu", menuButtonWidgetClass,
3219 menu = XtVaCreatePopupShell("menu", simpleMenuWidgetClass, fkey_menu_button, NULL);
3220 for (j = 0; j <= 1; j++) {
3221 for (i = 1; i <= appres.editable_function_keys; i++) {
3223 sprintf(label, "F%d", i);
3225 sprintf(label, "Shift-F%d", i);
3226 key = XtNewString(label);
3227 menu_entry = XtVaCreateManagedWidget(key, smeBSBObjectClass, menu, NULL);
3228 XtAddCallback(menu_entry, XtNcallback, (XtCallbackProc)FKeyMenuSelected,
3233 fkey_value_menu_button = XtVaCreateManagedWidget("fkey_value_menu", menuButtonWidgetClass,
3235 menu = XtVaCreatePopupShell("menu", simpleMenuWidgetClass, fkey_value_menu_button, NULL);
3236 menu_entry = XtVaCreateManagedWidget("string", smeBSBObjectClass, menu, NULL);
3237 XtAddCallback(menu_entry, XtNcallback, (XtCallbackProc)FKeyValueMenuSelected,
3238 (XtPointer)"string");
3239 menu_entry = XtVaCreateManagedWidget("command", smeBSBObjectClass, menu, NULL);
3240 XtAddCallback(menu_entry, XtNcallback, (XtCallbackProc)FKeyValueMenuSelected,
3241 (XtPointer)"command");
3243 XtVaCreateManagedWidget("fkey_value_sep", labelWidgetClass, form2, NULL);
3245 fkey_value_entry = XtVaCreateManagedWidget("fkey_value", asciiTextWidgetClass, form2,
3246 XtNuseStringInPlace, True,
3247 XtNeditType, XawtextEdit,
3248 XtNstring, fkey_value,
3249 XtNlength, sizeof(fkey_value) - 1,
3252 button = XtVaCreateManagedWidget("save_button", commandWidgetClass, form, NULL);
3253 XtAddCallback(button, XtNcallback, (XtCallbackProc)SaveFunctionKey, NULL);
3255 button = XtVaCreateManagedWidget("close_button", commandWidgetClass, form, NULL);
3256 XtAddCallback(button, XtNcallback, (XtCallbackProc)CloseFunctionKeyPanel, NULL);
3258 XtRealizeWidget(edit_fkey_panel);
3259 XSetWMProtocols(dpy, XtWindow(edit_fkey_panel), &wm_delete_window, 1);
3261 XtSetKeyboardFocus(edit_fkey_panel, fkey_value_entry);
3263 FKeyMenuSelected(None, "F1");
3266 XtPopup(edit_fkey_panel, XtGrabNone);
3270 * If text is assigned to the specified function key,
3271 * return the text. Otherwise, return NULL.
3273 static const char *FindFunctionKeyValue(const char *key, Boolean shiftable)
3277 struct fkey_struct *sp;
3282 if (shift_state & meta_mask) prefix = 'm';
3283 else if (shift_state & alt_mask) prefix = 'a';
3284 else if (shift_state & ControlMask) prefix = 'c';
3285 else if (shift_state & ShiftMask) prefix = 's';
3287 if (prefix == '\0') sprintf(label, "%s", key);
3288 else sprintf(label, "%c:%s", prefix, key);
3289 len = strlen(label);
3291 for (sp = fkey_list; sp != NULL; sp = sp->next) {
3292 if (strncmp(sp->value, label, len) == 0 && isspace(sp->value[len]))
3293 return &(sp->value[len + 1]);
3304 XKeyboardControl kc;
3306 if (0 < appres.key_click_duration) {
3307 XGetKeyboardControl(dpy, &ks);
3309 kc.bell_duration = ks.bell_duration;
3310 kc.bell_pitch = appres.key_click_pitch;
3311 kc.bell_duration = appres.key_click_duration;
3312 XChangeKeyboardControl(dpy, KBBellPitch | KBBellDuration, &kc);
3316 kc.bell_pitch = ks.bell_pitch;
3317 kc.bell_duration = ks.bell_duration;
3318 XChangeKeyboardControl(dpy, KBBellPitch | KBBellDuration, &kc);
3324 * Display balloon message for the function keys,
3325 * if text is assigned to the key.
3327 static Boolean balloon_panel_open = FALSE;
3328 static Widget balloon_panel = None;
3330 static XtIntervalId autoclick_id = (XtIntervalId)0;
3332 static void StopAutoclick(void)
3334 if (autoclick_id != (XtIntervalId)0) {
3335 if (appres.debug) fprintf(stderr, "StopAutoclick: %lx\n", (long)autoclick_id);
3337 XtRemoveTimeOut(autoclick_id);
3338 autoclick_id = (XtIntervalId)0;
3342 static void Autoclick(void)
3346 XTestFakeButtonEvent(target_dpy, 1, True, CurrentTime);
3347 XTestFakeButtonEvent(target_dpy, 1, False, CurrentTime);
3350 static void ShowBalloon(Widget w, XEvent *event, String *pars, Cardinal *n_pars)
3352 static Widget message;
3357 if (strcmp(XtName(w), "MainMenu") == 0) {
3358 value = "Main menu";
3360 if (0 < appres.autoclick_delay) {
3361 autoclick_id = XtAppAddTimeOut(app_con, (long)appres.autoclick_delay,
3362 (XtTimerCallbackProc)Autoclick, (XtPointer)w);
3364 if (appres.debug) fprintf(stderr, "ShowBalloon: auto click triggerd: %lx, %d\n",
3365 (long)autoclick_id, appres.autoclick_delay);
3367 value = FindFunctionKeyValue(XtName(w), TRUE);
3368 if (value == NULL) return;
3371 if (balloon_panel == None) {
3372 balloon_panel = XtVaCreatePopupShell("balloon_panel", transientShellWidgetClass, toplevel,
3373 XtNoverrideRedirect, TRUE, NULL);
3374 message = XtVaCreateManagedWidget("message", labelWidgetClass, balloon_panel, NULL);
3376 XtVaGetValues(w, XtNheight, &ht, NULL);
3377 XtTranslateCoords(w, 6, ht + 2, &x, &y);
3378 XtVaSetValues(balloon_panel, XtNx, x, XtNy, y, NULL);
3379 if (value[0] == '!') {
3380 if (appres.secure) return;
3381 XtVaSetValues(message, XtNlabel, value + 1,
3382 XtNbackground, appres.launch_balloon_background, NULL);
3384 if (value[0] == '\\') value = value + 1;
3385 XtVaSetValues(message, XtNlabel, value,
3386 XtNbackground, appres.balloon_background, NULL);
3388 XtPopup(balloon_panel, XtGrabNone);
3390 balloon_panel_open = TRUE;
3393 static void CloseBalloon(Widget w, XEvent *event, String *pars, Cardinal *n_pars)
3396 if (balloon_panel_open) {
3397 XtPopdown(balloon_panel);
3398 balloon_panel_open = FALSE;
3405 static void SetIconBitmap(Widget w)
3407 #include "xvkbd_icon.xbm"
3408 #include "xvkbd_iconmask.xbm"
3410 Pixmap icon_pixmap, icon_mask;
3412 icon_pixmap = XCreateBitmapFromData(XtDisplay(w), XtWindow(w),
3413 (char *)xvkbd_icon_bits,
3414 xvkbd_icon_width, xvkbd_icon_height);;
3415 icon_mask = XCreateBitmapFromData(XtDisplay(w), XtWindow(w),
3416 (char *)xvkbd_iconmask_bits,
3417 xvkbd_iconmask_width, xvkbd_iconmask_height);
3418 XtVaSetValues(w, XtNiconPixmap, icon_pixmap, XtNiconMask, icon_mask, NULL);
3422 * Callback for VisibilityChanged event, which will be invoked
3423 * when xvkbd window is hidden by other window. ** EXPERIMENTAL **
3425 static void VisibilityChanged(Widget w, XEvent *event,
3426 String *pars, Cardinal *n_pars)
3429 static time_t t1 = 0;
3432 if (!appres.always_on_top) return;
3434 if (balloon_panel_open) return;
3436 if (main_menu != None && XtWindow(main_menu) != None) {
3437 XWindowAttributes attr;
3438 XGetWindowAttributes(dpy, XtWindow(main_menu), &attr);
3439 if (attr.map_state != IsUnmapped) return;
3443 if (t1 != t2) cnt = 0;
3447 fprintf(stderr, "%s: visibility of the window changed (cnt = %d)\n", PROGRAM_NAME, cnt);
3449 XRaiseWindow(XtDisplay(toplevel), XtWindow(toplevel));
3455 int main(int argc, char *argv[])
3457 static XtActionsRec actions[] = {
3458 { "DeleteWindowProc", DeleteWindowProc },
3459 { "WindowResized", WindowResized },
3460 { "VisibilityChanged", VisibilityChanged },
3461 { "ReadKeymap", (XtActionProc)ReadKeymap },
3462 { "ButtonDownAction", ButtonDownAction },
3463 { "ButtonUpAction", ButtonUpAction },
3464 { "ShowBalloon", ShowBalloon },
3465 { "CloseBalloon", CloseBalloon },
3466 { "ClosePopupPanel", (XtActionProc)ClosePopupPanel },
3468 static String fallback_resources[] = {
3469 #include "XVkbd-common.h"
3473 Boolean open_keypad_panel = FALSE;
3479 argv1 = malloc(sizeof(char *) * (argc1 + 5));
3480 memcpy(argv1, argv, sizeof(char *) * argc1);
3481 argv1[argc1] = NULL;
3484 XtSetLanguageProc(NULL, NULL, NULL);
3487 toplevel = XtVaAppInitialize(NULL, "XVkbd",
3488 options, XtNumber(options),
3489 &argc, argv, fallback_resources, NULL);
3490 dpy = XtDisplay(toplevel);
3491 app_con = XtWidgetToApplicationContext(toplevel);
3492 XtAppAddActions(app_con, actions, XtNumber(actions));
3497 fprintf(stderr, "%s: illegal option: %s\n\n", PROGRAM_NAME, argv[1]);
3500 XtGetApplicationResources(toplevel, &appres,
3501 application_resources, XtNumber(application_resources),
3503 if (appres.version) {
3504 fprintf(stdout, "%s\n", appres.description);
3508 if (appres.compact) {
3509 appres.keypad = FALSE;
3510 appres.function_key = FALSE;
3512 if (appres.keypad_only && !appres.keypad) {
3513 appres.keypad_only = FALSE;
3514 open_keypad_panel = TRUE;
3517 if (appres.no_sync) {
3519 XSetErrorHandler(MyErrorHandler);
3522 if (0 < strlen(appres.window) || 0 < strlen(appres.instance)) {
3523 if (strcmp(appres.window, "root") == 0) {
3524 focused_window = RootWindow(dpy, DefaultScreen(dpy));
3525 } else if (sscanf(appres.window, "0x%lX%c", &focused_window, &ch) != 1) {
3526 if (sscanf(appres.window, "%ld%c", &focused_window, &ch) != 1) {
3527 focused_window = FindWindow(RootWindow(dpy, DefaultScreen(dpy)),
3529 if (focused_window == None) {
3530 fprintf(stderr, "%s: no such window: window=%s and class=%s\n", PROGRAM_NAME, appres.window, appres.instance);
3537 focused_subwindow = focused_window;
3540 if (!altgr_mask && appres.auto_add_keysym) AddModifier(XK_Mode_switch);
3542 if (strlen(appres.text) != 0 || strlen(appres.file) != 0) {
3543 appres.keypad_keysym = TRUE;
3544 if (focused_window != None &&
3545 (appres.list_widgets || strlen(appres.widget) != 0)) {
3546 XtVaSetValues(toplevel, XtNwidth, 1, XtNheight, 1, NULL);
3547 XtRealizeWidget(toplevel);
3548 child = FindWidget(toplevel, focused_window, appres.widget);
3549 if (child != None) focused_subwindow = child;
3551 if (strlen(appres.text) != 0)
3552 SendString(appres.text);
3554 SendFileContent(appres.file);
3559 if (0 < strlen(appres.keys_normal))
3560 RedefineKeys(keys_normal, appres.keys_normal);
3561 if (0 < strlen(appres.keys_shift))
3562 RedefineKeys(keys_shift, appres.keys_shift);
3563 if (0 < strlen(appres.keys_altgr))
3564 RedefineKeys(keys_altgr, appres.keys_altgr);
3565 if (0 < strlen(appres.keys_shift_altgr))
3566 RedefineKeys(keys_shift_altgr, appres.keys_shift_altgr);
3568 if (0 < strlen(appres.key_labels))
3569 RedefineKeys(key_labels, appres.key_labels);
3570 if (0 < strlen(appres.normal_key_labels))
3571 RedefineKeys(normal_key_labels, appres.normal_key_labels);
3572 if (0 < strlen(appres.shift_key_labels))
3573 RedefineKeys(shift_key_labels, appres.shift_key_labels);
3574 if (0 < strlen(appres.altgr_key_labels))
3575 RedefineKeys(altgr_key_labels, appres.altgr_key_labels);
3576 if (0 < strlen(appres.shift_altgr_key_labels))
3577 RedefineKeys(shift_altgr_key_labels, appres.shift_altgr_key_labels);
3579 if (0 < strlen(appres.keypad_normal)) {
3580 RedefineKeys(keypad, appres.keypad_normal);
3581 RedefineKeys(keypad_shift, appres.keypad_normal);
3582 RedefineKeys(keypad_label, appres.keypad_normal);
3584 if (0 < strlen(appres.keypad_shift))
3585 RedefineKeys(keypad_shift, appres.keypad_shift);
3586 if (0 < strlen(appres.keypad_labels))
3587 RedefineKeys(keypad_label, appres.keypad_labels);
3589 MakeKeyboard(FALSE);
3591 if (focused_window != None &&
3592 (appres.list_widgets || strlen(appres.widget) != 0)) {
3593 child = FindWidget(toplevel, focused_window, appres.widget);
3594 if (child != None) focused_subwindow = child;
3597 if (main_menu != None) {
3598 if (strlen(dict_filename) == 0)
3599 XtSetSensitive(XtNameToWidget(main_menu, "*completion"), FALSE);
3600 if (strlen(appres.customizations) == 0)
3601 XtSetSensitive(XtNameToWidget(main_menu, "*select_layout"), FALSE);
3602 if (appres.nonexitable)
3603 XtSetSensitive(XtNameToWidget(main_menu, "*quit"), FALSE);
3604 if (appres.secure) {
3605 XtSetSensitive(XtNameToWidget(main_menu, "*man"), FALSE);
3606 XtSetSensitive(XtNameToWidget(main_menu, "*open_display"), FALSE);
3611 if (!XQueryExtension(dpy, "XTEST", &op, &ev, &err)) {
3613 fprintf(stderr, "%s: XTEST extension is not supported by the X server\n",
3615 fprintf(stderr, "%s: XSendEvent will be used instead\n",
3617 appres.xtest = FALSE;
3619 if (main_menu != None) {
3620 XtSetSensitive(XtNameToWidget(main_menu, "*use_xtest"), FALSE);
3626 if (!appres.debug) {
3628 signal(SIGINT, SIG_IGN);
3629 signal(SIGQUIT, SIG_IGN);
3631 struct sigaction sigact;
3632 sigact.sa_handler = SIG_IGN;
3633 sigemptyset(&sigact.sa_mask);
3634 sigact.sa_flags = 0;
3635 sigaction(SIGINT, &sigact, NULL);
3636 sigaction(SIGQUIT, &sigact, NULL);
3642 signal(SIGUSR1, SignalUser1);
3644 struct sigaction sigact;
3645 sigact.sa_handler = SignalUser1;
3646 sigemptyset(&sigact.sa_mask);
3647 sigact.sa_flags = 0;
3648 sigaction(SIGUSR1, &sigact, NULL);
3652 SetIconBitmap(toplevel);
3654 if (open_keypad_panel) MenuSelected(None, "keypad");
3657 XtAppMainLoop(app_con);
3663 * Replace setlocale() in the standard library here, because
3664 * it may not support some locales used for localized keyboards.
3666 #if defined(USE_I18N) && !defined(HAVE_SETLOCALE)
3668 char *setlocale(int category, const char *locale)
3670 static char old_locale[100] = "C";
3671 static char cur_locale[100] = "C";
3673 if (locale == NULL) {
3675 } else if (category == LC_ALL) {
3676 strcpy(old_locale, cur_locale);
3677 if (locale[0] == '\0') {
3678 s = getenv("LC_ALL");
3679 if (s == NULL) s = "C"; /* LC_ALL not defined */
3683 strncpy(cur_locale, s, sizeof(cur_locale) - 1);
3689 #endif /* HAVE_SETLOCALE */