├── .gitignore ├── sxlock.pam ├── Makefile ├── LICENSE ├── README.md └── sxlock.c /.gitignore: -------------------------------------------------------------------------------- 1 | sxlock 2 | *.o 3 | -------------------------------------------------------------------------------- /sxlock.pam: -------------------------------------------------------------------------------- 1 | # 2 | # PAM configuration file for the sxlock screen locker. By default, it includes 3 | # the 'system-auth' configuration file (see /etc/pam.d/system-auth) 4 | # 5 | 6 | auth include system-auth 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # sxlock - simple X screen locker 2 | # © 2013-2021 Jakub Klinkovský 3 | # Based on sflock 4 | # © 2010-2011 Ben Ruijl 5 | # Based on slock 6 | # © 2006-2008 Anselm R. Garbe, Sander van Dijk 7 | 8 | NAME = sxlock 9 | VERSION = 2.0 10 | 11 | CC := $(CC) -std=c99 12 | 13 | base_CFLAGS = -Wall -Wextra -pedantic -O2 -g 14 | base_LIBS = -lpam 15 | 16 | pkgs = x11 xext xrandr xft 17 | pkgs_CFLAGS = $(shell pkg-config --cflags $(pkgs)) 18 | pkgs_LIBS = $(shell pkg-config --libs $(pkgs)) 19 | 20 | CPPFLAGS += -DPROGNAME=\"${NAME}\" -DVERSION=\"${VERSION}\" -D_XOPEN_SOURCE=500 21 | CFLAGS := $(base_CFLAGS) $(pkgs_CFLAGS) $(CFLAGS) 22 | LDLIBS := $(base_LIBS) $(pkgs_LIBS) 23 | 24 | all: sxlock 25 | 26 | sxlock: sxlock.c 27 | 28 | clean: 29 | $(RM) sxlock 30 | 31 | install: sxlock 32 | install -Dm755 sxlock $(DESTDIR)/usr/bin/sxlock 33 | install -Dm644 sxlock.pam $(DESTDIR)/etc/pam.d/sxlock 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT/X Consortium License 2 | 3 | © 2013-2021 Jakub Klinkovský 4 | © 2010-2011 Ben Ruijl 5 | © 2006-2008 Anselm R Garbe 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a 8 | copy of this software and associated documentation files (the "Software"), 9 | to deal in the Software without restriction, including without limitation 10 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 | and/or sell copies of the Software, and to permit persons to whom the 12 | Software is furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | sxlock - simple X screen locker 2 | =============================== 3 | 4 | Simple screen locker utility for X, fork of sflock, which is based on slock. Main difference is that 5 | sxlock uses PAM authentication, so no suid is needed. 6 | 7 | 8 | Features 9 | -------- 10 | 11 | - provides basic user feedback 12 | - uses PAM 13 | - sets DPMS timeout to 10 seconds, before exit restores original settings 14 | - basic RandR support (drawing centered on the primary output) 15 | 16 | 17 | Requirements 18 | ------------ 19 | 20 | - libX11 (Xlib headers) 21 | - libXext (X11 extensions library, for DPMS) 22 | - libXrandr (RandR support) 23 | - libXft 24 | - PAM 25 | 26 | 27 | Installation 28 | ------------ 29 | 30 | Arch Linux users can install this package from the [AUR](https://aur.archlinux.org/packages/sxlock-git/). 31 | 32 | For manual installation just install dependencies, checkout and make: 33 | 34 | git clone https://github.com/lahwaacz/sxlock.git 35 | cd ./sxlock 36 | make 37 | ./sxlock 38 | 39 | 40 | Hooking into systemd events 41 | --------------------------- 42 | 43 | When using [systemd](http://freedesktop.org/wiki/Software/systemd/), you can use the following service (create `/etc/systemd/system/sxlock.service`) to let the system lock your X session on hibernation or suspend: 44 | 45 | ```ini 46 | [Unit] 47 | Description=Lock X session using sxlock 48 | Before=sleep.target 49 | 50 | [Service] 51 | User= 52 | Environment=DISPLAY=:0 53 | ExecStart=/usr/bin/sxlock 54 | 55 | [Install] 56 | WantedBy=sleep.target 57 | ``` 58 | 59 | However, this approach is useful only for single-user systems, because there is no way to know which user is currently logged in. Use [xss-lock](https://bitbucket.org/raymonad/xss-lock) as an alternative for multi-user systems. 60 | -------------------------------------------------------------------------------- /sxlock.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT/X Consortium License 3 | * 4 | * © 2013 Jakub Klinkovský 5 | * © 2010-2011 Ben Ruijl 6 | * © 2006-2008 Anselm R Garbe 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a 9 | * copy of this software and associated documentation files (the "Software"), 10 | * to deal in the Software without restriction, including without limitation 11 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 12 | * and/or sell copies of the Software, and to permit persons to whom the 13 | * Software is furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 24 | * DEALINGS IN THE SOFTWARE. 25 | * 26 | */ 27 | 28 | #include // variable arguments number 29 | #include 30 | #include 31 | #include 32 | #include // isprint() 33 | #include // time() 34 | #include // getopt_long() 35 | #include 36 | #include 37 | #include // mlock() 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #ifdef __GNUC__ 47 | #define UNUSED(x) UNUSED_ ## x __attribute__((__unused__)) 48 | #else 49 | #define UNUSED(x) UNUSED_ ## x 50 | #endif 51 | 52 | #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) 53 | #define MAX(X, Y) (((X) > (Y)) ? (X) : (Y)) 54 | 55 | typedef struct Dpms { 56 | BOOL state; 57 | CARD16 level; // why? 58 | CARD16 standby, suspend, off; 59 | } Dpms; 60 | 61 | typedef struct WindowPositionInfo { 62 | int display_width, display_height; 63 | int output_x, output_y; 64 | int output_width, output_height; 65 | } WindowPositionInfo; 66 | 67 | static int conv_callback(int num_msgs, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr); 68 | 69 | 70 | /* command-line arguments */ 71 | static char* opt_font; 72 | static char* opt_username; 73 | static char* opt_passchar; 74 | static Bool opt_hidelength; 75 | static Bool opt_usedpms; 76 | 77 | /* need globals for signal handling */ 78 | Display *dpy; 79 | Dpms dpms_original = { .state = True, .level = 0, .standby = 600, .suspend = 600, .off = 600 }; // holds original values 80 | int dpms_timeout = 10; // dpms timeout until program exits 81 | Bool using_dpms; 82 | 83 | pam_handle_t *pam_handle; 84 | struct pam_conv conv = { conv_callback, NULL }; 85 | 86 | /* Holds the password you enter */ 87 | static char password[256]; 88 | 89 | 90 | static void 91 | die(const char *errstr, ...) { 92 | va_list ap; 93 | va_start(ap, errstr); 94 | fprintf(stderr, "%s: ", PROGNAME); 95 | vfprintf(stderr, errstr, ap); 96 | va_end(ap); 97 | exit(EXIT_FAILURE); 98 | } 99 | 100 | /* 101 | * Clears the memory which stored the password to be a bit safer against 102 | * cold-boot attacks. 103 | * 104 | */ 105 | static void 106 | clear_password_memory(void) { 107 | /* A volatile pointer to the password buffer to prevent the compiler from 108 | * optimizing this out. */ 109 | volatile char *vpassword = password; 110 | for (unsigned int c = 0; c < sizeof(password); c++) 111 | /* rewrite with random values */ 112 | vpassword[c] = rand(); 113 | } 114 | 115 | /* 116 | * Callback function for PAM. We only react on password request callbacks. 117 | * 118 | */ 119 | static int 120 | conv_callback(int num_msgs, const struct pam_message **msg, struct pam_response **resp, void *UNUSED(appdata_ptr)) { 121 | if (num_msgs == 0) 122 | return PAM_BUF_ERR; 123 | 124 | // PAM expects an array of responses, one for each message 125 | if ((*resp = calloc(num_msgs, sizeof(struct pam_message))) == NULL) 126 | return PAM_BUF_ERR; 127 | 128 | for (int i = 0; i < num_msgs; i++) { 129 | if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF && 130 | msg[i]->msg_style != PAM_PROMPT_ECHO_ON) 131 | continue; 132 | 133 | // return code is currently not used but should be set to zero 134 | resp[i]->resp_retcode = 0; 135 | if ((resp[i]->resp = strdup(password)) == NULL) { 136 | free(*resp); 137 | return PAM_BUF_ERR; 138 | } 139 | } 140 | 141 | return PAM_SUCCESS; 142 | } 143 | 144 | void 145 | handle_signal(int sig) { 146 | /* restore dpms settings */ 147 | if (using_dpms) { 148 | DPMSSetTimeouts(dpy, dpms_original.standby, dpms_original.suspend, dpms_original.off); 149 | if (!dpms_original.state) 150 | DPMSDisable(dpy); 151 | } 152 | 153 | die("Caught signal %d; dying\n", sig); 154 | } 155 | 156 | void 157 | main_loop(Window w, GC gc, XftDraw* xftdraw, XftFont* font, WindowPositionInfo* info, char passdisp[256], char* username, XftColor white, XftColor red, Bool hidelength) { 158 | XEvent event; 159 | KeySym ksym; 160 | 161 | unsigned int len = 0; 162 | Bool running = True; 163 | Bool sleepmode = False; 164 | Bool failed = False; 165 | 166 | XSync(dpy, False); 167 | 168 | /* text properties */ 169 | XGlyphInfo ext_username, ext_pass, ext_authfail; 170 | XftTextExtents8(dpy, font, (XftChar8*) username, strlen(username), &ext_username); 171 | 172 | /* distance of text from the line */ 173 | int line_dist = font->height; 174 | 175 | /* define base coordinates - middle of screen */ 176 | int base_x = info->output_x + info->output_width / 2; 177 | int base_y = info->output_y + info->output_height / 2; /* y-position of the line */ 178 | 179 | /* not changed in the loop */ 180 | int line_width = MIN(info->output_width, MAX(info->output_width / 4, ext_username.width + ext_username.height)); 181 | int line_x_left = base_x - line_width / 2; 182 | int line_x_right = base_x + line_width / 2; 183 | 184 | /* main event loop */ 185 | while(running && !XNextEvent(dpy, &event)) { 186 | if (sleepmode && using_dpms) 187 | DPMSForceLevel(dpy, DPMSModeOff); 188 | 189 | /* update window if no events pending */ 190 | if (!XPending(dpy)) { 191 | /* clear old username */ 192 | XClearArea(dpy, w, info->output_x, font->ascent + font->descent, info->output_width, base_y - line_dist, False); 193 | 194 | /* clear old passdisp */ 195 | XClearArea(dpy, w, info->output_x, base_y + line_dist, info->output_width, font->ascent + font->descent, False); 196 | 197 | /* draw username and line */ 198 | int x = base_x - ext_username.width / 2; 199 | XftDrawString8(xftdraw, &white, font, x, base_y - line_dist, (XftChar8*) username, strlen(username)); 200 | XDrawLine(dpy, w, gc, line_x_left, base_y, line_x_right, base_y); 201 | 202 | /* draw new passdisp or 'auth failed' */ 203 | if (failed) { 204 | char sauthfail[22]= "authentication failed"; 205 | XftTextExtents8(dpy, font, (XftChar8*) sauthfail, strlen(sauthfail), &ext_authfail); 206 | x = base_x - ext_authfail.width / 2; 207 | XftDrawString8(xftdraw, &red, font, x, base_y + font->ascent + line_dist, (XftChar8*) sauthfail, 21); 208 | } else { 209 | int lendisp = len; 210 | if (hidelength && len > 0) 211 | lendisp += (passdisp[len] * len) % 5; 212 | XftTextExtents8(dpy, font, (XftChar8*) passdisp, lendisp % 256, &ext_pass); 213 | x = base_x - ext_pass.width / 2; 214 | XftDrawString8(xftdraw, &white, font, x, base_y + font->ascent + line_dist, (XftChar8*) passdisp, lendisp % 256); 215 | } 216 | } 217 | 218 | if (event.type == MotionNotify) { 219 | sleepmode = False; 220 | failed = False; 221 | } 222 | 223 | if (event.type == KeyPress) { 224 | sleepmode = False; 225 | failed = False; 226 | 227 | char inputChar = 0; 228 | XLookupString(&event.xkey, &inputChar, sizeof(inputChar), &ksym, 0); 229 | 230 | switch (ksym) { 231 | case XK_Return: 232 | case XK_KP_Enter: 233 | password[len] = 0; 234 | if (pam_authenticate(pam_handle, 0) == PAM_SUCCESS) { 235 | clear_password_memory(); 236 | 237 | /* refresh PAM credentials (for example, any kerberos tickets will be updated, 238 | * modules like pam_gnupg will forward the password to a caching agent, etc. */ 239 | pam_setcred(pam_handle, PAM_REFRESH_CRED); 240 | 241 | running = False; 242 | } else { 243 | failed = True; 244 | } 245 | len = 0; 246 | break; 247 | case XK_Escape: 248 | len = 0; 249 | sleepmode = True; 250 | break; 251 | case XK_BackSpace: 252 | if (len) 253 | --len; 254 | break; 255 | default: 256 | if (isprint(inputChar) && (len + sizeof(inputChar) < sizeof password)) { 257 | memcpy(password + len, &inputChar, sizeof(inputChar)); 258 | len += sizeof(inputChar); 259 | } 260 | break; 261 | } 262 | } 263 | } 264 | } 265 | 266 | Bool 267 | parse_options(int argc, char** argv) 268 | { 269 | static struct option opts[] = { 270 | { "font", required_argument, 0, 'f' }, 271 | { "help", no_argument, 0, 'h' }, 272 | { "passchar", required_argument, 0, 'p' }, 273 | { "username", required_argument, 0, 'u' }, 274 | { "hidelength", no_argument, 0, 'l' }, 275 | { "nodpms", no_argument, 0, 'd' }, 276 | { "version", no_argument, 0, 'v' }, 277 | { 0, 0, 0, 0 }, 278 | }; 279 | 280 | for (;;) { 281 | int opt = getopt_long(argc, argv, "f:hp:u:vld", opts, NULL); 282 | if (opt == -1) 283 | break; 284 | 285 | switch (opt) { 286 | case 'f': 287 | opt_font = optarg; 288 | break; 289 | case 'h': 290 | die("usage: "PROGNAME" [-hvd] [-p passchars] [-f font] [-u username]\n" 291 | " -h: show this help page and exit\n" 292 | " -v: show version info and exit\n" 293 | " -l: derange the password length indicator\n" 294 | " -d: do not handle DPMS\n" 295 | " -p passchars: characters used to obfuscate the password\n" 296 | " -f font name (fontconfig pattern string\n" 297 | " -u username: user name to show\n" 298 | ); 299 | break; 300 | case 'p': 301 | if(strlen(optarg) >= 1) { 302 | opt_passchar = optarg; 303 | } 304 | else { 305 | fprintf(stderr, "Warning: -p must be 1 character at least, using the default.\n"); 306 | } 307 | break; 308 | case 'u': 309 | opt_username = optarg; 310 | break; 311 | case 'l': 312 | opt_hidelength = True; 313 | break; 314 | case 'd': 315 | opt_usedpms = False; 316 | break; 317 | case 'v': 318 | die(PROGNAME"-"VERSION", © 2013 Jakub Klinkovský\n"); 319 | break; 320 | default: 321 | return False; 322 | } 323 | } 324 | 325 | return True; 326 | } 327 | 328 | int 329 | main(int argc, char** argv) { 330 | char passdisp[256]; 331 | int screen_num; 332 | WindowPositionInfo info; 333 | 334 | Cursor invisible; 335 | Window root, w; 336 | XColor black; 337 | XftColor red, white; 338 | XftFont* font; 339 | XftDraw* xftdraw; 340 | GC gc; 341 | 342 | /* get username (used for PAM authentication) */ 343 | char* username; 344 | if ((username = getenv("USER")) == NULL) 345 | die("USER environment variable not set, please set it.\n"); 346 | 347 | /* set default values for command-line arguments */ 348 | opt_passchar = "*"; 349 | opt_font = "sans-24"; 350 | opt_username = username; 351 | opt_hidelength = False; 352 | opt_usedpms = True; 353 | 354 | if (!parse_options(argc, argv)) 355 | exit(EXIT_FAILURE); 356 | 357 | /* register signal handler function */ 358 | if (signal (SIGINT, handle_signal) == SIG_IGN) 359 | signal (SIGINT, SIG_IGN); 360 | if (signal (SIGHUP, handle_signal) == SIG_IGN) 361 | signal (SIGHUP, SIG_IGN); 362 | if (signal (SIGTERM, handle_signal) == SIG_IGN) 363 | signal (SIGTERM, SIG_IGN); 364 | 365 | /* fill with password characters */ 366 | for (unsigned int i = 0; i < sizeof(passdisp); i += strlen(opt_passchar)) 367 | for (unsigned int j = 0; j < strlen(opt_passchar) && i + j < sizeof(passdisp); j++) 368 | passdisp[i + j] = opt_passchar[j]; 369 | 370 | /* initialize random number generator */ 371 | srand(time(NULL)); 372 | 373 | if (!(dpy = XOpenDisplay(NULL))) 374 | die("cannot open dpy\n"); 375 | 376 | if (!(font = XftFontOpenName(dpy, DefaultScreen(dpy), opt_font))) 377 | die("error: Xft could not open font %s.\n", opt_font); 378 | 379 | screen_num = DefaultScreen(dpy); 380 | root = DefaultRootWindow(dpy); 381 | 382 | /* get display/output size and position */ 383 | { 384 | XRRScreenResources* screen = NULL; 385 | RROutput output; 386 | XRROutputInfo* output_info = NULL; 387 | XRRCrtcInfo* crtc_info = NULL; 388 | 389 | screen = XRRGetScreenResourcesCurrent (dpy, root); 390 | output = XRRGetOutputPrimary(dpy, root); 391 | 392 | /* When there is no primary output, the return value of XRRGetOutputPrimary 393 | * is undocumented, probably it is 0. Fall back to the first output in this 394 | * case, connected state will be checked later. 395 | */ 396 | if (output == 0) { 397 | output = screen->outputs[0]; 398 | } 399 | output_info = XRRGetOutputInfo(dpy, screen, output); 400 | 401 | /* Iterate through screen->outputs until connected output is found. */ 402 | int i = 0; 403 | while (output_info->connection != RR_Connected || output_info->crtc == 0) { 404 | XRRFreeOutputInfo(output_info); 405 | output_info = XRRGetOutputInfo(dpy, screen, screen->outputs[i]); 406 | fprintf(stderr, "Warning: no primary output detected, trying %s.\n", output_info->name); 407 | if (i == screen->noutput) 408 | die("error: no connected output detected.\n"); 409 | i++; 410 | } 411 | 412 | crtc_info = XRRGetCrtcInfo (dpy, screen, output_info->crtc); 413 | 414 | info.output_x = crtc_info->x; 415 | info.output_y = crtc_info->y; 416 | info.output_width = crtc_info->width; 417 | info.output_height = crtc_info->height; 418 | info.display_width = DisplayWidth(dpy, screen_num); 419 | info.display_height = DisplayHeight(dpy, screen_num); 420 | 421 | XRRFreeScreenResources(screen); 422 | XRRFreeOutputInfo(output_info); 423 | XRRFreeCrtcInfo(crtc_info); 424 | } 425 | 426 | /* allocate colors */ 427 | { 428 | XColor dummy; 429 | Colormap cmap = DefaultColormap(dpy, screen_num); 430 | XAllocNamedColor(dpy, cmap, "black", &black, &dummy); 431 | } 432 | 433 | /* allocate Xft colors */ 434 | { 435 | XRenderColor xrcolor; 436 | 437 | xrcolor.red = 0xffff; 438 | xrcolor.green = 0xffff; 439 | xrcolor.blue = 0xffff; 440 | xrcolor.alpha = 0xffff; 441 | 442 | XftColorAllocValue(dpy, DefaultVisual(dpy, DefaultScreen(dpy)), 443 | DefaultColormap(dpy, DefaultScreen(dpy)), &xrcolor, &white); 444 | 445 | xrcolor.red = 0xffff; 446 | xrcolor.green = 0x0; 447 | xrcolor.blue = 0x0; 448 | xrcolor.alpha = 0xffff; 449 | 450 | XftColorAllocValue(dpy, DefaultVisual(dpy, DefaultScreen(dpy)), 451 | DefaultColormap(dpy, DefaultScreen(dpy)), &xrcolor, &red); 452 | } 453 | 454 | /* create window */ 455 | { 456 | XSetWindowAttributes wa; 457 | wa.override_redirect = 1; 458 | wa.background_pixel = black.pixel; 459 | w = XCreateWindow(dpy, root, 0, 0, info.display_width, info.display_height, 460 | 0, DefaultDepth(dpy, screen_num), CopyFromParent, 461 | DefaultVisual(dpy, screen_num), CWOverrideRedirect | CWBackPixel, &wa); 462 | XMapRaised(dpy, w); 463 | } 464 | 465 | /* define cursor */ 466 | { 467 | char curs[] = {0, 0, 0, 0, 0, 0, 0, 0}; 468 | Pixmap pmap = XCreateBitmapFromData(dpy, w, curs, 8, 8); 469 | invisible = XCreatePixmapCursor(dpy, pmap, pmap, &black, &black, 0, 0); 470 | XDefineCursor(dpy, w, invisible); 471 | XFreePixmap(dpy, pmap); 472 | } 473 | 474 | /* create Graphics Context */ 475 | { 476 | XGCValues values; 477 | gc = XCreateGC(dpy, w, (unsigned long)0, &values); 478 | XSetForeground(dpy, gc, white.pixel); 479 | } 480 | 481 | /* Xft Draw */ 482 | { 483 | xftdraw = XftDrawCreate(dpy, w, DefaultVisual(dpy, DefaultScreen(dpy)), 484 | DefaultColormap(dpy, DefaultScreen(dpy))); 485 | } 486 | 487 | /* grab pointer and keyboard */ 488 | int len = 1000; 489 | while (len-- > 0) { 490 | if (XGrabPointer(dpy, root, False, ButtonPressMask | ButtonReleaseMask | PointerMotionMask, 491 | GrabModeAsync, GrabModeAsync, None, invisible, CurrentTime) == GrabSuccess) 492 | break; 493 | usleep(50); 494 | } 495 | while (len-- > 0) { 496 | if (XGrabKeyboard(dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess) 497 | break; 498 | usleep(50); 499 | } 500 | if (len <= 0) 501 | die("Cannot grab pointer/keyboard\n"); 502 | 503 | /* set up PAM */ 504 | { 505 | int ret = pam_start("sxlock", username, &conv, &pam_handle); 506 | if (ret != PAM_SUCCESS) 507 | die("PAM: %s\n", pam_strerror(pam_handle, ret)); 508 | } 509 | 510 | /* Lock the area where we store the password in memory, we don’t want it to 511 | * be swapped to disk. Since Linux 2.6.9, this does not require any 512 | * privileges, just enough bytes in the RLIMIT_MEMLOCK limit. */ 513 | if (mlock(password, sizeof(password)) != 0) 514 | die("Could not lock page in memory, check RLIMIT_MEMLOCK\n"); 515 | 516 | /* handle dpms */ 517 | using_dpms = opt_usedpms && DPMSCapable(dpy); 518 | if (using_dpms) { 519 | /* save dpms timeouts to restore on exit */ 520 | DPMSGetTimeouts(dpy, &dpms_original.standby, &dpms_original.suspend, &dpms_original.off); 521 | DPMSInfo(dpy, &dpms_original.level, &dpms_original.state); 522 | 523 | /* set program specific dpms timeouts */ 524 | DPMSSetTimeouts(dpy, dpms_timeout, dpms_timeout, dpms_timeout); 525 | 526 | /* force dpms enabled until exit */ 527 | DPMSEnable(dpy); 528 | } 529 | 530 | /* run main loop */ 531 | main_loop(w, gc, xftdraw, font, &info, passdisp, opt_username, white, red, opt_hidelength); 532 | 533 | /* restore dpms settings */ 534 | if (using_dpms) { 535 | DPMSSetTimeouts(dpy, dpms_original.standby, dpms_original.suspend, dpms_original.off); 536 | if (!dpms_original.state) 537 | DPMSDisable(dpy); 538 | } 539 | 540 | /* clean up PAM handle */ 541 | pam_end(pam_handle, PAM_SUCCESS); 542 | 543 | XUngrabPointer(dpy, CurrentTime); 544 | XftFontClose(dpy, font); 545 | XftDrawDestroy(xftdraw); 546 | XftColorFree(dpy, DefaultVisual(dpy, DefaultScreen(dpy)), 547 | DefaultColormap(dpy, DefaultScreen(dpy)), &white); 548 | XftColorFree(dpy, DefaultVisual(dpy, DefaultScreen(dpy)), 549 | DefaultColormap(dpy, DefaultScreen(dpy)), &red); 550 | XFreeGC(dpy, gc); 551 | XDestroyWindow(dpy, w); 552 | XCloseDisplay(dpy); 553 | return 0; 554 | } 555 | --------------------------------------------------------------------------------