1
0
mirror of https://github.com/gryf/wmaker.git synced 2025-12-30 10:22:34 +01:00
Files
wmaker/WINGs/wevent.c
dan 1b41d56ad0 - updated code to use the new runmodal loop where necessary.
- 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.
2001-01-10 04:24:11 +00:00

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