~ubuntu-branches/ubuntu/vivid/gtk-vnc/vivid

« back to all changes in this revision

Viewing changes to tools/gvnccapture.c

  • Committer: Bazaar Package Importer
  • Author(s): Guido Günther
  • Date: 2010-07-16 17:55:31 UTC
  • mfrom: (1.1.11 upstream)
  • Revision ID: james.westby@ubuntu.com-20100716175531-vnolppaqgmo3rqij
Tags: 0.4.1-1
* [2703df5] Imported Upstream version 0.4.1
* [5a5910e] Drop patches applied upstream 
      0001-say-disconnected-from-server-only-once-connected.patch
      0002-remove-WERROR_CFLAGS.patch
      0004-Don-t-use-PATH_MAX.patch
      0005-Drop-space-after-Wl-in-libgtk_vnc_1_0_la_LDFLAGS.patch
      0006-Add-Wl-since-no-undefined-is-a-linker-option.patch
      0007-Explicitly-link-against-X11-libs-for-XkbGetKeyboard.patch
      0008-Add-missing-symbols.patch
      0009-Fix-missing-intialisation-of-sysdir-and-uderdir.patch
* [e3ed1d0] No need to run autoreconf anymore
* [1529b9a] No static libs built
* [15068ef] Update symbols file with new symbols
* [337c397] Bump standards version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Vnc Image Capture
 
3
 *
 
4
 * Copyright (C) 2010 Daniel P. Berrange <dan@berrange.com>
 
5
 *
 
6
 * This library is free software; you can redistribute it and/or
 
7
 * modify it under the terms of the GNU Lesser General Public
 
8
 * License as published by the Free Software Foundation; either
 
9
 * version 2.0 of the License, or (at your option) any later version.
 
10
 *
 
11
 * This library is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
14
 * Lesser General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU Lesser General Public
 
17
 * License along with this library; if not, write to the Free Software
 
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 
19
 */
 
20
 
 
21
/*
 
22
=head1 NAME
 
23
 
 
24
gvnccapture - VNC screenshot capture
 
25
 
 
26
=head1 SYNOPSIS
 
27
 
 
28
gvnccapture [OPTION]... [HOST][:DISPLAY] FILENAME
 
29
 
 
30
=head1 DESCRIPTION
 
31
 
 
32
Capture a screenshot of the VNC desktop at HOST:DISPLAY saving to the
 
33
image file FILENAME. If HOST is omitted it defaults to "localhost",
 
34
if :DISPLAY is omitted, it defaults to ":1". FILENAME must end in a
 
35
known image format extension (eg ".png", ".jpeg"). Supported options
 
36
are
 
37
 
 
38
=over 4
 
39
 
 
40
=item --help, -?
 
41
 
 
42
Display command line help information
 
43
 
 
44
=item --quiet, -q
 
45
 
 
46
Do not display information on the console when capturing the screenshot,
 
47
with the exception of any password prompt.
 
48
 
 
49
=item --debug, -d
 
50
 
 
51
Display verbose debugging information on the console
 
52
 
 
53
=back
 
54
 
 
55
=head1 EXIT STATUS
 
56
 
 
57
The exit status is 0 upon successful screen capture, otherwise
 
58
it is a non-zero integer
 
59
 
 
60
=head1 EXAMPLES
 
61
 
 
62
 # gvnccapture localhost:1 desktop.png
 
63
 Password: 
 
64
 Connected to localhost:1
 
65
 Saved display to desktop.png
 
66
 
 
67
=head1 AUTHORS
 
68
 
 
69
Daniel P. Berrange <dan@berrange.com>
 
70
 
 
71
=head1 COPYRIGHT
 
72
 
 
73
Copyright (C) 2010 Daniel P. Berrange <dan@berrange.com>.
 
74
 
 
75
License LGPLv2+: GNU Lesser GPL version 2 or later <http://gnu.org/licenses/gpl.html>.
 
76
 
 
77
This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.
 
78
 
 
79
=head1 SEE ALSO
 
80
 
 
81
vinagre(1)
 
82
 
 
83
=cut
 
84
 */
 
85
 
 
86
#include "config.h"
 
87
 
 
88
#include <string.h>
 
89
#include <stdlib.h>
 
90
#include <stdio.h>
 
91
#ifdef HAVE_TERMIOS_H
 
92
#include <termios.h>
 
93
#endif
 
94
#include <unistd.h>
 
95
#include <glib/gi18n.h>
 
96
#include <gdk-pixbuf/gdk-pixbuf.h>
 
97
#include <vncconnection.h>
 
98
#include <vncbaseframebuffer.h>
 
99
 
 
100
struct GVncCapture {
 
101
        gchar *host;
 
102
        int port;
 
103
 
 
104
        gboolean quiet;
 
105
        gboolean saved;
 
106
 
 
107
        VncConnection *conn;
 
108
        GMainLoop *loop;
 
109
        gboolean connected;
 
110
        gchar *output;
 
111
 
 
112
        GdkPixbuf *pixbuf;
 
113
};
 
114
 
 
115
static const guint preferable_auths[] = {
 
116
        /*
 
117
         * Both these two provide TLS based auth, and can layer
 
118
         * all the other auth types on top. So these two must
 
119
         * be the first listed
 
120
         */
 
121
        VNC_CONNECTION_AUTH_VENCRYPT,
 
122
        VNC_CONNECTION_AUTH_TLS,
 
123
 
 
124
        /*
 
125
         * Then stackable auth types in order of preference
 
126
         */
 
127
        VNC_CONNECTION_AUTH_SASL,
 
128
        VNC_CONNECTION_AUTH_MSLOGON,
 
129
        VNC_CONNECTION_AUTH_VNC,
 
130
 
 
131
        /*
 
132
         * Or nothing at all
 
133
         */
 
134
        VNC_CONNECTION_AUTH_NONE
 
135
};
 
136
 
 
137
 
 
138
static gchar *
 
139
do_vnc_get_credential(const gchar *prompt, gboolean doecho)
 
140
{
 
141
#ifdef HAVE_TERMIOS_H
 
142
        struct termios old, new;
 
143
#endif
 
144
        gchar buf[100];
 
145
        gchar *res;
 
146
        int n = sizeof(buf);
 
147
        ssize_t len;
 
148
 
 
149
        printf("%s", prompt);
 
150
        fflush(stdout);
 
151
 
 
152
#ifdef HAVE_TERMIOS_H
 
153
        /* Turn echoing off and fail if we can't. */
 
154
        if (!doecho && tcgetattr (fileno (stdin), &old) != 0)
 
155
                return NULL;
 
156
        new = old;
 
157
        new.c_lflag &= ~ECHO;
 
158
        if (!doecho && tcsetattr(fileno(stdin), TCSAFLUSH, &new) != 0)
 
159
                return NULL;
 
160
#else
 
161
        doecho = TRUE; /* Avoid unused parameter compile warning */
 
162
#endif
 
163
 
 
164
        /* Read the password. */
 
165
        if ((res = fgets(buf, n, stdin)) != NULL) {
 
166
                len = strlen(res);
 
167
                if (res[len-1] == '\n')
 
168
                        res[len-1] = '\0';
 
169
        }
 
170
 
 
171
#ifdef HAVE_TERMIOS_H
 
172
        /* Restore terminal. */
 
173
        if (!doecho) {
 
174
                printf("\n");
 
175
                (void) tcsetattr(fileno (stdin), TCSAFLUSH, &old);
 
176
        }
 
177
#endif
 
178
 
 
179
        return res ? g_strdup(res) : NULL;
 
180
}
 
181
 
 
182
 
 
183
static void do_vnc_framebuffer_update(VncConnection *conn,
 
184
                                      guint16 x, guint16 y,
 
185
                                      guint16 width, guint16 height,
 
186
                                      gpointer opaque)
 
187
{
 
188
        struct GVncCapture *capture = opaque;
 
189
 
 
190
        if (!capture->pixbuf)
 
191
                return;
 
192
 
 
193
        /* Crude attempt to see if we've got a complete frame by
 
194
         * checking if we've hit the bottom-right corner. Will
 
195
         * probably break
 
196
         */
 
197
        if ((x + width) == vnc_connection_get_width(conn) &&
 
198
            (y + height) == vnc_connection_get_height(conn)) {
 
199
                VNC_DEBUG("All done, saving to %s", capture->output);
 
200
                gchar *ext = strrchr(capture->output, '.');
 
201
                GError *err = NULL;
 
202
                if (ext)
 
203
                        ext++;
 
204
 
 
205
                if (!gdk_pixbuf_save(capture->pixbuf,
 
206
                                     capture->output,
 
207
                                     ext ? ext : "png",
 
208
                                     &err,
 
209
                                     "tEXt::Generator App", "gvnccapture", NULL)) {
 
210
                        if (!capture->quiet)
 
211
                                g_print("Unable to save display to %s: %s",
 
212
                                        capture->output, err->message);
 
213
                } else {
 
214
                        capture->saved = TRUE;
 
215
                        if (!capture->quiet)
 
216
                                g_print("Saved display to %s\n",
 
217
                                        capture->output);
 
218
                }
 
219
                vnc_connection_shutdown(conn);
 
220
                g_main_quit(capture->loop);
 
221
        }
 
222
}
 
223
 
 
224
static void do_vnc_desktop_resize(VncConnection *conn,
 
225
                                  int width, int height,
 
226
                                  gpointer opaque)
 
227
{
 
228
        struct GVncCapture *capture = opaque;
 
229
        const VncPixelFormat *remoteFormat;
 
230
        VncPixelFormat localFormat = {
 
231
                .bits_per_pixel = 32,
 
232
                .depth = 32,
 
233
                .byte_order = G_BYTE_ORDER,
 
234
                .true_color_flag = TRUE,
 
235
                .red_max = 255,
 
236
                .green_max = 255,
 
237
                .blue_max = 255,
 
238
                .red_shift = 0,
 
239
                .green_shift = 8,
 
240
                .blue_shift = 16,
 
241
        };
 
242
        VncBaseFramebuffer *fb;
 
243
 
 
244
        if (capture->pixbuf) {
 
245
                gdk_pixbuf_unref(capture->pixbuf);
 
246
                capture->pixbuf = NULL;
 
247
        }
 
248
 
 
249
        VNC_DEBUG("Resize %dx%d", width, height);
 
250
        remoteFormat = vnc_connection_get_pixel_format(conn);
 
251
 
 
252
        /* We'll fix our local copy as rgb888 */
 
253
        capture->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
 
254
                                         TRUE, 
 
255
                                         8,
 
256
                                         width,
 
257
                                         height);
 
258
        gdk_pixbuf_fill(capture->pixbuf, 0);
 
259
 
 
260
        fb = vnc_base_framebuffer_new(gdk_pixbuf_get_pixels(capture->pixbuf),
 
261
                                      gdk_pixbuf_get_width(capture->pixbuf),
 
262
                                      gdk_pixbuf_get_height(capture->pixbuf),
 
263
                                      gdk_pixbuf_get_rowstride(capture->pixbuf),
 
264
                                      remoteFormat,
 
265
                                      &localFormat);
 
266
 
 
267
        vnc_connection_set_framebuffer(conn, VNC_FRAMEBUFFER(fb));
 
268
 
 
269
        g_object_unref(fb);
 
270
}
 
271
 
 
272
 
 
273
static void do_vnc_initialized(VncConnection *conn,
 
274
                               gpointer opaque)
 
275
{
 
276
        struct GVncCapture *capture = opaque;
 
277
        gint32 encodings[] = {  VNC_CONNECTION_ENCODING_DESKTOP_RESIZE,
 
278
                                VNC_CONNECTION_ENCODING_ZRLE,
 
279
                                VNC_CONNECTION_ENCODING_HEXTILE,
 
280
                                VNC_CONNECTION_ENCODING_RRE,
 
281
                                VNC_CONNECTION_ENCODING_COPY_RECT,
 
282
                                VNC_CONNECTION_ENCODING_RAW };
 
283
        gint32 *encodingsp;
 
284
        int n_encodings;
 
285
 
 
286
        do_vnc_desktop_resize(conn,
 
287
                              vnc_connection_get_width(conn),
 
288
                              vnc_connection_get_height(conn),
 
289
                              capture);
 
290
 
 
291
        encodingsp = encodings;
 
292
        n_encodings = G_N_ELEMENTS(encodings);
 
293
 
 
294
        VNC_DEBUG("Sending %d encodings", n_encodings);
 
295
        if (!vnc_connection_set_encodings(conn, n_encodings, encodingsp))
 
296
                goto error;
 
297
 
 
298
        VNC_DEBUG("Requesting first framebuffer update");
 
299
        if (!vnc_connection_framebuffer_update_request(capture->conn,
 
300
                                                       0, 0, 0,
 
301
                                                       vnc_connection_get_width(capture->conn),
 
302
                                                       vnc_connection_get_height(capture->conn)))
 
303
                vnc_connection_shutdown(capture->conn);
 
304
 
 
305
        if (!capture->quiet)
 
306
                g_print("Connected to %s:%d\n", capture->host, capture->port - 5900);
 
307
        capture->connected = TRUE;
 
308
        return;
 
309
 
 
310
 error:
 
311
        vnc_connection_shutdown(conn);
 
312
}
 
313
 
 
314
static void do_vnc_disconnected(VncConnection *conn G_GNUC_UNUSED,
 
315
                                gpointer opaque)
 
316
{
 
317
        struct GVncCapture *capture = opaque;
 
318
        if (!capture->quiet) {
 
319
                if (capture->connected)
 
320
                        g_print("Disconnected from %s:%d\n", capture->host, capture->port - 5900);
 
321
                else
 
322
                        g_print("Unable to connect to %s:%d\n", capture->host, capture->port - 5900);
 
323
        }
 
324
        g_main_quit(capture->loop);
 
325
}
 
326
 
 
327
static void do_vnc_auth_credential(VncConnection *conn, GValueArray *credList, gpointer opaque)
 
328
{
 
329
        struct GVncCapture *capture = opaque;
 
330
        guint i;
 
331
        char **data;
 
332
 
 
333
        data = g_new0(char *, credList->n_values);
 
334
 
 
335
        for (i = 0 ; i < credList->n_values ; i++) {
 
336
                GValue *cred = g_value_array_get_nth(credList, i);
 
337
                switch (g_value_get_enum(cred)) {
 
338
                case VNC_CONNECTION_CREDENTIAL_PASSWORD:
 
339
                        data[i] = do_vnc_get_credential("Password: ", FALSE);
 
340
                        if (!data[i]) {
 
341
                                if (!capture->quiet)
 
342
                                        g_print("Failed to read password\n");
 
343
                                vnc_connection_shutdown(conn);
 
344
                                goto cleanup;
 
345
                        }
 
346
                        break;
 
347
                case VNC_CONNECTION_CREDENTIAL_USERNAME:
 
348
                        data[i] = do_vnc_get_credential("Username: ", TRUE);
 
349
                        if (!data[i]) {
 
350
                                if (!capture->quiet)
 
351
                                        g_print("Failed to read username\n");
 
352
                                vnc_connection_shutdown(conn);
 
353
                                goto cleanup;
 
354
                        }
 
355
                        break;
 
356
                case VNC_CONNECTION_CREDENTIAL_CLIENTNAME:
 
357
                        data[i] = g_strdup("gvnccapture");
 
358
                        break;
 
359
                default:
 
360
                        break;
 
361
                }
 
362
        }
 
363
        for (i = 0 ; i < credList->n_values ; i++) {
 
364
                GValue *cred = g_value_array_get_nth(credList, i);
 
365
                if (data[i]) {
 
366
                        if (!vnc_connection_set_credential(conn,
 
367
                                                           g_value_get_enum(cred),
 
368
                                                           data[i])) {
 
369
                                g_print("Failed to set credential type %d %s\n", g_value_get_enum(cred), data[i]);
 
370
                                vnc_connection_shutdown(conn);
 
371
                        }
 
372
                } else {
 
373
                        if (!capture->quiet)
 
374
                                g_print("Unsupported credential type %d\n", g_value_get_enum(cred));
 
375
                        vnc_connection_shutdown(conn);
 
376
                }
 
377
        }
 
378
 
 
379
 cleanup:
 
380
        for (i = 0 ; i < credList->n_values ; i++)
 
381
                g_free(data[i]);
 
382
        g_free(data);
 
383
}
 
384
 
 
385
static void do_vnc_auth_choose_type(VncConnection *conn,
 
386
                                    GValueArray *types,
 
387
                                    gpointer opaque G_GNUC_UNUSED)
 
388
{
 
389
        guint i, j;
 
390
 
 
391
        if (!types->n_values) {
 
392
                VNC_DEBUG("No auth types to choose from");
 
393
                return;
 
394
        }
 
395
 
 
396
        for (i = 0 ; i < G_N_ELEMENTS(preferable_auths) ; i++) {
 
397
                int pref = preferable_auths[i];
 
398
 
 
399
                for (j = 0 ; j < types->n_values ; j++) {
 
400
                        GValue *type = g_value_array_get_nth(types, j);
 
401
                        VNC_DEBUG("Compare %d vs %d", pref, g_value_get_enum(type));
 
402
                        if (pref == g_value_get_enum(type)) {
 
403
                                VNC_DEBUG("Chosen auth %d", pref);
 
404
                                vnc_connection_set_auth_type(conn, pref);
 
405
                                return;
 
406
                        }
 
407
                }
 
408
        }
 
409
 
 
410
        GValue *type = g_value_array_get_nth(types, 0);
 
411
        VNC_DEBUG("Chosen default auth %d", g_value_get_enum(type));
 
412
        vnc_connection_set_auth_type(conn, g_value_get_enum(type));
 
413
}
 
414
 
 
415
 
 
416
static void show_help(const char *binary, const char *error)
 
417
{
 
418
        if (error)
 
419
                g_print("%s\n\n", error);
 
420
        g_print("Usage: %s [HOSTNAME][:DISPLAY] FILENAME\n\n", binary);
 
421
        g_print("Run '%s --help' to see a full list of available command line options\n",
 
422
                binary);
 
423
}
 
424
 
 
425
static gboolean vnc_debug_option_arg(const gchar *option_name G_GNUC_UNUSED,
 
426
                                     const gchar *value G_GNUC_UNUSED,
 
427
                                     gpointer data G_GNUC_UNUSED,
 
428
                                     GError **error G_GNUC_UNUSED)
 
429
{
 
430
        vnc_util_set_debug(TRUE);
 
431
        return TRUE;
 
432
}
 
433
 
 
434
 
 
435
int main(int argc, char **argv)
 
436
{
 
437
        GOptionContext *context;
 
438
        GError *error = NULL;
 
439
        gchar *display;
 
440
        gchar *port;
 
441
        gchar **args = NULL;
 
442
        int ret;
 
443
        gboolean quiet = FALSE;
 
444
        const GOptionEntry options [] = {
 
445
                { "debug", 'd', G_OPTION_FLAG_NO_ARG,  G_OPTION_ARG_CALLBACK,
 
446
                  vnc_debug_option_arg, "Enables debug output", NULL },
 
447
                { "quiet", 'q', 0, G_OPTION_ARG_NONE,
 
448
                  &quiet, "Don't print any status to console", NULL },
 
449
                { G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_STRING_ARRAY, &args,
 
450
                  NULL, "HOSTNAME[:DISPLAY] FILENAME" },
 
451
                { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, 0 }
 
452
        };
 
453
        struct GVncCapture *capture;
 
454
 
 
455
        g_type_init();
 
456
 
 
457
        /* Setup command line options */
 
458
        context = g_option_context_new("- Vnc Image Capture");
 
459
        g_option_context_add_main_entries(context, options, NULL);
 
460
        g_option_context_parse(context, &argc, &argv, &error);
 
461
        if (error) {
 
462
                show_help(argv[0], error->message);
 
463
                g_error_free(error);
 
464
                return 1;
 
465
        }
 
466
        if (!args || (g_strv_length(args) != 2)) {
 
467
                show_help(argv[0], NULL);
 
468
                return 1;
 
469
        }
 
470
 
 
471
        capture = g_new0(struct GVncCapture, 1);
 
472
        capture->quiet = quiet;
 
473
 
 
474
        if (args[0][0] == ':') {
 
475
                capture->host = g_strdup("localhost");
 
476
                display = args[0];
 
477
        } else {
 
478
                capture->host = g_strdup(args[0]);
 
479
                display = strchr(capture->host, ':');
 
480
        }
 
481
        if (display) {
 
482
                *display = 0;
 
483
                display++;
 
484
                capture->port = 5900 + atoi(display);
 
485
        } else {
 
486
                capture->port = 5900;
 
487
        }
 
488
        port = g_strdup_printf("%d", capture->port);
 
489
 
 
490
        capture->conn = vnc_connection_new();
 
491
        capture->output = g_strdup(args[1]);
 
492
 
 
493
        g_signal_connect(capture->conn, "vnc-initialized",
 
494
                         G_CALLBACK(do_vnc_initialized), capture);
 
495
        g_signal_connect(capture->conn, "vnc-disconnected",
 
496
                         G_CALLBACK(do_vnc_disconnected), capture);
 
497
        g_signal_connect(capture->conn, "vnc-auth-choose-type",
 
498
                         G_CALLBACK(do_vnc_auth_choose_type), capture);
 
499
        g_signal_connect(capture->conn, "vnc-auth-choose-subtype",
 
500
                         G_CALLBACK(do_vnc_auth_choose_type), capture);
 
501
        g_signal_connect(capture->conn, "vnc-auth-credential",
 
502
                         G_CALLBACK(do_vnc_auth_credential), capture);
 
503
        g_signal_connect(capture->conn, "vnc-desktop-resize",
 
504
                         G_CALLBACK(do_vnc_desktop_resize), capture);
 
505
        g_signal_connect(capture->conn, "vnc-framebuffer-update",
 
506
                         G_CALLBACK(do_vnc_framebuffer_update), capture);
 
507
 
 
508
        vnc_connection_open_host(capture->conn, capture->host, port);
 
509
 
 
510
        capture->loop = g_main_loop_new(g_main_context_default(), FALSE);
 
511
 
 
512
        g_main_loop_run(capture->loop);
 
513
 
 
514
        vnc_connection_shutdown(capture->conn);
 
515
        g_object_unref(capture->conn);
 
516
        if (capture->pixbuf)
 
517
                gdk_pixbuf_unref(capture->pixbuf);
 
518
 
 
519
        ret = capture->saved ? 0 : 1;
 
520
 
 
521
        g_free(capture->host);
 
522
        g_free(capture);
 
523
 
 
524
        return ret;
 
525
}
 
526
 
 
527
/*
 
528
 * Local variables:
 
529
 *  c-indent-level: 8
 
530
 *  c-basic-offset: 8
 
531
 *  tab-width: 8
 
532
 * End:
 
533
 */