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),
+ ];
}