diff --git a/config.def.h b/config.def.h index 38560f0..d2a6588 100644 --- a/config.def.h +++ b/config.def.h @@ -3,6 +3,8 @@ static const char *user = ""; static const char *group = ""; +static const int displaytimeout = 5; /* 0 to disable */ + static const char *colorname[NUMCOLS] = { [INIT] = "black", /* after initialization */ [INPUT] = "#005577", /* during input */ diff --git a/config.h b/config.h new file mode 100644 index 0000000..d2a6588 --- /dev/null +++ b/config.h @@ -0,0 +1,36 @@ +/* user and group to drop privileges to */ +/* NOTE: Leave these empty if you want them to be determined automatically. */ +static const char *user = ""; +static const char *group = ""; + +static const int displaytimeout = 5; /* 0 to disable */ + +static const char *colorname[NUMCOLS] = { + [INIT] = "black", /* after initialization */ + [INPUT] = "#005577", /* during input */ + [FAILED] = "#CC3333", /* wrong password */ +}; + +/* treat a cleared input like a wrong password (color) */ +static const int failonclear = 1; + +static const Passthrough passthroughs[] = { + /* Modifier Key */ + { 0, XF86XK_AudioRaiseVolume }, + { 0, XF86XK_AudioLowerVolume }, + { 0, XF86XK_AudioMute }, + { 0, XF86XK_AudioPause }, + { 0, XF86XK_AudioStop }, + { 0, XF86XK_AudioNext }, + { 0, XF86XK_AudioPrev }, + { 0, XF86XK_MonBrightnessUp }, + { 0, XF86XK_MonBrightnessDown }, +}; + +/*static const char *dspoffcmd[] = { "xset", "dpms", "force", "off", NULL };*/ + +static const Key keys[] = { + /* modifier key function argument */ + 0 + /*{ 0, XK_Escape, spawn, {.v = dspoffcmd } }*/ +}; diff --git a/slock.c b/slock.c index abc256e..3f8891a 100644 --- a/slock.c +++ b/slock.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -96,21 +97,90 @@ die(const char *errstr, ...) #include #include -int is_screen_on(Display *dpy) -{ - CARD16 power_level; - BOOL state; +XRRScreenResources *crtcres = 0; +static int crtccount = 0; +static XRRCrtcInfo *crtcinfos[16]; - if (DPMSInfo(dpy, &power_level, &state)) { - if(state) { - if(power_level == DPMSModeOn) - return 1; +void update_crtc_infos(Display *dpy) +{ + if(crtcres) { + /* TODO: Turn on displays here first? */ + int i; + for(i = 0; i < crtccount; i++) { + XRRFreeCrtcInfo(crtcinfos[i]); + } + memset(crtcinfos, 0, sizeof(XRRCrtcInfo *)*16); + XRRFreeScreenResources(crtcres); + crtcres = 0; + crtccount = 0; + } else { + Window root; + int i; + + root = DefaultRootWindow(dpy); + crtcres = XRRGetScreenResources(dpy, root); + + for(i = 0; i < crtcres->ncrtc; i++) { /* TODO: FIX: Choose minimum of ncrtc and my crtcinfos value. + Could it be that the OS disables displays after inactivity? */ + RRCrtc crtc; + XRRCrtcInfo *info; + + crtc = crtcres->crtcs[i]; + info = XRRGetCrtcInfo(dpy, crtcres, crtc); + crtcinfos[i] = info; } } - - return 0; } +// TODO: Error-prone. See which of these can return errors. +void set_screen_on(Display *dpy) +{ + if(crtcres) { + int i; + for(i = 0; i < crtcres->ncrtc; i++) { + RRCrtc crtc; + static XRRCrtcInfo *info; + + crtc = crtcres->crtcs[i]; + info = crtcinfos[i]; + + XRRSetCrtcConfig(dpy, crtcres, crtcres->crtcs[i], CurrentTime, + info->x, info->y, + info->mode, info->rotation, info->outputs, + info->noutput); + } + } +} + +// TODO: Error-prone. See which of these can return errors. +void set_screen_off(Display *dpy) +{ + if(crtcres) { + int i; + for(i = 0; i < crtcres->ncrtc; i++) { + RRCrtc crtc; + static XRRCrtcInfo *info; + + crtc = crtcres->crtcs[i]; + info = crtcinfos[i]; + + if(info->mode != None) { + Status s = + XRRSetCrtcConfig(dpy, crtcres, crtcres->crtcs[i], + CurrentTime, 0, 0, None, RR_Rotate_0, + NULL, 0); + if(s != RRSetConfigSuccess) { + /* TODO: Failed to disable... */ + } + } + } + } +} + +/* TODO: When new CRTCs are connected when the screen is off. Turn on all + * screens and then update the structure. We want the displays on when new + * one connects. */ + static void dontkillme(void) { @@ -180,136 +250,203 @@ readpw(Display *dpy, struct xrandr *rr, struct lock **locks, int nscreens, { XRRScreenChangeNotifyEvent *rre; char buf[32], passwd[256], *inputhash; - int num, screen, running, failure, oldc, i, passing, dspon; + int num, screen, running, failure, oldc, i, passing, dson, xfd; unsigned int len, color; + struct timeval tv; KeySym ksym; XEvent ev; + Bool dpmsavailable; + CARD16 dpmspowerlevel; + BOOL dpmsstate; + CARD16 dpmsstandby, dpmssuspend, dpmsoff; + len = 0; running = 1; failure = 0; oldc = INIT; - dspon = 1; + dson = 1; - while (running && !XNextEvent(dpy, &ev)) { - DPMSForceLevel(dpy, dspon ? DPMSModeOn : DPMSModeOff); + xfd = ConnectionNumber(dpy); - if (ev.type == KeyPress || ev.type == MotionNotify) - dspon = 1; + update_crtc_infos(dpy); - if (ev.type == KeyPress) { - explicit_bzero(&buf, sizeof(buf)); - num = XLookupString(&ev.xkey, buf, sizeof(buf), &ksym, 0); - if (IsKeypadKey(ksym)) { - if (ksym == XK_KP_Enter) - ksym = XK_Return; - else if (ksym >= XK_KP_0 && ksym <= XK_KP_9) - ksym = (ksym - XK_KP_0) + XK_0; - } - if (IsFunctionKey(ksym) || - IsKeypadKey(ksym) || - IsMiscFunctionKey(ksym) || - IsPFKey(ksym) || - IsPrivateKeypadKey(ksym)) - continue; + if(DPMSCapable(dpy) && DPMSInfo(dpy, &dpmspowerlevel, &dpmsstate) && + DPMSGetTimeouts(dpy, &dpmsstandby, &dpmssuspend, &dpmsoff)) + { + dpmsavailable = 1; + if(dpmsstate) + DPMSDisable(dpy); + XSync(dpy, 0); + } - passing = 0; - for (i = 0; i < LENGTH(passthroughs); i++) { - if (ksym == passthroughs[i].keysym && - CLEANMASK(passthroughs[i].mod) == CLEANMASK(ev.xkey.state)) { - passing = 1; - XSendEvent(dpy, DefaultRootWindow(dpy), True, KeyPressMask, &ev); - break; - } - } + /* TODO: Disable the DPMS timeout, save settings, and restore. */ - if(passing) - continue; + while(running) { + fd_set fds; + FD_ZERO(&fds); + FD_SET(xfd, &fds); - for (i = 0; i < LENGTH(keys); i++) { - Key key = keys[i]; + XFlush(dpy); - if (ksym == key.keysym && - CLEANMASK(key.mod) == CLEANMASK(ev.xkey.state)) { - passing = 1; - key.func(&(key.arg)); + while(XPending(dpy)) { + XNextEvent(dpy, &ev); + + if(ev.type == MotionNotify) { + dson = 1; + set_screen_on(dpy); + } else if (ev.type == KeyPress) { + explicit_bzero(&buf, sizeof(buf)); + num = XLookupString(&ev.xkey, buf, sizeof(buf), &ksym, 0); + + if(ksym != XK_Escape) { + dson = 1; + set_screen_on(dpy); } + + if (IsKeypadKey(ksym)) { + if (ksym == XK_KP_Enter) + ksym = XK_Return; + else if (ksym >= XK_KP_0 && ksym <= XK_KP_9) + ksym = (ksym - XK_KP_0) + XK_0; + } + if (IsFunctionKey(ksym) || + IsKeypadKey(ksym) || + IsMiscFunctionKey(ksym) || + IsPFKey(ksym) || + IsPrivateKeypadKey(ksym)) + continue; + + passing = 0; + for (i = 0; i < (int)LENGTH(passthroughs); i++) { /* WARNING: Unsafe cast. */ + if (ksym == passthroughs[i].keysym && + CLEANMASK(passthroughs[i].mod) == CLEANMASK(ev.xkey.state)) { + passing = 1; + XSendEvent(dpy, DefaultRootWindow(dpy), True, KeyPressMask, &ev); + break; + } + } + + if(passing) + continue; + + for (i = 0; i < (int)LENGTH(keys); i++) { /* WARNING: Unsafe cast. */ + Key key = keys[i]; + + if (ksym == key.keysym && + CLEANMASK(key.mod) == CLEANMASK(ev.xkey.state)) { + passing = 1; + key.func(&(key.arg)); + } + } + + if(passing) + continue; + + if(len == 0 && ksym == XK_Escape) { + dson = !dson; + if(dson) + set_screen_on(dpy); + else + set_screen_off(dpy); + continue; + } + + switch (ksym) { + case XK_Return: + passwd[len] = '\0'; + errno = 0; + if (!(inputhash = crypt(passwd, hash))) + fprintf(stderr, "slock: crypt: %s\n", strerror(errno)); + else + running = !!strcmp(inputhash, hash); + if (running) { + XBell(dpy, 100); + failure = 1; + } + explicit_bzero(&passwd, sizeof(passwd)); + len = 0; + break; + case XK_Escape: + explicit_bzero(&passwd, sizeof(passwd)); + len = 0; + break; + case XK_BackSpace: + if (len) + passwd[--len] = '\0'; + break; + default: + if (num && !iscntrl((unsigned char)buf[0]) && + (len + num < sizeof(passwd))) { + memcpy(passwd + len, buf, num); + len += num; + } else if (buf[0] == '\025') { /* ctrl-u clears input */ + explicit_bzero(&passwd, sizeof(passwd)); + len = 0; + } else { + continue; /* Don't trigger fail screen when pressing control characters */ + } + break; + } + color = len ? INPUT : ((failure || failonclear) ? FAILED : INIT); + if (running && oldc != (int)color) { /* WARNING: Unsafe cast. */ + for (screen = 0; screen < nscreens; screen++) { + XSetWindowBackground(dpy, + locks[screen]->win, + locks[screen]->colors[color]); + XClearWindow(dpy, locks[screen]->win); + } + oldc = color; + } + } else if (rr->active && ev.type == rr->evbase + RRScreenChangeNotify) { + rre = (XRRScreenChangeNotifyEvent*)&ev; + for (screen = 0; screen < nscreens; screen++) { + if (locks[screen]->win == rre->window) { + if (rre->rotation == RR_Rotate_90 || + rre->rotation == RR_Rotate_270) + { + XResizeWindow(dpy, locks[screen]->win, + rre->height, rre->width); + } else { + XResizeWindow(dpy, locks[screen]->win, + rre->width, rre->height); + } + XClearWindow(dpy, locks[screen]->win); + break; + } + } + } else if(rr->active && ev.type == rr->evbase + RRNotify) { + /* TODO: Update crts variables and detect if new displays + * are connected. XRRNotifyEvent->subtype, + * XRRCrtcChangeNotifyEvent, XRROutputChangeNotifyEvent, + * RRNotify_OutputProperty*/ + } else { + for(screen = 0; screen < nscreens; screen++) + XRaiseWindow(dpy, locks[screen]->win); } + } - if(passing) - continue; + int ret; + if(displaytimeout > 0 && dson) { + tv.tv_sec = displaytimeout; + tv.tv_usec = 0; + ret = select(xfd + 1, &fds, 0, 0, &tv); + } else { + ret = select(xfd + 1, &fds, 0, 0, 0); + } + if (ret == 0) { + dson = 0; + set_screen_off(dpy); + } else if (ret > 0 && FD_ISSET(xfd, &fds)) { + } + } - if(len == 0 && ksym == XK_Escape) { - dspon = !is_screen_on(dpy); - continue; - } - - switch (ksym) { - case XK_Return: - passwd[len] = '\0'; - errno = 0; - if (!(inputhash = crypt(passwd, hash))) - fprintf(stderr, "slock: crypt: %s\n", strerror(errno)); - else - running = !!strcmp(inputhash, hash); - if (running) { - XBell(dpy, 100); - failure = 1; - } - explicit_bzero(&passwd, sizeof(passwd)); - len = 0; - break; - case XK_Escape: - explicit_bzero(&passwd, sizeof(passwd)); - len = 0; - break; - case XK_BackSpace: - if (len) - passwd[--len] = '\0'; - break; - default: - if (num && !iscntrl((unsigned char)buf[0]) && - (len + num < sizeof(passwd))) { - memcpy(passwd + len, buf, num); - len += num; - } else if (buf[0] == '\025') { /* ctrl-u clears input */ - explicit_bzero(&passwd, sizeof(passwd)); - len = 0; - } else { - continue; /* Don't trigger fail screen when pressing control characters */ - } - break; - } - color = len ? INPUT : ((failure || failonclear) ? FAILED : INIT); - if (running && oldc != color) { - for (screen = 0; screen < nscreens; screen++) { - XSetWindowBackground(dpy, - locks[screen]->win, - locks[screen]->colors[color]); - XClearWindow(dpy, locks[screen]->win); - } - oldc = color; - } - } else if (rr->active && ev.type == rr->evbase + RRScreenChangeNotify) { - rre = (XRRScreenChangeNotifyEvent*)&ev; - for (screen = 0; screen < nscreens; screen++) { - if (locks[screen]->win == rre->window) { - if (rre->rotation == RR_Rotate_90 || - rre->rotation == RR_Rotate_270) - XResizeWindow(dpy, locks[screen]->win, - rre->height, rre->width); - else - XResizeWindow(dpy, locks[screen]->win, - rre->width, rre->height); - XClearWindow(dpy, locks[screen]->win); - break; - } - } - } else { - for (screen = 0; screen < nscreens; screen++) - XRaiseWindow(dpy, locks[screen]->win); - } - } + if(dpmsavailable && dpmsstate) + { + DPMSSetTimeouts(dpy, dpmsstandby, dpmssuspend, dpmsoff); + DPMSEnable(dpy); + XSync(dpy, 0); + } } static struct lock * @@ -349,6 +486,11 @@ lockscreen(Display *dpy, struct xrandr *rr, int screen) &color, &color, 0, 0); XDefineCursor(dpy, lock->win, invisible); + XRRSelectInput(dpy, lock->win, RRScreenChangeNotifyMask | + RRCrtcChangeNotifyMask | + RROutputChangeNotifyMask | + RROutputPropertyNotifyMask); + /* Try to grab mouse pointer *and* keyboard for 600ms, else fail the lock */ for (i = 0, ptgrab = kbgrab = -1; i < 6; i++) { if (ptgrab != GrabSuccess) { @@ -427,8 +569,6 @@ main(int argc, char **argv) { groupname = username; } - printf("%s:%s\n", username, groupname); - /* validate drop-user and -group */ errno = 0; if (!(pwd = getpwnam(username)))