@@ -674,11 +674,16 @@ static const struct wl_registry_listener registry_listener = {
674674};
675675
676676static int sigusr_fds [2 ] = {-1 , -1 };
677+ static int sigusr2_fds [2 ] = {-1 , -1 };
677678
678679void do_sigusr (int sig ) {
679680 (void )write (sigusr_fds [1 ], "1" , 1 );
680681}
681682
683+ void do_sigusr2 (int sig ) {
684+ (void )write (sigusr2_fds [1 ], "1" , 1 );
685+ }
686+
682687static cairo_surface_t * select_image (struct swaylock_state * state ,
683688 struct swaylock_surface * surface ) {
684689 struct swaylock_image * image ;
@@ -854,6 +859,8 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
854859 LO_TEXT_CAPS_LOCK_COLOR ,
855860 LO_TEXT_VER_COLOR ,
856861 LO_TEXT_WRONG_COLOR ,
862+ LO_PLUGIN_GRACE ,
863+ LO_PLUGIN_POINTER_HYSTERESIS ,
857864 LO_PLUGIN_COMMAND ,
858865 LO_PLUGIN_COMMAND_EACH ,
859866 };
@@ -913,6 +920,8 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
913920 {"text-caps-lock-color" , required_argument , NULL , LO_TEXT_CAPS_LOCK_COLOR },
914921 {"text-ver-color" , required_argument , NULL , LO_TEXT_VER_COLOR },
915922 {"text-wrong-color" , required_argument , NULL , LO_TEXT_WRONG_COLOR },
923+ {"grace" , required_argument , NULL , LO_PLUGIN_GRACE },
924+ {"pointer-hysteresis" , required_argument , NULL , LO_PLUGIN_POINTER_HYSTERESIS },
916925 {"command" , required_argument , NULL , LO_PLUGIN_COMMAND },
917926 {"command-each" , required_argument , NULL , LO_PLUGIN_COMMAND_EACH },
918927 {0 , 0 , 0 , 0 }
@@ -1037,6 +1046,10 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
10371046 "Sets the color of the text when verifying.\n"
10381047 " --text-wrong-color <color> "
10391048 "Sets the color of the text when invalid.\n"
1049+ " --grace <seconds> "
1050+ "Allow unlocking without a password before <seconds> elapse\n"
1051+ " --pointer-hysteresis <distance> "
1052+ "If --grace used, minimum mouse motion needed to auto-unlock\n"
10401053 " --command <cmd> "
10411054 "Indicates which program to run to draw backgrounds.\n"
10421055 " --command-each <cmd> "
@@ -1322,6 +1335,38 @@ static int parse_options(int argc, char **argv, struct swaylock_state *state,
13221335 state -> args .colors .text .wrong = parse_color (optarg );
13231336 }
13241337 break ;
1338+ case LO_PLUGIN_GRACE :
1339+ if (state ) {
1340+ char * end = NULL ;
1341+ float value = strtof (optarg , & end );
1342+ float unit = 1.0 ;
1343+ if (* end == 0 || strcmp (end , "s" ) == 0 || strcmp (end , "sec" ) == 0 ) {
1344+ unit = 1.0 ;
1345+ } else if (strcmp (end , "m" ) == 0 || strcmp (end , "min" ) == 0 ) {
1346+ unit = 60.0 ;
1347+ } else if (strcmp (end , "h" ) == 0 || strcmp (end , "hr" ) == 0 ) {
1348+ unit = 3600.0 ;
1349+ } else {
1350+ swaylock_log (LOG_ERROR ,
1351+ "'%s' is not a valid grace time specification. Valid examples: 11.5, 100s, 30min, 2hr" ,
1352+ optarg );
1353+ break ;
1354+ }
1355+ state -> args .grace_time = value * unit ;
1356+ }
1357+ break ;
1358+ case LO_PLUGIN_POINTER_HYSTERESIS :
1359+ if (state ) {
1360+ char * end = NULL ;
1361+ float value = strtof (optarg , & end );
1362+ if (* end == '\0' ) {
1363+ state -> args .grace_pointer_hysteresis = value ;
1364+ } else {
1365+ swaylock_log (LOG_ERROR ,
1366+ "Invalid value for pointer hysteresis: '%s' is not a real number" , optarg );
1367+ }
1368+ }
1369+ break ;
13251370 case LO_PLUGIN_COMMAND :
13261371 if (state ) {
13271372 free (state -> args .plugin_command );
@@ -1852,6 +1897,19 @@ static void output_redraw_timeout(void *data) {
18521897 client_timeout (surface -> client ? surface -> client : surface -> state -> server .main_client );
18531898}
18541899
1900+ static void grace_timeout (void * data ) {
1901+ struct swaylock_state * state = data ;
1902+ // The event loop frees the timer object; setting grace_timer to null marks
1903+ // the grace period as over
1904+ swaylock_log (LOG_DEBUG , "Grace period ended" );
1905+ state -> grace_timer = NULL ;
1906+ loop_remove_fd (state -> eventloop , state -> sleep_comm_r );
1907+ close (state -> sleep_comm_r );
1908+ close (state -> sleep_comm_w );
1909+ state -> sleep_comm_r = -1 ;
1910+ state -> sleep_comm_w = -1 ;
1911+ }
1912+
18551913uint32_t posix_spawn_setsid_flag (void );
18561914static bool spawn_command (struct swaylock_state * state , int sock_child ,
18571915 int sock_local , const char * output_name , const char * output_desc ) {
@@ -2103,6 +2161,71 @@ static void term_in(int fd, short mask, void *data) {
21032161 state .run_display = false;
21042162}
21052163
2164+ static void lock_in (int fd , short mask , void * data ) {
2165+ /* On receipt of SIGUSR2, end the grace period */
2166+ if (state .grace_timer ) {
2167+ /* Timer will automatically be freed later */
2168+ swaylock_log (LOG_DEBUG , "Received SIGUSR2, ending unlock grace period" );
2169+ loop_remove_timer (state .eventloop , state .grace_timer );
2170+ state .grace_timer = NULL ;
2171+ loop_remove_fd (state .eventloop , state .sleep_comm_r );
2172+ close (state .sleep_comm_r );
2173+ close (state .sleep_comm_w );
2174+ state .sleep_comm_r = -1 ;
2175+ state .sleep_comm_w = -1 ;
2176+ }
2177+ }
2178+
2179+ static void sleep_in (int fd , short mask , void * data ) {
2180+ struct swaylock_state * state = data ;
2181+ if (state -> grace_timer ) {
2182+ swaylock_log (LOG_DEBUG , "Received sleep notification, ending unlock grace period" );
2183+ loop_remove_timer (state -> eventloop , state -> grace_timer );
2184+ state -> grace_timer = NULL ;
2185+ close (state -> sleep_comm_r );
2186+ close (state -> sleep_comm_w );
2187+ state -> sleep_comm_r = -1 ;
2188+ state -> sleep_comm_w = -1 ;
2189+ }
2190+ loop_remove_fd (state -> eventloop , state -> sleep_comm_r );
2191+ }
2192+
2193+ static int start_sleep_watcher (int * sleep_comm_r , int * sleep_comm_w ) {
2194+ int comm_r [2 ], comm_w [2 ];
2195+ if (pipe (comm_r ) != 0 || pipe (comm_w ) != 0 ) {
2196+ swaylock_log (LOG_ERROR , "Failed to create communication pipes" );
2197+ return -1 ;
2198+ }
2199+
2200+ if (!set_cloexec (comm_r [0 ]) || !set_cloexec (comm_w [1 ])) {
2201+ swaylock_log (LOG_ERROR , "Failed to make pipes close-on-exec" );
2202+ close (comm_r [0 ]);
2203+ close (comm_r [1 ]);
2204+ close (comm_w [0 ]);
2205+ close (comm_w [1 ]);
2206+ return -1 ;
2207+ }
2208+
2209+ pid_t pid ;
2210+ char proc_r_str [20 ], proc_w_str [20 ];
2211+ snprintf (proc_r_str , sizeof (proc_r_str ), "%d" , comm_w [0 ]);
2212+ snprintf (proc_w_str , sizeof (proc_w_str ), "%d" , comm_r [1 ]);
2213+ char * args [] = {"swaylock-sleep-watcher" , proc_w_str , proc_r_str , NULL };
2214+ if (posix_spawnp (& pid , "swaylock-sleep-watcher" , NULL , NULL , args , environ ) == -1 ) {
2215+ close (comm_r [0 ]);
2216+ close (comm_r [1 ]);
2217+ close (comm_w [0 ]);
2218+ close (comm_w [1 ]);
2219+ swaylock_log (LOG_ERROR , "Failed to run sleep detection helper" );
2220+ return -1 ;
2221+ }
2222+ close (comm_r [1 ]);
2223+ close (comm_w [0 ]);
2224+ * sleep_comm_r = comm_r [0 ];
2225+ * sleep_comm_w = comm_w [1 ];
2226+ return 0 ;
2227+ }
2228+
21062229// Check for --debug 'early' we also apply the correct loglevel
21072230// to the forked child, without having to first proces all of the
21082231// configuration (including from file) before forking and (in the
@@ -2130,6 +2253,15 @@ void log_init(int argc, char **argv) {
21302253}
21312254
21322255int main (int argc , char * * argv ) {
2256+ /* Initially ignore SIGUSR1 + SIGUSR2 to prevent stray signals (like
2257+ * `killall -SIGUSR2`) from affecting the pw backend subprocess */
2258+ struct sigaction sa ;
2259+ sa .sa_handler = SIG_IGN ;
2260+ sigemptyset (& sa .sa_mask );
2261+ sa .sa_flags = 0 ;
2262+ sigaction (SIGUSR1 , & sa , NULL );
2263+ sigaction (SIGUSR2 , & sa , NULL );
2264+
21332265 log_init (argc , argv );
21342266 initialize_pw_backend (argc , argv );
21352267 srand (time (NULL ));
@@ -2156,6 +2288,8 @@ int main(int argc, char **argv) {
21562288 .indicator_idle_visible = false,
21572289 .ready_fd = -1 ,
21582290 .plugin_command = NULL ,
2291+ .grace_time = 0.0f ,
2292+ .grace_pointer_hysteresis = 10.0f ,
21592293 };
21602294 wl_list_init (& state .images );
21612295 set_default_colors (& state .args .colors );
@@ -2214,6 +2348,18 @@ int main(int argc, char **argv) {
22142348 swaylock_log (LOG_ERROR , "Failed to make pipe end nonblocking" );
22152349 return EXIT_FAILURE ;
22162350 }
2351+ if (pipe (sigusr2_fds ) != 0 ) {
2352+ swaylock_log (LOG_ERROR , "Failed to pipe" );
2353+ return EXIT_FAILURE ;
2354+ }
2355+ if (!set_cloexec (sigusr2_fds [0 ]) || !set_cloexec (sigusr2_fds [1 ])) {
2356+ swaylock_log (LOG_ERROR , "Failed to make pipes close-on-exec" );
2357+ return EXIT_FAILURE ;
2358+ }
2359+ if (fcntl (sigusr2_fds [1 ], F_SETFL , O_NONBLOCK ) == -1 ) {
2360+ swaylock_log (LOG_ERROR , "Failed to make pipe end nonblocking" );
2361+ return EXIT_FAILURE ;
2362+ }
22172363
22182364 // temp: make all backgrounds use some sort of plugin command
22192365 if (!state .args .plugin_command ) {
@@ -2340,6 +2486,19 @@ int main(int argc, char **argv) {
23402486 daemonize ();
23412487 }
23422488
2489+ state .sleep_comm_r = -1 ;
2490+ state .sleep_comm_w = -1 ;
2491+ if (state .args .grace_time > 0. ) {
2492+ float delay_ms = ceilf (state .args .grace_time * 1000.f );
2493+ int delay = delay_ms >= (float )INT_MAX ? INT_MAX : (int )delay_ms ;
2494+
2495+ /* Failure of this process just cancels the grace period */
2496+ if (start_sleep_watcher (& state .sleep_comm_r , & state .sleep_comm_w ) == 0 ) {
2497+ state .grace_timer = loop_add_timer (state .eventloop , delay , grace_timeout , & state );
2498+ loop_add_fd (state .eventloop , state .sleep_comm_r , POLLIN , sleep_in , & state );
2499+ }
2500+ }
2501+
23432502 /* fill in dmabuf modifier list if empty and upstream provided dmabuf-feedback */
23442503 if (state .forward .linux_dmabuf && zwp_linux_dmabuf_v1_get_version (state .forward .linux_dmabuf ) >= 4 ) {
23452504 size_t npairs = 0 ;
@@ -2420,13 +2579,18 @@ int main(int argc, char **argv) {
24202579 POLLIN , dispatch_nested , NULL );
24212580
24222581 loop_add_fd (state .eventloop , sigusr_fds [0 ], POLLIN , term_in , NULL );
2582+ loop_add_fd (state .eventloop , sigusr2_fds [0 ], POLLIN , lock_in , NULL );
24232583
2424- struct sigaction sa ;
24252584 sa .sa_handler = do_sigusr ;
24262585 sigemptyset (& sa .sa_mask );
24272586 sa .sa_flags = SA_RESTART ;
24282587 sigaction (SIGUSR1 , & sa , NULL );
24292588
2589+ sa .sa_handler = do_sigusr2 ;
2590+ sigemptyset (& sa .sa_mask );
2591+ sa .sa_flags = SA_RESTART ;
2592+ sigaction (SIGUSR2 , & sa , NULL );
2593+
24302594 // Ignore SIGCHLD, to make child processes be automatically reaped.
24312595 // (This setting is not inherited to child processes.)
24322596 struct sigaction sa2 ;
0 commit comments