1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2
3 #include "ui_shortcut.h"
4
5 #include <stdio.h> /* sscanf */
6 #include <string.h> /* strchr/memcpy */
7 #include <pobl/bl_def.h> /* HAVE_WINDOWS_H */
8 #include <pobl/bl_mem.h>
9 #include <pobl/bl_debug.h>
10 #include <pobl/bl_file.h>
11 #include <pobl/bl_conf_io.h>
12 #include <pobl/bl_str.h> /* strdup */
13
14 #ifndef CommandMask
15 #define CommandMask (0)
16 #endif
17
18 /*
19 * !! Notice !!
20 * Mod1Mask - Mod5Mask are not distinguished.
21 */
22
23 typedef struct key_func_table {
24 char *name;
25 ui_key_func_t func;
26
27 } key_func_table_t;
28
29 /* --- static variables --- */
30
31 static char *key_file = "mlterm/key";
32
33 /*
34 * Button*Mask is disabled until Button* is specified in ~/.mlterm/key to avoid
35 * such a problem as
36 * http://sourceforge.net/mailarchive/message.php?msg_id=30866232
37 */
38 static int button_mask = 0;
39
40 /* --- static variables --- */
41
42 static key_func_table_t key_func_table[] = {
43 { "IM_HOTKEY", IM_HOTKEY, },
44 { "EXT_KBD", EXT_KBD, },
45 { "OPEN_SCREEN", OPEN_SCREEN, },
46 { "OPEN_PTY", OPEN_PTY, },
47 { "NEXT_PTY", NEXT_PTY, },
48 { "PREV_PTY", PREV_PTY, },
49 { "VSPLIT_SCREEN", VSPLIT_SCREEN, },
50 { "HSPLIT_SCREEN", HSPLIT_SCREEN, },
51 { "NEXT_SCREEN", NEXT_SCREEN, },
52 { "PREV_SCREEN", PREV_SCREEN, },
53 { "CLOSE_SCREEN", CLOSE_SCREEN, },
54 { "HEXPAND_SCREEN", HEXPAND_SCREEN, },
55 { "VEXPAND_SCREEN", VEXPAND_SCREEN, },
56 { "PAGE_UP", PAGE_UP, },
57 { "SCROLL_UP", SCROLL_UP, },
58 { "SCROLL_UP_TO_MARK", SCROLL_UP_TO_MARK, },
59 { "SCROLL_DOWN_TO_MARK", SCROLL_DOWN_TO_MARK, },
60 { "INSERT_SELECTION", INSERT_SELECTION, },
61 { "RESET", RESET, },
62 { "COPY_MODE", COPY_MODE, },
63 { "SET_MARK", SET_MARK, },
64 { "EXIT_PROGRAM", EXIT_PROGRAM, },
65
66 /* obsoleted: alias of OPEN_SCREEN */
67 { "NEW_PTY", OPEN_SCREEN, },
68 };
69
70 /* --- static functions --- */
71
read_conf(ui_shortcut_t * shortcut,char * filename)72 static int read_conf(ui_shortcut_t *shortcut, char *filename) {
73 bl_file_t *from;
74 char *key;
75 char *value;
76
77 if (!(from = bl_file_open(filename, "r"))) {
78 #ifdef DEBUG
79 bl_warn_printf(BL_DEBUG_TAG " %s couldn't be opened.\n", filename);
80 #endif
81
82 return 0;
83 }
84
85 while (bl_conf_io_read(from, &key, &value)) {
86 /*
87 * [shortcut key]=[operation]
88 */
89 ui_shortcut_parse(shortcut, key, value);
90 }
91
92 bl_file_close(from);
93
94 return 1;
95 }
96
97 /* --- global functions --- */
98
ui_shortcut_init(ui_shortcut_t * shortcut)99 void ui_shortcut_init(ui_shortcut_t *shortcut) {
100 char *rcpath;
101
102 ui_key_t default_key_map[] = {
103 /* IM_HOTKEY */
104 { 0, 0, 0, },
105
106 /* EXT_KBD(obsolete) */
107 { 0, 0, 0, },
108
109 #if defined(USE_QUARTZ) || (defined(USE_SDL2) && defined(__APPLE__))
110 /* OPEN_SCREEN */
111 { XK_F1, CommandMask, 1, },
112
113 /* OPEN_PTY */
114 { XK_F2, CommandMask, 1, },
115
116 /* NEXT_PTY */
117 { XK_F3, CommandMask, 1, },
118
119 /* PREV_PTY */
120 { XK_F4, CommandMask, 1, },
121 #else
122 /* OPEN_SCREEN */
123 { XK_F1, ControlMask, 1, },
124
125 /* OPEN_PTY */
126 { XK_F2, ControlMask, 1, },
127
128 /* NEXT_PTY */
129 { XK_F3, ControlMask, 1, },
130
131 /* PREV_PTY */
132 { XK_F4, ControlMask, 1, },
133 #endif
134
135 /* HSPLIT_SCREEN */
136 { XK_F1, ShiftMask, 1, },
137
138 /* VSPLIT_SCREEN */
139 { XK_F2, ShiftMask, 1, },
140
141 /* NEXT_SCREEN */
142 { XK_F3, ShiftMask, 1, },
143
144 /* PREV_SCREEN */
145 { XK_F4, ShiftMask, 1, },
146
147 /* CLOSE_SCREEN */
148 { XK_F5, ShiftMask, 1, },
149
150 /* HEXPAND_SCREEN */
151 { XK_F6, ShiftMask, 1, },
152
153 /* VEXPAND_SCREEN */
154 { XK_F7, ShiftMask, 1, },
155
156 /* PAGE_UP */
157 { XK_Prior, ShiftMask, 1, },
158
159 /* SCROLL_UP */
160 { XK_Up, ShiftMask, 1, },
161
162 /* SCROLL_UP_TO_MARK */
163 { XK_Up, ControlMask|ShiftMask, 1, },
164
165 /* SCROLL_DOWN_TO_MARK */
166 { XK_Down, ControlMask|ShiftMask, 1, },
167
168 /* INSERT_SELECTION */
169 #if defined(USE_QUARTZ) || (defined(USE_SDL2) && defined(__APPLE__))
170 { 'v', CommandMask, 1, },
171 #else
172 { XK_Insert, ShiftMask, 1, },
173 #endif
174
175 /* RESET */
176 { XK_Pause, 0, 1, },
177
178 /* COPY_MODE */
179 { XK_Return, ControlMask|ShiftMask, 1, },
180
181 /* SET_MARK */
182 { 'm', ControlMask|ShiftMask, 1, },
183
184 #ifdef DEBUG
185 /* EXIT_PROGRAM(only for debug) */
186 { XK_F1, ControlMask | ShiftMask, 1, },
187 #else
188 { 0, 0, 0, },
189 #endif
190 };
191
192 memcpy(&shortcut->map, &default_key_map, sizeof(default_key_map));
193
194 if ((shortcut->str_map = malloc(2 * sizeof(ui_str_key_t)))) {
195 shortcut->str_map_size = 2;
196
197 shortcut->str_map[0].ksym = 0;
198 shortcut->str_map[0].state = Button1Mask | ControlMask;
199
200 shortcut->str_map[0].str =
201 #ifdef HAVE_WINDOWS_H
202 strdup("menu:mlterm-menu.exe");
203 #else
204 strdup("menu:mlterm-menu");
205 #endif
206
207 shortcut->str_map[1].ksym = 0;
208 shortcut->str_map[1].state = Button3Mask | ControlMask;
209 shortcut->str_map[1].str =
210 #ifdef HAVE_WINDOWS_H
211 strdup("menu:mlconfig.exe");
212 #else
213 strdup("menu:mlconfig");
214 #endif
215 button_mask |= (Button1Mask | Button3Mask);
216 } else {
217 shortcut->str_map_size = 0;
218 }
219
220 if ((rcpath = bl_get_sys_rc_path(key_file))) {
221 read_conf(shortcut, rcpath);
222 free(rcpath);
223 }
224
225 if ((rcpath = bl_get_user_rc_path(key_file))) {
226 read_conf(shortcut, rcpath);
227 free(rcpath);
228 }
229 }
230
ui_shortcut_final(ui_shortcut_t * shortcut)231 void ui_shortcut_final(ui_shortcut_t *shortcut) {
232 u_int count;
233
234 for (count = 0; count < shortcut->str_map_size; count++) {
235 free(shortcut->str_map[count].str);
236 }
237
238 free(shortcut->str_map);
239 }
240
ui_shortcut_match(ui_shortcut_t * shortcut,ui_key_func_t func,KeySym ksym,u_int state)241 int ui_shortcut_match(ui_shortcut_t *shortcut, ui_key_func_t func, KeySym ksym, u_int state) {
242 if (shortcut->map[func].is_used == 0) {
243 return 0;
244 }
245
246 if ('A' <= ksym && ksym <= 'Z') {
247 ksym += 0x20;
248 }
249
250 /* ingoring except these masks */
251 state &= (ModMask | ControlMask | ShiftMask | CommandMask | button_mask);
252
253 if (state & button_mask) {
254 state &= ~Mod2Mask; /* XXX NumLock */
255 }
256
257 if (shortcut->map[func].ksym == ksym &&
258 shortcut->map[func].state ==
259 (state |
260 ((state & ModMask) && (shortcut->map[func].state & ModMask) == ModMask ? ModMask : 0))) {
261 return 1;
262 } else {
263 return 0;
264 }
265 }
266
ui_shortcut_str(ui_shortcut_t * shortcut,KeySym ksym,u_int state)267 char *ui_shortcut_str(ui_shortcut_t *shortcut, KeySym ksym, u_int state) {
268 u_int count;
269
270 if ('A' <= ksym && ksym <= 'Z') {
271 ksym += 0x20;
272 }
273
274 /* ingoring except these masks */
275 state &= (ModMask | ControlMask | ShiftMask | CommandMask | button_mask);
276
277 if (state & button_mask) {
278 state &= ~Mod2Mask; /* XXX NumLock */
279 }
280
281 for (count = 0; count < shortcut->str_map_size; count++) {
282 if (shortcut->str_map[count].ksym == ksym &&
283 shortcut->str_map[count].state ==
284 (state |
285 ((state & ModMask) && (shortcut->str_map[count].state & ModMask) == ModMask ? ModMask
286 : 0))) {
287 return shortcut->str_map[count].str;
288 }
289 }
290
291 return NULL;
292 }
293
ui_shortcut_parse(ui_shortcut_t * shortcut,char * key,char * oper)294 int ui_shortcut_parse(ui_shortcut_t *shortcut, char *key, char *oper) {
295 char *p;
296 KeySym ksym;
297 u_int state;
298 int count;
299
300 if (strcmp(key, "UNUSED") == 0) {
301 goto replace_shortcut_map;
302 }
303
304 state = 0;
305
306 while ((p = strchr(key, '+')) != NULL) {
307 *(p++) = '\0';
308
309 if (strcmp(key, "Control") == 0) {
310 state |= ControlMask;
311 } else if (strcmp(key, "Shift") == 0) {
312 state |= ShiftMask;
313 } else if (strcmp(key, "Mod") == 0 || strcmp(key, "Alt") == 0) {
314 state |= ModMask;
315 } else if (strncmp(key, "Mod", 3) == 0) {
316 switch (key[3]) {
317 case 0:
318 state |= ModMask;
319 break;
320 case '1':
321 state |= Mod1Mask;
322 break;
323 case '2':
324 state |= Mod2Mask;
325 break;
326 case '3':
327 state |= Mod3Mask;
328 break;
329 case '4':
330 state |= Mod4Mask;
331 break;
332 case '5':
333 state |= Mod5Mask;
334 break;
335 #ifdef DEBUG
336 default:
337 bl_warn_printf(BL_DEBUG_TAG " unrecognized Mod mask(%s)\n", key);
338 break;
339 #endif
340 }
341 } else if (strcmp(key, "Command") == 0) {
342 state |= CommandMask;
343 }
344 #ifdef DEBUG
345 else {
346 bl_warn_printf(BL_DEBUG_TAG " unrecognized mask(%s)\n", key);
347 }
348 #endif
349
350 key = p;
351 }
352
353 if (strncmp(key, "Button", 6) == 0) {
354 state |= (Button1Mask << (key[6] - '1'));
355 ksym = 0;
356 } else if ((ksym = XStringToKeysym(key)) != NoSymbol) {
357 if ('A' <= ksym && ksym <= 'Z') {
358 ksym += 0x20;
359 }
360 } else {
361 return 0;
362 }
363
364 for (count = 0; count < sizeof(key_func_table) / sizeof(key_func_table_t); count++) {
365 ui_key_t *map_entry;
366
367 map_entry = shortcut->map + key_func_table[count].func;
368 if (map_entry->ksym == ksym && map_entry->state == state) {
369 map_entry->is_used = 0;
370 break;
371 }
372 }
373
374 for (count = 0; count < shortcut->str_map_size; count++) {
375 if (shortcut->str_map[count].ksym == ksym && shortcut->str_map[count].state == state) {
376 free(shortcut->str_map[count].str);
377 shortcut->str_map[count] = shortcut->str_map[--shortcut->str_map_size];
378 break;
379 }
380 }
381
382 if (*oper == '"') {
383 char *str;
384 char *p;
385 ui_str_key_t *str_map;
386
387 if (!(str = bl_str_unescape(++oper)) || !(p = strrchr(str, '\"')) ||
388 !(str_map =
389 realloc(shortcut->str_map, sizeof(ui_str_key_t) * (shortcut->str_map_size + 1)))) {
390 free(str);
391
392 return 0;
393 }
394
395 *p = '\0';
396 str_map[shortcut->str_map_size].ksym = ksym;
397 str_map[shortcut->str_map_size].state = state;
398 str_map[shortcut->str_map_size].str = str;
399
400 shortcut->str_map_size++;
401 shortcut->str_map = str_map;
402 } else {
403 replace_shortcut_map:
404 for (count = 0; count < sizeof(key_func_table) / sizeof(key_func_table_t); count++) {
405 if (strcmp(oper, key_func_table[count].name) == 0) {
406 if (strcmp(key, "UNUSED") == 0) {
407 shortcut->map[key_func_table[count].func].is_used = 0;
408
409 return 1;
410 } else {
411 shortcut->map[key_func_table[count].func].is_used = 1;
412 shortcut->map[key_func_table[count].func].ksym = ksym;
413 shortcut->map[key_func_table[count].func].state = state;
414
415 goto success;
416 }
417 }
418 }
419
420 return 0;
421 }
422
423 success:
424 if (state & ButtonMask) {
425 int mask;
426
427 for (mask = Button1Mask; mask <= Button7Mask; mask <<= 1) {
428 if (state & mask) {
429 button_mask |= mask;
430 break;
431 }
432 }
433 }
434
435 return 1;
436 }
437