mouse support (working in qemu, semi-working in virtualbox)

This commit is contained in:
Benji Dial 2024-05-19 04:34:40 -04:00
parent b1a912a8a6
commit e60fa7740c
16 changed files with 365 additions and 67 deletions

View file

@ -1,28 +1,120 @@
#include <daguerre.hpp>
void overlay_encode(
daguerre::hilbert_color &dest, const daguerre::rgb24 &src) {
if (src.r != 0xff || src.g != 0x00 || src.b != 0xff)
daguerre::encode(dest, src);
}
void invert(daguerre::rgb24 &dest, const daguerre::rgb24 &src) {
dest.r = 255 - src.r;
dest.g = 255 - src.g;
dest.b = 255 - src.b;
}
int main(int, char **) {
auto hfb = daguerre::get_hilbert_framebuffer();
daguerre::image<daguerre::rgb24> bim;
auto framebuffer = daguerre::get_hilbert_framebuffer();
std::FILE *burdon = std::fopen("/assets/burden.ppm", "r");
daguerre::try_load_ppm(burdon, bim);
std::fclose(burdon);
daguerre::image<daguerre::rgb24> burden;
std::FILE *burden_file = std::fopen("/assets/burden.ppm", "r");
daguerre::try_load_ppm(burden_file, burden);
std::fclose(burden_file);
unsigned width = bim.width < hfb.width ? bim.width : hfb.width;
unsigned height = bim.height < hfb.height ? bim.height : hfb.height;
unsigned x = (hfb.width - width) / 2;
unsigned y = (hfb.height - height) / 2;
daguerre::image<daguerre::rgb24> pointer;
std::FILE *pointer_file = std::fopen("/assets/pointer.ppm", "r");
daguerre::try_load_ppm(pointer_file, pointer);
std::fclose(pointer_file);
int32_t width = burden.width < framebuffer.width
? burden.width : framebuffer.width;
int32_t height = burden.height < framebuffer.height
? burden.height : framebuffer.height;
unsigned x = (framebuffer.width - width) / 2;
unsigned y = (framebuffer.height - height) / 2;
int32_t new_mouse_x = width / 2;
int32_t new_mouse_y = height / 2;
int32_t old_mouse_x = new_mouse_x;
int32_t old_mouse_y = new_mouse_y;
bool was_left_mouse_down = false;
daguerre::overlay_region<
daguerre::hilbert_color, daguerre::rgb24, daguerre::encode>(
framebuffer, x, y, burden, 0, 0, width, height);
while (1) {
daguerre::copy_region<>(hfb, bim, 0, 0, x, y, width, height);
while ((__euler_read_key_packet() & 0x0400ff) != 0x00005a)
;
for (unsigned i = 0; i < bim.width * bim.height; ++i) {
bim.buffer[i].r = 255 - bim.buffer[i].r;
bim.buffer[i].g = 255 - bim.buffer[i].g;
bim.buffer[i].b = 255 - bim.buffer[i].b;
__euler_mouse_buttons mouse_buttons;
int16_t mouse_change_x, mouse_change_y;
uint32_t key_packet;
__euler_input_packet_type packet_type = __euler_get_input_packet(
mouse_buttons, mouse_change_x, mouse_change_y, key_packet);
bool anything_changed = false;
bool should_invert = false;
if (packet_type == __EULER_IPT_MOUSE) {
if (mouse_change_x != 0 || mouse_change_y != 0) {
old_mouse_x = new_mouse_x;
old_mouse_y = new_mouse_y;
new_mouse_x += mouse_change_x;
new_mouse_y += mouse_change_y;
if (new_mouse_x < 0)
new_mouse_x = 0;
else if ((unsigned)new_mouse_x > width - pointer.width)
new_mouse_x = width - pointer.width;
if (new_mouse_y < 0)
new_mouse_y = 0;
else if ((unsigned)new_mouse_y > height - pointer.height)
new_mouse_y = height - pointer.height;
anything_changed = true;
}
if (!was_left_mouse_down && (mouse_buttons & __EULER_MB_LEFT))
should_invert = true;
was_left_mouse_down = mouse_buttons & __EULER_MB_LEFT;
}
else if (packet_type == __EULER_IPT_KEYBOARD)
if ((key_packet & 0x0400ff) == 0x00005a)
should_invert = true;
if (should_invert) {
//this works with the current implementation of overlay_region, but
//maybe it would be better to have a dedicated function for when the
//two regions are exactly the same, given the comment on overlay_region.
daguerre::overlay_region<daguerre::rgb24, daguerre::rgb24, invert>(
burden, 0, 0, burden, 0, 0, width, height);
daguerre::overlay_region<
daguerre::hilbert_color, daguerre::rgb24, daguerre::encode>(
framebuffer, x, y, burden, 0, 0, width, height);
anything_changed = true;
}
if (anything_changed) {
daguerre::overlay_region<
daguerre::hilbert_color, daguerre::rgb24, daguerre::encode>(
framebuffer, old_mouse_x + x, old_mouse_y + y, burden,
old_mouse_x, old_mouse_y, pointer.width, pointer.height);
daguerre::overlay_region<
daguerre::hilbert_color, daguerre::rgb24, overlay_encode>(
framebuffer, new_mouse_x + x, new_mouse_y + y,
pointer, 0, 0, pointer.width, pointer.height);
}
}
}

View file

@ -63,9 +63,19 @@ get new pages:
rax out: start of first page
the allocated pages are next to each other, writable, and not executable.
read key packet:
get input packet:
rax in: 5
eax out: key packet
al out:
bit 7: was mouse packet
if bit 7:
bit 0: left mouse button down
bit 1: right mouse button down
bit 2: middle mouse button down
if bit 7:
di out: mouse x change (signed)
si out: mouse y change (signed)
else:
edi out: key packet
create private socket:
rax in: 6

View file

@ -58,4 +58,17 @@ extern "C" uint32_t *__euler_get_framebuffer(
extern "C" uint32_t __euler_encode_color(uint8_t r, uint8_t g, uint8_t b);
extern "C" uint32_t __euler_read_key_packet();
enum __euler_mouse_buttons : uint8_t {
__EULER_MB_LEFT = 1,
__EULER_MB_RIGHT = 2,
__EULER_MB_MIDDLE = 4
};
enum __euler_input_packet_type : uint8_t {
__EULER_IPT_MOUSE = 1,
__EULER_IPT_KEYBOARD = 2
};
extern "C" __euler_input_packet_type __euler_get_input_packet(
__euler_mouse_buttons &buttons_out, int16_t &x_change_out,
int16_t &y_change_out, uint32_t &keyboard_packet_out);

View file

@ -11,7 +11,7 @@ global __euler_end_this_thread
global __euler_read_from_stream
global __euler_get_framebuffer
global __euler_encode_color
global __euler_read_key_packet
global __euler_get_input_packet
section .text
@ -96,7 +96,31 @@ __euler_encode_color:
syscall
ret
__euler_read_key_packet:
__euler_get_input_packet:
push rdi
push rsi
push rdx
push rcx
mov rax, 5
syscall
test al, 0x80
jnz .mouse_packet
pop rcx
mov dword [rcx], edi
add rsp, 24
mov al, 2
ret
.mouse_packet:
add rsp, 8
pop rdx
mov word [rdx], si
pop rdx
mov word [rdx], di
pop rdx
and al, 0x7f
mov byte [rdx], al
mov al, 1
ret

View file

@ -18,7 +18,26 @@ namespace hilbert::kernel::input {
BREAK = 1 << 18,
};
extern utility::queue<uint32_t> *key_queue;
enum buttons_t : uint8_t {
LEFT_BUTTON = 1,
RIGHT_BUTTON = 2,
MIDDLE_BUTTON = 4
};
struct input_packet {
union {
struct {
int16_t x_change;
int16_t y_change;
buttons_t buttons;
} mouse;
uint32_t keyboard;
};
bool is_mouse;
};
extern utility::queue<input_packet> *input_queue;
//notify a process waiting for input
void got_input();

View file

@ -342,6 +342,11 @@ namespace hilbert::kernel::utility {
return move(ret);
}
//assumes not empty
value_t &last_inserted() const {
return buffer[(count - 1 + next_read_index) % buffer_len];
}
};
}

View file

@ -5,10 +5,10 @@
namespace hilbert::kernel::input {
utility::queue<uint32_t> *key_queue;
utility::queue<input_packet> *input_queue;
void init_input() {
key_queue = new utility::queue<uint32_t>();
input_queue = new utility::queue<input_packet>();
}
void got_input() {

View file

@ -227,14 +227,6 @@ section .bss
section .text
write_keyboard_byte:
in al, 0x64
test al, 0x02
jnz write_keyboard_byte
mov al, dil
out 0x60, al
ret
extern on_keyboard_interrupt
keyboard_isr:
@ -249,6 +241,7 @@ keyboard_isr:
push rcx
push rax
call wait_read_ps2
in al, 0x60
mov dil, al
@ -269,6 +262,54 @@ keyboard_isr:
iretq
extern on_mouse_interrupt
mouse_isr:
push r11
push r10
push r9
push r8
push rsi
push rdi
push rdx
push rcx
push rax
call wait_read_ps2
in al, 0x60
mov dil, al
call on_mouse_interrupt
mov al, 0x20
out 0x20, al
out 0xa0, al
pop rax
pop rcx
pop rdx
pop rdi
pop rsi
pop r8
pop r9
pop r10
pop r11
iretq
wait_send_ps2:
in al, 0x64
test al, 0x02
jnz wait_send_ps2
ret
wait_read_ps2:
in al, 0x64
test al, 0x01
jz wait_send_ps2
ret
load_gdt_and_idt:
;fill exception entries in idt
@ -294,7 +335,7 @@ load_gdt_and_idt:
out 0x21, al
mov al, 0x01
out 0x21, al
mov al, 0xfd ;mask all but irq 1
mov al, 0xf9 ;mask all but irq 1 and 2
out 0x21, al
mov al, 0x11
@ -305,19 +346,54 @@ load_gdt_and_idt:
out 0xa1, al
mov al, 0x01
out 0xa1, al
mov al, 0xff ;mask all
mov al, 0xef ;mask all but irq 12
out 0xa1, al
;register keyboard and mouse interrupts
mov rdi, 0x21
mov rsi, keyboard_isr
call set_isr
;set keyboard config
mov rdi, 0x2c
mov rsi, mouse_isr
call set_isr
;set ps2 config
call wait_send_ps2
mov al, 0x60
out 0x64, al
mov dil, 0x01
call write_keyboard_byte
call wait_send_ps2
mov al, 0x03
out 0x60, al
;set mouse defaults
call wait_send_ps2
mov al, 0xd4
out 0x64, al
call wait_send_ps2
mov al, 0xf6
out 0x60, al
call wait_read_ps2
in al, 0x60
;enable mouse reporting
call wait_send_ps2
mov al, 0xd4
out 0x64, al
call wait_send_ps2
mov al, 0xf4
out 0x60, al
call wait_read_ps2
in al, 0x60
;make tss entry in gdt

View file

@ -1,3 +1,4 @@
#include <hilbert/kernel/framebuffer.hpp>
#include <hilbert/kernel/input.hpp>
#include <hilbert/kernel/panic.hpp>
@ -54,7 +55,9 @@ static uint32_t current_flags = 0;
static void got_key(uint32_t key) {
input::key_queue->insert(current_flags | key);
input::input_queue->insert({
.keyboard = current_flags | key, .is_mouse = false});
input::got_input();
if (key == (input::BREAK | 0x77))
@ -90,7 +93,7 @@ static void got_key(uint32_t key) {
}
static uint8_t key_so_far[8];
uint8_t key_so_far_len = 0;
static uint8_t key_so_far_len = 0;
extern "C" void on_keyboard_interrupt(uint8_t byte) {
@ -150,3 +153,45 @@ extern "C" void on_keyboard_interrupt(uint8_t byte) {
}
}
static uint8_t mouse_packet_so_far[3];
static uint8_t mouse_packet_so_far_len = 0;
extern "C" void on_mouse_interrupt(uint8_t byte) {
if (mouse_packet_so_far_len == 0 && byte == 0xfa)
//dirty hack
return;
mouse_packet_so_far[mouse_packet_so_far_len++] = byte;
if (mouse_packet_so_far_len < 3)
return;
mouse_packet_so_far_len = 0;
int16_t x_change = mouse_packet_so_far[1];
int16_t y_change = -(int16_t)mouse_packet_so_far[2];
if (mouse_packet_so_far[0] & 0x10)
x_change -= 0x100;
if (mouse_packet_so_far[0] & 0x20)
y_change += 0x100;
input::input_packet packet = {
.mouse = {
.x_change = x_change, .y_change = y_change,
.buttons = (input::buttons_t)(mouse_packet_so_far[0] & 7) },
.is_mouse = true };
if (input::input_queue->count > 0 &&
input::input_queue->last_inserted().is_mouse &&
input::input_queue->last_inserted().mouse.buttons ==
packet.mouse.buttons) {
input::input_queue->last_inserted().mouse.x_change = packet.mouse.x_change;
input::input_queue->last_inserted().mouse.y_change = packet.mouse.y_change;
}
else
input::input_queue->insert(packet);
input::got_input();
}

View file

@ -157,7 +157,7 @@ namespace hilbert::kernel::syscall {
}
void read_key_packet_syscall(
void get_input_packet_syscall(
uint64_t &rax, uint64_t &rdi, uint64_t &rsi, uint64_t &rdx
) {
@ -165,8 +165,17 @@ namespace hilbert::kernel::syscall {
auto *t = application::running_thread;
do
if (input::key_queue->count > 0) {
rax = (uint64_t)input::key_queue->take();
if (input::input_queue->count > 0) {
input::input_packet packet = input::input_queue->take();
if (packet.is_mouse) {
rax = packet.mouse.buttons | 0x80;
rdi = (uint16_t)packet.mouse.x_change;
rsi = (uint16_t)packet.mouse.y_change;
}
else {
rax = 0;
rdi = packet.keyboard;
}
return;
}
while (application::save_thread_state(t->cpu));
@ -504,7 +513,7 @@ namespace hilbert::kernel::syscall {
&open_file_syscall,
&end_this_thread_syscall,
&get_new_pages_syscall,
&read_key_packet_syscall,
&get_input_packet_syscall,
&create_private_socket_syscall,
&create_socket_listener_syscall,
&stop_socket_listener_syscall,
@ -520,7 +529,7 @@ namespace hilbert::kernel::syscall {
&set_stream_length_syscall
};
static constexpr int max_syscall_number = 18;
static constexpr int max_syscall_number = 19;
}

View file

@ -1,6 +1,7 @@
#pragma once
#include <stdint.h>
#include <cstring>
#include <cstdio>
namespace daguerre {
@ -13,14 +14,6 @@ namespace daguerre {
uint8_t b;
};
template <class to_type, class from_type>
to_type convert_color(const from_type &from);
template <>
inline hilbert_color convert_color<hilbert_color, rgb24>(const rgb24 &from) {
return __euler_encode_color(from.r, from.g, from.b);
}
template <class color_t>
class image {
@ -88,15 +81,15 @@ namespace daguerre {
//from [from_x, from_x + width) x [from_y, from_y + height).
template <class color_t>
void copy_region(
image<color_t> &to, const image<color_t> &from, unsigned from_x,
unsigned from_y, unsigned to_x, unsigned to_y, unsigned width,
unsigned height) {
image<color_t> &to, unsigned to_x, unsigned to_y,
const image<color_t> &from, unsigned from_x, unsigned from_y,
unsigned width, unsigned height) {
color_t *to_start = to.buffer + to.pitch * to_y + to_x;
const color_t *from_start = from.buffer + from.pitch * from_y + from_x;
for (unsigned y = 0; y < height; ++y)
memcpy(
std::memcpy(
to_start + to.pitch * y, from_start + from.pitch * y,
width * sizeof(color_t));
@ -107,12 +100,11 @@ namespace daguerre {
//from [from_x, from_x + width) x [from_y, from_y + height).
template <
class to_color_t, class from_color_t,
to_color_t converter(const from_color_t &) =
convert_color<to_color_t, from_color_t>>
void copy_region(
image<to_color_t> &to, const image<from_color_t> &from, unsigned from_x,
unsigned from_y, unsigned to_x, unsigned to_y, unsigned width,
unsigned height) {
void overlay(to_color_t &dest, const from_color_t &src)>
void overlay_region(
image<to_color_t> &to, unsigned to_x, unsigned to_y,
const image<from_color_t> &from, unsigned from_x, unsigned from_y,
unsigned width, unsigned height) {
to_color_t *to_start = to.buffer + to.pitch * to_y + to_x;
const from_color_t *from_start =
@ -120,10 +112,14 @@ namespace daguerre {
for (unsigned y = 0; y < height; ++y)
for (unsigned x = 0; x < width; ++x)
to_start[to.pitch * y + x] = converter(from_start[from.pitch * y + x]);
overlay(to_start[to.pitch * y + x], from_start[from.pitch * y + x]);
}
static inline void encode(hilbert_color &dest, const rgb24 &src) {
dest = __euler_encode_color(src.r, src.g, src.b);
}
image<hilbert_color> get_hilbert_framebuffer();
bool try_load_ppm(std::FILE *input, image<rgb24> &into);

View file

@ -8,15 +8,22 @@ namespace daguerre {
return image<hilbert_color>(ptr, width, height, pitch, false);
}
//TODO: make this more robust
unsigned read_text_int(std::FILE *input) {
unsigned n = 0;
char ch;
while (true) {
std::fread(&ch, 1, 1, input);
if (ch == '#') {
do
std::fread(&ch, 1, 1, input);
while (ch != '\n');
std::fread(&ch, 1, 1, input);
if (ch < '0' || ch > '9')
return n;
n = n * 10 + ch - '0';
}
do {
n = n * 10 + ch - '0';
std::fread(&ch, 1, 1, input);
} while (ch >= '0' && ch <= '9');
return n;
}
bool try_load_ppm(std::FILE *input, image<rgb24> &into) {

BIN
skeleton/assets/pointer.ppm Normal file

Binary file not shown.

View file

@ -2,3 +2,5 @@ the photo in burden.ppm is "selective focus photography snowflakes" by aaron
burden. it can be found online at
https://unsplash.com/photos/selective-focus-photography-snowflakes-9yhy1FXlKwI.
its license can be found online at https://unsplash.com/license.
the icon in pointer.ppm is by me :)