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
-// -*- mode: C++; indent-tabs-mode: nil; -*-
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
#ifdef HAVE_CONFIG_H
# include "../config.h"
-// -*- mode: C++; indent-tabs-mode: nil; -*-
+// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
#ifndef __actions_hh
#define __actions_hh
--- /dev/null
+// -*- 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 <X11/Xlib.h>
+}
+
+namespace ob {
+
+#include <stdio.h>
+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<char *>(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("<BUILDING>\n");
+ print_branch(ret);
+ printf("</BUILDING>\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);
+}
+
+}
--- /dev/null
+// -*- 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 <string>
+#include <vector>
+
+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<std::string> 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
#include "client.hh"
#include "screen.hh"
#include "actions.hh"
+#include "bindings.hh"
#include "otk/property.hh"
#include "otk/display.hh"
#include "otk/assassin.hh"
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
_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();
class OBScreen;
class OBClient;
class OBActions;
+class OBBindings;
//! Mouse cursors used throughout Openbox
struct Cursors {
//! 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;
//! 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());
%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;
%{
#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) -------- */
}
+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 ;
}
+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 },
{ (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 },
{ (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 }
};
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}};
_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,
// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
#include "python.hh"
+#include "openbox.hh"
#include <vector>
#include <algorithm>
typedef std::vector<PyObject*> FunctionList;
static FunctionList callbacks[OBActions::NUM_ACTIONS];
+static FunctionList bindfuncs;
bool python_register(int action, PyObject *callback)
{
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;
+}
+
+
}
#include "actions.hh"
#include "widget.hh"
+#include "bindings.hh"
extern "C" {
#include <Python.h>
//! 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,