diff --git a/evoke/Autostart.cpp b/evoke/Autostart.cpp new file mode 100644 index 0000000..b2d1134 --- /dev/null +++ b/evoke/Autostart.cpp @@ -0,0 +1,287 @@ +/* + * $Id$ + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Autostart.h" +#include "icons/warning.xpm" + +EDELIB_NS_USING(String) +EDELIB_NS_USING(DesktopFile) +EDELIB_NS_USING(list) +EDELIB_NS_USING(dir_list) +EDELIB_NS_USING(system_config_dirs) +EDELIB_NS_USING(user_config_dir) +EDELIB_NS_USING(str_ends) +EDELIB_NS_USING(run_program) + +#ifdef DEBUG_AUTOSTART_RUN + #define AUTOSTART_RUN(s) E_DEBUG("Executing %s\n", s) +#else + #define AUTOSTART_RUN(s) run_program(s, false) +#endif + +struct DialogEntry { + String name; + String exec; +}; + +typedef list StringList; +typedef list::iterator StringListIter; + +typedef list DialogEntryList; +typedef list::iterator DialogEntryListIter; + +static Fl_Window* dialog_win; +static Fl_Check_Browser* cbrowser; +static Fl_Pixmap warnpix(warning_xpm); + +static char* get_basename(const char* path) { + char* p = strrchr(path, '/'); + if(p) + return (p + 1); + + return (char*)path; +} + +/* + * 'Remove' duplicate entries by looking at their basename + * (aka. filename, but ignoring directory path). Item is not actually removed from + * the list (will mess up list pointers, but this can be resolved), but data it points + * to is cleared, which is a sort of marker to caller to skip it. Dumb yes, but very simple. + * + * It will use brute force for lookup and 'removal' and (hopfully) it should not have + * a large impact on startup since, afaik, no one keeps hundreds of files in autostart + * directories (if keeps them, then that issue is not up to this program :-P). + * + * Alternative would be to sort items (by their basename) and apply consecutive unique on + * them, but... is it worth ? + */ +static void unique_by_basename(StringList& lst) { + if(lst.empty()) + return; + + StringListIter first, last, first1, last1; + first = lst.begin(); + last = lst.end(); + + if(first == last) + return; + + const char* p1, *p2; + for(; first != last; ++first) { + for(first1 = lst.begin(), last1 = lst.end(); first1 != last1; ++first1) { + p1 = (*first).c_str(); + p2 = (*first1).c_str(); + + if(first != first1 && strcmp(get_basename(p1), get_basename(p2)) == 0) + (*first1).clear(); + } + } +} + +static void entry_list_run_clear(DialogEntryList& l, bool run) { + DialogEntryListIter dit = l.begin(), dit_end = l.end(); + for(; dit != dit_end; ++dit) { + if(run) + AUTOSTART_RUN((*dit)->exec.c_str()); + delete *dit; + } + + l.clear(); +} + +static void dialog_runsel_cb(Fl_Widget*, void* e) { + DialogEntryList* lst = (DialogEntryList*)e; + E_ASSERT(lst->size() == (unsigned int)cbrowser->nitems() && "Size mismatch in local list and browser widget"); + + DialogEntryListIter it = lst->begin(); + for(int i = 1; i <= cbrowser->nitems(); i++, ++it) { + if(cbrowser->checked(i)) + AUTOSTART_RUN((*it)->exec.c_str()); + } + + dialog_win->hide(); + entry_list_run_clear(*lst, false); +} + +static void dialog_runall_cb(Fl_Widget*, void* e) { + DialogEntryList* lst = (DialogEntryList*)e; + E_DEBUG("%i != %i\n", lst->size(), cbrowser->nitems()); + E_ASSERT(lst->size() == (unsigned int)cbrowser->nitems() && "Size mismatch in local list and browser widget"); + + dialog_win->hide(); + entry_list_run_clear(*lst, true); +} + +static void dialog_close_cb(Fl_Widget*, void* e) { + dialog_win->hide(); + + DialogEntryList* lst = (DialogEntryList*)e; + entry_list_run_clear(*lst, false); +} + +static void run_autostart_dialog(DialogEntryList& l) { + DialogEntryList* ptr = (DialogEntryList*)&l; + + dialog_win = new Fl_Window(370, 305, _("Autostart warning")); + dialog_win->begin(); + Fl_Box* img = new Fl_Box(10, 10, 65, 60); + img->image(warnpix); + Fl_Box* txt = new Fl_Box(80, 10, 280, 60, _("The following applications are registered for starting. Please chose what to do next")); + txt->align(FL_ALIGN_INSIDE | FL_ALIGN_LEFT | FL_ALIGN_WRAP); + cbrowser = new Fl_Check_Browser(10, 75, 350, 185); + + DialogEntryListIter it = l.begin(), it_end = l.end(); + for(; it != it_end; ++it) + cbrowser->add((*it)->name.c_str()); + + Fl_Button* rsel = new Fl_Button(45, 270, 125, 25, _("Run &selected")); + rsel->callback(dialog_runsel_cb, ptr); + + Fl_Button* rall = new Fl_Button(175, 270, 90, 25, _("&Run all")); + rall->callback(dialog_runall_cb, ptr); + + Fl_Button* cancel = new Fl_Button(270, 270, 90, 25, _("&Cancel")); + cancel->callback(dialog_close_cb, ptr); + cancel->take_focus(); + dialog_win->end(); + dialog_win->show(); + + while(dialog_win->shown()) + Fl::wait(); +} + +/* + * This is implementation of Autostart Spec + * (http://standards.freedesktop.org/autostart-spec/autostart-spec-0.5.html). + * + * The Autostart Directories are $XDG_CONFIG_DIRS/autostart. + * If the same filename is located under multiple Autostart Directories only the file under + * the most important directory should be used. + * + * Example: If $XDG_CONFIG_HOME is not set the Autostart Directory in the user's home directory + * is ~/.config/autostart/ + * Example: If $XDG_CONFIG_DIRS is not set the system wide Autostart Directory is /etc/xdg/autostart/ + * Example: If $XDG_CONFIG_HOME and $XDG_CONFIG_DIRS are not set and the two files + * /etc/xdg/autostart/foo.desktop and ~/.config/autostart/foo.desktop exist then only the file + * ~/.config/autostart/foo.desktop will be used because ~/.config/autostart/ is more important + * than /etc/xdg/autostart/. + * + * If Hidden key is set true in .desktop file, file MUST be ignored. + * OnlyShowIn and NotShowIn (list of strings identifying desktop environments) if (or if not) + * contains environment name, MUST not be started/not started. + * TryExec is same as for .desktop spec. + */ +void perform_autostart(bool safe) { + const char* autostart_dirname = "/autostart/"; + + String adir = edelib::user_config_dir(); + adir += autostart_dirname; + + StringList dfiles, sysdirs, tmp; + StringListIter it, it_end, tmp_it, tmp_it_end; + + dir_list(adir.c_str(), dfiles, true); + + system_config_dirs(sysdirs); + if(!sysdirs.empty()) { + for(it = sysdirs.begin(), it_end = sysdirs.end(); it != it_end; ++it) { + *it += autostart_dirname; + + /* + * append content + * FIXME: too much of copying. There should be some way to merge list items + * probably via merge() member + */ + dir_list((*it).c_str(), tmp, true); + for(tmp_it = tmp.begin(), tmp_it_end = tmp.end(); tmp_it != tmp_it_end; ++tmp_it) + dfiles.push_back(*tmp_it); + } + } + + if(dfiles.empty()) + return; + + /* + * Remove duplicates where first one seen have priority to be keept. + * This way is required by spec. + * + * Also handle this case (noted in spec): + * if $XDG_CONFIG_HOME/autostart/foo.desktop and $XDG_CONFIG_DIRS/autostart/foo.desktop + * exists, but $XDG_CONFIG_HOME/autostart/foo.desktop have 'Hidden = true', + * $XDG_CONFIG_DIRS/autostart/foo.autostart is ignored too. + * + * Latter is implied via unique_by_basename(). + */ + unique_by_basename(dfiles); + + const char* name; + char buff[1024]; + DesktopFile df; + DialogEntryList entry_list; + + for(it = dfiles.begin(), it_end = dfiles.end(); it != it_end; ++it) { + if((*it).empty()) + continue; + + name = (*it).c_str(); + + if(!str_ends(name, ".desktop")) + continue; + + if(!df.load(name)) { + E_WARNING(E_STRLOC ": Can't load '%s'. Skipping...\n", name); + continue; + } + + /* if Hidden key is set true in .desktop file, file MUST be ignored */ + if(df.hidden()) + continue; + + if(!(df.try_exec(buff, sizeof(buff)) || df.exec(buff, sizeof(buff)))) + continue; + + DialogEntry* en = new DialogEntry; + en->exec = buff; + + /* figure out the name */ + if(df.name(buff, sizeof(buff))) + en->name = buff; + else + en->name = name; + + entry_list.push_back(en); + } + + if(safe) + run_autostart_dialog(entry_list); + else + entry_list_run_clear(entry_list, true); +} diff --git a/evoke/Autostart.h b/evoke/Autostart.h new file mode 100644 index 0000000..c00f87c --- /dev/null +++ b/evoke/Autostart.h @@ -0,0 +1,18 @@ +/* + * $Id$ + * + * 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. + */ + +#ifndef __AUTOSTART_H__ +#define __AUTOSTART_H__ + +void perform_autostart(bool safe); + +#endif diff --git a/evoke/EvokeService.cpp b/evoke/EvokeService.cpp new file mode 100644 index 0000000..8497a92 --- /dev/null +++ b/evoke/EvokeService.cpp @@ -0,0 +1,231 @@ +/* + * $Id$ + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "EvokeService.h" +#include "Splash.h" +#include "Logout.h" +#include "Xsm.h" + +EDELIB_NS_USING(Config) +EDELIB_NS_USING(Resource) +EDELIB_NS_USING(RES_SYS_ONLY) +EDELIB_NS_USING(file_exists) +EDELIB_NS_USING(file_remove) +EDELIB_NS_USING(str_trim) + +#ifdef USE_LOCAL_CONFIG + #define CONFIG_GET_STRVAL(object, section, key, buff) object.get(section, key, buff, sizeof(buff)) +#else + #define CONFIG_GET_STRVAL(object, section, key, buff) object.get(section, key, buff, sizeof(buff), RES_SYS_ONLY) +#endif + +static Atom XA_EDE_EVOKE_SHUTDOWN_ALL; +static Atom XA_EDE_EVOKE_QUIT; + +static 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; +} + +EvokeService::EvokeService() : lock_name(NULL), xsm(NULL), is_running(false) { + /* TODO: or add setup_atoms() function */ + XA_EDE_EVOKE_SHUTDOWN_ALL = XInternAtom(fl_display, "_EDE_EVOKE_SHUTDOWN_ALL", False); + XA_EDE_EVOKE_QUIT = XInternAtom(fl_display, "_EDE_EVOKE_QUIT", False); +} + +EvokeService::~EvokeService() { + E_DEBUG(E_STRLOC ": EvokeService::~EvokeService()\n"); + + clear_startup_items(); + stop_xsettings_manager(true); + remove_lock(); +} + +EvokeService* EvokeService::instance(void) { + static EvokeService es; + return &es; +} + +bool EvokeService::setup_lock(const char* name) { + if(file_exists(name)) + return false; + + FILE* f = fopen(name, "w"); + if(!f) + return false; + fclose(f); + + lock_name = strdup(name); + return true; +} + +void EvokeService::remove_lock(void) { + if(!lock_name) + return; + + file_remove(lock_name); + free(lock_name); + lock_name = NULL; +} + +void EvokeService::clear_startup_items(void) { + E_DEBUG(E_STRLOC ": EvokeService::clear_startup_items()\n"); + + if(startup_items.empty()) + return; + + StartupItemListIter it = startup_items.begin(), it_end = startup_items.end(); + for(; it != it_end; ++it) + delete *it; + + startup_items.clear(); +} + +void EvokeService::read_startup(void) { +#ifdef USE_LOCAL_CONFIG + /* + * this will load SETTINGS_FILENAME only from local directory; + * intended for development and testing only + */ + Config c; + int ret = c.load("ede-startup.conf"); +#else + /* only system resource will be loaded; use ede-startup will be skipped */ + Resource c; + int ret = c.load("ede/ede-startup"); +#endif + if(!ret) { + E_WARNING(E_STRLOC ": Unable to load EDE startup file\n"); + return; + } + + char tok_buff[256], buff[256]; + + /* if nothing found, exit */ + if(!CONFIG_GET_STRVAL(c, "Startup", "start_order", tok_buff)) + return; + + if(CONFIG_GET_STRVAL(c, "Startup", "splash_theme", buff)) + splash_theme = buff; + + for(const char* sect = strtok(tok_buff, ","); sect; sect = strtok(NULL, ",")) { + /* remove leading/ending spaces, if exists */ + str_trim(buff); + + /* assure each startup item has 'exec' key */ + if(!CONFIG_GET_STRVAL(c, sect, "exec", buff)) { + E_WARNING(E_STRLOC ": Startup item '%s' does not have anything to execute. Skipping...\n", sect); + continue; + } + + StartupItem *s = new StartupItem; + s->exec = buff; + + if(CONFIG_GET_STRVAL(c, sect, "icon", buff)) + s->icon = buff; + if(CONFIG_GET_STRVAL(c, sect, "description", buff)) + s->description = buff; + + startup_items.push_back(s); + } +} + +void EvokeService::run_startup(bool splash, bool dryrun) { + if(startup_items.empty()) + return; + + Splash s(startup_items, splash_theme, splash, dryrun); + s.run(); + clear_startup_items(); +} + +void EvokeService::start_xsettings_manager(void) { + xsm = new Xsm; + + if(Xsm::manager_running(fl_display, fl_screen)) { + int ret = edelib::ask(_("XSETTINGS manager already running on this screen. Would you like to replace it?")); + if(ret < 1) { + stop_xsettings_manager(false); + return; + } + } + + if(!xsm->init(fl_display, fl_screen)) { + edelib::alert(_("Unable to load XSETTINGS manager properly :-(")); + stop_xsettings_manager(false); + } + + E_RETURN_IF_FAIL(xsm); + + if(xsm->load_serialized()) + xsm->notify(); +} + +void EvokeService::stop_xsettings_manager(bool serialize) { + if(!xsm) + return; + + if(serialize) + xsm->save_serialized(); + + delete xsm; + xsm = NULL; +} + +int EvokeService::handle(const XEvent* xev) { + if(xsm && xsm->should_terminate(xev)) { + E_DEBUG(E_STRLOC ": Terminating XSETTINGS manager on request by X event\n"); + stop_xsettings_manager(true); + return 1; + } + + if(xev->xproperty.atom == XA_EDE_EVOKE_QUIT) { + int val = get_int_property_value(XA_EDE_EVOKE_QUIT); + if(val == 1) + stop(); + } + + if(xev->xproperty.atom == XA_EDE_EVOKE_SHUTDOWN_ALL) { + int val = get_int_property_value(XA_EDE_EVOKE_SHUTDOWN_ALL); + if(val == 1) { + int dw = DisplayWidth(fl_display, fl_screen); + int dh = DisplayHeight(fl_display, fl_screen); + + logout_dialog_show(dw, dh, LOGOUT_OPT_SHUTDOWN | LOGOUT_OPT_RESTART); + } + } + + return 0; +} diff --git a/evoke/EvokeService.h b/evoke/EvokeService.h new file mode 100644 index 0000000..9951f5b --- /dev/null +++ b/evoke/EvokeService.h @@ -0,0 +1,60 @@ +/* + * $Id$ + * + * 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. + */ + + +#ifndef __EVOKESERVICE_H__ +#define __EVOKESERVICE_H__ + +#include +#include +#include + +struct StartupItem { + edelib::String exec; + edelib::String icon; + edelib::String description; +}; + +typedef edelib::list StartupItemList; +typedef edelib::list::iterator StartupItemListIter; +class Xsm; + +class EvokeService { + private: + char* lock_name; + Xsm* xsm; + bool is_running; + StartupItemList startup_items; + edelib::String splash_theme; + + void clear_startup_items(void); + public: + EvokeService(); + ~EvokeService(); + static EvokeService* instance(void); + + bool setup_lock(const char* name); + void remove_lock(void); + + void start(void) { is_running = true; } + void stop(void) { is_running = false; } + bool running(void) { return is_running; } + + void read_startup(void); + void run_startup(bool splash, bool dryrun); + int handle(const XEvent* xev); + + void start_xsettings_manager(void); + void stop_xsettings_manager(bool serialize); +}; + +#endif diff --git a/evoke/Jamfile b/evoke/Jamfile new file mode 100644 index 0000000..fa2422b --- /dev/null +++ b/evoke/Jamfile @@ -0,0 +1,31 @@ +# +# $Id$ +# +# Part of Equinox Desktop Environment (EDE). +# Copyright (c) 2009 EDE Authors. +# +# This program is licensed under terms of the +# GNU General Public License version 2 or newer. +# See COPYING for details. + +SubDir TOP evoke ; + +# use SIGHUP for now as default +ObjectC++Flags evoke.cpp : -DUSE_SIGHUP ; + +SOURCE = evoke.cpp + EvokeService.cpp + Splash.cpp + Xsm.cpp + Logout.cpp + Autostart.cpp ; + +#ObjectC++Flags $(SOURCE) : -DUSE_LOCAL_CONFIG ; + +EdeProgram evoke : $(SOURCE) ; +TranslationStrings locale : $(SOURCE) ; +EdeManual doc/evoke.txt ; + +FltkProgramBare test/evoke_test : test/evoke_test.cpp : "noinstall" ; + +SubInclude TOP evoke splash-themes ; diff --git a/evoke/Logout.cpp b/evoke/Logout.cpp new file mode 100644 index 0000000..6bccbf7 --- /dev/null +++ b/evoke/Logout.cpp @@ -0,0 +1,95 @@ +/* + * $Id$ + * + * Evoke, head honcho of everything + * Part of Equinox Desktop Environment (EDE). + * Copyright (c) 2008-2009 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 "Logout.h" + +/* Note that order of initialized items is important so LOGOUT_OPT_XXX can work */ +struct LogoutOptions { + const char* opt_short; + const char* opt_long; +} logout_options[] = { + { _("Logout"), _("This option will close all programs and logs out from the current session") }, + { _("Restart"), _("This option will restart the computer closing all running programs") }, + { _("Shut down"), _("This option will shut down the computer closing all running programs") } +}; + +static Fl_Window* win; +static Fl_Box* description; +static int ret_option; + +static void ok_cb(Fl_Widget*, void*) { + win->hide(); +} + +static void cancel_cb(Fl_Widget*, void*) { + ret_option = LOGOUT_RET_CANCEL; + win->hide(); +} + +static void option_cb(Fl_Widget*, void* o) { + Fl_Choice* c = (Fl_Choice*)o; + int v = c->value(); + + description->label(logout_options[v].opt_long); + ret_option = v; +} + +int logout_dialog_show(int screen_w, int screen_h, int opt) { + ret_option = LOGOUT_RET_LOGOUT; + + win = new Fl_Window(335, 180, _("Quit EDE?")); + win->begin(); + Fl_Box* b1 = new Fl_Box(10, 9, 315, 25, _("How do you want to quit EDE?")); + b1->labelfont(1); + b1->align(196|FL_ALIGN_INSIDE); + + Fl_Choice* c = new Fl_Choice(10, 45, 315, 25); + c->down_box(FL_BORDER_BOX); + + /* fill choice menu */ + c->add(logout_options[0].opt_short, 0, option_cb, c); + if(opt & LOGOUT_OPT_RESTART) + c->add(logout_options[1].opt_short, 0, option_cb, c); + if(opt & LOGOUT_OPT_SHUTDOWN) + c->add(logout_options[2].opt_short, 0, option_cb, c); + + description = new Fl_Box(10, 80, 315, 55); + description->align(197|FL_ALIGN_INSIDE); + + /* set to first menu item */ + c->value(0); + description->label(logout_options[0].opt_long); + + Fl_Button* ok = new Fl_Button(140, 145, 90, 25, _("&OK")); + ok->callback(ok_cb, c); + Fl_Button* cancel = new Fl_Button(235, 145, 90, 25, _("&Cancel")); + cancel->callback(cancel_cb); + win->end(); + + /* so when X in titlebar was clicked, we can get LOGOUT_RET_CANCEL */ + win->callback(cancel_cb); + + win->position(screen_w / 2 - win->w() / 2, screen_h / 2 - win->h() / 2); + win->show(); + + while(win->shown()) + Fl::wait(); + + return ret_option; +} diff --git a/evoke/Logout.h b/evoke/Logout.h new file mode 100644 index 0000000..c7ab1ba --- /dev/null +++ b/evoke/Logout.h @@ -0,0 +1,26 @@ +/* + * $Id$ + * + * Evoke, head honcho of everything + * Part of Equinox Desktop Environment (EDE). + * Copyright (c) 2008-2009 EDE Authors. + * + * This program is licensed under terms of the + * GNU General Public License version 2 or newer. + * See COPYING for details. + */ + +#ifndef __LOGOUT_H__ +#define __LOGOUT_H__ + +#define LOGOUT_RET_CANCEL -1 +#define LOGOUT_RET_LOGOUT 0 +#define LOGOUT_RET_RESTART 1 +#define LOGOUT_RET_SHUTDOWN 2 + +#define LOGOUT_OPT_RESTART (1 << 1) +#define LOGOUT_OPT_SHUTDOWN (1 << 2) + +int logout_dialog_show(int screen_w, int screen_h, int opt); + +#endif diff --git a/evoke/Splash.cpp b/evoke/Splash.cpp new file mode 100644 index 0000000..1c03767 --- /dev/null +++ b/evoke/Splash.cpp @@ -0,0 +1,311 @@ +/* + * $Id$ + * + * 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. + */ + +#include // snprintf +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Splash.h" + +#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 */ + +EDELIB_NS_USING(String) +EDELIB_NS_USING(Resource) +EDELIB_NS_USING(build_filename) +EDELIB_NS_USING(dir_exists) +EDELIB_NS_USING(run_program) +EDELIB_NS_USING(RES_SYS_ONLY) + +#ifndef EDEWM_HAVE_NET_SPLASH +static Splash* global_splash = NULL; + +static int splash_xmessage_handler(int e) { + if(fl_xevent->type == MapNotify) { + XRaiseWindow(fl_display, fl_xid(global_splash)); + return 1; + } + + if(fl_xevent->type == ConfigureNotify) { + if(fl_xevent->xconfigure.event == DefaultRootWindow(fl_display)) { + XRaiseWindow(fl_display, fl_xid(global_splash)); + return 1; + } + } + + return 0; +} +#endif + +/* + * repeatedly call runner() until all clients are + * started then hide splash window + */ +static 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(StartupItemList& s, String& theme, bool show_it, bool dr) : Fl_Double_Window(480, 365) { + slist = &s; + splash_theme = &theme; + show_splash = show_it; + dryrun = dr; + icons = NULL; + counter = 0; + + box(FL_BORDER_BOX); +} + + +Splash::~Splash() { + E_DEBUG(E_STRLOC ": Cleaning splash data\n"); + /* elements of icons cleans Fl_Group */ + delete [] icons; + Fl::remove_timeout(runner_cb); +} + + +/* after edewm got _NET_WM_WINDOW_TYPE_SPLASH support */ +#if EDEWM_HAVE_NET_SPLASH +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) { + E_ASSERT(slist != NULL); + + if(!show_splash) { + while(next_client_nosplash()) + ; + return; + } + + fl_register_images(); + + String path, splash_theme_path; + +#ifdef USE_LOCAL_CONFIG + splash_theme_path = "splash-themes/"; +#else + splash_theme_path = Resource::find_data("ede/splash-themes", RES_SYS_ONLY); + if(splash_theme_path.empty()) { + E_WARNING(E_STRLOC ": Unable to locate splash themes in $XDG_DATA_DIRS directories\n"); + return; + } +#endif + splash_theme_path += E_DIR_SEPARATOR; + splash_theme_path += *splash_theme; + + if(!dir_exists(splash_theme_path.c_str())) { + E_WARNING(E_STRLOC ": Unable to locate '%s' in '%s' theme directory\n", + splash_theme->c_str(), splash_theme_path.c_str()); + return; + } + + /* setup widgets */ + begin(); + Fl_Box* bimg = new Fl_Box(0, 0, w(), h()); + Fl_Image* splash_img = 0; + + path = build_filename(splash_theme_path.c_str(), "background.png"); + splash_img = Fl_Shared_Image::get(path.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*[slist->size()]; + + icon_group->begin(); + int i = 0; + const char* imgpath; + Fl_Image* iconimg = 0; + + for(StartupItemListIter it = slist->begin(); it != slist->end(); ++it, ++i) { + Fl_Box* bb = new Fl_Box(X, Y, 64, 64); + + path = build_filename(splash_theme_path.c_str(), (*it)->icon.c_str()); + imgpath = path.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); + + // to keep splash at the top +#ifndef EDEWM_HAVE_NET_SPLASH + global_splash = this; + XSelectInput(fl_display, RootWindow(fl_display, fl_screen), SubstructureNotifyMask); + Fl::add_handler(splash_xmessage_handler); +#endif + + while(shown()) + Fl::wait(); + +#ifndef EDEWM_HAVE_NET_SPLASH + Fl::remove_handler(splash_xmessage_handler); +#endif +} + +/* called when splash option is on */ +bool Splash::next_client(void) { + if(slist->empty()) + return false; + + if(counter == 0) + slist_it = slist->begin(); + + if(slist_it == slist->end()) { + counter = 0; + return false; + } + + + E_ASSERT(counter < slist->size() && "Internal error; 'counter' out of bounds"); + + char buff[1024]; + const char* msg = (*slist_it)->description.c_str(); + const char* cmd = (*slist_it)->exec.c_str(); + snprintf(buff, sizeof(buff), _("Starting %s..."), msg); + + icons[counter]->show(); + msgbox->copy_label(buff); + redraw(); + + /* run command */ + if(!dryrun) + run_program(cmd, false); + + ++slist_it; + ++counter; + return true; +} + +/* called when splash option is off */ +bool Splash::next_client_nosplash(void) { + if(slist->empty()) + return false; + + if(counter == 0) + slist_it = slist->begin(); + + if(slist_it == slist->end()) { + counter = 0; + return false; + } + + E_ASSERT(counter < slist->size() && "Internal error; 'counter' out of bounds"); + + char buff[1024]; + const char* msg = (*slist_it)->description.c_str(); + const char* cmd = (*slist_it)->exec.c_str(); + snprintf(buff, sizeof(buff), _("Starting %s..."), msg); + + printf("%s\n", buff); + + /* run command */ + if(!dryrun) + run_program(cmd, false); + + ++slist_it; + ++counter; + return true; +} diff --git a/evoke/Splash.h b/evoke/Splash.h new file mode 100644 index 0000000..63e08ea --- /dev/null +++ b/evoke/Splash.h @@ -0,0 +1,48 @@ +/* + * $Id$ + * + * 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. + */ + +#ifndef __SPLASH_H__ +#define __SPLASH_H__ + +#include +#include + +#include "EvokeService.h" + +class Splash : public Fl_Double_Window { + private: + StartupItemList* slist; + StartupItemListIter slist_it; + edelib::String* splash_theme; + + unsigned int counter; + bool show_splash; + bool dryrun; + + Fl_Box* msgbox; + Fl_Box** icons; + + public: + Splash(StartupItemList& s, edelib::String& theme, bool show_it, bool dr); + ~Splash(); + + bool next_client(void); + bool next_client_nosplash(void); + + void run(void); + +#if EDEWM_HAVE_NET_SPLASH + virtual void show(void); +#endif +}; + +#endif diff --git a/evoke/Xsm.cpp b/evoke/Xsm.cpp new file mode 100644 index 0000000..f12e70f --- /dev/null +++ b/evoke/Xsm.cpp @@ -0,0 +1,375 @@ +/* + * $Id$ + * + * 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. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Xsm.h" + +#define USER_XRESOURCE ".Xdefaults" +#define USER_XRESOURCE_TMP ".Xdefaults-tmp" +#define USER_XRESOURCE_SAVED ".Xdefaults-ede-saved" + +#define SETTINGS_FILENAME "ede-settings" + +EDELIB_NS_USING(String) +EDELIB_NS_USING(Resource) +EDELIB_NS_USING(XSettingsSetting) +EDELIB_NS_USING(XSettingsList) +EDELIB_NS_USING(dir_home) +EDELIB_NS_USING(file_remove) +EDELIB_NS_USING(file_rename) +EDELIB_NS_USING(build_filename) +EDELIB_NS_USING(user_config_dir) +EDELIB_NS_USING(xsettings_list_find) +EDELIB_NS_USING(xsettings_list_free) +EDELIB_NS_USING(xsettings_decode) +EDELIB_NS_USING(color_rgb_to_fltk) +EDELIB_NS_USING(color_fltk_to_html) +EDELIB_NS_USING(XSETTINGS_TYPE_COLOR) +EDELIB_NS_USING(XSETTINGS_TYPE_INT) +EDELIB_NS_USING(XSETTINGS_TYPE_STRING) + +struct ResourceMap { + char* name; + char* xresource_key; + char* xresource_klass; +}; + +/* + * Make sure xresource_klass with '*' is listed last since it have + * highest priority and will override all previous classes (X Resource class, not C++ one :P) + * with the same xresource_key. + */ +static ResourceMap resource_map [] = { + { "Fltk/Background2", "background", "*Text" }, + { "Fltk/Background", "background", "*" }, + { "Fltk/Foreground", "foreground", "*" } +}; + +#define RESOURCE_MAP_SIZE(x) (sizeof(x)/sizeof(x[0])) + +static int ignore_xerrors(Display* display, XErrorEvent* xev) { + return True; +} + +Xsm::Xsm() { +} + +Xsm::~Xsm() { + E_DEBUG(E_STRLOC ": Xsm::~Xsm()\n"); +} + +/* + * This is a short explaination how evoke's XSETTINGS part is combined + * with X Resource database (xrdb). First of all, why mess with this? Almost + * all pure X apps (xterm, xedit, rxvt) reads color (and more) from xrdb, not to say + * that FLTK apps do that too, at least those not linked with edelib::Window. On other + * hand since edelib::Window already have builtin XSETTINGS and FLTK backend, you will + * that colors for edelib::Window will be specified twice, but this is not a big deal + * since painting is done only once. + * + * Here, in the code, we look for XSETTINGS names listed in resource_map[] and they should + * be colors only; when they are found, their equivalents will be created in xrdb as class/key + * (see X Resource manual about these). + * + * Values picked up from XSETTINGS color items will be converted to html since because clients + * who reads xrdb expects html colors (or X11 ones, like red/green/blue names, but let we not + * complicate). After conversion, everything is stored in ~/.Xdefaults file. If this file + * already exists (someone else made it), it will be first merged, picking old values and backed up. + * + * After evoke quits, file is restored, if existed before or deleted if not. This is also a + * workaround for missing functions to delete key/value pairs from xrdb (what was they thinking for??). + */ +void Xsm::xresource_replace(void) { + // with inheritance we got manager_data + if(!manager_data->settings) + return; + + String home = dir_home(); + + // try to open ~/.Xdefaults; if failed, X Resource will not complain + String db_file = build_filename(home.c_str(), USER_XRESOURCE); + + // initialize XResource manager + XrmInitialize(); + + // load XResource database + XrmDatabase db = XrmGetFileDatabase(db_file.c_str()); + + XSettingsSetting* s; + int status; + char* type, *value; + XrmValue xrmv; + char color_val[8]; + + String tmp; + + /* + * XSETTINGS does not contains duplicate entries so there is no need to + * check for them. We only scan ResourceMap table for XSETTINGS name and + * its X Resource equivalent. + */ + for(unsigned int i = 0; i < RESOURCE_MAP_SIZE(resource_map); i++) { + s = xsettings_list_find(manager_data->settings, resource_map[i].name); + if(!s) + continue; + + // assure that XSETTINGS key is color type + if(s->type != XSETTINGS_TYPE_COLOR) { + E_WARNING(E_STRLOC ": Expected color type in %s, but it is not, skipping...\n", s->name); + continue; + } + + // check if resource is present + status = XrmGetResource(db, resource_map[i].xresource_key, resource_map[i].xresource_klass, &type, &xrmv); + + if(status && strcmp(type, "String") == 0) { + E_DEBUG(E_STRLOC ": %s.%s found in database\n", + resource_map[i].xresource_klass, resource_map[i].xresource_key); + + value = xrmv.addr; + } + + /* + * Now convert color from XSETTINGS to html value. First convert to fltk color. + * TODO: Strange, didn't implemented something like color_rgb_to_html in edelib ? + */ + int fltk_color = color_rgb_to_fltk(s->data.v_color.red, s->data.v_color.green, s->data.v_color.blue); + color_fltk_to_html(fltk_color, color_val); + + // and save it + tmp.clear(); + tmp.printf("%s.%s: %s", resource_map[i].xresource_klass, resource_map[i].xresource_key, color_val); + XrmPutLineResource(&db, tmp.c_str()); + } + + String tmp_db_file = build_filename(home.c_str(), USER_XRESOURCE_TMP); + + /* + * Try to merge existing ~/.Xdefaults (if present) with our changes. If there is existing + * key/values, they will be replaced with our. If XrmCombineFileDatabase() fails, this means + * there is no ~/.Xdefaults, so we don't need to backup it; opposite, backup it before we do + * final rename. + */ + status = XrmCombineFileDatabase(db_file.c_str(), &db, 0); + XrmPutFileDatabase(db, tmp_db_file.c_str()); + XrmDestroyDatabase(db); + + if(status) { + String db_backup = build_filename(home.c_str(), USER_XRESOURCE_SAVED); + file_rename(db_file.c_str(), db_backup.c_str()); + } + + file_rename(tmp_db_file.c_str(), db_file.c_str()); +} + +void Xsm::xresource_undo(void) { + String home, db_file_backup, db_file; + + home = dir_home(); + db_file_backup = build_filename(home.c_str(), USER_XRESOURCE_SAVED); + db_file = build_filename(home.c_str(), USER_XRESOURCE); + + /* + * If we have backup, restore it; otherwise delete ~/.Xdefaults. + * TODO: what if user something write in it? Changes will be lost... + */ + if(!file_rename(db_file_backup.c_str(), db_file.c_str())) + file_remove(db_file.c_str()); +} + +bool Xsm::load_serialized(void) { +#ifdef USE_LOCAL_CONFIG + /* + * this will load SETTINGS_FILENAME only from local directory; + * intended for development and testing only + */ + String file = SETTINGS_FILENAME".conf"; +#else + /* try to find it in home directory, then will scan for the system one */ + String file = Resource::find_config("ede/"SETTINGS_FILENAME); + if(file.empty()) { + E_WARNING(E_STRLOC ": Unable to load XSETTINGS data from '%s'\n", file.c_str()); + return false; + } +#endif + + TiXmlDocument doc(file.c_str()); + if(!doc.LoadFile()) + return false; + + const char* name = NULL, *type = NULL; + const char* v_string = NULL; + int v_int = 0; + int v_red = 0, v_green = 0, v_blue = 0, v_alpha = 0; + int cmp = 0; + + TiXmlNode* elem = doc.FirstChild("ede-settings"); + if(!elem) + return false; + + for(elem = elem->FirstChildElement(); elem; elem = elem->NextSibling()) { + if(strcmp(elem->Value(), "setting") != 0) { + E_WARNING(E_STRLOC ": Got unknown child in 'ede-setting' %s\n", elem->Value()); + continue; + } + + name = elem->ToElement()->Attribute("name"); + if(!name) { + E_WARNING(E_STRLOC ": Missing name key\n"); + continue; + } + + type = elem->ToElement()->Attribute("type"); + if(!type) { + E_WARNING(E_STRLOC ": Missing type key\n"); + continue; + } + + if(strcmp(type, "int") == 0) + cmp = 1; + else if(strcmp(type, "string") == 0) + cmp = 2; + else if(strcmp(type, "color") == 0) + cmp = 3; + else { + E_WARNING(E_STRLOC ": Unknown type %s\n", type); + continue; + } + + switch(cmp) { + case 1: + if(elem->ToElement()->QueryIntAttribute("value", &v_int) == TIXML_SUCCESS) + set(name, v_int); + else + E_WARNING(E_STRLOC ": Unable to query integer value\n"); + break; + case 2: + v_string = elem->ToElement()->Attribute("value"); + if(v_string) + set(name, v_string); + break; + case 3: + if((elem->ToElement()->QueryIntAttribute("red", &v_red) == TIXML_SUCCESS) && + (elem->ToElement()->QueryIntAttribute("green", &v_green) == TIXML_SUCCESS) && + (elem->ToElement()->QueryIntAttribute("blue", &v_blue) == TIXML_SUCCESS) && + (elem->ToElement()->QueryIntAttribute("alpha", &v_alpha) == TIXML_SUCCESS)) { + set(name, v_red, v_green, v_blue, v_alpha); + } + break; + default: + break; + } + } + + xresource_replace(); + return true; +} + +bool Xsm::save_serialized(void) { + Atom type; + int format; + unsigned long n_items, bytes_after; + unsigned char* data; + int result; + XSettingsList* settings = NULL, *iter = NULL; + + int (*old_handler)(Display*, XErrorEvent*); + + /* possible ? */ + if(!manager_data->manager_win) + return false; + + old_handler = XSetErrorHandler(ignore_xerrors); + result = XGetWindowProperty(manager_data->display, manager_data->manager_win, manager_data->xsettings_atom, + 0, LONG_MAX, False, manager_data->xsettings_atom, + &type, &format, &n_items, &bytes_after, (unsigned char**)&data); + + XSetErrorHandler(old_handler); + if(result == Success && type != None) { + if(type != manager_data->xsettings_atom) + E_WARNING(E_STRLOC ": Invalid type for XSETTINGS property\n"); + else if(format != 8) + E_WARNING(E_STRLOC ": Invalid format for XSETTINGS property\n"); + else + settings = xsettings_decode(data, n_items, NULL); + XFree(data); + } + + if(!settings) + return false; + +#ifdef USE_LOCAL_CONFIG + /* + * this will load SETTINGS_FILENAME only from local directory; + * intended for development and testing only + */ + String file = SETTINGS_FILENAME".conf"; +#else + String file = user_config_dir(); + file += "/ede/"SETTINGS_FILENAME".conf"; +#endif + + FILE* setting_file = fopen(file.c_str(), "w"); + if(!setting_file) { + E_WARNING(E_STRLOC ": Unable to write to %s\n", file.c_str()); + xsettings_list_free(settings); + return false; + } + + fprintf(setting_file, "\n"); + fprintf(setting_file, "\n"); + + iter = settings; + while(iter) { + fprintf(setting_file, " setting->name); + switch(iter->setting->type) { + case XSETTINGS_TYPE_INT: + fprintf(setting_file, "type=\"int\" value=\"%i\" />\n", iter->setting->data.v_int); + break; + case XSETTINGS_TYPE_STRING: + fprintf(setting_file, "type=\"string\" value=\"%s\" />\n", iter->setting->data.v_string); + break; + case XSETTINGS_TYPE_COLOR: + fprintf(setting_file, "type=\"color\" red=\"%i\" green=\"%i\" blue=\"%i\" alpha=\"%i\" />\n", + iter->setting->data.v_color.red, + iter->setting->data.v_color.green, + iter->setting->data.v_color.blue, + iter->setting->data.v_color.alpha); + break; + } + + iter = iter->next; + } + + fprintf(setting_file, "\n"); + + fclose(setting_file); + xsettings_list_free(settings); + xresource_undo(); + + return true; +} diff --git a/evoke/Xsm.h b/evoke/Xsm.h new file mode 100644 index 0000000..c512c88 --- /dev/null +++ b/evoke/Xsm.h @@ -0,0 +1,36 @@ +/* + * $Id$ + * + * 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. + */ + +#ifndef __XSM_H__ +#define __XSM_H__ + +#include + +/* + * XSETTINGS manager with serialization capability. + * Also it will write/undo to xrdb (X Resource database). + */ +class Xsm : public edelib::XSettingsManager { + public: + Xsm(); + ~Xsm(); + + bool load_serialized(void); + bool save_serialized(void); + + /* replace XResource values from one from XSETTINGS */ + void xresource_replace(void); + /* undo old XResource values */ + void xresource_undo(void); +}; + +#endif diff --git a/evoke/doc/evoke.txt b/evoke/doc/evoke.txt new file mode 100644 index 0000000..f82f7fb --- /dev/null +++ b/evoke/doc/evoke.txt @@ -0,0 +1,121 @@ +evoke documentation +=================== + +[NOTE] +.TODO +===================== +Add info about splash themes; reorder document +===================== + +evoke is EDE service responsible for starting and quitting desktop, including +all needed prerequisites. Also, it will autostart programs (run programs using +http://www.freedesktop.org/wiki/Specifications/autostart-spec[autostart specification]) +and behave as http://www.freedesktop.org/wiki/Specifications/xsettings-spec[XSETTINGS] manager. + +It is usually started at EDE startup, from 'startede' script and will allow only one instance of +itself to be running. + +Options +------- + +-s, --startup:: + Run in starup mode. Startup mode should be used when environment is + starting and it will read 'ede-startup.conf' file looking for components to + be started. + +-n, --no-splash:: + Do not display splash during startup. Only usefull if in startup mode + (given '-s' or '--startup'). + +-d, --dry-run:: + Only useful in startup mode (if '-s' or '--startup' are given). Mainly to test + splash screen themes + +-a, --autostart:: + Run in autostart mode. It will check 'autostart' directory and run .desktop + files from it. This will be done according to freedesktop.org autostart specification. + +-u, --autostart-safe:: + Same as '-a' or '--autostart' option, but display dialog with items that should + be run. + +-c, --config [FILE]:: + Read [FILE] as config file. This file is only used in startup mode to read + components to be started. + +-h, --help:: + This help. + + +Details +------- + +evoke can be started with or without options. If started without options, it will behave +as XSETTINGS manager, reading options from 'ede-settings.conf'. + +[NOTE] +.XSETTINGS support +================================== +Not all toolkits supports XSETTINGS protocol; currently only Gtk+ and edelib apps understainds +it (as I know). +================================== + +As you can see from options, there are two 'modes' (if parameteres for them are given): + + * autstart mode + * startup mode + +*Autostart mode* is used to run applications from 'autostart' directory. This directory is +usually located at $HOME/.config/autostart or if not exists, in /etc/xdg/autostart. Of course, +both directories can exists and evoke will run applications from both, but with one exception: +if application with the same .desktop name exists in both (and is valid desktop file), it will +be run only from prefered directory, which is $HOME/.config/autostart. + +If you want some application to be run at EDE startup (implying evoke is started with '-a' option), +just copy it's .desktop file to autostart directory. Before that, make sure that .desktop file +confirms to http://freedesktop.org specification for Desktop Files. + +On other hand if you want to disable running some application from autostart directory, you can +simply delete that file, or set this: +--------------- +Hidden = true +--------------- +Make sure that 'Hidden' key is under '[Desktop Entry]' section. + +Autostart mode can be run in two ways: safe and unsafe. Safe way ('-u' or '--autostart-safe') will +popup window with programs that are 'registered' for starting, from where you can choose either to +start some of them, start all of them or to start nothing. On other hand, unsafe way ('-a' or '--autostart') +will simply run all 'registered' items without any questioning (should I say that this can be very insecure). + +These options are intentionally provided so you can choose startup policy depending on your security +options. + +*Startup mode* is meant to be used when EDE is starting, running each component (or program) +from 'ede-startup.conf' (or file specified with '-c' option). This configuration file must be in the following form: +-------------- +[Startup] + # start_order key is used to notify evoke what to start + # and program keys must be separated with comma (if multiple ones are given) + start_order = program1, program2, program3 + + # splash_data is directory with icons used for splash screen + splash_data = /some/path/some_dir_name + +# now comes each value from Startup key as separate key +[program1] + # Icon is icon used in splash screen progress (searched in ImagesDirectory) + Icon = icon1.png + + # Exec is program executable to be run + Exec = program1 + + # Description is used to describe what is starting + Description = my cool program + +[program2] + Icon = icon2.png + Exec = program2 + Description = my cool second program + +... +-------------- diff --git a/evoke/ede-settings.conf b/evoke/ede-settings.conf new file mode 100644 index 0000000..f906a9f --- /dev/null +++ b/evoke/ede-settings.conf @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/evoke/ede-startup.conf b/evoke/ede-startup.conf new file mode 100644 index 0000000..751ac56 --- /dev/null +++ b/evoke/ede-startup.conf @@ -0,0 +1,24 @@ +# ede-startup configuration sample +[Startup] + start_order = edewm,eiconman,eworkpanel,xscreensaver + splash_theme = scape + +[edewm] + exec = edewm + icon = edewm.png + description = window manager + +[eiconman] + exec = eiconman + icon = eiconman.png + description = desktop + +[eworkpanel] + exec = eworkpanel + icon = eworkpanel.png + description = panel + +[xscreensaver] + exec = xscreensaver -nosplash + icon = xscreensaver.png + description = screensaver diff --git a/evoke/evoke.cpp b/evoke/evoke.cpp new file mode 100644 index 0000000..1cb7c9a --- /dev/null +++ b/evoke/evoke.cpp @@ -0,0 +1,153 @@ +/* + * $Id$ + * + * 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 +#endif + +#include +#include +#include +#include +#include + +#include "EvokeService.h" +#include "Autostart.h" + +#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 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\n"); +} + +int main(int argc, char** argv) { + bool do_startup = false; + bool do_dryrun = false; + bool show_splash = true; + bool do_autostart = false; + bool do_autostart_safe = false; + + 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", "--starup")) + 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 if(CHECK_ARGV(a, "-a", "--autostart")) + do_autostart = true; + else if(CHECK_ARGV(a, "-u", "--autostart-safe")) + do_autostart_safe = true; + 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(); + + /* + * 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 + + if(do_autostart || do_autostart_safe) + perform_autostart(do_autostart_safe); + + 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); + + 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; +} diff --git a/evoke/fl/autostart.fl b/evoke/fl/autostart.fl new file mode 100644 index 0000000..dee328e --- /dev/null +++ b/evoke/fl/autostart.fl @@ -0,0 +1,33 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0108 +header_name {.h} +code_name {.cxx} +Function {} {open selected +} { + Fl_Window {} {open + xywh {344 264 370 305} type Double visible + } { + Fl_Box {} { + label {The following applications are registered to be started. Please choose what to do next} + xywh {80 10 280 61} align 148 + } + Fl_Check_Browser {} { + xywh {10 75 350 185} labelsize 14 + } + Fl_Button {} { + label {&Cancel} + xywh {270 270 90 25} + } + Fl_Button {} { + label {&Run all} + xywh {175 270 90 25} + } + Fl_Button {} { + label {Run &selected} + xywh {45 270 125 25} + } + Fl_Box {} { + image {../icons/warning.xpm} xywh {10 10 65 61} labelsize 14 + } + } +} diff --git a/evoke/fl/logout.fl b/evoke/fl/logout.fl new file mode 100644 index 0000000..7c74ae6 --- /dev/null +++ b/evoke/fl/logout.fl @@ -0,0 +1,30 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0108 +header_name {.h} +code_name {.cxx} +Function {} {open +} { + Fl_Window {} {open + xywh {479 284 335 180} type Double visible + } { + Fl_Box {} { + label {How do you want to quit?} + xywh {10 9 315 25} labelfont 1 align 212 + } + Fl_Choice {} {open + xywh {10 45 315 25} down_box BORDER_BOX labelsize 14 textsize 14 + } {} + Fl_Box {} { + label {This option will close all programs and logs out from the current session} + xywh {10 80 315 55} align 213 + } + Fl_Button {} { + label {&OK} selected + xywh {140 145 90 25} + } + Fl_Button {} { + label {&Cancel} + xywh {235 145 90 25} + } + } +} diff --git a/evoke/icons/warning.xpm b/evoke/icons/warning.xpm new file mode 100644 index 0000000..447467b --- /dev/null +++ b/evoke/icons/warning.xpm @@ -0,0 +1,319 @@ +/* XPM */ +static char * warning_xpm[] = { +"32 32 284 2", +" c None", +". c #D04D00", +"+ c #D24E00", +"@ c #C64900", +"# c #E37B00", +"$ c #D14E00", +"% c #D55600", +"& c #FCBF00", +"* c #EE9A00", +"= c #FFC700", +"- c #DA6500", +"; c #FFD32E", +"> c #FFFDCB", +", c #CB4C00", +"' c #F4AA02", +") c #FFF3AB", +"! c #FFFFE1", +"~ c #B44300", +"{ c #E37C00", +"] c #FFDE58", +"^ c #FFFFE0", +"/ c #FFFFDF", +"( c #FFDE56", +"_ c #F9BF0D", +": c #FFF9C3", +"< c #C6C6B8", +"[ c #969696", +"} c #FFF8C1", +"| c #BD4600", +"1 c #E99002", +"2 c #FFEA7C", +"3 c #EDEDD4", +"4 c #979797", +"5 c #989898", +"6 c #949494", +"7 c #D9D9C6", +"8 c #FFEA7A", +"9 c #E98F01", +"0 c #D85F00", +"a c #FFDA26", +"b c #FFFCCD", +"c c #E5E5C8", +"d c #C9C9B6", +"e c #FFFCCB", +"f c #FFD924", +"g c #C94B00", +"h c #F1AB01", +"i c #FFF292", +"j c #FFFFD3", +"k c #EBEBC7", +"l c #959595", +"m c #929292", +"n c #DCDCBC", +"o c #FFFFD1", +"p c #FFF28C", +"q c #DD7100", +"r c #FFE43F", +"s c #FFFEC5", +"t c #FFFFCB", +"u c #FFFFCC", +"v c #989894", +"w c #919191", +"x c #909090", +"y c #8E8E8E", +"z c #F8F8C6", +"A c #FFFFC7", +"B c #FFFEBF", +"C c #FFE236", +"D c #F9CC0A", +"E c #FFF99D", +"F c #FFFFC4", +"G c #FFFFC3", +"H c #B7B7A1", +"I c #8B8B8B", +"J c #8A8A8A", +"K c #9E9E93", +"L c #FFFFC1", +"M c #FFFFBE", +"N c #FFFFBC", +"O c #FFF892", +"P c #F9CB06", +"Q c #B84500", +"R c #E68D00", +"S c #FFEC51", +"T c #FFFFBA", +"U c #FFFFB9", +"V c #FFFFBB", +"W c #D1D1A6", +"X c #838383", +"Y c #B8B898", +"Z c #FFFFB6", +"` c #FFFFB3", +" . c #FFFFAF", +".. c #FFFFAB", +"+. c #FFEA43", +"@. c #D55700", +"#. c #FCDB13", +"$. c #FFFB97", +"%. c #FFFFAE", +"&. c #EEEEA8", +"*. c #7A7A7A", +"=. c #797979", +"-. c #DCDC9E", +";. c #FFFFA9", +">. c #FFFFA5", +",. c #FFFFA1", +"'. c #FFFF9E", +"). c #FFFB82", +"!. c #FCDA0C", +"~. c #EEAC00", +"{. c #FFF356", +"]. c #FFFFA4", +"^. c #FFFFA2", +"/. c #FFFFA3", +"(. c #8B8B79", +"_. c #6E6E6E", +":. c #FFFF9D", +"<. c #FFFF9B", +"[. c #FFFF98", +"}. c #FFFF93", +"|. c #FFFF8D", +"1. c #FFFF8B", +"2. c #FFF243", +"3. c #DA6B00", +"4. c #FFEB18", +"5. c #FFFE8A", +"6. c #FFFF92", +"7. c #FFFF94", +"8. c #A7A776", +"9. c #88886C", +"0. c #FFFF8A", +"a. c #FFFF87", +"b. c #FFFF82", +"c. c #FFFF7C", +"d. c #FFFF78", +"e. c #FFFD6D", +"f. c #FFEA0E", +"g. c #F4C400", +"h. c #FFF854", +"i. c #FFFF81", +"j. c #FFFF80", +"k. c #F4F47C", +"l. c #E9E978", +"m. c #FFFF7A", +"n. c #FFFF77", +"o. c #FFFF73", +"p. c #FFFF6F", +"q. c #FFFF6B", +"r. c #FFFF65", +"s. c #FFFF62", +"t. c #FFF73A", +"u. c #E38A00", +"v. c #FFF21E", +"w. c #FFFF69", +"x. c #FFFF6E", +"y. c #FFFF6C", +"z. c #FFFF66", +"A. c #FFFF5F", +"B. c #FFFF5C", +"C. c #FFFF57", +"D. c #FFFF53", +"E. c #FFFF4D", +"F. c #FFFF4A", +"G. c #FFF111", +"H. c #D55800", +"I. c #F9DD01", +"J. c #FFFC43", +"K. c #FFFF58", +"L. c #FFFF5B", +"M. c #B5B54D", +"N. c #46463C", +"O. c #373737", +"P. c #9A9A44", +"Q. c #FFFF50", +"R. c #FFFF4C", +"S. c #FFFF49", +"T. c #FFFF45", +"U. c #FFFF40", +"V. c #FFFF3D", +"W. c #FFFF3A", +"X. c #FFFB29", +"Y. c #F9DD00", +"Z. c #E9A100", +"`. c #FFF819", +" + c #FFFF41", +".+ c #FFFF43", +"++ c #FFFF48", +"@+ c #FFFF47", +"#+ c #FFFF46", +"$+ c #3C3C31", +"%+ c #2E2E2E", +"&+ c #2C2C2C", +"*+ c #292929", +"=+ c #E4E43A", +"-+ c #FFFF39", +";+ c #FFFF37", +">+ c #FFFF33", +",+ c #FFFF30", +"'+ c #FFFF2C", +")+ c #FFFF29", +"!+ c #FFFF27", +"~+ c #FFF70D", +"{+ c #D86300", +"]+ c #FCED05", +"^+ c #FFFE25", +"/+ c #FFFF2D", +"(+ c #FFFF32", +"_+ c #FFFF31", +":+ c #323225", +"<+ c #232323", +"[+ c #202020", +"}+ c #1E1E1E", +"|+ c #E3E326", +"1+ c #FFFF25", +"2+ c #FFFF22", +"3+ c #FFFF20", +"4+ c #FFFF1E", +"5+ c #FFFF1B", +"6+ c #FFFF19", +"7+ c #FFFF17", +"8+ c #FFFE13", +"9+ c #FCED02", +"0+ c #C54900", +"a+ c #F1C500", +"b+ c #FFFB0E", +"c+ c #FFFF1A", +"d+ c #FFFF1D", +"e+ c #FFFF1F", +"f+ c #A9A91C", +"g+ c #191919", +"h+ c #181818", +"i+ c #6E6E16", +"j+ c #FFFF14", +"k+ c #FFFF13", +"l+ c #FFFF11", +"m+ c #FFFF0F", +"n+ c #FFFF0D", +"o+ c #FFFF0B", +"p+ c #FFFF0A", +"q+ c #FFFF08", +"r+ c #FFFB04", +"s+ c #DD7A00", +"t+ c #FFFE00", +"u+ c #FFFE0F", +"v+ c #FFFF15", +"w+ c #FFFF16", +"x+ c #FFFF12", +"y+ c #FFFF10", +"z+ c #FFFF0E", +"A+ c #FFFF0C", +"B+ c #FFFF09", +"C+ c #FFFF07", +"D+ c #FFFF06", +"E+ c #FFFF05", +"F+ c #FFFE02", +"G+ c #E08500", +"H+ c #F4D300", +"I+ c #F5D50C", +"J+ c #F5D50D", +"K+ c #F5D50E", +"L+ c #F5D60F", +"M+ c #F5D610", +"N+ c #F4D50B", +"O+ c #F4D509", +"P+ c #F4D408", +"Q+ c #F4D407", +"R+ c #F4D406", +"S+ c #F4D405", +"T+ c #F4D404", +"U+ c #F4D403", +"V+ c #F4D302", +"W+ c #F4D301", +"X+ c #CA4B00", +"Y+ c #D35104", +"Z+ c #D35307", +"`+ c #D35408", +" @ c #D45409", +".@ c #D4550A", +"+@ c #D35206", +"@@ c #D35105", +"#@ c #D35003", +"$@ c #D24F02", +"%@ c #D24F01", +"&@ c #B14200", +" ", +" ", +" . + + @ ", +" + # # + ", +" $ % & & % @ ", +" + * = = * + ", +" $ - ; > > ; - , ", +" . + ' ) ! ! ) ' + ~ ", +" $ { ] ^ / / / ( { . ", +" . % _ : < [ [ < } _ % | ", +" + 1 2 3 [ 4 5 6 7 8 9 + ", +" $ 0 a b c [ [ [ 6 d e f 0 g ", +" + h i j k l l 6 m n o p h + ", +" + q r s t u v w x y z A B C q . ", +" . + D E F G F H I J K L M N O P + Q ", +" + R S T U T V W X X Y Z ` ...+.R + ", +" $ @.#.$. .%. . .&.*.=.-.;.>.,.'.).!.@.@ ", +" + ~.{.].,.,.^./.,.(._.:.<.[.}.|.1.2.~.+ ", +" $ 3.4.5.6.6.7.7.7.6.8.9.|.0.a.b.c.d.e.f.3., ", +" . + g.h.i.j.i.b.b.i.i.k.l.m.n.o.p.q.r.s.t.g.+ ~ ", +" $ u.v.w.q.x.x.p.x.x.y.q.w.z.s.A.B.C.D.E.F.G.u.. ", +" . H.I.J.C.K.L.B.B.L.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.H.| ", +" + Z.`. +.+T.++++++@+#+$+%+&+*+=+-+;+>+,+'+)+!+~+Z.+ ", +" $ {+]+^+/+,+(+(+(+(+(+_+:+<+[+}+|+1+2+3+4+5+6+7+8+9+{+0+ ", +" + a+b+c+5+d+e+e+3+3+e+e+f+g+h+i+7+j+k+l+m+n+o+p+q+r+a++ ", +"$ s+t+u+k+j+v+w+7+w+7+v+v+j+k+x+y+m+z+A+A+B+q+C+D+E+F+t+s+, ", +"+ G+H+I+J+K+L+L+M+M+M+L+L+K+J+J+N+O+O+P+Q+R+S+T+U+V+W+H+G++ ", +"X++ + Y+Z+`+`+ @.@.@ @ @ @`+`+Z++@@@+@@@Y+Y+#@$@%@%@%@+ + &@ ", +" ", +" ", +" ", +" "}; diff --git a/evoke/splash-themes/Jamfile b/evoke/splash-themes/Jamfile new file mode 100644 index 0000000..f69e3a2 --- /dev/null +++ b/evoke/splash-themes/Jamfile @@ -0,0 +1,14 @@ +# +# $Id$ +# +# Part of Equinox Desktop Environment (EDE). +# Copyright (c) 2009 EDE Authors. +# +# This program is licensed under terms of the +# GNU General Public License version 2 or newer. +# See COPYING for details. + +SubDir TOP evoke splash-themes ; + +SubInclude TOP evoke splash-themes scape ; +SubInclude TOP evoke splash-themes standard ; diff --git a/evoke/splash-themes/scape/Jamfile b/evoke/splash-themes/scape/Jamfile new file mode 100644 index 0000000..4278e08 --- /dev/null +++ b/evoke/splash-themes/scape/Jamfile @@ -0,0 +1,14 @@ +# +# $Id$ +# +# Part of Equinox Desktop Environment (EDE). +# Copyright (c) 2009 EDE Authors. +# +# This program is licensed under terms of the +# GNU General Public License version 2 or newer. +# See COPYING for details. + +SubDir TOP evoke splash-themes scape ; + +DATA = background.png background.xcf edewm.png eiconman.png eworkpanel.png xscreensaver.png ; +InstallData $(datarootdir)/splash-themes/scape : $(DATA) ; diff --git a/evoke/splash-themes/scape/background.png b/evoke/splash-themes/scape/background.png new file mode 100644 index 0000000..22badbf Binary files /dev/null and b/evoke/splash-themes/scape/background.png differ diff --git a/evoke/splash-themes/scape/background.xcf b/evoke/splash-themes/scape/background.xcf new file mode 100644 index 0000000..e0ad51e Binary files /dev/null and b/evoke/splash-themes/scape/background.xcf differ diff --git a/evoke/splash-themes/scape/edewm.png b/evoke/splash-themes/scape/edewm.png new file mode 100644 index 0000000..2ee792d Binary files /dev/null and b/evoke/splash-themes/scape/edewm.png differ diff --git a/evoke/splash-themes/scape/eiconman.png b/evoke/splash-themes/scape/eiconman.png new file mode 100644 index 0000000..056bde3 Binary files /dev/null and b/evoke/splash-themes/scape/eiconman.png differ diff --git a/evoke/splash-themes/scape/eworkpanel.png b/evoke/splash-themes/scape/eworkpanel.png new file mode 100644 index 0000000..781209a Binary files /dev/null and b/evoke/splash-themes/scape/eworkpanel.png differ diff --git a/evoke/splash-themes/scape/xscreensaver.png b/evoke/splash-themes/scape/xscreensaver.png new file mode 100644 index 0000000..511d0c4 Binary files /dev/null and b/evoke/splash-themes/scape/xscreensaver.png differ diff --git a/evoke/splash-themes/standard/Jamfile b/evoke/splash-themes/standard/Jamfile new file mode 100644 index 0000000..e3d6459 --- /dev/null +++ b/evoke/splash-themes/standard/Jamfile @@ -0,0 +1,14 @@ +# +# $Id$ +# +# Part of Equinox Desktop Environment (EDE). +# Copyright (c) 2009 EDE Authors. +# +# This program is licensed under terms of the +# GNU General Public License version 2 or newer. +# See COPYING for details. + +SubDir TOP evoke splash-themes standard ; + +DATA = background.png edewm.png eiconman.png eworkpanel.png xscreensaver.png ; +InstallData $(datarootdir)/splash-themes/standard : $(DATA) ; diff --git a/evoke/splash-themes/standard/background.png b/evoke/splash-themes/standard/background.png new file mode 100644 index 0000000..94b277b Binary files /dev/null and b/evoke/splash-themes/standard/background.png differ diff --git a/evoke/splash-themes/standard/edewm.png b/evoke/splash-themes/standard/edewm.png new file mode 100644 index 0000000..2ee792d Binary files /dev/null and b/evoke/splash-themes/standard/edewm.png differ diff --git a/evoke/splash-themes/standard/eiconman.png b/evoke/splash-themes/standard/eiconman.png new file mode 100644 index 0000000..056bde3 Binary files /dev/null and b/evoke/splash-themes/standard/eiconman.png differ diff --git a/evoke/splash-themes/standard/eworkpanel.png b/evoke/splash-themes/standard/eworkpanel.png new file mode 100644 index 0000000..781209a Binary files /dev/null and b/evoke/splash-themes/standard/eworkpanel.png differ diff --git a/evoke/splash-themes/standard/xscreensaver.png b/evoke/splash-themes/standard/xscreensaver.png new file mode 100644 index 0000000..511d0c4 Binary files /dev/null and b/evoke/splash-themes/standard/xscreensaver.png differ diff --git a/evoke/test/evoke_test.cpp b/evoke/test/evoke_test.cpp new file mode 100644 index 0000000..a365064 --- /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 + } + } +}