gliv-1.3.1
[gliv.git] / loading.c
blob87ee136e60b4cbc03f2975c4f637d307b35ed62d
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 * See the COPYING file for license information.
18 * Guillaume Chazarain <booh@altern.org>
21 /************************************
22 * Loading and unloading of images. *
23 ************************************/
25 #include <stdlib.h>
26 #include <string.h>
27 #include <time.h>
28 #include <unistd.h>
29 #include <dirent.h>
30 #include <signal.h>
32 #ifdef HAVE_FTW
33 #include <ftw.h>
34 #else
35 #include "ftw.h"
36 #endif
38 #include "gliv.h"
40 extern gliv_image *current_image;
42 static gliv_image *previous_image = NULL;
43 static gliv_image *next_image = NULL;
45 static GPtrArray *filenames_array;
47 static gchar *loading_filename;
49 /*** Image destroying. ***/
51 void free_imlib_image(Imlib_Image im)
53 if (im != NULL) {
54 imlib_context_set_image(im);
55 imlib_free_image();
59 static void destroy_image(gliv_image * im)
61 if (im != NULL) {
62 glDeleteTextures(im->nb_width * im->nb_height, im->textures);
63 glDeleteLists(im->list, 1);
64 g_free(im->ident);
65 g_free(im);
69 /*** Misc. ***/
71 #define FILE_ID(x) g_ptr_array_index(filenames_array, x)
73 static void segv_handler(gint sig)
75 if (loading_filename == NULL)
76 g_printerr("Segmentation fault, not while loading an image\n");
77 else
78 g_printerr("Segmentation fault while loading %s\n", loading_filename);
80 signal(sig, SIG_DFL);
81 raise(sig);
84 void fill_ident(gliv_image * im)
86 g_free(im->ident);
87 im->ident = g_strdup_printf("%s (%u/%u)", (gchar *) FILE_ID(im->number),
88 im->number + 1, filenames_array->len);
91 /* Used by qsort. */
92 static gint compar(gconstpointer a, gconstpointer b)
94 return strcmp(*((gchar **) a), *((gchar **) b));
97 static void reorder_array(gpointer * ptr, guint num, gboolean shuffle)
99 guint i, r;
100 gpointer tmp;
102 if (shuffle == TRUE) {
104 srandom((guint) (time(NULL) + getpid()));
106 for (i = 0; i < num; i++) {
107 tmp = ptr[i];
108 r = random() % num;
109 ptr[i] = ptr[r];
110 ptr[r] = tmp;
112 } else
113 qsort(ptr, num, sizeof(gpointer), compar);
116 /*** Operations on the filenames array. ***/
118 static void add_file_to_list(gchar * filename)
120 g_ptr_array_add(filenames_array, filename);
123 /* Called only by ftw(). */
124 static gint ftw_add_to_list(const gchar * file, const struct stat *s, gint flag)
126 if (flag == FTW_F && S_ISDIR(s->st_mode) == FALSE)
127 add_file_to_list(g_strdup(file));
128 return 0;
131 static void add_to_list(gchar * name, gboolean recursive)
133 struct stat st;
134 struct dirent *dir_ent;
135 DIR *dir;
136 gboolean is_dir;
137 gchar *full_path;
139 if (stat(name, &st) == -1)
140 return;
142 if (S_ISDIR(st.st_mode) == TRUE) {
143 if (recursive == TRUE)
144 /* Traverse recursively the directory. */
145 ftw(name, ftw_add_to_list, 32);
146 else {
147 /* Add all files in the directory. */
148 dir = opendir(name);
149 if (dir == NULL)
150 return;
152 dir_ent = readdir(dir);
153 while (dir_ent != NULL) {
154 full_path = g_strconcat(name, "/", dir_ent->d_name, NULL);
156 #ifdef _DIRENT_HAVE_D_TYPE
157 is_dir = (dir_ent->d_type == DT_DIR);
158 #else
159 stat(full_path, &st);
160 is_dir = (S_ISDIR(st.st_mode) == TRUE);
161 #endif
163 if (is_dir == FALSE)
164 add_file_to_list(full_path);
165 else
166 g_free(full_path);
167 dir_ent = readdir(dir);
169 closedir(dir);
171 } else
172 /* Add the file. */
173 add_file_to_list(name);
176 void init_list(gchar ** files, guint num, gboolean recursive, gboolean shuffle)
178 guint i;
180 filenames_array = g_ptr_array_new();
182 for (i = 0; i < num; i++)
183 add_to_list(files[i], recursive);
185 reorder_array(filenames_array->pdata, filenames_array->len, shuffle);
189 * This function is used when we want an image in a bunch
190 * starting at 'position' to be the first of the bunch.
191 * That's why the searching loop starts at 'i = position'.
193 void place_at_position(gchar * filename, gint position)
195 guint i;
197 for (i = position; i < filenames_array->len; i++) {
198 if (g_str_equal(FILE_ID(i), filename) == TRUE) {
199 g_free(FILE_ID(i));
200 FILE_ID(i) = FILE_ID(position);
201 FILE_ID(position) = filename;
202 return;
208 * This function is used to place what was added at the end
209 * of the array at its correct position. It exchanges :
210 * FILE_ID(index1) with FILE_ID(index2),
211 * FILE_ID(index1 + 1) with FILE_ID(index2 + 1),
212 * ..
213 * FILE_ID(index1 + nb) with FILE_ID(index2 + nb).
215 static void array_switch(guint index1, guint index2, guint nb)
217 gchar *tmp;
219 for (; nb > 0; index1++, index2++, nb--) {
220 tmp = FILE_ID(index2);
221 FILE_ID(index2) = FILE_ID(index1);
222 FILE_ID(index1) = tmp;
226 void insert_at_current_position(gchar ** filename, gint nb, gboolean shuffle)
228 gint offset;
229 gint i;
231 offset = filenames_array->len;
233 for (i = 0; i < nb; i++)
234 add_to_list(filename[i], FALSE);
236 reorder_array(filenames_array->pdata + offset,
237 filenames_array->len - offset, shuffle);
239 /* Put the extension at its place in the array. */
240 array_switch(offset, current_image->number + 1,
241 filenames_array->len - offset);
244 /*** Image loading. ***/
246 static gliv_image *load_file(const gchar * filename)
248 gliv_image *im;
250 im = g_new0(gliv_image, 1);
252 /* Used if there is a segfault. */
253 loading_filename = (gchar *) filename;
255 im->im = imlib_load_image(filename);
256 if (im->im == NULL) {
257 g_free(im);
258 return NULL;
261 imlib_context_set_image(im->im);
262 im->width = imlib_image_get_width();
263 im->height = imlib_image_get_height();
265 create_display_list(im);
267 /* If there is a segfault it won't be because of the loading. */
268 loading_filename = NULL;
270 return im;
273 static gliv_image *load_in_direction(gshort dir)
275 gliv_image *im = NULL;
276 guint id;
278 if (current_image == NULL)
279 /* First time. */
280 id = 0;
281 else
282 id = current_image->number + dir;
284 while (id < filenames_array->len) {
285 im = load_file(FILE_ID(id));
286 if (im == NULL)
287 /* Delete bad images from the list. */
288 g_ptr_array_remove_index(filenames_array, id);
289 else
290 break;
293 if (im != NULL)
294 im->number = id;
296 return im;
299 void load_direction(gchar dir)
301 gliv_image **backup;
302 gliv_image **next;
303 gliv_image *trash;
304 static gchar next_direction;
307 * Before loading the next image in a prebuffer we let GTK process
308 * remaining events and if there is a request for the next
309 * image in the events pending then -> crash.
310 * With this boolean we process loading request only if we know
311 * all loadings are finished.
313 static gboolean load_finished = TRUE;
315 if (load_finished == FALSE) {
317 * We come from a gtk_main_iteration() called here, so
318 * we stop processing events and we load the next image.
320 next_direction = dir;
321 return;
324 load_finished = FALSE;
326 while (ABS(dir) == 1 && gtk_events_pending() == 0) {
328 if ((dir == 1 && next_image == NULL)
329 || (dir == -1 && previous_image == NULL)) {
330 /* Out of bounds. */
331 load_finished = TRUE;
332 return;
335 if (dir == -1) {
336 backup = &next_image;
337 next = &previous_image;
338 } else {
339 /* dir == 1 */
340 backup = &previous_image;
341 next = &next_image;
344 trash = *backup;
345 *backup = current_image;
346 current_image = *next;
347 render();
348 destroy_image(trash);
350 next_direction = 0;
351 while (gtk_events_pending() != 0 && next_direction == 0)
352 gtk_main_iteration();
354 *next = load_in_direction(dir);
356 /* Usually next_direction == 0, and the while loop ends. */
357 dir = next_direction;
359 load_finished = TRUE;
362 void load_first_images(void)
364 signal(SIGSEGV, segv_handler);
366 current_image = load_in_direction(1);
367 if (current_image == NULL) {
368 g_printerr("No images found.\n");
369 gtk_exit(1);
372 render();
373 next_image = load_in_direction(1);
376 void open_file(gchar * filename, gboolean add_dir, gboolean shuffle_flag)
378 gchar *dir;
379 gliv_image *im;
380 gliv_image *trash;
381 gchar *tmp;
383 /* Try to load the requested image. */
384 im = load_file(filename);
385 if (im == NULL)
386 return;
388 if (add_dir == TRUE) {
389 /* Add all files in the directory to the end of the array. */
390 dir = g_dirname(filename);
391 insert_at_current_position(&dir, 1, shuffle_flag);
393 /* Put the requested image at current position in the file list. */
394 place_at_position(filename, current_image->number + 1);
395 } else {
396 tmp = g_strdup(filename);
397 insert_at_current_position(&tmp, 1, shuffle_flag);
400 im->number = current_image->number + 1;
402 trash = previous_image;
403 previous_image = current_image;
404 current_image = im;
405 render();
407 destroy_image(trash);
408 destroy_image(next_image);
410 next_image = load_in_direction(1);