Atrinik Client  4.0
keybind.c
Go to the documentation of this file.
1 /*************************************************************************
2  * Atrinik, a Multiplayer Online Role Playing Game *
3  * *
4  * Copyright (C) 2009-2014 Alex Tokar and Atrinik Development Team *
5  * *
6  * Fork from Crossfire (Multiplayer game for X-windows). *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
21  * *
22  * The author can be reached at admin@atrinik.org *
23  ************************************************************************/
24 
47 #include <global.h>
48 #include <toolkit/packet.h>
49 #include <toolkit/string.h>
50 #include <toolkit/path.h>
51 
55 size_t keybindings_num = 0;
56 
60 void keybind_load(void)
61 {
62  FILE *fp;
63  char buf[HUGE_BUF], *cp;
64  keybind_struct *keybind = NULL;
65 
66  fp = path_fopen(FILE_KEYBIND, "r");
67 
68  if (fp == NULL) {
69  LOG(ERROR, "Failed to open file: %s", FILE_KEYBIND);
70  return;
71  }
72 
73  while (fgets(buf, sizeof(buf) - 1, fp)) {
74  cp = strchr(buf, '\n');
75 
76  if (cp) {
77  *cp = '\0';
78  }
79 
80  cp = buf;
81 
82  while (*cp != '\0') {
83  if (isspace(*cp)) {
84  cp++;
85  } else {
86  break;
87  }
88  }
89 
90  if (*cp == '#' || *cp == '\0') {
91  continue;
92  }
93 
94  /* End of a single keybinding definition, add it to the list. */
95  if (!strcmp(cp, "end")) {
96  keybindings = erealloc(keybindings, sizeof(*keybindings) * (keybindings_num + 1));
97  keybindings[keybindings_num] = keybind;
99  keybind = NULL;
100  } else if (keybind) {
101  if (!strncmp(cp, "command ", 8)) {
102  keybind->command = estrdup(cp + 8);
103  } else if (!strncmp(cp, "key ", 4)) {
104  keybind->key = atoi(cp + 4);
105  } else if (!strncmp(cp, "mod ", 4)) {
106  keybind->mod = atoi(cp + 4);
107  } else if (!strncmp(cp, "repeat ", 7)) {
108  keybind->repeat = atoi(cp + 7);
109  }
110  } else if (!strcmp(cp, "bind")) {
111  /* Keybinding definition start. */
112  keybind = ecalloc(1, sizeof(*keybind));
113  }
114  }
115 
116  fclose(fp);
117 }
118 
122 void keybind_save(void)
123 {
124  FILE *fp = path_fopen(FILE_KEYBIND, "w");
125  if (fp == NULL) {
126  LOG(ERROR, "Could not open %s for writing: %s (%d)", FILE_KEYBIND,
127  strerror(errno), errno);
128  return;
129  }
130 
131  for (size_t i = 0; i < keybindings_num; i++) {
132  fprintf(fp, "bind\n");
133  fprintf(fp, "\t# %s\n\tkey %d\n", SDL_GetKeyName(keybindings[i]->key),
134  keybindings[i]->key);
135 
136  if (keybindings[i]->mod != 0) {
137  fprintf(fp, "\tmod %d\n", keybindings[i]->mod);
138  }
139 
140  if (keybindings[i]->repeat) {
141  fprintf(fp, "\trepeat %d\n", keybindings[i]->repeat);
142  }
143 
144  if (keybindings[i]->command != NULL) {
145  fprintf(fp, "\tcommand %s\n", keybindings[i]->command);
146  }
147 
148  fprintf(fp, "end\n");
149  }
150 
151  fclose(fp);
152 }
153 
160 {
161  efree(keybind->command);
162  efree(keybind);
163 }
164 
168 void keybind_deinit(void)
169 {
170  size_t i;
171 
172  /* Save them... */
173  keybind_save();
174 
175  for (i = 0; i < keybindings_num; i++) {
176  keybind_free(keybindings[i]);
177  }
178 
179  if (keybindings) {
180  efree(keybindings);
181  keybindings = NULL;
182  }
183 
184  keybindings_num = 0;
185 }
186 
196 static SDLMod keybind_adjust_kmod(SDLMod mod)
197 {
198  /* We only care about left/right shift, ctrl, alt, and super
199  * modifiers, so remove any others. */
200  mod &= KMOD_SHIFT | KMOD_CTRL | KMOD_ALT | KMOD_META;
201 
202  /* The following code deals with making sure that if the modifier
203  * contains only for example left shift modifier, right shift is also
204  * added to the modifier, in order to simplify saving and state
205  * checks. */
206  if (mod & KMOD_SHIFT) {
207  mod |= KMOD_SHIFT;
208  }
209 
210  if (mod & KMOD_CTRL) {
211  mod |= KMOD_CTRL;
212  }
213 
214  if (mod & KMOD_ALT) {
215  mod |= KMOD_ALT;
216  }
217 
218  if (mod & KMOD_META) {
219  mod |= KMOD_META;
220  }
221 
222  return mod;
223 }
224 
237 keybind_struct *keybind_add(SDLKey key, SDLMod mod, const char *command)
238 {
239  keybind_struct *keybind;
240 
241  /* Allocate a new keybinding, and store the values. */
242  keybind = ecalloc(1, sizeof(*keybind));
243  keybind->key = key;
244  keybind->mod = keybind_adjust_kmod(mod);
245  keybind->command = estrdup(command);
246 
247  /* Expand the keybindings array, and store the new keybinding. */
248  keybindings = erealloc(keybindings, sizeof(*keybindings) * (keybindings_num + 1));
249  keybindings[keybindings_num] = keybind;
250  keybindings_num++;
251 
252  return keybind;
253 }
254 
266 void keybind_edit(size_t i, SDLKey key, SDLMod mod, const char *command)
267 {
268  /* Sanity check. */
269  if (i >= keybindings_num) {
270  return;
271  }
272 
273  /* Store the values. */
274  keybindings[i]->key = key;
275  keybindings[i]->mod = keybind_adjust_kmod(mod);
276  efree(keybindings[i]->command);
277  keybindings[i]->command = estrdup(command);
278 }
279 
285 void keybind_remove(size_t i)
286 {
287  size_t j;
288 
289  /* Sanity check. */
290  if (i >= keybindings_num) {
291  return;
292  }
293 
294  /* Free the entry. */
295  keybind_free(keybindings[i]);
296 
297  /* Shift entries below the removed keybinding, if any. */
298  for (j = i + 1; j < keybindings_num; j++) {
299  keybindings[j - 1] = keybindings[j];
300  }
301 
302  /* Shrink the array. */
303  keybindings_num--;
304  keybindings = erealloc(keybindings, sizeof(*keybindings) * keybindings_num);
305 }
306 
313 void keybind_repeat_toggle(size_t i)
314 {
315  /* Sanity check. */
316  if (i >= keybindings_num) {
317  return;
318  }
319 
320  keybindings[i]->repeat = !keybindings[i]->repeat;
321 }
322 
336 char *keybind_get_key_shortcut(SDLKey key, SDLMod mod, char *buf, size_t len)
337 {
338  buf[0] = '\0';
339 
340  /* Prefix with the keyboard modifier. */
341  if (mod & KMOD_SHIFT) {
342  strncat(buf, "shift + ", len - strlen(buf) - 1);
343  }
344 
345  if (mod & KMOD_CTRL) {
346  strncat(buf, "ctrl + ", len - strlen(buf) - 1);
347  }
348 
349  if (mod & KMOD_ALT) {
350  strncat(buf, "alt + ", len - strlen(buf) - 1);
351  }
352 
353  if (mod & KMOD_META) {
354  strncat(buf, "super + ", len - strlen(buf) - 1);
355  }
356 
357  if (key != SDLK_UNKNOWN) {
358  strncat(buf, SDL_GetKeyName(key), len - strlen(buf) - 1);
359  }
360 
361  return buf;
362 }
363 
372 {
373  size_t i;
374 
375  for (i = 0; i < keybindings_num; i++) {
376  if (!strcmp(cmd, keybindings[i]->command)) {
377  return keybindings[i];
378  }
379  }
380 
381  return NULL;
382 }
383 
391 int keybind_command_matches_event(const char *cmd, SDL_KeyboardEvent *event)
392 {
393  keybind_struct *keybind = keybind_find_by_command(cmd);
394 
395  if (!keybind) {
396  return 0;
397  }
398 
399  if (event->keysym.sym == keybind->key && (!keybind->mod || keybind->mod == keybind_adjust_kmod(event->keysym.mod))) {
400  return 1;
401  }
402 
403  return 0;
404 }
405 
414 int keybind_command_matches_state(const char *cmd)
415 {
416  size_t i;
417 
418  for (i = 0; i < keybindings_num; i++) {
419  if (!strcmp(cmd, keybindings[i]->command)) {
420  if (keys[keybindings[i]->key].pressed && (!keybindings[i]->mod || keybindings[i]->mod == keybind_adjust_kmod(SDL_GetModState()))) {
421  return 1;
422  }
423  }
424  }
425 
426  return 0;
427 }
428 
436 int keybind_process_event(SDL_KeyboardEvent *event)
437 {
438  size_t i;
439 
440  /* Try to handle keybindings with modifier keys first. */
441  for (i = 0; i < keybindings_num; i++) {
442  if (event->keysym.sym == keybindings[i]->key && keybindings[i]->mod == keybind_adjust_kmod(event->keysym.mod)) {
443  keybind_process(keybindings[i], event->type);
444  return 1;
445  }
446  }
447 
448  /* Now handle keys with no modifier keys, regardless of what the
449  * current keyboard modifier combination is. */
450  for (i = 0; i < keybindings_num; i++) {
451  if (event->keysym.sym == keybindings[i]->key && !keybindings[i]->mod) {
452  keybind_process(keybindings[i], event->type);
453  return 1;
454  }
455  }
456 
457  return 0;
458 }
459 
467 void keybind_process(keybind_struct *keybind, uint8_t type)
468 {
469  char command[MAX_BUF], *cp;
470 
471  /* Do not repeat keys that should not be repeated. */
472  if (!keybind->repeat && keys[keybind->key].repeated) {
473  return;
474  }
475 
476  strncpy(command, keybind->command, sizeof(command) - 1);
477  command[sizeof(command) - 1] = '\0';
478 
479  cp = strtok(command, ";");
480 
481  while (cp) {
482  while (*cp == ' ') {
483  cp++;
484  }
485 
486  if (type == SDL_KEYDOWN) {
488  } else {
490  }
491 
492  cp = strtok(NULL, ";");
493  }
494 }
495 
503 int keybind_process_command_up(const char *cmd)
504 {
505  const char *cmd_orig = cmd;
506 
507  if (*cmd == '?') {
508  cmd++;
509 
510  if (!strcmp(cmd, "RUNON")) {
511  cpl.run_on = 0;
512  move_keys(0);
513  } else if (!strcmp(cmd, "FIREON")) {
514  cpl.fire_on = 0;
515  } else if (!strncmp(cmd, "MOVE_", 5)) {
516  keybind_struct *keybind;
517 
518  cmd += 5;
519 
520  if (strcmp(cmd, "STAY") != 0 && !cpl.fire_on && (keybind = keybind_find_by_command(cmd_orig)) && keys[keybind->key].repeated) {
521  move_keys(5);
522  }
523  }
524 
525  return 1;
526  }
527 
528  return 0;
529 }
530 
536 {
537  if (cpl.run_on && !keybind_command_matches_state("?RUNON")) {
538  keybind_process_command_up("?RUNON");
539  }
540 
541  if (cpl.fire_on && !keybind_command_matches_state("?FIREON")) {
542  keybind_process_command_up("?FIREON");
543  }
544 }
545 
553 int keybind_process_command(const char *cmd)
554 {
555  if (notification_keybind_check(cmd)) {
556  return 1;
557  }
558 
559  if (*cmd == '?') {
560  cmd++;
561 
562  if (!strncmp(cmd, "MOVE_", 5)) {
563  cmd += 5;
564 
565  if (!strcmp(cmd, "N")) {
566  move_keys(8);
567  } else if (!strcmp(cmd, "NE")) {
568  move_keys(9);
569  } else if (!strcmp(cmd, "E")) {
570  move_keys(6);
571  } else if (!strcmp(cmd, "SE")) {
572  move_keys(3);
573  } else if (!strcmp(cmd, "S")) {
574  move_keys(2);
575  } else if (!strcmp(cmd, "SW")) {
576  move_keys(1);
577  } else if (!strcmp(cmd, "W")) {
578  move_keys(4);
579  } else if (!strcmp(cmd, "NW")) {
580  move_keys(7);
581  } else if (!strcmp(cmd, "N")) {
582  move_keys(8);
583  } else if (!strcmp(cmd, "STAY")) {
584  move_keys(5);
585  }
586  } else if (!strcmp(cmd, "CONSOLE")) {
587  widget_textwin_handle_console(NULL);
588  } else if (!strcmp(cmd, "APPLY")) {
590  } else if (!strcmp(cmd, "EXAMINE")) {
592  } else if (!strcmp(cmd, "MARK")) {
594  } else if (!strcmp(cmd, "LOCK")) {
596  } else if (!strcmp(cmd, "GET")) {
598  } else if (!strcmp(cmd, "DROP")) {
600  } else if (!strcmp(cmd, "HELP")) {
601  help_show("main");
602  } else if (!strcmp(cmd, "QLIST")) {
603  packet_struct *packet;
604 
605  packet = packet_new(SERVER_CMD_QUESTLIST, 0, 0);
606  socket_send_packet(packet);
607  } else if (!strcmp(cmd, "TARGET_ENEMY")) {
609  } else if (!strcmp(cmd, "TARGET_FRIEND")) {
611  } else if (!strcmp(cmd, "SPELL_LIST")) {
612  cur_widget[SPELLS_ID]->show = !cur_widget[SPELLS_ID]->show;
613  SetPriorityWidget(cur_widget[SPELLS_ID]);
614  } else if (!strcmp(cmd, "SKILL_LIST")) {
615  cur_widget[SKILLS_ID]->show = !cur_widget[SKILLS_ID]->show;
616  SetPriorityWidget(cur_widget[SKILLS_ID]);
617  } else if (!strcmp(cmd, "PARTY_LIST")) {
618  if (cur_widget[PARTY_ID]->show) {
619  cur_widget[PARTY_ID]->show = 0;
620  } else {
621  send_command("/party list");
622  }
623  } else if (!strncmp(cmd, "MCON", 4)) {
624  cmd += 4;
625 
626  while (*cmd == ' ') {
627  cmd++;
628  }
629 
630  widget_textwin_handle_console(cmd);
631  } else if (!strcmp(cmd, "UP")) {
633  } else if (!strcmp(cmd, "DOWN")) {
635  } else if (!strcmp(cmd, "LEFT")) {
637  } else if (!strcmp(cmd, "RIGHT")) {
639  } else if (!strncmp(cmd, "RUNON", 5)) {
640  if (!strcmp(cmd + 5, "_TOGGLE")) {
641  if (cpl.run_on) {
642  move_keys(5);
643  }
644 
645  cpl.run_on = !cpl.run_on;
646  } else {
647  cpl.run_on = 1;
648  }
649  } else if (!strncmp(cmd, "FIREON", 6)) {
650  if (!strcmp(cmd + 6, "_TOGGLE")) {
651  cpl.fire_on = !cpl.fire_on;
652  } else {
653  cpl.fire_on = 1;
654  }
655  } else if (!strncmp(cmd, "QUICKSLOT_", 10)) {
656  cmd += 10;
657 
658  if (string_startswith(cmd, "GROUP_")) {
659  widgetdata *widget;
660 
661  cmd += 6;
662  widget = widget_find(NULL, QUICKSLOT_ID, NULL, NULL);
663 
664  if (strcmp(cmd, "NEXT") == 0) {
665  quickslots_scroll(widget, 0, 1);
666  } else if (strcmp(cmd, "PREV") == 0) {
667  quickslots_scroll(widget, 1, 1);
668  } else if (strcmp(cmd, "CYCLE") == 0) {
669  quickslots_cycle(widget);
670  }
671  } else if (string_isdigit(cmd)) {
672  quickslots_handle_key(MAX(1, MIN(8, atoi(cmd))) - 1);
673  }
674  } else if (!strcmp(cmd, "COPY")) {
675  textwin_handle_copy(NULL);
676  } else if (!strcmp(cmd, "HELLO")) {
677  send_command_check("/talk 1 hello");
678  } else if (strcmp(cmd, "COMBAT") == 0 ||
679  strcmp(cmd, "COMBAT_FORCE") == 0) {
680  uint8_t combat = cpl.combat, combat_force = cpl.combat_force;
681 
682  if (strcmp(cmd, "COMBAT") == 0) {
683  combat = !combat;
684  } else {
685  combat_force = !combat_force;
686  }
687 
688  WIDGET_REDRAW_ALL(TARGET_ID);
689 
690  packet_struct *packet = packet_new(SERVER_CMD_COMBAT, 8, 0);
691  packet_append_uint8(packet, combat);
692  packet_append_uint8(packet, combat_force);
693  socket_send_packet(packet);
694  }
695 
696  return 1;
697  } else {
698  if (send_command_check(cmd)) {
699  draw_info(COLOR_DGOLD, cmd);
700  }
701  }
702 
703  return 0;
704 }
void keybind_save(void)
Definition: keybind.c:122
void keybind_process(keybind_struct *keybind, uint8_t type)
Definition: keybind.c:467
void menu_inventory_lock(widgetdata *widget, widgetdata *menuitem, SDL_Event *event)
Definition: inventory.c:1223
void draw_info(const char *color, const char *str)
Definition: textwin.c:448
void menu_inventory_drop(widgetdata *widget, widgetdata *menuitem, SDL_Event *event)
Definition: inventory.c:938
void keybind_state_ensure(void)
Definition: keybind.c:535
void quickslots_handle_key(int slot)
Definition: quickslots.c:230
void menu_inventory_get(widgetdata *widget, widgetdata *menuitem, SDL_Event *event)
Definition: inventory.c:1019
void send_command(const char *command)
Definition: player.c:175
int keybind_command_matches_state(const char *cmd)
Definition: keybind.c:414
int notification_keybind_check(const char *cmd)
Definition: notification.c:95
widgetdata * widget_find(widgetdata *where, int type, const char *id, SDL_Surface *surface)
Definition: widget.c:2091
keybind_struct ** keybindings
Definition: keybind.c:53
void help_show(const char *name)
Definition: help.c:219
static SDLMod keybind_adjust_kmod(SDLMod mod)
Definition: keybind.c:196
void map_target_handle(uint8_t is_friend)
Definition: map.c:2009
uint8_t show
Definition: widget.h:69
uint8_t fire_on
Definition: player.h:155
char * command
Definition: keybind.h:41
void keybind_load(void)
Definition: keybind.c:60
Client_Player cpl
Definition: client.c:50
char * keybind_get_key_shortcut(SDLKey key, SDLMod mod, char *buf, size_t len)
Definition: keybind.c:336
void menu_inventory_mark(widgetdata *widget, widgetdata *menuitem, SDL_Event *event)
Definition: inventory.c:1194
void quickslots_cycle(widgetdata *widget)
Definition: quickslots.c:111
int keybind_process_command_up(const char *cmd)
Definition: keybind.c:503
widgetdata * cur_widget[TOTAL_SUBWIDGETS]
Definition: widget.c:75
uint8_t run_on
Definition: player.h:158
void widget_inventory_handle_apply(widgetdata *widget)
Definition: inventory.c:1273
void menu_inventory_examine(widgetdata *widget, widgetdata *menuitem, SDL_Event *event)
Definition: inventory.c:1119
uint8_t repeat
Definition: keybind.h:50
SDLKey key
Definition: keybind.h:44
void keybind_edit(size_t i, SDLKey key, SDLMod mod, const char *command)
Definition: keybind.c:266
void keybind_deinit(void)
Definition: keybind.c:168
keybind_struct * keybind_add(SDLKey key, SDLMod mod, const char *command)
Definition: keybind.c:237
int keybind_process_event(SDL_KeyboardEvent *event)
Definition: keybind.c:436
int keybind_command_matches_event(const char *cmd, SDL_KeyboardEvent *event)
Definition: keybind.c:391
void quickslots_scroll(widgetdata *widget, int up, int scroll)
Definition: quickslots.c:98
#define FILE_KEYBIND
Definition: keybind.h:34
void textwin_handle_copy(widgetdata *widget)
Definition: textwin.c:459
keybind_struct * keybind_find_by_command(const char *cmd)
Definition: keybind.c:371
int send_command_check(const char *cmd)
Definition: menu.c:376
void keybind_repeat_toggle(size_t i)
Definition: keybind.c:313
uint8_t repeated
Definition: event.h:56
widgetdata * inventory_focus
Definition: player.h:200
uint8_t combat_force
Definition: player.h:264
void SetPriorityWidget(widgetdata *node)
Definition: widget.c:1788
uint8_t combat
Definition: player.h:258
void keybind_remove(size_t i)
Definition: keybind.c:285
void widget_inventory_handle_arrow_key(widgetdata *widget, SDLKey key)
Definition: inventory.c:795
size_t keybindings_num
Definition: keybind.c:55
int keybind_process_command(const char *cmd)
Definition: keybind.c:553
void keybind_free(keybind_struct *keybind)
Definition: keybind.c:159
#define COLOR_DGOLD
Definition: text.h:321
SDLMod mod
Definition: keybind.h:47