From 98c4b4cfe58c712e70575d8d8b00e7a261a2e729 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Mon, 30 Dec 2002 06:31:45 +0000 Subject: [PATCH] keybindings underway. dont work yet --- src/Makefile.am | 2 +- src/actions.cc | 2 +- src/actions.hh | 2 +- src/bindings.cc | 257 ++++++++++++++++++++++++++++++++++++++++++++ src/bindings.hh | 91 ++++++++++++++++ src/openbox.cc | 21 +++- src/openbox.hh | 7 ++ src/openbox.i | 3 + src/openbox_wrap.cc | 83 +++++++++++++- src/python.cc | 81 ++++++++++++++ src/python.hh | 13 +++ 11 files changed, 554 insertions(+), 8 deletions(-) create mode 100644 src/bindings.cc create mode 100644 src/bindings.hh diff --git a/src/Makefile.am b/src/Makefile.am index e406de3f..a8995148 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,7 +19,7 @@ openbox3_LDADD=-L../otk -lotk @LIBINTL@ openbox3_SOURCES= actions.cc client.cc frame.cc openbox.cc screen.cc \ main.cc rootwindow.cc backgroundwidget.cc labelwidget.cc \ - buttonwidget.cc python.cc openbox_wrap.cc + buttonwidget.cc python.cc bindings.cc openbox_wrap.cc MAINTAINERCLEANFILES= Makefile.in diff --git a/src/actions.cc b/src/actions.cc index 0dbe4640..716b033b 100644 --- a/src/actions.cc +++ b/src/actions.cc @@ -1,4 +1,4 @@ -// -*- mode: C++; indent-tabs-mode: nil; -*- +// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*- #ifdef HAVE_CONFIG_H # include "../config.h" diff --git a/src/actions.hh b/src/actions.hh index 4cf37ef1..f210f6ce 100644 --- a/src/actions.hh +++ b/src/actions.hh @@ -1,4 +1,4 @@ -// -*- mode: C++; indent-tabs-mode: nil; -*- +// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*- #ifndef __actions_hh #define __actions_hh diff --git a/src/bindings.cc b/src/bindings.cc new file mode 100644 index 00000000..4bb651aa --- /dev/null +++ b/src/bindings.cc @@ -0,0 +1,257 @@ +// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*- + +#ifdef HAVE_CONFIG_H +# include "../config.h" +#endif + +#include "bindings.hh" +#include "otk/display.hh" + +extern "C" { +#include +} + +namespace ob { + +#include +static void print_branch(BindingTree *first, std::string str) +{ + BindingTree *p = first; + + while (p) { + if (p->first_child) + print_branch(p->first_child, str + " " + p->text); + printf("%d%s\n", p->id, (str + " " + p->text).c_str()); + BindingTree *s = p->next_sibling; + delete p; + p = s; + } +} + + +void OBBindings::display() +{ + if (_bindings.first_child) + print_branch(_bindings.first_child, ""); +} + + + +static bool translate(const std::string str, Binding &b) +{ + KeySym sym = XStringToKeysym(const_cast(str.c_str())); + if (sym == NoSymbol) return false; + b.modifiers = Mod1Mask; + b.key = XKeysymToKeycode(otk::OBDisplay::display, sym); + return b.key != 0; +} + +static BindingTree *buildtree(const OBBindings::StringVect &keylist, int id) +{ + if (keylist.empty()) return 0; // nothing in the list.. return 0 + + BindingTree *ret = new BindingTree(id), *p = 0; + + OBBindings::StringVect::const_iterator it, end = keylist.end(); + for (it = keylist.begin(); it != end; ++it) { + if (p) + p = p->first_child = new BindingTree(id); + else + p = ret; // the first node + + if (!translate(*it, p->binding)) + break; + p->text = *it; + } + if (it != end) { + // build failed.. clean up and return 0 + p = ret; + while (p->first_child) { + BindingTree *c = p->first_child; + delete p; + p = c; + } + delete p; + return 0; + } else { + // set the proper chain status on the last node + p->chain = false; + } + + printf("\n"); + print_branch(ret); + printf("\n"); + + // successfully built a tree + return ret; +} + +static void destroytree(BindingTree *tree) +{ + while (tree) { + BindingTree *c = tree->first_child; + delete tree; + tree = c; + } +} + +OBBindings::OBBindings() +{ +} + + +OBBindings::~OBBindings() +{ + remove_all(); +} + + +static void assimilate(BindingTree *parent, BindingTree *node) +{ + BindingTree *p, *lastsib, *nextparent, *nextnode = node->first_child; + + if (!parent->first_child) { + // there are no nodes at this level yet + parent->first_child = node; + nextparent = node; + } else { + p = lastsib = parent->first_child; + + while (p->next_sibling) { + p = p->next_sibling; + lastsib = p; // finds the last sibling + if (p->binding == node->binding) { + // found an identical binding.. + assert(node->chain && p->chain); + delete node; // kill the one we aren't using + break; + } + } + if (!p) { + // couldn't find an existing binding, use this new one, and insert it + // into the list + p = lastsib->next_sibling = node; + } + nextparent = p; + } + + if (nextnode) + assimilate(nextparent, nextnode); +} + + +static int find_bind(BindingTree *tree, BindingTree *search) { + BindingTree *a, *b; + a = tree; + b = search; + while (a && b) { + if (a->binding != b->binding) { + a = a->next_sibling; + } else { + if (a->chain == b->chain) { + if (!a->chain) + return a->id; // found it! (return the actual id, not the search's) + } else + return -2; // the chain status' don't match (conflict!) + b = b->first_child; + a = a->first_child; + } + } + return -1; // it just isn't in here +} + +/* +static int find(BindingTree *parent, BindingTree *node) { + BindingTree *p, *lastsib, *nextparent, *nextnode = node->first_child; + + if (!parent->first_child) + return -1; + + p = parent->first_child; + while (p) { + if (node->binding == p->binding) { + if (node->chain == p->chain) { + if (!node->chain) { + return p->id; // found it! (return the actual id, not the search's) + } else { + break; // go on to the next child in the chain + } + } else { + return -2; // the chain status' don't match (conflict!) + } + } + p = p->next_sibling; + } + if (!p) return -1; // doesn't exist + + if (node->chain) { + assert(node->first_child); + return find(p, node->first_child); + } else + return -1; // it just isnt in here +} +*/ + +bool OBBindings::add(const StringVect &keylist, int id) +{ + BindingTree *tree; + + if (!(tree = buildtree(keylist, id))) + return false; // invalid binding requested + + if (find_bind(_bindings.first_child, tree) < -1) { + // conflicts with another binding + destroytree(tree); + return false; + } + + // assimilate this built tree into the main tree + assimilate(&_bindings, tree); // assimilation destroys/uses the tree + return true; +} + + +int OBBindings::find(const StringVect &keylist) +{ + BindingTree *tree; + bool ret; + + if (!(tree = buildtree(keylist, 0))) + return false; // invalid binding requested + + ret = find_bind(_bindings.first_child, tree) >= 0; + + destroytree(tree); + + return ret; +} + + +int OBBindings::remove(const StringVect &keylist) +{ + (void)keylist; + assert(false); // XXX: function not implemented yet +} + + +static void remove_branch(BindingTree *first) +{ + BindingTree *p = first; + + while (p) { + if (p->first_child) + remove_branch(p->first_child); + BindingTree *s = p->next_sibling; + delete p; + p = s; + } +} + + +void OBBindings::remove_all() +{ + if (_bindings.first_child) + remove_branch(_bindings.first_child); +} + +} diff --git a/src/bindings.hh b/src/bindings.hh new file mode 100644 index 00000000..70b2326d --- /dev/null +++ b/src/bindings.hh @@ -0,0 +1,91 @@ +// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*- +#ifndef __binding_hh +#define __binding_hh + +/*! @file binding.hh + @brief I dunno.. some binding stuff? +*/ + +#include +#include + +namespace ob { + +typedef struct Binding { + unsigned int modifiers; + unsigned int key; + + bool operator==(struct Binding &b2) { return key == b2.key && + modifiers == b2.modifiers; } + bool operator!=(struct Binding &b2) { return key != b2.key || + modifiers != b2.modifiers; } + Binding(unsigned int mod, unsigned int k) { modifiers = mod; key = k; } +} Binding; + +typedef struct BindingTree { + Binding binding; + std::string text; + int id; // the id given for the binding in add() + bool chain; // true if this is a chain to another key (not an action) + + struct BindingTree *next_sibling; // the next binding in the tree at the same + // level + struct BindingTree *first_child; // the first child of this binding (next + // binding in a chained sequence). + BindingTree(int id) : binding(0, 0) { + this->id = id; chain = true; next_sibling = first_child = 0; + } + BindingTree() : binding(0, 0) { + this->id = -1; chain = true; next_sibling = first_child = 0; + } +} BindingTree; + +class OBBindings { +public: + //! A list of strings + typedef std::vector StringVect; + +private: + BindingTree _bindings;// root nodes (these dont have siblings!) + +public: + //! Initializes an OBBinding object + OBBindings(); + //! Destroys the OBBinding object + virtual ~OBBindings(); + + //! Adds a new binding + /*! + A binding will fail to be added if the binding already exists (as part of + a chain or not), or if any of the strings in the keylist are invalid. + @return true if the binding could be added; false if it could not. + */ + bool add(const StringVect &keylist, int id); + + //! Removes a key binding + /*! + @return The id of the binding that was removed, or '< 0' if none were + removed. + */ + int remove(const StringVect &keylist); + + //! Removes all key bindings + void remove_all(); + + //! Finds a keybinding and returns its id or '< 0' if it isn't found. + /*! + @return -1 if the keybinding was not found but does not conflict with + any others; -2 if the keybinding conflicts with another. + */ + int find(const StringVect &keylist); + + // XXX: need an exec() function or something that will be used by openbox + // and hold state for which chain we're in etc. (it could have a timer + // for reseting too...) + + void display(); +}; + +} + +#endif // __binding_hh diff --git a/src/openbox.cc b/src/openbox.cc index ad317183..952fca51 100644 --- a/src/openbox.cc +++ b/src/openbox.cc @@ -9,6 +9,7 @@ #include "client.hh" #include "screen.hh" #include "actions.hh" +#include "bindings.hh" #include "otk/property.hh" #include "otk/display.hh" #include "otk/assassin.hh" @@ -145,8 +146,22 @@ Openbox::Openbox(int argc, char **argv) sigaction(SIGHUP, &action, (struct sigaction *) 0); _property = new otk::OBProperty(); - _actions = new OBActions(); + _bindings = new OBBindings(); + + OBBindings::StringVect v; +// v.push_back("C-x"); +// v.push_back("C-y"); + v.push_back("v"); + _bindings->add(v, 1); + v.clear(); +// v.push_back("C-x"); +// v.push_back("C-z"); + v.push_back("a"); + _bindings->add(v, 2); + + _bindings->display(); + ::exit(0); setMasterHandler(_actions); // set as the master event handler @@ -198,6 +213,10 @@ Openbox::~Openbox() _state = State_Exiting; // time to kill everything std::for_each(_screens.begin(), _screens.end(), otk::PointerAssassin()); + + delete _bindings; + delete _actions; + delete _property; // close the X display otk::OBDisplay::destroy(); diff --git a/src/openbox.hh b/src/openbox.hh index 976a4977..d64068f1 100644 --- a/src/openbox.hh +++ b/src/openbox.hh @@ -30,6 +30,7 @@ namespace ob { class OBScreen; class OBClient; class OBActions; +class OBBindings; //! Mouse cursors used throughout Openbox struct Cursors { @@ -121,6 +122,9 @@ private: //! The action interface through which all user-available actions occur OBActions *_actions; + //! The interface through which keys/buttons are grabbed and handled + OBBindings *_bindings; + //! Run the application in synchronous mode? (for debugging) bool _sync; @@ -184,6 +188,9 @@ public: //! Returns the otk::OBProperty instance for the window manager inline const otk::OBProperty *property() const { return _property; } + //! Returns the OBBinding instance for the window manager + inline OBBindings *bindings() const { return _bindings; } + //! Returns a managed screen inline OBScreen *screen(int num) { assert(num >= 0); assert(num < (signed)_screens.size()); diff --git a/src/openbox.i b/src/openbox.i index 4d11507c..d4b83575 100644 --- a/src/openbox.i +++ b/src/openbox.i @@ -56,6 +56,9 @@ %rename(preregister) ob::python_preregister; %rename(unregister) ob::python_unregister; %rename(unregister_all) ob::python_unregister_all; +%rename(bind) ob::python_bind; +%rename(unbind) ob::python_unbind; +%rename(unbind_all) ob::python_unbind_all; %ignore ob::OBScreen::clients; %{ diff --git a/src/openbox_wrap.cc b/src/openbox_wrap.cc index 8337d1ee..f9945024 100644 --- a/src/openbox_wrap.cc +++ b/src/openbox_wrap.cc @@ -667,10 +667,11 @@ SWIG_InstallConstants(PyObject *d, swig_const_info constants[]) { #define SWIGTYPE_p_XDestroyWindowEvent swig_types[19] #define SWIGTYPE_p_otk__BImageControl swig_types[20] #define SWIGTYPE_p_PyObject swig_types[21] -#define SWIGTYPE_p_ob__MwmHints swig_types[22] -#define SWIGTYPE_p_otk__Configuration swig_types[23] -#define SWIGTYPE_p_XUnmapEvent swig_types[24] -static swig_type_info *swig_types[26]; +#define SWIGTYPE_p_ob__OBBindings swig_types[22] +#define SWIGTYPE_p_ob__MwmHints swig_types[23] +#define SWIGTYPE_p_otk__Configuration swig_types[24] +#define SWIGTYPE_p_XUnmapEvent swig_types[25] +static swig_type_info *swig_types[27]; /* -------- TYPES TABLE (END) -------- */ @@ -1112,6 +1113,23 @@ static PyObject *_wrap_Openbox_property(PyObject *self, PyObject *args) { } +static PyObject *_wrap_Openbox_bindings(PyObject *self, PyObject *args) { + PyObject *resultobj; + ob::Openbox *arg1 = (ob::Openbox *) 0 ; + ob::OBBindings *result; + PyObject * obj0 = 0 ; + + if(!PyArg_ParseTuple(args,(char *)"O:Openbox_bindings",&obj0)) goto fail; + if ((SWIG_ConvertPtr(obj0,(void **) &arg1, SWIGTYPE_p_ob__Openbox,SWIG_POINTER_EXCEPTION | 0 )) == -1) SWIG_fail; + result = (ob::OBBindings *)((ob::Openbox const *)arg1)->bindings(); + + resultobj = SWIG_NewPointerObj((void *) result, SWIGTYPE_p_ob__OBBindings, 0); + return resultobj; + fail: + return NULL; +} + + static PyObject *_wrap_Openbox_screen(PyObject *self, PyObject *args) { PyObject *resultobj; ob::Openbox *arg1 = (ob::Openbox *) 0 ; @@ -2551,6 +2569,57 @@ static PyObject *_wrap_unregister_all(PyObject *self, PyObject *args) { } +static PyObject *_wrap_bind(PyObject *self, PyObject *args) { + PyObject *resultobj; + PyObject *arg1 = (PyObject *) 0 ; + PyObject *arg2 = (PyObject *) 0 ; + bool result; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + + if(!PyArg_ParseTuple(args,(char *)"OO:bind",&obj0,&obj1)) goto fail; + arg1 = obj0; + arg2 = obj1; + result = (bool)ob::python_bind(arg1,arg2); + + resultobj = PyInt_FromLong((long)result); + return resultobj; + fail: + return NULL; +} + + +static PyObject *_wrap_unbind(PyObject *self, PyObject *args) { + PyObject *resultobj; + PyObject *arg1 = (PyObject *) 0 ; + bool result; + PyObject * obj0 = 0 ; + + if(!PyArg_ParseTuple(args,(char *)"O:unbind",&obj0)) goto fail; + arg1 = obj0; + result = (bool)ob::python_unbind(arg1); + + resultobj = PyInt_FromLong((long)result); + return resultobj; + fail: + return NULL; +} + + +static PyObject *_wrap_unbind_all(PyObject *self, PyObject *args) { + PyObject *resultobj; + bool result; + + if(!PyArg_ParseTuple(args,(char *)":unbind_all")) goto fail; + result = (bool)ob::python_unbind_all(); + + resultobj = PyInt_FromLong((long)result); + return resultobj; + fail: + return NULL; +} + + static PyMethodDef SwigMethods[] = { { (char *)"Openbox_instance", _wrap_Openbox_instance, METH_VARARGS }, { (char *)"Cursors_session_set", _wrap_Cursors_session_set, METH_VARARGS }, @@ -2569,6 +2638,7 @@ static PyMethodDef SwigMethods[] = { { (char *)"Openbox_state", _wrap_Openbox_state, METH_VARARGS }, { (char *)"Openbox_timerManager", _wrap_Openbox_timerManager, METH_VARARGS }, { (char *)"Openbox_property", _wrap_Openbox_property, METH_VARARGS }, + { (char *)"Openbox_bindings", _wrap_Openbox_bindings, METH_VARARGS }, { (char *)"Openbox_screen", _wrap_Openbox_screen, METH_VARARGS }, { (char *)"Openbox_screenCount", _wrap_Openbox_screenCount, METH_VARARGS }, { (char *)"Openbox_cursors", _wrap_Openbox_cursors, METH_VARARGS }, @@ -2649,6 +2719,9 @@ static PyMethodDef SwigMethods[] = { { (char *)"preregister", _wrap_preregister, METH_VARARGS }, { (char *)"unregister", _wrap_unregister, METH_VARARGS }, { (char *)"unregister_all", _wrap_unregister_all, METH_VARARGS }, + { (char *)"bind", _wrap_bind, METH_VARARGS }, + { (char *)"unbind", _wrap_unbind, METH_VARARGS }, + { (char *)"unbind_all", _wrap_unbind_all, METH_VARARGS }, { NULL, NULL } }; @@ -2689,6 +2762,7 @@ static swig_type_info _swigt__p_XPropertyEvent[] = {{"_p_XPropertyEvent", 0, "XP static swig_type_info _swigt__p_XDestroyWindowEvent[] = {{"_p_XDestroyWindowEvent", 0, "XDestroyWindowEvent *", 0},{"_p_XDestroyWindowEvent"},{0}}; static swig_type_info _swigt__p_otk__BImageControl[] = {{"_p_otk__BImageControl", 0, "otk::BImageControl *", 0},{"_p_otk__BImageControl"},{0}}; static swig_type_info _swigt__p_PyObject[] = {{"_p_PyObject", 0, "PyObject *", 0},{"_p_PyObject"},{0}}; +static swig_type_info _swigt__p_ob__OBBindings[] = {{"_p_ob__OBBindings", 0, "ob::OBBindings *", 0},{"_p_ob__OBBindings"},{0}}; static swig_type_info _swigt__p_ob__MwmHints[] = {{"_p_ob__MwmHints", 0, "ob::MwmHints *", 0},{"_p_ob__MwmHints"},{0}}; static swig_type_info _swigt__p_otk__Configuration[] = {{"_p_otk__Configuration", 0, "otk::Configuration *", 0},{"_p_otk__Configuration"},{0}}; static swig_type_info _swigt__p_XUnmapEvent[] = {{"_p_XUnmapEvent", 0, "XUnmapEvent *", 0},{"_p_XUnmapEvent"},{0}}; @@ -2716,6 +2790,7 @@ _swigt__p_XPropertyEvent, _swigt__p_XDestroyWindowEvent, _swigt__p_otk__BImageControl, _swigt__p_PyObject, +_swigt__p_ob__OBBindings, _swigt__p_ob__MwmHints, _swigt__p_otk__Configuration, _swigt__p_XUnmapEvent, diff --git a/src/python.cc b/src/python.cc index ed171f9c..8f2e4f5a 100644 --- a/src/python.cc +++ b/src/python.cc @@ -1,6 +1,7 @@ // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*- #include "python.hh" +#include "openbox.hh" #include #include @@ -10,6 +11,7 @@ namespace ob { typedef std::vector FunctionList; static FunctionList callbacks[OBActions::NUM_ACTIONS]; +static FunctionList bindfuncs; bool python_register(int action, PyObject *callback) { @@ -125,4 +127,83 @@ void python_callback(OBActions::ActionType action, Window window, Py_DECREF(arglist); } + + + + + +bool python_bind(PyObject *keylist, PyObject *callback) +{ + if (!PyList_Check(keylist)) { + PyErr_SetString(PyExc_AssertionError, "Invalid keylist. Not a list."); + return false; + } + if (!PyCallable_Check(callback)) { + PyErr_SetString(PyExc_AssertionError, "Invalid callback function."); + return false; + } + + OBBindings::StringVect vectkeylist; + for (int i = 0, end = PyList_Size(keylist); i < end; ++i) { + PyObject *str = PyList_GetItem(keylist, i); + if (!PyString_Check(str)) { + PyErr_SetString(PyExc_AssertionError, + "Invalid keylist. It must contain only strings."); + return false; + } + vectkeylist.push_back(PyString_AsString(str)); + } + + // the id is what the binding class can call back with so it doesnt have to + // worry about the python function pointer + int id = bindfuncs.size(); + if (Openbox::instance->bindings()->add(vectkeylist, id)) { + Py_XINCREF(callback); // Add a reference to new callback + bindfuncs.push_back(callback); + return true; + } else { + PyErr_SetString(PyExc_AssertionError,"Unable to create binding. Invalid."); + return false; + } +} + +bool python_unbind(PyObject *keylist) +{ + if (!PyList_Check(keylist)) { + PyErr_SetString(PyExc_AssertionError, "Invalid keylist. Not a list."); + return false; + } + + OBBindings::StringVect vectkeylist; + for (int i = 0, end = PyList_Size(keylist); i < end; ++i) { + PyObject *str = PyList_GetItem(keylist, i); + if (!PyString_Check(str)) { + PyErr_SetString(PyExc_AssertionError, + "Invalid keylist. It must contain only strings."); + return false; + } + vectkeylist.push_back(PyString_AsString(str)); + } + + int id; + if ((id = + Openbox::instance->bindings()->remove(vectkeylist)) >= 0) { + assert(bindfuncs[id]); // shouldn't be able to remove it twice + Py_XDECREF(bindfuncs[id]); // Dispose of previous callback + // important note: we don't erase the item from the list cuz that would + // ruin all the id's that are in use. simply nullify it. + bindfuncs[id] = 0; + return true; + } + + return false; +} + +bool python_unbind_all() +{ + Openbox::instance->bindings()->remove_all(); + return true; +} + + } diff --git a/src/python.hh b/src/python.hh index 496b459d..8a1bb161 100644 --- a/src/python.hh +++ b/src/python.hh @@ -8,6 +8,7 @@ #include "actions.hh" #include "widget.hh" +#include "bindings.hh" extern "C" { #include @@ -25,6 +26,18 @@ bool python_unregister(int action, PyObject *callback); //! Removes all python callback functions from the hook list bool python_unregister_all(int action); +//! Add a mouse/keybinding +/*! + @param keylist A python list of modifier/key/buttons, in the form: + "C-A-space" or "A-Button1" etc. + @param callback A python function to call when the binding is used. +*/ +bool python_bind(PyObject *keylist, PyObject *callback); + +bool python_unbind(PyObject *keylist); + +bool python_unbind_all(); + //! Fire a python callback function void python_callback(OBActions::ActionType action, Window window, OBWidget::WidgetType type, unsigned int state, -- 2.39.2