rkremap 0.1.0 → 0.3.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: 945483dcd11240d5c54b359a15055219532f2f311275633503e7d485aa68917b
4
- data.tar.gz: d594c9b549696f964cde750094169ba7198c0ac9ab5f5dfc59a7936d9610a09d
3
+ metadata.gz: 04a75325e18cfa14e1eeb93462af309c6d692083c5ef4a2609e6dfd53747a6c8
4
+ data.tar.gz: 1f86fc05c3098eed25b0d09bbebfc75fdad37122898ee9762376ce5241f21c60
5
5
  SHA512:
6
- metadata.gz: a1412a373a0673172a9eba0449613ab9a76debf400b622977887031c294f8381a58a7c060ef6451e6264703690974aeddb305ce35619c07958a2b5a1239ecf12
7
- data.tar.gz: 619ee1de0814c2542c2a4f347dca7c48e640590a01ff58f2b7362ed80ef447acaf1576df2a2a659eaac95c9a7ec0a8f52d732d8024356613fbef43f055b16dd2
6
+ metadata.gz: 3874c3c1a1c9b78a82faf4c3ceb40f28e1d9d5f1380a8ac3f604ffc904f2dcb4099db5b84f2f9542ae4d104ac8f242f4edd8060c8da0aa4f65e8ddc472a4cab2
7
+ data.tar.gz: f9700cd0991fb27fa03de2a402dd1bfebe0f23cc35d128b5b35ca9115608988ce2f1aeea55cd8525a3c645e1b4a465b0aa0995703e4cb0b58f73bdb7e2efda3f
data/README.md CHANGED
@@ -25,7 +25,9 @@ KERNEL=="uinput", GROUP="input"
25
25
 
26
26
  ### `Rkremap.new`
27
27
 
28
- 引数に `/dev/input/event*` を指定する。キーボードデバイス以外を指定してもたぶん無意味。
28
+ 引数を指定しなければ自動的にキーボードデバイスの検出を試みる。
29
+
30
+ 引数を指定する場合は `/dev/input/event*` を指定する。キーボードデバイス以外を指定してもたぶん無意味。
29
31
  キーボードのデバイスファイル名を調べるには
30
32
  ```
31
33
  cat /proc/bus/input/devices
@@ -46,6 +48,15 @@ ThinkPad 本体のキーボードは `/dev/input/event3` だった。
46
48
 
47
49
  `true` に設定すると X のアプリ名等を取得できる。X 環境下でないなら `false` を設定する。
48
50
 
51
+ ### Rkremap#modifers
52
+
53
+ 修飾キーの一覧。これらのキーは単体で押されただけでは `start` ブロックを実行しない。
54
+
55
+ ### auto_detect
56
+
57
+ `true` の場合はキーボードデバイスの接続を自動検知する。
58
+ `new` 時にデバイスファイルが指定されない場合は `true` になる。
59
+
49
60
  ### Rkremap#start
50
61
 
51
62
  なにかキーを押されたらブロックを実行する。修飾キー単体ではブロックは実行されない。
@@ -79,6 +90,7 @@ ThinkPad 本体のキーボードは `/dev/input/event3` だった。
79
90
  * [xkeysnail - もうひとつの Linux 向けキーリマッパ - Qiita](https://qiita.com/mooz@github/items/c5f25f27847333dd0b37)
80
91
  * [k0kubun/xremap: Dynamic key remapper for X11 and Wayland](https://github.com/k0kubun/xremap)
81
92
  * [Linux向けの最強のキーリマッパーを作った - k0kubun's blog](https://k0kubun.hatenablog.com/entry/xkremap)
93
+ * [Linux用キーリマッパーxremapをRustで書き直した - k0kubun's blog](https://k0kubun.hatenablog.com/entry/xremap)
82
94
  * [kui/rbindkeys: key remap with ruby](https://github.com/kui/rbindkeys)
83
95
  * [Ruby で設定できる Linux 環境向けキーリマッパー作った — 電卓片手に](http://k-ui.jp/blog/2012/06/06/rbindkeys-configurable-key-remapper-in-ruby/)
84
96
  * [Linux の入力デバイスをカスタマイズ - Qiita](https://qiita.com/propella/items/a73fc333c95d14d06835)
@@ -4,7 +4,7 @@
4
4
  require 'rkremap'
5
5
  include Rkremap::KeyCode
6
6
 
7
- rk = Rkremap.new('/dev/input/event3')
7
+ rk = Rkremap.new
8
8
  rk.grab = true
9
9
 
10
10
  class State
data/example/keylogger.rb CHANGED
@@ -6,7 +6,7 @@ def code2key(code)
6
6
  Rkremap::CODE_KEY[code].to_s.sub(/\AKEY_/, '')
7
7
  end
8
8
 
9
- rk = Rkremap.new('/dev/input/event3')
9
+ rk = Rkremap.new
10
10
  rk.grab = false
11
11
  rk.x11 = true
12
12
  rk.start do |code, mod, app|
@@ -33,7 +33,7 @@ mod_disable_meta = {
33
33
  }
34
34
  mod_disable_all = {}.merge(mod_disable_ctrl, mod_disable_shift, mod_disable_alt, mod_disable_meta)
35
35
 
36
- rk = Rkremap.new("/dev/input/event3")
36
+ rk = Rkremap.new
37
37
  rk.grab = true
38
38
  rk.x11 = true
39
39
  rk.start do |code, mod, app|
data/example/tmtms.rb CHANGED
@@ -1,14 +1,4 @@
1
1
  # tmtms 用
2
- #
3
- # fcitx が有効な場合に /tmp/fcitx-enabled ファイルを作るスクリプトを動かしておく:
4
- # while :; do
5
- # if [ $(fcitx-remote) -eq 2 ]; then
6
- # touch /tmp/fcitx-enabled
7
- # else
8
- # rm -f /tmp/fcitx-enabled
9
- # fi
10
- # sleep 0.1
11
- # done
12
2
 
13
3
  require 'rkremap'
14
4
  include Rkremap::KeyCode
@@ -40,7 +30,7 @@ class State
40
30
  end
41
31
  end
42
32
 
43
- rk = Rkremap.new("/dev/input/event3")
33
+ rk = Rkremap.new
44
34
  rk.grab = true
45
35
  rk.x11 = true
46
36
 
@@ -159,8 +149,13 @@ rk.start do |code, mod, app|
159
149
 
160
150
  # Ctrl は他のキーに変換
161
151
  if mod[KEY_LEFTCTRL] || mod[KEY_RIGHTCTRL]
162
- # Ctrl+K は行末まで削除 (日本語変換時はそのまま)
163
- if code == KEY_K && !File.exist?('/tmp/fcitx-enabled')
152
+ # Ctrl+I/O はそのまま
153
+ if code == KEY_I || code ==KEY_O
154
+ rk.key(code, mod)
155
+ next
156
+ end
157
+ # Ctrl+K は行末まで削除
158
+ if code == KEY_K
164
159
  rk.key(KEY_END, mod_disable_all.merge({KEY_LEFTSHIFT => true}))
165
160
  rk.key(KEY_X, mod_disable_all.merge({KEY_LEFTCTRL => true}))
166
161
  next
@@ -0,0 +1,154 @@
1
+ class Rkremap
2
+ EV_KEY = 1
3
+ EV_SYN = 0
4
+ SYN_REPORT = 0
5
+
6
+ # https://www.kernel.org/doc/html/v4.12/input/input_uapi.html
7
+ class Evdev
8
+ EVIOCGBIT_ANY = 2147566880 # EVIOCGBIT(0, 1)
9
+ EVIOCGBIT_EV_KEY = 2153792801 # EVIOCGBIT(EV_KEY, (KEY_MAX-1)/8+1)
10
+ EVIOCGRAB = 1074021776
11
+ EVIOCGNAME = 2164278534 # EVIOCGNAME(256)
12
+
13
+ @io2evdev = {}
14
+ def self.io2evdev
15
+ @io2evdev
16
+ end
17
+
18
+ @device_list = {}
19
+
20
+ # @return [Array<Evdev>]
21
+ def self.detect
22
+ devs = []
23
+ new_devs = Dir.children("/dev/input").map do |dev|
24
+ if dev =~ /\Aevent\d+\z/
25
+ path = "/dev/input/#{dev}"
26
+ devs.push path
27
+ next if @device_list[path]
28
+ @device_list[path] = true
29
+ Evdev.new(path)
30
+ end
31
+ end.compact
32
+ (@device_list.keys - devs).each do |path|
33
+ @device_list.delete path
34
+ end
35
+ new_devs
36
+ end
37
+
38
+ def self.remove_device(evdev)
39
+ @io2evdev.delete evdev.io
40
+ @device_list.delete evdev.path
41
+ end
42
+
43
+ # @param inputs [Array<Evdev>]
44
+ # @param timeout [Numeric]
45
+ # @return [Evdev]
46
+ def self.select(inputs, timeout)
47
+ r, = IO.select(inputs.map(&:io), nil, nil, timeout)
48
+ r && @io2evdev[r[0]]
49
+ end
50
+
51
+ # @return [String]
52
+ attr_reader :path
53
+
54
+ # @param path [String]
55
+ def initialize(path)
56
+ @path = path
57
+ @io = nil
58
+ @capa = nil
59
+ @uinput = nil
60
+ end
61
+
62
+ def eql?(other)
63
+ other.is_a?(Evdev) && self.path == other.path
64
+ end
65
+
66
+ # @return [IO]
67
+ def io
68
+ return @io if @io
69
+ @io = File.open(@path)
70
+ self.class.io2evdev[@io] = self
71
+ @io
72
+ end
73
+
74
+ def close
75
+ @io&.close
76
+ self.class.remove_device(self)
77
+ end
78
+
79
+ # @param key [Integer]
80
+ # @return [Boolean]
81
+ def capable?(key)
82
+ unless @capa
83
+ buf = ' ' * ((KeyCode::KEY_MAX-1)/8+1)
84
+ io.ioctl(EVIOCGBIT_EV_KEY, buf)
85
+ @capa = buf.unpack('C*')
86
+ end
87
+ @capa[key/8][key%8] != 0
88
+ end
89
+
90
+ # @return [Boolean]
91
+ def keyboard?
92
+ buf = ' '
93
+ io.ioctl(EVIOCGNAME, buf)
94
+ return false if buf =~ /\Arkremap\0*\z/
95
+
96
+ io.ioctl(EVIOCGBIT_ANY, buf)
97
+ return false if buf.unpack1('C')[EV_KEY] == 0
98
+ capable?(KeyCode::KEY_0) && capable?(KeyCode::KEY_9) &&
99
+ capable?(KeyCode::KEY_A) && capable?(KeyCode::KEY_Z) && capable?(KeyCode::KEY_SPACE)
100
+ end
101
+
102
+ def grab
103
+ io.ioctl(EVIOCGRAB, 1)
104
+ end
105
+
106
+ # struct input_event {
107
+ # struct timeval time;
108
+ # unsigned short type;
109
+ # unsigned short code;
110
+ # unsigned int value;
111
+ # };
112
+ # @return [Array<Time, Integer, Integer, Integer>]
113
+ def read_event
114
+ raw = io.sysread(24) # sizeof(struct input_event)
115
+ sec, usec, type, code, value = raw.unpack('Q!Q!SSl')
116
+ time = Time.at(sec, usec)
117
+ return time, type, code, value
118
+ end
119
+ end
120
+
121
+ # https://www.kernel.org/doc/html/v4.12/input/uinput.html
122
+ class Uinput
123
+ UI_SET_EVBIT = 1074025828
124
+ UI_SET_KEYBIT = 1074025829
125
+ UI_DEV_SETUP = 1079792899
126
+ UI_DEV_CREATE = 21761
127
+
128
+ def initialize
129
+ @dev = File.open('/dev/uinput', 'w')
130
+ setup
131
+ end
132
+
133
+ def setup
134
+ @dev.ioctl(UI_SET_EVBIT, EV_KEY)
135
+ KeyCode::KEY_CNT.times{|k| @dev.ioctl(UI_SET_KEYBIT, k)}
136
+ bustype = 0x03 # BUS_USB
137
+ vendor = 0x1234 # てきとー
138
+ product = 0x5678 # てきとー
139
+ version = 1 # てきとー
140
+ name = 'rkremap'
141
+ ff_efects_max = 0
142
+ setup = [bustype, vendor, product, version, name, ff_efects_max].pack("SSSSZ80L") # struct uinput_setup
143
+ @dev.ioctl(UI_DEV_SETUP, setup)
144
+ @dev.ioctl(UI_DEV_CREATE)
145
+ end
146
+
147
+ # @param type [Integer] EV_KEY/ EV_SYN
148
+ # @param code [Integer] キーコード / SYN_REPORT
149
+ # @param value [Integer] 0:release / 1:press / 2:repeat
150
+ def write_event(type, code, value)
151
+ @dev.syswrite(['', type, code, value].pack('a16SSl'))
152
+ end
153
+ end
154
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Rkremap
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -1,12 +1,4 @@
1
- require 'ffi'
2
-
3
- module FFI
4
- typedef :pointer, :Display
5
- typedef :ulong, :XID
6
- typedef :XID, :Window
7
- typedef :int, :Status
8
- typedef :ulong, :Atom
9
- end
1
+ require 'fiddle/import'
10
2
 
11
3
  class Rkremap
12
4
  # アプリの name, class, title を取得する
@@ -14,38 +6,39 @@ class Rkremap
14
6
  def initialize
15
7
  @app_win = {}
16
8
  @display = X11.XOpenDisplay(nil) or raise "Cannot open display: #{ENV['DISPLAY']}"
9
+ @buf = ' '*8
17
10
  end
18
11
 
12
+ # @return [Integer] Window
19
13
  def focus_win
20
- win = FFI::MemoryPointer.new(:Window)
21
- revert_to = FFI::MemoryPointer.new(:int)
22
- X11.XGetInputFocus(@display, win, revert_to)
23
- win.read(:Window)
14
+ win = X11::Window.malloc(Fiddle::RUBY_FREE)
15
+ X11.XGetInputFocus(@display, win, @buf)
16
+ win.window
24
17
  end
25
18
 
26
19
  # @return [Array<window, name, class>]
27
20
  def app_win(window)
28
21
  return @app_win[window] if @app_win[window]
29
- class_hint = XClassHint.new
30
- root = FFI::MemoryPointer.new(:Window)
31
- parent = FFI::MemoryPointer.new(:Window)
32
- children = FFI::MemoryPointer.new(:pointer)
33
- nchildren = FFI::MemoryPointer.new(:int)
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)
34
25
  win = window
35
26
  while win > 0
27
+ class_hint.name = nil
28
+ class_hint.class_name = nil
36
29
  X11.XGetClassHint(@display, win, class_hint)
37
- X11.XQueryTree(@display, win, root, parent, children, nchildren)
38
- X11.XFree(children.read_pointer)
39
- break unless class_hint[:name].null? && class_hint[:class].null?
40
- win = parent.read(:Window)
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
41
34
  end
42
- unless class_hint[:name].null?
43
- win_name = class_hint[:name].read_string
44
- X11.XFree(class_hint[:name])
35
+ unless class_hint.name.null?
36
+ win_name = class_hint.name.to_s
37
+ X11.XFree(class_hint.name)
45
38
  end
46
- unless class_hint[:class].null?
47
- win_class = class_hint[:class].read_string
48
- X11.XFree(class_hint[:class])
39
+ unless class_hint.class_name.null?
40
+ win_class = class_hint.class_name.to_s
41
+ X11.XFree(class_hint.class_name)
49
42
  end
50
43
  @app_win[window] = [win, win_name, win_class]
51
44
  end
@@ -53,43 +46,35 @@ class Rkremap
53
46
  # @return [String]
54
47
  def app_title(window)
55
48
  win = app_win(window)[0]
56
- prop = XTextProperty.new
57
- text_list = FFI::MemoryPointer.new(:pointer)
58
- count = FFI::MemoryPointer.new(:int)
49
+ prop = X11::XTextProperty.malloc(Fiddle::RUBY_FREE)
50
+ text_list = X11::Pointer.malloc(Fiddle::RUBY_FREE)
59
51
  X11.XGetWMName(@display, win, prop)
60
- X11.Xutf8TextPropertyToTextList(@display, prop, text_list, count)
61
- title = text_list.read(:pointer).read(:string)&.force_encoding('utf-8')
62
- X11.XFreeStringList(text_list.read(:pointer))
52
+ X11.Xutf8TextPropertyToTextList(@display, prop, text_list, @buf)
53
+ title = text_list.ptr.ptr.to_s.force_encoding('utf-8')
54
+ X11.XFreeStringList(text_list.ptr)
63
55
  title
64
56
  end
65
57
  end
66
58
 
67
- class XClassHint < FFI::Struct
68
- layout(
69
- :name, :pointer,
70
- :class, :pointer,
71
- )
72
- end
73
-
74
- class XTextProperty < FFI::Struct
75
- layout(
76
- :value, :pointer,
77
- :encoding, :Atom,
78
- :format, :int,
79
- :nitems, :ulong,
80
- )
81
- end
82
-
83
59
  module X11
84
- extend FFI::Library
85
- ffi_lib 'X11'
86
- attach_function :XOpenDisplay, [:string], :Display
87
- attach_function :XGetInputFocus, [:Display, :pointer, :pointer], :int
88
- attach_function :XGetClassHint, [:Display, :Window, XClassHint.by_ref], :int
89
- attach_function :XQueryTree, [:Display, :Window, :pointer, :pointer, :pointer, :pointer], :Status
90
- attach_function :XGetWMName, [:Display, :Window, XTextProperty.by_ref], :void
91
- attach_function :Xutf8TextPropertyToTextList, [:Display, XTextProperty.by_ref, :pointer, :pointer], :int
92
- attach_function :XFree, [:pointer], :int
93
- attach_function :XFreeStringList, [:pointer], :void
60
+ extend Fiddle::Importer
61
+ dlload 'libX11.so.6'
62
+ typealias 'XID', 'unsigned long'
63
+ typealias 'Window', 'XID'
64
+ typealias 'Status', 'int'
65
+ typealias 'Atom', 'unsigned long'
66
+ Window = struct ['Window window']
67
+ Pointer = struct ['void *ptr']
68
+ XClassHint = struct ['char *name', 'char *class_name']
69
+ XTextProperty = struct ['unsigned char *value', 'Atom encoding', 'int format', 'unsigned long nitems']
70
+
71
+ extern 'Display* XOpenDisplay(char*)'
72
+ extern 'int XGetInputFocus(Display*, Window*, int*)'
73
+ extern 'int XGetClassHint(Display*, Window, XClassHint*)'
74
+ extern 'Status XQueryTree(Display*, Window, Window*, Window*, Window**, unsigned int*)'
75
+ extern 'Status XGetWMName(Display*, Window, XTextProperty*)'
76
+ extern 'int Xutf8TextPropertyToTextList(Display*, XTextProperty*, char***, int*)'
77
+ extern 'int XFree(void*)'
78
+ extern 'void XFreeStringList(char**)'
94
79
  end
95
80
  end
data/lib/rkremap.rb CHANGED
@@ -3,20 +3,11 @@
3
3
  require_relative "rkremap/version"
4
4
  require_relative 'rkremap/keycode'
5
5
  require_relative 'rkremap/winattr'
6
+ require_relative 'rkremap/evdev'
6
7
 
7
8
  class Rkremap
8
9
  include KeyCode
9
10
 
10
- # ioctl constants
11
- EVIOCGRAB = 1074021776
12
- UI_SET_EVBIT = 1074025828
13
- UI_SET_KEYBIT = 1074025829
14
- UI_DEV_SETUP = 1079792899
15
- UI_DEV_CREATE = 21761
16
- EV_KEY = 1
17
- EV_SYN = 0
18
- SYN_REPORT = 0
19
-
20
11
  EVENT_TYPE_VALUE = {
21
12
  release: 0,
22
13
  press: 1,
@@ -26,12 +17,19 @@ class Rkremap
26
17
  attr_accessor :grab
27
18
  attr_accessor :modifiers
28
19
  attr_accessor :x11
29
-
30
- # @param device [String]
31
- def initialize(device)
32
- @input = File.open(device)
33
- @uinput = File.open('/dev/uinput', 'w')
34
- setup_uinput
20
+ attr_accessor :auto_detect
21
+
22
+ # @param devices [Array<String>, String]
23
+ def initialize(devices=nil)
24
+ if devices
25
+ devices = Array(devices)
26
+ @inputs = devices.map{|d| Evdev.new(d)}
27
+ else
28
+ @auto_detect = true
29
+ @inputs = Evdev.detect.select(&:keyboard?)
30
+ end
31
+ raise 'Unable to detect keyboard device' if @inputs.empty?
32
+ @uinput = Uinput.new
35
33
  @grab = false
36
34
  @x11 = false
37
35
  @modifiers = [
@@ -51,15 +49,14 @@ class Rkremap
51
49
  end
52
50
 
53
51
  def start(&block)
54
- @mod_state = @modifiers.map{|m| [m, false]}.to_h
52
+ detect_device_loop if @auto_detect
53
+ @mod_state = @modifiers.map.to_h{|m| [m, false]}
55
54
  @winattr = WinAttr.new if @x11
56
- @input.ioctl(EVIOCGRAB, 1) if @grab
55
+ @inputs.each(&:grab) if @grab
57
56
  while true
58
57
  @keys = []
59
- raw = @input.sysread(24)
60
- sec, usec, type, code, value = raw.unpack('Q!Q!SSl')
58
+ time, type, code, value = read_event
61
59
  next if type != EV_KEY
62
- time = Time.at(sec, usec)
63
60
  event = Event.new(time, code, value, @winattr)
64
61
  @events.each do |cond, b|
65
62
  synchronize{ b.call event } if event.match?(**cond)
@@ -73,6 +70,32 @@ class Rkremap
73
70
  end
74
71
  end
75
72
 
73
+ def detect_device_loop
74
+ Thread.new do
75
+ while true
76
+ sleep 3
77
+ new_devs = Evdev.detect.select(&:keyboard?)
78
+ unless new_devs.empty?
79
+ new_devs.each(&:grab) if @grab
80
+ @inputs += new_devs
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ def read_event
87
+ while true
88
+ begin
89
+ input = Evdev.select(@inputs, 3)
90
+ next unless input
91
+ return input.read_event
92
+ rescue Errno::ENODEV
93
+ input.close rescue nil
94
+ @inputs.delete input
95
+ end
96
+ end
97
+ end
98
+
76
99
  def synchronize(&block)
77
100
  @mutex.synchronize(&block)
78
101
  end
@@ -100,20 +123,6 @@ class Rkremap
100
123
 
101
124
  private
102
125
 
103
- def setup_uinput
104
- @uinput.ioctl(UI_SET_EVBIT, EV_KEY)
105
- KEY_CNT.times{|k| @uinput.ioctl(UI_SET_KEYBIT, k)}
106
- bustype = 0x03 # BUS_USB
107
- vendor = 0x1234 # てきとー
108
- product = 0x5678 # てきとー
109
- version = 1 # てきとー
110
- name = 'rkremap'
111
- ff_efects_max = 0
112
- setup = [bustype, vendor, product, version, name, ff_efects_max].pack("SSSSZ80L") # struct uinput_setup
113
- @uinput.ioctl(UI_DEV_SETUP, setup)
114
- @uinput.ioctl(UI_DEV_CREATE)
115
- end
116
-
117
126
  def proc_event(code, value, event)
118
127
  if @mod_state.include?(code)
119
128
  @mod_state[code] = value != 0
@@ -130,8 +139,8 @@ class Rkremap
130
139
  # @param code [Integer]
131
140
  # @param value [Integer] 0:release / 1:press / 2:repeat
132
141
  def write_event(code, value)
133
- @uinput.syswrite(['', EV_KEY, code, value].pack('a16SSl'))
134
- @uinput.syswrite(['', EV_SYN, SYN_REPORT, 0].pack('a16SSl'))
142
+ @uinput.write_event(EV_KEY, code, value)
143
+ @uinput.write_event(EV_SYN, SYN_REPORT, 0)
135
144
  end
136
145
 
137
146
  class Event
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rkremap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - TOMITA Masahiro
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-01-10 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: ffi
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '1.0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '1.0'
11
+ date: 2022-03-05 00:00:00.000000000 Z
12
+ dependencies: []
27
13
  description: key remapper
28
14
  email:
29
15
  - tommy@tmtm.org
@@ -37,6 +23,7 @@ files:
37
23
  - example/mac-shortcut.rb
38
24
  - example/tmtms.rb
39
25
  - lib/rkremap.rb
26
+ - lib/rkremap/evdev.rb
40
27
  - lib/rkremap/keycode.rb
41
28
  - lib/rkremap/version.rb
42
29
  - lib/rkremap/winattr.rb