summaryrefslogtreecommitdiffstats
path: root/client_examples/gtkvncviewer.c
diff options
context:
space:
mode:
authorMateus Cesar Groess <mateuscg@gmail.com>2012-02-11 19:34:22 +0100
committerChristian Beier <dontmind@freeshell.org>2012-02-11 19:34:22 +0100
commit1078e8a8b050b5b4ebbcb011750f5dd2d8eacc37 (patch)
treec77de6a7641e39324743ca4c26fb402a52626912 /client_examples/gtkvncviewer.c
parent4ed29e0a36e043ddf53f572b84004c7fe4d40b1d (diff)
downloadlibtdevnc-1078e8a8b050b5b4ebbcb011750f5dd2d8eacc37.tar.gz
libtdevnc-1078e8a8b050b5b4ebbcb011750f5dd2d8eacc37.zip
Here is a port of SDLvncviewer to GTK+2.
I think it may encourage people to implement more features for the viewer, because a GTK GUI seems to be easier to implement than a SDL one (and it is more integrated with the major Linux Desktops out there). Signed-off-by: Christian Beier <dontmind@freeshell.org>
Diffstat (limited to 'client_examples/gtkvncviewer.c')
-rw-r--r--client_examples/gtkvncviewer.c668
1 files changed, 668 insertions, 0 deletions
diff --git a/client_examples/gtkvncviewer.c b/client_examples/gtkvncviewer.c
new file mode 100644
index 0000000..9e9e72b
--- /dev/null
+++ b/client_examples/gtkvncviewer.c
@@ -0,0 +1,668 @@
+
+/*
+ * Copyright (C) 2007 - Mateus Cesar Groess
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <rfb/rfbclient.h>
+
+static rfbClient *cl;
+static gchar *server_cut_text = NULL;
+static gboolean framebuffer_allocated = FALSE;
+
+/* Redraw the screen from the backing pixmap */
+static gboolean expose_event (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ static GdkImage *image = NULL;
+
+ if (framebuffer_allocated == FALSE) {
+
+ rfbClientSetClientData (cl, gtk_init, widget);
+
+ image = gdk_drawable_get_image (widget->window, 0, 0,
+ widget->allocation.width,
+ widget->allocation.height);
+
+ cl->frameBuffer= image->mem;
+
+ cl->width = widget->allocation.width;
+ cl->height = widget->allocation.height;
+
+ cl->format.bitsPerPixel = image->bits_per_pixel;
+ cl->format.redShift = image->visual->red_shift;
+ cl->format.greenShift = image->visual->green_shift;
+ cl->format.blueShift = image->visual->blue_shift;
+
+ cl->format.redMax = (1 << image->visual->red_prec) - 1;
+ cl->format.greenMax = (1 << image->visual->green_prec) - 1;
+ cl->format.blueMax = (1 << image->visual->blue_prec) - 1;
+
+ SetFormatAndEncodings (cl);
+
+ framebuffer_allocated = TRUE;
+ }
+
+ gdk_draw_image (GDK_DRAWABLE (widget->window),
+ widget->style->fg_gc[gtk_widget_get_state(widget)],
+ image,
+ event->area.x, event->area.y,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
+
+ return FALSE;
+}
+
+struct { int gdk; int rfb; } buttonMapping[] = {
+ { GDK_BUTTON1_MASK, rfbButton1Mask },
+ { GDK_BUTTON2_MASK, rfbButton2Mask },
+ { GDK_BUTTON3_MASK, rfbButton3Mask },
+ { 0, 0 }
+};
+
+static gboolean button_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ int x, y;
+ GdkModifierType state;
+ int i, buttonMask;
+
+ gdk_window_get_pointer (event->window, &x, &y, &state);
+
+ for (buttonMask = 0, i = 0; buttonMapping[i].gdk; i++)
+ if (state & buttonMapping[i].gdk)
+ buttonMask |= buttonMapping[i].rfb;
+ SendPointerEvent (cl, x, y, buttonMask);
+
+ return TRUE;
+}
+
+static gboolean motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ int x, y;
+ GdkModifierType state;
+ int i, buttonMask;
+
+ if (event->is_hint)
+ gdk_window_get_pointer (event->window, &x, &y, &state);
+ else {
+ x = event->x;
+ y = event->y;
+ state = event->state;
+ }
+
+ for (buttonMask = 0, i = 0; buttonMapping[i].gdk; i++)
+ if (state & buttonMapping[i].gdk)
+ buttonMask |= buttonMapping[i].rfb;
+ SendPointerEvent (cl, x, y, buttonMask);
+
+ return TRUE;
+}
+
+static void got_cut_text (rfbClient *cl, const char *text, int textlen)
+{
+ if (server_cut_text != NULL) {
+ g_free (server_cut_text);
+ server_cut_text = NULL;
+ }
+
+ server_cut_text = g_strdup (text);
+}
+
+void received_text_from_clipboard (GtkClipboard *clipboard,
+ const gchar *text,
+ gpointer data)
+{
+ if (text)
+ SendClientCutText (cl, (char *) text, strlen (text));
+}
+
+static void clipboard_local_to_remote (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+ GtkClipboard *clipboard;
+
+ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (menuitem),
+ GDK_SELECTION_CLIPBOARD);
+ gtk_clipboard_request_text (clipboard, received_text_from_clipboard,
+ NULL);
+}
+
+static void clipboard_remote_to_local (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+ GtkClipboard *clipboard;
+
+ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (menuitem),
+ GDK_SELECTION_CLIPBOARD);
+
+ gtk_clipboard_set_text (clipboard, server_cut_text,
+ strlen (server_cut_text));
+}
+
+static void request_screen_refresh (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+ SendFramebufferUpdateRequest (cl, 0, 0, cl->width, cl->height, FALSE);
+}
+
+static void send_f8 (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+ SendKeyEvent(cl, XK_F8, TRUE);
+ SendKeyEvent(cl, XK_F8, FALSE);
+}
+
+static void send_crtl_alt_del (GtkMenuItem *menuitem,
+ gpointer user_data)
+{
+ SendKeyEvent(cl, XK_Control_L, TRUE);
+ SendKeyEvent(cl, XK_Alt_L, TRUE);
+ SendKeyEvent(cl, XK_Delete, TRUE);
+ SendKeyEvent(cl, XK_Alt_L, FALSE);
+ SendKeyEvent(cl, XK_Control_L, FALSE);
+ SendKeyEvent(cl, XK_Delete, FALSE);
+}
+
+GtkWidget *dialog_connecting = NULL;
+
+static void show_connect_window(int argc, char **argv)
+{
+ GtkWidget *label;
+ char buf[256];
+
+ dialog_connecting = gtk_dialog_new_with_buttons ("VNC Viewer",
+ NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ /*GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,*/
+ NULL);
+
+ /* FIXME: this works only when address[:port] is at end of arg list */
+ char *server;
+ if(argc==1)
+ server = "localhost";
+ else
+ server = argv[argc-1];
+ snprintf(buf, 255, "Connecting to %s...", server);
+
+ label = gtk_label_new (buf);
+ gtk_widget_show (label);
+
+ gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog_connecting)->vbox),
+ label);
+
+ gtk_widget_show (dialog_connecting);
+
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+}
+
+static void show_popup_menu()
+{
+ GtkWidget *popup_menu;
+ GtkWidget *menu_item;
+
+ popup_menu = gtk_menu_new ();
+
+ menu_item = gtk_menu_item_new_with_label ("Dismiss popup");
+ gtk_widget_show (menu_item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item);
+
+ menu_item = gtk_menu_item_new_with_label ("Clipboard: local -> remote");
+ g_signal_connect (G_OBJECT (menu_item), "activate",
+ G_CALLBACK (clipboard_local_to_remote), NULL);
+ gtk_widget_show (menu_item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item);
+
+ menu_item = gtk_menu_item_new_with_label ("Clipboard: local <- remote");
+ g_signal_connect (G_OBJECT (menu_item), "activate",
+ G_CALLBACK (clipboard_remote_to_local), NULL);
+ gtk_widget_show (menu_item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item);
+
+ menu_item = gtk_menu_item_new_with_label ("Request refresh");
+ g_signal_connect (G_OBJECT (menu_item), "activate",
+ G_CALLBACK (request_screen_refresh), NULL);
+ gtk_widget_show (menu_item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item);
+
+ menu_item = gtk_menu_item_new_with_label ("Send ctrl-alt-del");
+ g_signal_connect (G_OBJECT (menu_item), "activate",
+ G_CALLBACK (send_crtl_alt_del), NULL);
+ gtk_widget_show (menu_item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item);
+
+ menu_item = gtk_menu_item_new_with_label ("Send F8");
+ g_signal_connect (G_OBJECT (menu_item), "activate",
+ G_CALLBACK (send_f8), NULL);
+ gtk_widget_show (menu_item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item);
+
+ gtk_menu_popup (GTK_MENU (popup_menu), NULL, NULL, NULL, NULL, 0,
+ gtk_get_current_event_time());
+}
+
+static rfbKeySym gdkKey2rfbKeySym(guint keyval)
+{
+ rfbKeySym k = 0;
+ switch(keyval) {
+ case GDK_BackSpace: k = XK_BackSpace; break;
+ case GDK_Tab: k = XK_Tab; break;
+ case GDK_Clear: k = XK_Clear; break;
+ case GDK_Return: k = XK_Return; break;
+ case GDK_Pause: k = XK_Pause; break;
+ case GDK_Escape: k = XK_Escape; break;
+ case GDK_space: k = XK_space; break;
+ case GDK_Delete: k = XK_Delete; break;
+ case GDK_KP_0: k = XK_KP_0; break;
+ case GDK_KP_1: k = XK_KP_1; break;
+ case GDK_KP_2: k = XK_KP_2; break;
+ case GDK_KP_3: k = XK_KP_3; break;
+ case GDK_KP_4: k = XK_KP_4; break;
+ case GDK_KP_5: k = XK_KP_5; break;
+ case GDK_KP_6: k = XK_KP_6; break;
+ case GDK_KP_7: k = XK_KP_7; break;
+ case GDK_KP_8: k = XK_KP_8; break;
+ case GDK_KP_9: k = XK_KP_9; break;
+ case GDK_KP_Decimal: k = XK_KP_Decimal; break;
+ case GDK_KP_Divide: k = XK_KP_Divide; break;
+ case GDK_KP_Multiply: k = XK_KP_Multiply; break;
+ case GDK_KP_Subtract: k = XK_KP_Subtract; break;
+ case GDK_KP_Add: k = XK_KP_Add; break;
+ case GDK_KP_Enter: k = XK_KP_Enter; break;
+ case GDK_KP_Equal: k = XK_KP_Equal; break;
+ case GDK_Up: k = XK_Up; break;
+ case GDK_Down: k = XK_Down; break;
+ case GDK_Right: k = XK_Right; break;
+ case GDK_Left: k = XK_Left; break;
+ case GDK_Insert: k = XK_Insert; break;
+ case GDK_Home: k = XK_Home; break;
+ case GDK_End: k = XK_End; break;
+ case GDK_Page_Up: k = XK_Page_Up; break;
+ case GDK_Page_Down: k = XK_Page_Down; break;
+ case GDK_F1: k = XK_F1; break;
+ case GDK_F2: k = XK_F2; break;
+ case GDK_F3: k = XK_F3; break;
+ case GDK_F4: k = XK_F4; break;
+ case GDK_F5: k = XK_F5; break;
+ case GDK_F6: k = XK_F6; break;
+ case GDK_F7: k = XK_F7; break;
+ case GDK_F8: k = XK_F8; break;
+ case GDK_F9: k = XK_F9; break;
+ case GDK_F10: k = XK_F10; break;
+ case GDK_F11: k = XK_F11; break;
+ case GDK_F12: k = XK_F12; break;
+ case GDK_F13: k = XK_F13; break;
+ case GDK_F14: k = XK_F14; break;
+ case GDK_F15: k = XK_F15; break;
+ case GDK_Num_Lock: k = XK_Num_Lock; break;
+ case GDK_Caps_Lock: k = XK_Caps_Lock; break;
+ case GDK_Scroll_Lock: k = XK_Scroll_Lock; break;
+ case GDK_Shift_R: k = XK_Shift_R; break;
+ case GDK_Shift_L: k = XK_Shift_L; break;
+ case GDK_Control_R: k = XK_Control_R; break;
+ case GDK_Control_L: k = XK_Control_L; break;
+ case GDK_Alt_R: k = XK_Alt_R; break;
+ case GDK_Alt_L: k = XK_Alt_L; break;
+ case GDK_Meta_R: k = XK_Meta_R; break;
+ case GDK_Meta_L: k = XK_Meta_L; break;
+#if 0
+ /* TODO: find out keysyms */
+ case GDK_Super_L: k = XK_LSuper; break; /* left "windows" key */
+ case GDK_Super_R: k = XK_RSuper; break; /* right "windows" key */
+ case GDK_Multi_key: k = XK_Compose; break;
+#endif
+ case GDK_Mode_switch: k = XK_Mode_switch; break;
+ case GDK_Help: k = XK_Help; break;
+ case GDK_Print: k = XK_Print; break;
+ case GDK_Sys_Req: k = XK_Sys_Req; break;
+ case GDK_Break: k = XK_Break; break;
+ default: break;
+ }
+ if (k == 0) {
+ if (keyval < 0x100)
+ k = keyval;
+ else
+ rfbClientLog ("Unknown keysym: %d\n", keyval);
+ }
+
+ return k;
+}
+
+static gboolean key_event (GtkWidget *widget, GdkEventKey *event,
+ gpointer user_data)
+{
+ if ((event->type == GDK_KEY_PRESS) && (event->keyval == GDK_F8))
+ show_popup_menu();
+ else
+ SendKeyEvent(cl, gdkKey2rfbKeySym (event->keyval),
+ (event->type == GDK_KEY_PRESS) ? TRUE : FALSE);
+ return FALSE;
+}
+
+void quit ()
+{
+ exit (0);
+}
+
+static rfbBool resize (rfbClient *client) {
+ GtkWidget *window;
+ GtkWidget *scrolled_window;
+ GtkWidget *drawing_area=NULL;
+ static char first=TRUE;
+ int tmp_width, tmp_height;
+
+ if (first) {
+ first=FALSE;
+
+ /* Create the drawing area */
+
+ drawing_area = gtk_drawing_area_new ();
+ gtk_widget_set_size_request (GTK_WIDGET (drawing_area),
+ client->width, client->height);
+
+ /* Signals used to handle backing pixmap */
+
+ g_signal_connect (G_OBJECT (drawing_area), "expose_event",
+ G_CALLBACK (expose_event), NULL);
+
+ /* Event signals */
+
+ g_signal_connect (G_OBJECT (drawing_area),
+ "motion-notify-event",
+ G_CALLBACK (motion_notify_event), NULL);
+ g_signal_connect (G_OBJECT (drawing_area),
+ "button-press-event",
+ G_CALLBACK (button_event), NULL);
+ g_signal_connect (G_OBJECT (drawing_area),
+ "button-release-event",
+ G_CALLBACK (button_event), NULL);
+
+ gtk_widget_set_events (drawing_area, GDK_EXPOSURE_MASK
+ | GDK_LEAVE_NOTIFY_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_BUTTON_RELEASE_MASK
+ | GDK_POINTER_MOTION_MASK
+ | GDK_POINTER_MOTION_HINT_MASK);
+
+ gtk_widget_show (drawing_area);
+
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_add_with_viewport (
+ GTK_SCROLLED_WINDOW (scrolled_window),
+ drawing_area);
+ g_signal_connect (G_OBJECT (scrolled_window),
+ "key-press-event", G_CALLBACK (key_event),
+ NULL);
+ g_signal_connect (G_OBJECT (scrolled_window),
+ "key-release-event", G_CALLBACK (key_event),
+ NULL);
+ gtk_widget_show (scrolled_window);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (window), client->desktopName);
+ gtk_container_add (GTK_CONTAINER (window), scrolled_window);
+ tmp_width = (int) (
+ gdk_screen_get_width (gdk_screen_get_default ())
+ * 0.85);
+ if (client->width > tmp_width) {
+ tmp_height = (int) (
+ gdk_screen_get_height (
+ gdk_screen_get_default ())
+ * 0.85);
+ gtk_widget_set_size_request (window,
+ tmp_width, tmp_height);
+ } else {
+ gtk_widget_set_size_request (window,
+ client->width + 2,
+ client->height + 2);
+ }
+
+ g_signal_connect (G_OBJECT (window), "destroy",
+ G_CALLBACK (quit), NULL);
+
+ gtk_widget_show (window);
+ } else {
+ gtk_widget_set_size_request (GTK_WIDGET (drawing_area),
+ client->width, client->height);
+ }
+
+ return TRUE;
+}
+
+static void update (rfbClient *cl, int x, int y, int w, int h) {
+ GtkWidget *drawing_area = rfbClientGetClientData (cl, gtk_init);
+
+ if (drawing_area != NULL)
+ gtk_widget_queue_draw_area (drawing_area, x, y, w, h);
+}
+
+static void kbd_leds (rfbClient *cl, int value, int pad) {
+ /* note: pad is for future expansion 0=unused */
+ fprintf (stderr, "Led State= 0x%02X\n", value);
+ fflush (stderr);
+}
+
+/* trivial support for textchat */
+static void text_chat (rfbClient *cl, int value, char *text) {
+ switch (value) {
+ case rfbTextChatOpen:
+ fprintf (stderr, "TextChat: We should open a textchat window!\n");
+ TextChatOpen (cl);
+ break;
+ case rfbTextChatClose:
+ fprintf (stderr, "TextChat: We should close our window!\n");
+ break;
+ case rfbTextChatFinished:
+ fprintf (stderr, "TextChat: We should close our window!\n");
+ break;
+ default:
+ fprintf (stderr, "TextChat: Received \"%s\"\n", text);
+ break;
+ }
+ fflush (stderr);
+}
+
+static gboolean on_entry_key_press_event (GtkWidget *widget, GdkEventKey *event,
+ gpointer user_data)
+{
+ if (event->keyval == GDK_Escape)
+ gtk_dialog_response (GTK_DIALOG(user_data), GTK_RESPONSE_REJECT);
+ else if (event->keyval == GDK_Return)
+ gtk_dialog_response (GTK_DIALOG(user_data), GTK_RESPONSE_ACCEPT);
+
+ return FALSE;
+}
+
+static void GtkErrorLog (const char *format, ...)
+{
+ GtkWidget *dialog, *label;
+ va_list args;
+ char buf[256];
+
+ if (dialog_connecting != NULL) {
+ gtk_widget_destroy (dialog_connecting);
+ dialog_connecting = NULL;
+ }
+
+ va_start (args, format);
+ vsnprintf (buf, 255, format, args);
+ va_end (args);
+
+ if (g_utf8_validate (buf, strlen (buf), NULL)) {
+ label = gtk_label_new (buf);
+ } else {
+ const gchar *charset;
+ gchar *utf8;
+
+ (void) g_get_charset (&charset);
+ utf8 = g_convert_with_fallback (buf, strlen (buf), "UTF-8",
+ charset, NULL, NULL, NULL, NULL);
+
+ if (utf8) {
+ label = gtk_label_new (utf8);
+ g_free (utf8);
+ } else {
+ label = gtk_label_new (buf);
+ g_warning ("Message Output is not in UTF-8"
+ "nor in locale charset.\n");
+ }
+ }
+
+ dialog = gtk_dialog_new_with_buttons ("Error",
+ NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_OK,
+ GTK_RESPONSE_ACCEPT,
+ NULL);
+ label = gtk_label_new (buf);
+ gtk_widget_show (label);
+
+ gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
+ label);
+ gtk_widget_show (dialog);
+
+ switch (gtk_dialog_run (GTK_DIALOG (dialog))) {
+ case GTK_RESPONSE_ACCEPT:
+ break;
+ default:
+ break;
+ }
+ gtk_widget_destroy (dialog);
+}
+
+static void GtkDefaultLog (const char *format, ...)
+{
+ va_list args;
+ char buf[256];
+ time_t log_clock;
+
+ va_start (args, format);
+
+ time (&log_clock);
+ strftime (buf, 255, "%d/%m/%Y %X ", localtime (&log_clock));
+ fprintf (stdout, buf);
+
+ vfprintf (stdout, format, args);
+ fflush (stdout);
+
+ va_end (args);
+}
+
+static char * get_password (rfbClient *client)
+{
+ GtkWidget *dialog, *entry;
+ char *password;
+
+ gtk_widget_destroy (dialog_connecting);
+ dialog_connecting = NULL;
+
+ dialog = gtk_dialog_new_with_buttons ("Password",
+ NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_REJECT,
+ GTK_STOCK_OK,
+ GTK_RESPONSE_ACCEPT,
+ NULL);
+ entry = gtk_entry_new ();
+ gtk_entry_set_visibility (GTK_ENTRY (entry),
+ FALSE);
+ g_signal_connect (GTK_OBJECT(entry), "key-press-event",
+ G_CALLBACK(on_entry_key_press_event),
+ GTK_OBJECT (dialog));
+ gtk_widget_show (entry);
+
+ gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
+ entry);
+ gtk_widget_show (dialog);
+
+ switch (gtk_dialog_run (GTK_DIALOG (dialog))) {
+ case GTK_RESPONSE_ACCEPT:
+ password = strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
+ break;
+ default:
+ password = NULL;
+ break;
+ }
+ gtk_widget_destroy (dialog);
+ return password;
+}
+
+int main (int argc, char *argv[])
+{
+ int i;
+ GdkImage *image;
+
+ rfbClientLog = GtkDefaultLog;
+ rfbClientErr = GtkErrorLog;
+
+ gtk_init (&argc, &argv);
+
+ /* create a dummy image just to make use of its properties */
+ image = gdk_image_new (GDK_IMAGE_FASTEST, gdk_visual_get_system(),
+ 200, 100);
+
+ cl = rfbGetClient (image->depth / 3, 3, image->bpp);
+
+ cl->format.redShift = image->visual->red_shift;
+ cl->format.greenShift = image->visual->green_shift;
+ cl->format.blueShift = image->visual->blue_shift;
+
+ cl->format.redMax = (1 << image->visual->red_prec) - 1;
+ cl->format.greenMax = (1 << image->visual->green_prec) - 1;
+ cl->format.blueMax = (1 << image->visual->blue_prec) - 1;
+
+ g_object_unref (image);
+
+ cl->MallocFrameBuffer = resize;
+ cl->canHandleNewFBSize = TRUE;
+ cl->GotFrameBufferUpdate = update;
+ cl->GotXCutText = got_cut_text;
+ cl->HandleKeyboardLedState = kbd_leds;
+ cl->HandleTextChat = text_chat;
+ cl->GetPassword = get_password;
+
+ show_connect_window (argc, argv);
+
+ if (!rfbInitClient (cl, &argc, argv))
+ return 1;
+
+ while (1) {
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+ i = WaitForMessage (cl, 500);
+ if (i < 0)
+ return 0;
+ if (i && framebuffer_allocated == TRUE)
+ if (!HandleRFBServerMessage(cl))
+ return 0;
+ }
+
+ gtk_main ();
+
+ return 0;
+}
+