2222# along with this program. If not, see <https://www.gnu.org/licenses/>.
2323
2424# This file is the system monitor main program to display HW sensors on your screen using themes (see README)
25- import locale
2625import os
27- import platform
28- import signal
2926import sys
30- import time
3127
3228MIN_PYTHON = (3 , 7 )
3329if sys .version_info < MIN_PYTHON :
3733 except :
3834 os ._exit (0 )
3935
36+ import atexit
37+ import locale
38+ import platform
39+ import signal
40+ import time
4041from PIL import Image
4142
43+ if platform .system () == 'Windows' :
44+ import win32api
45+ import win32con
46+ import win32gui
47+
4248try :
4349 import pystray
4450except :
@@ -86,7 +92,7 @@ def clean_stop(tray_icon=None):
8692 os ._exit (0 )
8793
8894
89- def sighandler (signum , frame = None ):
95+ def on_signal_caught (signum , frame = None ):
9096 logger .info ("Caught signal %d, exiting" % signum )
9197 clean_stop ()
9298
@@ -96,6 +102,25 @@ def on_exit_tray(tray_icon, item):
96102 clean_stop (tray_icon )
97103
98104
105+ def on_clean_exit (* args ):
106+ logger .info ("Program will now exit" )
107+ clean_stop ()
108+
109+
110+ if platform .system () == "Windows" :
111+ def on_win32_ctrl_event (event ):
112+ """Handle Windows console control events (like Ctrl-C)."""
113+ if event in (win32con .CTRL_C_EVENT , win32con .CTRL_BREAK_EVENT , win32con .CTRL_CLOSE_EVENT ):
114+ logger .info ("Caught Windows control event %s, exiting" % event )
115+ clean_stop ()
116+ return 0
117+
118+
119+ def on_win32_wm_event (hWnd , msg , wParam , lParam ):
120+ """Handle Windows window message events (like ENDSESSION, CLOSE, DESTROY)."""
121+ logger .debug ("Caught Windows window message event %s, exiting" % msg )
122+ clean_stop ()
123+
99124 # Create a tray icon for the program, with an Exit entry in menu
100125 try :
101126 tray_icon = pystray .Icon (
@@ -116,12 +141,15 @@ def on_exit_tray(tray_icon, item):
116141 tray_icon = None
117142 logger .warning ("Tray icon is not supported on your platform" )
118143
119- # Set the signal handlers, to send a complete frame to the LCD before exit
120- signal .signal (signal .SIGINT , sighandler )
121- signal .signal (signal .SIGTERM , sighandler )
144+ # Set the different stopping event handlers, to send a complete frame to the LCD before exit
145+ atexit .register (on_clean_exit )
146+ signal .signal (signal .SIGINT , on_signal_caught )
147+ signal .signal (signal .SIGTERM , on_signal_caught )
122148 is_posix = os .name == 'posix'
123149 if is_posix :
124- signal .signal (signal .SIGQUIT , sighandler )
150+ signal .signal (signal .SIGQUIT , on_signal_caught )
151+ if platform .system () == "Windows" :
152+ win32api .SetConsoleCtrlHandler (on_win32_ctrl_event , True )
125153
126154 # Initialize the display
127155 display .initialize_display ()
@@ -150,13 +178,49 @@ def on_exit_tray(tray_icon, item):
150178 scheduler .DateStats ()
151179 scheduler .QueueHandler ()
152180
153- if tray_icon and platform .system () == "Darwin" :
154- from AppKit import NSBundle , NSApp , NSAutoreleasePool , NSApplicationActivationPolicyRegular , NSApplicationActivationPolicyProhibited
181+ if tray_icon and platform .system () == "Darwin" : # macOS-specific
182+ from AppKit import NSBundle , NSApp , NSApplicationActivationPolicyProhibited
155183
156- # Hide Python Launcher icon from MacOS dock
184+ # Hide Python Launcher icon from macOS dock
157185 info = NSBundle .mainBundle ().infoDictionary ()
158186 info ["LSUIElement" ] = "1"
159187 NSApp .setActivationPolicy_ (NSApplicationActivationPolicyProhibited )
160188
161189 # For macOS: display the tray icon now with blocking function
162190 tray_icon .run ()
191+
192+ elif platform .system () == "Windows" : # Windows-specific
193+ # Create a hidden window just to be able to receive window message events (for shutdown/logoff clean stop)
194+ hinst = win32api .GetModuleHandle (None )
195+ wndclass = win32gui .WNDCLASS ()
196+ wndclass .hInstance = hinst
197+ wndclass .lpszClassName = "turingEventWndClass"
198+ messageMap = {win32con .WM_QUERYENDSESSION : on_win32_wm_event ,
199+ win32con .WM_ENDSESSION : on_win32_wm_event ,
200+ win32con .WM_QUIT : on_win32_wm_event ,
201+ win32con .WM_DESTROY : on_win32_wm_event ,
202+ win32con .WM_CLOSE : on_win32_wm_event }
203+
204+ wndclass .lpfnWndProc = messageMap
205+
206+ try :
207+ myWindowClass = win32gui .RegisterClass (wndclass )
208+ hwnd = win32gui .CreateWindowEx (win32con .WS_EX_LEFT ,
209+ myWindowClass ,
210+ "turingEventWnd" ,
211+ 0 ,
212+ 0 ,
213+ 0 ,
214+ win32con .CW_USEDEFAULT ,
215+ win32con .CW_USEDEFAULT ,
216+ 0 ,
217+ 0 ,
218+ hinst ,
219+ None )
220+ while True :
221+ # Receive and dispatch window messages
222+ win32gui .PumpWaitingMessages ()
223+ time .sleep (0.5 )
224+
225+ except Exception as e :
226+ logger .error ("Exception while creating event window: %s" % str (e ))
0 commit comments