Skip to content

Commit

Permalink
squash wip commits
Browse files Browse the repository at this point in the history
  • Loading branch information
gvalkov committed Nov 6, 2012
1 parent c068585 commit 5fd8b04
Show file tree
Hide file tree
Showing 8 changed files with 472 additions and 0 deletions.
28 changes: 28 additions & 0 deletions LICENSE
@@ -0,0 +1,28 @@
Copyright (c) 2011 Georgi Valkov. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.

3. Neither the name of author nor the names of its contributors may
be used to endorse or promote products derived from this software
without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL GEORGI VALKOV BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1 change: 1 addition & 0 deletions MANIFEST.in
@@ -0,0 +1 @@
include README.rst
138 changes: 138 additions & 0 deletions README.rst
@@ -0,0 +1,138 @@
Pyzmo
=====

Pyzmo is a small hotkey library for Python 2.7+ based on
python-evdev_. It has support for keys, key chords (simultaneously
pressed keys) and key sequences (keys pressed in succession).


Usage
=====

::

from pyzmo import *
from evdev.ecodes import *

# triggered when KEY_PLAYPAUSE is pressed
@key(KEY_PLAYPAUSE)
def playpause(events):
pass

# triggered when KEY_B is pressed, held or released
@key(KEY_B, states=['down', 'hold', 'up'])
def back(events):
pass

# triggered when either KEY_C or KEY_D are pressed
@key(KEY_C, KEY_D)
def c_or_d(events):
pass

# triggered when KEY_LEFTCTRL, KEY_LEFTALT and KEY_DELETE are
# pressed at the same time
@chord(KEY_LEFTCTRL, KEY_LEFTALT, KEY_DELETE)
def ctrlaltdel(events):
pass

# triggered when KEY_A, KEY_B and KEY_C are pressed one after the other
@keyseq(KEY_A, KEY_B, KEY_C)
def abc(events):
pass

# specifying multiple sequences for one callback (syntax applies for
# @chord and @event as well)
@keyseq( (KEY_1, KEY_2, KEY_3), (KEY_Z, KEY_X, KEY_C) )
def zxc_or_123(events):
pass

# each handler receives the list of input events, because of
# which it was triggered
@chord(e.KEY_LEFTMETA, e.KEY_A)
def term(events):
for event in events:
print(event) # instance of evdev.events.InputEvent
#event at 1352244701.749908, code 125, type 01, val 01
#event at 1352244701.861897, code 30, type 01, val 01

## Note 1:
# If we define two triggers:
# - @chord(KEY_LEFTCTRL, KEY_V)
# - @key(KEY_V)
#
# Pressing 'ctrl-v' will run both their callbacks. To stop
# processing any further triggers after a match is made, use:
@chord(KEY_LEFTCTRL, KEY_V, quick=True)
def copy(events):
pass

## Note 2:
# Pyzmo can actually match arbitrary input events. The following
# will be triggered on scroll-wheel movement:
@event(EV_REL, REL_WHEEL, -1)
def vertical_scroll(events):
pass

# start main loop
poll('/dev/input/event1', '/dev/input/event2')

# .. or if you wish to get exclusive access to a input device
from evdev import InputDevice
dev = InputDevice('/dev/input/eventX')
dev.grab()
poll(dev)


You can avoid polluting the global namespace with::

from pyzmo import EventHandler
from evdev import ecodes as e

app = EventHandler('name')

@app.key(e.KEY_F)
def f(events): pass

@app.poll(...)


Installing
----------

The latest stable version of pyzmo is available on pypi, while the
development version can be installed from github:

.. code-block:: bash
$ pip install pyzmo # latest stable version
$ pip install git+git://github.com/gvalkov/pyzmo.git # latest development version
Alternatively, you can install it manually like any other python package:

.. code-block:: bash
$ git clone git@github.com:gvalkov/pyzmo.git
$ cd pyzmo
$ git reset --hard HEAD $versiontag
$ python setup.py install
Similar Projects
----------------

- triggerhappy_

- actkbd_


License
-------

Pyzmo is released under the terms of the `New BSD License`_.


.. _python-evdev: https://github.com:gvalkov/python-evdev.git
.. _triggerhappy: https://github.com/wertarbyte/triggerhappy.git
.. _actkbd: http://users.softlab.ece.ntua.gr/~thkala/projects/actkbd/
.. _`NEW BSD License`: https://raw.github.com/gvalkov/pyzmo/master/LICENSE

25 changes: 25 additions & 0 deletions pyzmo/__init__.py
@@ -0,0 +1,25 @@
import functools
from pyzmo.handler import EventHandler
from evdev import ecodes


default_handler = EventHandler('default-handler')

def default_handler_wrap(name):
'Return a callable that relays calls to a default handler.'

# @functools.wraps(getattr(default_handler, name))
def wrapper(*args, **kw):
return getattr(default_handler, name)(*args, **kw)

return wrapper

key = default_handler_wrap('key')
chord = default_handler_wrap('chord')
keyseq = default_handler_wrap('keyseq')
event = default_handler_wrap('event')
poll = default_handler_wrap('poll')


__all__ = ('key', 'chord', 'keyseq', 'event', 'poll', 'default_handler',
'EventHandler', 'ecodes')
99 changes: 99 additions & 0 deletions pyzmo/decorators.py
@@ -0,0 +1,99 @@
import logging

from evdev import ecodes as e
from pyzmo.util import maketuple, raise_on_unknown_key

log = logging.getLogger('decorator')


class Decorator:
def __init__(self, triggers, *events, **options):
'''
@param triggers: reference to EventHandler.triggers
@param events: list of events - subclass specific
@param options: dictionary of options
'''

self.triggers = triggers
self.options = options
self.evseq = tuple(self.create_event_sequence(events))

def create_event_sequence(self, events):
raise NotImplementedError

def __call__(self, func):
for seq in self.evseq:
self.triggers[seq] = (func, self.options)
return func

class EventDecorator(Decorator):
'''
Arbitrary input event decorator. Example:
@event(EV_REL, REL_WHEEL, -1)
@event((EV_REL, REL_WHEEL, 1), (EV_REL, REL_HWHEEL, 1))
The latter is used to specify multiple events for a single
callback (not a sequence of events).
'''

def create_event_sequence(self, events):
for ev in maketuple(events):
yield (ev,)

class KeyDecorator(Decorator):
'''
Single key (EV_KEY or EV_BTN) decorator. Example:
@key(KEY_A)
@key(KEY_B, KEY_C)
@key(KEY_D, states=('up', 'down', 'hold'))
By default callbacks will be triggered once the key is
pressed (value 1). The ``states`` options
'''
def create_event_sequence(self, keys):
states = self.options.get('states', ['down'])

for pair in maketuple(keys):
for key in pair:
raise_on_unknown_key(key)
for state in states:
val = {'up':0, 'down':1, 'hold':2}[state]
ev = [(e.EV_KEY, key, val)]
yield tuple(ev)

class ChordDecorator(Decorator):
'''
A combination of keys, pressed simultaneously. Example:
@chord(KEY_LEFTCTRL, KEY_LEFTALT, KEY_DELETE)
@chord((KEY_LEFTCTRL, KEY_C), (KEY_LEFTCTRL, KEY_X))
'''
def create_event_sequence(self, keys):
for pair in maketuple(keys):
seq = []
for key in pair:
raise_on_unknown_key(key)
ev = (e.EV_KEY, key, 1)
seq.append(ev)

yield tuple(seq)

class SequenceDecorator(Decorator):
'''
A sequence of keys, pressed one after the other. Example:
@keyseq(KEY_Q, KEY_W, KEY_E)
@keyseq((KEY_A, KEY_B, KEY_C), (KEY_1, KEY_2, KEY_3))
'''
def create_event_sequence(self, keys):
for pair in maketuple(keys):
seq = []
for key in pair:
raise_on_unknown_key(key)
events = ((e.EV_KEY, key, 1), (e.EV_KEY, key, 0))
for ev in events:
seq.append(ev)

yield tuple(seq)

0 comments on commit 5fd8b04

Please sign in to comment.