mac-event-monitor 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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