-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 05f16dd
Showing
6 changed files
with
335 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
name: Build and Release WebAssembly | ||
|
||
on: | ||
push: | ||
tags: | ||
- 'v*' # Trigger on version tags | ||
workflow_dispatch: # Allow manual trigger | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- uses: actions/checkout@v3 | ||
|
||
- name: Setup Emscripten | ||
uses: mymindstorm/setup-emsdk@v12 | ||
with: | ||
version: latest | ||
actions-cache-folder: 'emsdk-cache' | ||
|
||
- name: Verify Emscripten Installation | ||
run: emcc --version | ||
|
||
- name: Create Build Directory | ||
run: mkdir build | ||
|
||
- name: Configure CMake | ||
run: | | ||
cd build | ||
emcmake cmake .. | ||
- name: Build | ||
run: | | ||
cd build | ||
emmake make | ||
- name: Prepare Release Files | ||
run: | | ||
mkdir release | ||
cp build/terminal.wasm release/ | ||
cp build/terminal.js release/ | ||
cp index.html release/ | ||
cp terminal.js release/ | ||
- name: Create Release | ||
if: startsWith(github.ref, 'refs/tags/') | ||
uses: softprops/action-gh-release@v1 | ||
with: | ||
files: | | ||
release/terminal.wasm | ||
release/terminal.js | ||
release/index.html | ||
release/terminal.js | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
cmake_minimum_required(VERSION 3.13) | ||
project(wasm_terminal) | ||
|
||
set(CMAKE_C_STANDARD 11) | ||
set(CMAKE_C_STANDARD_REQUIRED ON) | ||
|
||
# Ensure we're using Emscripten | ||
if(NOT EMSCRIPTEN) | ||
message(FATAL_ERROR "This project must be compiled with Emscripten") | ||
endif() | ||
|
||
add_executable(terminal terminal.c) | ||
|
||
# Set Emscripten link flags | ||
set_target_properties(terminal PROPERTIES | ||
LINK_FLAGS "-s WASM=1 -s EXPORTED_RUNTIME_METHODS=['stringToNewUTF8','UTF8ToString'] -s EXPORTED_FUNCTIONS=['_malloc','_free','_init_terminal','_put_char','_get_line','_get_cursor_x','_get_cursor_y','_write_text'] -s NO_EXIT_RUNTIME=1" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
# WebAssembly Terminal | ||
|
||
A simple terminal emulator implemented in C and compiled to WebAssembly, rendered in an HTML canvas. | ||
|
||
## Prerequisites | ||
|
||
- Emscripten SDK (emsdk) | ||
- CMake (version 3.13 or higher) | ||
- A modern web browser with WebAssembly support | ||
|
||
## Building the Project | ||
|
||
1. First, ensure you have activated the Emscripten environment: | ||
```bash | ||
source /path/to/emsdk/emsdk_env.sh | ||
``` | ||
|
||
2. Create a build directory and navigate to it: | ||
```bash | ||
mkdir build | ||
cd build | ||
``` | ||
|
||
3. Configure the project with CMake: | ||
```bash | ||
emcmake cmake .. | ||
``` | ||
|
||
4. Build the project: | ||
```bash | ||
emmake make | ||
``` | ||
|
||
5. The build process will generate: | ||
- `terminal.wasm`: The WebAssembly binary | ||
- `terminal.js`: The JavaScript glue code | ||
|
||
6. Copy these files to your project root directory. | ||
|
||
## Running the Terminal | ||
|
||
You can serve the files using any HTTP server. For example, using Python: | ||
|
||
```bash | ||
python3 -m http.server 8000 | ||
``` | ||
|
||
Then open your browser and navigate to `http://localhost:8000` | ||
|
||
## Features | ||
|
||
- Basic terminal emulation | ||
- Text input and display | ||
- Cursor movement | ||
- Scrolling support | ||
- Classic green-on-black terminal styling | ||
|
||
## Implementation Details | ||
|
||
The terminal is implemented with the following components: | ||
|
||
- `terminal.c`: Core terminal logic in C | ||
- `terminal.js`: WebAssembly integration and canvas rendering | ||
- `index.html`: HTML canvas container and styling |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<title>WebAssembly Terminal</title> | ||
<style> | ||
body { | ||
margin: 0; | ||
padding: 20px; | ||
background-color: #1e1e1e; | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
min-height: 100vh; | ||
font-family: monospace; | ||
} | ||
#terminal-container { | ||
background-color: #000; | ||
padding: 10px; | ||
border-radius: 5px; | ||
box-shadow: 0 0 10px rgba(0,0,0,0.5); | ||
} | ||
#terminal { | ||
border: none; | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
<div id="terminal-container"> | ||
<canvas id="terminal" width="800" height="480"></canvas> | ||
</div> | ||
<script src="terminal.js"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
#include <emscripten.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
|
||
#define TERM_WIDTH 80 | ||
#define TERM_HEIGHT 24 | ||
#define CHAR_WIDTH 10 | ||
#define CHAR_HEIGHT 20 | ||
|
||
// Terminal state | ||
static char terminal_buffer[TERM_HEIGHT][TERM_WIDTH]; | ||
static int cursor_x = 0; | ||
static int cursor_y = 0; | ||
|
||
EMSCRIPTEN_KEEPALIVE | ||
void init_terminal() { | ||
// Initialize terminal buffer with spaces | ||
for (int y = 0; y < TERM_HEIGHT; y++) { | ||
for (int x = 0; x < TERM_WIDTH; x++) { | ||
terminal_buffer[y][x] = ' '; | ||
} | ||
} | ||
} | ||
|
||
EMSCRIPTEN_KEEPALIVE | ||
void put_char(char c) { | ||
if (c == '\n') { | ||
cursor_x = 0; | ||
cursor_y++; | ||
if (cursor_y >= TERM_HEIGHT) { | ||
// Scroll up | ||
for (int y = 0; y < TERM_HEIGHT - 1; y++) { | ||
memcpy(terminal_buffer[y], terminal_buffer[y + 1], TERM_WIDTH); | ||
} | ||
// Clear last line | ||
memset(terminal_buffer[TERM_HEIGHT - 1], ' ', TERM_WIDTH); | ||
cursor_y = TERM_HEIGHT - 1; | ||
} | ||
} else { | ||
if (cursor_x >= TERM_WIDTH) { | ||
cursor_x = 0; | ||
cursor_y++; | ||
if (cursor_y >= TERM_HEIGHT) { | ||
// Scroll up | ||
for (int y = 0; y < TERM_HEIGHT - 1; y++) { | ||
memcpy(terminal_buffer[y], terminal_buffer[y + 1], TERM_WIDTH); | ||
} | ||
// Clear last line | ||
memset(terminal_buffer[TERM_HEIGHT - 1], ' ', TERM_WIDTH); | ||
cursor_y = TERM_HEIGHT - 1; | ||
} | ||
} | ||
terminal_buffer[cursor_y][cursor_x] = c; | ||
cursor_x++; | ||
} | ||
} | ||
|
||
EMSCRIPTEN_KEEPALIVE | ||
char* get_line(int y) { | ||
if (y >= 0 && y < TERM_HEIGHT) { | ||
return terminal_buffer[y]; | ||
} | ||
return NULL; | ||
} | ||
|
||
EMSCRIPTEN_KEEPALIVE | ||
int get_cursor_x() { | ||
return cursor_x; | ||
} | ||
|
||
EMSCRIPTEN_KEEPALIVE | ||
int get_cursor_y() { | ||
return cursor_y; | ||
} | ||
|
||
EMSCRIPTEN_KEEPALIVE | ||
void write_text(const char* text) { | ||
int len = strlen(text); | ||
for (int i = 0; i < len; i++) { | ||
put_char(text[i]); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
let Module = { | ||
onRuntimeInitialized: function() { | ||
initTerminal(); | ||
} | ||
}; | ||
|
||
const CHAR_WIDTH = 10; | ||
const CHAR_HEIGHT = 20; | ||
const TERM_WIDTH = 80; | ||
const TERM_HEIGHT = 24; | ||
|
||
let canvas, ctx; | ||
|
||
function initTerminal() { | ||
canvas = document.getElementById('terminal'); | ||
ctx = canvas.getContext('2d'); | ||
|
||
// Set up canvas for terminal rendering | ||
ctx.font = `${CHAR_HEIGHT}px monospace`; | ||
ctx.textBaseline = 'top'; | ||
ctx.fillStyle = '#00ff00'; // Classic green terminal text | ||
|
||
// Initialize the terminal buffer in WebAssembly | ||
Module._init_terminal(); | ||
|
||
// Write some initial text | ||
writeToTerminal("WebAssembly Terminal v1.0\n> "); | ||
|
||
// Set up keyboard input | ||
document.addEventListener('keypress', handleKeyPress); | ||
|
||
// Start the render loop | ||
requestAnimationFrame(render); | ||
} | ||
|
||
function writeToTerminal(text) { | ||
const textPtr = Module.stringToNewUTF8(text); | ||
Module._write_text(textPtr); | ||
Module._free(textPtr); | ||
} | ||
|
||
function handleKeyPress(event) { | ||
const char = String.fromCharCode(event.charCode); | ||
writeToTerminal(char); | ||
if (event.key === 'Enter') { | ||
writeToTerminal('\n> '); | ||
} | ||
} | ||
|
||
function render() { | ||
// Clear the canvas | ||
ctx.fillStyle = '#000000'; | ||
ctx.fillRect(0, 0, canvas.width, canvas.height); | ||
ctx.fillStyle = '#00ff00'; | ||
|
||
// Render each line | ||
for (let y = 0; y < TERM_HEIGHT; y++) { | ||
const linePtr = Module._get_line(y); | ||
if (linePtr) { | ||
const line = Module.UTF8ToString(linePtr, TERM_WIDTH); | ||
for (let x = 0; x < TERM_WIDTH; x++) { | ||
ctx.fillText( | ||
line[x], | ||
x * CHAR_WIDTH, | ||
y * CHAR_HEIGHT | ||
); | ||
} | ||
} | ||
} | ||
|
||
// Draw cursor | ||
const cursorX = Module._get_cursor_x(); | ||
const cursorY = Module._get_cursor_y(); | ||
ctx.fillStyle = '#00ff00'; | ||
ctx.fillRect( | ||
cursorX * CHAR_WIDTH, | ||
cursorY * CHAR_HEIGHT, | ||
CHAR_WIDTH, | ||
2 | ||
); | ||
|
||
requestAnimationFrame(render); | ||
} |