]> git.sur5r.net Git - i3/i3lock/blob - i3lock.c
Handle error codes when loading the pixmap
[i3/i3lock] / i3lock.c
1 /*
2  * vim:ts=8:expandtab
3  *
4  * i3lock - an improved version of slock
5  *
6  * i3lock © 2009 Michael Stapelberg and contributors
7  * slock  © 2006-2008 Anselm R Garbe
8  *
9  * See file LICENSE for license information.
10  *
11  * Note that on any error (calloc is out of memory for example)
12  * we do not do anything so that the user can fix the error by
13  * himself (kill X to get more free memory or stop some other
14  * program using SSH/console).
15  *
16  */
17 #define _XOPEN_SOURCE 500
18
19 #include <ctype.h>
20 #include <stdarg.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <X11/keysym.h>
27 #include <X11/Xlib.h>
28 #include <X11/Xutil.h>
29 #include <X11/xpm.h>
30 #include <X11/extensions/dpms.h>
31 #include <stdbool.h>
32 #include <getopt.h>
33
34 #include <security/pam_appl.h>
35
36 static char passwd[256];
37
38 static void die(const char *errstr, ...) {
39         va_list ap;
40
41         va_start(ap, errstr);
42         vfprintf(stderr, errstr, ap);
43         va_end(ap);
44         exit(EXIT_FAILURE);
45 }
46
47 /*
48  * Check if given file can be opened => exists
49  *
50  */
51 bool file_exists(const char *filename)
52 {
53         FILE * file = fopen(filename, "r");
54         if(file)
55         {
56                 fclose(file);
57                 return true;
58         }
59         return false;
60 }
61
62 /*
63  * Puts the given XPM error code to stderr
64  *
65  */
66 void print_xpm_error(int err)
67 {
68         switch (err) {
69                 case XpmColorError:
70                         fprintf(stderr, "XPM: Could not parse or alloc requested color\n");
71                         break;
72                 case XpmOpenFailed:
73                         fprintf(stderr, "XPM: Cannot open file\n");
74                         break;
75                 case XpmFileInvalid:
76                         fprintf(stderr, "XPM: invalid XPM file\n");
77                         break;
78                 case XpmNoMemory:
79                         fprintf(stderr, "XPM: Not enough memory\n");
80                         break;
81                 case XpmColorFailed:
82                         fprintf(stderr, "XPM: Color not found\n");
83                         break;
84         }
85 }
86
87
88 /*
89  * Callback function for PAM. We only react on password request callbacks.
90  *
91  */
92 static int conv_callback(int num_msg, const struct pam_message **msg,
93                          struct pam_response **resp, void *appdata_ptr) {
94         if (num_msg == 0)
95                 return 1;
96
97         /* PAM expects an arry of responses, one for each message */
98         if ((*resp = calloc(num_msg, sizeof(struct pam_message))) == NULL) {
99                 perror("calloc");
100                 return 1;
101         }
102
103         for (int c = 0; c < num_msg; c++) {
104                 if (msg[c]->msg_style != PAM_PROMPT_ECHO_OFF &&
105                     msg[c]->msg_style != PAM_PROMPT_ECHO_ON)
106                         continue;
107
108                 /* return code is currently not used but should be set to zero */
109                 resp[c]->resp_retcode = 0;
110                 if ((resp[c]->resp = strdup(passwd)) == NULL) {
111                         perror("strdup");
112                         return 1;
113                 }
114         }
115
116         return 0;
117 }
118
119 int main(int argc, char *argv[]) {
120         char curs[] = {0, 0, 0, 0, 0, 0, 0, 0};
121         char buf[32];
122         char *username;
123         int num, screen;
124
125         unsigned int len;
126         bool running = true;
127
128         /* By default, fork, don’t beep and don’t turn off monitor */
129         bool dont_fork = false;
130         bool beep = false;
131         bool dpms = false;
132         bool xpm_image = false;
133         char xpm_image_path[256];
134         Cursor invisible;
135         Display *dpy;
136         KeySym ksym;
137         Pixmap pmap;
138         Window root, w;
139         XColor black, dummy;
140         XEvent ev;
141         XSetWindowAttributes wa;
142
143         pam_handle_t *handle;
144         struct pam_conv conv = {conv_callback, NULL};
145
146         char opt;
147         int optind = 0;
148         static struct option long_options[] = {
149                 {"version", no_argument, NULL, 'v'},
150                 {"nofork", no_argument, NULL, 'n'},
151                 {"beep", no_argument, NULL, 'b'},
152                 {"dpms", no_argument, NULL, 'd'},
153                 {"image", required_argument, NULL, 'i'},
154                 {NULL, no_argument, NULL, 0}
155         };
156
157         while ((opt = getopt_long(argc, argv, "vnbdi:", long_options, &optind)) != -1) {
158                 switch (opt) {
159                         case 'v':
160                                 die("i3lock-"VERSION", © 2009 Michael Stapelberg\n"
161                                     "based on slock, which is © 2006-2008 Anselm R Garbe\n");
162                         case 'n':
163                                 dont_fork = true;
164                                 break;
165                         case 'b':
166                                 beep = true;
167                                 break;
168                         case 'd':
169                                 dpms = true;
170                                 break;
171                         case 'i':
172                                 strncpy(xpm_image_path, optarg, 255);
173                                 xpm_image = true;
174                                 break;
175                         default:
176                                 die("i3lock: Unknown option. Syntax: i3lock [-v] [-n] [-b] [-d] [-i image.xpm]\n");
177                 }
178         }
179
180         if ((username = getenv("USER")) == NULL)
181                 die("USER environment variable not set, please set it.\n");
182
183         int ret = pam_start("i3lock", username, &conv, &handle);
184         if (ret != PAM_SUCCESS)
185                 die("PAM: %s\n", pam_strerror(handle, ret));
186
187         if(!(dpy = XOpenDisplay(0)))
188                 die("i3lock: cannot open display\n");
189         screen = DefaultScreen(dpy);
190         root = RootWindow(dpy, screen);
191
192         if (!dont_fork) {
193                 if (fork() != 0)
194                         return 0;
195         }
196
197         /* init */
198         wa.override_redirect = 1;
199         wa.background_pixel = WhitePixel(dpy, screen);
200         w = XCreateWindow(dpy, root, 0, 0, DisplayWidth(dpy, screen), DisplayHeight(dpy, screen),
201                         0, DefaultDepth(dpy, screen), CopyFromParent,
202                         DefaultVisual(dpy, screen), CWOverrideRedirect | CWBackPixel, &wa);
203         XAllocNamedColor(dpy, DefaultColormap(dpy, screen), "black", &black, &dummy);
204         pmap = XCreateBitmapFromData(dpy, w, curs, 8, 8);
205         invisible = XCreatePixmapCursor(dpy, pmap, pmap, &black, &black, 0, 0);
206         XDefineCursor(dpy, w, invisible);
207         XMapRaised(dpy, w);
208
209         if(xpm_image && file_exists(xpm_image_path))
210         {
211                 GC gc = XDefaultGC(dpy, 0);
212                 int depth = DefaultDepth(dpy, screen);
213                 int disp_width = DisplayWidth(dpy, screen);
214                 int disp_height = DisplayHeight(dpy, screen);
215                 Pixmap pix = XCreatePixmap(dpy, w, disp_width, disp_height, depth);
216                 int err = XpmReadFileToPixmap(dpy, w, xpm_image_path, &pix, 0, 0);
217                 if (err != 0) {
218                         print_xpm_error(err);
219                         return 1;
220                 }
221                 XCopyArea(dpy, pix, w, gc, 0, 0, disp_width, disp_height, 0, 0);
222         }
223
224         for(len = 1000; len; len--) {
225                 if(XGrabPointer(dpy, root, False, ButtonPressMask | ButtonReleaseMask,
226                         GrabModeAsync, GrabModeAsync, None, invisible, CurrentTime) == GrabSuccess)
227                         break;
228                 usleep(1000);
229         }
230         if((running = running && (len > 0))) {
231                 for(len = 1000; len; len--) {
232                         if(XGrabKeyboard(dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime)
233                                 == GrabSuccess)
234                                 break;
235                         usleep(1000);
236                 }
237                 running = (len > 0);
238         }
239         len = 0;
240         XSync(dpy, False);
241
242         /* main event loop */
243         while(running && !XNextEvent(dpy, &ev)) {
244                 if (len == 0 && dpms && DPMSCapable(dpy)) {
245                         DPMSEnable(dpy);
246                         DPMSForceLevel(dpy, DPMSModeOff);
247                 }
248
249                 if(ev.type != KeyPress)
250                         continue;
251
252                 buf[0] = 0;
253                 num = XLookupString(&ev.xkey, buf, sizeof buf, &ksym, 0);
254                 if(IsKeypadKey(ksym)) {
255                         if(ksym == XK_KP_Enter)
256                                 ksym = XK_Return;
257                         else if(ksym >= XK_KP_0 && ksym <= XK_KP_9)
258                                 ksym = (ksym - XK_KP_0) + XK_0;
259                 }
260                 if(IsFunctionKey(ksym) ||
261                    IsKeypadKey(ksym) ||
262                    IsMiscFunctionKey(ksym) ||
263                    IsPFKey(ksym) ||
264                    IsPrivateKeypadKey(ksym))
265                         continue;
266                 switch(ksym) {
267                 case XK_Return:
268                         passwd[len] = 0;
269                         if ((ret = pam_authenticate(handle, 0)) == PAM_SUCCESS)
270                                 running = false;
271                         else {
272                                 fprintf(stderr, "PAM: %s\n", pam_strerror(handle, ret));
273                                 if (beep)
274                                         XBell(dpy, 100);
275                         }
276                         len = 0;
277                         break;
278                 case XK_Escape:
279                         len = 0;
280                         break;
281                 case XK_BackSpace:
282                         if (len > 0)
283                                 len--;
284                         break;
285                 default:
286                         if(num && !iscntrl((int) buf[0]) && (len + num < sizeof passwd)) {
287                                 memcpy(passwd + len, buf, num);
288                                 len += num;
289                         }
290                         break;
291                 }
292         }
293         XUngrabPointer(dpy, CurrentTime);
294         XFreePixmap(dpy, pmap);
295         XDestroyWindow(dpy, w);
296         XCloseDisplay(dpy);
297         return 0;
298 }