diff --git a/i3lock.c b/i3lock.c index 74e15f2..533a28d 100644 --- a/i3lock.c +++ b/i3lock.c @@ -364,7 +364,8 @@ static void handle_key_press(xcb_key_press_event_t *event) { * hiding us) gets hidden. * */ -static void handle_visibility_notify(xcb_visibility_notify_event_t *event) { +static void handle_visibility_notify(xcb_connection_t *conn, + xcb_visibility_notify_event_t *event) { if (event->state != XCB_VISIBILITY_UNOBSCURED) { uint32_t values[] = { XCB_STACK_MODE_ABOVE }; xcb_configure_window(conn, event->window, XCB_CONFIG_WINDOW_STACK_MODE, values); @@ -501,7 +502,7 @@ static void xcb_check_cb(EV_P_ ev_check *w, int revents) { break; case XCB_VISIBILITY_NOTIFY: - handle_visibility_notify((xcb_visibility_notify_event_t*)event); + handle_visibility_notify(conn, (xcb_visibility_notify_event_t*)event); break; case XCB_MAP_NOTIFY: @@ -531,6 +532,63 @@ static void xcb_check_cb(EV_P_ ev_check *w, int revents) { } } +/* + * This function is called from a fork()ed child and will raise the i3lock + * window when the window is obscured, even when the main i3lock process is + * blocked due to PAM. + * + */ +static void raise_loop(xcb_window_t window) { + xcb_connection_t *conn; + xcb_generic_event_t *event; + int screens; + + if ((conn = xcb_connect(NULL, &screens)) == NULL || + xcb_connection_has_error(conn)) + errx(EXIT_FAILURE, "Cannot open display\n"); + + /* We need to know about the window being obscured or getting destroyed. */ + xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, + (uint32_t[]){ + XCB_EVENT_MASK_VISIBILITY_CHANGE | + XCB_EVENT_MASK_STRUCTURE_NOTIFY + }); + xcb_flush(conn); + + DEBUG("Watching window 0x%08x\n", window); + while ((event = xcb_wait_for_event(conn)) != NULL) { + if (event->response_type == 0) { + xcb_generic_error_t *error = (xcb_generic_error_t*)event; + DEBUG("X11 Error received! sequence 0x%x, error_code = %d\n", + error->sequence, error->error_code); + free(event); + continue; + } + /* Strip off the highest bit (set if the event is generated) */ + int type = (event->response_type & 0x7F); + DEBUG("Read event of type %d\n", type); + switch (type) { + case XCB_VISIBILITY_NOTIFY: + handle_visibility_notify(conn, (xcb_visibility_notify_event_t*)event); + break; + case XCB_UNMAP_NOTIFY: + DEBUG("UnmapNotify for 0x%08x\n", (((xcb_unmap_notify_event_t*)event)->window)); + if (((xcb_unmap_notify_event_t*)event)->window == window) + exit(EXIT_SUCCESS); + break; + case XCB_DESTROY_NOTIFY: + DEBUG("DestroyNotify for 0x%08x\n", (((xcb_destroy_notify_event_t*)event)->window)); + if (((xcb_destroy_notify_event_t*)event)->window == window) + exit(EXIT_SUCCESS); + break; + default: + DEBUG("Unhandled event type %d\n", type); + break; + } + free(event); + } +} + int main(int argc, char *argv[]) { char *username; char *image_path = NULL; @@ -692,6 +750,17 @@ int main(int argc, char *argv[]) { win = open_fullscreen_window(conn, screen, color, bg_pixmap); xcb_free_pixmap(conn, bg_pixmap); + pid_t pid = fork(); + /* The pid == -1 case is intentionally ignored here: + * While the child process is useful for preventing other windows from + * popping up while i3lock blocks, it is not critical. */ + if (pid == 0) { + /* Child */ + close(xcb_get_file_descriptor(conn)); + raise_loop(win); + exit(EXIT_SUCCESS); + } + cursor = create_cursor(conn, screen, win, curs_choice); grab_pointer_and_keyboard(conn, screen, cursor);