From bd8d77dd8a6fbbc2a8925a2210917bfe0e2d6849 Mon Sep 17 00:00:00 2001 From: "N.N." Date: Tue, 20 Jun 2006 17:20:32 +0000 Subject: This commit was generated by cvs2svn to compensate for changes in r5269, which included commits to RCS files with non-trunk default branches. svn path=/trunk/externals/input_noticer/; revision=5270 --- input_noticer.c | 371 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 371 insertions(+) create mode 100644 input_noticer.c (limited to 'input_noticer.c') diff --git a/input_noticer.c b/input_noticer.c new file mode 100644 index 0000000..ef2c91a --- /dev/null +++ b/input_noticer.c @@ -0,0 +1,371 @@ +/* + * input_noticer - input noticer external for pure-data + * + * David Merrill + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "input_noticer.h" + +static char *version = "$Revision: 1.1.1.1 $"; +#define MAX_INPUT_DEVICES 32 + +/*------------------------------------------------------------------------------ + * CLASS DEF + */ + +typedef struct callback_info { + void (*device_added)(char *device_file); + void (*device_removed)(char *device_file); +} callback_info; + +static t_class *input_noticer_class; + +typedef struct _input_noticer { + t_object x_obj; + GMainContext *gmc; + GMainLoop *gml; + callback_info *cbi; + LibHalContext *lhc; + DBusConnection *connection; + char *capability; + char *product_substring; + int device_idx; + t_outlet *notify_out; + char *last_notification_sent; + GThread *gthread; + int started; +} t_input_noticer; + +int udi_matches_device(LibHalContext *ctx, const char *udi); +static void output_list(t_input_noticer *x, t_symbol *s, int argc, t_atom *argv); + +/*------------------------------------------------------------------------------ + * IMPLEMENTATION + */ + +static void output_inputpath(t_input_noticer *x, int idx, char *path) +{ + t_atom t[2]; + + // set up the output array + SETFLOAT(&(t[0]),idx); + SETSYMBOL(&(t[1]),gensym(path)); + + // output a list + outlet_list(x->notify_out, &s_list, 2, t); +} + +int scmp(const void *sp1, const void *sp2 ) +{ + return( strcmp(*(char **)sp1, *(char **)sp2) ); +} + +void scan_for_devices(LibHalContext *ctx) { + char ** input_devices; + int num_input_devices; + int i,j; + int this_device_idx; + DBusError dbus_error; + char *linux_device_file = NULL; + char *found_devices[MAX_INPUT_DEVICES]; + t_input_noticer *x = libhal_ctx_get_user_data(ctx); + + /// can't do anything here if we don't have a pointer back to our struct + if (x == NULL) return; + + // not really using this, but why not initialize... :) + dbus_error_init (&dbus_error); + + // grab an array of all devices that match the capability we're looking for + input_devices = libhal_find_device_by_capability (ctx, "input", &num_input_devices, &dbus_error); + if (dbus_error_is_set (&dbus_error)) + { + post("could not find existing networking devices: %s\n", dbus_error.message); + dbus_error_free (&dbus_error); + return; + } + + if (input_devices) + { + // we found at least one + this_device_idx = 0; + for (i = 0; i < num_input_devices; i++) + { + if (udi_matches_device(ctx, input_devices[i])) + { + post("found a %s",x->product_substring); + + // get the linux.device_file + linux_device_file = libhal_device_get_property_string(ctx, input_devices[i],"linux.device_file", NULL); + + // store the linux device file + found_devices[this_device_idx] = linux_device_file; + + this_device_idx++; + } + + } + // sort the devices alphabetically, so they will stay in the same order + qsort (found_devices, this_device_idx, sizeof (char *), scmp); + for (i = 0; i < this_device_idx; i++) + { + output_inputpath(x, i, found_devices[i]); + libhal_free_string(found_devices[i]); + } + libhal_free_string_array(input_devices); + } else { + post("no input devices found!"); + } +} + +// this function checks the UDI returned from libhal against the device product string +// that we are looking +int udi_matches_device(LibHalContext *ctx, const char *udi) { + t_input_noticer *x = libhal_ctx_get_user_data(ctx); + int i; + char *capability = malloc (strlen("input") + strlen(x->capability) + 2); + char *temp1; + + sprintf(capability,"input.%s",x->capability); + temp1 = libhal_device_get_property_string (ctx, udi, "info.product", NULL); + if (libhal_device_property_exists(ctx, udi, "info.capabilities", NULL)) + { + char **capabilities = libhal_device_get_property_strlist(ctx, udi, "info.capabilities", NULL); + for (i=0; capabilities[i] != NULL; i++) + { + //post("looking for %s, now checking capability #%d, %s",capability,i,capabilities[i]); + if (!strcmp (capabilities[i], capability)) + { + char *temp = libhal_device_get_property_string (ctx, udi, "info.product", NULL); + if (temp != NULL && strstr(temp, x->product_substring)) // if product string matches up + { + libhal_free_string_array(capabilities); + libhal_free_string (temp); + free(capability); + return 1; + } else { + // product string does not match up + } + + libhal_free_string (temp); + } + } + libhal_free_string_array(capabilities); + } else { + // no capabilities found + } + free(capability); + return 0; +} + +// this callback gets called whenever HAL notices a new device being added +void hal_device_added(LibHalContext *ctx, const char *udi) { + t_input_noticer *x = libhal_ctx_get_user_data(ctx); + + if (x != NULL && x->started) { + if (udi_matches_device(ctx,udi)) { + scan_for_devices(ctx); + } else { + // post("nope"); + } + } +} + +void hal_device_removed(LibHalContext *ctx, const char *udi) { + // post("device removed, udi = %s\n", udi); +} + +// I haven't seen this one get called... +void hal_device_new_capability(LibHalContext *ctx, const char *udi, const char *capability) { + // post("new device capability, udi = %s\n", udi); +} + +void input_noticer_stop(t_input_noticer* x) { + DEBUG(post("input_noticer_stop");); + + /* Signal the HAL listener to stop */ + g_main_loop_quit(x->gml); + + /* Wait until it has actually stopped */ + g_thread_join(x->gthread); +} + +static int input_noticer_close(t_input_noticer *x) { + DEBUG(post("input_noticer_close");); + + input_noticer_stop(x); + + if (x->product_substring) free(x->product_substring); + if (x->capability) free(x->capability); + + return 1; +} + +static int input_noticer_open(t_input_noticer *x, t_symbol *s) { + DEBUG(post("input_noticer_open");) + + // close it down, if running already + input_noticer_close(x); + + return 1; +} + +void input_noticer_start(t_input_noticer* x) { + post("input_noticer: started"); + + x->started = 1; + + // do first scan here (NOT in input_noticer_new, can't generate output from there) + scan_for_devices(x->lhc); +} + +gpointer input_noticer_thread_main(gpointer user_data) { + t_input_noticer *x = (t_input_noticer *) user_data; + + /* Run the main loop. We stay here until g_main_quit() is called */ + g_main_loop_run(x->gml); + + return user_data; +} + +/* teardown functions */ +static void input_noticer_free(t_input_noticer* x) { + DEBUG(post("input_noticer_free");) +} + +// removes double-quotes from a string, and returns a copy of it, otherwise unharmed +static char *remove_quotes(char *input_str) +{ + char *rv, *tp; + unsigned int i; + + post ("removing quotes from %s", input_str); + + if (input_str != NULL) + { + rv = malloc ((strlen(input_str) + 1) * sizeof(char)); + tp = rv; + for (i=0; i < strlen(input_str); i++) { + if (input_str[i] != '"') { + *tp = input_str[i]; + tp++; + } + } + *tp = '\0'; + } else { + return NULL; + } + + post ("returning %s", rv); + return rv; +} + +/* setup functions */ +static void *input_noticer_new(t_symbol *capability, t_symbol *product_substring) { + int i; + t_input_noticer *x = (t_input_noticer *)pd_new(input_noticer_class); + + post("[input_noticer] %s, written by David Merrill ",version); + + /* init vars */ + x->gmc = NULL; + x->cbi = NULL; + x->lhc = NULL; + x->connection = NULL; + x->notify_out = NULL; + x->last_notification_sent = NULL; + x->started = 0; + x->capability = remove_quotes((char *)capability->s_name); + x->product_substring = remove_quotes((char *)product_substring->s_name); + + // create outlet for notifying + x->notify_out = outlet_new(&x->x_obj, 0); // list outlet + + // Setup glib and dbus for threading + g_type_init(); + if (!g_thread_supported ()) + g_thread_init (NULL); + dbus_g_thread_init(); + + // create a context for the callback functions + x->gmc = g_main_context_new(); + x->gml = g_main_loop_new(x->gmc, FALSE); + + // create a libhal context + if ((x->lhc = libhal_ctx_new()) == NULL) { + // complain here (exit) + DEBUG(post("input_noticer_open: error, could not create a libhal context!");) + } + + // get the dbus connection + x->connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); + if (x->connection == NULL) { + // complain here (exit) + DEBUG(post("input_noticer_open: error, could not get the DBUS connection!");) + } + + // attaches the main loop to dbus, so that the main loop + // gets dbus events + dbus_connection_setup_with_g_main(x->connection,x->gmc); + + // tells libhal to use our dbus connection, in order to receive + // events from hal + libhal_ctx_set_dbus_connection(x->lhc,x->connection); + + if (!libhal_ctx_init (x->lhc, NULL)) { + DEBUG(post("input_noticer_open: error, could not init libhal!");) + } + + // handing my custom data structure to libhal context, so that the callback functions + // can get to it + libhal_ctx_set_user_data(x->lhc, x); + + libhal_ctx_set_device_added(x->lhc, hal_device_added); + libhal_ctx_set_device_removed(x->lhc, hal_device_removed); + libhal_ctx_set_device_new_capability(x->lhc, hal_device_new_capability); + + /* Create the thread that listens for HAL events */ + x->gthread = g_thread_create(input_noticer_thread_main, x, TRUE, NULL); + + return (void *)x; +} + +void input_noticer_setup(void) { + // DEBUG(post("input_noticer_setup");) + + // define how the object gets instantiated + // example: [input_noticer joystick "SideWinder Dual Strike"] + input_noticer_class = class_new( + gensym("input_noticer"), + (t_newmethod)input_noticer_new, + (t_method)input_noticer_free, + sizeof(t_input_noticer), + CLASS_DEFAULT, + A_DEFSYMBOL, + A_DEFSYMBOL, + 0); + + class_addbang(input_noticer_class, input_noticer_start); +} -- cgit v1.2.1