Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate Fusuma-plugin-remap into Detector #3

Merged
merged 8 commits into from
Apr 15, 2024
4 changes: 2 additions & 2 deletions fusuma-plugin-thumbsense.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ Gem::Specification.new do |spec|
# https://packages.ubuntu.com/search?keywords=ruby&searchon=names&exact=1&suite=all&section=main
# support focal (20.04LTS) 2.7
spec.add_dependency "fusuma", ">= 3.2"
spec.add_dependency "fusuma-plugin-keypress", ">= 0.5"
spec.add_dependency "fusuma-plugin-remap"
spec.add_dependency "fusuma-plugin-keypress", ">= 0.11"
spec.add_dependency "fusuma-plugin-remap", ">= 0.4"
spec.metadata = {
"rubygems_mfa_required" => "true"
}
Expand Down
5 changes: 2 additions & 3 deletions lib/fusuma/plugin/buffers/thumbsense_buffer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module Plugin
module Buffers
# manage events and generate command
class ThumbsenseBuffer < Buffer
DEFAULT_SOURCE = "thumbsense_parser"
DEFAULT_SOURCE = "remap_touchpad_input"

def config_param_types
{
Expand All @@ -20,8 +20,7 @@ def clear_expired(*)
# skip palm/begin record
return if !ended?(@events.last)

released_finger = @events.last.record.finger
@events.delete_if { |e| e.record.finger == released_finger }
clear
end

# @param event [Event]
Expand Down
4 changes: 4 additions & 0 deletions lib/fusuma/plugin/buffers/thumbsense_buffer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
plugin:
buffers:
thumbsense_buffer:
source: remap_touchpad_input
131 changes: 76 additions & 55 deletions lib/fusuma/plugin/detectors/thumbsense_detector.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ module Plugin
module Detectors
# Detect Thumbsense context and change remap layer of fusuma-plugin-remap
class ThumbsenseDetector < Detector
# keypress buffer is used to detect modifier keys
SOURCES = %w[thumbsense keypress].freeze
BUFFER_TYPE = "thumbsense"

MODIFIER_KEYS = Set.new(%w[
CAPSLOCK
Expand All @@ -28,51 +28,74 @@ class ThumbsenseDetector < Detector
LAYER_CONTEXT = {thumbsense: true}.freeze

# Detect Context event and change remap layer of fusuma-plugin-remap
# @param buffers [Array<Buffer>]
# @param buffers [Array<Buffer>] ThumbsenseBuffer, KeypressBuffer
# @return [Event] if Thumbsense context is detected
# @return [Array<Event>] if Thumbsense context and Remap index when keypress is detected
# @return [NilClass] if event is NOT detected
def detect(buffers)
thumbsense_buffer = buffers.find { |b| b.type == BUFFER_TYPE }

return if thumbsense_buffer.empty?

MultiLogger.debug("thumbsense_buffer: #{thumbsense_buffer.events.map(&:record).map { |r| "#{r.finger} #{r.gesture} #{r.status}" }}")
@thumbsense_buffer ||= find_buffer(buffers, "thumbsense")
@keypress_buffer ||= find_buffer(buffers, "keypress")

layer_manager = Fusuma::Plugin::Remap::LayerManager.instance

if touch_released?(thumbsense_buffer)
# layer is thumbsense => create thumbsense context and remap index
# touch is touching => create thumbsense context and remap index
# touch is released => remove thumbsense context
# keypress -> touch => remove thumbsense context
if touch_released? && !thumbsense_layer?
MultiLogger.debug("thumbsense layer removed")
layer_manager.send_layer(layer: LAYER_CONTEXT, remove: true)
return
end

keypress_buffer = buffers.find { |b| b.type == "keypress" }
# When keypress event is first:
# If current layer is thumbsense, the layer should not be changed
# If current layer is not thumbsense, it should remain a normal key
# In other words, if the key event comes first, do nothing
if keypress_first?
MultiLogger.debug("keypress event is first")

# If only modifier keys are pressed or no key is pressed
if pressed_codes(keypress_buffer).all? { |code| MODIFIER_KEYS.include?(code) }

# Even if the palm is detected, keep the thumbsense layer until `:end` event
if palm_detected?(thumbsense_buffer)
layer_manager.send_layer(layer: LAYER_CONTEXT, remove: true)
return
end
return
end

layer_manager.send_layer(layer: LAYER_CONTEXT)
MultiLogger.debug("thumbsense context created") unless thumbsense_layer?
layer_manager.send_layer(layer: LAYER_CONTEXT)

# create thumbsense context
record = Events::Records::ContextRecord.new(
# create thumbsense context
context = create_event(
record: Events::Records::ContextRecord.new(
name: :thumbsense,
value: true
)
return create_event(record: record)
)

# TODO: Threshold
# create remap index
index = if (keys = pressed_codes) && !keys.empty?
MultiLogger.debug("thumbsense remap index created: #{keys}")
combined_keys = keys.join("+")
create_event(
record: Events::Records::IndexRecord.new(
index: Config::Index.new([:remap, combined_keys])
)
)
end

nil
[context, index].compact
end

private

def pressed_codes(keypress_buffer)
records = keypress_buffer.events.map(&:record)
# @param buffers [Array<Buffer>]
# @param type [String]
# @return [Buffer]
def find_buffer(buffers, type)
buffers.find { |b| b.type == type }
end

# @return [Array<String>]
def pressed_codes
records = @keypress_buffer.events.map(&:record)
codes = []
records.each do |r|
if r.status == "pressed"
Expand All @@ -84,45 +107,43 @@ def pressed_codes(keypress_buffer)
codes
end

def touching?(thumbsense_buffer)
!touch_released?(thumbsense_buffer)
# @return [TrueClass, FalseClass]
def touching?
!touch_released?(@thumbsense_buffer)
end

# @return [TrueClass, FalseClass]
def touch_released?(thumbsense_buffer)
thumbsense_events = thumbsense_buffer.events
touch_num = thumbsense_events.count { |e| (e.record.status == "begin") }
release_num = thumbsense_events.count { |e| e.record.status == "end" }
def touch_released?
return true if @thumbsense_buffer.empty?

touch_num <= release_num
@thumbsense_buffer.events.map(&:record).last&.status == "end"
end

# Detect palm, except when there is another touch
# @param thumbsense_buffer [Buffer]
# @return [TrueClass, FalseClass]
def palm_detected?(thumbsense_buffer)
# finger is a number to distinguish different touches and palms
# If the count remains, it is judged as a touch state
touch_state_per_finger = {}
thumbsense_buffer.events.each do |e|
f = e.record.finger
touch_state_per_finger[f] ||= 0

case e.record.status
when "begin"
touch_state_per_finger[f] += 1
when "palm"
if touch_state_per_finger[f] < 0
# NOTE: If Palm continues, it is equivalent to end
touch_state_per_finger[f] = 0
else
touch_state_per_finger[f] -= 1
end
when "end"
touch_state_per_finger[f] = 0
end
def thumbsense_layer?
return if @keypress_buffer.empty?

last_keypress = @keypress_buffer.events.last.record
return if last_keypress.status == "released"

current_layer = last_keypress&.layer
current_layer && current_layer["thumbsense"]
end

# Check if keypress event is first, before thumbsense event
# If last keypress event is modifier key, return false
# @param keypress_buffer [Buffer]
# @param thumbsense_buffer [Buffer]
# @return [TrueClass] if keypress event is first
# @return [FalseClass] if keypress event is NOT first or buffers are empty
def keypress_first?
return false if @thumbsense_buffer.empty? || @keypress_buffer.empty?

if (keys = pressed_codes) && !keys.empty?
return false if MODIFIER_KEYS.include?(keys.first)
end
touch_state_per_finger.values.all? { |count| count <= 0 }

@keypress_buffer.events.first.time < @thumbsense_buffer.events.first.time
end
end
end
Expand Down
27 changes: 0 additions & 27 deletions lib/fusuma/plugin/filters/thumbsense_filter.rb

This file was deleted.

5 changes: 0 additions & 5 deletions lib/fusuma/plugin/filters/thumbsense_filter.yml

This file was deleted.

50 changes: 0 additions & 50 deletions lib/fusuma/plugin/parsers/thumbsense_parser.rb

This file was deleted.

2 changes: 1 addition & 1 deletion spec/fusuma/plugin/buffers/thumbsense_buffer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module Buffers
@buffer = ThumbsenseBuffer.new
@event_generator = lambda { |time = nil, finger = 1, status = "begin"|
Events::Event.new(time: time,
tag: "thumbsense_parser",
tag: "remap_touchpad_input",
record: Events::Records::GestureRecord.new(
status: status,
gesture: "thumbsense",
Expand Down
Loading
Loading