class Rkremap

Constants

CODE_KEY
EVENT_TYPE_VALUE
EV_KEY
EV_SYN
SYN_REPORT
VERSION

Attributes

auto_detect[RW]
grab[RW]
modifiers[RW]
x11[RW]

Public Class Methods

new(devices=nil) click to toggle source

@param devices [Array<String>, String]

# File lib/rkremap.rb, line 23
def initialize(devices=nil)
  if devices
    devices = Array(devices)
    @inputs = devices.map{|d| Evdev.new(d)}
  else
    @auto_detect = true
    @inputs = Evdev.detect.select(&:keyboard?)
  end
  raise 'Unable to detect keyboard device' if @inputs.empty?
  @uinput = Uinput.new
  @grab = false
  @x11 = false
  @modifiers = [
    KEY_LEFTCTRL,  KEY_RIGHTCTRL,
    KEY_LEFTSHIFT, KEY_RIGHTSHIFT,
    KEY_LEFTALT,   KEY_RIGHTALT,
    KEY_LEFTMETA,  KEY_RIGHTMETA,
  ]
  @events = []
  @mutex = Mutex.new
end

Public Instance Methods

detect_device_loop() click to toggle source
# File lib/rkremap.rb, line 73
def detect_device_loop
  Thread.new do
    while true
      sleep 3
      new_devs = Evdev.detect.select(&:keyboard?)
      unless new_devs.empty?
        new_devs.each(&:grab) if @grab
        @inputs += new_devs
      end
    end
  end
end
event(code: nil, type: nil, event: nil) click to toggle source

@param code [Integer] @param type [Symbol] :press / :release / :repeat @param ev [Rkmap::Event]

# File lib/rkremap.rb, line 106
def event(code: nil, type: nil, event: nil)
  value = EVENT_TYPE_VALUE[type] or raise "invalid type: #{type.inspect}"
  proc_event(code, value, event)
end
key(code, mod={}) click to toggle source

@param code [Integer] @param mod [Hash] {MOD_KEY => true, …}

# File lib/rkremap.rb, line 113
def key(code, mod={})
  mod_diff(mod).each do |mcode, state|
    write_event(mcode, state ? 1 : 0)
  end
  write_event(code, 1)
  write_event(code, 0)
  mod_diff(mod).each do |mcode, _|
    write_event(mcode, @mod_state[mcode] ? 1 : 0)
  end
end
match(code: nil, type: nil, &block) click to toggle source

@param code [Integer] @param type [Symbol] :press / :release / :repeat

# File lib/rkremap.rb, line 47
def match(code: nil, type: nil, &block)
  @events.push [{code: code, type: type}, block]
end
read_event() click to toggle source
# File lib/rkremap.rb, line 86
def read_event
  while true
    begin
      input = Evdev.select(@inputs, 3)
      next unless input
      return input.read_event
    rescue Errno::ENODEV
      input.close rescue nil
      @inputs.delete input
    end
  end
end
start(&block) click to toggle source
# File lib/rkremap.rb, line 51
def start(&block)
  detect_device_loop if @auto_detect
  @mod_state = @modifiers.map.to_h{|m| [m, false]}
  @winattr = WinAttr.new if @x11
  @inputs.each(&:grab) if @grab
  while true
    @keys = []
    time, type, code, value = read_event
    next if type != EV_KEY
    event = Event.new(time, code, value, @winattr)
    @events.each do |cond, b|
      synchronize{ b.call event } if event.match?(**cond)
      break if event.skipped
    end
    next if event.skipped
    synchronize{ proc_event(code, value, event) }
    @keys.each do |c, mod, app|
      synchronize{ block.call(c, mod, app) } if block
    end
  end
end
synchronize(&block) click to toggle source
# File lib/rkremap.rb, line 99
def synchronize(&block)
  @mutex.synchronize(&block)
end

Private Instance Methods

mod_diff(mod) click to toggle source
# File lib/rkremap.rb, line 135
def mod_diff(mod)
  mod.select{|k, v| @mod_state[k] != v}
end
proc_event(code, value, event) click to toggle source
# File lib/rkremap.rb, line 126
def proc_event(code, value, event)
  if @mod_state.include?(code)
    @mod_state[code] = value != 0
    write_event(code, value)
  elsif value != 0
    @keys.push [code, @mod_state.dup, App.new(event&.win, @winattr)]
  end
end
write_event(code, value) click to toggle source

@param code [Integer] @param value [Integer] 0:release / 1:press / 2:repeat

# File lib/rkremap.rb, line 141
def write_event(code, value)
  @uinput.write_event(EV_KEY, code, value)
  @uinput.write_event(EV_SYN, SYN_REPORT, 0)
end