macos 0.1.1 → 0.1.3

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: bd5cbbc912f0fab0192641cb14735ebbd151c1db00381dc1f837174a2dc2bff2
4
- data.tar.gz: 30c5de93807739ead6fc84a5fdc899bf6041b5c9946f6b4f21961704d808ecda
3
+ metadata.gz: f5f7eea4e2dd2200b9cf31edba5eeafb649c61f04c3ce97977e3b2b9eb7ac9ab
4
+ data.tar.gz: 3a57005b465e9751bf0390fc077a916be95665b8e9b67dc3d2e550063bbd3d57
5
5
  SHA512:
6
- metadata.gz: 974a5ed9a34e8a890179836a5ad80d2fe584b5218d948d390d586fd8fd7df04d9bd303e248e30020f893675236b4f94ab53697f3faebd0a8ca64a6c02dbd0621
7
- data.tar.gz: 766c1ea410a4f9e7ca8e7d05b2e5eddb115932c41356c8dfce44020558a68d3ede27cd9237449173b2a1e87656e8d3f12d18100f57d5c90e6a4c69a604b52b7e
6
+ metadata.gz: f5bbc37912e6e9e726c4d0f8e0b5315d2a5b1a79ad3d68e4399d575127e88b20b8adb4a1c7081ebf124a0fdb5cab274138882cfdcc6e0b49fa2dc87589dd8663
7
+ data.tar.gz: 2324a3f60635e949c2d90ab30705a91047edbf78b4dbe7b12c8684783d54a8282c74e33fcae98f37be8cbe825b7fcd15b85046872239dbc3005dee8d9c3b1cdc
data/Gemfile CHANGED
@@ -2,11 +2,14 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- # Specify your gem's dependencies in macos.gemspec
6
5
  gemspec
7
6
 
8
- gem "rake", "~> 13.0"
9
-
10
- gem "rspec", "~> 3.0"
11
-
12
- gem "rubocop", "~> 1.21"
7
+ gem "rake"
8
+ gem "rspec"
9
+ gem "rspec_junit_formatter"
10
+ gem "rubocop"
11
+ gem "rubocop-basic"
12
+ gem "rubocop-rake"
13
+ gem "rubocop-rspec"
14
+ gem "simplecov"
15
+ gem "yard"
data/README.md CHANGED
@@ -1,4 +1,10 @@
1
- # MacOS.rb
1
+ # MacOS
2
+
3
+ [![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/ksylvest/macos/blob/main/LICENSE)
4
+ [![RubyGems](https://img.shields.io/gem/v/macos)](https://rubygems.org/gems/macos)
5
+ [![GitHub](https://img.shields.io/badge/github-repo-blue.svg)](https://github.com/ksylvest/macos)
6
+ [![Yard](https://img.shields.io/badge/docs-site-blue.svg)](https://macos.ksylvest.com)
7
+ [![CircleCI](https://img.shields.io/circleci/build/github/ksylvest/macos)](https://circleci.com/gh/ksylvest/macos)
2
8
 
3
9
  A library for interacting with your Mac through ruby.
4
10
 
@@ -10,13 +16,33 @@ gem install macos
10
16
 
11
17
  ## Usage
12
18
 
13
- ### Screen
19
+ ### Mouse
14
20
 
15
21
  ```ruby
16
22
  require 'macos'
17
23
 
18
- screen = MacOS::screen
19
- screen.capture do |tempfile|
24
+ mouse = MacOS.mouse
25
+
26
+ mouse.move(x: 0, y: 0)
27
+ position = mouse.position
28
+ puts "#{position.x},#{position.y}"
29
+
30
+ mouse.move(x: 2, y: 3)
31
+ position = mouse.position
32
+ puts "#{position.x},#{position.y}"
33
+ ```
34
+
35
+ ### Display
36
+
37
+ ```ruby
38
+ require 'macos'
39
+
40
+ display = MacOS.display
41
+ bounds = display.bounds
42
+
43
+ puts "#{bounds.width}×#{bounds.height}"
44
+
45
+ display.capture do |tempfile|
20
46
  # ...
21
47
  end
22
48
  ```
data/bin/demo ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "macos"
6
+
7
+ mouse = MacOS.mouse
8
+
9
+ loop do
10
+ puts mouse.position
11
+
12
+ mouse.move(x: 20.0, y: 20.0)
13
+ sleep 5
14
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MacOS
4
+ module CG
5
+ # https://developer.apple.com/documentation/coregraphics/cgeventtaplocation
6
+ module EventTapLocation
7
+ HID_EVENT_TAP = 0 # https://developer.apple.com/documentation/coregraphics/cgeventtaplocation/cghideventtap
8
+ SESSSION_EVENT_TAP = 1 # https://developer.apple.com/documentation/coregraphics/cgeventtaplocation/sessioneventtap
9
+ ANNOTATED_SESSION_EVENT_TAP = 2 # https://developer.apple.com/documentation/coregraphics/cgeventtaplocation/annotatedsessioneventtap
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MacOS
4
+ module CG
5
+ # https://developer.apple.com/documentation/coregraphics/cgeventtype
6
+ module EventType
7
+ NULL = 0 # https://developer.apple.com/documentation/coregraphics/cgeventtype/null
8
+ LEFT_MOUSE_DOWN = 1 # https://developer.apple.com/documentation/coregraphics/cgeventtype/leftmousedown
9
+ LEFT_MOUSE_UP = 2 # https://developer.apple.com/documentation/coregraphics/cgeventtype/leftmouseup
10
+ RIGHT_MOUSE_DOWN = 3 # https://developer.apple.com/documentation/coregraphics/cgeventtype/rightmousedown
11
+ RIGHT_MOUSE_UP = 4 # https://developer.apple.com/documentation/coregraphics/cgeventtype/rightmouseup
12
+ MOUSE_MOVED = 5 # https://developer.apple.com/documentation/coregraphics/cgeventtype/mousemoved
13
+ LEFT_MOUSE_DRAGGED = 6 # https://developer.apple.com/documentation/coregraphics/cgeventtype/leftmousedragged
14
+ RIGHT_MOUSE_DRAGGED = 7 # https://developer.apple.com/documentation/coregraphics/cgeventtype/rightmousedragged
15
+ KEY_DOWN = 10 # https://developer.apple.com/documentation/coregraphics/cgeventtype/keydown
16
+ KEY_UP = 11 # https://developer.apple.com/documentation/coregraphics/cgeventtype/keyup
17
+ OTHER_MOUSE_DOWN = 25 # https://developer.apple.com/documentation/coregraphics/cgeventtype/othermousedown
18
+ OTHER_MOUSE_UP = 26 # https://developer.apple.com/documentation/coregraphics/cgeventtype/othermouseup
19
+ OTHER_MOUSE_DRAGGED = 27 # https://developer.apple.com/documentation/coregraphics/cgeventtype/othermousedragged
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MacOS
4
+ module CG
5
+ # https://developer.apple.com/documentation/coregraphics/cgmousebutton
6
+ module MouseButton
7
+ LEFT = 0 # https://developer.apple.com/documentation/coregraphics/cgmousebutton/left
8
+ RIGHT = 1 # https://developer.apple.com/documentation/coregraphics/cgmousebutton/right
9
+ CENTER = 2 # https://developer.apple.com/documentation/coregraphics/cgmousebutton/center
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ffi"
4
+
5
+ module MacOS
6
+ module CG
7
+ # https://developer.apple.com/documentation/corefoundation/cgpoint
8
+ class Point < FFI::Struct
9
+ layout :x, :double, :y, :double
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ffi"
4
+
5
+ module MacOS
6
+ module CG
7
+ # https://developer.apple.com/documentation/corefoundation/cgrect
8
+ class Rect < FFI::Struct
9
+ layout :origin, MacOS::CG::Point, :size, MacOS::CG::Size
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ffi"
4
+
5
+ module MacOS
6
+ module CG
7
+ # https://developer.apple.com/documentation/corefoundation/cgsize
8
+ class Size < FFI::Struct
9
+ layout :width, :double, :height, :double
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MacOS
4
+ # Simulates a macOS display.
5
+ class Display
6
+ Size = Struct.new(:width, :height)
7
+
8
+ def self.main
9
+ id = Library.CGMainDisplayID
10
+ new(id:)
11
+ end
12
+
13
+ # @param id [Integer
14
+ def initialize(id:)
15
+ @id = id
16
+ end
17
+
18
+ # @return [Size]
19
+ def bounds
20
+ bounds = Library.CGDisplayBounds(@id)
21
+
22
+ width = Integer(bounds[:size][:width])
23
+ height = Integer(bounds[:size][:height])
24
+
25
+ Size.new(width, height)
26
+ end
27
+
28
+ # @yield [tempfile]
29
+ # @yieldparam tempfile [Tempfile]
30
+ def capture
31
+ tempfile = Tempfile.new(["screenshot", ".png"])
32
+ system("screencapture -o -x #{tempfile.path}")
33
+ yield tempfile
34
+ ensure
35
+ tempfile.close
36
+ tempfile.unlink
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MacOS
4
+ # Simulates a macOS keyboard.
5
+ class Keyboard
6
+ def type(text)
7
+ text.chars.each do |character|
8
+ key_down(character)
9
+ key_up(character)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ffi"
4
+
5
+ module MacOS
6
+ # https://developer.apple.com/documentation/coregraphics
7
+ module Library
8
+ extend ::FFI::Library
9
+ ffi_lib "/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics"
10
+
11
+ attach_function :CGEventCreate, %i[pointer], :pointer
12
+ attach_function :CGEventGetLocation, %i[pointer], CG::Point.by_value
13
+ attach_function :CFRelease, %i[pointer], :void
14
+ attach_function :CGEventCreateMouseEvent, [:pointer, :uint32, CG::Point.by_value, :uint32], :pointer
15
+ attach_function :CGEventPost, %i[uint32 pointer], :void
16
+ attach_function :CGWarpMouseCursorPosition, [CG::Point.by_value], :pointer
17
+ attach_function :CGEventCreateKeyboardEvent, %i[pointer uint16 bool bool], :pointer
18
+ attach_function :CGMainDisplayID, [], :uint32
19
+ attach_function :CGDisplayBounds, [:uint32], CG::Rect.by_value
20
+ end
21
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MacOS
4
+ # Simulates a macOS mouse.
5
+ class Mouse
6
+ Position = Data.define(:x, :y)
7
+
8
+ # @return [Position]
9
+ def position
10
+ event = Library.CGEventCreate(nil)
11
+ position = Library.CGEventGetLocation(event)
12
+ Library.CFRelease(event)
13
+
14
+ x = position[:x]
15
+ y = position[:y]
16
+
17
+ Position.new(x, y)
18
+ end
19
+
20
+ # @param type [Integer] e.g. `MacOS::CG::EventType::MOUSE_MOVED`
21
+ # @param x [Float]
22
+ # @param y [Float]
23
+ # @param button [Integer] e.g. `MacOS::CG::MouseButton::LEFT` / `MacOS::CG::MouseButton::RIGHT` / etc.
24
+ def trigger(type:, x:, y:, button: 0)
25
+ position = CG::Point.new
26
+ position[:x] = x
27
+ position[:y] = y
28
+ event = Library.CGEventCreateMouseEvent(nil, type, position, button)
29
+ Library.CGEventPost(CG::EventTapLocation::HID_EVENT_TAP, event)
30
+ Library.CFRelease(event)
31
+ end
32
+
33
+ # @param x [Float]
34
+ # @param y [Float]
35
+ def move(x:, y:)
36
+ position = CG::Point.new
37
+ position[:x] = x
38
+ position[:y] = y
39
+ Library.CGWarpMouseCursorPosition(position)
40
+ end
41
+
42
+ # @param x [Float]
43
+ # @param y [Float]
44
+ def left_down(x:, y:)
45
+ trigger(type: CG::EventType::LEFT_MOUSE_DOWN, x:, y:, button: CG::MouseButton::LEFT)
46
+ end
47
+
48
+ # @param x [Float]
49
+ # @param y [Float]
50
+ def left_up(x:, y:)
51
+ trigger(type: CG::EventType::LEFT_MOUSE_UP, x:, y:, button: CG::MouseButton::LEFT)
52
+ end
53
+
54
+ # @param x [Float]
55
+ # @param y [Float]
56
+ def left_click(x:, y:)
57
+ left_down(x:, y:)
58
+ left_up(x:, y:)
59
+ end
60
+
61
+ # @param x [Float]
62
+ # @param y [Float]
63
+ def right_down(x:, y:)
64
+ trigger(type: CG::EventType::RIGHT_MOUSE_DOWN, x:, y:, button: CG::MouseButton::RIGHT)
65
+ end
66
+
67
+ # @param x [Float]
68
+ # @param y [Float]
69
+ def right_up(x:, y:)
70
+ trigger(type: CG::EventType::RIGHT_MOUSE_UP, x:, y:, button: CG::MouseButton::RIGHT)
71
+ end
72
+
73
+ # @param x [Float]
74
+ # @param y [Float]
75
+ def right_click(x:, y:)
76
+ right_down(x:, y:)
77
+ right_up(x:, y:)
78
+ end
79
+
80
+ # @param x [Float]
81
+ # @param y [Float]
82
+ def middle_down(x:, y:)
83
+ trigger(type: CG::EventType::OTHER_MOUSE_DOWN, x:, y:, button: CG::MouseButton::CENTER)
84
+ end
85
+
86
+ # @param x [Float]
87
+ # @param y [Float]
88
+ def middle_up(x:, y:)
89
+ trigger(type: CG::EventType::OTHER_MOUSE_UP, x:, y:, button: CG::MouseButton::CENTER)
90
+ end
91
+
92
+ # @param x [Float]
93
+ # @param y [Float]
94
+ def middle_click(x:, y:)
95
+ middle_down(x:, y:)
96
+ middle_up(x:, y:)
97
+ end
98
+ end
99
+ end
data/lib/macos/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Macos
4
- VERSION = "0.1.1"
3
+ module MacOS
4
+ VERSION = "0.1.3"
5
5
  end
data/lib/macos.rb CHANGED
@@ -1,16 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'zeitwerk'
3
+ require "fiddle"
4
+ require "zeitwerk"
4
5
 
5
6
  loader = Zeitwerk::Loader.for_gem
6
- loader.inflector.inflect 'macos' => 'MacOS'
7
+ loader.inflector.inflect "cg" => "CG"
8
+ loader.inflector.inflect "macos" => "MacOS"
7
9
  loader.setup
8
10
 
11
+ # Interacts with macOS.
9
12
  module MacOS
10
13
  class Error < StandardError; end
11
14
 
12
- # @return [MacOS::Screen]
13
- def screen
14
- Screen.new
15
+ # @example
16
+ # MacOS.keyboard.type('Hello, World!')
17
+ # MacOS.keyboard.key_down('a')
18
+ # MacOS.keyboard.key_up('a')
19
+ #
20
+ # @return [MacOS::Keyboard]
21
+ def self.keyboard
22
+ Keyboard.new
23
+ end
24
+
25
+ # @example
26
+ # mouse = MacOS.mouse
27
+ # mouse.move(x: 4, y: 8)
28
+ # position = mouse.position
29
+ # mouse.left_click(x: position.x, y: position.y)
30
+ # mouse.right_click(x: position.x, y: position.y)
31
+ #
32
+ # @return [MacOS::Mouse]
33
+ def self.mouse
34
+ Mouse.new
35
+ end
36
+
37
+ # @return [MacOS::Display]
38
+ def self.display
39
+ Display.main
15
40
  end
16
41
  end
metadata CHANGED
@@ -1,15 +1,42 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: macos
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Sylvestre
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-11-18 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: ffi
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: fiddle
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
13
40
  - !ruby/object:Gem::Dependency
14
41
  name: zeitwerk
15
42
  requirement: !ruby/object:Gem::Requirement
@@ -34,9 +61,19 @@ files:
34
61
  - Gemfile
35
62
  - README.md
36
63
  - bin/console
64
+ - bin/demo
37
65
  - bin/setup
38
66
  - lib/macos.rb
39
- - lib/macos/screen.rb
67
+ - lib/macos/cg/event_tap_location.rb
68
+ - lib/macos/cg/event_type.rb
69
+ - lib/macos/cg/mouse_button.rb
70
+ - lib/macos/cg/point.rb
71
+ - lib/macos/cg/rect.rb
72
+ - lib/macos/cg/size.rb
73
+ - lib/macos/display.rb
74
+ - lib/macos/keyboard.rb
75
+ - lib/macos/library.rb
76
+ - lib/macos/mouse.rb
40
77
  - lib/macos/version.rb
41
78
  homepage: https://github.com/ksylvest/macos
42
79
  licenses:
@@ -44,8 +81,8 @@ licenses:
44
81
  metadata:
45
82
  homepage_uri: https://github.com/ksylvest/macos
46
83
  source_code_uri: https://github.com/ksylvest/macos
47
- changelog_uri: https://github.com/ksylvest/macos
48
- post_install_message:
84
+ changelog_uri: https://github.com/ksylvest/macos/releases
85
+ rubygems_mfa_required: 'true'
49
86
  rdoc_options: []
50
87
  require_paths:
51
88
  - lib
@@ -60,8 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
60
97
  - !ruby/object:Gem::Version
61
98
  version: '0'
62
99
  requirements: []
63
- rubygems_version: 3.5.22
64
- signing_key:
100
+ rubygems_version: 3.6.7
65
101
  specification_version: 4
66
102
  summary: An interface with macOS.
67
103
  test_files: []
data/lib/macos/screen.rb DELETED
@@ -1,14 +0,0 @@
1
- module MacOS
2
- module Screen
3
- # @yield [tempfile]
4
- # @yieldparam tempfile [Tempfile]
5
- def capture
6
- tempfile = Tempfile.new(['screenshot', '.png'])
7
- system("screencapture -o -x #{tempfile.path}")
8
- yield tempfile
9
- ensure
10
- tempfile.close
11
- tempfile.unlink
12
- end
13
- end
14
- end