Compare commits
13 Commits
d1f9104aba
...
dfaabea00d
| Author | SHA1 | Date | |
|---|---|---|---|
| dfaabea00d | |||
| 8683eaf17a | |||
| 5fa6c3ecbf | |||
| 10a061c41b | |||
| c390519393 | |||
| 4f881117cf | |||
| 4084659a4a | |||
| 63e279f879 | |||
| f68ae00098 | |||
| 2937de86d6 | |||
| 788c839937 | |||
| 90e76c8cd9 | |||
| d2873d6353 |
@@ -81,6 +81,31 @@ ctx.setForegroundColor(White);
|
|||||||
ctx.write("Hello world!");
|
ctx.write("Hello world!");
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Under the hood
|
||||||
|
|
||||||
|
There are a number of interfaces and classes that needs to be explained to understand how the GUI works.
|
||||||
|
|
||||||
|
`TermWriteable` is an interface that allows the usage of the normal CC terminal write methods. Stuff like `write`, `setCursorPos` and `setCursorBlink` are defined here. This is of course implemented by the physical screens and the main terminal.
|
||||||
|
|
||||||
|
Most of the time you will not write directory to a real screen but to a `VirtualTermWriter` which extends `TermWriteable` with some more methods like `enable`
|
||||||
|
and `setTarget`. The `setTarget` is used as the proxy target of a `VirtualTermWriter` and with `enable` and `disable` you can enable and disable the forwarding of the write methods to the target.
|
||||||
|
|
||||||
|
The `StatelessVirtualTermWriter` and `BufferedVirtualTermWriter` are both `VirtualTermWriter`. They can have a real output as a target. Or they can have another `VirtualTermWriter` as target like the `BufferedVirtualTermWriter` which uses a `TermBuffer` as an intermediate target.
|
||||||
|
|
||||||
|
All of that is just for printing to the screen. If you want to read input you have to use the `WindowContext` which is a `TermWriteable`.
|
||||||
|
`WindowContext` also handles events like `onClick` or `onKey`. This is need so that the right program gets the input depending on the active window on the
|
||||||
|
screen or terminal.
|
||||||
|
|
||||||
|
All of the `WindowContext` are managed by the `WindowManager`. The `WindowManager` also delegates the events to the right `WindowContext`.
|
||||||
|
|
||||||
|
## GUI helper classes
|
||||||
|
|
||||||
|
Because we want a more abstract way of writing to the screen we have some "helper" classes. I call them "helper" but they a very essential to the GUI.
|
||||||
|
|
||||||
|
First there is the `Pixel` class which is nothing more that a char and a foreground and background color.
|
||||||
|
|
||||||
|
A collection of `Pixel` is called a `Canvas` which is nothing more than a 2D array of `Pixel` with some functions strapped to it.
|
||||||
|
|
||||||
# Proceses
|
# Proceses
|
||||||
|
|
||||||
The concept of processes tryes to encapsulate programs. A process is basically an interface with the `run(handle: ProcessHandle)` method.
|
The concept of processes tryes to encapsulate programs. A process is basically an interface with the `run(handle: ProcessHandle)` method.
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package bin;
|
package bin;
|
||||||
|
|
||||||
|
import kernel.log.Log;
|
||||||
import kernel.ps.ProcessHandle;
|
import kernel.ps.ProcessHandle;
|
||||||
import kernel.ps.Process;
|
import kernel.ps.Process;
|
||||||
|
|
||||||
@@ -11,6 +12,13 @@ class HelloWorld implements Process {
|
|||||||
|
|
||||||
public function run(handle:ProcessHandle) {
|
public function run(handle:ProcessHandle) {
|
||||||
handle.write("Hello World!");
|
handle.write("Hello World!");
|
||||||
|
|
||||||
|
var c = new HelloWorldServiceRPC(0);
|
||||||
|
|
||||||
|
c.getNumber().handle((res)->{
|
||||||
|
Log.debug("Got number: " + res);
|
||||||
|
});
|
||||||
|
|
||||||
handle.close();
|
handle.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,25 @@
|
|||||||
package bin;
|
package bin;
|
||||||
|
|
||||||
import kernel.Timer;
|
import macros.rpc.RPC;
|
||||||
import kernel.ps.ProcessHandle;
|
import kernel.ps.ProcessHandle;
|
||||||
import kernel.ps.Process;
|
import kernel.ps.Process;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
@:build(macros.rpc.RPC.buildRPC())
|
||||||
class HelloWorldService implements Process {
|
class HelloWorldService implements Process {
|
||||||
private var timer:Timer;
|
private var handle:ProcessHandle;
|
||||||
|
|
||||||
public function new() {}
|
public function new() {}
|
||||||
|
|
||||||
public function run(handle:ProcessHandle) {
|
public function run(handle:ProcessHandle) {
|
||||||
handle.write("Hello World! Started\n");
|
this.handle = handle;
|
||||||
this.startTimer(handle);
|
|
||||||
handle.addDeferFunc(()->{
|
RPC.generateRPCPackageHandle();
|
||||||
timer.cancle();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function startTimer(handle: ProcessHandle) {
|
@rpc
|
||||||
this.timer = new Timer(5, function() {
|
public function getNumber():Int{
|
||||||
handle.write("Hello World!\n");
|
return 42;
|
||||||
this.startTimer(handle);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
54
src/bin/pathfinder/PFClient.hx
Normal file
54
src/bin/pathfinder/PFClient.hx
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package bin.pathfinder;
|
||||||
|
|
||||||
|
import lib.Pos3;
|
||||||
|
import lib.ui.elements.UIElement;
|
||||||
|
import lib.ui.elements.TextElement;
|
||||||
|
import lib.ui.elements.RootElement;
|
||||||
|
import kernel.ui.WindowContext;
|
||||||
|
import kernel.ps.ProcessHandle;
|
||||||
|
import kernel.ps.Process;
|
||||||
|
|
||||||
|
class PFClient implements Process {
|
||||||
|
private var handle:ProcessHandle;
|
||||||
|
|
||||||
|
private var ctx: WindowContext;
|
||||||
|
private var requestRender:Void -> Void;
|
||||||
|
private var root:RootElement;
|
||||||
|
|
||||||
|
public function new() {}
|
||||||
|
|
||||||
|
public function run(handle:ProcessHandle) {
|
||||||
|
this.handle = handle;
|
||||||
|
|
||||||
|
var stateless = handle.createStatelessWindowContext();
|
||||||
|
this.ctx = stateless.ctx;
|
||||||
|
this.requestRender = stateless.requestRender;
|
||||||
|
|
||||||
|
stateless.setRenderFunc(this.render);
|
||||||
|
|
||||||
|
this.root = new RootElement();
|
||||||
|
this.root.setTitle("Pathfinder");
|
||||||
|
|
||||||
|
this.ctx.delegateEvents(this.root);
|
||||||
|
|
||||||
|
this.requestRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function render() {
|
||||||
|
var acc = kernel.gps.GPS.instance.getAccuracy();
|
||||||
|
var pos: Pos3 = kernel.gps.GPS.instance.getPosition() ?? {x: 0, y: 0, z: 0};
|
||||||
|
|
||||||
|
var childre: Array<UIElement> = [
|
||||||
|
new TextElement('Acc: ${acc}'),
|
||||||
|
new TextElement('Pos: X:${pos.x} Y:${pos.y} Z:${pos.z}'),
|
||||||
|
new TextElement('UPDATE', { style: {bgColor: Gray}, uiEvents: {onClick: () -> {
|
||||||
|
kernel.gps.GPS.instance.locate().handle((pos) ->{
|
||||||
|
this.requestRender();
|
||||||
|
});
|
||||||
|
}}}),
|
||||||
|
];
|
||||||
|
|
||||||
|
this.root.setChildren(childre);
|
||||||
|
this.root.render(ctx.getSize()).renderToContext(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -70,5 +70,20 @@ class CLI extends CLIAppBase {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}, "<name>");
|
}, "<name>");
|
||||||
|
|
||||||
|
registerAsyncSubcommand("list", (args) -> {
|
||||||
|
return RessourceNames.list().map((res) -> {
|
||||||
|
switch (res) {
|
||||||
|
case Success(data):
|
||||||
|
for (name in data) {
|
||||||
|
handle.writeLine(name);
|
||||||
|
}
|
||||||
|
case Failure(error):
|
||||||
|
handle.writeLine("Error: " + error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,3 +30,7 @@ typedef UnregisterRequest = {
|
|||||||
typedef UnregisterResponse = {
|
typedef UnregisterResponse = {
|
||||||
public var success:Bool;
|
public var success:Bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef ListRequest = {
|
||||||
|
public var ?type:String;
|
||||||
|
}
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ class SiteRessourceController implements Process {
|
|||||||
pkg.respond(handleUnregister(cast pkg.data));
|
pkg.respond(handleUnregister(cast pkg.data));
|
||||||
case "get":
|
case "get":
|
||||||
pkg.respond(handleGet(cast pkg.data));
|
pkg.respond(handleGet(cast pkg.data));
|
||||||
|
case "list":
|
||||||
|
pkg.respond(list());
|
||||||
default:
|
default:
|
||||||
handle.writeLine("Unknown message type: " + pkg.data.type);
|
handle.writeLine("Unknown message type: " + pkg.data.type);
|
||||||
}
|
}
|
||||||
@@ -84,6 +86,10 @@ class SiteRessourceController implements Process {
|
|||||||
return ressources.get(name);
|
return ressources.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private inline function list():Array<String> {
|
||||||
|
return [ for (k in ressources.keys()) k];
|
||||||
|
}
|
||||||
|
|
||||||
private function load() {
|
private function load() {
|
||||||
var store = KVStore.getStoreForClass();
|
var store = KVStore.getStoreForClass();
|
||||||
var data:Null<Map<String, NetworkID>> = store.get("ressources");
|
var data:Null<Map<String, NetworkID>> = store.get("ressources");
|
||||||
|
|||||||
@@ -3,26 +3,7 @@ package kernel;
|
|||||||
@:keep
|
@:keep
|
||||||
class DCEHack {
|
class DCEHack {
|
||||||
// Dont actually call this
|
// Dont actually call this
|
||||||
public static function load() {
|
public static function load():Array<kernel.ps.Process>{
|
||||||
return [
|
macros.DCEHack.dceGenerateCreate();
|
||||||
new bin.Disk(),
|
|
||||||
new bin.GPS(),
|
|
||||||
new bin.HelloWorld(),
|
|
||||||
new bin.KernelLog(),
|
|
||||||
new bin.LSPS(),
|
|
||||||
new bin.Net(),
|
|
||||||
new bin.Redstone(),
|
|
||||||
new bin.Service(),
|
|
||||||
new bin.Terminal(),
|
|
||||||
new bin.Turtle(),
|
|
||||||
new bin.HelloWorldService(),
|
|
||||||
new bin.srsc.SiteRessourceController(),
|
|
||||||
new bin.srsc.CLI(),
|
|
||||||
new bin.Perf(),
|
|
||||||
new bin.KSettings(),
|
|
||||||
new bin.exporter.ResManager(),
|
|
||||||
new bin.exporter.Res(),
|
|
||||||
new bin.ID(),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class Init {
|
|||||||
|
|
||||||
// Register default terminate handler
|
// Register default terminate handler
|
||||||
KernelEvents.instance.onTerminate.handle(_->{
|
KernelEvents.instance.onTerminate.handle(_->{
|
||||||
OS.reboot();
|
KernelEvents.instance.shutdown();
|
||||||
});
|
});
|
||||||
|
|
||||||
Debug.printBuildInfo();
|
Debug.printBuildInfo();
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package kernel;
|
package kernel;
|
||||||
|
|
||||||
import haxe.MainLoop;
|
import kernel.peripherals.Peripherals.Peripheral;
|
||||||
import cc.OS;
|
|
||||||
import kernel.log.Log;
|
import kernel.log.Log;
|
||||||
import lib.Pos;
|
import lib.Pos;
|
||||||
import cc.HTTP.HTTPResponse;
|
import cc.HTTP.HTTPResponse;
|
||||||
@@ -150,6 +149,12 @@ class KernelEvents {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function shutdown() {
|
public function shutdown() {
|
||||||
|
|
||||||
|
// clearing screens
|
||||||
|
for (screen in Peripheral.instance.getAllScreens()) {
|
||||||
|
screen.reset();
|
||||||
|
}
|
||||||
|
|
||||||
Log.info('Shutting down event loop');
|
Log.info('Shutting down event loop');
|
||||||
this.stopLoop = true;
|
this.stopLoop = true;
|
||||||
MainTerm.instance.reset();
|
MainTerm.instance.reset();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package kernel.binstore;
|
package kernel.binstore;
|
||||||
|
|
||||||
|
import bin.pathfinder.PFClient;
|
||||||
import bin.ID;
|
import bin.ID;
|
||||||
import bin.exporter.Res;
|
import bin.exporter.Res;
|
||||||
import bin.exporter.ResManager;
|
import bin.exporter.ResManager;
|
||||||
@@ -41,7 +42,8 @@ class BinStore {
|
|||||||
{c: KSettings, name: "KSettings", aliases: ["ksettings","ks"]},
|
{c: KSettings, name: "KSettings", aliases: ["ksettings","ks"]},
|
||||||
{c: ResManager, name: "ResManager", aliases: ["resmanager","resmgr"]},
|
{c: ResManager, name: "ResManager", aliases: ["resmanager","resmgr"]},
|
||||||
{c: Res, name: "Res", aliases: ["res"]},
|
{c: Res, name: "Res", aliases: ["res"]},
|
||||||
{c: ID , name: "ID", aliases: ["id"]}
|
{c: ID , name: "ID", aliases: ["id"]},
|
||||||
|
{c: PFClient, name: "PFClient", aliases: ["pfclient"]}
|
||||||
];
|
];
|
||||||
|
|
||||||
@:allow(kernel.Init)
|
@:allow(kernel.Init)
|
||||||
@@ -68,4 +70,12 @@ class BinStore {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getNameByAlias(alias: String): String {
|
||||||
|
var bin = getBinByAlias(alias);
|
||||||
|
if (bin == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return bin.name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
182
src/kernel/peripherals/BigReactor.hx
Normal file
182
src/kernel/peripherals/BigReactor.hx
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
package kernel.peripherals;
|
||||||
|
|
||||||
|
import cc.Peripheral;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
class BigReactor implements IPeripheral {
|
||||||
|
public static inline final TYPE_NAME:String = "BigReactors-Reactor";
|
||||||
|
|
||||||
|
private final addr:String;
|
||||||
|
|
||||||
|
public function new(addr:String) {
|
||||||
|
this.addr = addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAddr():String {
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType():String {
|
||||||
|
return TYPE_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getActive():Bool {
|
||||||
|
return Peripheral.call(addr, "getActive");
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getNumberOfControlRods():Int {
|
||||||
|
return Peripheral.call(addr, "getNumberOfControlRods");
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getEnergyStored():Float {
|
||||||
|
return Peripheral.call(addr, "getEnergyStored");
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getFuelTemperature():Float {
|
||||||
|
return Peripheral.call(addr, "getFuelTemperature");
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getCasingTemperature():Float {
|
||||||
|
return Peripheral.call(addr, "getCasingTemperature");
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getFuelAmount():Int {
|
||||||
|
return Peripheral.call(addr, "getFuelAmount");
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getWasteAmount():Int {
|
||||||
|
return Peripheral.call(addr, "getWasteAmount");
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getFuelAmountMax():Int {
|
||||||
|
return Peripheral.call(addr, "getFuelAmountMax");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getControlRodName(controlRodIndex:Int):String {
|
||||||
|
// TODO: check in bounds
|
||||||
|
return Peripheral.call(addr, "getControlRodName", controlRodIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getControlRodLevel(controlRodIndex:Int):Int {
|
||||||
|
// TODO: check in bounds
|
||||||
|
return Peripheral.call(addr, "getControlRodLevel", controlRodIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
If the reactor is actively cooled, returns the amount of hot fluid produced in the past tick, in milli-Buckets (mB).
|
||||||
|
**/
|
||||||
|
public inline function getEnergyProducedLastTick():Float {
|
||||||
|
return Peripheral.call(addr, "getEnergyProducedLastTick");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
If the reactor is passively cooled, always returns 0.
|
||||||
|
**/
|
||||||
|
public inline function getHotFluidProducedLastTick():Float {
|
||||||
|
return Peripheral.call(addr, "getHotFluidProducedLastTick");
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getCoolantType():Null<String> {
|
||||||
|
return Peripheral.call(addr, "getCoolantType");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
in milli-buckets (mB)
|
||||||
|
**/
|
||||||
|
public inline function getCoolantAmount():Int {
|
||||||
|
return Peripheral.call(addr, "getCoolantAmount");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
in milli-buckets (mB)
|
||||||
|
**/
|
||||||
|
public inline function getCoolantAmountMax():Int {
|
||||||
|
return Peripheral.call(addr, "getCoolantAmountMax");
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getHotFluidType():Null<String> {
|
||||||
|
return Peripheral.call(addr, "getHotFluidType");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
in milli-buckets (mB)
|
||||||
|
**/
|
||||||
|
public inline function getHotFluidAmount():Int {
|
||||||
|
return Peripheral.call(addr, "getHotFluidAmount");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the reactivity level of the reactor's fuel. 100 = 100 percent
|
||||||
|
**/
|
||||||
|
public inline function getFuelReactivity():Int {
|
||||||
|
return Peripheral.call(addr, "getFuelReactivity");
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getFuelConsumedLastTick():Float {
|
||||||
|
return Peripheral.call(addr, "getFuelConsumedLastTick");
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function isActivelyCooled():Bool {
|
||||||
|
return Peripheral.call(addr, "isActivelyCooled");
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function setActive(active:Bool):Void {
|
||||||
|
Peripheral.call(addr, "setActive", active);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
0 (not inserted) to 100 (fully inserted)
|
||||||
|
**/
|
||||||
|
public inline function setAllControlRodLevels(level:Int) {
|
||||||
|
Peripheral.call(addr, "setAllControlRodLevels", level);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
0 (not inserted) to 100 (fully inserted)
|
||||||
|
**/
|
||||||
|
public function setControlRodLevel(index:Int, level:Int):Void {
|
||||||
|
// TODO: check in bounds
|
||||||
|
Peripheral.call(addr, "setControlRodLevel", index, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function doEjectWaste():Void {
|
||||||
|
throw new Error("Not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function doEjectFuel():Void {
|
||||||
|
throw new Error("Not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getControlRodLocation(index:Int):Void {
|
||||||
|
throw new Error("Not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getEnergyStoredAsText():String {
|
||||||
|
return Peripheral.call(addr, "getEnergyStoredAsText");
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getVariant():String {
|
||||||
|
return Peripheral.call(addr, "getVariant");
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function setControlRodName(index:Int, name:String):Void {
|
||||||
|
Peripheral.call(addr, "setControlRodName", index, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Useless methods
|
||||||
|
// getFuelStats
|
||||||
|
// getEnergyStats
|
||||||
|
// getCoolantFluidStats
|
||||||
|
// getHotFluidStats
|
||||||
|
|
||||||
|
// TODO: need research
|
||||||
|
// isMethodAvailable(method: String): Bool
|
||||||
|
// mbGetMaximumCoordinate(): Pos3
|
||||||
|
// mbGetMinimumCoordinate(): Pos3
|
||||||
|
// mbGetMultiblockControllerTypeName(): String
|
||||||
|
// mbIsAssembled(): Bool
|
||||||
|
// mbIsConnected(): Bool
|
||||||
|
// mbIsDisassembled(): Bool
|
||||||
|
// mbIsPaused(): Bool
|
||||||
|
}
|
||||||
@@ -36,7 +36,7 @@ class Peripheral {
|
|||||||
return cc.Peripheral.getType(addr).toArray();
|
return cc.Peripheral.getType(addr).toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function findAddrByType(type: String): Array<String> {
|
public function findAddrByType(type: String): Array<String> {
|
||||||
return getAllAddresses().filter(addr -> getTypes(addr).contains(type));
|
return getAllAddresses().filter(addr -> getTypes(addr).contains(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ abstract BundleMask(Int) from cc.Colors.Color to cc.Colors.Color {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@:build(macros.Exporter.buildExport())
|
||||||
class Redstone implements IPeripheral implements IExportable {
|
class Redstone implements IPeripheral implements IExportable {
|
||||||
public static inline final TYPE_NAME:String = "redstone"; // TODO: there is technically not a type for redstone.
|
public static inline final TYPE_NAME:String = "redstone"; // TODO: there is technically not a type for redstone.
|
||||||
|
|
||||||
@@ -89,10 +90,12 @@ class Redstone implements IPeripheral implements IExportable {
|
|||||||
cc.Redstone.setOutput(this.addr,on);
|
cc.Redstone.setOutput(this.addr,on);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@export("output")
|
||||||
public inline function getOutput(): Bool {
|
public inline function getOutput(): Bool {
|
||||||
return cc.Redstone.getOutput(this.addr);
|
return cc.Redstone.getOutput(this.addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@export("input")
|
||||||
public inline function getInput(): Bool {
|
public inline function getInput(): Bool {
|
||||||
return cc.Redstone.getInput(this.addr);
|
return cc.Redstone.getInput(this.addr);
|
||||||
}
|
}
|
||||||
@@ -126,14 +129,4 @@ class Redstone implements IPeripheral implements IExportable {
|
|||||||
public inline function testBundledInput(mask: Color): Bool {
|
public inline function testBundledInput(mask: Color): Bool {
|
||||||
return cc.Redstone.testBundledInput(this.addr,mask);
|
return cc.Redstone.testBundledInput(this.addr,mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function export():ExportConfig {
|
|
||||||
return {
|
|
||||||
getDelegates: [
|
|
||||||
"input" => (_) -> {return Bool(this.getInput());},
|
|
||||||
"analog" => (_) -> {return Number(this.getAnalogInput());}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package kernel.ps;
|
|||||||
/**
|
/**
|
||||||
Defines an independent process that can be run by the kernel.
|
Defines an independent process that can be run by the kernel.
|
||||||
**/
|
**/
|
||||||
|
@:autoBuild(macros.DCEHack.DCEHack.dceInclude())
|
||||||
interface Process {
|
interface Process {
|
||||||
public function run(handle: ProcessHandle): Void;
|
public function run(handle: ProcessHandle): Void;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ class StatelessVirtualTermWriter implements VirtualTermWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
|
target.reset();
|
||||||
renderFunc();
|
renderFunc();
|
||||||
renderRequested = false;
|
renderRequested = false;
|
||||||
}else{
|
}else{
|
||||||
@@ -84,6 +85,8 @@ class StatelessVirtualTermWriter implements VirtualTermWriter {
|
|||||||
});
|
});
|
||||||
|
|
||||||
target = newTarget;
|
target = newTarget;
|
||||||
|
|
||||||
|
target.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isEnabled():Bool {
|
public function isEnabled():Bool {
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package lib;
|
package lib;
|
||||||
|
|
||||||
|
import kernel.log.Log;
|
||||||
|
import kernel.binstore.BinStore;
|
||||||
|
import kernel.peripherals.Screen;
|
||||||
|
import kernel.peripherals.Peripherals.Peripheral;
|
||||||
import kernel.ps.Process;
|
import kernel.ps.Process;
|
||||||
import kernel.ps.ProcessManager;
|
import kernel.ps.ProcessManager;
|
||||||
import bin.KernelLog;
|
import bin.KernelLog;
|
||||||
@@ -23,6 +27,15 @@ class HomeContext {
|
|||||||
private var currentWorkspace:Int = -1;
|
private var currentWorkspace:Int = -1;
|
||||||
private var requestRender: Void->Void;
|
private var requestRender: Void->Void;
|
||||||
private var renderer:RootElement;
|
private var renderer:RootElement;
|
||||||
|
|
||||||
|
private var selectedOutput:String = "main";
|
||||||
|
private var selectedOutputIndex:Int = -1;
|
||||||
|
|
||||||
|
private final listedApps:Array<String> = [
|
||||||
|
"terminal",
|
||||||
|
"log",
|
||||||
|
"pfclient"
|
||||||
|
];
|
||||||
|
|
||||||
public function new() {}
|
public function new() {}
|
||||||
|
|
||||||
@@ -34,6 +47,7 @@ class HomeContext {
|
|||||||
WindowManager.instance.focusContextToOutput(ctx, "main");
|
WindowManager.instance.focusContextToOutput(ctx, "main");
|
||||||
|
|
||||||
renderer = new RootElement();
|
renderer = new RootElement();
|
||||||
|
renderer.setTitle("Home");
|
||||||
|
|
||||||
ctx.delegateEvents(renderer);
|
ctx.delegateEvents(renderer);
|
||||||
stateless.setRenderFunc(this.render);
|
stateless.setRenderFunc(this.render);
|
||||||
@@ -71,7 +85,7 @@ class HomeContext {
|
|||||||
|
|
||||||
private function focusContext(id:Int) {
|
private function focusContext(id:Int) {
|
||||||
if (workspaces.exists(id)) {
|
if (workspaces.exists(id)) {
|
||||||
WindowManager.instance.focusContextToOutput(workspaces[id], "main");
|
WindowManager.instance.focusContextToOutput(workspaces[id], selectedOutput);
|
||||||
currentWorkspace = id;
|
currentWorkspace = id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,7 +104,15 @@ class HomeContext {
|
|||||||
focusContext(contextId);
|
focusContext(contextId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function spawnPs(ps: Process) {
|
private function spawnPs(binName: String) {
|
||||||
|
var bin = BinStore.instance.getBinByAlias(binName);
|
||||||
|
|
||||||
|
if (bin == null) {
|
||||||
|
Log.error('Could not find bin: ${binName}');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ps = Type.createInstance(bin.c,[]);
|
||||||
var pid = ProcessManager.run(ps, {});
|
var pid = ProcessManager.run(ps, {});
|
||||||
var lastContextID = -1;
|
var lastContextID = -1;
|
||||||
|
|
||||||
@@ -101,26 +123,52 @@ class HomeContext {
|
|||||||
if (lastContextID == -1) {
|
if (lastContextID == -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
focusContext(lastContextID);
|
focusContext(lastContextID);
|
||||||
|
|
||||||
|
if (selectedOutputIndex != -1) {
|
||||||
|
requestRender();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function cycleOutput() {
|
||||||
|
var screenAddr = Peripheral.instance.findAddrByType(Screen.TYPE_NAME);
|
||||||
|
|
||||||
|
if (selectedOutputIndex == -1) {
|
||||||
|
selectedOutputIndex = 0;
|
||||||
|
selectedOutput = screenAddr[selectedOutputIndex];
|
||||||
|
} else if (selectedOutputIndex >= screenAddr.length - 1) {
|
||||||
|
selectedOutputIndex = -1;
|
||||||
|
selectedOutput = "main";
|
||||||
|
} else {
|
||||||
|
selectedOutputIndex++;
|
||||||
|
selectedOutput = screenAddr[selectedOutputIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
requestRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function render() {
|
private function render() {
|
||||||
ctx.clear();
|
|
||||||
ctx.setCursorBlink(false);
|
ctx.setCursorBlink(false);
|
||||||
|
|
||||||
var workspaceIDs:Array<Int> = [for (k=>v in workspaces) k];
|
var workspaceIDs:Array<Int> = [for (k=>v in workspaces) k];
|
||||||
workspaceIDs.sort((a, b) -> a - b);
|
workspaceIDs.sort((a, b) -> a - b);
|
||||||
|
|
||||||
var children:Array<UIElement> = [
|
var children:Array<UIElement> = [
|
||||||
for (i in workspaceIDs) new TextElement('Switch to ${i + 1}', {onClick: this.handleSelectContext.bind(i)})
|
for (i in workspaceIDs) new TextElement('Switch to ${i + 1}', {uiEvents: {onClick: this.handleSelectContext.bind(i)}})
|
||||||
];
|
];
|
||||||
|
|
||||||
children.push(new TextElement('Add Terminal', {onClick: this.spawnPs.bind(new Terminal())}));
|
for (i in 0...listedApps.length) {
|
||||||
children.push(new TextElement('Add Log', {onClick: this.spawnPs.bind(new KernelLog())}));
|
children.push(new TextElement(
|
||||||
children.push(new TextElement('Exit', {onClick: KernelEvents.instance.shutdown}));
|
'Add ${BinStore.instance.getNameByAlias(listedApps[i])}',
|
||||||
|
{uiEvents: {onClick: this.spawnPs.bind(listedApps[i])}}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
children.push(new TextElement('Output: ${selectedOutput}',{ uiEvents:{ onClick: this.cycleOutput}}));
|
||||||
|
children.push(new TextElement('Exit', {style: {bgColor: Red}, uiEvents: {onClick: KernelEvents.instance.shutdown}}));
|
||||||
|
|
||||||
renderer.setChildren(children);
|
renderer.setChildren(children);
|
||||||
|
|
||||||
renderer.render().renderToContext(ctx);
|
renderer.render(ctx.getSize()).renderToContext(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package lib;
|
package lib;
|
||||||
|
|
||||||
|
import bin.srsc.PackageTypes.ListRequest;
|
||||||
import kernel.KernelSettings;
|
import kernel.KernelSettings;
|
||||||
import bin.srsc.PackageTypes.UnregisterRequest;
|
import bin.srsc.PackageTypes.UnregisterRequest;
|
||||||
import kernel.log.Log;
|
|
||||||
import bin.srsc.PackageTypes.RegisterRequest;
|
import bin.srsc.PackageTypes.RegisterRequest;
|
||||||
import bin.srsc.PackageTypes.GetRequest;
|
import bin.srsc.PackageTypes.GetRequest;
|
||||||
import bin.srsc.SiteRessourceController;
|
import bin.srsc.SiteRessourceController;
|
||||||
@@ -62,4 +62,23 @@ class RessourceNames {
|
|||||||
payload
|
payload
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function list(controllerID: NetworkID = -1): Promise<Array<String>> {
|
||||||
|
if (controllerID == -1) controllerID = KernelSettings.siteController;
|
||||||
|
|
||||||
|
var payload: ListRequest = {type: "list"};
|
||||||
|
|
||||||
|
return Net.instance.sendAndAwait(
|
||||||
|
controllerID,
|
||||||
|
SiteRessourceController.SITE_CONTROLLER_RESSOURCE_MANAGER_PROTO,
|
||||||
|
payload
|
||||||
|
).map(res->{
|
||||||
|
switch (res){
|
||||||
|
case Success(pkg):
|
||||||
|
return Success(pkg.data);
|
||||||
|
case Failure(error):
|
||||||
|
return Failure(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
8
src/lib/ui/Style.hx
Normal file
8
src/lib/ui/Style.hx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package lib.ui;
|
||||||
|
|
||||||
|
import lib.Color;
|
||||||
|
|
||||||
|
typedef Style = {
|
||||||
|
public var ?fgColor: Color;
|
||||||
|
public var ?bgColor: Color;
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package lib.ui.elements;
|
|||||||
class RootElement implements UIElement {
|
class RootElement implements UIElement {
|
||||||
private var children:Array<UIElement>;
|
private var children:Array<UIElement>;
|
||||||
private final eventManager:UIEventManager = new UIEventManager();
|
private final eventManager:UIEventManager = new UIEventManager();
|
||||||
|
private var title:String = "";
|
||||||
|
|
||||||
public function new(?children:Array<UIElement>) {
|
public function new(?children:Array<UIElement>) {
|
||||||
if (children == null) {
|
if (children == null) {
|
||||||
@@ -20,14 +21,21 @@ class RootElement implements UIElement {
|
|||||||
this.children = children;
|
this.children = children;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render():Canvas {
|
public function render(bounds: Pos):Canvas {
|
||||||
var canvas = new Canvas();
|
var canvas = new Canvas();
|
||||||
var offset = new Pos({x: 0, y: 0});
|
var offset = new Pos({x: 0, y: 0});
|
||||||
|
|
||||||
|
if (hasTitle()) {
|
||||||
|
var title = new TextElement(this.title);
|
||||||
|
var halfWidth = Math.floor(bounds.x / 2) - Math.floor(this.title.length / 2);
|
||||||
|
canvas.combine(title.render(bounds), {x: halfWidth, y: offset.y});
|
||||||
|
offset = new Pos({x: 0, y: 1});
|
||||||
|
}
|
||||||
|
|
||||||
this.eventManager.clearMap();
|
this.eventManager.clearMap();
|
||||||
|
|
||||||
for (child in children) {
|
for (child in children) {
|
||||||
var childCanvas = child.render();
|
var childCanvas = child.render(bounds);
|
||||||
var bounds = childCanvas.getBounds();
|
var bounds = childCanvas.getBounds();
|
||||||
bounds.offset(offset);
|
bounds.offset(offset);
|
||||||
|
|
||||||
@@ -39,4 +47,12 @@ class RootElement implements UIElement {
|
|||||||
|
|
||||||
return canvas;
|
return canvas;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setTitle(title: String) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline function hasTitle(): Bool {
|
||||||
|
return title != "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,12 @@ class TextElement implements UIElement {
|
|||||||
public var text:String;
|
public var text:String;
|
||||||
|
|
||||||
private final uiEvents:UIEvents;
|
private final uiEvents:UIEvents;
|
||||||
|
private final style:Style;
|
||||||
|
|
||||||
public function new(text:String, ?uiEvents:UIEvents) {
|
public function new(text:String, ?props: {?style:Style, ?uiEvents:UIEvents}) {
|
||||||
this.text = text;
|
this.text = text;
|
||||||
this.uiEvents = uiEvents;
|
this.uiEvents = props?.uiEvents;
|
||||||
|
this.style = props?.style ?? {fgColor: White, bgColor: Black};
|
||||||
}
|
}
|
||||||
|
|
||||||
public function set(text:String) {
|
public function set(text:String) {
|
||||||
@@ -24,11 +26,11 @@ class TextElement implements UIElement {
|
|||||||
return uiEvents;
|
return uiEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render():Canvas {
|
public function render(bounds: Pos):Canvas {
|
||||||
var canvas = new Canvas();
|
var canvas = new Canvas();
|
||||||
for (i in 0...this.text.length) {
|
for (i in 0...this.text.length) {
|
||||||
var c = this.text.charAt(i);
|
var c = this.text.charAt(i);
|
||||||
canvas.set({x: i, y: 0}, {bg: Black, textColor: White, char: c});
|
canvas.set({x: i, y: 0}, {bg: style.bgColor ?? Black, textColor: style.fgColor ?? White, char: c});
|
||||||
}
|
}
|
||||||
|
|
||||||
return canvas;
|
return canvas;
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ package lib.ui.elements;
|
|||||||
import lib.ui.rendere.UIEventDelegate;
|
import lib.ui.rendere.UIEventDelegate;
|
||||||
|
|
||||||
interface UIElement extends UIEventDelegate {
|
interface UIElement extends UIEventDelegate {
|
||||||
public function render():Canvas;
|
public function render(bounds: Pos):Canvas;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,74 +0,0 @@
|
|||||||
package lib.ui.reactive;
|
|
||||||
|
|
||||||
import lib.Pos;
|
|
||||||
import util.Rect;
|
|
||||||
import util.ObservableArray;
|
|
||||||
import lib.Vec.Vec2;
|
|
||||||
|
|
||||||
class ListElement extends UIElement {
|
|
||||||
|
|
||||||
private final content:ObservableArray<UIElement>;
|
|
||||||
private var elementMap: Map<Rect,UIElement> = new Map(); // Position in the map is relative.
|
|
||||||
private var offset:Pos;
|
|
||||||
|
|
||||||
public function new(content: ObservableArray<UIElement>) {
|
|
||||||
var events: UIEvents = {
|
|
||||||
onClick: (p)->{
|
|
||||||
var element = UIElement.getElementInMap((p.pos:Pos) - offset,elementMap);
|
|
||||||
if (element != null)
|
|
||||||
if (element.eventListner.onClick != null)
|
|
||||||
element.eventListner.onClick.invoke(p);
|
|
||||||
},
|
|
||||||
onMouseUp: (p)->{
|
|
||||||
var element = UIElement.getElementInMap((p.pos:Pos) - offset,elementMap);
|
|
||||||
if (element != null)
|
|
||||||
if (element.eventListner.onMouseUp != null)
|
|
||||||
element.eventListner.onMouseUp.invoke(p);
|
|
||||||
},
|
|
||||||
onMouseScroll: (p)->{
|
|
||||||
var element = UIElement.getElementInMap((p.pos:Pos) - offset,elementMap);
|
|
||||||
if (element != null)
|
|
||||||
if (element.eventListner.onMouseScroll != null)
|
|
||||||
element.eventListner.onMouseScroll.invoke(p);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
super(events);
|
|
||||||
|
|
||||||
this.content = content;
|
|
||||||
this.content.subscribe(value -> {
|
|
||||||
this.changedTrigger.trigger(null);
|
|
||||||
// TODO: subscribe to elements and forward onChange event
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function render(bounds:Vec2<Int>,offset: Pos):Canvas {
|
|
||||||
var canvas: Canvas = new Canvas();
|
|
||||||
var writePoint:Pos = {x: 0, y: 0};
|
|
||||||
this.offset = offset;
|
|
||||||
|
|
||||||
for(element in this.content.get()){
|
|
||||||
if (bounds.y - writePoint.y <= 0) {
|
|
||||||
// No more space to render children
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var childRender = element.render({
|
|
||||||
x: bounds.x,
|
|
||||||
y: bounds.y - writePoint.y
|
|
||||||
}, offset + writePoint);
|
|
||||||
|
|
||||||
canvas.combine(childRender, writePoint);
|
|
||||||
elementMap.set(new Rect(writePoint,
|
|
||||||
{
|
|
||||||
x: childRender.maxWidth() + writePoint.x,
|
|
||||||
y: writePoint.y + (childRender.height() - 1),
|
|
||||||
}
|
|
||||||
),element);
|
|
||||||
|
|
||||||
writePoint = {x: 0, y: writePoint.y + childRender.height()};
|
|
||||||
}
|
|
||||||
|
|
||||||
return canvas;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
package lib.ui.reactive;
|
|
||||||
|
|
||||||
import util.Pos;
|
|
||||||
import util.Rect;
|
|
||||||
import util.Color;
|
|
||||||
import util.Vec.Vec2;
|
|
||||||
import kernel.ui.WindowContext;
|
|
||||||
|
|
||||||
using tink.CoreApi;
|
|
||||||
|
|
||||||
class ReactiveUI {
|
|
||||||
private final context:WindowContext;
|
|
||||||
private final children:Array<UIElement>;
|
|
||||||
private var elementMap: Map<Rect,UIElement> = new Map();
|
|
||||||
|
|
||||||
public function new(context:WindowContext, children:Array<UIElement>) {
|
|
||||||
this.context = context;
|
|
||||||
this.children = children;
|
|
||||||
|
|
||||||
for (child in children) {
|
|
||||||
child.changed.handle(_ -> {
|
|
||||||
context.reset();
|
|
||||||
render();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setupEvents();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function setupEvents() {
|
|
||||||
context.onClick.handle(params ->{
|
|
||||||
for (k => v in elementMap){
|
|
||||||
if (k.isInside(params.pos)){
|
|
||||||
if (v.eventListner.onClick != null){
|
|
||||||
v.eventListner.onClick.invoke(params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// context.onKey.handle(params ->{
|
|
||||||
// for (k => v in elementMap){
|
|
||||||
// if (k.isInside(params.pos)){
|
|
||||||
// if (v.eventListner.onKey != null){
|
|
||||||
// v.eventListner.onKey.invoke(params);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// context.onKeyUp.handle(params ->{
|
|
||||||
// for (k => v in elementMap){
|
|
||||||
// if (k.isInside(params.pos)){
|
|
||||||
// if (v.eventListner.onKeyUp != null){
|
|
||||||
// v.eventListner.onKeyUp.invoke(params);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// context.onMouseDrag.handle(params ->{
|
|
||||||
// for (k => v in elementMap){
|
|
||||||
// if (k.isInside(params.pos)){
|
|
||||||
// if (v.eventListner.onMouseDrag != null){
|
|
||||||
// v.eventListner.onMouseDrag.invoke(params);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
context.onMouseScroll.handle(params ->{
|
|
||||||
for (k => v in elementMap){
|
|
||||||
if (k.isInside(params.pos)){
|
|
||||||
if (v.eventListner.onMouseScroll != null){
|
|
||||||
v.eventListner.onMouseScroll.invoke(params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
context.onMouseUp.handle(params ->{
|
|
||||||
for (k => v in elementMap){
|
|
||||||
if (k.isInside(params.pos)){
|
|
||||||
if (v.eventListner.onMouseUp != null){
|
|
||||||
v.eventListner.onMouseUp.invoke(params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// context.onPaste.handle(params ->{
|
|
||||||
// for (k => v in elementMap){
|
|
||||||
// if (k.isInside(params.pos)){
|
|
||||||
// if (v.eventListner.onPaste != null){
|
|
||||||
// v.eventListner.onPaste.invoke(params);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function render() {
|
|
||||||
var size = context.getSize();
|
|
||||||
var result = renderChildren(children, size);
|
|
||||||
this.elementMap = result.map;
|
|
||||||
|
|
||||||
writeToContext(result.canvas);
|
|
||||||
}
|
|
||||||
|
|
||||||
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<UIElement>, bounds:Vec2<Int>):{canvas: Canvas, map: Map<Rect,UIElement>} {
|
|
||||||
var canvas:Canvas = new Canvas();
|
|
||||||
var elementMap: Map<Rect,UIElement> = new Map();
|
|
||||||
|
|
||||||
var writePoint:Pos = {x: 0, y: 0};
|
|
||||||
|
|
||||||
for (child in children) {
|
|
||||||
if (bounds.y - writePoint.y <= 0) {
|
|
||||||
// No more space to render children
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var childRender = child.render({
|
|
||||||
x: bounds.x,
|
|
||||||
y: bounds.y - writePoint.y
|
|
||||||
},writePoint);
|
|
||||||
|
|
||||||
canvas.combine(childRender, writePoint);
|
|
||||||
elementMap.set(new Rect(writePoint,{x: childRender.maxWidth() + writePoint.x, y: writePoint.y + (childRender.height() - 1)}),child);
|
|
||||||
|
|
||||||
writePoint = {x: 0, y: writePoint.y + childRender.height()};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {canvas: canvas,map: elementMap};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
package lib.ui.reactive;
|
|
||||||
|
|
||||||
import lib.ui.Dimensions;
|
|
||||||
import util.ObjMerge;
|
|
||||||
import lib.Pos;
|
|
||||||
import util.Observable;
|
|
||||||
import lib.Color;
|
|
||||||
import lib.Vec.Vec2;
|
|
||||||
|
|
||||||
using tink.CoreApi;
|
|
||||||
|
|
||||||
typedef TextElementArgs = {
|
|
||||||
public var ?text:Observable<String>;
|
|
||||||
public var ?simpleText: String;
|
|
||||||
public var ?bg:Color;
|
|
||||||
public var ?fg:Color;
|
|
||||||
public var ?padding: Dimensions;
|
|
||||||
public var ?margin: Dimensions;
|
|
||||||
}
|
|
||||||
|
|
||||||
class TextElement extends UIElement {
|
|
||||||
private var args:TextElementArgs;
|
|
||||||
|
|
||||||
public function new(args: TextElementArgs,events: UIEvents = null) {
|
|
||||||
super(events);
|
|
||||||
this.args = args;
|
|
||||||
|
|
||||||
if (args.text != null) {
|
|
||||||
args.text.subscribe(value -> {
|
|
||||||
this.changedTrigger.trigger(null);
|
|
||||||
});
|
|
||||||
}else if (args.simpleText == null) {
|
|
||||||
throw new Error("text or simpleText must be set");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getText() {
|
|
||||||
if (args.text != null) {
|
|
||||||
return args.text.get();
|
|
||||||
} else {
|
|
||||||
return args.simpleText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function render(bounds:Vec2<Int>,offset: Pos):Canvas {
|
|
||||||
var rtn = new Canvas();
|
|
||||||
|
|
||||||
var fullText = getText();
|
|
||||||
|
|
||||||
var writePoint: Pos = {x: 0,y: 0};
|
|
||||||
|
|
||||||
for (pos in 0...fullText.length) {
|
|
||||||
var char = fullText.charAt(pos);
|
|
||||||
|
|
||||||
if (char == "\n"){
|
|
||||||
writePoint = {x: 0, y: writePoint.y + 1};
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (writePoint.y >= bounds.y) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (writePoint.x >= bounds.x) {
|
|
||||||
writePoint = {x: 0, y: writePoint.y + 1};
|
|
||||||
}
|
|
||||||
|
|
||||||
rtn.set(writePoint, {char: char,textColor: this.args.fg, bg: this.args.bg});
|
|
||||||
|
|
||||||
writePoint = {x: writePoint.x + 1, y: writePoint.y};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return UIElement.applyPaddignAndMargin(rtn, this.args.padding, this.args.margin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
package lib.ui.reactive;
|
|
||||||
|
|
||||||
import lib.Debug;
|
|
||||||
import lib.Vec.Vec2;
|
|
||||||
import lib.Pos;
|
|
||||||
|
|
||||||
class TurtleController extends UIElement {
|
|
||||||
|
|
||||||
public function new() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function addButton(label:String): Canvas {
|
|
||||||
var txt = new TextElement({simpleText: '$label', fg: Red,bg: Orange});
|
|
||||||
return txt.render({x:3,y:3},{x:0,y:0});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function render(bounds:Vec2<Int>, offset:Pos):Canvas {
|
|
||||||
var canvas: Canvas = new Canvas();
|
|
||||||
|
|
||||||
canvas.combine(addButton("F"),{x:1,y:1});
|
|
||||||
canvas.combine(addButton("F"),{x:5,y:2});
|
|
||||||
|
|
||||||
return canvas;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// var innerText = switch (sqr) {
|
|
||||||
// case 0: "D";
|
|
||||||
// case 1: "F";
|
|
||||||
// case 2: "U";
|
|
||||||
// case 3: "L";
|
|
||||||
// case 4: "B";
|
|
||||||
// case 5: "R";
|
|
||||||
// case 6: "D";
|
|
||||||
// case 7: "D";
|
|
||||||
// case 8: "D";
|
|
||||||
// default: "?";
|
|
||||||
// };
|
|
||||||
|
|
||||||
// canvas.set({x: offsetX + 0, y: offsetY + 0}, {char: borderChar,bg: bgColor,textColor: textColor});
|
|
||||||
// canvas.set({x: offsetX + 1, y: offsetY + 0}, {char: borderChar,bg: bgColor,textColor: textColor});
|
|
||||||
// canvas.set({x: offsetX + 2, y: offsetY + 0}, {char: borderChar,bg: bgColor,textColor: textColor});
|
|
||||||
// canvas.set({x: offsetX + 3, y: offsetY + 0}, {char: " ",bg: spaceColor,textColor: textColor});
|
|
||||||
|
|
||||||
// canvas.set({x: offsetX + 0, y: offsetY + 1}, {char: borderChar,bg: bgColor,textColor: textColor});
|
|
||||||
// canvas.set({x: offsetX + 1, y: offsetY + 1}, {char: innerText,bg: bgColor,textColor: textColor});
|
|
||||||
// canvas.set({x: offsetX + 2, y: offsetY + 1}, {char: borderChar,bg: bgColor,textColor: textColor});
|
|
||||||
// canvas.set({x: offsetX + 3, y: offsetY + 1}, {char: " ",bg: spaceColor,textColor: textColor});
|
|
||||||
|
|
||||||
// canvas.set({x: offsetX + 0, y: offsetY + 2}, {char: borderChar,bg: bgColor,textColor: textColor});
|
|
||||||
// canvas.set({x: offsetX + 1, y: offsetY + 2}, {char: borderChar,bg: bgColor,textColor: textColor});
|
|
||||||
// canvas.set({x: offsetX + 2, y: offsetY + 2}, {char: borderChar,bg: bgColor,textColor: textColor});
|
|
||||||
// canvas.set({x: offsetX + 3, y: offsetY + 2}, {char: " ",bg: spaceColor,textColor: textColor});
|
|
||||||
|
|
||||||
// canvas.set({x: offsetX + 0, y: offsetY + 3}, {char: " ",bg: spaceColor,textColor: textColor});
|
|
||||||
// canvas.set({x: offsetX + 1, y: offsetY + 3}, {char: " ",bg: spaceColor,textColor: textColor});
|
|
||||||
// canvas.set({x: offsetX + 2, y: offsetY + 3}, {char: " ",bg: spaceColor,textColor: textColor});
|
|
||||||
// canvas.set({x: offsetX + 3, y: offsetY + 3}, {char: " ",bg: spaceColor,textColor: textColor});
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
package lib.ui.reactive;
|
|
||||||
|
|
||||||
import util.ObjMerge;
|
|
||||||
import lib.Pos;
|
|
||||||
import lib.Rect;
|
|
||||||
import lib.Vec.Vec2;
|
|
||||||
|
|
||||||
using tink.CoreApi;
|
|
||||||
|
|
||||||
abstract class UIElement {
|
|
||||||
/**
|
|
||||||
Render the element inside the bounds. `offset` is the offset to the parents position
|
|
||||||
and can be used to calculate the absolute position of element.
|
|
||||||
Just save `offset` and pass it to the children.
|
|
||||||
**/
|
|
||||||
abstract public function render(bounds:Vec2<Int>, offset: Pos):Canvas;
|
|
||||||
public var changed(default, null):Signal<Noise>;
|
|
||||||
private final changedTrigger:SignalTrigger<Noise> = Signal.trigger();
|
|
||||||
public final eventListner:UIEvents = {};
|
|
||||||
|
|
||||||
public function new(events: UIEvents = null) {
|
|
||||||
changed = changedTrigger.asSignal();
|
|
||||||
if (events != null){
|
|
||||||
this.eventListner = events;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getElementInMap(pos: Pos,elementMap: Map<Rect,UIElement>):Null<UIElement>{
|
|
||||||
for (k => v in elementMap){
|
|
||||||
if (k.isInside(pos)){
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
public static function applyPaddignAndMargin(canvas:Canvas,padding: Dimensions, margin: Dimensions):Canvas{
|
|
||||||
var passing = ObjMerge.merge(padding, {top: 0, left: 0, bottom: 0, right: 0});
|
|
||||||
var margin = ObjMerge.merge(margin, {top: 0, left: 0, bottom: 0, right: 0});
|
|
||||||
var rtn = new Canvas();
|
|
||||||
|
|
||||||
rtn.combine(canvas,{x:margin.left + padding.left,y: margin.top + padding.top});
|
|
||||||
|
|
||||||
return rtn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
45
src/macros/DCEHack.hx
Normal file
45
src/macros/DCEHack.hx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package macros;
|
||||||
|
|
||||||
|
import haxe.macro.Context;
|
||||||
|
import haxe.macro.Expr;
|
||||||
|
using Lambda;
|
||||||
|
|
||||||
|
|
||||||
|
class DCEHack {
|
||||||
|
|
||||||
|
public static final classes: Array<haxe.macro.Type> = [];
|
||||||
|
|
||||||
|
macro static public function dceInclude(): Array<Field> {
|
||||||
|
#if !display
|
||||||
|
var localClass = Context.getLocalClass();
|
||||||
|
|
||||||
|
if (localClass == null){
|
||||||
|
return Context.getBuildFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore abstract classes
|
||||||
|
if (localClass.get().isAbstract){
|
||||||
|
return Context.getBuildFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
classes.push(Context.getLocalType());
|
||||||
|
#end
|
||||||
|
return Context.getBuildFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
macro static public function dceGenerateCreate(){
|
||||||
|
var exprs = [];
|
||||||
|
|
||||||
|
for (c in classes){
|
||||||
|
switch (c){
|
||||||
|
case TInst(_.get() => t, _):
|
||||||
|
var path: TypePath = {pack: t.pack, name: t.name};
|
||||||
|
exprs.push(macro new $path());
|
||||||
|
default:
|
||||||
|
Context.error("Unknown type: " + c, Context.currentPos());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return macro return $a{exprs};
|
||||||
|
}
|
||||||
|
}
|
||||||
77
src/macros/Exporter.hx
Normal file
77
src/macros/Exporter.hx
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package macros;
|
||||||
|
|
||||||
|
import haxe.macro.Context;
|
||||||
|
import haxe.macro.Expr;
|
||||||
|
using Lambda;
|
||||||
|
|
||||||
|
class Exporter {
|
||||||
|
macro public static function buildExport():Array<haxe.macro.Expr.Field> {
|
||||||
|
var fields = Context.getBuildFields();
|
||||||
|
|
||||||
|
var getExp = [];
|
||||||
|
|
||||||
|
for (field in fields){
|
||||||
|
if (field.meta == null) continue;
|
||||||
|
|
||||||
|
var s = "";
|
||||||
|
for (meta in field.meta){
|
||||||
|
if (meta.name == "export"){
|
||||||
|
switch (meta.params[0].expr){
|
||||||
|
case EConst(CString(s1)):
|
||||||
|
s = s1;
|
||||||
|
default:
|
||||||
|
Context.error("Invalid export name", meta.pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s == "") continue;
|
||||||
|
|
||||||
|
switch (field.kind){
|
||||||
|
case FFun(f):
|
||||||
|
var funName = field.name;
|
||||||
|
switch (f.ret){
|
||||||
|
case TPath(p):
|
||||||
|
switch (p.name){
|
||||||
|
case "Int":
|
||||||
|
getExp.push( macro $v{s} => (i: Int) -> lib.exporter.Response.ValueType.Int(this.$funName()) );
|
||||||
|
case "Float":
|
||||||
|
getExp.push( macro $v{s} => (i: Int) -> lib.exporter.Response.ValueType.Float(this.$funName()) );
|
||||||
|
case "Bool":
|
||||||
|
getExp.push( macro $v{s} => (i: Int) -> lib.exporter.Response.ValueType.Bool(this.$funName()) );
|
||||||
|
case "String":
|
||||||
|
getExp.push( macro $v{s} => (i: Int) -> lib.exporter.Response.ValueType.String(this.$funName()) );
|
||||||
|
default:
|
||||||
|
Context.error("Only Int, Float, Bool and String can be exported", field.pos);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
Context.error("Only functions returning a type can be exported", field.pos);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
Context.error("Only functions can be exported", field.pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var exportField: Field = {
|
||||||
|
name: "export",
|
||||||
|
pos: Context.currentPos(),
|
||||||
|
kind: FFun({
|
||||||
|
args: [],
|
||||||
|
ret: TPath({ name: "ExportConfig", pack: []}),
|
||||||
|
expr: macro {
|
||||||
|
return {
|
||||||
|
getDelegates: $a{getExp},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
access: [APublic],
|
||||||
|
doc: null,
|
||||||
|
meta: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
fields.push(exportField);
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
79
src/macros/rpc/RPC.hx
Normal file
79
src/macros/rpc/RPC.hx
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package macros.rpc;
|
||||||
|
|
||||||
|
import haxe.macro.Context;
|
||||||
|
import haxe.macro.Expr;
|
||||||
|
using Lambda;
|
||||||
|
|
||||||
|
class RPC {
|
||||||
|
macro static public function buildRPC(): Array<Field> {
|
||||||
|
var fields = Context.getBuildFields();
|
||||||
|
|
||||||
|
var className = Context.getLocalClass().get().name + "RPC";
|
||||||
|
|
||||||
|
var c = macro class $className extends macros.rpc.RPCBase {
|
||||||
|
public function new(id: kernel.net.Package.NetworkID) {
|
||||||
|
super(id,$v{className});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (field in fields){
|
||||||
|
if (field.meta == null) continue;
|
||||||
|
if (field.meta.exists((i) -> i.name == "rpc") == false) continue;
|
||||||
|
|
||||||
|
switch (field.kind){
|
||||||
|
case FFun(f):
|
||||||
|
c.fields.push({
|
||||||
|
name: field.name,
|
||||||
|
pos: field.pos,
|
||||||
|
kind: FFun({
|
||||||
|
args: f.args,
|
||||||
|
expr: macro {
|
||||||
|
return cast this._performRequest($v{field.name},[]);
|
||||||
|
},
|
||||||
|
ret: TPath({name: "Promise", params: [TPType(f.ret)], pack: ["tink","core"]}),
|
||||||
|
}),
|
||||||
|
access: [APublic],
|
||||||
|
doc: null,
|
||||||
|
meta: [],
|
||||||
|
});
|
||||||
|
default:
|
||||||
|
Context.error("Only functions can be used for rpc", field.pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
haxe.macro.Context.defineType(c);
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro static public function generateRPCPackageHandle() {
|
||||||
|
var proto = Context.getLocalClass().get().name + "RPC";
|
||||||
|
var exprs: Array<Expr> = [];
|
||||||
|
|
||||||
|
var fields = Context.getLocalClass().get().fields.get();
|
||||||
|
|
||||||
|
for (field in fields){
|
||||||
|
if (field.meta == null) continue;
|
||||||
|
if (!field.meta.has("rpc")) continue;
|
||||||
|
|
||||||
|
switch (field.kind){
|
||||||
|
case FMethod(k):
|
||||||
|
var funName = field.name;
|
||||||
|
exprs.push(macro {
|
||||||
|
if (pack.data.func == $v{funName}){
|
||||||
|
pack.respond(this.$funName());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
default:
|
||||||
|
Context.error("Only functions can be used for rpc", field.pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return macro {
|
||||||
|
kernel.net.Net.instance.registerProto($v{proto},(pack)->{
|
||||||
|
$a{exprs}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/macros/rpc/RPCBase.hx
Normal file
29
src/macros/rpc/RPCBase.hx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package macros.rpc;
|
||||||
|
|
||||||
|
import kernel.net.Net;
|
||||||
|
import kernel.net.Package.NetworkID;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
abstract class RPCBase {
|
||||||
|
public final id:NetworkID;
|
||||||
|
private final _proto:String;
|
||||||
|
public function new(id: NetworkID, proto: String) {
|
||||||
|
this.id = id;
|
||||||
|
this._proto = proto;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _performRequest(func: String, args: Array<Dynamic>):Promise<Dynamic> {
|
||||||
|
return Net.instance.sendAndAwait(id, this._proto, {
|
||||||
|
func: func,
|
||||||
|
// args: args
|
||||||
|
}).map((res) -> {
|
||||||
|
switch (res){
|
||||||
|
case Success(pack):
|
||||||
|
return pack.data;
|
||||||
|
case Failure(_):
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user