Adding upstream version 0.4.1.
[debian/lxdm.git] / src / greeter.c
1 /*
2 * lxdm-ui.c
3 *
4 * Copyright 2009 PCMan <pcman.tw@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 2 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 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <gtk/gtk.h>
27 #include <gdk/gdkx.h>
28 #include <glib/gi18n.h>
29 #include <X11/XKBlib.h>
30
31 #include "lang.h"
32 #include <time.h>
33 #include <stdlib.h>
34 #include <sys/stat.h>
35 #include <sys/wait.h>
36
37 #include "lxcom.h"
38 #include "greeter-utils.h"
39
40 enum {
41 COL_SESSION_NAME,
42 COL_SESSION_EXEC,
43 COL_SESSION_DESKTOP_FILE,
44 N_SESSION_COLS
45 };
46
47 enum {
48 COL_LANG_DISPNAME,
49 COL_LANG,
50 N_LANG_COLS
51 };
52
53 #define VCONFIG_FILE "/var/lib/lxdm/lxdm.conf"
54 #define XKB_SYMBOL_DIR "/usr/share/X11/xkb/symbols.dir"
55
56 static GtkBuilder* builder;
57 static GKeyFile *config;
58 static GKeyFile * var_config;
59 static GtkWidget* win;
60 static GtkWidget* prompt;
61 static GtkWidget* login_entry;
62 static GtkWidget* user_list;
63
64 static GtkWidget* sessions;
65 static GtkWidget* lang;
66
67 static GtkWidget* exit_btn;
68
69 static GtkWidget* exit_menu;
70 static GtkWidget *lang_menu;
71
72 static char* user = NULL;
73 static char* pass = NULL;
74
75 static char* ui_file = NULL;
76 static char *ui_nobody = NULL;
77
78 static GIOChannel *greeter_io;
79
80 static int auto_login;
81 static char datetime_fmt[8]="%c";
82
83 static void do_reboot(void)
84 {
85 printf("reboot\n");
86 }
87
88 static void do_shutdown(void)
89 {
90 printf("shutdown\n");
91 }
92
93 static char *get_session_lang(void)
94 {
95 GtkTreeModel* model;
96 GtkTreeIter it;
97 gchar *res;
98 if(!lang)
99 return g_strdup("");
100
101 if(!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(lang), &it))
102 return g_strdup("");
103 model = gtk_combo_box_get_model(GTK_COMBO_BOX(lang));
104 gtk_tree_model_get(model, &it, 1, &res, -1);
105 return res;
106 }
107
108 static char *get_session_exec(void)
109 {
110 GtkTreeModel* model;
111 GtkTreeIter it;
112 gchar *res;
113 if(!lang)
114 return g_strdup("");
115
116 if(!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(sessions), &it))
117 return g_strdup("");
118 model = gtk_combo_box_get_model(GTK_COMBO_BOX(sessions));
119 gtk_tree_model_get(model, &it, 1, &res, -1);
120 return res;
121 }
122
123 static void on_entry_activate(GtkEntry* entry)
124 {
125 char* tmp;
126 if( !user )
127 {
128 user = g_strdup( gtk_entry_get_text( GTK_ENTRY(entry) ) );
129 gtk_entry_set_text(GTK_ENTRY(entry), "");
130 gtk_label_set_text( GTK_LABEL(prompt), _("Password:") );
131 if(strchr(user, ' '))
132 {
133 g_free(user);
134 user = NULL;
135 return;
136 }
137 gtk_entry_set_visibility(entry, FALSE);
138 }
139 else
140 {
141 char *session_exec=get_session_exec();
142 char *session_lang=get_session_lang();
143
144 tmp = g_strdup( gtk_entry_get_text(entry) );
145 pass=g_base64_encode((guchar*)tmp,strlen(tmp)+1);
146 g_free(tmp);
147
148 printf("login user=%s pass=%s session=%s lang=%s\n",
149 user, pass, session_exec, session_lang);
150
151 /* password check failed */
152 g_free(user);
153 user = NULL;
154 g_free(pass);
155 pass = NULL;
156 g_free(session_lang);
157 g_free(session_exec);
158
159 gtk_widget_hide(prompt);
160 gtk_widget_hide( GTK_WIDGET(entry) );
161
162 gtk_label_set_text( GTK_LABEL(prompt), _("User:") );
163 gtk_entry_set_text(GTK_ENTRY(entry), "");
164 gtk_entry_set_visibility(GTK_ENTRY(entry), TRUE);
165 }
166 }
167
168 static void load_sessions()
169 {
170 GtkListStore* list;
171 GtkTreeIter it, active_it = {0};
172 char* last;
173 char *path, *file_name, *name, *exec;
174 GKeyFile* kf;
175 GDir* dir = g_dir_open(XSESSIONS_DIR, 0, NULL);
176 if( !dir )
177 return;
178
179 last = g_key_file_get_string(var_config, "base", "last_session", NULL);
180
181 list = gtk_list_store_new(N_SESSION_COLS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
182 kf = g_key_file_new();
183 while( ( file_name = (char*)g_dir_read_name(dir) ) != NULL )
184 {
185 path = g_build_filename(XSESSIONS_DIR, file_name, NULL);
186 if( g_key_file_load_from_file(kf, path, 0, NULL) )
187 {
188 name = g_key_file_get_locale_string(kf, "Desktop Entry", "Name", NULL, NULL);
189 if(!name || !name[0])
190 {
191 g_free(name);
192 g_free(path);
193 continue;
194 }
195 exec = g_key_file_get_string(kf, "Desktop Entry", "Exec", NULL);
196 if(!exec || !exec[0])
197 {
198 /* bad session config file */
199 g_free(exec);
200 g_free(name);
201 g_free(path);
202 continue;
203 }
204 g_free(exec); /* we just test it, and not use it */
205 exec=g_strdup(path);
206
207 if( !strcmp(name, "LXDE") )
208 gtk_list_store_prepend(list, &it);
209 else
210 gtk_list_store_append(list, &it);
211 gtk_list_store_set(list, &it,
212 COL_SESSION_NAME, name,
213 COL_SESSION_EXEC, exec,
214 COL_SESSION_DESKTOP_FILE, file_name, -1);
215 if( last && g_strcmp0(path, last) == 0 )
216 {
217 active_it = it;
218 }
219
220 g_free(name);
221 g_free(exec);
222 }
223 g_free(path);
224 }
225 g_dir_close(dir);
226 g_key_file_free(kf);
227
228 gtk_list_store_prepend(list, &it);
229 gtk_list_store_set(list, &it,
230 COL_SESSION_NAME, _("Default"),
231 COL_SESSION_EXEC, "",
232 COL_SESSION_DESKTOP_FILE, "__default__", -1);
233 if( last && g_strcmp0("__default__", last) == 0 )
234 active_it = it;
235
236 g_free(last);
237 gtk_combo_box_set_model( GTK_COMBO_BOX(sessions), GTK_TREE_MODEL(list) );
238 #if GTK_CHECK_VERSION(2,24,0)
239 gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(sessions), 0);
240 #else
241 gtk_combo_box_entry_set_text_column(GTK_COMBO_BOX_ENTRY(sessions), 0);
242 #endif
243 if( active_it.stamp )
244 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(sessions), &active_it);
245 else
246 gtk_combo_box_set_active(GTK_COMBO_BOX(sessions), 0);
247
248 g_object_unref(list);
249 }
250
251 static void load_lang_cb(void *arg, char *lang, char *desc)
252 {
253 GtkListStore* list = (GtkListStore*)arg;
254 GtkTreeIter it;
255 gchar *temp,*p,*lang2;
256
257 lang2=g_strdup(lang);
258 p=strchr(lang2,'.');
259 if(p) *p=0;
260
261 if(lang2[0] && lang2[0]!='~')
262 temp=g_strdup_printf("%s\t%s",lang2,desc?desc:"");
263 else
264 temp=g_strdup(desc);
265 g_free(lang2);
266 gtk_list_store_append(list, &it);
267 gtk_list_store_set(list, &it,
268 COL_LANG_DISPNAME, temp,
269 COL_LANG, lang, -1);
270 g_free(temp);
271 }
272
273 static gint lang_cmpr(GtkTreeModel *list,GtkTreeIter *a,GtkTreeIter *b,gpointer user_data)
274 {
275 gint ret;
276 gchar *as,*bs;
277 gtk_tree_model_get(list,a,1,&as,-1);
278 gtk_tree_model_get(list,b,1,&bs,-1);
279 ret=strcmp(as,bs);
280 g_free(as);g_free(bs);
281 return ret;
282 }
283
284 static gint keyboard_cmpr(GtkTreeModel *list,GtkTreeIter *a,GtkTreeIter *b,gpointer user_data)
285 {
286 gint ret;
287 gchar *as,*bs;
288 gtk_tree_model_get(list,a,0,&as,-1);
289 gtk_tree_model_get(list,b,0,&bs,-1);
290 ret=strcmp(as,bs);
291 g_free(as);g_free(bs);
292 return ret;
293 }
294
295 static void on_menu_lang_select(GtkMenuItem *item,gpointer user_data)
296 {
297 GtkTreeIter iter;
298 char *sel=(char*)user_data;
299 int i;
300 gboolean res;
301 GtkTreeModel *list;
302 int active=-1;
303 char *temp;
304 if(!sel || !sel[0]) return;
305
306 list=gtk_combo_box_get_model(GTK_COMBO_BOX(lang));
307 res=gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list),&iter);
308 for(i=0;res==TRUE;i++)
309 {
310 gtk_tree_model_get(GTK_TREE_MODEL(list),&iter,1,&temp,-1);
311 if(!strcmp(temp,sel))
312 {
313 g_free(temp);
314 active=i;
315 break;
316 }
317 g_free(temp);
318 res=gtk_tree_model_iter_next(GTK_TREE_MODEL(list),&iter);
319 }
320 if(active>=0)
321 {
322 gtk_combo_box_set_active(GTK_COMBO_BOX(lang),active);
323 return;
324 }
325 gtk_list_store_append((GtkListStore*)list, &iter);
326 temp=(char*)gtk_menu_item_get_label(item);
327 gtk_list_store_set((GtkListStore*)list, &iter,
328 COL_LANG_DISPNAME, temp,
329 COL_LANG, sel, -1);
330 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(lang),&iter);
331 }
332
333 static void load_menu_lang_cb(void *arg, char *lang, char *desc)
334 {
335 GtkWidget *menu=GTK_WIDGET(arg);
336 GtkWidget* item;
337
338 gchar *temp,*p,*lang2;
339
340 lang2=g_strdup(lang);
341 p=strchr(lang2,'.');
342 if(p) *p=0;
343
344 if(lang2[0] && lang2[0]!='~')
345 temp=g_strdup_printf("%s\t%s",lang2,desc?desc:"");
346 else
347 temp=g_strdup(desc);
348 g_free(lang2);
349
350 item = gtk_menu_item_new_with_label(temp);
351 g_signal_connect(item, "activate", G_CALLBACK(on_menu_lang_select), g_strdup(lang));
352 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
353 g_free(temp);
354 }
355
356 static void show_all_languages(void)
357 {
358 if(!lang_menu)
359 {
360 lang_menu=gtk_menu_new();
361 lxdm_load_langs(var_config,TRUE,lang_menu,load_menu_lang_cb);
362 gtk_widget_show_all(lang_menu);
363 }
364 gtk_menu_popup(GTK_MENU(lang_menu),NULL,NULL,NULL,NULL,0,gtk_get_current_event_time());
365 }
366
367 static void on_lang_changed(GtkComboBox *widget)
368 {
369 GtkTreeIter it;
370 if( gtk_combo_box_get_active_iter(widget, &it) )
371 {
372 GtkListStore *list=(GtkListStore*)gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
373 char *lang=NULL;
374 gtk_tree_model_get(GTK_TREE_MODEL(list), &it, 1, &lang, -1);
375 if(lang[0]=='~')
376 {
377 gtk_combo_box_set_active(widget,0);
378 show_all_languages();
379 }
380 g_free(lang);
381 }
382 }
383
384 static void load_langs()
385 {
386 GtkListStore* list;
387 char* lang_str;
388 int active = 0;
389
390 list = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
391 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(list),0,GTK_SORT_ASCENDING);
392 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(list),0,lang_cmpr,NULL,NULL);
393 lxdm_load_langs(var_config,FALSE,list, load_lang_cb);
394 lang_str = g_key_file_get_string(var_config, "base", "last_lang", NULL);
395 if(lang_str && lang_str[0])
396 {
397 gboolean res;
398 GtkTreeIter iter;
399 int i;
400 res=gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list),&iter);
401 if(res) for(i=0;;i++)
402 {
403 gchar *lang;
404 gtk_tree_model_get(GTK_TREE_MODEL(list),&iter,1,&lang,-1);
405 if(!strcmp(lang,lang_str))
406 {
407 g_free(lang);
408 active=i;
409 break;
410 }
411 g_free(lang);
412 res=gtk_tree_model_iter_next(GTK_TREE_MODEL(list),&iter);
413 if(!res) break;
414 }
415 }
416 g_free(lang_str);
417 gtk_combo_box_set_model( GTK_COMBO_BOX(lang), GTK_TREE_MODEL(list) );
418 #if GTK_CHECK_VERSION(2,24,0)
419 gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(lang), 0);
420 #else
421 gtk_combo_box_entry_set_text_column(GTK_COMBO_BOX_ENTRY(lang), 0);
422 #endif
423 gtk_combo_box_set_active(GTK_COMBO_BOX(lang), active < 0 ? 0 : active);
424 g_object_unref(list);
425
426 g_signal_connect(G_OBJECT(lang),"changed",G_CALLBACK(on_lang_changed),NULL);
427 }
428
429 static void on_keyboard_changed(GtkComboBox *widget)
430 {
431 GtkTreeIter it;
432 if( gtk_combo_box_get_active_iter(widget, &it) )
433 {
434 GtkListStore *list=(GtkListStore*)gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
435 char *keyboard=NULL;
436 char *cmd;
437 int status;
438 gboolean res;
439 gtk_tree_model_get(GTK_TREE_MODEL(list), &it, 0, &keyboard, -1);
440 /* set the current xkb */
441 cmd=g_strdup_printf("setxkbmap %s",keyboard);
442 res=g_spawn_command_line_sync(cmd,NULL,NULL,&status,NULL);
443 printf("%s %d %d\n",cmd,res,WEXITSTATUS (status));
444 g_free(cmd);
445 g_free(keyboard);
446 }
447 }
448
449 static gchar *xkb_name_norm(gchar *s)
450 {
451 if(!strcmp(s,"pc")) return NULL;
452 if(!strncmp(s,"pc(",3)) return NULL;
453 if(!strncmp(s,"inet(",5)) return NULL;
454 if(!strncmp(s,"group(",6)) return NULL;
455 if(!strncmp(s,"srvr_ctrl(",10)) return NULL;
456 if(g_str_has_suffix(s,"(basic)"))
457 {
458 *strstr(s,"(basic)")=0;
459 }
460 else if(!strcmp(s,"jp(106)")) //TODO: is default jp jp(106)
461 {
462 s[2]=0;
463 }
464 return s;
465 }
466
467 static char *xkb_get_current(void)
468 {
469 Display *dpy=gdk_x11_display_get_xdisplay(gdk_display_get_default());
470 XkbDescRec * xkb_desc;
471 char *symbol_string=NULL;
472 gchar **list;
473 int i;
474
475 if(!dpy) return NULL;
476 xkb_desc=XkbAllocKeyboard();
477 if (xkb_desc == NULL)
478 return NULL;
479 XkbGetControls(dpy, XkbAllControlsMask, xkb_desc);
480 XkbGetNames(dpy, XkbSymbolsNameMask | XkbGroupNamesMask, xkb_desc);
481 if ((xkb_desc->names == NULL) || (xkb_desc->ctrls == NULL) || (xkb_desc->names->groups == NULL))
482 {
483 }
484 else
485 {
486 if (xkb_desc->names->symbols != None)
487 {
488 symbol_string=XGetAtomName(dpy, xkb_desc->names->symbols);
489 }
490 }
491 XkbFreeKeyboard(xkb_desc, 0, True);
492
493 if(!symbol_string)
494 return FALSE;
495 list=g_strsplit(symbol_string,"+",-1);
496 XFree(symbol_string);
497 if(!list) return NULL;
498 for(i=0;list[i]!=NULL;i++)
499 {
500 if(!xkb_name_norm(list[i])) continue;
501 symbol_string=g_strdup(list[i]);
502 break;
503 }
504 g_strfreev(list);
505
506 return symbol_string;
507 }
508
509 #if 0
510
511 static gboolean load_keyboards(GtkWidget *w)
512 {
513 GtkListStore* list;
514 char p1[16],p2[16],p3[64];
515 FILE *fp;
516 char *cur;
517 int ret;
518 int count,active;
519 GtkTreeIter active_iter;
520 GdkScreen *scr;
521
522 scr=gdk_screen_get_default();
523 if(gdk_screen_get_width(scr)<1024)
524 return FALSE;
525
526 if(!w) return FALSE;
527
528 cur=xkb_get_current();
529 if(!cur) return FALSE;
530 fp=fopen(XKB_SYMBOL_DIR,"r");
531 if(!fp)
532 {
533 g_print("open xkb symbol dir fail\n");
534 g_free(cur);
535 return FALSE;
536 }
537 list = gtk_list_store_new(1, G_TYPE_STRING);
538 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(list),0,GTK_SORT_ASCENDING);
539 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(list),0,keyboard_cmpr,NULL,NULL);
540 for(count=0,active=-1;(ret=fscanf(fp,"%16s %16s %64s\n",p1,p2,p3))==3;)
541 {
542 GtkTreeIter iter;
543 if(strchr(p2,'m') && !strchr(p2,'a')) continue;
544 if(!xkb_name_norm(p3)) continue;
545 gtk_list_store_append(list,&iter);
546 gtk_list_store_set(list,&iter,0,p3,-1);
547 if(!strcmp(cur,p3))
548 {
549 active=count;
550 active_iter=iter;
551 }
552 count++;
553 }
554 fclose(fp);
555 g_free(cur);
556
557 if(count==0 || active==-1)
558 {
559 g_object_unref(list);
560 return FALSE;
561 }
562
563 gtk_combo_box_set_model(GTK_COMBO_BOX(w), GTK_TREE_MODEL(list) );
564 gtk_combo_box_entry_set_text_column(GTK_COMBO_BOX_ENTRY(w), 0);
565 g_object_unref(G_OBJECT(list));
566 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(w), &active_iter);
567
568 g_signal_connect(G_OBJECT(w),"changed",G_CALLBACK(on_keyboard_changed),NULL);
569
570 return TRUE;
571 }
572 #else
573 static gboolean load_keyboards(GtkWidget *w)
574 {
575 GtkListStore* list;
576 char *cur;
577 int count,active;
578 GtkTreeIter active_iter;
579 GdkScreen *scr;
580 GDir *dir;
581 const gchar *filename;
582
583 scr=gdk_screen_get_default();
584 if(gdk_screen_get_width(scr)<1024)
585 return FALSE;
586
587 if(!w) return FALSE;
588
589 cur=xkb_get_current();
590 if(!cur) return FALSE;
591 dir=g_dir_open("/usr/share/X11/xkb/symbols",0,FALSE);
592 if(!dir)
593 {
594 g_print("open xkb symbol dir fail\n");
595 g_free(cur);
596 return FALSE;
597 }
598 list = gtk_list_store_new(1, G_TYPE_STRING);
599 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(list),0,GTK_SORT_ASCENDING);
600 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(list),0,keyboard_cmpr,NULL,NULL);
601 for(count=0,active=-1;(filename=g_dir_read_name(dir))!=NULL;)
602 {
603 GtkTreeIter iter;
604 if(strlen(filename)!=2)
605 continue;
606 gtk_list_store_append(list,&iter);
607 gtk_list_store_set(list,&iter,0,filename,-1);
608 if(!strcmp(cur,filename))
609 {
610 active=count;
611 active_iter=iter;
612 }
613 count++;
614 }
615 g_dir_close(dir);
616 g_free(cur);
617
618 if(count==0 || active==-1)
619 {
620 g_object_unref(list);
621 return FALSE;
622 }
623
624 gtk_combo_box_set_model(GTK_COMBO_BOX(w), GTK_TREE_MODEL(list) );
625 #if GTK_CHECK_VERSION(2,24,0)
626 gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(w), 0);
627 #else
628 gtk_combo_box_entry_set_text_column(GTK_COMBO_BOX_ENTRY(w), 0);
629 #endif
630 g_object_unref(G_OBJECT(list));
631 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(w), &active_iter);
632
633 g_signal_connect(G_OBJECT(w),"changed",G_CALLBACK(on_keyboard_changed),NULL);
634
635 return TRUE;
636 }
637 #endif
638
639 static void on_exit_clicked(GtkButton* exit_btn, gpointer user_data)
640 {
641 gtk_menu_popup( GTK_MENU(exit_menu), NULL, NULL, NULL, NULL,
642 0, gtk_get_current_event_time() );
643 }
644
645 static void load_exit()
646 {
647 GtkWidget* item;
648 exit_menu = gtk_menu_new();
649 item = gtk_image_menu_item_new_with_mnemonic( _("_Reboot") );
650 g_signal_connect(item, "activate", G_CALLBACK(do_reboot), NULL);
651 gtk_menu_shell_append(GTK_MENU_SHELL(exit_menu), item);
652
653 item = gtk_image_menu_item_new_with_mnemonic( _("_Shutdown") );
654 g_signal_connect(item, "activate", G_CALLBACK(do_shutdown), NULL);
655 gtk_menu_shell_append(GTK_MENU_SHELL(exit_menu), item);
656
657 gtk_widget_show_all(exit_menu);
658 g_signal_connect(exit_btn, "clicked", G_CALLBACK(on_exit_clicked), NULL);
659 }
660
661 #if 0
662 static gboolean on_expose(GtkWidget* widget, GdkEventExpose* evt, gpointer user_data)
663 {
664 cairo_t *cr;
665 GdkRectangle *r=&evt->area;
666
667 #if GTK_CHECK_VERSION(2,18,0)
668 if(! gtk_widget_get_has_window(widget))
669 #else
670 if( !GTK_WIDGET_REALIZED(widget) )
671 #endif
672 return FALSE;
673 #if GTK_CHECK_VERSION(2,14,0)
674 cr = gdk_cairo_create(gtk_widget_get_window(widget));
675 #else
676 cr = gdk_cairo_create(widget->window);
677 #endif
678 if(bg_img )
679 {
680 gdk_cairo_set_source_pixbuf(cr, bg_img, 0, 0);
681 gdk_cairo_rectangle (cr,r);
682 cairo_paint(cr);
683 }
684 else
685 {
686 gdk_cairo_set_source_color(cr, &bg_color);
687 gdk_cairo_rectangle (cr,r);
688 cairo_fill(cr);
689 }
690 cairo_destroy(cr);
691 return FALSE;
692 }
693 #endif
694
695 static gboolean on_combobox_entry_button_release(GtkWidget* w, GdkEventButton* evt, GtkComboBox* combo)
696 {
697 gboolean shown;
698 g_object_get(combo, "popup-shown", &shown, NULL);
699 if( shown )
700 gtk_combo_box_popdown(combo);
701 else
702 gtk_combo_box_popup(combo);
703 return FALSE;
704 }
705
706 static void fix_combobox_entry(GtkWidget* combo)
707 {
708 GtkWidget* edit = gtk_bin_get_child(GTK_BIN(combo));
709 gtk_editable_set_editable( (GtkEditable*)edit, FALSE );
710 #if GTK_CHECK_VERSION(2,18,0)
711 gtk_widget_set_can_focus(edit, FALSE);
712 #else
713 GTK_WIDGET_UNSET_FLAGS(edit, GTK_CAN_FOCUS);
714 #endif
715 g_signal_connect(edit, "button-release-event", G_CALLBACK(on_combobox_entry_button_release), combo);
716 }
717
718 #if GTK_CHECK_VERSION(3,0,0)
719 static gboolean on_evt_box_draw(GtkWidget *widget,cairo_t *cr,gpointer user_data)
720 {
721 GtkStyleContext *style=gtk_widget_get_style_context(widget);
722 gtk_render_background(style,cr,0,0,
723 gtk_widget_get_allocated_width(widget),
724 gtk_widget_get_allocated_height(widget));
725 return FALSE;
726 }
727 #else
728 static gboolean on_evt_box_expose(GtkWidget *widget, GdkEventExpose* evt, gpointer user_data)
729 {
730 #if GTK_CHECK_VERSION(2,18,0)
731 if (gtk_widget_is_drawable(widget))
732 #else
733 if (GTK_WIDGET_DRAWABLE (widget))
734 #endif
735 {
736 GtkWidgetClass* klass = (GtkWidgetClass*)G_OBJECT_GET_CLASS(widget);
737 gtk_paint_flat_box (gtk_widget_get_style(widget),
738 #if GTK_CHECK_VERSION(2,14,0)
739 gtk_widget_get_window(widget),
740 #else
741 widget->window,
742 #endif
743 #if GTK_CHECK_VERSION(2,18,0)
744 gtk_widget_get_state(widget), GTK_SHADOW_NONE,
745 #else
746 widget->state, GTK_SHADOW_NONE,
747 #endif
748 &evt->area, widget, "eventbox",
749 0, 0, -1, -1);
750 klass->expose_event (widget, evt);
751 }
752 return FALSE;
753 }
754 #endif
755
756 static gboolean on_timeout(GtkLabel* label)
757 {
758 char buf[128];
759 time_t t;
760 struct tm* tmbuf;
761 time(&t);
762 tmbuf = localtime(&t);
763 strftime(buf, 128, datetime_fmt, tmbuf);
764 gtk_label_set_text(label, buf);
765 return TRUE;
766 }
767
768 static gboolean autologin_timeout(gpointer data)
769 {
770 char *user=g_key_file_get_string(config,"base","autologin",NULL);
771 if(auto_login && user && user[0])
772 {
773 char *session_exec=get_session_exec();
774 char *session_lang=get_session_lang();
775
776 printf("autologin session=%s lang=%s\n",
777 session_exec, session_lang);
778
779 g_free(session_lang);
780 g_free(session_exec);
781
782 gtk_widget_hide(prompt);
783 gtk_widget_hide( GTK_WIDGET(login_entry) );
784 }
785 g_free(user);
786 return FALSE;
787 }
788
789 static gboolean is_autologin_user(const char *name)
790 {
791 char *autologin=g_key_file_get_string(config,"base","autologin",NULL);
792 if(!autologin)
793 return FALSE;
794 return strcmp(name,autologin)?FALSE:TRUE;
795 }
796
797 static void on_user_select(GtkIconView *iconview)
798 {
799 GList *list=gtk_icon_view_get_selected_items(iconview);
800 GtkTreeIter iter;
801 GtkTreeModel *model=gtk_icon_view_get_model(iconview);
802 char *name;
803 if(!list) return;
804 gtk_tree_model_get_iter(model,&iter,list->data);
805 g_list_foreach (list, (GFunc)gtk_tree_path_free, NULL);
806 g_list_free (list);
807 gtk_tree_model_get(model,&iter,2,&name,-1);
808 gtk_widget_hide(user_list);
809 if(name && name[0])
810 {
811 if(auto_login && is_autologin_user(name))
812 {
813 g_free(name);
814
815 char *session_exec=get_session_exec();
816 char *session_lang=get_session_lang();
817
818 printf("autologin session=%s lang=%s\n",
819 session_exec, session_lang);
820
821 g_free(session_lang);
822 g_free(session_exec);
823
824 gtk_widget_hide(prompt);
825 gtk_widget_hide( GTK_WIDGET(login_entry) );
826 return;
827 }
828 gtk_entry_set_text(GTK_ENTRY(login_entry),name);
829 g_free(name);
830 on_entry_activate(GTK_ENTRY(login_entry));
831 gtk_widget_show(login_entry);
832 gtk_widget_grab_focus(login_entry);
833
834 gtk_label_set_text( GTK_LABEL(prompt), _("Password:") );
835 gtk_widget_show(prompt);
836 }
837 else
838 {
839 g_free(name);
840 if(user)
841 {
842 g_free(user);
843 user=NULL;
844 }
845 gtk_entry_set_text(GTK_ENTRY(login_entry),"");
846 gtk_widget_show(login_entry);
847 gtk_widget_grab_focus(login_entry);
848 gtk_label_set_text( GTK_LABEL(prompt), _("User:") );
849 gtk_widget_show(prompt);
850 }
851 auto_login=0;
852 }
853
854 static void on_user_click(GtkIconView *iconview)
855 {
856 on_user_select(iconview);
857 }
858
859 static gboolean load_user_list(GtkWidget *widget)
860 {
861 GtkListStore *model;
862 GtkTreeIter iter;
863 GKeyFile *kf;
864 GtkTreePath *path;
865 char *res=NULL;
866 char **users;
867 gsize count;
868 int i;
869 lxcom_send("/var/run/lxdm/lxdm.sock","USER_LIST",&res);
870 if(!res)
871 {
872 printf("log USER_LIST fail\n");
873 return FALSE;
874 }
875 kf=g_key_file_new();
876 if(!g_key_file_load_from_data(kf,res,-1,0,NULL))
877 {
878 g_key_file_free(kf);
879 g_free(res);
880 printf("log USER_LIST data bad\n");
881 return FALSE;
882 }
883 g_free(res);
884
885 gtk_icon_view_set_selection_mode(GTK_ICON_VIEW(widget),GTK_SELECTION_SINGLE);
886 gtk_icon_view_set_pixbuf_column(GTK_ICON_VIEW(widget),0);
887 gtk_icon_view_set_markup_column(GTK_ICON_VIEW(widget),1);
888 #if GTK_CHECK_VERSION(2,22,0)
889 gtk_icon_view_set_item_orientation(GTK_ICON_VIEW(widget),GTK_ORIENTATION_HORIZONTAL);
890 #else
891 gtk_icon_view_set_orientation(GTK_ICON_VIEW(widget),GTK_ORIENTATION_HORIZONTAL);
892 #endif
893 model=gtk_list_store_new(5,GDK_TYPE_PIXBUF,G_TYPE_STRING,
894 G_TYPE_STRING,G_TYPE_STRING,G_TYPE_BOOLEAN);
895 gtk_icon_view_set_model(GTK_ICON_VIEW(widget),GTK_TREE_MODEL(model));
896 g_signal_connect(G_OBJECT(widget),"item-activated",G_CALLBACK(on_user_select),NULL);
897 //g_signal_connect(G_OBJECT(widget),"selection-changed",G_CALLBACK(on_user_select),NULL);
898 g_signal_connect(G_OBJECT(widget),"button-release-event",G_CALLBACK(on_user_click),NULL);
899
900 users=g_key_file_get_groups(kf,&count);
901 if(!users || count<=0)
902 {
903 g_key_file_free(kf);
904 printf("USER_LIST 0 user\n");
905 return FALSE;
906 }
907 if(count>3)
908 {
909 // TODO: better ui needed
910 count=3;
911 }
912 for(i=0;i<count;i++)
913 {
914 char *gecos,*face_path,*display;
915 gboolean login;
916 GdkPixbuf *face=NULL;
917 gtk_list_store_append(model,&iter);
918 gecos=g_key_file_get_string(kf,users[i],"gecos",0);
919 face_path=g_key_file_get_string(kf,users[i],"face",0);
920 login=g_key_file_get_boolean(kf,users[i],"login",0);
921 if(face_path)
922 face=gdk_pixbuf_new_from_file_at_scale(face_path,48,48,TRUE,NULL);
923 if(!face)
924 {
925 /* TODO: load some default face */
926 if(ui_nobody)
927 face=gdk_pixbuf_new_from_file_at_scale(ui_nobody,48,48,TRUE,NULL);
928 if(!face)
929 face=gtk_icon_theme_load_icon(gtk_icon_theme_get_default(),
930 "avatar-default", 48,GTK_ICON_LOOKUP_FORCE_SIZE,NULL);
931 }
932 display=g_strdup_printf("<span font_size=\"x-large\">%s</span>%s%s%s%s",
933 gecos?gecos:users[i],
934 (gecos&&strcmp(gecos,users[i]))?"(":"",
935 (gecos&&strcmp(gecos,users[i]))?users[i]:"",
936 (gecos&&strcmp(gecos,users[i]))?")":"",
937 login?_("\n<i>logged in</i>"):"");
938 // don't translate it now, not freeze
939 gtk_list_store_set(model,&iter,0,face,1,display,2,users[i],3,gecos,4,login,-1);
940 if(face) g_object_unref(G_OBJECT(face));
941 g_free(display);
942 g_free(gecos);
943 g_free(face_path);
944 }
945 g_strfreev(users);
946 g_key_file_free(kf);
947
948 // add "More ..."
949 gtk_list_store_append(model,&iter);
950 gtk_list_store_set(model,&iter,1,_("More ..."),2,"",3,"",4,FALSE,-1);
951
952 path=gtk_tree_path_new_from_string("0");
953 gtk_icon_view_select_path(GTK_ICON_VIEW(widget),path);
954 gtk_widget_grab_focus(widget);
955 gtk_icon_view_set_cursor(GTK_ICON_VIEW(widget),path,NULL,FALSE);
956 gtk_tree_path_free(path);
957 return TRUE;
958 }
959
960 static void on_screen_size_changed(GdkScreen *screen,GtkWidget *win)
961 {
962 GdkRectangle rc;
963 GdkWindow *window;
964 #if GTK_CHECK_VERSION(3,0,0)
965 window=gtk_widget_get_window(win);
966 #else
967 window=win->window;
968 #endif
969 ui_get_geometry(window,&rc);
970 if(rc.width<1024 && g_key_file_get_integer(config, "display", "keyboard", 0)==1)
971 {
972 GtkWidget *w;
973 w=(GtkWidget*)gtk_builder_get_object(builder, "keyboard");
974 gtk_widget_hide(w);
975 w=(GtkWidget*)gtk_builder_get_object(builder, "label_keyboard");
976 gtk_widget_hide(w);
977 }
978
979 gtk_window_move(GTK_WINDOW(win),rc.x,rc.y);
980 gtk_window_resize(GTK_WINDOW(win),rc.width,rc.height);
981 ui_set_bg(window,config);
982 }
983
984 static void create_win()
985 {
986 GSList* objs, *l;
987 GtkWidget* w;
988 GdkWindow *window;
989 gchar *temp;
990 GdkRectangle rc;
991 GdkScreen *scr;
992
993 temp=g_key_file_get_string(config,"display","datetime",NULL);
994 if(temp && temp[0]=='%' && strlen(temp)<=3)
995 strcpy(datetime_fmt,temp);
996 g_free(temp);
997
998 builder = gtk_builder_new();
999 gtk_builder_add_from_file(builder, ui_file ? ui_file : LXDM_DATA_DIR "/lxdm.glade", NULL);
1000 win = (GtkWidget*)gtk_builder_get_object(builder, "lxdm");
1001 #if GTK_CHECK_VERSION(3,0,0)
1002 gtk_window_set_has_resize_grip(GTK_WINDOW(win),FALSE);
1003 #endif
1004 gtk_widget_realize(win);
1005 #if GTK_CHECK_VERSION(3,0,0)
1006 window=gtk_widget_get_window(win);
1007 #else
1008 window=win->window;
1009 #endif
1010
1011 /* set widget names according to their object id in GtkBuilder xml */
1012 objs = gtk_builder_get_objects(builder);
1013 for( l = objs; l; l = l->next )
1014 {
1015 char* path;
1016 GtkWidget* widget = (GtkWidget*)l->data;
1017 gtk_widget_set_name( widget, gtk_buildable_get_name( (GtkBuildable*)widget ) );
1018 gtk_widget_path(widget, NULL, &path, NULL);
1019 }
1020 g_slist_free(objs);
1021
1022 if(g_key_file_has_key(config,"display","bg",NULL ))
1023 {
1024 #if GTK_CHECK_VERSION(3,0,0)
1025 GtkStyleContext *style=gtk_widget_get_style_context(win);
1026 gtk_style_context_remove_class(style,GTK_STYLE_PROPERTY_BACKGROUND_IMAGE);
1027 #endif
1028 gtk_widget_set_app_paintable(win, TRUE);
1029
1030 } /* otherwise, let gtk theme paint it. */
1031
1032 user_list=(GtkWidget*)gtk_builder_get_object(builder,"user_list");
1033
1034 prompt = (GtkWidget*)gtk_builder_get_object(builder, "prompt");
1035 login_entry = (GtkWidget*)gtk_builder_get_object(builder, "login_entry");
1036
1037 g_signal_connect(login_entry, "activate", G_CALLBACK(on_entry_activate), NULL);
1038
1039 sessions = (GtkWidget*)gtk_builder_get_object(builder, "sessions");
1040 gtk_widget_set_name(sessions, "sessions");
1041 fix_combobox_entry(sessions);
1042 load_sessions();
1043
1044 w = (GtkWidget*)gtk_builder_get_object(builder, "bottom_pane");
1045 if( g_key_file_get_integer(config, "display", "bottom_pane", 0) )
1046 {
1047 /* hacks to let GtkEventBox paintable with gtk pixmap engine. */
1048 #if GTK_CHECK_VERSION(2,18,0)
1049 if(gtk_widget_get_app_paintable(w))
1050 #else
1051 if(GTK_WIDGET_APP_PAINTABLE(w))
1052 #endif
1053
1054 #if GTK_CHECK_VERSION(3,0,0)
1055 g_signal_connect(w,"draw",G_CALLBACK(on_evt_box_draw),NULL);
1056 #else
1057 g_signal_connect(w, "expose-event", G_CALLBACK(on_evt_box_expose), NULL);
1058 #endif
1059 }
1060 else
1061 gtk_event_box_set_visible_window(GTK_EVENT_BOX(w), FALSE);
1062
1063 if( g_key_file_get_integer(config, "display", "lang", 0) == 0 )
1064 {
1065 w = (GtkWidget*)gtk_builder_get_object(builder, "lang_box");
1066 if( w )
1067 gtk_widget_hide(w);
1068 }
1069 else
1070 {
1071 lang = (GtkWidget*)gtk_builder_get_object(builder, "lang");
1072 gtk_widget_set_name(lang, "lang");
1073 fix_combobox_entry(lang);
1074 load_langs();
1075 }
1076
1077 if(g_key_file_get_integer(config, "display", "keyboard", 0)==1)
1078 {
1079 w=(GtkWidget*)gtk_builder_get_object(builder, "keyboard");
1080 if((load_keyboards(w))!=FALSE)
1081 {
1082 fix_combobox_entry(w);
1083 gtk_widget_show(w);
1084 w=(GtkWidget*)gtk_builder_get_object(builder, "label_keyboard");
1085 if(w) gtk_widget_show(w);
1086 }
1087 }
1088
1089 if( (w = (GtkWidget*)gtk_builder_get_object(builder, "time"))!=NULL )
1090 {
1091 guint timeout = g_timeout_add(1000, (GSourceFunc)on_timeout, w);
1092 g_signal_connect_swapped(w, "destroy",
1093 G_CALLBACK(g_source_remove), GUINT_TO_POINTER(timeout));
1094 on_timeout((GtkLabel*)w);
1095 }
1096
1097 exit_btn = (GtkWidget*)gtk_builder_get_object(builder, "exit");
1098 load_exit();
1099
1100 ui_get_geometry(window,&rc);
1101 gtk_window_move(GTK_WINDOW(win),rc.x,rc.y);
1102 gtk_window_set_default_size(GTK_WINDOW(win),rc.width,rc.height);
1103 ui_set_bg(window,config);
1104
1105 if(user_list && !g_key_file_get_integer(config,"userlist","disable",NULL) &&
1106 load_user_list(user_list))
1107 {
1108 gtk_widget_hide(login_entry);
1109 }
1110 else
1111 {
1112 if(user_list)
1113 {
1114 gtk_widget_hide(user_list);
1115 user_list=NULL;
1116 }
1117 }
1118
1119 ui_add_cursor();
1120 ui_set_cursor(gtk_widget_get_window(win),GDK_LEFT_PTR);
1121 gtk_widget_show(win);
1122 ui_set_focus(window);
1123 if(!user_list)
1124 gtk_widget_grab_focus(login_entry);
1125
1126 scr = gtk_widget_get_screen(win);
1127 g_signal_connect(scr, "size-changed", G_CALLBACK(on_screen_size_changed), win);
1128 }
1129
1130 static gboolean on_lxdm_command(GIOChannel *source, GIOCondition condition, gpointer data)
1131 {
1132 GIOStatus ret;
1133 char *str;
1134
1135 if( !(G_IO_IN & condition) )
1136 return FALSE;
1137 ret = g_io_channel_read_line(source, &str, NULL, NULL, NULL);
1138 if( ret != G_IO_STATUS_NORMAL )
1139 return FALSE;
1140
1141 if( !strncmp(str, "quit", 4) || !strncmp(str, "exit",4))
1142 gtk_main_quit();
1143 else if( !strncmp(str, "reset", 5) )
1144 {
1145 if(user)
1146 {
1147 g_free(user);
1148 user=NULL;
1149 }
1150 if(pass)
1151 {
1152 g_free(pass);
1153 pass=NULL;
1154 }
1155 gtk_label_set_text( GTK_LABEL(prompt), _("User:"));
1156 gtk_widget_show(prompt);
1157 if(user_list)
1158 {
1159 gtk_widget_hide(login_entry);
1160 gtk_widget_show(user_list);
1161 gtk_widget_grab_focus(user_list);
1162 }
1163 else
1164 {
1165 gtk_widget_show(login_entry);
1166 gtk_widget_grab_focus(login_entry);
1167 }
1168 }
1169 g_free(str);
1170 return TRUE;
1171 }
1172
1173 void listen_stdin(void)
1174 {
1175 greeter_io = g_io_channel_unix_new(0);
1176 g_io_add_watch(greeter_io, G_IO_IN, on_lxdm_command, NULL);
1177 }
1178
1179 static void apply_theme(const char* theme_name)
1180 {
1181 char* theme_dir = g_build_filename(LXDM_DATA_DIR "/themes", theme_name, NULL);
1182 char *rc;
1183
1184 #if GTK_CHECK_VERSION(3,0,0)
1185 ui_file = g_build_filename(theme_dir, "greeter-gtk3.ui", NULL);
1186 rc=g_build_filename(theme_dir, "gtk.css", NULL);
1187 GtkCssProvider *css=gtk_css_provider_new();
1188 gtk_css_provider_load_from_path(css,rc,NULL);
1189 g_free(rc);
1190 gtk_style_context_add_provider_for_screen(gdk_screen_get_default(),
1191 GTK_STYLE_PROVIDER(css),
1192 GTK_STYLE_PROVIDER_PRIORITY_USER);
1193 #else
1194 ui_file = g_build_filename(theme_dir, "greeter.ui", NULL);
1195 rc = g_build_filename(theme_dir, "gtkrc", NULL);
1196 if( g_file_test(rc, G_FILE_TEST_EXISTS) )
1197 {
1198 gtk_rc_parse(rc);
1199 }
1200 g_free(rc);
1201 #endif
1202
1203 if( !g_file_test(ui_file, G_FILE_TEST_EXISTS) )
1204 {
1205 g_free(ui_file);
1206 ui_file = NULL;
1207 }
1208
1209 #if GTK_CHECK_VERSION(3,0,0)
1210
1211 #endif
1212
1213 ui_nobody = g_build_filename(theme_dir, "nobody.png", NULL);
1214 if( !g_file_test(ui_nobody, G_FILE_TEST_EXISTS) )
1215 {
1216 g_free(ui_nobody);
1217 ui_nobody = NULL;
1218 }
1219
1220 g_free(theme_dir);
1221 }
1222
1223 int main(int arc, char *arg[])
1224 {
1225 char* theme_name;
1226 GtkSettings *settings;
1227 int i;
1228
1229 /* this will override LC_MESSAGES */
1230 unsetenv("LANGUAGE");
1231
1232 #if !GTK_CHECK_VERSION(3,0,0)
1233 gtk_set_locale();
1234 #endif
1235 bindtextdomain("lxdm", "/usr/share/locale");
1236 textdomain("lxdm");
1237
1238 config = g_key_file_new();
1239 g_key_file_load_from_file(config, CONFIG_FILE, G_KEY_FILE_KEEP_COMMENTS, NULL);
1240 var_config = g_key_file_new();
1241 g_key_file_set_list_separator(var_config, ' ');
1242 g_key_file_load_from_file(var_config,VCONFIG_FILE,G_KEY_FILE_KEEP_COMMENTS, NULL);
1243
1244 gtk_init(&arc, &arg);
1245 for(i=1;i<arc;i++)
1246 {
1247 if(!strcmp(arg[i],"--auto-login"))
1248 {
1249 auto_login=g_key_file_get_integer(config,"base","timeout",NULL);
1250 }
1251 }
1252
1253 settings=gtk_settings_get_default();
1254 if(settings)
1255 {
1256 setenv("GTK_IM_MODULE","gtk-im-context-simple",1);
1257 gtk_settings_set_string_property(settings,"gtk-im-module","gtk-im-context-simple",0);
1258 gtk_settings_set_long_property(settings,"gtk-show-input-method-menu",0,0);
1259 }
1260
1261 /* set gtk+ theme */
1262 theme_name = g_key_file_get_string(config, "display", "gtk_theme", NULL);
1263 if(theme_name)
1264 {
1265 if(settings)
1266 g_object_set(settings, "gtk-theme-name", theme_name, NULL);
1267 g_free(theme_name);
1268 }
1269
1270 /* load gtkrc-based themes */
1271 theme_name = g_key_file_get_string(config, "display", "theme", NULL);
1272 if( theme_name ) /* theme is specified */
1273 {
1274 apply_theme(theme_name);
1275 g_free(theme_name);
1276 }
1277
1278 /* create the login window */
1279 create_win();
1280 listen_stdin();
1281 /* use line buffered stdout for inter-process-communcation of
1282 * single-line-commands */
1283 setvbuf(stdout, NULL, _IOLBF, 0 );
1284
1285 if(auto_login)
1286 {
1287 g_timeout_add_seconds(auto_login,autologin_timeout,NULL);
1288 }
1289 gtk_main();
1290
1291 g_key_file_free(config);
1292 g_key_file_free(var_config);
1293
1294 return 0;
1295 }