Skip to content

Commit

Permalink
Safari support
Browse files Browse the repository at this point in the history
  • Loading branch information
tillvit committed Sep 23, 2023
1 parent e2a02f0 commit cc5b11d
Show file tree
Hide file tree
Showing 9 changed files with 163 additions and 147 deletions.
1 change: 1 addition & 0 deletions index.css
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,7 @@ input[type="number"] {
height: 100%;
user-select: text;
caret-color: auto;
font-size: 13.33px;
}

input:disabled {
Expand Down
31 changes: 0 additions & 31 deletions src/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import { Keybinds } from "./util/Keybinds"
import { Options } from "./util/Options"
import { extname } from "./util/Path"
import { fpsUpdate } from "./util/Performance"
import { getBrowser } from "./util/Util"
import { FileHandler } from "./util/file-handler/FileHandler"
declare global {
interface Window {
Expand Down Expand Up @@ -317,36 +316,6 @@ function init() {
<div>Please visit your browser settings and enable WebGL.</div>
</div>
</div>`
} else if (getBrowser().includes("Safari")) {
document.querySelector(
"body"
)!.innerHTML = `<div class='browser-unsupported'>
<div class='browser-unsupported-item'>
<h1>Safari is currently not supported</h1>
<div>Please use Chrome/Firefox instead.</div>
<div class='browser-unsupported-detail'>Check the console for more info.</div>
</div>
</div>`
console.log(
`SMEditor is not supported for Safari due to various issues involving rendering and sound.
PIXI.js, the library used in SMEditor, takes an extremely long time to load and does not perform well on Safari.
Additionally, many audio files cannot be played in Safari.
If you still want to try loading SMEditor, run the command runSafari()`
)
window.runSafari = () => {
document.querySelector("body")!.innerHTML = `<div id="view-wrapper">
<div id="menubar"></div>
<div id="waterfall"></div>
<canvas id="pixi"></canvas>
</div>
<div id="popups"></div>
<div id="context-menu"></div>
<div id="blocker" style="display: none"></div>
<div id="windows"></div>
`
window.app = new App()
window.runSafari = undefined
}
} else {
window.app = new App()
}
Expand Down
2 changes: 1 addition & 1 deletion src/chart/audio/ChartAudio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ export class ChartAudio {
} catch (e) {
if (this.type == ".ogg") {
// attempt to decode with ogg
const oggdec = await (await import("../../util/OggDec")).default
const oggdec = (await import("../../util/OggDec")).default
try {
resolve(await oggdec.decodeOggData(data))
} catch (err) {
Expand Down
8 changes: 4 additions & 4 deletions src/gui/widget/DebugWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import {
Texture,
} from "pixi.js"
import { BetterRoundedRect } from "../../util/BetterRoundedRect"
import { clamp, roundDigit } from "../../util/Math"
import { Options } from "../../util/Options"
import { getFPS, getTPS } from "../../util/Performance"
import { Widget } from "./Widget"
import { WidgetManager } from "./WidgetManager"
import { clamp, roundDigit } from "../../util/Math"
import { getFPS, getTPS } from "../../util/Performance"

const GRAPH_HEIGHT = 50

Expand Down Expand Up @@ -115,8 +115,8 @@ export class DebugWidget extends Widget {
this.fpsBg.width = this.fpsText.width + 10
this.fpsBg.height = this.fpsText.height + 10
if (Options.debug.showTimers) {
this.fpsBg.y = (GRAPH_HEIGHT + 5) * (this.children.length + 2) - 5
this.fpsText.y = (GRAPH_HEIGHT + 5) * (this.children.length + 2)
this.fpsBg.y = (GRAPH_HEIGHT + 5) * this.graphs.children.length - 5
this.fpsText.y = (GRAPH_HEIGHT + 5) * this.graphs.children.length
} else {
this.fpsBg.y = -5
this.fpsText.y = 0
Expand Down
7 changes: 4 additions & 3 deletions src/util/OggDec.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const ogg: OggDec
export default ogg

interface OggDec {
decodeOggData(data: ArrayBuffer): ArrayBuffer
decodeOggData(data: ArrayBuffer): Promise<AudioBuffer>
}

export default oggdec = OggDec
102 changes: 0 additions & 102 deletions src/util/SafariFileWorker.ts

This file was deleted.

110 changes: 110 additions & 0 deletions src/util/file-handler/SafariFileWorker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// hacky type stuff
interface SecureFileSystemFileHandle extends FileSystemHandle {
readonly kind: "file"
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/FileSystemFileHandle/createSyncAccessHandle) */
createSyncAccessHandle(): Promise<FileSystemSyncAccessHandle>
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/FileSystemFileHandle/createWritable) */
createWritable(
options?: FileSystemCreateWritableOptions
): Promise<FileSystemWritableFileStream>
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/FileSystemFileHandle/getFile) */
getFile(): Promise<File>
}

interface FileSystemSyncAccessHandle {
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/FileSystemSyncAccessHandle/close) */
close(): void
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/FileSystemSyncAccessHandle/flush) */
flush(): void
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/FileSystemSyncAccessHandle/getSize) */
getSize(): number
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/FileSystemSyncAccessHandle/read) */
read(
buffer: AllowSharedBufferSource,
options?: FileSystemReadWriteOptions
): number
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/FileSystemSyncAccessHandle/truncate) */
truncate(newSize: number): void
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/FileSystemSyncAccessHandle/write) */
write(
buffer: AllowSharedBufferSource,
options?: FileSystemReadWriteOptions
): number
}

interface FileSystemReadWriteOptions {
at?: number
}

onmessage = async event => {
const [id, path, buffer] = event.data
const fileHandle = await getFileHandle(path)
if (!fileHandle) {
postMessage({
id,
success: false,
error: "Couldn't locate file",
})
return
}
const accessHandle = await fileHandle.createSyncAccessHandle()
accessHandle.write(buffer)
accessHandle.flush()
accessHandle.close()
postMessage({
id,
success: true,
})
}

async function getDirectoryHandle(
path: string,
options?: FileSystemGetFileOptions,
dir?: FileSystemDirectoryHandle
): Promise<FileSystemDirectoryHandle | undefined> {
dir ||= await navigator.storage.getDirectory()
if (path == "" || path == ".") return dir
const pathParts = resolvePath(path).split("/")
const dirname = pathParts.shift()!
try {
const dirHandle = await dir.getDirectoryHandle(dirname, options)
if (!dirHandle) return undefined
if (pathParts.length == 0) return dirHandle
return getDirectoryHandle(pathParts.join("/"), options, dirHandle)
} catch (err) {
console.error(`Failed to get directory ${path} (${dirname}): ` + err)
return undefined
}
}

async function getFileHandle(
path: string,
options?: FileSystemGetFileOptions
): Promise<SecureFileSystemFileHandle | undefined> {
try {
const pathParts = resolvePath(path).split("/")
const filename = pathParts.pop()!
const dirHandle = await getDirectoryHandle(pathParts.join("/"), options)
if (!dirHandle) return undefined
return (await dirHandle.getFileHandle(
filename,
options
)) as SecureFileSystemFileHandle
} catch (err) {
console.error("Failed to get file " + path + ": " + err)
return undefined
}
}

function resolvePath(path: string): string {
let pathParts = path.split("/")
pathParts = pathParts.filter(item => item != "." && item != "")
while (pathParts.indexOf("..") > -1) {
const ind = pathParts.indexOf("..")
if (ind == 0) {
throw Error("Path" + pathParts.join("/") + "is invalid!")
}
pathParts.splice(ind - 1, 2)
}
return pathParts.join("/")
}
39 changes: 39 additions & 0 deletions src/util/file-handler/SafariFileWriter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export class SafariFileWriter {
private static worker = new Worker(
new URL("./SafariFileWorker.ts", import.meta.url),
{
type: "module",
}
)
private static workID = 0
private static map: Map<
number,
[(value: void | PromiseLike<void>) => void, (reason?: any) => void]
> = new Map()
static {
this.worker.onmessage = event => {
const data = event.data
console.log("finished job " + data.id)
console.log(data)
if (data.success) {
this.map.get(data.id)![0]()
} else {
this.map.get(data.id)![1](data.reason)
}
this.map.delete(data.id)
}
}

static async writeHandle(path: string, data: Blob | string) {
const id = this.workID++
console.log("Starting work write " + path + ", id: " + id)
const promise = new Promise<void>((resolve, reject) =>
this.map.set(id, [resolve, reject])
)
const encode = new TextEncoder()
const buffer =
typeof data == "string" ? encode.encode(data) : await data.arrayBuffer()
this.worker.postMessage([id, path, buffer], [buffer])
return promise
}
}
10 changes: 4 additions & 6 deletions src/util/file-handler/WebFileHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ import {
import JSZip from "jszip"
import { WaterfallManager } from "../../gui/element/WaterfallManager"
import { basename, dirname, extname } from "../Path"
import { SafariFileWorker } from "../SafariFileWorker"
import { getBrowser } from "../Util"
import { BaseFileHandler } from "./FileHandler"
import { SafariFileWriter } from "./SafariFileWriter"

export class WebFileHandler implements BaseFileHandler {
private root!: FileSystemDirectoryHandle

constructor() {
if (support.adapter.native && !getBrowser().includes("Safari")) {
if (support.adapter.native) {
getOriginPrivateDirectory().then(root => (this.root = root))
} else {
getOriginPrivateDirectory(
Expand Down Expand Up @@ -173,8 +172,7 @@ export class WebFileHandler implements BaseFileHandler {

async hasFile(path: string): Promise<boolean> {
try {
await this.getFileHandle(path)
return true
return (await this.getFileHandle(path)) !== undefined
} catch (err) {
return false
}
Expand Down Expand Up @@ -440,7 +438,7 @@ export class WebFileHandler implements BaseFileHandler {
} else {
const path = await this.root.resolve(handle)
if (!path) return
await SafariFileWorker.writeHandle(path.join("/"), data)
await SafariFileWriter.writeHandle(path.join("/"), data)
}
}
}

0 comments on commit cc5b11d

Please sign in to comment.