/*
 * $Id: evoke.cpp 3027 2010-03-28 12:54:45Z karijes $
 *
 * Evoke, head honcho of everything
 * Part of Equinox Desktop Environment (EDE).
 * Copyright (c) 2007-2009 EDE Authors.
 *
 * This program is licensed under terms of the 
 * GNU General Public License version 2 or newer.
 * See COPYING for details.
 */

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

#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <FL/Fl.H>
#include <FL/x.H>
#include <edelib/Run.h>
#include <edelib/Ede.h>

#include "EvokeService.h"

EDELIB_NS_USING(run_async)

#define FOREVER   1e20
#define LOCK_FILE "/tmp/.evoke.lock"

#define CHECK_ARGV(argv, pshort, plong) ((strcmp(argv, pshort) == 0) || (strcmp(argv, plong) == 0))

static void quit_signal(int sig) {
	EvokeService::instance()->stop();
}

static int xmessage_handler(int) {
	return EvokeService::instance()->handle(fl_xevent);
}

static void help(void) {
	puts("Usage: evoke [OPTIONS]");
	puts("EDE startup manager responsible for desktop starting and quitting");
	puts("(including various bits and pieces desktop needs)");
	puts("Options:");
	puts("  -h, --help            this help");
	puts("  -s, --startup         run in startup mode");
	puts("  -n, --no-splash       do not show splash screen in startup mode");
	puts("  -d, --dry-run         run in startup mode, but don't execute anything");
}

int main(int argc, char** argv) {
	bool do_startup         = false;
	bool do_dryrun          = false;
	bool show_splash        = true;

	if(argc > 1) {
		const char* a;
		for(int i = 1; i < argc; i++) {
			a = argv[i];
			if(CHECK_ARGV(a, "-h", "--help")) {
				help();
				return 0;
			}
			else if(CHECK_ARGV(a, "-s", "--startup"))
				do_startup = true;
			else if(CHECK_ARGV(a, "-d", "--dry-run"))
				do_dryrun = true;
			else if(CHECK_ARGV(a, "-n", "--no-splash"))
				show_splash = false;
			else {
				printf("Unknown parameter '%s'. Run 'evoke -h' for options\n", a);
				return 1;
			}
		}
	}

	EDE_APPLICATION("evoke");

	/* make sure X11 is running before rest of code is called */
	fl_open_display();

	/* initialize main service object */
	EvokeService* service = EvokeService::instance();

	/*
	 * Main loop is not initialized before startup (and splash) are finished;
	 * this is intentional so we could use 'dryrun' to test a splash theme and startup items
	 * without interfering with already running evoke instance (and getting lock error)
	 *
	 * TODO: dryrun option is pretty dummy; better to use "test-splash" or similar
	 */
	if(do_startup) {
		service->read_startup();
		service->run_startup(show_splash, do_dryrun);
	}

	/* only testing, quit nicely */
	if(do_dryrun)
		return 0;

	if(!service->setup_lock(LOCK_FILE)) {
		printf("*** Either another evoke instance is running or I can't create lock file.\n");
		printf("*** If program abnormaly crashed before, just remove '%s' and start it again.\n", LOCK_FILE);
		return 1;
	}

	signal(SIGINT,  quit_signal);
	signal(SIGTERM, quit_signal);
	signal(SIGKILL, quit_signal);
	signal(SIGQUIT, quit_signal);

#ifdef USE_SIGHUP
	/*
	 * This is mostly used when we get request to shutdown session and close/kill all windows.
	 * If evoke is started from gui console (xterm, rxvt), closing that window we will get 
	 * SIGHUP since terminal will disconnect all controlling processes. On other hand, if evoke
	 * is started as session carrier (eg. run from xinitrc), this is not needed.
	 */
	signal(SIGHUP,  quit_signal);
#endif

	service->start_xsettings_manager();

	/* set stuff so xsettings manager can receive events */
	XSelectInput(fl_display, RootWindow(fl_display, fl_screen), 
			PropertyChangeMask | SubstructureNotifyMask | ClientMessage);
	Fl::add_handler(xmessage_handler);

	/* run applicator for settings; it must be done after manager is fully on */
	if(do_startup)
		run_async("ede-settings-apply");

	service->start();

	while(service->running()) {
		/*
		 * FLTK will not report SelectionClear needed for XSETTINGS manager
		 * so we must catch it first before FLTK discards it (if we can :P)
		 * This can be missed greatly due a large number of XDamage events
		 * and using this method is not quite safe.
		 */
		if(fl_xevent && fl_xevent->type == SelectionClear)
			service->handle(fl_xevent);

		Fl::wait(FOREVER);
	}

	return 0;
}
