Compare commits
7 Commits
406aa0d925
...
f0ce6c36de
Author | SHA1 | Date | |
---|---|---|---|
f0ce6c36de | |||
2951c539b1 | |||
70c63572b2 | |||
fc37634a18 | |||
f05fea441c | |||
5975e58b4a | |||
d385e01796 |
@ -53,3 +53,4 @@ add_subdirectory(tests)
|
||||
add_subdirectory(shell)
|
||||
add_subdirectory(wind)
|
||||
add_subdirectory(terminal)
|
||||
add_subdirectory(editor)
|
||||
|
@ -50,9 +50,9 @@ class GameWidget final : public ui::Widget
|
||||
|
||||
bool should_add_tile = false;
|
||||
|
||||
switch (request.key)
|
||||
switch (request.code)
|
||||
{
|
||||
case 'w': {
|
||||
case moon::K_UpArrow: {
|
||||
bool changed;
|
||||
changed = move_up();
|
||||
if (changed) should_add_tile = true;
|
||||
@ -61,7 +61,7 @@ class GameWidget final : public ui::Widget
|
||||
if (changed) should_add_tile = true;
|
||||
}
|
||||
break;
|
||||
case 'a': {
|
||||
case moon::K_LeftArrow: {
|
||||
bool changed;
|
||||
changed = move_left();
|
||||
if (changed) should_add_tile = true;
|
||||
@ -70,7 +70,7 @@ class GameWidget final : public ui::Widget
|
||||
if (changed) should_add_tile = true;
|
||||
}
|
||||
break;
|
||||
case 's': {
|
||||
case moon::K_DownArrow: {
|
||||
bool changed;
|
||||
changed = move_down();
|
||||
if (changed) should_add_tile = true;
|
||||
@ -79,7 +79,7 @@ class GameWidget final : public ui::Widget
|
||||
if (changed) should_add_tile = true;
|
||||
}
|
||||
break;
|
||||
case 'd': {
|
||||
case moon::K_RightArrow: {
|
||||
bool changed;
|
||||
changed = move_right();
|
||||
if (changed) should_add_tile = true;
|
||||
@ -88,7 +88,7 @@ class GameWidget final : public ui::Widget
|
||||
if (changed) should_add_tile = true;
|
||||
}
|
||||
break;
|
||||
case 'r': {
|
||||
case moon::K_Home: {
|
||||
reset();
|
||||
return ui::EventResult::DidHandle;
|
||||
}
|
||||
|
12
editor/CMakeLists.txt
Normal file
12
editor/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
||||
set(SOURCES
|
||||
main.cpp
|
||||
EditorWidget.h
|
||||
EditorWidget.cpp
|
||||
)
|
||||
|
||||
add_executable(editor ${SOURCES})
|
||||
target_compile_options(editor PRIVATE -Os ${COMMON_FLAGS} -Wno-write-strings)
|
||||
add_dependencies(editor libc)
|
||||
target_include_directories(editor PRIVATE ${LUNA_BASE}/usr/include ${CMAKE_CURRENT_LIST_DIR})
|
||||
target_link_libraries(editor PRIVATE os ui)
|
||||
install(TARGETS editor DESTINATION ${LUNA_BASE}/usr/bin)
|
267
editor/EditorWidget.cpp
Normal file
267
editor/EditorWidget.cpp
Normal file
@ -0,0 +1,267 @@
|
||||
/**
|
||||
* @file EditorWidget.cpp
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief Multiline text editing widget.
|
||||
*
|
||||
* @copyright Copyright (c) 2024, the Luna authors.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "EditorWidget.h"
|
||||
#include <ctype.h>
|
||||
#include <luna/Utf8.h>
|
||||
#include <os/File.h>
|
||||
#include <ui/App.h>
|
||||
|
||||
EditorWidget::EditorWidget(SharedPtr<ui::Font> font) : ui::Widget(), m_font(font)
|
||||
{
|
||||
m_cursor_timer = os::Timer::create_repeating(500, [this]() { this->tick_cursor(); }).release_value();
|
||||
recalculate_lines();
|
||||
}
|
||||
|
||||
Result<void> EditorWidget::load_file(const os::Path& path)
|
||||
{
|
||||
os::eprintln("Loading file: %s", path.name().chars());
|
||||
|
||||
auto file = TRY(os::File::open_or_create(path, os::File::ReadOnly));
|
||||
|
||||
m_data = TRY(file->read_all());
|
||||
|
||||
os::eprintln("Read %zu bytes.", m_data.size());
|
||||
|
||||
m_cursor = m_data.size();
|
||||
|
||||
m_path = path;
|
||||
|
||||
String title = TRY(String::format("Text Editor - %s"_sv, m_path.name().chars()));
|
||||
ui::App::the().main_window()->set_title(title.view());
|
||||
|
||||
TRY(recalculate_lines());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<ui::EventResult> EditorWidget::handle_key_event(const ui::KeyEventRequest& request)
|
||||
{
|
||||
// Avoid handling "key released" events
|
||||
if (!request.pressed) return ui::EventResult::DidNotHandle;
|
||||
|
||||
if (m_insert)
|
||||
{
|
||||
if (request.code == moon::K_Esc)
|
||||
{
|
||||
m_insert = false;
|
||||
String title = TRY(String::from_cstring("Text Editor"));
|
||||
if (!m_path.is_empty_path()) title = TRY(String::format("Text Editor - %s"_sv, m_path.name().chars()));
|
||||
ui::App::the().main_window()->set_title(title.view());
|
||||
}
|
||||
|
||||
if (request.code == moon::K_Backspace)
|
||||
{
|
||||
if (m_cursor == 0) return ui::EventResult::DidNotHandle;
|
||||
m_cursor--;
|
||||
|
||||
usize size = m_data.size() - m_cursor;
|
||||
u8* slice = TRY(m_data.slice(m_cursor, size));
|
||||
memmove(slice, slice + 1, size - 1);
|
||||
TRY(m_data.try_resize(m_data.size() - 1));
|
||||
|
||||
TRY(recalculate_lines());
|
||||
|
||||
m_cursor_timer->restart();
|
||||
m_cursor_activated = true;
|
||||
|
||||
return ui::EventResult::DidHandle;
|
||||
}
|
||||
|
||||
if (request.letter != '\n' && iscntrl(request.letter)) return ui::EventResult::DidNotHandle;
|
||||
|
||||
if (m_cursor == m_data.size()) TRY(m_data.append_data((const u8*)&request.letter, 1));
|
||||
else
|
||||
{
|
||||
usize size = m_data.size() - m_cursor;
|
||||
u8* slice = TRY(m_data.slice(m_cursor, size + 1));
|
||||
memmove(slice + 1, slice, size);
|
||||
*slice = request.letter;
|
||||
}
|
||||
|
||||
m_cursor++;
|
||||
TRY(recalculate_lines());
|
||||
|
||||
m_cursor_timer->restart();
|
||||
m_cursor_activated = true;
|
||||
|
||||
return ui::EventResult::DidHandle;
|
||||
}
|
||||
|
||||
switch (request.letter)
|
||||
{
|
||||
case 'w': {
|
||||
if (m_cursor_position.y > 0) m_cursor_position.y--;
|
||||
else
|
||||
return ui::EventResult::DidNotHandle;
|
||||
recalculate_cursor_index();
|
||||
m_cursor_timer->restart();
|
||||
m_cursor_activated = true;
|
||||
return ui::EventResult::DidHandle;
|
||||
}
|
||||
case 's': {
|
||||
if (m_cursor_position.y + 1 < (int)m_lines.size()) m_cursor_position.y++;
|
||||
else
|
||||
return ui::EventResult::DidNotHandle;
|
||||
recalculate_cursor_index();
|
||||
m_cursor_timer->restart();
|
||||
m_cursor_activated = true;
|
||||
return ui::EventResult::DidHandle;
|
||||
}
|
||||
case 'a': {
|
||||
if (m_cursor > 0) m_cursor--;
|
||||
else
|
||||
return ui::EventResult::DidNotHandle;
|
||||
recalculate_cursor_position();
|
||||
m_cursor_timer->restart();
|
||||
m_cursor_activated = true;
|
||||
return ui::EventResult::DidHandle;
|
||||
}
|
||||
case 'd': {
|
||||
if (m_cursor < m_data.size()) m_cursor++;
|
||||
else
|
||||
return ui::EventResult::DidNotHandle;
|
||||
recalculate_cursor_position();
|
||||
m_cursor_timer->restart();
|
||||
m_cursor_activated = true;
|
||||
return ui::EventResult::DidHandle;
|
||||
}
|
||||
case 'i': {
|
||||
m_insert = true;
|
||||
String title = TRY(String::from_cstring("Text Editor (insert)"));
|
||||
if (!m_path.is_empty_path()) title = TRY(String::format("Text Editor - %s (insert)"_sv, m_path.name().chars()));
|
||||
ui::App::the().main_window()->set_title(title.view());
|
||||
return ui::EventResult::DidHandle;
|
||||
}
|
||||
case 'f': {
|
||||
auto result = save_file();
|
||||
if (result.has_error())
|
||||
{
|
||||
os::eprintln("TextEditor: failed to save file: %s", result.error_string());
|
||||
return ui::EventResult::DidNotHandle;
|
||||
}
|
||||
os::println("TextEditor: buffer saved to %s successfully", m_path.name().chars());
|
||||
return ui::EventResult::DidNotHandle;
|
||||
}
|
||||
default: return ui::EventResult::DidNotHandle;
|
||||
}
|
||||
}
|
||||
|
||||
Result<void> EditorWidget::save_file()
|
||||
{
|
||||
if (m_path.is_empty_path())
|
||||
{
|
||||
os::eprintln("TextEditor: no file to save buffer to!");
|
||||
return err(ENOENT);
|
||||
}
|
||||
|
||||
auto file = TRY(os::File::open(m_path, os::File::WriteOnly));
|
||||
return file->write(m_data);
|
||||
}
|
||||
|
||||
Result<void> EditorWidget::draw(ui::Canvas& canvas)
|
||||
{
|
||||
int visible_lines = canvas.height / m_font->height();
|
||||
int visible_columns = canvas.width / m_font->width();
|
||||
|
||||
if ((usize)visible_lines > m_lines.size()) visible_lines = static_cast<int>(m_lines.size());
|
||||
|
||||
for (int i = 0; i < visible_lines; i++)
|
||||
{
|
||||
auto line = m_lines[i];
|
||||
if (line.begin == line.end) continue;
|
||||
|
||||
auto slice = TRY(m_data.slice(line.begin, line.end - line.begin));
|
||||
auto string = TRY(
|
||||
String::from_string_view(StringView::from_fixed_size_cstring((const char*)slice, line.end - line.begin)));
|
||||
|
||||
Utf8StringDecoder decoder(string.chars());
|
||||
wchar_t buf[4096];
|
||||
decoder.decode(buf, sizeof(buf)).release_value();
|
||||
|
||||
int characters_to_render = (int)wcslen(buf);
|
||||
|
||||
for (int j = 0; j < visible_columns && j < characters_to_render; j++)
|
||||
{
|
||||
auto subcanvas =
|
||||
canvas.subcanvas({ j * m_font->width(), i * m_font->height(), m_font->width(), m_font->height() });
|
||||
m_font->render(buf[j], ui::WHITE, subcanvas);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the cursor
|
||||
if (m_cursor_position.x < visible_columns && m_cursor_position.y < visible_lines && m_cursor_activated)
|
||||
{
|
||||
canvas
|
||||
.subcanvas(
|
||||
{ m_cursor_position.x * m_font->width(), m_cursor_position.y * m_font->height(), 1, m_font->height() })
|
||||
.fill(ui::WHITE);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void> EditorWidget::recalculate_lines()
|
||||
{
|
||||
m_lines.clear();
|
||||
|
||||
Line l;
|
||||
l.begin = 0;
|
||||
for (usize i = 0; i < m_data.size(); i++)
|
||||
{
|
||||
if (m_data.data()[i] == '\n')
|
||||
{
|
||||
l.end = i;
|
||||
TRY(m_lines.try_append(l));
|
||||
l.begin = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
l.end = m_data.size();
|
||||
TRY(m_lines.try_append(l));
|
||||
|
||||
recalculate_cursor_position();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void EditorWidget::tick_cursor()
|
||||
{
|
||||
m_cursor_activated = !m_cursor_activated;
|
||||
|
||||
ui::App::the().main_window()->draw();
|
||||
}
|
||||
|
||||
void EditorWidget::recalculate_cursor_position()
|
||||
{
|
||||
if (m_cursor == 0) m_cursor_position = { 0, 0 };
|
||||
|
||||
for (int i = 0; i < (int)m_lines.size(); i++)
|
||||
{
|
||||
auto line = m_lines[i];
|
||||
if (m_cursor >= line.begin && m_cursor <= line.end)
|
||||
{
|
||||
m_cursor_position.x = (int)(m_cursor - line.begin);
|
||||
m_cursor_position.y = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
unreachable();
|
||||
}
|
||||
|
||||
void EditorWidget::recalculate_cursor_index()
|
||||
{
|
||||
m_cursor = m_lines[m_cursor_position.y].begin + m_cursor_position.x;
|
||||
if (m_cursor > m_lines[m_cursor_position.y].end)
|
||||
{
|
||||
m_cursor = m_lines[m_cursor_position.y].end;
|
||||
recalculate_cursor_position();
|
||||
}
|
||||
}
|
55
editor/EditorWidget.h
Normal file
55
editor/EditorWidget.h
Normal file
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* @file EditorWidget.h
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief Multiline text editing widget.
|
||||
*
|
||||
* @copyright Copyright (c) 2024, the Luna authors.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <luna/String.h>
|
||||
#include <os/Timer.h>
|
||||
#include <ui/Font.h>
|
||||
#include <ui/Widget.h>
|
||||
|
||||
class EditorWidget : public ui::Widget
|
||||
{
|
||||
public:
|
||||
EditorWidget(SharedPtr<ui::Font> font);
|
||||
|
||||
Result<void> load_file(const os::Path& path);
|
||||
|
||||
Result<void> save_file();
|
||||
|
||||
Result<ui::EventResult> handle_key_event(const ui::KeyEventRequest& request) override;
|
||||
|
||||
Result<void> draw(ui::Canvas& canvas) override;
|
||||
|
||||
private:
|
||||
SharedPtr<ui::Font> m_font;
|
||||
|
||||
Buffer m_data;
|
||||
|
||||
struct Line
|
||||
{
|
||||
usize begin;
|
||||
usize end;
|
||||
};
|
||||
Vector<Line> m_lines;
|
||||
|
||||
usize m_cursor { 0 };
|
||||
ui::Point m_cursor_position { 0, 0 };
|
||||
|
||||
OwnedPtr<os::Timer> m_cursor_timer;
|
||||
bool m_cursor_activated = true;
|
||||
|
||||
bool m_insert = false;
|
||||
|
||||
os::Path m_path { AT_FDCWD };
|
||||
|
||||
void tick_cursor();
|
||||
|
||||
Result<void> recalculate_lines();
|
||||
void recalculate_cursor_position();
|
||||
void recalculate_cursor_index();
|
||||
};
|
39
editor/main.cpp
Normal file
39
editor/main.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* @file main.cpp
|
||||
* @author apio (cloudapio.eu)
|
||||
* @brief Graphical text editor.
|
||||
*
|
||||
* @copyright Copyright (c) 2024, the Luna authors.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "EditorWidget.h"
|
||||
#include <os/ArgumentParser.h>
|
||||
#include <ui/App.h>
|
||||
|
||||
Result<int> luna_main(int argc, char** argv)
|
||||
{
|
||||
StringView path;
|
||||
|
||||
os::ArgumentParser parser;
|
||||
parser.add_description("A graphical text editor"_sv);
|
||||
parser.add_system_program_info("editor"_sv);
|
||||
parser.add_positional_argument(path, "path", false);
|
||||
parser.parse(argc, argv);
|
||||
|
||||
ui::App app;
|
||||
TRY(app.init());
|
||||
|
||||
auto* window = TRY(ui::Window::create(ui::Rect { 200, 300, 600, 600 }));
|
||||
window->set_background(ui::Color::from_rgb(40, 40, 40));
|
||||
window->set_title("Text Editor");
|
||||
app.set_main_window(window);
|
||||
|
||||
auto* editor = TRY(make<EditorWidget>(ui::Font::default_font()));
|
||||
if (!path.is_empty()) TRY(editor->load_file(path));
|
||||
window->set_main_widget(*editor);
|
||||
|
||||
window->draw();
|
||||
|
||||
return app.run();
|
||||
}
|
@ -117,6 +117,30 @@ namespace moon
|
||||
K_CH46, // .
|
||||
K_CH47, // /
|
||||
K_CH48, // Space
|
||||
// Multimedia keys
|
||||
K_MediaPrev,
|
||||
K_MediaNext,
|
||||
K_MediaMute,
|
||||
K_MediaCalc,
|
||||
K_MediaPlay,
|
||||
K_MediaStop,
|
||||
K_MediaVolDown,
|
||||
K_MediaVolUp,
|
||||
// WWW keys
|
||||
K_WWWHome,
|
||||
K_WWWSearch,
|
||||
K_WWWFavorites,
|
||||
K_WWWRefresh,
|
||||
K_WWWStop,
|
||||
K_WWWForward,
|
||||
K_WWWBack,
|
||||
K_WWWMyComputer,
|
||||
K_WWWEmail,
|
||||
K_WWWSelect,
|
||||
// Power keys
|
||||
K_ACPIPower,
|
||||
K_ACPISleep,
|
||||
K_ACPIWake,
|
||||
// Unknown key
|
||||
K_Unknown,
|
||||
};
|
||||
|
@ -1,24 +1,16 @@
|
||||
#pragma once
|
||||
#include "api/Keyboard.h"
|
||||
#include <luna/Option.h>
|
||||
#include <luna/Vector.h>
|
||||
|
||||
namespace Keyboard
|
||||
{
|
||||
struct TTYKeyboardState
|
||||
{
|
||||
bool ignore_next { false };
|
||||
bool left_shift { false };
|
||||
bool right_shift { false };
|
||||
bool left_control { false };
|
||||
bool capslock { false };
|
||||
};
|
||||
|
||||
struct KeyboardState
|
||||
{
|
||||
bool ignore_next { false };
|
||||
Vector<u8> key_state;
|
||||
bool parsing_ext { false };
|
||||
bool parsing_pause { false };
|
||||
};
|
||||
|
||||
Option<char> decode_scancode_tty(u8 scancode, TTYKeyboardState& state);
|
||||
|
||||
Option<moon::KeyboardPacket> decode_scancode(u8 scancode, KeyboardState& state);
|
||||
}
|
||||
|
@ -13,104 +13,9 @@ static bool is_key_released(u8& scancode)
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr u8 EXTENDED_KEY_CODE = 0xe0;
|
||||
constexpr u8 LEFT_SHIFT = 0x2a;
|
||||
constexpr u8 RIGHT_SHIFT = 0x36;
|
||||
constexpr u8 CAPS_LOCK = 0x3a;
|
||||
|
||||
constexpr u8 LEFT_CONTROL = 0x1D;
|
||||
constexpr u8 LEFT_ALT = 0x38;
|
||||
constexpr u8 F11 = 0x57;
|
||||
constexpr u8 F12 = 0x58;
|
||||
|
||||
static bool should_ignore_key(u8 scancode)
|
||||
{
|
||||
return (scancode > 0x3A && scancode < 0x47) || scancode == F11 || scancode == F12;
|
||||
}
|
||||
|
||||
constexpr char key_table[] = {
|
||||
'\0',
|
||||
'\1', // escape
|
||||
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',
|
||||
'\t', // tab
|
||||
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',
|
||||
'\0', // left ctrl
|
||||
'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`',
|
||||
'\0', // left shift
|
||||
'\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/',
|
||||
'\0', // right shift
|
||||
'*', // keypad *
|
||||
'\0', // left alt
|
||||
' ',
|
||||
'\0', // caps lock
|
||||
'\0', // f1
|
||||
'\0', // f2
|
||||
'\0', // f3
|
||||
'\0', // f4
|
||||
'\0', // f5
|
||||
'\0', // f6
|
||||
'\0', // f7
|
||||
'\0', // f8
|
||||
'\0', // f9
|
||||
'\0', // f10
|
||||
'\0', // num lock
|
||||
'\0', // scroll lock
|
||||
'7', // keypad 7
|
||||
'8', // keypad 8
|
||||
'9', // keypad 9
|
||||
'-', // keypad -
|
||||
'4', // keypad 4
|
||||
'5', // keypad 5
|
||||
'6', // keypad 6
|
||||
'+', // keypad +
|
||||
'1', // keypad 1
|
||||
'2', // keypad 2
|
||||
'3', // keypad 3
|
||||
'0', // keypad 0
|
||||
'.', // keypad .
|
||||
};
|
||||
|
||||
constexpr char shifted_key_table[] = {
|
||||
'\0',
|
||||
'\1', // escape
|
||||
'!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b',
|
||||
'\t', // tab
|
||||
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n',
|
||||
'\0', // left ctrl
|
||||
'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~',
|
||||
'\0', // left shift
|
||||
'|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?',
|
||||
'\0', // right shift
|
||||
'*', // keypad *
|
||||
'\0', // left alt
|
||||
' ',
|
||||
'\0', // caps lock
|
||||
'\0', // f1
|
||||
'\0', // f2
|
||||
'\0', // f3
|
||||
'\0', // f4
|
||||
'\0', // f5
|
||||
'\0', // f6
|
||||
'\0', // f7
|
||||
'\0', // f8
|
||||
'\0', // f9
|
||||
'\0', // f10
|
||||
'\0', // num lock
|
||||
'\0', // scroll lock
|
||||
'7', // keypad 7
|
||||
'8', // keypad 8
|
||||
'9', // keypad 9
|
||||
'-', // keypad -
|
||||
'4', // keypad 4
|
||||
'5', // keypad 5
|
||||
'6', // keypad 6
|
||||
'+', // keypad +
|
||||
'1', // keypad 1
|
||||
'2', // keypad 2
|
||||
'3', // keypad 3
|
||||
'0', // keypad 0
|
||||
'.', // keypad .
|
||||
};
|
||||
constexpr static u8 print_screen_pressed[] = { 0xe0, 0x2a, 0xe0, 0x37 };
|
||||
constexpr static u8 print_screen_released[] = { 0xe0, 0xb7, 0xe0, 0xaa };
|
||||
constexpr static u8 pause[] = { 0xe1, 0x1d, 0x45, 0xe1, 0x9d, 0xc5 };
|
||||
|
||||
using namespace moon;
|
||||
|
||||
@ -176,95 +81,113 @@ constexpr KeyCode keycode_table[] = {
|
||||
K_F11, K_F12,
|
||||
};
|
||||
|
||||
static bool is_shifted(const Keyboard::TTYKeyboardState& state)
|
||||
{
|
||||
if (state.capslock) return !(state.left_shift || state.right_shift);
|
||||
return state.left_shift || state.right_shift;
|
||||
}
|
||||
constexpr KeyCode extended_keycode_table[] = {
|
||||
K_MediaPrev, K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Unknown,
|
||||
K_Unknown, K_Unknown, K_MediaNext, K_Unknown, K_Unknown, K_KeypadEnter, K_RightControl,
|
||||
K_Unknown, K_Unknown, K_MediaMute, K_MediaCalc, K_MediaPlay, K_Unknown, K_MediaStop,
|
||||
K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Unknown,
|
||||
K_Unknown, K_Unknown, K_MediaVolDown, K_Unknown, K_MediaVolUp, K_Unknown, K_WWWHome,
|
||||
K_Unknown, K_Unknown, K_KeypadDiv, K_Unknown, K_Unknown, K_RightAlt, K_Unknown,
|
||||
K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Unknown,
|
||||
K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Home,
|
||||
K_UpArrow, K_PageUp, K_Unknown, K_LeftArrow, K_Unknown, K_RightArrow, K_Unknown,
|
||||
K_End, K_DownArrow, K_PageDown, K_Insert, K_Delete, K_Unknown, K_Unknown,
|
||||
K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Unknown, K_Super, K_Super,
|
||||
K_Menu, K_ACPIPower, K_ACPISleep, K_Unknown, K_Unknown, K_Unknown, K_ACPIWake,
|
||||
K_Unknown, K_WWWSearch, K_WWWFavorites, K_WWWRefresh, K_WWWStop, K_WWWForward, K_WWWBack,
|
||||
K_WWWMyComputer, K_WWWEmail, K_WWWSelect
|
||||
};
|
||||
|
||||
namespace Keyboard
|
||||
{
|
||||
Option<char> decode_scancode_tty(u8 scancode, TTYKeyboardState& state)
|
||||
{
|
||||
if (state.ignore_next)
|
||||
{
|
||||
state.ignore_next = false;
|
||||
return {};
|
||||
}
|
||||
|
||||
// FIXME: Support extended scancodes.
|
||||
if (scancode == EXTENDED_KEY_CODE)
|
||||
{
|
||||
state.ignore_next = true;
|
||||
return {};
|
||||
}
|
||||
|
||||
bool released = is_key_released(scancode);
|
||||
|
||||
if (scancode == LEFT_SHIFT)
|
||||
{
|
||||
state.left_shift = !released;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (scancode == RIGHT_SHIFT)
|
||||
{
|
||||
state.right_shift = !released;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (scancode == CAPS_LOCK)
|
||||
{
|
||||
if (!released) state.capslock = !state.capslock;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (scancode == LEFT_CONTROL)
|
||||
{
|
||||
if (released) state.left_control = false;
|
||||
else
|
||||
state.left_control = true;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (should_ignore_key(scancode)) return {};
|
||||
|
||||
if (released) return {};
|
||||
|
||||
if (state.left_control)
|
||||
{
|
||||
char key;
|
||||
if (is_shifted(state)) key = shifted_key_table[scancode];
|
||||
else
|
||||
key = key_table[scancode];
|
||||
if (_islower(key)) key = (char)_toupper(key);
|
||||
if (_isupper(key)) return key - 0x40;
|
||||
if (key == '@') return key - 0x40;
|
||||
if (key > 'Z' && key < '`') return key - 0x40;
|
||||
if (key == '?') return 0x7f;
|
||||
}
|
||||
|
||||
if (is_shifted(state)) return shifted_key_table[scancode];
|
||||
return key_table[scancode];
|
||||
}
|
||||
|
||||
Option<KeyboardPacket> decode_scancode(u8 scancode, KeyboardState& state)
|
||||
{
|
||||
if (state.ignore_next)
|
||||
if (state.parsing_pause)
|
||||
{
|
||||
state.ignore_next = false;
|
||||
if (state.key_state.size() < 6) state.key_state.try_append(scancode).release_value();
|
||||
|
||||
if (state.key_state.size() == 6)
|
||||
{
|
||||
state.parsing_pause = state.parsing_ext = false;
|
||||
if (!memcmp(state.key_state.data(), pause, 6))
|
||||
{
|
||||
state.key_state.clear_data();
|
||||
return KeyboardPacket { K_Pause, false };
|
||||
}
|
||||
|
||||
state.key_state.clear_data();
|
||||
return KeyboardPacket { K_Unknown, false };
|
||||
}
|
||||
}
|
||||
|
||||
if (state.parsing_ext)
|
||||
{
|
||||
if (scancode == 0xe0 && state.key_state.size() == 2)
|
||||
{
|
||||
state.key_state.try_append(scancode).release_value();
|
||||
return {};
|
||||
}
|
||||
|
||||
if (state.key_state.size() == 3)
|
||||
{
|
||||
state.key_state.try_append(scancode).release_value();
|
||||
if (!memcmp(state.key_state.data(), print_screen_pressed, 4))
|
||||
{
|
||||
state.parsing_ext = false;
|
||||
state.key_state.clear_data();
|
||||
return KeyboardPacket { K_PrtScr, false };
|
||||
}
|
||||
|
||||
if (!memcmp(state.key_state.data(), print_screen_released, 4))
|
||||
{
|
||||
state.parsing_ext = false;
|
||||
state.key_state.clear_data();
|
||||
return KeyboardPacket { K_PrtScr, true };
|
||||
}
|
||||
|
||||
state.parsing_ext = false;
|
||||
state.key_state.clear_data();
|
||||
return KeyboardPacket { K_Unknown, false };
|
||||
}
|
||||
|
||||
if (scancode == 0x2a || scancode == 0xb7)
|
||||
{
|
||||
state.key_state.try_append(scancode).release_value();
|
||||
return {};
|
||||
}
|
||||
|
||||
bool released = is_key_released(scancode);
|
||||
|
||||
KeyCode key = KeyCode::K_Unknown;
|
||||
|
||||
if (scancode <= 0x6d) key = extended_keycode_table[scancode - 0x10];
|
||||
|
||||
state.parsing_ext = false;
|
||||
state.key_state.clear_data();
|
||||
|
||||
return KeyboardPacket { key, released };
|
||||
}
|
||||
|
||||
if (scancode == 0xe0)
|
||||
{
|
||||
state.parsing_ext = true;
|
||||
state.key_state.try_append(scancode).release_value();
|
||||
return {};
|
||||
}
|
||||
|
||||
// FIXME: Support extended scancodes.
|
||||
if (scancode == EXTENDED_KEY_CODE)
|
||||
if (scancode == 0xe1)
|
||||
{
|
||||
state.ignore_next = true;
|
||||
state.parsing_pause = true;
|
||||
state.key_state.try_append(scancode).release_value();
|
||||
return {};
|
||||
}
|
||||
|
||||
bool released = is_key_released(scancode);
|
||||
|
||||
return KeyboardPacket { keycode_table[scancode], released };
|
||||
KeyCode key = KeyCode::K_Unknown;
|
||||
|
||||
if (scancode <= 0x58) key = keycode_table[scancode];
|
||||
|
||||
return KeyboardPacket { key, released };
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ template <typename T> class LinkedListNode
|
||||
{
|
||||
using SelfType = LinkedListNode<T>;
|
||||
|
||||
private:
|
||||
protected:
|
||||
SelfType* m_next_node;
|
||||
SelfType* m_last_node;
|
||||
|
||||
@ -284,3 +284,29 @@ template <typename T> class LinkedList
|
||||
|
||||
usize m_count = 0;
|
||||
};
|
||||
|
||||
struct Tag : public LinkedListNode<Tag>
|
||||
{
|
||||
public:
|
||||
Tag(void* ptr) : m_ptr(ptr)
|
||||
{
|
||||
}
|
||||
|
||||
~Tag()
|
||||
{
|
||||
detach_from_list();
|
||||
}
|
||||
|
||||
void* ptr()
|
||||
{
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
template <typename T> T& as()
|
||||
{
|
||||
return *static_cast<T*>(m_ptr);
|
||||
}
|
||||
|
||||
private:
|
||||
void* m_ptr;
|
||||
};
|
||||
|
@ -13,7 +13,7 @@ template <class T> struct __remove_const_impl<T const>
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <class T> using RemoveConst = __remove_const_impl<T>::type;
|
||||
template <class T> using RemoveConst = typename __remove_const_impl<T>::type;
|
||||
|
||||
template <class T> struct __remove_volatile_impl
|
||||
{
|
||||
@ -25,7 +25,7 @@ template <class T> struct __remove_volatile_impl<T volatile>
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <class T> using RemoveVolatile = __remove_volatile_impl<T>::type;
|
||||
template <class T> using RemoveVolatile = typename __remove_volatile_impl<T>::type;
|
||||
|
||||
template <class T> using RemoveCV = RemoveVolatile<RemoveConst<T>>;
|
||||
|
||||
@ -44,6 +44,6 @@ template <class T> struct __remove_ref_impl<T&&>
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <class T> using RemoveReference = __remove_ref_impl<T>::type;
|
||||
template <class T> using RemoveReference = typename __remove_ref_impl<T>::type;
|
||||
|
||||
template <class T> using RemoveCVReference = RemoveCV<RemoveReference<T>>;
|
||||
|
@ -5,7 +5,7 @@ dependencies=(gmp)
|
||||
|
||||
# Download options
|
||||
format="tar"
|
||||
url="https://www.mpfr.org/mpfr-current/mpfr-$version.tar.gz"
|
||||
url="https://www.mpfr.org/mpfr-$version/mpfr-$version.tar.gz"
|
||||
output="mpfr-$version.tar.gz"
|
||||
sha256sum="f1cc1c6bb14d18f0c61cc416e083f5e697b6e0e3cf9630b9b33e8e483fc960f0"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user