wmthemech: Add version 0.3 to repository.
[dockapps.git] / wmthemech / src / wmThemeCh.c
blob7db9df85334b9e2408799f6adf625c4e0f0969b8
1 #include <config.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
7 #include <unistd.h>
8 #include <dirent.h>
9 #include <sys/stat.h>
10 #include <fcntl.h>
12 #include <X11/X.h>
13 #include <X11/xpm.h>
15 #include <time.h>
16 #include "wmgeneral.h"
17 #include "stringlist.h"
19 #include "xpm/back.xpm"
20 #include "xpm/star.xpm"
21 #include "xpm/no_icon.xpm"
25 /*
26 * Delay between refreshes (in microseconds)
28 #define DELAY 10000L
30 #define ICONSIZE 64
33 /* Database structures */
35 enum th_type {THEME, WALL};
37 struct theme {
38 enum th_type type;
39 char * path;
40 /* int theme;
41 char * options;*/
44 struct category {
45 char * name;
46 LIST * themes;
50 LIST * thm_db;
51 LIST * cat_db;
53 /* Themes directories, last one will be replaced */
54 char * themes_dir[] = {
55 "/usr/share/WindowMaker/Themes",
56 "/usr/X11R6/share/WindowMaker/Themes",
57 "/usr/local/share/WindowMaker/Themes",
58 "$GNUSTEP_USER_ROOT/Library/WindowMaker/Themes"
62 /* Wallpapers directories, last one will be replaced */
63 char * wallpapers_dir[] = {
64 "/usr/share/WindowMaker/Backgrounds",
65 "/usr/X11R6/share/WindowMaker/Backgrounds",
66 "/usr/local/share/WindowMaker/Backgrounds",
67 "$GNUSTEP_USER_ROOT/Library/WindowMaker/Backgrounds"
71 char hid_cat_db[] = ".wmThemeCh.cat.db";
72 char cat_db_file[] = "wmThemeCh.cat.db";
73 char reg_cat [] = "categories";
75 int current_category = 0;
76 unsigned long int auto_switch_delay = 0;
77 unsigned long int auto_switch_next = 0;
79 char * mask_bits;
81 XpmAttributes Attributes;
82 Colormap cmap;
85 int
86 BuildBitmask ()
88 int i, j, off = 0;
89 int size = (ICONSIZE * ICONSIZE - 1) / 8 + 1;
91 mask_bits = malloc (size);
93 /* must have iconsize % 8 = 0 */
95 for (i = 0; i < 4 * ICONSIZE / 8; i++) {
96 mask_bits[off++] = 0;
98 for (i = 0; i < ICONSIZE - 8; i++) {
99 mask_bits[off++] = 0xf0;
100 for (j = 0; j < (ICONSIZE / 8 - 2); j++) {
101 mask_bits[off++] = 0xff;
103 mask_bits[off++] = 0x0f;
105 for (i = 0; i < 4 * ICONSIZE / 8; i++) {
106 mask_bits[off++] = 0;
109 return 0;
113 #define ADD_CATEGORY(string) \
114 do { \
115 struct category * newcat; \
117 newcat = malloc (sizeof (struct category) ); \
118 newcat->name = strdup (string); \
119 newcat->themes = create_list (); \
120 add_item (cat_db, (unsigned int) newcat); \
121 } while (0);
123 #define PROCESS_CATEGORY_FILE \
124 if ( (catfile = fopen (catname,"r") ) ) \
126 while (fscanf (catfile, "%255s", ImageName) == 1) { \
127 ADD_CATEGORY(ImageName); \
129 fclose (catfile); \
131 free (catname);
133 /* Register categories recognized by system + user */
135 get_categories ()
137 char * catname;
138 char * home = getenv ("HOME");
139 char ImageName[256];
140 FILE * catfile;
142 ADD_CATEGORY("*");
144 catname = malloc (strlen (home) + strlen (PACKAGE_NAME)
145 + strlen (reg_cat) + 4);
147 sprintf (catname, "%s/.%s/%s", home, PACKAGE_NAME, reg_cat);
149 PROCESS_CATEGORY_FILE
151 catname = malloc (strlen (PACKAGE) + strlen (reg_cat) + 13);
153 sprintf (catname, "/usr/share/%s/%s", PACKAGE, reg_cat);
155 PROCESS_CATEGORY_FILE
157 catname = malloc (strlen (PACKAGE) + strlen (reg_cat) + 19);
159 sprintf (catname, "/usr/local/share/%s/%s", PACKAGE, reg_cat);
161 PROCESS_CATEGORY_FILE
163 return 0;
167 enum de_type { ERROR = -1, IGNORED, STYLE, DE_THEME, SUBDIR, IMAGE };
169 /* Return values :
170 * -1 : stat error
171 * 0 : Ignored entries : ., .., category
172 * 1 : regular file (.style)
173 * 2 : theme directory (.themed)
174 * 3 : sub-directory
175 * 4 : image
177 enum de_type
178 entry_type (char * dir, char * entry)
180 char * name = malloc (strlen (dir) + strlen (entry) + 2);
181 struct stat buf;
183 sprintf (name, "%s/%s", dir, entry);
185 if (stat (name, &buf) ) {
186 perror ("stat ");
187 free (name);
188 return ERROR;
191 free (name);
193 if (! S_ISDIR (buf.st_mode) ) {
194 if (! strcmp (entry,cat_db_file ) ) return IGNORED;
195 if (! strcmp (entry,hid_cat_db) ) return IGNORED;
196 /* TODO : Use file (1) */
197 if (strstr (entry, ".xpm" ) ) return IMAGE;
198 if (strstr (entry, ".png" ) ) return IMAGE;
199 if (strstr (entry, ".jpg" ) ) return IMAGE;
200 if (strstr (entry, ".jpeg") ) return IMAGE;
201 if (strstr (entry, ".tif" ) ) return IMAGE;
202 if (strstr (entry, ".ppm" ) ) return IMAGE;
204 return STYLE;
207 if (strstr (entry, ".themed") ) return DE_THEME;
208 if (strcmp (entry,"." ) && strcmp (entry,"..") ) return SUBDIR;
210 return IGNORED;
214 struct theme *
215 insert_into_thm (char * path, enum th_type type)
217 struct theme * new_theme = malloc (sizeof (struct theme) );
219 new_theme->type = type;
220 new_theme->path = strdup (path);
221 add_item (thm_db, (int) new_theme);
223 return new_theme;
227 insert_into_cat (struct theme * theme, LIST * cat_list)
229 int i, j, lcat;
230 char * lcat_nam;
232 add_item ( ( (struct category *) get_item (cat_db, 0) ) -> themes,
233 (int) theme);
235 /* for each category theme is in */
236 for (j = 0; (lcat = get_item (cat_list, j) ) != -1; j++) {
237 int ucat;
238 lcat_nam = (char *) lcat;
239 /* for each user category */
240 for (i=1; (ucat = get_item (cat_db, i) ) != -1; i++) {
241 struct category * cat = (struct category *) ucat;
242 /* check if they match */
243 if (! strcmp (lcat_nam, cat->name) ) {
244 add_item (cat->themes, (int) theme);
245 break;
250 return 0;
253 LIST *
254 theme_specific_cat (char * path, LIST * cat_list)
256 FILE * th_cat;
257 char * th_cat_nam = malloc (strlen (path) + strlen (hid_cat_db) + 2);
258 int i, ret, version, subversion;
259 char cat_nam[512];
260 LIST * new_list;
262 sprintf (th_cat_nam, "%s/%s", path, cat_db_file);
263 th_cat = fopen (th_cat_nam, "r");
265 if (! th_cat) {
266 sprintf (th_cat_nam, "%s/%s", path, hid_cat_db);
267 th_cat = fopen (th_cat_nam, "r");
269 if (! th_cat) {
270 free (th_cat_nam);
271 return cat_list;
275 ret = fscanf (th_cat, "# wmThemeCh categories database file version %i.%i",
276 &version, &subversion);
278 if (ret != 2) {
279 printf("%s : invalid signature\n", th_cat_nam);
280 fclose (th_cat);
281 free (th_cat_nam);
282 return cat_list;
285 if (version > 1) {
286 printf("%s : incompatible version\n", th_cat_nam);
287 fclose (th_cat);
288 free (th_cat_nam);
289 return cat_list;
292 new_list = create_list ();
293 add_list (new_list, cat_list);
295 while ( (ret = fscanf (th_cat, "%s", cat_nam) ) == 1) {
296 for (i=0; get_item (cat_db,i) != -1; i++) {
297 struct category * cat =
298 (struct category *) get_item (cat_db,i);
299 if (! strcmp (cat_nam, cat->name) ) {
300 /* printf("%s : '%s'\n", th_cat_nam, cat_nam); */
301 add_item (new_list, (int) strdup (cat->name) );
302 break;
307 fclose (th_cat);
308 free (th_cat_nam);
310 return new_list;
313 /* Read "categories" file in path.
314 * Themes in path and its subdirs are belonging to categories listed
315 * in that file if these categories are defined by user profile.
316 * Updates cat_list accordingly.
319 add_dir_cat (const char * path, LIST * cat_list)
321 FILE * th_cat;
322 char * th_cat_nam = malloc (strlen (path) + strlen (hid_cat_db) + 2);
323 int ret, version, subversion;
324 char cat_nam[512];
326 sprintf (th_cat_nam, "%s/%s", path, hid_cat_db);
327 th_cat = fopen (th_cat_nam, "r");
329 if (! th_cat) {
330 free (th_cat_nam);
331 return 0;
334 ret = fscanf (th_cat, "# wmThemeCh categories database file version %i.%i",
335 &version, &subversion);
337 if (ret != 2) {
338 printf("%s : invalid signature\n", th_cat_nam);
339 fclose (th_cat);
340 free (th_cat_nam);
341 return 0;
345 while ( (ret = fscanf (th_cat, "%s", cat_nam) ) == 1) {
346 int i;
348 for (i=0; get_item (cat_db, i) != -1; i++) {
349 struct category * cat =
350 (struct category *) get_item (cat_db, i);
351 if (! strcmp (cat_nam, cat->name) ) {
352 /* printf("%s : '%s'\n", th_cat_nam, cat_nam); */
353 add_item (cat_list, (int) strdup (cat->name) );
354 break;
359 fclose (th_cat);
360 free (th_cat_nam);
362 return 0;
367 get_themes (char * path, LIST * par_cat_list)
369 struct dirent ** namelist;
370 LIST * cat_list = create_list();
371 int themes_count = 0;
373 int count = scandir (path, &namelist, NULL, alphasort);
375 if (count < 0) {
376 free (cat_list);
377 return -1;
380 if (par_cat_list) add_list (cat_list, par_cat_list);
382 add_dir_cat (path, cat_list);
384 while (count--) {
385 char fullname[512];
386 struct theme * new_theme;
387 LIST * th_spec_cat;
389 sprintf (fullname, "%s/%s", path, namelist[count]->d_name);
391 switch (entry_type (path, namelist[count]->d_name) ) {
392 case ERROR :
393 case IGNORED : break;
394 case STYLE :
395 new_theme = insert_into_thm (fullname, THEME);
396 insert_into_cat (new_theme, cat_list);
397 break;
398 case DE_THEME :
399 new_theme = insert_into_thm (fullname, THEME);
400 th_spec_cat = theme_specific_cat (fullname, cat_list);
401 insert_into_cat (new_theme, th_spec_cat);
402 if (th_spec_cat != cat_list) {
403 delete_list (th_spec_cat);
405 break;
406 case SUBDIR :
407 get_themes (fullname, cat_list);
408 case IMAGE : break;
412 free (cat_list);
414 return 0;
418 get_wallpapers (char * path, LIST * par_cat_list)
420 struct dirent ** namelist;
421 LIST * cat_list = create_list();
422 int themes_count = 0;
424 int count = scandir (path, &namelist, NULL, NULL);
426 if (count < 0) {
427 free (cat_list);
428 return -1;
431 if (par_cat_list) add_list (cat_list, par_cat_list);
433 add_dir_cat (path, cat_list);
435 while (count--) {
436 char fullname[512];
437 struct theme * new_theme;
439 sprintf (fullname, "%s/%s", path, namelist[count]->d_name);
441 switch (entry_type (path, namelist[count]->d_name) ) {
442 case ERROR :
443 case IGNORED :
444 case STYLE :
445 case DE_THEME: break;
446 case SUBDIR :
447 get_wallpapers (fullname, cat_list);
448 break;
449 case IMAGE :
450 new_theme = insert_into_thm (fullname, WALL);
451 insert_into_cat (new_theme, cat_list);
455 free (cat_list);
457 return 0;
461 BuildDatabase ()
463 char * user_theme_dir = "/Library/WindowMaker/Themes";
464 char * user_wallpaper_dir = "/Library/WindowMaker/Backgrounds";
465 char * gnustep_user_root = getenv("GNUSTEP_USER_ROOT");
467 int dir_idx;
469 thm_db = create_list();
470 cat_db = create_list();
472 if (!gnustep_user_root) {
473 char * home = getenv ("HOME");
474 gnustep_user_root = malloc (strlen (home) + 9);
475 sprintf (gnustep_user_root, "%s/GNUstep", home);
478 themes_dir[3] = malloc (strlen (gnustep_user_root)
479 + strlen (user_theme_dir) + 1);
480 sprintf (themes_dir[3], "%s%s", gnustep_user_root, user_theme_dir);
482 wallpapers_dir[3] = malloc (strlen (gnustep_user_root)
483 + strlen (user_wallpaper_dir) + 1);
484 sprintf (wallpapers_dir[3], "%s%s", gnustep_user_root, user_wallpaper_dir);
486 get_categories ();
488 for (dir_idx=0; dir_idx < 4; dir_idx++) {
489 get_themes ( themes_dir[dir_idx], NULL);
490 get_wallpapers (wallpapers_dir[dir_idx], NULL);
493 return 0;
500 char *
501 get_pixmap_path (const char * icon_name)
503 char * path;
504 char * home = getenv ("HOME");
505 struct stat stat_buf;
507 path = malloc ( strlen (home) + strlen (PACKAGE_NAME)
508 + strlen (icon_name) + 8);
510 sprintf (path, "%s/.%s/%s.xpm", home, PACKAGE_NAME, icon_name);
512 if (! stat (path, &stat_buf) ) {
513 return path;
516 free (path);
517 path = malloc (strlen (PACKAGE) + strlen (icon_name) + 23);
519 sprintf (path, "/usr/local/share/%s/%s.xpm", PACKAGE, icon_name);
521 if (! stat (path, &stat_buf) ) {
522 return path;
525 free (path);
526 path = malloc (strlen (PACKAGE) + strlen (icon_name) + 17);
528 sprintf (path, "/usr/share/%s/%s.xpm", PACKAGE, icon_name);
530 if (! stat (path, &stat_buf) ) {
531 return path;
534 free (path);
535 return NULL;
540 DrawPixmap (char * XpmFileName)
542 static Pixmap NewPixmap, NewShapeMask = 0;
543 static int ret, havePixmap= 0;
545 /* copyXPMArea(5, 69, 54, 54, 5, 5); * Clear window */
546 copyXPMArea(4, 4, 56, 56, 4, 4); /* Clear window */
548 if (havePixmap) {
550 * free up the colors, if we alloc'd some before
552 if (Attributes.nalloc_pixels > 0)
553 XFreeColors(display, cmap, Attributes.alloc_pixels,
554 Attributes.nalloc_pixels, 0);
556 * Free last pixmap -- we dont need it anymore...
557 * A ShapeMask is returned if the Pixmap
558 * had the color None used.
559 * We could probably change Transparent to None
560 * to make use of this, but for now,
561 * lets just ignore it...
563 if (NewShapeMask != 0) XFreePixmap (display, NewShapeMask);
565 XFreePixmap(display, NewPixmap);
567 XpmFreeAttributes (&Attributes);
569 havePixmap= 0;
570 } /* havePixmap */
573 * Grab new pixmap. Accept a reasonable color match.
575 Attributes.valuemask = XpmExactColors | XpmCloseness
576 | XpmReturnAllocPixels;
577 Attributes.exactColors = 0;
578 Attributes.closeness = 40000;
580 /* TODO : We might cache icons */
582 if (XpmFileName) {
583 if (strcmp (XpmFileName,"*") ) {
584 ret = XpmReadFileToPixmap (display, Root, XpmFileName,
585 &NewPixmap, &NewShapeMask, &Attributes);
586 } else {
587 ret = XpmCreatePixmapFromData (display, Root, star_xpm,
588 &NewPixmap, &NewShapeMask, &Attributes);
590 } else {
591 ret = XpmCreatePixmapFromData (display, Root, no_icon_xpm,
592 &NewPixmap, &NewShapeMask, &Attributes);
595 if (ret >= 0)
597 int Height = Attributes.height;
598 XCopyArea (display, NewPixmap, wmgen.pixmap, NormalGC,
599 0, 0, Attributes.width, Height, 4, 4);
600 havePixmap= 1;
604 * Make changes visible
606 RedrawWindow();
608 return 0;
616 ChangeCategory ()
618 char * XpmFileName = NULL;
620 current_category++;
621 if (get_item (cat_db, current_category) == -1) current_category = 0;
622 if (current_category) {
623 XpmFileName = get_pixmap_path (
624 ( (struct category *) get_item
625 (cat_db, current_category) ) -> name);
626 } else {
627 XpmFileName = strdup("*");
630 DrawPixmap (XpmFileName);
632 if (XpmFileName) free (XpmFileName);
634 return 0;
639 ChangeTheme ()
641 int fd;
642 char Command[512];
644 static unsigned int oldidx = -1;
645 unsigned int val, i;
646 struct category * cur_cat = (struct category *)
647 get_item (cat_db, current_category);
649 for (i = 0; get_item (cur_cat->themes, i) != -1; i++);
651 switch (i) {
652 case 0 : return 1;
653 case 1 : val = 0; break;
654 default :
655 fd = open ("/dev/random", O_RDONLY);
657 /* We're disallowing "changing" to the same */
658 read (fd, &val, sizeof (int) );
659 val = val % (i - 1);
660 if (val >= oldidx) val++;
662 close (fd);
663 oldidx = val;
666 switch ( ( (struct theme *) get_item (cur_cat->themes, val) ) -> type) {
667 case THEME :
668 sprintf (Command, "setstyle \"%s\"", ( (struct theme *)
669 get_item (cur_cat->themes, val) ) -> path);
670 break;
671 case WALL :
672 sprintf (Command, "wmsetbg -u \"%s\"", ( (struct theme *)
673 get_item (cur_cat->themes, val) ) -> path);
676 system(Command);
678 auto_switch_next = time(NULL) + (auto_switch_delay * 60);
679 return 0;
683 void
684 pressEvent (XButtonEvent *xev)
686 /* Mouse's left button clicked */
687 if (xev->button == Button1) ChangeTheme ();
689 /* Mouse's right button clicked */
690 if (xev->button == Button3) ChangeCategory ();
695 ProcessXEvents ()
697 while (XPending (display) )
699 XEvent event;
701 XNextEvent (display, &event);
703 switch (event.type) {
704 case Expose : RedrawWindow (); break;
705 case ButtonPress : pressEvent (&event.xbutton); break;
706 case ButtonRelease : break;
710 return 0;
717 void
718 print_usage ()
720 printf ("%s\n", PACKAGE_STRING);
721 printf ("Report problems to <%s>\n", PACKAGE_BUGREPORT);
722 printf ("\nusage: %s [options]...\n\n", PACKAGE_NAME);
723 printf ("\t-display <Display>\tUse alternate X display.\n");
724 printf ("\t-h\t\t\tDisplay help screen.\n\n");
728 void
729 ParseCMDLine (int argc, char *argv[])
731 int i, x;
733 for (i = 1; i < argc; i++) {
734 if (strcmp(argv[i], "-a") == 0) {
735 if (argc > i+1) {
736 x = sscanf(argv[i+1], "%u", &auto_switch_delay);
737 if (x != 1) {
738 auto_switch_delay = 0;
739 printf("Found auto-option but could not parse parameter\n");
740 exit(-1);
741 } else {
742 ++i;
744 } else {
745 printf("Found auto-option but number is missing!\n");
746 exit(-1);
748 } else {
750 if (!strcmp (argv[i], "-display") ) {
751 ++i;
752 } else {
753 print_usage();
754 exit (1);
762 Init (int argc, char * argv[])
764 char * basename = current_category
765 ? ( (struct category *)
766 get_item (cat_db, current_category)
767 ) -> name
768 : NULL;
770 char * path = NULL;
772 ParseCMDLine (argc, argv);
773 BuildDatabase ();
775 if (current_category) {
776 path = get_pixmap_path (basename);
777 } else {
778 path = strdup ("*");
781 BuildBitmask();
782 openXwindow (argc, argv, back_xpm, mask_bits,
783 ICONSIZE, ICONSIZE);
785 cmap = DefaultColormap(display, DefaultScreen(display));
787 Attributes.nalloc_pixels = 0;
789 DrawPixmap (path);
791 if (path) free (path);
793 return 0;
798 main (int argc, char *argv[])
800 unsigned int delay_timer = 0;
801 Init (argc, argv);
803 if (auto_switch_delay > 0) {
804 auto_switch_next = time(NULL) + (auto_switch_delay * 60);
807 /* TODO : use sigsuspend */
809 while (1) {
810 ProcessXEvents ();
811 delay_timer += DELAY;
812 if ( delay_timer > 10000000L ) { /* check every 10 seconds */
813 delay_timer = 0;
814 if (auto_switch_delay > 0) {
815 if (auto_switch_next < time(NULL)) {
816 ChangeTheme();
820 usleep (DELAY);