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.
spice_plugin.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2016-2018 Denis Ollier
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (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,
18  * Boston, MA 02110-1301, USA.
19  *
20  * In addition, as a special exception, the copyright holders give
21  * permission to link the code of portions of this program with the
22  * OpenSSL library under certain conditions as described in each
23  * individual source file, and distribute linked combinations
24  * including the two.
25  * You must obey the GNU General Public License in all respects
26  * for all of the code used other than OpenSSL. * If you modify
27  * file(s) with this exception, you may extend this exception to your
28  * version of the file(s), but you are not obligated to do so. * If you
29  * do not wish to do so, delete this exception statement from your
30  * version. * If you delete this exception statement from all source
31  * files in the program, then also delete it here.
32  *
33  */
34 
35 #include "spice_plugin.h"
36 
37 #define XSPICE_DEFAULT_PORT 5900
38 
39 enum {
46 };
47 
49 
50 static void remmina_plugin_spice_channel_new_cb(SpiceSession *, SpiceChannel *, RemminaProtocolWidget *);
51 static void remmina_plugin_spice_main_channel_event_cb(SpiceChannel *, SpiceChannelEvent, RemminaProtocolWidget *);
52 static void remmina_plugin_spice_display_ready_cb(GObject *, GParamSpec *, RemminaProtocolWidget *);
54 
56 #ifdef SPICE_GTK_CHECK_VERSION
57 # if SPICE_GTK_CHECK_VERSION(0, 31, 0)
58 void remmina_plugin_spice_file_transfer_new_cb(SpiceMainChannel *, SpiceFileTransferTask *, RemminaProtocolWidget *);
59 # endif /* SPICE_GTK_CHECK_VERSION(0, 31, 0) */
60 #endif /* SPICE_GTK_CHECK_VERSION */
61 
63 {
64  TRACE_CALL(__func__);
65 
66  RemminaPluginSpiceData *gpdata;
67  RemminaFile *remminafile;
68 
69  gpdata = g_new0(RemminaPluginSpiceData, 1);
70  g_object_set_data_full(G_OBJECT(gp), "plugin-data", gpdata, g_free);
71  remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
72 
73  gpdata->session = spice_session_new();
74  g_signal_connect(gpdata->session,
75  "channel-new",
77  gp);
78 
79  g_object_set(gpdata->session,
80  "password", g_strdup(remmina_plugin_service->file_get_string(remminafile, "password")),
81  "read-only", remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE),
82  "enable-audio", remmina_plugin_service->file_get_int(remminafile, "enableaudio", FALSE),
83  "enable-smartcard", remmina_plugin_service->file_get_int(remminafile, "sharesmartcard", FALSE),
84  "shared-dir", remmina_plugin_service->file_get_string(remminafile, "sharefolder"),
85  NULL);
86 
87  gpdata->gtk_session = spice_gtk_session_get(gpdata->session);
88  g_object_set(gpdata->gtk_session,
89  "auto-clipboard",
90  !remmina_plugin_service->file_get_int(remminafile, "disableclipboard", FALSE),
91  NULL);
92 }
93 
95 {
96  TRACE_CALL(__func__);
97 
98  gint port;
99  const gchar *cacert;
100  gchar *host, *tunnel;
101  RemminaPluginSpiceData *gpdata = GET_PLUGIN_DATA(gp);
102  RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
103 
104  /* Setup SSH tunnel if needed */
105  tunnel = remmina_plugin_service->protocol_plugin_start_direct_tunnel(gp, XSPICE_DEFAULT_PORT, FALSE);
106 
107  if (!tunnel) {
108  return FALSE;
109  }
110 
111  remmina_plugin_service->get_server_port(tunnel,
112  XSPICE_DEFAULT_PORT,
113  &host,
114  &port);
115 
116  g_object_set(gpdata->session, "host", host, NULL);
117  g_free(host);
118  g_free(tunnel);
119 
120  /* Unencrypted connection */
121  if (!remmina_plugin_service->file_get_int(remminafile, "usetls", FALSE)) {
122  g_object_set(gpdata->session, "port", g_strdup_printf("%i", port), NULL);
123  }
124  /* TLS encrypted connection */
125  else{
126  g_object_set(gpdata->session, "tls_port", g_strdup_printf("%i", port), NULL);
127 
128  /* Server CA certificate */
129  cacert = remmina_plugin_service->file_get_string(remminafile, "cacert");
130  if (cacert) {
131  g_object_set(gpdata->session, "ca-file", cacert, NULL);
132  }
133  }
134 
135  spice_session_connect(gpdata->session);
136 
137  /*
138  * FIXME: Add a waiting loop until the g_signal "channel-event" occurs.
139  * If the event is SPICE_CHANNEL_OPENED, TRUE should be returned,
140  * otherwise FALSE should be returned.
141  */
142  return TRUE;
143 }
144 
146 {
147  TRACE_CALL(__func__);
148  RemminaPluginSpiceData *gpdata = GET_PLUGIN_DATA(gp);
149 
150  if (gpdata->main_channel) {
151  g_signal_handlers_disconnect_by_func(gpdata->main_channel,
153  gp);
154  }
155 
156  if (gpdata->session) {
157  spice_session_disconnect(gpdata->session);
158  g_object_unref(gpdata->session);
159  gpdata->session = NULL;
160  remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
161  }
162 
163 #ifdef SPICE_GTK_CHECK_VERSION
164 # if SPICE_GTK_CHECK_VERSION(0, 31, 0)
165  if (gpdata->file_transfers) {
166  g_hash_table_unref(gpdata->file_transfers);
167  }
168 # endif /* SPICE_GTK_CHECK_VERSION(0, 31, 0) */
169 #endif /* SPICE_GTK_CHECK_VERSION */
170 
171  return FALSE;
172 }
173 
174 static void remmina_plugin_spice_channel_new_cb(SpiceSession *session, SpiceChannel *channel, RemminaProtocolWidget *gp)
175 {
176  TRACE_CALL(__func__);
177 
178  gint id;
179  RemminaPluginSpiceData *gpdata = GET_PLUGIN_DATA(gp);
180  RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
181 
182  g_object_get(channel, "channel-id", &id, NULL);
183 
184  if (SPICE_IS_MAIN_CHANNEL(channel)) {
185  gpdata->main_channel = SPICE_MAIN_CHANNEL(channel);
186  g_signal_connect(channel,
187  "channel-event",
189  gp);
190 #ifdef SPICE_GTK_CHECK_VERSION
191 # if SPICE_GTK_CHECK_VERSION(0, 31, 0)
192  g_signal_connect(channel,
193  "new-file-transfer",
195  gp);
196 # endif /* SPICE_GTK_CHECK_VERSION(0, 31, 0) */
197 #endif /* SPICE_GTK_CHECK_VERSION */
198  }
199 
200  if (SPICE_IS_DISPLAY_CHANNEL(channel)) {
201  gpdata->display_channel = SPICE_DISPLAY_CHANNEL(channel);
202  gpdata->display = spice_display_new(gpdata->session, id);
203  g_signal_connect(gpdata->display,
204  "notify::ready",
206  gp);
207  remmina_plugin_spice_display_ready_cb(G_OBJECT(gpdata->display), NULL, gp);
208  }
209 
210  if (SPICE_IS_PLAYBACK_CHANNEL(channel)) {
211  if (remmina_plugin_service->file_get_int(remminafile, "enableaudio", FALSE)) {
212  gpdata->audio = spice_audio_get(gpdata->session, NULL);
213  }
214  }
215 
216  if (SPICE_IS_WEBDAV_CHANNEL(channel)) {
217  if (remmina_plugin_service->file_get_string(remminafile, "sharefolder")) {
218  spice_channel_connect(channel);
219  }
220  }
221 }
222 
224 {
225  TRACE_CALL(__func__);
226 
227  gint ret;
228  gboolean disablepasswordstoring;
229  gchar *s_password;
230  gboolean save;
231 
232  RemminaPluginSpiceData *gpdata = GET_PLUGIN_DATA(gp);
233  RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
234 
235  disablepasswordstoring = remmina_plugin_service->file_get_int(remminafile, "disablepasswordstoring", FALSE);
236 
237  ret = remmina_plugin_service->protocol_plugin_init_auth(gp,
238  (disablepasswordstoring ? 0 : REMMINA_MESSAGE_PANEL_FLAG_SAVEPASSWORD),
239  _("Enter SPICE password"),
240  NULL,
241  remmina_plugin_service->file_get_string(remminafile, "password"),
242  NULL,
243  NULL);
244  if (ret == GTK_RESPONSE_OK) {
245  s_password = remmina_plugin_service->protocol_plugin_init_get_password(gp);
246  save = remmina_plugin_service->protocol_plugin_init_get_savepassword(gp);
247  if (save) {
248  remmina_plugin_service->file_set_string(remminafile, "password", s_password);
249  } else {
250  remmina_plugin_service->file_set_string(remminafile, "password", NULL);
251  }
252  } else {
253  return FALSE;
254  }
255 
256  g_object_set(gpdata->session, "password", s_password, NULL);
257  return TRUE;
258 }
259 
260 static void remmina_plugin_spice_main_channel_event_cb(SpiceChannel *channel, SpiceChannelEvent event, RemminaProtocolWidget *gp)
261 {
262  TRACE_CALL(__func__);
263 
264  gchar *server;
265  gint port;
266  RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
267 
268  switch (event) {
269  case SPICE_CHANNEL_CLOSED:
270  remmina_plugin_service->get_server_port(remmina_plugin_service->file_get_string(remminafile, "server"),
271  XSPICE_DEFAULT_PORT,
272  &server,
273  &port);
274  remmina_plugin_service->protocol_plugin_set_error(gp, _("Disconnected from the SPICE server \"%s\"."), server);
275  g_free(server);
277  break;
278  case SPICE_CHANNEL_OPENED:
279  break;
280  case SPICE_CHANNEL_ERROR_AUTH:
283  }else{
284  /* Connection is cancelled by the user by clicking cancel on auth panel, close it without showing errors */
285  // remmina_plugin_service->protocol_plugin_set_error(gp, _("Invalid password."));
287  }
288  break;
289  case SPICE_CHANNEL_ERROR_TLS:
290  remmina_plugin_service->protocol_plugin_set_error(gp, _("TLS connection error."));
292  break;
293  case SPICE_CHANNEL_ERROR_IO:
294  case SPICE_CHANNEL_ERROR_LINK:
295  case SPICE_CHANNEL_ERROR_CONNECT:
296  remmina_plugin_service->protocol_plugin_set_error(gp, _("Connection to the SPICE server dropped."));
298  break;
299  default:
300  break;
301  }
302 }
303 
304 static void remmina_plugin_spice_display_ready_cb(GObject *display, GParamSpec *param_spec, RemminaProtocolWidget *gp)
305 {
306  TRACE_CALL(__func__);
307 
308  gboolean ready;
309  RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
310 
311  g_object_get(display, "ready", &ready, NULL);
312 
313  if (ready) {
314  g_signal_handlers_disconnect_by_func(display,
316  gp);
317 
318  g_object_set(display,
320  "resize-guest", remmina_plugin_service->file_get_int(remminafile, "resizeguest", FALSE),
321  NULL);
322  gtk_container_add(GTK_CONTAINER(gp), GTK_WIDGET(display));
323  gtk_widget_show(GTK_WIDGET(display));
324 
325  remmina_plugin_service->protocol_plugin_register_hostkey(gp, GTK_WIDGET(display));
326  remmina_plugin_service->protocol_plugin_signal_connection_opened(gp);
327  }
328 }
329 
330 /* Send a keystroke to the plugin window */
331 static void remmina_plugin_spice_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
332 {
333  TRACE_CALL(__func__);
334  RemminaPluginSpiceData *gpdata = GET_PLUGIN_DATA(gp);
335 
336  if (gpdata->display) {
337  spice_display_send_keys(gpdata->display,
338  keystrokes,
339  keylen,
340  SPICE_DISPLAY_KEY_EVENT_CLICK);
341  }
342 }
343 
344 /* Send CTRL+ALT+DEL keys keystrokes to the plugin socket widget */
346 {
347  TRACE_CALL(__func__);
348 
349  guint keys[] = { GDK_KEY_Control_L, GDK_KEY_Alt_L, GDK_KEY_Delete };
350 
351  remmina_plugin_spice_keystroke(gp, keys, G_N_ELEMENTS(keys));
352 }
353 
355 {
356  TRACE_CALL(__func__);
357 
358  gint scale, width, height;
359  RemminaPluginSpiceData *gpdata = GET_PLUGIN_DATA(gp);
360  RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
361 
362  scale = remmina_plugin_service->file_get_int(remminafile, "scale", FALSE);
363  g_object_set(gpdata->display, "scaling", scale, NULL);
364 
365  if (scale) {
366  /* In scaled mode, the SpiceDisplay will get its dimensions from its parent */
367  gtk_widget_set_size_request(GTK_WIDGET(gpdata->display), -1, -1 );
368  }else {
369  /* In non scaled mode, the plugins forces dimensions of the SpiceDisplay */
370  g_object_get(gpdata->display_channel,
371  "width", &width,
372  "height", &height,
373  NULL);
374  gtk_widget_set_size_request(GTK_WIDGET(gpdata->display), width, height);
375  }
376 }
377 
379 {
380  TRACE_CALL(__func__);
381 
382  return TRUE;
383 }
384 
386 {
387  TRACE_CALL(__func__);
388 
389  RemminaPluginSpiceData *gpdata = GET_PLUGIN_DATA(gp);
390  RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
391 
392  switch (feature->id) {
394  g_object_set(gpdata->session,
395  "read-only",
396  remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE),
397  NULL);
398  break;
400  g_object_set(gpdata->display,
401  "resize-guest",
402  remmina_plugin_service->file_get_int(remminafile, "resizeguest", TRUE),
403  NULL);
404  break;
406  g_object_set(gpdata->gtk_session,
407  "auto-clipboard",
408  !remmina_plugin_service->file_get_int(remminafile, "disableclipboard", FALSE),
409  NULL);
410  break;
413  break;
416  break;
419  break;
420  default:
421  break;
422  }
423 }
424 
425 /* Array of RemminaProtocolSetting for basic settings.
426  * Each item is composed by:
427  * a) RemminaProtocolSettingType for setting type
428  * b) Setting name
429  * c) Setting description
430  * d) Compact disposition
431  * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
432  * f) Setting Tooltip
433  */
435 {
436  { REMMINA_PROTOCOL_SETTING_TYPE_SERVER, "server", NULL, FALSE, NULL, NULL},
437  { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "password", N_("User password"), FALSE, NULL, NULL},
438  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "usetls", N_("Use TLS encryption"), FALSE, NULL, NULL},
439  { REMMINA_PROTOCOL_SETTING_TYPE_FILE, "cacert", N_("Server CA certificate"), FALSE, NULL, NULL},
440  { REMMINA_PROTOCOL_SETTING_TYPE_FOLDER, "sharefolder", N_("Share folder"), FALSE, NULL, NULL},
441  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL}
442 };
443 
444 /* Array of RemminaProtocolSetting for advanced settings.
445  * Each item is composed by:
446  * a) RemminaProtocolSettingType for setting type
447  * b) Setting name
448  * c) Setting description
449  * d) Compact disposition
450  * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
451  * f) Setting Tooltip
452  */
454 {
455  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableclipboard", N_("Disable clipboard sync"), TRUE, NULL, NULL},
456  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disablepasswordstoring", N_("Forget passwords after use"), TRUE, NULL, NULL},
457  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "enableaudio", N_("Enable audio channel"), TRUE, NULL, NULL},
458  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "resizeguest", N_("Resize guest to match window size"), TRUE, NULL, NULL},
459  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "sharesmartcard", N_("Share smart card"), TRUE, NULL, NULL},
460  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "viewonly", N_("View only"), TRUE, NULL, NULL},
461  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, TRUE, NULL, NULL}
462 };
463 
464 /* Array for available features.
465  * The last element of the array must be REMMINA_PROTOCOL_FEATURE_TYPE_END. */
467 {
468  { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_PLUGIN_SPICE_FEATURE_PREF_VIEWONLY, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), "viewonly",
469  N_("View only") },
470  { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_PLUGIN_SPICE_FEATURE_PREF_RESIZEGUEST, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), "resizeguest", N_("Resize guest to match window size")},
471  { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_PLUGIN_SPICE_FEATURE_PREF_DISABLECLIPBOARD, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), "disableclipboard", N_("Disable clipboard sync")},
473  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PLUGIN_SPICE_FEATURE_TOOL_USBREDIR, N_("Select USB devices for redirection"), NULL, NULL},
475  { REMMINA_PROTOCOL_FEATURE_TYPE_END, 0, NULL, NULL, NULL}
476 };
477 
479 {
481  "SPICE", // Name
482  N_("SPICE - Simple Protocol for Independent Computing Environments"), // Description
483  GETTEXT_PACKAGE, // Translation domain
484  VERSION, // Version number
485  "remmina-spice-symbolic", // Icon for normal connection
486  "remmina-spice-ssh-symbolic", // Icon for SSH connection
487  remmina_plugin_spice_basic_settings, // Array for basic settings
488  remmina_plugin_spice_advanced_settings, // Array for advanced settings
489  REMMINA_PROTOCOL_SSH_SETTING_TUNNEL, // SSH settings type
490  remmina_plugin_spice_features, // Array for available features
491  remmina_plugin_spice_init, // Plugin initialization
492  remmina_plugin_spice_open_connection, // Plugin open connection
493  remmina_plugin_spice_close_connection, // Plugin close connection
494  remmina_plugin_spice_query_feature, // Query for available features
495  remmina_plugin_spice_call_feature, // Call a feature
496  remmina_plugin_spice_keystroke, // Send a keystroke
497  NULL // No screenshot support available
498 };
499 
500 G_MODULE_EXPORT gboolean
502 {
503  TRACE_CALL(__func__);
504  remmina_plugin_service = service;
505 
506  bindtextdomain(GETTEXT_PACKAGE, REMMINA_RUNTIME_LOCALEDIR);
507  bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
508 
509  if (!service->register_plugin((RemminaPlugin*)&remmina_plugin_spice)) {
510  return FALSE;
511  }
512 
513  return TRUE;
514 }
static const RemminaProtocolFeature remmina_plugin_spice_features[]
Definition: spice_plugin.c:466
SpiceDisplay * display
Definition: spice_plugin.h:55
RemminaScaleMode(* remmina_protocol_widget_get_current_scale_mode)(RemminaProtocolWidget *gp)
Definition: plugin.h:154
void(* protocol_plugin_set_error)(RemminaProtocolWidget *gp, const gchar *fmt,...)
Definition: plugin.h:158
static void remmina_plugin_spice_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
Definition: spice_plugin.c:331
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:41
static RemminaProtocolPlugin remmina_plugin_spice
Definition: spice_plugin.c:478
static void remmina_plugin_spice_main_channel_event_cb(SpiceChannel *, SpiceChannelEvent, RemminaProtocolWidget *)
Definition: spice_plugin.c:260
SpiceDisplayChannel * display_channel
Definition: spice_plugin.h:56
static gboolean remmina_plugin_spice_query_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: spice_plugin.c:378
void(* protocol_plugin_signal_connection_closed)(RemminaProtocolWidget *gp)
Definition: plugin.h:167
void(* get_server_port)(const gchar *server, gint defaultport, gchar **host, gint *port)
Definition: plugin.h:221
gint(* file_get_int)(RemminaFile *remminafile, const gchar *setting, gint default_value)
Definition: plugin.h:203
GHashTable * file_transfers
Definition: spice_plugin.h:64
void(* protocol_plugin_signal_connection_opened)(RemminaProtocolWidget *gp)
Definition: plugin.h:168
gboolean(* protocol_plugin_init_get_savepassword)(RemminaProtocolWidget *gp)
Definition: plugin.h:178
void remmina_plugin_spice_select_usb_devices(RemminaProtocolWidget *)
gboolean(* register_plugin)(RemminaPlugin *plugin)
Definition: plugin.h:148
static SoupSession * session
Definition: rmnews.c:77
static gboolean remmina_plugin_spice_open_connection(RemminaProtocolWidget *gp)
Definition: spice_plugin.c:94
void remmina_plugin_spice_file_transfer_new_cb(SpiceMainChannel *, SpiceFileTransferTask *, RemminaProtocolWidget *)
SpiceGtkSession * gtk_session
Definition: spice_plugin.h:57
gint(* protocol_plugin_init_auth)(RemminaProtocolWidget *gp, RemminaMessagePanelFlags pflags, const gchar *title, const gchar *default_username, const gchar *default_password, const gchar *default_domain, const gchar *password_prompt)
Definition: plugin.h:172
static void remmina_plugin_spice_display_ready_cb(GObject *, GParamSpec *, RemminaProtocolWidget *)
Definition: spice_plugin.c:304
RemminaFile *(* protocol_plugin_get_file)(RemminaProtocolWidget *gp)
Definition: plugin.h:160
static const RemminaProtocolSetting remmina_plugin_spice_basic_settings[]
Definition: spice_plugin.c:434
gchar *(* protocol_plugin_init_get_password)(RemminaProtocolWidget *gp)
Definition: plugin.h:176
static void remmina_plugin_spice_update_scale(RemminaProtocolWidget *)
Definition: spice_plugin.c:354
static void remmina_plugin_spice_call_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: spice_plugin.c:385
SpiceSession * session
Definition: spice_plugin.h:59
void(* protocol_plugin_register_hostkey)(RemminaProtocolWidget *gp, GtkWidget *widget)
Definition: plugin.h:162
static gboolean remmina_plugin_spice_ask_auth(RemminaProtocolWidget *gp)
Definition: spice_plugin.c:223
SpiceMainChannel * main_channel
Definition: spice_plugin.h:58
void(* file_set_string)(RemminaFile *remminafile, const gchar *setting, const gchar *value)
Definition: plugin.h:199
static const RemminaProtocolSetting remmina_plugin_spice_advanced_settings[]
Definition: spice_plugin.c:453
G_MODULE_EXPORT gboolean remmina_plugin_entry(RemminaPluginService *service)
Definition: spice_plugin.c:501
gchar *(* protocol_plugin_start_direct_tunnel)(RemminaProtocolWidget *gp, gint default_port, gboolean port_plus)
Definition: plugin.h:163
static void remmina_plugin_spice_channel_new_cb(SpiceSession *, SpiceChannel *, RemminaProtocolWidget *)
Definition: spice_plugin.c:174
const gchar *(* file_get_string)(RemminaFile *remminafile, const gchar *setting)
Definition: plugin.h:200
static void remmina_plugin_spice_send_ctrlaltdel(RemminaProtocolWidget *gp)
Definition: spice_plugin.c:345
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:907
static void remmina_plugin_spice_init(RemminaProtocolWidget *gp)
Definition: spice_plugin.c:62
static RemminaPluginService * remmina_plugin_service
Definition: spice_plugin.c:48
static gboolean remmina_plugin_spice_close_connection(RemminaProtocolWidget *gp)
Definition: spice_plugin.c:145