656 lines
18 KiB
Forth
656 lines
18 KiB
Forth
\ Copyright 2022 Bradley D. Nelson
|
|
\
|
|
\ Licensed under the Apache License, Version 2.0 (the "License");
|
|
\ you may not use this file except in compliance with the License.
|
|
\ You may obtain a copy of the License at
|
|
\
|
|
\ http://www.apache.org/licenses/LICENSE-2.0
|
|
\
|
|
\ Unless required by applicable law or agreed to in writing, software
|
|
\ distributed under the License is distributed on an "AS IS" BASIS,
|
|
\ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
\ See the License for the specific language governing permissions and
|
|
\ limitations under the License.
|
|
|
|
vocabulary web web definitions
|
|
|
|
: jseval! ( a n index -- ) 0 call ;
|
|
|
|
r|
|
|
(function(sp) {
|
|
var n = i32[sp>>2]; sp -= 4;
|
|
var a = i32[sp>>2]; sp -= 4;
|
|
var text = GetString(a, n);
|
|
eval(text);
|
|
return sp;
|
|
})
|
|
| 1 jseval!
|
|
: jseval ( a n -- ) 1 call ;
|
|
|
|
r~
|
|
globalObj.ueforth = context;
|
|
context.inbuffer = [];
|
|
context.Update = function() {};
|
|
if (!globalObj.write) {
|
|
function AddMeta(name, content) {
|
|
var meta = document.createElement('meta');
|
|
meta.name = name;
|
|
meta.content = content;
|
|
document.head.appendChild(meta);
|
|
}
|
|
|
|
AddMeta('apple-mobile-web-app-capable', 'yes');
|
|
AddMeta('apple-mobile-web-app-status-bar-style', 'black-translucent');
|
|
AddMeta('viewport', 'width=device-width, initial-scale=1.0, ' +
|
|
'maximum-scale=1.0, user-scalable=no, minimal-ui');
|
|
|
|
context.screen = document.getElementById('ueforth');
|
|
if (context.screen === null) {
|
|
context.screen = document.createElement('div');
|
|
context.screen.style.width = '100%';
|
|
document.body.appendChild(context.screen);
|
|
}
|
|
context.filler = document.createElement('div');
|
|
document.body.insertBefore(context.filler, document.body.firstChild);
|
|
context.canvas = document.createElement('canvas');
|
|
context.canvas.width = 1000;
|
|
context.canvas.height = 1000;
|
|
context.canvas.style.width = '1px';
|
|
context.canvas.style.height = '1px';
|
|
context.canvas.style.top = 0;
|
|
context.canvas.style.left = 0;
|
|
context.canvas.style.position = 'fixed';
|
|
context.canvas.style.backgroundColor = '#000';
|
|
context.screen.appendChild(context.canvas);
|
|
context.ctx = context.canvas.getContext('2d');
|
|
|
|
context.cursor = null;
|
|
context.cursor_time = new Date().getTime();
|
|
setInterval(function() {
|
|
if (context.cursor) {
|
|
var now = new Date().getTime();
|
|
var state = Math.floor((now - context.cursor_time) / 250) % 2;
|
|
if (state) {
|
|
context.cursor.style.visibility = 'hidden';
|
|
} else {
|
|
context.cursor.style.visibility = 'visible';
|
|
}
|
|
}
|
|
}, 50);
|
|
|
|
context.terminal = document.createElement('div');
|
|
context.terminal.style.width = '100%';
|
|
context.terminal.style.whiteSpace = 'pre-wrap';
|
|
context.screen.appendChild(context.terminal);
|
|
const DEFAULT_FG = 0x000000;
|
|
const DEFAULT_BG = 0xFFFFFF;
|
|
context.attrib = [DEFAULT_FG, DEFAULT_BG];
|
|
context.lines = [];
|
|
context.escaping = [];
|
|
|
|
context.LineFeed = function() {
|
|
var line = document.createElement('pre');
|
|
line.style.width = '100%';
|
|
line.style.whiteSpace = 'pre-wrap';
|
|
line.style.margin = '0px';
|
|
if (context.cy < 0) {
|
|
context.terminal.appendChild(line);
|
|
} else {
|
|
context.terminal.insertBefore(line, context.lines[context.cy].nextSibling);
|
|
}
|
|
context.cx = 0; // implicit cr
|
|
if (context.cy >= 0) {
|
|
context.dirty[context.cy] = 1;
|
|
}
|
|
++context.cy;
|
|
context.lines.splice(context.cy, 0, [line, []]);
|
|
context.dirty[context.cy] = 1;
|
|
};
|
|
|
|
context.toRGB = function(col) {
|
|
var r = (col >> 16) & 0xff;
|
|
var g = (col >> 8) & 0xff;
|
|
var b = col & 0xff;
|
|
return 'rgb(' + r + ',' + g + ',' + b + ')';
|
|
};
|
|
|
|
context.ResetTerminal = function() {
|
|
// TODO: Make this nicer.
|
|
document.body.style.color = context.toRGB(context.attrib[0]);
|
|
document.body.style.backgroundColor = context.toRGB(context.attrib[1]);
|
|
for (var i = 0; i < context.lines.length; ++i) {
|
|
context.terminal.removeChild(context.lines[i][0]);
|
|
}
|
|
context.lines = [];
|
|
context.cx = 0;
|
|
context.cy = -1;
|
|
context.dirty = {};
|
|
context.LineFeed();
|
|
};
|
|
context.ResetTerminal();
|
|
|
|
context.TermColor = function(n) {
|
|
n = n & 0xff;
|
|
if (n < 16) {
|
|
var i = n & 8;
|
|
var r = (n & 1) ? (i ? 255 : 192) : (i ? 128 : 0);
|
|
var g = (n & 2) ? (i ? 255 : 192) : (i ? 128 : 0);
|
|
var b = (n & 4) ? (i ? 255 : 192) : (i ? 128 : 0);
|
|
return (r << 16) | (g << 8) | b;
|
|
} else if (n < 232) {
|
|
n -= 16;
|
|
var r = Math.round((Math.floor(n / 36) % 6) * 255 / 5);
|
|
var g = Math.round((Math.floor(n / 6) % 6) * 255 / 5);
|
|
var b = Math.round((n % 6) * 255 / 5);
|
|
return (r << 16) | (g << 8) | b;
|
|
} else {
|
|
n = Math.round((n - 232) * 255 / 23);
|
|
return (n << 16) | (n << 8) | n;
|
|
}
|
|
};
|
|
|
|
context.EscapeCode = function(code) {
|
|
var m;
|
|
if (code == '[2J') {
|
|
context.ResetTerminal();
|
|
} else if (code == '[H') {
|
|
context.cx = 0;
|
|
context.cy = 0;
|
|
} else if (code == '[0m') {
|
|
context.attrib = [DEFAULT_FG, DEFAULT_BG];
|
|
} else if (m = code.match(/\[38;5;([0-9]+)m/)) {
|
|
context.attrib[0] = context.TermColor(parseInt(m[1]));
|
|
} else if (m = code.match(/\[48;5;([0-9]+)m/)) {
|
|
context.attrib[1] = context.TermColor(parseInt(m[1]));
|
|
} else {
|
|
console.log('Unknown escape code: ' + code);
|
|
}
|
|
};
|
|
|
|
context.Emit = function(ch) {
|
|
if (ch === 27) {
|
|
context.escaping = [27];
|
|
return;
|
|
}
|
|
if (context.escaping.length) {
|
|
context.escaping.push(ch);
|
|
if ((ch >= 65 && ch <= 90) || (ch >= 97 && ch <= 122)) { // [A-Za-z]
|
|
context.EscapeCode(new TextDecoder('utf-8').decode(new Uint8Array(context.escaping)).slice(1));
|
|
context.escaping = [];
|
|
}
|
|
return;
|
|
}
|
|
if (ch === 12) {
|
|
context.ResetTerminal();
|
|
context.dirty = {};
|
|
return;
|
|
} else if (ch == 10) {
|
|
context.LineFeed();
|
|
return;
|
|
}
|
|
context.dirty[context.cy] = 1;
|
|
if (ch === 8) {
|
|
context.cx = Math.max(0, context.cx - 1);
|
|
} else if (ch === 13) {
|
|
context.cx = 0;
|
|
} else {
|
|
context.lines[context.cy][1].splice(
|
|
context.cx++, 1, [context.attrib[0], context.attrib[1], ch]);
|
|
}
|
|
};
|
|
|
|
context.Update = function() {
|
|
const CURSOR = String.fromCharCode(0x2592);
|
|
var count = 0;
|
|
for (var y in context.dirty) {
|
|
++count;
|
|
var tag = context.lines[y][0];
|
|
var line = context.lines[y][1];
|
|
var parts = [];
|
|
var p = null;
|
|
for (var x = 0; x < line.length; ++x) {
|
|
if (x == 0 ||
|
|
(x == context.cx && y == context.cy) ||
|
|
p[0] != line[x][0] || p[1] != line[x][1]) {
|
|
parts.push([line[x][0], line[x][1], []]);
|
|
p = parts[parts.length - 1];
|
|
if (x == context.cx && y == context.cy) {
|
|
p[0] |= 0x1000000;
|
|
}
|
|
}
|
|
p[2].push(line[x][2]);
|
|
}
|
|
if (x == context.cx && y == context.cy) {
|
|
if (parts.length > 0) {
|
|
parts.push([parts[parts.length - 1][0] | 0x1000000,
|
|
parts[parts.length - 1][1], []]);
|
|
} else {
|
|
parts.push([context.attrib[0] | 0x1000000,
|
|
context.attrib[1], []]);
|
|
}
|
|
}
|
|
var ntag = document.createElement('pre');
|
|
ntag.style.width = '100%';
|
|
ntag.style.whiteSpace = 'pre-wrap';
|
|
ntag.style.margin = '0px';
|
|
for (var i = 0; i < parts.length; ++i) {
|
|
var span = document.createElement('span');
|
|
span.innerText = new TextDecoder('utf-8').decode(new Uint8Array(parts[i][2]));
|
|
span.style.color = context.toRGB(parts[i][0]);
|
|
span.style.backgroundColor = context.toRGB(parts[i][1]);
|
|
if (parts[i][0] & 0x1000000) {
|
|
span.style.position = 'relative';
|
|
var cursor = document.createElement('span');
|
|
cursor.classList.add('cursor');
|
|
cursor.innerText = CURSOR;
|
|
cursor.style.position = 'absolute';
|
|
cursor.style.left = '0px';
|
|
cursor.style.backgroundColor = span.style.backgroundColor;
|
|
span.appendChild(cursor);
|
|
context.cursor = cursor;
|
|
}
|
|
ntag.appendChild(span);
|
|
if (i === parts.length - 1) {
|
|
ntag.style.color = span.style.color;
|
|
ntag.style.backgroundColor = span.style.backgroundColor;
|
|
}
|
|
}
|
|
context.terminal.replaceChild(ntag, tag);
|
|
context.lines[y][0] = ntag;
|
|
}
|
|
var newline = count > 1 || context.dirty[context.lines.length - 1];
|
|
context.dirty = {};
|
|
if (newline) {
|
|
window.scrollTo(0, document.body.scrollHeight);
|
|
}
|
|
};
|
|
|
|
context.keyboard = document.createElement('div');
|
|
context.KEY_HEIGHT = 45;
|
|
context.KEYBOARD_HEIGHT = context.KEY_HEIGHT * 4;
|
|
const TAB = ['⭾', 9, 45];
|
|
const PIPE = [String.fromCharCode(124), 124, 45];
|
|
const BACKSLASH = ['\\', 92, 45];
|
|
const ENTER = ['⏎', 13, 45];
|
|
const SHIFT = ['⇧', 1, 45, 0];
|
|
const SHIFT2 = ['⬆', 0, 45, 0];
|
|
const SHIFT3 = ['=\\<', 3, 45, 0];
|
|
const NUMS = ['?123', 2, 45, 0];
|
|
const ABC = ['ABC', 0, 45, 0];
|
|
const BACKSPACE = ['⌫', 8, 45];
|
|
const BACKTICK = String.fromCharCode(96);
|
|
const TILDE = String.fromCharCode(126);
|
|
const PASTE = ['^V', 22, 30];
|
|
const G1 = ['Gap', 0, 15];
|
|
const KEY_COLOR = 'linear-gradient(to bottom right, #ccc, #999)';
|
|
var keymaps = [
|
|
AddKeymap([
|
|
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', 'Newline',
|
|
G1, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', G1, 'Newline',
|
|
SHIFT, 'z', 'x', 'c', 'v', 'b', 'n', 'm', BACKSPACE, 'Newline',
|
|
NUMS, '/', [' ', 32, 5 * 30], '.', ENTER,
|
|
]),
|
|
AddKeymap([
|
|
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', 'Newline',
|
|
G1, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', G1, 'Newline',
|
|
SHIFT2, 'Z', 'X', 'C', 'V', 'B', 'N', 'M', BACKSPACE, 'Newline',
|
|
NUMS, '/', [' ', 32, 5 * 30], '.', ENTER,
|
|
]),
|
|
AddKeymap([
|
|
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'Newline',
|
|
PASTE, '@', '$', '_', '&', '-', '+', '(', ')', '/', 'Newline',
|
|
SHIFT3, '*', '"', '\'', ':', ';', '!', '?', BACKSPACE, 'Newline',
|
|
ABC, ',', [' ', 32, 5 * 30], '.', ENTER,
|
|
]),
|
|
AddKeymap([
|
|
TILDE, BACKTICK, '3', '4', '5', '^', '7', '8', '9', '0', 'Newline',
|
|
'#', '@', '$', '_', '&', '-', '=', '{', '}', '\\', 'Newline',
|
|
NUMS, '%', '"', '\'', ':', ';', '[', ']', BACKSPACE, 'Newline',
|
|
ABC, '<', [' ', 32, 5 * 30], '>', ENTER,
|
|
]),
|
|
];
|
|
function SwitchKeymap(n) {
|
|
for (var i = 0; i < keymaps.length; ++i) {
|
|
keymaps[i].style.display = i == n ? '' : 'none';
|
|
}
|
|
}
|
|
context.Inject = function(text) {
|
|
var data = new TextEncoder().encode(text);
|
|
for (var i = 0; i < data.length; ++i) {
|
|
context.inbuffer.push(data[i]);
|
|
}
|
|
};
|
|
context.Paste = function() {
|
|
navigator.clipboard.readText().then(function(clipText) {
|
|
context.Inject(clipText);
|
|
});
|
|
};
|
|
function AddKey(keymap, item) {
|
|
if (item === 'Newline') {
|
|
var k = document.createElement('br');
|
|
keymap.appendChild(k);
|
|
return;
|
|
}
|
|
var k = document.createElement('button');
|
|
k.style.fontFamily = 'monospace';
|
|
k.style.verticalAlign = 'middle';
|
|
k.style.border = 'none';
|
|
k.style.margin = '0';
|
|
k.style.padding = '0';
|
|
k.style.backgroundImage = KEY_COLOR;
|
|
k.style.width = (100 / 10) + '%';
|
|
k.style.height = context.KEY_HEIGHT + 'px';
|
|
if (item.length > 2) {
|
|
k.style.width = (100 / 10 * item[2] / 30) + '%';
|
|
}
|
|
if (item[0] === 'Gap') {
|
|
k.style.backgroundColor = '#444';
|
|
k.style.backgroundImage = '';
|
|
keymap.appendChild(k);
|
|
return;
|
|
}
|
|
if (item.length > 1) {
|
|
var keycode = item[1];
|
|
} else {
|
|
var keycode = item[0].charCodeAt(0);
|
|
}
|
|
k.innerHTML = item instanceof Array ? item[0] : item;
|
|
k.onclick = function() {
|
|
if (item.length > 3) { // SHIFT
|
|
SwitchKeymap(item[1]);
|
|
} else if (keycode === 22) { // PASTE
|
|
context.Paste();
|
|
} else {
|
|
context.inbuffer.push(keycode);
|
|
}
|
|
};
|
|
keymap.appendChild(k);
|
|
}
|
|
function AddKeymap(keymap) {
|
|
var div = document.createElement('div');
|
|
for (var i = 0; i < keymap.length; ++i) {
|
|
var item = keymap[i];
|
|
AddKey(div, item);
|
|
}
|
|
context.keyboard.appendChild(div);
|
|
return div;
|
|
}
|
|
SwitchKeymap(0);
|
|
context.keyboard.style.position = 'fixed';
|
|
context.keyboard.style.width = '100%';
|
|
context.keyboard.style.bottom = '0px';
|
|
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
|
|
context.mobile = -1;
|
|
context.tailer = document.createElement('div');
|
|
context.tailer.style.width = '1px';
|
|
context.tailer.style.height = context.KEYBOARD_HEIGHT + 'px';
|
|
context.screen.appendChild(context.tailer);
|
|
document.body.appendChild(context.keyboard);
|
|
} else {
|
|
context.mobile = 0;
|
|
}
|
|
|
|
context.text_fraction = context.mobile ? 3000 : 1667;
|
|
context.min_text_portion = 120 + (context.mobile ? context.KEYBOARD_HEIGHT : 0);
|
|
context.mode = 1;
|
|
function setMode(mode) {
|
|
if (context.mode === mode) {
|
|
return ;
|
|
}
|
|
if (mode) {
|
|
context.filler.style.display = '';
|
|
context.canvas.style.display = '';
|
|
} else {
|
|
context.filler.style.display = 'none';
|
|
context.canvas.style.display = 'none';
|
|
}
|
|
context.mode = mode;
|
|
}
|
|
context.setMode = setMode;
|
|
function Resize() {
|
|
var width = window.innerWidth;
|
|
var theight = Math.max(context.min_text_portion,
|
|
Math.floor(window.innerHeight *
|
|
context.min_text_portion / 10000));
|
|
var height = window.innerHeight - theight;
|
|
if (width === context.width && height === context.height) {
|
|
return;
|
|
}
|
|
context.canvas.style.width = width + 'px';
|
|
context.canvas.style.height = height + 'px';
|
|
if (context.text_fraction == 0 &&
|
|
context.min_text_portion == 0) {
|
|
context.filler.style.width = '1px';
|
|
context.filler.style.height = '0px';
|
|
} else {
|
|
context.filler.style.width = '1px';
|
|
context.filler.style.height = height + 'px';
|
|
}
|
|
context.width = width;
|
|
context.height = height;
|
|
}
|
|
context.Resize = Resize;
|
|
function Clear() {
|
|
Resize();
|
|
context.ctx.fillStyle = '#000';
|
|
context.ctx.fillRect(0, 0, context.canvas.width, context.canvas.height);
|
|
}
|
|
context.Clear = Clear;
|
|
window.onresize = function(e) {
|
|
Resize();
|
|
};
|
|
function KeyPress(e) {
|
|
context.cursor_time = new Date().getTime();
|
|
context.inbuffer.push(e.keyCode);
|
|
e.preventDefault();
|
|
return false;
|
|
}
|
|
window.onkeypress = KeyPress;
|
|
function KeyDown(e) {
|
|
if (e.keyCode == 8) {
|
|
context.cursor_time = new Date().getTime();
|
|
context.inbuffer.push(e.keyCode);
|
|
e.preventDefault();
|
|
return false;
|
|
}
|
|
}
|
|
window.onkeydown = KeyDown;
|
|
window.addEventListener('paste', function(e) {
|
|
context.Inject(e.clipboardData.getData('text'));
|
|
});
|
|
setMode(0);
|
|
context.Clear();
|
|
}
|
|
~ jseval
|
|
|
|
r|
|
|
(function(sp) {
|
|
var n = i32[sp>>2]; sp -= 4;
|
|
var a = i32[sp>>2]; sp -= 4;
|
|
if (globalObj.write) {
|
|
var text = GetString(a, n);
|
|
write(text);
|
|
sp += 4; i32[sp>>2] = 0;
|
|
} else {
|
|
var newline = false;
|
|
for (var i = 0; i < n; ++i) {
|
|
var ch = u8[a + i];
|
|
if (ch == 10) { newline = true; }
|
|
context.Emit(ch);
|
|
}
|
|
if (newline) {
|
|
context.Update();
|
|
}
|
|
sp += 4; i32[sp>>2] = newline ? -1 : 0;
|
|
}
|
|
return sp;
|
|
})
|
|
| 2 jseval!
|
|
: web-type ( a n -- ) 2 call if yield then ;
|
|
' web-type is type
|
|
|
|
r|
|
|
(function(sp) {
|
|
context.Update();
|
|
if (globalObj.readline && !context.inbuffer.length) {
|
|
var line = unescape(encodeURIComponent(readline()));
|
|
for (var i = 0; i < line.length; ++i) {
|
|
context.inbuffer.push(line.charCodeAt(i));
|
|
}
|
|
context.inbuffer.push(13);
|
|
}
|
|
if (context.inbuffer.length) {
|
|
sp += 4; i32[sp>>2] = context.inbuffer.shift();
|
|
} else {
|
|
sp += 4; i32[sp>>2] = 0;
|
|
}
|
|
return sp;
|
|
})
|
|
| 3 jseval!
|
|
: web-key ( -- n ) begin yield 3 call dup if exit then drop again ;
|
|
' web-key is key
|
|
|
|
r|
|
|
(function(sp) {
|
|
context.Update();
|
|
if (globalObj.readline) {
|
|
sp += 4; i32[sp>>2] = -1;
|
|
return sp;
|
|
}
|
|
sp += 4; i32[sp>>2] = context.inbuffer.length ? -1 : 0;
|
|
return sp;
|
|
})
|
|
| 4 jseval!
|
|
: web-key? ( -- f ) yield 4 call ;
|
|
' web-key? is key?
|
|
|
|
r|
|
|
(function(sp) {
|
|
var val = i32[sp>>2]; sp -= 4;
|
|
if (globalObj.quit) {
|
|
quit(val);
|
|
} else {
|
|
Init();
|
|
}
|
|
return sp;
|
|
})
|
|
| 5 jseval!
|
|
: terminate ( n -- ) 5 call ;
|
|
|
|
r|
|
|
(function(sp) {
|
|
if (globalObj.write) {
|
|
sp += 4; i32[sp>>2] = 0; // Disable echo.
|
|
} else {
|
|
sp += 4; i32[sp>>2] = -1; // Enable echo.
|
|
}
|
|
return sp;
|
|
})
|
|
| 6 jseval! 6 call echo !
|
|
|
|
r|
|
|
(function(sp) {
|
|
var mode = i32[sp>>2]; sp -= 4;
|
|
if (globalObj.write) {
|
|
return sp;
|
|
}
|
|
context.setMode(mode);
|
|
return sp;
|
|
})
|
|
| 7 jseval!
|
|
|
|
r|
|
|
(function(sp) {
|
|
var c = i32[sp>>2]; sp -= 4;
|
|
var h = i32[sp>>2]; sp -= 4;
|
|
var w = i32[sp>>2]; sp -= 4;
|
|
var y = i32[sp>>2]; sp -= 4;
|
|
var x = i32[sp>>2]; sp -= 4;
|
|
if (globalObj.write) {
|
|
return sp;
|
|
}
|
|
function HexDig(n) {
|
|
return ('0' + n.toString(16)).slice(-2);
|
|
}
|
|
context.ctx.fillStyle = '#' + HexDig((c >> 16) & 0xff) +
|
|
HexDig((c >> 8) & 0xff) +
|
|
HexDig(c & 0xff);
|
|
context.ctx.fillRect(x, y, w, h);
|
|
return sp;
|
|
})
|
|
| 8 jseval!
|
|
|
|
r|
|
|
(function(sp) {
|
|
var h = i32[sp>>2]; sp -= 4;
|
|
var w = i32[sp>>2]; sp -= 4;
|
|
if (globalObj.write) {
|
|
return sp;
|
|
}
|
|
context.canvas.width = w;
|
|
context.canvas.height = h;
|
|
return sp;
|
|
})
|
|
| 9 jseval!
|
|
|
|
r|
|
|
(function(sp) {
|
|
if (globalObj.write) {
|
|
sp += 4; i32[sp>>2] = 1;
|
|
sp += 4; i32[sp>>2] = 1;
|
|
return sp;
|
|
}
|
|
sp += 4; i32[sp>>2] = context.width;
|
|
sp += 4; i32[sp>>2] = context.height;
|
|
return sp;
|
|
})
|
|
| 10 jseval!
|
|
|
|
r|
|
|
(function(sp) {
|
|
var mp = i32[sp>>2]; sp -= 4;
|
|
var tf = i32[sp>>2]; sp -= 4;
|
|
if (globalObj.write) {
|
|
return sp;
|
|
}
|
|
context.text_fraction = tf;
|
|
context.min_text_portion = mp;
|
|
context.Resize();
|
|
return sp;
|
|
})
|
|
| 11 jseval!
|
|
|
|
r|
|
|
(function(sp) {
|
|
sp += 4; i32[sp>>2] = context.mobile;
|
|
return sp;
|
|
})
|
|
| 12 jseval!
|
|
|
|
r|
|
|
(function(sp) {
|
|
sp += 4; i32[sp>>2] = context.KEYBOARD_HEIGHT;
|
|
return sp;
|
|
})
|
|
| 13 jseval!
|
|
|
|
forth definitions web
|
|
|
|
: bye 0 terminate ;
|
|
: gr 1 7 call ;
|
|
: text 0 7 call ;
|
|
: mobile ( -- f ) 12 call ;
|
|
: keys-height ( -- n ) 13 call ;
|
|
$ffffff value color
|
|
: box ( x y w h -- ) color 8 call ;
|
|
: window ( w h -- ) 9 call ;
|
|
: viewport@ ( -- w h ) 10 call ;
|
|
: show-text ( f -- )
|
|
if
|
|
mobile if 3000 120 keys-height + else 1667 120 then
|
|
else
|
|
mobile if 0 keys-height else 0 0 then
|
|
then 11 call ;
|
|
|
|
forth definitions
|