rkremap 0.5.1 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 93f0a2d7d2c705b779d942618d4da708ef395386bc9529d06cc4c4f644243bac
4
- data.tar.gz: bddb932d29c287b3e00105adac03fc420506253c5f00c309dee22da46678502f
3
+ metadata.gz: 590e1e170524210ac70556095262d1cda5a7a94330d2aaa2927a644a96a431d0
4
+ data.tar.gz: 97f670607504cac0b34b8e128711eabf8f8a7000edf122350deaf7409d481315
5
5
  SHA512:
6
- metadata.gz: 41fff4f8444a6c585403ac5a3655eb5b4e1d631332594c95e4d3149a973dced926b4a726cf912522e7ac3c0dcbb4ba0e3ea6461dfe6853e04c1a9086162307e2
7
- data.tar.gz: 88b6bdb97c04ed2629d1eac337e89f9854f954daf392b4e0a395e6df94699ff1c7e7e66be78ea3adeb3221fff8a6a88d2d8a38699f503575c5376bbdfcf96556
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#x11
69
+ ### Rkremap#window_system
70
70
 
71
- `true` に設定すると X のアプリ名等を取得できる。X 環境下でないなら `false` を設定する。
71
+ `"x11"` に設定すると X のアプリ名等を取得できる。Wayland 環境では動かない。
72
+ `"kde"`, `"kwin"` に設定すると KDE/Kubuntu 環境でアプリメオ等を取得できる。Wayland 環境でも動作する。
72
73
 
73
74
  ### Rkremap#modifers
74
75
 
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.x11 = true
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: {
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)
@@ -56,6 +56,8 @@ class Rkremap
56
56
  stat = 'ignored'
57
57
  end
58
58
  puts "detect: #{ev.name} (#{ev.path}) #{stat}" if $VERBOSE
59
+ rescue => e
60
+ warn "#{e}: #{path}"
59
61
  end
60
62
  end
61
63
 
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:)
@@ -43,13 +47,12 @@ class Rkremap
43
47
  # @param hold [Numeric] seconds
44
48
  def setup_holding_event(map, hold)
45
49
  keycodes = map.keys.flatten
46
- timer = Tmtms::Timer.new
47
50
  jobs = map.transform_values{nil}
48
51
 
49
52
  @rk.match(code: keycodes, type: :press) do |event|
50
53
  map.each do |from, to|
51
54
  if from == event.code
52
- jobs[event.code] = timer.set(hold) do
55
+ jobs[event.code] = Remap.timer.set(hold) do
53
56
  @rk.synchronize{ make_press_event(to) }
54
57
  end
55
58
  event.skip
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Rkremap
4
- VERSION = "0.5.1"
4
+ VERSION = "0.6.0"
5
5
  end
@@ -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 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)
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,7 +84,8 @@ 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
- winattr = WinAttr.new if @x11
87
+ @window_system = 'x11' if @x11
88
+ winattr = WinAttr.for(@window_system) if @window_system
86
89
  @events = []
87
90
  while true
88
91
  @keys = []
@@ -134,7 +137,7 @@ class Rkremap
134
137
  write_event(mcode, state ? 1 : 0)
135
138
  end
136
139
  block.call
137
- mod_diff(mod).each do |mcode, _|
140
+ mod_diff(mod).each_key do |mcode|
138
141
  write_event(mcode, @mod_state[mcode] ? 1 : 0)
139
142
  end
140
143
  end
@@ -186,7 +189,7 @@ class Rkremap
186
189
 
187
190
  # @return [String, nil]
188
191
  def class_name
189
- @winattr.app_win(@win)[2] if @winattr && @win
192
+ @winattr.class_name(@win) if @winattr && @win
190
193
  end
191
194
 
192
195
  # @return [String, nil]
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.5.1
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: 2023-09-24 00:00:00.000000000 Z
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.5.0.dev
72
- signing_key:
72
+ rubygems_version: 3.7.0.dev
73
73
  specification_version: 4
74
74
  summary: key remapper
75
75
  test_files: []