Files

323 lines
6.9 KiB
C

/*
* AsterOS Kernel
* Autor: Pavel Kalaš
* Rok: 2026
*
*/
/*
* Tento soubor implementuje zakladni klavesnicovy vstup pres PS/2 porty.
* Preklada scancode na ASCII znaky a poskytuje funkci pro nacitani radku,
* kterou pouziva shell pro prijem prikazu od uzivatele.
*/
#include "display.h"
#include "drivers.h"
#include "timer.h"
#define KBD_HISTORY_MAX 32
#define KBD_HISTORY_LINE_MAX 128
static char g_history[KBD_HISTORY_MAX][KBD_HISTORY_LINE_MAX];
static int g_history_count = 0;
static int g_history_head = 0;
static int g_ctrl_down = 0;
static int g_shift_down = 0;
static void (*g_refresh_cb)(void) = 0;
static unsigned long g_refresh_interval_ticks = 0;
static char apply_letter_case(char c) {
int upper = g_shift_down;
if (!upper) {
return c;
}
if (c >= 'a' && c <= 'z') {
return (char)(c - ('a' - 'A'));
}
return c;
}
static inline unsigned char inb(unsigned short port) {
unsigned char ret;
__asm__ volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port));
return ret;
}
static const char keymap[128] = {
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', '\t',
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0, 'a', 's',
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v',
'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' ',
};
void keyboard_init(void) {
}
void keyboard_set_refresh_callback(void (*cb)(void), unsigned long interval_ticks) {
g_refresh_cb = cb;
g_refresh_interval_ticks = interval_ticks;
}
int keyboard_try_read_key(void) {
unsigned char sc;
int out = -1;
if ((inb(0x64) & 1) == 0) {
return -1;
}
sc = inb(0x60);
if (sc == 0xE0) {
unsigned char ext;
if ((inb(0x64) & 1) == 0) {
return -1;
}
ext = inb(0x60);
if (ext == 0x1D) {
g_ctrl_down = 1;
return -1;
}
if (ext == 0x9D) {
g_ctrl_down = 0;
return -1;
}
if (ext & 0x80) {
return -1;
}
if (ext == 0x48) {
out = ASTER_KEY_UP;
return out;
}
if (ext == 0x50) {
out = ASTER_KEY_DOWN;
return out;
}
if (ext == 0x4B) {
out = ASTER_KEY_LEFT;
return out;
}
if (ext == 0x4D) {
out = ASTER_KEY_RIGHT;
return out;
}
return -1;
}
if (sc == 0x1D) {
g_ctrl_down = 1;
return -1;
}
if (sc == 0x9D) {
g_ctrl_down = 0;
return -1;
}
if (sc == 0x2A || sc == 0x36) {
g_shift_down = 1;
return -1;
}
if (sc == 0xAA || sc == 0xB6) {
g_shift_down = 0;
return -1;
}
if (sc & 0x80) {
return -1;
}
if (sc == 0x3B) {
out = ASTER_KEY_F1;
return out;
}
if (sc == 0x3C) {
out = ASTER_KEY_F2;
return out;
}
if (sc == 0x1F && g_ctrl_down) {
out = 19; /* Ctrl+S */
return out;
}
if (sc < 128 && keymap[sc]) {
out = apply_letter_case(keymap[sc]);
return out;
}
return -1;
}
int keyboard_read_key(void) {
for (;;) {
int k = keyboard_try_read_key();
if (k != -1) {
return k;
}
__asm__ volatile ("pause");
}
}
static void clear_current_line(int len) {
int j;
for (j = 0; j < len; ++j) {
display_putc('\b');
}
}
static void set_line_from_history(char *buffer, int *len, int max_len, const char *src) {
int j = 0;
int limit = max_len - 1;
clear_current_line(*len);
while (src[j] && j < limit) {
buffer[j] = src[j];
display_putc(src[j]);
++j;
}
buffer[j] = '\0';
*len = j;
}
static void history_push(const char *line) {
int i;
int slot;
if (!line || !line[0]) {
return;
}
if (g_history_count > 0) {
int last = (g_history_head + KBD_HISTORY_MAX - 1) % KBD_HISTORY_MAX;
if (g_history[last][0] != '\0') {
i = 0;
while (line[i] && g_history[last][i] && line[i] == g_history[last][i]) {
++i;
}
if (line[i] == '\0' && g_history[last][i] == '\0') {
return;
}
}
}
slot = g_history_head;
for (i = 0; i < KBD_HISTORY_LINE_MAX - 1 && line[i]; ++i) {
g_history[slot][i] = line[i];
}
g_history[slot][i] = '\0';
g_history_head = (g_history_head + 1) % KBD_HISTORY_MAX;
if (g_history_count < KBD_HISTORY_MAX) {
++g_history_count;
}
}
int keyboard_readline(char *buffer, int max_len) {
int i = 0;
int history_nav = -1;
unsigned long last_refresh_tick = timer_ticks();
if (max_len <= 1) {
return 0;
}
for (;;) {
int c = keyboard_try_read_key();
if (c == -1) {
if (g_refresh_cb && g_refresh_interval_ticks > 0) {
unsigned long now = timer_ticks();
if ((now - last_refresh_tick) >= g_refresh_interval_ticks) {
g_refresh_cb();
last_refresh_tick = now;
}
}
__asm__ volatile ("pause");
continue;
}
if (c == ASTER_KEY_F1 || c == ASTER_KEY_F2) {
continue;
}
if (c == ASTER_KEY_UP) {
if (g_history_count == 0) {
continue;
}
if (history_nav < g_history_count - 1) {
++history_nav;
}
{
int idx = (g_history_head + KBD_HISTORY_MAX - 1 - history_nav) % KBD_HISTORY_MAX;
set_line_from_history(buffer, &i, max_len, g_history[idx]);
}
continue;
}
if (c == ASTER_KEY_DOWN) {
if (g_history_count == 0) {
continue;
}
if (history_nav > 0) {
--history_nav;
{
int idx = (g_history_head + KBD_HISTORY_MAX - 1 - history_nav) % KBD_HISTORY_MAX;
set_line_from_history(buffer, &i, max_len, g_history[idx]);
}
} else if (history_nav == 0) {
history_nav = -1;
clear_current_line(i);
i = 0;
buffer[0] = '\0';
}
continue;
}
if (c == '\n') {
display_putc('\n');
buffer[i] = '\0';
history_push(buffer);
return i;
}
if (c == '\b') {
if (i > 0) {
--i;
display_putc('\b');
}
continue;
}
if (i < max_len - 1) {
buffer[i++] = (char)c;
display_putc((char)c);
buffer[i] = '\0';
history_nav = -1;
}
}
}