fusuma-plugin-thumbsense 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d52fd9c01f2bd94ab5ec05ba4c4d6abc1c7d38c6f33d136cf4aa34df71355c4e
4
+ data.tar.gz: fd096d77f2d0b1e9fa839004f2ea8d57f8724a324682a8bf98141abaef6d04d2
5
+ SHA512:
6
+ metadata.gz: 06e6df1b845c55b9383cbdf179e978dfbe71767905df558db409f936262c0bab95282d9eacd6565fb0320df76f99be969fb693f55c245e1e58a9f10dcf8cc986
7
+ data.tar.gz: dfa00e0a3ff211ee5526be0c6047f05c8d75436c1b042ef7f30cc9ce2b3202a77df0d2d9321afc7d9a9240a811e52866d3e60b088711a6e8ac02074a627c92a5
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 iberianpig
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # Fusuma::Plugin::Thumbsense [![Gem Version](https://badge.fury.io/rb/fusuma-plugin-thumbsense.svg)](https://badge.fury.io/rb/fusuma-plugin-thumbsense) [![Build Status](https://github.com/iberianpig/fusuma-plugin-thumbsense/actions/workflows/ubuntu.yml/badge.svg)](https://github.com/iberianpig/fusuma-plugin-thumbsense/actions/workflows/ubuntu.yml)
2
+
3
+ **THIS PLUGIN IS EXPERIMENTAL.**
4
+
5
+ ThumbSense plugin for [Fusuma](https://github.com/iberianpig/fusuma)
6
+
7
+ ThumbSense is a tool that lets you control a laptop's touchpad using the keyboard. It assigns certain keyboard keys as mouse buttons and switches between acting as mouse buttons or normal keyboard keys based on whether the user's thumb is touching the touchpad. ThumbSense aims to make it easier to use the touchpad without moving your hand away from the keyboard.
8
+
9
+ [ThumbSense](https://www2.sonycsl.co.jp/person/rekimoto/tsense/soft/index.html)
10
+
11
+ ## Installation
12
+
13
+ Run the following code in your terminal.
14
+
15
+ ### Install fusuma-plugin-thumbsense
16
+
17
+ This plugin requires [fusuma](https://github.com/iberianpig/fusuma#update) 2.0 or later and [fusuma-plugin-keypress](https://github.com/iberianpig/fusuma-plugin-keypress) 0.5 or later.
18
+
19
+ ```sh
20
+ $ sudo gem install fusuma-plugin-thumbsense
21
+ ```
22
+
23
+ ### Add show-keycodes option
24
+
25
+ Open `~/.config/fusuma/config.yml` and add the following code at the bottom.
26
+
27
+ ```yaml
28
+ plugin:
29
+ inputs:
30
+ libinput_command_input:
31
+ show-keycodes: true
32
+ ```
33
+
34
+ **NOTE: fusuma can read your keyboard inputs if show-keycodes option is true**
35
+
36
+ ## Properties
37
+
38
+ ### Thumbsense
39
+
40
+ First, add the `thumbsense` context to `~/.config/fusuma/config.yml`.
41
+ The context is separated by `---` and specified by `context: thumbsense`.
42
+
43
+ ## Example
44
+
45
+ Set the following code in `~/.config/fusuma/config.yml`.
46
+
47
+ ```yaml
48
+ ---
49
+ context: thumbsense
50
+
51
+ remap:
52
+ J: BTN_LEFT
53
+ K: BTN_RIGHT
54
+ ```
55
+
56
+ ### TODO
57
+
58
+ - Using the fusuma-plugin-sendkey to emulate mouse buttons
59
+ - Creating a simple key remapper using evdev and uinput to prevent the pressing of J/K keys on the physical keyboard from being sent.
60
+
61
+ ## Contributing
62
+
63
+ Bug reports and pull requests are welcome on GitHub at https://github.com/iberianpig/fusuma-plugin-thumbsense. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
64
+
65
+ ## License
66
+
67
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
68
+
69
+ ## Code of Conduct
70
+
71
+ Everyone interacting in the Fusuma::Plugin::Thumbsense project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/iberianpig/fusuma-plugin-thumbsense/blob/master/CODE_OF_CONDUCT.md).
data/bin/console ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "fusuma/plugin/manager"
6
+ Fusuma::Plugin::Manager.require_base_plugins
7
+ require "fusuma/plugin/remap"
8
+ require "fusuma/plugin/thumbsense"
9
+
10
+ # You can add fixtures and/or initialization code here to make experimenting
11
+ # with your gem easier. You can also use a different console, if you like.
12
+
13
+ # (If you use this, don't forget to add pry to your Gemfile!)
14
+ # require "pry"
15
+ # Pry.start
16
+
17
+ require "irb"
18
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("lib", __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "fusuma/plugin/thumbsense/version"
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "fusuma-plugin-thumbsense"
9
+ spec.version = Fusuma::Plugin::Thumbsense::VERSION
10
+ spec.authors = ["iberianpig"]
11
+ spec.email = ["yhkyky@gmail.com"]
12
+
13
+ spec.summary = "Thumbsense plugin for Fusuma "
14
+ spec.description = "fusuma-plugin-thumbsense is Fusuma plugin for thumbsense."
15
+ spec.homepage = "https://github.com/iberianpig/fusuma-plugin-thumbsense"
16
+ spec.license = "MIT"
17
+
18
+ # Specify which files should be added to the gem when it is released.
19
+ spec.files = Dir["{bin,lib,exe}/**/*", "LICENSE*", "README*", "*.gemspec"]
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.required_ruby_version = ">= 2.5.1" # https://packages.ubuntu.com/search?keywords=ruby&searchon=names&exact=1&suite=all&section=main
25
+ # support bionic (18.04LTS) 2.5.1
26
+ spec.add_dependency "fusuma", "~> 2.0"
27
+ spec.add_dependency "fusuma-plugin-keypress", ">= 0.5"
28
+ spec.add_dependency "fusuma-plugin-remap"
29
+ spec.metadata = {
30
+ "rubygems_mfa_required" => "true"
31
+ }
32
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fusuma
4
+ module Plugin
5
+ module Buffers
6
+ # manage events and generate command
7
+ class ThumbsenseBuffer < Buffer
8
+ DEFAULT_SOURCE = "thumbsense_parser"
9
+
10
+ def config_param_types
11
+ {
12
+ source: [String]
13
+ }
14
+ end
15
+
16
+ # clear old events
17
+ def clear_expired(*)
18
+ return if @events.empty?
19
+
20
+ # skip palm/begin record
21
+ return if !ended?(@events.last)
22
+
23
+ released_finger = @events.last.record.finger
24
+ @events.delete_if { |e| e.record.finger == released_finger }
25
+ end
26
+
27
+ # @param event [Event]
28
+ # @return [NilClass, ThumbsenseBuffer]
29
+ def buffer(event)
30
+ return if event&.tag != source
31
+
32
+ @events.push(event)
33
+ self
34
+ end
35
+
36
+ # return [Integer]
37
+ def finger
38
+ @events.map { |e| e.record.finger }.max
39
+ end
40
+
41
+ def empty?
42
+ @events.empty?
43
+ end
44
+
45
+ def present?
46
+ !empty?
47
+ end
48
+
49
+ def select_by_events(&block)
50
+ return enum_for(:select) unless block
51
+
52
+ events = @events.select(&block)
53
+ self.class.new events
54
+ end
55
+
56
+ def ended?(event)
57
+ event.record.status == "end"
58
+ end
59
+
60
+ def begin?(event)
61
+ event.record.status == "begin"
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fusuma/plugin/detectors/detector"
4
+
5
+ require "fusuma/plugin/remap/layer_manager"
6
+
7
+ module Fusuma
8
+ module Plugin
9
+ module Detectors
10
+ # Detect Thumbsense context and change remap layer of fusuma-plugin-remap
11
+ class ThumbsenseDetector < Detector
12
+ SOURCES = %w[thumbsense gesture keypress].freeze
13
+ BUFFER_TYPE = "thumbsense"
14
+
15
+ MODIFIER_KEYS = Set.new(%w[
16
+ CAPSLOCK
17
+ LEFTALT
18
+ LEFTCTRL
19
+ LEFTMETA
20
+ LEFTSHIFT
21
+ RIGHTALT
22
+ RIGHTCTRL
23
+ RIGHTSHIFT
24
+ RIGHTMETA
25
+ ])
26
+
27
+ # @param buffers [Array<Buffer>]
28
+ # @return [Event] if event is detected
29
+ # @return [NilClass] if event is NOT detected
30
+ def detect(buffers)
31
+ thumbsense_buffer = buffers.find { |b| b.type == BUFFER_TYPE }
32
+
33
+ return if thumbsense_buffer.empty?
34
+
35
+ MultiLogger.debug("thumbsense_buffer: #{thumbsense_buffer.events.map(&:record).map { |r| "#{r.finger} #{r.gesture} #{r.status}" }}")
36
+
37
+ layer_manager = Fusuma::Plugin::Remap::LayerManager.instance
38
+ layer = {thumbsense: true}
39
+
40
+ if touch_released?(thumbsense_buffer)
41
+ layer_manager.send_layer(layer: layer, remove: true)
42
+ return
43
+ end
44
+
45
+ keypress_buffer = buffers.find { |b| b.type == "keypress" }
46
+ if pressed_codes(keypress_buffer).all? { |code| MODIFIER_KEYS.include?(code) }
47
+
48
+ # Even if the palm is detected, keep the thumbsense layer until `:end` event
49
+ if palm_detected?(thumbsense_buffer)
50
+ hold_events = fetch_hold_events(buffers)
51
+ MultiLogger.debug "hold_events: #{hold_events.map(&:record).map { |r| "#{r.finger} #{r.gesture} #{r.status}" }}"
52
+ if hold_events.empty? || hold_events.last.record.status != "begin"
53
+ layer_manager.send_layer(layer: layer, remove: true)
54
+ return
55
+ end
56
+ end
57
+
58
+ layer_manager.send_layer(layer: layer)
59
+ record = Events::Records::ContextRecord.new(
60
+ name: "thumbsense",
61
+ value: true
62
+ )
63
+ return create_event(record: record)
64
+ end
65
+ nil
66
+ end
67
+
68
+ # Change remap layer of fusuma-plugin-remap
69
+ # @param context [Hash]
70
+ def add_layer
71
+ Fusuma::Plugin::Remap::Layer.add({thumbsense: true})
72
+ end
73
+
74
+ # Remove remap layer of fusuma-plugin-remap
75
+ # @param context [Hash]
76
+ def remove_layer
77
+ Fusuma::Plugin::Remap::Layer.remove({thumbsense: true})
78
+ end
79
+
80
+ private
81
+
82
+ def fetch_hold_events(buffers)
83
+ buffers.find { |b| b.type == "gesture" }
84
+ .select_from_last_begin
85
+ .select_by_events { |e| e.record.gesture == "hold" }.events
86
+ end
87
+
88
+ def pressed_codes(keypress_buffer)
89
+ records = keypress_buffer.events.map(&:record)
90
+ codes = []
91
+ records.each do |r|
92
+ if r.status == "pressed"
93
+ codes << r.code
94
+ else
95
+ codes.delete_if { |code| code == r.code }
96
+ end
97
+ end
98
+ codes
99
+ end
100
+
101
+ def touching?(thumbsense_buffer)
102
+ !touch_released?(thumbsense_buffer)
103
+ end
104
+
105
+ # @return [TrueClass, FalseClass]
106
+ def touch_released?(thumbsense_buffer)
107
+ thumbsense_events = thumbsense_buffer.events
108
+ touch_num = thumbsense_events.count { |e| (e.record.status == "begin") }
109
+ release_num = thumbsense_events.count { |e| e.record.status == "end" }
110
+
111
+ touch_num <= release_num
112
+ end
113
+
114
+ def palm_detected?(thumbsense_buffer)
115
+ thumbsense_buffer.events.any? { |e| (e.record.status == "palm") }
116
+ end
117
+
118
+ def palm_count(thumbsense_buffer)
119
+ thumbsense_buffer.events.count { |e| (e.record.status == "palm") }
120
+ end
121
+
122
+ def thumbsense_keys
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fusuma/device"
4
+
5
+ module Fusuma
6
+ module Plugin
7
+ module Filters
8
+ # Filter keyboard events from libinput_command_input
9
+ class ThumbsenseFilter < Filter
10
+ DEFAULT_SOURCE = "libinput_command_input"
11
+
12
+ # @return [TrueClass] when keeping it
13
+ # @return [FalseClass] when discarding it
14
+ def keep?(record)
15
+ case record.to_s
16
+ when %r{\sevent\d+\s+-\sbutton state: touch (?<finger>[[:digit:]])}
17
+ true
18
+ when %r{\sevent\d+\s+-\spalm: touch (?<finger>[[:digit:]])}
19
+ true
20
+ else
21
+ false
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ # require 'fusuma/plugin/parsers/parser.rb'
4
+ # require 'fusuma/plugin/events/event.rb'
5
+
6
+ module Fusuma
7
+ module Plugin
8
+ module Parsers
9
+ # parse libinput and generate event
10
+ class ThumbsenseParser < Parser
11
+ DEFAULT_SOURCE = "libinput_command_input"
12
+
13
+ # ... event7 - button state: touch 3 from BUTTON_STATE_AREA event BUTTON_EVENT_UP to BUTTON_STATE_NONE
14
+ # 10766: event7 - button state: touch 1 from BUTTON_STATE_AREA event BUTTON_EVENT_UP to BUTTON_STATE_NONE
15
+ # 10768: event7 - button state: touch 0 from BUTTON_STATE_AREA event BUTTON_EVENT_UP to BUTTON_STATE_NONE
16
+
17
+ # @param record [String]
18
+ # @return [Records::Gesture, nil]
19
+ def parse_record(record)
20
+ gesture = "touch"
21
+
22
+ case record.to_s
23
+
24
+ # touched
25
+ when %r{\sevent\d+\s+-\sbutton state: touch (?<finger>[[:digit:]]) from BUTTON_STATE_NONE}
26
+ status = "begin"
27
+ finger = $~[:finger].to_i + 1
28
+ # released
29
+ when %r{\sevent\d+\s+-\sbutton state: touch (?<finger>[[:digit:]]) .* to BUTTON_STATE_NONE}
30
+ status = "end"
31
+ finger = $~[:finger].to_i + 1
32
+
33
+ # palm
34
+ when %r{\sevent\d+\s+-\spalm: touch (?<finger>[[:digit:]]) .*}
35
+ status = "palm"
36
+ finger = $~[:finger].to_i + 1
37
+ else
38
+ return
39
+ end
40
+
41
+ Events::Records::GestureRecord.new(status: status, gesture: gesture, finger: finger, delta: nil)
42
+ end
43
+
44
+ def tag
45
+ "thumbsense_parser"
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fusuma
4
+ module Plugin
5
+ module Thumbsense
6
+ VERSION = "0.1.0"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "thumbsense/version"
4
+
5
+ module Fusuma
6
+ module Plugin
7
+ module Thumbsense
8
+ end
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fusuma-plugin-thumbsense
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - iberianpig
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-05-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: fusuma
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: fusuma-plugin-keypress
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0.5'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: fusuma-plugin-remap
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: fusuma-plugin-thumbsense is Fusuma plugin for thumbsense.
56
+ email:
57
+ - yhkyky@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - LICENSE.txt
63
+ - README.md
64
+ - bin/console
65
+ - bin/setup
66
+ - fusuma-plugin-thumbsense.gemspec
67
+ - lib/fusuma/plugin/buffers/thumbsense_buffer.rb
68
+ - lib/fusuma/plugin/detectors/thumbsense_detector.rb
69
+ - lib/fusuma/plugin/filters/thumbsense_filter.rb
70
+ - lib/fusuma/plugin/parsers/thumbsense_parser.rb
71
+ - lib/fusuma/plugin/thumbsense.rb
72
+ - lib/fusuma/plugin/thumbsense/version.rb
73
+ homepage: https://github.com/iberianpig/fusuma-plugin-thumbsense
74
+ licenses:
75
+ - MIT
76
+ metadata:
77
+ rubygems_mfa_required: 'true'
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: 2.5.1
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubygems_version: 3.2.22
94
+ signing_key:
95
+ specification_version: 4
96
+ summary: Thumbsense plugin for Fusuma
97
+ test_files: []