mirror of
https://github.com/gryf/wmaker.git
synced 2025-12-30 10:22:34 +01:00
- made the WMRunModalLoop() reentrant (a modal panel can have its own modal panel and so on) by saving the modal related information when entering the modal loop and restoring it after.
1014 lines
23 KiB
C
1014 lines
23 KiB
C
|
|
|
|
/*
|
|
* This event handling stuff was inspired on Tk.
|
|
*/
|
|
|
|
#include "WINGsP.h"
|
|
|
|
#include "../src/config.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#ifdef HAVE_POLL_H
|
|
#include <poll.h>
|
|
#endif
|
|
|
|
|
|
#include <X11/Xos.h>
|
|
|
|
#ifdef HAVE_SYS_SELECT_H
|
|
# include <sys/select.h>
|
|
#endif
|
|
|
|
#include <time.h>
|
|
|
|
#ifndef X_GETTIMEOFDAY
|
|
#define X_GETTIMEOFDAY(t) gettimeofday(t, (struct timezone*)0)
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct TimerHandler {
|
|
WMCallback *callback; /* procedure to call */
|
|
struct timeval when; /* when to call the callback */
|
|
void *clientData;
|
|
struct TimerHandler *next;
|
|
} TimerHandler;
|
|
|
|
|
|
typedef struct IdleHandler {
|
|
WMCallback *callback;
|
|
void *clientData;
|
|
} IdleHandler;
|
|
|
|
|
|
typedef struct InputHandler {
|
|
WMInputProc *callback;
|
|
void *clientData;
|
|
int fd;
|
|
int mask;
|
|
} InputHandler;
|
|
|
|
|
|
/* table to map event types to event masks */
|
|
static unsigned long eventMasks[] = {
|
|
0,
|
|
0,
|
|
KeyPressMask, /* KeyPress */
|
|
KeyReleaseMask, /* KeyRelease */
|
|
ButtonPressMask, /* ButtonPress */
|
|
ButtonReleaseMask, /* ButtonRelease */
|
|
PointerMotionMask|PointerMotionHintMask|ButtonMotionMask
|
|
|Button1MotionMask|Button2MotionMask|Button3MotionMask
|
|
|Button4MotionMask|Button5MotionMask,
|
|
/* MotionNotify */
|
|
EnterWindowMask, /* EnterNotify */
|
|
LeaveWindowMask, /* LeaveNotify */
|
|
FocusChangeMask, /* FocusIn */
|
|
FocusChangeMask, /* FocusOut */
|
|
KeymapStateMask, /* KeymapNotify */
|
|
ExposureMask, /* Expose */
|
|
ExposureMask, /* GraphicsExpose */
|
|
ExposureMask, /* NoExpose */
|
|
VisibilityChangeMask, /* VisibilityNotify */
|
|
SubstructureNotifyMask, /* CreateNotify */
|
|
StructureNotifyMask, /* DestroyNotify */
|
|
StructureNotifyMask, /* UnmapNotify */
|
|
StructureNotifyMask, /* MapNotify */
|
|
SubstructureRedirectMask, /* MapRequest */
|
|
StructureNotifyMask, /* ReparentNotify */
|
|
StructureNotifyMask, /* ConfigureNotify */
|
|
SubstructureRedirectMask, /* ConfigureRequest */
|
|
StructureNotifyMask, /* GravityNotify */
|
|
ResizeRedirectMask, /* ResizeRequest */
|
|
StructureNotifyMask, /* CirculateNotify */
|
|
SubstructureRedirectMask, /* CirculateRequest */
|
|
PropertyChangeMask, /* PropertyNotify */
|
|
0, /* SelectionClear */
|
|
0, /* SelectionRequest */
|
|
0, /* SelectionNotify */
|
|
ColormapChangeMask, /* ColormapNotify */
|
|
ClientMessageMask, /* ClientMessage */
|
|
0, /* Mapping Notify */
|
|
};
|
|
|
|
|
|
|
|
/* queue of timer event handlers */
|
|
static TimerHandler *timerHandler=NULL;
|
|
|
|
static WMBag *idleHandler=NULL;
|
|
|
|
static WMBag *inputHandler=NULL;
|
|
|
|
/* hook for other toolkits or wmaker process their events */
|
|
static WMEventHook *extraEventHandler=NULL;
|
|
|
|
|
|
|
|
#define timerPending() (timerHandler)
|
|
|
|
|
|
|
|
static void
|
|
rightNow(struct timeval *tv) {
|
|
X_GETTIMEOFDAY(tv);
|
|
}
|
|
|
|
/* is t1 after t2 ? */
|
|
#define IS_AFTER(t1, t2) (((t1).tv_sec > (t2).tv_sec) || \
|
|
(((t1).tv_sec == (t2).tv_sec) \
|
|
&& ((t1).tv_usec > (t2).tv_usec)))
|
|
|
|
|
|
static void
|
|
addmillisecs(struct timeval *tv, int milliseconds)
|
|
{
|
|
tv->tv_usec += milliseconds*1000;
|
|
|
|
tv->tv_sec += tv->tv_usec/1000000;
|
|
tv->tv_usec = tv->tv_usec%1000000;
|
|
}
|
|
|
|
|
|
WMHandlerID
|
|
WMAddTimerHandler(int milliseconds, WMCallback *callback, void *cdata)
|
|
{
|
|
TimerHandler *handler, *tmp;
|
|
|
|
handler = malloc(sizeof(TimerHandler));
|
|
if (!handler)
|
|
return NULL;
|
|
|
|
rightNow(&handler->when);
|
|
addmillisecs(&handler->when, milliseconds);
|
|
handler->callback = callback;
|
|
handler->clientData = cdata;
|
|
/* insert callback in queue, sorted by time left */
|
|
if (!timerHandler || !IS_AFTER(handler->when, timerHandler->when)) {
|
|
/* first in the queue */
|
|
handler->next = timerHandler;
|
|
timerHandler = handler;
|
|
} else {
|
|
tmp = timerHandler;
|
|
while (tmp->next && IS_AFTER(handler->when, tmp->next->when)) {
|
|
tmp = tmp->next;
|
|
}
|
|
handler->next = tmp->next;
|
|
tmp->next = handler;
|
|
}
|
|
return handler;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
WMDeleteTimerWithClientData(void *cdata)
|
|
{
|
|
TimerHandler *handler, *tmp;
|
|
|
|
if (!cdata || !timerHandler)
|
|
return;
|
|
|
|
tmp = timerHandler;
|
|
if (tmp->clientData==cdata) {
|
|
timerHandler = tmp->next;
|
|
wfree(tmp);
|
|
} else {
|
|
while (tmp->next) {
|
|
if (tmp->next->clientData==cdata) {
|
|
handler = tmp->next;
|
|
tmp->next = handler->next;
|
|
wfree(handler);
|
|
break;
|
|
}
|
|
tmp = tmp->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void
|
|
WMDeleteTimerHandler(WMHandlerID handlerID)
|
|
{
|
|
TimerHandler *tmp, *handler=(TimerHandler*)handlerID;
|
|
|
|
if (!handler || !timerHandler)
|
|
return;
|
|
|
|
tmp = timerHandler;
|
|
if (tmp==handler) {
|
|
timerHandler = handler->next;
|
|
wfree(handler);
|
|
} else {
|
|
while (tmp->next) {
|
|
if (tmp->next==handler) {
|
|
tmp->next=handler->next;
|
|
wfree(handler);
|
|
break;
|
|
}
|
|
tmp = tmp->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
WMHandlerID
|
|
WMAddIdleHandler(WMCallback *callback, void *cdata)
|
|
{
|
|
IdleHandler *handler;
|
|
|
|
handler = malloc(sizeof(IdleHandler));
|
|
if (!handler)
|
|
return NULL;
|
|
|
|
handler->callback = callback;
|
|
handler->clientData = cdata;
|
|
/* add handler at end of queue */
|
|
if (!idleHandler) {
|
|
idleHandler = WMCreateBag(16);
|
|
}
|
|
WMPutInBag(idleHandler, handler);
|
|
|
|
return handler;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
WMDeleteIdleHandler(WMHandlerID handlerID)
|
|
{
|
|
IdleHandler *handler = (IdleHandler*)handlerID;
|
|
int pos;
|
|
|
|
if (!handler || !idleHandler)
|
|
return;
|
|
|
|
pos = WMGetFirstInBag(idleHandler, handler);
|
|
if (pos != WBNotFound) {
|
|
wfree(handler);
|
|
WMDeleteFromBag(idleHandler, pos);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
WMHandlerID
|
|
WMAddInputHandler(int fd, int condition, WMInputProc *proc, void *clientData)
|
|
{
|
|
InputHandler *handler;
|
|
|
|
handler = wmalloc(sizeof(InputHandler));
|
|
|
|
handler->fd = fd;
|
|
handler->mask = condition;
|
|
handler->callback = proc;
|
|
handler->clientData = clientData;
|
|
|
|
if (!inputHandler)
|
|
inputHandler = WMCreateBag(16);
|
|
WMPutInBag(inputHandler, handler);
|
|
|
|
return handler;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
WMDeleteInputHandler(WMHandlerID handlerID)
|
|
{
|
|
InputHandler *handler = (InputHandler*)handlerID;
|
|
int pos;
|
|
|
|
if (!handler || !inputHandler)
|
|
return;
|
|
|
|
pos = WMGetFirstInBag(inputHandler, handler);
|
|
if (pos != WBNotFound) {
|
|
wfree(handler);
|
|
WMDeleteFromBag(inputHandler, pos);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static Bool
|
|
checkIdleHandlers()
|
|
{
|
|
IdleHandler *handler;
|
|
WMBag *handlerCopy;
|
|
WMBagIterator iter;
|
|
|
|
if (!idleHandler || WMGetBagItemCount(idleHandler)==0) {
|
|
W_FlushIdleNotificationQueue();
|
|
/* make sure an observer in queue didn't added an idle handler */
|
|
return (idleHandler!=NULL && WMGetBagItemCount(idleHandler)>0);
|
|
}
|
|
|
|
handlerCopy = WMCreateBag(WMGetBagItemCount(idleHandler));
|
|
WMAppendBag(handlerCopy, idleHandler);
|
|
|
|
for (handler = WMBagFirst(handlerCopy, &iter);
|
|
iter != NULL;
|
|
handler = WMBagNext(handlerCopy, &iter)) {
|
|
/* check if the handler still exist or was removed by a callback */
|
|
if (WMGetFirstInBag(idleHandler, handler) == WBNotFound)
|
|
continue;
|
|
|
|
(*handler->callback)(handler->clientData);
|
|
WMDeleteIdleHandler(handler);
|
|
}
|
|
|
|
WMFreeBag(handlerCopy);
|
|
|
|
W_FlushIdleNotificationQueue();
|
|
|
|
/* this is not necesarrily False, because one handler can re-add itself */
|
|
return (WMGetBagItemCount(idleHandler)>0);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
checkTimerHandlers()
|
|
{
|
|
TimerHandler *handler;
|
|
struct timeval now;
|
|
|
|
if (!timerHandler) {
|
|
W_FlushASAPNotificationQueue();
|
|
return;
|
|
}
|
|
|
|
rightNow(&now);
|
|
|
|
while (timerHandler && IS_AFTER(now, timerHandler->when)) {
|
|
handler = timerHandler;
|
|
timerHandler = timerHandler->next;
|
|
handler->next = NULL;
|
|
(*handler->callback)(handler->clientData);
|
|
wfree(handler);
|
|
}
|
|
|
|
W_FlushASAPNotificationQueue();
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
delayUntilNextTimerEvent(struct timeval *delay)
|
|
{
|
|
struct timeval now;
|
|
|
|
if (!timerHandler) {
|
|
/* The return value of this function is only valid if there _are_
|
|
timers active. */
|
|
delay->tv_sec = 0;
|
|
delay->tv_usec = 0;
|
|
return;
|
|
}
|
|
|
|
rightNow(&now);
|
|
if (IS_AFTER(now, timerHandler->when)) {
|
|
delay->tv_sec = 0;
|
|
delay->tv_usec = 0;
|
|
} else {
|
|
delay->tv_sec = timerHandler->when.tv_sec - now.tv_sec;
|
|
delay->tv_usec = timerHandler->when.tv_usec - now.tv_usec;
|
|
if (delay->tv_usec < 0) {
|
|
delay->tv_usec += 1000000;
|
|
delay->tv_sec--;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* WMCreateEventHandler--
|
|
* Create an event handler and put it in the event handler list for the
|
|
* view. If the same callback and clientdata are already used in another
|
|
* handler, the masks are swapped.
|
|
*
|
|
*/
|
|
void
|
|
WMCreateEventHandler(WMView *view, unsigned long mask, WMEventProc *eventProc,
|
|
void *clientData)
|
|
{
|
|
W_EventHandler *handler, *ptr;
|
|
unsigned long eventMask;
|
|
WMBagIterator iter;
|
|
|
|
|
|
handler = NULL;
|
|
eventMask = mask;
|
|
|
|
WM_ITERATE_BAG(view->eventHandlers, ptr, iter) {
|
|
if (ptr->clientData == clientData && ptr->proc == eventProc) {
|
|
handler = ptr;
|
|
eventMask |= ptr->eventMask;
|
|
}
|
|
}
|
|
if (!handler) {
|
|
handler = wmalloc(sizeof(W_EventHandler));
|
|
|
|
WMPutInBag(view->eventHandlers, handler);
|
|
}
|
|
/* select events for window */
|
|
handler->eventMask = eventMask;
|
|
handler->proc = eventProc;
|
|
handler->clientData = clientData;
|
|
}
|
|
|
|
|
|
/*
|
|
* WMDeleteEventHandler--
|
|
* Delete event handler matching arguments from windows
|
|
* event handler list.
|
|
*
|
|
*/
|
|
void
|
|
WMDeleteEventHandler(WMView *view, unsigned long mask, WMEventProc *eventProc,
|
|
void *clientData)
|
|
{
|
|
W_EventHandler *handler, *ptr;
|
|
WMBagIterator iter;
|
|
|
|
handler = NULL;
|
|
|
|
WM_ITERATE_BAG(view->eventHandlers, ptr, iter) {
|
|
if (ptr->eventMask == mask && ptr->proc == eventProc
|
|
&& ptr->clientData == clientData) {
|
|
handler = ptr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!handler)
|
|
return;
|
|
|
|
WMRemoveFromBag(view->eventHandlers, handler);
|
|
|
|
wfree(handler);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
W_CleanUpEvents(WMView *view)
|
|
{
|
|
W_EventHandler *ptr;
|
|
WMBagIterator iter;
|
|
|
|
WM_ITERATE_BAG(view->eventHandlers, ptr, iter) {
|
|
wfree(ptr);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static Time
|
|
getEventTime(WMScreen *screen, XEvent *event)
|
|
{
|
|
switch (event->type) {
|
|
case ButtonPress:
|
|
case ButtonRelease:
|
|
return event->xbutton.time;
|
|
case KeyPress:
|
|
case KeyRelease:
|
|
return event->xkey.time;
|
|
case MotionNotify:
|
|
return event->xmotion.time;
|
|
case EnterNotify:
|
|
case LeaveNotify:
|
|
return event->xcrossing.time;
|
|
case PropertyNotify:
|
|
return event->xproperty.time;
|
|
case SelectionClear:
|
|
return event->xselectionclear.time;
|
|
case SelectionRequest:
|
|
return event->xselectionrequest.time;
|
|
case SelectionNotify:
|
|
return event->xselection.time;
|
|
default:
|
|
return screen->lastEventTime;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
W_CallDestroyHandlers(W_View *view)
|
|
{
|
|
XEvent event;
|
|
WMBagIterator iter;
|
|
W_EventHandler *hPtr;
|
|
|
|
event.type = DestroyNotify;
|
|
event.xdestroywindow.window = view->window;
|
|
event.xdestroywindow.event = view->window;
|
|
|
|
WM_ITERATE_BAG(view->eventHandlers, hPtr, iter) {
|
|
if (hPtr->eventMask & StructureNotifyMask) {
|
|
(*hPtr->proc)(&event, hPtr->clientData);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
WMHandleEvent(XEvent *event)
|
|
{
|
|
W_EventHandler *hPtr;
|
|
W_View *view, *vPtr, *toplevel;
|
|
unsigned long mask;
|
|
Window window;
|
|
WMBagIterator iter;
|
|
|
|
if (event->type == MappingNotify) {
|
|
XRefreshKeyboardMapping(&event->xmapping);
|
|
return True;
|
|
}
|
|
|
|
mask = eventMasks[event->xany.type];
|
|
|
|
window = event->xany.window;
|
|
|
|
/* diferentiate SubstructureNotify with StructureNotify */
|
|
if (mask == StructureNotifyMask) {
|
|
if (event->xmap.event != event->xmap.window) {
|
|
mask = SubstructureNotifyMask;
|
|
window = event->xmap.event;
|
|
}
|
|
}
|
|
view = W_GetViewForXWindow(event->xany.display, window);
|
|
|
|
if (!view) {
|
|
if (extraEventHandler)
|
|
(extraEventHandler)(event);
|
|
|
|
return False;
|
|
}
|
|
|
|
view->screen->lastEventTime = getEventTime(view->screen, event);
|
|
|
|
toplevel = W_TopLevelOfView(view);
|
|
|
|
if (event->type == SelectionNotify || event->type == SelectionClear
|
|
|| event->type == SelectionRequest) {
|
|
/* handle selection related events */
|
|
W_HandleSelectionEvent(event);
|
|
|
|
} else if (event->type == ClientMessage) {
|
|
|
|
W_HandleDNDClientMessage(toplevel, &event->xclient);
|
|
}
|
|
|
|
/* if it's a key event, redispatch it to the focused control */
|
|
if (mask & (KeyPressMask|KeyReleaseMask)) {
|
|
W_View *focused = W_FocusedViewOfToplevel(toplevel);
|
|
|
|
if (focused) {
|
|
view = focused;
|
|
}
|
|
}
|
|
|
|
/* compress Motion events */
|
|
if (event->type == MotionNotify && !view->flags.dontCompressMotion) {
|
|
while (XPending(event->xmotion.display)) {
|
|
XEvent ev;
|
|
XPeekEvent(event->xmotion.display, &ev);
|
|
if (ev.type == MotionNotify
|
|
&& event->xmotion.window == ev.xmotion.window
|
|
&& event->xmotion.subwindow == ev.xmotion.subwindow) {
|
|
/* replace events */
|
|
XNextEvent(event->xmotion.display, event);
|
|
} else break;
|
|
}
|
|
}
|
|
|
|
/* compress expose events */
|
|
if (event->type == Expose && !view->flags.dontCompressExpose) {
|
|
while (XCheckTypedWindowEvent(event->xexpose.display, view->window,
|
|
Expose, event));
|
|
}
|
|
|
|
|
|
if (view->screen->modalLoop && toplevel!=view->screen->modalView
|
|
&& !toplevel->flags.worksWhenModal) {
|
|
if (event->type == KeyPress || event->type == KeyRelease
|
|
|| event->type == MotionNotify || event->type == ButtonPress
|
|
|| event->type == ButtonRelease
|
|
|| event->type == FocusIn || event->type == FocusOut) {
|
|
return True;
|
|
}
|
|
}
|
|
|
|
/* do balloon stuffs */
|
|
if (event->type == EnterNotify)
|
|
W_BalloonHandleEnterView(view);
|
|
else if (event->type == LeaveNotify)
|
|
W_BalloonHandleLeaveView(view);
|
|
|
|
/* This is a hack. It will make the panel be secure while
|
|
* the event handlers are handled, as some event handler
|
|
* might destroy the widget. */
|
|
W_RetainView(toplevel);
|
|
|
|
WM_ITERATE_BAG(view->eventHandlers, hPtr, iter) {
|
|
if ((hPtr->eventMask & mask)) {
|
|
(*hPtr->proc)(event, hPtr->clientData);
|
|
}
|
|
}
|
|
|
|
/* pass the event to the top level window of the widget */
|
|
/* TODO: change this to a responder chain */
|
|
if (view->parent != NULL) {
|
|
vPtr = view;
|
|
while (vPtr->parent != NULL)
|
|
vPtr = vPtr->parent;
|
|
|
|
WM_ITERATE_BAG(vPtr->eventHandlers, hPtr, iter) {
|
|
if (hPtr->eventMask & mask) {
|
|
(*hPtr->proc)(event, hPtr->clientData);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* save button click info to track double-clicks */
|
|
if (view->screen->ignoreNextDoubleClick) {
|
|
view->screen->ignoreNextDoubleClick = 0;
|
|
} else {
|
|
if (event->type == ButtonPress) {
|
|
view->screen->lastClickWindow = event->xbutton.window;
|
|
view->screen->lastClickTime = event->xbutton.time;
|
|
}
|
|
}
|
|
|
|
W_ReleaseView(toplevel);
|
|
|
|
return True;
|
|
}
|
|
|
|
|
|
int
|
|
WMIsDoubleClick(XEvent *event)
|
|
{
|
|
W_View *view;
|
|
|
|
if (event->type != ButtonPress)
|
|
return False;
|
|
|
|
view = W_GetViewForXWindow(event->xany.display, event->xbutton.window);
|
|
|
|
if (!view)
|
|
return False;
|
|
|
|
if (view->screen->lastClickWindow != event->xbutton.window)
|
|
return False;
|
|
|
|
if (event->xbutton.time - view->screen->lastClickTime
|
|
< WINGsConfiguration.doubleClickDelay) {
|
|
view->screen->lastClickTime = 0;
|
|
view->screen->lastClickWindow = None;
|
|
view->screen->ignoreNextDoubleClick = 1;
|
|
return True;
|
|
} else
|
|
return False;
|
|
}
|
|
|
|
|
|
Bool
|
|
W_WaitForEvent(Display *dpy, unsigned long xeventmask)
|
|
{
|
|
#if defined(HAVE_POLL) && defined(HAVE_POLL_H) && !defined(HAVE_SELECT)
|
|
struct pollfd *fds;
|
|
InputHandler *handler;
|
|
int count, timeout, nfds, i, retval;
|
|
|
|
if (inputHandler)
|
|
nfds = WMGetBagItemCount(inputHandler);
|
|
else
|
|
nfds = 0;
|
|
|
|
fds = wmalloc(nfds+1 * sizeof(struct pollfd));
|
|
/* put this to the end of array to avoid using ranges from 1 to nfds+1 */
|
|
fds[nfds].fd = ConnectionNumber(dpy);
|
|
fds[nfds].events = POLLIN;
|
|
|
|
for (i = 0; i<nfds; i++) {
|
|
handler = WMGetFromBag(inputHandler, i);
|
|
fds[i].fd = handler->fd;
|
|
fds[i].events = 0;
|
|
if (handler->mask & WIReadMask)
|
|
fds[i].events |= POLLIN;
|
|
|
|
if (handler->mask & WIWriteMask)
|
|
fds[i].events |= POLLOUT;
|
|
|
|
#if 0 /* FIXME */
|
|
if (handler->mask & WIExceptMask)
|
|
FD_SET(handler->fd, &eset);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Setup the select() timeout to the estimated time until the
|
|
* next timer expires.
|
|
*/
|
|
if (timerPending()) {
|
|
struct timeval tv;
|
|
delayUntilNextTimerEvent(&tv);
|
|
timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
|
} else {
|
|
timeout = -1;
|
|
}
|
|
|
|
if (xeventmask==0) {
|
|
if (XPending(dpy))
|
|
return True;
|
|
} else {
|
|
XEvent ev;
|
|
if (XCheckMaskEvent(dpy, xeventmask, &ev)) {
|
|
XPutBackEvent(dpy, &ev);
|
|
return True;
|
|
}
|
|
}
|
|
|
|
count = poll(fds, nfds, timeout);
|
|
|
|
if (count>0 && nfds>0) {
|
|
WMBag *handlerCopy = WMCreateBag(nfds);
|
|
|
|
for (i=0; i<nfds; i++)
|
|
WMPutInBag(handlerCopy, WMGetFromBag(inputHandler, i));
|
|
|
|
for (i=0; i<nfds; i++) {
|
|
int mask;
|
|
|
|
handler = WMGetFromBag(handlerCopy, i);
|
|
/* check if the handler still exist or was removed by a callback */
|
|
if (WMGetFirstInBag(inputHandler, handler) == WBNotFound)
|
|
continue;
|
|
|
|
mask = 0;
|
|
|
|
if ((handler->mask & WIReadMask) &&
|
|
(fds[i].revents & (POLLIN|POLLRDNORM|POLLRDBAND|POLLPRI)))
|
|
mask |= WIReadMask;
|
|
|
|
if ((handler->mask & WIWriteMask) &&
|
|
(fds[i].revents & (POLLOUT | POLLWRBAND)))
|
|
mask |= WIWriteMask;
|
|
|
|
if ((handler->mask & WIExceptMask) &&
|
|
(fds[i].revents & (POLLHUP | POLLNVAL | POLLERR)))
|
|
mask |= WIExceptMask;
|
|
|
|
if (mask!=0 && handler->callback) {
|
|
(*handler->callback)(handler->fd, mask,
|
|
handler->clientData);
|
|
}
|
|
}
|
|
|
|
WMFreeBag(handlerCopy);
|
|
}
|
|
|
|
retval = fds[nfds].revents & (POLLIN|POLLRDNORM|POLLRDBAND|POLLPRI);
|
|
wfree(fds);
|
|
|
|
W_FlushASAPNotificationQueue();
|
|
|
|
return retval;
|
|
#else /* not HAVE_POLL */
|
|
#ifdef HAVE_SELECT
|
|
struct timeval timeout;
|
|
struct timeval *timeoutPtr;
|
|
fd_set rset, wset, eset;
|
|
int maxfd, nfds, i;
|
|
int count;
|
|
InputHandler *handler;
|
|
|
|
FD_ZERO(&rset);
|
|
FD_ZERO(&wset);
|
|
FD_ZERO(&eset);
|
|
|
|
FD_SET(ConnectionNumber(dpy), &rset);
|
|
maxfd = ConnectionNumber(dpy);
|
|
|
|
if (inputHandler)
|
|
nfds = WMGetBagItemCount(inputHandler);
|
|
else
|
|
nfds = 0;
|
|
|
|
for (i=0; i<nfds; i++) {
|
|
handler = WMGetFromBag(inputHandler, i);
|
|
if (handler->mask & WIReadMask)
|
|
FD_SET(handler->fd, &rset);
|
|
|
|
if (handler->mask & WIWriteMask)
|
|
FD_SET(handler->fd, &wset);
|
|
|
|
if (handler->mask & WIExceptMask)
|
|
FD_SET(handler->fd, &eset);
|
|
|
|
if (maxfd < handler->fd)
|
|
maxfd = handler->fd;
|
|
}
|
|
|
|
|
|
/*
|
|
* Setup the select() timeout to the estimated time until the
|
|
* next timer expires.
|
|
*/
|
|
if (timerPending()) {
|
|
delayUntilNextTimerEvent(&timeout);
|
|
timeoutPtr = &timeout;
|
|
} else {
|
|
timeoutPtr = (struct timeval*)0;
|
|
}
|
|
|
|
XSync(dpy, False);
|
|
if (xeventmask==0) {
|
|
if (XPending(dpy))
|
|
return True;
|
|
} else {
|
|
XEvent ev;
|
|
if (XCheckMaskEvent(dpy, xeventmask, &ev)) {
|
|
XPutBackEvent(dpy, &ev);
|
|
return True;
|
|
}
|
|
}
|
|
|
|
count = select(1 + maxfd, &rset, &wset, &eset, timeoutPtr);
|
|
|
|
if (count>0 && nfds>0) {
|
|
WMBag *handlerCopy = WMCreateBag(nfds);
|
|
|
|
for (i=0; i<nfds; i++)
|
|
WMPutInBag(handlerCopy, WMGetFromBag(inputHandler, i));
|
|
|
|
for (i=0; i<nfds; i++) {
|
|
int mask;
|
|
|
|
handler = WMGetFromBag(handlerCopy, i);
|
|
/* check if the handler still exist or was removed by a callback */
|
|
if (WMGetFirstInBag(inputHandler, handler) == WBNotFound)
|
|
continue;
|
|
|
|
mask = 0;
|
|
|
|
if ((handler->mask & WIReadMask) && FD_ISSET(handler->fd, &rset))
|
|
mask |= WIReadMask;
|
|
|
|
if ((handler->mask & WIWriteMask) && FD_ISSET(handler->fd, &wset))
|
|
mask |= WIWriteMask;
|
|
|
|
if ((handler->mask & WIExceptMask) && FD_ISSET(handler->fd, &eset))
|
|
mask |= WIExceptMask;
|
|
|
|
if (mask!=0 && handler->callback) {
|
|
(*handler->callback)(handler->fd, mask,
|
|
handler->clientData);
|
|
}
|
|
}
|
|
|
|
WMFreeBag(handlerCopy);
|
|
}
|
|
|
|
W_FlushASAPNotificationQueue();
|
|
|
|
return FD_ISSET(ConnectionNumber(dpy), &rset);
|
|
#else /* not HAVE_SELECT, not HAVE_POLL */
|
|
Neither select nor poll. You lose.
|
|
#endif /* HAVE_SELECT */
|
|
#endif /* HAVE_POLL */
|
|
}
|
|
|
|
|
|
void
|
|
WMNextEvent(Display *dpy, XEvent *event)
|
|
{
|
|
/* Check any expired timers */
|
|
checkTimerHandlers();
|
|
|
|
while (XPending(dpy) == 0) {
|
|
/* Do idle stuff */
|
|
/* Do idle and timer stuff while there are no timer or X events */
|
|
while (!XPending(dpy) && checkIdleHandlers()) {
|
|
/* dispatch timer events */
|
|
checkTimerHandlers();
|
|
}
|
|
|
|
/*
|
|
* Make sure that new events did not arrive while we were doing
|
|
* timer/idle stuff. Or we might block forever waiting for
|
|
* an event that already arrived.
|
|
*/
|
|
/* wait to something happen */
|
|
W_WaitForEvent(dpy, 0);
|
|
|
|
/* Check any expired timers */
|
|
checkTimerHandlers();
|
|
}
|
|
|
|
XNextEvent(dpy, event);
|
|
}
|
|
|
|
#if 0
|
|
void
|
|
WMMaskEvent(Display *dpy, long mask, XEvent *event)
|
|
{
|
|
unsigned long milliseconds;
|
|
struct timeval timeout;
|
|
struct timeval *timeoutOrInfty;
|
|
fd_set readset;
|
|
|
|
while (!XCheckMaskEvent(dpy, mask, event)) {
|
|
/* Do idle stuff while there are no timer or X events */
|
|
while (checkIdleHandlers()) {
|
|
if (XCheckMaskEvent(dpy, mask, event))
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Setup the select() timeout to the estimated time until the
|
|
* next timer expires.
|
|
*/
|
|
if (timerPending()) {
|
|
delayUntilNextTimerEvent(&timeout);
|
|
timeoutOrInfty = &timeout;
|
|
} else {
|
|
timeoutOrInfty = (struct timeval*)0;
|
|
}
|
|
|
|
if (XCheckMaskEvent(dpy, mask, event))
|
|
return;
|
|
|
|
/* Wait for input on the X connection socket */
|
|
FD_ZERO(&readset);
|
|
FD_SET(ConnectionNumber(dpy), &readset);
|
|
select(1 + ConnectionNumber(dpy), &readset, (fd_set*)0, (fd_set*)0,
|
|
timeoutOrInfty);
|
|
|
|
/* Check any expired timers */
|
|
checkTimerHandlers();
|
|
}
|
|
}
|
|
#endif
|
|
#if 1
|
|
/*
|
|
* Cant use this because XPending() will make W_WaitForEvent
|
|
* return even if the event in the queue is not what we want,
|
|
* and if we block until some new event arrives from the
|
|
* server, other events already in the queue (like Expose)
|
|
* will be deferred.
|
|
*/
|
|
void
|
|
WMMaskEvent(Display *dpy, long mask, XEvent *event)
|
|
{
|
|
while (!XCheckMaskEvent(dpy, mask, event)) {
|
|
/* Do idle stuff while there are no timer or X events */
|
|
while (checkIdleHandlers()) {
|
|
if (XCheckMaskEvent(dpy, mask, event))
|
|
return;
|
|
}
|
|
|
|
/* Wait for input on the X connection socket */
|
|
W_WaitForEvent(dpy, mask);
|
|
|
|
/* Check any expired timers */
|
|
checkTimerHandlers();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
Bool
|
|
WMScreenPending(WMScreen *scr)
|
|
{
|
|
if (XPending(scr->display))
|
|
return True;
|
|
else
|
|
return False;
|
|
}
|
|
|
|
|
|
WMEventHook*
|
|
WMHookEventHandler(WMEventHook *handler)
|
|
{
|
|
WMEventHook *oldHandler = extraEventHandler;
|
|
|
|
extraEventHandler = handler;
|
|
|
|
return oldHandler;
|
|
}
|
|
|
|
|