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
30static rfbClient *cl;
31static gchar *server_cut_text = NULL;
32static gboolean framebuffer_allocated = FALSE;
33static GtkWidget *window;
34static GtkWidget *dialog_connecting = NULL;
35
36/* Redraw the screen from the backing pixmap */
37static 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
94struct { 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
101static 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
118static 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
141static 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
151void 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
159static 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
170static 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
182static void request_screen_refresh (GtkMenuItem *menuitem,
183                                    gpointer     user_data)
184{
185	SendFramebufferUpdateRequest (cl, 0, 0, cl->width, cl->height, FALSE);
186}
187
188static void send_f8 (GtkMenuItem *menuitem,
189                     gpointer     user_data)
190{
191	SendKeyEvent(cl, XK_F8, TRUE);
192	SendKeyEvent(cl, XK_F8, FALSE);
193}
194
195static 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
206static 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
238static 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
283static 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
370static 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
381void quit ()
382{
383	exit (0);
384}
385
386static 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
473static 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
487static 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 */
494static 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
513static 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
524static 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
581static 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
599static 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
639int 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