mirror of
https://github.com/felixrieseberg/windows95.git
synced 2026-05-14 18:31:59 +00:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6aa39e66ec | ||
|
|
ed42ea8e0e | ||
|
|
0779f18071 | ||
|
|
a9c4e38386 | ||
|
|
62b0909cb4 | ||
|
|
873cb75241 | ||
|
|
6467acb0c8 | ||
|
|
ed1bd0a1e0 | ||
|
|
ac84f4164e | ||
|
|
77569d4ce6 | ||
|
|
68b7c181ad | ||
|
|
293491477b | ||
|
|
7eb750752b | ||
|
|
f1488cedc2 | ||
|
|
9f366063eb | ||
|
|
55135f052e | ||
|
|
95fd8e4925 |
@@ -7,8 +7,8 @@ Interested in Electron? Join as at [CovalenceConf](http://covalenceconf.com) in
|
||||
## Downloads
|
||||
| | Windows | macOS | Linux |
|
||||
|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Standalone Download | 📦[Standalone, 32-bit](https://github.com/felixrieseberg/windows95/releases/download/v1.3.0/windows95-win32-1.3.0-standalone-ia32.zip) <br /> 📦[Standalone, 64-bit](https://github.com/felixrieseberg/windows95/releases/download/v1.3.0/windows95-win32-1.3.0-standalone-x64.zip) | 📦[Standalone](https://github.com/felixrieseberg/windows95/releases/download/v1.3.0/windows95-macos-1.3.0.zip) | |
|
||||
| Installer | 💽[Setup, 64-bit](https://github.com/felixrieseberg/windows95/releases/download/v1.3.0/windows95-win32-1.3.0-setup-x64.exe) <br /> 💽[Setup, 32-bit](https://github.com/felixrieseberg/windows95/releases/download/v1.3.0/windows95-win32-1.3.0-setup-ia32.exe) | | 💽[deb, 64-bit](https://github.com/felixrieseberg/windows95/releases/download/v1.3.0/windows95-linux_1.3.0_amd64.deb) <br /> 💽[rpm, 64-bit](https://github.com/felixrieseberg/windows95/releases/download/v1.3.0/windows95-linux-1.3.0.x86_64.rpm) |
|
||||
| Standalone Download | 📦[Standalone, 32-bit](https://github.com/felixrieseberg/windows95/releases/download/v1.4.0/windows95-1.4.0-win32-standalone-ia32.zip) <br /> 📦[Standalone, 64-bit](https://github.com/felixrieseberg/windows95/releases/download/v1.4.0/windows95-1.4.0-win32-standalone-x64.zip) | 📦[Standalone](https://github.com/felixrieseberg/windows95/releases/download/v1.4.0/windows95-macos-1.4.0.zip) | |
|
||||
| Installer | 💽[Setup, 64-bit](https://github.com/felixrieseberg/windows95/releases/download/v1.4.0/windows95-1.4.0-setup-win32-x64.exe) <br /> 💽[Setup, 32-bit](https://github.com/felixrieseberg/windows95/releases/download/v1.4.0/windows95-1.4.0-setup-win32-ia32.exe) | | 💽[deb, 64-bit](https://github.com/felixrieseberg/windows95/releases/download/v1.4.0/windows95-linux-1.4.0_amd64.deb) <br /> 💽[rpm, 64-bit](https://github.com/felixrieseberg/windows95/releases/download/v1.4.0/windows95-linux-1.4.0.x86_64.rpm) |
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -21,16 +21,18 @@ module.exports = {
|
||||
{
|
||||
name: '@electron-forge/maker-squirrel',
|
||||
platforms: ['win32'],
|
||||
config: {
|
||||
name: 'windows95',
|
||||
authors: 'Felix Rieseberg',
|
||||
exe: 'windows95.exe',
|
||||
noMsi: true,
|
||||
remoteReleases: '',
|
||||
setupExe: `windows95-${package.version}-setup-${process.arch}.exe`,
|
||||
setupIcon: path.resolve(__dirname, 'assets', 'icon.ico'),
|
||||
certificateFile: process.env.WINDOWS_CERTIFICATE_FILE,
|
||||
certificatePassword: process.env.WINDOWS_CERTIFICATE_PASSWORD
|
||||
config: (arch) => {
|
||||
return {
|
||||
name: 'windows95',
|
||||
authors: 'Felix Rieseberg',
|
||||
exe: 'windows95.exe',
|
||||
noMsi: true,
|
||||
remoteReleases: '',
|
||||
setupExe: `windows95-${package.version}-setup-${arch}.exe`,
|
||||
setupIcon: path.resolve(__dirname, 'assets', 'icon.ico'),
|
||||
certificateFile: process.env.WINDOWS_CERTIFICATE_FILE,
|
||||
certificatePassword: process.env.WINDOWS_CERTIFICATE_PASSWORD
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
1536
package-lock.json
generated
1536
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
22
package.json
22
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "windows95",
|
||||
"productName": "windows95",
|
||||
"version": "1.4.0",
|
||||
"version": "2.0.0",
|
||||
"description": "Windows 95, in an app. I'm sorry.",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
@@ -28,19 +28,19 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"electron-default-menu": "^1.0.1",
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"fs-extra": "^7.0.0",
|
||||
"fs-extra": "^7.0.1",
|
||||
"update-electron-app": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-forge/cli": "^6.0.0-beta.30",
|
||||
"@electron-forge/maker-deb": "^6.0.0-beta.30",
|
||||
"@electron-forge/maker-flatpak": "^6.0.0-beta.30",
|
||||
"@electron-forge/maker-rpm": "^6.0.0-beta.30",
|
||||
"@electron-forge/maker-squirrel": "^6.0.0-beta.30",
|
||||
"@electron-forge/maker-zip": "^6.0.0-beta.30",
|
||||
"electron": "3.0.13",
|
||||
"standard": "^11.0.1"
|
||||
"@electron-forge/cli": "^6.0.0-beta.32",
|
||||
"@electron-forge/maker-deb": "^6.0.0-beta.32",
|
||||
"@electron-forge/maker-flatpak": "^6.0.0-beta.32",
|
||||
"@electron-forge/maker-rpm": "^6.0.0-beta.32",
|
||||
"@electron-forge/maker-squirrel": "^6.0.0-beta.32",
|
||||
"@electron-forge/maker-zip": "^6.0.0-beta.32",
|
||||
"electron": "4.0.4",
|
||||
"node-abi": "^2.6.0",
|
||||
"standard": "^12.0.1"
|
||||
}
|
||||
}
|
||||
|
||||
24
src/constants.js
Normal file
24
src/constants.js
Normal file
@@ -0,0 +1,24 @@
|
||||
const { remote, app } = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
const _app = app || remote.app
|
||||
|
||||
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')
|
||||
}
|
||||
|
||||
const IPC_COMMANDS = {
|
||||
TOGGLE_INFO: 'TOGGLE_INFO',
|
||||
MACHINE_RESTART: 'MACHINE_RESTART',
|
||||
MACHINE_RESET: 'MACHINE_RESET',
|
||||
MACHINE_CTRL_ALT_DEL: 'MACHINE_CTRL_ALT_DEL',
|
||||
SHOW_DISK_IMAGE: 'SHOW_DISK_IMAGE'
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
CONSTANTS,
|
||||
IPC_COMMANDS
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
const { clearCaches } = require('./cache')
|
||||
const { createMenu } = require('./menu')
|
||||
const { setupProtocol } = require('./es6')
|
||||
|
||||
@@ -21,8 +20,8 @@ let mainWindow
|
||||
const createWindow = () => {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 1280,
|
||||
height: 800,
|
||||
width: 1024,
|
||||
height: 768,
|
||||
useContentSize: true,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
@@ -40,7 +39,6 @@ const createWindow = () => {
|
||||
app.on('ready', async () => {
|
||||
await setupProtocol()
|
||||
await createMenu()
|
||||
await clearCaches()
|
||||
|
||||
createWindow()
|
||||
})
|
||||
|
||||
222
src/menu.js
222
src/menu.js
@@ -1,8 +1,10 @@
|
||||
const { app, shell, Menu, BrowserWindow } = require('electron')
|
||||
const defaultMenu = require('electron-default-menu')
|
||||
|
||||
const { clearCaches } = require('./cache')
|
||||
const { IPC_COMMANDS } = require('./constants')
|
||||
|
||||
const LINKS = {
|
||||
homepage: 'https://www.felixrieseberg.com',
|
||||
homepage: 'https://www.twitter.com/felixrieseberg',
|
||||
repo: 'https://github.com/felixrieseberg/windows95',
|
||||
credits: 'https://github.com/felixrieseberg/windows95/blob/master/CREDITS.md',
|
||||
help: 'https://github.com/felixrieseberg/windows95/blob/master/HELP.md'
|
||||
@@ -17,74 +19,162 @@ function send (cmd) {
|
||||
}
|
||||
|
||||
async function createMenu () {
|
||||
const menu = defaultMenu(app, shell)
|
||||
.map((item) => {
|
||||
if (item.label === 'View') {
|
||||
item.submenu = item.submenu.filter((subItem) => {
|
||||
return subItem.label !== 'Reload'
|
||||
})
|
||||
}
|
||||
|
||||
if (item.label === 'Help') {
|
||||
item.submenu = [
|
||||
{
|
||||
label: 'Author',
|
||||
click() {
|
||||
shell.openExternal(LINKS.homepage)
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Learn More',
|
||||
click() {
|
||||
shell.openExternal(LINKS.repo)
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Help',
|
||||
click() {
|
||||
shell.openExternal(LINKS.help)
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Credits',
|
||||
click() {
|
||||
shell.openExternal(LINKS.credits)
|
||||
}
|
||||
const template = [
|
||||
{
|
||||
label: 'View',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Toggle Full Screen',
|
||||
accelerator: (function () {
|
||||
if (process.platform === 'darwin') { return 'Ctrl+Command+F' } else { return 'F11' }
|
||||
})(),
|
||||
click: function (_item, focusedWindow) {
|
||||
if (focusedWindow) { focusedWindow.setFullScreen(!focusedWindow.isFullScreen()) }
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Toggle Developer Tools',
|
||||
accelerator: (function () {
|
||||
if (process.platform === 'darwin') { return 'Alt+Command+I' } else { return 'Ctrl+Shift+I' }
|
||||
})(),
|
||||
click: function (_item, focusedWindow) {
|
||||
if (focusedWindow) { focusedWindow.toggleDevTools() }
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Toggle Emulator Info',
|
||||
click: () => send(IPC_COMMANDS.TOGGLE_INFO)
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Window',
|
||||
role: 'window',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Minimize',
|
||||
accelerator: 'CmdOrCtrl+M',
|
||||
role: 'minimize'
|
||||
},
|
||||
{
|
||||
label: 'Close',
|
||||
accelerator: 'CmdOrCtrl+W',
|
||||
role: 'close'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Machine',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Send Ctrl+Alt+Del',
|
||||
click: () => send(IPC_COMMANDS.MACHINE_CTRL_ALT_DEL)
|
||||
},
|
||||
{
|
||||
label: 'Restart',
|
||||
click: () => send(IPC_COMMANDS.MACHINE_RESTART)
|
||||
},
|
||||
{
|
||||
label: 'Reset',
|
||||
click: () => send(IPC_COMMANDS.MACHINE_RESET)
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Go to Disk Image',
|
||||
click: () => send(IPC_COMMANDS.SHOW_DISK_IMAGE)
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Help',
|
||||
role: 'help',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Author',
|
||||
click: () => shell.openExternal(LINKS.homepage)
|
||||
},
|
||||
{
|
||||
label: 'windows95 on GitHub',
|
||||
click: () => shell.openExternal(LINKS.repo)
|
||||
},
|
||||
{
|
||||
label: 'Help',
|
||||
click: () => shell.openExternal(LINKS.help)
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Troubleshooting',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Clear Cache and Restart',
|
||||
async click () {
|
||||
await clearCaches()
|
||||
|
||||
return item
|
||||
})
|
||||
.filter((item) => {
|
||||
return item.label !== 'Edit'
|
||||
app.relaunch()
|
||||
app.quit()
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
template.unshift({
|
||||
label: 'windows95',
|
||||
submenu: [
|
||||
{
|
||||
label: 'About windows95',
|
||||
role: 'about'
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Services',
|
||||
role: 'services',
|
||||
submenu: []
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Hide windows95',
|
||||
accelerator: 'Command+H',
|
||||
role: 'hide'
|
||||
},
|
||||
{
|
||||
label: 'Hide Others',
|
||||
accelerator: 'Command+Shift+H',
|
||||
role: 'hideothers'
|
||||
},
|
||||
{
|
||||
label: 'Show All',
|
||||
role: 'unhide'
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Quit',
|
||||
accelerator: 'Command+Q',
|
||||
click () {
|
||||
app.quit()
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
menu.splice(1, 0, {
|
||||
label: 'Machine',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Send Ctrl+Alt+Del',
|
||||
click: () => send('ctrlaltdel')
|
||||
},
|
||||
{
|
||||
label: 'Restart',
|
||||
click: () => send('restart')
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Go to Disk Image',
|
||||
click: () => send('disk-image')
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
Menu.setApplicationMenu(Menu.buildFromTemplate(menu))
|
||||
Menu.setApplicationMenu(Menu.buildFromTemplate(template))
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -1,45 +1,42 @@
|
||||
const { remote, shell, ipcRenderer } = require('electron')
|
||||
const path = require('path')
|
||||
const EventEmitter = require('events')
|
||||
|
||||
const { STATE_PATH, resetState, restoreState, saveState } = require('./state')
|
||||
const { resetState, restoreState, saveState } = require('./state')
|
||||
const { getDiskImageSize } = require('./utils/disk-image-size')
|
||||
const { IPC_COMMANDS, CONSTANTS } = require('./constants')
|
||||
|
||||
window.windows95 = {
|
||||
STATE_PATH,
|
||||
restoreState,
|
||||
resetState,
|
||||
saveState,
|
||||
class Windows95 extends EventEmitter {
|
||||
constructor () {
|
||||
super()
|
||||
|
||||
// Constants
|
||||
this.CONSTANTS = CONSTANTS
|
||||
this.IPC_COMMANDS = IPC_COMMANDS
|
||||
|
||||
// Methods
|
||||
this.getDiskImageSize = getDiskImageSize
|
||||
this.restoreState = restoreState
|
||||
this.resetState = resetState
|
||||
this.saveState = saveState
|
||||
|
||||
Object.keys(IPC_COMMANDS).forEach((command) => {
|
||||
ipcRenderer.on(command, (...args) => {
|
||||
this.emit(command, args)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
showDiskImage () {
|
||||
const imagePath = path.join(__dirname, 'images/windows95.img')
|
||||
.replace('app.asar', 'app.asar.unpacked')
|
||||
|
||||
shell.showItemInFolder(imagePath)
|
||||
},
|
||||
}
|
||||
|
||||
quit: () => remote.app.quit()
|
||||
quit () {
|
||||
remote.app.quit()
|
||||
}
|
||||
}
|
||||
|
||||
ipcRenderer.on('ctrlaltdel', () => {
|
||||
if (!window.emulator || !window.emulator.is_running) return
|
||||
|
||||
window.emulator.keyboard_send_scancodes([
|
||||
0x1D, // ctrl
|
||||
0x38, // alt
|
||||
0x53, // delete
|
||||
|
||||
// break codes
|
||||
0x1D | 0x80,
|
||||
0x38 | 0x80,
|
||||
0x53 | 0x80
|
||||
])
|
||||
})
|
||||
|
||||
ipcRenderer.on('restart', () => {
|
||||
if (!window.emulator || !window.emulator.is_running) return
|
||||
|
||||
window.emulator.restart()
|
||||
})
|
||||
|
||||
ipcRenderer.on('disk-image', () => {
|
||||
windows95.showDiskImage()
|
||||
})
|
||||
window.windows95 = new Windows95()
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
export function setupState () {
|
||||
window.appState = {
|
||||
isResetting: false,
|
||||
isQuitting: false,
|
||||
cursorCaptured: false,
|
||||
floppyFile: null,
|
||||
bootFresh: false,
|
||||
infoInterval: null
|
||||
bootFresh: false
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -13,7 +13,7 @@ export function setupButtons (start) {
|
||||
$('#discard-state').addEventListener('click', () => {
|
||||
window.appState.bootFresh = true
|
||||
|
||||
start('win95')
|
||||
start()
|
||||
})
|
||||
|
||||
// Floppy
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<div id="status">
|
||||
Disk: <span id="disk-status">Idle</span>
|
||||
| CPU Speed: <span id="cpu-status">0</span>
|
||||
| <a onclick="document.querySelector('#status').style.display='none'">Hide</a>
|
||||
| <a href="#" id="toggle-status">Hide</a>
|
||||
</div>
|
||||
<div id="buttons">
|
||||
<div id="start-buttons">
|
||||
|
||||
@@ -1,28 +1,63 @@
|
||||
const $ = document.querySelector.bind(document)
|
||||
const status = $('#status')
|
||||
const diskStatus = $('#disk-status')
|
||||
const cpuStatus = $('#cpu-status')
|
||||
const toggleStatus = $('#toggle-status')
|
||||
|
||||
export function setupInfo () {
|
||||
const diskStatus = $('#disk-status')
|
||||
const cpuStatus = $('#cpu-status')
|
||||
let lastCounter = 0
|
||||
let lastTick = 0
|
||||
let lastCounter = 0
|
||||
let lastTick = 0
|
||||
let infoInterval = null
|
||||
|
||||
window.emulator.add_listener('ide-read-start', () => {
|
||||
diskStatus.innerHTML = 'Read'
|
||||
})
|
||||
const onIDEReadStart = () => {
|
||||
diskStatus.innerHTML = 'Read'
|
||||
}
|
||||
|
||||
window.emulator.add_listener('ide-read-end', () => {
|
||||
diskStatus.innerHTML = 'Idle'
|
||||
})
|
||||
const onIDEReadWriteEnd = () => {
|
||||
diskStatus.innerHTML = 'Idle'
|
||||
}
|
||||
|
||||
window.emulator.add_listener('ide-write-end', () => {
|
||||
diskStatus.innerHTML = 'Idle'
|
||||
})
|
||||
toggleStatus.onclick = toggleInfo
|
||||
|
||||
window.emulator.add_listener('screen-set-size-graphical', (...args) => {
|
||||
console.log(...args)
|
||||
})
|
||||
/**
|
||||
* Toggle the information display
|
||||
*/
|
||||
export function toggleInfo () {
|
||||
if (status.style.display !== 'none') {
|
||||
disableInfo()
|
||||
} else {
|
||||
enableInfo()
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(() => {
|
||||
/**
|
||||
* Start information gathering, but only if the panel is visible
|
||||
*/
|
||||
export function startInfoMaybe () {
|
||||
if (status.style.display !== 'none') {
|
||||
enableInfo()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the gathering of information (and hide the little information tab)
|
||||
*/
|
||||
export function enableInfo () {
|
||||
// Show the info thingy
|
||||
status.style.display = 'block'
|
||||
|
||||
// We can only do the rest with an emulator
|
||||
if (!window.emulator.add_listener) {
|
||||
return
|
||||
}
|
||||
|
||||
// Set listeners
|
||||
window.emulator.add_listener('ide-read-start', onIDEReadStart)
|
||||
window.emulator.add_listener('ide-read-end', onIDEReadWriteEnd)
|
||||
window.emulator.add_listener('ide-write-end', onIDEReadWriteEnd)
|
||||
window.emulator.add_listener('screen-set-size-graphical', console.log)
|
||||
|
||||
// Set an interval
|
||||
infoInterval = setInterval(() => {
|
||||
const now = Date.now()
|
||||
const instructionCounter = window.emulator.get_instruction_counter()
|
||||
const ips = instructionCounter - lastCounter
|
||||
@@ -34,3 +69,26 @@ export function setupInfo () {
|
||||
cpuStatus.innerHTML = Math.round(ips / deltaTime)
|
||||
}, 500)
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the gathering of information (and hide the little information tab)
|
||||
*/
|
||||
export function disableInfo () {
|
||||
// Hide the info thingy
|
||||
status.style.display = 'none'
|
||||
|
||||
// Clear the interval
|
||||
clearInterval(infoInterval)
|
||||
infoInterval = null
|
||||
|
||||
// We can only do the rest with an emulator
|
||||
if (!window.emulator.remove_listener) {
|
||||
return
|
||||
}
|
||||
|
||||
// Unset the listeners
|
||||
window.emulator.remove_listener('ide-read-start', onIDEReadStart)
|
||||
window.emulator.remove_listener('ide-read-end', onIDEReadWriteEnd)
|
||||
window.emulator.remove_listener('ide-write-end', onIDEReadWriteEnd)
|
||||
window.emulator.remove_listener('screen-set-size-graphical', console.log)
|
||||
}
|
||||
|
||||
44
src/renderer/ipc.js
Normal file
44
src/renderer/ipc.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import { toggleInfo } from 'es6://info.js'
|
||||
|
||||
export function setupIpcListeners (start) {
|
||||
const { windows95 } = window
|
||||
|
||||
windows95.addListener(windows95.IPC_COMMANDS.TOGGLE_INFO, () => {
|
||||
toggleInfo()
|
||||
})
|
||||
|
||||
windows95.addListener(windows95.IPC_COMMANDS.MACHINE_RESTART, () => {
|
||||
console.log(`Restarting machine`)
|
||||
|
||||
if (!window.emulator || !window.emulator.is_running) return
|
||||
|
||||
window.emulator.restart()
|
||||
})
|
||||
|
||||
windows95.addListener(windows95.IPC_COMMANDS.MACHINE_RESET, () => {
|
||||
console.log(`Resetting machine`)
|
||||
|
||||
window.appState.isResetting = true
|
||||
document.location.hash = `#AUTO_START`
|
||||
document.location.reload()
|
||||
})
|
||||
|
||||
windows95.addListener(windows95.IPC_COMMANDS.MACHINE_CTRL_ALT_DEL, () => {
|
||||
if (!window.emulator || !window.emulator.is_running) return
|
||||
|
||||
window.emulator.keyboard_send_scancodes([
|
||||
0x1D, // ctrl
|
||||
0x38, // alt
|
||||
0x53, // delete
|
||||
|
||||
// break codes
|
||||
0x1D | 0x80,
|
||||
0x38 | 0x80,
|
||||
0x53 | 0x80
|
||||
])
|
||||
})
|
||||
|
||||
windows95.addListener(windows95.IPC_COMMANDS.SHOW_DISK_IMAGE, () => {
|
||||
windows95.showDiskImage()
|
||||
})
|
||||
}
|
||||
@@ -9,6 +9,7 @@ export function setupCloseListener () {
|
||||
|
||||
window.onbeforeunload = (event) => {
|
||||
if (window.appState.isQuitting) return
|
||||
if (window.appState.isResetting) return
|
||||
|
||||
handleClose()
|
||||
event.preventDefault()
|
||||
@@ -22,6 +23,7 @@ export function setupEscListener () {
|
||||
if (evt.keyCode === 27) {
|
||||
if (window.appState.cursorCaptured) {
|
||||
window.appState.cursorCaptured = false
|
||||
window.emulator.mouse_set_status(false)
|
||||
document.exitPointerLock()
|
||||
} else {
|
||||
window.appState.cursorCaptured = true
|
||||
@@ -31,11 +33,15 @@ export function setupEscListener () {
|
||||
}
|
||||
}
|
||||
|
||||
export function setupClickListener () {
|
||||
document.addEventListener('click', () => {
|
||||
if (!window.appState.cursorCaptured) {
|
||||
window.appState.cursorCaptured = true
|
||||
window.emulator.lock_mouse()
|
||||
}
|
||||
})
|
||||
function onDocumentClick () {
|
||||
if (!window.appState.cursorCaptured) {
|
||||
window.appState.cursorCaptured = true
|
||||
window.emulator.mouse_set_status(true)
|
||||
window.emulator.lock_mouse()
|
||||
}
|
||||
}
|
||||
|
||||
export function setupClickListener () {
|
||||
document.removeEventListener('click', onDocumentClick)
|
||||
document.addEventListener('click', onDocumentClick)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
/* We're using modern esm imports here */
|
||||
import { setupState } from 'es6://app-state.js'
|
||||
import { setupClickListener, setupEscListener, setupCloseListener } from 'es6://listeners.js'
|
||||
import { toggleButtons, setupButtons } from 'es6://buttons.js'
|
||||
import { setupInfo } from 'es6://info.js'
|
||||
import { startInfoMaybe } from 'es6://info.js'
|
||||
import { setupIpcListeners } from 'es6://ipc.js'
|
||||
|
||||
setupState()
|
||||
|
||||
@@ -9,9 +11,10 @@ setupState()
|
||||
* The main method executing the VM.
|
||||
*/
|
||||
async function main () {
|
||||
// New v86 instance
|
||||
window.emulator = new V86Starter({
|
||||
memory_size: 64 * 1024 * 1024,
|
||||
const imageSize = await window.windows95.getDiskImageSize()
|
||||
const options = {
|
||||
memory_size: 128 * 1024 * 1024,
|
||||
video_memory_size: 32 * 1024 * 1024,
|
||||
screen_container: document.getElementById('emulator'),
|
||||
bios: {
|
||||
url: './bios/seabios.bin'
|
||||
@@ -22,21 +25,19 @@ async function main () {
|
||||
hda: {
|
||||
url: '../images/windows95.img',
|
||||
async: true,
|
||||
size: 242049024
|
||||
size: imageSize
|
||||
},
|
||||
fda: {
|
||||
buffer: window.appState.floppyFile || undefined
|
||||
},
|
||||
boot_order: 0x132
|
||||
})
|
||||
|
||||
// High DPI support
|
||||
if (navigator.userAgent.includes('Windows')) {
|
||||
const scale = window.devicePixelRatio
|
||||
|
||||
window.emulator.screen_adapter.set_scale(scale, scale)
|
||||
}
|
||||
|
||||
console.log(`Starting emulator with options`, options)
|
||||
|
||||
// New v86 instance
|
||||
window.emulator = new V86Starter(options)
|
||||
|
||||
// Restore state. We can't do this right away
|
||||
// and randomly chose 500ms as the appropriate
|
||||
// wait time (lol)
|
||||
@@ -45,7 +46,7 @@ async function main () {
|
||||
windows95.restoreState()
|
||||
}
|
||||
|
||||
setupInfo()
|
||||
startInfoMaybe()
|
||||
|
||||
window.appState.cursorCaptured = true
|
||||
window.emulator.lock_mouse()
|
||||
@@ -61,6 +62,11 @@ function start () {
|
||||
main()
|
||||
}
|
||||
|
||||
setupIpcListeners(start)
|
||||
setupEscListener()
|
||||
setupCloseListener()
|
||||
setupButtons(start)
|
||||
|
||||
if (document.location.hash.includes('AUTO_START')) {
|
||||
start()
|
||||
}
|
||||
|
||||
37
src/state.js
37
src/state.js
@@ -1,9 +1,6 @@
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const { remote } = require('electron')
|
||||
|
||||
const DEFAULT_PATH = path.join(__dirname, 'images/default-state.bin')
|
||||
const STATE_PATH = path.join(remote.app.getPath('userData'), 'state.bin')
|
||||
const { CONSTANTS } = require('./constants')
|
||||
|
||||
/**
|
||||
* Returns the current machine's state - either what
|
||||
@@ -12,11 +9,13 @@ const STATE_PATH = path.join(remote.app.getPath('userData'), 'state.bin')
|
||||
* @returns {ArrayBuffer}
|
||||
*/
|
||||
function getState () {
|
||||
const statePath = fs.existsSync(STATE_PATH)
|
||||
? STATE_PATH
|
||||
: DEFAULT_PATH
|
||||
const statePath = fs.existsSync(CONSTANTS.STATE_PATH)
|
||||
? CONSTANTS.STATE_PATH
|
||||
: CONSTANTS.DEFAULT_STATE_PATH
|
||||
|
||||
return fs.readFileSync(statePath).buffer
|
||||
if (fs.existsSync(statePath)) {
|
||||
return fs.readFileSync(statePath).buffer
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -25,8 +24,8 @@ function getState () {
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function resetState () {
|
||||
if (fs.existsSync(STATE_PATH)) {
|
||||
return fs.remove(STATE_PATH)
|
||||
if (fs.existsSync(CONSTANTS.STATE_PATH)) {
|
||||
return fs.remove(CONSTANTS.STATE_PATH)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,13 +42,13 @@ async function saveState () {
|
||||
|
||||
window.emulator.save_state(async (error, newState) => {
|
||||
if (error) {
|
||||
console.log(error)
|
||||
console.warn(`State: Could not save state`, error)
|
||||
return
|
||||
}
|
||||
|
||||
await fs.outputFile(STATE_PATH, Buffer.from(newState))
|
||||
await fs.outputFile(CONSTANTS.STATE_PATH, Buffer.from(newState))
|
||||
|
||||
console.log(`Saved state to ${STATE_PATH}`)
|
||||
console.log(`State: Saved state to ${CONSTANTS.STATE_PATH}`)
|
||||
|
||||
resolve()
|
||||
})
|
||||
@@ -60,15 +59,21 @@ async function saveState () {
|
||||
* Restores the VM's state.
|
||||
*/
|
||||
function restoreState () {
|
||||
const state = getState()
|
||||
|
||||
// Nothing to do with if we don't have a state
|
||||
if (!state) {
|
||||
console.log(`State: No state present, not restoring.`)
|
||||
}
|
||||
|
||||
try {
|
||||
window.emulator.restore_state(getState())
|
||||
window.emulator.restore_state(state)
|
||||
} catch (error) {
|
||||
console.log(`Could not read state file. Maybe none exists?`, error)
|
||||
console.log(`State: Could not read state file. Maybe none exists?`, error)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
STATE_PATH,
|
||||
saveState,
|
||||
restoreState,
|
||||
resetState,
|
||||
|
||||
26
src/utils/disk-image-size.js
Normal file
26
src/utils/disk-image-size.js
Normal file
@@ -0,0 +1,26 @@
|
||||
const fs = require('fs-extra')
|
||||
|
||||
const { CONSTANTS } = require('../constants')
|
||||
|
||||
/**
|
||||
* Get the size of the disk image
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
async function getDiskImageSize () {
|
||||
try {
|
||||
const stats = await fs.stat(CONSTANTS.IMAGE_PATH)
|
||||
|
||||
if (stats) {
|
||||
return stats.size
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Could not determine image size`, error)
|
||||
}
|
||||
|
||||
return CONSTANTS.IMAGE_DEFAULT_SIZE
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getDiskImageSize
|
||||
}
|
||||
Reference in New Issue
Block a user