/* * Called when the properties on the root window change, e.g. when the screen * resolution changes. If so we update the window to cover the whole screen * and also redraw the image, if any. * */ void handle_screen_resize(void) { xcb_get_geometry_cookie_t geomc; xcb_get_geometry_reply_t *geom; geomc = xcb_get_geometry(conn, screen->root); if ((geom = xcb_get_geometry_reply(conn, geomc, 0)) == NULL) return; if (last_resolution[0] == geom->width && last_resolution[1] == geom->height) { free(geom); return; } last_resolution[0] = geom->width; last_resolution[1] = geom->height; free(geom); redraw_screen(); uint32_t mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; xcb_configure_window(conn, win, mask, last_resolution); xcb_flush(conn); xinerama_query_screens(); redraw_screen(); }
int main(int argc, char *argv[]) { struct passwd *pw; char *username; char *image_path = NULL; int ret; struct pam_conv conv = {conv_callback, NULL}; int curs_choice = CURS_NONE; int o; int optind = 0; struct option longopts[] = { {"version", no_argument, NULL, 'v'}, {"nofork", no_argument, NULL, 'n'}, {"beep", no_argument, NULL, 'b'}, {"dpms", no_argument, NULL, 'd'}, {"color", required_argument, NULL, 'c'}, {"pointer", required_argument, NULL, 'p'}, {"debug", no_argument, NULL, 0}, {"help", no_argument, NULL, 'h'}, {"no-unlock-indicator", no_argument, NULL, 'u'}, {"image", required_argument, NULL, 'i'}, {"tiling", no_argument, NULL, 't'}, {"ignore-empty-password", no_argument, NULL, 'e'}, {"inactivity-timeout", required_argument, NULL, 'I'}, {"show-failed-attempts", no_argument, NULL, 'f'}, {NULL, no_argument, NULL, 0} }; if ((pw = getpwuid(getuid())) == NULL) err(EXIT_FAILURE, "getpwuid() failed"); if ((username = pw->pw_name) == NULL) errx(EXIT_FAILURE, "pw->pw_name is NULL.\n"); char *optstring = "hvnbdc:p:ui:teI:f"; while ((o = getopt_long(argc, argv, optstring, longopts, &optind)) != -1) { switch (o) { case 'v': errx(EXIT_SUCCESS, "version " VERSION " © 2010 Michael Stapelberg"); case 'n': dont_fork = true; break; case 'b': beep = true; break; case 'd': fprintf(stderr, "DPMS support has been removed from i3lock. Please see the manpage i3lock(1).\n"); break; case 'I': { int time = 0; if (sscanf(optarg, "%d", &time) != 1 || time < 0) errx(EXIT_FAILURE, "invalid timeout, it must be a positive integer\n"); inactivity_timeout = time; break; } case 'c': { char *arg = optarg; /* Skip # if present */ if (arg[0] == '#') arg++; if (strlen(arg) != 6 || sscanf(arg, "%06[0-9a-fA-F]", color) != 1) errx(EXIT_FAILURE, "color is invalid, it must be given in 3-byte hexadecimal format: rrggbb\n"); break; } case 'u': unlock_indicator = false; break; case 'i': image_path = strdup(optarg); break; case 't': tile = true; break; case 'p': if (!strcmp(optarg, "win")) { curs_choice = CURS_WIN; } else if (!strcmp(optarg, "default")) { curs_choice = CURS_DEFAULT; } else { errx(EXIT_FAILURE, "i3lock: Invalid pointer type given. Expected one of \"win\" or \"default\".\n"); } break; case 'e': ignore_empty_password = true; break; case 0: if (strcmp(longopts[optind].name, "debug") == 0) debug_mode = true; break; case 'f': show_failed_attempts = true; break; default: errx(EXIT_FAILURE, "Syntax: i3lock [-v] [-n] [-b] [-d] [-c color] [-u] [-p win|default]" " [-i image.png] [-t] [-e] [-I timeout] [-f]"); } } /* We need (relatively) random numbers for highlighting a random part of * the unlock indicator upon keypresses. */ srand(time(NULL)); /* Initialize PAM */ ret = pam_start("i3lock", username, &conv, &pam_handle); if (ret != PAM_SUCCESS) errx(EXIT_FAILURE, "PAM: %s", pam_strerror(pam_handle, ret)); /* Using mlock() as non-super-user seems only possible in Linux. Users of other * operating systems should use encrypted swap/no swap (or remove the ifdef and * run i3lock as super-user). */ #if defined(__linux__) /* Lock the area where we store the password in memory, we don’t want it to * be swapped to disk. Since Linux 2.6.9, this does not require any * privileges, just enough bytes in the RLIMIT_MEMLOCK limit. */ if (mlock(password, sizeof(password)) != 0) err(EXIT_FAILURE, "Could not lock page in memory, check RLIMIT_MEMLOCK"); #endif /* Double checking that connection is good and operatable with xcb */ int screennr; if ((conn = xcb_connect(NULL, &screennr)) == NULL || xcb_connection_has_error(conn)) errx(EXIT_FAILURE, "Could not connect to X11, maybe you need to set DISPLAY?"); if (xkb_x11_setup_xkb_extension(conn, XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION, 0, NULL, NULL, &xkb_base_event, &xkb_base_error) != 1) errx(EXIT_FAILURE, "Could not setup XKB extension."); static const xcb_xkb_map_part_t required_map_parts = (XCB_XKB_MAP_PART_KEY_TYPES | XCB_XKB_MAP_PART_KEY_SYMS | XCB_XKB_MAP_PART_MODIFIER_MAP | XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS | XCB_XKB_MAP_PART_KEY_ACTIONS | XCB_XKB_MAP_PART_VIRTUAL_MODS | XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP); static const xcb_xkb_event_type_t required_events = (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY | XCB_XKB_EVENT_TYPE_STATE_NOTIFY); xcb_xkb_select_events( conn, xkb_x11_get_core_keyboard_device_id(conn), required_events, 0, required_events, required_map_parts, required_map_parts, 0); /* When we cannot initially load the keymap, we better exit */ if (!load_keymap()) errx(EXIT_FAILURE, "Could not load keymap"); const char *locale = getenv("LC_ALL"); if (!locale) locale = getenv("LC_CTYPE"); if (!locale) locale = getenv("LANG"); if (!locale) { if (debug_mode) fprintf(stderr, "Can't detect your locale, fallback to C\n"); locale = "C"; } load_compose_table(locale); xinerama_init(); xinerama_query_screens(); screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data; last_resolution[0] = screen->width_in_pixels; last_resolution[1] = screen->height_in_pixels; xcb_change_window_attributes(conn, screen->root, XCB_CW_EVENT_MASK, (uint32_t[]) { XCB_EVENT_MASK_STRUCTURE_NOTIFY }); if (image_path) { /* Create a pixmap to render on, fill it with the background color */ img = cairo_image_surface_create_from_png(image_path); /* In case loading failed, we just pretend no -i was specified. */ if (cairo_surface_status(img) != CAIRO_STATUS_SUCCESS) { fprintf(stderr, "Could not load image \"%s\": %s\n", image_path, cairo_status_to_string(cairo_surface_status(img))); img = NULL; } } /* Pixmap on which the image is rendered to (if any) */ xcb_pixmap_t bg_pixmap = draw_image(last_resolution); /* open the fullscreen window, already with the correct pixmap in place */ 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); /* Load the keymap again to sync the current modifier state. Since we first * loaded the keymap, there might have been changes, but starting from now, * we should get all key presses/releases due to having grabbed the * keyboard. */ (void)load_keymap(); /* Initialize the libev event loop. */ main_loop = EV_DEFAULT; if (main_loop == NULL) errx(EXIT_FAILURE, "Could not initialize libev. Bad LIBEV_FLAGS?\n"); struct ev_io *xcb_watcher = calloc(sizeof(struct ev_io), 1); struct ev_check *xcb_check = calloc(sizeof(struct ev_check), 1); struct ev_prepare *xcb_prepare = calloc(sizeof(struct ev_prepare), 1); ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ); ev_io_start(main_loop, xcb_watcher); ev_check_init(xcb_check, xcb_check_cb); ev_check_start(main_loop, xcb_check); ev_prepare_init(xcb_prepare, xcb_prepare_cb); ev_prepare_start(main_loop, xcb_prepare); /* Invoke the event callback once to catch all the events which were * received up until now. ev will only pick up new events (when the X11 * file descriptor becomes readable). */ ev_invoke(main_loop, xcb_check, 0); ev_loop(main_loop, 0); }
int main(int argc, char *argv[]) { setlocale(LC_TIME, "ru_RU.UTF-8"); char *username; char *image_path = NULL; int ret; struct pam_conv conv = {conv_callback, NULL}; int curs_choice = CURS_NONE; int o; int optind = 0; struct option longopts[] = { {"version", no_argument, NULL, 'v'}, {"nofork", no_argument, NULL, 'n'}, {"beep", no_argument, NULL, 'b'}, {"dpms", no_argument, NULL, 'd'}, {"color", required_argument, NULL, 'c'}, {"pointer", required_argument, NULL , 'p'}, {"debug", no_argument, NULL, 0}, {"help", no_argument, NULL, 'h'}, {"no-unlock-indicator", no_argument, NULL, 'u'}, {"image", required_argument, NULL, 'i'}, {"tiling", no_argument, NULL, 't'}, {"auth-fail-command", required_argument, NULL, 'f'}, {"auth-done-command", required_argument, NULL, 'o'}, {NULL, no_argument, NULL, 0} }; if ((username = getenv("USER")) == NULL) errx(1, "USER environment variable not set, please set it.\n"); while ((o = getopt_long(argc, argv, "hvnbdc:p:ui:f:o:t", longopts, &optind)) != -1) { switch (o) { case 'v': errx(EXIT_SUCCESS, "version " VERSION " © 2010-2012 Michael Stapelberg"); case 'n': dont_fork = true; break; case 'b': beep = true; break; case 'd': dpms = true; break; case 'c': { char *arg = optarg; /* Skip # if present */ if (arg[0] == '#') arg++; if (strlen(arg) != 6 || sscanf(arg, "%06[0-9a-fA-F]", color) != 1) errx(1, "color is invalid, it must be given in 3-byte hexadecimal format: rrggbb\n"); break; } case 'u': unlock_indicator = false; break; case 'i': image_path = strdup(optarg); break; case 't': tile = true; break; case 'p': if (!strcmp(optarg, "win")) { curs_choice = CURS_WIN; } else if (!strcmp(optarg, "default")) { curs_choice = CURS_DEFAULT; } else { errx(1, "i3lock: Invalid pointer type given. Expected one of \"win\" or \"default\".\n"); } break; case 'f': shell_auth_fail_command = strdup(optarg); break; case 'o': shell_auth_done_command = strdup(optarg); break; case 'h': show_time = false; break; case 0: if (strcmp(longopts[optind].name, "debug") == 0) debug_mode = true; break; default: errx(1, "Syntax: i3lock [-v] [-n] [-b] [-d] [-c color] [-u] [-p win|default] [-h]" " [-i image.png] [-t] [-f command] [-o command]" ); } } /* We need (relatively) random numbers for highlighting a random part of * the unlock indicator upon keypresses. */ srand(time(NULL)); /* Initialize PAM */ ret = pam_start("i3lock", username, &conv, &pam_handle); if (ret != PAM_SUCCESS) errx(EXIT_FAILURE, "PAM: %s", pam_strerror(pam_handle, ret)); /* Using mlock() as non-super-user seems only possible in Linux. Users of other * operating systems should use encrypted swap/no swap (or remove the ifdef and * run i3lock as super-user). */ #if defined(__linux__) /* Lock the area where we store the password in memory, we don’t want it to * be swapped to disk. Since Linux 2.6.9, this does not require any * privileges, just enough bytes in the RLIMIT_MEMLOCK limit. */ if (mlock(password, sizeof(password)) != 0) err(EXIT_FAILURE, "Could not lock page in memory, check RLIMIT_MEMLOCK"); #endif /* Initialize connection to X11 */ if ((display = XOpenDisplay(NULL)) == NULL) errx(EXIT_FAILURE, "Could not connect to X11, maybe you need to set DISPLAY?"); XSetEventQueueOwner(display, XCBOwnsEventQueue); conn = XGetXCBConnection(display); /* Double checking that connection is good and operatable with xcb */ if (xcb_connection_has_error(conn)) errx(EXIT_FAILURE, "Could not connect to X11, maybe you need to set DISPLAY?"); /* When we cannot initially load the keymap, we better exit */ if (!load_keymap()) errx(EXIT_FAILURE, "Could not load keymap"); xinerama_init(); xinerama_query_screens(); /* if DPMS is enabled, check if the X server really supports it */ if (dpms) { xcb_dpms_capable_cookie_t dpmsc = xcb_dpms_capable(conn); xcb_dpms_capable_reply_t *dpmsr; if ((dpmsr = xcb_dpms_capable_reply(conn, dpmsc, NULL))) { if (!dpmsr->capable) { if (debug_mode) fprintf(stderr, "Disabling DPMS, X server not DPMS capable\n"); dpms = false; } free(dpmsr); } } screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data; last_resolution[0] = screen->width_in_pixels; last_resolution[1] = screen->height_in_pixels; xcb_change_window_attributes(conn, screen->root, XCB_CW_EVENT_MASK, (uint32_t[]){ XCB_EVENT_MASK_STRUCTURE_NOTIFY }); if (image_path) { /* Create a pixmap to render on, fill it with the background color */ img = cairo_image_surface_create_from_png(image_path); /* In case loading failed, we just pretend no -i was specified. */ if (cairo_surface_status(img) != CAIRO_STATUS_SUCCESS) { fprintf(stderr, "Could not load image \"%s\": cairo surface status %d\n", image_path, cairo_surface_status(img)); img = NULL; } } /* Pixmap on which the image is rendered to (if any) */ xcb_pixmap_t bg_pixmap = draw_image(last_resolution); /* open the fullscreen window, already with the correct pixmap in place */ win = open_fullscreen_window(conn, screen, color, bg_pixmap); xcb_free_pixmap(conn, bg_pixmap); cursor = create_cursor(conn, screen, win, curs_choice); grab_pointer_and_keyboard(conn, screen, cursor); if (dpms) dpms_turn_off_screen(conn); /* Initialize the libev event loop. */ main_loop = EV_DEFAULT; if (main_loop == NULL) errx(EXIT_FAILURE, "Could not initialize libev. Bad LIBEV_FLAGS?\n"); struct ev_io *xcb_watcher = calloc(sizeof(struct ev_io), 1); struct ev_check *xcb_check = calloc(sizeof(struct ev_check), 1); struct ev_prepare *xcb_prepare = calloc(sizeof(struct ev_prepare), 1); ev_io_init(xcb_watcher, xcb_got_event, xcb_get_file_descriptor(conn), EV_READ); ev_io_start(main_loop, xcb_watcher); ev_check_init(xcb_check, xcb_check_cb); ev_check_start(main_loop, xcb_check); ev_prepare_init(xcb_prepare, xcb_prepare_cb); ev_prepare_start(main_loop, xcb_prepare); /* Invoke the event callback once to catch all the events which were * received up until now. ev will only pick up new events (when the X11 * file descriptor becomes readable). */ ev_invoke(main_loop, xcb_check, 0); if(show_time) start_time_redraw_tick(); ev_loop(main_loop, 0); }