Vystem 0.2

This commit is contained in:
2026-05-27 19:34:54 +02:00
parent a43c08b893
commit d238606b75
372 changed files with 51320 additions and 83217 deletions

View File

@@ -0,0 +1,36 @@
# PS2 keyboard
The PS2 keyboard driver is defined in `shelter/lib/include/devs/input/ps2.h` and implemented in `shelter/lib/src/devs/input/ps2.c`. This documentation only describes the public API, the one that is safe to access from external subsystems.
## Driver initialization
The PS2 is initialized using `sh_ps2_driver_init()`. The pre-requirement for this function include the keyboard input subsystem being initialized. Here is the exact procedure:
1) Disable interrupts for safety
2) Flush output buffer
3) Debug log status and config byte initial value
4) Disable keyboard and mouse PS2 ports
5) Reflush output buffer for safety
6) Disable both ports IRQs
7) Perform the PS2 controller self test
8) Enable port 1
9) Enable scancodes translation
10) Initialize driver internal state
11) Enable IRQ for port 1
12) Register interrupt handler for legacy IRQ1
13) Enable interrupts
14) Disable scanning
15) Reset leds state
16) Identify keyboard
17) Register PS2 keyboard to the keyboard input subsystem
18) Enable scanning
For the moments, commands responses handling and scancodes parsing is working but there are a few limitations:
- since we only parsed a few ACPI tables, we are unable to detect with certainty the presence of a PS2 controller. So the system consider that the PS2 controller is available and working and the keyboard device registered to the keyboard input subsystem will always be marked as present
- any error in the driver initialization will be interpreted as a failure of the boot process
- for the moment, we are unable to defer the leds state update when a scancode change the NumLock/CapsLock state, so these leds won't update
## API
A few additionnal functions are provided:
- `sh_ps2_disable_scanning()`: disable PS2 keyboard scanning
- `sh_ps2_enable_scanning()`: enable PS2 keyboard scanning

43
docs/shelter/devs/acpi.md Normal file
View File

@@ -0,0 +1,43 @@
# ACPI Parsing
Shelter only support the ACPI v2 and newer ACPI specifications. That mean the XSDT pointer must exists.
## Root structures parsing
The root structures parsing is defined in `shelter/lib/include/devs/acpi.h` and implemented in `shelter/lib/src/devs/acpi.c`.
The `sh_acpi_parse_rsdp()` function takes the RSDP and completes a `sh_apci_ACPI_POINTERS_ROOT` structure that look like this:
``` C
typedef struct {
sh_uint64 rsdt;
sh_uint64 xsdt;
} sh_apci_ACPI_POINTERS_ROOT;
```
Then, the `sh_acpi_parse_xsdt()` function takes the XSDT pointer and parses the XSDT header. It ensures that the structure isn't corrupted, and map all the entries. At the end, it completes this structure:
``` C
typedef struct {
sh_uint64 *entries;
sh_uint64 entry_count;
} sh_acpi_ACPI_XSDT_BODY;
```
Finally, the `sh_acpi_parse_tables()` takes the `sh_acpi_ACPI_XSDT_BODY` structure and parses all the entries. It ensures that at least the first pages of each table is mapped. All the tables pointers are sorted in a structure that look like this:
``` C
typedef struct {
sh_uint64 madt_physical_address;
} sh_acpi_ACPI_TABLES;
```
## Tables parsing
The parsing of ACPI tables is delegated to several functions, one for each tables. Here are the tables that are currently supported:
- MADT
All parsing functions are defined in the files inside `shelter/lib/include/devs/acpi_tables` and in the files inside `shelter/lib/src/devs/acpi_tables`.
### MADT parsing
The MADT is parsed using the `sh_acpi_madt_parse(sh_uint64 madt_ptr)`, defined in `shelter/lib/include/devs/acpi_tables/madt.h` and implemented in `shelter/lib/src/devs/acpi_tables/madt.c`.
This function logs all the entries and register all LAPIC devices, IOAPIC devices and ISOs.

142
docs/shelter/devs/devs.md Normal file
View File

@@ -0,0 +1,142 @@
# DevS querying
## Introduction
The Device System provides a central way to query various kernel subsystems. It is defined in `shelter/lib/include/devs/devs.h` and implemented in `shelter/lib/src/devs/devs.c`.
## Entry point
All DevS related API call goes through a standard entry point:
``` C
sh_devs_query(char *path,sh_devs_RESULT *result)
```
The result is stored inside `result`, which is a structure like this:
``` C
typedef struct {
enum sh_devs_RESULT_TYPE type;
sh_uint64 value;
} sh_devs_RESULT;
```
There are severals returned types:
- `SH_DEVS_VALUE`: a 64 bits value
- `SH_DEVS_BOOL`: a boolean stored inside `value` by casting the `sh_bool` to `sh_uint64`
- `SH_DEVS_LAPIC`: `value` should be casted to `sh_lapic_DEVICE*`, which points directly to a LAPIC device structure
- `SH_DEVS_IOAPIC`: `value` should be casted to `sh_ioapic_DEVICE*`, which points directly to a IOAPIC device structure
Each endpoint has a specific syntax, but here are the common syntax elements:
- each path starts with the `$` sign
- each subsystem has its own prefix
Unless you are querying for a pointer (in this case, you can modify the value inside the pointed structure), you can't write anything in the DevS endpoints.
## Endpoint categories
All the kernel subsystems that expose datas through the DevS provide a function to parse the rest of the path without the subsystem prefix once `sh_devs_query()` dispatched the query.
These functions aren't documented in the documentation of these subsystems and shouldn't be called directly.
### LAPIC endpoints
Function:
``` C
sh_lapic_devs_query(char *sub_path,sh_devs_RESULT *result)
```
Prefix: `$apic/lapic`
Endpoints:
Path with syntax | Returned type | Description
-----------------|---------------|------------
`$apic/lapic/by-apic-id/<id>` | `SH_DEVS_LAPIC` | Return the struct of the LAPIC device correspoding to the provided LAPIC id. **Can return `SH_NULLPTR` with a success due to sparses id**
`$apic/lapic/by-acpi-id/<id>` | `SH_DEVS_LAPIC` | Return the struct of the LAPIC device correspoding to the provided ACPI processor id. **Can return `SH_NULLPTR` with a success due to sparses id**
`$apic/lapic/count` | `SH_DEVS_VALUE` | Return the number of LAPIC devices registered
`$apic/lapic/timer-frequency` | `SH_DEVS_VALUE` | Return the estimated frequency of the LAPIC timer
`$apic/lapic/max-apic-id` | `SH_DEVS_VALUE` | Return the biggest LAPIC id among all registered LAPIC devices
`$apic/lapic/max-acpi-id` | `SH_DEVS_VALUE` | Return the biggest ACPI processor id among all registered LAPIC devices
`$apic/lapic/base-address` | `SH_DEVS_VALUE` | Return the physical address of the memory mapped pages for LAPICs
`$apic/lapic/this-cpu-lapic` | `SH_DEVS_LAPIC` | Return the struct of the LAPIC device corresponding to this CPU
### IOAPIC endpoints
Function:
``` C
sh_ioapic_devs_query(char *sub_path,sh_devs_RESULT *result)
```
Prefix: `$apic/ioapic`
Endpoints:
Path with syntax | Returned type | Description
-----------------|---------------|------------
`$apic/ioapic/by-ioapic-id/<id>` | `SH_DEVS_ioAPIC` | Return the struct of the IOAPIC device correspoding to the provided IOAPIC id. **Can return `SH_NULLPTR` with a success due to sparses id**
`$apic/lapic/by-gsi/<gsi>` | `SH_DEVS_IOAPIC` | Return the struct of the IOAPIC device that manages the corresponding GSI. **Can return `SH_NULLPTR` with a success due to sparses GSI**
`$apic/lapic/count` | `SH_DEVS_VALUE` | Return the number of IOAPIC devices registered
`$apic/lapic/max-ioapic-id` | `SH_DEVS_VALUE` | Return the biggest IOAPIC id among all registered IOAPIC devices
### TSC endpoints
Function:
``` C
sh_tsc_devs_query(char *sub_path,sh_devs_RESULT *result)
```
Prefix: `$tsc`
Endpoints:
Path with syntax | Returned type | Description
-----------------|---------------|------------
`$tsc/kernel-init-tsc` | `SH_DEVS_VALUE` | Return the TSC value saved at the start of the kernel C entry point, before AP bootstrap
`$tsc/cpu-freq` | `SH_DEVS_VALUE` | Return the CPU frequency, either estimated or provided by CPU ID
`$tsc/is-cpu-freq-provided` | `SH_DEVS_BOOL` | Return `SH_TRUE` if CPU frequency is provided by CPU ID, `SH_FALSE` if CPU frequency is estimated
### Kernel config endpoints
Function:
``` C
sh_conf_devs_query(char *sub_path,sh_devs_RESULT *result)
```
Prefix: `$kernel/conf`
Endpoints:
Path with syntax | Returned type | Description
-----------------|---------------|------------
`$kernel/conf/log-level` | `SH_DEVS_VALUE` | Return the log level of the kernel
`$kernel/conf/test-benchmark` | `SH_DEVS_BOOL` | Return the test-benchmark state, `SH_TRUE` if enabled
`$kernel/conf/bench-iterations` | `SH_DEVS_VALUE` | Return the amount of iterations used for benchmarking
`$kernel/conf/disable-serial-port` | `SH_DEVS_BOOL` | Return `SH_TRUE` if serial port is disabled, `SH_FALSE` otherwise
`$kernel/conf/kbd-events-queue-capacity` | `SH_DEVS_VALUE` | Return the amount of events a keyboard events queue can store
### Memory subsystem endpoints
Function:
``` C
sh_memory_devs_query(char *sub_path,sh_devs_RESULT *result)
```
Prefix: `$memory`
Endpoints:
Path with syntax | Returned type | Description
-----------------|---------------|------------
`$memory/free-pages` | `SH_DEVS_VALUE` | Return the amount of free pages in the Pez physical plane
`$memory/used-pages` | `SH_DEVS_VALUE` | Return the amount of allocated pages in the Pez physical plane
`$memory/total-pages` | `SH_DEVS_VALUE` | Return the amount of physical pages detected when parsing the memory map
`$memory/installed-pages` | `SH_DEVS_VALUE` | Return the amount of pages detected usable when parsing the memory map. Can be slightly smaller than total pages
### SMP endpoints
Function: integrated to `sh_devs_query()`
Prefix: `$smp`
Path with syntax | Returned type | Description
-----------------|---------------|------------
`$smp/cpu-count` | `SH_DEVS_VALUE` | Return the number of processors

View File

@@ -0,0 +1,18 @@
# Device drivers
## Overview
The current device drivers are scattered through the `shelter/lib/include/devs` folder. There is two kinds of devices drivers:
- integrated: these one are used as a core part of the kernel, and aren't exposed through subsystem abstraction
- abstracted: these one are abstracted using a specific subsystem depending on the device type that provides a standard API both for drivers and consumers
## Integrated drivers
For the moment, integrated drivers are implemented for the following devices:
- [LAPIC devices](integrated/lapic.md)
- [IOAPIC devices](integrated/ioapic.md)
## Abstracted drivers
For the moment, abstracted drivers are implemented for the following devices:
- [PS2 keyboard](abstracted/ps2.md)

View File

@@ -0,0 +1,13 @@
# Device System
## Introduction
The Device System (also called DevS) is the entire subsystem managing everything related to ACPI parsing and devices abstractions.
## Overview
The DevS is splitted in several subparts:
- [ACPI parsing](acpi.md)
- [Device drivers](drivers.md)
- [Keyboard input subsystem](kbdinput.md)
- [DevS querying](devs.md)

View File

@@ -0,0 +1,35 @@
# IOAPIC devices
This driver is defined inside `shelter/lib/include/devs/apic/ioapic.h` and implemented inside `shelter/lib/src/devs/apic/ioapic.c`.
## Driver initialization
During the MADT parsing, all IOAPIC entries are counted. Then, a call to `sh_ioapic_init_devs(sh_uint8 max_lapic_id)` is made to initialize the driver internal state. This call allocates an array indexed by IOAPIC id.
## Device initialization
After that, each time a IOAPIC entry is parsed, a call to `sh_ioapic_init(sh_uint32 base,sh_uint8 ioapic_id,sh_uint32 gsi_base,sh_ioapic_DEVICE *ioapic)` is made. This does three things:
1) Identity map the memory space for the LAPIC
2) Fill the structure for the LAPIC device
3) Initialize the device without modifying the IOREDTBL for the moment
Then, a call to `sh_ioapic_bind(sh_ioapic_DEVICE *ioapic_dev)` is made, registering the LAPIC structure in the array.
## Basic IOAPIC management
For basic IOAPIC management, the following function are provided:
- `sh_ioapic_get_dev_ioapic_id(sh_uint64 ioapic_id)`: return a `sh_ioapic_DEVICE*` pointing to the IOAPIC structure corresponding to the provided IOAPIC id
- `sh_ioapic_get_dev_by_gsi(sh_uint32 gsi)`: return a `sh_ioapic_DEVICE*` pointing to the IOAPIC structure of the IOAPIC that manages the provided GSI
- `sh_ioapic_read_ioredtbl_entry(sh_ioapic_DEVICE *ioapic_dev,sh_uint32 gsi,sh_ioapic_IOREDTBL_ENTRY *entry)`: read a IOREDTBL entry and put its value into `entry`. If the GSI isn't managed by the provided device, raise an error
- `sh_ioapic_write_ioredtbl_entry(sh_ioapic_DEVICE *ioapic_dev,sh_uint32 gsi,sh_ioapic_IOREDTBL_ENTRY *entry)`: write and IOREDTBL entry corresponding to the provided GSI. If the GSI isn't managed by the provided device, raise an error
IOREDTBL read and write are thread-safe operations.
## Higher level operations
The following higher level operations are provided:
- `sh_ioapic_mask_gsi(sh_ioapic_DEVICE *dev,sh_uint32 gsi)`: mask the provided GSI. If the GSI isn't managed by the provided device, raise an error
- `sh_ioapic_unmask_gsi(sh_ioapic_DEVICE *dev,sh_uint32 gsi)`: unmask the provided GSI. If the GSI isn't managed by the provided device, raise an error
- `sh_ioapic_mask_all()`: mask all the GSI for all registered IOAPIC.
If you are looking for an even more abstracted API, please check the GSI subsystem.

View File

@@ -0,0 +1,81 @@
# LAPIC devices
This driver is defined inside `shelter/lib/include/devs/apic/lapic.h` and implemented inside `shelter/lib/src/devs/apic/lapic.c`.
## Driver initialization
During the MADT parsing, all LAPIC entries are counted. Then, a call to `sh_lapic_init_devs(sh_uint16 max_lapic_id,sh_uint16 max_acpi_processor_id)` is made to initialize the driver internal state. This call allocates two arrays: one indexed by LAPIC id and one indexed by ACPI processors id.
## Device initialization
After that, each time a LAPIC entry is parsed, a call to `sh_lapic_init(sh_uint64 lapic_phys,sh_uint8 spurious_vector,sh_uint8 apic_id,sh_uint8 apic_processor_id,sh_uint32 flags,sh_lapic_DEVICE *lapic)` is made. This does two things:
1) Identity map the memory space for the LAPIC
2) Fill the structure for the LAPIC device
Then, a call to `sh_lapic_bind(sh_lapic_DEVICE *lapic_dev)` is made, registering the LAPIC structure in both arrays.
Finally, when the MADT parsing is over, a call to `sh_lapic_init_dev(sh_uint8 spurious_vector,sh_lapic_DEVICE *lapic)` initialize the LAPIC by setuping a few of its registers.
## Basic LAPIC management
For basic LAPIC management, the following functions are provided:
- `sh_lapic_eoi(sh_lapic_DEVICE *lapic)`: send a End Of Interrupt (EOI) to the LAPIC
- `sh_lapic_get_dev_apic_id(sh_uint64 apic_id)`: return a `sh_lapic_DEVICE*` pointing to the LAPIC structure corresponding to the provided APIC id
- `sh_lapic_get_dev_acpi_cpu_id(sh_uint64 acpi_processor_id)`: return a `sh_lapic_DEVICE*` pointing to the LAPIC structure corresponding to the provided ACPI processor id
- `sh_lapic_get_by_apic_id_array()`: return a `sh_lapic_DEVICE**` of the LAPIC devices pointers array, indexed by LAPIC id
- `sh_lapic_get_max_apic_id()`: return the max LAPIC id
- `sh_lapic_get_max_acpi_processor_id()`: return the max ACPI processor id
- `sh_lapic_get_lapic_count()`: return the number of registered LAPICs
- `sh_lapic_get_current_core()`: return the LAPIC id of the current core, return -1 if error happened
## LAPIC timer
To launch a one shot timer without knowing the frequency, the `sh_lapic_timer_one_shot(sh_lapic_DEVICE *lapic_dev,sh_uint32 initial_value)` can be used.
But the LAPIC driver provides a way to calibrate the LAPIC in order to obtain its frequency. For this, we use the following process:
1) Launch a timer with max value
2) Read the start TSC and current LAPIC value, iterate for 10 millions times, read the end TSC and current LAPIC value.
3) Compute `tsc_delta` and `lapic_delta`
4) Using the following formula, estimate the LAPIC frequency: `lapic_freq = (lapic_delta * cpu_freq) / tsc_delta`
This calibration process is implemented in `sh_lapic_calibrate(sh_lapic_DEVICE *lapic_dev,sh_uint64 cpu_freq)`. But the TSC frequency estimation must have been achevied before that.
When the LAPIC frequency is obtained, two new functions are unlocked:
- `sh_lapic_get_frequency()`: return the estimated LAPIC frequency
- `sh_lapic_timer_one_shot_us(sh_lapic_DEVICE *lapic_dev,sh_uint64 microseconds_count)`: launch a one shot timer by taking a value in microsecond. Compute the initial value using the estimated frequency before hand.
## IPI management
The LAPIC driver provides a helper to know if the LAPIC IPI feature is busy: `sh_lapic_ipi_is_busy(sh_lapic_DEVICE *lapic_dev)`.
### Sending a fixed IPI
The primitive to send a fixed IPI is:
``` C
sh_lapic_send_fixed_ipi(sh_lapic_DEVICE *lapic_dev,sh_uint8 vector,sh_uint32 destination_mode,sh_int16 target_lapic_id);
```
`lapic_dev` must be the struct of the LAPIC of the current CPU.
`destination_mode` can either be `SH_LAPIC_IPI_DESTINATION_SPECIFIC`, `SH_LAPIC_IPI_DESTINATION_SELF`, `SH_LAPIC_IPI_DESTINATION_ALL` or `SH_LAPIC_IPI_DESTINATION_ALL_EXCLUDING_SELF`.
If `destination_mode` isn't `SH_LAPIC_IPI_DESTINATION_SPECIFIC`, `target_lapic_id` must be `SH_LAPIC_IPI_NO_DESTINATION`.
### Sending any others IPI
The primitive to send any other IPI than a fixed IPI is:
``` C
sh_lapic_send_ipi(sh_lapic_DEVICE *lapic_dev,sh_uint32 ipi_type,sh_uint32 destination_mode,sh_int16 target_lapic_id,sh_page_PHYSICAL_ADDRESS start_address);
```
`lapic_dev` must be the struct of the LAPIC of the current CPU.
`ipi_type` can either be `SH_LAPIC_IPI_TYPE_INIT`, `SH_LAPIC_IPI_TYPE_STARTUP` or `SH_LAPIC_IPI_TYPE_NMI`.
If `ipi_type` is `SH_LAPIC_IPI_TYPE_STARTUP`, `start_address` must contain the starting address. Address constraint (compatible with 16 bits mode) are checked.
If `ipi_type` isn't `SH_LAPIC_IPI_TYPE_STARTUP`, `start_address` must be 0.
`destination_mode` can either be `SH_LAPIC_IPI_DESTINATION_SPECIFIC`, `SH_LAPIC_IPI_DESTINATION_SELF`, `SH_LAPIC_IPI_DESTINATION_ALL` or `SH_LAPIC_IPI_DESTINATION_ALL_EXCLUDING_SELF`.
If `destination_mode` isn't `SH_LAPIC_IPI_DESTINATION_SPECIFIC`, `target_lapic_id` must be `SH_LAPIC_IPI_NO_DESTINATION`.

View File

@@ -0,0 +1,134 @@
# Keyboard input subsystem
## Introduction
The keyboard input subsystem is responsible for abstracting keyboard devices and keyboards events. It's defined in `shelter/lib/include/devs/input/kbd.h` and implemented in `shelter/lib/src/devs/input/kbd.c`. It's still in the early stage and isn't finished for the moment.
## Overview
The keyboard input subsystem is based on two key concepts:
- keyboard devices: this is the standard abstraction for any keyboard-like devices
- keyboard events: provides a standard way to interpret any events from any keyboard devices
The keyboard input subsystem is initialized using `sh_kbd_init_devs()`.
### Keyboard devices
### Driver side
The keyboard device structure look like this:
``` C
typedef struct {
sh_uint8 kbd_id;
sh_queue_KBD_EVENT *events_queue;
sh_bool present;
sh_bool ver_maj;
sh_bool ver_num;
sh_bool shift;
sh_bool alt;
sh_bool ctrl;
sh_bool win;
sh_SPIN_LOCK spinlock;
} sh_kbd_DEVICE;
```
Each keyboard device is identified with a keyboard id. When a keyboard is unregistered, the keyboard id can be recycled for a new keyboard device. There is a maximum of 256 keyboards id, and so 256 maximum registered keyboard devices at the same time.
The keyboard id 0 is reserved for the PS2 keyboard. It's always valid, even if there is no PS2 controller.
The `present` boolean defines if the keyboard is present. If `present` is `SH_FALSE`, no actions except unregistering the keyboard is allowed on the keyboard device.
The `present` boolean for the keyboard od keyboard id 0 indicates if the PS2 controller is available.
There is two primitives to register keyboards devices:
- `sh_kbd_register_ps2(sh_bool is_present,sh_kbd_DRIVER_HANDLE **handle)`: register the PS2 keyboard, reserved for the PS2 driver
- `sh_kbd_register(sh_bool is_present,sh_kbd_DRIVER_HANDLE **handle)`: register a new keyboard device
Upon registration, a `sh_kbd_DRIVER_HANDLE` struct is completed. It became non valid when the keyboard is unregistered. It is reserved for driver usage only.
Two primitives accessible to drivers require this driver handle:
- `sh_kbd_set_flag(sh_kbd_DRIVER_HANDLE *handle,sh_uint8 flag,sh_bool value)`: set a flag about the keyboard corresponding to the provided driver handle
- `sh_kbd_push_events(sh_kbd_DRIVER_HANDLE *handle,sh_kbd_EVENT *event)`: push an event in the events queue of the keyboard device corresponding to the provided driver handle
There is, for the moment, no primitive to unregister a keyboard.
### Consumer side
Four functions are available for consumers:
- `sh_kbd_enumerate_devices(sh_kbd_ENUMERATION *enumeration)`: complete a `sh_kbd_ENUMERATION` structure, each bit set to 1 correspond to a valid keyboard id
- `sh_kbd_get_handle(sh_uint8 kbd_id,sh_kbd_CONSUMER_HANDLE **handle)`: return a valid consumer handle for the provided keyboard id. It uses `sh_malloc`
- `sh_kbd_destroy_handle(sh_kbd_CONSUMER_HANDLE **handle)`: free the provided consumer handle
- `sh_kbd_read_flag(sh_kbd_DRIVER_HANDLE *d_handle,sh_kbd_CONSUMER_HANDLE *c_handle,sh_uint8 flag,sh_bool *value)`: read a flag about the keyboard corresponding to the provided handle. Accepts either a driver or consumer handle, but fails if both handle are provided.
The possible flags to read/write are: `SH_KBD_FLAG_PRESENT`, `SH_KBD_FLAG_VER_MAJ`, `SH_KBD_FLAG_VER_NUM`, `SH_KBD_FLAG_SHIFT`, `SH_KBD_FLAG_ALT`, `SH_KBD_FLAG_CTRL`, `SH_KBD_FLAG_WIN`.
There is, for the moment, no way to read keyboards events.
## Keyboard events
A keyboard event is represented by this structure:
``` C
typedef struct {
sh_uint16 scancode;
sh_bool pressed;
sh_uint8 context_shift_win;
sh_uint8 context_alt;
sh_uint8 context_ctrl;
sh_uint8 context_lock_status;
sh_uint8 event_type;
sh_uint64 timestamp;
} sh_kbd_EVENT;
```
All non-pause events contain a snapshot of keyboard context state at emission time, before the state of corresponding flag in the keyboard device changes. These fields remain valid even for `SH_EVENT_TYPE_KBD_CONTEXT`:
- `context_shift_win`:
- Bit 0: shift active, checked with macro `SH_KBD_EVENT_SHIFT_ACTIVE`
- Bit 1: win active, checked with macro `SH_KBD_EVENT_WIN_ACTIVE`
- `context_alt`:
- Bit 0: right alt active, checked with macro `SH_KBD_EVENT_RIGHT_ACTIVE`
- Bit 1: left alt active, checked with macro `SH_KBD_EVENT_LEFT_ACTIVE`
- `context_ctrl`:
- Bit 0: right control active, checked with macro `SH_KBD_EVENT_RIGHT_ACTIVE`
- Bit 1: left control active, checked with macro `SH_KBD_EVENT_LEFT_ACTIVE`
- `context_lock_status`:
- Bit 0: numlock active, checked with `SH_KBD_EVENT_NUMLOCK_ACTIVE`
- Bit 0: capslock active, checked with `SH_KBD_EVENT_CAPSLOCK_ACTIVE`
There is a precise way of interpreting what this structure mean. It start by checking the value of `event_type`.
### `SH_EVENT_TYPE_KBD_PAUSE`
The event is a press of the pause key. The `scancode` and `pressed` fields should be ignored.
### `SH_EVENT_TYPE_KBD_CONTEXT`
This event is a press of a modifier key. It allows to abstract modifiers keys events accross all types of keyboards.
The scancode must be one of the following:
- `SH_KBD_CONTEXT_LSHIFT`
- `SH_KBD_CONTEXT_RSHIFT`
- `SH_KBD_CONTEXT_LCTRL`
- `SH_KBD_CONTEXT_RCTRL`
- `SH_KBD_CONTEXT_LALT`
- `SH_KBD_CONTEXT_RALT`
- `SH_KBD_CONTEXT_WIN`
- `SH_KBD_CONTEXT_NUMLOCK`
- `SH_KBD_CONTEXT_CAPSLOCK`
You need to check the `pressed` boolean to know if the key was pressed or released.
### `SH_EVENT_TYPE_PS2_NORMAL`
This event type represents a standard PS2 Set1 scancode. The scancode is put in the low 8 bits of the `scancode` fields. The high 8 bits are set to 0.
You need to check the `pressed` boolean to know if the key was pressed or released.
### `SH_EVENT_TYPE_PS2_E0`
This event type represents an extended PS2 Set1 scancode prefixed by `0xE0`. The scancode is put in the low 8 bits of the `scancode` fields. The high 8 bits are set to `0xE0`.
You need to check the `pressed` boolean to know if the key was pressed or released.
### Notes
For `SH_EVENT_TYPE_PS2_NORMAL` and `SH_EVENT_TYPE_PS2_E0` events type, the scancode, when `pressed` is `SH_FALSE`, keeps its bit 8 to 1.