xserver-multidpi/hw/dmx/input/dmxconsole.c
Eamon Walsh 4017d31902 devPrivates rework: since API is already broken, switch everything
over to new system.

Need to update documentation and address some remaining vestiges of
old system such as CursorRec structure, fb "offman" structure, and
FontRec privates.
2007-08-28 09:28:25 -04:00

1032 lines
38 KiB
C

/*
* Copyright 2001-2003 Red Hat Inc., Durham, North Carolina.
*
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation on the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/*
* Authors:
* David H. Dawes <dawes@xfree86.org>
* Kevin E. Martin <kem@redhat.com>
* Rickard E. (Rik) Faith <faith@redhat.com>
*
*/
/** \file
*
* This file implements the console input devices.
*/
#ifdef HAVE_DMX_CONFIG_H
#include <dmx-config.h>
#endif
#define DMX_CONSOLE_DEBUG 0
#define DMX_WINDOW_DEBUG 0
#include "dmxinputinit.h"
#include "dmxevents.h"
#include "dmxconsole.h"
#include "dmxcommon.h"
#include "dmxscrinit.h"
#include "dmxcb.h"
#include "dmxsync.h"
#include "inputstr.h"
#include "input.h"
#include "mipointer.h"
#include "windowstr.h"
#define CONSOLE_NUM 3
#define CONSOLE_DEN 4
#define DMX_CONSOLE_NAME "DMX Console"
#define DMX_RES_NAME "Xdmx"
#define DMX_RES_CLASS "XDmx"
#define CONSOLE_BG_COLOR "gray75"
#define CONSOLE_FG_COLOR "black"
#define CONSOLE_SCREEN_BG_COLOR "white"
#define CONSOLE_SCREEN_FG_COLOR "black"
#define CONSOLE_SCREEN_DET_COLOR "gray75"
#define CONSOLE_SCREEN_CUR_COLOR "red"
#if DMX_CONSOLE_DEBUG
#define DMXDBG0(f) dmxLog(dmxDebug,f)
#define DMXDBG1(f,a) dmxLog(dmxDebug,f,a)
#define DMXDBG2(f,a,b) dmxLog(dmxDebug,f,a,b)
#define DMXDBG3(f,a,b,c) dmxLog(dmxDebug,f,a,b,c)
#define DMXDBG4(f,a,b,c,d) dmxLog(dmxDebug,f,a,b,c,d)
#define DMXDBG5(f,a,b,c,d,e) dmxLog(dmxDebug,f,a,b,c,d,e)
#define DMXDBG6(f,a,b,c,d,e,g) dmxLog(dmxDebug,f,a,b,c,d,e,g)
#define DMXDBG7(f,a,b,c,d,e,g,h) dmxLog(dmxDebug,f,a,b,c,d,e,g,h)
#else
#define DMXDBG0(f)
#define DMXDBG1(f,a)
#define DMXDBG2(f,a,b)
#define DMXDBG3(f,a,b,c)
#define DMXDBG4(f,a,b,c,d)
#define DMXDBG5(f,a,b,c,d,e)
#define DMXDBG6(f,a,b,c,d,e,g)
#define DMXDBG7(f,a,b,c,d,e,g,h)
#endif
/* Private area for consoles. */
typedef struct _myPrivate {
DMX_COMMON_PRIVATE;
int lastX;
int lastY;
int globalX;
int globalY;
int curX;
int curY;
int width;
int height;
int consWidth;
int consHeight;
double xScale;
double yScale;
XlibGC gc, gcDet, gcRev, gcCur;
int grabbed, fine, captured;
Cursor cursorNormal, cursorGrabbed, cursorEmpty;
Pixmap pixmap;
CloseScreenProcPtr CloseScreen;
struct _myPrivate *next; /* for closing multiple consoles */
int initialized;
DevicePtr mou, kbd;
} myPrivate;
static int scalex(myPrivate *priv, int x)
{
return (int)((x * priv->xScale) + .5);
}
static int scaley(myPrivate *priv, int y)
{
return (int)((y * priv->yScale) + .5);
}
static int unscalex(myPrivate *priv, int x)
{
return (int)((x / priv->xScale) + .5);
}
static int unscaley(myPrivate *priv, int y)
{
return (int)((y / priv->yScale) + .5);
}
/** Create the private area for \a pDevice. */
pointer dmxConsoleCreatePrivate(DeviceIntPtr pDevice)
{
GETDMXLOCALFROMPDEVICE;
myPrivate *priv = xalloc(sizeof(*priv));
memset(priv, 0, sizeof(*priv));
priv->dmxLocal = dmxLocal;
return priv;
}
/** If \a private is non-NULL, free its associated memory. */
void dmxConsoleDestroyPrivate(pointer private)
{
if (private) xfree(private);
}
static void dmxConsoleDrawFineCursor(myPrivate *priv, XRectangle *rect)
{
int size = 6;
int x, y;
XDrawLine(priv->display, priv->pixmap, priv->gcCur,
x = scalex(priv, priv->globalX) - size,
scaley(priv, priv->globalY),
scalex(priv, priv->globalX) + size,
scaley(priv, priv->globalY));
XDrawLine(priv->display, priv->pixmap, priv->gcCur,
scalex(priv, priv->globalX),
y = scaley(priv, priv->globalY) - size,
scalex(priv, priv->globalX),
scaley(priv, priv->globalY) + size);
if (priv->grabbed) {
XDrawLine(priv->display, priv->pixmap, priv->gcCur,
scalex(priv, priv->globalX) - (int)(size / 1.4),
scaley(priv, priv->globalY) - (int)(size / 1.4),
scalex(priv, priv->globalX) + (int)(size / 1.4),
scaley(priv, priv->globalY) + (int)(size / 1.4));
XDrawLine(priv->display, priv->pixmap, priv->gcCur,
scalex(priv, priv->globalX) - (int)(size / 1.4),
scaley(priv, priv->globalY) + (int)(size / 1.4),
scalex(priv, priv->globalX) + (int)(size / 1.4),
scaley(priv, priv->globalY) - (int)(size / 1.4));
}
if (rect) {
rect->x = x;
rect->y = y;
rect->width = 2 * size;
rect->height = 2 * size;
}
}
static void dmxConsoleDrawWindows(pointer private)
{
GETONLYPRIVFROMPRIVATE;
Display *dpy = priv->display;
int i;
Region whole, used, avail;
XRectangle rect;
whole = XCreateRegion();
used = XCreateRegion();
avail = XCreateRegion();
rect.x = 0;
rect.y = 0;
rect.width = priv->consWidth;
rect.height = priv->consHeight;
XUnionRectWithRegion(&rect, whole, whole);
for (i = 0; i < dmxNumScreens; i++) {
WindowPtr pRoot = WindowTable[i];
WindowPtr pChild;
#if DMX_WINDOW_DEBUG
dmxLog(dmxDebug, "%lu %p %p %p 2\n",
pRoot->drawable.id,
pRoot->parent, pRoot->firstChild, pRoot->lastChild);
#endif
for (pChild = pRoot->firstChild; pChild; pChild = pChild->nextSib) {
if (pChild->mapped
&& pChild->realized) {
#if DMX_WINDOW_DEBUG
dmxLog(dmxDebug, " %p %d,%d %dx%d %d %d %d RECTS\n",
pChild,
pChild->drawable.x,
pChild->drawable.y,
pChild->drawable.width,
pChild->drawable.height,
pChild->visibility,
pChild->overrideRedirect,
REGION_NUM_RECTS(&pChild->clipList));
#endif
rect.x = scalex(priv, pChild->drawable.x
+ dixScreenOrigins[i].x);
rect.y = scaley(priv, pChild->drawable.y
+ dixScreenOrigins[i].y);
rect.width = scalex(priv, pChild->drawable.width);
rect.height = scaley(priv, pChild->drawable.height);
XDrawRectangle(dpy, priv->pixmap, priv->gc,
rect.x, rect.y, rect.width, rect.height);
XUnionRectWithRegion(&rect, used, used);
XSubtractRegion(whole, used, avail);
XSetRegion(dpy, priv->gc, avail);
}
}
#ifdef PANORAMIX
if (!noPanoramiXExtension) break; /* Screen 0 valid with Xinerama */
#endif
}
XDestroyRegion(avail);
XDestroyRegion(used);
XDestroyRegion(whole);
XSetClipMask(dpy, priv->gc, None);
}
static void dmxConsoleDraw(myPrivate *priv, int updateCursor, int update)
{
GETDMXINPUTFROMPRIV;
Display *dpy = priv->display;
int i;
XFillRectangle(dpy, priv->pixmap, priv->gc, 0, 0,
priv->consWidth, priv->consHeight);
for (i = 0; i < dmxNumScreens; i++) {
DMXScreenInfo *dmxScreen = &dmxScreens[i];
XFillRectangle(dpy, priv->pixmap,
dmxScreen->beDisplay ? priv->gcRev : priv->gcDet,
scalex(priv, dixScreenOrigins[i].x),
scaley(priv, dixScreenOrigins[i].y),
scalex(priv, screenInfo.screens[i]->width),
scaley(priv, screenInfo.screens[i]->height));
}
for (i = 0; i < dmxNumScreens; i++) {
XDrawRectangle(dpy, priv->pixmap, priv->gc,
scalex(priv, dixScreenOrigins[i].x),
scaley(priv, dixScreenOrigins[i].y),
scalex(priv, screenInfo.screens[i]->width),
scaley(priv, screenInfo.screens[i]->height));
}
if (dmxInput->windows) dmxConsoleDrawWindows(priv);
if (priv->fine && updateCursor) dmxConsoleDrawFineCursor(priv, 0);
if (update) {
XCopyArea(priv->display, priv->pixmap, priv->window, priv->gc,
0, 0, priv->consWidth, priv->consHeight, 0, 0);
XSync(priv->display, False); /* Not a backend display */
}
}
static void dmxConsoleClearCursor(myPrivate *priv, int x, int y,
XRectangle *rect)
{
int cw = 14, ch = 14; /* Clear width and height */
rect->x = scalex(priv, x) - cw/2;
rect->y = scaley(priv, y) - ch/2;
rect->width = cw;
rect->height = ch;
XSetClipRectangles(priv->display, priv->gc, 0, 0, rect, 1, Unsorted);
XSetClipRectangles(priv->display, priv->gcDet, 0, 0, rect, 1, Unsorted);
XSetClipRectangles(priv->display, priv->gcRev, 0, 0, rect, 1, Unsorted);
dmxConsoleDraw(priv, 0, 0);
XSetClipMask(priv->display, priv->gc, None);
XSetClipMask(priv->display, priv->gcDet, None);
XSetClipMask(priv->display, priv->gcRev, None);
}
static void dmxConsoleUpdateFineCursor(myPrivate *priv)
{
int leave = 0;
XRectangle rects[2];
dmxConsoleClearCursor(priv, priv->globalX, priv->globalY, &rects[0]);
if (priv->dmxLocal->sendsCore) {
dmxGetGlobalPosition(&priv->globalX, &priv->globalY);
} else {
priv->globalX = priv->dmxLocal->lastX;
priv->globalY = priv->dmxLocal->lastY;
}
priv->lastX = scalex(priv, priv->width / 2);
priv->lastY = scaley(priv, priv->height / 2);
/* Compute new warp position, which may be
outside the window */
if (priv->globalX < 1 || priv->globalX >= priv->width) {
if (priv->globalX < 1) priv->lastX = 0;
else priv->lastX = scalex(priv, priv->width);
priv->lastY = scaley(priv, priv->globalY);
++leave;
}
if (priv->globalY < 1 || priv->globalY >= priv->height) {
if (priv->globalY < 1) priv->lastY = 0;
else priv->lastY = scaley(priv, priv->height);
priv->lastX = scalex(priv, priv->globalX);
++leave;
}
/* Draw pseudo cursor in window */
dmxConsoleDrawFineCursor(priv, &rects[1]);
XSetClipRectangles(priv->display, priv->gc, 0, 0, rects, 2, Unsorted);
XCopyArea(priv->display, priv->pixmap, priv->window, priv->gc,
0, 0, priv->consWidth, priv->consHeight, 0, 0);
XSetClipMask(priv->display, priv->gc, None);
DMXDBG2("dmxConsoleUpdateFineCursor: WARP %d %d\n",
priv->lastX, priv->lastY);
XWarpPointer(priv->display, priv->window, priv->window,
0, 0, 0, 0, priv->lastX, priv->lastY);
XSync(priv->display, False); /* Not a backend display */
if (leave) {
XEvent X;
while (XCheckMaskEvent(priv->display, PointerMotionMask, &X)) {
if (X.type == MotionNotify) {
if (X.xmotion.x != priv->lastX || X.xmotion.y != priv->lastY) {
DMXDBG4("Ignoring motion to %d %d after leave frm %d %d\n",
X.xmotion.x, X.xmotion.y,
priv->lastX, priv->lastY);
}
} else {
dmxLog(dmxInfo, "Ignoring event (%d): %s ****************\n",
X.type, dmxEventName(X.type));
}
}
}
DMXDBG6("dmxConsoleUpdateFineCursor: Warp %d %d on %d %d [%d %d]\n",
priv->lastX, priv->lastY,
scalex(priv, priv->width),
scaley(priv, priv->height),
priv->globalX, priv->globalY);
}
/** Whenever the window layout (size, position, stacking order) might be
* changed, this routine is called with the \a pWindow that changed and
* the \a type of change. This routine is called in a conservative
* fashion: the actual layout of the windows of the screen might not
* have had any human-visible changes. */
void dmxConsoleUpdateInfo(pointer private, DMXUpdateType type,
WindowPtr pWindow)
{
GETONLYPRIVFROMPRIVATE;
dmxConsoleDraw(priv, 1, 1);
}
static void dmxConsoleMoveAbsolute(myPrivate *priv, int x, int y,
DevicePtr pDev, dmxMotionProcPtr motion,
DMXBlockType block)
{
int tmpX, tmpY, v[2];
tmpX = unscalex(priv, x);
tmpY = unscalex(priv, y);
DMXDBG6("dmxConsoleMoveAbsolute(,%d,%d) %d %d =? %d %d\n",
x, y, tmpX, tmpY, priv->curX, priv->curY);
if (tmpX == priv->curX && tmpY == priv->curY) return;
v[0] = unscalex(priv, x);
v[1] = unscaley(priv, y);
motion(pDev, v, 0, 2, DMX_ABSOLUTE_CONFINED, block);
/* dmxConsoleUpdatePosition gets called here by dmxCoreMotion */
}
static void dmxConsoleMoveRelative(myPrivate *priv, int x, int y,
DevicePtr pDev, dmxMotionProcPtr motion,
DMXBlockType block)
{
int v[2];
/* Ignore the event generated from * warping back to middle */
if (x == priv->lastX && y == priv->lastY) return;
v[0] = priv->lastX - x;
v[1] = priv->lastY - y;
motion(pDev, v, 0, 2, DMX_RELATIVE, block);
/* dmxConsoleUpdatePosition gets called here by dmxCoreMotion */
}
/** This routine gets called from #dmxCoreMotion for each motion. This
* allows the console's notion of the cursor postion to change when
* another input device actually caused the change. */
void dmxConsoleUpdatePosition(pointer private, int x, int y)
{
GETONLYPRIVFROMPRIVATE;
int tmpX, tmpY;
Display *dpy = priv->display;
static unsigned long dmxGeneration = 0;
tmpX = scalex(priv, x);
tmpY = scaley(priv, y);
DMXDBG6("dmxConsoleUpdatePosition(,%d,%d) new=%d,%d dims=%d,%d\n",
x, y, tmpX, tmpY, priv->consWidth, priv->consHeight);
if (priv->fine) dmxConsoleUpdateFineCursor(priv);
if (tmpX != priv->curX || tmpY != priv->curY) {
if (tmpX < 0) tmpX = 0;
if (tmpY < 0) tmpY = 0;
if (tmpX >= priv->consWidth) tmpX = priv->consWidth - 1;
if (tmpY >= priv->consHeight) tmpY = priv->consHeight - 1;
priv->curX = tmpX;
priv->curY = tmpY;
if (!priv->fine) {
DMXDBG2(" WARP B %d %d\n", priv->curX, priv->curY);
XWarpPointer(dpy, priv->window,
priv->window, 0, 0, 0, 0, tmpX, tmpY);
XSync(dpy, False); /* Not a backend display */
}
}
if (dmxGeneration != serverGeneration) {
dmxGeneration = serverGeneration;
dmxConsoleDraw(priv, 1, 1);
}
}
/** Collect all pending events from the console's display. Plase these
* events on the server event queue using the \a motion and \a enqueue
* routines. The \a checkspecial routine is used to check for special
* keys that need handling. \a block tells if signals should be blocked
* when updating the event queue. */
void dmxConsoleCollectEvents(DevicePtr pDev,
dmxMotionProcPtr motion,
dmxEnqueueProcPtr enqueue,
dmxCheckSpecialProcPtr checkspecial,
DMXBlockType block)
{
GETPRIVFROMPDEV;
GETDMXINPUTFROMPRIV;
Display *dpy = priv->display;
Window win = priv->window;
int width = priv->width;
int height = priv->height;
XEvent X, N;
XSetWindowAttributes attribs;
static int rInitialized = 0;
static Region r;
XRectangle rect;
static int raising = 0, raiseX, raiseY; /* FIXME */
while (XPending(dpy)) {
XNextEvent(dpy, &X);
switch(X.type) {
case VisibilityNotify:
break;
case Expose:
DMXDBG5("dmxConsoleCollectEvents: Expose #%d %d %d %d %d\n",
X.xexpose.count,
X.xexpose.x, X.xexpose.y,
X.xexpose.width, X.xexpose.height);
if (!rInitialized++) r = XCreateRegion();
rect.x = X.xexpose.x;
rect.y = X.xexpose.y;
rect.width = X.xexpose.width;
rect.height = X.xexpose.height;
XUnionRectWithRegion(&rect, r, r);
if (X.xexpose.count == 0) {
XSetRegion(dpy, priv->gc, r);
XSetRegion(dpy, priv->gcDet, r);
XSetRegion(dpy, priv->gcRev, r);
dmxConsoleDraw(priv, 1, 1);
XSetClipMask(dpy, priv->gc, None);
XSetClipMask(dpy, priv->gcDet, None);
XSetClipMask(dpy, priv->gcRev, None);
XDestroyRegion(r);
rInitialized = 0;
}
break;
case ResizeRequest:
DMXDBG2("dmxConsoleCollectEvents: Resize %d %d\n",
X.xresizerequest.width, X.xresizerequest.height);
priv->consWidth = X.xresizerequest.width;
priv->consHeight = X.xresizerequest.height;
priv->xScale = (double)priv->consWidth / width;
priv->yScale = (double)priv->consHeight / height;
attribs.override_redirect = True;
XChangeWindowAttributes(dpy, win, CWOverrideRedirect, &attribs);
XResizeWindow(dpy, win, priv->consWidth, priv->consHeight);
XFreePixmap(dpy, priv->pixmap);
priv->pixmap = XCreatePixmap(dpy,
RootWindow(dpy, DefaultScreen(dpy)),
priv->consWidth,
priv->consHeight,
DefaultDepth(dpy,DefaultScreen(dpy)));
dmxConsoleDraw(priv, 1, 1);
attribs.override_redirect = False;
XChangeWindowAttributes(dpy, win, CWOverrideRedirect, &attribs);
break;
case LeaveNotify:
DMXDBG4("dmxConsoleCollectEvents: Leave @ %d,%d; r=%d f=%d\n",
X.xcrossing.x, X.xcrossing.y, raising, priv->fine);
if (!priv->captured) dmxCommonRestoreState(priv);
else {
dmxConsoleUncapture(dmxInput);
dmxCommonRestoreState(priv);
}
break;
case EnterNotify:
DMXDBG6("dmxConsoleCollectEvents: Enter %d,%d r=%d f=%d (%d,%d)\n",
X.xcrossing.x, X.xcrossing.y, raising, priv->fine,
priv->curX, priv->curY);
dmxCommonSaveState(priv);
if (raising) {
raising = 0;
dmxConsoleMoveAbsolute(priv, raiseX, raiseY,
priv->mou, motion, block);
} else {
if (priv->fine) {
/* The raise will generate an event near the center,
* which is not where the cursor should be. So we
* save the real position, do the raise, and move
* the cursor here again after the raise generates
* the event. */
raising = 1;
raiseX = X.xcrossing.x;
raiseY = X.xcrossing.y;
XRaiseWindow(dpy, priv->window);
}
XSync(dpy, False); /* Not a backend display */
if (!X.xcrossing.x && !X.xcrossing.y)
dmxConsoleMoveAbsolute(priv, priv->curX, priv->curY,
priv->mou, motion, block);
}
break;
case MotionNotify:
if (priv->curX == X.xmotion.x && priv->curY == X.xmotion.y)
continue;
if (XPending(dpy)) { /* do motion compression */
XPeekEvent(dpy, &N);
if (N.type == MotionNotify) continue;
}
DMXDBG2("dmxConsoleCollectEvents: Motion %d %d\n",
X.xmotion.x, X.xmotion.y);
if (raising) {
raising = 0;
dmxConsoleMoveAbsolute(priv, raiseX, raiseY,
priv->mou, motion, block);
} else {
if (priv->fine)
dmxConsoleMoveRelative(priv, X.xmotion.x, X.xmotion.y,
priv->mou, motion, block);
else
dmxConsoleMoveAbsolute(priv, X.xmotion.x, X.xmotion.y,
priv->mou, motion, block);
}
break;
case KeyPress:
case KeyRelease:
enqueue(priv->kbd, X.type, X.xkey.keycode, 0, NULL, block);
break;
default:
/* Pass the whole event here, because
* this may be an extension event. */
enqueue(priv->mou, X.type, X.xbutton.button, 0, &X, block);
break;
}
}
}
static void dmxCloseConsole(myPrivate *priv)
{
GETDMXINPUTFROMPRIV;
dmxCommonRestoreState(priv);
if (priv->display) {
XFreeGC(priv->display, priv->gc);
XFreeGC(priv->display, priv->gcDet);
XFreeGC(priv->display, priv->gcRev);
XFreeGC(priv->display, priv->gcCur);
if (!dmxInput->console) XCloseDisplay(priv->display);
}
priv->display = NULL;
}
static Bool dmxCloseConsoleScreen(int idx, ScreenPtr pScreen)
{
myPrivate *priv, *last;
for (last = priv = (myPrivate *)dixLookupPrivate(&pScreen->devPrivates,
dmxScreenPrivateKey);
priv;
priv = priv->next) dmxCloseConsole(last = priv);
DMX_UNWRAP(CloseScreen, last, pScreen);
return pScreen->CloseScreen(idx, pScreen);
}
static Cursor dmxConsoleCreateEmptyCursor(myPrivate *priv)
{
char noCursorData[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
Pixmap pixmap;
Cursor cursor;
XColor color, tmpColor;
Display *dpy = priv->display;
/* Create empty cursor for window */
pixmap = XCreateBitmapFromData(priv->display, priv->window,
noCursorData, 8, 8);
if (!XAllocNamedColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)),
"black",
&color,
&tmpColor))
dmxLog(dmxFatal, "Cannot allocate color for cursor\n");
cursor = XCreatePixmapCursor(dpy, pixmap, pixmap, &color, &color, 0, 0);
XFreePixmap(dpy, pixmap);
return cursor;
}
static void dmxConsoleComputeWidthHeight(myPrivate *priv,
int *width, int *height,
double *xScale, double *yScale,
int *consWidth, int *consHeight)
{
int screen;
Display *dpy = priv->display;
*width = 0;
*height = 0;
*xScale = 1.0;
*yScale = 1.0;
screen = DefaultScreen(dpy);
*consWidth = DisplayWidth(dpy, screen) * CONSOLE_NUM / CONSOLE_DEN;
*consHeight = DisplayHeight(dpy, screen) * CONSOLE_NUM / CONSOLE_DEN;
if (*consWidth < 1) *consWidth = 1;
if (*consHeight < 1) *consHeight = 1;
#if 1
/* Always keep the console size similar
* to the global bounding box. */
*width = dmxGlobalWidth;
*height = dmxGlobalHeight;
#else
/* Make the console window as big as
* possible by computing the visible
* bounding box. */
for (i = 0; i < dmxNumScreens; i++) {
if (dixScreenOrigins[i].x+screenInfo.screens[i]->width > *width)
*width = dixScreenOrigins[i].x+screenInfo.screens[i]->width;
if (dixScreenOrigins[i].y+screenInfo.screens[i]->height > *height)
*height = dixScreenOrigins[i].y+screenInfo.screens[i]->height;
}
#endif
if ((double)*consWidth / *width < (double)*consHeight / *height)
*xScale = *yScale = (double)*consWidth / *width;
else
*xScale = *yScale = (double)*consHeight / *height;
*consWidth = scalex(priv, *width);
*consHeight = scaley(priv, *height);
if (*consWidth < 1) *consWidth = 1;
if (*consHeight < 1) *consHeight = 1;
}
/** Re-initialized the console device described by \a pDev (after a
* reconfig). */
void dmxConsoleReInit(DevicePtr pDev)
{
GETPRIVFROMPDEV;
Display *dpy;
if (!priv || !priv->initialized) return;
dpy = priv->display;
dmxConsoleComputeWidthHeight(priv,
&priv->width, &priv->height,
&priv->xScale, &priv->yScale,
&priv->consWidth, &priv->consHeight);
XResizeWindow(dpy, priv->window, priv->consWidth, priv->consHeight);
XFreePixmap(dpy, priv->pixmap);
priv->pixmap = XCreatePixmap(dpy,
RootWindow(dpy, DefaultScreen(dpy)),
priv->consWidth,
priv->consHeight,
DefaultDepth(dpy,DefaultScreen(dpy)));
dmxConsoleDraw(priv, 1, 1);
}
/** Initialized the console device described by \a pDev. */
void dmxConsoleInit(DevicePtr pDev)
{
GETPRIVFROMPDEV;
DMXInputInfo *dmxInput = &dmxInputs[dmxLocal->inputIdx];
int screen;
unsigned long mask;
XSetWindowAttributes attribs;
Display *dpy;
Window win;
XGCValues gcvals;
XColor color;
XClassHint class_hints;
unsigned long tmp;
if (dmxLocal->type == DMX_LOCAL_MOUSE) priv->mou = pDev;
if (dmxLocal->type == DMX_LOCAL_KEYBOARD) priv->kbd = pDev;
if (priv->initialized++) return; /* Only do once for mouse/keyboard pair */
if (!(dpy = priv->display = XOpenDisplay(dmxInput->name)))
dmxLog(dmxFatal,
"dmxOpenConsole: cannot open console display %s\n",
dmxInput->name);
/* Set up defaults */
dmxConsoleComputeWidthHeight(priv,
&priv->width, &priv->height,
&priv->xScale, &priv->yScale,
&priv->consWidth, &priv->consHeight);
/* Private initialization using computed values or constants. */
screen = DefaultScreen(dpy);
priv->initPointerX = scalex(priv, priv->width / 2);
priv->initPointerY = scaley(priv, priv->height / 2);
priv->eventMask = (ButtonPressMask
| ButtonReleaseMask
| PointerMotionMask
| EnterWindowMask
| LeaveWindowMask
| KeyPressMask
| KeyReleaseMask
| ExposureMask
| ResizeRedirectMask);
mask = CWBackPixel | CWEventMask | CWColormap | CWOverrideRedirect;
attribs.colormap = DefaultColormap(dpy, screen);
if (XParseColor(dpy, attribs.colormap, CONSOLE_BG_COLOR, &color)
&& XAllocColor(dpy, attribs.colormap, &color)) {
attribs.background_pixel = color.pixel;
} else
attribs.background_pixel = WhitePixel(dpy, screen);
attribs.event_mask = priv->eventMask;
attribs.override_redirect = False;
win = priv->window = XCreateWindow(dpy,
RootWindow(dpy, screen),
0, 0, priv->consWidth, priv->consHeight,
0,
DefaultDepth(dpy, screen),
InputOutput,
DefaultVisual(dpy, screen),
mask, &attribs);
priv->pixmap = XCreatePixmap(dpy, RootWindow(dpy, screen),
priv->consWidth, priv->consHeight,
DefaultDepth(dpy, screen));
/* Set up properties */
XStoreName(dpy, win, DMX_CONSOLE_NAME);
class_hints.res_name = DMX_RES_NAME;
class_hints.res_class = DMX_RES_CLASS;
XSetClassHint(dpy, win, &class_hints);
/* Map the window */
XMapWindow(dpy, win);
/* Create cursors */
priv->cursorNormal = XCreateFontCursor(dpy, XC_circle);
priv->cursorGrabbed = XCreateFontCursor(dpy, XC_spider);
priv->cursorEmpty = dmxConsoleCreateEmptyCursor(priv);
XDefineCursor(dpy, priv->window, priv->cursorNormal);
/* Create GC */
mask = (GCFunction | GCPlaneMask | GCClipMask | GCForeground |
GCBackground | GCLineWidth | GCLineStyle | GCCapStyle |
GCFillStyle | GCGraphicsExposures);
gcvals.function = GXcopy;
gcvals.plane_mask = AllPlanes;
gcvals.clip_mask = None;
if (XParseColor(dpy, attribs.colormap, CONSOLE_SCREEN_FG_COLOR, &color)
&& XAllocColor(dpy, attribs.colormap, &color)) {
gcvals.foreground = color.pixel;
} else
gcvals.foreground = BlackPixel(dpy, screen);
if (XParseColor(dpy, attribs.colormap, CONSOLE_SCREEN_BG_COLOR, &color)
&& XAllocColor(dpy, attribs.colormap, &color)) {
gcvals.background = color.pixel;
} else
gcvals.background = WhitePixel(dpy, screen);
gcvals.line_width = 0;
gcvals.line_style = LineSolid;
gcvals.cap_style = CapNotLast;
gcvals.fill_style = FillSolid;
gcvals.graphics_exposures = False;
priv->gc = XCreateGC(dpy, win, mask, &gcvals);
tmp = gcvals.foreground;
if (XParseColor(dpy, attribs.colormap, CONSOLE_SCREEN_DET_COLOR, &color)
&& XAllocColor(dpy, attribs.colormap, &color)) {
gcvals.foreground = color.pixel;
} else
gcvals.foreground = BlackPixel(dpy, screen);
priv->gcDet = XCreateGC(dpy, win, mask, &gcvals);
gcvals.foreground = tmp;
tmp = gcvals.background;
gcvals.background = gcvals.foreground;
gcvals.foreground = tmp;
priv->gcRev = XCreateGC(dpy, win, mask, &gcvals);
gcvals.background = gcvals.foreground;
if (XParseColor(dpy, attribs.colormap, CONSOLE_SCREEN_CUR_COLOR, &color)
&& XAllocColor(dpy, attribs.colormap, &color)) {
gcvals.foreground = color.pixel;
} else
gcvals.foreground = BlackPixel(dpy, screen);
priv->gcCur = XCreateGC(dpy, win, mask, &gcvals);
dmxConsoleDraw(priv, 1, 1);
if (dixLookupPrivate(&screenInfo.screens[0]->devPrivates,
dmxScreenPrivateKey))
priv->next = dixLookupPrivate(&screenInfo.screens[0]->devPrivates,
dmxScreenPrivateKey);
else
DMX_WRAP(CloseScreen, dmxCloseConsoleScreen,
priv, screenInfo.screens[0]);
dixSetPrivate(&screenInfo.screens[0]->devPrivates, dmxScreenPrivateKey,
priv);
}
/** Fill in the \a info structure for the specified \a pDev. Only used
* for pointers. */
void dmxConsoleMouGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
{
GETPRIVFROMPDEV;
info->buttonClass = 1;
dmxCommonMouGetMap(pDev, info->map, &info->numButtons);
info->valuatorClass = 1;
info->numRelAxes = 2;
info->minval[0] = 0;
info->minval[1] = 0;
/* max possible console window size: */
info->maxval[0] = DisplayWidth(priv->display, DefaultScreen(priv->display));
info->maxval[1] = DisplayHeight(priv->display, DefaultScreen(priv->display));
info->res[0] = 1;
info->minres[0] = 0;
info->maxres[0] = 1;
info->ptrFeedbackClass = 1;
}
/** Fill in the \a info structure for the specified \a pDev. Only used
* for keyboard. */
void dmxConsoleKbdGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info)
{
dmxCommonKbdGetInfo(pDev, info);
info->keyboard = 1;
info->keyClass = 1;
dmxCommonKbdGetMap(pDev, &info->keySyms, info->modMap);
info->freemap = 1;
info->focusClass = 1;
info->kbdFeedbackClass = 1;
}
/** Handle special console-only keys. */
int dmxConsoleFunctions(pointer private, DMXFunctionType function)
{
GETONLYPRIVFROMPRIVATE;
XRectangle rect;
Display *dpy = priv->display;
switch (function) {
case DMX_FUNCTION_FINE:
if (priv->fine) {
priv->fine = 0;
dmxConsoleClearCursor(priv, priv->globalX, priv->globalY, &rect);
XSetClipRectangles(dpy, priv->gc, 0, 0, &rect, 1, Unsorted);
XCopyArea(dpy, priv->pixmap, priv->window, priv->gc,
0, 0, priv->consWidth, priv->consHeight, 0, 0);
XSetClipMask(dpy, priv->gc, None);
XDefineCursor(dpy, priv->window,
priv->grabbed
? priv->cursorGrabbed
: priv->cursorNormal);
XWarpPointer(dpy, priv->window, priv->window,
0, 0, 0, 0,
scalex(priv, priv->globalX),
scaley(priv, priv->globalY));
XSync(dpy, False); /* Not a backend display */
} else {
priv->fine = 1;
XRaiseWindow(dpy, priv->window);
XDefineCursor(dpy, priv->window, priv->cursorEmpty);
dmxConsoleUpdateFineCursor(priv);
}
return 1;
case DMX_FUNCTION_GRAB:
if (priv->grabbed) {
XUngrabKeyboard(dpy, CurrentTime);
XUngrabPointer(dpy, CurrentTime);
XDefineCursor(dpy, priv->window,
priv->fine
? priv->cursorEmpty
: priv->cursorNormal);
} else {
if (XGrabPointer(dpy, priv->window, True,
0, GrabModeAsync, GrabModeAsync, priv->window,
None, CurrentTime)) {
dmxLog(dmxError, "XGrabPointer failed\n");
return 0;
}
if (XGrabKeyboard(dpy, priv->window, True,
GrabModeAsync, GrabModeAsync, CurrentTime)) {
dmxLog(dmxError, "XGrabKeyboard failed\n");
XUngrabPointer(dpy, CurrentTime);
return 0;
}
XDefineCursor(dpy, priv->window,
priv->fine
? priv->cursorEmpty
: priv->cursorGrabbed);
}
priv->grabbed = !priv->grabbed;
if (priv->fine) dmxConsoleUpdateFineCursor(priv);
return 1;
case DMX_FUNCTION_TERMINATE:
return 1;
default:
return 0;
}
}
static void dmxDump(void)
{
int i, j;
DMXInputInfo *dmxInput;
XEvent X;
for (i = 0, dmxInput = &dmxInputs[0]; i < dmxNumInputs; i++, dmxInput++) {
for (j = 0; j < dmxInput->numDevs; j++) {
DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[j];
myPrivate *priv = dmxLocal->private;
while (priv
&& priv->display
&& XCheckTypedEvent(priv->display, MotionNotify, &X)) {
DMXDBG4("dmxDump: %s/%d threw event away %d %s\n",
dmxInput->name, j, X.type, dmxEventName(X.type));
}
}
}
}
/** This routine is used to warp the pointer into the console window
* from anywhere on the screen. It is used when backend and console
* input are both being taken from the same X display. */
void dmxConsoleCapture(DMXInputInfo *dmxInput)
{
int i;
XEvent X;
DMXDBG0("dmxConsoleCapture\n");
dmxSync(NULL, TRUE);
for (i = 0; i < dmxInput->numDevs; i++) {
DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[i];
myPrivate *priv = dmxLocal->private;
if (dmxLocal->extType != DMX_LOCAL_TYPE_CONSOLE) continue;
if (dmxLocal->type != DMX_LOCAL_MOUSE) continue;
if (priv->captured) continue;
priv->captured = 2; /* Ungrab only after proximal events. */
XRaiseWindow(priv->display, priv->window);
XSync(priv->display, False); /* Not a backend display */
while (XCheckTypedEvent(priv->display, MotionNotify, &X)) {
DMXDBG3(" Ignoring motion to %d %d after capture on %s\n",
X.xmotion.x, X.xmotion.y, dmxInput->name);
}
XWarpPointer(priv->display, None,
priv->window, 0, 0, 0, 0, priv->curX, priv->curY);
XSync(priv->display, False); /* Not a backend display */
dmxDump();
if (priv->fine) dmxConsoleUpdateFineCursor(priv);
}
}
/** Undo the capture that was done by #dmxConsoleCapture. */
void dmxConsoleUncapture(DMXInputInfo *dmxInput)
{
int i;
DMXDBG0("dmxConsoleUncapture\n");
dmxSync(NULL, TRUE);
for (i = 0; i < dmxInput->numDevs; i++) {
DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[i];
myPrivate *priv = dmxLocal->private;
if (dmxLocal->extType != DMX_LOCAL_TYPE_CONSOLE) continue;
if (dmxLocal->type != DMX_LOCAL_MOUSE) continue;
if (!priv->captured) continue;
priv->captured = 0;
XSync(priv->display, False); /* Not a backend display */
}
}