/* * Copyright (C) 2007-2014 various contributors (see AUTHORS) * * This file is part of Python-EFL. * * Python-EFL is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * Python-EFL 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this Python-EFL. If not, see . */ #include #include "e_dbus.h" #ifndef E_DBUS_COLOR_DEFAULT #define E_DBUS_COLOR_DEFAULT EINA_COLOR_CYAN #endif static int _e_dbus_log_dom = -1; #define DBG(...) EINA_LOG_DOM_DBG(_e_dbus_log_dom, __VA_ARGS__) #define INFO(...) EINA_LOG_DOM_INFO(_e_dbus_log_dom, __VA_ARGS__) #define WARN(...) EINA_LOG_DOM_WARN(_e_dbus_log_dom, __VA_ARGS__) #define ERR(...) EINA_LOG_DOM_ERR(_e_dbus_log_dom, __VA_ARGS__) static int connection_slot = -1; static int e_dbus_idler_active = 0; static int close_connection = 0; static int _edbus_init_count = 0; // change this define for extra debug // #define DDBG(...) printf(__VA_ARGS__); printf("\n"); #define DDBG(...) static Eina_Bool e_dbus_idler(void *data) { E_DBus_Connection *cd; cd = data; if (DBUS_DISPATCH_COMPLETE == dbus_connection_get_dispatch_status(cd->conn)) { DBG("done dispatching!"); cd->idler = NULL; return ECORE_CALLBACK_CANCEL; } e_dbus_idler_active++; dbus_connection_ref(cd->conn); DBG("dispatch()"); dbus_connection_dispatch(cd->conn); dbus_connection_unref(cd->conn); e_dbus_idler_active--; if (!e_dbus_idler_active && close_connection) { do { e_dbus_connection_close(cd); } while (--close_connection); } return ECORE_CALLBACK_RENEW; } static void cb_dispatch_status(DBusConnection *conn, DBusDispatchStatus new_status, void *data) { E_DBus_Connection *cd; DBG("dispatch status: %d!", new_status); cd = data; if (new_status == DBUS_DISPATCH_DATA_REMAINS && !cd->idler) cd->idler = ecore_idler_add(e_dbus_idler, cd); else if (new_status != DBUS_DISPATCH_DATA_REMAINS && cd->idler) { ecore_idler_del(cd->idler); cd->idler = NULL; } } /* ecore fd handler */ static Eina_Bool e_dbus_fd_handler(void *data, Ecore_Fd_Handler *fd_handler) { E_DBus_Handler_Data *hd = data; unsigned int condition = 0; DDBG("DATA READY ON FD"); if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ)) condition |= DBUS_WATCH_READABLE; if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_WRITE)) condition |= DBUS_WATCH_WRITABLE; if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_ERROR)) condition |= DBUS_WATCH_ERROR; if (hd->watch_read && dbus_watch_get_enabled(hd->watch_read)) dbus_watch_handle(hd->watch_read, condition); if (hd->watch_read != hd->watch_write && hd->watch_write && dbus_watch_get_enabled(hd->watch_write)) dbus_watch_handle(hd->watch_write, condition); return ECORE_CALLBACK_RENEW; } static void e_dbus_fd_handler_update(E_DBus_Handler_Data *hd) { Ecore_Fd_Handler_Flags eflags = ECORE_FD_ERROR; if (hd->watch_read && dbus_watch_get_enabled(hd->watch_read)) eflags |= ECORE_FD_READ; if (hd->watch_write && dbus_watch_get_enabled(hd->watch_write)) eflags |= ECORE_FD_WRITE; DDBG("FD update %d (flags: %d)", hd->fd, eflags) ecore_main_fd_handler_active_set(hd->fd_handler, eflags); } static void e_dbus_fd_handler_add(E_DBus_Connection *cd, DBusWatch *watch) { E_DBus_Handler_Data *hd = NULL, *hd2; Eina_List *l; int dflags; int fd; fd = dbus_watch_get_unix_fd(watch); dflags = dbus_watch_get_flags(watch); EINA_LIST_FOREACH(cd->fd_handlers, l, hd2) { if (hd2->fd == fd) { hd = hd2; break; } } if (!hd) { hd = calloc(1, sizeof(E_DBus_Handler_Data)); if (!hd) return; hd->cd = cd; hd->fd = fd; hd->fd_handler = ecore_main_fd_handler_add(fd, ECORE_FD_ERROR, e_dbus_fd_handler, hd, NULL, NULL); if (!hd->fd_handler) { DDBG("ERROR! cannot create FD handler") } cd->fd_handlers = eina_list_append(cd->fd_handlers, hd); } if (dflags & DBUS_WATCH_READABLE) hd->watch_read = watch; if (dflags & DBUS_WATCH_WRITABLE) hd->watch_write = watch; dbus_watch_set_data(watch, hd, NULL); e_dbus_fd_handler_update(hd); } static void e_dbus_fd_handler_del(E_DBus_Handler_Data *hd) { DDBG(" FD handler del"); hd->cd->fd_handlers = eina_list_remove(hd->cd->fd_handlers, hd); ecore_main_fd_handler_del(hd->fd_handler); free(hd); } /* watch */ static dbus_bool_t cb_watch_add(DBusWatch *watch, void *data) { E_DBus_Connection *cd = data; DDBG("Watch add on fd: %d (flags: %d) enable: %d", dbus_watch_get_unix_fd(watch), dbus_watch_get_flags(watch), dbus_watch_get_enabled(watch)); e_dbus_fd_handler_add(cd, watch); return true; } static void cb_watch_del(DBusWatch *watch, void *data) { E_DBus_Handler_Data *hd; DDBG("Watch del on fd: %d (flags: %d)", dbus_watch_get_unix_fd(watch), dbus_watch_get_flags(watch)); hd = dbus_watch_get_data(watch); int dflags = dbus_watch_get_flags(watch); if (dflags & DBUS_WATCH_READABLE) hd->watch_read = NULL; if (dflags & DBUS_WATCH_WRITABLE) hd->watch_write = NULL; if (!hd->watch_read && !hd->watch_write) e_dbus_fd_handler_del(hd); else e_dbus_fd_handler_update(hd); } static void cb_watch_toggle(DBusWatch *watch, void *data) { E_DBus_Handler_Data *hd; DDBG("Watch toggle on fd: %d (flags: %d) enable: %d", dbus_watch_get_unix_fd(watch), dbus_watch_get_flags(watch), dbus_watch_get_enabled(watch)); hd = dbus_watch_get_data(watch); e_dbus_fd_handler_update(hd); } /* timeout */ static Eina_Bool e_dbus_timeout_handler(void *data) { E_DBus_Timeout_Data *td; td = data; if (!dbus_timeout_get_enabled(td->timeout)) { DBG("timeout_handler (not enabled, ending)"); td->handler = NULL; return ECORE_CALLBACK_CANCEL; } DBG("timeout handler!"); dbus_timeout_handle(td->timeout); return ECORE_CALLBACK_CANCEL; } static void e_dbus_timeout_data_free(void *timeout_data) { E_DBus_Timeout_Data *td = timeout_data; DBG("e_dbus_timeout_data_free"); if (td->handler) ecore_timer_del(td->handler); free(td); } static dbus_bool_t cb_timeout_add(DBusTimeout *timeout, void *data) { E_DBus_Connection *cd; E_DBus_Timeout_Data *td; cd = data; DBG("timeout add!"); td = calloc(1, sizeof(E_DBus_Timeout_Data)); td->cd = cd; dbus_timeout_set_data(timeout, (void *)td, e_dbus_timeout_data_free); td->interval = dbus_timeout_get_interval(timeout); td->timeout = timeout; if (dbus_timeout_get_enabled(timeout)) td->handler = ecore_timer_add(td->interval, e_dbus_timeout_handler, td); td->cd->timeouts = eina_list_append(td->cd->timeouts, td->handler); return true; } static void cb_timeout_del(DBusTimeout *timeout, void *data) { E_DBus_Timeout_Data *td; DBG("timeout del!"); td = (E_DBus_Timeout_Data *)dbus_timeout_get_data(timeout); if (td->handler) { td->cd->timeouts = eina_list_remove(td->cd->timeouts, td->handler); ecore_timer_del(td->handler); td->handler = NULL; } /* Note: timeout data gets freed when the timeout itself is freed by dbus */ } static void cb_timeout_toggle(DBusTimeout *timeout, void *data) { E_DBus_Timeout_Data *td; DBG("timeout toggle!"); td = (E_DBus_Timeout_Data *)dbus_timeout_get_data(timeout); if (dbus_timeout_get_enabled(td->timeout)) { td->interval = dbus_timeout_get_interval(timeout); td->handler = ecore_timer_add(td->interval, e_dbus_timeout_handler, td); } else { ecore_timer_del(td->handler); td->handler = NULL; } } /* dbus connection */ static E_DBus_Connection * e_dbus_connection_new(DBusConnection *conn) { E_DBus_Connection *cd; cd = calloc(1, sizeof(E_DBus_Connection)); if (!cd) return NULL; cd->conn = conn; return cd; } static void e_dbus_connection_free(void *data) { E_DBus_Connection *cd = data; E_DBus_Handler_Data *hd; Ecore_Timer *timer; DBG("e_dbus_connection free!"); EINA_LIST_FREE(cd->fd_handlers, hd) e_dbus_fd_handler_del(hd); EINA_LIST_FREE(cd->timeouts, timer) ecore_timer_del(timer); if (cd->idler) ecore_idler_del(cd->idler); free(cd); } /* public functions */ int e_dbus_init(void) { if (++_edbus_init_count != 1) return _edbus_init_count; if (!eina_init()) { fprintf(stderr,"E-dbus: Enable to initialize eina\n"); return --_edbus_init_count; } _e_dbus_log_dom = eina_log_domain_register("e_dbus", E_DBUS_COLOR_DEFAULT); if (_e_dbus_log_dom < 0) { EINA_LOG_ERR("Unable to create an 'e_dbus' log domain"); eina_shutdown(); return --_edbus_init_count; } if (!ecore_init()) { ERR("E-dbus: Unable to initialize ecore"); eina_shutdown(); return --_edbus_init_count; } return _edbus_init_count; } int e_dbus_shutdown(void) { if (_edbus_init_count <= 0) { EINA_LOG_ERR("Init count not greater than 0 in shutdown."); return 0; } if (--_edbus_init_count) return _edbus_init_count; ecore_shutdown(); eina_log_domain_unregister(_e_dbus_log_dom); _e_dbus_log_dom = -1; eina_shutdown(); return _edbus_init_count; } E_DBus_Connection * e_dbus_connection_setup(DBusConnection *conn) { E_DBus_Connection *cd; cd = e_dbus_connection_new(conn); if (!cd) return NULL; /* connection_setup */ dbus_connection_set_exit_on_disconnect(cd->conn, EINA_FALSE); dbus_connection_allocate_data_slot(&connection_slot); dbus_connection_set_data(cd->conn, connection_slot, (void *)cd, e_dbus_connection_free); dbus_connection_set_watch_functions(cd->conn, cb_watch_add, cb_watch_del, cb_watch_toggle, cd, NULL); dbus_connection_set_timeout_functions(cd->conn, cb_timeout_add, cb_timeout_del, cb_timeout_toggle, cd, NULL); dbus_connection_set_dispatch_status_function(cd->conn, cb_dispatch_status, cd, NULL); cb_dispatch_status(cd->conn, dbus_connection_get_dispatch_status(cd->conn), cd); return cd; } void e_dbus_connection_close(E_DBus_Connection *conn) { if (!conn) return; DBG("e_dbus_connection_close"); if (e_dbus_idler_active) { close_connection++; return; } if (--(conn->refcount) != 0) return; dbus_connection_free_data_slot(&connection_slot); dbus_connection_set_watch_functions(conn->conn, NULL, NULL, NULL, NULL, NULL); dbus_connection_set_timeout_functions(conn->conn, NULL, NULL, NULL, NULL, NULL); dbus_connection_set_dispatch_status_function(conn->conn, NULL, NULL, NULL); /* Idler functin must be cancelled when dbus connection is unreferenced */ if (conn->idler) { ecore_idler_del(conn->idler); conn->idler = NULL; } dbus_connection_close(conn->conn); dbus_connection_unref(conn->conn); // Note: the E_DBus_Connection gets freed when the dbus_connection is cleaned up by the previous unref } #undef DDBG