Skip to content

Instantly share code, notes, and snippets.

@thenoviceoof
Last active January 4, 2016 14:59
Show Gist options
  • Save thenoviceoof/8637497 to your computer and use it in GitHub Desktop.
Save thenoviceoof/8637497 to your computer and use it in GitHub Desktop.
Keyboard Mouse
# - A first approximation of using the keyboard as a trackpad
# - It uses curses/Xlib for input and mouse interaction, so if you don't
# have those you're going to have a bad time
# - This is only a proof of concept, sniffing X for keystrokes and
# finding out how to seamlessly pop in and out of
# keyboard-as-trackpad-mode is left as an exercise to the reader
# - It turns out this isn't a very good control scheme, given the
# limited vertical resolution (4-6 rows vs. ~12 columns), and that
# it additionally suffers from all the problems inherent in
# acceleration based control schemes
# - You will have to edit KEYBOARD_MAP to suit your own keyboard: I
# use a dvorak layout, so it is unlikely to fit your
# layout. Creating a program to automatically create the map from a
# photo of a keyboard is left as an exercise to the reader
#
# "THE BEER-WARE LICENSE" (Revision 42):
# <thenoviceoof> wrote this file. As long as you retain this notice
# you can do whatever you want with this stuff. If we meet some day,
# and you think this stuff is worth it, you can buy me a beer in
# return
# - thenoviceoof
import curses
import math
import time
import traceback
import Xlib
from Xlib import display
DISPLAY = display.Display()
def move_mouse_x(x,y):
DISPLAY.warp_pointer(x,y)
DISPLAY.sync()
def dist(a, b):
return ((a[0]-b[0])**2 + (a[1]-b[1])**2)**0.5
def drag(dx,dy):
v = (dx**2 + dy**2)**0.5
ang = math.atan2(dy,dx)
v *= 0.9
# threshold the velocity
if v < 0.5:
v = 0
dx = v * math.cos(ang)
dy = v * math.sin(ang)
return dx,dy
KEYBOARD_MAP = {
'1': (-0.3,-1),
'2': (0.6,-1),
'3': (1.6,-1),
'4': (2.6,-1),
'5': (3.6,-1),
'6': (4.6,-1),
'7': (5.6,-1),
'8': (6.6,-1),
'9': (7.6,-1),
'0': (8.6,-1),
'[': (9.6,-1),
']': (10.6,-1),
'\'': (0,0),
',': (1,0),
'.': (2,0),
'p': (3,0),
'y': (4,0),
'f': (5,0),
'g': (6,0),
'c': (7,0),
'r': (8,0),
'l': (9,0),
'/': (10,0),
'=': (11,0),
'a': (0.3,1),
'o': (1.3,1),
'e': (2.3,1),
'u': (3.3,1),
'i': (4.3,1),
'd': (5.3,1),
'h': (6.3,1),
't': (7.3,1),
'n': (8.3,1),
's': (9.3,1),
'-': (10.3,1),
';': (0.6,2),
'q': (1.6,2),
'j': (2.6,2),
'k': (3.6,2),
'x': (4.6,2),
'b': (5.6,2),
'm': (6.6,2),
'w': (7.6,2),
'v': (8.6,2),
'z': (9.6,2),
}
def main():
# velocity
dx = 0
dy = 0
# previous keyboard position for acceleration
prev = None
prev_time = None
# set up curses
stdscr = curses.initscr()
curses.cbreak()
stdscr.nodelay(1)
char = None
while 1:
# grab keystrokes
char = stdscr.getch()
# turn them into acceleration
if char != -1 and chr(char) in KEYBOARD_MAP:
t = time.time()
char = chr(char)
x,y = KEYBOARD_MAP[char]
if prev and dist(prev, (x,y)) > 1.5:
# assume this is the user picking up and moving their
# finger
dx, dy = 0, 0
elif prev:
# accelerate them over that away
dx += (x - prev[0]) / (t - prev_time)
dy += (y - prev[1]) / (t - prev_time)
prev = (x,y)
prev_time = t
# move the mouse according to the velocity
move_mouse_x(dx,dy)
# decelerate the movement
dx,dy = drag(dx,dy)
time.sleep(0.02)
if __name__ == '__main__':
try:
main()
except Exception, e:
# clean up after curses
curses.nocbreak()
curses.endwin()
# re-raising an exception is annoyingly opaque
traceback.print_exc()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment