VirtualBox

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

Last change on this file since 5204 was 4861, checked in by vboxsync, 17 years ago

Solaris is using X11. But it's currently missing Xcursor, so disable that until we've time to build it ourselves.

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