From f60b2c6ec4ad30888085c3915af655a8d87481f1 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Tue, 22 Apr 2014 14:02:04 +0200 Subject: [PATCH] Add keyboard layout support to kernel. --- kernel/.gitignore | 2 + kernel/Makefile | 10 ++ kernel/include/sortix/kernel/keyboard.h | 8 -- kernel/kb/default-kblayout.kblayout | 1 + kernel/kb/kblayout.cpp | 163 ++++++++++++++++++++++++ kernel/kb/kblayout.h | 60 +++++++++ kernel/kb/layout/us.cpp | 108 ++++++++-------- kernel/kb/layout/us.h | 10 +- kernel/kernel.cpp | 9 +- kernel/logterminal.cpp | 42 ++++-- kernel/logterminal.h | 9 +- 11 files changed, 338 insertions(+), 84 deletions(-) create mode 120000 kernel/kb/default-kblayout.kblayout create mode 100644 kernel/kb/kblayout.cpp create mode 100644 kernel/kb/kblayout.h diff --git a/kernel/.gitignore b/kernel/.gitignore index 643aa562..33050842 100644 --- a/kernel/.gitignore +++ b/kernel/.gitignore @@ -6,3 +6,5 @@ *.a *.initrd *.out +kb/default-kblayout +kb/default-kblayout.h diff --git a/kernel/Makefile b/kernel/Makefile index 18d4070f..be826e6f 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -108,6 +108,7 @@ interlock.o \ interrupt.o \ ioctx.o \ io.o \ +kb/kblayout.o \ kb/layout/us.o \ kb/ps2.o \ kernelinfo.o \ @@ -193,6 +194,14 @@ sortix.bin: $(ALLOBJS) endif +%: %.kblayout + kblayout-compiler --format=sortix-kblayout-1 --compression=none $< -o $@ + +kb/default-kblayout.h: kb/default-kblayout + carray -cgis --uint8_t --guard=SORTIX_KB_DEFAULT_KBLAYOUT_H --identifier=default_kblayout $< -o $@ + +*.cpp */*.cpp */*/*.cpp: | kb/default-kblayout.h + %.o: %.cpp $(CXX) -c $< -o $@ $(CPPFLAGS) $(CXXFLAGS) @@ -203,6 +212,7 @@ clean: rm -f $(ALLOBJS) sortix.bin rm -f *.bin *.out *.tmp rm -f *.o */*.o */*/*.o + rm -f kb/default-kblayout kb/default-kblayout.h # Installation into sysroot install: install-headers install-kernel diff --git a/kernel/include/sortix/kernel/keyboard.h b/kernel/include/sortix/kernel/keyboard.h index 1b2c3dec..b461d085 100644 --- a/kernel/include/sortix/kernel/keyboard.h +++ b/kernel/include/sortix/kernel/keyboard.h @@ -52,14 +52,6 @@ public: }; -class KeyboardLayout -{ -public: - virtual ~KeyboardLayout() { } - virtual uint32_t Translate(int kbkey) = 0; - -}; - } // namespace Sortix #endif diff --git a/kernel/kb/default-kblayout.kblayout b/kernel/kb/default-kblayout.kblayout new file mode 120000 index 00000000..6107a053 --- /dev/null +++ b/kernel/kb/default-kblayout.kblayout @@ -0,0 +1 @@ +../../kblayout/us.kblayout \ No newline at end of file diff --git a/kernel/kb/kblayout.cpp b/kernel/kb/kblayout.cpp new file mode 100644 index 00000000..ec2b47f8 --- /dev/null +++ b/kernel/kb/kblayout.cpp @@ -0,0 +1,163 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2014. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + kb/kblayout.cpp + Engine that executes a keyboard layout program. + +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "kblayout.h" + +namespace Sortix { + +KeyboardLayoutExecutor::KeyboardLayoutExecutor() +{ + memset(&header, 0, sizeof(header)); + memset(&modifier_counts, 0, sizeof(modifier_counts)); + loaded = false; + actions = NULL; + keys_down = NULL; + modifiers = 0; + saved_data = 0; + saved_data_size = 0; +} + +KeyboardLayoutExecutor::~KeyboardLayoutExecutor() +{ + delete[] actions; + delete[] keys_down; + delete[] saved_data; +} + +bool KeyboardLayoutExecutor::Upload(const uint8_t* input_data, size_t input_size) +{ + size_t data_size = input_size; + uint8_t* data = new uint8_t[data_size]; + if ( !data ) + return false; + memcpy(data, input_data, data_size); + struct kblayout new_header; + if ( data_size < sizeof(struct kblayout) ) + return delete[] data, errno = EINVAL, false; + memcpy(&new_header, data, sizeof(new_header)); + if ( strncmp(new_header.magic, "sortix-kblayout-1", sizeof(new_header.magic)) != 0 ) + return delete[] data, errno = EINVAL, false; + new_header.num_modifiers = le32toh(new_header.num_modifiers); + new_header.num_scancodes = le32toh(new_header.num_scancodes); + if ( KBLAYOUT_MAX_NUM_MODIFIERS <= new_header.num_modifiers ) + return errno = EINVAL, false; + // TODO: Limit num_scancodes and max_actions. + size_t remaining_size = data_size - sizeof(new_header); + // TODO: Check for overflow. + size_t table_length = (size_t) (1 << new_header.num_modifiers) * + (size_t) new_header.num_scancodes; + size_t table_size = sizeof(struct kblayout_action) * table_length; + if ( remaining_size < table_size ) + return delete[] data, errno = EINVAL, false; + struct kblayout_action* new_actions = new struct kblayout_action[table_length]; + if ( !new_actions ) + return delete[] data, false; + uint8_t* new_keys_down = new uint8_t[new_header.num_scancodes]; + if ( !new_keys_down ) + return delete[] data, delete[] new_actions, false; + memcpy(new_actions, data + sizeof(new_header), table_size); + for ( size_t i = 0; i < table_length; i++ ) + new_actions[i] = le32toh(new_actions[i]); + memcpy(&header, &new_header, sizeof(header)); + delete[] actions; + actions = new_actions; + memset(new_keys_down, 0, sizeof(keys_down[0]) * new_header.num_scancodes); + delete[] keys_down; + keys_down = new_keys_down; + memset(&modifier_counts, 0, sizeof(modifier_counts)); + modifiers = 0; + delete[] saved_data; + saved_data = data; + saved_data_size = data_size; + loaded = true; + return true; +} + +bool KeyboardLayoutExecutor::Download(const uint8_t** data, size_t* data_size) +{ + if ( !saved_data ) + return errno = EINIT, false; + return *data = saved_data, *data_size = saved_data_size, true; +} + +uint32_t KeyboardLayoutExecutor::Translate(int kbkey) +{ + if ( !loaded ) + return 0; + unsigned int abskbkey = kbkey < 0 ? - (unsigned int) kbkey : kbkey; + if ( header.num_scancodes <= abskbkey ) + return 0; + + bool repeated_key = 0 <= kbkey && keys_down[abskbkey]; + keys_down[abskbkey] = 0 <= kbkey ? 1 : 0; + + size_t action_index = abskbkey * (1 << header.num_modifiers) + modifiers; + struct kblayout_action* action = &actions[action_index]; + if ( 0 < kbkey && action->type == KBLAYOUT_ACTION_TYPE_CODEPOINT ) + return action->codepoint; + // TODO: Properly implement dead keys! + if ( 0 < kbkey && action->type == KBLAYOUT_ACTION_TYPE_DEAD ) + return action->codepoint; + if ( repeated_key ) + return 0; + if ( action->type == KBLAYOUT_ACTION_TYPE_MODIFY && + action->codepoint < header.num_modifiers ) + { + if ( kbkey < 0 ) + { + if ( --modifier_counts[action->codepoint] == 0 ) + modifiers &= ~(1 << action->codepoint); + } + else + { + modifier_counts[action->codepoint]++; + modifiers |= 1 << action->codepoint; + } + return 0; + } + if ( 0 <= kbkey && + action->type == KBLAYOUT_ACTION_TYPE_TOGGLE && + action->codepoint < header.num_modifiers ) + { + if ( (modifier_counts[action->codepoint] = !modifier_counts[action->codepoint]) ) + modifiers |= 1 << action->codepoint; + else + modifiers &= ~(1 << action->codepoint); + return 0; + } + return 0; +} + +} // namespace Sortix diff --git a/kernel/kb/kblayout.h b/kernel/kb/kblayout.h new file mode 100644 index 00000000..acc75fe9 --- /dev/null +++ b/kernel/kb/kblayout.h @@ -0,0 +1,60 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2014. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + kb/kblayout.h + Engine that executes a keyboard layout program. + +*******************************************************************************/ + +#ifndef SORTIX_KB_KBLAYOUT_H +#define SORTIX_KB_KBLAYOUT_H + +#include +#include + +#include + +namespace Sortix { + +class KeyboardLayoutExecutor +{ +public: + KeyboardLayoutExecutor(); + ~KeyboardLayoutExecutor(); + +public: + bool Upload(const uint8_t* data, size_t data_size); + bool Download(const uint8_t** data, size_t* data_size); + uint32_t Translate(int kbkey); + +private: + struct kblayout header; + struct kblayout_action* actions; + uint8_t* keys_down; + uint32_t modifier_counts[KBLAYOUT_MAX_NUM_MODIFIERS]; + uint32_t modifiers; + bool loaded; + uint8_t* saved_data; + size_t saved_data_size; + +}; + +} // namespace Sortix + +#endif diff --git a/kernel/kb/layout/us.cpp b/kernel/kb/layout/us.cpp index 3f46391a..6598b910 100644 --- a/kernel/kb/layout/us.cpp +++ b/kernel/kb/layout/us.cpp @@ -42,62 +42,62 @@ const uint32_t LAYOUT_US[4UL*128UL] = { 0, 0, 0, 0, /* unused: kbkey 0 is invalid */ 0, 0, 0, 0, /* KBKEY_ESC */ - '1', '!', '1', '!', - '2', '@', '2', '@', - '3', '#', '3', '#', - '4', '$', '4', '$', - '5', '%', '5', '%', - '6', '^', '6', '^', - '7', '&', '7', '&', - '8', '*', '8', '*', - '9', '(', '9', '(', - '0', ')', '0', ')', - '-', '_', '-', '_', - '=', '+', '=', '+', - '\b', '\b', '\b', '\b', - '\t', '\t', '\t', '\t', - 'q', 'Q', 'Q', 'q', - 'w', 'W', 'W', 'w', - 'e', 'E', 'E', 'e', - 'r', 'R', 'R', 'r', - 't', 'T', 'T', 't', - 'y', 'Y', 'Y', 'y', - 'u', 'U', 'U', 'u', - 'i', 'I', 'I', 'i', - 'o', 'O', 'O', 'o', - 'p', 'P', 'P', 'p', - '[', '{', '[', '{', - ']', '}', ']', '}', - '\n', '\n', '\n', '\n', + L'1', L'!', L'1', L'!', + L'2', L'@', L'2', L'@', + L'3', L'#', L'3', L'#', + L'4', L'$', L'4', L'$', + L'5', L'%', L'5', L'%', + L'6', L'^', L'6', L'^', + L'7', L'&', L'7', L'&', + L'8', L'*', L'8', L'*', + L'9', L'(', L'9', L'(', + L'0', L')', L'0', L')', + L'-', L'_', L'-', L'_', + L'=', L'+', L'=', L'+', + L'\b', L'\b', L'\b', L'\b', + L'\t', L'\t', L'\t', L'\t', + L'q', L'Q', L'Q', L'q', + L'w', L'W', L'W', L'w', + L'e', L'E', L'E', L'e', + L'r', L'R', L'R', L'r', + L't', L'T', L'T', L't', + L'y', L'Y', L'Y', L'y', + L'u', L'U', L'U', L'u', + L'i', L'I', L'I', L'i', + L'o', L'O', L'O', L'o', + L'p', L'P', L'P', L'p', + L'[', L'{', L'[', L'{', + L']', L'}', L']', L'}', + L'\n', L'\n', L'\n', L'\n', 0, 0, 0, 0, /* KBKEY_LCTRL */ - 'a', 'A', 'A', 'a', - 's', 'S', 'S', 's', - 'd', 'D', 'D', 'd', - 'f', 'F', 'F', 'f', - 'g', 'G', 'G', 'g', - 'h', 'H', 'H', 'h', - 'j', 'J', 'J', 'j', - 'k', 'K', 'K', 'k', - 'l', 'L', 'L', 'l', - ';', ':', ';', ':', - '\'', '"', '\'', '"', - '`', '~', '`', '~', + L'a', L'A', L'A', L'a', + L's', L'S', L'S', L's', + L'd', L'D', L'D', L'd', + L'f', L'F', L'F', L'f', + L'g', L'G', L'G', L'g', + L'h', L'H', L'H', L'h', + L'j', L'J', L'J', L'j', + L'k', L'K', L'K', L'k', + L'l', L'L', L'L', L'l', + L';', L':', L';', L':', + L'\'', L'"', L'\'', L'"', + L'`', L'~', L'`', L'~', 0, 0, 0, 0, /* KBKEY_LSHIFT */ - '\\', '|', '\\', '|', - 'z', 'Z', 'Z', 'z', - 'x', 'X', 'X', 'x', - 'c', 'C', 'C', 'c', - 'v', 'V', 'V', 'v', - 'b', 'B', 'B', 'b', - 'n', 'N', 'N', 'n', - 'm', 'M', 'M', 'm', - ',', '<', ',', '<', - '.', '>', '.', '>', - '/', '?', '/', '?', + L'\\', L'|', L'\\', L'|', + L'z', L'Z', L'Z', L'z', + L'x', L'X', L'X', L'x', + L'c', L'C', L'C', L'c', + L'v', L'V', L'V', L'v', + L'b', L'B', L'B', L'b', + L'n', L'N', L'N', L'n', + L'm', L'M', L'M', L'm', + L',', L'<', L',', L'<', + L'.', L'>', L'.', L'>', + L'/', L'?', L'/', L'?', 0, 0, 0, 0, /* KBKEY_RSHIFT */ - '*', '*', '*', '*', + L'*', L'*', L'*', L'*', 0, 0, 0, 0, /* KBKEY_LALT */ - ' ', ' ', ' ', ' ', + L' ', L' ', L' ', L' ', 0, 0, 0, 0, /* KBKEY_CAPSLOCK */ 0, 0, 0, 0, /* KBKEY_F1 */ 0, 0, 0, 0, /* KBKEY_F2 */ @@ -114,11 +114,11 @@ const uint32_t LAYOUT_US[4UL*128UL] = 0, 0, 0, 0, /* KBKEY_KPAD7 */ 0, 0, 0, 0, /* KBKEY_KPAD8 */ 0, 0, 0, 0, /* KBKEY_KPAD9 */ - '-', '-', '-', '-', + L'-', L'-', L'-', L'-', 0, 0, 0, 0, /* KBKEY_KPAD4 */ 0, 0, 0, 0, /* KBKEY_KPAD5 */ 0, 0, 0, 0, /* KBKEY_KPAD6 */ - '+', '+', '+', '+', + L'+', L'+', L'+', L'+', /* Nothing printable after this point */ }; diff --git a/kernel/kb/layout/us.h b/kernel/kb/layout/us.h index e22dd1b3..0aaf6b00 100644 --- a/kernel/kb/layout/us.h +++ b/kernel/kb/layout/us.h @@ -27,18 +27,14 @@ #include -#include - namespace Sortix { -class KBLayoutUS : public KeyboardLayout +class KBLayoutUS { public: KBLayoutUS(); - virtual ~KBLayoutUS(); - virtual uint32_t Translate(int kbkey); - -public: + ~KBLayoutUS(); + uint32_t Translate(int kbkey); bool ProcessModifier(int kbkey, int modkey, unsigned flag); private: diff --git a/kernel/kernel.cpp b/kernel/kernel.cpp index f99acc80..0175eb7d 100644 --- a/kernel/kernel.cpp +++ b/kernel/kernel.cpp @@ -81,7 +81,8 @@ #include "fs/zero.h" #include "gpu/bga/bga.h" #include "initrd.h" -#include "kb/layout/us.h" +#include "kb/default-kblayout.h" +#include "kb/kblayout.h" #include "kb/ps2.h" #include "logterminal.h" #include "multiboot.h" @@ -554,9 +555,11 @@ static void BootThread(void* /*user*/) Keyboard* keyboard = new PS2Keyboard(0x60, Interrupt::IRQ1); if ( !keyboard ) Panic("Could not allocate PS2 Keyboard driver"); - KeyboardLayout* kblayout = new KBLayoutUS; + KeyboardLayoutExecutor* kblayout = new KeyboardLayoutExecutor; if ( !kblayout ) - Panic("Could not allocate keyboard layout driver"); + Panic("Could not allocate keyboard layout executor"); + if ( !kblayout->Upload(default_kblayout, sizeof(default_kblayout)) ) + Panic("Could not load the default keyboard layout into the executor"); // Register the kernel terminal as /dev/tty. Ref tty(new LogTerminal(slashdev->dev, 0666, 0, 0, keyboard, kblayout)); diff --git a/kernel/logterminal.cpp b/kernel/logterminal.cpp index 279f75df..01c2f3d2 100644 --- a/kernel/logterminal.cpp +++ b/kernel/logterminal.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2012, 2013. + Copyright(C) Jonas 'Sortie' Termansen 2012, 2013, 2014. This file is part of Sortix. @@ -63,7 +63,7 @@ const unsigned SUPPORTED_MODES = TERMMODE_KBKEY | TERMMODE_NONBLOCK; LogTerminal::LogTerminal(dev_t dev, mode_t mode, uid_t owner, gid_t group, - Keyboard* keyboard, KeyboardLayout* kblayout) + Keyboard* keyboard, KeyboardLayoutExecutor* kblayout) { inode_type = INODE_TYPE_TTY; this->dev = dev; @@ -448,12 +448,26 @@ ssize_t LogTerminal::tcgetblob(ioctx_t* ctx, const char* name, void* buffer, siz { if ( !name ) { - char index[] = ""; - if ( buffer && count < sizeof(index) ) + static const char index[] = "kblayout\0"; + size_t index_size = sizeof(index) - 1; + if ( buffer && count < index_size ) return errno = ERANGE, -1; - if ( buffer && !ctx->copy_to_dest(buffer, &index, sizeof(index)) ) + if ( buffer && !ctx->copy_to_dest(buffer, &index, index_size) ) return -1; - return (ssize_t) sizeof(index); + return (ssize_t) index_size; + } + else if ( !strcmp(name, "kblayout") ) + { + ScopedLockSignal lock(&termlock); + const uint8_t* data; + size_t size; + if ( !kblayout->Download(&data, &size) ) + return -1; + if ( buffer && count < size ) + return errno = ERANGE, -1; + if ( buffer && !ctx->copy_to_dest(buffer, data, size) ) + return -1; + return (ssize_t) size; } else return errno = ENOENT, -1; @@ -461,11 +475,21 @@ ssize_t LogTerminal::tcgetblob(ioctx_t* ctx, const char* name, void* buffer, siz ssize_t LogTerminal::tcsetblob(ioctx_t* ctx, const char* name, const void* buffer, size_t count) { - (void) ctx; - (void) buffer; - (void) count; if ( !name ) return errno = EPERM, -1; + else if ( !strcmp(name, "kblayout") ) + { + uint8_t* data = new uint8_t[count]; + if ( !data ) + return -1; + if ( !ctx->copy_from_src(data, buffer, count) ) + return -1; + ScopedLockSignal lock(&termlock); + if ( !kblayout->Upload(data, count) ) + return -1; + delete[] data; + return (ssize_t) count; + } else return errno = ENOENT, -1; } diff --git a/kernel/logterminal.h b/kernel/logterminal.h index 9355e3ab..2b128e2d 100644 --- a/kernel/logterminal.h +++ b/kernel/logterminal.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2012. + Copyright(C) Jonas 'Sortie' Termansen 2012, 2014. This file is part of Sortix. @@ -29,6 +29,9 @@ #include #include #include + +#include "kb/kblayout.h" + #include "linebuffer.h" namespace Sortix { @@ -37,7 +40,7 @@ class LogTerminal : public AbstractInode, public KeyboardOwner { public: LogTerminal(dev_t dev, mode_t mode, uid_t owner, gid_t group, - Keyboard* keyboard, KeyboardLayout* kblayout); + Keyboard* keyboard, KeyboardLayoutExecutor* kblayout); virtual ~LogTerminal(); public: @@ -71,7 +74,7 @@ private: size_t numwaiting; size_t numeofs; Keyboard* keyboard; - KeyboardLayout* kblayout; + KeyboardLayoutExecutor* kblayout; LineBuffer linebuffer; size_t partiallywritten; unsigned termmode;