diff --git a/src/firenvim b/src/firenvim new file mode 100755 index 00000000..7fff567e --- /dev/null +++ b/src/firenvim @@ -0,0 +1,45 @@ +#!/bin/python3 + +import json +import os +import random +import re +import subprocess + +xdg_data_home = os.environ.get("XDG_DATA_HOME", os.path.join(os.environ["HOME"], ".local", "share")) +userscripts_dir = os.path.join(xdg_data_home, "qutebrowser", "userscripts") +def dir(name): + return os.path.join(userscripts_dir, name) + +password = random.getrandbits(64) + +# Start firenvim script, send password, get port +firenvim = subprocess.Popen(["/home/me/.local/share/firenvim/firenvim"], stdout = subprocess.PIPE, stdin = subprocess.PIPE, stderr = subprocess.PIPE) +request = 'abcde{"newInstance":true,"password":"' + str(password) + '"}' +firenvim.stdin.write(bytes(request, 'utf-8')) +outs, errs = firenvim.communicate() +result = json.loads(outs[4:]) + +with open(dir("firenvim.js"), "rb") as f: + js = re.sub(rb'Promise.resolve({ port: .*, password: ".*" });', + bytes('Promise.resolve({ port: %s, password: "%s" });' % (result["port"], password), 'utf-8'), + f.read()) + +with open(dir("index.html"), "rb") as f: + html = f.read() + script_tag = html.find(b'') + html = html[0:script_tag] + b'' + html[script_tag_end:] + with open("/tmp/blah", "wb") as f2: + f2.write(html) + +with open(dir("index.js"), "wb") as f: + f.write(b'const iframe = document.createElement("iframe");\n') + f.write(b'const blob = new Blob([new Uint8Array([') + f.write(bytes(''.join([str(c) + "," for c in html]), 'utf-8')) + f.write(b']).buffer], {type: "text/html; charset=utf-8"});\n') + f.write(b'iframe.src = URL.createObjectURL(blob);\n') + f.write(b'document.documentElement.appendChild(iframe);\n') + +with open(os.environ["QUTE_FIFO"], "w") as f: + f.write("jseval --file %s" % dir("index.js")) diff --git a/src/qutebrowser.ts b/src/qutebrowser.ts new file mode 100644 index 00000000..7ef7c87f --- /dev/null +++ b/src/qutebrowser.ts @@ -0,0 +1,106 @@ +import { PageEventEmitter } from "./page"; +import { KeydownHandler } from "./KeyHandler"; +import { setupInput } from "./input"; + +const connectionPromise = Promise.resolve({ port: 12345, password: "password" }); +const pageLoaded = new Promise((resolve, reject) => { + window.addEventListener("load", resolve); + setTimeout(reject, 10000) +}); + +class QutePageEventEmitter extends PageEventEmitter { + private resizeCount = 0; + constructor() { + super(); + const onResize = (() => { + this.resizeCount += 1; + this.emit("resize", [this.resizeCount, window.innerWidth, window.innerHeight]); + }).bind(this) + window.addEventListener("resize", onResize); + // We need to trigger a resize on startup because for some reason the + // window might be 1px wide when the compose script is created. + setTimeout(onResize, 100); + } + async evalInPage(js: string) { console.error(`eval(${js})`); } + async focusInput() { return Promise.resolve(); } + async focusPage() { return Promise.resolve(); } + async getEditorInfo() { return [document.location.href, "", [1, 1], undefined] as [string, string, [number, number], string] } + async getElementContent() { + return "Hello world"; + } + async hideEditor() { return Promise.resolve(); } + async killEditor() { console.error("killEditor"); } + async pressKeys(_: any[]) { return Promise.resolve(); } + async resizeEditor(_: number, __: number) { return Promise.resolve(); } + async setElementContent(_: string) { return; } + async setElementCursor(_: number, __: number) { return Promise.resolve(); } +} + + +class QuteKeyHandler extends KeydownHandler { + + constructor(private keyHandler: HTMLElement) { + super(keyHandler, { + alt: "all", + "": "noop", + "": "noop", + "": "noop", + "": "noop", + "": "noop", + "": "noop", + ignoreKeys: { + "all": [], + "normal": [], + "visual": [], + "insert": [], + "replace": [], + "cmdline_normal": [], + "cmdline_insert": [], + "cmdline_replace": [], + "operator": [], + "visual_select": [], + "cmdline_hover": [], + "statusline_hover": [], + "statusline_drag": [], + "vsep_hover": [], + "vsep_drag": [], + "more": [], + "more_lastline": [], + "showmatch": [], + }, + cmdlineTimeout: 3000, + }); + + const acceptInput = ((evt: any) => { + this.emit("input", evt.target.value); + evt.preventDefault(); + evt.stopImmediatePropagation(); + }).bind(this); + + this.keyHandler.addEventListener("input", (evt: any) => { + if (evt.isTrusted && !evt.isComposing) { + acceptInput(evt); + evt.target.innerText = ""; + evt.target.value = ""; + } + }); + + this.keyHandler.addEventListener("compositionend", (e: CompositionEvent) => { + acceptInput(e); + }); + } + + moveTo(x: number, y: number) { + this.keyHandler.style.left = `${x}px`; + this.keyHandler.style.top = `${y}px`; + } +} + +export const isReady = (async () => { + await pageLoaded; + return setupInput( + new QutePageEventEmitter(), + document.getElementById("canvas") as HTMLCanvasElement, + new QuteKeyHandler(document.getElementById("keyhandler")), + connectionPromise); +})(); diff --git a/webpack.config.js b/webpack.config.js index ceceda5b..bba6b5d2 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -29,6 +29,11 @@ const thunderbirdFiles = [ "static/firenvim.svg", ]; +const qutebrowserFiles = [ + "src/index.html", + "src/firenvim", +]; + const config = { mode: "development", @@ -85,6 +90,7 @@ const package_json = JSON.parse(require("fs").readFileSync(path.join(__dirname, const chrome_target_dir = path.join(__dirname, "target", "chrome") const firefox_target_dir = path.join(__dirname, "target", "firefox") const thunderbird_target_dir = path.join(__dirname, "target", "thunderbird") +const qutebrowser_target_dir = path.join(__dirname, "target", "qutebrowser") const chromeConfig = (config, env) => { const result = Object.assign(deepCopy(config), { @@ -213,6 +219,36 @@ const thunderbirdConfig = (config, env) => { return result; } +const qutebrowserConfig = (config, env) => { + delete config.entry.background; + delete config.entry.content; + delete config.entry.index; + delete config.entry.browserAction; + config.entry.firenvim = "./src/qutebrowser.ts"; + console.log(config) + const result = Object.assign(deepCopy(config), { + output: { + path: qutebrowser_target_dir, + }, + plugins: [new CopyWebPackPlugin({ + patterns: qutebrowserFiles.map(file => ({ + from: file, + to: qutebrowser_target_dir, + transform: (content, src) => { + console.log(src); + return content; + } + })) + })] + }); + try { + fs.rmdirSync(result.output.path, { recursive: true }) + } catch (e) { + console.log(`Could not delete output dir (${e.message})`); + } + return result; +} + module.exports = args => { let env = ""; if (args instanceof Object) { @@ -236,7 +272,14 @@ module.exports = args => { return [firefoxConfig(config, env)]; } else if (env.startsWith("thunderbird")) { return [thunderbirdConfig(config, env)]; + } else if (env.startsWith("qutebrowser")) { + return [qutebrowserConfig(config, env)]; } - return [chromeConfig(config, env), firefoxConfig(config, env), thunderbirdConfig(config, env)]; + return [ + chromeConfig(config, env), + firefoxConfig(config, env), + thunderbirdConfig(config, env), + qutebrowserConfig(config, env), + ]; }