not using CurrentTime anywhere
This commit is contained in:
parent
1d5fa24e4b
commit
f29dd7e0cd
11 changed files with 19 additions and 1074 deletions
|
@ -1934,7 +1934,7 @@ gboolean client_focus(Client *self)
|
|||
|
||||
if (self->can_focus)
|
||||
XSetInputFocus(ob_display, self->window, RevertToNone,
|
||||
CurrentTime);
|
||||
event_lasttime);
|
||||
|
||||
if (self->focus_notify) {
|
||||
XEvent ce;
|
||||
|
@ -1944,7 +1944,7 @@ gboolean client_focus(Client *self)
|
|||
ce.xclient.window = self->window;
|
||||
ce.xclient.format = 32;
|
||||
ce.xclient.data.l[0] = prop_atoms.wm_take_focus;
|
||||
ce.xclient.data.l[1] = CurrentTime;
|
||||
ce.xclient.data.l[1] = event_lasttime;
|
||||
ce.xclient.data.l[2] = 0l;
|
||||
ce.xclient.data.l[3] = 0l;
|
||||
ce.xclient.data.l[4] = 0l;
|
||||
|
@ -1972,6 +1972,7 @@ void client_set_focused(Client *self, gboolean focused)
|
|||
if (focus_client != self)
|
||||
focus_set_client(self);
|
||||
} else {
|
||||
event_unfocustime = event_lasttime;
|
||||
if (focus_client == self)
|
||||
focus_set_client(NULL);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ static void event_handle_root(XEvent *e);
|
|||
static void event_handle_client(Client *c, XEvent *e);
|
||||
|
||||
Time event_lasttime = 0;
|
||||
Time event_unfocustime = 0;
|
||||
|
||||
/*! The value of the mask for the NumLock modifier */
|
||||
unsigned int NumLockMask;
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
|
||||
/*! Time at which the last event with a timestamp occured. */
|
||||
extern Time event_lasttime;
|
||||
/*! Time at which the last event with a timestamp occured before we tried to
|
||||
unfocus a window. */
|
||||
extern Time event_unfocustime;
|
||||
|
||||
/*! The value of the mask for the NumLock modifier */
|
||||
extern unsigned int NumLockMask;
|
||||
|
|
|
@ -62,7 +62,8 @@ void focus_set_client(Client *client)
|
|||
|
||||
if (client == NULL) {
|
||||
/* when nothing will be focused, send focus to the backup target */
|
||||
XSetInputFocus(ob_display, focus_backup, RevertToNone, event_lasttime);
|
||||
XSetInputFocus(ob_display, focus_backup, RevertToNone,
|
||||
event_unfocustime);
|
||||
}
|
||||
|
||||
old = focus_client;
|
||||
|
|
|
@ -17,13 +17,13 @@ void grab_keyboard(gboolean grab)
|
|||
if (kgrabs++ == 0) {
|
||||
g_message("GRABBING KEYBOARD %d", kgrabs);
|
||||
XGrabKeyboard(ob_display, ob_root, 0, GrabModeAsync, GrabModeSync,
|
||||
CurrentTime);
|
||||
event_lasttime);
|
||||
} else
|
||||
g_message("NOT GRABBING KEYBOARD %d", kgrabs);
|
||||
} else if (kgrabs > 0) {
|
||||
if (--kgrabs == 0) {
|
||||
g_message("UNGRABBING KEYBOARD %d", kgrabs);
|
||||
XUngrabKeyboard(ob_display, CurrentTime);
|
||||
XUngrabKeyboard(ob_display, event_lasttime);
|
||||
} else
|
||||
g_message("NOT UNGRABBING KEYBOARD %d", kgrabs);
|
||||
}
|
||||
|
@ -34,10 +34,10 @@ void grab_pointer(gboolean grab, Cursor cur)
|
|||
if (grab) {
|
||||
if (pgrabs++ == 0)
|
||||
XGrabPointer(ob_display, ob_root, False, 0, GrabModeAsync,
|
||||
GrabModeAsync, FALSE, cur, CurrentTime);
|
||||
GrabModeAsync, FALSE, cur, event_lasttime);
|
||||
} else if (pgrabs > 0) {
|
||||
if (--pgrabs == 0)
|
||||
XUngrabPointer(ob_display, CurrentTime);
|
||||
XUngrabPointer(ob_display, event_lasttime);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,297 +0,0 @@
|
|||
#include "focus.h"
|
||||
#include "openbox.h"
|
||||
#include "keyboard.h"
|
||||
#include "clientwrap.h"
|
||||
|
||||
#include <Python.h>
|
||||
#include <glib.h>
|
||||
#ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#endif
|
||||
|
||||
typedef struct KeyBindingTree {
|
||||
guint state;
|
||||
guint key;
|
||||
GList *keylist;
|
||||
PyObject *func;
|
||||
|
||||
/* the next binding in the tree at the same level */
|
||||
struct KeyBindingTree *next_sibling;
|
||||
/* the first child of this binding (next binding in a chained sequence).*/
|
||||
struct KeyBindingTree *first_child;
|
||||
} KeyBindingTree;
|
||||
|
||||
|
||||
static KeyBindingTree *firstnode, *curpos;
|
||||
static guint reset_key, reset_state;
|
||||
static gboolean grabbed, user_grabbed;
|
||||
static PyObject *grab_func;
|
||||
|
||||
/***************************************************************************
|
||||
|
||||
Define the type 'KeyboardData'
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
typedef struct KeyboardData {
|
||||
PyObject_HEAD
|
||||
PyObject *keychain;
|
||||
guint state;
|
||||
guint keycode;
|
||||
gboolean press;
|
||||
} KeyboardData;
|
||||
|
||||
staticforward PyTypeObject KeyboardDataType;
|
||||
|
||||
/***************************************************************************
|
||||
|
||||
Type methods/struct
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
static PyObject *keybdata_new(PyObject *keychain, guint state,
|
||||
guint keycode, gboolean press)
|
||||
{
|
||||
KeyboardData *data = PyObject_New(KeyboardData, &KeyboardDataType);
|
||||
data->keychain = keychain;
|
||||
Py_INCREF(keychain);
|
||||
data->state = state;
|
||||
data->keycode = keycode;
|
||||
data->press = press;
|
||||
return (PyObject*) data;
|
||||
}
|
||||
|
||||
static void keybdata_dealloc(KeyboardData *self)
|
||||
{
|
||||
Py_DECREF(self->keychain);
|
||||
PyObject_Del((PyObject*)self);
|
||||
}
|
||||
|
||||
static PyObject *keybdata_getattr(KeyboardData *self, char *name)
|
||||
{
|
||||
if (!strcmp(name, "keychain")) {
|
||||
Py_INCREF(self->keychain);
|
||||
return self->keychain;
|
||||
} else if (!strcmp(name, "state"))
|
||||
return PyInt_FromLong(self->state);
|
||||
else if (!strcmp(name, "keycode"))
|
||||
return PyInt_FromLong(self->keycode);
|
||||
else if (!strcmp(name, "press"))
|
||||
return PyInt_FromLong(!!self->press);
|
||||
|
||||
PyErr_Format(PyExc_AttributeError, "no such attribute '%s'", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PyTypeObject KeyboardDataType = {
|
||||
PyObject_HEAD_INIT(NULL)
|
||||
0,
|
||||
"KeyboardData",
|
||||
sizeof(KeyboardData),
|
||||
0,
|
||||
(destructor) keybdata_dealloc, /*tp_dealloc*/
|
||||
0, /*tp_print*/
|
||||
(getattrfunc) keybdata_getattr, /*tp_getattr*/
|
||||
0, /*tp_setattr*/
|
||||
0, /*tp_compare*/
|
||||
0, /*tp_repr*/
|
||||
0, /*tp_as_number*/
|
||||
0, /*tp_as_sequence*/
|
||||
0, /*tp_as_mapping*/
|
||||
0, /*tp_hash */
|
||||
};
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
|
||||
|
||||
static gboolean grab_keyboard(gboolean grab)
|
||||
{
|
||||
gboolean ret = TRUE;
|
||||
|
||||
g_message("grab_keyboard(%s). grabbed: %d", (grab?"True":"False"),grabbed);
|
||||
|
||||
user_grabbed = grab;
|
||||
if (!grabbed) {
|
||||
if (grab)
|
||||
ret = XGrabKeyboard(ob_display, ob_root, 0, GrabModeAsync,
|
||||
GrabModeAsync, CurrentTime) == GrabSuccess;
|
||||
else
|
||||
XUngrabKeyboard(ob_display, CurrentTime);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
|
||||
Define the type 'Keyboard'
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#define IS_KEYBOARD(v) ((v)->ob_type == &KeyboardType)
|
||||
#define CHECK_KEYBOARD(self, funcname) { \
|
||||
if (!IS_KEYBOARD(self)) { \
|
||||
PyErr_SetString(PyExc_TypeError, \
|
||||
"descriptor '" funcname "' requires a 'Keyboard' " \
|
||||
"object"); \
|
||||
return NULL; \
|
||||
} \
|
||||
}
|
||||
|
||||
typedef struct Keyboard {
|
||||
PyObject_HEAD
|
||||
} Keyboard;
|
||||
|
||||
staticforward PyTypeObject KeyboardType;
|
||||
|
||||
|
||||
static PyObject *keyb_clearBinds(Keyboard *self, PyObject *args)
|
||||
{
|
||||
CHECK_KEYBOARD(self, "clearBinds");
|
||||
if (!PyArg_ParseTuple(args, ":clearBinds"))
|
||||
return NULL;
|
||||
clearall();
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
static PyObject *keyb_grab(Keyboard *self, PyObject *args)
|
||||
{
|
||||
PyObject *func;
|
||||
|
||||
CHECK_KEYBOARD(self, "grab");
|
||||
if (!PyArg_ParseTuple(args, "O:grab", &func))
|
||||
return NULL;
|
||||
if (!PyCallable_Check(func)) {
|
||||
PyErr_SetString(PyExc_ValueError, "expected a callable object");
|
||||
return NULL;
|
||||
}
|
||||
if (!grab_keyboard(TRUE)) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "failed to grab keyboard");
|
||||
return NULL;
|
||||
}
|
||||
grab_func = func;
|
||||
Py_INCREF(grab_func);
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
static PyObject *keyb_ungrab(Keyboard *self, PyObject *args)
|
||||
{
|
||||
CHECK_KEYBOARD(self, "ungrab");
|
||||
if (!PyArg_ParseTuple(args, ":ungrab"))
|
||||
return NULL;
|
||||
grab_keyboard(FALSE);
|
||||
Py_XDECREF(grab_func);
|
||||
grab_func = NULL;
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
#define METH(n, d) {#n, (PyCFunction)keyb_##n, METH_VARARGS, #d}
|
||||
|
||||
static PyMethodDef KeyboardMethods[] = {
|
||||
METH(bind,
|
||||
"bind(keychain, func)\n\n"
|
||||
"Binds a key-chain to a function. The keychain is a tuple of strings "
|
||||
"which define a chain of key presses. Each member of the tuple has "
|
||||
"the format [Modifier-]...[Key]. Modifiers can be 'mod1', 'mod2', "
|
||||
"'mod3', 'mod4', 'mod5', 'control', and 'shift'. The keys on your "
|
||||
"keyboard that are bound to each of these modifiers can be found by "
|
||||
"running 'xmodmap'. The Key can be any valid key definition. Key "
|
||||
"definitions can be found by running 'xev', pressing the key while "
|
||||
"its window is focused, and watching its output. Here are some "
|
||||
"examples of valid keychains: ('a'), ('F7'), ('control-a', 'd'), "
|
||||
"('control-mod1-x', 'control-mod4-g'), ('F1', 'space'). The func "
|
||||
"must have a definition similar to 'def func(keydata, client)'. A "
|
||||
"keychain cannot be bound to more than one function."),
|
||||
METH(clearBinds,
|
||||
"clearBinds()\n\n"
|
||||
"Removes all bindings that were previously made by bind()."),
|
||||
METH(grab,
|
||||
"grab(func)\n\n"
|
||||
"Grabs the entire keyboard, causing all possible keyboard events to "
|
||||
"be passed to the given function. CAUTION: Be sure when you grab() "
|
||||
"that you also have an ungrab() that will execute, or you will not "
|
||||
"be able to type until you restart Openbox. The func must have a "
|
||||
"definition similar to 'def func(keydata)'. The keyboard cannot be "
|
||||
"grabbed if it is already grabbed."),
|
||||
METH(ungrab,
|
||||
"ungrab()\n\n"
|
||||
"Ungrabs the keyboard. The keyboard cannot be ungrabbed if it is not "
|
||||
"grabbed."),
|
||||
{ NULL, NULL, 0, NULL }
|
||||
};
|
||||
|
||||
/***************************************************************************
|
||||
|
||||
Type methods/struct
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
static void keyb_dealloc(PyObject *self)
|
||||
{
|
||||
PyObject_Del(self);
|
||||
}
|
||||
|
||||
static PyTypeObject KeyboardType = {
|
||||
PyObject_HEAD_INIT(NULL)
|
||||
0,
|
||||
"Keyboard",
|
||||
sizeof(Keyboard),
|
||||
0,
|
||||
(destructor) keyb_dealloc, /*tp_dealloc*/
|
||||
0, /*tp_print*/
|
||||
0, /*tp_getattr*/
|
||||
0, /*tp_setattr*/
|
||||
0, /*tp_compare*/
|
||||
0, /*tp_repr*/
|
||||
0, /*tp_as_number*/
|
||||
0, /*tp_as_sequence*/
|
||||
0, /*tp_as_mapping*/
|
||||
0, /*tp_hash */
|
||||
};
|
||||
|
||||
/**************************************************************************/
|
||||
|
||||
void keyboard_startup()
|
||||
{
|
||||
PyObject *input, *inputdict, *ptr;
|
||||
gboolean b;
|
||||
|
||||
curpos = firstnode = NULL;
|
||||
grabbed = user_grabbed = FALSE;
|
||||
|
||||
b = translate("C-G", &reset_state, &reset_key);
|
||||
g_assert(b);
|
||||
|
||||
KeyboardType.ob_type = &PyType_Type;
|
||||
KeyboardType.tp_methods = KeyboardMethods;
|
||||
PyType_Ready(&KeyboardType);
|
||||
PyType_Ready(&KeyboardDataType);
|
||||
|
||||
/* get the input module/dict */
|
||||
input = PyImport_ImportModule("input"); /* new */
|
||||
g_assert(input != NULL);
|
||||
inputdict = PyModule_GetDict(input); /* borrowed */
|
||||
g_assert(inputdict != NULL);
|
||||
|
||||
/* add a Keyboard instance to the input module */
|
||||
ptr = (PyObject*) PyObject_New(Keyboard, &KeyboardType);
|
||||
PyDict_SetItemString(inputdict, "Keyboard", ptr);
|
||||
Py_DECREF(ptr);
|
||||
|
||||
Py_DECREF(input);
|
||||
}
|
||||
|
||||
void keyboard_shutdown()
|
||||
{
|
||||
if (grabbed || user_grabbed) {
|
||||
grabbed = FALSE;
|
||||
grab_keyboard(FALSE);
|
||||
}
|
||||
grab_keys(FALSE);
|
||||
destroytree(firstnode);
|
||||
firstnode = NULL;
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
#ifndef __keyboard_h
|
||||
#define __keyboard_h
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
void keyboard_startup();
|
||||
void keyboard_shutdown();
|
||||
|
||||
guint keyboard_translate_modifier(char *str);
|
||||
|
||||
void keyboard_event(XKeyEvent *e);
|
||||
|
||||
#endif
|
|
@ -1,740 +0,0 @@
|
|||
#include "pointer.h"
|
||||
#include "keyboard.h"
|
||||
#include "frame.h"
|
||||
#include "engine.h"
|
||||
#include "openbox.h"
|
||||
#include "hooks.h"
|
||||
#include "configwrap.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <Python.h>
|
||||
#include <structmember.h> /* for PyMemberDef stuff */
|
||||
#ifdef HAVE_STDLIB_H
|
||||
# include <stdlib.h>
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
Action_Press,
|
||||
Action_Release,
|
||||
Action_Click,
|
||||
Action_DoubleClick,
|
||||
Action_Motion,
|
||||
NUM_ACTIONS
|
||||
} Action;
|
||||
|
||||
/* GData of GSList*s of PointerBinding*s. */
|
||||
static GData *bound_contexts;
|
||||
static gboolean grabbed;
|
||||
PyObject *grab_func;
|
||||
|
||||
struct foreach_grab_temp {
|
||||
Client *client;
|
||||
gboolean grab;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
guint state;
|
||||
guint button;
|
||||
Action action;
|
||||
char *name;
|
||||
GSList *funcs[NUM_ACTIONS];
|
||||
} PointerBinding;
|
||||
|
||||
/***************************************************************************
|
||||
|
||||
Define the type 'ButtonData'
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
typedef struct PointerData {
|
||||
PyObject_HEAD
|
||||
Action action;
|
||||
GQuark context;
|
||||
char *button;
|
||||
guint state;
|
||||
guint buttonnum;
|
||||
int posx, posy;
|
||||
int pressposx, pressposy;
|
||||
int pcareax, pcareay, pcareaw, pcareah;
|
||||
} PointerData;
|
||||
|
||||
staticforward PyTypeObject PointerDataType;
|
||||
|
||||
/***************************************************************************
|
||||
|
||||
Type methods/struct
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
static PyObject *ptrdata_new(char *button, GQuark context, Action action,
|
||||
guint state, guint buttonnum, int posx, int posy,
|
||||
int pressposx, int pressposy, int pcareax,
|
||||
int pcareay, int pcareaw, int pcareah)
|
||||
{
|
||||
PointerData *self = PyObject_New(PointerData, &PointerDataType);
|
||||
self->button = g_strdup(button);
|
||||
self->context = context;
|
||||
self->action = action;
|
||||
self->state = state;
|
||||
self->buttonnum = buttonnum;
|
||||
self->posx = posx;
|
||||
self->posy = posy;
|
||||
self->pressposx = pressposx;
|
||||
self->pressposy = pressposy;
|
||||
self->pcareax = pcareax;
|
||||
self->pcareay = pcareay;
|
||||
self->pcareaw = pcareaw;
|
||||
self->pcareah = pcareah;
|
||||
return (PyObject*) self;
|
||||
}
|
||||
|
||||
static void ptrdata_dealloc(PointerData *self)
|
||||
{
|
||||
g_free(self->button);
|
||||
PyObject_Del((PyObject*)self);
|
||||
}
|
||||
|
||||
static PyObject *ptrdata_getattr(PointerData *self, char *name)
|
||||
{
|
||||
if (!strcmp(name, "button"))
|
||||
return PyString_FromString(self->button);
|
||||
if (!strcmp(name, "action"))
|
||||
return PyInt_FromLong(self->action);
|
||||
if (!strcmp(name, "context"))
|
||||
return PyString_FromString(g_quark_to_string(self->context));
|
||||
if (!strcmp(name, "state"))
|
||||
return PyInt_FromLong(self->state);
|
||||
if (!strcmp(name, "buttonnum"))
|
||||
return PyInt_FromLong(self->buttonnum);
|
||||
|
||||
if (self->action == Action_Motion) { /* the rest are only for motions */
|
||||
if (!strcmp(name, "pos")) {
|
||||
PyObject *pos = PyTuple_New(2);
|
||||
PyTuple_SET_ITEM(pos, 0, PyInt_FromLong(self->posx));
|
||||
PyTuple_SET_ITEM(pos, 1, PyInt_FromLong(self->posy));
|
||||
return pos;
|
||||
}
|
||||
if (!strcmp(name, "presspos")) {
|
||||
PyObject *presspos = PyTuple_New(2);
|
||||
PyTuple_SET_ITEM(presspos, 0, PyInt_FromLong(self->pressposx));
|
||||
PyTuple_SET_ITEM(presspos, 1, PyInt_FromLong(self->pressposy));
|
||||
return presspos;
|
||||
}
|
||||
if (!strcmp(name, "pressclientarea")) {
|
||||
if (self->pcareaw < 0) { /* < 0 indicates no client */
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
} else {
|
||||
PyObject *ca = PyTuple_New(4);
|
||||
PyTuple_SET_ITEM(ca, 0, PyInt_FromLong(self->pcareax));
|
||||
PyTuple_SET_ITEM(ca, 1, PyInt_FromLong(self->pcareay));
|
||||
PyTuple_SET_ITEM(ca, 2, PyInt_FromLong(self->pcareaw));
|
||||
PyTuple_SET_ITEM(ca, 3, PyInt_FromLong(self->pcareah));
|
||||
return ca;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PyErr_Format(PyExc_AttributeError, "no such attribute '%s'", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PyTypeObject PointerDataType = {
|
||||
PyObject_HEAD_INIT(NULL)
|
||||
0,
|
||||
"PointerData",
|
||||
sizeof(PointerData),
|
||||
0,
|
||||
(destructor) ptrdata_dealloc, /*tp_dealloc*/
|
||||
0, /*tp_print*/
|
||||
(getattrfunc) ptrdata_getattr, /*tp_getattr*/
|
||||
0, /*tp_setattr*/
|
||||
0, /*tp_compare*/
|
||||
0, /*tp_repr*/
|
||||
0, /*tp_as_number*/
|
||||
0, /*tp_as_sequence*/
|
||||
0, /*tp_as_mapping*/
|
||||
0, /*tp_hash */
|
||||
};
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
static gboolean translate(char *str, guint *state, guint *button)
|
||||
{
|
||||
char **parsed;
|
||||
char *l;
|
||||
int i;
|
||||
gboolean ret = FALSE;
|
||||
|
||||
parsed = g_strsplit(str, "-", -1);
|
||||
|
||||
/* first, find the button (last token) */
|
||||
l = NULL;
|
||||
for (i = 0; parsed[i] != NULL; ++i)
|
||||
l = parsed[i];
|
||||
if (l == NULL)
|
||||
goto translation_fail;
|
||||
|
||||
/* figure out the mod mask */
|
||||
*state = 0;
|
||||
for (i = 0; parsed[i] != l; ++i) {
|
||||
guint m = keyboard_translate_modifier(parsed[i]);
|
||||
if (!m) goto translation_fail;
|
||||
*state |= m;
|
||||
}
|
||||
|
||||
/* figure out the button */
|
||||
*button = atoi(l);
|
||||
if (!*button) {
|
||||
g_warning("Invalid button '%s' in pointer binding.", l);
|
||||
goto translation_fail;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
|
||||
translation_fail:
|
||||
g_strfreev(parsed);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void grab_button(Client *client, guint state, guint button,
|
||||
GQuark context, gboolean grab)
|
||||
{
|
||||
Window win;
|
||||
int mode = GrabModeAsync;
|
||||
unsigned int mask;
|
||||
|
||||
if (context == g_quark_try_string("frame")) {
|
||||
win = client->frame->window;
|
||||
mask = ButtonPressMask | ButtonMotionMask | ButtonReleaseMask;
|
||||
} else if (context == g_quark_try_string("client")) {
|
||||
win = client->frame->plate;
|
||||
mode = GrabModeSync; /* this is handled in pointer_event */
|
||||
mask = ButtonPressMask; /* can't catch more than this with Sync mode
|
||||
the release event is manufactured in
|
||||
pointer_fire */
|
||||
} else return;
|
||||
|
||||
if (grab)
|
||||
XGrabButton(ob_display, button, state, win, FALSE, mask, mode,
|
||||
GrabModeAsync, None, None);
|
||||
else
|
||||
XUngrabButton(ob_display, button, state, win);
|
||||
}
|
||||
|
||||
static void foreach_grab(GQuark key, gpointer data, gpointer user_data)
|
||||
{
|
||||
struct foreach_grab_temp *d = user_data;
|
||||
GSList *it;
|
||||
for (it = data; it != NULL; it = it->next) {
|
||||
PointerBinding *b = it->data;
|
||||
grab_button(d->client, b->state, b->button, key, d->grab);
|
||||
}
|
||||
}
|
||||
|
||||
void pointer_grab_all(Client *client, gboolean grab)
|
||||
{
|
||||
struct foreach_grab_temp bt;
|
||||
bt.client = client;
|
||||
bt.grab = grab;
|
||||
g_datalist_foreach(&bound_contexts, foreach_grab, &bt);
|
||||
}
|
||||
|
||||
static void grab_all_clients(gboolean grab)
|
||||
{
|
||||
GSList *it;
|
||||
|
||||
for (it = client_list; it != NULL; it = it->next)
|
||||
pointer_grab_all(it->data, grab);
|
||||
}
|
||||
|
||||
static gboolean grab_pointer(gboolean grab)
|
||||
{
|
||||
gboolean ret = TRUE;
|
||||
if (grab)
|
||||
ret = XGrabPointer(ob_display, ob_root, FALSE, (ButtonPressMask |
|
||||
ButtonReleaseMask |
|
||||
ButtonMotionMask |
|
||||
PointerMotionMask),
|
||||
GrabModeAsync, GrabModeAsync, None, None,
|
||||
CurrentTime) == GrabSuccess;
|
||||
else
|
||||
XUngrabPointer(ob_display, CurrentTime);
|
||||
if (ret) grabbed = grab;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void foreach_clear(GQuark key, gpointer data, gpointer user_data)
|
||||
{
|
||||
GSList *it;
|
||||
user_data = user_data;
|
||||
for (it = data; it != NULL; it = it->next) {
|
||||
int i;
|
||||
|
||||
PointerBinding *b = it->data;
|
||||
for (i = 0; i < NUM_ACTIONS; ++i)
|
||||
while (b->funcs[i] != NULL) {
|
||||
Py_DECREF((PyObject*)b->funcs[i]->data);
|
||||
b->funcs[i] = g_slist_delete_link(b->funcs[i], b->funcs[i]);
|
||||
}
|
||||
g_free(b->name);
|
||||
g_free(b);
|
||||
}
|
||||
g_slist_free(data);
|
||||
}
|
||||
|
||||
static void clearall()
|
||||
{
|
||||
grab_all_clients(FALSE);
|
||||
g_datalist_foreach(&bound_contexts, foreach_clear, NULL);
|
||||
}
|
||||
|
||||
static void fire_event(char *button, GQuark context, Action action,
|
||||
guint state, guint buttonnum, int posx, int posy,
|
||||
int pressposx, int pressposy, int pcareax,
|
||||
int pcareay, int pcareaw, int pcareah,
|
||||
PyObject *client, GSList *functions)
|
||||
{
|
||||
PyObject *ptrdata, *args, *ret;
|
||||
GSList *it;
|
||||
|
||||
ptrdata = ptrdata_new(button, context, action,
|
||||
state, buttonnum, posx, posy, pressposx, pressposy,
|
||||
pcareax, pcareay, pcareaw, pcareah);
|
||||
args = Py_BuildValue("OO", ptrdata, client);
|
||||
|
||||
if (grabbed) {
|
||||
ret = PyObject_CallObject(grab_func, args);
|
||||
if (ret == NULL) PyErr_Print();
|
||||
Py_XDECREF(ret);
|
||||
} else {
|
||||
for (it = functions; it != NULL; it = it->next) {
|
||||
ret = PyObject_CallObject(it->data, args);
|
||||
if (ret == NULL) PyErr_Print();
|
||||
Py_XDECREF(ret);
|
||||
}
|
||||
}
|
||||
|
||||
Py_DECREF(args);
|
||||
Py_DECREF(ptrdata);
|
||||
}
|
||||
|
||||
void pointer_event(XEvent *e, Client *c)
|
||||
{
|
||||
static guint button = 0, lastbutton = 0;
|
||||
static Time time = 0;
|
||||
static Rect carea;
|
||||
static guint pressx, pressy;
|
||||
GQuark contextq;
|
||||
gboolean click = FALSE, dblclick = FALSE;
|
||||
PyObject *client;
|
||||
GString *str = g_string_sized_new(0);
|
||||
guint state;
|
||||
GSList *it = NULL;
|
||||
PointerBinding *b = NULL;
|
||||
guint drag_threshold;
|
||||
|
||||
drag_threshold = configwrap_get_int("input", "drag_threshold");
|
||||
|
||||
contextq = engine_get_context(c, e->xany.window);
|
||||
|
||||
/* pick a button, figure out clicks/double clicks */
|
||||
switch (e->type) {
|
||||
case ButtonPress:
|
||||
if (!button) {
|
||||
button = e->xbutton.button;
|
||||
if (c != NULL) carea = c->frame->area;
|
||||
else carea.width = -1; /* indicates no client */
|
||||
pressx = e->xbutton.x_root;
|
||||
pressy = e->xbutton.y_root;
|
||||
}
|
||||
state = e->xbutton.state;
|
||||
break;
|
||||
case ButtonRelease:
|
||||
state = e->xbutton.state;
|
||||
break;
|
||||
case MotionNotify:
|
||||
state = e->xmotion.state;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!grabbed) {
|
||||
for (it = g_datalist_id_get_data(&bound_contexts, contextq);
|
||||
it != NULL; it = it->next) {
|
||||
b = it->data;
|
||||
if (b->state == state && b->button == button)
|
||||
break;
|
||||
}
|
||||
/* if not grabbed and not bound, then nothing to do! */
|
||||
if (it == NULL) return;
|
||||
}
|
||||
|
||||
if (c) client = clientwrap_new(c);
|
||||
else client = Py_None;
|
||||
|
||||
/* build the button string */
|
||||
if (state & ControlMask) g_string_append(str, "C-");
|
||||
if (state & ShiftMask) g_string_append(str, "S-");
|
||||
if (state & Mod1Mask) g_string_append(str, "Mod1-");
|
||||
if (state & Mod2Mask) g_string_append(str, "Mod2-");
|
||||
if (state & Mod3Mask) g_string_append(str, "Mod3-");
|
||||
if (state & Mod4Mask) g_string_append(str, "Mod4-");
|
||||
if (state & Mod5Mask) g_string_append(str, "Mod5-");
|
||||
g_string_append_printf(str, "%d", button);
|
||||
|
||||
/* figure out clicks/double clicks */
|
||||
switch (e->type) {
|
||||
case ButtonRelease:
|
||||
if (button == e->xbutton.button) {
|
||||
/* determine if this is a valid 'click'. Its not if the release is
|
||||
not over the window, or if a drag occured. */
|
||||
if (ABS(e->xbutton.x_root - pressx) < drag_threshold &&
|
||||
ABS(e->xbutton.y_root - pressy) < drag_threshold &&
|
||||
e->xbutton.x >= 0 && e->xbutton.y >= 0) {
|
||||
int junk;
|
||||
Window wjunk;
|
||||
guint ujunk, w, h;
|
||||
XGetGeometry(ob_display, e->xany.window, &wjunk, &junk, &junk,
|
||||
&w, &h, &ujunk, &ujunk);
|
||||
if (e->xbutton.x < (signed)w && e->xbutton.y < (signed)h)
|
||||
click =TRUE;
|
||||
}
|
||||
|
||||
/* determine if this is a valid 'double-click' */
|
||||
if (click) {
|
||||
if (lastbutton == button &&
|
||||
e->xbutton.time -
|
||||
configwrap_get_int("input", "double_click_rate") < time) {
|
||||
dblclick = TRUE;
|
||||
lastbutton = 0;
|
||||
} else
|
||||
lastbutton = button;
|
||||
} else
|
||||
lastbutton = 0;
|
||||
time = e->xbutton.time;
|
||||
pressx = pressy = 0;
|
||||
button = 0;
|
||||
carea.x = carea.y = carea.width = carea.height = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* fire off the events */
|
||||
switch (e->type) {
|
||||
case ButtonPress:
|
||||
fire_event(str->str, contextq, Action_Press,
|
||||
state, button, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
client, b == NULL ? NULL : b->funcs[Action_Press]);
|
||||
break;
|
||||
case ButtonRelease:
|
||||
fire_event(str->str, contextq, Action_Release,
|
||||
state, button, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
client, b == NULL ? NULL : b->funcs[Action_Release]);
|
||||
break;
|
||||
case MotionNotify:
|
||||
/* watch out for the drag threshold */
|
||||
if (ABS(e->xmotion.x_root - pressx) < drag_threshold &&
|
||||
ABS(e->xmotion.y_root - pressy) < drag_threshold)
|
||||
break;
|
||||
fire_event(str->str, contextq, Action_Motion,
|
||||
state, button, e->xmotion.x_root,
|
||||
e->xmotion.y_root, pressx, pressy,
|
||||
carea.x, carea.y, carea.width, carea.height,
|
||||
client, b == NULL ? NULL : b->funcs[Action_Motion]);
|
||||
break;
|
||||
}
|
||||
|
||||
if (click)
|
||||
fire_event(str->str, contextq, Action_Click,
|
||||
state, button, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
client, b == NULL ? NULL : b->funcs[Action_Click]);
|
||||
if (dblclick)
|
||||
fire_event(str->str, contextq, Action_DoubleClick,
|
||||
state, button, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
client, b == NULL ? NULL : b->funcs[Action_DoubleClick]);
|
||||
|
||||
g_string_free(str, TRUE);
|
||||
if (client != Py_None) { Py_DECREF(client); }
|
||||
|
||||
if (contextq == g_quark_try_string("client")) {
|
||||
/* Replay the event, so it goes to the client*/
|
||||
XAllowEvents(ob_display, ReplayPointer, CurrentTime);
|
||||
/* generate a release event since we don't get real ones */
|
||||
if (e->type == ButtonPress) {
|
||||
e->type = ButtonRelease;
|
||||
pointer_event(e, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
|
||||
Define the type 'Pointer'
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#define IS_POINTER(v) ((v)->ob_type == &PointerType)
|
||||
#define CHECK_POINTER(self, funcname) { \
|
||||
if (!IS_POINTER(self)) { \
|
||||
PyErr_SetString(PyExc_TypeError, \
|
||||
"descriptor '" funcname "' requires a 'Pointer' " \
|
||||
"object"); \
|
||||
return NULL; \
|
||||
} \
|
||||
}
|
||||
|
||||
typedef struct Pointer {
|
||||
PyObject_HEAD
|
||||
Action press;
|
||||
Action release;
|
||||
Action click;
|
||||
Action doubleclick;
|
||||
Action motion;
|
||||
} Pointer;
|
||||
|
||||
staticforward PyTypeObject PointerType;
|
||||
|
||||
static PyObject *ptr_bind(Pointer *self, PyObject *args)
|
||||
{
|
||||
char *buttonstr;
|
||||
char *contextstr;
|
||||
guint state, button;
|
||||
PointerBinding *b;
|
||||
GSList *it;
|
||||
GQuark context;
|
||||
PyObject *func;
|
||||
Action action;
|
||||
int i;
|
||||
|
||||
CHECK_POINTER(self, "grab");
|
||||
if (!PyArg_ParseTuple(args, "ssiO:grab",
|
||||
&buttonstr, &contextstr, &action, &func))
|
||||
return NULL;
|
||||
|
||||
if (!translate(buttonstr, &state, &button)) {
|
||||
PyErr_SetString(PyExc_ValueError, "invalid button");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
context = g_quark_try_string(contextstr);
|
||||
if (!context) {
|
||||
PyErr_SetString(PyExc_ValueError, "invalid context");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (action < 0 || action >= NUM_ACTIONS) {
|
||||
PyErr_SetString(PyExc_ValueError, "invalid action");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!PyCallable_Check(func)) {
|
||||
PyErr_SetString(PyExc_ValueError, "expected a callable object");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (it = g_datalist_id_get_data(&bound_contexts, context);
|
||||
it != NULL; it = it->next){
|
||||
b = it->data;
|
||||
if (b->state == state && b->button == button) {
|
||||
/* already bound */
|
||||
b->funcs[action] = g_slist_append(b->funcs[action], func);
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
}
|
||||
|
||||
grab_all_clients(FALSE);
|
||||
|
||||
/* add the binding */
|
||||
b = g_new(PointerBinding, 1);
|
||||
b->state = state;
|
||||
b->button = button;
|
||||
b->name = g_strdup(buttonstr);
|
||||
for (i = 0; i < NUM_ACTIONS; ++i)
|
||||
if (i != (signed)action) b->funcs[i] = NULL;
|
||||
b->funcs[action] = g_slist_append(NULL, func);
|
||||
g_datalist_id_set_data(&bound_contexts, context,
|
||||
g_slist_append(g_datalist_id_get_data(&bound_contexts, context), b));
|
||||
grab_all_clients(TRUE);
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
static PyObject *ptr_clearBinds(Pointer *self, PyObject *args)
|
||||
{
|
||||
CHECK_POINTER(self, "clearBinds");
|
||||
if (!PyArg_ParseTuple(args, ":clearBinds"))
|
||||
return NULL;
|
||||
clearall();
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
static PyObject *ptr_grab(Pointer *self, PyObject *args)
|
||||
{
|
||||
PyObject *func;
|
||||
|
||||
CHECK_POINTER(self, "grab");
|
||||
if (!PyArg_ParseTuple(args, "O:grab", &func))
|
||||
return NULL;
|
||||
if (!PyCallable_Check(func)) {
|
||||
PyErr_SetString(PyExc_ValueError, "expected a callable object");
|
||||
return NULL;
|
||||
}
|
||||
if (!grab_pointer(TRUE)) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "failed to grab pointer");
|
||||
return NULL;
|
||||
}
|
||||
grab_func = func;
|
||||
Py_INCREF(grab_func);
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
static PyObject *ptr_ungrab(Pointer *self, PyObject *args)
|
||||
{
|
||||
CHECK_POINTER(self, "ungrab");
|
||||
if (!PyArg_ParseTuple(args, ":ungrab"))
|
||||
return NULL;
|
||||
grab_pointer(FALSE);
|
||||
Py_XDECREF(grab_func);
|
||||
grab_func = NULL;
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
#define METH(n, d) {#n, (PyCFunction)ptr_##n, METH_VARARGS, #d}
|
||||
|
||||
static PyMethodDef PointerMethods[] = {
|
||||
METH(bind,
|
||||
"bind(button, context, func)\n\n"
|
||||
"Binds a pointer button for a context to a function. See the "
|
||||
"Terminology section for a decription and list of common contexts. "
|
||||
"The button is a string which defines a modifier and button "
|
||||
"combination with the format [Modifier-]...[Button]. Modifiers can "
|
||||
"be 'mod1', 'mod2', 'mod3', 'mod4', 'mod5', 'control', and 'shift'. "
|
||||
"The keys on your keyboard that are bound to each of these modifiers "
|
||||
"can be found by running 'xmodmap'. The button is the number of the "
|
||||
"button. Button numbers can be found by running 'xev', pressing the "
|
||||
"button with the pointer over its window, and watching its output. "
|
||||
"Here are some examples of valid buttons: 'control-1', '2', "
|
||||
"'mod1-shift-5'. The func must have a definition similar to "
|
||||
"'def func(keydata, client)'. A button and context may be bound to "
|
||||
"more than one function."),
|
||||
METH(clearBinds,
|
||||
"clearBinds()\n\n"
|
||||
"Removes all bindings that were previously made by bind()."),
|
||||
METH(grab,
|
||||
"grab(func)\n\n"
|
||||
"Grabs the pointer device, causing all possible pointer events to be "
|
||||
"sent to the given function. CAUTION: Be sure when you grab() that "
|
||||
"you also have an ungrab() that will execute, or you will not be "
|
||||
"able to use the pointer device until you restart Openbox. The func "
|
||||
"must have a definition similar to 'def func(keydata)'. The pointer "
|
||||
"cannot be grabbed if it is already grabbed."),
|
||||
METH(ungrab,
|
||||
"ungrab()\n\n"
|
||||
"Ungrabs the pointer. The pointer cannot be ungrabbed if it is not "
|
||||
"grabbed."),
|
||||
{ NULL, NULL, 0, NULL }
|
||||
};
|
||||
|
||||
static PyMemberDef PointerMembers[] = {
|
||||
{"Action_Press", T_INT, offsetof(Pointer, press), READONLY,
|
||||
"a pointer button press"},
|
||||
{"Action_Release", T_INT, offsetof(Pointer, release), READONLY,
|
||||
"a pointer button release"},
|
||||
{"Action_Click", T_INT, offsetof(Pointer, click), READONLY,
|
||||
"a pointer button click (press-release)"},
|
||||
{"Action_DoubleClick", T_INT, offsetof(Pointer, doubleclick), READONLY,
|
||||
"a pointer button double-click"},
|
||||
{"Action_Motion", T_INT, offsetof(Pointer, motion), READONLY,
|
||||
"a pointer drag"},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
/***************************************************************************
|
||||
|
||||
Type methods/struct
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
static void ptr_dealloc(PyObject *self)
|
||||
{
|
||||
PyObject_Del(self);
|
||||
}
|
||||
|
||||
static PyTypeObject PointerType = {
|
||||
PyObject_HEAD_INIT(NULL)
|
||||
0,
|
||||
"Pointer",
|
||||
sizeof(Pointer),
|
||||
0,
|
||||
(destructor) ptr_dealloc, /*tp_dealloc*/
|
||||
0, /*tp_print*/
|
||||
0, /*tp_getattr*/
|
||||
0, /*tp_setattr*/
|
||||
0, /*tp_compare*/
|
||||
0, /*tp_repr*/
|
||||
0, /*tp_as_number*/
|
||||
0, /*tp_as_sequence*/
|
||||
0, /*tp_as_mapping*/
|
||||
0, /*tp_hash */
|
||||
};
|
||||
|
||||
/**************************************************************************/
|
||||
|
||||
void pointer_startup()
|
||||
{
|
||||
PyObject *input, *inputdict;
|
||||
Pointer *ptr;
|
||||
|
||||
grabbed = FALSE;
|
||||
configwrap_add_int("input", "double_click_rate", "Double-Click Rate",
|
||||
"An integer containing the number of milliseconds in "
|
||||
"which 2 clicks must be received to cause a "
|
||||
"double-click event.", 300);
|
||||
configwrap_add_int("input", "drag_threshold", "Drag Threshold",
|
||||
"An integer containing the number of pixels a drag "
|
||||
"must go before motion events start getting generated. "
|
||||
"Once a drag has begun, the button release will not "
|
||||
"count as a click event.", 3);
|
||||
g_datalist_init(&bound_contexts);
|
||||
|
||||
PointerType.ob_type = &PyType_Type;
|
||||
PointerType.tp_methods = PointerMethods;
|
||||
PointerType.tp_members = PointerMembers;
|
||||
PyType_Ready(&PointerType);
|
||||
PyType_Ready(&PointerDataType);
|
||||
|
||||
/* get the input module/dict */
|
||||
input = PyImport_ImportModule("input"); /* new */
|
||||
g_assert(input != NULL);
|
||||
inputdict = PyModule_GetDict(input); /* borrowed */
|
||||
g_assert(inputdict != NULL);
|
||||
|
||||
/* add a Pointer instance to the input module */
|
||||
ptr = PyObject_New(Pointer, &PointerType);
|
||||
ptr->press = Action_Press;
|
||||
ptr->release = Action_Release;
|
||||
ptr->click = Action_Click;
|
||||
ptr->doubleclick = Action_DoubleClick;
|
||||
ptr->motion = Action_Motion;
|
||||
PyDict_SetItemString(inputdict, "Pointer", (PyObject*) ptr);
|
||||
Py_DECREF(ptr);
|
||||
|
||||
Py_DECREF(input);
|
||||
}
|
||||
|
||||
void pointer_shutdown()
|
||||
{
|
||||
if (grabbed)
|
||||
grab_pointer(FALSE);
|
||||
clearall();
|
||||
g_datalist_clear(&bound_contexts);
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
#ifndef __pointer_h
|
||||
#define __pointer_h
|
||||
|
||||
#include "client.h"
|
||||
#include <glib.h>
|
||||
|
||||
void pointer_startup();
|
||||
void pointer_shutdown();
|
||||
|
||||
void pointer_grab_all(Client *client, gboolean grab);
|
||||
|
||||
void pointer_event(XEvent *e, Client *c);
|
||||
|
||||
#endif
|
|
@ -1,6 +1,7 @@
|
|||
#include "../../kernel/focus.h"
|
||||
#include "../../kernel/dispatch.h"
|
||||
#include "../../kernel/openbox.h"
|
||||
#include "../../kernel/event.h"
|
||||
#include "../../kernel/grab.h"
|
||||
#include "../../kernel/action.h"
|
||||
#include "tree.h"
|
||||
|
@ -40,7 +41,7 @@ static void reset_chains()
|
|||
grabbed = FALSE;
|
||||
grab_keyboard(FALSE);
|
||||
} else
|
||||
XAllowEvents(ob_display, AsyncKeyboard, CurrentTime);
|
||||
XAllowEvents(ob_display, AsyncKeyboard, event_lasttime);
|
||||
}
|
||||
|
||||
gboolean kbind(GList *keylist, Action *action)
|
||||
|
@ -102,7 +103,8 @@ static void press(ObEvent *e, void *foo)
|
|||
if (!grabbed) {
|
||||
grab_keyboard(TRUE);
|
||||
grabbed = TRUE;
|
||||
XAllowEvents(ob_display, AsyncKeyboard, CurrentTime);
|
||||
XAllowEvents(ob_display, AsyncKeyboard,
|
||||
event_lasttime);
|
||||
}
|
||||
curpos = p;
|
||||
} else {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "../../kernel/openbox.h"
|
||||
#include "../../kernel/dispatch.h"
|
||||
#include "../../kernel/action.h"
|
||||
#include "../../kernel/event.h"
|
||||
#include "../../kernel/client.h"
|
||||
#include "../../kernel/frame.h"
|
||||
#include "../../kernel/grab.h"
|
||||
|
@ -219,7 +220,7 @@ static void event(ObEvent *e, void *foo)
|
|||
|
||||
if (context == g_quark_try_string("client")) {
|
||||
/* Replay the event, so it goes to the client*/
|
||||
XAllowEvents(ob_display, ReplayPointer, CurrentTime);
|
||||
XAllowEvents(ob_display, ReplayPointer, event_lasttime);
|
||||
/* Fall through to the release case! */
|
||||
} else
|
||||
break;
|
||||
|
|
Loading…
Reference in a new issue