1 /*
2 linphone, gtk-glade interface.
3 Copyright (C) 2015 Belledonne Communications <info@belledonne-communications.com>
4 
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 */
19 
20 #include "status_icon.h"
21 #include "linphone.h"
22 
23 #ifdef HAVE_GTK_OSX
24 #include <gtkosxapplication.h>
25 #endif
26 
27 #if !defined(_WIN32) && !defined(__APPLE__) && GLIB_CHECK_VERSION(2, 26, 0)
28 #define STATUS_NOTIFIER_IS_USABLE 1
29 #endif
30 
31 #include "status_notifier.h"
32 #include <mediastreamer2/mscommon.h>
33 
34 typedef struct __LinphoneStatusIconDesc _LinphoneStatusIconDesc;
35 
36 static LinphoneStatusIcon *_linphone_status_icon_instance = NULL;
37 static const _LinphoneStatusIconDesc *_linphone_status_icon_selected_desc = NULL;
38 static GSList *_linphone_status_icon_impls = NULL;
39 
40 
41 struct _LinphoneStatusIconParams {
42 	char *title;
43 	char *desc;
44 	GtkWidget *menu;
45 	LinphoneStatusIconOnClickCallback on_click_cb;
46 	void *user_data;
47 	int ref;
48 };
49 
linphone_status_icon_params_new(void)50 LinphoneStatusIconParams *linphone_status_icon_params_new(void) {
51 	return g_new0(LinphoneStatusIconParams, 1);
52 }
53 
linphone_status_icon_params_ref(LinphoneStatusIconParams * obj)54 LinphoneStatusIconParams *linphone_status_icon_params_ref(LinphoneStatusIconParams *obj) {
55 	obj->ref++;
56 	return obj;
57 }
58 
linphone_status_icon_params_unref(LinphoneStatusIconParams * obj)59 void linphone_status_icon_params_unref(LinphoneStatusIconParams *obj) {
60 	obj->ref--;
61 	if(obj->ref < 0) {
62 		if(obj->title) g_free(obj->title);
63 		if(obj->menu) g_object_unref(obj->menu);
64 		if(obj->desc) g_free(obj->desc);
65 		g_free(obj);
66 	}
67 }
68 
linphone_status_icon_params_set_title(LinphoneStatusIconParams * obj,const char * title)69 void linphone_status_icon_params_set_title(LinphoneStatusIconParams *obj, const char *title) {
70 	if(obj->title) g_free(obj->title);
71 	if(title) obj->title = g_strdup(title);
72 	else obj->title = NULL;
73 }
74 
linphone_status_icon_params_set_description(LinphoneStatusIconParams * obj,const char * desc)75 void linphone_status_icon_params_set_description(LinphoneStatusIconParams *obj, const char *desc) {
76 	if(obj->desc) g_free(obj->desc);
77 	if(desc) obj->desc = g_strdup(desc);
78 	else obj->desc = NULL;
79 }
80 
linphone_status_icon_params_set_menu(LinphoneStatusIconParams * obj,GtkWidget * menu)81 void linphone_status_icon_params_set_menu(LinphoneStatusIconParams *obj, GtkWidget *menu) {
82 	if(obj->menu) g_object_unref(obj->menu);
83 	if(menu) obj->menu = g_object_ref_sink(menu);
84 	else obj->menu = NULL;
85 }
86 
linphone_status_icon_params_set_on_click_cb(LinphoneStatusIconParams * obj,LinphoneStatusIconOnClickCallback cb,void * user_data)87 void linphone_status_icon_params_set_on_click_cb(LinphoneStatusIconParams *obj, LinphoneStatusIconOnClickCallback cb, void *user_data) {
88 	obj->on_click_cb = cb;
89 	obj->user_data = user_data;
90 }
91 
92 
93 typedef void (*LinphoneStatusIconDescInitFunc)(LinphoneStatusIcon *obj);
94 typedef void (*LinphoneStatusIconDescUninitFunc)(LinphoneStatusIcon *obj);
95 typedef void (*LinphoneStatusIconDescStartFunc)(LinphoneStatusIcon *obj);
96 typedef void (*LinphoneStatusIconDescEnableBlinkingFunc)(LinphoneStatusIcon *obj, gboolean enable);
97 typedef void (*LinphoneStatusIconDescIsSupportedResultCb)(const _LinphoneStatusIconDesc *obj, gboolean result, void *user_data);
98 typedef gboolean (*LinphoneStatusIconDescIsSupportedFunc)(
99 	const _LinphoneStatusIconDesc *desc,
100 	gboolean *result,
101 	LinphoneStatusIconDescIsSupportedResultCb cb,
102 	void *user_data
103 );
104 typedef void (*LinphoneStatusIconDescFindResultCb)(const _LinphoneStatusIconDesc *desc, void *user_data);
105 
106 struct __LinphoneStatusIconDesc {
107 	const char *impl_name;
108 	LinphoneStatusIconDescInitFunc init;
109 	LinphoneStatusIconDescUninitFunc uninit;
110 	LinphoneStatusIconDescStartFunc start;
111 	LinphoneStatusIconDescEnableBlinkingFunc enable_blinking;
112 	LinphoneStatusIconDescIsSupportedFunc is_supported;
113 };
114 
_linphone_status_icon_desc_is_supported(const _LinphoneStatusIconDesc * desc,gboolean * result,LinphoneStatusIconDescIsSupportedResultCb cb,void * user_data)115 static gboolean _linphone_status_icon_desc_is_supported(
116 	const _LinphoneStatusIconDesc *desc,
117 	gboolean *result,
118 	LinphoneStatusIconDescIsSupportedResultCb cb,
119 	void *user_data) {
120 
121 	return desc->is_supported(desc, result, cb, user_data);
122 }
123 
124 typedef struct {
125 	GSList *i;
126 	LinphoneStatusIconDescFindResultCb cb;
127 	void *user_data;
128 } _LinphoneStatusIconDescSearchCtx;
129 
_linphone_status_icon_desc_is_supported_result_cb(const _LinphoneStatusIconDesc * desc,gboolean result,_LinphoneStatusIconDescSearchCtx * ctx)130 static void _linphone_status_icon_desc_is_supported_result_cb(
131 	const _LinphoneStatusIconDesc *desc,
132 	gboolean result,
133 	_LinphoneStatusIconDescSearchCtx *ctx) {
134 
135 	if(!result) {
136 		ctx->i = g_slist_next(ctx->i);
137 		for(; ctx->i; ctx->i = g_slist_next(ctx->i)) {
138 			if(_linphone_status_icon_desc_is_supported(
139 				(const _LinphoneStatusIconDesc *)g_slist_nth_data(ctx->i, 0),
140 				&result,
141 				(LinphoneStatusIconDescIsSupportedResultCb)_linphone_status_icon_desc_is_supported_result_cb,
142 				ctx)) {
143 
144 				if(result) break;
145 			} else return;
146 		}
147 	}
148 
149 	if(ctx->i) {
150 		const _LinphoneStatusIconDesc *desc = (const _LinphoneStatusIconDesc *)g_slist_nth_data(ctx->i, 0);
151 		ms_message("StatusIcon: found implementation: %s", desc->impl_name);
152 		if(ctx->cb) ctx->cb(desc, ctx->user_data);
153 	} else {
154 		g_warning("StatusIcon: no implementation found");
155 	}
156 
157 	g_free(ctx);
158 }
159 
_linphone_status_icon_find_first_available_impl(const _LinphoneStatusIconDesc ** desc,LinphoneStatusIconDescFindResultCb cb,void * user_data)160 static gboolean _linphone_status_icon_find_first_available_impl(
161 	const _LinphoneStatusIconDesc **desc,
162 	LinphoneStatusIconDescFindResultCb cb,
163 	void *user_data) {
164 
165 	gboolean result;
166 	_LinphoneStatusIconDescSearchCtx *ctx = g_new0(_LinphoneStatusIconDescSearchCtx, 1);
167 	ctx->cb = cb;
168 	ctx->user_data = user_data;
169 
170 	ms_message("StatusIcon: looking for implementation...");
171 
172 	for(ctx->i=_linphone_status_icon_impls; ctx->i; ctx->i = g_slist_next(ctx->i)) {
173 		if(_linphone_status_icon_desc_is_supported(
174 			(const _LinphoneStatusIconDesc *)g_slist_nth_data(ctx->i, 0),
175 			&result,
176 			(LinphoneStatusIconDescIsSupportedResultCb)_linphone_status_icon_desc_is_supported_result_cb,
177 			ctx)) {
178 
179 			if(result) {
180 				*desc = (const _LinphoneStatusIconDesc *)g_slist_nth_data(ctx->i, 0);
181 				ms_message("StatusIcon: found implementation: %s", (*desc)->impl_name);
182 				goto sync_return;
183 			}
184 		} else {
185 			return 0;
186 		}
187 	}
188 	g_warning("StatusIcon: no implementation found");
189 	*desc = NULL;
190 
191 sync_return:
192 	g_free(ctx);
193 	return 1;
194 }
195 
196 
197 struct _LinphoneStatusIcon {
198 	const _LinphoneStatusIconDesc *desc;
199 	LinphoneStatusIconParams *params;
200 	void *data;
201 };
202 
_linphone_status_icon_new(const _LinphoneStatusIconDesc * desc)203 static LinphoneStatusIcon *_linphone_status_icon_new(const _LinphoneStatusIconDesc *desc) {
204 	LinphoneStatusIcon *si = (LinphoneStatusIcon *)g_new0(LinphoneStatusIcon, 1);
205 	si->desc = desc;
206 	if(desc->init) desc->init(si);
207 	return si;
208 }
209 
_linphone_status_icon_free(LinphoneStatusIcon * obj)210 static void _linphone_status_icon_free(LinphoneStatusIcon *obj) {
211 	if(obj->desc->uninit) obj->desc->uninit(obj);
212 	if(obj->params) linphone_status_icon_params_unref(obj->params);
213 	g_free(obj);
214 }
215 
linphone_status_icon_get_implementation_name(const LinphoneStatusIcon * obj)216 const char *linphone_status_icon_get_implementation_name(const LinphoneStatusIcon *obj) {
217 	return obj->desc->impl_name;
218 }
219 
linphone_status_icon_start(LinphoneStatusIcon * obj,LinphoneStatusIconParams * params)220 void linphone_status_icon_start(LinphoneStatusIcon *obj, LinphoneStatusIconParams *params) {
221 	ms_message("StatusIcon: starting status icon");
222 	obj->params = linphone_status_icon_params_ref(params);
223 	if(obj->desc->start) obj->desc->start(obj);
224 }
225 
linphone_status_icon_enable_blinking(LinphoneStatusIcon * obj,gboolean enable)226 void linphone_status_icon_enable_blinking(LinphoneStatusIcon *obj, gboolean enable) {
227 	ms_message("StatusIcon: blinking set to %s", enable ? "TRUE" : "FALSE");
228 	if(obj->desc->enable_blinking) obj->desc->enable_blinking(obj, enable);
229 }
230 
_linphone_status_icon_notify_click(LinphoneStatusIcon * obj)231 static void _linphone_status_icon_notify_click(LinphoneStatusIcon *obj) {
232 	if(obj->params->on_click_cb) {
233 		obj->params->on_click_cb(obj, obj->params->user_data);
234 	}
235 }
236 
237 
_linphone_status_icon_init_cb(const _LinphoneStatusIconDesc * desc,void * user_data)238 void _linphone_status_icon_init_cb(const _LinphoneStatusIconDesc *desc, void *user_data) {
239 	void **ctx = (void **)user_data;
240 	LinphoneStatusIconReadyCb cb = (LinphoneStatusIconReadyCb)ctx[0];
241 	_linphone_status_icon_selected_desc = desc;
242 	if(cb) cb(ctx[1]);
243 	g_free(ctx);
244 }
245 
246 #ifdef STATUS_NOTIFIER_IS_USABLE
247 static const _LinphoneStatusIconDesc _linphone_status_icon_impl_status_notifier;
248 #endif
249 #ifdef HAVE_GTK_OSX
250 static const _LinphoneStatusIconDesc _linphone_status_icon_impl_gtkosx_app_desc;
251 #else
252 static const _LinphoneStatusIconDesc _linphone_status_icon_impl_gtk_desc;
253 #endif
254 
_linphone_status_icon_create_implementations_list(void)255 void _linphone_status_icon_create_implementations_list(void) {
256 #ifdef STATUS_NOTIFIER_IS_USABLE
257 	_linphone_status_icon_impls = g_slist_append(_linphone_status_icon_impls, (void *)&_linphone_status_icon_impl_status_notifier);
258 #endif
259 #if HAVE_GTK_OSX
260 	_linphone_status_icon_impls = g_slist_append(_linphone_status_icon_impls, (void *)&_linphone_status_icon_impl_gtkosx_app_desc);
261 #else
262 	_linphone_status_icon_impls = g_slist_append(_linphone_status_icon_impls, (void *)&_linphone_status_icon_impl_gtk_desc);
263 #endif
264 }
265 
linphone_status_icon_init(LinphoneStatusIconReadyCb ready_cb,void * user_data)266 gboolean linphone_status_icon_init(LinphoneStatusIconReadyCb ready_cb, void *user_data) {
267 	const _LinphoneStatusIconDesc *desc;
268 	void **ctx;
269 
270 	ms_message("StatusIcon: Initialising");
271 
272 	_linphone_status_icon_create_implementations_list();
273 
274 	ctx = g_new(void *, 2);
275 	ctx[0] = ready_cb;
276 	ctx[1] = user_data;
277 
278 	if(_linphone_status_icon_find_first_available_impl(&desc, _linphone_status_icon_init_cb, ctx)) {
279 		_linphone_status_icon_selected_desc = desc;
280 		g_free(ctx);
281 		return 1;
282 	} else return 0;
283 }
284 
linphone_status_icon_uninit(void)285 void linphone_status_icon_uninit(void) {
286 	if(_linphone_status_icon_instance) {
287 		_linphone_status_icon_free(_linphone_status_icon_instance);
288 		_linphone_status_icon_instance = NULL;
289 	}
290 	if(_linphone_status_icon_impls) {
291 		g_slist_free(_linphone_status_icon_impls);
292 		_linphone_status_icon_impls = NULL;
293 	}
294 	_linphone_status_icon_selected_desc = NULL;
295 }
296 
linphone_status_icon_get(void)297 LinphoneStatusIcon *linphone_status_icon_get(void) {
298 	if(_linphone_status_icon_instance == NULL) {
299 		if(_linphone_status_icon_selected_desc)
300 			ms_message("StatusIcon: instanciating singleton");
301 			_linphone_status_icon_instance = _linphone_status_icon_new(_linphone_status_icon_selected_desc);
302 	}
303 	return _linphone_status_icon_instance;
304 }
305 
306 
307 /* GtkStatusIcon implementation */
308 #ifndef HAVE_GTK_OSX
_linphone_status_icon_impl_gtk_on_click_cb(LinphoneStatusIcon * si)309 static void _linphone_status_icon_impl_gtk_on_click_cb(LinphoneStatusIcon *si) {
310 	_linphone_status_icon_notify_click(si);
311 }
312 
_linphone_status_icon_impl_gtk_popup_menu(GtkStatusIcon * status_icon,guint button,guint activate_time,LinphoneStatusIcon * si)313 static void _linphone_status_icon_impl_gtk_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, LinphoneStatusIcon *si){
314 	GtkWidget *menu = si->params->menu;
315 	gtk_menu_popup(GTK_MENU(menu),NULL,NULL,gtk_status_icon_position_menu,status_icon,button,activate_time);
316 }
317 
_linphone_status_icon_impl_gtk_init(LinphoneStatusIcon * si)318 static void _linphone_status_icon_impl_gtk_init(LinphoneStatusIcon *si) {
319 	const char *icon_name=linphone_gtk_get_ui_config("icon_name",LINPHONE_ICON_NAME);
320 	const char *blinking_icon_name=linphone_gtk_get_ui_config("binking_status_icon_name","linphone-start-call");
321 	GtkStatusIcon *icon=gtk_status_icon_new_from_icon_name(icon_name);
322 	g_signal_connect_swapped(G_OBJECT(icon),"activate", G_CALLBACK(_linphone_status_icon_impl_gtk_on_click_cb), si);
323 	g_signal_connect(G_OBJECT(icon), "popup-menu", G_CALLBACK(_linphone_status_icon_impl_gtk_popup_menu), si);
324 	g_object_set_data_full(G_OBJECT(icon), "icon", g_strdup(icon_name), g_free);
325 	g_object_set_data_full(G_OBJECT(icon), "call_icon", g_strdup(blinking_icon_name), g_free);
326 	si->data = icon;
327 }
328 
_linphone_status_icon_impl_gtk_uninit(LinphoneStatusIcon * si)329 static void _linphone_status_icon_impl_gtk_uninit(LinphoneStatusIcon *si) {
330 	GtkStatusIcon *icon = GTK_STATUS_ICON(si->data);
331 	gtk_status_icon_set_visible(icon, FALSE);
332 }
333 
_linphone_status_icon_impl_gtk_start(LinphoneStatusIcon * si)334 static void _linphone_status_icon_impl_gtk_start(LinphoneStatusIcon *si) {
335 	GtkStatusIcon *icon = GTK_STATUS_ICON(si->data);
336 #if GTK_CHECK_VERSION(2,20,2)
337 	char *name = g_strdup_printf("%s - %s", si->params->title, si->params->desc);
338 	gtk_status_icon_set_name(icon, name);
339 	g_free(name);
340 #endif
341 	gtk_status_icon_set_visible(icon,TRUE);
342 }
343 
_linphone_status_icon_impl_gtk_do_icon_blink_cb(GtkStatusIcon * gi)344 static gboolean _linphone_status_icon_impl_gtk_do_icon_blink_cb(GtkStatusIcon *gi){
345 	const gchar *call_icon = (const gchar *)g_object_get_data(G_OBJECT(gi),"call_icon");
346 	const gchar *normal_icon = (const gchar *)g_object_get_data(G_OBJECT(gi),"icon");
347 	const gchar *cur_icon = (const gchar *)gtk_status_icon_get_icon_name(gi);
348 	if (cur_icon == call_icon){
349 		gtk_status_icon_set_from_icon_name(gi,normal_icon);
350 	}else{
351 		gtk_status_icon_set_from_icon_name(gi,call_icon);
352 	}
353 	return TRUE;
354 }
355 
_linphone_status_icon_impl_enable_blinking(LinphoneStatusIcon * si,gboolean val)356 static void _linphone_status_icon_impl_enable_blinking(LinphoneStatusIcon *si, gboolean val) {
357 	GtkStatusIcon *icon = GTK_STATUS_ICON(si->data);
358 	guint tout;
359 	tout=(unsigned)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(icon),"timeout"));
360 	if (val && tout==0){
361 		tout=g_timeout_add(500,(GSourceFunc)_linphone_status_icon_impl_gtk_do_icon_blink_cb,icon);
362 		g_object_set_data(G_OBJECT(icon),"timeout",GINT_TO_POINTER(tout));
363 	}else if (!val && tout!=0){
364 		const gchar *normal_icon = (const gchar *)g_object_get_data(G_OBJECT(icon),"icon");
365 		g_source_remove(tout);
366 		g_object_set_data(G_OBJECT(icon),"timeout",NULL);
367 		gtk_status_icon_set_from_icon_name(icon,normal_icon);
368 	}
369 }
370 
_linphone_status_icon_impl_is_supported(const _LinphoneStatusIconDesc * desc,gboolean * result,LinphoneStatusIconDescIsSupportedResultCb cb,void * user_data)371 static gboolean _linphone_status_icon_impl_is_supported(
372 	const _LinphoneStatusIconDesc *desc,
373 	gboolean *result,
374 	LinphoneStatusIconDescIsSupportedResultCb cb,
375 	void *user_data) {
376 
377 	*result = 1;
378 	return 1;
379 }
380 
381 static const _LinphoneStatusIconDesc _linphone_status_icon_impl_gtk_desc = {
382 	"gtk_status_icon",
383 	_linphone_status_icon_impl_gtk_init,
384 	_linphone_status_icon_impl_gtk_uninit,
385 	_linphone_status_icon_impl_gtk_start,
386 	_linphone_status_icon_impl_enable_blinking,
387 	_linphone_status_icon_impl_is_supported
388 };
389 #endif
390 
391 
392 /* GtkosxApplication implementation */
393 #ifdef HAVE_GTK_OSX
_linphone_status_icon_impl_gtkosx_app_enable_blinking(LinphoneStatusIcon * si,gboolean val)394 static void _linphone_status_icon_impl_gtkosx_app_enable_blinking(LinphoneStatusIcon *si, gboolean val) {
395 	GtkosxApplication *theMacApp=gtkosx_application_get();
396 	gint *attention_id = (gint *)&si->data;
397 	if (val) {
398 		*attention_id=gtkosx_application_attention_request(theMacApp, CRITICAL_REQUEST);
399 	} else if (*attention_id != 0) {
400 		gtkosx_application_cancel_attention_request(theMacApp, *attention_id);
401 		*attention_id = 0;
402 	}
403 }
404 
_linphone_status_icon_impl_gtkosx_app_is_supported(const _LinphoneStatusIconDesc * desc,gboolean * result,LinphoneStatusIconDescIsSupportedResultCb cb,void * user_data)405 static gboolean _linphone_status_icon_impl_gtkosx_app_is_supported(
406 	const _LinphoneStatusIconDesc *desc,
407 	gboolean *result,
408 	LinphoneStatusIconDescIsSupportedResultCb cb,
409 	void *user_data) {
410 
411 	*result = 1;
412 	return 1;
413 }
414 
415 static const _LinphoneStatusIconDesc _linphone_status_icon_impl_gtkosx_app_desc = {
416 	.impl_name = "gtkosx_application",
417 	.init = NULL,
418 	.uninit = NULL,
419 	.start = NULL,
420 	.enable_blinking = _linphone_status_icon_impl_gtkosx_app_enable_blinking,
421 	.is_supported = _linphone_status_icon_impl_gtkosx_app_is_supported
422 };
423 #endif
424 
425 
426 /* Implementation based on the StatusNotifier Freedesktop standard */
427 #ifdef STATUS_NOTIFIER_IS_USABLE
428 typedef struct {
429 	int x;
430 	int y;
431 } _LinphoneStatusIconPosition;
432 
_linphone_status_icon_impl_sn_init(LinphoneStatusIcon * si)433 static void _linphone_status_icon_impl_sn_init(LinphoneStatusIcon *si) {
434 	si->data = bc_status_notifier_new();
435 }
436 
437 // static void _linphone_status_icon_impl_sn_uninit(LinphoneStatusIcon *si) {
438 // 	bc_status_notifier_unref((BcStatusNotifier *)si->data);
439 // }
440 
_linphone_status_icon_impl_sn_activated_cb(BcStatusNotifier * sn,int x,int y,void * user_data)441 static void _linphone_status_icon_impl_sn_activated_cb(BcStatusNotifier *sn, int x, int y, void *user_data) {
442 	LinphoneStatusIcon *si = (LinphoneStatusIcon *)user_data;
443 	_linphone_status_icon_notify_click(si);
444 }
445 
_linphone_status_icon_impr_sn_get_position(GtkMenu * menu,int * x,int * y,gboolean * push_in,gpointer data)446 static void _linphone_status_icon_impr_sn_get_position(GtkMenu *menu, int *x, int *y, gboolean *push_in, gpointer data) {
447 	_LinphoneStatusIconPosition *pos = (_LinphoneStatusIconPosition *)data;
448 	*x = pos->x;
449 	*y = pos->y;
450 	*push_in = TRUE;
451 }
452 
_linphone_status_icon_impl_sn_menu_called_cb(BcStatusNotifier * sn,int x,int y,void * user_data)453 static void _linphone_status_icon_impl_sn_menu_called_cb(BcStatusNotifier *sn, int x, int y, void *user_data) {
454 	LinphoneStatusIcon *si = (LinphoneStatusIcon *)user_data;
455 	GtkWidget *menu = si->params->menu;
456 	_LinphoneStatusIconPosition pos = {x, y};
457 	gtk_menu_popup(
458 		GTK_MENU(menu),
459 		NULL,
460 		NULL,
461 		_linphone_status_icon_impr_sn_get_position,
462 		&pos,
463 		0,
464 		gtk_get_current_event_time()
465 	);
466 }
467 
_linphone_status_icon_impl_sn_start(LinphoneStatusIcon * si)468 static void _linphone_status_icon_impl_sn_start(LinphoneStatusIcon *si) {
469 	BcStatusNotifier *sn = (BcStatusNotifier *)si->data;
470 	BcStatusNotifierParams *params;
471 	BcStatusNotifierToolTip *tooltip = bc_status_notifier_tool_tip_new("linphone", si->params->title, si->params->desc);
472 	BcStatusNotifierSignalsVTable vtable = {NULL};
473 
474 	vtable.activate_called_cb = _linphone_status_icon_impl_sn_activated_cb;
475 	vtable.context_menu_called_cb = _linphone_status_icon_impl_sn_menu_called_cb;
476 
477 	params = bc_status_notifier_params_new();
478 	bc_status_notifier_params_set_dbus_prefix(params, "org.kde");
479 	bc_status_notifier_params_set_category(params, BcStatusNotifierCategoryCommunications);
480 	bc_status_notifier_params_set_id(params, "linphone");
481 	bc_status_notifier_params_set_title(params, si->params->title);
482 	bc_status_notifier_params_set_icon_name(params, "linphone");
483 	bc_status_notifier_params_set_tool_tip(params, tooltip);
484 	bc_status_notifier_params_set_vtable(params, &vtable, si);
485 
486 	bc_status_notifier_start(sn, params, NULL, NULL);
487 
488 	bc_status_notifier_tool_tip_unref(tooltip);
489 	bc_status_notifier_params_unref(params);
490 }
491 
_linphone_status_icon_impl_sn_enable_blinking(LinphoneStatusIcon * si,gboolean val)492 static void _linphone_status_icon_impl_sn_enable_blinking(LinphoneStatusIcon *si, gboolean val) {
493 	BcStatusNotifier *sn = (BcStatusNotifier *)si->data;
494 	if(val) {
495 		bc_status_notifier_update_status(sn, BcStatusNotifierStatusNeedsAttention);
496 	} else {
497 		bc_status_notifier_update_status(sn, BcStatusNotifierStatusPassive);
498 	}
499 }
500 
_linphone_status_icon_impl_is_supported_cb(const char * prefix,gboolean result,void ** data)501 static void _linphone_status_icon_impl_is_supported_cb(const char *prefix, gboolean result, void **data) {
502 	_LinphoneStatusIconDesc *desc = (_LinphoneStatusIconDesc *)data[0];
503 	LinphoneStatusIconDescIsSupportedResultCb cb = (LinphoneStatusIconDescIsSupportedResultCb)data[1];
504 	if(cb) cb(desc, result, data[2]);
505 	g_free(data);
506 	g_free(desc);
507 }
508 
_linphone_status_icon_impl_sn_is_supported(const _LinphoneStatusIconDesc * desc,gboolean * result,LinphoneStatusIconDescIsSupportedResultCb cb,void * user_data)509 static gboolean _linphone_status_icon_impl_sn_is_supported(
510 	const _LinphoneStatusIconDesc *desc,
511 	gboolean *result,
512 	LinphoneStatusIconDescIsSupportedResultCb cb,
513 	void *user_data) {
514 
515 	_LinphoneStatusIconDesc *desc2;
516 	void **data;
517 	const char *desktop = g_getenv("XDG_CURRENT_DESKTOP");
518 
519 	if(desktop == NULL || g_strcmp0(desktop, "KDE") != 0) {
520 		*result = FALSE;
521 		return TRUE;
522 	}
523 
524 	desc2 = g_new(_LinphoneStatusIconDesc, 1);
525 	*desc2 = *desc;
526 	data = g_new(void *, 3);
527 	data[0] = desc2;
528 	data[1] = cb;
529 	data[2] = user_data;
530 	bc_status_notifier_is_supported(
531 		"org.kde",
532 		(BcStatusNotifierSupportDetectionCb)_linphone_status_icon_impl_is_supported_cb,
533 		data
534 	);
535 	return FALSE;
536 }
537 
538 static const _LinphoneStatusIconDesc _linphone_status_icon_impl_status_notifier = {
539 	.impl_name = "status_notifier",
540 	.init = _linphone_status_icon_impl_sn_init,
541 	.uninit = NULL,
542 	.start = _linphone_status_icon_impl_sn_start,
543 	.enable_blinking = _linphone_status_icon_impl_sn_enable_blinking,
544 	.is_supported = _linphone_status_icon_impl_sn_is_supported
545 };
546 #endif
547