701 lines
32 KiB
Ruby
Executable File
701 lines
32 KiB
Ruby
Executable File
#!/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
|
||
╳ Ln┳o ___ __ Ln┳4 ___ __ Ln┳3 ___ __ Ln┳2 ___ __ Ln┳1 ___ __ Ln┳0 ___ __ Ln┳d ___ __
|
||
_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
|
||
╳ Lt┳o ___ __ Lt┳4 ___ __ Lt┳3 ___ __ Lt┳2 ___ __ Lt┳1 ___ __ Lt┳0 ___ __ Lt┳d ___ __
|
||
_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
|