1091 lines
37 KiB
C
1091 lines
37 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 = calloc(1, sizeof(*priv));
|
|
|
|
priv->dmxLocal = dmxLocal;
|
|
return priv;
|
|
}
|
|
|
|
/** If \a private is non-NULL, free its associated memory. */
|
|
void
|
|
dmxConsoleDestroyPrivate(pointer private)
|
|
{
|
|
free(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++) {
|
|
ScreenPtr pScreen = screenInfo.screens[i];
|
|
WindowPtr pRoot = pScreen->root;
|
|
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,
|
|
RegionNumRects(&pChild->clipList));
|
|
#endif
|
|
rect.x = scalex(priv, pChild->drawable.x + pScreen->x);
|
|
rect.y = scaley(priv, pChild->drawable.y + pScreen->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, screenInfo.screens[i]->x),
|
|
scaley(priv, screenInfo.screens[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, screenInfo.screens[i]->x),
|
|
scaley(priv, screenInfo.screens[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(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(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 (screenInfo.screens[i]->x + screenInfo.screens[i]->width > *width)
|
|
*width = screenInfo.screens[i]->x + screenInfo.screens[i]->width;
|
|
|
|
if (screenInfo.screens[i]->y + screenInfo.screens[i]->height > *height)
|
|
*height = screenInfo.screens[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 */
|
|
}
|
|
}
|