mac-event-monitor 0.1.0 → 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.
data/README.md CHANGED
@@ -9,13 +9,26 @@ A Library to Monitor User Interactions
9
9
  ## Usage
10
10
 
11
11
  require 'mac-event-monitor'
12
-
12
+
13
13
  monitor = Mac::EventMonitor::Monitor.new
14
+
15
+ ### Monitor Mouse Event
16
+
14
17
  monitor.add_listener(:mouse_down) do |event|
15
18
  puts [event.location.x, event.location.y].join(',')
16
19
  end
17
20
  monitor.run
18
21
 
22
+ ### Monitor Keyboard Event
23
+
24
+ You need to enable "Access to assistive devices" in the Universal Access preference pane to monitor keyboard event.
25
+
26
+ monitor.add_listener(:key_down) do |event|
27
+ p event.keycode
28
+ p event.shift_key?
29
+ end
30
+ monitor.run
31
+
19
32
  ## Contributing
20
33
 
21
34
  1. Fork it
@@ -1,4 +1,4 @@
1
- #include <ruby.h>
1
+ #include <ruby/ruby.h>
2
2
  #import <AppKit/AppKit.h>
3
3
 
4
4
  @interface EventMonitorAppDelegate : NSObject <NSApplicationDelegate> {
@@ -29,6 +29,7 @@ NSEventMask mask =
29
29
  NSEvent *event;
30
30
 
31
31
  [NSEvent removeMonitor:[timer userInfo]];
32
+ [timer invalidate];
32
33
  [timer release];
33
34
 
34
35
  [[NSApplication sharedApplication] stop:nil];
@@ -49,6 +50,22 @@ NSEventMask mask =
49
50
 
50
51
  @end
51
52
 
53
+ double getScreenHeight()
54
+ {
55
+ NSScreen *mainScreen = [NSScreen mainScreen];
56
+ NSScreen *firstScreen = [[NSScreen screens] objectAtIndex:0];
57
+
58
+ NSRect screenRect = [mainScreen visibleFrame];
59
+ double screenHeight = NSHeight(screenRect);
60
+
61
+ // http://stackoverflow.com/questions/3163343/how-can-i-detemine-which-screen-holds-the-menubar
62
+ if(mainScreen == firstScreen) {
63
+ screenHeight += 22;
64
+ }
65
+
66
+ return screenHeight;
67
+ }
68
+
52
69
  static VALUE rb_cMonitor;
53
70
 
54
71
  static VALUE cMonitor_run_app(int argc, VALUE *argv, VALUE self)
@@ -66,7 +83,7 @@ static VALUE cMonitor_run_app(int argc, VALUE *argv, VALUE self)
66
83
 
67
84
  eventMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:mask
68
85
  handler:^(NSEvent *incomingEvent) {
69
- rb_funcall(self, rb_intern("receive_event"), 1, rb_str_new2([[incomingEvent description] UTF8String]));
86
+ rb_funcall(self, rb_intern("receive_event"), 2, rb_str_new2([[incomingEvent description] UTF8String]), rb_float_new(getScreenHeight()));
70
87
  }];
71
88
 
72
89
  if(stopAfter != Qnil) {
@@ -3,8 +3,8 @@ module Mac
3
3
  class KeyboardEvent < Event
4
4
  attr_reader :keycode, :flags
5
5
 
6
- def initialize(type, keycode, flags)
7
- super(type)
6
+ def initialize(type, time, location, keycode, flags)
7
+ super(type, time, location)
8
8
 
9
9
  @keycode = keycode.to_i
10
10
  @flags = flags.to_i(16)
@@ -38,6 +38,10 @@ module Mac
38
38
  check_flag(23)
39
39
  end
40
40
 
41
+ def data
42
+ super + [keycode, flags.to_s(16)]
43
+ end
44
+
41
45
  private
42
46
 
43
47
  def check_flag(shift)
@@ -1,19 +1,16 @@
1
1
  module Mac
2
2
  module EventMonitor
3
3
  class MouseEvent < Event
4
- attr_reader :location, :button
4
+ attr_reader :button
5
5
 
6
- def initialize(type, location_as_str, button)
7
- super(type)
6
+ def initialize(type, time, location, button)
7
+ super(type, time, location)
8
8
 
9
- @location = parse_location(location_as_str)
10
- @button = button
9
+ @button = button && button.to_sym
11
10
  end
12
11
 
13
- private
14
-
15
- def parse_location(location_as_str)
16
- Struct.new(:x, :y).new(*location_as_str.scan(/[\d\.]+/).map {|v| v.to_f })
12
+ def data
13
+ super + [button]
17
14
  end
18
15
  end
19
16
  end
@@ -1,10 +1,12 @@
1
1
  module Mac
2
2
  module EventMonitor
3
3
  class Event
4
- attr_reader :type
4
+ include JsonSerializable
5
+
6
+ attr_reader :type, :time, :location
5
7
 
6
8
  class << self
7
- def create_from_description(description)
9
+ def create_from_description(description, screen_height)
8
10
  _, *atts_as_string = description.split(/ +/)
9
11
 
10
12
  attrs = (atts_as_string.join(' ') + ' ').scan(/([^=]+)=([^=]+) (?=\w?)/).inject({}) do |result, pair|
@@ -13,37 +15,52 @@ module Mac
13
15
  result
14
16
  end
15
17
 
18
+ location = parse_location(attrs[:loc])
19
+ location.y = screen_height - location.y
20
+
21
+ time = attrs[:time].to_f
22
+
16
23
  case attrs[:type]
17
24
  when 'LMouseUp'
18
- MouseEvent.new(:mouse_up, attrs[:loc], :left)
25
+ MouseEvent.new(:mouse_up, time, location, :left)
19
26
  when 'RMouseUp'
20
- MouseEvent.new(:mouse_up, attrs[:loc], :right)
27
+ MouseEvent.new(:mouse_up, time, location, :right)
21
28
  when 'OMouseUp'
22
- MouseEvent.new(:mouse_up, attrs[:loc], :other)
29
+ MouseEvent.new(:mouse_up, time, location, :other)
23
30
  when 'LMouseDown'
24
- MouseEvent.new(:mouse_down, attrs[:loc], :left)
31
+ MouseEvent.new(:mouse_down, time, location, :left)
25
32
  when 'RMouseDown'
26
- MouseEvent.new(:mouse_down, attrs[:loc], :right)
33
+ MouseEvent.new(:mouse_down, time, location, :right)
27
34
  when 'OMouseDown'
28
- MouseEvent.new(:mouse_down, attrs[:loc], :other)
35
+ MouseEvent.new(:mouse_down, time, location, :other)
29
36
  when 'MouseMoved'
30
- MouseEvent.new(:mouse_move, attrs[:loc], nil)
37
+ MouseEvent.new(:mouse_move, time, location, nil)
31
38
  when 'LMouseDragged'
32
- MouseEvent.new(:mouse_drag, attrs[:loc], :left)
39
+ MouseEvent.new(:mouse_drag, time, location, :left)
33
40
  when 'RMouseDragged'
34
- MouseEvent.new(:mouse_drag, attrs[:loc], :right)
41
+ MouseEvent.new(:mouse_drag, time, location, :right)
35
42
  when 'OMouseDragged'
36
- MouseEvent.new(:mouse_drag, attrs[:loc], :other)
43
+ MouseEvent.new(:mouse_drag, time, location, :other)
37
44
  when 'KeyDown'
38
- KeyboardEvent.new(:key_down, attrs[:keyCode], attrs[:flags])
45
+ KeyboardEvent.new(:key_down, time, location, attrs[:keyCode], attrs[:flags])
39
46
  when 'KeyUp'
40
- KeyboardEvent.new(:key_up, attrs[:keyCode], attrs[:flags])
47
+ KeyboardEvent.new(:key_up, time, location, attrs[:keyCode], attrs[:flags])
41
48
  end
42
49
  end
50
+
51
+ def parse_location(location_as_str)
52
+ Location.new(*location_as_str.scan(/[\d\.]+/).map {|v| v.to_f })
53
+ end
54
+ end
55
+
56
+ def initialize(type, time, location)
57
+ @type = type.to_sym
58
+ @time = time
59
+ @location = location
43
60
  end
44
61
 
45
- def initialize(type)
46
- @type = type
62
+ def data
63
+ [type, time, location]
47
64
  end
48
65
  end
49
66
  end
@@ -0,0 +1,18 @@
1
+ module Mac
2
+ module EventMonitor
3
+ class Location
4
+ include JsonSerializable
5
+
6
+ attr_accessor :x, :y
7
+
8
+ def initialize(x, y)
9
+ @x = x
10
+ @y = y
11
+ end
12
+
13
+ def data
14
+ [x, y]
15
+ end
16
+ end
17
+ end
18
+ end
@@ -2,24 +2,33 @@ module Mac
2
2
  module EventMonitor
3
3
  class Monitor
4
4
  def initialize
5
- @listeners = {}
5
+ @listeners = {}
6
+ @any_listeners = []
6
7
  end
7
8
 
8
- def add_listener(type, &block)
9
- @listeners[type] ||= []
10
- @listeners[type] << block
9
+ def add_listener(type = nil, &block)
10
+ if type
11
+ @listeners[type] ||= []
12
+ @listeners[type] << block
13
+ else
14
+ @any_listeners << block
15
+ end
11
16
  end
12
17
 
13
18
  def run(stop_after = nil)
14
19
  run_app(stop_after)
15
20
  end
16
21
 
17
- def receive_event(str)
18
- event = Event.create_from_description(str)
22
+ def receive_event(str, screen_height)
23
+ event = Event.create_from_description(str, screen_height)
19
24
 
20
25
  (@listeners[event.type] || []).each do |block|
21
26
  block.call(event)
22
27
  end
28
+
29
+ @any_listeners.each do |block|
30
+ block.call(event)
31
+ end
23
32
  end
24
33
  end
25
34
  end
@@ -0,0 +1,22 @@
1
+ require 'json'
2
+
3
+ module Mac
4
+ module EventMonitor
5
+ module JsonSerializable
6
+ def self.included(klass)
7
+ klass.instance_eval do
8
+ def json_create(o)
9
+ new(*o['data'])
10
+ end
11
+ end
12
+ end
13
+
14
+ def to_json(*a)
15
+ {
16
+ 'json_class' => self.class.name,
17
+ 'data' => data
18
+ }.to_json(*a)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,5 +1,5 @@
1
1
  module Mac
2
2
  module EventMonitor
3
- VERSION = "0.1.0"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
@@ -6,7 +6,9 @@ module Mac
6
6
  end
7
7
 
8
8
  require 'event_monitor'
9
+ require 'mac-event-monitor/serializable.rb'
9
10
  require 'mac-event-monitor/monitor.rb'
10
11
  require 'mac-event-monitor/event.rb'
11
12
  require 'mac-event-monitor/event/mouse_event.rb'
12
13
  require 'mac-event-monitor/event/keyboard_event.rb'
14
+ require 'mac-event-monitor/location.rb'
@@ -16,7 +16,9 @@ Gem::Specification.new do |gem|
16
16
  gem.require_paths = ["lib"]
17
17
  gem.version = Mac::EventMonitor::VERSION
18
18
 
19
+ gem.add_dependency('json')
19
20
  gem.add_development_dependency('rspec', ['~> 2.8.0'])
20
21
  gem.add_development_dependency('mac-robot')
21
22
  gem.add_development_dependency('eventmachine')
23
+ gem.add_development_dependency('rake')
22
24
  end
@@ -1,24 +1,59 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
+ require 'json'
4
+
3
5
  include Mac::EventMonitor
4
6
 
5
7
  describe Event do
6
- it 'should create event from description' do
7
- event = Event.create_from_description('NSEvent: type=LMouseDown loc=(618,524) time=1136789.4 flags=0x100 win=0x0 winNum=17310 ctxt=0x0 evNum=14883 click=1 buttonNumber=0 pressure=1')
8
-
9
- event.should be_an_instance_of(MouseEvent)
10
- event.type.should equal(:mouse_down)
11
- event.button.should equal(:left)
12
- event.location.x.should be_an_instance_of(Float)
13
- event.location.y.should be_an_instance_of(Float)
8
+ describe '.create_from_description' do
9
+ it 'should create event from description' do
10
+ event = Event.create_from_description('NSEvent: type=LMouseDown loc=(618,524) time=1136789.4 flags=0x100 win=0x0 winNum=17310 ctxt=0x0 evNum=14883 click=1 buttonNumber=0 pressure=1', 768)
11
+
12
+ event.should be_an_instance_of(MouseEvent)
13
+ event.type.should equal(:mouse_down)
14
+ event.button.should equal(:left)
15
+ event.location.x.should eql(618.0)
16
+ event.location.y.should eql(244.0)
17
+ event.time.should eql(1136789.4)
18
+ end
19
+
20
+ it 'should create event from description with space' do
21
+ event = Event.create_from_description('NSEvent: type=KeyDown loc=(184,646) time=68067.5 flags=0x20104 win=0x0 winNum=0 ctxt=0x0 chars=" " unmodchars=" " repeat=0 keyCode=49', 768)
22
+
23
+ event.should be_an_instance_of(KeyboardEvent)
24
+ event.type.should equal(:key_down)
25
+ event.keycode.should equal(49)
26
+ event.shift_key?.should be_true
27
+ event.location.x.should eql(184.0)
28
+ event.location.y.should eql(122.0)
29
+ event.time.should eql(68067.5)
30
+ end
14
31
  end
15
32
 
16
- it 'should create event from description with space' do
17
- event = Event.create_from_description('NSEvent: type=KeyDown loc=(184,646) time=68067.5 flags=0x20104 win=0x0 winNum=0 ctxt=0x0 chars=" " unmodchars=" " repeat=0 keyCode=49')
33
+ describe '#to_json' do
34
+ it 'serializes mouse event to json' do
35
+ event = Event.create_from_description('NSEvent: type=LMouseDown loc=(618,524) time=1136789.4 flags=0x100 win=0x0 winNum=17310 ctxt=0x0 evNum=14883 click=1 buttonNumber=0 pressure=1', 768)
36
+ event = JSON.parse(event.to_json)
37
+
38
+ event.should be_an_instance_of(MouseEvent)
39
+ event.type.should equal(:mouse_down)
40
+ event.button.should equal(:left)
41
+ event.location.x.should eql(618.0)
42
+ event.location.y.should eql(244.0)
43
+ event.time.should eql(1136789.4)
44
+ end
45
+
46
+ it 'serializes mouse event to json' do
47
+ event = Event.create_from_description('NSEvent: type=KeyDown loc=(184,646) time=68067.5 flags=0x20104 win=0x0 winNum=0 ctxt=0x0 chars=" " unmodchars=" " repeat=0 keyCode=49', 768)
48
+ event = JSON.parse(event.to_json)
18
49
 
19
- event.should be_an_instance_of(KeyboardEvent)
20
- event.type.should equal(:key_down)
21
- event.keycode.should equal(49)
22
- event.shift_key?.should be_true
50
+ event.should be_an_instance_of(KeyboardEvent)
51
+ event.type.should equal(:key_down)
52
+ event.keycode.should equal(49)
53
+ event.shift_key?.should be_true
54
+ event.location.x.should eql(184.0)
55
+ event.location.y.should eql(122.0)
56
+ event.time.should eql(68067.5)
57
+ end
23
58
  end
24
59
  end
@@ -50,7 +50,6 @@ describe Monitor do
50
50
  [1, 1.5].each do |t|
51
51
  EM.add_timer(t) do
52
52
  robot.key_press(0x04)
53
- #robot.key_release(0x04)
54
53
  end
55
54
  end
56
55
 
@@ -65,4 +64,39 @@ describe Monitor do
65
64
 
66
65
  result.should be >= 2
67
66
  end
67
+
68
+ it 'should monitor all' do
69
+ result = 0
70
+ robot = Mac::Robot.new
71
+
72
+ @monitor.add_listener do |event|
73
+ result += 1
74
+ end
75
+
76
+ result.should be_zero
77
+
78
+ EM.run do
79
+ [1, 1.5].each do |t|
80
+ EM.add_timer(t) do
81
+ robot.mouse_press
82
+ end
83
+ end
84
+
85
+ [2, 2.5].each do |t|
86
+ EM.add_timer(t) do
87
+ robot.key_press(0x04)
88
+ end
89
+ end
90
+
91
+ EM.add_timer(3) do
92
+ EM.stop
93
+ end
94
+
95
+ EM.add_periodic_timer(0.1) do
96
+ @monitor.run(0.1)
97
+ end
98
+ end
99
+
100
+ result.should be >= 4
101
+ end
68
102
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mac-event-monitor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,8 +9,24 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-18 00:00:00.000000000 Z
12
+ date: 2013-03-06 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: json
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
14
30
  - !ruby/object:Gem::Dependency
15
31
  name: rspec
16
32
  requirement: !ruby/object:Gem::Requirement
@@ -59,6 +75,22 @@ dependencies:
59
75
  - - ! '>='
60
76
  - !ruby/object:Gem::Version
61
77
  version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: rake
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
62
94
  description: A Library to Monitor User Interactions
63
95
  email:
64
96
  - youpy@buycheapviagraonlinenow.com
@@ -81,7 +113,9 @@ files:
81
113
  - lib/mac-event-monitor/event.rb
82
114
  - lib/mac-event-monitor/event/keyboard_event.rb
83
115
  - lib/mac-event-monitor/event/mouse_event.rb
116
+ - lib/mac-event-monitor/location.rb
84
117
  - lib/mac-event-monitor/monitor.rb
118
+ - lib/mac-event-monitor/serializable.rb
85
119
  - lib/mac-event-monitor/version.rb
86
120
  - mac-event-monitor.gemspec
87
121
  - spec/mac-event-monitor/event_spec.rb
@@ -101,7 +135,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
101
135
  version: '0'
102
136
  segments:
103
137
  - 0
104
- hash: -3347223318557235811
138
+ hash: 420514610567595612
105
139
  required_rubygems_version: !ruby/object:Gem::Requirement
106
140
  none: false
107
141
  requirements:
@@ -110,7 +144,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
110
144
  version: '0'
111
145
  segments:
112
146
  - 0
113
- hash: -3347223318557235811
147
+ hash: 420514610567595612
114
148
  requirements: []
115
149
  rubyforge_project:
116
150
  rubygems_version: 1.8.24