windowstate 0.0.1
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/LICENSE +19 -0
- data/README.md +75 -0
- data/Rakefile +20 -0
- data/bin/windowstate.rb +78 -0
- data/lib/windowstate.rb +201 -0
- data/lib/windowstate/version.rb +3 -0
- metadata +88 -0
data/LICENSE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2012 Phil Stewart
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to
|
|
5
|
+
deal in the Software without restriction, including without limitation the
|
|
6
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
7
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
|
11
|
+
all copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
18
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
19
|
+
IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
windowstate
|
|
2
|
+
===========
|
|
3
|
+
|
|
4
|
+
Windowstate is a utility for saving and restoring the sizes and positions of
|
|
5
|
+
application windows on Microsoft Windows systems.
|
|
6
|
+
|
|
7
|
+
Why?
|
|
8
|
+
----
|
|
9
|
+
|
|
10
|
+
Windows 7 (and probably Vista) have a tendancy to mess up window positions and
|
|
11
|
+
sizes in certain circumstances, such as when disconnecting and reconnecting
|
|
12
|
+
the primary display. The usual symptom is that once the display is reconnected
|
|
13
|
+
all of the application windows have been shrunk and repositioned to the top-left
|
|
14
|
+
corner of the screen. This can be particularly irritating if you have a large
|
|
15
|
+
number of carefully sized and positioned windows open. So far as I can tell
|
|
16
|
+
there is no universal solution to this problem, so I wrote this program as a
|
|
17
|
+
workaround.
|
|
18
|
+
|
|
19
|
+
Installation
|
|
20
|
+
------------
|
|
21
|
+
|
|
22
|
+
If you have a RubyInstaller for Windows installation, then you can install
|
|
23
|
+
the gem from a command prompt in the usual way:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
gem install windowstate
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Alternatively, you can download the standalone executable from github on
|
|
30
|
+
|
|
31
|
+
https://github.com/downloads/LichP/windowstate/windowstate.exe
|
|
32
|
+
|
|
33
|
+
Usage
|
|
34
|
+
-----
|
|
35
|
+
|
|
36
|
+
Windowstate is a command line application, so you will need to run it from the
|
|
37
|
+
command prompt. To save the current window states:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
windowstate save
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
It is recommended you do this immediately before causing a display disconnect.
|
|
44
|
+
Once your display is reconnected, to restore the previously saved window
|
|
45
|
+
state:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
windowstate restore
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The saved state is stored as JSON in a file called `windowstate.json`, which is
|
|
52
|
+
save in your user local temp directory by default. You can override this with
|
|
53
|
+
the `--file` option - see `windowstate --help` for details.
|
|
54
|
+
|
|
55
|
+
Does it Work?
|
|
56
|
+
-------------
|
|
57
|
+
|
|
58
|
+
Windowstate has not been extensively tested: it works on my system with my
|
|
59
|
+
typical window set, but it might not catch some legitimate application
|
|
60
|
+
windows, and has not been tested in a multi-display environment. If you
|
|
61
|
+
run in to problems, please let me know and/or open an issue on Github.
|
|
62
|
+
|
|
63
|
+
Contact and Contributing
|
|
64
|
+
------------------------
|
|
65
|
+
|
|
66
|
+
The homepage for this project is
|
|
67
|
+
|
|
68
|
+
http://github.com/LichP/windowstate
|
|
69
|
+
|
|
70
|
+
Any feedback, suggestions, etc are very welcome. If you have bugfixes and/or
|
|
71
|
+
contributions, feel free to fork, branch, and send a pull request.
|
|
72
|
+
|
|
73
|
+
Enjoy :-)
|
|
74
|
+
|
|
75
|
+
Phil Stewart, June 2012
|
data/Rakefile
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#require 'erb'
|
|
2
|
+
#require './lib/windowstate/version'
|
|
3
|
+
|
|
4
|
+
#$app_name = 'WindowState'
|
|
5
|
+
$exe_name = 'windowstate'
|
|
6
|
+
#$app_version = ''
|
|
7
|
+
|
|
8
|
+
task :exe do
|
|
9
|
+
sh %{ ocra bin/#{$exe_name}.rb --output #{$exe_name}.exe }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
#task :installer => :inno_iss_file do
|
|
13
|
+
# sh %{ ocra applaunch.rb app --output #{$app_name}.exe --chdir-first --no-lzma --innosetup #{$app_name}.iss }
|
|
14
|
+
#end
|
|
15
|
+
|
|
16
|
+
#task :inno_iss_file do
|
|
17
|
+
# File.open("#{$app_name}.iss", "w") do |f|
|
|
18
|
+
# f.write(ERB.new(File.read("template.iss.erb")).result(binding))
|
|
19
|
+
# end
|
|
20
|
+
#end
|
data/bin/windowstate.rb
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Copyright (c) 2012 Phil Stewart
|
|
2
|
+
#
|
|
3
|
+
# License: BSD - see LICENSE file
|
|
4
|
+
# Homepage: https://github.com/LichP/windowstate
|
|
5
|
+
|
|
6
|
+
begin
|
|
7
|
+
require 'windowstate'
|
|
8
|
+
rescue LoadError
|
|
9
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
10
|
+
require 'windowstate'
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
require 'trollop'
|
|
14
|
+
require 'tmpdir'
|
|
15
|
+
|
|
16
|
+
exit if defined?(Ocra)
|
|
17
|
+
|
|
18
|
+
SUB_COMMANDS = %w(save restore debug)
|
|
19
|
+
|
|
20
|
+
global_opts = Trollop::options do
|
|
21
|
+
banner "Save and restore the position and size of application windows"
|
|
22
|
+
banner ""
|
|
23
|
+
banner "Usage:"
|
|
24
|
+
banner ""
|
|
25
|
+
banner " #{File.basename(ENV['OCRA_EXECUTABLE'] || $0)} [GLOBAL OPTIONS] COMMAND [COMMAND OPTIONS]"
|
|
26
|
+
banner ""
|
|
27
|
+
banner "Commands:"
|
|
28
|
+
banner ""
|
|
29
|
+
banner " save Save the state of current application windows"
|
|
30
|
+
banner " restore Restore the state of current application windows from an earlier save"
|
|
31
|
+
banner " debug Generate debugging information"
|
|
32
|
+
banner ""
|
|
33
|
+
banner "Global options:"
|
|
34
|
+
banner ""
|
|
35
|
+
|
|
36
|
+
opt :file, "File to use to save/restore state",
|
|
37
|
+
short: "-f",
|
|
38
|
+
default: File.join(Dir.tmpdir, "windowstate.json")
|
|
39
|
+
|
|
40
|
+
stop_on SUB_COMMANDS
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
cmd = ARGV.shift
|
|
44
|
+
|
|
45
|
+
cmd_opts = case cmd
|
|
46
|
+
when "save"
|
|
47
|
+
Trollop::options do
|
|
48
|
+
opt :stdout, "Write state JSON to stdout instead of to file",
|
|
49
|
+
short: "-c",
|
|
50
|
+
default: false
|
|
51
|
+
end
|
|
52
|
+
when "restore"
|
|
53
|
+
Trollop::options do
|
|
54
|
+
end
|
|
55
|
+
when "debug"
|
|
56
|
+
Trollop::options do
|
|
57
|
+
end
|
|
58
|
+
else
|
|
59
|
+
Trollop::die "unknown subcommand #{cmd.inspect}"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
case cmd
|
|
63
|
+
when 'save'
|
|
64
|
+
if cmd_opts[:stdout]
|
|
65
|
+
puts WindowState.get_state_json
|
|
66
|
+
else
|
|
67
|
+
WindowState.save(global_opts[:file])
|
|
68
|
+
end
|
|
69
|
+
when 'restore'
|
|
70
|
+
WindowState.restore(global_opts[:file])
|
|
71
|
+
when 'debug'
|
|
72
|
+
debug_data = WindowState.debug
|
|
73
|
+
debug_data.each do |hash|
|
|
74
|
+
hash.each_pair do |key, value|
|
|
75
|
+
puts "%s: %s" % [key.to_s, value.inspect]
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
data/lib/windowstate.rb
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# encoding: iso-8859-1
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2012 Phil Stewart
|
|
4
|
+
#
|
|
5
|
+
# License: BSD - see LICENSE file
|
|
6
|
+
# Homepage: https://github.com/Lichp/windowstate
|
|
7
|
+
|
|
8
|
+
require 'json'
|
|
9
|
+
require 'windows/window'
|
|
10
|
+
|
|
11
|
+
module Windows
|
|
12
|
+
module Window
|
|
13
|
+
API.new('SetWindowPlacement', 'LP', 'B', 'user32')
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
module WindowState
|
|
18
|
+
include Windows::Window
|
|
19
|
+
include Win32
|
|
20
|
+
|
|
21
|
+
class WindowPlacement
|
|
22
|
+
include Windows::Window
|
|
23
|
+
|
|
24
|
+
attr_reader :handle, :data_encoded, :title
|
|
25
|
+
attr_writer :title
|
|
26
|
+
|
|
27
|
+
def self.get(handle)
|
|
28
|
+
wp = new(handle)
|
|
29
|
+
wp.get
|
|
30
|
+
wp
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.json_create(o)
|
|
34
|
+
wp = new(o['data'][0])
|
|
35
|
+
wp.title = o['data'][1]
|
|
36
|
+
wp.data = o['data'][2]
|
|
37
|
+
wp
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def initialize(handle)
|
|
41
|
+
@handle = handle
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def get
|
|
45
|
+
wp_buffer = [44].pack("L") + '\0' * 40
|
|
46
|
+
title_buffer = "\0" * 200
|
|
47
|
+
|
|
48
|
+
GetWindowPlacement(handle, wp_buffer)
|
|
49
|
+
@data_encoded = wp_buffer
|
|
50
|
+
|
|
51
|
+
GetWindowText(handle, title_buffer, 200)
|
|
52
|
+
@title = title_buffer.strip
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def set
|
|
56
|
+
SetWindowPlacement(handle, data_encoded)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def data
|
|
60
|
+
data_encoded.unpack("L11")
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def data=(array)
|
|
64
|
+
@data_encoded = array.pack("L11")
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def to_json(*a)
|
|
68
|
+
{
|
|
69
|
+
'json_class' => self.class.name,
|
|
70
|
+
'data' => [handle, title, data]
|
|
71
|
+
}.to_json(*a)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def inspect
|
|
75
|
+
"#<WindowState::WindowPlacement:0x%08x @handle=%i, @title=\"%s\", data=%s>" % [object_id, handle, title, data]
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
class WindowInfo
|
|
80
|
+
include Windows::Window
|
|
81
|
+
|
|
82
|
+
WS_CAPTION = 0x00C00000
|
|
83
|
+
WS_CHILD = 0x40000000
|
|
84
|
+
WS_CLIPSIBLINGS = 0x04000000
|
|
85
|
+
WS_DISABLED = 0x08000000
|
|
86
|
+
WS_DLGFRAME = 0x00400000
|
|
87
|
+
WS_POPUP = 0x80000000
|
|
88
|
+
WS_SIZEBOX = 0x00040000
|
|
89
|
+
WS_SYSMENU = 0x00080000
|
|
90
|
+
WS_VISIBLE = 0x10000000
|
|
91
|
+
|
|
92
|
+
attr_reader :data_encoded, :handle
|
|
93
|
+
|
|
94
|
+
def self.get(handle)
|
|
95
|
+
wp = new(handle)
|
|
96
|
+
wp.get
|
|
97
|
+
wp
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def initialize(handle)
|
|
101
|
+
@handle = handle
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def get
|
|
105
|
+
buffer = [60].pack("L") + '\0' * 56
|
|
106
|
+
GetWindowInfo(handle, buffer)
|
|
107
|
+
@data_encoded = buffer
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def data
|
|
111
|
+
data_encoded.unpack("L14S2")
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def style
|
|
115
|
+
data[9]
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def ignorable?
|
|
119
|
+
style & (WS_DISABLED | WS_CHILD) > 0
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def application_window?
|
|
123
|
+
mask = (WS_SYSMENU | WS_VISIBLE)
|
|
124
|
+
(style & mask == mask) && !ignorable?
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def data=(array)
|
|
128
|
+
@data_encoded = array.pack("L14S2")
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def to_json(*a)
|
|
132
|
+
{
|
|
133
|
+
'json_class' => self.class.name,
|
|
134
|
+
'data' => [handle, data]
|
|
135
|
+
}.to_json(*a)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def inspect
|
|
139
|
+
"#<WindowState::WindowInfo:0x%08x @handle=%i, data=%s>" % [object_id, handle, data]
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
@wp_save = []
|
|
144
|
+
@debug = []
|
|
145
|
+
|
|
146
|
+
@save_proc = API::Callback.new('LP', 'I') do |handle, param|
|
|
147
|
+
wp = WindowPlacement.get(handle)
|
|
148
|
+
wi = WindowInfo.get(handle)
|
|
149
|
+
|
|
150
|
+
if wi.application_window?
|
|
151
|
+
@wp_save << wp
|
|
152
|
+
end
|
|
153
|
+
true
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
@debug_proc = API::Callback.new('LP', 'I') do |handle, param|
|
|
157
|
+
wp = WindowPlacement.get(handle)
|
|
158
|
+
wi = WindowInfo.get(handle)
|
|
159
|
+
|
|
160
|
+
@debug << {:wp => wp, :wi => wi}
|
|
161
|
+
true
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
class << self
|
|
165
|
+
include Windows::Window
|
|
166
|
+
include Win32
|
|
167
|
+
|
|
168
|
+
def get_state
|
|
169
|
+
EnumWindows(@save_proc, nil)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def get_state_json
|
|
173
|
+
get_state
|
|
174
|
+
JSON.generate(@wp_save, object_nl: "\n")
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def set_state(wp_array)
|
|
178
|
+
wp_array.each { |wp| wp.set }
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def set_state_json(wp_json)
|
|
182
|
+
set_state(JSON.parse(wp_json))
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def save(filename)
|
|
186
|
+
File.open(filename, "w") do |dump_file|
|
|
187
|
+
dump_file.puts get_state_json
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def restore(filename)
|
|
192
|
+
set_state_json(File.read(filename))
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def debug
|
|
196
|
+
EnumWindows(@debug_proc, nil)
|
|
197
|
+
@debug
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
metadata
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: windowstate
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Phil Stewart
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2012-06-10 00:00:00.000000000Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: windows-pr
|
|
16
|
+
requirement: &27383856 !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ~>
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '1.2'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: *27383856
|
|
25
|
+
- !ruby/object:Gem::Dependency
|
|
26
|
+
name: trollop
|
|
27
|
+
requirement: &27383556 !ruby/object:Gem::Requirement
|
|
28
|
+
none: false
|
|
29
|
+
requirements:
|
|
30
|
+
- - ~>
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: *27383556
|
|
36
|
+
- !ruby/object:Gem::Dependency
|
|
37
|
+
name: ocra
|
|
38
|
+
requirement: &27383280 !ruby/object:Gem::Requirement
|
|
39
|
+
none: false
|
|
40
|
+
requirements:
|
|
41
|
+
- - ~>
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: '1.3'
|
|
44
|
+
type: :development
|
|
45
|
+
prerelease: false
|
|
46
|
+
version_requirements: *27383280
|
|
47
|
+
description: ! "WindowState is a utility for saving and restoring the\n positions
|
|
48
|
+
and sizes of your application windows, which is useful for\n monitor hotplugging
|
|
49
|
+
when Windows decides to shrink everything and\n shove it in the top-left corner
|
|
50
|
+
of your desktop."
|
|
51
|
+
email:
|
|
52
|
+
- phil.stewart@lichp.co.uk
|
|
53
|
+
executables:
|
|
54
|
+
- windowstate.rb
|
|
55
|
+
extensions: []
|
|
56
|
+
extra_rdoc_files: []
|
|
57
|
+
files:
|
|
58
|
+
- lib/windowstate/version.rb
|
|
59
|
+
- lib/windowstate.rb
|
|
60
|
+
- README.md
|
|
61
|
+
- LICENSE
|
|
62
|
+
- Rakefile
|
|
63
|
+
- bin/windowstate.rb
|
|
64
|
+
homepage: http://github.com/lichp/windowstate
|
|
65
|
+
licenses: []
|
|
66
|
+
post_install_message:
|
|
67
|
+
rdoc_options: []
|
|
68
|
+
require_paths:
|
|
69
|
+
- lib
|
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
71
|
+
none: false
|
|
72
|
+
requirements:
|
|
73
|
+
- - ! '>='
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
77
|
+
none: false
|
|
78
|
+
requirements:
|
|
79
|
+
- - ! '>='
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '0'
|
|
82
|
+
requirements: []
|
|
83
|
+
rubyforge_project:
|
|
84
|
+
rubygems_version: 1.7.2
|
|
85
|
+
signing_key:
|
|
86
|
+
specification_version: 3
|
|
87
|
+
summary: Save and restore window positions and sizes on MS Windows
|
|
88
|
+
test_files: []
|