From 8c80a9912b196526ecdd0c67ddfd059282b21b3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Sat, 25 Apr 2015 20:36:06 +0200 Subject: [PATCH] Add the StatusNotifier implementation --- gtk/CMakeLists.txt | 1 + gtk/Makefile.am | 1 + gtk/main.c | 28 +- gtk/status_icon.c | 121 +++++++- gtk/status_notifier.c | 633 ++++++++++++++++++++++++++++++++++++++++++ gtk/status_notifier.h | 156 +++++++++++ 6 files changed, 931 insertions(+), 9 deletions(-) create mode 100644 gtk/status_notifier.c create mode 100644 gtk/status_notifier.h diff --git a/gtk/CMakeLists.txt b/gtk/CMakeLists.txt index bacbb448f..848a872b9 100644 --- a/gtk/CMakeLists.txt +++ b/gtk/CMakeLists.txt @@ -65,6 +65,7 @@ set(SOURCE_FILES propertybox.c singleinstance.c status_icon.c + status_notifier.c support.c update.c utils.c diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 03c209b55..95a06789d 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -56,6 +56,7 @@ linphone_SOURCES= \ audio_assistant.c \ videowindow.c \ status_icon.c \ + status_notifier.c \ linphone.h if BUILD_WIZARD linphone_SOURCES+= \ diff --git a/gtk/main.c b/gtk/main.c index eb24746cf..fc689a5b6 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -1548,19 +1548,31 @@ static void handle_icon_click(LinphoneStatusIcon *si, void *user_data) { } } +static void linphone_gtk_status_icon_initialised_cb(LinphoneStatusIconParams *params) { + LinphoneStatusIcon *icon = linphone_status_icon_get(); + if(icon) { + linphone_status_icon_start(icon, params); + } + linphone_status_icon_params_unref(params); +} + static void linphone_gtk_init_status_icon(void) { - if(linphone_status_icon_init(NULL, NULL)) { + GtkWidget *menu = create_icon_menu(); + LinphoneStatusIconParams *params = linphone_status_icon_params_new(); + linphone_status_icon_params_set_menu(params, menu); + linphone_status_icon_params_set_title(params, _("Linphone - a video internet phone")); + linphone_status_icon_params_set_on_click_cb(params, handle_icon_click, NULL); + g_object_unref(G_OBJECT(menu)); + + if(linphone_status_icon_init( + (LinphoneStatusIconReadyCb)linphone_gtk_status_icon_initialised_cb, + params)) { + LinphoneStatusIcon *icon = linphone_status_icon_get(); if(icon) { - GtkWidget *menu = create_icon_menu(); - LinphoneStatusIconParams *params = linphone_status_icon_params_new(); - linphone_status_icon_params_set_menu(params, menu); - linphone_status_icon_params_set_title(params, _("Linphone - a video internet phone")); - linphone_status_icon_params_set_on_click_cb(params, handle_icon_click, NULL); linphone_status_icon_start(icon, params); - g_object_unref(G_OBJECT(menu)); - linphone_status_icon_params_unref(params); } + linphone_status_icon_params_unref(params); } } diff --git a/gtk/status_icon.c b/gtk/status_icon.c index 0b46aecb6..daf00203d 100644 --- a/gtk/status_icon.c +++ b/gtk/status_icon.c @@ -24,6 +24,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #endif +#include "status_notifier.h" + typedef struct __LinphoneStatusIconDesc _LinphoneStatusIconDesc; static LinphoneStatusIcon *_linphone_status_icon_instance = NULL; @@ -216,6 +218,8 @@ void _linphone_status_icon_init_cb(const _LinphoneStatusIconDesc *desc, void *us gboolean linphone_status_icon_init(LinphoneStatusIconReadyCb ready_cb, void *user_data) { const _LinphoneStatusIconDesc *desc; void **ctx = g_new(void *, 2); + ctx[0] = ready_cb; + ctx[1] = user_data; if(_linphone_status_icon_find_first_available_impl(&desc, _linphone_status_icon_init_cb, ctx)) { _linphone_status_icon_selected_desc = desc; g_free(ctx); @@ -335,7 +339,7 @@ static void _linphone_status_icon_impl_gtkosx_app_enable_blinking(StatusIcon *si } } -static gboolean _linphone_satus_icon_impl_gtkosx_app_is_supported( +static gboolean _linphone_status_icon_impl_gtkosx_app_is_supported( const _LinphoneStatusIconDesc *desc, gboolean *result, LinphoneStatusIconDescIsSupportedResultCb cb, @@ -356,7 +360,122 @@ static const _LinphoneStatusIconDesc _linphone_status_icon_impl_gtkosx_app_desc #endif +/* Implementation based on the StatusNotifier Freedesktop standard */ +typedef struct { + int x; + int y; +} _LinphoneStatusIconPosition; + +static void _linphone_status_icon_impl_sn_init(LinphoneStatusIcon *si) { + si->data = bc_status_notifier_new(); +} + +static void _linphone_status_icon_impl_sn_uninit(LinphoneStatusIcon *si) { + bc_status_notifier_unref((BcStatusNotifier *)si->data); +} + +static void _linphone_status_icon_impl_sn_activated_cb(BcStatusNotifier *sn, int x, int y, void *user_data) { + LinphoneStatusIcon *si = (LinphoneStatusIcon *)user_data; + _linphone_status_icon_notify_click(si); +} + +static void _linphone_status_icon_impr_sn_get_position(GtkMenu *menu, int *x, int *y, gboolean *push_in, gpointer data) { + _LinphoneStatusIconPosition *pos = (_LinphoneStatusIconPosition *)data; + *x = pos->x; + *y = pos->y; + *push_in = TRUE; +} + +static void _linphone_status_icon_impl_sn_menu_called_cb(BcStatusNotifier *sn, int x, int y, void *user_data) { + LinphoneStatusIcon *si = (LinphoneStatusIcon *)user_data; + GtkWidget *menu = si->params->menu; + _LinphoneStatusIconPosition pos = {x, y}; + gtk_menu_popup( + GTK_MENU(menu), + NULL, + NULL, + _linphone_status_icon_impr_sn_get_position, + &pos, + 0, + gtk_get_current_event_time() + ); +} + +static void _linphone_status_icon_impl_sn_start(LinphoneStatusIcon *si) { + BcStatusNotifier *sn = (BcStatusNotifier *)si->data; + BcStatusNotifierParams *params; + BcStatusNotifierToolTip *tooltip = bc_status_notifier_tool_tip_new("linphone", "Linphone", NULL); + BcStatusNotifierSignalsVTable vtable = {NULL}; + + vtable.activate_called_cb = _linphone_status_icon_impl_sn_activated_cb; + vtable.context_menu_called_cb = _linphone_status_icon_impl_sn_menu_called_cb; + + params = bc_status_notifier_params_new(); + bc_status_notifier_params_set_dbus_prefix(params, "org.kde"); + bc_status_notifier_params_set_category(params, BcStatusNotifierCategoryCommunications); + bc_status_notifier_params_set_id(params, "linphone"); + bc_status_notifier_params_set_title(params, "Linphone"); + bc_status_notifier_params_set_icon_name(params, "linphone"); + bc_status_notifier_params_set_tool_tip(params, tooltip); + bc_status_notifier_params_set_vtable(params, &vtable, si); + + bc_status_notifier_start(sn, params, NULL, NULL); + + bc_status_notifier_tool_tip_unref(tooltip); + bc_status_notifier_params_unref(params); +} + +static void _linphone_status_icon_impl_sn_enable_blinking(LinphoneStatusIcon *si, gboolean val) { + BcStatusNotifier *sn = (BcStatusNotifier *)si->data; + if(val) { + bc_status_notifier_update_status(sn, BcStatusNotifierStatusNeedsAttention); + } else { + bc_status_notifier_update_status(sn, BcStatusNotifierStatusPassive); + } +} + +static void _linphone_status_icon_impl_is_supported_cb(const char *prefix, gboolean result, void **data) { + _LinphoneStatusIconDesc *desc = (_LinphoneStatusIconDesc *)data[0]; + LinphoneStatusIconDescIsSupportedResultCb cb = (LinphoneStatusIconDescIsSupportedResultCb)data[1]; + if(cb) cb(desc, result, data[2]); + g_free(data); + g_free(desc); +} + +static gboolean _linphone_status_icon_impl_sn_is_supported( + const _LinphoneStatusIconDesc *desc, + gboolean *result, + LinphoneStatusIconDescIsSupportedResultCb cb, + void *user_data) { + + _LinphoneStatusIconDesc *desc2 = g_new(_LinphoneStatusIconDesc, 1); + void **data = g_new(void *, 3); + + *desc2 = *desc; + data[0] = desc2; + data[1] = cb; + data[2] = user_data; + bc_status_notifier_is_supported( + "org.kde", + (BcStatusNotifierSupportDetectionCb)_linphone_status_icon_impl_is_supported_cb, + data + ); + return 0; +} + +static const _LinphoneStatusIconDesc _linphone_status_icon_impl_status_notifier = { + .impl_name = "status_notifier", + .init = _linphone_status_icon_impl_sn_init, + .uninit = _linphone_status_icon_impl_sn_uninit, + .start = _linphone_status_icon_impl_sn_start, + .enable_blinking = _linphone_status_icon_impl_sn_enable_blinking, + .is_supported = _linphone_status_icon_impl_sn_is_supported +}; + + +/* List of implementations */ static const _LinphoneStatusIconDesc *_linphone_status_icon_impls[] = { + &_linphone_status_icon_impl_status_notifier, #ifndef HAVE_GTK_OSX &_linphone_status_icon_impl_gtk_desc, #else diff --git a/gtk/status_notifier.c b/gtk/status_notifier.c new file mode 100644 index 000000000..141308ff6 --- /dev/null +++ b/gtk/status_notifier.c @@ -0,0 +1,633 @@ +/* +linphone, gtk-glade interface. +Copyright (C) 2015 Belledonne Communications + +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 "status_notifier.h" +#include +#include +#include + + +static const gchar *_bc_status_notifier_category_to_string[] = { + "ApplicationStatus", + "Communications", + "SystemServices", + "Hardware" +}; + +const gchar *bc_status_notifier_category_to_string(BcStatusNotifierCategory c) { + return _bc_status_notifier_category_to_string[c]; +} + + +static const gchar *_bc_status_notifier_status_to_string[] = { + "Passive", + "Active" + "NeedsAttention" +}; + +const gchar *bc_status_notifier_status_to_string(BcStatusNotifierStatus s) { + return _bc_status_notifier_status_to_string[s]; +}; + + +struct _BcStatusNotifierToolTip { + char *icon_name; + char *title; + char *text; + int ref; +}; + +BcStatusNotifierToolTip *bc_status_notifier_tool_tip_new(const char *icon_name, const char *title, const char *text) { + BcStatusNotifierToolTip *obj = (BcStatusNotifierToolTip *)g_new0(BcStatusNotifierToolTip, 1); + if(icon_name) obj->icon_name = g_strdup(icon_name); + if(title) obj->title = g_strdup(title); + if(text) obj->text = g_strdup(text); + return obj; +} + +BcStatusNotifierToolTip *bc_status_notifier_tool_tip_ref(BcStatusNotifierToolTip *obj) { + obj->ref++; + return obj; +} + +void bc_status_notifier_tool_tip_unref(BcStatusNotifierToolTip *obj) { + obj->ref--; + if(obj->ref < 0) { + if(obj->icon_name) g_free(obj->icon_name); + if(obj->title) g_free(obj->title); + if(obj->text) g_free(obj->text); + g_free(obj); + } +} + +static GVariant *_bc_status_notifier_tool_tip_to_variant(const BcStatusNotifierToolTip *obj) { + GVariant *attr[] = { + g_variant_new_string(obj->icon_name ? obj->icon_name : ""), + g_variant_new_array(G_VARIANT_TYPE_VARIANT, NULL, 0), + g_variant_new_string(obj->title ? obj->title : ""), + g_variant_new_string(obj->text ? obj->text : ""), + }; + return g_variant_new_tuple(attr, 4); +} + + +static const char *_bc_status_notifier_to_string[] = { + "vertical", + "horizontal", + NULL +}; + +static BcStatusNotifierOrientation _bc_status_notifier_orientation_from_string(const char *s) { + int i; + for(i=0; _bc_status_notifier_to_string[i] && g_strcmp0(s, _bc_status_notifier_to_string[i]) == 0; i++); + if(_bc_status_notifier_to_string[i]) return i; + else return BcStatusNotifierOrientationVertical; +} + + +struct _BcStatusNotifierParams{ + char *prefix; + int item_id; + BcStatusNotifierCategory category; + char *id; + char *title; + BcStatusNotifierStatus status; + guint32 window_id; + char *icon_name; + char *overlay_icon_name; + char *attention_icon_name; + char *attention_movie_name; + BcStatusNotifierToolTip *tool_tip; + BcStatusNotifierSignalsVTable vtable; + void *user_data; + int ref; +}; + +#define DEFAULT_PREFIX "org.freedesktop" + +BcStatusNotifierParams *bc_status_notifier_params_new(void) { + BcStatusNotifierParams *obj = (BcStatusNotifierParams *)g_new0(BcStatusNotifierParams, 1); + obj->prefix = g_strdup(DEFAULT_PREFIX); + return obj; +} + +BcStatusNotifierParams *bc_status_notifier_params_ref(BcStatusNotifierParams *obj) { + obj->ref++; + return obj; +} + +void bc_status_notifier_params_unref(BcStatusNotifierParams *obj) { + obj->ref--; + if(obj->ref < 0) { + if(obj->prefix) g_free(obj->prefix); + if(obj->id) g_free(obj->id); + if(obj->title) g_free(obj->title); + if(obj->icon_name) g_free(obj->icon_name); + if(obj->overlay_icon_name) g_free(obj->overlay_icon_name); + if(obj->attention_icon_name) g_free(obj->attention_icon_name); + if(obj->attention_movie_name) g_free(obj->attention_movie_name); + if(obj->tool_tip) bc_status_notifier_tool_tip_unref(obj->tool_tip); + g_free(obj); + } +} + +void bc_status_notifier_params_set_dbus_prefix(BcStatusNotifierParams *obj, const char *prefix) { + if(obj->prefix) g_free(obj->prefix); + if(prefix) obj->prefix = g_strdup(prefix); + else obj->prefix = NULL; +} + +const char *bc_satus_notifier_params_get_dbus_prefix(const BcStatusNotifierParams *obj) { + return obj->prefix; +} + +void bc_status_notifier_params_set_item_id(BcStatusNotifierParams *obj, int item_id) { + obj->item_id = item_id; +} + +int bc_status_notifier_params_get_item_id(const BcStatusNotifierParams *obj) { + return obj->item_id; +} + +void bc_status_notifier_params_set_category(BcStatusNotifierParams *obj, BcStatusNotifierCategory category) { + obj->category = category; +} + +BcStatusNotifierCategory bc_status_notifier_params_get_category(const BcStatusNotifierParams *obj) { + return obj->category; +} + +void bc_status_notifier_params_set_id(BcStatusNotifierParams *obj, const char *id) { + if(obj->id) g_free(obj->id); + if(id) obj->id = g_strdup(id); + else obj->id = NULL; +} + +const char *bc_status_notifier_params_get_id(const BcStatusNotifierParams *obj) { + return obj->id; +} + +void bc_status_notifier_params_set_title(BcStatusNotifierParams *obj, const char *title) { + if(obj->title) g_free(obj->title); + if(title) obj->title = g_strdup(title); + else obj->title = NULL; +} + +const char *bc_status_notifier_params_get_title(const BcStatusNotifierParams *obj) { + return obj->title; +} + +void bc_status_notifier_params_set_status(BcStatusNotifierParams *obj, BcStatusNotifierStatus status) { + obj->status = status; +} + +BcStatusNotifierStatus bc_status_notifier_params_get_status(const BcStatusNotifierParams *obj) { + return obj->status; +} + +void bc_status_notifier_params_set_window_id(BcStatusNotifierParams *obj, guint32 window_id) { + obj->window_id = window_id; +} + +guint32 bc_status_notifier_params_get_window_id(const BcStatusNotifierParams *obj) { + return obj->window_id; +} + +void bc_status_notifier_params_set_icon_name(BcStatusNotifierParams *obj, const char *name) { + if(obj->icon_name) g_free(obj->icon_name); + if(name) obj->icon_name = g_strdup(name); + else obj->icon_name = NULL; +} + +const char *bc_status_notifier_params_get_icon_name(const BcStatusNotifierParams *obj) { + return obj->icon_name; +} + +void bc_status_notifier_params_set_overlay_icon_name(BcStatusNotifierParams *obj, const char *name) { + if(obj->overlay_icon_name) g_free(obj->overlay_icon_name); + if(name) obj->overlay_icon_name = g_strdup(name); + else obj->overlay_icon_name = NULL; +} + +const char *bc_status_notifier_params_get_overlay_icon_name(const BcStatusNotifierParams *obj) { + return obj->overlay_icon_name; +} + +void bc_status_notifier_params_set_attention_icon_name(BcStatusNotifierParams *obj, const char *name) { + if(obj->attention_icon_name) g_free(obj->attention_icon_name); + if(name) obj->attention_icon_name = g_strdup(name); + else obj->attention_icon_name = NULL; +} + +const char *bc_status_notifier_params_get_attention_icon_name(const BcStatusNotifierParams *obj) { + return obj->attention_icon_name; +} + +void bc_status_notifier_params_set_attention_movie_name(BcStatusNotifierParams *obj, const char *name) { + if(obj->attention_movie_name) g_free(obj->attention_movie_name); + if(name) obj->attention_movie_name = g_strdup(name); + else obj->attention_movie_name = NULL; +} + +const char *bc_status_notifier_params_get_attention_movie_name(const BcStatusNotifierParams *obj) { + return obj->attention_movie_name; +} + +void bc_status_notifier_params_set_tool_tip(BcStatusNotifierParams *obj, BcStatusNotifierToolTip *tool_tip) { + if(obj->tool_tip) bc_status_notifier_tool_tip_unref(obj->tool_tip); + if(tool_tip) obj->tool_tip = bc_status_notifier_tool_tip_ref(tool_tip); + else obj->tool_tip = NULL; +} + +const BcStatusNotifierToolTip *bc_status_notifier_params_get_tool_tip(const BcStatusNotifierParams *obj) { + return obj->tool_tip; +} + +void bc_status_notifier_params_set_vtable(BcStatusNotifierParams *obj, const BcStatusNotifierSignalsVTable *vtable, void *user_data) { + obj->vtable = *vtable; + obj->user_data = user_data; +} + + +struct _BcStatusNotifier { + BcStatusNotifierParams *params; + guint bus_owner_id; + GDBusConnection *conn; + BcStatusNotifierState state; + BcStatusNotifierStateVTable vtable; + void *user_data; + int ref; +}; + +#define ITEM_NAME "StatusNotifierItem" +#define WATCHER_NAME "StatusNotifierWatcher" +#define CALL_TIMEOUT 1000 + +#define STATUS_NOTIFIER_INTROSPECTION_DATA \ + " \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + " + +BcStatusNotifier *bc_status_notifier_new(void) { + return (BcStatusNotifier *)g_new0(BcStatusNotifier, 1); +} + +BcStatusNotifier *bc_status_notifier_ref(BcStatusNotifier *obj) { + obj->ref++; + return obj; +} + +void bc_status_notifier_unref(BcStatusNotifier *obj) { + obj->ref--; + if(obj->ref < 0) { + bc_status_notifier_stop(obj); + if(obj->params) bc_status_notifier_params_unref(obj->params); + g_free(obj); + } +} + +static GVariant *_bc_status_notifier_get_property_cb( + GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GError **error, + BcStatusNotifier *sn) { + + + GVariant *value = NULL; + if(g_strcmp0(property_name, "Category") == 0) { + value = g_variant_new_string(bc_status_notifier_category_to_string(sn->params->category)); + } else if(g_strcmp0(property_name, "Id") == 0) { + value = g_variant_new_string(sn->params->id ? sn->params->id : ""); + } else if(g_strcmp0(property_name, "Title") == 0) { + value = g_variant_new_string(sn->params->title ? sn->params->title : ""); + } else if(g_strcmp0(property_name, "Status") == 0) { + value = g_variant_new_string(bc_status_notifier_status_to_string(sn->params->status)); + } else if(g_strcmp0(property_name, "WindowId") == 0) { + value = g_variant_new_uint32(sn->params->window_id); + } else if(g_strcmp0(property_name, "IconName") == 0) { + value = g_variant_new_string(sn->params->icon_name ? sn->params->icon_name : ""); + } else if(g_strcmp0(property_name, "IconPixmap") == 0) { + value = g_variant_new_array(G_VARIANT_TYPE_VARIANT, NULL, 0); + } else if(g_strcmp0(property_name, "OverlayIconName") == 0) { + value = g_variant_new_string(sn->params->overlay_icon_name ? sn->params->overlay_icon_name : ""); + } else if(g_strcmp0(property_name, "OverlayIconPixmap") == 0) { + value = g_variant_new_array(G_VARIANT_TYPE_VARIANT, NULL, 0); + } else if(g_strcmp0(property_name, "AttentionIconName") == 0) { + value = g_variant_new_string(sn->params->attention_icon_name ? sn->params->attention_icon_name : ""); + } else if(g_strcmp0(property_name, "AttentionIconPixmap") == 0) { + value = g_variant_new_array(G_VARIANT_TYPE_VARIANT, NULL, 0); + } else if(g_strcmp0(property_name, "AttentionMovieName") == 0) { + value = g_variant_new_string(sn->params->attention_movie_name ? sn->params->attention_movie_name : ""); + } else if(g_strcmp0(property_name, "ToolTip") == 0) { + if(sn->params->tool_tip) { + value = _bc_status_notifier_tool_tip_to_variant(sn->params->tool_tip); + } else { + BcStatusNotifierToolTip *tool_tip = bc_status_notifier_tool_tip_new("", "", ""); + value = _bc_status_notifier_tool_tip_to_variant(tool_tip); + bc_status_notifier_tool_tip_unref(tool_tip); + } + } + return value; +} + +static void _bc_status_notifier_method_call_cb( + GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + BcStatusNotifier *sn) { + + if(g_strcmp0(method_name, "ContextMenu") == 0) { + if(sn->params->vtable.context_menu_called_cb) { + int x, y; + g_variant_get_child(parameters, 0, "i", &x); + g_variant_get_child(parameters, 1, "i", &y); + sn->params->vtable.context_menu_called_cb(sn, x, y, sn->params->user_data); + } + } else if(g_strcmp0(method_name, "Activate") == 0) { + if(sn->params->vtable.activate_called_cb) { + int x, y; + g_variant_get_child(parameters, 0, "i", &x); + g_variant_get_child(parameters, 1, "i", &y); + sn->params->vtable.activate_called_cb(sn, x, y, sn->params->user_data); + } + } else if(g_strcmp0(method_name, "SecondaryActivate") == 0) { + if(sn->params->vtable.secondary_activate_called_cb) { + int x, y; + g_variant_get_child(parameters, 0, "i", &x); + g_variant_get_child(parameters, 1, "i", &y); + sn->params->vtable.secondary_activate_called_cb(sn, x, y, sn->params->user_data); + } + } else if(g_strcmp0(method_name, "Scroll") == 0) { + if(sn->params->vtable.scroll_called_cb) { + int delta; + BcStatusNotifierOrientation orient; + char *orient_str; + g_variant_get_child(parameters, 0, "i", &delta); + g_variant_get_child(parameters, 1, "&s", &orient_str); + orient = _bc_status_notifier_orientation_from_string(orient_str); + sn->params->vtable.scroll_called_cb(sn, delta, orient, sn->params->user_data); + } + } + g_dbus_method_invocation_return_value(invocation, NULL); +} + +static void _bc_status_notifier_bus_acquired_cb(GDBusConnection *conn, const gchar *name, BcStatusNotifier *sn) { + char *interface_name = g_strdup_printf("%s.%s", sn->params->prefix, ITEM_NAME); + char *item_path = g_strdup_printf("/%s", ITEM_NAME); + GDBusInterfaceVTable vtable = { + (GDBusInterfaceMethodCallFunc)_bc_status_notifier_method_call_cb, + (GDBusInterfaceGetPropertyFunc)_bc_status_notifier_get_property_cb, + NULL + }; + + GDBusNodeInfo *node_info = g_dbus_node_info_new_for_xml(STATUS_NOTIFIER_INTROSPECTION_DATA, NULL); + GDBusInterfaceInfo *interface = g_dbus_node_info_lookup_interface( + node_info, + interface_name + ); + g_free(interface_name); + + sn->conn = conn; + + g_dbus_connection_register_object( + conn, + item_path, + interface, + &vtable, + bc_status_notifier_ref(sn), + (GDestroyNotify)bc_status_notifier_unref, + NULL + ); + g_free(item_path); + + g_dbus_node_info_unref(node_info); +} + +static void _bc_status_notifier_name_acquired_cb(GDBusConnection *conn, const gchar *name, BcStatusNotifier *sn) { + GVariant *item_name = g_variant_new_string(name); + GVariant *parameters = g_variant_new_tuple(&item_name, 1); + char *watcher_bus_name = g_strdup_printf("%s.%s", sn->params->prefix, WATCHER_NAME); + char *watcher_interface_name = watcher_bus_name; + char *watcher_path = g_strdup_printf("/%s", WATCHER_NAME); + + g_dbus_connection_call( + conn, + watcher_bus_name, + watcher_path, + watcher_interface_name, + "RegisterStatusNotifierItem", + parameters, + NULL, + G_DBUS_CALL_FLAGS_NONE, + CALL_TIMEOUT, + NULL, + NULL, + NULL + ); + g_free(watcher_bus_name); + g_free(watcher_path); + + sn->state = BcStatusNotifierStateRunning; + if(sn->vtable.success) sn->vtable.success(sn, sn->user_data); +} + +static void _bc_status_notifier_name_lost(GDBusConnection *conn, const gchar *name, BcStatusNotifier *sn) { + if(conn == NULL) { + sn->state = BcStatusNotifierStateStopped; + if(sn->vtable.fail) sn->vtable.fail(sn, sn->user_data); + } +} + +void bc_status_notifier_start(BcStatusNotifier* obj, BcStatusNotifierParams* params, const BcStatusNotifierStateVTable *vtable, void *user_data) { + if(obj->state == BcStatusNotifierStateStopped) { + pid_t pid = getpid(); + char *dbus_name = g_strdup_printf("%s.%s-%d-%d", params->prefix, ITEM_NAME, pid, params->item_id); + + if(obj->params) bc_status_notifier_params_unref(obj->params); + obj->params = bc_status_notifier_params_ref(params); + if(vtable) obj->vtable = *vtable; + else { + obj->vtable.success = NULL; + obj->vtable.fail = NULL; + } + obj->user_data = user_data; + obj->state = BcStatusNotifierStateStarting; + obj->bus_owner_id = g_bus_own_name( + G_BUS_TYPE_SESSION, + dbus_name, + G_BUS_NAME_OWNER_FLAGS_NONE, + (GBusAcquiredCallback)_bc_status_notifier_bus_acquired_cb, + (GBusNameAcquiredCallback)_bc_status_notifier_name_acquired_cb, + (GBusNameLostCallback)_bc_status_notifier_name_lost, + bc_status_notifier_ref(obj), + (GDestroyNotify)bc_status_notifier_unref + ); + g_free(dbus_name); + } +} + +void bc_status_notifier_stop(BcStatusNotifier *obj) { + if(obj->state == BcStatusNotifierStateRunning) { + g_bus_unown_name(obj->bus_owner_id); + obj->bus_owner_id = 0; + obj->conn = NULL; + obj->state = BcStatusNotifierStateStopped; + } +} + +const BcStatusNotifierParams *bc_status_notifier_get_params(const BcStatusNotifier *obj) { + return obj->params; +} + +static void _bc_status_notifier_emit_signal(const BcStatusNotifier *obj, const char *sig_name, GVariant *parameters) { + char *item_interface_name = g_strdup_printf("%s.%s", obj->params->prefix, ITEM_NAME); + char *item_path = g_strdup_printf("/%s", ITEM_NAME); + g_dbus_connection_emit_signal( + obj->conn, + NULL, + item_path, + item_interface_name, + sig_name, + parameters, + NULL + ); + g_free(item_interface_name); + g_free(item_path); +} + +void bc_status_notifier_update_title(BcStatusNotifier *obj, const char *title) { + bc_status_notifier_params_set_title(obj->params, title); + _bc_status_notifier_emit_signal(obj, "NewTitle", NULL); +} + +void bc_status_notifier_update_icon(BcStatusNotifier *obj, const char *icon_name) { + bc_status_notifier_params_set_icon_name(obj->params, icon_name); + _bc_status_notifier_emit_signal(obj, "NewIcon", NULL); +} + +void bc_status_notifier_update_attention_icon(BcStatusNotifier *obj, const char *icon_name) { + bc_status_notifier_params_set_attention_icon_name(obj->params, icon_name); + _bc_status_notifier_emit_signal(obj, "NewAttentionIcon", NULL); +} + +void bc_status_notifier_update_overlay_icon(BcStatusNotifier *obj, const char *icon_name) { + bc_status_notifier_params_set_overlay_icon_name(obj->params, icon_name); + _bc_status_notifier_emit_signal(obj, "NewOverlayIcon", NULL); +} + +void bc_status_notifier_update_tool_tip(BcStatusNotifier *obj, BcStatusNotifierToolTip *tool_tip) { + bc_status_notifier_params_set_tool_tip(obj->params, tool_tip); + _bc_status_notifier_emit_signal(obj, "NewToolTip", NULL); +} + +void bc_status_notifier_update_status(BcStatusNotifier *obj, BcStatusNotifierStatus status) { + GVariant *status_var = g_variant_new_string(bc_status_notifier_status_to_string(status)); + GVariant *parameter = g_variant_new_tuple(&status_var, 1); + bc_status_notifier_params_set_status(obj->params, status); + _bc_status_notifier_emit_signal(obj, "NewStatus", parameter); +} + + +typedef struct _BcWatcherDetectionCtx { + char *prefix; + guint watcher_id; + BcStatusNotifierSupportDetectionCb cb; + void *user_data; +} BcWatcherDetectionCtx; + +static void _bc_watcher_detection_ctx_free(BcWatcherDetectionCtx *obj) { + g_free(obj->prefix); + g_free(obj); +} + +static void _bc_watcher_name_appeared_cb(GDBusConnection *conn, const char *name, const char *name_owner, BcWatcherDetectionCtx *ctx) { + g_bus_unwatch_name(ctx->watcher_id); + if(ctx->cb) ctx->cb(ctx->prefix, 1, ctx->user_data); +} + +static void _bc_watcher_name_vannished_cb(GDBusConnection *conn, const char *name, BcWatcherDetectionCtx *ctx) { + g_bus_unwatch_name(ctx->watcher_id); + if(ctx->cb) ctx->cb(ctx->prefix, 0, ctx->user_data); +} + +void bc_status_notifier_is_supported(const char* prefix, BcStatusNotifierSupportDetectionCb cb, void *user_data) { + char *bus_name = g_strdup_printf("%s.%s", prefix, WATCHER_NAME); + BcWatcherDetectionCtx *ctx = (BcWatcherDetectionCtx *)g_new0(BcWatcherDetectionCtx, 1); + ctx->prefix = g_strdup(prefix); + ctx->cb = cb; + ctx->user_data = user_data; + + ctx->watcher_id = g_bus_watch_name( + G_BUS_TYPE_SESSION, + bus_name, + G_BUS_NAME_WATCHER_FLAGS_NONE, + (GBusNameAppearedCallback)_bc_watcher_name_appeared_cb, + (GBusNameVanishedCallback)_bc_watcher_name_vannished_cb, + ctx, + (GDestroyNotify)_bc_watcher_detection_ctx_free + ); + g_free(bus_name); +} diff --git a/gtk/status_notifier.h b/gtk/status_notifier.h new file mode 100644 index 000000000..c357b20b0 --- /dev/null +++ b/gtk/status_notifier.h @@ -0,0 +1,156 @@ +/* +linphone, gtk-glade interface. +Copyright (C) 2015 Belledonne Communications + +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. +*/ + +#ifndef STATUS_NOTIFIER_H +#define STATUS_NOTIFIER_H + +#include + +struct _BcStatusNotifier; + + +typedef enum { + BcStatusNotifierCategoryApplicationStatus, + BcStatusNotifierCategoryCommunications, + BcStatusNotifierCategorySystemService, + BcStatusNotifierCategoryHardware +} BcStatusNotifierCategory; + +const gchar *bc_status_notifier_category_to_string(BcStatusNotifierCategory c); + + +typedef enum { + BcStatusNotifierStatusPassive, + BcStatusNotifierStatusActive, + BcStatusNotifierStatusNeedsAttention +} BcStatusNotifierStatus; + +const gchar *bc_status_notifier_status_to_string(BcStatusNotifierStatus s); + + +typedef struct _BcStatusNotifierToolTip BcStatusNotifierToolTip; + +BcStatusNotifierToolTip *bc_status_notifier_tool_tip_new(const char *icon_name, const char *title, const char *text); +BcStatusNotifierToolTip *bc_status_notifier_tool_tip_ref(BcStatusNotifierToolTip *obj); +void bc_status_notifier_tool_tip_unref(BcStatusNotifierToolTip *obj); + + +typedef enum _BcStatusNotifierOrientation { + BcStatusNotifierOrientationVertical, + BcStatusNotifierOrientationHorizontal +} BcStatusNotifierOrientation; + + +typedef void (*BcStatusNotifierContextMenuCalledCb)(struct _BcStatusNotifier *sn, int x, int y, void *user_data); +typedef void (*BcStatusNotifierActivateCalledCb)(struct _BcStatusNotifier *sn, int x, int y, void *user_data); +typedef void (*BcStatusNotifierSecondaryActivateCb)(struct _BcStatusNotifier *sn, int x, int y, void *user_data); +typedef void (*BcStatusNotifierScrollCalledCb)(struct _BcStatusNotifier *sn, int delta, BcStatusNotifierOrientation o, void *user_data); + +typedef struct _BcStatusNotifierSignalsVTable { + BcStatusNotifierContextMenuCalledCb context_menu_called_cb; + BcStatusNotifierActivateCalledCb activate_called_cb; + BcStatusNotifierSecondaryActivateCb secondary_activate_called_cb; + BcStatusNotifierScrollCalledCb scroll_called_cb; +} BcStatusNotifierSignalsVTable; + + +typedef struct _BcStatusNotifierParams BcStatusNotifierParams; + +BcStatusNotifierParams *bc_status_notifier_params_new(void); +BcStatusNotifierParams *bc_status_notifier_params_ref(BcStatusNotifierParams *obj); +void bc_status_notifier_params_unref(BcStatusNotifierParams *obj); + +void bc_status_notifier_params_set_dbus_prefix(BcStatusNotifierParams *obj, const char *prefix); +const char *bc_satus_notifier_params_get_dbus_prefix(const BcStatusNotifierParams *obj); + +void bc_status_notifier_params_set_item_id(BcStatusNotifierParams *obj, int item_id); +int bc_status_notifier_params_get_item_id(const BcStatusNotifierParams *obj); + +void bc_status_notifier_params_set_category(BcStatusNotifierParams *obj, BcStatusNotifierCategory category); +BcStatusNotifierCategory bc_status_notifier_params_get_category(const BcStatusNotifierParams *obj); + +void bc_status_notifier_params_set_id(BcStatusNotifierParams *obj, const char *id); +const char *bc_status_notifier_params_get_id(const BcStatusNotifierParams *obj); + +void bc_status_notifier_params_set_title(BcStatusNotifierParams *obj, const char *title); +const char *bc_status_notifier_params_get_title(const BcStatusNotifierParams *obj); + +void bc_status_notifier_params_set_status(BcStatusNotifierParams *obj, BcStatusNotifierStatus status); +BcStatusNotifierStatus bc_status_notifier_params_get_status(const BcStatusNotifierParams *obj); + +void bc_status_notifier_params_set_window_id(BcStatusNotifierParams *obj, guint32 window_id); +guint32 bc_status_notifier_params_get_window_id(const BcStatusNotifierParams *obj); + +void bc_status_notifier_params_set_icon_name(BcStatusNotifierParams *obj, const char *name); +const char *bc_status_notifier_params_get_icon_name(const BcStatusNotifierParams *obj); + +void bc_status_notifier_params_set_overlay_icon_name(BcStatusNotifierParams *obj, const char *name); +const char *bc_status_notifier_params_get_overlay_icon_name(const BcStatusNotifierParams *obj); + +void bc_status_notifier_params_set_attention_icon_name(BcStatusNotifierParams *obj, const char *icon_name); +const char *bc_status_notifier_params_get_attention_icon_name(const BcStatusNotifierParams *obj); + +void bc_status_notifier_params_set_attention_movie_name(BcStatusNotifierParams *obj, const char *name); +const char *bc_status_notifier_params_get_attention_movie_name(const BcStatusNotifierParams *obj); + +void bc_status_notifier_params_set_tool_tip(BcStatusNotifierParams *obj, BcStatusNotifierToolTip *tool_tip); +const BcStatusNotifierToolTip *bc_status_notifier_params_get_tool_tip(const BcStatusNotifierParams *obj); + +void bc_status_notifier_params_set_vtable(BcStatusNotifierParams *obj, const BcStatusNotifierSignalsVTable *vtable, void *user_data); + + +typedef enum _BcStatusNotifierState { + BcStatusNotifierStateStopped, + BcStatusNotifierStateStarting, + BcStatusNotifierStateRunning +} BcStatusNotifierState; + + +typedef void (*BcStatusNotifierStartedCb)(struct _BcStatusNotifier *sn, void *user_data); +typedef void (*BcStatusNotifierStartingFailedCb)(struct _BcStatusNotifier *sn, void *user_data); + +typedef struct _BcStatusNotifierStateVTable { + BcStatusNotifierStartedCb success; + BcStatusNotifierStartingFailedCb fail; +} BcStatusNotifierStateVTable; + + +typedef struct _BcStatusNotifier BcStatusNotifier; + +BcStatusNotifier *bc_status_notifier_new(void); +BcStatusNotifier *bc_status_notifier_ref(BcStatusNotifier *obj); +void bc_status_notifier_unref(BcStatusNotifier *obj); + +void bc_status_notifier_start(BcStatusNotifier* obj, BcStatusNotifierParams* params, const BcStatusNotifierStateVTable* vtable, void* user_data); +void bc_status_notifier_stop(BcStatusNotifier* obj); + +const BcStatusNotifierParams *bc_status_notifier_get_params(const BcStatusNotifier *obj); +void bc_status_notifier_update_title(BcStatusNotifier* obj, const char* title); +void bc_status_notifier_update_icon(BcStatusNotifier* obj, const char* icon_name); +void bc_status_notifier_update_attention_icon(BcStatusNotifier* obj, const char* icon_name); +void bc_status_notifier_update_overlay_icon(BcStatusNotifier* obj, const char* icon_name); +void bc_status_notifier_update_tool_tip(BcStatusNotifier* obj, BcStatusNotifierToolTip* tool_tip); +void bc_status_notifier_update_status(BcStatusNotifier* obj, BcStatusNotifierStatus status); + + +typedef void (*BcStatusNotifierSupportDetectionCb)(const char *prefix, gboolean is_supported, void *user_data); + +void bc_status_notifier_is_supported(const char* prefix, BcStatusNotifierSupportDetectionCb cb, void *user_data); + +#endif \ No newline at end of file