allow "event bindings" via ebind() for new windows/window enter/leave
@@ -19,6 +19,10 @@ # If no 'L' is included in the string, one will be added to
# the end by Openbox. titlebar_layout = "ILC" +# double_click_delay - the number of milliseconds in which 2 clicks are +# perceived as a double-click +double_click_delay = 300 + ############################################################################# ### Options that can be modified by the user to change the default hooks' ### ### behaviors. ###
@@ -16,7 +16,6 @@ #include <stdio.h>
namespace ob { -const unsigned int OBActions::DOUBLECLICKDELAY = 300; const int OBActions::BUTTONS; OBActions::OBActions()@@ -24,6 +23,9 @@ : _button(0)
{ for (int i=0; i<BUTTONS; ++i) _posqueue[i] = new ButtonPressAction(); + + for (int i = 0; i < NUM_EVENTS; ++i) + _callback[i] = 0; }@@ -116,7 +118,12 @@ w->mcontext(), MouseClick);
Openbox::instance->bindings()->fireButton(data); - if (e.time - _release.time < DOUBLECLICKDELAY && + // XXX: dont load this every time!!@* + long dblclick; + if (!python_get_long("double_click_delay", &dblclick)) + dblclick = 300; + + if (e.time - _release.time < (unsigned)dblclick && _release.win == e.window && _release.button == e.button) { // run the DOUBLECLICK python hook@@ -142,12 +149,12 @@ void OBActions::enterHandler(const XCrossingEvent &e)
{ OtkEventHandler::enterHandler(e); - OBWidget *w = dynamic_cast<OBWidget*> - (Openbox::instance->findHandler(e.window)); - // run the ENTER python hook - doCallback(Action_EnterWindow, e.window, - (OBWidget::WidgetType)(w ? w->type():-1), e.state, 0, 0, 0, 0); + if (_callback[EventEnterWindow]) { + EventData *data = new_event_data(e.window, EventEnterWindow, e.state); + python_callback(_callback[EventEnterWindow], (PyObject*)data); + Py_DECREF((PyObject*)data); + } }@@ -155,19 +162,18 @@ void OBActions::leaveHandler(const XCrossingEvent &e)
{ OtkEventHandler::leaveHandler(e); - OBWidget *w = dynamic_cast<OBWidget*> - (Openbox::instance->findHandler(e.window)); - // run the LEAVE python hook - doCallback(Action_LeaveWindow, e.window, - (OBWidget::WidgetType)(w ? w->type():-1), e.state, 0, 0, 0, 0); + if (_callback[EventLeaveWindow]) { + EventData *data = new_event_data(e.window, EventLeaveWindow, e.state); + python_callback(_callback[EventLeaveWindow], (PyObject*)data); + Py_DECREF((PyObject*)data); + } } void OBActions::keyPressHandler(const XKeyEvent &e) { -// OBWidget *w = dynamic_cast<OBWidget*> -// (Openbox::instance->findHandler(e.window)); + OtkEventHandler::keyPressHandler(e); unsigned int state = e.state & (ControlMask | ShiftMask | Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);@@ -177,6 +183,8 @@
void OBActions::motionHandler(const XMotionEvent &e) { + OtkEventHandler::motionHandler(e); + if (!e.same_screen) return; // this just gets stupid int x_root = e.x_root, y_root = e.y_root;@@ -211,97 +219,66 @@ }
void OBActions::mapRequestHandler(const XMapRequestEvent &e) { - doCallback(Action_NewWindow, e.window, (OBWidget::WidgetType)-1, - 0, 0, 0, 0, 0); + OtkEventHandler::mapRequestHandler(e); + + if (_callback[EventNewWindow]) { + EventData *data = new_event_data(e.window, EventNewWindow, 0); + python_callback(_callback[EventNewWindow], (PyObject*)data); + Py_DECREF((PyObject*)data); + } } void OBActions::unmapHandler(const XUnmapEvent &e) { - (void)e; - doCallback(Action_CloseWindow, e.window, (OBWidget::WidgetType)-1, - 0, 0, 0, 0, 0); + OtkEventHandler::unmapHandler(e); + + if (_callback[EventCloseWindow]) { + EventData *data = new_event_data(e.window, EventCloseWindow, 0); + python_callback(_callback[EventCloseWindow], (PyObject*)data); + Py_DECREF((PyObject*)data); + } } void OBActions::destroyHandler(const XDestroyWindowEvent &e) { - (void)e; - doCallback(Action_CloseWindow, e.window, (OBWidget::WidgetType)-1, - 0, 0, 0, 0, 0); -} + OtkEventHandler::destroyHandler(e); -void OBActions::doCallback(ActionType action, Window window, - OBWidget::WidgetType type, unsigned int state, - unsigned int button, int xroot, int yroot, - Time time) -{ - std::pair<CallbackMap::iterator, CallbackMap::iterator> it_pair = - _callbacks.equal_range(action); - - CallbackMap::iterator it; -// for (it = it_pair.first; it != it_pair.second; ++it) -// python_callback(it->second, action, window, type, state, -// button, xroot, yroot, time); - // XXX do a callback + if (_callback[EventCloseWindow]) { + EventData *data = new_event_data(e.window, EventCloseWindow, 0); + python_callback(_callback[EventCloseWindow], (PyObject*)data); + Py_DECREF((PyObject*)data); + } } -bool OBActions::registerCallback(ActionType action, PyObject *func, - bool atfront) +bool OBActions::bind(EventAction action, PyObject *func) { - if (action < 0 || action >= OBActions::NUM_ACTIONS) { + if (action < 0 || action >= NUM_EVENTS) { return false; } - if (!func) - return false; - std::pair<CallbackMap::iterator, CallbackMap::iterator> it_pair = - _callbacks.equal_range(action); - - CallbackMap::iterator it; - for (it = it_pair.first; it != it_pair.second; ++it) - if (it->second == func) - return true; // already in there - if (atfront) - _callbacks.insert(_callbacks.begin(), CallbackMapPair(action, func)); - else - _callbacks.insert(CallbackMapPair(action, func)); + Py_XDECREF(_callback[action]); + _callback[action] = func; Py_INCREF(func); return true; } -bool OBActions::unregisterCallback(ActionType action, PyObject *func) +bool OBActions::unbind(EventAction action) { - if (action < 0 || action >= OBActions::NUM_ACTIONS) { + if (action < 0 || action >= NUM_EVENTS) { return false; } - if (!func) - return false; - - std::pair<CallbackMap::iterator, CallbackMap::iterator> it_pair = - _callbacks.equal_range(action); - CallbackMap::iterator it; - for (it = it_pair.first; it != it_pair.second; ++it) - if (it->second == func) - break; - if (it != it_pair.second) { // its been registered before - Py_DECREF(func); - _callbacks.erase(it); - } + Py_XDECREF(_callback[action]); + _callback[action] = 0; return true; } -bool OBActions::unregisterAllCallbacks(ActionType action) +void OBActions::unbindAll() { - if (action < 0 || action >= OBActions::NUM_ACTIONS) { - return false; - } - - while (!_callbacks.empty()) { - CallbackMap::iterator it = _callbacks.begin(); - Py_DECREF(it->second); - _callbacks.erase(it); + for (int i = 0; i < NUM_EVENTS; ++i) { + Py_XDECREF(_callback[i]); + _callback[i] = 0; } - return true; } }
@@ -27,15 +27,7 @@ guile code is run.
*/ class OBActions : public otk::OtkEventHandler { public: - // update the same enum in openbox.i when making changes to this - enum ActionType { - Action_EnterWindow, - Action_LeaveWindow, - Action_NewWindow, - Action_CloseWindow, - NUM_ACTIONS - }; - +#ifndef SWIG // get rid of a swig warning struct ButtonReleaseAction { Window win; unsigned int button;@@ -49,10 +41,9 @@ otk::Point pos;
otk::Rect clientarea; ButtonPressAction() { button = 0; } }; - +#endif // SWIG private: // milliseconds XXX: config option - static const unsigned int DOUBLECLICKDELAY; static const int BUTTONS = 5; //! The mouse button currently being watched from a press for a CLICK@@ -68,15 +59,9 @@
void insertPress(const XButtonEvent &e); void removePress(const XButtonEvent &e); - - typedef std::multimap<ActionType, PyObject*> CallbackMap; - typedef std::pair<ActionType, PyObject*> CallbackMapPair; - CallbackMap _callbacks; + + PyObject *_callback[NUM_EVENTS]; - void doCallback(ActionType action, Window window, OBWidget::WidgetType type, - unsigned int state, unsigned int button, - int xroot, int yroot, Time time); - public: //! Constructs an OBActions object OBActions();@@ -97,18 +82,14 @@ virtual void mapRequestHandler(const XMapRequestEvent &e);
virtual void unmapHandler(const XUnmapEvent &e); virtual void destroyHandler(const XDestroyWindowEvent &e); - //! Add a callback funtion to the back of the hook list - /*! - Registering functions for KeyPress events is pointless. Use - OBSCript::bindKey instead to do this. - */ - bool registerCallback(ActionType action, PyObject *func, bool atfront); + //! Bind a callback for an action + bool bind(EventAction action, PyObject *func); - //! Remove a callback function from the hook list - bool unregisterCallback(ActionType action, PyObject *func); + //! Unbind the callback function from an action + bool unbind(EventAction action); - //! Remove all callback functions from the hook list - bool unregisterAllCallbacks(ActionType action); + //! Remove all callback functions + void unbindAll(); }; }
@@ -24,8 +24,6 @@ %inline %{
ob::Openbox *Openbox_instance() { return ob::Openbox::instance; } %}; -%rename(register) python_register; - %ignore ob::OBScreen::clients; %{ #include <iterator>
@@ -2554,30 +2554,18 @@ return NULL;
} -static PyObject *_wrap_kunbind(PyObject *self, PyObject *args) { +static PyObject *_wrap_ebind(PyObject *self, PyObject *args) { PyObject *resultobj; - PyObject *arg1 = (PyObject *) 0 ; + int arg1 ; + PyObject *arg2 = (PyObject *) 0 ; PyObject *result; - PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; - if(!PyArg_ParseTuple(args,(char *)"O:kunbind",&obj0)) goto fail; - arg1 = obj0; - result = (PyObject *)ob::kunbind(arg1); + if(!PyArg_ParseTuple(args,(char *)"iO:ebind",&arg1,&obj1)) goto fail; + arg2 = obj1; + result = (PyObject *)ob::ebind((ob::EventAction )arg1,arg2); resultobj = result; - return resultobj; - fail: - return NULL; -} - - -static PyObject *_wrap_kunbind_all(PyObject *self, PyObject *args) { - PyObject *resultobj; - - if(!PyArg_ParseTuple(args,(char *)":kunbind_all")) goto fail; - ob::kunbind_all(); - - Py_INCREF(Py_None); resultobj = Py_None; return resultobj; fail: return NULL;@@ -2706,8 +2694,7 @@ { (char *)"OBClient_reparentHandler", _wrap_OBClient_reparentHandler, METH_VARARGS },
{ (char *)"OBClient_swigregister", OBClient_swigregister, METH_VARARGS }, { (char *)"mbind", _wrap_mbind, METH_VARARGS }, { (char *)"kbind", _wrap_kbind, METH_VARARGS }, - { (char *)"kunbind", _wrap_kunbind, METH_VARARGS }, - { (char *)"kunbind_all", _wrap_kunbind_all, METH_VARARGS }, + { (char *)"ebind", _wrap_ebind, METH_VARARGS }, { (char *)"set_reset_key", _wrap_set_reset_key, METH_VARARGS }, { NULL, NULL } };@@ -2867,6 +2854,11 @@ { SWIG_PY_INT, (char *)"NUM_MOUSE_ACTION", (long) ob::NUM_MOUSE_ACTION, 0, 0, 0},
{ SWIG_PY_INT, (char *)"KC_Menu", (long) ob::KC_Menu, 0, 0, 0}, { SWIG_PY_INT, (char *)"KC_All", (long) ob::KC_All, 0, 0, 0}, { SWIG_PY_INT, (char *)"NUM_KEY_CONTEXT", (long) ob::NUM_KEY_CONTEXT, 0, 0, 0}, +{ SWIG_PY_INT, (char *)"EventEnterWindow", (long) ob::EventEnterWindow, 0, 0, 0}, +{ SWIG_PY_INT, (char *)"EventLeaveWindow", (long) ob::EventLeaveWindow, 0, 0, 0}, +{ SWIG_PY_INT, (char *)"EventNewWindow", (long) ob::EventNewWindow, 0, 0, 0}, +{ SWIG_PY_INT, (char *)"EventCloseWindow", (long) ob::EventCloseWindow, 0, 0, 0}, +{ SWIG_PY_INT, (char *)"NUM_EVENTS", (long) ob::NUM_EVENTS, 0, 0, 0}, { SWIG_PY_INT, (char *)"X_PROTOCOL", (long) 11, 0, 0, 0}, { SWIG_PY_INT, (char *)"X_PROTOCOL_REVISION", (long) 0, 0, 0, 0}, { SWIG_PY_INT, (char *)"None", (long) 0L, 0, 0, 0},
@@ -171,6 +171,28 @@ "Return the time at which the event occured."},
{NULL, NULL, 0, NULL} }; +PyObject *EventData_action(EventData *self, PyObject *args) +{ + if(!PyArg_ParseTuple(args,":action")) return NULL; + return PyLong_FromLong((int)self->action); +} + +PyObject *EventData_modifiers(EventData *self, PyObject *args) +{ + if(!PyArg_ParseTuple(args,":modifiers")) return NULL; + return PyLong_FromUnsignedLong(self->state); +} + +static PyMethodDef EventData_methods[] = { + {"window", (PyCFunction)MotionData_window, METH_VARARGS, + "Return the client window id."}, + {"action", (PyCFunction)EventData_action, METH_VARARGS, + "Return the action being executed."}, + {"modifiers", (PyCFunction)EventData_modifiers, METH_VARARGS, + "Return the modifier keys state."}, + {NULL, NULL, 0, NULL} +}; + PyObject *KeyData_key(KeyData *self, PyObject *args) { if(!PyArg_ParseTuple(args,":key")) return NULL;@@ -199,6 +221,11 @@
static PyObject *ButtonDataGetAttr(PyObject *obj, char *name) { return Py_FindMethod(ButtonData_methods, obj, name); +} + +static PyObject *EventDataGetAttr(PyObject *obj, char *name) +{ + return Py_FindMethod(EventData_methods, obj, name); } static PyObject *KeyDataGetAttr(PyObject *obj, char *name)@@ -230,6 +257,18 @@ (getattrfunc)ButtonDataGetAttr,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; +static PyTypeObject EventData_Type = { + PyObject_HEAD_INIT(NULL) + 0, + "EventData", + sizeof(EventData), + 0, + dealloc, + 0, + (getattrfunc)EventDataGetAttr, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + static PyTypeObject KeyData_Type = { PyObject_HEAD_INIT(NULL) 0,@@ -276,6 +315,16 @@ data->state = state;
data->button = button; data->context= context; data->action = action; + return data; +} + +EventData *new_event_data(Window window, EventAction action, + unsigned int state) +{ + EventData *data = PyObject_New(EventData, &EventData_Type); + data->window = window; + data->action = action; + data->state = state; return data; }@@ -348,6 +397,15 @@ Py_XDECREF(result);
Py_DECREF(arglist); } +bool python_get_long(const char *name, long *value) +{ + PyObject *val = PyDict_GetItemString(obdict, const_cast<char*>(name)); + if (!(val && PyLong_Check(val))) return false; + + *value = PyLong_AsLong(val); + return true; +} + bool python_get_string(const char *name, std::string *value) { PyObject *val = PyDict_GetItemString(obdict, const_cast<char*>(name));@@ -374,65 +432,37 @@ // ************************************* //
// Stuff for calling from Python scripts // // ************************************* // -/* -PyObject * python_register(int action, PyObject *func, bool infront = false) +PyObject *mbind(const std::string &button, ob::MouseContext context, + ob::MouseAction action, PyObject *func) { if (!PyCallable_Check(func)) { PyErr_SetString(PyExc_TypeError, "Invalid callback function."); return NULL; } - - if (!ob::Openbox::instance->actions()->registerCallback( - (ob::OBActions::ActionType)action, func, infront)) { - PyErr_SetString(PyExc_RuntimeError, "Unable to register action callback."); + + if (!ob::Openbox::instance->bindings()->addButton(button, context, + action, func)) { + PyErr_SetString(PyExc_RuntimeError,"Unable to add binding."); return NULL; } Py_INCREF(Py_None); return Py_None; } -PyObject *unregister(int action, PyObject *func) -{ - if (!PyCallable_Check(func)) { - PyErr_SetString(PyExc_TypeError, "Invalid callback function."); - return NULL; - } - - if (!ob::Openbox::instance->actions()->unregisterCallback( - (ob::OBActions::ActionType)action, func)) { - PyErr_SetString(PyExc_RuntimeError, "Unable to unregister action callback."); - return NULL; - } - Py_INCREF(Py_None); return Py_None; -} - -PyObject *unregister_all(int action) -{ - if (!ob::Openbox::instance->actions()->unregisterAllCallbacks( - (ob::OBActions::ActionType)action)) { - PyErr_SetString(PyExc_RuntimeError, - "Unable to unregister action callbacks."); - return NULL; - } - Py_INCREF(Py_None); return Py_None; -} -*/ -PyObject * mbind(const std::string &button, ob::MouseContext context, - ob::MouseAction action, PyObject *func) +PyObject *ebind(ob::EventAction action, PyObject *func) { if (!PyCallable_Check(func)) { PyErr_SetString(PyExc_TypeError, "Invalid callback function."); return NULL; } - if (!ob::Openbox::instance->bindings()->addButton(button, context, - action, func)) { + if (!ob::Openbox::instance->actions()->bind(action, func)) { PyErr_SetString(PyExc_RuntimeError,"Unable to add binding."); return NULL; } Py_INCREF(Py_None); return Py_None; } -PyObject * kbind(PyObject *keylist, ob::KeyContext context, PyObject *func) +PyObject *kbind(PyObject *keylist, ob::KeyContext context, PyObject *func) { if (!PyCallable_Check(func)) { PyErr_SetString(PyExc_TypeError, "Invalid callback function.");@@ -461,7 +491,7 @@ }
Py_INCREF(Py_None); return Py_None; } -PyObject * kunbind(PyObject *keylist) +PyObject *kunbind(PyObject *keylist) { if (!PyList_Check(keylist)) { PyErr_SetString(PyExc_TypeError, "Invalid keylist. Not a list.");
@@ -48,6 +48,14 @@ KC_All,
NUM_KEY_CONTEXT }; +enum EventAction { + EventEnterWindow, + EventLeaveWindow, + EventNewWindow, + EventCloseWindow, + NUM_EVENTS +}; + #ifndef SWIG // *** MotionData can be (and is) cast ButtonData!! (in actions.cc) *** //@@ -79,6 +87,13 @@ unsigned int button;
MouseContext context; MouseAction action; } ButtonData; + +typedef struct { + PyObject_HEAD; + Window window; + unsigned int state; + EventAction action; +} EventData; typedef struct { PyObject_HEAD;@@ -100,21 +115,25 @@ const otk::Rect &initarea);
ButtonData *new_button_data(Window window, Time time, unsigned int state, unsigned int button, MouseContext context, MouseAction action); +EventData *new_event_data(Window window, EventAction action, + unsigned int state); KeyData *new_key_data(Window window, Time time, unsigned int state, unsigned int key); void python_callback(PyObject *func, PyObject *data); +bool python_get_long(const char *name, long *value); bool python_get_string(const char *name, std::string *value); bool python_get_stringlist(const char *name, std::vector<std::string> *value); #endif -PyObject * mbind(const std::string &button, ob::MouseContext context, - ob::MouseAction action, PyObject *func); +PyObject *mbind(const std::string &button, ob::MouseContext context, + ob::MouseAction action, PyObject *func); -PyObject * kbind(PyObject *keylist, ob::KeyContext context, PyObject *func); -PyObject * kunbind(PyObject *keylist); -void kunbind_all(); +PyObject *kbind(PyObject *keylist, ob::KeyContext context, PyObject *func); + +PyObject *ebind(ob::EventAction action, PyObject *func); + void set_reset_key(const std::string &key); }