ef06b87e5f2439e54151a1809d3f1c075d37ae27
[audiocontrol.git] / Joyboard.py
1 import Views, Events, Logger, Bindings, Actions
2 from Views import EventWidget
3 import time, os, struct, curses
4
5 class View(Views.WidgetView):
6
7     def __init__(self, context, label, numeric_switches, alpha_switches, x, y, dx=0, size=11):
8         size += 1
9         dx = max(dx,size*numeric_switches+3)
10         delta = dx - size*numeric_switches - 3
11         shift = delta / 2
12         delta = dx - size*numeric_switches - 2*shift
13         Views.WidgetView.__init__(self, context, label, x, y, dx, 7)
14
15         split = numeric_switches // 2
16         for i in range(split):
17             self.add( EventWidget(i,str(i+1)[-1:],size*i+1+shift,4,size) )
18         for i in range(split,numeric_switches):
19             self.add( EventWidget(i,str(i+1)[-1:],size*i+shift+delta,4,size) )
20         split = max(0,alpha_switches-(numeric_switches-split))
21         offset = size//2+(numeric_switches-alpha_switches-1)*size
22         for i in range(split):
23             self.add( EventWidget(i+numeric_switches, chr(ord('A')+i), size*i+1+offset+shift,1,size) )
24         for i in range(split, alpha_switches):
25             self.add( EventWidget(i+numeric_switches, chr(ord('A')+i), size*i+delta+offset+shift,1,size) )
26
27
28 class JSEvent:
29
30     STRUCT = "IhBB"
31     STRUCT_len = struct.calcsize(STRUCT)
32
33     TYPE_BUTTON = 0x01
34     TYPE_AXIS = 0x02
35     TYPE_INIT = 0x80
36     
37     def __init__(self, data):
38         self.time, self.value, self.type, self.number = struct.unpack(self.STRUCT,data)
39
40     def __str__(self):
41         return "%s(time=%d, value=%d,type=%d (%s)), number=%d)" % (
42             str(self.__class__),self.time, self.value, self.type, self.decode_type(), self.number)
43
44     def decode_type(self):
45         return ('','INIT|')[(self.type & self.TYPE_INIT) and 1 or 0] \
46                + ('','BUTTON','AXIS')[self.type & ~self.TYPE_INIT]
47
48     def readFrom(klass,f):
49         return klass(os.read(f.fileno(),klass.STRUCT_len))
50     readFrom=classmethod(readFrom)
51
52     def readMultipleFrom(klass,f,maxevents=256):
53         data = os.read(f.fileno(),maxevents*klass.STRUCT_len)
54         rv = []
55         while data:
56             rv.append(klass(data[:klass.STRUCT_len]))
57             data = data[klass.STRUCT_len:]
58         return rv
59     readMultipleFrom=classmethod(readMultipleFrom)
60
61
62 class Source(Events.EventSource):
63
64     def __init__(self, joydev, context, bits=None, mindelay=100):
65         Events.EventSource.__init__(self, file(joydev), context)
66         self._bits = bits
67         self._lastevent = 0
68         self._mindelay = mindelay
69         self._controllers = {}
70
71     def readEvents(self):
72         n = 0
73         lev = self._lastevent
74         if self._bits:
75             time.sleep(0.0005)
76             jsevents = JSEvent.readMultipleFrom(self.fd())
77             for event in jsevents:
78                 if event.type == JSEvent.TYPE_AXIS:
79                     return self._controller(event.number, event.value)
80                 if event.type == JSEvent.TYPE_BUTTON and event.value == 1:
81                     self._lastevent = event.time
82                     if event.time - lev < self._mindelay : return []
83                     n = n | self._bits[event.number]
84             if n == 0 : return []
85             n -= 1
86         else:
87             event = JSEvent.readFrom(self.fd())
88             if event.type == JSEvent.TYPE_AXIS:
89                 return self._controller(event.number, event.value)
90             if event.type == JSEvent.TYPE_BUTTON and event.value == 1:
91                 self._lastevent = event.time
92                 if event.time - lev < self._mindelay : return []
93                 n = event.number
94             else:
95                 return []
96         return [Events.Event(self.context(), n)]
97
98     def registerController(self, controller, low, high):
99         self._controllers[controller] = { 'low': low, 'high': high }
100
101     def _controller(self, number, value):
102         controller = self._controllers.get(number)
103         if controller:
104             value = (value - controller['low']) * 1000 / (controller['high']-controller['low'])
105             value = max(min(value,1000),0)
106             return [Events.ControlEvent(self.context(), 'p%d' % number, value)]
107         return []
108
109
110 class Controller(Views.View):
111
112     def __init__(self, context, name, x, y, dx, dy, keylist, dispatcher,
113                  controller, source, low, high):
114         Views.View.__init__(self, context, name, x, y, dx, dy)
115         self._valueEvent = None
116         self._setCommand = None
117         self._valueCommand = None
118         self._parameter = None
119         self._keylist = keylist
120         self._keymap = Bindings.KeyMap()
121         if source:
122             self._keymap.add( Bindings.Binding(Events.Event(source.context(), 'p%d' % controller), '',
123                                                Actions.Command('controllerChanged',
124                                                                self._controllerChanged)) )
125         self._keylist.prepend(self._keymap)
126         if source:
127             source.registerController(controller, low, high)
128         self._dispatcher = dispatcher
129         self._min = None
130         self._max = None
131         self._stops = None
132         self._value = None
133         self._controlValue = None
134
135     def updateView(self, bindings):
136         pass
137
138     def init(self):
139         Views.View.init(self)
140         self._redraw()
141
142     def assign(self, parameter,  setCommand, valueCommand, valueEvent, min, max, stops=[]):
143         self._keylist.removeMap(self._keymap)
144         if self._valueEvent is not None:
145             self._keymap.unbind(self._valueEvent)
146         self._parameter = parameter
147         self._valueEvent = valueEvent
148         self._setCommand = setCommand
149         self._valueCommand = valueCommand
150         self._min = min
151         self._max = max
152         self._stops = stops
153         if self._valueEvent:
154             self._keymap.add( Bindings.Binding( self._valueEvent, '',
155                                                 Actions.Command('updateValue',
156                                                                 self._updateValue)) )
157         self._keylist.prepend(self._keymap)
158         self._value = None
159         if self._valueEvent:
160             self._valueCommand()
161         else:
162             self._value = self._valueCommand()
163         self._redraw()
164
165     def _updateValue(self, binding):
166         event = self._dispatcher.currentEvent()
167         self._value = event.value
168         self._redrawValue()
169
170     def _controllerChanged(self, binding):
171         event = self._dispatcher.currentEvent()
172         Logger.log('ctl',"value = %d" % event.value)
173         self._controlValue = event.value
174         if self._controlValue >= 999 or self._controlValue <= 1:
175             self._dispatcher.setIdleCallback(self._changeValue,75)
176         elif self._controlValue >= 700 or self._controlValue <= 200:
177             self._dispatcher.setIdleCallback(self._changeValue,200)
178         else:
179             self._dispatcher.unsetIdleCallback()
180         self._redrawController()
181
182     def _changeValue(self):
183         if self._value is None: return
184         if self._controlValue > 500:
185             self.stepValue(+1)
186         else:
187             self.stepValue(-1)
188
189     def stepValue(self, direction):
190         if direction > 0:
191             newValue = self._value + (self._max - self._min) / (3000/50)
192             crossed = [ x for x in self._stops if x > self._value*1.0001 and x <= newValue ]
193         elif direction < 0:
194             newValue = self._value - (self._max - self._min) / (3000/50)
195             crossed = [ x for x in self._stops if x < self._value/1.0001 and x >= newValue ]
196         if newValue >= self._max:
197             crossed = [ self._max ]
198         elif newValue <= self._min:
199             crossed = [ self._min ]
200         if crossed:
201             newValue = crossed[0]
202             self._dispatcher.unsetIdleCallback()
203             self._setCommand(newValue)
204             # Hmm ... why does value_command not work sometimes ??
205             self._value = newValue
206             self._redrawValue()
207         else:
208             self._setCommand(newValue)
209             if self._valueEvent:
210                 self._valueCommand()
211             else:
212                 self._value = self._valueCommand()
213                 self._redrawValue()
214
215     def _redraw(self):
216         height, width = self.win().getmaxyx()
217         if self._parameter is not None:
218             self.win().addstr(1,2,self._parameter[:width-4].ljust(width-4), curses.A_BOLD)
219         self._redrawValue(False)
220         self._redrawController()
221
222     def _flt(self, value, width):
223         return ('%.3f' % value)[:width].ljust(width)
224
225     def _redrawValue(self, refresh=True):
226         height, width = self.win().getmaxyx()
227         pos = None
228         if self._value is not None:
229             pos = height - 3 - int( (self._value - self._min) * (height-6)
230                                     / (self._max - self._min) + .5 )
231         if self._max is not None:
232             self.win().addstr(2,2, self._flt(self._max,width-7))
233         if pos is not None and pos == 3:
234             self.win().addstr(pos, 5, self._flt(self._value,width-7), curses.A_BOLD)
235         else:
236             self.win().addstr(3, 5, "".ljust(width-7))
237         for row in range(4,height-3):
238             if pos is not None and row == pos:
239                 self.win().addch(pos,3,curses.ACS_PLUS)
240                 self.win().addstr(pos, 5, self._flt(self._value,width-7), curses.A_BOLD)
241             else:
242                 self.win().addch(row,3,curses.ACS_VLINE)
243                 self.win().addstr(row, 5, "".ljust(width-7))
244         if pos is not None and pos == height-3:
245             self.win().addstr(pos, 5, self._flt(self._value,width-7), curses.A_BOLD)
246         else:
247             self.win().addstr(height-3, 5, "".ljust(width-7))
248         if self._min is not None:
249             self.win().addstr(height-2,2,self._flt(self._min,width-7))
250         
251         if refresh:
252             self.win().refresh()
253
254     def _redrawController(self, refresh=True):
255         height, width = self.win().getmaxyx()
256         if self._controlValue is not None and self._controlValue >= 700:
257             self.win().addch(3,3,curses.ACS_UARROW)
258         else:
259             self.win().addch(3,3,curses.ACS_TTEE)
260         if self._controlValue is not None and self._controlValue <= 200:
261             self.win().addch(height-3,3,curses.ACS_DARROW)
262         else:
263             self.win().addch(height-3,3,curses.ACS_BTEE)
264             
265         if refresh:
266             self.win().refresh()
267     
268
269 class StepController(Actions.Action):
270
271     def __init__(self, name, controller, direction):
272         Actions.Action.__init__(self, name)
273         self._controller = controller
274         self._direction = direction
275
276     def __call__(self, binding):
277         self._controller.stepValue(self._direction)
278         
279
280 def register( viewmanager,
281               dispatcher,
282               context,
283               label,
284               numeric_switches,
285               alpha_switches,
286               x,
287               y,
288               dx,
289               size,
290               device,
291               bits = None,
292               controllers = []):
293     viewmanager.registerView( View(context, label, numeric_switches, alpha_switches, x, y, dx, size) )
294     source = None
295     if device is not None:
296         source = Source(device, context, bits)
297         dispatcher.registerSource( source )
298     return source
299
300 def registerController( viewmanager, dispatcher, keylist, source, context, name, x, y, dx, dy,
301                         controller, low, high ):
302     controller = Controller(context, name, x, y, dx, dy, keylist, dispatcher,
303                                          controller, source, low, high)
304     viewmanager.registerView( controller )
305     return controller