win32screenshot 0.0.2 → 0.0.4

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.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,6 @@
1
+ coverage
2
+ rdoc
3
+ pkg
4
+ .idea
5
+ *.bmp
6
+ *.png
@@ -0,0 +1,23 @@
1
+ = 0.0.4 2010-05-27 - A Complete Overhaul
2
+ * Fixed a bug where taking of screenshots failed on some sizes of windows (thanks for the tip from Tony Popiel)
3
+ * Blocks should be used when taking screenshots for cleaning up resources after usage (Aslak Hellesøy)
4
+ * Changed library structure - it is now needed to require 'win32/screenshot' (Aslak Hellesøy)
5
+ * Replaced Ruby::DL with Ruby-FFI for better VM support and less segfaults (Jarmo Pertman)
6
+ * Added Ruby 1.9.1 support (Jarmo Pertman)
7
+ * Win32::Screenshot.window restores window if it's minimized before taking screenshots and brings it to the foreground (Jarmo Pertman)
8
+ * Changed some internal method names (Jarmo Pertman)
9
+ * Replaced Test::Unit with RSpec and made specs more robust (Jarmo Pertman)
10
+
11
+ PS! This version is not backwards compatible due to different method names and usage, but upgrading should be relatively easy nevertheless.
12
+
13
+ = 0.0.3 2007-01-18
14
+ * Fixed bug with too many callbacks
15
+ * Added get_hwnd(Regexp)
16
+
17
+ = 0.0.2 2006-12-02
18
+ * Added desktop method (patch from Ryan Schuft)
19
+ * Added HTTP server example (patch from Ryan Schuft)
20
+ * Added window(regexp) method
21
+
22
+ = 0.0.1 2006-11-29
23
+ * 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.
@@ -0,0 +1,51 @@
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 screen
21
+ Win32::Screenshot.desktop do |width, height, bmp|
22
+ File.open("picture2.bmp", "wb") {|file| file.puts bmp}
23
+ end
24
+
25
+ # take a screenshot of the window, which has a text part of it's title
26
+ Win32::Screenshot.window("Internet Explorer") do |width, height, bmp|
27
+ File.open("picture3.bmp", "wb") {|file| file.puts bmp}
28
+ end
29
+
30
+ # take a screenshot of the window, which matches regexp against it's title
31
+ Win32::Screenshot.window(/Internet Explorer/) do |width, height, bmp|
32
+ File.open("picture4.bmp", "wb") {|file| file.puts bmp}
33
+ end
34
+
35
+ # take a screenshot of the window with specified window handle
36
+ Win32::Screenshot.hwnd(window_handle) do |width, height, bmp|
37
+ File.open("picture5.bmp", "wb") {|file| file.puts bmp}
38
+ end
39
+
40
+ # convert a screenshot to the png format with RMagick
41
+ require 'rmagick'
42
+
43
+ Win32::Screenshot.hwnd(window_handle) do |width, height, bmp|
44
+ img = Magick::Image.from_blob(bmp)
45
+ png = img[0].to_blob {self.format = 'PNG'}
46
+ File.open("picture6.png", "wb") {|file| file.puts png}
47
+ end
48
+
49
+ == Copyright
50
+
51
+ Copyright (c) 2010 Jarmo Pertman, Aslak Hellesøy. See LICENSE for details.
data/Rakefile CHANGED
@@ -1,50 +1,52 @@
1
+ # coding: utf-8
2
+
1
3
  require 'rubygems'
2
4
  require 'rake'
3
- require 'rake/clean'
4
- require 'rake/testtask'
5
- require 'rake/packagetask'
6
- require 'rake/gempackagetask'
5
+
6
+ begin
7
+ require 'jeweler'
8
+ Jeweler::Tasks.new do |gem|
9
+ gem.name = "win32screenshot"
10
+ gem.summary = %Q{Capture Screenshots on Windows with Ruby}
11
+ gem.description = %Q{Capture Screenshots on Windows with Ruby}
12
+ gem.email = ["jarmo.p@gmail.com", "aslak.hellesoy@gmail.com"]
13
+ gem.homepage = "http://github.com/jarmo/win32screenshot"
14
+ gem.authors = ["Jarmo Pertman", "Aslak Hellesøy"]
15
+
16
+ gem.rdoc_options = ["--main", "README.rdoc"]
17
+
18
+ gem.add_dependency "ffi"
19
+
20
+ gem.add_development_dependency "rspec", ">= 1.2.9"
21
+ gem.add_development_dependency "rmagick"
22
+ end
23
+ Jeweler::GemcutterTasks.new
24
+ rescue LoadError
25
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
26
+ end
27
+
28
+ require 'spec/rake/spectask'
29
+ Spec::Rake::SpecTask.new(:spec) do |spec|
30
+ spec.libs << 'lib' << 'spec'
31
+ spec.spec_files = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
35
+ spec.libs << 'lib' << 'spec'
36
+ spec.pattern = 'spec/**/*_spec.rb'
37
+ spec.rcov = true
38
+ end
39
+
40
+ task :spec => :check_dependencies
41
+
42
+ task :default => :spec
43
+
7
44
  require 'rake/rdoctask'
8
- require 'rake/contrib/rubyforgepublisher'
9
- require 'fileutils'
10
- require 'hoe'
11
- include FileUtils
12
- require File.join(File.dirname(__FILE__), 'lib', 'win32screenshot', 'version')
13
-
14
- AUTHOR = "Aslak Helles�y"
15
- EMAIL = "aslak.hellesoy@gmail.com"
16
- DESCRIPTION = "Capture Screenshots on Windows with Ruby"
17
- GEM_NAME = "win32screenshot"
18
- RUBYFORGE_PROJECT = "win32screenshot"
19
- HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
20
- RELEASE_TYPES = %w( gem ) # can use: gem, tar, zip
21
-
22
-
23
- NAME = "win32screenshot"
24
- REV = nil # UNCOMMENT IF REQUIRED: File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
25
- VERS = ENV['VERSION'] || (Win32screenshot::VERSION::STRING + (REV ? ".#{REV}" : ""))
26
- CLEAN.include ['**/.*.sw?', '*.gem', '.config']
27
- RDOC_OPTS = ['--quiet', '--title', "win32screenshot documentation",
28
- "--opname", "index.html",
29
- "--line-numbers",
30
- "--main", "README",
31
- "--inline-source"]
32
-
33
- # Generate all the Rake tasks
34
- # Run 'rake -T' to see list of generated tasks (from gem root directory)
35
- hoe = Hoe.new(GEM_NAME, VERS) do |p|
36
- p.author = AUTHOR
37
- p.description = DESCRIPTION
38
- p.email = EMAIL
39
- p.summary = DESCRIPTION
40
- p.url = HOMEPATH
41
- p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
42
- p.test_globs = ["test/**/*_test.rb"]
43
- p.clean_globs = CLEAN #An array of file patterns to delete on clean.
44
-
45
- # == Optional
46
- #p.changes - A description of the release's latest changes.
47
- #p.extra_deps - An array of rubygem dependencies.
48
- #p.spec_extras - A hash of extra values to set in the gemspec.
45
+ Rake::RDocTask.new do |rdoc|
46
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
47
+
48
+ rdoc.rdoc_dir = 'rdoc'
49
+ rdoc.title = "Win32::Screenshot #{version}"
50
+ rdoc.rdoc_files.include('README*')
51
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
52
  end
50
-
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.4
@@ -0,0 +1,36 @@
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(hwnd, &proc)
12
+ end
13
+
14
+ # captures desktop
15
+ def desktop(&proc)
16
+ hwnd = BitmapMaker.desktop_window
17
+ BitmapMaker.capture(hwnd, &proc)
18
+ end
19
+
20
+ # captures window with a *title_query* and waits *pause* (by default is 0.5)
21
+ # seconds after trying to set window to the foreground
22
+ def window(title_query, pause=0.5, &proc)
23
+ hwnd = BitmapMaker.hwnd(title_query)
24
+ raise "window with title '#{title_query}' was not found!" unless hwnd
25
+ hwnd(hwnd, pause, &proc)
26
+ end
27
+
28
+ # captures by window handle
29
+ def hwnd(hwnd, pause=0.5, &proc)
30
+ BitmapMaker.prepare_window(hwnd, pause)
31
+ BitmapMaker.capture(hwnd, &proc)
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,168 @@
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', 'kernel32', '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 :set_focus, :SetFocus,
42
+ [:long], :bool
43
+
44
+ # kernel32.dll
45
+ attach_function :current_thread_id, :GetCurrentThreadId,
46
+ [], :long
47
+
48
+ # gdi32.dll
49
+ attach_function :create_compatible_dc, :CreateCompatibleDC,
50
+ [:long], :long
51
+ attach_function :create_compatible_bitmap, :CreateCompatibleBitmap,
52
+ [:long, :int, :int], :long
53
+ attach_function :select_object, :SelectObject,
54
+ [:long, :long], :long
55
+ attach_function :bit_blt, :BitBlt,
56
+ [:long, :int, :int, :int, :int, :long, :int, :int, :long], :bool
57
+ attach_function :di_bits, :GetDIBits,
58
+ [:long, :long, :int, :int, :pointer, :pointer, :int], :int
59
+ attach_function :delete_object, :DeleteObject,
60
+ [:long], :bool
61
+ attach_function :delete_dc, :DeleteDC,
62
+ [:long], :bool
63
+ attach_function :release_dc, :ReleaseDC,
64
+ [:long, :long], :int
65
+
66
+ EnumWindowCallback = Proc.new do |hwnd, param|
67
+ title_length = window_text_length(hwnd) + 1
68
+ title = FFI::MemoryPointer.new :char, title_length
69
+ window_text(hwnd, title, title_length)
70
+ title = title.read_string
71
+ if title =~ Regexp.new(param.read_string) && window_visible(hwnd)
72
+ param.write_long hwnd
73
+ false
74
+ else
75
+ true
76
+ end
77
+ end
78
+
79
+ module_function
80
+
81
+ def hwnd(window_title)
82
+ window_title = window_title.to_s
83
+ window_params = FFI::MemoryPointer.from_string(window_title)
84
+ enum_windows(EnumWindowCallback, window_params)
85
+ if window_title != window_params.read_string
86
+ # hwnd found
87
+ window_params.read_long
88
+ else
89
+ nil
90
+ end
91
+ end
92
+
93
+ SW_SHOW = 5
94
+
95
+ def prepare_window(hwnd, pause)
96
+ restore(hwnd) if minimized(hwnd)
97
+ set_foreground(hwnd)
98
+ show_window(hwnd, SW_SHOW)
99
+ sleep pause
100
+ end
101
+
102
+ SW_RESTORE = 9
103
+
104
+ def restore(hwnd)
105
+ show_window(hwnd, SW_RESTORE)
106
+ end
107
+
108
+ def set_foreground(hwnd)
109
+ if foreground_window != hwnd
110
+ foreground_thread = window_thread_process_id(current_thread_id, nil)
111
+ other_thread = window_thread_process_id(hwnd, nil)
112
+ attach_thread_input(foreground_thread, other_thread, true)
113
+ set_foreground_window(hwnd)
114
+ set_focus(hwnd)
115
+ attach_thread_input(foreground_thread, other_thread, false)
116
+ end
117
+ end
118
+
119
+ def dimensions_for(hwnd)
120
+ rect = [0, 0, 0, 0].pack('L4')
121
+ client_rect(hwnd, rect)
122
+ x1, y1, x2, y2 = rect.unpack('L4')
123
+ end
124
+
125
+ def capture(hwnd, &proc)
126
+ hScreenDC = dc(hwnd)
127
+ x1, y1, x2, y2 = dimensions_for(hwnd)
128
+ __capture(hScreenDC, x1, y1, x2, y2, &proc)
129
+ end
130
+
131
+ SRCCOPY = 0x00CC0020
132
+ DIB_RGB_COLORS = 0
133
+
134
+ def __capture(hScreenDC, x1, y1, x2, y2, &proc)
135
+ w = x2-x1
136
+ h = y2-y1
137
+
138
+ hmemDC = create_compatible_dc(hScreenDC)
139
+ hmemBM = create_compatible_bitmap(hScreenDC, w, h)
140
+ select_object(hmemDC, hmemBM)
141
+ bit_blt(hmemDC, 0, 0, w, h, hScreenDC, 0, 0, SRCCOPY)
142
+ bitmap_size = w * h * 3 + w % 4 * h
143
+ lpvpxldata = FFI::MemoryPointer.new(bitmap_size)
144
+
145
+ # Bitmap header
146
+ # http://www.fortunecity.com/skyscraper/windows/364/bmpffrmt.html
147
+ bmInfo = [40, w, h, 1, 24, 0, 0, 0, 0, 0, 0, 0].pack('L3S2L6')
148
+ di_bits(hmemDC, hmemBM, 0, h, lpvpxldata, bmInfo, DIB_RGB_COLORS)
149
+
150
+ bmFileHeader = [
151
+ 19778,
152
+ bitmap_size + 40 + 14,
153
+ 0,
154
+ 0,
155
+ 54
156
+ ].pack('SLSSL')
157
+
158
+ bmp_data = bmFileHeader + bmInfo + lpvpxldata.read_string(bitmap_size)
159
+ proc.call(w, h, bmp_data)
160
+ ensure
161
+ lpvpxldata.free
162
+ delete_object(hmemBM)
163
+ delete_dc(hmemDC)
164
+ release_dc(0, hScreenDC)
165
+ end
166
+ end
167
+ end
168
+ end
@@ -1,130 +1,3 @@
1
- Dir[File.join(File.dirname(__FILE__), 'win32screenshot/**/*.rb')].sort.each { |lib| require lib }
2
-
3
- require 'rubygems'
4
- require 'dl/import'
5
-
6
- module Win32
7
- module Screenshot
8
- extend DL::Importable
9
-
10
- dlload "kernel32.dll","user32.dll","gdi32.dll"
11
-
12
- USER32 = DL.dlopen("user32")
13
- EnumWindows = USER32['EnumWindows', 'IPL']
14
- GetWindowTextLength = USER32['GetWindowTextLengthA' ,'LI' ]
15
- GetWindowText = USER32['GetWindowTextA', 'iLsL' ]
16
-
17
- SRCCOPY = 0xCC0020
18
- GMEM_FIXED = 0
19
- DIB_RGB_COLORS = 0
20
-
21
- typealias "HBITMAP","unsigned int"
22
- typealias "LPRECT","unsigned int*"
23
-
24
- extern "HWND GetForegroundWindow()"
25
- extern "HWND GetDesktopWindow()"
26
- extern "BOOL GetWindowRect(HWND, LPRECT)"
27
- extern "BOOL GetClientRect(HWND, LPRECT)"
28
- extern "HDC GetDC(HWND)"
29
- extern "HDC GetWindowDC(int)"
30
- extern "HDC CreateCompatibleDC(HDC)"
31
- extern "int GetDeviceCaps(HDC, int)"
32
- extern "HBITMAP CreateCompatibleBitmap(HDC, int, int)"
33
- extern "long SelectObject(HDC, HBITMAP)"
34
- extern "long BitBlt(HDC, long, long, long, long, HDC, long, long, long)"
35
- extern "void* GlobalAlloc(long, long)"
36
- extern "void* GlobalLock(void*)"
37
- extern "long GetDIBits(HDC, HBITMAP, long, long, void*, void*, long)"
38
- extern "long GlobalUnlock(void*)"
39
- extern "long GlobalFree(void*)"
40
- extern "long DeleteObject(unsigned long)"
41
- extern "long DeleteDC(HDC)"
42
- extern "long ReleaseDC(long, HDC)"
43
- extern "BOOL SetForegroundWindow(HWND)"
44
-
45
- module_function
46
- def capture(hScreenDC, x1, y1, x2, y2)
47
- w = x2-x1
48
- h = y2-y1
49
-
50
- # Reserve some memory
51
- hmemDC = createCompatibleDC(hScreenDC)
52
- hmemBM = createCompatibleBitmap(hScreenDC, w, h)
53
- selectObject(hmemDC, hmemBM)
54
- bitBlt(hmemDC, 0, 0, w, h, hScreenDC, 0, 0, SRCCOPY)
55
- hpxldata = globalAlloc(GMEM_FIXED, w * h * 3)
56
- lpvpxldata = globalLock(hpxldata)
57
-
58
- # Bitmap header
59
- # http://www.fortunecity.com/skyscraper/windows/364/bmpffrmt.html
60
- bmInfo = [40, w, h, 1, 24, 0, 0, 0, 0, 0, 0, 0].pack('LLLSSLLLLLL').to_ptr
61
-
62
- getDIBits(hmemDC, hmemBM, 0, h, lpvpxldata, bmInfo, DIB_RGB_COLORS)
63
-
64
- bmFileHeader = [
65
- 19778,
66
- (w * h * 3) + 40 + 14,
67
- 0,
68
- 0,
69
- 54
70
- ].pack('SLSSL').to_ptr
71
-
72
- data = bmFileHeader.to_s(14) + bmInfo.to_s(40) + lpvpxldata.to_s(w * h * 3)
73
-
74
- globalUnlock(hpxldata)
75
- globalFree(hpxldata)
76
- deleteObject(hmemBM)
77
- deleteDC(hmemDC)
78
- releaseDC(0, hScreenDC)
79
-
80
- return [w, h, data]
81
- end
82
-
83
- def capture_hwnd(hwnd)
84
- hScreenDC = getDC(hwnd)
85
-
86
- # Find the dimensions of the window
87
- rect = DL.malloc(DL.sizeof('LLLL'))
88
- getClientRect(hwnd, rect)
89
- x1, y1, x2, y2 = rect.to_a('LLLL')
90
-
91
- capture(hScreenDC, x1, y1, x2, y2)
92
- end
93
-
94
- module_function
95
- def foreground
96
- hwnd = getForegroundWindow
97
- capture_hwnd(hwnd)
98
- end
99
-
100
- module_function
101
- def desktop
102
- hwnd = getDesktopWindow
103
- capture_hwnd(hwnd)
104
- end
105
-
106
- module_function
107
- def window(title_query, delay=0.1)
108
- hwnd = nil
109
-
110
- proc = DL.callback('ILL') do |curr_hwnd, lparam|
111
- textLength, a = GetWindowTextLength.call(curr_hwnd)
112
- captionBuffer = " " * (textLength+1)
113
- t, textCaption = GetWindowText.call(curr_hwnd, captionBuffer, textLength+1)
114
- text = textCaption[1].to_s
115
- if text =~ title_query
116
- hwnd = curr_hwnd
117
- 0
118
- else
119
- 1
120
- end
121
- end
122
- EnumWindows.call(proc, 0)
123
-
124
- raise "Couldn't find window with title matching #{title_query}" if hwnd.nil?
125
- setForegroundWindow(hwnd)
126
- sleep(delay)
127
- capture_hwnd(hwnd)
128
- end
129
- end
130
- end
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
2
+ Kernel.warn %q(DEPRECATION: use "require 'win32/screenshot'" instead of "require 'win32screenshot'")
3
+ require "win32/screenshot"