initial commit

This commit is contained in:
2021-12-20 01:55:30 +01:00
commit bd790c1488
38 changed files with 2320 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
package kernel;
import cc.OS;
import lua.Coroutine;
import util.EventBus;
using lua.Table;
/**
Class for interacting with the native pullEvent system.
**/
class KernelEvents{
public static final instance:KernelEvents = new KernelEvents();
private function new () {}
private var eventBus: util.EventBus<Array<Dynamic>> = new EventBus();
/**
Start pulling events. Blocking.
**/
public function startEventLoop() {
// Log.info("Starting event loop");
while (true){
var event:Table<Int, Dynamic> = OS.pullEventRaw();
var eventName:String = event[1];
if (eventName == "terminate"){
return;
}
eventBus.emit(eventName,event.toArray());
}
}
public function on(eventName:String, callback:Array<Dynamic> -> Void):EventBusListner<Array<Dynamic>> {
return eventBus.on(eventName,callback);
}
public function once(eventName:String, callback:Array<Dynamic> -> Void):EventBusListner<Array<Dynamic>> {
return eventBus.once(eventName,callback);
}
public function removeListner(id:EventBusListner<Array<Dynamic>>) {
return eventBus.removeListner(id);
}
}

38
src/kernel/Log.hx Normal file
View File

@@ -0,0 +1,38 @@
package kernel;
import kernel.ui.WindowContext;
import kernel.ui.WindowManager;
import lib.TermWriteable;
import lib.TermIO;
/**
Log messages to specified output.
**/
class Log {
private static final context:WindowContext = WindowManager.instance.createNewContext();
private static var writer:TermIO = new TermIO(context);
private static function setMainoutout(newOutput: TermWriteable) {
writer = new TermIO(newOutput);
}
public static function info(msg: Dynamic, ?pos:haxe.PosInfos){
writer.writeLn("[INFO]["+pos.className+"]: "+Std.string(msg));
}
public static function warn(msg: Dynamic, ?pos:haxe.PosInfos){
writer.writeLn("[WARN]["+pos.className+"]: "+Std.string(msg),Yellow);
}
public static function error(msg: Dynamic,?pos:haxe.PosInfos) {
writer.writeLn("[ERRO]["+pos.className+"]: "+Std.string(msg),Red);
}
public static function debug(msg: Dynamic,?pos:haxe.PosInfos) {
writer.writeLn("[DEBG]["+pos.className+"]: "+Std.string(msg),Gray);
}
public static function moveToOutput(addr: String) {
WindowManager.instance.focusContextToOutput(context,addr);
}
}

91
src/kernel/MainTerm.hx Normal file
View File

@@ -0,0 +1,91 @@
package kernel;
import util.Signal;
import lib.TermWriteable;
import cc.Term;
import util.Vec.Vec2;
import util.Color;
/**
Represents the main computer screen.
**/
class MainTerm implements TermWriteable{
public static final instance:MainTerm = new MainTerm();
private function new() {
KernelEvents.instance.on("term_resize",params ->{
_onResize.emit(null);
});
}
public var onResize(get, null):SignalReadonly<Vec2<Int>>;
private var _onResize:Signal<Vec2<Int>> = new Signal();
function get_onResize():SignalReadonly<Vec2<Int>> {
return _onResize;
}
public function write(text:String) {
Term.write(text);
}
public function scroll(y:Int) {
Term.scroll(y);
}
public function getCursorPos():Vec2<Int> {
var rtn = Term.getCursorPos();
return {
x: rtn.x - 1,
y: rtn.y - 1
}
}
public function setCursorPos(x:Int, y:Int) {
Term.setCursorPos(x + 1,y + 1);
}
public function getCursorBlink():Bool {
// Missing in api
throw new haxe.exceptions.NotImplementedException();
}
public function setCursorBlink(blink:Bool) {
Term.setCursorBlink(blink);
}
public function getSize():Vec2<Int> {
var rtn = Term.getSize();
return {
x: rtn.width,
y: rtn.height,
}
}
public function clear() {
Term.clear();
}
public function clearLine() {
Term.clearLine();
}
public function getTextColor():Color {
return ColorConvert.ccToColor(Term.getTextColor());
}
public function setTextColor(colour:Color) {
Term.setTextColor(ColorConvert.colorToCC(colour));
}
public function getBackgroundColor():Color {
return ColorConvert.ccToColor(Term.getBackgroundColor());
}
public function setBackgroundColor(color:Color) {
Term.setBackgroundColor(ColorConvert.colorToCC(color));
}
public function isColor():Bool {
return Term.isColor();
}
}

36
src/kernel/Timer.hx Normal file
View File

@@ -0,0 +1,36 @@
package kernel;
import util.EventBus.EventBusListner;
import cc.OS;
/**
Wrapper class for using timer.
**/
class Timer {
private final timerID:Int;
private final callback:Void->Void;
private final timerListner:EventBusListner<Array<Dynamic>>;
/**
Create new timer with timeout in seconds.
**/
public function new(timeout: Int, cb: Void->Void) {
timerID = OS.startTimer(timeout);
callback = cb;
timerListner = KernelEvents.instance.on("timer",(params)->{
if (params[1] == timerID){
cb();
KernelEvents.instance.removeListner(timerListner);
}
});
}
/**
Cancle timer.
**/
public function cancle() {
OS.cancelTimer(timerID);
KernelEvents.instance.removeListner(timerListner);
}
}

224
src/kernel/net/Net.hx Normal file
View File

@@ -0,0 +1,224 @@
package kernel.net;
import kernel.peripherals.Peripherals.Peripheral;
import kernel.Log;
import kernel.KernelEvents;
import haxe.Exception;
import util.Promise;
import kernel.Timer;
import util.EventBus;
import cc.OS;
using Lambda;
using util.Extender.LambdaExtender;
/**
Class responsible for everything network related.
Used to send and recceive packages.
**/
class Net{
public static final instance:Net = new Net();
private function new () {}
public static inline final BRODCAST_PORT:Int = 65533;
public static inline final MESSAGE_TIMEOUT:Int = 3;
private var networkID:Int = OS.getComputerID();
private var responseBus: util.EventBus<Package> = new EventBus();
private var protoHandlers: Map<String,Package -> Void> = new Map();
private var allModems:Array<kernel.peripherals.Modem>;
private var routingTable: Map<Int,kernel.peripherals.Modem> = new Map();
public function init() {
KernelEvents.instance.on("modem_message",(params)->{
var pack = Package.fromEvent(params);
handelIncomming(pack,params[1]);
});
allModems = Peripheral.instance.getModems();
open();
discoverNeighbors();
}
private function handelIncomming(pack: Package, ?addr:String) {
switch pack.type {
case Data(_):
routeTo(pack);
case DataNoResponse(_):
routeTo(pack);
case Response | RouteDiscoverResponse:
responseBus.emit(Std.string(pack.msgID),pack);
case RouteDiscover:
handleRoute(pack,addr);
}
}
private function newRoutPackage(): Package {
var pack: Package = {
type: RouteDiscover,
toID: BRODCAST_PORT,
msgID: generateMessageID(),
fromID: networkID,
data: null,
}
return pack;
}
private function discoverNeighbors() {
for (modem in allModems) {
var pack = newRoutPackage();
var timeout: Timer = null;
var responeListner = responseBus.on(Std.string(pack.msgID),pack -> {
addRoute(pack.fromID,modem.addr);
});
timeout = new Timer(MESSAGE_TIMEOUT,() -> {
responseBus.removeListner(responeListner);
});
modem.transmit(BRODCAST_PORT,OS.getComputerID(),pack);
}
}
private function handleRoute(pack: Package, addr: String) {
addRoute(pack.fromID,addr);
// Respond to peer
var response: Package = {
toID: pack.fromID,
fromID: OS.getComputerID(),
msgID: pack.msgID,
type: RouteDiscoverResponse,
data: null
}
for (reponseModem in allModems.filter(m -> m.addr == addr)) {
reponseModem.transmit(pack.fromID,networkID,response);
}
}
private function addRoute(toID: Int,addr: String) {
Log.debug("Added new route to "+toID+" via "+addr);
routingTable.set(toID,allModems.find(item -> item.addr == addr));
}
public function getAllNeighbors(): Array<Int> {
return routingTable.mapi((index, item) -> index);
}
private function generateMessageID(): Int {
return Std.random(2147483647); // TODO: better uniqe number
}
/**
Open all wireless and wired modem.
**/
private function open() {
for (m in allModems) {
m.open(networkID);
m.open(BRODCAST_PORT);
}
}
/**
Send a message. Dont care if its reaches its destination nor it has a response.
**/
public function sendAndForget(dest:Int,proto:String,data: Dynamic){
var pack: Package = {
toID: dest,
fromID: networkID,
msgID: generateMessageID(),
type: DataNoResponse(proto),
data: data
}
sendRaw(pack);
}
public function respondTo(pack: Package,data: Dynamic) {
if (pack.type.match(DataNoResponse(_))){
Log.warn("Responed to a no response package. Ignoring");
return;
}
var response = pack.createResponse(data);
sendRaw(response);
}
private function routeTo(pack: Package) {
var proto: String = switch pack.type {
case Data(proto):
proto;
case DataNoResponse(proto):
proto;
case _:
return;
}
if (!protoHandlers.exists(proto) && protoHandlers[proto] != null){
return;
}
protoHandlers[proto](pack);
}
private function sendRaw(pack: Package){
if (pack.toID == networkID){
// Loopback
handelIncomming(pack);
}else{
if (routingTable.exists(pack.toID)){
routingTable[pack.toID].transmit(pack.toID,pack.fromID,pack);
}else{
// Route not found
// TODO: forward package or report not reachable
}
}
}
public function sendAndAwait(dest: Int,proto:String,data: Dynamic): Promise<Package> {
return new Promise<Package>((resolve, reject) -> {
var pack: Package = {
toID: dest,
fromID: networkID,
msgID: generateMessageID(),
type: Data(proto),
data: data
}
var timeout: Timer = null;
var responeListner = responseBus.once(Std.string(pack.msgID),p -> {
resolve(p);
if (timeout != null){
timeout.cancle();
}
});
timeout = new Timer(MESSAGE_TIMEOUT,() -> {
responseBus.removeListner(responeListner);
reject(new Exception("Timeout"));
});
sendRaw(pack);
});
}
public function registerProto(proto: String,cb: Package -> Void) {
if (protoHandlers.exists(proto)){
// Failed. Handler already exist.
// TODO: return error
return;
}
protoHandlers[proto] = cb;
}
public function removeProto(proto: String) {
protoHandlers.remove(proto);
}
}

55
src/kernel/net/Package.hx Normal file
View File

@@ -0,0 +1,55 @@
package kernel.net;
enum PackageTypes {
Data(proto: String);
DataNoResponse(proto: String);
Response;
RouteDiscover();
RouteDiscoverResponse();
}
/**
Representing a network package.
**/
@:structInit class Package {
public final fromID:Int;
public final toID:Int;
public final msgID:Int;
public final type:PackageTypes;
public final data:Dynamic;
/**
Parse package from an `modem_message` event.
**/
public static function fromEvent(params: Array<Dynamic>): Package {
var payload = params[4];
return {
fromID: params[3],
toID: params[2],
msgID: payload.msgID,
type: payload.type,
data: payload.data,
};
}
/**
Create package that can be used as a response.
**/
public function createResponse(newData: Dynamic): Package {
return {
toID: fromID,
fromID: toID,
msgID: msgID,
type: Response,
data: newData
};
}
/**
Wrapper for `Net.instance.respondTo`.
**/
public function respond(data: Dynamic) {
Net.instance.respondTo(this,data);
}
}

View File

@@ -0,0 +1,31 @@
package kernel.peripherals;
class Item {
}
class Inventory {
public function size():Int {
}
public function list(): Map<Int,Item> {
}
public function getItemDetail(slot: Int): Item {
}
public function pushItems(toName: String, fromSlot: Int, ?limit:Int, toSlot: Int): Int {
}
public function pullItems(fromName: String, fromSlot: Int, ?limit:Int, ?toSlot: Int): Int {
}
}

View File

@@ -0,0 +1,101 @@
package kernel.peripherals;
import haxe.exceptions.NotImplementedException;
import haxe.Exception;
using lua.Table;
class Modem {
private final nativ:cc.periphs.Modem.Modem;
public final addr:String;
@:allow(kernel.peripherals)
private function new(nativePeripherals: cc.periphs.Modem.Modem,addr: String) {
this.nativ = nativePeripherals;
this.addr = addr;
}
public function open(chan: Int) {
nativ.open(chan);
}
public function isOpen(chan: Int): Bool {
return nativ.isOpen(chan);
}
public function close(chan: Int) {
nativ.close(chan);
}
public function closAll() {
nativ.closeAll();
}
public function transmit(chan: Int,replyChan: Int,payload: Any) {
nativ.transmit(chan,replyChan,payload);
}
public function isWireless(): Bool {
return nativ.isWireless();
}
public function getNamesRemote():Array<String> {
if (isWireless()){
throw new Exception("'getNamesRemote' only works with wired modems");
}
return nativ.getNamesRemote().toArray();
}
public function isPresentRemote(name: String): Bool {
if (isWireless()){
throw new Exception("'isPresentRemote' only works with wired modems");
}
return nativ.isPresentRemote(name);
}
public function getTypeRemote(name: String): String {
if (isWireless()){
throw new Exception("'getTypeRemote' only works with wired modems");
}
return nativ.getTypeRemote(name);
}
public function hasTypeRemote(name:String, type:String):Bool {
if (isWireless()){
throw new Exception("'hasTypeRemote' only works with wired modems");
}
// Missing in upstream API
throw new haxe.exceptions.NotImplementedException();
// return nativ.hasRemoteType(name,type);
}
public function getMethodsRemote(name: String): Array<String> {
if (isWireless()){
throw new Exception("'getMethodsRemote' only works with wired modems");
}
return nativ.getMethodsRemote(name).toArray();
}
public function callRemote(remoteName: String, method:String):Dynamic {
if (isWireless()){
throw new Exception("'callRemote' only works with wired modems");
}
// TODO: implment or solve differently
throw new haxe.exceptions.NotImplementedException();
}
public function getNameLocal(): String {
if (isWireless()){
throw new Exception("'getNameLocal' only works with wired modems");
}
return nativ.getNameLocal();
}
}

View File

@@ -0,0 +1,57 @@
package kernel.peripherals;
import kernel.peripherals.Modem;
import kernel.peripherals.Screen;
using lua.Table;
using Lambda;
/**
Class responseable for retrieving peripherals.
**/
class Peripheral {
public static final instance:Peripheral = new Peripheral();
private function new() {}
/**
Get all connected screens.
**/
public function getScreens(): Array<Screen> {
var allScreens = cc.Peripheral.getNames().toArray().filter(s -> cc.Peripheral.getType(s) == "monitor");
return allScreens.map(s -> return new Screen((cc.Peripheral.wrap(s):Dynamic),s));
}
public function getScreen(addr: String): Screen {
if (!getAllPeripheralsAddr().exists(item -> item == addr)){
return null;
}
return new Screen((cc.Peripheral.wrap(addr):Dynamic),addr);
}
/**
Get all connected modems.
**/
public function getModems(): Array<Modem> {
var allModems = cc.Peripheral.getNames().toArray().filter(s -> cc.Peripheral.getType(s) == "modem");
return allModems.map(s -> return new Modem((cc.Peripheral.wrap(s): Dynamic),s));
}
/**
Get all connected wireless modems.
**/
public function getWirelessModems(): Array<Modem> {
return getModems().filter(modem -> return modem.isWireless());
}
/**
Get all connected wired modems.
**/
public function getWiredModems(): Array<Modem> {
return getModems().filter(modem -> return !modem.isWireless());
}
public function getAllPeripheralsAddr(): Array<String> {
return cc.Peripheral.getNames().toArray();
}
}

View File

@@ -0,0 +1,112 @@
package kernel.peripherals;
import util.Signal;
import cc.Term.TerminalSize;
import lib.TermWriteable;
import util.Vec.Vec2;
import util.Color;
class Screen implements TermWriteable{
private final nativ:cc.periphs.Monitor.Monitor;
private final addr:String;
@:allow(kernel.peripherals)
public function new(nativePeripherals: cc.periphs.Monitor.Monitor,addr: String) {
this.nativ = nativePeripherals;
this.addr = addr;
KernelEvents.instance.on("monitor_resize",params -> {
if (params[1] == this.addr){
_onResize.emit(null);
}
});
setTextScale(0.5);
}
public function getAddr(): String {
return this.addr;
}
public var onResize(get,null):SignalReadonly<Vec2<Int>>;
private final _onResize:Signal<Vec2<Int>> = new Signal();
function get_onResize():SignalReadonly<Vec2<Int>> {
return _onResize;
}
public function getTextScale(): Float {
return nativ.getTextScale();
}
public function setTextScale(scale: Float) {
nativ.setTextScale(scale);
}
public function write(text:String) {
nativ.write(text);
}
public function scroll(y:Int) {
nativ.scroll(y);
}
public function getCursorPos():Vec2<Int> {
var rtn = nativ.getCursorPos();
return {
x: rtn.x - 1,
y: rtn.y - 1
}
}
public function setCursorPos(x:Int, y:Int) {
nativ.setCursorPos(x + 1,y + 1);
}
public function getCursorBlink():Bool {
return nativ.getCursorBlink();
}
public function setCursorBlink(blink:Bool) {
nativ.setCursorBlink(blink);
}
public function getSize():Vec2<Int> {
// FIXME: this will not compile. Has to be changes upstream
var size:TerminalSize = nativ.getSize();
return {
x: size.width,
y: size.height,
}
}
public function clear() {
nativ.clear();
}
public function clearLine() {
nativ.clearLine();
}
public function getTextColor():Color {
return ColorConvert.ccToColor(nativ.getTextColor());
}
public function setTextColor(colour:Color) {
nativ.setTextColor(ColorConvert.colorToCC(colour));
}
public function getBackgroundColor():Color {
return ColorConvert.ccToColor(nativ.getBackgroundColor());
}
public function setBackgroundColor(color:Color) {
nativ.setBackgroundColor(ColorConvert.colorToCC(color));
}
public function isColor():Bool {
return nativ.isColor();
}
}

174
src/kernel/ui/TermBuffer.hx Normal file
View File

@@ -0,0 +1,174 @@
package kernel.ui;
import util.Signal;
import util.Vec.Vec2;
import util.Color;
import lib.TermWriteable;
@:structInit class Pixel {
public var char:String;
public var bg:Color;
public var textColor:Color;
}
class TermBuffer implements TermWriteable {
/**
format [y][x]. First index is the line. Second index the char in the line.
**/
private var screenBuffer: Array<Array<Pixel>>;
private var cursorPos: Vec2<Int> = {x: 0, y: 0};
private var currentTextColor: Color = White;
private var currentBgColor: Color = Black;
private var size: Vec2<Int> = {x: 51,y:19}; // Default size set to default size of the main terminal
public function new() {
initScreenBuffer(size);
}
private function setSize(size: Vec2<Int>) {
if (this.size != size){
this._onResize.emit(size);
}
this.size = size;
updateScreenBufferSize(size);
}
private function updateScreenBufferSize(size: Vec2<Int>) {
// TODO
}
private function initScreenBuffer(size: Vec2<Int>) {
screenBuffer = new Array();
for (y in 0...size.y){
screenBuffer[y] = new Array();
for (x in 0...size.x){
screenBuffer[y][x] = {
char: " ",
textColor: White,
bg: Black,
}
}
}
}
private function copyBufferToTarget(target: TermWriteable) {
target.setCursorPos(0,0);
target.setBackgroundColor(Black);
target.setTextColor(White);
var tmpFgColor: Color = White;
var tmpBgColor: Color = Black;
for (y => line in screenBuffer){
for(x => pixel in line){
if (tmpFgColor != pixel.textColor){
tmpFgColor = pixel.textColor;
target.setTextColor(pixel.textColor);
}
if (tmpBgColor != pixel.bg){
tmpBgColor = pixel.bg;
target.setBackgroundColor(pixel.bg);
}
target.setCursorPos(x,y);
target.write(pixel.char);
}
}
target.setCursorPos(cursorPos.x,cursorPos.y);
target.setTextColor(currentTextColor);
target.setBackgroundColor(currentBgColor);
}
private function safeWriteScreenBuffer(pos: Vec2<Int>,char: String) {
if (screenBuffer.length > pos.y && screenBuffer[pos.y].length > pos.x){
screenBuffer[pos.y][pos.x].char = char;
screenBuffer[pos.y][pos.x].bg = currentBgColor;
screenBuffer[pos.y][pos.x].textColor = currentTextColor;
}
}
//
// TermWriteable functions
//
public var onResize(get,null):SignalReadonly<Vec2<Int>>;
private final _onResize:Signal<Vec2<Int>> = new Signal();
function get_onResize():Signal<Vec2<Int>> {
return _onResize;
}
public function write(text:String) {
for (i in 0...text.length){
safeWriteScreenBuffer({x: cursorPos.x,y: cursorPos.y},text.charAt(i));
cursorPos = {y: cursorPos.y,x: cursorPos.x + 1};
}
}
public function scroll(y:Int) {
screenBuffer.unshift([for (i in 0...size.x) {
char: " ",
textColor: White, // TODO: maybe replace with current bg/text color. Check nativ implementation
bg: Black
}]);
}
public function getCursorPos():Vec2<Int> {
return cursorPos;
}
public function setCursorPos(x:Int, y:Int) {
cursorPos = {
x: x,
y: y,
};
}
public function getCursorBlink():Bool {
throw new haxe.exceptions.NotImplementedException();
}
public function setCursorBlink(blink:Bool) {
// TODO
}
public function getSize():Vec2<Int> {
return size;
}
public function clear() {
initScreenBuffer(size);
}
public function clearLine() {
if (screenBuffer.length > cursorPos.y){
screenBuffer[cursorPos.y] = [for(x in 0...size.x){textColor: White,char: " ",bg: Black}];
}
}
public function getTextColor():Color {
return currentTextColor;
}
public function setTextColor(colour:Color) {
currentTextColor = colour;
}
public function getBackgroundColor():Color {
return currentBgColor;
}
public function setBackgroundColor(color:Color) {
currentBgColor = color;
}
public function isColor():Bool {
throw new haxe.exceptions.NotImplementedException();
}
}

View File

@@ -0,0 +1,164 @@
package kernel.ui;
import util.Signal.SignalListner;
import util.Vec.Vec2;
import util.Color;
import lib.TermWriteable;
class VirtualTermWriter implements TermWriteable extends TermBuffer {
private static final defaultSize:Vec2<Int> = {x: 50,y: 50};
private var target: TermWriteable;
private var enabled:Bool = false;
private var onResizeListner: SignalListner<Vec2<Int>>;
public function new(?target: TermWriteable) {
setTarget(target);
if (enabled){
enable();
}
super();
}
public function enable() {
if (target != null){
enabled = true;
super.copyBufferToTarget(target);
}
}
public function disable() {
enabled = false;
}
public inline function isEnabled(): Bool {
return enabled;
}
public function setTarget(newTarget: TermWriteable) {
if (newTarget != null){
super.setSize(newTarget.getSize());
// Remove old target event listner
if (onResizeListner != null && target != null){
target.onResize.remove(onResizeListner);
}
// Add new target event listner
onResizeListner = newTarget.onResize.on(newSize -> {
setSuperSize(newSize);
});
target = newTarget;
}
}
private function setSuperSize(size: Vec2<Int>) {
super.setSize(target.getSize());
}
//
// TermWriteable functions.
//
public override function write(text:String) {
if (isEnabled()){
target.write(text);
}
super.write(text);
}
public override function scroll(y:Int) {
if (isEnabled()){
target.scroll(y);
}
super.scroll(y);
}
public override function getCursorPos():Vec2<Int> {
if (isEnabled()){
return target.getCursorPos();
}else{
return super.getCursorPos();
}
}
public override function setCursorPos(x:Int, y:Int) {
if (isEnabled()){
target.setCursorPos(x,y);
}
super.setCursorPos(x,y);
}
public override function getCursorBlink():Bool {
throw new haxe.exceptions.NotImplementedException();
}
public override function setCursorBlink(blink:Bool) {
// TODO
}
public override function getSize():Vec2<Int> {
// TODO: make sense ?
if (target != null){
return target.getSize();
}
return defaultSize;
}
public override function clear() {
if (isEnabled()){
target.clear();
}
super.clear();
}
public override function clearLine() {
if (isEnabled()){
target.clearLine();
}
super.clearLine();
}
public override function getTextColor():Color {
if (isEnabled()){
return target.getTextColor();
}
return super.getTextColor();
}
public override function setTextColor(colour:Color) {
if (isEnabled()){
target.setTextColor(colour);
}
super.setTextColor(colour);
}
public override function getBackgroundColor():Color {
if (isEnabled()){
return target.getBackgroundColor();
}
return super.getBackgroundColor();
}
public override function setBackgroundColor(color:Color) {
if (isEnabled()){
target.setBackgroundColor(color);
}
super.setBackgroundColor(color);
}
public override function isColor():Bool {
throw new haxe.exceptions.NotImplementedException();
}
}

View File

@@ -0,0 +1,176 @@
package kernel.ui;
import util.Color;
import util.Signal;
import kernel.ui.WindowManager.ButtonType;
import util.Vec.Vec2;
import lib.TermWriteable;
class WindowContext implements TermWriteable {
private final writer:VirtualTermWriter;
private final _clickSignal:Signal<{button: ButtonType, pos: Vec2<Int>}> = new Signal();
private final _keySignal:Signal<{keyCode: Int, isHeld: Bool}> = new Signal();
private final _keyUpSignal:Signal<Int> = new Signal();
private final _mouseDragSignal:Signal<{button: ButtonType, pos: Vec2<Int>}> = new Signal();
private final _mouseScrollSignal:Signal<{dir: Int,pos: Vec2<Int>}> = new Signal();
private final _mouseUpSignal:Signal<{button: ButtonType,pos: Vec2<Int>}> = new Signal();
private final _pasteSignal:Signal<String> = new Signal();
public var clickSignal(get,null):SignalReadonly<{button: ButtonType, pos: Vec2<Int>}>;
public var keySignal(get,null):SignalReadonly<{keyCode: Int, isHeld: Bool}>;
public var keyUpSignal(get,null):SignalReadonly<Int>;
public var mouseDragSignal(get,null):SignalReadonly<{button: ButtonType, pos: Vec2<Int>}>;
public var mouseScrollSignal(get,null):SignalReadonly<{dir: Int,pos: Vec2<Int>}>;
public var mouseUpSignal(get,null):SignalReadonly<{button: ButtonType,pos: Vec2<Int>}>;
public var pasteSignal(get,null):SignalReadonly<String> ;
public function new(writer: VirtualTermWriter) {
this.writer = writer;
}
public var onResize(get, null):SignalReadonly<Vec2<Int>>;
function get_onResize():SignalReadonly<Vec2<Int>> {
return writer.onResize;
}
@:allow(kernel.ui)
private function setTarget(target: TermWriteable) {
writer.setTarget(target);
}
@:allow(kernel.ui)
private function enable() {
writer.enable();
}
@:allow(kernel.ui)
private function disable() {
writer.disable();
}
@:allow(kernel.ui)
private function isEnabled() {
return writer.isEnabled();
}
public function get_clickSignal(){
return _clickSignal;
}
public function get_keySignal(){
return _keySignal;
}
public function get_keyUpSignal(){
return _keyUpSignal;
}
public function get_mouseDragSignal(){
return _mouseDragSignal;
}
public function get_mouseScrollSignal(){
return _mouseScrollSignal;
}
public function get_mouseUpSignal(){
return _mouseUpSignal;
}
public function get_pasteSignal(){
return _pasteSignal;
}
@:allow(kernel.ui.WindowManager) // Package private
private function click(button:ButtonType ,pos: Vec2<Int>) {
_clickSignal.emit({button: button,pos: pos});
}
@:allow(kernel.ui.WindowManager) // Package private
private function key(keyCode: Int, isHeld: Bool) {
_keySignal.emit({keyCode: keyCode,isHeld: isHeld});
}
@:allow(kernel.ui.WindowManager) // Package private
private function keyUp(keyCode: Int) {
_keyUpSignal.emit(keyCode);
}
@:allow(kernel.ui.WindowManager) // Package private
private function mouseDrag(button: ButtonType, pos: Vec2<Int>) {
_mouseDragSignal.emit({button: button,pos: pos});
}
@:allow(kernel.ui.WindowManager) // Package private
private function mouseScroll(dir: Int,pos: Vec2<Int>) {
_mouseScrollSignal.emit({dir: dir,pos: pos});
}
@:allow(kernel.ui.WindowManager) // Package private
private function mouseUp(button: ButtonType,pos: Vec2<Int>) {
_mouseUpSignal.emit({button: button,pos: pos});
}
@:allow(kernel.ui.WindowManager) // Package private
private function paste(text: String) {
_pasteSignal.emit(text);
}
public function write(text:String) {
writer.write(text);
}
public function scroll(y:Int) {
writer.scroll(y);
}
public function getCursorPos():Vec2<Int> {
return writer.getCursorPos();
}
public function setCursorPos(x:Int, y:Int) {
writer.setCursorPos(x,y);
}
public function getCursorBlink():Bool {
return writer.getCursorBlink();
}
public function setCursorBlink(blink:Bool) {
writer.setCursorBlink(blink);
}
public function getSize():Vec2<Int> {
return writer.getSize();
}
public function clear() {
writer.clear();
}
public function clearLine() {
writer.clearLine();
}
public function getTextColor():Color {
return writer.getTextColor();
}
public function setTextColor(colour:Color) {
writer.setTextColor(colour);
}
public function getBackgroundColor():Color {
return writer.getBackgroundColor();
}
public function setBackgroundColor(color:Color) {
writer.setBackgroundColor(color);
}
public function isColor():Bool {
return writer.isColor();
}
}

View File

@@ -0,0 +1,155 @@
package kernel.ui;
import lib.TermWriteable;
import kernel.peripherals.Screen;
import kernel.peripherals.Peripherals.Peripheral;
import haxe.Exception;
import util.Vec.Vec2;
enum ButtonType {
Left;
Middle;
Right;
}
class WindowManager {
public static final instance:WindowManager = new WindowManager();
private function new() {}
private var currentMainContext:WindowContext;
private final allContexts:Array<WindowContext> = new Array();
private final outputMap:Map<String,WindowContext> = new Map();
public function init() {
KernelEvents.instance.on("key",params -> {
var keyCode: Int = params[1];
var isHeld: Bool = params[2];
if (currentMainContext != null){
currentMainContext.key(keyCode,isHeld);
}
});
KernelEvents.instance.on("key_up",params -> {
var keyCode: Int = params[1];
if (currentMainContext != null){
currentMainContext.keyUp(keyCode);
}
});
KernelEvents.instance.on("mouse_click",params -> {
var button: ButtonType = ccButtonToEnum(params[1]);
var clickPos: Vec2<Int> = {
x: (params[2]:Int) - 1,
y: (params[3]:Int) - 1
};
if (currentMainContext != null){
currentMainContext.click(button,clickPos);
}
});
KernelEvents.instance.on("mouse_drag",params -> {
var button: ButtonType = ccButtonToEnum(params[1]);
var pos: Vec2<Int> = {
x: (params[2]:Int) - 1,
y: (params[3]:Int) - 1,
}
if (currentMainContext != null){
currentMainContext.mouseDrag(button,pos);
}
});
KernelEvents.instance.on("mouse_scroll",params -> {
var dir: Int = params[1];
var pos: Vec2<Int> = {
x: (params[2]:Int) - 1,
y: (params[3]:Int) - 1,
}
if (currentMainContext != null){
currentMainContext.mouseScroll(dir,pos);
}
});
KernelEvents.instance.on("mouse_up",params -> {
var button: ButtonType = ccButtonToEnum(params[1]);
var pos: Vec2<Int> = {
x: (params[2]:Int) - 1,
y: (params[2]:Int) - 1,
}
if (currentMainContext != null){
currentMainContext.mouseUp(button,pos);
}
});
KernelEvents.instance.on("paste",params -> {
var text: String = params[1];
if (currentMainContext != null){
currentMainContext.paste(text);
}
});
KernelEvents.instance.on("monitor_touch",array -> {
// TODO
});
}
public function createNewContext(): WindowContext {
var newContext = new WindowContext(new VirtualTermWriter());
allContexts.push(newContext);
newContext.setTarget(MainTerm.instance);
newContext.enable();
currentMainContext = newContext;
return newContext;
}
public function getOutputs(): Array<String> {
var arr = Peripheral.instance.getScreens().map(screen -> return screen.getAddr());
arr.push("main");
return arr;
}
public function focusContextToOutput(context: WindowContext,output: String) {
var target: TermWriteable;
if (output == "main"){
target = MainTerm.instance;
}else{
target = Peripheral.instance.getScreen(output);
if (target == null){
// output target not found
return;
}
}
if (outputMap.exists(output)){
outputMap[output].disable();
}
outputMap[output] = context;
context.setTarget(target);
context.enable();
}
private static function ccButtonToEnum(button: Int): ButtonType {
switch button {
case 1:
return Left;
case 2:
return Middle;
case 3:
return Right;
case _:
throw new Exception("Invalid input");
}
}
}