lib/fusuma/plugin/sendkey/keyboard.rb in fusuma-plugin-sendkey-0.2.1 vs lib/fusuma/plugin/sendkey/keyboard.rb in fusuma-plugin-sendkey-0.3.0
- old
+ new
@@ -1,7 +1,9 @@
# frozen_string_literal: true
+require 'linux_input'
+require 'fcntl'
require_relative './device.rb'
module Fusuma
module Plugin
module Sendkey
@@ -10,44 +12,103 @@
def initialize(device: nil)
@device = device || Device.new
end
# @param param [String]
- def type_command(param:)
+ def type(param:)
return unless param.is_a?(String)
- codes = param.split('+')
- press_commands = codes.map { |code| press_command(code) }
- release_commands = codes.reverse.map { |code| release_command(code) }
+ keycodes = split_param(param)
- (press_commands | release_commands).join(' && ')
+ clear_modifiers
+ keycodes.each { |keycode| key_event(keycode: keycode, press: true) }
+ key_sync(press: true)
+ keycodes.reverse.map { |keycode| key_event(keycode: keycode, press: false) }
+ key_sync(press: false)
+ device_file.close
end
- def press_command(code)
- return unless support?(code)
+ # @param param [String]
+ def valid?(param:)
+ return unless param.is_a?(String)
- @device.emulate(code: code, press: true)
+ keycodes = split_param(param)
+ keycodes.all? { |keycode| support?(keycode) }
end
- def release_command(code)
- return unless support?(code)
+ def device_file
+ return @device_file if @device_file && !@device_file.closed?
- @device.emulate(code: code, press: false)
+ @device_file = File.open(@device.path, Fcntl::O_WRONLY | Fcntl::O_NDELAY)
end
- def support?(code)
+ def key_event(keycode:, press: true)
+ event = LinuxInput::InputEvent.new
+ event[:time] = LinuxInput::Timeval.new
+ event[:time][:tv_sec] = Time.now.to_i
+ event[:type] = LinuxInput::EV_KEY
+ event[:code] = keycode_const(keycode)
+ event[:value] = press ? 1 : 0
+ device_file.syswrite(event.pointer.read_bytes(event.size))
+ end
+
+ def key_sync(press: true)
+ event = LinuxInput::InputEvent.new
+ event[:time] = LinuxInput::Timeval.new
+ event[:time][:tv_sec] = Time.now.to_i
+ event[:type] = LinuxInput::EV_SYN
+ event[:code] = LinuxInput::SYN_REPORT
+ event[:value] = press ? 1 : 0
+ device_file.syswrite(event.pointer.read_bytes(event.size))
+ end
+
+ def support?(keycode)
@supported_code ||= {}
- @supported_code[code] ||= if @device.support?(code)
- true
- else
- warn "sendkey: #{code} is unsupported."
- warn 'Please check your config.yml.'
- exit 1
- end
+ @supported_code[keycode] ||= if @device.support?(keycode)
+ true
+ else
+ search_candidates(keycode: keycode)
+ exit(1)
+ end
end
- def available_codes
- @device.search_codes
+ def search_candidates(keycode:)
+ candidates = search_codes(keycode: keycode)
+
+ warn "Did you mean? #{candidates.join(' / ')}" unless candidates.empty?
+ warn "sendkey: #{remove_prefix(keycode)} is unsupported."
+ end
+
+ def search_codes(keycode: nil)
+ query = keycode&.upcase&.gsub('KEY_', '')
+ LinuxInput.constants
+ .select { |c| c[/KEY_.*#{query}.*/] }
+ .select { |c| @device.support?(c) }
+ .map { |c| c.to_s.gsub('KEY_', '') }
+ end
+
+ def keycode_const(keycode)
+ Object.const_get "LinuxInput::#{keycode}"
+ end
+
+ def clear_modifiers
+ modifiers = %w[ CAPSLOCK LEFTALT LEFTCTRL LEFTMETA
+ LEFTSHIFT RIGHTALT RIGHTCTRL RIGHTSHIFT ]
+ modifiers.each { |code| key_event(keycode: key_prefix(code), press: false) }
+ end
+
+ private
+
+ def split_param(param)
+ param.split('+').map { |code| key_prefix(code) }
+ end
+
+ def key_prefix(code)
+ "KEY_#{code.upcase}"
+ end
+
+ def remove_prefix(keycode)
+ keycode.gsub('KEY_', '')
end
end
end
end
end