Extensible Loader

Looks very good :-)I like the big icons. Adding favourites is a fine idea also.

It looks a bit like NIntendo 3DS menu. Maybe Pico-8 palette would be nice.

2 Likes

I like it… almost reminds me Emulation Station… Could just add how many games are under each section and it’s almost the same :wink:

1 Like

Good idea !

I chose this one because it’s harder for me to make something terribly hideous with the limited amount of colors. :stuck_out_tongue:

Hmm… I’ll have to think about this a bit. :thinking:

Pokitto Mint:

image

I basically redrew what I see on my desktop. CC0.

Would be cool if you could create plugin “”“apps”"" for the bottom bar.

4 Likes

I implemented support for desktop plugins.
The desktop looks in “loader/desktopwidgets” and runs all the pops inside. Widgets can customize the desktop app by loading a background wallpaper, changing the palette, printing the time/battery level, etc.

A boring/trivial “hello world” example:
1

The counter on the bottom left increments each time I press left/right. The compiled widget is just 340 bytes (352 in pop format). Here’s what the code looks like:

#include "../kernel/kapi.h"

namespace DESKTOP {
    #include "../desktop/api.h"
}

DESKTOP::API *desktop;
int c, pressed;

void setup( DESKTOP::API *dtapi ){
    desktop = dtapi;
}

void loop( KAPI *kernel ){
    if( isPressedLeft() || isPressedRight() ) c += pressed?0:++pressed;
    else pressed = 0;
    desktop->setCursor( 0, DESKTOP::height-5 );
    *desktop->drawcolor = c&0xF;
    desktop->printf("%d", c);
}

6 Likes

Can dtapi and kernel be nullptr?
If not, references would be better than pointers.

Also why setup and loop?
Seems a bit odd in a non-Arduino environment.

Not in this particular case, but there are enough situations where an api can be null that I preferred the uniformity of just using pointers. It’s not set in stone though, I’m going to have to clean up before I make a release and I’ll reconsider it.

I thought it seemed fitting/convenient considering how simple desktop widgets are supposed to be.
What would you suggest instead?


3
Same desktop, customized with a plugin:

#include "../kernel/kapi.h"

namespace DESKTOP {
    #include "../desktop/api.h"
}

void setup( DESKTOP::API *desktop ){
    
    desktop->itemStrideX = 0;
    desktop->itemStrideY = 42;
    desktop->itemOffsetX = DESKTOP::width - 38;
    desktop->itemOffsetY = -8;
    desktop->moveX = 0;
    desktop->moveY = -40;
    
    desktop->lblX = DESKTOP::width - 20;
    desktop->lblY = 28;
    desktop->lblColor = 0;

    desktop->clearColor = 1;
    desktop->clearX = DESKTOP::width - 40;
    desktop->clearWidth = 40;
    
    FS.init("");
    
    FILE *f = FS.fopen("loader/wallpaper.16c", "r");
    if( f ){
	
	FS.fread( desktop->screenbuffer, 1, 55*88, f );
	FS.fclose(f);
	
    }

}

4 Likes

I’m OK with that.

ALSO: Impressive work so far Felipe!

1 Like

If there are provable cases where it makes sense for the API to be nullptr then it makes sense to use pointers.
But that means everyone will always have to check that the pointer isn’t nullptr as a precaution.

If there aren’t any situations where the API can be nullptr then references make more sense.

initialise and update (or init and update if you want to avoid the -ise vs -ize debate, though it’s not hard to make initialize an alias for initialise).


Essentially I would expect usage more like:

#include "../kernel/kapi.h"
#include "../desktop/api.h"

using KernelApi = Kernel::API;
using DesktopApi = Desktop::API;

int c = 0;
int pressed = 0;

void initialise(DesktopApi & desktop)
{
	(void)desktop;
}

void update(KernelApi & kernel, DesktopApi & desktop)
{
	(void)kernel;

	if(isPressedLeft() || isPressedRight())
	{
		if(pressed > 0)
		{
			++pressed;
			c += pressed;
		}
	}
	else
	{
		pressed = 0;
	}
		
	desktop.setCursor(0, (Desktop::height - 5));
	desktop.drawcolor = (c & 0x0F);
	desktop.print(c);
}
#include "../kernel/kapi.h"
#include "../desktop/api.h"

void initialise(Desktop::API & desktop)
{
	(void)kernel;
	
	// No nullptr check required
	
	desktop.itemStrideX = 0;
	desktop.itemStrideY = 42;
	desktop.itemOffsetX = (Desktop::width - 38);
	desktop.itemOffsetY = -8;
	desktop.moveX = 0;
	desktop.moveY = -40;
	
	desktop.lblX = (Desktop::width - 20);
	desktop.lblY = 28;
	desktop.lblColor = 0;

	desktop.clearColor = 1;
	desktop.clearX = (Desktop::width - 40);
	desktop.clearWidth = 40;
	
	FileSystem::init("");
	
	FileReader file = FileSystem::openRead("loader/wallpaper.16c");
	if(file.isValid())
	{	
		file.read(desktop->screenbuffer, 1, 55 * 88);
	}
	// Let file's destructor close the file, as per RAII
}
1 Like

Yes, checking for nullptr can be cumbersome, I’ll probably switch to references where it makes sense to do so later on.

Right now, simply using pointers everywhere makes things easier to write: don’t have to try to remember “was this supposed to be a pointer or a reference?” and I don’t have to think about all the possible future cases (will I ever want to change this to point to something else?).

Later on I’ll go over it all and make it easier to read.

I like init and update.

The names can be changed freely by the widget’s boilerplate without impact to anything else, so one widget can use init and update while another uses Widget::Widget(DesktopAPI &, KernelAPI &) and void Widget::update() or something else. All the kernel wants is function pointers.

I’ll make some template widgets that use different API styles so that others can just duplicate the one they want to start off with.

A class-based FILE wrapper with RAII is in my to-do list. Just haven’t had the chance to get to it yet.

Very nice and easy customisation! I cannot wait to test the loader and add Python script support :wink:

1 Like

The problem isn’t the burden of having to check,
the problem is what happens when you don’t and the pointer is null.

I learnt the word ‘segfault’ very early in my C++ career :P
(Not that ‘segfault’ applies to ARM, but it’s still nasal demon territory.)

Function pointers or std::function?

If it’s just function pointers, member functions won’t be an option, member function pointers are fundamentally different beasts.

(I’m assuming Widget::Widget is supposed to be a constructor?)

If I knew more about the innards of the existing file system I’d have a go myself.
Unfortunately there’s about 2-3 different file systems in use in PokittoLib so I have no clue which is capable of what.

1 Like

Directory navigation, long file names, flashing: done.
Now to implement support for progress bars…

6 Likes

Function pointers, as I intend to call APIs from code that isn’t C++.
Member pointers can either be wrapped (void update(){ instance.update(); }) or declared static.

2 Likes

How did you manage to speed up the flashing so much?

The game I flashed in the video is small so it flashes quickly.
I haven’t done a speed comparison yet, so I don’t know how it compares to the original loader.

2 Likes

agape

How can I offer you a beer or two?

2 Likes

Very good progress! Maybe we soon see game icons too… :slight_smile:

2 Likes

How about Pokitto games instead? I’m looking forward to that platformer you’re working on. :wink:

Icons are a chicken and egg problem: none of the games have icons because they weren’t needed yet. Now that I need them, there aren’t any.
I’m thinking that for the first release I could use a full screenshot instead, since that is easier to make.

4 Likes