4
* Copyright (C) 2010 Daniel P. Berrange <dan@berrange.com>
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.
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.
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
24
gvnccapture - VNC screenshot capture
28
gvnccapture [OPTION]... [HOST][:DISPLAY] FILENAME
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
42
Display command line help information
46
Do not display information on the console when capturing the screenshot,
47
with the exception of any password prompt.
51
Display verbose debugging information on the console
57
The exit status is 0 upon successful screen capture, otherwise
58
it is a non-zero integer
62
# gvnccapture localhost:1 desktop.png
64
Connected to localhost:1
65
Saved display to desktop.png
69
Daniel P. Berrange <dan@berrange.com>
73
Copyright (C) 2010 Daniel P. Berrange <dan@berrange.com>.
75
License LGPLv2+: GNU Lesser GPL version 2 or later <http://gnu.org/licenses/gpl.html>.
77
This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.
95
#include <glib/gi18n.h>
96
#include <gdk-pixbuf/gdk-pixbuf.h>
97
#include <vncconnection.h>
98
#include <vncbaseframebuffer.h>
115
static const guint preferable_auths[] = {
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
121
VNC_CONNECTION_AUTH_VENCRYPT,
122
VNC_CONNECTION_AUTH_TLS,
125
* Then stackable auth types in order of preference
127
VNC_CONNECTION_AUTH_SASL,
128
VNC_CONNECTION_AUTH_MSLOGON,
129
VNC_CONNECTION_AUTH_VNC,
134
VNC_CONNECTION_AUTH_NONE
139
do_vnc_get_credential(const gchar *prompt, gboolean doecho)
141
#ifdef HAVE_TERMIOS_H
142
struct termios old, new;
149
printf("%s", prompt);
152
#ifdef HAVE_TERMIOS_H
153
/* Turn echoing off and fail if we can't. */
154
if (!doecho && tcgetattr (fileno (stdin), &old) != 0)
157
new.c_lflag &= ~ECHO;
158
if (!doecho && tcsetattr(fileno(stdin), TCSAFLUSH, &new) != 0)
161
doecho = TRUE; /* Avoid unused parameter compile warning */
164
/* Read the password. */
165
if ((res = fgets(buf, n, stdin)) != NULL) {
167
if (res[len-1] == '\n')
171
#ifdef HAVE_TERMIOS_H
172
/* Restore terminal. */
175
(void) tcsetattr(fileno (stdin), TCSAFLUSH, &old);
179
return res ? g_strdup(res) : NULL;
183
static void do_vnc_framebuffer_update(VncConnection *conn,
184
guint16 x, guint16 y,
185
guint16 width, guint16 height,
188
struct GVncCapture *capture = opaque;
190
if (!capture->pixbuf)
193
/* Crude attempt to see if we've got a complete frame by
194
* checking if we've hit the bottom-right corner. Will
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, '.');
205
if (!gdk_pixbuf_save(capture->pixbuf,
209
"tEXt::Generator App", "gvnccapture", NULL)) {
211
g_print("Unable to save display to %s: %s",
212
capture->output, err->message);
214
capture->saved = TRUE;
216
g_print("Saved display to %s\n",
219
vnc_connection_shutdown(conn);
220
g_main_quit(capture->loop);
224
static void do_vnc_desktop_resize(VncConnection *conn,
225
int width, int height,
228
struct GVncCapture *capture = opaque;
229
const VncPixelFormat *remoteFormat;
230
VncPixelFormat localFormat = {
231
.bits_per_pixel = 32,
233
.byte_order = G_BYTE_ORDER,
234
.true_color_flag = TRUE,
242
VncBaseFramebuffer *fb;
244
if (capture->pixbuf) {
245
gdk_pixbuf_unref(capture->pixbuf);
246
capture->pixbuf = NULL;
249
VNC_DEBUG("Resize %dx%d", width, height);
250
remoteFormat = vnc_connection_get_pixel_format(conn);
252
/* We'll fix our local copy as rgb888 */
253
capture->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
258
gdk_pixbuf_fill(capture->pixbuf, 0);
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),
267
vnc_connection_set_framebuffer(conn, VNC_FRAMEBUFFER(fb));
273
static void do_vnc_initialized(VncConnection *conn,
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 };
286
do_vnc_desktop_resize(conn,
287
vnc_connection_get_width(conn),
288
vnc_connection_get_height(conn),
291
encodingsp = encodings;
292
n_encodings = G_N_ELEMENTS(encodings);
294
VNC_DEBUG("Sending %d encodings", n_encodings);
295
if (!vnc_connection_set_encodings(conn, n_encodings, encodingsp))
298
VNC_DEBUG("Requesting first framebuffer update");
299
if (!vnc_connection_framebuffer_update_request(capture->conn,
301
vnc_connection_get_width(capture->conn),
302
vnc_connection_get_height(capture->conn)))
303
vnc_connection_shutdown(capture->conn);
306
g_print("Connected to %s:%d\n", capture->host, capture->port - 5900);
307
capture->connected = TRUE;
311
vnc_connection_shutdown(conn);
314
static void do_vnc_disconnected(VncConnection *conn G_GNUC_UNUSED,
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);
322
g_print("Unable to connect to %s:%d\n", capture->host, capture->port - 5900);
324
g_main_quit(capture->loop);
327
static void do_vnc_auth_credential(VncConnection *conn, GValueArray *credList, gpointer opaque)
329
struct GVncCapture *capture = opaque;
333
data = g_new0(char *, credList->n_values);
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);
342
g_print("Failed to read password\n");
343
vnc_connection_shutdown(conn);
347
case VNC_CONNECTION_CREDENTIAL_USERNAME:
348
data[i] = do_vnc_get_credential("Username: ", TRUE);
351
g_print("Failed to read username\n");
352
vnc_connection_shutdown(conn);
356
case VNC_CONNECTION_CREDENTIAL_CLIENTNAME:
357
data[i] = g_strdup("gvnccapture");
363
for (i = 0 ; i < credList->n_values ; i++) {
364
GValue *cred = g_value_array_get_nth(credList, i);
366
if (!vnc_connection_set_credential(conn,
367
g_value_get_enum(cred),
369
g_print("Failed to set credential type %d %s\n", g_value_get_enum(cred), data[i]);
370
vnc_connection_shutdown(conn);
374
g_print("Unsupported credential type %d\n", g_value_get_enum(cred));
375
vnc_connection_shutdown(conn);
380
for (i = 0 ; i < credList->n_values ; i++)
385
static void do_vnc_auth_choose_type(VncConnection *conn,
387
gpointer opaque G_GNUC_UNUSED)
391
if (!types->n_values) {
392
VNC_DEBUG("No auth types to choose from");
396
for (i = 0 ; i < G_N_ELEMENTS(preferable_auths) ; i++) {
397
int pref = preferable_auths[i];
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);
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));
416
static void show_help(const char *binary, const char *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",
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)
430
vnc_util_set_debug(TRUE);
435
int main(int argc, char **argv)
437
GOptionContext *context;
438
GError *error = NULL;
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 }
453
struct GVncCapture *capture;
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);
462
show_help(argv[0], error->message);
466
if (!args || (g_strv_length(args) != 2)) {
467
show_help(argv[0], NULL);
471
capture = g_new0(struct GVncCapture, 1);
472
capture->quiet = quiet;
474
if (args[0][0] == ':') {
475
capture->host = g_strdup("localhost");
478
capture->host = g_strdup(args[0]);
479
display = strchr(capture->host, ':');
484
capture->port = 5900 + atoi(display);
486
capture->port = 5900;
488
port = g_strdup_printf("%d", capture->port);
490
capture->conn = vnc_connection_new();
491
capture->output = g_strdup(args[1]);
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);
508
vnc_connection_open_host(capture->conn, capture->host, port);
510
capture->loop = g_main_loop_new(g_main_context_default(), FALSE);
512
g_main_loop_run(capture->loop);
514
vnc_connection_shutdown(capture->conn);
515
g_object_unref(capture->conn);
517
gdk_pixbuf_unref(capture->pixbuf);
519
ret = capture->saved ? 0 : 1;
521
g_free(capture->host);