girara
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros
commands.c
Go to the documentation of this file.
1 /* See LICENSE file for license and copyright information */
2 
3 #include <string.h>
4 #include <stdlib.h>
5 #include <glib/gi18n-lib.h>
6 
7 #include "commands.h"
8 #include "datastructures.h"
9 #include "session.h"
10 #include "internal.h"
11 #include "utils.h"
12 #include "settings.h"
13 #include "shortcuts.h"
14 
15 #if GTK_MAJOR_VERSION == 2
16 #include "gtk2-compat.h"
17 #endif
18 
19 /* default commands implementation */
20 bool
21 girara_cmd_map_unmap(girara_session_t* session, girara_list_t* argument_list,
22  bool unmap)
23 {
24  typedef struct gdk_keyboard_button_s
25  {
26  char* identifier;
27  int keyval;
28  } gdk_keyboard_button_t;
29 
30  static const gdk_keyboard_button_t gdk_keyboard_buttons[] = {
31  {"BackSpace", GDK_KEY_BackSpace},
32  {"CapsLock", GDK_KEY_Caps_Lock},
33  {"Down", GDK_KEY_Down},
34  {"Esc", GDK_KEY_Escape},
35  {"F10", GDK_KEY_F10},
36  {"F11", GDK_KEY_F11},
37  {"F12", GDK_KEY_F12},
38  {"F1", GDK_KEY_F1},
39  {"F2", GDK_KEY_F2},
40  {"F3", GDK_KEY_F3},
41  {"F4", GDK_KEY_F4},
42  {"F5", GDK_KEY_F5},
43  {"F6", GDK_KEY_F6},
44  {"F7", GDK_KEY_F7},
45  {"F8", GDK_KEY_F8},
46  {"F9", GDK_KEY_F9},
47  {"Left", GDK_KEY_Left},
48  {"PageDown", GDK_KEY_Page_Down},
49  {"PageUp", GDK_KEY_Page_Up},
50  {"Return", GDK_KEY_Return},
51  {"Right", GDK_KEY_Right},
52  {"Space", GDK_KEY_space},
53  {"Super", GDK_KEY_Super_L},
54  {"Tab", GDK_KEY_Tab},
55  {"ShiftTab", GDK_KEY_ISO_Left_Tab},
56  {"Up", GDK_KEY_Up}
57  };
58 
59  typedef struct gdk_mouse_button_s
60  {
61  char* identifier;
62  int button;
63  } gdk_mouse_button_t;
64 
65  static const gdk_mouse_button_t gdk_mouse_buttons[] = {
66  {"Button1", GIRARA_MOUSE_BUTTON1},
67  {"Button2", GIRARA_MOUSE_BUTTON2},
68  {"Button3", GIRARA_MOUSE_BUTTON3},
69  {"Button4", GIRARA_MOUSE_BUTTON4},
70  {"Button5", GIRARA_MOUSE_BUTTON5},
71  {"Button6", GIRARA_MOUSE_BUTTON6},
72  {"Button7", GIRARA_MOUSE_BUTTON7},
73  {"Button8", GIRARA_MOUSE_BUTTON8},
74  {"Button9", GIRARA_MOUSE_BUTTON9}
75  };
76 
77  typedef struct event_type_s
78  {
79  char* identifier;
80  int event;
81  } event_type_t;
82 
83  static const event_type_t event_types[] = {
84  {"motion", GIRARA_EVENT_MOTION_NOTIFY},
85  {"scroll_up", GIRARA_EVENT_SCROLL_UP},
86  {"scroll_down", GIRARA_EVENT_SCROLL_DOWN},
87  {"scroll_left", GIRARA_EVENT_SCROLL_LEFT},
88  {"scroll_right", GIRARA_EVENT_SCROLL_RIGHT}
89  };
90 
91  typedef struct mouse_event_s
92  {
93  char* identifier;
94  int event;
95  } mouse_event_t;
96 
97  static const mouse_event_t mouse_events[] = {
98  {"button-pressed", GIRARA_EVENT_BUTTON_PRESS},
99  {"2-button-pressed", GIRARA_EVENT_2BUTTON_PRESS},
100  {"3-button-pressed", GIRARA_EVENT_2BUTTON_PRESS},
101  {"button-released", GIRARA_EVENT_BUTTON_RELEASE}
102  };
103 
104  size_t number_of_arguments = girara_list_size(argument_list);
105 
106  if (number_of_arguments < ((unmap == true) ? 1 : 2)) {
107  if (unmap == true) {
108  girara_notify(session, GIRARA_WARNING, _("Usage: unmap <binding>"));
109  } else {
110  girara_notify(session, GIRARA_WARNING, _("Usage: map <binding> <function>"));
111  }
112  return false;
113  }
114 
115  int shortcut_mask = 0;
116  int shortcut_key = 0;
117  int shortcut_mouse_button = 0;
118  girara_mode_t shortcut_mode = session->modes.normal;
119  char* shortcut_argument_data = NULL;
120  int shortcut_argument_n = 0;
121  char* shortcut_buffer_command = NULL;
123  girara_shortcut_function_t shortcut_function = NULL;
124  bool mouse_event = false;
125 
126  size_t current_command = 0;
127  char* tmp = girara_list_nth(argument_list, current_command);
128  size_t tmp_length = strlen(tmp);
129 
130  /* Check first argument for mode */
131  bool is_mode = false;
132  if (tmp_length >= 3 && tmp[0] == '[' && tmp[tmp_length - 1] == ']') {
133  char* tmp_inner = g_strndup(tmp + 1, tmp_length - 2);
134 
135  GIRARA_LIST_FOREACH(session->modes.identifiers, girara_mode_string_t*, iter, mode)
136  if (!g_strcmp0(tmp_inner, mode->name)) {
137  shortcut_mode = mode->index;
138  is_mode = true;
139  break;
140  }
141  GIRARA_LIST_FOREACH_END(session->modes.identifiers, girara_mode_string_t*, iter, mode);
142 
143  if (is_mode == false) {
144  girara_warning("Unregistered mode specified: %s", tmp_inner);
145  girara_notify(session, GIRARA_ERROR, _("Unregistered mode specified: %s"), tmp_inner);
146  g_free(tmp_inner);
147  return false;
148  }
149  g_free(tmp_inner);
150  }
151 
152  unsigned int limit = (unmap == true) ? 1 : 2;
153  if (number_of_arguments < limit) {
154  girara_warning("Invalid number of arguments passed: %zu instead of at least %u", number_of_arguments, limit);
155  girara_notify(session, GIRARA_ERROR,
156  _("Invalid number of arguments passed: %zu instead of at least %u"), number_of_arguments, limit);
157  return false;
158  }
159 
160  if (is_mode == true) {
161  tmp = girara_list_nth(argument_list, ++current_command);
162  tmp_length = strlen(tmp);
163  }
164 
165  /* Check for multi key shortcut */
166  if (tmp_length >= 3 && tmp[0] == '<' && tmp[tmp_length - 1] == '>') {
167  tmp = g_strndup(tmp + 1, tmp_length - 2);
168  tmp_length = strlen(tmp);
169 
170  /* Multi key shortcut */
171  if (strchr(tmp, '-') != NULL && tmp_length > 2) {
172  switch (tmp[0]) {
173  case 'S':
174  shortcut_mask = GDK_SHIFT_MASK;
175  break;
176  case 'A':
177  shortcut_mask = GDK_MOD1_MASK;
178  break;
179  case 'C':
180  shortcut_mask = GDK_CONTROL_MASK;
181  break;
182  default:
183  girara_warning("Invalid modifier in %s", tmp);
184  girara_notify(session, GIRARA_ERROR, _("Invalid modifier in %s"), tmp);
185  g_free(tmp);
186  return false;
187  }
188 
189  /* Single key */
190  if (tmp_length == 3) {
191  shortcut_key = tmp[2];
192  /* Possible special key */
193  } else {
194  bool found = false;
195  for (unsigned int i = 0; i < LENGTH(gdk_keyboard_buttons); i++) {
196  if (g_strcmp0(tmp + 2, gdk_keyboard_buttons[i].identifier) == 0) {
197  shortcut_key = gdk_keyboard_buttons[i].keyval;
198  found = true;
199  break;
200  }
201  }
202 
203  for (unsigned int i = 0; i < LENGTH(gdk_mouse_buttons); i++) {
204  if (!g_strcmp0(tmp + 2, gdk_mouse_buttons[i].identifier)) {
205  shortcut_mouse_button = gdk_mouse_buttons[i].button;
206  mouse_event = true;
207  found = true;
208  break;
209  }
210  }
211 
212  for (unsigned int i = 0; i < LENGTH(event_types); i++) {
213  if (!g_strcmp0(tmp + 2, event_types[i].identifier)) {
214  event_type = event_types[i].event;
215  mouse_event = true;
216  found = true;
217  break;
218  }
219  }
220 
221  if (found == false) {
222  girara_warning("Invalid special key value or mode: %s", tmp);
223  girara_notify(session, GIRARA_ERROR, _("Invalid special key value for %s"), tmp);
224  g_free(tmp);
225  return false;
226  }
227  }
228  /* Possible special key */
229  } else {
230  bool found = false;
231  for (unsigned int i = 0; i < LENGTH(gdk_keyboard_buttons); i++) {
232  if (g_strcmp0(tmp, gdk_keyboard_buttons[i].identifier) == 0) {
233  shortcut_key = gdk_keyboard_buttons[i].keyval;
234  found = true;
235  break;
236  }
237  }
238 
239  for (unsigned int i = 0; i < LENGTH(gdk_mouse_buttons); i++) {
240  if (!g_strcmp0(tmp, gdk_mouse_buttons[i].identifier)) {
241  shortcut_mouse_button = gdk_mouse_buttons[i].button;
242  mouse_event = true;
243  found = true;
244  break;
245  }
246  }
247 
248  for (unsigned int i = 0; i < LENGTH(event_types); i++) {
249  if (!g_strcmp0(tmp, event_types[i].identifier)) {
250  event_type = event_types[i].event;
251  mouse_event = true;
252  found = true;
253  break;
254  }
255  }
256 
257  if (found == false) {
258  girara_warning("Invalid special key value or mode: %s", tmp);
259  girara_notify(session, GIRARA_ERROR, _("Invalid special key value or mode %s"), tmp);
260  g_free(tmp);
261  return false;
262  }
263  }
264 
265  g_free(tmp);
266  /* Single key shortcut */
267  } else if (tmp_length == 1) {
268  shortcut_key = tmp[0];
269  /* Buffer command */
270  } else {
271  shortcut_buffer_command = g_strdup(tmp);
272  }
273 
274  /* check for mouse mode */
275  bool mouse_mode = false;
276  if (unmap == false) {
277  if (++current_command < number_of_arguments) {
278  tmp = girara_list_nth(argument_list, current_command);
279  tmp_length = strlen(tmp);
280 
281  if (tmp_length >= 3 && tmp[0] == '[' && tmp[tmp_length - 1] == ']') {
282  mouse_mode = true;
283  if (mouse_event == false) {
284  girara_warning("Mode passed on non-mouse event: %s", tmp);
285  return false;
286  }
287 
288  char* tmp_inner = g_strndup(tmp + 1, tmp_length - 2);
289 
290  bool found = false;
291  for (unsigned int i = 0; i < LENGTH(mouse_events); i++) {
292  if (!g_strcmp0(tmp_inner, mouse_events[i].identifier)) {
293  event_type = mouse_events[i].event;
294  found = true;
295  break;
296  }
297  }
298 
299  if (found == false) {
300  girara_warning("Invalid mouse event mode has been passed: %s", tmp_inner);
301  g_free(tmp_inner);
302  return false;
303  }
304 
305  g_free(tmp_inner);
306  }
307  } else {
308  girara_warning("Invalid number of arguments passed");
309  return false;
310  }
311  }
312 
313  if (unmap == false) {
314  limit = (mouse_mode == true) ? 3 : 2;
315  if (number_of_arguments < limit) {
316  girara_warning("Invalid number of arguments passed: %zu instead of at least %u", number_of_arguments, limit);
317  girara_notify(session, GIRARA_ERROR,
318  _("Invalid number of arguments passed: %zu instead of at least %u"), number_of_arguments, limit);
319  return false;
320  }
321 
322  if (mouse_mode == true) {
323  tmp = girara_list_nth(argument_list, ++current_command);
324  }
325  }
326 
327  /* Check for passed shortcut command */
328  if (unmap == false) {
329  bool found_mapping = false;
330  GIRARA_LIST_FOREACH(session->config.shortcut_mappings, girara_shortcut_mapping_t*, iter, mapping)
331  if (!g_strcmp0(tmp, mapping->identifier)) {
332  shortcut_function = mapping->function;
333  found_mapping = true;
334  break;
335  }
336  GIRARA_LIST_FOREACH_END(session->config.shortcut_mappings, girara_shortcut_mapping_t*, iter, mapping);
337 
338  if (found_mapping == false) {
339  girara_warning("Not a valid shortcut function: %s", tmp);
340  girara_notify(session, GIRARA_ERROR, _("Not a valid shortcut function: %s"), tmp);
341  if (shortcut_buffer_command) {
342  g_free(shortcut_buffer_command);
343  }
344  return false;
345  }
346  }
347 
348  /* Check for passed argument */
349  if (unmap == false) {
350  if (++current_command < number_of_arguments) {
351  tmp = (char*) girara_list_nth(argument_list, current_command);
352 
353  GIRARA_LIST_FOREACH(session->config.argument_mappings, girara_argument_mapping_t*, iter, mapping)
354  if (!g_strcmp0(tmp, mapping->identifier)) {
355  shortcut_argument_n = mapping->value;
356  break;
357  }
358  GIRARA_LIST_FOREACH_END(session->config.argument_mappings, girara_argument_mapping_t*, iter, mapping);
359 
360  /* If no known argument is passed we save it in the data field */
361  if (shortcut_argument_n == 0) {
362  shortcut_argument_data = tmp;
363  /* If a known argument is passed and there are still more arguments,
364  * we save the next one in the data field */
365  } else if (++current_command < number_of_arguments) {
366  tmp = (char*) girara_list_nth(argument_list, current_command);
367  shortcut_argument_data = tmp;
368  }
369  }
370  }
371 
372  if (mouse_event == false) {
373  if (unmap == true) {
374  girara_shortcut_remove(session, shortcut_mask, shortcut_key,
375  shortcut_buffer_command, shortcut_mode);
376  } else {
377  girara_shortcut_add(session, shortcut_mask, shortcut_key, shortcut_buffer_command,
378  shortcut_function, shortcut_mode, shortcut_argument_n, shortcut_argument_data);
379  }
380  } else {
381  if (unmap == true) {
382  girara_mouse_event_remove(session, shortcut_mask, shortcut_mouse_button,
383  shortcut_mode);
384  } else {
385  girara_mouse_event_add(session, shortcut_mask, shortcut_mouse_button,
386  shortcut_function, shortcut_mode, event_type, shortcut_argument_n, shortcut_argument_data);
387  }
388  }
389 
390  if (shortcut_buffer_command) {
391  g_free(shortcut_buffer_command);
392  }
393 
394  return true;
395 }
396 
397 bool
398 girara_cmd_map(girara_session_t* session, girara_list_t* argument_list)
399 {
400  return girara_cmd_map_unmap(session, argument_list, false);
401 }
402 
403 bool
404 girara_cmd_unmap(girara_session_t* session, girara_list_t* argument_list)
405 {
406  return girara_cmd_map_unmap(session, argument_list, true);
407 }
408 
409 
410 bool
411 girara_cmd_quit(girara_session_t* session, girara_list_t* UNUSED(argument_list))
412 {
413  girara_argument_t arg = { GIRARA_HIDE, NULL };
414  girara_isc_completion(session, &arg, NULL, 0);
415 
416  gtk_main_quit();
417 
418  return true;
419 }
420 
421 bool
422 girara_cmd_set(girara_session_t* session, girara_list_t* argument_list)
423 {
424  const size_t number_of_arguments = girara_list_size(argument_list);
425 
426  if (number_of_arguments == 0) {
427  girara_warning("Not enough arguments for :set.");
428  girara_notify(session, GIRARA_ERROR, _("Not enough arguments."));
429  return false;
430  }
431  if (number_of_arguments > 2) {
432  girara_warning("Too many arguments for :set.");
433  girara_notify(session, GIRARA_ERROR, _("Too many arguments."));
434  return false;
435  }
436 
437  /* search for existing setting */
438  char* name = (char*) girara_list_nth(argument_list, 0);
439  if (name == NULL) {
440  return false;
441  }
442 
443  girara_setting_t* setting = girara_setting_find(session, name);
444  if (setting == NULL) {
445  girara_warning("Unknown option: %s", name);
446  girara_notify(session, GIRARA_ERROR, _("Unknown option: %s"), name);
447  return false;
448  }
449 
450  if (number_of_arguments == 1) {
451  /* display setting*/
452  switch (girara_setting_get_type(setting)) {
453  case BOOLEAN:
454  {
455  /* for compatibility reasons: toogle the setting */
456  bool value = false;
457  girara_setting_get_value(setting, &value);
458  bool tmp = !value;
459  girara_setting_set_value(session, setting, &tmp);
460  girara_notify(session, GIRARA_INFO, "%s: %s", name, tmp ? _("true") : _("false"));
461  break;
462  }
463  case FLOAT:
464  {
465  float value = 0;
466  girara_setting_get_value(setting, &value);
467  girara_notify(session, GIRARA_INFO, "%s: %f", name, value);
468  break;
469  }
470  case INT:
471  {
472  int value = 0;
473  girara_setting_get_value(setting, &value);
474  girara_notify(session, GIRARA_INFO, "%s: %i", name, value);
475  break;
476  }
477  case STRING:
478  {
479  char* str = NULL;
480  girara_setting_get_value(setting, &str);
481  girara_notify(session, GIRARA_INFO, "%s: %s", name, str ? str : "(NULL)");
482  g_free(str);
483  break;
484  }
485  default:
486  return false;
487  }
488  } else {
489  char* value = (char*) girara_list_nth(argument_list, 1);
490  if (value == NULL) {
491  girara_warning("No value defined for option: %s", name);
492  girara_notify(session, GIRARA_ERROR, _("No value defined for option: %s"), name);
493  return false;
494  }
495 
496  /* update value */
497  switch (girara_setting_get_type(setting)) {
498  case BOOLEAN:
499  if (g_strcmp0(value, "false") == 0 || g_strcmp0(value, "0") == 0) {
500  bool b = false;
501  girara_setting_set_value(session, setting, &b);
502  } else if (g_strcmp0(value, "true") == 0 || g_strcmp0(value, "1") == 0) {
503  bool b = true;
504  girara_setting_set_value(session, setting, &b);
505  } else {
506  girara_warning("Unknown value for option: %s", name);
507  girara_notify(session, GIRARA_ERROR, _("Unknown value for option: %s"), name);
508  }
509  break;
510  case FLOAT:
511  {
512  float f = strtof(value, NULL);
513  girara_setting_set_value(session, setting, &f);
514  break;
515  }
516  case INT:
517  {
518  int i = atoi(value);
519  girara_setting_set_value(session, setting, &i);
520  break;
521  }
522  case STRING:
523  girara_setting_set_value(session, setting, value);
524  break;
525  default:
526  return false;
527  }
528  }
529 
530  return true;
531 }
532 
533 bool
534 girara_inputbar_command_add(girara_session_t* session, const char* command,
535  const char* abbreviation, girara_command_function_t function,
536  girara_completion_function_t completion, const char* description)
537 {
538  g_return_val_if_fail(session != NULL, false);
539  g_return_val_if_fail(command != NULL, false);
540  g_return_val_if_fail(function != NULL, false);
541 
542  /* search for existing binding */
543  GIRARA_LIST_FOREACH(session->bindings.commands, girara_command_t*, iter, commands_it)
544  if (g_strcmp0(commands_it->command, command) == 0) {
545  g_free(commands_it->abbr);
546  g_free(commands_it->description);
547 
548  commands_it->abbr = abbreviation ? g_strdup(abbreviation) : NULL;
549  commands_it->function = function;
550  commands_it->completion = completion;
551  commands_it->description = description ? g_strdup(description) : NULL;
552 
554  return true;
555  }
556  GIRARA_LIST_FOREACH_END(session->bindings.commands, girara_command_t*, iter, commands_it);
557 
558  /* add new inputbar command */
559  girara_command_t* new_command = g_slice_new(girara_command_t);
560 
561  new_command->command = g_strdup(command);
562  new_command->abbr = abbreviation ? g_strdup(abbreviation) : NULL;
563  new_command->function = function;
564  new_command->completion = completion;
565  new_command->description = description ? g_strdup(description) : NULL;
566  girara_list_append(session->bindings.commands, new_command);
567 
568  return true;
569 }
570 
571 bool
572 girara_special_command_add(girara_session_t* session, char identifier, girara_inputbar_special_function_t function, bool always, int argument_n, void* argument_data)
573 {
574  g_return_val_if_fail(session != NULL, false);
575  g_return_val_if_fail(function != NULL, false);
576 
577  girara_argument_t argument = {argument_n, argument_data};
578 
579  /* search for existing special command */
580  GIRARA_LIST_FOREACH(session->bindings.special_commands, girara_special_command_t*, iter, scommand_it)
581  if (scommand_it->identifier == identifier) {
582  scommand_it->function = function;
583  scommand_it->always = always;
584  scommand_it->argument = argument;
586  return true;
587  }
588  GIRARA_LIST_FOREACH_END(session->bindings.special_commands, girara_special_command_t*, iter, scommand_it);
589 
590  /* create new special command */
591  girara_special_command_t* special_command = g_slice_new(girara_special_command_t);
592 
593  special_command->identifier = identifier;
594  special_command->function = function;
595  special_command->always = always;
596  special_command->argument = argument;
597 
598  girara_list_append(session->bindings.special_commands, special_command);
599 
600  return true;
601 }
602 
603 void
604 girara_special_command_free(girara_special_command_t* special_command)
605 {
606  if (special_command == NULL) {
607  return;
608  }
609  g_slice_free(girara_special_command_t, special_command);
610 }
611 
612 void
613 girara_command_free(girara_command_t* command)
614 {
615  if (command == NULL) {
616  return;
617  }
618 
619  g_free(command->command);
620  g_free(command->abbr);
621  g_free(command->description);
622  g_slice_free(girara_command_t, command);
623 }
624 
625 bool
626 girara_cmd_exec(girara_session_t* session, girara_list_t* argument_list)
627 {
628  char* cmd = NULL;
629  girara_setting_get(session, "exec-command", &cmd);
630  if (cmd == NULL || strlen(cmd) == 0) {
631  girara_warning("exec-command is invalid.");
632  girara_notify(session, GIRARA_ERROR, _("exec-command is invalid."));
633  g_free(cmd);
634  return false;
635  }
636 
637  GString* command = g_string_new(cmd);
638  g_free(cmd);
639 
640  GIRARA_LIST_FOREACH(argument_list, char*, iter, value)
641  g_string_append_c(command, ' ');
642  char* tmp = g_shell_quote(value);
643  g_string_append(command, tmp);
644  g_free(tmp);
645  GIRARA_LIST_FOREACH_END(argument_list, char*, iter, value);
646 
647  GError* error = NULL;
648  gboolean ret = g_spawn_command_line_async(command->str, &error);
649  if (error != NULL) {
650  girara_warning("Failed to execute command: %s", error->message);
651  girara_notify(session, GIRARA_ERROR, _("Failed to execute command: %s"), error->message);
652  g_error_free(error);
653  }
654 
655  g_string_free(command, TRUE);
656  return ret;
657 }