VirtualBox

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

Last change on this file since 28587 was 26981, checked in by vboxsync, 15 years ago

FE/BFE: attempt to fix a Solaris burn

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette