diff options
author | Viktor Semykin <thesame.ml@gmail.com> | 2009-09-02 18:04:03 +0300 |
---|---|---|
committer | waker <wakeroid@gmail.com> | 2009-09-02 19:17:54 +0200 |
commit | 121c7130795cf36a7057c6389af592aa001a4e9b (patch) | |
tree | dfdfec73776cecc88f5f662a87a04695affde130 | |
parent | fb48738e15221251ce243214be391552dcf2d3b1 (diff) |
hotkeys plugin
-rw-r--r-- | plugins/hotkeys/hotkeys.c | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/plugins/hotkeys/hotkeys.c b/plugins/hotkeys/hotkeys.c new file mode 100644 index 00000000..3806d2d3 --- /dev/null +++ b/plugins/hotkeys/hotkeys.c @@ -0,0 +1,331 @@ +/* + Hotkeys plugin for DeaDBeeF + Copyright (C) 2009 Viktor Semykin + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <X11/Xlib.h> + +#include "../../deadbeef.h" + +static DB_misc_t plugin; +static DB_functions_t *deadbeef; +static int finished; +static Display *disp; +static int loop_tid; + +#define MAX_COMMAND_COUNT 256 + +typedef struct { + char *name; + KeySym keysym; +} xkey_t; + +#define KEY( kname, kcode ) { .name=kname, .keysym=kcode }, + +static const xkey_t keys[] = { + #include "keysyms.inc" +}; + +typedef void (*command_func_t) (void); + +typedef struct { + int keycode; + int modifier; + command_func_t func; +} command_t; + +static command_t commands [MAX_COMMAND_COUNT]; +static int command_count = 0; + + +typedef struct { + char* name; + void (*func) (void); +} known_command_t; + +static int +get_keycode( Display *disp, const char* name ) { + static int first_kk, last_kk; + static KeySym* syms; + static int ks_per_kk; + static int first_time = 1; + int i, j, ks; + + if ( first_time ) + { + XDisplayKeycodes( disp, &first_kk, &last_kk ); + + syms = XGetKeyboardMapping( disp, first_kk, last_kk - first_kk, &ks_per_kk ); + first_time = 0; + } + + for ( i = 0; i < last_kk-first_kk; i++ ) + { + KeySym sym = *(syms + i*ks_per_kk); + for ( ks = 0; ks < sizeof( keys ) / sizeof( xkey_t ); ks++ ) + { + if ( (keys[ ks ].keysym == sym) && ( 0 == strcmp(name, keys[ ks ].name) ) ) + { + return i+first_kk; + } + } + } + return 0; +} + +static char* +trim(char* s) +{ + char *h, *t; + + for ( h = s; *h == ' ' || *h == '\t'; h++ ); + for ( t = s + strlen(s); *t == ' ' || *t == '\t'; t-- ); + *(t+1) = 0; + return h; +} + +static void +cmd_seek_fwd() { + deadbeef->playback_set_pos( deadbeef->playback_get_pos() + 5 ); +} + +static void +cmd_seek_back() { + deadbeef->playback_set_pos( deadbeef->playback_get_pos() - 5 ); +} + +static void +cmd_volume_up() { + deadbeef->volume_set_db( deadbeef->volume_get_db() + 2 ); +} + +static void +cmd_volume_down() { + deadbeef->volume_set_db( deadbeef->volume_get_db() - 2 ); +} + +static command_func_t +get_command( const char* command ) +{ + if ( 0 == strcasecmp( command, "toggle_pause" ) ) + return deadbeef->playback_pause; + + if ( 0 == strcasecmp( command, "play" ) ) + return deadbeef->playback_play; + + if ( 0 == strcasecmp( command, "prev" ) ) + return deadbeef->playback_play; + + if ( 0 == strcasecmp( command, "next" ) ) + return deadbeef->playback_next; + + if ( 0 == strcasecmp( command, "stop" ) ) + return deadbeef->playback_stop; + + if ( 0 == strcasecmp( command, "play_random" ) ) + return deadbeef->playback_random; + + if ( 0 == strcasecmp( command, "seek_fwd" ) ) + return cmd_seek_fwd; + + if ( 0 == strcasecmp( command, "seek_back" ) ) + return cmd_seek_back; + + if ( 0 == strcasecmp( command, "volume_up" ) ) + return cmd_volume_up; + + if ( 0 == strcasecmp( command, "volume_down" ) ) + return cmd_volume_down; + + return NULL; +} + +static int +read_config( Display *disp ) +{ + char param[ 256 ]; + char config[1024]; + snprintf (config, 1024, "%s/hotkeys", deadbeef->get_config_dir()); + FILE *cfg_file = fopen (config, "rt"); + if (!cfg_file) { + fprintf (stderr, "hotkeys: failed open %s\n", config); + return -1; + } + + int line_nr = 0; + + while ( fgets( param, sizeof(param), cfg_file ) ) + { + line_nr++; + if ( command_count == MAX_COMMAND_COUNT ) + { + fprintf( stderr, "hotkeys: [Config line %d] Maximum count (%d) of commands exceeded\n", line_nr, MAX_COMMAND_COUNT ); + break; + } + + command_t *cmd_entry = &commands[ command_count ]; + cmd_entry->modifier = 0; + cmd_entry->keycode = 0; + + param[ strlen( param )-1 ] = 0; //terminating \n + char* colon = strchr( param, ':' ); + if ( !colon ) + { + fprintf( stderr, "hotkeys: [Config line %d] Wrong config line\n", line_nr ); + continue; + } + char* command = colon+1; + *colon = 0; + + int modifier = 0; + char* key = NULL; + + int done = 0; + char* p; + char* space = param - 1; + do { + p = space+1; + space = strchr( p, ' ' ); + if ( space ) + *space = 0; + else + done = 1; + + if ( 0 == strcasecmp( p, "Ctrl" ) ) + cmd_entry->modifier |= ControlMask; + + else if ( 0 == strcasecmp( p, "Alt" ) ) + cmd_entry->modifier |= Mod1Mask; + + else if ( 0 == strcasecmp( p, "Shift" ) ) + cmd_entry->modifier |= ShiftMask; + + else { + cmd_entry->keycode = get_keycode( disp, p ); + if ( !cmd_entry->keycode ) + { + fprintf( stderr, "hotkeys: [Config line %d] Unknown key: <%s>\n", line_nr, key ); + continue; + } + } + } + while ( !done ); + + if ( cmd_entry->keycode == 0 ) + { + fprintf( stderr, "hotkeys: [Config line %d] Key not specified\n", line_nr ); + continue; + } + + command = trim (command); + cmd_entry->func = get_command( command ); + if ( !cmd_entry->func ) + { + fprintf( stderr, "hotkeys: [Config line %d] Unknown command <%s>\n", line_nr, command ); + continue; + } + command_count++; + } +} + +DB_plugin_t * +hotkeys_load (DB_functions_t *api) { + deadbeef = api; + return DB_PLUGIN (&plugin); +} + +static void +cleanup() { + command_count = 0; + XCloseDisplay( disp ); +} + +static void +hotkeys_event_loop( uintptr_t unused ) { + int i; + + for ( i = 0; i < command_count; i++ ) + XGrabKey( disp, commands[ i ].keycode, commands[ i ].modifier, DefaultRootWindow( disp ), False, GrabModeAsync, GrabModeAsync ); + + while (!finished) { + XEvent event; + + if ( XPending( disp ) ) + { + XNextEvent( disp, &event ); + + if ( event.xkey.type == KeyPress ) + { + for ( i = 0; i < command_count; i++ ) + if ( (event.xkey.keycode == commands[ i ].keycode) && + ((event.xkey.state & commands[ i ].modifier) == commands[ i ].modifier) ) + { + commands[i].func(); + break; + } + } + } + else + usleep( 200 * 1000 ); + } +} + +static int +x_err_handler (Display *disp, XErrorEvent *evt) { + fprintf( stderr, "hotkeys: We got an Xlib error. Most probably one or more of your hotkeys won't work\n" ); +} + +static int +hotkeys_start (void) { + finished = 0; + + disp = XOpenDisplay( NULL ); + if ( !disp ) + { + fprintf( stderr, "Could not open display\n" ); + return -1; + } + XSetErrorHandler( x_err_handler ); + + read_config( disp ); + loop_tid = deadbeef->thread_start( hotkeys_event_loop, 0 ); +} + +static int +hotkeys_stop (void) { + XEvent evt; + + finished = 1; + + deadbeef->thread_join( loop_tid ); + cleanup(); +} + +// define plugin interface +static DB_misc_t plugin = { + .plugin.type = DB_PLUGIN_MISC, + .plugin.name = "Global Hotkeys", + .plugin.descr = "Allows to control player using xlib global hotkeys", + .plugin.author = "Viktor Semykin", + .plugin.email = "thesame.ml@gmail.com", + .plugin.website = "http://deadbeef.sf.net", + .plugin.start = hotkeys_start, + .plugin.stop = hotkeys_stop +}; + |