summaryrefslogtreecommitdiff
path: root/apps/fileop.c
diff options
context:
space:
mode:
authorChristian Soffke <christian.soffke@gmail.com>2024-07-30 19:12:33 +0200
committerChristian Soffke <christian.soffke@gmail.com>2024-08-11 13:18:50 -0400
commit48400b6ec112118b69123d2374a3bb7d8df78f89 (patch)
tree70fafdc76f0b5c91455afec84e672f3f9ce93295 /apps/fileop.c
parent28badc5703615d71dbffbdb6b4be499513727980 (diff)
downloadrockbox-48400b6ec112118b69123d2374a3bb7d8df78f89.tar.gz
rockbox-48400b6ec112118b69123d2374a3bb7d8df78f89.zip
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
Diffstat (limited to 'apps/fileop.c')
-rw-r--r--apps/fileop.c149
1 files 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 @@
44struct file_op_params 44struct file_op_params
45{ 45{
46 char path[MAX_PATH]; /* Buffer for full path */ 46 char path[MAX_PATH]; /* Buffer for full path */
47 const char* toplevel_name;
47 bool is_dir; 48 bool is_dir;
48 int objects; /* how many files and subdirectories*/ 49 int objects; /* how many files and subdirectories*/
49 int processed; 50 int processed;
51 unsigned long long total_size;
52 unsigned long long processed_size;
50 size_t append; /* Append position in 'path' for stack push */ 53 size_t append; /* Append position in 'path' for stack push */
51}; 54};
52 55
@@ -61,28 +64,52 @@ static int prompt_name(char* buf, size_t bufsz)
61 return FORC_UNKNOWN_FAILURE; 64 return FORC_UNKNOWN_FAILURE;
62} 65}
63 66
64static bool poll_cancel_action(const char *path, int operation, int current, int total) 67static bool poll_cancel_action(int operation, struct file_op_params *param)
65{ 68{
66 const char *op_str = ""; 69 static unsigned long last_tick;
67 70
68 clear_screen_buffer(false); 71 if (operation == FOC_COUNT)
69 72 {
70 if (operation == FOC_COPY) 73 if (param->objects <= 1)
71 op_str = str(LANG_COPYING); 74 last_tick = current_tick;
72 else if (operation == FOC_MOVE) 75 else if (TIME_AFTER(current_tick, last_tick + HZ/2))
73 op_str = str(LANG_MOVING); 76 {
74 else if (operation == FOC_COUNT) 77 clear_screen_buffer(false);
75 op_str = str(LANG_SCANNING_DISK); 78 splashf(0, "%s (%d)", str(LANG_SCANNING_DISK), param->objects);
76 else if (operation == FOC_DELETE) 79 last_tick = current_tick;
77 op_str = str(LANG_DELETING); 80 }
78 81 }
79 path_basename(path, &path);
80
81 if (total <= 0)
82 splashf(0, "%s (%d) %s", op_str, current, path);
83 else 82 else
84 splash_progress(current, total, "%s %s", op_str, path); 83 {
84 const char *op_str = (operation == FOC_DELETE) ? str(LANG_DELETING) :
85 (operation == FOC_COPY) ? str(LANG_COPYING) :
86 str(LANG_MOVING);
85 87
88 if ((operation == FOC_DELETE || !param->total_size) &&
89 param->objects > 0)
90 {
91 splash_progress(param->processed, param->objects,
92 "%s %s", op_str, param->toplevel_name);
93 }
94 else if (param->total_size >= 10 * 1024 * 1024)
95 {
96 int total_shft = (int) (param->total_size >> 15);
97 int current_shft = (int) (param->processed_size >> 15);
98 splash_progress(current_shft, total_shft,
99 "%s %s (%d MiB)\n%d MiB",
100 op_str, param->toplevel_name,
101 total_shft >> 5, current_shft >> 5);
102 }
103 else if (param->total_size >= 1024)
104 {
105 int total_kib = (int) (param->total_size >> 10);
106 int current_kib = (int) (param->processed_size >> 10);
107 splash_progress(current_kib, total_kib,
108 "%s %s (%d KiB)\n%d KiB",
109 op_str, param->toplevel_name,
110 total_kib, current_kib);
111 }
112 }
86 return ACTION_STD_CANCEL == get_action(CONTEXT_STD, TIMEOUT_NOBLOCK); 113 return ACTION_STD_CANCEL == get_action(CONTEXT_STD, TIMEOUT_NOBLOCK);
87} 114}
88 115
@@ -94,13 +121,20 @@ static void init_file_op(struct file_op_params *param,
94 if (selected_file == NULL) 121 if (selected_file == NULL)
95 { 122 {
96 param->append = strlcpy(param->path, basename, sizeof (param->path)); 123 param->append = strlcpy(param->path, basename, sizeof (param->path));
124 path_basename(basename, &basename);
125 param->toplevel_name = basename;
97 } 126 }
98 else 127 else
128 {
99 param->append = path_append(param->path, basename, 129 param->append = path_append(param->path, basename,
100 selected_file, sizeof (param->path)); 130 selected_file, sizeof (param->path));
131 param->toplevel_name = selected_file;
132 }
101 param->is_dir = dir_exists(param->path); 133 param->is_dir = dir_exists(param->path);
102 param->objects = 0; /* how many files and subdirectories*/ 134 param->objects = 0; /* how many files and subdirectories*/
103 param->processed = 0; 135 param->processed = 0;
136 param->total_size = 0;
137 param->processed_size = 0;
104} 138}
105 139
106/* counts file objects, deletes file objects */ 140/* counts file objects, deletes file objects */
@@ -156,7 +190,7 @@ static int directory_fileop(struct file_op_params *param, enum file_op_current f
156 rc = directory_fileop(param, fileop); /* recursion */ 190 rc = directory_fileop(param, fileop); /* recursion */
157 } else { 191 } else {
158 /* remove a file */ 192 /* remove a file */
159 if (poll_cancel_action(param->path, FOC_DELETE, param->processed, param->objects)) 193 if (poll_cancel_action(FOC_DELETE, param))
160 { 194 {
161 rc = FORC_CANCELLED; 195 rc = FORC_CANCELLED;
162 break; 196 break;
@@ -167,6 +201,13 @@ static int directory_fileop(struct file_op_params *param, enum file_op_current f
167 else /* count objects */ 201 else /* count objects */
168 { 202 {
169 param->objects++; 203 param->objects++;
204 param->total_size += info.size;
205
206 if (poll_cancel_action(FOC_COUNT, param))
207 {
208 rc = FORC_CANCELLED;
209 break;
210 }
170 211
171 if (param->append >= sizeof (param->path)) { 212 if (param->append >= sizeof (param->path)) {
172 rc = FORC_PATH_TOO_LONG; 213 rc = FORC_PATH_TOO_LONG;
@@ -176,12 +217,6 @@ static int directory_fileop(struct file_op_params *param, enum file_op_current f
176 if (info.attribute & ATTR_DIRECTORY) { 217 if (info.attribute & ATTR_DIRECTORY) {
177 /* enter subdirectory */ 218 /* enter subdirectory */
178 rc = directory_fileop(param, FOC_COUNT); /* recursion */ 219 rc = directory_fileop(param, FOC_COUNT); /* recursion */
179 } else {
180 if (poll_cancel_action(param->path, FOC_COUNT, param->objects, 0))
181 {
182 rc = FORC_CANCELLED;
183 break;
184 }
185 } 220 }
186 } 221 }
187 param->append = append; /* other functions may use param, reset append */ 222 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
193 228
194 if (fileop == FOC_DELETE && rc == FORC_SUCCESS) { 229 if (fileop == FOC_DELETE && rc == FORC_SUCCESS) {
195 /* remove the now empty directory */ 230 /* remove the now empty directory */
196 if (poll_cancel_action(param->path, FOC_DELETE, param->processed, param->objects)) 231 if (poll_cancel_action(FOC_DELETE, param))
197 { 232 {
198 rc = FORC_CANCELLED; 233 rc = FORC_CANCELLED;
199 } else { 234 } else {
@@ -216,7 +251,7 @@ static int check_count_fileobjects(struct file_op_params *param)
216} 251}
217 252
218/* Attempt to just rename a file or directory */ 253/* Attempt to just rename a file or directory */
219static int move_by_rename(const char *src_path, 254static int move_by_rename(struct file_op_params *src,
220 const char *dst_path, 255 const char *dst_path,
221 unsigned int *pflags) 256 unsigned int *pflags)
222{ 257{
@@ -225,19 +260,15 @@ static int move_by_rename(const char *src_path,
225 if (!(flags & (PASTE_COPY | PASTE_EXDEV))) { 260 if (!(flags & (PASTE_COPY | PASTE_EXDEV))) {
226 if ((flags & PASTE_OVERWRITE) || !file_exists(dst_path)) { 261 if ((flags & PASTE_OVERWRITE) || !file_exists(dst_path)) {
227 /* Just try to move the directory / file */ 262 /* Just try to move the directory / file */
228 if (poll_cancel_action(src_path, FOC_MOVE, 0 , 0)) { 263 rc = rename(src->path, dst_path);
229 rc = FORC_CANCELLED;
230 } else {
231 rc = rename(src_path, dst_path);
232#ifdef HAVE_MULTIVOLUME 264#ifdef HAVE_MULTIVOLUME
233 if (rc < FORC_SUCCESS && errno == EXDEV) { 265 if (rc < FORC_SUCCESS && errno == EXDEV) {
234 /* Failed because cross volume rename doesn't work */ 266 /* Failed because cross volume rename doesn't work */
235 *pflags |= PASTE_EXDEV; /* force a move instead */ 267 *pflags |= PASTE_EXDEV; /* force a move instead */
236 } 268 }
237#endif /* HAVE_MULTIVOLUME */ 269#endif /* HAVE_MULTIVOLUME */
238 /* if (errno == ENOTEMPTY && (flags & PASTE_OVERWRITE)) { 270 /* if (errno == ENOTEMPTY && (flags & PASTE_OVERWRITE)) {
239 * Directory is not empty thus rename() will not do a quick overwrite */ 271 * Directory is not empty thus rename() will not do a quick overwrite */
240 }
241 } 272 }
242 273
243 } 274 }
@@ -245,12 +276,16 @@ static int move_by_rename(const char *src_path,
245} 276}
246 277
247/* Paste a file */ 278/* Paste a file */
248static int copy_move_file(const char *src_path, const char *dst_path, unsigned int flags) 279static int copy_move_file(struct file_op_params *src, const char *dst_path,
280 unsigned int flags)
249{ 281{
250 /* Try renaming first */ 282 /* Try renaming first */
251 int rc = move_by_rename(src_path, dst_path, &flags); 283 int rc = move_by_rename(src, dst_path, &flags);
252 if (rc == FORC_SUCCESS) 284 if (rc == FORC_SUCCESS)
285 {
286 src->total_size = 0; /* switch from counting size to number of items */
253 return rc; 287 return rc;
288 }
254 289
255 /* See if we can get the plugin buffer for the file copy buffer */ 290 /* See if we can get the plugin buffer for the file copy buffer */
256 size_t buffersize; 291 size_t buffersize;
@@ -268,9 +303,11 @@ static int copy_move_file(const char *src_path, const char *dst_path, unsigned i
268 303
269 buffersize &= ~0x1ff; /* Round buffer size to multiple of sector size */ 304 buffersize &= ~0x1ff; /* Round buffer size to multiple of sector size */
270 305
271 int src_fd = open(src_path, O_RDONLY); 306 int src_fd = open(src->path, O_RDONLY);
272 if (src_fd >= 0) { 307 if (src_fd >= 0) {
273 off_t src_sz = lseek(src_fd, 0, SEEK_END); 308 off_t src_sz = lseek(src_fd, 0, SEEK_END);
309 if (!src->total_size && !src->processed) /* single file copy */
310 src->total_size = src_sz;
274 lseek(src_fd, 0, SEEK_SET); 311 lseek(src_fd, 0, SEEK_SET);
275 312
276 int oflag = O_WRONLY|O_CREAT; 313 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
289 while (rc == FORC_SUCCESS) { 326 while (rc == FORC_SUCCESS) {
290 if (total_size >= next_cancel_test) { 327 if (total_size >= next_cancel_test) {
291 next_cancel_test = total_size + 0x10000; 328 next_cancel_test = total_size + 0x10000;
292 if (poll_cancel_action(src_path, 329 if (poll_cancel_action(!(flags & PASTE_COPY) ?
293 !(flags & PASTE_COPY) ? FOC_MOVE : FOC_COPY, 330 FOC_MOVE : FOC_COPY, src))
294 total_size, src_sz))
295 { 331 {
296 rc = FORC_CANCELLED; 332 rc = FORC_CANCELLED;
297 break; 333 break;
@@ -315,6 +351,7 @@ static int copy_move_file(const char *src_path, const char *dst_path, unsigned i
315 } 351 }
316 352
317 total_size += byteswritten; 353 total_size += byteswritten;
354 src->processed_size += byteswritten;
318 355
319 if (bytesread < (ssize_t)buffersize) { 356 if (bytesread < (ssize_t)buffersize) {
320 /* EOF with trailing bytes */ 357 /* EOF with trailing bytes */
@@ -344,7 +381,7 @@ static int copy_move_file(const char *src_path, const char *dst_path, unsigned i
344 381
345 if (rc == FORC_SUCCESS && !(flags & PASTE_COPY)) { 382 if (rc == FORC_SUCCESS && !(flags & PASTE_COPY)) {
346 /* Remove the source file */ 383 /* Remove the source file */
347 rc = remove(src_path) * 10; 384 rc = remove(src->path) * 10;
348 } 385 }
349 386
350 return rc; 387 return rc;
@@ -408,9 +445,8 @@ static int copy_move_directory(struct file_op_params *src,
408 break; 445 break;
409 } 446 }
410 447
411 if (poll_cancel_action(src->path, 448 if (poll_cancel_action(!(flags & PASTE_COPY) ?
412 !(flags & PASTE_COPY) ? FOC_MOVE : FOC_COPY, 449 FOC_MOVE : FOC_COPY, src))
413 src->processed, src->objects))
414 { 450 {
415 rc = FORC_CANCELLED; 451 rc = FORC_CANCELLED;
416 break; 452 break;
@@ -419,11 +455,12 @@ static int copy_move_directory(struct file_op_params *src,
419 DEBUGF("Copy %s to %s\n", src->path, dst->path); 455 DEBUGF("Copy %s to %s\n", src->path, dst->path);
420 456
421 if (info.attribute & ATTR_DIRECTORY) { 457 if (info.attribute & ATTR_DIRECTORY) {
458 src->processed_size += info.size;
422 /* Copy/move a subdirectory */ 459 /* Copy/move a subdirectory */
423 rc = copy_move_directory(src, dst, flags); /* recursion */; 460 rc = copy_move_directory(src, dst, flags); /* recursion */;
424 } else { 461 } else {
425 /* Copy/move a file */ 462 /* Copy/move a file */
426 rc = copy_move_file(src->path, dst->path, flags); 463 rc = copy_move_file(src, dst->path, flags);
427 } 464 }
428 465
429 /* Remove basenames we added above */ 466 /* Remove basenames we added above */
@@ -484,7 +521,7 @@ int copy_move_fileobject(const char *src_path, const char *dst_path, unsigned in
484 if (src.is_dir) { 521 if (src.is_dir) {
485 /* Copy or move a subdirectory */ 522 /* Copy or move a subdirectory */
486 /* Try renaming first */ 523 /* Try renaming first */
487 rc = move_by_rename(src.path, dst.path, &flags); 524 rc = move_by_rename(&src, dst.path, &flags);
488 if (rc < FORC_SUCCESS) { 525 if (rc < FORC_SUCCESS) {
489 rc = check_count_fileobjects(&src); 526 rc = check_count_fileobjects(&src);
490 if (rc == FORC_SUCCESS) { 527 if (rc == FORC_SUCCESS) {
@@ -493,7 +530,7 @@ int copy_move_fileobject(const char *src_path, const char *dst_path, unsigned in
493 } 530 }
494 } else { 531 } else {
495 /* Copy or move a file */ 532 /* Copy or move a file */
496 rc = copy_move_file(src_path, dst.path, flags); 533 rc = copy_move_file(&src, dst.path, flags);
497 } 534 }
498 535
499 cpu_boost(false); 536 cpu_boost(false);
@@ -533,6 +570,12 @@ int delete_fileobject(const char *selected_file)
533 if (param.append >= sizeof (param.path)) 570 if (param.append >= sizeof (param.path))
534 return FORC_PATH_TOO_LONG; 571 return FORC_PATH_TOO_LONG;
535 572
573 /* Note: delete_fileobject() will happily delete whatever
574 * path is passed (after confirmation) */
575 if (confirm_delete_yesno(param.path) != YESNO_YES) {
576 return FORC_CANCELLED;
577 }
578
536 if (param.is_dir) { 579 if (param.is_dir) {
537 int rc = check_count_fileobjects(&param); 580 int rc = check_count_fileobjects(&param);
538 DEBUGF("%s res: %d, ct: %d, %s", __func__, rc, param.objects, param.path); 581 DEBUGF("%s res: %d, ct: %d, %s", __func__, rc, param.objects, param.path);
@@ -540,15 +583,7 @@ int delete_fileobject(const char *selected_file)
540 return rc; 583 return rc;
541 } 584 }
542 585
543 /* Note: delete_fileobject() will happily delete whatever
544 * path is passed (after confirmation) */
545 if (confirm_delete_yesno(param.path) != YESNO_YES) {
546 return FORC_CANCELLED;
547 }
548
549 clear_screen_buffer(true); 586 clear_screen_buffer(true);
550 if (poll_cancel_action(param.path, FOC_DELETE, param.processed, param.objects))
551 return FORC_CANCELLED;
552 587
553 if (param.is_dir) { /* if directory */ 588 if (param.is_dir) { /* if directory */
554 cpu_boost(true); 589 cpu_boost(true);