diff --git a/source/modes/recursivebrowser.c b/source/modes/recursivebrowser.c index 5a69bc31..a8b49450 100644 --- a/source/modes/recursivebrowser.c +++ b/source/modes/recursivebrowser.c @@ -103,6 +103,13 @@ typedef struct { FBFile *array; unsigned int array_length; unsigned int array_length_real; + + GThread *reading_thread; + GAsyncQueue *async_queue; + guint wake_source; + guint end_thread; + gboolean loading; + int pipefd2[2]; } FileBrowserModePrivateData; /** @@ -138,220 +145,14 @@ static void free_list(FileBrowserModePrivateData *pd) { #include #include -static gint compare_name(gconstpointer a, gconstpointer b, - G_GNUC_UNUSED gpointer data) { - FBFile *fa = (FBFile *)a; - FBFile *fb = (FBFile *)b; - - if (recursive_browser_config.directories_first && fa->type != fb->type) { - return fa->type - fb->type; - } - - return g_strcmp0(fa->name, fb->name); -} - -static gint compare_time(gconstpointer a, gconstpointer b, - G_GNUC_UNUSED gpointer data) { - FBFile *fa = (FBFile *)a; - FBFile *fb = (FBFile *)b; - - if (recursive_browser_config.directories_first && fa->type != fb->type) { - return fa->type - fb->type; - } - - if (fa->time < 0) { - return -1; - } - - if (fb->time < 0) { - return 1; - } - - return fb->time - fa->time; -} - -static gint compare(gconstpointer a, gconstpointer b, gpointer data) { - GCompareDataFunc comparator = NULL; - - switch (recursive_browser_config.sorting_method) { - case FB_SORT_NAME: - comparator = compare_name; - break; - case FB_SORT_TIME: - comparator = compare_time; - break; - default: - comparator = compare_name; - break; - } - - return comparator(a, b, data); -} - -static time_t get_time(const GStatBuf *statbuf) { - switch (recursive_browser_config.sorting_time) { - case FB_MTIME: - return statbuf->st_mtim.tv_sec; - case FB_ATIME: - return statbuf->st_atim.tv_sec; - case FB_CTIME: - return statbuf->st_ctim.tv_sec; - default: - return 0; - } -} - -static void set_time(FBFile *file) { - // GError *error = NULL; - // gchar *path = g_filename_from_utf8(file->path, -1, NULL, NULL, &error); - // if (error) { - // g_warning("Failed to convert filename: %s: %s", file->path, - // error->message); g_error_free(error); return; - // } - - GStatBuf statbuf; - - if (g_lstat(file->path, &statbuf) == 0) { - file->time = get_time(&statbuf); - } else { - g_warning("Failed to stat file: %s, %s", file->path, strerror(errno)); - } - - // g_free(path); -} - inline static void fb_resize_array(FileBrowserModePrivateData *pd) { if ((pd->array_length + 1) > pd->array_length_real) { - pd->array_length_real += 1024; + pd->array_length_real += 10240; pd->array = g_realloc(pd->array, (pd->array_length_real + 1) * sizeof(FBFile)); } } -static void get_recursive_browser(Mode *sw, GFile *scan_dir) { - FileBrowserModePrivateData *pd = - (FileBrowserModePrivateData *)mode_get_private_data(sw); - /** - * Get the entries to display. - * this gets called on plugin initialization. - */ - char *cdir = g_file_get_path(scan_dir); - DIR *dir = opendir(cdir); - if (dir) { - struct dirent *rd = NULL; - while ((rd = readdir(dir)) != NULL) { - if (g_strcmp0(rd->d_name, "..") == 0) { - continue; - //fb_resize_array(pd); - //// Rofi expects utf-8, so lets convert the filename. - //pd->array[pd->array_length].name = g_strdup(".."); - //pd->array[pd->array_length].path = NULL; - //pd->array[pd->array_length].type = UP; - //pd->array[pd->array_length].icon_fetch_uid = 0; - //pd->array[pd->array_length].icon_fetch_size = 0; - //pd->array[pd->array_length].link = FALSE; - //pd->array[pd->array_length].time = -1; - //pd->array_length++; - } - if (g_strcmp0(rd->d_name, ".") == 0) { - continue; - } - if (rd->d_name[0] == '.' && recursive_browser_config.show_hidden == FALSE) { - 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: - fb_resize_array(pd); - // Rofi expects utf-8, so lets convert the filename. - pd->array[pd->array_length].path = - g_build_filename(cdir, rd->d_name, NULL); - pd->array[pd->array_length].name = - g_filename_to_utf8(pd->array[pd->array_length].path, -1, NULL, NULL, NULL); - if (pd->array[pd->array_length].name == NULL) { - pd->array[pd->array_length].name = rofi_force_utf8(rd->d_name, -1); - } - pd->array[pd->array_length].type = - (rd->d_type == DT_DIR) ? DIRECTORY : RFILE; - pd->array[pd->array_length].icon_fetch_uid = 0; - pd->array[pd->array_length].icon_fetch_size = 0; - pd->array[pd->array_length].link = FALSE; - - if (recursive_browser_config.sorting_method == FB_SORT_TIME) { - set_time(&pd->array[pd->array_length]); - } - - pd->array_length++; - break; - case DT_DIR: - - char *d = g_build_filename(cdir, rd->d_name, NULL); - GFile *dirp = g_file_new_for_path(d); - get_recursive_browser(sw,dirp); - g_object_unref(dirp); - g_free(d); - break; - case DT_LNK: - fb_resize_array(pd); - // Rofi expects utf-8, so lets convert the filename. - pd->array[pd->array_length].name = - g_filename_to_utf8(rd->d_name, -1, NULL, NULL, NULL); - if (pd->array[pd->array_length].name == NULL) { - pd->array[pd->array_length].name = rofi_force_utf8(rd->d_name, -1); - } - pd->array[pd->array_length].path = - g_build_filename(cdir, rd->d_name, NULL); - pd->array[pd->array_length].icon_fetch_uid = 0; - pd->array[pd->array_length].icon_fetch_size = 0; - pd->array[pd->array_length].link = TRUE; - // Default to file. - pd->array[pd->array_length].type = RFILE; - if (0){ - // 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); - if (pd->array[pd->array_length].path) { - GStatBuf statbuf; - if (g_stat(pd->array[pd->array_length].path, &statbuf) == 0) { - if (S_ISDIR(statbuf.st_mode)) { - pd->array[pd->array_length].type = DIRECTORY; - } else if (S_ISREG(statbuf.st_mode)) { - pd->array[pd->array_length].type = RFILE; - } - - if (recursive_browser_config.sorting_method == FB_SORT_TIME) { - pd->array[pd->array_length].time = get_time(&statbuf); - } - } else { - g_warning("Failed to stat file: %s, %s", - pd->array[pd->array_length].path, strerror(errno)); - } - - // g_free(file); - } - } - pd->array_length++; - break; - } - } - closedir(dir); - } - g_free(cdir); - //g_qsort_with_data(pd->array, pd->array_length, sizeof(FBFile), compare, NULL); -} - static void recursive_browser_mode_init_config(Mode *sw) { FileBrowserModePrivateData *pd = (FileBrowserModePrivateData *)mode_get_private_data(sw); @@ -376,8 +177,8 @@ static void recursive_browser_mode_init_config(Mode *sw) { } else { found_error = TRUE; - msg = g_strdup_printf("\"%s\" is not a valid recursivebrowser sorting method", - p->value.s); + msg = g_strdup_printf( + "\"%s\" is not a valid recursivebrowser sorting method", p->value.s); } } @@ -440,6 +241,127 @@ 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 (rd->d_name[0] == '.') { + 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->name = g_filename_to_utf8(rd->d_name, -1, NULL, NULL, NULL); + if (f->name == NULL) { + f->name = rofi_force_utf8(rd->d_name, -1); + } + f->path = g_build_filename(cdir, rd->d_name, NULL); + 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); + } + + g_free(cdir); +} +static gpointer recursive_browser_input_thread(gpointer userdata) { + FileBrowserModePrivateData *pd = (FileBrowserModePrivateData *)userdata; + printf("Start scan.\n"); + scan_dir(pd, pd->current_dir); + write(pd->pipefd2[1], "r", 1); + write(pd->pipefd2[1], "q", 1); + printf("End scan.\n"); + return NULL; +} +static gboolean recursive_browser_async_read_proc(gint fd, + GIOCondition condition, + gpointer user_data) { + FileBrowserModePrivateData *pd = (FileBrowserModePrivateData *)user_data; + char command; + // Only interrested in read events. + if ((condition & G_IO_IN) != G_IO_IN) { + return G_SOURCE_CONTINUE; + } + // Read the entry from the pipe that was used to signal this action. + if (read(fd, &command, 1) == 1) { + if (command == 'r') { + FBFile *block = NULL; + gboolean changed = FALSE; + // Empty out the AsyncQueue (that is thread safe) from all blocks pushed + // into it. + while ((block = g_async_queue_try_pop(pd->async_queue)) != NULL) { + + fb_resize_array(pd); + pd->array[pd->array_length] = *block; + pd->array_length++; + g_free(block); + changed = TRUE; + } + if (changed) { + rofi_view_reload(); + } + } else if (command == 'q') { + if (pd->loading) { + rofi_view_set_overlay(rofi_view_get_active(), NULL); + } + } + } + return G_SOURCE_CONTINUE; +} + static int recursive_browser_mode_init(Mode *sw) { /** * Called on startup when enabled (in modes list) @@ -452,7 +374,18 @@ static int recursive_browser_mode_init(Mode *sw) { recursive_browser_mode_init_current_dir(sw); // Load content. - get_recursive_browser(sw,pd->current_dir); + if (pipe(pd->pipefd2) == -1) { + g_error("Failed to create pipe"); + } + pd->wake_source = g_unix_fd_add(pd->pipefd2[0], G_IO_IN, + recursive_browser_async_read_proc, pd); + + // Create the message passing queue to the UI thread. + pd->async_queue = g_async_queue_new(); + pd->end_thread = FALSE; + pd->reading_thread = g_thread_new( + "dmenu-read", (GThreadFunc)recursive_browser_input_thread, pd); + pd->loading = TRUE; } return TRUE; } @@ -463,7 +396,7 @@ static unsigned int recursive_browser_mode_get_num_entries(const Mode *sw) { } static ModeMode recursive_browser_mode_result(Mode *sw, int mretv, char **input, - unsigned int selected_line) { + unsigned int selected_line) { ModeMode retv = MODE_EXIT; FileBrowserModePrivateData *pd = (FileBrowserModePrivateData *)mode_get_private_data(sw); @@ -489,18 +422,7 @@ static ModeMode recursive_browser_mode_result(Mode *sw, int mretv, char **input, retv = (mretv & MENU_LOWER_MASK); } else if ((mretv & MENU_OK)) { if (selected_line < pd->array_length) { - if (pd->array[selected_line].type == UP) { - GFile *new = g_file_get_parent(pd->current_dir); - if (new) { - g_object_unref(pd->current_dir); - pd->current_dir = new; - free_list(pd); - get_recursive_browser(sw,pd->current_dir); - return RESET_DIALOG; - } - } else if ((pd->array[selected_line].type == RFILE) || - (pd->array[selected_line].type == DIRECTORY && - special_command)) { + if (pd->array[selected_line].type == RFILE) { char *d_esc = g_shell_quote(pd->array[selected_line].path); char *cmd = g_strdup_printf("%s %s", pd->command, d_esc); g_free(d_esc); @@ -509,51 +431,12 @@ static ModeMode recursive_browser_mode_result(Mode *sw, int mretv, char **input, g_free(cdir); g_free(cmd); return MODE_EXIT; - } else if (pd->array[selected_line].type == DIRECTORY) { - char *path = g_build_filename(cache_dir, FILEBROWSER_CACHE_FILE, NULL); - g_file_set_contents(path, pd->array[selected_line].path, -1, NULL); - g_free(path); - GFile *new = g_file_new_for_path(pd->array[selected_line].path); - g_object_unref(pd->current_dir); - pd->current_dir = new; - free_list(pd); - get_recursive_browser(sw,pd->current_dir); - return RESET_DIALOG; } } retv = RELOAD_DIALOG; } else if ((mretv & MENU_CUSTOM_INPUT)) { - if (special_command) { - GFile *new = g_file_get_parent(pd->current_dir); - if (new) { - g_object_unref(pd->current_dir); - pd->current_dir = new; - free_list(pd); - get_recursive_browser(sw,pd->current_dir); - } - return RESET_DIALOG; - } - if (*input) { - char *p = rofi_expand_path(*input); - char *dir = g_filename_from_utf8(p, -1, NULL, NULL, NULL); - g_free(p); - if (g_file_test(dir, G_FILE_TEST_EXISTS)) { - if (g_file_test(dir, G_FILE_TEST_IS_DIR)) { - g_object_unref(pd->current_dir); - pd->current_dir = g_file_new_for_path(dir); - g_free(dir); - free_list(pd); - get_recursive_browser(sw,pd->current_dir); - return RESET_DIALOG; - } - } - g_free(dir); - retv = RELOAD_DIALOG; - } + retv = RELOAD_DIALOG; } else if ((mretv & MENU_ENTRY_DELETE) == MENU_ENTRY_DELETE) { - recursive_browser_config.show_hidden = !recursive_browser_config.show_hidden; - free_list(pd); - get_recursive_browser(sw,pd->current_dir); retv = RELOAD_DIALOG; } return retv; @@ -563,6 +446,10 @@ static void recursive_browser_mode_destroy(Mode *sw) { FileBrowserModePrivateData *pd = (FileBrowserModePrivateData *)mode_get_private_data(sw); if (pd != NULL) { + if (pd->reading_thread) { + pd->end_thread = TRUE; + g_thread_join(pd->reading_thread); + } g_object_unref(pd->current_dir); g_free(pd->command); free_list(pd); @@ -600,8 +487,9 @@ static char *_get_display_value(const Mode *sw, unsigned int selected_line, * * @returns try when a match. */ -static int recursive_browser_token_match(const Mode *sw, rofi_int_matcher **tokens, - unsigned int index) { +static int recursive_browser_token_match(const Mode *sw, + rofi_int_matcher **tokens, + unsigned int index) { FileBrowserModePrivateData *pd = (FileBrowserModePrivateData *)mode_get_private_data(sw); @@ -621,7 +509,8 @@ static cairo_surface_t *_get_icon(const Mode *sw, unsigned int selected_line, if (rofi_icon_fetcher_file_is_image(dr->path)) { dr->icon_fetch_uid = rofi_icon_fetcher_query(dr->path, height); } 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); @@ -657,9 +546,10 @@ Mode *create_new_recursive_browser(void) { return sw; } -#if 1 +#if 0 ModeMode recursive_browser_mode_completer(Mode *sw, int mretv, char **input, - unsigned int selected_line, char **path) { + unsigned int selected_line, + char **path) { ModeMode retv = MODE_EXIT; FileBrowserModePrivateData *pd = (FileBrowserModePrivateData *)mode_get_private_data(sw); @@ -677,7 +567,7 @@ ModeMode recursive_browser_mode_completer(Mode *sw, int mretv, char **input, g_object_unref(pd->current_dir); pd->current_dir = new; free_list(pd); - get_recursive_browser(sw,pd->current_dir); + get_recursive_browser(sw, pd->current_dir); return RESET_DIALOG; } } else if (pd->array[selected_line].type == DIRECTORY) { @@ -685,7 +575,7 @@ ModeMode recursive_browser_mode_completer(Mode *sw, int mretv, char **input, g_object_unref(pd->current_dir); pd->current_dir = new; free_list(pd); - get_recursive_browser(sw,pd->current_dir); + get_recursive_browser(sw, pd->current_dir); return RESET_DIALOG; } else if (pd->array[selected_line].type == RFILE) { *path = g_strescape(pd->array[selected_line].path, NULL); @@ -703,16 +593,17 @@ ModeMode recursive_browser_mode_completer(Mode *sw, int mretv, char **input, pd->current_dir = g_file_new_for_path(dir); g_free(dir); free_list(pd); - get_recursive_browser(sw,pd->current_dir); + get_recursive_browser(sw, pd->current_dir); return RESET_DIALOG; } } g_free(dir); retv = RELOAD_DIALOG; } else if ((mretv & MENU_ENTRY_DELETE) == MENU_ENTRY_DELETE) { - recursive_browser_config.show_hidden = !recursive_browser_config.show_hidden; + recursive_browser_config.show_hidden = + !recursive_browser_config.show_hidden; free_list(pd); - get_recursive_browser(sw,pd->current_dir); + get_recursive_browser(sw, pd->current_dir); retv = RELOAD_DIALOG; } return retv;