win32screenshot 3.1.0 → 4.1.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 +4 -4
- data/CHANGES.md +15 -0
- data/Gemfile.lock +2 -2
- data/README.md +54 -48
- data/lib/win32/screenshot/bitmap_maker.rb +121 -100
- data/lib/win32/screenshot/desktop.rb +5 -0
- data/lib/win32/screenshot/image.rb +48 -48
- data/lib/win32/screenshot/take.rb +79 -109
- data/lib/win32/screenshot/version.rb +1 -1
- data/lib/win32/screenshot.rb +12 -11
- data/spec/spec_helper.rb +1 -1
- data/spec/win32/screenshot/take_spec.rb +14 -82
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 20fb85f0a8a6db55a03ef208225cb7a0f081b704f41abf405b39e63283c2c7ea
|
4
|
+
data.tar.gz: bfeb14a87af3452d3bd39b37edfd13733a940657e671e4e038a1ec06775aa858
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 75b4b936b11909e01426ee6ae23a8885106c594a8585372e9e8d7cef59c739d0ce5b7369173b1ae28484559637466f2d22774b0d29a487df13dca8011ed1464d
|
7
|
+
data.tar.gz: e428a2fdcaa46dd961f351000af951d4c4ffa8f556e1b8b9befb7e09dd99eb7c3412d950f9e4122301d6ebc99009cda805eb7343c0772bb14ac0b152fcd4163c
|
data/CHANGES.md
CHANGED
@@ -1,11 +1,26 @@
|
|
1
|
+
# 4.1.0 2023-02-05
|
2
|
+
* Add support for Ruby 3.2 (by @kaiwaredaikon009 via PR #36)
|
3
|
+
|
4
|
+
|
5
|
+
# 4.0.0 2022-06-18
|
6
|
+
* Add support for taking screenchots of non-native Windows (e.g. Google Chrome).
|
7
|
+
* When taking screenshot of `:desktop` then all monitors will be included if they exist.
|
8
|
+
* Remove `:area` option - instead whole windows will be screenshotted (for browser windows, the whole page will be saved).
|
9
|
+
* Remove `context: :client` option.
|
10
|
+
|
11
|
+
Changes done by @bensandland via PR #34.
|
12
|
+
|
13
|
+
|
1
14
|
# 3.1.0 2022-02-05
|
2
15
|
* Update dependencies
|
3
16
|
|
17
|
+
|
4
18
|
# 3.0.0 2019-12-11
|
5
19
|
* Use RAutomation 1.0.0 as its dependency to be able to use newer ffi.
|
6
20
|
* Update bundled ImageMagick to 7.0.9-8.
|
7
21
|
* Update MiniMagick dependency.
|
8
22
|
|
23
|
+
|
9
24
|
# 2.1.0 2016-01-09
|
10
25
|
* Add Image#write! for overwriting images.
|
11
26
|
* Update MiniMagick dependency.
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,48 +1,54 @@
|
|
1
|
-
# Win32::Screenshot
|
2
|
-
|
3
|
-
*
|
4
|
-
|
5
|
-
|
6
|
-
## DESCRIPTION
|
7
|
-
|
8
|
-
Capture Screenshots on Windows with Ruby to bmp, gif, jpg or png formats!
|
9
|
-
|
10
|
-
## INSTALL
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
1
|
+
# Win32::Screenshot
|
2
|
+
|
3
|
+
* http://github.com/jarmo/win32screenshot
|
4
|
+
|
5
|
+
|
6
|
+
## DESCRIPTION
|
7
|
+
|
8
|
+
Capture Screenshots on Windows with Ruby to bmp, gif, jpg or png formats!
|
9
|
+
|
10
|
+
## INSTALL
|
11
|
+
|
12
|
+
```
|
13
|
+
gem install win32screenshot
|
14
|
+
```
|
15
|
+
|
16
|
+
## SYNOPSIS
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
require 'win32/screenshot'
|
20
|
+
|
21
|
+
# Take a screenshot of the window with the specified title
|
22
|
+
Win32::Screenshot::Take.of(:window, title: "Windows Internet Explorer").write("image.bmp")
|
23
|
+
|
24
|
+
# Take a screenshot of the whole desktop area, including all monitors if multiple are connected
|
25
|
+
Win32::Screenshot::Take.of(:desktop).write("image.png")
|
26
|
+
|
27
|
+
# Take a screenshot of the foreground window
|
28
|
+
Win32::Screenshot::Take.of(:foreground).write("image.png")
|
29
|
+
|
30
|
+
# Take a screenshot of the foreground window, and writing over previous image if it exists
|
31
|
+
Win32::Screenshot::Take.of(:foreground).write!("image.png")
|
32
|
+
|
33
|
+
# Take a screenshot of the specified window with title matching the regular expression
|
34
|
+
Win32::Screenshot::Take.of(:window, title: /internet/i).write("image.jpg")
|
35
|
+
|
36
|
+
# Take a screenshot of the window with the specified handle
|
37
|
+
Win32::Screenshot::Take.of(:window, hwnd: 123456).write("image.gif")
|
38
|
+
|
39
|
+
# Take a screenshot of the child window with the specified internal class name
|
40
|
+
Win32::Screenshot::Take.of(
|
41
|
+
:rautomation,
|
42
|
+
RAutomation::Window.new(hwnd: 123456).child(class: "Internet Explorer_Server")
|
43
|
+
).write("image.png")
|
44
|
+
|
45
|
+
# Use the bitmap blob for something else
|
46
|
+
image = Win32::Screenshot::Take.of(:window, hwnd: 123456)
|
47
|
+
image.height # => height of the image
|
48
|
+
image.width # => width of the image
|
49
|
+
image.bitmap # => bitmap blob
|
50
|
+
```
|
51
|
+
|
52
|
+
## Copyright
|
53
|
+
|
54
|
+
Copyright (c) Jarmo Pertman, Aslak Hellesøy. See LICENSE for details.
|
@@ -1,100 +1,121 @@
|
|
1
|
-
module Win32
|
2
|
-
module Screenshot
|
3
|
-
# @private
|
4
|
-
# This is an internal class for taking the actual screenshots and not part of a public API.
|
5
|
-
class BitmapMaker
|
6
|
-
class << self
|
7
|
-
extend FFI::Library
|
8
|
-
|
9
|
-
ffi_lib 'user32', 'gdi32'
|
10
|
-
ffi_convention :stdcall
|
11
|
-
|
12
|
-
# user32.dll
|
13
|
-
attach_function :window_dc, :GetWindowDC,
|
14
|
-
[:long], :long
|
15
|
-
attach_function :
|
16
|
-
[:long], :
|
17
|
-
attach_function :
|
18
|
-
[
|
19
|
-
attach_function :
|
20
|
-
[
|
21
|
-
attach_function :
|
22
|
-
[], :
|
23
|
-
attach_function :
|
24
|
-
[], :
|
25
|
-
|
26
|
-
# gdi32.dll
|
27
|
-
attach_function :create_compatible_dc, :CreateCompatibleDC,
|
28
|
-
[:long], :long
|
29
|
-
attach_function :create_compatible_bitmap, :CreateCompatibleBitmap,
|
30
|
-
[:long, :int, :int], :long
|
31
|
-
attach_function :select_object, :SelectObject,
|
32
|
-
[:long, :long], :long
|
33
|
-
attach_function :
|
34
|
-
[:long, :
|
35
|
-
attach_function :
|
36
|
-
[:long
|
37
|
-
attach_function :
|
38
|
-
[:long], :bool
|
39
|
-
attach_function :
|
40
|
-
[:long], :
|
41
|
-
attach_function :
|
42
|
-
[:long, :long], :
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
hmemDC
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
lpvpxldata.
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
end
|
1
|
+
module Win32
|
2
|
+
module Screenshot
|
3
|
+
# @private
|
4
|
+
# This is an internal class for taking the actual screenshots and not part of a public API.
|
5
|
+
class BitmapMaker
|
6
|
+
class << self
|
7
|
+
extend FFI::Library
|
8
|
+
|
9
|
+
ffi_lib 'user32', 'gdi32'
|
10
|
+
ffi_convention :stdcall
|
11
|
+
|
12
|
+
# user32.dll
|
13
|
+
attach_function :window_dc, :GetWindowDC,
|
14
|
+
[:long], :long
|
15
|
+
attach_function :window_rect, :GetWindowRect,
|
16
|
+
[:long, :pointer], :bool
|
17
|
+
attach_function :foreground_window, :GetForegroundWindow,
|
18
|
+
[], :long
|
19
|
+
attach_function :desktop_window, :GetDesktopWindow,
|
20
|
+
[], :long
|
21
|
+
attach_function :print_window, :PrintWindow,
|
22
|
+
[:long, :long, :int], :bool
|
23
|
+
attach_function :get_system_metrics, :GetSystemMetrics,
|
24
|
+
[:int], :int
|
25
|
+
|
26
|
+
# gdi32.dll
|
27
|
+
attach_function :create_compatible_dc, :CreateCompatibleDC,
|
28
|
+
[:long], :long
|
29
|
+
attach_function :create_compatible_bitmap, :CreateCompatibleBitmap,
|
30
|
+
[:long, :int, :int], :long
|
31
|
+
attach_function :select_object, :SelectObject,
|
32
|
+
[:long, :long], :long
|
33
|
+
attach_function :di_bits, :GetDIBits,
|
34
|
+
[:long, :long, :int, :int, :pointer, :pointer, :int], :int
|
35
|
+
attach_function :delete_object, :DeleteObject,
|
36
|
+
[:long], :bool
|
37
|
+
attach_function :delete_dc, :DeleteDC,
|
38
|
+
[:long], :bool
|
39
|
+
attach_function :release_dc, :ReleaseDC,
|
40
|
+
[:long, :long], :int
|
41
|
+
attach_function :bit_blt, :BitBlt,
|
42
|
+
[:long, :int, :int, :int, :int, :long, :int, :int, :long], :bool
|
43
|
+
|
44
|
+
DIB_RGB_COLORS = 0
|
45
|
+
PW_RENDERFULLCONTENT = 0x00000002
|
46
|
+
SRCCOPY = 0x00CC0020
|
47
|
+
|
48
|
+
SM_XVIRTUALSCREEN = 76
|
49
|
+
SM_YVIRTUALSCREEN = 77
|
50
|
+
SM_CXVIRTUALSCREEN = 78
|
51
|
+
SM_CYVIRTUALSCREEN = 79
|
52
|
+
|
53
|
+
def capture_window(hwnd)
|
54
|
+
width, height = dimensions_for(hwnd)
|
55
|
+
|
56
|
+
hScreenDC, hmemDC, hmemBM = prepare_object(hwnd, width, height)
|
57
|
+
print_window(hwnd, hmemDC, PW_RENDERFULLCONTENT)
|
58
|
+
create_bitmap(hScreenDC, hmemDC, hmemBM, width, height)
|
59
|
+
end
|
60
|
+
|
61
|
+
def capture_screen(hwnd)
|
62
|
+
left, top, width, height = desktop.dimensions
|
63
|
+
|
64
|
+
hScreenDC, hmemDC, hmemBM = prepare_object(hwnd, width, height)
|
65
|
+
bit_blt(hmemDC, 0, 0, width, height, hScreenDC, left, top, SRCCOPY)
|
66
|
+
create_bitmap(hScreenDC, hmemDC, hmemBM, width, height)
|
67
|
+
end
|
68
|
+
|
69
|
+
def prepare_object(hwnd, width, height)
|
70
|
+
hScreenDC = window_dc(hwnd)
|
71
|
+
hmemDC = create_compatible_dc(hScreenDC)
|
72
|
+
hmemBM = create_compatible_bitmap(hScreenDC, width, height)
|
73
|
+
select_object(hmemDC, hmemBM)
|
74
|
+
[hScreenDC, hmemDC, hmemBM]
|
75
|
+
end
|
76
|
+
|
77
|
+
def create_bitmap(hScreenDC, hmemDC, hmemBM, width, height)
|
78
|
+
bitmap_size = width * height * 3 + width % 4 * height
|
79
|
+
lpvpxldata = FFI::MemoryPointer.new(bitmap_size)
|
80
|
+
|
81
|
+
# Bitmap header
|
82
|
+
# http://www.fortunecity.com/skyscraper/windows/364/bmpffrmt.html
|
83
|
+
bmInfo = [40, width, height, 1, 24, 0, 0, 0, 0, 0, 0, 0].pack('L3S2L6')
|
84
|
+
di_bits(hmemDC, hmemBM, 0, height, lpvpxldata, bmInfo, DIB_RGB_COLORS)
|
85
|
+
|
86
|
+
bmFileHeader = [
|
87
|
+
19778,
|
88
|
+
bitmap_size + 40 + 14,
|
89
|
+
0,
|
90
|
+
0,
|
91
|
+
54
|
92
|
+
].pack('SLSSL')
|
93
|
+
|
94
|
+
Image.new(bmFileHeader + bmInfo + lpvpxldata.read_string(bitmap_size), width, height)
|
95
|
+
ensure
|
96
|
+
lpvpxldata.free
|
97
|
+
delete_object(hmemBM)
|
98
|
+
delete_dc(hmemDC)
|
99
|
+
release_dc(0, hScreenDC)
|
100
|
+
end
|
101
|
+
|
102
|
+
def desktop
|
103
|
+
Win32::Screenshot::Desktop.new(
|
104
|
+
get_system_metrics(SM_XVIRTUALSCREEN),
|
105
|
+
get_system_metrics(SM_YVIRTUALSCREEN),
|
106
|
+
get_system_metrics(SM_CXVIRTUALSCREEN),
|
107
|
+
get_system_metrics(SM_CYVIRTUALSCREEN)
|
108
|
+
)
|
109
|
+
end
|
110
|
+
|
111
|
+
def dimensions_for(hwnd)
|
112
|
+
rect = [0, 0, 0, 0].pack('l4')
|
113
|
+
window_rect(hwnd.to_i, rect)
|
114
|
+
left, top, width, height = rect.unpack('l4')
|
115
|
+
|
116
|
+
[width - left, height - top]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -1,48 +1,48 @@
|
|
1
|
-
module Win32
|
2
|
-
module Screenshot
|
3
|
-
# Holds the bitmap data and writes it to the disk
|
4
|
-
class Image
|
5
|
-
# [String] raw bitmap blob
|
6
|
-
attr_reader :bitmap
|
7
|
-
|
8
|
-
# [String] bitmap width
|
9
|
-
attr_reader :width
|
10
|
-
|
11
|
-
# [String] bitmap height
|
12
|
-
attr_reader :height
|
13
|
-
|
14
|
-
# Supported output formats
|
15
|
-
FORMATS = %w{bmp gif jpg png}
|
16
|
-
|
17
|
-
# @private
|
18
|
-
def initialize(blob, width, height)
|
19
|
-
@bitmap = blob
|
20
|
-
@width = width
|
21
|
-
@height = height
|
22
|
-
end
|
23
|
-
|
24
|
-
# Writes image to the disk.
|
25
|
-
# @param [String] file_path writes image to the specified path.
|
26
|
-
# @raise [RuntimeError] when _file_path_ already exists.
|
27
|
-
# @raise [RuntimeError] when _file_path_ is not with the supported output {FORMATS} extension.
|
28
|
-
def write(file_path)
|
29
|
-
raise "File already exists: #{file_path}!" if File.
|
30
|
-
write! file_path
|
31
|
-
end
|
32
|
-
|
33
|
-
#Writes image to disk, writing over existing copy if it exists.
|
34
|
-
def write!(file_path)
|
35
|
-
ext = File.extname(file_path)[1..-1]
|
36
|
-
raise "File '#{file_path}' has to have one of the following extensions: #{FORMATS.join(", ")}" unless ext && FORMATS.include?(ext.downcase)
|
37
|
-
|
38
|
-
if ext.downcase == "bmp"
|
39
|
-
File.open(file_path, "wb") {|io| io.write @bitmap}
|
40
|
-
else
|
41
|
-
image = ::MiniMagick::Image.read @bitmap
|
42
|
-
image.format ext
|
43
|
-
image.write file_path
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
1
|
+
module Win32
|
2
|
+
module Screenshot
|
3
|
+
# Holds the bitmap data and writes it to the disk
|
4
|
+
class Image
|
5
|
+
# [String] raw bitmap blob
|
6
|
+
attr_reader :bitmap
|
7
|
+
|
8
|
+
# [String] bitmap width
|
9
|
+
attr_reader :width
|
10
|
+
|
11
|
+
# [String] bitmap height
|
12
|
+
attr_reader :height
|
13
|
+
|
14
|
+
# Supported output formats
|
15
|
+
FORMATS = %w{bmp gif jpg png}
|
16
|
+
|
17
|
+
# @private
|
18
|
+
def initialize(blob, width, height)
|
19
|
+
@bitmap = blob
|
20
|
+
@width = width
|
21
|
+
@height = height
|
22
|
+
end
|
23
|
+
|
24
|
+
# Writes image to the disk.
|
25
|
+
# @param [String] file_path writes image to the specified path.
|
26
|
+
# @raise [RuntimeError] when _file_path_ already exists.
|
27
|
+
# @raise [RuntimeError] when _file_path_ is not with the supported output {FORMATS} extension.
|
28
|
+
def write(file_path)
|
29
|
+
raise "File already exists: #{file_path}!" if File.exist? file_path
|
30
|
+
write! file_path
|
31
|
+
end
|
32
|
+
|
33
|
+
#Writes image to disk, writing over existing copy if it exists.
|
34
|
+
def write!(file_path)
|
35
|
+
ext = File.extname(file_path)[1..-1]
|
36
|
+
raise "File '#{file_path}' has to have one of the following extensions: #{FORMATS.join(", ")}" unless ext && FORMATS.include?(ext.downcase)
|
37
|
+
|
38
|
+
if ext.downcase == "bmp"
|
39
|
+
File.open(file_path, "wb") {|io| io.write @bitmap}
|
40
|
+
else
|
41
|
+
image = ::MiniMagick::Image.read @bitmap
|
42
|
+
image.format ext
|
43
|
+
image.write file_path
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -1,109 +1,79 @@
|
|
1
|
-
module Win32
|
2
|
-
module Screenshot
|
3
|
-
# Capture Screenshots on Windows with Ruby
|
4
|
-
class Take
|
5
|
-
|
6
|
-
class << self
|
7
|
-
# Takes a screenshot of the specified object
|
8
|
-
#
|
9
|
-
# @example Take a screenshot of the window with the specified title
|
10
|
-
# Win32::Screenshot::Take.of(:window, :title => "Windows Internet Explorer")
|
11
|
-
#
|
12
|
-
# @example Take a screenshot of the foreground
|
13
|
-
# Win32::Screenshot::Take.of(:foreground)
|
14
|
-
#
|
15
|
-
# @example Take a screenshot of the
|
16
|
-
# Win32::Screenshot::Take.of(:
|
17
|
-
#
|
18
|
-
# @example Take a screenshot of the window with the specified handle
|
19
|
-
# Win32::Screenshot::Take.of(:window, :hwnd => 123456)
|
20
|
-
#
|
21
|
-
# @example Take a screenshot of the window's client area (e.g. without title bar) with the specified handle
|
22
|
-
# Win32::Screenshot::Take.of(:window, :hwnd => 123456, :context => :client)
|
23
|
-
#
|
24
|
-
# @example Take a screenshot of the child window with the specified internal class name
|
25
|
-
# Win32::Screenshot::Take.of(:rautomation, RAutomation::Window.new(:hwnd => 123456).child(:class => "Internet Explorer_Server"))
|
26
|
-
#
|
27
|
-
# @param [Symbol] what the type of the object to take a screenshot of,
|
28
|
-
# possible values are _:foreground_, _:desktop_ and _:window_.
|
29
|
-
# @param [Hash] opts options are optional for specifying
|
30
|
-
# It is possible to specify as many options as are needed for searching for the unique window.
|
31
|
-
# By default the first window with matching identifiers will be taken screenshot of.
|
32
|
-
# It is possible to use in addition to other options a 0-based _:index_ option to search for other windows if multiple
|
33
|
-
# windows match the specified criteria.
|
34
|
-
# @option opts [String,
|
35
|
-
# @option opts [String, Regexp] :
|
36
|
-
# @option opts [String,
|
37
|
-
# @option opts [String,
|
38
|
-
# @option opts [String, Fixnum] :
|
39
|
-
#
|
40
|
-
# @option opts [
|
41
|
-
#
|
42
|
-
# @
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
def take_screenshot(hwnd, opts)
|
82
|
-
if opts[:area]
|
83
|
-
validate_coordinates(hwnd, opts[:context], *opts[:area])
|
84
|
-
BitmapMaker.capture_area(hwnd, opts[:context], *opts[:area])
|
85
|
-
else
|
86
|
-
BitmapMaker.capture_all(hwnd, opts[:context])
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def validate_coordinates(hwnd, context, x1, y1, x2, y2)
|
91
|
-
specified_coordinates = "x1: #{x1}, y1: #{y1}, x2: #{x2}, y2: #{y2}"
|
92
|
-
if [x1, y1, x2, y2].any? {|c| c < 0}
|
93
|
-
raise "specified coordinates (#{specified_coordinates}) are invalid - cannot be negative!"
|
94
|
-
end
|
95
|
-
|
96
|
-
if x1 >= x2 || y1 >= y2
|
97
|
-
raise "specified coordinates (#{specified_coordinates}) are invalid - cannot be x1 >= x2 or y1 >= y2!"
|
98
|
-
end
|
99
|
-
|
100
|
-
max_width, max_height = BitmapMaker.dimensions_for(hwnd, context)
|
101
|
-
if x2 > max_width || y2 > max_height
|
102
|
-
raise "specified coordinates (#{specified_coordinates}) are invalid - maximum x2: #{max_width} and y2: #{max_height}!"
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
1
|
+
module Win32
|
2
|
+
module Screenshot
|
3
|
+
# Capture Screenshots on Windows with Ruby
|
4
|
+
class Take
|
5
|
+
|
6
|
+
class << self
|
7
|
+
# Takes a screenshot of the specified object.
|
8
|
+
#
|
9
|
+
# @example Take a screenshot of the window with the specified title
|
10
|
+
# Win32::Screenshot::Take.of(:window, :title => "Windows Internet Explorer")
|
11
|
+
#
|
12
|
+
# @example Take a screenshot of the foreground
|
13
|
+
# Win32::Screenshot::Take.of(:foreground)
|
14
|
+
#
|
15
|
+
# @example Take a screenshot of the desktop
|
16
|
+
# Win32::Screenshot::Take.of(:desktop)
|
17
|
+
#
|
18
|
+
# @example Take a screenshot of the window with the specified handle
|
19
|
+
# Win32::Screenshot::Take.of(:window, :hwnd => 123456)
|
20
|
+
#
|
21
|
+
# @example Take a screenshot of the window's client area (e.g. without title bar) with the specified handle
|
22
|
+
# Win32::Screenshot::Take.of(:window, :hwnd => 123456, :context => :client)
|
23
|
+
#
|
24
|
+
# @example Take a screenshot of the child window with the specified internal class name
|
25
|
+
# Win32::Screenshot::Take.of(:rautomation, RAutomation::Window.new(:hwnd => 123456).child(:class => "Internet Explorer_Server"))
|
26
|
+
#
|
27
|
+
# @param [Symbol] what the type of the object to take a screenshot of,
|
28
|
+
# possible values are _:foreground_, _:desktop_ and _:window_.
|
29
|
+
# @param [Hash] opts options are optional for specifying _:context_ to take a screenshot.
|
30
|
+
# It is possible to specify as many options as are needed for searching for the unique window.
|
31
|
+
# By default the first window with matching identifiers will be taken screenshot of.
|
32
|
+
# It is possible to use in addition to other options a 0-based _:index_ option to search for other windows if multiple
|
33
|
+
# windows match the specified criteria.
|
34
|
+
# @option opts [String, Regexp] :title Title of the window
|
35
|
+
# @option opts [String, Regexp] :text Visible text of the window
|
36
|
+
# @option opts [String, Fixnum] :hwnd Window handle in decimal format
|
37
|
+
# @option opts [String, Fixnum] :pid Window process ID (PID)
|
38
|
+
# @option opts [String, Fixnum] :index 0-based index to specify n-th window to take a screenshot of if
|
39
|
+
# all other criteria match
|
40
|
+
# @option opts [RAutomation::Window] :rautomation RAutomation::Window object to take a screenshot of. Useful for
|
41
|
+
# taking screenshots of the child windows
|
42
|
+
# @return [Image] the {Image} of the specified object
|
43
|
+
def of(what, opts = {})
|
44
|
+
valid_whats = [:foreground, :desktop, :window]
|
45
|
+
raise "It is not possible to take a screenshot of '#{what}', possible values are #{valid_whats.join(", ")}" unless valid_whats.include?(what)
|
46
|
+
|
47
|
+
self.send(what, opts)
|
48
|
+
end
|
49
|
+
|
50
|
+
alias_method :new, :of
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def foreground(opts)
|
55
|
+
hwnd = BitmapMaker.foreground_window
|
56
|
+
BitmapMaker.capture_window(hwnd)
|
57
|
+
end
|
58
|
+
|
59
|
+
def desktop(opts)
|
60
|
+
hwnd = BitmapMaker.desktop_window
|
61
|
+
BitmapMaker.capture_screen(hwnd)
|
62
|
+
end
|
63
|
+
|
64
|
+
def window(opts)
|
65
|
+
win = opts[:rautomation] || RAutomation::Window.new(opts)
|
66
|
+
timeout = Time.now + 10
|
67
|
+
until win.active?
|
68
|
+
if Time.now >= timeout
|
69
|
+
Kernel.warn "[WARN] Failed to set window '#{win.locators.inspect}' into focus for taking the screenshot"
|
70
|
+
break
|
71
|
+
end
|
72
|
+
win.activate
|
73
|
+
end
|
74
|
+
BitmapMaker.capture_window(win.hwnd)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/win32/screenshot.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
|
-
require 'ffi'
|
2
|
-
require 'mini_magick'
|
3
|
-
require 'rautomation'
|
4
|
-
|
5
|
-
require File.dirname(__FILE__) + '/screenshot/version'
|
6
|
-
require File.dirname(__FILE__) + '/screenshot/take'
|
7
|
-
require File.dirname(__FILE__) + '/screenshot/image'
|
8
|
-
require File.dirname(__FILE__) + '/screenshot/bitmap_maker'
|
9
|
-
|
10
|
-
|
11
|
-
|
1
|
+
require 'ffi'
|
2
|
+
require 'mini_magick'
|
3
|
+
require 'rautomation'
|
4
|
+
|
5
|
+
require File.dirname(__FILE__) + '/screenshot/version'
|
6
|
+
require File.dirname(__FILE__) + '/screenshot/take'
|
7
|
+
require File.dirname(__FILE__) + '/screenshot/image'
|
8
|
+
require File.dirname(__FILE__) + '/screenshot/bitmap_maker'
|
9
|
+
require File.dirname(__FILE__) + '/screenshot/desktop'
|
10
|
+
|
11
|
+
# add bundled ImageMagick into path
|
12
|
+
ENV["PATH"] = "#{File.dirname(__FILE__) + "/../../ext/ImageMagick-7.0.9-8-portable-Q16-x86"};#{ENV["PATH"]}"
|
data/spec/spec_helper.rb
CHANGED
@@ -20,7 +20,7 @@ module SpecHelper
|
|
20
20
|
[:long, :long, :int, :int, :int, :int, :int], :bool
|
21
21
|
|
22
22
|
def save_and_verify_image(img, file=nil)
|
23
|
-
FileUtils.mkdir @temp_dir unless File.
|
23
|
+
FileUtils.mkdir @temp_dir unless File.exist?(@temp_dir)
|
24
24
|
file_name = File.join @temp_dir, "#{file}.bmp"
|
25
25
|
img.write file_name
|
26
26
|
expect(img.bitmap[0..1]).to eq('BM')
|
@@ -13,38 +13,22 @@ describe Win32::Screenshot::Take do
|
|
13
13
|
image = Win32::Screenshot::Take.of(:foreground)
|
14
14
|
save_and_verify_image(image, 'foreground')
|
15
15
|
hwnd = Win32::Screenshot::BitmapMaker.foreground_window
|
16
|
-
expect([image.width, image.height]).to eq(Win32::Screenshot::BitmapMaker.dimensions_for(hwnd
|
17
|
-
end
|
18
|
-
|
19
|
-
it "captures an area of the foreground" do
|
20
|
-
image = Win32::Screenshot::Take.of(:foreground, :area => [30, 30, 100, 150])
|
21
|
-
save_and_verify_image(image, 'foreground_area')
|
22
|
-
expect(image.width).to eq(70)
|
23
|
-
expect(image.height).to eq(120)
|
24
|
-
end
|
25
|
-
|
26
|
-
it "doesn't allow to capture an area of the foreground with invalid coordinates" do
|
27
|
-
expect {Win32::Screenshot::Take.of(:foreground, :area => [0, 0, -1, 100])}.
|
28
|
-
to raise_exception("specified coordinates (x1: 0, y1: 0, x2: -1, y2: 100) are invalid - cannot be negative!")
|
16
|
+
expect([image.width, image.height]).to eq(Win32::Screenshot::BitmapMaker.dimensions_for(hwnd))
|
29
17
|
end
|
30
18
|
|
31
19
|
it "captures the desktop" do
|
32
20
|
image = Win32::Screenshot::Take.of(:desktop)
|
21
|
+
desktop_dimensions = Win32::Screenshot::BitmapMaker::desktop
|
33
22
|
save_and_verify_image(image, 'desktop')
|
34
|
-
hwnd = Win32::Screenshot::BitmapMaker.desktop_window
|
35
|
-
expect([image.width, image.height]).to eq(Win32::Screenshot::BitmapMaker.dimensions_for(hwnd, :window))
|
36
|
-
end
|
37
23
|
|
38
|
-
|
39
|
-
image = Win32::Screenshot::Take.of(:desktop, :area => [30, 30, 100, 150])
|
40
|
-
save_and_verify_image(image, 'desktop_area')
|
41
|
-
expect(image.width).to eq(70)
|
42
|
-
expect(image.height).to eq(120)
|
24
|
+
expect([image.width, image.height]).to eq([desktop_dimensions.width, desktop_dimensions.height])
|
43
25
|
end
|
44
26
|
|
45
|
-
it "
|
46
|
-
|
47
|
-
|
27
|
+
it "captures a window" do
|
28
|
+
image = Win32::Screenshot::Take.of(:window, :pid => @notepad)
|
29
|
+
save_and_verify_image(image, 'notepad_context_window')
|
30
|
+
window = RAutomation::Window.new(:pid => @notepad)
|
31
|
+
expect([image.width, image.height]).to eq(Win32::Screenshot::BitmapMaker.dimensions_for(window.hwnd))
|
48
32
|
end
|
49
33
|
|
50
34
|
it "captures a maximized window" do
|
@@ -52,7 +36,7 @@ describe Win32::Screenshot::Take do
|
|
52
36
|
window.maximize
|
53
37
|
image = Win32::Screenshot::Take.of(:window, :pid => @iexplore)
|
54
38
|
save_and_verify_image(image, 'iexplore_max')
|
55
|
-
expect([image.width, image.height]).to eq(Win32::Screenshot::BitmapMaker.dimensions_for(window.hwnd
|
39
|
+
expect([image.width, image.height]).to eq(Win32::Screenshot::BitmapMaker.dimensions_for(window.hwnd))
|
56
40
|
end
|
57
41
|
|
58
42
|
it "captures a minimized window" do
|
@@ -60,7 +44,7 @@ describe Win32::Screenshot::Take do
|
|
60
44
|
window.minimize
|
61
45
|
image = Win32::Screenshot::Take.of(:window, :pid => @notepad)
|
62
46
|
save_and_verify_image(image, 'notepad_min')
|
63
|
-
expect([image.width, image.height]).to eq(Win32::Screenshot::BitmapMaker.dimensions_for(window.hwnd
|
47
|
+
expect([image.width, image.height]).to eq(Win32::Screenshot::BitmapMaker.dimensions_for(window.hwnd))
|
64
48
|
end
|
65
49
|
|
66
50
|
it "captures a small window" do
|
@@ -73,73 +57,21 @@ describe Win32::Screenshot::Take do
|
|
73
57
|
# screenshot doesn't include titlebar and the size
|
74
58
|
# varies between different themes and Windows versions
|
75
59
|
window = RAutomation::Window.new(:pid => @iexplore)
|
76
|
-
expect([image.width, image.height]).to eq(Win32::Screenshot::BitmapMaker.dimensions_for(window.hwnd
|
77
|
-
end
|
78
|
-
|
79
|
-
it "captures a window context of the window" do
|
80
|
-
image = Win32::Screenshot::Take.of(:window, :pid => @notepad, :context => :window)
|
81
|
-
save_and_verify_image(image, 'notepad_context_window')
|
82
|
-
window = RAutomation::Window.new(:pid => @notepad)
|
83
|
-
expect([image.width, image.height]).to eq(Win32::Screenshot::BitmapMaker.dimensions_for(window.hwnd, :window))
|
84
|
-
end
|
85
|
-
|
86
|
-
it "captures a client context of the window" do
|
87
|
-
image = Win32::Screenshot::Take.of(:window, :pid => @notepad, :context => :client)
|
88
|
-
save_and_verify_image(image, 'notepad_context_client')
|
89
|
-
window = RAutomation::Window.new(:pid => @notepad)
|
90
|
-
expect([image.width, image.height]).to eq(Win32::Screenshot::BitmapMaker.dimensions_for(window.hwnd, :client))
|
91
|
-
end
|
92
|
-
|
93
|
-
it "captures an area of the window" do
|
94
|
-
image = Win32::Screenshot::Take.of(:window, :pid => @notepad, :area => [30, 30, 100, 150])
|
95
|
-
save_and_verify_image(image, 'notepad_area')
|
96
|
-
expect(image.width).to eq(70)
|
97
|
-
expect(image.height).to eq(120)
|
60
|
+
expect([image.width, image.height]).to eq(Win32::Screenshot::BitmapMaker.dimensions_for(window.hwnd))
|
98
61
|
end
|
99
62
|
|
100
63
|
it "captures by the RAutomation::Window" do
|
101
64
|
window = RAutomation::Window.new(:pid => @notepad)
|
102
65
|
image = Win32::Screenshot::Take.of(:window, :rautomation => window)
|
103
66
|
save_and_verify_image(image, 'notepad_rautomation')
|
104
|
-
expect([image.width, image.height]).to eq(Win32::Screenshot::BitmapMaker.dimensions_for(window.hwnd
|
67
|
+
expect([image.width, image.height]).to eq(Win32::Screenshot::BitmapMaker.dimensions_for(window.hwnd))
|
105
68
|
end
|
106
69
|
|
107
|
-
it "captures a child
|
70
|
+
it "captures a child window" do
|
108
71
|
window = RAutomation::Window.new(:pid => @iexplore).child(:class => "Internet Explorer_Server")
|
109
72
|
image = Win32::Screenshot::Take.of(:window, :rautomation => window)
|
110
73
|
save_and_verify_image(image, 'iexplore_child')
|
111
|
-
expect([image.width, image.height]).to eq(Win32::Screenshot::BitmapMaker.dimensions_for(window.hwnd
|
112
|
-
end
|
113
|
-
|
114
|
-
it "captures a whole window if window size is specified as coordinates" do
|
115
|
-
window = RAutomation::Window.new(:pid => @notepad)
|
116
|
-
expected_width, expected_height = Win32::Screenshot::BitmapMaker.dimensions_for(window.hwnd, :window)
|
117
|
-
image = Win32::Screenshot::Take.of(:window, :rautomation => window, :area => [0, 0, expected_width, expected_height])
|
118
|
-
save_and_verify_image(image, 'notepad_area_full_window')
|
119
|
-
expect(image.width).to eq(expected_width)
|
120
|
-
expect(image.height).to eq(expected_height)
|
121
|
-
end
|
122
|
-
|
123
|
-
it "doesn't allow to capture an area of the window with negative coordinates" do
|
124
|
-
expect {Win32::Screenshot::Take.of(:window, :pid => @notepad, :area => [0, 0, -1, 100])}.
|
125
|
-
to raise_exception("specified coordinates (x1: 0, y1: 0, x2: -1, y2: 100) are invalid - cannot be negative!")
|
126
|
-
end
|
127
|
-
|
128
|
-
it "doesn't allow to capture an area of the window if coordinates are the same" do
|
129
|
-
expect {Win32::Screenshot::Take.of(:window, :pid => @notepad, :area => [10, 0, 10, 20])}.
|
130
|
-
to raise_exception("specified coordinates (x1: 10, y1: 0, x2: 10, y2: 20) are invalid - cannot be x1 >= x2 or y1 >= y2!")
|
131
|
-
end
|
132
|
-
|
133
|
-
it "doesn't allow to capture an area of the window if second coordinate is smaller than first one" do
|
134
|
-
expect {Win32::Screenshot::Take.of(:window, :pid => @notepad, :area => [0, 10, 10, 9])}.
|
135
|
-
to raise_exception("specified coordinates (x1: 0, y1: 10, x2: 10, y2: 9) are invalid - cannot be x1 >= x2 or y1 >= y2!")
|
136
|
-
end
|
137
|
-
|
138
|
-
it "doesn't allow to capture an area of the window with too big coordinates" do
|
139
|
-
window = RAutomation::Window.new(:pid => @notepad)
|
140
|
-
expected_width, expected_height = Win32::Screenshot::BitmapMaker.dimensions_for(window.hwnd, :window)
|
141
|
-
expect {Win32::Screenshot::Take.of(:window, :pid => @notepad, :area => [0, 0, 10, 100000])}.
|
142
|
-
to raise_exception("specified coordinates (x1: 0, y1: 0, x2: 10, y2: 100000) are invalid - maximum x2: #{expected_width} and y2: #{expected_height}!")
|
74
|
+
expect([image.width, image.height]).to eq(Win32::Screenshot::BitmapMaker.dimensions_for(window.hwnd))
|
143
75
|
end
|
144
76
|
|
145
77
|
after :all do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: win32screenshot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jarmo Pertman
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2023-02-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ffi
|
@@ -118,6 +118,7 @@ files:
|
|
118
118
|
- ext/ImageMagick-7.0.9-8-portable-Q16-x86/mogrify.exe
|
119
119
|
- lib/win32/screenshot.rb
|
120
120
|
- lib/win32/screenshot/bitmap_maker.rb
|
121
|
+
- lib/win32/screenshot/desktop.rb
|
121
122
|
- lib/win32/screenshot/image.rb
|
122
123
|
- lib/win32/screenshot/take.rb
|
123
124
|
- lib/win32/screenshot/version.rb
|
@@ -144,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
144
145
|
- !ruby/object:Gem::Version
|
145
146
|
version: '0'
|
146
147
|
requirements: []
|
147
|
-
rubygems_version: 3.
|
148
|
+
rubygems_version: 3.3.24
|
148
149
|
signing_key:
|
149
150
|
specification_version: 4
|
150
151
|
summary: Capture Screenshots on Windows with Ruby to bmp, gif, jpg or png formats.
|