From d0bd6bf88398aeba3e143f7c635645dd2c74e068 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Wed, 22 Apr 2015 22:18:15 +0200 Subject: [PATCH 1/6] Creates an interface which abstracts all ways to instanciate a status icon and create an implementation based on GtkStatusIcon --- gtk/CMakeLists.txt | 1 + gtk/Makefile.am | 1 + gtk/main.c | 92 ++++------------- gtk/status_icon.c | 241 +++++++++++++++++++++++++++++++++++++++++++++ gtk/status_icon.h | 42 ++++++++ 5 files changed, 303 insertions(+), 74 deletions(-) create mode 100644 gtk/status_icon.c create mode 100644 gtk/status_icon.h diff --git a/gtk/CMakeLists.txt b/gtk/CMakeLists.txt index ea923ea6d..bacbb448f 100644 --- a/gtk/CMakeLists.txt +++ b/gtk/CMakeLists.txt @@ -64,6 +64,7 @@ set(SOURCE_FILES main.c propertybox.c singleinstance.c + status_icon.c support.c update.c utils.c diff --git a/gtk/Makefile.am b/gtk/Makefile.am index f979376ef..03c209b55 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -55,6 +55,7 @@ linphone_SOURCES= \ config-fetching.c \ audio_assistant.c \ videowindow.c \ + status_icon.c \ linphone.h if BUILD_WIZARD linphone_SOURCES+= \ diff --git a/gtk/main.c b/gtk/main.c index aa993cf4c..d4f5d6718 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -55,6 +55,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #endif +#include "status_icon.h" const char *this_program_ident_string="linphone_ident_string=" LINPHONE_VERSION; @@ -1496,15 +1497,6 @@ void linphone_gtk_link_to_website(GtkWidget *item){ linphone_gtk_open_browser(home); } -#ifndef HAVE_GTK_OSX - -static GtkStatusIcon *icon=NULL; - -static void icon_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data){ - GtkWidget *menu=(GtkWidget*)g_object_get_data(G_OBJECT(status_icon),"menu"); - gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time); -} - static GtkWidget *create_icon_menu(){ GtkWidget *menu=gtk_menu_new(); GtkWidget *menu_item; @@ -1542,7 +1534,7 @@ void linphone_gtk_save_main_window_position(GtkWindow* mw, GdkEvent *event, gpoi gtk_window_get_position(GTK_WINDOW(mw), &main_window_x, &main_window_y); } -static void handle_icon_click() { +static void handle_icon_click(LinphoneStatusIcon *si, void *user_data) { GtkWidget *mw=linphone_gtk_get_main_window(); if (!gtk_window_is_active((GtkWindow*)mw)) { if(!gtk_widget_is_drawable(mw)){ @@ -1556,65 +1548,25 @@ static void handle_icon_click() { } } -static void linphone_gtk_init_status_icon(){ - const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON); - const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png"); - GdkPixbuf *pbuf=create_pixbuf(icon_path); - GtkWidget *menu=create_icon_menu(); - const char *title; - title=linphone_gtk_get_ui_config("title",_("Linphone - a video internet phone")); - icon=gtk_status_icon_new_from_pixbuf(pbuf); -#if GTK_CHECK_VERSION(2,20,2) - gtk_status_icon_set_name(icon,title); -#endif - g_signal_connect_swapped(G_OBJECT(icon),"activate",(GCallback)handle_icon_click,NULL); - g_signal_connect(G_OBJECT(icon),"popup-menu",(GCallback)icon_popup_menu,NULL); - gtk_status_icon_set_tooltip(icon,title); - gtk_status_icon_set_visible(icon,TRUE); - g_object_set_data(G_OBJECT(icon),"menu",menu); - g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)gtk_widget_destroy,menu); - g_object_set_data(G_OBJECT(icon),"icon",pbuf); - g_object_weak_ref(G_OBJECT(icon),(GWeakNotify)g_object_unref,pbuf); - pbuf=create_pixbuf(call_icon_path); - g_object_set_data(G_OBJECT(icon),"call_icon",pbuf); +static void linphone_gtk_init_status_icon(void) { + 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); + } } -static gboolean do_icon_blink(GtkStatusIcon *gi){ - GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon"); - GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon"); - GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi); - if (cur_icon==call_icon){ - gtk_status_icon_set_from_pixbuf(gi,normal_icon); - }else{ - gtk_status_icon_set_from_pixbuf(gi,call_icon); +void linphone_gtk_status_icon_set_blinking(gboolean val) { + LinphoneStatusIcon *icon = linphone_status_icon_get(); + if(icon) { + linphone_status_icon_enable_blinking(icon, val); } - return TRUE; -} - -#endif - -void linphone_gtk_status_icon_set_blinking(gboolean val){ -#ifdef HAVE_GTK_OSX - static gint attention_id; - GtkosxApplication *theMacApp=gtkosx_application_get(); - if (val) - attention_id=gtkosx_application_attention_request(theMacApp,CRITICAL_REQUEST); - else gtkosx_application_cancel_attention_request(theMacApp,attention_id); -#else - if (icon!=NULL){ - guint tout; - tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout")); - if (val && tout==0){ - tout=g_timeout_add(500,(GSourceFunc)do_icon_blink,icon); - g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout)); - }else if (!val && tout!=0){ - GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon"); - g_source_remove(tout); - g_object_set_data(G_OBJECT(icon),"timeout",NULL); - gtk_status_icon_set_from_pixbuf(icon,normal_icon); - } - } -#endif } void linphone_gtk_options_activate(GtkWidget *item){ @@ -2018,10 +1970,6 @@ static void linphone_gtk_quit(void){ quit_done=TRUE; linphone_gtk_quit_core(); linphone_gtk_uninit_instance(); -#ifndef HAVE_GTK_OSX - g_object_unref(icon); - icon=NULL; -#endif #ifdef HAVE_NOTIFY notify_uninit(); #endif @@ -2242,10 +2190,6 @@ core_start: goto core_start; } if (config_file) g_free(config_file); -#ifndef HAVE_GTK_OSX - /*workaround a bug on win32 that makes status icon still present in the systray even after program exit.*/ - if (icon) gtk_status_icon_set_visible(icon,FALSE); -#endif free(progpath); /*output a translated "hello" string to the terminal, which allows the builder to check that translations are working.*/ if (selftest){ diff --git a/gtk/status_icon.c b/gtk/status_icon.c new file mode 100644 index 000000000..d57a04f10 --- /dev/null +++ b/gtk/status_icon.c @@ -0,0 +1,241 @@ +/* +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 +#include +#include "status_icon.h" +#include "linphone.h" + +struct _LinphoneStatusIconParams { + char *title; + GtkWidget *menu; + LinphoneStatusIconOnClickCallback on_click_cb; + void *user_data; + int ref; +}; + +LinphoneStatusIconParams *linphone_status_icon_params_new(void) { + return g_new0(LinphoneStatusIconParams, 1); +} + +LinphoneStatusIconParams *linphone_status_icon_params_ref(LinphoneStatusIconParams *obj) { + obj->ref++; + return obj; +} + +void linphone_status_icon_params_unref(LinphoneStatusIconParams *obj) { + obj->ref--; + if(obj->ref < 0) { + if(obj->title) g_free(obj->title); + if(obj->menu) g_object_unref(obj->menu); + g_free(obj); + } +} + +void linphone_status_icon_params_set_title(LinphoneStatusIconParams *obj, const char *title) { + if(obj->title) g_free(obj->title); + if(title) obj->title = g_strdup(title); + else obj->title = NULL; +} + +void linphone_status_icon_params_set_menu(LinphoneStatusIconParams *obj, GtkWidget *menu) { + if(obj->menu) g_object_unref(obj->menu); + if(menu) obj->menu = g_object_ref(menu); + else obj->menu = NULL; +} + +void linphone_status_icon_params_set_on_click_cb(LinphoneStatusIconParams *obj, LinphoneStatusIconOnClickCallback cb, void *user_data) { + obj->on_click_cb = cb; + obj->user_data = user_data; +} + + +typedef void (*LinphoneStatusIconDescInitFunc)(LinphoneStatusIcon *obj); +typedef void (*LinphoneStatusIconDescUninitFunc)(LinphoneStatusIcon *obj); +typedef void (*LinphoneStatusIconDescStartFunc)(LinphoneStatusIcon *obj); +typedef void (*LinphoneStatusIconDescEnableBlinkingFunc)(LinphoneStatusIcon *obj, gboolean enable); +typedef gboolean (*LinphoneStatusIconDescIsSupported)(void); + +typedef struct { + const char *impl_name; + LinphoneStatusIconDescInitFunc init; + LinphoneStatusIconDescUninitFunc uninit; + LinphoneStatusIconDescStartFunc start; + LinphoneStatusIconDescEnableBlinkingFunc enable_blinking; + LinphoneStatusIconDescIsSupported is_supported; +} _LinphoneStatusIconDesc; + +static const _LinphoneStatusIconDesc *_linphone_status_icon_impls[]; + +static const _LinphoneStatusIconDesc *_status_icon_find_instance(void) { + int i; + for(i=0; _linphone_status_icon_impls[i] && !_linphone_status_icon_impls[i]->is_supported(); i++); + return _linphone_status_icon_impls[i]; +} + + +struct _LinphoneStatusIcon { + const _LinphoneStatusIconDesc *desc; + LinphoneStatusIconParams *params; + void *data; +}; + +static LinphoneStatusIcon *_linphone_status_icon_new(const _LinphoneStatusIconDesc *desc) { + LinphoneStatusIcon *si = (LinphoneStatusIcon *)g_new0(LinphoneStatusIcon, 1); + si->desc = desc; + if(desc->init) desc->init(si); + return si; +} + +static void _linphone_status_icon_free(LinphoneStatusIcon *obj) { + if(obj->desc->uninit) obj->desc->uninit(obj->data); + g_free(obj); +} + +const char *linphone_status_icon_get_implementation_name(const LinphoneStatusIcon *obj) { + return obj->desc->impl_name; +} + +void linphone_status_icon_start(LinphoneStatusIcon *obj, LinphoneStatusIconParams *params) { + obj->params = linphone_status_icon_params_ref(params); + obj->desc->start(obj); +} + +void linphone_status_icon_enable_blinking(LinphoneStatusIcon *obj, gboolean enable) { + obj->desc->enable_blinking(obj, enable); +} + +static void _linphone_status_icon_notify_click(LinphoneStatusIcon *obj) { + if(obj->params->on_click_cb) { + obj->params->on_click_cb(obj, obj->params->user_data); + } +} + + +static LinphoneStatusIcon *_linphone_status_icon_instance = NULL; + +static void _linphone_status_icon_free_singleton(void) { + _linphone_status_icon_free(_linphone_status_icon_instance); +} + +LinphoneStatusIcon *linphone_status_icon_get(void) { + if(_linphone_status_icon_instance == NULL) { + const _LinphoneStatusIconDesc *desc = _status_icon_find_instance(); + if(desc) { + _linphone_status_icon_instance = _linphone_status_icon_new(desc); + atexit(_linphone_status_icon_free_singleton); + } + } + return _linphone_status_icon_instance; +} + + +/* GtkStatusIcon implementation */ +static void _linphone_status_icon_impl_gtk_on_click_cb(LinphoneStatusIcon *si) { + _linphone_status_icon_notify_click(si); +} + +static void _linphone_status_icon_impl_gtk_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, LinphoneStatusIcon *si){ + GtkWidget *menu = si->params->menu; + gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time); +} + +static void _linphone_status_icon_impl_gtk_init(LinphoneStatusIcon *si) { + const char *icon_path=linphone_gtk_get_ui_config("icon",LINPHONE_ICON); + const char *call_icon_path=linphone_gtk_get_ui_config("start_call_icon","startcall-green.png"); + GdkPixbuf *pbuf=create_pixbuf(icon_path); + GtkStatusIcon *icon=gtk_status_icon_new_from_pixbuf(pbuf); + g_signal_connect_swapped(G_OBJECT(icon),"activate", G_CALLBACK(_linphone_status_icon_impl_gtk_on_click_cb), si); + g_signal_connect(G_OBJECT(icon), "popup-menu", G_CALLBACK(_linphone_status_icon_impl_gtk_popup_menu), si); + g_object_set_data(G_OBJECT(icon),"icon",pbuf); + g_object_unref(pbuf); + pbuf=create_pixbuf(call_icon_path); + g_object_set_data(G_OBJECT(icon),"call_icon",pbuf); + g_object_unref(pbuf); + si->data = icon; +} + +// static void _linphone_status_icon_impl_gtk_uninit(LinphoneStatusIcon *si) { +// g_object_unref((GtkStatusIcon *)si->data); +// } + +static void _linphone_status_icon_impl_gtk_start(LinphoneStatusIcon *si) { + GtkStatusIcon *icon = GTK_STATUS_ICON(si->data); +#if GTK_CHECK_VERSION(2,20,2) + gtk_status_icon_set_name(icon, si->params->title); +#endif + gtk_status_icon_set_visible(icon,TRUE); +} + +static gboolean _linphone_status_icon_impl_gtk_do_icon_blink_cb(GtkStatusIcon *gi){ + GdkPixbuf *call_icon=g_object_get_data(G_OBJECT(gi),"call_icon"); + GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(gi),"icon"); + GdkPixbuf *cur_icon=gtk_status_icon_get_pixbuf(gi); + if (cur_icon==call_icon){ + gtk_status_icon_set_from_pixbuf(gi,normal_icon); + }else{ + gtk_status_icon_set_from_pixbuf(gi,call_icon); + } + return TRUE; +} + +static void _linphone_status_icon_impl_enable_blinking(LinphoneStatusIcon *si, gboolean val) { + GtkStatusIcon *icon = GTK_STATUS_ICON(si->data); +#ifdef HAVE_GTK_OSX + static gint attention_id; + GtkosxApplication *theMacApp=gtkosx_application_get(); + if (val) + attention_id=gtkosx_application_attention_request(theMacApp,CRITICAL_REQUEST); + else gtkosx_application_cancel_attention_request(theMacApp,attention_id); +#else + guint tout; + tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout")); + if (val && tout==0){ + tout=g_timeout_add(500,(GSourceFunc)_linphone_status_icon_impl_gtk_do_icon_blink_cb,icon); + g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout)); + }else if (!val && tout!=0){ + GdkPixbuf *normal_icon=g_object_get_data(G_OBJECT(icon),"icon"); + g_source_remove(tout); + g_object_set_data(G_OBJECT(icon),"timeout",NULL); + gtk_status_icon_set_from_pixbuf(icon,normal_icon); + } +#endif +} + +static gboolean _linphone_status_icon_impl_is_supported(void) { +#ifndef HAVE_GTK_OSX + return 1; +#else + return 0; +#endif +} + +static const _LinphoneStatusIconDesc _linphone_status_icon_impl_gtk_desc = { + .impl_name = "gtk_status_icon", + .init = _linphone_status_icon_impl_gtk_init, + .uninit = NULL, + .start = _linphone_status_icon_impl_gtk_start, + .enable_blinking = _linphone_status_icon_impl_enable_blinking, + .is_supported = _linphone_status_icon_impl_is_supported +}; + +static const _LinphoneStatusIconDesc *_linphone_status_icon_impls[] = { + &_linphone_status_icon_impl_gtk_desc, + NULL +}; diff --git a/gtk/status_icon.h b/gtk/status_icon.h new file mode 100644 index 000000000..630a7493e --- /dev/null +++ b/gtk/status_icon.h @@ -0,0 +1,42 @@ +/* +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 + +struct _LinphoneStatusIcon; +typedef void (*LinphoneStatusIconOnClickCallback)(struct _LinphoneStatusIcon *si, void *user_data); + + +typedef struct _LinphoneStatusIconParams LinphoneStatusIconParams; + +LinphoneStatusIconParams *linphone_status_icon_params_new(void); +LinphoneStatusIconParams *linphone_status_icon_params_ref(LinphoneStatusIconParams *obj); +void linphone_status_icon_params_unref(LinphoneStatusIconParams *obj); + +void linphone_status_icon_params_set_title(LinphoneStatusIconParams *obj, const char *title); +void linphone_status_icon_params_set_menu(LinphoneStatusIconParams *obj, GtkWidget *menu); +void linphone_status_icon_params_set_on_click_cb(LinphoneStatusIconParams* obj, LinphoneStatusIconOnClickCallback cb, void *user_data); + + +typedef struct _LinphoneStatusIcon LinphoneStatusIcon; + +LinphoneStatusIcon *linphone_status_icon_get(void); +const char *linphone_status_icon_get_implementation_name(const LinphoneStatusIcon *obj); +void linphone_status_icon_start(LinphoneStatusIcon *obj, LinphoneStatusIconParams *params); +void linphone_status_icon_enable_blinking(LinphoneStatusIcon *obj, gboolean enable); From 752de821b097130350d9c8b435c8faa593037816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Wed, 22 Apr 2015 23:12:05 +0200 Subject: [PATCH 2/6] Add an implementation to the StatusIcon interface based on GtkosxApplication --- gtk/status_icon.c | 53 ++++++++++++++++++++++++++++++++++------------- gtk/status_icon.h | 1 + 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/gtk/status_icon.c b/gtk/status_icon.c index d57a04f10..4ff492468 100644 --- a/gtk/status_icon.c +++ b/gtk/status_icon.c @@ -17,11 +17,13 @@ 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 "status_icon.h" #include "linphone.h" +#ifdef HAVE_GTK_OSX +#include +#endif + struct _LinphoneStatusIconParams { char *title; GtkWidget *menu; @@ -147,6 +149,7 @@ LinphoneStatusIcon *linphone_status_icon_get(void) { /* GtkStatusIcon implementation */ +#ifndef HAVE_GTK_OSX static void _linphone_status_icon_impl_gtk_on_click_cb(LinphoneStatusIcon *si) { _linphone_status_icon_notify_click(si); } @@ -197,13 +200,6 @@ static gboolean _linphone_status_icon_impl_gtk_do_icon_blink_cb(GtkStatusIcon *g static void _linphone_status_icon_impl_enable_blinking(LinphoneStatusIcon *si, gboolean val) { GtkStatusIcon *icon = GTK_STATUS_ICON(si->data); -#ifdef HAVE_GTK_OSX - static gint attention_id; - GtkosxApplication *theMacApp=gtkosx_application_get(); - if (val) - attention_id=gtkosx_application_attention_request(theMacApp,CRITICAL_REQUEST); - else gtkosx_application_cancel_attention_request(theMacApp,attention_id); -#else guint tout; tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout")); if (val && tout==0){ @@ -215,15 +211,10 @@ static void _linphone_status_icon_impl_enable_blinking(LinphoneStatusIcon *si, g g_object_set_data(G_OBJECT(icon),"timeout",NULL); gtk_status_icon_set_from_pixbuf(icon,normal_icon); } -#endif } static gboolean _linphone_status_icon_impl_is_supported(void) { -#ifndef HAVE_GTK_OSX return 1; -#else - return 0; -#endif } static const _LinphoneStatusIconDesc _linphone_status_icon_impl_gtk_desc = { @@ -234,8 +225,42 @@ static const _LinphoneStatusIconDesc _linphone_status_icon_impl_gtk_desc = { .enable_blinking = _linphone_status_icon_impl_enable_blinking, .is_supported = _linphone_status_icon_impl_is_supported }; +#endif + + +/* GtkosxApplication implementation */ +#ifdef HAVE_GTK_OSX +static void _linphone_status_icon_impl_gtkosx_app_enable_blinking(StatusIcon *si, gboolean val) { + GtkosxApplication *theMacApp=gtkosx_application_get(); + gint *attention_id = (gint *)&si->data; + if (val && *attention_id == 0) { + *attention_id=gtkosx_application_attention_request(theMacApp,CRITICAL_REQUEST); + } else if(!val && *attention_id != 0) { + gtkosx_application_cancel_attention_request(theMacApp, *attention_id); + *attention_id = 0; + } +} + +static gboolean _linphone_satus_icon_impl_gtkosx_app_is_supported(void) { + return 1; +} + +static const _LinphoneStatusIconDesc _linphone_status_icon_impl_gtkosx_app_desc = { + .impl_name = "gtkosx_application", + .init = NULL, + .uninit = NULL, + .start = NULL, + .enable_blinking = _linphone_status_icon_impl_gtkosx_app_enable_blinking, + .is_supported = _linphone_satus_icon_impl_gtkosx_app_is_supported +}; +#endif + static const _LinphoneStatusIconDesc *_linphone_status_icon_impls[] = { +#ifndef HAVE_GTK_OSX &_linphone_status_icon_impl_gtk_desc, +#else + &_linphone_status_icon_impl_gtkosx_app_desc, +#endif NULL }; diff --git a/gtk/status_icon.h b/gtk/status_icon.h index 630a7493e..0abaa60d7 100644 --- a/gtk/status_icon.h +++ b/gtk/status_icon.h @@ -17,6 +17,7 @@ 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 struct _LinphoneStatusIcon; From 9cb0974eb4fb262c33ad95e9191c52c218379033 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Thu, 23 Apr 2015 23:31:52 +0200 Subject: [PATCH 3/6] Support of asynchrone status icon implementations --- gtk/main.c | 23 ++++---- gtk/status_icon.c | 136 ++++++++++++++++++++++++++++++++++++++++------ gtk/status_icon.h | 4 ++ 3 files changed, 135 insertions(+), 28 deletions(-) diff --git a/gtk/main.c b/gtk/main.c index d4f5d6718..eb24746cf 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -1549,16 +1549,18 @@ static void handle_icon_click(LinphoneStatusIcon *si, void *user_data) { } static void linphone_gtk_init_status_icon(void) { - 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); + if(linphone_status_icon_init(NULL, NULL)) { + 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); + } } } @@ -1973,6 +1975,7 @@ static void linphone_gtk_quit(void){ #ifdef HAVE_NOTIFY notify_uninit(); #endif + linphone_status_icon_uninit(); gtk_widget_destroy(the_ui); the_ui=NULL; gdk_threads_leave(); diff --git a/gtk/status_icon.c b/gtk/status_icon.c index 4ff492468..0b46aecb6 100644 --- a/gtk/status_icon.c +++ b/gtk/status_icon.c @@ -24,6 +24,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #endif +typedef struct __LinphoneStatusIconDesc _LinphoneStatusIconDesc; + +static LinphoneStatusIcon *_linphone_status_icon_instance = NULL; +static const _LinphoneStatusIconDesc *_linphone_status_icon_selected_desc = NULL; +static const _LinphoneStatusIconDesc *_linphone_status_icon_impls[]; + + struct _LinphoneStatusIconParams { char *title; GtkWidget *menu; @@ -72,23 +79,91 @@ typedef void (*LinphoneStatusIconDescInitFunc)(LinphoneStatusIcon *obj); typedef void (*LinphoneStatusIconDescUninitFunc)(LinphoneStatusIcon *obj); typedef void (*LinphoneStatusIconDescStartFunc)(LinphoneStatusIcon *obj); typedef void (*LinphoneStatusIconDescEnableBlinkingFunc)(LinphoneStatusIcon *obj, gboolean enable); -typedef gboolean (*LinphoneStatusIconDescIsSupported)(void); +typedef void (*LinphoneStatusIconDescIsSupportedResultCb)(const _LinphoneStatusIconDesc *obj, gboolean result, void *user_data); +typedef gboolean (*LinphoneStatusIconDescIsSupportedFunc)( + const _LinphoneStatusIconDesc *desc, + gboolean *result, + LinphoneStatusIconDescIsSupportedResultCb cb, + void *user_data +); +typedef void (*LinphoneStatusIconDescFindResultCb)(const _LinphoneStatusIconDesc *desc, void *user_data); -typedef struct { +struct __LinphoneStatusIconDesc { const char *impl_name; LinphoneStatusIconDescInitFunc init; LinphoneStatusIconDescUninitFunc uninit; LinphoneStatusIconDescStartFunc start; LinphoneStatusIconDescEnableBlinkingFunc enable_blinking; - LinphoneStatusIconDescIsSupported is_supported; -} _LinphoneStatusIconDesc; + LinphoneStatusIconDescIsSupportedFunc is_supported; +}; -static const _LinphoneStatusIconDesc *_linphone_status_icon_impls[]; +static gboolean _linphone_status_icon_desc_is_supported( + const _LinphoneStatusIconDesc *desc, + gboolean *result, + LinphoneStatusIconDescIsSupportedResultCb cb, + void *user_data) { + + return desc->is_supported(desc, result, cb, user_data); +} -static const _LinphoneStatusIconDesc *_status_icon_find_instance(void) { +typedef struct { int i; - for(i=0; _linphone_status_icon_impls[i] && !_linphone_status_icon_impls[i]->is_supported(); i++); - return _linphone_status_icon_impls[i]; + LinphoneStatusIconDescFindResultCb cb; + void *user_data; +} _LinphoneStatusIconDescSearchCtx; + +static void _linphone_status_icon_desc_is_supprted_result_cb( + const _LinphoneStatusIconDesc *desc, + gboolean result, + _LinphoneStatusIconDescSearchCtx *ctx) { + + if(!result) { + ctx->i++; + for(; _linphone_status_icon_impls[ctx->i]; ctx->i++) { + if(_linphone_status_icon_desc_is_supported( + _linphone_status_icon_impls[ctx->i], + &result, + (LinphoneStatusIconDescIsSupportedResultCb)_linphone_status_icon_desc_is_supprted_result_cb, + ctx)) { + + if(result) break; + } else return; + } + } + if(ctx->cb) ctx->cb(_linphone_status_icon_impls[ctx->i], ctx->user_data); + g_free(ctx); +} + +static gboolean _linphone_status_icon_find_first_available_impl( + const _LinphoneStatusIconDesc **desc, + LinphoneStatusIconDescFindResultCb cb, + void *user_data) { + + gboolean result; + _LinphoneStatusIconDescSearchCtx *ctx = g_new0(_LinphoneStatusIconDescSearchCtx, 1); + ctx->cb = cb; + ctx->user_data = user_data; + + for(ctx->i=0; _linphone_status_icon_impls[ctx->i]; ctx->i++) { + if(_linphone_status_icon_desc_is_supported( + _linphone_status_icon_impls[ctx->i], + &result, + (LinphoneStatusIconDescIsSupportedResultCb)_linphone_status_icon_desc_is_supprted_result_cb, + ctx)) { + + if(result) { + *desc = _linphone_status_icon_impls[ctx->i]; + goto sync_return; + } + } else { + return 0; + } + } + *desc = NULL; + +sync_return: + g_free(ctx); + return 1; } @@ -130,19 +205,32 @@ static void _linphone_status_icon_notify_click(LinphoneStatusIcon *obj) { } -static LinphoneStatusIcon *_linphone_status_icon_instance = NULL; +void _linphone_status_icon_init_cb(const _LinphoneStatusIconDesc *desc, void *user_data) { + void **ctx = (void **)user_data; + LinphoneStatusIconReadyCb cb = (LinphoneStatusIconReadyCb)ctx[0]; + _linphone_status_icon_selected_desc = desc; + if(cb) cb(ctx[1]); + g_free(ctx); +} -static void _linphone_status_icon_free_singleton(void) { - _linphone_status_icon_free(_linphone_status_icon_instance); +gboolean linphone_status_icon_init(LinphoneStatusIconReadyCb ready_cb, void *user_data) { + const _LinphoneStatusIconDesc *desc; + void **ctx = g_new(void *, 2); + if(_linphone_status_icon_find_first_available_impl(&desc, _linphone_status_icon_init_cb, ctx)) { + _linphone_status_icon_selected_desc = desc; + g_free(ctx); + return 1; + } else return 0; +} + +void linphone_status_icon_uninit(void) { + if(_linphone_status_icon_instance) _linphone_status_icon_free(_linphone_status_icon_instance); } LinphoneStatusIcon *linphone_status_icon_get(void) { if(_linphone_status_icon_instance == NULL) { - const _LinphoneStatusIconDesc *desc = _status_icon_find_instance(); - if(desc) { - _linphone_status_icon_instance = _linphone_status_icon_new(desc); - atexit(_linphone_status_icon_free_singleton); - } + if(_linphone_status_icon_selected_desc) + _linphone_status_icon_instance = _linphone_status_icon_new(_linphone_status_icon_selected_desc); } return _linphone_status_icon_instance; } @@ -213,7 +301,13 @@ static void _linphone_status_icon_impl_enable_blinking(LinphoneStatusIcon *si, g } } -static gboolean _linphone_status_icon_impl_is_supported(void) { +static gboolean _linphone_status_icon_impl_is_supported( + const _LinphoneStatusIconDesc *desc, + gboolean *result, + LinphoneStatusIconDescIsSupportedResultCb cb, + void *user_data) { + + *result = 1; return 1; } @@ -241,7 +335,13 @@ static void _linphone_status_icon_impl_gtkosx_app_enable_blinking(StatusIcon *si } } -static gboolean _linphone_satus_icon_impl_gtkosx_app_is_supported(void) { +static gboolean _linphone_satus_icon_impl_gtkosx_app_is_supported( + const _LinphoneStatusIconDesc *desc, + gboolean *result, + LinphoneStatusIconDescIsSupportedResultCb cb, + void *user_data) { + + *result = 1; return 1; } diff --git a/gtk/status_icon.h b/gtk/status_icon.h index 0abaa60d7..de0dd9951 100644 --- a/gtk/status_icon.h +++ b/gtk/status_icon.h @@ -35,8 +35,12 @@ void linphone_status_icon_params_set_menu(LinphoneStatusIconParams *obj, GtkWidg void linphone_status_icon_params_set_on_click_cb(LinphoneStatusIconParams* obj, LinphoneStatusIconOnClickCallback cb, void *user_data); +typedef void (*LinphoneStatusIconReadyCb)(void *user_data); + typedef struct _LinphoneStatusIcon LinphoneStatusIcon; +gboolean linphone_status_icon_init(LinphoneStatusIconReadyCb ready_cb, void* user_data); +void linphone_status_icon_uninit(void); LinphoneStatusIcon *linphone_status_icon_get(void); const char *linphone_status_icon_get_implementation_name(const LinphoneStatusIcon *obj); void linphone_status_icon_start(LinphoneStatusIcon *obj, LinphoneStatusIconParams *params); 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 4/6] 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 From 70c42b98232f67cec7fb4cb2aa01133b55e7ff04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Sun, 26 Apr 2015 21:42:16 +0200 Subject: [PATCH 5/6] Add a methode to the LinphoneStatusIcon interface to set the description to display in the tooltip --- gtk/main.c | 3 ++- gtk/status_icon.c | 15 ++++++++++++--- gtk/status_icon.h | 1 + 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/gtk/main.c b/gtk/main.c index fc689a5b6..22eeef4a2 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -1560,7 +1560,8 @@ static void linphone_gtk_init_status_icon(void) { 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_title(params, _("Linphone")); + linphone_status_icon_params_set_description(params, _("A video internet phone")); linphone_status_icon_params_set_on_click_cb(params, handle_icon_click, NULL); g_object_unref(G_OBJECT(menu)); diff --git a/gtk/status_icon.c b/gtk/status_icon.c index daf00203d..c652653a3 100644 --- a/gtk/status_icon.c +++ b/gtk/status_icon.c @@ -35,6 +35,7 @@ static const _LinphoneStatusIconDesc *_linphone_status_icon_impls[]; struct _LinphoneStatusIconParams { char *title; + char *desc; GtkWidget *menu; LinphoneStatusIconOnClickCallback on_click_cb; void *user_data; @@ -65,6 +66,12 @@ void linphone_status_icon_params_set_title(LinphoneStatusIconParams *obj, const else obj->title = NULL; } +void linphone_status_icon_params_set_description(LinphoneStatusIconParams *obj, const char *desc) { + if(obj->desc) g_free(obj->desc); + if(desc) obj->desc = g_strdup(desc); + else obj->desc = NULL; +} + void linphone_status_icon_params_set_menu(LinphoneStatusIconParams *obj, GtkWidget *menu) { if(obj->menu) g_object_unref(obj->menu); if(menu) obj->menu = g_object_ref(menu); @@ -273,7 +280,9 @@ static void _linphone_status_icon_impl_gtk_init(LinphoneStatusIcon *si) { static void _linphone_status_icon_impl_gtk_start(LinphoneStatusIcon *si) { GtkStatusIcon *icon = GTK_STATUS_ICON(si->data); #if GTK_CHECK_VERSION(2,20,2) - gtk_status_icon_set_name(icon, si->params->title); + char *name = g_strdup_printf("%s - %s", si->params->title, si->params->desc); + gtk_status_icon_set_name(icon, name); + g_free(name); #endif gtk_status_icon_set_visible(icon,TRUE); } @@ -404,7 +413,7 @@ static void _linphone_status_icon_impl_sn_menu_called_cb(BcStatusNotifier *sn, i 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); + BcStatusNotifierToolTip *tooltip = bc_status_notifier_tool_tip_new("linphone", si->params->title, si->params->desc); BcStatusNotifierSignalsVTable vtable = {NULL}; vtable.activate_called_cb = _linphone_status_icon_impl_sn_activated_cb; @@ -414,7 +423,7 @@ static void _linphone_status_icon_impl_sn_start(LinphoneStatusIcon *si) { 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_title(params, si->params->title); 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); diff --git a/gtk/status_icon.h b/gtk/status_icon.h index de0dd9951..07d7a34a4 100644 --- a/gtk/status_icon.h +++ b/gtk/status_icon.h @@ -31,6 +31,7 @@ LinphoneStatusIconParams *linphone_status_icon_params_ref(LinphoneStatusIconPara void linphone_status_icon_params_unref(LinphoneStatusIconParams *obj); void linphone_status_icon_params_set_title(LinphoneStatusIconParams *obj, const char *title); +void linphone_status_icon_params_set_description(LinphoneStatusIconParams *obj, const char *desc); void linphone_status_icon_params_set_menu(LinphoneStatusIconParams *obj, GtkWidget *menu); void linphone_status_icon_params_set_on_click_cb(LinphoneStatusIconParams* obj, LinphoneStatusIconOnClickCallback cb, void *user_data); From a188779a77c5a3c3b220dc19b970e53a65d89d29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Mon, 27 Apr 2015 17:51:12 +0200 Subject: [PATCH 6/6] Fix build issues on MacOSX --- gtk/main.c | 4 ++++ gtk/status_icon.c | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/gtk/main.c b/gtk/main.c index 22eeef4a2..78e320532 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -1530,11 +1530,14 @@ static GtkWidget *create_icon_menu(){ return menu; } +#ifndef HAVE_GTK_OSX void linphone_gtk_save_main_window_position(GtkWindow* mw, GdkEvent *event, gpointer data){ gtk_window_get_position(GTK_WINDOW(mw), &main_window_x, &main_window_y); } +#endif static void handle_icon_click(LinphoneStatusIcon *si, void *user_data) { +#ifndef HAVE_GTK_OSX GtkWidget *mw=linphone_gtk_get_main_window(); if (!gtk_window_is_active((GtkWindow*)mw)) { if(!gtk_widget_is_drawable(mw)){ @@ -1546,6 +1549,7 @@ static void handle_icon_click(LinphoneStatusIcon *si, void *user_data) { linphone_gtk_save_main_window_position((GtkWindow*)mw, NULL, NULL); gtk_widget_hide(mw); } +#endif } static void linphone_gtk_status_icon_initialised_cb(LinphoneStatusIconParams *params) { diff --git a/gtk/status_icon.c b/gtk/status_icon.c index c652653a3..08445a468 100644 --- a/gtk/status_icon.c +++ b/gtk/status_icon.c @@ -337,7 +337,7 @@ static const _LinphoneStatusIconDesc _linphone_status_icon_impl_gtk_desc = { /* GtkosxApplication implementation */ #ifdef HAVE_GTK_OSX -static void _linphone_status_icon_impl_gtkosx_app_enable_blinking(StatusIcon *si, gboolean val) { +static void _linphone_status_icon_impl_gtkosx_app_enable_blinking(LinphoneStatusIcon *si, gboolean val) { GtkosxApplication *theMacApp=gtkosx_application_get(); gint *attention_id = (gint *)&si->data; if (val && *attention_id == 0) { @@ -364,7 +364,7 @@ static const _LinphoneStatusIconDesc _linphone_status_icon_impl_gtkosx_app_desc .uninit = NULL, .start = NULL, .enable_blinking = _linphone_status_icon_impl_gtkosx_app_enable_blinking, - .is_supported = _linphone_satus_icon_impl_gtkosx_app_is_supported + .is_supported = _linphone_status_icon_impl_gtkosx_app_is_supported }; #endif