feature/file_browser #61
@@ -6,7 +6,7 @@ ENV DEBIAN_FRONTEND=noninteractive
|
|||||||
RUN dpkg --add-architecture i386
|
RUN dpkg --add-architecture i386
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
curl dos2unix wget git make nasm binutils xorriso grub-pc-bin gcc gcc-multilib \
|
curl dos2unix wget git make nasm binutils xorriso grub-pc-bin gcc gcc-multilib \
|
||||||
python3 python3-pip && \
|
python3 python3-pip python3-pil && \
|
||||||
apt-get clean my room
|
apt-get clean my room
|
||||||
|
|
||||||
RUN pip3 install --no-cache-dir --break-system-packages "mkdocs>=1.6,<2" mkdocs-material
|
RUN pip3 install --no-cache-dir --break-system-packages "mkdocs>=1.6,<2" mkdocs-material
|
||||||
|
|||||||
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
@@ -30,8 +30,8 @@
|
|||||||
#define LV_LIMITS_INCLUDE <limits.h>
|
#define LV_LIMITS_INCLUDE <limits.h>
|
||||||
#define LV_STDARG_INCLUDE <stdarg.h>
|
#define LV_STDARG_INCLUDE <stdarg.h>
|
||||||
|
|
||||||
/* Built-in memory pool: 256 KB should be plenty for our UI */
|
/* Built-in memory pool: 1 MB for complex UI (file browser etc.) */
|
||||||
#define LV_MEM_SIZE (256 * 1024U)
|
#define LV_MEM_SIZE (1024 * 1024U)
|
||||||
#define LV_MEM_POOL_EXPAND_SIZE 0
|
#define LV_MEM_POOL_EXPAND_SIZE 0
|
||||||
#define LV_MEM_ADR 0
|
#define LV_MEM_ADR 0
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ uses
|
|||||||
driver.video.lvgl,
|
driver.video.lvgl,
|
||||||
driver.storage.types,
|
driver.storage.types,
|
||||||
core.strings,
|
core.strings,
|
||||||
|
core.strings.helpers,
|
||||||
io.syslog,
|
io.syslog,
|
||||||
debug.tracer,
|
debug.tracer,
|
||||||
core.util, arch.x86.util,
|
core.util, arch.x86.util,
|
||||||
@@ -262,119 +263,6 @@ begin
|
|||||||
if cb <> nil then cb(sel_path, userdata);
|
if cb <> nil then cb(sel_path, userdata);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ ============================================================
|
|
||||||
fmtFileSize — returns a kalloc'd human-readable size string.
|
|
||||||
Caller must kfree the result.
|
|
||||||
============================================================ }
|
|
||||||
function fmtFileSize(sz: uint32): pchar;
|
|
||||||
var
|
|
||||||
whole, frac : uint32;
|
|
||||||
s1, s2, s3, res : pchar;
|
|
||||||
begin
|
|
||||||
if sz < 1024 then begin
|
|
||||||
s1 := intToString(sz);
|
|
||||||
res := stringConcat(s1, ' B');
|
|
||||||
kfree(void(s1));
|
|
||||||
end else if sz < uint32(1024 * 1024) then begin
|
|
||||||
whole := sz div 1024;
|
|
||||||
frac := (sz mod 1024) * 10 div 1024;
|
|
||||||
s1 := intToString(whole);
|
|
||||||
s2 := stringConcat(s1, '.'); kfree(void(s1));
|
|
||||||
s3 := intToString(frac);
|
|
||||||
s1 := stringConcat(s2, s3); kfree(void(s2)); kfree(void(s3));
|
|
||||||
res := stringConcat(s1, ' KB'); kfree(void(s1));
|
|
||||||
end else begin
|
|
||||||
whole := sz div (1024 * 1024);
|
|
||||||
frac := (sz mod (1024 * 1024)) * 10 div (1024 * 1024);
|
|
||||||
s1 := intToString(whole);
|
|
||||||
s2 := stringConcat(s1, '.'); kfree(void(s1));
|
|
||||||
s3 := intToString(frac);
|
|
||||||
s1 := stringConcat(s2, s3); kfree(void(s2)); kfree(void(s3));
|
|
||||||
res := stringConcat(s1, ' MB'); kfree(void(s1));
|
|
||||||
end;
|
|
||||||
fmtFileSize := res;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ ============================================================
|
|
||||||
matchesFilter
|
|
||||||
Returns true when name ends with the filter extension, or
|
|
||||||
when filter is nil (show everything).
|
|
||||||
============================================================ }
|
|
||||||
function matchesFilter(name: pchar; fltr: pchar): boolean;
|
|
||||||
var
|
|
||||||
nlen, flen, i: uint32;
|
|
||||||
begin
|
|
||||||
matchesFilter := true;
|
|
||||||
if fltr = nil then exit;
|
|
||||||
nlen := stringSize(name);
|
|
||||||
flen := stringSize(fltr);
|
|
||||||
if flen = 0 then exit;
|
|
||||||
if nlen < flen then begin matchesFilter := false; exit; end;
|
|
||||||
for i := 0 to flen - 1 do
|
|
||||||
if name[nlen - flen + i] <> fltr[i] then begin
|
|
||||||
matchesFilter := false;
|
|
||||||
exit;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ ============================================================
|
|
||||||
strLess — case-sensitive alphabetical comparison for sort
|
|
||||||
============================================================ }
|
|
||||||
function strLess(a, b: pchar): boolean;
|
|
||||||
var
|
|
||||||
i: uint32;
|
|
||||||
begin
|
|
||||||
strLess := false;
|
|
||||||
if (a = nil) or (b = nil) then exit;
|
|
||||||
i := 0;
|
|
||||||
while (a[i] <> #0) and (b[i] <> #0) do begin
|
|
||||||
if ord(a[i]) < ord(b[i]) then begin strLess := true; exit; end;
|
|
||||||
if ord(a[i]) > ord(b[i]) then exit;
|
|
||||||
i := i + 1;
|
|
||||||
end;
|
|
||||||
strLess := (a[i] = #0) and (b[i] <> #0);
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ Simple insertion sort on a pchar array of length n }
|
|
||||||
procedure sortNames(var arr: array of pchar; n: uint32);
|
|
||||||
var
|
|
||||||
i, j : uint32;
|
|
||||||
tmp : pchar;
|
|
||||||
begin
|
|
||||||
if n < 2 then exit;
|
|
||||||
for i := 1 to n - 1 do begin
|
|
||||||
tmp := arr[i];
|
|
||||||
j := i;
|
|
||||||
while (j > 0) and strLess(tmp, arr[j - 1]) do begin
|
|
||||||
arr[j] := arr[j - 1];
|
|
||||||
j := j - 1;
|
|
||||||
end;
|
|
||||||
arr[j] := tmp;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ Insertion sort on name array, swapping a parallel size array in lockstep }
|
|
||||||
procedure sortNamesWithSizes(var names: array of pchar; var sizes: array of uint32; n: uint32);
|
|
||||||
var
|
|
||||||
i, j : uint32;
|
|
||||||
tmpN : pchar;
|
|
||||||
tmpS : uint32;
|
|
||||||
begin
|
|
||||||
if n < 2 then exit;
|
|
||||||
for i := 1 to n - 1 do begin
|
|
||||||
tmpN := names[i];
|
|
||||||
tmpS := sizes[i];
|
|
||||||
j := i;
|
|
||||||
while (j > 0) and strLess(tmpN, names[j - 1]) do begin
|
|
||||||
names[j] := names[j - 1];
|
|
||||||
sizes[j] := sizes[j - 1];
|
|
||||||
j := j - 1;
|
|
||||||
end;
|
|
||||||
names[j] := tmpN;
|
|
||||||
sizes[j] := tmpS;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ ============================================================
|
{ ============================================================
|
||||||
fp_collect_cb
|
fp_collect_cb
|
||||||
core.ds.hashmap.forEach callback: classifies each VFS entry as dir or file
|
core.ds.hashmap.forEach callback: classifies each VFS entry as dir or file
|
||||||
@@ -395,7 +283,7 @@ begin
|
|||||||
ctx^.dir_count := ctx^.dir_count + 1;
|
ctx^.dir_count := ctx^.dir_count + 1;
|
||||||
end;
|
end;
|
||||||
otFILE, otVFILE:
|
otFILE, otVFILE:
|
||||||
if matchesFilter(key, ctx^.filter) then
|
if stringEndsWith(key, ctx^.filter) then
|
||||||
if ctx^.file_count < ENTRY_MAX then begin
|
if ctx^.file_count < ENTRY_MAX then begin
|
||||||
ctx^.file_names^[ctx^.file_count] := key;
|
ctx^.file_names^[ctx^.file_count] := key;
|
||||||
ctx^.file_sizes^[ctx^.file_count] := obj^.FileSize;
|
ctx^.file_sizes^[ctx^.file_count] := obj^.FileSize;
|
||||||
@@ -487,8 +375,8 @@ begin
|
|||||||
io.syslog.logln('FPCIK', 'do_refresh: collection done, sorting');
|
io.syslog.logln('FPCIK', 'do_refresh: collection done, sorting');
|
||||||
|
|
||||||
{ --- Sort both collections alphabetically --- }
|
{ --- Sort both collections alphabetically --- }
|
||||||
if dir_count > 0 then sortNames(dir_names^, dir_count);
|
if dir_count > 0 then sortStringArray(@dir_names^[0], dir_count);
|
||||||
if file_count > 0 then sortNamesWithSizes(file_names^, file_sizes^, file_count);
|
if file_count > 0 then sortStringArrayWithData(@file_names^[0], @file_sizes^[0], file_count);
|
||||||
|
|
||||||
{ --- Build directory rows --- }
|
{ --- Build directory rows --- }
|
||||||
if dir_count > 0 then
|
if dir_count > 0 then
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ uses
|
|||||||
core.version,
|
core.version,
|
||||||
//command provider units
|
//command provider units
|
||||||
arch.x86.cpu,
|
arch.x86.cpu,
|
||||||
app.diskcmd, driver.bus.usb.core, app.diskutil, app.notepad, app.partcmd, app.volcmd;
|
app.diskcmd, driver.bus.usb.core, app.diskutil, app.notepad, app.partcmd, app.volcmd,
|
||||||
|
app.filebrowser;
|
||||||
|
|
||||||
procedure init();
|
procedure init();
|
||||||
begin
|
begin
|
||||||
@@ -64,6 +65,10 @@ begin
|
|||||||
io.stdio.registerCommand('TCPHTTP', @driver.net.proto.tcp.terminal_command_tcphttp, 'Send HTTP GET to a host IP (port 80 default).');
|
io.stdio.registerCommand('TCPHTTP', @driver.net.proto.tcp.terminal_command_tcphttp, 'Send HTTP GET to a host IP (port 80 default).');
|
||||||
io.stdio.registerCommand('USB', @driver.bus.usb.core.terminal_command_usb, 'driver.bus.usb subsystem information.');
|
io.stdio.registerCommand('USB', @driver.bus.usb.core.terminal_command_usb, 'driver.bus.usb subsystem information.');
|
||||||
|
|
||||||
|
{ File dispatch — must init before apps that register handlers }
|
||||||
|
driver.storage.ctl.ram.init();
|
||||||
|
driver.storage.filedispatch.init();
|
||||||
|
|
||||||
{ Initialize baked-in programs }
|
{ Initialize baked-in programs }
|
||||||
app.diskcmd.init();
|
app.diskcmd.init();
|
||||||
app.partcmd.init();
|
app.partcmd.init();
|
||||||
@@ -77,12 +82,11 @@ begin
|
|||||||
app.testcmd.init();
|
app.testcmd.init();
|
||||||
app.ping.init();
|
app.ping.init();
|
||||||
app.meminfo.init();
|
app.meminfo.init();
|
||||||
{ File dispatch & WASM integration }
|
{ WASM & remaining apps }
|
||||||
driver.storage.ctl.ram.init();
|
|
||||||
driver.storage.filedispatch.init();
|
|
||||||
app.wasm.runner.init();
|
app.wasm.runner.init();
|
||||||
app.setres.init();
|
app.setres.init();
|
||||||
app.divzero.init();
|
app.divzero.init();
|
||||||
|
app.filebrowser.init();
|
||||||
end;
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
@@ -64,6 +64,7 @@ var
|
|||||||
hist_count : uint32; { samples collected so far }
|
hist_count : uint32; { samples collected so far }
|
||||||
|
|
||||||
visible : boolean;
|
visible : boolean;
|
||||||
|
toggle_pending : uint32; { set by ISR, checked by update in gfxd context }
|
||||||
|
|
||||||
{ FPS tracking }
|
{ FPS tracking }
|
||||||
frame_count : uint32;
|
frame_count : uint32;
|
||||||
@@ -432,24 +433,31 @@ end;
|
|||||||
============================================================ }
|
============================================================ }
|
||||||
procedure update;
|
procedure update;
|
||||||
begin
|
begin
|
||||||
|
{ Handle deferred toggle from ISR (Ctrl+D keyboard hook).
|
||||||
|
Must run here in gfxd context, NOT in ISR — LVGL is not reentrant. }
|
||||||
|
if puint32(@toggle_pending)^ <> 0 then begin
|
||||||
|
puint32(@toggle_pending)^ := 0;
|
||||||
|
if visible then begin
|
||||||
|
destroyOverlay;
|
||||||
|
visible := false;
|
||||||
|
end else begin
|
||||||
|
frame_count := 0;
|
||||||
|
last_tick := lvgl_get_ticks;
|
||||||
|
current_fps := 0;
|
||||||
|
hist_index := 0;
|
||||||
|
hist_count := 0;
|
||||||
|
createOverlay;
|
||||||
|
visible := true;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
if visible then
|
if visible then
|
||||||
refreshLabels;
|
refreshLabels;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure toggle;
|
procedure toggle;
|
||||||
begin
|
begin
|
||||||
if visible then begin
|
{ Called from keyboard ISR — just set flag, actual work happens in update() }
|
||||||
destroyOverlay;
|
puint32(@toggle_pending)^ := 1;
|
||||||
visible := false;
|
|
||||||
end else begin
|
|
||||||
frame_count := 0;
|
|
||||||
last_tick := lvgl_get_ticks;
|
|
||||||
current_fps := 0;
|
|
||||||
hist_index := 0;
|
|
||||||
hist_count := 0;
|
|
||||||
createOverlay;
|
|
||||||
visible := true;
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function isVisible: boolean;
|
function isVisible: boolean;
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ uses
|
|||||||
driver.storage.mgr,
|
driver.storage.mgr,
|
||||||
driver.storage.test,
|
driver.storage.test,
|
||||||
core.strings,
|
core.strings,
|
||||||
|
core.strings.helpers,
|
||||||
io.syslog,
|
io.syslog,
|
||||||
driver.exp.testdriver,
|
driver.exp.testdriver,
|
||||||
proc.testprocs,
|
proc.testprocs,
|
||||||
@@ -328,6 +329,7 @@ begin
|
|||||||
{ Run unit tests }
|
{ Run unit tests }
|
||||||
boot.splash.update(65, 'Running test suite...');
|
boot.splash.update(65, 'Running test suite...');
|
||||||
core.strings.UnitTest;
|
core.strings.UnitTest;
|
||||||
|
core.strings.helpers.UnitTest;
|
||||||
driver.bus.usb.types.UnitTest;
|
driver.bus.usb.types.UnitTest;
|
||||||
driver.bus.usb.core.UnitTest;
|
driver.bus.usb.core.UnitTest;
|
||||||
boot.splash.update(70, 'Running test suite...');
|
boot.splash.update(70, 'Running test suite...');
|
||||||
|
|||||||