ia64/xen-unstable

view tools/ioemu/vnc.c @ 6946:e703abaf6e3d

Add behaviour to the remove methods to remove the transaction's path itself. This allows us to write Remove(path) to remove the specified path rather than having to slice the path ourselves.
author emellor@ewan
date Sun Sep 18 14:42:13 2005 +0100 (2005-09-18)
parents 3233e7ecfa9f
children 06d84bf87159
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 if(mouse_magic->calibration)
191 free(mouse_magic->calibration);
192 mouse_magic->calibration = malloc(size);
193 memcpy(mouse_magic->calibration, screen->frameBuffer, size);
194 calibration_step=0;
195 // calibration_count=-1;
196 //calibration_count=1000; updates_since_mouse=1;
197 fprintf(stderr,"Starting mouse calibration:\n");
198 }
200 static void stop_mouse_calibration() {
201 if(mouse_magic->calibration)
202 free(mouse_magic->calibration);
203 mouse_magic->calibration = 0;
204 }
206 static void mouse_calibration_update(int x,int y,int w,int h) {
207 mouse_find_bounding_box_of_difference(&x,&y,&w,&h);
208 if(w<=0 || h<=0)
209 return;
210 last_update.x=x;
211 last_update.y=y;
212 last_update.w=w;
213 last_update.h=h;
214 updates_since_mouse++;
215 }
217 static void mouse_calibration_refresh() {
218 static rectangle_t cursor;
219 static int x,y;
220 static int idle_counter;
222 if(calibration_step==0)
223 idle_counter=0;
224 else {
225 if(updates_since_mouse==0) {
226 idle_counter++;
227 if(idle_counter>5) {
228 fprintf(stderr, "Calibration failed: no update for 5 cycles\n");
229 stop_mouse_calibration();
230 }
231 return;
232 }
233 if(updates_since_mouse!=1) {
234 fprintf(stderr,"Calibration failed: updates=%d\n",updates_since_mouse);
235 stop_mouse_calibration();
236 return;
237 }
238 }
240 if(calibration_step==0) {
241 x=0; y=1;
242 kbd_mouse_event(0,-1,0,0);
243 calibration_step++;
244 } else if(calibration_step==1) {
245 // find out the initial position of the cursor
246 cursor=last_update;
247 cursor.h--;
248 calibration_step++;
249 mouse_magic->sonic_wall_y=-1;
250 last_update=cursor;
251 x=0; y=2;
252 goto move_calibrate;
253 } else if(calibration_step==2) {
254 // find out the sonic_wall
255 if(last_update.y==before_update.y-2*y) {
256 mouse_magic->sonic_wall_y=y;
257 // test orthogonality
258 calibration_step++;
259 x=mouse_magic->sonic_wall_y+1; y=1;
260 goto move_calibrate;
261 } else if(last_update.y<=2) {
262 if(y<6)
263 fprintf(stderr,"Calibration failed: not enough head room!\n");
264 else
265 fprintf(stderr,"Calibration finished.\n");
266 mouse_magic->sonic_wall_x=mouse_magic->sonic_wall_y=32768;
267 goto stop_calibration;
268 } else if(last_update.y!=before_update.y-y) {
269 fprintf(stderr,"Calibration failed: delta=%d (expected: %d)\n",last_update.y-before_update.y,-y);
270 goto stop_calibration;
271 } else {
272 y++;
273 move_calibrate:
274 kbd_mouse_event(-x,-y,0,0);
275 before_update=last_update;
276 }
277 } else if(calibration_step==3) {
278 if(last_update.y==before_update.y-2)
279 mouse_magic->sonic_wall_is_orthogonal=0;
280 else if(last_update.y==before_update.y-1)
281 mouse_magic->sonic_wall_is_orthogonal=-1;
282 else
283 fprintf(stderr,"Calibration failed: no clue of orthogonal.\n");
284 mouse_magic->sonic_wall_x=mouse_magic->sonic_wall_y;
285 if(last_update.x==before_update.x-mouse_magic->sonic_wall_x)
286 mouse_magic->sonic_wall_x++;
287 else if(last_update.x!=before_update.x-x*2)
288 fprintf(stderr,"Calibration failed: could not determine horizontal sonic wall x\n");
289 fprintf(stderr,"Calibration finished\n");
290 stop_calibration:
291 mouse_x=last_update.x;
292 mouse_y=last_update.y;
293 stop_mouse_calibration();
294 }
295 updates_since_mouse=0;
296 }
298 /* end of mouse stuff */
300 static void vnc_update(DisplayState *ds, int x, int y, int w, int h)
301 {
302 if(ds_sdl)
303 ds_sdl->dpy_update(ds_sdl,x,y,w,h);
304 if(0) fprintf(stderr,"updating x=%d y=%d w=%d h=%d\n", x, y, w, h);
305 rfbMarkRectAsModified(screen,x,y,x+w,y+h);
306 if(mouse_magic->calibration) {
307 mouse_calibration_update(x,y,w,h);
308 }
309 }
311 #include <SDL/SDL_video.h>
312 extern SDL_PixelFormat* sdl_get_format();
314 static void vnc_resize(DisplayState *ds, int w, int h)
315 {
316 int depth = screen->bitsPerPixel;
317 rfbClientIteratorPtr iter;
318 rfbClientPtr cl;
320 if(w==screen->width && h==screen->height)
321 return;
323 if(ds_sdl) {
324 SDL_PixelFormat* sdl_format;
325 ds_sdl->dpy_resize(ds_sdl,w,h);
326 ds->data = ds_sdl->data;
327 ds->linesize = screen->paddedWidthInBytes = ds_sdl->linesize;
328 screen->serverFormat.bitsPerPixel = screen->serverFormat.depth
329 = screen->bitsPerPixel = depth = ds->depth = ds_sdl->depth;
330 w = ds->width = ds_sdl->width;
331 h = ds->height = ds_sdl->height;
332 sdl_format=sdl_get_format();
333 if(sdl_format->palette==0) {
334 screen->serverFormat.trueColour=TRUE;
335 screen->serverFormat.redShift=sdl_format->Rshift;
336 screen->serverFormat.greenShift=sdl_format->Gshift;
337 screen->serverFormat.blueShift=sdl_format->Bshift;
338 screen->serverFormat.redMax=sdl_format->Rmask>>screen->serverFormat.redShift;
339 screen->serverFormat.greenMax=sdl_format->Gmask>>screen->serverFormat.greenShift;
340 screen->serverFormat.blueMax=sdl_format->Bmask>>screen->serverFormat.blueShift;
341 } else {
342 rfbColourMap* cmap=&(screen->colourMap);
343 int i;
344 screen->serverFormat.trueColour=FALSE;
345 cmap->is16=FALSE;
346 cmap->count=sdl_format->palette->ncolors;
347 if(cmap->data.bytes==0)
348 cmap->data.bytes=malloc(256*3);
349 for(i=0;i<cmap->count;i++) {
350 cmap->data.bytes[3*i+0]=sdl_format->palette->colors[i].r;
351 cmap->data.bytes[3*i+1]=sdl_format->palette->colors[i].g;
352 cmap->data.bytes[3*i+2]=sdl_format->palette->colors[i].b;
353 }
354 }
355 } else {
356 ds->data = (unsigned char*)realloc(ds->data, w*h*depth/8);
357 ds->linesize = screen->paddedWidthInBytes = w*2;
358 ds->width = w;
359 ds->height = h;
360 ds->depth = depth;
361 screen->paddedWidthInBytes = w*depth/8;
362 }
363 screen->frameBuffer = ds->data;
365 screen->width = w;
366 screen->height = h;
368 iter=rfbGetClientIterator(screen);
369 while((cl=rfbClientIteratorNext(iter)))
370 if(cl->useNewFBSize)
371 cl->newFBSizePending = TRUE;
372 else
373 rfbLog("Warning: Client %s does not support NewFBSize!\n",cl->host);
374 rfbReleaseClientIterator(iter);
376 if(mouse_magic->calibration) {
377 fprintf(stderr,"Warning: mouse calibration interrupted by video mode change\n");
378 stop_mouse_calibration();
379 }
380 init_mouse(w/2,h/2);
381 }
383 static void vnc_process_key(rfbBool down, rfbKeySym keySym, rfbClientPtr cl)
384 {
385 static int magic=0; // Ctrl+Alt starts calibration
387 if(is_active_console(vga_console)) {
388 WORD keycode=keysym2scancode(kbd_layout, keySym);
389 if(keycode>=0x80)
390 keycode=(keycode<<8)^0x80e0;
391 while(keycode!=0) {
392 kbd_put_keycode((keycode&0xff)|(down?0:0x80));
393 keycode>>=8;
394 }
395 } else if(down) {
396 int qemu_keysym = 0;
397 if (keySym <= 128) { /* normal ascii */
398 qemu_keysym = keySym;
399 } else {
400 switch(keySym) {
401 case XK_Up: qemu_keysym = QEMU_KEY_UP; break;
402 case XK_Down: qemu_keysym = QEMU_KEY_DOWN; break;
403 case XK_Left: qemu_keysym = QEMU_KEY_LEFT; break;
404 case XK_Right: qemu_keysym = QEMU_KEY_RIGHT; break;
405 case XK_Home: qemu_keysym = QEMU_KEY_HOME; break;
406 case XK_End: qemu_keysym = QEMU_KEY_END; break;
407 case XK_Page_Up: qemu_keysym = QEMU_KEY_PAGEUP; break;
408 case XK_Page_Down: qemu_keysym = QEMU_KEY_PAGEDOWN; break;
409 case XK_BackSpace: qemu_keysym = QEMU_KEY_BACKSPACE; break;
410 case XK_Delete: qemu_keysym = QEMU_KEY_DELETE; break;
411 case XK_Return:
412 case XK_Linefeed: qemu_keysym = keySym; break;
413 default: break;
414 }
415 }
416 if (qemu_keysym != 0)
417 kbd_put_keysym(qemu_keysym);
418 }
419 if(down) {
420 if(keySym==XK_Control_L)
421 magic|=1;
422 else if(keySym==XK_Alt_L)
423 magic|=2;
424 } else {
425 if((magic&3)==3) {
426 switch(keySym) {
427 case XK_Control_L:
428 magic&=~1;
429 break;
430 case XK_Alt_L:
431 magic&=~2;
432 break;
433 case XK_m:
434 magic=0;
435 start_mouse_calibration();
436 break;
437 case XK_1 ... XK_9:
438 magic=0;
439 fprintf(stderr,"switch to %d\n",keySym-XK_1);
440 console_select(keySym - XK_1);
441 if (is_active_console(vga_console)) {
442 /* tell the vga console to redisplay itself */
443 vga_invalidate_display();
444 vnc_update(0,0,0,screen->width,screen->height);
445 }
446 break;
447 }
448 }
449 }
450 }
452 static void vnc_process_mouse(int buttonMask, int x, int y, rfbClientPtr cl)
453 {
454 new_mouse_x=x; new_mouse_y=y; new_mouse_buttons=0;
455 if(buttonMask&1) new_mouse_buttons|=MOUSE_EVENT_LBUTTON;
456 if(buttonMask&2) new_mouse_buttons|=MOUSE_EVENT_MBUTTON;
457 if(buttonMask&4) new_mouse_buttons|=MOUSE_EVENT_RBUTTON;
458 if(buttonMask&8) new_mouse_z--;
459 if(buttonMask&16) new_mouse_z++;
460 }
462 static void vnc_refresh(DisplayState *ds) {
463 if(ds_sdl)
464 ds_sdl->dpy_refresh(ds_sdl);
465 else
466 vga_update_display();
467 rfbProcessEvents(screen,0);
468 if(mouse_magic->calibration) {
469 mouse_calibration_refresh();
470 } else {
471 mouse_refresh();
472 }
473 }
475 static void vnc_cleanup(void)
476 {
477 rfbScreenCleanup(screen);
478 }
481 void vnc_display_init(DisplayState *ds, int useAlsoSDL,
482 long port, const char* connect)
483 {
484 int len, rport = 5500;
485 char host[1024];
486 char *p;
487 rfbClientPtr cl;
489 if(!keyboard_layout) {
490 fprintf(stderr, "No keyboard language specified\n");
491 exit(1);
492 }
494 kbd_layout=init_keyboard_layout(keyboard_layout);
495 if(!kbd_layout) {
496 fprintf(stderr, "Could not initialize keyboard\n");
497 exit(1);
498 }
501 mouse_magic=init_mouse_magic();
502 register_savevm("vnc", 0, 1, vnc_save, vnc_load, mouse_magic);
504 rfbLog=rfbErr=term_printf;
505 screen=rfbGetScreen(0,0,0,0,5,3,2);
506 if(screen==0) {
507 fprintf(stderr, "Could not initialize VNC - exiting\n");
508 exit(1);
509 }
512 screen->serverFormat.redShift = 11;
513 screen->serverFormat.greenShift = 5;
514 screen->serverFormat.blueShift = 0;
515 screen->serverFormat.redMax = 31;
516 screen->serverFormat.greenMax = 63;
517 screen->serverFormat.blueMax = 31;
519 if (port != 0)
520 screen->port = port;
521 else
522 screen->autoPort = TRUE;
524 if(useAlsoSDL) {
525 ds_sdl=(DisplayState*)malloc(sizeof(DisplayState));
526 sdl_display_init(ds_sdl,0);
527 screen->frameBuffer = ds_sdl->data;
528 } else
529 screen->frameBuffer = malloc(640*400*2);
531 screen->desktopName = "QEMU/VNC";
532 screen->cursor = 0;
533 screen->kbdAddEvent = vnc_process_key;
534 screen->ptrAddEvent = vnc_process_mouse;
535 rfbInitServer(screen);
537 vnc_resize(ds,640,400);
539 ds->dpy_update = vnc_update;
540 ds->dpy_resize = vnc_resize;
541 ds->dpy_refresh = vnc_refresh;
543 /* deal with reverse connections */
544 if ( connect == NULL || (len = strlen(connect)) < 1) {
545 return;
546 }
547 if ( len > 1024 ) {
548 fprintf(stderr, "vnc reverse connect name too long\n");
549 exit(1);
550 }
551 strncpy(host, connect, len);
552 host[len] = '\0';
553 /* extract port, if any */
554 if ((p = strchr(host, ':')) != NULL) {
555 rport = atoi(p+1);
556 *p = '\0';
557 }
558 cl = rfbReverseConnection(screen, host, rport);
559 if (cl == NULL) {
560 fprintf(stderr, "reverse_connect: %s failed\n", connect);
561 } else {
562 fprintf(stderr, "reverse_connect: %s/%s OK\n", connect, cl->host);
563 }
565 atexit(vnc_cleanup);
569 }