Imported Upstream version 0.1.0
[debian/lxdm.git] / src / ui.c
1 /*
2 * lxdm.c - basic ui of lxdm
3 *
4 * Copyright 2009 dgod <dgod.osa@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22
23 #define XLIB_ILLEGAL_ACCESS
24 #include <gdk/gdk.h>
25 #include <gdk/gdkx.h>
26 #include <gdk/gdkkeysyms.h>
27 #include <X11/Xlib.h>
28
29 #include <string.h>
30 #include <poll.h>
31 #include <grp.h>
32 #include <unistd.h>
33 #include <ctype.h>
34
35 #include "lxdm.h"
36
37 #define MAX_INPUT_CHARS 32
38 #define MAX_VISIBLE_CHARS 14
39
40 static Display *dpy;
41 static GdkWindow *root,*win;
42 static PangoLayout *layout;
43 static char user[MAX_INPUT_CHARS];
44 static char pass[MAX_INPUT_CHARS];
45 static int stage;
46 static GdkRectangle rc;
47 static GdkColor bg,border,hint,text,msg;
48
49 static GSList *sessions;
50 static int session_select=-1;
51
52 static char *message;
53
54 static pid_t greeter=-1;
55 static guint greeter_id=0;
56 static int greeter_pipe[2];
57 static GIOChannel *greeter_io;
58 static guint io_id;
59
60 static int get_text_layout(char *s,int *w,int *h)
61 {
62 pango_layout_set_text(layout,s,-1);
63 pango_layout_get_pixel_size(layout,w,h);
64 return 0;
65 }
66
67 static void draw_text(cairo_t *cr,double x,double y,char *text,GdkColor *color)
68 {
69 pango_layout_set_text (layout, text, -1);
70 cairo_move_to(cr,x,y);
71 gdk_cairo_set_source_color(cr,color);
72 pango_cairo_show_layout(cr,layout);
73 }
74
75 static void on_expose(void)
76 {
77 cairo_t *cr=gdk_cairo_create(win);
78 char *p=(stage==0)?user:pass;
79 int len=strlen(p);
80 GdkColor *color;
81
82 gdk_cairo_set_source_color(cr,&bg);
83 cairo_rectangle(cr,0,0,rc.width,rc.height);
84 cairo_fill(cr);
85 gdk_cairo_set_source_color(cr,&border);
86 cairo_set_line_width(cr,1.0);
87 cairo_stroke(cr);
88 cairo_rectangle(cr,0,0,rc.width,rc.height);
89
90 if(message)
91 {
92 color=&msg;
93 p=message;
94 }
95 else if(stage==0)
96 {
97 if(len<MAX_VISIBLE_CHARS)
98 p=user;
99 else
100 p=user+len-MAX_VISIBLE_CHARS;
101 color=&text;
102 if(len==0)
103 {
104 p="Username";
105 color=&hint;
106 }
107 }
108 else if(stage==1)
109 {
110 char spy[MAX_VISIBLE_CHARS+1];
111 p=spy;
112 if(len<MAX_VISIBLE_CHARS)
113 {
114 memset(spy,'*',len);
115 p[len]=0;
116 }
117 else
118 {
119 memset(spy,'*',MAX_VISIBLE_CHARS);
120 p[MAX_VISIBLE_CHARS]=0;
121 }
122 color=&text;
123 if(len==0)
124 {
125 p="Password";
126 color=&hint;
127 }
128 }
129 draw_text(cr,3,3,p,color);
130 cairo_destroy(cr);
131 }
132
133 static void on_key(GdkEventKey *event)
134 {
135 char *p;
136 int len;
137 int key;
138
139 if(stage!=0 && stage!=1)
140 return;
141 message=0;
142 key=event->keyval;
143 p=(stage==0)?user:pass;
144 len=strlen(p);
145 if(key==GDK_Escape)
146 {
147 user[0]=0;
148 pass[0]=0;
149 stage=0;
150 session_select=-1;
151 }
152 else if(key==GDK_BackSpace)
153 {
154 if(len>0)
155 {
156 p[--len]=0;
157 }
158 }
159 else if(key==GDK_Return)
160 {
161 if(stage==0 && len==0)
162 return;
163 stage++;
164 if(stage==1)
165 {
166 if(!strcmp(user,"reboot") || !strcmp(user,"shutdown"))
167 {
168 stage=2;
169 }
170 }
171 }
172 else if(key==GDK_F1)
173 {
174 LXSESSION *sess;
175 if(!sessions)
176 {
177 sessions=do_scan_xsessions();
178 session_select=0;
179 }
180 else
181 {
182 session_select++;
183 if(session_select>= g_slist_length(sessions))
184 session_select=0;
185 }
186 sess=g_slist_nth_data(sessions,session_select);
187 if(sess) message=sess->name;
188 }
189 else if(key>=0x20 && key<=0x7e)
190 {
191 if(len<MAX_INPUT_CHARS-1)
192 {
193 p[len++]=key;
194 p[len]=0;
195 }
196 }
197 on_expose();
198 if(stage==2)
199 {
200 ui_do_login();
201 if(stage!=2)
202 {
203 on_expose();
204 }
205 }
206 }
207
208 int ui_do_login(void)
209 {
210 struct passwd *pw;
211 int ret;
212
213 if(stage!=2)
214 return -1;
215
216 if(!strcmp(user,"reboot"))
217 {
218 lxdm_do_reboot();
219 }
220 else if(!strcmp(user,"shutdown"))
221 {
222 lxdm_do_shutdown();
223 }
224 ret=lxdm_auth_user(user,pass,&pw);
225 if(AUTH_SUCCESS==ret && pw!=NULL)
226 {
227 char *exec=0;
228 if(sessions && session_select>0)
229 {
230 LXSESSION *sess;
231 sess=g_slist_nth_data(sessions,session_select);
232 exec=g_strdup(sess->exec);
233 free_xsessions(sessions);
234 }
235 sessions=0;session_select=-1;
236 ui_drop();
237 lxdm_do_login(pw,exec,0);
238 g_free(exec);
239 if(lxdm_cur_session()<=0)
240 {
241 ui_prepare();
242 }
243 }
244 else
245 {
246 user[0]=pass[0]=0;
247 stage=0;
248 }
249 return 0;
250 }
251
252 void ui_event_cb(GdkEvent *event,gpointer data)
253 {
254 if(stage==2)
255 return;
256 if(event->type==GDK_KEY_PRESS)
257 {
258 on_key((GdkEventKey*)event);
259 }
260 else if(event->type==GDK_EXPOSE)
261 {
262 on_expose();
263 }
264 }
265
266 void ui_drop(void)
267 {
268 /* drop connect event */
269 if(dpy)
270 {
271 if(win)
272 {
273 gdk_window_destroy(win);
274 win=NULL;
275 XUngrabKeyboard(dpy,CurrentTime);
276 }
277 }
278
279 /* destroy the font */
280 if(layout)
281 {
282 g_object_unref(layout);
283 layout=NULL;
284 }
285
286 /* if greeter, do quit */
287 if(greeter>0)
288 {
289 write(greeter_pipe[0],"exit\n",5);
290 g_source_remove(io_id);
291 io_id=0;
292 g_io_channel_unref(greeter_io);
293 greeter_io=NULL;
294 close(greeter_pipe[1]);
295 close(greeter_pipe[0]);
296 kill(greeter,SIGTERM);
297 }
298 }
299
300 #if 1
301 void ui_set_bg(void)
302 {
303 char *p;
304 GdkWindow *root=gdk_get_default_root_window();
305 GdkColor screen;
306 GdkPixbuf *bg_img=NULL;
307
308 /* get background */
309 p=g_key_file_get_string(config,"display","bg",0);
310 if(!p) p=g_strdup("#222E45");
311 if(p && p[0]!='#')
312 {
313 GdkPixbuf *pb=gdk_pixbuf_new_from_file(p,0);
314 if(!pb)
315 {
316 g_free(p);
317 p=g_strdup("#222E45");
318 }
319 else
320 {
321 bg_img=pb;
322 }
323 }
324 if(p && p[0]=='#')
325 {
326 gdk_color_parse(p,&screen);
327 }
328 g_free(p);
329
330 /* set background */
331 if(!bg_img)
332 {
333 GdkColormap *map = gdk_window_get_colormap(root);
334 gdk_color_alloc(map, &screen);
335 gdk_window_set_background(root,&screen);
336 }
337 else
338 {
339 GdkPixmap *pix=NULL;
340 p=g_key_file_get_string(config,"display","bg_style",0);
341 if(!p || !strcmp(p,"stretch"))
342 {
343 GdkPixbuf *pb=gdk_pixbuf_scale_simple(bg_img,
344 gdk_screen_width(),
345 gdk_screen_height(),
346 GDK_INTERP_HYPER);
347 g_object_unref(bg_img);
348 bg_img=pb;
349 }
350 g_free(p);
351 gdk_pixbuf_render_pixmap_and_mask(bg_img,&pix,NULL,0);
352 g_object_unref(bg_img);
353 /* call x directly, because gdk will ref the pixmap */
354 //gdk_window_set_back_pixmap(root,pix,FALSE);
355 XSetWindowBackgroundPixmap(GDK_WINDOW_XDISPLAY(root),
356 GDK_WINDOW_XID(root), GDK_PIXMAP_XID(pix));
357 g_object_unref(pix);
358 }
359 gdk_window_clear(root);
360 }
361 #else
362 void ui_set_bg(void)
363 {
364 char *p;
365 GdkWindow *root=gdk_get_default_root_window();
366 GdkColor screen;
367
368 p=g_key_file_get_string(config,"display","bg",0);
369 if(!p) p=g_strdup("#222E45");
370 if(p && p[0]!='#')
371 {
372 char *style=g_key_file_get_string(config,"display","bg_style",0);
373 GdkPixbuf *pb;
374 GdkPixmap *pix=NULL;
375 if(!p || !strcmp(p,"stretch"))
376 {
377 pb=gdk_pixbuf_new_from_file_at_scale(p,
378 gdk_screen_width(),gdk_screen_height(),TRUE,NULL);
379 }
380 else
381 {
382 pb=gdk_pixbuf_new_from_file(p,0);
383 }
384 g_free(style);
385 if(pb)
386 {
387 gdk_pixbuf_render_pixmap_and_mask(pb,&pix,NULL,0);
388 g_object_unref(pb);
389 gdk_window_set_back_pixmap(root,pix,FALSE);
390 g_object_unref(pix);
391 }
392 }
393 if(p && p[0]=='#')
394 {
395 GdkColormap *map = gdk_window_get_colormap(root);
396 gdk_color_parse(p,&screen);
397 gdk_color_alloc(map, &screen);
398 gdk_window_set_background(root,&screen);
399 }
400 g_free(p);
401 gdk_window_clear(root);
402 }
403 #endif
404
405 static void greeter_setup(gpointer user)
406 {
407 struct passwd *pw;
408 if(AUTH_SUCCESS==lxdm_auth_user("lxdm",NULL,&pw))
409 {
410 initgroups(pw->pw_name, pw->pw_gid);
411 setgid(pw->pw_gid);
412 setuid(pw->pw_uid);
413 }
414 }
415
416 static gchar *greeter_param(char *str,char *name)
417 {
418 char *temp,*p;
419 char ret[128];
420 int i;
421 temp=g_strdup_printf(" %s=",name);
422 p=strstr(str,temp);
423 if(!p)
424 {
425 g_free(temp);
426 return NULL;
427 }
428 p+=strlen(temp);
429 g_free(temp);
430 for(i=0;i<127;i++)
431 {
432 if(!p[i] || isspace(p[i]))
433 break;
434 ret[i]=p[i];
435 }
436 ret[i]=0;
437 return g_strdup(ret);
438 }
439
440 static gboolean greeter_input(GIOChannel *source,GIOCondition condition,gpointer data)
441 {
442 GIOStatus ret;
443 char *str;
444
445 if(!(G_IO_IN&condition))
446 return FALSE;
447 ret=g_io_channel_read_line(source,&str,NULL,NULL,NULL);
448 if(ret!=G_IO_STATUS_NORMAL)
449 return FALSE;
450
451 if(!strncmp(str,"reboot",6))
452 {
453 lxdm_do_reboot();
454 }
455 else if(!strncmp(str,"shutdown",6))
456 {
457 lxdm_do_shutdown();
458 }
459 else if(!strncmp(str,"log ",4))
460 {
461 log_print(str+4);
462 }
463 else if(!strncmp(str,"login ",6))
464 {
465 char *user=greeter_param(str,"user");
466 char *pass=greeter_param(str,"pass");
467 char *session=greeter_param(str,"session");
468 char *lang=greeter_param(str,"lang");
469 if(user && pass)
470 {
471 struct passwd *pw;
472 int ret=lxdm_auth_user(user,pass,&pw);
473 if(AUTH_SUCCESS==ret && pw!=NULL)
474 {
475 ui_drop();
476 lxdm_do_login(pw,session,lang);
477 if(lxdm_cur_session()<=0)
478 {
479 ui_prepare();
480 }
481 }
482 else
483 {
484 write(greeter_pipe[0],"reset\n",6);
485 }
486 }
487 g_free(user);g_free(pass);g_free(session);g_free(lang);
488 }
489 g_free(str);
490 return TRUE;
491 }
492
493 static void greeter_watch(GPid pid,gint status,gpointer data)
494 {
495 if(pid!=greeter)
496 return;
497 greeter=-1;
498 g_source_remove(greeter_id);
499 }
500
501 void ui_prepare(void)
502 {
503 cairo_t *cr;
504 PangoFontDescription *desc;
505 char *p;
506 int w,h;
507
508 /* get current display */
509 dpy=gdk_x11_get_default_xdisplay();
510 root=gdk_get_default_root_window();
511
512 /* if session is running */
513 if(lxdm_cur_session()>0)
514 return;
515
516 /* if find greeter, run it */
517 p=g_key_file_get_string(config,"base","greeter",NULL);
518 if(p && p[0])
519 {
520 //char *arg[]={p,NULL};
521 char **arg=g_strsplit(p," ",9);
522 gboolean ret;
523 if(greeter>0 && kill(greeter,0)==0)
524 {
525 return;
526 }
527 #if 0
528 /* FIXME this may spawn a lot of greeter, why ? */
529 ret=g_spawn_async_with_pipes(NULL,arg,NULL,
530 G_SPAWN_SEARCH_PATH,greeter_setup,0,
531 &greeter,greeter_pipe+0,greeter_pipe+1,NULL,NULL);
532 if(ret==TRUE)
533 {
534 g_free(p);
535 greeter_io=g_io_channel_unix_new(greeter_pipe[1]);
536 io_id=g_io_add_watch(greeter_io,G_IO_IN|G_IO_HUP|G_IO_ERR,
537 greeter_input,NULL);
538 return;
539 }
540
541 #else
542 ret=g_spawn_async_with_pipes(NULL,arg,NULL,
543 G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD,greeter_setup,0,
544 &greeter,greeter_pipe+0,greeter_pipe+1,NULL,NULL);
545 g_strfreev(arg);
546 if(ret==TRUE)
547 {
548 g_free(p);
549 greeter_io=g_io_channel_unix_new(greeter_pipe[1]);
550 io_id=g_io_add_watch(greeter_io,G_IO_IN|G_IO_HUP|G_IO_ERR,
551 greeter_input,NULL);
552 greeter_id=g_child_watch_add(greeter,greeter_watch,0);
553 return;
554 }
555 #endif
556 }
557 g_free(p);
558
559 /* set root window bg */
560 ui_set_bg();
561
562 /* init something */
563 if(sessions)
564 free_xsessions(sessions);
565 sessions=0;session_select=0;
566 user[0]=0;pass[0]=0;stage=0;
567 p=g_key_file_get_string(config,"input","border",0);
568 if(!p) p=g_strdup("#CBCAE6");
569 gdk_color_parse(p,&border);
570 g_free(p);
571 p=g_key_file_get_string(config,"input","bg",0);
572 if(!p) p=g_strdup("#ffffff");
573 gdk_color_parse(p,&bg);
574 g_free(p);
575 p=g_key_file_get_string(config,"input","hint",0);
576 if(!p) p=g_strdup("#CBCAE6");
577 gdk_color_parse(p,&hint);
578 g_free(p);
579 p=g_key_file_get_string(config,"input","text",0);
580 if(!p) p=g_strdup("#000000");
581 gdk_color_parse(p,&text);
582 g_free(p);
583 p=g_key_file_get_string(config,"input","msg",0);
584 if(!p) p=g_strdup("#ff0000");
585 gdk_color_parse(p,&msg);
586 g_free(p);
587
588 /* create the window */
589 if(!win)
590 {
591 GdkWindowAttr attr;
592 int mask=0;
593 memset(&attr,0,sizeof(attr));
594 attr.window_type=GDK_WINDOW_CHILD;
595 attr.event_mask=GDK_EXPOSURE_MASK|GDK_KEY_PRESS_MASK;
596 attr.wclass=GDK_INPUT_OUTPUT;
597 win=gdk_window_new(0,&attr,mask);
598 }
599
600 /* create the font */
601 if(layout)
602 {
603 g_object_unref(layout);
604 layout=NULL;
605 }
606 cr=gdk_cairo_create(win);
607 layout=pango_cairo_create_layout(cr);
608 cairo_destroy(cr);
609 p=g_key_file_get_string(config,"input","font",0);
610 if(!p) p=g_strdup("Sans 14");
611 desc=pango_font_description_from_string(p);
612 pango_layout_set_font_description(layout,desc);
613 pango_font_description_free(desc);
614 g_free(p);
615
616 /* set window size */
617 if(layout)
618 {
619 char temp[MAX_VISIBLE_CHARS+1+1];
620 memset(temp,'A',sizeof(temp));
621 temp[sizeof(temp)-1]=0;
622 get_text_layout(temp,&w,&h);
623 rc.width=w+6;rc.height=h+6;
624 rc.x=(gdk_screen_width()-rc.width)/2;
625 rc.y=(gdk_screen_height()-rc.height)/2;
626 gdk_window_move_resize(win,rc.x,rc.y,rc.width,rc.height);
627 }
628
629 /* connect event */
630 gdk_window_set_events(win,GDK_EXPOSURE_MASK|GDK_KEY_PRESS_MASK);
631 XGrabKeyboard(dpy, GDK_WINDOW_XWINDOW(win), False, GrabModeAsync, GrabModeAsync, CurrentTime);
632
633 /* draw the first time */
634 gdk_window_show(win);
635 gdk_window_focus(win,0);
636 }
637
638 void ui_add_cursor(void)
639 {
640 GdkCursor *cur;
641 if(!root) return;
642 cur=gdk_cursor_new(GDK_LEFT_PTR);
643 gdk_window_set_cursor(root,cur);
644 gdk_cursor_unref(cur);
645 }
646
647 int ui_main(void)
648 {
649 GMainLoop *loop=g_main_loop_new(NULL,0);
650 ui_add_cursor();
651 ui_prepare();
652 gdk_event_handler_set(ui_event_cb,0,0);
653 g_main_loop_run(loop);
654 return 0;
655 }
656