Created
September 24, 2012 04:50
-
-
Save tron1point0/3774231 to your computer and use it in GitHub Desktop.
Generates a keyboard map given a list of keybinds
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env perl | |
use strict; | |
use warnings; | |
use Template::Toolkit::Simple; | |
use YAML qw(Load); | |
my %config = Load do {local $/;<DATA>}; | |
die $config{usage} unless @ARGV; | |
my $title = join ' ', grep {not -r} @ARGV; | |
@ARGV = grep {-r} @ARGV; | |
my %binds; map { | |
chomp; | |
my ($key,$action) = /([^-\s]+)\s+(.+)$/; | |
my ($mod) = /^(.)?-/; | |
push @{$binds{uc $key}}, { | |
($mod ? (modifiers => [$config{modifiers}{uc $mod}]) : ()), | |
action => $action, | |
} | |
} <>; | |
sub key { | |
my ($key) = (@_,$_); | |
scalar { | |
%{${config}{keys}{uc $key}}, | |
split => 0, | |
(ref $binds{uc $key} ? (functions => $binds{uc $key}) : ()), | |
} | |
} | |
my @rows = map {scalar { | |
classes => [qw(row)], | |
keys => [map {(/\|/ ? scalar { | |
split => 1, | |
classes => [qw(key)], | |
keys => [map {key} split /\|/], | |
} : key)} split /\s+/], | |
}} split "\n", $config{layout}; | |
push @{$rows[0]{classes}}, qw(small); | |
tt->process( | |
\$config{template}, | |
{ title => $title, | |
rows => \@rows, | |
stylesheet => $config{stylesheet}, | |
},\*STDOUT, | |
{binmode => ':utf8'}); | |
=head1 NAME | |
generate.pl - Generate a graphical keyboard map given an optional set of | |
keybinds. | |
=head1 SYNOPSIS | |
$ ./generate.pl binds.txt Vim Keybinds | |
$ ./generate.pl Keyboard Layout | |
$ ./generate.pl Vim Keybinds < binds.txt | |
$ cat binds.txt | |
i Insert Mode | |
colon Command Mode | |
C-w Window Mode | |
=cut | |
__DATA__ | |
--- layout | |
--- |- | |
escape f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 power | |
grave 1 2 3 4 5 6 7 8 9 0 lbracket rbracket back | |
tab apostrophe comma period p y f g c r l slash equals backslash | |
lcontrol a o e u i d h t n s minus return | |
lshift semicolon q j k x b m w v z rshift | |
fn capital lmenu lmeta space rmeta rmenu left up|down right | |
--- usage | |
--- | | |
generate.pl: generate a graphical keyboard map given a set of keybinds | |
Usage: generate.pl [keybinds...] [title...] | |
keybinds: file or files that specify keybinds, one per line | |
title: arguments that are not readable files are assumed to be the title of | |
the keymap | |
--- template | |
--- | | |
[%- MACRO key_t(key) BLOCK %] | |
[%- IF key.split == 1 %] | |
<span class="split[% IF key.classes %] [% key.classes.join(' ') %][% END %]"> | |
[%- FOR k IN key.keys %] | |
[%- key_t(k) %] | |
[%- END %] | |
[% ELSE %] | |
<span class="key[% IF key.classes %] [% key.classes.join(' ') %][% END %]"> | |
<span class="cap">[% key.cap %]</span> | |
[%- FOR function IN key.functions %] | |
<span class="function[% IF function.modifiers %] [% function.modifiers.join(' ') %][% END %]">[% function.action %]</span> | |
[%- END %] | |
[%- END %] | |
<span class="code">[% key.code %]</span> | |
</span> | |
[%- END -%] | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>[% title %]</title> | |
<style type="text/css"> | |
[%- stylesheet %] | |
</style> | |
</head> | |
<body> | |
<div class="keyboard"> | |
[%- FOR row IN rows %] | |
<div class="[% row.classes.join(' ') %]"> | |
[%- FOR k IN row.keys %] | |
[%- key_t(k) %] | |
[%- END %] | |
</div> | |
[%- END %] | |
</div> | |
</body> | |
</html> | |
--- stylesheet | |
--- |- | |
body { | |
font: 12pt Helvetica; | |
padding: 1em; | |
} | |
.row { | |
width: 65em; | |
height: 4em; | |
box-sizing: border-box; | |
display: block; | |
padding: 0; | |
margin: 0 0 0.4em 0; | |
position: relative; | |
} | |
.row .key { | |
position: relative; | |
display: inline-block; | |
box-sizing: border-box; | |
width: 4em; | |
height: 4em; | |
padding: 0.3em 0.3em 0.3em 0.3em; | |
margin: 0 0.2em 0 0; | |
border: 0.1em solid #06e; | |
border-radius: 0.4em; | |
background-color: #eee; | |
background: -webkit-linear-gradient(90deg, #eee 0%, #ddd 100%); | |
box-shadow: 0 0 0.2em #08e; | |
color: #111; | |
vertical-align: middle; | |
text-transform: capitalize; | |
text-shadow: 0 0 0.1em #fff; | |
} | |
.row .key > .code { | |
position: absolute; | |
color: rgba(0,0,128,0.2); | |
text-shadow: 0 0 0.1em rgba(0,0,128,0.1); | |
font-size: 50%; | |
right: 0.3em; | |
bottom: 0.1em; | |
} | |
.row .key > .cap { | |
position: absolute; | |
font-size: 2.5em; | |
color: rgba(0,0,0,0.1); | |
text-shadow: 0 0 0.2em rgba(0,0,0,0.05); | |
left: 0; | |
top: 0.2em; | |
width: 100%; | |
height: 100%; | |
text-align: center; | |
} | |
.row .key:before { | |
content: ''; | |
position: absolute; | |
width: 100%; | |
height: 100%; | |
left: -0.1em; | |
top: -0.1em; | |
border-radius: 0.4em; | |
box-shadow: inset 0 0 0.2em #fff; | |
border: 0.1em solid transparent; | |
} | |
.row .key > .function { | |
display: block; | |
font-size: 80%; | |
padding: 0.1em 0.2em 0.1em 0.2em; | |
margin-left: -0.4em; | |
margin-right: -0.2em; | |
text-shadow: 0 0 0.7em rgba(128,128,128,0.5); | |
text-align: center; | |
} | |
/* Special key coloring */ | |
.row .key.special:before { | |
background-color: rgba(128,128,128,0.3); | |
} | |
.row .key.shift:before { | |
background-color: rgba(128,0,0,0.3); | |
} | |
.row .key > .function.shift { | |
text-shadow: 0 0 0.7em rgba(128,0,0,0.5); | |
color: rgba(128,0,0,1); | |
} | |
.row .key.ctrl:before { | |
background: rgba(0,128,0,0.3); | |
} | |
.row .key > .function.ctrl { | |
text-shadow: 0 0 0.7em rgba(0,128,0,0.5); | |
color: rgba(0,128,0,1); | |
} | |
.row .key.command:before { | |
background: rgba(0,0,128,0.3); | |
} | |
.row .key > .function.command { | |
text-shadow: 0 0 0.7em rgba(0,0,128,0.5); | |
color: rgba(0,0,128,1); | |
} | |
.row .key > .function.option, | |
.row .key.option:before { | |
background: rgba(128,128,0,0.3); | |
} | |
.row .key > .function.option { | |
text-shadow: 0 0 0.7em rgba(128,128,0,0.5); | |
color: rgba(128,128,0,1); | |
} | |
.row .key.fn:before { | |
background: rgba(0,128,128,0.3); | |
} | |
.row .key > .function.fn { | |
text-shadow: 0 0 0.7em rgba(0,128,128,0.5); | |
color: rgba(0,128,128,1); | |
} | |
/* Special Key Sizing */ | |
.row .key.tab, | |
.row .key.delete { | |
width: 6em; | |
} | |
.row .key.ctrl, | |
.row .key.return { | |
width: 7.25em; | |
} | |
.row .key.shift { | |
width: 9.55em; | |
} | |
.row .key.fn, | |
.row .key.caps, | |
.row .key.option { | |
width: 4em; | |
} | |
.row .key.command { | |
width: 4.6em; | |
} | |
.row .key.space { | |
width: 22.8em; | |
} | |
/* Small Row Sizing & Positioning */ | |
.row.small { | |
height: 2.1em; | |
} | |
.row.small .key { | |
height: 2em; | |
width: 4.145em; | |
padding: 0.2em 0.15em 0.2em 0.15em; | |
} | |
.row.small .key > .cap { | |
font-size: 120%; | |
} | |
.row.small .key > .function { | |
font-size: 66%; | |
margin-left: -0.2em; | |
} | |
.row.small .key > .function:first-child { | |
padding-top: 0; | |
} | |
/* Half-keys */ | |
.row .key.top, | |
.row .key.bottom { | |
height: 2em; | |
} | |
.row .key.top > .cap, | |
.row .key.bottom > .cap { | |
font-size: 100%; | |
vertical-align: middle; | |
} | |
.row > .key.top { | |
margin-bottom: 2em; | |
} | |
.row > .key.bottom { | |
margin-top: 2em; | |
} | |
.row .split { | |
background: none; | |
border: 0.1em transparent; | |
box-shadow: none; | |
padding: 0; | |
} | |
.row .split > .bottom { | |
border-top-width: 0; | |
} | |
.row .split > .top, | |
.row .split > .top:before { | |
border-bottom-left-radius: 0; | |
border-bottom-right-radius: 0; | |
} | |
.row .split > .bottom, | |
.row .split > .bottom:before { | |
border-top-left-radius: 0; | |
border-top-right-radius: 0; | |
} | |
--- modifiers | |
--- | |
S: shift | |
C: ctrl | |
F: fn | |
D: command | |
M: option | |
--- keys | |
--- | |
0: | |
cap: 0 | |
code: 11 | |
1: | |
cap: 1 | |
code: 2 | |
2: | |
cap: 2 | |
code: 3 | |
3: | |
cap: 3 | |
code: 4 | |
4: | |
cap: 4 | |
code: 5 | |
5: | |
cap: 5 | |
code: 6 | |
6: | |
cap: 6 | |
code: 7 | |
7: | |
cap: 7 | |
code: 8 | |
8: | |
cap: 8 | |
code: 9 | |
9: | |
cap: 9 | |
code: 10 | |
A: | |
cap: A | |
code: 30 | |
ADD: | |
cap: + | |
code: 78 | |
APOSTROPHE: | |
cap: "'" | |
code: 40 | |
APPS: | |
cap: APPS | |
code: 221 | |
AT: | |
cap: '@' | |
code: 145 | |
AX: | |
cap: AX | |
code: 150 | |
B: | |
cap: B | |
code: 48 | |
BACK: | |
cap: ⌫ | |
classes: | |
- special | |
- delete | |
code: 14 | |
BACKSLASH: | |
cap: \ | |
code: 43 | |
C: | |
cap: C | |
code: 46 | |
CAPITAL: | |
cap: ⇪ | |
classes: | |
- special | |
code: 58 | |
CIRCUMFLEX: | |
cap: '^' | |
code: 144 | |
COLON: | |
cap: ':' | |
code: 146 | |
COMMA: | |
cap: ',' | |
code: 51 | |
CONVERT: | |
cap: CONVERT | |
code: 121 | |
D: | |
cap: D | |
code: 32 | |
DECIMAL: | |
cap: . | |
code: 83 | |
DELETE: | |
cap: ⌦ | |
code: 211 | |
DIVIDE: | |
cap: ÷ | |
code: 181 | |
DOWN: | |
cap: ▼ | |
classes: | |
- special | |
- bottom | |
code: 208 | |
E: | |
cap: E | |
code: 18 | |
END: | |
cap: ↘ | |
code: 207 | |
EQUALS: | |
cap: = | |
code: 13 | |
ESCAPE: | |
cap: ⎋ | |
code: 1 | |
F: | |
cap: F | |
code: 33 | |
F1: | |
cap: F1 | |
code: 59 | |
F10: | |
cap: F10 | |
code: 68 | |
F11: | |
cap: F11 | |
code: 87 | |
F12: | |
cap: F12 | |
code: 88 | |
F13: | |
cap: F13 | |
code: 100 | |
F14: | |
cap: F14 | |
code: 101 | |
F15: | |
cap: F15 | |
code: 102 | |
F2: | |
cap: F2 | |
code: 60 | |
F3: | |
cap: F3 | |
code: 61 | |
F4: | |
cap: F4 | |
code: 62 | |
F5: | |
cap: F5 | |
code: 63 | |
F6: | |
cap: F6 | |
code: 64 | |
F7: | |
cap: F7 | |
code: 65 | |
F8: | |
cap: F8 | |
code: 66 | |
F9: | |
cap: F9 | |
code: 67 | |
FN: | |
cap: Fn | |
classes: | |
- fn | |
code: x01 | |
G: | |
cap: G | |
code: 34 | |
GRAVE: | |
cap: '`' | |
code: 41 | |
H: | |
cap: H | |
code: 35 | |
HOME: | |
cap: ↖ | |
code: 199 | |
I: | |
cap: I | |
code: 23 | |
INSERT: | |
cap: Ins. | |
code: 210 | |
J: | |
cap: J | |
code: 36 | |
K: | |
cap: K | |
code: 37 | |
KANA: | |
cap: KANA | |
code: 112 | |
KANJI: | |
cap: KANJI | |
code: 148 | |
L: | |
cap: L | |
code: 38 | |
LBRACKET: | |
cap: '[' | |
code: 26 | |
LCONTROL: | |
cap: ⌃ | |
classes: | |
- ctrl | |
code: 29 | |
LEFT: | |
cap: ◀ | |
classes: | |
- special | |
- bottom | |
code: 203 | |
LMENU: | |
cap: ⌥ | |
classes: | |
- option | |
code: 56 | |
LMETA: | |
cap: ⌘ | |
classes: | |
- command | |
code: 219 | |
LSHIFT: | |
cap: ⇧ | |
classes: | |
- shift | |
code: 42 | |
M: | |
cap: M | |
code: 50 | |
MINUS: | |
cap: - | |
code: 12 | |
MULTIPLY: | |
cap: × | |
code: 55 | |
N: | |
cap: N | |
code: 49 | |
NEXT: | |
cap: ⇟ | |
code: 209 | |
NOCONVERT: | |
cap: NOCONVERT | |
code: 123 | |
NONE: | |
cap: NONE | |
code: 0 | |
NUMLOCK: | |
cap: ⌗ | |
code: 69 | |
NUMPAD0: | |
cap: NUMPAD0 | |
code: 82 | |
NUMPAD1: | |
cap: 1 | |
code: 79 | |
NUMPAD2: | |
cap: 2 | |
code: 80 | |
NUMPAD3: | |
cap: 3 | |
code: 81 | |
NUMPAD4: | |
cap: 4 | |
code: 75 | |
NUMPAD5: | |
cap: 5 | |
code: 76 | |
NUMPAD6: | |
cap: 6 | |
code: 77 | |
NUMPAD7: | |
cap: 7 | |
code: 71 | |
NUMPAD8: | |
cap: 8 | |
code: 72 | |
NUMPAD9: | |
cap: 9 | |
code: 73 | |
NUMPADCOMMA: | |
cap: ',' | |
code: 179 | |
NUMPADENTER: | |
cap: ⎆ | |
code: 156 | |
NUMPADEQUALS: | |
cap: = | |
code: 141 | |
O: | |
cap: O | |
code: 24 | |
P: | |
cap: P | |
code: 25 | |
PAUSE: | |
cap: Pause | |
code: 197 | |
PERIOD: | |
cap: . | |
code: 52 | |
POWER: | |
cap: ⌽ | |
classes: | |
- special | |
code: 222 | |
PRIOR: | |
cap: ⇞ | |
code: 201 | |
Q: | |
cap: Q | |
code: 16 | |
R: | |
cap: R | |
code: 19 | |
RBRACKET: | |
cap: ']' | |
code: 27 | |
RCONTROL: | |
cap: ⌃ | |
classes: | |
- ctrl | |
code: 157 | |
RETURN: | |
cap: ⏎ | |
classes: | |
- special | |
- return | |
code: 28 | |
RIGHT: | |
cap: ▶ | |
classes: | |
- special | |
- bottom | |
code: 205 | |
RMENU: | |
cap: ⌥ | |
classes: | |
- option | |
code: 184 | |
RMETA: | |
cap: ⌘ | |
classes: | |
- command | |
code: 220 | |
RSHIFT: | |
cap: ⇧ | |
classes: | |
- shift | |
code: 54 | |
S: | |
cap: S | |
code: 31 | |
SCROLL: | |
cap: Scr. Lock | |
code: 70 | |
SEMICOLON: | |
cap: ; | |
code: 39 | |
SLASH: | |
cap: / | |
code: 53 | |
SLEEP: | |
cap: SLEEP | |
code: 223 | |
SPACE: | |
cap: Space | |
classes: | |
- special | |
- space | |
code: 57 | |
STOP: | |
cap: STOP | |
code: 149 | |
SUBTRACT: | |
cap: − | |
code: 74 | |
SYSRQ: | |
cap: Sys. Rq. | |
code: 183 | |
T: | |
cap: T | |
code: 20 | |
TAB: | |
cap: ⇥ | |
classes: | |
- special | |
- tab | |
code: 15 | |
U: | |
cap: U | |
code: 22 | |
UNDERLINE: | |
cap: _ | |
code: 147 | |
UNLABLED: | |
cap: UNLABLED | |
code: 151 | |
UP: | |
cap: ▲ | |
classes: | |
- special | |
- top | |
code: 200 | |
V: | |
cap: V | |
code: 47 | |
W: | |
cap: W | |
code: 17 | |
X: | |
cap: X | |
code: 45 | |
Y: | |
cap: Y | |
code: 21 | |
YEN: | |
cap: ¥ | |
code: 125 | |
Z: | |
cap: Z | |
code: 44 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment