vrvirtualdesktop 0.0.1

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