Class: VER::Event

Inherits:
Struct
  • Object
show all
Defined in:
lib/ver/event.rb

Overview

This is used to represent events. It also represents the minimum required properties for events.

The reason we don’t use the default Tk::Event::Data is to compensate for differences of platforms and to have correct values for the event even if we generate them in specs.

Constant Summary collapse

PATTERN =
{}
UNICODE =
Hash.new{|h,k| h[k] = Set.new }
KEYSYM =
Hash.new{|h,k| h[k] = Set.new }
MODIFIERS =
%w[
Control Mod1 M1 Command Alt Mod2 M2 Option Shift Mod3 M3 Lock Mod4 M4
Extended Mod5 M5 Button1 B1 Meta M Button2 B2 Double Button3 B3 Triple
Button4 B4 Quadruple Button5 B5 ]
DETAILS =
%w[
Activate Destroy Map ButtonPress Button Enter MapRequest ButtonRelease
Expose Motion Circulate FocusIn MouseWheel CirculateRequest FocusOut
Property Colormap Gravity Reparent Configure KeyPress Key ResizeRequest
ConfigureRequest KeyRelease Unmap Create Leave Visibility Deactivate ]
MODIFIERS_ABBREV =
MODIFIERS.abbrev
DETAILS_ABBREV =
DETAILS.abbrev
PATTERN_ALIAS =
{}

Constants inherited from Struct

Struct::CACHE

Instance Attribute Summary collapse

Class Method Summary collapse

Methods inherited from Struct

new

Instance Attribute Details

#keysymObject

Returns the value of attribute keysym

Returns:

  • (Object)

    the current value of keysym



8
9
10
# File 'lib/ver/event.rb', line 8

def keysym
  @keysym
end

#patternObject

Returns the value of attribute pattern

Returns:

  • (Object)

    the current value of pattern



8
9
10
# File 'lib/ver/event.rb', line 8

def pattern
  @pattern
end

#unicodeObject

Returns the value of attribute unicode

Returns:

  • (Object)

    the current value of unicode



8
9
10
# File 'lib/ver/event.rb', line 8

def unicode
  @unicode
end

Class Method Details

.[](string) ⇒ Object

given a <pattern>, this returns the event for this pattern. given a unicode char, returns the event with shortest pattern.



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/ver/event.rb', line 33

def self.[](string)
  pattern = nil

  case string
  when /^<(.*)>$/ # an actual or virtual pattern
    pattern = expand_pattern(string)
    PATTERN.fetch(pattern)
  when /^[^-]{2,}$/ # keysym
    pattern = "<#{string}>"
    KEYSYM.fetch(string)
  when /^.$/ # single unicode char
    pattern = "<#{string}>"
    UNICODE.fetch(string).min_by{|event| event.pattern.size }
  end
rescue KeyError => ex
  capture(pattern || string)
end

.add_alias(from, to = {}) ⇒ Object



322
323
324
325
326
327
328
329
330
331
332
333
# File 'lib/ver/event.rb', line 322

def self.add_alias(from, to = {})
  fsym = from[:keysym] || from[:fsym] || raise(ArgumentError, "Need keysym")
  tsym = to[  :keysym] || from[:tsym] || raise(ArgumentError, "Need keysym")
  fpat = from[:pattern] || from[:fpat] || "<#{fsym}>"
  tpat = to[  :pattern] || from[:tpat] || "<#{tsym}>"
  unicode = from[:unicode] || to[:unicode] || ''

  event = pattern(tpat, tsym, unicode)
  PATTERN[fpat] = event
  KEYSYM[fsym] << event
  PATTERN_ALIAS[fpat] = tpat
end

.capture(pattern) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/ver/event.rb', line 107

def self.capture(pattern)
  l "capturing %p" % [pattern]
  @entry ||= setup_capture
  @pending += 1

  @entry.bind(pattern){|event|
    keysym, unicode = event.keysym, event.unicode
    l "Received: %p" % [[pattern, keysym, unicode]]

    if pattern =~ /^<(.*)>$/
      pattern(pattern, keysym, unicode)
    elsif pattern.size == 1
      pattern("<#{keysym}>", keysym, unicode)
    else
      raise "Don't know how to handle: %p" % [event]
    end

    Tk.callback_break
  }

  @entry.value = ""

  send_pattern = if Platform.mac?
                   pattern
                 else
                   pattern.gsub(/\bAlt\b/, 'M1')
                 end

  counter = 0
  until self.key?(pattern)
    Tk::Event.generate(@entry, send_pattern, when: :now)
    Tk.update
    counter += 1

    # just try a couple of times before annoying the user.
    if counter == 10
      @entry.value = "Please press %p" % [pattern]
      @entry.focus
    end
  end

  @progress.step
  @pending -= 1
  self[pattern]
end

.done_yet?Boolean

Returns:

  • (Boolean)


174
175
176
177
178
179
# File 'lib/ver/event.rb', line 174

def self.done_yet?
  return if !@toplevel || @pending > 0
  persist!
  @toplevel.destroy
  @toplevel = @entry = @label = @progress = nil
end

.each(&block) ⇒ Object



13
14
15
# File 'lib/ver/event.rb', line 13

def self.each(&block)
  PATTERN.values.each(&block)
end

.expand_pattern(given_pattern) ⇒ Object

pattern has the form of <modifier-modifier-type-detail> This may be shortened down to <detail>. Here we expand short forms of modifier and type. Short modifiers take precedence over short types, as they are way more used.



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/ver/event.rb', line 90

def self.expand_pattern(given_pattern)
  *parts, detail = given_pattern[1..-2].split('-')

  if parts.empty?
    PATTERN_ALIAS.fetch(given_pattern, given_pattern)
  else
    parts.map! do |part|
      MODIFIERS_ABBREV[part] || DETAILS_ABBREV[part] || part
    end

    aliased_detail = PATTERN_ALIAS.fetch(detail, detail)
    inner = (parts << aliased_detail).join('-')
    pattern = "<#{inner}>"
    PATTERN_ALIAS.fetch(pattern, pattern)
  end
end

.fake(alias_pattern, keysym, unicode = '') ⇒ Object



25
26
27
28
29
# File 'lib/ver/event.rb', line 25

def self.fake(alias_pattern, keysym, unicode = '')
  event = pattern("<#{alias_pattern}>", keysym, unicode)
  MODIFIERS.each{|mod| pattern("<#{mod}-#{alias_pattern}>", keysym, unicode) }
  event
end

.key?(string) ⇒ Boolean

Returns:

  • (Boolean)


51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/ver/event.rb', line 51

def self.key?(string)
  if string =~ /^<(.*)>$/
    PATTERN.fetch(string)
  elsif string.size == 1
    UNICODE.fetch(string).min_by{|event| event.pattern.size }
  else # it may be keysym, but let's try to make it pattern instead
    PATTERN.fetch("<#{string}>")
  end

  true
rescue KeyError
  false
end

.load!Object



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/ver/event.rb', line 193

def self.load!
  path = persist_location
  l "Attempt to load event patterns from #{path}"

  path.open 'rb' do |io|
    pattern = Marshal.load(io.read)
    pattern.each do |sym, event|
      PATTERN[event.pattern] = event
      UNICODE[event.unicode] << event
      KEYSYM[event.keysym] << event
    end
  end

  load_aliases
  l "Event patterns loaded"
rescue Errno::ENOENT
  # Mostly harmless, the .events file isn't created yet.
  l "Attempt to load event patterns failed"
  load_default
  load_aliases
end

.load_aliasesObject

Compatibility between windowingsystems



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/ver/event.rb', line 292

def self.load_aliases
  if Platform.unix? && Platform.x11?
    # Shift-Fn is handled differently, we alias them to F12+n
    # So if you map <F13>, it works, do not use <Shift-F1> or
    # <XF86_Switch_VT_1> in the keymap itself.
    1.upto 12 do |n|
      add_alias(fsym: "F#{12 + n}", tsym: "XF86_Switch_VT_#{n}")
    end
  else
    # Here we map all Shift-Fn keys to a F12+n pattern.
    1.upto 12 do |n|
      add_alias(fsym: "F#{12 + n}", tsym: "F#{n}", tpat: "<Shift-F#{n}>")
    end
  end

  if Platform.mac?
    pattern '<Shift-Insert>', 'Shift-Insert', ''
    if Platform.x11?
      pattern '<Shift-Left>',  'Left',  ''
      pattern '<Shift-Right>', 'Right', ''
      pattern '<Shift-Control-Left>', 'Shift-Control-Left', "\uffe1"
      pattern '<Shift-Control-Right>', 'Shift-Control-Right', "\uffe2"
      pattern '<Shift-Down>', 'Down', "\uff54"
      pattern '<Shift-Up>',   'Up',   "\uff52"
      pattern '<ISO_Left_Tab>', 'Tab', "\t"
    end
  end
end

.load_defaultObject



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/ver/event.rb', line 215

def self.load_default
  # To make creation easier, we define well-known patterns for ASCII here.
  # The rest is still to be defined by actual events.
  ('0'..'9').each{|chr| pattern("<Key-#{chr}>", chr, chr) }
  ('A'..'Z').each{|chr| pattern(chr, chr, chr) }
  ('a'..'z').each{|chr| pattern(chr, chr, chr) }
  keysyms = {
    'space'        => ' ',
    'exclam'       => '!',
    'quotedbl'     => '"',
    'numbersign'   => '#',
    'dollar'       => '$',
    'percent'      => '%',
    'ampersand'    => '&',
    'parenleft'    => '(',
    'parenright'   => ')',
    'asterisk'     => '*',
    'plus'         => '+',
    'comma'        => ',',
    'minus'        => '-',
    'period'       => '.',
    'slash'        => '/',
    'colon'        => ':',
    'semicolon'    => ';',
    'less'         => '<',
    'equal'        => '=',
    'greater'      => '>',
    'question'     => '?',
    'at'           => '@',
    'bracketleft'  => '[',
    'backslash'    => '\\',
    'bracketright' => ']',
    'asciicircum'  => '^',
    'underscore'   => '_',
    'quoteright'   => '`',
    'braceleft'    => '{',
    'bar'          => '|',
    'braceright'   => '}',
    'asciitilde'   => '~',

    # Now a couple of basic keys that don't change across platforms
    'BackSpace'    => "\b",
    'Delete'       => "\x7F",
    'Down'         => '',
    'End'          => '',
    'Escape'       => "\e",
    'Home'         => '',
    'Insert'       => '',
    'Left'         => '',
    'Next'         => '',
    'Prior'        => '',
    'Return'       => "\n",
    'Right'        => '',
    'Tab'          => "\t",
    'Up'           => '',
  }

  if Platform.x11?
    keysyms.merge!({
      'apostrophe'   => "'",
      'grave'        => '`',
    })
  end

  keysyms.each do |keysym, unicode|
    pattern("<#{keysym}>", keysym, unicode)
  end

  # F1-F12
  1.upto(12){|n| pattern("<F#{n}>", "F#{n}", '') }

  # Something for german keyboards
  pattern('<Control-bracketleft>', 'bracketleft', '[')
  pattern('<Control-bracketright>', 'bracketright', ']')
end

.pattern(pattern, keysym, unicode) ⇒ Object



17
18
19
20
21
22
23
# File 'lib/ver/event.rb', line 17

def self.pattern(pattern, keysym, unicode)
  event = new(pattern, keysym, unicode)
  PATTERN[event.pattern] = event
  UNICODE[event.unicode] << event
  KEYSYM[event.keysym] << event
  event
end

.persist!Object



185
186
187
188
189
190
191
# File 'lib/ver/event.rb', line 185

def self.persist!
  path = persist_location
  l "Persisting event patterns to #{path}"
  path.open 'wb+' do |io|
    io.write(Marshal.dump(PATTERN))
  end
end

.persist_locationObject



181
182
183
# File 'lib/ver/event.rb', line 181

def self.persist_location
  VER.options.home_conf_dir/'.events'
end

.setup_captureObject



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/ver/event.rb', line 153

def self.setup_capture
  @toplevel = Tk::Toplevel.new
  @label = Tk::Tile::Label.new(
    @toplevel,
    text: "Detected updates to keymap, this may take a few seconds..."
  )
  @entry = Tk::Entry.new(@toplevel)
  @progress = Tk::Tile::Progressbar.new(
    @toplevel,
    orient: :horizontal,
    mode: :indeterminate
  )

  @pending = 0
  @label.pack fill: :x
  @progress.pack fill: :x
  @entry.pack fill: :x
  @entry.bind('<Map>'){ @entry.focus }
  @entry
end