Compare commits

...

4 Commits

Author SHA1 Message Date
cee677b1f7
libui: Make App::process_events() private
Previously this was public as some applications (mainly terminal) needed to run some code in-between events, but this is no longer needed, due to the EventLoop providing these services (timers and file descriptor notifiers)
2024-01-08 19:13:47 +01:00
e4c9211edc
terminal: Use os::Timer and EventLoop::register_fd_listener 2024-01-08 19:04:04 +01:00
6bf8225f63
libos: Add Timer::reset, restart and stop 2024-01-08 19:01:59 +01:00
1223c6c20b
libos: Add FIXME to EventLoop 2024-01-08 19:01:39 +01:00
8 changed files with 83 additions and 57 deletions

View File

@ -22,15 +22,21 @@ namespace os
static Result<OwnedPtr<Timer>> create_singleshot(unsigned int milliseconds, Action&& action); static Result<OwnedPtr<Timer>> create_singleshot(unsigned int milliseconds, Action&& action);
static Result<OwnedPtr<Timer>> create_repeating(unsigned int milliseconds, Action&& action); static Result<OwnedPtr<Timer>> create_repeating(unsigned int milliseconds, Action&& action);
void restart();
void reset(unsigned int milliseconds);
void stop();
~Timer(); ~Timer();
private: private:
timer_t m_timerid { -1 }; timer_t m_timerid { -1 };
bool m_repeating; bool m_repeating;
bool m_stopped { false };
struct timespec m_interval; struct timespec m_interval;
Action m_action; Action m_action;
void check_if_should_invoke_action(); void check_if_should_invoke_action();
void readd_if_necessary();
Timer() = default; Timer() = default;

View File

@ -44,6 +44,7 @@ namespace os
return *s_the; return *s_the;
} }
// FIXME: Support lambdas (by using os::Action). This means that os::Action needs variable parameter support.
Result<void> EventLoop::register_fd_listener(int fd, void (*listener)(int, int)) Result<void> EventLoop::register_fd_listener(int fd, void (*listener)(int, int))
{ {
TRY(m_fd_listeners.try_set(fd, listener)); TRY(m_fd_listeners.try_set(fd, listener));

View File

@ -54,21 +54,45 @@ namespace os
return timer; return timer;
} }
void Timer::restart()
{
struct itimerspec itimer = { .it_interval = { .tv_sec = 0, .tv_nsec = 0 }, .it_value = m_interval };
timer_settime(m_timerid, 0, &itimer, nullptr);
readd_if_necessary();
}
void Timer::reset(unsigned int milliseconds)
{
m_interval = { .tv_sec = milliseconds / 1000, .tv_nsec = (milliseconds % 1000) * 1'000'000 };
restart();
}
void Timer::stop()
{
EventLoop::the().m_timer_list.remove(this);
m_stopped = true;
struct itimerspec itimer;
memset(&itimer, 0, sizeof(itimer));
timer_settime(m_timerid, 0, &itimer, nullptr);
}
void Timer::check_if_should_invoke_action() void Timer::check_if_should_invoke_action()
{ {
struct itimerspec remaining; struct itimerspec remaining;
if (timer_gettime(m_timerid, &remaining) < 0) return; if (timer_gettime(m_timerid, &remaining) < 0) return;
if (!remaining.it_value.tv_sec && !remaining.it_value.tv_nsec) if (!remaining.it_value.tv_sec && !remaining.it_value.tv_nsec && !m_stopped)
{ {
// Timer expired! // Timer expired!
m_action(); m_action();
if (!m_repeating) if (!m_repeating)
{ {
timer_delete(m_timerid);
EventLoop::the().m_timer_list.remove(this); EventLoop::the().m_timer_list.remove(this);
m_timerid = -1; m_stopped = true;
} }
else else
{ {
@ -79,6 +103,15 @@ namespace os
} }
} }
void Timer::readd_if_necessary()
{
if (m_stopped)
{
m_stopped = false;
EventLoop::the().m_timer_list.append(this);
}
}
Timer::~Timer() Timer::~Timer()
{ {
if (m_timerid >= 0) timer_delete(m_timerid); if (m_timerid >= 0) timer_delete(m_timerid);

View File

@ -53,8 +53,6 @@ namespace ui
Result<void> handle_ipc_event(u8 id); Result<void> handle_ipc_event(u8 id);
bool process_events();
static App& the(); static App& the();
private: private:
@ -65,6 +63,10 @@ namespace ui
bool m_should_close { false }; bool m_should_close { false };
os::EventLoop m_loop; os::EventLoop m_loop;
bool process_events();
Window* find_window(int id); Window* find_window(int id);
friend void handle_socket_event(int, int);
}; };
} }

View File

@ -19,14 +19,14 @@ Result<void> handle_ipc_client_event(os::LocalClient&, u8 id)
return ui::App::the().handle_ipc_event(id); return ui::App::the().handle_ipc_event(id);
} }
void handle_socket_event(int, int status)
{
if (status & POLLHUP) ui::App::the().set_should_close(true);
if (status & POLLIN) { ui::App::the().process_events(); }
}
namespace ui namespace ui
{ {
void handle_socket_event(int, int status)
{
if (status & POLLHUP) ui::App::the().set_should_close(true);
if (status & POLLIN) { ui::App::the().process_events(); }
}
App* App::s_app { nullptr }; App* App::s_app { nullptr };
App::App() App::App()

View File

@ -29,20 +29,14 @@ static constexpr auto BRIGHT_MAGENTA = ui::Color::from_u32(0xffff00ff);
static constexpr auto BRIGHT_CYAN = ui::Color::from_u32(0xff00ffff); static constexpr auto BRIGHT_CYAN = ui::Color::from_u32(0xff00ffff);
static constexpr auto BRIGHT_GRAY = ui::Color::from_u32(0xffffffff); static constexpr auto BRIGHT_GRAY = ui::Color::from_u32(0xffffffff);
static long get_time_in_milliseconds()
{
struct timespec ts;
check(clock_gettime(CLOCK_REALTIME, &ts) == 0);
return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}
static void sigchld_handler(int) static void sigchld_handler(int)
{ {
wait(NULL); wait(NULL);
ui::App::the().set_should_close(true); ui::App::the().set_should_close(true);
} }
TerminalWidget* s_main = nullptr;
Result<void> TerminalWidget::init(char* const* args) Result<void> TerminalWidget::init(char* const* args)
{ {
m_font = ui::Font::default_font(); m_font = ui::Font::default_font();
@ -51,6 +45,8 @@ Result<void> TerminalWidget::init(char* const* args)
m_terminal_canvas = ui::App::the().main_window()->canvas(); m_terminal_canvas = ui::App::the().main_window()->canvas();
m_terminal_canvas.fill(ui::BLACK); m_terminal_canvas.fill(ui::BLACK);
m_cursor_timer = TRY(os::Timer::create_repeating(500, [this]() { this->tick_cursor(); }));
signal(SIGCHLD, sigchld_handler); signal(SIGCHLD, sigchld_handler);
int master; int master;
@ -64,11 +60,18 @@ Result<void> TerminalWidget::init(char* const* args)
m_pty = master; m_pty = master;
fcntl(master, F_SETFL, O_NONBLOCK); fcntl(m_pty, F_SETFL, O_NONBLOCK);
// FIXME: Find a better solution for this. What if the caller needs several TerminalWidgets in one app? We can't
// just use a singleton, can we?
os::EventLoop::the().register_fd_listener(m_pty, [](int, int) {
check(s_main);
s_main->process();
});
m_child_pid = child; m_child_pid = child;
m_last_cursor_tick = get_time_in_milliseconds(); s_main = this;
return {}; return {};
} }
@ -91,7 +94,7 @@ Result<void> TerminalWidget::draw(ui::Canvas&)
return {}; return {};
} }
Result<bool> TerminalWidget::process() Result<void> TerminalWidget::process()
{ {
char buffer[BUFSIZ]; char buffer[BUFSIZ];
ssize_t nread = read(m_pty, buffer, BUFSIZ); ssize_t nread = read(m_pty, buffer, BUFSIZ);
@ -102,8 +105,6 @@ Result<bool> TerminalWidget::process()
return err(errno); return err(errno);
} }
bool should_update_cursor = tick_cursor();
ssize_t drawn = 0; ssize_t drawn = 0;
for (ssize_t i = 0; i < nread; i++) for (ssize_t i = 0; i < nread; i++)
@ -112,32 +113,23 @@ Result<bool> TerminalWidget::process()
if (did_draw) drawn++; if (did_draw) drawn++;
} }
if (should_update_cursor || drawn > 0) ui::App::the().main_window()->draw(); if (drawn > 0) ui::App::the().main_window()->draw();
return nread == 0; return {};
} }
bool TerminalWidget::tick_cursor() void TerminalWidget::tick_cursor()
{ {
if (!m_cursor_enabled) return false; if (!m_cursor_enabled) return;
long now = get_time_in_milliseconds(); m_cursor_timer->restart();
long diff = now - m_last_cursor_tick;
m_last_cursor_tick = now;
m_current_cursor_timeout -= (int)diff;
if (m_current_cursor_timeout <= 0)
{
m_current_cursor_timeout = CURSOR_TIMEOUT;
m_cursor_activated = !m_cursor_activated; m_cursor_activated = !m_cursor_activated;
if (m_cursor_activated) draw_cursor(); if (m_cursor_activated) draw_cursor();
else else
erase_current_char(); erase_current_char();
return true;
}
return false; ui::App::the().main_window()->draw();
} }
void TerminalWidget::draw_glyph(wchar_t c, int x, int y) void TerminalWidget::draw_glyph(wchar_t c, int x, int y)
@ -471,7 +463,7 @@ bool TerminalWidget::put_code_point(wchar_t c)
if (should_draw_cursor) if (should_draw_cursor)
{ {
m_current_cursor_timeout = CURSOR_TIMEOUT; m_cursor_timer->restart();
m_cursor_activated = true; m_cursor_activated = true;
draw_cursor(); draw_cursor();
} }

View File

@ -2,6 +2,7 @@
#include <luna/EscapeSequence.h> #include <luna/EscapeSequence.h>
#include <luna/Utf8.h> #include <luna/Utf8.h>
#include <luna/Vector.h> #include <luna/Vector.h>
#include <os/Timer.h>
#include <stdio.h> #include <stdio.h>
#include <termios.h> #include <termios.h>
#include <ui/Font.h> #include <ui/Font.h>
@ -16,8 +17,6 @@ class TerminalWidget : public ui::Widget
Result<void> draw(ui::Canvas& canvas) override; Result<void> draw(ui::Canvas& canvas) override;
Result<bool> process();
private: private:
ui::Canvas m_terminal_canvas; ui::Canvas m_terminal_canvas;
Vector<u8> m_line_buffer; Vector<u8> m_line_buffer;
@ -29,9 +28,7 @@ class TerminalWidget : public ui::Widget
SharedPtr<ui::Font> m_font; SharedPtr<ui::Font> m_font;
SharedPtr<ui::Font> m_bold_font; SharedPtr<ui::Font> m_bold_font;
static constexpr int CURSOR_TIMEOUT = 500; OwnedPtr<os::Timer> m_cursor_timer;
int m_current_cursor_timeout = CURSOR_TIMEOUT;
bool m_cursor_activated = false; bool m_cursor_activated = false;
bool m_cursor_enabled = true; bool m_cursor_enabled = true;
@ -45,7 +42,7 @@ class TerminalWidget : public ui::Widget
ui::Color m_foreground_color { ui::WHITE }; ui::Color m_foreground_color { ui::WHITE };
ui::Color m_background_color { ui::BLACK }; ui::Color m_background_color { ui::BLACK };
bool tick_cursor(); void tick_cursor();
Utf8StateDecoder m_decoder; Utf8StateDecoder m_decoder;
Option<EscapeSequenceParser> m_escape_parser; Option<EscapeSequenceParser> m_escape_parser;
@ -63,4 +60,5 @@ class TerminalWidget : public ui::Widget
bool handle_escape_sequence(wchar_t c); bool handle_escape_sequence(wchar_t c);
Result<bool> putchar(char c); Result<bool> putchar(char c);
bool put_code_point(wchar_t c); bool put_code_point(wchar_t c);
Result<void> process();
}; };

View File

@ -20,11 +20,5 @@ Result<int> luna_main(int argc, char** argv)
window->draw(); window->draw();
while (app.process_events()) return app.run();
{
bool should_sleep = TRY(terminal.process());
if (should_sleep) usleep(10000);
}
return 0;
} }