From 81e667feab00fb38d5a29bf738f1320aec812250 Mon Sep 17 00:00:00 2001 From: Kazunobu Kuriyama Date: Wed, 2 Sep 2015 17:23:48 +0900 Subject: [PATCH] keyboardlayout: Extend allowable 'group_names' pattern range Extend the range of the 'group_names' patterns which is currently restricted to typical ones like pc+us+ru:2+de:3+ba:4+inet to more general ones such as macintosh_vndr/apple(alukbd)+macintosh_vndr/jp(usmac)+macintosh_vndr/jp(mac):2 so that the keyboardlayout widget can handles all possible patterns returned by awesome.xkb_get_layout_group(). --- lib/awful/widget/keyboardlayout.lua | 275 ++++++++++++++++++++++++---- 1 file changed, 239 insertions(+), 36 deletions(-) diff --git a/lib/awful/widget/keyboardlayout.lua b/lib/awful/widget/keyboardlayout.lua index 96825811f..9eec19318 100644 --- a/lib/awful/widget/keyboardlayout.lua +++ b/lib/awful/widget/keyboardlayout.lua @@ -17,58 +17,261 @@ local widget_base = require("wibox.widget.base") -- awful.widget.keyboardlayout local keyboardlayout = { mt = {} } +-- As to the country-code-like symbols below, refer to the names of XKB's +-- data files in /.../xkb/symbols/*. +keyboardlayout.xkeyboard_country_code = { + ["ad"] = true, -- Andorra + ["af"] = true, -- Afganistan + ["al"] = true, -- Albania + ["am"] = true, -- Armenia + ["ara"] = true, -- Arabic + ["at"] = true, -- Austria + ["az"] = true, -- Azerbaijan + ["ba"] = true, -- Bosnia and Herzegovina + ["bd"] = true, -- Bangladesh + ["be"] = true, -- Belgium + ["bg"] = true, -- Bulgaria + ["br"] = true, -- Brazil + ["bt"] = true, -- Bhutan + ["bw"] = true, -- Botswana + ["by"] = true, -- Belarus + ["ca"] = true, -- Canada + ["cd"] = true, -- Congo + ["ch"] = true, -- Switzerland + ["cm"] = true, -- Cameroon + ["cn"] = true, -- China + ["cz"] = true, -- Czechia + ["de"] = true, -- Germany + ["dk"] = true, -- Denmark + ["ee"] = true, -- Estonia + ["epo"] = true, -- Esperanto + ["es"] = true, -- Spain + ["et"] = true, -- Ethiopia + ["fi"] = true, -- Finland + ["fo"] = true, -- Faroe Islands + ["fr"] = true, -- France + ["gb"] = true, -- United Kingdom + ["ge"] = true, -- Georgia + ["gh"] = true, -- Ghana + ["gn"] = true, -- Guinea + ["gr"] = true, -- Greece + ["hr"] = true, -- Croatia + ["hu"] = true, -- Hungary + ["ie"] = true, -- Ireland + ["il"] = true, -- Israel + ["in"] = true, -- India + ["iq"] = true, -- Iraq + ["ir"] = true, -- Iran + ["is"] = true, -- Iceland + ["it"] = true, -- Italy + ["jp"] = true, -- Japan + ["ke"] = true, -- Kenya + ["kg"] = true, -- Kyrgyzstan + ["kh"] = true, -- Cambodia + ["kr"] = true, -- Korea + ["kz"] = true, -- Kazakhstan + ["la"] = true, -- Laos + ["latam"] = true, -- Latin America + ["latin"] = true, -- Latin + ["lk"] = true, -- Sri Lanka + ["lt"] = true, -- Lithuania + ["lv"] = true, -- Latvia + ["ma"] = true, -- Morocco + ["mao"] = true, -- Maori + ["me"] = true, -- Montenegro + ["mk"] = true, -- Macedonia + ["ml"] = true, -- Mali + ["mm"] = true, -- Myanmar + ["mn"] = true, -- Mongolia + ["mt"] = true, -- Malta + ["mv"] = true, -- Maldives + ["ng"] = true, -- Nigeria + ["nl"] = true, -- Netherlands + ["no"] = true, -- Norway + ["np"] = true, -- Nepal + ["ph"] = true, -- Philippines + ["pk"] = true, -- Pakistan + ["pl"] = true, -- Poland + ["pt"] = true, -- Portugal + ["ro"] = true, -- Romania + ["rs"] = true, -- Serbia + ["ru"] = true, -- Russia + ["se"] = true, -- Sweden + ["si"] = true, -- Slovenia + ["sk"] = true, -- Slovakia + ["sn"] = true, -- Senegal + ["sy"] = true, -- Syria + ["th"] = true, -- Thailand + ["tj"] = true, -- Tajikistan + ["tm"] = true, -- Turkmenistan + ["tr"] = true, -- Turkey + ["tw"] = true, -- Taiwan + ["tz"] = true, -- Tanzania + ["ua"] = true, -- Ukraine + ["us"] = true, -- USA + ["uz"] = true, -- Uzbekistan + ["vn"] = true, -- Vietnam + ["za"] = true, -- South Africa +} + -- Callback for updaing current layout -local function update_status (keyboardlayout) - keyboardlayout._current = awesome.xkb_get_layout_group(); +local function update_status (self) + self._current = awesome.xkb_get_layout_group(); local text = "" - if (#keyboardlayout._layout > 0) then - text = (" " .. keyboardlayout._layout[keyboardlayout._current] .. " ") + if (#self._layout > 0) then + text = (" " .. self._layout[self._current] .. " ") end - keyboardlayout.widget:set_text(text) + self.widget:set_text(text) +end + +--- Auxiliary function for the local function update_layout(). +-- Create an array whose element is a table consisting of the four fields: +-- vendor, file, section and group_idx, which all correspond to the +-- xkb_symbols pattern "vendor/file(section):group_idx". +-- @tparam string group_names The string awesome.xkb_get_group_names() returns. +-- @treturn table An array of tables whose keys are vendor, file, section, and group_idx. +function keyboardlayout.get_groups_from_group_names(group_names) + if group_names == nil then + return nil + end + + -- Pattern elements to be captured. + local word_pat = "([%w_]+)" + local sec_pat = "(%b())" + local idx_pat = ":(%d)" + -- Pairs of a pattern and its callback. In callbacks, set 'group_idx' to 1 + -- and return it if there's no specification on 'group_idx' in the given + -- pattern. + local pattern_and_callback_pairs = { + -- vendor/file(section):group_idx + ["^" .. word_pat .. "/" .. word_pat .. sec_pat .. idx_pat .. "$"] + = function(token, pattern) + local vendor, file, section, group_idx = string.match(token, pattern) + return vendor, file, section, group_idx + end, + -- vendor/file(section) + ["^" .. word_pat .. "/" .. word_pat .. sec_pat .. "$"] + = function(token, pattern) + local vendor, file, section = string.match(token, pattern) + return vendor, file, section, 1 + end, + -- vendor/file:group_idx + ["^" .. word_pat .. "/" .. word_pat .. idx_pat .. "$"] + = function(token, pattern) + local vendor, file, group_idx = string.match(token, pattern) + return vendor, file, nil, group_idx + end, + -- vendor/file + ["^" .. word_pat .. "/" .. word_pat .. "$"] + = function(token, pattern) + local vendor, file = string.match(token, pattern) + return vendor, file, nil, 1 + end, + -- file(section):group_idx + ["^" .. word_pat .. sec_pat .. idx_pat .. "$"] + = function(token, pattern) + local file, section, group_idx = string.match(token, pattern) + return nil, file, section, group_idx + end, + -- file(section) + ["^" .. word_pat .. sec_pat .. "$"] + = function(token, pattern) + local file, section = string.match(token, pattern) + return nil, file, section, 1 + end, + -- file:group_idx + ["^" .. word_pat .. idx_pat .. "$"] + = function(token, pattern) + local file, group_idx = string.match(token, pattern) + return nil, file, nil, group_idx + end, + -- file + ["^" .. word_pat .. "$"] + = function(token, pattern) + local file = string.match(token, pattern) + return nil, file, nil, 1 + end + } + + -- Split 'group_names' into 'tokens'. The separator is "+". + local tokens = {} + string.gsub(group_names, "[^+]+", function(match) + table.insert(tokens, match) + end) + + -- For each token in 'tokens', check if it matches one of the patterns in + -- the array 'pattern_and_callback_pairs', where the patterns are used as + -- key. If a match is found, extract captured strings using the + -- corresponding callback function. Check if those extracted is country + -- specific part of a layout. If so, add it to 'layout_groups'; otherwise, + -- ignore it. + local layout_groups = {} + for i = 1, #tokens do + for pattern, callback in pairs(pattern_and_callback_pairs) do + local vendor, file, section, group_idx = callback(tokens[i], pattern) + if file then + if not keyboardlayout.xkeyboard_country_code[file] then + break + end + + if section then + section = string.gsub(section, "%(([%w_]+)%)", "%1") + end + + table.insert(layout_groups, { vendor = vendor, + file = file, + section = section, + group_idx = tonumber(group_idx) }) + break + end + end + end + + return layout_groups end -- Callback for updating list of layouts -local function update_layout(keyboardlayout) - keyboardlayout._layout = {}; - local group_names = awesome.xkb_get_group_names(); - --- A typical layout string looks like "pc+us+ru:2+de:3+ba:4+inet", --- and we want to get only three matches: "us", "ru:2", "de:3" "ba:4". --- Please note that numbers of groups reported by xkb_get_group_names --- is greater by one than the real group number. - local first_group = string.match(group_names, "+(%a+)"); - if (not first_group) then - error ("Failed to get list of keyboard groups"); +local function update_layout(self) + self._layout = {}; + local layouts = keyboardlayout.get_groups_from_group_names(awesome.xkb_get_group_names()) + if layouts == nil or layouts[1] == nil then + error("Failed to get list of keyboard groups") return; end - keyboardlayout._layout[0] = first_group; - - for name, number_str in string.gmatch(group_names, "+(%a+):(%d)") do - group = tonumber(number_str); - keyboardlayout._layout[group - 1] = name; + if #layouts == 1 then + layouts[1].group_idx = 0 end - update_status(keyboardlayout) + for _, v in ipairs(layouts) do + local layout_name = v.file + if v.section ~= nil then + layout_name = layout_name .. "(" .. v.section .. ")" + end + -- Please note that numbers of groups reported by xkb_get_group_names + -- is greater by one than the real group number. + self._layout[v.group_idx - 1] = layout_name + end + update_status(self) end --- Create a keyboard layout widget. It shows current keyboard layout name in a textbox. -- @return A keyboard layout widget. function keyboardlayout.new() local widget = textbox() - local keyboardlayout = widget_base.make_widget(widget) + local self = widget_base.make_widget(widget) - keyboardlayout.widget = widget + self.widget = widget - update_layout(keyboardlayout); + update_layout(self); - keyboardlayout.next_layout = function() - new_layout = (keyboardlayout._current + 1) % (#keyboardlayout._layout + 1) - keyboardlayout.set_layout(new_layout) + self.next_layout = function() + new_layout = (self._current + 1) % (#self._layout + 1) + self.set_layout(new_layout) end - keyboardlayout.set_layout = function(group_number) - if (0 > group_number) or (group_number > #keyboardlayout._layout) then + self.set_layout = function(group_number) + if (0 > group_number) or (group_number > #self._layout) then error("Invalid group number: " .. group_number .. - "expected number from 0 to " .. #keyboardlayout._layout) + "expected number from 0 to " .. #self._layout) return; end awesome.xkb_set_layout_group(group_number); @@ -76,16 +279,16 @@ function keyboardlayout.new() -- callback for processing layout changes capi.awesome.connect_signal("xkb::map_changed", - function () update_layout(keyboardlayout) end) + function () update_layout(self) end) capi.awesome.connect_signal("xkb::group_changed", - function () update_status(keyboardlayout) end); + function () update_status(self) end); -- Mouse bindings - keyboardlayout:buttons( - util.table.join(button({ }, 1, keyboardlayout.next_layout)) + self:buttons( + util.table.join(button({ }, 1, self.next_layout)) ) - return keyboardlayout + return self end local _instance = nil;