diff --git a/mouseless.py b/mouseless.py new file mode 100755 index 0000000..706b03f --- /dev/null +++ b/mouseless.py @@ -0,0 +1,181 @@ +#!/usr/bin/python2 +# author: hruskam +# source: https://hruskam.wordpress.com/2015/11/14/mouseless/ +# http://phoebe.inf.upol.cz/~hruskam/temporary/mouseless.py + +# description: +# mouseless is a small python script, which enables fast movement of +# mouse cursor by successive division of screen. In effect, one can reach +# every pixel in small number of steps (logarithmic in size of screen) +# by using adequate keys on numeric keypad. +# An example of process of division is shown on the following video. +# https://www.youtube.com/watch?v=OsefVqkWpgs + +# requirements: python2 python2-qt4 xdotool + +# run mouseless: (bind script to hotkeys examples) +# Left Hand: Ctrl+Shift+A or Hyper_L + A (Capslock + A) +# Right Hand: Alt_R + Semicolon +# Numpad: Pause/Break key + + +# -*- coding: utf-8 -*- +""" +Created on Sat Sep 28 14:03:06 2013 + +@author: hruskam +""" + +verbose = True + +# import libraries +from PyQt4 import QtGui, Qt, QtCore +import sys +import itertools as it +import subprocess as sp +import re + +# configuration +xdotool_bin = '/usr/bin/xdotool' + +# graphics +pen_width = 1 +# pen used for the innermost grid +final_pen = QtGui.QPen(QtGui.QColor(255, 0, 0, 255), pen_width, QtCore.Qt.DashLine) +# pen used for the rest of grids +nonfinal_pen = QtGui.QPen(QtGui.QColor( 0, 0, 0, 255), pen_width, QtCore.Qt.SolidLine) +# opacity of the main window +win_opacity = 0.25 + +# keys used for exiting the application +exit_keys = [QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter, QtCore.Qt.Key_Escape, QtCore.Qt.Key_Semicolon, QtCore.Qt.Key_Plus] +# keys used for going to previous grid +back_keys = [QtCore.Qt.Key_Backspace, QtCore.Qt.Key_Space, QtCore.Qt.Key_0] + +# mapping keypad to selection tuples +qk = QtCore.Qt + +keyd = {qk.Key_7: (-1, -1), qk.Key_8: ( 0, -1), qk.Key_9: ( 1, -1), + qk.Key_4: (-1, 0), qk.Key_5: ( 0, 0), qk.Key_6: ( 1, 0), + qk.Key_1: (-1, 1), qk.Key_2: ( 0, 1), qk.Key_3: ( 1, 1), + qk.Key_Q: (-1, -1), qk.Key_W: ( 0, -1), qk.Key_E: ( 1, -1), + qk.Key_A: (-1, 0), qk.Key_S: ( 0, 0), qk.Key_D: ( 1, 0), + qk.Key_Z: (-1, 1), qk.Key_X: ( 0, 1), qk.Key_C: ( 1, 1), + qk.Key_U: (-1, -1), qk.Key_I: ( 0, -1), qk.Key_O: ( 1, -1), + qk.Key_J: (-1, 0), qk.Key_K: ( 0, 0), qk.Key_L: ( 1, 0), + qk.Key_M: (-1, 1), qk.Key_Comma: ( 0, 1), qk.Key_Period: ( 1, 1)} + +# mouse moving using xdotool +def mouse_move(x, y): + sp.check_call([xdotool_bin, 'mousemove', "%d" % x, "%d" % y]) + +# perform mouse move to center of an QRect +def mouse_move_center(qr): + mouse_move(qr.center().x(), qr.center().y()) + +# auxiliary functions from fplib (not yet on pypi) +# performs flattening of the list +def unlist1(value): + return list(it.chain(*value)) + +# retrieves the list without its last element +def nolast(l): + return l[:-1] + +# retrievies set of lists, which start +def prefixes(l): + return map(lambda i: l[:(i + 1)], range(len(l))) + +# centered scale of rectangle +def qt_centered_scale(qr, scale): + qc = Qt.QRect(qr) + t = Qt.QTransform().fromScale(scale, scale) + qr = t.mapRect(qr) + qr.translate(qc.center() - qr.center()) + return qr + +# obtain final rectangle, based on user selections +# e.g. if selections are [(-1, -1), (0, 0)] +# that means the user selected 7 (upper left) and 5 (center) +# in each step, the rectangle is scaled (three times smaller), preserving +# its center; and shifted accordingly +def get_selections_final_rect(selections): + qr = QtGui.QDesktopWidget().screenGeometry() + for d_x, d_y in selections: + qr = qt_centered_scale(qr, 1.0 / 3) + qr.translate(d_x * qr.width(), d_y * qr.height()) + return qr + +class GridScene(QtGui.QGraphicsScene): + def __init__(self, parent=None): + QtGui.QGraphicsScene.__init__(self, parent) + qr = QtGui.QDesktopWidget().screenGeometry() + self.draw_grid(qr, nonfinal_pen) + + def draw_grid(self, qr, pen, n=3): + for y in range(qr.top(), qr.bottom(), qr.height() / n): + self.addLine(qr.left(), y, qr.right(), y, pen=pen) + for x in range(qr.left(), qr.right(), qr.width() / n): + self.addLine(x, qr.top(), x, qr.bottom(), pen=pen) + +class GridWindow(QtGui.QMainWindow): + + def __init__(self, parent=None): + # initialize user selections to empty + self.selections = [] + # + QtGui.QMainWindow.__init__(self, parent) + self.scene = GridScene() + view = QtGui.QGraphicsView(self.scene) + # hide scrollbars + view.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + view.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + # simple layout + hbox = QtGui.QHBoxLayout() + hbox.setSpacing(0) + hbox.setMargin(0) + hbox.addWidget(view) + # window properties + self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint) + self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint) + self.setWindowState(self.windowState() | QtCore.Qt.WindowActive) + self.setWindowOpacity(win_opacity) + # + mainWidget = QtGui.QWidget() + mainWidget.setLayout(hbox) + + def widgetKeypress(event): + key = event.key() + + def update_to_user_selections(): + self.scene.clear() + for sels in [[]] + prefixes(self.selections): + qr = get_selections_final_rect(sels) + pen = final_pen if len(sels) == len(self.selections) else nonfinal_pen + self.scene.draw_grid(qr, pen) + mouse_move_center(qr) + + # if key is in numeric keypad, then add selection + if key in keyd.keys(): + self.selections.append(keyd[key]) + # if key is in backspace keys, then remove last selection + elif key in back_keys: + self.selections = nolast(self.selections) + # exit if key is in exit keys + elif key in exit_keys: + sys.exit() + + update_to_user_selections() + + mainWidget.keyPressEvent = widgetKeypress + self.setCentralWidget(mainWidget) + +# mouse_move(screen_width() / 2, screen_height() / 2) +app = QtGui.QApplication(['mouseless']) +screen = QtGui.QDesktopWidget().screenGeometry() + +# create main window maximized +window = GridWindow() +window.showMaximized() +# +sys.exit(app.exec_()) diff --git a/mouseless_keyboard_ninja.md b/mouseless_keyboard_ninja.md new file mode 100644 index 0000000..cc8191a --- /dev/null +++ b/mouseless_keyboard_ninja.md @@ -0,0 +1,49 @@ +# Mouseless Keyboard Ninja +Emulate moving the mouse around using just our keyboard. All part of becoming a keyboard ninja lols. We have two modes, mousless grid mode then mousemove mode, with these two together we can hop around and do basic task quickly without reaching for the mouse. Saves time depending on task. +* tutorial video: [Link](https://www.youtube.com/playlist?list=PLqv94xWU9zZ1cXfg3ED24G6RSt4NbFBfO) +* offical website: [Link](https://www.youtube.com/user/gotbletu) + +### install requirements + python2 python2-qt4 xdotool xterm wmctrl + +### download scripts +note: put it all in the same folder and make sure to chmod +x the scripts to give it permission to run + + wget https://raw.githubusercontent.com/gotbletu/shownotes/master/mouseless_mousemove_mode.sh + wget https://raw.githubusercontent.com/gotbletu/shownotes/master/mouseless.py + wget https://raw.githubusercontent.com/gotbletu/shownotes/master/mousemove_mode.sh + + +### activate + bind (mouseless_mousemove_mode.sh) script to a hotkey + + Example: + Left Hand: Ctrl+Shift+A or Hyper_L + A (Capslock + A) + Right Hand: Alt_R + Semicolon + Numpad: Pause/Break key + +### references +- https://hruskam.wordpress.com/2015/11/14/mouseless/ +- http://phoebe.inf.upol.cz/~hruskam/temporary/mouseless.py +- https://bbs.archlinux.org/viewtopic.php?pid=1106808#p1106808 +- http://stackoverflow.com/a/10680015 +- http://stackoverflow.com/a/11759139 +- http://www.bbc.co.uk/accessibility/guides/keyboard_mouse/computer/linux/gnome/ + + +### contact + + _ _ _ _ + __ _ ___ | |_| |__ | | ___| |_ _ _ + / _` |/ _ \| __| '_ \| |/ _ \ __| | | | + | (_| | (_) | |_| |_) | | __/ |_| |_| | + \__, |\___/ \__|_.__/|_|\___|\__|\__,_| + |___/ + +- http://www.youtube.com/user/gotbletu +- https://twitter.com/gotbletu +- https://plus.google.com/+gotbletu +- https://github.com/gotbletu +- gotbletu@gmail.com + + diff --git a/mouseless_mousemove_mode.sh b/mouseless_mousemove_mode.sh new file mode 100755 index 0000000..003e853 --- /dev/null +++ b/mouseless_mousemove_mode.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# _ _ _ _ +# __ _ ___ | |_| |__ | | ___| |_ _ _ +# / _` |/ _ \| __| '_ \| |/ _ \ __| | | | +#| (_| | (_) | |_| |_) | | __/ |_| |_| | +# \__, |\___/ \__|_.__/|_|\___|\__|\__,_| +# |___/ +# https://www.youtube.com/user/gotbletu +# https://twitter.com/gotbletu +# https://plus.google.com/+gotbletu +# https://github.com/gotbletu +# gotbleu@gmail.com + +# tutorial video playlist: https://www.youtube.com/playlist?list=PLqv94xWU9zZ1cXfg3ED24G6RSt4NbFBfO + +# description: start mouseless (Grid) then wait for it to exit to start mousemove mode +# requires: mouselesss.py, mousemove_mode.sh and this script (mouseless_mousemove_mode.sh) to be in the same folder + +# bind this script (mouseless_mousemove_mode.sh) to hotkeys examples +# Left Hand: Ctrl+Shift+A or Hyper_L + A (Capslock + A) +# Right Hand: Alt_R + Semicolon +# Numpad: Pause/Break key + +# kill existing script +wmctrl -F -c "mouseless" +sleep 0.1 +wmctrl -F -c "mousemove_mode.sh" + +# start script +DIR=$(dirname "$0") +"$DIR"/mouseless.py && xterm -geometry 0x0+0+0 -e "$DIR"/mousemove_mode.sh + diff --git a/mousemove_mode.sh b/mousemove_mode.sh new file mode 100755 index 0000000..561dc49 --- /dev/null +++ b/mousemove_mode.sh @@ -0,0 +1,68 @@ +#!/bin/bash +# _ _ _ _ +# __ _ ___ | |_| |__ | | ___| |_ _ _ +# / _` |/ _ \| __| '_ \| |/ _ \ __| | | | +#| (_| | (_) | |_| |_) | | __/ |_| |_| | +# \__, |\___/ \__|_.__/|_|\___|\__|\__,_| +# |___/ +# https://www.youtube.com/user/gotbletu +# https://twitter.com/gotbletu +# https://plus.google.com/+gotbletu +# https://github.com/gotbletu +# gotbleu@gmail.com + +# tutorial video playlist: https://www.youtube.com/playlist?list=PLqv94xWU9zZ1cXfg3ED24G6RSt4NbFBfO + +# description: move mouse cursor around, mouse clicks and drag using just the keyboard (vim style, wasd, numpad, or arrow keys) +# requirements: xdotool xterm wmctrl + +# note: if you want to use this script by itself then bind commmand to a hotkey: +# xterm -geometry 0x0+0+0 -e /path/to/mousemove_mode.sh + +# references: +# https://bbs.archlinux.org/viewtopic.php?pid=1106808#p1106808 +# http://stackoverflow.com/a/10680015 +# http://stackoverflow.com/a/11759139 +# http://www.bbc.co.uk/accessibility/guides/keyboard_mouse/computer/linux/gnome/ + +# use to set always on top and refocusing +title="mousemove_mode.sh" + +# Always on top: check if terminal script window is focus or not, if not then refocus it +refocus_window() { + while : + do + detect_focus_window=$(xdotool getwindowfocus getwindowname) + if [[ "$detect_focus_window" != "$title" ]]; then + wmctrl -a "$title" + fi + done +} +# background the process +refocus_window & + +# movemouse/mouse clicks using xdotools (using vim, wasd, numpad or arrow keys) +while read -rsn1 key # 1 char (not delimiter), silent, dont need to hit enter key +do + + # catch multi-char special key sequences + # allows special arrow keys to work + read -sN1 -t 0.0001 k1 + read -sN1 -t 0.0001 k2 + read -sN1 -t 0.0001 k3 + key+=${k1}${k2}${k3} + + case "$key" in + h|a|4|$'\e[D') xdotool mousemove_relative -- -15 0 ;; # move left + j|s|5|$'\e[B') xdotool mousemove_relative 0 15 ;; # move down + k|w|8|$'\e[A') xdotool mousemove_relative -- 0 -15 ;; # move up + l|d|6|$'\e[C') xdotool mousemove_relative 15 0 ;; # move right + u|e|7|'/') xdotool click 1 ;; # primary click + o|q|9|'-') sleep 0.2 && xdotool click 3 ;; # secondary click (menu click) + i|r|'*'|',') xdotool click 2 ;; # middle click + n|z|0) xdotool mousedown 1 ;; # highlight | drag&drop mode (begin) + m|x|'.') xdotool mouseup 1 ;; # highlight | drag&drop mode (end) + $'\e'|';'|'+') break ;; # quit + esac + +done