ia64/xen-unstable

view tools/ioemu/vnc.c @ 8740:3d7ea7972b39

Update patches for linux 2.6.15.

Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author cl349@firebug.cl.cam.ac.uk
date Thu Feb 02 17:16:00 2006 +0000 (2006-02-02)
parents b8ba1bbba882
children 58d1ef215706
line source
1 /*
2 * QEMU VNC display driver (uses LibVNCServer, based on QEMU SDL driver)
3 *
4 * Copyright (c) 2003,2004 Fabrice Bellard, Matthew Mastracci,
5 * Johannes E. Schindelin
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 *
25 *
26 * reverse connection setup copied from x11vnc.c
27 * Copyright (c) 2002-2005 Karl J. Runge <runge@karlrunge.com>
28 * All rights reserved.
29 * based on:
30 * the originial x11vnc.c in libvncserver (Johannes E. Schindelin)
31 * x0rfbserver, the original native X vnc server (Jens Wagner)
32 * krfb, the KDE desktopsharing project (Tim Jansen)
33 */
34 #include "vl.h"
36 #include <rfb/rfb.h>
38 /* keyboard stuff */
39 #include <rfb/keysym.h>
40 #include "keysym_adapter_vnc.h"
41 #include "keyboard_rdesktop.c"
44 #ifndef _WIN32
45 #include <signal.h>
46 #endif
48 static rfbScreenInfoPtr screen;
49 static DisplayState* ds_sdl=0;
50 static void* kbd_layout=0; // TODO: move into rfbClient
52 /* mouse stuff */
54 typedef struct mouse_magic_t {
55 /* When calibrating, mouse_calibration contains a copy of the
56 * current frame buffer. After a simulated mouse movement, the
57 * update function only gets (0,y1,width,y2) as bounding box
58 * of the changed region, so we refine that with the help of
59 * this copy, and then update the copy. */
60 char* calibration;
61 /* Mouse handling using VNC used to be wrong, because if moving the
62 * mouse very fast, the pointer got even faster. The reason for this:
63 * when the mouse sends a delta of at least 4 (Windows: 3) pixels,
64 * it is treated as if it were double the amount. I call this the
65 * sonic wall. */
66 int sonic_wall_x;
67 int sonic_wall_y;
68 /* Unfortunately, Windows and X behave differently, when the sonic
69 * wall was reached in one axis, but not the other: Windows treats
70 * them independently. I call this orthogonal. */
71 char sonic_wall_is_orthogonal;
72 /* last_dy contains the last delta sent on the y axis. We don't
73 * use the x axis (see mouse_calibration). */
74 //static int last_dy=0;
75 } mouse_magic_t;
77 mouse_magic_t* init_mouse_magic() {
78 mouse_magic_t* ret=(mouse_magic_t*)malloc(sizeof(mouse_magic_t));
80 ret->calibration=0;
81 #ifdef EXPECT_WINDOWS_GUEST
82 ret->sonic_wall_x=3;
83 ret->sonic_wall_y=3;
84 ret->sonic_wall_is_orthogonal=1;
85 #else
86 ret->sonic_wall_x=4;
87 ret->sonic_wall_y=4;
88 ret->sonic_wall_is_orthogonal=0;
89 #endif
90 return ret;
91 }
93 static void vnc_save(QEMUFile* f,void* opaque)
94 {
95 mouse_magic_t* s=(mouse_magic_t*)opaque;
97 qemu_put_be32s(f, &s->sonic_wall_x);
98 qemu_put_be32s(f, &s->sonic_wall_y);
99 qemu_put_8s(f, &s->sonic_wall_is_orthogonal);
100 }
102 static int vnc_load(QEMUFile* f,void* opaque,int version_id)
103 {
104 mouse_magic_t* s=(mouse_magic_t*)opaque;
106 if (version_id != 1)
107 return -EINVAL;
109 qemu_get_be32s(f, &s->sonic_wall_x);
110 qemu_get_be32s(f, &s->sonic_wall_y);
111 qemu_get_8s(f, &s->sonic_wall_is_orthogonal);
113 return 0;
114 }
116 static mouse_magic_t* mouse_magic;
118 typedef struct {
119 int x,y,w,h;
120 } rectangle_t;
121 /* In order to calibrate the mouse, we have to know about the bounding boxes
122 * of the last changes. */
123 static rectangle_t last_update, before_update;
124 static int updates_since_mouse=0;
126 static int mouse_x,mouse_y;
127 static int new_mouse_x,new_mouse_y,new_mouse_z,new_mouse_buttons;
129 static void init_mouse(int initial_x,int initial_y) {
130 mouse_x=new_mouse_x=initial_x;
131 mouse_y=new_mouse_y=initial_y;
132 new_mouse_z=new_mouse_buttons=0;
133 mouse_magic->calibration = 0;
134 }
136 static void mouse_refresh() {
137 int dx=0,dy=0,dz=new_mouse_z;
138 static int counter=1;
140 counter++;
141 if(!mouse_magic->calibration && counter>=2) { counter=0; return; }
143 dx=new_mouse_x-mouse_x;
144 dy=new_mouse_y-mouse_y;
146 if(mouse_magic->sonic_wall_is_orthogonal) {
147 if(abs(dx)>=mouse_magic->sonic_wall_x) { dx/=2; mouse_x+=dx; }
148 if(abs(dy)>=mouse_magic->sonic_wall_y) { dy/=2; mouse_y+=dy; }
149 } else {
150 if(abs(dx)>=mouse_magic->sonic_wall_x || abs(dy)>=mouse_magic->sonic_wall_y) {
151 dx/=2; mouse_x+=dx;
152 dy/=2; mouse_y+=dy;
153 }
154 }
155 //fprintf(stderr,"sending mouse event %d,%d\n",dx,dy);
156 kbd_mouse_event(dx,dy,dz,new_mouse_buttons);
157 mouse_x+=dx;
158 mouse_y+=dy;
160 updates_since_mouse=0;
161 }
163 static int calibration_step=0;
164 //static int calibration_count=0;
166 static void mouse_find_bounding_box_of_difference(int* x,int* y,int* w,int* h) {
167 int i,j,X=*x,Y=*y,W=*w,H=*h;
168 int bpp=screen->depth/8;
170 *x=screen->width; *w=-*x;
171 *y=screen->height; *h=-*y;
172 for(i=X;i<X+W;i++)
173 for(j=Y;j<Y+H;j++) {
174 int offset=i*bpp+j*screen->paddedWidthInBytes;
175 if(memcmp(mouse_magic->calibration+offset,screen->frameBuffer+offset,bpp)) {
176 if(i<((*x))) { (*w)+=(*x)-i; (*x)=i; }
177 if(i>(*x)+(*w)) (*w)=i-(*x);
178 if(j<(*y)) { (*h)+=(*y)-j; (*y)=j; }
179 if(j>(*y)+(*h)) (*h)=j-(*y);
180 }
181 }
182 if(h>0)
183 memcpy(mouse_magic->calibration+Y*screen->paddedWidthInBytes,
184 screen->frameBuffer+Y*screen->paddedWidthInBytes,
185 H*screen->paddedWidthInBytes);
186 }
188 static void start_mouse_calibration() {
189 int size = screen->height*screen->paddedWidthInBytes;
190 free(mouse_magic->calibration);
191 mouse_magic->calibration = malloc(size);
192 memcpy(mouse_magic->calibration, screen->frameBuffer, size);
193 calibration_step=0;
194 // calibration_count=-1;
195 //calibration_count=1000; updates_since_mouse=1;
196 fprintf(stderr,"Starting mouse calibration:\n");
197 }
199 static void stop_mouse_calibration() {
200 free(mouse_magic->calibration);
201 mouse_magic->calibration = 0;
202 }
204 static void mouse_calibration_update(int x,int y,int w,int h) {
205 mouse_find_bounding_box_of_difference(&x,&y,&w,&h);
206 if(w<=0 || h<=0)
207 return;
208 last_update.x=x;
209 last_update.y=y;
210 last_update.w=w;
211 last_update.h=h;
212 updates_since_mouse++;
213 }
215 static void mouse_calibration_refresh() {
216 static rectangle_t cursor;
217 static int x,y;
218 static int idle_counter;
220 if(calibration_step==0)
221 idle_counter=0;
222 else {
223 if(updates_since_mouse==0) {
224 idle_counter++;
225 if(idle_counter>5) {
226 fprintf(stderr, "Calibration failed: no update for 5 cycles\n");
227 stop_mouse_calibration();
228 }
229 return;
230 }
231 if(updates_since_mouse!=1) {
232 fprintf(stderr,"Calibration failed: updates=%d\n",updates_since_mouse);
233 stop_mouse_calibration();
234 return;
235 }
236 }
238 if(calibration_step==0) {
239 x=0; y=1;
240 kbd_mouse_event(0,-1,0,0);
241 calibration_step++;
242 } else if(calibration_step==1) {
243 // find out the initial position of the cursor
244 cursor=last_update;
245 cursor.h--;
246 calibration_step++;
247 mouse_magic->sonic_wall_y=-1;
248 last_update=cursor;
249 x=0; y=2;
250 goto move_calibrate;
251 } else if(calibration_step==2) {
252 // find out the sonic_wall
253 if(last_update.y==before_update.y-2*y) {
254 mouse_magic->sonic_wall_y=y;
255 // test orthogonality
256 calibration_step++;
257 x=mouse_magic->sonic_wall_y+1; y=1;
258 goto move_calibrate;
259 } else if(last_update.y<=2) {
260 if(y<6)
261 fprintf(stderr,"Calibration failed: not enough head room!\n");
262 else
263 fprintf(stderr,"Calibration finished.\n");
264 mouse_magic->sonic_wall_x=mouse_magic->sonic_wall_y=32768;
265 goto stop_calibration;
266 } else if(last_update.y!=before_update.y-y) {
267 fprintf(stderr,"Calibration failed: delta=%d (expected: %d)\n",last_update.y-before_update.y,-y);
268 goto stop_calibration;
269 } else {
270 y++;
271 move_calibrate:
272 kbd_mouse_event(-x,-y,0,0);
273 before_update=last_update;
274 }
275 } else if(calibration_step==3) {
276 if(last_update.y==before_update.y-2)
277 mouse_magic->sonic_wall_is_orthogonal=0;
278 else if(last_update.y==before_update.y-1)
279 mouse_magic->sonic_wall_is_orthogonal=-1;
280 else
281 fprintf(stderr,"Calibration failed: no clue of orthogonal.\n");
282 mouse_magic->sonic_wall_x=mouse_magic->sonic_wall_y;
283 if(last_update.x==before_update.x-mouse_magic->sonic_wall_x)
284 mouse_magic->sonic_wall_x++;
285 else if(last_update.x!=before_update.x-x*2)
286 fprintf(stderr,"Calibration failed: could not determine horizontal sonic wall x\n");
287 fprintf(stderr,"Calibration finished\n");
288 stop_calibration:
289 mouse_x=last_update.x;
290 mouse_y=last_update.y;
291 stop_mouse_calibration();
292 }
293 updates_since_mouse=0;
294 }
296 /* end of mouse stuff */
298 static void vnc_update(DisplayState *ds, int x, int y, int w, int h)
299 {
300 if(ds_sdl)
301 ds_sdl->dpy_update(ds_sdl,x,y,w,h);
302 if(0) fprintf(stderr,"updating x=%d y=%d w=%d h=%d\n", x, y, w, h);
303 rfbMarkRectAsModified(screen,x,y,x+w,y+h);
304 if(mouse_magic->calibration) {
305 mouse_calibration_update(x,y,w,h);
306 }
307 }
309 #include <SDL/SDL_video.h>
310 extern SDL_PixelFormat* sdl_get_format();
312 static void vnc_resize(DisplayState *ds, int w, int h)
313 {
314 int depth = screen->bitsPerPixel;
315 rfbClientIteratorPtr iter;
316 rfbClientPtr cl;
318 if(w==screen->width && h==screen->height)
319 return;
321 if(ds_sdl) {
322 SDL_PixelFormat* sdl_format;
323 ds_sdl->dpy_resize(ds_sdl,w,h);
324 ds->data = ds_sdl->data;
325 ds->linesize = screen->paddedWidthInBytes = ds_sdl->linesize;
326 screen->serverFormat.bitsPerPixel = screen->serverFormat.depth
327 = screen->bitsPerPixel = depth = ds->depth = ds_sdl->depth;
328 w = ds->width = ds_sdl->width;
329 h = ds->height = ds_sdl->height;
330 sdl_format=sdl_get_format();
331 if(sdl_format->palette==0) {
332 screen->serverFormat.trueColour=TRUE;
333 screen->serverFormat.redShift=sdl_format->Rshift;
334 screen->serverFormat.greenShift=sdl_format->Gshift;
335 screen->serverFormat.blueShift=sdl_format->Bshift;
336 screen->serverFormat.redMax=sdl_format->Rmask>>screen->serverFormat.redShift;
337 screen->serverFormat.greenMax=sdl_format->Gmask>>screen->serverFormat.greenShift;
338 screen->serverFormat.blueMax=sdl_format->Bmask>>screen->serverFormat.blueShift;
339 } else {
340 rfbColourMap* cmap=&(screen->colourMap);
341 int i;
342 screen->serverFormat.trueColour=FALSE;
343 cmap->is16=FALSE;
344 cmap->count=sdl_format->palette->ncolors;
345 if(cmap->data.bytes==0)
346 cmap->data.bytes=malloc(256*3);
347 for(i=0;i<cmap->count;i++) {
348 cmap->data.bytes[3*i+0]=sdl_format->palette->colors[i].r;
349 cmap->data.bytes[3*i+1]=sdl_format->palette->colors[i].g;
350 cmap->data.bytes[3*i+2]=sdl_format->palette->colors[i].b;
351 }
352 }
353 } else {
354 ds->data = (unsigned char*)realloc(ds->data, w*h*depth/8);
355 ds->linesize = screen->paddedWidthInBytes = w*2;
356 ds->width = w;
357 ds->height = h;
358 ds->depth = depth;
359 screen->paddedWidthInBytes = w*depth/8;
360 }
361 screen->frameBuffer = ds->data;
363 screen->width = w;
364 screen->height = h;
366 iter=rfbGetClientIterator(screen);
367 while((cl=rfbClientIteratorNext(iter)))
368 if(cl->useNewFBSize)
369 cl->newFBSizePending = TRUE;
370 else
371 rfbLog("Warning: Client %s does not support NewFBSize!\n",cl->host);
372 rfbReleaseClientIterator(iter);
374 if(mouse_magic->calibration) {
375 fprintf(stderr,"Warning: mouse calibration interrupted by video mode change\n");
376 stop_mouse_calibration();
377 }
378 init_mouse(w/2,h/2);
379 }
381 static void vnc_process_key(rfbBool down, rfbKeySym keySym, rfbClientPtr cl)
382 {
383 static int magic=0; // Ctrl+Alt starts calibration
385 if(is_active_console(vga_console)) {
386 WORD keycode=keysym2scancode(kbd_layout, keySym);
387 if(keycode>=0x80)
388 keycode=(keycode<<8)^0x80e0;
389 while(keycode!=0) {
390 kbd_put_keycode((keycode&0xff)|(down?0:0x80));
391 keycode>>=8;
392 }
393 } else if(down) {
394 int qemu_keysym = 0;
395 if (keySym <= 128) { /* normal ascii */
396 qemu_keysym = keySym;
397 } else {
398 switch(keySym) {
399 case XK_Up: qemu_keysym = QEMU_KEY_UP; break;
400 case XK_Down: qemu_keysym = QEMU_KEY_DOWN; break;
401 case XK_Left: qemu_keysym = QEMU_KEY_LEFT; break;
402 case XK_Right: qemu_keysym = QEMU_KEY_RIGHT; break;
403 case XK_Home: qemu_keysym = QEMU_KEY_HOME; break;
404 case XK_End: qemu_keysym = QEMU_KEY_END; break;
405 case XK_Page_Up: qemu_keysym = QEMU_KEY_PAGEUP; break;
406 case XK_Page_Down: qemu_keysym = QEMU_KEY_PAGEDOWN; break;
407 case XK_BackSpace: qemu_keysym = QEMU_KEY_BACKSPACE; break;
408 case XK_Delete: qemu_keysym = QEMU_KEY_DELETE; break;
409 case XK_Return:
410 case XK_Linefeed: qemu_keysym = keySym; break;
411 default: break;
412 }
413 }
414 if (qemu_keysym != 0)
415 kbd_put_keysym(qemu_keysym);
416 }
417 if(down) {
418 if(keySym==XK_Control_L)
419 magic|=1;
420 else if(keySym==XK_Alt_L)
421 magic|=2;
422 } else {
423 if((magic&3)==3) {
424 switch(keySym) {
425 case XK_Control_L:
426 magic&=~1;
427 break;
428 case XK_Alt_L:
429 magic&=~2;
430 break;
431 case XK_m:
432 magic=0;
433 start_mouse_calibration();
434 break;
435 case XK_1 ... XK_9:
436 magic=0;
437 fprintf(stderr,"switch to %d\n",keySym-XK_1);
438 console_select(keySym - XK_1);
439 if (is_active_console(vga_console)) {
440 /* tell the vga console to redisplay itself */
441 vga_invalidate_display();
442 vnc_update(0,0,0,screen->width,screen->height);
443 }
444 break;
445 }
446 }
447 }
448 }
450 static void vnc_process_mouse(int buttonMask, int x, int y, rfbClientPtr cl)
451 {
452 new_mouse_x=x; new_mouse_y=y; new_mouse_buttons=0;
453 if(buttonMask&1) new_mouse_buttons|=MOUSE_EVENT_LBUTTON;
454 if(buttonMask&2) new_mouse_buttons|=MOUSE_EVENT_MBUTTON;
455 if(buttonMask&4) new_mouse_buttons|=MOUSE_EVENT_RBUTTON;
456 if(buttonMask&8) new_mouse_z--;
457 if(buttonMask&16) new_mouse_z++;
458 }
460 static void vnc_refresh(DisplayState *ds) {
461 if(ds_sdl)
462 ds_sdl->dpy_refresh(ds_sdl);
463 else
464 vga_update_display();
465 rfbProcessEvents(screen,0);
466 if(mouse_magic->calibration) {
467 mouse_calibration_refresh();
468 } else {
469 mouse_refresh();
470 }
471 }
473 static void vnc_cleanup(void)
474 {
475 rfbScreenCleanup(screen);
476 }
479 void vnc_display_init(DisplayState *ds, int useAlsoSDL,
480 long port, const char* connect)
481 {
482 int len, rport = 5500;
483 char host[1024];
484 char *p;
485 rfbClientPtr cl;
487 if(!keyboard_layout) {
488 fprintf(stderr, "No keyboard language specified\n");
489 exit(1);
490 }
492 kbd_layout=init_keyboard_layout(keyboard_layout);
493 if(!kbd_layout) {
494 fprintf(stderr, "Could not initialize keyboard\n");
495 exit(1);
496 }
499 mouse_magic=init_mouse_magic();
500 register_savevm("vnc", 0, 1, vnc_save, vnc_load, mouse_magic);
502 rfbLog=rfbErr=term_printf;
503 screen=rfbGetScreen(0,0,0,0,5,3,2);
504 if(screen==0) {
505 fprintf(stderr, "Could not initialize VNC - exiting\n");
506 exit(1);
507 }
510 screen->serverFormat.redShift = 11;
511 screen->serverFormat.greenShift = 5;
512 screen->serverFormat.blueShift = 0;
513 screen->serverFormat.redMax = 31;
514 screen->serverFormat.greenMax = 63;
515 screen->serverFormat.blueMax = 31;
517 if (port != 0)
518 screen->port = port;
519 else
520 screen->autoPort = TRUE;
522 if(useAlsoSDL) {
523 ds_sdl=(DisplayState*)malloc(sizeof(DisplayState));
524 sdl_display_init(ds_sdl,0);
525 screen->frameBuffer = ds_sdl->data;
526 } else
527 screen->frameBuffer = malloc(640*400*2);
529 screen->desktopName = domain_name;
530 screen->cursor = 0;
531 screen->kbdAddEvent = vnc_process_key;
532 screen->ptrAddEvent = vnc_process_mouse;
533 rfbInitServer(screen);
535 vnc_resize(ds,640,400);
537 ds->dpy_update = vnc_update;
538 ds->dpy_resize = vnc_resize;
539 ds->dpy_refresh = vnc_refresh;
541 /* deal with reverse connections */
542 if ( connect == NULL || (len = strlen(connect)) < 1) {
543 return;
544 }
545 if ( len > 1024 ) {
546 fprintf(stderr, "vnc reverse connect name too long\n");
547 exit(1);
548 }
549 strncpy(host, connect, len);
550 host[len] = '\0';
551 /* extract port, if any */
552 if ((p = strchr(host, ':')) != NULL) {
553 rport = atoi(p+1);
554 *p = '\0';
555 }
556 cl = rfbReverseConnection(screen, host, rport);
557 if (cl == NULL) {
558 fprintf(stderr, "reverse_connect: %s failed\n", connect);
559 } else {
560 fprintf(stderr, "reverse_connect: %s/%s OK\n", connect, cl->host);
561 }
563 atexit(vnc_cleanup);
567 }