68478eab4b
but couldn't release immediately since they depended on virtual GPU features which were not yet publicly released in any products. This checkin moves those features from our internal repository to the open source repository. Future development on these tests and examples will take place directly in the open source repository. The primary feature added by this patch is 'Screen Object', a new dynamic display management extension supported by Workstation 7.0 and Fusion 3.0. See the README for a quick explanation.
471 lines
11 KiB
C
471 lines
11 KiB
C
/*
|
|
* This is an example of dynamic multi-monitor support in the VMware
|
|
* SVGA device, using the Screen Object extension. This demo lets you
|
|
* interactively modify, create, and destroy screens.
|
|
*/
|
|
|
|
#include "svga.h"
|
|
#include "gmr.h"
|
|
#include "screen.h"
|
|
#include "intr.h"
|
|
#include "screendraw.h"
|
|
#include "math.h"
|
|
#include "keyboard.h"
|
|
#include "vmbackdoor.h"
|
|
#include "timer.h"
|
|
|
|
SVGAScreenObject screens[9];
|
|
int currentScreen;
|
|
int screenContainingCursor;
|
|
volatile uint32 keyBuffer;
|
|
int movementAmount = 10;
|
|
SVGASignedPoint screenCursorPos;
|
|
SVGASignedRect boundingRect;
|
|
|
|
#define POLL_RATE_HZ 60
|
|
#define CURSOR_WIDTH 35
|
|
#define CURSOR_HEIGHT 40
|
|
|
|
|
|
/*
|
|
* updateBoundingRect --
|
|
*
|
|
* Recalculate the bounding rectangle of all rooted screens and
|
|
* store the result in boundingRect.
|
|
*/
|
|
|
|
void
|
|
updateBoundingRect(void)
|
|
{
|
|
uint32 id;
|
|
|
|
boundingRect.left = boundingRect.top = 0x7fffffff;
|
|
boundingRect.right = boundingRect.bottom = 0x80000000;
|
|
|
|
for (id = 0; id < arraysize(screens); id++) {
|
|
SVGAScreenObject *screen = &screens[id];
|
|
|
|
if (screen->id == SVGA_ID_INVALID ||
|
|
!(screen->flags & SVGA_SCREEN_HAS_ROOT)) {
|
|
continue;
|
|
}
|
|
|
|
boundingRect.left = MIN(boundingRect.left, screen->root.x);
|
|
boundingRect.top = MIN(boundingRect.top, screen->root.y);
|
|
boundingRect.right = MAX(boundingRect.right,
|
|
(int32)(screen->root.x + screen->size.width));
|
|
boundingRect.bottom = MAX(boundingRect.bottom,
|
|
(int32)(screen->root.y + screen->size.height));
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* defineAlphaArrow --
|
|
*
|
|
* Defines an alpha cursor (a pointing arrow).
|
|
*/
|
|
|
|
void
|
|
defineAlphaArrow(void)
|
|
{
|
|
static const SVGAFifoCmdDefineAlphaCursor cursor = {
|
|
.id = 0,
|
|
.hotspotX = 1,
|
|
.hotspotY = 1,
|
|
.width = CURSOR_WIDTH,
|
|
.height = CURSOR_HEIGHT,
|
|
};
|
|
|
|
static const uint32 data[] = {
|
|
# include "rgba_arrow.h"
|
|
};
|
|
|
|
void *fifoData;
|
|
|
|
SVGA_BeginDefineAlphaCursor(&cursor, &fifoData);
|
|
memcpy(fifoData, data, sizeof data);
|
|
SVGA_FIFOCommitAll();
|
|
}
|
|
|
|
|
|
/*
|
|
* drawScreenBorder --
|
|
*
|
|
* Redraw the border of a screen, indicating whether or not it's current.
|
|
*/
|
|
|
|
void
|
|
drawScreenBorder(const SVGAScreenObject *screen)
|
|
{
|
|
const int t = 4; // Thickness
|
|
const uint32 color = currentScreen == screen->id ? 0xffffdd : 0x555555;
|
|
const int w = screen->size.width;
|
|
const int h = screen->size.height;
|
|
|
|
if (screen->id != SVGA_ID_INVALID) {
|
|
ScreenDraw_SetScreen(screen->id, screen->size.width, screen->size.height);
|
|
ScreenDraw_Border(0, 0, w, h, color, t);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* drawScreenText --
|
|
*
|
|
* Draw informational text on a screen.
|
|
*/
|
|
|
|
void
|
|
drawScreenText(const SVGAScreenObject *screen)
|
|
{
|
|
ScreenDraw_SetScreen(screen->id, screen->size.width, screen->size.height);
|
|
Console_MoveTo(10, 10);
|
|
Console_Format("Screen #%d\n"
|
|
"%dx%d at (%d,%d) \n",
|
|
screen->id, screen->size.width, screen->size.height,
|
|
screen->root.x, screen->root.y);
|
|
|
|
if (screen->id == screenContainingCursor) {
|
|
Console_Format("Cursor: (%d,%d) \n",
|
|
screenCursorPos.x, screenCursorPos.y);
|
|
} else {
|
|
Console_Format(" \n");
|
|
}
|
|
|
|
Console_Format("\n"
|
|
"1-%d or mouse click selects screen.\n"
|
|
"Arrow keys move screen.\n"
|
|
"'wasd' adjusts size.\n"
|
|
"'WASD' adjusts size without repaint.\n"
|
|
"Space bar toggles create/destroy.\n"
|
|
"\n"
|
|
"Moving %d pixels at a time. \n"
|
|
"(Adjust with [ ] keys.)\n",
|
|
arraysize(screens),
|
|
movementAmount);
|
|
}
|
|
|
|
|
|
/*
|
|
* paintScreen --
|
|
*
|
|
* Draw informational text, a background, and a border to each
|
|
* screen.
|
|
*/
|
|
|
|
void
|
|
paintScreen(const SVGAScreenObject *screen)
|
|
{
|
|
ScreenDraw_SetScreen(screen->id, screen->size.width, screen->size.height);
|
|
Console_Clear();
|
|
drawScreenText(screen);
|
|
drawScreenBorder(screen);
|
|
}
|
|
|
|
|
|
/*
|
|
* setCurrentScreen --
|
|
*
|
|
* Switch to a new 'current' screen, and indicate it visually by
|
|
* repainting the border. If the new screen ID is bad, do nothing.
|
|
*/
|
|
|
|
void
|
|
setCurrentScreen(int nextScreen)
|
|
{
|
|
if (nextScreen >= 0 && nextScreen < arraysize(screens)) {
|
|
int prevScreen = currentScreen;
|
|
currentScreen = nextScreen;
|
|
|
|
drawScreenBorder(&screens[prevScreen]);
|
|
drawScreenBorder(&screens[nextScreen]);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* toggleScreenExistence --
|
|
*
|
|
* Define or undefine the current screen. When we first define it,
|
|
* paint its contents.
|
|
*/
|
|
|
|
void
|
|
toggleScreenExistence(void)
|
|
{
|
|
SVGAScreenObject *screen = &screens[currentScreen];
|
|
|
|
if (screen->id == SVGA_ID_INVALID) {
|
|
screen->id = currentScreen;
|
|
Screen_Define(screen);
|
|
paintScreen(screen);
|
|
} else {
|
|
Screen_Destroy(screen->id);
|
|
screen->id = SVGA_ID_INVALID;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* kbIRQ --
|
|
*
|
|
* Keyboard IRQ handler. This runs with IRQs disabled, so we need
|
|
* to thunk this key back to the main loop before we do any SVGA
|
|
* drawing as a result.
|
|
*/
|
|
|
|
fastcall void
|
|
kbIRQ(KeyEvent *event)
|
|
{
|
|
if (event->key && event->pressed) {
|
|
keyBuffer = event->key;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* kbHandler --
|
|
*
|
|
* Keyboard event handler, running in the main loop.
|
|
*/
|
|
|
|
fastcall void
|
|
kbHandler(uint32 key)
|
|
{
|
|
/* Digits select a screen */
|
|
setCurrentScreen(key - '1');
|
|
|
|
SVGAScreenObject *screen = &screens[currentScreen];
|
|
|
|
if (key == ' ') {
|
|
toggleScreenExistence();
|
|
updateBoundingRect();
|
|
return;
|
|
}
|
|
|
|
if (screen->id == SVGA_ID_INVALID) {
|
|
return;
|
|
}
|
|
|
|
switch (key) {
|
|
|
|
case '[':
|
|
movementAmount = MAX(1, movementAmount - 1);
|
|
drawScreenText(screen);
|
|
break;
|
|
|
|
case ']':
|
|
movementAmount++;
|
|
drawScreenText(screen);
|
|
break;
|
|
|
|
case KEY_LEFT:
|
|
screen->root.x -= movementAmount;
|
|
drawScreenText(screen);
|
|
Screen_Define(screen);
|
|
break;
|
|
|
|
case KEY_RIGHT:
|
|
screen->root.x += movementAmount;
|
|
drawScreenText(screen);
|
|
Screen_Define(screen);
|
|
break;
|
|
|
|
case KEY_UP:
|
|
screen->root.y -= movementAmount;
|
|
drawScreenText(screen);
|
|
Screen_Define(screen);
|
|
break;
|
|
|
|
case KEY_DOWN:
|
|
screen->root.y += movementAmount;
|
|
drawScreenText(screen);
|
|
Screen_Define(screen);
|
|
break;
|
|
|
|
case 'A':
|
|
screen->size.width -= movementAmount;
|
|
drawScreenText(screen);
|
|
Screen_Define(screen);
|
|
break;
|
|
|
|
case 'D':
|
|
screen->size.width += movementAmount;
|
|
drawScreenText(screen);
|
|
Screen_Define(screen);
|
|
break;
|
|
|
|
case 'W':
|
|
screen->size.height -= movementAmount;
|
|
drawScreenText(screen);
|
|
Screen_Define(screen);
|
|
break;
|
|
|
|
case 'S':
|
|
screen->size.height += movementAmount;
|
|
drawScreenText(screen);
|
|
Screen_Define(screen);
|
|
break;
|
|
|
|
case 'a':
|
|
screen->size.width -= movementAmount;
|
|
Screen_Define(screen);
|
|
paintScreen(screen);
|
|
break;
|
|
|
|
case 'd':
|
|
screen->size.width += movementAmount;
|
|
Screen_Define(screen);
|
|
paintScreen(screen);
|
|
break;
|
|
|
|
case 'w':
|
|
screen->size.height -= movementAmount;
|
|
Screen_Define(screen);
|
|
paintScreen(screen);
|
|
break;
|
|
|
|
case 's':
|
|
screen->size.height += movementAmount;
|
|
Screen_Define(screen);
|
|
paintScreen(screen);
|
|
break;
|
|
}
|
|
|
|
updateBoundingRect();
|
|
}
|
|
|
|
|
|
/*
|
|
* main --
|
|
*
|
|
* Initialization and main loop.
|
|
*/
|
|
|
|
int
|
|
main(void)
|
|
{
|
|
uint32 id;
|
|
|
|
Intr_Init();
|
|
Intr_SetFaultHandlers(SVGA_DefaultFaultHandler);
|
|
|
|
/*
|
|
* TODO: We currently poll the mouse on a timer. We could instead
|
|
* make this interrupt-driven, but that might need changes to the
|
|
* PS/2 controller.
|
|
*/
|
|
Timer_InitPIT(PIT_HZ / POLL_RATE_HZ);
|
|
Intr_SetMask(PIT_IRQ, TRUE);
|
|
Keyboard_Init();
|
|
Keyboard_SetHandler(kbIRQ);
|
|
VMBackdoor_MouseInit(TRUE);
|
|
SVGA_Init();
|
|
GMR_Init();
|
|
Heap_Reset();
|
|
SVGA_SetMode(0, 0, 32);
|
|
Screen_Init();
|
|
ScreenDraw_Init(0);
|
|
|
|
|
|
/*
|
|
* Initialize default parameters for each screen. None of these
|
|
* exist yet, and we indicate that by setting their ID to
|
|
* SVGA_ID_INVALID.
|
|
*/
|
|
|
|
for (id = 0; id < arraysize(screens); id++) {
|
|
SVGAScreenObject *screen = &screens[id];
|
|
|
|
screen->structSize = sizeof *screen;
|
|
screen->id = SVGA_ID_INVALID;
|
|
screen->flags = SVGA_SCREEN_HAS_ROOT;
|
|
screen->size.width = 320;
|
|
screen->size.height = 240;
|
|
screen->root.x = 320 * id;
|
|
screen->root.y = 0;
|
|
}
|
|
|
|
/*
|
|
* Make the first screen primary, to work around bug 399528 in the
|
|
* Workstation UI.
|
|
*/
|
|
screens[0].flags |= SVGA_SCREEN_IS_PRIMARY;
|
|
|
|
/*
|
|
* Make a screen visible, so the user can see our instructions.
|
|
*/
|
|
toggleScreenExistence();
|
|
|
|
updateBoundingRect();
|
|
|
|
defineAlphaArrow();
|
|
|
|
/*
|
|
* Main loop: Wait for keyboard interrupts, and handle keys by
|
|
* pulling them out of eventBuffer.
|
|
*/
|
|
|
|
while (1) {
|
|
uint32 key = KEY_NONE;
|
|
Bool needCursorUpdate = FALSE;
|
|
static VMMousePacket mouseState;
|
|
|
|
Intr_Halt();
|
|
Atomic_Exchange(keyBuffer, key);
|
|
if (key != KEY_NONE) {
|
|
kbHandler(key);
|
|
}
|
|
while (VMBackdoor_MouseGetPacket(&mouseState)) {
|
|
needCursorUpdate = TRUE;
|
|
}
|
|
|
|
if (needCursorUpdate) {
|
|
SVGASignedPoint virtualCursorPos;
|
|
Bool cursorOnScreen = FALSE;
|
|
|
|
virtualCursorPos.x = boundingRect.left +
|
|
(mouseState.x * (boundingRect.right - boundingRect.left)) / 65535;
|
|
virtualCursorPos.y = boundingRect.top +
|
|
(mouseState.y * (boundingRect.bottom - boundingRect.top)) / 65535;
|
|
|
|
for (id = 0; id < arraysize(screens); id++) {
|
|
SVGAScreenObject *screen = &screens[id];
|
|
if (screen->id == SVGA_ID_INVALID ||
|
|
!(screen->flags & SVGA_SCREEN_HAS_ROOT)) {
|
|
continue;
|
|
}
|
|
|
|
if (screen->root.x <= virtualCursorPos.x &&
|
|
screen->root.x + screen->size.width > virtualCursorPos.x &&
|
|
screen->root.y <= virtualCursorPos.y &&
|
|
screen->root.y + screen->size.height > virtualCursorPos.y) {
|
|
|
|
cursorOnScreen = TRUE;
|
|
screenCursorPos.x = virtualCursorPos.x - screen->root.x;
|
|
screenCursorPos.y = virtualCursorPos.y - screen->root.y;
|
|
|
|
if (mouseState.buttons & VMMOUSE_LEFT_BUTTON) {
|
|
setCurrentScreen(screen->id);
|
|
}
|
|
|
|
if (screenContainingCursor != screen->id) {
|
|
int oldScreenContainingCursor = screenContainingCursor;
|
|
screenContainingCursor = screen->id;
|
|
drawScreenText(&screens[oldScreenContainingCursor]);
|
|
}
|
|
drawScreenText(screen);
|
|
}
|
|
}
|
|
|
|
SVGA_MoveCursor(cursorOnScreen,
|
|
screenCursorPos.x,
|
|
screenCursorPos.y,
|
|
screenContainingCursor);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|