File Coverage

blib/lib/Tk/StayOnTop.pm
Criterion Covered Total %
statement 24 120 20.0
branch 0 30 0.0
condition 0 6 0.0
subroutine 8 13 61.5
pod n/a
total 32 169 18.9


line stmt bran cond sub pod time code
1             package Tk::StayOnTop;
2              
3             our $VERSION = 0.12;
4              
5             #==============================================================================#
6              
7             =head1 NAME
8              
9             Tk::StayOnTop - Keep your window in the foreground
10              
11             =head1 SYNOPSIS
12              
13             use Tk::StayOnTop;
14             $toplevel->stayOnTop;
15             $toplevel->dontStayOnTop;
16              
17             =head1 DESCRIPTION
18              
19             Adds methods to the Tk::Toplevel base class so that a window can stay on top
20             off all other windows.
21              
22             =head2 METHODS
23              
24             =over 4
25              
26             =cut
27              
28             #==============================================================================#
29              
30             package Tk::Toplevel;
31              
32 1     1   9267 use strict;
  1         2  
  1         38  
33 1     1   5 use warnings;
  1         1  
  1         32  
34 1     1   1044 use Switch;
  1         66913  
  1         6  
35 1     1   64543 use Carp;
  1         3  
  1         100  
36              
37             my ($method,$win32_winpos,$repeat_id); # Globals
38              
39             ## We have various ways to do this - we have to guess which is best
40 1     1   5 use constant METHOD_SIMPLE => 1; # Pure Tk - Visibility event/Timer
  1         3  
  1         74  
41 1     1   6 use constant METHOD_ATTRIB => 2; # Use the new -topmost => 1 attib (Win32)
  1         2  
  1         43  
42 1     1   7 use constant METHOD_WINAPI => 3; # Win32 API calls
  1         2  
  1         52  
43 1     1   5 use constant METHOD_WMSTATE => 4; # Use Magic X WM calls (Gnome, maybe KDE etc)
  1         2  
  1         1164  
44              
45              
46             #==============================================================================#
47             # Guess which method to use. This gets called after the window has been created
48             # because we may need to ask the window manager questions about it.
49             # $method is stored as a global which may be a bad thing - but let's see who
50             # complains.
51             #
52              
53             sub get_method {
54 0     0     my ($obj) = @_;
55              
56 0 0         if ($^O =~ /Win32/) {
57              
58 0 0         return METHOD_ATTRIB if $Tk::VERSION >= 804.027;
59              
60 0           eval "use Win32::API";
61 0 0         unless ($@) {
62 0           $win32_winpos = Win32::API->new(
63             'user32', 'SetWindowPos',
64             ['N','N','N','N','N','N','N'], 'N'
65             );
66 0           return METHOD_WINAPI;
67             }
68              
69 0           croak "Sorry you need either Tk version >= 804.027 or Win32::API installed";
70              
71             } else {
72              
73 0           eval {
74 0           die "not supported" if !grep {
75 0 0         $_ eq '_NET_WM_STATE_STAYS_ON_TOP'
76             } $obj->property('get', '_NET_SUPPORTED', 'root');
77             };
78 0 0         return METHOD_WMSTATE unless $@;
79             }
80              
81 0           return METHOD_SIMPLE;
82             }
83              
84            
85             #==============================================================================#
86              
87             =item $toplevel->stayOnTop();
88              
89             Keep $toplevel in the foreground.
90              
91             =cut
92              
93             sub stayOnTop {
94 0     0     my ($obj) = @_;
95              
96 0   0       $method ||= $obj->get_method;
97             #warn "Chosen method is $method";
98              
99 0           switch ($method) {
  0            
  0            
  0            
100              
101 0 0         case METHOD_WINAPI {
  0            
102 0           $obj->update;
103             # HWND_TOPMOST (-1) and SWP_NOSIZE+SWP_NOMOVE (3)
104 0           $win32_winpos->Call(hex($obj->frame()),-1,0,0,0,0,3);
105 0           }
  0            
  0            
  0            
106              
107 0 0         case METHOD_ATTRIB {
  0            
108 0           $obj->attributes(-topmost => 1);
109 0           }
  0            
  0            
  0            
110              
111 0 0         case METHOD_WMSTATE {
  0            
112 0           my($wrapper) = $obj->toplevel->wrapper;
113 0           $obj->property('set', '_NET_WM_STATE', "ATOM", 32,
114             ["_NET_WM_STATE_STAYS_ON_TOP"], $wrapper);
115 0           }
  0            
  0            
  0            
116              
117 0 0         case METHOD_SIMPLE {
  0            
118 0           my $stay_above_after;
119             $obj->bind("" => sub {
120 0 0   0     if ($repeat_id) {
121 0           $obj->deiconify;
122 0           $obj->raise;
123             }
124 0           });
125             $repeat_id = $obj->repeat(1000, sub {
126 0     0     $obj->deiconify;
127 0           $obj->raise;
128 0           undef $stay_above_after;
129 0 0         }) unless defined $repeat_id;
130              
131 0           }
  0            
  0            
  0            
132              
133             else {
134 0           die "Invalid method type [$method]";
135             }
136             }
137             }
138              
139             #==============================================================================#
140              
141             =item $toplevel->dontStayOnTop();
142              
143             Return $toplevel to normal behaviour.
144              
145             =cut
146              
147             sub dontStayOnTop {
148 0     0     my ($obj) = @_;
149              
150 0   0       $method ||= $obj->get_method;
151              
152 0           switch ($method) {
  0            
  0            
  0            
153              
154 0 0         case METHOD_WINAPI {
  0            
155 0           $obj->update;
156             # HWND_NOTOPMOST (-2) and SWP_NOSIZE+SWP_NOMOVE (3)
157 0           $win32_winpos->Call(hex($obj->frame()),-2,0,0,0,0,3);
158 0           }
  0            
  0            
  0            
159              
160 0 0         case METHOD_ATTRIB {
  0            
161 0           $obj->attributes(-topmost => 0);
162 0           }
  0            
  0            
  0            
163              
164 0 0         case METHOD_WMSTATE {
  0            
165 0           my($wrapper) = $obj->toplevel->wrapper;
166 0           $obj->property('delete', "_NET_WM_STATE_STAYS_ON_TOP", $wrapper);
167 0           }
  0            
  0            
  0            
168              
169 0 0         case METHOD_SIMPLE {
  0            
170 0           $obj->afterCancel($repeat_id);
171 0           $repeat_id = undef;
172 0           }
  0            
  0            
  0            
173              
174             else {
175 0           die "Invalid method type [$method]";
176             }
177             }
178              
179             }
180              
181             1;
182              
183             #==============================================================================#
184              
185             =back
186              
187             =head1 IMPLEMENTATION DETAILS
188              
189             The module uses a number of methods for trying to keep the window in the
190             foreground. It will atomatically choose the most suitable available. The methods
191             can be split between Microsoft Windows and X-Windows:
192              
193             =over 4
194              
195             =item Microsoft Windows
196              
197             Perl Tk Version 804.027 and newer support the "-toplevel => 1" attribute. This
198             will be used if possible.
199              
200             On older Perl Tk versions, the module will search for the Win32::API module and
201             use direct API calls to the OS.
202              
203             =item X-Windows
204              
205             For newer X window managers (Gnome, myabe KDE) it will try to set the
206             NET_WM_STATE_STAYS_ON_TOP property of the window.
207              
208             If this is not implemented, it will just try to the raise window every time
209             it's covered. This could cause problems if you have two windows competing to
210             stay on top.
211              
212             =back
213              
214             I am hoping that the Perl Tk build in "-toplevel => 1" attribute will be
215             implement in the future and this module will no longer be needed. However
216             in the mean time, if you have any other platform dependent solutions, please
217             let me know and I will try to include them.
218              
219             =head1 BUGS
220              
221             See limits in X-Windows functionality descibed above.
222              
223             =head1 AUTHOR
224              
225             This module is Copyright (c) 2004 Gavin Brock gbrock@cpan.org. All rights
226             reserved. This program is free software; you can redistribute it and/or
227             modify it under the same terms as Perl itself.
228              
229             Many thanks to Slaven Rezic for his many implemntation suggestions.
230              
231             =head1 SEE ALSO
232              
233             L
234              
235             L
236              
237             =cut
238              
239              
240              
241             #==============================================================================#
242             # NOTES FOR ME!!
243             #
244             # Use of setwindowpos() function.
245             # See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/windows/windowreference/windowfunctions/setwindowpos.asp
246             #define SWP_NOSIZE 0x0001
247             #define SWP_NOMOVE 0x0002
248             #define SWP_NOZORDER 0x0004
249             #define SWP_NOREDRAW 0x0008
250             #define SWP_NOACTIVATE 0x0010
251             #define SWP_FRAMECHANGED 0x0020
252             #define SWP_SHOWWINDOW 0x0040
253             #define SWP_HIDEWINDOW 0x0080
254             #define SWP_NOCOPYBITS 0x0100
255             #define SWP_NOOWNERZORDER 0x0200
256             #define SWP_NOSENDCHANGING 0x0400
257             #define SWP_DRAWFRAME SWP_FRAMECHANGED
258             #define SWP_NOREPOSITION SWP_NOOWNERZORDER
259             #if(WINVER >= 0x0400)
260             #define SWP_DEFERERASE 0x2000
261             #define SWP_ASYNCWINDOWPOS 0x4000
262             #endif /* WINVER >= 0x0400 */
263             #define HWND_TOP ((HWND)0)
264             #define HWND_BOTTOM ((HWND)1)
265             #define HWND_TOPMOST ((HWND)-1)
266             #define HWND_NOTOPMOST ((HWND)-2)
267             #
268              
269             # That's all folks..
270             #==============================================================================#
271              
272             1;