Remmina - The GTK+ Remote Desktop Client  v1.4.2
Remmina is a remote desktop client written in GTK+, aiming to be useful for system administrators and travellers, who need to work with lots of remote computers in front of either large monitors or tiny netbooks. Remmina supports multiple network protocols in an integrated and consistent user interface. Currently RDP, VNC, NX, XDMCP and SSH are supported.
remmina_icon.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2010 Vic Lee
4  * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
5  * Copyright (C) 2016-2020 Antenore Gatta, Giovanni Panozzo
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  * In addition, as a special exception, the copyright holders give
23  * permission to link the code of portions of this program with the
24  * OpenSSL library under certain conditions as described in each
25  * individual source file, and distribute linked combinations
26  * including the two.
27  * You must obey the GNU General Public License in all respects
28  * for all of the code used other than OpenSSL. * If you modify
29  * file(s) with this exception, you may extend this exception to your
30  * version of the file(s), but you are not obligated to do so. * If you
31  * do not wish to do so, delete this exception statement from your
32  * version. * If you delete this exception statement from all source
33  * files in the program, then also delete it here.
34  *
35  */
36 
37 #include "config.h"
38 #include <gtk/gtk.h>
39 #include <glib/gi18n.h>
40 #include "remmina_widget_pool.h"
41 #include "remmina_pref.h"
42 #include "remmina_exec.h"
43 #include "remmina_avahi.h"
45 #include "remmina_applet_menu.h"
46 #include "rcw.h"
47 #include "remmina_icon.h"
49 #include "remmina_sysinfo.h"
50 
51 #ifdef HAVE_LIBAPPINDICATOR
52  #ifdef HAVE_AYATANA_LIBAPPINDICATOR
53  #include <libayatana-appindicator/app-indicator.h>
54  #else
55  #include <libappindicator/app-indicator.h>
56  #endif
57 #endif
58 
59 typedef struct _RemminaIcon {
60 #ifdef HAVE_LIBAPPINDICATOR
61  AppIndicator *icon;
62 #else
63  GtkStatusIcon *icon;
64 #endif
66  guint32 popup_time;
68  gchar *gsversion; // GnomeShell version string, or null if not available
69 } RemminaIcon;
70 
72 { 0 };
73 
75 {
76  TRACE_CALL(__func__);
77  if (remmina_icon.icon) {
78 #ifdef HAVE_LIBAPPINDICATOR
79  app_indicator_set_status(remmina_icon.icon, APP_INDICATOR_STATUS_PASSIVE);
80 #else
81  gtk_status_icon_set_visible(remmina_icon.icon, FALSE);
82 #endif
83  remmina_icon.icon = NULL;
84  }
85  if (remmina_icon.avahi) {
86  remmina_avahi_free(remmina_icon.avahi);
87  remmina_icon.avahi = NULL;
88  }
89  if (remmina_icon.autostart_file) {
90  g_free(remmina_icon.autostart_file);
91  remmina_icon.autostart_file = NULL;
92  }
93  if (remmina_icon.gsversion) {
94  g_free(remmina_icon.gsversion);
95  remmina_icon.gsversion = NULL;
96  }
97 }
98 
99 static void remmina_icon_main(void)
100 {
101  TRACE_CALL(__func__);
103 }
104 
105 static void remmina_icon_preferences(void)
106 {
107  TRACE_CALL(__func__);
109 }
110 
111 static void remmina_icon_about(void)
112 {
113  TRACE_CALL(__func__);
115 }
116 
117 static void remmina_icon_enable_avahi(GtkCheckMenuItem *checkmenuitem, gpointer data)
118 {
119  TRACE_CALL(__func__);
120  if (!remmina_icon.avahi)
121  return;
122 
123  if (gtk_check_menu_item_get_active(checkmenuitem)) {
125  if (!remmina_icon.avahi->started)
126  remmina_avahi_start(remmina_icon.avahi);
127  }else {
129  remmina_avahi_stop(remmina_icon.avahi);
130  }
132 }
133 
134 static void remmina_icon_populate_additional_menu_item(GtkWidget *menu)
135 {
136  TRACE_CALL(__func__);
137  GtkWidget *menuitem;
138 
139  menuitem = gtk_menu_item_new_with_label(_("Open Main Window"));
140  gtk_widget_show(menuitem);
141  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
142  g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(remmina_icon_main), NULL);
143 
144  menuitem = gtk_menu_item_new_with_mnemonic(_("_Preferences"));
145  gtk_widget_show(menuitem);
146  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
147  g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(remmina_icon_preferences), NULL);
148 
149  menuitem = gtk_menu_item_new_with_mnemonic(_("_About"));
150  gtk_widget_show(menuitem);
151  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
152  g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(remmina_icon_about), NULL);
153 
154  menuitem = gtk_separator_menu_item_new();
155  gtk_widget_show(menuitem);
156  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
157 
158 #ifdef HAVE_LIBAVAHI_CLIENT
159  menuitem = gtk_check_menu_item_new_with_label(_("Enable Service Discovery"));
161  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
162  }
163  gtk_widget_show(menuitem);
164  g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(remmina_icon_enable_avahi), NULL);
165  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
166 
167  menuitem = gtk_separator_menu_item_new();
168  gtk_widget_show(menuitem);
169  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
170 #endif
171 
172  menuitem = gtk_menu_item_new_with_mnemonic(_("_Quit"));
173  gtk_widget_show(menuitem);
174  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
175  g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(remmina_exec_exitremmina), NULL);
176 }
177 
178 static void remmina_icon_on_launch_item(RemminaAppletMenu *menu, RemminaAppletMenuItem *menuitem, gpointer data)
179 {
180  TRACE_CALL(__func__);
181  gchar *s;
182 
183  switch (menuitem->item_type) {
186  break;
189  break;
191  s = g_strdup_printf("%s,%s", menuitem->protocol, menuitem->name);
193  g_free(s);
194  break;
195  }
196 }
197 
198 static void remmina_icon_on_edit_item(RemminaAppletMenu *menu, RemminaAppletMenuItem *menuitem, gpointer data)
199 {
200  TRACE_CALL(__func__);
201  gchar *s;
202 
203  switch (menuitem->item_type) {
206  break;
209  break;
211  s = g_strdup_printf("%s,%s", menuitem->protocol, menuitem->name);
213  g_free(s);
214  break;
215  }
216 }
217 
218 static void remmina_icon_populate_extra_menu_item(GtkWidget *menu)
219 {
220  TRACE_CALL(__func__);
221  GtkWidget *menuitem;
222  gboolean new_ontop;
223  GHashTableIter iter;
224  gchar *tmp;
225 
226  new_ontop = remmina_pref.applet_new_ontop;
227 
228  /* Iterate all discovered services from Avahi */
229  if (remmina_icon.avahi) {
230  g_hash_table_iter_init(&iter, remmina_icon.avahi->discovered_services);
231  while (g_hash_table_iter_next(&iter, NULL, (gpointer*)&tmp)) {
233  gtk_widget_show(menuitem);
234  remmina_applet_menu_add_item(REMMINA_APPLET_MENU(menu), REMMINA_APPLET_MENU_ITEM(menuitem));
235  }
236  }
237 
238  /* Separator */
239  menuitem = gtk_separator_menu_item_new();
240  gtk_widget_show(menuitem);
241  if (new_ontop) {
242  gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
243  }else {
244  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
245  }
246 
247  /* New Connection */
249  gtk_widget_show(menuitem);
250  remmina_applet_menu_register_item(REMMINA_APPLET_MENU(menu), REMMINA_APPLET_MENU_ITEM(menuitem));
251  if (new_ontop) {
252  gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
253  }else {
254  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
255  }
256 
257  g_signal_connect(G_OBJECT(menu), "launch-item", G_CALLBACK(remmina_icon_on_launch_item), NULL);
258  g_signal_connect(G_OBJECT(menu), "edit-item", G_CALLBACK(remmina_icon_on_edit_item), NULL);
259 }
260 
261 #ifdef HAVE_LIBAPPINDICATOR
262 
263 void
265 {
266  TRACE_CALL(__func__);
267  GtkWidget *menu;
268  GtkWidget *menuitem;
269 
270  if (remmina_icon.icon && !remmina_pref.disable_tray_icon) {
271  menu = remmina_applet_menu_new();
272  app_indicator_set_menu(remmina_icon.icon, GTK_MENU(menu));
273 
275  remmina_applet_menu_populate(REMMINA_APPLET_MENU(menu));
277 
278  menuitem = gtk_separator_menu_item_new();
279  gtk_widget_show(menuitem);
280  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
281 
283  }
284 }
285 
286 #else
287 
289 {
290  TRACE_CALL(__func__);
291 }
292 
293 static void remmina_icon_popdown_menu(GtkWidget *widget, gpointer data)
294 {
295  TRACE_CALL(__func__);
296  if (gtk_get_current_event_time() - remmina_icon.popup_time <= 500) {
298  }
299 }
300 
301 static void remmina_icon_on_activate(GtkStatusIcon *icon, gpointer user_data)
302 {
303  TRACE_CALL(__func__);
304  GtkWidget *menu;
305  gint button, event_time;
306 
307  remmina_icon.popup_time = gtk_get_current_event_time();
308  menu = remmina_applet_menu_new();
310  remmina_applet_menu_populate(REMMINA_APPLET_MENU(menu));
311 
313 
314  button = 0;
315  event_time = gtk_get_current_event_time();
316 
317  g_signal_connect(G_OBJECT(menu), "deactivate", G_CALLBACK(remmina_icon_popdown_menu), NULL);
318 
319  gtk_menu_popup(GTK_MENU(menu), NULL, NULL, gtk_status_icon_position_menu, remmina_icon.icon, button, event_time);
320 }
321 
322 static void remmina_icon_on_popup_menu(GtkStatusIcon *icon, guint button, guint activate_time, gpointer user_data)
323 {
324  TRACE_CALL(__func__);
325  GtkWidget *menu;
326 
327  menu = gtk_menu_new();
328 
330 
331  gtk_menu_popup(GTK_MENU(menu), NULL, NULL, gtk_status_icon_position_menu, remmina_icon.icon, button, activate_time);
332 }
333 
334 #endif
335 
336 static void remmina_icon_save_autostart_file(GKeyFile *gkeyfile)
337 {
338  TRACE_CALL(__func__);
339  gchar *content;
340  gsize length;
341 
342  content = g_key_file_to_data(gkeyfile, &length, NULL);
343  g_file_set_contents(remmina_icon.autostart_file, content, length, NULL);
344  g_free(content);
345 }
346 
348 {
349  TRACE_CALL(__func__);
350  GKeyFile *gkeyfile;
351 
352  if (g_file_test(remmina_icon.autostart_file, G_FILE_TEST_EXISTS))
353  return;
354 
355  gkeyfile = g_key_file_new();
356  g_key_file_set_string(gkeyfile, "Desktop Entry", "Version", "1.0");
357  g_key_file_set_string(gkeyfile, "Desktop Entry", "Name", _("Remmina Applet"));
358  g_key_file_set_string(gkeyfile, "Desktop Entry", "Comment",
359  _("Connect to remote desktops through the applet menu"));
360  g_key_file_set_string(gkeyfile, "Desktop Entry", "Icon", REMMINA_APP_ID);
361  g_key_file_set_string(gkeyfile, "Desktop Entry", "Exec", "remmina -i");
362  g_key_file_set_boolean(gkeyfile, "Desktop Entry", "Terminal", FALSE);
363  g_key_file_set_string(gkeyfile, "Desktop Entry", "Type", "Application");
364  g_key_file_set_boolean(gkeyfile, "Desktop Entry", "Hidden", TRUE);
366  g_key_file_free(gkeyfile);
367 }
368 
377 {
378  TRACE_CALL(__func__);
379  gchar *gsversion;
380  unsigned int gsv_maj, gsv_min, gsv_seq;
381  gboolean gshell_has_legacyTray;
382 
383  if (!remmina_icon.icon)
384  return FALSE;
386  return FALSE;
387 
388  /* Special treatmen under Gnome Shell */
389  if ((gsversion = remmina_sysinfo_get_gnome_shell_version()) != NULL) {
390  if (sscanf(gsversion, "%u.%u", &gsv_maj, &gsv_min) == 2)
391  gsv_seq = gsv_maj << 16 | gsv_min << 8;
392  else
393  gsv_seq = 0x030000;
394  g_free(gsversion);
395 
396  gshell_has_legacyTray = FALSE;
397  if (gsv_seq >= 0x031000 && gsv_seq <= 0x031800) {
398  /* Gnome shell from 3.16 to 3.24, Status Icon (GtkStatusIcon) is visible on the drawer
399  * at the bottom left of the screen */
400  gshell_has_legacyTray = TRUE;
401  }
402 
403 
404 #ifdef HAVE_LIBAPPINDICATOR
405 
409  /* No libappindicator extension for gnome shell, no remmina_icon */
410  return TRUE;
411  } else if (gshell_has_legacyTray) {
412  return TRUE;
413  } else {
414  return FALSE;
415  }
416 #endif
417  /* Gnome Shell without LIBAPPINDICATOR */
418  if (gshell_has_legacyTray) {
419  return TRUE;
420  }else {
421  /* Gnome shell < 3.16, Status Icon (GtkStatusIcon) is hidden
422  * on the message tray */
423  return FALSE;
424  }
425  }
426  return TRUE;
427 }
428 
430 {
431  TRACE_CALL(__func__);
432 
433  gchar remmina_panel[23];
434  gboolean sni_supported;
435  char msg[200];
436 
438  g_stpcpy(remmina_panel, "remmina-panel-inverted");
439  else
440  g_stpcpy(remmina_panel, "remmina-panel");
441 
442 
443  /* Print on stdout the availability of appindicators on DBUS */
445 
446  strcpy(msg, "StatusNotifier/Appindicator support: ");
447  if (sni_supported) {
448  strcat(msg, "your desktop does support it");
449 #ifdef HAVE_LIBAPPINDICATOR
450  strcat(msg, " and libappindicator is compiled in remmina. Good!");
451 #else
452  strcat(msg, ", but you did not compile remmina with cmake’s -DWITH_APPINDICATOR=on");
453 #endif
454  } else {
455 #ifdef HAVE_LIBAPPINDICATOR
456  strcat(msg, "not supported by desktop. libappindicator will try to fallback to GtkStatusIcon/xembed");
457 #else
458  strcat(msg, "not supported by desktop. Remmina will try to fallback to GtkStatusIcon/xembed");
459 #endif
460  }
461  strcat(msg, "\n");
462  fputs(msg, stderr);
463 
465  if (remmina_icon.gsversion != NULL) {
466  printf("Running under Gnome Shell version %s\n", remmina_icon.gsversion);
467  }
468 
469  if (!remmina_icon.icon && !remmina_pref.disable_tray_icon) {
470 #ifdef HAVE_LIBAPPINDICATOR
471  remmina_icon.icon = app_indicator_new("remmina-icon", remmina_panel, APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
472  app_indicator_set_icon_theme_path(remmina_icon.icon, REMMINA_RUNTIME_DATADIR G_DIR_SEPARATOR_S "icons");
473 
474  app_indicator_set_status(remmina_icon.icon, APP_INDICATOR_STATUS_ACTIVE);
475  app_indicator_set_title(remmina_icon.icon, "Remmina");
477 #else
478  remmina_icon.icon = gtk_status_icon_new_from_icon_name(remmina_panel);
479 
480  gtk_status_icon_set_title(remmina_icon.icon, _("Remmina Remote Desktop Client"));
481  gtk_status_icon_set_tooltip_text(remmina_icon.icon, _("Remmina Remote Desktop Client"));
482 
483  g_signal_connect(G_OBJECT(remmina_icon.icon), "popup-menu", G_CALLBACK(remmina_icon_on_popup_menu), NULL);
484  g_signal_connect(G_OBJECT(remmina_icon.icon), "activate", G_CALLBACK(remmina_icon_on_activate), NULL);
485 #endif
486  }else if (remmina_icon.icon) {
487 #ifdef HAVE_LIBAPPINDICATOR
488  app_indicator_set_status(remmina_icon.icon, remmina_pref.disable_tray_icon ?
489  APP_INDICATOR_STATUS_PASSIVE : APP_INDICATOR_STATUS_ACTIVE);
490  /* With libappindicator we can also change the icon on the fly */
491  app_indicator_set_icon(remmina_icon.icon, remmina_panel);
492 #else
493  gtk_status_icon_set_visible(remmina_icon.icon, !remmina_pref.disable_tray_icon);
494 
495 #endif
496  }
497  if (!remmina_icon.avahi) {
498  remmina_icon.avahi = remmina_avahi_new();
499  }
500  if (remmina_icon.avahi) {
502  if (!remmina_icon.avahi->started)
503  remmina_avahi_start(remmina_icon.avahi);
504  }else {
505  remmina_avahi_stop(remmina_icon.avahi);
506  }
507  }
508  if (!remmina_icon.autostart_file) {
509  remmina_icon.autostart_file = g_strdup_printf("%s/.config/autostart/remmina-applet.desktop", g_get_home_dir());
511  }
512 }
513 
515 {
516  TRACE_CALL(__func__);
517  GKeyFile *gkeyfile;
518  gboolean b;
519 
520  gkeyfile = g_key_file_new();
521  g_key_file_load_from_file(gkeyfile, remmina_icon.autostart_file, G_KEY_FILE_NONE, NULL);
522  b = !g_key_file_get_boolean(gkeyfile, "Desktop Entry", "Hidden", NULL);
523  g_key_file_free(gkeyfile);
524  return b;
525 }
526 
527 void remmina_icon_set_autostart(gboolean autostart)
528 {
529  TRACE_CALL(__func__);
530  GKeyFile *gkeyfile;
531  gboolean b;
532 
533  gkeyfile = g_key_file_new();
534  g_key_file_load_from_file(gkeyfile, remmina_icon.autostart_file, G_KEY_FILE_NONE, NULL);
535  b = !g_key_file_get_boolean(gkeyfile, "Desktop Entry", "Hidden", NULL);
536  if (b != autostart) {
537  g_key_file_set_boolean(gkeyfile, "Desktop Entry", "Hidden", !autostart);
538  /* Refresh it in case translation is updated */
539  g_key_file_set_string(gkeyfile, "Desktop Entry", "Name", _("Remmina Applet"));
540  g_key_file_set_string(gkeyfile, "Desktop Entry", "Comment",
541  _("Connect to remote desktops through the applet menu"));
543  }
544  g_key_file_free(gkeyfile);
545 }
546 
gchar * gsversion
Definition: remmina_icon.c:68
static void remmina_icon_main(void)
Definition: remmina_icon.c:99
RemminaAvahi * remmina_avahi_new(void)
static void remmina_icon_preferences(void)
Definition: remmina_icon.c:105
guint32 popup_time
Definition: remmina_icon.c:66
gchar * autostart_file
Definition: remmina_icon.c:67
gboolean dark_tray_icon
Definition: remmina_pref.h:141
static void remmina_icon_on_launch_item(RemminaAppletMenu *menu, RemminaAppletMenuItem *menuitem, gpointer data)
Definition: remmina_icon.c:178
void remmina_icon_populate_menu(void)
Definition: remmina_icon.c:264
gboolean remmina_icon_is_available(void)
Determine whenever the Remmina icon is available.
Definition: remmina_icon.c:376
RemminaAppletMenuItemType item_type
gboolean disable_tray_icon
Definition: remmina_pref.h:140
GtkWidget * remmina_applet_menu_new(void)
void remmina_icon_init(void)
Definition: remmina_icon.c:429
RemminaAvahi * avahi
Definition: remmina_icon.c:65
void remmina_icon_destroy(void)
Definition: remmina_icon.c:74
static void remmina_icon_on_popup_menu(GtkStatusIcon *icon, guint button, guint activate_time, gpointer user_data)
Definition: remmina_icon.c:322
static void remmina_icon_populate_additional_menu_item(GtkWidget *menu)
Definition: remmina_icon.c:134
static void remmina_icon_enable_avahi(GtkCheckMenuItem *checkmenuitem, gpointer data)
Definition: remmina_icon.c:117
static void remmina_icon_on_edit_item(RemminaAppletMenu *menu, RemminaAppletMenuItem *menuitem, gpointer data)
Definition: remmina_icon.c:198
static RemminaIcon remmina_icon
Definition: remmina_icon.c:71
void remmina_applet_menu_set_hide_count(RemminaAppletMenu *menu, gboolean hide_count)
gchar * remmina_sysinfo_get_gnome_shell_version()
Query DBUS to get gnome shell version.
struct _RemminaIcon RemminaIcon
void remmina_exec_command(RemminaCommandType command, const gchar *data)
Definition: remmina_exec.c:214
void remmina_avahi_stop(RemminaAvahi *ga)
gboolean remmina_icon_is_autostart(void)
Definition: remmina_icon.c:514
static void remmina_icon_populate_extra_menu_item(GtkWidget *menu)
Definition: remmina_icon.c:218
void remmina_exec_exitremmina()
Definition: remmina_exec.c:88
void remmina_applet_menu_populate(RemminaAppletMenu *menu)
void remmina_avahi_free(RemminaAvahi *ga)
gboolean started
Definition: remmina_avahi.h:44
GHashTable * discovered_services
Definition: remmina_avahi.h:43
gboolean applet_hide_count
Definition: remmina_pref.h:139
RemminaPref remmina_pref
Definition: rcw.c:73
gboolean applet_enable_avahi
Definition: remmina_pref.h:177
void remmina_avahi_start(RemminaAvahi *ga)
static void remmina_icon_on_activate(GtkStatusIcon *icon, gpointer user_data)
Definition: remmina_icon.c:301
static void remmina_icon_popdown_menu(GtkWidget *widget, gpointer data)
Definition: remmina_icon.c:293
gboolean remmina_pref_save(void)
Definition: remmina_pref.c:707
gboolean remmina_sysinfo_is_appindicator_available()
gboolean applet_new_ontop
Definition: remmina_pref.h:138
static void remmina_icon_about(void)
Definition: remmina_icon.c:111
AppIndicator * icon
Definition: remmina_icon.c:61
GtkStatusIcon * icon
Definition: remmina_icon.c:63
GtkWidget * remmina_applet_menu_item_new(RemminaAppletMenuItemType item_type,...)
static void remmina_icon_create_autostart_file(void)
Definition: remmina_icon.c:347
void remmina_icon_set_autostart(gboolean autostart)
Definition: remmina_icon.c:527
void remmina_applet_menu_add_item(RemminaAppletMenu *menu, RemminaAppletMenuItem *menuitem)
void remmina_applet_menu_register_item(RemminaAppletMenu *menu, RemminaAppletMenuItem *menuitem)
static void remmina_icon_save_autostart_file(GKeyFile *gkeyfile)
Definition: remmina_icon.c:336