C code for WebAssembly

Before we get to the C code we'll use for our WebAssembly module, let's try an experiment. Open the CLI in the /chapter-05-create-load-module folder, and try running this command:

emcc with-glue.c -Os -s WASM=1 -s USE_SDL=2 -s SIDE_MODULE=1 -s BINARYEN_ASYNC_COMPILATION=0 -o try-with-glue.wasm

You should see a try-with-glue.wasm file appear in VS Code's file explorer panel after the compilation is complete. Right-click on the file and select Show WebAssembly. The beginning of the corresponding Wat representation should resemble the following code:

(module
(type $t0 (func (param i32)))
(type $t1 (func (param i32 i32 i32 i32 i32) (result i32)))
(type $t2 (func (param i32) (result i32)))
(type $t3 (func))
(type $t4 (func (param i32 i32) (result i32)))
(type $t5 (func (param i32 i32 i32 i32)))
(type $t6 (func (result i32)))
(type $t7 (func (result f64)))
(import "env" "memory" (memory $env.memory 256))
(import "env" "table" (table $env.table 4 anyfunc))
(import "env" "memoryBase" (global $env.memoryBase i32))
(import "env" "tableBase" (global $env.tableBase i32))
(import "env" "abort" (func $env.abort (type $t0)))
(import "env" "_SDL_CreateWindowAndRenderer" (func $env._SDL_CreateWindowAndRenderer (type $t1)))
(import "env" "_SDL_DestroyRenderer" (func $env._SDL_DestroyRenderer (type $t0)))
(import "env" "_SDL_DestroyWindow" (func $env._SDL_DestroyWindow (type $t0)))
(import "env" "_SDL_Init" (func $env._SDL_Init (type $t2)))
(import "env" "_SDL_Quit" (func $env._SDL_Quit (type $t3)))
(import "env" "_SDL_RenderClear" (func $env._SDL_RenderClear (type $t2)))
(import "env" "_SDL_RenderFillRect" (func $env._SDL_RenderFillRect (type $t4)))
(import "env" "_SDL_RenderPresent" (func $env._SDL_RenderPresent (type $t0)))
(import "env" "_SDL_SetRenderDrawColor" (func $env._SDL_SetRenderDrawColor (type $t1)))
(import "env" "_emscripten_set_main_loop_arg" (func $env._emscripten_set_main_loop_arg (type $t5)))
...

If you wanted to load this in a browser and execute it, you'd have to pass in an importObj object to WebAssembly's instantiate() or compile() function with an env object containing each of those import "env" functions. Emscripten handles all of this for us behind the scenes with the glue code, which makes it an incredibly valuable tool. However, we can replace the SDL2 functionality by using the DOM while still tracking the rectangle's location in C.

We will write the C code differently to ensure we only have to pass a few functions into the importObj.env object to execute the code. Create a file named without-glue.c in the /chapter-05-create-load-module folder and populate it with the following contents:

/*
* This file interacts with the canvas through imported functions.
* It moves a blue rectangle diagonally across the canvas
* (mimics the SDL example).
*/
#include <stdbool.h>

#define BOUNDS 255
#define RECT_SIDE 50
#define BOUNCE_POINT (BOUNDS - RECT_SIDE)

// These functions are passed in through the importObj.env object
// and update the rectangle on the <canvas>:
extern int jsClearRect();
extern int jsFillRect(int x, int y, int width, int height);

bool isRunning = true;

typedef struct Rect {
int x;
int y;
char direction;
} Rect;

struct Rect rect;

/*
* Updates the rectangle location by 1px in the x and y in a
* direction based on its current position.
*/
void updateRectLocation() {
// Since we want the rectangle to "bump" into the edge of the
// canvas, we need to determine when the right edge of the
// rectangle encounters the bounds of the canvas, which is why
// we're using the canvas width - rectangle width:
if (rect.x == BOUNCE_POINT) rect.direction = 'L';

// As soon as the rectangle "bumps" into the left side of the
// canvas, it should change direction again.
if (rect.x == 0) rect.direction = 'R';

// If the direction has changed based on the x and y
// coordinates, ensure the x and y points update
// accordingly:
int incrementer = 1;
if (rect.direction == 'L') incrementer = -1;
rect.x = rect.x + incrementer;
rect.y = rect.y + incrementer;
}

/*
* Clear the existing rectangle element from the canvas and draw a
* new one in the updated location.
*/
void moveRect() {
jsClearRect();
updateRectLocation();
jsFillRect(rect.x, rect.y, RECT_SIDE, RECT_SIDE);
}

bool getIsRunning() {
return isRunning;
}

void setIsRunning(bool newIsRunning) {
isRunning = newIsRunning;
}

void init() {
rect.x = 0;
rect.y = 0;
rect.direction = 'R';
setIsRunning(true);
}

We will call the functions from the C code to determine the x and y coordinates. The setIsRunning() function can be used to pause the rectangle's movement. Now that our C code is ready, let's compile it. In the VS Code terminal, cd into the /chapter-05-create-load-module folder, and run the following command:

emcc without-glue.c -Os -s WASM=1 -s SIDE_MODULE=1 -s BINARYEN_ASYNC_COMPILATION=0 -o without-glue.wasm

Once the compilation is complete, you can right-click on the resultant without-glue.wasm file and select Show WebAssembly to see the Wat representation. You should see the following at the top of the file for the import "env" items:

(module
(type $t0 (func (param i32)))
(type $t1 (func (result i32)))
(type $t2 (func (param i32 i32 i32 i32) (result i32)))
(type $t3 (func))
(type $t4 (func (result f64)))
(import "env" "memory" (memory $env.memory 256))
(import "env" "table" (table $env.table 8 anyfunc))
(import "env" "memoryBase" (global $env.memoryBase i32))
(import "env" "tableBase" (global $env.tableBase i32))
(import "env" "abort" (func $env.abort (type $t0)))
(import "env" "_jsClearRect" (func $env._jsClearRect (type $t1)))
(import "env" "_jsFillRect" (func $env._jsFillRect (type $t2)))
...

We need to pass in the _jsClearRect and _jsFillRect functions within the importObj object. We'll cover how to do that in the section on the HTML file with JavaScript interaction code.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset