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.
This commit is contained in:
Sanel Zukan 2009-01-12 13:49:54 +00:00
parent fea5d1bc19
commit 15421f6f9a
5 changed files with 422 additions and 54 deletions

View File

@ -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 ;

View File

@ -2,7 +2,11 @@
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <ctype.h> // toupper
#include <ctype.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <dirent.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
@ -11,13 +15,17 @@
#include <edelib/File.h>
#include <edelib/Run.h>
#include <edelib/Directory.h>
#include <edelib/TiXml.h>
#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 '<command>' 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;
}
}

View File

@ -1,21 +1,27 @@
#ifndef __XSCREENSAVER_H__
#define __XSCREENSAVER_H__
#include <X11/Xproto.h>
#include <edelib/List.h>
#include <edelib/String.h>
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<SaverHack*> HackList;
typedef edelib::list<SaverHack*>::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

View File

@ -7,6 +7,7 @@
#include <FL/Fl_Check_Button.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Pixmap.H>
#include <FL/x.H>
#include <edelib/Nls.h>
#include <edelib/Debug.h>
@ -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;
}

View File

@ -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 {} {