Dillo v3.2.0-151-g90488cbf
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-2025 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
18#include "dlib/dlib.h" /* dIscntrl */
19#include "keys.hh"
20#include "utf8.hh"
21#include "msg.h"
22#include "cache.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 { "copy" , KEYS_COPY , FL_CTRL , 'c' },
141 { "zoom-in" , KEYS_ZOOM_IN , FL_CTRL , '+' },
142 { "zoom-in" , KEYS_ZOOM_IN , FL_CTRL , '=' /* US + */ },
143 { "zoom-out" , KEYS_ZOOM_OUT , FL_CTRL , '-' },
144 { "zoom-reset" , KEYS_ZOOM_RESET , FL_CTRL , '0' },
145 { "focus-tab1" , KEYS_FOCUS_TAB1 , FL_ALT , '1' },
146 { "focus-tab2" , KEYS_FOCUS_TAB2 , FL_ALT , '2' },
147 { "focus-tab3" , KEYS_FOCUS_TAB3 , FL_ALT , '3' },
148 { "focus-tab4" , KEYS_FOCUS_TAB4 , FL_ALT , '4' },
149 { "focus-tab5" , KEYS_FOCUS_TAB5 , FL_ALT , '5' },
150 { "focus-tab6" , KEYS_FOCUS_TAB6 , FL_ALT , '6' },
151 { "focus-tab7" , KEYS_FOCUS_TAB7 , FL_ALT , '7' },
152 { "focus-tab8" , KEYS_FOCUS_TAB8 , FL_ALT , '8' },
153 { "focus-tab9" , KEYS_FOCUS_TAB9 , FL_ALT , '9' },
154 { "focus-tab10" , KEYS_FOCUS_TAB10 , FL_ALT , '0' },
155};
156
158
159
160
165{
166 KeyBinding_t *node;
167
168 // Fill our key bindings list
169 bindings = dList_new(32);
170 for (uint_t i = 0; i < sizeof(default_keys) / sizeof(default_keys[0]); i++) {
171 if (default_keys[i].key) {
172 node = dNew(KeyBinding_t, 1);
173 node->name = dStrdup(default_keys[i].name);
174 node->cmd = default_keys[i].cmd;
175 node->modifier = default_keys[i].modifier;
176 node->key = default_keys[i].key;
178 }
179 }
180}
181
186{
187 KeyBinding_t *node;
188
189 while ((node = (KeyBinding_t*)dList_nth_data(bindings, 0))) {
190 dFree((char*)node->name);
192 dFree(node);
193 }
195}
196
200int Keys::nodeByKeyCmp(const void *node, const void *key)
201{
202 KeyBinding_t *n = (KeyBinding_t*)node, *k = (KeyBinding_t*)key;
203 _MSG("Keys::nodeByKeyCmp modifier=%d\n", k->modifier);
204 return (n->key != k->key) ? (n->key - k->key) : (n->modifier - k->modifier);
205}
206
212{
214 KeyBinding_t keyNode;
215
216 keyNode.modifier = Fl::event_state() & (FL_SHIFT | FL_CTRL |FL_ALT|FL_META);
217 if (dIscntrl(Fl::event_text()[0])) {
218 keyNode.key = Fl::event_key();
219 } else {
220 const char *beyond = Fl::event_text() + Fl::event_length();
221 keyNode.key = a_Utf8_decode(Fl::event_text(), beyond, NULL);
222
223 /* BUG: The idea is to drop the modifiers if their use results in a
224 * different character (e.g., if shift-8 gives '*', drop the shift,
225 * but if ctrl-6 gives '6', keep the ctrl), but we have to compare a
226 * keysym with a Unicode codepoint, which only works for characters
227 * below U+0100 (those known to latin-1).
228 */
229 if (keyNode.key != Fl::event_key())
230 keyNode.modifier = 0;
231 }
232 _MSG("getKeyCmd: evkey=0x%x evtext=\'%s\' key=0x%x, mod=0x%x\n",
233 Fl::event_key(), Fl::event_text(), keyNode.key, keyNode.modifier);
234 void *data = dList_find_sorted(bindings, &keyNode, nodeByKeyCmp);
235 if (data)
236 ret = ((KeyBinding_t*)data)->cmd;
237 return ret;
238}
239
243void Keys::delKeyCmd(int key, int mod)
244{
245 KeyBinding_t keyNode, *node;
246 keyNode.key = key;
247 keyNode.modifier = mod;
248
249 node = (KeyBinding_t*) dList_find_sorted(bindings, &keyNode, nodeByKeyCmp);
250 if (node) {
251 dList_remove(bindings, node);
252 dFree((char*)node->name);
253 dFree(node);
254 }
255}
256
262int Keys::getKeyCode(char *keyName)
263{
264 uint_t i;
265 for (i = 0; i < sizeof(keyNames) / sizeof(keyNames[0]); i++) {
266 if (!dStrAsciiCasecmp(keyNames[i].name, keyName)) {
267 return keyNames[i].value;
268 }
269 }
270
271 return -1;
272}
273
274const char *Keys::getKeyName(int key)
275{
276 static char buf[128];
277
278 uint_t i;
279 for (i = 0; i < sizeof(keyNames) / sizeof(keyNames[0]); i++) {
280 if (keyNames[i].value == key)
281 return keyNames[i].name;
282 }
283
284 if (dIsascii(key)) {
285 sprintf(buf, "%c", key);
286 } else {
287 /* Otherwise print hexadecimal */
288 sprintf(buf, "0x%x", key);
289 }
290
291 return buf;
292}
293
298KeysCommand_t Keys::getCmdCode(const char *commandName)
299{
300 uint_t i;
301
302 for (i = 0; i < sizeof(default_keys) / sizeof(KeyBinding_t); i++) {
303 if (!dStrAsciiCasecmp(default_keys[i].name, commandName))
304 return default_keys[i].cmd;
305 }
306 return KEYS_INVALID;
307}
308
313int Keys::getModifier(char *modifierName)
314{
315 uint_t i;
316 for (i = 0; i < sizeof(modifierNames) / sizeof(modifierNames[0]); i++) {
317 if (!dStrAsciiCasecmp(modifierNames[i].name, modifierName)) {
318 return modifierNames[i].value;
319 }
320 }
321
322 return -1;
323}
324
330{
331 int len = dList_length(bindings);
332
333 for (int i = 0; i < len; i++) {
334 KeyBinding_t *node = (KeyBinding_t*)dList_nth_data(bindings, i);
335 if (cmd == node->cmd)
336 return node->modifier + node->key;
337 }
338 return 0;
339}
340
345void Keys::parseKey(char *key, char *commandName)
346{
347 char *p, *modstr, *keystr;
348 KeysCommand_t symcode;
349 int st, keymod = 0, keycode = 0;
350
351 _MSG("Keys::parseKey key='%s' commandName='%s'\n", key, commandName);
352
353 // Get command code
354 if ((symcode = getCmdCode(commandName)) == KEYS_INVALID) {
355 MSG("Keys::parseKey: Invalid command name: '%s'\n", commandName);
356 return;
357 }
358
359 // Skip space
360 for ( ; dIsspace(*key); ++key) ;
361 // Get modifiers
362 while(*key == '<' && (p = strchr(key, '>'))) {
363 ++key;
364 modstr = dStrndup(key, p - key);
365 if ((st = getModifier(modstr)) == -1) {
366 MSG("Keys::parseKey unknown modifier: %s\n", modstr);
367 } else {
368 keymod |= st;
369 }
370 dFree(modstr);
371 key = p + 1;
372 }
373 // Allow trailing space after keyname
374 keystr = (*key && (p = strchr(key + 1, ' '))) ? dStrndup(key, p - key - 1) :
375 dStrdup(key);
376 // Get key code
377 if (!key[1]) {
378 keycode = *key;
379 } else if (a_Utf8_char_count(keystr, strlen(keystr)) == 1) {
380 const char *beyond = keystr + strlen(keystr);
381 keycode = a_Utf8_decode(keystr, beyond, NULL);
382 } else if (key[0] == '0' && key[1] == 'x') {
383 /* keysym */
384 keycode = strtol(key, NULL, 0x10);
385 } else if ((st = getKeyCode(keystr)) == -1) {
386 MSG("Keys::parseKey unknown keyname: %s\n", keystr);
387 } else {
388 keycode = st;
389 }
390 dFree(keystr);
391
392 // Set binding
393 if (keycode) {
394 delKeyCmd(keycode, keymod);
395 if (symcode != KEYS_NOP) {
396 KeyBinding_t *node = dNew(KeyBinding_t, 1);
397 node->name = dStrdup(commandName);
398 node->cmd = symcode;
399 node->modifier = keymod;
400 node->key = keycode;
402 _MSG("parseKey: Adding key=%d, mod=%d\n", node->key, node->modifier);
403 }
404 }
405}
406
410void Keys::parse(FILE *fp)
411{
412 char *line, *keycomb, *command;
413 int st, lineno = 1;
414
415 // scan the file line by line
416 while ((line = dGetline(fp)) != NULL) {
417 st = dParser_parse_rc_line(&line, &keycomb, &command);
418
419 if (st == 0) {
420 _MSG("Keys::parse: keycomb=%s, command=%s\n", keycomb, command);
421 parseKey(keycomb, command);
422 } else if (st < 0) {
423 MSG("Keys::parse: Syntax error in keysrc line %d: "
424 "keycomb=\"%s\" command=\"%s\"\n", lineno, keycomb, command);
425 }
426
427 dFree(line);
428 ++lineno;
429 }
430 fclose(fp);
431}
432
434{
435 int len = dList_length(bindings);
436 Dstr *table = dStr_new("");
437
438 dStr_sprintfa(table,
439 "<!DOCTYPE HTML>\n"
440 "<html>\n"
441 "<head>\n"
442 " <title>Keyboard shortcuts</title>\n"
443 " <style>\n"
444 " body {\n"
445 " background: white;\n"
446 " margin: 3em;\n"
447 " font-size: 16px;\n"
448 " font-family: sans-serif;\n"
449 " line-height: 1.4em;\n"
450 " }\n"
451 " .main { max-width: 40em; }\n"
452 " p { margin-top: 1em; }\n"
453 " table {\n"
454 " border-collapse: collapse;\n"
455 " }\n"
456 " th,td {\n"
457 " padding: 0.25em;\n"
458 " padding: 0.25em;\n"
459 " border-spacing: 0px;\n"
460 " border-top: solid 1px #ccc;\n"
461 " border-bottom: solid 1px #ccc;\n"
462 " }\n"
463 " td.key {\n"
464 " min-width: 15em;\n"
465 " }\n"
466 " td.action {\n"
467 " min-width: 10em;\n"
468 " }\n"
469 " th {\n"
470 " background-color: #eee;\n"
471 " }\n"
472 " kbd {\n"
473 " display: inline-block;\n"
474 " border: solid 1px #aaa;\n"
475 " padding: 1px 5px;\n"
476 " }\n"
477 " kbd.mod {\n"
478 " background: #f5f5f5;\n"
479 " }\n"
480 " </style>\n"
481 "</head>\n"
482 "<body>\n"
483 "<div class=\"main\">\n"
484 "\n"
485 "<h1>Keyboard shortcuts</h1>\n"
486 "\n"
487 "<p>The following table contains the current key bindings in Dillo.\n"
488 "To change them, edit the configuration file <code>~/.dillo/keysrc</code> \n"
489 "and restart the browser.</p>\n"
490 "<table>\n"
491 "<tr><th>Shortcut<th>Action</tr>\n");
492
493 for (int i = 0; i < len; i++) {
494 KeyBinding_t *node = (KeyBinding_t*)dList_nth_data(bindings, i);
495 const char *key = Keys::getKeyName(node->key);
496
497 dStr_sprintfa(table, "<tr><td class=key><code>");
498
499 for (uint_t j = 0; j < sizeof(modifierNames) / sizeof(modifierNames[0]); j++) {
500 if (modifierNames[j].value & node->modifier) {
501 dStr_sprintfa(table, "<kbd class=mod>%s</kbd> ",
502 modifierNames[j].name);
503 }
504 }
505
506 dStr_sprintfa(table, "<kbd class=key>%s</kbd></code></td>", key);
507 dStr_sprintfa(table, "<td class=action><code>%s</code></td>", node->name);
508 dStr_sprintfa(table, "</tr>\n");
509 }
510
511 dStr_sprintfa(table,
512 "</table>\n"
513 "</div>\n"
514 "</body>\n"
515 "</html>\n");
516
517 /* inject keymaps after loading them */
518 DilloUrl *url = a_Url_new("about:keys", NULL);
519 a_Cache_entry_inject(url, table->str, table->len,
521 dStr_free(table, 1);
522 a_Url_free(url);
523}
#define _MSG(...)
Definition bookmarks.c:44
#define MSG(...)
Definition bookmarks.c:45
void a_Cache_entry_inject(const DilloUrl *Url, const char *buf, size_t len, int flags)
Inject full page content directly into the cache.
Definition cache.c:292
#define CA_InternalUrl
Definition cache.h:45
#define CA_GotHeader
Definition cache.h:34
#define CA_GotLength
Definition cache.h:36
static void parse(FILE *fp)
Parse the keysrc.
Definition keys.cc:410
static void init()
Initialize the bindings list.
Definition keys.cc:164
static KeysCommand_t getKeyCmd(void)
Look if the just pressed key is bound to a command.
Definition keys.cc:211
static KeysCommand_t getCmdCode(const char *symbolName)
Takes a command name and searches it in the mapping table.
Definition keys.cc:298
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:329
static void free()
Free data.
Definition keys.cc:185
static void delKeyCmd(int key, int mod)
Remove a key binding from the table.
Definition keys.cc:243
static const char * getKeyName(int key)
Definition keys.cc:274
static void parseKey(char *key, char *symbol)
Parse a key-combination/command-name pair, and insert it into the bindings list.
Definition keys.cc:345
static void genAboutKeys(void)
Definition keys.cc:433
static int nodeByKeyCmp(const void *node, const void *key)
Compare function by {key,modifier} pairs.
Definition keys.cc:200
static int getModifier(char *modifierName)
Takes a modifier name and looks it up in the mapping table.
Definition keys.cc:313
static int getKeyCode(char *keyName)
Takes a key name and looks it up in the mapping table.
Definition keys.cc:262
unsigned int uint_t
Definition d_size.h:20
char * dGetline(FILE *stream)
Get a line from a FILE stream.
Definition dlib.c:955
void dList_insert_sorted(Dlist *lp, void *data, dCompareFunc func)
Insert an element into a sorted list.
Definition dlib.c:796
void dFree(void *mem)
Definition dlib.c:67
int dStrAsciiCasecmp(const char *s1, const char *s2)
Definition dlib.c:202
void dStr_sprintfa(Dstr *ds, const char *format,...)
Printf-like function that appends.
Definition dlib.c:463
char * dStrdup(const char *s)
Definition dlib.c:76
Dlist * dList_new(int size)
Create a new empty list.
Definition dlib.c:575
int dList_length(Dlist *lp)
For completing the ADT.
Definition dlib.c:640
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:689
void dList_remove_fast(Dlist *lp, const void *data)
Remove a data item without preserving order.
Definition dlib.c:650
void dStr_free(Dstr *ds, int all)
Free a dillo string.
Definition dlib.c:336
char * dStrndup(const char *s, size_t sz)
Definition dlib.c:87
Dstr * dStr_new(const char *s)
Create a new string.
Definition dlib.c:324
void * dList_find_sorted(Dlist *lp, const void *data, dCompareFunc func)
Search a sorted list.
Definition dlib.c:823
void dList_free(Dlist *lp)
Free a list (not its elements)
Definition dlib.c:591
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:861
void dList_remove(Dlist *lp, const void *data)
Definition dlib.c:668
static int dIsspace(unsigned char c)
Definition dlib.h:53
static int dIsascii(unsigned char c)
Definition dlib.h:57
static int dIscntrl(unsigned char c)
Definition dlib.h:49
#define dNew(type, count)
Definition dlib.h:78
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:157
KeysCommand_t
Definition keys.hh:17
@ KEYS_INVALID
Definition keys.hh:18
@ KEYS_COPY
Definition keys.hh:52
@ 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_FOCUS_TAB10
Definition keys.hh:65
@ KEYS_RELOAD
Definition keys.hh:31
@ KEYS_RIGHT
Definition keys.hh:49
@ KEYS_NOP
Definition keys.hh:19
@ KEYS_FOCUS_TAB9
Definition keys.hh:64
@ KEYS_LEFT_TAB
Definition keys.hh:23
@ KEYS_FOCUS_TAB7
Definition keys.hh:62
@ 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:54
@ KEYS_FOCUS_TAB8
Definition keys.hh:63
@ KEYS_LINE_UP
Definition keys.hh:46
@ KEYS_FOCUS_TAB5
Definition keys.hh:60
@ KEYS_WEBSEARCH
Definition keys.hh:29
@ KEYS_ZOOM_RESET
Definition keys.hh:55
@ KEYS_SCREEN_DOWN
Definition keys.hh:43
@ KEYS_GOTO
Definition keys.hh:39
@ KEYS_OPEN
Definition keys.hh:20
@ KEYS_FOCUS_TAB4
Definition keys.hh:59
@ KEYS_SAVE
Definition keys.hh:33
@ KEYS_FIND
Definition keys.hh:28
@ KEYS_FOCUS_TAB2
Definition keys.hh:57
@ KEYS_HIDE_PANELS
Definition keys.hh:34
@ KEYS_FILE_MENU
Definition keys.hh:35
@ KEYS_FORWARD
Definition keys.hh:38
@ KEYS_FOCUS_TAB3
Definition keys.hh:58
@ KEYS_TOP
Definition keys.hh:50
@ KEYS_FOCUS_TAB6
Definition keys.hh:61
@ 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_FOCUS_TAB1
Definition keys.hh:56
@ 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:53
Definition url.h:88
Definition dlib.h:161
Definition dlib.h:131
Dstr_char_t * str
Definition dlib.h:134
int len
Definition dlib.h:133
void a_Url_free(DilloUrl *url)
Free a DilloUrl.
Definition url.c:207
DilloUrl * a_Url_new(const char *url_str, const char *base_url)
Transform (and resolve) an URL string into the respective DilloURL.
Definition url.c:370
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