diff options
Diffstat (limited to 'layouts/community/ergodox/algernon/tools')
5 files changed, 1494 insertions, 0 deletions
diff --git a/layouts/community/ergodox/algernon/tools/heatmap-layout.ADORE.json b/layouts/community/ergodox/algernon/tools/heatmap-layout.ADORE.json new file mode 100644 index 0000000000..e09efecc46 --- /dev/null +++ b/layouts/community/ergodox/algernon/tools/heatmap-layout.ADORE.json @@ -0,0 +1,486 @@ +[ + { + "backcolor": "#ffffff", + "name": "ErgoDox - algernon's layout: Heatmap", + "author": "Gergely Nagy <kbd@gergo.csillger.hu>", + "notes": "See [here](https://github.com/algernon/ergodox-layout#readme) for the QMK keymap source.", + "switchMount": "cherry", + "switchBrand": "gateron", + "switchType": "KS-3-Tea", + "pcb": true, + "css": ".keyborder { -webkit-filter: blur(5px); filter: blur(5px); } .keytop { -webkit-filter: blur(10px); } .keylabels { border: 1px solid black; }" + }, + [ + { + "x": 3.5, + "fa": [ + 0, + 0, + 2 + ] + }, + "*\n5\nF5", + { + "x": 10.5, + "a": 4, + "fa": [ + 0, + 0, + 2 + ] + }, + "#\n4\nF4" + ], + [ + { + "y": -0.875, + "x": 2.5 + }, + "@\n7\nF7", + { + "x": 1 + }, + "^\n3\nF3", + { + "x": 8.5 + }, + "!\n2\nF2", + { + "x": 1 + }, + "&\n6\nF6" + ], + [ + { + "y": -0.875, + "x": 5.5 + }, + "$\n1\nF1", + { + "a": 7, + "f": 3 + }, + "F11", + { + "x": 4.5, + "f": 3 + }, + "Fx", + { + "a": 4, + "f": 3, + "fa": [ + 0, + 0, + 2 + ] + }, + "%\n0\nF10" + ], + [ + { + "y": -0.875, + "f": 9, + "a": 6, + "w": 1.5 + }, + "\n\n<i class='kb kb-Multimedia-Play-Pause'></i>", + { + "f": 3, + "a": 4, + "fa": [ + 0, + 0, + 2 + ] + }, + " \n9\nF9", + { + "x": 14.5 + }, + " \n8\nF8", + { + "a": 7, + "w": 1.5 + }, + "STENO" + ], + [ + { + "y": -0.375, + "x": 3.5, + "a": 6 + }, + "C", + { + "x": 10.5 + }, + "L" + ], + [ + { + "y": -0.875, + "x": 2.5, + "a": 6 + }, + "W", + { + "x": 1, + "a": 6 + }, + "H", + { + "x": 8.5 + }, + "G", + { + "x": 1 + }, + "P" + ], + [ + { + "y": -0.875, + "x": 5.5 + }, + "F", + { + "a": 4, + "fa": [0, 0, 0], + "h": 1.5 + }, + "{\n(\n[", + { + "x": 4.5, + "h": 1.5 + }, + "}\n)\n]", + { + "a": 6 + }, + "M" + ], + [ + { + "y": -0.875, + "f": 3, + "a": 4, + "w": 1.5 + }, + "\n\n|\n\\", + { + "a": 6, + "f": 3 + }, + "X", + { + "x": 14.5, + "a": 4 + }, + "/\n?", + { + "a": 4, + "w": 1.5 + }, + "~\n`" + ], + [ + { + "y": -0.375, + "x": 3.5, + "a": 6 + }, + "E", + { + "x": 10.5 + }, + "T" + ], + [ + { + "y": -0.875, + "x": 2.5 + }, + "O", + { + "x": 1, + "n": true + }, + "I", + { + "x": 8.5, + "n": true + }, + "R", + { + "x": 1 + }, + "N" + ], + [ + { + "y": -0.875, + "x": 5.5 + }, + "U", + { + "x": 6.5 + }, + "D" + ], + [ + { + "y": -0.875, + "fa": [ + 6 + ], + "w": 1.5 + }, + "<i class='fa fa-arrows'></i>\n\nTab", + { + "f": 3 + }, + "A", + { + "x": 14.5, + "f": 3 + }, + "S", + { + "a": 4, + "fa": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6 + ], + "w": 1.5 + }, + "+\n=" + ], + [ + { + "y": -0.625, + "x": 6.5, + "a": 7, + "f": 9, + "h": 1.5 + }, + "<i class='fa fa-columns'></i>", + { + "x": 4.5, + "h": 1.5 + }, + "<i class='fa fa-table'></i>" + ], + [ + { + "y": -0.75, + "x": 3.5, + "a": 4, + "f": 3 + }, + "\"\n'", + { + "x": 10.5, + "a": 6, + "f": 3 + }, + "V" + ], + [ + { + "y": -0.875, + "x": 2.5, + "a": 6 + }, + "Q", + { + "x": 1, + "a": 4 + }, + "<\n,", + { + "x": 8.5, + "a": 6 + }, + "K", + { + "x": 1 + }, + "Y" + ], + [ + { + "y": -0.875, + "x": 5.5, + "a": 4 + }, + ">\n.", + { + "x": 6.5, + "a": 6 + }, + "B" + ], + [ + { + "y": -0.875, + "f": 9, + "w": 1.5, + "g": true + }, + "", + { + "a": 6, + "f": 3, + "g": false + }, + "Z", + { + "x": 14.5 + }, + "J", + { + "f": 9, + "g": true, + "w": 1.5, + "a": 4 + }, + "" + ], + [ + { + "y": -0.375, + "x": 3.5, + "g": true, + "a": 7, + "f": 3 + }, + "", + { + "x": 10.5 + }, + "" + ], + [ + { + "y": -0.875, + "x": 2.5 + }, + "", + { + "x": 1, + "g": false, + "a": 5 + }, + ";\n:", + { + "x": 8.5 + }, + "_\n-", + { + "x": 1, + "g": true, + "a": 7 + }, + "" + ], + [ + { + "y": -0.75, + "x": 0.5 + }, + "", + {}, + "", + { + "x": 14.5 + }, + "", + {}, + "" + ], + [ + { + "r": 30, + "rx": 6.5, + "ry": 4.25, + "y": -1, + "x": 1, + "g": false + }, + "Alt", + { + "a": 4, + "fa": [ + 0, + 0, + 0, + 9 + ] + }, + "\n\n\n<i class='kb kb-logo-linux-debian fa-large'></i>" + ], + [ + { + "a": 7, + "f": 9, + "h": 2 + }, + "<i class='kb kb-Unicode-BackSpace-DeleteLeft-Big'></i>", + { + "h": 2 + }, + "<i class='fa fa-angle-double-up'></i>", + { + "f": 3 + }, + "Ctrl" + ], + [ + { + "x": 2 + }, + "ESC" + ], + [ + { + "r": -30, + "rx": 13, + "y": -1, + "x": -3, + "f": 2 + }, + "MEDIA", + {}, + "DEL" + ], + [ + { + "x": -3 + }, + "HUN", + { + "f": 9, + "h": 2 + }, + "<i class='kb kb-Return-2'></i>", + { + "f": 3, + "h": 2 + }, + "SPC" + ], + [ + { + "x": -3, + "f": 2 + }, + "LEAD" + ] +] diff --git a/layouts/community/ergodox/algernon/tools/heatmap-layout.Dvorak.json b/layouts/community/ergodox/algernon/tools/heatmap-layout.Dvorak.json new file mode 100644 index 0000000000..1e53281c56 --- /dev/null +++ b/layouts/community/ergodox/algernon/tools/heatmap-layout.Dvorak.json @@ -0,0 +1,477 @@ +[ + { + "backcolor": "#ffffff", + "name": "ErgoDox - algernon's layout: Heatmap", + "author": "Gergely Nagy <kbd@gergo.csillger.hu>", + "notes": "See [here](https://github.com/algernon/ergodox-layout#readme) for the QMK keymap source.", + "switchMount": "cherry", + "switchBrand": "gateron", + "switchType": "KS-3-Tea", + "pcb": true, + "css": ".keyborder { -webkit-filter: blur(5px); filter: blur(5px); } .keytop { -webkit-filter: blur(10px); } .keylabels { border: 1px solid black; }" + }, + [ + { + "x": 3.5, + "fa": [ + 0, + 0, + 2 + ] + }, + "*\n5\nF5", + { + "x": 10.5, + "a": 4, + "fa": [ + 0, + 0, + 2 + ] + }, + "#\n4\nF4" + ], + [ + { + "y": -0.875, + "x": 2.5 + }, + "@\n7\nF7", + { + "x": 1 + }, + "^\n3\nF3", + { + "x": 8.5 + }, + "!\n2\nF2", + { + "x": 1 + }, + "&\n6\nF6" + ], + [ + { + "y": -0.875, + "x": 5.5 + }, + "$\n1\nF1", + { + "a": 7, + "f": 3 + }, + "F11", + { + "x": 4.5, + "f": 3 + }, + "Fx", + { + "a": 4, + "f": 3, + "fa": [ + 0, + 0, + 2 + ] + }, + "%\n0\nF10" + ], + [ + { + "y": -0.875, + "f": 6, + "a": 6, + "w": 1.5 + }, + "<i class='fa fa-fast-backward'></i>\n\n<i class='fa fa-fast-forward'></i>", + { + "f": 3, + "a": 4, + "fa": [ + 0, + 0, + 2 + ] + + }, + " \n9\nF9", + { + "x": 14.5 + }, + " \n8\nF8", + { + "a": 7, + "w": 1.5 + }, + "STENO" + ], + [ + { + "y": -0.375, + "x": 3.5, + "a": 4 + }, + ">\n.", + { + "x": 10.5, + "a": 6 + }, + "C" + ], + [ + { + "y": -0.875, + "x": 2.5, + "a": 4 + }, + "<\n,", + { + "x": 1, + "a": 6 + }, + "P", + { + "x": 8.5 + }, + "G", + { + "x": 1 + }, + "R" + ], + [ + { + "y": -0.875, + "x": 5.5 + }, + "Y", + { + "a": 4, + "h": 1.5 + }, + "{\n(\n[", + { + "x": 4.5, + "h": 1.5 + }, + "}\n)\n]", + { + "a": 6 + }, + "F" + ], + [ + { + "y": -0.875, + "f": 3, + "a": 4, + "w": 1.5 + }, + "\n\n~\n`", + { + "a": 4, + "f": 3 + }, + "\"\n'", + { + "x": 14.5, + "a": 6 + }, + "L", + { + "a": 4, + "w": 1.5 + }, + "|\n\\" + ], + [ + { + "y": -0.375, + "x": 3.5, + "a": 6 + }, + "E", + { + "x": 10.5 + }, + "T" + ], + [ + { + "y": -0.875, + "x": 2.5 + }, + "O", + { + "x": 1, + "n": true + }, + "U", + { + "x": 8.5, + "n": true + }, + "H", + { + "x": 1 + }, + "N" + ], + [ + { + "y": -0.875, + "x": 5.5 + }, + "I", + { + "x": 6.5 + }, + "D" + ], + [ + { + "y": -0.875, + "fa": [ + 6 + ], + "w": 1.5 + }, + "<i class='fa fa-arrows'></i>\n\nTab", + { + "f": 3 + }, + "A", + { + "x": 14.5, + "f": 3 + }, + "S", + { + "a": 4, + "fa": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6 + ], + "w": 1.5 + }, + "+\n=" + ], + [ + { + "y": -0.625, + "x": 6.5, + "a": 7, + "f": 9, + "h": 1.5 + }, + "<i class='fa fa-columns'></i>", + { + "x": 4.5, + "h": 1.5 + }, + "<i class='fa fa-table'></i>" + ], + [ + { + "y": -0.75, + "x": 3.5, + "f": 3, + "a": 6 + }, + "J", + { + "x": 10.5 + }, + "W" + ], + [ + { + "y": -0.875, + "x": 2.5 + }, + "Q", + { + "x": 1 + }, + "K", + { + "x": 8.5 + }, + "M", + { + "x": 1 + }, + "V" + ], + [ + { + "y": -0.875, + "x": 5.5 + }, + "X", + { + "x": 6.5 + }, + "B" + ], + [ + { + "y": -0.875, + "f": 9, + "w": 1.5 + }, + "\n\n<i class='kb kb-Multimedia-Play-Pause'></i>", + { + "a": 4, + "f": 3 + }, + "?\n/", + { + "x": 14.5, + "a": 6 + }, + "Z", + { + "f": 9, + "w": 1.5 + }, + "<i class='kb kb-Multimedia-Stop'></i>" + ], + [ + { + "y": -0.375, + "x": 3.5, + "g": true, + "a": 7, + "f": 3 + }, + "", + { + "x": 10.5 + }, + "" + ], + [ + { + "y": -0.875, + "x": 2.5 + }, + "", + { + "x": 1, + "g": false, + "a": 5 + }, + ";\n:", + { + "x": 8.5 + }, + "_\n-", + { + "x": 1, + "g": true, + "a": 7 + }, + "" + ], + [ + { + "y": -0.75, + "x": 0.5 + }, + "", + {}, + "", + { + "x": 14.5 + }, + "", + {}, + "" + ], + [ + { + "r": 30, + "rx": 6.5, + "ry": 4.25, + "y": -1, + "x": 1, + "g": false + }, + "Alt", + { + "a": 4, + "fa": [ + 0, + 0, + 0, + 9 + ] + }, + "\n\n\n<i class='kb kb-logo-linux-debian fa-large'></i>" + ], + [ + { + "a": 7, + "f": 9, + "h": 2 + }, + "<i class='kb kb-Unicode-BackSpace-DeleteLeft-Big'></i>", + { + "h": 2 + }, + "<i class='fa fa-angle-double-up'></i>", + { + "f": 3 + }, + "Ctrl" + ], + [ + { + "x": 2 + }, + "ESC" + ], + [ + { + "r": -30, + "rx": 13, + "y": -1, + "x": -3, + "f": 2 + }, + "MEDIA", + {}, + "DEL" + ], + [ + { + "x": -3 + }, + "LEAD", + { + "f": 9, + "h": 2 + }, + "<i class='kb kb-Return-2'></i>", + { + "f": 3, + "h": 2 + }, + "SPC" + ], + [ + { + "x": -3, + "f": 2 + }, + "HUN" + ] +] diff --git a/layouts/community/ergodox/algernon/tools/hid-commands b/layouts/community/ergodox/algernon/tools/hid-commands new file mode 100644 index 0000000000..54ca7556ab --- /dev/null +++ b/layouts/community/ergodox/algernon/tools/hid-commands @@ -0,0 +1,80 @@ +#!/bin/bash +set -e + +LAST_APPSEL_START=0 + +cmd_wm () { + WIN="$(xdotool getactivewindow)" + wmctrl -i -r ${WIN} -b remove,maximized_vert,maximized_horz + xdotool windowsize ${WIN} 100% 100% + wmctrl -i -r ${WIN} -b add,maximized_vert,maximized_horz +} + +_cmd_appsel () { + wmctrl -x -a $1 || true + xdotool key Escape +} + +cmd_appsel_music () { + wmctrl -x -a rhythmbox || wmctrl -x -a spotify || \ + wmctrl -x -a banshee || wmctrl -x -a kodi || true + xdotool key Escape +} + +cmd_appsel_slack () { + _cmd_appsel slack +} + +cmd_appsel_emacs () { + _cmd_appsel emacs +} + +cmd_appsel_term () { + _cmd_appsel gnome-terminal +} + +cmd_appsel_chrome () { + _cmd_appsel chrom +} + +cmd_appsel_start () { + if [ ! -z "${DISABLE_APPSEL_START}" ]; then + return + fi + + APPSEL_START=$(date +%s) + if [ $APPSEL_START -lt $(expr $LAST_APPSEL_START + 10) ]; then + return + fi + LAST_APPSEL_START=$APPSEL_START + notify-send -t 1000 "Please select an application!" -c device -u low \ + -i /usr/share/icons/Adwaita/24x24/devices/video-display.png +} + +cmd_reflash () { + teensy_loader_cli -v -w ~/src/ext/qmk_firmware/algernon.hex --mcu atmega32u4 || true +} + +cmd_help () { + cat <<EOF +Use the source, Luke! +EOF +} + +while read l; do + case "$l" in + "CMD:"*) + ;; + *) + continue + ;; + esac + + cmd="$(echo $l | cut -d: -f2-)" + + echo "Got command: ${cmd}" + + if type cmd_${cmd} >/dev/null 2>&1; then + cmd_${cmd} + fi +done diff --git a/layouts/community/ergodox/algernon/tools/log-to-heatmap.py b/layouts/community/ergodox/algernon/tools/log-to-heatmap.py new file mode 100644 index 0000000000..e927e0e39d --- /dev/null +++ b/layouts/community/ergodox/algernon/tools/log-to-heatmap.py @@ -0,0 +1,344 @@ +#! /usr/bin/env python3 +import json +import os +import sys +import re +import argparse +import time + +from math import floor +from os.path import dirname +from subprocess import Popen, PIPE, STDOUT +from blessings import Terminal + +class Heatmap(object): + coords = [ + [ + # Row 0 + [ 4, 0], [ 4, 2], [ 2, 0], [ 1, 0], [ 2, 2], [ 3, 0], [ 3, 2], + [ 3, 4], [ 3, 6], [ 2, 4], [ 1, 2], [ 2, 6], [ 4, 4], [ 4, 6], + ], + [ + # Row 1 + [ 8, 0], [ 8, 2], [ 6, 0], [ 5, 0], [ 6, 2], [ 7, 0], [ 7, 2], + [ 7, 4], [ 7, 6], [ 6, 4], [ 5, 2], [ 6, 6], [ 8, 4], [ 8, 6], + ], + [ + # Row 2 + [12, 0], [12, 2], [10, 0], [ 9, 0], [10, 2], [11, 0], [ ], + [ ], [11, 2], [10, 4], [ 9, 2], [10, 6], [12, 4], [12, 6], + ], + [ + # Row 3 + [17, 0], [17, 2], [15, 0], [14, 0], [15, 2], [16, 0], [13, 0], + [13, 2], [16, 2], [15, 4], [14, 2], [15, 6], [17, 4], [17, 6], + ], + [ + # Row 4 + [20, 0], [20, 2], [19, 0], [18, 0], [19, 2], [], [], [], [], + [19, 4], [18, 2], [19, 6], [20, 4], [20, 6], [], [], [], [] + ], + [ + # Row 5 + [ ], [23, 0], [22, 2], [22, 0], [22, 4], [21, 0], [21, 2], + [24, 0], [24, 2], [25, 0], [25, 4], [25, 2], [26, 0], [ ], + ], + ] + + def set_attr_at(self, block, n, attr, fn, val): + blk = self.heatmap[block][n] + if attr in blk: + blk[attr] = fn(blk[attr], val) + else: + blk[attr] = fn(None, val) + + def coord(self, col, row): + return self.coords[row][col] + + @staticmethod + def set_attr(orig, new): + return new + + def set_bg(self, coords, color): + (block, n) = coords + self.set_attr_at(block, n, "c", self.set_attr, color) + #self.set_attr_at(block, n, "g", self.set_attr, False) + + def set_tap_info(self, coords, count, cap): + (block, n) = coords + def _set_tap_info(o, _count, _cap): + ns = 4 - o.count ("\n") + return o + "\n" * ns + "%.02f%%" % (float(_count) / float(_cap) * 100) + + if not cap: + cap = 1 + self.heatmap[block][n + 1] = _set_tap_info (self.heatmap[block][n + 1], count, cap) + + @staticmethod + def heatmap_color (v): + colors = [ [0.3, 0.3, 1], [0.3, 1, 0.3], [1, 1, 0.3], [1, 0.3, 0.3]] + fb = 0 + if v <= 0: + idx1, idx2 = 0, 0 + elif v >= 1: + idx1, idx2 = len(colors) - 1, len(colors) - 1 + else: + val = v * (len(colors) - 1) + idx1 = int(floor(val)) + idx2 = idx1 + 1 + fb = val - float(idx1) + + r = (colors[idx2][0] - colors[idx1][0]) * fb + colors[idx1][0] + g = (colors[idx2][1] - colors[idx1][1]) * fb + colors[idx1][1] + b = (colors[idx2][2] - colors[idx1][2]) * fb + colors[idx1][2] + + r, g, b = [x * 255 for x in (r, g, b)] + return "#%02x%02x%02x" % (int(r), int(g), int(b)) + + def __init__(self, layout): + self.log = {} + self.total = 0 + self.max_cnt = 0 + self.layout = layout + + def update_log(self, coords): + (c, r) = coords + if not (c, r) in self.log: + self.log[(c, r)] = 0 + self.log[(c, r)] = self.log[(c, r)] + 1 + self.total = self.total + 1 + if self.max_cnt < self.log[(c, r)]: + self.max_cnt = self.log[(c, r)] + + def get_heatmap(self): + with open("%s/heatmap-layout.%s.json" % (dirname(sys.argv[0]), self.layout), "r") as f: + self.heatmap = json.load (f) + + ## Reset colors + for row in self.coords: + for coord in row: + if coord != []: + self.set_bg (coord, "#d9dae0") + + for (c, r) in self.log: + coords = self.coord(c, r) + b, n = coords + cap = self.max_cnt + if cap == 0: + cap = 1 + v = float(self.log[(c, r)]) / cap + self.set_bg (coords, self.heatmap_color (v)) + self.set_tap_info (coords, self.log[(c, r)], self.total) + return self.heatmap + + def get_stats(self): + usage = [ + # left hand + [0, 0, 0, 0, 0], + # right hand + [0, 0, 0, 0, 0] + ] + finger_map = [0, 0, 1, 2, 3, 3, 3, 1, 1, 1, 2, 3, 4, 4] + for (c, r) in self.log: + if r == 5: # thumb cluster + if c <= 6: # left side + usage[0][4] = usage[0][4] + self.log[(c, r)] + else: + usage[1][0] = usage[1][0] + self.log[(c, r)] + elif r == 4 and (c == 4 or c == 9): # bottom row thumb keys + if c <= 6: # left side + usage[0][4] = usage[0][4] + self.log[(c, r)] + else: + usage[1][0] = usage[1][0] + self.log[(c, r)] + else: + fc = c + hand = 0 + if fc >= 7: + hand = 1 + fm = finger_map[fc] + usage[hand][fm] = usage[hand][fm] + self.log[(c, r)] + hand_usage = [0, 0] + for f in usage[0]: + hand_usage[0] = hand_usage[0] + f + for f in usage[1]: + hand_usage[1] = hand_usage[1] + f + + total = self.total + if total == 0: + total = 1 + stats = { + "total-keys": total, + "hands": { + "left": { + "usage": round(float(hand_usage[0]) / total * 100, 2), + "fingers": { + "pinky": 0, + "ring": 0, + "middle": 0, + "index": 0, + "thumb": 0, + } + }, + "right": { + "usage": round(float(hand_usage[1]) / total * 100, 2), + "fingers": { + "thumb": 0, + "index": 0, + "middle": 0, + "ring": 0, + "pinky": 0, + } + }, + } + } + + hmap = ['left', 'right'] + fmap = ['pinky', 'ring', 'middle', 'index', 'thumb', + 'thumb', 'index', 'middle', 'ring', 'pinky'] + for hand_idx in range(len(usage)): + hand = usage[hand_idx] + for finger_idx in range(len(hand)): + stats['hands'][hmap[hand_idx]]['fingers'][fmap[finger_idx + hand_idx * 5]] = round(float(hand[finger_idx]) / total * 100, 2) + return stats + +def dump_all(out_dir, heatmaps): + stats = {} + t = Terminal() + t.clear() + sys.stdout.write("\x1b[2J\x1b[H") + + print ('{t.underline}{outdir}{t.normal}\n'.format(t=t, outdir=out_dir)) + + keys = list(heatmaps.keys()) + keys.sort() + + for layer in keys: + if len(heatmaps[layer].log) == 0: + continue + + with open ("%s/%s.json" % (out_dir, layer), "w") as f: + json.dump(heatmaps[layer].get_heatmap(), f) + stats[layer] = heatmaps[layer].get_stats() + + left = stats[layer]['hands']['left'] + right = stats[layer]['hands']['right'] + + print ('{t.bold}{layer}{t.normal} ({total:,} taps):'.format(t=t, layer=layer, + total=int(stats[layer]['total-keys'] / 2))) + print (('{t.underline} | ' + \ + 'left ({l[usage]:6.2f}%) | ' + \ + 'right ({r[usage]:6.2f}%) |{t.normal}').format(t=t, l=left, r=right)) + print ((' {t.bright_magenta}pinky{t.white} | {left[pinky]:6.2f}% | {right[pinky]:6.2f}% |\n' + \ + ' {t.bright_cyan}ring{t.white} | {left[ring]:6.2f}% | {right[ring]:6.2f}% |\n' + \ + ' {t.bright_blue}middle{t.white} | {left[middle]:6.2f}% | {right[middle]:6.2f}% |\n' + \ + ' {t.bright_green}index{t.white} | {left[index]:6.2f}% | {right[index]:6.2f}% |\n' + \ + ' {t.bright_red}thumb{t.white} | {left[thumb]:6.2f}% | {right[thumb]:6.2f}% |\n' + \ + '').format(left=left['fingers'], right=right['fingers'], t=t)) + +def process_line(line, heatmaps, opts, stamped_log = None): + m = re.search ('KL: col=(\d+), row=(\d+), pressed=(\d+), layer=(.*)', line) + if not m: + return False + if stamped_log is not None: + if line.startswith("KL:"): + print ("%10.10f %s" % (time.time(), line), + file = stamped_log, end = '') + else: + print (line, + file = stamped_log, end = '') + stamped_log.flush() + + (c, r, l) = (int(m.group (2)), int(m.group (1)), m.group (4)) + if (c, r) not in opts.allowed_keys: + return False + + heatmaps[l].update_log ((c, r)) + + return True + +def setup_allowed_keys(opts): + if len(opts.only_key): + incmap={} + for v in opts.only_key: + m = re.search ('(\d+),(\d+)', v) + if not m: + continue + (c, r) = (int(m.group(1)), int(m.group(2))) + incmap[(c, r)] = True + else: + incmap={} + for r in range(0, 6): + for c in range(0, 14): + incmap[(c, r)] = True + + for v in opts.ignore_key: + m = re.search ('(\d+),(\d+)', v) + if not m: + continue + (c, r) = (int(m.group(1)), int(m.group(2))) + del(incmap[(c, r)]) + + return incmap + +def main(opts): + heatmaps = {"Dvorak": Heatmap("Dvorak"), + "ADORE": Heatmap("ADORE") + } + cnt = 0 + out_dir = opts.outdir + + if not os.path.exists(out_dir): + os.makedirs(out_dir) + + opts.allowed_keys = setup_allowed_keys(opts) + + if not opts.one_shot: + + try: + with open("%s/stamped-log" % out_dir, "r") as f: + while True: + line = f.readline() + if not line: + break + if not process_line(line, heatmaps, opts): + continue + except: + pass + + stamped_log = open ("%s/stamped-log" % (out_dir), "a+") + else: + stamped_log = None + + while True: + line = sys.stdin.readline() + if not line: + break + if not process_line(line, heatmaps, opts, stamped_log): + continue + + cnt = cnt + 1 + + if opts.dump_interval != -1 and cnt >= opts.dump_interval and not opts.one_shot: + cnt = 0 + dump_all(out_dir, heatmaps) + + dump_all (out_dir, heatmaps) + +if __name__ == "__main__": + parser = argparse.ArgumentParser (description = "keylog to heatmap processor") + parser.add_argument ('outdir', action = 'store', + help = 'Output directory') + parser.add_argument ('--dump-interval', dest = 'dump_interval', action = 'store', type = int, + default = 100, help = 'Dump stats and heatmap at every Nth event, -1 for dumping at EOF only') + parser.add_argument ('--ignore-key', dest = 'ignore_key', action = 'append', type = str, + default = [], help = 'Ignore the key at position (x, y)') + parser.add_argument ('--only-key', dest = 'only |