diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index a493b24..8796847 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -20,11 +20,10 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 + with: + clean: false - - name: Restart Web Service + - name: Deploy via Docker Compose run: | - echo "Deploying the latest main branch..." - # Insert command to restart the application, e.g., - # sudo systemctl restart gunicorn - # Or if running via a local script: - # ./restart_server.sh + echo "Building and restarting the Docker container..." + docker compose up -d --build diff --git a/.gitignore b/.gitignore index b7faf40..890b221 100644 --- a/.gitignore +++ b/.gitignore @@ -201,6 +201,9 @@ cython_debug/ .cursorignore .cursorindexingignore +# Private assets (e.g., CV) kept out of git +private_assets/ + # Marimo marimo/_static/ marimo/_lsp/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..bcc1337 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +FROM python:3.9-slim + +WORKDIR /app + +# Install gettext for envsubst +RUN apt-get update && apt-get install -y gettext-base && rm -rf /var/lib/apt/lists/* + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +# Expose port +EXPOSE 80 + +# Run gunicorn +CMD ["gunicorn", "--bind", "0.0.0.0:80", "app:app"] diff --git a/README.md b/README.md index 7790107..8c8bee5 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,20 @@ # home -This is the code for my website +This is the code for my website. I'll mostly be publishing blogs and documenting my kernel dev/system administration journey here. + +``` +_________________________________________ +/ Base 8 is just like base 10, if you are \ +| missing two fingers. | +| | +\ -- Tom Lehrer / + ----------------------------------------- + \ ^__^ + \ (oo)\_______ + (__)\ )\/\ + ||----w | + || || +``` + +## Deployment Status + +[![Deploy](https://github.com/TuringVerified/home/actions/workflows/deploy.yml/badge.svg?branch=main)](https://github.com/TuringVerified/home/actions/workflows/deploy.yml) \ No newline at end of file diff --git a/app.py b/app.py index b53f577..e856bed 100644 --- a/app.py +++ b/app.py @@ -1,7 +1,10 @@ -from flask import Flask, render_template, send_from_directory +import os + +from flask import Flask, redirect, render_template, send_from_directory app = Flask(__name__) + @app.route("/") def home(): return render_template("index.html") @@ -26,5 +29,10 @@ def robots(): def blog_omen(): return render_template("blog_omen.html") + +@app.route("/cv") +def cv(): + return render_template("cv_embed.html") + if __name__ == "__main__": - app.run(debug=True) + app.run(debug=False, host='0.0.0.0') diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..fda96b2 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +Flask==2.0.1 +Werkzeug==2.0.1 +gunicorn==20.1.0 diff --git a/templates/about.html b/templates/about.html index 5cdf654..da8f4ec 100644 --- a/templates/about.html +++ b/templates/about.html @@ -24,7 +24,7 @@

About Me

-

I'm Turing Tested. Here's my home on the internet. I mainly plan on posting my findings .

+

I'm Turing Tested. Here's my home on the internet. I mainly plan on posting my kernel dev journey and homelabbing pursuits.

Socials

-
-

Content goes here...

+
+ +

HP's Omen series comes with a great suite of features via the Omen Gaming Hub. These include a full-fledged lighting studio for the keyboard backlights (it even ships with a built-in audio visualiser and a bunch of themes), GPU performance modes, fan speed support and statistics. Unfortunately, this technology is only available on Windows through their proprietary app, with no plans from HP's end to expand support for Linux users as well.

+ +

This makes our lives harder, as none of these features are available for Linux out-of-the-box. While there has been significant effort from the community to reverse-engineer parts of the stack and develop support for Linux, most features still remain unsupported. The Linux ecosystem still remains incomplete and undocumented. Through this blog series, I will be attempting to implement some of these features myself, something like asusctl, but for Omen laptops. My target is to create at least a complete utility and drivers to interface with software like OpenRGB.

+ +

Fortunately much of the groundwork has already been laid down by community members. Most significantly, pelrun has done a significant amount of reverse-engineering of the interfaces exposed by HP's firmware. Their work documents parts of the WMI/ACPI interface used by the Omen Gaming Hub on Windows and shows how they can be triggered outside of Windows as well. Their fork of the hp-wmi driver provided great insight into the command structure used by the firmware. However, the current solution is not a fully-integrated Linux solution yet. My goal with this project is to build on that and move towards something more cohesive.

+ +

The Omen series exposes device controls through ACPI methods that are accessible through WMI. Unfortunately, HP does not make any documentation for these public, forcing us to poke around quite a bit in order to find relevant values. Like for example, the GUID for the WMI interface on my system was:

+ +
5FB7F034-2C63-45E9-BE91-3D44E2C707E4
+ +

This will enable us to access the firmware like so:

+ +
+

How Omen Features Work

+
+
+

Windows

+
Omen Gaming Hub
+
+
Windows WMI API
+
+
ACPI / WMI Firmware
+
+
Laptop Hardware
(RGB • Fans • GPU modes)
+
+
+

Linux (Goal)

+
OpenRGB / omenctl
+
+
Custom Kernel Driver
+
+
Linux WMI Subsystem
+
+
ACPI / WMI Firmware
+
+
Laptop Hardware
(RGB • Fans • GPU modes)
+
+
+
+ + + +

To test this out, I wrote a minimal external kernel module, that registered itself with the WMI subsystem and attached itself to the firmware interface exposed by the system:

+ +
#include <linux/module.h>
+#include <linux/wmi.h>
+
+#define HPWMI_BIOS_GUID "5FB7F034-2C63-45E9-BE91-3D44E2C70733"
+
+static int omen_wmi_probe(struct wmi_device *wdev, const void *context)
+{
+    pr_info("omen_wmi: device detected\n");
+    return 0;
+}
+
+static void omen_wmi_remove(struct wmi_device *wdev)
+{
+    pr_info("omen_wmi: device removed\n");
+}
+
+static const struct wmi_device_id omen_wmi_id_table[] = {
+    { HPWMI_BIOS_GUID, NULL },
+    { }
+};
+
+static struct wmi_driver omen_wmi_driver = {
+    .driver = {
+        .name = "omen_wmi",
+    },
+    .id_table = omen_wmi_id_table,
+    .probe = omen_wmi_probe,
+    .remove = omen_wmi_remove,
+};
+
+module_wmi_driver(omen_wmi_driver);
+
+MODULE_LICENSE("GPL");
+ +

The Makefile is a separate file in the same directory (save it as Makefile with no extension):

+ +
obj-m += omen_wmi_bridge.o
+
+all:
+	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
+
+clean:
+	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
+ +

In order to run this module:

+ +
make
+sudo rmmod omen_wmi_bridge   # No need to run this the first time
+sudo insmod omen_wmi_bridge.ko
+sudo dmesg -w
+ +

Et voilà, you should see the driver when you run lsmod | grep omen. However, this was a very minimal example to show the structure of the driver. However, we aren't exposing anything to the userspace yet. In order to control the backlight, we need to create interfaces that userspace programs will be able to interact with.

+

The ACPI interface used by Omen's firmware expects a 128-byte buffer as input. A significant chunk has already been reverse-engineered by pelrun and reveals some important details about the internals of the firmware and the structure of this buffer. The keyboard is divided into 4 different zones (Zone0 - Right, Zone1- Middle, Zone2 - Left, Zone3 - WASD) and the buffer looks like this:

+ +
+ +

Firmware Command Buffer (128 bytes)

+ +
+ +
0-3
PASS status
+
4-24
Unknown fields
+ +
25
Z1 R
+
26
Z1 G
+
27
Z1 B
+ +
28
Z2 R
+
29
Z2 G
+
30
Z2 B
+ +
31
Z3 R
+
32
Z3 G
+
33
Z3 B
+ +
34
Z4 R
+
35
Z4 G
+
36
Z4 B
+ +
37-127
Unknown / unused
+ +
+ + +
+

If you want to play around with this yourself, here is the full kernel module:

+ +
+ omen_wmi_bridge.c — full source +
#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/wmi.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+
+#define HPWMI_BIOS_GUID "5FB7F034-2C63-45E9-BE91-3D44E2C707E4"
+
+#define HPWMI_FOURZONE 0x20009
+
+#define HPWMI_FOURZONE_COLOR_GET 2
+#define HPWMI_FOURZONE_COLOR_SET 3
+#define HPWMI_FOURZONE_BRIGHT_GET 4
+#define HPWMI_FOURZONE_BRIGHT_SET 5
+#define HPWMI_FOURZONE_ANIM_GET  6
+#define HPWMI_FOURZONE_ANIM_SET  7
+
+#define ZONE_BASE  25
+#define ZONE_COUNT 4
+
+struct bios_args {
+    u32 signature;
+    u32 command;
+    u32 commandtype;
+    u32 datasize;
+    u8  data[128];
+};
+
+static struct kobject *omen_kobj;
+
+/* ---------- WMI helper ---------- */
+
+static int omen_wmi_query(int cmdtype, u8 *buffer)
+{
+    struct bios_args args = {0};
+    struct acpi_buffer input;
+    struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+    union acpi_object *obj;
+    int ret = -EINVAL;
+
+    args.signature   = 0x55434553;
+    args.command     = HPWMI_FOURZONE;
+    args.commandtype = cmdtype;
+    args.datasize    = 128;
+
+    if (buffer && cmdtype == HPWMI_FOURZONE_COLOR_SET)
+        memcpy(args.data, buffer, 128);
+
+    input.length  = sizeof(args);
+    input.pointer = &args;
+
+    wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 3, &input, &output);
+
+    obj = output.pointer;
+
+    if (!obj || obj->type != ACPI_TYPE_BUFFER)
+        goto out;
+
+    if (buffer && cmdtype == HPWMI_FOURZONE_COLOR_GET)
+        memcpy(buffer, obj->buffer.pointer + 8, 128);
+
+    ret = 0;
+
+out:
+    kfree(obj);
+    return ret;
+}
+
+/* ---------- zone interface ---------- */
+
+static ssize_t zone_show(struct kobject *kobj,
+                         struct kobj_attribute *attr,
+                         char *buf)
+{
+    u8 state[128];
+    int zone = attr->attr.name[4] - '0';
+    int off  = ZONE_BASE + zone * 3;
+
+    if (omen_wmi_query(HPWMI_FOURZONE_COLOR_GET, state))
+        return -EIO;
+
+    return sprintf(buf, "%02x%02x%02x\n",
+                   state[off], state[off+1], state[off+2]);
+}
+
+static ssize_t zone_store(struct kobject *kobj,
+                          struct kobj_attribute *attr,
+                          const char *buf,
+                          size_t count)
+{
+    u8 state[128];
+    unsigned int rgb;
+    int zone = attr->attr.name[4] - '0';
+    int off  = ZONE_BASE + zone * 3;
+
+    if (kstrtouint(buf, 16, &rgb))
+        return -EINVAL;
+
+    if (omen_wmi_query(HPWMI_FOURZONE_COLOR_GET, state))
+        return -EIO;
+
+    state[off]   = (rgb >> 16) & 0xff;
+    state[off+1] = (rgb >>  8) & 0xff;
+    state[off+2] =  rgb         & 0xff;
+
+    if (omen_wmi_query(HPWMI_FOURZONE_COLOR_SET, state))
+        return -EIO;
+
+    return count;
+}
+
+/* ---------- raw buffer ---------- */
+
+static ssize_t raw_show(struct kobject *kobj,
+                        struct kobj_attribute *attr,
+                        char *buf)
+{
+    u8 state[128];
+
+    if (omen_wmi_query(HPWMI_FOURZONE_COLOR_GET, state))
+        return -EIO;
+
+    memcpy(buf, state, 128);
+    return 128;
+}
+
+static ssize_t raw_store(struct kobject *kobj,
+                         struct kobj_attribute *attr,
+                         const char *buf,
+                         size_t count)
+{
+    u8 state[128];
+
+    if (count != 128)
+        return -EINVAL;
+
+    memcpy(state, buf, 128);
+
+    if (omen_wmi_query(HPWMI_FOURZONE_COLOR_SET, state))
+        return -EIO;
+
+    return count;
+}
+
+/* ---------- command interface ---------- */
+
+static ssize_t cmd_store(struct kobject *kobj,
+                         struct kobj_attribute *attr,
+                         const char *buf,
+                         size_t count)
+{
+    struct bios_args args = {0};
+    struct acpi_buffer input;
+    struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+    union acpi_object *obj;
+    unsigned int cmdtype;
+    unsigned int value = 0;
+
+    sscanf(buf, "%u %x", &cmdtype, &value);
+
+    args.signature   = 0x55434553;
+    args.command     = HPWMI_FOURZONE;
+    args.commandtype = cmdtype;
+    args.datasize    = 128;
+
+    memset(args.data, 0, 128);
+    args.data[0] = value;
+
+    input.length  = sizeof(args);
+    input.pointer = &args;
+
+    wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 3, &input, &output);
+
+    obj = output.pointer;
+
+    if (obj && obj->type == ACPI_TYPE_BUFFER)
+        print_hex_dump(KERN_INFO,
+                       "omen_cmd: ",
+                       DUMP_PREFIX_OFFSET,
+                       16, 1,
+                       obj->buffer.pointer,
+                       obj->buffer.length,
+                       false);
+
+    kfree(obj);
+    return count;
+}
+
+/* ---------- sysfs ---------- */
+
+#define ZONE_ATTR(n) \
+static struct kobj_attribute zone##n##_attr = \
+__ATTR(zone##n, 0664, zone_show, zone_store);
+
+ZONE_ATTR(0)
+ZONE_ATTR(1)
+ZONE_ATTR(2)
+ZONE_ATTR(3)
+
+static struct kobj_attribute raw_attr =
+    __ATTR(raw, 0664, raw_show, raw_store);
+
+static struct kobj_attribute cmd_attr =
+    __ATTR(cmd, 0220, NULL, cmd_store);
+
+static struct attribute *attrs[] = {
+    &zone0_attr.attr,
+    &zone1_attr.attr,
+    &zone2_attr.attr,
+    &zone3_attr.attr,
+    &raw_attr.attr,
+    &cmd_attr.attr,
+    NULL
+};
+
+static struct attribute_group attr_group = {
+    .attrs = attrs,
+};
+
+/* ---------- module ---------- */
+
+static int __init omen_init(void)
+{
+    int ret;
+
+    omen_kobj = kobject_create_and_add("omen_rgb", kernel_kobj);
+    if (!omen_kobj)
+        return -ENOMEM;
+
+    ret = sysfs_create_group(omen_kobj, &attr_group);
+
+    pr_info("omen rgb module loaded\n");
+    return ret;
+}
+
+static void __exit omen_exit(void)
+{
+    kobject_put(omen_kobj);
+    pr_info("omen rgb module unloaded\n");
+}
+
+module_init(omen_init);
+module_exit(omen_exit);
+
+MODULE_LICENSE("GPL");
+
+ + + +
+

Once the module is compiled and loaded, the driver will create a small set of interfaces under sysfs:

+ +

ls /sys/kernel/omen_rgb

+ +

You should see something like:

+ +

cmd raw zone0 zone1 zone2 zone3

+ +

Each of these files exposes a different way of interacting with the firmware.

+ +

The raw file simply dumps the entire buffer returned by the firmware. You can use cat to view the current state of the RGB lighting.

+ +

cat /sys/kernel/omen_rgb/raw

+ +

Similarly, cmd allows commands to be sent to this buffer, like echo "3 ff0000 ff0000 ff0000 ff0000" | sudo tee /sys/kernel/omen_rgb/cmd, this is an unsafe function and intended for easier exploration. Every now and then, you will hit a state where your keyboard light is unresponsive because you messed with the wrong input. Simply reboot your laptop and hold the power button for 30 seconds. This discharges all capacitors and restarts the embedded controller managing our lighting.

+

Just like cmd, you can also use something like echo ff0000 | sudo tee /sys/kernel/omen_rgb/zone0 to set the color of the zeroth zone and so on.

+ +

Now that we are done with setup, let's start hacking. When I was going through pelrun's code, I found a very interesting comment:

+

+ + if (read_or_write == HPWMI_WRITE) { + // Zones start at offset 25. Wonder what's in the rest of the buffer? + state[zone->offset + 0] = zone->colors.red; + state[zone->offset + 1] = zone->colors.green; + state[zone->offset + 2] = zone->colors.blue; + + ret = hp_wmi_perform_query(HPWMI_FOURZONE_COLOR_SET, HPWMI_FOURZONE, &state, + sizeof(state), sizeof(state)); +

+ + +

Zones start at offset 25. Which is correct. This is what started off the whole quest. So between the pass message and the zone, there are multiple empty bits that may/may not toggle more functionality that haven't been explored yet. Could be brightness. Could be animations. Could be anything? I decided to test this out by creating a separate Python file, and looping through the most plausible iterations, like so:

+ +


+        import os
+        import time
+
+        CMD = "/sys/kernel/omen_rgb/cmd"
+
+        BUFFER_SIZE = 128
+        VALUES = ["00", "01", "0f", "7f", "ff"]
+
+        for offset in range(4, 25):
+            for v in VALUES:
+
+                buf = ["00"] * BUFFER_SIZE
+                buf[offset] = v
+
+                payload = " ".join(buf)
+
+                os.system(f"echo '3 {payload}' | sudo tee {CMD} > /dev/null")
+
+                print(f"testing offset {offset} -> {v}")
+                time.sleep(1)
+

+

However, I did not recieve any output. My keyboard lighting remained as-is. Very disappointing :<. But, we have a good set up for experimentation now. We've exposed the raw firmware interface using sysfs, know parts of the buffer, and are on track to discovering more. In the next blog, I'll take a few more different approaches to uncover more of this structre, especially considering how so many more bytes are left. I'll dig through the ACPI tables and try experimenting with other methods. Hopefully this blog helps others as well, to set up a small little kernel module for experimentation. I plan on rewriting everything properly and merging it with the main hp-wmi.c file once this whole project is done. But for now, having a separate module gives a great amount of flexibility to mess around and find out more. Hopefully, this will translate into an omenctl some day!

+ +
+ // AI disclaimer
+ Parts of the code in this post were written with AI assistance. All code has been reviewed, tested, and understood before being published here. +
+
[cd ../] Return to Blogs
+
diff --git a/templates/blogs.html b/templates/blogs.html index 5005276..3fb7db7 100644 --- a/templates/blogs.html +++ b/templates/blogs.html @@ -26,7 +26,7 @@
-

Logs

+

Blogs

Documenting my findings
@@ -40,10 +40,10 @@

Logs

-
March 9, 2026
+
Creating drivers for Omen Gaming Hub exclusive features on Linux.

- Currently being written... + We're missing out on quite a lot.

[Read more]
diff --git a/templates/cv_embed.html b/templates/cv_embed.html new file mode 100644 index 0000000..34ba29c --- /dev/null +++ b/templates/cv_embed.html @@ -0,0 +1,92 @@ + + + + + + + My CV + + + +
+ +

+ Your browser does not support PDFs. Download the PDF. +

+
+
+ + \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 0b813d8..8dfc66c 100644 --- a/templates/index.html +++ b/templates/index.html @@ -44,25 +44,28 @@

Turing Tested

import { init, Terminal as GhosttyTerminal } from "https://cdn.jsdelivr.net/npm/ghostty-web/+esm"; let term; -let isGhostty = false; try { + await init(); + term = new GhosttyTerminal({ - cursorBlink: true, - fontSize: 15, - fontFamily: "'JetBrains Mono', 'Fira Code', monospace", - theme: { - background: "#020202", - foreground: "#00ff9c", - cursor: "#00ff9c", - cursorAccent: "#020202", - selection: "rgba(0, 255, 156, 0.3)", - } + cursorBlink: true, + fontSize: 15, + fontFamily: "'JetBrains Mono', 'Fira Code', monospace", + theme: { + background: "#020202", + foreground: "#00ff9c", + cursor: "#00ff9c", + cursorAccent: "#020202", + selection: "rgba(0, 255, 156, 0.3)", + } }); - isGhostty = true; + } catch (e) { + console.warn("Falling back to Xterm...", e); + term = new window.Terminal({ cursorBlink: true, fontSize: 15, @@ -78,110 +81,93 @@

Turing Tested

} term.open(document.getElementById("terminal")); -if(term.focus) term.focus(); + +if (term.focus) { + term.focus(); +} let buffer = ""; -async function runFortune(term) { - term.write("\r\nFetching fortune...\r\n"); - try { - const response = await fetch("https://api.justyy.workers.dev/api/fortune?t=" + Date.now()); - if (response.ok) { - const data = await response.text(); - const cleanData = data.replace(/^"|"$/g, '').replace(/\\n/g, '\r\n').replace(/\\t/g, '\t'); - term.write("\x1b[36m" + cleanData + "\x1b[0m\r\n"); - } else { - term.write("\x1b[31mError retrieving fortune.\x1b[0m\r\n"); - } - } catch (e) { - term.write("\x1b[31mNetwork error retrieving fortune.\x1b[0m\r\n"); - } - prompt(); +/* + Prompt renderer. +*/ +function prompt() { + term.write("\x1b[1m[turing@tested ~]$\x1b[0m "); } -function prompt() { - term.write("\r\n\x1b[1m[turing@tested ~]$\x1b[0m "); +/* + Future shell engine boundary. + + Later this becomes: + wasm.executeCommand(input) + + JS should only: + - send text + - receive text +*/ +function executeCommand(input) { + + if (input === "") { + return ""; + } + + return `You typed: ${input}`; } term.write("Use `help` to list available commands.\r\n"); + prompt(); term.onData(e => { - if (e === "\r") { - const cmdArgs = buffer.trim().split(" "); - const cmd = cmdArgs[0]; - const rawCmd = buffer.trim(); - buffer = ""; - - if (cmd === "help") { - term.write("\r\nTuring Tested OS - Help Menu"); - term.write("\r\n=========================="); - term.write("\r\n\x1b[32mhelp\x1b[0m : Show this help message"); - term.write("\r\n\x1b[32mls\x1b[0m : List files in current directory"); - term.write("\r\n\x1b[32mcat [file]\x1b[0m : Display contents of a file (try 'cat about' or 'cat pgp')"); - term.write("\r\n\x1b[32mopen [app]\x1b[0m : Open an application or page (try 'open blogs')"); - term.write("\r\n\x1b[32mclear\x1b[0m : Clear terminal screen"); - term.write("\r\n\x1b[32mfortune\x1b[0m : The classic Linux command"); - } - else if (cmd === "ls") { - term.write("\r\nabout pgp blogs"); - } - else if (rawCmd === "cat about") { - term.write("\r\nFOSS shill."); - term.write("\r\nInterested in system administration, the Linux kernel, and decentralised technologies."); - } - else if (rawCmd === "cat pgp") { - term.write("\r\nPublic key available at /pgp"); - } - else if (cmd === "cat") { - if (cmdArgs.length > 1) { - term.write("\r\ncat: " + cmdArgs[1] + ": No such file or directory"); - } else { - term.write("\r\ncat: missing file operand"); - } - } - else if (cmd === "clear") { - term.write('\x1b[2J\x1b[H'); - prompt(); - return; + + // ENTER + if (e === "\r") { + + const input = buffer.trim(); + + buffer = ""; + + const output = executeCommand(input); + + term.write("\r\n"); + + if (output !== "") { + term.write(output); + } + + term.write("\r\n"); + + prompt(); } - else if (cmd === "fortune") { - runFortune(term); - return; // prompt is handled by runFortune async + + // CTRL+C + else if (e === "\u0003") { + + term.write("^C\r\n"); + + buffer = ""; + + prompt(); } - else if (cmd === "open") { - if (cmdArgs.length > 1) { - const target = cmdArgs[1]; - if (["blogs", "about", "pgp"].includes(target)) { - term.write(`\r\nNavigating to /${target}...`); - setTimeout(() => { window.location = `/${target}`; }, 400); - } else { - term.write("\r\nopen: unknown target. Available targets: blogs, about, pgp"); + + // BACKSPACE + else if (e === "\u007f") { + + if (buffer.length > 0) { + + buffer = buffer.slice(0, -1); + + term.write("\b \b"); } - } else { - term.write("\r\nopen: missing target"); - } - } - else if (cmd !== "") { - term.write("\r\n\x1b[31mturing: command not found: \x1b[0m" + cmd); } - prompt(); - } - else if (e === "\u0003") { // Ctrl+C handling - term.write("^C"); - buffer = ""; - prompt(); - } - else if (e === "\u007f") { - if (buffer.length > 0) { - buffer = buffer.slice(0, -1); - term.write("\b \b"); + + // NORMAL CHARACTER + else { + + buffer += e; + + term.write(e); } - } - else { - buffer += e; - term.write(e); - } }); diff --git a/templates/pgp.html b/templates/pgp.html index 2b908d6..e113c08 100644 --- a/templates/pgp.html +++ b/templates/pgp.html @@ -22,12 +22,11 @@

PGP Public Key

-

If you need to send me an encrypted email (tt@turingtested.xyz) or verify a signature, you can use the following PGP public key block:

+

If you need to send me an encrypted email or verify a signature, you can use the following PGP public key block:

             -----BEGIN PGP PUBLIC KEY BLOCK-----
 Comment: F902 C22A 0181 D858 9003  884A 097B C53F F8D4 1046
-Comment: Turing Tested 
 
 xsDNBGml+V8BDAC2GNxfXSJXCF3P4vrGUhLZNDpqzQavbfXqzTEEDpBpTOUNz9/7
 0ZXVdUgtnb7mKhAync966AE3ufe6c7kUlTjouTet7YC9PXWR1yqiXM3+q61fof3H