mirror of
https://github.com/felixrieseberg/windows95.git
synced 2026-05-14 18:31:59 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b720750db | ||
|
|
ee317ec5aa | ||
|
|
d7c657e671 | ||
|
|
7a8a54c76b | ||
|
|
c29f98b6bc | ||
|
|
8d1847a8d1 | ||
|
|
194f4fabaf | ||
|
|
3f4a5e97fa | ||
|
|
3eb789d055 | ||
|
|
8a8f064864 |
8
.github/workflows/build.yml
vendored
8
.github/workflows/build.yml
vendored
@@ -83,18 +83,12 @@ jobs:
|
||||
run: yarn
|
||||
- name: Make
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
run: yarn make
|
||||
run: yarn make --arch=all
|
||||
env:
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||
WINDOWS_CODESIGN_FILE: ${{ steps.write_file.outputs.filePath }}
|
||||
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
||||
- name: Make (ia32)
|
||||
if: matrix.os == 'windows-latest' && startsWith(github.ref, 'refs/tags/')
|
||||
run: yarn make -- --arch=ia32
|
||||
env:
|
||||
WINDOWS_CODESIGN_FILE: ${{ steps.write_file.outputs.filePath }}
|
||||
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
||||
# - name: Archive production artifacts
|
||||
# uses: actions/upload-artifact@v2
|
||||
# with:
|
||||
|
||||
@@ -5,8 +5,8 @@ This is Windows 95, running in an [Electron](https://electronjs.org/) app. Yes,
|
||||
## Downloads
|
||||
| | Windows | macOS | Linux |
|
||||
|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Standalone Download | 📦[Standalone, 32-bit](https://github.com/felixrieseberg/windows95/releases/download/v2.2.1/windows95-2.2.1-win32-standalone-ia32.zip) <br /> 📦[Standalone, 64-bit](https://github.com/felixrieseberg/windows95/releases/download/v2.2.1/windows95-2.2.1-win32-standalone-x64.zip) | 📦[Standalone](https://github.com/felixrieseberg/windows95/releases/download/v2.2.1/windows95-macos-2.2.1.zip) | |
|
||||
| Installer | 💽[Setup, 64-bit](https://github.com/felixrieseberg/windows95/releases/download/v2.2.1/windows95-2.2.1-setup-win32-x64.exe) <br /> 💽[Setup, 32-bit](https://github.com/felixrieseberg/windows95/releases/download/v2.2.1/windows95-2.2.1-setup-win32-ia32.exe) | | 💽[deb, 64-bit](https://github.com/felixrieseberg/windows95/releases/download/v2.2.1/windows95-linux-2.2.1_amd64.deb) <br /> 💽[rpm, 64-bit](https://github.com/felixrieseberg/windows95/releases/download/v2.2.1/windows95-linux-2.2.1.x86_64.rpm) |
|
||||
| Standalone Download | 📦[Standalone, 32-bit](https://github.com/felixrieseberg/windows95/releases/download/v2.2.2/windows95-win32-ia32-2.2.2.zip) <br /> 📦[Standalone, 64-bit](https://github.com/felixrieseberg/windows95/releases/download/v2.2.2/windows95-win32-x64-2.2.2.zip) | 📦[Standalone](https://github.com/felixrieseberg/windows95/releases/download/v2.2.2/windows95-darwin-x64-2.2.2.zip) | |
|
||||
| Installer | 💽[Setup, 64-bit](https://github.com/felixrieseberg/windows95/releases/download/v2.2.2/windows95-2.2.2-setup-x64.exe) <br /> 💽[Setup, 32-bit](https://github.com/felixrieseberg/windows95/releases/download/v2.2.2/windows95-2.2.2-setup-ia32.exe) | | 💽[deb, 64-bit](https://github.com/felixrieseberg/windows95/releases/download/v2.2.2/windows95_2.2.2_amd64.deb) <br /> 💽[rpm, 64-bit](https://github.com/felixrieseberg/windows95/releases/download/v2.2.2/windows95-2.2.2-1.x86_64.rpm) |
|
||||
|
||||

|
||||
|
||||
|
||||
47
package.json
47
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "windows95",
|
||||
"productName": "windows95",
|
||||
"version": "2.2.2",
|
||||
"version": "2.3.0",
|
||||
"description": "Windows 95, in an app. I'm sorry.",
|
||||
"main": "./dist/src/main/main",
|
||||
"scripts": {
|
||||
@@ -10,7 +10,8 @@
|
||||
"make": "electron-forge make",
|
||||
"publish": "electron-forge publish",
|
||||
"lint": "prettier --write src/**/*.{ts,tsx}",
|
||||
"less": "node ./tools/lessc.js"
|
||||
"less": "node ./tools/lessc.js",
|
||||
"tsc": "tsc -p tsconfig.json --noEmit"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Felix Rieseberg, felix@felixrieseberg.com",
|
||||
@@ -21,30 +22,30 @@
|
||||
"dependencies": {
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"fs-extra": "^9.0.1",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"tslib": "^2.0.0",
|
||||
"update-electron-app": "^1.5.0"
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"tslib": "^2.0.3",
|
||||
"update-electron-app": "^2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-forge/cli": "^6.0.0-beta.52",
|
||||
"@electron-forge/maker-deb": "^6.0.0-beta.52",
|
||||
"@electron-forge/maker-flatpak": "^6.0.0-beta.52",
|
||||
"@electron-forge/maker-rpm": "^6.0.0-beta.52",
|
||||
"@electron-forge/maker-squirrel": "^6.0.0-beta.52",
|
||||
"@electron-forge/maker-zip": "^6.0.0-beta.52",
|
||||
"@electron-forge/publisher-github": "^6.0.0-beta.52",
|
||||
"@types/fs-extra": "^9.0.1",
|
||||
"@types/node": "^12.12.14",
|
||||
"@types/react": "^16.9.44",
|
||||
"@types/react-dom": "^16.9.8",
|
||||
"electron": "9.1.2",
|
||||
"less": "^3.12.2",
|
||||
"node-abi": "^2.18.0",
|
||||
"@electron-forge/cli": "^6.0.0-beta.54",
|
||||
"@electron-forge/maker-deb": "^6.0.0-beta.54",
|
||||
"@electron-forge/maker-flatpak": "^6.0.0-beta.54",
|
||||
"@electron-forge/maker-rpm": "^6.0.0-beta.54",
|
||||
"@electron-forge/maker-squirrel": "^6.0.0-beta.54",
|
||||
"@electron-forge/maker-zip": "^6.0.0-beta.54",
|
||||
"@electron-forge/publisher-github": "^6.0.0-beta.54",
|
||||
"@types/fs-extra": "^9.0.5",
|
||||
"@types/node": "^12.19.9",
|
||||
"@types/react": "^17.0.0",
|
||||
"@types/react-dom": "^17.0.0",
|
||||
"electron": "11.1.0",
|
||||
"less": "^3.13.0",
|
||||
"node-abi": "^2.19.3",
|
||||
"parcel-bundler": "^1.12.4",
|
||||
"prettier": "^2.0.5",
|
||||
"prettier": "^2.2.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"standard": "^14.3.4",
|
||||
"typescript": "^3.9.7"
|
||||
"standard": "^16.0.3",
|
||||
"typescript": "^4.1.3"
|
||||
}
|
||||
}
|
||||
|
||||
41
src/cache.ts
41
src/cache.ts
@@ -11,27 +11,24 @@ export async function clearCache() {
|
||||
}
|
||||
}
|
||||
|
||||
export function clearStorageData() {
|
||||
return new Promise((resolve) => {
|
||||
if (!session.defaultSession) {
|
||||
return resolve();
|
||||
}
|
||||
export async function clearStorageData() {
|
||||
if (!session.defaultSession) {
|
||||
return;
|
||||
}
|
||||
|
||||
session.defaultSession.clearStorageData(
|
||||
{
|
||||
storages: [
|
||||
"appcache",
|
||||
"cookies",
|
||||
"filesystem",
|
||||
"indexdb",
|
||||
"localstorage",
|
||||
"shadercache",
|
||||
"websql",
|
||||
"serviceworkers",
|
||||
],
|
||||
quotas: ["temporary", "persistent", "syncable"],
|
||||
},
|
||||
resolve
|
||||
);
|
||||
});
|
||||
await session.defaultSession.clearStorageData(
|
||||
{
|
||||
storages: [
|
||||
"appcache",
|
||||
"cookies",
|
||||
"filesystem",
|
||||
"indexdb",
|
||||
"localstorage",
|
||||
"shadercache",
|
||||
"websql",
|
||||
"serviceworkers",
|
||||
],
|
||||
quotas: ["temporary", "persistent", "syncable"],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import { remote, app } from "electron";
|
||||
import * as path from "path";
|
||||
|
||||
const _app = app || remote.app;
|
||||
|
||||
export const CONSTANTS = {
|
||||
IMAGE_PATH: path.join(__dirname, "../../images/windows95.img"),
|
||||
IMAGE_DEFAULT_SIZE: 1073741824, // 1GB
|
||||
DEFAULT_STATE_PATH: path.join(__dirname, "../../images/default-state.bin"),
|
||||
STATE_PATH: path.join(_app.getPath("userData"), "state-v2.bin"),
|
||||
DEFAULT_STATE_PATH: path.join(__dirname, "../../images/default-state.bin")
|
||||
};
|
||||
|
||||
export const IPC_COMMANDS = {
|
||||
@@ -28,4 +24,7 @@ export const IPC_COMMANDS = {
|
||||
// Machine events
|
||||
MACHINE_STARTED: "MACHINE_STARTED",
|
||||
MACHINE_STOPPED: "MACHINE_STOPPED",
|
||||
// Else
|
||||
APP_QUIT: "APP_QUIT",
|
||||
GET_STATE_PATH: "GET_STATE_PATH",
|
||||
};
|
||||
|
||||
14
src/main/ipc.ts
Normal file
14
src/main/ipc.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { ipcMain, app } from 'electron';
|
||||
import * as path from "path";
|
||||
|
||||
import { IPC_COMMANDS } from '../constants';
|
||||
|
||||
export function setupIpcListeners() {
|
||||
ipcMain.handle(IPC_COMMANDS.GET_STATE_PATH, () => {
|
||||
return path.join(app.getPath("userData"), "state-v2.bin")
|
||||
});
|
||||
|
||||
ipcMain.handle(IPC_COMMANDS.APP_QUIT, () => {
|
||||
app.quit();
|
||||
});
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import { shouldQuit } from "./squirrel";
|
||||
import { setupUpdates } from "./update";
|
||||
import { getOrCreateWindow } from "./windows";
|
||||
import { setupMenu } from "./menu";
|
||||
import { setupIpcListeners } from './ipc';
|
||||
|
||||
/**
|
||||
* Handle the app's "ready" event. This is essentially
|
||||
@@ -14,6 +15,7 @@ import { setupMenu } from "./menu";
|
||||
export async function onReady() {
|
||||
if (!isDevMode()) process.env.NODE_ENV = "production";
|
||||
|
||||
setupIpcListeners();
|
||||
getOrCreateWindow();
|
||||
setupAboutPanel();
|
||||
setupMenu();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from "react";
|
||||
import * as fs from "fs-extra";
|
||||
|
||||
import { CONSTANTS } from "../constants";
|
||||
import { getStatePath } from './utils/get-state-path';
|
||||
|
||||
interface CardSettingsProps {
|
||||
bootFromScratch: () => void;
|
||||
@@ -152,8 +152,10 @@ export class CardSettings extends React.Component<
|
||||
* Handle the state reset
|
||||
*/
|
||||
private async onResetState() {
|
||||
if (fs.existsSync(CONSTANTS.STATE_PATH)) {
|
||||
await fs.remove(CONSTANTS.STATE_PATH);
|
||||
const statePath = await getStatePath();
|
||||
|
||||
if (fs.existsSync(statePath)) {
|
||||
await fs.remove(statePath);
|
||||
}
|
||||
|
||||
this.setState({ isStateReset: true });
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from "react";
|
||||
import * as fs from "fs-extra";
|
||||
import * as path from "path";
|
||||
import { ipcRenderer, remote, shell } from "electron";
|
||||
import { ipcRenderer, shell } from "electron";
|
||||
|
||||
import { CONSTANTS, IPC_COMMANDS } from "../constants";
|
||||
import { getDiskImageSize } from "../utils/disk-image-size";
|
||||
@@ -10,6 +10,7 @@ import { StartMenu } from "./start-menu";
|
||||
import { CardSettings } from "./card-settings";
|
||||
import { EmulatorInfo } from "./emulator-info";
|
||||
import { CardDrive } from "./card-drive";
|
||||
import { getStatePath } from './utils/get-state-path';
|
||||
|
||||
export interface EmulatorState {
|
||||
currentUiCard: string;
|
||||
@@ -95,11 +96,11 @@ export class Emulator extends React.Component<{}, EmulatorState> {
|
||||
this.isQuitting = true;
|
||||
|
||||
setImmediate(() => {
|
||||
remote.app.quit();
|
||||
ipcRenderer.invoke(IPC_COMMANDS.APP_QUIT);
|
||||
});
|
||||
};
|
||||
|
||||
window.onbeforeunload = (event) => {
|
||||
window.onbeforeunload = (event: Event) => {
|
||||
if (this.isQuitting || this.isResetting) {
|
||||
console.log(`Unload: Not preventing`);
|
||||
return;
|
||||
@@ -366,6 +367,7 @@ export class Emulator extends React.Component<{}, EmulatorState> {
|
||||
*/
|
||||
private async saveState(): Promise<void> {
|
||||
const { emulator } = this.state;
|
||||
const statePath = await getStatePath();
|
||||
|
||||
return new Promise((resolve) => {
|
||||
if (!emulator || !emulator.save_state) {
|
||||
@@ -379,9 +381,9 @@ export class Emulator extends React.Component<{}, EmulatorState> {
|
||||
return resolve();
|
||||
}
|
||||
|
||||
await fs.outputFile(CONSTANTS.STATE_PATH, Buffer.from(newState));
|
||||
await fs.outputFile(statePath, Buffer.from(newState));
|
||||
|
||||
console.log(`saveState: Saved state to ${CONSTANTS.STATE_PATH}`);
|
||||
console.log(`saveState: Saved state to ${statePath}`);
|
||||
|
||||
resolve();
|
||||
});
|
||||
@@ -391,9 +393,9 @@ export class Emulator extends React.Component<{}, EmulatorState> {
|
||||
/**
|
||||
* Restores state to the emulator.
|
||||
*/
|
||||
private restoreState() {
|
||||
private async restoreState() {
|
||||
const { emulator } = this.state;
|
||||
const state = this.getState();
|
||||
const state = await this.getState();
|
||||
|
||||
// Nothing to do with if we don't have a state
|
||||
if (!state) {
|
||||
@@ -420,9 +422,10 @@ export class Emulator extends React.Component<{}, EmulatorState> {
|
||||
*
|
||||
* @returns {ArrayBuffer}
|
||||
*/
|
||||
private getState(): ArrayBuffer | null {
|
||||
const statePath = fs.existsSync(CONSTANTS.STATE_PATH)
|
||||
? CONSTANTS.STATE_PATH
|
||||
private async getState(): Promise<ArrayBuffer | null> {
|
||||
const expectedStatePath = await getStatePath();
|
||||
const statePath = fs.existsSync(expectedStatePath)
|
||||
? expectedStatePath
|
||||
: CONSTANTS.DEFAULT_STATE_PATH;
|
||||
|
||||
if (fs.existsSync(statePath)) {
|
||||
|
||||
14
src/renderer/utils/get-state-path.ts
Normal file
14
src/renderer/utils/get-state-path.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { IPC_COMMANDS } from '../../constants';
|
||||
|
||||
let _statePath = '';
|
||||
|
||||
export async function getStatePath(): Promise<string> {
|
||||
if (_statePath) {
|
||||
return _statePath;
|
||||
}
|
||||
|
||||
const statePath = await ipcRenderer.invoke(IPC_COMMANDS.GET_STATE_PATH);
|
||||
return _statePath = statePath;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user