dockapps/wmthemech/src/wmThemeCh.c
2017-02-21 11:15:02 +00:00

823 lines
17 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <X11/X.h>
#include <X11/xpm.h>
#include <time.h>
#include "wmgeneral.h"
#include "stringlist.h"
#include "xpm/back.xpm"
#include "xpm/star.xpm"
#include "xpm/no_icon.xpm"
/*
* Delay between refreshes (in microseconds)
*/
#define DELAY 10000L
#define ICONSIZE 64
/* Database structures */
enum th_type {THEME, WALL};
struct theme {
enum th_type type;
char * path;
/* int theme;
char * options;*/
};
struct category {
char * name;
LIST * themes;
};
LIST * thm_db;
LIST * cat_db;
/* Themes directories, last one will be replaced */
char * themes_dir[] = {
"/usr/share/WindowMaker/Themes",
"/usr/X11R6/share/WindowMaker/Themes",
"/usr/local/share/WindowMaker/Themes",
"$GNUSTEP_USER_ROOT/Library/WindowMaker/Themes"
};
/* Wallpapers directories, last one will be replaced */
char * wallpapers_dir[] = {
"/usr/share/WindowMaker/Backgrounds",
"/usr/X11R6/share/WindowMaker/Backgrounds",
"/usr/local/share/WindowMaker/Backgrounds",
"$GNUSTEP_USER_ROOT/Library/WindowMaker/Backgrounds"
};
char hid_cat_db[] = ".wmThemeCh.cat.db";
char cat_db_file[] = "wmThemeCh.cat.db";
char reg_cat [] = "categories";
int current_category = 0;
unsigned long int auto_switch_delay = 0;
unsigned long int auto_switch_next = 0;
char * mask_bits;
XpmAttributes Attributes;
Colormap cmap;
int
BuildBitmask ()
{
int i, j, off = 0;
int size = (ICONSIZE * ICONSIZE - 1) / 8 + 1;
mask_bits = malloc (size);
/* must have iconsize % 8 = 0 */
for (i = 0; i < 4 * ICONSIZE / 8; i++) {
mask_bits[off++] = 0;
}
for (i = 0; i < ICONSIZE - 8; i++) {
mask_bits[off++] = 0xf0;
for (j = 0; j < (ICONSIZE / 8 - 2); j++) {
mask_bits[off++] = 0xff;
}
mask_bits[off++] = 0x0f;
}
for (i = 0; i < 4 * ICONSIZE / 8; i++) {
mask_bits[off++] = 0;
}
return 0;
}
#define ADD_CATEGORY(string) \
do { \
struct category * newcat; \
\
newcat = malloc (sizeof (struct category) ); \
newcat->name = strdup (string); \
newcat->themes = create_list (); \
add_item (cat_db, (unsigned int) newcat); \
} while (0);
#define PROCESS_CATEGORY_FILE \
if ( (catfile = fopen (catname,"r") ) ) \
{ \
while (fscanf (catfile, "%255s", ImageName) == 1) { \
ADD_CATEGORY(ImageName); \
} \
fclose (catfile); \
} \
free (catname);
/* Register categories recognized by system + user */
int
get_categories ()
{
char * catname;
char * home = getenv ("HOME");
char ImageName[256];
FILE * catfile;
ADD_CATEGORY("*");
catname = malloc (strlen (home) + strlen (PACKAGE_NAME)
+ strlen (reg_cat) + 4);
sprintf (catname, "%s/.%s/%s", home, PACKAGE_NAME, reg_cat);
PROCESS_CATEGORY_FILE
catname = malloc (strlen (PACKAGE) + strlen (reg_cat) + 13);
sprintf (catname, "/usr/share/%s/%s", PACKAGE, reg_cat);
PROCESS_CATEGORY_FILE
catname = malloc (strlen (PACKAGE) + strlen (reg_cat) + 19);
sprintf (catname, "/usr/local/share/%s/%s", PACKAGE, reg_cat);
PROCESS_CATEGORY_FILE
return 0;
}
enum de_type { ERROR = -1, IGNORED, STYLE, DE_THEME, SUBDIR, IMAGE };
/* Return values :
* -1 : stat error
* 0 : Ignored entries : ., .., category
* 1 : regular file (.style)
* 2 : theme directory (.themed)
* 3 : sub-directory
* 4 : image
*/
enum de_type
entry_type (char * dir, char * entry)
{
char * name = malloc (strlen (dir) + strlen (entry) + 2);
struct stat buf;
sprintf (name, "%s/%s", dir, entry);
if (stat (name, &buf) ) {
perror ("stat ");
free (name);
return ERROR;
}
free (name);
if (! S_ISDIR (buf.st_mode) ) {
if (! strcmp (entry,cat_db_file ) ) return IGNORED;
if (! strcmp (entry,hid_cat_db) ) return IGNORED;
/* TODO : Use file (1) */
if (strstr (entry, ".xpm" ) ) return IMAGE;
if (strstr (entry, ".png" ) ) return IMAGE;
if (strstr (entry, ".jpg" ) ) return IMAGE;
if (strstr (entry, ".jpeg") ) return IMAGE;
if (strstr (entry, ".tif" ) ) return IMAGE;
if (strstr (entry, ".ppm" ) ) return IMAGE;
return STYLE;
}
if (strstr (entry, ".themed") ) return DE_THEME;
if (strcmp (entry,"." ) && strcmp (entry,"..") ) return SUBDIR;
return IGNORED;
}
struct theme *
insert_into_thm (char * path, enum th_type type)
{
struct theme * new_theme = malloc (sizeof (struct theme) );
new_theme->type = type;
new_theme->path = strdup (path);
add_item (thm_db, (int) new_theme);
return new_theme;
}
int
insert_into_cat (struct theme * theme, LIST * cat_list)
{
int i, j, lcat;
char * lcat_nam;
add_item ( ( (struct category *) get_item (cat_db, 0) ) -> themes,
(int) theme);
/* for each category theme is in */
for (j = 0; (lcat = get_item (cat_list, j) ) != -1; j++) {
int ucat;
lcat_nam = (char *) lcat;
/* for each user category */
for (i=1; (ucat = get_item (cat_db, i) ) != -1; i++) {
struct category * cat = (struct category *) ucat;
/* check if they match */
if (! strcmp (lcat_nam, cat->name) ) {
add_item (cat->themes, (int) theme);
break;
}
}
}
return 0;
}
LIST *
theme_specific_cat (char * path, LIST * cat_list)
{
FILE * th_cat;
char * th_cat_nam = malloc (strlen (path) + strlen (hid_cat_db) + 2);
int i, ret, version, subversion;
char cat_nam[512];
LIST * new_list;
sprintf (th_cat_nam, "%s/%s", path, cat_db_file);
th_cat = fopen (th_cat_nam, "r");
if (! th_cat) {
sprintf (th_cat_nam, "%s/%s", path, hid_cat_db);
th_cat = fopen (th_cat_nam, "r");
if (! th_cat) {
free (th_cat_nam);
return cat_list;
}
}
ret = fscanf (th_cat, "# wmThemeCh categories database file version %i.%i",
&version, &subversion);
if (ret != 2) {
printf("%s : invalid signature\n", th_cat_nam);
fclose (th_cat);
free (th_cat_nam);
return cat_list;
}
if (version > 1) {
printf("%s : incompatible version\n", th_cat_nam);
fclose (th_cat);
free (th_cat_nam);
return cat_list;
}
new_list = create_list ();
add_list (new_list, cat_list);
while ( (ret = fscanf (th_cat, "%s", cat_nam) ) == 1) {
for (i=0; get_item (cat_db,i) != -1; i++) {
struct category * cat =
(struct category *) get_item (cat_db,i);
if (! strcmp (cat_nam, cat->name) ) {
/* printf("%s : '%s'\n", th_cat_nam, cat_nam); */
add_item (new_list, (int) strdup (cat->name) );
break;
}
}
}
fclose (th_cat);
free (th_cat_nam);
return new_list;
}
/* Read "categories" file in path.
* Themes in path and its subdirs are belonging to categories listed
* in that file if these categories are defined by user profile.
* Updates cat_list accordingly.
*/
int
add_dir_cat (const char * path, LIST * cat_list)
{
FILE * th_cat;
char * th_cat_nam = malloc (strlen (path) + strlen (hid_cat_db) + 2);
int ret, version, subversion;
char cat_nam[512];
sprintf (th_cat_nam, "%s/%s", path, hid_cat_db);
th_cat = fopen (th_cat_nam, "r");
if (! th_cat) {
free (th_cat_nam);
return 0;
}
ret = fscanf (th_cat, "# wmThemeCh categories database file version %i.%i",
&version, &subversion);
if (ret != 2) {
printf("%s : invalid signature\n", th_cat_nam);
fclose (th_cat);
free (th_cat_nam);
return 0;
}
while ( (ret = fscanf (th_cat, "%s", cat_nam) ) == 1) {
int i;
for (i=0; get_item (cat_db, i) != -1; i++) {
struct category * cat =
(struct category *) get_item (cat_db, i);
if (! strcmp (cat_nam, cat->name) ) {
/* printf("%s : '%s'\n", th_cat_nam, cat_nam); */
add_item (cat_list, (int) strdup (cat->name) );
break;
}
}
}
fclose (th_cat);
free (th_cat_nam);
return 0;
}
int
get_themes (char * path, LIST * par_cat_list)
{
struct dirent ** namelist;
LIST * cat_list = create_list();
int themes_count = 0;
int count = scandir (path, &namelist, NULL, alphasort);
if (count < 0) {
free (cat_list);
return -1;
}
if (par_cat_list) add_list (cat_list, par_cat_list);
add_dir_cat (path, cat_list);
while (count--) {
char fullname[512];
struct theme * new_theme;
LIST * th_spec_cat;
sprintf (fullname, "%s/%s", path, namelist[count]->d_name);
switch (entry_type (path, namelist[count]->d_name) ) {
case ERROR :
case IGNORED : break;
case STYLE :
new_theme = insert_into_thm (fullname, THEME);
insert_into_cat (new_theme, cat_list);
break;
case DE_THEME :
new_theme = insert_into_thm (fullname, THEME);
th_spec_cat = theme_specific_cat (fullname, cat_list);
insert_into_cat (new_theme, th_spec_cat);
if (th_spec_cat != cat_list) {
delete_list (th_spec_cat);
}
break;
case SUBDIR :
get_themes (fullname, cat_list);
case IMAGE : break;
}
}
free (cat_list);
return 0;
}
int
get_wallpapers (char * path, LIST * par_cat_list)
{
struct dirent ** namelist;
LIST * cat_list = create_list();
int themes_count = 0;
int count = scandir (path, &namelist, NULL, NULL);
if (count < 0) {
free (cat_list);
return -1;
}
if (par_cat_list) add_list (cat_list, par_cat_list);
add_dir_cat (path, cat_list);
while (count--) {
char fullname[512];
struct theme * new_theme;
sprintf (fullname, "%s/%s", path, namelist[count]->d_name);
switch (entry_type (path, namelist[count]->d_name) ) {
case ERROR :
case IGNORED :
case STYLE :
case DE_THEME: break;
case SUBDIR :
get_wallpapers (fullname, cat_list);
break;
case IMAGE :
new_theme = insert_into_thm (fullname, WALL);
insert_into_cat (new_theme, cat_list);
}
}
free (cat_list);
return 0;
}
int
BuildDatabase ()
{
char * user_theme_dir = "/Library/WindowMaker/Themes";
char * user_wallpaper_dir = "/Library/WindowMaker/Backgrounds";
char * gnustep_user_root = getenv("GNUSTEP_USER_ROOT");
int dir_idx;
thm_db = create_list();
cat_db = create_list();
if (!gnustep_user_root) {
char * home = getenv ("HOME");
gnustep_user_root = malloc (strlen (home) + 9);
sprintf (gnustep_user_root, "%s/GNUstep", home);
}
themes_dir[3] = malloc (strlen (gnustep_user_root)
+ strlen (user_theme_dir) + 1);
sprintf (themes_dir[3], "%s%s", gnustep_user_root, user_theme_dir);
wallpapers_dir[3] = malloc (strlen (gnustep_user_root)
+ strlen (user_wallpaper_dir) + 1);
sprintf (wallpapers_dir[3], "%s%s", gnustep_user_root, user_wallpaper_dir);
get_categories ();
for (dir_idx=0; dir_idx < 4; dir_idx++) {
get_themes ( themes_dir[dir_idx], NULL);
get_wallpapers (wallpapers_dir[dir_idx], NULL);
}
return 0;
}
char *
get_pixmap_path (const char * icon_name)
{
char * path;
char * home = getenv ("HOME");
struct stat stat_buf;
path = malloc ( strlen (home) + strlen (PACKAGE_NAME)
+ strlen (icon_name) + 8);
sprintf (path, "%s/.%s/%s.xpm", home, PACKAGE_NAME, icon_name);
if (! stat (path, &stat_buf) ) {
return path;
}
free (path);
path = malloc (strlen (PACKAGE) + strlen (icon_name) + 23);
sprintf (path, "/usr/local/share/%s/%s.xpm", PACKAGE, icon_name);
if (! stat (path, &stat_buf) ) {
return path;
}
free (path);
path = malloc (strlen (PACKAGE) + strlen (icon_name) + 17);
sprintf (path, "/usr/share/%s/%s.xpm", PACKAGE, icon_name);
if (! stat (path, &stat_buf) ) {
return path;
}
free (path);
return NULL;
}
int
DrawPixmap (char * XpmFileName)
{
static Pixmap NewPixmap, NewShapeMask = 0;
static int ret, havePixmap= 0;
/* copyXPMArea(5, 69, 54, 54, 5, 5); * Clear window */
copyXPMArea(4, 4, 56, 56, 4, 4); /* Clear window */
if (havePixmap) {
/*
* free up the colors, if we alloc'd some before
*/
if (Attributes.nalloc_pixels > 0)
XFreeColors(display, cmap, Attributes.alloc_pixels,
Attributes.nalloc_pixels, 0);
/*
* Free last pixmap -- we dont need it anymore...
* A ShapeMask is returned if the Pixmap
* had the color None used.
* We could probably change Transparent to None
* to make use of this, but for now,
* lets just ignore it...
*/
if (NewShapeMask != 0) XFreePixmap (display, NewShapeMask);
XFreePixmap(display, NewPixmap);
XpmFreeAttributes (&Attributes);
havePixmap= 0;
} /* havePixmap */
/*
* Grab new pixmap. Accept a reasonable color match.
*/
Attributes.valuemask = XpmExactColors | XpmCloseness
| XpmReturnAllocPixels;
Attributes.exactColors = 0;
Attributes.closeness = 40000;
/* TODO : We might cache icons */
if (XpmFileName) {
if (strcmp (XpmFileName,"*") ) {
ret = XpmReadFileToPixmap (display, Root, XpmFileName,
&NewPixmap, &NewShapeMask, &Attributes);
} else {
ret = XpmCreatePixmapFromData (display, Root, star_xpm,
&NewPixmap, &NewShapeMask, &Attributes);
}
} else {
ret = XpmCreatePixmapFromData (display, Root, no_icon_xpm,
&NewPixmap, &NewShapeMask, &Attributes);
}
if (ret >= 0)
{
int Height = Attributes.height;
XCopyArea (display, NewPixmap, wmgen.pixmap, NormalGC,
0, 0, Attributes.width, Height, 4, 4);
havePixmap= 1;
}
/*
* Make changes visible
*/
RedrawWindow();
return 0;
}
int
ChangeCategory ()
{
char * XpmFileName = NULL;
current_category++;
if (get_item (cat_db, current_category) == -1) current_category = 0;
if (current_category) {
XpmFileName = get_pixmap_path (
( (struct category *) get_item
(cat_db, current_category) ) -> name);
} else {
XpmFileName = strdup("*");
}
DrawPixmap (XpmFileName);
if (XpmFileName) free (XpmFileName);
return 0;
}
int
ChangeTheme ()
{
int fd;
char Command[512];
static unsigned int oldidx = -1;
unsigned int val, i;
struct category * cur_cat = (struct category *)
get_item (cat_db, current_category);
for (i = 0; get_item (cur_cat->themes, i) != -1; i++);
switch (i) {
case 0 : return 1;
case 1 : val = 0; break;
default :
fd = open ("/dev/random", O_RDONLY);
/* We're disallowing "changing" to the same */
read (fd, &val, sizeof (int) );
val = val % (i - 1);
if (val >= oldidx) val++;
close (fd);
oldidx = val;
}
switch ( ( (struct theme *) get_item (cur_cat->themes, val) ) -> type) {
case THEME :
sprintf (Command, "setstyle \"%s\"", ( (struct theme *)
get_item (cur_cat->themes, val) ) -> path);
break;
case WALL :
sprintf (Command, "wmsetbg -u \"%s\"", ( (struct theme *)
get_item (cur_cat->themes, val) ) -> path);
}
system(Command);
auto_switch_next = time(NULL) + (auto_switch_delay * 60);
return 0;
}
void
pressEvent (XButtonEvent *xev)
{
/* Mouse's left button clicked */
if (xev->button == Button1) ChangeTheme ();
/* Mouse's right button clicked */
if (xev->button == Button3) ChangeCategory ();
}
int
ProcessXEvents ()
{
while (XPending (display) )
{
XEvent event;
XNextEvent (display, &event);
switch (event.type) {
case Expose : RedrawWindow (); break;
case ButtonPress : pressEvent (&event.xbutton); break;
case ButtonRelease : break;
}
}
return 0;
}
void
print_usage ()
{
printf ("%s\n", PACKAGE_STRING);
printf ("Report problems to <%s>\n", PACKAGE_BUGREPORT);
printf ("\nusage: %s [options]...\n\n", PACKAGE_NAME);
printf ("\t-display <Display>\tUse alternate X display.\n");
printf ("\t-h\t\t\tDisplay help screen.\n\n");
}
void
ParseCMDLine (int argc, char *argv[])
{
int i, x;
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "-a") == 0) {
if (argc > i+1) {
x = sscanf(argv[i+1], "%u", &auto_switch_delay);
if (x != 1) {
auto_switch_delay = 0;
printf("Found auto-option but could not parse parameter\n");
exit(-1);
} else {
++i;
}
} else {
printf("Found auto-option but number is missing!\n");
exit(-1);
}
} else {
if (!strcmp (argv[i], "-display") ) {
++i;
} else {
print_usage();
exit (1);
}
}
}
}
int
Init (int argc, char * argv[])
{
char * basename = current_category
? ( (struct category *)
get_item (cat_db, current_category)
) -> name
: NULL;
char * path = NULL;
ParseCMDLine (argc, argv);
BuildDatabase ();
if (current_category) {
path = get_pixmap_path (basename);
} else {
path = strdup ("*");
}
BuildBitmask();
openXwindow (argc, argv, back_xpm, mask_bits,
ICONSIZE, ICONSIZE);
cmap = DefaultColormap(display, DefaultScreen(display));
Attributes.nalloc_pixels = 0;
DrawPixmap (path);
if (path) free (path);
return 0;
}
int
main (int argc, char *argv[])
{
unsigned int delay_timer = 0;
Init (argc, argv);
if (auto_switch_delay > 0) {
auto_switch_next = time(NULL) + (auto_switch_delay * 60);
}
/* TODO : use sigsuspend */
while (1) {
ProcessXEvents ();
delay_timer += DELAY;
if ( delay_timer > 10000000L ) { /* check every 10 seconds */
delay_timer = 0;
if (auto_switch_delay > 0) {
if (auto_switch_next < time(NULL)) {
ChangeTheme();
}
}
}
usleep (DELAY);
}
}