Home | History | Annotate | Download | only in client_examples
      1 
      2 /*
      3  * Copyright (C) 2007 - Mateus Cesar Groess
      4  *
      5  * This program is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License as published by
      7  * the Free Software Foundation; either version 2 of the License, or
      8  * (at your option) any later version.
      9  *
     10  * This program is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  * GNU General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License
     16  * along with this program; if not, write to the Free Software
     17  * Foundation, Inc., 59 Temple Place, Suite 330,
     18  * Boston, MA 02111-1307, USA.
     19  */
     20 
     21 #include <stdlib.h>
     22 #include <gtk/gtk.h>
     23 #include <gdk/gdkkeysyms.h>
     24 #include <rfb/rfbclient.h>
     25 
     26 #ifdef LIBVNCSERVER_CONFIG_LIBVA
     27 #include <gdk/gdkx.h>
     28 #endif
     29 
     30 static rfbClient *cl;
     31 static gchar *server_cut_text = NULL;
     32 static gboolean framebuffer_allocated = FALSE;
     33 static GtkWidget *window;
     34 static GtkWidget *dialog_connecting = NULL;
     35 
     36 /* Redraw the screen from the backing pixmap */
     37 static gboolean expose_event (GtkWidget      *widget,
     38                               GdkEventExpose *event)
     39 {
     40 	static GdkImage *image = NULL;
     41 
     42 	if (framebuffer_allocated == FALSE) {
     43 
     44 		rfbClientSetClientData (cl, gtk_init, widget);
     45 
     46 		image = gdk_drawable_get_image (widget->window, 0, 0,
     47 		                                widget->allocation.width,
     48 		                                widget->allocation.height);
     49 
     50 		cl->frameBuffer= image->mem;
     51 
     52 		cl->width  = widget->allocation.width;
     53 		cl->height = widget->allocation.height;
     54 
     55 		cl->format.bitsPerPixel = image->bits_per_pixel;
     56 		cl->format.redShift     = image->visual->red_shift;
     57 		cl->format.greenShift   = image->visual->green_shift;
     58 		cl->format.blueShift    = image->visual->blue_shift;
     59 
     60 		cl->format.redMax   = (1 << image->visual->red_prec) - 1;
     61 		cl->format.greenMax = (1 << image->visual->green_prec) - 1;
     62 		cl->format.blueMax  = (1 << image->visual->blue_prec) - 1;
     63 
     64 #ifdef LIBVNCSERVER_CONFIG_LIBVA
     65 		/* Allow libvncclient to use a more efficient way
     66 		 * of putting the framebuffer on the screen when
     67 		 * using the H.264 format.
     68 		 */
     69 		cl->outputWindow = GDK_WINDOW_XID(widget->window);
     70 #endif
     71 
     72 		SetFormatAndEncodings (cl);
     73 
     74 		framebuffer_allocated = TRUE;
     75 
     76 		/* Also disable local cursor */
     77 		GdkCursor* cur = gdk_cursor_new( GDK_BLANK_CURSOR );
     78 		gdk_window_set_cursor (gtk_widget_get_window(GTK_WIDGET(window)), cur);
     79 		gdk_cursor_unref( cur );
     80 	}
     81 
     82 #ifndef LIBVNCSERVER_CONFIG_LIBVA
     83 	gdk_draw_image (GDK_DRAWABLE (widget->window),
     84 	                widget->style->fg_gc[gtk_widget_get_state(widget)],
     85 	                image,
     86 	                event->area.x, event->area.y,
     87 	                event->area.x, event->area.y,
     88 	                event->area.width, event->area.height);
     89 #endif
     90 
     91 	return FALSE;
     92 }
     93 
     94 struct { int gdk; int rfb; } buttonMapping[] = {
     95 	{ GDK_BUTTON1_MASK, rfbButton1Mask },
     96 	{ GDK_BUTTON2_MASK, rfbButton2Mask },
     97 	{ GDK_BUTTON3_MASK, rfbButton3Mask },
     98 	{ 0, 0 }
     99 };
    100 
    101 static gboolean button_event (GtkWidget      *widget,
    102                               GdkEventButton *event)
    103 {
    104 	int x, y;
    105 	GdkModifierType state;
    106 	int i, buttonMask;
    107 
    108 	gdk_window_get_pointer (event->window, &x, &y, &state);
    109 
    110 	for (buttonMask = 0, i = 0; buttonMapping[i].gdk; i++)
    111 		if (state & buttonMapping[i].gdk)
    112 			buttonMask |= buttonMapping[i].rfb;
    113 	SendPointerEvent (cl, x, y, buttonMask);
    114 
    115 	return TRUE;
    116 }
    117 
    118 static gboolean motion_notify_event (GtkWidget *widget,
    119                                      GdkEventMotion *event)
    120 {
    121 	int x, y;
    122 	GdkModifierType state;
    123 	int i, buttonMask;
    124 
    125 	if (event->is_hint)
    126 		gdk_window_get_pointer (event->window, &x, &y, &state);
    127 	else {
    128 		x = event->x;
    129 		y = event->y;
    130 		state = event->state;
    131 	}
    132 
    133 	for (buttonMask = 0, i = 0; buttonMapping[i].gdk; i++)
    134 		if (state & buttonMapping[i].gdk)
    135 			buttonMask |= buttonMapping[i].rfb;
    136 	SendPointerEvent (cl, x, y, buttonMask);
    137 
    138 	return TRUE;
    139 }
    140 
    141 static void got_cut_text (rfbClient *cl, const char *text, int textlen)
    142 {
    143 	if (server_cut_text != NULL) {
    144 		g_free (server_cut_text);
    145 		server_cut_text = NULL;
    146 	}
    147 
    148 	server_cut_text = g_strdup (text);
    149 }
    150 
    151 void received_text_from_clipboard (GtkClipboard *clipboard,
    152                                    const gchar *text,
    153                                    gpointer data)
    154 {
    155 	if (text)
    156 		SendClientCutText (cl, (char *) text, strlen (text));
    157 }
    158 
    159 static void clipboard_local_to_remote (GtkMenuItem *menuitem,
    160                                        gpointer     user_data)
    161 {
    162 	GtkClipboard *clipboard;
    163 
    164 	clipboard = gtk_widget_get_clipboard (GTK_WIDGET (menuitem),
    165 	                                      GDK_SELECTION_CLIPBOARD);
    166 	gtk_clipboard_request_text (clipboard, received_text_from_clipboard,
    167 	                            NULL);
    168 }
    169 
    170 static void clipboard_remote_to_local (GtkMenuItem *menuitem,
    171                                        gpointer     user_data)
    172 {
    173 	GtkClipboard *clipboard;
    174 
    175 	clipboard = gtk_widget_get_clipboard (GTK_WIDGET (menuitem),
    176 	                                      GDK_SELECTION_CLIPBOARD);
    177 
    178 	gtk_clipboard_set_text (clipboard, server_cut_text,
    179 	                        strlen (server_cut_text));
    180 }
    181 
    182 static void request_screen_refresh (GtkMenuItem *menuitem,
    183                                     gpointer     user_data)
    184 {
    185 	SendFramebufferUpdateRequest (cl, 0, 0, cl->width, cl->height, FALSE);
    186 }
    187 
    188 static void send_f8 (GtkMenuItem *menuitem,
    189                      gpointer     user_data)
    190 {
    191 	SendKeyEvent(cl, XK_F8, TRUE);
    192 	SendKeyEvent(cl, XK_F8, FALSE);
    193 }
    194 
    195 static void send_crtl_alt_del (GtkMenuItem *menuitem,
    196                                gpointer     user_data)
    197 {
    198 	SendKeyEvent(cl, XK_Control_L, TRUE);
    199 	SendKeyEvent(cl, XK_Alt_L, TRUE);
    200 	SendKeyEvent(cl, XK_Delete, TRUE);
    201 	SendKeyEvent(cl, XK_Alt_L, FALSE);
    202 	SendKeyEvent(cl, XK_Control_L, FALSE);
    203 	SendKeyEvent(cl, XK_Delete, FALSE);
    204 }
    205 
    206 static void show_connect_window(int argc, char **argv)
    207 {
    208 	GtkWidget *label;
    209 	char buf[256];
    210 
    211 	dialog_connecting = gtk_dialog_new_with_buttons ("VNC Viewer",
    212 	                                       NULL,
    213 	                                       GTK_DIALOG_DESTROY_WITH_PARENT,
    214 	                                       /*GTK_STOCK_CANCEL,
    215 	                                       GTK_RESPONSE_CANCEL,*/
    216 	                                       NULL);
    217 
    218 	/* FIXME: this works only when address[:port] is at end of arg list */
    219 	char *server;
    220 	if(argc==1)
    221 	    server = "localhost";
    222 	else
    223 	   server = argv[argc-1];
    224 	snprintf(buf, 255, "Connecting to %s...", server);
    225 
    226 	label = gtk_label_new (buf);
    227 	gtk_widget_show (label);
    228 
    229 	gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog_connecting)->vbox),
    230 	                   label);
    231 
    232 	gtk_widget_show (dialog_connecting);
    233 
    234 	while (gtk_events_pending ())
    235 		gtk_main_iteration ();
    236 }
    237 
    238 static void show_popup_menu()
    239 {
    240 	GtkWidget *popup_menu;
    241 	GtkWidget *menu_item;
    242 
    243 	popup_menu = gtk_menu_new ();
    244 
    245 	menu_item = gtk_menu_item_new_with_label ("Dismiss popup");
    246 	gtk_widget_show (menu_item);
    247 	gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item);
    248 
    249 	menu_item = gtk_menu_item_new_with_label ("Clipboard: local -> remote");
    250 	g_signal_connect (G_OBJECT (menu_item), "activate",
    251 	                  G_CALLBACK (clipboard_local_to_remote), NULL);
    252 	gtk_widget_show (menu_item);
    253 	gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item);
    254 
    255 	menu_item = gtk_menu_item_new_with_label ("Clipboard: local <- remote");
    256 	g_signal_connect (G_OBJECT (menu_item), "activate",
    257 	                  G_CALLBACK (clipboard_remote_to_local), NULL);
    258 	gtk_widget_show (menu_item);
    259 	gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item);
    260 
    261 	menu_item = gtk_menu_item_new_with_label ("Request refresh");
    262 	g_signal_connect (G_OBJECT (menu_item), "activate",
    263 	                  G_CALLBACK (request_screen_refresh), NULL);
    264 	gtk_widget_show (menu_item);
    265 	gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item);
    266 
    267 	menu_item = gtk_menu_item_new_with_label ("Send ctrl-alt-del");
    268 	g_signal_connect (G_OBJECT (menu_item), "activate",
    269 	                  G_CALLBACK (send_crtl_alt_del), NULL);
    270 	gtk_widget_show (menu_item);
    271 	gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item);
    272 
    273 	menu_item = gtk_menu_item_new_with_label ("Send F8");
    274 	g_signal_connect (G_OBJECT (menu_item), "activate",
    275 	                  G_CALLBACK (send_f8), NULL);
    276 	gtk_widget_show (menu_item);
    277 	gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item);
    278 
    279 	gtk_menu_popup (GTK_MENU (popup_menu), NULL, NULL, NULL, NULL, 0,
    280 	                gtk_get_current_event_time());
    281 }
    282 
    283 static rfbKeySym gdkKey2rfbKeySym(guint keyval)
    284 {
    285 	rfbKeySym k = 0;
    286 	switch(keyval) {
    287 	case GDK_BackSpace: k = XK_BackSpace; break;
    288 	case GDK_Tab: k = XK_Tab; break;
    289 	case GDK_Clear: k = XK_Clear; break;
    290 	case GDK_Return: k = XK_Return; break;
    291 	case GDK_Pause: k = XK_Pause; break;
    292 	case GDK_Escape: k = XK_Escape; break;
    293 	case GDK_space: k = XK_space; break;
    294 	case GDK_Delete: k = XK_Delete; break;
    295 	case GDK_KP_0: k = XK_KP_0; break;
    296 	case GDK_KP_1: k = XK_KP_1; break;
    297 	case GDK_KP_2: k = XK_KP_2; break;
    298 	case GDK_KP_3: k = XK_KP_3; break;
    299 	case GDK_KP_4: k = XK_KP_4; break;
    300 	case GDK_KP_5: k = XK_KP_5; break;
    301 	case GDK_KP_6: k = XK_KP_6; break;
    302 	case GDK_KP_7: k = XK_KP_7; break;
    303 	case GDK_KP_8: k = XK_KP_8; break;
    304 	case GDK_KP_9: k = XK_KP_9; break;
    305 	case GDK_KP_Decimal: k = XK_KP_Decimal; break;
    306 	case GDK_KP_Divide: k = XK_KP_Divide; break;
    307 	case GDK_KP_Multiply: k = XK_KP_Multiply; break;
    308 	case GDK_KP_Subtract: k = XK_KP_Subtract; break;
    309 	case GDK_KP_Add: k = XK_KP_Add; break;
    310 	case GDK_KP_Enter: k = XK_KP_Enter; break;
    311 	case GDK_KP_Equal: k = XK_KP_Equal; break;
    312 	case GDK_Up: k = XK_Up; break;
    313 	case GDK_Down: k = XK_Down; break;
    314 	case GDK_Right: k = XK_Right; break;
    315 	case GDK_Left: k = XK_Left; break;
    316 	case GDK_Insert: k = XK_Insert; break;
    317 	case GDK_Home: k = XK_Home; break;
    318 	case GDK_End: k = XK_End; break;
    319 	case GDK_Page_Up: k = XK_Page_Up; break;
    320 	case GDK_Page_Down: k = XK_Page_Down; break;
    321 	case GDK_F1: k = XK_F1; break;
    322 	case GDK_F2: k = XK_F2; break;
    323 	case GDK_F3: k = XK_F3; break;
    324 	case GDK_F4: k = XK_F4; break;
    325 	case GDK_F5: k = XK_F5; break;
    326 	case GDK_F6: k = XK_F6; break;
    327 	case GDK_F7: k = XK_F7; break;
    328 	case GDK_F8: k = XK_F8; break;
    329 	case GDK_F9: k = XK_F9; break;
    330 	case GDK_F10: k = XK_F10; break;
    331 	case GDK_F11: k = XK_F11; break;
    332 	case GDK_F12: k = XK_F12; break;
    333 	case GDK_F13: k = XK_F13; break;
    334 	case GDK_F14: k = XK_F14; break;
    335 	case GDK_F15: k = XK_F15; break;
    336 	case GDK_Num_Lock: k = XK_Num_Lock; break;
    337 	case GDK_Caps_Lock: k = XK_Caps_Lock; break;
    338 	case GDK_Scroll_Lock: k = XK_Scroll_Lock; break;
    339 	case GDK_Shift_R: k = XK_Shift_R; break;
    340 	case GDK_Shift_L: k = XK_Shift_L; break;
    341 	case GDK_Control_R: k = XK_Control_R; break;
    342 	case GDK_Control_L: k = XK_Control_L; break;
    343 	case GDK_Alt_R: k = XK_Alt_R; break;
    344 	case GDK_Alt_L: k = XK_Alt_L; break;
    345 	case GDK_Meta_R: k = XK_Meta_R; break;
    346 	case GDK_Meta_L: k = XK_Meta_L; break;
    347 #if 0
    348 	/* TODO: find out keysyms */
    349 	case GDK_Super_L: k = XK_LSuper; break;      /* left "windows" key */
    350 	case GDK_Super_R: k = XK_RSuper; break;      /* right "windows" key */
    351 	case GDK_Multi_key: k = XK_Compose; break;
    352 #endif
    353 	case GDK_Mode_switch: k = XK_Mode_switch; break;
    354 	case GDK_Help: k = XK_Help; break;
    355 	case GDK_Print: k = XK_Print; break;
    356 	case GDK_Sys_Req: k = XK_Sys_Req; break;
    357 	case GDK_Break: k = XK_Break; break;
    358 	default: break;
    359 	}
    360 	if (k == 0) {
    361 		if (keyval < 0x100)
    362 			k = keyval;
    363 		else
    364 			rfbClientLog ("Unknown keysym: %d\n", keyval);
    365 	}
    366 
    367 	return k;
    368 }
    369 
    370 static gboolean key_event (GtkWidget *widget, GdkEventKey *event,
    371                                  gpointer user_data)
    372 {
    373 	if ((event->type == GDK_KEY_PRESS) && (event->keyval == GDK_F8))
    374 		show_popup_menu();
    375 	else
    376 		SendKeyEvent(cl, gdkKey2rfbKeySym (event->keyval),
    377 		             (event->type == GDK_KEY_PRESS) ? TRUE : FALSE);
    378 	return FALSE;
    379 }
    380 
    381 void quit ()
    382 {
    383 	exit (0);
    384 }
    385 
    386 static rfbBool resize (rfbClient *client) {
    387 	GtkWidget *scrolled_window;
    388 	GtkWidget *drawing_area=NULL;
    389 	static char first=TRUE;
    390 	int tmp_width, tmp_height;
    391 
    392 	if (first) {
    393 		first=FALSE;
    394 
    395 		/* Create the drawing area */
    396 
    397 		drawing_area = gtk_drawing_area_new ();
    398 		gtk_widget_set_size_request (GTK_WIDGET (drawing_area),
    399 		                             client->width, client->height);
    400 
    401 		/* Signals used to handle backing pixmap */
    402 
    403 		g_signal_connect (G_OBJECT (drawing_area), "expose_event",
    404 		                  G_CALLBACK (expose_event), NULL);
    405 
    406 		/* Event signals */
    407 
    408 		g_signal_connect (G_OBJECT (drawing_area),
    409 		                  "motion-notify-event",
    410 		                  G_CALLBACK (motion_notify_event), NULL);
    411 		g_signal_connect (G_OBJECT (drawing_area),
    412 		                  "button-press-event",
    413 		                  G_CALLBACK (button_event), NULL);
    414 		g_signal_connect (G_OBJECT (drawing_area),
    415 		                  "button-release-event",
    416 		                  G_CALLBACK (button_event), NULL);
    417 
    418 		gtk_widget_set_events (drawing_area, GDK_EXPOSURE_MASK
    419 		                       | GDK_LEAVE_NOTIFY_MASK
    420 		                       | GDK_BUTTON_PRESS_MASK
    421 		                       | GDK_BUTTON_RELEASE_MASK
    422 		                       | GDK_POINTER_MOTION_MASK
    423 		                       | GDK_POINTER_MOTION_HINT_MASK);
    424 
    425 		gtk_widget_show (drawing_area);
    426 
    427 		scrolled_window = gtk_scrolled_window_new (NULL, NULL);
    428 		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
    429 		                                GTK_POLICY_AUTOMATIC,
    430 		                                GTK_POLICY_AUTOMATIC);
    431 		gtk_scrolled_window_add_with_viewport (
    432 		                  GTK_SCROLLED_WINDOW (scrolled_window),
    433 		                  drawing_area);
    434 		g_signal_connect (G_OBJECT (scrolled_window),
    435 		                  "key-press-event", G_CALLBACK (key_event),
    436 		                  NULL);
    437 		g_signal_connect (G_OBJECT (scrolled_window),
    438 		                  "key-release-event", G_CALLBACK (key_event),
    439 		                  NULL);
    440 		gtk_widget_show (scrolled_window);
    441 
    442 		window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    443 		gtk_window_set_title (GTK_WINDOW (window), client->desktopName);
    444 		gtk_container_add (GTK_CONTAINER (window), scrolled_window);
    445 		tmp_width = (int) (
    446 		            gdk_screen_get_width (gdk_screen_get_default ())
    447 		            * 0.85);
    448 		if (client->width > tmp_width) {
    449 			tmp_height = (int) (
    450 			             gdk_screen_get_height (
    451 			                     gdk_screen_get_default ())
    452 			             * 0.85);
    453 			gtk_widget_set_size_request (window,
    454 			                             tmp_width, tmp_height);
    455 		} else {
    456 			gtk_widget_set_size_request (window,
    457 			       client->width + 2,
    458 			       client->height + 2);
    459 		}
    460 
    461 		g_signal_connect (G_OBJECT (window), "destroy",
    462 		                  G_CALLBACK (quit), NULL);
    463 
    464 		gtk_widget_show (window);
    465 	} else {
    466 		gtk_widget_set_size_request (GTK_WIDGET (drawing_area),
    467 		                             client->width, client->height);
    468 	}
    469 
    470 	return TRUE;
    471 }
    472 
    473 static void update (rfbClient *cl, int x, int y, int w, int h) {
    474 	if (dialog_connecting != NULL) {
    475 		gtk_widget_destroy (dialog_connecting);
    476 		dialog_connecting = NULL;
    477 	}
    478 
    479 #ifndef LIBVNCSERVER_CONFIG_LIBVA
    480 	GtkWidget *drawing_area = rfbClientGetClientData (cl, gtk_init);
    481 
    482 	if (drawing_area != NULL)
    483 		gtk_widget_queue_draw_area (drawing_area, x, y, w, h);
    484 #endif
    485 }
    486 
    487 static void kbd_leds (rfbClient *cl, int value, int pad) {
    488         /* note: pad is for future expansion 0=unused */
    489         fprintf (stderr, "Led State= 0x%02X\n", value);
    490         fflush (stderr);
    491 }
    492 
    493 /* trivial support for textchat */
    494 static void text_chat (rfbClient *cl, int value, char *text) {
    495         switch (value) {
    496         case rfbTextChatOpen:
    497                 fprintf (stderr, "TextChat: We should open a textchat window!\n");
    498                 TextChatOpen (cl);
    499                 break;
    500         case rfbTextChatClose:
    501                 fprintf (stderr, "TextChat: We should close our window!\n");
    502                 break;
    503         case rfbTextChatFinished:
    504                 fprintf (stderr, "TextChat: We should close our window!\n");
    505                 break;
    506         default:
    507                 fprintf (stderr, "TextChat: Received \"%s\"\n", text);
    508                 break;
    509         }
    510         fflush (stderr);
    511 }
    512 
    513 static gboolean on_entry_key_press_event (GtkWidget *widget, GdkEventKey *event,
    514                                           gpointer user_data)
    515 {
    516 	if (event->keyval == GDK_Escape)
    517 		gtk_dialog_response (GTK_DIALOG(user_data), GTK_RESPONSE_REJECT);
    518 	else if (event->keyval == GDK_Return)
    519 		gtk_dialog_response (GTK_DIALOG(user_data), GTK_RESPONSE_ACCEPT);
    520 
    521 	return FALSE;
    522 }
    523 
    524 static void GtkErrorLog (const char *format, ...)
    525 {
    526 	GtkWidget *dialog, *label;
    527 	va_list args;
    528 	char buf[256];
    529 
    530 	if (dialog_connecting != NULL) {
    531 		gtk_widget_destroy (dialog_connecting);
    532 		dialog_connecting = NULL;
    533 	}
    534 
    535 	va_start (args, format);
    536 	vsnprintf (buf, 255, format, args);
    537 	va_end (args);
    538 
    539 	if (g_utf8_validate (buf, strlen (buf), NULL)) {
    540 		label = gtk_label_new (buf);
    541 	} else {
    542 		const gchar *charset;
    543 		gchar       *utf8;
    544 
    545 		(void) g_get_charset (&charset);
    546 		utf8 = g_convert_with_fallback (buf, strlen (buf), "UTF-8",
    547 		                                charset, NULL, NULL, NULL, NULL);
    548 
    549 		if (utf8) {
    550 			label = gtk_label_new (utf8);
    551 			g_free (utf8);
    552 		} else {
    553 			label = gtk_label_new (buf);
    554 			g_warning ("Message Output is not in UTF-8"
    555 			           "nor in locale charset.\n");
    556 		}
    557 	}
    558 
    559 	dialog = gtk_dialog_new_with_buttons ("Error",
    560 	                                       NULL,
    561 	                                       GTK_DIALOG_DESTROY_WITH_PARENT,
    562 	                                       GTK_STOCK_OK,
    563 	                                       GTK_RESPONSE_ACCEPT,
    564 	                                       NULL);
    565 	label = gtk_label_new (buf);
    566 	gtk_widget_show (label);
    567 
    568 	gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
    569 	                   label);
    570 	gtk_widget_show (dialog);
    571 
    572 	switch (gtk_dialog_run (GTK_DIALOG (dialog))) {
    573 	case GTK_RESPONSE_ACCEPT:
    574 		break;
    575 	default:
    576 		break;
    577 	}
    578 	gtk_widget_destroy (dialog);
    579 }
    580 
    581 static void GtkDefaultLog (const char *format, ...)
    582 {
    583 	va_list args;
    584 	char buf[256];
    585 	time_t log_clock;
    586 
    587 	va_start (args, format);
    588 
    589 	time (&log_clock);
    590 	strftime (buf, 255, "%d/%m/%Y %X ", localtime (&log_clock));
    591 	fprintf (stdout, buf);
    592 
    593 	vfprintf (stdout, format, args);
    594 	fflush (stdout);
    595 
    596 	va_end (args);
    597 }
    598 
    599 static char * get_password (rfbClient *client)
    600 {
    601 	GtkWidget *dialog, *entry;
    602 	char *password;
    603 
    604 	gtk_widget_destroy (dialog_connecting);
    605 	dialog_connecting = NULL;
    606 
    607 	dialog = gtk_dialog_new_with_buttons ("Password",
    608 	                                       NULL,
    609 	                                       GTK_DIALOG_DESTROY_WITH_PARENT,
    610 	                                       GTK_STOCK_CANCEL,
    611 	                                       GTK_RESPONSE_REJECT,
    612 	                                       GTK_STOCK_OK,
    613 	                                       GTK_RESPONSE_ACCEPT,
    614 	                                       NULL);
    615 	entry = gtk_entry_new ();
    616 	gtk_entry_set_visibility (GTK_ENTRY (entry),
    617 	                          FALSE);
    618 	g_signal_connect (GTK_OBJECT(entry), "key-press-event",
    619 	                    G_CALLBACK(on_entry_key_press_event),
    620 	                    GTK_OBJECT (dialog));
    621 	gtk_widget_show (entry);
    622 
    623 	gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
    624 	                   entry);
    625 	gtk_widget_show (dialog);
    626 
    627 	switch (gtk_dialog_run (GTK_DIALOG (dialog))) {
    628 	case GTK_RESPONSE_ACCEPT:
    629 		password = strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
    630 		break;
    631 	default:
    632 		password = NULL;
    633 		break;
    634 	}
    635 	gtk_widget_destroy (dialog);
    636 	return password;
    637 }
    638 
    639 int main (int argc, char *argv[])
    640 {
    641 	int i;
    642 	GdkImage *image;
    643 
    644 	rfbClientLog = GtkDefaultLog;
    645 	rfbClientErr = GtkErrorLog;
    646 
    647 	gtk_init (&argc, &argv);
    648 
    649 	/* create a dummy image just to make use of its properties */
    650 	image = gdk_image_new (GDK_IMAGE_FASTEST, gdk_visual_get_system(),
    651 				200, 100);
    652 
    653 	cl = rfbGetClient (image->depth / 3, 3, image->bpp);
    654 
    655 	cl->format.redShift     = image->visual->red_shift;
    656 	cl->format.greenShift   = image->visual->green_shift;
    657 	cl->format.blueShift    = image->visual->blue_shift;
    658 
    659 	cl->format.redMax   = (1 << image->visual->red_prec) - 1;
    660 	cl->format.greenMax = (1 << image->visual->green_prec) - 1;
    661 	cl->format.blueMax  = (1 << image->visual->blue_prec) - 1;
    662 
    663 	g_object_unref (image);
    664 
    665 	cl->MallocFrameBuffer = resize;
    666 	cl->canHandleNewFBSize = TRUE;
    667 	cl->GotFrameBufferUpdate = update;
    668 	cl->GotXCutText = got_cut_text;
    669 	cl->HandleKeyboardLedState = kbd_leds;
    670 	cl->HandleTextChat = text_chat;
    671 	cl->GetPassword = get_password;
    672 
    673 	show_connect_window (argc, argv);
    674 
    675 	if (!rfbInitClient (cl, &argc, argv))
    676 		return 1;
    677 
    678 	while (1) {
    679 		while (gtk_events_pending ())
    680 			gtk_main_iteration ();
    681 		i = WaitForMessage (cl, 500);
    682 		if (i < 0)
    683 			return 0;
    684 		if (i && framebuffer_allocated == TRUE)
    685 			if (!HandleRFBServerMessage(cl))
    686 				return 0;
    687 	}
    688 
    689 	gtk_main ();
    690 
    691 	return 0;
    692 }
    693 
    694