1 /** 2 * @example SDLvncviewer.c 3 */ 4 5 #include <SDL.h> 6 #include <signal.h> 7 #include <rfb/rfbclient.h> 8 #include "scrap.h" 9 10 struct { int sdl; int rfb; } buttonMapping[]={ 11 {1, rfbButton1Mask}, 12 {2, rfbButton2Mask}, 13 {3, rfbButton3Mask}, 14 {4, rfbButton4Mask}, 15 {5, rfbButton5Mask}, 16 {0,0} 17 }; 18 19 static int enableResizable = 1, viewOnly, listenLoop, buttonMask; 20 #ifdef SDL_ASYNCBLIT 21 int sdlFlags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL; 22 #else 23 int sdlFlags = SDL_HWSURFACE | SDL_HWACCEL; 24 #endif 25 static int realWidth, realHeight, bytesPerPixel, rowStride; 26 static char *sdlPixels; 27 28 static int rightAltKeyDown, leftAltKeyDown; 29 30 static rfbBool resize(rfbClient* client) { 31 int width=client->width,height=client->height, 32 depth=client->format.bitsPerPixel; 33 34 if (enableResizable) 35 sdlFlags |= SDL_RESIZABLE; 36 37 client->updateRect.x = client->updateRect.y = 0; 38 client->updateRect.w = width; client->updateRect.h = height; 39 rfbBool okay=SDL_VideoModeOK(width,height,depth,sdlFlags); 40 if(!okay) 41 for(depth=24;!okay && depth>4;depth/=2) 42 okay=SDL_VideoModeOK(width,height,depth,sdlFlags); 43 if(okay) { 44 SDL_Surface* sdl=SDL_SetVideoMode(width,height,depth,sdlFlags); 45 rfbClientSetClientData(client, SDL_Init, sdl); 46 client->width = sdl->pitch / (depth / 8); 47 if (sdlPixels) { 48 free(client->frameBuffer); 49 sdlPixels = NULL; 50 } 51 client->frameBuffer=sdl->pixels; 52 53 client->format.bitsPerPixel=depth; 54 client->format.redShift=sdl->format->Rshift; 55 client->format.greenShift=sdl->format->Gshift; 56 client->format.blueShift=sdl->format->Bshift; 57 client->format.redMax=sdl->format->Rmask>>client->format.redShift; 58 client->format.greenMax=sdl->format->Gmask>>client->format.greenShift; 59 client->format.blueMax=sdl->format->Bmask>>client->format.blueShift; 60 SetFormatAndEncodings(client); 61 62 } else { 63 SDL_Surface* sdl=rfbClientGetClientData(client, SDL_Init); 64 rfbClientLog("Could not set resolution %dx%d!\n", 65 client->width,client->height); 66 if(sdl) { 67 client->width=sdl->pitch / (depth / 8); 68 client->height=sdl->h; 69 } else { 70 client->width=0; 71 client->height=0; 72 } 73 return FALSE; 74 } 75 SDL_WM_SetCaption(client->desktopName, "SDL"); 76 return TRUE; 77 } 78 79 static rfbKeySym SDL_key2rfbKeySym(SDL_KeyboardEvent* e) { 80 rfbKeySym k = 0; 81 SDLKey sym = e->keysym.sym; 82 83 switch (sym) { 84 case SDLK_BACKSPACE: k = XK_BackSpace; break; 85 case SDLK_TAB: k = XK_Tab; break; 86 case SDLK_CLEAR: k = XK_Clear; break; 87 case SDLK_RETURN: k = XK_Return; break; 88 case SDLK_PAUSE: k = XK_Pause; break; 89 case SDLK_ESCAPE: k = XK_Escape; break; 90 case SDLK_SPACE: k = XK_space; break; 91 case SDLK_DELETE: k = XK_Delete; break; 92 case SDLK_KP0: k = XK_KP_0; break; 93 case SDLK_KP1: k = XK_KP_1; break; 94 case SDLK_KP2: k = XK_KP_2; break; 95 case SDLK_KP3: k = XK_KP_3; break; 96 case SDLK_KP4: k = XK_KP_4; break; 97 case SDLK_KP5: k = XK_KP_5; break; 98 case SDLK_KP6: k = XK_KP_6; break; 99 case SDLK_KP7: k = XK_KP_7; break; 100 case SDLK_KP8: k = XK_KP_8; break; 101 case SDLK_KP9: k = XK_KP_9; break; 102 case SDLK_KP_PERIOD: k = XK_KP_Decimal; break; 103 case SDLK_KP_DIVIDE: k = XK_KP_Divide; break; 104 case SDLK_KP_MULTIPLY: k = XK_KP_Multiply; break; 105 case SDLK_KP_MINUS: k = XK_KP_Subtract; break; 106 case SDLK_KP_PLUS: k = XK_KP_Add; break; 107 case SDLK_KP_ENTER: k = XK_KP_Enter; break; 108 case SDLK_KP_EQUALS: k = XK_KP_Equal; break; 109 case SDLK_UP: k = XK_Up; break; 110 case SDLK_DOWN: k = XK_Down; break; 111 case SDLK_RIGHT: k = XK_Right; break; 112 case SDLK_LEFT: k = XK_Left; break; 113 case SDLK_INSERT: k = XK_Insert; break; 114 case SDLK_HOME: k = XK_Home; break; 115 case SDLK_END: k = XK_End; break; 116 case SDLK_PAGEUP: k = XK_Page_Up; break; 117 case SDLK_PAGEDOWN: k = XK_Page_Down; break; 118 case SDLK_F1: k = XK_F1; break; 119 case SDLK_F2: k = XK_F2; break; 120 case SDLK_F3: k = XK_F3; break; 121 case SDLK_F4: k = XK_F4; break; 122 case SDLK_F5: k = XK_F5; break; 123 case SDLK_F6: k = XK_F6; break; 124 case SDLK_F7: k = XK_F7; break; 125 case SDLK_F8: k = XK_F8; break; 126 case SDLK_F9: k = XK_F9; break; 127 case SDLK_F10: k = XK_F10; break; 128 case SDLK_F11: k = XK_F11; break; 129 case SDLK_F12: k = XK_F12; break; 130 case SDLK_F13: k = XK_F13; break; 131 case SDLK_F14: k = XK_F14; break; 132 case SDLK_F15: k = XK_F15; break; 133 case SDLK_NUMLOCK: k = XK_Num_Lock; break; 134 case SDLK_CAPSLOCK: k = XK_Caps_Lock; break; 135 case SDLK_SCROLLOCK: k = XK_Scroll_Lock; break; 136 case SDLK_RSHIFT: k = XK_Shift_R; break; 137 case SDLK_LSHIFT: k = XK_Shift_L; break; 138 case SDLK_RCTRL: k = XK_Control_R; break; 139 case SDLK_LCTRL: k = XK_Control_L; break; 140 case SDLK_RALT: k = XK_Alt_R; break; 141 case SDLK_LALT: k = XK_Alt_L; break; 142 case SDLK_RMETA: k = XK_Meta_R; break; 143 case SDLK_LMETA: k = XK_Meta_L; break; 144 case SDLK_LSUPER: k = XK_Super_L; break; 145 case SDLK_RSUPER: k = XK_Super_R; break; 146 #if 0 147 case SDLK_COMPOSE: k = XK_Compose; break; 148 #endif 149 case SDLK_MODE: k = XK_Mode_switch; break; 150 case SDLK_HELP: k = XK_Help; break; 151 case SDLK_PRINT: k = XK_Print; break; 152 case SDLK_SYSREQ: k = XK_Sys_Req; break; 153 case SDLK_BREAK: k = XK_Break; break; 154 default: break; 155 } 156 /* both SDL and X11 keysyms match ASCII in the range 0x01-0x7f */ 157 if (k == 0 && sym > 0x0 && sym < 0x100) { 158 k = sym; 159 if (e->keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) { 160 if (k >= '1' && k <= '9') 161 k &= ~0x10; 162 else if (k >= 'a' && k <= 'f') 163 k &= ~0x20; 164 } 165 } 166 if (k == 0) { 167 if (e->keysym.unicode < 0x100) 168 k = e->keysym.unicode; 169 else 170 rfbClientLog("Unknown keysym: %d\n", sym); 171 } 172 173 return k; 174 } 175 176 static uint32_t get(rfbClient *cl, int x, int y) 177 { 178 switch (bytesPerPixel) { 179 case 1: return ((uint8_t *)cl->frameBuffer)[x + y * cl->width]; 180 case 2: return ((uint16_t *)cl->frameBuffer)[x + y * cl->width]; 181 case 4: return ((uint32_t *)cl->frameBuffer)[x + y * cl->width]; 182 default: 183 rfbClientErr("Unknown bytes/pixel: %d", bytesPerPixel); 184 exit(1); 185 } 186 } 187 188 static void put(int x, int y, uint32_t v) 189 { 190 switch (bytesPerPixel) { 191 case 1: ((uint8_t *)sdlPixels)[x + y * rowStride] = v; break; 192 case 2: ((uint16_t *)sdlPixels)[x + y * rowStride] = v; break; 193 case 4: ((uint32_t *)sdlPixels)[x + y * rowStride] = v; break; 194 default: 195 rfbClientErr("Unknown bytes/pixel: %d", bytesPerPixel); 196 exit(1); 197 } 198 } 199 200 static void resizeRectangleToReal(rfbClient *cl, int x, int y, int w, int h) 201 { 202 int i0 = x * realWidth / cl->width; 203 int i1 = ((x + w) * realWidth - 1) / cl->width + 1; 204 int j0 = y * realHeight / cl->height; 205 int j1 = ((y + h) * realHeight - 1) / cl->height + 1; 206 int i, j; 207 208 for (j = j0; j < j1; j++) 209 for (i = i0; i < i1; i++) { 210 int x0 = i * cl->width / realWidth; 211 int x1 = ((i + 1) * cl->width - 1) / realWidth + 1; 212 int y0 = j * cl->height / realHeight; 213 int y1 = ((j + 1) * cl->height - 1) / realHeight + 1; 214 uint32_t r = 0, g = 0, b = 0; 215 216 for (y = y0; y < y1; y++) 217 for (x = x0; x < x1; x++) { 218 uint32_t v = get(cl, x, y); 219 #define REDSHIFT cl->format.redShift 220 #define REDMAX cl->format.redMax 221 #define GREENSHIFT cl->format.greenShift 222 #define GREENMAX cl->format.greenMax 223 #define BLUESHIFT cl->format.blueShift 224 #define BLUEMAX cl->format.blueMax 225 r += (v >> REDSHIFT) & REDMAX; 226 g += (v >> GREENSHIFT) & GREENMAX; 227 b += (v >> BLUESHIFT) & BLUEMAX; 228 } 229 r /= (x1 - x0) * (y1 - y0); 230 g /= (x1 - x0) * (y1 - y0); 231 b /= (x1 - x0) * (y1 - y0); 232 233 put(i, j, (r << REDSHIFT) | (g << GREENSHIFT) | 234 (b << BLUESHIFT)); 235 } 236 } 237 238 static void update(rfbClient* cl,int x,int y,int w,int h) { 239 if (sdlPixels) { 240 resizeRectangleToReal(cl, x, y, w, h); 241 w = ((x + w) * realWidth - 1) / cl->width + 1; 242 h = ((y + h) * realHeight - 1) / cl->height + 1; 243 x = x * realWidth / cl->width; 244 y = y * realHeight / cl->height; 245 w -= x; 246 h -= y; 247 } 248 SDL_UpdateRect(rfbClientGetClientData(cl, SDL_Init), x, y, w, h); 249 } 250 251 static void setRealDimension(rfbClient *client, int w, int h) 252 { 253 SDL_Surface* sdl; 254 255 if (w < 0) { 256 const SDL_VideoInfo *info = SDL_GetVideoInfo(); 257 w = info->current_h; 258 h = info->current_w; 259 } 260 261 if (w == realWidth && h == realHeight) 262 return; 263 264 if (!sdlPixels) { 265 int size; 266 267 sdlPixels = (char *)client->frameBuffer; 268 rowStride = client->width; 269 270 bytesPerPixel = client->format.bitsPerPixel / 8; 271 size = client->width * bytesPerPixel * client->height; 272 client->frameBuffer = malloc(size); 273 if (!client->frameBuffer) { 274 rfbClientErr("Could not allocate %d bytes", size); 275 exit(1); 276 } 277 memcpy(client->frameBuffer, sdlPixels, size); 278 } 279 280 sdl = rfbClientGetClientData(client, SDL_Init); 281 if (sdl->w != w || sdl->h != h) { 282 int depth = sdl->format->BitsPerPixel; 283 sdl = SDL_SetVideoMode(w, h, depth, sdlFlags); 284 rfbClientSetClientData(client, SDL_Init, sdl); 285 sdlPixels = sdl->pixels; 286 rowStride = sdl->pitch / (depth / 8); 287 } 288 289 realWidth = w; 290 realHeight = h; 291 update(client, 0, 0, client->width, client->height); 292 } 293 294 static void kbd_leds(rfbClient* cl, int value, int pad) { 295 /* note: pad is for future expansion 0=unused */ 296 fprintf(stderr,"Led State= 0x%02X\n", value); 297 fflush(stderr); 298 } 299 300 /* trivial support for textchat */ 301 static void text_chat(rfbClient* cl, int value, char *text) { 302 switch(value) { 303 case rfbTextChatOpen: 304 fprintf(stderr,"TextChat: We should open a textchat window!\n"); 305 TextChatOpen(cl); 306 break; 307 case rfbTextChatClose: 308 fprintf(stderr,"TextChat: We should close our window!\n"); 309 break; 310 case rfbTextChatFinished: 311 fprintf(stderr,"TextChat: We should close our window!\n"); 312 break; 313 default: 314 fprintf(stderr,"TextChat: Received \"%s\"\n", text); 315 break; 316 } 317 fflush(stderr); 318 } 319 320 #ifdef __MINGW32__ 321 #define LOG_TO_FILE 322 #endif 323 324 #ifdef LOG_TO_FILE 325 #include <stdarg.h> 326 static void 327 log_to_file(const char *format, ...) 328 { 329 FILE* logfile; 330 static char* logfile_str=0; 331 va_list args; 332 char buf[256]; 333 time_t log_clock; 334 335 if(!rfbEnableClientLogging) 336 return; 337 338 if(logfile_str==0) { 339 logfile_str=getenv("VNCLOG"); 340 if(logfile_str==0) 341 logfile_str="vnc.log"; 342 } 343 344 logfile=fopen(logfile_str,"a"); 345 346 va_start(args, format); 347 348 time(&log_clock); 349 strftime(buf, 255, "%d/%m/%Y %X ", localtime(&log_clock)); 350 fprintf(logfile,buf); 351 352 vfprintf(logfile, format, args); 353 fflush(logfile); 354 355 va_end(args); 356 fclose(logfile); 357 } 358 #endif 359 360 361 static void cleanup(rfbClient* cl) 362 { 363 /* 364 just in case we're running in listenLoop: 365 close viewer window by restarting SDL video subsystem 366 */ 367 SDL_QuitSubSystem(SDL_INIT_VIDEO); 368 SDL_InitSubSystem(SDL_INIT_VIDEO); 369 if(cl) 370 rfbClientCleanup(cl); 371 } 372 373 374 static rfbBool handleSDLEvent(rfbClient *cl, SDL_Event *e) 375 { 376 switch(e->type) { 377 #if SDL_MAJOR_VERSION > 1 || SDL_MINOR_VERSION >= 2 378 case SDL_VIDEOEXPOSE: 379 SendFramebufferUpdateRequest(cl, 0, 0, 380 cl->width, cl->height, FALSE); 381 break; 382 #endif 383 case SDL_MOUSEBUTTONUP: 384 case SDL_MOUSEBUTTONDOWN: 385 case SDL_MOUSEMOTION: 386 { 387 int x, y, state, i; 388 if (viewOnly) 389 break; 390 391 if (e->type == SDL_MOUSEMOTION) { 392 x = e->motion.x; 393 y = e->motion.y; 394 state = e->motion.state; 395 } 396 else { 397 x = e->button.x; 398 y = e->button.y; 399 state = e->button.button; 400 for (i = 0; buttonMapping[i].sdl; i++) 401 if (state == buttonMapping[i].sdl) { 402 state = buttonMapping[i].rfb; 403 if (e->type == SDL_MOUSEBUTTONDOWN) 404 buttonMask |= state; 405 else 406 buttonMask &= ~state; 407 break; 408 } 409 } 410 if (sdlPixels) { 411 x = x * cl->width / realWidth; 412 y = y * cl->height / realHeight; 413 } 414 SendPointerEvent(cl, x, y, buttonMask); 415 buttonMask &= ~(rfbButton4Mask | rfbButton5Mask); 416 break; 417 } 418 case SDL_KEYUP: 419 case SDL_KEYDOWN: 420 if (viewOnly) 421 break; 422 SendKeyEvent(cl, SDL_key2rfbKeySym(&e->key), 423 e->type == SDL_KEYDOWN ? TRUE : FALSE); 424 if (e->key.keysym.sym == SDLK_RALT) 425 rightAltKeyDown = e->type == SDL_KEYDOWN; 426 if (e->key.keysym.sym == SDLK_LALT) 427 leftAltKeyDown = e->type == SDL_KEYDOWN; 428 break; 429 case SDL_QUIT: 430 if(listenLoop) 431 { 432 cleanup(cl); 433 return FALSE; 434 } 435 else 436 { 437 rfbClientCleanup(cl); 438 exit(0); 439 } 440 case SDL_ACTIVEEVENT: 441 if (!e->active.gain && rightAltKeyDown) { 442 SendKeyEvent(cl, XK_Alt_R, FALSE); 443 rightAltKeyDown = FALSE; 444 rfbClientLog("released right Alt key\n"); 445 } 446 if (!e->active.gain && leftAltKeyDown) { 447 SendKeyEvent(cl, XK_Alt_L, FALSE); 448 leftAltKeyDown = FALSE; 449 rfbClientLog("released left Alt key\n"); 450 } 451 452 if (e->active.gain && lost_scrap()) { 453 static char *data = NULL; 454 static int len = 0; 455 get_scrap(T('T', 'E', 'X', 'T'), &len, &data); 456 if (len) 457 SendClientCutText(cl, data, len); 458 } 459 break; 460 case SDL_SYSWMEVENT: 461 clipboard_filter(e); 462 break; 463 case SDL_VIDEORESIZE: 464 setRealDimension(cl, e->resize.w, e->resize.h); 465 break; 466 default: 467 rfbClientLog("ignore SDL event: 0x%x\n", e->type); 468 } 469 return TRUE; 470 } 471 472 static void got_selection(rfbClient *cl, const char *text, int len) 473 { 474 put_scrap(T('T', 'E', 'X', 'T'), len, text); 475 } 476 477 478 #ifdef mac 479 #define main SDLmain 480 #endif 481 482 int main(int argc,char** argv) { 483 rfbClient* cl; 484 int i, j; 485 SDL_Event e; 486 487 #ifdef LOG_TO_FILE 488 rfbClientLog=rfbClientErr=log_to_file; 489 #endif 490 491 for (i = 1, j = 1; i < argc; i++) 492 if (!strcmp(argv[i], "-viewonly")) 493 viewOnly = 1; 494 else if (!strcmp(argv[i], "-resizable")) 495 enableResizable = 1; 496 else if (!strcmp(argv[i], "-no-resizable")) 497 enableResizable = 0; 498 else if (!strcmp(argv[i], "-listen")) { 499 listenLoop = 1; 500 argv[i] = "-listennofork"; 501 ++j; 502 } 503 else { 504 if (i != j) 505 argv[j] = argv[i]; 506 j++; 507 } 508 argc = j; 509 510 SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE); 511 SDL_EnableUNICODE(1); 512 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, 513 SDL_DEFAULT_REPEAT_INTERVAL); 514 atexit(SDL_Quit); 515 signal(SIGINT, exit); 516 517 do { 518 /* 16-bit: cl=rfbGetClient(5,3,2); */ 519 cl=rfbGetClient(8,3,4); 520 cl->MallocFrameBuffer=resize; 521 cl->canHandleNewFBSize = TRUE; 522 cl->GotFrameBufferUpdate=update; 523 cl->HandleKeyboardLedState=kbd_leds; 524 cl->HandleTextChat=text_chat; 525 cl->GotXCutText = got_selection; 526 cl->listenPort = LISTEN_PORT_OFFSET; 527 cl->listen6Port = LISTEN_PORT_OFFSET; 528 if(!rfbInitClient(cl,&argc,argv)) 529 { 530 cl = NULL; /* rfbInitClient has already freed the client struct */ 531 cleanup(cl); 532 break; 533 } 534 535 init_scrap(); 536 537 while(1) { 538 if(SDL_PollEvent(&e)) { 539 /* 540 handleSDLEvent() return 0 if user requested window close. 541 In this case, handleSDLEvent() will have called cleanup(). 542 */ 543 if(!handleSDLEvent(cl, &e)) 544 break; 545 } 546 else { 547 i=WaitForMessage(cl,500); 548 if(i<0) 549 { 550 cleanup(cl); 551 break; 552 } 553 if(i) 554 if(!HandleRFBServerMessage(cl)) 555 { 556 cleanup(cl); 557 break; 558 } 559 } 560 } 561 } 562 while(listenLoop); 563 564 return 0; 565 } 566 567