rkremap 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +3 -2
- data/example/henkan-super.rb +4 -2
- data/example/hoge.rb +11 -0
- data/example/tmtms.rb +37 -10
- data/lib/rkremap/evdev.rb +4 -3
- data/lib/rkremap/evdev_list.rb +2 -0
- data/lib/rkremap/event.rb +6 -2
- data/lib/rkremap/remap.rb +26 -10
- data/lib/rkremap/version.rb +1 -1
- data/lib/rkremap/winattr.rb +10 -74
- data/lib/rkremap/winattr_kde.rb +67 -0
- data/lib/rkremap/winattr_x11.rb +89 -0
- data/lib/rkremap.rb +15 -5
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 590e1e170524210ac70556095262d1cda5a7a94330d2aaa2927a644a96a431d0
|
4
|
+
data.tar.gz: 97f670607504cac0b34b8e128711eabf8f8a7000edf122350deaf7409d481315
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9932e445cb887e0a4ee4e7b48c9b29767bee3a44936d99e5eb34e6f873a7c0ce24a1edbdea10da5985744ac29c6871d284896c2497ff0ce82407bb8e16eacdf
|
7
|
+
data.tar.gz: 8f88809a29fa4d7a407807ddd11c8e570258fcb8f760cf4b285c2e35314e61cd18de0c43516d143a63cfc3f71d778fa5e07e97c5eb204ba5687338572f186545
|
data/README.md
CHANGED
@@ -66,9 +66,10 @@ rkremap.exclude = /touchpad/i
|
|
66
66
|
|
67
67
|
正規表現を設定するとデバイス名がマッチした場合にデバイスを grab する。
|
68
68
|
|
69
|
-
### Rkremap#
|
69
|
+
### Rkremap#window_system
|
70
70
|
|
71
|
-
`
|
71
|
+
`"x11"` に設定すると X のアプリ名等を取得できる。Wayland 環境では動かない。
|
72
|
+
`"kde"`, `"kwin"` に設定すると KDE/Kubuntu 環境でアプリメオ等を取得できる。Wayland 環境でも動作する。
|
72
73
|
|
73
74
|
### Rkremap#modifers
|
74
75
|
|
data/example/henkan-super.rb
CHANGED
@@ -16,8 +16,10 @@ rk.remap(hold: 0.5, map: {
|
|
16
16
|
|
17
17
|
# マウスボタンイベントはそのまま
|
18
18
|
rk.match(device: :mouse) do |event|
|
19
|
-
|
20
|
-
|
19
|
+
unless Rkremap::CODE_KEY[event.code] =~ /^KEY_/
|
20
|
+
rk.event(code: event.code, type: event.type)
|
21
|
+
event.skip
|
22
|
+
end
|
21
23
|
end
|
22
24
|
|
23
25
|
rk.start do |code, mod|
|
data/example/hoge.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rkremap'
|
2
|
+
|
3
|
+
include Rkremap::KeyCode
|
4
|
+
|
5
|
+
rk = Rkremap.new(*ARGV, mouse: false, exclude: /\AELAN0676/i)
|
6
|
+
rk.grab = true
|
7
|
+
|
8
|
+
rk.remap(KEY_CAPSLOCK => KEY_LEFTCTRL, KEY_MUHENKAN => KEY_LEFTMETA, KEY_HENKAN => KEY_RIGHTMETA)
|
9
|
+
rk.remap(KEY_LEFTMETA => [KEY_LEFTSHIFT, KEY_LEFTCTRL])
|
10
|
+
rk.match{|ev| rk.event(code: ev.code, type: ev.value); ev.skip }
|
11
|
+
rk.start
|
data/example/tmtms.rb
CHANGED
@@ -4,13 +4,9 @@ require 'rkremap'
|
|
4
4
|
|
5
5
|
include Rkremap::KeyCode
|
6
6
|
|
7
|
-
rk = Rkremap.new(*ARGV, mouse: true, exclude: /\AELAN0676/i)
|
7
|
+
rk = Rkremap.new(*ARGV, mouse: true, exclude: /\AELAN0676|\ALenovo TrackPoint Keyboard II/i)
|
8
8
|
rk.grab = true
|
9
|
-
rk.
|
10
|
-
|
11
|
-
# rkremap がシステムに認識されたら中ボタンスクロールを有効にする
|
12
|
-
sleep 0.1 until system "xinput | grep -q rkremap"
|
13
|
-
system "xinput set-prop pointer:rkremap 'libinput Scroll Method Enabled' 0 0 1"
|
9
|
+
rk.window_system = 'kde'
|
14
10
|
|
15
11
|
# HENKAN, MUHENKAN を 0.5秒以上押したら SUPER とする
|
16
12
|
rk.remap(hold: 0.5, map: {
|
@@ -20,8 +16,10 @@ rk.remap(hold: 0.5, map: {
|
|
20
16
|
|
21
17
|
# マウスボタンイベントはそのまま
|
22
18
|
rk.match(device: :mouse) do |event|
|
23
|
-
|
24
|
-
|
19
|
+
unless Rkremap::CODE_KEY[event.code] =~ /^KEY_/
|
20
|
+
rk.event(code: event.code, type: event.type)
|
21
|
+
event.skip
|
22
|
+
end
|
25
23
|
end
|
26
24
|
|
27
25
|
# CAPSLOCK は CTRL に変換
|
@@ -55,12 +53,26 @@ mod_disable_meta = {
|
|
55
53
|
}
|
56
54
|
mod_disable_all = {}.merge(mod_disable_ctrl, mod_disable_shift, mod_disable_alt, mod_disable_meta)
|
57
55
|
|
58
|
-
#
|
59
|
-
rk.match(app: /
|
56
|
+
# VirtualBox
|
57
|
+
rk.match(app: /VirtualBox Machine/) do |event|
|
60
58
|
rk.event(code: event.code, type: event.type)
|
61
59
|
event.skip
|
62
60
|
end
|
63
61
|
|
62
|
+
# Emacs
|
63
|
+
rk.match(app: {class_name: 'Emacs'}) do |event, mod|
|
64
|
+
# 修飾キーが Super だけの場合は Alt として扱う。ただしキーが TAB の場合は除く。
|
65
|
+
mod_enabled = mod.select{_2}.keys
|
66
|
+
if !mod_enabled.empty? && mod_enabled.all?{_1 == KEY_LEFTMETA || _1 == KEY_RIGHTMETA} && event.code != KEY_TAB
|
67
|
+
rk.with_modifier(mod_disable_meta.merge(KEY_LEFTALT => true)) do
|
68
|
+
rk.event(code: event.code, type: event.type)
|
69
|
+
end
|
70
|
+
else
|
71
|
+
rk.event(code: event.code, type: event.type)
|
72
|
+
end
|
73
|
+
event.skip
|
74
|
+
end
|
75
|
+
|
64
76
|
# 端末
|
65
77
|
rk.match(app: /terminal/i) do |event, mod|
|
66
78
|
# Super+Ctrl はそのまま
|
@@ -83,6 +95,16 @@ rk.match(app: /terminal/i) do |event, mod|
|
|
83
95
|
event.skip
|
84
96
|
end
|
85
97
|
|
98
|
+
# Firefox で Super+Ctrl+C は Alt+Ctrl+C に変換
|
99
|
+
rk.match(app: {class_name: 'firefox'}) do |event, mod|
|
100
|
+
if (mod[KEY_LEFTMETA] || mod[KEY_RIGHTMETA]) && (mod[KEY_LEFTCTRL] || mod[KEY_RIGHTCTRL]) && event.code == KEY_C
|
101
|
+
rk.with_modifier(mod_disable_meta.merge(KEY_LEFTCTRL => true, KEY_LEFTALT => true)) do
|
102
|
+
rk.event(code: event.code, type: event.type)
|
103
|
+
end
|
104
|
+
event.skip
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
86
108
|
# その他のウィンドウ
|
87
109
|
ctrl_map = {
|
88
110
|
KEY_A => KEY_HOME,
|
@@ -108,6 +130,11 @@ rk.start do |code, mod|
|
|
108
130
|
rk.key(code, mod.merge(mod_disable_meta, KEY_LEFTCTRL => true))
|
109
131
|
next
|
110
132
|
end
|
133
|
+
# Super+[←→] を Alt+[←→] に変換
|
134
|
+
if (mod[KEY_LEFTMETA] || mod[KEY_RIGHTMETA]) && (Rkremap::CODE_KEY[code] =~ /\AKEY_(LEFT|RIGHT)\z/)
|
135
|
+
rk.key(code, mod.merge(mod_disable_meta, KEY_LEFTALT => true))
|
136
|
+
next
|
137
|
+
end
|
111
138
|
|
112
139
|
# Alt + F, B は Ctrl + →, ← に変換
|
113
140
|
if (mod[KEY_LEFTALT] || mod[KEY_RIGHTALT]) && (code == KEY_F || code == KEY_B)
|
data/lib/rkremap/evdev.rb
CHANGED
@@ -16,6 +16,7 @@ class Rkremap
|
|
16
16
|
@capa = nil
|
17
17
|
@uinput = nil
|
18
18
|
@grab = false
|
19
|
+
io
|
19
20
|
end
|
20
21
|
|
21
22
|
def inspect
|
@@ -35,7 +36,7 @@ class Rkremap
|
|
35
36
|
# @return [String]
|
36
37
|
def name
|
37
38
|
return @name if @name
|
38
|
-
buf = ''
|
39
|
+
buf = +''
|
39
40
|
io.ioctl(EVIOCGNAME, buf)
|
40
41
|
@name = buf.sub(/\0+$/, '')
|
41
42
|
end
|
@@ -61,7 +62,7 @@ class Rkremap
|
|
61
62
|
@is_keyboard = false
|
62
63
|
return false if name =~ /\Arkremap\0*\z/
|
63
64
|
|
64
|
-
buf = ''
|
65
|
+
buf = +''
|
65
66
|
io.ioctl(EVIOCGBIT_ANY, buf)
|
66
67
|
return false if buf.unpack1('C')[EV_KEY] == 0
|
67
68
|
@is_keyboard = capable?(KeyCode::KEY_0) && capable?(KeyCode::KEY_9) &&
|
@@ -74,7 +75,7 @@ class Rkremap
|
|
74
75
|
@is_mouse = false
|
75
76
|
return false if name =~ /\Arkremap\0*\z/
|
76
77
|
|
77
|
-
buf = ''
|
78
|
+
buf = +''
|
78
79
|
io.ioctl(EVIOCGBIT_ANY, buf)
|
79
80
|
return false if buf.unpack1('C')[EV_KEY] == 0
|
80
81
|
@is_mouse = capable?(KeyCode::BTN_MOUSE)
|
data/lib/rkremap/evdev_list.rb
CHANGED
data/lib/rkremap/event.rb
CHANGED
@@ -23,11 +23,15 @@ class Rkremap
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def inspect
|
26
|
-
|
26
|
+
if ev_type == EV_KEY
|
27
|
+
"#<Rkremap::Event: #{code}(#{CODE_KEY[code]}) #{value} #{device.inspect}>"
|
28
|
+
else
|
29
|
+
"#<Rkremap::Event: type: #{ev_type}>"
|
30
|
+
end
|
27
31
|
end
|
28
32
|
|
29
33
|
def type
|
30
|
-
|
34
|
+
value == 1 ? :press : value == 0 ? :release : :repeat
|
31
35
|
end
|
32
36
|
|
33
37
|
def match?(device:, code:, type:, app:)
|
data/lib/rkremap/remap.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
class Rkremap
|
2
2
|
class Remap
|
3
|
+
def self.timer
|
4
|
+
@timer ||= Tmtms::Timer.new
|
5
|
+
end
|
6
|
+
|
3
7
|
# @param map [Hash] from => to
|
4
8
|
# @param hold [Numeric] seconds
|
5
9
|
def initialize(rk, map:, hold:)
|
@@ -26,6 +30,10 @@ class Rkremap
|
|
26
30
|
end
|
27
31
|
end
|
28
32
|
|
33
|
+
@rk.match(code: keycodes, type: :repeat) do |event|
|
34
|
+
event.skip
|
35
|
+
end
|
36
|
+
|
29
37
|
@rk.match(code: keycodes, type: :release) do |event|
|
30
38
|
if @pressed[event.code]
|
31
39
|
@rk.event(code: @pressed[event.code], type: :release)
|
@@ -39,13 +47,12 @@ class Rkremap
|
|
39
47
|
# @param hold [Numeric] seconds
|
40
48
|
def setup_holding_event(map, hold)
|
41
49
|
keycodes = map.keys.flatten
|
42
|
-
timer = Tmtms::Timer.new
|
43
50
|
jobs = map.transform_values{nil}
|
44
51
|
|
45
52
|
@rk.match(code: keycodes, type: :press) do |event|
|
46
53
|
map.each do |from, to|
|
47
54
|
if from == event.code
|
48
|
-
jobs[event.code] = timer.set(hold) do
|
55
|
+
jobs[event.code] = Remap.timer.set(hold) do
|
49
56
|
@rk.synchronize{ make_press_event(to) }
|
50
57
|
end
|
51
58
|
event.skip
|
@@ -59,12 +66,17 @@ class Rkremap
|
|
59
66
|
end
|
60
67
|
|
61
68
|
@rk.match(code: keycodes, type: :release) do |event|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
69
|
+
code = event.code
|
70
|
+
if jobs[code]
|
71
|
+
if jobs[code].cancel
|
72
|
+
event.value = 1
|
73
|
+
@rk.append_event(Event.new(event.device, event.time, event.ev_type, event.code, 0))
|
74
|
+
elsif map[code]
|
75
|
+
event.code = map[code]
|
76
|
+
@pressed[code] = nil
|
77
|
+
end
|
78
|
+
jobs[code] = nil
|
66
79
|
end
|
67
|
-
event.skip
|
68
80
|
end
|
69
81
|
|
70
82
|
@rk.match(type: :press) do
|
@@ -89,17 +101,21 @@ class Rkremap
|
|
89
101
|
# @param to [Integer, Array<Integer>]
|
90
102
|
# @param event [Rkremap::Event, nil]
|
91
103
|
def make_press_event(to, event=nil)
|
92
|
-
Array(to)
|
104
|
+
to = Array(to)
|
105
|
+
f = true
|
106
|
+
to.each do |key|
|
93
107
|
@pressed[event.code] = key if event
|
94
108
|
if @rk.modifiers.include? key
|
95
109
|
@rk.event(code: key, type: :press)
|
96
|
-
event&.skip
|
97
110
|
elsif event
|
98
111
|
event.code = key
|
112
|
+
f = false
|
99
113
|
else
|
100
|
-
@rk.key(
|
114
|
+
@rk.key(key)
|
115
|
+
f = false
|
101
116
|
end
|
102
117
|
end
|
118
|
+
event&.skip if f
|
103
119
|
end
|
104
120
|
end
|
105
121
|
end
|
data/lib/rkremap/version.rb
CHANGED
data/lib/rkremap/winattr.rb
CHANGED
@@ -1,83 +1,19 @@
|
|
1
1
|
require 'fiddle/import'
|
2
|
+
require_relative 'winattr_x11'
|
3
|
+
require_relative 'winattr_kde'
|
2
4
|
|
3
5
|
class Rkremap
|
4
6
|
# アプリの name, class, title を取得する
|
5
7
|
class WinAttr
|
6
|
-
def
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
win = X11::Window.malloc(Fiddle::RUBY_FREE)
|
15
|
-
X11.XGetInputFocus(@display, win, @buf)
|
16
|
-
win.window
|
17
|
-
end
|
18
|
-
|
19
|
-
# @return [Array<window, name, class>]
|
20
|
-
def app_win(window)
|
21
|
-
return @app_win[window] if @app_win[window]
|
22
|
-
class_hint = X11::XClassHint.malloc(Fiddle::RUBY_FREE)
|
23
|
-
parent = X11::Window.malloc(Fiddle::RUBY_FREE)
|
24
|
-
children = X11::Pointer.malloc(Fiddle::RUBY_FREE)
|
25
|
-
win = window
|
26
|
-
while win > 0
|
27
|
-
class_hint.name = nil
|
28
|
-
class_hint.class_name = nil
|
29
|
-
X11.XGetClassHint(@display, win, class_hint)
|
30
|
-
X11.XQueryTree(@display, win, @buf, parent, children, @buf)
|
31
|
-
X11.XFree(children.ptr)
|
32
|
-
break unless class_hint.name.null? && class_hint.class_name.null?
|
33
|
-
win = parent.window
|
34
|
-
end
|
35
|
-
unless class_hint.name.null?
|
36
|
-
win_name = class_hint.name.to_s
|
37
|
-
X11.XFree(class_hint.name)
|
38
|
-
end
|
39
|
-
unless class_hint.class_name.null?
|
40
|
-
win_class = class_hint.class_name.to_s
|
41
|
-
X11.XFree(class_hint.class_name)
|
8
|
+
def self.for(win_system)
|
9
|
+
case win_system.to_s
|
10
|
+
when 'x11'
|
11
|
+
WinAttrX11.new
|
12
|
+
when 'kde', 'kwin', 'kubuntu'
|
13
|
+
WinAttrKDE.new
|
14
|
+
else
|
15
|
+
raise "unknown system: #{win_system}"
|
42
16
|
end
|
43
|
-
@app_win[window] = [win, win_name, win_class]
|
44
17
|
end
|
45
|
-
|
46
|
-
# @return [String]
|
47
|
-
def app_title(window)
|
48
|
-
win = app_win(window)[0]
|
49
|
-
return '' if win == 0
|
50
|
-
prop = X11::XTextProperty.malloc(Fiddle::RUBY_FREE)
|
51
|
-
text_list = X11::Pointer.malloc(Fiddle::RUBY_FREE)
|
52
|
-
X11.XGetWMName(@display, win, prop)
|
53
|
-
X11.Xutf8TextPropertyToTextList(@display, prop, text_list, @buf)
|
54
|
-
ptr = text_list.ptr
|
55
|
-
return '' if ptr.null?
|
56
|
-
title = ptr.ptr.to_s.force_encoding('utf-8')
|
57
|
-
X11.XFreeStringList(text_list.ptr)
|
58
|
-
title
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
module X11
|
63
|
-
extend Fiddle::Importer
|
64
|
-
dlload 'libX11.so.6'
|
65
|
-
typealias 'XID', 'unsigned long'
|
66
|
-
typealias 'Window', 'XID'
|
67
|
-
typealias 'Status', 'int'
|
68
|
-
typealias 'Atom', 'unsigned long'
|
69
|
-
Window = struct ['Window window']
|
70
|
-
Pointer = struct ['void *ptr']
|
71
|
-
XClassHint = struct ['char *name', 'char *class_name']
|
72
|
-
XTextProperty = struct ['unsigned char *value', 'Atom encoding', 'int format', 'unsigned long nitems']
|
73
|
-
|
74
|
-
extern 'Display* XOpenDisplay(char*)'
|
75
|
-
extern 'int XGetInputFocus(Display*, Window*, int*)'
|
76
|
-
extern 'int XGetClassHint(Display*, Window, XClassHint*)'
|
77
|
-
extern 'Status XQueryTree(Display*, Window, Window*, Window*, Window**, unsigned int*)'
|
78
|
-
extern 'Status XGetWMName(Display*, Window, XTextProperty*)'
|
79
|
-
extern 'int Xutf8TextPropertyToTextList(Display*, XTextProperty*, char***, int*)'
|
80
|
-
extern 'int XFree(void*)'
|
81
|
-
extern 'void XFreeStringList(char**)'
|
82
18
|
end
|
83
19
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'dbus'
|
2
|
+
|
3
|
+
class Rkremap
|
4
|
+
class WinAttrKDE
|
5
|
+
attr_reader :focus_win
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@app_win = {}
|
9
|
+
|
10
|
+
bus = DBus.session_bus
|
11
|
+
kwin_service = bus.service('org.kde.KWin')
|
12
|
+
kwin_scripting = kwin_service.object('/Scripting')
|
13
|
+
kwin_scripting.introspect
|
14
|
+
full_path = File.expand_path('./kwin_script.js', __dir__)
|
15
|
+
if kwin_scripting.isScriptLoaded(full_path)[0]
|
16
|
+
kwin_scripting.unloadScript(full_path)
|
17
|
+
end
|
18
|
+
kwin_scripting.loadScript(full_path)
|
19
|
+
kwin_scripting.start
|
20
|
+
|
21
|
+
create_service
|
22
|
+
end
|
23
|
+
|
24
|
+
def class_name(window)
|
25
|
+
@app_win[window][:resource_class]
|
26
|
+
end
|
27
|
+
|
28
|
+
def app_title(win_id)
|
29
|
+
@app_win[win_id][:caption]
|
30
|
+
end
|
31
|
+
|
32
|
+
def set(id, caption, resource_name, resource_class)
|
33
|
+
@app_win[id] = {caption: caption, resource_name: resource_name, resource_class: resource_class}
|
34
|
+
@focus_win = id
|
35
|
+
end
|
36
|
+
|
37
|
+
def remove(id)
|
38
|
+
@app_win.delete id
|
39
|
+
end
|
40
|
+
|
41
|
+
def create_service
|
42
|
+
winattr = self
|
43
|
+
|
44
|
+
bus = DBus.session_bus
|
45
|
+
service = bus.request_service('net.tmtms.Rkremap')
|
46
|
+
klass = Class.new(DBus::Object) do
|
47
|
+
dbus_interface 'net.tmtms.RkremapInterface' do
|
48
|
+
dbus_method :windowActivated, 'in id:s, in caption:s, in resourceName:s, in resourceClass:s' do |id, caption, resource_name, resource_class|
|
49
|
+
winattr.set(id, caption, resource_name, resource_class)
|
50
|
+
end
|
51
|
+
dbus_method :windowRemoved, 'in id:s' do |id|
|
52
|
+
winattr.remove(id)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
obj = klass.new('/net/tmtms/Rkremap')
|
57
|
+
service.export(obj)
|
58
|
+
|
59
|
+
Thread.new do
|
60
|
+
loop = DBus::Main.new
|
61
|
+
loop << bus
|
62
|
+
loop.run
|
63
|
+
end
|
64
|
+
sleep 0.1
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'fiddle/import'
|
2
|
+
|
3
|
+
class Rkremap
|
4
|
+
# アプリの name, class, title を取得する
|
5
|
+
class WinAttrX11
|
6
|
+
def initialize
|
7
|
+
@app_win = {}
|
8
|
+
@display = X11.XOpenDisplay(nil) or raise "Cannot open display: #{ENV['DISPLAY']}"
|
9
|
+
@buf = ' '*8
|
10
|
+
end
|
11
|
+
|
12
|
+
# @return [Integer] Window
|
13
|
+
def focus_win
|
14
|
+
win = X11::Window.malloc(Fiddle::RUBY_FREE)
|
15
|
+
X11.XGetInputFocus(@display, win, @buf)
|
16
|
+
win.window
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Array<window, name, class>]
|
20
|
+
def app_win(window)
|
21
|
+
return @app_win[window] if @app_win[window]
|
22
|
+
class_hint = X11::XClassHint.malloc(Fiddle::RUBY_FREE)
|
23
|
+
parent = X11::Window.malloc(Fiddle::RUBY_FREE)
|
24
|
+
children = X11::Pointer.malloc(Fiddle::RUBY_FREE)
|
25
|
+
win = window
|
26
|
+
while win > 0
|
27
|
+
class_hint.name = nil
|
28
|
+
class_hint.class_name = nil
|
29
|
+
X11.XGetClassHint(@display, win, class_hint)
|
30
|
+
X11.XQueryTree(@display, win, @buf, parent, children, @buf)
|
31
|
+
X11.XFree(children.ptr)
|
32
|
+
break unless class_hint.name.null? && class_hint.class_name.null?
|
33
|
+
win = parent.window
|
34
|
+
end
|
35
|
+
unless class_hint.name.null?
|
36
|
+
win_name = class_hint.name.to_s
|
37
|
+
X11.XFree(class_hint.name)
|
38
|
+
end
|
39
|
+
unless class_hint.class_name.null?
|
40
|
+
win_class = class_hint.class_name.to_s
|
41
|
+
X11.XFree(class_hint.class_name)
|
42
|
+
end
|
43
|
+
@app_win[window] = [win, win_name, win_class]
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [String]
|
47
|
+
def class_name(window)
|
48
|
+
app_win(window)[2]
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [String]
|
52
|
+
def app_title(window)
|
53
|
+
win = app_win(window)[0]
|
54
|
+
return '' if win == 0
|
55
|
+
prop = X11::XTextProperty.malloc(Fiddle::RUBY_FREE)
|
56
|
+
text_list = X11::Pointer.malloc(Fiddle::RUBY_FREE)
|
57
|
+
X11.XGetWMName(@display, win, prop)
|
58
|
+
X11.Xutf8TextPropertyToTextList(@display, prop, text_list, @buf)
|
59
|
+
ptr = text_list.ptr
|
60
|
+
return '' if ptr.null?
|
61
|
+
title = ptr.ptr.to_s.force_encoding('utf-8')
|
62
|
+
X11.XFreeStringList(text_list.ptr)
|
63
|
+
title
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
module X11
|
68
|
+
extend Fiddle::Importer
|
69
|
+
|
70
|
+
dlload 'libX11.so.6'
|
71
|
+
typealias 'XID', 'unsigned long'
|
72
|
+
typealias 'Window', 'XID'
|
73
|
+
typealias 'Status', 'int'
|
74
|
+
typealias 'Atom', 'unsigned long'
|
75
|
+
Window = struct ['Window window']
|
76
|
+
Pointer = struct ['void *ptr']
|
77
|
+
XClassHint = struct ['char *name', 'char *class_name']
|
78
|
+
XTextProperty = struct ['unsigned char *value', 'Atom encoding', 'int format', 'unsigned long nitems']
|
79
|
+
|
80
|
+
extern 'Display* XOpenDisplay(char*)'
|
81
|
+
extern 'int XGetInputFocus(Display*, Window*, int*)'
|
82
|
+
extern 'int XGetClassHint(Display*, Window, XClassHint*)'
|
83
|
+
extern 'Status XQueryTree(Display*, Window, Window*, Window*, Window**, unsigned int*)'
|
84
|
+
extern 'Status XGetWMName(Display*, Window, XTextProperty*)'
|
85
|
+
extern 'int Xutf8TextPropertyToTextList(Display*, XTextProperty*, char***, int*)'
|
86
|
+
extern 'int XFree(void*)'
|
87
|
+
extern 'void XFreeStringList(char**)'
|
88
|
+
end
|
89
|
+
end
|
data/lib/rkremap.rb
CHANGED
@@ -29,6 +29,7 @@ class Rkremap
|
|
29
29
|
attr_accessor :grab
|
30
30
|
attr_accessor :modifiers
|
31
31
|
attr_accessor :x11
|
32
|
+
attr_accessor :window_system
|
32
33
|
attr_accessor :auto_detect
|
33
34
|
attr_accessor :exclude
|
34
35
|
attr_accessor :mouse
|
@@ -48,6 +49,7 @@ class Rkremap
|
|
48
49
|
@uinput = Uinput.new
|
49
50
|
@grab = false
|
50
51
|
@x11 = false
|
52
|
+
@window_system = @x11 ? 'x11' : nil # 'x11' or 'kde'
|
51
53
|
@modifiers = [
|
52
54
|
KEY_LEFTCTRL, KEY_RIGHTCTRL,
|
53
55
|
KEY_LEFTSHIFT, KEY_RIGHTSHIFT,
|
@@ -82,10 +84,12 @@ class Rkremap
|
|
82
84
|
@evdev_list.grab = @grab
|
83
85
|
@evdev_list.detect_loop
|
84
86
|
@mod_state = @modifiers.map.to_h{|m| [m, false]}
|
85
|
-
|
87
|
+
@window_system = 'x11' if @x11
|
88
|
+
winattr = WinAttr.for(@window_system) if @window_system
|
89
|
+
@events = []
|
86
90
|
while true
|
87
91
|
@keys = []
|
88
|
-
event = @evdev_list.read_event
|
92
|
+
event = @events.shift || @evdev_list.read_event
|
89
93
|
if event.ev_type != EV_KEY
|
90
94
|
if event.device.grab?
|
91
95
|
@uinput.write_event(event.ev_type, event.code, event.value)
|
@@ -133,11 +137,17 @@ class Rkremap
|
|
133
137
|
write_event(mcode, state ? 1 : 0)
|
134
138
|
end
|
135
139
|
block.call
|
136
|
-
mod_diff(mod).
|
140
|
+
mod_diff(mod).each_key do |mcode|
|
137
141
|
write_event(mcode, @mod_state[mcode] ? 1 : 0)
|
138
142
|
end
|
139
143
|
end
|
140
144
|
|
145
|
+
# @param event [Rkremap::Event]
|
146
|
+
def append_event(event)
|
147
|
+
@events.push event
|
148
|
+
@events.push Event.new(event.device, event.time, EV_SYN, SYN_REPORT, 0)
|
149
|
+
end
|
150
|
+
|
141
151
|
private
|
142
152
|
|
143
153
|
def update_modifiers(code, value)
|
@@ -179,7 +189,7 @@ class Rkremap
|
|
179
189
|
|
180
190
|
# @return [String, nil]
|
181
191
|
def class_name
|
182
|
-
@winattr.
|
192
|
+
@winattr.class_name(@win) if @winattr && @win
|
183
193
|
end
|
184
194
|
|
185
195
|
# @return [String, nil]
|
@@ -190,7 +200,7 @@ class Rkremap
|
|
190
200
|
# @param app [Array, Hash, String, Regexp]
|
191
201
|
# @return [Boolean]
|
192
202
|
def match?(app)
|
193
|
-
|
203
|
+
[app].flatten.each do |a|
|
194
204
|
a = {class_name: a, title: a} unless a.is_a? Hash
|
195
205
|
return true if match_sub(a[:class_name], class_name) || match_sub(a[:title], title)
|
196
206
|
end
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rkremap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- TOMITA Masahiro
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: tmtms_timer
|
@@ -33,6 +32,7 @@ extra_rdoc_files: []
|
|
33
32
|
files:
|
34
33
|
- README.md
|
35
34
|
- example/henkan-super.rb
|
35
|
+
- example/hoge.rb
|
36
36
|
- example/keylogger.rb
|
37
37
|
- example/mac-shortcut.rb
|
38
38
|
- example/tmtms.rb
|
@@ -45,6 +45,8 @@ files:
|
|
45
45
|
- lib/rkremap/uinput.rb
|
46
46
|
- lib/rkremap/version.rb
|
47
47
|
- lib/rkremap/winattr.rb
|
48
|
+
- lib/rkremap/winattr_kde.rb
|
49
|
+
- lib/rkremap/winattr_x11.rb
|
48
50
|
homepage: https://gitlab.com/tmtms/rkremap
|
49
51
|
licenses:
|
50
52
|
- MIT
|
@@ -53,7 +55,6 @@ metadata:
|
|
53
55
|
source_code_uri: https://gitlab.com/tmtms/rkremap
|
54
56
|
documentation_uri: https://www.rubydoc.info/gems/rkremap
|
55
57
|
rubygems_mfa_required: 'true'
|
56
|
-
post_install_message:
|
57
58
|
rdoc_options: []
|
58
59
|
require_paths:
|
59
60
|
- lib
|
@@ -68,8 +69,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
68
69
|
- !ruby/object:Gem::Version
|
69
70
|
version: '0'
|
70
71
|
requirements: []
|
71
|
-
rubygems_version: 3.
|
72
|
-
signing_key:
|
72
|
+
rubygems_version: 3.7.0.dev
|
73
73
|
specification_version: 4
|
74
74
|
summary: key remapper
|
75
75
|
test_files: []
|