Mercurial > audlegacy-plugins
diff src/evdev-plug/ed_internals.c @ 422:5e46b57d1eda trunk
[svn] - added evdev-plug, written-from-scratch plugin that allows to control the player via event devices on linux systems
author | giacomo |
---|---|
date | Sun, 14 Jan 2007 17:55:24 -0800 |
parents | |
children | c18fd1befd1f |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/evdev-plug/ed_internals.c Sun Jan 14 17:55:24 2007 -0800 @@ -0,0 +1,1005 @@ +/* +* +* Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007 +* +* 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, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +*/ + +#include "ed_types.h" +#include "ed_internals.h" +#include "ed_actions.h" +#include "ed_bindings_store.h" +#include "ed_common.h" + +#include <stdint.h> +#include <stdio.h> +#include <linux/input.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <regex.h> +/* for variadic */ +#include <stdarg.h> + +#include <glib/gi18n.h> + + +static gboolean ed_device_giofunc ( GIOChannel * , GIOCondition , gpointer ); + +static gint ed_util_get_data_from_keyfile( GKeyFile * , gchar * , ... ); +static gpointer ed_util_get_bindings_from_keyfile( GKeyFile * , gchar * ); + +extern GList *ed_device_listening_list; + + +/* ***************** */ +/* internals */ + +ed_device_info_t * +ed_device_info_new ( gchar * device_name , gchar * device_filename , + gchar * device_phys , gint device_is_custom ) +{ + ed_device_info_t *info = g_malloc(sizeof(ed_device_info_t)); + info->name = g_strdup(device_name); + info->filename = g_strdup(device_filename); + info->phys = g_strdup(device_phys); + info->is_custom = device_is_custom; + info->is_active = FALSE; + info->bindings = NULL; + info->reg = 0; + return info; +} + + +gint +ed_device_info_delete ( ed_device_info_t * info ) +{ + g_free( info->phys ); + g_free( info->filename ); + g_free( info->name ); + g_free( info ); + return 0; +} + + +ed_device_t * +ed_device_new ( gchar * device_name , gchar * device_filename , + gchar * device_phys , gint device_is_custom ) +{ + ed_device_t *event_device; + GIOChannel *iochan; + gint fd; + + fd = g_open( device_filename , O_RDONLY , 0 ); + if ( fd < 0 ) + { + /* an error occurred */ + g_warning( _("event-device-plugin: unable to open device file %s , skipping this device; check that " + "the file exists and that you have read permission for it\n") , device_filename ); + return NULL; + } + + iochan = g_io_channel_unix_new( fd ); + if ( iochan == NULL ) + { + /* an error occurred */ + g_warning( _("event-device-plugin: unable to create a io_channel for device file %s ," + "skipping this device\n") , device_filename ); + close( fd ); + return NULL; + } + g_io_channel_set_encoding( iochan , NULL , NULL ); /* binary data */ + + event_device = g_malloc(sizeof(ed_device_t)); + event_device->fd = fd; + event_device->iochan = iochan; + event_device->is_listening = FALSE; + event_device->info = ed_device_info_new( + device_name , device_filename , device_phys , device_is_custom ); + + return event_device; +} + + +gint +ed_device_delete ( ed_device_t * event_device ) +{ + if ( event_device->is_listening ) + ed_device_stop_listening( event_device ); + + g_io_channel_shutdown( event_device->iochan , TRUE , NULL ); + g_io_channel_unref( event_device->iochan ); + close( event_device->fd ); + + ed_device_info_delete( event_device->info ); + g_free( event_device ); + + return 0; +} + + +gboolean +ed_inputevent_check_equality( ed_inputevent_t *iev1 , ed_inputevent_t *iev2 ) +{ + if (( iev1 == NULL ) || ( iev2 == NULL )) + { + if (( iev1 == NULL ) && ( iev2 == NULL )) + return TRUE; + else + return FALSE; + } + + if ( ( iev1->code == iev2->code ) && + ( iev1->type == iev2->type ) && + ( iev1->value == iev2->value ) ) + return TRUE; + else + return FALSE; +} + + +gboolean +ed_device_info_check_equality( ed_device_info_t *info1 , ed_device_info_t *info2 ) +{ + if (( info1 == NULL ) || ( info2 == NULL )) + { + if (( info1 == NULL ) && ( info2 == NULL )) + return TRUE; + else + return FALSE; + } + + if ( ( strcmp(info1->name,info2->name) == 0 ) && + ( strcmp(info1->filename,info2->filename) == 0 ) && + ( strcmp(info1->phys,info2->phys) == 0 ) && + ( info1->is_custom == info2->is_custom ) ) + return TRUE; + else + return FALSE; +} + + +gint +ed_device_start_listening ( ed_device_t * event_device ) +{ + if ( g_list_find( ed_device_listening_list , event_device ) != NULL ) + { + DEBUGMSG( "called start listening for device \"%s\" ( %s - %s ) but device listening is already active!\n" , + event_device->info->name , event_device->info->filename , event_device->info->phys ); + return -1; /* device listening is already active, do nothing */ + } + else + { + DEBUGMSG( "start listening for device \"%s\" ( %s - %s )\n" , + event_device->info->name , event_device->info->filename , event_device->info->phys ); + /* add a watch that checks if there's data to read */ + event_device->iochan_sid = g_io_add_watch( event_device->iochan , G_IO_IN , + (GIOFunc)ed_device_giofunc , event_device ); + + /* add event_device to the list */ + ed_device_listening_list = g_list_append( ed_device_listening_list , event_device ); + event_device->is_listening = TRUE; + return 0; + } +} + + +gint +ed_device_stop_listening ( ed_device_t * event_device ) +{ + if ( ( g_list_find( ed_device_listening_list , event_device ) != NULL ) && + ( event_device->is_listening == TRUE ) ) + { + DEBUGMSG( "stop listening for device \"%s\" ( %s - %s )\n" , + event_device->info->name , event_device->info->filename , event_device->info->phys ); + g_source_remove( event_device->iochan_sid ); + ed_device_listening_list = g_list_remove( ed_device_listening_list , event_device ); + event_device->is_listening = FALSE; + return 0; + } + else + { + DEBUGMSG( "called stop listening for device \"%s\" ( %s - %s ) but device listening is not active!\n" , + event_device->info->name , event_device->info->filename , event_device->info->phys ); + return -1; + } +} + + +gint +ed_device_stop_listening_from_info ( ed_device_info_t * info ) +{ + GList *list_iter = ed_device_listening_list; + while ( list_iter != NULL ) + { + ed_device_t *dev = list_iter->data; + if ( ed_device_info_check_equality( dev->info , info ) == TRUE ) + { + ed_device_stop_listening( dev ); + return 0; + } + list_iter = g_list_next( list_iter ); + } + return -1; +} + + +gint +ed_device_stop_listening_all ( gboolean delete_bindings ) +{ + /* convenience function that stops listening for all + devices and also deletes bindings if requested */ + GList *list_iter = ed_device_listening_list; + while ( list_iter != NULL ) + { + ed_device_t *dev = list_iter->data; + + if (( delete_bindings == TRUE ) && ( dev->info->bindings != NULL )) + ed_bindings_store_delete( dev->info->bindings ); + + ed_device_delete( dev ); + + list_iter = g_list_next( list_iter ); + } +} + + +gboolean +ed_device_check_listening_from_info ( ed_device_info_t * info ) +{ + /* note: this must not alter the reg parameter of info */ + GList *list_iter = ed_device_listening_list; + while ( list_iter != NULL ) + { + ed_device_t *dev = list_iter->data; + if ( ed_device_info_check_equality( dev->info , info ) == TRUE ) + return TRUE; + list_iter = g_list_next( list_iter ); + } + return FALSE; +} + + +static gboolean +ed_device_giofunc ( GIOChannel * iochan , GIOCondition cond , gpointer event_device ) +{ + switch ( cond ) + { + case G_IO_IN: + { + gsize rb = 0; + struct input_event inputev; + + if ( g_io_channel_read_chars( iochan , (gchar*)&inputev , + sizeof(struct input_event) , &rb , NULL ) == G_IO_STATUS_NORMAL ) + { + if ( rb == sizeof(struct input_event) ) + { + gint action_code = -1; + ed_device_t *dev = event_device; + + DEBUGMSG( "event (%d,%d,%d) intercepted for device \"%s\" ( %s - %s )\n" , + inputev.type , inputev.code , inputev.value , + dev->info->name , dev->info->filename , dev->info->phys ); + + if ( dev->info->bindings != NULL ) + { + ed_inputevent_t ev; + ev.type = inputev.type; + ev.code = inputev.code; + ev.value = inputev.value; + + /* lookup event type/code/value in the binding tree for this device */ + if ( ed_bindings_store_lookup( dev->info->bindings , &ev , &action_code ) == TRUE ) + { + /* this has been binded to an action, call the corresponding action */ + DEBUGMSG( "found action code %i for event (%d,%d,%d)\n" , + action_code , inputev.type , inputev.code , inputev.value ); + ed_action_call( action_code , NULL ); + } + } + } + } + break; + } + } + + return TRUE; +} + + +GList * +ed_device_get_list_from_system ( void ) +{ + GIOChannel *iochan; + gchar *buffer; + gsize buffer_len; + gint fd = -1; + + fd = g_open( "/proc/bus/input/devices" , O_RDONLY , 0 ); + if ( fd < 0 ) + { + /* an error occurred */ + g_warning( _("event-device-plugin: unable to open /proc/bus/input/devices , automatic " + "detection of event devices won't work.\n") ); + return NULL; + } + + iochan = g_io_channel_unix_new( fd ); + if ( iochan == NULL ) + { + /* an error occurred */ + g_warning( _("event-device-plugin: unable to open a io_channel for /proc/bus/input/devices , " + "automatic detection of event devices won't work.\n") ); + close( fd ); + return NULL; + } + g_io_channel_set_encoding( iochan , "UTF-8" , NULL ); /* utf-8 text */ + + if ( g_io_channel_read_to_end( iochan , &buffer , &buffer_len , NULL ) != G_IO_STATUS_NORMAL ) + { + /* an error occurred */ + g_warning( _("event-device-plugin: an error occurred while reading /proc/bus/input/devices , " + "automatic detection of event devices won't work.\n") ); + g_io_channel_shutdown( iochan , TRUE , NULL ); + g_io_channel_unref( iochan ); + close( fd ); + return NULL; + } + else + { + regex_t preg; + gint search_offset = 0; + GList *system_devices_list = NULL; + + /* we don't need these anymore */ + g_io_channel_shutdown( iochan , TRUE , NULL ); + g_io_channel_unref( iochan ); + close( fd ); + + /* parse content of /proc/bus/input/devices */ + regcomp( &preg, + "I:[^\n]*\nN: Name=\"([^\n]*)\"\nP: Phys=([^\n]*)\n[^\n]+\nH: Handlers=[^\n]*(event[0-9]+)[^\n]*\n" , + REG_ICASE | REG_EXTENDED ); + + while ( search_offset > -1 ) + { + size_t nmatch = 4; + regmatch_t submatch[4]; + + if ( regexec( &preg , &buffer[search_offset] , nmatch , submatch , 0 ) == 0 ) + { + GString *device_name = NULL; + GString *device_phys = NULL; + GString *device_file = NULL; + + if ( submatch[1].rm_so != -1 ) /* check validity of name sub-expression */ + { + device_name = g_string_new( "" ); + g_string_append_len( device_name , + &buffer[(search_offset + submatch[1].rm_so)] , + submatch[1].rm_eo - submatch[1].rm_so ); + } + + if ( submatch[2].rm_so != -1 ) /* check validity of physicalport sub-expression */ + { + device_phys = g_string_new( "" ); + g_string_append_len( device_phys , + &buffer[(search_offset + submatch[2].rm_so)] , + submatch[2].rm_eo - submatch[2].rm_so ); + } + + if ( submatch[3].rm_so != -1 ) /* check validity of filename sub-expression */ + { + device_file = g_string_new( "" ); + GString *device_test = g_string_new( "" ); + g_string_append_len( device_file , + &buffer[(search_offset + submatch[3].rm_so)] , + submatch[3].rm_eo - submatch[3].rm_so ); + + /* let's check if the filename actually exists in /dev */ + g_string_printf( device_test , "/dev/input/%s" , device_file->str ); + if ( !g_file_test( device_test->str , G_FILE_TEST_EXISTS ) ) + { + /* it doesn't exist, mark as invalid device by nullifying device_file*/ + g_warning( _("event-device-plugin: device %s not found in /dev/input , skipping.\n") , device_file ); + g_string_free( device_file , TRUE ); + device_file = NULL; + } + else + { + /* it does exist, mark as valid device by using the full path in device_file*/ + g_string_assign( device_file , device_test->str ); + } + g_string_free( device_test , TRUE ); + } + + if (( device_name != NULL ) && ( device_phys != NULL ) && ( device_file != NULL )) + { + /* add item to the list */ + ed_device_info_t *info = ed_device_info_new( + device_name->str , device_file->str , device_phys->str , 0 ); + info->reg = 0; + DEBUGMSG( "device found, name:\"%s\" , file \"%s\" , phys \"%s\"\n" , + info->name , info->filename , info->phys ); + system_devices_list = g_list_append( system_devices_list , info ); + } + + if ( device_name != NULL ) + g_string_free( device_name , TRUE ); + if ( device_phys != NULL ) + g_string_free( device_phys , TRUE ); + if ( device_file != NULL ) + g_string_free( device_file , TRUE ); + + search_offset += submatch[0].rm_eo; /* update offset for further search */ + } + else + { + /* no more valid devices found */ + search_offset = -1; + } + } + regfree( &preg ); + return system_devices_list; + } +} + + +GList * +ed_device_get_list_from_config ( void ) +{ + GKeyFile *keyfile = NULL; + GList *config_devices_list = NULL; + gboolean is_loaded = FALSE; + gchar **device_names = NULL; + gsize device_names_num = 0; + gchar *config_pathfilename = NULL; + gint i = 0; + + config_pathfilename = g_strjoin( "" , g_get_home_dir() , + "/" PLAYER_LOCALRC_DIR "/" PLAYER_LOCALRC_FILE , NULL ); + keyfile = g_key_file_new(); + is_loaded = g_key_file_load_from_file( keyfile , config_pathfilename , G_KEY_FILE_NONE , NULL ); + g_free( config_pathfilename ); + + if ( is_loaded != TRUE ) + { + g_warning( _("event-device-plugin: unable to load config file %s , default settings will be used.\n") , + PLAYER_LOCALRC_FILE ); + g_key_file_free( keyfile ); + return NULL; + } + + /* remove ___plugin___ group that contains plugin settings */ + g_key_file_remove_group( keyfile , "___plugin___" , NULL ); + + /* the other groups are devices; check them and run active ones */ + device_names = g_key_file_get_groups( keyfile , &device_names_num ); + while ( device_names[i] != NULL ) + { + gint device_is_custom = 0; + gchar *device_file = NULL; + gchar *device_phys = NULL; + gboolean device_is_active = FALSE; + gint result = 0; + + result = ed_util_get_data_from_keyfile( + keyfile , device_names[i] , + ED_CONFIG_INFO_FILENAME , &device_file , + ED_CONFIG_INFO_PHYS , &device_phys , + ED_CONFIG_INFO_ISCUSTOM , &device_is_custom , + ED_CONFIG_INFO_ISACTIVE , &device_is_active , + ED_CONFIG_INFO_END ); + + if ( result == 0 ) + { + /* all information succesfully retrieved from config, create a ed_device_info_t */ + ed_device_info_t *info; + + info = ed_device_info_new( device_names[i] , device_file , + device_phys , device_is_custom ); + + /* pick bindings for this device */ + info->bindings = ed_util_get_bindings_from_keyfile( keyfile , device_names[i] ); + info->is_active = device_is_active; + + /* add this device to the config list */ + config_devices_list = g_list_append( config_devices_list , info ); + /* free information from config file, info has its own copies */ + g_free( device_file ); g_free( device_phys ); + } + else + { + g_warning( _("event-device-plugin: incomplete information in config file for device \"%s\"" + " , skipping.\n") , device_names[i] ); + } + + i++; /* on with next */ + } + + g_strfreev( device_names ); + g_key_file_free( keyfile ); + return config_devices_list; +} + + +void +ed_device_free_list ( GList * system_devices_list ) +{ + GList *list_iter = system_devices_list; + while ( list_iter != NULL ) + { + ed_device_info_delete( (ed_device_info_t*)list_iter->data ); + list_iter = g_list_next( list_iter ); + } + g_list_free( system_devices_list ); + return; +} + + +void +ed_device_start_listening_from_config ( void ) +{ + GKeyFile *keyfile = NULL; + gboolean is_loaded = FALSE; + gchar **device_names = NULL; + gsize device_names_num = 0; + gchar *config_pathfilename = NULL; + GList *system_devices_list = NULL; + gint i = 0; + + config_pathfilename = g_strjoin( "" , g_get_home_dir() , + "/" PLAYER_LOCALRC_DIR "/" PLAYER_LOCALRC_FILE , NULL ); + keyfile = g_key_file_new(); + is_loaded = g_key_file_load_from_file( keyfile , config_pathfilename , G_KEY_FILE_NONE , NULL ); + g_free( config_pathfilename ); + + if ( is_loaded != TRUE ) + { + g_warning( _("event-device-plugin: unable to load config file %s , default settings will be used.\n") , + PLAYER_LOCALRC_FILE ); + g_key_file_free( keyfile ); + return; + } + + system_devices_list = ed_device_get_list_from_system(); + + /* remove ___plugin___ group that contains plugin settings */ + g_key_file_remove_group( keyfile , "___plugin___" , NULL ); + + /* check available devices and run active ones */ + device_names = g_key_file_get_groups( keyfile , &device_names_num ); + while ( device_names[i] != NULL ) + { + GError *gerr = NULL; + gboolean is_active; + + is_active = g_key_file_get_boolean( keyfile , device_names[i] , "is_active" , &gerr ); + if ( gerr != NULL ) + { + g_warning( _("event-device-plugin: configuration, unable to get is_active value for device \"%s\"" + ", skipping it.\n") , device_names[i] ); + g_clear_error( &gerr ); + } + + if ( is_active == TRUE ) /* only care about active devices at this time, ignore others */ + { + gint is_custom = 0; + gchar *device_file = NULL; + gchar *device_phys = NULL; + gint result = 0; + + result = ed_util_get_data_from_keyfile( + keyfile , device_names[i] , + ED_CONFIG_INFO_FILENAME , &device_file , + ED_CONFIG_INFO_PHYS , &device_phys , + ED_CONFIG_INFO_ISCUSTOM , &is_custom , + ED_CONFIG_INFO_END ); + + if ( result != 0 ) + { + /* something wrong, skip this device */ + i++; continue; + } + + /* unless this is a custom device, perform a device check */ + if ( is_custom != 1 ) + { + /* not a custom device, check it against system_devices_list + to see if its information should be updated or if it's not plugged at all */ + gint check_result = ed_device_check( + system_devices_list , device_names[i] , &device_file , &device_phys ); + + if ( check_result == ED_DEVCHECK_OK ) + { + /* ok, we have an active not-custom device and it has been successfully + checked too; create a ed_device_t item for it */ + ed_device_t *dev = ed_device_new ( device_names[i] , device_file , device_phys , 0 ); + g_free( device_file ); g_free( device_phys ); /* not needed anymore */ + if ( dev != NULL ) + { + dev->info->bindings = ed_util_get_bindings_from_keyfile( keyfile , device_names[i] ); + ed_device_start_listening ( dev ); + } + } + + /* note: if check_result == ED_DEVCHECK_ABSENT, we simply skip this device */ + } + else + { + /* ok, we have an active custom device; create a ed_device_t item for it */ + ed_device_t *dev = ed_device_new ( device_names[i] , device_file , device_phys , 1 ); + g_free( device_file ); g_free( device_phys ); /* not needed anymore */ + if ( dev != NULL ) + { + dev->info->bindings = ed_util_get_bindings_from_keyfile( keyfile , device_names[i] ); + ed_device_start_listening ( dev ); + } + } + } + + /* on with next device name */ + i++; + } + + g_strfreev( device_names ); + ed_device_free_list( system_devices_list ); + g_key_file_free( keyfile ); + return; +} + + +/* this function checks that a given event device (with device_name, + device_phys and device_file) exists in system_devices_list; device_phys + and device_file must be dynamically-allocated string, they could + be freed and reallocated if their information needs to be updated; + it returns an integer, its value represents the performed operations */ +gint +ed_device_check ( GList * system_devices_list , + gchar * device_name , + gchar ** device_file , + gchar ** device_phys ) +{ + /* first, search in the list for a device, named device_name, + that has not been found in a previous ed_device_check + made with the same system_devices_list (info->reg == 0) */ + GList *list_iter = system_devices_list; + + while ( list_iter != NULL ) + { + ed_device_info_t *info = list_iter->data; + + if ( ( info->reg == 0 ) && ( strcmp( device_name , info->name ) == 0 ) ) + { + /* found a device, check if it has the same physical address */ + if ( strcmp( *device_phys , info->phys ) == 0 ) + { + /* good, same device name and same physical + address; update device_file if necessary */ + if ( strcmp( *device_file , info->filename ) != 0 ) + { + g_free( *device_file ); + *device_file = g_strdup( info->filename ); + } + /* now mark it as "found" so it won't be searched in next + ed_device_check made with the same system_devices_list*/ + info->reg = 1; + /* everything done */ + return ED_DEVCHECK_OK; + } + else + { + /* device found, but physical address is not the one from *device_phys; try to + search further in system_devices_list for a device with same name and address */ + GList *list_iter2 = g_list_next(list_iter); + while ( list_iter2 != NULL ) + { + ed_device_info_t *info2 = list_iter2->data; + if ( ( info2->reg == 0 ) && + ( strcmp( device_name , info2->name ) == 0 ) && + ( strcmp( *device_phys , info2->phys ) == 0 ) ) + { + /* found a device with the same name and address, + so let's use it; update device_file if necessary */ + if ( strcmp( *device_file , info2->filename ) != 0 ) + { + g_free( *device_file ); + *device_file = g_strdup( info2->filename ); + } + /* now mark it as "found" so it won't be searched in next + ed_device_check made with the same system_devices_list */ + info2->reg = 1; + /* everything done */ + return ED_DEVCHECK_OK; + } + list_iter2 = g_list_next(list_iter2); + } + + /* if we get to this point, it means that there isn't any device named + device_name with physical address equal to *device_phys ; there is only + one (or more) device named device_name but with different physical + address; we'll use the first of those (alas the current content of info) */ + g_free( *device_phys ); /* free outdated device_phys */ + *device_phys = g_strdup( info->phys ); /* update it with the new one */ + + /* update device_file if necessary */ + if ( strcmp( *device_file , info->filename ) != 0 ) + { + g_free( *device_file ); + *device_file = g_strdup( info->filename ); + } + + /* now mark it as "found" so it won't be searched in next + ed_device_check made with the same system_devices_list*/ + info->reg = 1; + /* everything done */ + return ED_DEVCHECK_OK; + } + } + + list_iter = g_list_next(list_iter); + } + + /* the entire system_devices_list was searched, + but no device named device_name was found */ + return ED_DEVCHECK_ABSENT; +} + + + +/* config */ +static void +ed_config_save_from_list_bindings_foreach ( ed_inputevent_t * iev , + gint action_code , + gpointer keyfile , + gpointer info_gp ) +{ + gint int_list[4]; + gchar *keyname; + ed_device_info_t *info = info_gp; + keyname = g_strdup_printf( "b%i" , info->reg ); + int_list[0] = action_code; + int_list[1] = iev->type; + int_list[2] = iev->code; + int_list[3] = iev->value; + g_key_file_set_integer_list( keyfile , info->name , keyname , int_list , 4 ); + g_free( keyname ); + info->reg++; + return; +} + +gint +ed_config_save_from_list ( GList * config_devices_list ) +{ + GKeyFile *keyfile; + GList *iter_list = NULL; + gchar *keyfile_str = NULL; + gsize keyfile_str_len = 0; + GIOChannel *iochan; + gchar *config_pathfilename = NULL; + + config_pathfilename = g_strjoin( "" , g_get_home_dir() , + "/" PLAYER_LOCALRC_DIR "/" PLAYER_LOCALRC_FILE , NULL ); + + keyfile = g_key_file_new(); + + g_key_file_set_string( keyfile , "___plugin___" , "config_ver" , ED_VERSION_CONFIG ); + + iter_list = config_devices_list; + while ( iter_list != NULL ) + { + ed_device_info_t *info = iter_list->data; + g_key_file_set_string( keyfile , info->name , "filename" , info->filename ); + g_key_file_set_string( keyfile , info->name , "phys" , info->phys ); + g_key_file_set_boolean( keyfile , info->name , "is_active" , info->is_active ); + g_key_file_set_integer( keyfile , info->name , "is_custom" , info->is_custom ); + /* use the info->reg field as a counter to list actions */ + info->reg = 0; /* init the counter */ + if ( info->bindings != NULL ) + ed_bindings_store_foreach( info->bindings , + ed_config_save_from_list_bindings_foreach , keyfile , info ); + iter_list = g_list_next( iter_list ); + } + + keyfile_str = g_key_file_to_data( keyfile , &keyfile_str_len , NULL ); + iochan = g_io_channel_new_file( config_pathfilename , "w" , NULL ); + g_io_channel_set_encoding( iochan , "UTF-8" , NULL ); + g_io_channel_write_chars( iochan , keyfile_str , keyfile_str_len , NULL , NULL ); + g_io_channel_shutdown( iochan , TRUE , NULL ); + g_io_channel_unref( iochan ); + + g_free( keyfile_str ); + g_key_file_free( keyfile ); + return 0; +} + + +/* utils */ + + +/* this picks information from a keyfile, using device_name + as group name; information must be requested by passing + a ed_config_info_t value and a corresponding container; + list of requested information must be terminated with + ED_CONFIG_INFO_END; returns 0 if everything is found, + returns negative values if some information is missing */ +static gint +ed_util_get_data_from_keyfile( GKeyFile * keyfile , gchar * device_name , ... ) +{ + GError *gerr = NULL; + gboolean is_failed = FALSE; + ed_config_info_t info_code = ED_CONFIG_INFO_END; + GList *temp_stringstore = NULL; + va_list ap; + + /* when we get a string value from g_key_file_get_string, we temporarily + store its container in temp_stringstore; if subsequent information + requests in the iteraton fails, we free the information in previous + container and nullify its content; + this way, user will get complete information (return value 0) or + absent information (all string containers nullified, return value -1) */ + + va_start( ap, device_name ); + + while ( ( is_failed == FALSE ) && + ( ( info_code = va_arg( ap , ed_config_info_t ) ) != ED_CONFIG_INFO_END ) ) + { + switch ( info_code ) + { + case ED_CONFIG_INFO_FILENAME: + { + gchar **device_file = va_arg( ap , gchar ** ); + *device_file = g_key_file_get_string( keyfile , device_name , "filename" , &gerr ); + if ( gerr != NULL ) + { + g_clear_error( &gerr ); + g_warning( _("event-device-plugin: configuration, unable to get filename value for device \"%s\"" + ", skipping it.\n") , device_name ); + is_failed = TRUE; + } + else + temp_stringstore = g_list_append( temp_stringstore , device_file ); + break; + } + + case ED_CONFIG_INFO_PHYS: + { + gchar **device_phys = va_arg( ap , gchar ** ); + *device_phys = g_key_file_get_string( keyfile , device_name , "phys" , &gerr ); + if ( gerr != NULL ) + { + g_clear_error( &gerr ); + g_warning( _("event-device-plugin: configuration, unable to get phys value for device \"%s\"" + ", skipping it.\n") , device_name ); + is_failed = TRUE; + } + else + temp_stringstore = g_list_append( temp_stringstore , device_phys ); + break; + } + + case ED_CONFIG_INFO_ISCUSTOM: + { + gint *is_custom = va_arg( ap , gint * ); + *is_custom = g_key_file_get_integer( keyfile , device_name , "is_custom" , &gerr ); + if ( gerr != NULL ) + { + g_clear_error( &gerr ); + g_warning( _("event-device-plugin: configuration, unable to get is_custom value for device \"%s\"" + ", skipping it.\n") , device_name ); + is_failed = TRUE; + } + break; + } + + case ED_CONFIG_INFO_ISACTIVE: + { + gboolean *is_active = va_arg( ap , gboolean * ); + *is_active = g_key_file_get_boolean( keyfile , device_name , "is_active" , &gerr ); + if ( gerr != NULL ) + { + g_clear_error( &gerr ); + g_warning( _("event-device-plugin: configuration, unable to get is_active value for device \"%s\"" + ", skipping it.\n") , device_name ); + is_failed = TRUE; + } + break; + } + + default: + { + /* unexpected value in info_code, skipping */ + g_warning( _("event-device-plugin: configuration, unexpected value for device \"%s\"" + ", skipping it.\n") , device_name ); + is_failed = TRUE; + } + } + } + + va_end( ap ); + + if ( is_failed == FALSE ) + { + /* temp_stringstore is not needed anymore, + do not change pointed containers */ + g_list_free( temp_stringstore ); + return 0; + } + else + { + /* temp_stringstore is not needed anymore, + nullify pointed containers and free content */ + GList *list_iter = temp_stringstore; + while ( list_iter != NULL ) + { + gchar **container = list_iter->data; + g_free( *container ); + *container = NULL; + list_iter = g_list_next( list_iter ); + } + g_list_free( temp_stringstore ); + return -1; + } +} + + +/* this does just what its name says :) */ +static gpointer +ed_util_get_bindings_from_keyfile( GKeyFile * keyfile , gchar * device_name ) +{ + ed_inputevent_t *iev = g_malloc(sizeof(ed_inputevent_t)); + gpointer bindings = ed_bindings_store_new(); + gchar **keys; + gint j = 0; + + /* now get bindings for this device */ + keys = g_key_file_get_keys( keyfile , device_name , NULL , NULL ); + while ( keys[j] != NULL ) + { + /* in the config file, only bindings start with the 'b' character */ + if ( keys[j][0] == 'b' ) + { + gsize ilist_len = 0; + gint *ilist; + ilist = g_key_file_get_integer_list( keyfile , + device_name , keys[j] , &ilist_len , NULL ); + if ( ilist_len > 3 ) + { + gint action_code = (gint)ilist[0]; + iev->type = (guint)ilist[1]; + iev->code = (guint)ilist[2]; + iev->value = (gint)ilist[3]; + ed_bindings_store_insert( bindings , iev , action_code ); + } + g_free( ilist ); + } + j++; + } + + g_strfreev( keys ); + g_free( iev ); + + if ( ed_bindings_store_size( bindings ) == 0 ) + { + ed_bindings_store_delete( bindings ); + bindings = NULL; + } + + return bindings; +}