22
22
# along with this program. If not, see <https://www.gnu.org/licenses/>.
23
23
24
24
# This file is the system monitor main program to display HW sensors on your screen using themes (see README)
25
- import locale
26
25
import os
27
- import platform
28
- import signal
29
26
import sys
30
- import time
31
27
32
28
MIN_PYTHON = (3 , 7 )
33
29
if sys .version_info < MIN_PYTHON :
37
33
except :
38
34
os ._exit (0 )
39
35
36
+ import atexit
37
+ import locale
38
+ import platform
39
+ import signal
40
+ import time
40
41
from PIL import Image
41
42
43
+ if platform .system () == 'Windows' :
44
+ import win32api
45
+ import win32con
46
+ import win32gui
47
+
42
48
try :
43
49
import pystray
44
50
except :
@@ -86,7 +92,7 @@ def clean_stop(tray_icon=None):
86
92
os ._exit (0 )
87
93
88
94
89
- def sighandler (signum , frame = None ):
95
+ def on_signal_caught (signum , frame = None ):
90
96
logger .info ("Caught signal %d, exiting" % signum )
91
97
clean_stop ()
92
98
@@ -96,6 +102,25 @@ def on_exit_tray(tray_icon, item):
96
102
clean_stop (tray_icon )
97
103
98
104
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
+
99
124
# Create a tray icon for the program, with an Exit entry in menu
100
125
try :
101
126
tray_icon = pystray .Icon (
@@ -116,12 +141,15 @@ def on_exit_tray(tray_icon, item):
116
141
tray_icon = None
117
142
logger .warning ("Tray icon is not supported on your platform" )
118
143
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 )
122
148
is_posix = os .name == 'posix'
123
149
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 )
125
153
126
154
# Initialize the display
127
155
display .initialize_display ()
@@ -150,13 +178,49 @@ def on_exit_tray(tray_icon, item):
150
178
scheduler .DateStats ()
151
179
scheduler .QueueHandler ()
152
180
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
155
183
156
- # Hide Python Launcher icon from MacOS dock
184
+ # Hide Python Launcher icon from macOS dock
157
185
info = NSBundle .mainBundle ().infoDictionary ()
158
186
info ["LSUIElement" ] = "1"
159
187
NSApp .setActivationPolicy_ (NSApplicationActivationPolicyProhibited )
160
188
161
189
# For macOS: display the tray icon now with blocking function
162
190
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