Class: Fusuma::Plugin::Sendkey::Keyboard

Inherits:
Object
  • Object
show all
Defined in:
lib/fusuma/plugin/sendkey/keyboard.rb

Overview

Emulate Keyboard

Constant Summary collapse

KEY_INTERVAL =
0.03
MODIFIER_KEY_CODES =
%w[
  KEY_CAPSLOCK
  KEY_LEFTALT
  KEY_LEFTCTRL
  KEY_LEFTMETA
  KEY_LEFTSHIFT
  KEY_RIGHTALT
  KEY_RIGHTCTRL
  KEY_RIGHTSHIFT
  KEY_RIGHTMETA
].freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(device:) ⇒ Keyboard

Returns a new instance of Keyboard.



47
48
49
# File 'lib/fusuma/plugin/sendkey/keyboard.rb', line 47

def initialize(device:)
  @device = device
end

Class Method Details

.find_device(name_patterns:) ⇒ Object



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/fusuma/plugin/sendkey/keyboard.rb', line 27

def self.find_device(name_patterns:)
  Fusuma::Device.reset

  Array(name_patterns).each do |name_pattern|
    fusuma_device = Fusuma::Device.all.find { |d|
      next unless d.capabilities.include?("keyboard")

      d.name.match(/#{name_pattern}/)
    }

    if fusuma_device
      MultiLogger.info "sendkey: Keyboard: #{fusuma_device.name}"
      return Device.new(path: "/dev/input/#{fusuma_device.id}")
    end
    warn "sendkey: Keyboard: /#{name_pattern}/ is not found"
  end

  exit(1)
end

Instance Method Details

#capabilitiesObject



134
135
136
137
138
139
140
141
142
143
# File 'lib/fusuma/plugin/sendkey/keyboard.rb', line 134

def capabilities
  return @capabilities if defined?(@capabilities)

  @capabilities = Set.new.tap do |set|
    @device.reload_capability.each do |id|
      code_sym = Revdev::REVERSE_MAPS[:KEY][id]
      set << code_sym if code_sym
    end
  end
end

#clear_modifiers(keycodes) ⇒ Object

Parameters:

  • keycodes (Array<String>)

    to be released



174
175
176
# File 'lib/fusuma/plugin/sendkey/keyboard.rb', line 174

def clear_modifiers(keycodes)
  keycodes.each { |code| send_event(code: code, press: false) }
end

#find_code(code) ⇒ Object



162
163
164
165
166
167
# File 'lib/fusuma/plugin/sendkey/keyboard.rb', line 162

def find_code(code)
  result = capabilities.find { |c| c == code.to_sym }

  warn_undefined_codes(code) unless result
  result
end

#key_syncObject



124
125
126
127
128
129
130
131
132
# File 'lib/fusuma/plugin/sendkey/keyboard.rb', line 124

def key_sync
  event = Revdev::InputEvent.new(
    nil,
    Revdev.const_get(:EV_SYN),
    Revdev.const_get(:SYN_REPORT),
    0
  )
  @device.write_event(event)
end

#keycode_const(keycode) ⇒ Object



169
170
171
# File 'lib/fusuma/plugin/sendkey/keyboard.rb', line 169

def keycode_const(keycode)
  Object.const_get "Revdev::#{keycode}"
end

#keydown(keycode) ⇒ Object



91
92
93
# File 'lib/fusuma/plugin/sendkey/keyboard.rb', line 91

def keydown(keycode)
  send_event(code: keycode, press: true)
end

#keyup(keycode) ⇒ Object



95
96
97
# File 'lib/fusuma/plugin/sendkey/keyboard.rb', line 95

def keyup(keycode)
  send_event(code: keycode, press: false)
end

#param_to_codes(param) ⇒ Array<String>

Parameters:

  • (String)

Returns:

  • (Array<String>)


180
181
182
# File 'lib/fusuma/plugin/sendkey/keyboard.rb', line 180

def param_to_codes(param)
  param.split("+").map { |keyname| add_prefix(keyname) }
end

#search_codes(code) ⇒ Object



158
159
160
# File 'lib/fusuma/plugin/sendkey/keyboard.rb', line 158

def search_codes(code)
  capabilities.select { |c| c[code] }
end

#send_event(code:, press: true) ⇒ Object



114
115
116
117
118
119
120
121
122
# File 'lib/fusuma/plugin/sendkey/keyboard.rb', line 114

def send_event(code:, press: true)
  event = Revdev::InputEvent.new(
    nil,
    Revdev.const_get(:EV_KEY),
    Revdev.const_get(code),
    press ? 1 : 0
  )
  @device.write_event(event)
end

#support?(code) ⇒ Boolean

Returns:

  • (Boolean)


145
146
147
148
# File 'lib/fusuma/plugin/sendkey/keyboard.rb', line 145

def support?(code)
  @supported_code ||= {}
  @supported_code[code] ||= find_code(code)
end

#type(param:, keep: "", clear: :none) ⇒ Object

Parameters:

  • param (String)

    key names separated by ‘+’ to type

  • keep (String) (defaults to: "")

    key names separated by ‘+’ to keep

  • clear (String, Symbol, TrueClass) (defaults to: :none)

    key names separated by ‘+’ to clear or :all to release all modifiers



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/fusuma/plugin/sendkey/keyboard.rb', line 68

def type(param:, keep: "", clear: :none)
  return unless param.is_a?(String)

  param_keycodes = param_to_codes(param)
  type_keycodes = param_keycodes - param_to_codes(keep)

  clear_keycodes =
    case clear
    when true
      MODIFIER_KEY_CODES
    when :none, false
      []
    else
      # release keys specified by clearmodifiers
      param_to_codes(clear)
    end

  clear_modifiers(clear_keycodes - param_keycodes)

  type_keycodes.each { |keycode| keydown(keycode) && key_sync && sleep(KEY_INTERVAL) }
  type_keycodes.reverse_each { |keycode| keyup(keycode) && key_sync && sleep(KEY_INTERVAL) }
end

#types(args) ⇒ Object

Parameters:

  • params (Array)


52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/fusuma/plugin/sendkey/keyboard.rb', line 52

def types(args)
  return unless args.is_a?(Array)

  args.each do |arg|
    case arg
    when String
      type(param: arg)
    when Hash
      type(**arg)
    end
  end
end

#valid?(params) ⇒ Boolean

Parameters:

  • param (String)

Returns:

  • (Boolean)


100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/fusuma/plugin/sendkey/keyboard.rb', line 100

def valid?(params)
  case params
  when Array
    params.all? { |param| valid?(param) }
  when String
    param = params
    keycodes = param_to_codes(param)
    keycodes.all? { |keycode| support?(keycode) }
  else
    MultiLogger.error "sendkey: Invalid config: #{params}"
    nil
  end
end

#warn_undefined_codes(code) ⇒ Object



150
151
152
153
154
155
156
# File 'lib/fusuma/plugin/sendkey/keyboard.rb', line 150

def warn_undefined_codes(code)
  candidates = search_codes(code).map { |c| remove_prefix(c.to_s) }

  warn "Did you mean? #{candidates.join(" / ")}" unless candidates.empty?

  warn "sendkey: #{remove_prefix(code)} is unsupported."
end