windowmaker-wl/WINGs/wevent.c

392 lines
10 KiB
C

/*
* wevent.c — WINGs event dispatch layer.
*
* Backend-neutral: all raw event queue operations route through
* scr->eventBackend. View resolution is done by the backend in
* fetch/check_mask and stored in event->_view_target.
*/
#include "wconfig.h"
#include "WINGsP.h"
/*
* Event mask table indexed by WMEventType enum values.
* Maps each event type to the X11-style event mask used by
* WMCreateEventHandler's mask parameter.
*/
static const unsigned long wmeventMasks[] = {
[WME_NONE] = 0,
[WME_KEY_PRESS] = WM_EVMASK_KEY_PRESS,
[WME_KEY_RELEASE] = WM_EVMASK_KEY_RELEASE,
[WME_BUTTON_PRESS] = WM_EVMASK_BUTTON_PRESS,
[WME_BUTTON_RELEASE] = WM_EVMASK_BUTTON_RELEASE,
[WME_MOTION] = WM_EVMASK_POINTER_MOTION | WM_EVMASK_BUTTON_MOTION
| WM_EVMASK_BUTTON1_MOTION | WM_EVMASK_BUTTON2_MOTION
| WM_EVMASK_BUTTON3_MOTION,
[WME_ENTER] = WM_EVMASK_ENTER_WINDOW,
[WME_LEAVE] = WM_EVMASK_LEAVE_WINDOW,
[WME_EXPOSE] = WM_EVMASK_EXPOSURE,
[WME_CONFIGURE] = WM_EVMASK_STRUCTURE_NOTIFY,
[WME_CONFIGURE_REQUEST] = WM_EVMASK_SUBSTRUCTURE_REDIRECT,
[WME_MAP] = WM_EVMASK_STRUCTURE_NOTIFY,
[WME_MAP_REQUEST] = WM_EVMASK_SUBSTRUCTURE_REDIRECT,
[WME_UNMAP] = WM_EVMASK_STRUCTURE_NOTIFY,
[WME_DESTROY] = WM_EVMASK_STRUCTURE_NOTIFY,
[WME_REPARENT] = WM_EVMASK_STRUCTURE_NOTIFY,
[WME_FOCUS_IN] = WM_EVMASK_FOCUS_CHANGE,
[WME_FOCUS_OUT] = WM_EVMASK_FOCUS_CHANGE,
[WME_PROPERTY] = WM_EVMASK_PROPERTY_CHANGE,
[WME_SELECTION] = 0,
[WME_SELECTION_REQUEST] = 0,
[WME_SELECTION_CLEAR] = 0,
[WME_CLIENT_MESSAGE] = ClientMessageMask,
[WME_COLORMAP] = WM_EVMASK_COLORMAP_CHANGE,
[WME_VISIBILITY] = WM_EVMASK_VISIBILITY_CHANGE,
[WME_MAPPING] = 0,
};
#define WME_MASK_TABLE_SIZE (sizeof(wmeventMasks) / sizeof(wmeventMasks[0]))
static inline unsigned long
wmeMaskForType(int type)
{
if (type >= 0 && (unsigned)type < WME_MASK_TABLE_SIZE)
return wmeventMasks[type];
return 0;
}
static WMEventHook *extraEventHandler = NULL;
void WMCreateEventHandler(WMView * view, unsigned long mask, WMEventProc * eventProc, void *clientData)
{
W_EventHandler *hPtr;
WMArrayIterator iter;
WM_ITERATE_ARRAY(view->eventHandlers, hPtr, iter) {
if (hPtr->clientData == clientData && hPtr->proc == eventProc) {
hPtr->eventMask |= mask;
return;
}
}
hPtr = wmalloc(sizeof(W_EventHandler));
hPtr->eventMask = mask;
hPtr->proc = eventProc;
hPtr->clientData = clientData;
WMAddToArray(view->eventHandlers, hPtr);
}
static int matchHandler(const void *item, const void *cdata)
{
const W_EventHandler *h1 = item;
const W_EventHandler *h2 = cdata;
return ((h1->eventMask == h2->eventMask) &&
(h1->proc == h2->proc) &&
(h1->clientData == h2->clientData));
}
void WMDeleteEventHandler(WMView * view, unsigned long mask, WMEventProc * eventProc, void *clientData)
{
W_EventHandler tmp;
tmp.eventMask = mask;
tmp.proc = eventProc;
tmp.clientData = clientData;
WMRemoveFromArrayMatching(view->eventHandlers, matchHandler, (void *)&tmp);
}
static Time getEventTime(WMScreen * screen, WMEvent * event)
{
switch (event->type) {
case WME_BUTTON_PRESS:
case WME_BUTTON_RELEASE:
return event->u.button.time;
case WME_KEY_PRESS:
case WME_KEY_RELEASE:
return event->u.key.time;
case WME_MOTION:
return event->u.motion.time;
case WME_ENTER:
case WME_LEAVE:
return event->u.crossing.time;
case WME_PROPERTY:
return event->u.property.time;
case WME_SELECTION_CLEAR:
case WME_SELECTION_REQUEST:
case WME_SELECTION:
return event->u.selection.time;
default:
return screen->lastEventTime;
}
}
void W_CallDestroyHandlers(W_View * view)
{
WMEvent event;
WMArrayIterator iter;
W_EventHandler *hPtr;
memset(&event, 0, sizeof(event));
event.type = WME_DESTROY;
event.window = (WNativeWindow)view->window;
event.u.lifecycle.window = (WNativeWindow)view->window;
event.u.lifecycle.event = (WNativeWindow)view->window;
WM_ITERATE_ARRAY(view->eventHandlers, hPtr, iter) {
if (hPtr->eventMask & WM_EVMASK_STRUCTURE_NOTIFY) {
(*hPtr->proc) (&event, hPtr->clientData);
}
}
}
void WMSetViewNextResponder(WMView * view, WMView * responder)
{
view->nextResponder = responder;
}
void WMRelayToNextResponder(WMView * view, WMEvent * event)
{
unsigned long mask = wmeMaskForType(event->type);
if (view->nextResponder) {
WMView *next = view->nextResponder;
W_EventHandler *hPtr;
WMArrayIterator iter;
WM_ITERATE_ARRAY(next->eventHandlers, hPtr, iter) {
if ((hPtr->eventMask & mask)) {
(*hPtr->proc) (event, hPtr->clientData);
}
}
}
}
int WMHandleEvent(WMEvent * event)
{
W_EventHandler *hPtr;
W_View *view, *toplevel;
unsigned long mask;
WNativeWindow window;
WMArrayIterator iter;
if (event->type == WME_MAPPING)
return True;
mask = wmeMaskForType(event->type);
window = event->window;
/* differentiate SubstructureNotify with StructureNotify */
if (mask == WM_EVMASK_STRUCTURE_NOTIFY) {
if (event->type == WME_MAP || event->type == WME_UNMAP ||
event->type == WME_DESTROY || event->type == WME_CONFIGURE) {
WNativeWindow ev_win = WNATIVE_NONE;
if (event->type == WME_MAP || event->type == WME_UNMAP ||
event->type == WME_DESTROY)
ev_win = event->u.lifecycle.event;
else if (event->type == WME_CONFIGURE)
ev_win = event->u.configure.event;
if (ev_win != WNATIVE_NONE && ev_win != window) {
mask = WM_EVMASK_SUBSTRUCTURE_NOTIFY;
window = ev_win;
}
}
}
/* Look up the WINGs view for this event via the view backend. */
view = (W_View *)event->_view_target;
if (!view && event->type != WME_MAP_REQUEST && event->type != WME_CONFIGURE_REQUEST) {
extern W_View *W_GetViewForWindow(WNativeWindow id);
view = W_GetViewForWindow((WNativeWindow)window);
}
if (!view) {
if (extraEventHandler)
(extraEventHandler)(event);
return False;
}
if (!view->screen || view->flags.alreadyDead) /* view was destroyed between queue and dispatch */
return True;
view->screen->lastEventTime = getEventTime(view->screen, event);
toplevel = W_TopLevelOfView(view);
if (!toplevel)
toplevel = view;
if (mask & (WM_EVMASK_KEY_PRESS | WM_EVMASK_KEY_RELEASE)) {
W_View *focused = W_FocusedViewOfToplevel(toplevel);
if (focused)
view = focused;
}
/* compress Motion events */
if (event->type == WME_MOTION && !view->flags.dontCompressMotion) {
const W_EventBackend *be = view->screen->eventBackend;
void *ectx = view->screen->eventBackendCtx;
while (be->pending(ectx)) {
WMEvent ev;
be->fetch(ectx, &ev);
if (ev.type == WME_MOTION
&& event->u.motion.window == ev.u.motion.window
&& event->u.motion.subwindow == ev.u.motion.subwindow) {
*event = ev;
} else {
be->put_back(ectx, &ev);
break;
}
}
}
/* compress Expose events */
if (event->type == WME_EXPOSE && !view->flags.dontCompressExpose) {
const W_EventBackend *be = view->screen->eventBackend;
void *ectx = view->screen->eventBackendCtx;
WMEvent tmp;
while (be->check_typed_window(ectx, (WNativeWindow)view->window, WME_EXPOSE, &tmp))
;
}
if (view->screen->modalLoop && toplevel != view->screen->modalView && !toplevel->flags.worksWhenModal) {
if (event->type == WME_KEY_PRESS || event->type == WME_KEY_RELEASE
|| event->type == WME_MOTION || event->type == WME_BUTTON_PRESS
|| event->type == WME_BUTTON_RELEASE || event->type == WME_FOCUS_IN
|| event->type == WME_FOCUS_OUT) {
return True;
}
}
if (event->type == WME_ENTER)
W_BalloonHandleEnterView(view);
else if (event->type == WME_LEAVE)
W_BalloonHandleLeaveView(view);
W_RetainView(toplevel);
WM_ITERATE_ARRAY(view->eventHandlers, hPtr, iter) {
if (mask == 0 || (hPtr->eventMask & mask)) {
(*hPtr->proc) (event, hPtr->clientData);
}
}
if (view->screen->ignoreNextDoubleClick) {
view->screen->ignoreNextDoubleClick = 0;
} else {
if (event->type == WME_BUTTON_PRESS) {
view->screen->lastClickWindow = (WNativeWindow)event->u.button.window;
view->screen->lastClickTime = event->u.button.time;
}
}
{
const W_EventBackend *be = view->screen->eventBackend;
void *ectx = view->screen->eventBackendCtx;
if (be->post_dispatch)
be->post_dispatch(ectx, event, toplevel);
}
W_ReleaseView(toplevel);
return True;
}
int WMIsDoubleClick(WMEvent * event)
{
W_View *view;
if (event->type != WME_BUTTON_PRESS)
return False;
view = (W_View *)event->_view_target;
if (!view)
return False;
if (view->screen->lastClickWindow != (WNativeWindow)event->u.button.window)
return False;
if (event->u.button.time - view->screen->lastClickTime < WINGsConfiguration.doubleClickDelay) {
view->screen->lastClickTime = 0;
view->screen->lastClickWindow = WM_NATIVE_NONE;
view->screen->ignoreNextDoubleClick = 1;
return True;
} else
return False;
}
/* ------------------------------------------------------------------ */
/* Internal: wait for events or timer expiry. */
/* ------------------------------------------------------------------ */
static Bool waitForEvent(WMScreen *scr, unsigned long xeventmask, Bool waitForInput)
{
const W_EventBackend *be = scr->eventBackend;
void *ectx = scr->eventBackendCtx;
be->flush(ectx, False);
if (xeventmask == 0) {
if (be->pending(ectx))
return True;
} else {
WMEvent ev;
if (be->check_mask(ectx, xeventmask, &ev)) {
be->put_back(ectx, &ev);
return True;
}
}
return W_HandleInputEvents(waitForInput, be->fd(ectx));
}
void WMNextEvent(WMScreen * scr, WMEvent * event)
{
const W_EventBackend *be = scr->eventBackend;
void *ectx = scr->eventBackendCtx;
W_CheckTimerHandlers();
while (!be->pending(ectx)) {
while (!waitForEvent(scr, 0, False) && W_CheckIdleHandlers())
W_CheckTimerHandlers();
waitForEvent(scr, 0, True);
W_CheckTimerHandlers();
}
be->fetch(ectx, event);
}
void WMMaskEvent(WMScreen * scr, long mask, WMEvent * event)
{
const W_EventBackend *be = scr->eventBackend;
void *ectx = scr->eventBackendCtx;
W_CheckTimerHandlers();
while (!be->check_mask(ectx, mask, event)) {
while (!waitForEvent(scr, mask, False) && W_CheckIdleHandlers())
W_CheckTimerHandlers();
if (be->check_mask(ectx, mask, event))
return;
waitForEvent(scr, mask, True);
W_CheckTimerHandlers();
}
}
Bool WMScreenPending(WMScreen * scr)
{
return scr->eventBackend->pending(scr->eventBackendCtx);
}
WMEventHook *WMHookEventHandler(WMEventHook * handler)
{
WMEventHook *oldHandler = extraEventHandler;
extraEventHandler = handler;
return oldHandler;
}