VirtualBox

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

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

The X11 key translation doesn't work on solaris, using the fallback method for now.

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