VirtualBox

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

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

The Big Sun Rebranding Header Change

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