~ubuntu-branches/debian/squeeze/virt-viewer/squeeze

« back to all changes in this revision

Viewing changes to src/viewer.c

  • Committer: Bazaar Package Importer
  • Author(s): Guido Günther, Guido Günther, Laurent Léonard
  • Date: 2010-02-02 21:27:29 UTC
  • mfrom: (1.2.1 upstream) (2.1.3 experimental)
  • Revision ID: james.westby@ubuntu.com-20100202212729-8jp220b4xzqel70t
Tags: 0.2.1-1
[ Guido Günther ]
* [c2945b4] bump standards version
* [8ca5ea3] exclude plugins from dh_makeshlibs

[ Laurent Léonard ]
* [39043d7] Remove libtool issue fix.
* [a460a21] Revert "Remove libtool issue fix." This reverts commit
  39043d7460220d7aeecb30b7b489e9064b591440.
* [9ab8056] Bump Debhelper version to 7.
* [366e279] Bump Standards-Version to 3.8.3.
* [0a4e524] Clean debian/rules.
* [e662ab7] Remove debian/docs.
* [409e5bf] Update clean target in debian/rules.
* [a57c8ba] Allow DM upload and add myself as uploader.
* [2f26f4f] Clean debian/watch.

[ Guido Günther ]
* [6806613] Imported Upstream version 0.2.1
* [b956a6e] Build-dep on intltool
* [b6fa2e2] Use xulrunner-dev instead of iceape-dev
* [431a46e] New patch 0001-Fix-build-with-xulrunner-1.9.1.patch: Fix build
  with xulrunner 1.9.1
* [c3381c5] New patch 0002-Define-GLADE_DIR.patch: Define GLADE_DIR
* [720c999] Depend on virt-viewer
* [94828c7] New patch 0003-Add-some-docs.patch: Adds some docs
* [8aaf986] Add example web page
* [ae04386] Warn about security implications

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Virt Viewer: A virtual machine console viewer
 
3
 *
 
4
 * Copyright (C) 2007-2009 Red Hat,
 
5
 * Copyright (C) 2009 Daniel P. Berrange
 
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
20
 *
 
21
 * Author: Daniel P. Berrange <berrange@redhat.com>
 
22
 */
 
23
 
 
24
#include <config.h>
 
25
 
 
26
#include <vncdisplay.h>
 
27
#include <gdk/gdkkeysyms.h>
 
28
#include <sys/types.h>
 
29
#include <sys/stat.h>
 
30
#include <stdlib.h>
 
31
#include <string.h>
 
32
#include <unistd.h>
 
33
#include <locale.h>
 
34
#include <glib/gi18n.h>
 
35
#include <libvirt/libvirt.h>
 
36
#include <libvirt/virterror.h>
 
37
#include <libxml/xpath.h>
 
38
#include <libxml/uri.h>
 
39
 
 
40
#ifdef HAVE_SYS_SOCKET_H
 
41
#include <sys/socket.h>
 
42
#endif
 
43
 
 
44
#ifdef HAVE_SYS_UN_H
 
45
#include <sys/un.h>
 
46
#endif
 
47
 
 
48
#ifdef HAVE_WINDOWS_H
 
49
#include <windows.h>
 
50
#endif
 
51
 
 
52
#include "viewer.h"
 
53
#include "events.h"
 
54
#include "auth.h"
 
55
 
 
56
gboolean doDebug = FALSE;
 
57
 
 
58
enum menuNums {
 
59
        FILE_MENU,
 
60
        VIEW_MENU,
 
61
        SEND_KEY_MENU,
 
62
        HELP_MENU,
 
63
        LAST_MENU // sentinel
 
64
};
 
65
 
 
66
static const char * const menuNames[LAST_MENU] = {
 
67
        "menu-file", "menu-view", "menu-send", "menu-help"
 
68
};
 
69
 
 
70
 
 
71
#define MAX_KEY_COMBO 3
 
72
struct  keyComboDef {
 
73
        guint keys[MAX_KEY_COMBO];
 
74
        guint nkeys;
 
75
        const char *label;
 
76
};
 
77
 
 
78
static const struct keyComboDef keyCombos[] = {
 
79
        { { GDK_Control_L, GDK_Alt_L, GDK_Delete }, 3, "Ctrl+Alt+_Del"},
 
80
        { { GDK_Control_L, GDK_Alt_L, GDK_BackSpace }, 3, "Ctrl+Alt+_Backspace"},
 
81
        { {}, 0, "" },
 
82
        { { GDK_Control_L, GDK_Alt_L, GDK_F1 }, 3, "Ctrl+Alt+F_1"},
 
83
        { { GDK_Control_L, GDK_Alt_L, GDK_F2 }, 3, "Ctrl+Alt+F_2"},
 
84
        { { GDK_Control_L, GDK_Alt_L, GDK_F3 }, 3, "Ctrl+Alt+F_3"},
 
85
        { { GDK_Control_L, GDK_Alt_L, GDK_F4 }, 3, "Ctrl+Alt+F_4"},
 
86
        { { GDK_Control_L, GDK_Alt_L, GDK_F5 }, 3, "Ctrl+Alt+F_5"},
 
87
        { { GDK_Control_L, GDK_Alt_L, GDK_F6 }, 3, "Ctrl+Alt+F_6"},
 
88
        { { GDK_Control_L, GDK_Alt_L, GDK_F7 }, 3, "Ctrl+Alt+F_7"},
 
89
        { { GDK_Control_L, GDK_Alt_L, GDK_F8 }, 3, "Ctrl+Alt+F_8"},
 
90
        { { GDK_Control_L, GDK_Alt_L, GDK_F5 }, 3, "Ctrl+Alt+F_9"},
 
91
        { { GDK_Control_L, GDK_Alt_L, GDK_F6 }, 3, "Ctrl+Alt+F1_0"},
 
92
        { { GDK_Control_L, GDK_Alt_L, GDK_F7 }, 3, "Ctrl+Alt+F11"},
 
93
        { { GDK_Control_L, GDK_Alt_L, GDK_F8 }, 3, "Ctrl+Alt+F12"},
 
94
        { {}, 0, "" },
 
95
        { { GDK_Print }, 1, "_PrintScreen"},
 
96
};
 
97
 
 
98
 
 
99
 
 
100
typedef struct VirtViewer {
 
101
        char *uri;
 
102
        virConnectPtr conn;
 
103
        char *domkey;
 
104
        char *domtitle;
 
105
 
 
106
        GladeXML *glade;
 
107
        GtkWidget *window;
 
108
        GtkWidget *container;
 
109
        GtkWidget *vnc;
 
110
 
 
111
        int desktopWidth;
 
112
        int desktopHeight;
 
113
        gboolean autoResize;
 
114
        gboolean fullscreen;
 
115
        gboolean withEvents;
 
116
 
 
117
        gboolean active;
 
118
        char *vncAddress;
 
119
 
 
120
        gboolean accelEnabled;
 
121
        GValue accelSetting;
 
122
        GSList *accelList;
 
123
        int accelMenuSig[LAST_MENU];
 
124
 
 
125
        gboolean waitvm;
 
126
        gboolean reconnect;
 
127
        gboolean direct;
 
128
        gboolean verbose;
 
129
        gboolean authretry;
 
130
        gboolean connected;
 
131
 
 
132
        gchar *clipboard;
 
133
} VirtViewer;
 
134
 
 
135
typedef struct VirtViewerSize {
 
136
        VirtViewer *viewer;
 
137
        gint width, height;
 
138
        gulong sig_id;
 
139
} VirtViewerSize;
 
140
 
 
141
 
 
142
static gboolean viewer_connect_timer(void *opaque);
 
143
static int viewer_initial_connect(VirtViewer *viewer);
 
144
 
 
145
 
 
146
static void viewer_simple_message_dialog(GtkWidget *window, const char *fmt, ...)
 
147
{
 
148
        GtkWidget *dialog;
 
149
        char *msg;
 
150
        va_list vargs;
 
151
 
 
152
        va_start(vargs, fmt);
 
153
 
 
154
        msg = g_strdup_vprintf(fmt, vargs);
 
155
 
 
156
        va_end(vargs);
 
157
 
 
158
        dialog = gtk_message_dialog_new(GTK_WINDOW(window),
 
159
                                        GTK_DIALOG_MODAL |
 
160
                                        GTK_DIALOG_DESTROY_WITH_PARENT,
 
161
                                        GTK_MESSAGE_ERROR,
 
162
                                        GTK_BUTTONS_OK,
 
163
                                        "%s",
 
164
                                        msg);
 
165
 
 
166
        gtk_dialog_run(GTK_DIALOG(dialog));
 
167
 
 
168
        gtk_widget_destroy(dialog);
 
169
 
 
170
        g_free(msg);
 
171
}
 
172
 
 
173
 
 
174
/* Now that the size is set to our preferred sizing, this
 
175
 * triggers another resize calculation but without our
 
176
 * scrolled window callback active. This is the key that
 
177
 * allows us to set the fixed size, but then allow the user
 
178
 * to later resize it smaller again
 
179
 */
 
180
static gboolean
 
181
viewer_unset_widget_size_cb(gpointer data)
 
182
{
 
183
        GtkWidget *widget = data;
 
184
        DEBUG_LOG("Unset requisition on widget=%p", widget);
 
185
 
 
186
        gtk_widget_queue_resize_no_redraw (widget);
 
187
 
 
188
        return FALSE;
 
189
}
 
190
 
 
191
/*
 
192
 * This sets the actual size of the widget, and then
 
193
 * sets an idle callback to resize again, without constraints
 
194
 * activated
 
195
 */
 
196
static gboolean
 
197
viewer_set_widget_size_cb(GtkWidget *widget,
 
198
                          GtkRequisition *req,
 
199
                          gpointer data)
 
200
{
 
201
        VirtViewerSize *size = data;
 
202
        DEBUG_LOG("Set requisition on widget=%p to %dx%d", widget, size->width, size->height);
 
203
 
 
204
        req->width = size->width;
 
205
        req->height = size->height;
 
206
 
 
207
        g_signal_handler_disconnect (widget, size->sig_id);
 
208
        g_free (size);
 
209
        g_idle_add (viewer_unset_widget_size_cb, widget);
 
210
 
 
211
        return FALSE;
 
212
}
 
213
 
 
214
 
 
215
/*
 
216
 * Registers a callback used to set the widget size once
 
217
 */
 
218
static void
 
219
viewer_set_widget_size(VirtViewer *viewer,
 
220
                       GtkWidget *widget,
 
221
                       int width,
 
222
                       int height)
 
223
{
 
224
        VirtViewerSize *size = g_new (VirtViewerSize, 1);
 
225
        DEBUG_LOG("Queue resize widget=%p width=%d height=%d", widget, width, height);
 
226
        size->viewer = viewer;
 
227
        size->width = width;
 
228
        size->height = height;
 
229
        size->sig_id = g_signal_connect
 
230
                (widget, "size-request",
 
231
                 G_CALLBACK (viewer_set_widget_size_cb),
 
232
                 size);
 
233
 
 
234
        gtk_widget_queue_resize (widget);
 
235
}
 
236
 
 
237
 
 
238
/*
 
239
 * Called when the main container widget's size has been set.
 
240
 * It attempts to fit the VNC widget into this space while
 
241
 * maintaining aspect ratio
 
242
 */
 
243
static gboolean viewer_resize_align(GtkWidget *widget,
 
244
                                    GtkAllocation *alloc,
 
245
                                    VirtViewer *viewer)
 
246
{
 
247
        double desktopAspect = (double)viewer->desktopWidth / (double)viewer->desktopHeight;
 
248
        double scrollAspect = (double)alloc->width / (double)alloc->height;
 
249
        int height, width;
 
250
        GtkAllocation child;
 
251
        int dx = 0, dy = 0;
 
252
 
 
253
        if (!viewer->active) {
 
254
                DEBUG_LOG("Skipping inactive resize");
 
255
                return TRUE;
 
256
        }
 
257
 
 
258
        if (scrollAspect > desktopAspect) {
 
259
                width = alloc->height * desktopAspect;
 
260
                dx = (alloc->width - width) / 2;
 
261
                height = alloc->height;
 
262
        } else {
 
263
                width = alloc->width;
 
264
                height = alloc->width / desktopAspect;
 
265
                dy = (alloc->height - height) / 2;
 
266
        }
 
267
 
 
268
        DEBUG_LOG("Align widget=%p is %dx%d, desktop is %dx%d, setting VNC to %dx%d",
 
269
                  widget,
 
270
                  alloc->width, alloc->height,
 
271
                  viewer->desktopWidth, viewer->desktopHeight,
 
272
                  width, height);
 
273
 
 
274
        child.x = alloc->x + dx;
 
275
        child.y = alloc->y + dy;
 
276
        child.width = width;
 
277
        child.height = height;
 
278
        gtk_widget_size_allocate(viewer->vnc, &child);
 
279
 
 
280
        return FALSE;
 
281
}
 
282
 
 
283
 
 
284
/*
 
285
 * Triggers a resize of the main container to indirectly cause
 
286
 * the VNC widget to be resized to fit the available space
 
287
 */
 
288
static void
 
289
viewer_resize_vnc_widget(VirtViewer *viewer)
 
290
{
 
291
        GtkWidget *align;
 
292
        align = glade_xml_get_widget(viewer->glade, "vnc-align");
 
293
        gtk_widget_queue_resize(align);
 
294
}
 
295
 
 
296
 
 
297
/*
 
298
 * This code attempts to resize the top level window to be large enough
 
299
 * to contain the entire VNC desktop at 1:1 ratio. If the local desktop
 
300
 * isn't large enough that it goes as large as possible and lets VNC
 
301
 * scale down to fit, maintaining aspect ratio
 
302
 */
 
303
static void
 
304
viewer_resize_main_window(VirtViewer *viewer)
 
305
{
 
306
        GdkRectangle fullscreen;
 
307
        GdkScreen *screen;
 
308
        int width, height;
 
309
        double desktopAspect;
 
310
        double screenAspect;
 
311
 
 
312
        DEBUG_LOG("Preparing main window resize");
 
313
        if (!viewer->active) {
 
314
                DEBUG_LOG("Skipping inactive resize");
 
315
                return;
 
316
        }
 
317
 
 
318
        gtk_window_resize(GTK_WINDOW (viewer->window), 1, 1);
 
319
 
 
320
        screen = gdk_drawable_get_screen(gtk_widget_get_window(viewer->window));
 
321
        gdk_screen_get_monitor_geometry(screen,
 
322
                                        gdk_screen_get_monitor_at_window
 
323
                                        (screen, gtk_widget_get_window(viewer->window)),
 
324
                                        &fullscreen);
 
325
 
 
326
        desktopAspect = (double)viewer->desktopWidth / (double)viewer->desktopHeight;
 
327
        screenAspect = (double)(fullscreen.width - 128) / (double)(fullscreen.height - 128);
 
328
 
 
329
        if ((viewer->desktopWidth > (fullscreen.width - 128)) ||
 
330
            (viewer->desktopHeight > (fullscreen.height - 128))) {
 
331
                /* Doesn't fit native res, so go as large as possible
 
332
                   maintaining aspect ratio */
 
333
                if (screenAspect > desktopAspect) {
 
334
                        width = viewer->desktopHeight * desktopAspect;
 
335
                        height = viewer->desktopHeight;
 
336
                } else {
 
337
                        width = viewer->desktopWidth;
 
338
                        height = viewer->desktopWidth / desktopAspect;
 
339
                }
 
340
        } else {
 
341
                width = viewer->desktopWidth;
 
342
                height = viewer->desktopHeight;
 
343
        }
 
344
 
 
345
        viewer_set_widget_size(viewer,
 
346
                               glade_xml_get_widget(viewer->glade, "vnc-align"),
 
347
                               width,
 
348
                               height);
 
349
}
 
350
 
 
351
 
 
352
/*
 
353
 * Called when VNC desktop size changes.
 
354
 *
 
355
 * It either tries to resize the main window, or it triggers
 
356
 * recalculation of VNC within existing window size
 
357
 */
 
358
static void viewer_resize_desktop(GtkWidget *vnc G_GNUC_UNUSED, gint width, gint height, VirtViewer *viewer)
 
359
{
 
360
        DEBUG_LOG("VNC desktop resize %dx%d", width, height);
 
361
        viewer->desktopWidth = width;
 
362
        viewer->desktopHeight = height;
 
363
 
 
364
        if (viewer->autoResize && viewer->window && !viewer->fullscreen) {
 
365
                viewer_resize_main_window(viewer);
 
366
        } else {
 
367
                viewer_resize_vnc_widget(viewer);
 
368
        }
 
369
}
 
370
 
 
371
 
 
372
static void viewer_set_title(VirtViewer *viewer, gboolean grabbed)
 
373
{
 
374
        char *title;
 
375
        const char *subtitle;
 
376
 
 
377
        if (!viewer->window)
 
378
                return;
 
379
 
 
380
        if (grabbed)
 
381
                subtitle = "(Press Ctrl+Alt to release pointer) ";
 
382
        else
 
383
                subtitle = "";
 
384
 
 
385
        title = g_strdup_printf("%s%s - Virt Viewer",
 
386
                                subtitle, viewer->domtitle);
 
387
 
 
388
        gtk_window_set_title(GTK_WINDOW(viewer->window), title);
 
389
 
 
390
        g_free(title);
 
391
}
 
392
 
 
393
static gboolean viewer_ignore_accel(GtkWidget *menu G_GNUC_UNUSED,
 
394
                                    VirtViewer *viewer G_GNUC_UNUSED)
 
395
{
 
396
        /* ignore accelerator */
 
397
        return TRUE;
 
398
}
 
399
 
 
400
 
 
401
static void viewer_disable_modifiers(VirtViewer *viewer)
 
402
{
 
403
        GtkSettings *settings = gtk_settings_get_default();
 
404
        GValue empty;
 
405
        GSList *accels;
 
406
        int i;
 
407
 
 
408
        if (!viewer->window)
 
409
                return;
 
410
 
 
411
        if (!viewer->accelEnabled)
 
412
                return;
 
413
 
 
414
        /* This stops F10 activating menu bar */
 
415
        memset(&empty, 0, sizeof empty);
 
416
        g_value_init(&empty, G_TYPE_STRING);
 
417
        g_object_get_property(G_OBJECT(settings), "gtk-menu-bar-accel", &viewer->accelSetting);
 
418
        g_object_set_property(G_OBJECT(settings), "gtk-menu-bar-accel", &empty);
 
419
 
 
420
        /* This stops global accelerators like Ctrl+Q == Quit */
 
421
        for (accels = viewer->accelList ; accels ; accels = accels->next) {
 
422
                gtk_window_remove_accel_group(GTK_WINDOW(viewer->window), accels->data);
 
423
        }
 
424
 
 
425
        /* This stops menu bar shortcuts like Alt+F == File */
 
426
        for (i = 0 ; i < LAST_MENU ; i++) {
 
427
                GtkWidget *menu = glade_xml_get_widget(viewer->glade, menuNames[i]);
 
428
                viewer->accelMenuSig[i] =
 
429
                        g_signal_connect(menu, "mnemonic-activate",
 
430
                                         G_CALLBACK(viewer_ignore_accel), viewer);
 
431
        }
 
432
 
 
433
        viewer->accelEnabled = FALSE;
 
434
}
 
435
 
 
436
 
 
437
static void viewer_enable_modifiers(VirtViewer *viewer)
 
438
{
 
439
        GtkSettings *settings = gtk_settings_get_default();
 
440
        GSList *accels;
 
441
        int i;
 
442
 
 
443
        if (!viewer->window)
 
444
                return;
 
445
 
 
446
        if (viewer->accelEnabled)
 
447
                return;
 
448
 
 
449
        /* This allows F10 activating menu bar */
 
450
        g_object_set_property(G_OBJECT(settings), "gtk-menu-bar-accel", &viewer->accelSetting);
 
451
 
 
452
        /* This allows global accelerators like Ctrl+Q == Quit */
 
453
        for (accels = viewer->accelList ; accels ; accels = accels->next) {
 
454
                gtk_window_add_accel_group(GTK_WINDOW(viewer->window), accels->data);
 
455
        }
 
456
 
 
457
        /* This allows menu bar shortcuts like Alt+F == File */
 
458
        for (i = 0 ; i < LAST_MENU ; i++) {
 
459
                GtkWidget *menu = glade_xml_get_widget(viewer->glade, menuNames[i]);
 
460
                g_signal_handler_disconnect(menu, viewer->accelMenuSig[i]);
 
461
        }
 
462
 
 
463
        viewer->accelEnabled = TRUE;
 
464
}
 
465
 
 
466
 
 
467
 
 
468
static void viewer_mouse_grab(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer)
 
469
{
 
470
        viewer_set_title(viewer, TRUE);
 
471
}
 
472
 
 
473
static void viewer_mouse_ungrab(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer)
 
474
{
 
475
        viewer_set_title(viewer, FALSE);
 
476
}
 
477
 
 
478
static void viewer_key_grab(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer)
 
479
{
 
480
        viewer_disable_modifiers(viewer);
 
481
}
 
482
 
 
483
static void viewer_key_ungrab(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer)
 
484
{
 
485
        viewer_enable_modifiers(viewer);
 
486
}
 
487
 
 
488
 
 
489
static void viewer_shutdown(GtkWidget *src G_GNUC_UNUSED, void *dummy G_GNUC_UNUSED, VirtViewer *viewer)
 
490
{
 
491
        vnc_display_close(VNC_DISPLAY(viewer->vnc));
 
492
        gtk_main_quit();
 
493
}
 
494
 
 
495
static void viewer_menu_file_quit(GtkWidget *src G_GNUC_UNUSED, VirtViewer *viewer)
 
496
{
 
497
        viewer_shutdown(src, NULL, viewer);
 
498
}
 
499
 
 
500
static void viewer_menu_view_fullscreen(GtkWidget *menu, VirtViewer *viewer)
 
501
{
 
502
        if (!viewer->window)
 
503
                return;
 
504
 
 
505
        if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu))) {
 
506
                viewer->fullscreen = TRUE;
 
507
                gtk_window_fullscreen(GTK_WINDOW(viewer->window));
 
508
        } else {
 
509
                viewer->fullscreen = FALSE;
 
510
                gtk_window_unfullscreen(GTK_WINDOW(viewer->window));
 
511
                if (viewer->autoResize)
 
512
                        viewer_resize_main_window(viewer);
 
513
        }
 
514
}
 
515
 
 
516
static void viewer_menu_view_resize(GtkWidget *menu, VirtViewer *viewer)
 
517
{
 
518
        if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu))) {
 
519
                viewer->autoResize = TRUE;
 
520
                if (!viewer->fullscreen)
 
521
                        viewer_resize_main_window(viewer);
 
522
        } else {
 
523
                viewer->autoResize = FALSE;
 
524
        }
 
525
}
 
526
 
 
527
static void viewer_menu_send(GtkWidget *menu G_GNUC_UNUSED, VirtViewer *viewer)
 
528
{
 
529
        int i;
 
530
        GtkWidget *label = gtk_bin_get_child(GTK_BIN(menu));
 
531
        const char *text = gtk_label_get_label(GTK_LABEL(label));
 
532
 
 
533
        for (i = 0 ; i < G_N_ELEMENTS(keyCombos) ; i++) {
 
534
                if (!strcmp(text, keyCombos[i].label)) {
 
535
                        DEBUG_LOG("Sending key combo %s", gtk_label_get_text(GTK_LABEL(label)));
 
536
                        vnc_display_send_keys(VNC_DISPLAY(viewer->vnc),
 
537
                                              keyCombos[i].keys,
 
538
                                              keyCombos[i].nkeys);
 
539
                        return;
 
540
                }
 
541
        }
 
542
        DEBUG_LOG("Failed to find key combo %s", gtk_label_get_text(GTK_LABEL(label)));
 
543
}
 
544
 
 
545
 
 
546
static void viewer_save_screenshot(GtkWidget *vnc, const char *file)
 
547
{
 
548
        GdkPixbuf *pix = vnc_display_get_pixbuf(VNC_DISPLAY(vnc));
 
549
        gdk_pixbuf_save(pix, file, "png", NULL,
 
550
                        "tEXt::Generator App", PACKAGE, NULL);
 
551
        gdk_pixbuf_unref(pix);
 
552
}
 
553
 
 
554
static void viewer_menu_file_screenshot(GtkWidget *menu G_GNUC_UNUSED, VirtViewer *viewer)
 
555
{
 
556
        GtkWidget *dialog;
 
557
 
 
558
        dialog = gtk_file_chooser_dialog_new ("Save screenshot",
 
559
                                              NULL,
 
560
                                              GTK_FILE_CHOOSER_ACTION_SAVE,
 
561
                                              GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 
562
                                              GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
 
563
                                              NULL);
 
564
        gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
 
565
 
 
566
        //gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), default_folder_for_saving);
 
567
        //gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), "Screenshot");
 
568
 
 
569
        if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
 
570
                char *filename;
 
571
 
 
572
                filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
 
573
                viewer_save_screenshot(viewer->vnc, filename);
 
574
                g_free (filename);
 
575
        }
 
576
 
 
577
        gtk_widget_destroy (dialog);
 
578
}
 
579
 
 
580
static void viewer_about_close(GtkWidget *dialog, VirtViewer *viewer G_GNUC_UNUSED)
 
581
{
 
582
        gtk_widget_hide(dialog);
 
583
        gtk_widget_destroy(dialog);
 
584
}
 
585
 
 
586
static void viewer_about_delete(GtkWidget *dialog, void *dummy G_GNUC_UNUSED, VirtViewer *viewer G_GNUC_UNUSED)
 
587
{
 
588
        gtk_widget_hide(dialog);
 
589
        gtk_widget_destroy(dialog);
 
590
}
 
591
 
 
592
static void viewer_menu_help_about(GtkWidget *menu G_GNUC_UNUSED, VirtViewer *viewer)
 
593
{
 
594
        GladeXML *about;
 
595
        GtkWidget *dialog;
 
596
 
 
597
        about = viewer_load_glade("about.glade", "about");
 
598
 
 
599
        dialog = glade_xml_get_widget(about, "about");
 
600
        gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(dialog), VERSION);
 
601
        glade_xml_signal_connect_data(about, "about_delete",
 
602
                                      G_CALLBACK(viewer_about_delete), viewer);
 
603
        glade_xml_signal_connect_data(about, "about_close",
 
604
                                      G_CALLBACK(viewer_about_close), viewer);
 
605
 
 
606
        gtk_widget_show_all(dialog);
 
607
 
 
608
        g_object_unref(G_OBJECT(about));
 
609
}
 
610
 
 
611
 
 
612
 
 
613
static int viewer_parse_uuid(const char *name, unsigned char *uuid)
 
614
{
 
615
        int i;
 
616
 
 
617
        const char *cur = name;
 
618
        for (i = 0;i < 16;) {
 
619
                uuid[i] = 0;
 
620
                if (*cur == 0)
 
621
                        return -1;
 
622
                if ((*cur == '-') || (*cur == ' ')) {
 
623
                        cur++;
 
624
                        continue;
 
625
                }
 
626
                if ((*cur >= '0') && (*cur <= '9'))
 
627
                        uuid[i] = *cur - '0';
 
628
                else if ((*cur >= 'a') && (*cur <= 'f'))
 
629
                        uuid[i] = *cur - 'a' + 10;
 
630
                else if ((*cur >= 'A') && (*cur <= 'F'))
 
631
                        uuid[i] = *cur - 'A' + 10;
 
632
                else
 
633
                        return -1;
 
634
                uuid[i] *= 16;
 
635
                cur++;
 
636
                if (*cur == 0)
 
637
                        return -1;
 
638
                if ((*cur >= '0') && (*cur <= '9'))
 
639
                        uuid[i] += *cur - '0';
 
640
                else if ((*cur >= 'a') && (*cur <= 'f'))
 
641
                        uuid[i] += *cur - 'a' + 10;
 
642
                else if ((*cur >= 'A') && (*cur <= 'F'))
 
643
                        uuid[i] += *cur - 'A' + 10;
 
644
                else
 
645
                        return -1;
 
646
                i++;
 
647
                cur++;
 
648
        }
 
649
 
 
650
        return 0;
 
651
}
 
652
 
 
653
 
 
654
static virDomainPtr viewer_lookup_domain(VirtViewer *viewer)
 
655
{
 
656
        char *end;
 
657
        int id = strtol(viewer->domkey, &end, 10);
 
658
        virDomainPtr dom = NULL;
 
659
        unsigned char uuid[16];
 
660
 
 
661
        if (id >= 0 && end && !*end) {
 
662
                dom = virDomainLookupByID(viewer->conn, id);
 
663
        }
 
664
        if (!dom && viewer_parse_uuid(viewer->domkey, uuid) == 0) {
 
665
                dom = virDomainLookupByUUID(viewer->conn, uuid);
 
666
        }
 
667
        if (!dom) {
 
668
                dom = virDomainLookupByName(viewer->conn, viewer->domkey);
 
669
        }
 
670
        return dom;
 
671
}
 
672
 
 
673
static int viewer_matches_domain(VirtViewer *viewer,
 
674
                                 virDomainPtr dom)
 
675
{
 
676
        char *end;
 
677
        const char *name;
 
678
        int id = strtol(viewer->domkey, &end, 10);
 
679
        unsigned char wantuuid[16];
 
680
        unsigned char domuuid[16];
 
681
 
 
682
        if (id >= 0 && end && !*end) {
 
683
                if (virDomainGetID(dom) == id)
 
684
                        return 1;
 
685
        }
 
686
        if (!dom && viewer_parse_uuid(viewer->domkey, wantuuid) == 0) {
 
687
                virDomainGetUUID(dom, domuuid);
 
688
                if (memcmp(wantuuid, domuuid, VIR_UUID_BUFLEN) == 0)
 
689
                        return 1;
 
690
        }
 
691
 
 
692
        name = virDomainGetName(dom);
 
693
        if (strcmp(name, viewer->domkey) == 0)
 
694
                return 1;
 
695
 
 
696
        return 0;
 
697
}
 
698
 
 
699
static char * viewer_extract_vnc_port(virDomainPtr dom)
 
700
{
 
701
        char *xmldesc = virDomainGetXMLDesc(dom, 0);
 
702
        xmlDocPtr xml = NULL;
 
703
        xmlParserCtxtPtr pctxt = NULL;
 
704
        xmlXPathContextPtr ctxt = NULL;
 
705
        xmlXPathObjectPtr obj = NULL;
 
706
        char *port = NULL;
 
707
 
 
708
        pctxt = xmlNewParserCtxt();
 
709
        if (!pctxt || !pctxt->sax)
 
710
                goto error;
 
711
 
 
712
        xml = xmlCtxtReadDoc(pctxt, (const xmlChar *)xmldesc, "domain.xml", NULL,
 
713
                             XML_PARSE_NOENT | XML_PARSE_NONET |
 
714
                             XML_PARSE_NOWARNING);
 
715
        free(xmldesc);
 
716
        if (!xml)
 
717
                goto error;
 
718
 
 
719
        ctxt = xmlXPathNewContext(xml);
 
720
        if (!ctxt)
 
721
                goto error;
 
722
 
 
723
        obj = xmlXPathEval((const xmlChar *)"string(/domain/devices/graphics[@type='vnc']/@port)", ctxt);
 
724
        if (!obj || obj->type != XPATH_STRING || !obj->stringval || !obj->stringval[0])
 
725
                goto error;
 
726
        if (!strcmp((const char*)obj->stringval, "-1"))
 
727
                goto error;
 
728
 
 
729
        port = g_strdup((const char*)obj->stringval);
 
730
        xmlXPathFreeObject(obj);
 
731
        obj = NULL;
 
732
 
 
733
 error:
 
734
        if (obj)
 
735
                xmlXPathFreeObject(obj);
 
736
        if (ctxt)
 
737
                xmlXPathFreeContext(ctxt);
 
738
        if (xml)
 
739
                xmlFreeDoc(xml);
 
740
        if (pctxt)
 
741
                xmlFreeParserCtxt(pctxt);
 
742
        return port;
 
743
}
 
744
 
 
745
 
 
746
static int viewer_extract_host(const char *uristr, char **host, char **transport, char **user, int *port)
 
747
{
 
748
        xmlURIPtr uri;
 
749
        char *offset;
 
750
 
 
751
        *host = NULL;
 
752
        *transport = NULL;
 
753
        *user = NULL;
 
754
 
 
755
        if (uristr == NULL ||
 
756
            !g_strcasecmp(uristr, "xen"))
 
757
                uristr = "xen:///";
 
758
 
 
759
        uri = xmlParseURI(uristr);
 
760
        if (!uri || !uri->server) {
 
761
                *host = g_strdup("localhost");
 
762
        } else {
 
763
                *host = g_strdup(uri->server);
 
764
        }
 
765
 
 
766
        if (uri->user)
 
767
                *user = g_strdup(uri->user);
 
768
        *port = uri->port;
 
769
 
 
770
        offset = strchr(uri->scheme, '+');
 
771
        if (offset)
 
772
                *transport = g_strdup(offset+1);
 
773
 
 
774
        xmlFreeURI(uri);
 
775
        return 0;
 
776
}
 
777
 
 
778
#if defined(HAVE_SOCKETPAIR) && defined(HAVE_FORK)
 
779
 
 
780
static int viewer_open_tunnel(const char **cmd)
 
781
{
 
782
        int fd[2];
 
783
        pid_t pid;
 
784
 
 
785
        if (socketpair(PF_UNIX, SOCK_STREAM, 0, fd) < 0)
 
786
                return -1;
 
787
 
 
788
        pid = fork();
 
789
        if (pid == -1) {
 
790
                close(fd[0]);
 
791
                close(fd[1]);
 
792
                return -1;
 
793
        }
 
794
 
 
795
        if (pid == 0) { /* child */
 
796
                close(fd[0]);
 
797
                close(0);
 
798
                close(1);
 
799
                if (dup(fd[1]) < 0)
 
800
                        _exit(1);
 
801
                if (dup(fd[1]) < 0)
 
802
                        _exit(1);
 
803
                close(fd[1]);
 
804
                execvp("ssh", (char *const*)cmd);
 
805
                _exit(1);
 
806
        }
 
807
        close(fd[1]);
 
808
        return fd[0];
 
809
}
 
810
 
 
811
 
 
812
static int viewer_open_tunnel_ssh(const char *sshhost, int sshport, const char *sshuser, const char *vncport)
 
813
{
 
814
        const char *cmd[10];
 
815
        char portstr[50];
 
816
        int n = 0;
 
817
 
 
818
        if (!sshport)
 
819
                sshport = 22;
 
820
 
 
821
        sprintf(portstr, "%d", sshport);
 
822
 
 
823
        cmd[n++] = "ssh";
 
824
        cmd[n++] = "-p";
 
825
        cmd[n++] = portstr;
 
826
        if (sshuser) {
 
827
                cmd[n++] = "-l";
 
828
                cmd[n++] = sshuser;
 
829
        }
 
830
        cmd[n++] = sshhost;
 
831
        cmd[n++] = "nc";
 
832
        cmd[n++] = "localhost";
 
833
        cmd[n++] = vncport;
 
834
        cmd[n++] = NULL;
 
835
 
 
836
        return viewer_open_tunnel(cmd);
 
837
}
 
838
 
 
839
#endif /* defined(HAVE_SOCKETPAIR) && defined(HAVE_FORK) */
 
840
 
 
841
static void viewer_set_status(VirtViewer *viewer, const char *text)
 
842
{
 
843
        GtkWidget *status, *notebook;
 
844
 
 
845
        notebook = glade_xml_get_widget(viewer->glade, "notebook");
 
846
        status = glade_xml_get_widget(viewer->glade, "status");
 
847
 
 
848
        gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), 0);
 
849
        gtk_label_set_text(GTK_LABEL(status), text);
 
850
}
 
851
 
 
852
 
 
853
static void viewer_set_vnc(VirtViewer *viewer)
 
854
{
 
855
        GtkWidget *notebook;
 
856
 
 
857
        notebook = glade_xml_get_widget(viewer->glade, "notebook");
 
858
        gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), 1);
 
859
 
 
860
        gtk_widget_show(viewer->vnc);
 
861
}
 
862
 
 
863
 
 
864
static int viewer_activate(VirtViewer *viewer,
 
865
                           virDomainPtr dom)
 
866
{
 
867
        char *vncport = NULL;
 
868
        char *host = NULL;
 
869
        char *transport = NULL;
 
870
        char *user = NULL;
 
871
        int port, fd = -1;
 
872
        int ret = -1;
 
873
 
 
874
        if (viewer->active)
 
875
                goto cleanup;
 
876
 
 
877
        if ((vncport = viewer_extract_vnc_port(dom)) == NULL) {
 
878
                viewer_simple_message_dialog(viewer->window, _("Cannot determine the VNC port for the guest %s"),
 
879
                                             viewer->domkey);
 
880
                goto cleanup;
 
881
        }
 
882
 
 
883
        if (viewer_extract_host(viewer->uri, &host, &transport, &user, &port) < 0) {
 
884
                viewer_simple_message_dialog(viewer->window, _("Cannot determine the VNC host for the guest %s"),
 
885
                                             viewer->domkey);
 
886
                goto cleanup;
 
887
        }
 
888
 
 
889
        DEBUG_LOG("Remote host is %s and transport %s user %s",
 
890
                  host, transport ? transport : "", user ? user : "");
 
891
 
 
892
        viewer->vncAddress = g_strdup_printf("%s:%s", host, vncport);
 
893
 
 
894
#if defined(HAVE_SOCKETPAIR) && defined(HAVE_FORK)
 
895
        if (transport && g_strcasecmp(transport, "ssh") == 0 &&
 
896
            !viewer->direct)
 
897
                if ((fd = viewer_open_tunnel_ssh(host, port, user, vncport)) < 0)
 
898
                        return -1;
 
899
#endif
 
900
 
 
901
        if (fd >= 0) {
 
902
                vnc_display_open_fd(VNC_DISPLAY(viewer->vnc), fd);
 
903
        } else {
 
904
                vnc_display_open_host(VNC_DISPLAY(viewer->vnc), host, vncport);
 
905
        }
 
906
 
 
907
        viewer_set_status(viewer, "Connecting to VNC server");
 
908
 
 
909
        free(viewer->domtitle);
 
910
        viewer->domtitle = g_strdup(virDomainGetName(dom));
 
911
 
 
912
        viewer->connected = FALSE;
 
913
        viewer->active = TRUE;
 
914
        viewer_set_title(viewer, FALSE);
 
915
 
 
916
        ret = 0;
 
917
 cleanup:
 
918
        free(host);
 
919
        free(transport);
 
920
        free(user);
 
921
        free(vncport);
 
922
        return ret;
 
923
 
 
924
}
 
925
 
 
926
 
 
927
static gboolean viewer_retryauth(gpointer opaque)
 
928
{
 
929
        VirtViewer *viewer = opaque;
 
930
        viewer_initial_connect(viewer);
 
931
 
 
932
        return FALSE;
 
933
}
 
934
 
 
935
static void viewer_deactivate(VirtViewer *viewer)
 
936
{
 
937
        if (!viewer->active)
 
938
                return;
 
939
 
 
940
        vnc_display_close(VNC_DISPLAY(viewer->vnc));
 
941
        free(viewer->domtitle);
 
942
        viewer->domtitle = NULL;
 
943
 
 
944
        viewer->connected = FALSE;
 
945
        viewer->active = FALSE;
 
946
        g_free(viewer->vncAddress);
 
947
        viewer->vncAddress = NULL;
 
948
        viewer_set_title(viewer, FALSE);
 
949
 
 
950
        if (viewer->authretry) {
 
951
                viewer->authretry = FALSE;
 
952
                g_idle_add(viewer_retryauth, viewer);
 
953
        } else if (viewer->reconnect) {
 
954
                if (!viewer->withEvents) {
 
955
                        DEBUG_LOG("No domain events, falling back to polling");
 
956
                        g_timeout_add(500,
 
957
                                      viewer_connect_timer,
 
958
                                      viewer);
 
959
                }
 
960
 
 
961
                viewer_set_status(viewer, "Waiting for guest domain to re-start");
 
962
        } else {
 
963
                viewer_set_status(viewer, "Guest domain has shutdown");
 
964
                gtk_main_quit();
 
965
        }
 
966
}
 
967
 
 
968
static void viewer_connected(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer)
 
969
{
 
970
        viewer->connected = TRUE;
 
971
        viewer_set_status(viewer, "Connected to VNC server");
 
972
}
 
973
 
 
974
static void viewer_initialized(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer)
 
975
{
 
976
        viewer_set_vnc(viewer);
 
977
        viewer_set_title(viewer, FALSE);
 
978
}
 
979
 
 
980
 
 
981
static void viewer_disconnected(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer)
 
982
{
 
983
        if (!viewer->connected) {
 
984
                viewer_simple_message_dialog(viewer->window, _("Unable to connect to the VNC server %s"),
 
985
                                             viewer->vncAddress);
 
986
        }
 
987
        viewer_deactivate(viewer);
 
988
}
 
989
 
 
990
 
 
991
static void viewer_vnc_auth_failure(GtkWidget *vnc G_GNUC_UNUSED, const char *reason, VirtViewer *viewer)
 
992
{
 
993
        GtkWidget *dialog;
 
994
        int ret;
 
995
 
 
996
        dialog = gtk_message_dialog_new(GTK_WINDOW(viewer->window),
 
997
                                        GTK_DIALOG_MODAL |
 
998
                                        GTK_DIALOG_DESTROY_WITH_PARENT,
 
999
                                        GTK_MESSAGE_ERROR,
 
1000
                                        GTK_BUTTONS_YES_NO,
 
1001
                                        _("Unable to authenticate with VNC server at %s: %s\n"
 
1002
                                          "Retry connection again?"),
 
1003
                                        viewer->vncAddress, reason);
 
1004
 
 
1005
        ret = gtk_dialog_run(GTK_DIALOG(dialog));
 
1006
 
 
1007
        gtk_widget_destroy(dialog);
 
1008
 
 
1009
        if (ret == GTK_RESPONSE_YES)
 
1010
                viewer->authretry = TRUE;
 
1011
        else
 
1012
                viewer->authretry = FALSE;
 
1013
}
 
1014
 
 
1015
 
 
1016
static void viewer_vnc_auth_unsupported(GtkWidget *vnc G_GNUC_UNUSED, unsigned int authType, VirtViewer *viewer)
 
1017
{
 
1018
        viewer_simple_message_dialog(viewer->window,
 
1019
                                     _("Unable to authenticate with VNC server at %s\n"
 
1020
                                       "Unsupported authentication type %d"),
 
1021
                                     viewer->vncAddress, authType);
 
1022
}
 
1023
 
 
1024
 
 
1025
static void viewer_vnc_bell(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer)
 
1026
{
 
1027
        gdk_window_beep(GTK_WIDGET(viewer->window)->window);
 
1028
}
 
1029
 
 
1030
 
 
1031
/* text was actually requested */
 
1032
static void viewer_vnc_clipboard_copy(GtkClipboard *clipboard G_GNUC_UNUSED,
 
1033
                                      GtkSelectionData *data,
 
1034
                                      guint info G_GNUC_UNUSED,
 
1035
                                      VirtViewer *viewer)
 
1036
{
 
1037
        gtk_selection_data_set_text(data, viewer->clipboard, -1);
 
1038
}
 
1039
 
 
1040
static void viewer_vnc_server_cut_text(VncDisplay *vnc G_GNUC_UNUSED,
 
1041
                                       const gchar *text, VirtViewer *viewer)
 
1042
{
 
1043
        GtkClipboard *cb;
 
1044
        gsize a, b;
 
1045
        GtkTargetEntry targets[] = {
 
1046
                {g_strdup("UTF8_STRING"), 0, 0},
 
1047
                {g_strdup("COMPOUND_TEXT"), 0, 0},
 
1048
                {g_strdup("TEXT"), 0, 0},
 
1049
                {g_strdup("STRING"), 0, 0},
 
1050
        };
 
1051
 
 
1052
        if (!text)
 
1053
                return;
 
1054
 
 
1055
        g_free (viewer->clipboard);
 
1056
        viewer->clipboard = g_convert (text, -1, "utf-8", "iso8859-1", &a, &b, NULL);
 
1057
 
 
1058
        if (viewer->clipboard) {
 
1059
                cb = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
 
1060
 
 
1061
                gtk_clipboard_set_with_owner (cb,
 
1062
                                              targets,
 
1063
                                              G_N_ELEMENTS(targets),
 
1064
                                              (GtkClipboardGetFunc)viewer_vnc_clipboard_copy,
 
1065
                                              NULL,
 
1066
                                              G_OBJECT (viewer));
 
1067
        }
 
1068
}
 
1069
 
 
1070
 
 
1071
static int viewer_domain_event(virConnectPtr conn G_GNUC_UNUSED,
 
1072
                               virDomainPtr dom,
 
1073
                               int event,
 
1074
                               int detail G_GNUC_UNUSED,
 
1075
                               void *opaque)
 
1076
{
 
1077
        VirtViewer *viewer = opaque;
 
1078
 
 
1079
        DEBUG_LOG("Got domain event %d %d", event, detail);
 
1080
 
 
1081
        if (!viewer_matches_domain(viewer, dom))
 
1082
                return 0;
 
1083
 
 
1084
        switch (event) {
 
1085
        case VIR_DOMAIN_EVENT_STOPPED:
 
1086
                viewer_deactivate(viewer);
 
1087
                break;
 
1088
 
 
1089
        case VIR_DOMAIN_EVENT_STARTED:
 
1090
                viewer_activate(viewer, dom);
 
1091
                break;
 
1092
        }
 
1093
 
 
1094
        return 0;
 
1095
}
 
1096
 
 
1097
 
 
1098
static int viewer_initial_connect(VirtViewer *viewer)
 
1099
{
 
1100
        virDomainPtr dom = NULL;
 
1101
        virDomainInfo info;
 
1102
        int ret = -1;
 
1103
 
 
1104
        viewer_set_status(viewer, "Finding guest domain");
 
1105
        dom = viewer_lookup_domain(viewer);
 
1106
        if (!dom) {
 
1107
                if (viewer->waitvm) {
 
1108
                        viewer_set_status(viewer, "Waiting for guest domain to be created");
 
1109
                        goto done;
 
1110
                } else {
 
1111
                        viewer_simple_message_dialog(viewer->window, _("Cannot find guest domain %s"),
 
1112
                                                     viewer->domkey);
 
1113
                        DEBUG_LOG("Cannot find guest %s", viewer->domkey);
 
1114
                        goto cleanup;
 
1115
                }
 
1116
        }
 
1117
 
 
1118
        viewer_set_status(viewer, "Checking guest domain status");
 
1119
        if (virDomainGetInfo(dom, &info) < 0) {
 
1120
                DEBUG_LOG("Cannot get guest state");
 
1121
                goto cleanup;
 
1122
        }
 
1123
 
 
1124
        if (info.state == VIR_DOMAIN_SHUTOFF) {
 
1125
                viewer_set_status(viewer, "Waiting for guest domain to start");
 
1126
        } else {
 
1127
                if (viewer_activate(viewer, dom) < 0) {
 
1128
                        if (viewer->waitvm) {
 
1129
                                viewer_set_status(viewer, "Waiting for guest domain to start VNC");
 
1130
                        } else {
 
1131
                                DEBUG_LOG("Failed to activate viewer");
 
1132
                                goto cleanup;
 
1133
                        }
 
1134
                }
 
1135
        }
 
1136
 
 
1137
 done:
 
1138
        ret = 0;
 
1139
 cleanup:
 
1140
        if (dom)
 
1141
                virDomainFree(dom);
 
1142
        return ret;
 
1143
}
 
1144
 
 
1145
static gboolean viewer_connect_timer(void *opaque)
 
1146
{
 
1147
        VirtViewer *viewer = opaque;
 
1148
 
 
1149
        DEBUG_LOG("Connect timer fired");
 
1150
 
 
1151
        if (!viewer->active &&
 
1152
            viewer_initial_connect(viewer) < 0)
 
1153
                gtk_main_quit();
 
1154
 
 
1155
        if (viewer->active)
 
1156
                return FALSE;
 
1157
 
 
1158
        return TRUE;
 
1159
}
 
1160
 
 
1161
static void viewer_error_func (void *data G_GNUC_UNUSED, virErrorPtr error G_GNUC_UNUSED)
 
1162
{
 
1163
        /* nada */
 
1164
}
 
1165
 
 
1166
int
 
1167
viewer_start (const char *uri,
 
1168
              const char *name,
 
1169
              gboolean direct,
 
1170
              gboolean waitvm,
 
1171
              gboolean reconnect,
 
1172
              gboolean verbose,
 
1173
              gboolean debug,
 
1174
              GtkWidget *container)
 
1175
{
 
1176
        VirtViewer *viewer;
 
1177
        GtkWidget *notebook;
 
1178
        GtkWidget *align;
 
1179
        GtkWidget *menu;
 
1180
        int cred_types[] =
 
1181
                { VIR_CRED_AUTHNAME, VIR_CRED_PASSPHRASE };
 
1182
        virConnectAuth auth_libvirt = {
 
1183
                .credtype = cred_types,
 
1184
                .ncredtype = ARRAY_CARDINALITY(cred_types),
 
1185
                .cb = viewer_auth_libvirt_credentials,
 
1186
                .cbdata = (void *)uri,
 
1187
        };
 
1188
 
 
1189
        doDebug = debug;
 
1190
 
 
1191
        viewer = g_new0(VirtViewer, 1);
 
1192
 
 
1193
        viewer->active = 0;
 
1194
        viewer->autoResize = TRUE;
 
1195
        viewer->direct = direct;
 
1196
        viewer->waitvm = waitvm;
 
1197
        viewer->reconnect = reconnect;
 
1198
        viewer->verbose = verbose;
 
1199
        viewer->domkey = g_strdup(name);
 
1200
        viewer->uri = g_strdup(uri);
 
1201
 
 
1202
        g_value_init(&viewer->accelSetting, G_TYPE_STRING);
 
1203
 
 
1204
        viewer_event_register();
 
1205
 
 
1206
        virSetErrorFunc(NULL, viewer_error_func);
 
1207
 
 
1208
        viewer->conn = virConnectOpenAuth(uri,
 
1209
                                          //virConnectAuthPtrDefault,
 
1210
                                          &auth_libvirt,
 
1211
                                          VIR_CONNECT_RO);
 
1212
        if (!viewer->conn) {
 
1213
                viewer_simple_message_dialog(NULL, _("Unable to connect to libvirt with URI %s"),
 
1214
                                             uri ? uri : _("[none]"));
 
1215
                return -1;
 
1216
        }
 
1217
 
 
1218
        if (!(viewer->glade = viewer_load_glade("viewer.glade",
 
1219
                                                container ? "notebook" : "viewer")))
 
1220
                return -1;
 
1221
 
 
1222
        menu = glade_xml_get_widget(viewer->glade, "menu-view-resize");
 
1223
        if (!container)
 
1224
                gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), TRUE);
 
1225
 
 
1226
        glade_xml_signal_connect_data(viewer->glade, "viewer_menu_file_quit",
 
1227
                                      G_CALLBACK(viewer_menu_file_quit), viewer);
 
1228
        glade_xml_signal_connect_data(viewer->glade, "viewer_menu_file_screenshot",
 
1229
                                      G_CALLBACK(viewer_menu_file_screenshot), viewer);
 
1230
        glade_xml_signal_connect_data(viewer->glade, "viewer_menu_view_fullscreen",
 
1231
                                      G_CALLBACK(viewer_menu_view_fullscreen), viewer);
 
1232
        glade_xml_signal_connect_data(viewer->glade, "viewer_menu_view_resize",
 
1233
                                      G_CALLBACK(viewer_menu_view_resize), viewer);
 
1234
        glade_xml_signal_connect_data(viewer->glade, "viewer_menu_send",
 
1235
                                      G_CALLBACK(viewer_menu_send), viewer);
 
1236
        glade_xml_signal_connect_data(viewer->glade, "viewer_menu_help_about",
 
1237
                                      G_CALLBACK(viewer_menu_help_about), viewer);
 
1238
 
 
1239
 
 
1240
        viewer->vnc = vnc_display_new();
 
1241
        vnc_display_set_keyboard_grab(VNC_DISPLAY(viewer->vnc), TRUE);
 
1242
        vnc_display_set_pointer_grab(VNC_DISPLAY(viewer->vnc), TRUE);
 
1243
 
 
1244
        /*
 
1245
         * In auto-resize mode we have things setup so that we always
 
1246
         * automatically resize the top level window to be exactly the
 
1247
         * same size as the VNC desktop, except when it won't fit on
 
1248
         * the local screen, at which point we let it scale down.
 
1249
         * The upshot is, we always want scaling enabled.
 
1250
         * We disable force_size because we want to allow user to
 
1251
         * manually size the widget smaller too
 
1252
         */
 
1253
        vnc_display_set_force_size(VNC_DISPLAY(viewer->vnc), FALSE);
 
1254
        vnc_display_set_scaling(VNC_DISPLAY(viewer->vnc), TRUE);
 
1255
 
 
1256
        g_signal_connect(viewer->vnc, "vnc-connected",
 
1257
                         G_CALLBACK(viewer_connected), viewer);
 
1258
        g_signal_connect(viewer->vnc, "vnc-initialized",
 
1259
                         G_CALLBACK(viewer_initialized), viewer);
 
1260
        g_signal_connect(viewer->vnc, "vnc-disconnected",
 
1261
                         G_CALLBACK(viewer_disconnected), viewer);
 
1262
 
 
1263
        /* When VNC desktop resizes, we have to resize the containing widget */
 
1264
        g_signal_connect(viewer->vnc, "vnc-desktop-resize",
 
1265
                         G_CALLBACK(viewer_resize_desktop), viewer);
 
1266
        g_signal_connect(viewer->vnc, "vnc-pointer-grab",
 
1267
                         G_CALLBACK(viewer_mouse_grab), viewer);
 
1268
        g_signal_connect(viewer->vnc, "vnc-pointer-ungrab",
 
1269
                         G_CALLBACK(viewer_mouse_ungrab), viewer);
 
1270
        g_signal_connect(viewer->vnc, "vnc-keyboard-grab",
 
1271
                         G_CALLBACK(viewer_key_grab), viewer);
 
1272
        g_signal_connect(viewer->vnc, "vnc-keyboard-ungrab",
 
1273
                         G_CALLBACK(viewer_key_ungrab), viewer);
 
1274
 
 
1275
        g_signal_connect(viewer->vnc, "vnc-auth-credential",
 
1276
                         G_CALLBACK(viewer_auth_vnc_credentials), &viewer->vncAddress);
 
1277
        g_signal_connect(viewer->vnc, "vnc-auth-failure",
 
1278
                         G_CALLBACK(viewer_vnc_auth_failure), viewer);
 
1279
        g_signal_connect(viewer->vnc, "vnc-auth-unsupported",
 
1280
                         G_CALLBACK(viewer_vnc_auth_unsupported), viewer);
 
1281
 
 
1282
        g_signal_connect(viewer->vnc, "vnc-bell",
 
1283
                         G_CALLBACK(viewer_vnc_bell), viewer);
 
1284
        g_signal_connect(viewer->vnc, "vnc-server-cut-text",
 
1285
                         G_CALLBACK(viewer_vnc_server_cut_text), viewer);
 
1286
 
 
1287
        notebook = glade_xml_get_widget(viewer->glade, "notebook");
 
1288
 
 
1289
        gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
 
1290
        align = glade_xml_get_widget(viewer->glade, "vnc-align");
 
1291
        gtk_container_add(GTK_CONTAINER(align), viewer->vnc);
 
1292
 
 
1293
        g_signal_connect(align, "size-allocate",
 
1294
                         G_CALLBACK(viewer_resize_align), viewer);
 
1295
 
 
1296
        if (container) {
 
1297
                viewer->container = container;
 
1298
                gtk_container_add(GTK_CONTAINER(container), GTK_WIDGET(notebook));
 
1299
                gtk_widget_show_all(container);
 
1300
        } else {
 
1301
                GtkWidget *window = glade_xml_get_widget(viewer->glade, "viewer");
 
1302
                GSList *accels;
 
1303
                viewer->container = window;
 
1304
                viewer->window = window;
 
1305
                g_signal_connect(window, "delete-event",
 
1306
                                 G_CALLBACK(viewer_shutdown), viewer);
 
1307
                gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
 
1308
                viewer->accelEnabled = TRUE;
 
1309
                accels = gtk_accel_groups_from_object(G_OBJECT(window));
 
1310
                for ( ; accels ; accels = accels->next) {
 
1311
                        viewer->accelList = g_slist_append(viewer->accelList, accels->data);
 
1312
                        g_object_ref(G_OBJECT(accels->data));
 
1313
                }
 
1314
                gtk_widget_show_all(window);
 
1315
        }
 
1316
 
 
1317
        gtk_widget_realize(viewer->vnc);
 
1318
 
 
1319
        if (viewer_initial_connect(viewer) < 0)
 
1320
                return -1;
 
1321
 
 
1322
        if (virConnectDomainEventRegister(viewer->conn,
 
1323
                                          viewer_domain_event,
 
1324
                                          viewer,
 
1325
                                          NULL) < 0)
 
1326
                viewer->withEvents = FALSE;
 
1327
        else
 
1328
                viewer->withEvents = TRUE;
 
1329
 
 
1330
        if (!viewer->withEvents &&
 
1331
            !viewer->active) {
 
1332
                DEBUG_LOG("No domain events, falling back to polling");
 
1333
                g_timeout_add(500,
 
1334
                              viewer_connect_timer,
 
1335
                              viewer);
 
1336
        }
 
1337
 
 
1338
        return 0;
 
1339
}
 
1340
 
 
1341
 
 
1342
/*
 
1343
 * Local variables:
 
1344
 *  c-indent-level: 8
 
1345
 *  c-basic-offset: 8
 
1346
 *  tab-width: 8
 
1347
 * End:
 
1348
 */