[gtk-vnc] Add a gvnccapture command line tool
- From: Daniel P. Berrange <dberrange src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk-vnc] Add a gvnccapture command line tool
- Date: Tue, 6 Jul 2010 13:37:07 +0000 (UTC)
commit eb49243fb9aeb8d23cdfe3e7960939019347019f
Author: Daniel P. Berrange <dan berrange com>
Date: Mon Jul 5 23:13:09 2010 +0100
Add a gvnccapture command line tool
Add a command line tool for capturing a screenshot of a VNC
desktop
Makefile.am | 2 +-
configure.ac | 1 +
gtk-vnc.spec.in | 17 ++-
tools/Makefile.am | 24 +++
tools/gvnccapture.c | 522 +++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 564 insertions(+), 2 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 1c77adc..4d7bb87 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = gnulib/lib src examples plugin gnulib/tests po
+SUBDIRS = gnulib/lib src tools examples plugin gnulib/tests po
ACLOCAL_AMFLAGS = -I gnulib/m4 ${ACLOCAL_FLAGS}
pkgconfig_DATA = @PACKAGE -1 0 pc gvnc-1.0.pc
diff --git a/configure.ac b/configure.ac
index c98fce7..e85ad6f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -358,6 +358,7 @@ AC_CONFIG_FILES(
gnulib/lib/Makefile
gnulib/tests/Makefile
src/Makefile
+ tools/Makefile
examples/Makefile
plugin/Makefile
po/Makefile.in
diff --git a/gtk-vnc.spec.in b/gtk-vnc.spec.in
index 94b91cf..cf97a60 100644
--- a/gtk-vnc.spec.in
+++ b/gtk-vnc.spec.in
@@ -93,6 +93,15 @@ with the raw protocol itself.
Libraries, includes, etc. to compile with the gvnc library
+%package -n gvnc-tools
+Summary: Command line VNC tools
+Group: Applications/Internet
+
+%description -n gvnc-tools
+Provides useful command line utilities for interacting with
+VNC servers. Includes the gvnccapture program for capturing
+screenshots of a VNC desktop
+
%prep
%setup -q
@@ -110,7 +119,7 @@ Libraries, includes, etc. to compile with the gvnc library
%endif
%configure %{plugin_arg} %{gir_arg}
-%__make %{?_smp_mflags}
+%__make %{?_smp_mflags} V=1
%install
rm -fr %{buildroot}
@@ -183,6 +192,12 @@ rm -fr %{buildroot}
%{_datadir}/gir-1.0/GVnc-1.0.gir
%endif
+%files -n gvnc-tools
+%defattr(-, root, root)
+%doc AUTHORS ChangeLog NEWS README COPYING.LIB
+%{_bindir}/gvnccapture
+%{_mandir}/man1/gvnccapture.1*
+
%changelog
* Thu Sep 13 2007 Daniel P. Berrange <berrange redhat com> - 0.2.0-1
- Support client cursor offload
diff --git a/tools/Makefile.am b/tools/Makefile.am
new file mode 100644
index 0000000..ac0c413
--- /dev/null
+++ b/tools/Makefile.am
@@ -0,0 +1,24 @@
+
+POD2MAN = pod2man -c "VNC Tools" -r "$(PACKAGE)-$(VERSION)"
+
+bin_PROGRAMS = gvnccapture
+
+man1_MANS = gvnccapture.1
+
+CLEANFILES = $(man1_MANS)
+
+gvnccapture.1: gvnccapture.c
+ $(AM_V_GEN)$(POD2MAN) --name 'gvnccapture' $< $@
+
+gvnccapture_SOURCES = gvnccapture.c
+gvnccapture_LDADD = \
+ ../src/libgvnc-1.0.la \
+ $(GOBJECT_LIBS) \
+ $(GDK_PIXBUF_LIBS)
+gvnccapture_CFLAGS = \
+ $(GOBJECT_CFLAGS) \
+ $(GDK_PIXBUF_CFLAGS) \
+ $(WARNING_CFLAGS) \
+ -I$(top_srcdir)/src/
+
+-include $(top_srcdir)/git.mk
diff --git a/tools/gvnccapture.c b/tools/gvnccapture.c
new file mode 100644
index 0000000..67c274a
--- /dev/null
+++ b/tools/gvnccapture.c
@@ -0,0 +1,522 @@
+/*
+ * Vnc Image Capture
+ *
+ * Copyright (C) 2010 Daniel P. Berrange <dan berrange com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.0 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+=head1 NAME
+
+gvnccapture - VNC screenshot capture
+
+=head1 SYNOPSIS
+
+gvnccapture [OPTION]... [HOST][:DISPLAY] FILENAME
+
+=head1 DESCRIPTION
+
+Capture a screenshot of the VNC desktop at HOST:DISPLAY saving to the
+image file FILENAME. If HOST is omitted it defaults to "localhost",
+if :DISPLAY is omitted, it defaults to ":1". FILENAME must end in a
+known image format extension (eg ".png", ".jpeg"). Supported options
+are
+
+=over 4
+
+=item --help, -?
+
+Display command line help information
+
+=item --quiet, -q
+
+Do not display information on the console when capturing the screenshot,
+with the exception of any password prompt.
+
+=item --debug, -d
+
+Display verbose debugging information on the console
+
+=back
+
+=head1 EXIT STATUS
+
+The exit status is 0 upon successful screen capture, otherwise
+it is a non-zero integer
+
+=head1 EXAMPLES
+
+ # gvnccapture localhost:1 desktop.png
+ Password:
+ Connected to localhost:1
+ Saved display to desktop.png
+
+=head1 AUTHORS
+
+Daniel P. Berrange <dan berrange com>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2010 Daniel P. Berrange <dan berrange com>.
+
+License LGPLv2+: GNU Lesser GPL version 2 or later <http://gnu.org/licenses/gpl.html>.
+
+This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.
+
+=head1 SEE ALSO
+
+vinagre(1)
+
+=cut
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <termios.h>
+#include <unistd.h>
+#include <glib/gi18n.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <vncconnection.h>
+#include <vncbaseframebuffer.h>
+
+struct GVncCapture {
+ gchar *host;
+ int port;
+
+ gboolean quiet;
+ gboolean saved;
+
+ VncConnection *conn;
+ GMainLoop *loop;
+ gboolean connected;
+ gchar *output;
+
+ GdkPixbuf *pixbuf;
+};
+
+static const guint preferable_auths[] = {
+ /*
+ * Both these two provide TLS based auth, and can layer
+ * all the other auth types on top. So these two must
+ * be the first listed
+ */
+ VNC_CONNECTION_AUTH_VENCRYPT,
+ VNC_CONNECTION_AUTH_TLS,
+
+ /*
+ * Then stackable auth types in order of preference
+ */
+ VNC_CONNECTION_AUTH_SASL,
+ VNC_CONNECTION_AUTH_MSLOGON,
+ VNC_CONNECTION_AUTH_VNC,
+
+ /*
+ * Or nothing at all
+ */
+ VNC_CONNECTION_AUTH_NONE
+};
+
+
+static gchar *
+do_vnc_get_credential(const gchar *prompt, gboolean doecho)
+{
+ struct termios old, new;
+ gchar *res = NULL;
+ size_t n;
+ ssize_t len;
+
+ printf("%s", prompt);
+ fflush(stdout);
+
+ /* Turn echoing off and fail if we can't. */
+ if (!doecho && tcgetattr (fileno (stdin), &old) != 0)
+ return NULL;
+ new = old;
+ new.c_lflag &= ~ECHO;
+ if (!doecho && tcsetattr(fileno(stdin), TCSAFLUSH, &new) != 0)
+ return NULL;
+
+ /* Read the password. */
+ if ((len = getline(&res, &n, stdin)) < 0)
+ res = NULL;
+
+ if (res && res[len-1] == '\n')
+ res[len-1] = '\0';
+
+ /* Restore terminal. */
+ if (!doecho) {
+ printf("\n");
+ (void) tcsetattr(fileno (stdin), TCSAFLUSH, &old);
+ }
+
+ return res;
+}
+
+
+static void do_vnc_framebuffer_update(VncConnection *conn,
+ guint16 x, guint16 y,
+ guint16 width, guint16 height,
+ gpointer opaque)
+{
+ struct GVncCapture *capture = opaque;
+
+ if (!capture->pixbuf)
+ return;
+
+ /* Crude attempt to see if we've got a complete frame by
+ * checking if we've hit the bottom-right corner. Will
+ * probably break
+ */
+ if ((x + width) == vnc_connection_get_width(conn) &&
+ (y + height) == vnc_connection_get_height(conn)) {
+ VNC_DEBUG("All done, saving to %s", capture->output);
+ gchar *ext = strrchr(capture->output, '.');
+ GError *err = NULL;
+ if (ext)
+ ext++;
+
+ if (!gdk_pixbuf_save(capture->pixbuf,
+ capture->output,
+ ext ? ext : "png",
+ &err,
+ "tEXt::Generator App", "gvnccapture", NULL)) {
+ if (!capture->quiet)
+ g_print("Unable to save display to %s: %s",
+ capture->output, err->message);
+ } else {
+ capture->saved = TRUE;
+ if (!capture->quiet)
+ g_print("Saved display to %s\n",
+ capture->output);
+ }
+ vnc_connection_shutdown(conn);
+ g_main_quit(capture->loop);
+ }
+}
+
+static void do_vnc_desktop_resize(VncConnection *conn,
+ int width, int height,
+ gpointer opaque)
+{
+ struct GVncCapture *capture = opaque;
+ const VncPixelFormat *remoteFormat;
+ VncPixelFormat localFormat = {
+ .bits_per_pixel = 32,
+ .depth = 32,
+ .byte_order = G_BYTE_ORDER,
+ .true_color_flag = TRUE,
+ .red_max = 255,
+ .green_max = 255,
+ .blue_max = 255,
+ .red_shift = 0,
+ .green_shift = 8,
+ .blue_shift = 16,
+ };
+ VncBaseFramebuffer *fb;
+
+ if (capture->pixbuf) {
+ gdk_pixbuf_unref(capture->pixbuf);
+ capture->pixbuf = NULL;
+ }
+
+ VNC_DEBUG("Resize %dx%d", width, height);
+ remoteFormat = vnc_connection_get_pixel_format(conn);
+
+ /* We'll fix our local copy as rgb888 */
+ capture->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
+ TRUE,
+ 8,
+ width,
+ height);
+ gdk_pixbuf_fill(capture->pixbuf, 0);
+
+ fb = vnc_base_framebuffer_new(gdk_pixbuf_get_pixels(capture->pixbuf),
+ gdk_pixbuf_get_width(capture->pixbuf),
+ gdk_pixbuf_get_height(capture->pixbuf),
+ gdk_pixbuf_get_rowstride(capture->pixbuf),
+ remoteFormat,
+ &localFormat);
+
+ vnc_connection_set_framebuffer(conn, VNC_FRAMEBUFFER(fb));
+
+ g_object_unref(fb);
+}
+
+
+static void do_vnc_initialized(VncConnection *conn,
+ gpointer opaque)
+{
+ struct GVncCapture *capture = opaque;
+ gint32 encodings[] = { VNC_CONNECTION_ENCODING_DESKTOP_RESIZE,
+ VNC_CONNECTION_ENCODING_ZRLE,
+ VNC_CONNECTION_ENCODING_HEXTILE,
+ VNC_CONNECTION_ENCODING_RRE,
+ VNC_CONNECTION_ENCODING_COPY_RECT,
+ VNC_CONNECTION_ENCODING_RAW };
+ gint32 *encodingsp;
+ int n_encodings;
+
+ do_vnc_desktop_resize(conn,
+ vnc_connection_get_width(conn),
+ vnc_connection_get_height(conn),
+ capture);
+
+ encodingsp = encodings;
+ n_encodings = G_N_ELEMENTS(encodings);
+
+ VNC_DEBUG("Sending %d encodings", n_encodings);
+ if (!vnc_connection_set_encodings(conn, n_encodings, encodingsp))
+ goto error;
+
+ VNC_DEBUG("Requesting first framebuffer update");
+ if (!vnc_connection_framebuffer_update_request(capture->conn,
+ 0, 0, 0,
+ vnc_connection_get_width(capture->conn),
+ vnc_connection_get_height(capture->conn)))
+ vnc_connection_shutdown(capture->conn);
+
+ if (!capture->quiet)
+ g_print("Connected to %s:%d\n", capture->host, capture->port - 5900);
+ capture->connected = TRUE;
+ return;
+
+ error:
+ vnc_connection_shutdown(conn);
+}
+
+static void do_vnc_disconnected(VncConnection *conn G_GNUC_UNUSED,
+ gpointer opaque)
+{
+ struct GVncCapture *capture = opaque;
+ if (!capture->quiet) {
+ if (capture->connected)
+ g_print("Disconnected from %s:%d\n", capture->host, capture->port - 5900);
+ else
+ g_print("Unable to connect to %s:%d\n", capture->host, capture->port - 5900);
+ }
+ g_main_quit(capture->loop);
+}
+
+static void do_vnc_auth_credential(VncConnection *conn, GValueArray *credList, gpointer opaque)
+{
+ struct GVncCapture *capture = opaque;
+ guint i;
+ char **data;
+
+ data = g_new0(char *, credList->n_values);
+
+ for (i = 0 ; i < credList->n_values ; i++) {
+ GValue *cred = g_value_array_get_nth(credList, i);
+ switch (g_value_get_enum(cred)) {
+ case VNC_CONNECTION_CREDENTIAL_PASSWORD:
+ data[i] = do_vnc_get_credential("Password: ", FALSE);
+ if (!data[i]) {
+ if (!capture->quiet)
+ g_print("Failed to read password\n");
+ vnc_connection_shutdown(conn);
+ goto cleanup;
+ }
+ break;
+ case VNC_CONNECTION_CREDENTIAL_USERNAME:
+ data[i] = do_vnc_get_credential("Username: ", TRUE);
+ if (!data[i]) {
+ if (!capture->quiet)
+ g_print("Failed to read username\n");
+ vnc_connection_shutdown(conn);
+ goto cleanup;
+ }
+ break;
+ case VNC_CONNECTION_CREDENTIAL_CLIENTNAME:
+ data[i] = g_strdup("gvnccapture");
+ break;
+ default:
+ break;
+ }
+ }
+ for (i = 0 ; i < credList->n_values ; i++) {
+ GValue *cred = g_value_array_get_nth(credList, i);
+ if (data[i]) {
+ if (!vnc_connection_set_credential(conn,
+ g_value_get_enum(cred),
+ data[i])) {
+ g_print("Failed to set credential type %d %s\n", g_value_get_enum(cred), data[i]);
+ vnc_connection_shutdown(conn);
+ }
+ } else {
+ if (!capture->quiet)
+ g_print("Unsupported credential type %d\n", g_value_get_enum(cred));
+ vnc_connection_shutdown(conn);
+ }
+ }
+
+ cleanup:
+ for (i = 0 ; i < credList->n_values ; i++)
+ g_free(data[i]);
+ g_free(data);
+}
+
+static void do_vnc_auth_choose_type(VncConnection *conn,
+ GValueArray *types,
+ gpointer opaque G_GNUC_UNUSED)
+{
+ guint i, j;
+
+ if (!types->n_values) {
+ VNC_DEBUG("No auth types to choose from");
+ return;
+ }
+
+ for (i = 0 ; i < G_N_ELEMENTS(preferable_auths) ; i++) {
+ int pref = preferable_auths[i];
+
+ for (j = 0 ; j < types->n_values ; j++) {
+ GValue *type = g_value_array_get_nth(types, j);
+ VNC_DEBUG("Compare %d vs %d", pref, g_value_get_enum(type));
+ if (pref == g_value_get_enum(type)) {
+ VNC_DEBUG("Chosen auth %d", pref);
+ vnc_connection_set_auth_type(conn, pref);
+ return;
+ }
+ }
+ }
+
+ GValue *type = g_value_array_get_nth(types, 0);
+ VNC_DEBUG("Chosen default auth %d", g_value_get_enum(type));
+ vnc_connection_set_auth_type(conn, g_value_get_enum(type));
+}
+
+
+static void show_help(const char *binary, const char *error)
+{
+ if (error)
+ g_print("%s\n\n", error);
+ g_print("Usage: %s [HOSTNAME][:DISPLAY] FILENAME\n\n", binary);
+ g_print("Run '%s --help' to see a full list of available command line options\n",
+ binary);
+}
+
+static gboolean vnc_debug_option_arg(const gchar *option_name G_GNUC_UNUSED,
+ const gchar *value G_GNUC_UNUSED,
+ gpointer data G_GNUC_UNUSED,
+ GError **error G_GNUC_UNUSED)
+{
+ vnc_util_set_debug(TRUE);
+ return TRUE;
+}
+
+
+int main(int argc, char **argv)
+{
+ GOptionContext *context;
+ GError *error = NULL;
+ gchar *display;
+ gchar *port;
+ gchar **args = NULL;
+ int ret;
+ gboolean quiet = FALSE;
+ const GOptionEntry options [] = {
+ { "debug", 'd', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
+ vnc_debug_option_arg, N_("Enables debug output"), NULL },
+ { "quiet", 'q', 0, G_OPTION_ARG_NONE,
+ &quiet, N_("Don't print any status to console"), NULL },
+ { G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_STRING_ARRAY, &args,
+ NULL, "HOSTNAME[:DISPLAY] FILENAME" },
+ { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, 0 }
+ };
+ struct GVncCapture *capture;
+
+ g_type_init();
+
+ /* Setup command line options */
+ context = g_option_context_new("- Vnc Image Capture");
+ g_option_context_add_main_entries(context, options, NULL);
+ g_option_context_parse(context, &argc, &argv, &error);
+ if (error) {
+ show_help(argv[0], error->message);
+ g_error_free(error);
+ return 1;
+ }
+ if (!args || (g_strv_length(args) != 2)) {
+ show_help(argv[0], NULL);
+ return 1;
+ }
+
+ capture = g_new0(struct GVncCapture, 1);
+ capture->quiet = quiet;
+
+ if (args[0][0] == ':') {
+ capture->host = g_strdup("localhost");
+ display = args[0];
+ } else {
+ capture->host = g_strdup(args[0]);
+ display = strchr(capture->host, ':');
+ }
+ if (display) {
+ *display = 0;
+ display++;
+ capture->port = 5900 + atoi(display);
+ } else {
+ capture->port = 5900;
+ }
+ port = g_strdup_printf("%d", capture->port);
+
+ capture->conn = vnc_connection_new();
+ capture->output = g_strdup(args[1]);
+
+ g_signal_connect(capture->conn, "vnc-initialized",
+ G_CALLBACK(do_vnc_initialized), capture);
+ g_signal_connect(capture->conn, "vnc-disconnected",
+ G_CALLBACK(do_vnc_disconnected), capture);
+ g_signal_connect(capture->conn, "vnc-auth-choose-type",
+ G_CALLBACK(do_vnc_auth_choose_type), capture);
+ g_signal_connect(capture->conn, "vnc-auth-choose-subtype",
+ G_CALLBACK(do_vnc_auth_choose_type), capture);
+ g_signal_connect(capture->conn, "vnc-auth-credential",
+ G_CALLBACK(do_vnc_auth_credential), capture);
+ g_signal_connect(capture->conn, "vnc-desktop-resize",
+ G_CALLBACK(do_vnc_desktop_resize), capture);
+ g_signal_connect(capture->conn, "vnc-framebuffer-update",
+ G_CALLBACK(do_vnc_framebuffer_update), capture);
+
+ vnc_connection_open_host(capture->conn, capture->host, port);
+
+ capture->loop = g_main_loop_new(g_main_context_default(), FALSE);
+
+ g_main_loop_run(capture->loop);
+
+ vnc_connection_shutdown(capture->conn);
+ g_object_unref(capture->conn);
+ if (capture->pixbuf)
+ gdk_pixbuf_unref(capture->pixbuf);
+
+ ret = capture->saved ? 0 : 1;
+
+ g_free(capture->host);
+ g_free(capture);
+
+ return ret;
+}
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * tab-width: 8
+ * End:
+ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]