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