1 /* SPDX-License-Identifier: Zlib */
2
3 #include "shortcuts.h"
4
5 #include "datastructures.h"
6 #include "input-history.h"
7 #include "internal.h"
8 #include "log.h"
9 #include "session.h"
10 #include "settings.h"
11
12 #include <gtk/gtk.h>
13 #include <string.h>
14
15 static void girara_toggle_widget_visibility(GtkWidget* widget);
16
17 bool
girara_shortcut_add(girara_session_t * session,guint modifier,guint key,const char * buffer,girara_shortcut_function_t function,girara_mode_t mode,int argument_n,void * argument_data)18 girara_shortcut_add(girara_session_t* session, guint modifier, guint key, const char* buffer, girara_shortcut_function_t function, girara_mode_t mode, int argument_n, void* argument_data)
19 {
20 g_return_val_if_fail(session != NULL, false);
21 g_return_val_if_fail(buffer || key || modifier, false);
22 g_return_val_if_fail(function != NULL, false);
23
24 girara_argument_t argument = {argument_n, (argument_data != NULL) ?
25 g_strdup(argument_data) : NULL};
26
27 /* search for existing binding */
28 bool found_existing_shortcut = false;
29 GIRARA_LIST_FOREACH_BODY_WITH_ITER(session->bindings.shortcuts, girara_shortcut_t*, iter, shortcuts_it,
30 if (((shortcuts_it->mask == modifier && shortcuts_it->key == key && (modifier != 0 || key != 0)) ||
31 (buffer && shortcuts_it->buffered_command && !g_strcmp0(shortcuts_it->buffered_command, buffer)))
32 && ((shortcuts_it->mode == mode) || (mode == 0)))
33 {
34 if (shortcuts_it->argument.data != NULL) {
35 g_free(shortcuts_it->argument.data);
36 }
37
38 shortcuts_it->function = function;
39 shortcuts_it->argument = argument;
40 found_existing_shortcut = true;
41
42 if (mode != 0) {
43 girara_list_iterator_free(iter);
44 return true;
45 }
46 }
47 );
48
49 if (found_existing_shortcut == true) {
50 return true;
51 }
52
53 /* add new shortcut */
54 girara_shortcut_t* shortcut = g_slice_new(girara_shortcut_t);
55
56 shortcut->mask = modifier;
57 shortcut->key = key;
58 shortcut->buffered_command = g_strdup(buffer);
59 shortcut->function = function;
60 shortcut->mode = mode;
61 shortcut->argument = argument;
62 girara_list_append(session->bindings.shortcuts, shortcut);
63
64 return true;
65 }
66
67 bool
girara_shortcut_remove(girara_session_t * session,guint modifier,guint key,const char * buffer,girara_mode_t mode)68 girara_shortcut_remove(girara_session_t* session, guint modifier, guint key, const char* buffer, girara_mode_t mode)
69 {
70 g_return_val_if_fail(session != NULL, false);
71 g_return_val_if_fail(buffer || key || modifier, false);
72
73 bool handled = false;
74 /* search for existing binding */
75 GIRARA_LIST_FOREACH_BODY(session->bindings.shortcuts, girara_shortcut_t*, shortcuts_it,
76 if (((shortcuts_it->mask == modifier && shortcuts_it->key == key && (modifier != 0 || key != 0)) ||
77 (buffer && shortcuts_it->buffered_command && !g_strcmp0(shortcuts_it->buffered_command, buffer)))
78 && shortcuts_it->mode == mode) {
79 girara_list_remove(session->bindings.shortcuts, shortcuts_it);
80 handled = true;
81 break;
82 }
83 );
84
85 return handled;
86 }
87
88 void
girara_shortcut_free(girara_shortcut_t * shortcut)89 girara_shortcut_free(girara_shortcut_t* shortcut)
90 {
91 g_return_if_fail(shortcut != NULL);
92 g_free(shortcut->buffered_command);
93 g_free(shortcut->argument.data);
94 g_slice_free(girara_shortcut_t, shortcut);
95 }
96
97 bool
girara_inputbar_shortcut_add(girara_session_t * session,guint modifier,guint key,girara_shortcut_function_t function,int argument_n,void * argument_data)98 girara_inputbar_shortcut_add(girara_session_t* session, guint modifier, guint key, girara_shortcut_function_t function, int argument_n, void* argument_data)
99 {
100 g_return_val_if_fail(session != NULL, false);
101 g_return_val_if_fail(function != NULL, false);
102
103 girara_argument_t argument = {argument_n, argument_data};
104 bool found = false;
105
106 /* search for existing special command */
107 GIRARA_LIST_FOREACH_BODY(session->bindings.inputbar_shortcuts, girara_inputbar_shortcut_t*, inp_sh_it,
108 if (inp_sh_it->mask == modifier && inp_sh_it->key == key) {
109 inp_sh_it->function = function;
110 inp_sh_it->argument = argument;
111
112 found = true;
113 break;
114 }
115 );
116
117 if (found == false) {
118 /* create new inputbar shortcut */
119 girara_inputbar_shortcut_t* inputbar_shortcut = g_slice_new(girara_inputbar_shortcut_t);
120
121 inputbar_shortcut->mask = modifier;
122 inputbar_shortcut->key = key;
123 inputbar_shortcut->function = function;
124 inputbar_shortcut->argument = argument;
125
126 girara_list_append(session->bindings.inputbar_shortcuts, inputbar_shortcut);
127 }
128
129 return true;
130 }
131
132 bool
girara_inputbar_shortcut_remove(girara_session_t * session,guint modifier,guint key)133 girara_inputbar_shortcut_remove(girara_session_t* session, guint modifier, guint key)
134 {
135 g_return_val_if_fail(session != NULL, false);
136
137 /* search for existing special command */
138 GIRARA_LIST_FOREACH_BODY(session->bindings.inputbar_shortcuts, girara_inputbar_shortcut_t*, inp_sh_it,
139 if (inp_sh_it->mask == modifier && inp_sh_it->key == key) {
140 girara_list_remove(session->bindings.inputbar_shortcuts, inp_sh_it);
141 break;
142 }
143 );
144
145 return true;
146 }
147
148 void
girara_inputbar_shortcut_free(girara_inputbar_shortcut_t * inputbar_shortcut)149 girara_inputbar_shortcut_free(girara_inputbar_shortcut_t* inputbar_shortcut)
150 {
151 g_slice_free(girara_inputbar_shortcut_t, inputbar_shortcut);
152 }
153
154 bool
girara_isc_activate(girara_session_t * session,girara_argument_t * UNUSED (argument),girara_event_t * UNUSED (event),unsigned int UNUSED (t))155 girara_isc_activate(girara_session_t* session, girara_argument_t* UNUSED(argument), girara_event_t* UNUSED(event), unsigned int UNUSED(t))
156 {
157 girara_callback_inputbar_activate(session->gtk.inputbar_entry, session);
158 return true;
159 }
160
161 bool
girara_isc_abort(girara_session_t * session,girara_argument_t * UNUSED (argument),girara_event_t * UNUSED (event),unsigned int UNUSED (t))162 girara_isc_abort(girara_session_t* session, girara_argument_t* UNUSED(argument), girara_event_t* UNUSED(event), unsigned int UNUSED(t))
163 {
164 g_return_val_if_fail(session != NULL, false);
165
166 /* hide completion */
167 girara_argument_t arg = { GIRARA_HIDE, NULL };
168 girara_isc_completion(session, &arg, NULL, 0);
169
170 /* clear inputbar */
171 gtk_editable_delete_text(GTK_EDITABLE(session->gtk.inputbar_entry), 0, -1);
172
173 /* grab view */
174 gtk_widget_grab_focus(GTK_WIDGET(session->gtk.view));
175
176 /* hide inputbar */
177 gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar_dialog));
178 if (session->global.autohide_inputbar == true) {
179 gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar));
180 }
181
182 /* Begin from the last command when navigating through history */
183 girara_input_history_reset(session->command_history);
184
185 /* reset custom functions */
186 session->signals.inputbar_custom_activate = NULL;
187 session->signals.inputbar_custom_key_press_event = NULL;
188 gtk_entry_set_visibility(session->gtk.inputbar_entry, TRUE);
189
190 return true;
191 }
192
193 bool
girara_isc_string_manipulation(girara_session_t * session,girara_argument_t * argument,girara_event_t * UNUSED (event),unsigned int UNUSED (t))194 girara_isc_string_manipulation(girara_session_t* session, girara_argument_t* argument, girara_event_t* UNUSED(event), unsigned int UNUSED(t))
195 {
196 g_return_val_if_fail(session != NULL, false);
197
198 gchar *separator = NULL;
199 girara_setting_get(session, "word-separator", &separator);
200 gchar *input = gtk_editable_get_chars(GTK_EDITABLE(session->gtk.inputbar_entry), 0, -1);
201 int length = strlen(input);
202 int pos = gtk_editable_get_position(GTK_EDITABLE(session->gtk.inputbar_entry));
203 int i;
204
205 switch (argument->n) {
206 case GIRARA_DELETE_LAST_WORD:
207 if (pos == 1 && (input[0] == ':' || input[0] == '/')) {
208 break;
209 }
210 if (pos == 0) {
211 break;
212 }
213
214 i = pos - 1;
215
216 /* remove trailing spaces */
217 for (; i >= 0 && input[i] == ' '; i--) {
218 }
219
220 /* find the beginning of the word */
221 while ((i == (pos - 1)) || ((i > 0) && separator != NULL && !strchr(separator, input[i]))) {
222 i--;
223 }
224
225 gtk_editable_delete_text(GTK_EDITABLE(session->gtk.inputbar_entry), i + 1, pos);
226 gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), i + 1);
227 break;
228 case GIRARA_DELETE_LAST_CHAR:
229 if (length != 1 && pos == 1 && (input[0] == ':' || input[0] == '/')) {
230 break;
231 }
232 if (length == 1 && pos == 1) {
233 girara_isc_abort(session, argument, NULL, 0);
234 }
235 gtk_editable_delete_text(GTK_EDITABLE(session->gtk.inputbar_entry), pos - 1, pos);
236 break;
237 case GIRARA_DELETE_TO_LINE_START:
238 gtk_editable_delete_text(GTK_EDITABLE(session->gtk.inputbar_entry), 1, pos);
239 break;
240 case GIRARA_NEXT_CHAR:
241 gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), pos + 1);
242 break;
243 case GIRARA_PREVIOUS_CHAR:
244 gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), (pos == 1) ? 1 : pos - 1);
245 break;
246 case GIRARA_DELETE_CURR_CHAR:
247 if (length != 1 && pos == 0 && (input[0] == ':' || input[0] == '/')){
248 break;
249 }
250 if(length == 1 && pos == 0) {
251 girara_isc_abort(session, argument, NULL, 0);
252 }
253 gtk_editable_delete_text(GTK_EDITABLE(session->gtk.inputbar_entry), pos, pos + 1);
254 break;
255 case GIRARA_DELETE_TO_LINE_END:
256 gtk_editable_delete_text(GTK_EDITABLE(session->gtk.inputbar_entry), pos, length);
257 break;
258 case GIRARA_GOTO_START:
259 gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), 1);
260 break;
261 case GIRARA_GOTO_END:
262 gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), -1);
263 break;
264 }
265
266 g_free(separator);
267 g_free(input);
268
269 return false;
270 }
271
272 bool
girara_isc_command_history(girara_session_t * session,girara_argument_t * argument,girara_event_t * UNUSED (event),unsigned int UNUSED (t))273 girara_isc_command_history(girara_session_t* session, girara_argument_t*
274 argument, girara_event_t* UNUSED(event), unsigned int UNUSED(t))
275 {
276 g_return_val_if_fail(session != NULL, false);
277
278 char* temp = gtk_editable_get_chars(GTK_EDITABLE(session->gtk.inputbar_entry), 0, -1);
279 const char* command = argument->n == GIRARA_NEXT ?
280 girara_input_history_next(session->command_history, temp) :
281 girara_input_history_previous(session->command_history, temp);
282 g_free(temp);
283
284 if (command != NULL) {
285 gtk_entry_set_text(session->gtk.inputbar_entry, command);
286 gtk_widget_grab_focus(GTK_WIDGET(session->gtk.inputbar_entry));
287 gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), -1);
288 }
289
290 return true;
291 }
292
293 /* default shortcut implementation */
294 bool
girara_sc_focus_inputbar(girara_session_t * session,girara_argument_t * argument,girara_event_t * UNUSED (event),unsigned int UNUSED (t))295 girara_sc_focus_inputbar(girara_session_t* session, girara_argument_t* argument, girara_event_t* UNUSED(event), unsigned int UNUSED(t))
296 {
297 g_return_val_if_fail(session != NULL, false);
298 g_return_val_if_fail(session->gtk.inputbar_entry != NULL, false);
299
300 if (gtk_widget_get_visible(GTK_WIDGET(session->gtk.inputbar)) == false) {
301 gtk_widget_show(GTK_WIDGET(session->gtk.inputbar));
302 }
303
304 if (gtk_widget_get_visible(GTK_WIDGET(session->gtk.notification_area)) == true) {
305 gtk_widget_hide(GTK_WIDGET(session->gtk.notification_area));
306 }
307
308 gtk_widget_grab_focus(GTK_WIDGET(session->gtk.inputbar_entry));
309
310 if (argument != NULL && argument->data != NULL) {
311 gtk_entry_set_text(session->gtk.inputbar_entry, (char*) argument->data);
312
313 /* we save the X clipboard that will be clear by "grab_focus" */
314 gchar* x_clipboard_text = gtk_clipboard_wait_for_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY));
315
316 gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), -1);
317
318 if (x_clipboard_text != NULL) {
319 /* we reset the X clipboard with saved text */
320 gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY), x_clipboard_text, -1);
321 g_free(x_clipboard_text);
322 }
323 }
324
325 return true;
326 }
327
328 bool
girara_sc_abort(girara_session_t * session,girara_argument_t * UNUSED (argument),girara_event_t * UNUSED (event),unsigned int UNUSED (t))329 girara_sc_abort(girara_session_t* session, girara_argument_t* UNUSED(argument), girara_event_t* UNUSED(event), unsigned int UNUSED(t))
330 {
331 g_return_val_if_fail(session != NULL, false);
332
333 girara_isc_abort(session, NULL, NULL, 0);
334
335 gtk_widget_hide(GTK_WIDGET(session->gtk.notification_area));
336
337 if (session->global.autohide_inputbar == false) {
338 gtk_widget_show(GTK_WIDGET(session->gtk.inputbar));
339 }
340
341 return false;
342 }
343
344 bool
girara_sc_quit(girara_session_t * session,girara_argument_t * UNUSED (argument),girara_event_t * UNUSED (event),unsigned int UNUSED (t))345 girara_sc_quit(girara_session_t* session, girara_argument_t* UNUSED(argument), girara_event_t* UNUSED(event), unsigned int UNUSED(t))
346 {
347 g_return_val_if_fail(session != NULL, false);
348
349 girara_argument_t arg = { GIRARA_HIDE, NULL };
350 girara_isc_completion(session, &arg, NULL, 0);
351
352 gtk_main_quit();
353
354 return false;
355 }
356
357 static void
girara_toggle_widget_visibility(GtkWidget * widget)358 girara_toggle_widget_visibility(GtkWidget* widget)
359 {
360 if (widget == NULL) {
361 return;
362 }
363
364 if (gtk_widget_get_visible(widget) == TRUE) {
365 gtk_widget_hide(widget);
366 } else {
367 gtk_widget_show(widget);
368 }
369 }
370
371 bool
girara_sc_toggle_inputbar(girara_session_t * session,girara_argument_t * UNUSED (argument),girara_event_t * UNUSED (event),unsigned int UNUSED (t))372 girara_sc_toggle_inputbar(girara_session_t* session, girara_argument_t* UNUSED(argument), girara_event_t* UNUSED(event), unsigned int UNUSED(t))
373 {
374 g_return_val_if_fail(session != NULL, false);
375
376 girara_toggle_widget_visibility(GTK_WIDGET(session->gtk.inputbar));
377
378 return true;
379 }
380
381 bool
girara_sc_toggle_statusbar(girara_session_t * session,girara_argument_t * UNUSED (argument),girara_event_t * UNUSED (event),unsigned int UNUSED (t))382 girara_sc_toggle_statusbar(girara_session_t* session, girara_argument_t* UNUSED(argument), girara_event_t* UNUSED(event), unsigned int UNUSED(t))
383 {
384 g_return_val_if_fail(session != NULL, false);
385
386 girara_toggle_widget_visibility(GTK_WIDGET(session->gtk.statusbar));
387
388 return true;
389 }
390
391 static girara_list_t*
argument_to_argument_list(girara_argument_t * argument)392 argument_to_argument_list(girara_argument_t* argument) {
393 girara_list_t* argument_list = girara_list_new();
394 if (argument_list == NULL) {
395 return NULL;
396 }
397
398 gchar** argv = NULL;
399 gint argc = 0;
400
401 girara_list_set_free_function(argument_list, g_free);
402 if (g_shell_parse_argv((const gchar*) argument->data, &argc, &argv, NULL) != FALSE) {
403 for (int i = 0; i < argc; i++) {
404 char* arg = g_strdup(argv[i]);
405 girara_list_append(argument_list, arg);
406 }
407
408 return argument_list;
409 }
410
411 girara_list_free(argument_list);
412 return NULL;
413 }
414
415 bool
girara_sc_set(girara_session_t * session,girara_argument_t * argument,girara_event_t * UNUSED (event),unsigned int UNUSED (t))416 girara_sc_set(girara_session_t* session, girara_argument_t* argument, girara_event_t* UNUSED(event), unsigned int UNUSED(t))
417 {
418 g_return_val_if_fail(session != NULL, false);
419
420 if (argument == NULL || argument->data == NULL) {
421 return false;
422 }
423
424 /* create argument list */
425 girara_list_t* argument_list = argument_to_argument_list(argument);
426 if (argument_list == NULL) {
427 return false;
428 }
429
430 /* call set */
431 girara_cmd_set(session, argument_list);
432
433 /* cleanup */
434 girara_list_free(argument_list);
435
436 return false;
437 }
438
439 bool
girara_sc_exec(girara_session_t * session,girara_argument_t * argument,girara_event_t * UNUSED (event),unsigned int UNUSED (t))440 girara_sc_exec(girara_session_t* session, girara_argument_t* argument, girara_event_t* UNUSED(event), unsigned int UNUSED(t))
441 {
442 g_return_val_if_fail(session != NULL, false);
443
444 if (argument == NULL || argument->data == NULL) {
445 return false;
446 }
447
448 /* create argument list */
449 girara_list_t* argument_list = argument_to_argument_list(argument);
450 if (argument_list == NULL) {
451 return false;
452 }
453
454 /* call exec */
455 girara_cmd_exec(session, argument_list);
456
457 /* cleanup */
458 girara_list_free(argument_list);
459
460 return false;
461 }
462
463 static bool
simulate_key_press(girara_session_t * session,int state,int key)464 simulate_key_press(girara_session_t* session, int state, int key)
465 {
466 if (session == NULL || session->gtk.box == NULL) {
467 return false;
468 }
469
470 GdkEvent* event = gdk_event_new(GDK_KEY_PRESS);
471
472 event->any.type = GDK_KEY_PRESS;
473 event->key.window = g_object_ref(gtk_widget_get_parent_window(GTK_WIDGET(session->gtk.box)));
474 event->key.send_event = false;
475 event->key.time = GDK_CURRENT_TIME;
476 event->key.state = state;
477 event->key.keyval = key;
478
479 GdkDisplay* display = gtk_widget_get_display(GTK_WIDGET(session->gtk.box));
480 GdkKeymapKey* keys = NULL;
481 gint number_of_keys = 0;
482
483 if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_for_display(display),
484 event->key.keyval, &keys, &number_of_keys) == FALSE) {
485 gdk_event_free(event);
486 return false;
487 }
488
489 event->key.hardware_keycode = keys[0].keycode;
490 event->key.group = keys[0].group;
491
492 g_free(keys);
493
494 gdk_event_put(event);
495 gdk_event_free(event);
496
497 gtk_main_iteration_do(FALSE);
498
499 return true;
500 }
501
502 static int
update_state_by_keyval(int state,int keyval)503 update_state_by_keyval(int state, int keyval)
504 {
505 /* The following is probably not true for some keyboard layouts. */
506 if ((keyval >= '!' && keyval <= '/')
507 || (keyval >= ':' && keyval <= '@')
508 || (keyval >= '[' && keyval <= '`')
509 || (keyval >= '{' && keyval <= '~')
510 ) {
511 state |= GDK_SHIFT_MASK;
512 }
513
514 return state;
515 }
516
517 bool
girara_sc_feedkeys(girara_session_t * session,girara_argument_t * argument,girara_event_t * UNUSED (event),unsigned int t)518 girara_sc_feedkeys(girara_session_t* session, girara_argument_t* argument,
519 girara_event_t* UNUSED(event), unsigned int t)
520 {
521 if (session == NULL || argument == NULL) {
522 return false;
523 }
524
525 if (g_mutex_trylock(&session->private_data->feedkeys_mutex) == FALSE) {
526 girara_error("Recursive use of feedkeys detected. Aborting evaluation.");
527 return false;
528 }
529
530 typedef struct gdk_keyboard_button_s {
531 char* identifier;
532 int keyval;
533 } gdk_keyboard_button_t;
534
535 static const gdk_keyboard_button_t gdk_keyboard_buttons[] = {
536 {"BackSpace", GDK_KEY_BackSpace},
537 {"CapsLock", GDK_KEY_Caps_Lock},
538 {"Down", GDK_KEY_Down},
539 {"Esc", GDK_KEY_Escape},
540 {"F10", GDK_KEY_F10},
541 {"F11", GDK_KEY_F11},
542 {"F12", GDK_KEY_F12},
543 {"F1", GDK_KEY_F1},
544 {"F2", GDK_KEY_F2},
545 {"F3", GDK_KEY_F3},
546 {"F4", GDK_KEY_F4},
547 {"F5", GDK_KEY_F5},
548 {"F6", GDK_KEY_F6},
549 {"F7", GDK_KEY_F7},
550 {"F8", GDK_KEY_F8},
551 {"F9", GDK_KEY_F9},
552 {"Left", GDK_KEY_Left},
553 {"PageDown", GDK_KEY_Page_Down},
554 {"PageUp", GDK_KEY_Page_Up},
555 {"Home", GDK_KEY_Home},
556 {"End", GDK_KEY_End},
557 {"Return", GDK_KEY_Return},
558 {"Right", GDK_KEY_Right},
559 {"Space", GDK_KEY_space},
560 {"Super", GDK_KEY_Super_L},
561 {"Tab", GDK_KEY_Tab},
562 {"ShiftTab", GDK_KEY_ISO_Left_Tab},
563 {"Up", GDK_KEY_Up}
564 };
565
566 char* input = (char*) argument->data;
567 unsigned int input_length = strlen(input);
568
569 t = MAX(1, t);
570 for (unsigned int c = 0; c < t; c++) {
571 for (unsigned i = 0; i < input_length; i++) {
572 int state = 0;
573 int keyval = input[i];
574
575 /* possible special button */
576 if ((input_length - i) >= 3 && input[i] == '<') {
577 char* end = strchr(input + i, '>');
578 if (end == NULL) {
579 goto single_key;
580 }
581
582 const int length = end - (input + i) - 1;
583 char* tmp = g_strndup(input + i + 1, length);
584 bool found = false;
585
586 /* Multi key shortcut */
587 if (length > 2 && tmp[1] == '-') {
588 switch (tmp[0]) {
589 case 'S':
590 state = GDK_SHIFT_MASK;
591 break;
592 case 'A':
593 state = GDK_MOD1_MASK;
594 break;
595 case 'C':
596 state = GDK_CONTROL_MASK;
597 break;
598 default:
599 break;
600 }
601
602 if (length == 3) {
603 keyval = tmp[2];
604 found = true;
605 } else {
606 for (unsigned int j = 0; j < LENGTH(gdk_keyboard_buttons); ++j) {
607 if (g_strcmp0(tmp + 2, gdk_keyboard_buttons[j].identifier) == 0) {
608 keyval = gdk_keyboard_buttons[j].keyval;
609 found = true;
610 break;
611 }
612 }
613 }
614 /* Possible special key */
615 } else {
616 for (unsigned int j = 0; j < LENGTH(gdk_keyboard_buttons); ++j) {
617 if (g_strcmp0(tmp, gdk_keyboard_buttons[j].identifier) == 0) {
618 keyval = gdk_keyboard_buttons[j].keyval;
619 found = true;
620 break;
621 }
622 }
623 }
624
625 g_free(tmp);
626
627 /* parsed special key */
628 if (found == true) {
629 i += length + 1;
630 }
631 }
632
633 single_key:
634 state = update_state_by_keyval(state, keyval);
635 simulate_key_press(session, state, keyval);
636 }
637 }
638
639 g_mutex_unlock(&session->private_data->feedkeys_mutex);
640 return true;
641 }
642
643 bool
girara_shortcut_mapping_add(girara_session_t * session,const char * identifier,girara_shortcut_function_t function)644 girara_shortcut_mapping_add(girara_session_t* session, const char* identifier, girara_shortcut_function_t function)
645 {
646 g_return_val_if_fail(session != NULL, false);
647
648 if (function == NULL || identifier == NULL) {
649 return false;
650 }
651
652 girara_session_private_t* session_private = session->private_data;
653 bool found = false;
654
655 GIRARA_LIST_FOREACH_BODY(session_private->config.shortcut_mappings, girara_shortcut_mapping_t*, data,
656 if (g_strcmp0(data->identifier, identifier) == 0) {
657 data->function = function;
658 found = true;
659 break;
660 }
661 );
662
663 if (found == false) {
664 /* add new config handle */
665 girara_shortcut_mapping_t* mapping = g_slice_new(girara_shortcut_mapping_t);
666
667 mapping->identifier = g_strdup(identifier);
668 mapping->function = function;
669 girara_list_append(session_private->config.shortcut_mappings, mapping);
670 }
671
672 return true;
673 }
674
675 void
girara_shortcut_mapping_free(girara_shortcut_mapping_t * mapping)676 girara_shortcut_mapping_free(girara_shortcut_mapping_t* mapping)
677 {
678 if (mapping == NULL) {
679 return;
680 }
681
682 g_free(mapping->identifier);
683 g_slice_free(girara_shortcut_mapping_t, mapping);
684 }
685
686 bool
girara_argument_mapping_add(girara_session_t * session,const char * identifier,int value)687 girara_argument_mapping_add(girara_session_t* session, const char* identifier, int value)
688 {
689 g_return_val_if_fail(session != NULL, false);
690
691 if (identifier == NULL) {
692 return false;
693 }
694
695 girara_session_private_t* session_private = session->private_data;
696 bool found = false;
697
698 GIRARA_LIST_FOREACH_BODY(session_private->config.argument_mappings, girara_argument_mapping_t*, mapping,
699 if (g_strcmp0(mapping->identifier, identifier) == 0) {
700 mapping->value = value;
701 found = true;
702 break;
703 }
704 );
705
706 if (found == false) {
707 /* add new config handle */
708 girara_argument_mapping_t* mapping = g_slice_new(girara_argument_mapping_t);
709
710 mapping->identifier = g_strdup(identifier);
711 mapping->value = value;
712 girara_list_append(session_private->config.argument_mappings, mapping);
713 }
714
715 return true;
716 }
717
718 void
girara_argument_mapping_free(girara_argument_mapping_t * argument_mapping)719 girara_argument_mapping_free(girara_argument_mapping_t* argument_mapping)
720 {
721 if (argument_mapping == NULL) {
722 return;
723 }
724
725 g_free(argument_mapping->identifier);
726 g_slice_free(girara_argument_mapping_t, argument_mapping);
727 }
728
729 bool
girara_mouse_event_add(girara_session_t * session,guint mask,guint button,girara_shortcut_function_t function,girara_mode_t mode,girara_event_type_t event_type,int argument_n,void * argument_data)730 girara_mouse_event_add(girara_session_t* session, guint mask, guint button,
731 girara_shortcut_function_t function, girara_mode_t mode, girara_event_type_t
732 event_type, int argument_n, void* argument_data)
733 {
734 g_return_val_if_fail(session != NULL, false);
735 g_return_val_if_fail(function != NULL, false);
736
737 girara_argument_t argument = {argument_n, argument_data};
738 bool found = false;
739
740 /* search for existing binding */
741 GIRARA_LIST_FOREACH_BODY(session->bindings.mouse_events, girara_mouse_event_t*, me_it,
742 if (me_it->mask == mask && me_it->button == button &&
743 me_it->mode == mode && me_it->event_type == event_type) {
744 me_it->function = function;
745 me_it->argument = argument;
746
747 found = true;
748 break;
749 }
750 );
751
752 if (found == false) {
753 /* add new mouse event */
754 girara_mouse_event_t* mouse_event = g_slice_new(girara_mouse_event_t);
755
756 mouse_event->mask = mask;
757 mouse_event->button = button;
758 mouse_event->function = function;
759 mouse_event->mode = mode;
760 mouse_event->event_type = event_type;
761 mouse_event->argument = argument;
762 girara_list_append(session->bindings.mouse_events, mouse_event);
763 }
764
765 return true;
766 }
767
768 bool
girara_mouse_event_remove(girara_session_t * session,guint mask,guint button,girara_mode_t mode)769 girara_mouse_event_remove(girara_session_t* session, guint mask, guint button, girara_mode_t mode)
770 {
771 g_return_val_if_fail(session != NULL, false);
772
773 bool found = false;
774 /* search for existing binding */
775 GIRARA_LIST_FOREACH_BODY(session->bindings.mouse_events, girara_mouse_event_t*, me_it,
776 if (me_it->mask == mask && me_it->button == button &&
777 me_it->mode == mode) {
778 girara_list_remove(session->bindings.mouse_events, me_it);
779 found = true;
780 break;
781 }
782 );
783
784 return found;
785 }
786
787 void
girara_mouse_event_free(girara_mouse_event_t * mouse_event)788 girara_mouse_event_free(girara_mouse_event_t* mouse_event)
789 {
790 if (mouse_event == NULL) {
791 return;
792 }
793 g_slice_free(girara_mouse_event_t, mouse_event);
794 }
795