<feed xmlns='http://www.w3.org/2005/Atom'>
<title>puzzles/emcclib.js, branch devel</title>
<subtitle>My sgt-puzzles tree</subtitle>
<link rel='alternate' type='text/html' href='https://www.franklinwei.com/cgit/puzzles/'/>
<entry>
<title>js: load preferences from HTML elements</title>
<updated>2023-08-21T22:06:32+00:00</updated>
<author>
<name>Ben Harris</name>
<email>bjh21@bjh21.me.uk</email>
</author>
<published>2023-08-21T21:45:48+00:00</published>
<link rel='alternate' type='text/html' href='https://www.franklinwei.com/cgit/puzzles/commit/?id=b5367ed18ac96f54595fbe6c17fe8a4a897020aa'/>
<id>b5367ed18ac96f54595fbe6c17fe8a4a897020aa</id>
<content type='text'>
It will be useful on KaiOS to be able to specify default user
preferences that aren't the standard ones, in the same way that we
specify some environment variables.  As with environment variables, we
can now do this be embedding a &lt;script&gt; element in the HTML like this:

&lt;script class="preferences" type="text/plain"&gt;
show-labels=true
&lt;/script&gt;

These are loaded before the preferences from localStorage, so they just
set defaults and can be overridden by the user (but not on KaiOS yet,
because we still don't have dialogue boxes there).
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
It will be useful on KaiOS to be able to specify default user
preferences that aren't the standard ones, in the same way that we
specify some environment variables.  As with environment variables, we
can now do this be embedding a &lt;script&gt; element in the HTML like this:

&lt;script class="preferences" type="text/plain"&gt;
show-labels=true
&lt;/script&gt;

These are loaded before the preferences from localStorage, so they just
set defaults and can be overridden by the user (but not on KaiOS yet,
because we still don't have dialogue boxes there).
</pre>
</div>
</content>
</entry>
<entry>
<title>js: prefer some puzzle size even if loading isn't complete</title>
<updated>2023-08-21T21:06:21+00:00</updated>
<author>
<name>Ben Harris</name>
<email>bjh21@bjh21.me.uk</email>
</author>
<published>2023-08-21T21:03:18+00:00</published>
<link rel='alternate' type='text/html' href='https://www.franklinwei.com/cgit/puzzles/commit/?id=85b00e56a034713a8e34f9e10423ae14dbc810e0'/>
<id>85b00e56a034713a8e34f9e10423ae14dbc810e0</id>
<content type='text'>
The js_canvas_get_preferred_size() function was declining to suggest a
size for the puzzle if document.readyState wasn't "complete".  I think
my idea here was that if the document wasn't fully loaded then I
couldn't trust the size of the containing &lt;div&gt;.  While this was true,
declining to provide a size didn't help much since the puzzle still
needed a size, and the size of the containing &lt;div&gt; was the best guess
we had.

Now that function always returns the size of the containing &lt;div&gt; if
it exists.  This appears to mean that puzzles don't show a brief flash
of being the wrong size on KaiOS.  That was particularly visible with
Flood, where the wrong-size version had borders around the tiles that
the right-size version lacked.  The containing &lt;div&gt; isn't used on the
standard Web versions, so there's no change to behaviour there.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
The js_canvas_get_preferred_size() function was declining to suggest a
size for the puzzle if document.readyState wasn't "complete".  I think
my idea here was that if the document wasn't fully loaded then I
couldn't trust the size of the containing &lt;div&gt;.  While this was true,
declining to provide a size didn't help much since the puzzle still
needed a size, and the size of the containing &lt;div&gt; was the best guess
we had.

Now that function always returns the size of the containing &lt;div&gt; if
it exists.  This appears to mean that puzzles don't show a brief flash
of being the wrong size on KaiOS.  That was particularly visible with
Flood, where the wrong-size version had borders around the tiles that
the right-size version lacked.  The containing &lt;div&gt; isn't used on the
standard Web versions, so there's no change to behaviour there.
</pre>
</div>
</content>
</entry>
<entry>
<title>js: keep colour strings in JavaScript rather than in C</title>
<updated>2023-07-30T10:50:25+00:00</updated>
<author>
<name>Ben Harris</name>
<email>bjh21@bjh21.me.uk</email>
</author>
<published>2023-07-29T15:06:19+00:00</published>
<link rel='alternate' type='text/html' href='https://www.franklinwei.com/cgit/puzzles/commit/?id=76da6ec140cbbdac6136469ce50aab40e218f398'/>
<id>76da6ec140cbbdac6136469ce50aab40e218f398</id>
<content type='text'>
The drawing routines in JavaScript used to take pointers to a C string
containing a CSS colour name.  That meant that JavaScript had to create
a new JavaScript string on ever call to a drawing function, which seemed
ugly.

So now we instead pass colour numbers all the way down into JavaScript
and keep an array of JavaScript strings there that can be re-used.  The
conversion from RGB triples to strings is still done in C, though.

This doesn't seem to have fixed either of the bugs I hoped it would, but
it does measurably improve drawing performance so I think it's worth
doing.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
The drawing routines in JavaScript used to take pointers to a C string
containing a CSS colour name.  That meant that JavaScript had to create
a new JavaScript string on ever call to a drawing function, which seemed
ugly.

So now we instead pass colour numbers all the way down into JavaScript
and keep an array of JavaScript strings there that can be re-used.  The
conversion from RGB triples to strings is still done in C, though.

This doesn't seem to have fixed either of the bugs I hoped it would, but
it does measurably improve drawing performance so I think it's worth
doing.
</pre>
</div>
</content>
</entry>
<entry>
<title>js: pass preferences file from JS to C on the heap, not the stack</title>
<updated>2023-05-30T15:00:31+00:00</updated>
<author>
<name>Ben Harris</name>
<email>bjh21@bjh21.me.uk</email>
</author>
<published>2023-05-30T14:55:22+00:00</published>
<link rel='alternate' type='text/html' href='https://www.franklinwei.com/cgit/puzzles/commit/?id=5acce15ed907d29a5575668a09e7d94cf7a36b3f'/>
<id>5acce15ed907d29a5575668a09e7d94cf7a36b3f</id>
<content type='text'>
The C stack used by Emscripten is quite small, so passing more than a
few klilobytes of data on it tends to cause an overflow.  Current
versions of puzzles will only generate tiny preferences files, but this
might change in future and in any case Puzzles shouldn't crash just
because the preferences in local storage have got corrupted.

To fix this, we now have JavaScript allocate a suitable amount of C heap
memory using malloc() and stick the preferences file in there.

This could plausibly fail if the preferences file were really big, but
that's unlikely since browsers generally limit an origin to about 5 MB
of local storage.  In any case, if malloc() does fail, we'll just ignore
the preferences file, which is probably the right thing to do.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
The C stack used by Emscripten is quite small, so passing more than a
few klilobytes of data on it tends to cause an overflow.  Current
versions of puzzles will only generate tiny preferences files, but this
might change in future and in any case Puzzles shouldn't crash just
because the preferences in local storage have got corrupted.

To fix this, we now have JavaScript allocate a suitable amount of C heap
memory using malloc() and stick the preferences file in there.

This could plausibly fail if the preferences file were really big, but
that's unlikely since browsers generally limit an origin to about 5 MB
of local storage.  In any case, if malloc() does fail, we'll just ignore
the preferences file, which is probably the right thing to do.
</pre>
</div>
</content>
</entry>
<entry>
<title>js: handle exceptions when accessing localStorage</title>
<updated>2023-05-30T13:07:41+00:00</updated>
<author>
<name>Ben Harris</name>
<email>bjh21@bjh21.me.uk</email>
</author>
<published>2023-05-30T13:07:41+00:00</published>
<link rel='alternate' type='text/html' href='https://www.franklinwei.com/cgit/puzzles/commit/?id=6a41c0b7a0c0f8299a86e5d33e7008ced3911924'/>
<id>6a41c0b7a0c0f8299a86e5d33e7008ced3911924</id>
<content type='text'>
Trying to access window.localStorage will generate an exception if the
local storage is for some reason inaccessible.  This can be
demonstrated in Firefox by configuring it to block a site from using
site data.  Writing to local storage might also cause an exception if,
for instance, the quota of data for a site is exceeded.

If an exception is raised while loading preferences we log the fact
but don't make any report to the user, behaving as if no preferences
were found.  This avoids annoying the user on every visit to a puzzle
page if they're not trying to use preferences.

If something goes wrong when saving, we do currently report that to
the user in an alert box.  This seems reasonable since it's in
response to an explicit user action.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Trying to access window.localStorage will generate an exception if the
local storage is for some reason inaccessible.  This can be
demonstrated in Firefox by configuring it to block a site from using
site data.  Writing to local storage might also cause an exception if,
for instance, the quota of data for a site is exceeded.

If an exception is raised while loading preferences we log the fact
but don't make any report to the user, behaving as if no preferences
were found.  This avoids annoying the user on every visit to a puzzle
page if they're not trying to use preferences.

If something goes wrong when saving, we do currently report that to
the user in an alert box.  This seems reasonable since it's in
response to an explicit user action.
</pre>
</div>
</content>
</entry>
<entry>
<title>Emscripten: fix edge case of js_canvas_find_font_midpoint.</title>
<updated>2023-05-26T20:29:29+00:00</updated>
<author>
<name>Simon Tatham</name>
<email>anakin@pobox.com</email>
</author>
<published>2023-05-26T20:29:29+00:00</published>
<link rel='alternate' type='text/html' href='https://www.franklinwei.com/cgit/puzzles/commit/?id=b6c842a28cf6597df063fcff35079c3e3982381e'/>
<id>b6c842a28cf6597df063fcff35079c3e3982381e</id>
<content type='text'>
If the puzzle canvas is at a ludicrously small size, so that you
attempt to use a zero-height font, then obviously nothing sensible
will appear in the way of text, but you'd at least like to avoid a
crash. But currently, js_canvas_find_font_midpoint will make a canvas,
print some height-0 text into it, and try to retrieve the image pixels
to see what the actual font height was - and this will involve asking
getImageData for a zero-sized rectangle of pixels, which is an error.

Of course, there's only one possible return value from this function
if the font height is 0, so we can just return it without going via
getImageData at all.

(This crash can be provoked by trying to resize the puzzle canvas to
Far Too Small, or by interleaving canvas resizes with browser-tab
zooming. I've had one report that it also occurs in less silly
situations, which I haven't been able to reproduce. However, this
seems like a general improvement anyway.)
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
If the puzzle canvas is at a ludicrously small size, so that you
attempt to use a zero-height font, then obviously nothing sensible
will appear in the way of text, but you'd at least like to avoid a
crash. But currently, js_canvas_find_font_midpoint will make a canvas,
print some height-0 text into it, and try to retrieve the image pixels
to see what the actual font height was - and this will involve asking
getImageData for a zero-sized rectangle of pixels, which is an error.

Of course, there's only one possible return value from this function
if the font height is 0, so we can just return it without going via
getImageData at all.

(This crash can be provoked by trying to resize the puzzle canvas to
Far Too Small, or by interleaving canvas resizes with browser-tab
zooming. I've had one report that it also occurs in less silly
situations, which I haven't been able to reproduce. However, this
seems like a general improvement anyway.)
</pre>
</div>
</content>
</entry>
<entry>
<title>Emscripten: change the localStorage key used for preferences.</title>
<updated>2023-04-24T13:24:19+00:00</updated>
<author>
<name>Simon Tatham</name>
<email>anakin@pobox.com</email>
</author>
<published>2023-04-24T13:24:19+00:00</published>
<link rel='alternate' type='text/html' href='https://www.franklinwei.com/cgit/puzzles/commit/?id=20d424bf1bef4c1ed249ec3c72a859deb926e207'/>
<id>20d424bf1bef4c1ed249ec3c72a859deb926e207</id>
<content type='text'>
I picked pathname + "#preferences" because Ben suggested either that
or pathname + "/preferences", and I thought the former sounded neater.
But Ben now suggests that it might be better to avoid using # in the
key, just in case anyone should later want to use the whole URL
_including_ the fragment ID as the key for some other localStorage
usage - for example, autosaved progress per puzzle _instance_, keying
off the puzzle URL with a fragment ID in the game description.

So, per Ben's replacement suggestion, change the "#" to a space.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
I picked pathname + "#preferences" because Ben suggested either that
or pathname + "/preferences", and I thought the former sounded neater.
But Ben now suggests that it might be better to avoid using # in the
key, just in case anyone should later want to use the whole URL
_including_ the fragment ID as the key for some other localStorage
usage - for example, autosaved progress per puzzle _instance_, keying
off the puzzle URL with a fragment ID in the game description.

So, per Ben's replacement suggestion, change the "#" to a space.
</pre>
</div>
</content>
</entry>
<entry>
<title>Support user preferences in the Emscripten frontend.</title>
<updated>2023-04-24T09:17:33+00:00</updated>
<author>
<name>Simon Tatham</name>
<email>anakin@pobox.com</email>
</author>
<published>2023-04-24T09:17:33+00:00</published>
<link rel='alternate' type='text/html' href='https://www.franklinwei.com/cgit/puzzles/commit/?id=43db4aa38e83595dc6df245cb952795f9f306ed0'/>
<id>43db4aa38e83595dc6df245cb952795f9f306ed0</id>
<content type='text'>
Here, user preferences are stored in localStorage, so that they can
persist when you come back to the same puzzle page later.

localStorage is global across a whole web server, which means we need
to take care to put our uses of it in a namespace reasonably unlikely
to collide with unrelated web pages on the same server. Ben suggested
that a good way to do this would be to store things in localStorage
under keys derived from location.pathname. In this case I've appended
a fragment id "#preferences" to that, so that space alongside it
remains for storing other things we might want in future (such as
serialised saved-game files used as quick-save slots).

When loading preferences, I've chosen to pass the whole serialised
preferences buffer from Javascript to C as a single C string argument
to a callback, rather than reusing the existing system for C to read
the save file a chunk at a time. Partly that's because preferences
data is bounded in size whereas saved games can keep growing; also
it's because the way I'm storing preferences data means it will be a
UTF-8 string, and I didn't fancy trying to figure out byte offsets in
the data on the JS side.

I think at this point I should stop keeping a list in the docs of
which frontends support preferences. Most of the in-tree ones do now,
and that means the remaining interesting frontends are ones I don't
have a full list of. At this moment I guess no out-of-tree frontends
support preferences (unless someone is _very_ quick off the mark), but
as and when that changes, I won't necessarily know, and don't want to
have to keep updating the docs when I find out.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Here, user preferences are stored in localStorage, so that they can
persist when you come back to the same puzzle page later.

localStorage is global across a whole web server, which means we need
to take care to put our uses of it in a namespace reasonably unlikely
to collide with unrelated web pages on the same server. Ben suggested
that a good way to do this would be to store things in localStorage
under keys derived from location.pathname. In this case I've appended
a fragment id "#preferences" to that, so that space alongside it
remains for storing other things we might want in future (such as
serialised saved-game files used as quick-save slots).

When loading preferences, I've chosen to pass the whole serialised
preferences buffer from Javascript to C as a single C string argument
to a callback, rather than reusing the existing system for C to read
the save file a chunk at a time. Partly that's because preferences
data is bounded in size whereas saved games can keep growing; also
it's because the way I'm storing preferences data means it will be a
UTF-8 string, and I didn't fancy trying to figure out byte offsets in
the data on the JS side.

I think at this point I should stop keeping a list in the docs of
which frontends support preferences. Most of the in-tree ones do now,
and that means the remaining interesting frontends are ones I don't
have a full list of. At this moment I guess no out-of-tree frontends
support preferences (unless someone is _very_ quick off the mark), but
as and when that changes, I won't necessarily know, and don't want to
have to keep updating the docs when I find out.
</pre>
</div>
</content>
</entry>
<entry>
<title>js: Load save files into the C side incrementally</title>
<updated>2023-04-03T20:09:57+00:00</updated>
<author>
<name>Ben Harris</name>
<email>bjh21@bjh21.me.uk</email>
</author>
<published>2023-04-03T14:16:35+00:00</published>
<link rel='alternate' type='text/html' href='https://www.franklinwei.com/cgit/puzzles/commit/?id=36c282aaa92bd42d570cdc76fe3b9e76d8da1ff1'/>
<id>36c282aaa92bd42d570cdc76fe3b9e76d8da1ff1</id>
<content type='text'>
Before this commit, JavaScript Puzzles loaded a save file by pushing the
entire file onto the Emscripten stack and then reading it from there.
This worked tolerably for typical save files, but Emscripten's stack
defaults to only having 64 kiB of space.  That meant that trying to load
something that wasn't a real save file tended to cause a stack overflow.
I expect that at least some real save files would suffer from the same
problem.

The stack overflow would generally cause a JavaScript exception and then
leave the stack pointer outside the stack, so that any future attempt to
call into C would fail as well.

To fix this, arrange that the C function for reading data from the save
file calls out to JavaScript.  The JavaScript can then copy just the
requested data into the caller's buffer.  We can't pass a JavaScript
function pointer to C, but since only one file can be loaded at a time,
we can just have a global variable that's the current loading callback.

There might still be a problem if you try to load a stupendously large
file, since I think FileReader.readAsArrayBuffer() reads the whole file
into the browser's RAM.  It works on my laptop with files up to a few
hundred megabytes, though.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Before this commit, JavaScript Puzzles loaded a save file by pushing the
entire file onto the Emscripten stack and then reading it from there.
This worked tolerably for typical save files, but Emscripten's stack
defaults to only having 64 kiB of space.  That meant that trying to load
something that wasn't a real save file tended to cause a stack overflow.
I expect that at least some real save files would suffer from the same
problem.

The stack overflow would generally cause a JavaScript exception and then
leave the stack pointer outside the stack, so that any future attempt to
call into C would fail as well.

To fix this, arrange that the C function for reading data from the save
file calls out to JavaScript.  The JavaScript can then copy just the
requested data into the caller's buffer.  We can't pass a JavaScript
function pointer to C, but since only one file can be loaded at a time,
we can just have a global variable that's the current loading callback.

There might still be a problem if you try to load a stupendously large
file, since I think FileReader.readAsArrayBuffer() reads the whole file
into the browser's RAM.  It works on my laptop with files up to a few
hundred megabytes, though.
</pre>
</div>
</content>
</entry>
<entry>
<title>js: Tolerate the absence of various UI elements from the HTML</title>
<updated>2023-01-19T20:34:48+00:00</updated>
<author>
<name>Ben Harris</name>
<email>bjh21@bjh21.me.uk</email>
</author>
<published>2022-12-11T12:17:54+00:00</published>
<link rel='alternate' type='text/html' href='https://www.franklinwei.com/cgit/puzzles/commit/?id=5ba1bf55600443ca6cdad448aa629ffbce7c4e22'/>
<id>5ba1bf55600443ca6cdad448aa629ffbce7c4e22</id>
<content type='text'>
To make things more consistent, the static buttons now all have their
own variables.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
To make things more consistent, the static buttons now all have their
own variables.
</pre>
</div>
</content>
</entry>
</feed>
