OSDev.org

The Place to Start for Operating System Developers
It is currently Sun Sep 25, 2022 6:33 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 19 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: How into implementing a proper keyboard driver?
PostPosted: Fri Aug 12, 2022 5:47 pm 
Offline
Member
Member
User avatar

Joined: Wed Feb 19, 2020 1:08 pm
Posts: 199
Location: Italy
I do already have a minimal keyboard driver: it catches interrupts, translates raw scancodes to a fixed table of keys (thus loading a keyboard layout that maps scancodes to keys), and allows to either check if a specific key is currently pressed or to poll from a queue of keypresses. That's all good but I'm now facing the problem of the actual keyboard layout: knowing that 0x29 maps to KEY_BACKTICK is not enough, it is also needed to know that KEY_BACKTICK + KEY_SHIFT = VK_TILDE. Now here comes the problem. If I implement this logic at the keyboard driver level I will not be able to catch a CTRL+C keypress because according to the currently loaded layout KEY_C means nothing together with KEY_CTRL. I thought about making the keyboard driver output just basic keys so making the final application (like a tty driver) responsible for translating things to a virtual layout; this will allow to catch raw key combinations and to translate to some printable char if none is matched. This is a viable solution but to reduce the boilerplate code that would form on each application I should create a library that exposes a function like key_to_string(char* buff, uint16 key, uint8 mods). This function would rely on a layout loaded prior to any call, but this layout would be different from the one loaded in the original keyboard driver, so in the end, I need to load two layouts to use the keyboard.
This is really clunky design I come up in a long night and I think something really better can be done. Do you know any technique I can use?

_________________
Greetings, Bonfra.


Top
 Profile  
 
 Post subject: Re: How into implementing a proper keyboard driver?
PostPosted: Sat Aug 13, 2022 3:11 am 
Offline
Member
Member

Joined: Wed Oct 01, 2008 1:55 pm
Posts: 2870
I think the trick is that there needs to be two different interfaces to the keyboard. One is the "raw" interface where you get presses & releases + which control keys are pressed. This interface is used to implement GUIs. The other is to simulate somethig that works like a command line where you only get translated keys. This interface is used to implement tty or command line. Another possibility is to put a translation layer between the raw interface and "tty" in the kernel or in a user level library.


Top
 Profile  
 
 Post subject: Re: How into implementing a proper keyboard driver?
PostPosted: Sat Aug 13, 2022 4:40 am 
Offline
Member
Member
User avatar

Joined: Wed Feb 19, 2020 1:08 pm
Posts: 199
Location: Italy
So it appears that the existence of two distinct keyboard interfaces, hence two places to load a layout, is needed to have a functional system. Whether the second system is embedded in the final piece of software that needs cooked keys (e.g. text editor) or is provided as an API by some library. In general, the keyboard driver (and so the kernel) needs to expose just raw keys. Idk it seems like some bug-abusable design by the end user to need to load two different layouts; yes a layout file could contain both layouts but it still looks like an overcomplicated solution.

_________________
Greetings, Bonfra.


Top
 Profile  
 
 Post subject: Re: How into implementing a proper keyboard driver?
PostPosted: Sat Aug 13, 2022 8:22 am 
Offline
Member
Member
User avatar

Joined: Fri Jun 11, 2021 6:02 am
Posts: 77
Location: Belgium
I've also been wondering how to best deal with keyboards and the plethora of scancodes.
This problem can IMO best be split up in multiple subproblems:

  • Translate arbitrary scancodes to OS-wide consistent "raw" keycodes.
  • Apply modifiers to raw keycodes.
  • Convert modified keycodes to a stream of UTF-8 characters

Keycode & event format

I first defined a format for the keycodes:

  • To ensure I cover all possible characters I use the Unicode character set. This required at least 21 bits (0 to 0x10ffff)
  • For keys that can't be mapped to a Unicode character or otherwise need to be distinguishable like Shift, keypad numbers ... I use the 0x110000 to 0x1fffff space that is unused by Unicode.

To distinguish between a press and release event I added another bit.
This allows encoding each keypress as 22 bits. I rounded this up to 32 bits.

Scancode to raw keycode

We then need to map scancodes to keycodes.
For PS/2 I first translate the scancodes to USB HID codes so I don't need to make two keymaps for each keyboard.
For translating the HID (or any) codes I use a configuration file:

Code:
; Based on Logitech K120, AZERTY layout

(raw
   ; ordered row-wise, top to bottom, left to right

   (² 35)
   (& 1e)
   (é 1f)
   ('"' 20)
   ("'" 21)
   ("(" 22)
   (§ 23)
   (è 24)
   (! 25)
   (ç 26)
   (à 27)
   (")" 2d)
   (- 2e)
   (backspace 2a)

   (tab 2b)
   (a 14)
   (z 1a)
   (e 08)
   (r 15)
   (t 17)
   (y 1c)
   ...


Raw keycode to modified keycode

For modified keycodes I add a section for each modifier combination and a mapping from raw keycodes to modified keycodes:

Code:
(altgr
   (¬ ²)
   (| &)
   (@ é)
   ("#" '"')
   (¼ "'")
   (½ "(")
   (^ §)
   ({ è)
   ...

(caps
   (³ ²)
   (1 &)
   (2 é)
   (3 '"')
   (4 "'")
   (5 "(")
   (6 §)
   (7 è)
   (8 !)
   ...

(altgr+caps
   (¡ &)
   (⅛ é)
   (£ '"')
   ($ "'")
   (⅜ "(")
   (⅝ §)
   ...
   (˙ =))


(Note that scancodes are supposed to map one-to-one to a raw keycode, so using raw keycodes here is fine).

I hardcoded some translations such as a -> A since I don't expect any sane keyboard to put those on different keys.

Modified keycodes to UTF-8 stream

With the modified keycodes this is trivial to do: check for press events with modified keycodes, see if it is Unicode and if yes, just write it out. Otherwise ignore.

_________________
My OS is Norost B (website, Github, sourcehut)


Top
 Profile  
 
 Post subject: Re: How into implementing a proper keyboard driver?
PostPosted: Sat Aug 13, 2022 1:22 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 4141
Demindiro wrote:
I hardcoded some translations such as a -> A since I don't expect any sane keyboard to put those on different keys.

I guess Turkish keyboard layouts are not sane. (ı -> I and i -> İ)


Top
 Profile  
 
 Post subject: Re: How into implementing a proper keyboard driver?
PostPosted: Sat Aug 13, 2022 1:30 pm 
Offline
Member
Member
User avatar

Joined: Fri Jun 11, 2021 6:02 am
Posts: 77
Location: Belgium
Octocontrabass wrote:
Demindiro wrote:
I hardcoded some translations such as a -> A since I don't expect any sane keyboard to put those on different keys.

I guess Turkish keyboard layouts are not sane. (ı -> I and i -> İ)


There goes another one of my assumptions :P

I guess I'll keep the hardcoded translations but allow them to be overridden.

_________________
My OS is Norost B (website, Github, sourcehut)


Top
 Profile  
 
 Post subject: Re: How into implementing a proper keyboard driver?
PostPosted: Sat Aug 13, 2022 2:14 pm 
Offline
Member
Member

Joined: Wed Aug 30, 2017 8:24 am
Posts: 1336
Keyboard layouts are a difficult problem, especially if you only know a few, so I would simply try to use the Xkb database. The format there is something like
Code:
keycode 29 = z Z z Z leftarrow yen leftarrow

The first is without modifiers, the second with shift, the third and fourth I don't know (I cannot find any mapping where they don't equal the first and second), the fifth is with AltGr, the sixth with AltGr + Shift, the seventh and eighth once more I don't know. Maybe you need keys I don't have.

Anyway, this format is reasonably good at turning keycodes into keycaps, so you can probably rely on it. Although there is an enormous number of keycaps.

_________________
Carpe diem!


Top
 Profile  
 
 Post subject: Re: How into implementing a proper keyboard driver?
PostPosted: Mon Aug 15, 2022 2:09 am 
Offline
Member
Member

Joined: Mon Dec 07, 2020 8:09 am
Posts: 212
Bonfra wrote:
looks like an overcomplicated solution.


This problem itself is very complicated.

Bonfra wrote:
I will not be able to catch a CTRL+C keypress because according to the currently loaded layout KEY_C means nothing together with KEY_CTRL.


This part of the problem is not complicated, there're only a few modifier keys. CTRL + C means something, but KEY_A + KEY_C doesn't.

I'm using a method similar to what has been mentioned a few times, a hand-coded and hard-coded look up table indexed by scan code.

Mine has 4 main cols: normal (1 or a) shift (! or A) caps (1 or A) ctrl (\x82 or \x1), and other aux cols to help with processing. You can customize it in ways that suite your needs and your processing code.

When you get a new keyboard (layout) you'd just need to create another table for it, when you need to support a new modifier key or some other properties for special processing just add a column to it.

Bonfra wrote:
piece of software that needs cooked keys (e.g. text editor) or is provided as an API by some library.


IMO this is the diffcult part. If you are interested in porting POSIX apps, you should take a look at the termios interface and think about how would you provide it.

But it also has far reaching implications (tied to job control, as in, how does Ctrl+C propagate within your system), so if you don't want to write another unix clone, maybe try to design your own library and port some text editors before looking too closely at it?


Top
 Profile  
 
 Post subject: Re: How into implementing a proper keyboard driver?
PostPosted: Mon Aug 15, 2022 3:59 am 
Offline
Member
Member
User avatar

Joined: Wed Feb 19, 2020 1:08 pm
Posts: 199
Location: Italy
Yea I underestimated the problem, now I full-on agree with you: this is hard. Thanks a lot for the suggestion, I'm going to strictly follow the lookup table solution as it seems the more portable one for different (even custom layouts). I'm thinking about something like this
Code:
typedef struct kb_layout
{
    uint16_t physical[UINT16_MAX];
    struct
    {
        uint16_t vk_normal;
        uint16_t vk_shift;
        uint16_t vk_altgr;
        uint16_t vk_shift_altgr;
    } virtual[UINT16_MAX];
} kb_layout_t;

so I can implement a layout like this
Code:
const kb_layout_t kb_layout_en_us = {
    .physical = {
        [0x00 ... UINT16_MAX - 1] = 0xFFFF,

        [0x29] = KEY_BACKTICK,
        [0x02] = KEY_1,
    }
    .virtual = {
        [KEY_BACKTICK] = { .vk_normal = VK_BACKTICK, .vk_shift = VK_TILDE, .vk_altgr = VK_NONE, .vk_shift_altgr = VK_NONE },
        [KEY_1] = { .vk_normal = VK_1, .vk_shift = VK_EXCLAMATION_MARK, .vk_altgr = VK_NONE, .vk_shift_altgr = VK_NONE },
    }
}

And then add a stand-alone method that takes a physical key and outputs a virtual key based on the current modifiers.
Thanks again for all your help :)

_________________
Greetings, Bonfra.


Top
 Profile  
 
 Post subject: Re: How into implementing a proper keyboard driver?
PostPosted: Mon Aug 15, 2022 10:53 am 
Offline
Member
Member
User avatar

Joined: Mon May 22, 2017 5:56 am
Posts: 672
Location: Oscillating between two different potentials
I've concluded I can't do better than X11. For each keypress, it can supply an untranslated keycode, whichever modifiers are pressed/active, and the translated character if there is one.

nullplan wrote:
Keyboard layouts are a difficult problem, especially if you only know a few, so I would simply try to use the Xkb database. The format there is something like
Code:
keycode 29 = z Z z Z leftarrow yen leftarrow

If I remember right, one of these columns is caps-lock; it's treated as a separate modifier. This allows caps-lock to only affect the alphabetical keys. It also allows for all sorts of language-specific peculiarities, such as German having no capital letter eszett.

_________________
[Kaph — A set of system ideals] [Pichiciego — A slightly quirky bootable Forth]
"May wisdom, fun, and the greater good shine forth in all your work." — Leo Brodie


Top
 Profile  
 
 Post subject: Re: How into implementing a proper keyboard driver?
PostPosted: Mon Sep 05, 2022 1:16 pm 
Offline
Member
Member
User avatar

Joined: Fri Jun 11, 2021 6:02 am
Posts: 77
Location: Belgium
After wondering how to best support keyboards with analog input, where the keyboard can tell how deep a user is pressing a key, I figured I should use the remaining 11 bits to send a signed integer instead of just a single bit indicating whether a key is pressed or not.

Incidentally, this makes it practical to treat the mouse as a very special kind of keyboard: when the mouse moves, send a Mouse{X,Y} event (where Mouse{X,Y} is a keycode) with as press level how much the mouse moved, then immediately send another event that sends the same event but with 0 press level.

This should also make it easier to support joy sticks, since you can send the same Mouse{X,Y} event to move the cursor without adding special support.

_________________
My OS is Norost B (website, Github, sourcehut)


Top
 Profile  
 
 Post subject: Re: How into implementing a proper keyboard driver?
PostPosted: Tue Sep 06, 2022 1:46 am 
Offline

Joined: Fri Nov 26, 2021 11:08 am
Posts: 6
eekee wrote:
such as German having no capital letter eszett.


This changed a few years ago, though (2017). But of course it does not have a glyph in the VGA BIOS font.

https://de.wikipedia.org/wiki/Gro%C3%9Fes_%C3%9F
https://en.wikipedia.org/wiki/%C3%9F#De ... pital_form


Top
 Profile  
 
 Post subject: Re: How into implementing a proper keyboard driver?
PostPosted: Tue Sep 06, 2022 4:56 pm 
Offline
Member
Member
User avatar

Joined: Mon May 22, 2017 5:56 am
Posts: 672
Location: Oscillating between two different potentials
Demindiro wrote:
After wondering how to best support keyboards with analog input, where the keyboard can tell how deep a user is pressing a key, I figured I should use the remaining 11 bits to send a signed integer instead of just a single bit indicating whether a key is pressed or not.

Interesting keyboards!

Demindiro wrote:
Incidentally, this makes it practical to treat the mouse as a very special kind of keyboard: when the mouse moves, send a Mouse{X,Y} event (where Mouse{X,Y} is a keycode) with as press level how much the mouse moved, then immediately send another event that sends the same event but with 0 press level.

This should also make it easier to support joy sticks, since you can send the same Mouse{X,Y} event to move the cursor without adding special support.

I've always been in favour of treating the mouse buttons as keys, but this is new. :) Since keys are single-axis, am I right in thinking the X and Y axes would be sent as separate keycodes? I think that would work all right.

What would you do to report absolute mouse position? Oh I know: you could use another pair of keycodes if the API sends axis data as 32-bit integers. :)

_________________
[Kaph — A set of system ideals] [Pichiciego — A slightly quirky bootable Forth]
"May wisdom, fun, and the greater good shine forth in all your work." — Leo Brodie


Top
 Profile  
 
 Post subject: Re: How into implementing a proper keyboard driver?
PostPosted: Wed Sep 07, 2022 8:06 am 
Offline
Member
Member
User avatar

Joined: Fri Jun 11, 2021 6:02 am
Posts: 77
Location: Belgium
eekee wrote:
I've always been in favour of treating the mouse buttons as keys, but this is new. :) Since keys are single-axis, am I right in thinking the X and Y axes would be sent as separate keycodes? I think that would work all right.


It does indeed work fine. Technically it's possible for a mouse event to be "torn" (i.e. X axis is processed in one frame and Y axis in the next) but this shouldn't be a problem unless a massive amount of keycodes is sent at once.

eekee wrote:
What would you do to report absolute mouse position? Oh I know: you could use another pair of keycodes if the API sends axis data as 32-bit integers. :)


I've considered it but decided against it because a device that reports an absolute position is usually a tablet or a touchscreen, which can register multiple touches and also measure the pressure of each touch. While it would be possible to hack something together with keycodes I think it'll be much cleaner to have a separate API for such devices.

_________________
My OS is Norost B (website, Github, sourcehut)


Top
 Profile  
 
 Post subject: Re: How into implementing a proper keyboard driver?
PostPosted: Thu Sep 08, 2022 8:57 am 
Offline
Member
Member

Joined: Sat Nov 21, 2009 5:11 pm
Posts: 790
The way I did it, I have applications receiving both key press/release events (which are the same regardless of layout) and character input as separate message types. Keyboard drivers know nothing of characters or keyboard layouts. Instead, a key mapper is automatically created for each keyboard and invoked by the UI to generate character messages. Now, I hope that there aren't languages which put things like backspace, arrows keys and the like at different positions or I'm going to have a headache.

Mice and touchpads are registered with the UI in the same way as keyboards, except that they don't have an associated key mapper. They report input with a separate set of codes (movement, scrolling, button states for up to 5 buttons, and absolute position). X and Y axes are reported together in a single input packet, as they of course should be.

For any other type of input (key pressure, joy sticks, etc), applications should probably connect to the device using a separate API capable of receiving any kind of input report and describing what is in them. Joy sticks certainly shouldn't cause cursor movement.

Quote:
Incidentally, this makes it practical to treat the mouse as a very special kind of keyboard: when the mouse moves, send a Mouse{X,Y} event (where Mouse{X,Y} is a keycode) with as press level how much the mouse moved, then immediately send another event that sends the same event but with 0 press level.

This should also make it easier to support joy sticks, since you can send the same Mouse{X,Y} event to move the cursor without adding special support.

But why? What benefits do you get from treating a mouse axis like key pressure? When you see yourself forcing some thing to be some entirely different kind of thing it is usually a sign of something needing to be reworked, especially when you end up with things like separate events for X and Y axis movements. Mouse inputs aren't keys, a mouse is used to point at and interact with things on the screen. Joy sticks again have an entirely different purpose, you don't point at things with a joy stick. Joy sticks are usually found on game pads where there are often several of them, and are used for specialized applications such as games. Axis values mean nothing in themselves without knowing what they represent.

Quote:
I've concluded I can't do better than X11. For each keypress, it can supply an untranslated keycode, whichever modifiers are pressed/active, and the translated character if there is one.

With the traditional processing of PC keyboard input, a single keypress can generate multiple characters if an accent key is pressed followed by another key for which no corresponding accented character exists. For example, ~ followed by G outputs ~g. I recommend keeping to this tradition. Linux forces me to press space in between, or the ~ just doesn't register, which is always a frustration when being used to Windows.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 19 posts ]  Go to page 1, 2  Next

All times are UTC - 6 hours


Who is online

Users browsing this forum: Google [Bot], Majestic-12 [Bot], PetalBot [Bot], SemrushBot [Bot] and 9 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group