@ -1,5 +1,5 @@
/*
/*
* Copyright ( c ) 2021 Alessandro Mauri
* Copyright ( c ) 2020 Alessandro Mauri
*
*
* Permission is hereby granted , free of charge , to any person obtaining a copy
* Permission is hereby granted , free of charge , to any person obtaining a copy
* of this software and associated documentation files ( the " Software " ) , to deal
* of this software and associated documentation files ( the " Software " ) , to deal
@ -31,6 +31,7 @@
# include <fcntl.h>
# include <fcntl.h>
# include <dirent.h>
# include <dirent.h>
# include <unistd.h>
# include <unistd.h>
# include <locale.h>
# include <signal.h>
# include <signal.h>
# include <sys/wait.h>
# include <sys/wait.h>
# include <sys/epoll.h>
# include <sys/epoll.h>
@ -39,12 +40,12 @@
# include <ctype.h>
# include <ctype.h>
# include <sys/stat.h>
# include <sys/stat.h>
# include <stdarg.h>
# include <stdarg.h>
# include <sys/mma n.h>
# include <xkbcommon/xkbcommo n.h>
# include "keys.h"
# include <linux/input.h>
/* Value defines */
/* Value defines */
# define FILE_NAME_MAX_LENGTH 255
# define FILE_NAME_MAX_LENGTH 255
# define KEY_BUFFER_SIZE 16
# define KEY_BUFFER_SIZE 10
# define BLOCK_SIZE 512
# define BLOCK_SIZE 512
/* ANSI colors escape codes */
/* ANSI colors escape codes */
@ -63,8 +64,6 @@
# define test_bit(yalv, abs_b) ((((char *)abs_b)[yalv / 8] & (1<<yalv%8)) > 0)
# define test_bit(yalv, abs_b) ((((char *)abs_b)[yalv / 8] & (1<<yalv%8)) > 0)
# define array_size(val) (val ? sizeof(val) / sizeof(val[0]) : 0)
# define array_size(val) (val ? sizeof(val) / sizeof(val[0]) : 0)
# define array_size_const(val) ((int)(sizeof(val) / sizeof(val[0])))
# define array_size_const(val) ((int)(sizeof(val) / sizeof(val[0])))
# define wrap_err(s) "[%s] " s, __func__
# define is_empty(s) (!(s) || !(s)[0])
# define EVENT_SIZE (sizeof(struct inotify_event))
# define EVENT_SIZE (sizeof(struct inotify_event))
# define EVENT_BUF_LEN (1024*(EVENT_SIZE+16))
# define EVENT_BUF_LEN (1024*(EVENT_SIZE+16))
@ -78,14 +77,12 @@ const char *config_paths[] = {
} ;
} ;
struct key_buffer {
struct key_buffer {
unsigned shor t buf [ KEY_BUFFER_SIZE ] ;
xkb_keysym_ t buf [ KEY_BUFFER_SIZE ] ;
unsigned int size ;
unsigned int size ;
} ;
} ;
/* Hotkey list: linked list that holds all valid hoteys parsed from the
/* Hotkey list: linked list that holds all valid hoteys parsed from the
* config file and the corresponding command
* config file and the corresponding command */
* TODO : re - implement hotkey_list as a hash table to make searching O ( 1 )
*/
union hotkey_main_data {
union hotkey_main_data {
struct key_buffer kb ;
struct key_buffer kb ;
@ -99,21 +96,23 @@ struct hotkey_list_e {
struct hotkey_list_e * next ;
struct hotkey_list_e * next ;
} ;
} ;
struct {
struct xkb_context * context ;
struct xkb_rule_names names ;
struct xkb_keymap * keymap ;
struct xkb_state * state ;
// struct xkb_compose_state *comp_state;
} keyboard ;
struct hotkey_list_e * hotkey_list = NULL ;
struct hotkey_list_e * hotkey_list = NULL ;
/* TODO: add hotkey_range struct as a second check to avoid accessing the list
* struct {
* unsigned int min ;
* unsigned int max ;
* } hotkey_range ;
*/
unsigned long hotkey_size_mask = 0 ;
unsigned long hotkey_size_mask = 0 ;
char * ext_config_file = NULL ;
char * ext_config_file = NULL ;
/* Global flags */
/* Global flags */
int vflag = 0 ;
int vflag = 0 ;
int dead = 0 ; /* Exit flag */
int dead = 0 ; /* Exit flag */
/* key buffer operations */
/* key buffer operations */
int key_buffer_add ( struct key_buffer * , unsigned shor t) ;
int key_buffer_add ( struct key_buffer * , xkb_keysym_ t) ;
int key_buffer_remove ( struct key_buffer * , unsigned shor t) ;
int key_buffer_remove ( struct key_buffer * , xkb_keysym_ t) ;
int key_buffer_compare_fuzzy ( struct key_buffer * , struct key_buffer * ) ;
int key_buffer_compare_fuzzy ( struct key_buffer * , struct key_buffer * ) ;
int key_buffer_compare ( struct key_buffer * , struct key_buffer * ) ;
int key_buffer_compare ( struct key_buffer * , struct key_buffer * ) ;
void key_buffer_reset ( struct key_buffer * ) ;
void key_buffer_reset ( struct key_buffer * ) ;
@ -122,7 +121,7 @@ void int_handler (int signum);
void exec_command ( char * ) ;
void exec_command ( char * ) ;
void parse_config_file ( void ) ;
void parse_config_file ( void ) ;
void update_descriptors_list ( int * * , int * ) ;
void update_descriptors_list ( int * * , int * ) ;
inline void remove_lock ( void ) ;
void remove_lock ( void ) ;
void die ( const char * , . . . ) ;
void die ( const char * , . . . ) ;
void usage ( void ) ;
void usage ( void ) ;
int prepare_epoll ( int * , int , int ) ;
int prepare_epoll ( int * , int , int ) ;
@ -132,7 +131,6 @@ const char * code_to_name (unsigned int);
void hotkey_list_add ( struct hotkey_list_e * , union hotkey_main_data * , char * , int ) ;
void hotkey_list_add ( struct hotkey_list_e * , union hotkey_main_data * , char * , int ) ;
void hotkey_list_destroy ( struct hotkey_list_e * ) ;
void hotkey_list_destroy ( struct hotkey_list_e * ) ;
void hotkey_list_remove ( struct hotkey_list_e * , struct hotkey_list_e * ) ;
void hotkey_list_remove ( struct hotkey_list_e * , struct hotkey_list_e * ) ;
void replace ( char * * , const char * , const char * ) ;
int main ( int argc , char * argv [ ] )
int main ( int argc , char * argv [ ] )
{
{
@ -148,6 +146,8 @@ int main (int argc, char *argv[])
struct sigaction action ;
struct sigaction action ;
struct input_event event ;
struct input_event event ;
struct key_buffer pb = { { 0 } , 0 } ; /* Pressed keys buffer */
struct key_buffer pb = { { 0 } , 0 } ; /* Pressed keys buffer */
// struct xkb_compose_table_t *comp_table = NULL;
// const char *locale = NULL;
/* Parse command line arguments */
/* Parse command line arguments */
while ( ( opc = getopt ( argc , argv , " vc:dh " ) ) ! = - 1 ) {
while ( ( opc = getopt ( argc , argv , " vc:dh " ) ) ! = - 1 ) {
@ -158,7 +158,7 @@ int main (int argc, char *argv[])
case ' c ' :
case ' c ' :
ext_config_file = malloc ( strlen ( optarg ) + 1 ) ;
ext_config_file = malloc ( strlen ( optarg ) + 1 ) ;
if ( ! ext_config_file )
if ( ! ext_config_file )
die ( wrap_err ( " Bad malloc:" ) ) ;
die ( " malloc in main() : " ) ;
strcpy ( ext_config_file , optarg ) ;
strcpy ( ext_config_file , optarg ) ;
break ;
break ;
case ' d ' :
case ' d ' :
@ -175,13 +175,37 @@ int main (int argc, char *argv[])
dead = 0 ;
dead = 0 ;
memset ( & action , 0 , sizeof ( action ) ) ;
memset ( & action , 0 , sizeof ( action ) ) ;
action . sa_handler = int_handler ;
action . sa_handler = int_handler ;
sigaction ( SIGINT , & action , NULL ) ;
if ( sigaction ( SIGINT , & action , NULL ) = = - 1 )
sigaction ( SIGUSR1 , & action , NULL ) ;
die ( wrap_err ( " Error setting interrupt handler: " ) ) ;
sigaction ( SIGCHLD , & action , NULL ) ;
if ( sigaction ( SIGUSR1 , & action , NULL ) = = - 1 )
die ( wrap_err ( " Error setting interrupt handler: " ) ) ;
/* Initialize xkbcommon */
if ( sigaction ( SIGCHLD , & action , NULL ) = = - 1 )
if ( ! ( keyboard . context = xkb_context_new ( XKB_CONTEXT_NO_FLAGS ) ) )
die ( wrap_err ( " Error setting interrupt handler: " ) ) ;
die ( " Error initializing xkbcommon context: " ) ;
keyboard . names = ( struct xkb_rule_names ) {
. rules = NULL ,
. model = NULL ,
. layout = " gb " ,
. variant = NULL ,
. options = NULL ,
} ;
if ( ! ( keyboard . keymap = xkb_keymap_new_from_names ( keyboard . context ,
& keyboard . names , XKB_KEYMAP_COMPILE_NO_FLAGS ) ) )
die ( " Error compiling keymap: " ) ;
if ( ! ( keyboard . state = xkb_state_new ( keyboard . keymap ) ) )
die ( " Error creating keyboard state: " ) ;
/*
locale = setlocale ( LC_CTYPE , NULL ) ;
if ( ! ( comp_table = xkb_compose_table_new_from_locale ( keyboard . context ,
locale , XKB_COMPOSE_COMPILE_NO_FLAGS ) ) )
die ( " Error compiling compose table: " ) ;
if ( ! ( keyboard . comp_state = xkb_compose_state_new ( comp_table ,
XKB_COMPOSE_TABLE_NO_FLAGS ) ) )
die ( " Error creating compose state: " ) ;
*/
/* Parse config file */
/* Parse config file */
parse_config_file ( ) ;
parse_config_file ( ) ;
@ -189,7 +213,7 @@ int main (int argc, char *argv[])
/* Check if hkd is already running */
/* Check if hkd is already running */
lock_file_descriptor = open ( LOCK_FILE , O_RDWR | O_CREAT , 0600 ) ;
lock_file_descriptor = open ( LOCK_FILE , O_RDWR | O_CREAT , 0600 ) ;
if ( lock_file_descriptor < 0 )
if ( lock_file_descriptor < 0 )
die ( wrap_err ( " Can't open lock file: " ) ) ;
die ( " Can't open lock file: " ) ;
fl . l_start = 0 ;
fl . l_start = 0 ;
fl . l_len = 0 ;
fl . l_len = 0 ;
fl . l_type = F_WRLCK ;
fl . l_type = F_WRLCK ;
@ -200,12 +224,15 @@ int main (int argc, char *argv[])
/* If a dump is requested print the hotkey list then exit */
/* If a dump is requested print the hotkey list then exit */
if ( dump ) {
if ( dump ) {
char key_name [ 64 ] ;
printf ( " DUMPING HOTKEY LIST \n \n " ) ;
printf ( " DUMPING HOTKEY LIST \n \n " ) ;
for ( struct hotkey_list_e * tmp = hotkey_list ; tmp ; tmp = tmp - > next ) {
for ( struct hotkey_list_e * tmp = hotkey_list ; tmp ; tmp = tmp - > next ) {
printf ( " Hotkey \n " ) ;
printf ( " Hotkey \n " ) ;
printf ( " \t Keys: " ) ;
printf ( " \t Keys: " ) ;
for ( unsigned int i = 0 ; i < tmp - > data . kb . size ; i + + )
for ( unsigned int i = 0 ; i < tmp - > data . kb . size ; i + + ) {
printf ( " %s " , code_to_name ( tmp - > data . kb . buf [ i ] ) ) ;
xkb_keysym_get_name ( tmp - > data . kb . buf [ i ] , key_name , 64 ) ;
printf ( " %s " , key_name ) ;
}
printf ( " \n \t Matching: %s \n " , tmp - > fuzzy ? " fuzzy " : " ordered " ) ;
printf ( " \n \t Matching: %s \n " , tmp - > fuzzy ? " fuzzy " : " ordered " ) ;
printf ( " \t Command: %s \n \n " , tmp - > command ) ;
printf ( " \t Command: %s \n \n " , tmp - > command ) ;
}
}
@ -217,9 +244,10 @@ int main (int argc, char *argv[])
/* Prepare directory update watcher */
/* Prepare directory update watcher */
if ( event_watcher < 0 )
if ( event_watcher < 0 )
die ( wrap_err ( " Could not call inotify_init: " ) ) ;
die ( " Could not call inotify_init: " ) ;
if ( inotify_add_watch ( event_watcher , EVDEV_ROOT_DIR , IN_CREATE | IN_DELETE ) < 0 )
if ( inotify_add_watch ( event_watcher , EVDEV_ROOT_DIR , IN_CREATE | IN_DELETE ) < 0 )
die ( wrap_err ( " Could not add /dev/input to the watch list: " ) ) ;
die ( " Could not add /dev/input to the watch list: " ) ;
/* Prepare epoll list */
/* Prepare epoll list */
ev_fd = prepare_epoll ( fds , fd_num , event_watcher ) ;
ev_fd = prepare_epoll ( fds , fd_num , event_watcher ) ;
@ -229,8 +257,13 @@ int main (int argc, char *argv[])
int t = 0 ;
int t = 0 ;
static unsigned int prev_size ;
static unsigned int prev_size ;
static struct epoll_event ev_type ;
static struct epoll_event ev_type ;
static xkb_keycode_t keycode ;
static xkb_keysym_t keysym ;
// enum xkb_state_component changed;
struct hotkey_list_e * tmp ;
struct hotkey_list_e * tmp ;
char buf [ EVENT_BUF_LEN ] ;
char buf [ EVENT_BUF_LEN ] ;
static char key_name [ 64 ] ;
// enum xkb_compose_status status;
/* On linux use epoll(2) as it gives better performance */
/* On linux use epoll(2) as it gives better performance */
if ( epoll_wait ( ev_fd , & ev_type , fd_num , - 1 ) < 0 | | dead ) {
if ( epoll_wait ( ev_fd , & ev_type , fd_num , - 1 ) < 0 | | dead ) {
@ -245,7 +278,7 @@ int main (int argc, char *argv[])
sleep ( 1 ) ; // wait for devices to settle
sleep ( 1 ) ; // wait for devices to settle
update_descriptors_list ( & fds , & fd_num ) ;
update_descriptors_list ( & fds , & fd_num ) ;
if ( close ( ev_fd ) < 0 )
if ( close ( ev_fd ) < 0 )
die ( wrap_err ( " Could not close event fd list (ev_fd): " ) ) ;
die ( " Could not close event file descriptors list (ev_fd): " ) ;
ev_fd = prepare_epoll ( fds , fd_num , event_watcher ) ;
ev_fd = prepare_epoll ( fds , fd_num , event_watcher ) ;
goto mainloop_begin ;
goto mainloop_begin ;
}
}
@ -256,24 +289,32 @@ int main (int argc, char *argv[])
read_b = read ( fds [ i ] , & event , sizeof ( struct input_event ) ) ;
read_b = read ( fds [ i ] , & event , sizeof ( struct input_event ) ) ;
if ( read_b ! = sizeof ( struct input_event ) ) continue ;
if ( read_b ! = sizeof ( struct input_event ) ) continue ;
/* Ignore touchpad events */
if ( event . type ! = EV_KEY )
if (
continue ;
event . type = = EV_KEY & &
event . code ! = BTN_TOUCH & &
// evdev offset
event . code ! = BTN_TOOL_FINGER & &
keycode = event . code + 8 ;
event . code ! = BTN_TOOL_DOUBLETAP & &
if ( event . value = = 2 & & ! xkb_keymap_key_repeats (
event . code ! = BTN_TOOL_TRIPLETAP
keyboard . keymap , keycode ) )
) {
continue ;
switch ( event . value ) {
if ( event . value ) {
/* Key released */
keysym = xkb_state_key_get_one_sym ( keyboard . state , keycode ) ;
case 0 :
// kbd_compose_state_feed(keyboard.comp_state, keysym);
key_buffer_remove ( & pb , event . code ) ;
break ;
/* Key pressed */
case 1 :
key_buffer_add ( & pb , event . code ) ;
break ;
}
}
/* status = xkb_compose_state_get_status(keyboard.comp_state);
if ( status = = XKB_COMPOSE_CANCELLED | | status = = XKB_COMPOSE_COMPOSED )
xkb_compose_state_reset ( kbd - > compose_state ) ;
*/
if ( event . value ) { /* Key pressed */
xkb_state_update_key ( keyboard . state ,
keycode , XKB_KEY_UP ) ;
key_buffer_add ( & pb , keysym ) ;
} else { /* Key released */
xkb_state_update_key ( keyboard . state ,
keycode , XKB_KEY_DOWN ) ;
key_buffer_remove ( & pb , keysym ) ;
}
}
}
}
@ -283,7 +324,8 @@ int main (int argc, char *argv[])
if ( vflag ) {
if ( vflag ) {
printf ( " Pressed keys: " ) ;
printf ( " Pressed keys: " ) ;
for ( unsigned int i = 0 ; i < pb . size ; i + + )
for ( unsigned int i = 0 ; i < pb . size ; i + + )
printf ( " %s " , code_to_name ( pb . buf [ i ] ) ) ;
xkb_keysym_get_name ( pb . buf [ i ] , key_name , 64 ) ;
printf ( " %s " , key_name ) ;
putchar ( ' \n ' ) ;
putchar ( ' \n ' ) ;
}
}
@ -305,17 +347,21 @@ int main (int argc, char *argv[])
wait ( NULL ) ;
wait ( NULL ) ;
if ( ! dead )
if ( ! dead )
fprintf ( stderr , red ( " An error occured: %s \n " ) , errno ? strerror ( errno ) : " idk " ) ;
fprintf ( stderr , red ( " An error occured: %s \n " ) , errno ? strerror ( errno ) : " idk " ) ;
xkb_state_unref ( keyboard . state ) ;
xkb_keymap_unref ( keyboard . keymap ) ;
// xkb_compose_state_unref(keyboard.comp_state);
xkb_context_unref ( keyboard . context ) ;
close ( ev_fd ) ;
close ( ev_fd ) ;
close ( event_watcher ) ;
close ( event_watcher ) ;
for ( int i = 0 ; i < fd_num ; i + + )
for ( int i = 0 ; i < fd_num ; i + + )
if ( close ( fds [ i ] ) = = - 1 )
if ( close ( fds [ i ] ) = = - 1 )
die ( wrap_err ( " Error closing file descriptors: " ) ) ;
die ( " Error closing file descriptors: " ) ;
return 0 ;
return 0 ;
}
}
/* Adds a keycode to the pressed buffer if it is not already present
/* Adds a keycode to the pressed buffer if it is not already present
* Returns non zero if the key was not added . */
* Returns non zero if the key was not added . */
int key_buffer_add ( struct key_buffer * pb , unsigned shor t key )
int key_buffer_add ( struct key_buffer * pb , xkb_keysym_ t key )
{
{
if ( ! pb ) return 1 ;
if ( ! pb ) return 1 ;
/* Linear search if the key is already buffered */
/* Linear search if the key is already buffered */
@ -332,7 +378,7 @@ int key_buffer_add (struct key_buffer *pb, unsigned short key)
/* Removes a keycode from a pressed buffer if it is present returns
/* Removes a keycode from a pressed buffer if it is present returns
* non zero in case of failure ( key not present or buffer empty ) . */
* non zero in case of failure ( key not present or buffer empty ) . */
int key_buffer_remove ( struct key_buffer * pb , unsigned shor t key )
int key_buffer_remove ( struct key_buffer * pb , xkb_keysym_ t key )
{
{
if ( ! pb ) return 1 ;
if ( ! pb ) return 1 ;
@ -357,7 +403,7 @@ void int_handler (int signum)
switch ( signum ) {
switch ( signum ) {
case SIGINT :
case SIGINT :
if ( dead )
if ( dead )
die ( wrap_err ( " An error occured, exiting " ) ) ;
die ( " An error occured, exiting " ) ;
if ( vflag )
if ( vflag )
printf ( yellow ( " Received interrupt signal, exiting gracefully... \n " ) ) ;
printf ( yellow ( " Received interrupt signal, exiting gracefully... \n " ) ) ;
dead = 1 ;
dead = 1 ;
@ -387,20 +433,20 @@ void exec_command (char *command)
return ;
return ;
default :
default :
/* Some other error */
/* Some other error */
fprintf ( stderr , wrap_err ( " Could not parse, %s is not valid \n " ) , command ) ;
fprintf ( stderr , " Could not parse, %s is not valid \n " , command ) ;
return ;
return ;
}
}
pid_t cpid ;
pid_t cpid ;
switch ( cpid = fork ( ) ) {
switch ( cpid = fork ( ) ) {
case - 1 :
case - 1 :
fprintf ( stderr , wrap_err ( " Could not create child process: %s " ) , strerror ( errno ) ) ;
fprintf ( stderr , " Could not create child process: %s " , strerror ( errno ) ) ;
wordfree ( & result ) ;
wordfree ( & result ) ;
break ;
break ;
case 0 :
case 0 :
/* This is the child process, execute the command */
/* This is the child process, execute the command */
execvp ( result . we_wordv [ 0 ] , result . we_wordv ) ;
execvp ( result . we_wordv [ 0 ] , result . we_wordv ) ;
die ( wrap_err ( " %s: " ) , command ) ;
die ( " %s: " , command ) ;
break ;
break ;
default :
default :
while ( waitpid ( cpid , NULL , WNOHANG ) = = - 1 ) { }
while ( waitpid ( cpid , NULL , WNOHANG ) = = - 1 ) { }
@ -419,7 +465,7 @@ void update_descriptors_list (int **fds, int *fd_num)
/* Open the event directory */
/* Open the event directory */
DIR * ev_dir = opendir ( EVDEV_ROOT_DIR ) ;
DIR * ev_dir = opendir ( EVDEV_ROOT_DIR ) ;
if ( ! ev_dir )
if ( ! ev_dir )
die ( wrap_err ( " Could not open /dev/input: " ) ) ;
die ( " Could not open /dev/input: " ) ;
( * fd_num ) = 0 ;
( * fd_num ) = 0 ;
@ -446,7 +492,7 @@ void update_descriptors_list (int **fds, int *fd_num)
memset ( evtype_b , 0 , sizeof ( evtype_b ) ) ;
memset ( evtype_b , 0 , sizeof ( evtype_b ) ) ;
if ( ioctl ( tmp_fd , EVIOCGBIT ( 0 , EV_MAX ) , evtype_b ) < 0 ) {
if ( ioctl ( tmp_fd , EVIOCGBIT ( 0 , EV_MAX ) , evtype_b ) < 0 ) {
if ( vflag )
if ( vflag )
printf ( red ( " Could not read capabilities of device %s \n " ) , ev_path ) ;
printf ( red ( " Could not read capabilities of device %s \n " ) , ev_path ) ;
close ( tmp_fd ) ;
close ( tmp_fd ) ;
continue ;
continue ;
}
}
@ -460,7 +506,7 @@ void update_descriptors_list (int **fds, int *fd_num)
tmp_p = realloc ( ( * fds ) , sizeof ( int ) * ( ( * fd_num ) + 1 ) ) ;
tmp_p = realloc ( ( * fds ) , sizeof ( int ) * ( ( * fd_num ) + 1 ) ) ;
if ( ! tmp_p )
if ( ! tmp_p )
die ( wrap_err ( " R ealloc file descriptors:" ) ) ;
die ( " r ealloc file descriptors:" ) ;
( * fds ) = ( int * ) tmp_p ;
( * fds ) = ( int * ) tmp_p ;
( * fds ) [ ( * fd_num ) ] = tmp_fd ;
( * fds ) [ ( * fd_num ) ] = tmp_fd ;
@ -471,7 +517,7 @@ void update_descriptors_list (int **fds, int *fd_num)
if ( vflag )
if ( vflag )
printf ( green ( " Monitoring %d devices \n " ) , * fd_num ) ;
printf ( green ( " Monitoring %d devices \n " ) , * fd_num ) ;
} else {
} else {
die ( wrap_err ( " Could not open any devices, exiting " ) ) ;
die ( " Could not open any devices, exiting " ) ;
}
}
}
}
@ -481,12 +527,12 @@ int prepare_epoll (int *fds, int fd_num, int event_watcher)
static struct epoll_event epoll_read_ev ;
static struct epoll_event epoll_read_ev ;
epoll_read_ev . events = EPOLLIN ;
epoll_read_ev . events = EPOLLIN ;
if ( ev_fd < 0 )
if ( ev_fd < 0 )
die ( wrap_err ( " epoll_create failed: " ) ) ;
die ( " epoll_create failed in prepare_epoll : " ) ;
if ( epoll_ctl ( ev_fd , EPOLL_CTL_ADD , event_watcher , & epoll_read_ev ) < 0 )
if ( epoll_ctl ( ev_fd , EPOLL_CTL_ADD , event_watcher , & epoll_read_ev ) < 0 )
die ( wrap_err ( " Could not add file descriptor to the epoll list: " ) ) ;
die ( " Could not add file descriptor to the epoll list: " ) ;
for ( int i = 0 ; i < fd_num ; i + + )
for ( int i = 0 ; i < fd_num ; i + + )
if ( epoll_ctl ( ev_fd , EPOLL_CTL_ADD , fds [ i ] , & epoll_read_ev ) < 0 )
if ( epoll_ctl ( ev_fd , EPOLL_CTL_ADD , fds [ i ] , & epoll_read_ev ) < 0 )
die ( wrap_err ( " Could not add file descriptor to the epoll list: " ) ) ;
die ( " Could not add file descriptor to the epoll list: " ) ;
return ev_fd ;
return ev_fd ;
}
}
@ -534,12 +580,12 @@ void hotkey_list_add (struct hotkey_list_e *head, union hotkey_main_data *dt, ch
{
{
int size ;
int size ;
struct hotkey_list_e * tmp ;
struct hotkey_list_e * tmp ;
if ( is_empty ( cmd ) | | ! ( size = strlen ( cmd ) ) )
if ( ! ( size = strlen ( cmd ) ) )
return ;
return ;
if ( ! ( tmp = malloc ( sizeof ( struct hotkey_list_e ) ) ) )
if ( ! ( tmp = malloc ( sizeof ( struct hotkey_list_e ) ) ) )
die ( wrap_err ( " Bad malloc: " ) ) ;
die ( " Memory allocation failed in hotkey_list_add(): " ) ;
if ( ! ( tmp - > command = malloc ( size + 1 ) ) )
if ( ! ( tmp - > command = malloc ( size + 1 ) ) )
die ( wrap_err ( " Bad malloc: " ) ) ;
die ( " Memory allocation failed in hotkey_list_add(): " ) ;
strcpy ( tmp - > command , cmd ) ;
strcpy ( tmp - > command , cmd ) ;
tmp - > data = * dt ;
tmp - > data = * dt ;
tmp - > fuzzy = f ;
tmp - > fuzzy = f ;
@ -575,22 +621,22 @@ void hotkey_list_remove (struct hotkey_list_e *head, struct hotkey_list_e *elem)
void parse_config_file ( void )
void parse_config_file ( void )
{
{
wordexp_t result = { 0 } ;
wordexp_t result = { 0 } ;
int config_file ;
FILE * fd ;
/* normal, skip line, get matching, get keys, get command, output */
/* normal, skip line, get matching, get keys, get command, output */
enum { NORM , LINE_SKIP , GET_TYPE , GET_KEYS , GET_CMD , LAST } parse_state = NORM ;
enum { NORM , LINE_SKIP , GET_TYPE , GET_KEYS , GET_CMD , LAST } parse_state = NORM ;
enum { CONT , NEW_BL , LAST_BL , END } block_state = CONT ; /* continue, new block, last block, end */
enum { HK_NORM = 0 , HK_FUZZY = 1 , ALIAS = - 1 } type ;
enum { HK_NORM = 0 , HK_FUZZY = 1 , ALIAS = - 1 } type ;
int eof = 0 ;
int cmd_is_alias = 0 ;
int token _size = 0 ;
int alloc_tmp = 0 , alloc _size = 0 ;
int i_tmp = 0 , linenum = 1 ;
int i_tmp = 0 , linenum = 1 ;
char * buffer ;
char block [ BLOCK_SIZE + 1 ] = { 0 } ;
char * bb = NULL ;
char * bb = NULL ;
char * keys = NULL ;
char * keys = NULL ;
char * cmd = NULL ;
char * cmd = NULL ;
char * cp_tmp = NULL ;
char * cp_tmp = NULL ;
union hotkey_main_data dt = { 0 } ;
union hotkey_main_data dt = { 0 } ;
unsigned short u s_tmp = 0 ;
xkb_keysym_t k s_tmp = 0 ;
/* Choose config file */
if ( ext_config_file ) {
if ( ext_config_file ) {
switch ( wordexp ( ext_config_file , & result , 0 ) ) {
switch ( wordexp ( ext_config_file , & result , 0 ) ) {
case 0 :
case 0 :
@ -599,15 +645,15 @@ void parse_config_file (void)
/* If the error was WRDE_NOSPACE,
/* If the error was WRDE_NOSPACE,
* then perhaps part of the result was allocated */
* then perhaps part of the result was allocated */
wordfree ( & result ) ;
wordfree ( & result ) ;
die ( wrap_err ( " Not enough space: " ) ) ;
die ( " Not enough space: " ) ;
default :
default :
die ( wrap_err ( " Path not valid: " ) ) ;
die ( " Path not valid: " ) ;
}
}
config_file = open ( result . we_wordv [ 0 ] , O_RDONLY | O_NONBLOCK ) ;
fd = f open( result . we_wordv [ 0 ] , " r " ) ;
wordfree ( & result ) ;
wordfree ( & result ) ;
if ( config_file < 0 )
if ( ! fd )
die ( wrap_err ( " Error opening config file: " ) ) ;
die ( " Error opening config file: " ) ;
free ( ext_config_file ) ;
free ( ext_config_file ) ;
ext_config_file = NULL ;
ext_config_file = NULL ;
} else {
} else {
@ -619,54 +665,51 @@ void parse_config_file (void)
/* If the error was WRDE_NOSPACE,
/* If the error was WRDE_NOSPACE,
* then perhaps part of the result was allocated */
* then perhaps part of the result was allocated */
wordfree ( & result ) ;
wordfree ( & result ) ;
die ( wrap_err ( " Not enough space: " ) ) ;
die ( " Not enough space: " ) ;
default :
default :
die ( wrap_err ( " Path not valid: " ) ) ;
die ( " Path not valid: " ) ;
}
}
config_file = open ( result . we_wordv [ 0 ] , O_RDONLY | O_NONBLOCK ) ;
fd = f open( result . we_wordv [ 0 ] , " r " ) ;
wordfree ( & result ) ;
wordfree ( & result ) ;
if ( config_file > = 0 )
if ( fd )
break ;
break ;
if ( vflag )
if ( vflag )
printf ( yellow ( " config file not found at %s \n " ) , config_paths [ i ] ) ;
printf ( yellow ( " config file not found at %s \n " ) , config_paths [ i ] ) ;
}
}
if ( ! config_file )
if ( ! fd )
die ( wrap_err ( " Could not open any config files, check stderr for more details " ) ) ;
die ( " Could not open any config files, check the log for more details " ) ;
}
}
/* Using mmap because of simplicity, most config files are smaller than
* a page but this method mostly ensures that big files are taken care
* of efficiently and reduces the overall complexity of the code .
* Furthermore we only need this space when parsing the config file ,
* afterwards we release it .
*/
struct stat sb ;
int file_size ;
if ( fstat ( config_file , & sb ) = = - 1 )
die ( " fstat " ) ;
file_size = sb . st_size ;
// FIXME: page align size
buffer = mmap ( NULL , file_size , PROT_READ , MAP_PRIVATE , config_file , 0 ) ;
if ( buffer = = MAP_FAILED )
die ( wrap_err ( " mmap failed: " ) ) ;
close ( config_file ) ;
bb = buffer ;
hotkey_list_destroy ( hotkey_list ) ;
hotkey_list_destroy ( hotkey_list ) ;
hotkey_list = NULL ;
hotkey_list = NULL ;
while ( ! eof ) {
while ( block_state ! = END ) {
// FIXME: incorect line counting, especially for multiline commands
int tmp = 0 ;
memset ( block , 0 , BLOCK_SIZE + 1 ) ;
tmp = fread ( block , sizeof ( char ) , BLOCK_SIZE , fd ) ;
if ( ! tmp )
break ;
if ( tmp < BLOCK_SIZE | | feof ( fd ) )
block_state = LAST_BL ;
else
block_state = CONT ;
bb = block ;
while ( block_state = = CONT | | block_state = = LAST_BL ) {
switch ( parse_state ) {
switch ( parse_state ) {
// First state
// First state
case NORM :
case NORM :
// remove whitespaces
// remove whitespaces
while ( isblank ( * bb ) )
while ( isblank ( * bb ) & & * bb )
bb + + ;
bb + + ;
// get state
// get state
switch ( * bb ) {
switch ( * bb ) {
# if defined(__X86_64__) || defined(__i386__)
case EOF :
# endif
case ' \0 ' :
case ' \0 ' :
eof = 1 ;
// If it is the end of the last block exit
block_state = block_state = = LAST_BL ? END : NEW_BL ;
break ;
break ;
case ' \n ' :
case ' \n ' :
case ' # ' :
case ' # ' :
@ -679,10 +722,15 @@ void parse_config_file (void)
break ;
break ;
// Skip line (comment)
// Skip line (comment)
case LINE_SKIP :
case LINE_SKIP :
for ( ; ( bb - buffer ) < file_size & & * bb ! = ' \n ' ; bb + + ) ;
while ( * bb ! = ' \n ' & & * bb )
bb + + ;
if ( * bb ) {
bb + + ;
bb + + ;
linenum + + ;
linenum + + ;
parse_state = NORM ;
parse_state = NORM ;
} else {
block_state = NEW_BL ;
}
break ;
break ;
// Get compairson method
// Get compairson method
case GET_TYPE :
case GET_TYPE :
@ -697,8 +745,8 @@ void parse_config_file (void)
type = ALIAS ;
type = ALIAS ;
break ;
break ;
default :
default :
die ( wrap_err ( " Error at line %d: "
die ( " Error at line %d: "
" hotkey definition must start with '-', '*' or '@' " ) ,
" hotkey definition must start with '-', '*' or '@' " ,
linenum ) ;
linenum ) ;
break ;
break ;
}
}
@ -707,78 +755,128 @@ void parse_config_file (void)
break ;
break ;
// Get keys
// Get keys
case GET_KEYS :
case GET_KEYS :
for ( token_size = 0 ; token_size < ( file_size - ( bb - buffer ) ) & & ! ( bb [ token_size ] = = ' : ' | | bb [ token_size ] = = ' \n ' ) ; token_size + + ) ;
if ( ! keys ) {
if ( bb [ token_size ] = = ' \n ' )
if ( ! ( keys = malloc ( alloc_size = ( sizeof ( char ) * 64 ) ) ) )
die ( wrap_err ( " Error at line %d: "
die ( " malloc for keys in parse_config_file(): " ) ;
" no command specified, missing ':' after keys " ) ,
memset ( keys , 0 , alloc_size ) ;
linenum ) ;
} else if ( alloc_tmp > = alloc_size ) {
keys = malloc ( token_size + 1 ) ;
if ( ! ( keys = realloc ( keys , alloc_size = alloc_size * 2 ) ) )
if ( ! keys )
die ( " realloc for keys in parse_config_file(): " ) ;
die ( wrap_err ( " Bad malloc parsing keys: " ) ) ;
memset ( & keys [ alloc_size / 2 ] , 0 , alloc_size / 2 ) ;
memcpy ( keys , bb , token_size ) ;
}
keys [ token_size ] = ' \0 ' ;
bb + = token_size + 1 ;
for ( alloc_tmp = 0 ; bb [ alloc_tmp ] & &
bb [ alloc_tmp ] ! = ' : ' & &
bb [ alloc_tmp ] ! = ' < ' & &
bb [ alloc_tmp ] ! = ' \n ' & &
alloc_tmp < alloc_size ; alloc_tmp + + ) ;
if ( ! bb [ alloc_tmp ] | | alloc_tmp = = alloc_size ) {
strncat ( keys , bb , alloc_tmp ) ;
bb + = alloc_tmp ;
if ( block_state = = LAST_BL )
die ( " Keys not finished before end of file " ) ;
else
block_state = NEW_BL ;
break ;
} else if ( bb [ alloc_tmp ] = = ' : ' | | bb [ alloc_tmp ] = = ' < ' ) {
cmd_is_alias = ( bb [ alloc_tmp ] = = ' < ' ) ;
strncat ( keys , bb , alloc_tmp ) ;
bb + = alloc_tmp + 1 ;
parse_state = GET_CMD ;
parse_state = GET_CMD ;
break ;
break ;
} else {
die ( " Error at line %d: "
" no command specified, missing ':' or '<' after keys " ,
linenum ) ;
}
break ;
// Get command
// Get command
case GET_CMD :
case GET_CMD :
for ( token_size = 0 ; token_size < ( file_size - ! ( bb - buffer ) ) ; token_size + + ) {
if ( ! cmd ) {
if ( bb [ token_size ] = = ' : ' )
if ( ! ( cmd = malloc ( alloc_size = ( sizeof ( char ) * 128 ) ) ) )
die ( " malloc for cmd in parse_config_file(): " ) ;
memset ( cmd , 0 , alloc_size ) ;
} else if ( alloc_tmp > = alloc_size ) {
if ( ! ( cmd = realloc ( cmd , alloc_size = alloc_size * 2 ) ) )
die ( " realloc for cmd in parse_config_file(): " ) ;
memset ( & cmd [ alloc_size / 2 ] , 0 , alloc_size / 2 ) ;
}
for ( alloc_tmp = 0 ; bb [ alloc_tmp ] & & bb [ alloc_tmp ] ! = ' \n ' & &
alloc_tmp < alloc_size ; alloc_tmp + + ) ;
if ( ! bb [ alloc_tmp ] | | alloc_tmp = = alloc_size ) {
strncat ( cmd , bb , alloc_tmp ) ;
bb + = alloc_tmp ;
if ( block_state = = LAST_BL )
die ( " Command not finished before end of file " ) ;
else
block_state = NEW_BL ;
break ;
break ;
if ( bb [ token_size ] = = ' \n ' & & bb [ token_size - token_size ? 1 : 0 ] ! = ' \\ ' )
} else {
strncat ( cmd , bb , alloc_tmp ) ;
if ( ! ( bb [ alloc_tmp - 1 ] = = ' \\ ' ) )
parse_state = LAST ;
bb + = alloc_tmp + 1 ;
linenum + + ;
break ;
break ;
}
}
cmd = malloc ( token_size + 1 ) ;
if ( ! cmd )
die ( wrap_err ( " Bad malloc parsing command: " ) ) ;
memcpy ( cmd , bb , token_size ) ;
cmd [ token_size ] = ' \0 ' ;
bb + = token_size ;
parse_state = LAST ;
break ;
break ;
case LAST :
case LAST :
if ( ! keys )
if ( ! keys )
die ( wrap_err ( " Keys is NULL " ) ) ;
die ( " error " ) ;
i_tmp = strlen ( keys ) ;
i_tmp = strlen ( keys ) ;
for ( int i = 0 ; i < i_tmp ; i + + ) {
for ( int i = 0 ; i < i_tmp ; i + + ) {
if ( isblank ( keys [ i ] ) )
if ( isblank ( keys [ i ] ) ) {
memmove ( & keys [ i ] , & keys [ i + 1 ] , i_tmp - i ) ;
memmove ( & keys [ i ] , & keys [ i + 1 ] , - - i_tmp ) ;
keys [ i_tmp ] = ' \0 ' ;
}
}
}
cp_tmp = strtok ( keys , " , " ) ;
cp_tmp = strtok ( keys , " , " ) ;
if ( ! cp_tmp )
if ( ! cp_tmp )
die ( wrap_err ( " Error at line %d: "
die ( " Error at line %d: "
" keys not present " ) , linenum - 1 ) ;
" keys not present " , linenum - 1 ) ;
if ( type ! = ALIAS ) {
if ( type ! = ALIAS ) {
do {
do {
if ( ! ( us_tmp = key_to_code ( cp_tmp ) ) ) {
if ( ( ks_tmp = xkb_keysym_from_name ( cp_tmp , XKB_KEYSYM_NO_FLAGS ) ) = = XKB_KEY_NoSymbol ) {
die ( wrap_err ( " Error at line %d: "
die ( " Error at line %d: "
" %s is not a valid key " ) ,
" %s is not a valid key " ,
linenum - 1 , cp_tmp ) ;
linenum - 1 , cp_tmp ) ;
}
}
if ( key_buffer_add ( & dt . kb , u s_tmp) )
if ( key_buffer_add ( & dt . kb , k s_tmp) )
die ( wrap_err ( " Too many keys " ) ) ;
die ( " Too many keys " ) ;
} while ( ( cp_tmp = strtok ( NULL , " , " ) ) ) ;
} while ( ( cp_tmp = strtok ( NULL , " , " ) ) ) ;
} else {
} else {
if ( ! ( dt . name = malloc ( strlen ( cp_tmp ) + 1 ) ) )
if ( ! ( dt . name = malloc ( strlen ( cp_tmp ) + 1 ) ) )
die ( wrap_err ( " Bad malloc:" ) ) ;
die ( " malloc in parse_config_file() : " ) ;
strcpy ( dt . name , cp_tmp ) ;
strcpy ( dt . name , cp_tmp ) ;
}
}
/* search the command in the known aliases and replace */
struct hotkey_list_e * hkl = hotkey_list ;
while ( hkl & & hkl - > fuzzy = = ALIAS ) {
replace ( & cmd , hkl - > data . name , hkl - > command ) ;
hkl = hkl - > next ;
}
cp_tmp = cmd ;
cp_tmp = cmd ;
while ( isblank ( * cp_tmp ) )
while ( isblank ( * cp_tmp ) )
cp_tmp + + ;
cp_tmp + + ;
if ( * cp_tmp = = ' \0 ' )
if ( * cp_tmp = = ' \0 ' )
die ( wrap_err ( " Error at line %d: "
die ( " Error at line %d: "
" command not present " ) , linenum - 1 ) ;
" command not present " , linenum - 1 ) ;
if ( cmd_is_alias ) {
struct hotkey_list_e * hkl = hotkey_list ;
// stolen way of removing leading spaces
char * end = cp_tmp + strlen ( cp_tmp ) - 1 ;
while ( end > cp_tmp & & isspace ( ( unsigned char ) * end ) ) end - - ;
end [ 1 ] = ' \0 ' ;
while ( hkl & & ! ( hkl - > fuzzy = = ALIAS & & strstr ( hkl - > data . name , cp_tmp ) ) )
hkl = hkl - > next ;
if ( hkl ) {
cp_tmp = hkl - > command ;
} else {
die ( " Error at line %d: "
" alias %s not found " , linenum - 1 ,
cp_tmp ) ;
}
}
hotkey_list_add ( hotkey_list , & dt , cp_tmp , type ) ;
hotkey_list_add ( hotkey_list , & dt , cp_tmp , type ) ;
@ -791,23 +889,24 @@ void parse_config_file (void)
parse_state = NORM ;
parse_state = NORM ;
break ;
break ;
default :
default :
die ( wrap_err ( " Unknown state " ) ) ;
die ( " Unknown state in parse_config_file " ) ;
break ;
break ;
}
}
}
}
munmap ( buffer , file_size ) ;
}
for ( struct hotkey_list_e * hkl = hotkey_list , * tmp ; hkl ; hkl = hkl - > next ) {
for ( struct hotkey_list_e * hkl = hotkey_list , * tmp ; hkl ; ) {
if ( hkl - > fuzzy = = ALIAS ) {
tmp = hkl ;
tmp = hkl ;
hkl = hkl - > next ;
hkl = hkl - > next ;
if ( tmp - > fuzzy = = ALIAS )
hotkey_list_remove ( hotkey_list , tmp ) ;
hotkey_list_remove ( hotkey_list , tmp ) ;
else
} else {
hotkey_size_mask | = 1 < < ( tmp - > data . kb . size - 1 ) ;
hotkey_size_mask | = 1 < < ( hkl - > data . kb . size - 1 ) ;
}
}
}
}
}
/*
unsigned short key_to_code ( char * key )
unsigned short key_to_code ( char * key )
{
{
for ( char * tmp = key ; * tmp ; tmp + + ) {
for ( char * tmp = key ; * tmp ; tmp + + ) {
@ -820,6 +919,7 @@ unsigned short key_to_code (char *key)
}
}
return 0 ;
return 0 ;
}
}
*/
void remove_lock ( void )
void remove_lock ( void )
{
{
@ -846,6 +946,7 @@ void die(const char *fmt, ...)
exit ( errno ? errno : 1 ) ;
exit ( errno ? errno : 1 ) ;
}
}
/*
const char * code_to_name ( unsigned int code )
const char * code_to_name ( unsigned int code )
{
{
for ( int i = 0 ; i < array_size_const ( key_conversion_table ) ; i + + ) {
for ( int i = 0 ; i < array_size_const ( key_conversion_table ) ; i + + ) {
@ -854,6 +955,7 @@ const char * code_to_name (unsigned int code)
}
}
return " Key not recognized " ;
return " Key not recognized " ;
}
}
*/
void usage ( void )
void usage ( void )
{
{
@ -864,47 +966,3 @@ void usage (void)
" \t -c file uses the specified file as config \n " ) ;
" \t -c file uses the specified file as config \n " ) ;
exit ( EXIT_SUCCESS ) ;
exit ( EXIT_SUCCESS ) ;
}
}
/* replaces every instance of m(match) with r(eplace) inside of s */
void replace ( char * * s , const char * m , const char * r )
{
if ( is_empty ( s ) | | is_empty ( * s ) | | is_empty ( m ) | | is_empty ( r ) )
return ;
int ms = strlen ( m ) , rs = strlen ( r ) ;
int count = 0 , o = 0 ;
int * offs = NULL , * t2 = NULL ;
char * t1 = NULL ;
while ( ( t1 = strstr ( ( * s ) + o , m ) ) ) {
/* check if the match is surrounded by whitespace */
if ( ( t1 [ ms ] = = ' \0 ' | | isblank ( t1 [ ms ] ) )
& & isblank ( t1 > * s ? * ( t1 - 1 ) : ' ' ) ) {
if ( ! ( t2 = realloc ( offs , sizeof ( int ) * ( count + 1 ) ) ) )
die ( wrap_err ( " Bad realloc: " ) ) ;
offs = t2 ;
offs [ count ] = ( t1 - * s ) + ( rs - ms ) * count ;
count + + ;
}
o = ( t1 - * s ) + 1 ;
}
if ( ! offs )
return ;
int nss = strlen ( * s ) ;
if ( ( rs - ms ) > 0 ) {
if ( ! ( t1 = realloc ( * s , nss + 1 + ( rs - ms ) * count ) ) )
die ( wrap_err ( " Bad realloc: " ) ) ;
* s = t1 ;
}
for ( int i = 0 ; i < count ; i + + ) {
char * x = * s + offs [ i ] ;
int d = strlen ( x ) - ms ;
memmove ( x + rs , x + ms , d ) ;
memcpy ( x , r , rs ) ;
}
if ( offs )
free ( offs ) ;
}