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;