1 /*
2 * $Id$
3 *
4 * (c) 2004 by Kalle Wallin <kaw@linux.se>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
21 #include "command.h"
22 #include "config.h"
23 #include "ncmpc.h"
24 #include "mpdclient.h"
25 #include "screen.h"
27 #include <ctype.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <glib.h>
32 #include <signal.h>
34 #undef DEBUG_KEYS
36 #ifdef DEBUG_KEYS
37 #define DK(x) x
38 #else
39 #define DK(x)
40 #endif
42 #define BS KEY_BACKSPACE
43 #define DEL KEY_DC
44 #define UP KEY_UP
45 #define DWN KEY_DOWN
46 #define LEFT KEY_LEFT
47 #define RGHT KEY_RIGHT
48 #define HOME KEY_HOME
49 #define END KEY_END
50 #define PGDN KEY_NPAGE
51 #define PGUP KEY_PPAGE
52 #define TAB 0x09
53 #define STAB 0x161
54 #define ESC 0x1B
55 #define F1 KEY_F(1)
56 #define F2 KEY_F(2)
57 #define F3 KEY_F(3)
58 #define F4 KEY_F(4)
59 #define F5 KEY_F(5)
60 #define F6 KEY_F(6)
61 #define F7 KEY_F(7)
64 static command_definition_t cmds[] =
65 {
66 #ifdef ENABLE_KEYDEF_SCREEN
67 { {'K', 0, 0 }, 0, CMD_SCREEN_KEYDEF, "screen-keyedit",
68 N_("Key configuration screen") },
69 #endif
70 { { 'q', 'Q', 3 }, 0, CMD_QUIT, "quit",
71 N_("Quit") },
73 /* movment */
74 { { UP, 'k', 0 }, 0, CMD_LIST_PREVIOUS, "up",
75 N_("Move cursor up") },
76 { { DWN, 'j', 0 }, 0, CMD_LIST_NEXT, "down",
77 N_("Move cursor down") },
78 { { HOME, 0x01, 0 }, 0, CMD_LIST_FIRST, "home",
79 N_("Home ") },
80 { { END, 0x05, 0 }, 0, CMD_LIST_LAST, "end",
81 N_("End ") },
82 { { PGUP, 0, 0 }, 0, CMD_LIST_PREVIOUS_PAGE, "pgup",
83 N_("Page up") },
84 { { PGDN, 0, 0 }, 0, CMD_LIST_NEXT_PAGE, "pgdn",
85 N_("Page down") },
88 /* basic screens */
89 { { '1', F1, 'h' }, 0, CMD_SCREEN_HELP, "screen-help",
90 N_("Help screen") },
91 { { '2', F2, 0 }, 0, CMD_SCREEN_PLAY, "screen-playlist",
92 N_("Playlist screen") },
93 { { '3', F3, 0 }, 0, CMD_SCREEN_FILE, "screen-browse",
94 N_("Browse screen") },
97 /* player commands */
98 { { 13, 0, 0 }, 0, CMD_PLAY, "play",
99 N_("Play/Enter directory") },
100 { { 'P', 0, 0 }, 0, CMD_PAUSE,"pause",
101 N_("Pause") },
102 { { 's', BS, 0 }, 0, CMD_STOP, "stop",
103 N_("Stop") },
104 { { '>', 0, 0 }, 0, CMD_TRACK_NEXT, "next",
105 N_("Next track") },
106 { { '<', 0, 0 }, 0, CMD_TRACK_PREVIOUS, "prev",
107 N_("Previous track") },
108 { { 'f', 0, 0 }, 0, CMD_SEEK_FORWARD, "seek-forward",
109 N_("Seek forward") },
110 { { 'b', 0, 0 }, 0, CMD_SEEK_BACKWARD, "seek-backward",
111 N_("Seek backward") },
112 { { '+', RGHT, 0 }, 0, CMD_VOLUME_UP, "volume-up",
113 N_("Increase volume") },
114 { { '-', LEFT, 0 }, 0, CMD_VOLUME_DOWN, "volume-down",
115 N_("Decrease volume") },
116 { { ' ', 0, 0 }, 0, CMD_SELECT, "select",
117 N_("Select/deselect song in playlist") },
118 { { 't', 0, 0 }, 0, CMD_SELECT_ALL, "select_all",
119 N_("Select all listed items") },
120 { { DEL, 'd', 0 }, 0, CMD_DELETE, "delete",
121 N_("Delete song from playlist") },
122 { { 'Z', 0, 0 }, 0, CMD_SHUFFLE, "shuffle",
123 N_("Shuffle playlist") },
124 { { 'c', 0, 0 }, 0, CMD_CLEAR, "clear",
125 N_("Clear playlist") },
126 { { 'r', 0, 0 }, 0, CMD_REPEAT, "repeat",
127 N_("Toggle repeat mode") },
128 { { 'z', 0, 0 }, 0, CMD_RANDOM, "random",
129 N_("Toggle random mode") },
130 { { 'x', 0, 0 }, 0, CMD_CROSSFADE, "crossfade",
131 N_("Toggle crossfade mode") },
132 { { 21, 0, 0 }, 0, CMD_DB_UPDATE, "db-update",
133 N_("Start a music database update") },
134 { { 'S', 0, 0 }, 0, CMD_SAVE_PLAYLIST, "save",
135 N_("Save playlist") },
136 { { 'a', 0, 0 }, 0, CMD_ADD, "add",
137 N_("Add url/file to playlist") },
139 { { '!', 0, 0 }, 0, CMD_GO_ROOT_DIRECTORY, "go-root-directory",
140 N_("Go to root directory") },
141 { { '"', 0, 0 }, 0, CMD_GO_PARENT_DIRECTORY, "go-parent-directory",
142 N_("Go to parent directory") },
144 /* lists */
145 { { 11, 0, 0 }, 0, CMD_LIST_MOVE_UP, "move-up",
146 N_("Move item up") },
147 { { 10, 0, 0 }, 0, CMD_LIST_MOVE_DOWN, "move-down",
148 N_("Move item down") },
149 { { 12, 0, 0 }, 0, CMD_SCREEN_UPDATE, "update",
150 N_("Update screen") },
153 /* ncmpc options */
154 { { 'w', 0, 0 }, 0, CMD_TOGGLE_FIND_WRAP, "wrap-mode",
155 N_("Toggle find mode") },
156 { { 'U', 0, 0 }, 0, CMD_TOGGLE_AUTOCENTER, "autocenter-mode",
157 N_("Toggle auto center mode") },
160 /* change screen */
161 { { TAB, 0, 0 }, 0, CMD_SCREEN_NEXT, "screen-next",
162 N_("Next screen") },
163 { { STAB, 0, 0 }, 0, CMD_SCREEN_PREVIOUS, "screen-prev",
164 N_("Previous screen") },
167 /* find */
168 { { '/', 0, 0 }, 0, CMD_LIST_FIND, "find",
169 N_("Forward find") },
170 { { 'n', 0, 0 }, 0, CMD_LIST_FIND_NEXT, "find-next",
171 N_("Forward find next") },
172 { { '?', 0, 0 }, 0, CMD_LIST_RFIND, "rfind",
173 N_("Backward find") },
174 { { 'p', 0, 0 }, 0, CMD_LIST_RFIND_NEXT, "rfind-next",
175 N_("Backward find previous") },
178 /* extra screens */
179 #ifdef ENABLE_ARTIST_SCREEN
180 { {'4', F4, 0 }, 0, CMD_SCREEN_ARTIST, "screen-artist",
181 N_("Artist screen") },
182 #endif
183 #ifdef ENABLE_SEARCH_SCREEN
184 { {'5', F5, 0 }, 0, CMD_SCREEN_SEARCH, "screen-search",
185 N_("Search screen") },
186 { {'m', 0, 0 }, 0, CMD_SEARCH_MODE, "search-mode",
187 N_("Change search mode") },
188 #endif
190 #ifdef ENABLE_CLOCK_SCREEN
191 { {'6', F6, 0 }, 0, CMD_SCREEN_CLOCK, "screen-clock",
192 N_("Clock screen") },
193 #endif
194 #ifdef ENABLE_LYRICS_SCREEN
195 { {'7', F7, 0 }, 0, CMD_SCREEN_LYRICS, "screen-lyrics",
196 N_("Lyrics screen") },
197 { {ESC, 0, 0 }, 0, CMD_INTERRUPT, "lyrics-interrupt",
198 N_("Interrupt action") },
199 { {'u', 0, 0 }, 0, CMD_LYRICS_UPDATE, "lyrics-update",
200 N_("Update Lyrics") },
201 #endif
204 { { -1, -1, -1 }, 0, CMD_NONE, NULL, NULL }
205 };
207 command_definition_t *
208 get_command_definitions(void)
209 {
210 return cmds;
211 }
213 const char *
214 key2str(int key)
215 {
216 static char buf[32];
217 int i;
219 buf[0] = 0;
220 switch(key)
221 {
222 case 0:
223 return _("Undefined");
224 case ' ':
225 return _("Space");
226 case 13:
227 return _("Enter");
228 case BS:
229 return _("Backspace");
230 case DEL:
231 return _("Delete");
232 case UP:
233 return _("Up");
234 case DWN:
235 return _("Down");
236 case LEFT:
237 return _("Left");
238 case RGHT:
239 return _("Right");
240 case HOME:
241 return _("Home");
242 case END:
243 return _("End");
244 case PGDN:
245 return _("PageDown");
246 case PGUP:
247 return _("PageUp");
248 case TAB:
249 return _("Tab");
250 case STAB:
251 return _("Shift+Tab");
252 case ESC:
253 return _("Esc");
254 case KEY_IC:
255 return _("Insert");
256 default:
257 for(i=0; i<=63; i++)
258 if( key==KEY_F(i) )
259 {
260 g_snprintf(buf, 32, "F%d", i );
261 return buf;
262 }
263 if( !(key & ~037) )
264 g_snprintf(buf, 32, "Ctrl-%c", 'A'+(key & 037)-1 );
265 else if( (key & ~037) == 224 )
266 g_snprintf(buf, 32, "Alt-%c", 'A'+(key & 037)-1 );
267 else if( key>32 && key<256 )
268 g_snprintf(buf, 32, "%c", key);
269 else
270 g_snprintf(buf, 32, "0x%03X", key);
271 }
272 return buf;
273 }
275 void
276 command_dump_keys(void)
277 {
278 int i;
280 i=0;
281 while( cmds[i].description )
282 {
283 if( cmds[i].command != CMD_NONE )
284 printf(" %20s : %s\n", get_key_names(cmds[i].command,1),cmds[i].name);
285 i++;
286 }
287 }
289 static int
290 set_key_flags(command_definition_t *cp, command_t command, int flags)
291 {
292 int i;
294 i=0;
295 while( cp[i].name )
296 {
297 if( cp[i].command == command )
298 {
299 cp[i].flags |= flags;
300 return 0;
301 }
302 i++;
303 }
304 return 1;
305 }
307 const char *
308 get_key_names(command_t command, int all)
309 {
310 int i;
312 i=0;
313 while( cmds[i].description )
314 {
315 if( cmds[i].command == command )
316 {
317 int j;
318 static char keystr[80];
320 g_strlcpy(keystr, key2str(cmds[i].keys[0]), sizeof(keystr));
321 if( !all )
322 return keystr;
323 j=1;
324 while( j<MAX_COMMAND_KEYS && cmds[i].keys[j]>0 )
325 {
326 g_strlcat(keystr, " ", sizeof(keystr));
327 g_strlcat(keystr, key2str(cmds[i].keys[j]), sizeof(keystr));
328 j++;
329 }
330 return keystr;
331 }
332 i++;
333 }
334 return NULL;
335 }
337 const char *
338 get_key_description(command_t command)
339 {
340 int i;
342 i=0;
343 while( cmds[i].description )
344 {
345 if( cmds[i].command == command )
346 return _(cmds[i].description);
347 i++;
348 }
349 return NULL;
350 }
352 const char *
353 get_key_command_name(command_t command)
354 {
355 int i;
357 i=0;
358 while( cmds[i].name )
359 {
360 if( cmds[i].command == command )
361 return cmds[i].name;
362 i++;
363 }
364 return NULL;
365 }
367 command_t
368 get_key_command_from_name(char *name)
369 {
370 int i;
372 i=0;
373 while( cmds[i].name )
374 {
375 if( strcmp(name, cmds[i].name) == 0 )
376 return cmds[i].command;
377 i++;
378 }
379 return CMD_NONE;
380 }
383 command_t
384 find_key_command(int key, command_definition_t *cmds)
385 {
386 int i;
388 i=0;
389 while (key && cmds && cmds[i].name) {
390 if (cmds[i].keys[0] == key ||
391 cmds[i].keys[1] == key ||
392 cmds[i].keys[2] == key)
393 return cmds[i].command;
394 i++;
395 }
397 return CMD_NONE;
398 }
400 command_t
401 get_key_command(int key)
402 {
403 return find_key_command(key, cmds);
404 }
406 int
407 my_wgetch(WINDOW *w)
408 {
409 int c;
411 c = wgetch(w);
413 /* handle resize event */
414 if( c==KEY_RESIZE )
415 screen_resize();
417 #ifdef ENABLE_RAW_MODE
418 /* handle SIGSTOP (Ctrl-Z) */
419 if( c==26 || c==407 )
420 sigstop();
421 /* handle SIGINT (Ctrl-C) */
422 if( c==3 )
423 exit(EXIT_SUCCESS);
424 #endif
426 return c;
427 }
429 command_t
430 get_keyboard_command_with_timeout(int ms)
431 {
432 int key;
434 if( ms != SCREEN_TIMEOUT)
435 timeout(ms);
436 key = my_wgetch(stdscr);
437 if( ms != SCREEN_TIMEOUT)
438 timeout(SCREEN_TIMEOUT);
440 if( key==ERR )
441 return CMD_NONE;
443 #ifdef HAVE_GETMOUSE
444 if( key==KEY_MOUSE )
445 return CMD_MOUSE_EVENT;
446 #endif
448 return get_key_command(key);
449 }
451 command_t
452 get_keyboard_command(void)
453 {
454 return get_keyboard_command_with_timeout(SCREEN_TIMEOUT);
455 }
457 int
458 assign_keys(command_t command, int keys[MAX_COMMAND_KEYS])
459 {
460 int i;
462 i=0;
463 while( cmds[i].name )
464 {
465 if( cmds[i].command == command )
466 {
467 memcpy(cmds[i].keys, keys, sizeof(int)*MAX_COMMAND_KEYS);
468 cmds[i].flags |= COMMAND_KEY_MODIFIED;
469 return 0;
470 }
471 i++;
472 }
473 return -1;
474 }
476 int
477 check_key_bindings(command_definition_t *cp, char *buf, size_t bufsize)
478 {
479 int i;
480 int retval = 0;
482 if( cp==NULL )
483 cp = cmds;
485 i=0;
486 while( cp[i].name )
487 {
488 cp[i].flags &= ~COMMAND_KEY_CONFLICT;
489 i++;
490 }
492 i=0;
493 while( cp[i].name )
494 {
495 int j;
496 command_t cmd;
498 for(j=0; j<MAX_COMMAND_KEYS; j++)
499 if( cp[i].keys[j] &&
500 (cmd=find_key_command(cp[i].keys[j],cp)) != cp[i].command )
501 {
502 if( buf )
503 #ifdef ENABLE_KEYDEF_SCREEN
504 g_snprintf(buf, bufsize,
505 _("Key %s assigned to %s and %s (press %s for the key editor)"),
506 key2str(cp[i].keys[j]),
507 get_key_command_name(cp[i].command),
508 get_key_command_name(cmd),
509 get_key_names(CMD_SCREEN_KEYDEF,0));
510 #else
511 g_snprintf(buf, bufsize,
512 _("Error: Key %s assigned to %s and %s !!!\n"),
513 key2str(cp[i].keys[j]),
514 get_key_command_name(cp[i].command),
515 get_key_command_name(cmd));
516 #endif
517 else
518 fprintf(stderr,
519 _("Error: Key %s assigned to %s and %s !!!\n"),
520 key2str(cp[i].keys[j]),
521 get_key_command_name(cp[i].command),
522 get_key_command_name(cmd));
523 cp[i].flags |= COMMAND_KEY_CONFLICT;
524 set_key_flags(cp, cmd, COMMAND_KEY_CONFLICT);
525 retval = -1;
526 }
527 i++;
528 }
529 return retval;
530 }
532 int
533 write_key_bindings(FILE *f, int flags)
534 {
535 int i,j;
537 if( flags & KEYDEF_WRITE_HEADER )
538 fprintf(f, "## Key bindings for ncmpc (generated by ncmpc)\n\n");
540 i=0;
541 while( cmds[i].name && !ferror(f) )
542 {
543 if( cmds[i].flags & COMMAND_KEY_MODIFIED || flags & KEYDEF_WRITE_ALL)
544 {
545 fprintf(f, "## %s\n", cmds[i].description);
546 if( flags & KEYDEF_COMMENT_ALL )
547 fprintf(f, "#");
548 fprintf(f, "key %s = ", cmds[i].name);
549 for(j=0; j<MAX_COMMAND_KEYS; j++)
550 {
551 if( j && cmds[i].keys[j] )
552 fprintf(f, ", ");
553 if( !j || cmds[i].keys[j] )
554 {
555 if( cmds[i].keys[j]<256 && (isalpha(cmds[i].keys[j]) ||
556 isdigit(cmds[i].keys[j])) )
557 fprintf(f, "\'%c\'", cmds[i].keys[j]);
558 else
559 fprintf(f, "%d", cmds[i].keys[j]);
560 }
561 }
562 fprintf(f,"\n\n");
563 }
564 i++;
565 }
566 return ferror(f);
567 }