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!");
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
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;
|
||||
|
||||
import kernel.log.Log;
|
||||
import kernel.ps.ProcessHandle;
|
||||
import kernel.ps.Process;
|
||||
|
||||
@@ -11,6 +12,13 @@ class HelloWorld implements Process {
|
||||
|
||||
public function run(handle:ProcessHandle) {
|
||||
handle.write("Hello World!");
|
||||
|
||||
var c = new HelloWorldServiceRPC(0);
|
||||
|
||||
c.getNumber().handle((res)->{
|
||||
Log.debug("Got number: " + res);
|
||||
});
|
||||
|
||||
handle.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
package bin;
|
||||
|
||||
import kernel.Timer;
|
||||
import macros.rpc.RPC;
|
||||
import kernel.ps.ProcessHandle;
|
||||
import kernel.ps.Process;
|
||||
|
||||
using tink.CoreApi;
|
||||
|
||||
@:build(macros.rpc.RPC.buildRPC())
|
||||
class HelloWorldService implements Process {
|
||||
private var timer:Timer;
|
||||
private var handle:ProcessHandle;
|
||||
|
||||
public function new() {}
|
||||
|
||||
public function run(handle:ProcessHandle) {
|
||||
handle.write("Hello World! Started\n");
|
||||
this.startTimer(handle);
|
||||
handle.addDeferFunc(()->{
|
||||
timer.cancle();
|
||||
});
|
||||
this.handle = handle;
|
||||
|
||||
RPC.generateRPCPackageHandle();
|
||||
}
|
||||
|
||||
public function startTimer(handle: ProcessHandle) {
|
||||
this.timer = new Timer(5, function() {
|
||||
handle.write("Hello World!\n");
|
||||
this.startTimer(handle);
|
||||
});
|
||||
@rpc
|
||||
public function getNumber():Int{
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
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;
|
||||
});
|
||||
}, "<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 = {
|
||||
public var success:Bool;
|
||||
}
|
||||
|
||||
typedef ListRequest = {
|
||||
public var ?type:String;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@ class SiteRessourceController implements Process {
|
||||
pkg.respond(handleUnregister(cast pkg.data));
|
||||
case "get":
|
||||
pkg.respond(handleGet(cast pkg.data));
|
||||
case "list":
|
||||
pkg.respond(list());
|
||||
default:
|
||||
handle.writeLine("Unknown message type: " + pkg.data.type);
|
||||
}
|
||||
@@ -84,6 +86,10 @@ class SiteRessourceController implements Process {
|
||||
return ressources.get(name);
|
||||
}
|
||||
|
||||
private inline function list():Array<String> {
|
||||
return [ for (k in ressources.keys()) k];
|
||||
}
|
||||
|
||||
private function load() {
|
||||
var store = KVStore.getStoreForClass();
|
||||
var data:Null<Map<String, NetworkID>> = store.get("ressources");
|
||||
|
||||
@@ -3,26 +3,7 @@ package kernel;
|
||||
@:keep
|
||||
class DCEHack {
|
||||
// Dont actually call this
|
||||
public static function load() {
|
||||
return [
|
||||
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(),
|
||||
];
|
||||
public static function load():Array<kernel.ps.Process>{
|
||||
macros.DCEHack.dceGenerateCreate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ class Init {
|
||||
|
||||
// Register default terminate handler
|
||||
KernelEvents.instance.onTerminate.handle(_->{
|
||||
OS.reboot();
|
||||
KernelEvents.instance.shutdown();
|
||||
});
|
||||
|
||||
Debug.printBuildInfo();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package kernel;
|
||||
|
||||
import haxe.MainLoop;
|
||||
import cc.OS;
|
||||
import kernel.peripherals.Peripherals.Peripheral;
|
||||
import kernel.log.Log;
|
||||
import lib.Pos;
|
||||
import cc.HTTP.HTTPResponse;
|
||||
@@ -150,6 +149,12 @@ class KernelEvents {
|
||||
}
|
||||
|
||||
public function shutdown() {
|
||||
|
||||
// clearing screens
|
||||
for (screen in Peripheral.instance.getAllScreens()) {
|
||||
screen.reset();
|
||||
}
|
||||
|
||||
Log.info('Shutting down event loop');
|
||||
this.stopLoop = true;
|
||||
MainTerm.instance.reset();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package kernel.binstore;
|
||||
|
||||
import bin.pathfinder.PFClient;
|
||||
import bin.ID;
|
||||
import bin.exporter.Res;
|
||||
import bin.exporter.ResManager;
|
||||
@@ -41,7 +42,8 @@ class BinStore {
|
||||
{c: KSettings, name: "KSettings", aliases: ["ksettings","ks"]},
|
||||
{c: ResManager, name: "ResManager", aliases: ["resmanager","resmgr"]},
|
||||
{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)
|
||||
@@ -68,4 +70,12 @@ class BinStore {
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
private function findAddrByType(type: String): Array<String> {
|
||||
public function findAddrByType(type: String): Array<String> {
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
|
||||
@export("output")
|
||||
public inline function getOutput(): Bool {
|
||||
return cc.Redstone.getOutput(this.addr);
|
||||
}
|
||||
|
||||
@export("input")
|
||||
public inline function getInput(): Bool {
|
||||
return cc.Redstone.getInput(this.addr);
|
||||
}
|
||||
@@ -126,14 +129,4 @@ class Redstone implements IPeripheral implements IExportable {
|
||||
public inline function testBundledInput(mask: Color): Bool {
|
||||
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.
|
||||
**/
|
||||
@:autoBuild(macros.DCEHack.DCEHack.dceInclude())
|
||||
interface Process {
|
||||
public function run(handle: ProcessHandle): Void;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ class StatelessVirtualTermWriter implements VirtualTermWriter {
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
target.reset();
|
||||
renderFunc();
|
||||
renderRequested = false;
|
||||
}else{
|
||||
@@ -84,6 +85,8 @@ class StatelessVirtualTermWriter implements VirtualTermWriter {
|
||||
});
|
||||
|
||||
target = newTarget;
|
||||
|
||||
target.reset();
|
||||
}
|
||||
|
||||
public function isEnabled():Bool {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
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.ProcessManager;
|
||||
import bin.KernelLog;
|
||||
@@ -24,6 +28,15 @@ class HomeContext {
|
||||
private var requestRender: Void->Void;
|
||||
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 run() {
|
||||
@@ -34,6 +47,7 @@ class HomeContext {
|
||||
WindowManager.instance.focusContextToOutput(ctx, "main");
|
||||
|
||||
renderer = new RootElement();
|
||||
renderer.setTitle("Home");
|
||||
|
||||
ctx.delegateEvents(renderer);
|
||||
stateless.setRenderFunc(this.render);
|
||||
@@ -71,7 +85,7 @@ class HomeContext {
|
||||
|
||||
private function focusContext(id:Int) {
|
||||
if (workspaces.exists(id)) {
|
||||
WindowManager.instance.focusContextToOutput(workspaces[id], "main");
|
||||
WindowManager.instance.focusContextToOutput(workspaces[id], selectedOutput);
|
||||
currentWorkspace = id;
|
||||
}
|
||||
}
|
||||
@@ -90,7 +104,15 @@ class HomeContext {
|
||||
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 lastContextID = -1;
|
||||
|
||||
@@ -102,25 +124,51 @@ class HomeContext {
|
||||
return;
|
||||
}
|
||||
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() {
|
||||
ctx.clear();
|
||||
ctx.setCursorBlink(false);
|
||||
|
||||
var workspaceIDs:Array<Int> = [for (k=>v in workspaces) k];
|
||||
workspaceIDs.sort((a, b) -> a - b);
|
||||
|
||||
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())}));
|
||||
children.push(new TextElement('Add Log', {onClick: this.spawnPs.bind(new KernelLog())}));
|
||||
children.push(new TextElement('Exit', {onClick: KernelEvents.instance.shutdown}));
|
||||
for (i in 0...listedApps.length) {
|
||||
children.push(new TextElement(
|
||||
'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.render().renderToContext(ctx);
|
||||
renderer.render(ctx.getSize()).renderToContext(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package lib;
|
||||
|
||||
import bin.srsc.PackageTypes.ListRequest;
|
||||
import kernel.KernelSettings;
|
||||
import bin.srsc.PackageTypes.UnregisterRequest;
|
||||
import kernel.log.Log;
|
||||
import bin.srsc.PackageTypes.RegisterRequest;
|
||||
import bin.srsc.PackageTypes.GetRequest;
|
||||
import bin.srsc.SiteRessourceController;
|
||||
@@ -62,4 +62,23 @@ class RessourceNames {
|
||||
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 {
|
||||
private var children:Array<UIElement>;
|
||||
private final eventManager:UIEventManager = new UIEventManager();
|
||||
private var title:String = "";
|
||||
|
||||
public function new(?children:Array<UIElement>) {
|
||||
if (children == null) {
|
||||
@@ -20,14 +21,21 @@ class RootElement implements UIElement {
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
public function render():Canvas {
|
||||
public function render(bounds: Pos):Canvas {
|
||||
var canvas = new Canvas();
|
||||
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();
|
||||
|
||||
for (child in children) {
|
||||
var childCanvas = child.render();
|
||||
var childCanvas = child.render(bounds);
|
||||
var bounds = childCanvas.getBounds();
|
||||
bounds.offset(offset);
|
||||
|
||||
@@ -39,4 +47,12 @@ class RootElement implements UIElement {
|
||||
|
||||
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;
|
||||
|
||||
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.uiEvents = uiEvents;
|
||||
this.uiEvents = props?.uiEvents;
|
||||
this.style = props?.style ?? {fgColor: White, bgColor: Black};
|
||||
}
|
||||
|
||||
public function set(text:String) {
|
||||
@@ -24,11 +26,11 @@ class TextElement implements UIElement {
|
||||
return uiEvents;
|
||||
}
|
||||
|
||||
public function render():Canvas {
|
||||
public function render(bounds: Pos):Canvas {
|
||||
var canvas = new Canvas();
|
||||
for (i in 0...this.text.length) {
|
||||
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;
|
||||
|
||||
@@ -3,5 +3,5 @@ package lib.ui.elements;
|
||||
import lib.ui.rendere.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