Skip to content

Commit

Permalink
[Patcher] Support onLoad, and eventually onUnload
Browse files Browse the repository at this point in the history
  • Loading branch information
maisymoe committed Aug 10, 2023
1 parent 2d66cb7 commit 7b1d5e4
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 55 deletions.
15 changes: 15 additions & 0 deletions src/core/patcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export function applyPatches(mainScript: HTMLScriptElement) {
console.group("[ ropeswing-patcher ]");

for (let extension of extensions) {
if (!extension.patches) continue;

for (let patch of extension.patches) {
if (patch.executable) continue;
// TODO: Using `as string` is bad, but TypeScript wasn't having it with my replace typings, and this works
Expand All @@ -26,3 +28,16 @@ export function applyPatches(mainScript: HTMLScriptElement) {

console.groupEnd();
}

export function executePostload() {
console.group("[ ropeswing-postload ]");

for (let extension of extensions) {
if (!extension.onLoad) continue;

extension.onLoad();
console.log(`executed onLoad of ${extension.manifest.name}`);
}

console.groupEnd();
}
45 changes: 45 additions & 0 deletions src/ext/housekeeper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
export default {
patches: [
{
find: /Windows 96 main<br>/,
replace: (match) => `${match}ropeswing ${ROPESWING_COMMIT}<br>`,
},
{
find: /Other trademarks and logos are property of their respective owners\./,
replace: (match) => `${match}\n\nropeswing ${ROPESWING_COMMIT}, an <a href="https://github.com/uwu/ropeswing">uwunet project</a>.`,
executable: "C:/system/local/bin/about-ui",
}
],
onLoad() {
if (localStorage["ropeswing-welcome"] !== "true") {
localStorage["ropeswing-welcome"] = "true";
w96.WApplication.execAsync(this.dialog(w96.WApplication), []);
}
},
manifest: {
name: "housekeeper",
description: "basic ropeswing info, e.g. welcome dialog, version listing",
authors: ["redstonekasi", "Beef"],
},
core: true,
dialog: (WApplication: typeof w96.WApplication) => new (class WelcomeDialog extends WApplication {
async main(argv: string[]) {
super.main(argv);
// @ts-expect-error
const popup = this.createWindow({
title: "ropeswing",
initialHeight: 120,
initialWidth: 260,
resizable: false,
bodyClass: "dlg-run-box",
controlBoxStyle: "WS_CBX_CLOSE",
});
const body = popup.getBodyContainer();
body.innerHTML = `<div class="text exp">ropeswing has been installed!<br>Check your system settings in order to configure it.</div><button class="w96-button">OK</button>`;
body.querySelector("button")?.addEventListener("click", () => popup.close(false));

popup.setPosition(window.innerWidth / 2 - 130, window.innerHeight / 2 - 60);
popup.show();
}
})(),
} as Extension;
4 changes: 2 additions & 2 deletions src/ext/royalmail/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ export default {
authors: ["Beef"],
},
core: true,
app: (WApplication: any, components: Record<string, any>) => class RoyalmailApplet extends WApplication {
app: (WApplication: typeof w96.WApplication, components: Record<string, any>) => class RoyalmailApplet extends WApplication {
constructor() {
super();
}

// TODO: Check that this whole thing disposes properly.
// By all right, it should, however I am unsure if the weird stuff I do with the pages will mess anything up.
public main = (argv: string[]) => useRoot((dispose) => {
public main = async (argv: string[]) => useRoot((dispose) => {
// HACK: Persist the original onterminated behaviour because we are an applet, but also dispose our reactive root
before("onterminated", this, dispose, true);
if ((super.main(argv), document.querySelector(".royalmail-applet"))) return;
Expand Down
8 changes: 5 additions & 3 deletions src/ext/treebranch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ export default {
async getFileContent(path: string) {
let content = await w96.FS.readstr(path);

for (const ext of extensions) {
for (let patch of ext.patches) {
for (const extension of extensions) {
if (!extension.patches) continue;

for (let patch of extension.patches) {
if (patch.executable !== path) continue;
content = content.replace(patch.find, contextify(patch.replace, ext.manifest.name) as string);
content = content.replace(patch.find, contextify(patch.replace, extension.manifest.name) as string);
}
}

Expand Down
19 changes: 0 additions & 19 deletions src/ext/version.ts

This file was deleted.

33 changes: 3 additions & 30 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { instead } from "spitroast";
import { writeLine } from "@lib/console";
import { applyPatches } from "@core/patcher";
import { applyPatches, executePostload } from "@core/patcher";
import api from "@core/api";

// W96 like to do this, let's follow the convention
Expand All @@ -23,35 +23,8 @@ const unpatch = instead("appendChild", document.head, (args, orig) => {
writeLine("booting original!");
orig(...args);

if (localStorage["ropeswing-welcome"] !== "true") {
localStorage["ropeswing-welcome"] = "true";
w96.evt.sys.on("init-complete", () =>
w96.WApplication.execAsync(
new (class extends w96.WApplication {
main(argv) {
super.main(argv);
const popup = this.createWindow({
title: "ropeswing",
initialHeight: 120,
initialWidth: 260,
resizable: false,
bodyClass: "dlg-run-box",
controlBoxStyle: "WS_CBX_CLOSE",
});
const body = popup.getBodyContainer();
body.innerHTML = `<div class="text exp">ropeswing has been installed!<br>Check your system settings in order to configure it.</div><button class="w96-button">OK</button>`;
body.querySelector("button").addEventListener("click", () => {
popup.close();
});
popup.setPosition(window.innerWidth / 2 - 130, window.innerHeight / 2 - 60);
popup.show();
}
})(),
null,
null
)
);
}
writeLine("assigning postload init event...");
w96.evt.sys.on("init-complete", executePostload);
});

console.log("kernel done!");
Expand Down
6 changes: 5 additions & 1 deletion src/types/core.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ interface Patch {
}

interface Extension {
patches: Patch[];
patches?: Patch[];
/** Runs *after* w96 has initialised */
onLoad?: () => void;
/** Currently unused. */
onUnload?: () => void;
manifest: {
name: string;
description: string;
Expand Down
130 changes: 130 additions & 0 deletions src/types/w96.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,135 @@
// adapted from https://git.sys36.net/windows-96/utilities/api-typings/-/blob/master/win96.d.ts

interface EmitterEvent {
name: string;
callback: Function;
type: "recurring" | "single";
}

interface EventEmitter {
eventQueue: EmitterEvent[];
on(evtName: string, callback: Function): void;
once(evtName: string, callback: Function): void;
emit(evtName: string, ...args: any): void;
}

interface WindowParams {
initialX?: number;
initialY?: number;
minHeight?: number;
minWidth?: number;
initialHeight?: number;
initialWidth?: number;
title?: string;
resizable?: boolean;
draggable?: boolean;
taskbar?: boolean;
icon?: string;
center?: boolean;
body?: string;
bodyClass?: string;
windowClass?: string;
controlBoxStyle?: "WS_CBX_MINMAXCLOSE" | "WS_CBX_CLOSE" | "WS_CBX_MINCLOSE" | "WS_CBX_NONE";
mobResize?: boolean;
iframeFix?: boolean;
animations?: {
windowOpen: string;
windowClose: string;
};
wndContainer?: HTMLDivElement;
}

declare class StandardWindow {
readonly id: string;
readonly useIcon: boolean;
readonly wndObject: HTMLDivElement;
readonly animations: {};
readonly params: WindowParams;
readonly isRegistered: boolean;
readonly appbarRegistered: boolean;
readonly shown: boolean;
readonly _wmStylingAllowed: boolean;
readonly maximized: boolean;
readonly minimized: boolean;
readonly uiUpdating: boolean;
readonly windowIcon: string;
readonly title: string;
readonly maximizeInfo: {
x: string;
y: string;
h: string;
w: string;
};

onclose: (e: { canceled: boolean }) => void;
onload: () => void;
ondarkenelements: () => void;
onlightenelements: () => void;
_ext: {
windowSnap: {
snapped: boolean;
originalSize: null;
};
};
showTitlebarMenu(e: MouseEvent): void;
registerWindow(): void;
registerAppBar(): void;
setTitle(text: string): void;
setHtml(text: string): void;
randomizePosition(): void;
show(): void;
center(current: boolean): void;
activate(): void;
toggleMinimize(): void;
toggleMaximize(): void;
close(ignoreEvents: boolean): void;
darkenElements(): void;
lightenElements(): void;
setWindowIcon(icon_url: string): HTMLDivElement;
setControlBoxStyle(cbstyle: "WS_CBX_CLOSE" | "WS_CBX_MINCLOSE" | "WS_CBX_MINMAXCLOSE" | "WS_CBX_NONE"): void;
setSize(w: number, h: number, ignoreThemeOffsets?: boolean): void;
setPosition(x: number, y: number): void;
getBounds(): {
x: number;
y: number;
height: number;
width: number;
};
getComputedBounds(): DOMRect;
getBodyContainer(): HTMLDivElement;
constructor(params: WindowParams);
}

declare class WApplication {
readonly appId: number;
readonly appWindow: StandardWindow;
readonly windows: StandardWindow[];
onterminated: (result: any) => void;
_running: boolean;
_terminating: boolean;
_appResult: any;
setAppResult(v: any): void;
terminate(): void;
createWindow(params: WindowParams, isAppWindow: boolean): StandardWindow;
main(argv: string[]): Promise<any>;
ontermination(): void;
}

interface W96API {
FS: {
readstr(path: string): Promise<string>;
};
evt: {
fs: EventEmitter;
sys: EventEmitter;
wm: EventEmitter;
ui: EventEmitter;
};
WApplication: {
new(): WApplication;
prototype: WApplication;

kill(app_id: number, force: boolean): void;
execAsync(instance: WApplication, args: string[]): Promise<any>;
};
}

0 comments on commit 7b1d5e4

Please sign in to comment.