ede/evoke/Splash.cpp
Sanel Zukan 29475da925 Allow setting splash theme to 'none'.
If 'none' was set, no splash theme will be used. This is another way
splash screen can be disabled without adding '-n' option to evoke.
2014-12-08 23:00:55 +00:00

286 lines
7.0 KiB
C++

/*
* $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 <FL/Fl.H>
#include <FL/Fl_Shared_Image.H>
#include <edelib/Debug.h>
#include <edelib/Nls.h>
#include <edelib/Util.h>
#include <edelib/Directory.h>
#include <edelib/FileTest.h>
#include <edelib/Resource.h>
#include <edelib/Run.h>
#include <edelib/Netwm.h>
#include "Splash.h"
#define TIMEOUT_START 0.5 /* timeout when splash is first time shown (also for first client) */
#define TIMEOUT_CONTINUE 0.4 /* timeout between starting rest of the cliens */
EDELIB_NS_USING(String)
EDELIB_NS_USING(Resource)
EDELIB_NS_USING(build_filename)
EDELIB_NS_USING(file_test)
EDELIB_NS_USING(run_async)
EDELIB_NS_USING(netwm_window_set_type)
EDELIB_NS_USING(RES_SYS_ONLY)
EDELIB_NS_USING(FILE_TEST_IS_DIR)
EDELIB_NS_USING(NETWM_WINDOW_TYPE_SPLASH)
#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;
sp->next_client() ? Fl::repeat_timeout(TIMEOUT_CONTINUE, runner_cb, sp) : 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);
}
/* for wm's with _NET_WM_WINDOW_TYPE_SPLASH support */
#if EDEWM_HAVE_NET_SPLASH
void Splash::show(void) {
if(shown()) return;
Fl_X::make_xid(this);
netwm_window_set_type(fl_xid(this), NETWM_WINDOW_TYPE_SPLASH);
}
#endif
void Splash::run(void) {
E_ASSERT(slist != NULL);
if(!show_splash || (splash_theme && (strcasecmp(splash_theme->c_str(), "none") == 0))) {
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("themes/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(!file_test(splash_theme_path.c_str(), FILE_TEST_IS_DIR)) {
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) {
E_RETURN_VAL_IF_FAIL(slist->size() > 0, 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");
const char *msg, *cmd;
msg = (*slist_it)->description.c_str();
cmd = (*slist_it)->exec.c_str();
icons[counter]->show();
msgbox->label(msg);
redraw();
/* run command */
if(!dryrun)
run_async(cmd);
++slist_it;
++counter;
return true;
}
/* called when splash option is off */
bool Splash::next_client_nosplash(void) {
E_RETURN_VAL_IF_FAIL(slist->size() > 0, 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");
const char* msg = (*slist_it)->description.c_str();
const char* cmd = (*slist_it)->exec.c_str();
E_DEBUG(E_STRLOC ": Starting '%s'...\n", msg);
/* run command */
if(!dryrun)
run_async(cmd);
++slist_it;
++counter;
return true;
}