1 /* theme.c - functions related to theme handling
2 *
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) any later version.
7 */
8 #include <gtk/gtk.h>
9 #include <stdlib.h>
10 #include <sys/types.h>
11 #include <dirent.h>
12 #include <string.h>
13 #include <sys/stat.h>
14 #include <unistd.h>
15 #include <stdio.h>
16
17 #include "theme.h"
18
19 #include "themerc.h"
20 #include "game.h"
21 #include "gtkutils.h" /* ut_simple_message... */
22 #include "prefs.h"
23
24 GtkbTheme *gtkbTheme=NULL;
25
26 /* some file/dir names */
27 gchar *INSTALLPATH=DATADIR "/gtkballs/themes/";
28 gchar *THEMEPREFIX="/.gtkballs/themes/";
29
30 #define DELTA(d, h) \
31 d = h < 0 ? (d > -h ? d + h : 0) : (d + h < 255 ? d + h : 255)
32
hilight_pixbuff8(GdkPixbuf * pb,gint dr,gint dg,gint db)33 void hilight_pixbuff8(GdkPixbuf *pb, gint dr, gint dg, gint db) {
34 /* pb created with 8b/rgb without alpha */
35 gint i;
36 gint nc = gdk_pixbuf_get_n_channels(pb);
37 gint delta = nc - 3;
38 gint w = gdk_pixbuf_get_width(pb);
39 gint l = w * gdk_pixbuf_get_height(pb);
40 gint d = gdk_pixbuf_get_rowstride(pb) - w * nc;
41 guchar *data = gdk_pixbuf_get_pixels(pb);
42
43 for(i = 0; i < l; i++) {
44 if(i && (i%w == 0)) {
45 data += d;
46 }
47 DELTA(*data, dr);
48 data++;
49 DELTA(*data, dg);
50 data++;
51 DELTA(*data, db);
52 data += delta + 1;
53 }
54 }
55
find_theme_path(gchar * themename)56 gchar *find_theme_path(gchar *themename) {
57 gchar *homedir;
58 gchar *themepath;
59 struct stat buf;
60
61 if((homedir = getenv("HOME")) &&
62 (themepath = g_strconcat(homedir, THEMEPREFIX, themename, G_DIR_SEPARATOR_S, NULL))) {
63 if(!stat(themepath, &buf) && S_ISDIR(buf.st_mode)) {
64 return themepath;
65 }
66 g_free(themepath);
67 }
68 if((themepath = g_strconcat(INSTALLPATH, themename, G_DIR_SEPARATOR_S, NULL))) {
69 if(!stat(themepath, &buf) && S_ISDIR(buf.st_mode)) {
70 return themepath;
71 }
72 g_free(themepath);
73 }
74 return NULL;
75 }
76
gtkb_load_pixmap(GtkbPixmap * pixmap,gchar * path,gchar * pixmapname)77 gint gtkb_load_pixmap(GtkbPixmap *pixmap, gchar *path, gchar *pixmapname) {
78 gchar *fname;
79 GError *error = NULL;
80 gint rv = 1;
81
82 if(!(fname = g_strconcat(path, pixmapname, NULL))) {
83 return 0;
84 }
85 if(pixmap->pixbuf) g_object_unref(pixmap->pixbuf);
86 pixmap->pixbuf = gdk_pixbuf_new_from_file(fname, &error);
87 if(!pixmap->pixbuf) {
88 ut_simple_message_box(error->message);
89 g_error_free(error);
90 rv = 0;
91 } else {
92 pixmap->xsize = gdk_pixbuf_get_width(pixmap->pixbuf);
93 pixmap->ysize = gdk_pixbuf_get_height(pixmap->pixbuf);
94 }
95 g_free(fname);
96 return rv;
97 }
98
gtkb_pixmap_free(GtkbPixmap pixmap)99 void gtkb_pixmap_free(GtkbPixmap pixmap) {
100 if(pixmap.pixbuf) {
101 g_object_unref(pixmap.pixbuf);
102 }
103 }
104
gtkb_theme_free(GtkbTheme * theme)105 void gtkb_theme_free(GtkbTheme *theme) {
106 gint i, j;
107
108 if(!theme) {
109 return;
110 }
111 gtkb_pixmap_free(theme->emptycell);
112 gtkb_pixmap_free(theme->hemptycell);
113 for(i = 0; i < 8; i++) {
114 gtkb_pixmap_free(theme->paws[i]);
115 }
116 if(theme->balls) {
117 for(i = 0; i < theme->numballs; i++) {
118 gtkb_pixmap_free(theme->balls[i].ball);
119 gtkb_pixmap_free(theme->balls[i].small);
120 if(theme->balls[i].jump) {
121 for(j = 0; j < theme->balls[i].jumpphases; j++) {
122 gtkb_pixmap_free(theme->balls[i].jump[j]);
123 }
124 g_free(theme->balls[i].jump);
125 if(theme->balls[i].jumpdelays) {
126 g_free(theme->balls[i].jumpdelays);
127 }
128 }
129 if(theme->balls[i].destroy) {
130 for(j = 0; j < theme->balls[i].destroyphases; j++) {
131 gtkb_pixmap_free(theme->balls[i].destroy[j]);
132 }
133 g_free(theme->balls[i].destroy);
134 if(theme->balls[i].destroydelays) {
135 g_free(theme->balls[i].destroydelays);
136 }
137 }
138 }
139 }
140 g_free(theme->balls);
141 g_free(theme);
142 }
143
144 /* warning! tmpname will be free()'d! */
gtkb_load_pixmap_from(gchar ** trc,gchar * themepath,gchar * tmpname,GtkbPixmap * pixmap)145 gint gtkb_load_pixmap_from(gchar **trc, gchar *themepath, gchar *tmpname, GtkbPixmap *pixmap) {
146 gchar *val;
147 gint ret;
148
149 val = trc_get_str(trc, tmpname);
150 g_free(tmpname);
151 if(val == NULL) return 0;
152 ret = gtkb_load_pixmap(pixmap, themepath, val);
153 g_free(val);
154
155 return ret;
156 }
157
158 #define CHECKRET(ret, cond) \
159 if(ret == cond) { \
160 gtkb_theme_free(theme); \
161 trc_close(trc); \
162 return NULL; \
163 }
164
gtkb_make_hl_pixmap(GtkbTheme * theme)165 void gtkb_make_hl_pixmap(GtkbTheme *theme) {
166 if(theme->hemptycell.pixbuf) {
167 gtkb_pixmap_free(theme->hemptycell);
168 }
169 theme->hemptycell.pixbuf = gdk_pixbuf_copy(theme->emptycell.pixbuf);
170 theme->hemptycell.xsize = theme->emptycell.xsize;
171 theme->hemptycell.ysize = theme->emptycell.ysize;
172 hilight_pixbuff8(theme->hemptycell.pixbuf, prefs_get_hl_dr(), prefs_get_hl_dg(), prefs_get_hl_db());
173 }
174
gtkb_load_theme(gchar * themepath)175 GtkbTheme *gtkb_load_theme(gchar *themepath) {
176 gchar **trc, *opt;
177 gchar *paws[] = {"down_up", "left_right", "up_down", "right_left",
178 "down_right", "down_left", "up_right", "up_left", NULL};
179 gint i, j, ret;
180 GtkbTheme *theme;
181
182 opt = g_strconcat(themepath, "themerc", NULL);
183 trc = trc_open(opt); /* open theme description file */
184 g_free(opt);
185 if(!trc) {
186 return NULL;
187 }
188 theme = g_new0(GtkbTheme, 1);
189
190 /* find and load "empty cell" pixmap. */
191 opt = trc_get_str(trc, "cell");
192 CHECKRET(opt, NULL);
193 ret = gtkb_load_pixmap(&theme->emptycell, themepath, opt);
194 g_free(opt);
195 CHECKRET(ret, 0);
196 /*
197 theme->hemptycell.pixbuf = gdk_pixbuf_copy(theme->emptycell.pixbuf);
198 theme->hemptycell.xsize = theme->emptycell.xsize;
199 theme->hemptycell.ysize = theme->emptycell.ysize;
200 CHECKRET(theme->hemptycell.pixbuf, NULL);
201 hilight_pixbuff8(theme->hemptycell.pixbuf, prefs_get_hl_dr(), prefs_get_hl_dg(), prefs_get_hl_db());
202 */
203 gtkb_make_hl_pixmap(theme);
204
205 /* find and load "footprints" pixmaps. */
206 for(i = 0; paws[i]; i++) {
207 CHECKRET(gtkb_load_pixmap_from(trc, themepath, g_strconcat("paw.", paws[i], NULL),
208 &theme->paws[i]),
209 0);
210 }
211
212 /* query number of available balls in theme */
213 theme->numballs = trc_get_uint(trc, "ball.numbers");
214 CHECKRET(theme->numballs, -1);
215 if(theme->numballs < rules_get_colors()) CHECKRET(0, 0); /* yes, i know. its ugly =) */
216 theme->balls = g_new0(GtkbBall, theme->numballs);
217
218 /* find and load all balls data. */
219 for(i = 0; i < theme->numballs; i++) {
220 CHECKRET(gtkb_load_pixmap_from(trc, themepath, g_strdup_printf("ball.%d.still", i + 1),
221 &theme->balls[i].ball),
222 0);
223 CHECKRET(gtkb_load_pixmap_from(trc, themepath, g_strdup_printf("ball.%d.small", i + 1),
224 &theme->balls[i].small),
225 0);
226
227 opt = g_strdup_printf("ball.%d.jump.numbers", i + 1);
228 theme->balls[i].jumpphases = trc_get_uint(trc, opt);
229 g_free(opt);
230 CHECKRET(theme->balls[i].jumpphases, -1);
231 if(theme->balls[i].jumpphases < 2) CHECKRET(0, 0); /* yes, i know. its ugly =) */
232 theme->balls[i].jump = g_new0(GtkbPixmap, theme->balls[i].jumpphases);
233 theme->balls[i].jumpdelays = g_new0(gint, theme->balls[i].jumpphases);
234
235 for(j = 0; j < theme->balls[i].jumpphases; j++) {
236 CHECKRET(gtkb_load_pixmap_from(trc, themepath,
237 g_strdup_printf("ball.%d.jump.%d", i + 1, j + 1),
238 &theme->balls[i].jump[j]),
239 0);
240 opt = g_strdup_printf("ball.%d.jump.%d.usec", i + 1, j + 1);
241 theme->balls[i].jumpdelays[j] = trc_get_uint(trc, opt);
242 g_free(opt);
243 CHECKRET(theme->balls[i].jumpdelays[j], -1);
244 }
245
246 opt = g_strdup_printf("ball.%d.destroy.numbers", i + 1);
247 theme->balls[i].destroyphases = trc_get_uint(trc, opt);
248 g_free(opt);
249 CHECKRET(theme->balls[i].destroyphases, -1);
250 if(theme->balls[i].destroyphases < 2) CHECKRET(0, 0); /* yes, i know. its ugly =) */
251 if(theme->balls[i].destroyphases > theme->maxdestphases) {
252 theme->maxdestphases = theme->balls[i].destroyphases;
253 }
254 theme->balls[i].destroy = g_new0(GtkbPixmap, theme->balls[i].destroyphases);
255 theme->balls[i].destroydelays = g_new0(gint, theme->balls[i].destroyphases);
256
257 for(j = 0; j < theme->balls[i].destroyphases; j++) {
258 CHECKRET(gtkb_load_pixmap_from(trc, themepath,
259 g_strdup_printf("ball.%d.destroy.%d", i + 1, j + 1),
260 &theme->balls[i].destroy[j]),
261 0);
262 opt = g_strdup_printf("ball.%d.destroy.%d.usec", i + 1, j + 1);
263 theme->balls[i].destroydelays[j] = trc_get_uint(trc, opt);
264 g_free(opt);
265 CHECKRET(theme->balls[i].destroydelays[j], -1);
266 }
267 }
268 trc_close(trc);
269
270 return theme;
271 }
272
load_theme(gchar * themename)273 gint load_theme(gchar *themename) {
274 gchar *themepath;
275 GtkbTheme *theme;
276
277 if(!(themepath = find_theme_path(themename))) return 0;
278
279 theme = gtkb_load_theme(themepath);
280 g_free(themepath);
281
282 if(!theme) return 0;
283
284 gtkb_theme_free(gtkbTheme);
285 gtkbTheme = theme;
286
287 return 1;
288 }
289
gtkb_theme_free_handler(GtkWidget * widget,gpointer data)290 gint gtkb_theme_free_handler(GtkWidget *widget, gpointer data) {
291 gtkb_theme_free(gtkbTheme);
292 gtkbTheme = NULL;
293 return 0;
294 }
295
gtkb_theme_get_balls_num(void)296 gint gtkb_theme_get_balls_num(void) {
297 return gtkbTheme ? gtkbTheme->numballs : 0;
298 }
299
300 /* returns board coordinate of the pointer */
gtkb_theme_get_coord_at_x(gint x)301 gint gtkb_theme_get_coord_at_x(gint x) {
302 if(gtkbTheme) {
303 return (x - 1) / gtkbTheme->emptycell.xsize;
304 }
305 return -1;
306 }
307
gtkb_theme_get_coord_at_y(gint y)308 gint gtkb_theme_get_coord_at_y(gint y) {
309 if(gtkbTheme) {
310 return (y - 1) / gtkbTheme->emptycell.ysize;
311 }
312 return -1;
313 }
314
theme_get_width(void)315 gint theme_get_width(void) {
316 return gtkbTheme->emptycell.xsize;
317 }
318
theme_get_height(void)319 gint theme_get_height(void) {
320 return gtkbTheme->emptycell.xsize;
321 }
322
323 /* find all available themes. */
get_available_themes(void)324 gchar **get_available_themes(void) {
325 DIR *directory;
326 struct dirent *dir_entry;
327 struct stat entry_stat;
328 gchar *entry, *currdir, *hdir, *rcentry;
329 gint i, j, num, flag;
330 gchar **tlist = NULL;
331
332 if(getenv("HOME")) {
333 hdir = g_strconcat(getenv("HOME"), THEMEPREFIX, NULL);
334 } else {
335 hdir = g_strdup("./"); /* FIXME: does it work on non unix os? */
336 }
337 for(j = 0, currdir = INSTALLPATH, num = 0; j < 2; j++, currdir = hdir) {
338 if(!(directory = opendir(currdir))) {
339 continue;
340 }
341 while((dir_entry = readdir(directory))) {
342 if(!strncmp(dir_entry->d_name, ".", 2) ||
343 !strncmp(dir_entry->d_name, "..", 3)) {
344 continue;
345 }
346 entry = g_strconcat(currdir, dir_entry->d_name, NULL);
347 if(!stat(entry, &entry_stat) && S_ISDIR(entry_stat.st_mode)) {
348 rcentry = g_strconcat(entry, G_DIR_SEPARATOR_S, "themerc", NULL);
349 if(!stat(rcentry, &entry_stat)) {
350 flag = 0;
351 for(i=0; i < num && !flag; i++) {
352 if(!strcmp(tlist[i], dir_entry->d_name)) {
353 flag++;
354 }
355 }
356 if(!flag) {
357 num++;
358 tlist = g_realloc(tlist, num * sizeof(gchar *));
359 tlist[num-1] = g_strdup(dir_entry->d_name);
360 }
361 }
362 g_free(rcentry);
363 }
364 g_free(entry);
365 }
366 closedir(directory);
367 }
368 g_free(hdir);
369 tlist = g_realloc(tlist, (num + 1) * sizeof(gchar *));
370 tlist[num] = NULL;
371 return tlist;
372 }
373