You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

700 lines
32 KiB

#!/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
# 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
LayoutDir = "src/keyboard"
LayoutFile = "#{LayoutDir}/layout.c"
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
class Key
attr_reader :keydefs, :name, :row, :col
Layers = [ :LB, :LP, :LN, :LF, :LD ]
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",
}
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
"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 ],
"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 ],
"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 ],
}
Layers.each.with_index do |layer, i|
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
layers.each.with_index do |(key, type), i|
if type.nil? and key.nil?
@keydefs[i] = @keydefs[i-1]
next
end
# keycode and default keyfunc
keycode, default_type = Keys[key]
# keyfunc
type ||= default_type || "basic"
keyfunc = Functions[type]
raise "overwriting shift key: #{key}, #{type}" if default_type == "shift" and type != default_type
raise "key not found: #{key}" if keycode.nil?
raise "func not found: #{type}" if keyfunc.nil?
case type
when "layer", "sticky", "_L"
raise "invalid layer: #{key}" unless key.to_i < Key::Layers.size
when "mod", "sticky_mod", "_M"
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
else
raise "invalid modifier keyfunc: #{key}, #{type}"
end
end
@keydefs[i] = Keydef.new(keycode, keyfunc)
end
raise "transparency error: #{layers}" if @keydefs.any?(&:nil?)
end
Dummy = Key.new(Key::Layers.map{"NULL"}, "dummy")
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
end
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")
end
def save! file
puts "saving #{@name}..."
header =<<HEADER
// ----------------------------------------------------------------------------
// ergoDOX layout : saneo (generated)
// ----------------------------------------------------------------------------
HEADER
keys = keys_to_matrix "keycode", :code
funcs = keys_to_matrix "keyfunc", :func
File.open(LayoutFile, "w+") do |f|
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, "};"
end
end
end
left =<<EOL
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 ┃
╳ Lh┳o ___ __ Lh┳4 ___ __ Lh┳3 ___ __ Lh┳2 ___ __ Lh┳1 ___ __ Lh┳0 ___ __
_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
╳ Lb┳o ___ __ Lb┳4 ___ __ Lb┳3 ___ __ Lb┳2 ___ __ Lb┳1 ___ __ Lb┳0 ___ __ Lb┳d ___ __
_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 ┃
╳ Lu┳o ___ __ Lu┳4 ___ __ Lu┳3 ___ __ Lu┳2 ___ __ Lu┳1 ___ __
_LB ┃ a-< ┃ a-^ ┃ a-v ┃ a-> ┃ W
_LP ┃ ┃ ┃ ┃ ┃
_LN ┃ ┃ ┃ ┃ ┃
_LF ┃ ┃ ┃ ┃ ┃
_LD ┃ ┃ ┃ ┃ ┃
╳ Lz┳b ___ __ Lz┳c ___ __
_LB ┃ esc ┃ SCL
_LP ┃ ┃
_LN ┃ ┃
_LF ┃ ┃
_LD ┃ ┃
╳ Ly┳a ___ __ Ly┳b ___ __ Ly┳c ___ __
_LB ┃ spc ┃ C ┃ A
_LP ┃ ┃ ┃
_LN ┃ ┃ ┃
_LF ┃ ┃ ┃
_LD ┃ ┃ ┃
╳ Lx┳a ___ __ Lx┳b ___ __ Lx┳c ___ __
_LB ┃ spc ┃ C ┃ A
_LP ┃ ┃ ┃
_LN ┃ ┃ ┃
_LF ┃ ┃ ┃
_LD ┃ ┃ ┃
EOL
right =<<EOL
╳ Rn┳d ___ __ Rn┳0 ___ __ Rn┳1 ___ __ Rn┳2 ___ __ Rn┳3 ___ __ Rn┳4 ___ __ Rn┳o ___ __
_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
╳ Rt┳d ___ __ Rt┳0 ___ __ Rt┳1 ___ __ Rt┳2 ___ __ Rt┳3 ___ __ Rt┳4 ___ __ Rt┳o ___ __
_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
╳ Rh┳0 ___ __ Rh┳1 ___ __ Rh┳2 ___ __ Rh┳3 ___ __ Rh┳4 ___ __ Rh┳o ___ __
_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 ┃
╳ Rb┳d ___ __ Rb┳0 ___ __ Rb┳1 ___ __ Rb┳2 ___ __ Rb┳3 ___ __ Rb┳4 ___ __ Rb┳o ___ __
_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 ┃
╳ Ru┳1 ___ __ Ru┳2 ___ __ Ru┳3 ___ __ Ru┳4 ___ __ Ru┳o ___ __
_LB ┃ LN ┃ a-< ┃ a-^ ┃ a-v ┃ a->
_LP ┃ ┃ ┃ ┃ ┃
_LN ┃ ┃ ┃ ┃ ┃
_LF ┃ ┃ ┃ ┃ ┃
_LD ┃ ┃ ┃ ┃ ┃
╳ Rz┳c ___ __ Rz┳b ___ __
_LB ┃ LF ┃ LF _C
_LP ┃ ┃
_LN ┃ ┃
_LF ┃ ┃
_LD ┃ ┃
╳ Ry┳c ___ __ Ry┳b ___ __ Ry┳a ___ __
_LB ┃ men ┃ LF ┃ LP _L
_LP ┃ ┃ ┃
_LN ┃ ┃ ┃
_LF ┃ ┃ ┃
_LD ┃ ┃ ┃
╳ Rx┳c ___ __ Rx┳b ___ __ Rx┳a ___ __
_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
end
keys = keys_parsed.map {|name, layers| Key.new(layers, name)}
saneo = Layout.new :saneo, keys
saneo.save! LayoutFile