316 lines
7.2 KiB
D
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 {
|
|
|
|
}
|
|
}
|