Compare commits
No commits in common. "cee677b1f73b56db8aaeb9225d0035deb8ac18b2" and "fd402083d7d65092059002c44e70e22bb9e54e23" have entirely different histories.
cee677b1f7
...
fd402083d7
@ -22,21 +22,15 @@ 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;
|
||||||
|
|
||||||
|
@ -44,7 +44,6 @@ 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));
|
||||||
|
@ -54,45 +54,21 @@ 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 && !m_stopped)
|
if (!remaining.it_value.tv_sec && !remaining.it_value.tv_nsec)
|
||||||
{
|
{
|
||||||
// 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_stopped = true;
|
m_timerid = -1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -103,15 +79,6 @@ 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);
|
||||||
|
@ -53,6 +53,8 @@ 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:
|
||||||
@ -63,10 +65,6 @@ 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);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ui
|
|
||||||
{
|
|
||||||
void handle_socket_event(int, int status)
|
void handle_socket_event(int, int status)
|
||||||
{
|
{
|
||||||
if (status & POLLHUP) ui::App::the().set_should_close(true);
|
if (status & POLLHUP) ui::App::the().set_should_close(true);
|
||||||
if (status & POLLIN) { ui::App::the().process_events(); }
|
if (status & POLLIN) { ui::App::the().process_events(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
App* App::s_app { nullptr };
|
App* App::s_app { nullptr };
|
||||||
|
|
||||||
App::App()
|
App::App()
|
||||||
|
@ -29,14 +29,20 @@ 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();
|
||||||
@ -45,8 +51,6 @@ 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;
|
||||||
@ -60,18 +64,11 @@ Result<void> TerminalWidget::init(char* const* args)
|
|||||||
|
|
||||||
m_pty = master;
|
m_pty = master;
|
||||||
|
|
||||||
fcntl(m_pty, F_SETFL, O_NONBLOCK);
|
fcntl(master, 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;
|
||||||
|
|
||||||
s_main = this;
|
m_last_cursor_tick = get_time_in_milliseconds();
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -94,7 +91,7 @@ Result<void> TerminalWidget::draw(ui::Canvas&)
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> TerminalWidget::process()
|
Result<bool> TerminalWidget::process()
|
||||||
{
|
{
|
||||||
char buffer[BUFSIZ];
|
char buffer[BUFSIZ];
|
||||||
ssize_t nread = read(m_pty, buffer, BUFSIZ);
|
ssize_t nread = read(m_pty, buffer, BUFSIZ);
|
||||||
@ -105,6 +102,8 @@ Result<void> 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++)
|
||||||
@ -113,23 +112,32 @@ Result<void> TerminalWidget::process()
|
|||||||
if (did_draw) drawn++;
|
if (did_draw) drawn++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (drawn > 0) ui::App::the().main_window()->draw();
|
if (should_update_cursor || drawn > 0) ui::App::the().main_window()->draw();
|
||||||
|
|
||||||
return {};
|
return nread == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TerminalWidget::tick_cursor()
|
bool TerminalWidget::tick_cursor()
|
||||||
{
|
{
|
||||||
if (!m_cursor_enabled) return;
|
if (!m_cursor_enabled) return false;
|
||||||
|
|
||||||
m_cursor_timer->restart();
|
long now = get_time_in_milliseconds();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
ui::App::the().main_window()->draw();
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TerminalWidget::draw_glyph(wchar_t c, int x, int y)
|
void TerminalWidget::draw_glyph(wchar_t c, int x, int y)
|
||||||
@ -463,7 +471,7 @@ bool TerminalWidget::put_code_point(wchar_t c)
|
|||||||
|
|
||||||
if (should_draw_cursor)
|
if (should_draw_cursor)
|
||||||
{
|
{
|
||||||
m_cursor_timer->restart();
|
m_current_cursor_timeout = CURSOR_TIMEOUT;
|
||||||
m_cursor_activated = true;
|
m_cursor_activated = true;
|
||||||
draw_cursor();
|
draw_cursor();
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
#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>
|
||||||
@ -17,6 +16,8 @@ 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;
|
||||||
@ -28,7 +29,9 @@ 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;
|
||||||
|
|
||||||
OwnedPtr<os::Timer> m_cursor_timer;
|
static constexpr int CURSOR_TIMEOUT = 500;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
@ -42,7 +45,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 };
|
||||||
|
|
||||||
void tick_cursor();
|
bool tick_cursor();
|
||||||
|
|
||||||
Utf8StateDecoder m_decoder;
|
Utf8StateDecoder m_decoder;
|
||||||
Option<EscapeSequenceParser> m_escape_parser;
|
Option<EscapeSequenceParser> m_escape_parser;
|
||||||
@ -60,5 +63,4 @@ 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();
|
|
||||||
};
|
};
|
||||||
|
@ -20,5 +20,11 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
|
|
||||||
window->draw();
|
window->draw();
|
||||||
|
|
||||||
return app.run();
|
while (app.process_events())
|
||||||
|
{
|
||||||
|
bool should_sleep = TRY(terminal.process());
|
||||||
|
if (should_sleep) usleep(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user