316 lines
7.2 KiB
D

module arsd.simpledisplay;
public import arsd.color;
import arsd.webassembly;
//shared static this() { eval("hi there"); }
// the js bridge is SO EXPENSIVE we have to minimize using it.
class SimpleWindow {
this(int width, int height, string title = "D Application") {
this.width = width;
this.height = height;
element = eval!NativeHandle(q{
var s = document.getElementById("screen");
var canvas = document.createElement("canvas");
canvas.addEventListener("contextmenu", function(event) { event.preventDefault(); });
canvas.setAttribute("width", $0);
canvas.setAttribute("height", $1);
canvas.setAttribute("title", $2);
s.appendChild(canvas);
return canvas;
}, width, height, title);
canvasContext = eval!NativeHandle(q{
return $0.getContext("2d");
}, element);
}
NativeHandle element;
NativeHandle canvasContext;
int width;
int height;
void close() {
eval(q{ clearInterval($0); }, intervalId);
intervalId = 0;
}
void delegate() onClosing;
ScreenPainter draw() {
return ScreenPainter(this);
}
int intervalId;
void eventLoop(T...)(int timeout, T t) {
foreach(arg; t) {
static if(is(typeof(arg) : void delegate())) {
sdpy_timer = arg;
} else static if(is(typeof(arg) : void delegate(KeyEvent))) {
sdpy_key = arg;
} else static if(is(typeof(arg) : void delegate(MouseEvent))) {
sdpy_mouse = arg;
} else static assert(0, typeof(arg).stringof);
}
if(timeout)
intervalId = eval!int(q{
return setInterval(function(a) { exports.sdpy_timer_trigger(); }, $0);
}, timeout);
eval(q{
function translate(key) {
var k = 0;
switch(key) {
case "[": k = 1; break;
case "]": k = 2; break;
case "Left": case "ArrowLeft": k = 3; break;
case "Right": case "ArrowRight": k = 4; break;
case "Down": case "ArrowDown": k = 5; break;
case "Up": case "ArrowUp": k = 6; break;
case " ": k = 7; break;
// "Enter", "Esc" / "Escape"
default: k = 0;
}
return k;
}
document.body.addEventListener("keydown", function(event) {
exports.sdpy_key_trigger(1, translate(event.key));
event.preventDefault();
}, true);
document.body.addEventListener("keyup", function(event) {
exports.sdpy_key_trigger(0, translate(event.key));
event.preventDefault();
}, true);
$0.addEventListener("mousedown", function(event) {
exports.sdpy_mouse_trigger(1, event.button, event.offsetX, event.offsetY);
}, true);
$0.addEventListener("mouseup", function(event) {
exports.sdpy_mouse_trigger(0, event.button);
}, true);
}, element);
}
}
void delegate() sdpy_timer;
void delegate(KeyEvent) sdpy_key;
void delegate(MouseEvent) sdpy_mouse;
export extern(C) void sdpy_timer_trigger() {
sdpy_timer();
}
export extern(C) void sdpy_key_trigger(int pressed, int key) {
KeyEvent ke;
ke.pressed = pressed ? true : false;
ke.key = key;
if(sdpy_key)
sdpy_key(ke);
}
export extern(C) void sdpy_mouse_trigger(int pressed, int button, int x, int y) {
MouseEvent me;
me.type = pressed ? MouseEventType.buttonPressed : MouseEventType.buttonReleased;
switch(button) {
case 0:
me.button = MouseButton.left;
break;
case 1:
me.button = MouseButton.middle;
break;
case 2:
me.button = MouseButton.right;
break;
default:
}
me.x = x;
me.y = y;
if(sdpy_mouse)
sdpy_mouse(me);
}
// push arguments in reverse order then push the command
enum canvasRender = q{
};
struct ScreenPainter {
this(SimpleWindow window) {
// no need to arc here tbh
this.w = window.width;
this.h = window.height;
this.element = NativeHandle(window.element.handle, false);
this.context = NativeHandle(window.canvasContext.handle, false);
}
@disable this(this); // for now...
NativeHandle element;
NativeHandle context;
private int w, h;
void clear() {
addCommand(1);
}
void outlineColor(Color c) {
char[7] data;
c.toTempString(data[]);
addCommand(2, 7, data[0], data[1], data[2], data[3], data[4], data[5], data[6]);
return;
//context.properties.strokeStyle!string = cast(immutable)(data[]);
}
void fillColor(Color c) {
char[7] data;
c.toTempString(data[]);
addCommand(3, 7, data[0], data[1], data[2], data[3], data[4], data[5], data[6]);
return;
//context.properties.fillStyle!string = cast(immutable)(data[]);
}
void drawPolygon(Point[] points) {
addCommand(8);
addCommand(cast(double) points.length);
foreach(point; points) {
push(cast(double) point.x);
push(cast(double) point.y);
}
}
void drawRectangle(Point p, int w, int h) {
addCommand(4, p.x, p.y, w, h);
}
void drawRectangle(Point p, Size s) {
drawRectangle(p, s.width, s.height);
}
void drawText(Point p, in char[] txt, Point lowerRight = Point(0, 0), uint alignment = 0) {
// FIXME use the new system
addCommand(5, p.x, p.y + 16, txt.length);
foreach(c; txt)
push(cast(double) c);
return;
eval(q{
var context = $0;
context.font = "18px sans-serif";
context.strokeText($1, $2, $3 + 16);
}, context, txt, p.x, p.y);
}
void drawCircle(Point upperLeft, int diameter) {
addCommand(6, upperLeft.x + diameter / 2, upperLeft.y + diameter / 2, diameter / 2);
}
void drawLine(Point p1, Point p2) {
drawLine(p1.x, p1.y, p2.x, p2.y);
}
void drawLine(int x1, int y1, int x2, int y2) {
addCommand(7, x1, y1, x2, y2);
}
private:
void addCommand(T...)(int cmd, T args) {
push(cmd);
foreach(arg; args) {
push(arg);
}
}
// 50ish % on ronaroids total cpu without this
// with it, we at like 16%
static __gshared double[] commandStack;
size_t commandStackPosition;
void push(T)(T t) {
if(commandStackPosition == commandStack.length) {
commandStack.length = commandStack.length + 1024;
commandStack.assumeUniqueReference();
}
commandStack[commandStackPosition++] = t;
}
~this() {
executeCanvasCommands(this.context.handle, this.commandStack.ptr, commandStackPosition);
}
}
extern(C) void executeCanvasCommands(int handle, double* start, size_t len);
struct KeyEvent {
int key;
bool pressed;
}
enum MouseEventType : int {
motion = 0, /// The mouse moved inside the window
buttonPressed = 1, /// A mouse button was pressed or the wheel was spun
buttonReleased = 2, /// A mouse button was released
}
struct MouseEvent {
MouseEventType type;
int x;
int y;
int dx;
int dy;
MouseButton button;
int modifierState;
}
enum MouseButton : int {
none = 0,
left = 1, ///
right = 2, ///
middle = 4, ///
wheelUp = 8, ///
wheelDown = 16, ///
backButton = 32, /// often found on the thumb and used for back in browsers
forwardButton = 64, /// often found on the thumb and used for forward in browsers
}
enum TextAlignment : uint {
Left = 0, ///
Center = 1, ///
Right = 2, ///
VerticalTop = 0, ///
VerticalCenter = 4, ///
VerticalBottom = 8, ///
}
enum Key {
LeftBracket = 1,
RightBracket,
Left,
Right,
Down,
Up,
Space
}
enum MouseCursor { cross }
class OperatingSystemFont {}
enum UsingSimpledisplayX11 = false;
enum SimpledisplayTimerAvailable = false;
class Sprite{}
enum bool OpenGlEnabled = false;
alias ScreenPainterImplementation = ScreenPainter;
mixin template ExperimentalTextComponent() {
class TextLayout {
}
}