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