/* * $Id$ * * Evoke, head honcho of everything * Part of Equinox Desktop Environment (EDE). * Copyright (c) 2007-2008 EDE Authors. * * This program is licensed under terms of the * GNU General Public License version 2 or newer. * See COPYING for details. */ #include #include #include #include #include #include #include #include "EvokeService.h" #define FOREVER 1e20 #define CONFIG_FILE "evoke.conf" #define DEFAULT_PID "/tmp/evoke.pid" /* * Used to assure unique instance, even if is given another * path for pid. This option can't be modified by user. * TODO: add lock on file so it can't be removed ? */ #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) { EVOKE_LOG("Got quit signal %i\n", sig); EvokeService::instance()->stop(); } static void xmessage_handler(int, void*) { #ifdef USE_FLTK_LOOP_EMULATION XEvent xev; while(XEventsQueued(fl_display, QueuedAfterReading)) { XNextEvent(fl_display, &xev); EvokeService::instance()->handle((const XEvent*)&xev); } #else return EvokeService::instance()->handle(fl_xevent); #endif } static int composite_handler(int ev) { return EvokeService::instance()->composite_handle(fl_xevent); } static const char* next_param(int curr, char** argv, int argc) { int j = curr + 1; if(j >= argc) return NULL; if(argv[j][0] == '-') return NULL; return argv[j]; } void help(void) { puts("Usage: evoke [OPTIONS]"); puts("EDE startup manager responsible for starting, quitting and tracking"); puts("various pieces of desktop environment and external programs."); puts("...and to popup a nice window when something crashes...\n"); puts("Options:"); puts(" -h, --help this help"); puts(" -s, --startup run in starup mode"); puts(" -n, --no-splash do not show splash screen in starup mode"); puts(" -d, --dry-run run in starup mode, but don't execute anything"); puts(" -a, --autostart read autostart directory and run all items"); puts(" -u, --autostart-safe read autostart directory and display dialog what will be run"); puts(" -c, --config [FILE] use FILE as config file"); puts(" -p, --pid [FILE] use FILE to store PID number"); puts(" -l, --log [FILE] log traffic to FILE (FILE can be stdout/stderr for console output)\n"); } int main(int argc, char** argv) { const char* config_file = NULL; const char* pid_file = NULL; const char* log_file = NULL; bool do_startup = 0; bool do_dryrun = 0; bool no_splash = 0; bool do_autostart = 0; bool do_autostart_safe = 0; 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, "-c", "--config")) { config_file = next_param(i, argv, argc); if(!config_file) { puts("Missing configuration filename"); return 1; } i++; } else if(CHECK_ARGV(a, "-p", "--pid")) { pid_file = next_param(i, argv, argc); if(!pid_file) { puts("Missing pid filename"); return 1; } i++; } else if(CHECK_ARGV(a, "-l", "--log")) { log_file = next_param(i, argv, argc); if(!log_file) { puts("Missing log filename"); return 1; } i++; } else if(CHECK_ARGV(a, "-s", "--startup")) do_startup = 1; else if(CHECK_ARGV(a, "-d", "--dry-run")) do_dryrun = 1; else if(CHECK_ARGV(a, "-n", "--no-splash")) no_splash = 1; else if(CHECK_ARGV(a, "-a", "--autostart")) do_autostart = 1; else if(CHECK_ARGV(a, "-u", "--autostart-safe")) do_autostart_safe = 1; else { printf("Unknown parameter '%s'. Run 'evoke -h' for options\n", a); return 1; } } } // make sure X11 is running before rest of code is called fl_open_display(); // initialize main service object EvokeService* service = EvokeService::instance(); if(!service->setup_logging(log_file)) { printf("Can't open %s for logging. Please choose some writeable place\n", log_file); return 1; } if(!service->setup_channels()) { printf("Can't setup internal channels\n"); return 1; } EVOKE_LOG("= evoke started =\n"); if(!pid_file) pid_file = DEFAULT_PID; if(!service->setup_pid(pid_file, LOCK_FILE)) { printf("Either another evoke instance is running or can't create pid file. Please correct this\n"); printf("Note: if program abnormaly crashed before, just remove '%s' and start it again\n", LOCK_FILE); printf("= evoke abrupted shutdown =\n"); return 1; } if(!config_file) config_file = CONFIG_FILE; // TODO: XDG paths if(do_startup) { if(!service->init_splash(config_file, no_splash, do_dryrun)) { EVOKE_LOG("Unable to read correctly %s. Please check it is correct config file\n", config_file); EVOKE_LOG("= evoke abrupted shutdown =\n"); return 1; } } service->setup_atoms(fl_display); service->init_xsettings_manager(); /* * Run autostart code after XSETTINGS manager since some Gtk apps (mozilla) will eats a lot * of cpu during runtime settings changes */ if(do_autostart || do_autostart_safe) service->init_autostart(do_autostart_safe); /* * Let composite manager be run the latest. Running autostart after it will not deliver * X events to possible shown autostart window, and I'm not sure why. Probably due event * throttle from XDamage ? */ service->init_composite(); 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 #if 0 XSelectInput(fl_display, RootWindow(fl_display, fl_screen), PropertyChangeMask | SubstructureNotifyMask | ClientMessage); #endif // composite engine included too XSelectInput(fl_display, RootWindow(fl_display, fl_screen), SubstructureNotifyMask | ExposureMask | StructureNotifyMask | PropertyChangeMask | ClientMessage); /* * Register event listener and run in infinite loop. Loop will be * interrupted from one of the received signals. * * I choose to use fltk for this since wait() will nicely poll events * and pass expecting ones to xmessage_handler(). Other (non-fltk) solution would * be to manually pool events via select() and that code could be very messy. * So stick with the simplicity :) * * Also note that '1' in add_fd (when USE_FLTK_LOOP_EMULATION is defined) parameter * means POLLIN, and for the details see Fl_x.cxx * * Let me explaint what these USE_FLTK_LOOP_EMULATION parts means. It was introduced * since FLTK eats up SelectionClear event and so other parts (evoke specific atoms, splash etc.) * could be used and tested. FLTK does not have event handler that could be registered * _before_ it process events, but only add_handler() which will be called _after_ FLTK process * all events and where will be reported ones that FLTK does not understainds or for those * windows it already don't know. */ #ifdef USE_FLTK_LOOP_EMULATION Fl::add_fd(ConnectionNumber(fl_display), 1, xmessage_handler); Fl::add_handler(composite_handler); #else /* * NOTE: composite_handler() is not needed since it will be included * within xmessage_handler() call */ Fl::add_handler(xmessage_handler); #endif service->start(); while(service->running()) { #ifdef USE_FLTK_LOOP_EMULATION /* * Seems that when XQLength() is not used, damage events will not be correctly * send to xmessage_handler() and composite will wrongly draw the screen. */ if(XQLength(fl_display)) { xmessage_handler(0, 0); continue; } #else /* * 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); #endif Fl::wait(FOREVER); #ifndef USE_FLTK_LOOP_EMULATION if(fl_xevent && fl_xevent->type == SelectionClear) service->handle(fl_xevent); #endif } EVOKE_LOG("= evoke nice shutdown =\n"); return 0; }