diff --git a/evoke/EvokeService.cpp b/evoke/EvokeService.cpp index 80e0a8d..bbeb37f 100644 --- a/evoke/EvokeService.cpp +++ b/evoke/EvokeService.cpp @@ -12,23 +12,87 @@ #include "Log.h" #include "EvokeService.h" +#include "Splash.h" +#include "Spawn.h" + #include #include #include +#include +#include + +#include #include // getpid #include // #include // free #include // strdup +void resolve_path(const edelib::String& imgdir, edelib::String& img, bool have_imgdir) { + if(img.empty()) + return; -EvokeService::EvokeService() : logfile(NULL), pidfile(NULL) { + const char* im = img.c_str(); + + if(!edelib::file_exists(im) && have_imgdir) { + img = edelib::build_filename("/", imgdir.c_str(), im); + im = img.c_str(); + if(!edelib::file_exists(im)) { + // no file, send then empty + img.clear(); + } + } +} + +int get_int_property_value(Atom at) { + Atom real; + int format; + unsigned long n, extra; + unsigned char* prop; + + int status = XGetWindowProperty(fl_display, RootWindow(fl_display, fl_screen), + at, 0L, 0x7fffffff, False, XA_CARDINAL, &real, &format, &n, &extra, + (unsigned char**)&prop); + int ret = -1; + if(status != Success && !prop) + return ret; + ret = int(*(long*)prop); + XFree(prop); + return ret; +} + +int get_string_property_value(Atom at, char* txt, int txt_len) { + XTextProperty names; + XGetTextProperty(fl_display, RootWindow(fl_display, fl_screen), &names, at); + if(!names.nitems || !names.value) + return 0; + + char** vnames; + int nsz = 0; + if(!XTextPropertyToStringList(&names, &vnames, &nsz)) { + XFree(names.value); + return 0; + } + + strncpy(txt, vnames[0], txt_len); + txt[txt_len] = '\0'; + XFreeStringList(vnames); + return 1; +} + +EvokeService::EvokeService() : is_running(0), logfile(NULL), pidfile(NULL), lockfile(NULL) { + top_win = NULL; } EvokeService::~EvokeService() { if(logfile) delete logfile; + if(lockfile) { + edelib::file_remove(lockfile); + free(lockfile); + } + if(pidfile) { edelib::file_remove(pidfile); free(pidfile); @@ -54,13 +118,20 @@ bool EvokeService::setup_logging(const char* file) { return true; } -bool EvokeService::setup_pid(const char* file) { +bool EvokeService::setup_pid(const char* file, const char* lock) { if(!file) return false; - if(edelib::file_exists(file)) + if(edelib::file_exists(lock)) return false; + FILE* l = fopen(lock, "w"); + if(!l) + return false; + fprintf(l, " "); + fclose(l); + lockfile = strdup(lock); + FILE* f = fopen(file, "w"); if(!f) return false; @@ -72,20 +143,35 @@ bool EvokeService::setup_pid(const char* file) { return true; } -bool EvokeService::setup_config(const char* config, bool do_startup) { - // for now if is not startup mode, ignore it - if(!do_startup) - return true; - +bool EvokeService::init_splash(const char* config, bool no_splash, bool dry_run) { edelib::Config c; if(!c.load(config)) return false; char buff[1024]; + bool have_imgdir = false; + + c.get("evoke", "ImagesDirectory", buff, sizeof(buff)); + + // no evoke section + if(c.error() == edelib::CONF_ERR_SECTION) + return false; + + edelib::String imgdir; + if(c.error() == edelib::CONF_SUCCESS) { + imgdir = buff; + have_imgdir = true; + } + + edelib::String splashimg; + if(c.get("evoke", "Splash", buff, sizeof(buff))) + splashimg = buff; + + // Startup key must exists if(!c.get("evoke", "Startup", buff, sizeof(buff))) return false; - edelib::vector vs; + StringList vs; edelib::stringtok(vs, buff, ","); // nothing, fine, do nothing @@ -95,8 +181,9 @@ bool EvokeService::setup_config(const char* config, bool do_startup) { EvokeClient ec; const char* key_name; - for(unsigned int i = 0; i < sz; i++) { - key_name = vs[i].c_str(); + + for(StringListIter it = vs.begin(); it != vs.end(); ++it) { + key_name = (*it).c_str(); edelib::str_trim((char*)key_name); // probably listed but not the same key; also Exec value must exists @@ -105,10 +192,8 @@ bool EvokeService::setup_config(const char* config, bool do_startup) { else ec.exec = buff; - if(c.get(key_name, "Message", buff, sizeof(buff))) - ec.message = buff; - // it is no EDE app untill say so - c.get(key_name, "Core", ec.core, false); + if(c.get(key_name, "Description", buff, sizeof(buff))) + ec.desc = buff; if(c.get(key_name, "Icon", buff, sizeof(buff))) ec.icon = buff; @@ -116,23 +201,81 @@ bool EvokeService::setup_config(const char* config, bool do_startup) { clients.push_back(ec); } - for(unsigned int i = 0; i < clients.size(); i++) { - printf("Exec: %s\n", clients[i].exec.c_str()); - printf("Message: %s\n", clients[i].message.c_str()); - printf("Icon: %s\n", clients[i].icon.c_str()); - printf("Core: %i\n\n", clients[i].core); - } + /* + * Now, before data is send to Splash, resolve directories + * since Splash expects that. + */ + resolve_path(imgdir, splashimg, have_imgdir); + for(ClientListIter it = clients.begin(); it != clients.end(); ++it) + resolve_path(imgdir, (*it).icon, have_imgdir); + + Splash sp(no_splash, dry_run); + sp.set_clients(&clients); + sp.set_background(&splashimg); + + sp.run(); return true; } void EvokeService::setup_atoms(Display* d) { + // with them must be send '1' or property will be ignored (except _EDE_EVOKE_SPAWN) _ede_shutdown_all = XInternAtom(d, "_EDE_EVOKE_SHUTDOWN_ALL", False); + _ede_evoke_quit = XInternAtom(d, "_EDE_EVOKE_QUIT", False); + _ede_spawn = XInternAtom(d, "_EDE_EVOKE_SPAWN", False); - _ede_shutdown_client = XInternAtom(d, "_EDE_SHUTDOWN", False); } -int EvokeService::handle(int event) { - logfile->printf("Got event %i\n", event); +int EvokeService::handle(const XEvent* ev) { + logfile->printf("Got event %i\n", ev->type); + + if(ev->type == MapNotify) { + if(top_win) { + // for splash to keep it at top (working in old edewm) + XRaiseWindow(fl_display, fl_xid(top_win)); + return 1; + } + } else if(ev->type == ConfigureNotify) { + if(ev->xconfigure.event == DefaultRootWindow(fl_display) && top_win) { + // splash too, but keep window under other wm's + XRaiseWindow(fl_display, fl_xid(top_win)); + return 1; + } + } else if(ev->type == PropertyNotify) { + if(ev->xproperty.atom == _ede_spawn) { + char buff[1024]; + + if(get_string_property_value(_ede_spawn, buff, sizeof(buff))) { + logfile->printf("Got _EVOKE_SPAWN with %s. Starting client...\n", buff); + int r = spawn_program(buff, false); + + if(r != 0) + fl_alert("Unable to start %s. Got code %i", buff, r); + } else + logfile->printf("Got _EVOKE_SPAWN with malformed data. Ignoring...\n"); + + return 1; + } + + if(ev->xproperty.atom == _ede_evoke_quit) { + int val = get_int_property_value(_ede_evoke_quit); + if(val == 1) { + logfile->printf("Got accepted _EDE_EVOKE_QUIT\n"); + stop(); + } else + logfile->printf("Got _EDE_EVOKE_QUIT with bad code (%i). Ignoring...\n", val); + return 1; + } + + if(ev->xproperty.atom == _ede_shutdown_all) { + int val = get_int_property_value(_ede_shutdown_all); + if(val == 1) + logfile->printf("Got accepted _EDE_EVOKE_SHUTDOWN_ALL\n"); + else + logfile->printf("Got _EDE_EVOKE_SHUTDOWN_ALL with bad code (%i). Ignoring...\n", val); + return 1; + } + } + return 0; } diff --git a/evoke/EvokeService.h b/evoke/EvokeService.h index 9229818..d4e06c4 100644 --- a/evoke/EvokeService.h +++ b/evoke/EvokeService.h @@ -14,42 +14,59 @@ #define __EVOKESERVICE_H__ #include "Log.h" -#include - -#include +#include #include +#include struct EvokeClient { - edelib::String message; // message during startup + edelib::String desc; // short program description (used in Starting... message) edelib::String icon; // icon for this client edelib::String exec; // program name/path to run - bool core; // does understaind _EDE_SHUTDOWN_SELF (only ede apps) }; +typedef edelib::list ClientList; +typedef edelib::list::iterator ClientListIter; +typedef edelib::list StringList; +typedef edelib::list::iterator StringListIter; + +class Fl_Double_Window; + class EvokeService { private: + bool is_running; Log* logfile; char* pidfile; + char* lockfile; + + Fl_Double_Window* top_win; Atom _ede_shutdown_all; - Atom _ede_shutdown_client; Atom _ede_spawn; + Atom _ede_evoke_quit; - edelib::vector clients; + ClientList clients; public: EvokeService(); ~EvokeService(); static EvokeService* instance(void); + void start(void) { is_running = true; } + void stop(void) { is_running = false; } + bool running(void) { return is_running; } + bool setup_logging(const char* file); - bool setup_pid(const char* file); - bool setup_config(const char* config, bool do_startup); + bool setup_pid(const char* file, const char* lock); void setup_atoms(Display* d); - int handle(int event); + bool init_splash(const char* config, bool no_splash, bool dry_run); + + int handle(const XEvent* ev); Log* log(void) { return logfile; } + + void register_top(Fl_Double_Window* win) { top_win = win; } + void unregister_top(void) { top_win = NULL; } }; #define EVOKE_LOG EvokeService::instance()->log()->printf diff --git a/evoke/Jamfile b/evoke/Jamfile index facf74a..91ceb07 100644 --- a/evoke/Jamfile +++ b/evoke/Jamfile @@ -10,7 +10,8 @@ SubDir TOP evoke ; -SOURCE = evoke.cpp EvokeService.cpp Log.cpp ; +SOURCE = evoke.cpp EvokeService.cpp Spawn.cpp Splash.cpp Log.cpp ; EdeProgram evoke : $(SOURCE) ; -TranslationStrings locale : $(SOURCE) ; +FltkProgramBare test/evoke_test : test/evoke_test.cpp ; +#TranslationStrings locale : $(SOURCE) ; diff --git a/evoke/Spawn.cpp b/evoke/Spawn.cpp new file mode 100644 index 0000000..bf5437b --- /dev/null +++ b/evoke/Spawn.cpp @@ -0,0 +1,69 @@ +/* + * $Id$ + * + * Evoke, head honcho of everything + * Part of Equinox Desktop Environment (EDE). + * Copyright (c) 2000-2007 EDE Authors. + * + * This program is licensed under terms of the + * GNU General Public License version 2 or newer. + * See COPYING for details. + */ + +#include "Spawn.h" + +#include // fork +#include // fork, open, close, dup +#include +#include +#include +#include + +extern char** environ; + +int run_fork(const char* cmd, bool wait) { + if(!cmd) + return SPAWN_EMPTY; + + int nulldev = -1; + int status_ret = 0; + + pid_t pid = fork(); + + if(pid == -1) + return SPAWN_FORK_FAILED; + + // run the child + if(pid == 0) { + char* argv[4]; + argv[0] = "/bin/sh"; + argv[1] = "-c"; + argv[2] = (char*)cmd; + argv[3] = NULL; + + /* + * The following is to avoid X locking when executing + * terminal based application that requires user input + */ + if((nulldev = open("/dev/null", O_RDWR)) == -1) + return SPAWN_FORK_FAILED; + + /* TODO: redirect these to EvokeService log */ + close(0); dup(nulldev); + close(1); dup(nulldev); + close(2); dup(nulldev); + + if(execve(argv[0], argv, environ) == -1) { + close(nulldev); + // should not get here + return SPAWN_EXECVE_FAILED; + } + } + + return status_ret; +} + + +int spawn_program(const char* cmd, bool wait) { + return run_fork(cmd, wait); +} diff --git a/evoke/Spawn.h b/evoke/Spawn.h new file mode 100644 index 0000000..679bc12 --- /dev/null +++ b/evoke/Spawn.h @@ -0,0 +1,33 @@ +/* + * $Id$ + * + * Evoke, head honcho of everything + * Part of Equinox Desktop Environment (EDE). + * Copyright (c) 2000-2007 EDE Authors. + * + * This program is licensed under terms of the + * GNU General Public License version 2 or newer. + * See COPYING for details. + */ + +#ifndef __SPAWN_H__ +#define __SPAWN_H__ + +/* + * This is little bit modified code from edelib run_program() + * so evoke specific stuff can be added. Also, possible option + * is that edelib run_program(), at some time, consult evoke + * for running programs. + */ +#define SPAWN_NOT_FOUND 65535 // executable not found +#define SPAWN_EMPTY 65534 // given parameter is NULL +#define SPAWN_NOT_EXEC 65533 // given parameter is not executable on system +#define SPAWN_FORK_FAILED 65532 // internal fork failed +#define SPAWN_WAITPID_FAILED 65531 // internal waitpid failed +#define SPAWN_EXECVE_FAILED 65530 // internal execve failed +#define SPAWN_PTY_FAILED 65529 // TODO +#define SPAWN_USER_CANCELED 65528 // TODO + +int spawn_program(const char* cmd, bool wait); + +#endif diff --git a/evoke/Splash.cpp b/evoke/Splash.cpp new file mode 100644 index 0000000..e4001b4 --- /dev/null +++ b/evoke/Splash.cpp @@ -0,0 +1,305 @@ +/* + * $Id$ + * + * Evoke, head honcho of everything + * Part of Equinox Desktop Environment (EDE). + * Copyright (c) 2000-2007 EDE Authors. + * + * This program is licensed under terms of the + * GNU General Public License version 2 or newer. + * See COPYING for details. + */ + +#include "Splash.h" +#include "Spawn.h" + +#include +#include +#include + +#include +#include + +#include // snprintf + +#define TIMEOUT_START 0.5 // timeout when splash is first time shown (also for first client) +#define TIMEOUT_CONTINUE 2.0 // timeout between starting rest of the cliens + +extern int xmessage_handler(int e); + +/* + * repeatedly call runner() untill all clients are + * started then hide splash window + */ +void runner_cb(void* s) { + Splash* sp = (Splash*)s; + + if(sp->next_client()) + Fl::repeat_timeout(TIMEOUT_CONTINUE, runner_cb, sp); + else + sp->hide(); +} + +Splash::Splash(bool sp, bool dr) : Fl_Double_Window(480, 364), clist(NULL), bkg(NULL), + counter(0), no_splash(sp), dry_run(dr) { + icons = NULL; +} + +Splash::~Splash() { + EVOKE_LOG("Cleaning splash data\n"); + // elements of icons cleans Fl_Group + delete [] icons; +} + + +#if 0 +// after edewm got _NET_WM_WINDOW_TYPE_SPLASH support +void Splash::show(void) { + if(shown()) + return; + + Fl_X::make_xid(this); + /* + * Edewm does not implement this for now. Alternative, working solution + * is used via register_top()/unregister_top(); also looks like later + * is working on othe wm's too. + */ + Atom win_type = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE", False); + Atom win_splash = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE_SPLASH", False); + XChangeProperty(fl_display, fl_xid(this), win_type, XA_ATOM, 32, PropModeReplace, + (unsigned char*)&win_splash, sizeof(Atom)); +} +#endif + +void Splash::run(void) { + EASSERT(clist != NULL); + + if(no_splash) { + while(next_client_nosplash()) + ; + return; + } + + fl_register_images(); + + // setup widgets + begin(); + Fl_Box* bimg = new Fl_Box(0, 0, w(), h()); + Fl_Image* splash_img = 0; + + if(bkg) + splash_img = Fl_Shared_Image::get(bkg->c_str()); + + if(splash_img) { + int W = splash_img->w(); + int H = splash_img->h(); + // update window and Box sizes + size(W, H); + bimg->size(W, H); + + bimg->image(splash_img); + } + + /* + * place message box at the bottom with + * nice offset (10 px) from window borders + */ + msgbox = new Fl_Box(10, h() - 25 - 10, w() - 20, 25); + + /* + * Setup icons positions, based on position of msgbox assuming someone + * will not abuse splash to start hundrets of programs, since icons will + * be placed only in horizontal order, one line, so in case their large + * number, some of them will go out of window borders. + * + * Icon box will be 64x64 so larger icons can fit too. + * + * This code will use Fl_Group (moving group, later, will move all icons + * saving me from code mess), but will not update it's w() for two reasons: + * icons does not use it, and will be drawn correctly, and second, setting + * width will initiate fltk layout engine which will mess everything up. + */ + Fl_Group* icon_group = new Fl_Group(10, msgbox->y() - 10 - 64, 0, 64); + int X = icon_group->x(); + int Y = icon_group->y(); + // offset between icons + int ioffset = 5; + + // FIXME: use malloc/something instead this + icons = new Fl_Box*[clist->size()]; + + icon_group->begin(); + const char* imgpath; + Fl_Image* iconimg; + int i = 0; + for(ClientListIter it = clist->begin(); it != clist->end(); ++it, ++i) { + Fl_Box* bb = new Fl_Box(X, Y, 64, 64); + + imgpath = (*it).icon.c_str(); + iconimg = Fl_Shared_Image::get(imgpath); + if(!iconimg) { + bb->label(_("No image")); + bb->align(FL_ALIGN_INSIDE | FL_ALIGN_WRAP); + } else + bb->image(iconimg); + + bb->hide(); + + X += bb->w() + ioffset; + icons[i] = bb; + } + icon_group->end(); + + // see X as width of all icons + int gx = w()/2 - X/2; + // gx can be negative + gx = (gx > 10) ? gx : 10; + icon_group->position(gx, Y); + end(); + + clear_border(); + + /* + * If set_override() is used, message boxes will be + * popped behind splash. Using it or not ??? + */ + set_override(); + + // make sure window is centered + int sw = DisplayWidth(fl_display, fl_screen); + int sh = DisplayHeight(fl_display, fl_screen); + position(sw/2 - w()/2, sh/2 - h()/2); + + show(); + + Fl::add_timeout(TIMEOUT_START, runner_cb, this); + + /* + * Fl::wait() will block all events to EvokeService and it will not + * be able to keep this window at the top. So we again select event inputs + * and redirect them to EvokeService::handle(). + */ + XSelectInput(fl_display, RootWindow(fl_display, fl_screen), SubstructureNotifyMask); + Fl::add_handler(xmessage_handler); + + // make sure MappingNotify keeps this window at the top + EvokeService::instance()->register_top(this); + + while(shown()) + Fl::wait(); + + EvokeService::instance()->unregister_top(); +} + +// called when splash option is on +bool Splash::next_client(void) { + if(clist->empty()) + return false; + + if(counter == 0) + clist_it = clist->begin(); + + if(clist_it == clist->end()) { + counter = 0; + return false; + } + + EASSERT(counter < clist->size() && "Internal error; 'counter' out of bounds"); + + char buff[1024]; + const char* msg = (*clist_it).desc.c_str(); + const char* cmd = (*clist_it).exec.c_str(); + snprintf(buff, sizeof(buff), _("Starting %s..."), msg); + + icons[counter]->show(); + msgbox->copy_label(buff); + redraw(); + + if(!dry_run) + spawn_program(cmd, false); + + ++clist_it; + ++counter; + return true; + +#if 0 + /* + * This was vector implementation; I'm leaving here + * untill list is proven to be safe. If code is changed + * back to vector, make sure to update rest code removing + * iterators. + */ + if(counter >= clist->size()) { + counter = 0; + return false; + } + char buff[1024]; + const char* msg = (*clist)[counter].message.c_str(); + const char* cmd = (*clist)[counter].exec.c_str(); + snprintf(buff, sizeof(buff), _("Starting %s..."), msg); + + icons[counter]->show(); + msgbox->copy_label(buff); + redraw(); + + if(!dry_run) + spawn_program(cmd, false); + + counter++; +#endif +} + +// called when splash option is off +bool Splash::next_client_nosplash(void) { + if(clist->empty()) + return false; + + if(counter == 0) + clist_it = clist->begin(); + + if(clist_it == clist->end()) { + counter = 0; + return false; + } + + EASSERT(counter < clist->size() && "Internal error; 'counter' out of bounds"); + + char buff[1024]; + const char* msg = (*clist_it).desc.c_str(); + const char* cmd = (*clist_it).exec.c_str(); + snprintf(buff, sizeof(buff), _("Starting %s..."), msg); + + printf("%s\n", buff); + + if(!dry_run) + spawn_program(cmd, false); + + ++clist_it; + ++counter; + return true; + +#if 0 + /* + * This was vector implementation; I'm leaving here + * untill list is proven to be safe. If code is changed + * back to vector, make sure to update rest code removing + * iterators. + */ + if(counter >= clist->size()) { + counter = 0; + return false; + } + + char buff[1024]; + const char* msg = (*clist)[counter].message.c_str(); + const char* cmd = (*clist)[counter].exec.c_str(); + snprintf(buff, sizeof(buff), _("Starting %s..."), msg); + + printf("%s\n", buff); + + if(!dry_run) + spawn_program(cmd, false); + + counter++; +#endif +} diff --git a/evoke/Splash.h b/evoke/Splash.h new file mode 100644 index 0000000..01e10f3 --- /dev/null +++ b/evoke/Splash.h @@ -0,0 +1,53 @@ +/* + * $Id$ + * + * Evoke, head honcho of everything + * Part of Equinox Desktop Environment (EDE). + * Copyright (c) 2000-2007 EDE Authors. + * + * This program is licensed under terms of the + * GNU General Public License version 2 or newer. + * See COPYING for details. + */ + +#ifndef __SPLASH_H__ +#define __SPLASH_H__ + +#include +#include + +#include "EvokeService.h" + +class Splash : public Fl_Double_Window { + private: + const ClientList* clist; + const edelib::String* bkg; + unsigned int counter; + bool no_splash; + bool dry_run; + + ClientListIter clist_it; + + Fl_Box* msgbox; + Fl_Box** icons; + + public: + Splash(bool sp, bool dr); + ~Splash(); + + /* + * NOTE: clients() and set_background() uses address of passed data, + * so make sure passed data does not destroys. + */ + void set_clients(const ClientList* cl) { clist = cl; } + void set_background(const edelib::String* s) { bkg = s; } + + const ClientList* get_clients(void) const { return clist; } + bool next_client(void); + bool next_client_nosplash(void); + + void run(void); + //virtual void show(void); +}; + +#endif diff --git a/evoke/evoke.conf b/evoke/evoke.conf index ad779be..6a0dce2 100644 --- a/evoke/evoke.conf +++ b/evoke/evoke.conf @@ -1,29 +1,26 @@ # evoke configuration sample - -# main section -# must be present +# main section; must be present [evoke] Startup = edewm,eiconman,eworkpanel,xscreensaver + ImagesDirectory = images + Splash = splash-alpha1.png [edewm] - Icon = wm.png - Exec = edewm - Core = True - Message = Starting window manager + Icon = edewm.png + Exec = gvim + Description = window manager [eiconman] - Icon = desktop.png - Exec = eiconman - Core = True - Message = Starting desktop + Icon = eiconman.png + Exec = mozilla + Description = desktop [eworkpanel] - Icon = workpanel.png - Exec = eworkpanel - Core = True - Message = Starting workpanel + Icon = eworkpanel.png + Exec = mrxvt -bg black + Description = panel [xscreensaver] Icon = xscreensaver.png - Exec = xscreensaver - Message = Starting screensaver + Exec = rxvt + Description = screensaver diff --git a/evoke/evoke.cpp b/evoke/evoke.cpp index 5bfe81c..9fbb0ae 100644 --- a/evoke/evoke.cpp +++ b/evoke/evoke.cpp @@ -26,18 +26,22 @@ #define CONFIG_FILE "evoke.conf" #define APPNAME "evoke" #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)) -bool running = false; - void quit_signal(int sig) { EVOKE_LOG("Got quit signal %i\n", sig); - running = false; + EvokeService::instance()->stop(); } int xmessage_handler(int e) { - return EvokeService::instance()->handle(e); + return EvokeService::instance()->handle(fl_xevent); } const char* next_param(int curr, char** argv, int argc) { @@ -57,9 +61,11 @@ void help(void) { 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(" -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\n"); + puts(" -l, --log [FILE] log traffic to FILE (FILE can be stdout/stderr for console output)\n"); } int main(int argc, char** argv) { @@ -67,7 +73,9 @@ int main(int argc, char** argv) { const char* pid_file = NULL; const char* log_file = NULL; - bool do_startup = false; + bool do_startup = 0; + bool do_dryrun = 0; + bool no_splash = 0; if(argc > 1) { const char* a; @@ -99,7 +107,11 @@ int main(int argc, char** argv) { i++; } else if(CHECK_ARGV(a, "-s", "--startup")) - do_startup = true; + 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 { printf("Unknown parameter '%s'. Run '"APPNAME" -h' for options\n", a); return 1; @@ -110,20 +122,6 @@ int main(int argc, char** argv) { // make sure X11 is running before rest of code is called fl_open_display(); - // actually, evoke must not fork itself -#if 0 - // start service - if(!do_foreground) { - int x; - if((x = fork()) > 0) - exit(0); - else if(x == -1) { - printf("Fatal: fork failed !\n"); - return 1; - } - } -#endif - EvokeService* service = EvokeService::instance(); if(!service->setup_logging(log_file)) { @@ -136,8 +134,9 @@ int main(int argc, char** argv) { if(!pid_file) pid_file = DEFAULT_PID; - if(!service->setup_pid(pid_file)) { + if(!service->setup_pid(pid_file, LOCK_FILE)) { EVOKE_LOG("Either another "APPNAME" instance is running or can't create pid file. Please correct this\n"); + EVOKE_LOG("Note: if program abnormaly crashed before, just remove '%s' and start it again\n", LOCK_FILE); EVOKE_LOG("= "APPNAME" abrupted shutdown =\n"); return 1; } @@ -145,10 +144,12 @@ int main(int argc, char** argv) { if(!config_file) config_file = CONFIG_FILE; // TODO: XDG paths - if(!service->setup_config(config_file, do_startup)) { - EVOKE_LOG("Unable to read correctly %s. Please check it is correct config file\n"); - EVOKE_LOG("= "APPNAME" abrupted shutdown =\n"); - return 1; + 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("= "APPNAME" abrupted shutdown =\n"); + return 1; + } } service->setup_atoms(fl_display); @@ -157,9 +158,9 @@ int main(int argc, char** argv) { signal(SIGTERM, quit_signal); signal(SIGKILL, quit_signal); - running = true; + service->start(); - XSelectInput(fl_display, RootWindow(fl_display, fl_screen), PropertyChangeMask | StructureNotifyMask | ClientMessage); + XSelectInput(fl_display, RootWindow(fl_display, fl_screen), PropertyChangeMask | SubstructureNotifyMask | ClientMessage); /* * Register event listener and run in infinite loop. Loop will be @@ -171,7 +172,7 @@ int main(int argc, char** argv) { * So stick with the simplicity :) */ Fl::add_handler(xmessage_handler); - while(running) + while(service->running()) Fl::wait(FOREVER); EVOKE_LOG("= "APPNAME" nice shutdown =\n"); diff --git a/evoke/evoke_csize.txt b/evoke/evoke_csize.txt new file mode 100644 index 0000000..6405052 --- /dev/null +++ b/evoke/evoke_csize.txt @@ -0,0 +1,2 @@ +-rwx--x--x 1 sanel users 1794407 2007-07-30 02:27 evoke (std::vector) +-rwx--x--x 1 sanel users 1607516 2007-07-30 02:54 evoke (edelib::list) diff --git a/evoke/images/edewm.png b/evoke/images/edewm.png new file mode 100644 index 0000000..2ee792d Binary files /dev/null and b/evoke/images/edewm.png differ diff --git a/evoke/images/eiconman.png b/evoke/images/eiconman.png new file mode 100644 index 0000000..056bde3 Binary files /dev/null and b/evoke/images/eiconman.png differ diff --git a/evoke/images/eworkpanel.png b/evoke/images/eworkpanel.png new file mode 100644 index 0000000..781209a Binary files /dev/null and b/evoke/images/eworkpanel.png differ diff --git a/evoke/images/splash-alpha1.png b/evoke/images/splash-alpha1.png new file mode 100644 index 0000000..3b69121 Binary files /dev/null and b/evoke/images/splash-alpha1.png differ diff --git a/evoke/images/splash-alpha1.png.old b/evoke/images/splash-alpha1.png.old new file mode 100644 index 0000000..c6f9fcf Binary files /dev/null and b/evoke/images/splash-alpha1.png.old differ diff --git a/evoke/images/xscreensaver.png b/evoke/images/xscreensaver.png new file mode 100644 index 0000000..511d0c4 Binary files /dev/null and b/evoke/images/xscreensaver.png differ diff --git a/evoke/splash-alpha1.png b/evoke/splash-alpha1.png deleted file mode 100644 index 63b13d2..0000000 Binary files a/evoke/splash-alpha1.png and /dev/null differ diff --git a/evoke/splash.fl b/evoke/splash.fl index 45a6873..4754f66 100644 --- a/evoke/splash.fl +++ b/evoke/splash.fl @@ -5,7 +5,7 @@ code_name {.cxx} Function {} {open selected } { Fl_Window {} {open - xywh {365 175 480 364} type Double visible + xywh {365 175 480 365} type Double visible } { Fl_Box {} { image {splash-alpha1.png} xywh {0 0 480 364} labelsize 14 diff --git a/evoke/test/evoke_test.cpp b/evoke/test/evoke_test.cpp new file mode 100644 index 0000000..fb687ba --- /dev/null +++ b/evoke/test/evoke_test.cpp @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include + +#include + +Fl_Input* inp; +Fl_Double_Window* win; + +void run_cb(Fl_Widget*, void*) { + Atom _XA_EDE_EVOKE_SPAWN = XInternAtom(fl_display, "_EDE_EVOKE_SPAWN", False); + // max size + unsigned char txt_send[8192]; + int i; + const char* txt_val = inp->value() ? inp->value() : "(none)"; + int len = strlen(txt_val); + + for(i = 0; i < 8192-2 && i < len; i++) + txt_send[i] = txt_val[i]; + + txt_send[i] = '\0'; + + // send text + XChangeProperty(fl_display, RootWindow(fl_display, fl_screen), + _XA_EDE_EVOKE_SPAWN, XA_STRING, 8, PropModeReplace, + txt_send, i + 1); +} + +void evoke_quit_cb(Fl_Widget*, void*) { + Atom _XA_EDE_EVOKE_QUIT = XInternAtom(fl_display, "_EDE_EVOKE_QUIT", False); + + int dummy = 1; + XChangeProperty(fl_display, RootWindow(fl_display, fl_screen), + _XA_EDE_EVOKE_QUIT, XA_CARDINAL, 32, PropModeReplace, + (unsigned char*)&dummy, sizeof(int)); +} + +void quit_all_cb(Fl_Widget*, void*) { + Atom _XA_EDE_EVOKE_SHUTDOWN_ALL = XInternAtom(fl_display, "_EDE_EVOKE_SHUTDOWN_ALL", False); + + int dummy = 1; + XChangeProperty(fl_display, RootWindow(fl_display, fl_screen), + _XA_EDE_EVOKE_SHUTDOWN_ALL, XA_CARDINAL, 32, PropModeReplace, + (unsigned char*)&dummy, sizeof(int)); +} + +int main(int argc, char **argv) { + win = new Fl_Double_Window(355, 94, "Evoke test"); + win->begin(); + inp = new Fl_Input(10, 25, 335, 25, "Program to run:"); + inp->align(FL_ALIGN_TOP_LEFT); + Fl_Button* b1 = new Fl_Button(255, 60, 90, 25, "&Run"); + b1->callback(run_cb); + Fl_Button* b2 = new Fl_Button(150, 60, 90, 25, "&Quit evoke"); + b2->callback(evoke_quit_cb); + Fl_Button* b3 = new Fl_Button(55, 60, 90, 25, "&Quit all"); + b3->callback(quit_all_cb); + win->end(); + win->show(argc, argv); + return Fl::run(); +} diff --git a/evoke/test/evoke_test.fl b/evoke/test/evoke_test.fl new file mode 100644 index 0000000..4f2d3bc --- /dev/null +++ b/evoke/test/evoke_test.fl @@ -0,0 +1,28 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0108 +header_name {.h} +code_name {.cxx} +Function {} {open +} { + Fl_Window {} { + label {Evoke test} open + xywh {493 478 355 94} type Double visible + } { + Fl_Input {} { + label {Program to run:} selected + xywh {10 25 335 25} labelsize 14 align 5 textsize 14 + } + Fl_Button {} { + label {&Run} + xywh {255 60 90 25} labelsize 14 + } + Fl_Button {} { + label {&Quit evoke} + xywh {150 60 90 25} labelsize 14 + } + Fl_Button {} { + label {&Quit all} + xywh {55 60 90 25} labelsize 14 + } + } +}