Introduction

The application is the part of our system that handles our application logic. This includes the windowing system; requesting a window, watching for window events (key presses, resizing etc.). We didn’t deal with this in our brief WebGL Overview, but we deal with it now as a bridge between the previous part on the JS Event Loop, and the canvas element that we did see before in the overview. For our application logic, we use winit which is a good standard library to work with. So, let’s get to it.

How to start

The first thing we need to do is create our basic webpage, that will hold our canvas for rendering. This is similar to WebGL, and we can proceed in a similar way. The big change will be how we initialize the canvas, which will be to call the Rust WASM from our Javascript. So, let’s first create a very basic webpage:

You can see we have the canvas element, but also our script right at the end after the DOM has been parsed. So, what does our main.js contain ? It contains code to initialise the WASM code. Let’s say that our wasm code compiles to wasm.js, we initialise it using the following:

Well, what does wasm.js contain, I hear you ask ? The init function initialises the WASM virtual machine, and then calls a specially WASM Rust function that is marked with an attribute macro:

This function is called by the JS event loop, through init; recall, that the script is evaluated, the init function is placed on the call stack, and then executed. We must therefore ensure that it is async, as if the function is blocking, then the JS Event Loop will block, freezing the browser (and that would be bad). Now we need to talk about what goes inside this function, and this is where we need to start using the winit library.

ApplicationHandler

The main backbone of winit is the ApplicationHandler. It is the handler that manages the interactions with the state of the application. For now, we will leave the state blank, which we will call Application:

We then implement the ApplicationHandler for the Application, by implementing three main methods:

  • The resumed method is the main method that is used for initialisation of the application either initially when the application first starts, or after resuming.
  • The user_event method contains code to deal with a custom event that we define in the enum AppEvent.
  • The window_event method contains code to deal with window events as described by winit, such as Resizing, Reddrawing, MouseInput etc.

We will fill in these methods as we go along, for now we must know that they are available and create the relevant barebones structure.

Initialising the Application

Now we can piece it together our application and its initialisation inside our WASM start function. The first thing we want to do is having some sort of logging that reports back to the browser; otherwise our lives our going to be an absolute nightmare. In JS, we typically use console.log, and there is a replicating library in Rust:

This ensures that any call to log::info will pipe the error to the Developer console in our browser. Excellent. The next step to create our Application; but recall that the JS Event Loop controls everything in the browser running tasks and microtasks. In the browser, therefore, our application does not own the loop, but is itself created and owned by the Event Loop! In fact the winit EventLoop, while in the browser, is essentially a shim around the JS Event Loop. This is a subtle twist to those who are our native aficionados and used to traditional GUIs, where the application typically owns the event loop. We therefore create the Event Loop before the application:

This creates our EventLoop object with user events defined in our AppEvent enum, which we will return to later. We then create an Application object:

And run it with our EventLoop object:

And that is essentially it! The beginnings of our application, we are now ready to move on to initialising the Context and the graphics subsystem.