+/*
+ * This callback is only a dummy, see xcb_prepare_cb and xcb_check_cb.
+ * See also man libev(3): "ev_prepare" and "ev_check" - customise your event loop
+ *
+ */
+static void xcb_got_event(EV_P_ struct ev_io *w, int revents) {
+ /* empty, because xcb_prepare_cb and xcb_check_cb are used */
+}
+
+/*
+ * Flush before blocking (and waiting for new events)
+ *
+ */
+static void xcb_prepare_cb(EV_P_ ev_prepare *w, int revents) {
+ xcb_flush(conn);
+}
+
+/*
+ * Instead of polling the X connection socket we leave this to
+ * xcb_poll_for_event() which knows better than we can ever know.
+ *
+ */
+static void xcb_check_cb(EV_P_ ev_check *w, int revents) {
+ xcb_generic_event_t *event;
+
+ while ((event = xcb_poll_for_event(conn)) != NULL) {
+ if (event->response_type == 0) {
+ xcb_generic_error_t *error = (xcb_generic_error_t*)event;
+ if (debug_mode)
+ fprintf(stderr, "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);
+ switch (type) {
+ case XCB_KEY_PRESS:
+ handle_key_press((xcb_key_press_event_t*)event);
+ break;
+
+ case XCB_KEY_RELEASE:
+ handle_key_release((xcb_key_release_event_t*)event);
+
+ /* If this was the backspace or escape key we are back at an
+ * empty input, so turn off the screen if DPMS is enabled */
+ if (input_position == 0)
+ turn_monitors_off();
+
+ break;
+
+ case XCB_VISIBILITY_NOTIFY:
+ handle_visibility_notify(conn, (xcb_visibility_notify_event_t*)event);
+ break;
+
+ case XCB_MAP_NOTIFY:
+ if (!dont_fork) {
+ /* After the first MapNotify, we never fork again. We don’t
+ * expect to get another MapNotify, but better be sure… */
+ dont_fork = true;
+
+ /* In the parent process, we exit */
+ if (fork() != 0)
+ exit(0);
+
+ ev_loop_fork(EV_DEFAULT);
+ }
+ break;
+
+ case XCB_MAPPING_NOTIFY:
+ handle_mapping_notify((xcb_mapping_notify_event_t*)event);
+ break;
+
+ case XCB_CONFIGURE_NOTIFY:
+ handle_screen_resize();
+ break;
+ }
+
+ free(event);
+ }
+}
+
+/*
+ * 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);
+ }
+}
+