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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: af9b9611193b8f4cc8d98745a6130cf16fe6fb2674e26e76a3745e34cf8de959
4
- data.tar.gz: 278502bd7bc360a2813db04f590ee82b706eaee894cd6ce252227acb0765676e
3
+ metadata.gz: 20fb85f0a8a6db55a03ef208225cb7a0f081b704f41abf405b39e63283c2c7ea
4
+ data.tar.gz: bfeb14a87af3452d3bd39b37edfd13733a940657e671e4e038a1ec06775aa858
5
5
  SHA512:
6
- metadata.gz: 636cfeb1613a8b84a000b1c5b449168cf398306bfbe47de4ac882ad6bfbb5e461055e921b63cc17ad4bd1f915fc6e13c582158da5518f835813f41b55662f947
7
- data.tar.gz: 1209209433553596e94ed0a65c67becbb652756eca79ac3ff00777e5cc8b29ea8faa9eea42312303d048bd5121a985622e5aac93db1a55e86242f3fa196d92a4
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- win32screenshot (3.1.0)
4
+ win32screenshot (4.1.0)
5
5
  ffi (~> 1.15.0)
6
6
  mini_magick (~> 4.9)
7
7
  rautomation (~> 1.1.0)
@@ -42,4 +42,4 @@ DEPENDENCIES
42
42
  yard
43
43
 
44
44
  BUNDLED WITH
45
- 2.3.3
45
+ 2.3.24
data/README.md CHANGED
@@ -1,48 +1,54 @@
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
- gem install win32screenshot
13
-
14
- ## SYNOPSIS
15
-
16
- require 'win32/screenshot'
17
-
18
- # Take a screenshot of the window with the specified title
19
- Win32::Screenshot::Take.of(:window, title: "Windows Internet Explorer").write("image.bmp")
20
-
21
- # Take a screenshot of the foreground
22
- Win32::Screenshot::Take.of(:foreground).write("image.png")
23
-
24
- # Take a screenshot of the foreground, and writing over previous image if it exists
25
- Win32::Screenshot::Take.of(:foreground).write!("image.png")
26
-
27
- # Take a screenshot of the specified window's top-left corner's area
28
- Win32::Screenshot::Take.of(:window, title: /internet/i, area: [10, 10, 20, 20]).write("image.jpg")
29
-
30
- # Take a screenshot of the window with the specified handle
31
- Win32::Screenshot::Take.of(:window, hwnd: 123456).write("image.gif")
32
-
33
- # Take a screenshot of the window's client area (e.g. without title bar) with the specified handle
34
- Win32::Screenshot::Take.of(:window, hwnd: 123456, context: :client)
35
-
36
- # Take a screenshot of the child window with the specified internal class name
37
- Win32::Screenshot::Take.of(:rautomation, RAutomation::Window.new(hwnd: 123456).
38
- child(class: "Internet Explorer_Server")).write("image.png")
39
-
40
- # Use the bitmap blob for something else
41
- image = Win32::Screenshot::Take.of(:window, hwnd: 123456)
42
- image.height # => height of the image
43
- image.width # => width of the image
44
- image.bitmap # => bitmap blob
45
-
46
- ## Copyright
47
-
48
- Copyright (c) Jarmo Pertman, Aslak Hellesøy. See LICENSE for details.
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 :client_dc, :GetDC,
16
- [:long], :long
17
- attach_function :client_rect, :GetClientRect,
18
- [:long, :pointer], :bool
19
- attach_function :window_rect, :GetWindowRect,
20
- [:long, :pointer], :bool
21
- attach_function :foreground_window, :GetForegroundWindow,
22
- [], :long
23
- attach_function :desktop_window, :GetDesktopWindow,
24
- [], :long
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 :bit_blt, :BitBlt,
34
- [:long, :int, :int, :int, :int, :long, :int, :int, :long], :bool
35
- attach_function :di_bits, :GetDIBits,
36
- [:long, :long, :int, :int, :pointer, :pointer, :int], :int
37
- attach_function :delete_object, :DeleteObject,
38
- [:long], :bool
39
- attach_function :delete_dc, :DeleteDC,
40
- [:long], :bool
41
- attach_function :release_dc, :ReleaseDC,
42
- [:long, :long], :int
43
-
44
- def capture_all(hwnd, context)
45
- width, height = dimensions_for(hwnd, context)
46
- capture_area(hwnd, context, 0, 0, width, height)
47
- end
48
-
49
- SRCCOPY = 0x00CC0020
50
- DIB_RGB_COLORS = 0
51
-
52
- def capture_area(hwnd, context, x1, y1, x2, y2)
53
- hScreenDC = send("#{context}_dc", hwnd)
54
- w = x2-x1
55
- h = y2-y1
56
-
57
- hmemDC = create_compatible_dc(hScreenDC)
58
- hmemBM = create_compatible_bitmap(hScreenDC, w, h)
59
- select_object(hmemDC, hmemBM)
60
- bit_blt(hmemDC, 0, 0, w, h, hScreenDC, x1, y1, SRCCOPY)
61
- bitmap_size = w * h * 3 + w % 4 * h
62
- lpvpxldata = FFI::MemoryPointer.new(bitmap_size)
63
-
64
- # Bitmap header
65
- # http://www.fortunecity.com/skyscraper/windows/364/bmpffrmt.html
66
- bmInfo = [40, w, h, 1, 24, 0, 0, 0, 0, 0, 0, 0].pack('L3S2L6')
67
- di_bits(hmemDC, hmemBM, 0, h, lpvpxldata, bmInfo, DIB_RGB_COLORS)
68
-
69
- bmFileHeader = [
70
- 19778,
71
- bitmap_size + 40 + 14,
72
- 0,
73
- 0,
74
- 54
75
- ].pack('SLSSL')
76
-
77
- Image.new(bmFileHeader + bmInfo + lpvpxldata.read_string(bitmap_size), w, h)
78
- ensure
79
- lpvpxldata.free
80
- delete_object(hmemBM)
81
- delete_dc(hmemDC)
82
- release_dc(0, hScreenDC)
83
- end
84
-
85
- def dimensions_for(hwnd, context)
86
- rect = [0, 0, 0, 0].pack('l4')
87
- BitmapMaker.send("#{context}_rect", hwnd.to_i, rect)
88
- left, top, width, height = rect.unpack('l4')
89
-
90
- if context == :window
91
- [width + 1 - left, height + 1 - top]
92
- else
93
- [width, height]
94
- end
95
- end
96
-
97
- end
98
- end
99
- end
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
@@ -0,0 +1,5 @@
1
+ Win32::Screenshot::Desktop = Struct.new(:left, :top, :width, :height) do
2
+ def dimensions
3
+ [left, top, width, height]
4
+ end
5
+ 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.exists? 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
+ 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 or it's area.
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 specified window's top-left corner's area
16
- # Win32::Screenshot::Take.of(:window, :title => /internet/i, :area => [10, 10, 20, 20])
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 an _:area_ and/or _: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, Symbol] :context Context to take a screenshot of. Can be _:window_ or _:client_. Defaults to _:window_
35
- # @option opts [String, Regexp] :title Title of the window
36
- # @option opts [String, Regexp] :text Visible text of the window
37
- # @option opts [String, Regexp] :class Internal class name of the window
38
- # @option opts [String, Fixnum] :hwnd Window handle in decimal format
39
- # @option opts [String, Fixnum] :pid Window process ID (PID)
40
- # @option opts [String, Fixnum] :index 0-based index to specify n-th window to take a screenshot of if
41
- # all other criteria match
42
- # @option opts [RAutomation::Window] :rautomation RAutomation::Window object to take a screenshot of. Useful for
43
- # taking screenshots of the child windows
44
- # @return [Image] the {Image} of the specified object
45
- def of(what, opts = {})
46
- valid_whats = [:foreground, :desktop, :window]
47
- raise "It is not possible to take a screenshot of '#{what}', possible values are #{valid_whats.join(", ")}" unless valid_whats.include?(what)
48
-
49
- self.send(what, {:context => :window}.merge(opts))
50
- end
51
-
52
- alias_method :new, :of
53
-
54
- private
55
-
56
- def foreground(opts)
57
- hwnd = BitmapMaker.foreground_window
58
- take_screenshot(hwnd, opts)
59
- end
60
-
61
- def desktop(opts)
62
- hwnd = BitmapMaker.desktop_window
63
- take_screenshot(hwnd, opts)
64
- end
65
-
66
- def window(opts)
67
- area = {:area => opts.delete(:area)}
68
- context = {:context => opts.delete(:context)}
69
- win = opts[:rautomation] || RAutomation::Window.new(opts)
70
- timeout = Time.now + 10
71
- until win.active?
72
- if Time.now >= timeout
73
- Kernel.warn "[WARN] Failed to set window '#{win.locators.inspect}' into focus for taking the screenshot"
74
- break
75
- end
76
- win.activate
77
- end
78
- take_screenshot(win.hwnd, opts.merge(context).merge(area || {}))
79
- end
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
@@ -1,5 +1,5 @@
1
1
  module Win32
2
2
  module Screenshot
3
- VERSION = "3.1.0"
3
+ VERSION = "4.1.0"
4
4
  end
5
5
  end
@@ -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
- # add bundled ImageMagick into path
11
- ENV["PATH"] = "#{File.dirname(__FILE__) + "/../../ext/ImageMagick-7.0.9-8-portable-Q16-x86"};#{ENV["PATH"]}"
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.exists?(@temp_dir)
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, :window))
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
- it "captures an area of the desktop" do
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 "doesn't allow to capture an area of the desktop with invalid coordinates" do
46
- expect {Win32::Screenshot::Take.of(:desktop, :area => [0, 0, -1, 100])}.
47
- to raise_exception("specified coordinates (x1: 0, y1: 0, x2: -1, y2: 100) are invalid - cannot be negative!")
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, :window))
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, :window))
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, :window))
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, :window))
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 windows" do
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, :window))
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: 3.1.0
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: 2022-02-05 00:00:00.000000000 Z
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.0.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.