fusuma-plugin-thumbsense 0.6.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 25560d1130c397e047ad36abc976e19e3117d57b02525f82db04a66771900772
4
- data.tar.gz: fb72b8d4b755c6fa03dd71cb296218f30377f7f0bc189e04ea80c4f137b9ee10
3
+ metadata.gz: 1bdd2539bacc9ce3db7375ce445affc2a22fcadfc5a7849243737c8fe028911e
4
+ data.tar.gz: 4b4b1e40b305ef98b032ae186828dbd69b50fb8d82e978fb02d7a2540f8da517
5
5
  SHA512:
6
- metadata.gz: a4344efe92c9a0abdf086adeba86e6a5dc2df9ff542b07df304ea9532ccc1058c338d0b902cc560579d129b1250ac4ceef55291b025cd8c82e6633a6dbbe46b4
7
- data.tar.gz: b9b4e0c163e9dc4f3ddaa1aed115b367e5f4282684cb5741e534c51e77071bb9cba44248f8a6aa80d127e429ef291ea30e202c2e859c0b9d258545d8783181da
6
+ metadata.gz: e69801eb4e32d544bf7ba7a322b9204683211372e94ef36375e1c2c70a730254638da594fcbf3e13b4c31a7d3a9cba64a25ef3b086696e44bf1dbccb5b145724
7
+ data.tar.gz: 16d973013a91adc273687a849f48915c6acff2d3bff72ed97aaf44e7b89328051d32c32e1e0a2fb514453cdda80b0989df12bbe039df3209bd69d3d659780e1b
data/README.md CHANGED
@@ -1,51 +1,109 @@
1
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/main.yml/badge.svg)](https://github.com/iberianpig/fusuma-plugin-thumbsense/actions/workflows/main.yml)
2
2
 
3
+ Remapper from key to click only while tapping the touchpad.
4
+ Implemented as [Fusuma](https://github.com/iberianpig/fusuma) Plugin.
5
+
3
6
  **THIS PLUGIN IS EXPERIMENTAL.**
4
7
 
5
- ThumbSense plugin for [Fusuma](https://github.com/iberianpig/fusuma)
8
+ ## What is ThumbSense?
9
+ [ThumbSense](https://www2.sonycsl.co.jp/person/rekimoto/tsense/soft/index.html) 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.
6
10
 
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.
11
+ ## Installation
8
12
 
9
- [ThumbSense](https://www2.sonycsl.co.jp/person/rekimoto/tsense/soft/index.html)
13
+ ### Requirements
10
14
 
11
- ## Installation
15
+ - [fusuma](https://github.com/iberianpig/fusuma#update) 2.0 or later
16
+ - [fusuma-plugin-keypress](https://github.com/iberianpig/fusuma-plugin-keypress) 0.5 or later
17
+ - fusuma-plugin-keypress is used to get keyboard input and is installed automatically.
18
+ - [fusuma-plugin-remap](https://github.com/iberianpig/fusuma-plugin-remap)
19
+ - You need to set up udev rules for creating a virtual input device.
20
+ - fusuma-plugin-remap is installed automatically.
21
+ - Please refer to [fusuma-plugin-remap's README](https://github.com/iberianpig/fusuma-plugin-remap?tab=readme-ov-file#set-up-udev-rules) for details.
22
+
23
+ ### Install and set up fusuma-plugin-thumbsense
12
24
 
13
25
  Run the following code in your terminal.
14
26
 
15
- ### Install fusuma-plugin-thumbsense
27
+ #### Install ruby-dev and build-essential for installing native extension
28
+ ```sh
29
+ $ sudo apt install ruby-dev build-essential
30
+ ```
16
31
 
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
- - fusuma-plugin-keypress is used to get keyboard input and is installed automatically.
32
+ #### Install libevdev-dev for building fusuma-plugin-remap
33
+ ```sh
34
+ $ sudo apt install libevdev-dev
35
+ ```
19
36
 
37
+ #### set up udev rules for creating a virtual input device (fusuma-plugin-remap)
20
38
  ```sh
21
- $ sudo gem install fusuma-plugin-thumbsense
39
+ $ echo 'KERNEL=="uinput", MODE="0660", GROUP="input", OPTIONS+="static_node=uinput"' | sudo tee /etc/udev/rules.d/60-udev-fusuma-remap.rules
40
+ $ sudo udevadm control --reload-rules && sudo udevadm trigger
22
41
  ```
23
42
 
24
43
 
44
+ #### Install fusuma-plugin-thumbsense
45
+ ```sh
46
+ $ sudo gem install fusuma-plugin-thumbsense
47
+ ```
48
+
25
49
  ## Properties
26
50
 
27
- ### Thumbsense
51
+ ### Thumbsense context
52
+
53
+ First, add the thumbsense `context` to `~/.config/fusuma/config.yml`.
54
+ The `context` is separated by `---` and specified by `context: thumbsense`.
55
+ Fusuma will switch to the `thumbsense` context while tapping the touchpad.
28
56
 
29
- First, add the `thumbsense` context to `~/.config/fusuma/config.yml`.
30
- The context is separated by `---` and specified by `context: thumbsense`.
57
+ ### Remap key to mouse button
58
+
59
+ You can remap keys to mouse buttons while tapping the touchpad.
60
+ The `remap` property is specified in the `thumbsense` context.
61
+
62
+ Available mouse buttons are:
63
+
64
+ - `BTN_LEFT`
65
+ - `BTN_MIDDLE`
66
+ - `BTN_RIGHT`
67
+ - `BTN_SIDE`
68
+ - `BTN_EXTRA`
69
+ - `BTN_FORWARD`
70
+ - `BTN_BACK`
71
+ - `BTN_TASK`
72
+ - `BTN_0`
73
+ - `BTN_1`
74
+ - ...
75
+ - `BTN_9`
31
76
 
32
77
  ## Example
33
78
 
34
79
  Set the following code in `~/.config/fusuma/config.yml`.
35
80
 
36
81
  ```yaml
82
+ # add thumbsense context
83
+
37
84
  ---
38
85
  context: thumbsense
39
86
 
40
87
  remap:
88
+ F: BTN_LEFT
89
+ E: BTN_MIDDLE
90
+ D: BTN_RIGHT
91
+ SPACE: BTN_LEFT
41
92
  J: BTN_LEFT
42
93
  K: BTN_RIGHT
43
94
  ```
44
95
 
45
- ### TODO
96
+ ## TODO
97
+
98
+ - thumbsense
99
+ - [x] change layer of remap while tapping
100
+ - [ ] make it possible to execute executor like `command:` `sendkey:`
46
101
 
47
- - Using the fusuma-plugin-sendkey to emulate mouse buttons
48
- - Creating a simple key remapper using evdev and uinput to prevent the pressing of J/K keys on the physical keyboard from being sent.
102
+ - remap
103
+ - [x] remap to single key like `remap: { J: BTN_LEFT }`
104
+ - [x] send BTN_LEFT/BTN_MIDDLE/BTN_RIGHT click `remap: { I: BTN_MIDDLE }`
105
+ - [ ] remap multiple keys like `remap: { H: LEFTCTRL+TAB }`
106
+ - [ ] remap POINTER_MOTION to POINTER_SCROLL_FINGER `remap: { S: POINTER_SCROLL_FINGER }`
49
107
 
50
108
  ## Contributing
51
109
 
@@ -24,9 +24,9 @@ Gem::Specification.new do |spec|
24
24
  spec.required_ruby_version = ">= 2.7"
25
25
  # https://packages.ubuntu.com/search?keywords=ruby&searchon=names&exact=1&suite=all&section=main
26
26
  # support focal (20.04LTS) 2.7
27
- spec.add_dependency "fusuma", ">= 3.1"
28
- spec.add_dependency "fusuma-plugin-keypress", ">= 0.5"
29
- spec.add_dependency "fusuma-plugin-remap"
27
+ spec.add_dependency "fusuma", ">= 3.2"
28
+ spec.add_dependency "fusuma-plugin-keypress", ">= 0.11"
29
+ spec.add_dependency "fusuma-plugin-remap", ">= 0.4"
30
30
  spec.metadata = {
31
31
  "rubygems_mfa_required" => "true"
32
32
  }
@@ -5,7 +5,7 @@ module Fusuma
5
5
  module Buffers
6
6
  # manage events and generate command
7
7
  class ThumbsenseBuffer < Buffer
8
- DEFAULT_SOURCE = "thumbsense_parser"
8
+ DEFAULT_SOURCE = "remap_touchpad_input"
9
9
 
10
10
  def config_param_types
11
11
  {
@@ -20,8 +20,7 @@ module Fusuma
20
20
  # skip palm/begin record
21
21
  return if !ended?(@events.last)
22
22
 
23
- released_finger = @events.last.record.finger
24
- @events.delete_if { |e| e.record.finger == released_finger }
23
+ clear
25
24
  end
26
25
 
27
26
  # @param event [Event]
@@ -0,0 +1,4 @@
1
+ plugin:
2
+ buffers:
3
+ thumbsense_buffer:
4
+ source: remap_touchpad_input
@@ -10,8 +10,8 @@ module Fusuma
10
10
  module Detectors
11
11
  # Detect Thumbsense context and change remap layer of fusuma-plugin-remap
12
12
  class ThumbsenseDetector < Detector
13
+ # keypress buffer is used to detect modifier keys
13
14
  SOURCES = %w[thumbsense keypress].freeze
14
- BUFFER_TYPE = "thumbsense"
15
15
 
16
16
  MODIFIER_KEYS = Set.new(%w[
17
17
  CAPSLOCK
@@ -28,51 +28,74 @@ module Fusuma
28
28
  LAYER_CONTEXT = {thumbsense: true}.freeze
29
29
 
30
30
  # Detect Context event and change remap layer of fusuma-plugin-remap
31
- # @param buffers [Array<Buffer>]
31
+ # @param buffers [Array<Buffer>] ThumbsenseBuffer, KeypressBuffer
32
32
  # @return [Event] if Thumbsense context is detected
33
+ # @return [Array<Event>] if Thumbsense context and Remap index when keypress is detected
33
34
  # @return [NilClass] if event is NOT detected
34
35
  def detect(buffers)
35
- thumbsense_buffer = buffers.find { |b| b.type == BUFFER_TYPE }
36
-
37
- return if thumbsense_buffer.empty?
38
-
39
- MultiLogger.debug("thumbsense_buffer: #{thumbsense_buffer.events.map(&:record).map { |r| "#{r.finger} #{r.gesture} #{r.status}" }}")
36
+ @thumbsense_buffer ||= find_buffer(buffers, "thumbsense")
37
+ @keypress_buffer ||= find_buffer(buffers, "keypress")
40
38
 
41
39
  layer_manager = Fusuma::Plugin::Remap::LayerManager.instance
42
40
 
43
- if touch_released?(thumbsense_buffer)
41
+ # layer is thumbsense => create thumbsense context and remap index
42
+ # touch is touching => create thumbsense context and remap index
43
+ # touch is released => remove thumbsense context
44
+ # keypress -> touch => remove thumbsense context
45
+ if touch_released? && !thumbsense_layer?
46
+ MultiLogger.debug("thumbsense layer removed")
44
47
  layer_manager.send_layer(layer: LAYER_CONTEXT, remove: true)
45
48
  return
46
49
  end
47
50
 
48
- keypress_buffer = buffers.find { |b| b.type == "keypress" }
51
+ # When keypress event is first:
52
+ # If current layer is thumbsense, the layer should not be changed
53
+ # If current layer is not thumbsense, it should remain a normal key
54
+ # In other words, if the key event comes first, do nothing
55
+ if keypress_first?
56
+ MultiLogger.debug("keypress event is first")
49
57
 
50
- # If only modifier keys are pressed or no key is pressed
51
- if pressed_codes(keypress_buffer).all? { |code| MODIFIER_KEYS.include?(code) }
52
-
53
- # Even if the palm is detected, keep the thumbsense layer until `:end` event
54
- if palm_detected?(thumbsense_buffer)
55
- layer_manager.send_layer(layer: LAYER_CONTEXT, remove: true)
56
- return
57
- end
58
+ return
59
+ end
58
60
 
59
- layer_manager.send_layer(layer: LAYER_CONTEXT)
61
+ MultiLogger.debug("thumbsense context created") unless thumbsense_layer?
62
+ layer_manager.send_layer(layer: LAYER_CONTEXT)
60
63
 
61
- # create thumbsense context
62
- record = Events::Records::ContextRecord.new(
64
+ # create thumbsense context
65
+ context = create_event(
66
+ record: Events::Records::ContextRecord.new(
63
67
  name: :thumbsense,
64
68
  value: true
65
69
  )
66
- return create_event(record: record)
70
+ )
71
+
72
+ # TODO: Threshold
73
+ # create remap index
74
+ index = if (keys = pressed_codes) && !keys.empty?
75
+ MultiLogger.debug("thumbsense remap index created: #{keys}")
76
+ combined_keys = keys.join("+")
77
+ create_event(
78
+ record: Events::Records::IndexRecord.new(
79
+ index: Config::Index.new([:remap, combined_keys])
80
+ )
81
+ )
67
82
  end
68
83
 
69
- nil
84
+ [context, index].compact
70
85
  end
71
86
 
72
87
  private
73
88
 
74
- def pressed_codes(keypress_buffer)
75
- records = keypress_buffer.events.map(&:record)
89
+ # @param buffers [Array<Buffer>]
90
+ # @param type [String]
91
+ # @return [Buffer]
92
+ def find_buffer(buffers, type)
93
+ buffers.find { |b| b.type == type }
94
+ end
95
+
96
+ # @return [Array<String>]
97
+ def pressed_codes
98
+ records = @keypress_buffer.events.map(&:record)
76
99
  codes = []
77
100
  records.each do |r|
78
101
  if r.status == "pressed"
@@ -84,45 +107,43 @@ module Fusuma
84
107
  codes
85
108
  end
86
109
 
87
- def touching?(thumbsense_buffer)
88
- !touch_released?(thumbsense_buffer)
110
+ # @return [TrueClass, FalseClass]
111
+ def touching?
112
+ !touch_released?(@thumbsense_buffer)
89
113
  end
90
114
 
91
115
  # @return [TrueClass, FalseClass]
92
- def touch_released?(thumbsense_buffer)
93
- thumbsense_events = thumbsense_buffer.events
94
- touch_num = thumbsense_events.count { |e| (e.record.status == "begin") }
95
- release_num = thumbsense_events.count { |e| e.record.status == "end" }
116
+ def touch_released?
117
+ return true if @thumbsense_buffer.empty?
96
118
 
97
- touch_num <= release_num
119
+ @thumbsense_buffer.events.map(&:record).last&.status == "end"
98
120
  end
99
121
 
100
- # Detect palm, except when there is another touch
101
- # @param thumbsense_buffer [Buffer]
102
122
  # @return [TrueClass, FalseClass]
103
- def palm_detected?(thumbsense_buffer)
104
- # finger is a number to distinguish different touches and palms
105
- # If the count remains, it is judged as a touch state
106
- touch_state_per_finger = {}
107
- thumbsense_buffer.events.each do |e|
108
- f = e.record.finger
109
- touch_state_per_finger[f] ||= 0
110
-
111
- case e.record.status
112
- when "begin"
113
- touch_state_per_finger[f] += 1
114
- when "palm"
115
- if touch_state_per_finger[f] < 0
116
- # NOTE: If Palm continues, it is equivalent to end
117
- touch_state_per_finger[f] = 0
118
- else
119
- touch_state_per_finger[f] -= 1
120
- end
121
- when "end"
122
- touch_state_per_finger[f] = 0
123
- end
123
+ def thumbsense_layer?
124
+ return if @keypress_buffer.empty?
125
+
126
+ last_keypress = @keypress_buffer.events.last.record
127
+ return if last_keypress.status == "released"
128
+
129
+ current_layer = last_keypress&.layer
130
+ current_layer && current_layer["thumbsense"]
131
+ end
132
+
133
+ # Check if keypress event is first, before thumbsense event
134
+ # If last keypress event is modifier key, return false
135
+ # @param keypress_buffer [Buffer]
136
+ # @param thumbsense_buffer [Buffer]
137
+ # @return [TrueClass] if keypress event is first
138
+ # @return [FalseClass] if keypress event is NOT first or buffers are empty
139
+ def keypress_first?
140
+ return false if @thumbsense_buffer.empty? || @keypress_buffer.empty?
141
+
142
+ if (keys = pressed_codes) && !keys.empty?
143
+ return false if MODIFIER_KEYS.include?(keys.first)
124
144
  end
125
- touch_state_per_finger.values.all? { |count| count <= 0 }
145
+
146
+ @keypress_buffer.events.first.time < @thumbsense_buffer.events.first.time
126
147
  end
127
148
  end
128
149
  end
@@ -3,7 +3,7 @@
3
3
  module Fusuma
4
4
  module Plugin
5
5
  module Thumbsense
6
- VERSION = "0.6.0"
6
+ VERSION = "0.8.0"
7
7
  end
8
8
  end
9
9
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fusuma-plugin-thumbsense
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - iberianpig
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-09-03 00:00:00.000000000 Z
11
+ date: 2024-04-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fusuma
@@ -16,42 +16,42 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '3.1'
19
+ version: '3.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '3.1'
26
+ version: '3.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: fusuma-plugin-keypress
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '0.5'
33
+ version: '0.11'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '0.5'
40
+ version: '0.11'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: fusuma-plugin-remap
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '0.4'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: '0.4'
55
55
  description: fusuma-plugin-thumbsense is Fusuma plugin for thumbsense.
56
56
  email:
57
57
  - yhkyky@gmail.com
@@ -65,10 +65,8 @@ files:
65
65
  - bin/setup
66
66
  - fusuma-plugin-thumbsense.gemspec
67
67
  - lib/fusuma/plugin/buffers/thumbsense_buffer.rb
68
+ - lib/fusuma/plugin/buffers/thumbsense_buffer.yml
68
69
  - lib/fusuma/plugin/detectors/thumbsense_detector.rb
69
- - lib/fusuma/plugin/filters/thumbsense_filter.rb
70
- - lib/fusuma/plugin/filters/thumbsense_filter.yml
71
- - lib/fusuma/plugin/parsers/thumbsense_parser.rb
72
70
  - lib/fusuma/plugin/thumbsense.rb
73
71
  - lib/fusuma/plugin/thumbsense/version.rb
74
72
  homepage: https://github.com/iberianpig/fusuma-plugin-thumbsense
@@ -91,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
91
89
  - !ruby/object:Gem::Version
92
90
  version: '0'
93
91
  requirements: []
94
- rubygems_version: 3.4.10
92
+ rubygems_version: 3.4.19
95
93
  signing_key:
96
94
  specification_version: 4
97
95
  summary: Thumbsense plugin for Fusuma
@@ -1,27 +0,0 @@
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
@@ -1,4 +0,0 @@
1
- plugin:
2
- inputs:
3
- libinput_command_input:
4
- verbose: true
@@ -1,50 +0,0 @@
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