#include #include #include #include "preload.h" #include "wallpapers.h" #include "walls_conf.h" static gboolean preload_thread_exit = FALSE; static GHashTable *hashtable = NULL; static GMutex *preload_hashtable_mutex = NULL, *preload_thread_mutex = NULL; static GThread *preload_thread = NULL; static gpointer preload_thread_func(gpointer); struct preload_thread_data_t { GArray *array; gint win_width, win_height; }; static void preload_hash_table_free(gpointer data) { struct preload_hash_table_value_t *pt; pt = data; if(pt->pb) g_object_unref(pt->pb); g_object_unref(pt->pb_orig); g_free(data); } void preload_init() { preload_hashtable_mutex = g_mutex_new(); preload_thread_mutex = g_mutex_new(); hashtable = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, preload_hash_table_free); } void preload_free() { if(preload_thread) { preload_thread_exit = TRUE; g_thread_join(preload_thread); } g_mutex_free(preload_hashtable_mutex); g_mutex_free(preload_thread_mutex); g_hash_table_destroy(hashtable); preload_hashtable_mutex = NULL; } #define preload_index(i) (((i) / 2) + 1) * (((i) % 2 * (-2) + 1)) static GArray *preload_prepare_data(GtkWidget *thumbview) { GList *list; GArray *array; GtkTreePath *sel_path, *path; GtkTreeModel *model; GtkTreeIter iter; GValue value = {0}; gchar *filename; guint list_len; gint selected_index, *indices, index; gboolean b; array = g_array_new(FALSE, FALSE, sizeof(gchar*)); list = gtk_icon_view_get_selected_items(GTK_ICON_VIEW(thumbview)); list_len = g_list_length(list); if(list_len != 1) { g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL); g_list_free(list); return array; } sel_path = g_list_nth_data(list, 0); indices = gtk_tree_path_get_indices(sel_path); if(!indices) { g_warning("preload_thread: Failed to get indices"); g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL); g_list_free(list); return array; } selected_index = indices[0]; model = gtk_icon_view_get_model(GTK_ICON_VIEW(thumbview)); /* Make sure the selected pixbuf is also preloaded. */ if(gtk_tree_model_get_iter(model, &iter, sel_path)) { gtk_tree_model_get_value(model, &iter, 1, &value); filename = g_value_dup_string(&value); g_value_unset(&value); g_array_append_val(array, filename); } for(gint i = 0; i < config.max_preload; i++) { /* Make sure the selected pixbuf is also preloaded. */ if(array->len == 0 && gtk_tree_model_get_iter(model, &iter, sel_path)) { gtk_tree_model_get_value(model, &iter, 1, &value); filename = g_value_dup_string(&value); g_value_unset(&value); g_array_append_val(array, filename); } index = selected_index + preload_index(i); if(index < 0) { continue; } path = gtk_tree_path_new_from_indices(index, -1); if(gtk_tree_path_get_depth(path) != 1) { /* Assume we're out of bounds. */ gtk_tree_path_free(path); continue; } b = gtk_tree_model_get_iter(model, &iter, path); gtk_tree_path_free(path); if(b == FALSE) continue; gtk_tree_model_get_value(model, &iter, 1, &value); filename = g_value_dup_string(&value); g_value_unset(&value); g_array_append_val(array, filename); } g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL); g_list_free(list); return array; } void preload_start_thread(GtkWidget *thumbview, gint win_width, gint win_height) { struct preload_thread_data_t *data; GError *error = NULL; if(preload_thread) { preload_thread_exit = TRUE; g_thread_join(preload_thread); } data = g_malloc(sizeof(struct preload_thread_data_t)); data->array = preload_prepare_data(thumbview); data->win_width = win_width; data->win_height = win_height; preload_thread_exit = FALSE; preload_thread = g_thread_create(preload_thread_func, data, TRUE, &error); if(!preload_thread) { g_error("%s", error->message); g_error_free(error); } } void preload_clear() { g_mutex_lock(preload_hashtable_mutex); g_hash_table_remove_all(hashtable); g_mutex_unlock(preload_hashtable_mutex); } void preload_clear_sized() { GHashTableIter iter; struct preload_hash_table_value_t *data; g_mutex_lock(preload_hashtable_mutex); g_hash_table_iter_init(&iter, hashtable); while(g_hash_table_iter_next(&iter, NULL, (gpointer)&data)) { if(data->pb) { g_object_unref(data->pb); data->pb = NULL; } } g_mutex_unlock(preload_hashtable_mutex); } static void preload_add(const gchar *filename, gpointer data) { g_mutex_lock(preload_hashtable_mutex); g_hash_table_replace(hashtable, g_strdup(filename), data); g_mutex_unlock(preload_hashtable_mutex); } gpointer preload_get(const gchar *filename) { gpointer data; g_mutex_lock(preload_hashtable_mutex); data = g_hash_table_lookup(hashtable, filename); g_mutex_unlock(preload_hashtable_mutex); return data; } inline static void add_pixbuf(const gchar *filename, gpointer _data) { struct preload_thread_data_t *data; struct preload_hash_table_value_t *value; GdkPixbuf *orig, *pb; GError *error = NULL; gint width, height; gdouble ratio; /* Check if we already loaded this filename. */ if(preload_get(filename)) return; orig = gdk_pixbuf_new_from_file(filename, &error); if(!orig) { g_warning("%s", error->message); g_error_free(error); return; } data = _data; pb = resize_pixbuf(orig, data->win_width, data->win_height, &width, &height, &ratio); value = g_malloc(sizeof(struct preload_hash_table_value_t)); value->pb_orig = orig; if(pb) { value->pb = pb; value->width = width; value->height = height; value->ratio = ratio; } else { value->pb = NULL; value->width = 0; value->height = 0; value->ratio = 0; } preload_add(filename, value); } static gpointer preload_thread_func(gpointer _data) { struct preload_thread_data_t *data; GArray *array; GList *keys; gchar *added_name; const gchar *key_name; gboolean found; data = _data; array = data->array; g_mutex_lock(preload_hashtable_mutex); keys = g_hash_table_get_keys(hashtable); g_mutex_unlock(preload_hashtable_mutex); for(int i = 0; i < g_list_length(keys) && preload_thread_exit == FALSE; i++) { found = FALSE; key_name = g_list_nth_data(keys, i); for(int j = 0; j < array->len && preload_thread_exit == FALSE; j++) { added_name = g_array_index(array, gchar*, j); if(g_strcmp0(key_name, added_name) == 0) { found = TRUE; break; } } if(found == FALSE) { g_mutex_lock(preload_hashtable_mutex); g_hash_table_remove(hashtable, key_name); g_mutex_unlock(preload_hashtable_mutex); } } for(int i = 0; i < array->len && preload_thread_exit == FALSE; i++) { added_name = g_array_index(array, gchar*, i); add_pixbuf(added_name, data); } g_list_free(keys); g_array_free(array, TRUE); g_free(data); preload_thread = NULL; return NULL; }