ergodox-firmware/generate_layout.rb

701 lines
32 KiB
Ruby
Raw Permalink Normal View History

2015-12-14 01:28:13 +01:00
#!/usr/bin/env ruby
# -*- encoding: utf-8 -*-
# Copyright muflax <mail@muflax.com>, 2015
# License: GNU GPLv3 (or later) <http://www.gnu.org/copyleft/gpl.html>
GC.disable
2016-06-12 07:51:49 +02:00
# avoid having to require muflax
class Object
def blank?
respond_to?(:empty?) ? !!empty? : !self
end
end
class String
BLANK_RE = /\A[[:space:]]*\z/
def blank?
BLANK_RE === self
end
end
2015-12-14 01:28:13 +01:00
2016-06-11 22:19:48 +02:00
LayoutDir = "src/keyboard"
2016-06-12 01:58:43 +02:00
LayoutFile = "#{LayoutDir}/layout.c"
2015-12-14 01:28:13 +01:00
puts "generating #{LayoutFile}..."
class Layer
@@layers = {}
@@primitives = 0
def initialize name
case name
when Symbol
raise "layer already defined: #{name}" if @@layers.include? name
@@primitives += 1
@@layers[name] = @@primitives
when Array
name.each do |prim|
raise "unknown layers: #{prim}" if not @@layers.include? prim
raise "can only combo primitive layers: #{prim}" if not prim.is_a? Symbol
end
combo = name.reduce(0) do |code, prim|
code | (@@layers[prim])
end
combo_name = name.sort_by{|p| @@layers[p]}.join("_").to_sym
@@layers[combo_name] = combo
else
raise "invalid layer definition: #{name}"
end
end
end
Keydef = Struct.new :code, :func
2015-12-14 01:28:13 +01:00
class Key
attr_reader :keydefs, :name, :row, :col
2015-12-14 01:28:13 +01:00
Layers = [ :LB, :LP, :LN, :LF, :LD ]
2015-12-14 01:28:13 +01:00
Functions = {
#
"basic" => "&kbfun_normal_press_release",
"media" => "&kbfun_mediakey_press_release",
#
"mod" => "&kbfun_modifier_press_release",
"sticky_mod" => "&kbfun_modifier_sticky",
#
"layer" => "&kbfun_layer_press_release",
"sticky" => "&kbfun_layer_sticky",
#
"shift" => "&kbfun_shift_press_release",
"ctrl" => "&kbfun_control_press_release",
"alt" => "&kbfun_alt_press_release",
"win" => "&kbfun_win_press_release",
#
"shift_once" => "&kbfun_shift_press_release_once",
"ctrl_once" => "&kbfun_control_press_release_once",
"alt_once" => "&kbfun_alt_press_release_once",
"win_once" => "&kbfun_win_press_release_once",
#
"shift_layer" => "&kbfun_shift_layer_press_release",
"ctrl_layer" => "&kbfun_control_layer_press_release",
"alt_layer" => "&kbfun_alt_layer_press_release",
"win_layer" => "&kbfun_win_layer_press_release",
#
"capslock" => "&kbfun_capslock_press_release",
#
"S" => "&kbfun_shift_press_release",
"C" => "&kbfun_control_press_release",
"A" => "&kbfun_alt_press_release",
"M" => "&kbfun_alt_press_release",
"W" => "&kbfun_win_press_release",
"CL" => "&kbfun_capslock_press_release",
"_L" => "&kbfun_layer_sticky",
"_M" => "&kbfun_modifier_sticky",
"_C" => "&kbfun_control_layer_press_release",
"_A" => "&kbfun_alt_layer_press_release",
"_W" => "&kbfun_win_layer_press_release",
2015-12-14 01:28:13 +01:00
}
Keys = {
# letters
"a" => "KEY_a_A",
"b" => "KEY_b_B",
"c" => "KEY_c_C",
"d" => "KEY_d_D",
"e" => "KEY_e_E",
"f" => "KEY_f_F",
"g" => "KEY_g_G",
"h" => "KEY_h_H",
"i" => "KEY_i_I",
"j" => "KEY_j_J",
"k" => "KEY_k_K",
"l" => "KEY_l_L",
"m" => "KEY_m_M",
"n" => "KEY_n_N",
"o" => "KEY_o_O",
"p" => "KEY_p_P",
"q" => "KEY_q_Q",
"r" => "KEY_r_R",
"s" => "KEY_s_S",
"t" => "KEY_t_T",
"u" => "KEY_u_U",
"v" => "KEY_v_V",
"w" => "KEY_w_W",
"x" => "KEY_x_X",
"y" => "KEY_y_Y",
"z" => "KEY_z_Z",
# numbers
"0" => "KEY_0_RightParenthesis",
"1" => "KEY_1_Exclamation",
"2" => "KEY_2_At",
"3" => "KEY_3_Pound",
"4" => "KEY_4_Dollar",
"5" => "KEY_5_Percent",
"6" => "KEY_6_Caret",
"7" => "KEY_7_Ampersand",
"8" => "KEY_8_Asterisk",
"9" => "KEY_9_LeftParenthesis",
# punctuation
"\\" => "KEY_Backslash_Pipe",
"{" => ["KEY_LeftBracket_LeftBrace", "shift_once"],
"}" => ["KEY_RightBracket_RightBrace", "shift_once"],
"[" => "KEY_LeftBracket_LeftBrace",
"]" => "KEY_RightBracket_RightBrace",
"," => "KEY_Comma_LessThan",
"-" => "KEY_Dash_Underscore",
"=" => "KEY_Equal_Plus",
"`" => "KEY_GraveAccent_Tilde",
"." => "KEY_Period_GreaterThan",
"\'" => "KEY_SingleQuote_DoubleQuote",
";" => "KEY_Semicolon_Colon",
"/" => "KEY_Slash_Question",
"~" => ["KEY_GraveAccent_Tilde", "shift_once"],
"%" => ["KEY_5_Percent", "shift_once"],
"*" => ["KEY_8_Asterisk", "shift_once"],
":" => ["KEY_Semicolon_Colon", "shift_once"],
"^" => ["KEY_6_Caret", "shift_once"],
"<" => ["KEY_Comma_LessThan", "shift_once"],
">" => ["KEY_Period_GreaterThan", "shift_once"],
"?" => ["KEY_Slash_Question", "shift_once"],
"!" => ["KEY_1_Exclamation", "shift_once"],
"(" => ["KEY_9_LeftParenthesis", "shift_once"],
")" => ["KEY_0_RightParenthesis", "shift_once"],
"|" => ["KEY_Backslash_Pipe", "shift_once"],
"@" => ["KEY_2_At", "shift_once"],
"\"" => ["KEY_SingleQuote_DoubleQuote", "shift_once"],
"_" => ["KEY_Dash_Underscore", "shift_once"],
"+" => ["KEY_Equal_Plus", "shift_once"],
"$" => ["KEY_4_Dollar", "shift_once"],
"&" => ["KEY_7_Ampersand", "shift_once"],
"#" => ["KEY_3_Pound", "shift_once"],
"hash" => ["KEY_3_Pound", "shift_once"],
"hsh" => ["KEY_3_Pound", "shift_once"],
# enter etc
"enter" => "KEY_ReturnEnter",
"return" => "KEY_ReturnEnter",
"ret" => "KEY_ReturnEnter",
"space" => "KEY_Spacebar",
"spc" => "KEY_Spacebar",
"tab" => "KEY_Tab",
"backspace" => "KEY_DeleteBackspace",
"bksp" => "KEY_DeleteBackspace",
"bs" => "KEY_DeleteBackspace",
"delete" => "KEY_DeleteForward",
"del" => "KEY_DeleteForward",
"home" => "KEY_Home",
"hom" => "KEY_Home",
"end" => "KEY_End",
"page_up" => "KEY_PageUp",
"pgup" => "KEY_PageUp",
"p-^" => "KEY_PageUp",
"page_down" => "KEY_PageDown",
"pgdn" => "KEY_PageDown",
"p-v" => "KEY_PageDown",
"up" => "KEY_UpArrow",
"a-^" => "KEY_UpArrow",
"down" => "KEY_DownArrow",
"a-v" => "KEY_DownArrow",
"left" => "KEY_LeftArrow",
"a-<" => "KEY_LeftArrow",
"right" => "KEY_RightArrow",
"a->" => "KEY_RightArrow",
"escape" => "KEY_Escape",
"esc" => "KEY_Escape",
"insert" => "KEY_Insert",
"ins" => "KEY_Insert",
"menu" => "KEY_Application",
"men" => "KEY_Application",
# modifiers
"alt" => ["MOD_KEY_LeftAlt", "mod" ],
"A" => ["MOD_KEY_LeftAlt", "mod" ],
"M" => ["MOD_KEY_LeftAlt", "mod" ],
"alt_gr" => ["MOD_KEY_RightAlt", "mod" ],
"umlaut" => ["MOD_KEY_RightAlt", "mod" ],
# "UM" => ["MOD_KEY_RightAlt", "mod" ],
"UM" => ["MOD_KEY_RightAlt", "sticky_mod" ],
"ctrl" => ["MOD_KEY_LeftControl", "mod" ],
"C" => ["MOD_KEY_LeftControl", "mod" ],
"control_l" => ["MOD_KEY_LeftControl", "mod" ],
"control_r" => ["MOD_KEY_RightControl", "mod" ],
"win" => ["MOD_KEY_LeftGUI", "mod" ],
"W" => ["MOD_KEY_LeftGUI", "mod" ],
"shift_l" => ["MOD_KEY_LeftShift", "mod" ],
"S_l" => ["MOD_KEY_LeftShift", "mod" ],
"shift_r" => ["MOD_KEY_RightShift", "mod" ],
"S_r" => ["MOD_KEY_RightShift", "mod" ],
"scroll_lock" => "KEY_ScrollLock",
"SCL" => "KEY_ScrollLock",
# null
"NULL" => "KEY_NULL",
"NUL" => "KEY_NULL",
"" => "KEY_NULL",
# functions 1..24
"f1" => "KEY_F1",
"f2" => "KEY_F2",
"f3" => "KEY_F3",
"f4" => "KEY_F4",
"f5" => "KEY_F5",
"f6" => "KEY_F6",
"f7" => "KEY_F7",
"f8" => "KEY_F8",
"f9" => "KEY_F9",
"f10" => "KEY_F10",
"f11" => "KEY_F11",
"f12" => "KEY_F12",
"f13" => "KEY_F13",
"f14" => "KEY_F14",
"f15" => "KEY_F15",
"f16" => "KEY_F16",
"f17" => "KEY_F17",
"f18" => "KEY_F18",
"f19" => "KEY_F19",
"f20" => "KEY_F20",
"f21" => "KEY_F21",
"f22" => "KEY_F22",
"f23" => "KEY_F23",
"f24" => "KEY_F24",
# keypad stuff
"kp_1" => "KEYPAD_1_End",
"kp_2" => "KEYPAD_2_DownArrow",
"kp_3" => "KEYPAD_3_PageDown",
"kp_4" => "KEYPAD_4_LeftArrow",
"kp_5" => "KEYPAD_5",
"kp_6" => "KEYPAD_6_RightArrow",
"kp_7" => "KEYPAD_7_Home",
"kp_8" => "KEYPAD_8_UpArrow",
"kp_9" => "KEYPAD_9_PageUp",
"kp_0" => "KEYPAD_0_Insert",
#
"kp_." => "KEYPAD_Period_Delete",
"kp_/" => "KEYPAD_Slash",
"kp_*" => "KEYPAD_Asterisk",
"kp_-" => "KEYPAD_Minus",
"kp_+" => "KEYPAD_Plus",
"kp_en" => "KEYPAD_ENTER",
# mediakeys
"MD_PP" => ["MEDIAKEY_PLAY_PAUSE", "media"],
"MD_PT" => ["MEDIAKEY_PREV_TRACK", "media"],
"MD_NT" => ["MEDIAKEY_NEXT_TRACK", "media"],
"MD_S" => ["MEDIAKEY_STOP", "media"],
"MD_M" => ["MEDIAKEY_AUDIO_MUTE", "media"],
"MD_VU" => ["MEDIAKEY_AUDIO_VOL_UP", "media"],
"MD_VD" => ["MEDIAKEY_AUDIO_VOL_DOWN", "media"],
"record" => ["MEDIAKEY_RECORD", "media"],
"rewind" => ["MEDIAKEY_REWIND", "media"],
"eject" => ["MEDIAKEY_EJECT", "media"],
"cc_config" => ["MEDIAKEY_CC_CONFIG", "media"],
"email" => ["MEDIAKEY_EMAIL", "media"],
"calculator" => ["MEDIAKEY_CALCULATOR", "media"],
"local_browser" => ["MEDIAKEY_LOCAL_BROWSER", "media"],
"bs_search" => ["MEDIAKEY_BROWSER_SEARCH", "media"],
"bs_home" => ["MEDIAKEY_BROWSER_HOME", "media"],
"bs_b" => ["MEDIAKEY_BROWSER_BACK", "media"],
"bs_f" => ["MEDIAKEY_BROWSER_FORWARD", "media"],
"bs_stop" => ["MEDIAKEY_BROWSER_STOP", "media"],
"bs_refresh" => ["MEDIAKEY_BROWSER_REFRESH", "media"],
"bs_bookmarks" => ["MEDIAKEY_BROWSER_BOOKMARKS", "media"],
# other weird shit
"pause" => "KEY_Pause",
"sysreq" => "KEY_SysReq_Attention",
"sreq" => "KEY_SysReq_Attention",
"SR" => "KEY_SysReq_Attention",
#
"clear" => "KEY_Clear",
# "Clear_Again" => "KEY_Clear_Again", # wut
"printscreen" => "KEY_PrintScreen",
"prtscr" => "KEY_PrintScreen",
"PS" => "KEY_PrintScreen",
#
"execute" => "KEY_Execute",
"exe" => "KEY_Execute",
"help" => "KEY_Help",
# "menu" => "KEY_Menu", # NOT normal menu!
"select" => "KEY_Select",
"selct" => "KEY_Select",
"sel" => "KEY_Select",
"stop" => "KEY_Stop",
#
"again" => "KEY_Again",
"redo" => "KEY_Again",
"undo" => "KEY_Undo",
#
"cut" => "KEY_Cut",
"copy" => "KEY_Copy",
"paste" => "KEY_Paste",
#
"find" => "KEY_Find",
#
"mute" => "KEY_Mute",
#
"volume_up" => "KEY_VolumeUp",
"volume_down" => "KEY_VolumeDown",
"vol_up" => "KEY_VolumeUp",
"vol_down" => "KEY_VolumeDown",
"volumeup" => "KEY_VolumeUp",
"volumedown" => "KEY_VolumeDown",
"voldown" => "KEY_VolumeDown",
"volup" => "KEY_VolumeUp",
"voldn" => "KEY_VolumeDown",
"vlup" => "KEY_VolumeUp",
"vldn" => "KEY_VolumeDown",
"vup" => "KEY_VolumeUp",
"vdn" => "KEY_VolumeDown",
}
Names = {
# unique key identifiers, must have a name for each of the 80 physical keys
# NOTE: the mapping to the matrix is a bit weird, so be careful
# name row col
2016-08-22 01:24:28 +02:00
"Ln┳o" => [ 5, 0 ],
"Ln┳4" => [ 5, 1 ],
"Ln┳3" => [ 5, 2 ],
"Ln┳2" => [ 5, 3 ],
"Ln┳1" => [ 5, 4 ],
"Ln┳0" => [ 5, 5 ],
"Ln┳d" => [ 5, 6 ],
2016-08-22 01:24:28 +02:00
"Lt┳o" => [ 4, 0 ],
"Lt┳4" => [ 4, 1 ],
"Lt┳3" => [ 4, 2 ],
"Lt┳2" => [ 4, 3 ],
"Lt┳1" => [ 4, 4 ],
"Lt┳0" => [ 4, 5 ],
"Lt┳d" => [ 4, 6 ],
"Lh┳o" => [ 3, 0 ],
"Lh┳4" => [ 3, 1 ],
"Lh┳3" => [ 3, 2 ],
"Lh┳2" => [ 3, 3 ],
"Lh┳1" => [ 3, 4 ],
"Lh┳0" => [ 3, 5 ],
"Lb┳o" => [ 2, 0 ],
"Lb┳4" => [ 2, 1 ],
"Lb┳3" => [ 2, 2 ],
"Lb┳2" => [ 2, 3 ],
"Lb┳1" => [ 2, 4 ],
"Lb┳0" => [ 2, 5 ],
"Lb┳d" => [ 2, 6 ],
"Lu┳o" => [ 1, 0 ],
"Lu┳4" => [ 1, 1 ],
"Lu┳3" => [ 1, 2 ],
"Lu┳2" => [ 1, 3 ],
"Lu┳1" => [ 1, 4 ],
"Lz┳b" => [ 0, 5 ],
"Lz┳c" => [ 0, 6 ],
"Ly┳a" => [ 1, 5 ],
"Ly┳b" => [ 1, 6 ],
"Ly┳c" => [ 0, 4 ],
"Lx┳a" => [ 0, 3 ],
"Lx┳b" => [ 0, 2 ],
"Lx┳c" => [ 0, 1 ],
"Rn┳d" => [ 5, 7 ],
"Rn┳0" => [ 5, 8 ],
"Rn┳1" => [ 5, 9 ],
"Rn┳2" => [ 5, 10 ],
"Rn┳3" => [ 5, 11 ],
"Rn┳4" => [ 5, 12 ],
"Rn┳o" => [ 5, 13 ],
"Rt┳d" => [ 4, 7 ],
"Rt┳0" => [ 4, 8 ],
"Rt┳1" => [ 4, 9 ],
"Rt┳2" => [ 4, 10 ],
"Rt┳3" => [ 4, 11 ],
"Rt┳4" => [ 4, 12 ],
"Rt┳o" => [ 4, 13 ],
"Rh┳0" => [ 3, 8 ],
"Rh┳1" => [ 3, 9 ],
"Rh┳2" => [ 3, 10 ],
"Rh┳3" => [ 3, 11 ],
"Rh┳4" => [ 3, 12 ],
"Rh┳o" => [ 3, 13 ],
"Rb┳d" => [ 2, 7 ],
"Rb┳0" => [ 2, 8 ],
"Rb┳1" => [ 2, 9 ],
"Rb┳2" => [ 2, 10 ],
"Rb┳3" => [ 2, 11 ],
"Rb┳4" => [ 2, 12 ],
"Rb┳o" => [ 2, 13 ],
"Ru┳1" => [ 1, 9 ],
"Ru┳2" => [ 1, 10 ],
"Ru┳3" => [ 1, 11 ],
"Ru┳4" => [ 1, 12 ],
"Ru┳o" => [ 1, 13 ],
"Rz┳c" => [ 0, 7 ],
2016-08-22 01:24:28 +02:00
"Rz┳b" => [ 0, 8 ],
"Ry┳c" => [ 0, 9 ],
"Ry┳b" => [ 1, 7 ],
"Ry┳a" => [ 1, 8 ],
"Rx┳c" => [ 0, 12 ],
"Rx┳b" => [ 0, 11 ],
"Rx┳a" => [ 0, 10 ],
2015-12-14 01:28:13 +01:00
}
Layers.each.with_index do |layer, i|
2016-06-14 12:57:20 +02:00
Keys["#{layer}"] = ["#{i}", "layer"]
end
def initialize layers, name
@keydefs = Array.new(layers.size)
@name = name
@pos = Key::Names[name]
@row, @col = @pos
if layers.all?(&:empty?)
layers[0] = ["NUL"]
end
2015-12-14 01:28:13 +01:00
layers.each.with_index do |(key, type), i|
if type.nil? and key.nil?
@keydefs[i] = @keydefs[i-1]
next
end
2016-06-14 12:57:20 +02:00
# keycode and default keyfunc
keycode, default_type = Keys[key]
2016-06-14 10:39:45 +02:00
2016-06-14 12:57:20 +02:00
# keyfunc
type ||= default_type || "basic"
keyfunc = Functions[type]
2015-12-14 01:28:13 +01:00
2016-07-20 23:32:15 +02:00
raise "overwriting shift key: #{key}, #{type}" if default_type == "shift" and type != default_type
2016-06-14 12:57:20 +02:00
raise "key not found: #{key}" if keycode.nil?
raise "func not found: #{type}" if keyfunc.nil?
2016-06-14 10:39:45 +02:00
case type
when "layer", "sticky", "_L"
2016-06-14 10:39:45 +02:00
raise "invalid layer: #{key}" unless key.to_i < Key::Layers.size
when "mod", "sticky_mod", "_M"
2016-06-14 10:39:45 +02:00
raise "invalid modifier: #{key}" unless keycode.start_with? "MOD_KEY"
end
case keycode
when /^MOD_KEY/
case type
when "mod", "sticky_mod", "CL", "_M"; # pass
2016-06-14 10:39:45 +02:00
else
raise "invalid modifier keyfunc: #{key}, #{type}"
end
end
@keydefs[i] = Keydef.new(keycode, keyfunc)
2015-12-14 01:28:13 +01:00
end
raise "transparency error: #{layers}" if @keydefs.any?(&:nil?)
2015-12-14 01:28:13 +01:00
end
Dummy = Key.new(Key::Layers.map{"NULL"}, "dummy")
2015-12-14 01:28:13 +01:00
end
class Layout
def initialize name, keys
@name = name
@keys = keys
@rows = @keys.map(&:row).max + 1
@cols = @keys.map(&:col).max + 1
@matrix = @rows.times.map{Array.new(@cols)}
@keys.each do |key|
@matrix[key.row][key.col] = key
end
2015-12-14 01:28:13 +01:00
end
2016-06-12 02:31:58 +02:00
def keys_to_matrix type, method
matrix = []
@matrix.each.with_index do |row, row_i|
matrix << "{"
row.each.with_index do |key, col_i|
key ||= Key::Dummy # some matrix positions are empty
matrix << "\t{ // row #{row_i} x col #{col_i}"
Key::Layers.each.with_index do |layer_name, layer|
matrix << "\t\t(#{type}) #{key.keydefs[layer].send(method)},".ljust(60) + "// #{key.name} on #{layer_name}"
end
matrix << "\t},"
end
matrix << "},"
end
matrix.join("\n")
2015-12-14 01:28:13 +01:00
end
def save! file
puts "saving #{@name}..."
header =<<HEADER
// ----------------------------------------------------------------------------
// ergoDOX layout : saneo (generated)
// ----------------------------------------------------------------------------
HEADER
2016-06-14 02:49:01 +02:00
keys = keys_to_matrix "keycode", :code
funcs = keys_to_matrix "keyfunc", :func
2015-12-14 01:28:13 +01:00
2016-02-06 09:03:35 +01:00
File.open(LayoutFile, "w+") do |f|
2015-12-14 01:28:13 +01:00
f.puts header
f.puts "#define KB_LAYERS #{Key::Layers.size}"
f.puts "static const keycode PROGMEM _kb_layout_code[KB_ROWS][KB_COLUMNS][KB_LAYERS] = {", keys, "};"
f.puts "static const keyfunc PROGMEM _kb_layout_func[KB_ROWS][KB_COLUMNS][KB_LAYERS] = {", funcs, "};"
2015-12-14 01:28:13 +01:00
end
end
end
left =<<EOL
2017-01-28 03:04:57 +01:00
Lno ___ __ Ln4 ___ __ Ln3 ___ __ Ln2 ___ __ Ln1 ___ __ Ln0 ___ __ Lnd ___ __
_LB 0 1 2 3 4 5 6
_LP f11 f1 f2 f3 f4 f5 f6
_LN f11 f1 f2 f3 f4 f5 f6
_LF f11 f1 f2 f3 f4 f5 f6
_LD 0 1 2 3 4 5 6
Lto ___ __ Lt4 ___ __ Lt3 ___ __ Lt2 ___ __ Lt1 ___ __ Lt0 ___ __ Ltd ___ __
_LB tab x v l c w pause
_LP ~ [ ' < \\
_LN esc bs ret del ins
_LF f24 f23 f22 f21 f24
_LD 8 7 6 5 9
Lho ___ __ Lh4 ___ __ Lh3 ___ __ Lh2 ___ __ Lh1 ___ __ Lh0 ___ __
_LB UM u i a e o
_LP , \{ ? ! (
_LN a-< a-^ a-v a-> tab
_LF f14 f13 f12 f11 f20
_LD 4 3 2 1 0
Lbo ___ __ Lb4 ___ __ Lb3 ___ __ Lb2 ___ __ Lb1 ___ __ Lb0 ___ __ Lbd ___ __
_LB S_l CL % * : p z ret
_LP ` ┃ ^ ┃ | ┃ - ┃ @ ┃
_LN hom p-^ p-v end NUL
_LF f18 f17 f16 f15 f19
_LD 8 7 6 5 9
Luo ___ __ Lu4 ___ __ Lu3 ___ __ Lu2 ___ __ Lu1 ___ __
_LB a-< a-^ a-v a-> W
_LP
_LN
_LF
_LD
Lzb ___ __ Lzc ___ __
_LB esc SCL
_LP
_LN
_LF
_LD
Lya ___ __ Lyb ___ __ Lyc ___ __
_LB spc C A
_LP
_LN
_LF
_LD
Lxa ___ __ Lxb ___ __ Lxc ___ __
_LB spc C A
_LP
_LN
_LF
_LD
EOL
right =<<EOL
Rnd ___ __ Rn0 ___ __ Rn1 ___ __ Rn2 ___ __ Rn3 ___ __ Rn4 ___ __ Rno ___ __
_LB 5 6 7 8 9 0 0
_LP f5 f6 f7 f8 f9 f10 f12
_LN f5 f6 f7 f8 f9 f10 f12
_LF f5 f6 f7 f8 f9 f10 f12
_LD 5 6 7 8 9 0 0
Rtd ___ __ Rt0 ___ __ Rt1 ___ __ Rt2 ___ __ Rt3 ___ __ Rt4 ___ __ Rto ___ __
_LB NUL k h g f q q
_LP = > " ┃ ] ┃ ` ┃ `
_LN 9 5 6 7 8 8
_LF f24 f21 f22 f23 f24 f24
_LD 9 5 6 7 8 8
Rh0 ___ __ Rh1 ___ __ Rh2 ___ __ Rh3 ___ __ Rh4 ___ __ Rho ___ __
_LB s n r t d UM
_LP ) _ / \} .
_LN 0 1 2 3 4
_LF f10 f1 f2 f3 f4
_LD 0 1 2 3 4
Rbd ___ __ Rb0 ___ __ Rb1 ___ __ Rb2 ___ __ Rb3 ___ __ Rb4 ___ __ Rbo ___ __
_LB ret b m j y ; S_r CL
_LP + $ & hsh ^
_LN 9 5 6 7 8
_LF f9 f5 f6 f7 f8
_LD 9 5 6 7 8
Ru1 ___ __ Ru2 ___ __ Ru3 ___ __ Ru4 ___ __ Ruo ___ __
_LB LN a-< a-^ a-v a->
_LP
_LN
_LF
_LD
Rzc ___ __ Rzb ___ __
_LB LF LF _C
_LP
_LN
_LF
_LD
Ryc ___ __ Ryb ___ __ Rya ___ __
_LB men LF LP _L
_LP
_LN
_LF
_LD
Rxc ___ __ Rxb ___ __ Rxa ___ __
_LB men LF LP _L
_LP
_LN
_LF
_LD
EOL
keys_string = [
left,
right,
]
groups = keys_string.flat_map do |str|
str.gsub(/#.*$/, '').split(//).reject(&:blank?).map(&:lines)
end
keys_parsed = {}
groups.each.with_index(1) do |group, group_id|
layers = Key::Layers.size
# extract names
names = group.first
names = names.scan(/(\S+)(?:[ ]*\t[^\t]*){2}/).map{|name, _| name.strip}
keyrow = group[1..-1].reject(&:blank?)
# sanity check
if keyrow.size != layers
raise "wrong number of key layers in group ##{group_id}: #{keyrow}"
end
# remove first label
keyrow = keyrow.map{|l| l.split("").drop(1)}
(0..(keyrow.size-1)).step(layers).each do |row|
keyrow[row].size.times do |col|
key = layers.times.map do |layer|
keyrow[row+layer][col]
end
key = key.map{|l| l.strip.split(/\s+/, 2)}
name = names[col]
raise "name not unique: #{name}" if keys_parsed.include? name
raise "unknown name: #{name}" unless Key::Names.include? name
keys_parsed[name] = key
end
end
2015-12-14 01:28:13 +01:00
end
keys = keys_parsed.map {|name, layers| Key.new(layers, name)}
2015-12-14 01:28:13 +01:00
saneo = Layout.new :saneo, keys
saneo.save! LayoutFile