VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxBFE/SDLConsole.cpp@ 43421

Last change on this file since 43421 was 35346, checked in by vboxsync, 13 years ago

VMM reorg: Moving the public include files from include/VBox to include/VBox/vmm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 52.5 KB
Line 
1/** @file
2 *
3 * VBox frontends: Basic Frontend (BFE):
4 * Implementation of SDLConsole class
5 */
6
7/*
8 * Copyright (C) 2006-2009 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP LOG_GROUP_GUI
23#define __STDC_LIMIT_MACROS
24#define __STDC_CONSTANT_MACROS
25
26#ifdef RT_OS_DARWIN
27# include <Carbon/Carbon.h>
28# undef PAGE_SIZE
29# undef PAGE_SHIFT
30#endif
31
32#ifdef VBOXBFE_WITHOUT_COM
33# include "COMDefs.h"
34#else
35# include <VBox/com/defs.h>
36#endif
37#include <VBox/types.h>
38#include <VBox/err.h>
39#include <VBox/param.h>
40#include <VBox/vmm/pdm.h>
41#include <VBox/log.h>
42#include <VBox/version.h>
43#include <iprt/path.h>
44#include <iprt/string.h>
45#include <iprt/initterm.h>
46#include <iprt/assert.h>
47#include <iprt/semaphore.h>
48#include <iprt/stream.h>
49#include <iprt/uuid.h>
50#include <iprt/alloca.h>
51
52#ifdef VBOXBFE_WITH_X11
53# define Display Display_
54# include <X11/Xlib.h>
55# ifndef VBOXBFE_WITHOUT_XCURSOR
56# include <X11/Xcursor/Xcursor.h>
57# endif
58# undef Display
59#endif
60
61#include "VBoxBFE.h"
62
63#include <vector>
64
65#include "DisplayImpl.h"
66#include "MouseImpl.h"
67#include "KeyboardImpl.h"
68#include "VMMDev.h"
69#include "Framebuffer.h"
70#include "MachineDebuggerImpl.h"
71#include "VMControl.h"
72
73#include "ConsoleImpl.h"
74#include "SDLConsole.h"
75#include "Ico64x01.h"
76
77/*******************************************************************************
78* Internal Functions *
79*******************************************************************************/
80
81SDLConsole::SDLConsole() : Console()
82{
83 int rc;
84
85 mfInputGrab = false;
86 gpDefaultCursor = NULL;
87 gpCustomCursor = NULL;
88 /** Custom window manager cursor? */
89 gpCustomWMcursor = NULL;
90 mfInitialized = false;
91 mWMIcon = NULL;
92
93 memset(gaModifiersState, 0, sizeof(gaModifiersState));
94
95 rc = SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE);
96 if (rc != 0)
97 {
98 RTPrintf("SDL Error: '%s'\n", SDL_GetError());
99 return;
100 }
101
102 /* memorize the default cursor */
103 gpDefaultCursor = SDL_GetCursor();
104 /* create a fake empty cursor */
105 {
106 uint8_t cursorData[1] = {0};
107 gpCustomCursor = SDL_CreateCursor(cursorData, cursorData, 8, 1, 0, 0);
108 gpCustomWMcursor = gpCustomCursor->wm_cursor;
109 gpCustomCursor->wm_cursor = NULL;
110 }
111#ifdef VBOXBFE_WITH_X11
112 /* get Window Manager info */
113 SDL_VERSION(&gSdlInfo.version);
114 if (!SDL_GetWMInfo(&gSdlInfo))
115 {
116 /** @todo: Is this fatal? */
117 AssertMsgFailed(("Error: could not get SDL Window Manager info!\n"));
118 }
119#endif
120
121 if (12320 == g_cbIco64x01)
122 {
123 mWMIcon = SDL_AllocSurface(SDL_SWSURFACE, 64, 64, 24, 0xff, 0xff00, 0xff0000, 0);
124 /** @todo make it as simple as possible. No PNM interpreter here... */
125 if (mWMIcon)
126 {
127 memcpy(mWMIcon->pixels, g_abIco64x01+32, g_cbIco64x01-32);
128 SDL_WM_SetIcon(mWMIcon, NULL);
129 }
130 }
131
132 /*
133 * Enable keyboard repeats
134 */
135 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
136
137 /*
138 * Initialise "children" (so far only Mouse)
139 */
140 if (FAILED(gMouse->init(this)))
141 {
142 RTPrintf("VBoxBFE: failed to initialise the mouse device\n");
143 return;
144 }
145
146 mfInitialized = true;
147}
148
149SDLConsole::~SDLConsole()
150{
151 if (mfInputGrab)
152 inputGrabEnd();
153
154 /*
155 * Uninitialise "children" (so far only Mouse)
156 */
157 gMouse->uninit();
158
159 if (mWMIcon)
160 {
161 SDL_FreeSurface(mWMIcon);
162 mWMIcon = NULL;
163 }
164}
165
166CONEVENT SDLConsole::eventWait()
167{
168 SDL_Event *ev = &ev1;
169
170 if (SDL_WaitEvent(ev) != 1)
171 return CONEVENT_QUIT;
172
173 switch (ev->type)
174 {
175
176 /*
177 * The screen needs to be repainted.
178 */
179 case SDL_VIDEOEXPOSE:
180 {
181 return CONEVENT_SCREENUPDATE;
182 }
183
184 /*
185 * Keyboard events.
186 */
187 case SDL_KEYDOWN:
188 case SDL_KEYUP:
189 {
190 switch (enmHKeyState)
191 {
192 case HKEYSTATE_NORMAL:
193 {
194 if ( ev->type == SDL_KEYDOWN
195 && ev->key.keysym.sym == gHostKeySym
196 && (SDL_GetModState() & ~(KMOD_MODE | KMOD_NUM | KMOD_RESERVED)) == gHostKey)
197 {
198 EvHKeyDown = *ev;
199 enmHKeyState = HKEYSTATE_DOWN;
200 break;
201 }
202 processKey(&ev->key);
203 break;
204 }
205
206 case HKEYSTATE_DOWN:
207 {
208
209 if (ev->type == SDL_KEYDOWN)
210 {
211 /* potential host key combination, try execute it */
212 int rc = handleHostKey(&ev->key);
213 if (rc == VINF_SUCCESS)
214 {
215 enmHKeyState = HKEYSTATE_USED;
216 break;
217 }
218 if (RT_SUCCESS(rc))
219 {
220 return CONEVENT_QUIT;
221 }
222 }
223 else /* SDL_KEYUP */
224 {
225 if (ev->key.keysym.sym == gHostKeySym)
226 {
227 /* toggle grabbing state */
228 if (!mfInputGrab)
229 inputGrabStart();
230 else
231 inputGrabEnd();
232
233 /* SDL doesn't always reset the keystates, correct it */
234 resetKeys();
235 enmHKeyState = HKEYSTATE_NORMAL;
236 break;
237 }
238 }
239
240 /* not host key */
241 enmHKeyState = HKEYSTATE_NOT_IT;
242 ev1 = *ev;
243 processKey(&EvHKeyDown.key);
244 processKey(&ev->key);
245 break;
246 }
247
248 case HKEYSTATE_USED:
249 {
250 if ((SDL_GetModState() & ~(KMOD_MODE | KMOD_NUM | KMOD_RESERVED)) == 0)
251 {
252 enmHKeyState = HKEYSTATE_NORMAL;
253 }
254 if (ev->type == SDL_KEYDOWN)
255 {
256 int rc = handleHostKey(&ev->key);
257 if (RT_SUCCESS(rc) && rc != VINF_SUCCESS)
258 {
259 return CONEVENT_QUIT;
260 }
261 }
262 break;
263 }
264
265 default:
266 AssertMsgFailed(("enmHKeyState=%d\n", enmHKeyState));
267 /* fall thru */
268 case HKEYSTATE_NOT_IT:
269 {
270 if ((SDL_GetModState() & ~(KMOD_MODE | KMOD_NUM | KMOD_RESERVED)) == 0)
271 {
272 enmHKeyState = HKEYSTATE_NORMAL;
273 }
274 processKey(&ev->key);
275 break;
276 }
277 } /* state switch */
278 break;
279 }
280
281 /*
282 * The window was closed.
283 */
284 case SDL_QUIT:
285 {
286 return CONEVENT_QUIT;
287 }
288
289 /*
290 * The mouse has moved
291 */
292 case SDL_MOUSEMOTION:
293 {
294 BOOL fMouseAbsolute;
295 gMouse->COMGETTER(AbsoluteSupported)(&fMouseAbsolute);
296 if (mfInputGrab || fMouseAbsolute)
297 mouseSendEvent(0);
298 break;
299 }
300
301 /*
302 * A mouse button has been clicked or released.
303 */
304 case SDL_MOUSEBUTTONDOWN:
305 case SDL_MOUSEBUTTONUP:
306 {
307 BOOL fMouseAbsolute;
308 gMouse->COMGETTER(AbsoluteSupported)(&fMouseAbsolute);
309 SDL_MouseButtonEvent *bev = &ev->button;
310 if (!mfInputGrab && !fMouseAbsolute)
311 {
312 if (ev->type == SDL_MOUSEBUTTONDOWN && (bev->state & SDL_BUTTON_LMASK))
313 {
314 /* start grabbing all events */
315 inputGrabStart();
316 }
317 }
318 else
319 {
320 int dz = 0;
321 if (bev->button == SDL_BUTTON_WHEELUP)
322 {
323 dz = -1;
324 }
325 else if (bev->button == SDL_BUTTON_WHEELDOWN)
326 {
327 dz = 1;
328 }
329 mouseSendEvent(dz);
330 }
331 break;
332 }
333
334 /*
335 * The window has gained or lost focus.
336 */
337 case SDL_ACTIVEEVENT:
338 {
339 if (mfInputGrab && (SDL_GetAppState() & SDL_ACTIVEEVENTMASK) == 0)
340 {
341 inputGrabEnd();
342 }
343 break;
344 }
345
346
347 /*
348 * User specific update event.
349 */
350 /** @todo use a common user event handler so that SDL_PeepEvents() won't
351 * possibly remove other events in the queue!
352 */
353 case SDL_USER_EVENT_UPDATERECT:
354 {
355
356 /*
357 * Decode event parameters.
358 */
359 #define DECODEX(ev) ((intptr_t)(ev)->user.data1 >> 16)
360 #define DECODEY(ev) ((intptr_t)(ev)->user.data1 & 0xFFFF)
361 #define DECODEW(ev) ((intptr_t)(ev)->user.data2 >> 16)
362 #define DECODEH(ev) ((intptr_t)(ev)->user.data2 & 0xFFFF)
363 int x = DECODEX(ev);
364 int y = DECODEY(ev);
365 int w = DECODEW(ev);
366 int h = DECODEH(ev);
367 LogFlow(("SDL_USER_EVENT_UPDATERECT: x = %d, y = %d, w = %d, h = %d\n",
368 x, y, w, h));
369
370 Assert(gFramebuffer);
371 /*
372 * Lock the framebuffer, perform the update and lock again
373 */
374 gFramebuffer->Lock();
375 gFramebuffer->update(x, y, w, h);
376 gFramebuffer->Unlock();
377
378 #undef DECODEX
379 #undef DECODEY
380 #undef DECODEW
381 #undef DECODEH
382 break;
383 }
384
385 /*
386 * User specific resize event.
387 */
388 case SDL_USER_EVENT_RESIZE:
389 return CONEVENT_USR_SCREENRESIZE;
390
391 /*
392 * User specific update title bar notification event
393 */
394 case SDL_USER_EVENT_UPDATE_TITLEBAR:
395 return CONEVENT_USR_TITLEBARUPDATE;
396
397 /*
398 * User specific termination event
399 */
400 case SDL_USER_EVENT_TERMINATE:
401 {
402 if (ev->user.code != VBOXSDL_TERM_NORMAL)
403 RTPrintf("Error: VM terminated abnormally!\n");
404 return CONEVENT_USR_QUIT;
405 }
406
407#ifdef VBOX_SECURELABEL
408 /*
409 * User specific secure label update event
410 */
411 case SDL_USER_EVENT_SECURELABEL_UPDATE:
412 return CONEVENT_USR_SECURELABELUPDATE;
413
414#endif /* VBOX_SECURELABEL */
415
416 /*
417 * User specific pointer shape change event
418 */
419 case SDL_USER_EVENT_POINTER_CHANGE:
420 {
421 PointerShapeChangeData *data =
422 (PointerShapeChangeData *) ev->user.data1;
423 setPointerShape (data);
424 delete data;
425 break;
426 }
427
428 case SDL_VIDEORESIZE:
429 {
430 /* ignore this */
431 break;
432 }
433
434 default:
435 {
436 printf("%s:%d unknown SDL event %d\n",__FILE__,__LINE__,ev->type);
437 LogBird(("unknown SDL event %d\n", ev->type));
438 break;
439 }
440 }
441 return CONEVENT_NONE;
442}
443
444/**
445 * Push the exit event forcing the main event loop to terminate.
446 */
447void SDLConsole::doEventQuit()
448{
449 SDL_Event event;
450
451 event.type = SDL_USEREVENT;
452 event.user.type = SDL_USER_EVENT_TERMINATE;
453 event.user.code = VBOXSDL_TERM_NORMAL;
454 SDL_PushEvent(&event);
455}
456
457void SDLConsole::eventQuit()
458{
459 doEventQuit();
460}
461
462#if defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_OS2)
463/**
464 * Fallback keycode conversion using SDL symbols.
465 *
466 * This is used to catch keycodes that's missing from the translation table.
467 *
468 * @returns XT scancode
469 * @param ev SDL scancode
470 */
471static uint8_t Keyevent2KeycodeFallback(const SDL_KeyboardEvent *ev)
472{
473 const SDLKey sym = ev->keysym.sym;
474 Log(("SDL key event: sym=%d scancode=%#x unicode=%#x\n",
475 sym, ev->keysym.scancode, ev->keysym.unicode));
476 switch (sym)
477 { /* set 1 scan code */
478 case SDLK_ESCAPE: return 0x01;
479 case SDLK_EXCLAIM:
480 case SDLK_1: return 0x02;
481 case SDLK_AT:
482 case SDLK_2: return 0x03;
483 case SDLK_HASH:
484 case SDLK_3: return 0x04;
485 case SDLK_DOLLAR:
486 case SDLK_4: return 0x05;
487 /* % */
488 case SDLK_5: return 0x06;
489 case SDLK_CARET:
490 case SDLK_6: return 0x07;
491 case SDLK_AMPERSAND:
492 case SDLK_7: return 0x08;
493 case SDLK_ASTERISK:
494 case SDLK_8: return 0x09;
495 case SDLK_LEFTPAREN:
496 case SDLK_9: return 0x0a;
497 case SDLK_RIGHTPAREN:
498 case SDLK_0: return 0x0b;
499 case SDLK_UNDERSCORE:
500 case SDLK_MINUS: return 0x0c;
501 case SDLK_EQUALS:
502 case SDLK_PLUS: return 0x0d;
503 case SDLK_BACKSPACE: return 0x0e;
504 case SDLK_TAB: return 0x0f;
505 case SDLK_q: return 0x10;
506 case SDLK_w: return 0x11;
507 case SDLK_e: return 0x12;
508 case SDLK_r: return 0x13;
509 case SDLK_t: return 0x14;
510 case SDLK_y: return 0x15;
511 case SDLK_u: return 0x16;
512 case SDLK_i: return 0x17;
513 case SDLK_o: return 0x18;
514 case SDLK_p: return 0x19;
515 case SDLK_LEFTBRACKET: return 0x1a;
516 case SDLK_RIGHTBRACKET: return 0x1b;
517 case SDLK_RETURN: return 0x1c;
518 case SDLK_KP_ENTER: return 0x1c | 0x80;
519 case SDLK_LCTRL: return 0x1d;
520 case SDLK_RCTRL: return 0x1d | 0x80;
521 case SDLK_a: return 0x1e;
522 case SDLK_s: return 0x1f;
523 case SDLK_d: return 0x20;
524 case SDLK_f: return 0x21;
525 case SDLK_g: return 0x22;
526 case SDLK_h: return 0x23;
527 case SDLK_j: return 0x24;
528 case SDLK_k: return 0x25;
529 case SDLK_l: return 0x26;
530 case SDLK_COLON:
531 case SDLK_SEMICOLON: return 0x27;
532 case SDLK_QUOTEDBL:
533 case SDLK_QUOTE: return 0x28;
534 case SDLK_BACKQUOTE: return 0x29;
535 case SDLK_LSHIFT: return 0x2a;
536 case SDLK_BACKSLASH: return 0x2b;
537 case SDLK_z: return 0x2c;
538 case SDLK_x: return 0x2d;
539 case SDLK_c: return 0x2e;
540 case SDLK_v: return 0x2f;
541 case SDLK_b: return 0x30;
542 case SDLK_n: return 0x31;
543 case SDLK_m: return 0x32;
544 case SDLK_LESS:
545 case SDLK_COMMA: return 0x33;
546 case SDLK_GREATER:
547 case SDLK_PERIOD: return 0x34;
548 case SDLK_KP_DIVIDE: /*??*/
549 case SDLK_QUESTION:
550 case SDLK_SLASH: return 0x35;
551 case SDLK_RSHIFT: return 0x36;
552 case SDLK_KP_MULTIPLY:
553 case SDLK_PRINT: return 0x37; /* fixme */
554 case SDLK_LALT: return 0x38;
555 case SDLK_MODE: /* alt gr*/
556 case SDLK_RALT: return 0x38 | 0x80;
557 case SDLK_SPACE: return 0x39;
558 case SDLK_CAPSLOCK: return 0x3a;
559 case SDLK_F1: return 0x3b;
560 case SDLK_F2: return 0x3c;
561 case SDLK_F3: return 0x3d;
562 case SDLK_F4: return 0x3e;
563 case SDLK_F5: return 0x3f;
564 case SDLK_F6: return 0x40;
565 case SDLK_F7: return 0x41;
566 case SDLK_F8: return 0x42;
567 case SDLK_F9: return 0x43;
568 case SDLK_F10: return 0x44;
569 case SDLK_PAUSE: return 0x45; /* not right */
570 case SDLK_NUMLOCK: return 0x45;
571 case SDLK_SCROLLOCK: return 0x46;
572 case SDLK_KP7: return 0x47;
573 case SDLK_HOME: return 0x47 | 0x80;
574 case SDLK_KP8: return 0x48;
575 case SDLK_UP: return 0x48 | 0x80;
576 case SDLK_KP9: return 0x49;
577 case SDLK_PAGEUP: return 0x49 | 0x80;
578 case SDLK_KP_MINUS: return 0x4a;
579 case SDLK_KP4: return 0x4b;
580 case SDLK_LEFT: return 0x4b | 0x80;
581 case SDLK_KP5: return 0x4c;
582 case SDLK_KP6: return 0x4d;
583 case SDLK_RIGHT: return 0x4d | 0x80;
584 case SDLK_KP_PLUS: return 0x4e;
585 case SDLK_KP1: return 0x4f;
586 case SDLK_END: return 0x4f | 0x80;
587 case SDLK_KP2: return 0x50;
588 case SDLK_DOWN: return 0x50 | 0x80;
589 case SDLK_KP3: return 0x51;
590 case SDLK_PAGEDOWN: return 0x51 | 0x80;
591 case SDLK_KP0: return 0x52;
592 case SDLK_INSERT: return 0x52 | 0x80;
593 case SDLK_KP_PERIOD: return 0x53;
594 case SDLK_DELETE: return 0x53 | 0x80;
595 case SDLK_SYSREQ: return 0x54;
596 case SDLK_F11: return 0x57;
597 case SDLK_F12: return 0x58;
598 case SDLK_F13: return 0x5b;
599 case SDLK_LMETA:
600 case SDLK_LSUPER: return 0x5b | 0x80;
601 case SDLK_F14: return 0x5c;
602 case SDLK_RMETA:
603 case SDLK_RSUPER: return 0x5c | 0x80;
604 case SDLK_F15: return 0x5d;
605 case SDLK_MENU: return 0x5d | 0x80;
606#if 0
607 case SDLK_CLEAR: return 0x;
608 case SDLK_KP_EQUALS: return 0x;
609 case SDLK_COMPOSE: return 0x;
610 case SDLK_HELP: return 0x;
611 case SDLK_BREAK: return 0x;
612 case SDLK_POWER: return 0x;
613 case SDLK_EURO: return 0x;
614 case SDLK_UNDO: return 0x;
615#endif
616 default:
617 Log(("Unhandled sdl key event: sym=%d scancode=%#x unicode=%#x\n",
618 ev->keysym.sym, ev->keysym.scancode, ev->keysym.unicode));
619 return 0;
620 }
621}
622#endif /* RT_OS_DARWIN */
623
624/**
625 * Converts an SDL keyboard eventcode to a XT scancode.
626 *
627 * @returns XT scancode
628 * @param ev SDL scancode
629 */
630uint8_t SDLConsole::keyEventToKeyCode(const SDL_KeyboardEvent *ev)
631{
632 int keycode;
633
634 // start with the scancode determined by SDL
635 keycode = ev->keysym.scancode;
636
637#if defined(RT_OS_LINUX)
638 // workaround for SDL keyboard translation issues on Linux
639 // keycodes > 0x100 are sent as 0xe0 keycode
640 // Note that these are the keycodes used by XFree86/X.org
641 // servers on a Linux host, and will almost certainly not
642 // work on other hosts or on other servers on Linux hosts.
643 // For a more general approach, see the Wine code in the GUI.
644 static const uint8_t x_keycode_to_pc_keycode[61] =
645 {
646 0xc7, /* 97 Home */
647 0xc8, /* 98 Up */
648 0xc9, /* 99 PgUp */
649 0xcb, /* 100 Left */
650 0x4c, /* 101 KP-5 */
651 0xcd, /* 102 Right */
652 0xcf, /* 103 End */
653 0xd0, /* 104 Down */
654 0xd1, /* 105 PgDn */
655 0xd2, /* 106 Ins */
656 0xd3, /* 107 Del */
657 0x9c, /* 108 Enter */
658 0x9d, /* 109 Ctrl-R */
659 0x0, /* 110 Pause */
660 0xb7, /* 111 Print */
661 0xb5, /* 112 Divide */
662 0xb8, /* 113 Alt-R */
663 0xc6, /* 114 Break */
664 0x0, /* 115 */
665 0x0, /* 116 */
666 0x0, /* 117 */
667 0x0, /* 118 */
668 0x0, /* 119 */
669 0x70, /* 120 Hiragana_Katakana */
670 0x0, /* 121 */
671 0x0, /* 122 */
672 0x73, /* 123 backslash */
673 0x0, /* 124 */
674 0x0, /* 125 */
675 0x0, /* 126 */
676 0x0, /* 127 */
677 0x0, /* 128 */
678 0x79, /* 129 Henkan */
679 0x0, /* 130 */
680 0x7b, /* 131 Muhenkan */
681 0x0, /* 132 */
682 0x7d, /* 133 Yen */
683 0x0, /* 134 */
684 0x0, /* 135 */
685 0x47, /* 136 KP_7 */
686 0x48, /* 137 KP_8 */
687 0x49, /* 138 KP_9 */
688 0x4b, /* 139 KP_4 */
689 0x4c, /* 140 KP_5 */
690 0x4d, /* 141 KP_6 */
691 0x4f, /* 142 KP_1 */
692 0x50, /* 143 KP_2 */
693 0x51, /* 144 KP_3 */
694 0x52, /* 145 KP_0 */
695 0x53, /* 146 KP_. */
696 0x47, /* 147 KP_HOME */
697 0x48, /* 148 KP_UP */
698 0x49, /* 149 KP_PgUp */
699 0x4b, /* 150 KP_Left */
700 0x4c, /* 151 KP_ */
701 0x4d, /* 152 KP_Right */
702 0x4f, /* 153 KP_End */
703 0x50, /* 154 KP_Down */
704 0x51, /* 155 KP_PgDn */
705 0x52, /* 156 KP_Ins */
706 0x53, /* 157 KP_Del */
707 };
708
709 if (keycode < 9)
710 {
711 keycode = 0;
712 }
713 else if (keycode < 97)
714 {
715 // just an offset
716 keycode -= 8;
717 }
718 else if (keycode < 158)
719 {
720 // apply conversion table
721 keycode = x_keycode_to_pc_keycode[keycode - 97];
722 }
723 else
724 {
725 keycode = 0;
726 }
727
728#elif defined(RT_OS_DARWIN)
729 /* This is derived partially from SDL_QuartzKeys.h and partially from testing. */
730 static const uint8_t s_aMacToSet1[] =
731 {
732 /* set-1 SDL_QuartzKeys.h */
733 0x1e, /* QZ_a 0x00 */
734 0x1f, /* QZ_s 0x01 */
735 0x20, /* QZ_d 0x02 */
736 0x21, /* QZ_f 0x03 */
737 0x23, /* QZ_h 0x04 */
738 0x22, /* QZ_g 0x05 */
739 0x2c, /* QZ_z 0x06 */
740 0x2d, /* QZ_x 0x07 */
741 0x2e, /* QZ_c 0x08 */
742 0x2f, /* QZ_v 0x09 */
743 0x56, /* between lshift and z. 'INT 1'? */
744 0x30, /* QZ_b 0x0B */
745 0x10, /* QZ_q 0x0C */
746 0x11, /* QZ_w 0x0D */
747 0x12, /* QZ_e 0x0E */
748 0x13, /* QZ_r 0x0F */
749 0x15, /* QZ_y 0x10 */
750 0x14, /* QZ_t 0x11 */
751 0x02, /* QZ_1 0x12 */
752 0x03, /* QZ_2 0x13 */
753 0x04, /* QZ_3 0x14 */
754 0x05, /* QZ_4 0x15 */
755 0x07, /* QZ_6 0x16 */
756 0x06, /* QZ_5 0x17 */
757 0x0d, /* QZ_EQUALS 0x18 */
758 0x0a, /* QZ_9 0x19 */
759 0x08, /* QZ_7 0x1A */
760 0x0c, /* QZ_MINUS 0x1B */
761 0x09, /* QZ_8 0x1C */
762 0x0b, /* QZ_0 0x1D */
763 0x1b, /* QZ_RIGHTBRACKET 0x1E */
764 0x18, /* QZ_o 0x1F */
765 0x16, /* QZ_u 0x20 */
766 0x1a, /* QZ_LEFTBRACKET 0x21 */
767 0x17, /* QZ_i 0x22 */
768 0x19, /* QZ_p 0x23 */
769 0x1c, /* QZ_RETURN 0x24 */
770 0x26, /* QZ_l 0x25 */
771 0x24, /* QZ_j 0x26 */
772 0x28, /* QZ_QUOTE 0x27 */
773 0x25, /* QZ_k 0x28 */
774 0x27, /* QZ_SEMICOLON 0x29 */
775 0x2b, /* QZ_BACKSLASH 0x2A */
776 0x33, /* QZ_COMMA 0x2B */
777 0x35, /* QZ_SLASH 0x2C */
778 0x31, /* QZ_n 0x2D */
779 0x32, /* QZ_m 0x2E */
780 0x34, /* QZ_PERIOD 0x2F */
781 0x0f, /* QZ_TAB 0x30 */
782 0x39, /* QZ_SPACE 0x31 */
783 0x29, /* QZ_BACKQUOTE 0x32 */
784 0x0e, /* QZ_BACKSPACE 0x33 */
785 0x9c, /* QZ_IBOOK_ENTER 0x34 */
786 0x01, /* QZ_ESCAPE 0x35 */
787 0x5c|0x80, /* QZ_RMETA 0x36 */
788 0x5b|0x80, /* QZ_LMETA 0x37 */
789 0x2a, /* QZ_LSHIFT 0x38 */
790 0x3a, /* QZ_CAPSLOCK 0x39 */
791 0x38, /* QZ_LALT 0x3A */
792 0x1d, /* QZ_LCTRL 0x3B */
793 0x36, /* QZ_RSHIFT 0x3C */
794 0x38|0x80, /* QZ_RALT 0x3D */
795 0x1d|0x80, /* QZ_RCTRL 0x3E */
796 0, /* */
797 0, /* */
798 0x53, /* QZ_KP_PERIOD 0x41 */
799 0, /* */
800 0x37, /* QZ_KP_MULTIPLY 0x43 */
801 0, /* */
802 0x4e, /* QZ_KP_PLUS 0x45 */
803 0, /* */
804 0x45, /* QZ_NUMLOCK 0x47 */
805 0, /* */
806 0, /* */
807 0, /* */
808 0x35|0x80, /* QZ_KP_DIVIDE 0x4B */
809 0x1c|0x80, /* QZ_KP_ENTER 0x4C */
810 0, /* */
811 0x4a, /* QZ_KP_MINUS 0x4E */
812 0, /* */
813 0, /* */
814 0x0d/*?*/, /* QZ_KP_EQUALS 0x51 */
815 0x52, /* QZ_KP0 0x52 */
816 0x4f, /* QZ_KP1 0x53 */
817 0x50, /* QZ_KP2 0x54 */
818 0x51, /* QZ_KP3 0x55 */
819 0x4b, /* QZ_KP4 0x56 */
820 0x4c, /* QZ_KP5 0x57 */
821 0x4d, /* QZ_KP6 0x58 */
822 0x47, /* QZ_KP7 0x59 */
823 0, /* */
824 0x48, /* QZ_KP8 0x5B */
825 0x49, /* QZ_KP9 0x5C */
826 0, /* */
827 0, /* */
828 0, /* */
829 0x3f, /* QZ_F5 0x60 */
830 0x40, /* QZ_F6 0x61 */
831 0x41, /* QZ_F7 0x62 */
832 0x3d, /* QZ_F3 0x63 */
833 0x42, /* QZ_F8 0x64 */
834 0x43, /* QZ_F9 0x65 */
835 0, /* */
836 0x57, /* QZ_F11 0x67 */
837 0, /* */
838 0x37|0x80, /* QZ_PRINT / F13 0x69 */
839 0x63, /* QZ_F16 0x6A */
840 0x46, /* QZ_SCROLLOCK 0x6B */
841 0, /* */
842 0x44, /* QZ_F10 0x6D */
843 0x5d|0x80, /* */
844 0x58, /* QZ_F12 0x6F */
845 0, /* */
846 0/* 0xe1,0x1d,0x45*/, /* QZ_PAUSE 0x71 */
847 0x52|0x80, /* QZ_INSERT / HELP 0x72 */
848 0x47|0x80, /* QZ_HOME 0x73 */
849 0x49|0x80, /* QZ_PAGEUP 0x74 */
850 0x53|0x80, /* QZ_DELETE 0x75 */
851 0x3e, /* QZ_F4 0x76 */
852 0x4f|0x80, /* QZ_END 0x77 */
853 0x3c, /* QZ_F2 0x78 */
854 0x51|0x80, /* QZ_PAGEDOWN 0x79 */
855 0x3b, /* QZ_F1 0x7A */
856 0x4b|0x80, /* QZ_LEFT 0x7B */
857 0x4d|0x80, /* QZ_RIGHT 0x7C */
858 0x50|0x80, /* QZ_DOWN 0x7D */
859 0x48|0x80, /* QZ_UP 0x7E */
860 0x5e|0x80, /* QZ_POWER 0x7F */ /* have different break key! */
861 };
862
863 if (keycode == 0)
864 {
865 /* This could be a modifier or it could be 'a'. */
866 switch (ev->keysym.sym)
867 {
868 case SDLK_LSHIFT: keycode = 0x2a; break;
869 case SDLK_RSHIFT: keycode = 0x36; break;
870 case SDLK_LCTRL: keycode = 0x1d; break;
871 case SDLK_RCTRL: keycode = 0x1d | 0x80; break;
872 case SDLK_LALT: keycode = 0x38; break;
873 case SDLK_MODE: /* alt gr */
874 case SDLK_RALT: keycode = 0x38 | 0x80; break;
875 case SDLK_RMETA:
876 case SDLK_RSUPER: keycode = 0x5c | 0x80; break;
877 case SDLK_LMETA:
878 case SDLK_LSUPER: keycode = 0x5b | 0x80; break;
879 /* Assumes normal key. */
880 default: keycode = s_aMacToSet1[keycode]; break;
881 }
882 }
883 else
884 {
885 if ((unsigned)keycode < RT_ELEMENTS(s_aMacToSet1))
886 keycode = s_aMacToSet1[keycode];
887 else
888 keycode = 0;
889 if (!keycode)
890 {
891#ifdef DEBUG_bird
892 RTPrintf("Untranslated: keycode=%#x (%d)\n", keycode, keycode);
893#endif
894 keycode = Keyevent2KeycodeFallback(ev);
895 }
896 }
897#ifdef DEBUG_bird
898 RTPrintf("scancode=%#x -> %#x\n", ev->keysym.scancode, keycode);
899#endif
900
901#elif defined(RT_OS_SOLARIS) || defined(RT_OS_OS2)
902 /*
903 * For now, just use the fallback code.
904 */
905 keycode = Keyevent2KeycodeFallback(ev);
906#endif
907 return keycode;
908}
909
910/**
911 * Releases any modifier keys that are currently in pressed state.
912 */
913void SDLConsole::resetKeys(void)
914{
915 int i;
916 for(i = 0; i < 256; i++)
917 {
918 if (gaModifiersState[i])
919 {
920 if (i & 0x80)
921 gKeyboard->PutScancode(0xe0);
922 gKeyboard->PutScancode(i | 0x80);
923 gaModifiersState[i] = 0;
924 }
925 }
926}
927
928VMMDev *SDLConsole::getVMMDev()
929{
930 return gVMMDev;
931}
932
933Display *SDLConsole::getDisplay()
934{
935 return gDisplay;
936}
937
938/**
939 * Keyboard event handler.
940 *
941 * @param ev SDL keyboard event.
942 */
943void SDLConsole::processKey(SDL_KeyboardEvent *ev)
944{
945 int keycode, v;
946
947#if defined(VBOXSDL_ADVANCED_OPTIONS) && defined(DEBUG) && 0
948#error
949 // first handle the debugger hotkeys
950 uint8_t *keystate = SDL_GetKeyState(NULL);
951 if ((keystate[SDLK_LALT]) && (ev->type == SDL_KEYDOWN))
952 {
953 switch (ev->keysym.sym)
954 {
955 // pressing Alt-F12 toggles the supervisor recompiler
956 case SDLK_F12:
957 {
958 if (gMachineDebugger)
959 {
960 BOOL recompileSupervisor;
961 gMachineDebugger->COMGETTER(RecompileSupervisor)(&recompileSupervisor);
962 gMachineDebugger->COMSETTER(RecompileSupervisor)(!recompileSupervisor);
963 }
964 break;
965 }
966 // pressing Alt-F11 toggles the user recompiler
967 case SDLK_F11:
968 {
969 if (gMachineDebugger)
970 {
971 BOOL recompileUser;
972 gMachineDebugger->COMGETTER(RecompileUser)(&recompileUser);
973 gMachineDebugger->COMSETTER(RecompileUser)(!recompileUser);
974 }
975 break;
976 }
977 // pressing Alt-F10 toggles the patch manager
978 case SDLK_F10:
979 {
980 if (gMachineDebugger)
981 {
982 BOOL patmEnabled;
983 gMachineDebugger->COMGETTER(PATMEnabled)(&patmEnabled);
984 gMachineDebugger->COMSETTER(PATMEnabled)(!patmEnabled);
985 }
986 break;
987 }
988 // pressing Alt-F9 toggles CSAM
989 case SDLK_F9:
990 {
991 if (gMachineDebugger)
992 {
993 BOOL csamEnabled;
994 gMachineDebugger->COMGETTER(CSAMEnabled)(&csamEnabled);
995 gMachineDebugger->COMSETTER(CSAMEnabled)(!csamEnabled);
996 }
997 break;
998 }
999 // pressing Alt-F8 toggles singlestepping mode
1000 case SDLK_F8:
1001 {
1002 if (gMachineDebugger)
1003 {
1004 BOOL singlestepEnabled;
1005 gMachineDebugger->COMGETTER(Singlestep)(&singlestepEnabled);
1006 gMachineDebugger->COMSETTER(Singlestep)(!singlestepEnabled);
1007 }
1008 break;
1009 }
1010
1011 default:
1012 break;
1013 }
1014 }
1015 // pressing Ctrl-F12 toggles the logger
1016 else if ((keystate[SDLK_RCTRL] || keystate[SDLK_LCTRL]) &&
1017 (ev->keysym.sym == SDLK_F12) && (ev->type == SDL_KEYDOWN))
1018 {
1019 PRTLOGGER pLogger = RTLogDefaultInstance();
1020 bool fEnabled = (pLogger && !(pLogger->fFlags & RTLOGFLAGS_DISABLED));
1021 if (fEnabled)
1022 {
1023 RTLogFlags(pLogger, "disabled");
1024 }
1025 else
1026 {
1027 RTLogFlags(pLogger, "nodisabled");
1028 }
1029 }
1030 // pressing F12 sets a logmark
1031 else if ((ev->keysym.sym == SDLK_F12) && (ev->type == SDL_KEYDOWN))
1032 {
1033 RTLogPrintf("****** LOGGING MARK ******\n");
1034 RTLogFlush(NULL);
1035 }
1036 // now update the titlebar flags
1037 updateTitlebar();
1038#endif
1039
1040 // the pause key is the weirdest, needs special handling
1041 if (ev->keysym.sym == SDLK_PAUSE)
1042 {
1043 v = 0;
1044 if (ev->type == SDL_KEYUP)
1045 v |= 0x80;
1046 gKeyboard->PutScancode(0xe1);
1047 gKeyboard->PutScancode(0x1d | v);
1048 gKeyboard->PutScancode(0x45 | v);
1049 return;
1050 }
1051
1052 /*
1053 * Perform SDL key event to scancode conversion
1054 */
1055 keycode = keyEventToKeyCode(ev);
1056
1057 switch(keycode)
1058 {
1059 case 0x00:
1060 {
1061 /* sent when leaving window: reset the modifiers state */
1062 resetKeys();
1063 return;
1064 }
1065
1066 case 0x2a: /* Left Shift */
1067 case 0x36: /* Right Shift */
1068 case 0x1d: /* Left CTRL */
1069 case 0x9d: /* Right CTRL */
1070 case 0x38: /* Left ALT */
1071 case 0xb8: /* Right ALT */
1072 {
1073 if (ev->type == SDL_KEYUP)
1074 gaModifiersState[keycode] = 0;
1075 else
1076 gaModifiersState[keycode] = 1;
1077 break;
1078 }
1079
1080 case 0x45: /* num lock */
1081 case 0x3a: /* caps lock */
1082 {
1083 /* SDL does not send the key up event, so we generate it */
1084 gKeyboard->PutScancode(keycode);
1085 gKeyboard->PutScancode(keycode | 0x80);
1086 return;
1087 }
1088 }
1089
1090 /*
1091 * Now we send the event. Apply extended and release prefixes.
1092 */
1093 if (keycode & 0x80)
1094 gKeyboard->PutScancode(0xe0);
1095 if (ev->type == SDL_KEYUP)
1096 gKeyboard->PutScancode(keycode | 0x80);
1097 else
1098 gKeyboard->PutScancode(keycode & 0x7f);
1099}
1100
1101#ifdef RT_OS_DARWIN
1102
1103RT_C_DECLS_BEGIN
1104/* Private interface in 10.3 and later. */
1105typedef int CGSConnection;
1106typedef enum
1107{
1108 kCGSGlobalHotKeyEnable = 0,
1109 kCGSGlobalHotKeyDisable,
1110 kCGSGlobalHotKeyInvalid = -1 /* bird */
1111} CGSGlobalHotKeyOperatingMode;
1112extern CGSConnection _CGSDefaultConnection(void);
1113extern CGError CGSGetGlobalHotKeyOperatingMode(CGSConnection Connection, CGSGlobalHotKeyOperatingMode *enmMode);
1114extern CGError CGSSetGlobalHotKeyOperatingMode(CGSConnection Connection, CGSGlobalHotKeyOperatingMode enmMode);
1115RT_C_DECLS_END
1116
1117/** Keeping track of whether we disabled the hotkeys or not. */
1118static bool g_fHotKeysDisabled = false;
1119/** Whether we've connected or not. */
1120static bool g_fConnectedToCGS = false;
1121/** Cached connection. */
1122static CGSConnection g_CGSConnection;
1123
1124/**
1125 * Disables or enabled global hot keys.
1126 */
1127static void DisableGlobalHotKeys(bool fDisable)
1128{
1129 if (!g_fConnectedToCGS)
1130 {
1131 g_CGSConnection = _CGSDefaultConnection();
1132 g_fConnectedToCGS = true;
1133 }
1134
1135 /* get current mode. */
1136 CGSGlobalHotKeyOperatingMode enmMode = kCGSGlobalHotKeyInvalid;
1137 CGSGetGlobalHotKeyOperatingMode(g_CGSConnection, &enmMode);
1138
1139 /* calc new mode. */
1140 if (fDisable)
1141 {
1142 if (enmMode != kCGSGlobalHotKeyEnable)
1143 return;
1144 enmMode = kCGSGlobalHotKeyDisable;
1145 }
1146 else
1147 {
1148 if ( enmMode != kCGSGlobalHotKeyDisable
1149 /*|| !g_fHotKeysDisabled*/)
1150 return;
1151 enmMode = kCGSGlobalHotKeyEnable;
1152 }
1153
1154 /* try set it and check the actual result. */
1155 CGSSetGlobalHotKeyOperatingMode(g_CGSConnection, enmMode);
1156 CGSGlobalHotKeyOperatingMode enmNewMode = kCGSGlobalHotKeyInvalid;
1157 CGSGetGlobalHotKeyOperatingMode(g_CGSConnection, &enmNewMode);
1158 if (enmNewMode == enmMode)
1159 g_fHotKeysDisabled = enmMode == kCGSGlobalHotKeyDisable;
1160}
1161#endif /* RT_OS_DARWIN */
1162
1163/**
1164 * Start grabbing the mouse.
1165 */
1166void SDLConsole::inputGrabStart()
1167{
1168 BOOL fNeedsHostCursor;
1169 gMouse->COMGETTER(NeedsHostCursor)(&fNeedsHostCursor);
1170#ifdef RT_OS_DARWIN
1171 DisableGlobalHotKeys(true);
1172#endif
1173 if (!fNeedsHostCursor)
1174 SDL_ShowCursor(SDL_DISABLE);
1175 SDL_WM_GrabInput(SDL_GRAB_ON);
1176 // dummy read to avoid moving the mouse
1177 SDL_GetRelativeMouseState(NULL, NULL);
1178 mfInputGrab = true;
1179 updateTitlebar();
1180}
1181
1182/**
1183 * End mouse grabbing.
1184 */
1185void SDLConsole::inputGrabEnd()
1186{
1187 BOOL fNeedsHostCursor;
1188 gMouse->COMGETTER(NeedsHostCursor)(&fNeedsHostCursor);
1189 SDL_WM_GrabInput(SDL_GRAB_OFF);
1190 if (!fNeedsHostCursor)
1191 SDL_ShowCursor(SDL_ENABLE);
1192#ifdef RT_OS_DARWIN
1193 DisableGlobalHotKeys(false);
1194#endif
1195 mfInputGrab = false;
1196 updateTitlebar();
1197}
1198
1199/**
1200 * Query mouse position and button state from SDL and send to the VM
1201 *
1202 * @param dz Relative mouse wheel movement
1203 */
1204
1205extern int GetRelativeMouseState(int *, int*);
1206extern int GetMouseState(int *, int*);
1207
1208void SDLConsole::mouseSendEvent(int dz)
1209{
1210 int x, y, state, buttons;
1211 bool abs;
1212 BOOL fMouseAbsolute;
1213 BOOL fNeedsHostCursor;
1214
1215 gMouse->COMGETTER(AbsoluteSupported)(&fMouseAbsolute);
1216 gMouse->COMGETTER(NeedsHostCursor)(&fNeedsHostCursor);
1217 abs = (fMouseAbsolute && !mfInputGrab) || fNeedsHostCursor;
1218
1219 state = abs ? SDL_GetMouseState(&x, &y) : SDL_GetRelativeMouseState(&x, &y);
1220
1221 // process buttons
1222 buttons = 0;
1223 if (state & SDL_BUTTON(SDL_BUTTON_LEFT))
1224 buttons |= PDMIMOUSEPORT_BUTTON_LEFT;
1225 if (state & SDL_BUTTON(SDL_BUTTON_RIGHT))
1226 buttons |= PDMIMOUSEPORT_BUTTON_RIGHT;
1227 if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE))
1228 buttons |= PDMIMOUSEPORT_BUTTON_MIDDLE;
1229#ifdef SDL_BUTTON_X1
1230 if (state & SDL_BUTTON(SDL_BUTTON_X1))
1231 buttons |= PDMIMOUSEPORT_BUTTON_X1;
1232#endif
1233#ifdef SDL_BUTTON_X2
1234 if (state & SDL_BUTTON(SDL_BUTTON_X2))
1235 buttons |= PDMIMOUSEPORT_BUTTON_X2;
1236#endif
1237
1238 // now send the mouse event
1239 if (abs)
1240 {
1241 /**
1242 * @todo
1243 * PutMouseEventAbsolute() expects x and y starting from 1,1.
1244 * should we do the increment internally in PutMouseEventAbsolute()
1245 * or state it in PutMouseEventAbsolute() docs?
1246 */
1247 /* only send if outside the extra offset area */
1248 if (y >= gFramebuffer->getYOffset())
1249 gMouse->PutMouseEventAbsolute(x + 1, y + 1 - gFramebuffer->getYOffset(), dz, 0, buttons);
1250 }
1251 else
1252 {
1253 gMouse->PutMouseEvent(x, y, dz, 0, buttons);
1254 }
1255}
1256
1257/**
1258 * Update the pointer shape or visibility.
1259 *
1260 * This is called when the mouse pointer shape changes or pointer is
1261 * hidden/displaying. The new shape is passed as a caller allocated
1262 * buffer that will be freed after returning.
1263 *
1264 * @param fVisible Whether the pointer is visible or not.
1265 * @param fAlpha Alpha channel information is present.
1266 * @param xHot Horizontal coordinate of the pointer hot spot.
1267 * @param yHot Vertical coordinate of the pointer hot spot.
1268 * @param width Pointer width in pixels.
1269 * @param height Pointer height in pixels.
1270 * @param pShape The shape buffer. If NULL, then only
1271 * pointer visibility is being changed
1272 */
1273void SDLConsole::onMousePointerShapeChange(bool fVisible,
1274 bool fAlpha, uint32_t xHot,
1275 uint32_t yHot, uint32_t width,
1276 uint32_t height, void *pShape)
1277{
1278 PointerShapeChangeData *data;
1279 data = new PointerShapeChangeData (fVisible, fAlpha, xHot, yHot,
1280 width, height, (const uint8_t *) pShape);
1281 Assert (data);
1282 if (!data)
1283 return;
1284
1285 SDL_Event event = {0};
1286 event.type = SDL_USEREVENT;
1287 event.user.type = SDL_USER_EVENT_POINTER_CHANGE;
1288 event.user.data1 = data;
1289
1290 int rc = SDL_PushEvent (&event);
1291 AssertMsg (!rc, ("Error: SDL_PushEvent was not successful!\n"));
1292 if (rc)
1293 delete data;
1294}
1295
1296void SDLConsole::progressInfo(PVM pVM, unsigned uPercent, void *pvUser)
1297{
1298 if (uPercent != g_uProgressPercent)
1299 {
1300 SDL_Event event = {0};
1301 event.type = SDL_USEREVENT;
1302 event.user.type = SDL_USER_EVENT_UPDATE_TITLEBAR;
1303 SDL_PushEvent(&event);
1304 g_uProgressPercent = uPercent;
1305 }
1306}
1307
1308/**
1309 * Build the titlebar string
1310 */
1311void SDLConsole::updateTitlebar()
1312{
1313 char pszTitle[1024];
1314
1315 RTStrPrintf(pszTitle, sizeof(pszTitle),
1316 VBOX_PRODUCT "%s%s",
1317 g_uProgressPercent == ~0U && machineState == VMSTATE_SUSPENDED ? " - [Paused]" : "",
1318 mfInputGrab ? " - [Input captured]": "");
1319
1320 if (g_uProgressPercent != ~0U)
1321 RTStrPrintf(pszTitle + strlen(pszTitle), sizeof(pszTitle) - strlen(pszTitle),
1322 " - %s: %u%%", g_pszProgressString, g_uProgressPercent);
1323
1324#if defined(VBOXSDL_ADVANCED_OPTIONS) && defined(DEBUG)
1325 // do we have a debugger interface
1326 if (gMachineDebugger)
1327 {
1328 // query the machine state
1329 BOOL recompileSupervisor = FALSE;
1330 BOOL recompileUser = FALSE;
1331 BOOL patmEnabled = FALSE;
1332 BOOL csamEnabled = FALSE;
1333 BOOL singlestepEnabled = FALSE;
1334 gMachineDebugger->COMGETTER(RecompileSupervisor)(&recompileSupervisor);
1335 gMachineDebugger->COMGETTER(RecompileUser)(&recompileUser);
1336 gMachineDebugger->COMGETTER(PATMEnabled)(&patmEnabled);
1337 gMachineDebugger->COMGETTER(CSAMEnabled)(&csamEnabled);
1338 gMachineDebugger->COMGETTER(Singlestep)(&singlestepEnabled);
1339 PRTLOGGER pLogger = RTLogDefaultInstance();
1340 bool fEnabled = (pLogger && !(pLogger->fFlags & RTLOGFLAGS_DISABLED));
1341 RTStrPrintf(pszTitle + strlen(pszTitle), sizeof(pszTitle) - strlen(pszTitle),
1342 " [STEP=%d CS=%d PAT=%d RR0=%d RR3=%d LOG=%d]",
1343 singlestepEnabled == TRUE, csamEnabled == TRUE, patmEnabled == TRUE,
1344 recompileSupervisor == FALSE, recompileUser == FALSE, fEnabled == TRUE);
1345 }
1346#endif /* DEBUG */
1347
1348 SDL_WM_SetCaption(pszTitle, VBOX_PRODUCT);
1349}
1350
1351/**
1352 * Updates the title bar while saving the state.
1353 * @param iPercent Percentage.
1354 */
1355void SDLConsole::updateTitlebarProgress(const char *pszStr, int iPercent)
1356{
1357 char szTitle[256];
1358 AssertMsg(iPercent >= 0 && iPercent <= 100, ("%d\n", iPercent));
1359 RTStrPrintf(szTitle, sizeof(szTitle), VBOX_PRODUCT " - %s %d%%...", pszStr, iPercent);
1360 SDL_WM_SetCaption(szTitle, VBOX_PRODUCT);
1361}
1362
1363/**
1364 * Sets the pointer shape according to parameters.
1365 * Must be called only from the main SDL thread.
1366 */
1367void SDLConsole::setPointerShape (const PointerShapeChangeData *data)
1368{
1369 /*
1370 * don't do anything if there are no guest additions loaded (anymore)
1371 */
1372 BOOL fMouseAbsolute;
1373 gMouse->COMGETTER(AbsoluteSupported)(&fMouseAbsolute);
1374 if (!fMouseAbsolute)
1375 return;
1376
1377 if (data->shape)
1378 {
1379 bool ok = false;
1380
1381 uint32_t andMaskSize = (data->width + 7) / 8 * data->height;
1382 uint32_t srcShapePtrScan = data->width * 4;
1383
1384 const uint8_t *srcAndMaskPtr = data->shape;
1385 const uint8_t *srcShapePtr = data->shape + ((andMaskSize + 3) & ~3);
1386
1387#if defined (RT_OS_WINDOWS)
1388
1389 BITMAPV5HEADER bi;
1390 HBITMAP hBitmap;
1391 void *lpBits;
1392 HCURSOR hAlphaCursor = NULL;
1393
1394 ::ZeroMemory (&bi, sizeof (BITMAPV5HEADER));
1395 bi.bV5Size = sizeof (BITMAPV5HEADER);
1396 bi.bV5Width = data->width;
1397 bi.bV5Height = - (LONG) data->height;
1398 bi.bV5Planes = 1;
1399 bi.bV5BitCount = 32;
1400 bi.bV5Compression = BI_BITFIELDS;
1401 // specify a supported 32 BPP alpha format for Windows XP
1402 bi.bV5RedMask = 0x00FF0000;
1403 bi.bV5GreenMask = 0x0000FF00;
1404 bi.bV5BlueMask = 0x000000FF;
1405 if (data->alpha)
1406 bi.bV5AlphaMask = 0xFF000000;
1407 else
1408 bi.bV5AlphaMask = 0;
1409
1410 HDC hdc = ::GetDC (NULL);
1411
1412 // create the DIB section with an alpha channel
1413 hBitmap = ::CreateDIBSection (hdc, (BITMAPINFO *) &bi, DIB_RGB_COLORS,
1414 (void **) &lpBits, NULL, (DWORD) 0);
1415
1416 ::ReleaseDC (NULL, hdc);
1417
1418 HBITMAP hMonoBitmap = NULL;
1419 if (data->alpha)
1420 {
1421 // create an empty mask bitmap
1422 hMonoBitmap = ::CreateBitmap (data->width, data->height, 1, 1, NULL);
1423 }
1424 else
1425 {
1426 // for now, we assert if width is not multiple of 16. the
1427 // alternative is to manually align the AND mask to 16 bits.
1428 AssertMsg (!(data->width % 16), ("AND mask must be word-aligned!\n"));
1429
1430 // create the AND mask bitmap
1431 hMonoBitmap = ::CreateBitmap (data->width, data->height, 1, 1,
1432 srcAndMaskPtr);
1433 }
1434
1435 Assert (hBitmap);
1436 Assert (hMonoBitmap);
1437 if (hBitmap && hMonoBitmap)
1438 {
1439 DWORD *dstShapePtr = (DWORD *) lpBits;
1440
1441 for (uint32_t y = 0; y < data->height; y ++)
1442 {
1443 memcpy (dstShapePtr, srcShapePtr, srcShapePtrScan);
1444 srcShapePtr += srcShapePtrScan;
1445 dstShapePtr += data->width;
1446 }
1447
1448 ICONINFO ii;
1449 ii.fIcon = FALSE;
1450 ii.xHotspot = data->xHot;
1451 ii.yHotspot = data->yHot;
1452 ii.hbmMask = hMonoBitmap;
1453 ii.hbmColor = hBitmap;
1454
1455 hAlphaCursor = ::CreateIconIndirect (&ii);
1456 Assert (hAlphaCursor);
1457 if (hAlphaCursor)
1458 {
1459 // here we do a dirty trick by substituting a Window Manager's
1460 // cursor handle with the handle we created
1461
1462 WMcursor *old_wm_cursor = gpCustomCursor->wm_cursor;
1463
1464 // see SDL12/src/video/wincommon/SDL_sysmouse.c
1465 void *wm_cursor = malloc (sizeof (HCURSOR) + sizeof (uint8_t *) * 2);
1466 *(HCURSOR *) wm_cursor = hAlphaCursor;
1467
1468 gpCustomCursor->wm_cursor = (WMcursor *) wm_cursor;
1469 SDL_SetCursor (gpCustomCursor);
1470 SDL_ShowCursor (SDL_ENABLE);
1471
1472 if (old_wm_cursor)
1473 {
1474 ::DestroyCursor (* (HCURSOR *) old_wm_cursor);
1475 free (old_wm_cursor);
1476 }
1477
1478 ok = true;
1479 }
1480 }
1481
1482 if (hMonoBitmap)
1483 ::DeleteObject (hMonoBitmap);
1484 if (hBitmap)
1485 ::DeleteObject (hBitmap);
1486
1487#elif defined(VBOXBFE_WITH_X11) && !defined(VBOXBFE_WITHOUT_XCURSOR)
1488
1489 XcursorImage *img = XcursorImageCreate (data->width, data->height);
1490 Assert (img);
1491 if (img)
1492 {
1493 img->xhot = data->xHot;
1494 img->yhot = data->yHot;
1495
1496 XcursorPixel *dstShapePtr = img->pixels;
1497
1498 for (uint32_t y = 0; y < data->height; y ++)
1499 {
1500 memcpy (dstShapePtr, srcShapePtr, srcShapePtrScan);
1501
1502 if (!data->alpha)
1503 {
1504 // convert AND mask to the alpha channel
1505 uint8_t byte = 0;
1506 for (uint32_t x = 0; x < data->width; x ++)
1507 {
1508 if (!(x % 8))
1509 byte = *(srcAndMaskPtr ++);
1510 else
1511 byte <<= 1;
1512
1513 if (byte & 0x80)
1514 {
1515 // X11 doesn't support inverted pixels (XOR ops,
1516 // to be exact) in cursor shapes, so we detect such
1517 // pixels and always replace them with black ones to
1518 // make them visible at least over light colors
1519 if (dstShapePtr [x] & 0x00FFFFFF)
1520 dstShapePtr [x] = 0xFF000000;
1521 else
1522 dstShapePtr [x] = 0x00000000;
1523 }
1524 else
1525 dstShapePtr [x] |= 0xFF000000;
1526 }
1527 }
1528
1529 srcShapePtr += srcShapePtrScan;
1530 dstShapePtr += data->width;
1531 }
1532
1533 Cursor cur = XcursorImageLoadCursor (gSdlInfo.info.x11.display, img);
1534 Assert (cur);
1535 if (cur)
1536 {
1537 // here we do a dirty trick by substituting a Window Manager's
1538 // cursor handle with the handle we created
1539
1540 WMcursor *old_wm_cursor = gpCustomCursor->wm_cursor;
1541
1542 // see SDL12/src/video/x11/SDL_x11mouse.c
1543 void *wm_cursor = malloc (sizeof (Cursor));
1544 *(Cursor *) wm_cursor = cur;
1545
1546 gpCustomCursor->wm_cursor = (WMcursor *) wm_cursor;
1547 SDL_SetCursor (gpCustomCursor);
1548 SDL_ShowCursor (SDL_ENABLE);
1549
1550 if (old_wm_cursor)
1551 {
1552 XFreeCursor (gSdlInfo.info.x11.display, *(Cursor *) old_wm_cursor);
1553 free (old_wm_cursor);
1554 }
1555
1556 ok = true;
1557 }
1558
1559 XcursorImageDestroy (img);
1560 }
1561
1562#endif /* VBOXBFE_WITH_X11 */
1563
1564 if (!ok)
1565 {
1566 SDL_SetCursor (gpDefaultCursor);
1567 SDL_ShowCursor (SDL_ENABLE);
1568 }
1569 }
1570 else
1571 {
1572 if (data->visible)
1573 {
1574 SDL_ShowCursor (SDL_ENABLE);
1575 }
1576 else
1577 {
1578 SDL_ShowCursor (SDL_DISABLE);
1579 }
1580 }
1581}
1582
1583void SDLConsole::resetCursor(void)
1584{
1585 SDL_SetCursor (gpDefaultCursor);
1586 SDL_ShowCursor (SDL_ENABLE);
1587}
1588
1589/**
1590 * Handles a host key down event
1591 */
1592int SDLConsole::handleHostKey(const SDL_KeyboardEvent *pEv)
1593{
1594 /*
1595 * Revalidate the host key modifier
1596 */
1597 if ((SDL_GetModState() & ~(KMOD_MODE | KMOD_NUM | KMOD_RESERVED)) != gHostKey)
1598 return VERR_NOT_SUPPORTED;
1599
1600 /*
1601 * What was pressed?
1602 */
1603 switch (pEv->keysym.sym)
1604 {
1605 /* Control-Alt-Delete */
1606 case SDLK_DELETE:
1607 {
1608 gKeyboard->PutCAD();
1609 break;
1610 }
1611
1612 /*
1613 * Fullscreen / Windowed toggle.
1614 */
1615 case SDLK_f:
1616 {
1617 VMCtrlToggleFullscreen();
1618 break;
1619 }
1620
1621 /*
1622 * Pause / Resume toggle.
1623 */
1624 case SDLK_p:
1625 {
1626 if (machineState == VMSTATE_RUNNING)
1627 VMCtrlPause();
1628 else
1629 VMCtrlResume();
1630 updateTitlebar();
1631 break;
1632 }
1633
1634 /*
1635 * Reset the VM
1636 */
1637 case SDLK_r:
1638 {
1639 VMCtrlReset();
1640 break;
1641 }
1642
1643 /*
1644 * Terminate the VM
1645 */
1646 case SDLK_q:
1647 {
1648 return VINF_EM_TERMINATE;
1649 break;
1650 }
1651
1652 /*
1653 * Send ACPI power button press event
1654 */
1655 case SDLK_h:
1656 {
1657 VMCtrlACPIPowerButton();
1658 break;
1659 }
1660
1661 /*
1662 * Save the machine's state and exit
1663 */
1664 case SDLK_s:
1665 {
1666 VMCtrlSave(doEventQuit);
1667 break;
1668 }
1669
1670 /*
1671 * Not a host key combination.
1672 * Indicate this by returning false.
1673 */
1674 default:
1675 return VERR_NOT_SUPPORTED;
1676 }
1677
1678 return VINF_SUCCESS;
1679}
1680
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use