#include #include #include #include #include #include #include #include #include #include #include #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 \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); } }