rdp-win32screenshot 0.0.6
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.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/History.rdoc +40 -0
- data/LICENSE +20 -0
- data/README.rdoc +76 -0
- data/Rakefile +63 -0
- data/VERSION +1 -0
- data/lib/win32/screenshot.rb +100 -0
- data/lib/win32/screenshot/bitmap_maker.rb +172 -0
- data/lib/win32screenshot.rb +3 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +76 -0
- data/spec/win32_screenshot_spec.rb +184 -0
- metadata +142 -0
data/.document
ADDED
data/History.rdoc
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
= 0.0.6 2010-XX-XX
|
2
|
+
* Trying to bring window to the foreground more aggressively (Roger Pack)
|
3
|
+
|
4
|
+
= 0.0.5 2010-07-07
|
5
|
+
* Added method window_area for capturing specified area of the window instead of full window (Jarmo Pertman)
|
6
|
+
Usage: Win32::Screenshot.window_area(title, x1, y1, x2, y2) {|width, height, bmp|}
|
7
|
+
* Added method foreground_area for capturing area of the foreground (Jarmo Pertman)
|
8
|
+
Usage: Win32::Screenshot.foreground_area(x1, y1, x2, y2) {|width, height, bmp|}
|
9
|
+
* Added method desktop_area for capturing area of the visible view (Jarmo Pertman)
|
10
|
+
Usage: Win32::Screenshot.desktop_area(x1, y1, x2, y2) {|width, height, bmp|}
|
11
|
+
* Added method hwnd_area for capturing area of the window with specified handle (Jarmo Pertman)
|
12
|
+
Usage: Win32::Screenshot.hwnd_area(hwnd, x1, y1, x2, y2) {|width, height, bmp|}
|
13
|
+
|
14
|
+
== Internal changes
|
15
|
+
* Removed usage of ShowWindow with parameter SW_SHOW when trying to bring window to front due it's behaviour of resizing window if it was maximized (Jarmo Pertman)
|
16
|
+
* Using FFI::Struct when searching window handle (Roger Pack)
|
17
|
+
|
18
|
+
= 0.0.4 2010-05-27 - A Complete Overhaul
|
19
|
+
* Fixed a bug where taking of screenshots failed on some sizes of windows (thanks for the tip from Tony Popiel)
|
20
|
+
* Blocks should be used when taking screenshots for cleaning up resources after usage (Aslak Hellesøy)
|
21
|
+
* Changed library structure - it is now needed to require 'win32/screenshot' (Aslak Hellesøy)
|
22
|
+
* Replaced Ruby::DL with Ruby-FFI for better VM support and less segfaults (Jarmo Pertman)
|
23
|
+
* Added Ruby 1.9.1 support (Jarmo Pertman)
|
24
|
+
* Win32::Screenshot.window restores window if it's minimized before taking screenshots and brings it to the foreground (Jarmo Pertman)
|
25
|
+
* Changed some internal method names (Jarmo Pertman)
|
26
|
+
* Replaced Test::Unit with RSpec and made specs more robust (Jarmo Pertman)
|
27
|
+
|
28
|
+
PS! This version is not backwards compatible due to different method names and usage, but upgrading should be relatively easy nevertheless.
|
29
|
+
|
30
|
+
= 0.0.3 2007-01-18
|
31
|
+
* Fixed bug with too many callbacks
|
32
|
+
* Added get_hwnd(Regexp)
|
33
|
+
|
34
|
+
= 0.0.2 2006-12-02
|
35
|
+
* Added desktop method (patch from Ryan Schuft)
|
36
|
+
* Added HTTP server example (patch from Ryan Schuft)
|
37
|
+
* Added window(regexp) method
|
38
|
+
|
39
|
+
= 0.0.1 2006-11-29
|
40
|
+
* First release
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Jarmo Pertman, Aslak Hellesøy
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
= Win32::Screenshot (old name win32screenshot)
|
2
|
+
|
3
|
+
* http://github.com/jarmo/win32screenshot
|
4
|
+
|
5
|
+
== DESCRIPTION
|
6
|
+
|
7
|
+
Capture Screenshots on Windows with Ruby. This library captures
|
8
|
+
screenshots in bmp format, but you may use RMagick to convert these to some
|
9
|
+
other formats like png.
|
10
|
+
|
11
|
+
== SYNOPSIS
|
12
|
+
|
13
|
+
require 'win32/screenshot'
|
14
|
+
|
15
|
+
# take a screenshot of the foreground window
|
16
|
+
Win32::Screenshot.foreground do |width, height, bmp|
|
17
|
+
File.open("picture1.bmp", "wb") {|file| file.puts bmp}
|
18
|
+
end
|
19
|
+
|
20
|
+
# take a screenshot of the area of the foreground where upper left x and y
|
21
|
+
# coordinates are 0 and 10, width is 100 and height is 200
|
22
|
+
Win32::Screenshot.foreground_area(0, 10, 100, 200) do |width, height, bmp|
|
23
|
+
File.open("picture2.bmp", "wb") {|file| file.puts bmp}
|
24
|
+
end
|
25
|
+
|
26
|
+
# take a screenshot of the screen
|
27
|
+
Win32::Screenshot.desktop do |width, height, bmp|
|
28
|
+
File.open("picture3.bmp", "wb") {|file| file.puts bmp}
|
29
|
+
end
|
30
|
+
|
31
|
+
# take a screenshot of the area of the desktop where upper left x and y
|
32
|
+
# coordinates are 0 and 10, width is 100 and height is 200
|
33
|
+
Win32::Screenshot.desktop_area(0, 10, 100, 200) do |width, height, bmp|
|
34
|
+
File.open("picture4.bmp", "wb") {|file| file.puts bmp}
|
35
|
+
end
|
36
|
+
|
37
|
+
# take a screenshot of the window, which has a text part of it's title
|
38
|
+
Win32::Screenshot.window("Internet Explorer") do |width, height, bmp|
|
39
|
+
File.open("picture5.bmp", "wb") {|file| file.puts bmp}
|
40
|
+
end
|
41
|
+
|
42
|
+
# take a screenshot of the window, which matches regexp against it's title
|
43
|
+
Win32::Screenshot.window(/Internet Explorer/) do |width, height, bmp|
|
44
|
+
File.open("picture6.bmp", "wb") {|file| file.puts bmp}
|
45
|
+
end
|
46
|
+
|
47
|
+
# take a screenshot of the area of the window where upper left x and y
|
48
|
+
# coordinates are 0 and 10, width is 100 and height is 200
|
49
|
+
Win32::Screenshot.window_area("Internet Explorer", 0, 10, 100, 200) do |width, height, bmp|
|
50
|
+
File.open("picture7.bmp", "wb") {|file| file.puts bmp}
|
51
|
+
end
|
52
|
+
|
53
|
+
# take a screenshot of the window with specified window handle
|
54
|
+
Win32::Screenshot.hwnd(window_handle) do |width, height, bmp|
|
55
|
+
File.open("picture8.bmp", "wb") {|file| file.puts bmp}
|
56
|
+
end
|
57
|
+
|
58
|
+
# take a screenshot of the area of the window with a window handle
|
59
|
+
# where upper left x and y
|
60
|
+
# coordinates are 0 and 10, width is 100 and height is 200
|
61
|
+
Win32::Screenshot.hwnd_area(window_handle, 0, 10, 100, 200) do |width, height, bmp|
|
62
|
+
File.open("picture9.bmp", "wb") {|file| file.puts bmp}
|
63
|
+
end
|
64
|
+
|
65
|
+
# convert a screenshot to the png format with RMagick
|
66
|
+
require 'rmagick'
|
67
|
+
|
68
|
+
Win32::Screenshot.hwnd(window_handle) do |width, height, bmp|
|
69
|
+
img = Magick::Image.from_blob(bmp)
|
70
|
+
png = img[0].to_blob {self.format = 'PNG'}
|
71
|
+
File.open("picture10.png", "wb") {|file| file.puts png}
|
72
|
+
end
|
73
|
+
|
74
|
+
== Copyright
|
75
|
+
|
76
|
+
Copyright (c) 2010 Jarmo Pertman, Aslak Hellesøy. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rake'
|
5
|
+
require 'os'
|
6
|
+
|
7
|
+
begin
|
8
|
+
require 'jeweler'
|
9
|
+
Jeweler::Tasks.new do |gem|
|
10
|
+
gem.name = "rdp-win32screenshot"
|
11
|
+
gem.summary = %Q{Capture Screenshots on Windows with Ruby}
|
12
|
+
gem.description = %Q{Capture Screenshots on Windows with Ruby}
|
13
|
+
gem.email = ["jarmo.p@gmail.com", "aslak.hellesoy@gmail.com"]
|
14
|
+
gem.homepage = "http://github.com/jarmo/win32screenshot"
|
15
|
+
gem.authors = ["Jarmo Pertman", "Aslak Hellesøy"]
|
16
|
+
|
17
|
+
gem.rdoc_options = ["--main", "README.rdoc"]
|
18
|
+
|
19
|
+
gem.add_dependency "ffi"
|
20
|
+
|
21
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
22
|
+
gem.add_development_dependency 'os'
|
23
|
+
if OS.java?
|
24
|
+
gem.add_development_dependency "rmagick4j"
|
25
|
+
else
|
26
|
+
gem.add_development_dependency "rmagick"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
Jeweler::GemcutterTasks.new
|
30
|
+
rescue LoadError
|
31
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
32
|
+
end
|
33
|
+
|
34
|
+
require 'spec/rake/spectask'
|
35
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
36
|
+
spec.libs << 'lib' << 'spec'
|
37
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
38
|
+
end
|
39
|
+
|
40
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
41
|
+
spec.libs << 'lib' << 'spec'
|
42
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
43
|
+
spec.rcov = true
|
44
|
+
end
|
45
|
+
|
46
|
+
task :spec => :check_dependencies
|
47
|
+
|
48
|
+
task :default => :spec
|
49
|
+
|
50
|
+
require 'rake/rdoctask'
|
51
|
+
Rake::RDocTask.new do |rdoc|
|
52
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
53
|
+
|
54
|
+
rdoc.rdoc_dir = 'rdoc'
|
55
|
+
rdoc.title = "Win32::Screenshot #{version}"
|
56
|
+
rdoc.rdoc_files.include('README*')
|
57
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
58
|
+
end
|
59
|
+
|
60
|
+
desc "Remove all temporary files"
|
61
|
+
task :clobber => [:clobber_rdoc, :clobber_rcov] do
|
62
|
+
rm_r "spec/tmp"
|
63
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.6
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'win32/screenshot/bitmap_maker'
|
2
|
+
|
3
|
+
module Win32
|
4
|
+
# Captures screenshots with Ruby on Windows
|
5
|
+
class Screenshot
|
6
|
+
class << self
|
7
|
+
|
8
|
+
# captures foreground
|
9
|
+
def foreground(&proc)
|
10
|
+
hwnd = BitmapMaker.foreground_window
|
11
|
+
BitmapMaker.capture_all(hwnd, &proc)
|
12
|
+
end
|
13
|
+
|
14
|
+
# captures area of the foreground
|
15
|
+
# where *x1* and *y1* are 0 in the upper left corner and
|
16
|
+
# *x2* specifies the width and *y2* the height of the area to be captured
|
17
|
+
def foreground_area(x1, y1, x2, y2, &proc)
|
18
|
+
hwnd = BitmapMaker.foreground_window
|
19
|
+
validate_coordinates(hwnd, x1, y1, x2, y2)
|
20
|
+
BitmapMaker.capture_area(hwnd, x1, y1, x2, y2, &proc)
|
21
|
+
end
|
22
|
+
|
23
|
+
# captures visible view of the screen
|
24
|
+
#
|
25
|
+
# to make screenshot of the real desktop, all
|
26
|
+
# windows must be minimized before
|
27
|
+
def desktop(&proc)
|
28
|
+
hwnd = BitmapMaker.desktop_window
|
29
|
+
BitmapMaker.capture_all(hwnd, &proc)
|
30
|
+
end
|
31
|
+
|
32
|
+
# captures area of the visible view of the screen
|
33
|
+
# where *x1* and *y1* are 0 in the upper left corner and
|
34
|
+
# *x2* specifies the width and *y2* the height of the area to be captured
|
35
|
+
#
|
36
|
+
# to make screenshot of the real desktop, all
|
37
|
+
# windows must be minimized before
|
38
|
+
def desktop_area(x1, y1, x2, y2, &proc)
|
39
|
+
hwnd = BitmapMaker.desktop_window
|
40
|
+
validate_coordinates(hwnd, x1, y1, x2, y2)
|
41
|
+
BitmapMaker.capture_area(hwnd, x1, y1, x2, y2, &proc)
|
42
|
+
end
|
43
|
+
|
44
|
+
# captures window with a *title_query* and waits *pause* (by default is 0.5)
|
45
|
+
# seconds after trying to set window to the foreground
|
46
|
+
def window(title_query, pause=0.5, &proc)
|
47
|
+
hwnd = window_hwnd(title_query)
|
48
|
+
hwnd(hwnd, pause, &proc)
|
49
|
+
end
|
50
|
+
|
51
|
+
# captures area of the window with a *title_query*
|
52
|
+
# where *x1* and *y1* are 0 in the upper left corner and
|
53
|
+
# *x2* specifies the width and *y2* the height of the area to be captured
|
54
|
+
def window_area(title_query, x1, y1, x2, y2, pause=0.5, &proc)
|
55
|
+
hwnd = window_hwnd(title_query)
|
56
|
+
hwnd_area(hwnd, x1, y1, x2, y2, pause, &proc)
|
57
|
+
end
|
58
|
+
|
59
|
+
# captures by window handle
|
60
|
+
def hwnd(hwnd, pause=0.5, &proc)
|
61
|
+
BitmapMaker.prepare_window(hwnd, pause)
|
62
|
+
BitmapMaker.capture_all(hwnd, &proc)
|
63
|
+
end
|
64
|
+
|
65
|
+
# captures area of the window with a handle of *hwnd*
|
66
|
+
# where *x1* and *y1* are 0 in the upper left corner and
|
67
|
+
# *x2* specifies the width and *y2* the height of the area to be captured
|
68
|
+
def hwnd_area(hwnd, x1, y1, x2, y2, pause=0.5, &proc)
|
69
|
+
validate_coordinates(hwnd, x1, y1, x2, y2)
|
70
|
+
BitmapMaker.prepare_window(hwnd, pause)
|
71
|
+
BitmapMaker.capture_area(hwnd, x1, y1, x2, y2, &proc)
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def window_hwnd(title_query)
|
77
|
+
hwnd = BitmapMaker.hwnd(title_query)
|
78
|
+
raise "window with title '#{title_query}' was not found!" unless hwnd
|
79
|
+
hwnd
|
80
|
+
end
|
81
|
+
|
82
|
+
def validate_coordinates(hwnd, *coords)
|
83
|
+
specified_coordinates = coords.join(', ')
|
84
|
+
if coords.any? {|c| c < 0}
|
85
|
+
raise "specified coordinates (#{specified_coordinates}) are invalid! cannot have any less than zero"
|
86
|
+
end
|
87
|
+
x1, y1, x2, y2 = *coords
|
88
|
+
if x1 >= x2 || y1 >= y2
|
89
|
+
raise "specified coordinates (#{specified_coordinates}) are invalid! cannot have x1 > x2 or y1 > y2"
|
90
|
+
end
|
91
|
+
|
92
|
+
x1_always_zero, y1_always_zero, max_x2, max_y2 = BitmapMaker.dimensions_for(hwnd)
|
93
|
+
if x2 > max_x2 || y2 > max_y2
|
94
|
+
raise "specified coordinates (#{specified_coordinates}) are invalid! max is #{max_x2},#{max_y2}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module Win32
|
4
|
+
class Screenshot
|
5
|
+
# internal methods
|
6
|
+
module BitmapMaker #:nodoc:all
|
7
|
+
extend FFI::Library
|
8
|
+
|
9
|
+
ffi_lib 'user32', 'gdi32'
|
10
|
+
ffi_convention :stdcall
|
11
|
+
|
12
|
+
callback :enum_callback, [:long, :pointer], :bool
|
13
|
+
|
14
|
+
# user32.dll
|
15
|
+
attach_function :enum_windows, :EnumWindows,
|
16
|
+
[:enum_callback, :pointer], :long
|
17
|
+
attach_function :window_text, :GetWindowTextA,
|
18
|
+
[:long, :pointer, :int], :int
|
19
|
+
attach_function :window_text_length, :GetWindowTextLengthA,
|
20
|
+
[:long], :int
|
21
|
+
attach_function :window_visible, :IsWindowVisible,
|
22
|
+
[:long], :bool
|
23
|
+
attach_function :dc, :GetDC,
|
24
|
+
[:long], :long
|
25
|
+
attach_function :client_rect, :GetClientRect,
|
26
|
+
[:long, :pointer], :bool
|
27
|
+
attach_function :minimized, :IsIconic,
|
28
|
+
[:long], :bool
|
29
|
+
attach_function :show_window, :ShowWindow,
|
30
|
+
[:long, :int], :bool
|
31
|
+
attach_function :foreground_window, :GetForegroundWindow,
|
32
|
+
[], :long
|
33
|
+
attach_function :desktop_window, :GetDesktopWindow,
|
34
|
+
[], :long
|
35
|
+
attach_function :window_thread_process_id, :GetWindowThreadProcessId,
|
36
|
+
[:long, :pointer], :long
|
37
|
+
attach_function :attach_thread_input, :AttachThreadInput,
|
38
|
+
[:long, :long, :bool], :bool
|
39
|
+
attach_function :set_foreground_window, :SetForegroundWindow,
|
40
|
+
[:long], :bool
|
41
|
+
attach_function :bring_window_to_top, :BringWindowToTop,
|
42
|
+
[:long], :bool
|
43
|
+
attach_function :set_active_window, :SetActiveWindow,
|
44
|
+
[:long], :long
|
45
|
+
|
46
|
+
|
47
|
+
# gdi32.dll
|
48
|
+
attach_function :create_compatible_dc, :CreateCompatibleDC,
|
49
|
+
[:long], :long
|
50
|
+
attach_function :create_compatible_bitmap, :CreateCompatibleBitmap,
|
51
|
+
[:long, :int, :int], :long
|
52
|
+
attach_function :select_object, :SelectObject,
|
53
|
+
[:long, :long], :long
|
54
|
+
attach_function :bit_blt, :BitBlt,
|
55
|
+
[:long, :int, :int, :int, :int, :long, :int, :int, :long], :bool
|
56
|
+
attach_function :di_bits, :GetDIBits,
|
57
|
+
[:long, :long, :int, :int, :pointer, :pointer, :int], :int
|
58
|
+
attach_function :delete_object, :DeleteObject,
|
59
|
+
[:long], :bool
|
60
|
+
attach_function :delete_dc, :DeleteDC,
|
61
|
+
[:long], :bool
|
62
|
+
attach_function :release_dc, :ReleaseDC,
|
63
|
+
[:long, :long], :int
|
64
|
+
|
65
|
+
|
66
|
+
EnumWindowCallback = Proc.new do |hwnd, param|
|
67
|
+
searched_window = WindowStruct.new param
|
68
|
+
title_length = window_text_length(hwnd) + 1
|
69
|
+
title = FFI::MemoryPointer.new :char, title_length
|
70
|
+
window_text(hwnd, title, title_length)
|
71
|
+
title = title.read_string
|
72
|
+
if title =~ Regexp.new(searched_window[:title].read_string) && window_visible(hwnd)
|
73
|
+
searched_window[:hwnd] = hwnd
|
74
|
+
false
|
75
|
+
else
|
76
|
+
true
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
module_function
|
81
|
+
|
82
|
+
class WindowStruct < FFI::Struct
|
83
|
+
layout :title, :pointer,
|
84
|
+
:hwnd, :long
|
85
|
+
end
|
86
|
+
|
87
|
+
def hwnd(window_title)
|
88
|
+
window = WindowStruct.new
|
89
|
+
window_title = FFI::MemoryPointer.from_string(window_title.to_s)
|
90
|
+
window[:title] = window_title
|
91
|
+
enum_windows(EnumWindowCallback, window.to_ptr)
|
92
|
+
window[:hwnd] == 0 ? nil : window[:hwnd]
|
93
|
+
end
|
94
|
+
|
95
|
+
def prepare_window(hwnd, pause)
|
96
|
+
restore(hwnd) if minimized(hwnd)
|
97
|
+
set_foreground(hwnd)
|
98
|
+
sleep pause
|
99
|
+
end
|
100
|
+
|
101
|
+
SW_RESTORE = 9
|
102
|
+
|
103
|
+
def restore(hwnd)
|
104
|
+
show_window(hwnd, SW_RESTORE)
|
105
|
+
end
|
106
|
+
|
107
|
+
def set_foreground(hwnd)
|
108
|
+
if foreground_window != hwnd
|
109
|
+
set_foreground_window(hwnd)
|
110
|
+
set_active_window(hwnd)
|
111
|
+
bring_window_to_top(hwnd)
|
112
|
+
# and just in case...
|
113
|
+
foreground_thread = window_thread_process_id(foreground_window, nil)
|
114
|
+
other_thread = window_thread_process_id(hwnd, nil)
|
115
|
+
attach_thread_input(foreground_thread, other_thread, true) unless other_thread == foreground_thread
|
116
|
+
set_foreground_window(hwnd)
|
117
|
+
set_active_window(hwnd)
|
118
|
+
bring_window_to_top(hwnd)
|
119
|
+
attach_thread_input(foreground_thread, other_thread, false) unless other_thread == foreground_thread
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def dimensions_for(hwnd)
|
124
|
+
rect = [0, 0, 0, 0].pack('L4')
|
125
|
+
client_rect(hwnd, rect)
|
126
|
+
x1, y1, x2, y2 = rect.unpack('L4')
|
127
|
+
end
|
128
|
+
|
129
|
+
def capture_all(hwnd, &proc)
|
130
|
+
x1, y1, x2, y2 = dimensions_for(hwnd)
|
131
|
+
capture_area(hwnd, x1, y1, x2, y2, &proc)
|
132
|
+
end
|
133
|
+
|
134
|
+
SRCCOPY = 0x00CC0020
|
135
|
+
DIB_RGB_COLORS = 0
|
136
|
+
|
137
|
+
def capture_area(hwnd, x1, y1, x2, y2, &proc)
|
138
|
+
hScreenDC = dc(hwnd)
|
139
|
+
w = x2-x1
|
140
|
+
h = y2-y1
|
141
|
+
|
142
|
+
hmemDC = create_compatible_dc(hScreenDC)
|
143
|
+
hmemBM = create_compatible_bitmap(hScreenDC, w, h)
|
144
|
+
select_object(hmemDC, hmemBM)
|
145
|
+
bit_blt(hmemDC, 0, 0, w, h, hScreenDC, x1, y1, SRCCOPY)
|
146
|
+
bitmap_size = w * h * 3 + w % 4 * h
|
147
|
+
lpvpxldata = FFI::MemoryPointer.new(bitmap_size)
|
148
|
+
|
149
|
+
# Bitmap header
|
150
|
+
# http://www.fortunecity.com/skyscraper/windows/364/bmpffrmt.html
|
151
|
+
bmInfo = [40, w, h, 1, 24, 0, 0, 0, 0, 0, 0, 0].pack('L3S2L6')
|
152
|
+
di_bits(hmemDC, hmemBM, 0, h, lpvpxldata, bmInfo, DIB_RGB_COLORS)
|
153
|
+
|
154
|
+
bmFileHeader = [
|
155
|
+
19778,
|
156
|
+
bitmap_size + 40 + 14,
|
157
|
+
0,
|
158
|
+
0,
|
159
|
+
54
|
160
|
+
].pack('SLSSL')
|
161
|
+
|
162
|
+
bmp_data = bmFileHeader + bmInfo + lpvpxldata.read_string(bitmap_size)
|
163
|
+
proc.call(w, h, bmp_data)
|
164
|
+
ensure
|
165
|
+
lpvpxldata.free
|
166
|
+
delete_object(hmemBM)
|
167
|
+
delete_dc(hmemDC)
|
168
|
+
release_dc(0, hScreenDC)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
require 'win32/screenshot'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'spec'
|
6
|
+
require 'spec/autorun'
|
7
|
+
require 'RMagick'
|
8
|
+
require 'fileutils'
|
9
|
+
|
10
|
+
module SpecHelper
|
11
|
+
SW_MAXIMIZE = 3
|
12
|
+
SW_MINIMIZE = 6
|
13
|
+
HWND_TOPMOST = -1
|
14
|
+
HWND_NOTOPMOST = -2
|
15
|
+
SWP_NOSIZE = 1
|
16
|
+
SWP_NOMOVE = 2
|
17
|
+
SWP_SHOWWINDOW = 40
|
18
|
+
|
19
|
+
extend FFI::Library
|
20
|
+
ffi_lib 'user32'
|
21
|
+
ffi_convention :stdcall
|
22
|
+
|
23
|
+
# user32.dll
|
24
|
+
attach_function :set_window_pos, :SetWindowPos,
|
25
|
+
[:long, :long, :int, :int, :int, :int, :int], :bool
|
26
|
+
|
27
|
+
def cleanup
|
28
|
+
FileUtils.rm Dir.glob(File.join(File.dirname(__FILE__), "tmp/*"))
|
29
|
+
end
|
30
|
+
|
31
|
+
def check_image(bmp, file=nil)
|
32
|
+
temp_dir = File.join(File.dirname(__FILE__), 'tmp')
|
33
|
+
FileUtils.mkdir temp_dir unless File.exists?(temp_dir)
|
34
|
+
File.open(File.join(temp_dir, "#{file}.bmp"), "wb") {|io| io.write(bmp)} unless file.nil?
|
35
|
+
bmp[0..1].should == 'BM'
|
36
|
+
img = Magick::Image.from_blob(bmp)
|
37
|
+
png = img[0].to_blob {self.format = 'PNG'}
|
38
|
+
png[0..3].should == "\211PNG"
|
39
|
+
File.open(File.join(temp_dir, "#{file}.png"), "wb") {|io| io.puts(png)} unless file.nil?
|
40
|
+
end
|
41
|
+
|
42
|
+
def wait_for_programs_to_open
|
43
|
+
until Win32::Screenshot::BitmapMaker.hwnd(/Internet Explorer/) &&
|
44
|
+
Win32::Screenshot::BitmapMaker.hwnd(/Notepad/) &&
|
45
|
+
Win32::Screenshot::BitmapMaker.hwnd(/Calculator/)
|
46
|
+
sleep 0.1
|
47
|
+
end
|
48
|
+
# just in case of slow PC
|
49
|
+
sleep 10
|
50
|
+
end
|
51
|
+
|
52
|
+
def maximize title
|
53
|
+
Win32::Screenshot::BitmapMaker.show_window(Win32::Screenshot::BitmapMaker.hwnd(title),
|
54
|
+
SW_MAXIMIZE)
|
55
|
+
sleep 1
|
56
|
+
end
|
57
|
+
|
58
|
+
def minimize title
|
59
|
+
Win32::Screenshot::BitmapMaker.show_window(Win32::Screenshot::BitmapMaker.hwnd(title),
|
60
|
+
SW_MINIMIZE)
|
61
|
+
sleep 1
|
62
|
+
end
|
63
|
+
|
64
|
+
def resize title
|
65
|
+
hwnd = Win32::Screenshot::BitmapMaker.hwnd(title)
|
66
|
+
set_window_pos(hwnd,
|
67
|
+
HWND_TOPMOST,
|
68
|
+
0, 0, 150, 238,
|
69
|
+
SWP_NOMOVE)
|
70
|
+
set_window_pos(hwnd,
|
71
|
+
HWND_NOTOPMOST,
|
72
|
+
0, 0, 0, 0,
|
73
|
+
SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE)
|
74
|
+
sleep 1
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "win32-screenshot" do
|
4
|
+
include SpecHelper
|
5
|
+
|
6
|
+
before :all do
|
7
|
+
cleanup
|
8
|
+
PROGRAM_FILES = "c:/program files/"
|
9
|
+
@notepad = IO.popen("notepad").pid
|
10
|
+
@iexplore = IO.popen(File.join(PROGRAM_FILES, "Internet Explorer", "iexplore about:blank")).pid
|
11
|
+
@calc = IO.popen("calc").pid
|
12
|
+
wait_for_programs_to_open
|
13
|
+
end
|
14
|
+
|
15
|
+
it "captures foreground" do
|
16
|
+
Win32::Screenshot.foreground do |width, height, bmp|
|
17
|
+
check_image(bmp, 'foreground')
|
18
|
+
hwnd = Win32::Screenshot::BitmapMaker.foreground_window
|
19
|
+
dimensions = Win32::Screenshot::BitmapMaker.dimensions_for(hwnd)
|
20
|
+
width.should == dimensions[2]
|
21
|
+
height.should == dimensions[3]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it "captures area of the foreground" do
|
26
|
+
Win32::Screenshot.foreground_area(30, 30, 100, 150) do |width, height, bmp|
|
27
|
+
check_image(bmp, 'foreground_area')
|
28
|
+
width.should == 70
|
29
|
+
height.should == 120
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it "doesn't allow to capture area of the foreground with invalid coordinates" do
|
34
|
+
lambda {Win32::Screenshot.foreground_area(0, 0, -1, 100) {|width, height, bmp| check_image('foreground2')}}.
|
35
|
+
should raise_exception("specified coordinates (0, 0, -1, 100) are invalid! cannot have any less than zero")
|
36
|
+
end
|
37
|
+
|
38
|
+
it "captures desktop" do
|
39
|
+
Win32::Screenshot.desktop do |width, height, bmp|
|
40
|
+
check_image(bmp, 'desktop')
|
41
|
+
hwnd = Win32::Screenshot::BitmapMaker.desktop_window
|
42
|
+
dimensions = Win32::Screenshot::BitmapMaker.dimensions_for(hwnd)
|
43
|
+
width.should == dimensions[2]
|
44
|
+
height.should == dimensions[3]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it "captures area of the desktop" do
|
49
|
+
Win32::Screenshot.desktop_area(30, 30, 100, 150) do |width, height, bmp|
|
50
|
+
check_image(bmp, 'desktop_area')
|
51
|
+
width.should == 70
|
52
|
+
height.should == 120
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
it "doesn't allow to capture area of the desktop with invalid coordinates" do
|
57
|
+
lambda {Win32::Screenshot.desktop_area(0, 0, -1, 100) {|width, height, bmp| check_image('desktop2')}}.
|
58
|
+
should raise_exception("specified coordinates (0, 0, -1, 100) are invalid! cannot have any less than zero")
|
59
|
+
end
|
60
|
+
|
61
|
+
it "captures maximized window by window title" do
|
62
|
+
title = "Internet Explorer"
|
63
|
+
maximize(title)
|
64
|
+
Win32::Screenshot.window(title) do |width, height, bmp|
|
65
|
+
check_image(bmp, 'iexplore')
|
66
|
+
hwnd = Win32::Screenshot::BitmapMaker.hwnd(title)
|
67
|
+
dimensions = Win32::Screenshot::BitmapMaker.dimensions_for(hwnd)
|
68
|
+
width.should == dimensions[2]
|
69
|
+
height.should == dimensions[3]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it "captures minimized window by window title as a regexp" do
|
74
|
+
title = /calculator/i
|
75
|
+
minimize(title)
|
76
|
+
Win32::Screenshot.window(title) do |width, height, bmp|
|
77
|
+
check_image(bmp, 'calc')
|
78
|
+
hwnd = Win32::Screenshot::BitmapMaker.hwnd(title)
|
79
|
+
dimensions = Win32::Screenshot::BitmapMaker.dimensions_for(hwnd)
|
80
|
+
width.should == dimensions[2]
|
81
|
+
height.should == dimensions[3]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it "captures small windows" do
|
86
|
+
title = /Notepad/
|
87
|
+
resize(title)
|
88
|
+
Win32::Screenshot.window(title) do |width, height, bmp|
|
89
|
+
check_image(bmp, 'notepad')
|
90
|
+
# we should get the size of the picture because
|
91
|
+
# screenshot doesn't include titlebar and the size
|
92
|
+
# varies between different themes and Windows versions
|
93
|
+
hwnd = Win32::Screenshot::BitmapMaker.hwnd(title)
|
94
|
+
dimensions = Win32::Screenshot::BitmapMaker.dimensions_for(hwnd)
|
95
|
+
width.should == dimensions[2]
|
96
|
+
height.should == dimensions[3]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
it "captures area of the window" do
|
101
|
+
title = /calculator/i
|
102
|
+
Win32::Screenshot.window_area(title, 30, 30, 100, 150) do |width, height, bmp|
|
103
|
+
check_image(bmp, 'calc_area')
|
104
|
+
width.should == 70
|
105
|
+
height.should == 120
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
it "captures whole window if window size is specified as coordinates" do
|
110
|
+
title = /calculator/i
|
111
|
+
hwnd = Win32::Screenshot::BitmapMaker.hwnd(title)
|
112
|
+
dimensions = Win32::Screenshot::BitmapMaker.dimensions_for(hwnd)
|
113
|
+
Win32::Screenshot.window_area(title, 0, 0, dimensions[2], dimensions[3]) do |width, height, bmp|
|
114
|
+
check_image(bmp, 'calc_area_full_window')
|
115
|
+
width.should == dimensions[2]
|
116
|
+
height.should == dimensions[3]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
it "doesn't allow to capture area of the window with negative coordinates" do
|
121
|
+
title = /calculator/i
|
122
|
+
lambda {Win32::Screenshot.window_area(title, 0, 0, -1, 100) {|width, height, bmp| check_image('calc2')}}.
|
123
|
+
should raise_exception("specified coordinates (0, 0, -1, 100) are invalid! cannot have any less than zero")
|
124
|
+
end
|
125
|
+
|
126
|
+
it "doesn't allow to capture area of the window if coordinates are the same" do
|
127
|
+
title = /calculator/i
|
128
|
+
lambda {Win32::Screenshot.window_area(title, 10, 0, 10, 20) {|width, height, bmp| check_image('calc4')}}.
|
129
|
+
should raise_exception("specified coordinates (10, 0, 10, 20) are invalid! cannot have x1 > x2 or y1 > y2")
|
130
|
+
end
|
131
|
+
|
132
|
+
it "doesn't allow to capture area of the window if second coordinate is smaller than first one" do
|
133
|
+
title = /calculator/i
|
134
|
+
lambda {Win32::Screenshot.window_area(title, 0, 10, 10, 9) {|width, height, bmp| check_image('calc5')}}.
|
135
|
+
should raise_exception("specified coordinates (0, 10, 10, 9) are invalid! cannot have x1 > x2 or y1 > y2")
|
136
|
+
end
|
137
|
+
|
138
|
+
it "doesn't allow to capture area of the window with too big coordinates" do
|
139
|
+
title = /calculator/i
|
140
|
+
lambda {Win32::Screenshot.window_area(title, 0, 0, 10, 10000) {|width, height, bmp| check_image('calc3')}}.
|
141
|
+
should raise_exception("specified coordinates (0, 0, 10, 10000) are invalid! max is 212,264")
|
142
|
+
end
|
143
|
+
|
144
|
+
it "captures by window with handle" do
|
145
|
+
title = /calculator/i
|
146
|
+
hwnd = Win32::Screenshot::BitmapMaker.hwnd(title)
|
147
|
+
Win32::Screenshot.hwnd(hwnd) do |width, height, bmp|
|
148
|
+
check_image(bmp, 'calc_hwnd')
|
149
|
+
dimensions = Win32::Screenshot::BitmapMaker.dimensions_for(hwnd)
|
150
|
+
width.should == dimensions[2]
|
151
|
+
height.should == dimensions[3]
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
it "captures area of the window with handle" do
|
156
|
+
hwnd = Win32::Screenshot::BitmapMaker.hwnd(/calculator/i)
|
157
|
+
Win32::Screenshot.hwnd_area(hwnd, 30, 30, 100, 150) do |width, height, bmp|
|
158
|
+
check_image(bmp, 'calc_hwnd_area')
|
159
|
+
width.should == 70
|
160
|
+
height.should == 120
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
it "doesn't allow to capture area of the window with handle with invalid coordinates" do
|
165
|
+
hwnd = Win32::Screenshot::BitmapMaker.hwnd(/calculator/i)
|
166
|
+
lambda {Win32::Screenshot.hwnd_area(hwnd, 0, 0, -1, 100) {|width, height, bmp| check_image('desktop2')}}.
|
167
|
+
should raise_exception("specified coordinates (0, 0, -1, 100) are invalid! cannot have any less than zero")
|
168
|
+
end
|
169
|
+
|
170
|
+
it "captures based on coordinates" do
|
171
|
+
hwnd = Win32::Screenshot::BitmapMaker.hwnd(/calculator/i)
|
172
|
+
bmp1 = bmp2 = nil
|
173
|
+
Win32::Screenshot.hwnd_area(hwnd, 100, 100, 170, 220) do |width, height, bmp|; bmp1 = bmp; end
|
174
|
+
Win32::Screenshot.hwnd_area(hwnd, 0, 0, 70, 120) do |width, height, bmp|; bmp2 = bmp; end
|
175
|
+
bmp1.length.should == bmp2.length
|
176
|
+
bmp1.should_not == bmp2
|
177
|
+
end
|
178
|
+
|
179
|
+
after :all do
|
180
|
+
Process.kill 9, @notepad
|
181
|
+
Process.kill 9, @iexplore rescue nil # allow for a pre-existing IE to have been used.
|
182
|
+
Process.kill 9, @calc
|
183
|
+
end
|
184
|
+
end
|
metadata
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rdp-win32screenshot
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 19
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 6
|
10
|
+
version: 0.0.6
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Jarmo Pertman
|
14
|
+
- "Aslak Helles\xC3\xB8y"
|
15
|
+
autorequire:
|
16
|
+
bindir: bin
|
17
|
+
cert_chain: []
|
18
|
+
|
19
|
+
date: 2010-07-14 00:00:00 -06:00
|
20
|
+
default_executable:
|
21
|
+
dependencies:
|
22
|
+
- !ruby/object:Gem::Dependency
|
23
|
+
name: ffi
|
24
|
+
prerelease: false
|
25
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ">="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
hash: 3
|
31
|
+
segments:
|
32
|
+
- 0
|
33
|
+
version: "0"
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 13
|
45
|
+
segments:
|
46
|
+
- 1
|
47
|
+
- 2
|
48
|
+
- 9
|
49
|
+
version: 1.2.9
|
50
|
+
type: :development
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: os
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 3
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
64
|
+
type: :development
|
65
|
+
version_requirements: *id003
|
66
|
+
- !ruby/object:Gem::Dependency
|
67
|
+
name: rmagick
|
68
|
+
prerelease: false
|
69
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
hash: 3
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
type: :development
|
79
|
+
version_requirements: *id004
|
80
|
+
description: Capture Screenshots on Windows with Ruby
|
81
|
+
email:
|
82
|
+
- jarmo.p@gmail.com
|
83
|
+
- aslak.hellesoy@gmail.com
|
84
|
+
executables: []
|
85
|
+
|
86
|
+
extensions: []
|
87
|
+
|
88
|
+
extra_rdoc_files:
|
89
|
+
- LICENSE
|
90
|
+
- README.rdoc
|
91
|
+
files:
|
92
|
+
- .document
|
93
|
+
- .gitignore
|
94
|
+
- History.rdoc
|
95
|
+
- LICENSE
|
96
|
+
- README.rdoc
|
97
|
+
- Rakefile
|
98
|
+
- VERSION
|
99
|
+
- lib/win32/screenshot.rb
|
100
|
+
- lib/win32/screenshot/bitmap_maker.rb
|
101
|
+
- lib/win32screenshot.rb
|
102
|
+
- spec/spec.opts
|
103
|
+
- spec/spec_helper.rb
|
104
|
+
- spec/win32_screenshot_spec.rb
|
105
|
+
has_rdoc: true
|
106
|
+
homepage: http://github.com/jarmo/win32screenshot
|
107
|
+
licenses: []
|
108
|
+
|
109
|
+
post_install_message:
|
110
|
+
rdoc_options:
|
111
|
+
- --main
|
112
|
+
- README.rdoc
|
113
|
+
require_paths:
|
114
|
+
- lib
|
115
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
116
|
+
none: false
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
hash: 3
|
121
|
+
segments:
|
122
|
+
- 0
|
123
|
+
version: "0"
|
124
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
125
|
+
none: false
|
126
|
+
requirements:
|
127
|
+
- - ">="
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
hash: 3
|
130
|
+
segments:
|
131
|
+
- 0
|
132
|
+
version: "0"
|
133
|
+
requirements: []
|
134
|
+
|
135
|
+
rubyforge_project:
|
136
|
+
rubygems_version: 1.3.7
|
137
|
+
signing_key:
|
138
|
+
specification_version: 3
|
139
|
+
summary: Capture Screenshots on Windows with Ruby
|
140
|
+
test_files:
|
141
|
+
- spec/spec_helper.rb
|
142
|
+
- spec/win32_screenshot_spec.rb
|