initial commit
This commit is contained in:
commit
bd790c1488
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/build
|
||||
/node_modules
|
9
build.hxml
Normal file
9
build.hxml
Normal file
@ -0,0 +1,9 @@
|
||||
-p src
|
||||
--main Startup
|
||||
|
||||
--library cctweaked
|
||||
|
||||
--dce full
|
||||
|
||||
--lua build/Haxe.lua
|
||||
-D lua-vanilla
|
10
minify.js
Normal file
10
minify.js
Normal file
@ -0,0 +1,10 @@
|
||||
const fs = require("fs");
|
||||
const luamin = require("luamin");
|
||||
|
||||
const haxeOutput = fs.readFileSync("build/Haxe.lua",{encoding:"utf8"});
|
||||
|
||||
const minified = luamin.minify(haxeOutput);
|
||||
|
||||
fs.writeFileSync("build/Haxe.min.lua",minified);
|
||||
|
||||
console.log("minified lua");
|
10
package.json
Normal file
10
package.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "haxe",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"author": "Djeeberjr <djeeberjr@gmail.com>",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"luamin": "^1.0.4"
|
||||
}
|
||||
}
|
49
src/Startup.hx
Normal file
49
src/Startup.hx
Normal file
@ -0,0 +1,49 @@
|
||||
import lib.ui.TextElement;
|
||||
import lib.ui.ReactiveUI;
|
||||
import kernel.Log;
|
||||
import util.Debug;
|
||||
import kernel.ui.WindowManager;
|
||||
import kernel.net.Net;
|
||||
import kernel.KernelEvents;
|
||||
|
||||
using util.Extender.LambdaExtender;
|
||||
|
||||
class Startup {
|
||||
static public function main() {
|
||||
// OS.sleep(Math.random() * 3); // Native lua call to `sleep`
|
||||
Net.instance.init();
|
||||
WindowManager.instance.init();
|
||||
|
||||
Debug.printBuildInfo();
|
||||
|
||||
exampleUI();
|
||||
|
||||
Log.moveToOutput("top");
|
||||
|
||||
KernelEvents.instance.startEventLoop();
|
||||
}
|
||||
|
||||
static function exampleProgramm() {
|
||||
var context = WindowManager.instance.createNewContext();
|
||||
|
||||
context.clickSignal.on(data -> {
|
||||
context.setCursorPos(data.pos.x,data.pos.y);
|
||||
context.write("x");
|
||||
});
|
||||
|
||||
context.write("Example programm");
|
||||
}
|
||||
|
||||
static function exampleUI() {
|
||||
var context = WindowManager.instance.createNewContext();
|
||||
var ui = new ReactiveUI(context);
|
||||
|
||||
ui.render([
|
||||
new TextElement("Hello world"),
|
||||
new TextElement("Hello world",Green,Red),
|
||||
]);
|
||||
|
||||
WindowManager.instance.focusContextToOutput(context,"main");
|
||||
}
|
||||
}
|
||||
|
29
src/bin/NetTest.hx
Normal file
29
src/bin/NetTest.hx
Normal file
@ -0,0 +1,29 @@
|
||||
package bin;
|
||||
|
||||
import lib.TermIO;
|
||||
import kernel.net.Net;
|
||||
import kernel.ui.WindowManager;
|
||||
import kernel.ui.WindowContext;
|
||||
import lib.IUserProgramm;
|
||||
|
||||
class NetTest implements IUserProgramm {
|
||||
private final windowContext:WindowContext = WindowManager.instance.createNewContext();
|
||||
private final writer:TermIO;
|
||||
|
||||
public function new() {
|
||||
writer = new TermIO(windowContext);
|
||||
}
|
||||
|
||||
private function drawUI() {
|
||||
var allNeighbors = Net.instance.getAllNeighbors();
|
||||
|
||||
for (neighbor in allNeighbors) {
|
||||
writer.writeLn(""+neighbor,Green);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function getName():String {
|
||||
return "Network tester";
|
||||
}
|
||||
}
|
47
src/kernel/KernelEvents.hx
Normal file
47
src/kernel/KernelEvents.hx
Normal file
@ -0,0 +1,47 @@
|
||||
package kernel;
|
||||
|
||||
import cc.OS;
|
||||
import lua.Coroutine;
|
||||
import util.EventBus;
|
||||
|
||||
using lua.Table;
|
||||
|
||||
/**
|
||||
Class for interacting with the native pullEvent system.
|
||||
**/
|
||||
class KernelEvents{
|
||||
public static final instance:KernelEvents = new KernelEvents();
|
||||
private function new () {}
|
||||
|
||||
private var eventBus: util.EventBus<Array<Dynamic>> = new EventBus();
|
||||
|
||||
/**
|
||||
Start pulling events. Blocking.
|
||||
**/
|
||||
public function startEventLoop() {
|
||||
// Log.info("Starting event loop");
|
||||
while (true){
|
||||
var event:Table<Int, Dynamic> = OS.pullEventRaw();
|
||||
|
||||
var eventName:String = event[1];
|
||||
|
||||
if (eventName == "terminate"){
|
||||
return;
|
||||
}
|
||||
|
||||
eventBus.emit(eventName,event.toArray());
|
||||
}
|
||||
}
|
||||
|
||||
public function on(eventName:String, callback:Array<Dynamic> -> Void):EventBusListner<Array<Dynamic>> {
|
||||
return eventBus.on(eventName,callback);
|
||||
}
|
||||
|
||||
public function once(eventName:String, callback:Array<Dynamic> -> Void):EventBusListner<Array<Dynamic>> {
|
||||
return eventBus.once(eventName,callback);
|
||||
}
|
||||
|
||||
public function removeListner(id:EventBusListner<Array<Dynamic>>) {
|
||||
return eventBus.removeListner(id);
|
||||
}
|
||||
}
|
38
src/kernel/Log.hx
Normal file
38
src/kernel/Log.hx
Normal file
@ -0,0 +1,38 @@
|
||||
package kernel;
|
||||
|
||||
import kernel.ui.WindowContext;
|
||||
import kernel.ui.WindowManager;
|
||||
import lib.TermWriteable;
|
||||
import lib.TermIO;
|
||||
|
||||
/**
|
||||
Log messages to specified output.
|
||||
**/
|
||||
class Log {
|
||||
private static final context:WindowContext = WindowManager.instance.createNewContext();
|
||||
private static var writer:TermIO = new TermIO(context);
|
||||
|
||||
private static function setMainoutout(newOutput: TermWriteable) {
|
||||
writer = new TermIO(newOutput);
|
||||
}
|
||||
|
||||
public static function info(msg: Dynamic, ?pos:haxe.PosInfos){
|
||||
writer.writeLn("[INFO]["+pos.className+"]: "+Std.string(msg));
|
||||
}
|
||||
|
||||
public static function warn(msg: Dynamic, ?pos:haxe.PosInfos){
|
||||
writer.writeLn("[WARN]["+pos.className+"]: "+Std.string(msg),Yellow);
|
||||
}
|
||||
|
||||
public static function error(msg: Dynamic,?pos:haxe.PosInfos) {
|
||||
writer.writeLn("[ERRO]["+pos.className+"]: "+Std.string(msg),Red);
|
||||
}
|
||||
|
||||
public static function debug(msg: Dynamic,?pos:haxe.PosInfos) {
|
||||
writer.writeLn("[DEBG]["+pos.className+"]: "+Std.string(msg),Gray);
|
||||
}
|
||||
|
||||
public static function moveToOutput(addr: String) {
|
||||
WindowManager.instance.focusContextToOutput(context,addr);
|
||||
}
|
||||
}
|
91
src/kernel/MainTerm.hx
Normal file
91
src/kernel/MainTerm.hx
Normal file
@ -0,0 +1,91 @@
|
||||
package kernel;
|
||||
|
||||
import util.Signal;
|
||||
import lib.TermWriteable;
|
||||
import cc.Term;
|
||||
import util.Vec.Vec2;
|
||||
import util.Color;
|
||||
|
||||
/**
|
||||
Represents the main computer screen.
|
||||
**/
|
||||
class MainTerm implements TermWriteable{
|
||||
public static final instance:MainTerm = new MainTerm();
|
||||
private function new() {
|
||||
KernelEvents.instance.on("term_resize",params ->{
|
||||
_onResize.emit(null);
|
||||
});
|
||||
}
|
||||
|
||||
public var onResize(get, null):SignalReadonly<Vec2<Int>>;
|
||||
private var _onResize:Signal<Vec2<Int>> = new Signal();
|
||||
|
||||
function get_onResize():SignalReadonly<Vec2<Int>> {
|
||||
return _onResize;
|
||||
}
|
||||
|
||||
public function write(text:String) {
|
||||
Term.write(text);
|
||||
}
|
||||
|
||||
public function scroll(y:Int) {
|
||||
Term.scroll(y);
|
||||
}
|
||||
|
||||
public function getCursorPos():Vec2<Int> {
|
||||
var rtn = Term.getCursorPos();
|
||||
return {
|
||||
x: rtn.x - 1,
|
||||
y: rtn.y - 1
|
||||
}
|
||||
}
|
||||
|
||||
public function setCursorPos(x:Int, y:Int) {
|
||||
Term.setCursorPos(x + 1,y + 1);
|
||||
}
|
||||
|
||||
public function getCursorBlink():Bool {
|
||||
// Missing in api
|
||||
throw new haxe.exceptions.NotImplementedException();
|
||||
}
|
||||
|
||||
public function setCursorBlink(blink:Bool) {
|
||||
Term.setCursorBlink(blink);
|
||||
}
|
||||
|
||||
public function getSize():Vec2<Int> {
|
||||
var rtn = Term.getSize();
|
||||
return {
|
||||
x: rtn.width,
|
||||
y: rtn.height,
|
||||
}
|
||||
}
|
||||
|
||||
public function clear() {
|
||||
Term.clear();
|
||||
}
|
||||
|
||||
public function clearLine() {
|
||||
Term.clearLine();
|
||||
}
|
||||
|
||||
public function getTextColor():Color {
|
||||
return ColorConvert.ccToColor(Term.getTextColor());
|
||||
}
|
||||
|
||||
public function setTextColor(colour:Color) {
|
||||
Term.setTextColor(ColorConvert.colorToCC(colour));
|
||||
}
|
||||
|
||||
public function getBackgroundColor():Color {
|
||||
return ColorConvert.ccToColor(Term.getBackgroundColor());
|
||||
}
|
||||
|
||||
public function setBackgroundColor(color:Color) {
|
||||
Term.setBackgroundColor(ColorConvert.colorToCC(color));
|
||||
}
|
||||
|
||||
public function isColor():Bool {
|
||||
return Term.isColor();
|
||||
}
|
||||
}
|
36
src/kernel/Timer.hx
Normal file
36
src/kernel/Timer.hx
Normal file
@ -0,0 +1,36 @@
|
||||
package kernel;
|
||||
|
||||
import util.EventBus.EventBusListner;
|
||||
import cc.OS;
|
||||
|
||||
/**
|
||||
Wrapper class for using timer.
|
||||
**/
|
||||
class Timer {
|
||||
private final timerID:Int;
|
||||
private final callback:Void->Void;
|
||||
private final timerListner:EventBusListner<Array<Dynamic>>;
|
||||
|
||||
/**
|
||||
Create new timer with timeout in seconds.
|
||||
**/
|
||||
public function new(timeout: Int, cb: Void->Void) {
|
||||
timerID = OS.startTimer(timeout);
|
||||
callback = cb;
|
||||
|
||||
timerListner = KernelEvents.instance.on("timer",(params)->{
|
||||
if (params[1] == timerID){
|
||||
cb();
|
||||
KernelEvents.instance.removeListner(timerListner);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
Cancle timer.
|
||||
**/
|
||||
public function cancle() {
|
||||
OS.cancelTimer(timerID);
|
||||
KernelEvents.instance.removeListner(timerListner);
|
||||
}
|
||||
}
|
224
src/kernel/net/Net.hx
Normal file
224
src/kernel/net/Net.hx
Normal file
@ -0,0 +1,224 @@
|
||||
package kernel.net;
|
||||
|
||||
import kernel.peripherals.Peripherals.Peripheral;
|
||||
import kernel.Log;
|
||||
import kernel.KernelEvents;
|
||||
import haxe.Exception;
|
||||
import util.Promise;
|
||||
import kernel.Timer;
|
||||
import util.EventBus;
|
||||
import cc.OS;
|
||||
|
||||
using Lambda;
|
||||
using util.Extender.LambdaExtender;
|
||||
|
||||
/**
|
||||
Class responsible for everything network related.
|
||||
Used to send and recceive packages.
|
||||
**/
|
||||
class Net{
|
||||
public static final instance:Net = new Net();
|
||||
private function new () {}
|
||||
|
||||
public static inline final BRODCAST_PORT:Int = 65533;
|
||||
public static inline final MESSAGE_TIMEOUT:Int = 3;
|
||||
|
||||
private var networkID:Int = OS.getComputerID();
|
||||
private var responseBus: util.EventBus<Package> = new EventBus();
|
||||
private var protoHandlers: Map<String,Package -> Void> = new Map();
|
||||
private var allModems:Array<kernel.peripherals.Modem>;
|
||||
private var routingTable: Map<Int,kernel.peripherals.Modem> = new Map();
|
||||
|
||||
public function init() {
|
||||
KernelEvents.instance.on("modem_message",(params)->{
|
||||
var pack = Package.fromEvent(params);
|
||||
handelIncomming(pack,params[1]);
|
||||
});
|
||||
allModems = Peripheral.instance.getModems();
|
||||
open();
|
||||
discoverNeighbors();
|
||||
}
|
||||
|
||||
private function handelIncomming(pack: Package, ?addr:String) {
|
||||
switch pack.type {
|
||||
case Data(_):
|
||||
routeTo(pack);
|
||||
case DataNoResponse(_):
|
||||
routeTo(pack);
|
||||
case Response | RouteDiscoverResponse:
|
||||
responseBus.emit(Std.string(pack.msgID),pack);
|
||||
case RouteDiscover:
|
||||
handleRoute(pack,addr);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private function newRoutPackage(): Package {
|
||||
var pack: Package = {
|
||||
type: RouteDiscover,
|
||||
toID: BRODCAST_PORT,
|
||||
msgID: generateMessageID(),
|
||||
fromID: networkID,
|
||||
data: null,
|
||||
}
|
||||
|
||||
return pack;
|
||||
}
|
||||
|
||||
private function discoverNeighbors() {
|
||||
for (modem in allModems) {
|
||||
var pack = newRoutPackage();
|
||||
|
||||
var timeout: Timer = null;
|
||||
var responeListner = responseBus.on(Std.string(pack.msgID),pack -> {
|
||||
addRoute(pack.fromID,modem.addr);
|
||||
});
|
||||
|
||||
timeout = new Timer(MESSAGE_TIMEOUT,() -> {
|
||||
responseBus.removeListner(responeListner);
|
||||
});
|
||||
|
||||
modem.transmit(BRODCAST_PORT,OS.getComputerID(),pack);
|
||||
}
|
||||
}
|
||||
|
||||
private function handleRoute(pack: Package, addr: String) {
|
||||
addRoute(pack.fromID,addr);
|
||||
|
||||
// Respond to peer
|
||||
var response: Package = {
|
||||
toID: pack.fromID,
|
||||
fromID: OS.getComputerID(),
|
||||
msgID: pack.msgID,
|
||||
type: RouteDiscoverResponse,
|
||||
data: null
|
||||
}
|
||||
|
||||
for (reponseModem in allModems.filter(m -> m.addr == addr)) {
|
||||
reponseModem.transmit(pack.fromID,networkID,response);
|
||||
}
|
||||
}
|
||||
|
||||
private function addRoute(toID: Int,addr: String) {
|
||||
Log.debug("Added new route to "+toID+" via "+addr);
|
||||
routingTable.set(toID,allModems.find(item -> item.addr == addr));
|
||||
}
|
||||
|
||||
public function getAllNeighbors(): Array<Int> {
|
||||
return routingTable.mapi((index, item) -> index);
|
||||
}
|
||||
|
||||
private function generateMessageID(): Int {
|
||||
return Std.random(2147483647); // TODO: better uniqe number
|
||||
}
|
||||
|
||||
/**
|
||||
Open all wireless and wired modem.
|
||||
**/
|
||||
private function open() {
|
||||
for (m in allModems) {
|
||||
m.open(networkID);
|
||||
m.open(BRODCAST_PORT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Send a message. Dont care if its reaches its destination nor it has a response.
|
||||
**/
|
||||
public function sendAndForget(dest:Int,proto:String,data: Dynamic){
|
||||
|
||||
var pack: Package = {
|
||||
toID: dest,
|
||||
fromID: networkID,
|
||||
msgID: generateMessageID(),
|
||||
type: DataNoResponse(proto),
|
||||
data: data
|
||||
}
|
||||
|
||||
sendRaw(pack);
|
||||
}
|
||||
|
||||
public function respondTo(pack: Package,data: Dynamic) {
|
||||
if (pack.type.match(DataNoResponse(_))){
|
||||
Log.warn("Responed to a no response package. Ignoring");
|
||||
return;
|
||||
}
|
||||
|
||||
var response = pack.createResponse(data);
|
||||
|
||||
sendRaw(response);
|
||||
}
|
||||
|
||||
private function routeTo(pack: Package) {
|
||||
var proto: String = switch pack.type {
|
||||
case Data(proto):
|
||||
proto;
|
||||
case DataNoResponse(proto):
|
||||
proto;
|
||||
case _:
|
||||
return;
|
||||
}
|
||||
|
||||
if (!protoHandlers.exists(proto) && protoHandlers[proto] != null){
|
||||
return;
|
||||
}
|
||||
|
||||
protoHandlers[proto](pack);
|
||||
}
|
||||
|
||||
private function sendRaw(pack: Package){
|
||||
|
||||
if (pack.toID == networkID){
|
||||
// Loopback
|
||||
handelIncomming(pack);
|
||||
}else{
|
||||
if (routingTable.exists(pack.toID)){
|
||||
routingTable[pack.toID].transmit(pack.toID,pack.fromID,pack);
|
||||
}else{
|
||||
// Route not found
|
||||
// TODO: forward package or report not reachable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function sendAndAwait(dest: Int,proto:String,data: Dynamic): Promise<Package> {
|
||||
return new Promise<Package>((resolve, reject) -> {
|
||||
var pack: Package = {
|
||||
toID: dest,
|
||||
fromID: networkID,
|
||||
msgID: generateMessageID(),
|
||||
type: Data(proto),
|
||||
data: data
|
||||
}
|
||||
|
||||
var timeout: Timer = null;
|
||||
var responeListner = responseBus.once(Std.string(pack.msgID),p -> {
|
||||
resolve(p);
|
||||
if (timeout != null){
|
||||
timeout.cancle();
|
||||
}
|
||||
});
|
||||
|
||||
timeout = new Timer(MESSAGE_TIMEOUT,() -> {
|
||||
responseBus.removeListner(responeListner);
|
||||
reject(new Exception("Timeout"));
|
||||
});
|
||||
|
||||
sendRaw(pack);
|
||||
});
|
||||
}
|
||||
|
||||
public function registerProto(proto: String,cb: Package -> Void) {
|
||||
if (protoHandlers.exists(proto)){
|
||||
// Failed. Handler already exist.
|
||||
// TODO: return error
|
||||
return;
|
||||
}
|
||||
|
||||
protoHandlers[proto] = cb;
|
||||
}
|
||||
|
||||
public function removeProto(proto: String) {
|
||||
protoHandlers.remove(proto);
|
||||
}
|
||||
}
|
55
src/kernel/net/Package.hx
Normal file
55
src/kernel/net/Package.hx
Normal file
@ -0,0 +1,55 @@
|
||||
package kernel.net;
|
||||
|
||||
enum PackageTypes {
|
||||
Data(proto: String);
|
||||
DataNoResponse(proto: String);
|
||||
Response;
|
||||
RouteDiscover();
|
||||
RouteDiscoverResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
Representing a network package.
|
||||
**/
|
||||
@:structInit class Package {
|
||||
public final fromID:Int;
|
||||
public final toID:Int;
|
||||
public final msgID:Int;
|
||||
public final type:PackageTypes;
|
||||
public final data:Dynamic;
|
||||
|
||||
/**
|
||||
Parse package from an `modem_message` event.
|
||||
**/
|
||||
public static function fromEvent(params: Array<Dynamic>): Package {
|
||||
var payload = params[4];
|
||||
|
||||
return {
|
||||
fromID: params[3],
|
||||
toID: params[2],
|
||||
msgID: payload.msgID,
|
||||
type: payload.type,
|
||||
data: payload.data,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
Create package that can be used as a response.
|
||||
**/
|
||||
public function createResponse(newData: Dynamic): Package {
|
||||
return {
|
||||
toID: fromID,
|
||||
fromID: toID,
|
||||
msgID: msgID,
|
||||
type: Response,
|
||||
data: newData
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
Wrapper for `Net.instance.respondTo`.
|
||||
**/
|
||||
public function respond(data: Dynamic) {
|
||||
Net.instance.respondTo(this,data);
|
||||
}
|
||||
}
|
31
src/kernel/peripherals/Inventory.hx
Normal file
31
src/kernel/peripherals/Inventory.hx
Normal file
@ -0,0 +1,31 @@
|
||||
package kernel.peripherals;
|
||||
|
||||
class Item {
|
||||
|
||||
}
|
||||
|
||||
class Inventory {
|
||||
|
||||
|
||||
|
||||
public function size():Int {
|
||||
|
||||
}
|
||||
|
||||
public function list(): Map<Int,Item> {
|
||||
|
||||
}
|
||||
|
||||
public function getItemDetail(slot: Int): Item {
|
||||
|
||||
}
|
||||
|
||||
public function pushItems(toName: String, fromSlot: Int, ?limit:Int, toSlot: Int): Int {
|
||||
|
||||
}
|
||||
|
||||
public function pullItems(fromName: String, fromSlot: Int, ?limit:Int, ?toSlot: Int): Int {
|
||||
|
||||
}
|
||||
|
||||
}
|
101
src/kernel/peripherals/Modem.hx
Normal file
101
src/kernel/peripherals/Modem.hx
Normal file
@ -0,0 +1,101 @@
|
||||
package kernel.peripherals;
|
||||
|
||||
import haxe.exceptions.NotImplementedException;
|
||||
import haxe.Exception;
|
||||
|
||||
using lua.Table;
|
||||
|
||||
class Modem {
|
||||
private final nativ:cc.periphs.Modem.Modem;
|
||||
|
||||
public final addr:String;
|
||||
|
||||
@:allow(kernel.peripherals)
|
||||
private function new(nativePeripherals: cc.periphs.Modem.Modem,addr: String) {
|
||||
this.nativ = nativePeripherals;
|
||||
this.addr = addr;
|
||||
}
|
||||
|
||||
public function open(chan: Int) {
|
||||
nativ.open(chan);
|
||||
}
|
||||
|
||||
public function isOpen(chan: Int): Bool {
|
||||
return nativ.isOpen(chan);
|
||||
}
|
||||
|
||||
public function close(chan: Int) {
|
||||
nativ.close(chan);
|
||||
}
|
||||
|
||||
public function closAll() {
|
||||
nativ.closeAll();
|
||||
}
|
||||
|
||||
public function transmit(chan: Int,replyChan: Int,payload: Any) {
|
||||
nativ.transmit(chan,replyChan,payload);
|
||||
}
|
||||
|
||||
public function isWireless(): Bool {
|
||||
return nativ.isWireless();
|
||||
}
|
||||
|
||||
public function getNamesRemote():Array<String> {
|
||||
if (isWireless()){
|
||||
throw new Exception("'getNamesRemote' only works with wired modems");
|
||||
}
|
||||
|
||||
return nativ.getNamesRemote().toArray();
|
||||
}
|
||||
|
||||
public function isPresentRemote(name: String): Bool {
|
||||
if (isWireless()){
|
||||
throw new Exception("'isPresentRemote' only works with wired modems");
|
||||
}
|
||||
|
||||
return nativ.isPresentRemote(name);
|
||||
}
|
||||
|
||||
public function getTypeRemote(name: String): String {
|
||||
if (isWireless()){
|
||||
throw new Exception("'getTypeRemote' only works with wired modems");
|
||||
}
|
||||
|
||||
return nativ.getTypeRemote(name);
|
||||
}
|
||||
|
||||
public function hasTypeRemote(name:String, type:String):Bool {
|
||||
if (isWireless()){
|
||||
throw new Exception("'hasTypeRemote' only works with wired modems");
|
||||
}
|
||||
|
||||
// Missing in upstream API
|
||||
throw new haxe.exceptions.NotImplementedException();
|
||||
// return nativ.hasRemoteType(name,type);
|
||||
}
|
||||
|
||||
public function getMethodsRemote(name: String): Array<String> {
|
||||
if (isWireless()){
|
||||
throw new Exception("'getMethodsRemote' only works with wired modems");
|
||||
}
|
||||
|
||||
return nativ.getMethodsRemote(name).toArray();
|
||||
}
|
||||
|
||||
public function callRemote(remoteName: String, method:String):Dynamic {
|
||||
if (isWireless()){
|
||||
throw new Exception("'callRemote' only works with wired modems");
|
||||
}
|
||||
|
||||
// TODO: implment or solve differently
|
||||
throw new haxe.exceptions.NotImplementedException();
|
||||
}
|
||||
|
||||
public function getNameLocal(): String {
|
||||
if (isWireless()){
|
||||
throw new Exception("'getNameLocal' only works with wired modems");
|
||||
}
|
||||
|
||||
return nativ.getNameLocal();
|
||||
}
|
||||
}
|
57
src/kernel/peripherals/Peripherals.hx
Normal file
57
src/kernel/peripherals/Peripherals.hx
Normal file
@ -0,0 +1,57 @@
|
||||
package kernel.peripherals;
|
||||
|
||||
import kernel.peripherals.Modem;
|
||||
import kernel.peripherals.Screen;
|
||||
|
||||
using lua.Table;
|
||||
using Lambda;
|
||||
|
||||
/**
|
||||
Class responseable for retrieving peripherals.
|
||||
**/
|
||||
class Peripheral {
|
||||
public static final instance:Peripheral = new Peripheral();
|
||||
private function new() {}
|
||||
|
||||
/**
|
||||
Get all connected screens.
|
||||
**/
|
||||
public function getScreens(): Array<Screen> {
|
||||
var allScreens = cc.Peripheral.getNames().toArray().filter(s -> cc.Peripheral.getType(s) == "monitor");
|
||||
return allScreens.map(s -> return new Screen((cc.Peripheral.wrap(s):Dynamic),s));
|
||||
}
|
||||
|
||||
public function getScreen(addr: String): Screen {
|
||||
if (!getAllPeripheralsAddr().exists(item -> item == addr)){
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Screen((cc.Peripheral.wrap(addr):Dynamic),addr);
|
||||
}
|
||||
|
||||
/**
|
||||
Get all connected modems.
|
||||
**/
|
||||
public function getModems(): Array<Modem> {
|
||||
var allModems = cc.Peripheral.getNames().toArray().filter(s -> cc.Peripheral.getType(s) == "modem");
|
||||
return allModems.map(s -> return new Modem((cc.Peripheral.wrap(s): Dynamic),s));
|
||||
}
|
||||
|
||||
/**
|
||||
Get all connected wireless modems.
|
||||
**/
|
||||
public function getWirelessModems(): Array<Modem> {
|
||||
return getModems().filter(modem -> return modem.isWireless());
|
||||
}
|
||||
|
||||
/**
|
||||
Get all connected wired modems.
|
||||
**/
|
||||
public function getWiredModems(): Array<Modem> {
|
||||
return getModems().filter(modem -> return !modem.isWireless());
|
||||
}
|
||||
|
||||
public function getAllPeripheralsAddr(): Array<String> {
|
||||
return cc.Peripheral.getNames().toArray();
|
||||
}
|
||||
}
|
112
src/kernel/peripherals/Screen.hx
Normal file
112
src/kernel/peripherals/Screen.hx
Normal file
@ -0,0 +1,112 @@
|
||||
package kernel.peripherals;
|
||||
|
||||
import util.Signal;
|
||||
import cc.Term.TerminalSize;
|
||||
import lib.TermWriteable;
|
||||
import util.Vec.Vec2;
|
||||
import util.Color;
|
||||
|
||||
class Screen implements TermWriteable{
|
||||
private final nativ:cc.periphs.Monitor.Monitor;
|
||||
private final addr:String;
|
||||
|
||||
|
||||
@:allow(kernel.peripherals)
|
||||
public function new(nativePeripherals: cc.periphs.Monitor.Monitor,addr: String) {
|
||||
this.nativ = nativePeripherals;
|
||||
this.addr = addr;
|
||||
|
||||
KernelEvents.instance.on("monitor_resize",params -> {
|
||||
if (params[1] == this.addr){
|
||||
_onResize.emit(null);
|
||||
}
|
||||
});
|
||||
|
||||
setTextScale(0.5);
|
||||
}
|
||||
|
||||
public function getAddr(): String {
|
||||
return this.addr;
|
||||
}
|
||||
|
||||
public var onResize(get,null):SignalReadonly<Vec2<Int>>;
|
||||
private final _onResize:Signal<Vec2<Int>> = new Signal();
|
||||
|
||||
function get_onResize():SignalReadonly<Vec2<Int>> {
|
||||
return _onResize;
|
||||
}
|
||||
|
||||
public function getTextScale(): Float {
|
||||
return nativ.getTextScale();
|
||||
}
|
||||
|
||||
public function setTextScale(scale: Float) {
|
||||
nativ.setTextScale(scale);
|
||||
}
|
||||
|
||||
public function write(text:String) {
|
||||
nativ.write(text);
|
||||
}
|
||||
|
||||
public function scroll(y:Int) {
|
||||
nativ.scroll(y);
|
||||
}
|
||||
|
||||
public function getCursorPos():Vec2<Int> {
|
||||
var rtn = nativ.getCursorPos();
|
||||
return {
|
||||
x: rtn.x - 1,
|
||||
y: rtn.y - 1
|
||||
}
|
||||
}
|
||||
|
||||
public function setCursorPos(x:Int, y:Int) {
|
||||
nativ.setCursorPos(x + 1,y + 1);
|
||||
}
|
||||
|
||||
public function getCursorBlink():Bool {
|
||||
return nativ.getCursorBlink();
|
||||
}
|
||||
|
||||
public function setCursorBlink(blink:Bool) {
|
||||
nativ.setCursorBlink(blink);
|
||||
}
|
||||
|
||||
public function getSize():Vec2<Int> {
|
||||
// FIXME: this will not compile. Has to be changes upstream
|
||||
var size:TerminalSize = nativ.getSize();
|
||||
return {
|
||||
x: size.width,
|
||||
y: size.height,
|
||||
}
|
||||
}
|
||||
|
||||
public function clear() {
|
||||
nativ.clear();
|
||||
}
|
||||
|
||||
public function clearLine() {
|
||||
nativ.clearLine();
|
||||
}
|
||||
|
||||
public function getTextColor():Color {
|
||||
return ColorConvert.ccToColor(nativ.getTextColor());
|
||||
}
|
||||
|
||||
public function setTextColor(colour:Color) {
|
||||
nativ.setTextColor(ColorConvert.colorToCC(colour));
|
||||
}
|
||||
|
||||
public function getBackgroundColor():Color {
|
||||
return ColorConvert.ccToColor(nativ.getBackgroundColor());
|
||||
}
|
||||
|
||||
public function setBackgroundColor(color:Color) {
|
||||
nativ.setBackgroundColor(ColorConvert.colorToCC(color));
|
||||
}
|
||||
|
||||
public function isColor():Bool {
|
||||
return nativ.isColor();
|
||||
}
|
||||
}
|
||||
|
174
src/kernel/ui/TermBuffer.hx
Normal file
174
src/kernel/ui/TermBuffer.hx
Normal file
@ -0,0 +1,174 @@
|
||||
package kernel.ui;
|
||||
|
||||
import util.Signal;
|
||||
import util.Vec.Vec2;
|
||||
import util.Color;
|
||||
import lib.TermWriteable;
|
||||
|
||||
@:structInit class Pixel {
|
||||
public var char:String;
|
||||
public var bg:Color;
|
||||
public var textColor:Color;
|
||||
}
|
||||
|
||||
class TermBuffer implements TermWriteable {
|
||||
|
||||
/**
|
||||
format [y][x]. First index is the line. Second index the char in the line.
|
||||
**/
|
||||
private var screenBuffer: Array<Array<Pixel>>;
|
||||
private var cursorPos: Vec2<Int> = {x: 0, y: 0};
|
||||
private var currentTextColor: Color = White;
|
||||
private var currentBgColor: Color = Black;
|
||||
private var size: Vec2<Int> = {x: 51,y:19}; // Default size set to default size of the main terminal
|
||||
|
||||
public function new() {
|
||||
initScreenBuffer(size);
|
||||
}
|
||||
|
||||
private function setSize(size: Vec2<Int>) {
|
||||
if (this.size != size){
|
||||
this._onResize.emit(size);
|
||||
}
|
||||
|
||||
this.size = size;
|
||||
updateScreenBufferSize(size);
|
||||
}
|
||||
|
||||
private function updateScreenBufferSize(size: Vec2<Int>) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
private function initScreenBuffer(size: Vec2<Int>) {
|
||||
screenBuffer = new Array();
|
||||
for (y in 0...size.y){
|
||||
screenBuffer[y] = new Array();
|
||||
for (x in 0...size.x){
|
||||
screenBuffer[y][x] = {
|
||||
char: " ",
|
||||
textColor: White,
|
||||
bg: Black,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function copyBufferToTarget(target: TermWriteable) {
|
||||
target.setCursorPos(0,0);
|
||||
target.setBackgroundColor(Black);
|
||||
target.setTextColor(White);
|
||||
|
||||
var tmpFgColor: Color = White;
|
||||
var tmpBgColor: Color = Black;
|
||||
|
||||
for (y => line in screenBuffer){
|
||||
for(x => pixel in line){
|
||||
if (tmpFgColor != pixel.textColor){
|
||||
tmpFgColor = pixel.textColor;
|
||||
target.setTextColor(pixel.textColor);
|
||||
}
|
||||
|
||||
if (tmpBgColor != pixel.bg){
|
||||
tmpBgColor = pixel.bg;
|
||||
target.setBackgroundColor(pixel.bg);
|
||||
}
|
||||
|
||||
target.setCursorPos(x,y);
|
||||
target.write(pixel.char);
|
||||
}
|
||||
}
|
||||
|
||||
target.setCursorPos(cursorPos.x,cursorPos.y);
|
||||
target.setTextColor(currentTextColor);
|
||||
target.setBackgroundColor(currentBgColor);
|
||||
}
|
||||
|
||||
private function safeWriteScreenBuffer(pos: Vec2<Int>,char: String) {
|
||||
if (screenBuffer.length > pos.y && screenBuffer[pos.y].length > pos.x){
|
||||
screenBuffer[pos.y][pos.x].char = char;
|
||||
screenBuffer[pos.y][pos.x].bg = currentBgColor;
|
||||
screenBuffer[pos.y][pos.x].textColor = currentTextColor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// TermWriteable functions
|
||||
//
|
||||
|
||||
public var onResize(get,null):SignalReadonly<Vec2<Int>>;
|
||||
private final _onResize:Signal<Vec2<Int>> = new Signal();
|
||||
|
||||
function get_onResize():Signal<Vec2<Int>> {
|
||||
return _onResize;
|
||||
}
|
||||
|
||||
public function write(text:String) {
|
||||
for (i in 0...text.length){
|
||||
safeWriteScreenBuffer({x: cursorPos.x,y: cursorPos.y},text.charAt(i));
|
||||
cursorPos = {y: cursorPos.y,x: cursorPos.x + 1};
|
||||
}
|
||||
}
|
||||
|
||||
public function scroll(y:Int) {
|
||||
screenBuffer.unshift([for (i in 0...size.x) {
|
||||
char: " ",
|
||||
textColor: White, // TODO: maybe replace with current bg/text color. Check nativ implementation
|
||||
bg: Black
|
||||
}]);
|
||||
}
|
||||
|
||||
public function getCursorPos():Vec2<Int> {
|
||||
return cursorPos;
|
||||
}
|
||||
|
||||
public function setCursorPos(x:Int, y:Int) {
|
||||
cursorPos = {
|
||||
x: x,
|
||||
y: y,
|
||||
};
|
||||
}
|
||||
|
||||
public function getCursorBlink():Bool {
|
||||
throw new haxe.exceptions.NotImplementedException();
|
||||
}
|
||||
|
||||
public function setCursorBlink(blink:Bool) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
public function getSize():Vec2<Int> {
|
||||
return size;
|
||||
}
|
||||
|
||||
public function clear() {
|
||||
initScreenBuffer(size);
|
||||
}
|
||||
|
||||
public function clearLine() {
|
||||
if (screenBuffer.length > cursorPos.y){
|
||||
screenBuffer[cursorPos.y] = [for(x in 0...size.x){textColor: White,char: " ",bg: Black}];
|
||||
}
|
||||
}
|
||||
|
||||
public function getTextColor():Color {
|
||||
return currentTextColor;
|
||||
}
|
||||
|
||||
public function setTextColor(colour:Color) {
|
||||
currentTextColor = colour;
|
||||
}
|
||||
|
||||
public function getBackgroundColor():Color {
|
||||
return currentBgColor;
|
||||
}
|
||||
|
||||
public function setBackgroundColor(color:Color) {
|
||||
currentBgColor = color;
|
||||
}
|
||||
|
||||
public function isColor():Bool {
|
||||
throw new haxe.exceptions.NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
164
src/kernel/ui/VirtualTermWriter.hx
Normal file
164
src/kernel/ui/VirtualTermWriter.hx
Normal file
@ -0,0 +1,164 @@
|
||||
package kernel.ui;
|
||||
|
||||
import util.Signal.SignalListner;
|
||||
import util.Vec.Vec2;
|
||||
import util.Color;
|
||||
import lib.TermWriteable;
|
||||
|
||||
class VirtualTermWriter implements TermWriteable extends TermBuffer {
|
||||
private static final defaultSize:Vec2<Int> = {x: 50,y: 50};
|
||||
|
||||
private var target: TermWriteable;
|
||||
private var enabled:Bool = false;
|
||||
private var onResizeListner: SignalListner<Vec2<Int>>;
|
||||
|
||||
public function new(?target: TermWriteable) {
|
||||
setTarget(target);
|
||||
|
||||
if (enabled){
|
||||
enable();
|
||||
}
|
||||
|
||||
super();
|
||||
}
|
||||
|
||||
public function enable() {
|
||||
if (target != null){
|
||||
enabled = true;
|
||||
super.copyBufferToTarget(target);
|
||||
}
|
||||
}
|
||||
|
||||
public function disable() {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
public inline function isEnabled(): Bool {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public function setTarget(newTarget: TermWriteable) {
|
||||
if (newTarget != null){
|
||||
super.setSize(newTarget.getSize());
|
||||
|
||||
// Remove old target event listner
|
||||
if (onResizeListner != null && target != null){
|
||||
target.onResize.remove(onResizeListner);
|
||||
}
|
||||
|
||||
// Add new target event listner
|
||||
onResizeListner = newTarget.onResize.on(newSize -> {
|
||||
setSuperSize(newSize);
|
||||
});
|
||||
|
||||
target = newTarget;
|
||||
}
|
||||
}
|
||||
|
||||
private function setSuperSize(size: Vec2<Int>) {
|
||||
super.setSize(target.getSize());
|
||||
}
|
||||
|
||||
//
|
||||
// TermWriteable functions.
|
||||
//
|
||||
public override function write(text:String) {
|
||||
if (isEnabled()){
|
||||
target.write(text);
|
||||
}
|
||||
|
||||
super.write(text);
|
||||
}
|
||||
|
||||
public override function scroll(y:Int) {
|
||||
if (isEnabled()){
|
||||
target.scroll(y);
|
||||
}
|
||||
super.scroll(y);
|
||||
}
|
||||
|
||||
public override function getCursorPos():Vec2<Int> {
|
||||
if (isEnabled()){
|
||||
return target.getCursorPos();
|
||||
}else{
|
||||
return super.getCursorPos();
|
||||
}
|
||||
}
|
||||
|
||||
public override function setCursorPos(x:Int, y:Int) {
|
||||
if (isEnabled()){
|
||||
target.setCursorPos(x,y);
|
||||
}
|
||||
|
||||
super.setCursorPos(x,y);
|
||||
}
|
||||
|
||||
public override function getCursorBlink():Bool {
|
||||
throw new haxe.exceptions.NotImplementedException();
|
||||
}
|
||||
|
||||
public override function setCursorBlink(blink:Bool) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
public override function getSize():Vec2<Int> {
|
||||
// TODO: make sense ?
|
||||
if (target != null){
|
||||
return target.getSize();
|
||||
}
|
||||
|
||||
return defaultSize;
|
||||
}
|
||||
|
||||
public override function clear() {
|
||||
if (isEnabled()){
|
||||
target.clear();
|
||||
}
|
||||
|
||||
super.clear();
|
||||
}
|
||||
|
||||
public override function clearLine() {
|
||||
if (isEnabled()){
|
||||
target.clearLine();
|
||||
}
|
||||
|
||||
super.clearLine();
|
||||
}
|
||||
|
||||
public override function getTextColor():Color {
|
||||
if (isEnabled()){
|
||||
return target.getTextColor();
|
||||
}
|
||||
|
||||
return super.getTextColor();
|
||||
}
|
||||
|
||||
public override function setTextColor(colour:Color) {
|
||||
if (isEnabled()){
|
||||
target.setTextColor(colour);
|
||||
}
|
||||
|
||||
super.setTextColor(colour);
|
||||
}
|
||||
|
||||
public override function getBackgroundColor():Color {
|
||||
if (isEnabled()){
|
||||
return target.getBackgroundColor();
|
||||
}
|
||||
|
||||
return super.getBackgroundColor();
|
||||
}
|
||||
|
||||
public override function setBackgroundColor(color:Color) {
|
||||
if (isEnabled()){
|
||||
target.setBackgroundColor(color);
|
||||
}
|
||||
|
||||
super.setBackgroundColor(color);
|
||||
}
|
||||
|
||||
public override function isColor():Bool {
|
||||
throw new haxe.exceptions.NotImplementedException();
|
||||
}
|
||||
}
|
176
src/kernel/ui/WindowContext.hx
Normal file
176
src/kernel/ui/WindowContext.hx
Normal file
@ -0,0 +1,176 @@
|
||||
package kernel.ui;
|
||||
|
||||
import util.Color;
|
||||
import util.Signal;
|
||||
import kernel.ui.WindowManager.ButtonType;
|
||||
import util.Vec.Vec2;
|
||||
import lib.TermWriteable;
|
||||
|
||||
class WindowContext implements TermWriteable {
|
||||
private final writer:VirtualTermWriter;
|
||||
|
||||
private final _clickSignal:Signal<{button: ButtonType, pos: Vec2<Int>}> = new Signal();
|
||||
private final _keySignal:Signal<{keyCode: Int, isHeld: Bool}> = new Signal();
|
||||
private final _keyUpSignal:Signal<Int> = new Signal();
|
||||
private final _mouseDragSignal:Signal<{button: ButtonType, pos: Vec2<Int>}> = new Signal();
|
||||
private final _mouseScrollSignal:Signal<{dir: Int,pos: Vec2<Int>}> = new Signal();
|
||||
private final _mouseUpSignal:Signal<{button: ButtonType,pos: Vec2<Int>}> = new Signal();
|
||||
private final _pasteSignal:Signal<String> = new Signal();
|
||||
|
||||
public var clickSignal(get,null):SignalReadonly<{button: ButtonType, pos: Vec2<Int>}>;
|
||||
public var keySignal(get,null):SignalReadonly<{keyCode: Int, isHeld: Bool}>;
|
||||
public var keyUpSignal(get,null):SignalReadonly<Int>;
|
||||
public var mouseDragSignal(get,null):SignalReadonly<{button: ButtonType, pos: Vec2<Int>}>;
|
||||
public var mouseScrollSignal(get,null):SignalReadonly<{dir: Int,pos: Vec2<Int>}>;
|
||||
public var mouseUpSignal(get,null):SignalReadonly<{button: ButtonType,pos: Vec2<Int>}>;
|
||||
public var pasteSignal(get,null):SignalReadonly<String> ;
|
||||
|
||||
public function new(writer: VirtualTermWriter) {
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
public var onResize(get, null):SignalReadonly<Vec2<Int>>;
|
||||
|
||||
function get_onResize():SignalReadonly<Vec2<Int>> {
|
||||
return writer.onResize;
|
||||
}
|
||||
|
||||
@:allow(kernel.ui)
|
||||
private function setTarget(target: TermWriteable) {
|
||||
writer.setTarget(target);
|
||||
}
|
||||
|
||||
@:allow(kernel.ui)
|
||||
private function enable() {
|
||||
writer.enable();
|
||||
}
|
||||
|
||||
@:allow(kernel.ui)
|
||||
private function disable() {
|
||||
writer.disable();
|
||||
}
|
||||
|
||||
@:allow(kernel.ui)
|
||||
private function isEnabled() {
|
||||
return writer.isEnabled();
|
||||
}
|
||||
|
||||
public function get_clickSignal(){
|
||||
return _clickSignal;
|
||||
}
|
||||
|
||||
public function get_keySignal(){
|
||||
return _keySignal;
|
||||
}
|
||||
|
||||
public function get_keyUpSignal(){
|
||||
return _keyUpSignal;
|
||||
}
|
||||
|
||||
public function get_mouseDragSignal(){
|
||||
return _mouseDragSignal;
|
||||
}
|
||||
|
||||
public function get_mouseScrollSignal(){
|
||||
return _mouseScrollSignal;
|
||||
}
|
||||
|
||||
public function get_mouseUpSignal(){
|
||||
return _mouseUpSignal;
|
||||
}
|
||||
|
||||
public function get_pasteSignal(){
|
||||
return _pasteSignal;
|
||||
}
|
||||
|
||||
@:allow(kernel.ui.WindowManager) // Package private
|
||||
private function click(button:ButtonType ,pos: Vec2<Int>) {
|
||||
_clickSignal.emit({button: button,pos: pos});
|
||||
}
|
||||
|
||||
@:allow(kernel.ui.WindowManager) // Package private
|
||||
private function key(keyCode: Int, isHeld: Bool) {
|
||||
_keySignal.emit({keyCode: keyCode,isHeld: isHeld});
|
||||
}
|
||||
|
||||
@:allow(kernel.ui.WindowManager) // Package private
|
||||
private function keyUp(keyCode: Int) {
|
||||
_keyUpSignal.emit(keyCode);
|
||||
}
|
||||
|
||||
@:allow(kernel.ui.WindowManager) // Package private
|
||||
private function mouseDrag(button: ButtonType, pos: Vec2<Int>) {
|
||||
_mouseDragSignal.emit({button: button,pos: pos});
|
||||
}
|
||||
|
||||
@:allow(kernel.ui.WindowManager) // Package private
|
||||
private function mouseScroll(dir: Int,pos: Vec2<Int>) {
|
||||
_mouseScrollSignal.emit({dir: dir,pos: pos});
|
||||
}
|
||||
|
||||
@:allow(kernel.ui.WindowManager) // Package private
|
||||
private function mouseUp(button: ButtonType,pos: Vec2<Int>) {
|
||||
_mouseUpSignal.emit({button: button,pos: pos});
|
||||
}
|
||||
|
||||
@:allow(kernel.ui.WindowManager) // Package private
|
||||
private function paste(text: String) {
|
||||
_pasteSignal.emit(text);
|
||||
}
|
||||
|
||||
public function write(text:String) {
|
||||
writer.write(text);
|
||||
}
|
||||
|
||||
public function scroll(y:Int) {
|
||||
writer.scroll(y);
|
||||
}
|
||||
|
||||
public function getCursorPos():Vec2<Int> {
|
||||
return writer.getCursorPos();
|
||||
}
|
||||
|
||||
public function setCursorPos(x:Int, y:Int) {
|
||||
writer.setCursorPos(x,y);
|
||||
}
|
||||
|
||||
public function getCursorBlink():Bool {
|
||||
return writer.getCursorBlink();
|
||||
}
|
||||
|
||||
public function setCursorBlink(blink:Bool) {
|
||||
writer.setCursorBlink(blink);
|
||||
}
|
||||
|
||||
public function getSize():Vec2<Int> {
|
||||
return writer.getSize();
|
||||
}
|
||||
|
||||
public function clear() {
|
||||
writer.clear();
|
||||
}
|
||||
|
||||
public function clearLine() {
|
||||
writer.clearLine();
|
||||
}
|
||||
|
||||
public function getTextColor():Color {
|
||||
return writer.getTextColor();
|
||||
}
|
||||
|
||||
public function setTextColor(colour:Color) {
|
||||
writer.setTextColor(colour);
|
||||
}
|
||||
|
||||
public function getBackgroundColor():Color {
|
||||
return writer.getBackgroundColor();
|
||||
}
|
||||
|
||||
public function setBackgroundColor(color:Color) {
|
||||
writer.setBackgroundColor(color);
|
||||
}
|
||||
|
||||
public function isColor():Bool {
|
||||
return writer.isColor();
|
||||
}
|
||||
}
|
155
src/kernel/ui/WindowManager.hx
Normal file
155
src/kernel/ui/WindowManager.hx
Normal file
@ -0,0 +1,155 @@
|
||||
package kernel.ui;
|
||||
|
||||
import lib.TermWriteable;
|
||||
import kernel.peripherals.Screen;
|
||||
import kernel.peripherals.Peripherals.Peripheral;
|
||||
import haxe.Exception;
|
||||
import util.Vec.Vec2;
|
||||
|
||||
enum ButtonType {
|
||||
Left;
|
||||
Middle;
|
||||
Right;
|
||||
}
|
||||
|
||||
class WindowManager {
|
||||
public static final instance:WindowManager = new WindowManager();
|
||||
private function new() {}
|
||||
|
||||
private var currentMainContext:WindowContext;
|
||||
private final allContexts:Array<WindowContext> = new Array();
|
||||
private final outputMap:Map<String,WindowContext> = new Map();
|
||||
|
||||
public function init() {
|
||||
KernelEvents.instance.on("key",params -> {
|
||||
var keyCode: Int = params[1];
|
||||
var isHeld: Bool = params[2];
|
||||
if (currentMainContext != null){
|
||||
currentMainContext.key(keyCode,isHeld);
|
||||
}
|
||||
});
|
||||
|
||||
KernelEvents.instance.on("key_up",params -> {
|
||||
var keyCode: Int = params[1];
|
||||
if (currentMainContext != null){
|
||||
currentMainContext.keyUp(keyCode);
|
||||
}
|
||||
});
|
||||
|
||||
KernelEvents.instance.on("mouse_click",params -> {
|
||||
var button: ButtonType = ccButtonToEnum(params[1]);
|
||||
var clickPos: Vec2<Int> = {
|
||||
x: (params[2]:Int) - 1,
|
||||
y: (params[3]:Int) - 1
|
||||
};
|
||||
|
||||
if (currentMainContext != null){
|
||||
currentMainContext.click(button,clickPos);
|
||||
}
|
||||
});
|
||||
|
||||
KernelEvents.instance.on("mouse_drag",params -> {
|
||||
var button: ButtonType = ccButtonToEnum(params[1]);
|
||||
var pos: Vec2<Int> = {
|
||||
x: (params[2]:Int) - 1,
|
||||
y: (params[3]:Int) - 1,
|
||||
}
|
||||
if (currentMainContext != null){
|
||||
currentMainContext.mouseDrag(button,pos);
|
||||
}
|
||||
});
|
||||
|
||||
KernelEvents.instance.on("mouse_scroll",params -> {
|
||||
var dir: Int = params[1];
|
||||
var pos: Vec2<Int> = {
|
||||
x: (params[2]:Int) - 1,
|
||||
y: (params[3]:Int) - 1,
|
||||
}
|
||||
|
||||
if (currentMainContext != null){
|
||||
currentMainContext.mouseScroll(dir,pos);
|
||||
}
|
||||
});
|
||||
|
||||
KernelEvents.instance.on("mouse_up",params -> {
|
||||
var button: ButtonType = ccButtonToEnum(params[1]);
|
||||
var pos: Vec2<Int> = {
|
||||
x: (params[2]:Int) - 1,
|
||||
y: (params[2]:Int) - 1,
|
||||
}
|
||||
|
||||
if (currentMainContext != null){
|
||||
currentMainContext.mouseUp(button,pos);
|
||||
}
|
||||
});
|
||||
|
||||
KernelEvents.instance.on("paste",params -> {
|
||||
var text: String = params[1];
|
||||
|
||||
if (currentMainContext != null){
|
||||
currentMainContext.paste(text);
|
||||
}
|
||||
});
|
||||
|
||||
KernelEvents.instance.on("monitor_touch",array -> {
|
||||
// TODO
|
||||
});
|
||||
}
|
||||
|
||||
public function createNewContext(): WindowContext {
|
||||
var newContext = new WindowContext(new VirtualTermWriter());
|
||||
|
||||
allContexts.push(newContext);
|
||||
|
||||
newContext.setTarget(MainTerm.instance);
|
||||
newContext.enable();
|
||||
currentMainContext = newContext;
|
||||
|
||||
return newContext;
|
||||
}
|
||||
|
||||
|
||||
public function getOutputs(): Array<String> {
|
||||
var arr = Peripheral.instance.getScreens().map(screen -> return screen.getAddr());
|
||||
arr.push("main");
|
||||
return arr;
|
||||
}
|
||||
|
||||
public function focusContextToOutput(context: WindowContext,output: String) {
|
||||
var target: TermWriteable;
|
||||
if (output == "main"){
|
||||
target = MainTerm.instance;
|
||||
}else{
|
||||
target = Peripheral.instance.getScreen(output);
|
||||
|
||||
if (target == null){
|
||||
// output target not found
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (outputMap.exists(output)){
|
||||
outputMap[output].disable();
|
||||
}
|
||||
|
||||
|
||||
outputMap[output] = context;
|
||||
context.setTarget(target);
|
||||
context.enable();
|
||||
}
|
||||
|
||||
private static function ccButtonToEnum(button: Int): ButtonType {
|
||||
switch button {
|
||||
case 1:
|
||||
return Left;
|
||||
case 2:
|
||||
return Middle;
|
||||
case 3:
|
||||
return Right;
|
||||
case _:
|
||||
throw new Exception("Invalid input");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
5
src/lib/IUserProgramm.hx
Normal file
5
src/lib/IUserProgramm.hx
Normal file
@ -0,0 +1,5 @@
|
||||
package lib;
|
||||
|
||||
interface IUserProgramm {
|
||||
public function getName(): String;
|
||||
}
|
48
src/lib/TermIO.hx
Normal file
48
src/lib/TermIO.hx
Normal file
@ -0,0 +1,48 @@
|
||||
package lib;
|
||||
|
||||
import util.Vec.Vec2;
|
||||
import util.Color;
|
||||
import lib.TermWriteable;
|
||||
|
||||
/**
|
||||
Helpfull class for writing onto a `TermWriteable`.
|
||||
**/
|
||||
class TermIO {
|
||||
private var output: TermWriteable;
|
||||
|
||||
public function new(output: TermWriteable) {
|
||||
this.output = output;
|
||||
|
||||
output.clear();
|
||||
output.setCursorPos(0,0);
|
||||
}
|
||||
|
||||
public function writeLn(text: String,?textColor: Color){
|
||||
if (textColor != null){
|
||||
output.setTextColor(textColor);
|
||||
}
|
||||
|
||||
var size = output.getSize();
|
||||
|
||||
for (i in 0...Math.floor(text.length / size.x) + 1){
|
||||
output.write(text.substr(i * size.x,size.x));
|
||||
newLine();
|
||||
}
|
||||
|
||||
if (textColor != null){
|
||||
output.setTextColor(White);
|
||||
}
|
||||
}
|
||||
|
||||
private function newLine() {
|
||||
var cPos = output.getCursorPos();
|
||||
|
||||
if (cPos.y == output.getSize().y){
|
||||
output.scroll(1);
|
||||
output.setCursorPos(0,cPos.y);
|
||||
}else{
|
||||
output.setCursorPos(0,cPos.y + 1);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
38
src/lib/TermWriteable.hx
Normal file
38
src/lib/TermWriteable.hx
Normal file
@ -0,0 +1,38 @@
|
||||
package lib;
|
||||
|
||||
import util.Signal;
|
||||
import util.Color;
|
||||
import util.Vec.Vec2;
|
||||
|
||||
/**
|
||||
Interface describing a terminal. E.g. the main computer screen or a external screen.
|
||||
**/
|
||||
interface TermWriteable {
|
||||
|
||||
public var onResize(get,null): SignalReadonly<Vec2<Int>>;
|
||||
|
||||
public function write(text: String): Void;
|
||||
public function scroll(y: Int): Void;
|
||||
|
||||
/**
|
||||
Even though CC is 1 based we use a 0 based index.
|
||||
**/
|
||||
public function getCursorPos(): Vec2<Int>;
|
||||
|
||||
/**
|
||||
Even though CC is 1 based we use a 0 based index.
|
||||
**/
|
||||
public function setCursorPos(x: Int, y: Int):Void;
|
||||
public function getCursorBlink(): Bool;
|
||||
public function setCursorBlink(blink: Bool):Void;
|
||||
public function getSize(): Vec2<Int>;
|
||||
public function clear(): Void;
|
||||
public function clearLine(): Void;
|
||||
public function getTextColor():Color;
|
||||
public function setTextColor(colour: Color):Void;
|
||||
public function getBackgroundColor(): Color;
|
||||
public function setBackgroundColor(color: Color):Void;
|
||||
public function isColor(): Bool;
|
||||
// setPaletteColor(...)
|
||||
// getPaletteColor(colour)
|
||||
}
|
74
src/lib/ui/Canvas.hx
Normal file
74
src/lib/ui/Canvas.hx
Normal file
@ -0,0 +1,74 @@
|
||||
package lib.ui;
|
||||
|
||||
import util.Vec.Vec2;
|
||||
import kernel.ui.TermBuffer.Pixel;
|
||||
|
||||
abstract Canvas(Array<Array<Pixel>>) to Array<Array<Pixel>> {
|
||||
inline public function new() {
|
||||
this = [[]];
|
||||
}
|
||||
|
||||
public inline function set(i:Vec2<Int>,pixel:Pixel) {
|
||||
if (this[i.y] == null){
|
||||
this[i.y] = [];
|
||||
}
|
||||
|
||||
this[i.y][i.x] = pixel;
|
||||
}
|
||||
|
||||
public inline function get(i:Vec2<Int>): Pixel {
|
||||
return this[i.y][i.x];
|
||||
}
|
||||
|
||||
public function keyValueIterator(): KeyValueIterator<Vec2<Int>,Pixel>{
|
||||
return new CanvasKeyValueIterator(this);
|
||||
}
|
||||
|
||||
public function combine(other: Canvas,offset: Vec2<Int>) {
|
||||
for (key => value in other) {
|
||||
if (value == null){
|
||||
continue;
|
||||
}
|
||||
|
||||
var y = offset.y + key.y;
|
||||
var x = offset.x + key.x;
|
||||
|
||||
if (this[y] == null){
|
||||
this[y] = [];
|
||||
}
|
||||
|
||||
this[y][x] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CanvasKeyValueIterator{
|
||||
private final canvas:Array<Array<Pixel>>;
|
||||
private var index:Vec2<Int> = {x: 0,y: 0};
|
||||
|
||||
@:allow(lib.ui.Canvas)
|
||||
private function new(canvas: Array<Array<Pixel>>) {
|
||||
this.canvas = canvas;
|
||||
}
|
||||
|
||||
public function hasNext():Bool{
|
||||
return index.y < canvas.length && index.x < canvas[index.y].length;
|
||||
}
|
||||
|
||||
public function next():{key:Vec2<Int>, value:Pixel}{
|
||||
var oldIndex: Vec2<Int> = this.index;
|
||||
|
||||
if (index.x >= canvas[index.y].length){
|
||||
// Goto next line
|
||||
index = {x:0,y: index.y + 1};
|
||||
}else{
|
||||
// Goto next pixel in line
|
||||
index = {x:index.x + 1,y: index.y};
|
||||
}
|
||||
|
||||
return {
|
||||
key: oldIndex,
|
||||
value: this.canvas[oldIndex.y][oldIndex.x]
|
||||
};
|
||||
}
|
||||
}
|
7
src/lib/ui/IElement.hx
Normal file
7
src/lib/ui/IElement.hx
Normal file
@ -0,0 +1,7 @@
|
||||
package lib.ui;
|
||||
|
||||
import util.Vec.Vec2;
|
||||
|
||||
interface IElement {
|
||||
public function render(bounds: Vec2<Int>): Canvas;
|
||||
}
|
80
src/lib/ui/ReactiveUI.hx
Normal file
80
src/lib/ui/ReactiveUI.hx
Normal file
@ -0,0 +1,80 @@
|
||||
package lib.ui;
|
||||
|
||||
import kernel.Log;
|
||||
import util.Color;
|
||||
import util.Vec.Vec2;
|
||||
import kernel.ui.WindowContext;
|
||||
|
||||
class ReactiveUI {
|
||||
private final context:WindowContext;
|
||||
|
||||
public function new(context: WindowContext) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public function render(children: Array<IElement>) {
|
||||
var size = context.getSize();
|
||||
|
||||
var screen = renderChildren(children,size);
|
||||
|
||||
writeToContext(screen);
|
||||
}
|
||||
|
||||
private function writeToContext(screen: Canvas) {
|
||||
var currentBg: Color = Black;
|
||||
var currentFg: Color = White;
|
||||
|
||||
var currentLine = 0;
|
||||
|
||||
context.setBackgroundColor(currentBg);
|
||||
context.setTextColor(currentFg);
|
||||
context.setCursorPos(0,0);
|
||||
|
||||
for (key => pixel in screen) {
|
||||
|
||||
if (key.y != currentLine){
|
||||
currentLine = key.y;
|
||||
context.setCursorPos(key.x,key.y);
|
||||
}
|
||||
|
||||
if (pixel == null){
|
||||
context.write(' ');
|
||||
}else{
|
||||
if (pixel.bg != currentBg){
|
||||
context.setBackgroundColor(pixel.bg);
|
||||
currentBg = pixel.bg;
|
||||
}
|
||||
|
||||
if (pixel.textColor != currentFg){
|
||||
context.setTextColor(pixel.textColor);
|
||||
currentFg = pixel.textColor;
|
||||
}
|
||||
|
||||
context.write(pixel.char);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function renderChildren(children: Array<IElement>,bounds: Vec2<Int>): Canvas {
|
||||
var rtn: Canvas = new Canvas();
|
||||
|
||||
var writePoint: Vec2<Int> = {x: 0,y: 0};
|
||||
|
||||
for (child in children) {
|
||||
if (bounds.y - writePoint.y <= 0){
|
||||
// No more space to render children
|
||||
Log.debug("No more space");
|
||||
break;
|
||||
}
|
||||
|
||||
var childRender = child.render({
|
||||
x: bounds.x,
|
||||
y: bounds.y - writePoint.y
|
||||
});
|
||||
|
||||
rtn.combine(childRender,writePoint);
|
||||
}
|
||||
|
||||
return rtn;
|
||||
}
|
||||
}
|
31
src/lib/ui/TextElement.hx
Normal file
31
src/lib/ui/TextElement.hx
Normal file
@ -0,0 +1,31 @@
|
||||
package lib.ui;
|
||||
|
||||
import kernel.Log;
|
||||
import util.Color;
|
||||
import util.Vec.Vec2;
|
||||
import util.MathI;
|
||||
|
||||
class TextElement implements IElement {
|
||||
private final text:String;
|
||||
private final bg:Color;
|
||||
private final fg:Color;
|
||||
|
||||
public function new(text: String,?background: Color = Black,?textColor: Color = White) {
|
||||
this.text = text;
|
||||
this.bg = background;
|
||||
this.fg = textColor;
|
||||
}
|
||||
|
||||
public function render(bounds: Vec2<Int>):Canvas {
|
||||
var rtn = new Canvas();
|
||||
|
||||
for (i in 0...MathI.min(Math.floor(text.length / bounds.x) + 1,bounds.y)){
|
||||
var line = (text.substr(i * bounds.x,bounds.x));
|
||||
for (char in 0...line.length) {
|
||||
rtn.set({x: char,y: i},{textColor: fg,char: line.charAt(char),bg: bg});
|
||||
}
|
||||
}
|
||||
|
||||
return rtn;
|
||||
}
|
||||
}
|
18
src/lib/ui/VSplitLayout.hx
Normal file
18
src/lib/ui/VSplitLayout.hx
Normal file
@ -0,0 +1,18 @@
|
||||
package lib.ui;
|
||||
|
||||
import util.Vec.Vec2;
|
||||
import kernel.ui.TermBuffer.Pixel;
|
||||
|
||||
class VSplitLayout implements IElement{
|
||||
|
||||
public function new(childrenLeft: Array<IElement>,childrenRight: Array<IElement>) {
|
||||
|
||||
}
|
||||
|
||||
public function render(bounds:Vec2<Int>):Canvas {
|
||||
var boundsLeft: Vec2<Int> = { x: Math.ceil(bounds.x / 2), y: bounds.y};
|
||||
var boundsRight: Vec2<Int> = { x: Math.floor(bounds.x / 2), y: bounds.y};
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
43
src/util/BuildInfo.hx
Normal file
43
src/util/BuildInfo.hx
Normal file
@ -0,0 +1,43 @@
|
||||
package util;
|
||||
|
||||
/**
|
||||
Macros with static information.
|
||||
**/
|
||||
class BuildInfo {
|
||||
/**
|
||||
Get the latest git commit.
|
||||
**/
|
||||
public static macro function getGitCommitHash():haxe.macro.Expr.ExprOf<String> {
|
||||
#if !display
|
||||
var process = new sys.io.Process('git', ['rev-parse', 'HEAD']);
|
||||
if (process.exitCode() != 0) {
|
||||
var message = process.stderr.readAll().toString();
|
||||
var pos = haxe.macro.Context.currentPos();
|
||||
haxe.macro.Context.error("Cannot execute `git rev-parse HEAD`. " + message, pos);
|
||||
}
|
||||
|
||||
// read the output of the process
|
||||
var commitHash:String = process.stdout.readLine();
|
||||
|
||||
// Generates a string expression
|
||||
return macro $v{commitHash};
|
||||
#else
|
||||
// `#if display` is used for code completion. In this case returning an
|
||||
// empty string is good enough; We don't want to call git on every hint.
|
||||
var commitHash:String = "";
|
||||
return macro $v{commitHash};
|
||||
#end
|
||||
}
|
||||
|
||||
/**
|
||||
Get the time the file was build.
|
||||
**/
|
||||
public static macro function buildTime(): haxe.macro.Expr.ExprOf<Int> {
|
||||
#if !display
|
||||
return macro $v{Math.floor(Date.now().getTime())};
|
||||
#else
|
||||
return macro $v{0};
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
111
src/util/Color.hx
Normal file
111
src/util/Color.hx
Normal file
@ -0,0 +1,111 @@
|
||||
package util;
|
||||
|
||||
import haxe.Exception;
|
||||
import cc.Colors;
|
||||
|
||||
enum Color {
|
||||
White;
|
||||
Orange;
|
||||
Magenta;
|
||||
LightBlue;
|
||||
Yellow;
|
||||
Lime;
|
||||
Pink;
|
||||
Gray;
|
||||
Grey;
|
||||
LightGray;
|
||||
LightGrey;
|
||||
Cyan;
|
||||
Purple;
|
||||
Blue;
|
||||
Brown;
|
||||
Green;
|
||||
Red;
|
||||
Black;
|
||||
}
|
||||
|
||||
class ColorConvert {
|
||||
public static function colorToCC(color: Color): cc.Colors.Color {
|
||||
switch color {
|
||||
case White:
|
||||
return Colors.white;
|
||||
case Orange:
|
||||
return Colors.orange;
|
||||
case Magenta:
|
||||
return Colors.magenta;
|
||||
case LightBlue:
|
||||
return Colors.lightBlue;
|
||||
case Yellow:
|
||||
return Colors.yellow;
|
||||
case Lime:
|
||||
return Colors.lime;
|
||||
case Pink:
|
||||
return Colors.pink;
|
||||
case Gray:
|
||||
return Colors.gray;
|
||||
case Grey:
|
||||
return Colors.grey;
|
||||
case LightGray:
|
||||
return Colors.lightGray;
|
||||
case LightGrey:
|
||||
return Colors.lightGrey;
|
||||
case Cyan:
|
||||
return Colors.cyan;
|
||||
case Purple:
|
||||
return Colors.purple;
|
||||
case Blue:
|
||||
return Colors.blue;
|
||||
case Brown:
|
||||
return Colors.brown;
|
||||
case Green:
|
||||
return Colors.green;
|
||||
case Red:
|
||||
return Colors.red;
|
||||
case Black:
|
||||
return Colors.black;
|
||||
};
|
||||
}
|
||||
|
||||
public static function ccToColor(color: cc.Colors.Color): Color {
|
||||
switch color {
|
||||
case 0:
|
||||
return White;
|
||||
case 1:
|
||||
return Orange;
|
||||
case 2:
|
||||
return Magenta;
|
||||
case 3:
|
||||
return LightBlue;
|
||||
case 4:
|
||||
return Yellow;
|
||||
case 5:
|
||||
return Lime;
|
||||
case 6:
|
||||
return Pink;
|
||||
case 7:
|
||||
return Gray;
|
||||
case 8:
|
||||
return Grey;
|
||||
case 9:
|
||||
return LightGray;
|
||||
case 10:
|
||||
return LightGrey;
|
||||
case 11:
|
||||
return Cyan;
|
||||
case 12:
|
||||
return Purple;
|
||||
case 13:
|
||||
return Blue;
|
||||
case 14:
|
||||
return Brown;
|
||||
case 15:
|
||||
return Green;
|
||||
case 16:
|
||||
return Red;
|
||||
case 17:
|
||||
return Black;
|
||||
case _:
|
||||
throw new Exception("Invalid input");
|
||||
}
|
||||
}
|
||||
}
|
16
src/util/Debug.hx
Normal file
16
src/util/Debug.hx
Normal file
@ -0,0 +1,16 @@
|
||||
package util;
|
||||
|
||||
import cc.ComputerCraft;
|
||||
import kernel.Log;
|
||||
|
||||
class Debug {
|
||||
public static function printBuildInfo() {
|
||||
Log.debug("Commit: " + BuildInfo.getGitCommitHash());
|
||||
|
||||
var time:Date = Date.fromTime(BuildInfo.buildTime());
|
||||
|
||||
Log.debug("Build time: " + time.toString());
|
||||
|
||||
Log.debug("CC/MC version:" + ComputerCraft._HOST);
|
||||
}
|
||||
}
|
62
src/util/EventBus.hx
Normal file
62
src/util/EventBus.hx
Normal file
@ -0,0 +1,62 @@
|
||||
package util;
|
||||
|
||||
import util.Signal.SignalListner;
|
||||
|
||||
class EventBusListner<T> {
|
||||
@:allow(util.EventBus)
|
||||
private final signalListner:SignalListner<T>;
|
||||
|
||||
@:allow(util.EventBus)
|
||||
private final eventName:String;
|
||||
|
||||
@:allow(util.EventBus)
|
||||
private function new(signalListner: SignalListner<T>,eventName: String) {
|
||||
this.signalListner = signalListner;
|
||||
this.eventName = eventName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Generic event handler.
|
||||
**/
|
||||
class EventBus<T>{
|
||||
private var listner: Map<String,Signal<T>> = new Map();
|
||||
|
||||
public function new() {
|
||||
|
||||
}
|
||||
|
||||
public function on(eventName: String, callback: T->Void):EventBusListner<T>{
|
||||
if (!listner.exists(eventName)){
|
||||
listner[eventName] = new Signal();
|
||||
}
|
||||
|
||||
var signalListner = listner[eventName].on(callback);
|
||||
return new EventBusListner(signalListner,eventName);
|
||||
}
|
||||
|
||||
public function once(eventName: String,callback: T->Void):EventBusListner<T> {
|
||||
if (!listner.exists(eventName)){
|
||||
listner[eventName] = new Signal();
|
||||
}
|
||||
|
||||
var signalListner = listner[eventName].once(callback);
|
||||
return new EventBusListner(signalListner,eventName);
|
||||
}
|
||||
|
||||
public function emit(eventName: String, data: Any) {
|
||||
if (listner.exists(eventName)){
|
||||
var signal = listner[eventName];
|
||||
signal.emit(data);
|
||||
}
|
||||
}
|
||||
|
||||
public function removeListner(id: EventBusListner<T>) {
|
||||
if (!listner.exists(id.eventName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
listner[id.eventName].remove(id.signalListner);
|
||||
}
|
||||
|
||||
}
|
69
src/util/Extender.hx
Normal file
69
src/util/Extender.hx
Normal file
@ -0,0 +1,69 @@
|
||||
package util;
|
||||
|
||||
import haxe.Exception;
|
||||
|
||||
class LambdaExtender {
|
||||
/**
|
||||
Returns the first element if there are exectly one element present.
|
||||
Throws exception if not.
|
||||
**/
|
||||
static public function single<T>(it : Iterable<T>): T {
|
||||
var elem: T = null;
|
||||
for (t in it) {
|
||||
if (elem != null){
|
||||
throw new Exception("Multiple elements found");
|
||||
}
|
||||
elem = t;
|
||||
}
|
||||
|
||||
if (elem == null){
|
||||
throw new Exception("No element found");
|
||||
}
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
/**
|
||||
Like `single` but when no element was found return the default value.
|
||||
**/
|
||||
static public function singleOrDefault<T>(it : Iterable<T>, defaultValue: T): T {
|
||||
var elem: T = null;
|
||||
for (t in it) {
|
||||
if (elem != null){
|
||||
throw new Exception("Multiple elements found");
|
||||
}
|
||||
elem = t;
|
||||
}
|
||||
|
||||
if (elem == null){
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the first element.
|
||||
Throws execption if no first element found.
|
||||
**/
|
||||
static public function first<T>(it : Iterable<T>): T {
|
||||
for (t in it) {
|
||||
return t;
|
||||
}
|
||||
|
||||
throw new Exception("No element found");
|
||||
}
|
||||
|
||||
/**
|
||||
Like `first` only if no first element was found it returns the defalt value.
|
||||
**/
|
||||
static public function firstOrDefault<T>(it : Iterable<T>, defaultValue: T): T {
|
||||
var iter = it.iterator();
|
||||
|
||||
if (iter.hasNext()){
|
||||
return iter.next();
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
19
src/util/MathI.hx
Normal file
19
src/util/MathI.hx
Normal file
@ -0,0 +1,19 @@
|
||||
package util;
|
||||
|
||||
class MathI {
|
||||
public static function max(a: Int, b:Int): Int {
|
||||
if (a > b){
|
||||
return a;
|
||||
}else{
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
public static function min(a: Int,b:Int): Int {
|
||||
if (a < b){
|
||||
return a;
|
||||
}else{
|
||||
return b;
|
||||
}
|
||||
}
|
||||
}
|
40
src/util/Promise.hx
Normal file
40
src/util/Promise.hx
Normal file
@ -0,0 +1,40 @@
|
||||
package util;
|
||||
|
||||
import haxe.Exception;
|
||||
|
||||
/**
|
||||
JS-like promise class.
|
||||
**/
|
||||
class Promise<T> {
|
||||
private var thenCB: T->Void;
|
||||
private var errorCB: Exception -> Void;
|
||||
|
||||
public function then(cb: (data: T)->Void): Promise<T> {
|
||||
thenCB = cb;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public function error(cb: (err: Exception)->Void) {
|
||||
errorCB = cb;
|
||||
}
|
||||
|
||||
public function new(func:(resolve:(T)->Void,reject:(Exception)->Void)->Void) {
|
||||
try {
|
||||
func(data -> {
|
||||
if (thenCB != null){
|
||||
thenCB(data);
|
||||
}
|
||||
},e -> {
|
||||
if (errorCB != null){
|
||||
errorCB(e);
|
||||
}
|
||||
});
|
||||
}catch(e:Exception){
|
||||
if (errorCB != null){
|
||||
errorCB(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
61
src/util/Signal.hx
Normal file
61
src/util/Signal.hx
Normal file
@ -0,0 +1,61 @@
|
||||
package util;
|
||||
|
||||
interface SignalReadonly<T> {
|
||||
public function on(cb: T->Void):SignalListner<T>;
|
||||
public function once(cb: T->Void):SignalListner<T>;
|
||||
public function remove(id:SignalListner<T>):Void;
|
||||
}
|
||||
|
||||
class SignalListner<T> {
|
||||
private final callback:T->Void;
|
||||
|
||||
@:allow(util.Signal)
|
||||
private final once:Bool;
|
||||
|
||||
@:allow(util.Signal)
|
||||
private function new(callback: T->Void,?once: Bool = false) {
|
||||
this.callback = callback;
|
||||
this.once = once;
|
||||
}
|
||||
|
||||
@:allow(util.Signal)
|
||||
private function invoke(params: T) {
|
||||
if (callback != null){
|
||||
callback(params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Simple event system for one event type other than EventBus which has multiple events.
|
||||
**/
|
||||
class Signal<T> implements SignalReadonly<T>{
|
||||
public final listner:Array<SignalListner<T>> = new Array();
|
||||
|
||||
public function new() {}
|
||||
|
||||
public function on(cb: T->Void):SignalListner<T> {
|
||||
var l = new SignalListner<T>(cb,false);
|
||||
listner.push(l);
|
||||
return l;
|
||||
}
|
||||
|
||||
public function once(cb: T->Void):SignalListner<T> {
|
||||
var l = new SignalListner<T>(cb,true);
|
||||
listner.push(l);
|
||||
return l;
|
||||
}
|
||||
|
||||
public function emit(data: T) {
|
||||
for (cb in listner){
|
||||
cb.invoke(data);
|
||||
if (cb.once){
|
||||
listner.remove(cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function remove(id:SignalListner<T> ) {
|
||||
listner.remove(id);
|
||||
}
|
||||
}
|
13
src/util/Vec.hx
Normal file
13
src/util/Vec.hx
Normal file
@ -0,0 +1,13 @@
|
||||
package util;
|
||||
|
||||
@:structInit class Vec2<T> {
|
||||
public final x:T;
|
||||
public final y:T;
|
||||
}
|
||||
|
||||
|
||||
@:structInit class Vec3<T> {
|
||||
public final x:T;
|
||||
public final y:T;
|
||||
public final z:T;
|
||||
}
|
15
yarn.lock
Normal file
15
yarn.lock
Normal file
@ -0,0 +1,15 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
luamin@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/luamin/-/luamin-1.0.4.tgz#944529b58fc6fa4d31eace2e0353d41210f0e3d3"
|
||||
integrity sha1-lEUptY/G+k0x6s4uA1PUEhDw49M=
|
||||
dependencies:
|
||||
luaparse "^0.2.1"
|
||||
|
||||
luaparse@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/luaparse/-/luaparse-0.2.1.tgz#aa8f56132b0de97d37f3c991a9df42e0e17f656c"
|
||||
integrity sha1-qo9WEysN6X0388mRqd9C4OF/ZWw=
|
Loading…
Reference in New Issue
Block a user