diff options
Diffstat (limited to 'source')
-rw-r--r-- | source/modes/dmenu.c | 3 | ||||
-rw-r--r-- | source/modes/filebrowser.c | 7 | ||||
-rw-r--r-- | source/modes/recursivebrowser.c | 212 | ||||
-rw-r--r-- | source/modes/script.c | 18 | ||||
-rw-r--r-- | source/rofi-icon-fetcher.c | 368 | ||||
-rw-r--r-- | source/rofi.c | 6 | ||||
-rw-r--r-- | source/view.c | 31 | ||||
-rw-r--r-- | source/widgets/listview.c | 57 | ||||
-rw-r--r-- | source/xcb.c | 63 | ||||
-rw-r--r-- | source/xrmoptions.c | 7 |
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 |