vmware-svga/examples/screen-multimon/main.c
Micah Dowty 68478eab4b There are many new tests and examples that we wrote internally at VMware,
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.
2009-10-21 20:20:49 +00:00

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;
}