Skip to content
Snippets Groups Projects
Commit 36bd5ea0 authored by echicken's avatar echicken :chicken:
Browse files

Reorg

parent 75911da2
No related branches found
No related tags found
No related merge requests found
...@@ -3454,7 +3454,7 @@ ...@@ -3454,7 +3454,7 @@
}, },
"node_modules/swindows": { "node_modules/swindows": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "git+ssh://git@gitlab.synchro.net:echicken/swindows.git#2da0ded8a6acd7062a93aef24d44c5181e83c615" "resolved": "git+ssh://git@gitlab.synchro.net:echicken/swindows.git#38822fa9ca3b43ce1a59f07332f03f13925f437a"
}, },
"node_modules/to-fast-properties": { "node_modules/to-fast-properties": {
"version": "2.0.0", "version": "2.0.0",
......
import type { ICgaDefs, INodeDefs, ISbbsDefs } from '@swag/ts4s'; import type { ICgaDefs, IKeyDefs, ISbbsDefs } from '@swag/ts4s';
import { load } from '@swag/ts4s'; import { load } from '@swag/ts4s';
import * as swindows from 'swindows'; import * as swindows from 'swindows';
import ActivityWindow from './components/ActivityWindow';
import MainMenu from './components/MainMenu';
import MessageMenu from './components/MessageMenu';
import GamesMenu from './components/GamesMenu';
import { IShellWindow } from './lib/types'; import { IShellWindow } from './lib/types';
import ActivityWindow from './lib/ActivityWindow';
import MenuWindow from './lib/MenuWindow';
const sbbsdefs: ISbbsDefs = load('sbbsdefs.js'); const sbbsdefs: ISbbsDefs = load('sbbsdefs.js');
const cgadefs: ICgaDefs = load('cga_defs.js'); const cgadefs: ICgaDefs = load('cga_defs.js');
const nodedefs: INodeDefs = load('nodedefs.js'); const keydefs: IKeyDefs = load('key_defs.js');
const { bbs, console, mswait, system, time, skipsp, truncsp, user } = js.global; const { bbs, console, mswait } = js.global;
const queue = js.global.load(true, `${js.exec_dir}background.js`, `${user.number}`);
const windowManager = new swindows.WindowManager(); const windowManager = new swindows.WindowManager();
const windows: IShellWindow[] = [ const windows: IShellWindow[] = [
ActivityWindow(windowManager), new ActivityWindow(
MainMenu(windowManager), 'Activity',
MessageMenu(windowManager), windowManager,
GamesMenu(windowManager), { x: 0,
y: 0
},
{ width: windowManager.size.width,
height: Math.ceil(windowManager.size.height / 2)
},
),
new MenuWindow(
'Main',
`${js.exec_dir}main.ini`,
windowManager,
{ x: 0,
y: Math.floor(windowManager.size.height / 2)
},
{ width: Math.floor(windowManager.size.width / 3),
height: Math.floor(windowManager.size.height / 2)
}
),
new MenuWindow(
'Messages',
`${js.exec_dir}messages.ini`,
windowManager,
{ x: Math.floor(windowManager.size.width / 3),
y: Math.floor(windowManager.size.height / 2),
},
{ width: windowManager.size.width - (Math.floor(windowManager.size.width / 3) * 2),
height: Math.floor(windowManager.size.height / 2),
}
),
new MenuWindow(
'Games',
`${js.exec_dir}games.ini`,
windowManager,
{ x: Math.floor(windowManager.size.width / 3) + (windowManager.size.width - (Math.floor(windowManager.size.width / 3) * 2)),
y: Math.floor(windowManager.size.height / 2)
},
{ width: Math.floor(windowManager.size.width / 3),
height: Math.floor(windowManager.size.height / 2),
}
),
]; ];
let activeWindow: number = 1; let activeWindow: number = 1;
function cycleWindows(): void {
for (const window of windows) {
window.cycle();
}
}
function focusWindow(idx: number): void { function focusWindow(idx: number): void {
if (idx < 0) idx = windows.length - 1;
idx = idx % windows.length; idx = idx % windows.length;
windows[activeWindow].window.title = { windows[activeWindow].window.title = {
text: windows[activeWindow].window.title?.text ?? '', text: windows[activeWindow].window.title?.text ?? '',
...@@ -42,36 +84,19 @@ function focusWindow(idx: number): void { ...@@ -42,36 +84,19 @@ function focusWindow(idx: number): void {
function getInput(): void { function getInput(): void {
let input = console.inkey(sbbsdefs.K_NONE); let input = console.inkey(sbbsdefs.K_NONE);
if (input === '') return; if (input === '') return;
if (input === 'q') { if (input === 'q') js.global.exit(0);
js.global.exit(0); switch (input) {
} else if (input === '\t') { case '\t':
case keydefs.KEY_RIGHT:
focusWindow(activeWindow + 1); focusWindow(activeWindow + 1);
} else { break;
windows[activeWindow].menu.getCmd(input); case keydefs.KEY_LEFT:
} focusWindow(activeWindow - 1);
break;
default:
windows[activeWindow].getCmd(input);
break;
} }
function chomp(str: string): string {
return skipsp(truncsp(str));
}
function getNotifications(): void {
const msg = [];
while (!js.terminated && queue.data_waiting) {
msg.push(queue.read());
}
if (system.node_list[bbs.node_num - 1].misc&nodedefs.NODE_NMSG) {
const nm = chomp(system.get_node_message(bbs.node_num)).split('\r\n');
if (nm.length > 0) msg.push(...nm);
}
if (system.node_list[bbs.node_num - 1].misc&nodedefs.NODE_MSGW) {
const tg = chomp(system.get_telegram(user.number) ?? '').split('\r\n');
if (tg.length > 0) msg.push(...tg);
}
if (msg.length < 1) return;
if (windows[0].window.cursor.y > 0) windows[0].window.write('\r\n');
windows[0].window.write(`\x01h\x01w${system.timestr(time())}\x01n\x01w\r\n`);
windows[0].window.write(msg.join('\r\n'));
} }
function init(): void { function init(): void {
...@@ -85,17 +110,30 @@ function init(): void { ...@@ -85,17 +110,30 @@ function init(): void {
console.ctrlkey_passthru = "+UPKTG"; console.ctrlkey_passthru = "+UPKTG";
console.clear(cgadefs.BG_BLACK|cgadefs.LIGHTGRAY); console.clear(cgadefs.BG_BLACK|cgadefs.LIGHTGRAY);
windowManager.hideCursor(); windowManager.hideCursor();
focusWindow(1);
} }
function main(): void { function main(): void {
while (!js.terminated) { while (!js.terminated) {
getInput(); getInput();
getNotifications(); cycleWindows();
windowManager.refresh(); windowManager.refresh();
bbs.nodesync();
mswait(5); mswait(5);
} }
} }
init(); init();
main(); main();
/**
* To do:
* - Line input / console.getstr equivalent in swindows
* - Help text / motd in activity window on start
* - ctrlkey shortcut for who's online (print to activity window)
* - ctrlkey shortcut for node message, telegram, pop up line input box
* - ctrlkey shortcut for logoff
* - scroll / scrollTo logic in swindows is suspect; scrolling down in a window with dataheight < height drops contents to bottom for example.
* - mystery bug in swindows where returning from clear screen and calling wm.draw didn't set attribute correctly in some rows/cells for an avatar in an imsg
* - add HOME/END handling in LightBar
*/
\ No newline at end of file
import type { ISbbsDefs, ISmbDefs } from '@swag/ts4s'; import type { INodeDefs, ISbbsDefs, ISmbDefs } from '@swag/ts4s';
import { load } from '@swag/ts4s'; import { load } from '@swag/ts4s';
const sbbsdefs: ISbbsDefs = load('sbbsdefs.js'); const sbbsdefs: ISbbsDefs = load('sbbsdefs.js');
const smbdefs: ISmbDefs = load('smbdefs.js'); const smbdefs: ISmbDefs = load('smbdefs.js');
const nodedefs: INodeDefs = load('nodedefs.js');
const { argv, format, msg_area, mswait, parent_queue, system, time, File, MsgBase, Queue } = js.global; const { argc, argv, format, skipsp, truncsp, mswait, time, parent_queue, msg_area, system, File, MsgBase, Queue } = js.global;
const queue = parent_queue ?? new Queue('never');
if (argc < 2) throw new Error(`'background.js <user#> <node #>' expected two arguments but got ${argc}`);
const userNumber = parseInt(argv[0], 10);
const nodeNumber = parseInt(argv[1], 10);
const queue = parent_queue ?? new Queue('never');
const msgPointers: Record<string, number> = {}; const msgPointers: Record<string, number> = {};
function scanMessages(): void { function scanSubs(): void {
const sf = new File(`${system.data_dir}user/${format('%04d', argv[0])}.subs`); const sf = new File(`${system.data_dir}user/${format('%04d', userNumber)}.subs`);
if (!sf.open('r')) return; if (!sf.open('r')) return;
const subs = sf.iniGetAllObjects(); const subs = sf.iniGetAllObjects();
sf.close(); sf.close();
...@@ -36,15 +41,33 @@ function scanMessages(): void { ...@@ -36,15 +41,33 @@ function scanMessages(): void {
} }
} }
function chomp(str: string): string {
return skipsp(truncsp(str));
}
function scanInstantMessages(): void {
const msg: string[] = [];
if (system.node_list[nodeNumber - 1].misc&nodedefs.NODE_NMSG) {
const nm = chomp(system.get_node_message(nodeNumber));
if (nm.length > 0) msg.push(...nm);
}
if (system.node_list[nodeNumber - 1].misc&nodedefs.NODE_MSGW) {
const tg = chomp(system.get_telegram(userNumber) ?? '').split('\r\n');
if (tg.length > 0) msg.push(...tg);
}
if (msg.length > 0) queue.write(msg.join('\r\n'));
}
js.time_limit = 0; js.time_limit = 0;
let now = time(); let now = time();
let mscan = now - 30; let sscan = now - 30;
while (!js.terminated && !queue.orphan) { while (!js.terminated && !queue.orphan) {
now = time(); now = time();
if (now - mscan >= 30) { if (now - sscan >= 30) {
mscan = now; sscan = now;
scanMessages(); scanSubs();
} }
scanInstantMessages();
mswait(1000); mswait(1000);
} }
import type { IKeyDefs } from '@swag/ts4s';
import { load } from '@swag/ts4s';
import { defs, types, ControlledWindow } from 'swindows';
import { IShellWindow } from '../lib/types';
import getWindowOptions from '../lib/window-options';
const keydefs: IKeyDefs = load('key_defs.js');
const { user, bbs, system, time } = js.global;
const queue = js.global.load(true, `${js.exec_dir}background.js`, `${user.number}`, `${bbs.node_num}`);
export default function(wm: types.IWindowManager): IShellWindow {
const options = getWindowOptions('Activity', { x: 0, y: 0 }, { width: wm.size.width, height: Math.ceil(wm.size.height / 2) }, wm);
const window = new ControlledWindow(options);
window.wrap = defs.WRAP.NONE;
function getCmd(cmd: string): void {
switch (cmd) {
case keydefs.KEY_UP:
window.scroll(0, -1);
break;
case keydefs.KEY_PAGEUP:
window.scroll(0, -window.size.height);
break;
case keydefs.KEY_HOME:
window.scrollTo({ x: 0, y: 0 });
break;
case keydefs.KEY_DOWN:
window.scroll(0, 1);
break;
case keydefs.KEY_PAGEDN:
window.scroll(0, window.size.height);
break;
case keydefs.KEY_END:
window.scrollTo({ x: 0, y: window.dataHeight - window.size.height }); // Window should just magically fix any fucked numbers here
break;
default:
break;
}
}
function cycle(): void {
const msg = [];
while (!js.terminated && queue.data_waiting) {
msg.push(queue.read());
}
if (msg.length < 1) return;
if (window.cursor.y > 0) window.write('\r\n');
window.write(`\x01h\x01w${system.timestr(time())}\x01n\x01w\r\n`);
window.write(msg.join('\r\n'));
}
return { window, menu: { getCmd }, cycle };
}
\ No newline at end of file
import { types, ControlledWindow, LightBar } from 'swindows';
import { IShellWindow } from '../lib/types';
import getWindowOptions from '../lib/window-options';
import { FileWatcher, loadItems } from '../lib/menu';
export default function(wm: types.IWindowManager): IShellWindow {
const options = getWindowOptions('Games', { x: Math.floor(wm.size.width / 3) + (wm.size.width - (Math.floor(wm.size.width / 3) * 2)), y: Math.floor(wm.size.height / 2) }, { width: Math.floor(wm.size.width / 3), height: Math.floor(wm.size.height / 2) }, wm);
const window = new ControlledWindow(options);
const ini = `${js.exec_dir}games.ini`;
const items = loadItems(ini, wm);
const menu = new LightBar({
items,
name: 'Games Menu',
window: window,
});
menu.draw();
const fileWatcher = new FileWatcher(ini);
function cycle() {
if (fileWatcher.updated) {
const items = loadItems(ini, wm);
menu.items = items;
menu.draw();
}
}
return { window, menu, cycle };
}
\ No newline at end of file
import type { ICgaDefs } from '@swag/ts4s';
import { load } from '@swag/ts4s';
import { types, ControlledWindow, LightBar } from 'swindows';
import { IShellWindow } from '../lib/types';
import getWindowOptions from '../lib/window-options';
import { FileWatcher, loadItems } from '../lib/menu';
const cgadefs: ICgaDefs = load('cga_defs.js');
export default function(wm: types.IWindowManager): IShellWindow {
const options = getWindowOptions('Main', { x: 0, y: Math.floor(wm.size.height / 2) }, { width: Math.floor(wm.size.width / 3), height: Math.floor(wm.size.height / 2) }, wm);
if (options.title !== undefined) options.title.attr = ((cgadefs.BG_CYAN|cgadefs.LIGHTCYAN) as types.attr);
const window = new ControlledWindow(options);
const ini = `${js.exec_dir}main.ini`;
const items = loadItems(ini, wm);
const menu = new LightBar({
items,
name: 'Main Menu',
window,
});
menu.draw();
const fileWatcher = new FileWatcher(ini);
function cycle() {
if (fileWatcher.updated) {
const items = loadItems(ini, wm);
menu.items = items;
menu.draw();
}
}
return { window, menu, cycle };
}
import { types, ControlledWindow, LightBar } from 'swindows';
import { IShellWindow } from '../lib/types';
import getWindowOptions from '../lib/window-options';
import { FileWatcher, loadItems } from '../lib/menu';
export default function(wm: types.IWindowManager): IShellWindow {
const options = getWindowOptions('Messages', { x: Math.floor(wm.size.width / 3), y: Math.floor(wm.size.height / 2) }, { width: wm.size.width - (Math.floor(wm.size.width / 3) * 2), height: Math.floor(wm.size.height / 2) }, wm);
const window = new ControlledWindow(options);
const ini = `${js.exec_dir}messages.ini`;
const items = loadItems(ini, wm);
const menu = new LightBar({
items,
name: 'Messages Menu',
window: window,
});
menu.draw();
const fileWatcher = new FileWatcher(ini);
function cycle() {
if (fileWatcher.updated) {
const items = loadItems(ini, wm);
menu.items = items;
menu.draw();
}
}
return { window, menu, cycle };
}
\ No newline at end of file
import { ControlledWindow, WindowManager, LightBar } from "swindows"; import type { ICgaDefs } from '@swag/ts4s';
import { IControlledWindow, IPosition, ISize } from "swindows/src/types"; import { load } from '@swag/ts4s';
import getWindowOptions from './window-options'; import * as swindows from 'swindows';
const cgadefs: ICgaDefs = load('cga_defs.js');
export default abstract class ShellWindow { export default abstract class ShellWindow {
name: string; name: string;
window: IControlledWindow; window: swindows.types.IControlledWindow;
windowManager: WindowManager; windowManager: swindows.WindowManager;
constructor(name: string, wm: WindowManager, position: IPosition, size: ISize) { constructor(name: string, windowManager: swindows.WindowManager, position: swindows.types.IPosition, size: swindows.types.ISize) {
this.name = name; this.name = name;
this.windowManager = wm; this.windowManager = windowManager;
const options = getWindowOptions(name, position, size, wm);
this.window = new ControlledWindow(options);
const options = {
border: {
style: swindows.defs.BORDER_STYLE.SINGLE,
pattern: swindows.defs.BORDER_PATTERN.DIAGONAL,
attr: [
((cgadefs.BG_BLACK|cgadefs.WHITE) as swindows.types.attr),
((cgadefs.BG_BLACK|cgadefs.LIGHTCYAN) as swindows.types.attr),
((cgadefs.BG_BLACK|cgadefs.CYAN) as swindows.types.attr),
((cgadefs.BG_BLACK|cgadefs.LIGHTBLUE) as swindows.types.attr),
],
},
title: {
text: name,
attr: ((cgadefs.BG_BLACK|cgadefs.WHITE) as swindows.types.attr),
},
position,
size,
scrollBar: {
vertical: {
enabled: true,
}
},
windowManager,
name: `${name} window`,
}
options.title.text = name;
this.window = new swindows.ControlledWindow(options);
} }
cycle() {} cycle() {}
......
import type { ICgaDefs } from '@swag/ts4s';
import { load } from '@swag/ts4s';
import { WindowManager } from "swindows"; import { WindowManager } from "swindows";
import { ILightBarItem } from "swindows/src/LightBar"; import { ILightBarItem } from "swindows/src/LightBar";
const cgadefs: ICgaDefs = load('cga_defs.js'); const { bbs, file_exists, file_date, console, user, system, time, File } = js.global;
const { bbs, console, user, system, time, File } = js.global; function clear(): void {
console.home();
console.write('\x1B[0;37;40m');
console.write('\x1B[2J');
}
function exec(wm: WindowManager, fn: (...args: any[]) => any, ...args: any[]): void { function exec(wm: WindowManager, fn: (...args: any[]) => any, ...args: any[]): void {
console.clear(cgadefs.BG_BLACK|cgadefs.LIGHTGRAY); clear();
try { try {
fn(...args); fn(...args);
} catch (err: unknown) { } catch (err: unknown) {
console.putmsg(err as string); console.putmsg(err as string);
} }
console.clear(cgadefs.BG_BLACK|cgadefs.LIGHTGRAY); clear();
wm.draw(); wm.draw();
} }
...@@ -52,3 +54,23 @@ export function loadItems(filename: string, wm: WindowManager): ILightBarItem[] ...@@ -52,3 +54,23 @@ export function loadItems(filename: string, wm: WindowManager): ILightBarItem[]
} }
return ret; return ret;
} }
export class FileWatcher {
date: number;
filePath: string;
constructor(filePath: string) {
if (!file_exists(filePath)) throw new Error(`FileWatcher: ${filePath} not found`);
this.date = file_date(filePath);
this.filePath = filePath;
}
get updated(): boolean {
const fd = file_date(this.filePath);
if (fd > this.date) {
this.date = fd;
return true;
}
return false;
}
}
\ No newline at end of file
import { IControlledWindow } from "swindows/src/types"; import { IControlledWindow } from "swindows/src/types";
export interface IInputHandler {
getCmd: (str: string) => void;
};
export interface IShellWindow { export interface IShellWindow {
window: IControlledWindow, window: IControlledWindow,
menu: IInputHandler, getCmd: (str: string) => void,
cycle: () => void,
}; };
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment