vrvirtualdesktop 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.
@@ -0,0 +1,142 @@
1
+ #
2
+ # This module contains information allowing the user to call
3
+ # functions defined in User32.dll
4
+ #
5
+
6
+ require "dl"
7
+ require "Win32API"
8
+
9
+ module Win32
10
+ # Constants
11
+
12
+ NULL = 0
13
+ SW_HIDE = 0
14
+ SW_SHOWNORMAL = 1
15
+ SW_NORMAL = 1
16
+ SW_SHOWMINIMIZED = 2
17
+ SW_SHOWMAXIMIZED = 3
18
+ SW_MAXIMIZE = 3
19
+ SW_SHOWNOACTIVATE = 4
20
+ SW_SHOW = 5
21
+ SW_MINIMIZE = 6
22
+ SW_SHOWMINNOACTIVE = 7
23
+ SW_SHOWNA = 8
24
+ SW_RESTORE = 9
25
+ SW_SHOWDEFAULT = 10
26
+ SW_FORCEMINIMIZE = 11
27
+ SW_MAX = 11
28
+
29
+ GWL_WNDPROC = (-4)
30
+ GWL_HINSTANCE = (-6)
31
+ GWL_HWNDPARENT = (-8)
32
+ GWL_STYLE = (-16)
33
+ GWL_EXSTYLE = (-20)
34
+ GWL_USERDATA = (-21)
35
+ GWL_ID = (-12)
36
+
37
+ WS_OVERLAPPED = 0x00000000
38
+ WS_POPUP = 0x80000000
39
+ WS_CHILD = 0x40000000
40
+ WS_MINIMIZE = 0x20000000
41
+ WS_VISIBLE = 0x10000000
42
+ WS_DISABLED = 0x08000000
43
+ WS_CLIPSIBLINGS = 0x04000000
44
+ WS_CLIPCHILDREN = 0x02000000
45
+ WS_MAXIMIZE = 0x01000000
46
+ WS_CAPTION = 0x00C00000 #/* WS_BORDER | WS_DLGFRAME =*/
47
+ WS_BORDER = 0x00800000
48
+ WS_DLGFRAME = 0x00400000
49
+ WS_VSCROLL = 0x00200000
50
+ WS_HSCROLL = 0x00100000
51
+ WS_SYSMENU = 0x00080000
52
+ WS_THICKFRAME = 0x00040000
53
+ WS_GROUP = 0x00020000
54
+ WS_TABSTOP = 0x00010000
55
+
56
+ WS_MINIMIZEBOX = 0x00020000
57
+ WS_MAXIMIZEBOX = 0x00010000
58
+
59
+
60
+ WS_TILED = WS_OVERLAPPED
61
+ WS_ICONIC = WS_MINIMIZE
62
+ WS_SIZEBOX = WS_THICKFRAME
63
+
64
+ #
65
+ # Common Window Styles
66
+ #
67
+ WS_OVERLAPPEDWINDOW = (WS_OVERLAPPED |
68
+ WS_CAPTION |
69
+ WS_SYSMENU |
70
+ WS_THICKFRAME |
71
+ WS_MINIMIZEBOX |
72
+ WS_MAXIMIZEBOX)
73
+
74
+ WS_POPUPWINDOW = (WS_POPUP |
75
+ WS_BORDER |
76
+ WS_SYSMENU)
77
+
78
+ WS_CHILDWINDOW = (WS_CHILD)
79
+
80
+ WS_TILEDWINDOW = WS_OVERLAPPEDWINDOW
81
+
82
+ #
83
+ # Extended Window Styles
84
+ #
85
+ WS_EX_DLGMODALFRAME = 0x00000001
86
+ WS_EX_NOPARENTNOTIFY = 0x00000004
87
+ WS_EX_TOPMOST = 0x00000008
88
+ WS_EX_ACCEPTFILES = 0x00000010
89
+ WS_EX_TRANSPARENT = 0x00000020
90
+
91
+ WS_EX_MDICHILD = 0x00000040
92
+ WS_EX_TOOLWINDOW = 0x00000080
93
+ WS_EX_WINDOWEDGE = 0x00000100
94
+ WS_EX_CLIENTEDGE = 0x00000200
95
+ WS_EX_CONTEXTHELP = 0x00000400
96
+
97
+ WS_EX_RIGHT = 0x00001000
98
+ WS_EX_LEFT = 0x00000000
99
+ WS_EX_RTLREADING = 0x00002000
100
+ WS_EX_LTRREADING = 0x00000000
101
+ WS_EX_LEFTSCROLLBAR = 0x00004000
102
+ WS_EX_RIGHTSCROLLBAR = 0x00000000
103
+
104
+ WS_EX_CONTROLPARENT = 0x00010000
105
+ WS_EX_STATICEDGE = 0x00020000
106
+ WS_EX_APPWINDOW = 0x00040000
107
+
108
+ WS_EX_OVERLAPPEDWINDOW =(WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE)
109
+ WS_EX_PALETTEWINDOW = (WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST)
110
+
111
+ #
112
+ # GetWindow() Constants
113
+ #
114
+ GW_HWNDFIRST = 0
115
+ GW_HWNDLAST = 1
116
+ GW_HWNDNEXT = 2
117
+ GW_HWNDPREV = 3
118
+ GW_OWNER = 4
119
+ GW_CHILD = 5
120
+ GW_ENABLEDPOPUP = 6
121
+ GW_MAX = 6
122
+
123
+ # Functions
124
+
125
+ User32 = DL.dlopen("user32")
126
+ EnumWindows = User32['EnumWindows', 'IPL']
127
+ PostMessage = User32['PostMessage', 'ILILL']
128
+ GetClassName = User32['GetClassName', 'ILpI']
129
+ FindWindowEx = User32["FindWindowEx", "IIIPP"]
130
+ RegisterWindowMessage = User32["RegisterWindowMessage", "IP"]
131
+ SendMessage = User32["SendMessage", "IIIII"]
132
+ ShowWindow = User32["ShowWindow", "III"]
133
+ GetWindowLong = User32["GetWindowLong", "III"]
134
+ GetWindow = User32["GetWindow", "III"]
135
+ GetTopWindow = User32["GetTopWindow", "II"]
136
+ IsIconic = User32["IsIconic", "II"]
137
+
138
+ GetWindowLong_2 = Win32API.new("user32","GetWindowLong", ["I","I"],"I")
139
+ GetWindow_2 = Win32API.new("user32","GetWindow", ["I","I"],"I")
140
+ GetClassName_2 = Win32API.new("user32", "GetClassName", ["I", "P", "I"], "I")
141
+
142
+ end # module Win32
@@ -0,0 +1,418 @@
1
+ require "rubygems"
2
+ require_gem "SimpleTrace", "< 0.1.0"
3
+ require "yaml"
4
+ require "Win32API"
5
+ require_gem "VRTools", "< 0.1.0"
6
+ require "vr/vrdialog"
7
+
8
+ module Win32
9
+
10
+ #
11
+ # A virtual desktop consists of a set of windows on it, a name, a numbered icon and whether it is active
12
+ # or not
13
+ #
14
+ class VirtualDesktop
15
+ attr_reader :icon_id, :name, :index, :windows
16
+ attr_accessor :active
17
+ attr_accessor :form
18
+
19
+ class SetScreenName < VRModalDialog
20
+ #include AddControlFont
21
+ include VRCenterFeasible
22
+ include VRLayout
23
+ include VRKey_CREscFeasible
24
+
25
+ attr_reader :name
26
+
27
+ DEFAULT_FONT = VRLocalScreen.factory.newfont("Arial",14)
28
+
29
+ LABEL_H = 20
30
+ LABEL_W = 100
31
+ EDIT_H = 20
32
+ EDIT_W = 100
33
+ BUTTON_W = 70
34
+ BUTTON_H = 25
35
+
36
+ def exinit(name)
37
+ @name = name
38
+ self
39
+ end
40
+
41
+ def construct
42
+ self.caption = "Set Desktop Name"
43
+ v_frame = Layout::VertLayoutFrame.new(nil,{Layout::SPACING=>10}) do |vf|
44
+ # For the date input
45
+ Layout::HorzLayoutFrame.new(vf, {Layout::SPACING=>5}) do |hf|
46
+ hf.addcontrol(self, Layout::Dimensions.new(LABEL_W,LABEL_H), VRStatic, "static", "Screen Name:", 0)
47
+ hf.addcontrol(self, Layout::Dimensions.new(EDIT_W,EDIT_H), VREdit, "name_edit", @name, WStyle::WS_TABSTOP)
48
+ end
49
+
50
+ Layout::HorzLayoutFrame.new(vf, {Layout::SPACING=>5}) do |hf|
51
+ hf.addcontrol(self, Layout::Dimensions.new(BUTTON_W,BUTTON_H), VRButton, "okbutton", "Ok", WStyle::WS_TABSTOP)
52
+ hf.addcontrol(self, Layout::Dimensions.new(BUTTON_W,BUTTON_H), VRButton, "cancelbutton", "Cancel", WStyle::WS_TABSTOP)
53
+ end
54
+ end
55
+
56
+ v_frame.layout(0,0, v_frame.dimensions.min_width, v_frame.dimensions.min_height)
57
+
58
+ @name_edit.text = @name
59
+
60
+ if self.kind_of?(VRDialogComponent) then
61
+ setButtonAs(@okbutton, VRDialogComponent::IDOK)
62
+ setButtonAs(@cancelbutton, VRDialogComponent::IDCANCEL)
63
+ end
64
+
65
+ w, h = self.set_client_size(v_frame, v_frame.dimensions.min_width, v_frame.dimensions.min_height, 10)
66
+ center(@creator, w, h)
67
+ end
68
+
69
+ def okbutton_clicked
70
+ @name = @name_edit.text
71
+ self.close(VRDialogComponent::IDOK)
72
+ end
73
+
74
+ def cancelbutton_clicked
75
+ self.close(VRDialogComponent::IDCANCEL)
76
+ end
77
+ end
78
+
79
+
80
+ class << self
81
+ # create is here to allow another program to
82
+ # change how virtual desktops are created (use an object
83
+ # loaded off of disk instead of a freshly created one
84
+ def create(*args)
85
+ VirtualDesktop.new(*args)
86
+ end
87
+ end
88
+
89
+ def initialize(virtual_desktops, up_icon_filename, down_icon_filename, icon_id, name, index, active, window_manager)
90
+ @virtual_desktops ||= virtual_desktops
91
+ @up_icon ||= VRLocalScreen.factory.iconfromfile(up_icon_filename)
92
+ @down_icon ||= VRLocalScreen.factory.iconfromfile(down_icon_filename)
93
+ @icon_id ||= icon_id
94
+ @name ||= name
95
+ @index ||= index
96
+ @windows ||= []
97
+ @active ||= active
98
+ @form ||= nil
99
+ @windows ||= []
100
+ @window_manager ||= window_manager
101
+ end
102
+
103
+ def icon
104
+ if @active then
105
+ @down_icon
106
+ else
107
+ @up_icon
108
+ end
109
+ end
110
+
111
+ def modify_trayicon
112
+ form.modify_trayicon(icon.hicon, @name, @icon_id)
113
+ end
114
+
115
+ def create_trayicon
116
+ form.create_trayicon(icon.hicon, @name, @icon_id)
117
+ end
118
+
119
+ def delete_trayicon
120
+ form.delete_trayicon(@icon_id)
121
+ end
122
+
123
+ def active=(activate)
124
+ # if we are becoming active
125
+ if (activate && !@active) then
126
+ $TRACE.debug 5, "desktop '#{@name}' becoming active"
127
+ @windows.each do |window|
128
+ $TRACE.debug 5, "activating window #{window}"
129
+ window.show
130
+ end
131
+ # if we are becoming inactive
132
+ elsif (@active && !activate) then
133
+ $TRACE.debug 5, "desktop '#{@name}' becoming inactive"
134
+ @windows.each do |window|
135
+ $TRACE.debug 5, "deactivating window #{window}"
136
+ window.hide
137
+ end
138
+ end
139
+
140
+ @active = activate
141
+ modify_trayicon
142
+ end
143
+
144
+ def add_window(window)
145
+ @virtual_desktops.add_window(window)
146
+ @windows.push(window)
147
+ $TRACE.debug 5, "adding window #{window}"
148
+ $TRACE.debug 5, "after add windows = #{@windows.map{|w| w.to_s}.join(',')}"
149
+ end
150
+
151
+ def remove_window(window)
152
+ @windows.delete_if {|x| x.hwnd == window.hwnd}
153
+ $TRACE.debug 5, "removing window #{window}"
154
+ $TRACE.debug 5, "after remove windows = #{@windows.map{|w| w.to_s}.join(',')}"
155
+ end
156
+
157
+ def handle_exit
158
+ @windows.each {|window| window.show}
159
+ end
160
+
161
+ def set_screen_name
162
+ set_screen_name_form = VRLocalScreen.newdialog(@mainForm,nil,SetScreenName)
163
+ returnResult = set_screen_name_form.exinit(@name).open nil,true
164
+
165
+ if returnResult == VRDialogComponent::IDOK then
166
+ @name = set_screen_name_form.name
167
+ modify_trayicon
168
+ end
169
+ end
170
+
171
+ def contains_window(window)
172
+ @windows.index(window)
173
+ end
174
+
175
+ def to_s
176
+ "#{name}: windows = " + @windows.map{|w| w.to_s}.join(",")
177
+ end
178
+
179
+ class SaveInfo
180
+ attr_reader :window_handles, :name
181
+ def initialize(window_handles, name)
182
+ @window_handles = window_handles
183
+ @name = name
184
+ end
185
+
186
+ end
187
+
188
+ #
189
+ # the info that we save for this desktop is just an array of window handles of
190
+ # the windows on this desktop
191
+ #
192
+ def save_info
193
+ SaveInfo.new(@windows.map{|w| w.hwnd}, @name)
194
+ end
195
+
196
+ #
197
+ # when restoring those windows, find the window associated with the stored window handle
198
+ # and add it to the desktop and show it if we are active
199
+ #
200
+ def restore_info(save_info)
201
+ window_handles = save_info.window_handles
202
+
203
+ # add in the windows
204
+ $TRACE.debug 5, "window_handles = #{window_handles.inspect}"
205
+ all_windows = @window_manager.get_all_windows(true).values
206
+ $TRACE.debug 5, "all-windows = " + all_windows.map{|w| w.to_s}.join(",")
207
+ window_handles.each do |hwnd|
208
+ window = all_windows.select{|w| w.hwnd == hwnd}[0]
209
+ if window then
210
+ $TRACE.debug 5, "found window = #{window}"
211
+ add_window(window)
212
+ if @active then
213
+ window.show
214
+ else
215
+ window.hide
216
+ end
217
+ end
218
+ end
219
+
220
+ # modify the tray icon name
221
+ @name = save_info.name
222
+ modify_trayicon
223
+ end
224
+ end
225
+
226
+ #
227
+ # This class holds the array of virtual desktops
228
+ #
229
+ class VirtualDesktops < Array
230
+ attr_reader :active_desktop_index
231
+
232
+ class << self
233
+ # create is here to allow another program to
234
+ # change how virtual desktops are created (use an object
235
+ # loaded off of disk instead of a freshly created one
236
+ def create(*args)
237
+ VirtualDesktops.new(*args)
238
+ end
239
+ end
240
+
241
+ NUM_NAMES = ["one", "two", "three", "four", "five", "six", "seven", "eight"]
242
+ def initialize(form, dir_base, icon_id_base, num_desktops, window_manager, *args)
243
+ super(*args)
244
+ @form ||= form
245
+ @active_desktop_index ||= 0
246
+ @window_manager ||= window_manager
247
+ @last_windows ||= get_all_windows
248
+ @all_windows ||= []
249
+
250
+ NUM_NAMES.each_with_index do |number, index|
251
+ icon_base = dir_base + "/" + number
252
+ self.push(VirtualDesktop.create(self, icon_base + "_up.ico", icon_base + "_down.ico", icon_id_base+index, "Screen #{index+1}", index, index == 0, window_manager))
253
+ break if index+1 >= num_desktops
254
+ end
255
+ end
256
+
257
+ def push(virtual_desktop)
258
+ super(virtual_desktop)
259
+ virtual_desktop.form = @form
260
+ end
261
+
262
+ def active_desktop_index=(desktop_num)
263
+ self[@active_desktop_index].active = false
264
+ @active_desktop_index = desktop_num
265
+ self[desktop_num].active = true
266
+ @last_windows = get_all_windows
267
+ end
268
+
269
+ def active_desktop
270
+ self[@active_desktop_index]
271
+ end
272
+
273
+ def change_desktop(icon_id)
274
+ clicked_desktop = self.select{|s| s.icon_id == icon_id}[0]
275
+ $TRACE.debug 5, "change_desktop: #{icon_id} -> #{clicked_desktop} ? #{@active_desktop_index}"
276
+ if clicked_desktop.index != @active_desktop_index then
277
+ $TRACE.debug 5, "changing desktops"
278
+ self.active_desktop_index = clicked_desktop.index
279
+
280
+ # save the changed state
281
+ save
282
+ end
283
+ end
284
+
285
+ def move_window(icon_id)
286
+ # if the clicked desktop is not the active desktop then
287
+ clicked_desktop = self.select{|s| s.icon_id == icon_id}[0]
288
+ if clicked_desktop.index != @active_desktop_index then
289
+ # get top window object
290
+ windows = @window_manager.get_all_windows
291
+ top_window_hwnd = @window_manager.get_top_window(windows)
292
+ top_window = windows[top_window_hwnd]
293
+
294
+ # move it from the active to the clicked desktop
295
+ self[@active_desktop_index].remove_window(top_window)
296
+ self[clicked_desktop.index].add_window(top_window)
297
+
298
+ # and now hide it
299
+ top_window.hide
300
+
301
+ # save the changed state
302
+ save
303
+ end
304
+ end
305
+
306
+ def add_window(window)
307
+ @all_windows.push(window) unless @all_windows.index(window)
308
+ end
309
+
310
+ def create_trayicons
311
+ self.reverse.each do |desktop|
312
+ desktop.create_trayicon
313
+ end
314
+ end
315
+
316
+ def delete_trayicons
317
+ self.each do |desktop|
318
+ desktop.delete_trayicon
319
+ end
320
+ end
321
+
322
+ def get_all_windows
323
+ # select all non-tooltip windows
324
+ @window_manager.get_all_windows.values.select do |w|
325
+ !/tooltips/.match(w.class_name)
326
+ end
327
+ end
328
+
329
+ def contains_window(window)
330
+ self.each do |virtual_desktop|
331
+ if virtual_desktop.contains_window(window) then
332
+ return true
333
+ end
334
+ end
335
+
336
+ return false
337
+ end
338
+
339
+ #
340
+ # this needs to be called periodically (< 1sec) to detect
341
+ # windows that are being closed or opened
342
+ #
343
+ def periodic_timer
344
+ current_windows = get_all_windows
345
+ added_windows = current_windows - @last_windows
346
+ removed_windows = @last_windows - current_windows
347
+
348
+ $TRACE.debug 7, "cur windows = " + current_windows.map{|w|w.hwnd}.join(",")
349
+ $TRACE.debug 7, "last windows = " + @last_windows.map{|w|w.hwnd}.join(",")
350
+ $TRACE.debug 7, "added_windows = " + added_windows.map{|w|"#{w.hwnd}-#{w.class_name}"}.join(",")
351
+ $TRACE.debug 7, "removed_windows = " + removed_windows.map{|w|"#{w.hwnd}-#{w.class_name}"}.join(",")
352
+
353
+ active_desktop = self[@active_desktop_index]
354
+ added_windows.each do |window|
355
+ active_desktop.add_window(window)
356
+ end
357
+ removed_windows.each do |window|
358
+ active_desktop.remove_window(window)
359
+ end
360
+
361
+ # save the state if there were any modifications
362
+ if added_windows.size > 0 || removed_windows.size > 0 then
363
+ save
364
+ end
365
+
366
+ @last_windows = current_windows
367
+ end
368
+
369
+ def set_screen_name
370
+ self[@active_desktop_index].set_screen_name
371
+ end
372
+
373
+ def save_info
374
+ virtual_desktops = []
375
+ self.each do |vd|
376
+ virtual_desktops.push(vd.save_info)
377
+ end
378
+ virtual_desktops
379
+ end
380
+
381
+ def restore_info(virtual_desktop_infos)
382
+ self.each_with_index do |virtual_desktop, i|
383
+ virtual_desktop.restore_info(virtual_desktop_infos[i])
384
+ end
385
+ end
386
+
387
+ class Configuration
388
+ attr_reader :save_info
389
+ def initialize(save_info)
390
+ @name = "Virtual Desktop Configuration File"
391
+ @version = "1.0"
392
+ @save_info = save_info
393
+ end
394
+ end
395
+
396
+ def save
397
+ File.open("c:/vtdesk.txt", "w") do |f|
398
+ f.write(Configuration.new(self.save_info).to_yaml)
399
+ end
400
+ end
401
+
402
+ def restore
403
+ configuration = nil
404
+ File.open("c:/vtdesk.txt", "r") do |f|
405
+ configuration = YAML::load(f)
406
+ end
407
+
408
+ restore_info(configuration.save_info)
409
+ end
410
+
411
+ def handle_exit
412
+ self.each do |desktop|
413
+ desktop.handle_exit
414
+ end
415
+ end
416
+ end
417
+
418
+ end # module Win32