plan9port/src/libdraw/x11-mouse.c

221 lines
4.8 KiB
C

#include <u.h>
#include "x11-inc.h"
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <cursor.h>
#include <mouse.h>
#include <memdraw.h>
#include "x11-memdraw.h"
int _windowhasfocus = 1;
int _wantfocuschanges;
void
moveto(Mousectl *m, Point pt)
{
_xmoveto(pt);
}
void
closemouse(Mousectl *mc)
{
if(mc == nil)
return;
/* postnote(PNPROC, mc->pid, "kill");
*/
do; while(nbrecv(mc->c, &mc->m) > 0);
close(mc->mfd);
close(mc->cfd);
free(mc->file);
chanfree(mc->c);
chanfree(mc->resizec);
free(mc);
}
int
readmouse(Mousectl *mc)
{
if(mc->display)
flushimage(mc->display, 1);
if(recv(mc->c, &mc->m) < 0){
fprint(2, "readmouse: %r\n");
return -1;
}
return 0;
}
/*
* This is necessary because some X libraries (e.g., on FC3)
* use an inordinate amount of stack space to do _xsetcursor.
* Perhaps instead there should be a generic "run this X routine"
* stack that you send a function and argument to.
*/
static
void
_cursorproc(void *arg)
{
Mousectl *mc;
Cursor *c;
mc = arg;
threadsetname("cursorproc (sigh)");
for(;;){
c = recvp(mc->ccursor);
_xsetcursor(c);
sendp(mc->ccursorwait, nil);
}
}
static
void
_ioproc(void *arg)
{
int fd, one, buttons;
Atom a;
ulong mask;
Mouse m;
Mousectl *mc;
XEvent xevent;
one = 1;
mc = arg;
threadsetname("mouseproc");
memset(&m, 0, sizeof m);
mc->pid = getpid();
mask = MouseMask|ExposureMask|StructureNotifyMask;
XSelectInput(_x.mousecon, _x.drawable, mask);
fd = XConnectionNumber(_x.mousecon);
buttons = 0;
for(;;){
XNextEvent(_x.mousecon, &xevent);
switch(xevent.type){
case Expose:
_xexpose(&xevent, _x.mousecon);
continue;
case DestroyNotify:
if(_xdestroy(&xevent, _x.mousecon)){
/* drain it before sending */
/* apps that care can notice we sent a 0 */
/* otherwise we'll have getwindow send SIGHUP */
nbrecv(mc->resizec, 0);
nbrecv(mc->resizec, 0);
send(mc->resizec, 0);
}
continue;
case ConfigureNotify:
if(_xconfigure(&xevent, _x.mousecon))
nbsend(mc->resizec, &one);
continue;
case SelectionRequest:
_xselect(&xevent, _x.mousecon);
continue;
case ButtonPress:
case ButtonRelease:
case MotionNotify:
/* If the motion notifications are backing up, skip over some. */
if(0 && xevent.type == MotionNotify){
while(XCheckWindowEvent(_x.mousecon, _x.drawable, MouseMask, &xevent)){
if(xevent.type != MotionNotify)
break;
}
}
m.buttons = buttons;
if(_xtoplan9mouse(_x.mousecon, &xevent, &m) < 0)
continue;
buttons = m.buttons;
send(mc->c, &m);
/*
* mc->Mouse is updated after send so it doesn't have wrong value if we block during send.
* This means that programs should receive into mc->Mouse (see readmouse() above) if
* they want full synchrony.
*/
mc->m = m;
break;
case ClientMessage:
if(xevent.xclient.message_type == _x.wmprotos){
a = xevent.xclient.data.l[0];
if(_wantfocuschanges && a == _x.takefocus){
_windowhasfocus = 1;
_x.newscreenr = _x.screenr;
nbsend(mc->resizec, &one);
}else if(_wantfocuschanges && a == _x.losefocus){
_windowhasfocus = 0;
_x.newscreenr = _x.screenr;
nbsend(mc->resizec, &one);
}
}
break;
}
}
}
Mousectl*
initmouse(char *file, Image *i)
{
Mousectl *mc;
mc = mallocz(sizeof(Mousectl), 1);
if(i)
mc->display = i->display;
mc->c = chancreate(sizeof(Mouse), 0);
chansetname(mc->c, "mousec");
mc->resizec = chancreate(sizeof(int), 2);
chansetname(mc->resizec, "resizec");
mc->ccursor = chancreate(sizeof(void*), 0);
chansetname(mc->ccursor, "ccursor");
mc->ccursorwait = chancreate(sizeof(void*), 0);
chansetname(mc->ccursor, "ccursorwait");
proccreate(_ioproc, mc, 256*1024);
proccreate(_cursorproc, mc, 256*1024); /* sigh */
return mc;
}
void
setcursor(Mousectl *mc, Cursor *c)
{
qlock(&mc->cursorlock);
sendp(mc->ccursor, c);
recvp(mc->ccursorwait);
qunlock(&mc->cursorlock);
}
/*
* Send the mouse event back to the window manager.
* So that 9term can tell rio to pop up its button3 menu.
* Note that we're using _x.mousecon in a few places,
* so we have to be sure that the mouse proc isn't using it
* when we call! This is all a bit wonky and should be
* avoided unless you know what you're doing.
*/
void
bouncemouse(Mouse *m)
{
XButtonEvent e;
XWindow dw;
e.type = ButtonPress;
e.state = 0;
e.button = 0;
if(m->buttons&1)
e.button = 1;
else if(m->buttons&2)
e.button = 2;
else if(m->buttons&4)
e.button = 3;
e.same_screen = 1;
XTranslateCoordinates(_x.display, _x.drawable,
DefaultRootWindow(_x.display),
m->xy.x, m->xy.y, &e.x_root, &e.y_root, &dw);
e.root = DefaultRootWindow(_x.mousecon);
e.window = e.root;
e.subwindow = None;
e.x = e.x_root;
e.y = e.y_root;
#undef time
e.time = CurrentTime;
XUngrabPointer(_x.mousecon, m->msec);
XSendEvent(_x.mousecon, e.root, True, ButtonPressMask, (XEvent*)&e);
XFlush(_x.mousecon);
}