win32screenshot 0.0.2 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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"