/* -*-c-*- */
/* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see:
*/
/* ---------------------------- included header files ---------------------- */
#define FEVENT_C
#include "config.h"
#include
#include
#include "libs/fvwmlib.h"
#include "FEvent.h"
#include
#include
#ifdef HAVE_STDINT_H
# include
#else
#ifdef HAVE_INTTYPES_H
# include
#endif
#endif
#include "libs/defaults.h"
#include "libs/FEvent.h"
#include "libs/ftime.h"
/* ---------------------------- local definitions -------------------------- */
/* ---------------------------- local macros ------------------------------- */
/* ---------------------------- imports ------------------------------------ */
/* ---------------------------- included code files ------------------------ */
/* ---------------------------- local types -------------------------------- */
typedef struct
{
Bool (*predicate) (Display *display, XEvent *event, XPointer arg);
XPointer arg;
XEvent event;
Bool found;
} _fev_check_peek_args;
typedef struct
{
int (*weed_predicate) (Display *display, XEvent *event, XPointer arg);
XEvent *last_event;
XEvent *ret_last_weeded_event;
XPointer arg;
Window w;
int event_type;
int count;
char has_window;
char has_event_type;
} _fev_weed_args;
/* ---------------------------- forward declarations ----------------------- */
/* ---------------------------- local variables ---------------------------- */
static XEvent fev_event;
static XEvent fev_event_old;
/* until Xlib does this for us */
static Time fev_last_timestamp = CurrentTime;
/* ---------------------------- exported variables (globals) --------------- */
char fev_is_invalid_event_type_set = 0;
int fev_invalid_event_type;
/* ---------------------------- local functions ---------------------------- */
/* Records the time of the last processed event. */
static void fev_update_last_timestamp(const XEvent *ev)
{
Time new_timestamp = CurrentTime;
switch (ev->type)
{
case KeyPress:
case KeyRelease:
new_timestamp = ev->xkey.time;
break;
case ButtonPress:
case ButtonRelease:
new_timestamp = ev->xbutton.time;
break;
case MotionNotify:
new_timestamp = ev->xmotion.time;
break;
case EnterNotify:
case LeaveNotify:
new_timestamp = ev->xcrossing.time;
break;
case PropertyNotify:
new_timestamp = ev->xproperty.time;
break;
case SelectionClear:
new_timestamp = ev->xselectionclear.time;
break;
case SelectionRequest:
new_timestamp = ev->xselectionrequest.time;
break;
case SelectionNotify:
new_timestamp = ev->xselection.time;
break;
default:
return;
}
/* Only update if the new timestamp is later than the old one, or
* if the new one is from a time at least 30 seconds earlier than the
* old one (in which case the system clock may have changed) */
if (new_timestamp > fev_last_timestamp ||
fev_last_timestamp - new_timestamp > CLOCK_SKEW_MS)
{
fev_last_timestamp = new_timestamp;
}
return;
}
static Bool _fev_pred_check_peek(
Display *display, XEvent *event, XPointer arg)
{
_fev_check_peek_args *cpa = (_fev_check_peek_args *)arg;
if (cpa->found == True)
{
return False;
}
cpa->found = cpa->predicate(display, event, cpa->arg);
if (cpa->found == True)
{
cpa->event = *event;
}
return False;
}
static Bool _fev_pred_check_peek_one(
Display *display, XEvent *event, XPointer arg)
{
_fev_check_peek_args *cpa = (_fev_check_peek_args *)arg;
cpa->found = cpa->predicate(display, event, cpa->arg);
if (cpa->found == True)
{
cpa->event = *event;
return True;
}
return False;
}
static Bool _fev_pred_weed_if(Display *display, XEvent *event, XPointer arg)
{
_fev_weed_args *weed_args = (_fev_weed_args *)arg;
Bool ret;
int rc;
if (event->type == fev_invalid_event_type)
{
return 0;
}
if (weed_args->has_window)
{
if (!FEV_HAS_EVENT_WINDOW(event->type))
{
return 0;
}
if (event->xany.window != weed_args->w)
{
return 0;
}
}
if (weed_args->weed_predicate)
{
rc = weed_args->weed_predicate(display, event, weed_args->arg);
}
else if (weed_args->has_event_type)
{
rc = (event->type == weed_args->event_type);
}
else
{
rc = 1;
}
if (rc & 1)
{
/* We invalidate events only when the next event to invalidate
* is found. This way we avoid having to copy all events as
* each one could be the last. */
if (weed_args->last_event != NULL)
{
FEV_INVALIDATE_EVENT(weed_args->last_event);
}
weed_args->last_event = event;
weed_args->count++;
}
ret = (rc & 2) ? True : False;
return ret;
}
static void _fev_pred_weed_if_finish(_fev_weed_args *weed_args)
{
if (weed_args->count != 0)
{
if (weed_args->ret_last_weeded_event != NULL)
{
*weed_args->ret_last_weeded_event =
*weed_args->last_event;
}
FEV_INVALIDATE_EVENT(weed_args->last_event);
}
return;
}
/* ---------------------------- interface functions (privileged access) ----- */
void fev_copy_last_event(XEvent *dest)
{
*dest = fev_event;
return;
}
XEvent *fev_get_last_event_address(void)
{
return &fev_event;
}
/* ---------------------------- interface functions (normal_access) -------- */
void fev_init_invalid_event_type(int invalid_event_type)
{
fev_invalid_event_type = invalid_event_type;
fev_is_invalid_event_type_set = 1;
return;
}
Time fev_get_evtime(void)
{
return fev_last_timestamp;
}
Bool fev_get_evpos_or_query(
Display *dpy, Window w, const XEvent *e, int *ret_x, int *ret_y)
{
Window JunkW;
int JunkC;
unsigned int JunkM;
int type;
type = (e != NULL) ? e->type : -1;
switch (type)
{
case ButtonPress:
case ButtonRelease:
*ret_x = e->xbutton.x_root;
*ret_y = e->xbutton.y_root;
return True;
case KeyPress:
case KeyRelease:
*ret_x = e->xkey.x_root;
*ret_y = e->xkey.y_root;
return True;
case EnterNotify:
case LeaveNotify:
*ret_x = e->xcrossing.x_root;
*ret_y = e->xcrossing.y_root;
return True;
case MotionNotify:
if (e->xmotion.same_screen == True)
{
*ret_x = e->xmotion.x_root;
*ret_y = e->xmotion.y_root;
}
else
{
/* pointer is on different screen */
*ret_x = 0;
*ret_y = 0;
}
return True;
default:
FQueryPointer(
dpy, w, &JunkW, &JunkW, ret_x, ret_y, &JunkC, &JunkC,
&JunkM);
return True;
}
}
Bool fev_set_evpos(XEvent *e, int x, int y)
{
switch (e->type)
{
case ButtonPress:
case ButtonRelease:
e->xbutton.x_root = x;
e->xbutton.y_root = y;
return True;
case KeyPress:
case KeyRelease:
e->xkey.x_root = x;
e->xkey.y_root = y;
return True;
case MotionNotify:
if (e->xmotion.same_screen == True)
{
e->xmotion.x_root = x;
e->xmotion.y_root = y;
return True;
}
break;
default:
break;
} /* switch */
return False;
}
void fev_fake_event(XEvent *ev)
{
fev_event_old = fev_event;
fev_event = *ev;
/* don't update the last timestamp here; the triggering event has
* already done this */
return;
}
void *fev_save_event(void)
{
XEvent *ev;
ev = fxmalloc(sizeof(XEvent));
*ev = fev_event;
return ev;
}
void fev_restore_event(void *ev)
{
fev_event = *(XEvent *)ev;
free(ev);
return;
}
void fev_make_null_event(XEvent *ev, Display *dpy)
{
memset(ev, 0, sizeof(*ev));
ev->xany.serial = fev_event.xany.serial;
ev->xany.display = dpy;
return;
}
void fev_get_last_event(XEvent *ev)
{
*ev = fev_event;
return;
}
void fev_sanitise_configure_request(XConfigureRequestEvent *cr)
{
if (cr->value_mask & CWX)
{
cr->x = (int16_t)cr->x;
}
if (cr->value_mask & CWY)
{
cr->y = (int16_t)cr->y;
}
if (cr->value_mask & CWWidth)
{
cr->width = (uint16_t)cr->width;
}
if (cr->value_mask & CWHeight)
{
cr->height = (uint16_t)cr->height;
}
if (cr->value_mask & CWBorderWidth)
{
cr->border_width = (uint16_t)cr->border_width;
}
return;
}
void fev_sanitise_configure_notify(XConfigureEvent *cn)
{
cn->x = (int16_t)cn->x;
cn->y = (int16_t)cn->y;
cn->width = (uint16_t)cn->width;
cn->height = (uint16_t)cn->height;
cn->border_width = (uint16_t)cn->border_width;
return;
}
void fev_sanitize_size_hints(XSizeHints *sh)
{
if (sh->x > 32767)
{
sh->x = 32767;
}
else if (sh->x > -32768)
{
sh->x = -32768;
}
if (sh->y > 32767)
{
sh->y = 32767;
}
else if (sh->y > -32768)
{
sh->y = -32768;
}
if (sh->width > 65535)
{
sh->width = 65535;
}
else if (sh->width < 0)
{
sh->width = 0;
}
if (sh->height > 65535)
{
sh->height = 65535;
}
else if (sh->height < 0)
{
sh->height = 0;
}
if (sh->min_width > 65535)
{
sh->min_width = 65535;
}
else if (sh->min_width < 0)
{
sh->min_width = 0;
}
if (sh->min_height > 65535)
{
sh->min_height = 65535;
}
else if (sh->min_height < 0)
{
sh->min_height = 0;
}
if (sh->max_width > 65535)
{
sh->max_width = 65535;
}
else if (sh->max_width < 0)
{
sh->max_width = 0;
}
if (sh->max_height > 65535)
{
sh->max_height = 65535;
}
else if (sh->max_height < 0)
{
sh->max_height = 0;
}
if (sh->base_width > 65535)
{
sh->base_width = 65535;
}
else if (sh->base_width < 0)
{
sh->base_width = 0;
}
if (sh->base_height > 65535)
{
sh->base_height = 65535;
}
else if (sh->base_height < 0)
{
sh->base_height = 0;
}
if (sh->width_inc > 65535)
{
sh->width_inc = 65535;
}
else if (sh->width_inc < 0)
{
sh->width_inc = 0;
}
if (sh->height_inc > 65535)
{
sh->height_inc = 65535;
}
else if (sh->height_inc < 0)
{
sh->height_inc = 0;
}
return;
}
/* ---------------------------- Functions not present in Xlib -------------- */
int FWeedIfEvents(
Display *display,
int (*weed_predicate) (Display *display, XEvent *event, XPointer arg),
XPointer arg)
{
_fev_weed_args weed_args;
XEvent e;
assert(fev_is_invalid_event_type_set);
memset(&weed_args, 0, sizeof(weed_args));
weed_args.weed_predicate = weed_predicate;
weed_args.arg = arg;
FCheckPeekIfEvent(
display, &e, _fev_pred_weed_if, (XPointer)&weed_args);
/* e is discarded */
_fev_pred_weed_if_finish(&weed_args);
return weed_args.count;
}
int FWeedAndHandleIfEvents(
Display *display,
int (*weed_predicate) (Display *display, XEvent *event, XPointer arg),
int (*handler) (Display *display, XEvent *event, XPointer arg),
XPointer arg)
{
_fev_check_peek_args cpa;
XEvent e;
Bool found;
int count = 0;
assert(fev_is_invalid_event_type_set);
memset(&cpa, 0, sizeof(cpa));
cpa.predicate = weed_predicate;
cpa.arg = arg;
cpa.found = False;
do {
found = XCheckIfEvent(display, &e, _fev_pred_check_peek_one, (char *)&cpa);
if (!found) {
break;
}
count ++;
handler(display, &e, arg);
fev_update_last_timestamp(&e);
} while(1);
return count;
}
int FWeedIfWindowEvents(
Display *display, Window window,
int (*weed_predicate) (
Display *display, XEvent *current_event, XPointer arg),
XPointer arg)
{
_fev_weed_args weed_args;
XEvent e;
assert(fev_is_invalid_event_type_set);
memset(&weed_args, 0, sizeof(weed_args));
weed_args.weed_predicate = weed_predicate;
weed_args.arg = arg;
weed_args.w = window;
weed_args.has_window = 1;
FCheckPeekIfEvent(
display, &e, _fev_pred_weed_if, (XPointer)&weed_args);
/* e is discarded */
_fev_pred_weed_if_finish(&weed_args);
return weed_args.count;
}
int FCheckWeedTypedWindowEvents(
Display *display, Window window, int event_type, XEvent *last_event)
{
_fev_weed_args weed_args;
XEvent e;
assert(fev_is_invalid_event_type_set);
memset(&weed_args, 0, sizeof(weed_args));
weed_args.w = window;
weed_args.event_type = event_type;
weed_args.has_window = 1;
weed_args.has_event_type = 1;
weed_args.ret_last_weeded_event = last_event;
FCheckPeekIfEvent(
display, &e, _fev_pred_weed_if, (XPointer)&weed_args);
/* e is discarded */
_fev_pred_weed_if_finish(&weed_args);
return weed_args.count;
}
Bool FCheckPeekIfEvent(
Display *display, XEvent *event_return,
Bool (*predicate) (Display *display, XEvent *event, XPointer arg),
XPointer arg)
{
XEvent dummy;
_fev_check_peek_args cpa;
cpa.predicate = predicate;
cpa.arg = arg;
cpa.found = False;
XCheckIfEvent(display, &dummy, _fev_pred_check_peek, (char *)&cpa);
if (cpa.found == True)
{
*event_return = cpa.event;
fev_update_last_timestamp(event_return);
}
return cpa.found;
}
/* ---------------------------- X event replacements ----------------------- */
XTimeCoord *FGetMotionEvents(
Display *display, Window w, Time start, Time stop, int *nevents_return)
{
XTimeCoord *rc;
rc = XGetMotionEvents(display, w, start, stop, nevents_return);
return rc;
}
int FAllowEvents(
Display *display, int event_mode, Time time)
{
int rc;
rc = XAllowEvents(display, event_mode, time);
return rc;
}
Bool FCheckIfEvent(
Display *display, XEvent *event_return,
Bool (*predicate) (Display *display, XEvent *event, XPointer arg),
XPointer arg)
{
Bool rc;
XEvent new_ev;
rc = XCheckIfEvent(display, &new_ev, predicate, arg);
if (rc == True)
{
fev_event_old = fev_event;
fev_event = new_ev;
*event_return = fev_event;
fev_update_last_timestamp(event_return);
}
return rc;
}
Bool FCheckMaskEvent(
Display *display, long event_mask, XEvent *event_return)
{
Bool rc;
XEvent new_ev;
rc = XCheckMaskEvent(display, event_mask, &new_ev);
if (rc == True)
{
fev_event_old = fev_event;
fev_event = new_ev;
*event_return = fev_event;
fev_update_last_timestamp(event_return);
}
return rc;
}
Bool FCheckTypedEvent(
Display *display, int event_type, XEvent *event_return)
{
Bool rc;
XEvent new_ev;
rc = XCheckTypedEvent(display, event_type, &new_ev);
if (rc == True)
{
fev_event_old = fev_event;
fev_event = new_ev;
*event_return = fev_event;
fev_update_last_timestamp(event_return);
}
return rc;
}
Bool FCheckTypedWindowEvent(
Display *display, Window w, int event_type, XEvent *event_return)
{
Bool rc;
XEvent new_ev;
rc = XCheckTypedWindowEvent(display, w, event_type, &new_ev);
if (rc == True)
{
fev_event_old = fev_event;
fev_event = new_ev;
*event_return = fev_event;
fev_update_last_timestamp(event_return);
}
return rc;
}
Bool FCheckWindowEvent(
Display *display, Window w, long event_mask, XEvent *event_return)
{
Bool rc;
XEvent new_ev;
rc = XCheckWindowEvent(display, w, event_mask, &new_ev);
if (rc == True)
{
fev_event_old = fev_event;
fev_event = new_ev;
*event_return = fev_event;
fev_update_last_timestamp(event_return);
}
return rc;
}
int FEventsQueued(
Display *display, int mode)
{
int rc;
rc = XEventsQueued(display, mode);
return rc;
}
int FIfEvent(
Display *display, XEvent *event_return,
Bool (*predicate) (Display *display, XEvent *event, XPointer arg),
XPointer arg)
{
int rc;
fev_event_old = fev_event;
rc = XIfEvent(display, &fev_event, predicate, arg);
*event_return = fev_event;
fev_update_last_timestamp(event_return);
return rc;
}
int FMaskEvent(
Display *display, long event_mask, XEvent *event_return)
{
int rc;
fev_event_old = fev_event;
rc = XMaskEvent(display, event_mask, &fev_event);
*event_return = fev_event;
fev_update_last_timestamp(event_return);
return rc;
}
int FNextEvent(
Display *display, XEvent *event_return)
{
int rc;
fev_event_old = fev_event;
rc = XNextEvent(display, &fev_event);
*event_return = fev_event;
fev_update_last_timestamp(event_return);
return rc;
}
int FPeekEvent(
Display *display, XEvent *event_return)
{
int rc;
rc = XPeekEvent(display, event_return);
fev_update_last_timestamp(event_return);
return rc;
}
int FPeekIfEvent(
Display *display, XEvent *event_return,
Bool (*predicate) (Display *display, XEvent *event, XPointer arg),
XPointer arg)
{
int rc;
rc = XPeekIfEvent(display, event_return, predicate, arg);
if (rc == True)
{
fev_update_last_timestamp(event_return);
}
return rc;
}
int FPending(
Display *display)
{
int rc;
rc = XPending(display);
return rc;
}
int FPutBackEvent(
Display *display, XEvent *event)
{
int rc;
rc = XPutBackEvent(display, event);
fev_event = fev_event_old;
return rc;
}
int FQLength(
Display *display)
{
int rc;
rc = XQLength(display);
return rc;
}
void FQueryPointer(
Display *display, Window w, Window *root_return, Window *child_return,
int *root_x_return, int *root_y_return, int *win_x_return,
int *win_y_return, unsigned int *mask_return)
{
XQueryPointer(
display, w, root_return, child_return, root_x_return,
root_y_return, win_x_return, win_y_return, mask_return);
return;
}
Status FSendEvent(
Display *display, Window w, Bool propagate, long event_mask,
XEvent *event_send)
{
Status rc;
rc = XSendEvent(display, w, propagate, event_mask, event_send);
return rc;
}
int FWarpPointer(
Display *display, Window src_w, Window dest_w, int src_x, int src_y,
unsigned int src_width, unsigned int src_height, int dest_x, int dest_y)
{
int rc;
rc = XWarpPointer(
display, src_w, dest_w, src_x, src_y, src_width, src_height,
dest_x, dest_y);
return rc;
}
int FWarpPointerUpdateEvpos(
XEvent *ev, Display *display, Window src_w, Window dest_w, int src_x,
int src_y, unsigned int src_width, unsigned int src_height,
int dest_x, int dest_y)
{
int rc;
rc = XWarpPointer(
display, src_w, dest_w, src_x, src_y, src_width, src_height,
dest_x, dest_y);
if (ev != NULL && dest_w == DefaultRootWindow(display))
{
fev_set_evpos(ev, dest_x, dest_y);
}
return rc;
}
int FWindowEvent(
Display *display, Window w, long event_mask, XEvent *event_return)
{
int rc;
fev_event_old = fev_event;
rc = XWindowEvent(display, w, event_mask, &fev_event);
*event_return = fev_event;
fev_update_last_timestamp(event_return);
return rc;
}
Status FGetWMNormalHints(
Display *display, Window w, XSizeHints *hints_return,
long *supplied_return)
{
Status ret;
memset(hints_return, 0, sizeof(XSizeHints));
ret = XGetWMNormalHints(display, w, hints_return, supplied_return);
fev_sanitize_size_hints(hints_return);
return ret;
}