392 lines
10 KiB
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;
|
|
}
|