#!/usr/bin/env ruby # -*- encoding: utf-8 -*- # Copyright muflax , 2015 # License: GNU GPLv3 (or later) # 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}..." Layer = Struct.new :code, :up, :down class Key attr_reader :layers Layers = [ :basic, :punc, :nav, :func ] Functions = { # down up "basic" => [ "&kbfun_normal_press_release", ], "media" => [ "&kbfun_mediakey_press_release", ], # down up "mod" => [ "&kbfun_modifier_press_release", ], "sticky_mod" => [ "&kbfun_modifier_sticky", ], # down up "layer" => [ "&kbfun_layer_press_release", ], "sticky" => [ "&kbfun_layer_sticky", ], # down up "shift" => [ "&kbfun_shift_press_release", ], "ctrl" => [ "&kbfun_control_press_release", ], "alt" => [ "&kbfun_alt_press_release", ], "win" => [ "&kbfun_win_press_release", ], "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", ], # down up "capslock" => [ "&kbfun_capslock_press_release", ], } Keys = { "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", # "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", # "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", # "\\" => "KEY_Backslash_Pipe", "{" => ["KEY_LeftBracket_LeftBrace", "shift"], "}" => ["KEY_RightBracket_RightBrace", "shift"], "[" => "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"], "%" => ["KEY_5_Percent", "shift"], "*" => ["KEY_8_Asterisk", "shift"], ":" => ["KEY_Semicolon_Colon", "shift"], "^" => ["KEY_6_Caret", "shift"], "<" => ["KEY_Comma_LessThan", "shift"], ">" => ["KEY_Period_GreaterThan", "shift"], "?" => ["KEY_Slash_Question", "shift"], "!" => ["KEY_1_Exclamation", "shift"], "(" => ["KEY_9_LeftParenthesis", "shift"], ")" => ["KEY_0_RightParenthesis", "shift"], "|" => ["KEY_Backslash_Pipe", "shift"], "@" => ["KEY_2_At", "shift"], "\"" => ["KEY_SingleQuote_DoubleQuote", "shift"], "_" => ["KEY_Dash_Underscore", "shift"], "+" => ["KEY_Equal_Plus", "shift"], "$" => ["KEY_4_Dollar", "shift"], "&" => ["KEY_7_Ampersand", "shift"], "#" => ["KEY_3_Pound", "shift"], # "enter" => "KEY_ReturnEnter", "return" => "KEY_ReturnEnter", "space" => "KEY_Spacebar", "tab" => "KEY_Tab", "backspace" => "KEY_DeleteBackspace", "delete" => "KEY_DeleteForward", "home" => "KEY_Home", "end" => "KEY_End", "page_up" => "KEY_PageUp", "page_down" => "KEY_PageDown", "up" => "KEY_UpArrow", "down" => "KEY_DownArrow", "left" => "KEY_LeftArrow", "right" => "KEY_RightArrow", "escape" => "KEY_Escape", "insert" => "KEY_Insert", "menu" => "KEY_Application", # "alt" => ["MOD_KEY_LeftAlt", "mod"], "alt_gr" => ["MOD_KEY_RightAlt", "mod"], "umlaut" => ["MOD_KEY_RightAlt", "mod"], "control" => ["MOD_KEY_LeftControl", "mod"], "control_l" => ["MOD_KEY_LeftControl", "mod"], "control_r" => ["MOD_KEY_RightControl", "mod"], "win" => ["MOD_KEY_LeftGUI", "mod"], "shift_l" => ["MOD_KEY_LeftShift", "mod"], "shift_r" => ["MOD_KEY_RightShift", "mod"], "scroll_lock" => "KEY_ScrollLock", # "NULL" => "KEY_NULL", # "audio_mute" => ["MEDIAKEY_AUDIO_MUTE", "media"], "audio_vol_up" => ["MEDIAKEY_AUDIO_VOL_UP", "media"], "audio_vol_down" => ["MEDIAKEY_AUDIO_VOL_DOWN", "media"], "next_track" => ["MEDIAKEY_NEXT_TRACK", "media"], "prev_track" => ["MEDIAKEY_PREV_TRACK", "media"], "stop" => ["MEDIAKEY_STOP", "media"], "play_pause" => ["MEDIAKEY_PLAY_PAUSE", "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"], "browser_search" => ["MEDIAKEY_BROWSER_SEARCH", "media"], "browser_home" => ["MEDIAKEY_BROWSER_HOME", "media"], "browser_back" => ["MEDIAKEY_BROWSER_BACK", "media"], "browser_forward" => ["MEDIAKEY_BROWSER_FORWARD", "media"], "browser_stop" => ["MEDIAKEY_BROWSER_STOP", "media"], "browser_refresh" => ["MEDIAKEY_BROWSER_REFRESH", "media"], "browser_bookmarks" => ["MEDIAKEY_BROWSER_BOOKMARKS", "media"], } Layers.each.with_index do |layer, i| Keys["#{layer}"] = ["#{i}", "layer"] end def initialize layers @layers = Array.new(layers.size) layers.each.with_index do |(key, type), i| if type.nil? and key.nil? @layers[i] = @layers[i-1] next end # keycode and default keyfunc keycode, default_type = Keys[key] # keyfunc type ||= default_type || "basic" up, down = Functions[type] down ||= up raise "overwriting shift key: #{key}, #{type}" if default_type == "shift" and type != default_type raise "key not found: #{key}" if keycode.nil? raise "type not found: #{type}" if up.nil? or down.nil? case type when "layer", "sticky" raise "invalid layer: #{key}" unless key.to_i < Key::Layers.size when "mod", "sticky_mod" raise "invalid modifier: #{key}" unless keycode.start_with? "MOD_KEY" end case keycode when /^MOD_KEY/ case type when "mod", "sticky_mod", "capslock"; # pass else raise "invalid modifier keyfunc: #{key}, #{type}" end end @layers[i] = Layer.new(keycode, down, up) end raise "transparency error: #{layers}" if @layers.any?(&:nil?) end end class Layout def initialize name, keys @name = name @keys = keys end def keys_to_matrix type, method Key::Layers.map.with_index do |_, layer| "KB_MATRIX_LAYER(\n#{@keys.map{|key| "(#{type}) " + key.layers[layer].send(method)}.join(",\n")}),\n" end.join("\n") end def save! file puts "saving #{@name}..." header =<
}, %w{ 5 }, %w{ f5 }, %w{ g }, %w{ " }, %w{ 6 }, %w{ f6 }, %w{ f }, %w{ ] }, %w{ 7 }, %w{ f7 }, %w{ q }, %w{ ` }, %w{ 8 }, %w{ f8 }, %w{ q }, %w{ ` }, %w{ 8 }, %w{ f8 }, # 1.5 # home # letter type punc type nav type func type %w{ s }, %w{ ) }, %w{ 0 }, %w{ f10 }, %w{ n }, %w{ _ }, %w{ 1 }, %w{ f1 }, %w{ r }, %w{ / }, %w{ 2 }, %w{ f2 }, %w{ t }, %w{ \} }, %w{ 3 }, %w{ f3 }, %w{ d }, %w{ . }, %w{ 4 }, %w{ f4 }, %w{ umlaut }, %w{ }, %w{ }, %w{ }, # 1.5 # bottom # letter type punc type nav type func type %w{ enter }, %w{ }, %w{ }, %w{ }, # 1.5 %w{ b }, %w{ + }, %w{ 9 }, %w{ f9 }, %w{ m }, %w{ $ }, %w{ 5 }, %w{ f5 }, %w{ j }, %w{ & }, %w{ 6 }, %w{ f6 }, %w{ y }, %w{ # }, %w{ 7 }, %w{ f7 }, %w{ ; }, %w{ ^ }, %w{ 8 }, %w{ f8 }, %w{ shift_r capslock }, %w{ }, %w{ }, %w{ }, # 1.5 # underbottom # letter type punc type nav type func type %w{ nav }, %w{ }, %w{ }, %w{ }, %w{ left }, %w{ }, %w{ }, %w{ }, %w{ up }, %w{ }, %w{ }, %w{ }, %w{ down }, %w{ }, %w{ }, %w{ }, %w{ right }, %w{ }, %w{ }, %w{ }, # thumb-top # letter type punc type nav type func type %w{ func }, %w{ }, %w{ }, %w{ }, %w{ func ctrl_layer }, %w{ }, %w{ }, %w{ }, # thumb-double # letter type punc type nav type func type %w{ menu }, %w{ }, %w{ }, %w{ }, %w{ func }, %w{ }, %w{ }, %w{ }, %w{ punc sticky }, %w{ }, %w{ }, %w{ }, # thumb-home # letter type punc type nav type func type %w{ menu }, %w{ }, %w{ }, %w{ }, %w{ func }, %w{ }, %w{ }, %w{ }, %w{ punc sticky }, %w{ }, %w{ }, %w{ }, ].each_slice(Key::Layers.size).map do |layers| Key.new layers end saneo = Layout.new :saneo, keys saneo.save! LayoutFile