summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/modes/dmenu.c3
-rw-r--r--source/modes/filebrowser.c7
-rw-r--r--source/modes/recursivebrowser.c212
-rw-r--r--source/modes/script.c18
-rw-r--r--source/rofi-icon-fetcher.c368
-rw-r--r--source/rofi.c6
-rw-r--r--source/view.c31
-rw-r--r--source/widgets/listview.c57
-rw-r--r--source/xcb.c63
-rw-r--r--source/xrmoptions.c7
10 files changed, 635 insertions, 137 deletions
diff --git a/source/modes/dmenu.c b/source/modes/dmenu.c
index 3437a6e8..d393fd36 100644
--- a/source/modes/dmenu.c
+++ b/source/modes/dmenu.c
@@ -721,9 +721,6 @@ static cairo_surface_t *dmenu_get_icon(const Mode *sw,
if (dr->icon_name == NULL) {
return NULL;
}
- if (dr->icon_fetch_uid > 0 && dr->icon_fetch_size == height) {
- return rofi_icon_fetcher_get(dr->icon_fetch_uid);
- }
uint32_t uid = dr->icon_fetch_uid =
rofi_icon_fetcher_query(dr->icon_name, height);
dr->icon_fetch_size = height;
diff --git a/source/modes/filebrowser.c b/source/modes/filebrowser.c
index 57791cf8..3b4730b0 100644
--- a/source/modes/filebrowser.c
+++ b/source/modes/filebrowser.c
@@ -603,11 +603,12 @@ static cairo_surface_t *_get_icon(const Mode *sw, unsigned int selected_line,
(FileBrowserModePrivateData *)mode_get_private_data(sw);
g_return_val_if_fail(pd->array != NULL, NULL);
FBFile *dr = &(pd->array[selected_line]);
- if (dr->icon_fetch_uid > 0 && dr->icon_fetch_size == height) {
- return rofi_icon_fetcher_get(dr->icon_fetch_uid);
- }
if (rofi_icon_fetcher_file_is_image(dr->path)) {
dr->icon_fetch_uid = rofi_icon_fetcher_query(dr->path, height);
+ } else if (dr->type == RFILE) {
+ gchar* _path = g_strconcat("thumbnail://", dr->path, NULL);
+ dr->icon_fetch_uid = rofi_icon_fetcher_query(_path, height);
+ g_free(_path);
} else {
dr->icon_fetch_uid = rofi_icon_fetcher_query(icon_name[dr->type], height);
}
diff --git a/source/modes/recursivebrowser.c b/source/modes/recursivebrowser.c
index 7cbd0f96..e6aa1f01 100644
--- a/source/modes/recursivebrowser.c
+++ b/source/modes/recursivebrowser.c
@@ -178,81 +178,134 @@ static void recursive_browser_mode_init_current_dir(Mode *sw) {
}
static void scan_dir(FileBrowserModePrivateData *pd, GFile *path) {
- char *cdir = g_file_get_path(path);
- DIR *dir = opendir(cdir);
- if (dir) {
- struct dirent *rd = NULL;
- while (pd->end_thread == FALSE && (rd = readdir(dir)) != NULL) {
- if (g_strcmp0(rd->d_name, "..") == 0) {
- continue;
- }
- if (g_strcmp0(rd->d_name, ".") == 0) {
- continue;
- }
- if (pd->filter_regex &&
- g_regex_match(pd->filter_regex, rd->d_name, 0, NULL)) {
- continue;
- }
- switch (rd->d_type) {
- case DT_BLK:
- case DT_CHR:
- case DT_FIFO:
- case DT_UNKNOWN:
- case DT_SOCK:
- default:
- break;
- case DT_REG: {
- FBFile *f = g_malloc0(sizeof(FBFile));
- // Rofi expects utf-8, so lets convert the filename.
- f->path = g_build_filename(cdir, rd->d_name, NULL);
- f->name = g_filename_to_utf8(f->path, -1, NULL, NULL, NULL);
- if (f->name == NULL) {
- f->name = rofi_force_utf8(rd->d_name, -1);
- }
- f->type = (rd->d_type == DT_DIR) ? DIRECTORY : RFILE;
- f->icon_fetch_uid = 0;
- f->icon_fetch_size = 0;
- f->link = FALSE;
-
- g_async_queue_push(pd->async_queue, f);
- if (g_async_queue_length(pd->async_queue) > 10000) {
- write(pd->pipefd2[1], "r", 1);
- }
- break;
- }
- case DT_DIR: {
- char *d = g_build_filename(cdir, rd->d_name, NULL);
- GFile *dirp = g_file_new_for_path(d);
- scan_dir(pd, dirp);
- g_object_unref(dirp);
- g_free(d);
- break;
- }
- case DT_LNK: {
- FBFile *f = g_malloc0(sizeof(FBFile));
- // Rofi expects utf-8, so lets convert the filename.
- f->path = g_build_filename(cdir, rd->d_name, NULL);
- f->name = g_filename_to_utf8(f->path, -1, NULL, NULL, NULL);
- if (f->name == NULL) {
- f->name = rofi_force_utf8(rd->d_name, -1);
- }
- f->icon_fetch_uid = 0;
- f->icon_fetch_size = 0;
- f->link = TRUE;
- // Default to file.
- f->type = RFILE;
- g_async_queue_push(pd->async_queue, f);
- if (g_async_queue_length(pd->async_queue) > 10000) {
- write(pd->pipefd2[1], "r", 1);
- }
- break;
- }
- }
- }
- closedir(dir);
+ GQueue *dirs_to_scan = g_queue_new();
+ g_queue_push_tail(dirs_to_scan, g_object_ref(path));
+ GFile *dir_to_scan = NULL;
+ while ( (dir_to_scan = g_queue_pop_head(dirs_to_scan)) != NULL) {
+ char *cdir = g_file_get_path(dir_to_scan);
+ DIR *dir = opendir(cdir);
+ g_object_unref(dir_to_scan);
+ if (dir) {
+ struct dirent *rd = NULL;
+ while (pd->end_thread == FALSE && (rd = readdir(dir)) != NULL) {
+ if (g_strcmp0(rd->d_name, "..") == 0) {
+ continue;
+ }
+ if (g_strcmp0(rd->d_name, ".") == 0) {
+ continue;
+ }
+ if (pd->filter_regex &&
+ g_regex_match(pd->filter_regex, rd->d_name, 0, NULL)) {
+ continue;
+ }
+ switch (rd->d_type) {
+ case DT_BLK:
+ case DT_CHR:
+ case DT_FIFO:
+ case DT_SOCK:
+ default:
+ break;
+ case DT_REG: {
+ FBFile *f = g_malloc0(sizeof(FBFile));
+ // Rofi expects utf-8, so lets convert the filename.
+ f->path = g_build_filename(cdir, rd->d_name, NULL);
+ f->name = g_filename_to_utf8(f->path, -1, NULL, NULL, NULL);
+ if (f->name == NULL) {
+ f->name = rofi_force_utf8(rd->d_name, -1);
+ }
+ if (f->name == NULL) {
+ f->name = g_strdup("n/a");
+ }
+ f->type = (rd->d_type == DT_DIR) ? DIRECTORY : RFILE;
+ f->icon_fetch_uid = 0;
+ f->icon_fetch_size = 0;
+ f->link = FALSE;
+
+ g_async_queue_push(pd->async_queue, f);
+ if (g_async_queue_length(pd->async_queue) > 10000) {
+ write(pd->pipefd2[1], "r", 1);
+ }
+ break;
+ }
+ case DT_DIR: {
+ char *d = g_build_filename(cdir, rd->d_name, NULL);
+ GFile *dirp = g_file_new_for_path(d);
+ g_queue_push_tail(dirs_to_scan, dirp);
+ g_free(d);
+ break;
+ }
+ case DT_UNKNOWN:
+ case DT_LNK: {
+ FBFile *f = g_malloc0(sizeof(FBFile));
+ // Rofi expects utf-8, so lets convert the filename.
+ f->path = g_build_filename(cdir, rd->d_name, NULL);
+ f->name = g_filename_to_utf8(f->path, -1, NULL, NULL, NULL);
+ if (f->name == NULL) {
+ f->name = rofi_force_utf8(rd->d_name, -1);
+ }
+ if (f->name == NULL) {
+ f->name = g_strdup("n/a");
+ }
+ f->icon_fetch_uid = 0;
+ f->icon_fetch_size = 0;
+ // Default to file.
+ f->type = RFILE;
+ if (rd->d_type == DT_LNK) {
+ f->link = TRUE;
+ } else {
+ f->link = FALSE;
+ }
+ {
+ // If we have link, use a stat to fine out what it is, if we fail, we
+ // mark it as file.
+ // TODO have a 'broken link' mode?
+ // Convert full path to right encoding.
+ // DD: Path should be in file encoding, not utf-8
+ // char *file =
+ // g_filename_from_utf8(pd->array[pd->array_length].path,
+ // -1, NULL, NULL, NULL);
+ // TODO: How to handle loops in links.
+ if (f->path) {
+ GStatBuf statbuf;
+ if (g_stat(f->path, &statbuf) == 0) {
+ if (S_ISDIR(statbuf.st_mode)) {
+ char *new_full_path = g_build_filename(cdir, rd->d_name, NULL);
+ g_free(f->path);
+ g_free(f->name);
+ g_free(f);
+ f = NULL;
+ GFile *dirp = g_file_new_for_path(new_full_path);
+ g_queue_push_tail(dirs_to_scan, dirp);
+ g_free(new_full_path);
+ break;
+ } else if (S_ISREG(statbuf.st_mode)) {
+ f->type = RFILE;
+ }
+
+ } else {
+ g_warning("Failed to stat file: %s, %s", f->path,
+ strerror(errno));
+ }
+
+ // g_free(file);
+ }
+ }
+ if (f != NULL) {
+ g_async_queue_push(pd->async_queue, f);
+ if (g_async_queue_length(pd->async_queue) > 10000) {
+ write(pd->pipefd2[1], "r", 1);
+ }
+ }
+ break;
+ }
+ }
+ }
+ closedir(dir);
+ }
+ g_free(cdir);
}
- g_free(cdir);
+ g_queue_free(dirs_to_scan);
}
static gpointer recursive_browser_input_thread(gpointer userdata) {
FileBrowserModePrivateData *pd = (FileBrowserModePrivateData *)userdata;
@@ -335,7 +388,8 @@ static unsigned int recursive_browser_mode_get_num_entries(const Mode *sw) {
return pd->array_length;
}
-static ModeMode recursive_browser_mode_result(Mode *sw, int mretv, G_GNUC_UNUSED char **input,
+static ModeMode recursive_browser_mode_result(Mode *sw, int mretv,
+ G_GNUC_UNUSED char **input,
unsigned int selected_line) {
ModeMode retv = MODE_EXIT;
FileBrowserModePrivateData *pd =
@@ -439,14 +493,14 @@ static cairo_surface_t *_get_icon(const Mode *sw, unsigned int selected_line,
(FileBrowserModePrivateData *)mode_get_private_data(sw);
g_return_val_if_fail(pd->array != NULL, NULL);
FBFile *dr = &(pd->array[selected_line]);
- if (dr->icon_fetch_uid > 0 && dr->icon_fetch_size == height) {
- return rofi_icon_fetcher_get(dr->icon_fetch_uid);
- }
if (rofi_icon_fetcher_file_is_image(dr->path)) {
dr->icon_fetch_uid = rofi_icon_fetcher_query(dr->path, height);
+ } else if (dr->type == RFILE) {
+ gchar* _path = g_strconcat("thumbnail://", dr->path, NULL);
+ dr->icon_fetch_uid = rofi_icon_fetcher_query(_path, height);
+ g_free(_path);
} else {
- dr->icon_fetch_uid =
- rofi_icon_fetcher_query(rb_icon_name[dr->type], height);
+ dr->icon_fetch_uid = rofi_icon_fetcher_query(rb_icon_name[dr->type], height);
}
dr->icon_fetch_size = height;
return rofi_icon_fetcher_get(dr->icon_fetch_uid);
diff --git a/source/modes/script.c b/source/modes/script.c
index f9811704..6ca29813 100644
--- a/source/modes/script.c
+++ b/source/modes/script.c
@@ -72,6 +72,8 @@ typedef struct {
char delim;
/** no custom */
gboolean no_custom;
+ /** keep filter */
+ gboolean keep_filter;
gboolean use_hot_keys;
} ScriptModePrivateData;
@@ -157,6 +159,8 @@ static void parse_header_entry(Mode *sw, char *line, ssize_t length) {
pd->use_hot_keys = (strcasecmp(value, "true") == 0);
} else if (strcasecmp(line, "keep-selection") == 0) {
pd->keep_selection = (strcasecmp(value, "true") == 0);
+ } else if (strcasecmp(line, "keep-filter") == 0) {
+ pd->keep_filter = (strcasecmp(value, "true") == 0);
} else if (strcasecmp(line, "new-selection") == 0) {
pd->new_selection = (int64_t)g_ascii_strtoll(value, NULL, 0);
} else if (strcasecmp(line, "data") == 0) {
@@ -184,6 +188,7 @@ static DmenuScriptEntry *execute_executor(Mode *sw, char *arg,
// Reset these between runs.
pd->new_selection = -1;
pd->keep_selection = 0;
+ pd->keep_filter = 0;
// Environment
char **env = g_get_environ();
@@ -320,6 +325,9 @@ static ModeMode script_mode_result(Mode *sw, int mretv, char **input,
ModeMode retv = MODE_EXIT;
DmenuScriptEntry *new_list = NULL;
unsigned int new_length = 0;
+ // store them as they might be different on next executor and reset.
+ gboolean keep_filter = rmpd->keep_filter;
+ gboolean keep_selection = rmpd->keep_selection;
if ((mretv & MENU_CUSTOM_COMMAND)) {
if (rmpd->use_hot_keys) {
@@ -370,7 +378,7 @@ static ModeMode script_mode_result(Mode *sw, int mretv, char **input,
rmpd->cmd_list = new_list;
rmpd->cmd_list_length = new_length;
- if (rmpd->keep_selection) {
+ if (keep_selection) {
if (rmpd->new_selection >= 0 &&
rmpd->new_selection < rmpd->cmd_list_length) {
rofi_view_set_selected_line(rofi_view_get_active(),
@@ -378,12 +386,14 @@ static ModeMode script_mode_result(Mode *sw, int mretv, char **input,
} else {
rofi_view_set_selected_line(rofi_view_get_active(), selected_line);
}
+ } else {
+ rofi_view_set_selected_line(rofi_view_get_active(), 0);
+ }
+ if (keep_filter == FALSE) {
g_free(*input);
*input = NULL;
- retv = RELOAD_DIALOG;
- } else {
- retv = RESET_DIALOG;
}
+ retv = RELOAD_DIALOG;
}
return retv;
}
diff --git a/source/rofi-icon-fetcher.c b/source/rofi-icon-fetcher.c
index 106f8f10..e22a4975 100644
--- a/source/rofi-icon-fetcher.c
+++ b/source/rofi-icon-fetcher.c
@@ -51,6 +51,10 @@
#include "helper.h"
#include <gdk-pixbuf/gdk-pixbuf.h>
+// thumbnailers key file's group and file extension
+#define THUMBNAILER_ENTRY_GROUP "Thumbnailer Entry"
+#define THUMBNAILER_EXTENSION ".thumbnailer"
+
typedef struct {
// Context for icon-themes.
NkXdgThemeContext *xdg_context;
@@ -63,6 +67,9 @@ typedef struct {
// list extensions
GList *supported_extensions;
uint32_t last_uid;
+
+ // thumbnailers per mime-types hashmap
+ GHashTable *thumbnailers;
} IconFetcher;
typedef struct {
@@ -82,6 +89,7 @@ typedef struct {
int hsize;
cairo_surface_t *surface;
gboolean query_done;
+ gboolean query_started;
IconFetcherNameEntry *entry;
} IconFetcherEntry;
@@ -93,10 +101,156 @@ static void rofi_icon_fetch_entry_free(gpointer data);
*/
IconFetcher *rofi_icon_fetcher_data = NULL;
+static void rofi_icon_fetcher_load_thumbnailers(const gchar *path) {
+ gchar *thumb_path = g_build_filename(path, "thumbnailers", NULL);
+
+ GDir *dir = g_dir_open(thumb_path, 0, NULL);
+
+ if (!dir) {
+ g_free(thumb_path);
+ return;
+ }
+
+ const gchar *dirent;
+
+ while ((dirent = g_dir_read_name(dir))) {
+ if (!g_str_has_suffix(dirent, THUMBNAILER_EXTENSION))
+ continue;
+
+ gchar *filename = g_build_filename(thumb_path, dirent, NULL);
+ GKeyFile *key_file = g_key_file_new();
+ GError *error = NULL;
+
+ if (!g_key_file_load_from_file(key_file, filename, 0, &error)) {
+ g_warning("Error loading thumbnailer %s: %s", filename, error->message);
+ g_error_free(error);
+ } else {
+ gchar *command = g_key_file_get_string(
+ key_file, THUMBNAILER_ENTRY_GROUP, "Exec", NULL);
+ gchar **mime_types = g_key_file_get_string_list(
+ key_file, THUMBNAILER_ENTRY_GROUP, "MimeType", NULL, NULL);
+
+ if (mime_types && command) {
+ guint i;
+ for (i = 0; mime_types[i] != NULL; i++) {
+ if (!g_hash_table_lookup(
+ rofi_icon_fetcher_data->thumbnailers, mime_types[i])) {
+ g_info("Loading thumbnailer %s for mimetype %s", filename, mime_types[i]);
+ g_hash_table_insert(
+ rofi_icon_fetcher_data->thumbnailers,
+ g_strdup(mime_types[i]),
+ g_strdup(command)
+ );
+ }
+ }
+ }
+
+ if (mime_types) g_strfreev(mime_types);
+ if (command) g_free(command);
+ }
+
+ g_key_file_free(key_file);
+ g_free(filename);
+ }
+
+ g_dir_close(dir);
+ g_free(thumb_path);
+}
+
+static gchar** setup_thumbnailer_command(const gchar *command,
+ const gchar *filename,
+ const gchar *encoded_uri,
+ const gchar *output_path,
+ int size) {
+ gchar **command_parts = g_strsplit(command, " ", 0);
+ guint command_parts_count = g_strv_length(command_parts);
+
+ gchar **command_args = NULL;
+
+ if (command_parts) {
+ command_args = g_malloc0(sizeof(gchar*) * (command_parts_count + 3 + 1));
+
+ // set process niceness value to 19 (low priority)
+ guint current_index = 0;
+
+ command_args[current_index++] = g_strdup("nice");
+ command_args[current_index++] = g_strdup("-n");
+ command_args[current_index++] = g_strdup("19");
+
+ // add executable and arguments of the thumbnailer to the list
+ guint i;
+ for (i = 0; command_parts[i] != NULL; i++) {
+ if (strcmp(command_parts[i], "%i") == 0) {
+ command_args[current_index++] = g_strdup(filename);
+ } else if (strcmp(command_parts[i], "%u") == 0) {
+ command_args[current_index++] = g_strdup(encoded_uri);
+ } else if (strcmp(command_parts[i], "%o") == 0) {
+ command_args[current_index++] = g_strdup(output_path);
+ } else if (strcmp(command_parts[i], "%s") == 0) {
+ command_args[current_index++] = g_strdup_printf("%d", size);
+ } else {
+ command_args[current_index++] = g_strdup(command_parts[i]);
+ }
+ }
+
+ command_args[current_index++] = NULL;
+
+ g_strfreev(command_parts);
+ }
+
+ return command_args;
+}
+
+static gboolean exec_thumbnailer_command(gchar **command_args) {
+ // launch and wait thumbnailers process
+ gint wait_status;
+ GError *error = NULL;
+
+ gboolean spawned = g_spawn_sync(NULL, command_args,
+ NULL, G_SPAWN_DEFAULT | G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL, &wait_status, &error);
+
+ if (spawned) {
+ return g_spawn_check_exit_status(wait_status, NULL);
+ } else {
+ g_warning("Error calling thumbnailer: %s", error->message);
+ g_error_free(error);
+
+ return FALSE;
+ }
+}
+
+static gboolean rofi_icon_fetcher_create_thumbnail(const gchar *mime_type,
+ const gchar *filename,
+ const gchar *encoded_uri,
+ const gchar *output_path,
+ int size) {
+ gboolean thumbnail_created = FALSE;
+
+ gchar *command = g_hash_table_lookup(
+ rofi_icon_fetcher_data->thumbnailers, mime_type);
+
+ if (!command) {
+ return thumbnail_created;
+ }
+
+ // split command string to isolate arguments and expand them in a list
+ gchar **command_args = setup_thumbnailer_command(
+ command, filename, encoded_uri, output_path, size);
+
+ if (command_args) {
+ thumbnail_created = exec_thumbnailer_command(command_args);
+ g_strfreev(command_args);
+ }
+
+ return thumbnail_created;
+}
+
static void rofi_icon_fetch_thread_pool_entry_remove(gpointer data) {
IconFetcherEntry *entry = (IconFetcherEntry *)data;
// Mark it in a way it should be re-fetched on next query?
+ entry->query_started = FALSE;
}
+
static void rofi_icon_fetch_entry_free(gpointer data) {
IconFetcherNameEntry *entry = (IconFetcherNameEntry *)data;
@@ -147,6 +301,20 @@ void rofi_icon_fetcher_init(void) {
g_free(exts);
}
g_slist_free(l);
+
+ // load available thumbnailers from system dirs and user dir
+ rofi_icon_fetcher_data->thumbnailers = g_hash_table_new_full(
+ g_str_hash, g_str_equal, (GDestroyNotify)g_free, (GDestroyNotify)g_free);
+
+ const gchar * const *system_data_dirs = g_get_system_data_dirs();
+ const gchar *user_data_dir = g_get_user_data_dir();
+
+ rofi_icon_fetcher_load_thumbnailers(user_data_dir);
+
+ guint i;
+ for (i = 0; system_data_dirs[i] != NULL; i++) {
+ rofi_icon_fetcher_load_thumbnailers(system_data_dirs[i]);
+ }
}
static void free_wrapper(gpointer data, G_GNUC_UNUSED gpointer user_data) {
@@ -157,6 +325,8 @@ void rofi_icon_fetcher_destroy(void) {
if (rofi_icon_fetcher_data == NULL) {
return;
}
+
+ g_hash_table_unref(rofi_icon_fetcher_data->thumbnailers);
nk_xdg_theme_context_free(rofi_icon_fetcher_data->xdg_context);
@@ -288,6 +458,73 @@ gboolean rofi_icon_fetcher_file_is_image(const char *const path) {
return FALSE;
}
+// build thumbnail's path using md5 hash of an entry name
+static gchar* rofi_icon_fetcher_get_thumbnail(gchar* name,
+ int requested_size,
+ int *thumb_size) {
+ // calc entry_name md5 hash
+ GChecksum* checksum = g_checksum_new(G_CHECKSUM_MD5);
+ g_checksum_update(checksum, (guchar*)name, -1);
+ const gchar *md5_hex = g_checksum_get_string(checksum);
+
+ // determine thumbnail folder based on the request size
+ const gchar* cache_dir = g_get_user_cache_dir();
+ gchar* thumb_dir;
+ gchar* thumb_path;
+
+ if (requested_size <= 128) {
+ *thumb_size = 128;
+ thumb_dir = g_strconcat(cache_dir, "/thumbnails/normal/", NULL);
+ thumb_path = g_strconcat(cache_dir, "/thumbnails/normal/",
+ md5_hex, ".png", NULL);
+ } else if (requested_size <= 256) {
+ *thumb_size = 256;
+ thumb_dir = g_strconcat(cache_dir, "/thumbnails/large/", NULL);
+ thumb_path = g_strconcat(cache_dir, "/thumbnails/large/",
+ md5_hex, ".png", NULL);
+ } else if (requested_size <= 512) {
+ *thumb_size = 512;
+ thumb_dir = g_strconcat(cache_dir, "/thumbnails/x-large/", NULL);
+ thumb_path = g_strconcat(cache_dir, "/thumbnails/x-large/",
+ md5_hex, ".png", NULL);
+ } else {
+ *thumb_size = 1024;
+ thumb_dir = g_strconcat(cache_dir, "/thumbnails/xx-large/", NULL);
+ thumb_path = g_strconcat(cache_dir, "/thumbnails/xx-large/",
+ md5_hex, ".png", NULL);
+ }
+
+ // create thumbnail directory if it does not exist
+ g_mkdir_with_parents(thumb_dir, 0700);
+
+ g_free(thumb_dir);
+ g_checksum_free(checksum);
+
+ return thumb_path;
+}
+
+// retrieves icon key from a .desktop file
+static gchar* rofi_icon_fetcher_get_desktop_icon(const gchar* file_path) {
+ GKeyFile *kf = g_key_file_new();
+ GError *key_error = NULL;
+ gchar *icon_key = NULL;
+
+ gboolean res = g_key_file_load_from_file(kf, file_path, 0, &key_error);
+
+ if (res) {
+ icon_key = g_key_file_get_string(kf, "Desktop Entry", "Icon", NULL);
+ } else {
+ g_debug("Failed to parse desktop file %s because: %s.",
+ file_path, key_error->message);
+
+ g_error_free(key_error);
+ }
+
+ g_key_file_free(kf);
+
+ return icon_key;
+}
+
static void rofi_icon_fetcher_worker(thread_state *sdata,
G_GNUC_UNUSED gpointer user_data) {
g_debug("starting up icon fetching thread.");
@@ -299,7 +536,118 @@ static void rofi_icon_fetcher_worker(thread_state *sdata,
const gchar *icon_path;
gchar *icon_path_ = NULL;
- if (g_path_is_absolute(sentry->entry->name)) {
+ if (g_str_has_prefix(sentry->entry->name, "thumbnail://")) {
+ // remove uri thumbnail prefix from entry name
+ gchar *entry_name = &sentry->entry->name[12];
+
+ if (strcmp(entry_name, "") == 0) {
+ sentry->query_done = TRUE;
+ rofi_view_reload();
+ return;
+ }
+
+ // use custom user command to generate the thumbnail
+ if (config.preview_cmd != NULL) {
+ int requested_size = MAX(sentry->wsize, sentry->hsize);
+ int thumb_size;
+
+ icon_path = icon_path_ = rofi_icon_fetcher_get_thumbnail(
+ entry_name, requested_size, &thumb_size);
+
+ if (!g_file_test(icon_path, G_FILE_TEST_EXISTS)) {
+ char **command_args = NULL;
+ int argsv = 0;
+ gchar *size_str = g_strdup_printf("%d", thumb_size);
+
+ helper_parse_setup(
+ config.preview_cmd, &command_args, &argsv,
+ "{input}", entry_name,
+ "{output}", icon_path_, "{size}", size_str, NULL);
+
+ g_free(size_str);
+
+ if (command_args) {
+ exec_thumbnailer_command(command_args);
+ g_strfreev(command_args);
+ }
+ }
+ } else if (g_path_is_absolute(entry_name)) {
+ // if the entry name is an absolute path try to fetch its thumbnail
+ if (g_str_has_suffix(entry_name, ".desktop")) {
+ // if the entry is a .desktop file try to read its icon key
+ gchar *icon_key = rofi_icon_fetcher_get_desktop_icon(entry_name);
+
+ if (icon_key == NULL || strlen(icon_key) == 0) {
+ // no icon in .desktop file, fallback on mimetype icon (text/plain)
+ icon_path = icon_path_ = nk_xdg_theme_get_icon(
+ rofi_icon_fetcher_data->xdg_context, themes, NULL, "text-plain",
+ MIN(sentry->wsize, sentry->hsize), 1, TRUE);
+
+ g_free(icon_key);
+ } else if (g_path_is_absolute(icon_key)) {
+ // icon in .desktop file is an absolute path to an image
+ icon_path = icon_path_ = icon_key;
+ } else {
+ // icon in .desktop file is a standard icon name
+ icon_path = icon_path_ = nk_xdg_theme_get_icon(
+ rofi_icon_fetcher_data->xdg_context, themes, NULL, icon_key,
+ MIN(sentry->wsize, sentry->hsize), 1, TRUE);
+
+ g_free(icon_key);
+ }
+ } else {
+ // build encoded uri string from absolute file path
+ gchar *encoded_uri = g_filename_to_uri(entry_name, NULL, NULL);
+ int requested_size = MAX(sentry->wsize, sentry->hsize);
+ int thumb_size;
+
+ // look for file thumbnail in appropriate folder based on requested size
+ icon_path = icon_path_ = rofi_icon_fetcher_get_thumbnail(
+ encoded_uri, requested_size, &thumb_size);
+
+ if (!g_file_test(icon_path, G_FILE_TEST_EXISTS)) {
+ // try to generate thumbnail
+ char *content_type = g_content_type_guess(entry_name, NULL, 0, NULL);
+ char *mime_type = g_content_type_get_mime_type(content_type);
+
+ if (mime_type) {
+ gboolean created = rofi_icon_fetcher_create_thumbnail(
+ mime_type, entry_name, encoded_uri, icon_path_, thumb_size);
+
+ if (!created) {
+ // replace forward slashes with minus sign to get the icon's name
+ int index = 0;
+
+ while(mime_type[index]) {
+ if(mime_type[index] == '/')
+ mime_type[index] = '-';
+ index++;
+ }
+
+ g_free(icon_path_);
+
+ // try to fetch the mime-type icon
+ icon_path = icon_path_ = nk_xdg_theme_get_icon(
+ rofi_icon_fetcher_data->xdg_context, themes, NULL, mime_type,
+ MIN(sentry->wsize, sentry->hsize), 1, TRUE);
+ }
+
+ g_free(mime_type);
+ g_free(content_type);
+ }
+ }
+
+ g_free(encoded_uri);
+ }
+ }
+
+ // no suitable icon or thumbnail was found
+ if (icon_path_ == NULL || !g_file_test(icon_path, G_FILE_TEST_EXISTS)) {
+ sentry->query_done = TRUE;
+ rofi_view_reload();
+ return;
+ }
+ } else if (g_path_is_absolute(sentry->entry->name)) {
icon_path = sentry->entry->name;
} else if (g_str_has_prefix(sentry->entry->name, "<span")) {
cairo_surface_t *surface = cairo_image_surface_create(
@@ -354,6 +702,7 @@ static void rofi_icon_fetcher_worker(thread_state *sdata,
}
cairo_surface_t *icon_surf = NULL;
+#if 0 // unsure why added in past?
const char *suf = strrchr(icon_path, '.');
if (suf == NULL) {
sentry->query_done = TRUE;
@@ -361,6 +710,7 @@ static void rofi_icon_fetcher_worker(thread_state *sdata,
rofi_view_reload();
return;
}
+#endif
GError *error = NULL;
GdkPixbuf *pb = gdk_pixbuf_new_from_file_at_scale(
@@ -371,9 +721,9 @@ static void rofi_icon_fetcher_worker(thread_state *sdata,
* without decoding all the frames. Since gdk_pixbuf_new_from_file_at_scale
* only decodes the first frame, this specific error needs to be ignored.
*/
- if (error != NULL && g_error_matches(
- error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INCOMPLETE_ANIMATION)) {
- g_clear_error(&error);
+ if (error != NULL && g_error_matches(error, GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INCOMPLETE_ANIMATION)) {
+ g_clear_error(&error);
}
if (error != NULL) {
@@ -409,6 +759,9 @@ uint32_t rofi_icon_fetcher_query_advanced(const char *name, const int wsize,
iter = g_list_next(iter)) {
sentry = iter->data;
if (sentry->wsize == wsize && sentry->hsize == hsize) {
+ if (!sentry->query_started) {
+ g_thread_pool_push(tpool, sentry, NULL);
+ }
return sentry->uid;
}
}
@@ -420,6 +773,7 @@ uint32_t rofi_icon_fetcher_query_advanced(const char *name, const int wsize,
sentry->hsize = hsize;
sentry->entry = entry;
sentry->query_done = FALSE;
+ sentry->query_started = TRUE;
sentry->surface = NULL;
entry->sizes = g_list_prepend(entry->sizes, sentry);
@@ -448,6 +802,9 @@ uint32_t rofi_icon_fetcher_query(const char *name, const int size) {
iter = g_list_next(iter)) {
sentry = iter->data;
if (sentry->wsize == size && sentry->hsize == size) {
+ if (!sentry->query_started) {
+ g_thread_pool_push(tpool, sentry, NULL);
+ }
return sentry->uid;
}
}
@@ -458,6 +815,8 @@ uint32_t rofi_icon_fetcher_query(const char *name, const int size) {
sentry->wsize = size;
sentry->hsize = size;
sentry->entry = entry;
+ sentry->query_done = FALSE;
+ sentry->query_started = TRUE;
sentry->surface = NULL;
entry->sizes = g_list_prepend(entry->sizes, sentry);
@@ -466,6 +825,7 @@ uint32_t rofi_icon_fetcher_query(const char *name, const int size) {
// Push into fetching queue.
sentry->state.callback = rofi_icon_fetcher_worker;
+ sentry->state.free = rofi_icon_fetch_thread_pool_entry_remove;
sentry->state.priority = G_PRIORITY_LOW;
g_thread_pool_push(tpool, sentry, NULL);
diff --git a/source/rofi.c b/source/rofi.c
index 3d6917f8..9ebe28df 100644
--- a/source/rofi.c
+++ b/source/rofi.c
@@ -326,6 +326,9 @@ static void print_main_application_options(int is_term) {
is_term);
print_help_msg("-normal-window", "",
"Behave as a normal window. (experimental)", NULL, is_term);
+ print_help_msg("-transient-window", "",
+ "Behave as a modal dialog that is transient to the currently "
+ "focused window. (experimental)", NULL, is_term);
print_help_msg("-show", "[mode]",
"Show the mode 'mode' and exit. The mode has to be enabled.",
NULL, is_term);
@@ -768,6 +771,9 @@ static gboolean startup(G_GNUC_UNUSED gpointer data) {
if (find_arg("-normal-window") >= 0) {
window_flags |= MENU_NORMAL_WINDOW;
}
+ if (find_arg("-transient-window") >= 0) {
+ window_flags |= MENU_TRANSIENT_WINDOW;
+ }
TICK_N("Grab keyboard");
__create_window(window_flags);
TICK_N("Create Window");
diff --git a/source/view.c b/source/view.c
index aac8c22e..9e9f1661 100644
--- a/source/view.c
+++ b/source/view.c
@@ -1120,7 +1120,25 @@ void __create_window(MenuFlags menu_flags) {
TICK_N("textbox setup");
// // make it an unmanaged window
- if (((menu_flags & MENU_NORMAL_WINDOW) == 0)) {
+ if (((menu_flags & MENU_TRANSIENT_WINDOW) != 0)) {
+ xcb_atom_t atoms[] = {xcb->ewmh._NET_WM_STATE_MODAL};
+
+ window_set_atom_prop(box_window, xcb->ewmh._NET_WM_STATE, atoms,
+ sizeof(atoms) / sizeof(xcb_atom_t));
+ window_set_atom_prop (box_window, xcb->ewmh._NET_WM_WINDOW_TYPE,
+ &(xcb->ewmh._NET_WM_WINDOW_TYPE_UTILITY), 1);
+ x11_disable_decoration(box_window);
+
+ xcb_window_t active_window;
+ xcb_get_property_cookie_t awc;
+ awc = xcb_ewmh_get_active_window (&xcb->ewmh, xcb->screen_nbr);
+
+ if (xcb_ewmh_get_active_window_reply(&xcb->ewmh, awc, &active_window, NULL)) {
+ xcb_change_property(xcb->connection, XCB_PROP_MODE_REPLACE, box_window,
+ XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 32,
+ 1, &active_window);
+ }
+ } else if (((menu_flags & MENU_NORMAL_WINDOW) == 0)) {
window_set_atom_prop(box_window, xcb->ewmh._NET_WM_STATE,
&(xcb->ewmh._NET_WM_STATE_ABOVE), 1);
uint32_t values[] = {1};
@@ -1353,6 +1371,11 @@ static void update_callback(textbox *t, icon *ico, unsigned int index,
textbox_font(t, *type);
}
}
+static void page_changed_callback()
+{
+ rofi_view_workers_finalize();
+ rofi_view_workers_initialize();
+}
void rofi_view_update(RofiViewState *state, gboolean qr) {
if (!widget_need_redraw(WIDGET(state->main_window))) {
@@ -2354,7 +2377,8 @@ static void rofi_view_add_widget(RofiViewState *state, widget *parent_widget,
return;
}
state->list_view = listview_create(parent_widget, name, update_callback,
- state, config.element_height, 0);
+ page_changed_callback, state,
+ config.element_height, 0);
listview_set_select