keyboard_map 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 73f42043d059844447e80fadb95e205f0322d878d6916adb150bb884422b9ea6
4
- data.tar.gz: 116ca240612ee19ceb90b8541364cae3fba201998c9cff9a74688959b8c2037d
3
+ metadata.gz: d26c9809ebcc1a3c96e7880a2ba11dcd661376271914d1835331b6b065a8c040
4
+ data.tar.gz: 45e5461eefa006f0e1babd9f3077943c545cfd82610225f9ff697426aeb4121c
5
5
  SHA512:
6
- metadata.gz: a95f1a9d85e9ee5313f2251801c64bf026f21b7fd5c2f18ba4f4627481c311b0f63bbfdbfe51db6b66b770ca154dcdc2dc6eff872dfb7e0ffc24f3b0adde1777
7
- data.tar.gz: be940b88650dd017a2bdb66b86e7c6f6e6b326830572c60249e4c00f0d3f393d7e5b3aa6e570df997ee077c37e7e138de9d2e3a30306497c6b65ecf4ed054632
6
+ metadata.gz: e3fcde7cb8fef76c77de741c35d2d0eb6dc70ac95ce27fe6a782506c67358e5a0fb16a0798a29778e4d898b8c985a732a0cb6ca1657fe7ffa07bd21b3de1b17b
7
+ data.tar.gz: 29708ba703f28777f05c86482e80af5265b947ac0499bd258896e4077e8af086328d9ec758b7507601f74f0780c1b2409dad005ac653d168b6e0e7cc8d6e8bfc
data/README.md CHANGED
@@ -2,16 +2,37 @@
2
2
 
3
3
  Process key-presses and map escape-sequences to symbols.
4
4
 
5
- Dealing with raw keyboard input is painful because something like cursor-up
6
- can return several different sequences depending on terminal, *and* because
7
- there is no terribly simple algorithm determining what represents the
8
- end of a single sequence. In fact some software relies on key-presses being
9
- slow enough to set a timeout and read character by character.
5
+ E.g instead of getting ASCII 0x03 you'll get `:ctrl_c`
6
+
7
+ Dealing with raw keyboard input is painful because something like
8
+ cursor-up can return several different sequences depending on terminal,
9
+ *and* because there is no terribly simple algorithm determining what
10
+ represents the end of a single sequence. In fact some software relies on
11
+ key-presses being slow enough to set a timeout and read character by
12
+ character.
10
13
 
11
14
  KeyboardMap allows you to handle the reads in whichever way you prefer.
12
15
  It simply provides a simple state machine that will return an array of
13
16
  the keyboard events found so far.
14
17
 
18
+ ### Current state
19
+
20
+ This is currently usable and I rely on it daily in my personal editor,
21
+ which uses this gem for keyboard processing.
22
+
23
+ However there are many missing sequences (PR's welcome), and it's likely
24
+ that it will misreport sequences for certain terminals (PR's also
25
+ welcome), so use with some caution.
26
+
27
+ Eventually it's likely it will need to deal with termcaps etc., but at
28
+ the moment I'm "cheating" and relying on the fact that most modern
29
+ terminals support a mostly shared subset of VT100.
30
+
31
+ You can use the example in examples/example.rb to get an idea of what
32
+ your terminal returns for a given keyboard sequence. If you run into
33
+ problems, please include the output from that when filing an issue,
34
+ combined with your *expected* result.
35
+
15
36
  ## Installation
16
37
 
17
38
  Add this line to your application's Gemfile:
@@ -30,18 +51,56 @@ Or install it yourself as:
30
51
 
31
52
  ## Usage
32
53
 
33
- TODO: Write usage instructions here
54
+ See a full example in `examples/example.rb`, but the basics:
55
+
56
+ ```ruby
57
+ require 'bundler'
58
+ require 'io/console'
59
+ require 'keyboard_map'
60
+
61
+ kb = KeyboardMap.new
62
+
63
+ IO.console.raw do # You want to get individual keypresses.
64
+ loop do
65
+ ch = $stdin.getc
66
+
67
+ # events can include zero or more events.
68
+ # Zero events will happen if the character
69
+ # is part of a compound sequence
70
+
71
+ events = kb.call(ch)
72
+ events.each do |e|
73
+ # Process events here.
74
+ p e
75
+ end
76
+ end
77
+ end
78
+ ```
79
+
80
+ ### But I want to catch "Esc"
81
+
82
+ If you're sure you've read a complete sequence, you can do this
83
+ by passing :finished as a second argument to call, or by calling the
84
+ `#finish` method. You can see this done in `examples/example.rb`
34
85
 
35
86
  ## Development
36
87
 
37
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
88
+ After checking out the repo, run `bin/setup` to install dependencies.
89
+ Then, run `rake spec` to run the tests. You can also run `bin/console`
90
+ for an interactive prompt that will allow you to experiment.
38
91
 
39
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
92
+ To install this gem onto your local machine, run `bundle exec rake
93
+ install`. To release a new version, update the version number in
94
+ `version.rb`, and then run `bundle exec rake release`, which will create
95
+ a git tag for the version, push git commits and tags, and push the `.gem`
96
+ file to [rubygems.org](https://rubygems.org).
40
97
 
41
98
  ## Contributing
42
99
 
43
- Bug reports and pull requests are welcome on GitHub at https://github.com/vidarh/keyboard_map.
100
+ Bug reports and pull requests are welcome on GitHub at
101
+ https://github.com/vidarh/keyboard_map.
44
102
 
45
103
  ## License
46
104
 
47
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
105
+ The gem is available as open source under the terms of the [MIT
106
+ License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,51 @@
1
+
2
+ $: << File.dirname(__FILE__)+"/lib"
3
+ require 'io/console'
4
+ require_relative '../lib/keyboard_map'
5
+
6
+ kb = KeyboardMap.new
7
+
8
+ puts "'q' to quit"
9
+
10
+ at_exit do
11
+ STDOUT.print "\e[?2004l" #Disable bracketed paste
12
+ STDOUT.print "\e[?1000l" #Disable mouse reporting
13
+ end
14
+
15
+ STDOUT.print "\e[?2004h" # Enable bracketed paste
16
+ STDOUT.print "\e[?1000h" # Enable mouse reporting
17
+ STDOUT.print "\e[?1006h" # Enable extended reporting
18
+
19
+ IO.console.raw do
20
+ loop do
21
+ # We use a non-blocking read of multiple characters
22
+ # in the hope of the read returning a complete sequence,
23
+ # which allows us to assume a singular ESC is a single press of
24
+ # the Esc key.
25
+ #
26
+ # If you don't need/care about Esc, you can replace the below
27
+ # with ch = $stdin.getc and omit the `:finished` argument passed to
28
+ # `call` below.
29
+ #
30
+ begin
31
+ ch = $stdin.read_nonblock(32)
32
+ rescue IO::WaitReadable
33
+ IO.select([$stdin])
34
+ retry
35
+ end
36
+ print "\rRaw: #{ch.inspect}\n\r"
37
+ r = kb.call(ch, :finished)
38
+ r.each do |ev|
39
+ case ev
40
+ when KeyboardMap::Event
41
+ puts "Event: #{ev.inspect}"
42
+ print "\r"
43
+ puts "Symbol: #{ev.to_sym}"
44
+ else
45
+ print "Text: #{ev}\n\r"
46
+ end
47
+ end
48
+ print "\r"
49
+ break if r.first == "q"
50
+ end
51
+ end
data/keyboard_map.gemspec CHANGED
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
23
23
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
24
  spec.require_paths = ["lib"]
25
25
 
26
- spec.add_development_dependency "bundler", "~> 1.16"
26
+ spec.add_development_dependency "bundler", "~> 2"
27
27
  spec.add_development_dependency "rake", "~> 10.0"
28
28
  spec.add_development_dependency "rspec", "~> 3.0"
29
29
  end
@@ -1,3 +1,3 @@
1
1
  class KeyboardMap
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/keyboard_map.rb CHANGED
@@ -5,7 +5,7 @@ require "keyboard_map/version"
5
5
  require 'set'
6
6
 
7
7
  class KeyboardMap
8
- attr_reader :buf
8
+ attr_reader :buf, :state
9
9
 
10
10
  class Event
11
11
  attr_reader :modifiers,:key, :args
@@ -16,8 +16,12 @@ class KeyboardMap
16
16
  @args = args
17
17
  end
18
18
 
19
+ def to_s
20
+ (modifiers.to_a.sort << key).join("_")
21
+ end
22
+
19
23
  def to_sym
20
- (modifiers.to_a.sort << key).join("_").to_sym
24
+ to_s.to_sym
21
25
  end
22
26
 
23
27
  def ==(ev)
@@ -49,16 +53,19 @@ class KeyboardMap
49
53
  "E" => :keypad_5,
50
54
  "F" => :end,
51
55
  "H" => :home,
52
- "P" => :f1,
56
+ "J" => :ctrl_end, # st reports this
57
+ "L" => :ctrl_insert, # st reports this
58
+ "P" => :delete,
53
59
  "Q" => :f2,
54
60
  "R" => :f3,
55
- "S" => :f4,
61
+ "S" => :f4
56
62
  }.freeze
57
63
 
58
64
  # \e[{parameter1}{;...}~ from parameter1 => key
59
65
  CSI_TILDE_MAP = {
60
66
  "2" => :insert,
61
67
  "3" => :delete,
68
+ "4" => :end, # st reports this
62
69
  "5" => :page_up,
63
70
  "6" => :page_down,
64
71
  "11" => :f1,
@@ -86,19 +93,30 @@ class KeyboardMap
86
93
  @@key_events[k]
87
94
  end
88
95
 
96
+ def event(key, *modifiers)
97
+ self.class.event(key,*modifiers)
98
+ end
99
+
89
100
  # Map of simple/non-parameterised escape sequences to symbols
90
101
  ESCAPE_MAP = {
91
102
  "\e[Z" => event(:tab,:shift),
92
103
  "\eOP" => :f1,
93
104
  "\eOQ" => :f2,
94
105
  "\eOR" => :f3,
95
- "\eOS" => :f4
106
+ "\eOS" => :f4,
107
+ "\e[M" => event(:delete,:ctrl), # st reports this
108
+ "\e[4h" => :insert # st reports this
96
109
  }.freeze
97
110
 
98
111
  CSI_FINAL_BYTE = 0x40..0x7e
99
112
 
100
113
  def meta(key)
101
- self.class.event(key,:meta)
114
+ mod = [:meta]
115
+ if key.ord < 32
116
+ mod << :ctrl
117
+ key = (key.ord+96).chr
118
+ end
119
+ self.class.event(key,*mod)
102
120
  end
103
121
 
104
122
  ESC = "\e"
@@ -109,9 +127,13 @@ class KeyboardMap
109
127
  @state = :text
110
128
  end
111
129
 
112
- def call(input)
130
+ def finish
131
+ run || (@state == :esc ? :esc : nil)
132
+ end
133
+
134
+ def call(input, opt = nil)
113
135
  @buf << input
114
- run
136
+ opt == :finished ? finish : run
115
137
  end
116
138
 
117
139
  def map_escape(seq)
@@ -184,7 +206,7 @@ class KeyboardMap
184
206
  elsif ch == "\t"
185
207
  @state = :text
186
208
  @tmp = ""
187
- return meta(:tab)
209
+ return event(:tab, :meta)
188
210
  elsif ch == "\e"
189
211
  return :esc
190
212
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: keyboard_map
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vidar Hokstad
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-11-11 00:00:00.000000000 Z
11
+ date: 2022-10-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.16'
19
+ version: '2'
20
20
  type: :development
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: '1.16'
26
+ version: '2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -68,6 +68,7 @@ files:
68
68
  - Rakefile
69
69
  - bin/console
70
70
  - bin/setup
71
+ - example/example.rb
71
72
  - keyboard_map.gemspec
72
73
  - lib/keyboard_map.rb
73
74
  - lib/keyboard_map/version.rb