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 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.