[Mammoth] Add a keyboard library that translates scancode to keycodes.

This commit is contained in:
Drew Galbraith 2023-11-26 11:21:56 -08:00
parent aecae8e41f
commit 2bc64b045c
8 changed files with 286 additions and 108 deletions

View File

@ -1,5 +1,6 @@
add_library(mammoth STATIC
file/file.cpp
input/keyboard.cpp
ipc/channel.cpp
ipc/endpoint_client.cpp
ipc/endpoint_server.cpp
@ -25,6 +26,7 @@ target_link_libraries(mammoth
glacier
victoriafalls_yunq
yellowstone_yunq
voyageurs_yunq
zion_stub
)

View File

@ -0,0 +1,173 @@
#include "input/keyboard.h"
#include <voyageurs/voyageurs.yunq.client.h>
#include <yellowstone/yellowstone.yunq.client.h>
#include <zglobal.h>
#include "util/debug.h"
namespace mmth {
namespace {
void KeyboardListenerEntry(void* keyboard_base) {
reinterpret_cast<KeyboardListenerBase*>(keyboard_base)->ListenLoop();
}
} // namespace
KeyboardListenerBase::KeyboardListenerBase() {
auto server_or = PortServer::Create();
if (!server_or) {
crash("Failed to create server", server_or.error());
}
server_ = server_or.value();
}
void KeyboardListenerBase::Register() {
YellowstoneClient client(gInitEndpointCap);
GetEndpointRequest req;
req.set_endpoint_name("voyageurs");
Endpoint endpt;
check(client.GetEndpoint(req, endpt));
VoyageursClient vclient(endpt.endpoint());
KeyboardListener listn;
// TODO: Create a "ASSIGN_OR_CRASH" macro to simplify this.
auto client_or = server_.CreateClient();
if (!client_or.ok()) {
crash("Failed to create client", client_or.error());
}
listn.set_port_capability(client_or.value().cap());
None n;
check(vclient.RegisterKeyboardListener(listn, n));
}
Thread KeyboardListenerBase::Listen() {
return Thread(KeyboardListenerEntry, this);
}
void KeyboardListenerBase::ListenLoop() {
while (true) {
auto scancode_or = server_.RecvChar();
if (!scancode_or.ok()) {
check(scancode_or.error());
}
uint8_t scancode = scancode_or.value();
Keycode k = ScancodeToKeycode(scancode);
Action a = ScancodeToAction(scancode);
HandleKeycode(k, a);
}
}
void KeyboardListenerBase::HandleKeycode(Keycode code, Action action) {
char c = '\0';
if (action == kPressed) {
if (code >= kA && code <= kZ) {
const char* alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
c = alpha[code - kA];
} else if (code >= k1 && code <= k0) {
const char* num = "1234567890";
c = num[code - k1];
} else if (code == kEnter) {
c = '\n';
} else if (code == kSpace) {
c = ' ';
}
}
if (c != '\0') {
HandleCharacter(c);
}
}
Keycode KeyboardListenerBase::ScancodeToKeycode(uint8_t scancode) {
// Cancel out the released bit.
scancode &= 0x7F;
switch (scancode) {
case 0x02:
return k1;
case 0x03:
return k2;
case 0x04:
return k3;
case 0x05:
return k4;
case 0x06:
return k5;
case 0x07:
return k6;
case 0x08:
return k7;
case 0x09:
return k8;
case 0x0A:
return k9;
case 0x0B:
return k0;
case 0x10:
return kQ;
case 0x11:
return kW;
case 0x12:
return kE;
case 0x13:
return kR;
case 0x14:
return kT;
case 0x15:
return kY;
case 0x16:
return kU;
case 0x17:
return kI;
case 0x18:
return kO;
case 0x19:
return kP;
case 0x1E:
return kA;
case 0x1F:
return kS;
case 0x20:
return kD;
case 0x21:
return kF;
case 0x22:
return kG;
case 0x23:
return kH;
case 0x24:
return kJ;
case 0x25:
return kK;
case 0x26:
return kL;
case 0x2C:
return kZ;
case 0x2D:
return kX;
case 0x2E:
return kC;
case 0x2F:
return kV;
case 0x30:
return kB;
case 0x31:
return kN;
case 0x32:
return kM;
}
dbgln("Unknown scancode {x}", scancode);
return kUnknownKeycode;
}
Action KeyboardListenerBase::ScancodeToAction(uint8_t scancode) {
return (scancode & 0x80) ? kReleased : kPressed;
}
} // namespace mmth

View File

@ -0,0 +1,88 @@
#pragma once
#include "mammoth/ipc/port_server.h"
#include "mammoth/proc/thread.h"
namespace mmth {
enum Keycode {
kUnknownKeycode = 0x0,
kA = 0x1,
kB = 0x2,
kC = 0x3,
kD = 0x4,
kE = 0x5,
kF = 0x6,
kG = 0x7,
kH = 0x8,
kI = 0x9,
kJ = 0xA,
kK = 0xB,
kL = 0xC,
kM = 0xD,
kN = 0xE,
kO = 0xF,
kP = 0x10,
kQ = 0x11,
kR = 0x12,
kS = 0x13,
kT = 0x14,
kU = 0x15,
kV = 0x16,
kW = 0x17,
kX = 0x18,
kY = 0x19,
kZ = 0x1A,
k1 = 0x20,
k2 = 0x21,
k3 = 0x22,
k4 = 0x23,
k5 = 0x24,
k6 = 0x25,
k7 = 0x26,
k8 = 0x27,
k9 = 0x28,
k0 = 0x29,
kSpace = 0x30,
kEnter = 0x31,
};
enum Action {
kUnknownAction,
kPressed,
kReleased,
};
class KeyboardListenerBase {
public:
KeyboardListenerBase();
KeyboardListenerBase(const KeyboardListenerBase&) = delete;
KeyboardListenerBase(KeyboardListenerBase&&) = delete;
void Register();
Thread Listen();
void ListenLoop();
// Override this to recieve all raw keycodes. By default
// this function will try to translate each keycode into
// a printable character and call HandleCharacter.
virtual void HandleKeycode(Keycode code, Action action);
// This function is called by the default HandleKeycode
// implementation if you do not override it. If it recieves
// input that corresponds to a printable character it will
virtual void HandleCharacter(char c){};
private:
PortServer server_;
Keycode ScancodeToKeycode(uint8_t scancode);
Action ScancodeToAction(uint8_t scancode);
};
} // namespace mmth

View File

@ -2,6 +2,7 @@ add_executable(teton
framebuffer/console.cpp
framebuffer/framebuffer.cpp
framebuffer/psf.cpp
keyboard_listener.cpp
teton.cpp
)

View File

@ -0,0 +1,3 @@
#include "keyboard_listener.h"
void KeyboardListener::HandleCharacter(char c) { console_.WriteChar(c); }

View File

@ -0,0 +1,12 @@
#include <mammoth/input/keyboard.h>
#include "framebuffer/console.h"
class KeyboardListener : public mmth::KeyboardListenerBase {
public:
KeyboardListener(Console& c) : mmth::KeyboardListenerBase(), console_(c) {}
virtual void HandleCharacter(char c) override;
private:
Console& console_;
};

View File

@ -2,12 +2,12 @@
#include <mammoth/util/debug.h>
#include <mammoth/util/init.h>
#include <victoriafalls/victoriafalls.yunq.client.h>
#include <voyageurs/voyageurs.yunq.client.h>
#include <yellowstone/yellowstone.yunq.client.h>
#include "framebuffer/console.h"
#include "framebuffer/framebuffer.h"
#include "framebuffer/psf.h"
#include "keyboard_listener.h"
uint64_t main(uint64_t init_port) {
ParseInitPort(init_port);
@ -37,26 +37,12 @@ uint64_t main(uint64_t init_port) {
console.WriteChar(i);
}
GetEndpointRequest req;
req.set_endpoint_name("voyageurs");
Endpoint endpoint;
RET_ERR(client.GetEndpoint(req, endpoint));
KeyboardListener listener(console);
listener.Register();
VoyageursClient voyaguers(endpoint.endpoint());
ASSIGN_OR_RETURN(mmth::PortServer server, mmth::PortServer::Create());
KeyboardListener listener;
ASSIGN_OR_RETURN(mmth::PortClient pclient, server.CreateClient());
listener.set_port_capability(pclient.cap());
None n;
RET_ERR(voyaguers.RegisterKeyboardListener(listener, n));
Thread lthread = listener.Listen();
while (true) {
ASSIGN_OR_RETURN(char c, server.RecvChar());
if (c != '\0') {
console.WriteChar(c);
dbgln("{}", c);
}
}
check(lthread.Join());
return 0;
return glcr::OK;
}

View File

@ -2,92 +2,6 @@
#include <mammoth/util/debug.h>
namespace {
char ScancodeToChar(uint8_t code) {
switch (code) {
case 0x02:
return '1';
case 0x03:
return '2';
case 0x04:
return '3';
case 0x05:
return '4';
case 0x06:
return '5';
case 0x07:
return '6';
case 0x08:
return '7';
case 0x09:
return '8';
case 0x0A:
return '9';
case 0x0B:
return '0';
case 0x10:
return 'Q';
case 0x11:
return 'W';
case 0x12:
return 'E';
case 0x13:
return 'R';
case 0x14:
return 'T';
case 0x15:
return 'Y';
case 0x16:
return 'U';
case 0x17:
return 'I';
case 0x18:
return 'O';
case 0x19:
return 'P';
case 0x1E:
return 'A';
case 0x1F:
return 'S';
case 0x20:
return 'D';
case 0x21:
return 'F';
case 0x22:
return 'G';
case 0x23:
return 'H';
case 0x24:
return 'J';
case 0x25:
return 'K';
case 0x26:
return 'L';
case 0x2C:
return 'Z';
case 0x2D:
return 'X';
case 0x2E:
return 'Y';
case 0x2F:
return 'C';
case 0x30:
return 'V';
case 0x31:
return 'B';
case 0x32:
return 'N';
case 0x33:
return 'M';
}
dbgln("Unhandled scancode {x}", code);
return '\0';
}
} // namespace
void InterruptEnter(void* void_keyboard) {
KeyboardDriver* keyboard = static_cast<KeyboardDriver*>(void_keyboard);
@ -111,10 +25,9 @@ void KeyboardDriver::InterruptLoop() {
uint64_t num_bytes = 1;
uint64_t num_caps = 0;
check(ZPortRecv(irq_cap_, &num_bytes, &scancode, &num_caps, nullptr));
dbgln("Scan {x}", scancode);
for (mmth::PortClient& client : listeners_) {
client.WriteByte(ScancodeToChar(scancode));
client.WriteByte(scancode);
}
}
}