Skip to content

Commit

Permalink
Isolated q3 module
Browse files Browse the repository at this point in the history
  • Loading branch information
DustinBrett committed Jan 19, 2025
1 parent 2d16d6e commit c00d428
Show file tree
Hide file tree
Showing 11 changed files with 153 additions and 100 deletions.
1 change: 1 addition & 0 deletions .stylelintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"alpha-value-notation": "percentage",
"color-function-notation": "legacy",
"hue-degree-notation": "number",
"no-empty-source": null,
"order/properties-alphabetical-order": true,
"value-keyword-case": [
"lower",
Expand Down
14 changes: 0 additions & 14 deletions components/apps/Quake3/StyledQuake3.ts

This file was deleted.

3 changes: 1 addition & 2 deletions components/apps/Quake3/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import StyledQuake3 from "components/apps/Quake3/StyledQuake3";
import useQuake3 from "components/apps/Quake3/useQuake3";
import AppContainer from "components/system/Apps/AppContainer";
import { type ComponentProcessProps } from "components/system/Apps/RenderComponent";

const Quake3: FC<ComponentProcessProps> = ({ id }) => (
<AppContainer StyledComponent={StyledQuake3} id={id} useHook={useQuake3} />
<AppContainer id={id} useHook={useQuake3} />
);

export default Quake3;
123 changes: 79 additions & 44 deletions components/apps/Quake3/useQuake3.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { useTheme } from "styled-components";
import { useEffect, useRef } from "react";
import { useCallback, useEffect, useRef, useState } from "react";
import { type ContainerHookProps } from "components/system/Apps/AppContainer";
import useEmscriptenMount from "components/system/Files/FileManager/useEmscriptenMount";
import { type EmscriptenFS } from "contexts/fileSystem/useAsyncFs";
import { useProcesses } from "contexts/process";
import { useSession } from "contexts/session";
import { TRANSITIONS_IN_MILLISECONDS } from "utils/constants";
import { PREVENT_SCROLL, TRANSITIONS_IN_MILLISECONDS } from "utils/constants";
import { loadFiles, pxToNum } from "utils/functions";
import useIsolatedContentWindow from "hooks/useIsolatedContentWindow";

declare global {
interface Window {
Expand All @@ -22,7 +23,7 @@ declare global {
exit: () => void;
exitHandler: (error: Error | null) => void;
setCanvasSize: (width: number, height: number) => void;
viewport: HTMLDivElement | null;
viewport: HTMLElement | null;
};
}
}
Expand Down Expand Up @@ -52,70 +53,104 @@ const useQuake3 = ({
const wasMaximized = useRef(false);
const mountEmFs = useEmscriptenMount();
const { size } = windowState || {};
const focusCanvas = useCallback((focusedWindow: Window) => {
if (focusedWindow?.ioq3?.canvas) {
focusedWindow.ioq3.canvas.focus(PREVENT_SCROLL);
} else {
requestAnimationFrame(() => focusCanvas(focusedWindow));
}
}, []);
const getContentWindow = useIsolatedContentWindow(
id,
containerRef,
focusCanvas,
`
body { display: flex; place-content: center; place-items: center; }
canvas { background-color: #000; height: 100%; width: 100%; }
canvas:focus-visible { outline: none; }
`
);
const [contentWindow, setContentWindow] = useState<Window>();

useEffect(() => {
if (loading) {
loadFiles(libs).then(() => {
if (!window.ioq3) return;
const newContentWindow = getContentWindow?.();

if (!newContentWindow) return;

loadFiles(libs, undefined, undefined, undefined, newContentWindow).then(
() => {
if (!newContentWindow.ioq3) return;

window.ioq3.viewport = containerRef.current;
window.ioq3.elementPointerLock = true;
window.ioq3.callMain([]);
newContentWindow.ioq3.viewport = newContentWindow.document.body;
newContentWindow.ioq3.elementPointerLock = true;
newContentWindow.ioq3.callMain([]);

setLoading(false);
mountEmFs(window.FS as EmscriptenFS, "Quake3");
});
setLoading(false);
mountEmFs(window.FS as EmscriptenFS, "Quake3");
setContentWindow(newContentWindow);
}
);
}
}, [containerRef, libs, loading, mountEmFs, setLoading]);
}, [getContentWindow, libs, loading, mountEmFs, setLoading]);

useEffect(() => {
if (!window.ioq3) return;
if (!contentWindow?.ioq3) return;

const updateSize = (): void => {
if (!contentWindow.ioq3?.canvas) return;

wasMaximized.current = maximized;

const { height, width } =
(!maximized && size) || componentWindow?.getBoundingClientRect() || {};

if (!height || !width) return;

const aspectRatio = defaultSize
? pxToNum(defaultSize.width) / pxToNum(defaultSize.height)
: 4 / 3;
const numWidth = pxToNum(width);
const hasGreaterWidth = numWidth > pxToNum(height) - titleBar.height;
const newWidth =
maximized && hasGreaterWidth ? numWidth / aspectRatio : numWidth;
const newHeight = newWidth / aspectRatio;

if (newHeight > 0 && newWidth > 0) {
contentWindow.ioq3.setCanvasSize(newWidth, newHeight);
contentWindow.ioq3.canvas.setAttribute(
"style",
`object-fit: ${hasGreaterWidth ? "contain" : "scale-down"}`
);
}
};

setTimeout(
() => {
wasMaximized.current = maximized;

const { height, width } =
(!maximized && size) ||
componentWindow?.getBoundingClientRect() ||
{};

if (!height || !width) return;

const aspectRatio = defaultSize
? pxToNum(defaultSize.width) / pxToNum(defaultSize.height)
: 4 / 3;
const numWidth = pxToNum(width);
const hasGreaterWidth = numWidth > pxToNum(height) - titleBar.height;
const newWidth =
maximized && hasGreaterWidth ? numWidth / aspectRatio : numWidth;
const newHeight = newWidth / aspectRatio;

if (newHeight > 0 && newWidth > 0 && window.ioq3?.canvas) {
window.ioq3.setCanvasSize(newWidth, newHeight);
window.ioq3.canvas.setAttribute(
"style",
`object-fit: ${hasGreaterWidth ? "contain" : "scale-down"}`
);
}
},
updateSize,
maximized || wasMaximized.current
? TRANSITIONS_IN_MILLISECONDS.WINDOW + 10
: 0
);
}, [componentWindow, defaultSize, maximized, size, titleBar.height]);
}, [
componentWindow,
contentWindow,
defaultSize,
maximized,
size,
titleBar.height,
]);

useEffect(
() => () => {
try {
window.ioq3?.exit();
contentWindow?.ioq3?.exit();
} catch {
// Ignore error on exit
}

window.AL?.contexts.forEach(({ ctx }) => ctx.close());
contentWindow?.AL?.contexts.forEach(({ ctx }) => ctx.close());
},
[]
[contentWindow]
);
};

Expand Down
12 changes: 0 additions & 12 deletions components/apps/Tic80/StyledTic80.ts

This file was deleted.

3 changes: 1 addition & 2 deletions components/apps/Tic80/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import useTic80 from "components/apps/Tic80/useTic80";
import StyledTic80 from "components/apps/Tic80/StyledTic80";
import AppContainer from "components/system/Apps/AppContainer";
import { type ComponentProcessProps } from "components/system/Apps/RenderComponent";

const Tic80: FC<ComponentProcessProps> = ({ id }) => (
<AppContainer StyledComponent={StyledTic80} id={id} useHook={useTic80} />
<AppContainer id={id} useHook={useTic80} />
);

export default Tic80;
8 changes: 7 additions & 1 deletion components/apps/Tic80/useTic80.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ const useTic80 = ({
const { readFile } = useFileSystem();
const loadedUrl = useRef<string>(undefined);
const { appendFileToTitle } = useTitle(id);
const getContentWindow = useIsolatedContentWindow(id, containerRef, true);
const getContentWindow = useIsolatedContentWindow(
id,
containerRef,
undefined,
"canvas { image-rendering: pixelated; }",
true
);
const loadComputer = useCallback(
async (fileUrl?: string) => {
const loadApp = async (blobUrl?: string): Promise<void> => {
Expand Down
15 changes: 7 additions & 8 deletions components/system/Apps/AppContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { memo, useMemo, useRef, useState } from "react";
import { type IStyledComponent } from "styled-components";
import styled, { type IStyledComponent } from "styled-components";
import { type FastOmit } from "styled-components/dist/types";
import StyledLoading from "components/system/Files/FileManager/StyledLoading";
import useFileDrop from "components/system/Files/FileManager/useFileDrop";
Expand All @@ -16,7 +16,7 @@ export type ContainerHookProps = {
type ContainerHook = (props: ContainerHookProps) => void;

type AppContainerProps = {
StyledComponent: IStyledComponent<
StyledComponent?: IStyledComponent<
"web",
FastOmit<
React.DetailedHTMLProps<
Expand All @@ -30,6 +30,8 @@ type AppContainerProps = {
useHook: ContainerHook;
};

const StyledAppContainer = styled.div``;

const AppContainer: FC<AppContainerProps> = ({
id,
useHook,
Expand All @@ -48,19 +50,16 @@ const AppContainer: FC<AppContainerProps> = ({
}),
[loading]
);
const StyledWrapper = StyledComponent || StyledAppContainer;

useHook({ containerRef, id, loading, setLoading, url });

return (
<>
{loading && <StyledLoading />}
<StyledComponent
ref={containerRef}
style={style}
{...useFileDrop({ id })}
>
<StyledWrapper ref={containerRef} style={style} {...useFileDrop({ id })}>
{children}
</StyledComponent>
</StyledWrapper>
</>
);
};
Expand Down
30 changes: 25 additions & 5 deletions components/system/Files/FileManager/useFileDrop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,15 @@ const useFileDrop = ({
[id, mkdirRecursive, updateFolder, url, writeFile]
);
const { openTransferDialog } = useTransferDialog();

return {
onDragLeave,
onDragOver: (event) => {
const onDragOverThenHaltEvent = useCallback(
(event: DragEvent | React.DragEvent<HTMLElement>): void => {
onDragOver?.(event);
haltEvent(event);
},
onDrop: (event) => {
[onDragOver]
);
const onDrop = useCallback(
(event: DragEvent | React.DragEvent<HTMLElement>): void => {
if (MOUNTABLE_EXTENSIONS.has(getExtension(directory))) return;

if (updatePositions && event.target instanceof HTMLElement) {
Expand Down Expand Up @@ -189,6 +190,25 @@ const useFileDrop = ({
hasUpdateId
);
},
[
callback,
directory,
exists,
iconPositions,
id,
openTransferDialog,
processesRef,
setIconPositions,
sortOrders,
updatePositions,
updateProcessUrl,
]
);

return {
onDragLeave,
onDragOver: onDragOverThenHaltEvent,
onDrop,
};
};

Expand Down
Loading

0 comments on commit c00d428

Please sign in to comment.