--- /dev/null
+package canvas
+
+init :: () -> void #foreign "canvas" "init" ---
+clear :: () -> void #foreign "canvas" "clear" ---
+set_fill_style :: (style: str) -> void #foreign "canvas" "set_fill_style" ---
+fill_rect :: (x: i32, y: i32, w: i32, h: i32) -> void #foreign "canvas" "fill_rect" ---
+
+get_size_internal :: (out_width: ^i32, out_height: ^i32) -> void #foreign "canvas" "get_size" ---
+get_size :: () -> (i32, i32) {
+ sx, sy: i32;
+ get_size_internal(^sx, ^sy);
+ return sx, sy;
+}
with os.fdopen(code_file_desc, "w+b") as code_file:
code_file.write(code.encode('utf-8'))
- compile_process = subprocess.run(f"timeout -s KILL 10s onyx -r js --no-file-contents --no-colors -o {wasm_file_name} {code_file_name} ./read_line.onyx", shell=True, capture_output=True)
+ compile_process = subprocess.run(f"timeout -s KILL 10s onyx -r js --no-file-contents --no-colors -o {wasm_file_name} {code_file_name} ./read_line.onyx ./canvas.onyx", shell=True, capture_output=True)
os.remove(code_file_name)
:root {
--menu-bar-height: 32px;
- --input-bar-height: 32px;
+ --input-bar-height: 48px;
--divider-size: 4px;
--left-half-width: 70%;
+ --top-half-height: 30%;
}
* {
#code-result {
height: 100vh;
- max-height: calc(100vh - var(--menu-bar-height) - var(--input-bar-height)); /* subtract the padding */
+ max-height: calc(100vh - var(--menu-bar-height) - var(--input-bar-height) - var(--top-half-height) - var(--divider-size)); /* subtract the padding */
background: var(--terminal-background-color);
color: var(--terminal-foreground-color);
float: left;
}
+#vertical-divider {
+ width: 100%;
+ height: var(--divider-size);
+ background-color: #777;
+ cursor: row-resize;
+
+ display: inline-block;
+ float: left;
+}
+
#input-bar {
background-color: var(--background-color);
color: var(--foreground-color);
font-family: Monaco, Menlo, "Ubuntu Mono", Consolas, source-code-pro, monospace;
}
+#render-target {
+ width: 100%;
+ height: var(--top-half-height);
+}
+
.jquery-modal.blocker.current {
z-index: 100 !important;
}
--- /dev/null
+let canvas_element = "";
+let ctx = null;
+
+function update_canvas_size() {
+ let canvas_data = new Int32Array(canvas_shared_buffer);
+ let box = canvas_element.getBoundingClientRect();
+
+ canvas_element.width = box.width;
+ canvas_element.height = box.height;
+
+ Atomics.store(canvas_data, 0, box.width);
+ Atomics.store(canvas_data, 1, box.height);
+ Atomics.store(canvas_data, 2, 1);
+ Atomics.notify(canvas_data, 2);
+}
+
+function canvas_handler(instr) {
+ switch (instr[0]) {
+ case 'init': {
+ canvas_element = document.getElementById("render-target");
+ ctx = canvas_element.getContext('2d');
+ update_canvas_size();
+ break;
+ }
+
+ case 'set_fill_style': {
+ ctx.fillStyle = instr[1];
+ break;
+ }
+
+ case 'fill_rect': {
+ ctx.fillRect(instr[1], instr[2], instr[3], instr[4]);
+ let canvas_data = new Int32Array(canvas_shared_buffer);
+
+ // TODO: Do I need to synchonize the worker thread and the master thread?
+ Atomics.store(canvas_data, 2, 1);
+ Atomics.notify(canvas_data, 2);
+ break;
+ }
+
+ case 'clear': {
+ ctx.clearRect(0, 0, canvas_element.width, canvas_element.height);
+ break;
+ }
+ }
+}
let editor_theme = "chrome";
let ui_theme = "dark";
-let input_shared_buffer = new SharedArrayBuffer(1024 * Uint8Array.BYTES_PER_ELEMENT);
+let input_shared_buffer = new SharedArrayBuffer(1024 * Uint8Array.BYTES_PER_ELEMENT);
+let canvas_shared_buffer = new SharedArrayBuffer(3 * Int32Array.BYTES_PER_ELEMENT);
async function clear_output() {
let elem = document.getElementById('code-result');
update_running_msg();
break;
}
+
+ case 'canvas': {
+ canvas_handler(e.data.data);
+ }
}
};
- wasm_worker.postMessage({ type: 'set_buffer', data: input_shared_buffer });
- wasm_worker.postMessage({ type: 'start', data: wasm_bytes });
+ wasm_worker.postMessage({
+ type: 'set_buffers',
+ input_buffer: input_shared_buffer,
+ canvas_buffer: canvas_shared_buffer,
+ });
+ wasm_worker.postMessage({ type: 'start', data: wasm_bytes });
update_running_msg();
}
populate_examples();
load_settings();
- make_resizer("horizontal-divider", "--left-half-width", (e) => {
+ make_resizer("horizontal-divider", "--left-half-width", "", (e) => {
editor.resize(true);
});
+ make_resizer("vertical-divider", "", "--top-half-height", null);
+
document.getElementById("input-bar").addEventListener("keyup", (ev) => {
if (ev.keyCode === 13) {
submit_input();
-function make_resizer(divider_id, css_prop_name, on_resize) {
+function make_resizer(divider_id, css_width_prop_name, css_height_prop_name, on_resize) {
const elem = document.getElementById(divider_id);
const left_elem = elem.previousElementSibling;
const right_elem = elem.nextElementSibling;
let mx = 0;
let my = 0;
let left_width = 0;
+ let top_height = 0;
const mouse_move_handler = (e) => {
const dx = e.clientX - mx;
const dy = e.clientY - my;
- const pw = elem.parentNode.getBoundingClientRect().width;
- const lw = 100 * Math.min(pw - 10, Math.max(10, (left_width + dx))) / pw;
- root.style.setProperty(css_prop_name, `${lw}%`);
+ if (css_width_prop_name != null) {
+ const pw = elem.parentNode.getBoundingClientRect().width;
+ const lw = 100 * Math.min(pw - 10, Math.max(10, (left_width + dx))) / pw;
+ root.style.setProperty(css_width_prop_name, `${lw}%`);
+ }
+
+ if (css_height_prop_name != null) {
+ const ph = elem.parentNode.getBoundingClientRect().height;
+ const th = 100 * Math.min(ph - 10, Math.max(10, (top_height + dy))) / ph;
+ root.style.setProperty(css_height_prop_name, `${th}%`);
+ }
document.body.style.cursor = 'col-resize';
mx = e.clientX;
my = e.clientY;
left_width = left_elem.getBoundingClientRect().width;
+ top_height = left_elem.getBoundingClientRect().height;
document.addEventListener("mousemove", mouse_move_handler);
document.addEventListener("mouseup", mouse_up_handler);
function refresh_file_lists(callback) {
$(".file-list").empty();
+ let filenames = [];
let $lists = $(".file-list");
for (let i=0; i < localStorage.length; i++) {
let name = localStorage.key(i);
if (name.startsWith("saved_")) {
let filename = name.substr(6);
- let $button = $(`<button class='file-select'>${filename}</button>`);
- $button.on('click', (ev) => callback(filename));
+ filenames.push(filename);
+ }
+ }
- let $delete_button = $(`<button class='file-delete'><i class='fas fa-trash'></i></button>`);
- $delete_button.on('click', (ev) => delete_from_local_storage_with_name(filename, callback));
+ filenames.sort();
- $lists.append($button);
- $lists.append($delete_button);
- }
+ for (let filename of filenames) {
+ let $button = $(`<button class='file-select'>${filename}</button>`);
+ $button.on('click', (ev) => callback(filename));
+
+ let $delete_button = $(`<button class='file-delete'><i class='fas fa-trash'></i></button>`);
+ $delete_button.on('click', (ev) => delete_from_local_storage_with_name(filename, callback));
+
+ $lists.append($button);
+ $lists.append($delete_button);
}
}
let input_shared_buffer = null;
+let canvas_data_buffer = null;
let wasm_memory = null;
let import_obj = {
self.postMessage({ type: 'terminated' });
self.close();
},
+ },
+
+ canvas: {
+ init() {
+ self.postMessage({ type: 'canvas', data: [ 'init' ] });
+
+ let canvas_data = new Int32Array(canvas_data_buffer);
+ Atomics.wait(canvas_data, 2, 0);
+ Atomics.store(canvas_data, 2, 0);
+ },
+
+ clear() {
+ self.postMessage({ type: 'canvas', data: [ 'clear' ] });
+ },
+
+ get_size(outwidth, outheight) {
+ let data = new DataView(wasm_memory.buffer);
+ let canvas_data = new Int32Array(canvas_data_buffer);
+
+ data.setInt32(outwidth, Atomics.load(canvas_data, 0), true);
+ data.setInt32(outheight, Atomics.load(canvas_data, 1), true);
+ },
+
+ set_fill_style(styleptr, stylelen) {
+ let strdata = new Uint8Array(wasm_memory.buffer, styleptr, stylelen);
+ let str = new TextDecoder().decode(strdata);
+
+ self.postMessage({ type: 'canvas', data: [ 'set_fill_style', str ] });
+ },
+
+ fill_rect(x, y, w, h) {
+ self.postMessage({ type: 'canvas', data: [ 'fill_rect', x, y, w, h ] });
+
+ let canvas_data = new Int32Array(canvas_data_buffer);
+ Atomics.wait(canvas_data, 2, 0);
+ Atomics.store(canvas_data, 2, 0);
+ }
}
};
onmessage = function(m) {
switch (m.data.type) {
- case 'set_buffer': {
- input_shared_buffer = m.data.data;
+ case 'set_buffers': {
+ input_shared_buffer = m.data.input_buffer;
+ canvas_data_buffer = m.data.canvas_buffer;
break;
}
<script> window.ROOT_ENDPOINT = "{{ config['ENDPOINT']}}" </script>
<script src="{{ config['ENDPOINT'] + url_for('static', filename='src/resizer.js') }}"></script>
<script src="{{ config['ENDPOINT'] + url_for('static', filename='src/storage.js') }}"></script>
+ <script src="{{ config['ENDPOINT'] + url_for('static', filename='src/canvas.js') }}"></script>
<script src="{{ config['ENDPOINT'] + url_for('static', filename='src/index.js') }}"></script>
<script src="{{ config['ENDPOINT'] + url_for('static', filename='vendor/ace/ace.js') }}"></script>
<script src="{{ config['ENDPOINT'] + url_for('static', filename='vendor/jquery/jquery.min.js') }}"></script>
}</pre>
</div>
- <div id="horizontal-divider"></div>
+ <div id="horizontal-divider" class="divider"></div>
<div class="right-half">
+ <canvas id="render-target"></canvas>
+
+ <div id="vertical-divider" class="divider"></div>
+
<pre id="code-result"></pre>
<input type="textbox" id="input-bar" placeholder="Input line" />
<!--<input type="button" id="input-submit" value="Enter" onclick="submit_input()" /> -->