--- /dev/null
+import Bindings, Node
+
+class Action(Node.Node):
+
+ def __init__(self, name):
+ Node.Node.__init__(self)
+ self.name = name
+
+ def __call__(self, binding):
+ raise NotImplementedError()
+
+ def bindings(self):
+ return [ binding
+ for binding in self.owners
+ if isinstance(binding, Bindings.Binding) ]
+
+
+class _ActionRegistry(object):
+
+ def __init__(self):
+ self._registry = {}
+
+ def register(self, action):
+ self._registry[action.name] = action
+
+ def __getitem__(self, key):
+ return self._registry[key]
+
+Actions = _ActionRegistry()
+
+
+class Nop(Action):
+
+ def __init__(self, name='nop'):
+ Action.__init__(self, name)
+
+ def __call__(self, binding):
+ pass
+
+
+class Macro(Action):
+
+ _actions = Node.NodeList()
+
+ def __init__(self, name, actions):
+ Action.__init__(self, name)
+ for i in range(len(actions)):
+ if type(actions[i]) is str:
+ actions[i] = Actions[actions[i]]
+ self._actions = actions
+
+ def __call__(self, binding):
+ for action in self._actions:
+ action(binding)
+
+class Command(Action):
+
+ def __init__(self, name, callable):
+ Action.__init__(self, name)
+ self._callable = callable
+
+ def __call__(self, binding):
+ self._callable(binding)
+
+
+class ChangeBindingsAbsolute(Action):
+
+ def __init__(self, name, mapIndex, keymaps):
+ Action.__init__(self, name)
+ self._mapIndex = mapIndex
+ self._keymaps = keymaps
+
+ def __call__(self, binding):
+ binding.keymap().keylist().replaceFromIndex(self._mapIndex, self._keymaps)
+
+
+class ChangeBindingsRelative(Action):
+
+ def __init__(self, name, mapIndex, keymaps):
+ Action.__init__(self, name)
+ self._mapIndex = mapIndex
+ self._keymaps = keymaps
+
+ def __call__(self, binding):
+ keymap = binding.keymap()
+ keymap.keylist().replaceFromIndex(keymap.index()+self._mapIndex, self._keymaps)
+
+def action(func):
+ action = Command(func.__name__, func)
+ Actions.register(action)
+ return func
--- /dev/null
+"""The Bindings module manages an infrastructure for key bindings."""
+
+# Event = (context, sequence ...) wobei alle elsts strings sind
+
+# gebuynbden wird ein Action object. Jede Action hat einen namen unter
+# der sie registriert wird. die funktionen zum definieren eines
+# keymnap und zum binden von keys nehmen diesen namen als argument =>
+# alle Aktionen m"ussen vorher registriert werden
+
+# KeyStack => KeyMap => Binding => Action
+
+# Das Binding Objekt enthaelt das Event unter welchem die Action
+# gebunden ist. Jedes Binding aht auch einen label zum anzeigen des
+# bindings
+
+import Node, Actions, Events, Logger
+
+class Binding(Node.Node):
+
+ action = Node.NodeReference()
+
+ def __init__(self, event, label, action):
+ Node.Node.__init__(self)
+ self.event = event
+ self.label = label
+ self.action = action
+
+ def keymap(self):
+ return self.owner
+
+ def execute(self):
+ Logger.log('binding', 'execute: %s' % self.action.name)
+ self.action(self)
+
+
+class KeyMap(Node.Node):
+
+ _map = Node.NodeMap()
+
+ def __init__(self, name=None):
+ Node.Node.__init__(self)
+ self._name = name
+
+ def add(self, binding):
+ if self.owner : raise RuntimeError, 'changing an active KeyMap not supported'
+ self._map[binding.event] = binding
+
+ def unbind(self, event):
+ if self.owner : raise RuntimeError, 'changing an active KeyMap not supported'
+ del self._map[event]
+
+ def name(self):
+ return self._name
+
+ def index(self):
+ if self.owner : return self.owner.indexOf(self)
+ else : return None
+
+ def keylist(self):
+ return self.owner
+
+ def lookup(self, event):
+ return self._map.get(event,None)
+
+ def bindings(self):
+ return self._map
+
+
+class KeyList(Node.Node):
+
+ _list = Node.NodeList()
+
+ def __init__(self):
+ Node.Node.__init__(self)
+ self._callbacks = []
+
+ def indexOf(self, map):
+ return self._list.index(map)
+
+ def append(self, map):
+ self._list.append(map)
+ self._callCallbacks()
+
+ def replaceFromIndex(self, index, maps):
+ self._list[index:] = maps
+ self._callCallbacks()
+
+ def lookup(self, event):
+ for i in range(len(self._list)-1,-1,-1):
+ binding = self._list[i].lookup(event)
+ if binding is not None : return binding
+ return None
+
+ def bindings(self):
+ bindings = {}
+ for map in self._list:
+ bindings.update(map.bindings())
+ return bindings
+
+ def bindingsByContext(self):
+ bindings = {}
+ for map in self._list:
+ for binding in map.bindings().itervalues():
+ bindings.setdefault(binding.event.context,{})
+ bindings[binding.event.context][binding.event] = binding
+ return bindings
+
+ def registerCallback(self, cb):
+ self._callbacks.append(cb)
+
+ def unregisterCallback(self, cb):
+ self._callbacks.remove(cb)
+
+ def _callCallbacks(self):
+ for cb in self._callbacks : cb(self)
+
+ def path(self):
+ return ' / '.join([ map.name() for map in self._list if map.name() ])
--- /dev/null
+"""Event handling and generation.
+
+The Event module manages event generation. Events are created by
+EventSource instances. Each event source is associated with a file
+handle. This handle will be poll()'ed and the eventsource will be
+called, as sonn as input is available."""
+
+import select, Logger, Node
+
+class _Event(tuple):
+
+ def __init__(self, elts):
+ tuple.__init__(self, elts)
+
+ context = property(lambda self: self[0], None, None)
+ code = property(lambda self: self[1], None, None)
+
+
+def Event(context, code):
+ return _Event((context,code))
+
+
+class EventSource(Node.Node):
+
+ def __init__(self, fd, context):
+ Node.Node.__init__(self)
+ self._fd = fd
+ self._context = context
+
+ def fd(self):
+ return self._fd
+
+ def context(self):
+ return self._context
+
+ def fileno(self):
+ return self._fd.fileno()
+
+ def readEvents(self):
+ raise NotImplementedError
+
+
+class Dispatcher(Node.Node):
+
+ _sources = Node.NodeMap()
+
+ def __init__(self, keylist):
+ Node.Node.__init__(self)
+ self._sources = {}
+ self._callbacks = []
+ self._keylist = keylist
+ self._poller = select.poll()
+
+ def registerSource(self, eventSource):
+ self._sources[eventSource.fileno()] = eventSource
+ self._poller.register(eventSource.fileno(),select.POLLIN)
+
+ def unregisterSource(self, eventSource):
+ del self._sources[eventSource.fileno()]
+ self._poller.unregister(eventSource.fileno())
+
+ def registerCallback(self,cb):
+ self._callbacks.append(cb)
+
+ def unregisterCallback(self,cb):
+ self._callbacks.remove(cb)
+
+ def run(self):
+ while 1:
+ for cb in self._callbacks : cb()
+ try:
+ pollEvents = self._poller.poll()
+ except select.error, v:
+ if v[0]==4 : continue
+ else : raise
+ if not pollEvents : return
+ for fd, pollEvent in pollEvents:
+ if pollEvent != select.POLLIN : return
+ for event in self._sources[fd].readEvents():
+ Logger.log('dispatcher', 'event: ' + str(event))
+ binding = self._keylist.lookup(event)
+ if binding is not None:
+ binding.execute()
--- /dev/null
+import Views, Events, Logger
+from Views import EventWidget
+import time, os, struct
+
+class View(Views.WidgetView):
+
+ def __init__(self, context, label, numeric_switches, alpha_switches, x, y, size=11):
+ size += 1
+ Views.WidgetView.__init__(self, context, label, x, y,size*numeric_switches+3,7)
+
+ split = numeric_switches // 2
+ for i in range(split):
+ self.add( EventWidget(i,str(i),size*i+1,4,size) )
+ for i in range(split,numeric_switches):
+ self.add( EventWidget(i,str(i),size*i+3,4,size) )
+ split = max(0,alpha_switches-(numeric_switches-split))
+ offset = size//2+(numeric_switches-alpha_switches-1)*size
+ for i in range(split):
+ self.add( EventWidget(i+numeric_switches, chr(ord('A')+i), size*i+1+offset,1,size) )
+ for i in range(split, alpha_switches):
+ self.add( EventWidget(i+numeric_switches, chr(ord('A')+i), size*i+3+offset,1,size) )
+
+
+class JSEvent:
+
+ STRUCT = "IhBB"
+ STRUCT_len = struct.calcsize(STRUCT)
+
+ TYPE_BUTTON = 0x01
+ TYPE_AXIS = 0x02
+ TYPE_INIT = 0x80
+
+ def __init__(self, data):
+ self.time, self.value, self.type, self.number = struct.unpack(self.STRUCT,data)
+
+ def __str__(self):
+ return "%s(time=%d, value=%d,type=%d (%s)), number=%d)" % (
+ str(self.__class__),self.time, self.value, self.type, self.decode_type(), self.number)
+
+ def decode_type(self):
+ return ('','INIT|')[(self.type & self.TYPE_INIT) and 1 or 0] \
+ + ('','BUTTON','AXIS')[self.type & ~self.TYPE_INIT]
+
+ def readFrom(klass,f):
+ return klass(os.read(f.fileno(),klass.STRUCT_len))
+ readFrom=classmethod(readFrom)
+
+ def readMultipleFrom(klass,f,maxevents=256):
+ data = os.read(f.fileno(),maxevents*klass.STRUCT_len)
+ rv = []
+ while data:
+ rv.append(klass(data[:klass.STRUCT_len]))
+ data = data[klass.STRUCT_len:]
+ return rv
+ readMultipleFrom=classmethod(readMultipleFrom)
+
+class Source(Events.EventSource):
+
+ def __init__(self, joydev, context, bits=None, mindelay=100):
+ Events.EventSource.__init__(self, file(joydev), context)
+ self._bits = bits
+ self._lastevent = 0
+ self._mindelay = mindelay
+
+ def readEvents(self):
+ n = 0
+ lev = self._lastevent
+ if self._bits:
+ time.sleep(0.0005)
+ jsevents = JSEvent.readMultipleFrom(self.fd())
+ for event in jsevents:
+ if event.type == JSEvent.TYPE_BUTTON and event.value == 1:
+ self._lastevent = event.time
+ if event.time - lev < self._mindelay : return []
+ n = n | self._bits[event.number]
+ if n == 0 : return []
+ n -= 1
+ else:
+ event = JSEvent.readFrom(self.fd())
+ if event.type == JSEvent.TYPE_BUTTON and event.value == 1:
+ self._lastevent = event.time
+ if event.time - lev < self._mindelay : return []
+ n = event.number
+ else:
+ return []
+ return [Events.Event(self.context(), n)]
+
+def register( viewmanager,
+ dispatcher,
+ context,
+ label,
+ numeric_switches,
+ alpha_switches,
+ x,
+ y,
+ size,
+ device,
+ bits = None ):
+ viewmanager.registerView( View(context, label, numeric_switches, alpha_switches, x, y, size) )
+ dispatcher.registerSource( Source(device, context, bits) )
--- /dev/null
+import Views, Events
+import sys, curses, curses.ascii
+
+class Source(Events.EventSource):
+
+ def __init__(self, context, win):
+ Events.EventSource.__init__(self, sys.stdin, context)
+ self._win = win
+
+ def readEvents(self):
+ return [Events.Event(self.context(), self._win.getch())]
+
+
+class View(Views.View):
+
+ def __init__(self, context, label, x, y, dx, dy, size=9):
+ Views.View.__init__(self, context, label, x, y, dx, dy)
+ self._size = size
+
+ def updateView(self, bindings):
+ self.win().clear()
+ self.drawFrame()
+ keys = bindings.keys()
+ keys.sort()
+ column = 2
+ row = 2
+ for key in keys:
+ keyname = curses.keyname(key.code)
+ if keyname.startswith('KEY_'):
+ keyname = '%s' % keyname[4:10].lower()
+ if keyname[-2:-1] == '^':
+ keyname = '%sC-%s' % (keyname[:-2],keyname[-1:].lower())
+ if curses.ascii.isupper(ord(keyname[-1:])):
+ keyname = '%sS-%s' % (keyname[:-1],keyname[-1:].lower())
+ self.win().addstr(row, column, '%-6s %s'
+ % (keyname, bindings[key].label[:self._size]))
+ row += 1
+ if row >= self.dy-2:
+ row = 2
+ column += self._size+10
+ if column+self._size+7 >= self.dx : break
+ self.win().vline(1,column-2,curses.ACS_VLINE,self.dy-2)
+ self.win().nooutrefresh()
+
+def register( viewmanager,
+ dispatcher,
+ context,
+ label,
+ x,
+ y,
+ dx,
+ dy ):
+ viewmanager.registerView( View(context, label, x,y,dx,dy) )
+ dispatcher.registerSource( Source(context, viewmanager.win()) )
--- /dev/null
+import textwrap, time
+
+logger = None
+
+def init(viewmanager, x, y, dx, dy):
+ global logger
+ logger = Logger(viewmanager.win(), x, y, dx, dy)
+
+class Logger(object):
+
+ def __init__(self, win, x, y, dx, dy):
+ self._win = win.derwin(dy,dx,y,x)
+ self._win.border()
+ self._win.addstr(0,2,' Log ')
+ self._win.nooutrefresh()
+ self._textwin = self._win.derwin(dy-2,dx-3,1,2)
+ self._textwin.scrollok(1)
+ self._wrapper = textwrap.TextWrapper(width = dx-4,
+ subsequent_indent = ' '*4)
+
+ def log(self, src, msg):
+ lines = self._wrapper.wrap(
+ '[%s] (%s) %s' % (time.strftime("%H:%M:%S",time.localtime()),src, msg))
+ self._textwin.scroll(len(lines))
+ for i in range(len(lines)):
+ self._textwin.addstr(self._textwin.getmaxyx()[0]-len(lines)+i,0,
+ lines[i])
+ self._textwin.nooutrefresh()
+
+def log(src,msg):
+ if logger:
+ logger.log(src,msg)
--- /dev/null
+import socket, struct
+import Actions, Logger
+
+def osc_string(s) :
+ n = (len(s)+1)%4
+ if n : return s+'\0'*(5-n)
+ else : return s+'\0'
+
+def osc_command(url,*args):
+ tag=","
+ argstring=""
+ for arg in args :
+ if type(arg) is int:
+ tag += 'i'
+ argstring += struct.pack(">I",arg)
+ elif type(arg) is str:
+ tag += 's'
+ argstring += osc_string(arg)
+ else:
+ raise RunstimeError, "Invalid OSC argument"
+ return osc_string(url) + osc_string(tag) + argstring
+
+class OSCSender(object):
+
+ def __init__(self, host, port):
+ self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ self._target = (host,port)
+
+ def command(self, path, *args):
+ if type(args[0]) in (tuple, list) : args = args[0]
+ Logger.log('osc','osc://%s:%s%s %s'
+ % (self._target[0], self._target[1], path,
+ ' '.join(map(repr,args))))
+ self._sock.sendto(osc_command(path, *args),self._target)
+
+
+class Controller(object) :
+
+ def __init__(self, host, port):
+ self._sender = OSCSender(host,port)
+
+ def hit(self, command, index=0):
+ self._sender.command("/sl/%d/hit" % index, str(command))
+
+
+class Command(Actions.Action):
+
+ def __init__(self, name, controller, command):
+ Actions.Action.__init__(self, name)
+ self._controller = controller
+ self._command = command
+
+ def __call__(self, binding):
+ self._controller.hit(self._command)
--- /dev/null
+"""A Node is an object which autmatically manages it's owner(s).
+
+A Node should alweys be stored in attribute members declared as
+NodeReference or as member of a NodeMap or NodeList."""
+
+
+import weakref
+
+class Node(object):
+
+ def __init__(self):
+ self._owners = []
+
+ def _addOwner(self,owner):
+ self._owners.append(weakref.ref(owner,self._delRef))
+
+ def _delOwner(self,owner):
+ for i in range(len(self._owners)):
+ if self._owners[i]() is owner:
+ del self._owners[i]
+ return
+
+ def _delRef(self,ref):
+ for i in range(len(self._owners)):
+ if self._owners[i] is ref:
+ del self._owners[i]
+ return
+
+ def _owners(self):
+ return ( x() for x in self._owners )
+
+ def _owner(self):
+ if self._owners and self._owners[0]() is not None:
+ return self._owners[0]()
+ return None
+
+ owners = property(_owners, None, None, None)
+ owner = property(_owner, None, None, None)
+
+id_index = 0
+def _make_id():
+ global id_index
+ id_index += 1
+ return '__node_elt_%d' % id_index
+
+def NodeReference(id=None):
+ if id is None : id = _make_id()
+ return property(lambda self, id=id: getattr(self,id,None),
+ lambda self, node, id=id: _NodeReference_set(self,node,id),
+ None,
+ None)
+
+def _NodeReference_set(self, node, id):
+ old = getattr(self,id,None)
+ if old is not None : old._delOwner(self)
+ setattr(self,id,node)
+ if node is not None : node._addOwner(self)
+
+
+class _NodeMap(dict):
+
+ def __init__(self, owner):
+ dict.__init__(self)
+ self._owner = weakref.ref(owner)
+
+ def __setitem__(self, key, node):
+ try: v = self[key]
+ except KeyError: pass
+ else: v._delOwner(self._owner())
+ dict.__setitem__(self, key, node)
+ node._addOwner(self._owner())
+
+ def __delitem__(self, key):
+ try: v = self[key]
+ except KeyError: pass
+ else: v._delOwner(self._owner())
+ dict.__delitem__(self,key)
+
+ def clear(self):
+ owner = self._owner()
+ for v in self.itervalues() : v._delOwner(owner)
+ dict.clear(self)
+
+ def setdefault(self, key, node):
+ if not self.has_key(key) : self[key] = node
+
+ def pop(self, key):
+ v = self[key]
+ del self[key]
+ return v
+
+ def popitem(self):
+ try: k = self.iterkeys().next()
+ except StopIteration: raise KeyError, 'popitem(): dictionary is empty'
+ return (k, self.pop(k))
+
+ def update(self, *args, **kws):
+ for k,v in dict(*args,**kws).iteritems() : self[k] = v
+
+def NodeMap(id=None):
+ if id is None : id = _make_id()
+ return property(lambda self, id=id : _NodeMap_get(self, id),
+ lambda self, elts, id=id : _NodeMap_set(self, elts, id),
+ None)
+
+def _NodeMap_get(self, id):
+ if not hasattr(self, id) : setattr(self, id, _NodeMap(self))
+ return getattr(self, id)
+
+def _NodeMap_set(self, elts, id):
+ if hasattr(self, id) : getattr(self, id).clear()
+ else : setattr(self, id, _NodeMap(self))
+ getattr(self, id).update(elts)
+
+
+class _NodeList(list):
+
+ def __init__(self, owner):
+ list.__init__(self)
+ self._owner = weakref.ref(owner)
+
+ def __setitem__(self, index, node):
+ if type(index) is not slice:
+ self[index] # throw IndexError if index invalid
+ index = slice(index,index+1,None)
+ node = [ node ]
+ owner = self._owner()
+ for i in range(*index.indices(len(self))) : self[i]._delOwner(owner)
+ try:
+ list.__setitem__(self, index, node)
+ except ValueError:
+ for i in range(*index.indices(len(self))) : self[i]._addOwner(owner)
+ raise
+ else:
+ for n in node : n._addOwner(owner)
+
+ def __delitem__(self, index):
+ if type(index) is not slice:
+ self[index] # throw IndexError if index invalid
+ index = slice(index,index+1,None)
+ self.__setitem__(index,[])
+
+ def __setslice__(self, i, j, seq):
+ self[max(0, i):max(0, j):] = seq
+
+ def __delslice__(self, i, j):
+ del self[max(0, i):max(0, j):]
+
+ def append(self, node):
+ self[len(self):len(self)] = [node]
+
+ def extend(self, nodes):
+ self[len(self):len(self)] = nodes
+
+ def insert(self, index, node):
+ self[index:index] = [node]
+
+ def pop(self, index=None):
+ if index is None : index = len(self)-1
+ x = self[index]
+ del self[index]
+ return x
+
+ def remove(self, node):
+ del self[self.index(node)]
+
+def NodeList(id=None):
+ if id is None : id = _make_id()
+ return property(lambda self, id=id : _NodeList_get(self, id),
+ lambda self, elts, id=id : _NodeList_set(self, elts, id),
+ None)
+
+def _NodeList_get(self, id):
+ if not hasattr(self, id) : setattr(self, id, _NodeList(self))
+ return getattr(self, id)
+
+def _NodeList_set(self, elts, id):
+ if hasattr(self, id) : del getattr(self, id)[:]
+ else : setattr(self, id, _NodeList(self))
+ getattr(self, id).extend(elts)
--- /dev/null
+import Node
+import unittest
+
+class Owner(object):
+ child = Node.NodeReference()
+
+class OwnerMap(object):
+ children = Node.NodeMap()
+
+class OwnerList(object):
+ children = Node.NodeList()
+
+class Child(Node.Node):
+ pass
+
+class UnitTest(unittest.TestCase):
+
+ def testNode(self):
+ owner = Owner()
+ child1 = Child()
+ child2 = Child()
+ self.assertEqual(list(child1.owners),[])
+ self.assertEqual(list(child2.owners),[])
+ self.assertEqual(owner.child,None)
+
+ owner.child = child1
+ self.assertEqual(list(child1.owners), [owner])
+ self.assertEqual(list(child2.owners), [])
+ self.assertEqual(owner.child, child1)
+
+ owner.child = child2
+ self.assertEqual(list(child1.owners), [])
+ self.assertEqual(list(child2.owners), [owner])
+ self.assertEqual(owner.child, child2)
+
+ owner.child = None
+ self.assertEqual(list(child1.owners),[])
+ self.assertEqual(list(child2.owners),[])
+ self.assertEqual(owner.child,None)
+
+ owner.child = child1
+ self.assertEqual(list(child1.owners), [owner])
+ owner = None
+ self.assertEqual(list(child1.owners),[])
+ self.assertEqual(owner, None)
+
+
+ def testNodeMap(self):
+ owner = OwnerMap()
+ child = Child()
+ owner.children['a'] = child
+ self.assertEqual(list(child.owners),[owner])
+ del owner.children['a']
+ self.assertEqual(list(child.owners),[])
+
+ child2 = Child()
+ owner.children.setdefault('a',child)
+ owner.children['b'] = child2
+ self.assertEqual(list(child.owners),[owner])
+ self.assertEqual(list(child2.owners),[owner])
+ owner.children['a'] = owner.children['b']
+ self.assertEqual(list(child.owners),[])
+ self.assertEqual(list(child2.owners),[owner,owner])
+ del owner.children['a']
+ self.assertEqual(list(child2.owners),[owner])
+ owner.children.clear()
+ self.assertEqual(list(child2.owners),[])
+
+ owner.children = dict(a=child, b=child2)
+ self.assertEqual(list(owner.children.pop('a').owners), [])
+ self.assertEqual(list(child2.owners), [owner])
+ self.assertEqual(owner.children.popitem(),('b',child2))
+ self.assertEqual(list(child2.owners), [])
+
+ owner.children['a'] = child
+ owner.children.update(b=child2)
+ self.assertEqual(list(child2.owners), [owner])
+ self.assertEqual(list(child.owners), [owner])
+ owner.children.update(a=child2)
+ self.assertEqual(list(child.owners), [])
+ self.assertEqual(list(child2.owners),[owner,owner])
+
+ del owner
+ self.assertEqual(list(child2.owners), [])
+
+
+ def testNodeList(self):
+ owner = OwnerList()
+ child1 = Child()
+ child2 = Child()
+
+ owner.children.append(child1)
+ owner.children.append(child2)
+ self.assertEqual(owner.children, [child1,child2])
+ self.assertEqual(list(child1.owners),[owner])
+ self.assertEqual(list(child2.owners),[owner])
+
+ owner.children.extend([child1,child2])
+ self.assertEqual(owner.children, [child1,child2,child1,child2])
+ self.assertEqual(list(child1.owners),[owner,owner])
+ self.assertEqual(list(child2.owners),[owner,owner])
+
+ self.assertRaises(ValueError, self._testNodeList_1, owner)
+ self.assertEqual(owner.children, [child1,child2,child1,child2])
+ self.assertEqual(list(child1.owners),[owner,owner])
+ self.assertEqual(list(child2.owners),[owner,owner])
+
+ owner.children[1:2] = []
+ self.assertEqual(list(child1.owners), [owner,owner])
+ self.assertEqual(list(child2.owners), [owner])
+
+ owner.children[:] = []
+ self.assertEqual(list(child1.owners), [])
+ self.assertEqual(list(child2.owners), [])
+
+ owner.children[:] = [child1,child2]
+ self.assertEqual(list(child1.owners),[owner])
+ self.assertEqual(list(child2.owners),[owner])
+
+ del owner.children[0]
+ self.assertEqual(list(child1.owners),[])
+ self.assertEqual(list(child2.owners),[owner])
+
+ del owner.children[:]
+ self.assertEqual(list(child2.owners),[])
+
+ owner.children = [child1,child1]
+ owner.children.insert(1,child2)
+ self.assertEqual(owner.children,[child1,child2,child1])
+ self.assertEqual(list(child1.owners), [owner,owner])
+ self.assertEqual(list(child2.owners), [owner])
+
+ self.assertEqual(list(owner.children.pop().owners), [owner])
+ self.assertEqual(owner.children.pop(), child2)
+ self.assertEqual(owner.children, [child1])
+
+ owner.children.append(child2)
+ self.assertEqual(list(owner.children.pop(0).owners), [])
+ self.assertEqual(owner.children, [child2])
+ owner.children.append(child1)
+ owner.children.remove(child2)
+ self.assertEqual(list(child1.owners),[owner])
+ self.assertEqual(list(child2.owners),[])
+ self.assertEqual(owner.children, [child1])
+
+ del owner
+ self.assertEqual(list(child1.owners), [])
+
+
+ def _testNodeList_1(self, owner):
+ owner.children[::2] = []
+
+
+if __name__ == '__main__':
+ unittest.main()
--- /dev/null
+import Events, Logger
+import popen2, fcntl, os
+
+class Process(Events.EventSource):
+
+ def __init__(self, command, context):
+ self._proc = popen2.Popen4(command)
+ fcntl.fcntl(self._proc.fromchild, fcntl.F_SETFL, os.O_NDELAY)
+ Events.EventSource.__init__(self, self._proc.fromchild, context)
+ self._buffer = ""
+ Logger.log(self.context(),"launched %s" % command)
+
+ def readEvents(self):
+ data = (self._buffer + self._proc.fromchild.read()).split('\n')
+ self._buffer = data.pop(-1)
+ for line in data:
+ Logger.log(self.context(), line)
+ rv = self.poll()
+ if rv != -1 : self._term(rv)
+ return []
+
+ def terminate(self):
+ os.kill(self._proc.pid,signal.SIGTERM)
+ self._term(self._proc.wait())
+
+ def _term(self, rv):
+ if self._buffer : Logger.log(self.context(),self._buffer)
+ self._buffer = ""
+ Logger.log(self.context(), 'Terminated with exit code %s' % rv)
+ self.owner().unregisterSource(self)
--- /dev/null
+"""Manage binding views.
+
+The Views module manages the on-screen keymap representations. A View
+is registered for a specific context. The view will be called with a
+list of all active bindings whenever the bindings change."""
+
+import Events, Node, curses
+
+class View(Node.Node):
+ def __init__(self, context, label, x, y, dx, dy):
+ Node.Node.__init__(self)
+ self._context = context
+ self._label = label
+ self._win = None
+ self.x, self.y, self.dx, self.dy = x, y, dx, dy
+
+ def init(self):
+ self._win = self.owner.win().derwin(self.dy, self.dx, self.y, self.x)
+ self.drawFrame()
+
+ def drawFrame(self, subtitle=None):
+ label = ' %s ' % self._label
+ if subtitle:
+ label = '%s- %s ' % (label, subtitle)
+ self._win.border()
+ self._win.addstr(0,2,label)
+ self._win.nooutrefresh()
+
+ def win(self):
+ return self._win
+
+ def updateView(self, bindings):
+ raise NotImplementedError
+
+
+class WidgetView(View):
+
+ _widgets = Node.NodeMap()
+
+ def __init__(self, context, label, x, y, dx, dy):
+ View.__init__(self, context, label, x, y, dx, dy)
+
+ def init(self):
+ View.init(self)
+ for widget in self._widgets.itervalues():
+ widget.init()
+
+ def add(self,widget):
+ if self.win() : raise RuntimeError, 'Cannot change active view'
+ self._widgets[widget.code] = widget
+
+ def updateView(self, bindings):
+ if bindings:
+ self.drawFrame(bindings.itervalues().next().keymap().keylist().path())
+ for code, widget in self._widgets.iteritems():
+ event = Events.Event(self._context,code)
+ if bindings.has_key(event):
+ widget.updateView(bindings[event])
+ else:
+ widget.updateView(None)
+
+
+class EventWidget(Node.Node):
+
+ def __init__(self, code, label, x, y, size=9):
+ Node.Node.__init__(self)
+ self.x, self.y, self.size = x, y, size
+ self._win = None
+ self.code = code
+ self.label = label
+
+ def init(self):
+ self._win = self.owner.win().derwin(2, self.size, self.y, self.x)
+ self._win.hline(0,0,curses.ACS_HLINE,self.size-1)
+ self._win.addstr(0,(self.size-1-min(self.size-3,len(self.label)))/2, self.label[:self.size-3])
+ self._win.nooutrefresh()
+
+ def updateView(self, binding):
+ if binding:
+ self._win.addstr(1,0, binding.label[:self.size-1].center(self.size-1), curses.A_BOLD)
+ else:
+ self._win.addstr(1, 0, ' '*(self.size-1))
+ self._win.nooutrefresh()
+
+
+class ViewManager(object):
+
+ _views = Node.NodeMap()
+
+ def __init__(self, keylist, dispatcher, win):
+ self._win = win
+ keylist.registerCallback(self.updateViews)
+ dispatcher.registerCallback(self.beforeIdle)
+
+ def win(self):
+ return self._win
+
+ def registerView(self, view):
+ self._views[view._context] = view
+ view.init()
+
+ def updateViews(self, keylist):
+ bindings = keylist.bindingsByContext()
+ for context, keys in bindings.iteritems():
+ if self._views.has_key(context):
+ self._views[context].updateView(keys)
+
+ def beforeIdle(self):
+ self._win.refresh()
--- /dev/null
+#!/usr/bin/python2.4
+
+import main
+
+main.main()
--- /dev/null
+import Bindings, Actions, Views, Events, Logger
+from Bindings import Binding
+from Actions import Actions as Action, action
+from Views import EventWidget
+from Events import Event
+import main
+import Joyboard, Keyboard, Process
+import Looper
+import sys, curses
+from curses.ascii import alt, ctrl
+
+def shift(letter) : return ord(chr(letter).upper())
+def key(letter) : return ord(letter.lower())
+
+###########################################################################
+# Setup views and controllers
+#
+# Display size: 77x17
+
+Logger.init(main.viewmanager, 38, 0, 39, 10)
+
+Joyboard.register(
+ viewmanager = main.viewmanager,
+ dispatcher = main.dispatcher,
+
+ context = 'jb0',
+ label = 'Foot Switch',
+ numeric_switches = 10,
+ alpha_switches = 5,
+ x = 2,
+ y = 10,
+ size = 6,
+
+ device = '/dev/input/js0',
+ bits = { 1:1, 3:2, 2:4, 0:8 }
+)
+
+Keyboard.register(
+ viewmanager = main.viewmanager,
+ dispatcher = main.dispatcher,
+
+ context = 'kbd',
+ label = 'Key Bindings',
+ x = 0,
+ y = 0,
+ dx = 38,
+ dy = 10
+)
+
+jackd = Process.Process("jackd -d alsa -S -r 44100 -p 256",'jack')
+
+###########################################################################
+# Global keymap and auxilary actions
+
+global_map = Bindings.KeyMap()
+
+@action
+def quit(binding):
+ sys.exit(0)
+
+global_map.add( Binding( Event('jb0',0), '()', Actions.Nop() ) )
+global_map.add( Binding( Event('jb0',1), '()', Actions.Nop() ) )
+global_map.add( Binding( Event('jb0',10), '()', Actions.Nop() ) )
+
+global_map.add( Binding( Event('kbd',key('q')), 'Quit', Action['quit'] ) )
+
+Action.register( Actions.ChangeBindingsRelative( 'unset_this_map', 0, [] ) )
+
+###########################################################################
+# Looper mode
+
+looper_main_map = Bindings.KeyMap( 'Looper' )
+looper_aux_map = Bindings.KeyMap( 'Aux' )
+
+Action.register( Actions.ChangeBindingsAbsolute('set_mode_looper', 1, [looper_main_map]) )
+Action.register( Actions.ChangeBindingsRelative('looper_set_aux_map', 1, [looper_aux_map]) )
+
+looper = Looper.Controller('localhost',9951)
+
+Action.register( Looper.Command('looper_record', looper, 'record') )
+Action.register( Looper.Command('looper_overdub', looper, 'overdub') )
+Action.register( Looper.Command('looper_multiply', looper, 'multiply') )
+Action.register( Looper.Command('looper_mute', looper, 'mute') )
+Action.register( Looper.Command('looper_undo', looper, 'undo') )
+Action.register( Looper.Command('looper_redo', looper, 'redo') )
+Action.register( Looper.Command('looper_trigger', looper, 'trigger') )
+Action.register( Looper.Command('looper_insert', looper, 'insert') )
+Action.register( Looper.Command('looper_replace', looper, 'replace') )
+Action.register( Looper.Command('looper_substitute', looper, 'substitute') )
+Action.register( Looper.Command('looper_reverse', looper, 'reverse') )
+Action.register( Looper.Command('looper_oneshot', looper, 'oneshot') )
+
+looper_main_map.add ( Binding( Event('jb0',2), 'Rec', Action['looper_record'] ) )
+looper_main_map.add ( Binding( Event('jb0',3), 'Over', Action['looper_overdub'] ) )
+looper_main_map.add ( Binding( Event('jb0',4), 'Mult', Action['looper_multiply'] ) )
+looper_main_map.add ( Binding( Event('jb0',5), 'Mute', Action['looper_mute'] ) )
+looper_main_map.add ( Binding( Event('jb0',6), 'Undo', Action['looper_undo'] ) )
+looper_main_map.add ( Binding( Event('jb0',7), 'Redo', Action['looper_redo'] ) )
+looper_main_map.add ( Binding( Event('jb0',8), 'Trig', Action['looper_trigger'] ) )
+looper_main_map.add ( Binding( Event('jb0',9), 'Aux', Action['looper_set_aux_map'] ) )
+
+looper_aux_map.add ( Binding( Event('jb0',2), 'Ins', Action['looper_insert'] ) )
+looper_aux_map.add ( Binding( Event('jb0',3), 'Repl', Action['looper_replace'] ) )
+looper_aux_map.add ( Binding( Event('jb0',4), 'Subst', Action['looper_substitute'] ) )
+looper_aux_map.add ( Binding( Event('jb0',6), 'Rev', Action['looper_reverse'] ) )
+looper_aux_map.add ( Binding( Event('jb0',7), 'Once', Action['looper_oneshot'] ) )
+looper_aux_map.add ( Binding( Event('jb0',9), 'Main', Action['unset_this_map'] ) )
+
+###########################################################################
+# MegaPedal mode
+
+megapedal_main_map = Bindings.KeyMap( 'MegaPedal' )
+
+Action.register( Actions.ChangeBindingsAbsolute('set_mode_megapedal', 1, [megapedal_main_map]) )
+
+megapedal_main_map.add ( Binding( Event('jb0',11), 'Looper', Action['set_mode_looper'] ) )
+global_map.add ( Binding( Event('jb0',11), 'MegaPedal', Action['set_mode_megapedal'] ) )
+
+###########################################################################
+# RythmBox mode
+
+rythmbox_main_map = Bindings.KeyMap( 'RythmBox' )
+
+Action.register( Actions.ChangeBindingsAbsolute('set_mode_rythmbox', 1, [rythmbox_main_map]) )
+
+rythmbox_main_map.add ( Binding( Event('jb0',11), 'MegaPedal', Action['set_mode_megapedal'] ) )
+global_map.add ( Binding( Event('jb0',11), 'RythmBox', Action['set_mode_rythmbox'] ) )
+
+###########################################################################
+
+main.keylist.append(global_map)
+main.keylist.append(looper_main_map)
--- /dev/null
+import Bindings, Events, Views, curses.wrapper
+
+keylist = None
+dispatcher = None
+viewmanager = None
+stdscr = None
+
+
+def run(scr):
+
+ global keylist
+ global dispatcher
+ global viewmanager
+ global stdscr
+
+ keylist = Bindings.KeyList()
+ dispatcher = Events.Dispatcher(keylist)
+ viewmanager = Views.ViewManager(keylist, dispatcher, scr)
+ stdscr = scr
+
+ import config
+
+ curses.raw()
+ stdscr.keypad(1)
+ try: curses.curs_set(0)
+ except: pass
+ dispatcher.run()
+
+
+def main():
+ curses.wrapper(run)