71 lines
8.1 KiB
HTML
Executable File
71 lines
8.1 KiB
HTML
Executable File
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Workaround for external keyboards to make special characters in KOReader - Archive - MayVaneDay Studios</title>
|
|
<link href="../../../style.css" rel="stylesheet" type="text/css" media="all">
|
|
<meta name="author" content="Vane Vander">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
</head>
|
|
<body class="mayvaneday">
|
|
<article>
|
|
<div class="box">
|
|
<h1>Workaround for external keyboards to make special characters in KOReader</h1>
|
|
<p>published: 2023-09-01</p>
|
|
</div>
|
|
<hr>
|
|
<div class="box">
|
|
<p>For a while, I've fantasized about turning my little Kobo Libra H2O, an e-reader that runs an embedded and bare-bones version of Linux, into <a href="https://letsdecentralize.org/tutorials/kobo-terminal.html">a more general-purpose computing device</a>. You know, in case the apocalypse hits and somehow all my other devices eventually go to shit. For a while I've been most of the way there: KOReader, a third-party reading application for the Kobo, has an embedded terminal emulator that hands root access to the device to you, no questions asked. You just drop whatever command-line binaries you want in <code>/mnt/onboard/.adds/koreader/plugins/terminal.koplugin/</code>, and as long as they're compiled for Linux <code>armv7l</code> they're good to go. For a while, the terminal emulator had problems with <code>ncurses</code>, so you couldn't use any TUI programs and had to stick exclusively to the command line with its one-off commands... but somewhere along the way that got fixed, so now you can use your fancy Gemini browsers and the built-in <code>vi</code> and anything else that requires an actual text-based interface.</p>
|
|
<p>The only problem with this setup is that the Kobo Libra H2O doesn't support USB OTG out-of-the-box. That means, even if you have a special adapter, you can't use an external keyboard. You're stuck with the virtual keyboard on-screen, which is okay for small operations but <em>highly</em> uncomfortable for long typing sessions like writing, which is mainly what I'd use the extra functionality for. It's also a pain in the ass to use the virtual keyboard to type any special characters like the pipe symbol or an exclamation mark.</p>
|
|
<p>Well, apparently almost a year ago <a href="https://web.archive.org/web/20230120183715/https://github.com/koreader/koreader/releases/tag/v2022.11">KOReader added external keyboard support for Kobo devices</a>. You need a powered USB OTG adapter as the Kobo won't supply enough power to recognize that there's a device connected, but once you have the keyboard and the power source (I use one of those cheap portable battery packs, because if I have to be tethered to an outlet, I'd rather just use an actual computer) connected, KOReader recognizes that there's a keyboard plugged in and enables keyboard shortcuts in the interface and keyboard input for the text editor and terminal.</p>
|
|
<p>...Except that the shift button doesn't work properly.</p>
|
|
<p>On a standard US QWERTY keyboard, pressing Shift and one of the number keys on top makes a special character. Shift and 1, for example, make an exclamation mark. Shift and 2 make an <code>@</code> symbol. Shift and 3 make a hash. Shift and 4 make a dollar sign. Et cetera. However, the Kobo doesn't seem to recognize when the Shift key is being held down for any key <em>other than the twenty-six alphabetic keys.</em> I can TYPE IN ALL CAPS WITH THE SHIFT BUTTON HELD DOWN, but I can't make any underscores or question marks or double quotation marks. "Who cares?" you might ask, in which case I would respond, "Well, when writing, the characters often speak, and they accentuate their sentences! Which creates a problem: if I have to constantly capitulate to the keyboard on the screen to make special characters, then I will always be losing my rhythm (and possibly my train of thought as well)."</p>
|
|
<p>How keyboards work in Linux is pretty complicated, but the gist is this: when you press a key on your keyboard, it doesn't send that key specifically; it sends a "keycode" to the kernel, which decides what key that corresponds to. So when I press the A key on my laptop keyboard, it doesn't send an A, it sends a code which can be seen with <code>sudo evtest</code>:</p>
|
|
<p><code>Event: time 1693317010.094300, type 1 (EV_KEY), code 30 (KEY_A), value 1</code></p>
|
|
<p>As evidenced by the log, the A key corresponds with code 30. Which matches <a href="https://archive.ph/https://raw.githubusercontent.com/koreader/koreader/master/plugins/externalkeyboard.koplugin/event_map_keyboard.lua">the keycode map KOReader uses to interpret keyboard events on Kobo devices</a>.</p>
|
|
<p>So I thought that maybe the Shift key wasn't being captured properly on the Kobo, which was causing Shift and 1 to output 1 instead of an exclamation mark. So I ran <code>evtest --grab /dev/input/event4</code> on the Kobo:</p>
|
|
<pre>
|
|
Event: time 1693317622.024915, type 4 (Misc), code 4 (ScanCode), value 70004
|
|
Event: time 1693317554.992894, type 4 (Misc), code 4 (ScanCode), value 700e1
|
|
Event: time 1693317640.632890, type 4 (Misc), code 4 (ScanCode), value 700e1
|
|
Event: time 1693317640.744919, type 4 (Misc), code 4 (ScanCode), value 7001e
|
|
</pre>
|
|
<p>For whatever reason, the Kobo's kernel reports the key presses not as type 1 but as type 4, all with the same codes. The value is what's important here. According to the values, <a href="https://web.archive.org/web/20230829140343/https://deskthority.net/viewtopic.php?t=24076">the first key press is our example, the A key</a>. The second one is the Shift key all by itself. The third and fourth ones are when I attempted to make that exclamation mark by pressing Shift and 1 together. As you can see, the exclamation mark isn't its own key put together by the keyboard's firmware or whatever; it's something else in the system that sees the Shift and 1 being pressed at the same time and chooses to interpret that as an exclamation mark. And that "something else" on the Kobo only likes letters.</p>
|
|
<p>But we <em>know</em> that the Kobo can handle special characters. KOReader has to <em>somehow</em> map the hardware "previous page" and "next page" buttons to, well, turning the pages of the book back or forwards.</p>
|
|
<p>It turns out that our salvation lies in that keycode map in KOReader. If you <a href="https://archive.md/https://raw.githubusercontent.com/koreader/koreader/master/plugins/externalkeyboard.koplugin/event_map_keyboard.lua">take another look at it</a>, you can see that not all the keys match up to an ASCII equivalent. For example, Page Up, or whatever key sends keycode 104, gets mapped to "LPgBack". Which matches our standard laptop keyboard again:</p>
|
|
<p><code>Event: time 1693318576.571605, type 1 (EV_KEY), code 104 (KEY_PAGEUP), value 0</code></p>
|
|
<p><strong>Theoretically we should be able to take an arbitrary key on the keyboard, capture its keycode, and then put it into this map to make it output whatever character we want.</strong></p>
|
|
<p>So let's add the bracket keys, which got omitted for some reason:</p>
|
|
<p><code>[26] = "[", [27] = "]",</code></p>
|
|
<p>And then let's change all the F1 through F12 buttons to be the special characters locked behind the number keys, as well as the question mark and double quotations:</p>
|
|
<pre>
|
|
[59] = "!",
|
|
[60] = "@",
|
|
[61] = "#",
|
|
[62] = "$",
|
|
[63] = "%",
|
|
[64] = "^",
|
|
[65] = "&",
|
|
[66] = "*",
|
|
[67] = "(",
|
|
[68] = ")",
|
|
[87] = "?",
|
|
[88] = '"',
|
|
</pre>
|
|
<p>And just for my cheapo wireless keyboard I got off eBay, let's make the shoulder L and R buttons be the same as the left and right arrow keys:</p>
|
|
<p><code>[272] = "Left", [273] = "Right",</code></p>
|
|
<p>Save the map and restart KOReader, and the keys should now be working properly as mapped. Still no proper Shift, though...</p>
|
|
</div>
|
|
<hr>
|
|
<div class="box">
|
|
<p align=right>CC BY-NC-SA 4.0 © Vane Vander</p>
|
|
</div>
|
|
</article>
|
|
<script data-goatcounter="https://stats.letsdecentralize.org/count"
|
|
async src="//stats.letsdecentralize.org/count.js"></script>
|
|
<noscript>
|
|
<img src="https://stats.letsdecentralize.org/count?p=/blog/2023/september/koreader.html">
|
|
</noscript>
|
|
</body>
|
|
</html>
|