mirror of
https://github.com/gryf/wmaker.git
synced 2025-12-18 20:10:29 +01:00
When a function is used as a call-back, it is dangerous to have arguments with a type different than what is expected by the call-back definition. This patch sets the argument list to match what is expected by the call-back prototype and inserts an explicit type conversion at the beginning of the function.
432 lines
11 KiB
C
432 lines
11 KiB
C
|
|
/*
|
|
* This event handling stuff was inspired on Tk.
|
|
*/
|
|
|
|
#include "WINGsP.h"
|
|
|
|
/* table to map event types to event masks */
|
|
static const 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 */
|
|
};
|
|
|
|
/* hook for other toolkits or wmaker process their events */
|
|
static WMEventHook *extraEventHandler = NULL;
|
|
|
|
/*
|
|
* 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 OR'ed.
|
|
*
|
|
*/
|
|
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));
|
|
|
|
/* select events for window */
|
|
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));
|
|
}
|
|
|
|
/*
|
|
* WMDeleteEventHandler--
|
|
* Delete event handler matching arguments from windows
|
|
* event handler list.
|
|
*
|
|
*/
|
|
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, 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;
|
|
WMArrayIterator iter;
|
|
W_EventHandler *hPtr;
|
|
|
|
event.type = DestroyNotify;
|
|
event.xdestroywindow.window = view->window;
|
|
event.xdestroywindow.event = view->window;
|
|
|
|
WM_ITERATE_ARRAY(view->eventHandlers, hPtr, iter) {
|
|
if (hPtr->eventMask & StructureNotifyMask) {
|
|
(*hPtr->proc) (&event, hPtr->clientData);
|
|
}
|
|
}
|
|
}
|
|
|
|
void WMSetViewNextResponder(WMView * view, WMView * responder)
|
|
{
|
|
/* set the widget to receive keyboard events that aren't handled
|
|
* by this widget */
|
|
|
|
view->nextResponder = responder;
|
|
}
|
|
|
|
void WMRelayToNextResponder(WMView * view, XEvent * event)
|
|
{
|
|
unsigned long mask = eventMasks[event->xany.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(XEvent * event)
|
|
{
|
|
W_EventHandler *hPtr;
|
|
W_View *view, *toplevel;
|
|
unsigned long mask;
|
|
Window window;
|
|
WMArrayIterator iter;
|
|
|
|
if (event->type == MappingNotify) {
|
|
XRefreshKeyboardMapping(&event->xmapping);
|
|
return True;
|
|
}
|
|
|
|
if (XFilterEvent(event, None) == True) {
|
|
return False;
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
/* 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_ARRAY(view->eventHandlers, hPtr, iter) {
|
|
if ((hPtr->eventMask & mask)) {
|
|
(*hPtr->proc) (event, hPtr->clientData);
|
|
}
|
|
}
|
|
#if 0
|
|
/* 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_ARRAY(vPtr->eventHandlers, hPtr, iter) {
|
|
if (hPtr->eventMask & mask) {
|
|
(*hPtr->proc) (event, hPtr->clientData);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
/* 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;
|
|
}
|
|
}
|
|
|
|
if (event->type == ClientMessage) {
|
|
/* must be handled at the end, for such message can destroy the view */
|
|
W_HandleDNDClientMessage(toplevel, &event->xclient);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* Check for X and input events. If X events are present input events will
|
|
* not be checked.
|
|
*
|
|
* Return value: True if a X event is available or any input event was
|
|
* processed, false otherwise (including return because of
|
|
* some timer handler expired).
|
|
*
|
|
* If waitForInput is False, it will just peek for available input and return
|
|
* without processing. Return vaue will be True if input is available.
|
|
*
|
|
* If waitForInput is True, it will wait until an input event arrives on the
|
|
* registered input handlers and ConnectionNumber(dpy), or will return when
|
|
* a timer handler expires if no input event arrived until then.
|
|
*/
|
|
static Bool waitForEvent(Display * dpy, unsigned long xeventmask, Bool waitForInput)
|
|
{
|
|
XSync(dpy, False);
|
|
if (xeventmask == 0) {
|
|
if (XPending(dpy))
|
|
return True;
|
|
} else {
|
|
XEvent ev;
|
|
if (XCheckMaskEvent(dpy, xeventmask, &ev)) {
|
|
XPutBackEvent(dpy, &ev);
|
|
return True;
|
|
}
|
|
}
|
|
|
|
return W_HandleInputEvents(waitForInput, ConnectionNumber(dpy));
|
|
}
|
|
|
|
void WMNextEvent(Display * dpy, XEvent * event)
|
|
{
|
|
/* Check any expired timers */
|
|
W_CheckTimerHandlers();
|
|
|
|
while (XPending(dpy) == 0) {
|
|
/* Do idle and timer stuff while there are no input or X events */
|
|
while (!waitForEvent(dpy, 0, False) && W_CheckIdleHandlers()) {
|
|
/* dispatch timer events */
|
|
W_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 for something to happen or a timer to expire */
|
|
waitForEvent(dpy, 0, True);
|
|
|
|
/* Check any expired timers */
|
|
W_CheckTimerHandlers();
|
|
}
|
|
|
|
XNextEvent(dpy, event);
|
|
}
|
|
|
|
void WMMaskEvent(Display * dpy, long mask, XEvent * event)
|
|
{
|
|
/* Check any expired timers */
|
|
W_CheckTimerHandlers();
|
|
|
|
while (!XCheckMaskEvent(dpy, mask, event)) {
|
|
/* Do idle and timer stuff while there are no input or X events */
|
|
while (!waitForEvent(dpy, mask, False) && W_CheckIdleHandlers()) {
|
|
W_CheckTimerHandlers();
|
|
}
|
|
|
|
if (XCheckMaskEvent(dpy, mask, event))
|
|
return;
|
|
|
|
/* Wait for input on the X connection socket or another input handler */
|
|
waitForEvent(dpy, mask, True);
|
|
|
|
/* Check any expired timers */
|
|
W_CheckTimerHandlers();
|
|
}
|
|
}
|
|
|
|
Bool WMScreenPending(WMScreen * scr)
|
|
{
|
|
if (XPending(scr->display))
|
|
return True;
|
|
else
|
|
return False;
|
|
}
|
|
|
|
WMEventHook *WMHookEventHandler(WMEventHook * handler)
|
|
{
|
|
WMEventHook *oldHandler = extraEventHandler;
|
|
|
|
extraEventHandler = handler;
|
|
|
|
return oldHandler;
|
|
}
|