2020import logging
2121import os
2222import time
23+ import typing
2324
2425import gi
2526from safeeyes import utility
26- from safeeyes .model import TrayAction
27+ from safeeyes .model import Break , Config , TrayAction
2728from safeeyes .translations import translate as _
2829import Xlib
2930from Xlib .display import Display
@@ -44,7 +45,15 @@ class BreakScreen:
4445 This class creates and manages the fullscreen windows for every monitor.
4546 """
4647
47- def __init__ (self , application , context , on_skipped , on_postponed ):
48+ windows : list ["BreakScreenWindow" ]
49+
50+ def __init__ (
51+ self ,
52+ application : Gtk .Application ,
53+ context ,
54+ on_skipped : typing .Callable [[], None ],
55+ on_postponed : typing .Callable [[], None ],
56+ ):
4857 self .application = application
4958 self .context = context
5059 self .x11_display = None
@@ -64,7 +73,7 @@ def __init__(self, application, context, on_skipped, on_postponed):
6473 if not self .context ["is_wayland" ]:
6574 self .x11_display = Display ()
6675
67- def initialize (self , config ) :
76+ def initialize (self , config : Config ) -> None :
6877 """Initialize the internal properties from configuration."""
6978 logging .info ("Initialize the break screen" )
7079 self .enable_postpone = config .get ("allow_postpone" , False )
@@ -84,36 +93,38 @@ def initialize(self, config):
8493 self .shortcut_disable_time = config .get ("shortcut_disable_time" , 2 )
8594 self .strict_break = config .get ("strict_break" , False )
8695
87- def skip_break (self ):
96+ def skip_break (self ) -> None :
8897 """Skip the break from the break screen."""
8998 logging .info ("User skipped the break" )
9099 # Must call on_skipped before close to lock screen before closing the break
91100 # screen
92101 self .on_skipped ()
93102 self .close ()
94103
95- def postpone_break (self ):
104+ def postpone_break (self ) -> None :
96105 """Postpone the break from the break screen."""
97106 logging .info ("User postponed the break" )
98107 self .on_postponed ()
99108 self .close ()
100109
101- def on_skip_clicked (self , button ):
110+ def on_skip_clicked (self , button ) -> None :
102111 """Skip button press event handler."""
103112 self .skip_break ()
104113
105- def on_postpone_clicked (self , button ):
114+ def on_postpone_clicked (self , button ) -> None :
106115 """Postpone button press event handler."""
107116 self .postpone_break ()
108117
109- def show_count_down (self , countdown , seconds ) :
118+ def show_count_down (self , countdown : int , seconds : int ) -> None :
110119 """Show/update the count down on all screens."""
111120 self .enable_shortcut = self .shortcut_disable_time <= seconds
112121 mins , secs = divmod (countdown , 60 )
113122 timeformat = "{:02d}:{:02d}" .format (mins , secs )
114123 GLib .idle_add (lambda : self .__update_count_down (timeformat ))
115124
116- def show_message (self , break_obj , widget , tray_actions = []):
125+ def show_message (
126+ self , break_obj : Break , widget : str , tray_actions : list [TrayAction ] = []
127+ ) -> None :
117128 """Show the break screen with the given message on all displays."""
118129 message = break_obj .name
119130 image_path = break_obj .image
@@ -122,7 +133,7 @@ def show_message(self, break_obj, widget, tray_actions=[]):
122133 lambda : self .__show_break_screen (message , image_path , widget , tray_actions )
123134 )
124135
125- def close (self ):
136+ def close (self ) -> None :
126137 """Hide the break screen from active window and destroy all other
127138 windows.
128139 """
@@ -133,14 +144,24 @@ def close(self):
133144 # Destroy other windows if exists
134145 GLib .idle_add (lambda : self .__destroy_all_screens ())
135146
136- def __show_break_screen (self , message , image_path , widget , tray_actions ):
147+ def __show_break_screen (
148+ self ,
149+ message : str ,
150+ image_path : typing .Optional [str ],
151+ widget : str ,
152+ tray_actions : list [TrayAction ],
153+ ) -> None :
137154 """Show an empty break screen on all screens."""
138155 # Lock the keyboard
139156 if not self .context ["is_wayland" ]:
140157 utility .start_thread (self .__lock_keyboard_x11 )
141158
142159 display = Gdk .Display .get_default ()
143- monitors = display .get_monitors ()
160+
161+ if display is None :
162+ raise Exception ("display not found" )
163+
164+ monitors = typing .cast (typing .Sequence [Gdk .Monitor ], display .get_monitors ())
144165 logging .info ("Show break screens in %d display(s)" , len (monitors ))
145166
146167 skip_button_disabled = self .context .get ("skip_button_disabled" , False )
@@ -196,17 +217,22 @@ def __show_break_screen(self, message, image_path, widget, tray_actions):
196217
197218 if self .context ["is_wayland" ]:
198219 # this may or may not be granted by the window system
199- window .get_surface ().inhibit_system_shortcuts (None )
220+ surface = window .get_surface ()
221+ if surface is not None :
222+ typing .cast (Gdk .Toplevel , surface ).inhibit_system_shortcuts (None )
200223
201224 i = i + 1
202225
203- def __update_count_down (self , count ) :
226+ def __update_count_down (self , count : str ) -> None :
204227 """Update the countdown on all break screens."""
205228 for window in self .windows :
206229 window .set_count_down (count )
207230
208- def __window_set_keep_above_x11 (self , window ) :
231+ def __window_set_keep_above_x11 (self , window : "BreakScreenWindow" ) -> None :
209232 """Use EWMH hints to keep window above and on all desktops."""
233+ if self .x11_display is None :
234+ return
235+
210236 NET_WM_STATE = self .x11_display .intern_atom ("_NET_WM_STATE" )
211237 NET_WM_STATE_ABOVE = self .x11_display .intern_atom ("_NET_WM_STATE_ABOVE" )
212238 NET_WM_STATE_STICKY = self .x11_display .intern_atom ("_NET_WM_STATE_STICKY" )
@@ -216,7 +242,12 @@ def __window_set_keep_above_x11(self, window):
216242 # See https://specifications.freedesktop.org/wm-spec/1.3/ar01s05.html#id-1.6.8
217243 root_window = self .x11_display .screen ().root
218244
219- xid = GdkX11 .X11Surface .get_xid (window .get_surface ())
245+ surface = window .get_surface ()
246+
247+ if surface is None or not isinstance (surface , GdkX11 .X11Surface ):
248+ return
249+
250+ xid = GdkX11 .X11Surface .get_xid (surface )
220251
221252 root_window .send_event (
222253 Xlib .protocol .event .ClientMessage (
@@ -240,11 +271,14 @@ def __window_set_keep_above_x11(self, window):
240271
241272 self .x11_display .sync ()
242273
243- def __lock_keyboard_x11 (self ):
274+ def __lock_keyboard_x11 (self ) -> None :
244275 """Lock the keyboard to prevent the user from using keyboard shortcuts.
245276
246277 (X11 only)
247278 """
279+ if self .x11_display is None :
280+ return
281+
248282 logging .info ("Lock the keyboard" )
249283 self .lock_keyboard = True
250284
@@ -275,7 +309,9 @@ def __lock_keyboard_x11(self):
275309 # Reduce the CPU usage by sleeping for a second
276310 time .sleep (1 )
277311
278- def on_key_pressed_wayland (self , event_controller_key , keyval , keycode , state ):
312+ def on_key_pressed_wayland (
313+ self , event_controller_key , keyval , keycode , state
314+ ) -> bool :
279315 if self .enable_shortcut :
280316 if keyval == Gdk .KEY_space and self .show_postpone_button :
281317 self .postpone_break ()
@@ -286,14 +322,17 @@ def on_key_pressed_wayland(self, event_controller_key, keyval, keycode, state):
286322
287323 return False
288324
289- def __release_keyboard_x11 (self ):
325+ def __release_keyboard_x11 (self ) -> None :
290326 """Release the locked keyboard."""
327+ if self .x11_display is None :
328+ return
329+
291330 logging .info ("Unlock the keyboard" )
292331 self .lock_keyboard = False
293332 self .x11_display .ungrab_keyboard (X .CurrentTime )
294333 self .x11_display .flush ()
295334
296- def __destroy_all_screens (self ):
335+ def __destroy_all_screens (self ) -> None :
297336 """Close all the break screens."""
298337 for win in self .windows :
299338 win .destroy ()
@@ -309,25 +348,25 @@ class BreakScreenWindow(Gtk.Window):
309348
310349 __gtype_name__ = "BreakScreenWindow"
311350
312- lbl_message = Gtk .Template .Child ()
313- lbl_count = Gtk .Template .Child ()
314- lbl_widget = Gtk .Template .Child ()
315- img_break = Gtk .Template .Child ()
316- box_buttons = Gtk .Template .Child ()
317- toolbar = Gtk .Template .Child ()
351+ lbl_message : Gtk . Label = Gtk .Template .Child ()
352+ lbl_count : Gtk . Label = Gtk .Template .Child ()
353+ lbl_widget : Gtk . Label = Gtk .Template .Child ()
354+ img_break : Gtk . Image = Gtk .Template .Child ()
355+ box_buttons : Gtk . Box = Gtk .Template .Child ()
356+ toolbar : Gtk . Box = Gtk .Template .Child ()
318357
319358 def __init__ (
320359 self ,
321- application ,
322- message ,
323- image_path ,
324- widget ,
325- tray_actions ,
326- on_close ,
327- show_postpone ,
328- on_postpone ,
329- show_skip ,
330- on_skip ,
360+ application : Gtk . Application ,
361+ message : str ,
362+ image_path : typing . Optional [ str ] ,
363+ widget : str ,
364+ tray_actions : list [ TrayAction ] ,
365+ on_close : typing . Callable [[], None ] ,
366+ show_postpone : bool ,
367+ on_postpone : typing . Callable [[ Gtk . Button ], None ] ,
368+ show_skip : bool ,
369+ on_skip : typing . Callable [[ Gtk . Button ], None ] ,
331370 ):
332371 super ().__init__ (application = application )
333372
@@ -372,10 +411,10 @@ def __init__(
372411 self .lbl_message .set_label (message )
373412 self .lbl_widget .set_markup (widget )
374413
375- def set_count_down (self , count ) :
414+ def set_count_down (self , count : str ) -> None :
376415 self .lbl_count .set_text (count )
377416
378- def __tray_action (self , button , tray_action : TrayAction ):
417+ def __tray_action (self , button , tray_action : TrayAction ) -> None :
379418 """Tray action handler.
380419
381420 Hides all toolbar buttons for this action and call the action
@@ -386,7 +425,7 @@ def __tray_action(self, button, tray_action: TrayAction):
386425 tray_action .action ()
387426
388427 @Gtk .Template .Callback ()
389- def on_window_delete (self , * args ):
428+ def on_window_delete (self , * args ) -> None :
390429 """Window close event handler."""
391430 logging .info ("Closing the break screen" )
392431 self .on_close ()
0 commit comments