1 -- Authors: Sergey Redin <sergey@redin.info>
2 -- License: Public domain
3 -- Last Changed: Unknown
6 -- TODO: make xkbion_set understand some simple presets
8 -- Author: Sergey Redin <sergey at redin.info>
9 -- This software is in the public domain.
12 -- smersh at users.sf.net (author of xkbind) for the original idea
16 -- This script allows you to use independent keyboard layouts for different windows in Ion3.
17 -- It uses a window property to store the XKB groups, so you can restart Ion without losing
18 -- settings for each window.
20 -- Example usage. This is what I have in my cfg_ion.lua:
25 {name="EN", hint="", action = function() mod_xkb.lock_group(0) end},
26 {name="RU", hint="important", action = function() mod_xkb.lock_group(1) end},
28 statusname = "xkbion",
31 {name="num", command="numlockx on"},
32 {name="<->", command="numlockx off"},
35 atomname="XKBION_NUM",
38 {name="----", hint="", action = function() mod_xkb.lock_modifiers(2, 0) end},
39 {name="CAPS", hint="critical", action = function() mod_xkb.lock_modifiers(2, 2) end},
42 atomname="XKBION_CAPS",
45 -- Edit this to suit your needs.
46 -- Please note, if you want to use Caps_Lock key to change the keyboard groups like I do,
47 -- do not forget to add "grp:caps_toggle" to your XKB settings, just to prevent X from using
48 -- this key also for swiching keyboard registers.
50 -- At least one group definition must be present.
51 -- "name" is only neseccary if you want to use mod_statusbar to indicate current XKB group.
52 -- "hint" is only necessary if you want to highlight your XKB group in statusbar, possible
53 -- values are standard values provided by the mod_statusbar: important, normal, critical
54 -- "command" and "action" are also unneseccary but xkbion.lua is not particulary useful
55 -- without them. :) The same thing for "key".
57 -- The last thing to say about xkbion_set() parameters is that if you call xkbion_set
58 -- more than once (like I do it for XKB groups and NumLock state) you must choose different
59 -- "atomname" values. The default for atomname is XKBION_GROUP.
61 -- The second xkbion_set() call (numlock section) is here mostly for the example. Most users
62 -- will need only one call, for changing XKB group. Please also note that you can define more
63 -- than two groups in call to xkbion_set().
65 -- You can use this line in cfg_statusbar.lua to indicate the current XKB group:
67 template="... %xkbion ...",
69 -- If your Ion does not have mod_xkb, you may try the following:
72 {name="EN", command="setxkbmap us -option grp:caps_toggle"},
73 {name="RU", command="setxkbmap ru winkeys -option grp:caps_toggle"},
75 statusname = "xkbion",
80 function xkbion_set (groups
) -- the only global created by xkbion.lua
82 if not groups
or type(groups
) ~= "table" then error("bad args") end
83 if not groups
[1] or type(groups
[1]) ~= "table" then
84 error("default group is undefined")
87 -- window_group_prop(w) - get XKBION_GROUP integer property of window `w' (set it to 1 if it's not yet defined)
88 -- window_group_prop(w, group) - set XKBION_GROUP property of window `w' to integer `group'
89 -- "XKBION_GROUP" is just the default name
90 local window_group_prop
93 local atom
= ioncore
.x_intern_atom( tostring( groups
.atomname
or "XKBION_GROUP" ) )
94 if not atom
or type(atom
) ~= "number" then
95 error("Cannot intern atom " .. atomname
)
97 window_group_prop
= function(w
, gnum
)
98 if not w
or type(w
) ~= "userdata" or not w
.xid
or type(w
.xid
) ~= "function" then return 1 end
99 local xid
= tonumber( w
:xid() )
101 local t
= ioncore
.x_get_window_property( xid
, atom
, XA_INTEGER
, 1, true )
102 if t
and type(t
) == "table" and t
[1] ~= nil then
103 do return tonumber(t
[1]) end
108 gnum
= tonumber(gnum
)
110 -- we're here if the second argument is set or if the window does not have our property yet
111 ioncore
.defer( function()
112 ioncore
.x_change_property( xid
, atom
, XA_INTEGER
, 32, "replace", {gnum
} )
120 local current_gnum
= 1
121 local first_time
= true
122 local statusname
= groups
.statusname
123 if statusname
and type(statusname
) ~= "string" then statusname
= nil end
124 set_group
= function(w
, do_increment
)
127 gnum
= window_group_prop(w
)
131 if do_increment
then gnum
= gnum
+ 1 end
132 local g
= groups
[gnum
]
133 if not g
then gnum
, g
= 1, groups
[1] end
134 if not g
then return end -- error in settings, groups[1] not defined
137 elseif gnum
== current_gnum
then
140 window_group_prop(w
, gnum
) -- it's OK to call it even it `w' is nil
142 ioncore
.exec(g
.command
)
144 if g
.action
then ioncore
.defer(g
.action
) end
146 local group_name
= g
.name
147 local hint_name
= g
.hint
148 if statusname
and group_name
and type(group_name
) == "string" then
149 mod_statusbar
.inform(statusname
, group_name
)
150 mod_statusbar
.inform(statusname
.."_hint", hint_name
)
151 ioncore
.defer(mod_statusbar
.update
)
156 ioncore
.get_hook("region_notify_hook"):add(
157 function(reg
, action
)
158 if (obj_typename(reg
) == "WClientWin") and (action
== "activated") then
164 local key
= groups
.key
165 if key
and type(key
) == "string" then
166 defbindings("WClientWin", {
167 kpress(key
, function (_
, _sub
) set_group(_
, true) end, "_sub:WClientWin")
171 set_group() -- initialize