/***************************************************************************
                      directfb_mode.c  -  description
                            --------------------
    begin                : Apr 10 2003
    copyright            : (C) 2003-2006 by Noberasco Michele
    e-mail               : michele.noberasco@tiscali.it
***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.              *
 *                                                                         *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

/* system and DirectFB library stuff */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <directfb.h>
#include <directfb_keynames.h>
#include <signal.h>
#include <pthread.h>


#ifdef WANT_CRYPTO
#include "crypto.h"
#endif

/* misc stuff */
#include "memmgmt.h"
#include "vt.h"
#include "misc.h"
#include "session.h"
#include "load_settings.h"
#include "logger.h"
#include "keybindings.h"

/* my custom DirectFB stuff */
#include "utils.h"
#include "button.h"
#include "textbox.h"
#include "combobox.h"
#include "label.h"
#include "pm.h"


/* some super-structures */
typedef struct label_t
{
  Label *label;
  struct label_t *next;
} Label_list;
typedef struct button_t
{
  Button *button;
  struct button_t *next;
} Button_list;

#undef  exit
#define exit safe_exit


/*
 * Global variables... definitely too many!
 */

Label_list            *Labels             = NULL; /* This will contain all labels              */
Button_list           *Buttons            = NULL; /* This will contain all text boxes          */
IDirectFB             *dfb;                       /* the super interface                       */
IDirectFBDisplayLayer *layer;                     /* the primary layer                         */
IDirectFBSurface      *primary;                   /* surface of the primary layer              */
IDirectFBSurface      *panel_image        = NULL; /* background image                          */
IDirectFBSurface      *curs_surface       = NULL; /* main cursor shape                         */
IDirectFBEventBuffer  *events;                    /* all input events will be stored here      */
DeviceInfo            *devices            = NULL; /* the list of all input devices             */
IDirectFBFont         *font_tiny;                 /* fonts                                     */
IDirectFBFont         *font_smaller;  
IDirectFBFont         *font_small;
IDirectFBFont         *font_normal;
IDirectFBFont         *font_large;
TextBox               *username           = NULL; /* text boxes                                */
TextBox               *password           = NULL;
Label                 *username_label     = NULL; /* labels                                    */
Label                 *password_label     = NULL;
Label                 *session_label      = NULL;
Label                 *lock_key_statusA   = NULL;
Label                 *lock_key_statusB   = NULL;
Label                 *lock_key_statusC   = NULL;
Label                 *lock_key_statusD   = NULL;
ComboBox              *session            = NULL; /* combo boxes */
int                   screen_width;               /* screen resolution */
int                   screen_height;
int                   font_tiny_height;           /* font sizes */
int                   font_smaller_height;
int                   font_small_height;
int                   font_normal_height;
int                   font_large_height;
#ifdef USE_SCREEN_SAVERS
int                   screensaver_active    = 0; /* screensaver stuff */
int                   screensaver_countdown = 0;
#endif
float                 x_ratio               = 1; /* theme res. should be corrected by x_ratio */
float                 y_ratio               = 1; /* and y_ratio to match the actual res. */
int                   ppid                  = 0; /* process id of out parent */
int                   thread_action         = 0; /* whether one of our threads is doing anything significant */
pthread_mutex_t       lock_act              = PTHREAD_MUTEX_INITIALIZER; /* action lock */


void safe_exit(int exitstatus)
{
#undef exit

  unlock_tty_switching();
  exit(exitstatus);

#define exit safe_exit
}

void Draw_Background_Image(int do_the_drawing)
{
  /* width and height of the background image */
  static int panel_width, panel_height;
  /* We clear the primary surface */
  primary->Clear (primary, 0x00, 0x00, 0x00, 0xFF);
  if (!panel_image)
	{ /* we design the surface */
		if (background)  panel_image = load_image (background, dfb, x_ratio, y_ratio);
		if (panel_image) panel_image->GetSize (panel_image, &panel_width, &panel_height);
	}
  /*	
   * we put the backgound image in the center of the screen if it fits
   * the screen otherwise we stretch it to make it fit
   */
  if (panel_image)
	{
		if ( (panel_width <= screen_width) && (panel_height <= screen_height) )
			primary->Blit (primary, panel_image, NULL, (screen_width - panel_width)/2, (screen_height - panel_height)/2);
		else primary->StretchBlit (primary, panel_image, NULL, NULL);
	}
  
  /* we draw the surface, duplicating it */
  if (do_the_drawing) primary->Flip (primary, NULL, DSFLIP_BLIT);
}

/*
 * plot previous session of <user> if it exists,
 * otherwise plot default vaule
 */
void set_user_session(char *user)
{
  char *user_session = NULL;
	int   i            = 0;
  
  if (!session || !session->items)
		return;

	user_session = get_last_session(user);
  
  if (user_session)
	{
		for (; i<session->n_items; i++)
			if (!strcmp(session->items[i], user_session))
			{
				if (session->items[i] != session->selected)
				{
					session->SelectItem(session, session->items[i]);
					free(user_session);
					return;
				}
				else return;
			}

		free(user_session);
	}
		
	for (i=0; i<session->n_items; i++)
		if (!strcmp(session->items[i], "Text: Console"))
			if (session->items[i] != session->selected)
				session->SelectItem(session, session->items[i]);
}

void close_framebuffer_mode (int exit_status)
{
	/* write our exit status */
	fprintf(stdout, "%d\n", exit_status);
	fflush(stdout);
	kill (ppid, SIGUSR2);

	/* since we are shutting down, there is no point in deallocating all stuff nicely, as
	 * it is only a waste of time and CPU cycles.
	 */

	writelog(DEBUG,"Starting GUI shutdown...\n");

	/* disable bogus error messages on DirectFB exit */
  stderr_disable();
/* 	if (dfb) dfb->Release (dfb); */
/* 	stderr_enable(&current_tty); */

	unlock_tty_switching();

	writelog(DEBUG,"GUI shutdown complete\n");
}

void DirectFB_Error()
{
  writelog(ERROR, "Unrecoverable DirectFB error: reverting to text mode!\n"); /* dammit! */
  writelog(ERROR, "This usually means that you have an issue with DirectFB, not with qingy...\n");
  writelog(ERROR, "Most likely you don't have your console framebuffer properly set up.\n");
  writelog(ERROR, "Please turn on DEBUG log level in qingy to see exactly why DirectFB is letting you down.\n");
	close_framebuffer_mode(GUI_FAILURE);
}

void show_lock_key_status(DFBInputEvent *evt)
{
	static int status = 0;

	if (evt)
		status = (lock_is_pressed(evt) == CAPSLOCK) ? 1 : 0;

  if (status)
	{ /* CAPS lock is active */
		lock_key_statusA->Show(lock_key_statusA);
		lock_key_statusB->Show(lock_key_statusB);
		lock_key_statusC->Show(lock_key_statusC);
		lock_key_statusD->Show(lock_key_statusD);
	}
  else
	{ /* CAPS lock is not active */
		lock_key_statusA->Hide(lock_key_statusA);
		lock_key_statusB->Hide(lock_key_statusB);
		lock_key_statusC->Hide(lock_key_statusC);
		lock_key_statusD->Hide(lock_key_statusD);
	}
}

/* this redraws the login screen */
void reset_screen(DFBInputEvent *evt)
{
  Label_list  *labels  = Labels;
  Button_list *buttons = Buttons;
  
  Draw_Background_Image (1);
  
  /* redraw all labels */
  while (labels)
	{
		labels->label->Show(labels->label);
		labels = labels->next;
	}
 
	/* redraw all buttons */
  while (buttons)
	{
		buttons->button->Show(buttons->button);
		buttons = buttons->next;
	}
  
  username->Show(username);
  password->Show(password);
  session->Show(session);

	if (evt->type == DIET_KEYPRESS || evt->type == DIET_KEYRELEASE)
		show_lock_key_status(evt);
	else
		show_lock_key_status(NULL);

	if (!cursor)
		layer->EnableCursor (layer, 1);
	else
		layer->EnableCursor (layer, cursor->enable);
}

/* this clears the screen */
void clear_screen(void)
{
  Label_list  *labels  = Labels;
  Button_list *buttons = Buttons;
  
  /* hide all labels */
  while (labels)
	{
		labels->label->Hide(labels->label);
		labels = labels->next;
	}

  /* hide all buttons */
  while (buttons)
	{
		buttons->button->Hide(buttons->button);
		buttons = buttons->next;
	}
  
  username->Hide(username);
  password->Hide(password);
  lock_key_statusA->Hide(lock_key_statusA);
  lock_key_statusB->Hide(lock_key_statusB);
  lock_key_statusC->Hide(lock_key_statusC);
  lock_key_statusD->Hide(lock_key_statusD);
  session->Hide(session);
  layer->EnableCursor (layer, 0);
  primary->Clear (primary, 0x00, 0x00, 0x00, 0xFF);
	if (!clear_background) Draw_Background_Image(1);
	else primary->Flip (primary, NULL, DSFLIP_BLIT);
	primary->SetFont (primary, font_large);
  primary->SetColor (primary, other_text_color.R, other_text_color.G, other_text_color.B, other_text_color.A);
}

void begin_shutdown_sequence (actions action, IDirectFBEventBuffer *events)
{
  DFBInputEvent evt;
  int countdown = countdown_timeout;

  clear_screen();
  
  /* First, we check shutdown policy */
  switch (shutdown_policy)
	{
    case NOONE: /* no one is allowed to shut down the system */
			if (action == DO_SLEEP)
				primary->DrawString (primary, sleep_forbidden_message, -1, screen_width / 2, screen_height / 2, DSTF_CENTER);
			else
				primary->DrawString (primary, shutdown_forbidden_message, -1, screen_width / 2, screen_height / 2, DSTF_CENTER);
      primary->Flip (primary, NULL, 0);
      sleep(info_message_timeout);
      events->GetEvent(events, DFB_EVENT (&evt));
      reset_screen(&evt);
      return;
    case ROOT: /* only root can shutdown the system */
      if (!gui_check_password("root", password->text, session->selected, ppid))
			{
				if (action == DO_SLEEP)
					primary->DrawString (primary, sleep_password_message, -1, screen_width / 2, screen_height / 2, DSTF_CENTER);
				else
					primary->DrawString (primary, shutdown_password_message, -1, screen_width / 2, screen_height / 2, DSTF_CENTER);
				primary->Flip (primary, NULL, 0);
				sleep(info_message_timeout);
				events->GetEvent(events, DFB_EVENT (&evt));
				reset_screen(&evt);
				return;
			}
      break;
    case EVERYONE: /* everyone can shutdown, so we do nothing here */
      break;
	}
  
  /* we wait for <countdown> seconds */
  while (countdown >= 0)
	{
		char *message = NULL;
		char *pos     = NULL;

		while ((events->GetEvent (events, DFB_EVENT (&evt))) == DFB_OK)
			if (evt.type == DIET_KEYPRESS)
				if (evt.key_symbol == DIKS_ESCAPE)
				{ /* user aborted sequence */
					reset_screen(&evt);
					return;
				}
		if (!countdown) break;

		switch (action)
		{
			case DO_POWEROFF:
				message = strdup(shutdown_timeout_message);
				break;
			case DO_REBOOT:
				message = strdup(restart_timeout_message);
				break;
			case DO_SLEEP:
				message = strdup(sleep_timeout_message);
				break;
			default: /* other actions do not concern us */
				break;
		}

		while ((pos=strstr(message, "<INS_TIMEOUT_HERE>")))
		{
			char *prev       = strndup(message, pos - message);
			char *temp       = int_to_str(countdown);
			char *my_message = StrApp((char**)NULL, prev, temp, pos+18, (char*)NULL);

			free(prev);
			free(temp);
			free(message);

			message = my_message;
		}

		primary->Clear (primary, 0x00, 0x00, 0x00, 0xFF);
		if (!clear_background) Draw_Background_Image(0);
		primary->DrawString (primary, abort_message, -1, 0, screen_height, DSTF_LEFT | DSTF_BOTTOM);
		primary->DrawString (primary, message, -1, screen_width / 2, screen_height / 2, DSTF_CENTER);
		primary->Flip (primary, NULL, 0);
		sleep (1);
		countdown--;

		free(message);
	}
  if (no_shutdown_screen || (action == DO_SLEEP))
	{
		if (action == DO_POWEROFF) close_framebuffer_mode (EXIT_SHUTDOWN_H);
		if (action == DO_REBOOT)   close_framebuffer_mode (EXIT_SHUTDOWN_R);
		if (action == DO_SLEEP)    close_framebuffer_mode (EXIT_SLEEP);
		exit(EXIT_SUCCESS);
	}
  else
	{
		primary->Clear (primary, 0x00, 0x00, 0x00, 0xFF);
		Draw_Background_Image(0);
	}
  if (action == DO_POWEROFF)
	{
		if (!no_shutdown_screen)
		{
			primary->DrawString (primary, shutdown_message, -1, screen_width / 2, screen_height / 2, DSTF_CENTER);
			primary->Flip (primary, NULL, 0);
		}
		execl ("/sbin/shutdown", "/sbin/shutdown", "-h", "now", (char*)NULL);
	}
  if (action == DO_REBOOT)
	{
		if (!no_shutdown_screen)
		{
			primary->DrawString (primary, restart_message, -1, screen_width / 2, screen_height / 2, DSTF_CENTER);
			primary->Flip (primary, NULL, 0);
		}
		execl ("/sbin/shutdown", "/sbin/shutdown", "-r", "now", (char*)NULL);
	}
  
  /* we should never get here unless call to /sbin/shutdown fails */
  writelog(ERROR, "Unable to exec \"/sbin/shutdown\"!\n");
  if (!no_shutdown_screen) close_framebuffer_mode (GUI_FAILURE);
  exit (GUI_FAILURE);
}

void do_ctrl_alt_del(DFBInputEvent *evt)
{
  char *action = parse_inittab_file();
  
  if (!action) return;	
  if (!strcmp(action, "poweroff")) {free(action); begin_shutdown_sequence (DO_POWEROFF, events); return;}
  if (!strcmp(action, "reboot"))   {free(action); begin_shutdown_sequence (DO_REBOOT,   events); return;}
  
  /* we display a message */
  clear_screen();
  primary->DrawString (primary, action, -1, screen_width / 2, screen_height / 2, DSTF_CENTER);
  primary->Flip (primary, NULL, 0);
  sleep(info_message_timeout);
  reset_screen(evt);
  free(action);
}

/* callback function to handle label clicks */
void label_click(Label *label)
{
	if (!label)          return;
	if (label->hasfocus) return;

	if (label == username_label)
	{	/* username area has been clicked! */
		username->SetFocus(username, 1);
		password->SetFocus(password, 0);
		if (password_label) password_label->SetFocus(password_label, 0);
		session->SetFocus(session, 0);
		if (session_label) session_label->SetFocus(session_label, 0);
	}

	if (label == password_label)
	{	/* password area has been clicked! */
		username->SetFocus(username, 0);
		if (username_label) username_label->SetFocus(username_label, 0);
		password->SetFocus(password, 1);
		session->SetFocus(session, 0);
		if (session_label) session_label->SetFocus(session_label, 0);
	}

	if (label == session_label)
	{	/* session area has been clicked! */
		username->SetFocus(username, 0);
		if (username_label) username_label->SetFocus(username_label, 0);
		password->SetFocus(password, 0);
		if (password_label) password_label->SetFocus(password_label, 0);
		session->SetFocus(session, 1);
	}
}

/* callback function to handle textbox clicks */
void textbox_click(TextBox *textbox)
{
	if (!textbox)          return;
	if (textbox->hasfocus) return;

	if (textbox == username)
	{	/* username area has been clicked! */
		if (username_label) username_label->SetFocus(username_label, 1);
		password->SetFocus(password, 0);
		if (password_label) password_label->SetFocus(password_label, 0);
		session->SetFocus(session, 0);
		if (session_label) session_label->SetFocus(session_label, 0);
	}

	if (textbox == password)
	{	/* password area has been clicked! */
		username->SetFocus(username, 0);
		if (username_label) username_label->SetFocus(username_label, 0);
		if (password_label) password_label->SetFocus(password_label, 1);
		session->SetFocus(session, 0);
		if (session_label) session_label->SetFocus(session_label, 0);
	}
}

/* callback function to handle combobox clicks */
void combobox_click(ComboBox *combobox)
{
	if (!combobox)          return;
	if (combobox->hasfocus) return;

	if (combobox == session)
	{	/* session area has been clicked! */
		if (session_label) session_label->SetFocus(session_label, 1);
		username->SetFocus(username, 0);
		if (username_label) username_label->SetFocus(username_label, 0);
		password->SetFocus(password, 0);
		if (password_label) password_label->SetFocus(password_label, 0);
	}
}

/* callback function to handle button clicks */
void button_click(Button *button)
{
	int doit = 0;

	if (!button) return;

	/* main process is forbidden to perform actions until we are done,
	 * unless, of course, it is already doing something, in which case
	 * we wait for it to finish
	 */
	while (!doit)
	{
		pthread_mutex_lock(&lock_act);
		if (!thread_action)
		{
			thread_action = 1;
			doit          = 1;
		}
		if (!doit)
		{
			pthread_mutex_unlock(&lock_act);
			struct timespec ts;
			ts.tv_sec = 0;
			ts.tv_nsec = 10000000;
			sleep(1);
		}
	}
	pthread_mutex_unlock(&lock_act);

	switch (button->action)
	{
		case DO_POWEROFF:
		case DO_REBOOT:
			begin_shutdown_sequence (button->action, button->events);
			break;
		case DO_SCREEN_SAVER:
#ifdef USE_SCREEN_SAVERS
			do_ss(dfb, primary, font_large, 0);
#endif
			break;
		case DO_SLEEP:
			if (!sleep_cmd)
			{
				DFBInputEvent evt;
					
				clear_screen();
				primary->DrawString (primary, sleep_cmd_message, -1, screen_width / 2, screen_height / 2, DSTF_CENTER);
				primary->Flip (primary, NULL, 0);
				sleep(info_message_timeout);
				events->GetEvent (events, DFB_EVENT (&evt));
				reset_screen(&evt);
				break;
			}

			begin_shutdown_sequence (DO_SLEEP, button->events);
			break;
		default: /* no action */
			break;
	}

	pthread_mutex_lock(&lock_act);
	thread_action = 0;
	pthread_mutex_unlock(&lock_act);
}

void start_login_sequence(DFBInputEvent *evt)
{
	int   result;
  int   free_temp = 0;
  char *message;
  char *welcome_msg;
  char *temp;

  if (!strlen(username->text)) return;	
  message = StrApp((char**)NULL, login_message, " ", username->text, "...", (char*)NULL);
  clear_screen();
  primary->DrawString (primary, message, -1, screen_width / 2, screen_height / 2, DSTF_CENTER);
  free(message);
  primary->Flip (primary, NULL, DSFLIP_BLIT);

  if (hide_last_user && !strcmp(username->text, "lastuser"))
	{
		temp = get_last_user();
		free_temp = 1;
	}
  else temp = username->text;

	result = gui_check_password(temp, password->text, session->selected, ppid);
	switch (result)
	{
		case 1: /* login success */
			primary->Clear (primary, 0x00, 0x00, 0x00, 0xFF);
			if (!clear_background) Draw_Background_Image(0);
			welcome_msg = get_welcome_msg(temp);
			primary->DrawString (primary, welcome_msg, -1, screen_width / 2, screen_height / 2, DSTF_CENTER);
			primary->Flip (primary, NULL, DSFLIP_BLIT);
			free(welcome_msg);
			sleep(welcome_message_timeout);
			if (free_temp) free(temp);

			close_framebuffer_mode(EXIT_SUCCESS);
			exit(EXIT_SUCCESS);
			break;

		case 0: /* login failure */
			message = StrApp((char**)NULL, login_failed_message, (char*)NULL);
			break;

		default: /* crypto error occurred */
			message = StrApp((char**)NULL, crypto_error_message, (char*)NULL);
			break;
	}

	primary->Clear (primary, 0x00, 0x00, 0x00, 0xFF);
	if (!clear_background) Draw_Background_Image(0);
	primary->DrawString (primary, message, -1, screen_width / 2, screen_height / 2, DSTF_CENTER);
	primary->Flip (primary, NULL, DSFLIP_BLIT);
	sleep(info_message_timeout);
	password->ClearText(password);
	reset_screen(evt);
	if (free_temp) free(temp);
}

void handle_mouse_event()
{
	if (curs_surface)
		if (!pthread_mutex_trylock(lock_mouse_cursor))
		{
			layer->SetCursorShape (layer, curs_surface, cursor->x_off, cursor->y_off);
			pthread_mutex_unlock(lock_mouse_cursor);
		}
}

int handle_keyboard_event(DFBInputEvent *evt)
{
  struct DFBKeySymbolName *symbol_name; /* we store here the symbol name of the last key the user pressed */
	actions    action;
  int        returnstatus   =       -1;
  int        allow_tabbing  =        1;
  modifiers  modifier       =        NONE;
  int        ascii_code     = (int)  evt->key_symbol;

  show_lock_key_status(evt);
  symbol_name = bsearch (&(evt->key_symbol), keynames, 
												 sizeof (keynames) / sizeof (keynames[0]) - 1,
												 sizeof (keynames[0]), compare_symbol);

	modifier = modifier_is_pressed(evt);

	/* Let's check wether our user pressed some of our allowed key bindings... */
	action = search_keybindings(modifier, ascii_code);
	if (action != DO_NOTHING)
	{
		WRITELOG(DEBUG, "You pressed '%s%s' and I will now %s...\n", print_modifier(modifier), print_key(ascii_code), print_action(action));
		
		switch (action)
		{
			case DO_POWEROFF:
			case DO_REBOOT:
				begin_shutdown_sequence (action, events);
				break;
			case DO_KILL:
				close_framebuffer_mode(EXIT_RESPAWN);
				exit(EXIT_SUCCESS);
				break;
			case DO_SCREEN_SAVER:
#ifdef USE_SCREEN_SAVERS
				do_ss(dfb, primary, font_large, 0);
#endif
				break;
			case DO_SLEEP:
				if (!sleep_cmd)
				{
					DFBInputEvent evt;

					clear_screen();
					primary->DrawString (primary, sleep_cmd_message, -1, screen_width / 2, screen_height / 2, DSTF_CENTER);
					primary->Flip (primary, NULL, 0);
					sleep(info_message_timeout);
					events->GetEvent (events, DFB_EVENT (&evt));
					reset_screen(&evt);
					break;
				}
				begin_shutdown_sequence (DO_SLEEP, events);
				break;
			case DO_NEXT_TTY:
				return (current_tty + 1);
				break;
			case DO_PREV_TTY:
				return (current_tty - 1);
				break;
			case DO_TEXT_MODE:
				return EXIT_TEXT_MODE;
				break;
			case DO_NOTHING: /* Guess what, we do nothing here! */
				break;
		}
	}

	if (modifier == ALT || modifier == CTRLALT)
	{ 
		if (symbol_name)
		{
			/* we check if the user is pressing ctrl-alt-del... */
			if (!strcmp(symbol_name->name, "DELETE") && modifier == CTRLALT)
				do_ctrl_alt_del(evt);
			/*
			 * ... or [CTRL-]ALT-number with 1 <= number <= 12
			 * in this case we close directfb mode and send him to that tty
			 */
			if (!strncmp(symbol_name->name, "F", 1) && strlen (symbol_name->name) <= 3)
			{
				int temp = atoi (symbol_name->name + 1);
				if ((temp > 0) && (temp < 13))
					if (current_tty != temp)
						return temp;
			}
		}
		return returnstatus;
	}

  if (symbol_name)
	{
		/* ctr-j is alias for RETURN; */
		if (modifier == CONTROL && ascii_code == 'j') ascii_code = RETURN;

		/* Rock'n Roll! */
		if (!username->hasfocus && !session->isclicked && ascii_code == RETURN) start_login_sequence(evt);

		/* user name events */
		if (username->hasfocus && allow_tabbing)
		{
			if (ascii_code == TAB || ascii_code == RETURN)
	    {
	      allow_tabbing = 0;
	      if (username_label) username_label->SetFocus(username_label, 0);
	      username->SetFocus(username, 0);
	      if (modifier_is_pressed(evt) != SHIFT)
				{
					if (password_label) password_label->SetFocus(password_label, 1);
					password->SetFocus(password, 1);
				}
	      else
				{
					if (session_label) session_label->SetFocus(session_label, 1);
					session->SetFocus(session, 1);
				}
	    }
			else
	    {
				pthread_mutex_lock(&(username->lock));
				set_user_session(username->text);
				pthread_mutex_unlock(&(username->lock));
	    }
		}

		/* password events */
		if (password->hasfocus && allow_tabbing)
		{
			if (ascii_code == TAB)
	    {
	      allow_tabbing = 0;
	      if (password_label) password_label->SetFocus(password_label, 0);
	      password->SetFocus(password, 0);
	      if (modifier_is_pressed(evt) != SHIFT)
				{
					if (session_label) session_label->SetFocus(session_label, 1);
					session->SetFocus(session, 1);
				}
	      else
				{
					if (username_label) username_label->SetFocus(username_label, 1);
					username->SetFocus(username, 1);
				}
	    }
		}

		/* session events */
		if (session->hasfocus && allow_tabbing)
		{
			if (ascii_code == TAB)
	    {
	      allow_tabbing = 0;
	      if (session_label) session_label->SetFocus(session_label, 0);
	      session->SetFocus(session, 0);
	      if (modifier_is_pressed(evt) != SHIFT)
				{
					if (username_label) username_label->SetFocus(username_label, 1);
					username->SetFocus(username, 1);
				}
	      else
				{
					if (password_label) password_label->SetFocus(password_label, 1);
					password->SetFocus(password, 1);
				}
	    }
		}
	}

  return returnstatus;
}

void load_sessions(ComboBox *session)
{
  char *temp;

	/* we get all available sessions... */
  while ((temp = get_sessions()) != NULL)
	{
		session->AddItem(session, temp);
		free(temp);
	}

	/* ...and order them */
	session->SortItems(session);
}

int create_windows()
{
  DFBWindowDescription window_desc;
  IDirectFBFont *font;
  window_t *window = windowsList;
	char *message = caps_message;
	int width;

  window_desc.flags = ( DWDESC_POSX | DWDESC_POSY | DWDESC_WIDTH | DWDESC_HEIGHT | DWDESC_CAPS );
  window_desc.caps  = DWCAPS_ALPHACHANNEL;

  while (window)
	{
		window_desc.posx   = window->x      * screen_width  / theme_xres;
		window_desc.posy   = window->y      * screen_height / theme_yres;
		window_desc.width  = window->width  * screen_width  / theme_xres;
		window_desc.height = window->height * screen_height / theme_yres;	

		switch(window->text_size)
		{
			case TINY:
				font = font_tiny;
				break;
			case SMALLER:
				font = font_smaller;
				break;
			case SMALL:
				font = font_small;
				break;
			case MEDIUM:
				font = font_normal;
				break;
			case LARGE: /* fall to default */
			default:
				font = font_large;
				break;
		}

		/* what kind of window are we going to create? */
		switch (window->type)
		{
			case LOGIN:
				username = TextBox_Create(layer, dfb, font, window->text_color, window->cursor_color, &window_desc);
				if (!username) return 0;
				if (window->cursor) username->SetCursor(username, dfb, window->cursor, x_ratio, y_ratio);
				username->SetClickCallBack(username, textbox_click);
				break;
			case PASSWORD:
				password = TextBox_Create(layer, dfb, font, window->text_color, window->cursor_color, &window_desc);
				if (!password) return 0;
				if (window->cursor) password->SetCursor(password, dfb, window->cursor, x_ratio, y_ratio);
				password->SetClickCallBack(password, textbox_click);
				break;
			case LABEL:
			{
				static Label_list *labels = NULL;

				if (!labels)
	      {
					labels = (Label_list *) calloc(1, sizeof(Label_list));
					Labels = labels;
	      }
				else
	      {
					labels->next = (Label_list *) calloc(1, sizeof(Label_list));
					labels = labels->next;
	      }
				labels->label = Label_Create(layer, dfb, font, window->text_color, &window_desc);
				if (!labels->label) return 0;
				labels->label->SetTextOrientation(labels->label, window->text_orientation);
				labels->label->SetAction(labels->label, window->polltime, window->content, window->command);
				labels->label->SetFocus(labels->label, 0);
				labels->next = NULL;
				if (window->linkto)
	      {
					if (!strcmp(window->linkto, "login"))    username_label = labels->label;
					if (!strcmp(window->linkto, "password")) password_label = labels->label;
					if (!strcmp(window->linkto, "session"))  session_label  = labels->label;
					labels->label->SetClickCallBack(labels->label, label_click);
	      }
				else
				{
					labels->label->window->SetOpacity(labels->label->window, selected_window_opacity);
				}

				break;
			}
			case BUTTON:
			{
				static Button_list *buttons = NULL;
				char *image1, *image2;

				if (!buttons)
	      {
					buttons = (Button_list *) calloc(1, sizeof(Button_list));
					Buttons = buttons;
	      }
				else
	      {
					buttons->next = (Button_list *) calloc(1, sizeof(Button_list));
					buttons = buttons->next;
	      }
				image1 = StrApp((char **)NULL, theme_dir, window->content, "_normal.png",    (char *)NULL);
				image2 = StrApp((char **)NULL, theme_dir, window->content, "_mouseover.png", (char *)NULL);
				buttons->button = Button_Create(image1, image2, window_desc.posx, window_desc.posy, button_click, layer, primary, dfb, x_ratio, y_ratio);
				if (!buttons->button) return 0;
				buttons->next = NULL;
				free(image1); free(image2);
				if (!window->command)
					buttons->button->action = DO_NOTHING;
				else
				{
					if (!strcmp(window->command, "halt"       )) buttons->button->action = DO_POWEROFF;
					if (!strcmp(window->command, "reboot"     )) buttons->button->action = DO_REBOOT;
					if (!strcmp(window->command, "sleep"      )) buttons->button->action = DO_SLEEP;
					if (!strcmp(window->command, "screensaver")) buttons->button->action = DO_SCREEN_SAVER;
				}
				if (window->cursor) buttons->button->SetCursor(buttons->button, dfb, window->cursor, x_ratio, y_ratio);

				break;
			}
			case COMBO:
				if (window->type == COMBO && !strcmp(window->command, "sessions"))
				{
					session = ComboBox_Create(layer, dfb, font, window->text_color, &window_desc, screen_width, screen_height);
					if (!session) return 0;
					session->SetSortFunction(session, sort_sessions);
					session->SetClickCallBack(session, combobox_click);
					if (window->cursor) session->SetCursor(session, dfb, window->cursor, x_ratio, y_ratio);
				}

				break;
			default:
				return 0;
		}

		window = window->next;
	}
  destroy_windows_list(windowsList);

  /* Finally we create the four "CAPS LOCK is pressed" windows... */
	font_small->GetStringWidth (font_small, message, -1, &width);
  window_desc.posx   = 0;
  window_desc.posy   = screen_height - (font_small_height);
  window_desc.width  = width;
  window_desc.height = font_small_height;
  lock_key_statusA = Label_Create(layer, dfb, font_small, &other_text_color, &window_desc);
  if (!lock_key_statusA) return 0;
  lock_key_statusA->SetFocus(lock_key_statusA, 1);
  lock_key_statusA->Hide(lock_key_statusA);  
	lock_key_statusA->SetTextOrientation(lock_key_statusA, CENTERBOTTOM);
  lock_key_statusA->SetText(lock_key_statusA, message);
  window_desc.posy = 0;
  lock_key_statusB = Label_Create(layer, dfb, font_small, &other_text_color, &window_desc);
  if (!lock_key_statusB) return 0;
  lock_key_statusB->SetFocus(lock_key_statusB, 1);
  lock_key_statusB->Hide(lock_key_statusB);
	lock_key_statusB->SetTextOrientation(lock_key_statusB, LEFT);
  lock_key_statusB->SetText(lock_key_statusB, message);
  window_desc.posx = screen_width - width;
  window_desc.height = 2*font_small_height;
  lock_key_statusC = Label_Create(layer, dfb, font_small, &other_text_color, &window_desc);
  if (!lock_key_statusC) return 0;
  lock_key_statusC->SetFocus(lock_key_statusC, 1);
  lock_key_statusC->Hide(lock_key_statusC);
	lock_key_statusC->SetTextOrientation(lock_key_statusC, RIGHT);
  lock_key_statusC->SetText(lock_key_statusC, message);
  window_desc.posy = screen_height - (font_small_height);
  lock_key_statusD = Label_Create(layer, dfb, font_small, &other_text_color, &window_desc);
  if (!lock_key_statusD) return 0;
  lock_key_statusD->SetFocus(lock_key_statusD, 1);
  lock_key_statusD->Hide(lock_key_statusD);
	lock_key_statusD->SetTextOrientation(lock_key_statusD, RIGHT);
  lock_key_statusD->SetText(lock_key_statusD, message);

  return 1;
}

int set_font_sizes ()
{
  DFBFontDescription fdsc;
  const char *fontfile = font;

  fdsc.flags = DFDESC_HEIGHT;
  fdsc.height = screen_width / 30;
  if (dfb->CreateFont (dfb, fontfile, &fdsc, &font_large) != DFB_OK) return 0;
  font_large_height = fdsc.height;
  fdsc.height = screen_width / 40;
  if (dfb->CreateFont (dfb, fontfile, &fdsc, &font_normal) != DFB_OK) return 0;
  font_normal_height = fdsc.height;
  fdsc.height = screen_width / 50;
  if (dfb->CreateFont (dfb, fontfile, &fdsc, &font_small) != DFB_OK) return 0;
  font_small_height = fdsc.height;
  fdsc.height = screen_width / 60;
  if (dfb->CreateFont (dfb, fontfile, &fdsc, &font_smaller) != DFB_OK) return 0;
  font_smaller_height = fdsc.height;
  fdsc.height = screen_width / 70;
  if (dfb->CreateFont (dfb, fontfile, &fdsc, &font_tiny) != DFB_OK) return 0;
  font_tiny_height = fdsc.height;

  return 1;
}

int main (int argc, char *argv[])
{
  int returnstatus = -1;        /* return value of this function...         */
  DFBSurfaceDescription sdsc;   /* description for the primary surface      */
  DFBInputEvent evt;            /* generic input events will be stored here */
  DFBResult result;             /* we store eventual errors here            */
  char *lastuser=NULL;          /* latest user who logged in                */
	pthread_t pm_thread;

  /* load settings from file */
	initialize_variables();
	current_tty = ParseCMDLine(argc, argv, 0);
	if (!load_settings()) return GUI_FAILURE;
  if (!disable_last_user) lastuser = get_last_user();

#ifdef WANT_CRYPTO
	restore_public_key(stdin);
#endif

	/* get the pid of our father process */
	ppid = atoi(argv[argc-1]);

	log_stderr();

  /* we initialize directfb */
	result = DirectFBInit (&argc, &argv);
  if (result == DFB_OK) result = DirectFBSetOption("session","-1");
  if (result == DFB_OK) result = DirectFBCreate (&dfb);
  //if (result == DFB_OK) result = dfb->EnumInputDevices (dfb, enum_input_device, &devices);
  if (result == DFB_OK) result = dfb->CreateInputEventBuffer (dfb, DICAPS_ALL, DFB_TRUE, &events);
  if (result == DFB_OK) result = dfb->GetDisplayLayer (dfb, DLID_PRIMARY, &layer);

  /* any errors so far? */
	if (result != DFB_OK)
	{
		DirectFB_Error();
		return GUI_FAILURE;
	}

  /* more initialization */
  layer->SetCooperativeLevel (layer, DLSCL_ADMINISTRATIVE);
  layer->EnableCursor (layer, 0);
  sdsc.flags = DSDESC_CAPS;
  sdsc.caps  = DSCAPS_PRIMARY | DSCAPS_FLIPPING;

  if (dfb->CreateSurface( dfb, &sdsc, &primary ) != DFB_OK)
	{
		DirectFB_Error();
		return GUI_FAILURE;
	}

  primary->GetSize(primary, &screen_width, &screen_height);

	if (screen_width  != theme_xres) x_ratio = (float)screen_width/(float)theme_xres;
	if (screen_height != theme_yres) y_ratio = (float)screen_height/(float)theme_yres;

  if (!set_font_sizes ())
	{
		DirectFB_Error();
		return GUI_FAILURE;
	}

  Draw_Background_Image(1);

	lock_mouse_cursor = NULL;
	if (cursor)
		if (cursor->path)
		{
			char *path = StrApp((char**)NULL, theme_dir, "/", cursor->path, (char*)NULL);
			curs_surface = load_image (path, dfb, x_ratio, y_ratio);
			free(path);
			layer->SetCursorShape (layer, curs_surface, cursor->x_off, cursor->y_off);

			lock_mouse_cursor = (pthread_mutex_t *)calloc(1,sizeof(pthread_mutex_t));
			if (pthread_mutex_init(lock_mouse_cursor, NULL))
			{
				writelog(ERROR, "Could not initialize cursor management mutex\n");
				free(lock_mouse_cursor);
				lock_mouse_cursor = NULL;
			}
		}

  if (!create_windows())
	{
		DirectFB_Error();
		return GUI_FAILURE;
	}

  if (!hide_password) password->MaskText(password, 1);
  else password->HideText(password, 1);
  load_sessions(session);
  if (lastuser)
	{
		if (username_label) username_label->SetFocus(username_label, 0);
		if (password_label) password_label->SetFocus(password_label, 1);
		if (!hide_last_user) username->SetText(username, lastuser);
		else username->SetText(username, "lastuser");
		username->SetFocus(username, 0);
		password->SetFocus(password, 1);
		set_user_session(lastuser);
		free(lastuser);
		lastuser = NULL;
	}
  else
	{ 
		if (username_label) username_label->SetFocus(username_label, 1);
		if (password_label) password_label->SetFocus(password_label, 0);
		username->SetFocus(username, 1);
	}
  if (session_label) session_label->SetFocus(session_label, 0);
  session->SetFocus(session, 0);

	if (!cursor)
		layer->EnableCursor (layer, 1);
	else
		layer->EnableCursor (layer, cursor->enable);

	if (use_screen_power_management)
	{
		pthread_create(&pm_thread, NULL, (void *) screen_pm_thread, dfb);
	}

#ifdef USE_SCREEN_SAVERS
	if (use_screensaver)
		do_ss(dfb, primary, font_large, 1);
#endif

  /* we go on for ever... or until the user does something in particular */
  while (returnstatus == -1)
	{
		/* we wait for an input event... */
		events->WaitForEventWithTimeout(events, 0, 500);

		if (events->HasEvent(events) == DFB_OK) /* ...got that! */
		{
			int do_action = 1;

			events->GetEvent (events, DFB_EVENT (&evt));

			/* we perform our action only if allowed to */
			pthread_mutex_lock(&lock_act);
			if (thread_action)
				do_action = 0;
			else
				thread_action = 1;
			pthread_mutex_unlock(&lock_act);

			if (do_action)
			{
				switch (evt.type)
				{
					case DIET_KEYPRESS:
						returnstatus = handle_keyboard_event(&evt);
						break;
					case DIET_AXISMOTION:
						handle_mouse_event();
						break;
					default: /* we do nothing here */
						break;
				}

				pthread_mutex_lock(&lock_act);
				thread_action = 0;
				pthread_mutex_unlock(&lock_act);
			}
		}
	}

  close_framebuffer_mode (returnstatus);
	return EXIT_SUCCESS;
}
