Index: light-server.c =================================================================== --- light-server.c (revision 1690) +++ light-server.c (working copy) @@ -1,198 +1,245 @@ /* * Example UPnP device/service, implementing the BinaryLight device and * SwitchPower services to emulate a light switch. * * The user interface is as minimal as possible so that the GUPnP concepts and * best practises are more apparent. For a better implementation of * BinaryLight, see gupnp-tools. * * This example code is in the public domain. */ #include #include #include +#include static gboolean status; static gboolean quiet; static GOptionEntry entries[] = { { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, "Turn off output", NULL }, { NULL } }; G_BEGIN_DECLS G_MODULE_EXPORT void set_target_cb (GUPnPService *service, GUPnPServiceAction *action, gpointer user_data); G_MODULE_EXPORT void get_target_cb (GUPnPService *service, GUPnPServiceAction *action, gpointer user_data); G_MODULE_EXPORT void get_status_cb (GUPnPService *service, GUPnPServiceAction *action, gpointer user_data); G_MODULE_EXPORT void query_target_cb (GUPnPService *service, char *variable, GValue *value, gpointer user_data); G_MODULE_EXPORT void query_status_cb (GUPnPService *service, char *variable, GValue *value, gpointer user_data); G_END_DECLS /* * Action handlers */ +GMainContext* worker_context; + +struct Args { + GUPnPService* service; + GUPnPServiceAction* action; + gboolean* target; +}; + +void *do_set_target( void* arguments ); + /* SetTarget */ G_MODULE_EXPORT void set_target_cb (GUPnPService *service, GUPnPServiceAction *action, G_GNUC_UNUSED gpointer user_data) { - gboolean target; + struct Args* args = malloc( sizeof( struct Args ) ); + memset( args, 0, sizeof( struct Args )); + args->service = service; + args->action = action; + args->target = malloc( sizeof( gboolean ) ); /* Get the new target value */ gupnp_service_action_get (action, - "newTargetValue", G_TYPE_BOOLEAN, &target, + "newTargetValue", G_TYPE_BOOLEAN, args->target, NULL); + pthread_t do_thread; + pthread_create( &do_thread, NULL, &do_set_target, (void*)args ); +} + +void *do_set_target( void* arguments ) +{ + struct Args* args = (struct Args*) arguments; + + g_main_context_push_thread_default( worker_context ); + /* If the new target doesn't match the current status, change the status and emit a notification that the status has changed. */ - if (target != status) { - status = target; - gupnp_service_notify (service, + if (*(args->target) != status) { + status = *(args->target); + gupnp_service_notify (args->service, "Status", G_TYPE_BOOLEAN, status, NULL); if (!quiet) { g_print ("The light is now %s.\n", status ? "on" : "off"); } } /* Return success to the client */ - gupnp_service_action_return (action); + gupnp_service_action_return (args->action); } /* GetTarget */ G_MODULE_EXPORT void get_target_cb (G_GNUC_UNUSED GUPnPService *service, GUPnPServiceAction *action, G_GNUC_UNUSED gpointer user_data) { gupnp_service_action_set (action, "RetTargetValue", G_TYPE_BOOLEAN, status, NULL); gupnp_service_action_return (action); } /* GetStatus */ G_MODULE_EXPORT void get_status_cb (G_GNUC_UNUSED GUPnPService *service, GUPnPServiceAction *action, G_GNUC_UNUSED gpointer user_data) { gupnp_service_action_set (action, "ResultStatus", G_TYPE_BOOLEAN, status, NULL); gupnp_service_action_return (action); } /* * State Variable query handlers */ /* Target */ G_MODULE_EXPORT void query_target_cb (G_GNUC_UNUSED GUPnPService *service, G_GNUC_UNUSED char *variable, GValue *value, G_GNUC_UNUSED gpointer user_data) { g_value_init (value, G_TYPE_BOOLEAN); g_value_set_boolean (value, status); } /* Status */ G_MODULE_EXPORT void query_status_cb (G_GNUC_UNUSED GUPnPService *service, G_GNUC_UNUSED char *variable, GValue *value, G_GNUC_UNUSED gpointer user_data) { g_value_init (value, G_TYPE_BOOLEAN); g_value_set_boolean (value, status); } +void *run_worker_thread( void* args ) +{ + GMainLoop* worker_loop; + + worker_context = g_main_context_new(); + worker_loop = g_main_loop_new( worker_context, FALSE ); + g_main_loop_run( worker_loop ); +} int main (G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv) { GOptionContext *optionContext; GMainLoop *main_loop; GError *error = NULL; GUPnPContext *context; GUPnPRootDevice *dev; GUPnPServiceInfo *service; + pthread_t worker_thread; + pthread_create( &worker_thread, NULL, &run_worker_thread, NULL ); + optionContext = g_option_context_new (NULL); g_option_context_add_main_entries (optionContext, entries, NULL); if (!g_option_context_parse (optionContext, &argc, &argv, &error)) { g_print ("option parsing failed: %s\n", error->message); exit (1); } /* By default the light is off */ status = FALSE; if (!quiet) { g_print ("The light is now %s.\n", status ? "on" : "off"); } /* Create the UPnP context */ context = gupnp_context_new (NULL, NULL, 0, &error); if (error) { g_printerr ("Error creating the GUPnP context: %s\n", error->message); g_error_free (error); return EXIT_FAILURE; } /* Create root device */ dev = gupnp_root_device_new (context, "BinaryLight1.xml", "."); gupnp_root_device_set_available (dev, TRUE); /* Get the switch service from the root device */ service = gupnp_device_info_get_service (GUPNP_DEVICE_INFO (dev), "urn:schemas-upnp-org:service:SwitchPower:1"); if (!service) { g_printerr ("Cannot get SwitchPower1 service\n"); return EXIT_FAILURE; } /* Autoconnect the action and state variable handlers. This connects query_target_cb and query_status_cb to the Target and Status state variables query callbacks, and set_target_cb, get_target_cb and get_status_cb to SetTarget, GetTarget and GetStatus actions respectively. */ - gupnp_service_signals_autoconnect (GUPNP_SERVICE (service), NULL, &error); + /*gupnp_service_signals_autoconnect (GUPNP_SERVICE (service), NULL, &error); if (error) { g_printerr ("Failed to autoconnect signals: %s\n", error->message); g_error_free (error); return EXIT_FAILURE; - } + }*/ + + g_signal_connect( service, "action-invoked::SetTarget", + G_CALLBACK( set_target_cb ), NULL ); + g_signal_connect( service, "action-invoked::GetTarget", + G_CALLBACK( get_target_cb ), NULL ); + g_signal_connect( service, "action-invoked::GetStatus", + G_CALLBACK( get_status_cb ), NULL ); + g_signal_connect( service, "query-variable::Target", + G_CALLBACK( query_target_cb ), NULL ); + g_signal_connect( service, "query-variable::Status", + G_CALLBACK( query_status_cb ), NULL ); /* Run the main loop */ main_loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (main_loop); /* Cleanup */ g_main_loop_unref (main_loop); g_object_unref (service); g_object_unref (dev); g_object_unref (context); return EXIT_SUCCESS; }