summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md27
-rw-r--r--_posts/dec_29_2025.org61
-rw-r--r--_posts/hello.org46
-rw-r--r--_posts/jul_28_2025.org20
-rw-r--r--_posts/june_08_2025.org25
-rw-r--r--_posts/may_06_2025.org17
-rw-r--r--_posts/sitemap.org37
-rw-r--r--create-update-post.el86
-rw-r--r--index.css21
-rw-r--r--index.html8
-rw-r--r--posts.css58
-rw-r--r--posts.html54
-rw-r--r--posts/dec_29_2025.html122
-rw-r--r--posts/feed.xml50
-rw-r--r--posts/hello.html91
-rw-r--r--posts/jul_28_2025.html55
-rw-r--r--posts/june_08_2025.html67
-rw-r--r--posts/may_06_2025.html55
-rw-r--r--posts/post.css58
-rw-r--r--posts/post.js38
-rw-r--r--posts/sitemap.css169
-rw-r--r--posts/sitemap.html74
-rw-r--r--publish.el114
-rwxr-xr-xresume.css4
-rwxr-xr-xresume.html2
-rw-r--r--resume.mobile.css2
-rw-r--r--themes/.gitignore15
-rw-r--r--themes/Makefile44
-rw-r--r--themes/README.md58
-rw-r--r--themes/compile_commands.json127
-rwxr-xr-xthemes/compile_commands.sh7
-rw-r--r--themes/dist/output.js1669
-rwxr-xr-xthemes/dist/output.wasmbin157180 -> 101714 bytes
-rw-r--r--themes/emscripten.ini14
-rw-r--r--themes/meson.build122
-rw-r--r--themes/src/_shaders/sun.frag40
-rw-r--r--themes/src/_shaders/sun.vert27
-rw-r--r--themes/src/autumn/autumn_theme.cpp (renamed from themes/src/autumn/AutumnTheme.cpp)2
-rw-r--r--themes/src/autumn/autumn_theme.hpp (renamed from themes/src/autumn/AutumnTheme.hpp)6
-rw-r--r--themes/src/autumn/leaf_particle_render.cpp (renamed from themes/src/autumn/LeafParticleRender.cpp)6
-rw-r--r--themes/src/autumn/leaf_particle_render.h (renamed from themes/src/autumn/LeafParticleRender.h)2
-rw-r--r--themes/src/autumn/tree_shape.cpp (renamed from themes/src/autumn/TreeShape.cpp)2
-rw-r--r--themes/src/autumn/tree_shape.h (renamed from themes/src/autumn/TreeShape.h)2
-rw-r--r--themes/src/list.h2
-rw-r--r--themes/src/logger.cpp (renamed from themes/src/Logger.cpp)2
-rw-r--r--themes/src/logger.h (renamed from themes/src/Logger.h)0
-rw-r--r--themes/src/main.cpp21
-rw-r--r--themes/src/main_loop.cpp (renamed from themes/src/MainLoop.cpp)2
-rw-r--r--themes/src/main_loop.h (renamed from themes/src/MainLoop.h)2
-rw-r--r--themes/src/renderer_2d.cpp (renamed from themes/src/Renderer2d.cpp)6
-rw-r--r--themes/src/renderer_2d.h (renamed from themes/src/Renderer2d.h)4
-rw-r--r--themes/src/renderer_3d.cpp (renamed from themes/src/Renderer3d.cpp)8
-rw-r--r--themes/src/renderer_3d.h (renamed from themes/src/Renderer3d.h)0
-rw-r--r--themes/src/shader.cpp (renamed from themes/src/Shader.cpp)2
-rw-r--r--themes/src/shader.h (renamed from themes/src/Shader.h)0
-rw-r--r--themes/src/shaders/sun_frag.cpp40
-rw-r--r--themes/src/shaders/sun_vert.cpp27
-rw-r--r--themes/src/shapes_2d.cpp2
-rw-r--r--themes/src/shapes_2d.h2
-rw-r--r--themes/src/spring/grass_renderer.cpp (renamed from themes/src/spring/GrassRenderer.cpp)4
-rw-r--r--themes/src/spring/grass_renderer.hpp (renamed from themes/src/spring/GrassRenderer.hpp)2
-rw-r--r--themes/src/spring/spring_theme.cpp (renamed from themes/src/spring/SpringTheme.cpp)4
-rw-r--r--themes/src/spring/spring_theme.hpp (renamed from themes/src/spring/SpringTheme.hpp)2
-rw-r--r--themes/src/summer/summer_theme.cpp (renamed from themes/src/summer/SummerTheme.cpp)12
-rw-r--r--themes/src/summer/summer_theme.h (renamed from themes/src/summer/SummerTheme.h)4
-rw-r--r--themes/src/webgl_context.cpp (renamed from themes/src/WebglContext.cpp)2
-rw-r--r--themes/src/webgl_context.h (renamed from themes/src/WebglContext.h)0
-rw-r--r--themes/src/winter/snowflake.cpp (renamed from themes/src/winter/Snowflake.cpp)4
-rw-r--r--themes/src/winter/snowflake.h (renamed from themes/src/winter/Snowflake.h)2
-rw-r--r--themes/src/winter/windfield.cpp (renamed from themes/src/winter/Windfield.cpp)2
-rw-r--r--themes/src/winter/windfield.hpp (renamed from themes/src/winter/Windfield.hpp)0
-rw-r--r--themes/src/winter/winter_theme.cpp (renamed from themes/src/winter/WinterTheme.cpp)4
-rw-r--r--themes/src/winter/winter_theme.hpp (renamed from themes/src/winter/WinterTheme.hpp)4
73 files changed, 2323 insertions, 1347 deletions
diff --git a/README.md b/README.md
index 0a29a8d..97cc897 100644
--- a/README.md
+++ b/README.md
@@ -1,34 +1,27 @@
-# MatthewKosarek.xyz
+# matthewkosarek.xyz
This contains the entirety of https://matthewkosarek.xyz, which is my personal website. Feel free to look around and take any snippets that you like from it!
If you are on Github, this is a mirror of: https://git.matthewkosarek.xyz/matthew_kosarek_xyz/
-## Running
-```sh
-./run_dev.sh
-firefox localhost:8080
-```
## Building
### Themes
-```sh
-# Compile the shaders
-cd themes/src
-node tools/shaders.js
-
-# Build the project
-cd ..
-./compile-commands.sh
-make
-```
+See the [themes subproject](./themes/README.md) for more information on how to build the themes.
### Posts
```sh
./publish.sh
```
-## Upload
+## Running
+```sh
+./run_dev.sh
+firefox localhost:8080
+```
+
+
+## Publishing
```sh
./upload.sh
```
diff --git a/_posts/dec_29_2025.org b/_posts/dec_29_2025.org
new file mode 100644
index 0000000..4318ef6
--- /dev/null
+++ b/_posts/dec_29_2025.org
@@ -0,0 +1,61 @@
+:PROPERTIES:
+:ID: defb26e2-2966-477d-b232-f1fff4066247
+:END:
+
+#+TITLE: Update December 29, 2025
+#+DATE: <2025-12-29 Mon 10:27>
+#+filetags: :update:
+
+* What have I been up to?
+
+2025 has been one busy year for me! I feel as though I've been working on a dozen things at once and have spent much of my working (and personal) days productively. Miracle finally feels like it's getting somewhere fast, the Flutter multi-window work is landing at a solid pace, and Mir is feeling like a truly solid option for Wayland compositor development. I will refrain from speaking too much on the Flutter and Mir work in this post, as those are best left in the hands of Canonical.
+
+* Miracle Update
+Miracle has come a long way this past year. I now feel entirely confident using it as my daily driver, minus a few hiccups that I encounter in-between releases. A lot of great things are cooking for 2026 too,
+
+One of the major upcoming features in Miracle is a *plugin system*. Having a plugin system in Miracle will empower 3rd party authors to extend the compositor in ways that I haven't currently imagined while also providing me with the flexibility to iterate quickly on designs. Many compositors already have plugin systems. For example, GNOME does this via JavaScript and Hyprland does this by dynamically loading shared libraries at runtime (note: this is my understanding as of writing this). Both of these solutions are reasonable, but they come with a few downsides.
+
+The "true" scripting language solution comes with the overhead of shipping a complicated interpreter inside of the compositor. In addition to this, plugin developers are forced to use a particular language, perhaps one that they are unfamiliar with. While you get increased programming flexibility from using a scripting language, you have to balance this with the introduced complexity of that language.
+
+The shared library approach also has its downsides. While dynamically loading a shared library at runtime is lightweight, it prevents users of the compositor from safely running plugins unless they first trust the plugin author. The shared library will be running inside of the same process as your very priveleged compositor. This increases the attack surface to an extent that I would feel uncomfortable shipping to users.
+
+For these reasons, I decided that Miracle plugins will be written in *WebAssembly* with the help of [[https://wasmedge.org][WasmEdge]]. The benefits of this approach are:
+
+1. WebAssembly runs in a lightweight bytecode engine
+2. Many languages can compile down to WebAssembly (Rust will have first-class support to start)
+3. WebAssembly plugins will only be able to access APIs that we provide (reducing the requirement of "trusting" the plugin author)
+
+The WebAssembly modules will implement certain functions by signature. Miracle will search for a particular signature in the module. If that signature is found, Miracle will delegate the function call defined by that signature to the WebAssembly module instead of Miracle's internal implementation. In this way, the WASM plugin will only have the data that it needs to perform an action. It is perfectly isolated from the rest of the process while also running quickly in the bytecode engine. Here is an example of an animation plugin that will linearlly fade in a window:
+
+#+BEGIN_SRC rust
+#[unsafe(no_mangle)]
+pub extern "C" fn animate(
+ data: MiracleAnimationFrameData,
+) -> MiracleAnimationFrameResult {
+
+ let progress = data.runtime_seconds / data.duration_seconds;
+ let opacity = data.opacity_start + (data.opacity_end - data.opacity_start) * progress;
+ MiracleAnimationFrameResult {
+ completed: 0,
+ has_area: 1,
+ area: [data.destination[0], data.destination[1], data.destination[2], data.destination[3]],
+ has_transform: 0,
+ transform: [0.0; 16],
+ has_opacity: 1,
+ opacity,
+ }
+}
+#+END_SRC
+
+While this is still in a prototype phase, the types defined here will be provided by a Rust crate in the future. The system should be very easy to use from Rust.
+
+In parallel with this work, I have been working on improving the *shell authoring* experience in Miracle. The beginning of this has been the initial [[https://github.com/miracle-wm-org/miracle-wm/pull/745][implementation of a background on floating containers]], but work is proceeding to things like built-in context menus, workspace overview modes, and much more. The idea is to have simple, built-in versions for many shell components while allowing users to provide their own custom clients for each shell element. To this end, I have been working on [[https://github.com/miracle-wm-org/miracle.dart][miracle.dart]], which is a Dart API that should enable users to easily interact with Miracle in Flutter and provide Flutter apps as shell elements. This API is still in early stages, however.
+
+At the same time, I have been working on improving the *floating window management* in Miracle. Miracle should be a competent floating window manager for those who need it.
+
+v0.9.0 of Miracle will probably be a long time in the making. My estimate is that early spring will see it released. However, it should be the penultimate release before I am ready to make v1.0.0 the first, official stable release. And who knows - maybe we'll even have a shell to go along with it!
+
+* Conclusion
+2025 has been a whirlwind of a year, and I'm sure that 2026 won't slow down at all for me. A lot of the long term projects that I've been working on are finally coming together, and I feel as though I am on the cusp of making software that I'm truly proud of. On top of that, I am engaged! What a time to be alive :) I hope you all have a lovely New Year with your friends, family, cats, dogs, and everything else.
+
+Keep on making great stuff ✌️
diff --git a/_posts/hello.org b/_posts/hello.org
index 38a4fc7..3a7a9c6 100644
--- a/_posts/hello.org
+++ b/_posts/hello.org
@@ -8,6 +8,10 @@
* TLDR
+:PROPERTIES:
+:ID: b15846be-b33b-4233-86b1-b56a76622478
+:PUBDATE: 2025-05-06 Tue 14:32
+:END:
- Create a new folder
- Put [[https://raw.githubusercontent.com/mattkae/matthewkosarek-xyz/master/index.css][index.css]], [[https://raw.githubusercontent.com/mattkae/matthewkosarek-xyz/master/publish.el][publish.el]], and [[https://github.com/mattkae/matthewkosarek-xyz/blob/master/publish.sh][publish.sh]] in the folder
- Create a folder called ~_posts~ (this is where blog posts are written)
@@ -19,6 +23,10 @@
- Navigate to ~localhost:8080/posts/sitemap.html~ to see your posts
* Introduction
+:PROPERTIES:
+:ID: 39f1ddfb-72e3-4894-9c4c-b160c6e224fe
+:PUBDATE: 2025-05-06 Tue 14:32
+:END:
I've recently fallen in love with ~org-mode~, specifically when I use it with [[https://www.orgroam.com/][org-roam]]. I find the whole workflow of creating, tagging, and - later on - searching for information on my computer to be very elegant. On top of that, now that I have the time, I want to begin writing blog posts to better work out my thoughts. With both of these things in mind, I am again turning to the universal tool for human prospering: ~org-mode~. This time, I want to see how it can help me turn a simple org file into a blog post on my website. My requirements are:
1. Org files must get published to HTML files in a particular format with a preset stylesheet
@@ -33,6 +41,10 @@ And that's pretty much it for now. Without further ado, let's jump into getting
(Note: I will be heavily inspired by [[https://systemcrafters.net/publishing-websites-with-org-mode/building-the-site/#creating-the-build-script][this post from System Crafters]]. I highly recommend that you read his post first before you follow my post, as he provides more details about the ~org-publish-project-alist~ command than I am willing to go into in this post.)
* Basic HTML File
+:PROPERTIES:
+:ID: fbd6c42a-45cf-4c16-81fc-e99df88e2389
+:PUBDATE: 2025-05-06 Tue 14:32
+:END:
As a pilot, we are going to use this org file that I am currently writing (~hello.org~) as our guinea pig. The goal is to have this org file be our very first blog post.
Emacs ships with org export goodies out of the box via the ~ox-publish.el~ package (which you can find [[https://github.com/emacs-mirror/emacs/blob/master/lisp/org/ox-publish.el][here]]). In our case, we will want to use this package to write a script that exports all the ~./_posts/*.org~ files and outputs them to a corresponding ~./posts/*.html~. Leaning heavily on the System Crafters information, we can create a file called ~publish.el~ and write the following inside of it:
@@ -62,6 +74,10 @@ emacs -Q --script publish.el
We then do a ~chmod +x publish.sh~ to make it an executable and run it with ~./publish.sh~. If everything went according to plan, we should see a new file at ~posts/hello.html~.
* Disabling features that we don't want
+:PROPERTIES:
+:ID: 393f268d-3200-4857-9c78-097468330437
+:PUBDATE: 2025-05-06 Tue 14:32
+:END:
The next thing will be to remove some of the generated items that I didn't ask for, namely the table of contents, author, section numbers, creation time stamp, and the validation link.
#+BEGIN_SRC emacs-lisp
@@ -87,6 +103,10 @@ The next thing will be to remove some of the generated items that I didn't ask f
#+END_SRC
* Styling & Code Highlighting
+:PROPERTIES:
+:ID: 25bb1f38-ad48-4513-9238-229955db4328
+:PUBDATE: 2025-05-06 Tue 14:32
+:END:
Next thing on our list is custom styling. This can be achieved by first installing the ~htmlize~ package from ~melpa~ / ~elpa~. The EmacsWiki describes this as "a package for exporting the contents of an Emacs buffer to HTML while respecting display properties such as colors, fonts, underlining, invisibility, etc" ([[https://www.emacswiki.org/emacs/Htmlize][reference]]). If used "out-of-the-box", the buffer will be exported to HTML with all of the styles inlined (e.g. if you underline something in your org file, you will generate a ~<span style="text-decoration: underline">...</span>~). However, we are more interested in styling everything by ourselves: we don't want ~htmlize~ making assumptions about what underlining means to us! Luckily, ~htmlize~ gives us the option to export with class names instead of inline styles so that we can specify each style for ourselves.
#+BEGIN_SRC emacs-lisp
@@ -241,6 +261,10 @@ Finally, we need to tell org mode to include our two CSS files when the page is
If we run the publish again, we can see that we have full styling on our code snippets and everything else on our website.
* Images
+:PROPERTIES:
+:ID: 1ca21e46-c212-41ad-b9a7-34fa9909014a
+:PUBDATE: 2025-05-06 Tue 14:32
+:END:
Our first two criteria have been met! Next on the list is solving images. As an example, let's use this [[/_posts/assets/squirrel.jpg][squirrel image]] that I found online with an open source license. The ideal situation would be:
1. The squirrel image lives closely to this org document (~hello.org~)
@@ -273,6 +297,10 @@ That's all there is to it! There are simpler ways as well, but that should do it
* Creation Date
+:PROPERTIES:
+:ID: f061a150-b8bc-41cf-8c50-77338ac47c80
+:PUBDATE: 2025-05-06 Tue 14:32
+:END:
Let's add the creation date below the title next. To start, we will modify the publish command to remove the title (~:with-title nil~) and, in its place, show a preamble bit of HTML that contains a formatted ~div~ with the title and the "last modified" span.z
#+BEGIN_SRC emacs-lisp
@@ -317,6 +345,10 @@ If you want to see the full list of which values can be included in the ~html-pr
Note that the downside of this is that the created date will change whenever you next save the buffer. This isn't a huge deal for my purposes, but you may need to come up with a more sophisticated mechanism for the exact "creation" date for your use case.
* Generating the Directory
+:PROPERTIES:
+:ID: e2226b35-d9ed-4758-8471-71a5c82331f1
+:PUBDATE: 2025-05-06 Tue 14:32
+:END:
For every org file in my ~_posts~ folder, I would like to create a link to the generated HTML file at the ~/posts.html~ page of my website. You can think of this as the "directory" of all posts. My criteria is:
1. Posts should appear in order from newest to oldest
2. Posts should be searchable by tags (covered in the next section)
@@ -360,6 +392,9 @@ If you open the ~sitemap.html~ file in your browser, you will see a bulleted lis
From here, you may customize it however you like. The following are my customizations.
** Sitemap Title
+:PROPERTIES:
+:ID: 47f964f9-15d7-4370-9483-a76dafd79f4c
+:END:
I changed the title to "Matthew's Blog Posts".
#+BEGIN_SRC emacs-lisp
@@ -379,6 +414,9 @@ I changed the title to "Matthew's Blog Posts".
** Format blog entries in the list
+:PROPERTIES:
+:ID: a1c2f9b9-085a-4540-a9f1-7f7388334191
+:END:
I like to include the creation date on the blog posts. To do this, we can use ~org-publish-find-property~ to find the date property of the org file. Afterward, we can format a string that includes our formatted timestamp and the ~org-publish-sitemap-default-entry~, which is just a link with the title of the post.
#+BEGIN_SRC emacs-lisp
(defun get-org-file-title(entry style project)
@@ -395,6 +433,10 @@ I like to include the creation date on the blog posts. To do this, we can use ~o
#+END_SRC
* Tags & Filtering
+:PROPERTIES:
+:ID: bb278140-4b5c-4025-a267-693820251199
+:PUBDATE: 2025-05-06 Tue 14:32
+:END:
I use [[https://www.orgroam.com/][Org-roam]] for all of my note-taking and, in the next blog post, I plan to demonstrate how I will hook up my Org-roam note-taking workflow to my blogging. In the meantime, just know that we can add tags to the top of our org files like this:
#+BEGIN_SRC org
@@ -585,4 +627,8 @@ Finally, let's append the following to ~posts/posts.css~ so that our tag list is
#+END_SRC
* Conclusion
+:PROPERTIES:
+:ID: 0612cef6-bdb4-4f43-8ba7-86e2c22120fd
+:PUBDATE: 2025-05-06 Tue 14:32
+:END:
There are many more customizations that I plan to do on this system in the future, but I plan to leave this for now so that I can actually get to some blogging. I will proofread and fix my mistakes as time goes on, but this should be a good jumping off point for anyone interested in using org for their own blogging system.
diff --git a/_posts/jul_28_2025.org b/_posts/jul_28_2025.org
new file mode 100644
index 0000000..fa1bf08
--- /dev/null
+++ b/_posts/jul_28_2025.org
@@ -0,0 +1,20 @@
+:PROPERTIES:
+:ID: 1c09d4f3-1f72-49f6-930d-e2d92473a979
+:END:
+
+#+TITLE: Update July 28, 2025
+#+DATE: <2025-07-28 Sun 17:30>
+#+filetags: :update:
+
+* What have I been up to?
+Whoops! I missed this month's update by a /long/ shot, but I still want to get it out there before the end of the month.
+
+This month was busy busy. I released [[https://github.com/miracle-wm-org/miracle-wm/releases/tag/v0.6.0][v0.6.0 of miracle-wm]] which adds a bunch of new features, go and check it out if you haven't already! I am nearly finished with the sway/i3 IPC support and have big plans to wrap that up before the middle of August. There really isn't much more to go on that front so I might as well close that chapter. On top of that, the more interesting features of miracle that I have planned (e.g. some built-in shell integrations) are motivating me to wrap up the boring parts first.
+
+Aside from miracle, I've continued to land a bunch of things in Mir recently around accessibility. The magnifier work is finally done, so go check that out if you use that feature in your day-to-day! I also did a /ton of work/ to fix screenshooting on rotated displays. We were ignoring [[https://wayland.app/protocols/wayland#wl_surface:request:set_buffer_transform][wl_surface::set_buffer_transform]] in a big way. Now that we're not doing that, your screenshots should like perfect every time!
+
+Last but not least, the team has been making waves implementing multi-window in the Flutter toolkit. That's some really interesting and exciting work, so stay tuned for that if you're a Flutter developer!
+
+That's all I got! I will make another post shortly, maybe once miracle v0.7.0 is out. Have a great rest of your summer/winter πŸͺ
+
+
diff --git a/_posts/june_08_2025.org b/_posts/june_08_2025.org
new file mode 100644
index 0000000..0a9bc8f
--- /dev/null
+++ b/_posts/june_08_2025.org
@@ -0,0 +1,25 @@
+:PROPERTIES:
+:ID: c397a1c8-5980-419d-88d4-a3c9782343d5
+:END:
+#+TITLE: Update June 08, 2025
+#+DATE: <2025-06-08 Sun 15:30>
+#+filetags: :update:
+
+* What have I been up to?
+Another month has gone by, so I guess it's time to see what I've been up to.
+
+Canonical hosted our company's sprint in Frankfurt, Germany during the month of May. It was a very productive time for the whole team. I always enjoy seeing everyone in person, talking through big issues face-to-face, and exploring a new city. I also got to spend some time in Zurich, Switzerland before the sprint began. Zurich is pretty awesome πŸ”οΈ!
+
+In miracle-wm, I have been going down quite the rabbit-holeπŸ‡! What began as a plan to fix the remaining warnings in the source code has snowballed into me implemnenting the rest of the IPC mechanism and testing the entire thing. This has been no small feat, as both sway and i3 implement a _lot_ of different commands, and a failure to implement any one of them often leads to half-broken clients. I've had to make some "executive" decisions to ignore parts of the protocol that I deem irrelevant now (especially many of the X-specific bits), but it is a mostly compatible implementation. The good news is that I've nearly completed this journey and should be ready to release version ~0.6.0~ some time in the middle of June.
+
+Miracle is getting closer-and-closer to my vision of it every day. The only problem right now is finding the bandwidth to implement everything that I have in my head :)
+
+On the Mir side of things, I am still implementing the magnifier glass accessibility feature from before, but with a much-improved technical direction that we arrived at during our time in Frankfurt. Unfortunately, this required a quick detour to properly implement the ~overlay_cursor~ flag of [[https://wayland.app/protocols/wlr-screencopy-unstable-v1#zwlr_screencopy_manager_v1:request:capture_output:arg:overlay_cursor][zwlr_screencopy_manager_v1::capture_output_region]], as both screencopy and magnification rely on this same code path. The good news is that I'm quite close on this and it should be landing in full any day now 🀞
+
+I also fixed [[https://github.com/canonical/mir/pull/3968][this very breaking bug]] that was actively preventing miracle from rendering on my second monitor, so that's good.
+
+On addition to these two projects, I am also reinvolving myself in the Flutter multi-window work. For those who don't know, we're trying to make it so that the Flutter toolkit can render to multiple surfaces. This is no small feat, as Flutter was originally written with the assumption that only a single "view" would ever be drawn too. However we've managed to make some great progress on it thus far, and we're very excited to land the first pull request imminently with the help of the folks over a Google!
+
+I hope you're having a great and productive summer 😎
+
+
diff --git a/_posts/may_06_2025.org b/_posts/may_06_2025.org
new file mode 100644
index 0000000..2b90752
--- /dev/null
+++ b/_posts/may_06_2025.org
@@ -0,0 +1,17 @@
+:PROPERTIES:
+:ID: fe8e0951-3201-496f-aa0b-21578e269036
+:END:
+#+TITLE: Update May 06, 2025
+#+DATE: <2025-05-06 Tue 17:00>
+#+filetags: :update:
+
+* What have I been up to?
+I've been meaning to do these little blog-post type updates for a while, and I figured now is as good a time as any. So let's start :)
+
+In the world of miracle-wm, I've been hard at work writing a new settings application for the compositor called [[https://github.com/miracle-wm-org/miracle-settings][miracle-settings]]. While the application is written in Flutter, the logic behind the application is entirely implemented in ~libmiracle-wm-config.so~, a new library that will ship with miracle as part of ~v0.6.0~. If Flutter isn't your cup of tea, you should be able to implement your own settings app for miracle in the language/toolkit of your choice by simply binding to the C (or C++!) API.
+
+I also implemented [[https://wayland.app/protocols/wlr-output-management-unstable-v1][wlr-output-management]] in miracle and changed how we do display configuration in a big way. The display configuration will now always be loaded from ~$HOME/.config/miracle-wm/display.yaml~. Users should now be able to use apps like [[https://github.com/artizirk/wdisplays][wdisplays]] to change the output configuration at runtime, which is pretty cool!
+
+On the Mir project, I've been working on accessibility features (e.g. magnification) in addition to exposing some facilities for compositor authors to write end-to-end tests for Mir-based compositors. These new testing facilities should help us write tests in miracle in a big way.
+
+Also - my cat who ate a sewing needle last year has turned two! 🐱
diff --git a/_posts/sitemap.org b/_posts/sitemap.org
index b5e96d9..202c055 100644
--- a/_posts/sitemap.org
+++ b/_posts/sitemap.org
@@ -1,12 +1,43 @@
#+TITLE: Matthew's Blog Posts
-#+DATE: 2024-05-01 at 14:07
+#+DATE: 2025-12-29 at 13:03
#+HTML_LINK_HOME: /
#+HTML_LINK_UP: /
-- [[file:hello.org][Hello, Org]] created on June 20, 2023
+- [[file:dec_29_2025.org][Update December 29, 2025]]
+ #+begin_sitemap_date
+ December 29, 2025
+ #+end_sitemap_date
+ #+begin_sitemap_tag
+ update
+ #+end_sitemap_tag
+- [[file:jul_28_2025.org][Update July 28, 2025]]
+ #+begin_sitemap_date
+ July 28, 2025
+ #+end_sitemap_date
+ #+begin_sitemap_tag
+ update
+ #+end_sitemap_tag
+- [[file:june_08_2025.org][Update June 08, 2025]]
+ #+begin_sitemap_date
+ June 08, 2025
+ #+end_sitemap_date
+ #+begin_sitemap_tag
+ update
+ #+end_sitemap_tag
+- [[file:may_06_2025.org][Update May 06, 2025]]
+ #+begin_sitemap_date
+ May 06, 2025
+ #+end_sitemap_date
+ #+begin_sitemap_tag
+ update
+ #+end_sitemap_tag
+- [[file:hello.org][Hello, Org]]
+ #+begin_sitemap_date
+ June 20, 2023
+ #+end_sitemap_date
#+begin_sitemap_tag
technology,home
- #+end_sitemap_tag \ No newline at end of file
+ #+end_sitemap_tag
diff --git a/create-update-post.el b/create-update-post.el
new file mode 100644
index 0000000..e8253d9
--- /dev/null
+++ b/create-update-post.el
@@ -0,0 +1,86 @@
+(defun mk/create-update-post ()
+ "Create a new 'Update' post for today in the _posts folder."
+ (interactive)
+ (let* ((date (current-time))
+ (month-name (downcase (format-time-string "%b" date)))
+ (day (format-time-string "%d" date))
+ (year (format-time-string "%Y" date))
+ (full-month (format-time-string "%B" date))
+ (weekday (format-time-string "%a" date))
+ (time (format-time-string "%H:%M" date))
+ (filename (format "%s_%s_%s.org" month-name day year))
+ (project-root (locate-dominating-file default-directory ".git"))
+ (posts-dir (expand-file-name "_posts" project-root))
+ (filepath (expand-file-name filename posts-dir))
+ (uuid (org-id-uuid))
+ (org-date (format "<%s-%s-%s %s %s>" year (format-time-string "%m" date) day weekday time))
+ (title (format "Update %s %s, %s" full-month day year)))
+
+ ;; Check if file already exists
+ (if (file-exists-p filepath)
+ (message "Post file already exists: %s" filepath)
+
+ ;; Create the file with the template
+ (with-temp-file filepath
+ (insert ":PROPERTIES:\n")
+ (insert (format ":ID: %s\n" uuid))
+ (insert ":END:\n")
+ (insert "\n")
+ (insert (format "#+TITLE: %s\n" title))
+ (insert (format "#+DATE: %s\n" org-date))
+ (insert "#+filetags: :update:\n")
+ (insert "\n")
+ (insert "* What have I been up to?\n")
+ (insert "\n"))
+
+ ;; Open the file
+ (find-file filepath)
+ (goto-char (point-max))
+ (message "Created new update post: %s" filepath))))
+
+(defun mk/create-regular-post ()
+ "Create a new regular blog post with a custom title in the _posts folder."
+ (interactive)
+ (let* ((title (read-string "Post title: "))
+ (date (current-time))
+ (year (format-time-string "%Y" date))
+ (month (format-time-string "%m" date))
+ (day (format-time-string "%d" date))
+ (month-name (downcase (format-time-string "%b" date)))
+ (weekday (format-time-string "%a" date))
+ (time (format-time-string "%H:%M" date))
+ (filename-base (replace-regexp-in-string "[^a-z0-9_]+" "_" (downcase title)))
+ (filename (format "%s_%s_%s.org" month-name day year))
+ (project-root (locate-dominating-file default-directory ".git"))
+ (posts-dir (expand-file-name "_posts" project-root))
+ (filepath (expand-file-name filename posts-dir))
+ (uuid (org-id-uuid))
+ (org-date (format "<%s-%s-%s %s %s>" year month day weekday time)))
+
+ ;; Check if file already exists
+ (if (file-exists-p filepath)
+ (message "Post file already exists: %s" filepath)
+
+ ;; Create the file with the template
+ (with-temp-file filepath
+ (insert ":PROPERTIES:\n")
+ (insert (format ":ID: %s\n" uuid))
+ (insert ":END:\n")
+ (insert (format "#+TITLE: %s\n" title))
+ (insert (format "#+DATE: %s\n" org-date))
+ (insert "#+filetags: :TODO:\n")
+ (insert "\n")
+ (insert "\n")
+ (insert "\n")
+ (insert "* Introduction\n")
+ (insert ":PROPERTIES:\n")
+ (insert (format ":ID: %s\n" (org-id-uuid)))
+ (insert ":END:\n")
+ (insert "\n"))
+
+ ;; Open the file
+ (find-file filepath)
+ (goto-char (point-max))
+ (message "Created new regular post: %s" filepath))))
+
+(provide 'create-update-post)
diff --git a/index.css b/index.css
index 385fd5c..c07ffa8 100644
--- a/index.css
+++ b/index.css
@@ -2,12 +2,11 @@ body {
width: 60vw;
height: 100vh;
font-family: "Noto Sans", 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
- font-size: 16px;
+ font-size: 14px;
background-color: transparent;
color: black;
padding: 0;
margin: auto;
- line-height: 1.5;
}
header {
@@ -23,7 +22,7 @@ header > h1 {
}
header > nav {
- margin-top: 0.5rem;
+ margin-top: 2rem;
margin-bottom: 1rem;
padding-left: 0.25rem;
}
@@ -57,7 +56,7 @@ h1 {
}
h2 {
- font-size: 1.875rem;
+ font-size: 1.5rem;
margin-bottom: 0rem;
margin-top: 2rem;
}
@@ -172,14 +171,14 @@ input:focus {
}
/* Theme Selector */
-#theme_selector {;
+#theme_selector {
display: flex;
align-items: center;
justify-content: center;
bottom: 1rem;
- pointer-events: all;
- opacity: 1;
- transition: opacity 150ms linear;
+ pointer-events: all;
+ opacity: 1;
+ transition: opacity 150ms linear;
gap: 2rem;
}
@@ -238,6 +237,12 @@ only screen and (max-width:1440px) {
only screen and (max-width:960px) {
#theme_selector {
padding-bottom: 2rem !important;
+ flex-direction: column;
+ gap: 1rem;
+ }
+
+ #theme_selector > button {
+ width: 100%;
}
.image_item_expanded_container > .image_item {
diff --git a/index.html b/index.html
index 883f230..23af4bc 100644
--- a/index.html
+++ b/index.html
@@ -5,6 +5,7 @@
<meta charset="utf-8">
<link rel="stylesheet" href="/index.css">
<title>Matthew Kosarek</title>
+ <link href='https://fonts.googleapis.com/css?family=Noto Sans' rel='stylesheet'>
<link rel="shortcut icon" href="/favicon/favicon.ico" type="image/x-icon">
<meta name="description" content="The personal website of Matthew kosarek">
</head>
@@ -15,7 +16,6 @@
</div>
<header>
- <h1>Matthew Kosarek</h1>
<nav>
<ul>
<li><a href='/'>&#127969; Home</a></li>
@@ -62,9 +62,9 @@
<h2>About Me</h2>
<p>
Hi there 🌊 My name is Matthew Kosarek.
- I am a computer programmer from northern New Jersey and I currently live Philadelphia, PA. I work at Canonical on the
- <a href="https://github.com/canonical/mir">Mir display server</a> and I am currently building
- <a href="https://github.com/mattkae/miracle-wm">miracle-wm</a>, a Mir-based tiling window manager.
+ I am a computer programmer from northern New Jersey and I currently live in Philadelphia, PA. I work at Canonical on
+ <a href="https://github.com/canonical/mir">Mir</a> and I am currently building
+ <a href="https://github.com/miracle-wm-org/miracle-wm">miracle-wm</a>, a Mir-based tiling window manager.
</p>
</section>
<section>
diff --git a/posts.css b/posts.css
deleted file mode 100644
index f07381c..0000000
--- a/posts.css
+++ /dev/null
@@ -1,58 +0,0 @@
-#tag_list {
- width: 65%;
- display: grid;
- margin: auto;
- grid-template-columns: repeat(3, 1fr);
- column-gap: 8px;
- row-gap: 16px
-}
-
-@media (max-width: 767.98px) {
- #tag_list {
- width: 100%;
- }
-}
-
-#tag_list button {
- padding: 12px 0px;
- cursor: pointer;
- border: 1px solid transparent;
- border-radius: 3px;
- font-family: inherit;
- font-size: 14px;
- width: 100%;
- height: 100%;
- color: black;
- background-color: transparent;
- border: 2px solid black;
-}
-
-#tag_list button:hover {
- background-color: lightgray;
-}
-
-#post_list {
- display: flex;
- flex-direction: column;
-}
-
-article {
- width: 100%;
- text-align: left;
- padding-top: 1rem;
-}
-
-article > h2 {
- color: black;
- padding: 0;
- margin: 0;
-}
-
-article > h3 {
- margin: 0;
- margin-top: 12px;
- color: gray;
- font-size: 12px;
- font-weight: normal;
- padding: 0;
-}
diff --git a/posts.html b/posts.html
deleted file mode 100644
index d63d29a..0000000
--- a/posts.html
+++ /dev/null
@@ -1,54 +0,0 @@
-
-<!DOCTYPE html>
-<html lang="en">
- <head>
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <meta charset="utf-8">
- <link rel="stylesheet" href="/index.css">
- <link rel="stylesheet" href="/posts.css">
- <title>Matthew Kosarek</title>
- <link rel="shortcut icon" href="/favicon/favicon.ico" type="image/x-icon">
- </head>
- <body>
- <header>
- <h1>Matthew Kosarek</h1>
- <nav>
- <ul>
- <li><a href='/'>&#127969; Home</a></li>
- <li><a href='/resume.html'>&#128216; CV</a></li>
- <li><a href='/posts.html'>&#128221; Posts</a></li>
- </ul>
- </nav>
- </header>
-
- <section>
- <h2>Tags</h2>
-
- <div id='tag_list'>
-
- <a href="/posts/tag_personal.html"><button id="personal">Personal πŸ‘¨</button></a>
-
-
- <a href="/posts/tag_programming.html"><button id="programming">Programming πŸ’»</button></a>
-
-
- <a href="/posts/tag_books.html"><button id="books">Books πŸ“–</button></a>
-
-
- <a href="/posts/tag_food.html"><button id="food">Food 🍲</button></a>
-
- </div>
- </section
-
- <section>
- <h2>Posts</h2>
- <ul id='post_list'>
-
- <li><a href="/posts/hello_world.html">Hello, World!</a></li>
-
- <li><a href="/posts/plato_1.html">Euthyphro: the pious and the god-loved</a></li>
-
- </ul>
- </section>
- </body>
-</html>
diff --git a/posts/dec_29_2025.html b/posts/dec_29_2025.html
new file mode 100644
index 0000000..9e4ee07
--- /dev/null
+++ b/posts/dec_29_2025.html
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title>Update December 29, 2025</title>
+<meta name="generator" content="Org Mode" />
+
+<link rel="stylesheet" href="/index.css" />
+<link rel="stylesheet" href="/posts/post.css" />
+<link rel="shortcut icon" href="/favicon/favicon.ico" type="image/x-icon">
+<script src='/posts/post.js'></script>
+</head>
+<body>
+<div id="org-div-home-and-up">
+ <a accesskey="h" href="/posts/sitemap.html"> UP </a>
+ <a accesskey="H" href="/"> HOME </a>
+</div><div id="preamble" class="status">
+
+ <div class="org-article-title">
+ <h1>Update December 29, 2025</h1>
+ <span>Last modified: 2025-12-29 Mon 10:27</span>
+ <span><a href="/posts/feed.xml">RSS Feed</a></span>
+ </div>
+</div>
+<div id="content" class="content">
+<div id="outline-container-orgbac5ed0" class="outline-2">
+<h2 id="orgbac5ed0">What have I been up to?</h2>
+<div class="outline-text-2" id="text-orgbac5ed0">
+<p>
+2025 has been one busy year for me! I feel as though I've been working on a dozen things at once and have spent much of my working (and personal) days productively. Miracle finally feels like it's getting somewhere fast, the Flutter multi-window work is landing at a solid pace, and Mir is feeling like a truly solid option for Wayland compositor development. I will refrain from speaking too much on the Flutter and Mir work in this post, as those are best left in the hands of Canonical.
+</p>
+</div>
+</div>
+<div id="outline-container-orgb1a98f7" class="outline-2">
+<h2 id="orgb1a98f7">Miracle Update</h2>
+<div class="outline-text-2" id="text-orgb1a98f7">
+<p>
+Miracle has come a long way this past year. I now feel entirely confident using it as my daily driver, minus a few hiccups that I encounter in-between releases. A lot of great things are cooking for 2026 too,
+</p>
+
+<p>
+One of the major upcoming features in Miracle is a <b>plugin system</b>. Having a plugin system in Miracle will empower 3rd party authors to extend the compositor in ways that I haven't currently imagined while also providing me with the flexibility to iterate quickly on designs. Many compositors already have plugin systems. For example, GNOME does this via JavaScript and Hyprland does this by dynamically loading shared libraries at runtime (note: this is my understanding as of writing this). Both of these solutions are reasonable, but they come with a few downsides.
+</p>
+
+<p>
+The "true" scripting language solution comes with the overhead of shipping a complicated interpreter inside of the compositor. In addition to this, plugin developers are forced to use a particular language, perhaps one that they are unfamiliar with. While you get increased programming flexibility from using a scripting language, you have to balance this with the introduced complexity of that language.
+</p>
+
+<p>
+The shared library approach also has its downsides. While dynamically loading a shared library at runtime is lightweight, it prevents users of the compositor from safely running plugins unless they first trust the plugin author. The shared library will be running inside of the same process as your very priveleged compositor. This increases the attack surface to an extent that I would feel uncomfortable shipping to users.
+</p>
+
+<p>
+For these reasons, I decided that Miracle plugins will be written in <b>WebAssembly</b> with the help of <a href="https://wasmedge.org">WasmEdge</a>. The benefits of this approach are:
+</p>
+
+<ol class="org-ol">
+<li>WebAssembly runs in a lightweight bytecode engine</li>
+<li>Many languages can compile down to WebAssembly (Rust will have first-class support to start)</li>
+<li>WebAssembly plugins will only be able to access APIs that we provide (reducing the requirement of "trusting" the plugin author)</li>
+</ol>
+
+<p>
+The WebAssembly modules will implement certain functions by signature. Miracle will search for a particular signature in the module. If that signature is found, Miracle will delegate the function call defined by that signature to the WebAssembly module instead of Miracle's internal implementation. In this way, the WASM plugin will only have the data that it needs to perform an action. It is perfectly isolated from the rest of the process while also running quickly in the bytecode engine. Here is an example of an animation plugin that will linearlly fade in a window:
+</p>
+
+<div class="org-src-container">
+<pre class="src src-rust"><span class="org-preprocessor">#[</span><span class="org-rust-unsafe">unsafe</span><span class="org-preprocessor">(no_mangle)]</span>
+<span class="org-keyword">pub</span> <span class="org-keyword">extern</span> <span class="org-string">"C"</span> <span class="org-keyword">fn</span> <span class="org-function-name">animate</span>(
+ <span class="org-variable-name">data</span>: <span class="org-type">MiracleAnimationFrameData</span>,
+) -&gt; <span class="org-type">MiracleAnimationFrameResult</span> {
+
+ <span class="org-keyword">let</span> <span class="org-variable-name">progress</span> = data.runtime_seconds / data.duration_seconds;
+ <span class="org-keyword">let</span> <span class="org-variable-name">opacity</span> = data.opacity_start + (data.opacity_end - data.opacity_start) * progress;
+ <span class="org-type">MiracleAnimationFrameResult</span> {
+ <span class="org-variable-name">completed</span>: 0,
+ <span class="org-variable-name">has_area</span>: 1,
+ <span class="org-variable-name">area</span>: [data.destination[0], data.destination[1], data.destination[2], data.destination[3]],
+ <span class="org-variable-name">has_transform</span>: 0,
+ <span class="org-variable-name">transform</span>: [0.0; 16],
+ <span class="org-variable-name">has_opacity</span>: 1,
+ opacity,
+ }
+}
+</pre>
+</div>
+
+<p>
+While this is still in a prototype phase, the types defined here will be provided by a Rust crate in the future. The system should be very easy to use from Rust.
+</p>
+
+<p>
+In parallel with this work, I have been working on improving the <b>shell authoring</b> experience in Miracle. The beginning of this has been the initial <a href="https://github.com/miracle-wm-org/miracle-wm/pull/745">implementation of a background on floating containers</a>, but work is proceeding to things like built-in context menus, workspace overview modes, and much more. The idea is to have simple, built-in versions for many shell components while allowing users to provide their own custom clients for each shell element. To this end, I have been working on <a href="https://github.com/miracle-wm-org/miracle.dart">miracle.dart</a>, which is a Dart API that should enable users to easily interact with Miracle in Flutter and provide Flutter apps as shell elements. This API is still in early stages, however.
+</p>
+
+<p>
+At the same time, I have been working on improving the <b>floating window management</b> in Miracle. Miracle should be a competent floating window manager for those who need it.
+</p>
+
+<p>
+v0.9.0 of Miracle will probably be a long time in the making. My estimate is that early spring will see it released. However, it should be the penultimate release before I am ready to make v1.0.0 the first, official stable release. And who knows - maybe we'll even have a shell to go along with it!
+</p>
+</div>
+</div>
+<div id="outline-container-org5047f79" class="outline-2">
+<h2 id="org5047f79">Conclusion</h2>
+<div class="outline-text-2" id="text-org5047f79">
+<p>
+2025 has been a whirlwind of a year, and I'm sure that 2026 won't slow down at all for me. A lot of the long term projects that I've been working on are finally coming together, and I feel as though I am on the cusp of making software that I'm truly proud of. On top of that, I am engaged! What a time to be alive :) I hope you all have a lovely New Year with your friends, family, cats, dogs, and everything else.
+</p>
+
+<p>
+Keep on making great stuff ✌️
+</p>
+</div>
+</div>
+</div>
+</body>
+</html>
diff --git a/posts/feed.xml b/posts/feed.xml
new file mode 100644
index 0000000..affbb37
--- /dev/null
+++ b/posts/feed.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rss version="2.0">
+ <channel>
+ <title>Matthew&apos;s Blog</title>
+ <link>https://matthewkosarek.xyz/</link>
+ <description>The RSS feed for Matthew Kosarek's Blog</description>
+ <language>en-us</language>
+ <lastBuildDate>Mon, 29 December 2025 13:03:00 -0400</lastBuildDate>
+
+ <item>
+ <title>Update December 29, 2025</title>
+ <link>https://matthewkosarek.xyz/posts/dec_29_2025.html</link>
+ <guid>https://matthewkosarek.xyz/posts/dec_29_2025.html</guid>
+ <pubDate>Mon, 29 December 2025 10:27:00 -0400</pubDate>
+ <description>Update December 29, 2025</description>
+ </item>
+
+ <item>
+ <title>Update July 28, 2025</title>
+ <link>https://matthewkosarek.xyz/posts/jul_28_2025.html</link>
+ <guid>https://matthewkosarek.xyz/posts/jul_28_2025.html</guid>
+ <pubDate>Mon, 28 July 2025 17:30:00 -0400</pubDate>
+ <description>Update July 28, 2025</description>
+ </item>
+
+ <item>
+ <title>Update June 08, 2025</title>
+ <link>https://matthewkosarek.xyz/posts/june_08_2025.html</link>
+ <guid>https://matthewkosarek.xyz/posts/june_08_2025.html</guid>
+ <pubDate>Sun, 08 June 2025 15:30:00 -0400</pubDate>
+ <description>Update June 08, 2025</description>
+ </item>
+
+ <item>
+ <title>Update May 06, 2025</title>
+ <link>https://matthewkosarek.xyz/posts/may_06_2025.html</link>
+ <guid>https://matthewkosarek.xyz/posts/may_06_2025.html</guid>
+ <pubDate>Tue, 06 May 2025 17:00:00 -0400</pubDate>
+ <description>Update May 06, 2025</description>
+ </item>
+
+ <item>
+ <title>Hello, Org</title>
+ <link>https://matthewkosarek.xyz/posts/hello.html</link>
+ <guid>https://matthewkosarek.xyz/posts/hello.html</guid>
+ <pubDate>Tue, 20 Jun 2023 11:45:00 -0400</pubDate>
+ <description>Hello, Org</description>
+ </item>
+ </channel>
+</rss>
diff --git a/posts/hello.html b/posts/hello.html
index 8469f4e..992e0c7 100644
--- a/posts/hello.html
+++ b/posts/hello.html
@@ -22,15 +22,13 @@
<div class="org-article-title">
<h1>Hello, Org</h1>
<span>Last modified: 2023-06-20 Tue 11:45</span>
+ <span><a href="/posts/feed.xml">RSS Feed</a></span>
</div>
</div>
<div id="content" class="content">
-
-
-
-<div id="outline-container-org8ca89e4" class="outline-2">
-<h2 id="org8ca89e4">TLDR</h2>
-<div class="outline-text-2" id="text-org8ca89e4">
+<div id="outline-container-orgd5cccc4" class="outline-2">
+<h2 id="orgd5cccc4">TLDR</h2>
+<div class="outline-text-2" id="text-orgd5cccc4">
<ul class="org-ul">
<li>Create a new folder</li>
<li>Put <a href="https://raw.githubusercontent.com/mattkae/matthewkosarek-xyz/master/index.css">index.css</a>, <a href="https://raw.githubusercontent.com/mattkae/matthewkosarek-xyz/master/publish.el">publish.el</a>, and <a href="https://github.com/mattkae/matthewkosarek-xyz/blob/master/publish.sh">publish.sh</a> in the folder</li>
@@ -44,10 +42,9 @@
</ul>
</div>
</div>
-
-<div id="outline-container-orgdbe8752" class="outline-2">
-<h2 id="orgdbe8752">Introduction</h2>
-<div class="outline-text-2" id="text-orgdbe8752">
+<div id="outline-container-org61c4348" class="outline-2">
+<h2 id="org61c4348">Introduction</h2>
+<div class="outline-text-2" id="text-org61c4348">
<p>
I've recently fallen in love with <code>org-mode</code>, specifically when I use it with <a href="https://www.orgroam.com/">org-roam</a>. I find the whole workflow of creating, tagging, and - later on - searching for information on my computer to be very elegant. On top of that, now that I have the time, I want to begin writing blog posts to better work out my thoughts. With both of these things in mind, I am again turning to the universal tool for human prospering: <code>org-mode</code>. This time, I want to see how it can help me turn a simple org file into a blog post on my website. My requirements are:
</p>
@@ -70,10 +67,9 @@ And that's pretty much it for now. Without further ado, let's jump into getting
</p>
</div>
</div>
-
-<div id="outline-container-orgaada31d" class="outline-2">
-<h2 id="orgaada31d">Basic HTML File</h2>
-<div class="outline-text-2" id="text-orgaada31d">
+<div id="outline-container-org8795819" class="outline-2">
+<h2 id="org8795819">Basic HTML File</h2>
+<div class="outline-text-2" id="text-org8795819">
<p>
As a pilot, we are going to use this org file that I am currently writing (<code>hello.org</code>) as our guinea pig. The goal is to have this org file be our very first blog post.
</p>
@@ -113,10 +109,9 @@ We then do a <code>chmod +x publish.sh</code> to make it an executable and run i
</p>
</div>
</div>
-
-<div id="outline-container-orgd7e6626" class="outline-2">
-<h2 id="orgd7e6626">Disabling features that we don't want</h2>
-<div class="outline-text-2" id="text-orgd7e6626">
+<div id="outline-container-org2f52ef7" class="outline-2">
+<h2 id="org2f52ef7">Disabling features that we don't want</h2>
+<div class="outline-text-2" id="text-org2f52ef7">
<p>
The next thing will be to remove some of the generated items that I didn't ask for, namely the table of contents, author, section numbers, creation time stamp, and the validation link.
</p>
@@ -145,10 +140,9 @@ The next thing will be to remove some of the generated items that I didn't ask f
</div>
</div>
</div>
-
-<div id="outline-container-orgfe549f7" class="outline-2">
-<h2 id="orgfe549f7">Styling &amp; Code Highlighting</h2>
-<div class="outline-text-2" id="text-orgfe549f7">
+<div id="outline-container-org809ec6b" class="outline-2">
+<h2 id="org809ec6b">Styling &amp; Code Highlighting</h2>
+<div class="outline-text-2" id="text-org809ec6b">
<p>
Next thing on our list is custom styling. This can be achieved by first installing the <code>htmlize</code> package from <code>melpa</code> / <code>elpa</code>. The EmacsWiki describes this as "a package for exporting the contents of an Emacs buffer to HTML while respecting display properties such as colors, fonts, underlining, invisibility, etc" (<a href="https://www.emacswiki.org/emacs/Htmlize">reference</a>). If used "out-of-the-box", the buffer will be exported to HTML with all of the styles inlined (e.g. if you underline something in your org file, you will generate a <code>&lt;span style="text-decoration: underline"&gt;...&lt;/span&gt;</code>). However, we are more interested in styling everything by ourselves: we don't want <code>htmlize</code> making assumptions about what underlining means to us! Luckily, <code>htmlize</code> gives us the option to export with class names instead of inline styles so that we can specify each style for ourselves.
</p>
@@ -318,10 +312,9 @@ If we run the publish again, we can see that we have full styling on our code sn
</p>
</div>
</div>
-
-<div id="outline-container-org854b984" class="outline-2">
-<h2 id="org854b984">Images</h2>
-<div class="outline-text-2" id="text-org854b984">
+<div id="outline-container-org40ba39e" class="outline-2">
+<h2 id="org40ba39e">Images</h2>
+<div class="outline-text-2" id="text-org40ba39e">
<p>
Our first two criteria have been met! Next on the list is solving images. As an example, let's use this <a href="file:///_posts/assets/squirrel.jpg">squirrel image</a> that I found online with an open source license. The ideal situation would be:
</p>
@@ -366,18 +359,16 @@ So what's the fix here? Well, we have two options, but I am going to go with the
That's all there is to it! There are simpler ways as well, but that should do it:
</p>
-<div id="org9f865ab" class="figure">
+<div id="org11fc996" class="figure">
<p><img src="/_posts/assets/squirrel.jpg" alt="squirrel.jpg" width="300" />
</p>
<p><span class="figure-number">Figure 1: </span>A Cute Squirrel</p>
</div>
</div>
</div>
-
-
-<div id="outline-container-orgd7096b3" class="outline-2">
-<h2 id="orgd7096b3">Creation Date</h2>
-<div class="outline-text-2" id="text-orgd7096b3">
+<div id="outline-container-orgbf45d8f" class="outline-2">
+<h2 id="orgbf45d8f">Creation Date</h2>
+<div class="outline-text-2" id="text-orgbf45d8f">
<p>
Let's add the creation date below the title next. To start, we will modify the publish command to remove the title (<code>:with-title nil</code>) and, in its place, show a preamble bit of HTML that contains a formatted <code>div</code> with the title and the "last modified" span.z
</p>
@@ -436,10 +427,9 @@ Note that the downside of this is that the created date will change whenever you
</p>
</div>
</div>
-
-<div id="outline-container-org5fd9dcc" class="outline-2">
-<h2 id="org5fd9dcc">Generating the Directory</h2>
-<div class="outline-text-2" id="text-org5fd9dcc">
+<div id="outline-container-org4838556" class="outline-2">
+<h2 id="org4838556">Generating the Directory</h2>
+<div class="outline-text-2" id="text-org4838556">
<p>
For every org file in my <code>_posts</code> folder, I would like to create a link to the generated HTML file at the <code>/posts.html</code> page of my website. You can think of this as the "directory" of all posts. My criteria is:
</p>
@@ -499,10 +489,9 @@ If you open the <code>sitemap.html</code> file in your browser, you will see a b
From here, you may customize it however you like. The following are my customizations.
</p>
</div>
-
-<div id="outline-container-orga131ca9" class="outline-3">
-<h3 id="orga131ca9">Sitemap Title</h3>
-<div class="outline-text-3" id="text-orga131ca9">
+<div id="outline-container-org033bdd5" class="outline-3">
+<h3 id="org033bdd5">Sitemap Title</h3>
+<div class="outline-text-3" id="text-org033bdd5">
<p>
I changed the title to "Matthew's Blog Posts".
</p>
@@ -524,11 +513,9 @@ I changed the title to "Matthew's Blog Posts".
</div>
</div>
</div>
-
-
-<div id="outline-container-org39e11a9" class="outline-3">
-<h3 id="org39e11a9">Format blog entries in the list</h3>
-<div class="outline-text-3" id="text-org39e11a9">
+<div id="outline-container-orgf3eb8a5" class="outline-3">
+<h3 id="orgf3eb8a5">Format blog entries in the list</h3>
+<div class="outline-text-3" id="text-orgf3eb8a5">
<p>
I like to include the creation date on the blog posts. To do this, we can use <code>org-publish-find-property</code> to find the date property of the org file. Afterward, we can format a string that includes our formatted timestamp and the <code>org-publish-sitemap-default-entry</code>, which is just a link with the title of the post.
</p>
@@ -549,10 +536,9 @@ I like to include the creation date on the blog posts. To do this, we can use <c
</div>
</div>
</div>
-
-<div id="outline-container-org22c8fbe" class="outline-2">
-<h2 id="org22c8fbe">Tags &amp; Filtering</h2>
-<div class="outline-text-2" id="text-org22c8fbe">
+<div id="outline-container-org7dc2312" class="outline-2">
+<h2 id="org7dc2312">Tags &amp; Filtering</h2>
+<div class="outline-text-2" id="text-org7dc2312">
<p>
I use <a href="https://www.orgroam.com/">Org-roam</a> for all of my note-taking and, in the next blog post, I plan to demonstrate how I will hook up my Org-roam note-taking workflow to my blogging. In the meantime, just know that we can add tags to the top of our org files like this:
</p>
@@ -772,10 +758,9 @@ Finally, let's append the following to <code>posts/posts.css</code> so that our
</div>
</div>
</div>
-
-<div id="outline-container-org85c3052" class="outline-2">
-<h2 id="org85c3052">Conclusion</h2>
-<div class="outline-text-2" id="text-org85c3052">
+<div id="outline-container-org9f7bdd1" class="outline-2">
+<h2 id="org9f7bdd1">Conclusion</h2>
+<div class="outline-text-2" id="text-org9f7bdd1">
<p>
There are many more customizations that I plan to do on this system in the future, but I plan to leave this for now so that I can actually get to some blogging. I will proofread and fix my mistakes as time goes on, but this should be a good jumping off point for anyone interested in using org for their own blogging system.
</p>
diff --git a/posts/jul_28_2025.html b/posts/jul_28_2025.html
new file mode 100644
index 0000000..304d202
--- /dev/null
+++ b/posts/jul_28_2025.html
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title>Update July 28, 2025</title>
+<meta name="generator" content="Org Mode" />
+
+<link rel="stylesheet" href="/index.css" />
+<link rel="stylesheet" href="/posts/post.css" />
+<link rel="shortcut icon" href="/favicon/favicon.ico" type="image/x-icon">
+<script src='/posts/post.js'></script>
+</head>
+<body>
+<div id="org-div-home-and-up">
+ <a accesskey="h" href="/posts/sitemap.html"> UP </a>
+ <a accesskey="H" href="/"> HOME </a>
+</div><div id="preamble" class="status">
+
+ <div class="org-article-title">
+ <h1>Update July 28, 2025</h1>
+ <span>Last modified: 2025-07-28 Mon 17:30</span>
+ <span><a href="/posts/feed.xml">RSS Feed</a></span>
+ </div>
+</div>
+<div id="content" class="content">
+<div id="outline-container-org063974c" class="outline-2">
+<h2 id="org063974c">What have I been up to?</h2>
+<div class="outline-text-2" id="text-org063974c">
+<p>
+Whoops! I missed this month's update by a <i>long</i> shot, but I still want to get it out there before the end of the month.
+</p>
+
+<p>
+This month was busy busy. I released <a href="https://github.com/miracle-wm-org/miracle-wm/releases/tag/v0.6.0">v0.6.0 of miracle-wm</a> which adds a bunch of new features, go and check it out if you haven't already! I am nearly finished with the sway/i3 IPC support and have big plans to wrap that up before the middle of August. There really isn't much more to go on that front so I might as well close that chapter. On top of that, the more interesting features of miracle that I have planned (e.g. some built-in shell integrations) are motivating me to wrap up the boring parts first.
+</p>
+
+<p>
+Aside from miracle, I've continued to land a bunch of things in Mir recently around accessibility. The magnifier work is finally done, so go check that out if you use that feature in your day-to-day! I also did a <i>ton of work</i> to fix screenshooting on rotated displays. We were ignoring <a href="https://wayland.app/protocols/wayland#wl_surface:request:set_buffer_transform">wl<sub>surface</sub>::set<sub>buffer</sub><sub>transform</sub></a> in a big way. Now that we're not doing that, your screenshots should like perfect every time!
+</p>
+
+<p>
+Last but not least, the team has been making waves implementing multi-window in the Flutter toolkit. That's some really interesting and exciting work, so stay tuned for that if you're a Flutter developer!
+</p>
+
+<p>
+That's all I got! I will make another post shortly, maybe once miracle v0.7.0 is out. Have a great rest of your summer/winter πŸͺ
+</p>
+</div>
+</div>
+</div>
+</body>
+</html>
diff --git a/posts/june_08_2025.html b/posts/june_08_2025.html
new file mode 100644
index 0000000..4a69b4c
--- /dev/null
+++ b/posts/june_08_2025.html
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title>Update June 08, 2025</title>
+<meta name="generator" content="Org Mode" />
+
+<link rel="stylesheet" href="/index.css" />
+<link rel="stylesheet" href="/posts/post.css" />
+<link rel="shortcut icon" href="/favicon/favicon.ico" type="image/x-icon">
+<script src='/posts/post.js'></script>
+</head>
+<body>
+<div id="org-div-home-and-up">
+ <a accesskey="h" href="/posts/sitemap.html"> UP </a>
+ <a accesskey="H" href="/"> HOME </a>
+</div><div id="preamble" class="status">
+
+ <div class="org-article-title">
+ <h1>Update June 08, 2025</h1>
+ <span>Last modified: 2025-06-08 Sun 15:30</span>
+ <span><a href="/posts/feed.xml">RSS Feed</a></span>
+ </div>
+</div>
+<div id="content" class="content">
+<div id="outline-container-org7328e60" class="outline-2">
+<h2 id="org7328e60">What have I been up to?</h2>
+<div class="outline-text-2" id="text-org7328e60">
+<p>
+Another month has gone by, so I guess it's time to see what I've been up to.
+</p>
+
+<p>
+Canonical hosted our company's sprint in Frankfurt, Germany during the month of May. It was a very productive time for the whole team. I always enjoy seeing everyone in person, talking through big issues face-to-face, and exploring a new city. I also got to spend some time in Zurich, Switzerland before the sprint began. Zurich is pretty awesome πŸ”οΈ!
+</p>
+
+<p>
+In miracle-wm, I have been going down quite the rabbit-holeπŸ‡! What began as a plan to fix the remaining warnings in the source code has snowballed into me implemnenting the rest of the IPC mechanism and testing the entire thing. This has been no small feat, as both sway and i3 implement a <span class="underline">lot</span> of different commands, and a failure to implement any one of them often leads to half-broken clients. I've had to make some "executive" decisions to ignore parts of the protocol that I deem irrelevant now (especially many of the X-specific bits), but it is a mostly compatible implementation. The good news is that I've nearly completed this journey and should be ready to release version <code>0.6.0</code> some time in the middle of June.
+</p>
+
+<p>
+Miracle is getting closer-and-closer to my vision of it every day. The only problem right now is finding the bandwidth to implement everything that I have in my head :)
+</p>
+
+<p>
+On the Mir side of things, I am still implementing the magnifier glass accessibility feature from before, but with a much-improved technical direction that we arrived at during our time in Frankfurt. Unfortunately, this required a quick detour to properly implement the <code>overlay_cursor</code> flag of <a href="https://wayland.app/protocols/wlr-screencopy-unstable-v1#zwlr_screencopy_manager_v1:request:capture_output:arg:overlay_cursor">zwlr<sub>screencopy</sub><sub>manager</sub><sub>v1</sub>::capture<sub>output</sub><sub>region</sub></a>, as both screencopy and magnification rely on this same code path. The good news is that I'm quite close on this and it should be landing in full any day now 🀞
+</p>
+
+<p>
+I also fixed <a href="https://github.com/canonical/mir/pull/3968">this very breaking bug</a> that was actively preventing miracle from rendering on my second monitor, so that's good.
+</p>
+
+<p>
+On addition to these two projects, I am also reinvolving myself in the Flutter multi-window work. For those who don't know, we're trying to make it so that the Flutter toolkit can render to multiple surfaces. This is no small feat, as Flutter was originally written with the assumption that only a single "view" would ever be drawn too. However we've managed to make some great progress on it thus far, and we're very excited to land the first pull request imminently with the help of the folks over a Google!
+</p>
+
+<p>
+I hope you're having a great and productive summer 😎
+</p>
+</div>
+</div>
+</div>
+</body>
+</html>
diff --git a/posts/may_06_2025.html b/posts/may_06_2025.html
new file mode 100644
index 0000000..6e4665e
--- /dev/null
+++ b/posts/may_06_2025.html
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title>Update May 06, 2025</title>
+<meta name="generator" content="Org Mode" />
+
+<link rel="stylesheet" href="/index.css" />
+<link rel="stylesheet" href="/posts/post.css" />
+<link rel="shortcut icon" href="/favicon/favicon.ico" type="image/x-icon">
+<script src='/posts/post.js'></script>
+</head>
+<body>
+<div id="org-div-home-and-up">
+ <a accesskey="h" href="/posts/sitemap.html"> UP </a>
+ <a accesskey="H" href="/"> HOME </a>
+</div><div id="preamble" class="status">
+
+ <div class="org-article-title">
+ <h1>Update May 06, 2025</h1>
+ <span>Last modified: 2025-05-06 Tue 17:00</span>
+ <span><a href="/posts/feed.xml">RSS Feed</a></span>
+ </div>
+</div>
+<div id="content" class="content">
+<div id="outline-container-org77ab60c" class="outline-2">
+<h2 id="org77ab60c">What have I been up to?</h2>
+<div class="outline-text-2" id="text-org77ab60c">
+<p>
+I've been meaning to do these little blog-post type updates for a while, and I figured now is as good a time as any. So let's start :)
+</p>
+
+<p>
+In the world of miracle-wm, I've been hard at work writing a new settings application for the compositor called <a href="https://github.com/miracle-wm-org/miracle-settings">miracle-settings</a>. While the application is written in Flutter, the logic behind the application is entirely implemented in <code>libmiracle-wm-config.so</code>, a new library that will ship with miracle as part of <code>v0.6.0</code>. If Flutter isn't your cup of tea, you should be able to implement your own settings app for miracle in the language/toolkit of your choice by simply binding to the C (or C++!) API.
+</p>
+
+<p>
+I also implemented <a href="https://wayland.app/protocols/wlr-output-management-unstable-v1">wlr-output-management</a> in miracle and changed how we do display configuration in a big way. The display configuration will now always be loaded from <code>$HOME/.config/miracle-wm/display.yaml</code>. Users should now be able to use apps like <a href="https://github.com/artizirk/wdisplays">wdisplays</a> to change the output configuration at runtime, which is pretty cool!
+</p>
+
+<p>
+On the Mir project, I've been working on accessibility features (e.g. magnification) in addition to exposing some facilities for compositor authors to write end-to-end tests for Mir-based compositors. These new testing facilities should help us write tests in miracle in a big way.
+</p>
+
+<p>
+Also - my cat who ate a sewing needle last year has turned two! 🐱
+</p>
+</div>
+</div>
+</div>
+</body>
+</html>
diff --git a/posts/post.css b/posts/post.css
index decceaa..1867311 100644
--- a/posts/post.css
+++ b/posts/post.css
@@ -68,61 +68,3 @@ pre span.org-css-property {color:#00AA00;}
display: flex;
column-gap: 8px;
}
-
-
-/* Sitemap-specific items */
-.sitemap_tag {
- display: none;
-}
-
-#tag-filter-container {
- display: flex;
- flex-direction: row;
- column-gap: 8px;
- margin-top: 1rem;
-}
-
-.tag-filter-item {
- display: flex;
- flex-direction: row;
- align-items: center;
- padding: 0.25rem 0.5rem;
- border: 1px solid black;
- border-radius: 3px;
- justify-content: center;
- column-gap: 1rem;
- background-color: #fffed8;
-}
-
-.tag-filter-item button {
- background: none;
- border: none;
- outline: none;
- margin: 0;
- padding: 0;
- color: red;
- font-size: 1.5rem;
-}
-
-.tag-filter-item button:before {
- content: '\00d7';
-}
-
-.tag-filter-item.disabled button:before {
- content: '+';
-}
-
-.tag-filter-item.disabled {
- background-color: #f2f2f2;
- color: gray;
- border-color: gray;
-}
-
-.tag-filter-item.disabled button {
- color: green;
-}
-
-.tag-filter-item button:hover {
- cursor: pointer;
- opacity: 0.8;
-}
diff --git a/posts/post.js b/posts/post.js
index 0b5a4e4..233739f 100644
--- a/posts/post.js
+++ b/posts/post.js
@@ -1,7 +1,7 @@
function main() {
- // Gather the used set oof tags
+ // Gather the used set of tags
const tagSet = new Set();
const postList = [];
const tagContainers = document.getElementsByClassName('sitemap_tag');
@@ -12,8 +12,21 @@ function main() {
continue;
}
- const tagList = pContainer.textContent.split(',');
- tagList.forEach(tag => tagSet.add(tag));
+ const tagText = pContainer.textContent.trim();
+ const tagList = tagText.split(',').map(tag => tag.trim());
+
+ // Replace the text content with styled tag badges
+ pContainer.innerHTML = '';
+ tagList.forEach(tag => {
+ if (tag) {
+ tagSet.add(tag);
+ const tagBadge = document.createElement('span');
+ tagBadge.className = 'post-tag';
+ tagBadge.textContent = tag;
+ pContainer.appendChild(tagBadge);
+ }
+ });
+
postList.push({
container: container.parentElement,
tagList: tagList,
@@ -27,31 +40,18 @@ function main() {
tagContainer.id = 'tag-filter-container';
contentContainer.before(tagContainer);
- let numEnabled = tagSet.size;
for (const tag of tagSet) {
const tagElement = document.createElement('div');
tagElement.className = "tag-filter-item";
const tagElementLabel = document.createElement('span');
tagElementLabel.innerHTML = tag;
- const tagElementButton = document.createElement('button');
- tagElement.append(tagElementLabel, tagElementButton);
+ tagElement.append(tagElementLabel);
tagContainer.append(tagElement);
-
// Whenever a tag is clicked, execute the filtering behavior
- tagElementButton.onclick = function() {
- // Handle enable/disable
- tagElement.remove();
-
+ tagElement.onclick = function() {
if (tagElement.classList.contains('disabled')) {
tagElement.classList.remove('disabled');
- if (numEnabled === 0) {
- tagContainer.prepend(tagElement);
- }
- else {
- tagContainer.children[numEnabled - 1].after(tagElement);
- }
- numEnabled++;
// Filter
postList.forEach(post => {
@@ -66,8 +66,6 @@ function main() {
}
else {
tagElement.classList.add('disabled');
- tagContainer.append(tagElement);
- numEnabled--;
// Filter
postList.forEach(post => {
diff --git a/posts/sitemap.css b/posts/sitemap.css
new file mode 100644
index 0000000..b2de932
--- /dev/null
+++ b/posts/sitemap.css
@@ -0,0 +1,169 @@
+/* Sitemap-specific items */
+.sitemap_tag {
+ display: block;
+}
+
+.sitemap_tag p {
+ margin: 0;
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+ font-size: 0.85rem;
+}
+
+.sitemap_tag p::before {
+ content: '🏷️';
+ font-size: 1rem;
+}
+
+/* Individual tag badges */
+.post-tag {
+ display: inline-block;
+ padding: 0.25rem 0.75rem;
+ background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
+ color: #1976d2;
+ border-radius: 12px;
+ font-size: 0.8rem;
+ font-weight: 500;
+ border: 1px solid #90caf9;
+}
+
+.org-ul {
+ list-style: none;
+ padding: 0;
+}
+
+.org-ul > li {
+ background: #f9f9f9;
+ border: 1px solid #e0e0e0;
+ border-radius: 8px;
+ padding: 1.5rem;
+ margin-bottom: 1rem;
+ transition: box-shadow 0.2s ease, transform 0.2s ease;
+ position: relative;
+ padding-bottom: 2.5rem;
+}
+
+.org-ul > li:hover {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+ transform: translateY(-2px);
+}
+
+.org-ul > li > p {
+ margin: 0;
+ font-size: 1.1rem;
+}
+
+.org-ul > li > p > a {
+ color: #0066cc;
+ text-decoration: none;
+ font-weight: 600;
+ font-size: 1.25rem;
+}
+
+.org-ul > li > p > a:hover {
+ text-decoration: underline;
+ color: #004499;
+}
+
+.org-ul > li > p > a:after {
+ text-decoration: underline;
+ color: #004499;
+}
+
+.org-article-title {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+ margin-bottom: 2rem;
+}
+
+.org-article-title > span:last-child {
+ font-size: 0.95rem;
+}
+
+#tag-filter-container {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ gap: 0.75rem;
+ margin-top: 1.5rem;
+ margin-bottom: 2rem;
+}
+
+.tag-filter-item {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 0.5rem 1rem;
+ border-radius: 4px;
+ justify-content: center;
+ column-gap: 0.75rem;
+ background: linear-gradient(135deg, #667eea 0%, darkviolet 100%);
+ color: white;
+ font-weight: 600;
+ font-size: 0.9rem;
+ letter-spacing: 0.3px;
+ box-shadow: 0 2px 6px rgba(102, 126, 234, 0.3);
+ transition: all 0.3s ease;
+}
+
+.tag-filter-item:hover {
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
+ cursor: pointer;
+}
+
+.tag-filter-item button:before {
+ content: '\00d7';
+ line-height: 1;
+}
+
+.tag-filter-item.disabled {
+ background: linear-gradient(135deg, #e0e0e0 0%, #c0c0c0 100%);
+ color: #666;
+ border-color: #999;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+ opacity: 0.7;
+}
+
+.tag-filter-item.disabled:hover {
+ opacity: 1;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
+}
+
+.tag-filter-item.disabled button {
+ background: rgba(0, 0, 0, 0.1);
+ color: #666;
+}
+
+.tag-filter-item.disabled button:before {
+ content: '+';
+ font-size: 1.1rem;
+}
+
+.post-date {
+ position: absolute;
+ bottom: 0.75rem;
+ right: 1rem;
+ font-size: 0.85rem;
+ color: #666;
+ font-style: italic;
+}
+
+.sitemap_date {
+ position: absolute;
+ bottom: 0.75rem;
+ right: 1rem;
+ font-size: 0.85rem;
+ color: #666;
+ font-style: italic;
+}
+
+.sitemap_date p {
+ margin: 0;
+}
+
+.sitemap_date p::before {
+ content: 'created on ';
+}
diff --git a/posts/sitemap.html b/posts/sitemap.html
index 31ec52d..00ac911 100644
--- a/posts/sitemap.html
+++ b/posts/sitemap.html
@@ -12,6 +12,7 @@
<link rel="stylesheet" href="/posts/post.css" />
<link rel="shortcut icon" href="/favicon/favicon.ico" type="image/x-icon">
<script src='/posts/post.js'></script>
+<link rel="stylesheet" href="/posts/sitemap.css" />
</head>
<body>
<div id="org-div-home-and-up">
@@ -21,15 +22,82 @@
<div class="org-article-title">
<h1>Matthew's Blog Posts</h1>
- <span>Last modified: 2024-05-01 at 14:07</span>
+ <span>Last modified: 2025-12-29 at 13:03</span>
+ <span><a href="/posts/feed.xml">RSS Feed</a></span>
</div>
</div>
<div id="content" class="content">
<ul class="org-ul">
<li><p>
-<a href="hello.html">Hello, Org</a> created on June 20, 2023
+<a href="dec_29_2025.html">Update December 29, 2025</a>
</p>
-<div class="sitemap_tag" id="orgb9e6a4a">
+<div class="sitemap_date" id="orgd69e487">
+<p>
+December 29, 2025
+</p>
+
+</div>
+<div class="sitemap_tag" id="orgfc65be4">
+<p>
+update
+</p>
+
+</div></li>
+<li><p>
+<a href="jul_28_2025.html">Update July 28, 2025</a>
+</p>
+<div class="sitemap_date" id="org4f11db1">
+<p>
+July 28, 2025
+</p>
+
+</div>
+<div class="sitemap_tag" id="org6e689d4">
+<p>
+update
+</p>
+
+</div></li>
+<li><p>
+<a href="june_08_2025.html">Update June 08, 2025</a>
+</p>
+<div class="sitemap_date" id="org035bfd6">
+<p>
+June 08, 2025
+</p>
+
+</div>
+<div class="sitemap_tag" id="org374d739">
+<p>
+update
+</p>
+
+</div></li>
+<li><p>
+<a href="may_06_2025.html">Update May 06, 2025</a>
+</p>
+<div class="sitemap_date" id="org5523abd">
+<p>
+May 06, 2025
+</p>
+
+</div>
+<div class="sitemap_tag" id="org2580b8e">
+<p>
+update
+</p>
+
+</div></li>
+<li><p>
+<a href="hello.html">Hello, Org</a>
+</p>
+<div class="sitemap_date" id="orgb561eb9">
+<p>
+June 20, 2023
+</p>
+
+</div>
+<div class="sitemap_tag" id="org57d4497">
<p>
technology,home
</p>
diff --git a/publish.el b/publish.el
index 352c88f..537d5e1 100644
--- a/publish.el
+++ b/publish.el
@@ -13,11 +13,16 @@
;; Install dependencies
(package-install 'htmlize)
+;; Install rust-mode if not already installed
+(unless (package-installed-p 'rust-mode)
+ (package-refresh-contents)
+ (package-install 'rust-mode))
+
(defun get-org-file-title(entry style project)
(setq timestamp (org-timestamp-format (car (org-publish-find-property entry :date project)) "%B %d, %Y"))
(setq tag-list (org-publish-find-property entry :filetags project))
(setq tag-list-str (mapconcat 'identity tag-list ","))
- (setq result (format "%s created on %s\n#+begin_sitemap_tag\n%s\n#+end_sitemap_tag\n" (org-publish-sitemap-default-entry entry style project) timestamp tag-list-str))
+ (setq result (format "%s\n #+begin_sitemap_date\n%s\n#+end_sitemap_date\n#+begin_sitemap_tag\n%s\n#+end_sitemap_tag\n" (org-publish-sitemap-default-entry entry style project) timestamp tag-list-str))
)
(defun my-sitemap-function (title list)
@@ -33,7 +38,7 @@
:recursive t
:base-directory "./_posts"
:publishing-directory "./posts"
- :publishing-function: 'org-html-publish-to-html
+ :publishing-function 'my-org-html-publish-to-html
:with-toc nil
:with-author nil
:section-numbers nil
@@ -44,10 +49,11 @@
<div class=\"org-article-title\">
<h1>%t</h1>
<span>Last modified: %d</span>
+ <span><a href=\"/posts/feed.xml\">RSS Feed</a></span>
</div>
"))
:auto-sitemap t
- :sitemap-sort-files "chronologically"
+ :sitemap-sort-files 'anti-chronologically
:sitemap-title "Matthew's Blog Posts"
:sitemap-format-entry (lambda (entry style project) (get-org-file-title entry style project))
:sitemap-function (lambda (title list) (my-sitemap-function title list))
@@ -57,6 +63,13 @@
(setq org-html-htmlize-output-type 'css)
(setq org-html-htmlize-font-prefix "org-")
+(defun my-org-html-publish-to-html (plist filename pub-dir)
+ "Custom HTML publish function that conditionally adds sitemap.css."
+ (let* ((is-sitemap (string-match-p "sitemap\\.org$" filename))
+ (org-html-head-extra (when is-sitemap
+ "<link rel=\"stylesheet\" href=\"/posts/sitemap.css\" />")))
+ (org-html-publish-to-html plist filename pub-dir)))
+
(setq org-html-validation-link nil
org-html-head-include-scripts nil ;; Use our own scripts
org-html-head-include-default-style nil ;; Use our own styles
@@ -76,6 +89,101 @@
</div>"
)
+(defun update-rss-feed ()
+ "Update the RSS feed with the newest post from _posts directory."
+ (let* ((posts-dir (expand-file-name "_posts"))
+ (feed-file (expand-file-name "posts/feed.xml"))
+ (org-files (seq-filter (lambda (f) (not (string-match-p "sitemap\\.org$" f)))
+ (directory-files posts-dir t "\\.org$")))
+ (newest-file (car (sort org-files (lambda (a b) (time-less-p (file-attribute-modification-time (file-attributes b))
+ (file-attribute-modification-time (file-attributes a)))))))
+ title date-str filename-base)
+
+ (when newest-file
+ ;; Parse the newest org file
+ (with-temp-buffer
+ (insert-file-contents newest-file)
+ (goto-char (point-min))
+
+ ;; Extract title
+ (when (re-search-forward "^#\\+TITLE:\\s-*\\(.+\\)$" nil t)
+ (setq title (match-string 1)))
+
+ ;; Extract date
+ (goto-char (point-min))
+ (when (re-search-forward "^#\\+DATE:\\s-*<\\([0-9]\\{4\\}\\)-\\([0-9]\\{2\\}\\)-\\([0-9]\\{2\\}\\)\\s-+\\w+\\s-+\\([0-9]\\{2\\}\\):\\([0-9]\\{2\\}\\)>" nil t)
+ (let ((year (match-string 1))
+ (month (match-string 2))
+ (day (match-string 3))
+ (hour (match-string 4))
+ (minute (match-string 5))
+ (month-names ["" "January" "February" "March" "April" "May" "June"
+ "July" "August" "September" "October" "November" "December"])
+ (day-names ["Sun" "Mon" "Tue" "Wed" "Thu" "Fri" "Sat"]))
+ (setq date-str (format "%s, %s %s %s %s:%s:00 -0400"
+ (aref day-names (string-to-number (format-time-string "%w" (encode-time 0 0 0 (string-to-number day) (string-to-number month) (string-to-number year)))))
+ day
+ (aref month-names (string-to-number month))
+ year
+ hour
+ minute)))))
+
+ ;; Get filename without extension
+ (setq filename-base (file-name-sans-extension (file-name-nondirectory newest-file)))
+
+ ;; Create RSS item
+ (when (and title date-str filename-base)
+ (let ((rss-item (format " <item>
+ <title>%s</title>
+ <link>https://matthewkosarek.xyz/posts/%s.html</link>
+ <guid>https://matthewkosarek.xyz/posts/%s.html</guid>
+ <pubDate>%s</pubDate>
+ <description>%s</description>
+ </item>\n"
+ title filename-base filename-base date-str title)))
+
+ ;; Read current feed
+ (with-temp-buffer
+ (insert-file-contents feed-file)
+ (goto-char (point-min))
+
+ ;; Update lastBuildDate to current time
+ (let* ((current-time (current-time))
+ (decoded-time (decode-time current-time))
+ (day (nth 3 decoded-time))
+ (month (nth 4 decoded-time))
+ (year (nth 5 decoded-time))
+ (hour (nth 2 decoded-time))
+ (minute (nth 1 decoded-time))
+ (month-names ["" "January" "February" "March" "April" "May" "June"
+ "July" "August" "September" "October" "November" "December"])
+ (day-names ["Sun" "Mon" "Tue" "Wed" "Thu" "Fri" "Sat"])
+ (current-date-str (format "%s, %02d %s %s %02d:%02d:00 -0400"
+ (aref day-names (nth 6 decoded-time))
+ day
+ (aref month-names month)
+ year
+ hour
+ minute)))
+ (when (re-search-forward "<lastBuildDate>\\(.*\\)</lastBuildDate>" nil t)
+ (replace-match (format "<lastBuildDate>%s</lastBuildDate>" current-date-str))))
+
+ ;; Check if this item already exists
+ (goto-char (point-min))
+ (unless (search-forward (format "<link>https://matthewkosarek.xyz/posts/%s.html</link>" filename-base) nil t)
+ ;; Insert new item after lastBuildDate
+ (goto-char (point-min))
+ (when (re-search-forward "<lastBuildDate>.*</lastBuildDate>\n" nil t)
+ (insert "\n")
+ (insert rss-item)
+ (message "Added RSS item for: %s" title)))
+
+ ;; Write updated feed
+ (write-region (point-min) (point-max) feed-file)))))))
+
(org-publish-all t)
+;; Update RSS feed after publishing
+(update-rss-feed)
+
(message "Build Complete")
diff --git a/resume.css b/resume.css
index 0bccb2b..36bb6bf 100755
--- a/resume.css
+++ b/resume.css
@@ -1,5 +1,5 @@
@font-face {
- font-family: Iosevka;
+ font-family: Ubuntu;
src: url(fonts/Ubuntu-M.ttf);
}
@@ -25,7 +25,7 @@ header {
text-align: left;
background-color: white;
color: black;
- font-family: 'Iosevka', Tahoma, Geneva, Verdana, sans-serif;
+ font-family: 'Ubuntu', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.325rem;
}
diff --git a/resume.html b/resume.html
index 0ee34d4..143a87d 100755
--- a/resume.html
+++ b/resume.html
@@ -8,12 +8,12 @@
<script src='/resume.js' defer></script>
<title>Matthew Kosarek - Resume</title>
+ <link href='https://fonts.googleapis.com/css?family=Noto Sans' rel='stylesheet'>
<link rel="shortcut icon" href="/favicon/favicon.ico" type="image/x-icon">
<meta name="description" content="The hosted resume of Matthew kosarek">
</head>
<body>
<header>
- <h1>Matthew Kosarek</h1>
<nav>
<ul>
<li><a href='/'>&#127969; Home</a></li>
diff --git a/resume.mobile.css b/resume.mobile.css
index 5790e0e..016095b 100644
--- a/resume.mobile.css
+++ b/resume.mobile.css
@@ -1,5 +1,5 @@
@media (max-width: 767.98px) {
- html {
+ #resume {
font-size: 12px;
}
diff --git a/themes/.gitignore b/themes/.gitignore
new file mode 100644
index 0000000..a967258
--- /dev/null
+++ b/themes/.gitignore
@@ -0,0 +1,15 @@
+# Build directories
+dist/
+builddir/
+.cache/
+
+# IDE
+.idea/
+.vscode/
+*.swp
+*.swo
+*~
+
+# Meson
+.meson_subdir/
+compile_commands.json
diff --git a/themes/Makefile b/themes/Makefile
deleted file mode 100644
index 45967f5..0000000
--- a/themes/Makefile
+++ /dev/null
@@ -1,44 +0,0 @@
-TARGET_EXEC ?= output.js
-
-BUILD_DIR ?= ./dist
-SRC_DIRS ?= ./src
-
-CC := emcc
-CXX := em++
-SRCS := $(shell find $(SRC_DIRS) -name *.cpp -or -name *.c -or -name *.s)
-OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)
-DEPS := $(OBJS:.o=.d)
-
-INC_DIRS := $(shell find $(SRC_DIRS) -type d)
-INC_FLAGS := $(addprefix -I,$(INC_DIRS))
-LDFLAGS = -s ALLOW_MEMORY_GROWTH=1 -s USE_WEBGL2=1 -s FULL_ES3=1 -s WASM=1 -s NO_EXIT_RUNTIME=1 -s FETCH
-
-CPPFLAGS ?= $(INC_FLAGS) -MMD -MP
-
-$(BUILD_DIR)/$(TARGET_EXEC): $(OBJS)
- $(CC) $(OBJS) -o $@ $(LDFLAGS)
-
-# assembly
-$(BUILD_DIR)/%.s.o: %.s
- $(MKDIR_P) $(dir $@)
- $(AS) $(ASFLAGS) -c $< -o $@
-
-# c source
-$(BUILD_DIR)/%.c.o: %.c
- $(MKDIR_P) $(dir $@)
- $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
-
-# c++ source
-$(BUILD_DIR)/%.cpp.o: %.cpp
- $(MKDIR_P) $(dir $@)
- $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
-
-
-.PHONY: clean
-
-clean:
- $(RM) -r $(BUILD_DIR)
-
--include $(DEPS)
-
-MKDIR_P ?= mkdir -p
diff --git a/themes/README.md b/themes/README.md
new file mode 100644
index 0000000..3425fb8
--- /dev/null
+++ b/themes/README.md
@@ -0,0 +1,58 @@
+# Themes
+This subproject provides themes for the website.
+
+## Prerequisites
+
+- Emscripten SDK (emsdk)
+- Meson build system (`pip install meson` or via your package manager)
+- Ninja build backend (usually installed with Meson)
+
+## Building
+
+### Initial Setup
+
+```bash
+meson setup builddir --cross-file emscripten.ini
+```
+
+### Compile
+
+```bash
+meson compile -C builddir
+```
+
+The output files will be automatically copied to:
+- `dist/output.js`
+- `dist/output.wasm`
+
+The original build artifacts are also available in `builddir/` if needed.
+
+### Clean Build
+
+```bash
+# Clean and rebuild
+meson compile -C builddir --clean
+meson compile -C builddir
+```
+
+### Complete Clean
+
+```bash
+# Remove the build directory entirely
+rm -rf builddir
+```
+
+## Build Options
+
+You can configure build options during setup:
+
+```bash
+# Debug build
+meson setup builddir --cross-file emscripten.ini --buildtype=debug
+
+# Release build with optimizations
+meson setup builddir --cross-file emescripten.ini --buildtype=release
+
+# Reconfigure an existing build
+meson configure builddir --buildtype=debug
+```
diff --git a/themes/compile_commands.json b/themes/compile_commands.json
deleted file mode 100644
index bcf5405..0000000
--- a/themes/compile_commands.json
+++ /dev/null
@@ -1,127 +0,0 @@
-[
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/MainLoop.cpp -o dist/./src/MainLoop.cpp.o",
- "file": "dist/./src/MainLoop.cpp.o"
- },
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/spring/SpringTheme.cpp -o dist/./src/spring/SpringTheme.cpp.o",
- "file": "dist/./src/spring/SpringTheme.cpp.o"
- },
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/spring/GrassRenderer.cpp -o dist/./src/spring/GrassRenderer.cpp.o",
- "file": "dist/./src/spring/GrassRenderer.cpp.o"
- },
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/mathlib.cpp -o dist/./src/mathlib.cpp.o",
- "file": "dist/./src/mathlib.cpp.o"
- },
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/shaders/renderer3d_frag.cpp -o dist/./src/shaders/renderer3d_frag.cpp.o",
- "file": "dist/./src/shaders/renderer3d_frag.cpp.o"
- },
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/shaders/sun_frag.cpp -o dist/./src/shaders/sun_frag.cpp.o",
- "file": "dist/./src/shaders/sun_frag.cpp.o"
- },
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/shaders/renderer3d_vert.cpp -o dist/./src/shaders/renderer3d_vert.cpp.o",
- "file": "dist/./src/shaders/renderer3d_vert.cpp.o"
- },
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/shaders/renderer2d_frag.cpp -o dist/./src/shaders/renderer2d_frag.cpp.o",
- "file": "dist/./src/shaders/renderer2d_frag.cpp.o"
- },
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/shaders/sun_vert.cpp -o dist/./src/shaders/sun_vert.cpp.o",
- "file": "dist/./src/shaders/sun_vert.cpp.o"
- },
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/shaders/renderer2d_vert.cpp -o dist/./src/shaders/renderer2d_vert.cpp.o",
- "file": "dist/./src/shaders/renderer2d_vert.cpp.o"
- },
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/shader_fetcher.cpp -o dist/./src/shader_fetcher.cpp.o",
- "file": "dist/./src/shader_fetcher.cpp.o"
- },
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/Logger.cpp -o dist/./src/Logger.cpp.o",
- "file": "dist/./src/Logger.cpp.o"
- },
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/Renderer3d.cpp -o dist/./src/Renderer3d.cpp.o",
- "file": "dist/./src/Renderer3d.cpp.o"
- },
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/Shader.cpp -o dist/./src/Shader.cpp.o",
- "file": "dist/./src/Shader.cpp.o"
- },
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/summer/SummerTheme.cpp -o dist/./src/summer/SummerTheme.cpp.o",
- "file": "dist/./src/summer/SummerTheme.cpp.o"
- },
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/winter/Windfield.cpp -o dist/./src/winter/Windfield.cpp.o",
- "file": "dist/./src/winter/Windfield.cpp.o"
- },
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/winter/WinterTheme.cpp -o dist/./src/winter/WinterTheme.cpp.o",
- "file": "dist/./src/winter/WinterTheme.cpp.o"
- },
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/winter/Snowflake.cpp -o dist/./src/winter/Snowflake.cpp.o",
- "file": "dist/./src/winter/Snowflake.cpp.o"
- },
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/autumn/AutumnTheme.cpp -o dist/./src/autumn/AutumnTheme.cpp.o",
- "file": "dist/./src/autumn/AutumnTheme.cpp.o"
- },
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/autumn/TreeShape.cpp -o dist/./src/autumn/TreeShape.cpp.o",
- "file": "dist/./src/autumn/TreeShape.cpp.o"
- },
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/autumn/LeafParticleRender.cpp -o dist/./src/autumn/LeafParticleRender.cpp.o",
- "file": "dist/./src/autumn/LeafParticleRender.cpp.o"
- },
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/shapes_2d.cpp -o dist/./src/shapes_2d.cpp.o",
- "file": "dist/./src/shapes_2d.cpp.o"
- },
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/Renderer2d.cpp -o dist/./src/Renderer2d.cpp.o",
- "file": "dist/./src/Renderer2d.cpp.o"
- },
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/main.cpp -o dist/./src/main.cpp.o",
- "file": "dist/./src/main.cpp.o"
- },
- {
- "directory": ".",
- "command": "g++ -I./src -I./src/spring -I./src/shaders -I./src/tools -I./src/summer -I./src/winter -I./src/autumn -I./src/_shaders -MMD -MP -c src/WebglContext.cpp -o dist/./src/WebglContext.cpp.o",
- "file": "dist/./src/WebglContext.cpp.o"
- }
-]
diff --git a/themes/compile_commands.sh b/themes/compile_commands.sh
deleted file mode 100755
index 08a9eed..0000000
--- a/themes/compile_commands.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-make --always-make --dry-run \
- | grep -wE 'em++|emcc|gcc|g\+\+|c\+\+' \
- | grep -w '\-c' \
- | jq -nR '[inputs|{directory:".", command:., file: match(" [^ ]+$").string[1:]}]' \
- > compile_commands.json
-
-sed -i -e 's/em++/g++/g' compile_commands.json
diff --git a/themes/dist/output.js b/themes/dist/output.js
index 13d1675..011ac68 100644
--- a/themes/dist/output.js
+++ b/themes/dist/output.js
@@ -2,7 +2,7 @@
// The Module object: Our interface to the outside world. We import
// and export values on it. There are various ways Module can be used:
// 1. Not defined. We create it here
-// 2. A function parameter, function(Module) { ..generated code.. }
+// 2. A function parameter, function(moduleArg) => Promise<Module>
// 3. pre-run appended it, var Module = {}; ..generated code..
// 4. External script tag defines var Module.
// We need to check if Module already exists (e.g. case 3 above).
@@ -14,6 +14,25 @@
// can continue to use Module afterwards as well.
var Module = typeof Module != 'undefined' ? Module : {};
+// Determine the runtime environment we are in. You can customize this by
+// setting the ENVIRONMENT setting at compile time (see settings.js).
+
+// Attempt to auto-detect the environment
+var ENVIRONMENT_IS_WEB = typeof window == 'object';
+var ENVIRONMENT_IS_WORKER = typeof importScripts == 'function';
+// N.b. Electron.js environment is simultaneously a NODE-environment, but
+// also a web environment.
+var ENVIRONMENT_IS_NODE = typeof process == 'object' && typeof process.versions == 'object' && typeof process.versions.node == 'string' && process.type != 'renderer';
+var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER;
+
+if (ENVIRONMENT_IS_NODE) {
+ // `require()` is no-op in an ESM module, use `createRequire()` to construct
+ // the require()` function. This is only necessary for multi-environment
+ // builds, `-sENVIRONMENT=node` emits a static import declaration instead.
+ // TODO: Swap all `require()`'s with `import()`'s?
+
+}
+
// --pre-jses are emitted after the Module integration code, so that they can
// refer to Module (if they choose; they can also define Module)
@@ -31,21 +50,6 @@ var quit_ = (status, toThrow) => {
throw toThrow;
};
-// Determine the runtime environment we are in. You can customize this by
-// setting the ENVIRONMENT setting at compile time (see settings.js).
-
-// Attempt to auto-detect the environment
-var ENVIRONMENT_IS_WEB = typeof window == 'object';
-var ENVIRONMENT_IS_WORKER = typeof importScripts == 'function';
-// N.b. Electron.js environment is simultaneously a NODE-environment, but
-// also a web environment.
-var ENVIRONMENT_IS_NODE = typeof process == 'object' && typeof process.versions == 'object' && typeof process.versions.node == 'string';
-var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER;
-
-if (Module['ENVIRONMENT']) {
- throw new Error('Module.ENVIRONMENT has been deprecated. To force the environment, use the ENVIRONMENT compile-time option (for example, -sENVIRONMENT=web or -sENVIRONMENT=node)');
-}
-
// `/` should be present at the end if `scriptDirectory` is not empty
var scriptDirectory = '';
function locateFile(path) {
@@ -56,9 +60,7 @@ function locateFile(path) {
}
// Hooks that are implemented differently in different runtime environments.
-var read_,
- readAsync,
- readBinary;
+var readAsync, readBinary;
if (ENVIRONMENT_IS_NODE) {
if (typeof process == 'undefined' || !process.release || process.release.name !== 'node') throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)');
@@ -71,44 +73,31 @@ if (ENVIRONMENT_IS_NODE) {
throw new Error('This emscripten-generated code requires node v16.0.0 (detected v' + nodeVersion + ')');
}
- // `require()` is no-op in an ESM module, use `createRequire()` to construct
- // the require()` function. This is only necessary for multi-environment
- // builds, `-sENVIRONMENT=node` emits a static import declaration instead.
- // TODO: Swap all `require()`'s with `import()`'s?
// These modules will usually be used on Node.js. Load them eagerly to avoid
// the complexity of lazy-loading.
var fs = require('fs');
var nodePath = require('path');
- if (ENVIRONMENT_IS_WORKER) {
- scriptDirectory = nodePath.dirname(scriptDirectory) + '/';
- } else {
- scriptDirectory = __dirname + '/';
- }
+ scriptDirectory = __dirname + '/';
// include: node_shell_read.js
-read_ = (filename, binary) => {
+readBinary = (filename) => {
// We need to re-wrap `file://` strings to URLs. Normalizing isn't
// necessary in that case, the path should already be absolute.
filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename);
- return fs.readFileSync(filename, binary ? undefined : 'utf8');
-};
-
-readBinary = (filename) => {
- var ret = read_(filename, true);
- if (!ret.buffer) {
- ret = new Uint8Array(ret);
- }
+ var ret = fs.readFileSync(filename);
assert(ret.buffer);
return ret;
};
-readAsync = (filename, onload, onerror, binary = true) => {
- // See the comment in the `read_` function.
+readAsync = (filename, binary = true) => {
+ // See the comment in the `readBinary` function.
filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename);
- fs.readFile(filename, binary ? undefined : 'utf8', (err, data) => {
- if (err) onerror(err);
- else onload(binary ? data.buffer : data);
+ return new Promise((resolve, reject) => {
+ fs.readFile(filename, binary ? undefined : 'utf8', (err, data) => {
+ if (err) reject(err);
+ else resolve(binary ? data.buffer : data);
+ });
});
};
// end include: node_shell_read.js
@@ -122,89 +111,16 @@ readAsync = (filename, onload, onerror, binary = true) => {
module['exports'] = Module;
}
- process.on('uncaughtException', (ex) => {
- // suppress ExitStatus exceptions from showing an error
- if (ex !== 'unwind' && !(ex instanceof ExitStatus) && !(ex.context instanceof ExitStatus)) {
- throw ex;
- }
- });
-
quit_ = (status, toThrow) => {
process.exitCode = status;
throw toThrow;
};
- Module['inspect'] = () => '[Emscripten Module object]';
-
} else
if (ENVIRONMENT_IS_SHELL) {
if ((typeof process == 'object' && typeof require === 'function') || typeof window == 'object' || typeof importScripts == 'function') throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)');
- if (typeof read != 'undefined') {
- read_ = read;
- }
-
- readBinary = (f) => {
- if (typeof readbuffer == 'function') {
- return new Uint8Array(readbuffer(f));
- }
- let data = read(f, 'binary');
- assert(typeof data == 'object');
- return data;
- };
-
- readAsync = (f, onload, onerror) => {
- setTimeout(() => onload(readBinary(f)));
- };
-
- if (typeof clearTimeout == 'undefined') {
- globalThis.clearTimeout = (id) => {};
- }
-
- if (typeof setTimeout == 'undefined') {
- // spidermonkey lacks setTimeout but we use it above in readAsync.
- globalThis.setTimeout = (f) => (typeof f == 'function') ? f() : abort();
- }
-
- if (typeof scriptArgs != 'undefined') {
- arguments_ = scriptArgs;
- } else if (typeof arguments != 'undefined') {
- arguments_ = arguments;
- }
-
- if (typeof quit == 'function') {
- quit_ = (status, toThrow) => {
- // Unlike node which has process.exitCode, d8 has no such mechanism. So we
- // have no way to set the exit code and then let the program exit with
- // that code when it naturally stops running (say, when all setTimeouts
- // have completed). For that reason, we must call `quit` - the only way to
- // set the exit code - but quit also halts immediately. To increase
- // consistency with node (and the web) we schedule the actual quit call
- // using a setTimeout to give the current stack and any exception handlers
- // a chance to run. This enables features such as addOnPostRun (which
- // expected to be able to run code after main returns).
- setTimeout(() => {
- if (!(toThrow instanceof ExitStatus)) {
- let toLog = toThrow;
- if (toThrow && typeof toThrow == 'object' && toThrow.stack) {
- toLog = [toThrow, toThrow.stack];
- }
- err(`exiting due to exception: ${toLog}`);
- }
- quit(status);
- });
- throw toThrow;
- };
- }
-
- if (typeof print != 'undefined') {
- // Prefer to use print/printErr where they exist, as they usually work better.
- if (typeof console == 'undefined') console = /** @type{!Console} */({});
- console.log = /** @type{!function(this:Console, ...*): undefined} */ (print);
- console.warn = console.error = /** @type{!function(this:Console, ...*): undefined} */ (typeof printErr != 'undefined' ? printErr : print);
- }
-
} else
// Note that this includes Node.js workers when relevant (pthreads is enabled).
@@ -222,26 +138,17 @@ if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {
// and scriptDirectory will correctly be replaced with an empty string.
// If scriptDirectory contains a query (starting with ?) or a fragment (starting with #),
// they are removed because they could contain a slash.
- if (scriptDirectory.indexOf('blob:') !== 0) {
- scriptDirectory = scriptDirectory.substr(0, scriptDirectory.replace(/[?#].*/, "").lastIndexOf('/')+1);
- } else {
+ if (scriptDirectory.startsWith('blob:')) {
scriptDirectory = '';
+ } else {
+ scriptDirectory = scriptDirectory.substr(0, scriptDirectory.replace(/[?#].*/, '').lastIndexOf('/')+1);
}
if (!(typeof window == 'object' || typeof importScripts == 'function')) throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)');
- // Differentiate the Web Worker from the Node Worker case, as reading must
- // be done differently.
{
// include: web_or_worker_shell_read.js
-read_ = (url) => {
- var xhr = new XMLHttpRequest();
- xhr.open('GET', url, false);
- xhr.send(null);
- return xhr.responseText;
- }
-
- if (ENVIRONMENT_IS_WORKER) {
+if (ENVIRONMENT_IS_WORKER) {
readBinary = (url) => {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, false);
@@ -251,21 +158,35 @@ read_ = (url) => {
};
}
- readAsync = (url, onload, onerror) => {
- var xhr = new XMLHttpRequest();
- xhr.open('GET', url, true);
- xhr.responseType = 'arraybuffer';
- xhr.onload = () => {
- if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0
- onload(xhr.response);
- return;
- }
- onerror();
- };
- xhr.onerror = onerror;
- xhr.send(null);
- }
-
+ readAsync = (url) => {
+ // Fetch has some additional restrictions over XHR, like it can't be used on a file:// url.
+ // See https://github.com/github/fetch/pull/92#issuecomment-140665932
+ // Cordova or Electron apps are typically loaded from a file:// url.
+ // So use XHR on webview if URL is a file URL.
+ if (isFileURI(url)) {
+ return new Promise((resolve, reject) => {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', url, true);
+ xhr.responseType = 'arraybuffer';
+ xhr.onload = () => {
+ if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0
+ resolve(xhr.response);
+ return;
+ }
+ reject(xhr.status);
+ };
+ xhr.onerror = reject;
+ xhr.send(null);
+ });
+ }
+ return fetch(url, { credentials: 'same-origin' })
+ .then((response) => {
+ if (response.ok) {
+ return response.arrayBuffer();
+ }
+ return Promise.reject(new Error(response.status + ' : ' + response.url));
+ })
+ };
// end include: web_or_worker_shell_read.js
}
} else
@@ -279,7 +200,7 @@ var err = Module['printErr'] || console.error.bind(console);
// Merge back in the overrides
Object.assign(Module, moduleOverrides);
// Free the object hierarchy contained in the overrides, this lets the GC
-// reclaim data used e.g. in memoryInitializerRequest, which is a large typed array.
+// reclaim data used.
moduleOverrides = null;
checkIncomingModuleAPI();
@@ -292,21 +213,18 @@ if (Module['arguments']) arguments_ = Module['arguments'];legacyModuleProp('argu
if (Module['thisProgram']) thisProgram = Module['thisProgram'];legacyModuleProp('thisProgram', 'thisProgram');
-if (Module['quit']) quit_ = Module['quit'];legacyModuleProp('quit', 'quit_');
-
// perform assertions in shell.js after we set up out() and err(), as otherwise if an assertion fails it cannot print the message
// Assertions on removed incoming Module JS APIs.
assert(typeof Module['memoryInitializerPrefixURL'] == 'undefined', 'Module.memoryInitializerPrefixURL option was removed, use Module.locateFile instead');
assert(typeof Module['pthreadMainPrefixURL'] == 'undefined', 'Module.pthreadMainPrefixURL option was removed, use Module.locateFile instead');
assert(typeof Module['cdInitializerPrefixURL'] == 'undefined', 'Module.cdInitializerPrefixURL option was removed, use Module.locateFile instead');
assert(typeof Module['filePackagePrefixURL'] == 'undefined', 'Module.filePackagePrefixURL option was removed, use Module.locateFile instead');
-assert(typeof Module['read'] == 'undefined', 'Module.read option was removed (modify read_ in JS)');
+assert(typeof Module['read'] == 'undefined', 'Module.read option was removed');
assert(typeof Module['readAsync'] == 'undefined', 'Module.readAsync option was removed (modify readAsync in JS)');
assert(typeof Module['readBinary'] == 'undefined', 'Module.readBinary option was removed (modify readBinary in JS)');
assert(typeof Module['setWindowTitle'] == 'undefined', 'Module.setWindowTitle option was removed (modify emscripten_set_window_title in JS)');
assert(typeof Module['TOTAL_MEMORY'] == 'undefined', 'Module.TOTAL_MEMORY has been renamed Module.INITIAL_MEMORY');
legacyModuleProp('asm', 'wasmExports');
-legacyModuleProp('read', 'read_');
legacyModuleProp('readAsync', 'readAsync');
legacyModuleProp('readBinary', 'readBinary');
legacyModuleProp('setWindowTitle', 'setWindowTitle');
@@ -320,10 +238,10 @@ var OPFS = 'OPFS is no longer included by default; build with -lopfs.js';
var NODEFS = 'NODEFS is no longer included by default; build with -lnodefs.js';
-assert(!ENVIRONMENT_IS_SHELL, "shell environment detected but not enabled at build time. Add 'shell' to `-sENVIRONMENT` to enable.");
-
+assert(!ENVIRONMENT_IS_SHELL, 'shell environment detected but not enabled at build time. Add `shell` to `-sENVIRONMENT` to enable.');
// end include: shell.js
+
// include: preamble.js
// === Preamble library stuff ===
@@ -335,12 +253,10 @@ assert(!ENVIRONMENT_IS_SHELL, "shell environment detected but not enabled at bui
// An online HTML version (which may be of a different version of Emscripten)
// is up at http://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html
-var wasmBinary;
-if (Module['wasmBinary']) wasmBinary = Module['wasmBinary'];legacyModuleProp('wasmBinary', 'wasmBinary');
-var noExitRuntime = Module['noExitRuntime'] || true;legacyModuleProp('noExitRuntime', 'noExitRuntime');
+var wasmBinary = Module['wasmBinary'];legacyModuleProp('wasmBinary', 'wasmBinary');
if (typeof WebAssembly != 'object') {
- abort('no native wasm support detected');
+ err('no native wasm support detected');
}
// Wasm globals
@@ -360,6 +276,10 @@ var ABORT = false;
// but only when noExitRuntime is false.
var EXITSTATUS;
+// In STRICT mode, we only define assert() when ASSERTIONS is set. i.e. we
+// don't define it at all in release modes. This matches the behaviour of
+// MINIMAL_RUNTIME.
+// TODO(sbc): Make this the default even without STRICT enabled.
/** @type {function(*, string=)} */
function assert(condition, text) {
if (!condition) {
@@ -390,6 +310,7 @@ var HEAP,
/** @type {!Float64Array} */
HEAPF64;
+// include: runtime_shared.js
function updateMemoryViews() {
var b = wasmMemory.buffer;
Module['HEAP8'] = HEAP8 = new Int8Array(b);
@@ -402,6 +323,7 @@ function updateMemoryViews() {
Module['HEAPF64'] = HEAPF64 = new Float64Array(b);
}
+// end include: runtime_shared.js
assert(!Module['STACK_SIZE'], 'STACK_SIZE can no longer be set at runtime. Use -sSTACK_SIZE at link time')
assert(typeof Int32Array != 'undefined' && typeof Float64Array !== 'undefined' && Int32Array.prototype.subarray != undefined && Int32Array.prototype.set != undefined,
@@ -449,16 +371,6 @@ function checkStackCookie() {
}
}
// end include: runtime_stack_check.js
-// include: runtime_assertions.js
-// Endianness check
-(function() {
- var h16 = new Int16Array(1);
- var h8 = new Int8Array(h16.buffer);
- h16[0] = 0x6373;
- if (h8[0] !== 0x73 || h8[1] !== 0x63) throw 'Runtime error: expected the system to be little-endian! (Run with -sSUPPORT_BIG_ENDIAN to bypass)';
-})();
-
-// end include: runtime_assertions.js
var __ATPRERUN__ = []; // functions called before the runtime is initialized
var __ATINIT__ = []; // functions called during startup
var __ATMAIN__ = []; // functions called when main() is to be run
@@ -467,18 +379,11 @@ var __ATPOSTRUN__ = []; // functions called after the main() is called
var runtimeInitialized = false;
-var runtimeKeepaliveCounter = 0;
-
-function keepRuntimeAlive() {
- return noExitRuntime || runtimeKeepaliveCounter > 0;
-}
-
function preRun() {
- if (Module['preRun']) {
- if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']];
- while (Module['preRun'].length) {
- addOnPreRun(Module['preRun'].shift());
- }
+ var preRuns = Module['preRun'];
+ if (preRuns) {
+ if (typeof preRuns == 'function') preRuns = [preRuns];
+ preRuns.forEach(addOnPreRun);
}
callRuntimeCallbacks(__ATPRERUN__);
}
@@ -502,11 +407,10 @@ function preMain() {
function postRun() {
checkStackCookie();
- if (Module['postRun']) {
- if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']];
- while (Module['postRun'].length) {
- addOnPostRun(Module['postRun'].shift());
- }
+ var postRuns = Module['postRun'];
+ if (postRuns) {
+ if (typeof postRuns == 'function') postRuns = [postRuns];
+ postRuns.forEach(addOnPostRun);
}
callRuntimeCallbacks(__ATPOSTRUN__);
@@ -568,9 +472,7 @@ function getUniqueRunDependency(id) {
function addRunDependency(id) {
runDependencies++;
- if (Module['monitorRunDependencies']) {
- Module['monitorRunDependencies'](runDependencies);
- }
+ Module['monitorRunDependencies']?.(runDependencies);
if (id) {
assert(!runDependencyTracking[id]);
@@ -604,9 +506,7 @@ function addRunDependency(id) {
function removeRunDependency(id) {
runDependencies--;
- if (Module['monitorRunDependencies']) {
- Module['monitorRunDependencies'](runDependencies);
- }
+ Module['monitorRunDependencies']?.(runDependencies);
if (id) {
assert(runDependencyTracking[id]);
@@ -629,9 +529,7 @@ function removeRunDependency(id) {
/** @param {string|number=} what */
function abort(what) {
- if (Module['onAbort']) {
- Module['onAbort'](what);
- }
+ Module['onAbort']?.(what);
what = 'Aborted(' + what + ')';
// TODO(sbc): Should we remove printing and leave it up to whoever
@@ -639,7 +537,6 @@ function abort(what) {
err(what);
ABORT = true;
- EXITSTATUS = 1;
// Use a wasm runtime error, because a JS error might be seen as a foreign
// exception, which means we'd run destructors on it. We need the error to
@@ -651,7 +548,7 @@ function abort(what) {
// allows this in the wasm spec.
// Suppress closure compiler warning here. Closure compiler's builtin extern
- // defintion for WebAssembly.RuntimeError claims it takes no arguments even
+ // definition for WebAssembly.RuntimeError claims it takes no arguments even
// though it can.
// TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure gets fixed.
/** @suppress {checkTypes} */
@@ -688,33 +585,40 @@ Module['FS_createPreloadedFile'] = FS.createPreloadedFile;
// Prefix of data URIs emitted by SINGLE_FILE and related options.
var dataURIPrefix = 'data:application/octet-stream;base64,';
-// Indicates whether filename is a base64 data URI.
-function isDataURI(filename) {
- // Prefix of data URIs emitted by SINGLE_FILE and related options.
- return filename.startsWith(dataURIPrefix);
-}
-
-// Indicates whether filename is delivered via file protocol (as opposed to http/https)
-function isFileURI(filename) {
- return filename.startsWith('file://');
-}
+/**
+ * Indicates whether filename is a base64 data URI.
+ * @noinline
+ */
+var isDataURI = (filename) => filename.startsWith(dataURIPrefix);
+
+/**
+ * Indicates whether filename is delivered via file protocol (as opposed to http/https)
+ * @noinline
+ */
+var isFileURI = (filename) => filename.startsWith('file://');
// end include: URIUtils.js
-function createExportWrapper(name) {
- return function() {
+function createExportWrapper(name, nargs) {
+ return (...args) => {
assert(runtimeInitialized, `native function \`${name}\` called before runtime initialization`);
var f = wasmExports[name];
assert(f, `exported native function \`${name}\` not found`);
- return f.apply(null, arguments);
+ // Only assert for too many arguments. Too few can be valid since the missing arguments will be zero filled.
+ assert(args.length <= nargs, `native function \`${name}\` called with ${args.length} args but expects ${nargs}`);
+ return f(...args);
};
}
// include: runtime_exceptions.js
// end include: runtime_exceptions.js
+function findWasmBinary() {
+ var f = 'output.wasm';
+ if (!isDataURI(f)) {
+ return locateFile(f);
+ }
+ return f;
+}
+
var wasmBinaryFile;
- wasmBinaryFile = 'output.wasm';
- if (!isDataURI(wasmBinaryFile)) {
- wasmBinaryFile = locateFile(wasmBinaryFile);
- }
function getBinarySync(file) {
if (file == wasmBinaryFile && wasmBinary) {
@@ -723,33 +627,19 @@ function getBinarySync(file) {
if (readBinary) {
return readBinary(file);
}
- throw "both async and sync fetching of the wasm failed";
+ throw 'both async and sync fetching of the wasm failed';
}
function getBinaryPromise(binaryFile) {
- // If we don't have the binary yet, try to load it asynchronously.
- // Fetch has some additional restrictions over XHR, like it can't be used on a file:// url.
- // See https://github.com/github/fetch/pull/92#issuecomment-140665932
- // Cordova or Electron apps are typically loaded from a file:// url.
- // So use fetch if it is available and the url is not a file, otherwise fall back to XHR.
+ // If we don't have the binary yet, load it asynchronously using readAsync.
if (!wasmBinary
- && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)) {
- if (typeof fetch == 'function'
- && !isFileURI(binaryFile)
- ) {
- return fetch(binaryFile, { credentials: 'same-origin' }).then((response) => {
- if (!response['ok']) {
- throw "failed to load wasm binary file at '" + binaryFile + "'";
- }
- return response['arrayBuffer']();
- }).catch(() => getBinarySync(binaryFile));
- }
- else if (readAsync) {
- // fetch is not available or url is file => try XHR (readAsync uses XHR internally)
- return new Promise((resolve, reject) => {
- readAsync(binaryFile, (response) => resolve(new Uint8Array(/** @type{!ArrayBuffer} */(response))), reject)
- });
- }
+ ) {
+ // Fetch the binary using readAsync
+ return readAsync(binaryFile).then(
+ (response) => new Uint8Array(/** @type{!ArrayBuffer} */(response)),
+ // Fall back to getBinarySync if readAsync fails
+ () => getBinarySync(binaryFile)
+ );
}
// Otherwise, getBinarySync should be able to get it synchronously
@@ -759,8 +649,6 @@ function getBinaryPromise(binaryFile) {
function instantiateArrayBuffer(binaryFile, imports, receiver) {
return getBinaryPromise(binaryFile).then((binary) => {
return WebAssembly.instantiate(binary, imports);
- }).then((instance) => {
- return instance;
}).then(receiver, (reason) => {
err(`failed to asynchronously prepare wasm: ${reason}`);
@@ -808,14 +696,18 @@ function instantiateAsync(binary, binaryFile, imports, callback) {
return instantiateArrayBuffer(binaryFile, imports, callback);
}
-// Create the wasm instance.
-// Receives the wasm imports, returns the exports.
-function createWasm() {
+function getWasmImports() {
// prepare imports
- var info = {
+ return {
'env': wasmImports,
'wasi_snapshot_preview1': wasmImports,
- };
+ }
+}
+
+// Create the wasm instance.
+// Receives the wasm imports, returns the exports.
+function createWasm() {
+ var info = getWasmImports();
// Load the wasm module and create an instance of using native support in the JS engine.
// handle a generated wasm instance, receiving its exports and
// performing other necessary setup
@@ -827,16 +719,12 @@ function createWasm() {
wasmMemory = wasmExports['memory'];
- assert(wasmMemory, "memory not found in wasm exports");
- // This assertion doesn't hold when emscripten is run in --post-link
- // mode.
- // TODO(sbc): Read INITIAL_MEMORY out of the wasm file in post-link mode.
- //assert(wasmMemory.buffer.byteLength === 16777216);
+ assert(wasmMemory, 'memory not found in wasm exports');
updateMemoryViews();
wasmTable = wasmExports['__indirect_function_table'];
- assert(wasmTable, "table not found in wasm exports");
+ assert(wasmTable, 'table not found in wasm exports');
addOnInit(wasmExports['__wasm_call_ctors']);
@@ -868,7 +756,6 @@ function createWasm() {
// Also pthreads and wasm workers initialize the wasm instance through this
// path.
if (Module['instantiateWasm']) {
-
try {
return Module['instantiateWasm'](info, receiveInstance);
} catch(e) {
@@ -877,6 +764,8 @@ function createWasm() {
}
}
+ wasmBinaryFile ??= findWasmBinary();
+
instantiateAsync(wasmBinary, wasmBinaryFile, info, receiveInstantiationResult);
return {}; // no exports yet; we'll fill them in later
}
@@ -886,12 +775,24 @@ var tempDouble;
var tempI64;
// include: runtime_debug.js
-function legacyModuleProp(prop, newName, incomming=true) {
+// Endianness check
+(() => {
+ var h16 = new Int16Array(1);
+ var h8 = new Int8Array(h16.buffer);
+ h16[0] = 0x6373;
+ if (h8[0] !== 0x73 || h8[1] !== 0x63) throw 'Runtime error: expected the system to be little-endian! (Run with -sSUPPORT_BIG_ENDIAN to bypass)';
+})();
+
+if (Module['ENVIRONMENT']) {
+ throw new Error('Module.ENVIRONMENT has been deprecated. To force the environment, use the ENVIRONMENT compile-time option (for example, -sENVIRONMENT=web or -sENVIRONMENT=node)');
+}
+
+function legacyModuleProp(prop, newName, incoming=true) {
if (!Object.getOwnPropertyDescriptor(Module, prop)) {
Object.defineProperty(Module, prop, {
configurable: true,
get() {
- let extra = incomming ? ' (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)' : '';
+ let extra = incoming ? ' (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)' : '';
abort(`\`Module.${prop}\` has been replaced by \`${newName}\`` + extra);
}
@@ -918,46 +819,52 @@ function isExportedByForceFilesystem(name) {
name === 'removeRunDependency';
}
-function missingGlobal(sym, msg) {
- if (typeof globalThis !== 'undefined') {
+/**
+ * Intercept access to a global symbol. This enables us to give informative
+ * warnings/errors when folks attempt to use symbols they did not include in
+ * their build, or no symbols that no longer exist.
+ */
+function hookGlobalSymbolAccess(sym, func) {
+ if (typeof globalThis != 'undefined' && !Object.getOwnPropertyDescriptor(globalThis, sym)) {
Object.defineProperty(globalThis, sym, {
configurable: true,
get() {
- warnOnce('`' + sym + '` is not longer defined by emscripten. ' + msg);
+ func();
return undefined;
}
});
}
}
+function missingGlobal(sym, msg) {
+ hookGlobalSymbolAccess(sym, () => {
+ warnOnce(`\`${sym}\` is not longer defined by emscripten. ${msg}`);
+ });
+}
+
missingGlobal('buffer', 'Please use HEAP8.buffer or wasmMemory.buffer');
missingGlobal('asm', 'Please use wasmExports instead');
function missingLibrarySymbol(sym) {
- if (typeof globalThis !== 'undefined' && !Object.getOwnPropertyDescriptor(globalThis, sym)) {
- Object.defineProperty(globalThis, sym, {
- configurable: true,
- get() {
- // Can't `abort()` here because it would break code that does runtime
- // checks. e.g. `if (typeof SDL === 'undefined')`.
- var msg = '`' + sym + '` is a library symbol and not included by default; add it to your library.js __deps or to DEFAULT_LIBRARY_FUNCS_TO_INCLUDE on the command line';
- // DEFAULT_LIBRARY_FUNCS_TO_INCLUDE requires the name as it appears in
- // library.js, which means $name for a JS name with no prefix, or name
- // for a JS name like _name.
- var librarySymbol = sym;
- if (!librarySymbol.startsWith('_')) {
- librarySymbol = '$' + sym;
- }
- msg += " (e.g. -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE='" + librarySymbol + "')";
- if (isExportedByForceFilesystem(sym)) {
- msg += '. Alternatively, forcing filesystem support (-sFORCE_FILESYSTEM) can export this for you';
- }
- warnOnce(msg);
- return undefined;
- }
- });
- }
- // Any symbol that is not included from the JS libary is also (by definition)
+ hookGlobalSymbolAccess(sym, () => {
+ // Can't `abort()` here because it would break code that does runtime
+ // checks. e.g. `if (typeof SDL === 'undefined')`.
+ var msg = `\`${sym}\` is a library symbol and not included by default; add it to your library.js __deps or to DEFAULT_LIBRARY_FUNCS_TO_INCLUDE on the command line`;
+ // DEFAULT_LIBRARY_FUNCS_TO_INCLUDE requires the name as it appears in
+ // library.js, which means $name for a JS name with no prefix, or name
+ // for a JS name like _name.
+ var librarySymbol = sym;
+ if (!librarySymbol.startsWith('_')) {
+ librarySymbol = '$' + sym;
+ }
+ msg += ` (e.g. -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE='${librarySymbol}')`;
+ if (isExportedByForceFilesystem(sym)) {
+ msg += '. Alternatively, forcing filesystem support (-sFORCE_FILESYSTEM) can export this for you';
+ }
+ warnOnce(msg);
+ });
+
+ // Any symbol that is not included from the JS library is also (by definition)
// not exported on the Module object.
unexportedRuntimeSymbol(sym);
}
@@ -967,7 +874,7 @@ function unexportedRuntimeSymbol(sym) {
Object.defineProperty(Module, sym, {
configurable: true,
get() {
- var msg = "'" + sym + "' was not exported. add it to EXPORTED_RUNTIME_METHODS (see the Emscripten FAQ)";
+ var msg = `'${sym}' was not exported. add it to EXPORTED_RUNTIME_METHODS (see the Emscripten FAQ)`;
if (isExportedByForceFilesystem(sym)) {
msg += '. Alternatively, forcing filesystem support (-sFORCE_FILESYSTEM) can export this for you';
}
@@ -978,16 +885,16 @@ function unexportedRuntimeSymbol(sym) {
}
// Used by XXXXX_DEBUG settings to output debug messages.
-function dbg(text) {
+function dbg(...args) {
// TODO(sbc): Make this configurable somehow. Its not always convenient for
// logging to show up as warnings.
- console.warn.apply(console, arguments);
+ console.warn(...args);
}
// end include: runtime_debug.js
// === Body ===
-
// end include: preamble.js
+
/** @constructor */
function ExitStatus(status) {
this.name = 'ExitStatus';
@@ -996,10 +903,8 @@ function dbg(text) {
}
var callRuntimeCallbacks = (callbacks) => {
- while (callbacks.length > 0) {
- // Pass the module as the first argument.
- callbacks.shift()(Module);
- }
+ // Pass the module as the first argument.
+ callbacks.forEach((f) => f(Module));
};
@@ -1010,8 +915,8 @@ function dbg(text) {
function getValue(ptr, type = 'i8') {
if (type.endsWith('*')) type = '*';
switch (type) {
- case 'i1': return HEAP8[((ptr)>>0)];
- case 'i8': return HEAP8[((ptr)>>0)];
+ case 'i1': return HEAP8[ptr];
+ case 'i8': return HEAP8[ptr];
case 'i16': return HEAP16[((ptr)>>1)];
case 'i32': return HEAP32[((ptr)>>2)];
case 'i64': abort('to do getValue(i64) use WASM_BIGINT');
@@ -1022,6 +927,8 @@ function dbg(text) {
}
}
+ var noExitRuntime = Module['noExitRuntime'] || true;
+
var ptrToString = (ptr) => {
assert(typeof ptr === 'number');
// With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned.
@@ -1038,8 +945,8 @@ function dbg(text) {
function setValue(ptr, value, type = 'i8') {
if (type.endsWith('*')) type = '*';
switch (type) {
- case 'i1': HEAP8[((ptr)>>0)] = value; break;
- case 'i8': HEAP8[((ptr)>>0)] = value; break;
+ case 'i1': HEAP8[ptr] = value; break;
+ case 'i8': HEAP8[ptr] = value; break;
case 'i16': HEAP16[((ptr)>>1)] = value; break;
case 'i32': HEAP32[((ptr)>>2)] = value; break;
case 'i64': abort('to do setValue(i64) use WASM_BIGINT');
@@ -1050,8 +957,12 @@ function dbg(text) {
}
}
+ var stackRestore = (val) => __emscripten_stack_restore(val);
+
+ var stackSave = () => _emscripten_stack_get_current();
+
var warnOnce = (text) => {
- if (!warnOnce.shown) warnOnce.shown = {};
+ warnOnce.shown ||= {};
if (!warnOnce.shown[text]) {
warnOnce.shown[text] = 1;
if (ENVIRONMENT_IS_NODE) text = 'warning: ' + text;
@@ -1059,6 +970,87 @@ function dbg(text) {
}
};
+ var UTF8Decoder = typeof TextDecoder != 'undefined' ? new TextDecoder() : undefined;
+
+ /**
+ * Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given
+ * array that contains uint8 values, returns a copy of that string as a
+ * Javascript String object.
+ * heapOrArray is either a regular array, or a JavaScript typed array view.
+ * @param {number=} idx
+ * @param {number=} maxBytesToRead
+ * @return {string}
+ */
+ var UTF8ArrayToString = (heapOrArray, idx = 0, maxBytesToRead = NaN) => {
+ var endIdx = idx + maxBytesToRead;
+ var endPtr = idx;
+ // TextDecoder needs to know the byte length in advance, it doesn't stop on
+ // null terminator by itself. Also, use the length info to avoid running tiny
+ // strings through TextDecoder, since .subarray() allocates garbage.
+ // (As a tiny code save trick, compare endPtr against endIdx using a negation,
+ // so that undefined/NaN means Infinity)
+ while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr;
+
+ if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) {
+ return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr));
+ }
+ var str = '';
+ // If building with TextDecoder, we have already computed the string length
+ // above, so test loop end condition against that
+ while (idx < endPtr) {
+ // For UTF8 byte structure, see:
+ // http://en.wikipedia.org/wiki/UTF-8#Description
+ // https://www.ietf.org/rfc/rfc2279.txt
+ // https://tools.ietf.org/html/rfc3629
+ var u0 = heapOrArray[idx++];
+ if (!(u0 & 0x80)) { str += String.fromCharCode(u0); continue; }
+ var u1 = heapOrArray[idx++] & 63;
+ if ((u0 & 0xE0) == 0xC0) { str += String.fromCharCode(((u0 & 31) << 6) | u1); continue; }
+ var u2 = heapOrArray[idx++] & 63;
+ if ((u0 & 0xF0) == 0xE0) {
+ u0 = ((u0 & 15) << 12) | (u1 << 6) | u2;
+ } else {
+ if ((u0 & 0xF8) != 0xF0) warnOnce('Invalid UTF-8 leading byte ' + ptrToString(u0) + ' encountered when deserializing a UTF-8 string in wasm memory to a JS string!');
+ u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63);
+ }
+
+ if (u0 < 0x10000) {
+ str += String.fromCharCode(u0);
+ } else {
+ var ch = u0 - 0x10000;
+ str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF));
+ }
+ }
+ return str;
+ };
+
+ /**
+ * Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the
+ * emscripten HEAP, returns a copy of that string as a Javascript String object.
+ *
+ * @param {number} ptr
+ * @param {number=} maxBytesToRead - An optional length that specifies the
+ * maximum number of bytes to read. You can omit this parameter to scan the
+ * string until the first 0 byte. If maxBytesToRead is passed, and the string
+ * at [ptr, ptr+maxBytesToReadr[ contains a null byte in the middle, then the
+ * string will cut short at that byte index (i.e. maxBytesToRead will not
+ * produce a string of exact length [ptr, ptr+maxBytesToRead[) N.B. mixing
+ * frequent uses of UTF8ToString() with and without maxBytesToRead may throw
+ * JS JIT optimizations off, so it is worth to consider consistently using one
+ * @return {string}
+ */
+ var UTF8ToString = (ptr, maxBytesToRead) => {
+ assert(typeof ptr == 'number', `UTF8ToString expects a number (got ${typeof ptr})`);
+ return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : '';
+ };
+ var ___assert_fail = (condition, filename, line, func) => {
+ abort(`Assertion failed: ${UTF8ToString(condition)}, at: ` + [filename ? UTF8ToString(filename) : 'unknown filename', line, func ? UTF8ToString(func) : 'unknown function']);
+ };
+
+ var __abort_js = () => {
+ abort('native code called abort()');
+ };
+
function __emscripten_fetch_free(id) {
if (Fetch.xhrs.has(id)) {
var xhr = Fetch.xhrs.get(id);
@@ -1070,9 +1062,9 @@ function dbg(text) {
}
}
- var isLeapYear = (year) => {
- return year%4 === 0 && (year%100 !== 0 || year%400 === 0);
- };
+ var __emscripten_memcpy_js = (dest, src, num) => HEAPU8.copyWithin(dest, src, src + num);
+
+ var isLeapYear = (year) => year%4 === 0 && (year%100 !== 0 || year%400 === 0);
var MONTH_DAYS_LEAP_CUMULATIVE = [0,31,60,91,121,152,182,213,244,274,305,335];
@@ -1091,7 +1083,7 @@ function dbg(text) {
return ((hi + 0x200000) >>> 0 < 0x400001 - !!lo) ? (lo >>> 0) + hi * 4294967296 : NaN;
};
function __localtime_js(time_low, time_high,tmPtr) {
- var time = convertI32PairToI53Checked(time_low, time_high);;
+ var time = convertI32PairToI53Checked(time_low, time_high);
var date = new Date(time*1000);
@@ -1116,29 +1108,8 @@ function dbg(text) {
;
}
- var lengthBytesUTF8 = (str) => {
- var len = 0;
- for (var i = 0; i < str.length; ++i) {
- // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code
- // unit, not a Unicode code point of the character! So decode
- // UTF16->UTF32->UTF8.
- // See http://unicode.org/faq/utf_bom.html#utf16-3
- var c = str.charCodeAt(i); // possibly a lead surrogate
- if (c <= 0x7F) {
- len++;
- } else if (c <= 0x7FF) {
- len += 2;
- } else if (c >= 0xD800 && c <= 0xDFFF) {
- len += 4; ++i;
- } else {
- len += 3;
- }
- }
- return len;
- };
-
var stringToUTF8Array = (str, heap, outIdx, maxBytesToWrite) => {
- assert(typeof str === 'string');
+ assert(typeof str === 'string', `stringToUTF8Array expects a string (got ${typeof str})`);
// Parameter maxBytesToWrite is not optional. Negative values, 0, null,
// undefined and false each don't write out any bytes.
if (!(maxBytesToWrite > 0))
@@ -1189,13 +1160,27 @@ function dbg(text) {
return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite);
};
- var stringToNewUTF8 = (str) => {
- var size = lengthBytesUTF8(str) + 1;
- var ret = _malloc(size);
- if (ret) stringToUTF8(str, ret, size);
- return ret;
+ var lengthBytesUTF8 = (str) => {
+ var len = 0;
+ for (var i = 0; i < str.length; ++i) {
+ // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code
+ // unit, not a Unicode code point of the character! So decode
+ // UTF16->UTF32->UTF8.
+ // See http://unicode.org/faq/utf_bom.html#utf16-3
+ var c = str.charCodeAt(i); // possibly a lead surrogate
+ if (c <= 0x7F) {
+ len++;
+ } else if (c <= 0x7FF) {
+ len += 2;
+ } else if (c >= 0xD800 && c <= 0xDFFF) {
+ len += 4; ++i;
+ } else {
+ len += 3;
+ }
+ }
+ return len;
};
- var __tzset_js = (timezone, daylight, tzname) => {
+ var __tzset_js = (timezone, daylight, std_name, dst_name) => {
// TODO: Use (malleable) environment variables instead of system settings.
var currentYear = new Date().getFullYear();
var winter = new Date(currentYear, 0, 1);
@@ -1203,9 +1188,12 @@ function dbg(text) {
var winterOffset = winter.getTimezoneOffset();
var summerOffset = summer.getTimezoneOffset();
- // Local standard timezone offset. Local standard time is not adjusted for daylight savings.
- // This code uses the fact that getTimezoneOffset returns a greater value during Standard Time versus Daylight Saving Time (DST).
- // Thus it determines the expected output during Standard Time, and it compares whether the output of the given date the same (Standard) or less (DST).
+ // Local standard timezone offset. Local standard time is not adjusted for
+ // daylight savings. This code uses the fact that getTimezoneOffset returns
+ // a greater value during Standard Time versus Daylight Saving Time (DST).
+ // Thus it determines the expected output during Standard Time, and it
+ // compares whether the output of the given date the same (Standard) or less
+ // (DST).
var stdTimezoneOffset = Math.max(winterOffset, summerOffset);
// timezone is specified as seconds west of UTC ("The external variable
@@ -1217,51 +1205,44 @@ function dbg(text) {
HEAP32[((daylight)>>2)] = Number(winterOffset != summerOffset);
- function extractZone(date) {
- var match = date.toTimeString().match(/\(([A-Za-z ]+)\)$/);
- return match ? match[1] : "GMT";
- };
- var winterName = extractZone(winter);
- var summerName = extractZone(summer);
- var winterNamePtr = stringToNewUTF8(winterName);
- var summerNamePtr = stringToNewUTF8(summerName);
+ var extractZone = (timezoneOffset) => {
+ // Why inverse sign?
+ // Read here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset
+ var sign = timezoneOffset >= 0 ? "-" : "+";
+
+ var absOffset = Math.abs(timezoneOffset)
+ var hours = String(Math.floor(absOffset / 60)).padStart(2, "0");
+ var minutes = String(absOffset % 60).padStart(2, "0");
+
+ return `UTC${sign}${hours}${minutes}`;
+ }
+
+ var winterName = extractZone(winterOffset);
+ var summerName = extractZone(summerOffset);
+ assert(winterName);
+ assert(summerName);
+ assert(lengthBytesUTF8(winterName) <= 16, `timezone name truncated to fit in TZNAME_MAX (${winterName})`);
+ assert(lengthBytesUTF8(summerName) <= 16, `timezone name truncated to fit in TZNAME_MAX (${summerName})`);
if (summerOffset < winterOffset) {
// Northern hemisphere
- HEAPU32[((tzname)>>2)] = winterNamePtr;
- HEAPU32[(((tzname)+(4))>>2)] = summerNamePtr;
+ stringToUTF8(winterName, std_name, 17);
+ stringToUTF8(summerName, dst_name, 17);
} else {
- HEAPU32[((tzname)>>2)] = summerNamePtr;
- HEAPU32[(((tzname)+(4))>>2)] = winterNamePtr;
+ stringToUTF8(winterName, dst_name, 17);
+ stringToUTF8(summerName, std_name, 17);
}
};
- var _abort = () => {
- abort('native code called abort()');
- };
-
var _emscripten_date_now = () => Date.now();
- var withStackSave = (f) => {
- var stack = stackSave();
- var ret = f();
- stackRestore(stack);
- return ret;
- };
var JSEvents = {
- inEventHandler:0,
removeAllEventListeners() {
- for (var i = JSEvents.eventHandlers.length-1; i >= 0; --i) {
- JSEvents._removeHandler(i);
+ while (JSEvents.eventHandlers.length) {
+ JSEvents._removeHandler(JSEvents.eventHandlers.length - 1);
}
- JSEvents.eventHandlers = [];
JSEvents.deferredCalls = [];
},
- registerRemoveEventListeners() {
- if (!JSEvents.removeEventListenersRegistered) {
- __ATEXIT__.push(JSEvents.removeAllEventListeners);
- JSEvents.removeEventListenersRegistered = true;
- }
- },
+ inEventHandler:0,
deferredCalls:[],
deferCall(targetFunction, precedence, argsList) {
function arraysHaveEqualContent(arrA, arrB) {
@@ -1273,8 +1254,7 @@ function dbg(text) {
return true;
}
// Test if the given call was already queued, and if so, don't add it again.
- for (var i in JSEvents.deferredCalls) {
- var call = JSEvents.deferredCalls[i];
+ for (var call of JSEvents.deferredCalls) {
if (call.targetFunction == targetFunction && arraysHaveEqualContent(call.argsList, argsList)) {
return;
}
@@ -1288,12 +1268,7 @@ function dbg(text) {
JSEvents.deferredCalls.sort((x,y) => x.precedence < y.precedence);
},
removeDeferredCalls(targetFunction) {
- for (var i = 0; i < JSEvents.deferredCalls.length; ++i) {
- if (JSEvents.deferredCalls[i].targetFunction == targetFunction) {
- JSEvents.deferredCalls.splice(i, 1);
- --i;
- }
- }
+ JSEvents.deferredCalls = JSEvents.deferredCalls.filter((call) => call.targetFunction != targetFunction);
},
canPerformEventHandlerRequests() {
if (navigator.userActivation) {
@@ -1311,11 +1286,10 @@ function dbg(text) {
if (!JSEvents.canPerformEventHandlerRequests()) {
return;
}
- for (var i = 0; i < JSEvents.deferredCalls.length; ++i) {
- var call = JSEvents.deferredCalls[i];
- JSEvents.deferredCalls.splice(i, 1);
- --i;
- call.targetFunction.apply(null, call.argsList);
+ var deferredCalls = JSEvents.deferredCalls;
+ JSEvents.deferredCalls = [];
+ for (var call of deferredCalls) {
+ call.targetFunction(...call.argsList);
}
},
eventHandlers:[],
@@ -1338,25 +1312,25 @@ function dbg(text) {
console.dir(eventHandler);
return -4;
}
- var jsEventHandler = function jsEventHandler(event) {
- // Increment nesting count for the event handler.
- ++JSEvents.inEventHandler;
- JSEvents.currentEventHandler = eventHandler;
- // Process any old deferred calls the user has placed.
- JSEvents.runDeferredCalls();
- // Process the actual event, calls back to user C code handler.
- eventHandler.handlerFunc(event);
- // Process any new deferred calls that were placed right now from this event handler.
- JSEvents.runDeferredCalls();
- // Out of event handler - restore nesting count.
- --JSEvents.inEventHandler;
- };
-
if (eventHandler.callbackfunc) {
- eventHandler.eventListenerFunc = jsEventHandler;
- eventHandler.target.addEventListener(eventHandler.eventTypeString, jsEventHandler, eventHandler.useCapture);
+ eventHandler.eventListenerFunc = function(event) {
+ // Increment nesting count for the event handler.
+ ++JSEvents.inEventHandler;
+ JSEvents.currentEventHandler = eventHandler;
+ // Process any old deferred calls the user has placed.
+ JSEvents.runDeferredCalls();
+ // Process the actual event, calls back to user C code handler.
+ eventHandler.handlerFunc(event);
+ // Process any new deferred calls that were placed right now from this event handler.
+ JSEvents.runDeferredCalls();
+ // Out of event handler - restore nesting count.
+ --JSEvents.inEventHandler;
+ };
+
+ eventHandler.target.addEventListener(eventHandler.eventTypeString,
+ eventHandler.eventListenerFunc,
+ eventHandler.useCapture);
JSEvents.eventHandlers.push(eventHandler);
- JSEvents.registerRemoveEventListeners();
} else {
for (var i = 0; i < JSEvents.eventHandlers.length; ++i) {
if (JSEvents.eventHandlers[i].target == eventHandler.target
@@ -1371,7 +1345,7 @@ function dbg(text) {
if (!target) return '';
if (target == window) return '#window';
if (target == screen) return '#screen';
- return (target && target.nodeName) ? target.nodeName : '';
+ return target?.nodeName || '';
},
fullscreenEnabled() {
return document.fullscreenEnabled
@@ -1382,79 +1356,6 @@ function dbg(text) {
},
};
- var UTF8Decoder = typeof TextDecoder != 'undefined' ? new TextDecoder('utf8') : undefined;
-
- /**
- * Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given
- * array that contains uint8 values, returns a copy of that string as a
- * Javascript String object.
- * heapOrArray is either a regular array, or a JavaScript typed array view.
- * @param {number} idx
- * @param {number=} maxBytesToRead
- * @return {string}
- */
- var UTF8ArrayToString = (heapOrArray, idx, maxBytesToRead) => {
- var endIdx = idx + maxBytesToRead;
- var endPtr = idx;
- // TextDecoder needs to know the byte length in advance, it doesn't stop on
- // null terminator by itself. Also, use the length info to avoid running tiny
- // strings through TextDecoder, since .subarray() allocates garbage.
- // (As a tiny code save trick, compare endPtr against endIdx using a negation,
- // so that undefined means Infinity)
- while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr;
-
- if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) {
- return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr));
- }
- var str = '';
- // If building with TextDecoder, we have already computed the string length
- // above, so test loop end condition against that
- while (idx < endPtr) {
- // For UTF8 byte structure, see:
- // http://en.wikipedia.org/wiki/UTF-8#Description
- // https://www.ietf.org/rfc/rfc2279.txt
- // https://tools.ietf.org/html/rfc3629
- var u0 = heapOrArray[idx++];
- if (!(u0 & 0x80)) { str += String.fromCharCode(u0); continue; }
- var u1 = heapOrArray[idx++] & 63;
- if ((u0 & 0xE0) == 0xC0) { str += String.fromCharCode(((u0 & 31) << 6) | u1); continue; }
- var u2 = heapOrArray[idx++] & 63;
- if ((u0 & 0xF0) == 0xE0) {
- u0 = ((u0 & 15) << 12) | (u1 << 6) | u2;
- } else {
- if ((u0 & 0xF8) != 0xF0) warnOnce('Invalid UTF-8 leading byte ' + ptrToString(u0) + ' encountered when deserializing a UTF-8 string in wasm memory to a JS string!');
- u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63);
- }
-
- if (u0 < 0x10000) {
- str += String.fromCharCode(u0);
- } else {
- var ch = u0 - 0x10000;
- str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF));
- }
- }
- return str;
- };
-
- /**
- * Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the
- * emscripten HEAP, returns a copy of that string as a Javascript String object.
- *
- * @param {number} ptr
- * @param {number=} maxBytesToRead - An optional length that specifies the
- * maximum number of bytes to read. You can omit this parameter to scan the
- * string until the first 0 byte. If maxBytesToRead is passed, and the string
- * at [ptr, ptr+maxBytesToReadr[ contains a null byte in the middle, then the
- * string will cut short at that byte index (i.e. maxBytesToRead will not
- * produce a string of exact length [ptr, ptr+maxBytesToRead[) N.B. mixing
- * frequent uses of UTF8ToString() with and without maxBytesToRead may throw
- * JS JIT optimizations off, so it is worth to consider consistently using one
- * @return {string}
- */
- var UTF8ToString = (ptr, maxBytesToRead) => {
- assert(typeof ptr == 'number');
- return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : '';
- };
var maybeCStringToJsString = (cString) => {
// "cString > 2" checks if the input is a number, and isn't of the special
// values we accept here, EMSCRIPTEN_EVENT_TARGET_* (which map to 0, 1, 2).
@@ -1463,6 +1364,7 @@ function dbg(text) {
return cString > 2 ? UTF8ToString(cString) : cString;
};
+ /** @type {Object} */
var specialHTMLTargets = [0, typeof document != 'undefined' ? document : 0, typeof window != 'undefined' ? window : 0];
var findEventTarget = (target) => {
target = maybeCStringToJsString(target);
@@ -1485,10 +1387,9 @@ function dbg(text) {
var _emscripten_is_main_browser_thread = () =>
!ENVIRONMENT_IS_WORKER;
- var _emscripten_memcpy_js = (dest, src, num) => HEAPU8.copyWithin(dest, src, src + num);
-
var wasmTableMirror = [];
+ /** @type {WebAssembly.Table} */
var wasmTable;
var getWasmTableEntry = (funcPtr) => {
var func = wasmTableMirror[funcPtr];
@@ -1496,7 +1397,7 @@ function dbg(text) {
if (funcPtr >= wasmTableMirror.length) wasmTableMirror.length = funcPtr + 1;
wasmTableMirror[funcPtr] = func = wasmTable.get(funcPtr);
}
- assert(wasmTable.get(funcPtr) == func, "JavaScript-side Wasm function table mirror is out of date!");
+ assert(wasmTable.get(funcPtr) == func, 'JavaScript-side Wasm function table mirror is out of date!');
return func;
};
var _emscripten_request_animation_frame_loop = (cb, userData) => {
@@ -1515,9 +1416,14 @@ function dbg(text) {
// casing all heap size related code to treat 0 specially.
2147483648;
+ var alignMemory = (size, alignment) => {
+ assert(alignment, "alignment argument is required");
+ return Math.ceil(size / alignment) * alignment;
+ };
+
var growMemory = (size) => {
var b = wasmMemory.buffer;
- var pages = (size - b.byteLength + 65535) / 65536;
+ var pages = ((size - b.byteLength + 65535) / 65536) | 0;
try {
// round size grow request up to wasm page size (fixed 64KB per spec)
wasmMemory.grow(pages); // .grow() takes a delta compared to the previous size
@@ -1562,8 +1468,6 @@ function dbg(text) {
return false;
}
- var alignUp = (x, multiple) => x + (multiple - x % multiple) % multiple;
-
// Loop through potential heap size increases. If we attempt a too eager
// reservation that fails, cut down on the attempted size and reserve a
// smaller bump instead. (max 3 times, chosen somewhat arbitrarily)
@@ -1572,7 +1476,7 @@ function dbg(text) {
// but limit overreserving (default to capping at +96MB overgrowth at most)
overGrownHeapSize = Math.min(overGrownHeapSize, requestedSize + 100663296 );
- var newSize = Math.min(maxHeapSize, alignUp(Math.max(requestedSize, overGrownHeapSize), 65536));
+ var newSize = Math.min(maxHeapSize, alignMemory(Math.max(requestedSize, overGrownHeapSize), 65536));
var replacement = growMemory(newSize);
if (replacement) {
@@ -1585,7 +1489,7 @@ function dbg(text) {
};
- var findCanvasEventTarget = (target) => findEventTarget(target);
+ var findCanvasEventTarget = findEventTarget;
var _emscripten_set_canvas_element_size = (target, width, height) => {
var canvas = findCanvasEventTarget(target);
if (!canvas) return -4;
@@ -1600,34 +1504,35 @@ function dbg(text) {
var fillMouseEventData = (eventStruct, e, target) => {
assert(eventStruct % 4 == 0);
HEAPF64[((eventStruct)>>3)] = e.timeStamp;
- var idx = eventStruct >> 2;
+ var idx = ((eventStruct)>>2);
HEAP32[idx + 2] = e.screenX;
HEAP32[idx + 3] = e.screenY;
HEAP32[idx + 4] = e.clientX;
HEAP32[idx + 5] = e.clientY;
- HEAP32[idx + 6] = e.ctrlKey;
- HEAP32[idx + 7] = e.shiftKey;
- HEAP32[idx + 8] = e.altKey;
- HEAP32[idx + 9] = e.metaKey;
- HEAP16[idx*2 + 20] = e.button;
- HEAP16[idx*2 + 21] = e.buttons;
-
- HEAP32[idx + 11] = e["movementX"]
+ HEAP8[eventStruct + 24] = e.ctrlKey;
+ HEAP8[eventStruct + 25] = e.shiftKey;
+ HEAP8[eventStruct + 26] = e.altKey;
+ HEAP8[eventStruct + 27] = e.metaKey;
+ HEAP16[idx*2 + 14] = e.button;
+ HEAP16[idx*2 + 15] = e.buttons;
+
+ HEAP32[idx + 8] = e["movementX"]
;
- HEAP32[idx + 12] = e["movementY"]
+ HEAP32[idx + 9] = e["movementY"]
;
+ // Note: rect contains doubles (truncated to placate SAFE_HEAP, which is the same behaviour when writing to HEAP32 anyway)
var rect = getBoundingClientRect(target);
- HEAP32[idx + 13] = e.clientX - rect.left;
- HEAP32[idx + 14] = e.clientY - rect.top;
+ HEAP32[idx + 10] = e.clientX - (rect.left | 0);
+ HEAP32[idx + 11] = e.clientY - (rect.top | 0);
};
var registerMouseEventCallback = (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => {
- if (!JSEvents.mouseEvent) JSEvents.mouseEvent = _malloc(72);
+ JSEvents.mouseEvent ||= _malloc(64);
target = findEventTarget(target);
var mouseEventHandlerFunc = (e = event) => {
@@ -1654,7 +1559,7 @@ function dbg(text) {
var registerUiEventCallback = (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => {
- if (!JSEvents.uiEvent) JSEvents.uiEvent = _malloc(36);
+ JSEvents.uiEvent ||= _malloc(36);
target = findEventTarget(target);
@@ -1672,15 +1577,15 @@ function dbg(text) {
return;
}
var uiEvent = JSEvents.uiEvent;
- HEAP32[((uiEvent)>>2)] = e.detail;
+ HEAP32[((uiEvent)>>2)] = 0; // always zero for resize and scroll
HEAP32[(((uiEvent)+(4))>>2)] = b.clientWidth;
HEAP32[(((uiEvent)+(8))>>2)] = b.clientHeight;
HEAP32[(((uiEvent)+(12))>>2)] = innerWidth;
HEAP32[(((uiEvent)+(16))>>2)] = innerHeight;
HEAP32[(((uiEvent)+(20))>>2)] = outerWidth;
HEAP32[(((uiEvent)+(24))>>2)] = outerHeight;
- HEAP32[(((uiEvent)+(28))>>2)] = pageXOffset;
- HEAP32[(((uiEvent)+(32))>>2)] = pageYOffset;
+ HEAP32[(((uiEvent)+(28))>>2)] = pageXOffset | 0; // scroll offsets are float
+ HEAP32[(((uiEvent)+(32))>>2)] = pageYOffset | 0;
if (getWasmTableEntry(callbackfunc)(eventTypeId, uiEvent, userData)) e.preventDefault();
};
@@ -1698,40 +1603,40 @@ function dbg(text) {
- function handleAllocatorInit() {
- Object.assign(HandleAllocator.prototype, /** @lends {HandleAllocator.prototype} */ {
- get(id) {
- assert(this.allocated[id] !== undefined, `invalid handle: ${id}`);
- return this.allocated[id];
- },
- has(id) {
- return this.allocated[id] !== undefined;
- },
- allocate(handle) {
- var id = this.freelist.pop() || this.allocated.length;
- this.allocated[id] = handle;
- return id;
- },
- free(id) {
- assert(this.allocated[id] !== undefined);
- // Set the slot to `undefined` rather than using `delete` here since
- // apparently arrays with holes in them can be less efficient.
- this.allocated[id] = undefined;
- this.freelist.push(id);
- }
- });
- }
- /** @constructor */
- function HandleAllocator() {
- // Reserve slot 0 so that 0 is always an invalid handle
- this.allocated = [undefined];
- this.freelist = [];
+ class HandleAllocator {
+ constructor() {
+ // TODO(https://github.com/emscripten-core/emscripten/issues/21414):
+ // Use inline field declarations.
+ this.allocated = [undefined];
+ this.freelist = [];
+ }
+ get(id) {
+ assert(this.allocated[id] !== undefined, `invalid handle: ${id}`);
+ return this.allocated[id];
+ }
+ has(id) {
+ return this.allocated[id] !== undefined;
+ }
+ allocate(handle) {
+ var id = this.freelist.pop() || this.allocated.length;
+ this.allocated[id] = handle;
+ return id;
+ }
+ free(id) {
+ assert(this.allocated[id] !== undefined);
+ // Set the slot to `undefined` rather than using `delete` here since
+ // apparently arrays with holes in them can be less efficient.
+ this.allocated[id] = undefined;
+ this.freelist.push(id);
+ }
}
var Fetch = {
openDatabase(dbname, dbversion, onsuccess, onerror) {
try {
var openRequest = indexedDB.open(dbname, dbversion);
- } catch (e) { return onerror(e); }
+ } catch (e) {
+ return onerror(e);
+ }
openRequest.onupgradeneeded = (event) => {
var db = /** @type {IDBDatabase} */ (event.target.result);
@@ -1741,7 +1646,7 @@ function dbg(text) {
db.createObjectStore('FILES');
};
openRequest.onsuccess = (event) => onsuccess(event.target.result);
- openRequest.onerror = (error) => onerror(error);
+ openRequest.onerror = onerror;
},
init() {
Fetch.xhrs = new HandleAllocator();
@@ -1768,9 +1673,9 @@ function dbg(text) {
}
var url_ = UTF8ToString(url);
- var fetch_attr = fetch + 112;
+ var fetch_attr = fetch + 108;
var requestMethod = UTF8ToString(fetch_attr + 0);
- if (!requestMethod) requestMethod = 'GET';
+ requestMethod ||= 'GET';
var timeoutMsecs = HEAPU32[(((fetch_attr)+(56))>>2)];
var userName = HEAPU32[(((fetch_attr)+(68))>>2)];
var password = HEAPU32[(((fetch_attr)+(72))>>2)];
@@ -1788,7 +1693,7 @@ function dbg(text) {
var passwordStr = password ? UTF8ToString(password) : undefined;
var xhr = new XMLHttpRequest();
- xhr.withCredentials = !!HEAPU8[(((fetch_attr)+(60))>>0)];;
+ xhr.withCredentials = !!HEAPU8[(fetch_attr)+(60)];;
xhr.open(requestMethod, url_, !fetchAttrSynchronous, userNameStr, passwordStr);
if (!fetchAttrSynchronous) xhr.timeout = timeoutMsecs; // XHR timeout field is only accessible in async XHRs, and must be set after .open() but before .send().
xhr.url_ = url_; // Save the url for debugging purposes (and for comparing to the responseURL that server side advertised)
@@ -1824,7 +1729,7 @@ function dbg(text) {
function saveResponseAndStatus() {
var ptr = 0;
var ptrLen = 0;
- if (xhr.response && fetchAttrLoadToMemory && HEAPU32[fetch + 12 >> 2] === 0) {
+ if (xhr.response && fetchAttrLoadToMemory && HEAPU32[(((fetch)+(12))>>2)] === 0) {
ptrLen = xhr.response.byteLength;
}
if (ptrLen > 0) {
@@ -1833,7 +1738,7 @@ function dbg(text) {
ptr = _malloc(ptrLen);
HEAPU8.set(new Uint8Array(/** @type{Array<number>} */(xhr.response)), ptr);
}
- HEAPU32[fetch + 12 >> 2] = ptr;
+ HEAPU32[(((fetch)+(12))>>2)] = ptr
writeI53ToI64(fetch + 16, ptrLen);
writeI53ToI64(fetch + 24, 0);
var len = xhr.response ? xhr.response.byteLength : 0;
@@ -1843,8 +1748,8 @@ function dbg(text) {
// the most recent XHR.onprogress handler.
writeI53ToI64(fetch + 32, len);
}
- HEAPU16[fetch + 40 >> 1] = xhr.readyState;
- HEAPU16[fetch + 42 >> 1] = xhr.status;
+ HEAP16[(((fetch)+(40))>>1)] = xhr.readyState
+ HEAP16[(((fetch)+(42))>>1)] = xhr.status
if (xhr.statusText) stringToUTF8(xhr.statusText, fetch + 44, 64);
}
@@ -1855,9 +1760,9 @@ function dbg(text) {
}
saveResponseAndStatus();
if (xhr.status >= 200 && xhr.status < 300) {
- if (onsuccess) onsuccess(fetch, xhr, e);
+ onsuccess?.(fetch, xhr, e);
} else {
- if (onerror) onerror(fetch, xhr, e);
+ onerror?.(fetch, xhr, e);
}
};
xhr.onerror = (e) => {
@@ -1866,14 +1771,14 @@ function dbg(text) {
return;
}
saveResponseAndStatus();
- if (onerror) onerror(fetch, xhr, e);
+ onerror?.(fetch, xhr, e);
};
xhr.ontimeout = (e) => {
// check if xhr was aborted by user and don't try to call back
if (!Fetch.xhrs.has(id)) {
return;
}
- if (onerror) onerror(fetch, xhr, e);
+ onerror?.(fetch, xhr, e);
};
xhr.onprogress = (e) => {
// check if xhr was aborted by user and don't try to call back
@@ -1888,16 +1793,16 @@ function dbg(text) {
ptr = _malloc(ptrLen);
HEAPU8.set(new Uint8Array(/** @type{Array<number>} */(xhr.response)), ptr);
}
- HEAPU32[fetch + 12 >> 2] = ptr;
+ HEAPU32[(((fetch)+(12))>>2)] = ptr
writeI53ToI64(fetch + 16, ptrLen);
writeI53ToI64(fetch + 24, e.loaded - ptrLen);
writeI53ToI64(fetch + 32, e.total);
- HEAPU16[fetch + 40 >> 1] = xhr.readyState;
+ HEAP16[(((fetch)+(40))>>1)] = xhr.readyState
// If loading files from a source that does not give HTTP status code, assume success if we get data bytes
if (xhr.readyState >= 3 && xhr.status === 0 && e.loaded > 0) xhr.status = 200;
- HEAPU16[fetch + 42 >> 1] = xhr.status;
+ HEAP16[(((fetch)+(42))>>1)] = xhr.status
if (xhr.statusText) stringToUTF8(xhr.statusText, fetch + 44, 64);
- if (onprogress) onprogress(fetch, xhr, e);
+ onprogress?.(fetch, xhr, e);
if (ptr) {
_free(ptr);
}
@@ -1908,16 +1813,16 @@ function dbg(text) {
return;
}
- HEAPU16[fetch + 40 >> 1] = xhr.readyState;
+ HEAP16[(((fetch)+(40))>>1)] = xhr.readyState
if (xhr.readyState >= 2) {
- HEAPU16[fetch + 42 >> 1] = xhr.status;
+ HEAP16[(((fetch)+(42))>>1)] = xhr.status
}
- if (onreadystatechange) onreadystatechange(fetch, xhr, e);
+ onreadystatechange?.(fetch, xhr, e);
};
try {
xhr.send(data);
} catch(e) {
- if (onerror) onerror(fetch, xhr, e);
+ onerror?.(fetch, xhr, e);
}
}
@@ -1940,29 +1845,17 @@ function dbg(text) {
};
- var SYSCALLS = {
- varargs:undefined,
- get() {
- assert(SYSCALLS.varargs != undefined);
- // the `+` prepended here is necessary to convince the JSCompiler that varargs is indeed a number.
- var ret = HEAP32[((+SYSCALLS.varargs)>>2)];
- SYSCALLS.varargs += 4;
- return ret;
- },
- getp() { return SYSCALLS.get() },
- getStr(ptr) {
- var ret = UTF8ToString(ptr);
- return ret;
- },
- };
+ var runtimeKeepaliveCounter = 0;
+ var keepRuntimeAlive = () => noExitRuntime || runtimeKeepaliveCounter > 0;
var _proc_exit = (code) => {
EXITSTATUS = code;
if (!keepRuntimeAlive()) {
- if (Module['onExit']) Module['onExit'](code);
+ Module['onExit']?.(code);
ABORT = true;
}
quit_(code, new ExitStatus(code));
};
+
/** @suppress {duplicate } */
/** @param {boolean|number=} implicit */
var exitJS = (status, implicit) => {
@@ -1980,6 +1873,7 @@ function dbg(text) {
};
var _exit = exitJS;
+
var maybeExit = () => {
if (!keepRuntimeAlive()) {
try {
@@ -2025,9 +1919,9 @@ function dbg(text) {
return;
}
- var fetch_attr = fetch + 112;
- var destinationPath = HEAPU32[fetch_attr + 64 >> 2];
- if (!destinationPath) destinationPath = HEAPU32[fetch + 8 >> 2];
+ var fetch_attr = fetch + 108;
+ var destinationPath = HEAPU32[(((fetch_attr)+(64))>>2)];
+ destinationPath ||= HEAPU32[(((fetch)+(8))>>2)];
var destinationPathStr = UTF8ToString(destinationPath);
try {
@@ -2035,8 +1929,8 @@ function dbg(text) {
var packages = transaction.objectStore('FILES');
var putRequest = packages.put(data, destinationPathStr);
putRequest.onsuccess = (event) => {
- HEAPU16[fetch + 40 >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete'
- HEAPU16[fetch + 42 >> 1] = 200; // Mimic XHR HTTP status code 200 "OK"
+ HEAP16[(((fetch)+(40))>>1)] = 4 // Mimic XHR readyState 4 === 'DONE: The operation is complete'
+ HEAP16[(((fetch)+(42))>>1)] = 200 // Mimic XHR HTTP status code 200 "OK"
stringToUTF8("OK", fetch + 44, 64);
onsuccess(fetch, 0, destinationPathStr);
};
@@ -2044,8 +1938,8 @@ function dbg(text) {
// Most likely we got an error if IndexedDB is unwilling to store any more data for this page.
// TODO: Can we identify and break down different IndexedDB-provided errors and convert those
// to more HTTP status codes for more information?
- HEAPU16[fetch + 40 >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete'
- HEAPU16[fetch + 42 >> 1] = 413; // Mimic XHR HTTP status code 413 "Payload Too Large"
+ HEAP16[(((fetch)+(40))>>1)] = 4 // Mimic XHR readyState 4 === 'DONE: The operation is complete'
+ HEAP16[(((fetch)+(42))>>1)] = 413 // Mimic XHR HTTP status code 413 "Payload Too Large"
stringToUTF8("Payload Too Large", fetch + 44, 64);
onerror(fetch, 0, error);
};
@@ -2060,9 +1954,9 @@ function dbg(text) {
return;
}
- var fetch_attr = fetch + 112;
- var path = HEAPU32[fetch_attr + 64 >> 2];
- if (!path) path = HEAPU32[fetch + 8 >> 2];
+ var fetch_attr = fetch + 108;
+ var path = HEAPU32[(((fetch_attr)+(64))>>2)];
+ path ||= HEAPU32[(((fetch)+(8))>>2)];
var pathStr = UTF8ToString(path);
try {
@@ -2077,25 +1971,25 @@ function dbg(text) {
// freed when emscripten_fetch_close() is called.
var ptr = _malloc(len);
HEAPU8.set(new Uint8Array(value), ptr);
- HEAPU32[fetch + 12 >> 2] = ptr;
+ HEAPU32[(((fetch)+(12))>>2)] = ptr;
writeI53ToI64(fetch + 16, len);
writeI53ToI64(fetch + 24, 0);
writeI53ToI64(fetch + 32, len);
- HEAPU16[fetch + 40 >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete'
- HEAPU16[fetch + 42 >> 1] = 200; // Mimic XHR HTTP status code 200 "OK"
+ HEAP16[(((fetch)+(40))>>1)] = 4 // Mimic XHR readyState 4 === 'DONE: The operation is complete'
+ HEAP16[(((fetch)+(42))>>1)] = 200 // Mimic XHR HTTP status code 200 "OK"
stringToUTF8("OK", fetch + 44, 64);
onsuccess(fetch, 0, value);
} else {
// Succeeded to load, but the load came back with the value of undefined, treat that as an error since we never store undefined in db.
- HEAPU16[fetch + 40 >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete'
- HEAPU16[fetch + 42 >> 1] = 404; // Mimic XHR HTTP status code 404 "Not Found"
+ HEAP16[(((fetch)+(40))>>1)] = 4 // Mimic XHR readyState 4 === 'DONE: The operation is complete'
+ HEAP16[(((fetch)+(42))>>1)] = 404 // Mimic XHR HTTP status code 404 "Not Found"
stringToUTF8("Not Found", fetch + 44, 64);
onerror(fetch, 0, 'no data');
}
};
getRequest.onerror = (error) => {
- HEAPU16[fetch + 40 >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete'
- HEAPU16[fetch + 42 >> 1] = 404; // Mimic XHR HTTP status code 404 "Not Found"
+ HEAP16[(((fetch)+(40))>>1)] = 4 // Mimic XHR readyState 4 === 'DONE: The operation is complete'
+ HEAP16[(((fetch)+(42))>>1)] = 404 // Mimic XHR HTTP status code 404 "Not Found"
stringToUTF8("Not Found", fetch + 44, 64);
onerror(fetch, 0, error);
};
@@ -2110,9 +2004,10 @@ function dbg(text) {
return;
}
- var fetch_attr = fetch + 112;
- var path = HEAPU32[fetch_attr + 64 >> 2];
- if (!path) path = HEAPU32[fetch + 8 >> 2];
+ var fetch_attr = fetch + 108;
+ var path = HEAPU32[(((fetch_attr)+(64))>>2)];
+ path ||= HEAPU32[(((fetch)+(8))>>2)];
+
var pathStr = UTF8ToString(path);
try {
@@ -2121,18 +2016,20 @@ function dbg(text) {
var request = packages.delete(pathStr);
request.onsuccess = (event) => {
var value = event.target.result;
- HEAPU32[fetch + 12 >> 2] = 0;
+ HEAPU32[(((fetch)+(12))>>2)] = 0;
writeI53ToI64(fetch + 16, 0);
writeI53ToI64(fetch + 24, 0);
writeI53ToI64(fetch + 32, 0);
- HEAPU16[fetch + 40 >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete'
- HEAPU16[fetch + 42 >> 1] = 200; // Mimic XHR HTTP status code 200 "OK"
+ // Mimic XHR readyState 4 === 'DONE: The operation is complete'
+ HEAP16[(((fetch)+(40))>>1)] = 4;
+ // Mimic XHR HTTP status code 200 "OK"
+ HEAP16[(((fetch)+(42))>>1)] = 200;
stringToUTF8("OK", fetch + 44, 64);
onsuccess(fetch, 0, value);
};
request.onerror = (error) => {
- HEAPU16[fetch + 40 >> 1] = 4; // Mimic XHR readyState 4 === 'DONE: The operation is complete'
- HEAPU16[fetch + 42 >> 1] = 404; // Mimic XHR HTTP status code 404 "Not Found"
+ HEAP16[(((fetch)+(40))>>1)] = 4 // Mimic XHR readyState 4 === 'DONE: The operation is complete'
+ HEAP16[(((fetch)+(42))>>1)] = 404 // Mimic XHR HTTP status code 404 "Not Found"
stringToUTF8("Not Found", fetch + 44, 64);
onerror(fetch, 0, error);
};
@@ -2147,12 +2044,12 @@ function dbg(text) {
// response.
- var fetch_attr = fetch + 112;
- var onsuccess = HEAPU32[fetch_attr + 36 >> 2];
- var onerror = HEAPU32[fetch_attr + 40 >> 2];
- var onprogress = HEAPU32[fetch_attr + 44 >> 2];
- var onreadystatechange = HEAPU32[fetch_attr + 48 >> 2];
- var fetchAttributes = HEAPU32[fetch_attr + 52 >> 2];
+ var fetch_attr = fetch + 108;
+ var onsuccess = HEAPU32[(((fetch_attr)+(36))>>2)];
+ var onerror = HEAPU32[(((fetch_attr)+(40))>>2)];
+ var onprogress = HEAPU32[(((fetch_attr)+(44))>>2)];
+ var onreadystatechange = HEAPU32[(((fetch_attr)+(48))>>2)];
+ var fetchAttributes = HEAPU32[(((fetch_attr)+(52))>>2)];
var fetchAttrSynchronous = !!(fetchAttributes & 64);
function doCallback(f) {
@@ -2167,14 +2064,14 @@ function dbg(text) {
doCallback(() => {
if (onsuccess) getWasmTableEntry(onsuccess)(fetch);
- else if (successcb) successcb(fetch);
+ else successcb?.(fetch);
});
};
var reportProgress = (fetch, xhr, e) => {
doCallback(() => {
if (onprogress) getWasmTableEntry(onprogress)(fetch);
- else if (progresscb) progresscb(fetch);
+ else progresscb?.(fetch);
});
};
@@ -2182,14 +2079,14 @@ function dbg(text) {
doCallback(() => {
if (onerror) getWasmTableEntry(onerror)(fetch);
- else if (errorcb) errorcb(fetch);
+ else errorcb?.(fetch);
});
};
var reportReadyStateChange = (fetch, xhr, e) => {
doCallback(() => {
if (onreadystatechange) getWasmTableEntry(onreadystatechange)(fetch);
- else if (readystatechangecb) readystatechangecb(fetch);
+ else readystatechangecb?.(fetch);
});
};
@@ -2202,14 +2099,14 @@ function dbg(text) {
doCallback(() => {
if (onsuccess) getWasmTableEntry(onsuccess)(fetch);
- else if (successcb) successcb(fetch);
+ else successcb?.(fetch);
});
};
var storeError = (fetch, xhr, e) => {
doCallback(() => {
if (onsuccess) getWasmTableEntry(onsuccess)(fetch);
- else if (successcb) successcb(fetch);
+ else successcb?.(fetch);
});
};
fetchCacheData(Fetch.dbInstance, fetch, xhr.response, storeSuccess, storeError);
@@ -2240,9 +2137,14 @@ function dbg(text) {
return fetch;
}
+ var GLctx;
+
var webgl_enable_ANGLE_instanced_arrays = (ctx) => {
// Extension available in WebGL 1 from Firefox 26 and Google Chrome 30 onwards. Core feature in WebGL 2.
var ext = ctx.getExtension('ANGLE_instanced_arrays');
+ // Because this extension is a core function in WebGL 2, assign the extension entry points in place of
+ // where the core functions will reside in WebGL 2. This way the calling code can call these without
+ // having to dynamically branch depending if running against WebGL 1 or WebGL 2.
if (ext) {
ctx['vertexAttribDivisor'] = (index, divisor) => ext['vertexAttribDivisorANGLE'](index, divisor);
ctx['drawArraysInstanced'] = (mode, first, count, primcount) => ext['drawArraysInstancedANGLE'](mode, first, count, primcount);
@@ -2281,11 +2183,83 @@ function dbg(text) {
return !!(ctx.mdibvbi = ctx.getExtension('WEBGL_multi_draw_instanced_base_vertex_base_instance'));
};
+ var webgl_enable_EXT_polygon_offset_clamp = (ctx) => {
+ return !!(ctx.extPolygonOffsetClamp = ctx.getExtension('EXT_polygon_offset_clamp'));
+ };
+
+ var webgl_enable_EXT_clip_control = (ctx) => {
+ return !!(ctx.extClipControl = ctx.getExtension('EXT_clip_control'));
+ };
+
+ var webgl_enable_WEBGL_polygon_mode = (ctx) => {
+ return !!(ctx.webglPolygonMode = ctx.getExtension('WEBGL_polygon_mode'));
+ };
+
var webgl_enable_WEBGL_multi_draw = (ctx) => {
// Closure is expected to be allowed to minify the '.multiDrawWebgl' property, so not accessing it quoted.
return !!(ctx.multiDrawWebgl = ctx.getExtension('WEBGL_multi_draw'));
};
+ var getEmscriptenSupportedExtensions = (ctx) => {
+ // Restrict the list of advertised extensions to those that we actually
+ // support.
+ var supportedExtensions = [
+ // WebGL 1 extensions
+ 'ANGLE_instanced_arrays',
+ 'EXT_blend_minmax',
+ 'EXT_disjoint_timer_query',
+ 'EXT_frag_depth',
+ 'EXT_shader_texture_lod',
+ 'EXT_sRGB',
+ 'OES_element_index_uint',
+ 'OES_fbo_render_mipmap',
+ 'OES_standard_derivatives',
+ 'OES_texture_float',
+ 'OES_texture_half_float',
+ 'OES_texture_half_float_linear',
+ 'OES_vertex_array_object',
+ 'WEBGL_color_buffer_float',
+ 'WEBGL_depth_texture',
+ 'WEBGL_draw_buffers',
+ // WebGL 2 extensions
+ 'EXT_color_buffer_float',
+ 'EXT_conservative_depth',
+ 'EXT_disjoint_timer_query_webgl2',
+ 'EXT_texture_norm16',
+ 'NV_shader_noperspective_interpolation',
+ 'WEBGL_clip_cull_distance',
+ // WebGL 1 and WebGL 2 extensions
+ 'EXT_clip_control',
+ 'EXT_color_buffer_half_float',
+ 'EXT_depth_clamp',
+ 'EXT_float_blend',
+ 'EXT_polygon_offset_clamp',
+ 'EXT_texture_compression_bptc',
+ 'EXT_texture_compression_rgtc',
+ 'EXT_texture_filter_anisotropic',
+ 'KHR_parallel_shader_compile',
+ 'OES_texture_float_linear',
+ 'WEBGL_blend_func_extended',
+ 'WEBGL_compressed_texture_astc',
+ 'WEBGL_compressed_texture_etc',
+ 'WEBGL_compressed_texture_etc1',
+ 'WEBGL_compressed_texture_s3tc',
+ 'WEBGL_compressed_texture_s3tc_srgb',
+ 'WEBGL_debug_renderer_info',
+ 'WEBGL_debug_shaders',
+ 'WEBGL_lose_context',
+ 'WEBGL_multi_draw',
+ 'WEBGL_polygon_mode'
+ ];
+ // .getSupportedExtensions() can return null if context is lost, so coerce to empty array.
+ return (ctx.getSupportedExtensions() || []).filter(ext => supportedExtensions.includes(ext));
+ };
+
+ var registerPreMainLoop = (f) => {
+ // Does nothing unless $MainLoop is included/used.
+ typeof MainLoop != 'undefined' && MainLoop.preMainLoop.push(f);
+ };
+
var GL = {
counter:1,
@@ -2312,7 +2286,8 @@ function dbg(text) {
stringiCache:{
},
unpackAlignment:4,
- recordError:function recordError(errorCode) {
+ unpackRowLength:0,
+ recordError:(errorCode) => {
if (!GL.lastError) {
GL.lastError = errorCode;
}
@@ -2324,11 +2299,23 @@ function dbg(text) {
}
return ret;
},
+ genObject:(n, buffers, createFunction, objectTable
+ ) => {
+ for (var i = 0; i < n; i++) {
+ var buffer = GLctx[createFunction]();
+ var id = buffer && GL.getNewId(objectTable);
+ if (buffer) {
+ buffer.name = id;
+ objectTable[id] = buffer;
+ } else {
+ GL.recordError(0x502 /* GL_INVALID_OPERATION */);
+ }
+ HEAP32[(((buffers)+(i*4))>>2)] = id;
+ }
+ },
MAX_TEMP_BUFFER_SIZE:2097152,
numTempVertexBuffersPerSize:64,
- log2ceilLookup:(i) => {
- return 32 - Math.clz32(i === 0 ? 0 : i - 1);
- },
+ log2ceilLookup:(i) => 32 - Math.clz32(i === 0 ? 0 : i - 1),
generateTempBuffers:(quads, context) => {
var largestIndex = GL.log2ceilLookup(GL.MAX_TEMP_BUFFER_SIZE);
context.tempVertexBufferCounters1 = [];
@@ -2379,7 +2366,7 @@ function dbg(text) {
context.GLctx.bindBuffer(0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/, null);
}
},
- getTempVertexBuffer:function getTempVertexBuffer(sizeBytes) {
+ getTempVertexBuffer:(sizeBytes) => {
var idx = GL.log2ceilLookup(sizeBytes);
var ringbuffer = GL.currentContext.tempVertexBuffers1[idx];
var nextFreeBufferIndex = GL.currentContext.tempVertexBufferCounters1[idx];
@@ -2395,7 +2382,7 @@ function dbg(text) {
GLctx.bindBuffer(0x8892 /*GL_ARRAY_BUFFER*/, prevVBO);
return ringbuffer[nextFreeBufferIndex];
},
- getTempIndexBuffer:function getTempIndexBuffer(sizeBytes) {
+ getTempIndexBuffer:(sizeBytes) => {
var idx = GL.log2ceilLookup(sizeBytes);
var ibo = GL.currentContext.tempIndexBuffers[idx];
if (ibo) {
@@ -2408,7 +2395,7 @@ function dbg(text) {
GLctx.bindBuffer(0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/, prevIBO);
return GL.currentContext.tempIndexBuffers[idx];
},
- newRenderingFrameStarted:function newRenderingFrameStarted() {
+ newRenderingFrameStarted:() => {
if (!GL.currentContext) {
return;
}
@@ -2426,12 +2413,12 @@ function dbg(text) {
getSource:(shader, count, string, length) => {
var source = '';
for (var i = 0; i < count; ++i) {
- var len = length ? HEAP32[(((length)+(i*4))>>2)] : -1;
- source += UTF8ToString(HEAP32[(((string)+(i*4))>>2)], len < 0 ? undefined : len);
+ var len = length ? HEAPU32[(((length)+(i*4))>>2)] : undefined;
+ source += UTF8ToString(HEAPU32[(((string)+(i*4))>>2)], len);
}
return source;
},
- calcBufLength:function calcBufLength(size, type, stride, count) {
+ calcBufLength:(size, type, stride, count) => {
if (stride > 0) {
return count * stride; // XXXvlad this is not exactly correct I don't think
}
@@ -2439,10 +2426,11 @@ function dbg(text) {
return size * typeSize * count;
},
usedTempBuffers:[],
- preDrawHandleClientVertexAttribBindings:function preDrawHandleClientVertexAttribBindings(count) {
+ preDrawHandleClientVertexAttribBindings:(count) => {
GL.resetBufferBinding = false;
- // TODO: initial pass to detect ranges we need to upload, might not need an upload per attrib
+ // TODO: initial pass to detect ranges we need to upload, might not need
+ // an upload per attrib
for (var i = 0; i < GL.currentContext.maxVertexAttribs; ++i) {
var cb = GL.currentContext.clientBuffers[i];
if (!cb.clientside || !cb.enabled) continue;
@@ -2458,18 +2446,22 @@ function dbg(text) {
cb.vertexAttribPointerAdaptor.call(GLctx, i, cb.size, cb.type, cb.normalized, cb.stride, 0);
}
},
- postDrawHandleClientVertexAttribBindings:function postDrawHandleClientVertexAttribBindings() {
+ postDrawHandleClientVertexAttribBindings:() => {
if (GL.resetBufferBinding) {
GLctx.bindBuffer(0x8892 /*GL_ARRAY_BUFFER*/, GL.buffers[GLctx.currentArrayBufferBinding]);
}
},
createContext:(/** @type {HTMLCanvasElement} */ canvas, webGLContextAttributes) => {
- // BUG: Workaround Safari WebGL issue: After successfully acquiring WebGL context on a canvas,
- // calling .getContext() will always return that context independent of which 'webgl' or 'webgl2'
- // context version was passed. See https://bugs.webkit.org/show_bug.cgi?id=222758 and
- // https://github.com/emscripten-core/emscripten/issues/13295.
- // TODO: Once the bug is fixed and shipped in Safari, adjust the Safari version field in above check.
+ // BUG: Workaround Safari WebGL issue: After successfully acquiring WebGL
+ // context on a canvas, calling .getContext() will always return that
+ // context independent of which 'webgl' or 'webgl2'
+ // context version was passed. See:
+ // https://bugs.webkit.org/show_bug.cgi?id=222758
+ // and:
+ // https://github.com/emscripten-core/emscripten/issues/13295.
+ // TODO: Once the bug is fixed and shipped in Safari, adjust the Safari
+ // version field in above check.
if (!canvas.getContextSafariWebGL2Fixed) {
canvas.getContextSafariWebGL2Fixed = canvas.getContext;
/** @type {function(this:HTMLCanvasElement, string, (Object|null)=): (Object|null)} */
@@ -2506,7 +2498,8 @@ function dbg(text) {
GLctx: ctx
};
- // Store the created context object so that we can access the context given a canvas without having to pass the parameters again.
+ // Store the created context object so that we can access the context
+ // given a canvas without having to pass the parameters again.
if (ctx.canvas) ctx.canvas.GLctxObject = context;
GL.contexts[handle] = context;
if (typeof webGLContextAttributes.enableExtensionsByDefault == 'undefined' || webGLContextAttributes.enableExtensionsByDefault) {
@@ -2516,7 +2509,16 @@ function dbg(text) {
context.maxVertexAttribs = context.GLctx.getParameter(0x8869 /*GL_MAX_VERTEX_ATTRIBS*/);
context.clientBuffers = [];
for (var i = 0; i < context.maxVertexAttribs; i++) {
- context.clientBuffers[i] = { enabled: false, clientside: false, size: 0, type: 0, normalized: 0, stride: 0, ptr: 0, vertexAttribPointerAdaptor: null };
+ context.clientBuffers[i] = {
+ enabled: false,
+ clientside: false,
+ size: 0,
+ type: 0,
+ normalized: 0,
+ stride: 0,
+ ptr: 0,
+ vertexAttribPointerAdaptor: null,
+ };
}
GL.generateTempBuffers(false, context);
@@ -2525,31 +2527,51 @@ function dbg(text) {
},
makeContextCurrent:(contextHandle) => {
- GL.currentContext = GL.contexts[contextHandle]; // Active Emscripten GL layer context object.
- Module.ctx = GLctx = GL.currentContext && GL.currentContext.GLctx; // Active WebGL context object.
+ // Active Emscripten GL layer context object.
+ GL.currentContext = GL.contexts[contextHandle];
+ // Active WebGL context object.
+ Module.ctx = GLctx = GL.currentContext?.GLctx;
return !(contextHandle && !GLctx);
},
getContext:(contextHandle) => {
return GL.contexts[contextHandle];
},
deleteContext:(contextHandle) => {
- if (GL.currentContext === GL.contexts[contextHandle]) GL.currentContext = null;
- if (typeof JSEvents == 'object') JSEvents.removeAllHandlersOnTarget(GL.contexts[contextHandle].GLctx.canvas); // Release all JS event handlers on the DOM element that the GL context is associated with since the context is now deleted.
- if (GL.contexts[contextHandle] && GL.contexts[contextHandle].GLctx.canvas) GL.contexts[contextHandle].GLctx.canvas.GLctxObject = undefined; // Make sure the canvas object no longer refers to the context object so there are no GC surprises.
+ if (GL.currentContext === GL.contexts[contextHandle]) {
+ GL.currentContext = null;
+ }
+ if (typeof JSEvents == 'object') {
+ // Release all JS event handlers on the DOM element that the GL context is
+ // associated with since the context is now deleted.
+ JSEvents.removeAllHandlersOnTarget(GL.contexts[contextHandle].GLctx.canvas);
+ }
+ // Make sure the canvas object no longer refers to the context object so
+ // there are no GC surprises.
+ if (GL.contexts[contextHandle] && GL.contexts[contextHandle].GLctx.canvas) {
+ GL.contexts[contextHandle].GLctx.canvas.GLctxObject = undefined;
+ }
GL.contexts[contextHandle] = null;
},
initExtensions:(context) => {
- // If this function is called without a specific context object, init the extensions of the currently active context.
- if (!context) context = GL.currentContext;
+ // If this function is called without a specific context object, init the
+ // extensions of the currently active context.
+ context ||= GL.currentContext;
if (context.initExtensionsDone) return;
context.initExtensionsDone = true;
var GLctx = context.GLctx;
- // Detect the presence of a few extensions manually, ction GL interop layer itself will need to know if they exist.
+ // Detect the presence of a few extensions manually, ction GL interop
+ // layer itself will need to know if they exist.
- // Extensions that are only available in WebGL 1 (the calls will be no-ops if called on a WebGL 2 context active)
+ // Extensions that are available in both WebGL 1 and WebGL 2
+ webgl_enable_WEBGL_multi_draw(GLctx);
+ webgl_enable_EXT_polygon_offset_clamp(GLctx);
+ webgl_enable_EXT_clip_control(GLctx);
+ webgl_enable_WEBGL_polygon_mode(GLctx);
+ // Extensions that are only available in WebGL 1 (the calls will be no-ops
+ // if called on a WebGL 2 context active)
webgl_enable_ANGLE_instanced_arrays(GLctx);
webgl_enable_OES_vertex_array_object(GLctx);
webgl_enable_WEBGL_draw_buffers(GLctx);
@@ -2572,12 +2594,9 @@ function dbg(text) {
GLctx.disjointTimerQueryExt = GLctx.getExtension("EXT_disjoint_timer_query");
}
- webgl_enable_WEBGL_multi_draw(GLctx);
-
- // .getSupportedExtensions() can return null if context is lost, so coerce to empty array.
- var exts = GLctx.getSupportedExtensions() || [];
- exts.forEach((ext) => {
- // WEBGL_lose_context, WEBGL_debug_renderer_info and WEBGL_debug_shaders are not enabled by default.
+ getEmscriptenSupportedExtensions(GLctx).forEach((ext) => {
+ // WEBGL_lose_context, WEBGL_debug_renderer_info and WEBGL_debug_shaders
+ // are not enabled by default.
if (!ext.includes('lose_context') && !ext.includes('debug')) {
// Call .getExtension() to enable that extension permanently.
GLctx.getExtension(ext);
@@ -2587,31 +2606,31 @@ function dbg(text) {
};
- var emscripten_webgl_power_preferences = ['default', 'low-power', 'high-performance'];
+ var webglPowerPreferences = ["default","low-power","high-performance"];
/** @suppress {duplicate } */
var _emscripten_webgl_do_create_context = (target, attributes) => {
assert(attributes);
- var a = attributes >> 2;
- var powerPreference = HEAP32[a + (24>>2)];
+ var attr32 = ((attributes)>>2);
+ var powerPreference = HEAP32[attr32 + (8>>2)];
var contextAttributes = {
- 'alpha': !!HEAP32[a + (0>>2)],
- 'depth': !!HEAP32[a + (4>>2)],
- 'stencil': !!HEAP32[a + (8>>2)],
- 'antialias': !!HEAP32[a + (12>>2)],
- 'premultipliedAlpha': !!HEAP32[a + (16>>2)],
- 'preserveDrawingBuffer': !!HEAP32[a + (20>>2)],
- 'powerPreference': emscripten_webgl_power_preferences[powerPreference],
- 'failIfMajorPerformanceCaveat': !!HEAP32[a + (28>>2)],
+ 'alpha': !!HEAP8[attributes + 0],
+ 'depth': !!HEAP8[attributes + 1],
+ 'stencil': !!HEAP8[attributes + 2],
+ 'antialias': !!HEAP8[attributes + 3],
+ 'premultipliedAlpha': !!HEAP8[attributes + 4],
+ 'preserveDrawingBuffer': !!HEAP8[attributes + 5],
+ 'powerPreference': webglPowerPreferences[powerPreference],
+ 'failIfMajorPerformanceCaveat': !!HEAP8[attributes + 12],
// The following are not predefined WebGL context attributes in the WebGL specification, so the property names can be minified by Closure.
- majorVersion: HEAP32[a + (32>>2)],
- minorVersion: HEAP32[a + (36>>2)],
- enableExtensionsByDefault: HEAP32[a + (40>>2)],
- explicitSwapControl: HEAP32[a + (44>>2)],
- proxyContextToMainThread: HEAP32[a + (48>>2)],
- renderViaOffscreenBackBuffer: HEAP32[a + (52>>2)]
+ majorVersion: HEAP32[attr32 + (16>>2)],
+ minorVersion: HEAP32[attr32 + (20>>2)],
+ enableExtensionsByDefault: HEAP8[attributes + 24],
+ explicitSwapControl: HEAP8[attributes + 25],
+ proxyContextToMainThread: HEAP32[attr32 + (28>>2)],
+ renderViaOffscreenBackBuffer: HEAP8[attributes + 32]
};
var canvas = findCanvasEventTarget(target);
@@ -2629,34 +2648,24 @@ function dbg(text) {
};
var _emscripten_webgl_create_context = _emscripten_webgl_do_create_context;
- var _emscripten_webgl_init_context_attributes = (attributes) => {
- assert(attributes);
- var a = attributes >> 2;
- for (var i = 0; i < (56>>2); ++i) {
- HEAP32[a+i] = 0;
- }
-
- HEAP32[a + (0>>2)] =
- HEAP32[a + (4>>2)] =
- HEAP32[a + (12>>2)] =
- HEAP32[a + (16>>2)] =
- HEAP32[a + (32>>2)] =
- HEAP32[a + (40>>2)] = 1;
-
- };
-
var _emscripten_webgl_make_context_current = (contextHandle) => {
var success = GL.makeContextCurrent(contextHandle);
return success ? 0 : -5;
};
+ var SYSCALLS = {
+ varargs:undefined,
+ getStr(ptr) {
+ var ret = UTF8ToString(ptr);
+ return ret;
+ },
+ };
var _fd_close = (fd) => {
abort('fd_close called without SYSCALLS_REQUIRE_FILESYSTEM');
};
-
function _fd_seek(fd,offset_low, offset_high,whence,newOffset) {
- var offset = convertI32PairToI53Checked(offset_low, offset_high);;
+ var offset = convertI32PairToI53Checked(offset_low, offset_high);
return 70;
@@ -2669,7 +2678,7 @@ function dbg(text) {
var buffer = printCharBuffers[stream];
assert(buffer);
if (curr === 0 || curr === 10) {
- (stream === 1 ? out : err)(UTF8ArrayToString(buffer, 0));
+ (stream === 1 ? out : err)(UTF8ArrayToString(buffer));
buffer.length = 0;
} else {
buffer.push(curr);
@@ -2712,9 +2721,11 @@ function dbg(text) {
}
if (target == 0x88EB /*GL_PIXEL_PACK_BUFFER*/) {
- // In WebGL 2 glReadPixels entry point, we need to use a different WebGL 2 API function call when a buffer is bound to
- // GL_PIXEL_PACK_BUFFER_BINDING point, so must keep track whether that binding point is non-null to know what is
- // the proper API function to call.
+ // In WebGL 2 glReadPixels entry point, we need to use a different WebGL 2
+ // API function call when a buffer is bound to
+ // GL_PIXEL_PACK_BUFFER_BINDING point, so must keep track whether that
+ // binding point is non-null to know what is the proper API function to
+ // call.
GLctx.currentPixelPackBufferBinding = buffer;
} else if (target == 0x88EC /*GL_PIXEL_UNPACK_BUFFER*/) {
// In WebGL 2 gl(Compressed)Tex(Sub)Image[23]D entry points, we need to
@@ -2733,39 +2744,42 @@ function dbg(text) {
GLctx.currentElementArrayBufferBinding = ibo ? (ibo.name | 0) : 0;
};
- function _glBlendFunc(x0, x1) { GLctx.blendFunc(x0, x1) }
+ var _glBlendFunc = (x0, x1) => GLctx.blendFunc(x0, x1);
var _glBufferData = (target, size, data, usage) => {
- if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible.
- // If size is zero, WebGL would interpret uploading the whole input arraybuffer (starting from given offset), which would
- // not make sense in WebAssembly, so avoid uploading if size is zero. However we must still call bufferData to establish a
- // backing storage of zero bytes.
+ if (GL.currentContext.version >= 2) {
+ // If size is zero, WebGL would interpret uploading the whole input
+ // arraybuffer (starting from given offset), which would not make sense in
+ // WebAssembly, so avoid uploading if size is zero. However we must still
+ // call bufferData to establish a backing storage of zero bytes.
if (data && size) {
GLctx.bufferData(target, HEAPU8, usage, data, size);
} else {
GLctx.bufferData(target, size, usage);
}
- } else {
- // N.b. here first form specifies a heap subarray, second form an integer size, so the ?: code here is polymorphic. It is advised to avoid
- // randomly mixing both uses in calling code, to avoid any potential JS engine JIT issues.
- GLctx.bufferData(target, data ? HEAPU8.subarray(data, data+size) : size, usage);
+ return;
}
+ // N.b. here first form specifies a heap subarray, second form an integer
+ // size, so the ?: code here is polymorphic. It is advised to avoid
+ // randomly mixing both uses in calling code, to avoid any potential JS
+ // engine JIT issues.
+ GLctx.bufferData(target, data ? HEAPU8.subarray(data, data+size) : size, usage);
};
var _glBufferSubData = (target, offset, size, data) => {
- if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible.
+ if (GL.currentContext.version >= 2) {
size && GLctx.bufferSubData(target, offset, HEAPU8, data, size);
return;
}
GLctx.bufferSubData(target, offset, HEAPU8.subarray(data, data+size));
};
- function _glClear(x0) { GLctx.clear(x0) }
+ var _glClear = (x0) => GLctx.clear(x0);
- function _glClearColor(x0, x1, x2, x3) { GLctx.clearColor(x0, x1, x2, x3) }
+ var _glClearColor = (x0, x1, x2, x3) => GLctx.clearColor(x0, x1, x2, x3);
- function _glClearDepth(x0) { GLctx.clearDepth(x0) }
+ var _glClearDepth = (x0) => GLctx.clearDepth(x0);
var _glCompileShader = (shader) => {
GLctx.compileShader(GL.shaders[shader]);
@@ -2776,7 +2790,8 @@ function dbg(text) {
var program = GLctx.createProgram();
// Store additional information needed for each shader program:
program.name = id;
- // Lazy cache results of glGetProgramiv(GL_ACTIVE_UNIFORM_MAX_LENGTH/GL_ACTIVE_ATTRIBUTE_MAX_LENGTH/GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH)
+ // Lazy cache results of
+ // glGetProgramiv(GL_ACTIVE_UNIFORM_MAX_LENGTH/GL_ACTIVE_ATTRIBUTE_MAX_LENGTH/GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH)
program.maxUniformLength = program.maxAttributeLength = program.maxUniformBlockNameLength = 0;
program.uniformIdCounter = 1;
GL.programs[id] = program;
@@ -2813,7 +2828,9 @@ function dbg(text) {
var _glDeleteProgram = (id) => {
if (!id) return;
var program = GL.programs[id];
- if (!program) { // glDeleteProgram actually signals an error when deleting a nonexisting object, unlike some other GL delete functions.
+ if (!program) {
+ // glDeleteProgram actually signals an error when deleting a nonexisting
+ // object, unlike some other GL delete functions.
GL.recordError(0x501 /* GL_INVALID_VALUE */);
return;
}
@@ -2825,7 +2842,9 @@ function dbg(text) {
var _glDeleteShader = (id) => {
if (!id) return;
var shader = GL.shaders[id];
- if (!shader) { // glDeleteShader actually signals an error when deleting a nonexisting object, unlike some other GL delete functions.
+ if (!shader) {
+ // glDeleteShader actually signals an error when deleting a nonexisting
+ // object, unlike some other GL delete functions.
GL.recordError(0x501 /* GL_INVALID_VALUE */);
return;
}
@@ -2841,7 +2860,7 @@ function dbg(text) {
}
};
- function _glDepthFunc(x0) { GLctx.depthFunc(x0) }
+ var _glDepthFunc = (x0) => GLctx.depthFunc(x0);
var _glDepthMask = (flag) => {
GLctx.depthMask(!!flag);
@@ -2858,19 +2877,42 @@ function dbg(text) {
var _glDrawElements = (mode, count, type, indices) => {
var buf;
+ var vertexes = 0;
if (!GLctx.currentElementArrayBufferBinding) {
var size = GL.calcBufLength(1, type, 0, count);
buf = GL.getTempIndexBuffer(size);
GLctx.bindBuffer(0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/, buf);
GLctx.bufferSubData(0x8893 /*GL_ELEMENT_ARRAY_BUFFER*/,
- 0,
- HEAPU8.subarray(indices, indices + size));
+ 0,
+ HEAPU8.subarray(indices, indices + size));
+
+ // Calculating vertex count if shader's attribute data is on client side
+ if (count > 0) {
+ for (var i = 0; i < GL.currentContext.maxVertexAttribs; ++i) {
+ var cb = GL.currentContext.clientBuffers[i];
+ if (cb.clientside && cb.enabled) {
+ let arrayClass;
+ switch(type) {
+ case 0x1401 /* GL_UNSIGNED_BYTE */: arrayClass = Uint8Array; break;
+ case 0x1403 /* GL_UNSIGNED_SHORT */: arrayClass = Uint16Array; break;
+ case 0x1405 /* GL_UNSIGNED_INT */: arrayClass = Uint32Array; break;
+ default:
+ GL.recordError(0x502 /* GL_INVALID_OPERATION */);
+ return;
+ }
+
+ vertexes = new arrayClass(HEAPU8.buffer, indices, count).reduce((max, current) => Math.max(max, current)) + 1;
+ break;
+ }
+ }
+ }
+
// the index is now 0
indices = 0;
}
// bind any client-side buffers
- GL.preDrawHandleClientVertexAttribBindings(count);
+ GL.preDrawHandleClientVertexAttribBindings(vertexes);
GLctx.drawElements(mode, count, type, indices);
@@ -2881,7 +2923,7 @@ function dbg(text) {
}
};
- function _glEnable(x0) { GLctx.enable(x0) }
+ var _glEnable = (x0) => GLctx.enable(x0);
var _glEnableVertexAttribArray = (index) => {
var cb = GL.currentContext.clientBuffers[index];
@@ -2889,31 +2931,15 @@ function dbg(text) {
GLctx.enableVertexAttribArray(index);
};
- var __glGenObject = (n, buffers, createFunction, objectTable
- ) => {
- for (var i = 0; i < n; i++) {
- var buffer = GLctx[createFunction]();
- var id = buffer && GL.getNewId(objectTable);
- if (buffer) {
- buffer.name = id;
- objectTable[id] = buffer;
- } else {
- GL.recordError(0x502 /* GL_INVALID_OPERATION */);
- }
- HEAP32[(((buffers)+(i*4))>>2)] = id;
- }
- };
-
var _glGenBuffers = (n, buffers) => {
- __glGenObject(n, buffers, 'createBuffer', GL.buffers
+ GL.genObject(n, buffers, 'createBuffer', GL.buffers
);
};
-
- function _glGenVertexArrays(n, arrays) {
- __glGenObject(n, arrays, 'createVertexArray', GL.vaos
+ var _glGenVertexArrays = (n, arrays) => {
+ GL.genObject(n, arrays, 'createVertexArray', GL.vaos
);
- }
+ };
var _glGetAttribLocation = (program, name) => {
@@ -2929,8 +2955,9 @@ function dbg(text) {
var _glGetProgramiv = (program, pname, p) => {
if (!p) {
- // GLES2 specification does not specify how to behave if p is a null pointer. Since calling this function does not make sense
- // if p == null, issue a GL error to notify user about it.
+ // GLES2 specification does not specify how to behave if p is a null
+ // pointer. Since calling this function does not make sense if p == null,
+ // issue a GL error to notify user about it.
GL.recordError(0x501 /* GL_INVALID_VALUE */);
return;
}
@@ -2948,21 +2975,24 @@ function dbg(text) {
HEAP32[((p)>>2)] = log.length + 1;
} else if (pname == 0x8B87 /* GL_ACTIVE_UNIFORM_MAX_LENGTH */) {
if (!program.maxUniformLength) {
- for (var i = 0; i < GLctx.getProgramParameter(program, 0x8B86/*GL_ACTIVE_UNIFORMS*/); ++i) {
+ var numActiveUniforms = GLctx.getProgramParameter(program, 0x8B86/*GL_ACTIVE_UNIFORMS*/);
+ for (var i = 0; i < numActiveUniforms; ++i) {
program.maxUniformLength = Math.max(program.maxUniformLength, GLctx.getActiveUniform(program, i).name.length+1);
}
}
HEAP32[((p)>>2)] = program.maxUniformLength;
} else if (pname == 0x8B8A /* GL_ACTIVE_ATTRIBUTE_MAX_LENGTH */) {
if (!program.maxAttributeLength) {
- for (var i = 0; i < GLctx.getProgramParameter(program, 0x8B89/*GL_ACTIVE_ATTRIBUTES*/); ++i) {
+ var numActiveAttributes = GLctx.getProgramParameter(program, 0x8B89/*GL_ACTIVE_ATTRIBUTES*/);
+ for (var i = 0; i < numActiveAttributes; ++i) {
program.maxAttributeLength = Math.max(program.maxAttributeLength, GLctx.getActiveAttrib(program, i).name.length+1);
}
}
HEAP32[((p)>>2)] = program.maxAttributeLength;
} else if (pname == 0x8A35 /* GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH */) {
if (!program.maxUniformBlockNameLength) {
- for (var i = 0; i < GLctx.getProgramParameter(program, 0x8A36/*GL_ACTIVE_UNIFORM_BLOCKS*/); ++i) {
+ var numActiveUniformBlocks = GLctx.getProgramParameter(program, 0x8A36/*GL_ACTIVE_UNIFORM_BLOCKS*/);
+ for (var i = 0; i < numActiveUniformBlocks; ++i) {
program.maxUniformBlockNameLength = Math.max(program.maxUniformBlockNameLength, GLctx.getActiveUniformBlockName(program, i).length+1);
}
}
@@ -2982,8 +3012,9 @@ function dbg(text) {
var _glGetShaderiv = (shader, pname, p) => {
if (!p) {
- // GLES2 specification does not specify how to behave if p is a null pointer. Since calling this function does not make sense
- // if p == null, issue a GL error to notify user about it.
+ // GLES2 specification does not specify how to behave if p is a null
+ // pointer. Since calling this function does not make sense if p == null,
+ // issue a GL error to notify user about it.
GL.recordError(0x501 /* GL_INVALID_VALUE */);
return;
}
@@ -3011,9 +3042,7 @@ function dbg(text) {
var jstoi_q = (str) => parseInt(str);
/** @noinline */
- var webglGetLeftBracePos = (name) => {
- return name.slice(-1) == ']' && name.lastIndexOf('[');
- };
+ var webglGetLeftBracePos = (name) => name.slice(-1) == ']' && name.lastIndexOf('[');
var webglPrepareUniformLocationsBeforeFirstUse = (program) => {
var uniformLocsById = program.uniformLocsById, // Maps GLuint -> WebGLUniformLocation
@@ -3028,7 +3057,8 @@ function dbg(text) {
// maps integer locations back to uniform name strings, so that we can lazily fetch uniform array locations
program.uniformArrayNamesById = {};
- for (i = 0; i < GLctx.getProgramParameter(program, 0x8B86/*GL_ACTIVE_UNIFORMS*/); ++i) {
+ var numActiveUniforms = GLctx.getProgramParameter(program, 0x8B86/*GL_ACTIVE_UNIFORMS*/);
+ for (i = 0; i < numActiveUniforms; ++i) {
var u = GLctx.getActiveUniform(program, i);
var nm = u.name;
var sz = u.size;
@@ -3067,11 +3097,14 @@ function dbg(text) {
var arrayIndex = 0;
var uniformBaseName = name;
- // Invariant: when populating integer IDs for uniform locations, we must maintain the precondition that
- // arrays reside in contiguous addresses, i.e. for a 'vec4 colors[10];', colors[4] must be at location colors[0]+4.
- // However, user might call glGetUniformLocation(program, "colors") for an array, so we cannot discover based on the user
- // input arguments whether the uniform we are dealing with is an array. The only way to discover which uniforms are arrays
- // is to enumerate over all the active uniforms in the program.
+ // Invariant: when populating integer IDs for uniform locations, we must
+ // maintain the precondition that arrays reside in contiguous addresses,
+ // i.e. for a 'vec4 colors[10];', colors[4] must be at location
+ // colors[0]+4. However, user might call glGetUniformLocation(program,
+ // "colors") for an array, so we cannot discover based on the user input
+ // arguments whether the uniform we are dealing with is an array. The only
+ // way to discover which uniforms are arrays is to enumerate over all the
+ // active uniforms in the program.
var leftBrace = webglGetLeftBracePos(name);
// If user passed an array accessor "[index]", parse the array index off the accessor.
@@ -3081,10 +3114,12 @@ function dbg(text) {
}
// Have we cached the location of this uniform before?
- var sizeAndId = program.uniformSizeAndIdsByName[uniformBaseName]; // A pair [array length, GLint of the uniform location]
+ // A pair [array length, GLint of the uniform location]
+ var sizeAndId = program.uniformSizeAndIdsByName[uniformBaseName];
- // If an uniform with this name exists, and if its index is within the array limits (if it's even an array),
- // query the WebGLlocation, or return an existing cached location.
+ // If an uniform with this name exists, and if its index is within the
+ // array limits (if it's even an array), query the WebGLlocation, or
+ // return an existing cached location.
if (sizeAndId && arrayIndex < sizeAndId[0]) {
arrayIndex += sizeAndId[1]; // Add the base location of the uniform to the array index offset.
if ((uniformLocsById[arrayIndex] = uniformLocsById[arrayIndex] || GLctx.getUniformLocation(program, name))) {
@@ -3093,8 +3128,9 @@ function dbg(text) {
}
}
else {
- // N.b. we are currently unable to distinguish between GL program IDs that never existed vs GL program IDs that have been deleted,
- // so report GL_INVALID_VALUE in both cases.
+ // N.b. we are currently unable to distinguish between GL program IDs that
+ // never existed vs GL program IDs that have been deleted, so report
+ // GL_INVALID_VALUE in both cases.
GL.recordError(0x501 /* GL_INVALID_VALUE */);
}
return -1;
@@ -3120,12 +3156,12 @@ function dbg(text) {
if (p) {
var webglLoc = p.uniformLocsById[location];
- // p.uniformLocsById[location] stores either an integer, or a WebGLUniformLocation.
-
- // If an integer, we have not yet bound the location, so do it now. The integer value specifies the array index
- // we should bind to.
+ // p.uniformLocsById[location] stores either an integer, or a
+ // WebGLUniformLocation.
+ // If an integer, we have not yet bound the location, so do it now. The
+ // integer value specifies the array index we should bind to.
if (typeof webglLoc == 'number') {
- p.uniformLocsById[location] = webglLoc = GLctx.getUniformLocation(p, p.uniformArrayNamesById[location] + (webglLoc > 0 ? '[' + webglLoc + ']' : ''));
+ p.uniformLocsById[location] = webglLoc = GLctx.getUniformLocation(p, p.uniformArrayNamesById[location] + (webglLoc > 0 ? `[${webglLoc}]` : ''));
}
// Else an already cached WebGLUniformLocation, return it.
return webglLoc;
@@ -3134,22 +3170,28 @@ function dbg(text) {
}
};
+ var _glUniform1f = (location, v0) => {
+ GLctx.uniform1f(webglGetUniformLocation(location), v0);
+ };
+
+
var miniTempWebGLFloatBuffers = [];
var _glUniformMatrix4fv = (location, count, transpose, value) => {
- if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible.
- count && GLctx.uniformMatrix4fv(webglGetUniformLocation(location), !!transpose, HEAPF32, value>>2, count*16);
+ if (GL.currentContext.version >= 2) {
+ count && GLctx.uniformMatrix4fv(webglGetUniformLocation(location), !!transpose, HEAPF32, ((value)>>2), count*16);
return;
}
if (count <= 18) {
// avoid allocation when uploading few enough uniforms
- var view = miniTempWebGLFloatBuffers[16*count-1];
+ var view = miniTempWebGLFloatBuffers[16*count];
// hoist the heap out of the loop for size and for pthreads+growth.
var heap = HEAPF32;
- value >>= 2;
- for (var i = 0; i < 16 * count; i += 16) {
+ value = ((value)>>2);
+ count *= 16;
+ for (var i = 0; i < count; i += 16) {
var dst = value + i;
view[i] = heap[dst];
view[i + 1] = heap[dst + 1];
@@ -3170,7 +3212,7 @@ function dbg(text) {
}
} else
{
- var view = HEAPF32.subarray((value)>>2, (value+count*64)>>2);
+ var view = HEAPF32.subarray((((value)>>2)), ((value+count*64)>>2));
}
GLctx.uniformMatrix4fv(webglGetUniformLocation(location), !!transpose, view);
};
@@ -3207,34 +3249,39 @@ function dbg(text) {
Fetch.init();;
-handleAllocatorInit();
-var GLctx;;
+
+ // Signal GL rendering layer that processing of a new frame is about to
+ // start. This helps it optimize VBO double-buffering and reduce GPU stalls.
+ registerPreMainLoop(() => GL.newRenderingFrameStarted());
+ ;
var miniTempWebGLFloatBuffersStorage = new Float32Array(288);
- for (/**@suppress{duplicate}*/var i = 0; i < 288; ++i) {
- miniTempWebGLFloatBuffers[i] = miniTempWebGLFloatBuffersStorage.subarray(0, i+1);
- }
- ;
+ // Create GL_POOL_TEMP_BUFFERS_SIZE+1 temporary buffers, for uploads of size 0 through GL_POOL_TEMP_BUFFERS_SIZE inclusive
+ for (/**@suppress{duplicate}*/var i = 0; i <= 288; ++i) {
+ miniTempWebGLFloatBuffers[i] = miniTempWebGLFloatBuffersStorage.subarray(0, i);
+ };
function checkIncomingModuleAPI() {
ignoredModuleProp('fetchSettings');
}
var wasmImports = {
/** @export */
+ __assert_fail: ___assert_fail,
+ /** @export */
+ _abort_js: __abort_js,
+ /** @export */
_emscripten_fetch_free: __emscripten_fetch_free,
/** @export */
+ _emscripten_memcpy_js: __emscripten_memcpy_js,
+ /** @export */
_localtime_js: __localtime_js,
/** @export */
_tzset_js: __tzset_js,
/** @export */
- abort: _abort,
- /** @export */
emscripten_date_now: _emscripten_date_now,
/** @export */
emscripten_get_element_css_size: _emscripten_get_element_css_size,
/** @export */
emscripten_is_main_browser_thread: _emscripten_is_main_browser_thread,
/** @export */
- emscripten_memcpy_js: _emscripten_memcpy_js,
- /** @export */
emscripten_request_animation_frame_loop: _emscripten_request_animation_frame_loop,
/** @export */
emscripten_resize_heap: _emscripten_resize_heap,
@@ -3249,8 +3296,6 @@ var wasmImports = {
/** @export */
emscripten_webgl_create_context: _emscripten_webgl_create_context,
/** @export */
- emscripten_webgl_init_context_attributes: _emscripten_webgl_init_context_attributes,
- /** @export */
emscripten_webgl_make_context_current: _emscripten_webgl_make_context_current,
/** @export */
fd_close: _fd_close,
@@ -3323,6 +3368,8 @@ var wasmImports = {
/** @export */
glShaderSource: _glShaderSource,
/** @export */
+ glUniform1f: _glUniform1f,
+ /** @export */
glUniformMatrix4fv: _glUniformMatrix4fv,
/** @export */
glUseProgram: _glUseProgram,
@@ -3332,22 +3379,22 @@ var wasmImports = {
glVertexAttribPointer: _glVertexAttribPointer
};
var wasmExports = createWasm();
-var ___wasm_call_ctors = createExportWrapper('__wasm_call_ctors');
-var _malloc = createExportWrapper('malloc');
-var _free = createExportWrapper('free');
-var _main = Module['_main'] = createExportWrapper('main');
-var ___errno_location = createExportWrapper('__errno_location');
-var _fflush = Module['_fflush'] = createExportWrapper('fflush');
-var setTempRet0 = createExportWrapper('setTempRet0');
+var ___wasm_call_ctors = createExportWrapper('__wasm_call_ctors', 0);
+var _main = Module['_main'] = createExportWrapper('main', 2);
+var _free = createExportWrapper('free', 1);
+var _malloc = createExportWrapper('malloc', 1);
+var _fflush = createExportWrapper('fflush', 1);
+var _strerror = createExportWrapper('strerror', 1);
+var __emscripten_tempret_set = createExportWrapper('_emscripten_tempret_set', 1);
+var __emscripten_tempret_get = createExportWrapper('_emscripten_tempret_get', 0);
var _emscripten_stack_init = () => (_emscripten_stack_init = wasmExports['emscripten_stack_init'])();
var _emscripten_stack_get_free = () => (_emscripten_stack_get_free = wasmExports['emscripten_stack_get_free'])();
var _emscripten_stack_get_base = () => (_emscripten_stack_get_base = wasmExports['emscripten_stack_get_base'])();
var _emscripten_stack_get_end = () => (_emscripten_stack_get_end = wasmExports['emscripten_stack_get_end'])();
-var stackSave = createExportWrapper('stackSave');
-var stackRestore = createExportWrapper('stackRestore');
-var stackAlloc = createExportWrapper('stackAlloc');
+var __emscripten_stack_restore = (a0) => (__emscripten_stack_restore = wasmExports['_emscripten_stack_restore'])(a0);
+var __emscripten_stack_alloc = (a0) => (__emscripten_stack_alloc = wasmExports['_emscripten_stack_alloc'])(a0);
var _emscripten_stack_get_current = () => (_emscripten_stack_get_current = wasmExports['emscripten_stack_get_current'])();
-var dynCall_jiji = Module['dynCall_jiji'] = createExportWrapper('dynCall_jiji');
+var dynCall_jiji = Module['dynCall_jiji'] = createExportWrapper('dynCall_jiji', 5);
// include: postamble.js
@@ -3360,24 +3407,21 @@ var missingLibrarySymbols = [
'writeI53ToU64Signaling',
'convertI32PairToI53',
'convertU32PairToI53',
+ 'stackAlloc',
+ 'getTempRet0',
+ 'setTempRet0',
'zeroMemory',
- 'arraySum',
- 'addDays',
- 'setErrNo',
+ 'strError',
'inetPton4',
'inetNtop4',
'inetPton6',
'inetNtop6',
'readSockaddr',
'writeSockaddr',
- 'getHostByName',
'initRandomFill',
'randomFill',
- 'getCallstack',
'emscriptenLog',
- 'convertPCtoSourceLocation',
'readEmAsmArgs',
- 'jstoi_s',
'getExecutableName',
'listenOnce',
'autoResumeAudioContext',
@@ -3386,10 +3430,8 @@ var missingLibrarySymbols = [
'dynCall',
'runtimeKeepalivePush',
'runtimeKeepalivePop',
- 'safeSetTimeout',
'asmjsMangle',
'asyncLoad',
- 'alignMemory',
'mmapAlloc',
'getNativeTypeSize',
'STACK_SIZE',
@@ -3423,6 +3465,7 @@ var missingLibrarySymbols = [
'UTF32ToString',
'stringToUTF32',
'lengthBytesUTF32',
+ 'stringToNewUTF8',
'stringToUTF8OnStack',
'writeArrayToMemory',
'registerKeyEventCallback',
@@ -3460,37 +3503,41 @@ var missingLibrarySymbols = [
'registerBatteryEventCallback',
'setCanvasElementSize',
'getCanvasElementSize',
- 'demangle',
- 'demangleAll',
'jsStackTrace',
- 'stackTrace',
+ 'getCallstack',
+ 'convertPCtoSourceLocation',
'getEnvStrings',
'checkWasiClock',
'wasiRightsToMuslOFlags',
'wasiOFlagsToMuslOFlags',
'createDyncallWrapper',
+ 'safeSetTimeout',
'setImmediateWrapped',
'clearImmediateWrapped',
'polyfillSetImmediate',
+ 'registerPostMainLoop',
'getPromise',
'makePromise',
'idsToPromises',
'makePromiseCallback',
'ExceptionInfo',
'findMatchingCatch',
- 'setMainLoop',
+ 'Browser_asyncPrepareDataCounter',
+ 'safeRequestAnimationFrame',
+ 'arraySum',
+ 'addDays',
'getSocketFromFD',
'getSocketAddress',
'FS_createPreloadedFile',
'FS_modeStringToFlags',
'FS_getMode',
'FS_stdin_getChar',
- 'FS_createDataFile',
'FS_unlink',
+ 'FS_createDataFile',
'FS_mkdirTree',
'_setNetworkCallback',
'heapObjectForWebGLType',
- 'heapAccessShiftForWebGLHeap',
+ 'toTypedArrayIndex',
'emscriptenWebGLGet',
'computeUnpackAlignedImageSize',
'colorChannelsInGlTextureFormat',
@@ -3503,15 +3550,15 @@ var missingLibrarySymbols = [
'writeGLArray',
'registerWebGlEventCallback',
'runAndAbortIfError',
- 'SDL_unicode',
- 'SDL_ttfContext',
- 'SDL_audio',
'emscriptenWebGLGetIndexed',
'ALLOC_NORMAL',
'ALLOC_STACK',
'allocate',
'writeStringToMemory',
'writeAsciiToMemory',
+ 'setErrNo',
+ 'demangle',
+ 'stackTrace',
];
missingLibrarySymbols.forEach(missingLibrarySymbol)
@@ -3524,57 +3571,42 @@ var unexportedSymbols = [
'addOnPostRun',
'addRunDependency',
'removeRunDependency',
- 'FS_createFolder',
- 'FS_createPath',
- 'FS_createLazyFile',
- 'FS_createLink',
- 'FS_createDevice',
- 'FS_readFile',
'out',
'err',
'callMain',
'abort',
- 'keepRuntimeAlive',
'wasmMemory',
'wasmExports',
- 'stackAlloc',
- 'stackSave',
- 'stackRestore',
- 'getTempRet0',
- 'setTempRet0',
'writeStackCookie',
'checkStackCookie',
'writeI53ToI64',
'readI53FromI64',
'readI53FromU64',
'convertI32PairToI53Checked',
+ 'stackSave',
+ 'stackRestore',
'ptrToString',
'exitJS',
'getHeapMax',
'growMemory',
'ENV',
- 'MONTH_DAYS_REGULAR',
- 'MONTH_DAYS_LEAP',
- 'MONTH_DAYS_REGULAR_CUMULATIVE',
- 'MONTH_DAYS_LEAP_CUMULATIVE',
- 'isLeapYear',
- 'ydayFromDate',
'ERRNO_CODES',
- 'ERRNO_MESSAGES',
'DNS',
'Protocols',
'Sockets',
'timers',
'warnOnce',
- 'UNWIND_CACHE',
'readEmAsmArgsArray',
'jstoi_q',
+ 'jstoi_s',
'handleException',
+ 'keepRuntimeAlive',
'callUserCallback',
'maybeExit',
- 'handleAllocatorInit',
+ 'alignMemory',
'HandleAllocator',
'wasmTable',
+ 'noExitRuntime',
'freeTableIndexes',
'functionsInTableMap',
'setValue',
@@ -3588,7 +3620,6 @@ var unexportedSymbols = [
'stringToUTF8',
'lengthBytesUTF8',
'UTF16Decoder',
- 'stringToNewUTF8',
'JSEvents',
'specialHTMLTargets',
'maybeCStringToJsString',
@@ -3600,18 +3631,31 @@ var unexportedSymbols = [
'registerUiEventCallback',
'currentFullscreenStrategy',
'restoreOldWindowedStyle',
+ 'UNWIND_CACHE',
'ExitStatus',
'flush_NO_FILESYSTEM',
+ 'registerPreMainLoop',
'promiseMap',
'uncaughtExceptionCount',
'exceptionLast',
'exceptionCaught',
'Browser',
+ 'getPreloadedImageData__data',
'wget',
+ 'MONTH_DAYS_REGULAR',
+ 'MONTH_DAYS_LEAP',
+ 'MONTH_DAYS_REGULAR_CUMULATIVE',
+ 'MONTH_DAYS_LEAP_CUMULATIVE',
+ 'isLeapYear',
+ 'ydayFromDate',
'SYSCALLS',
'preloadPlugins',
'FS_stdin_getChar_buffer',
+ 'FS_createPath',
+ 'FS_createDevice',
+ 'FS_readFile',
'FS',
+ 'FS_createLazyFile',
'MEMFS',
'TTY',
'PIPEFS',
@@ -3623,12 +3667,13 @@ var unexportedSymbols = [
'webgl_enable_OES_vertex_array_object',
'webgl_enable_WEBGL_draw_buffers',
'webgl_enable_WEBGL_multi_draw',
+ 'webgl_enable_EXT_polygon_offset_clamp',
+ 'webgl_enable_EXT_clip_control',
+ 'webgl_enable_WEBGL_polygon_mode',
'GL',
- '__glGenObject',
'webglGetUniformLocation',
'webglPrepareUniformLocationsBeforeFirstUse',
'webglGetLeftBracePos',
- 'emscripten_webgl_power_preferences',
'AL',
'GLUT',
'EGL',
@@ -3640,6 +3685,8 @@ var unexportedSymbols = [
'webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance',
'allocateUTF8',
'allocateUTF8OnStack',
+ 'print',
+ 'printErr',
'Fetch',
'fetchDeleteCachedData',
'fetchLoadCachedData',
@@ -3651,6 +3698,7 @@ unexportedSymbols.forEach(unexportedRuntimeSymbol);
var calledRun;
+var calledPrerun;
dependenciesFulfilled = function runCaller() {
// If run has never been called, and we should call run (INVOKE_RUN is true, and Module.noInitialRun is not false)
@@ -3660,7 +3708,7 @@ dependenciesFulfilled = function runCaller() {
function callMain() {
assert(runDependencies == 0, 'cannot call main when async dependencies remain! (listen on Module["onRuntimeInitialized"])');
- assert(__ATPRERUN__.length == 0, 'cannot call main when preRun functions remain to be called');
+ assert(calledPrerun, 'cannot call main without calling preRun first');
var entryFunction = _main;
@@ -3697,19 +3745,22 @@ function run() {
stackCheckInit();
- preRun();
+ if (!calledPrerun) {
+ calledPrerun = 1;
+ preRun();
- // a preRun added a dependency, run will be called later
- if (runDependencies > 0) {
- return;
+ // a preRun added a dependency, run will be called later
+ if (runDependencies > 0) {
+ return;
+ }
}
function doRun() {
// run may have just been called through dependencies being fulfilled just in this very frame,
// or while the async setStatus time below was happening
if (calledRun) return;
- calledRun = true;
- Module['calledRun'] = true;
+ calledRun = 1;
+ Module['calledRun'] = 1;
if (ABORT) return;
@@ -3717,7 +3768,7 @@ function run() {
preMain();
- if (Module['onRuntimeInitialized']) Module['onRuntimeInitialized']();
+ Module['onRuntimeInitialized']?.();
if (shouldRunNow) callMain();
@@ -3726,10 +3777,8 @@ function run() {
if (Module['setStatus']) {
Module['setStatus']('Running...');
- setTimeout(function() {
- setTimeout(function() {
- Module['setStatus']('');
- }, 1);
+ setTimeout(() => {
+ setTimeout(() => Module['setStatus'](''), 1);
doRun();
}, 1);
} else
@@ -3782,5 +3831,5 @@ if (Module['noInitialRun']) shouldRunNow = false;
run();
-
// end include: postamble.js
+
diff --git a/themes/dist/output.wasm b/themes/dist/output.wasm
index 925d783..4c88053 100755
--- a/themes/dist/output.wasm
+++ b/themes/dist/output.wasm
Binary files differ
diff --git a/themes/emscripten.ini b/themes/emscripten.ini
new file mode 100644
index 0000000..5824ce6
--- /dev/null
+++ b/themes/emscripten.ini
@@ -0,0 +1,14 @@
+[binaries]
+c = 'emcc'
+cpp = 'em++'
+ar = 'emar'
+ranlib = 'emranlib'
+
+[host_machine]
+system = 'emscripten'
+cpu_family = 'wasm32'
+cpu = 'wasm32'
+endian = 'little'
+
+[properties]
+needs_exe_wrapper = true
diff --git a/themes/meson.build b/themes/meson.build
new file mode 100644
index 0000000..1e4c9f1
--- /dev/null
+++ b/themes/meson.build
@@ -0,0 +1,122 @@
+project('themes', 'cpp',
+ version: '1.0.0',
+ default_options: [
+ 'cpp_std=c++17',
+ 'buildtype=release',
+ 'warning_level=2'
+ ]
+)
+
+# Emscripten link flags
+emscripten_link_args = [
+ '-s', 'ALLOW_MEMORY_GROWTH=1',
+ '-s', 'USE_WEBGL2=1',
+ '-s', 'FULL_ES3=1',
+ '-s', 'WASM=1',
+ '-s', 'NO_EXIT_RUNTIME=1',
+ '-s', 'FETCH'
+]
+
+# Source files
+sources = files(
+ # Main
+ 'src/main.cpp',
+ 'src/main_loop.cpp',
+ 'src/mathlib.cpp',
+ 'src/logger.cpp',
+ 'src/shader.cpp',
+ 'src/shader_fetcher.cpp',
+ 'src/shapes_2d.cpp',
+ 'src/webgl_context.cpp',
+ 'src/renderer_2d.cpp',
+ 'src/renderer_3d.cpp',
+
+ # Shaders
+ 'src/shaders/renderer2d_frag.cpp',
+ 'src/shaders/renderer2d_vert.cpp',
+ 'src/shaders/renderer3d_frag.cpp',
+ 'src/shaders/renderer3d_vert.cpp',
+ 'src/shaders/sun_frag.cpp',
+ 'src/shaders/sun_vert.cpp',
+
+ # Autumn theme
+ 'src/autumn/autumn_theme.cpp',
+ 'src/autumn/leaf_particle_render.cpp',
+ 'src/autumn/tree_shape.cpp',
+
+ # Winter theme
+ 'src/winter/winter_theme.cpp',
+ 'src/winter/snowflake.cpp',
+ 'src/winter/windfield.cpp',
+
+ # Spring theme
+ 'src/spring/spring_theme.cpp',
+ 'src/spring/grass_renderer.cpp',
+
+ # Summer theme
+ 'src/summer/summer_theme.cpp'
+)
+
+# Include directories
+inc = include_directories(
+ 'src',
+ 'src/autumn',
+ 'src/winter',
+ 'src/spring',
+ 'src/summer',
+ 'src/shaders',
+ 'src/_shaders',
+ 'src/tools'
+)
+
+# Custom target to build shaders from GLSL files
+node = find_program('node')
+shader_script = meson.project_source_root() + '/src/tools/shader.js'
+
+# List all GLSL shader files as inputs
+shader_inputs = files(
+ 'src/_shaders/renderer2d.frag',
+ 'src/_shaders/renderer2d.vert',
+ 'src/_shaders/renderer3d.frag',
+ 'src/_shaders/renderer3d.vert',
+ 'src/_shaders/sun.frag',
+ 'src/_shaders/sun.vert'
+)
+
+# Custom target that runs whenever shader files change
+sh = find_program('sh')
+build_shaders = custom_target('build_shaders',
+ input: shader_inputs,
+ output: 'shaders.stamp',
+ command: [sh, '-c', node.full_path() + ' ' + shader_script + ' && touch @OUTPUT@'],
+ build_always_stale: true,
+ build_by_default: true
+)
+
+# Build the executable
+output_exe = executable('output',
+ sources,
+ include_directories: inc,
+ link_args: emscripten_link_args,
+ name_suffix: 'js'
+)
+
+# Custom target to copy output files to dist directory
+copy_script = find_program('sh')
+copy_to_dist = custom_target('copy_to_dist',
+ output: ['copy_to_dist.stamp'],
+ input: output_exe,
+ command: [
+ copy_script, '-c',
+ 'mkdir -p ' + meson.project_source_root() + '/dist && ' +
+ 'cp $1 ' + meson.project_source_root() + '/dist/output.js && ' +
+ 'cp $2/output.wasm ' + meson.project_source_root() + '/dist/output.wasm && ' +
+ 'touch $3',
+ '--', '@INPUT@', meson.current_build_dir(), '@OUTPUT@'
+ ],
+ build_by_default: true,
+ install: false
+)
+
+# Add an alias target for convenience
+alias_target('copy-dist', copy_to_dist)
diff --git a/themes/src/_shaders/sun.frag b/themes/src/_shaders/sun.frag
index 8463e06..2170b39 100644
--- a/themes/src/_shaders/sun.frag
+++ b/themes/src/_shaders/sun.frag
@@ -1,5 +1,43 @@
varying lowp vec4 VertexColor;
+varying lowp vec2 TexCoord;
+uniform lowp float time;
+
+// Simple noise function for edge distortion
+lowp float noise(lowp vec2 p) {
+ return sin(p.x * 10.0 + time) * cos(p.y * 10.0 + time * 0.5) * 0.5 + 0.5;
+}
void main() {
- gl_FragColor = VertexColor;
+ // TexCoord is now normalized: center is (0,0), edges are at distance ~1
+ lowp float dist = length(TexCoord);
+
+ // Add animated noise to the edge
+ lowp float angle = atan(TexCoord.y, TexCoord.x);
+ lowp float wave = sin(angle * 8.0 + time * 2.0) * 0.05 + sin(angle * 4.0 - time * 1.5) * 0.03;
+ lowp float noiseValue = noise(TexCoord + time * 0.1) * 0.02;
+
+ // Create soft edge using smoothstep - ensure fade reaches zero at the actual edge
+ lowp float innerEdge = 0.8;
+ lowp float outerEdge = 1.0;
+ lowp float alpha = 1.0 - smoothstep(innerEdge, outerEdge, dist);
+
+ // Apply wave distortion to the edge
+ alpha *= 1.0 - smoothstep(0.85 + wave + noiseValue * 2.0, 1.0, dist);
+
+ // Make edges more transparent but not too much
+ alpha = alpha * alpha;
+
+ // Add slight glow effect at the edge
+ lowp float glow = smoothstep(0.5, 0.8, dist) * (1.0 - smoothstep(0.8, 1.0, dist));
+
+ // Create orange gradient from center
+ lowp vec3 orangeColor = vec3(1.0, 0.5, 0.1);
+ lowp float centerGradient = smoothstep(0.6, 0.0, dist);
+ lowp vec3 baseColor = mix(VertexColor.rgb, orangeColor, centerGradient * 0.6);
+
+ // Mix in the glow with a brighter color
+ lowp vec3 glowColor = baseColor * 1.3;
+ lowp vec3 finalColor = mix(baseColor, glowColor, glow * 0.5);
+
+ gl_FragColor = vec4(finalColor, VertexColor.a * alpha);
}
diff --git a/themes/src/_shaders/sun.vert b/themes/src/_shaders/sun.vert
index 76150f0..5ed77d7 100644
--- a/themes/src/_shaders/sun.vert
+++ b/themes/src/_shaders/sun.vert
@@ -1,13 +1,22 @@
-attribute vec2 position;
-attribute vec4 color;
-attribute mat4 vMatrix;
-uniform mat4 projection;
-uniform mat4 model;
+attribute vec2 position;
+attribute vec4 color;
+attribute mat4 vMatrix;
+uniform mat4 projection;
+uniform mat4 model;
varying lowp vec4 VertexColor;
+varying lowp vec2 TexCoord;
-void main() {
- vec4 fragmentPosition = projection * model * vMatrix * vec4(position.x, position.y, 0, 1);
- gl_Position = fragmentPosition;
- VertexColor = color;
+void main() {
+ vec4 fragmentPosition = projection * model * vMatrix * vec4(position.x, position.y, 0, 1);
+ gl_Position = fragmentPosition;
+ VertexColor = color;
+ // Normalize the position - the center is at (0,0) and edge vertices are at distance 'radius'
+ // We want TexCoord to be in the range roughly [-1, 1] at the edges
+ lowp float maxDist = length(position);
+ if (maxDist > 0.1) {
+ TexCoord = position / maxDist;
+ } else {
+ TexCoord = vec2(0.0, 0.0);
+ }
}
diff --git a/themes/src/autumn/AutumnTheme.cpp b/themes/src/autumn/autumn_theme.cpp
index 4b7a2e2..d88b265 100644
--- a/themes/src/autumn/AutumnTheme.cpp
+++ b/themes/src/autumn/autumn_theme.cpp
@@ -1,4 +1,4 @@
-#include "AutumnTheme.hpp"
+#include "autumn_theme.hpp"
#include "../shapes_2d.h"
#include <memory>
diff --git a/themes/src/autumn/AutumnTheme.hpp b/themes/src/autumn/autumn_theme.hpp
index e3f5748..b61c0f3 100644
--- a/themes/src/autumn/AutumnTheme.hpp
+++ b/themes/src/autumn/autumn_theme.hpp
@@ -1,11 +1,11 @@
#ifndef AUTUMN_THEME_HPP
#define AUTUMN_THEME_HPP
-#include "TreeShape.h"
-#include "LeafParticleRender.h"
+#include "tree_shape.h"
+#include "leaf_particle_render.h"
#include "../types.h"
#include "../theme.h"
-#include "../Renderer2d.h"
+#include "../renderer_2d.h"
#include <memory>
#include <vector>
diff --git a/themes/src/autumn/LeafParticleRender.cpp b/themes/src/autumn/leaf_particle_render.cpp
index fee3df2..569bb2d 100644
--- a/themes/src/autumn/LeafParticleRender.cpp
+++ b/themes/src/autumn/leaf_particle_render.cpp
@@ -1,7 +1,7 @@
-#include "LeafParticleRender.h"
-#include "../Renderer2d.h"
+#include "leaf_particle_render.h"
+#include "../renderer_2d.h"
#include "../mathlib.h"
-#include "TreeShape.h"
+#include "tree_shape.h"
#include "../types.h"
#include <math.h>
diff --git a/themes/src/autumn/LeafParticleRender.h b/themes/src/autumn/leaf_particle_render.h
index f6efe1f..1209e1b 100644
--- a/themes/src/autumn/LeafParticleRender.h
+++ b/themes/src/autumn/leaf_particle_render.h
@@ -1,4 +1,4 @@
-#include "../Renderer2d.h"
+#include "../renderer_2d.h"
#include "../mathlib.h"
#include "../types.h"
diff --git a/themes/src/autumn/TreeShape.cpp b/themes/src/autumn/tree_shape.cpp
index 7c80929..622751b 100644
--- a/themes/src/autumn/TreeShape.cpp
+++ b/themes/src/autumn/tree_shape.cpp
@@ -1,4 +1,4 @@
-#include "TreeShape.h"
+#include "tree_shape.h"
#include "../mathlib.h"
#include <cstdio>
#include <cstdlib>
diff --git a/themes/src/autumn/TreeShape.h b/themes/src/autumn/tree_shape.h
index fc0d11e..0d18415 100644
--- a/themes/src/autumn/TreeShape.h
+++ b/themes/src/autumn/tree_shape.h
@@ -1,4 +1,4 @@
-#include "../Renderer2d.h"
+#include "../renderer_2d.h"
#include "../types.h"
#include "../mathlib.h"
diff --git a/themes/src/list.h b/themes/src/list.h
index 25b236a..9b6a719 100644
--- a/themes/src/list.h
+++ b/themes/src/list.h
@@ -1,7 +1,7 @@
#pragma once
#include <cstdlib>
#include <cstring>
-#include "Logger.h"
+#include "logger.h"
#define FOREACH(list) \
for (i32 idx = 0; idx < list.numElements; idx++) \
diff --git a/themes/src/Logger.cpp b/themes/src/logger.cpp
index 1068d88..bead282 100644
--- a/themes/src/Logger.cpp
+++ b/themes/src/logger.cpp
@@ -1,4 +1,4 @@
-#include "Logger.h"
+#include "logger.h"
#include <chrono>
#include <cstdarg>
#include <cstdio>
diff --git a/themes/src/Logger.h b/themes/src/logger.h
index 7596b6f..7596b6f 100644
--- a/themes/src/Logger.h
+++ b/themes/src/logger.h
diff --git a/themes/src/main.cpp b/themes/src/main.cpp
index 14227c9..60e6aed 100644
--- a/themes/src/main.cpp
+++ b/themes/src/main.cpp
@@ -1,13 +1,13 @@
-#include "WebglContext.h"
-#include "MainLoop.h"
-#include "Renderer2d.h"
+#include "webgl_context.h"
+#include "main_loop.h"
+#include "renderer_2d.h"
#include "mathlib.h"
#include "theme.h"
#include "types.h"
-#include "summer/SummerTheme.h"
-#include "autumn/AutumnTheme.hpp"
-#include "spring/SpringTheme.hpp"
-#include "winter/WinterTheme.hpp"
+#include "summer/summer_theme.h"
+#include "autumn/autumn_theme.hpp"
+#include "spring/spring_theme.hpp"
+#include "winter/winter_theme.hpp"
#include <cstdio>
#include <emscripten/fetch.h>
@@ -67,14 +67,19 @@ void load(ThemeType theme) {
}
void update(f32 dtSeconds, void* userData) {
+ if (!active_theme)
+ return;
active_theme->update(dtSeconds);
active_theme->render();
}
void unload() {
delete active_theme;
+ active_theme = nullptr;
type = ThemeType::Default;
+ glClearColor(0, 0, 0, 0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
if (mainLoop.isRunning) {
mainLoop.stop();
}
@@ -84,7 +89,7 @@ void unload() {
EM_BOOL selectNone(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) {
printf("Default theme selected\n");
unload();
- return true;
+ return true;
}
EM_BOOL selectAutumn(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) {
diff --git a/themes/src/MainLoop.cpp b/themes/src/main_loop.cpp
index 09aa643..e5397ca 100644
--- a/themes/src/MainLoop.cpp
+++ b/themes/src/main_loop.cpp
@@ -1,4 +1,4 @@
-#include "MainLoop.h"
+#include "main_loop.h"
#include <cstdio>
#include <cstdlib>
diff --git a/themes/src/MainLoop.h b/themes/src/main_loop.h
index 2573bb8..07520a2 100644
--- a/themes/src/MainLoop.h
+++ b/themes/src/main_loop.h
@@ -26,4 +26,4 @@ struct MainLoop {
void stop() {
isRunning = false;
}
-}; \ No newline at end of file
+};
diff --git a/themes/src/Renderer2d.cpp b/themes/src/renderer_2d.cpp
index f1d78e3..7200669 100644
--- a/themes/src/Renderer2d.cpp
+++ b/themes/src/renderer_2d.cpp
@@ -1,6 +1,6 @@
-#include "Renderer2d.h"
-#include "Shader.h"
-#include "WebglContext.h"
+#include "renderer_2d.h"
+#include "shader.h"
+#include "webgl_context.h"
#include "mathlib.h"
#include <cstdio>
#include "shaders/renderer2d_vert.h"
diff --git a/themes/src/Renderer2d.h b/themes/src/renderer_2d.h
index 7432894..d572533 100644
--- a/themes/src/Renderer2d.h
+++ b/themes/src/renderer_2d.h
@@ -1,8 +1,8 @@
#pragma once
-#include "WebglContext.h"
+#include "webgl_context.h"
#include "types.h"
-#include "Shader.h"
+#include "shader.h"
#include "mathlib.h"
struct WebglContext;
diff --git a/themes/src/Renderer3d.cpp b/themes/src/renderer_3d.cpp
index 00315de..cc79940 100644
--- a/themes/src/Renderer3d.cpp
+++ b/themes/src/renderer_3d.cpp
@@ -1,9 +1,9 @@
-#include "Renderer3d.h"
-#include "Shader.h"
+#include "renderer_3d.h"
+#include "shader.h"
#include "list.h"
#include "mathlib.h"
-#include "WebglContext.h"
-#include "Logger.h"
+#include "webgl_context.h"
+#include "logger.h"
#include <cstdio>
// Note: In the 'transform' attribute, the transform.x is the scale,
diff --git a/themes/src/Renderer3d.h b/themes/src/renderer_3d.h
index 5b2c8c8..5b2c8c8 100644
--- a/themes/src/Renderer3d.h
+++ b/themes/src/renderer_3d.h
diff --git a/themes/src/Shader.cpp b/themes/src/shader.cpp
index 5f2b00e..ed2cab5 100644
--- a/themes/src/Shader.cpp
+++ b/themes/src/shader.cpp
@@ -1,4 +1,4 @@
-#include "Shader.h"
+#include "shader.h"
#include <string>
GLuint loadIndividualShader(GLenum shaderType, const GLchar* cCode) {
diff --git a/themes/src/Shader.h b/themes/src/shader.h
index bc81764..bc81764 100644
--- a/themes/src/Shader.h
+++ b/themes/src/shader.h
diff --git a/themes/src/shaders/sun_frag.cpp b/themes/src/shaders/sun_frag.cpp
index d1ea160..696b3b9 100644
--- a/themes/src/shaders/sun_frag.cpp
+++ b/themes/src/shaders/sun_frag.cpp
@@ -1,8 +1,46 @@
#include "sun_frag.h"
const char* shader_sun_frag = "varying lowp vec4 VertexColor; \n"
+"varying lowp vec2 TexCoord; \n"
+"uniform lowp float time; \n"
+" \n"
+"// Simple noise function for edge distortion \n"
+"lowp float noise(lowp vec2 p) { \n"
+" return sin(p.x * 10.0 + time) * cos(p.y * 10.0 + time * 0.5) * 0.5 + 0.5; \n"
+"} \n"
" \n"
"void main() { \n"
-" gl_FragColor = VertexColor; \n"
+" // TexCoord is now normalized: center is (0,0), edges are at distance ~1 \n"
+" lowp float dist = length(TexCoord); \n"
+" \n"
+" // Add animated noise to the edge \n"
+" lowp float angle = atan(TexCoord.y, TexCoord.x); \n"
+" lowp float wave = sin(angle * 8.0 + time * 2.0) * 0.05 + sin(angle * 4.0 - time * 1.5) * 0.03; \n"
+" lowp float noiseValue = noise(TexCoord + time * 0.1) * 0.02; \n"
+" \n"
+" // Create soft edge using smoothstep - ensure fade reaches zero at the actual edge \n"
+" lowp float innerEdge = 0.8; \n"
+" lowp float outerEdge = 1.0; \n"
+" lowp float alpha = 1.0 - smoothstep(innerEdge, outerEdge, dist); \n"
+" \n"
+" // Apply wave distortion to the edge \n"
+" alpha *= 1.0 - smoothstep(0.85 + wave + noiseValue * 2.0, 1.0, dist); \n"
+" \n"
+" // Make edges more transparent but not too much \n"
+" alpha = alpha * alpha; \n"
+" \n"
+" // Add slight glow effect at the edge \n"
+" lowp float glow = smoothstep(0.5, 0.8, dist) * (1.0 - smoothstep(0.8, 1.0, dist)); \n"
+" \n"
+" // Create orange gradient from center \n"
+" lowp vec3 orangeColor = vec3(1.0, 0.5, 0.1); \n"
+" lowp float centerGradient = smoothstep(0.6, 0.0, dist); \n"
+" lowp vec3 baseColor = mix(VertexColor.rgb, orangeColor, centerGradient * 0.6); \n"
+" \n"
+" // Mix in the glow with a brighter color \n"
+" lowp vec3 glowColor = baseColor * 1.3; \n"
+" lowp vec3 finalColor = mix(baseColor, glowColor, glow * 0.5); \n"
+" \n"
+" gl_FragColor = vec4(finalColor, VertexColor.a * alpha); \n"
"} \n"
" \n";
diff --git a/themes/src/shaders/sun_vert.cpp b/themes/src/shaders/sun_vert.cpp
index ca617c0..bacf3a6 100644
--- a/themes/src/shaders/sun_vert.cpp
+++ b/themes/src/shaders/sun_vert.cpp
@@ -1,16 +1,25 @@
#include "sun_vert.h"
const char* shader_sun_vert = " \n"
-"attribute vec2 position; \n"
-"attribute vec4 color; \n"
-"attribute mat4 vMatrix; \n"
-"uniform mat4 projection; \n"
-"uniform mat4 model; \n"
+"attribute vec2 position; \n"
+"attribute vec4 color; \n"
+"attribute mat4 vMatrix; \n"
+"uniform mat4 projection; \n"
+"uniform mat4 model; \n"
"varying lowp vec4 VertexColor; \n"
+"varying lowp vec2 TexCoord; \n"
" \n"
-"void main() { \n"
-" vec4 fragmentPosition = projection * model * vMatrix * vec4(position.x, position.y, 0, 1); \n"
-" gl_Position = fragmentPosition; \n"
-" VertexColor = color; \n"
+"void main() { \n"
+" vec4 fragmentPosition = projection * model * vMatrix * vec4(position.x, position.y, 0, 1); \n"
+" gl_Position = fragmentPosition; \n"
+" VertexColor = color; \n"
+" // Normalize the position - the center is at (0,0) and edge vertices are at distance 'radius' \n"
+" // We want TexCoord to be in the range roughly [-1, 1] at the edges \n"
+" lowp float maxDist = length(position); \n"
+" if (maxDist > 0.1) { \n"
+" TexCoord = position / maxDist; \n"
+" } else { \n"
+" TexCoord = vec2(0.0, 0.0); \n"
+" } \n"
"} \n"
" \n";
diff --git a/themes/src/shapes_2d.cpp b/themes/src/shapes_2d.cpp
index d5a29ed..e00c521 100644
--- a/themes/src/shapes_2d.cpp
+++ b/themes/src/shapes_2d.cpp
@@ -1,5 +1,5 @@
#include "shapes_2d.h"
-#include "Renderer2d.h"
+#include "renderer_2d.h"
#include "mathlib.h"
#include "list.h"
diff --git a/themes/src/shapes_2d.h b/themes/src/shapes_2d.h
index 8e08504..325d525 100644
--- a/themes/src/shapes_2d.h
+++ b/themes/src/shapes_2d.h
@@ -2,7 +2,7 @@
#define SHAPES_2D
#include "mathlib.h"
-#include "Renderer2d.h"
+#include "renderer_2d.h"
#include "types.h"
class RectangularGradient
diff --git a/themes/src/spring/GrassRenderer.cpp b/themes/src/spring/grass_renderer.cpp
index b69d111..685f733 100644
--- a/themes/src/spring/GrassRenderer.cpp
+++ b/themes/src/spring/grass_renderer.cpp
@@ -1,5 +1,5 @@
-#include "GrassRenderer.hpp"
-#include "Renderer3d.h"
+#include "grass_renderer.hpp"
+#include "../renderer_3d.h"
void GrassRenderer::load(GrassRendererLoadData params, Renderer3d* renderer) {
const f32 COLUMN_INCREMENT = GRASS_BLADES_PER_COL / params.area.x;
diff --git a/themes/src/spring/GrassRenderer.hpp b/themes/src/spring/grass_renderer.hpp
index 8c96724..88879f3 100644
--- a/themes/src/spring/GrassRenderer.hpp
+++ b/themes/src/spring/grass_renderer.hpp
@@ -1,7 +1,7 @@
#ifndef GRASS_RENDERER_HPP
#define GRASS_RENDERER_HPP
-#include "Renderer3d.h"
+#include "../renderer_3d.h"
#include "mathlib.h"
#include "types.h"
diff --git a/themes/src/spring/SpringTheme.cpp b/themes/src/spring/spring_theme.cpp
index e39c138..8507194 100644
--- a/themes/src/spring/SpringTheme.cpp
+++ b/themes/src/spring/spring_theme.cpp
@@ -1,5 +1,5 @@
-#include "SpringTheme.hpp"
-#include "../Renderer3d.h"
+#include "spring_theme.hpp"
+#include "../renderer_3d.h"
#include "../shader_fetcher.hpp"
#include <cstdio>
#include <emscripten/fetch.h>
diff --git a/themes/src/spring/SpringTheme.hpp b/themes/src/spring/spring_theme.hpp
index 64f9cb5..6079958 100644
--- a/themes/src/spring/SpringTheme.hpp
+++ b/themes/src/spring/spring_theme.hpp
@@ -3,7 +3,7 @@
#include "../mathlib.h"
#include "../types.h"
-#include "../Renderer3d.h"
+#include "../renderer_3d.h"
#include "../theme.h"
diff --git a/themes/src/summer/SummerTheme.cpp b/themes/src/summer/summer_theme.cpp
index 1f76b56..6d2cfec 100644
--- a/themes/src/summer/SummerTheme.cpp
+++ b/themes/src/summer/summer_theme.cpp
@@ -1,5 +1,5 @@
-#include "SummerTheme.h"
-#include "../Renderer2d.h"
+#include "summer_theme.h"
+#include "../renderer_2d.h"
#include "../list.h"
#include "../mathlib.h"
#include "../shaders/sun_frag.h"
@@ -59,19 +59,23 @@ void Sun::load(Renderer2d* renderer) {
indices.add(second);
indices.add(third);
}
-
+
mesh.load(&vertices.data[0], vertices.numElements, &indices.data[0], indices.numElements, renderer);
mesh.model = Mat4x4().translateByVec2(Vector2(renderer->context->width / 2.f, renderer->context->height / 2.f));
+
+ timeUniform = getShaderUniform(renderer->shader, "time");
+
vertices.deallocate();
indices.deallocate();
}
void Sun::update(f32 dtSeconds) {
-
+ elapsedTime += dtSeconds;
}
void Sun::render(Renderer2d* renderer) {
setShaderMat4(renderer->uniforms.model, mesh.model);
+ glUniform1f(timeUniform, elapsedTime);
glBindVertexArray(mesh.vao);
glDrawElements(GL_TRIANGLES, mesh.numIndices, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
diff --git a/themes/src/summer/SummerTheme.h b/themes/src/summer/summer_theme.h
index 2ce6b7f..eb404fd 100644
--- a/themes/src/summer/SummerTheme.h
+++ b/themes/src/summer/summer_theme.h
@@ -1,6 +1,6 @@
#pragma once
#include "../types.h"
-#include "../Renderer2d.h"
+#include "../renderer_2d.h"
#include "../theme.h"
#include <vector>
@@ -8,6 +8,8 @@ struct Sun {
f32 radius = 20.f;
i32 sectors = 180;
Mesh2D mesh;
+ f32 elapsedTime = 0.f;
+ i32 timeUniform = -1;
void load(Renderer2d* renderer);
void update(f32 dtSeconds);
diff --git a/themes/src/WebglContext.cpp b/themes/src/webgl_context.cpp
index df49c2d..71b983e 100644
--- a/themes/src/WebglContext.cpp
+++ b/themes/src/webgl_context.cpp
@@ -1,4 +1,4 @@
-#include "WebglContext.h"
+#include "webgl_context.h"
#include <cstdio>
diff --git a/themes/src/WebglContext.h b/themes/src/webgl_context.h
index 1956092..1956092 100644
--- a/themes/src/WebglContext.h
+++ b/themes/src/webgl_context.h
diff --git a/themes/src/winter/Snowflake.cpp b/themes/src/winter/snowflake.cpp
index 57f1a8f..4ce8f3a 100644
--- a/themes/src/winter/Snowflake.cpp
+++ b/themes/src/winter/snowflake.cpp
@@ -1,5 +1,5 @@
-#include "Snowflake.h"
-#include "../Renderer2d.h"
+#include "snowflake.h"
+#include "../renderer_2d.h"
#include "../mathlib.h"
#include "../list.h"
#include <cstdio>
diff --git a/themes/src/winter/Snowflake.h b/themes/src/winter/snowflake.h
index ad027f6..11a1438 100644
--- a/themes/src/winter/Snowflake.h
+++ b/themes/src/winter/snowflake.h
@@ -4,7 +4,7 @@
#include "../types.h"
#include "../mathlib.h"
#include "../list.h"
-#include "Windfield.hpp"
+#include "windfield.hpp"
struct Renderer2d;
struct Vertex2D;
diff --git a/themes/src/winter/Windfield.cpp b/themes/src/winter/windfield.cpp
index 88fb74b..f6c3be3 100644
--- a/themes/src/winter/Windfield.cpp
+++ b/themes/src/winter/windfield.cpp
@@ -1,4 +1,4 @@
-#include "Windfield.hpp"
+#include "windfield.hpp"
template <i32 Width, i32 Height, i32 CellDimension>
diff --git a/themes/src/winter/Windfield.hpp b/themes/src/winter/windfield.hpp
index 5bf0c38..5bf0c38 100644
--- a/themes/src/winter/Windfield.hpp
+++ b/themes/src/winter/windfield.hpp
diff --git a/themes/src/winter/WinterTheme.cpp b/themes/src/winter/winter_theme.cpp
index 052670e..a628f18 100644
--- a/themes/src/winter/WinterTheme.cpp
+++ b/themes/src/winter/winter_theme.cpp
@@ -1,5 +1,5 @@
-#include "WinterTheme.hpp"
-#include "../Renderer2d.h"
+#include "winter_theme.hpp"
+#include "../renderer_2d.h"
WinterTheme::WinterTheme(WebglContext* context)
{
diff --git a/themes/src/winter/WinterTheme.hpp b/themes/src/winter/winter_theme.hpp
index 5ba6d94..d1c3e05 100644
--- a/themes/src/winter/WinterTheme.hpp
+++ b/themes/src/winter/winter_theme.hpp
@@ -1,10 +1,10 @@
#ifndef WINTER_THEME_HPP
#define WINTER_THEME_HPP
-#include "Snowflake.h"
+#include "snowflake.h"
#include "../types.h"
#include "../theme.h"
-#include "../Renderer2d.h"
+#include "../renderer_2d.h"
struct WebglContext;