Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
300 changes: 291 additions & 9 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ headless_chrome = "1"
urlencoding = "2.1"
scraper = "0.19"
futures = "0.3"
moka = { version = "0.12.10", features = ["future"] }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moka is a cache system. The idea is that when parsing images, we async read from the file system then cache the image in moka, so when picolauncher requests for another image, no file system read is needed and the image can be loaded quickly.


[target.'cfg(target_os = "linux")'.dependencies]
nix = { version = "0.29", features = ["fs", "process", "signal"] }
Expand Down
1 change: 1 addition & 0 deletions drive/carts/bbs
15 changes: 11 additions & 4 deletions drive/carts/pexsplore_bbs.p8
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ bar_color_1=12
bar_color_2=-4

cart_dir='games'
label_dir='labels'
label_dir='bbs' -- TODO: currently bbs is a symlink because I want to test p8.png loading. This should not be a symlink and instead games should have p8.png files when possible since those have more information.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this was a hack, because I wanted to test loading actual carts instead of image files.

loaded_carts={} -- list of all carts that can be displayed in the menu
carts={} -- menu for pexsplore ui

Expand Down Expand Up @@ -75,6 +75,7 @@ function draw_label(x, y, w, slot)
sspr(i*64, slot*32 + j, 64, 1, x-w/2, y-w/2+j*2+i)
end
end
-- sspr(0, 0, 128, 128, x-w/2, y-w/2+j*2+i, 64, 64)
end
end

Expand Down Expand Up @@ -188,10 +189,16 @@ function make_cart_swipe_tween(dir)
cart_swipe_tween:register_step_callback(function(pos)
cart_x_swipe=pos
end)

cart_swipe_tween:register_step_callback(function(_, frame)
serial_load_image("bbs/"..carts:cur().filename, 0x0000, 64, 64, frame)
end)

cart_swipe_tween:register_finished_callback(function(tween)
cart_x_swipe=64-1*dir*128
tween:remove()
load_label(carts:cur(), 0)

-- load_label(carts:cur(), 0)
make_cart_swipe_tween_2(dir)
end)
cart_swipe_tween:restart()
Expand Down Expand Up @@ -460,7 +467,7 @@ function build_new_cart_menu(resp)
item.menuitem = menuitem.cart
add(new_menuitems, item)
end

-- TODO would like to disable this option for local categories, but also need to make sure we don't have an empty menu
add(new_menuitems, {menuitem=menuitem.load})

Expand Down Expand Up @@ -505,7 +512,7 @@ function draw_carts_menu()
else
local str="❎ load more carts"
print(str, cart_x_swipe-#str*2, 64, 7)
end
end
else
draw_cart(cart_x_swipe, 64.5+cart_y_ease+cart_y_bob, 0)
local str="❎view"
Expand Down
76 changes: 42 additions & 34 deletions drive/carts/serial.p8
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ end

-- serial interface with the underlying operating system

stdin=0x806
stdout=0x807
stdin=0x804
stdout=0x805

chan_buf=0x4300
chan_buf_size=0x1000
Expand Down Expand Up @@ -88,6 +88,36 @@ function serial_spawn_pico8()
serial_writeline('spawn_pico8:')
end

-- load an image in parts through serial-in. image is loaded row-by-row.
-- image is loaded in the pico-8 4bit image format.
-- return true means the function is done at that frame and doesn't need to be called anymore.
-- load modes:
-- - "top_bot" -- load the image from the top to the bottom.
POSSIBLE_LOAD_MODES = {top_bot=true}
function serial_load_image(filename, location, scale_width, scale_height, frame, mode, bytes_per_frame)
filename = filename..".p8.png"
mode = POSSIBLE_LOAD_MODES[mode] and mode or "top_bot" -- load the image top to bottom.
bytes_per_frame = bytes_per_frame or 1024 -- 1024 bytes is about 50% cpu on 60 fps.
scale_width = mid(1, scale_width, 128)\1 -- scale_width, scaled down images load can faster
scale_height = mid(1, scale_height, 128)\1
frame = max(0, frame\1) -- which frame this is. used to determine which part of the image to load.

local buffer_len = ceil(scale_width*scale_height/2)
if bytes_per_frame*(frame-1) >= buffer_len then
return true
end

serial_writeline('load_image:'..filename..","..scale_width..","..scale_height..","..bytes_per_frame..","..frame..","..mode)

printh("about to read "..bytes_per_frame.." bytes")
local size = serial(stdin, location+bytes_per_frame*frame, bytes_per_frame) -- TODO: make it read less for the last frame
printh("finished reading "..bytes_per_frame.." bytes size "..size.." | "..(location+bytes_per_frame*frame))

if bytes_per_frame*(frame-1) >= buffer_len then
return true
end
end

function serial_spawn_splore()
serial_writeline('spawn_splore:')
end
Expand Down Expand Up @@ -136,45 +166,24 @@ end
-- read from input file until a newline is reached
-- TODO this can be refactored into a coroutine?
function serial_readline()
printh("about to read")
local result=''
local got_newline=false
while true do
-- also use the argument space to receive the result
size = serial(stdin, chan_buf, chan_buf_size)
if (size == 0) then return result end
-- printh('size: ' .. size)
for i=0,size do
b = peek(chan_buf+i)
-- printh('byte: '..b)
if b == 0x0a then
got_newline=true
break
end
result = result..chr(b)
end
serial(stdin, chan_buf, 1)
local byte = @chan_buf
if byte == 0x0a then break
else result ..= chr(byte) end
end
if not got_newline then
printh('warning: newline was not received')
end
printh('result '..result)

printh("finished reading")

return result
end

-- Yep a write is just a printh to stdout.
function serial_writeline(buf)
-- TODO check length of buf to avoid overfloe
-- TODO not super efficient
printh('output len '..#buf .. ' content ' .. buf)
for i=1,#buf do
b = ord(sub(buf, i, i))
-- printh('copy: '..b)
poke(chan_buf + i - 1, b)
end
-- write a newline character
poke(chan_buf+#buf, ord('\n'))

-- TODO currently this means that newlines are not allowed in messages
serial(stdout, chan_buf, #buf+1)
flip()
printh(buf)
end

function serial_fetch_carts()
Expand All @@ -199,7 +208,6 @@ function os_load(path, breadcrumb, param)
if param == nil then param = '' end

serial_writeline('pushcart:'..path..','..breadcrumb..','..param)
serial_readline() -- empty response
load(path, breadcrumb, param)
end

Expand Down
5 changes: 4 additions & 1 deletion drive/carts/tween.lua
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,7 @@ function tween_machine:add_tween(instance)
start_time = 0,
duration = 0,
elapsed = 0,
frame = 0,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be a hack. There is a tween when loading images. Frame is needed because the protocol for getting an image takes in how many bytes to read and the offset. That's because you can't load a full image in a single frame without getting fps throttle, so the idea was, read like 1/8th of the image each frame of an animation. A new cart image could load in like 8 frames while you are doing a screen swipe animation, so it could make it look seamless especially if the image is cached.

finished = false,

--- Callbacks
Expand Down Expand Up @@ -583,6 +584,7 @@ end
function __tween:restart()
self:init()
self.elapsed = 0
self.frame = 0
self.finished = false
end

Expand All @@ -604,6 +606,7 @@ function __tween:update()
if (self.finished or self.func == nil) return

self.elapsed = time() - self.start_time
self.frame += 1 -- frame just increments by 1 each frame.
if (self.elapsed > self.duration) self.elapsed = self.duration
self.value = self.func(
self.elapsed,
Expand All @@ -614,7 +617,7 @@ function __tween:update()

if #self.step_callbacks > 0 then
for v in all(self.step_callbacks) do
v(self.value)
v(self.value, self.frame-1)
end
end

Expand Down
2 changes: 1 addition & 1 deletion drive/config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ foreground_sleep_ms 2 // number of milliseconds to sleep each frame. Try 10 to c

background_sleep_ms 10 // number of milliseconds to sleep each frame when running in the background

sessions 700 // number of times program has been run
sessions 712 // number of times program has been run

// (scancode) hold this key down and left-click to simulate right-click
rmb_key 0 // 0 for none 226 for LALT
Expand Down
9 changes: 4 additions & 5 deletions src/hal/linux.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use std::{
fs::{File, OpenOptions},
path::{Path, PathBuf},
time::Duration,
fs::{File, OpenOptions}, path::{Path, PathBuf}, process::Stdio, time::Duration
};

use anyhow::anyhow;
Expand Down Expand Up @@ -52,7 +50,7 @@ pub fn open_in_pipe() -> anyhow::Result<File> {
}

pub fn open_out_pipe() -> anyhow::Result<File> {
create_pipe(&PathBuf::from(IN_PIPE))?;
create_pipe(&PathBuf::from(OUT_PIPE))?;

let out_pipe = OpenOptions::new().read(true).open(&*OUT_PIPE)?;

Expand Down Expand Up @@ -157,7 +155,8 @@ pub fn launch_pico8_binary(bin_names: &Vec<String>, args: Vec<&str>) -> anyhow::
for bin_name in bin_names {
let pico8_process = Command::new(bin_name.clone())
.args(args.clone())
// .stdout(Stdio::piped())
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn();

match pico8_process {
Expand Down
Loading