diff options
Diffstat (limited to 'gr-wxgui/python/wxgui/powermate.py')
-rw-r--r-- | gr-wxgui/python/wxgui/powermate.py | 448 |
1 files changed, 0 insertions, 448 deletions
diff --git a/gr-wxgui/python/wxgui/powermate.py b/gr-wxgui/python/wxgui/powermate.py deleted file mode 100644 index 7c324c5d95..0000000000 --- a/gr-wxgui/python/wxgui/powermate.py +++ /dev/null @@ -1,448 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2005 Free Software Foundation, Inc. -# -# This file is part of GNU Radio -# -# GNU Radio is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3, or (at your option) -# any later version. -# -# GNU Radio is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with GNU Radio; see the file COPYING. If not, write to -# the Free Software Foundation, Inc., 51 Franklin Street, -# Boston, MA 02110-1301, USA. -# - -""" -Handler for Griffin PowerMate, Contour ShuttlePro & ShuttleXpress USB knobs - -This is Linux and wxPython specific. -""" - -import os -import sys -import struct -import exceptions -import threading -import wx -from gnuradio import gru - -imported_ok = True - -try: - import select - import fcntl -except ImportError: - imported_ok = False - - -# First a little bit of background: -# -# The Griffin PowerMate has -# * a single knob which rotates -# * a single button (pressing the knob) -# -# The Contour ShuttleXpress (aka SpaceShuttle) has -# * "Jog Wheel" -- the knob (rotary encoder) on the inside -# * "Shuttle Ring" -- the spring loaded rubber covered ring -# * 5 buttons -# -# The Contour ShuttlePro has -# * "Jog Wheel" -- the knob (rotary encoder) on the inside -# * "Shuttle Ring" -- the spring loaded rubber covered ring -# * 13 buttons -# -# The Contour ShuttlePro V2 has -# *"Jog Wheel" -- the knob (rotary encoder) on the inside -# * "Shuttle Ring" -- the spring loaded rubber covered ring -# * 15 buttons - -# We remap all the buttons on the devices so that they start at zero. - -# For the ShuttleXpress the buttons are 0 to 4 (left to right) - -# For the ShuttlePro, we number the buttons immediately above -# the ring 0 to 4 (left to right) so that they match our numbering -# on the ShuttleXpress. The top row is 5, 6, 7, 8. The first row below -# the ring is 9, 10, and the bottom row is 11, 12. - -# For the ShuttlePro V2, buttons 13 & 14 are to the -# left and right of the wheel respectively. - -# We generate 3 kinds of events: -# -# button press/release (button_number, press/release) -# knob rotation (relative_clicks) # typically -1, +1 -# shuttle position (absolute_position) # -7,-6,...,0,...,6,7 - -# ---------------------------------------------------------------- -# Our ID's for the devices: -# Not to be confused with anything related to magic hardware numbers. - -ID_POWERMATE = 'powermate' -ID_SHUTTLE_XPRESS = 'shuttle xpress' -ID_SHUTTLE_PRO = 'shuttle pro' -ID_SHUTTLE_PRO_V2 = 'shuttle pro v2' - -# ------------------------------------------------------------------------ -# format of messages that we read from /dev/input/event* -# See /usr/include/linux/input.h for more info -# -#struct input_event { -# struct timeval time; = {long seconds, long microseconds} -# unsigned short type; -# unsigned short code; -# unsigned int value; -#}; - -input_event_struct = "@llHHi" -input_event_size = struct.calcsize(input_event_struct) - -# ------------------------------------------------------------------------ -# input_event types -# ------------------------------------------------------------------------ - -IET_SYN = 0x00 # aka RESET -IET_KEY = 0x01 # key or button press/release -IET_REL = 0x02 # relative movement (knob rotation) -IET_ABS = 0x03 # absolute position (graphics pad, etc) -IET_MSC = 0x04 -IET_LED = 0x11 -IET_SND = 0x12 -IET_REP = 0x14 -IET_FF = 0x15 -IET_PWR = 0x16 -IET_FF_STATUS = 0x17 -IET_MAX = 0x1f - -# ------------------------------------------------------------------------ -# input_event codes (there are a zillion of them, we only define a few) -# ------------------------------------------------------------------------ - -# these are valid for IET_KEY - -IEC_BTN_0 = 0x100 -IEC_BTN_1 = 0x101 -IEC_BTN_2 = 0x102 -IEC_BTN_3 = 0x103 -IEC_BTN_4 = 0x104 -IEC_BTN_5 = 0x105 -IEC_BTN_6 = 0x106 -IEC_BTN_7 = 0x107 -IEC_BTN_8 = 0x108 -IEC_BTN_9 = 0x109 -IEC_BTN_10 = 0x10a -IEC_BTN_11 = 0x10b -IEC_BTN_12 = 0x10c -IEC_BTN_13 = 0x10d -IEC_BTN_14 = 0x10e -IEC_BTN_15 = 0x10f - -# these are valid for IET_REL (Relative axes) - -IEC_REL_X = 0x00 -IEC_REL_Y = 0x01 -IEC_REL_Z = 0x02 -IEC_REL_HWHEEL = 0x06 -IEC_REL_DIAL = 0x07 # rotating the knob -IEC_REL_WHEEL = 0x08 # moving the shuttle ring -IEC_REL_MISC = 0x09 -IEC_REL_MAX = 0x0f - -# ------------------------------------------------------------------------ - -class powermate(threading.Thread): - """ - Interface to Griffin PowerMate and Contour Shuttles - """ - def __init__(self, event_receiver=None, filename=None, **kwargs): - self.event_receiver = event_receiver - self.handle = -1 - if not imported_ok: - raise exceptions.RuntimeError, 'powermate not supported on this platform' - - if filename: - if not self._open_device(filename): - raise exceptions.RuntimeError, 'Unable to find powermate' - else: - ok = False - for d in range(0, 16): - if self._open_device("/dev/input/event%d" % d): - ok = True - break - if not ok: - raise exceptions.RuntimeError, 'Unable to find powermate' - - threading.Thread.__init__(self, **kwargs) - self.setDaemon (1) - self.keep_running = True - self.start () - - def __del__(self): - self.keep_running = False - if self.handle >= 0: - os.close(self.handle) - self.handle = -1 - - def _open_device(self, filename): - try: - self.handle = os.open(filename, os.O_RDWR) - if self.handle < 0: - return False - - # read event device name - name = fcntl.ioctl(self.handle, gru.hexint(0x80ff4506), chr(0) * 256) - name = name.replace(chr(0), '') - - # do we see anything we recognize? - if name == 'Griffin PowerMate' or name == 'Griffin SoundKnob': - self.id = ID_POWERMATE - self.mapper = _powermate_remapper() - elif name == 'CAVS SpaceShuttle A/V' or name == 'Contour Design ShuttleXpress': - self.id = ID_SHUTTLE_XPRESS - self.mapper = _contour_remapper() - elif name == 'Contour Design ShuttlePRO': - self.id = ID_SHUTTLE_PRO - self.mapper = _contour_remapper() - elif name == 'Contour Design ShuttlePRO v2': - self.id = ID_SHUTTLE_PRO_V2 - self.mapper = _contour_remapper() - else: - os.close(self.handle) - self.handle = -1 - return False - - # get exclusive control of the device, using ioctl EVIOCGRAB - # there may be an issue with this on non x86 platforms and if - # the _IOW,_IOC,... macros in <asm/ioctl.h> are changed - fcntl.ioctl(self.handle,gru.hexint(0x40044590), 1) - return True - except exceptions.OSError: - return False - - - def set_event_receiver(self, obj): - self.event_receiver = obj - - - def set_led_state(self, static_brightness, pulse_speed=0, - pulse_table=0, pulse_on_sleep=0, pulse_on_wake=0): - """ - What do these magic values mean... - """ - if self.id != ID_POWERMATE: - return False - - static_brightness &= 0xff; - if pulse_speed < 0: - pulse_speed = 0 - if pulse_speed > 510: - pulse_speed = 510 - if pulse_table < 0: - pulse_table = 0 - if pulse_table > 2: - pulse_table = 2 - pulse_on_sleep = not not pulse_on_sleep # not not = convert to 0/1 - pulse_on_wake = not not pulse_on_wake - magic = (static_brightness - | (pulse_speed << 8) - | (pulse_table << 17) - | (pulse_on_sleep << 19) - | (pulse_on_wake << 20)) - data = struct.pack(input_event_struct, 0, 0, 0x04, 0x01, magic) - os.write(self.handle, data) - return True - - def run (self): - while (self.keep_running): - s = os.read (self.handle, input_event_size) - if not s: - self.keep_running = False - break - - raw_input_event = struct.unpack(input_event_struct,s) - sec, usec, type, code, val = self.mapper(raw_input_event) - - if self.event_receiver is None: - continue - - if type == IET_SYN: # ignore - pass - elif type == IET_MSC: # ignore (seems to be PowerMate reporting led brightness) - pass - elif type == IET_REL and code == IEC_REL_DIAL: - #print "Dial: %d" % (val,) - wx.PostEvent(self.event_receiver, PMRotateEvent(val)) - elif type == IET_REL and code == IEC_REL_WHEEL: - #print "Shuttle: %d" % (val,) - wx.PostEvent(self.event_receiver, PMShuttleEvent(val)) - elif type == IET_KEY: - #print "Key: Btn%d %d" % (code - IEC_BTN_0, val) - wx.PostEvent(self.event_receiver, - PMButtonEvent(code - IEC_BTN_0, val)) - else: - print "powermate: unrecognized event: type = 0x%x code = 0x%x val = %d" % (type, code, val) - - -class _powermate_remapper(object): - def __init__(self): - pass - def __call__(self, event): - """ - Notice how nice and simple this is... - """ - return event - -class _contour_remapper(object): - def __init__(self): - self.prev = None - def __call__(self, event): - """ - ...and how screwed up this is - """ - sec, usec, type, code, val = event - if type == IET_REL and code == IEC_REL_WHEEL: - # === Shuttle ring === - # First off, this really ought to be IET_ABS, not IET_REL! - # They never generate a zero value so you can't - # tell when the shuttle ring is back in the center. - # We kludge around this by calling both -1 and 1 zero. - if val == -1 or val == 1: - return (sec, usec, type, code, 0) - return event - - if type == IET_REL and code == IEC_REL_DIAL: - # === Jog knob (rotary encoder) === - # Dim wits got it wrong again! This one should return a - # a relative value, e.g., -1, +1. Instead they return - # a total that runs modulo 256 (almost!). For some - # reason they count like this 253, 254, 255, 1, 2, 3 - - if self.prev is None: # first time call - self.prev = val - return (sec, usec, IET_SYN, 0, 0) # will be ignored above - - diff = val - self.prev - if diff == 0: # sometimes it just sends stuff... - return (sec, usec, IET_SYN, 0, 0) # will be ignored above - - if abs(diff) > 100: # crossed into the twilight zone - if self.prev > val: # we've wrapped going forward - self.prev = val - return (sec, usec, type, code, +1) - else: # we've wrapped going backward - self.prev = val - return (sec, usec, type, code, -1) - - self.prev = val - return (sec, usec, type, code, diff) - - if type == IET_KEY: - # remap keys so that all 3 gadgets have buttons 0 to 4 in common - return (sec, usec, type, - (IEC_BTN_5, IEC_BTN_6, IEC_BTN_7, IEC_BTN_8, - IEC_BTN_0, IEC_BTN_1, IEC_BTN_2, IEC_BTN_3, IEC_BTN_4, - IEC_BTN_9, IEC_BTN_10, - IEC_BTN_11, IEC_BTN_12, - IEC_BTN_13, IEC_BTN_14)[code - IEC_BTN_0], val) - - return event - -# ------------------------------------------------------------------------ -# new wxPython event classes -# ------------------------------------------------------------------------ - -grEVT_POWERMATE_BUTTON = wx.NewEventType() -grEVT_POWERMATE_ROTATE = wx.NewEventType() -grEVT_POWERMATE_SHUTTLE = wx.NewEventType() - -EVT_POWERMATE_BUTTON = wx.PyEventBinder(grEVT_POWERMATE_BUTTON, 0) -EVT_POWERMATE_ROTATE = wx.PyEventBinder(grEVT_POWERMATE_ROTATE, 0) -EVT_POWERMATE_SHUTTLE = wx.PyEventBinder(grEVT_POWERMATE_SHUTTLE, 0) - -class PMButtonEvent(wx.PyEvent): - def __init__(self, button, value): - wx.PyEvent.__init__(self) - self.SetEventType(grEVT_POWERMATE_BUTTON) - self.button = button - self.value = value - - def Clone (self): - self.__class__(self.GetId()) - - -class PMRotateEvent(wx.PyEvent): - def __init__(self, delta): - wx.PyEvent.__init__(self) - self.SetEventType (grEVT_POWERMATE_ROTATE) - self.delta = delta - - def Clone (self): - self.__class__(self.GetId()) - - -class PMShuttleEvent(wx.PyEvent): - def __init__(self, position): - wx.PyEvent.__init__(self) - self.SetEventType (grEVT_POWERMATE_SHUTTLE) - self.position = position - - def Clone (self): - self.__class__(self.GetId()) - -# ------------------------------------------------------------------------ -# Example usage -# ------------------------------------------------------------------------ - -if __name__ == '__main__': - class Frame(wx.Frame): - def __init__(self,parent=None,id=-1,title='Title', - pos=wx.DefaultPosition, size=(400,200)): - wx.Frame.__init__(self,parent,id,title,pos,size) - EVT_POWERMATE_BUTTON(self, self.on_button) - EVT_POWERMATE_ROTATE(self, self.on_rotate) - EVT_POWERMATE_SHUTTLE(self, self.on_shuttle) - self.brightness = 128 - self.pulse_speed = 0 - - try: - self.pm = powermate(self) - except: - sys.stderr.write("Unable to find PowerMate or Contour Shuttle\n") - sys.exit(1) - - self.pm.set_led_state(self.brightness, self.pulse_speed) - - - def on_button(self, evt): - print "Button %d %s" % (evt.button, - ("Released", "Pressed")[evt.value]) - - def on_rotate(self, evt): - print "Rotated %d" % (evt.delta,) - if 0: - new = max(0, min(255, self.brightness + evt.delta)) - if new != self.brightness: - self.brightness = new - self.pm.set_led_state(self.brightness, self.pulse_speed) - - def on_shuttle(self, evt): - print "Shuttle %d" % (evt.position,) - - class App(wx.App): - def OnInit(self): - title='PowerMate Demo' - self.frame = Frame(parent=None,id=-1,title=title) - self.frame.Show() - self.SetTopWindow(self.frame) - return True - - app = App() - app.MainLoop () |