/* * $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); ++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); 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); ++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); counter++; #endif }