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