/* * 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; }