This repository has been archived by the owner on Aug 18, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
/
keyboard-layout-indicator.lua
175 lines (143 loc) · 4.72 KB
/
keyboard-layout-indicator.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
-- Keyboard Layout Switcher
-- Keyboard map indicator and changer
local awful = require("awful")
local wibox = require("wibox")
local gears = require("gears")
local timer = gears.timer or timer
local spawn = awful.spawn
------------------------------------------
-- Compatibility with Lua <= 5.1
------------------------------------------
local _unpack = table.unpack or unpack
-- same as table.pack in lua 5.2:
local function pack(...)
return {n = select('#', ...), ...}
end
-- different from table.unpack in lua.5.2:
local function unpack(t)
return _unpack(t, 1, t.n)
end
------------------------------------------
-- Private utility functions
------------------------------------------
local function trim(s)
if s == nil then return nil end
return (s:gsub("^%s*(.-)%s*$", "%1"))
end
local function findindex(array, match)
for k, v in pairs(array) do
if match(v) then return k end
end
end
local function spawn_sequential(...)
if select('#', ...) > 0 then
local command = select(1, ...)
local args = pack(select(2, ...))
local exec_tail = function()
spawn_sequential(unpack(args))
end
if type(command) == "function" then
command()
exec_tail()
elseif command == nil then
exec_tail()
else
spawn.easy_async(command, exec_tail)
end
end
end
------------------------------------------
-- Indicator class
------------------------------------------
local indicator = {}
function indicator:new(args)
return setmetatable({}, {__index = self}):init(args)
end
function indicator:init(args)
self.cmd = "setxkbmap"
self.layouts = args.layouts
self.prompt = args.prompt or "Run: "
self.preset = args.preset or self.cmd .. " "
self.post_set_hooks = args.post_set_hooks or {"xmodmap ~/.Xmodmap"}
self.index = 1 -- 1-based index!
self.current = nil
self.widget = wibox.widget.textbox()
self.widget:buttons(awful.util.table.join(
awful.button({ }, 1, function() self:next() end),
awful.button({ }, 3, function() self:prev() end),
awful.button({ }, 4, function() self:prev() end),
awful.button({ }, 5, function() self:next() end),
-- execute prompt on middle click:
awful.button({ }, 2, function ()
awful.prompt.run {
prompt = self.prompt,
text = self.preset,
textbox = awful.screen.focused().mypromptbox.widget,
exe_callback = function(cmd) self:setcustom(cmd) end,
}
end)
))
awesome.connect_signal("xkb::map_changed", function() self:update() end)
awesome.connect_signal("xkb::group_changed", function() self:update() end)
self.timer = timer({ timeout = args.timeout or 0.5 })
self.timer:connect_signal("timeout", function() self:update() end)
self.timer:start()
self:update()
return self
end
function indicator:set(i)
-- set current index
self.index = (i-1) % #(self.layouts) + 1
self.current = self.layouts[self.index]
self:update_text()
-- execute command
local command = self.current.command or ("%s %s %s"):format(
self.cmd, self.current.layout, self.current.variant or "")
spawn_sequential(command, unpack(self.post_set_hooks))
end
function indicator:setcustom(str)
spawn.easy_async(str, function()
self:update()
end)
end
function indicator:update()
self:get_async(function(index, info)
self.known = index ~= nil
self.index = index or self.index
self.current = info
self:update_text()
end)
end
function indicator:update_text()
self.widget:set_markup(("<span %s>%s</span>"):format(
self.current.attr or "", self.current.name))
end
function indicator:get_async(callback)
spawn.easy_async(self.cmd .. " -query", function(status)
callback(self:parse_status(status))
end)
end
function indicator:parse_status(status)
-- parse current layout from setxkbmap
local layout = trim(string.match(status, "layout:([^\n]*)"))
local variant = trim(string.match(status, "variant:([^\n]*)"))
-- find layout in self.layouts
local index = findindex(self.layouts, function (v)
return v.layout == layout and v.variant == variant
end)
return index, index and self.layouts[tonumber(index)] or {
attr = 'color="yellow"',
layout = layout,
variant = variant,
name = variant and layout.."/"..variant or layout,
}
end
function indicator:next()
self:set(self.index + (self.known and 1 or 0))
end
function indicator:prev()
self:set(self.index - (self.known and 1 or 0))
end
return setmetatable(indicator, {
__call = indicator.new,
})