Dillo v3.1.1-46-g8a360e32
Loading...
Searching...
No Matches
keys.cc
Go to the documentation of this file.
1/*
2 * Key parser
3 *
4 * Copyright (C) 2009 Jorge Arellano Cid <jcid@dillo.org>
5 * Copyright (C) 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 */
12
13#include <FL/Fl.H>
14#include <stdio.h>
15#include <stdlib.h> /* strtol */
16#include <string.h>
17#include <ctype.h>
18
19#include "dlib/dlib.h"
20#include "keys.hh"
21#include "utf8.hh"
22#include "msg.h"
23
24/*
25 * Local data types
26 */
27typedef struct {
28 const char *name;
29 KeysCommand_t cmd;
30 int modifier, key;
31} KeyBinding_t;
32
33typedef struct {
34 const char *name;
35 const int value;
36} Mapping_t;
37
38
39/*
40 * Local data
41 */
42static const Mapping_t keyNames[] = {
43 { "Backspace", FL_BackSpace },
44 { "Delete", FL_Delete },
45 { "Down", FL_Down },
46 { "End", FL_End },
47 { "Esc", FL_Escape },
48 { "F1", FL_F + 1 },
49 { "F2", FL_F + 2 },
50 { "F3", FL_F + 3 },
51 { "F4", FL_F + 4 },
52 { "F5", FL_F + 5 },
53 { "F6", FL_F + 6 },
54 { "F7", FL_F + 7 },
55 { "F8", FL_F + 8 },
56 { "F9", FL_F + 9 },
57 { "F10", FL_F + 10 },
58 { "F11", FL_F + 11 },
59 { "F12", FL_F + 12 },
60 { "Home", FL_Home },
61 { "Insert", FL_Insert },
62 { "Left", FL_Left },
63 { "Menu", FL_Menu },
64 { "PageDown", FL_Page_Down },
65 { "PageUp", FL_Page_Up },
66 { "Print", FL_Print },
67 { "Return", FL_Enter },
68 { "Right", FL_Right },
69 { "Space", ' ' },
70 { "Tab", FL_Tab },
71 { "Up", FL_Up },
72 /* multimedia keys */
73 { "Back", FL_Back },
74 { "Favorites", FL_Favorites },
75 { "Forward", FL_Forward },
76 { "HomePage", FL_Home_Page },
77 { "Mail", FL_Mail },
78 { "MediaNext", FL_Media_Next },
79 { "MediaPlay", FL_Media_Play },
80 { "MediaPrev", FL_Media_Prev },
81 { "MediaStop", FL_Media_Stop },
82 { "Refresh", FL_Refresh },
83 { "Search", FL_Search },
84 { "Sleep", FL_Sleep },
85 { "Stop", FL_Stop },
86 { "VolumeDown", FL_Volume_Down },
87 { "VolumeMute", FL_Volume_Mute },
88 { "VolumeUp", FL_Volume_Up },
89};
90
91static const Mapping_t modifierNames[] = {
92 { "Shift", FL_SHIFT },
93 { "Ctrl", FL_CTRL },
94 { "Alt", FL_ALT },
95 { "Meta", FL_META },
96 { "Button1", FL_BUTTON1 },
97 { "Button2", FL_BUTTON2 },
98 { "Button3", FL_BUTTON3 }
99};
100
101static const KeyBinding_t default_keys[] = {
102 { "nop" , KEYS_NOP , 0 , 0 },
103 { "open" , KEYS_OPEN , FL_CTRL , 'o' },
104 { "new-window" , KEYS_NEW_WINDOW , FL_CTRL , 'n' },
105 { "new-tab" , KEYS_NEW_TAB , FL_CTRL , 't' },
106 { "left-tab" , KEYS_LEFT_TAB , FL_CTRL |
107 FL_SHIFT , FL_Tab },
108 { "left-tab" , KEYS_LEFT_TAB , FL_CTRL , FL_Page_Up },
109 { "right-tab" , KEYS_RIGHT_TAB , FL_CTRL , FL_Tab },
110 { "right-tab" , KEYS_RIGHT_TAB , FL_CTRL , FL_Page_Down },
111 { "close-tab" , KEYS_CLOSE_TAB , FL_CTRL , 'w' },
112 { "find" , KEYS_FIND , FL_CTRL , 'f' },
113 { "websearch" , KEYS_WEBSEARCH , FL_CTRL , 's' },
114 { "bookmarks" , KEYS_BOOKMARKS , FL_CTRL , 'b' },
115 { "reload" , KEYS_RELOAD , FL_CTRL , 'r' },
116 { "stop" , KEYS_STOP , 0 , 0 },
117 { "save" , KEYS_SAVE , 0 , 0 },
118 { "hide-panels" , KEYS_HIDE_PANELS , 0 , FL_Escape },
119 { "file-menu" , KEYS_FILE_MENU , FL_ALT , 'f' },
120 { "close-all" , KEYS_CLOSE_ALL , FL_CTRL , 'q' },
121 { "back" , KEYS_BACK , 0 , FL_BackSpace },
122 { "back" , KEYS_BACK , 0 , ',' },
123 { "forward" , KEYS_FORWARD , FL_SHIFT , FL_BackSpace },
124 { "forward" , KEYS_FORWARD , 0 , '.' },
125 { "goto" , KEYS_GOTO , FL_CTRL , 'l' },
126 { "home" , KEYS_HOME , FL_CTRL , 'h' },
127 { "view-source" , KEYS_VIEW_SOURCE , FL_CTRL , 'u' },
128 { "screen-up" , KEYS_SCREEN_UP , 0 , FL_Page_Up },
129 { "screen-up" , KEYS_SCREEN_UP , 0 , 'b' },
130 { "screen-down" , KEYS_SCREEN_DOWN , 0 , FL_Page_Down },
131 { "screen-down" , KEYS_SCREEN_DOWN , 0 , ' ' },
132 { "screen-left" , KEYS_SCREEN_LEFT , 0 , 0 },
133 { "screen-right" , KEYS_SCREEN_RIGHT , 0 , 0 },
134 { "line-up" , KEYS_LINE_UP , 0 , FL_Up },
135 { "line-down" , KEYS_LINE_DOWN , 0 , FL_Down },
136 { "left" , KEYS_LEFT , 0 , FL_Left },
137 { "right" , KEYS_RIGHT , 0 , FL_Right },
138 { "top" , KEYS_TOP , 0 , FL_Home },
139 { "bottom" , KEYS_BOTTOM , 0 , FL_End },
140 { "zoom-in" , KEYS_ZOOM_IN , FL_CTRL , '+' },
141 { "zoom-in" , KEYS_ZOOM_IN , FL_CTRL , '=' /* US + */ },
142 { "zoom-out" , KEYS_ZOOM_OUT , FL_CTRL , '-' },
143 { "zoom-reset" , KEYS_ZOOM_RESET , FL_CTRL , '0' },
144};
145
147
148
149
154{
155 KeyBinding_t *node;
156
157 // Fill our key bindings list
158 bindings = dList_new(32);
159 for (uint_t i = 0; i < sizeof(default_keys) / sizeof(default_keys[0]); i++) {
160 if (default_keys[i].key) {
161 node = dNew(KeyBinding_t, 1);
162 node->name = dStrdup(default_keys[i].name);
163 node->cmd = default_keys[i].cmd;
164 node->modifier = default_keys[i].modifier;
165 node->key = default_keys[i].key;
167 }
168 }
169}
170
175{
176 KeyBinding_t *node;
177
178 while ((node = (KeyBinding_t*)dList_nth_data(bindings, 0))) {
179 dFree((char*)node->name);
181 dFree(node);
182 }
184}
185
189int Keys::nodeByKeyCmp(const void *node, const void *key)
190{
191 KeyBinding_t *n = (KeyBinding_t*)node, *k = (KeyBinding_t*)key;
192 _MSG("Keys::nodeByKeyCmp modifier=%d\n", k->modifier);
193 return (n->key != k->key) ? (n->key - k->key) : (n->modifier - k->modifier);
194}
195
201{
203 KeyBinding_t keyNode;
204
205 keyNode.modifier = Fl::event_state() & (FL_SHIFT | FL_CTRL |FL_ALT|FL_META);
206 if (iscntrl(Fl::event_text()[0])) {
207 keyNode.key = Fl::event_key();
208 } else {
209 const char *beyond = Fl::event_text() + Fl::event_length();
210 keyNode.key = a_Utf8_decode(Fl::event_text(), beyond, NULL);
211
212 /* BUG: The idea is to drop the modifiers if their use results in a
213 * different character (e.g., if shift-8 gives '*', drop the shift,
214 * but if ctrl-6 gives '6', keep the ctrl), but we have to compare a
215 * keysym with a Unicode codepoint, which only works for characters
216 * below U+0100 (those known to latin-1).
217 */
218 if (keyNode.key != Fl::event_key())
219 keyNode.modifier = 0;
220 }
221 _MSG("getKeyCmd: evkey=0x%x evtext=\'%s\' key=0x%x, mod=0x%x\n",
222 Fl::event_key(), Fl::event_text(), keyNode.key, keyNode.modifier);
223 void *data = dList_find_sorted(bindings, &keyNode, nodeByKeyCmp);
224 if (data)
225 ret = ((KeyBinding_t*)data)->cmd;
226 return ret;
227}
228
232void Keys::delKeyCmd(int key, int mod)
233{
234 KeyBinding_t keyNode, *node;
235 keyNode.key = key;
236 keyNode.modifier = mod;
237
238 node = (KeyBinding_t*) dList_find_sorted(bindings, &keyNode, nodeByKeyCmp);
239 if (node) {
240 dList_remove(bindings, node);
241 dFree((char*)node->name);
242 dFree(node);
243 }
244}
245
251int Keys::getKeyCode(char *keyName)
252{
253 uint_t i;
254 for (i = 0; i < sizeof(keyNames) / sizeof(keyNames[0]); i++) {
255 if (!dStrAsciiCasecmp(keyNames[i].name, keyName)) {
256 return keyNames[i].value;
257 }
258 }
259
260 return -1;
261}
262
267KeysCommand_t Keys::getCmdCode(const char *commandName)
268{
269 uint_t i;
270
271 for (i = 0; i < sizeof(default_keys) / sizeof(KeyBinding_t); i++) {
272 if (!dStrAsciiCasecmp(default_keys[i].name, commandName))
273 return default_keys[i].cmd;
274 }
275 return KEYS_INVALID;
276}
277
282int Keys::getModifier(char *modifierName)
283{
284 uint_t i;
285 for (i = 0; i < sizeof(modifierNames) / sizeof(modifierNames[0]); i++) {
286 if (!dStrAsciiCasecmp(modifierNames[i].name, modifierName)) {
287 return modifierNames[i].value;
288 }
289 }
290
291 return -1;
292}
293
299{
300 int len = dList_length(bindings);
301
302 for (int i = 0; i < len; i++) {
303 KeyBinding_t *node = (KeyBinding_t*)dList_nth_data(bindings, i);
304 if (cmd == node->cmd)
305 return node->modifier + node->key;
306 }
307 return 0;
308}
309
314void Keys::parseKey(char *key, char *commandName)
315{
316 char *p, *modstr, *keystr;
317 KeysCommand_t symcode;
318 int st, keymod = 0, keycode = 0;
319
320 _MSG("Keys::parseKey key='%s' commandName='%s'\n", key, commandName);
321
322 // Get command code
323 if ((symcode = getCmdCode(commandName)) == KEYS_INVALID) {
324 MSG("Keys::parseKey: Invalid command name: '%s'\n", commandName);
325 return;
326 }
327
328 // Skip space
329 for ( ; isspace(*key); ++key) ;
330 // Get modifiers
331 while(*key == '<' && (p = strchr(key, '>'))) {
332 ++key;
333 modstr = dStrndup(key, p - key);
334 if ((st = getModifier(modstr)) == -1) {
335 MSG("Keys::parseKey unknown modifier: %s\n", modstr);
336 } else {
337 keymod |= st;
338 }
339 dFree(modstr);
340 key = p + 1;
341 }
342 // Allow trailing space after keyname
343 keystr = (*key && (p = strchr(key + 1, ' '))) ? dStrndup(key, p - key - 1) :
344 dStrdup(key);
345 // Get key code
346 if (!key[1]) {
347 keycode = *key;
348 } else if (a_Utf8_char_count(keystr, strlen(keystr)) == 1) {
349 const char *beyond = keystr + strlen(keystr);
350 keycode = a_Utf8_decode(keystr, beyond, NULL);
351 } else if (key[0] == '0' && key[1] == 'x') {
352 /* keysym */
353 keycode = strtol(key, NULL, 0x10);
354 } else if ((st = getKeyCode(keystr)) == -1) {
355 MSG("Keys::parseKey unknown keyname: %s\n", keystr);
356 } else {
357 keycode = st;
358 }
359 dFree(keystr);
360
361 // Set binding
362 if (keycode) {
363 delKeyCmd(keycode, keymod);
364 if (symcode != KEYS_NOP) {
365 KeyBinding_t *node = dNew(KeyBinding_t, 1);
366 node->name = dStrdup(commandName);
367 node->cmd = symcode;
368 node->modifier = keymod;
369 node->key = keycode;
371 _MSG("parseKey: Adding key=%d, mod=%d\n", node->key, node->modifier);
372 }
373 }
374}
375
379void Keys::parse(FILE *fp)
380{
381 char *line, *keycomb, *command;
382 int st, lineno = 1;
383
384 // scan the file line by line
385 while ((line = dGetline(fp)) != NULL) {
386 st = dParser_parse_rc_line(&line, &keycomb, &command);
387
388 if (st == 0) {
389 _MSG("Keys::parse: keycomb=%s, command=%s\n", keycomb, command);
390 parseKey(keycomb, command);
391 } else if (st < 0) {
392 MSG("Keys::parse: Syntax error in keysrc line %d: "
393 "keycomb=\"%s\" command=\"%s\"\n", lineno, keycomb, command);
394 }
395
396 dFree(line);
397 ++lineno;
398 }
399 fclose(fp);
400}
#define _MSG(...)
Definition bookmarks.c:45
#define MSG(...)
Definition bookmarks.c:46
static void parse(FILE *fp)
Parse the keysrc.
Definition keys.cc:379
static void init()
Initialize the bindings list.
Definition keys.cc:153
static KeysCommand_t getKeyCmd(void)
Look if the just pressed key is bound to a command.
Definition keys.cc:200
static KeysCommand_t getCmdCode(const char *symbolName)
Takes a command name and searches it in the mapping table.
Definition keys.cc:267
static int getShortcut(KeysCommand_t cmd)
Given a keys command, return a shortcut for it, or 0 if there is none (e.g., for KEYS_NEW_WINDOW,...
Definition keys.cc:298
static void free()
Free data.
Definition keys.cc:174
static void delKeyCmd(int key, int mod)
Remove a key binding from the table.
Definition keys.cc:232
static void parseKey(char *key, char *symbol)
Parse a key-combination/command-name pair, and insert it into the bindings list.
Definition keys.cc:314
static int nodeByKeyCmp(const void *node, const void *key)
Compare function by {key,modifier} pairs.
Definition keys.cc:189
static int getModifier(char *modifierName)
Takes a modifier name and looks it up in the mapping table.
Definition keys.cc:282
static int getKeyCode(char *keyName)
Takes a key name and looks it up in the mapping table.
Definition keys.cc:251
unsigned int uint_t
Definition d_size.h:20
char * dGetline(FILE *stream)
Get a line from a FILE stream.
Definition dlib.c:928
void dList_insert_sorted(Dlist *lp, void *data, dCompareFunc func)
Insert an element into a sorted list.
Definition dlib.c:769
void dFree(void *mem)
Definition dlib.c:68
int dStrAsciiCasecmp(const char *s1, const char *s2)
Definition dlib.c:203
char * dStrdup(const char *s)
Definition dlib.c:77
Dlist * dList_new(int size)
Create a new empty list.
Definition dlib.c:548
int dList_length(Dlist *lp)
For completing the ADT.
Definition dlib.c:613
void * dList_nth_data(Dlist *lp, int n0)
Return the nth data item, NULL when not found or 'n0' is out of range.
Definition dlib.c:662
void dList_remove_fast(Dlist *lp, const void *data)
Remove a data item without preserving order.
Definition dlib.c:623
char * dStrndup(const char *s, size_t sz)
Definition dlib.c:88
void * dList_find_sorted(Dlist *lp, const void *data, dCompareFunc func)
Search a sorted list.
Definition dlib.c:796
void dList_free(Dlist *lp)
Free a list (not its elements)
Definition dlib.c:564
int dParser_parse_rc_line(char **line, char **name, char **value)
Take a dillo rc line and return 'name' and 'value' pointers to it.
Definition dlib.c:834
void dList_remove(Dlist *lp, const void *data)
Definition dlib.c:641
#define dNew(type, count)
Definition dlib.h:49
static const KeyBinding_t default_keys[]
Definition keys.cc:101
static const Mapping_t modifierNames[]
Definition keys.cc:91
static const Mapping_t keyNames[]
Definition keys.cc:42
static Dlist * bindings
Definition keys.cc:146
KeysCommand_t
Definition keys.hh:17
@ KEYS_INVALID
Definition keys.hh:18
@ KEYS_SCREEN_RIGHT
Definition keys.hh:45
@ KEYS_BOTTOM
Definition keys.hh:51
@ KEYS_STOP
Definition keys.hh:32
@ KEYS_LEFT
Definition keys.hh:48
@ KEYS_RELOAD
Definition keys.hh:31
@ KEYS_RIGHT
Definition keys.hh:49
@ KEYS_NOP
Definition keys.hh:19
@ KEYS_LEFT_TAB
Definition keys.hh:23
@ KEYS_RIGHT_TAB
Definition keys.hh:24
@ KEYS_BOOKMARKS
Definition keys.hh:30
@ KEYS_CLOSE_ALL
Definition keys.hh:36
@ KEYS_BACK
Definition keys.hh:37
@ KEYS_ZOOM_OUT
Definition keys.hh:53
@ KEYS_LINE_UP
Definition keys.hh:46
@ KEYS_WEBSEARCH
Definition keys.hh:29
@ KEYS_ZOOM_RESET
Definition keys.hh:54
@ KEYS_SCREEN_DOWN
Definition keys.hh:43
@ KEYS_GOTO
Definition keys.hh:39
@ KEYS_OPEN
Definition keys.hh:20
@ KEYS_SAVE
Definition keys.hh:33
@ KEYS_FIND
Definition keys.hh:28
@ KEYS_HIDE_PANELS
Definition keys.hh:34
@ KEYS_FILE_MENU
Definition keys.hh:35
@ KEYS_FORWARD
Definition keys.hh:38
@ KEYS_TOP
Definition keys.hh:50
@ KEYS_VIEW_SOURCE
Definition keys.hh:41
@ KEYS_HOME
Definition keys.hh:40
@ KEYS_LINE_DOWN
Definition keys.hh:47
@ KEYS_NEW_WINDOW
Definition keys.hh:21
@ KEYS_NEW_TAB
Definition keys.hh:22
@ KEYS_SCREEN_LEFT
Definition keys.hh:44
@ KEYS_SCREEN_UP
Definition keys.hh:42
@ KEYS_CLOSE_TAB
Definition keys.hh:25
@ KEYS_ZOOM_IN
Definition keys.hh:52
Definition dlib.h:131
uint_t a_Utf8_decode(const char *str, const char *end, int *len)
Decode a single UTF-8-encoded character starting at p.
Definition utf8.cc:46
int a_Utf8_char_count(const char *str, int len)
Definition utf8.cc:104