Customizable keys
[cmus.git] / cmus / keys.c
blob1d6b6fed992d4bc37c80efe3e22cb19693e6a3b7
1 /*
2 * Copyright 2004-2005 Timo Hirvonen
4 * keys.[ch] by Frank Terbeck <frank.terbeck@rwth-aachen.de>
5 * heavily modified by Timo Hirvonen
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 * 02111-1307, USA.
23 #include <keys.h>
24 #include <player.h>
25 #include <misc.h>
26 #include <cmus.h>
27 #include <filters.h>
28 #include <search_mode.h>
29 #include <play_queue.h>
30 #include <pl.h>
31 #include <browser.h>
32 #include <window.h>
33 #include <ui_curses.h>
34 #include <file_load.h>
35 #include <xmalloc.h>
36 #include <xstrjoin.h>
38 #include <curses.h>
39 #include <stdio.h>
40 #include <ctype.h>
42 enum context {
43 CTX_COMMON,
45 CTX_PLAYLIST,
46 CTX_PLAY_QUEUE,
47 CTX_BROWSER,
48 CTX_FILTERS
50 #define NR_CTXS (CTX_FILTERS + 1)
52 struct key {
53 const char *name;
54 int key;
55 uchar ch;
58 struct function {
59 char *name;
60 void (*func)(void);
63 struct binding {
64 struct binding *next;
65 const struct key *key;
66 const struct function *func;
69 static const char * const context_names[NR_CTXS] = {
70 "common",
71 "playlist",
72 "play_queue",
73 "browser",
74 "filters"
77 static const enum context view_to_context[] = {
78 CTX_PLAYLIST,
79 CTX_PLAYLIST,
80 CTX_PLAYLIST,
81 CTX_PLAY_QUEUE,
82 CTX_BROWSER,
83 CTX_FILTERS,
86 static struct binding *bindings[NR_CTXS] = { NULL, };
88 #define KEY_IS_CHAR -255
90 static int initializing = 1;
91 static char *filename;
93 static void win_activate_next(void)
95 if (ui_curses_view == TREE_VIEW)
96 pl_toggle_active_window();
99 static void win_bottom(void)
101 enum context c = view_to_context[ui_curses_view];
103 switch (c) {
104 case CTX_COMMON:
105 break;
106 case CTX_PLAYLIST:
107 pl_sel_bottom();
108 break;
109 case CTX_PLAY_QUEUE:
110 play_queue_lock();
111 if (window_goto_bottom(play_queue_win))
112 play_queue_changed = 1;
113 play_queue_unlock();
114 break;
115 case CTX_BROWSER:
116 if (window_goto_bottom(browser_win))
117 browser_changed = 1;
118 break;
119 case CTX_FILTERS:
120 if (window_goto_bottom(filters_win))
121 filters_changed = 1;
122 break;
126 static void win_down(void)
128 enum context c = view_to_context[ui_curses_view];
130 switch (c) {
131 case CTX_COMMON:
132 break;
133 case CTX_PLAYLIST:
134 pl_sel_down();
135 break;
136 case CTX_PLAY_QUEUE:
137 play_queue_lock();
138 if (window_move(play_queue_win, 1))
139 play_queue_changed = 1;
140 play_queue_unlock();
141 break;
142 case CTX_BROWSER:
143 if (window_move(browser_win, 1))
144 browser_changed = 1;
145 break;
146 case CTX_FILTERS:
147 if (window_move(filters_win, 1))
148 filters_changed = 1;
149 break;
153 static void win_page_down(void)
155 enum context c = view_to_context[ui_curses_view];
157 switch (c) {
158 case CTX_COMMON:
159 break;
160 case CTX_PLAYLIST:
161 pl_sel_page_down();
162 break;
163 case CTX_PLAY_QUEUE:
164 play_queue_lock();
165 if (window_page_down(play_queue_win))
166 play_queue_changed = 1;
167 play_queue_unlock();
168 break;
169 case CTX_BROWSER:
170 if (window_page_down(browser_win))
171 browser_changed = 1;
172 break;
173 case CTX_FILTERS:
174 if (window_page_down(filters_win))
175 filters_changed = 1;
176 break;
180 static void win_page_up(void)
182 enum context c = view_to_context[ui_curses_view];
184 switch (c) {
185 case CTX_COMMON:
186 break;
187 case CTX_PLAYLIST:
188 pl_sel_page_up();
189 break;
190 case CTX_PLAY_QUEUE:
191 play_queue_lock();
192 if (window_page_up(play_queue_win))
193 play_queue_changed = 1;
194 play_queue_unlock();
195 break;
196 case CTX_BROWSER:
197 if (window_page_up(browser_win))
198 browser_changed = 1;
199 break;
200 case CTX_FILTERS:
201 if (window_page_up(filters_win))
202 filters_changed = 1;
203 break;
207 static void win_top(void)
209 enum context c = view_to_context[ui_curses_view];
211 switch (c) {
212 case CTX_COMMON:
213 break;
214 case CTX_PLAYLIST:
215 pl_sel_top();
216 break;
217 case CTX_PLAY_QUEUE:
218 play_queue_lock();
219 if (window_goto_top(play_queue_win))
220 play_queue_changed = 1;
221 play_queue_unlock();
222 break;
223 case CTX_BROWSER:
224 if (window_goto_top(browser_win))
225 browser_changed = 1;
226 break;
227 case CTX_FILTERS:
228 if (window_goto_top(filters_win))
229 filters_changed = 1;
230 break;
234 static void win_up(void)
236 enum context c = view_to_context[ui_curses_view];
238 switch (c) {
239 case CTX_COMMON:
240 break;
241 case CTX_PLAYLIST:
242 pl_sel_up();
243 break;
244 case CTX_PLAY_QUEUE:
245 play_queue_lock();
246 if (window_move(play_queue_win, -1))
247 play_queue_changed = 1;
248 play_queue_unlock();
249 break;
250 case CTX_BROWSER:
251 if (window_move(browser_win, -1))
252 browser_changed = 1;
253 break;
254 case CTX_FILTERS:
255 if (window_move(filters_win, -1))
256 filters_changed = 1;
257 break;
261 static void play_selected(void)
263 struct track_info *info;
265 info = pl_set_selected();
266 if (info) {
267 player_play_file(info->filename);
268 track_info_unref(info);
272 static int queue_append_cb(void *data, struct track_info *ti)
274 __play_queue_append(ti);
275 return 0;
278 static int queue_prepend_cb(void *data, struct track_info *ti)
280 __play_queue_prepend(ti);
281 return 0;
284 static void queue_append(void)
286 play_queue_lock();
287 pl_for_each_selected(queue_append_cb, NULL, 0);
288 pl_sel_down();
289 play_queue_unlock();
292 static void queue_prepend(void)
294 play_queue_lock();
295 pl_for_each_selected(queue_prepend_cb, NULL, 1);
296 pl_sel_down();
297 play_queue_unlock();
300 static void search_next_forward(void)
302 if (search_str) {
303 if (!search_next(searchable, search_str, search_direction))
304 ui_curses_search_not_found();
308 static void search_next_backward(void)
310 if (search_str) {
311 if (!search_next(searchable, search_str, !search_direction))
312 ui_curses_search_not_found();
316 /* functions {{{ */
317 static const struct function common_functions[] = {
318 { "help", display_help },
319 { "next", cmus_next },
320 { "pause", player_pause },
321 { "play", player_play },
322 { "prev", cmus_prev },
323 { "quit", ui_curses_quit },
324 { "search_next", search_next_forward },
325 { "search_prev", search_next_backward },
326 { "seek_backward", cmus_seek_bwd },
327 { "seek_forward", cmus_seek_fwd },
328 { "stop", player_stop },
329 { "toggle_continue", player_toggle_cont },
330 { "toggle_play_mode", pl_toggle_play_mode },
331 { "toggle_playlist_mode", pl_toggle_playlist_mode },
332 { "toggle_remaining_time", ui_curses_toggle_remaining_time },
333 { "toggle_repeat", pl_toggle_repeat },
334 { "view_1", ui_curses_tree_view },
335 { "view_2", ui_curses_shuffle_view },
336 { "view_3", ui_curses_sorted_view },
337 { "view_4", ui_curses_play_queue_view },
338 { "view_5", ui_curses_browser_view },
339 { "view_6", ui_curses_filters_view },
340 { "vol_down", cmus_vol_down },
341 { "vol_left_down", cmus_vol_left_down },
342 { "vol_left_up", cmus_vol_left_up },
343 { "vol_right_down", cmus_vol_right_down },
344 { "vol_right_up", cmus_vol_right_up },
345 { "vol_up", cmus_vol_up },
346 { "win_activate_next", win_activate_next },
347 { "win_bottom", win_bottom },
348 { "win_down", win_down },
349 { "win_page_down", win_page_down },
350 { "win_page_up", win_page_up },
351 { "win_top", win_top },
352 { "win_up", win_up },
353 { NULL, NULL }
356 static struct function playlist_functions[] = {
357 { "expand_artist", pl_toggle_expand_artist },
358 { "play_selected", play_selected },
359 { "queue_append", queue_append },
360 { "queue_prepend", queue_prepend },
361 { "remove", pl_remove_sel },
362 { "select_current", pl_sel_current },
363 { "update", cmus_update_playlist },
364 { NULL, NULL }
367 static const struct function play_queue_functions[] = {
368 { "remove", play_queue_delete },
369 { NULL, NULL }
372 static const struct function browser_functions[] = {
373 { "add", browser_add },
374 { "cd_parent", browser_cd_parent },
375 { "enter", browser_enter },
376 { "queue_append", browser_queue_append },
377 { "queue_prepend", browser_queue_prepend },
378 { "reload", browser_reload },
379 { "remove", browser_delete },
380 { "toggle_show_hidden", browser_toggle_show_hidden },
381 { NULL, NULL }
384 static const struct function filters_functions[] = {
385 { "activate", filters_activate },
386 { "delete_filter", filters_delete_filter },
387 { "toggle_filter", filters_toggle_filter },
388 { NULL, NULL }
390 /* }}} */
392 static const struct function *all_functions[NR_CTXS] = {
393 common_functions,
394 playlist_functions,
395 play_queue_functions,
396 browser_functions,
397 filters_functions
400 /* keys {{{ */
401 static const struct key keys[] = {
402 { "!", KEY_IS_CHAR, 33 },
403 { "#", KEY_IS_CHAR, 35 },
404 { "$", KEY_IS_CHAR, 36 },
405 { "%", KEY_IS_CHAR, 37 },
406 { "&", KEY_IS_CHAR, 38 },
407 { "'", KEY_IS_CHAR, 39 },
408 { "(", KEY_IS_CHAR, 40 },
409 { ")", KEY_IS_CHAR, 41 },
410 { "*", KEY_IS_CHAR, 42 },
411 { "+", KEY_IS_CHAR, 43 },
412 { ",", KEY_IS_CHAR, 44 },
413 { "-", KEY_IS_CHAR, 45 },
414 { ".", KEY_IS_CHAR, 46 },
415 { "0", KEY_IS_CHAR, 48 },
416 { "1", KEY_IS_CHAR, 49 },
417 { "2", KEY_IS_CHAR, 50 },
418 { "3", KEY_IS_CHAR, 51 },
419 { "4", KEY_IS_CHAR, 52 },
420 { "5", KEY_IS_CHAR, 53 },
421 { "6", KEY_IS_CHAR, 54 },
422 { "7", KEY_IS_CHAR, 55 },
423 { "8", KEY_IS_CHAR, 56 },
424 { "9", KEY_IS_CHAR, 57 },
425 { ";", KEY_IS_CHAR, 59 },
426 { "<", KEY_IS_CHAR, 60 },
427 { "=", KEY_IS_CHAR, 61 },
428 { ">", KEY_IS_CHAR, 62 },
429 { "@", KEY_IS_CHAR, 64 },
430 { "A", KEY_IS_CHAR, 65 },
431 { "B", KEY_IS_CHAR, 66 },
432 { "C", KEY_IS_CHAR, 67 },
433 { "D", KEY_IS_CHAR, 68 },
434 { "E", KEY_IS_CHAR, 69 },
435 { "F", KEY_IS_CHAR, 70 },
436 { "F1", KEY_F(1), 0 },
437 { "F10", KEY_F(10), 0 },
438 { "F11", KEY_F(11), 0 },
439 { "F12", KEY_F(12), 0 },
440 { "F2", KEY_F(2), 0 },
441 { "F3", KEY_F(3), 0 },
442 { "F4", KEY_F(4), 0 },
443 { "F5", KEY_F(5), 0 },
444 { "F6", KEY_F(6), 0 },
445 { "F7", KEY_F(7), 0 },
446 { "F8", KEY_F(8), 0 },
447 { "F9", KEY_F(9), 0 },
448 { "G", KEY_IS_CHAR, 71 },
449 { "H", KEY_IS_CHAR, 72 },
450 { "I", KEY_IS_CHAR, 73 },
451 { "J", KEY_IS_CHAR, 74 },
452 { "K", KEY_IS_CHAR, 75 },
453 { "KP_center", KEY_B2, 0 },
454 { "KP_lower_left", KEY_C1, 0 },
455 { "KP_lower_right", KEY_C3, 0 },
456 { "KP_upper_left", KEY_A1, 0 },
457 { "KP_upper_right", KEY_A3, 0 },
458 { "L", KEY_IS_CHAR, 76 },
459 { "M", KEY_IS_CHAR, 77 },
460 { "M- ", KEY_IS_CHAR, 160 },
461 { "M-!", KEY_IS_CHAR, 161 },
462 { "M-#", KEY_IS_CHAR, 163 },
463 { "M-$", KEY_IS_CHAR, 164 },
464 { "M-%", KEY_IS_CHAR, 165 },
465 { "M-&", KEY_IS_CHAR, 166 },
466 { "M-'", KEY_IS_CHAR, 167 },
467 { "M-(", KEY_IS_CHAR, 168 },
468 { "M-)", KEY_IS_CHAR, 169 },
469 { "M-*", KEY_IS_CHAR, 170 },
470 { "M-+", KEY_IS_CHAR, 171 },
471 { "M-,", KEY_IS_CHAR, 172 },
472 { "M--", KEY_IS_CHAR, 173 },
473 { "M-.", KEY_IS_CHAR, 174 },
474 { "M-/", KEY_IS_CHAR, 175 },
475 { "M-0", KEY_IS_CHAR, 176 },
476 { "M-1", KEY_IS_CHAR, 177 },
477 { "M-2", KEY_IS_CHAR, 178 },
478 { "M-3", KEY_IS_CHAR, 179 },
479 { "M-4", KEY_IS_CHAR, 180 },
480 { "M-5", KEY_IS_CHAR, 181 },
481 { "M-6", KEY_IS_CHAR, 182 },
482 { "M-7", KEY_IS_CHAR, 183 },
483 { "M-8", KEY_IS_CHAR, 184 },
484 { "M-9", KEY_IS_CHAR, 185 },
485 { "M-:", KEY_IS_CHAR, 186 },
486 { "M-;", KEY_IS_CHAR, 187 },
487 { "M-<", KEY_IS_CHAR, 188 },
488 { "M-=", KEY_IS_CHAR, 189 },
489 { "M->", KEY_IS_CHAR, 190 },
490 { "M-?", KEY_IS_CHAR, 191 },
491 { "M-@", KEY_IS_CHAR, 192 },
492 { "M-A", KEY_IS_CHAR, 193 },
493 { "M-B", KEY_IS_CHAR, 194 },
494 { "M-C", KEY_IS_CHAR, 195 },
495 { "M-D", KEY_IS_CHAR, 196 },
496 { "M-E", KEY_IS_CHAR, 197 },
497 { "M-F", KEY_IS_CHAR, 198 },
498 { "M-G", KEY_IS_CHAR, 199 },
499 { "M-H", KEY_IS_CHAR, 200 },
500 { "M-I", KEY_IS_CHAR, 201 },
501 { "M-J", KEY_IS_CHAR, 202 },
502 { "M-K", KEY_IS_CHAR, 203 },
503 { "M-L", KEY_IS_CHAR, 204 },
504 { "M-M", KEY_IS_CHAR, 205 },
505 { "M-N", KEY_IS_CHAR, 206 },
506 { "M-O", KEY_IS_CHAR, 207 },
507 { "M-P", KEY_IS_CHAR, 208 },
508 { "M-Q", KEY_IS_CHAR, 209 },
509 { "M-R", KEY_IS_CHAR, 210 },
510 { "M-S", KEY_IS_CHAR, 211 },
511 { "M-T", KEY_IS_CHAR, 212 },
512 { "M-U", KEY_IS_CHAR, 213 },
513 { "M-V", KEY_IS_CHAR, 214 },
514 { "M-W", KEY_IS_CHAR, 215 },
515 { "M-X", KEY_IS_CHAR, 216 },
516 { "M-Y", KEY_IS_CHAR, 217 },
517 { "M-Z", KEY_IS_CHAR, 218 },
518 { "M-[", KEY_IS_CHAR, 219 },
519 { "M-\"", KEY_IS_CHAR, 162 },
520 { "M-\\", KEY_IS_CHAR, 220 },
521 { "M-]", KEY_IS_CHAR, 221 },
522 { "M-^", KEY_IS_CHAR, 222 },
523 { "M-^?", KEY_IS_CHAR, 255 },
524 { "M-^@", KEY_IS_CHAR, 128 },
525 { "M-^A", KEY_IS_CHAR, 129 },
526 { "M-^B", KEY_IS_CHAR, 130 },
527 { "M-^C", KEY_IS_CHAR, 131 },
528 { "M-^D", KEY_IS_CHAR, 132 },
529 { "M-^E", KEY_IS_CHAR, 133 },
530 { "M-^F", KEY_IS_CHAR, 134 },
531 { "M-^G", KEY_IS_CHAR, 135 },
532 { "M-^H", KEY_IS_CHAR, 136 },
533 { "M-^I", KEY_IS_CHAR, 137 },
534 { "M-^J", KEY_IS_CHAR, 138 },
535 { "M-^K", KEY_IS_CHAR, 139 },
536 { "M-^L", KEY_IS_CHAR, 140 },
537 { "M-^M", KEY_IS_CHAR, 141 },
538 { "M-^N", KEY_IS_CHAR, 142 },
539 { "M-^O", KEY_IS_CHAR, 143 },
540 { "M-^P", KEY_IS_CHAR, 144 },
541 { "M-^Q", KEY_IS_CHAR, 145 },
542 { "M-^R", KEY_IS_CHAR, 146 },
543 { "M-^S", KEY_IS_CHAR, 147 },
544 { "M-^T", KEY_IS_CHAR, 148 },
545 { "M-^U", KEY_IS_CHAR, 149 },
546 { "M-^V", KEY_IS_CHAR, 150 },
547 { "M-^W", KEY_IS_CHAR, 151 },
548 { "M-^X", KEY_IS_CHAR, 152 },
549 { "M-^Y", KEY_IS_CHAR, 153 },
550 { "M-^Z", KEY_IS_CHAR, 154 },
551 { "M-^[", KEY_IS_CHAR, 155 },
552 { "M-^\\", KEY_IS_CHAR, 156 },
553 { "M-^]", KEY_IS_CHAR, 157 },
554 { "M-^^", KEY_IS_CHAR, 158 },
555 { "M-^_", KEY_IS_CHAR, 159 },
556 { "M-_", KEY_IS_CHAR, 223 },
557 { "M-`", KEY_IS_CHAR, 224 },
558 { "M-a", KEY_IS_CHAR, 225 },
559 { "M-b", KEY_IS_CHAR, 226 },
560 { "M-c", KEY_IS_CHAR, 227 },
561 { "M-d", KEY_IS_CHAR, 228 },
562 { "M-e", KEY_IS_CHAR, 229 },
563 { "M-f", KEY_IS_CHAR, 230 },
564 { "M-g", KEY_IS_CHAR, 231 },
565 { "M-h", KEY_IS_CHAR, 232 },
566 { "M-i", KEY_IS_CHAR, 233 },
567 { "M-j", KEY_IS_CHAR, 234 },
568 { "M-k", KEY_IS_CHAR, 235 },
569 { "M-l", KEY_IS_CHAR, 236 },
570 { "M-m", KEY_IS_CHAR, 237 },
571 { "M-n", KEY_IS_CHAR, 238 },
572 { "M-o", KEY_IS_CHAR, 239 },
573 { "M-p", KEY_IS_CHAR, 240 },
574 { "M-q", KEY_IS_CHAR, 241 },
575 { "M-r", KEY_IS_CHAR, 242 },
576 { "M-s", KEY_IS_CHAR, 243 },
577 { "M-t", KEY_IS_CHAR, 244 },
578 { "M-u", KEY_IS_CHAR, 245 },
579 { "M-v", KEY_IS_CHAR, 246 },
580 { "M-w", KEY_IS_CHAR, 247 },
581 { "M-x", KEY_IS_CHAR, 248 },
582 { "M-y", KEY_IS_CHAR, 249 },
583 { "M-z", KEY_IS_CHAR, 250 },
584 { "M-{", KEY_IS_CHAR, 251 },
585 { "M-|", KEY_IS_CHAR, 252 },
586 { "M-}", KEY_IS_CHAR, 253 },
587 { "M-~", KEY_IS_CHAR, 254 },
588 { "N", KEY_IS_CHAR, 78 },
589 { "O", KEY_IS_CHAR, 79 },
590 { "P", KEY_IS_CHAR, 80 },
591 { "Q", KEY_IS_CHAR, 81 },
592 { "R", KEY_IS_CHAR, 82 },
593 { "S", KEY_IS_CHAR, 83 },
594 { "S-begin", KEY_SBEG, 0 },
595 { "S-cancel", KEY_SCANCEL, 0 },
596 { "S-command", KEY_SCOMMAND, 0 },
597 { "S-copy", KEY_SCOPY, 0 },
598 { "S-create", KEY_SCREATE, 0 },
599 { "S-del_line", KEY_SDL, 0 },
600 { "S-delete", KEY_SDC, 0 },
601 { "S-eol", KEY_SEOL, 0 },
602 { "S-exit", KEY_SEXIT, 0 },
603 { "S-find", KEY_SFIND, 0 },
604 { "S-help", KEY_SHELP, 0 },
605 { "S-home", KEY_SHOME, 0 },
606 { "S-insert", KEY_SIC, 0 },
607 { "S-left", KEY_SLEFT, 0 },
608 { "S-message", KEY_SMESSAGE, 0 },
609 { "S-move", KEY_SMOVE, 0 },
610 { "S-next", KEY_SNEXT, 0 },
611 { "S-options", KEY_SOPTIONS, 0 },
612 { "S-previous", KEY_SPREVIOUS, 0 },
613 { "S-print", KEY_SPRINT, 0 },
614 { "S-redo", KEY_SREDO, 0 },
615 { "S-replace", KEY_SREPLACE, 0 },
616 { "S-resume", KEY_SRSUME, 0 },
617 { "S-right", KEY_SRIGHT, 0 },
618 { "S-save", KEY_SSAVE, 0 },
619 { "S-suspend", KEY_SSUSPEND, 0 },
620 { "S-undo", KEY_SUNDO, 0 },
621 { "T", KEY_IS_CHAR, 84 },
622 { "U", KEY_IS_CHAR, 85 },
623 { "V", KEY_IS_CHAR, 86 },
624 { "W", KEY_IS_CHAR, 87 },
625 { "X", KEY_IS_CHAR, 88 },
626 { "Y", KEY_IS_CHAR, 89 },
627 { "Z", KEY_IS_CHAR, 90 },
628 { "[", KEY_IS_CHAR, 91 },
629 { "\"", KEY_IS_CHAR, 34 },
630 { "\\", KEY_IS_CHAR, 92 },
631 { "]", KEY_IS_CHAR, 93 },
632 { "^", KEY_IS_CHAR, 94 },
633 { "^?", KEY_IS_CHAR, 127 },
634 { "^A", KEY_IS_CHAR, 1 },
635 { "^B", KEY_IS_CHAR, 2 },
636 { "^C", KEY_IS_CHAR, 3 },
637 { "^D", KEY_IS_CHAR, 4 },
638 { "^E", KEY_IS_CHAR, 5 },
639 { "^F", KEY_IS_CHAR, 6 },
640 { "^G", KEY_IS_CHAR, 7 },
641 { "^H", KEY_IS_CHAR, 8 },
642 { "^K", KEY_IS_CHAR, 11 },
643 { "^L", KEY_IS_CHAR, 12 },
644 { "^M", KEY_IS_CHAR, 13 },
645 { "^N", KEY_IS_CHAR, 14 },
646 { "^O", KEY_IS_CHAR, 15 },
647 { "^P", KEY_IS_CHAR, 16 },
648 { "^Q", KEY_IS_CHAR, 17 },
649 { "^R", KEY_IS_CHAR, 18 },
650 { "^S", KEY_IS_CHAR, 19 },
651 { "^T", KEY_IS_CHAR, 20 },
652 { "^U", KEY_IS_CHAR, 21 },
653 { "^V", KEY_IS_CHAR, 22 },
654 { "^W", KEY_IS_CHAR, 23 },
655 { "^X", KEY_IS_CHAR, 24 },
656 { "^Y", KEY_IS_CHAR, 25 },
657 { "^Z", KEY_IS_CHAR, 26 },
658 { "^\\", KEY_IS_CHAR, 28 },
659 { "^]", KEY_IS_CHAR, 29 },
660 { "^^", KEY_IS_CHAR, 30 },
661 { "^_", KEY_IS_CHAR, 31 },
662 { "_", KEY_IS_CHAR, 95 },
663 { "`", KEY_IS_CHAR, 96 },
664 { "a", KEY_IS_CHAR, 97 },
665 { "b", KEY_IS_CHAR, 98 },
666 { "back_tab", KEY_BTAB, 0 },
667 { "backspace", KEY_BACKSPACE, 0 },
668 { "begin", KEY_BEG, 0 },
669 { "c", KEY_IS_CHAR, 99 },
670 { "cancel", KEY_CANCEL, 0 },
671 { "clear", KEY_CLEAR, 0 },
672 { "clear_all_tabs", KEY_CATAB, 0 },
673 { "clear_tab", KEY_CTAB, 0 },
674 { "close", KEY_CLOSE, 0 },
675 { "command", KEY_COMMAND, 0 },
676 { "copy", KEY_COPY, 0 },
677 { "create", KEY_CREATE, 0 },
678 { "d", KEY_IS_CHAR, 100 },
679 { "del_line", KEY_DL, 0 },
680 { "delete", KEY_DC, 0 },
681 { "down", KEY_DOWN, 0 },
682 { "e", KEY_IS_CHAR, 101 },
683 { "eic", KEY_EIC, 0 },
684 { "end", KEY_END, 0 },
685 { "enter", KEY_IS_CHAR, 10 },
686 { "eol", KEY_EOL, 0 },
687 { "eos", KEY_EOS, 0 },
688 { "exit", KEY_EXIT, 0 },
689 { "f", KEY_IS_CHAR, 102 },
690 { "find", KEY_FIND, 0 },
691 { "g", KEY_IS_CHAR, 103 },
692 { "h", KEY_IS_CHAR, 104 },
693 { "help", KEY_HELP, 0 },
694 { "home", KEY_HOME, 0 },
695 { "i", KEY_IS_CHAR, 105 },
696 { "ins_line", KEY_IL, 0 },
697 { "insert", KEY_IC, 0 },
698 { "j", KEY_IS_CHAR, 106 },
699 { "k", KEY_IS_CHAR, 107 },
700 { "l", KEY_IS_CHAR, 108 },
701 { "left", KEY_LEFT, 0 },
702 { "lower_left", KEY_LL, 0 },
703 { "m", KEY_IS_CHAR, 109 },
704 { "mark", KEY_MARK, 0 },
705 { "message", KEY_MESSAGE, 0 },
706 { "move", KEY_MOVE, 0 },
707 { "n", KEY_IS_CHAR, 110 },
708 { "next", KEY_NEXT, 0 },
709 { "o", KEY_IS_CHAR, 111 },
710 { "open", KEY_OPEN, 0 },
711 { "options", KEY_OPTIONS, 0 },
712 { "p", KEY_IS_CHAR, 112 },
713 { "page_down", KEY_NPAGE, 0 },
714 { "page_up", KEY_PPAGE, 0 },
715 { "previous", KEY_PREVIOUS, 0 },
716 { "print", KEY_PRINT, 0 },
717 { "q", KEY_IS_CHAR, 113 },
718 { "r", KEY_IS_CHAR, 114 },
719 { "redo", KEY_REDO, 0 },
720 { "reference", KEY_REFERENCE, 0 },
721 { "refresh", KEY_REFRESH, 0 },
722 { "replace", KEY_REPLACE, 0 },
723 { "restart", KEY_RESTART, 0 },
724 { "resume", KEY_RESUME, 0 },
725 { "right", KEY_RIGHT, 0 },
726 { "s", KEY_IS_CHAR, 115 },
727 { "save", KEY_SAVE, 0 },
728 { "scroll_b", KEY_SR, 0 },
729 { "scroll_f", KEY_SF, 0 },
730 { "select", KEY_SELECT, 0 },
731 { "send", KEY_SEND, 0 },
732 { "set_tab", KEY_STAB, 0 },
733 { "space", KEY_IS_CHAR, 32 },
734 { "suspend", KEY_SUSPEND, 0 },
735 { "t", KEY_IS_CHAR, 116 },
736 { "tab", KEY_IS_CHAR, 9 },
737 { "u", KEY_IS_CHAR, 117 },
738 { "undo", KEY_UNDO, 0 },
739 { "up", KEY_UP, 0 },
740 { "v", KEY_IS_CHAR, 118 },
741 { "w", KEY_IS_CHAR, 119 },
742 { "x", KEY_IS_CHAR, 120 },
743 { "y", KEY_IS_CHAR, 121 },
744 { "z", KEY_IS_CHAR, 122 },
745 { "{", KEY_IS_CHAR, 123 },
746 { "|", KEY_IS_CHAR, 124 },
747 { "}", KEY_IS_CHAR, 125 },
748 { "~", KEY_IS_CHAR, 126 },
749 { NULL, 0, 0 }
751 /* }}} */
753 /* default bindings {{{ */
754 static const struct {
755 enum context context;
756 const char *key;
757 const char *func;
758 } defaults[] = {
759 { CTX_COMMON, "F1", "help" },
760 { CTX_COMMON, "b", "next" },
761 { CTX_COMMON, "c", "pause" },
762 { CTX_COMMON, "x", "play" },
763 { CTX_COMMON, "z", "prev" },
764 { CTX_COMMON, "Q", "quit" },
765 { CTX_COMMON, "n", "search_next" },
766 { CTX_COMMON, "N", "search_prev" },
767 { CTX_COMMON, "h", "seek_backward" },
768 { CTX_COMMON, "l", "seek_forward" },
769 { CTX_COMMON, "left", "seek_backward" },
770 { CTX_COMMON, "right", "seek_forward" },
771 { CTX_COMMON, "v", "stop" },
772 { CTX_COMMON, "C", "toggle_continue" },
773 { CTX_COMMON, "p", "toggle_play_mode" },
774 { CTX_COMMON, "m", "toggle_playlist_mode" },
775 { CTX_COMMON, "t", "toggle_remaining_time" },
776 { CTX_COMMON, "r", "toggle_repeat" },
777 { CTX_COMMON, "1", "view_1" },
778 { CTX_COMMON, "2", "view_2" },
779 { CTX_COMMON, "3", "view_3" },
780 { CTX_COMMON, "4", "view_4" },
781 { CTX_COMMON, "5", "view_5" },
782 { CTX_COMMON, "6", "view_6" },
783 { CTX_COMMON, "-", "vol_down" },
784 { CTX_COMMON, "{", "vol_left_down" },
785 { CTX_COMMON, "[", "vol_left_up" },
786 { CTX_COMMON, "}", "vol_right_down" },
787 { CTX_COMMON, "]", "vol_right_up" },
788 { CTX_COMMON, "=", "vol_up" },
789 { CTX_COMMON, "+", "vol_up" },
790 { CTX_COMMON, "tab", "win_activate_next" },
791 { CTX_COMMON, "G", "win_bottom" },
792 { CTX_COMMON, "j", "win_down" },
793 { CTX_COMMON, "^F", "win_page_down" },
794 { CTX_COMMON, "^B", "win_page_up" },
795 { CTX_COMMON, "g", "win_top" },
796 { CTX_COMMON, "k", "win_up" },
797 { CTX_COMMON, "end", "win_bottom" },
798 { CTX_COMMON, "down", "win_down" },
799 { CTX_COMMON, "page_down", "win_page_down" },
800 { CTX_COMMON, "page_up", "win_page_up" },
801 { CTX_COMMON, "home", "win_top" },
802 { CTX_COMMON, "up", "win_up" },
804 { CTX_PLAYLIST, "space", "expand_artist" },
805 { CTX_PLAYLIST, "enter", "play_selected" },
806 { CTX_PLAYLIST, "e", "queue_append" },
807 { CTX_PLAYLIST, "E", "queue_prepend" },
808 { CTX_PLAYLIST, "D", "remove" },
809 { CTX_PLAYLIST, "delete", "remove" },
810 { CTX_PLAYLIST, "i", "select_current" },
811 { CTX_PLAYLIST, "u", "update" },
813 { CTX_PLAY_QUEUE, "D", "remove" },
814 { CTX_PLAY_QUEUE, "delete", "remove" },
816 { CTX_BROWSER, "a", "add" },
817 { CTX_BROWSER, "backspace", "cd_parent" },
818 { CTX_BROWSER, "enter", "enter" },
819 { CTX_BROWSER, "e", "queue_append" },
820 { CTX_BROWSER, "E", "queue_prepend" },
821 { CTX_BROWSER, "u", "reload" },
822 { CTX_BROWSER, "D", "remove" },
823 { CTX_BROWSER, "delete", "remove" },
824 { CTX_BROWSER, "i", "toggle_show_hidden" },
826 { CTX_FILTERS, "enter", "activate" },
827 { CTX_FILTERS, "D", "delete_filter" },
828 { CTX_FILTERS, "delete", "delete_filter" },
829 { CTX_FILTERS, "space", "toggle_filter" },
830 { -1, NULL, NULL }
832 /* }}} */
834 static void set_defaults(void)
836 int i;
838 d_print("setting default keybindings\n");
839 for (i = 0; defaults[i].key; i++)
840 key_bind(context_names[defaults[i].context], defaults[i].key, defaults[i].func);
845 * TODO: tables are sorted, do binary search
848 static int find_context(const char *name)
850 int i;
852 for (i = 0; i < NR_CTXS; i++) {
853 if (strcmp(name, context_names[i]) == 0)
854 return i;
856 if (initializing) {
857 fprintf(stderr, "invalid context '%s'\n", name);
858 } else {
859 ui_curses_display_error_msg("invalid context '%s'", name);
861 return -1;
864 static const struct key *find_key(const char *name)
866 int i;
868 for (i = 0; keys[i].name; i++) {
869 if (strcmp(name, keys[i].name) == 0)
870 return &keys[i];
872 if (initializing) {
873 fprintf(stderr, "invalid key '%s'\n", name);
874 } else {
875 ui_curses_display_error_msg("invalid key '%s'", name);
877 return NULL;
880 static const struct function *find_function(const char *name, enum context c)
882 const struct function *functions = all_functions[c];
883 int i;
885 for (i = 0; functions[i].name; i++) {
886 if (strcmp(name, functions[i].name) == 0)
887 return &functions[i];
889 if (initializing) {
890 fprintf(stderr, "function '%s' not found in context %s\n", name, context_names[c]);
891 } else {
892 ui_curses_display_error_msg("function '%s' not in context %s", name, context_names[c]);
894 return NULL;
897 static struct binding *find_binding(enum context c, const struct key *k)
899 struct binding *b = bindings[c];
901 while (b) {
902 if (b->key == k)
903 break;
904 b = b->next;
906 return b;
909 int key_bind(const char *context, const char *key, const char *func)
911 int c;
912 const struct key *k;
913 const struct function *f;
914 struct binding *b;
916 c = find_context(context);
917 if (c < 0)
918 return -1;
920 k = find_key(key);
921 if (k == NULL)
922 return -1;
924 f = find_function(func, c);
925 if (f == NULL)
926 return -1;
928 /* check if already bound */
929 b = find_binding(c, k);
930 if (b)
931 goto bound;
932 if (context != CTX_COMMON) {
933 /* always search CTX_COMMON because same key can't be bound
934 * in CTX_COMMON _and_ any other context at the same time
936 b = find_binding(CTX_COMMON, k);
937 if (b)
938 goto bound;
941 b = xnew(struct binding, 1);
942 b->key = k;
943 b->func = f;
945 b->next = bindings[c];
946 bindings[c] = b;
948 d_print("bound %s in %s to %s\n", key, context, func);
949 return 0;
950 bound:
951 if (initializing) {
952 fprintf(stderr, "key %s already bound in context %s\n", key, context);
953 } else {
954 ui_curses_display_error_msg("key %s already bound in context %s", key, context);
956 return -1;
959 int key_unbind(const char *context, const char *key)
961 enum context c;
962 const struct key *k;
963 struct binding *b, *prev;
965 c = find_context(context);
966 if (c < 0)
967 return -1;
969 k = find_key(key);
970 if (k == NULL)
971 return -1;
973 prev = NULL;
974 b = bindings[c];
975 while (b) {
976 if (b->key == k) {
977 if (prev) {
978 prev->next = b->next;
979 } else {
980 bindings[c] = b->next;
982 free(b);
983 return 0;
985 prev = b;
986 b = b->next;
988 ui_curses_display_error_msg("key %s not bound in context %s", key, context);
989 return -1;
992 static char *find_space(char *str)
994 while (*str) {
995 if (isspace(*str))
996 return str;
997 str++;
999 return NULL;
1002 static int parse_words(char *s, char **words, int nr)
1004 int i = 0;
1006 while (1) {
1007 while (isspace(*s))
1008 s++;
1009 if (*s == 0)
1010 break;
1011 if (i == nr)
1012 return -1;
1013 words[i++] = s;
1015 s = find_space(s);
1016 if (s == NULL)
1017 break;
1018 *s++ = 0;
1020 if (i != nr)
1021 return -1;
1022 return 0;
1025 static void handle_line(void *data, const char *line)
1027 char *words[3];
1029 if (parse_words((char *)line, words, 3))
1030 return;
1032 key_bind(words[0], words[1], words[2]);
1033 return;
1036 static int load_keys(void)
1038 filename = xstrjoin(cmus_config_dir, "/keybindings");
1039 if (file_load(filename, handle_line, NULL) && errno != ENOENT) {
1040 fprintf(stderr, "error: opening file %s: %s\n", filename, strerror(errno));
1041 return -1;
1043 return 0;
1046 int keys_init(void)
1048 int i;
1050 if (load_keys())
1051 return -1;
1053 /* set default keys only if no bingings */
1054 for (i = 0; i < NR_CTXS; i++) {
1055 if (bindings[i]) {
1056 d_print("user defined keys\n");
1057 initializing = 0;
1058 return 0;
1061 set_defaults();
1062 initializing = 0;
1063 return 0;
1066 void keys_exit(void)
1068 FILE *f;
1069 int i;
1071 f = fopen(filename, "w");
1072 if (f == NULL) {
1073 fprintf(stderr, "error: creating %s: %s\n", filename, strerror(errno));
1074 return;
1076 for (i = 0; i < NR_CTXS; i++) {
1077 struct binding *b = bindings[i];
1078 const char *name = context_names[i];
1080 while (b) {
1081 fprintf(f, "%s %-25s %s\n", name, b->key->name, b->func->name);
1082 b = b->next;
1085 fclose(f);
1088 static int handle_key(const struct binding *b, const struct key *k)
1090 while (b) {
1091 if (b->key == k) {
1092 b->func->func();
1093 return 1;
1095 b = b->next;
1097 return 0;
1100 static const struct key *ch_to_key(uchar ch)
1102 int i;
1104 for (i = 0; keys[i].name; i++) {
1105 if (keys[i].key == KEY_IS_CHAR && keys[i].ch == ch)
1106 return &keys[i];
1108 return NULL;
1111 static const struct key *keycode_to_key(int key)
1113 int i;
1115 for (i = 0; keys[i].name; i++) {
1116 if (keys[i].key != KEY_IS_CHAR && keys[i].key == key)
1117 return &keys[i];
1119 return NULL;
1122 void normal_mode_ch(uchar ch)
1124 enum context c;
1125 const struct key *k;
1127 /* you can't redefine these keys */
1128 switch (ch) {
1129 case ':':
1130 ui_curses_command_mode();
1131 return;
1132 case '/':
1133 ui_curses_search_mode();
1134 return;
1135 case '?':
1136 ui_curses_search_backward_mode();
1137 return;
1140 c = view_to_context[ui_curses_view];
1141 k = ch_to_key(ch);
1143 if (k == NULL) {
1144 return;
1147 /* view-specific ch */
1148 if (handle_key(bindings[c], k)) {
1149 ui_curses_update_view();
1150 return;
1153 /* common ch */
1154 if (!handle_key(bindings[0], k))
1155 d_print("key %s not bound in context %s or common\n", k->name, context_names[c]);
1158 void normal_mode_key(int key)
1160 enum context c = view_to_context[ui_curses_view];
1161 const struct key *k = keycode_to_key(key);
1163 if (k == NULL) {
1164 return;
1167 /* view-specific key */
1168 if (handle_key(bindings[c], k)) {
1169 ui_curses_update_view();
1170 return;
1173 /* common key */
1174 if (!handle_key(bindings[0], k))
1175 d_print("key %s not bound in context %s or common\n", k->name, context_names[c]);