From 15421f6f9ace53d51af412dfe214e12a22f998a9 Mon Sep 17 00:00:00 2001 From: Sanel Zukan Date: Mon, 12 Jan 2009 13:49:54 +0000 Subject: [PATCH] Adding a bunch of stuff, including reading ~/.xscreensaver file, previewing hacks, sniffing hack/config dirs if ~/.xscreensaver was not found etc. Changed options will be tried to be saved in user .xscreensaver file including merge of previous options and this is not working as expected. --- ede-screensaver-conf/Jamfile | 7 +- ede-screensaver-conf/XScreenSaver.cpp | 278 +++++++++++++++++- ede-screensaver-conf/XScreenSaver.h | 21 +- ede-screensaver-conf/ede-screensaver-conf.cpp | 161 ++++++++-- ede-screensaver-conf/ede-screensaver-conf.fl | 9 +- 5 files changed, 422 insertions(+), 54 deletions(-) diff --git a/ede-screensaver-conf/Jamfile b/ede-screensaver-conf/Jamfile index 5630a71..fc22431 100644 --- a/ede-screensaver-conf/Jamfile +++ b/ede-screensaver-conf/Jamfile @@ -2,7 +2,7 @@ # $Id$ # # Part of Equinox Desktop Environment (EDE). -# Copyright (c) 2000-2007 EDE Authors. +# Copyright (c) 2009 EDE Authors. # # This program is licenced under terms of the # GNU General Public Licence version 2 or newer. @@ -10,7 +10,4 @@ SubDir TOP ede-screensaver-conf ; -SOURCE = escreensaver.cpp escrsaverconf.cpp ; - -EfltkProgram ede-screensaver-conf : $(SOURCE) ; -TranslationStrings locale : $(SOURCE) ; +EdeProgram ede-screensaver-conf : ede-screensaver-conf.cpp XScreenSaver.cpp ; diff --git a/ede-screensaver-conf/XScreenSaver.cpp b/ede-screensaver-conf/XScreenSaver.cpp index a2f3bfc..d483b64 100644 --- a/ede-screensaver-conf/XScreenSaver.cpp +++ b/ede-screensaver-conf/XScreenSaver.cpp @@ -2,7 +2,11 @@ #include #include #include -#include // toupper +#include +#include +#include +#include +#include #include #include @@ -11,13 +15,17 @@ #include #include #include +#include #include "XScreenSaver.h" EDELIB_NS_USING(String) EDELIB_NS_USING(file_path) +EDELIB_NS_USING(file_remove) EDELIB_NS_USING(run_program) EDELIB_NS_USING(dir_home) +EDELIB_NS_USING(dir_exists) +EDELIB_NS_USING(dir_empty) static Atom XA_SCREENSAVER; static Atom XA_SCREENSAVER_VERSION; @@ -28,6 +36,31 @@ static XErrorHandler old_handler = 0; static Bool got_bad_window = False; static int atoms_loaded = 0; +static pid_t xscr_preview_pid = 0; +static const char* xscr_folder_found = 0; + +/* TODO: add PREFIX */ +static const char* xscr_hacks_dirs[] = { + "/usr/libexec/xscreensaver/", + "/usr/lib/xscreensaver/", + "/usr/X11R6/lib/xscreensaver/", + "/usr/local/lib/xscreensaver/", + "/lib/xscreensaver/", + 0 +}; + +/* TODO: add PREFIX */ +static const char* xscr_hacks_config_dirs[] = { + "/usr/share/xscreensaver/config/", + "/usr/local/share/xscreensaver/config/", + 0 +}; + +#define EAT_SPACES(c) \ +do { \ + while(*c && (*c == ' ' || *c == '\t')) c++; \ +} while(0) + static int bad_window_handler(Display *dpy, XErrorEvent *xevent) { if(xevent->error_code == BadWindow) { got_bad_window = True; @@ -129,6 +162,7 @@ static Window xscreensaver_find_own_window(Display *dpy) { return 0; } +#if 0 static void xscreensaver_run_hack(Display *dpy, Window id, long hack) { XEvent ev; @@ -152,21 +186,128 @@ static void xscreensaver_run_hack(Display *dpy, Window id, long hack) { else XSync(dpy, 0); } +#endif -bool xscreensaver_run(Display *dpy) { +static const char *find_hacks_dir_once(void) { + if(xscr_folder_found) + return xscr_folder_found; + + for(int i = 0; xscr_hacks_dirs[i]; i++) { + if(dir_exists(xscr_hacks_dirs[i])) { + xscr_folder_found = xscr_hacks_dirs[i]; + break; + } + } + + return xscr_folder_found; +} + +static SaverHack *hack_from_config(const char* path) { + TiXmlDocument doc(path); + + if(!doc.LoadFile()) + return NULL; + + TiXmlNode* el = doc.FirstChild("screensaver"); + if(!el) + return NULL; + + SaverHack* h = new SaverHack; + h->exec = el->ToElement()->Attribute("name"); + h->name = el->ToElement()->Attribute("_label"); + + /* + * now try to find all '' tags and merge values + * to form full command line + */ + for(el = el->FirstChildElement(); el; el = el->NextSibling()) { + if(strcmp(el->Value(), "command") == 0) { + h->exec += " "; + h->exec += el->ToElement()->Attribute("arg"); + } + } + + return h; +} + +static SaverPrefs *guess_config(void) { + const char* config_dir = NULL, *hacks_dir; + + for(int i = 0; xscr_hacks_config_dirs[i]; i++) { + if(dir_exists(xscr_hacks_config_dirs[i]) && !dir_empty(xscr_hacks_config_dirs[i])) { + config_dir = xscr_hacks_config_dirs[i]; + break; + } + } + + E_RETURN_VAL_IF_FAIL(config_dir, NULL); + + hacks_dir = find_hacks_dir_once(); + E_RETURN_VAL_IF_FAIL(hacks_dir, NULL); + + /* + * now do xscreensaver way: try to find xscreensaver hack programs and their matching + * .xml files for name and command args + */ + DIR* dfd = opendir(hacks_dir); + if(!dfd) + return NULL; + + char config_path[256]; + struct dirent* entry; + + SaverPrefs* sp = new SaverPrefs; + /* some default values */ + sp->curr_hack = 0; + sp->timeout = 2; + sp->dpms_enabled = false; + sp->dpms_standby = 20; + sp->dpms_suspend = 40; + sp->dpms_off = 60; + + unsigned int ind = 0; + + while(1) { + entry = readdir(dfd); + if(!entry) + break; + + /* skip '.' and '..' */ + if(entry->d_name[0] == '.' && + (entry->d_name[1] == '\0' || entry->d_name[1] == '.' && entry->d_name[2] == '\0')) + { + continue; + } + + snprintf(config_path, sizeof(config_path), "%s%s.xml", config_dir, entry->d_name); + SaverHack* h = hack_from_config(config_path); + if(h) { + h->sindex = ind++; + sp->hacks.push_back(h); + } + } + + return sp; +} + +bool xscreensaver_run_daemon(Display *dpy) { xscreensaver_init_atoms_once(dpy); Window id = xscreensaver_find_own_window(dpy); /* if not running, try to manualy start it */ if(id == 0) { + E_DEBUG(E_STRLOC ": xscreensaver daemon not running, starting it...\n"); + String p = file_path("xscreensaver"); if(p.empty()) return false; /* run 'xscreensaver -nosplash' */ p += " -nosplash"; - run_program(p.c_str()); + run_program(p.c_str(), false); + + usleep(250000); /* check again */ id = xscreensaver_find_own_window(dpy); @@ -193,8 +334,8 @@ SaverPrefs *xscreensaver_read_config(void) { db = XrmGetFileDatabase(path.c_str()); if(!db) { - puts("Unable to open xscreensaver config file"); - return NULL; + E_WARNING(E_STRLOC ": Unable to open xscreensaver config file, trying to guess...\n"); + return guess_config(); } SaverPrefs *ret = new SaverPrefs; @@ -248,6 +389,7 @@ SaverPrefs *xscreensaver_read_config(void) { unsigned int i; for(c = strtok(programs, "\n"); c; c = strtok(NULL, "\n"), nhacks++) { + /* skip those marked for skipping */ if(c[0] == '-') continue; @@ -256,9 +398,7 @@ SaverPrefs *xscreensaver_read_config(void) { c = p; } - /* eat spaces */ - while(*c && (*c == ' ' || *c == '\t')) - c++; + EAT_SPACES(c); if(*c == '"') { /* extract name from '"' */ @@ -268,10 +408,16 @@ SaverPrefs *xscreensaver_read_config(void) { buf[i] = *c; buf[i] = '\0'; + + /* skip ending '"' */ + c++; } else { + /* store it so we could back for command line */ + char *tc = c; + /* or read command and capitalize it */ - for(i = 0; i < sizeof(buf) && *c && *c != ' '; c++, i++) - buf[i] = *c; + for(i = 0; i < sizeof(buf) && *tc && *tc != ' '; tc++, i++) + buf[i] = *tc; buf[i] = '\0'; buf[0] = toupper(buf[0]); @@ -281,6 +427,22 @@ SaverPrefs *xscreensaver_read_config(void) { h->name = buf; h->sindex = nhacks; + /* remove remainig gap */ + EAT_SPACES(c); + + /* + * now go for exec command + * + * FIXME: it will miss options spread in two or more lines (in Xresource sense) + * so when read via XrmGetResource(), it will be merged in a long line with a bunch + * of spaces and tabs. See 'crackberg' command + */ + for(i = 0; i < sizeof(buf) && *c && *c != '\t'; c++, i++) + buf[i] = *c; + + buf[i] = '\0'; + h->exec = buf; + ret->hacks.push_back(h); } @@ -290,3 +452,99 @@ SaverPrefs *xscreensaver_read_config(void) { XrmDestroyDatabase(db); return ret; } + +void xscreensaver_save_config(SaverPrefs *sp) { + String tmp_path = dir_home(); + String path = tmp_path; + + tmp_path += "/.xscreensaver.tmp"; + path +="/.xscreensaver"; + + FILE *fd = fopen(tmp_path.c_str(), "w"); + if(!fd) { + E_WARNING(E_STRLOC ": Unable to write temporary in %s! Nothing will be saved\n", tmp_path.c_str()); + return; + } + + fprintf(fd, "# XScreenSaver Preferences File\n"); + fprintf(fd, "# Written by ede-screensaver-conf\n\n"); + + fprintf(fd, "selected: %i\n", sp->curr_hack); + fprintf(fd, "timeout: 00:%i:00\n", sp->timeout); + + const char* val; + + if(sp->dpms_enabled) + val = "yes"; + else + val = "no"; + + fprintf(fd, "dpmsEnabled: %s\n", val); + fprintf(fd, "dpmsStandby: 00:%i:00\n", sp->dpms_standby); + fprintf(fd, "dpmsSuspend: 00:%i:00\n", sp->dpms_suspend); + fprintf(fd, "dpmsOff: 00:%i:00\n", sp->dpms_off); + fprintf(fd, "programs:\t\t\\\n"); + + HackListIter it = sp->hacks.begin(), it_end = sp->hacks.end(); + for(; it != it_end; ++it) + fprintf(fd, "\t\t \"%s\" %s\t\t \\n\\\n", (*it)->name.c_str(), (*it)->exec.c_str()); + + fprintf(fd, "\n\n"); + fclose(fd); + + /* + * now open it as Xresource database and merge with the real ~/.xscreensaver + * file, so other values we didn't wrote/used are preserved + */ + XrmInitialize(); + XrmDatabase db = XrmGetFileDatabase(tmp_path.c_str()); + if(db) { + XrmCombineFileDatabase(path.c_str(), &db, 1); + /* and store it as ~/.xscreensaver */ + XrmPutFileDatabase(db, path.c_str()); + XrmDestroyDatabase(db); + } + + //file_remove(tmp_path.c_str()); +} + +/* run screensaver in in FLTK window */ +void xscreensaver_preview(int id, const char* name) { + const char* hacks_folder = find_hacks_dir_once(); + E_RETURN_IF_FAIL(hacks_folder); + + if(xscr_preview_pid) + xscreensaver_kill_preview(); + + String cmd; + cmd.printf("%s%s -window-id 0x%X", hacks_folder, name, id); + + pid_t f = fork(); + switch((int)f) { + case -1: + E_WARNING(E_STRLOC ": Unable to fork screensaver process\n"); + break; + case 0: { + usleep(250000); + char* argv[4]; + argv[0] = "sh"; + argv[1] = "-c"; + argv[2] = (char*)cmd.c_str(); + argv[3] = NULL; + execve("/bin/sh", argv, environ); + /* never reached */ + exit(1); + break; + } + default: + xscr_preview_pid = f; + break; + } +} + +void xscreensaver_kill_preview(void) { + if(xscr_preview_pid) { + kill(xscr_preview_pid, SIGTERM); + xscr_preview_pid = 0; + } +} diff --git a/ede-screensaver-conf/XScreenSaver.h b/ede-screensaver-conf/XScreenSaver.h index a23ff56..f23bdde 100644 --- a/ede-screensaver-conf/XScreenSaver.h +++ b/ede-screensaver-conf/XScreenSaver.h @@ -1,21 +1,27 @@ #ifndef __XSCREENSAVER_H__ #define __XSCREENSAVER_H__ +#include #include #include struct SaverHack { edelib::String name; - int sindex; + edelib::String exec; + unsigned int sindex; }; +/* TODO: edelib list::sort() bug */ +inline bool saver_hack_cmp(SaverHack* const& s1, SaverHack* const& s2) +{ return s1->name < s2->name; } + typedef edelib::list HackList; typedef edelib::list::iterator HackListIter; struct SaverPrefs { - HackList hacks; - int curr_hack; - int timeout; + HackList hacks; + unsigned int curr_hack; + int timeout; bool dpms_enabled; int dpms_standby; @@ -23,7 +29,12 @@ struct SaverPrefs { int dpms_off; }; -bool xscreensaver_run(void); +bool xscreensaver_run_daemon(Display* py); + SaverPrefs *xscreensaver_read_config(void); +void xscreensaver_save_config(SaverPrefs *sp); + +void xscreensaver_preview(int id, const char* name); +void xscreensaver_kill_preview(void); #endif diff --git a/ede-screensaver-conf/ede-screensaver-conf.cpp b/ede-screensaver-conf/ede-screensaver-conf.cpp index 381422f..fc5e386 100644 --- a/ede-screensaver-conf/ede-screensaver-conf.cpp +++ b/ede-screensaver-conf/ede-screensaver-conf.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -16,12 +17,16 @@ static Fl_Pixmap image_energy(energy_star_xpm); -static Fl_Spinner* standby_val; -static Fl_Spinner* suspend_val; -static Fl_Spinner* off_val; +static Fl_Spinner* standby_val; +static Fl_Spinner* suspend_val; +static Fl_Spinner* off_val; +static Fl_Double_Window* main_win; +static Fl_Double_Window* preview_win; -static void dpms_enable_cb(Fl_Widget* w, void*) { +static void dpms_enable_cb(Fl_Widget* w, void* s) { Fl_Check_Button* o = (Fl_Check_Button*)w; + SaverPrefs* sp = (SaverPrefs*)s; + if(o->value()) { standby_val->activate(); suspend_val->activate(); @@ -31,28 +36,78 @@ static void dpms_enable_cb(Fl_Widget* w, void*) { suspend_val->deactivate(); off_val->deactivate(); } + + sp->dpms_enabled = o->value(); } -static void close_cb(Fl_Widget*, void* w) { - Fl_Double_Window* win = (Fl_Double_Window*)w; - win->hide(); +static void choice_cb(Fl_Widget* w, void* s) { + Fl_Choice* c = (Fl_Choice*)w; + SaverPrefs* sp = (SaverPrefs*)s; + + const char* saver_name = c->text(); + if(!saver_name) + return; + + /* find the name matches in our list and run it's command */ + HackListIter it = sp->hacks.begin(), it_end = sp->hacks.end(); + for(; it != it_end; ++it) { + if((*it)->name == saver_name) { + xscreensaver_preview(fl_xid(preview_win), (*it)->exec.c_str()); + sp->curr_hack = (*it)->sindex; + break; + } + } +} + +static void close_cb(Fl_Widget*, void*) { + xscreensaver_kill_preview(); + preview_win->hide(); + main_win->hide(); +} + +static void ok_cb(Fl_Widget*, void* s) { + SaverPrefs* sp = (SaverPrefs*)s; + if(sp) + xscreensaver_save_config(sp); + close_cb(0, 0); } int main(int argc, char **argv) { - SaverPrefs* sp = xscreensaver_read_config(); - E_ASSERT(sp != NULL); + fl_open_display(); + /* start daemon if not started */ + xscreensaver_run_daemon(fl_display); - Fl_Double_Window* win = new Fl_Double_Window(340, 445, _("Screensaver options")); - win->begin(); + SaverPrefs* sp = xscreensaver_read_config(); + + main_win = new Fl_Double_Window(340, 445, _("Screensaver options")); + /* so ESC does not interrupt running process */ + main_win->callback(close_cb); + + main_win->begin(); /* monitor */ Fl_Box* b1 = new Fl_Box(120, 163, 100, 15); b1->box(FL_BORDER_BOX); Fl_Box* b2 = new Fl_Box(65, 10, 210, 158); b2->box(FL_THIN_UP_BOX); - /* box size is intentionaly odd so preserve aspect ratio */ + Fl_Box* b3 = new Fl_Box(78, 20, 184, 138); - b3->box(FL_DOWN_BOX); b3->color(FL_BLACK); + b3->box(FL_DOWN_BOX); + + /* preview window in 'b3' box */ + preview_win = new Fl_Double_Window(80, 22, 180, 134); + preview_win->color(FL_BLACK); + preview_win->begin(); + + /* if failed to load any screensaver */ + if(!sp) { + Fl_Box* b4 = new Fl_Box(0, 0, 180, 134, _("No screensavers found")); + b4->labelcolor(FL_GRAY); + b4->align(FL_ALIGN_INSIDE | FL_ALIGN_WRAP); + } + + preview_win->end(); + Fl_Box* b4 = new Fl_Box(95, 173, 146, 14); b4->box(FL_THIN_UP_BOX); @@ -63,53 +118,99 @@ int main(int argc, char **argv) { Fl_Choice* saver_list = new Fl_Choice(19, 225, 180, 25); saver_list->down_box(FL_BORDER_BOX); - HackListIter it = sp->hacks.begin(), it_end = sp->hacks.end(); - for(; it != it_end; ++it) { - saver_list->add((*it)->name.c_str(), 0, 0); - delete *it; + if(sp) { + saver_list->callback((Fl_Callback*)choice_cb, sp); + + /* 0 is first item */ + int sel = -1; + + /* fix possible error */ + if(sp->curr_hack >= sp->hacks.size()) + sp->curr_hack = 0; + + HackListIter it = sp->hacks.begin(), it_end = sp->hacks.end(); + for(int i = 0; it != it_end; ++it, i++) { + saver_list->add((*it)->name.c_str(), 0, 0); + + /* + * check real hack index number against current one + * and let it match position in our Fl_Choice list + */ + if(sel == -1 && (*it)->sindex == sp->curr_hack) + sel = i; + } + + saver_list->value(sel); } Fl_Spinner* timeout = new Fl_Spinner(275, 226, 45, 25, _("Timeout:")); timeout->tooltip(_("Idle time in minutes after screensaver is started")); - timeout->value(sp->timeout); + if(sp) + timeout->value(sp->timeout); + else + timeout->value(0); + g1->end(); - Fl_Group* g2 = new Fl_Group(10, 290, 320, 110, _("DPMS")); + Fl_Group* g2 = new Fl_Group(10, 290, 320, 110, _("Power management")); g2->box(FL_ENGRAVED_BOX); g2->align(FL_ALIGN_TOP_LEFT); g2->begin(); Fl_Check_Button* denabled = new Fl_Check_Button(20, 299, 180, 26, _("Enabled")); denabled->down_box(FL_DOWN_BOX); denabled->tooltip(_("Enable or disable Display Power Management Signaling support")); - denabled->callback((Fl_Callback*)dpms_enable_cb); - denabled->value(sp->dpms_enabled); + denabled->callback((Fl_Callback*)dpms_enable_cb, sp); + if(sp) + denabled->value(sp->dpms_enabled); + else + denabled->value(0); Fl_Box* energy_image = new Fl_Box(20, 341, 75, 49); energy_image->image(image_energy); standby_val = new Fl_Spinner(275, 301, 45, 24, _("Standby:")); standby_val->tooltip(_("Minutes for standby")); - standby_val->value(sp->dpms_standby); + if(sp) + standby_val->value(sp->dpms_standby); + else + standby_val->value(0); suspend_val = new Fl_Spinner(275, 331, 45, 24, _("Suspend:")); suspend_val->tooltip(_("Minutes for suspend")); - suspend_val->value(sp->dpms_suspend); + if(sp) + suspend_val->value(sp->dpms_suspend); + else + suspend_val->value(0); off_val = new Fl_Spinner(275, 360, 45, 24, _("Off:")); off_val->tooltip(_("Minutes to turn off the screen")); - off_val->value(sp->dpms_off); + if(sp) + off_val->value(sp->dpms_off); + else + off_val->value(0); - /* execute callback to apply changes before window is shown */ + /* execute callback to apply changes before main_window is shown */ denabled->do_callback(); g2->end(); Fl_Button* ok_button = new Fl_Button(145, 410, 90, 25, _("&OK")); + ok_button->callback(ok_cb, sp); Fl_Button* close_button = new Fl_Button(240, 410, 90, 25, _("&Cancel")); - close_button->callback(close_cb, win); - win->end(); - delete sp; + close_button->callback(close_cb); + main_win->end(); - win->show(argc, argv); - return Fl::run(); + main_win->show(argc, argv); + /* run preview immediately */ + saver_list->do_callback(); + int ret = Fl::run(); + + if(sp) { + HackListIter it = sp->hacks.begin(), it_end = sp->hacks.end(); + for(; it != it_end; ++it) + delete *it; + delete sp; + } + + return ret; } diff --git a/ede-screensaver-conf/ede-screensaver-conf.fl b/ede-screensaver-conf/ede-screensaver-conf.fl index 8c93da6..2cbed6c 100644 --- a/ede-screensaver-conf/ede-screensaver-conf.fl +++ b/ede-screensaver-conf/ede-screensaver-conf.fl @@ -2,10 +2,11 @@ version 1.0108 header_name {.h} code_name {.cxx} -Function {} {} { +Function {} {open +} { Fl_Window {} { label {Screensaver options} open - xywh {266 111 340 445} type Double visible + xywh {337 112 340 445} type Double visible } { Fl_Box {} { xywh {120 163 100 15} box BORDER_BOX @@ -33,14 +34,14 @@ Function {} {} { } } Fl_Group {} { - label DPMS open + label {Power management} open selected xywh {10 290 320 110} box ENGRAVED_BOX align 5 } { Fl_Check_Button {} { label Enabled xywh {20 299 180 26} down_box DOWN_BOX } - Fl_Box {} {selected + Fl_Box {} { image {icons/energy.xpm} xywh {20 341 75 49} labelsize 14 } Fl_Spinner {} {