Lots of changes. CSQC should be functional, but is still tied to debug builds. It WILL have some bugs still, hopefully I'll be able to clean them up better if people test it a bit. Precompiled headers are working properly now. Compile times are now much quicker in msvc. This takes most of the files this commit. Restructured how client commands work. They're buffered outside the network message, some multithreaded code is in. It needs a bit of testing before it's active. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@885 fc73d0e0-1445-4013-8a0c-d673dee63da5
1845 lines
41 KiB
C
1845 lines
41 KiB
C
//network interface
|
|
|
|
#include "quakedef.h"
|
|
|
|
#ifndef NOMEDIA
|
|
|
|
#include "qux.h"
|
|
|
|
#ifdef _WIN32
|
|
#define EWOULDBLOCK WSAEWOULDBLOCK
|
|
#define EMSGSIZE WSAEMSGSIZE
|
|
#define ECONNRESET WSAECONNRESET
|
|
#define ECONNABORTED WSAECONNABORTED
|
|
#define ECONNREFUSED WSAECONNREFUSED
|
|
#define EADDRNOTAVAIL WSAEADDRNOTAVAIL
|
|
|
|
#define qerrno WSAGetLastError()
|
|
#else
|
|
|
|
|
|
#define qerrno errno
|
|
|
|
#define MSG_PARTIAL 0
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <netdb.h>
|
|
#include <sys/param.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/uio.h>
|
|
#include <arpa/inet.h>
|
|
#include <errno.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#define closesocket close
|
|
#define ioctlsocket ioctl
|
|
#endif
|
|
|
|
#undef malloc
|
|
#undef free
|
|
|
|
#define free BZ_Free
|
|
#define malloc BZ_Malloc
|
|
#define realloc BZ_Realloc
|
|
|
|
static xclient_t *xclients;
|
|
static int xlistensocket=-1;
|
|
|
|
xwindow_t *xfocusedwindow;
|
|
|
|
qboolean xrefreshed;
|
|
|
|
xclient_t *xgrabbedclient; //clients can ask the server to ignore other clients
|
|
|
|
#define MAXREQUESTSIZE 65535
|
|
|
|
|
|
void X_SendData(xclient_t *cl, void *data, int datalen)
|
|
{
|
|
#ifdef MULTITHREADWIN32
|
|
if (cl->threadhandle)
|
|
EnterCriticalSection(&cl->delecatesection);
|
|
#endif
|
|
|
|
if (cl->outbufferlen + datalen > cl->outbuffermaxlen)
|
|
{ //extend buffer size
|
|
cl->outbuffermaxlen = cl->outbufferlen + datalen + 1024;
|
|
cl->outbuffer = realloc(cl->outbuffer, cl->outbuffermaxlen);
|
|
}
|
|
|
|
memcpy(cl->outbuffer+cl->outbufferlen, data, datalen);
|
|
cl->outbufferlen += datalen;
|
|
|
|
#ifdef MULTITHREADWIN32
|
|
if (cl->threadhandle)
|
|
LeaveCriticalSection(&cl->delecatesection);
|
|
#endif
|
|
}
|
|
|
|
void X_SendNotification(xEvent *data)
|
|
{
|
|
xclient_t *cl;
|
|
for (cl = xclients; cl; cl = cl->nextclient)
|
|
{
|
|
if (cl->stillinitialising)
|
|
continue;
|
|
if (cl->tobedropped)
|
|
continue;
|
|
if (cl->outbufferlen > MAXREQUESTSIZE*4)
|
|
continue;
|
|
|
|
data->u.u.sequenceNumber = cl->requestnum;
|
|
X_SendData(cl, data, sizeof(xEvent));
|
|
}
|
|
}
|
|
|
|
|
|
qboolean X_NotifcationMaskPresent(xwindow_t *window, int mask, xclient_t *notfor)
|
|
{
|
|
xnotificationmask_t *nm;
|
|
nm = window->notificationmask;
|
|
|
|
if (mask == SubstructureNotifyMask || mask == SubstructureRedirectMask)
|
|
{
|
|
window = window->parent;
|
|
// for(;window;window=window->parent)
|
|
{
|
|
for (nm = window->notificationmask; nm; nm = nm->next)
|
|
{
|
|
if (nm->mask & mask)
|
|
if (nm->client != notfor)
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
for (nm = window->notificationmask; nm; nm = nm->next)
|
|
{
|
|
if (nm->mask & mask)
|
|
if (nm->client != notfor)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int X_SendNotificationMasked(xEvent *data, xwindow_t *window, unsigned int mask)
|
|
{
|
|
int count=0;
|
|
xclient_t *cl;
|
|
xnotificationmask_t *nm;
|
|
|
|
xwindow_t *child = window;
|
|
|
|
if (mask == SubstructureNotifyMask || mask == SubstructureRedirectMask)
|
|
{
|
|
for (cl = xclients; cl; cl = cl->nextclient)
|
|
{
|
|
//don't send to if...
|
|
if (cl->stillinitialising)
|
|
continue;
|
|
if (cl->tobedropped)
|
|
continue;
|
|
if (cl->outbufferlen > MAXREQUESTSIZE*4)
|
|
{
|
|
cl->tobedropped = true;
|
|
continue;
|
|
}
|
|
window = child->parent;
|
|
|
|
// for (window = child; window; window = window->parent)
|
|
{
|
|
for (nm = window->notificationmask; nm; nm = nm->next)
|
|
{
|
|
if (nm->client != cl)
|
|
continue;
|
|
if (!(nm->mask & mask))
|
|
continue;
|
|
|
|
data->u.reparent.event = window->res.id; //so the request/notification/whatever knows who asked for it.
|
|
|
|
data->u.u.sequenceNumber = cl->requestnum;
|
|
X_SendData(cl, data, sizeof(xEvent));
|
|
count++;
|
|
break;
|
|
}
|
|
// if (nm)
|
|
// break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (nm = window->notificationmask; nm; nm = nm->next)
|
|
{
|
|
if (!(nm->mask & mask))
|
|
continue;
|
|
cl = nm->client;
|
|
|
|
if (cl->stillinitialising)
|
|
continue;
|
|
if (cl->tobedropped)
|
|
continue;
|
|
if (cl->outbufferlen > MAXREQUESTSIZE*4)
|
|
{
|
|
cl->tobedropped = true;
|
|
continue;
|
|
}
|
|
|
|
data->u.u.sequenceNumber = cl->requestnum;
|
|
X_SendData(cl, data, sizeof(xEvent));
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
int X_SendInputNotification(xEvent *data, xwindow_t *window, unsigned int mask)
|
|
{
|
|
int count=0;
|
|
xclient_t *cl;
|
|
xnotificationmask_t *nm;
|
|
|
|
xwindow_t *child = window;
|
|
xwindow_t *focus;
|
|
|
|
//we go all the way to the root if needed.
|
|
|
|
for (cl = xclients; cl; cl = cl->nextclient)
|
|
{
|
|
//don't send to if...
|
|
if (cl->stillinitialising)
|
|
continue;
|
|
if (cl->tobedropped)
|
|
continue;
|
|
if (cl->outbufferlen > MAXREQUESTSIZE*4)
|
|
{
|
|
cl->tobedropped = true;
|
|
continue;
|
|
}
|
|
window = child->parent;
|
|
|
|
for (window = child; window; window = window->parent)
|
|
{
|
|
for (nm = window->notificationmask; nm; nm = nm->next)
|
|
{
|
|
if (nm->client != cl)
|
|
continue;
|
|
if (!(nm->mask & mask))
|
|
continue;
|
|
|
|
Con_Printf("Sending input %i\n", data->u.u.type);
|
|
|
|
if (data->u.u.type == FocusIn || data->u.u.type == FocusOut)
|
|
{
|
|
data->u.u.sequenceNumber = cl->requestnum;
|
|
X_SendData(cl, data, sizeof(xEvent));
|
|
count++;
|
|
break;
|
|
}
|
|
|
|
data->u.keyButtonPointer.event = window->res.id; //so the request/notification/whatever knows who asked for it.
|
|
data->u.keyButtonPointer.eventX = data->u.keyButtonPointer.rootX;
|
|
data->u.keyButtonPointer.eventY = data->u.keyButtonPointer.rootY;
|
|
for (window = window; window; window = window->parent) //adjust event's xpos/ypos
|
|
{
|
|
data->u.keyButtonPointer.eventX -= window->xpos;
|
|
data->u.keyButtonPointer.eventY -= window->ypos;
|
|
}
|
|
|
|
if (data->u.u.type == EnterNotify || data->u.u.type == LeaveNotify)
|
|
{
|
|
data->u.enterLeave.flags &= ~ELFlagFocus;
|
|
|
|
focus = xfocusedwindow;
|
|
while(focus)
|
|
{
|
|
if (focus->res.id == data->u.enterLeave.event)
|
|
{
|
|
data->u.enterLeave.flags |= ELFlagFocus;
|
|
break;
|
|
}
|
|
focus = focus->parent;
|
|
}
|
|
}
|
|
|
|
data->u.u.sequenceNumber = cl->requestnum;
|
|
if (data->u.keyButtonPointer.event == data->u.keyButtonPointer.child)
|
|
{
|
|
data->u.keyButtonPointer.child = None;
|
|
X_SendData(cl, data, sizeof(xEvent));
|
|
data->u.keyButtonPointer.child = data->u.keyButtonPointer.event;
|
|
}
|
|
else
|
|
X_SendData(cl, data, sizeof(xEvent));
|
|
count++;
|
|
break;
|
|
}
|
|
if (nm || (window->donotpropagate & mask))
|
|
break;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void X_SendError(xclient_t *cl, int errorcode, int assocresource, int major, int minor)
|
|
{
|
|
xError err;
|
|
err.type = X_Error;
|
|
err.errorCode = errorcode;
|
|
err.sequenceNumber = cl->requestnum; /* the nth request from this client */
|
|
err.resourceID = assocresource;
|
|
err.minorCode = minor;
|
|
err.majorCode = major;
|
|
err.pad1 = 0;
|
|
err.pad3 = 0;
|
|
err.pad4 = 0;
|
|
err.pad5 = 0;
|
|
err.pad6 = 0;
|
|
err.pad7 = 0;
|
|
|
|
X_SendData(cl, &err, sizeof(err));
|
|
}
|
|
|
|
int X_NewRIDBase(void)
|
|
{
|
|
xclient_t *cl;
|
|
int ridbase = 0x200000;
|
|
while(ridbase) //it'll wrap at some point...
|
|
{
|
|
for (cl = xclients; cl; cl = cl->nextclient)
|
|
{
|
|
if (cl->ridbase == ridbase) //someone has this range...
|
|
{
|
|
ridbase+=0x200000;
|
|
break;
|
|
}
|
|
}
|
|
if (!cl)
|
|
return ridbase;
|
|
}
|
|
|
|
//err... bugger... that could be problematic...
|
|
//try again, but start allocating half quantities and hope a client drops soon...
|
|
|
|
ridbase = 0x200000;
|
|
while(ridbase)
|
|
{
|
|
for (cl = xclients; cl; cl = cl->nextclient)
|
|
{
|
|
if (cl->ridbase == ridbase) //someone has this range...
|
|
{
|
|
ridbase+=0x100000;
|
|
break;;
|
|
}
|
|
}
|
|
if (!cl)
|
|
return ridbase;
|
|
}
|
|
|
|
if (ridbase)
|
|
return ridbase;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void X_SendIntialResponse(xclient_t *cl)
|
|
{
|
|
int rid;
|
|
char buffer[8192];
|
|
xConnSetupPrefix *prefix;
|
|
xConnSetup *setup;
|
|
char *vendor;
|
|
xPixmapFormat *pixmapformats;
|
|
xnotificationmask_t *nm;
|
|
|
|
xWindowRoot *root;
|
|
xDepth *depth;
|
|
xVisualType *visualtype;
|
|
|
|
rid = X_NewRIDBase();
|
|
cl->ridbase = rid;
|
|
|
|
if (!rid)
|
|
{
|
|
prefix = (xConnSetupPrefix *)buffer;
|
|
prefix->success = 0;
|
|
prefix->lengthReason = 22;
|
|
prefix->majorVersion = 11; //protocol version.
|
|
prefix->minorVersion = 0;
|
|
prefix->length = (prefix->lengthReason/4+3)&~3;
|
|
strcpy((char *)(prefix+1), "No free resource range");
|
|
X_SendData(cl, prefix, sizeof(prefix)+(prefix->length+1)*4);
|
|
cl->tobedropped = true;
|
|
}
|
|
else
|
|
{
|
|
prefix = (xConnSetupPrefix *)buffer;
|
|
prefix->success = 1;
|
|
prefix->lengthReason = 0;
|
|
prefix->majorVersion = 11; //protocol version.
|
|
prefix->minorVersion = 0;
|
|
|
|
setup = (xConnSetup *)(prefix+1);
|
|
setup->release = build_number(); //our version number
|
|
setup->ridBase = rid;
|
|
setup->ridMask = 0x1fffff;
|
|
setup->motionBufferSize = 1;
|
|
setup->maxRequestSize = MAXREQUESTSIZE;
|
|
setup->numRoots = 1; //we only have one display. so only one root window please.
|
|
setup->imageByteOrder = LSBFirst; /* LSBFirst, MSBFirst */
|
|
setup->bitmapBitOrder = LSBFirst; /* LeastSignificant, MostSign...*/
|
|
setup->bitmapScanlineUnit = 32, /* 8, 16, 32 */
|
|
setup->bitmapScanlinePad = 32; /* 8, 16, 32 */
|
|
setup->minKeyCode = 1;
|
|
setup->maxKeyCode = 255;
|
|
|
|
vendor = (char *)(setup+1);
|
|
strcpy(vendor, "FTE QuakeWorld X");
|
|
setup->nbytesVendor = (strlen(vendor)+3)&~3;
|
|
|
|
pixmapformats = (xPixmapFormat *)(vendor + setup->nbytesVendor);
|
|
setup->numFormats = 0;
|
|
|
|
/* pixmapformats[setup->numFormats].depth = 16;
|
|
pixmapformats[setup->numFormats].bitsPerPixel = 16;
|
|
pixmapformats[setup->numFormats].scanLinePad = 16;
|
|
pixmapformats[setup->numFormats].pad1=0;
|
|
pixmapformats[setup->numFormats].pad2=0;
|
|
setup->numFormats++;*/
|
|
|
|
pixmapformats[setup->numFormats].depth = 24;
|
|
pixmapformats[setup->numFormats].bitsPerPixel = 32;
|
|
pixmapformats[setup->numFormats].scanLinePad = 32;
|
|
pixmapformats[setup->numFormats].pad1=0;
|
|
pixmapformats[setup->numFormats].pad2=0;
|
|
setup->numFormats++;
|
|
|
|
root = (xWindowRoot *)(pixmapformats + setup->numFormats);
|
|
root->windowId = rootwindow->res.id;
|
|
root->defaultColormap = 32;
|
|
root->whitePixel = 0xffffff;
|
|
root->blackPixel = 0;
|
|
root->currentInputMask = 0;
|
|
for (nm = rootwindow->notificationmask; nm; nm = nm->next)
|
|
root->currentInputMask |= nm->mask;
|
|
root->pixWidth = rootwindow->width;
|
|
root->pixHeight = rootwindow->height;
|
|
root->mmWidth = rootwindow->width/3;
|
|
root->mmHeight = rootwindow->height/3;
|
|
root->minInstalledMaps = 1;
|
|
root->maxInstalledMaps = 1;
|
|
root->rootVisualID = 0x22;
|
|
root->backingStore = 0;
|
|
root->saveUnders = false;
|
|
root->rootDepth = 24;
|
|
root->nDepths = 0;
|
|
|
|
depth = (xDepth*)(root + 1);
|
|
depth->depth = 24;
|
|
depth->pad1 = 0;
|
|
depth->nVisuals = 1;
|
|
depth->pad2 = 0;
|
|
root->nDepths++;
|
|
|
|
visualtype = (xVisualType*)(depth+1);
|
|
visualtype->visualID = root->rootVisualID;
|
|
visualtype->class = TrueColor;
|
|
visualtype->bitsPerRGB = 24;
|
|
visualtype->colormapEntries = 256;
|
|
visualtype->redMask = 0x0000ff;
|
|
visualtype->greenMask = 0x00ff00;
|
|
visualtype->blueMask = 0xff0000;
|
|
visualtype->pad = 0;
|
|
|
|
visualtype++;
|
|
prefix->length = ((char *)visualtype - (char *)setup)/4;
|
|
|
|
X_SendData(cl, prefix, (char *)visualtype - (char *)prefix);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
qboolean XWindows_TendToClient(xclient_t *cl) //true says drop
|
|
{
|
|
int err;
|
|
int len;
|
|
unsigned int inlen;
|
|
char *input;
|
|
|
|
if (!xgrabbedclient || xgrabbedclient == cl) //someone grabbed the server
|
|
if (cl->outbufferlen < 256 && !cl->tobedropped) //don't accept new messages if we still have a lot to write.
|
|
{
|
|
#ifdef MULTITHREADWIN32
|
|
if (!cl->threadhandle)
|
|
#endif
|
|
{
|
|
if (cl->inbuffermaxlen - cl->inbufferlen < 1000) //do we need to expand this message?
|
|
{
|
|
char *newbuffer;
|
|
cl->inbuffermaxlen += 1000;
|
|
newbuffer = malloc(cl->inbuffermaxlen);
|
|
if (cl->inbuffer)
|
|
{
|
|
memcpy(newbuffer, cl->inbuffer, cl->inbufferlen);
|
|
free(cl->inbuffer);
|
|
}
|
|
cl->inbuffer = newbuffer;
|
|
}
|
|
len = cl->inbuffermaxlen - cl->inbufferlen;
|
|
Con_Printf("recving\n");
|
|
len = recv(cl->socket, cl->inbuffer + cl->inbufferlen, len, 0);
|
|
Con_Printf("recved %i\n", len);
|
|
if (len == 0) //connection was closed. bummer.
|
|
{
|
|
Con_Printf("Closed\n");
|
|
return true;
|
|
}
|
|
if (len > 0)
|
|
{
|
|
cl->inbufferlen += len;
|
|
}
|
|
else
|
|
{
|
|
err = qerrno;
|
|
if (err != EWOULDBLOCK)
|
|
{
|
|
Con_Printf("X read error %i\n", err);
|
|
cl->tobedropped = true;
|
|
}
|
|
}
|
|
}
|
|
#ifdef MULTITHREADWIN32
|
|
else
|
|
EnterCriticalSection(&cl->delecatesection);
|
|
#endif
|
|
|
|
// if (len > 0) //the correct version
|
|
if (cl->inbufferlen > 0) //temp
|
|
{
|
|
input = cl->inbuffer;
|
|
nextmessage:
|
|
inlen = cl->inbufferlen - (input - cl->inbuffer);
|
|
if (cl->stillinitialising)
|
|
{
|
|
if (inlen >= sizeof(xConnClientPrefix))
|
|
{
|
|
xConnClientPrefix *prefix = (xConnClientPrefix *)input;
|
|
input += sizeof(xConnClientPrefix);
|
|
cl->stillinitialising = false;
|
|
if (prefix->byteOrder != 'l') //egad no. horrible.
|
|
{
|
|
#ifdef MULTITHREADWIN32
|
|
LeaveCriticalSection(&cl->delecatesection);
|
|
#endif
|
|
return true;
|
|
}
|
|
if (prefix->majorVersion != 11) //x11 only. Sorry.
|
|
{
|
|
#ifdef MULTITHREADWIN32
|
|
LeaveCriticalSection(&cl->delecatesection);
|
|
#endif
|
|
return true;
|
|
}
|
|
if (prefix->minorVersion != 0) //we don't know of any variations.
|
|
{
|
|
#ifdef MULTITHREADWIN32
|
|
LeaveCriticalSection(&cl->delecatesection);
|
|
#endif
|
|
return true;
|
|
}
|
|
if (prefix->nbytesAuthProto != 0) //we can't handle this
|
|
{
|
|
#ifdef MULTITHREADWIN32
|
|
LeaveCriticalSection(&cl->delecatesection);
|
|
#endif
|
|
return true;
|
|
}
|
|
if (prefix->nbytesAuthString != 0) //we can't handle this
|
|
{
|
|
#ifdef MULTITHREADWIN32
|
|
LeaveCriticalSection(&cl->delecatesection);
|
|
#endif
|
|
return true;
|
|
}
|
|
X_SendIntialResponse(cl);
|
|
goto nextmessage;
|
|
}
|
|
}
|
|
else if (inlen >= sizeof(xReq))
|
|
{
|
|
int rlen;
|
|
xReq *req;
|
|
req = (xReq *)input;
|
|
|
|
rlen = req->length;
|
|
if (!rlen && inlen >= sizeof(xReq)+sizeof(CARD32)) //BIG-REQUESTS says that if the length of a request is 0, then there's an extra 32bit int with the correct length imediatly after the 0.
|
|
rlen = *(CARD32 *)(req+1);
|
|
if (rlen && inlen >= rlen*4)
|
|
{
|
|
cl->requestnum++;
|
|
|
|
if (/*req->reqType < 0 || req->reqType >= 256 ||*/ !XRequests[req->reqType])
|
|
{
|
|
// Con_Printf("X request %i, len %i - NOT SUPPORTED\n", req->reqType, rlen*4);
|
|
|
|
//this is a minimal implementation...
|
|
X_SendError(cl, BadImplementation, 0, req->reqType, 0);
|
|
// cl->tobedropped = true;
|
|
}
|
|
else
|
|
{
|
|
// Con_Printf("X request %i, len %i\n", req->reqType, rlen*4);
|
|
|
|
//Con_Printf("Request %i\n", req->reqType);
|
|
// Z_CheckSentinals();
|
|
XS_CheckResourceSentinals();
|
|
if (!req->length)
|
|
{
|
|
int rt, data;
|
|
|
|
rt = req->reqType; //save these off
|
|
data = req->data;
|
|
|
|
req = (xReq *)((char *)req+sizeof(CARD32)); //adjust correctly.
|
|
|
|
req->reqType = rt; //and restore them into the space taken by the longer size.
|
|
req->data = data;
|
|
req->length = 0; //Don't rely on this. This isn't really needed.
|
|
|
|
XRequests[req->reqType](cl, req);
|
|
}
|
|
else
|
|
XRequests[req->reqType](cl, req);
|
|
XS_CheckResourceSentinals();
|
|
// Z_CheckSentinals();
|
|
//Con_Printf("Done request\n");
|
|
}
|
|
|
|
input += rlen*4;
|
|
|
|
goto nextmessage;
|
|
}
|
|
}
|
|
|
|
len = input - cl->inbuffer;
|
|
memmove(cl->inbuffer, input, cl->inbufferlen - len);
|
|
cl->inbufferlen -= len;
|
|
}
|
|
#ifdef MULTITHREADWIN32
|
|
if (cl->threadhandle)
|
|
LeaveCriticalSection(&cl->delecatesection);
|
|
#endif
|
|
}
|
|
|
|
if (cl->outbufferlen) //still send if grabbed. don't let things build up this side.
|
|
{
|
|
#ifdef MULTITHREADWIN32
|
|
if (!cl->threadhandle)
|
|
#endif
|
|
{
|
|
len = cl->outbufferlen;
|
|
if (len > 8000)
|
|
len = 8000;
|
|
len = send(cl->socket, cl->outbuffer, len, 0);
|
|
if (len>0)
|
|
{
|
|
memmove(cl->outbuffer, cl->outbuffer+len, cl->outbufferlen - len);
|
|
cl->outbufferlen -= len;
|
|
}
|
|
if (len == 0)
|
|
cl->tobedropped = true;
|
|
if (len < 0)
|
|
{
|
|
err = qerrno;
|
|
if (err != EWOULDBLOCK)
|
|
{
|
|
Con_Printf("X send error %i\n", err);
|
|
cl->tobedropped = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ((!xgrabbedclient || xgrabbedclient == cl) && cl->tobedropped)
|
|
return true; //grabbed servers do not allow altering state if a client drops
|
|
return false;
|
|
}
|
|
|
|
#ifdef MULTITHREAD
|
|
|
|
#ifdef _WIN32
|
|
DWORD WINAPI X_RunClient(void *parm)
|
|
#else
|
|
void X_RunClient(void *parm)
|
|
#endif
|
|
{
|
|
char buffer[8192*64];
|
|
int read, len, err;
|
|
xclient_t *cl = parm;
|
|
|
|
while(cl->threadhandle)
|
|
{
|
|
if (cl->tobedropped)
|
|
{ //don't bother reading more.
|
|
read = 0;
|
|
}
|
|
else
|
|
{
|
|
read = recv(cl->socket, buffer, sizeof(buffer), 0);
|
|
if (read<0 && !cl->outbufferlen)
|
|
{
|
|
if (qerrno != EWOULDBLOCK)
|
|
cl->tobedropped = true;
|
|
else
|
|
{
|
|
Sleep(1);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef MULTITHREADWIN32
|
|
EnterCriticalSection(&cl->delecatesection);
|
|
#endif
|
|
|
|
if (read > 0)
|
|
{
|
|
if (cl->inbuffermaxlen < cl->inbufferlen+read) //expand in buffer
|
|
{
|
|
cl->inbuffermaxlen = cl->inbufferlen+read + 1000; //add breathing room.
|
|
cl->inbuffer = realloc(cl->inbuffer, cl->inbuffermaxlen);
|
|
}
|
|
memcpy(cl->inbuffer+cl->inbufferlen, buffer, read);
|
|
cl->inbufferlen += read;
|
|
}
|
|
else if (!read) //no more socket.
|
|
cl->tobedropped = true;
|
|
else
|
|
{ //error of some sort
|
|
err = qerrno;
|
|
if (err != EWOULDBLOCK)
|
|
cl->tobedropped = true;
|
|
}
|
|
|
|
if (cl->outbufferlen)
|
|
{
|
|
len = cl->outbufferlen;
|
|
if (len > 8000)
|
|
len = 8000;
|
|
len = send(cl->socket, cl->outbuffer, len, 0); //move out of critical section?
|
|
if (len>0)
|
|
{
|
|
memmove(cl->outbuffer, cl->outbuffer+len, cl->outbufferlen - len);
|
|
cl->outbufferlen -= len;
|
|
}
|
|
if (len == 0)
|
|
{
|
|
cl->tobedropped = true;
|
|
cl->outbufferlen=0;
|
|
}
|
|
if (len < 0)
|
|
{
|
|
err = qerrno;
|
|
if (err != EWOULDBLOCK)
|
|
{
|
|
cl->tobedropped = true;
|
|
cl->outbufferlen=0;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef MULTITHREADWIN32
|
|
LeaveCriticalSection(&cl->delecatesection);
|
|
#endif
|
|
}
|
|
|
|
DeleteCriticalSection (&cl->delecatesection);
|
|
|
|
closesocket(cl->socket);
|
|
if (cl->inbuffer)
|
|
free(cl->inbuffer);
|
|
if (cl->outbuffer)
|
|
free(cl->outbuffer);
|
|
free(cl);
|
|
|
|
#ifdef MULTITHREADWIN32
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
#endif
|
|
|
|
void XWindows_TendToClients(void)
|
|
{
|
|
struct sockaddr_in address;
|
|
int addrlen;
|
|
xclient_t *cl, *prev=NULL;
|
|
int newclient;
|
|
#ifndef MULTITHREADWIN32
|
|
unsigned int _true = 1;
|
|
#endif
|
|
|
|
if (xlistensocket != -1)
|
|
{
|
|
addrlen = sizeof(address);
|
|
newclient = accept(xlistensocket, (struct sockaddr *)&address, &addrlen);
|
|
if (newclient != -1)
|
|
{
|
|
cl = malloc(sizeof(xclient_t));
|
|
memset(cl, 0, sizeof(xclient_t));
|
|
cl->socket = newclient;
|
|
cl->nextclient = xclients;
|
|
cl->stillinitialising = 1;
|
|
xclients = cl;
|
|
|
|
|
|
#ifdef MULTITHREADWIN32
|
|
InitializeCriticalSection (&cl->delecatesection);
|
|
{DWORD tid;
|
|
cl->threadhandle = CreateThread(NULL, 0, X_RunClient, cl, 0, &tid);
|
|
}
|
|
|
|
if (!cl->threadhandle)
|
|
DeleteCriticalSection (&cl->delecatesection);
|
|
#else
|
|
if (ioctlsocket(cl->socket, FIONBIO, &_true) == -1)
|
|
Sys_Error("Nonblocking failed\n");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
for (cl = xclients; cl; cl = cl->nextclient)
|
|
{
|
|
if (XWindows_TendToClient(cl))
|
|
{
|
|
if (prev)
|
|
{
|
|
prev->nextclient = cl->nextclient;
|
|
}
|
|
else
|
|
xclients = cl->nextclient;
|
|
|
|
XS_DestroyResourcesOfClient(cl);
|
|
|
|
#ifdef MULTITHREADWIN32
|
|
if (cl->threadhandle)
|
|
{
|
|
cl->threadhandle = NULL;
|
|
break;
|
|
}
|
|
#endif
|
|
closesocket(cl->socket);
|
|
if (cl->inbuffer)
|
|
free(cl->inbuffer);
|
|
if (cl->outbuffer)
|
|
free(cl->outbuffer);
|
|
free(cl);
|
|
break;
|
|
}
|
|
|
|
prev = cl;
|
|
}
|
|
}
|
|
|
|
void XWindows_Startup(void) //initialise the server socket and do any initial setup as required.
|
|
{
|
|
struct sockaddr_in address;
|
|
unsigned long _true = true;
|
|
|
|
int port = 6000;
|
|
|
|
port += atoi(Cmd_Argv(1));
|
|
|
|
if (xlistensocket == -1)
|
|
{
|
|
if ((xlistensocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
|
|
{
|
|
Con_Printf("Failed to create socket\n");
|
|
return;
|
|
}
|
|
if (ioctlsocket (xlistensocket, FIONBIO, &_true) == -1)
|
|
{
|
|
closesocket(xlistensocket);
|
|
Con_Printf("Failed to create socket\n");
|
|
xlistensocket = -1;
|
|
return;
|
|
}
|
|
|
|
address.sin_family = AF_INET;
|
|
address.sin_addr.s_addr = INADDR_ANY;
|
|
|
|
if (port == PORT_ANY)
|
|
address.sin_port = 0;
|
|
else
|
|
address.sin_port = htons((short)port);
|
|
|
|
if( bind (xlistensocket, (void *)&address, sizeof(address)) == -1)
|
|
{
|
|
Con_Printf("Couldn't bind socket to port %i\n", port);
|
|
closesocket(xlistensocket);
|
|
xlistensocket = -1;
|
|
return;
|
|
}
|
|
if( listen (xlistensocket, 3) == -1)
|
|
{
|
|
Con_Printf("Couldn't listen on port %i\n", port);
|
|
closesocket(xlistensocket);
|
|
xlistensocket = -1;
|
|
return;
|
|
}
|
|
|
|
X_InitRequests();
|
|
XS_CreateInitialResources();
|
|
}
|
|
|
|
XS_CheckResourceSentinals();
|
|
|
|
//quakie stuph
|
|
key_dest = key_menu;
|
|
m_state = m_xwindows;
|
|
m_entersound = true;
|
|
}
|
|
|
|
extern int x_windowwithfocus;
|
|
extern int x_windowwithcursor;
|
|
void XWindows_RefreshWindow(xwindow_t *wnd)
|
|
{
|
|
xwindow_t *p;
|
|
short xpos;
|
|
short ypos;
|
|
unsigned int *out, *in;
|
|
|
|
int x, y;
|
|
int maxx, maxy;
|
|
|
|
if (wnd->inputonly) //no thanks.
|
|
return;
|
|
|
|
xpos = 0;
|
|
ypos = 0;
|
|
for (p = wnd->parent; p; p = p->parent)
|
|
{
|
|
xpos += p->xpos;
|
|
ypos += p->ypos;
|
|
}
|
|
|
|
y = ypos + wnd->ypos;
|
|
maxy = y + wnd->height;
|
|
if (y < ypos+wnd->ypos)
|
|
{
|
|
y = ypos+wnd->ypos;
|
|
}
|
|
if (y < 0)
|
|
y = 0;
|
|
if (maxy >= xscreenheight)
|
|
maxy = xscreenheight-1;
|
|
|
|
if (!wnd->mapped)//&&rand()&1)
|
|
{ //unmapped windows are invisible.
|
|
return;
|
|
}
|
|
|
|
|
|
{
|
|
if (wnd->buffer)// && x_windowwithfocus != wnd->res.id)
|
|
{
|
|
for (; y < maxy; y++)
|
|
{
|
|
x = xpos + wnd->xpos;
|
|
maxx = x + wnd->width;
|
|
if (x < xpos+wnd->xpos)
|
|
{
|
|
x = xpos+wnd->xpos;
|
|
}
|
|
if (x < 0)
|
|
x = 0;
|
|
if (maxx > xscreenwidth)
|
|
maxx = xscreenwidth;
|
|
|
|
out = (unsigned int *)xscreen + (x+(y*xscreenwidth));
|
|
in = (unsigned int *)wnd->buffer + (x-xpos-wnd->xpos) + (y-ypos-wnd->ypos)*wnd->width;
|
|
|
|
for (; x < maxx; x++)
|
|
{
|
|
*out++ = *in++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
|
|
for (; y < maxy; y++)
|
|
{
|
|
x = xpos + wnd->xpos;
|
|
maxx = x + wnd->width;
|
|
if (x < xpos+wnd->xpos)
|
|
{
|
|
x = xpos+wnd->xpos;
|
|
}
|
|
if (x < 0)
|
|
{
|
|
x = 0;
|
|
}
|
|
if (maxx > xscreenwidth)
|
|
maxx = xscreenwidth;
|
|
|
|
out = (unsigned int *)xscreen + (x+(y*xscreenwidth));
|
|
|
|
for (; x < maxx; x++)
|
|
{
|
|
*out++ = wnd->backpixel;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
wnd = wnd->child;
|
|
while(wnd)
|
|
{
|
|
XWindows_RefreshWindow(wnd);
|
|
wnd = wnd->sibling;
|
|
}
|
|
}
|
|
/*
|
|
void XWindows_DrawWindowTree(xwindow_t *wnd, short xofs, short yofs)
|
|
{
|
|
int x, y;
|
|
int maxx, maxy;
|
|
unsigned int *out;
|
|
|
|
if (wnd->res.owner)
|
|
{
|
|
y = yofs + wnd->ypos;
|
|
maxy = y + wnd->width;
|
|
if (y < 0)
|
|
{
|
|
y = 0;
|
|
}
|
|
if (maxy >= xscreenheight)
|
|
maxy = xscreenheight-1;
|
|
for (y = 0; y < wnd->height; y++)
|
|
{
|
|
x = xofs + wnd->xpos;
|
|
maxx = x + wnd->height;
|
|
if (x < 0)
|
|
{
|
|
x = 0;
|
|
}
|
|
if (maxx >= xscreenwidth)
|
|
maxx = xscreenwidth-1;
|
|
|
|
out = (unsigned int *)xscreen + (x+(y*xscreenwidth));
|
|
|
|
for (; x < maxx; x++)
|
|
{
|
|
*out = rand();
|
|
out++;
|
|
}
|
|
}
|
|
}
|
|
|
|
xofs += wnd->xpos;
|
|
yofs += wnd->ypos;
|
|
|
|
wnd = wnd->child;
|
|
while(wnd)
|
|
{
|
|
XWindows_DrawWindowTree(wnd, xofs, yofs);
|
|
wnd = wnd->sibling;
|
|
}
|
|
}
|
|
*/
|
|
|
|
//quakie functions
|
|
void XWindows_Init(void)
|
|
{
|
|
Cmd_AddCommand("startx", XWindows_Startup);
|
|
}
|
|
|
|
int x_mousex;
|
|
int x_mousey;
|
|
int x_windowwithcursor;
|
|
int x_windowwithfocus;
|
|
|
|
int mousestate;
|
|
|
|
void X_EvalutateCursorOwner(int movemode)
|
|
{
|
|
xEvent ev;
|
|
xwindow_t *cursorowner, *wnd, *use;
|
|
int mx, my;
|
|
int wcx;
|
|
int wcy;
|
|
|
|
extern xwindow_t *xpconfinewindow;
|
|
|
|
{
|
|
extern int mousecursor_x, mousecursor_y;
|
|
mx = mousecursor_x * ((float)rootwindow->width/vid.width);
|
|
my = mousecursor_y * ((float)rootwindow->height/vid.height);
|
|
}
|
|
if (mx >= xscreenwidth)
|
|
mx = xscreenwidth-1;
|
|
if (my >= xscreenheight)
|
|
my = xscreenheight-1;
|
|
if (mx < 0)
|
|
mx = 0;
|
|
if (my < 0)
|
|
my = 0;
|
|
|
|
if (xpconfinewindow) //don't leave me!
|
|
{
|
|
cursorowner = xpconfinewindow;
|
|
|
|
wcx = 0; wcy = 0;
|
|
|
|
for (wnd = cursorowner; wnd; wnd = wnd->parent)
|
|
{
|
|
wcx += wnd->xpos;
|
|
wcy += wnd->ypos;
|
|
}
|
|
|
|
if (movemode == NotifyNormal)
|
|
movemode = NotifyWhileGrabbed;
|
|
}
|
|
else
|
|
{
|
|
cursorowner = rootwindow;
|
|
wcx = 0; wcy = 0;
|
|
while(1)
|
|
{
|
|
use = NULL;
|
|
//find the last window that contains the pointer (lower windows come first)
|
|
for (wnd = cursorowner->child; wnd; wnd = wnd->sibling)
|
|
{
|
|
if (/*!wnd->inputonly && */wnd->mapped)
|
|
if (wcx+wnd->xpos <= mx && wcx+wnd->xpos+wnd->width >= mx)
|
|
{
|
|
if (wcy+wnd->ypos <= my && wcy+wnd->ypos+wnd->height >= my)
|
|
{
|
|
use = wnd;
|
|
}
|
|
}
|
|
}
|
|
wnd = use;
|
|
|
|
if (wnd)
|
|
{
|
|
cursorowner = wnd;
|
|
wcx += wnd->xpos;
|
|
wcy += wnd->ypos;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (mx != x_mousex || my != x_mousey || x_windowwithcursor != cursorowner->res.id)
|
|
{
|
|
// extern qboolean keydown[256];
|
|
|
|
// Con_Printf("move %i %i\n", mx, my);
|
|
|
|
ev.u.u.detail = 0;
|
|
ev.u.u.sequenceNumber = 0;
|
|
ev.u.keyButtonPointer.time = Sys_DoubleTime()*1000;
|
|
ev.u.keyButtonPointer.root = rootwindow->res.id;
|
|
ev.u.keyButtonPointer.child = None;
|
|
ev.u.keyButtonPointer.rootX = mx;
|
|
ev.u.keyButtonPointer.rootY = my;
|
|
ev.u.keyButtonPointer.state = mousestate;
|
|
if (cursorowner->res.id != x_windowwithcursor) //changed window
|
|
{
|
|
xwindow_t *a,*b;
|
|
int d1,d2;
|
|
|
|
if (XS_GetResource(x_windowwithcursor, (void**)&wnd) != x_window)
|
|
wnd = rootwindow;
|
|
|
|
x_windowwithcursor = cursorowner->res.id;
|
|
|
|
//count how deep the windows are
|
|
for (a = wnd,d1=0; a; a = a->parent)
|
|
d1++;
|
|
for (b = cursorowner,d2=0; b; b = b->parent)
|
|
d2++;
|
|
|
|
a = wnd;
|
|
b = cursorowner;
|
|
|
|
if (d1>d2)
|
|
{
|
|
while(d1>d2) //a is too deep
|
|
{
|
|
a = a->parent;
|
|
d1--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while(d2>d1)
|
|
{
|
|
b = b->parent;
|
|
d2--;
|
|
}
|
|
}
|
|
while(a != b) //find the common ancestor.
|
|
{
|
|
a = a->parent;
|
|
b = b->parent;
|
|
}
|
|
|
|
ev.u.enterLeave.mode = movemode;
|
|
ev.u.enterLeave.flags = ELFlagSameScreen; /* sameScreen and focus booleans, packed together */
|
|
|
|
//the cursor moved from a to b via:
|
|
// if (!a) //changed screen...
|
|
// {
|
|
// } else
|
|
if (a != wnd && b != cursorowner)
|
|
{ //changed via a common root, indirectly.
|
|
|
|
//o LeaveNotify with detail Nonlinear is generated on A.
|
|
|
|
ev.u.u.type = LeaveNotify;
|
|
ev.u.u.detail = NotifyNonlinear;
|
|
ev.u.keyButtonPointer.child = wnd->res.id;
|
|
X_SendInputNotification(&ev, wnd, LeaveWindowMask);
|
|
|
|
//o LeaveNotify with detail NonlinearVirtual is generated
|
|
// on each window between A and C exclusive (in that
|
|
// order).
|
|
|
|
for (a = wnd->parent; a != b; a = a->parent)
|
|
{
|
|
ev.u.u.type = LeaveNotify;
|
|
ev.u.u.detail = NotifyNonlinearVirtual;
|
|
ev.u.keyButtonPointer.child = a->res.id;
|
|
X_SendInputNotification(&ev, a, LeaveWindowMask);
|
|
}
|
|
|
|
//o EnterNotify with detail NonlinearVirtual is generated
|
|
// on each window between C and B exclusive (in that
|
|
// order).
|
|
|
|
for (; b != cursorowner; )
|
|
{
|
|
for (a = cursorowner; ; a = a->parent) //we need to go through the children.
|
|
{
|
|
if (a->parent == b)
|
|
{
|
|
b = a;
|
|
break;
|
|
}
|
|
}
|
|
if (b == cursorowner)
|
|
break;
|
|
|
|
ev.u.u.type = EnterNotify;
|
|
ev.u.u.detail = NotifyNonlinearVirtual;
|
|
ev.u.keyButtonPointer.child = a->res.id;
|
|
X_SendInputNotification(&ev, a, EnterWindowMask);
|
|
}
|
|
|
|
//o EnterNotify with detail Nonlinear is generated on B.
|
|
|
|
ev.u.u.type = EnterNotify;
|
|
ev.u.u.detail = NotifyNonlinear;
|
|
ev.u.keyButtonPointer.child = cursorowner->res.id;
|
|
X_SendInputNotification(&ev, cursorowner, EnterWindowMask);
|
|
}
|
|
else if (a == wnd)
|
|
{ //b is a child of a
|
|
|
|
//o LeaveNotify with detail Inferior is generated on A.
|
|
|
|
ev.u.u.type = LeaveNotify;
|
|
ev.u.u.detail = NotifyInferior;
|
|
ev.u.keyButtonPointer.child = wnd->res.id;
|
|
X_SendInputNotification(&ev, wnd, LeaveWindowMask);
|
|
|
|
//o EnterNotify with detail Virtual is generated on each
|
|
// window between A and B exclusive (in that order).
|
|
|
|
if (wnd != cursorowner)
|
|
for (b = wnd; ; )
|
|
{
|
|
for (a = cursorowner; ; a = a->parent) //we need to go through the children.
|
|
{
|
|
if (a->parent == b)
|
|
{
|
|
b = a;
|
|
break;
|
|
}
|
|
}
|
|
if (b == cursorowner)
|
|
break;
|
|
|
|
ev.u.u.type = EnterNotify;
|
|
ev.u.u.detail = NotifyVirtual;
|
|
ev.u.keyButtonPointer.child = b->res.id;
|
|
X_SendInputNotification(&ev, b, EnterWindowMask);
|
|
}
|
|
|
|
//o EnterNotify with detail Ancestor is generated on B.
|
|
|
|
ev.u.u.type = EnterNotify;
|
|
ev.u.u.detail = NotifyAncestor;
|
|
ev.u.keyButtonPointer.child = cursorowner->res.id;
|
|
X_SendInputNotification(&ev, cursorowner, EnterWindowMask);
|
|
}
|
|
else// if (b == cursorowner)
|
|
{ //a is a child of b
|
|
|
|
//o LeaveNotify with detail Ancestor is generated on A.
|
|
|
|
ev.u.u.type = LeaveNotify;
|
|
ev.u.u.detail = NotifyAncestor;
|
|
ev.u.keyButtonPointer.child = wnd->res.id;
|
|
X_SendInputNotification(&ev, wnd, LeaveWindowMask);
|
|
|
|
//o LeaveNotify with detail Virtual is generated on each
|
|
// window between A and B exclusive (in that order).
|
|
|
|
for (b = wnd; ; )
|
|
{
|
|
b = b->parent;
|
|
if (b == cursorowner)
|
|
break;
|
|
|
|
ev.u.u.type = LeaveNotify;
|
|
ev.u.u.detail = NotifyVirtual;
|
|
ev.u.keyButtonPointer.child = b->res.id;
|
|
X_SendInputNotification(&ev, b, LeaveWindowMask);
|
|
}
|
|
|
|
|
|
//o EnterNotify with detail Inferior is generated on B.
|
|
|
|
ev.u.u.type = EnterNotify;
|
|
ev.u.u.detail = NotifyInferior;
|
|
ev.u.keyButtonPointer.child = cursorowner->res.id;
|
|
X_SendInputNotification(&ev, cursorowner, EnterWindowMask);
|
|
}
|
|
|
|
{
|
|
char title[1024];
|
|
Atom type;
|
|
int extrabytes;
|
|
int format;
|
|
while(cursorowner)
|
|
{
|
|
title[XS_GetProperty(cursorowner, 39, &type, title, sizeof(title), 0, &extrabytes, &format)] = '\0';
|
|
if (*title)
|
|
break;
|
|
cursorowner = cursorowner->parent;
|
|
}
|
|
Con_Printf("Entered \"%s\"\n", title);
|
|
}
|
|
}
|
|
|
|
{ //same window
|
|
ev.u.keyButtonPointer.child = x_windowwithcursor;
|
|
|
|
if (XS_GetResource(x_windowwithcursor, (void**)&wnd) == x_window)
|
|
{ //cursor still in the same child.
|
|
int mask = PointerMotionMask;
|
|
if (mousestate)
|
|
mask |= ButtonMotionMask;
|
|
if (mousestate & Button1Mask)
|
|
mask |= Button1MotionMask;
|
|
if (mousestate & Button2Mask)
|
|
mask |= Button2MotionMask;
|
|
if (mousestate & Button3Mask)
|
|
mask |= Button3MotionMask;
|
|
if (mousestate & Button4Mask)
|
|
mask |= Button4MotionMask;
|
|
if (mousestate & Button5Mask)
|
|
mask |= Button5MotionMask;
|
|
ev.u.u.type = MotionNotify;
|
|
X_SendInputNotification(&ev, wnd, mask);
|
|
}
|
|
}
|
|
|
|
x_mousex = mx;
|
|
x_mousey = my;
|
|
}
|
|
}
|
|
|
|
void X_EvalutateFocus(int movemode)
|
|
{
|
|
xEvent ev;
|
|
xwindow_t *fo, *po, *wnd;
|
|
|
|
if (XS_GetResource(x_windowwithcursor, (void**)&po) != x_window)
|
|
po = rootwindow;
|
|
|
|
// xfocusedwindow = NULL;
|
|
|
|
|
|
if (!xfocusedwindow)
|
|
{
|
|
if (XS_GetResource(x_windowwithcursor, (void**)&fo) != x_window)
|
|
fo = rootwindow;
|
|
}
|
|
else
|
|
{
|
|
fo = xfocusedwindow;
|
|
}
|
|
|
|
if (x_windowwithfocus != fo->res.id)
|
|
{
|
|
ev.u.u.detail = 0;
|
|
ev.u.u.sequenceNumber = 0;
|
|
ev.u.focus.mode = movemode;
|
|
{
|
|
xwindow_t *a,*b;
|
|
int d1,d2;
|
|
|
|
if (XS_GetResource(x_windowwithfocus, (void**)&wnd) != x_window)
|
|
wnd = rootwindow;
|
|
|
|
x_windowwithfocus = fo->res.id;
|
|
|
|
//count how deep the windows are
|
|
for (a = wnd,d1=0; a; a = a->parent)
|
|
d1++;
|
|
for (b = fo,d2=0; b; b = b->parent)
|
|
d2++;
|
|
|
|
a = wnd;
|
|
b = fo;
|
|
|
|
if (d1>d2)
|
|
{
|
|
while(d1>d2) //a is too deep
|
|
{
|
|
a = a->parent;
|
|
d1--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while(d2>d1)
|
|
{
|
|
b = b->parent;
|
|
d2--;
|
|
}
|
|
}
|
|
while(a != b) //find the common ancestor.
|
|
{
|
|
a = a->parent;
|
|
b = b->parent;
|
|
}
|
|
|
|
ev.u.enterLeave.mode = movemode;
|
|
ev.u.enterLeave.flags = ELFlagSameScreen; /* sameScreen and focus booleans, packed together */
|
|
|
|
//the cursor moved from a to b via:
|
|
// if (!a) //changed screen...
|
|
// {
|
|
// } else
|
|
if (a != wnd && b != fo)
|
|
{ //changed via a common root, indirectly.
|
|
|
|
//When the focus moves from window A to window B, window C is
|
|
//their least common ancestor, and the pointer is in window P:
|
|
|
|
//o If P is an inferior of A, FocusOut with detail Pointer
|
|
// is generated on each window from P up to but not
|
|
// including A (in order).
|
|
|
|
//FIXME
|
|
|
|
//o FocusOut with detail Nonlinear is generated on A.
|
|
|
|
ev.u.u.type = FocusOut;
|
|
ev.u.u.detail = NotifyNonlinear;
|
|
ev.u.focus.window = wnd->res.id;
|
|
X_SendInputNotification(&ev, wnd, FocusChangeMask);
|
|
|
|
|
|
|
|
|
|
//o FocusOut with detail NonlinearVirtual is generated on
|
|
// each window between A and C exclusive (in order).
|
|
|
|
for (a = wnd->parent; a != b; a = a->parent)
|
|
{
|
|
ev.u.u.type = FocusOut;
|
|
ev.u.u.detail = NotifyNonlinearVirtual;
|
|
ev.u.focus.window = a->res.id;
|
|
X_SendInputNotification(&ev, a, FocusChangeMask);
|
|
}
|
|
|
|
//o FocusIn with detail NonlinearVirtual is generated on
|
|
// each window between C and B exclusive (in order).
|
|
|
|
for (; b != fo; )
|
|
{
|
|
ev.u.u.type = FocusIn;
|
|
ev.u.u.detail = NotifyNonlinearVirtual;
|
|
ev.u.focus.window = a->res.id;
|
|
X_SendInputNotification(&ev, a, FocusChangeMask);
|
|
|
|
for (a = fo; ; a = a->parent) //we need to go through the children.
|
|
{
|
|
if (a->parent == b)
|
|
{
|
|
b = a;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//o FocusIn with detail Nonlinear is generated on B.
|
|
|
|
ev.u.u.type = FocusIn;
|
|
ev.u.u.detail = NotifyNonlinear;
|
|
ev.u.focus.window = fo->res.id;
|
|
X_SendInputNotification(&ev, fo, FocusChangeMask);
|
|
|
|
//o If P is an inferior of B, FocusIn with detail Pointer
|
|
// is generated on each window below B down to and includ-
|
|
// ing P (in order).
|
|
|
|
|
|
//FIXME:
|
|
|
|
}
|
|
else if (a == wnd)
|
|
{ //b is a child of a
|
|
|
|
//When the focus moves from window A to window B, B is an
|
|
//inferior of A, and the pointer is in window P:
|
|
|
|
//o If P is an inferior of A but P is not an inferior of B
|
|
// or an ancestor of B, FocusOut with detail Pointer is
|
|
// generated on each window from P up to but not including
|
|
// A (in order).
|
|
|
|
//FIXME
|
|
|
|
//o FocusOut with detail Inferior is generated on A.
|
|
|
|
ev.u.u.type = FocusOut;
|
|
ev.u.u.detail = NotifyInferior;
|
|
ev.u.focus.window = wnd->res.id;
|
|
X_SendInputNotification(&ev, wnd, FocusChangeMask);
|
|
|
|
//o FocusIn with detail Virtual is generated on each window
|
|
// between A and B exclusive (in order).
|
|
|
|
if (wnd != fo)
|
|
for (b = wnd; ; )
|
|
{
|
|
for (a = fo; ; a = a->parent) //we need to go through the children.
|
|
{
|
|
if (a->parent == b)
|
|
{
|
|
b = a;
|
|
break;
|
|
}
|
|
}
|
|
if (b == fo)
|
|
break;
|
|
|
|
ev.u.u.type = FocusIn;
|
|
ev.u.u.detail = NotifyVirtual;
|
|
ev.u.focus.window = b->res.id;
|
|
X_SendInputNotification(&ev, b, FocusChangeMask);
|
|
}
|
|
|
|
//o FocusIn with detail Ancestor is generated on B.
|
|
|
|
ev.u.u.type = FocusIn;
|
|
ev.u.u.detail = NotifyAncestor;
|
|
ev.u.focus.window = fo->res.id;
|
|
X_SendInputNotification(&ev, fo, FocusChangeMask);
|
|
}
|
|
else// if (b == cursorowner)
|
|
{ //a is a child of b
|
|
|
|
//When the focus moves from window A to window B, A is an
|
|
//inferior of B, and the pointer is in window P:
|
|
|
|
//o FocusOut with detail Ancestor is generated on A.
|
|
|
|
ev.u.u.type = FocusOut;
|
|
ev.u.u.detail = NotifyAncestor;
|
|
ev.u.focus.window = wnd->res.id;
|
|
X_SendInputNotification(&ev, wnd, FocusChangeMask);
|
|
|
|
//o FocusOut with detail Virtual is generated on each win-
|
|
// dow between A and B exclusive (in order).
|
|
|
|
for (b = wnd; ; )
|
|
{
|
|
b = b->parent;
|
|
if (b == fo)
|
|
break;
|
|
|
|
ev.u.u.type = FocusOut;
|
|
ev.u.u.detail = NotifyVirtual;
|
|
ev.u.focus.window = a->res.id;
|
|
X_SendInputNotification(&ev, a, FocusChangeMask);
|
|
}
|
|
|
|
|
|
//o FocusIn with detail Inferior is generated on B.
|
|
|
|
ev.u.u.type = FocusIn;
|
|
ev.u.u.detail = NotifyInferior;
|
|
ev.u.focus.window = fo->res.id;
|
|
X_SendInputNotification(&ev, fo, FocusChangeMask);
|
|
|
|
//o If P is an inferior of B but P is not A or an inferior
|
|
// of A or an ancestor of A, FocusIn with detail Pointer
|
|
// is generated on each window below B down to and includ-
|
|
// ing P (in order).
|
|
|
|
//FIXME: code missing
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void XWindows_Draw(void)
|
|
{
|
|
XS_CheckResourceSentinals();
|
|
{
|
|
X_EvalutateCursorOwner(NotifyNormal);
|
|
}
|
|
|
|
XWindows_TendToClients();
|
|
|
|
/* if (rand()&15 == 15)
|
|
xrefreshed = true;*/
|
|
|
|
// memset(xscreen, 0, xscreenwidth*4*xscreenheight);
|
|
|
|
XWindows_TendToClients();
|
|
|
|
// XW_ExposeWindow(rootwindow, 0, 0, rootwindow->width, rootwindow->height);
|
|
|
|
// XWindows_DrawWindowTree(rootwindow, 0, 0);
|
|
if (xrefreshed)
|
|
{
|
|
XWindows_RefreshWindow(rootwindow);
|
|
xrefreshed = false;
|
|
// Con_Printf("updated screen\n");
|
|
}
|
|
|
|
{
|
|
unsigned int *out = (unsigned int *)xscreen + (x_mousex+(x_mousey*xscreenwidth));
|
|
*out = rand();
|
|
// out[64] = rand();
|
|
}
|
|
|
|
XWindows_TendToClients();
|
|
Media_ShowFrameRGBA_32 (xscreen, xscreenwidth, xscreenheight);
|
|
|
|
Con_DrawNotify();
|
|
|
|
XWindows_TendToClients();
|
|
XS_CheckResourceSentinals();
|
|
}
|
|
|
|
void XWindows_Key(int key)
|
|
{
|
|
extern qboolean keydown[256];
|
|
XS_CheckResourceSentinals();
|
|
|
|
if (key == 'q' || (key == K_BACKSPACE && keydown[K_CTRL] && keydown[K_ALT])) //kill off the server
|
|
{
|
|
m_state = m_none;
|
|
key_dest = key_game;
|
|
|
|
closesocket(xlistensocket);
|
|
xlistensocket = -1;
|
|
}
|
|
|
|
|
|
|
|
{
|
|
xEvent ev;
|
|
xwindow_t *wnd;
|
|
|
|
X_EvalutateCursorOwner(NotifyNormal);
|
|
|
|
X_EvalutateFocus(NotifyNormal);
|
|
|
|
if (key >= K_MOUSE1 && key <= K_MOUSE5)
|
|
{
|
|
ev.u.u.type = ButtonPress;
|
|
switch(key)
|
|
{
|
|
case K_MOUSE1:
|
|
ev.u.u.detail = 1;
|
|
mousestate |= Button1Mask;
|
|
break;
|
|
|
|
case K_MOUSE3:
|
|
ev.u.u.detail = 2;
|
|
mousestate |= Button2Mask;
|
|
break;
|
|
|
|
case K_MOUSE2:
|
|
ev.u.u.detail = 3;
|
|
mousestate |= Button3Mask;
|
|
break;
|
|
|
|
case K_MOUSE4:
|
|
ev.u.u.detail = 4;
|
|
mousestate |= Button4Mask;
|
|
break;
|
|
|
|
default:
|
|
ev.u.u.detail = 5;
|
|
mousestate |= Button5Mask;
|
|
break;
|
|
}
|
|
ev.u.keyButtonPointer.state = mousestate;
|
|
ev.u.keyButtonPointer.child = x_windowwithcursor;
|
|
}
|
|
else
|
|
{
|
|
ev.u.u.type = KeyPress;
|
|
ev.u.u.detail = key;
|
|
ev.u.keyButtonPointer.state = 0;
|
|
ev.u.keyButtonPointer.child = x_windowwithfocus;
|
|
}
|
|
ev.u.u.sequenceNumber = 0;
|
|
ev.u.keyButtonPointer.time = Sys_DoubleTime()*1000;
|
|
ev.u.keyButtonPointer.rootX = x_mousex;
|
|
ev.u.keyButtonPointer.rootY = x_mousey;
|
|
ev.u.keyButtonPointer.sameScreen= true;
|
|
ev.u.keyButtonPointer.pad1 = 0;
|
|
|
|
// Con_Printf("key %i, %i %i\n", key, x_mousex, x_mousey);
|
|
|
|
if (xpointergrabclient)
|
|
{
|
|
ev.u.keyButtonPointer.event = ev.u.keyButtonPointer.child;
|
|
ev.u.keyButtonPointer.eventX = ev.u.keyButtonPointer.rootX;
|
|
ev.u.keyButtonPointer.eventY = ev.u.keyButtonPointer.rootY;
|
|
if (XS_GetResource(x_windowwithcursor, (void**)&wnd) == x_window)
|
|
{
|
|
ev.u.u.sequenceNumber = xpointergrabclient->requestnum;
|
|
while(wnd)
|
|
{
|
|
ev.u.keyButtonPointer.eventX -= wnd->xpos;
|
|
ev.u.keyButtonPointer.eventY -= wnd->ypos;
|
|
wnd = wnd->parent;
|
|
}
|
|
X_SendData(xpointergrabclient, &ev, sizeof(ev));
|
|
}
|
|
}
|
|
else if (XS_GetResource(ev.u.keyButtonPointer.child, (void**)&wnd) == x_window)
|
|
X_SendInputNotification(&ev, wnd, (ev.u.u.type==ButtonPress)?ButtonPressMask:KeyPressMask);
|
|
}
|
|
XS_CheckResourceSentinals();
|
|
}
|
|
void XWindows_Keyup(int key)
|
|
{
|
|
XS_CheckResourceSentinals();
|
|
{
|
|
xEvent ev;
|
|
xwindow_t *wnd;
|
|
|
|
X_EvalutateCursorOwner(NotifyNormal);
|
|
|
|
X_EvalutateFocus(NotifyNormal);
|
|
|
|
if (key >= K_MOUSE1 && key <= K_MOUSE5)
|
|
{
|
|
ev.u.u.type = ButtonRelease;
|
|
ev.u.keyButtonPointer.state = mousestate;
|
|
switch(key)
|
|
{
|
|
case K_MOUSE1:
|
|
ev.u.u.detail = 1;
|
|
mousestate &= ~Button1Mask;
|
|
break;
|
|
|
|
case K_MOUSE3:
|
|
ev.u.u.detail = 2;
|
|
mousestate &= ~Button2Mask;
|
|
break;
|
|
|
|
case K_MOUSE2:
|
|
ev.u.u.detail = 3;
|
|
mousestate &= ~Button3Mask;
|
|
break;
|
|
|
|
case K_MOUSE4:
|
|
ev.u.u.detail = 4;
|
|
mousestate &= ~Button4Mask;
|
|
break;
|
|
|
|
default:
|
|
ev.u.u.detail = 5;
|
|
mousestate &= ~Button5Mask;
|
|
break;
|
|
}
|
|
ev.u.keyButtonPointer.state = mousestate;
|
|
ev.u.keyButtonPointer.child = x_windowwithcursor;
|
|
}
|
|
else
|
|
{
|
|
ev.u.u.type = KeyRelease;
|
|
ev.u.u.detail = key;
|
|
ev.u.keyButtonPointer.child = x_windowwithfocus;
|
|
}
|
|
ev.u.u.sequenceNumber = 0;
|
|
ev.u.keyButtonPointer.time = Sys_DoubleTime()*1000;
|
|
ev.u.keyButtonPointer.rootX = x_mousex;
|
|
ev.u.keyButtonPointer.rootY = x_mousey;
|
|
ev.u.keyButtonPointer.state = 0;
|
|
ev.u.keyButtonPointer.sameScreen= true;
|
|
ev.u.keyButtonPointer.pad1 = 0;
|
|
|
|
// Con_Printf("keyup %i, %i %i\n", key, x_mousex, x_mousey);
|
|
|
|
if (xpointergrabclient)
|
|
{
|
|
ev.u.keyButtonPointer.event = ev.u.keyButtonPointer.child;
|
|
ev.u.keyButtonPointer.eventX = ev.u.keyButtonPointer.rootX;
|
|
ev.u.keyButtonPointer.eventY = ev.u.keyButtonPointer.rootY;
|
|
if (XS_GetResource(x_windowwithcursor, (void**)&wnd) == x_window)
|
|
{
|
|
ev.u.u.sequenceNumber = xpointergrabclient->requestnum;
|
|
while(wnd)
|
|
{
|
|
ev.u.keyButtonPointer.eventX -= wnd->xpos;
|
|
ev.u.keyButtonPointer.eventY -= wnd->ypos;
|
|
wnd = wnd->parent;
|
|
}
|
|
X_SendData(xpointergrabclient, &ev, sizeof(ev));
|
|
}
|
|
}
|
|
else if (XS_GetResource(ev.u.keyButtonPointer.child, (void**)&wnd) == x_window)
|
|
{
|
|
X_SendInputNotification(&ev, wnd, (ev.u.u.type==ButtonRelease)?ButtonReleaseMask:KeyReleaseMask);
|
|
}
|
|
}
|
|
XS_CheckResourceSentinals();
|
|
}
|
|
|
|
#else
|
|
//quakie functions
|
|
void XWindows_Init(void){}
|
|
void XWindows_Draw(void){}
|
|
void XWindows_Key(int key){}
|
|
void XWindows_Keyup(int key){}
|
|
#endif
|