File: xkbion.lua

package info (click to toggle)
notion 4.0.2%2Bdfsg-5
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 4,676 kB
  • sloc: ansic: 47,508; sh: 2,096; makefile: 603; perl: 270
file content (173 lines) | stat: -rw-r--r-- 6,440 bytes parent folder | download | duplicates (4)
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
-- Authors: Sergey Redin <sergey@redin.info>
-- License: Public domain
-- Last Changed: Unknown
--
-- xkbion.lua
-- TODO: make xkbion_set understand some simple presets

-- Author: Sergey Redin <sergey at redin.info>
-- This software is in the public domain.
--
-- Thanks:
-- smersh at users.sf.net (author of xkbind) for the original idea

--[[

-- This script allows you to use independent keyboard layouts for different windows in Ion3.
-- It uses a window property to store the XKB groups, so you can restart Ion without losing
-- settings for each window.

-- Example usage. This is what I have in my cfg_ion.lua:

dopath("mod_xkb")
dopath("xkbion.lua")
xkbion_set {
    {name="EN", hint="", action = function() mod_xkb.lock_group(0) end},
    {name="RU", hint="important", action = function() mod_xkb.lock_group(1) end},
    key="Caps_Lock",
    statusname = "xkbion",
}
xkbion_set {
    {name="num", command="numlockx on"},
    {name="<->", command="numlockx off"},
    key="Num_Lock",
    statusname="num",
    atomname="XKBION_NUM",
}
xkbion_set {
    {name="----", hint="", action = function() mod_xkb.lock_modifiers(2, 0) end},
    {name="CAPS", hint="critical", action = function() mod_xkb.lock_modifiers(2, 2) end},
    key="Caps_Lock",
    statusname = "caps",
    atomname="XKBION_CAPS",
}

-- Edit this to suit your needs.
-- Please note, if you want to use Caps_Lock key to change the keyboard groups like I do,
-- do not forget to add "grp:caps_toggle" to your XKB settings, just to prevent X from using
-- this key also for swiching keyboard registers.

-- At least one group definition must be present.
-- "name" is only neseccary if you want to use mod_statusbar to indicate current XKB group.
-- "hint" is only necessary if you want to highlight your XKB group in statusbar, possible
-- values are standard values provided by the mod_statusbar: important, normal, critical
-- "command" and "action" are also unneseccary but xkbion.lua is not particulary useful
-- without them. :) The same thing for "key".

-- The last thing to say about xkbion_set() parameters is that if you call xkbion_set
-- more than once (like I do it for XKB groups and NumLock state) you must choose different
-- "atomname" values. The default for atomname is XKBION_GROUP.

-- The second xkbion_set() call (numlock section) is here mostly for the example. Most users
-- will need only one call, for changing XKB group. Please also note that you can define more
-- than two groups in call to xkbion_set().

-- You can use this line in cfg_statusbar.lua to indicate the current XKB group:

template="... %xkbion ...",

-- If your Ion does not have mod_xkb, you may try the following:

xkbion_set {
    {name="EN", command="setxkbmap us -option grp:caps_toggle"},
    {name="RU", command="setxkbmap ru winkeys -option grp:caps_toggle"},
    key="Caps_Lock",
    statusname = "xkbion",
}

]]

function xkbion_set (groups) -- the only global created by xkbion.lua

    if not groups or type(groups) ~= "table" then error("bad args") end
    if not groups[1] or type(groups[1]) ~= "table" then
        error("default group is undefined")
    end

    -- window_group_prop(w) - get XKBION_GROUP integer property of window `w' (set it to 1 if it's not yet defined)
    -- window_group_prop(w, group) - set XKBION_GROUP property of window `w' to integer `group'
    -- "XKBION_GROUP" is just the default name
    local window_group_prop
    do
        local XA_INTEGER = 19
        local atom = ioncore.x_intern_atom( tostring( groups.atomname or "XKBION_GROUP" ) )
        if not atom or type(atom) ~= "number" then
            error("Cannot intern atom " .. atomname)
        end
        window_group_prop = function(w, gnum)
            if not w or type(w) ~= "userdata" or not w.xid or type(w.xid) ~= "function" then return 1 end
            local xid = tonumber( w:xid() )
            if gnum == nil then
                local t = ioncore.x_get_window_property( xid, atom, XA_INTEGER, 1, true )
                if t and type(t) == "table" and t[1] ~= nil then
                    do return tonumber(t[1]) end
                else
                    gnum = 1
                end
            else
                gnum = tonumber(gnum)
            end
            -- we're here if the second argument is set or if the window does not have our property yet
            ioncore.defer( function()
                ioncore.x_change_property( xid, atom, XA_INTEGER, 32, "replace", {gnum} )
            end )
            return gnum
        end
    end

    local set_group
    do
        local current_gnum = 1
        local first_time = true
        local statusname = groups.statusname
        if statusname and type(statusname) ~= "string" then statusname = nil end
        set_group = function(w, do_increment)
            local gnum
            if w then
                gnum = window_group_prop(w)
            else
                gnum = 1
            end
            if do_increment then gnum = gnum + 1 end
            local g = groups[gnum]
            if not g then gnum, g = 1, groups[1] end
            if not g then return end -- error in settings, groups[1] not defined
            if first_time then
                first_time = false
            elseif gnum == current_gnum then
                return
            end
            window_group_prop(w, gnum) -- it's OK to call it even it `w' is nil
            if g.command then
                ioncore.exec(g.command)
            end
            if g.action then ioncore.defer(g.action) end
            current_gnum = gnum
            local group_name = g.name
            local hint_name = g.hint
            if statusname and group_name and type(group_name) == "string" then
                mod_statusbar.inform(statusname, group_name)
                mod_statusbar.inform(statusname.."_hint", hint_name)
                ioncore.defer(mod_statusbar.update)
            end
        end
    end

    ioncore.get_hook("region_notify_hook"):add(
        function(reg, action)
            if (obj_typename(reg) == "WClientWin") and (action == "activated") then
                set_group(reg)
            end
        end
    )

    local key = groups.key
    if key and type(key) == "string" then
        defbindings("WClientWin", {
            kpress(key, function (_, _sub) set_group(_, true)  end, "_sub:WClientWin")
        })
    end

    set_group() -- initialize

end -- xkbion_set()