Mercurial > audlegacy-plugins
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 421:1a82af4f13cf | 422:5e46b57d1eda |
|---|---|
| 1 /* | |
| 2 * | |
| 3 * Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007 | |
| 4 * | |
| 5 * This program is free software; you can redistribute it and/or modify it | |
| 6 * under the terms of the GNU General Public License as published by the | |
| 7 * Free Software Foundation; either version 2 of the License, or (at your | |
| 8 * option) any later version. | |
| 9 * | |
| 10 * This program is distributed in the hope that it will be useful, but | |
| 11 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| 13 * General Public License for more details. | |
| 14 * | |
| 15 * You should have received a copy of the GNU General Public License along | |
| 16 * with this program; if not, write to the Free Software Foundation, Inc., | |
| 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
| 18 * | |
| 19 */ | |
| 20 | |
| 21 #include "ed_types.h" | |
| 22 #include "ed_internals.h" | |
| 23 #include "ed_actions.h" | |
| 24 #include "ed_bindings_store.h" | |
| 25 #include "ed_common.h" | |
| 26 | |
| 27 #include <stdint.h> | |
| 28 #include <stdio.h> | |
| 29 #include <linux/input.h> | |
| 30 #include <sys/types.h> | |
| 31 #include <sys/stat.h> | |
| 32 #include <string.h> | |
| 33 #include <fcntl.h> | |
| 34 #include <unistd.h> | |
| 35 #include <regex.h> | |
| 36 /* for variadic */ | |
| 37 #include <stdarg.h> | |
| 38 | |
| 39 #include <glib/gi18n.h> | |
| 40 | |
| 41 | |
| 42 static gboolean ed_device_giofunc ( GIOChannel * , GIOCondition , gpointer ); | |
| 43 | |
| 44 static gint ed_util_get_data_from_keyfile( GKeyFile * , gchar * , ... ); | |
| 45 static gpointer ed_util_get_bindings_from_keyfile( GKeyFile * , gchar * ); | |
| 46 | |
| 47 extern GList *ed_device_listening_list; | |
| 48 | |
| 49 | |
| 50 /* ***************** */ | |
| 51 /* internals */ | |
| 52 | |
| 53 ed_device_info_t * | |
| 54 ed_device_info_new ( gchar * device_name , gchar * device_filename , | |
| 55 gchar * device_phys , gint device_is_custom ) | |
| 56 { | |
| 57 ed_device_info_t *info = g_malloc(sizeof(ed_device_info_t)); | |
| 58 info->name = g_strdup(device_name); | |
| 59 info->filename = g_strdup(device_filename); | |
| 60 info->phys = g_strdup(device_phys); | |
| 61 info->is_custom = device_is_custom; | |
| 62 info->is_active = FALSE; | |
| 63 info->bindings = NULL; | |
| 64 info->reg = 0; | |
| 65 return info; | |
| 66 } | |
| 67 | |
| 68 | |
| 69 gint | |
| 70 ed_device_info_delete ( ed_device_info_t * info ) | |
| 71 { | |
| 72 g_free( info->phys ); | |
| 73 g_free( info->filename ); | |
| 74 g_free( info->name ); | |
| 75 g_free( info ); | |
| 76 return 0; | |
| 77 } | |
| 78 | |
| 79 | |
| 80 ed_device_t * | |
| 81 ed_device_new ( gchar * device_name , gchar * device_filename , | |
| 82 gchar * device_phys , gint device_is_custom ) | |
| 83 { | |
| 84 ed_device_t *event_device; | |
| 85 GIOChannel *iochan; | |
| 86 gint fd; | |
| 87 | |
| 88 fd = g_open( device_filename , O_RDONLY , 0 ); | |
| 89 if ( fd < 0 ) | |
| 90 { | |
| 91 /* an error occurred */ | |
| 92 g_warning( _("event-device-plugin: unable to open device file %s , skipping this device; check that " | |
| 93 "the file exists and that you have read permission for it\n") , device_filename ); | |
| 94 return NULL; | |
| 95 } | |
| 96 | |
| 97 iochan = g_io_channel_unix_new( fd ); | |
| 98 if ( iochan == NULL ) | |
| 99 { | |
| 100 /* an error occurred */ | |
| 101 g_warning( _("event-device-plugin: unable to create a io_channel for device file %s ," | |
| 102 "skipping this device\n") , device_filename ); | |
| 103 close( fd ); | |
| 104 return NULL; | |
| 105 } | |
| 106 g_io_channel_set_encoding( iochan , NULL , NULL ); /* binary data */ | |
| 107 | |
| 108 event_device = g_malloc(sizeof(ed_device_t)); | |
| 109 event_device->fd = fd; | |
| 110 event_device->iochan = iochan; | |
| 111 event_device->is_listening = FALSE; | |
| 112 event_device->info = ed_device_info_new( | |
| 113 device_name , device_filename , device_phys , device_is_custom ); | |
| 114 | |
| 115 return event_device; | |
| 116 } | |
| 117 | |
| 118 | |
| 119 gint | |
| 120 ed_device_delete ( ed_device_t * event_device ) | |
| 121 { | |
| 122 if ( event_device->is_listening ) | |
| 123 ed_device_stop_listening( event_device ); | |
| 124 | |
| 125 g_io_channel_shutdown( event_device->iochan , TRUE , NULL ); | |
| 126 g_io_channel_unref( event_device->iochan ); | |
| 127 close( event_device->fd ); | |
| 128 | |
| 129 ed_device_info_delete( event_device->info ); | |
| 130 g_free( event_device ); | |
| 131 | |
| 132 return 0; | |
| 133 } | |
| 134 | |
| 135 | |
| 136 gboolean | |
| 137 ed_inputevent_check_equality( ed_inputevent_t *iev1 , ed_inputevent_t *iev2 ) | |
| 138 { | |
| 139 if (( iev1 == NULL ) || ( iev2 == NULL )) | |
| 140 { | |
| 141 if (( iev1 == NULL ) && ( iev2 == NULL )) | |
| 142 return TRUE; | |
| 143 else | |
| 144 return FALSE; | |
| 145 } | |
| 146 | |
| 147 if ( ( iev1->code == iev2->code ) && | |
| 148 ( iev1->type == iev2->type ) && | |
| 149 ( iev1->value == iev2->value ) ) | |
| 150 return TRUE; | |
| 151 else | |
| 152 return FALSE; | |
| 153 } | |
| 154 | |
| 155 | |
| 156 gboolean | |
| 157 ed_device_info_check_equality( ed_device_info_t *info1 , ed_device_info_t *info2 ) | |
| 158 { | |
| 159 if (( info1 == NULL ) || ( info2 == NULL )) | |
| 160 { | |
| 161 if (( info1 == NULL ) && ( info2 == NULL )) | |
| 162 return TRUE; | |
| 163 else | |
| 164 return FALSE; | |
| 165 } | |
| 166 | |
| 167 if ( ( strcmp(info1->name,info2->name) == 0 ) && | |
| 168 ( strcmp(info1->filename,info2->filename) == 0 ) && | |
| 169 ( strcmp(info1->phys,info2->phys) == 0 ) && | |
| 170 ( info1->is_custom == info2->is_custom ) ) | |
| 171 return TRUE; | |
| 172 else | |
| 173 return FALSE; | |
| 174 } | |
| 175 | |
| 176 | |
| 177 gint | |
| 178 ed_device_start_listening ( ed_device_t * event_device ) | |
| 179 { | |
| 180 if ( g_list_find( ed_device_listening_list , event_device ) != NULL ) | |
| 181 { | |
| 182 DEBUGMSG( "called start listening for device \"%s\" ( %s - %s ) but device listening is already active!\n" , | |
| 183 event_device->info->name , event_device->info->filename , event_device->info->phys ); | |
| 184 return -1; /* device listening is already active, do nothing */ | |
| 185 } | |
| 186 else | |
| 187 { | |
| 188 DEBUGMSG( "start listening for device \"%s\" ( %s - %s )\n" , | |
| 189 event_device->info->name , event_device->info->filename , event_device->info->phys ); | |
| 190 /* add a watch that checks if there's data to read */ | |
| 191 event_device->iochan_sid = g_io_add_watch( event_device->iochan , G_IO_IN , | |
| 192 (GIOFunc)ed_device_giofunc , event_device ); | |
| 193 | |
| 194 /* add event_device to the list */ | |
| 195 ed_device_listening_list = g_list_append( ed_device_listening_list , event_device ); | |
| 196 event_device->is_listening = TRUE; | |
| 197 return 0; | |
| 198 } | |
| 199 } | |
| 200 | |
| 201 | |
| 202 gint | |
| 203 ed_device_stop_listening ( ed_device_t * event_device ) | |
| 204 { | |
| 205 if ( ( g_list_find( ed_device_listening_list , event_device ) != NULL ) && | |
| 206 ( event_device->is_listening == TRUE ) ) | |
| 207 { | |
| 208 DEBUGMSG( "stop listening for device \"%s\" ( %s - %s )\n" , | |
| 209 event_device->info->name , event_device->info->filename , event_device->info->phys ); | |
| 210 g_source_remove( event_device->iochan_sid ); | |
| 211 ed_device_listening_list = g_list_remove( ed_device_listening_list , event_device ); | |
| 212 event_device->is_listening = FALSE; | |
| 213 return 0; | |
| 214 } | |
| 215 else | |
| 216 { | |
| 217 DEBUGMSG( "called stop listening for device \"%s\" ( %s - %s ) but device listening is not active!\n" , | |
| 218 event_device->info->name , event_device->info->filename , event_device->info->phys ); | |
| 219 return -1; | |
| 220 } | |
| 221 } | |
| 222 | |
| 223 | |
| 224 gint | |
| 225 ed_device_stop_listening_from_info ( ed_device_info_t * info ) | |
| 226 { | |
| 227 GList *list_iter = ed_device_listening_list; | |
| 228 while ( list_iter != NULL ) | |
| 229 { | |
| 230 ed_device_t *dev = list_iter->data; | |
| 231 if ( ed_device_info_check_equality( dev->info , info ) == TRUE ) | |
| 232 { | |
| 233 ed_device_stop_listening( dev ); | |
| 234 return 0; | |
| 235 } | |
| 236 list_iter = g_list_next( list_iter ); | |
| 237 } | |
| 238 return -1; | |
| 239 } | |
| 240 | |
| 241 | |
| 242 gint | |
| 243 ed_device_stop_listening_all ( gboolean delete_bindings ) | |
| 244 { | |
| 245 /* convenience function that stops listening for all | |
| 246 devices and also deletes bindings if requested */ | |
| 247 GList *list_iter = ed_device_listening_list; | |
| 248 while ( list_iter != NULL ) | |
| 249 { | |
| 250 ed_device_t *dev = list_iter->data; | |
| 251 | |
| 252 if (( delete_bindings == TRUE ) && ( dev->info->bindings != NULL )) | |
| 253 ed_bindings_store_delete( dev->info->bindings ); | |
| 254 | |
| 255 ed_device_delete( dev ); | |
| 256 | |
| 257 list_iter = g_list_next( list_iter ); | |
| 258 } | |
| 259 } | |
| 260 | |
| 261 | |
| 262 gboolean | |
| 263 ed_device_check_listening_from_info ( ed_device_info_t * info ) | |
| 264 { | |
| 265 /* note: this must not alter the reg parameter of info */ | |
| 266 GList *list_iter = ed_device_listening_list; | |
| 267 while ( list_iter != NULL ) | |
| 268 { | |
| 269 ed_device_t *dev = list_iter->data; | |
| 270 if ( ed_device_info_check_equality( dev->info , info ) == TRUE ) | |
| 271 return TRUE; | |
| 272 list_iter = g_list_next( list_iter ); | |
| 273 } | |
| 274 return FALSE; | |
| 275 } | |
| 276 | |
| 277 | |
| 278 static gboolean | |
| 279 ed_device_giofunc ( GIOChannel * iochan , GIOCondition cond , gpointer event_device ) | |
| 280 { | |
| 281 switch ( cond ) | |
| 282 { | |
| 283 case G_IO_IN: | |
| 284 { | |
| 285 gsize rb = 0; | |
| 286 struct input_event inputev; | |
| 287 | |
| 288 if ( g_io_channel_read_chars( iochan , (gchar*)&inputev , | |
| 289 sizeof(struct input_event) , &rb , NULL ) == G_IO_STATUS_NORMAL ) | |
| 290 { | |
| 291 if ( rb == sizeof(struct input_event) ) | |
| 292 { | |
| 293 gint action_code = -1; | |
| 294 ed_device_t *dev = event_device; | |
| 295 | |
| 296 DEBUGMSG( "event (%d,%d,%d) intercepted for device \"%s\" ( %s - %s )\n" , | |
| 297 inputev.type , inputev.code , inputev.value , | |
| 298 dev->info->name , dev->info->filename , dev->info->phys ); | |
| 299 | |
| 300 if ( dev->info->bindings != NULL ) | |
| 301 { | |
| 302 ed_inputevent_t ev; | |
| 303 ev.type = inputev.type; | |
| 304 ev.code = inputev.code; | |
| 305 ev.value = inputev.value; | |
| 306 | |
| 307 /* lookup event type/code/value in the binding tree for this device */ | |
| 308 if ( ed_bindings_store_lookup( dev->info->bindings , &ev , &action_code ) == TRUE ) | |
| 309 { | |
| 310 /* this has been binded to an action, call the corresponding action */ | |
| 311 DEBUGMSG( "found action code %i for event (%d,%d,%d)\n" , | |
| 312 action_code , inputev.type , inputev.code , inputev.value ); | |
| 313 ed_action_call( action_code , NULL ); | |
| 314 } | |
| 315 } | |
| 316 } | |
| 317 } | |
| 318 break; | |
| 319 } | |
| 320 } | |
| 321 | |
| 322 return TRUE; | |
| 323 } | |
| 324 | |
| 325 | |
| 326 GList * | |
| 327 ed_device_get_list_from_system ( void ) | |
| 328 { | |
| 329 GIOChannel *iochan; | |
| 330 gchar *buffer; | |
| 331 gsize buffer_len; | |
| 332 gint fd = -1; | |
| 333 | |
| 334 fd = g_open( "/proc/bus/input/devices" , O_RDONLY , 0 ); | |
| 335 if ( fd < 0 ) | |
| 336 { | |
| 337 /* an error occurred */ | |
| 338 g_warning( _("event-device-plugin: unable to open /proc/bus/input/devices , automatic " | |
| 339 "detection of event devices won't work.\n") ); | |
| 340 return NULL; | |
| 341 } | |
| 342 | |
| 343 iochan = g_io_channel_unix_new( fd ); | |
| 344 if ( iochan == NULL ) | |
| 345 { | |
| 346 /* an error occurred */ | |
| 347 g_warning( _("event-device-plugin: unable to open a io_channel for /proc/bus/input/devices , " | |
| 348 "automatic detection of event devices won't work.\n") ); | |
| 349 close( fd ); | |
| 350 return NULL; | |
| 351 } | |
| 352 g_io_channel_set_encoding( iochan , "UTF-8" , NULL ); /* utf-8 text */ | |
| 353 | |
| 354 if ( g_io_channel_read_to_end( iochan , &buffer , &buffer_len , NULL ) != G_IO_STATUS_NORMAL ) | |
| 355 { | |
| 356 /* an error occurred */ | |
| 357 g_warning( _("event-device-plugin: an error occurred while reading /proc/bus/input/devices , " | |
| 358 "automatic detection of event devices won't work.\n") ); | |
| 359 g_io_channel_shutdown( iochan , TRUE , NULL ); | |
| 360 g_io_channel_unref( iochan ); | |
| 361 close( fd ); | |
| 362 return NULL; | |
| 363 } | |
| 364 else | |
| 365 { | |
| 366 regex_t preg; | |
| 367 gint search_offset = 0; | |
| 368 GList *system_devices_list = NULL; | |
| 369 | |
| 370 /* we don't need these anymore */ | |
| 371 g_io_channel_shutdown( iochan , TRUE , NULL ); | |
| 372 g_io_channel_unref( iochan ); | |
| 373 close( fd ); | |
| 374 | |
| 375 /* parse content of /proc/bus/input/devices */ | |
| 376 regcomp( &preg, | |
| 377 "I:[^\n]*\nN: Name=\"([^\n]*)\"\nP: Phys=([^\n]*)\n[^\n]+\nH: Handlers=[^\n]*(event[0-9]+)[^\n]*\n" , | |
| 378 REG_ICASE | REG_EXTENDED ); | |
| 379 | |
| 380 while ( search_offset > -1 ) | |
| 381 { | |
| 382 size_t nmatch = 4; | |
| 383 regmatch_t submatch[4]; | |
| 384 | |
| 385 if ( regexec( &preg , &buffer[search_offset] , nmatch , submatch , 0 ) == 0 ) | |
| 386 { | |
| 387 GString *device_name = NULL; | |
| 388 GString *device_phys = NULL; | |
| 389 GString *device_file = NULL; | |
| 390 | |
| 391 if ( submatch[1].rm_so != -1 ) /* check validity of name sub-expression */ | |
| 392 { | |
| 393 device_name = g_string_new( "" ); | |
| 394 g_string_append_len( device_name , | |
| 395 &buffer[(search_offset + submatch[1].rm_so)] , | |
| 396 submatch[1].rm_eo - submatch[1].rm_so ); | |
| 397 } | |
| 398 | |
| 399 if ( submatch[2].rm_so != -1 ) /* check validity of physicalport sub-expression */ | |
| 400 { | |
| 401 device_phys = g_string_new( "" ); | |
| 402 g_string_append_len( device_phys , | |
| 403 &buffer[(search_offset + submatch[2].rm_so)] , | |
| 404 submatch[2].rm_eo - submatch[2].rm_so ); | |
| 405 } | |
| 406 | |
| 407 if ( submatch[3].rm_so != -1 ) /* check validity of filename sub-expression */ | |
| 408 { | |
| 409 device_file = g_string_new( "" ); | |
| 410 GString *device_test = g_string_new( "" ); | |
| 411 g_string_append_len( device_file , | |
| 412 &buffer[(search_offset + submatch[3].rm_so)] , | |
| 413 submatch[3].rm_eo - submatch[3].rm_so ); | |
| 414 | |
| 415 /* let's check if the filename actually exists in /dev */ | |
| 416 g_string_printf( device_test , "/dev/input/%s" , device_file->str ); | |
| 417 if ( !g_file_test( device_test->str , G_FILE_TEST_EXISTS ) ) | |
| 418 { | |
| 419 /* it doesn't exist, mark as invalid device by nullifying device_file*/ | |
| 420 g_warning( _("event-device-plugin: device %s not found in /dev/input , skipping.\n") , device_file ); | |
| 421 g_string_free( device_file , TRUE ); | |
| 422 device_file = NULL; | |
| 423 } | |
| 424 else | |
| 425 { | |
| 426 /* it does exist, mark as valid device by using the full path in device_file*/ | |
| 427 g_string_assign( device_file , device_test->str ); | |
| 428 } | |
| 429 g_string_free( device_test , TRUE ); | |
| 430 } | |
| 431 | |
| 432 if (( device_name != NULL ) && ( device_phys != NULL ) && ( device_file != NULL )) | |
| 433 { | |
| 434 /* add item to the list */ | |
| 435 ed_device_info_t *info = ed_device_info_new( | |
| 436 device_name->str , device_file->str , device_phys->str , 0 ); | |
| 437 info->reg = 0; | |
| 438 DEBUGMSG( "device found, name:\"%s\" , file \"%s\" , phys \"%s\"\n" , | |
| 439 info->name , info->filename , info->phys ); | |
| 440 system_devices_list = g_list_append( system_devices_list , info ); | |
| 441 } | |
| 442 | |
| 443 if ( device_name != NULL ) | |
| 444 g_string_free( device_name , TRUE ); | |
| 445 if ( device_phys != NULL ) | |
| 446 g_string_free( device_phys , TRUE ); | |
| 447 if ( device_file != NULL ) | |
| 448 g_string_free( device_file , TRUE ); | |
| 449 | |
| 450 search_offset += submatch[0].rm_eo; /* update offset for further search */ | |
| 451 } | |
| 452 else | |
| 453 { | |
| 454 /* no more valid devices found */ | |
| 455 search_offset = -1; | |
| 456 } | |
| 457 } | |
| 458 regfree( &preg ); | |
| 459 return system_devices_list; | |
| 460 } | |
| 461 } | |
| 462 | |
| 463 | |
| 464 GList * | |
| 465 ed_device_get_list_from_config ( void ) | |
| 466 { | |
| 467 GKeyFile *keyfile = NULL; | |
| 468 GList *config_devices_list = NULL; | |
| 469 gboolean is_loaded = FALSE; | |
| 470 gchar **device_names = NULL; | |
| 471 gsize device_names_num = 0; | |
| 472 gchar *config_pathfilename = NULL; | |
| 473 gint i = 0; | |
| 474 | |
| 475 config_pathfilename = g_strjoin( "" , g_get_home_dir() , | |
| 476 "/" PLAYER_LOCALRC_DIR "/" PLAYER_LOCALRC_FILE , NULL ); | |
| 477 keyfile = g_key_file_new(); | |
| 478 is_loaded = g_key_file_load_from_file( keyfile , config_pathfilename , G_KEY_FILE_NONE , NULL ); | |
| 479 g_free( config_pathfilename ); | |
| 480 | |
| 481 if ( is_loaded != TRUE ) | |
| 482 { | |
| 483 g_warning( _("event-device-plugin: unable to load config file %s , default settings will be used.\n") , | |
| 484 PLAYER_LOCALRC_FILE ); | |
| 485 g_key_file_free( keyfile ); | |
| 486 return NULL; | |
| 487 } | |
| 488 | |
| 489 /* remove ___plugin___ group that contains plugin settings */ | |
| 490 g_key_file_remove_group( keyfile , "___plugin___" , NULL ); | |
| 491 | |
| 492 /* the other groups are devices; check them and run active ones */ | |
| 493 device_names = g_key_file_get_groups( keyfile , &device_names_num ); | |
| 494 while ( device_names[i] != NULL ) | |
| 495 { | |
| 496 gint device_is_custom = 0; | |
| 497 gchar *device_file = NULL; | |
| 498 gchar *device_phys = NULL; | |
| 499 gboolean device_is_active = FALSE; | |
| 500 gint result = 0; | |
| 501 | |
| 502 result = ed_util_get_data_from_keyfile( | |
| 503 keyfile , device_names[i] , | |
| 504 ED_CONFIG_INFO_FILENAME , &device_file , | |
| 505 ED_CONFIG_INFO_PHYS , &device_phys , | |
| 506 ED_CONFIG_INFO_ISCUSTOM , &device_is_custom , | |
| 507 ED_CONFIG_INFO_ISACTIVE , &device_is_active , | |
| 508 ED_CONFIG_INFO_END ); | |
| 509 | |
| 510 if ( result == 0 ) | |
| 511 { | |
| 512 /* all information succesfully retrieved from config, create a ed_device_info_t */ | |
| 513 ed_device_info_t *info; | |
| 514 | |
| 515 info = ed_device_info_new( device_names[i] , device_file , | |
| 516 device_phys , device_is_custom ); | |
| 517 | |
| 518 /* pick bindings for this device */ | |
| 519 info->bindings = ed_util_get_bindings_from_keyfile( keyfile , device_names[i] ); | |
| 520 info->is_active = device_is_active; | |
| 521 | |
| 522 /* add this device to the config list */ | |
| 523 config_devices_list = g_list_append( config_devices_list , info ); | |
| 524 /* free information from config file, info has its own copies */ | |
| 525 g_free( device_file ); g_free( device_phys ); | |
| 526 } | |
| 527 else | |
| 528 { | |
| 529 g_warning( _("event-device-plugin: incomplete information in config file for device \"%s\"" | |
| 530 " , skipping.\n") , device_names[i] ); | |
| 531 } | |
| 532 | |
| 533 i++; /* on with next */ | |
| 534 } | |
| 535 | |
| 536 g_strfreev( device_names ); | |
| 537 g_key_file_free( keyfile ); | |
| 538 return config_devices_list; | |
| 539 } | |
| 540 | |
| 541 | |
| 542 void | |
| 543 ed_device_free_list ( GList * system_devices_list ) | |
| 544 { | |
| 545 GList *list_iter = system_devices_list; | |
| 546 while ( list_iter != NULL ) | |
| 547 { | |
| 548 ed_device_info_delete( (ed_device_info_t*)list_iter->data ); | |
| 549 list_iter = g_list_next( list_iter ); | |
| 550 } | |
| 551 g_list_free( system_devices_list ); | |
| 552 return; | |
| 553 } | |
| 554 | |
| 555 | |
| 556 void | |
| 557 ed_device_start_listening_from_config ( void ) | |
| 558 { | |
| 559 GKeyFile *keyfile = NULL; | |
| 560 gboolean is_loaded = FALSE; | |
| 561 gchar **device_names = NULL; | |
| 562 gsize device_names_num = 0; | |
| 563 gchar *config_pathfilename = NULL; | |
| 564 GList *system_devices_list = NULL; | |
| 565 gint i = 0; | |
| 566 | |
| 567 config_pathfilename = g_strjoin( "" , g_get_home_dir() , | |
| 568 "/" PLAYER_LOCALRC_DIR "/" PLAYER_LOCALRC_FILE , NULL ); | |
| 569 keyfile = g_key_file_new(); | |
| 570 is_loaded = g_key_file_load_from_file( keyfile , config_pathfilename , G_KEY_FILE_NONE , NULL ); | |
| 571 g_free( config_pathfilename ); | |
| 572 | |
| 573 if ( is_loaded != TRUE ) | |
| 574 { | |
| 575 g_warning( _("event-device-plugin: unable to load config file %s , default settings will be used.\n") , | |
| 576 PLAYER_LOCALRC_FILE ); | |
| 577 g_key_file_free( keyfile ); | |
| 578 return; | |
| 579 } | |
| 580 | |
| 581 system_devices_list = ed_device_get_list_from_system(); | |
| 582 | |
| 583 /* remove ___plugin___ group that contains plugin settings */ | |
| 584 g_key_file_remove_group( keyfile , "___plugin___" , NULL ); | |
| 585 | |
| 586 /* check available devices and run active ones */ | |
| 587 device_names = g_key_file_get_groups( keyfile , &device_names_num ); | |
| 588 while ( device_names[i] != NULL ) | |
| 589 { | |
| 590 GError *gerr = NULL; | |
| 591 gboolean is_active; | |
| 592 | |
| 593 is_active = g_key_file_get_boolean( keyfile , device_names[i] , "is_active" , &gerr ); | |
| 594 if ( gerr != NULL ) | |
| 595 { | |
| 596 g_warning( _("event-device-plugin: configuration, unable to get is_active value for device \"%s\"" | |
| 597 ", skipping it.\n") , device_names[i] ); | |
| 598 g_clear_error( &gerr ); | |
| 599 } | |
| 600 | |
| 601 if ( is_active == TRUE ) /* only care about active devices at this time, ignore others */ | |
| 602 { | |
| 603 gint is_custom = 0; | |
| 604 gchar *device_file = NULL; | |
| 605 gchar *device_phys = NULL; | |
| 606 gint result = 0; | |
| 607 | |
| 608 result = ed_util_get_data_from_keyfile( | |
| 609 keyfile , device_names[i] , | |
| 610 ED_CONFIG_INFO_FILENAME , &device_file , | |
| 611 ED_CONFIG_INFO_PHYS , &device_phys , | |
| 612 ED_CONFIG_INFO_ISCUSTOM , &is_custom , | |
| 613 ED_CONFIG_INFO_END ); | |
| 614 | |
| 615 if ( result != 0 ) | |
| 616 { | |
| 617 /* something wrong, skip this device */ | |
| 618 i++; continue; | |
| 619 } | |
| 620 | |
| 621 /* unless this is a custom device, perform a device check */ | |
| 622 if ( is_custom != 1 ) | |
| 623 { | |
| 624 /* not a custom device, check it against system_devices_list | |
| 625 to see if its information should be updated or if it's not plugged at all */ | |
| 626 gint check_result = ed_device_check( | |
| 627 system_devices_list , device_names[i] , &device_file , &device_phys ); | |
| 628 | |
| 629 if ( check_result == ED_DEVCHECK_OK ) | |
| 630 { | |
| 631 /* ok, we have an active not-custom device and it has been successfully | |
| 632 checked too; create a ed_device_t item for it */ | |
| 633 ed_device_t *dev = ed_device_new ( device_names[i] , device_file , device_phys , 0 ); | |
| 634 g_free( device_file ); g_free( device_phys ); /* not needed anymore */ | |
| 635 if ( dev != NULL ) | |
| 636 { | |
| 637 dev->info->bindings = ed_util_get_bindings_from_keyfile( keyfile , device_names[i] ); | |
| 638 ed_device_start_listening ( dev ); | |
| 639 } | |
| 640 } | |
| 641 | |
| 642 /* note: if check_result == ED_DEVCHECK_ABSENT, we simply skip this device */ | |
| 643 } | |
| 644 else | |
| 645 { | |
| 646 /* ok, we have an active custom device; create a ed_device_t item for it */ | |
| 647 ed_device_t *dev = ed_device_new ( device_names[i] , device_file , device_phys , 1 ); | |
| 648 g_free( device_file ); g_free( device_phys ); /* not needed anymore */ | |
| 649 if ( dev != NULL ) | |
| 650 { | |
| 651 dev->info->bindings = ed_util_get_bindings_from_keyfile( keyfile , device_names[i] ); | |
| 652 ed_device_start_listening ( dev ); | |
| 653 } | |
| 654 } | |
| 655 } | |
| 656 | |
| 657 /* on with next device name */ | |
| 658 i++; | |
| 659 } | |
| 660 | |
| 661 g_strfreev( device_names ); | |
| 662 ed_device_free_list( system_devices_list ); | |
| 663 g_key_file_free( keyfile ); | |
| 664 return; | |
| 665 } | |
| 666 | |
| 667 | |
| 668 /* this function checks that a given event device (with device_name, | |
| 669 device_phys and device_file) exists in system_devices_list; device_phys | |
| 670 and device_file must be dynamically-allocated string, they could | |
| 671 be freed and reallocated if their information needs to be updated; | |
| 672 it returns an integer, its value represents the performed operations */ | |
| 673 gint | |
| 674 ed_device_check ( GList * system_devices_list , | |
| 675 gchar * device_name , | |
| 676 gchar ** device_file , | |
| 677 gchar ** device_phys ) | |
| 678 { | |
| 679 /* first, search in the list for a device, named device_name, | |
| 680 that has not been found in a previous ed_device_check | |
| 681 made with the same system_devices_list (info->reg == 0) */ | |
| 682 GList *list_iter = system_devices_list; | |
| 683 | |
| 684 while ( list_iter != NULL ) | |
| 685 { | |
| 686 ed_device_info_t *info = list_iter->data; | |
| 687 | |
| 688 if ( ( info->reg == 0 ) && ( strcmp( device_name , info->name ) == 0 ) ) | |
| 689 { | |
| 690 /* found a device, check if it has the same physical address */ | |
| 691 if ( strcmp( *device_phys , info->phys ) == 0 ) | |
| 692 { | |
| 693 /* good, same device name and same physical | |
| 694 address; update device_file if necessary */ | |
| 695 if ( strcmp( *device_file , info->filename ) != 0 ) | |
| 696 { | |
| 697 g_free( *device_file ); | |
| 698 *device_file = g_strdup( info->filename ); | |
| 699 } | |
| 700 /* now mark it as "found" so it won't be searched in next | |
| 701 ed_device_check made with the same system_devices_list*/ | |
| 702 info->reg = 1; | |
| 703 /* everything done */ | |
| 704 return ED_DEVCHECK_OK; | |
| 705 } | |
| 706 else | |
| 707 { | |
| 708 /* device found, but physical address is not the one from *device_phys; try to | |
| 709 search further in system_devices_list for a device with same name and address */ | |
| 710 GList *list_iter2 = g_list_next(list_iter); | |
| 711 while ( list_iter2 != NULL ) | |
| 712 { | |
| 713 ed_device_info_t *info2 = list_iter2->data; | |
| 714 if ( ( info2->reg == 0 ) && | |
| 715 ( strcmp( device_name , info2->name ) == 0 ) && | |
| 716 ( strcmp( *device_phys , info2->phys ) == 0 ) ) | |
| 717 { | |
| 718 /* found a device with the same name and address, | |
| 719 so let's use it; update device_file if necessary */ | |
| 720 if ( strcmp( *device_file , info2->filename ) != 0 ) | |
| 721 { | |
| 722 g_free( *device_file ); | |
| 723 *device_file = g_strdup( info2->filename ); | |
| 724 } | |
| 725 /* now mark it as "found" so it won't be searched in next | |
| 726 ed_device_check made with the same system_devices_list */ | |
| 727 info2->reg = 1; | |
| 728 /* everything done */ | |
| 729 return ED_DEVCHECK_OK; | |
| 730 } | |
| 731 list_iter2 = g_list_next(list_iter2); | |
| 732 } | |
| 733 | |
| 734 /* if we get to this point, it means that there isn't any device named | |
| 735 device_name with physical address equal to *device_phys ; there is only | |
| 736 one (or more) device named device_name but with different physical | |
| 737 address; we'll use the first of those (alas the current content of info) */ | |
| 738 g_free( *device_phys ); /* free outdated device_phys */ | |
| 739 *device_phys = g_strdup( info->phys ); /* update it with the new one */ | |
| 740 | |
| 741 /* update device_file if necessary */ | |
| 742 if ( strcmp( *device_file , info->filename ) != 0 ) | |
| 743 { | |
| 744 g_free( *device_file ); | |
| 745 *device_file = g_strdup( info->filename ); | |
| 746 } | |
| 747 | |
| 748 /* now mark it as "found" so it won't be searched in next | |
| 749 ed_device_check made with the same system_devices_list*/ | |
| 750 info->reg = 1; | |
| 751 /* everything done */ | |
| 752 return ED_DEVCHECK_OK; | |
| 753 } | |
| 754 } | |
| 755 | |
| 756 list_iter = g_list_next(list_iter); | |
| 757 } | |
| 758 | |
| 759 /* the entire system_devices_list was searched, | |
| 760 but no device named device_name was found */ | |
| 761 return ED_DEVCHECK_ABSENT; | |
| 762 } | |
| 763 | |
| 764 | |
| 765 | |
| 766 /* config */ | |
| 767 static void | |
| 768 ed_config_save_from_list_bindings_foreach ( ed_inputevent_t * iev , | |
| 769 gint action_code , | |
| 770 gpointer keyfile , | |
| 771 gpointer info_gp ) | |
| 772 { | |
| 773 gint int_list[4]; | |
| 774 gchar *keyname; | |
| 775 ed_device_info_t *info = info_gp; | |
| 776 keyname = g_strdup_printf( "b%i" , info->reg ); | |
| 777 int_list[0] = action_code; | |
| 778 int_list[1] = iev->type; | |
| 779 int_list[2] = iev->code; | |
| 780 int_list[3] = iev->value; | |
| 781 g_key_file_set_integer_list( keyfile , info->name , keyname , int_list , 4 ); | |
| 782 g_free( keyname ); | |
| 783 info->reg++; | |
| 784 return; | |
| 785 } | |
| 786 | |
| 787 gint | |
| 788 ed_config_save_from_list ( GList * config_devices_list ) | |
| 789 { | |
| 790 GKeyFile *keyfile; | |
| 791 GList *iter_list = NULL; | |
| 792 gchar *keyfile_str = NULL; | |
| 793 gsize keyfile_str_len = 0; | |
| 794 GIOChannel *iochan; | |
| 795 gchar *config_pathfilename = NULL; | |
| 796 | |
| 797 config_pathfilename = g_strjoin( "" , g_get_home_dir() , | |
| 798 "/" PLAYER_LOCALRC_DIR "/" PLAYER_LOCALRC_FILE , NULL ); | |
| 799 | |
| 800 keyfile = g_key_file_new(); | |
| 801 | |
| 802 g_key_file_set_string( keyfile , "___plugin___" , "config_ver" , ED_VERSION_CONFIG ); | |
| 803 | |
| 804 iter_list = config_devices_list; | |
| 805 while ( iter_list != NULL ) | |
| 806 { | |
| 807 ed_device_info_t *info = iter_list->data; | |
| 808 g_key_file_set_string( keyfile , info->name , "filename" , info->filename ); | |
| 809 g_key_file_set_string( keyfile , info->name , "phys" , info->phys ); | |
| 810 g_key_file_set_boolean( keyfile , info->name , "is_active" , info->is_active ); | |
| 811 g_key_file_set_integer( keyfile , info->name , "is_custom" , info->is_custom ); | |
| 812 /* use the info->reg field as a counter to list actions */ | |
| 813 info->reg = 0; /* init the counter */ | |
| 814 if ( info->bindings != NULL ) | |
| 815 ed_bindings_store_foreach( info->bindings , | |
| 816 ed_config_save_from_list_bindings_foreach , keyfile , info ); | |
| 817 iter_list = g_list_next( iter_list ); | |
| 818 } | |
| 819 | |
| 820 keyfile_str = g_key_file_to_data( keyfile , &keyfile_str_len , NULL ); | |
| 821 iochan = g_io_channel_new_file( config_pathfilename , "w" , NULL ); | |
| 822 g_io_channel_set_encoding( iochan , "UTF-8" , NULL ); | |
| 823 g_io_channel_write_chars( iochan , keyfile_str , keyfile_str_len , NULL , NULL ); | |
| 824 g_io_channel_shutdown( iochan , TRUE , NULL ); | |
| 825 g_io_channel_unref( iochan ); | |
| 826 | |
| 827 g_free( keyfile_str ); | |
| 828 g_key_file_free( keyfile ); | |
| 829 return 0; | |
| 830 } | |
| 831 | |
| 832 | |
| 833 /* utils */ | |
| 834 | |
| 835 | |
| 836 /* this picks information from a keyfile, using device_name | |
| 837 as group name; information must be requested by passing | |
| 838 a ed_config_info_t value and a corresponding container; | |
| 839 list of requested information must be terminated with | |
| 840 ED_CONFIG_INFO_END; returns 0 if everything is found, | |
| 841 returns negative values if some information is missing */ | |
| 842 static gint | |
| 843 ed_util_get_data_from_keyfile( GKeyFile * keyfile , gchar * device_name , ... ) | |
| 844 { | |
| 845 GError *gerr = NULL; | |
| 846 gboolean is_failed = FALSE; | |
| 847 ed_config_info_t info_code = ED_CONFIG_INFO_END; | |
| 848 GList *temp_stringstore = NULL; | |
| 849 va_list ap; | |
| 850 | |
| 851 /* when we get a string value from g_key_file_get_string, we temporarily | |
| 852 store its container in temp_stringstore; if subsequent information | |
| 853 requests in the iteraton fails, we free the information in previous | |
| 854 container and nullify its content; | |
| 855 this way, user will get complete information (return value 0) or | |
| 856 absent information (all string containers nullified, return value -1) */ | |
| 857 | |
| 858 va_start( ap, device_name ); | |
| 859 | |
| 860 while ( ( is_failed == FALSE ) && | |
| 861 ( ( info_code = va_arg( ap , ed_config_info_t ) ) != ED_CONFIG_INFO_END ) ) | |
| 862 { | |
| 863 switch ( info_code ) | |
| 864 { | |
| 865 case ED_CONFIG_INFO_FILENAME: | |
| 866 { | |
| 867 gchar **device_file = va_arg( ap , gchar ** ); | |
| 868 *device_file = g_key_file_get_string( keyfile , device_name , "filename" , &gerr ); | |
| 869 if ( gerr != NULL ) | |
| 870 { | |
| 871 g_clear_error( &gerr ); | |
| 872 g_warning( _("event-device-plugin: configuration, unable to get filename value for device \"%s\"" | |
| 873 ", skipping it.\n") , device_name ); | |
| 874 is_failed = TRUE; | |
| 875 } | |
| 876 else | |
| 877 temp_stringstore = g_list_append( temp_stringstore , device_file ); | |
| 878 break; | |
| 879 } | |
| 880 | |
| 881 case ED_CONFIG_INFO_PHYS: | |
| 882 { | |
| 883 gchar **device_phys = va_arg( ap , gchar ** ); | |
| 884 *device_phys = g_key_file_get_string( keyfile , device_name , "phys" , &gerr ); | |
| 885 if ( gerr != NULL ) | |
| 886 { | |
| 887 g_clear_error( &gerr ); | |
| 888 g_warning( _("event-device-plugin: configuration, unable to get phys value for device \"%s\"" | |
| 889 ", skipping it.\n") , device_name ); | |
| 890 is_failed = TRUE; | |
| 891 } | |
| 892 else | |
| 893 temp_stringstore = g_list_append( temp_stringstore , device_phys ); | |
| 894 break; | |
| 895 } | |
| 896 | |
| 897 case ED_CONFIG_INFO_ISCUSTOM: | |
| 898 { | |
| 899 gint *is_custom = va_arg( ap , gint * ); | |
| 900 *is_custom = g_key_file_get_integer( keyfile , device_name , "is_custom" , &gerr ); | |
| 901 if ( gerr != NULL ) | |
| 902 { | |
| 903 g_clear_error( &gerr ); | |
| 904 g_warning( _("event-device-plugin: configuration, unable to get is_custom value for device \"%s\"" | |
| 905 ", skipping it.\n") , device_name ); | |
| 906 is_failed = TRUE; | |
| 907 } | |
| 908 break; | |
| 909 } | |
| 910 | |
| 911 case ED_CONFIG_INFO_ISACTIVE: | |
| 912 { | |
| 913 gboolean *is_active = va_arg( ap , gboolean * ); | |
| 914 *is_active = g_key_file_get_boolean( keyfile , device_name , "is_active" , &gerr ); | |
| 915 if ( gerr != NULL ) | |
| 916 { | |
| 917 g_clear_error( &gerr ); | |
| 918 g_warning( _("event-device-plugin: configuration, unable to get is_active value for device \"%s\"" | |
| 919 ", skipping it.\n") , device_name ); | |
| 920 is_failed = TRUE; | |
| 921 } | |
| 922 break; | |
| 923 } | |
| 924 | |
| 925 default: | |
| 926 { | |
| 927 /* unexpected value in info_code, skipping */ | |
| 928 g_warning( _("event-device-plugin: configuration, unexpected value for device \"%s\"" | |
| 929 ", skipping it.\n") , device_name ); | |
| 930 is_failed = TRUE; | |
| 931 } | |
| 932 } | |
| 933 } | |
| 934 | |
| 935 va_end( ap ); | |
| 936 | |
| 937 if ( is_failed == FALSE ) | |
| 938 { | |
| 939 /* temp_stringstore is not needed anymore, | |
| 940 do not change pointed containers */ | |
| 941 g_list_free( temp_stringstore ); | |
| 942 return 0; | |
| 943 } | |
| 944 else | |
| 945 { | |
| 946 /* temp_stringstore is not needed anymore, | |
| 947 nullify pointed containers and free content */ | |
| 948 GList *list_iter = temp_stringstore; | |
| 949 while ( list_iter != NULL ) | |
| 950 { | |
| 951 gchar **container = list_iter->data; | |
| 952 g_free( *container ); | |
| 953 *container = NULL; | |
| 954 list_iter = g_list_next( list_iter ); | |
| 955 } | |
| 956 g_list_free( temp_stringstore ); | |
| 957 return -1; | |
| 958 } | |
| 959 } | |
| 960 | |
| 961 | |
| 962 /* this does just what its name says :) */ | |
| 963 static gpointer | |
| 964 ed_util_get_bindings_from_keyfile( GKeyFile * keyfile , gchar * device_name ) | |
| 965 { | |
| 966 ed_inputevent_t *iev = g_malloc(sizeof(ed_inputevent_t)); | |
| 967 gpointer bindings = ed_bindings_store_new(); | |
| 968 gchar **keys; | |
| 969 gint j = 0; | |
| 970 | |
| 971 /* now get bindings for this device */ | |
| 972 keys = g_key_file_get_keys( keyfile , device_name , NULL , NULL ); | |
| 973 while ( keys[j] != NULL ) | |
| 974 { | |
| 975 /* in the config file, only bindings start with the 'b' character */ | |
| 976 if ( keys[j][0] == 'b' ) | |
| 977 { | |
| 978 gsize ilist_len = 0; | |
| 979 gint *ilist; | |
| 980 ilist = g_key_file_get_integer_list( keyfile , | |
| 981 device_name , keys[j] , &ilist_len , NULL ); | |
| 982 if ( ilist_len > 3 ) | |
| 983 { | |
| 984 gint action_code = (gint)ilist[0]; | |
| 985 iev->type = (guint)ilist[1]; | |
| 986 iev->code = (guint)ilist[2]; | |
| 987 iev->value = (gint)ilist[3]; | |
| 988 ed_bindings_store_insert( bindings , iev , action_code ); | |
| 989 } | |
| 990 g_free( ilist ); | |
| 991 } | |
| 992 j++; | |
| 993 } | |
| 994 | |
| 995 g_strfreev( keys ); | |
| 996 g_free( iev ); | |
| 997 | |
| 998 if ( ed_bindings_store_size( bindings ) == 0 ) | |
| 999 { | |
| 1000 ed_bindings_store_delete( bindings ); | |
| 1001 bindings = NULL; | |
| 1002 } | |
| 1003 | |
| 1004 return bindings; | |
| 1005 } |
