1 /*
2 * vinagre-spice-tab.c
3 * SPICE Implementation for VinagreSpiceTab widget
4 * This file is part of vinagre
5 *
6 * Copyright (C) 2011 Red Hat, Inc.
7 * Author: Marc-Andre Lureau <marcandre.lureau@redhat.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include <config.h>
24 #include <glib/gi18n.h>
25 #include <spice-client-glib-2.0/spice-session.h>
26 #include <spice-client-glib-2.0/spice-audio.h>
27 #include <spice-client-gtk-3.0/spice-widget.h>
28 #include <gdk/gdkkeysyms.h>
29
30 #include <vinagre/vinagre-prefs.h>
31
32 #include "vinagre-spice-tab.h"
33 #include "vinagre-spice-connection.h"
34 #include "vinagre-spice-tunnel.h"
35 #include "vinagre-vala.h"
36
37 #define VINAGRE_SPICE_TAB_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), VINAGRE_TYPE_SPICE_TAB, VinagreSpiceTabPrivate))
38
39 /* From spice-protocol.h */
40 #define SPICE_MAX_PASSWORD_LENGTH 60
41
42 typedef struct _VinagreSpiceDisplay
43 {
44 GtkWidget *display;
45 } VinagreSpiceDisplay;
46
47 struct _VinagreSpiceTabPrivate
48 {
49 SpiceSession *spice;
50 SpiceAudio *audio;
51 GtkWidget *display, *align;
52 gboolean mouse_grabbed;
53 GSList *connected_actions, *initialized_actions;
54 GtkWidget *viewonly_button, *scaling_button;
55 GtkAction *scaling_action, *viewonly_action, *original_size_action, *resize_guest_action, *auto_clipboard_action;
56 gulong signal_align;
57 VinagreSpiceDisplay *wins[4]; /* TODO: handle multi-display */
58 };
59
60 G_DEFINE_TYPE (VinagreSpiceTab, vinagre_spice_tab, VINAGRE_TYPE_TAB)
61
62 /* Properties */
63 enum
64 {
65 PROP_0,
66 };
67
68 static void open_spice (VinagreSpiceTab *spice_tab);
69 static void setup_toolbar (VinagreSpiceTab *spice_tab);
70
71 static void
vinagre_spice_tab_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)72 vinagre_spice_tab_get_property (GObject *object,
73 guint prop_id,
74 GValue *value,
75 GParamSpec *pspec)
76 {
77 switch (prop_id)
78 {
79 default:
80 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
81 break;
82 }
83 }
84
85 static void
view_scaling_cb(GtkAction * action,VinagreSpiceTab * spice_tab)86 view_scaling_cb (GtkAction *action, VinagreSpiceTab *spice_tab)
87 {
88 vinagre_spice_tab_set_scaling (spice_tab,
89 gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
90 }
91
92 static void
view_resize_guest_cb(GtkAction * action,VinagreSpiceTab * spice_tab)93 view_resize_guest_cb (GtkAction *action, VinagreSpiceTab *spice_tab)
94 {
95 vinagre_spice_tab_set_resize_guest (spice_tab,
96 gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
97 }
98
99 static void
view_auto_clipboard_cb(GtkAction * action,VinagreSpiceTab * spice_tab)100 view_auto_clipboard_cb (GtkAction *action, VinagreSpiceTab *spice_tab)
101 {
102 vinagre_spice_tab_set_auto_clipboard (spice_tab,
103 gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
104 }
105
106 static void
view_viewonly_cb(GtkAction * action,VinagreSpiceTab * spice_tab)107 view_viewonly_cb (GtkAction *action, VinagreSpiceTab *spice_tab)
108 {
109 vinagre_spice_tab_set_viewonly (spice_tab,
110 gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
111 }
112
113 static void
send_ctrlaltdel_cb(GtkAction * action,VinagreSpiceTab * spice_tab)114 send_ctrlaltdel_cb (GtkAction *action, VinagreSpiceTab *spice_tab)
115 {
116 vinagre_spice_tab_send_ctrlaltdel (spice_tab);
117 }
118
119 const static GSList *
spice_get_connected_actions(VinagreTab * tab)120 spice_get_connected_actions (VinagreTab *tab)
121 {
122 VinagreSpiceTab *spice_tab = VINAGRE_SPICE_TAB (tab);
123
124 return spice_tab->priv->connected_actions;
125 }
126
127 const static GSList *
spice_get_initialized_actions(VinagreTab * tab)128 spice_get_initialized_actions (VinagreTab *tab)
129 {
130 VinagreSpiceTab *spice_tab = VINAGRE_SPICE_TAB (tab);
131
132 return spice_tab->priv->initialized_actions;
133 }
134
135 static gchar *
spice_tab_get_tooltip(VinagreTab * tab)136 spice_tab_get_tooltip (VinagreTab *tab)
137 {
138 VinagreConnection *conn = vinagre_tab_get_conn (tab);
139
140 return g_markup_printf_escaped ("<b>%s</b> %s\n\n"
141 "<b>%s</b> %d\n",
142 _("Host:"), vinagre_connection_get_host (conn),
143 _("Port:"), vinagre_connection_get_port (conn));
144 }
145
146 static void
vinagre_spice_tab_finalize(GObject * object)147 vinagre_spice_tab_finalize (GObject *object)
148 {
149 G_OBJECT_CLASS (vinagre_spice_tab_parent_class)->finalize (object);
150 }
151
152 static void
vinagre_spice_tab_dispose(GObject * object)153 vinagre_spice_tab_dispose (GObject *object)
154 {
155 VinagreSpiceTab *spice_tab = VINAGRE_SPICE_TAB (object);
156
157 if (spice_tab->priv->connected_actions)
158 {
159 vinagre_tab_free_actions (spice_tab->priv->connected_actions);
160 spice_tab->priv->connected_actions = NULL;
161 }
162
163 if (spice_tab->priv->initialized_actions)
164 {
165 vinagre_tab_free_actions (spice_tab->priv->initialized_actions);
166 spice_tab->priv->initialized_actions = NULL;
167 }
168
169 if (spice_tab->priv->audio)
170 {
171 g_object_unref (spice_tab->priv->audio);
172 spice_tab->priv->audio = NULL;
173 }
174
175 if (spice_tab->priv->spice)
176 {
177 spice_session_disconnect (spice_tab->priv->spice);
178 g_object_unref (spice_tab->priv->spice);
179 spice_tab->priv->spice = NULL;
180 }
181
182 G_OBJECT_CLASS (vinagre_spice_tab_parent_class)->dispose (object);
183 }
184
185 static void
vinagre_spice_tab_constructed(GObject * object)186 vinagre_spice_tab_constructed (GObject *object)
187 {
188 VinagreSpiceTab *spice_tab = VINAGRE_SPICE_TAB (object);
189
190 if (G_OBJECT_CLASS (vinagre_spice_tab_parent_class)->constructed)
191 G_OBJECT_CLASS (vinagre_spice_tab_parent_class)->constructed (object);
192
193 setup_toolbar (spice_tab);
194 open_spice (spice_tab);
195 }
196
197 static void
vinagre_spice_tab_class_init(VinagreSpiceTabClass * klass)198 vinagre_spice_tab_class_init (VinagreSpiceTabClass *klass)
199 {
200 GObjectClass *object_class = G_OBJECT_CLASS (klass);
201 VinagreTabClass* tab_class = VINAGRE_TAB_CLASS (klass);
202
203 object_class->finalize = vinagre_spice_tab_finalize;
204 object_class->dispose = vinagre_spice_tab_dispose;
205 object_class->get_property = vinagre_spice_tab_get_property;
206 object_class->constructed = vinagre_spice_tab_constructed;
207
208 tab_class->impl_get_tooltip = spice_tab_get_tooltip;
209 tab_class->impl_get_connected_actions = spice_get_connected_actions;
210 tab_class->impl_get_initialized_actions = spice_get_initialized_actions;
211
212 g_type_class_add_private (object_class, sizeof (VinagreSpiceTabPrivate));
213 }
214
215 static gboolean
idle_close(VinagreTab * tab)216 idle_close (VinagreTab *tab)
217 {
218 vinagre_notebook_close_tab (vinagre_tab_get_notebook (tab), tab);
219 return FALSE;
220 }
221
222 static void
open_spice(VinagreSpiceTab * spice_tab)223 open_spice (VinagreSpiceTab *spice_tab)
224 {
225 gchar *host, *port_str, *ssh_tunnel_host;
226 gint port, fd;
227 gboolean scaling, success;
228 GError *error;
229 SpiceSession *spice = spice_tab->priv->spice;
230 VinagreTab *tab = VINAGRE_TAB (spice_tab);
231 GtkWindow *window = GTK_WINDOW (vinagre_tab_get_window (tab));
232 VinagreConnection *conn = vinagre_tab_get_conn (tab);
233
234 success = TRUE;
235 error = NULL;
236
237 g_object_get (conn,
238 "port", &port,
239 "host", &host,
240 "scaling", &scaling,
241 "fd", &fd,
242 "ssh-tunnel-host", &ssh_tunnel_host,
243 NULL);
244
245 port_str = g_strdup_printf ("%d", port);
246
247 if (fd > 0)
248 success = spice_session_open_fd (spice, fd);
249 else
250 {
251 if (ssh_tunnel_host && *ssh_tunnel_host)
252 if (!vinagre_spice_tunnel_create (window, &host, &port_str, ssh_tunnel_host, &error))
253 {
254 success = FALSE;
255 vinagre_utils_show_error_dialog (_("Error creating the SSH tunnel"),
256 error ? error->message : _("Unknown reason"),
257 window);
258 goto out;
259 }
260 g_object_set (spice, "host", host, "port", port_str,
261 "password", vinagre_connection_get_password (conn),
262 NULL);
263 success = spice_session_connect (spice);
264 }
265
266 if (!success)
267 vinagre_utils_show_error_dialog (_("Error connecting to host."),
268 error ? error->message : _("Unknown reason"),
269 window);
270
271 out:
272 g_free (port_str);
273 g_free (host);
274 g_free (ssh_tunnel_host);
275 g_clear_error (&error);
276
277 if (!success)
278 g_idle_add ((GSourceFunc)idle_close, spice_tab);
279 }
280
281 static void
spice_main_channel_event_cb(SpiceChannel * channel,SpiceChannelEvent event,VinagreSpiceTab * spice_tab)282 spice_main_channel_event_cb(SpiceChannel *channel, SpiceChannelEvent event,
283 VinagreSpiceTab *spice_tab)
284 {
285 switch (event) {
286 case SPICE_CHANNEL_OPENED:
287 g_signal_emit_by_name (G_OBJECT (spice_tab), "tab-connected");
288 break;
289 case SPICE_CHANNEL_CLOSED:
290 g_signal_emit_by_name (G_OBJECT (spice_tab), "tab-disconnected");
291 break;
292 case SPICE_CHANNEL_ERROR_AUTH: {
293 VinagreTab *tab = VINAGRE_TAB (spice_tab);
294 VinagreConnection *conn = vinagre_tab_get_conn (tab);
295 gchar *name = vinagre_connection_get_best_name (conn), *password = NULL;
296 GtkWindow *window = GTK_WINDOW (vinagre_tab_get_window (tab));
297 gboolean save_in_keyring = FALSE;
298
299 vinagre_tab_remove_credentials_from_keyring (VINAGRE_TAB (tab));
300 if (!vinagre_utils_request_credential (window,
301 "SPICE",
302 name,
303 NULL,
304 NULL,
305 FALSE,
306 FALSE,
307 TRUE,
308 SPICE_MAX_PASSWORD_LENGTH,
309 NULL,
310 NULL,
311 &password,
312 &save_in_keyring))
313 {
314 vinagre_tab_remove_from_notebook (tab);
315 goto out;
316 }
317 vinagre_connection_set_password (conn, password);
318 vinagre_tab_set_save_credentials (tab, save_in_keyring);
319
320 g_object_set (spice_tab->priv->spice, "password", password, NULL);
321 spice_session_connect (spice_tab->priv->spice);
322
323 out:
324 g_free (password);
325 break;
326 }
327 case SPICE_CHANNEL_ERROR_IO:
328 case SPICE_CHANNEL_ERROR_TLS:
329 case SPICE_CHANNEL_ERROR_LINK:
330 case SPICE_CHANNEL_ERROR_CONNECT:
331 g_signal_emit_by_name (G_OBJECT (spice_tab), "tab-disconnected");
332 break;
333 default:
334 g_warning("unhandled main channel event: %d", event);
335 break;
336 }
337 }
338
339 static void
spice_mouse_grab_cb(GtkWidget * widget,gint grabbed,VinagreSpiceTab * spice_tab)340 spice_mouse_grab_cb(GtkWidget *widget, gint grabbed, VinagreSpiceTab *spice_tab)
341 {
342 spice_tab->priv->mouse_grabbed = grabbed;
343 }
344
345 static VinagreSpiceDisplay *
create_spice_display(VinagreSpiceTab * spice_tab,int id)346 create_spice_display (VinagreSpiceTab *spice_tab, int id)
347 {
348 VinagreSpiceDisplay *d;
349 GtkLabel *label;
350 gchar *name;
351 gboolean scaling, resize_guest, view_only, fullscreen, auto_clipboard;
352 VinagreTab *tab = VINAGRE_TAB (spice_tab);
353 VinagreConnection *conn = vinagre_tab_get_conn (tab);
354
355 d = g_new0(VinagreSpiceDisplay, 1);
356
357 /* Create the display widget */
358 spice_tab->priv->display = d->display = GTK_WIDGET (spice_display_new (spice_tab->priv->spice, id));
359
360 vinagre_tab_add_view (tab, d->display);
361 vinagre_tab_set_has_screenshot (tab, TRUE);
362
363 g_signal_connect (spice_tab->priv->display, "mouse-grab",
364 G_CALLBACK (spice_mouse_grab_cb), spice_tab);
365
366 g_object_get (conn,
367 "fullscreen", &fullscreen,
368 "resize-guest", &resize_guest,
369 "auto-clipboard", &auto_clipboard,
370 "scaling", &scaling,
371 "view-only", &view_only,
372 NULL);
373
374 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (spice_tab->priv->scaling_action), scaling);
375 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (spice_tab->priv->resize_guest_action), resize_guest);
376 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (spice_tab->priv->auto_clipboard_action), auto_clipboard);
377 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (spice_tab->priv->viewonly_action), view_only);
378 g_object_set (d->display,
379 "grab-keyboard", TRUE,
380 "grab-mouse", TRUE,
381 "resize-guest", resize_guest,
382 "auto-clipboard", auto_clipboard,
383 "scaling", scaling,
384 NULL);
385 /* TODO: add view-only here when spice-gtk ready */
386
387 g_object_get (spice_tab->priv->spice, "uri", &name, NULL); /* TODO: a better friendly name? */
388 vinagre_spice_connection_set_desktop_name (VINAGRE_SPICE_CONNECTION (conn), name);
389 g_free (name);
390
391 name = vinagre_connection_get_best_name (conn);
392 label = g_object_get_data (G_OBJECT (tab), "label");
393 g_return_val_if_fail (label != NULL, d);
394 gtk_label_set_label (label, name);
395 g_free (name);
396
397 vinagre_tab_save_credentials_in_keyring (tab);
398 vinagre_tab_add_recent_used (tab);
399 vinagre_tab_set_state (tab, VINAGRE_TAB_STATE_CONNECTED);
400
401 g_signal_emit_by_name (G_OBJECT (tab), "tab-initialized");
402
403 gtk_widget_show_all (GTK_WIDGET (tab));
404
405 return d;
406 }
407
408 static void
destroy_spice_display(VinagreSpiceTab * tab,VinagreSpiceDisplay * display)409 destroy_spice_display (VinagreSpiceTab *tab, VinagreSpiceDisplay *display)
410 {
411 }
412
413 static void
spice_channel_new_cb(SpiceSession * s,SpiceChannel * channel,VinagreSpiceTab * tab)414 spice_channel_new_cb (SpiceSession *s, SpiceChannel *channel, VinagreSpiceTab *tab)
415 {
416 int id;
417
418 g_object_get (channel, "channel-id", &id, NULL);
419 g_object_ref (tab);
420
421 if (SPICE_IS_MAIN_CHANNEL (channel)) {
422 g_signal_connect (channel, "channel-event",
423 G_CALLBACK (spice_main_channel_event_cb), tab);
424 }
425
426 if (SPICE_IS_DISPLAY_CHANNEL (channel)) {
427 if (id >= G_N_ELEMENTS (tab->priv->wins))
428 return;
429 if (tab->priv->wins[id] != NULL)
430 return;
431 tab->priv->wins[id] = create_spice_display (tab, id);
432 }
433
434 if (SPICE_IS_PLAYBACK_CHANNEL (channel) ||
435 SPICE_IS_RECORD_CHANNEL (channel)) {
436 if (tab->priv->audio != NULL)
437 return;
438 tab->priv->audio = spice_audio_new (s, NULL, NULL);
439 }
440 }
441
442 static void
spice_channel_destroy_cb(SpiceSession * s,SpiceChannel * channel,VinagreSpiceTab * tab)443 spice_channel_destroy_cb (SpiceSession *s, SpiceChannel *channel, VinagreSpiceTab *tab)
444 {
445 int id;
446
447 g_object_get (channel, "channel-id", &id, NULL);
448 g_object_unref (tab);
449
450 if (SPICE_IS_DISPLAY_CHANNEL (channel)) {
451 if (id >= G_N_ELEMENTS (tab->priv->wins))
452 return;
453 if (tab->priv->wins[id] == NULL)
454 return;
455 destroy_spice_display (tab, tab->priv->wins[id]);
456 tab->priv->wins[id] = NULL;
457 }
458 }
459
460 static GSList *
create_connected_actions(VinagreSpiceTab * tab)461 create_connected_actions (VinagreSpiceTab *tab)
462 {
463 GSList *list = NULL;
464 VinagreTabUiAction *a;
465
466 /* View->Scaling */
467 a = g_slice_new (VinagreTabUiAction);
468 a->paths = g_new (gchar *, 3);
469 a->paths[0] = g_strdup ("/MenuBar/ViewMenu");
470 a->paths[1] = g_strdup ("/ToolBar");
471 a->paths[2] = NULL;
472 a->action = GTK_ACTION (gtk_toggle_action_new ("SPICEViewScaling",
473 _("S_caling"),
474 _("Fit the remote screen into the current window size"),
475 "zoom-fit-best"));
476 gtk_action_set_icon_name (a->action, "zoom-fit-best");
477 g_signal_connect (a->action, "activate", G_CALLBACK (view_scaling_cb), tab);
478 list = g_slist_append (list, a);
479 tab->priv->scaling_action = a->action;
480
481 /* View->Resize Guest */
482 a = g_slice_new (VinagreTabUiAction);
483 a->paths = g_new (gchar *, 2);
484 a->paths[0] = g_strdup ("/MenuBar/ViewMenu");
485 a->paths[1] = NULL;
486 a->action = GTK_ACTION (gtk_toggle_action_new ("SPICEViewResizeGuest",
487 _("_Resize guest"),
488 _("Resize the screen guest to best fit"),
489 NULL));
490 g_signal_connect (a->action, "activate", G_CALLBACK (view_resize_guest_cb), tab);
491 list = g_slist_append (list, a);
492 tab->priv->resize_guest_action = a->action;
493
494 /* View->Share clipboard */
495 a = g_slice_new (VinagreTabUiAction);
496 a->paths = g_new (gchar *, 2);
497 a->paths[0] = g_strdup ("/MenuBar/ViewMenu");
498 a->paths[1] = NULL;
499 a->action = GTK_ACTION (gtk_toggle_action_new ("SPICEViewAutoClipboard",
500 _("_Share clipboard"),
501 _("Automatically share clipboard between client and guest"),
502 NULL));
503 g_signal_connect (a->action, "activate", G_CALLBACK (view_auto_clipboard_cb), tab);
504 list = g_slist_append (list, a);
505 tab->priv->auto_clipboard_action = a->action;
506
507 /* View->View Only TODO: not ready in spice-gtk yet */
508 a = g_slice_new (VinagreTabUiAction);
509 a->paths = g_new (gchar *, 3);
510 a->paths[0] = g_strdup ("/MenuBar/ViewMenu");
511 a->paths[1] = g_strdup ("/ToolBar");
512 a->paths[2] = NULL;
513 a->action = GTK_ACTION (gtk_toggle_action_new ("SPICEViewViewOnly",
514 _("_View only"),
515 _("Do not send mouse and keyboard events"),
516 "emblem-readonly"));
517 gtk_action_set_icon_name (a->action, "emblem-readonly");
518 g_signal_connect (a->action, "activate", G_CALLBACK (view_viewonly_cb), tab);
519 /* list = g_slist_append (list, a); */
520 tab->priv->viewonly_action = a->action;
521
522 return list;
523 }
524
525 static GSList *
create_initialized_actions(VinagreSpiceTab * tab)526 create_initialized_actions (VinagreSpiceTab *tab)
527 {
528 GSList *list = NULL;
529 VinagreTabUiAction *a;
530
531 /* Remote->Send CTRL-ALT-DEL */
532 a = g_slice_new (VinagreTabUiAction);
533 a->paths = g_new (gchar *, 3);
534 a->paths[0] = g_strdup ("/MenuBar/RemoteeMenu/RemoteOps_1");
535 a->paths[1] = g_strdup ("/ToolBar");
536 a->paths[2] = NULL;
537 a->action = gtk_action_new ("SPICERemoteSendCtrlAltDel",
538 _("_Send Ctrl-Alt-Del"),
539 _("Send Ctrl+Alt+Del to the remote desktop"),
540 "preferences-desktop-keyboard-shortcuts");
541 gtk_action_set_is_important (a->action, TRUE);
542 gtk_action_set_icon_name (a->action, "preferences-desktop-keyboard-shortcuts");
543 g_signal_connect (a->action, "activate", G_CALLBACK (send_ctrlaltdel_cb), tab);
544 list = g_slist_append (list, a);
545
546 return list;
547 }
548
549 static void
viewonly_button_clicked(GtkToggleToolButton * button,VinagreSpiceTab * spice_tab)550 viewonly_button_clicked (GtkToggleToolButton *button,
551 VinagreSpiceTab *spice_tab)
552 {
553 vinagre_spice_tab_set_viewonly (spice_tab, gtk_toggle_tool_button_get_active (button));
554
555 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (spice_tab->priv->viewonly_action),
556 vinagre_spice_tab_get_viewonly (spice_tab));
557 }
558
559 static void
scaling_button_clicked(GtkToggleToolButton * button,VinagreSpiceTab * spice_tab)560 scaling_button_clicked (GtkToggleToolButton *button,
561 VinagreSpiceTab *spice_tab)
562 {
563 if (!vinagre_spice_tab_set_scaling (spice_tab, gtk_toggle_tool_button_get_active (button)))
564 gtk_toggle_tool_button_set_active (button, FALSE);
565
566 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (spice_tab->priv->scaling_action),
567 vinagre_spice_tab_get_scaling (spice_tab));
568 }
569
570 static void
cad_button_clicked(GtkToolButton * button,VinagreSpiceTab * spice_tab)571 cad_button_clicked (GtkToolButton *button,
572 VinagreSpiceTab *spice_tab)
573 {
574 vinagre_spice_tab_send_ctrlaltdel (spice_tab);
575 }
576
577 static void
setup_toolbar(VinagreSpiceTab * spice_tab)578 setup_toolbar (VinagreSpiceTab *spice_tab)
579 {
580 GtkWidget *toolbar = vinagre_tab_get_toolbar (VINAGRE_TAB (spice_tab));
581 GtkWidget *button;
582
583 /* Space */
584 button = GTK_WIDGET (gtk_separator_tool_item_new ());
585 gtk_tool_item_set_expand (GTK_TOOL_ITEM (button), TRUE);
586 gtk_widget_show (GTK_WIDGET (button));
587 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (button), -1);
588
589 /* Scaling */
590 button = GTK_WIDGET (gtk_toggle_tool_button_new ());
591 gtk_tool_button_set_label (GTK_TOOL_BUTTON (button), _("Scaling"));
592 gtk_tool_item_set_tooltip_text (GTK_TOOL_ITEM (button), _("Scaling"));
593 gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (button), "zoom-fit-best");
594 gtk_widget_show (GTK_WIDGET (button));
595 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (button), -1);
596 g_signal_connect (button, "toggled", G_CALLBACK (scaling_button_clicked), spice_tab);
597 spice_tab->priv->scaling_button = button;
598
599 /* Read only */
600 button = GTK_WIDGET (gtk_toggle_tool_button_new ());
601 gtk_tool_button_set_label (GTK_TOOL_BUTTON (button), _("Read only"));
602 gtk_tool_item_set_tooltip_text (GTK_TOOL_ITEM (button), _("Read only"));
603 gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (button), "emblem-readonly");
604 gtk_widget_show (GTK_WIDGET (button));
605 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (button), -1);
606 g_signal_connect (button, "toggled", G_CALLBACK (viewonly_button_clicked), spice_tab);
607 spice_tab->priv->viewonly_button = button;
608
609 /* Send Ctrl-alt-del */
610 button = GTK_WIDGET (gtk_tool_button_new (NULL, _("Send Ctrl-Alt-Del")));
611 gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (button), "preferences-desktop-keyboard-shortcuts");
612 gtk_tool_item_set_tooltip_text (GTK_TOOL_ITEM (button), _("Send Ctrl+Alt+Del to the remote desktop"));
613 g_signal_connect (button, "clicked", G_CALLBACK (cad_button_clicked), spice_tab);
614 gtk_widget_show (GTK_WIDGET (button));
615 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (button), -1);
616 }
617
618 static void
vinagre_spice_tab_init(VinagreSpiceTab * spice_tab)619 vinagre_spice_tab_init (VinagreSpiceTab *spice_tab)
620 {
621 SpiceSession *spice;
622
623 spice_tab->priv = VINAGRE_SPICE_TAB_GET_PRIVATE (spice_tab);
624 spice_tab->priv->connected_actions = create_connected_actions (spice_tab);
625 spice_tab->priv->initialized_actions = create_initialized_actions (spice_tab);
626
627 /* Create the spice widget */
628 spice = spice_tab->priv->spice = spice_session_new ();
629 g_signal_connect(spice, "channel-new",
630 G_CALLBACK(spice_channel_new_cb), spice_tab);
631 g_signal_connect(spice, "channel-destroy",
632 G_CALLBACK(spice_channel_destroy_cb), spice_tab);
633 }
634
635 GtkWidget *
vinagre_spice_tab_new(VinagreConnection * conn,VinagreWindow * window)636 vinagre_spice_tab_new (VinagreConnection *conn,
637 VinagreWindow *window)
638 {
639 return GTK_WIDGET (g_object_new (VINAGRE_TYPE_SPICE_TAB,
640 "conn", conn,
641 "window", window,
642 NULL));
643 }
644
645 void
vinagre_spice_tab_send_ctrlaltdel(VinagreSpiceTab * tab)646 vinagre_spice_tab_send_ctrlaltdel (VinagreSpiceTab *tab)
647 {
648 guint keys[] = { GDK_KEY_Control_L, GDK_KEY_Alt_L, GDK_KEY_Delete };
649
650 g_return_if_fail (VINAGRE_IS_SPICE_TAB (tab));
651
652 spice_display_send_keys (SPICE_DISPLAY (tab->priv->display), keys, sizeof (keys) / sizeof (keys[0]), SPICE_DISPLAY_KEY_EVENT_CLICK);
653 }
654
655 gboolean
vinagre_spice_tab_set_scaling(VinagreSpiceTab * tab,gboolean active)656 vinagre_spice_tab_set_scaling (VinagreSpiceTab *tab, gboolean active)
657 {
658 gboolean scaling;
659
660 g_return_val_if_fail (VINAGRE_IS_SPICE_TAB (tab), FALSE);
661
662 g_object_get (tab->priv->display, "scaling", &scaling, NULL);
663 if (scaling == active)
664 return TRUE;
665
666 g_object_set (tab->priv->display, "scaling", active, NULL);
667
668 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (tab->priv->scaling_button),
669 active);
670
671 return TRUE;
672 }
673
674 gboolean
vinagre_spice_tab_get_scaling(VinagreSpiceTab * tab)675 vinagre_spice_tab_get_scaling (VinagreSpiceTab *tab)
676 {
677 gboolean scaling;
678
679 g_return_val_if_fail (VINAGRE_IS_SPICE_TAB (tab), FALSE);
680
681 g_object_get (tab->priv->display, "scaling", &scaling, NULL);
682
683 return scaling;
684 }
685
686 void
vinagre_spice_tab_set_viewonly(VinagreSpiceTab * tab,gboolean active)687 vinagre_spice_tab_set_viewonly (VinagreSpiceTab *tab, gboolean active)
688 {
689 g_return_if_fail (VINAGRE_IS_SPICE_TAB (tab));
690
691 #if 0
692 g_object_set (tab->priv->display, "read-only", active, NULL); /* only in future version of spice-gtk */
693 #endif
694
695 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (tab->priv->viewonly_button),
696 active);
697 }
698
699 gboolean
vinagre_spice_tab_get_viewonly(VinagreSpiceTab * tab)700 vinagre_spice_tab_get_viewonly (VinagreSpiceTab *tab)
701 {
702 gboolean active = FALSE;
703
704 g_return_val_if_fail (VINAGRE_IS_SPICE_TAB (tab), FALSE);
705 #if 0
706 g_object_get (tab->priv->display, "read-only", &active, NULL); /* only in future version of spice-gtk */
707 #endif
708 return active;
709 }
710
711 gboolean
vinagre_spice_tab_set_resize_guest(VinagreSpiceTab * tab,gboolean active)712 vinagre_spice_tab_set_resize_guest (VinagreSpiceTab *tab, gboolean active)
713 {
714 g_return_val_if_fail (VINAGRE_IS_SPICE_TAB (tab), FALSE);
715
716 g_object_set (tab->priv->display, "resize-guest", active, NULL);
717
718 return TRUE;
719 }
720
721 gboolean
vinagre_spice_tab_get_resize_guest(VinagreSpiceTab * tab)722 vinagre_spice_tab_get_resize_guest (VinagreSpiceTab *tab)
723 {
724 gboolean active;
725
726 g_return_val_if_fail (VINAGRE_IS_SPICE_TAB (tab), FALSE);
727
728 g_object_get (tab->priv->display, "resize-guest", &active, NULL);
729
730 return active;
731 }
732
733 void
vinagre_spice_tab_set_auto_clipboard(VinagreSpiceTab * tab,gboolean active)734 vinagre_spice_tab_set_auto_clipboard (VinagreSpiceTab *tab, gboolean active)
735 {
736 g_return_if_fail (VINAGRE_IS_SPICE_TAB (tab));
737
738 g_object_set (tab->priv->display, "auto-clipboard", active, NULL);
739 }
740
741 gboolean
vinagre_spice_tab_get_auto_clipboard(VinagreSpiceTab * tab)742 vinagre_spice_tab_get_auto_clipboard (VinagreSpiceTab *tab)
743 {
744 gboolean active;
745
746 g_return_val_if_fail (VINAGRE_IS_SPICE_TAB (tab), FALSE);
747
748 g_object_get (tab->priv->display, "auto-clipboard", &active, NULL);
749
750 return active;
751 }
752
753 gboolean
vinagre_spice_tab_is_mouse_grab(VinagreSpiceTab * tab)754 vinagre_spice_tab_is_mouse_grab (VinagreSpiceTab *tab)
755 {
756 g_return_val_if_fail (VINAGRE_IS_SPICE_TAB (tab), FALSE);
757
758 return tab->priv->mouse_grabbed;
759 }
760
761 /* vim: set ts=8: */
762