mirror of
https://github.com/He4eT/cheap-glkote.git
synced 2026-05-05 00:47:28 +00:00
Compare commits
22 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9979e40d76 | |||
| 860498b8da | |||
| 1c0e29e4b7 | |||
|
ad163ef466 |
|||
| 96fe7e7671 | |||
| 93db8c2224 | |||
| e8ad18ffe4 | |||
| fcc3a3b296 | |||
| 3499c54ea5 | |||
| 1e569811a9 | |||
| cb7d6e51f9 | |||
| a594cb1f74 | |||
| c9d8c7acc1 | |||
| 74b8e7ef2f | |||
| 528e7ae729 | |||
| 89f5caae5a | |||
| 1c35fe3fec | |||
| 15f54b48fa | |||
| b51dc35e75 | |||
| 38e8e34ba8 | |||
| 10891cf269 | |||
| df05e82e8b |
11 changed files with 2428 additions and 310 deletions
35
.eslintrc.json
Normal file
35
.eslintrc.json
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"es2021": true,
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": "eslint:recommended",
|
||||||
|
"overrides": [
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": "latest",
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"ignorePatterns": [
|
||||||
|
"src/glkOte/*.js"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"indent": [
|
||||||
|
"error",
|
||||||
|
2
|
||||||
|
],
|
||||||
|
"linebreak-style": [
|
||||||
|
"error",
|
||||||
|
"unix"
|
||||||
|
],
|
||||||
|
"quotes": [
|
||||||
|
"error",
|
||||||
|
"single"
|
||||||
|
],
|
||||||
|
"semi": [
|
||||||
|
"error",
|
||||||
|
"never"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
20
README.md
20
README.md
|
|
@ -11,23 +11,24 @@ This repository includes examples of [stdio interface implementation](https://gi
|
||||||
|
|
||||||
### Initialization
|
### Initialization
|
||||||
```js
|
```js
|
||||||
const { glkInterface, sendFn } =
|
const { Dialog, GlkOte, send } =
|
||||||
CheapGlkOte(handlers [, { loggers, size }])
|
CheapGlkOte(handlers [, { loggers, size }])
|
||||||
```
|
```
|
||||||
|
|
||||||
### Input
|
### Input
|
||||||
```js
|
```js
|
||||||
sendFn('open door', windowObject)
|
send('open door', inputType, targetWindow)
|
||||||
```
|
```
|
||||||
You can received `windowObject` in `onUpdateWindows` handler.<br>
|
You can obtain `inputType` and `id` of `targetWindow` inside the `onUpdateInputs` handler.<br>
|
||||||
You should respect input type setted by `onUpdateInputs`.
|
You can specify `targetWindow` by its `id` inside the `onUpdateWindows` handler.<br>
|
||||||
|
As I know, `inputType` can be `line` or `char`.
|
||||||
|
|
||||||
### Output and lifecycle
|
### Output and lifecycle
|
||||||
```js
|
```js
|
||||||
const handlers = {
|
const handlers = {
|
||||||
onInit: () => {
|
onInit: () => {
|
||||||
/**
|
/**
|
||||||
* It's time to prepare the user interface.
|
* It's time to initialize the user interface.
|
||||||
*/
|
*/
|
||||||
},
|
},
|
||||||
onUpdateWindows: windows => {
|
onUpdateWindows: windows => {
|
||||||
|
|
@ -35,10 +36,11 @@ const handlers = {
|
||||||
* Game wants to change the number of windows.
|
* Game wants to change the number of windows.
|
||||||
*/
|
*/
|
||||||
},
|
},
|
||||||
onUpdateInputs: type => {
|
onUpdateInputs: data => {
|
||||||
/**
|
/**
|
||||||
* Game wants to change input type.
|
* Game wants to change input type.
|
||||||
* Supported types: 'char', 'line'.
|
* 'data' is a list with info about
|
||||||
|
* the target window and the input type.
|
||||||
*/
|
*/
|
||||||
},
|
},
|
||||||
onUpdateContent: messages => {
|
onUpdateContent: messages => {
|
||||||
|
|
@ -85,7 +87,7 @@ Default loggers:
|
||||||
const defaultLoggers = {
|
const defaultLoggers = {
|
||||||
log: console.log,
|
log: console.log,
|
||||||
warning: console.warn,
|
warning: console.warn,
|
||||||
error: console.error
|
error: console.error,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -94,7 +96,7 @@ Default sizes:
|
||||||
```js
|
```js
|
||||||
const defaultSize = {
|
const defaultSize = {
|
||||||
width: 80,
|
width: 80,
|
||||||
height: 25
|
height: 25,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,12 @@
|
||||||
* @see: https://github.com/curiousdannii/emglken/blob/master/bin/emglken.js
|
* @see: https://github.com/curiousdannii/emglken/blob/master/bin/emglken.js
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const fs = require('fs')
|
import { readFileSync } from 'fs'
|
||||||
const minimist = require('minimist')
|
import minimist from 'minimist'
|
||||||
|
|
||||||
const CheapGlkOte = require('../src/')
|
import CheapGlkOte from '../src/index.js'
|
||||||
const { handlers } = require('./stdio')
|
|
||||||
|
import { handlers } from './stdio.js'
|
||||||
|
|
||||||
const formats = [
|
const formats = [
|
||||||
{
|
{
|
||||||
|
|
@ -50,13 +51,19 @@ if (!format) {
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { glkInterface, sendFn } = CheapGlkOte(handlers)
|
import(`emglken/src/${format.id}.js`)
|
||||||
handlers.setSend(sendFn)
|
.then(({default: engine}) => engine)
|
||||||
|
.then((engine) => new engine())
|
||||||
|
.then((vm) => {
|
||||||
|
const cheapGlkOte = CheapGlkOte(handlers)
|
||||||
|
|
||||||
const engine = require('emglken/src/' + format.engine)
|
handlers.setSend(cheapGlkOte.send)
|
||||||
const vm = new engine()
|
|
||||||
|
|
||||||
vm.prepare(
|
vm.init(readFileSync(storyfile), {
|
||||||
fs.readFileSync(storyfile),
|
Dialog: cheapGlkOte.Dialog,
|
||||||
glkInterface)
|
GlkOte: cheapGlkOte.GlkOte,
|
||||||
vm.start()
|
Glk: {},
|
||||||
|
wasmBinary: readFileSync(new URL(`../node_modules/emglken/build/${format.id}-core.wasm`, import.meta.url))
|
||||||
|
})
|
||||||
|
vm.start()
|
||||||
|
})
|
||||||
|
|
|
||||||
98
bin/stdio.js
98
bin/stdio.js
|
|
@ -2,20 +2,22 @@
|
||||||
* @see: https://github.com/curiousdannii/glkote-term/blob/master/src/glkote-dumb.js
|
* @see: https://github.com/curiousdannii/glkote-term/blob/master/src/glkote-dumb.js
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const readline = require('readline')
|
import { createInterface, emitKeypressEvents } from 'readline'
|
||||||
const MuteStream = require('mute-stream')
|
import MuteStream from 'mute-stream'
|
||||||
const ansiEscapes = require('ansi-escapes')
|
import ansiEsc from 'ansi-escapes'
|
||||||
|
|
||||||
const stdin = process.stdin
|
const stdin = process.stdin
|
||||||
const stdout = new MuteStream()
|
const stdout = new MuteStream()
|
||||||
stdout.pipe(process.stdout)
|
stdout.pipe(process.stdout)
|
||||||
const rl = readline.createInterface({
|
const rl = createInterface({
|
||||||
input: stdin,
|
input: stdin,
|
||||||
output: stdout,
|
output: stdout,
|
||||||
prompt: ''
|
prompt: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let currentWindowId = null
|
||||||
let currentWindow = null
|
let currentWindow = null
|
||||||
|
let currentInputType = null
|
||||||
|
|
||||||
let send = _ => _
|
let send = _ => _
|
||||||
|
|
||||||
|
|
@ -27,28 +29,38 @@ const onInit = () => {
|
||||||
if (stdin.isTTY) {
|
if (stdin.isTTY) {
|
||||||
stdin.setRawMode(true)
|
stdin.setRawMode(true)
|
||||||
}
|
}
|
||||||
readline.emitKeypressEvents(stdin)
|
emitKeypressEvents(stdin)
|
||||||
rl.resume()
|
rl.resume()
|
||||||
|
clearScreen()
|
||||||
}
|
}
|
||||||
|
|
||||||
const onUpdateWindows = windows => {
|
const onUpdateWindows = windows => {
|
||||||
currentWindow = windows
|
currentWindow = currentWindowId
|
||||||
.filter(x => x.type === 'buffer')
|
? windows
|
||||||
.slice(-1)[0]
|
.find(x => x.id === currentWindowId)
|
||||||
|
: windows
|
||||||
|
.filter(x => x.type === 'buffer')
|
||||||
|
.slice(-1)[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
const onUpdateInputs = type => {
|
const onUpdateInputs = data => {
|
||||||
type
|
if (data.length === 0) return null
|
||||||
? attach_handlers(type)
|
const { id, type } = data[0]
|
||||||
: detach_handlers()
|
|
||||||
|
currentWindowId = id
|
||||||
|
|
||||||
|
if (['char', 'line'].includes(type)) {
|
||||||
|
detach_handlers()
|
||||||
|
attach_handlers(type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onUpdateContent = allMessages => {
|
const clearScreen = () => {
|
||||||
const messages = allMessages.filter(
|
stdout.write('\u001B[2J\u001B[0;0f')
|
||||||
content => content.id === currentWindow.id
|
}
|
||||||
)[0]
|
|
||||||
|
|
||||||
return messages.text.forEach(({ append, content }) => {
|
const drawBuffer = messages => {
|
||||||
|
messages.text.forEach(({ append, content }) => {
|
||||||
if (!append) {
|
if (!append) {
|
||||||
stdout.write('\n')
|
stdout.write('\n')
|
||||||
}
|
}
|
||||||
|
|
@ -59,7 +71,7 @@ const onUpdateContent = allMessages => {
|
||||||
|
|
||||||
if (x.style === 'input') {
|
if (x.style === 'input') {
|
||||||
if (stdout.isTTY) {
|
if (stdout.isTTY) {
|
||||||
stdout.write(ansiEscapes.eraseLines(2))
|
stdout.write(ansiEsc.eraseLines(2))
|
||||||
stdout.write('> ')
|
stdout.write('> ')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -73,10 +85,32 @@ const onUpdateContent = allMessages => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const onDisable = disable =>
|
const drawGrid = messages => {
|
||||||
disable
|
clearScreen()
|
||||||
? detach_handlers()
|
messages.lines
|
||||||
: attach_handlers()
|
.map(x => x.content)
|
||||||
|
.map(([x]) => x)
|
||||||
|
.map(({text}) => text)
|
||||||
|
.map(x => x.trim())
|
||||||
|
.forEach(x => stdout.write(`${x}\n`))
|
||||||
|
}
|
||||||
|
|
||||||
|
const onUpdateContent = allMessages => {
|
||||||
|
detach_handlers()
|
||||||
|
|
||||||
|
const messages = allMessages.find(
|
||||||
|
content => content.id === currentWindow.id
|
||||||
|
)
|
||||||
|
|
||||||
|
;({
|
||||||
|
'buffer': drawBuffer,
|
||||||
|
'grid': drawGrid
|
||||||
|
})[currentWindow.type](messages)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDisable = disable => {
|
||||||
|
if (disable) detach_handlers()
|
||||||
|
}
|
||||||
|
|
||||||
const onExit = () => {
|
const onExit = () => {
|
||||||
detach_handlers()
|
detach_handlers()
|
||||||
|
|
@ -85,6 +119,8 @@ const onExit = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const onFileNameRequest = (tosave, usage, gameid, callback) => {
|
const onFileNameRequest = (tosave, usage, gameid, callback) => {
|
||||||
|
stdout.write('\n')
|
||||||
|
stdout.write(gameid, tosave)
|
||||||
stdout.write('\n')
|
stdout.write('\n')
|
||||||
rl.question(
|
rl.question(
|
||||||
'Please enter a file name: ',
|
'Please enter a file name: ',
|
||||||
|
|
@ -93,10 +129,10 @@ const onFileNameRequest = (tosave, usage, gameid, callback) => {
|
||||||
: null))
|
: null))
|
||||||
}
|
}
|
||||||
|
|
||||||
const onFileRead = (dirent, israw) =>
|
const onFileRead = (dirent) =>
|
||||||
void console.log('onFileRead:', dirent)
|
void console.log('onFileRead:', dirent)
|
||||||
|
|
||||||
const onFileWrite = (dirent, content, israw) =>
|
const onFileWrite = (dirent, content) =>
|
||||||
void console.log('onFileWrite:', dirent, content.length)
|
void console.log('onFileWrite:', dirent, content.length)
|
||||||
|
|
||||||
const handle_char_input = (str, key) => {
|
const handle_char_input = (str, key) => {
|
||||||
|
|
@ -105,8 +141,6 @@ const handle_char_input = (str, key) => {
|
||||||
'\t': 'tab',
|
'\t': 'tab',
|
||||||
}
|
}
|
||||||
|
|
||||||
detach_handlers()
|
|
||||||
|
|
||||||
// Make sure this char isn't being remembered for the next line input
|
// Make sure this char isn't being remembered for the next line input
|
||||||
rl._line_buffer = null
|
rl._line_buffer = null
|
||||||
rl.line = ''
|
rl.line = ''
|
||||||
|
|
@ -117,10 +151,12 @@ const handle_char_input = (str, key) => {
|
||||||
str ||
|
str ||
|
||||||
key.name.replace(/f(\d+)/, 'func$1')
|
key.name.replace(/f(\d+)/, 'func$1')
|
||||||
|
|
||||||
send(res, currentWindow)
|
send(res, currentInputType, currentWindow)
|
||||||
|
detach_handlers()
|
||||||
}
|
}
|
||||||
|
|
||||||
const attach_handlers = type => {
|
const attach_handlers = type => {
|
||||||
|
currentInputType = type
|
||||||
if (type === 'char') {
|
if (type === 'char') {
|
||||||
stdout.mute()
|
stdout.mute()
|
||||||
stdin.on('keypress', handle_char_input)
|
stdin.on('keypress', handle_char_input)
|
||||||
|
|
@ -134,18 +170,18 @@ const detach_handlers = () => {
|
||||||
stdin.removeListener('keypress', handle_char_input)
|
stdin.removeListener('keypress', handle_char_input)
|
||||||
rl.removeListener('line', handle_line_input)
|
rl.removeListener('line', handle_line_input)
|
||||||
stdout.unmute()
|
stdout.unmute()
|
||||||
|
currentInputType = null
|
||||||
}
|
}
|
||||||
|
|
||||||
const handle_line_input = line => {
|
const handle_line_input = line => {
|
||||||
if (stdout.isTTY) {
|
if (stdout.isTTY) {
|
||||||
stdout.write(ansiEscapes.eraseLines(1))
|
stdout.write(ansiEsc.eraseLines(1))
|
||||||
}
|
}
|
||||||
|
send(line, currentInputType, currentWindow)
|
||||||
detach_handlers()
|
detach_handlers()
|
||||||
|
|
||||||
send(line, currentWindow)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.handlers = {
|
export const handlers = {
|
||||||
onInit,
|
onInit,
|
||||||
onUpdateWindows,
|
onUpdateWindows,
|
||||||
onUpdateInputs,
|
onUpdateInputs,
|
||||||
|
|
|
||||||
1953
package-lock.json
generated
1953
package-lock.json
generated
File diff suppressed because it is too large
Load diff
12
package.json
12
package.json
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "cheap-glkote",
|
"name": "cheap-glkote",
|
||||||
"version": "0.3.0",
|
"version": "0.5.1",
|
||||||
"description": "Abstract JavaScript implementation of GlkOte",
|
"description": "Abstract JavaScript implementation of GlkOte",
|
||||||
"author": "He4eT <He4eTHb1u@gmail.com>",
|
"author": "He4eT <He4eTHb1u@gmail.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|
@ -10,20 +10,26 @@
|
||||||
"interactive fiction",
|
"interactive fiction",
|
||||||
"interactive-fiction"
|
"interactive-fiction"
|
||||||
],
|
],
|
||||||
|
"type": "module",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"files": [
|
"files": [
|
||||||
"/src"
|
"/src"
|
||||||
],
|
],
|
||||||
"repository": "https://github.com/He4eT/cheap-glkote",
|
"repository": "https://github.com/He4eT/cheap-glkote",
|
||||||
"bugs": "https://github.com/He4eT/cheap-glkote/issues",
|
"bugs": "https://github.com/He4eT/cheap-glkote/issues",
|
||||||
"dependencies": {},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"ansi-escapes": "^4.0.0",
|
"ansi-escapes": "^4.0.0",
|
||||||
"emglken": "^0.3.3",
|
"emglken": "^0.5.2",
|
||||||
|
"eslint": "^8.41.0",
|
||||||
"minimist": "^1.2.5",
|
"minimist": "^1.2.5",
|
||||||
"mute-stream": "0.0.8"
|
"mute-stream": "0.0.8"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"lint": "eslint bin/ src/",
|
||||||
|
"lint:fix": "eslint --fix bin/ src/",
|
||||||
"play": "node ./bin/player.stdio.js",
|
"play": "node ./bin/player.stdio.js",
|
||||||
"test": "./tests/runtests.sh"
|
"test": "./tests/runtests.sh"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,41 +2,34 @@
|
||||||
* @see: https://github.com/curiousdannii/glkote-term/blob/master/src/glkote-dumb.js
|
* @see: https://github.com/curiousdannii/glkote-term/blob/master/src/glkote-dumb.js
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const GlkOte = require('./glkOte/glkote-term')
|
import GlkOte from './glkOte/glkote-term.js'
|
||||||
|
|
||||||
class CheapGlkOte extends GlkOte {
|
class CheapGlkOte extends GlkOte {
|
||||||
constructor(handlers, loggers, size) {
|
constructor(handlers, loggers, size) {
|
||||||
super(size)
|
super(size)
|
||||||
|
|
||||||
this.current_input_type = null
|
|
||||||
|
|
||||||
this.handlers = handlers
|
this.handlers = handlers
|
||||||
|
this.loggers = loggers
|
||||||
}
|
}
|
||||||
|
|
||||||
sendFn(message, window) {
|
sendFn (message, type, window) {
|
||||||
this.send_response(
|
this.send_response(
|
||||||
this.current_input_type,
|
type,
|
||||||
window,
|
window,
|
||||||
message)
|
message)
|
||||||
this.current_input_type = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init(iface) {
|
init (iface) {
|
||||||
this.handlers.onInit()
|
this.handlers.onInit()
|
||||||
super.init(iface)
|
super.init(iface)
|
||||||
}
|
}
|
||||||
|
|
||||||
update_inputs(data) {
|
update_inputs (data) {
|
||||||
if (!data.length) return null
|
if (!data.length) return []
|
||||||
|
this.handlers.onUpdateInputs(data)
|
||||||
const { type } = data[0]
|
|
||||||
if (['char', 'line'].includes(type)) {
|
|
||||||
this.current_input_type = type
|
|
||||||
this.handlers.onUpdateInputs(type)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
accept_specialinput(data) {
|
accept_specialinput (data) {
|
||||||
if (data.type === 'fileref_prompt') {
|
if (data.type === 'fileref_prompt') {
|
||||||
const callback = ref =>
|
const callback = ref =>
|
||||||
this.send_response(
|
this.send_response(
|
||||||
|
|
@ -53,44 +46,38 @@ class CheapGlkOte extends GlkOte {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
update_content(messages) {
|
update_content (messages) {
|
||||||
this.handlers.onUpdateContent(messages)
|
this.handlers.onUpdateContent(messages)
|
||||||
}
|
}
|
||||||
|
|
||||||
exit() {
|
exit () {
|
||||||
this.handlers.onExit()
|
this.handlers.onExit()
|
||||||
super.exit()
|
super.exit()
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel_inputs(data) {
|
cancel_inputs (data) {
|
||||||
if (data.length === 0) {
|
this.handlers.onUpdateInputs(data)
|
||||||
this.current_input_type = null
|
|
||||||
this.handlers.onUpdateInputs(null)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
disable(disable) {
|
disable (data) {
|
||||||
this.disabled = disable
|
this.handlers.onDisable(data)
|
||||||
this.handlers.onDisable(disable)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update_windows(windows) {
|
update_windows (windows) {
|
||||||
if (windows.length) {
|
this.handlers.onUpdateWindows(windows)
|
||||||
this.handlers.onUpdateWindows(windows)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log(message) {
|
log (message) {
|
||||||
loggers.log(message)
|
this.loggers.log(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
warning(message) {
|
warning (message) {
|
||||||
loggers.warn(message)
|
this.loggers.warn(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
error(message) {
|
error (message) {
|
||||||
loggers.error(message)
|
this.loggers.error(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = CheapGlkOte
|
export default CheapGlkOte
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ class FakeDialog {
|
||||||
constructor(handlers, loggers) {
|
constructor(handlers, loggers) {
|
||||||
this.streaming = false
|
this.streaming = false
|
||||||
this.handlers = handlers
|
this.handlers = handlers
|
||||||
|
this.loggers = loggers
|
||||||
}
|
}
|
||||||
|
|
||||||
file_ref_exists({ usage }) {
|
file_ref_exists({ usage }) {
|
||||||
|
|
@ -15,11 +16,11 @@ class FakeDialog {
|
||||||
: false
|
: false
|
||||||
}
|
}
|
||||||
|
|
||||||
file_remove_ref (ref) {
|
file_remove_ref () {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
file_construct_ref(filename, usage, gameid) {
|
file_construct_ref(filename, usage) {
|
||||||
return {
|
return {
|
||||||
filename,
|
filename,
|
||||||
usage: usage || ''
|
usage: usage || ''
|
||||||
|
|
@ -40,16 +41,16 @@ class FakeDialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
log(message) {
|
log(message) {
|
||||||
loggers.log(message)
|
this.loggers.log(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
warning(message) {
|
warning(message) {
|
||||||
loggers.warn(message)
|
this.loggers.warn(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
error(message) {
|
error(message) {
|
||||||
loggers.error(message)
|
this.loggers.error(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = FakeDialog
|
export default FakeDialog
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
/* GlkAPI -- a Javascript Glk API for IF interfaces
|
/* GlkAPI -- a Javascript Glk API for IF interfaces
|
||||||
* GlkOte Library: version 2.2.3.
|
* GlkOte Library: version 2.3.2.
|
||||||
* Glk API which this implements: version 0.7.4.
|
* Glk API which this implements: version 0.7.4.
|
||||||
* Designed by Andrew Plotkin <erkyrath@eblong.com>
|
* Designed by Andrew Plotkin <erkyrath@eblong.com>
|
||||||
* <http://eblong.com/zarf/glk/glkote.html>
|
* <http://eblong.com/zarf/glk/glkote.html>
|
||||||
*
|
*
|
||||||
* This Javascript library is copyright 2010-16 by Andrew Plotkin.
|
* This Javascript library is copyright 2010-20 by Andrew Plotkin.
|
||||||
* It is distributed under the MIT license; see the "LICENSE" file.
|
* It is distributed under the MIT license; see the "LICENSE" file.
|
||||||
*
|
*
|
||||||
* This file is a Glk API compatibility layer for glkote.js. It offers a
|
* This file is a Glk API compatibility layer for glkote.js. It offers a
|
||||||
|
|
@ -38,21 +40,16 @@
|
||||||
and will also double the write-count in a stream.
|
and will also double the write-count in a stream.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Put everything inside the Glk namespace. */
|
/* All state is contained in GlkClass. */
|
||||||
|
var GlkClass = function() {
|
||||||
|
|
||||||
Glk = function() {
|
var GlkOte = null; /* imported API object */
|
||||||
|
var VM = null; /* imported API object (the VM interface) */
|
||||||
|
var GiDispa = null; /* imported API object (the dispatch layer) */
|
||||||
|
var Blorb = null; /* imported API object (the resource layer) */
|
||||||
|
|
||||||
/* The VM interface object. */
|
/* Environment capabilities. (Checked at init time.) */
|
||||||
var VM = null;
|
var has_canvas;
|
||||||
|
|
||||||
/* References to external libraries */
|
|
||||||
var Dialog;
|
|
||||||
var GiDispa;
|
|
||||||
var GiLoad;
|
|
||||||
var GlkOte;
|
|
||||||
|
|
||||||
/* Environment capabilities */
|
|
||||||
var support = {};
|
|
||||||
|
|
||||||
/* Options from the vm_options object. */
|
/* Options from the vm_options object. */
|
||||||
var option_exit_warning;
|
var option_exit_warning;
|
||||||
|
|
@ -70,60 +67,6 @@ var event_generation = 0;
|
||||||
var current_partial_inputs = null;
|
var current_partial_inputs = null;
|
||||||
var current_partial_outputs = null;
|
var current_partial_outputs = null;
|
||||||
|
|
||||||
// Set external variable references
|
|
||||||
function set_references( vm_options )
|
|
||||||
{
|
|
||||||
if ( vm_options.Dialog )
|
|
||||||
{
|
|
||||||
Dialog = vm_options.Dialog;
|
|
||||||
}
|
|
||||||
if ( !Dialog )
|
|
||||||
{
|
|
||||||
if ( typeof window !== 'undefined' && window.Dialog )
|
|
||||||
{
|
|
||||||
Dialog = window.Dialog;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new Error( 'No reference to Dialog' );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( vm_options.GiDispa )
|
|
||||||
{
|
|
||||||
GiDispa = vm_options.GiDispa;
|
|
||||||
}
|
|
||||||
else if ( !GiDispa && typeof window !== 'undefined' && window.GiDispa )
|
|
||||||
{
|
|
||||||
GiDispa = window.GiDispa;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( vm_options.GiLoad )
|
|
||||||
{
|
|
||||||
GiLoad = vm_options.GiLoad;
|
|
||||||
}
|
|
||||||
else if ( !GiLoad && typeof window !== 'undefined' && window.GiLoad )
|
|
||||||
{
|
|
||||||
GiLoad = window.GiLoad;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( vm_options.GlkOte )
|
|
||||||
{
|
|
||||||
GlkOte = vm_options.GlkOte;
|
|
||||||
}
|
|
||||||
if ( !GlkOte )
|
|
||||||
{
|
|
||||||
if ( typeof window !== 'undefined' && window.GlkOte )
|
|
||||||
{
|
|
||||||
GlkOte = window.GlkOte;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new Error('No reference to GlkOte');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize the library, initialize the VM, and set it running. (It will
|
/* Initialize the library, initialize the VM, and set it running. (It will
|
||||||
run until the first glk_select() or glk_exit() call.)
|
run until the first glk_select() or glk_exit() call.)
|
||||||
|
|
||||||
|
|
@ -139,17 +82,27 @@ function set_references( vm_options )
|
||||||
library sets that up for you.)
|
library sets that up for you.)
|
||||||
*/
|
*/
|
||||||
function init(vm_options) {
|
function init(vm_options) {
|
||||||
/* Set references to external libraries */
|
/* Either GlkOte was passed in or we must create one. */
|
||||||
set_references( vm_options );
|
if (vm_options.GlkOte) {
|
||||||
|
GlkOte = vm_options.GlkOte;
|
||||||
|
}
|
||||||
|
else if (window.GlkOteClass) {
|
||||||
|
GlkOte = new window.GlkOteClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Either Blorb was passed in or we don't have one. */
|
||||||
|
if (vm_options.Blorb) {
|
||||||
|
Blorb = vm_options.Blorb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for canvas support. We don't rely on jquery here. */
|
||||||
|
has_canvas = (document.createElement('canvas').getContext != undefined);
|
||||||
|
|
||||||
VM = vm_options.vm;
|
VM = vm_options.vm;
|
||||||
if (GiDispa)
|
GiDispa = vm_options.GiDispa; /* may be null/undefined */
|
||||||
GiDispa.set_vm(VM);
|
|
||||||
|
|
||||||
vm_options.accept = accept_ui_event;
|
vm_options.accept = accept_ui_event;
|
||||||
|
|
||||||
GlkOte.init(vm_options);
|
|
||||||
|
|
||||||
option_exit_warning = vm_options.exit_warning;
|
option_exit_warning = vm_options.exit_warning;
|
||||||
option_do_vm_autosave = vm_options.do_vm_autosave;
|
option_do_vm_autosave = vm_options.do_vm_autosave;
|
||||||
option_before_select_hook = vm_options.before_select_hook;
|
option_before_select_hook = vm_options.before_select_hook;
|
||||||
|
|
@ -159,6 +112,16 @@ function init(vm_options) {
|
||||||
if (option_before_select_hook) {
|
if (option_before_select_hook) {
|
||||||
option_before_select_hook();
|
option_before_select_hook();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Initialize the lower levels. */
|
||||||
|
|
||||||
|
if (GiDispa)
|
||||||
|
GiDispa.init({ io:vm_options.io, vm:vm_options.vm });
|
||||||
|
GlkOte.init(vm_options);
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_inited() {
|
||||||
|
return (VM != null && GlkOte != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
function accept_ui_event(obj) {
|
function accept_ui_event(obj) {
|
||||||
|
|
@ -184,12 +147,10 @@ function accept_ui_event(obj) {
|
||||||
|
|
||||||
switch (obj.type) {
|
switch (obj.type) {
|
||||||
case 'init':
|
case 'init':
|
||||||
content_metrics = obj.metrics;
|
content_metrics = complete_metrics(obj.metrics);
|
||||||
/* Process the support array */
|
/* We ignore the support array. This library is updated in sync
|
||||||
if (obj.support) {
|
with GlkOte, so we know what it supports. */
|
||||||
obj.support.forEach(function(item) {support[item] = 1;});
|
VM.start();
|
||||||
}
|
|
||||||
VM.init();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'external':
|
case 'external':
|
||||||
|
|
@ -231,7 +192,7 @@ function accept_ui_event(obj) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'arrange':
|
case 'arrange':
|
||||||
content_metrics = obj.metrics;
|
content_metrics = complete_metrics(obj.metrics);
|
||||||
box = {
|
box = {
|
||||||
left: content_metrics.outspacingx,
|
left: content_metrics.outspacingx,
|
||||||
top: content_metrics.outspacingy,
|
top: content_metrics.outspacingy,
|
||||||
|
|
@ -255,6 +216,134 @@ function accept_ui_event(obj) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Given a partial metrics object, return one with all the required
|
||||||
|
values. Missing values will default to 0 or the standard inherited
|
||||||
|
terms. (E.g., if "inspacingx" is missing it will default to
|
||||||
|
"inspacing", then "spacing", then 0. See measure_window() in
|
||||||
|
glkote.js or data_metrics_parse() in RemGlk.)
|
||||||
|
|
||||||
|
All values in the given object will be copied over; defaulting only
|
||||||
|
applies to missing values from the required set.
|
||||||
|
*/
|
||||||
|
function complete_metrics(metrics) {
|
||||||
|
|
||||||
|
// Default values if absolutely nothing is specified.
|
||||||
|
var res = {
|
||||||
|
width: 80,
|
||||||
|
height: 50,
|
||||||
|
|
||||||
|
gridcharwidth: 1,
|
||||||
|
gridcharheight: 1,
|
||||||
|
buffercharwidth: 1,
|
||||||
|
buffercharheight: 1,
|
||||||
|
|
||||||
|
gridmarginx: 0,
|
||||||
|
gridmarginy: 0,
|
||||||
|
buffermarginx: 0,
|
||||||
|
buffermarginy: 0,
|
||||||
|
graphicsmarginx: 0,
|
||||||
|
graphicsmarginy: 0,
|
||||||
|
|
||||||
|
outspacingx: 0,
|
||||||
|
outspacingy: 0,
|
||||||
|
inspacingx: 0,
|
||||||
|
inspacingy: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Various ways of specifying defaults.
|
||||||
|
var val;
|
||||||
|
|
||||||
|
val = metrics.charwidth;
|
||||||
|
if (val !== undefined) {
|
||||||
|
res.gridcharwidth = val;
|
||||||
|
res.buffercharwidth = val;
|
||||||
|
}
|
||||||
|
val = metrics.charheight;
|
||||||
|
if (val !== undefined) {
|
||||||
|
res.gridcharheight = val;
|
||||||
|
res.buffercharheight = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = metrics.margin;
|
||||||
|
if (val !== undefined) {
|
||||||
|
res.gridmarginx = val;
|
||||||
|
res.gridmarginy = val;
|
||||||
|
res.buffermarginx = val;
|
||||||
|
res.buffermarginy = val;
|
||||||
|
res.graphicsmarginx = val;
|
||||||
|
res.graphicsmarginy = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = metrics.gridmargin;
|
||||||
|
if (val !== undefined) {
|
||||||
|
res.gridmarginx = val;
|
||||||
|
res.gridmarginy = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = metrics.buffermargin;
|
||||||
|
if (val !== undefined) {
|
||||||
|
res.buffermarginx = val;
|
||||||
|
res.buffermarginy = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = metrics.graphicsmargin;
|
||||||
|
if (val !== undefined) {
|
||||||
|
res.graphicsmarginx = val;
|
||||||
|
res.graphicsmarginy = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = metrics.marginx;
|
||||||
|
if (val !== undefined) {
|
||||||
|
res.gridmarginx = val;
|
||||||
|
res.buffermarginx = val;
|
||||||
|
res.graphicsmarginx = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = metrics.marginy;
|
||||||
|
if (val !== undefined) {
|
||||||
|
res.gridmarginy = val;
|
||||||
|
res.buffermarginy = val;
|
||||||
|
res.graphicsmarginy = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = metrics.spacing;
|
||||||
|
if (val !== undefined) {
|
||||||
|
res.inspacingx = val;
|
||||||
|
res.inspacingy = val;
|
||||||
|
res.outspacingx = val;
|
||||||
|
res.outspacingy = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = metrics.inspacing;
|
||||||
|
if (val !== undefined) {
|
||||||
|
res.inspacingx = val;
|
||||||
|
res.inspacingy = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = metrics.outspacing;
|
||||||
|
if (val !== undefined) {
|
||||||
|
res.outspacingx = val;
|
||||||
|
res.outspacingy = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = metrics.spacingx;
|
||||||
|
if (val !== undefined) {
|
||||||
|
res.inspacingx = val;
|
||||||
|
res.outspacingx = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = metrics.spacingy;
|
||||||
|
if (val !== undefined) {
|
||||||
|
res.inspacingy = val;
|
||||||
|
res.outspacingy = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy over all the supplied fields. These override the defaults above.
|
||||||
|
res = Object.assign(res, metrics);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
function handle_arrange_input() {
|
function handle_arrange_input() {
|
||||||
if (!gli_selectref)
|
if (!gli_selectref)
|
||||||
return;
|
return;
|
||||||
|
|
@ -453,8 +542,8 @@ function handle_line_input(disprock, input, termkey) {
|
||||||
VM.resume();
|
VM.resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
function update(type) {
|
function update() {
|
||||||
var dataobj = { type: type || 'update', gen: event_generation };
|
var dataobj = { type: 'update', gen: event_generation };
|
||||||
var winarray = null;
|
var winarray = null;
|
||||||
var contentarray = null;
|
var contentarray = null;
|
||||||
var inputarray = null;
|
var inputarray = null;
|
||||||
|
|
@ -705,10 +794,27 @@ function update(type) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return the library interface object that we were passed or created.
|
||||||
|
Call this if you want to use, e.g., the same Dialog object that GlkOte
|
||||||
|
is using.
|
||||||
|
*/
|
||||||
|
function get_library(val) {
|
||||||
|
switch (val) {
|
||||||
|
case 'VM': return VM;
|
||||||
|
case 'GlkOte': return GlkOte;
|
||||||
|
case 'GiDispa': return GiDispa;
|
||||||
|
case 'Blorb': return Blorb;
|
||||||
|
case 'Dialog': return GlkOte.getlibrary('Dialog');
|
||||||
|
}
|
||||||
|
/* Unrecognized library name. */
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/* Wrap up the current display state as a (JSONable) object. This is
|
/* Wrap up the current display state as a (JSONable) object. This is
|
||||||
called from Quixe.vm_autosave.
|
called from Quixe.vm_autosave.
|
||||||
*/
|
*/
|
||||||
function save_allstate() {
|
function save_allstate() {
|
||||||
|
var Dialog = GlkOte.getlibrary('Dialog');
|
||||||
var res = {};
|
var res = {};
|
||||||
|
|
||||||
if (gli_rootwin)
|
if (gli_rootwin)
|
||||||
|
|
@ -886,6 +992,8 @@ function save_allstate() {
|
||||||
*/
|
*/
|
||||||
function restore_allstate(res)
|
function restore_allstate(res)
|
||||||
{
|
{
|
||||||
|
var Dialog = GlkOte.getlibrary('Dialog');
|
||||||
|
|
||||||
if (gli_windowlist || gli_streamlist || gli_filereflist)
|
if (gli_windowlist || gli_streamlist || gli_filereflist)
|
||||||
throw('restore_allstate: glkapi module has already been launched');
|
throw('restore_allstate: glkapi module has already been launched');
|
||||||
|
|
||||||
|
|
@ -1058,7 +1166,7 @@ function restore_allstate(res)
|
||||||
|
|
||||||
case strtype_Resource:
|
case strtype_Resource:
|
||||||
str.resfilenum = obj.resfilenum;
|
str.resfilenum = obj.resfilenum;
|
||||||
var el = GiLoad.find_data_chunk(str.resfilenum);
|
var el = Blorb.get_data_chunk(str.resfilenum);
|
||||||
if (el) {
|
if (el) {
|
||||||
str.buf = el.data;
|
str.buf = el.data;
|
||||||
}
|
}
|
||||||
|
|
@ -1143,6 +1251,11 @@ function restore_allstate(res)
|
||||||
function fatal_error(msg) {
|
function fatal_error(msg) {
|
||||||
has_exited = true;
|
has_exited = true;
|
||||||
ui_disabled = true;
|
ui_disabled = true;
|
||||||
|
if (!GlkOte) {
|
||||||
|
// We haven't been initialized yet, so we can only try to log the error and hope someone sees it.
|
||||||
|
console.log('Fatal error:', msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
GlkOte.error(msg);
|
GlkOte.error(msg);
|
||||||
var dataobj = { type: 'update', gen: event_generation, disable: true };
|
var dataobj = { type: 'update', gen: event_generation, disable: true };
|
||||||
dataobj.input = [];
|
dataobj.input = [];
|
||||||
|
|
@ -2693,8 +2806,10 @@ function UniArrayToBE32(arr) {
|
||||||
up in Safari, in Opera, and in Firefox if you have Firebug installed.)
|
up in Safari, in Opera, and in Firefox if you have Firebug installed.)
|
||||||
*/
|
*/
|
||||||
function qlog(msg) {
|
function qlog(msg) {
|
||||||
if (typeof console !== 'undefined' && console.log)
|
if (window.console && console.log)
|
||||||
console.log(msg);
|
console.log(msg);
|
||||||
|
else if (window.opera && opera.postError)
|
||||||
|
opera.postError(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RefBox: Simple class used for "call-by-reference" Glk arguments. The object
|
/* RefBox: Simple class used for "call-by-reference" Glk arguments. The object
|
||||||
|
|
@ -2896,13 +3011,13 @@ function gli_window_put_string(win, val) {
|
||||||
gli_window_grid_canonicalize(), but I've inlined it. */
|
gli_window_grid_canonicalize(), but I've inlined it. */
|
||||||
if (win.cursorx < 0)
|
if (win.cursorx < 0)
|
||||||
win.cursorx = 0;
|
win.cursorx = 0;
|
||||||
else if (win.cursorx >= win.gridwidth) {
|
if (win.cursorx >= win.gridwidth) {
|
||||||
win.cursorx = 0;
|
win.cursorx = 0;
|
||||||
win.cursory++;
|
win.cursory++;
|
||||||
}
|
}
|
||||||
if (win.cursory < 0)
|
if (win.cursory < 0)
|
||||||
win.cursory = 0;
|
win.cursory = 0;
|
||||||
else if (win.cursory >= win.gridheight)
|
if (win.cursory >= win.gridheight)
|
||||||
break; /* outside the window */
|
break; /* outside the window */
|
||||||
|
|
||||||
if (ch == "\n") {
|
if (ch == "\n") {
|
||||||
|
|
@ -2912,7 +3027,7 @@ function gli_window_put_string(win, val) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
lineobj = win.lines[win.cursory];
|
var lineobj = win.lines[win.cursory];
|
||||||
lineobj.dirty = true;
|
lineobj.dirty = true;
|
||||||
lineobj.chars[win.cursorx] = ch;
|
lineobj.chars[win.cursorx] = ch;
|
||||||
lineobj.styles[win.cursorx] = win.style;
|
lineobj.styles[win.cursorx] = win.style;
|
||||||
|
|
@ -2935,13 +3050,13 @@ function gli_window_put_string(win, val) {
|
||||||
function gli_window_grid_canonicalize(win) {
|
function gli_window_grid_canonicalize(win) {
|
||||||
if (win.cursorx < 0)
|
if (win.cursorx < 0)
|
||||||
win.cursorx = 0;
|
win.cursorx = 0;
|
||||||
else if (win.cursorx >= win.gridwidth) {
|
if (win.cursorx >= win.gridwidth) {
|
||||||
win.cursorx = 0;
|
win.cursorx = 0;
|
||||||
win.cursory++;
|
win.cursory++;
|
||||||
}
|
}
|
||||||
if (win.cursory < 0)
|
if (win.cursory < 0)
|
||||||
win.cursory = 0;
|
win.cursory = 0;
|
||||||
else if (win.cursory >= win.gridheight)
|
if (win.cursory >= win.gridheight)
|
||||||
return true; /* outside the window */
|
return true; /* outside the window */
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -3094,7 +3209,7 @@ function gli_window_close(win, recurse) {
|
||||||
|
|
||||||
function gli_window_rearrange(win, box) {
|
function gli_window_rearrange(win, box) {
|
||||||
var width, height, oldwidth, oldheight;
|
var width, height, oldwidth, oldheight;
|
||||||
var min, max, diff, splitwid, ix, cx, lineobj;
|
var min, max, diff, split, splitwid, ix, cx, lineobj;
|
||||||
var box1, box2, ch1, ch2;
|
var box1, box2, ch1, ch2;
|
||||||
|
|
||||||
geometry_changed = true;
|
geometry_changed = true;
|
||||||
|
|
@ -3380,6 +3495,8 @@ function gli_stream_dirty_file(str) {
|
||||||
buffer out.
|
buffer out.
|
||||||
*/
|
*/
|
||||||
function gli_stream_flush_file(str) {
|
function gli_stream_flush_file(str) {
|
||||||
|
var Dialog = GlkOte.getlibrary('Dialog');
|
||||||
|
|
||||||
if (str.streaming)
|
if (str.streaming)
|
||||||
GlkOte.log('### gli_stream_flush_file called for streaming file!');
|
GlkOte.log('### gli_stream_flush_file called for streaming file!');
|
||||||
if (!(str.timer_id === null)) {
|
if (!(str.timer_id === null)) {
|
||||||
|
|
@ -3390,6 +3507,8 @@ function gli_stream_flush_file(str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function gli_new_fileref(filename, usage, rock, ref) {
|
function gli_new_fileref(filename, usage, rock, ref) {
|
||||||
|
var Dialog = GlkOte.getlibrary('Dialog');
|
||||||
|
|
||||||
var fref = {};
|
var fref = {};
|
||||||
fref.filename = filename;
|
fref.filename = filename;
|
||||||
fref.rock = rock;
|
fref.rock = rock;
|
||||||
|
|
@ -3510,7 +3629,7 @@ function gli_put_char(str, ch) {
|
||||||
var len = arr.length;
|
var len = arr.length;
|
||||||
if (len > str.buflen-str.bufpos)
|
if (len > str.buflen-str.bufpos)
|
||||||
len = str.buflen-str.bufpos;
|
len = str.buflen-str.bufpos;
|
||||||
for (ix=0; ix<len; ix++)
|
for (var ix=0; ix<len; ix++)
|
||||||
str.buf[str.bufpos+ix] = arr[ix];
|
str.buf[str.bufpos+ix] = arr[ix];
|
||||||
str.bufpos += len;
|
str.bufpos += len;
|
||||||
if (str.bufpos > str.bufeof)
|
if (str.bufpos > str.bufeof)
|
||||||
|
|
@ -3813,6 +3932,7 @@ function gli_get_line(str, buf, want_unicode) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
var len = buf.length;
|
var len = buf.length;
|
||||||
|
var lx, ch;
|
||||||
var gotnewline;
|
var gotnewline;
|
||||||
|
|
||||||
switch (str.type) {
|
switch (str.type) {
|
||||||
|
|
@ -4076,7 +4196,6 @@ function glk_exit() {
|
||||||
gli_selectref = null;
|
gli_selectref = null;
|
||||||
if (option_exit_warning)
|
if (option_exit_warning)
|
||||||
GlkOte.warning(option_exit_warning);
|
GlkOte.warning(option_exit_warning);
|
||||||
update('exit');
|
|
||||||
return DidNotReturn;
|
return DidNotReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4133,20 +4252,22 @@ function glk_gestalt_ext(sel, val, arr) {
|
||||||
return 2; // gestalt_CharOutput_ExactPrint
|
return 2; // gestalt_CharOutput_ExactPrint
|
||||||
|
|
||||||
case 4: // gestalt_MouseInput
|
case 4: // gestalt_MouseInput
|
||||||
if (val == Const.wintype_TextGrid)
|
if (val == Const.wintype_TextBuffer)
|
||||||
return 1;
|
return 1;
|
||||||
if (support.graphics && val == Const.wintype_Graphics)
|
if (val == Const.wintype_Graphics && has_canvas)
|
||||||
return 1;
|
return 1;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case 5: // gestalt_Timer
|
case 5: // gestalt_Timer
|
||||||
return support.timer || 0;
|
return 1;
|
||||||
|
|
||||||
case 6: // gestalt_Graphics
|
case 6: // gestalt_Graphics
|
||||||
return support.graphics || 0;
|
return 1;
|
||||||
|
|
||||||
case 7: // gestalt_DrawImage
|
case 7: // gestalt_DrawImage
|
||||||
if (support.graphics && (val == Const.wintype_TextBuffer || val == Const.wintype_Graphics))
|
if (val == Const.wintype_TextBuffer)
|
||||||
|
return 1;
|
||||||
|
if (val == Const.wintype_Graphics && has_canvas)
|
||||||
return 1;
|
return 1;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
@ -4160,10 +4281,10 @@ function glk_gestalt_ext(sel, val, arr) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case 11: // gestalt_Hyperlinks
|
case 11: // gestalt_Hyperlinks
|
||||||
return support.hyperlinks || 0;
|
return 1;
|
||||||
|
|
||||||
case 12: // gestalt_HyperlinkInput
|
case 12: // gestalt_HyperlinkInput
|
||||||
if (support.hyperlinks && (val == Const.wintype_TextBuffer || val == Const.wintype_TextGrid))
|
if (val == 3 || val == 4) // TextBuffer or TextGrid
|
||||||
return 1;
|
return 1;
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -4172,7 +4293,7 @@ function glk_gestalt_ext(sel, val, arr) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case 14: // gestalt_GraphicsTransparency
|
case 14: // gestalt_GraphicsTransparency
|
||||||
return support.graphics || 0;
|
return 1;
|
||||||
|
|
||||||
case 15: // gestalt_Unicode
|
case 15: // gestalt_Unicode
|
||||||
return 1;
|
return 1;
|
||||||
|
|
@ -4311,7 +4432,7 @@ function glk_window_open(splitwin, method, size, wintype, rock) {
|
||||||
newwin.cursory = 0;
|
newwin.cursory = 0;
|
||||||
break;
|
break;
|
||||||
case Const.wintype_Graphics:
|
case Const.wintype_Graphics:
|
||||||
if (!support.graphics) {
|
if (!has_canvas) {
|
||||||
/* Graphics windows not supported; silently return null */
|
/* Graphics windows not supported; silently return null */
|
||||||
gli_delete_window(newwin);
|
gli_delete_window(newwin);
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -4691,6 +4812,8 @@ function glk_stream_get_rock(str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function glk_stream_open_file(fref, fmode, rock) {
|
function glk_stream_open_file(fref, fmode, rock) {
|
||||||
|
var Dialog = GlkOte.getlibrary('Dialog');
|
||||||
|
|
||||||
if (!fref)
|
if (!fref)
|
||||||
throw('glk_stream_open_file: invalid fileref');
|
throw('glk_stream_open_file: invalid fileref');
|
||||||
|
|
||||||
|
|
@ -4793,21 +4916,20 @@ function glk_stream_open_memory(buf, fmode, rock) {
|
||||||
function glk_stream_open_resource(filenum, rock) {
|
function glk_stream_open_resource(filenum, rock) {
|
||||||
var str;
|
var str;
|
||||||
|
|
||||||
if (!GiLoad || !GiLoad.find_data_chunk)
|
if (!Blorb)
|
||||||
return null;
|
return null;
|
||||||
var el = GiLoad.find_data_chunk(filenum);
|
var el = Blorb.get_data_chunk(filenum);
|
||||||
if (!el)
|
if (!el)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var buf = el.data;
|
var buf = el.data;
|
||||||
var isbinary = (el.type == 'BINA');
|
|
||||||
|
|
||||||
str = gli_new_stream(strtype_Resource,
|
str = gli_new_stream(strtype_Resource,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
rock);
|
rock);
|
||||||
str.unicode = false;
|
str.unicode = false;
|
||||||
str.isbinary = isbinary;
|
str.isbinary = el.binary;
|
||||||
|
|
||||||
str.resfilenum = filenum;
|
str.resfilenum = filenum;
|
||||||
|
|
||||||
|
|
@ -4832,21 +4954,20 @@ function glk_stream_open_resource(filenum, rock) {
|
||||||
function glk_stream_open_resource_uni(filenum, rock) {
|
function glk_stream_open_resource_uni(filenum, rock) {
|
||||||
var str;
|
var str;
|
||||||
|
|
||||||
if (!GiLoad || !GiLoad.find_data_chunk)
|
if (!Blorb)
|
||||||
return null;
|
return null;
|
||||||
var el = GiLoad.find_data_chunk(filenum);
|
var el = Blorb.get_data_chunk(filenum);
|
||||||
if (!el)
|
if (!el)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var buf = el.data;
|
var buf = el.data;
|
||||||
var isbinary = (el.type == 'BINA');
|
|
||||||
|
|
||||||
str = gli_new_stream(strtype_Resource,
|
str = gli_new_stream(strtype_Resource,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
rock);
|
rock);
|
||||||
str.unicode = true;
|
str.unicode = true;
|
||||||
str.isbinary = isbinary;
|
str.isbinary = el.binary;
|
||||||
|
|
||||||
str.resfilenum = filenum;
|
str.resfilenum = filenum;
|
||||||
|
|
||||||
|
|
@ -4869,6 +4990,8 @@ function glk_stream_open_resource_uni(filenum, rock) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function glk_stream_close(str, result) {
|
function glk_stream_close(str, result) {
|
||||||
|
var Dialog = GlkOte.getlibrary('Dialog');
|
||||||
|
|
||||||
if (!str)
|
if (!str)
|
||||||
throw('glk_stream_close: invalid stream');
|
throw('glk_stream_close: invalid stream');
|
||||||
|
|
||||||
|
|
@ -4949,18 +5072,21 @@ function glk_stream_get_current() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function glk_fileref_create_temp(usage, rock) {
|
function glk_fileref_create_temp(usage, rock) {
|
||||||
|
var Dialog = GlkOte.getlibrary('Dialog');
|
||||||
var filetype = (usage & Const.fileusage_TypeMask);
|
var filetype = (usage & Const.fileusage_TypeMask);
|
||||||
var filetypename = FileTypeMap[filetype];
|
var filetypename = FileTypeMap[filetype];
|
||||||
var ref = Dialog.file_construct_temp_ref(filetypename);
|
var ref = Dialog.file_construct_temp_ref(filetypename);
|
||||||
fref = gli_new_fileref(ref.filename, usage, rock, ref);
|
var fref = gli_new_fileref(ref.filename, usage, rock, ref);
|
||||||
return fref;
|
return fref;
|
||||||
}
|
}
|
||||||
|
|
||||||
function glk_fileref_create_by_name(usage, filename, rock) {
|
function glk_fileref_create_by_name(usage, filename, rock) {
|
||||||
|
var Dialog = GlkOte.getlibrary('Dialog');
|
||||||
|
|
||||||
/* Filenames that do not come from the user must be cleaned up. */
|
/* Filenames that do not come from the user must be cleaned up. */
|
||||||
filename = Dialog.file_clean_fixed_name(filename, (usage & Const.fileusage_TypeMask));
|
filename = Dialog.file_clean_fixed_name(filename, (usage & Const.fileusage_TypeMask));
|
||||||
|
|
||||||
fref = gli_new_fileref(filename, usage, rock, null);
|
var fref = gli_new_fileref(filename, usage, rock, null);
|
||||||
return fref;
|
return fref;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5010,19 +5136,19 @@ function glk_fileref_create_by_prompt(usage, fmode, rock) {
|
||||||
|
|
||||||
function gli_fileref_create_by_prompt_callback(obj) {
|
function gli_fileref_create_by_prompt_callback(obj) {
|
||||||
var ref = obj.value;
|
var ref = obj.value;
|
||||||
|
/* This "value" field will be a dialog.js fileref object if we are
|
||||||
|
connected to GlkOte. However, if we are connected to RegTest,
|
||||||
|
it will be a plain string. We attempt to handle both cases. */
|
||||||
|
|
||||||
var usage = ui_specialcallback.usage;
|
var usage = ui_specialcallback.usage;
|
||||||
var rock = ui_specialcallback.rock;
|
var rock = ui_specialcallback.rock;
|
||||||
|
|
||||||
var fref = null;
|
var fref = null;
|
||||||
if (ref) {
|
if (ref && typeof(ref) == 'object') {
|
||||||
fref = gli_new_fileref(ref.filename, usage, rock, ref);
|
fref = gli_new_fileref(ref.filename, usage, rock, ref);
|
||||||
}
|
}
|
||||||
|
else if (ref && typeof(ref) == 'string') {
|
||||||
// If reading a file which doesn't exist, return null
|
fref = gli_new_fileref(ref, usage, rock, null);
|
||||||
if ( ui_specialinput.filemode === 'read' && !Dialog.file_ref_exists( fref.ref ) )
|
|
||||||
{
|
|
||||||
glk_fileref_destroy( fref );
|
|
||||||
fref = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ui_specialinput = null;
|
ui_specialinput = null;
|
||||||
|
|
@ -5063,12 +5189,14 @@ function glk_fileref_get_rock(fref) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function glk_fileref_delete_file(fref) {
|
function glk_fileref_delete_file(fref) {
|
||||||
|
var Dialog = GlkOte.getlibrary('Dialog');
|
||||||
if (!fref)
|
if (!fref)
|
||||||
throw('glk_fileref_delete_file: invalid fileref');
|
throw('glk_fileref_delete_file: invalid fileref');
|
||||||
Dialog.file_remove_ref(fref.ref);
|
Dialog.file_remove_ref(fref.ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
function glk_fileref_does_file_exist(fref) {
|
function glk_fileref_does_file_exist(fref) {
|
||||||
|
var Dialog = GlkOte.getlibrary('Dialog');
|
||||||
if (!fref)
|
if (!fref)
|
||||||
throw('glk_fileref_does_file_exist: invalid fileref');
|
throw('glk_fileref_does_file_exist: invalid fileref');
|
||||||
if (Dialog.file_ref_exists(fref.ref))
|
if (Dialog.file_ref_exists(fref.ref))
|
||||||
|
|
@ -5369,10 +5497,10 @@ function glk_request_timer_events(msec) {
|
||||||
/* Graphics functions. */
|
/* Graphics functions. */
|
||||||
|
|
||||||
function glk_image_get_info(imgid, widthref, heightref) {
|
function glk_image_get_info(imgid, widthref, heightref) {
|
||||||
if (!GiLoad || !GiLoad.get_image_info)
|
if (!Blorb || !Blorb.get_image_info)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var info = GiLoad.get_image_info(imgid);
|
var info = Blorb.get_image_info(imgid);
|
||||||
if (info) {
|
if (info) {
|
||||||
if (widthref)
|
if (widthref)
|
||||||
widthref.set_value(info.width);
|
widthref.set_value(info.width);
|
||||||
|
|
@ -5391,9 +5519,9 @@ function glk_image_draw(win, imgid, val1, val2) {
|
||||||
if (!win)
|
if (!win)
|
||||||
throw('glk_image_draw: invalid window');
|
throw('glk_image_draw: invalid window');
|
||||||
|
|
||||||
if (!GiLoad || !GiLoad.get_image_info)
|
if (!Blorb || !Blorb.get_image_info)
|
||||||
return 0;
|
return 0;
|
||||||
var info = GiLoad.get_image_info(imgid);
|
var info = Blorb.get_image_info(imgid);
|
||||||
if (!info)
|
if (!info)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
@ -5441,9 +5569,9 @@ function glk_image_draw_scaled(win, imgid, val1, val2, width, height) {
|
||||||
if (!win)
|
if (!win)
|
||||||
throw('glk_image_draw_scaled: invalid window');
|
throw('glk_image_draw_scaled: invalid window');
|
||||||
|
|
||||||
if (!GiLoad || !GiLoad.get_image_info)
|
if (!Blorb || !Blorb.get_image_info)
|
||||||
return 0;
|
return 0;
|
||||||
var info = GiLoad.get_image_info(imgid);
|
var info = Blorb.get_image_info(imgid);
|
||||||
if (!info)
|
if (!info)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
@ -5954,6 +6082,8 @@ function glk_get_line_stream_uni(str, buf) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function glk_stream_open_file_uni(fref, fmode, rock) {
|
function glk_stream_open_file_uni(fref, fmode, rock) {
|
||||||
|
var Dialog = GlkOte.getlibrary('Dialog');
|
||||||
|
|
||||||
if (!fref)
|
if (!fref)
|
||||||
throw('glk_stream_open_file_uni: invalid fileref');
|
throw('glk_stream_open_file_uni: invalid fileref');
|
||||||
|
|
||||||
|
|
@ -6241,11 +6371,13 @@ function glk_date_to_simple_time_local(dateref, factor) {
|
||||||
|
|
||||||
/* End of Glk namespace function. Return the object which will
|
/* End of Glk namespace function. Return the object which will
|
||||||
become the Glk global. */
|
become the Glk global. */
|
||||||
var api = {
|
return {
|
||||||
version: '2.2.3', /* GlkOte/GlkApi version */
|
classname: 'Glk',
|
||||||
set_references: set_references,
|
version: '2.3.2', /* GlkOte/GlkApi version */
|
||||||
init : init,
|
init : init,
|
||||||
|
inited : is_inited,
|
||||||
update : update,
|
update : update,
|
||||||
|
getlibrary : get_library,
|
||||||
save_allstate : save_allstate,
|
save_allstate : save_allstate,
|
||||||
restore_allstate : restore_allstate,
|
restore_allstate : restore_allstate,
|
||||||
fatal_error : fatal_error,
|
fatal_error : fatal_error,
|
||||||
|
|
@ -6385,12 +6517,12 @@ var api = {
|
||||||
glk_stream_open_resource_uni : glk_stream_open_resource_uni
|
glk_stream_open_resource_uni : glk_stream_open_resource_uni
|
||||||
};
|
};
|
||||||
|
|
||||||
if (typeof module !== 'undefined' && module.exports) {
|
};
|
||||||
module.exports = api;
|
|
||||||
}
|
|
||||||
|
|
||||||
return api;
|
/* Glk is an instance of GlkClass, ready to init. */
|
||||||
|
var Glk = new GlkClass();
|
||||||
|
|
||||||
}();
|
// Node-compatible behavior
|
||||||
|
try { exports.Glk = Glk; exports.GlkClass = GlkClass; } catch (ex) {};
|
||||||
|
|
||||||
/* End of Glk library. */
|
/* End of Glk library. */
|
||||||
|
|
@ -11,7 +11,7 @@ class GlkOte {
|
||||||
this.disabled = false
|
this.disabled = false
|
||||||
this.generation = 0
|
this.generation = 0
|
||||||
this.interface = null
|
this.interface = null
|
||||||
this.version = require('../../package.json').version
|
this.version = '0.5.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
measure_window() {
|
measure_window() {
|
||||||
|
|
@ -149,4 +149,4 @@ class GlkOte {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = GlkOte
|
export default GlkOte
|
||||||
|
|
|
||||||
25
src/index.js
25
src/index.js
|
|
@ -1,5 +1,5 @@
|
||||||
const FakeDialog = require('./fakeDialog')
|
import FakeDialog from './fakeDialog.js'
|
||||||
const CheapGlkOte = require('./cheapGlkOte')
|
import CheapGlkOte from './cheapGlkOte.js'
|
||||||
|
|
||||||
const noop = () => void null
|
const noop = () => void null
|
||||||
|
|
||||||
|
|
@ -12,39 +12,36 @@ const defaultHandlers = [
|
||||||
'onFileNameRequest',
|
'onFileNameRequest',
|
||||||
'onFileRead',
|
'onFileRead',
|
||||||
'onFileWrite',
|
'onFileWrite',
|
||||||
'onExit'
|
'onExit',
|
||||||
].reduce((acc, x) => ((acc[x] = noop), acc), {})
|
].reduce((acc, x) => ((acc[x] = noop), acc), {})
|
||||||
|
|
||||||
const defaultLoggers = {
|
const defaultLoggers = {
|
||||||
log: console.log,
|
log: console.log,
|
||||||
warning: console.warn,
|
warning: console.warn,
|
||||||
error: console.error
|
error: console.error,
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultSize = {
|
const defaultSize = {
|
||||||
width: 80,
|
width: 80,
|
||||||
height: 25
|
height: 25,
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = (handlers_, {loggers: loggers_, size: size_ } = {}) => {
|
export default (handlers_, {loggers: loggers_, size: size_ } = {}) => {
|
||||||
const handlers =
|
const handlers =
|
||||||
Object.assign({}, defaultHandlers, handlers_)
|
Object.assign({}, defaultHandlers, handlers_)
|
||||||
const loggers =
|
const loggers =
|
||||||
Object.assign({}, defaultLoggers, size_)
|
Object.assign({}, defaultLoggers, loggers_)
|
||||||
const size =
|
const size =
|
||||||
Object.assign({}, defaultSize, size_)
|
Object.assign({}, defaultSize, size_)
|
||||||
|
|
||||||
const Dialog = new FakeDialog(handlers, loggers)
|
const Dialog = new FakeDialog(handlers, loggers)
|
||||||
const GlkOte = new CheapGlkOte(handlers, loggers, size)
|
const GlkOte = new CheapGlkOte(handlers, loggers, size)
|
||||||
|
|
||||||
const sendFn = GlkOte.sendFn.bind(GlkOte)
|
const send = GlkOte.sendFn.bind(GlkOte)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sendFn,
|
Dialog,
|
||||||
glkInterface: {
|
GlkOte,
|
||||||
Dialog,
|
send,
|
||||||
GlkOte,
|
|
||||||
Glk: {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue