From 48400b6ec112118b69123d2374a3bb7d8df78f89 Mon Sep 17 00:00:00 2001 From: Christian Soffke Date: Tue, 30 Jul 2024 19:12:33 +0200 Subject: fileop: fix slow pre-scan & flashing progress bar Scanning files before a delete or copy operation was slowed down by splashf() being called too frequently. - Delay and update splashf every 0.5s (the scan will often be finished in a split second) - Only clear screen buffer before scan info is redrawn - Display name of *top* level item only, to prevent bar from jumping around due to the string length changing - Use item size for progress bar, so that the overall progress isn't interrupted by each file's progress - Display processed & total file sizes (KiB if total is < 10MiB, or MiB otherwise) - Move delete confirmation before scan to provide instant UI feedback Change-Id: I882a8db4161a46aca2d398e371ec4ed018fc8501 --- apps/fileop.c | 149 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 92 insertions(+), 57 deletions(-) diff --git a/apps/fileop.c b/apps/fileop.c index 35bd9f2241..1aaee3bb64 100644 --- a/apps/fileop.c +++ b/apps/fileop.c @@ -44,9 +44,12 @@ struct file_op_params { char path[MAX_PATH]; /* Buffer for full path */ + const char* toplevel_name; bool is_dir; int objects; /* how many files and subdirectories*/ int processed; + unsigned long long total_size; + unsigned long long processed_size; size_t append; /* Append position in 'path' for stack push */ }; @@ -61,28 +64,52 @@ static int prompt_name(char* buf, size_t bufsz) return FORC_UNKNOWN_FAILURE; } -static bool poll_cancel_action(const char *path, int operation, int current, int total) +static bool poll_cancel_action(int operation, struct file_op_params *param) { - const char *op_str = ""; + static unsigned long last_tick; - clear_screen_buffer(false); - - if (operation == FOC_COPY) - op_str = str(LANG_COPYING); - else if (operation == FOC_MOVE) - op_str = str(LANG_MOVING); - else if (operation == FOC_COUNT) - op_str = str(LANG_SCANNING_DISK); - else if (operation == FOC_DELETE) - op_str = str(LANG_DELETING); - - path_basename(path, &path); - - if (total <= 0) - splashf(0, "%s (%d) %s", op_str, current, path); + if (operation == FOC_COUNT) + { + if (param->objects <= 1) + last_tick = current_tick; + else if (TIME_AFTER(current_tick, last_tick + HZ/2)) + { + clear_screen_buffer(false); + splashf(0, "%s (%d)", str(LANG_SCANNING_DISK), param->objects); + last_tick = current_tick; + } + } else - splash_progress(current, total, "%s %s", op_str, path); + { + const char *op_str = (operation == FOC_DELETE) ? str(LANG_DELETING) : + (operation == FOC_COPY) ? str(LANG_COPYING) : + str(LANG_MOVING); + if ((operation == FOC_DELETE || !param->total_size) && + param->objects > 0) + { + splash_progress(param->processed, param->objects, + "%s %s", op_str, param->toplevel_name); + } + else if (param->total_size >= 10 * 1024 * 1024) + { + int total_shft = (int) (param->total_size >> 15); + int current_shft = (int) (param->processed_size >> 15); + splash_progress(current_shft, total_shft, + "%s %s (%d MiB)\n%d MiB", + op_str, param->toplevel_name, + total_shft >> 5, current_shft >> 5); + } + else if (param->total_size >= 1024) + { + int total_kib = (int) (param->total_size >> 10); + int current_kib = (int) (param->processed_size >> 10); + splash_progress(current_kib, total_kib, + "%s %s (%d KiB)\n%d KiB", + op_str, param->toplevel_name, + total_kib, current_kib); + } + } return ACTION_STD_CANCEL == get_action(CONTEXT_STD, TIMEOUT_NOBLOCK); } @@ -94,13 +121,20 @@ static void init_file_op(struct file_op_params *param, if (selected_file == NULL) { param->append = strlcpy(param->path, basename, sizeof (param->path)); + path_basename(basename, &basename); + param->toplevel_name = basename; } else + { param->append = path_append(param->path, basename, selected_file, sizeof (param->path)); + param->toplevel_name = selected_file; + } param->is_dir = dir_exists(param->path); param->objects = 0; /* how many files and subdirectories*/ param->processed = 0; + param->total_size = 0; + param->processed_size = 0; } /* counts file objects, deletes file objects */ @@ -156,7 +190,7 @@ static int directory_fileop(struct file_op_params *param, enum file_op_current f rc = directory_fileop(param, fileop); /* recursion */ } else { /* remove a file */ - if (poll_cancel_action(param->path, FOC_DELETE, param->processed, param->objects)) + if (poll_cancel_action(FOC_DELETE, param)) { rc = FORC_CANCELLED; break; @@ -167,6 +201,13 @@ static int directory_fileop(struct file_op_params *param, enum file_op_current f else /* count objects */ { param->objects++; + param->total_size += info.size; + + if (poll_cancel_action(FOC_COUNT, param)) + { + rc = FORC_CANCELLED; + break; + } if (param->append >= sizeof (param->path)) { rc = FORC_PATH_TOO_LONG; @@ -176,12 +217,6 @@ static int directory_fileop(struct file_op_params *param, enum file_op_current f if (info.attribute & ATTR_DIRECTORY) { /* enter subdirectory */ rc = directory_fileop(param, FOC_COUNT); /* recursion */ - } else { - if (poll_cancel_action(param->path, FOC_COUNT, param->objects, 0)) - { - rc = FORC_CANCELLED; - break; - } } } param->append = append; /* other functions may use param, reset append */ @@ -193,7 +228,7 @@ static int directory_fileop(struct file_op_params *param, enum file_op_current f if (fileop == FOC_DELETE && rc == FORC_SUCCESS) { /* remove the now empty directory */ - if (poll_cancel_action(param->path, FOC_DELETE, param->processed, param->objects)) + if (poll_cancel_action(FOC_DELETE, param)) { rc = FORC_CANCELLED; } else { @@ -216,7 +251,7 @@ static int check_count_fileobjects(struct file_op_params *param) } /* Attempt to just rename a file or directory */ -static int move_by_rename(const char *src_path, +static int move_by_rename(struct file_op_params *src, const char *dst_path, unsigned int *pflags) { @@ -225,19 +260,15 @@ static int move_by_rename(const char *src_path, if (!(flags & (PASTE_COPY | PASTE_EXDEV))) { if ((flags & PASTE_OVERWRITE) || !file_exists(dst_path)) { /* Just try to move the directory / file */ - if (poll_cancel_action(src_path, FOC_MOVE, 0 , 0)) { - rc = FORC_CANCELLED; - } else { - rc = rename(src_path, dst_path); + rc = rename(src->path, dst_path); #ifdef HAVE_MULTIVOLUME - if (rc < FORC_SUCCESS && errno == EXDEV) { - /* Failed because cross volume rename doesn't work */ - *pflags |= PASTE_EXDEV; /* force a move instead */ - } + if (rc < FORC_SUCCESS && errno == EXDEV) { + /* Failed because cross volume rename doesn't work */ + *pflags |= PASTE_EXDEV; /* force a move instead */ + } #endif /* HAVE_MULTIVOLUME */ /* if (errno == ENOTEMPTY && (flags & PASTE_OVERWRITE)) { * Directory is not empty thus rename() will not do a quick overwrite */ - } } } @@ -245,12 +276,16 @@ static int move_by_rename(const char *src_path, } /* Paste a file */ -static int copy_move_file(const char *src_path, const char *dst_path, unsigned int flags) +static int copy_move_file(struct file_op_params *src, const char *dst_path, + unsigned int flags) { /* Try renaming first */ - int rc = move_by_rename(src_path, dst_path, &flags); + int rc = move_by_rename(src, dst_path, &flags); if (rc == FORC_SUCCESS) + { + src->total_size = 0; /* switch from counting size to number of items */ return rc; + } /* See if we can get the plugin buffer for the file copy buffer */ size_t buffersize; @@ -268,9 +303,11 @@ static int copy_move_file(const char *src_path, const char *dst_path, unsigned i buffersize &= ~0x1ff; /* Round buffer size to multiple of sector size */ - int src_fd = open(src_path, O_RDONLY); + int src_fd = open(src->path, O_RDONLY); if (src_fd >= 0) { off_t src_sz = lseek(src_fd, 0, SEEK_END); + if (!src->total_size && !src->processed) /* single file copy */ + src->total_size = src_sz; lseek(src_fd, 0, SEEK_SET); int oflag = O_WRONLY|O_CREAT; @@ -289,9 +326,8 @@ static int copy_move_file(const char *src_path, const char *dst_path, unsigned i while (rc == FORC_SUCCESS) { if (total_size >= next_cancel_test) { next_cancel_test = total_size + 0x10000; - if (poll_cancel_action(src_path, - !(flags & PASTE_COPY) ? FOC_MOVE : FOC_COPY, - total_size, src_sz)) + if (poll_cancel_action(!(flags & PASTE_COPY) ? + FOC_MOVE : FOC_COPY, src)) { rc = FORC_CANCELLED; break; @@ -315,6 +351,7 @@ static int copy_move_file(const char *src_path, const char *dst_path, unsigned i } total_size += byteswritten; + src->processed_size += byteswritten; if (bytesread < (ssize_t)buffersize) { /* EOF with trailing bytes */ @@ -344,7 +381,7 @@ static int copy_move_file(const char *src_path, const char *dst_path, unsigned i if (rc == FORC_SUCCESS && !(flags & PASTE_COPY)) { /* Remove the source file */ - rc = remove(src_path) * 10; + rc = remove(src->path) * 10; } return rc; @@ -408,9 +445,8 @@ static int copy_move_directory(struct file_op_params *src, break; } - if (poll_cancel_action(src->path, - !(flags & PASTE_COPY) ? FOC_MOVE : FOC_COPY, - src->processed, src->objects)) + if (poll_cancel_action(!(flags & PASTE_COPY) ? + FOC_MOVE : FOC_COPY, src)) { rc = FORC_CANCELLED; break; @@ -419,11 +455,12 @@ static int copy_move_directory(struct file_op_params *src, DEBUGF("Copy %s to %s\n", src->path, dst->path); if (info.attribute & ATTR_DIRECTORY) { + src->processed_size += info.size; /* Copy/move a subdirectory */ rc = copy_move_directory(src, dst, flags); /* recursion */; } else { /* Copy/move a file */ - rc = copy_move_file(src->path, dst->path, flags); + rc = copy_move_file(src, dst->path, flags); } /* Remove basenames we added above */ @@ -484,7 +521,7 @@ int copy_move_fileobject(const char *src_path, const char *dst_path, unsigned in if (src.is_dir) { /* Copy or move a subdirectory */ /* Try renaming first */ - rc = move_by_rename(src.path, dst.path, &flags); + rc = move_by_rename(&src, dst.path, &flags); if (rc < FORC_SUCCESS) { rc = check_count_fileobjects(&src); if (rc == FORC_SUCCESS) { @@ -493,7 +530,7 @@ int copy_move_fileobject(const char *src_path, const char *dst_path, unsigned in } } else { /* Copy or move a file */ - rc = copy_move_file(src_path, dst.path, flags); + rc = copy_move_file(&src, dst.path, flags); } cpu_boost(false); @@ -533,6 +570,12 @@ int delete_fileobject(const char *selected_file) if (param.append >= sizeof (param.path)) return FORC_PATH_TOO_LONG; + /* Note: delete_fileobject() will happily delete whatever + * path is passed (after confirmation) */ + if (confirm_delete_yesno(param.path) != YESNO_YES) { + return FORC_CANCELLED; + } + if (param.is_dir) { int rc = check_count_fileobjects(¶m); DEBUGF("%s res: %d, ct: %d, %s", __func__, rc, param.objects, param.path); @@ -540,15 +583,7 @@ int delete_fileobject(const char *selected_file) return rc; } - /* Note: delete_fileobject() will happily delete whatever - * path is passed (after confirmation) */ - if (confirm_delete_yesno(param.path) != YESNO_YES) { - return FORC_CANCELLED; - } - clear_screen_buffer(true); - if (poll_cancel_action(param.path, FOC_DELETE, param.processed, param.objects)) - return FORC_CANCELLED; if (param.is_dir) { /* if directory */ cpu_boost(true); -- cgit v1.2.3