Remmina - The GTK+ Remote Desktop Client  v1.4.34
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-2023 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 
39 #include <glib/gi18n.h>
40 
41 #include "remmina_icon.h"
42 
43 #ifdef HAVE_LIBAPPINDICATOR
44 # ifdef HAVE_AYATANA_LIBAPPINDICATOR
45 # include <libayatana-appindicator/app-indicator.h>
46 # else
47 # include <libappindicator/app-indicator.h>
48 # endif
49 #include "remmina_widget_pool.h"
50 #include "remmina_pref.h"
51 #include "remmina_exec.h"
52 #ifdef HAVE_LIBAVAHI_CLIENT
53 #include "remmina_avahi.h"
54 #endif
56 #include "remmina_applet_menu.h"
57 #include "rcw.h"
58 #include "remmina_log.h"
60 #include "remmina_sysinfo.h"
61 
62 typedef struct _RemminaIcon {
63  AppIndicator * icon;
65 #ifdef HAVE_LIBAVAHI_CLIENT
67 #endif
68  guint32 popup_time;
69  gchar * autostart_file;
71 
73 { 0 };
74 
76 {
77  TRACE_CALL(__func__);
78  if (remmina_icon.icon) {
79  app_indicator_set_status(remmina_icon.icon, APP_INDICATOR_STATUS_PASSIVE);
80  remmina_icon.icon = NULL;
81  }
82 #ifdef HAVE_LIBAVAHI_CLIENT
83  if (remmina_icon.avahi) {
85  remmina_icon.avahi = NULL;
86  }
87 #endif
91  }
92 }
93 
94 static void remmina_icon_main(void)
95 {
96  TRACE_CALL(__func__);
98 }
99 
100 static void remmina_icon_preferences(void)
101 {
102  TRACE_CALL(__func__);
104 }
105 
106 static void remmina_icon_about(void)
107 {
108  TRACE_CALL(__func__);
110 }
111 
112 #ifdef HAVE_LIBAVAHI_CLIENT
113 static void remmina_icon_enable_avahi(GtkCheckMenuItem *checkmenuitem, gpointer data)
114 {
115  TRACE_CALL(__func__);
116  if (!remmina_icon.avahi)
117  return;
118 
119  if (gtk_check_menu_item_get_active(checkmenuitem)) {
121  if (!remmina_icon.avahi->started)
123  } else {
126  }
128 }
129 #endif
130 
131 static void remmina_icon_populate_additional_menu_item(GtkWidget *menu)
132 {
133  TRACE_CALL(__func__);
134  GtkWidget *menuitem;
135 
136 
137 #ifdef HAVE_LIBAVAHI_CLIENT
138 
139  menuitem = gtk_separator_menu_item_new();
140  gtk_widget_show(menuitem);
141  gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
142 
143  menuitem = gtk_check_menu_item_new_with_label(_("Enable Service Discovery"));
145  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
146  gtk_widget_show(menuitem);
147  g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(remmina_icon_enable_avahi), NULL);
148  gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
149 
150 
151 #endif
152 
153  menuitem = gtk_separator_menu_item_new();
154  gtk_widget_show(menuitem);
155  gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
156 
157  menuitem = gtk_menu_item_new_with_mnemonic(_("_Quit"));
158  gtk_widget_show(menuitem);
159  gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
160  g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(remmina_exec_exitremmina_one_confirm), NULL);
161 
162  menuitem = gtk_menu_item_new_with_mnemonic(_("_About"));
163  gtk_widget_show(menuitem);
164  gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
165  g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(remmina_icon_about), NULL);
166 
167  menuitem = gtk_menu_item_new_with_mnemonic(_("_Preferences"));
168  gtk_widget_show(menuitem);
169  gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
170  g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(remmina_icon_preferences), NULL);
171 
172  menuitem = gtk_menu_item_new_with_label(_("Open Main Window"));
173  gtk_widget_show(menuitem);
174  gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
175  g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(remmina_icon_main), NULL);
176 
177 
178 }
179 
180 static void remmina_icon_on_launch_item(RemminaAppletMenu *menu, RemminaAppletMenuItem *menuitem, gpointer data)
181 {
182  TRACE_CALL(__func__);
183  gchar *s;
184 
185  switch (menuitem->item_type) {
188  break;
191  break;
193  s = g_strdup_printf("%s,%s", menuitem->protocol, menuitem->name);
195  g_free(s);
196  break;
197  }
198 }
199 
200 static void remmina_icon_on_edit_item(RemminaAppletMenu *menu, RemminaAppletMenuItem *menuitem, gpointer data)
201 {
202  TRACE_CALL(__func__);
203  gchar *s;
204 
205  switch (menuitem->item_type) {
208  break;
211  break;
213  s = g_strdup_printf("%s,%s", menuitem->protocol, menuitem->name);
215  g_free(s);
216  break;
217  }
218 }
219 
220 static void remmina_icon_populate_extra_menu_item(GtkWidget *menu)
221 {
222  TRACE_CALL(__func__);
223  GtkWidget *menuitem;
224  gboolean new_ontop;
225 
226  new_ontop = remmina_pref.applet_new_ontop;
227 
228 #ifdef HAVE_LIBAVAHI_CLIENT
229  GHashTableIter iter;
230  gchar *tmp;
231  /* Iterate all discovered services from Avahi */
232  if (remmina_icon.avahi) {
233  g_hash_table_iter_init(&iter, remmina_icon.avahi->discovered_services);
234  while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&tmp)) {
236  gtk_widget_show(menuitem);
237  remmina_applet_menu_add_item(REMMINA_APPLET_MENU(menu), REMMINA_APPLET_MENU_ITEM(menuitem));
238  }
239  }
240 #endif
241 
242  /* New Connection */
244  gtk_widget_show(menuitem);
245  remmina_applet_menu_register_item(REMMINA_APPLET_MENU(menu), REMMINA_APPLET_MENU_ITEM(menuitem));
246  if (new_ontop)
247  gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
248  else
249  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
250 
251  g_signal_connect(G_OBJECT(menu), "launch-item", G_CALLBACK(remmina_icon_on_launch_item), NULL);
252  g_signal_connect(G_OBJECT(menu), "edit-item", G_CALLBACK(remmina_icon_on_edit_item), NULL);
253 }
254 
255 void
257 {
258  TRACE_CALL(__func__);
259  GtkWidget *menu;
260  GtkWidget *menuitem;
261 
263  menu = remmina_applet_menu_new();
264  app_indicator_set_menu(remmina_icon.icon, GTK_MENU(menu));
265 
267  remmina_applet_menu_populate(REMMINA_APPLET_MENU(menu));
268 
269 
270  menuitem = gtk_separator_menu_item_new();
271  gtk_widget_show(menuitem);
272  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
273 
276  }
277 }
278 
279 static void remmina_icon_save_autostart_file(GKeyFile *gkeyfile)
280 {
281  TRACE_CALL(__func__);
282  gchar *content;
283  gsize length;
284 
285  content = g_key_file_to_data(gkeyfile, &length, NULL);
286  if (remmina_icon.autostart_file != NULL) {
287  g_file_set_contents(remmina_icon.autostart_file, content, length, NULL);
288  }
289  else {
290  REMMINA_WARNING("Cannot save remmina icon autostart file. Uncheck Preferences -> Applet -> No Tray Icon to recreate it.");
291  }
292  g_free(content);
293 }
294 
296 {
297  TRACE_CALL(__func__);
298  if (g_file_test(remmina_icon.autostart_file, G_FILE_TEST_EXISTS))
299  return;
300 
301  GKeyFile *gkeyfile;
302 
303  gkeyfile = g_key_file_new();
304  g_key_file_set_string(gkeyfile, "Desktop Entry", "Version", "1.0");
305  // TRANSLATORS: Applet name as per the Freedesktop Desktop entry specification https://specifications.freedesktop.org/desktop-entry-spec/latest/
306  g_key_file_set_string(gkeyfile, "Desktop Entry", "Name", _("Remmina Applet"));
307  // TRANSLATORS: Applet comment/description as per the Freedesktop Desktop entry specification https://specifications.freedesktop.org/desktop-entry-spec/latest/
308  g_key_file_set_string(gkeyfile, "Desktop Entry", "Comment", _("Connect to remote desktops through the applet menu"));
309  g_key_file_set_string(gkeyfile, "Desktop Entry", "Icon", REMMINA_APP_ID);
310  if (getenv("FLATPAK_ID")){
311  g_key_file_set_string(gkeyfile, "Desktop Entry", "Exec", "flatpak run org.remmina.Remmina -i");
312  }
313  else{
314  g_key_file_set_string(gkeyfile, "Desktop Entry", "Exec", "remmina -i");
315  }
316  g_key_file_set_boolean(gkeyfile, "Desktop Entry", "Terminal", FALSE);
317  g_key_file_set_string(gkeyfile, "Desktop Entry", "Type", "Application");
318  g_key_file_set_boolean(gkeyfile, "Desktop Entry", "Hidden", FALSE);
320  g_key_file_free(gkeyfile);
321 }
322 
331 {
332  TRACE_CALL(__func__);
333 
334  if (!remmina_icon.icon)
335  return FALSE;
337  return FALSE;
338 
339  if (remmina_icon.indicator_connected == FALSE) {
340  REMMINA_DEBUG("Indicator is not connected to panel, thus it cannot be displayed.");
341  return FALSE;
342  } else {
343  REMMINA_DEBUG("Indicator is connected to panel, thus it can be displayed.");
344  return TRUE;
345  }
351  return TRUE;
352 }
353 
354 static void
355 remmina_icon_connection_changed_cb(AppIndicator *indicator, gboolean connected, gpointer data)
356 {
357  TRACE_CALL(__func__);
358  REMMINA_DEBUG("Indicator connection changed to: %d", connected);
359  remmina_icon.indicator_connected = connected;
360 }
361 
363 {
364  TRACE_CALL(__func__);
365 
366  gchar remmina_panel[29];
367  gboolean sni_supported;
368 
369  g_stpcpy(remmina_panel, "org.remmina.Remmina-status");
370 
371  /* Print on stdout the availability of appindicators on DBUS */
373 
374  g_autofree gchar *wmname = g_ascii_strdown(remmina_sysinfo_get_wm_name(), -1);
375  //TRANSLATORS: These are Linux desktop components to show icons in the system tray, after the “ there's the Desktop Name (like GNOME).
376  g_autofree gchar *msg = g_strconcat(
377  _("StatusNotifier/Appindicator support in “"),
378  wmname,
379  "”:",
380  NULL);
381 
382  if (sni_supported) {
383  //TRANSLATORS: %s is a placeholder for "StatusNotifier/Appindicator suppor in “DESKTOP NAME”: "
384  REMMINA_INFO(_("%s your desktop does support it"), msg);
385  //TRANSLATORS: %s is a placeholder for "StatusNotifier/Appindicator suppor in “DESKTOP NAME”: "
386  REMMINA_INFO(_("%s and Remmina has built-in (compiled) support for libappindicator."), msg);
387  } else {
388  //TRANSLATORS: %s is a placeholder for "StatusNotifier/Appindicator suppor in “DESKTOP NAME”: "
389  REMMINA_INFO(_("%s not supported natively by your Desktop Environment. libappindicator will try to fallback to GtkStatusIcon/xembed"), msg);
390  }
391  if (g_strrstr(wmname, "mate") != NULL)
392  //TRANSLATORS: %s is a placeholder for "StatusNotifier/Appindicator suppor in “DESKTOP NAME”: "
393  REMMINA_INFO(_("%s You may need to install, and use XApp Status Applet"), msg);
394  if (g_strrstr(wmname, "kde") != NULL)
395  //TRANSLATORS: %s is a placeholder for "StatusNotifier/Appindicator suppor in “DESKTOP NAME”: "
396  REMMINA_INFO(_("%s You may need to install, and use KStatusNotifierItem"), msg);
397  if (g_strrstr(wmname, "plasma") != NULL)
398  //TRANSLATORS: %s is a placeholder for "StatusNotifier/Appindicator suppor in “DESKTOP NAME”: "
399  REMMINA_INFO(_("%s You may need to install, and use XEmbed SNI Proxy"), msg);
400  if (g_strrstr(wmname, "gnome") != NULL)
401  //TRANSLATORS: %s is a placeholder for "StatusNotifier/Appindicator suppor in “DESKTOP NAME”: "
402  REMMINA_INFO(_("%s You may need to install, and use Gnome Shell Extension Appindicator"), msg);
403 
405  remmina_icon.icon = app_indicator_new("remmina-icon", remmina_panel, APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
406  app_indicator_set_status(remmina_icon.icon, APP_INDICATOR_STATUS_ACTIVE);
407  app_indicator_set_title(remmina_icon.icon, "Remmina");
409  } else if (remmina_icon.icon) {
410  app_indicator_set_status(remmina_icon.icon, remmina_pref.disable_tray_icon ?
411  APP_INDICATOR_STATUS_PASSIVE : APP_INDICATOR_STATUS_ACTIVE);
412  /* With libappindicator we can also change the icon on the fly */
413  app_indicator_set_icon(remmina_icon.icon, remmina_panel);
414  }
416 #ifdef HAVE_LIBAVAHI_CLIENT
417  if (!remmina_icon.avahi)
419  if (remmina_icon.avahi) {
421  if (!remmina_icon.avahi->started)
423  } else {
425  }
426  }
427 #endif
429  remmina_icon.autostart_file = g_strdup_printf("%s/.config/autostart/remmina-applet.desktop", g_get_home_dir());
431  }
432  // "connected" property means a visible indicator, otherwise could be hidden. or fall back to GtkStatusIcon
433  if (remmina_icon.icon)
434  g_signal_connect(G_OBJECT(remmina_icon.icon), "connection-changed", G_CALLBACK(remmina_icon_connection_changed_cb), NULL);
435  //g_object_get(G_OBJECT(remmina_icon.icon), "connected", &remmina_icon.indicator_connected, NULL);
436 }
437 
439 {
440  TRACE_CALL(__func__);
441  GKeyFile *gkeyfile;
442  gboolean b;
443 
444  gkeyfile = g_key_file_new();
445 
446  if (remmina_icon.autostart_file != NULL) {
447  g_key_file_load_from_file(gkeyfile, remmina_icon.autostart_file, G_KEY_FILE_NONE, NULL);
448  }
449  else {
450  REMMINA_WARNING("Cannot load remmina icon autostart file. Uncheck Preferences -> Applet -> No Tray Icon to recreate it.");
451  }
452 
453  b = !g_key_file_get_boolean(gkeyfile, "Desktop Entry", "Hidden", NULL);
454  g_key_file_free(gkeyfile);
455  return b;
456 }
457 
458 void remmina_icon_set_autostart(gboolean autostart)
459 {
460  TRACE_CALL(__func__);
461  GKeyFile *gkeyfile;
462  gboolean b;
463 
464  if (remmina_icon.autostart_file != NULL) {
465  gkeyfile = g_key_file_new();
466  g_key_file_load_from_file(gkeyfile, remmina_icon.autostart_file, G_KEY_FILE_NONE, NULL);
467  b = !g_key_file_get_boolean(gkeyfile, "Desktop Entry", "Hidden", NULL);
468  if (b != autostart) {
469  g_key_file_set_boolean(gkeyfile, "Desktop Entry", "Hidden", !autostart);
470  /* Refresh it in case translation is updated */
471  // TRANSLATORS: Applet Name as per the Freedesktop Desktop entry specification https://specifications.freedesktop.org/desktop-entry-spec/latest/
472  g_key_file_set_string(gkeyfile, "Desktop Entry", "Name", _("Remmina Applet"));
473  // TRANSLATORS: Applet comment/description as per the Freedesktop Desktop entry specification https://specifications.freedesktop.org/desktop-entry-spec/latest/
474  g_key_file_set_string(gkeyfile, "Desktop Entry", "Comment", _("Connect to remote desktops through the applet menu"));
476  g_key_file_free(gkeyfile);
477  }
478  }
479  else {
480  REMMINA_WARNING("Cannot load remmina icon autostart file. Uncheck Preferences -> Applet -> No Tray Icon to recreate it.");
481  }
482 }
483 
484 #else
485 void remmina_icon_init(void) {};
486 void remmina_icon_destroy(void) {};
487 gboolean remmina_icon_is_available(void) {return FALSE;};
488 void remmina_icon_populate_menu(void) {};
489 void remmina_icon_set_autostart(gboolean autostart) {} ;
490 gboolean remmina_icon_is_autostart(void) {return FALSE;};
491 #endif
RemminaPref remmina_pref
Definition: rcw.c:79
void remmina_applet_menu_add_item(RemminaAppletMenu *menu, RemminaAppletMenuItem *menuitem)
void remmina_applet_menu_populate(RemminaAppletMenu *menu)
void remmina_applet_menu_register_item(RemminaAppletMenu *menu, RemminaAppletMenuItem *menuitem)
GtkWidget * remmina_applet_menu_new(void)
void remmina_applet_menu_set_hide_count(RemminaAppletMenu *menu, gboolean hide_count)
GtkWidget * remmina_applet_menu_item_new(RemminaAppletMenuItemType item_type,...)
@ REMMINA_APPLET_MENU_ITEM_DISCOVERED
@ REMMINA_APPLET_MENU_ITEM_NEW
@ REMMINA_APPLET_MENU_ITEM_FILE
void remmina_avahi_start(RemminaAvahi *ga)
void remmina_avahi_free(RemminaAvahi *ga)
RemminaAvahi * remmina_avahi_new(void)
void remmina_avahi_stop(RemminaAvahi *ga)
void remmina_exec_command(RemminaCommandType command, const gchar *data)
Definition: remmina_exec.c:382
void remmina_exec_exitremmina_one_confirm()
Definition: remmina_exec.c:123
@ REMMINA_COMMAND_NEW
Definition: remmina_exec.h:46
@ REMMINA_COMMAND_CONNECT
Definition: remmina_exec.h:47
@ REMMINA_COMMAND_PREF
Definition: remmina_exec.h:45
@ REMMINA_COMMAND_ABOUT
Definition: remmina_exec.h:49
@ REMMINA_COMMAND_EDIT
Definition: remmina_exec.h:48
@ REMMINA_COMMAND_MAIN
Definition: remmina_exec.h:44
static void remmina_icon_save_autostart_file(GKeyFile *gkeyfile)
Definition: remmina_icon.c:279
void remmina_icon_destroy(void)
Definition: remmina_icon.c:75
static void remmina_icon_populate_extra_menu_item(GtkWidget *menu)
Definition: remmina_icon.c:220
gboolean remmina_icon_is_available(void)
Determine whenever the Remmina icon is available.
Definition: remmina_icon.c:330
static void remmina_icon_connection_changed_cb(AppIndicator *indicator, gboolean connected, gpointer data)
Definition: remmina_icon.c:355
static void remmina_icon_create_autostart_file(void)
Definition: remmina_icon.c:295
void remmina_icon_set_autostart(gboolean autostart)
Definition: remmina_icon.c:458
static void remmina_icon_about(void)
Definition: remmina_icon.c:106
static void remmina_icon_populate_additional_menu_item(GtkWidget *menu)
Definition: remmina_icon.c:131
static RemminaIcon remmina_icon
Definition: remmina_icon.c:72
static void remmina_icon_main(void)
Definition: remmina_icon.c:94
static void remmina_icon_enable_avahi(GtkCheckMenuItem *checkmenuitem, gpointer data)
Definition: remmina_icon.c:113
static void remmina_icon_on_launch_item(RemminaAppletMenu *menu, RemminaAppletMenuItem *menuitem, gpointer data)
Definition: remmina_icon.c:180
static void remmina_icon_preferences(void)
Definition: remmina_icon.c:100
gboolean remmina_icon_is_autostart(void)
Definition: remmina_icon.c:438
struct _RemminaIcon RemminaIcon
void remmina_icon_populate_menu(void)
Definition: remmina_icon.c:256
static void remmina_icon_on_edit_item(RemminaAppletMenu *menu, RemminaAppletMenuItem *menuitem, gpointer data)
Definition: remmina_icon.c:200
void remmina_icon_init(void)
Definition: remmina_icon.c:362
gboolean remmina_pref_save(void)
Definition: remmina_pref.c:861
gchar * remmina_sysinfo_get_wm_name()
Query environment variables to get the Window manager name.
gboolean remmina_sysinfo_is_appindicator_available()
RemminaAppletMenuItemType item_type
GHashTable * discovered_services
Definition: remmina_avahi.h:45
gboolean started
Definition: remmina_avahi.h:46
RemminaAvahi * avahi
Definition: remmina_icon.c:66
guint32 popup_time
Definition: remmina_icon.c:68
AppIndicator * icon
Definition: remmina_icon.c:63
gboolean indicator_connected
Definition: remmina_icon.c:64
gchar * autostart_file
Definition: remmina_icon.c:69
gboolean applet_new_ontop
Definition: remmina_pref.h:163
gboolean applet_enable_avahi
Definition: remmina_pref.h:217
gboolean applet_hide_count
Definition: remmina_pref.h:164
gboolean disable_tray_icon
Definition: remmina_pref.h:165