Add a display server and graphical user interface #38

Merged
apio merged 103 commits from display-server into main 2023-09-20 18:49:21 +00:00
3 changed files with 167 additions and 29 deletions
Showing only changes of commit 9b1e19ef72 - Show all commits

View File

@ -1,9 +1,11 @@
#include "TerminalWidget.h" #include "TerminalWidget.h"
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <luna/CType.h>
#include <os/File.h> #include <os/File.h>
#include <os/Process.h> #include <os/Process.h>
#include <signal.h> #include <signal.h>
#include <stdlib.h>
#include <ui/App.h> #include <ui/App.h>
#include <unistd.h> #include <unistd.h>
@ -42,44 +44,164 @@ Result<void> TerminalWidget::init(char* const* args)
signal(SIGCHLD, sigchld_handler); signal(SIGCHLD, sigchld_handler);
int infds[2]; int fd = posix_openpt(O_RDWR | O_CLOEXEC);
int outfds[2]; if (fd < 0) return err(errno);
int result = pipe(infds); grantpt(fd);
if (result < 0) return err(errno); unlockpt(fd);
result = pipe(outfds);
if (result < 0) return err(errno);
pid_t child = TRY(os::Process::fork()); pid_t child = TRY(os::Process::fork());
if (child == 0) if (child == 0)
{ {
dup2(infds[0], STDIN_FILENO); int ptsfd = open(ptsname(fd), O_RDWR);
dup2(outfds[1], STDOUT_FILENO);
dup2(outfds[1], STDERR_FILENO);
close(infds[0]); dup2(ptsfd, STDIN_FILENO);
close(infds[1]); dup2(ptsfd, STDOUT_FILENO);
close(outfds[0]); dup2(ptsfd, STDERR_FILENO);
close(outfds[1]);
setpgid(0, 0);
tcsetpgrp(ptsfd, getpid());
close(ptsfd);
execv(args[0], args); execv(args[0], args);
_exit(127); _exit(127);
} }
close(infds[0]); m_pty = fd;
close(outfds[1]);
m_write_fd = infds[1]; fcntl(fd, F_SETFL, O_NONBLOCK);
m_read_fd = outfds[0];
fcntl(m_read_fd, F_SETFL, O_NONBLOCK);
m_child_pid = child; m_child_pid = child;
return {}; return {};
} }
Result<ui::EventResult> TerminalWidget::handle_key_event(const ui::KeyEventRequest& request)
{
if (!request.pressed) return ui::EventResult::DidNotHandle;
query_termios();
bool is_special_character { false };
if (/*is_canonical()*/ true)
{
if (request.letter == m_settings.c_cc[VERASE])
{
auto maybe_char = m_line_buffer.try_pop();
if (maybe_char.has_value() && maybe_char.value())
{
if ((m_settings.c_lflag & ECHO) && (m_settings.c_lflag & ECHOE))
{
put_code_point(L'\b');
if (_iscntrl(maybe_char.value())) put_code_point(L'\b');
if (maybe_char.value() == '\t')
{
put_code_point(L'\b');
put_code_point(L'\b');
}
}
ui::App::the().main_window()->draw();
return ui::EventResult::DidHandle;
}
if ((m_settings.c_lflag & ECHOE)) return ui::EventResult::DidHandle;
else
is_special_character = true;
}
if (request.letter == m_settings.c_cc[VEOF])
{
write(m_pty, m_line_buffer.data(), m_line_buffer.size());
m_line_buffer.clear();
// FIXME: tell the kernel that process may read without blocking.
is_special_character = true;
}
if (m_settings.c_lflag & ISIG)
{
if (request.letter == m_settings.c_cc[VINTR])
{
if (!(m_settings.c_lflag & NOFLSH)) m_line_buffer.clear();
// FIXME: Send SIGINT.
/*if (m_foreground_process_group.has_value())
{
Scheduler::for_each_in_process_group(m_foreground_process_group.value(), [](Thread* thread) {
thread->send_signal(SIGINT);
return true;
});
}*/
is_special_character = true;
}
if (request.letter == m_settings.c_cc[VQUIT])
{
if (!(m_settings.c_lflag & NOFLSH)) m_line_buffer.clear();
// FIXME: Send SIGINT.
/*if (m_foreground_process_group.has_value())
{
Scheduler::for_each_in_process_group(m_foreground_process_group.value(), [](Thread* thread) {
thread->send_signal(SIGQUIT);
return true;
});
}*/
is_special_character = true;
}
}
}
if (!is_special_character)
{
if (/*is_canonical()*/ true)
{
m_line_buffer.try_append((u8)request.letter);
if (request.letter == '\n')
{
write(m_pty, m_line_buffer.data(), m_line_buffer.size());
m_line_buffer.clear();
}
}
else
write(m_pty, &request.letter, 1);
}
if (!(m_settings.c_lflag & ECHO)) return ui::EventResult::DidHandle;
if (_iscntrl(request.letter))
{
if (m_settings.c_lflag & ECHOCTL)
{
bool should_echo = true;
if (request.letter == '\n' || request.letter == '\t' || request.letter == '\0' ||
request.letter == m_settings.c_cc[VEOF])
should_echo = false;
if (should_echo)
{
char caret_notation[3] = { '^', '\0', '\0' };
if (request.letter == 0x7f) caret_notation[1] = '?';
else
caret_notation[1] = request.letter + 0x40;
for (int i = 0; i < 2; i++) { putchar(caret_notation[i]); }
}
else
putchar(request.letter);
}
}
else
putchar(request.letter);
ui::App::the().main_window()->draw();
return ui::EventResult::DidHandle;
}
Result<void> TerminalWidget::draw(ui::Canvas& canvas) Result<void> TerminalWidget::draw(ui::Canvas& canvas)
{ {
canvas.fill((u32*)m_terminal_canvas.ptr, m_terminal_canvas.stride); canvas.fill((u32*)m_terminal_canvas.ptr, m_terminal_canvas.stride);
@ -89,14 +211,14 @@ Result<void> TerminalWidget::draw(ui::Canvas& canvas)
Result<void> TerminalWidget::process() Result<void> TerminalWidget::process()
{ {
char buffer[BUFSIZ]; char buffer[BUFSIZ];
ssize_t nread = read(m_read_fd, buffer, BUFSIZ); ssize_t nread = read(m_pty, buffer, BUFSIZ);
if (nread < 0) if (nread < 0)
{ {
if (errno == EAGAIN) return {}; if (errno == EAGAIN) return {};
return err(errno); return err(errno);
} }
os::eprintln("terminal: read %zd characters, processing...", nread); query_termios();
for (ssize_t i = 0; i < nread; i++) TRY(putchar(buffer[i])); for (ssize_t i = 0; i < nread; i++) TRY(putchar(buffer[i]));
@ -105,6 +227,11 @@ Result<void> TerminalWidget::process()
return {}; return {};
} }
void TerminalWidget::query_termios()
{
tcgetattr(m_pty, &m_settings);
}
void TerminalWidget::draw_glyph(wchar_t c, int x, int y) void TerminalWidget::draw_glyph(wchar_t c, int x, int y)
{ {
auto subcanvas = m_terminal_canvas.subcanvas({ x, y, m_font->width(), m_font->height() }); auto subcanvas = m_terminal_canvas.subcanvas({ x, y, m_font->width(), m_font->height() });
@ -114,7 +241,7 @@ void TerminalWidget::draw_glyph(wchar_t c, int x, int y)
void TerminalWidget::erase_current_line() void TerminalWidget::erase_current_line()
{ {
m_terminal_canvas.subcanvas({ 0, m_y_position, m_rect.width, m_font->height() }); m_terminal_canvas.subcanvas({ 0, m_y_position, m_rect.width, m_font->height() }).fill(ui::BLACK);
} }
void TerminalWidget::scroll() void TerminalWidget::scroll()
@ -143,7 +270,7 @@ void TerminalWidget::next_char()
void TerminalWidget::prev_char() void TerminalWidget::prev_char()
{ {
m_y_position -= m_font->width(); m_x_position -= m_font->width();
} }
void TerminalWidget::erase_current_char() void TerminalWidget::erase_current_char()

View File

@ -3,6 +3,7 @@
#include <luna/Utf8.h> #include <luna/Utf8.h>
#include <luna/Vector.h> #include <luna/Vector.h>
#include <stdio.h> #include <stdio.h>
#include <termios.h>
#include <ui/Font.h> #include <ui/Font.h>
#include <ui/Widget.h> #include <ui/Widget.h>
@ -11,6 +12,8 @@ class TerminalWidget : public ui::Widget
public: public:
Result<void> init(char* const* args); Result<void> init(char* const* args);
Result<ui::EventResult> handle_key_event(const ui::KeyEventRequest& request) override;
Result<void> draw(ui::Canvas& canvas) override; Result<void> draw(ui::Canvas& canvas) override;
Result<void> process(); Result<void> process();
@ -20,10 +23,11 @@ class TerminalWidget : public ui::Widget
private: private:
ui::Canvas m_terminal_canvas; ui::Canvas m_terminal_canvas;
Vector<u8> m_line_buffer; Vector<u8> m_line_buffer;
int m_write_fd; int m_pty;
int m_read_fd;
pid_t m_child_pid; pid_t m_child_pid;
struct termios m_settings;
SharedPtr<ui::Font> m_font; SharedPtr<ui::Font> m_font;
SharedPtr<ui::Font> m_bold_font; SharedPtr<ui::Font> m_bold_font;
@ -41,6 +45,8 @@ 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 query_termios();
Utf8StateDecoder m_decoder; Utf8StateDecoder m_decoder;
Option<EscapeSequenceParser> m_escape_parser; Option<EscapeSequenceParser> m_escape_parser;

View File

@ -1,6 +1,7 @@
#include "TerminalWidget.h" #include "TerminalWidget.h"
#include <os/ArgumentParser.h> #include <os/ArgumentParser.h>
#include <ui/App.h> #include <ui/App.h>
#include <unistd.h>
Result<int> luna_main(int argc, char** argv) Result<int> luna_main(int argc, char** argv)
{ {
@ -8,7 +9,7 @@ Result<int> luna_main(int argc, char** argv)
TRY(app.init(argc, argv)); TRY(app.init(argc, argv));
app.set_nonblocking(); app.set_nonblocking();
auto* window = TRY(ui::Window::create(ui::Rect { 150, 150, 500, 300 })); auto* window = TRY(ui::Window::create(ui::Rect { 150, 150, 640, 400 }));
app.set_main_window(window); app.set_main_window(window);
window->set_background(ui::BLACK); window->set_background(ui::BLACK);
window->set_title("Terminal"); window->set_title("Terminal");
@ -16,12 +17,16 @@ Result<int> luna_main(int argc, char** argv)
TerminalWidget terminal; TerminalWidget terminal;
window->set_main_widget(terminal); window->set_main_widget(terminal);
char* args[] = { "/bin/sh", "-i", nullptr }; char* args[] = { "/bin/sh", nullptr };
TRY(terminal.init(args)); TRY(terminal.init(args));
window->draw(); window->draw();
while (app.process_events()) TRY(terminal.process()); while (app.process_events())
{
TRY(terminal.process());
usleep(10000);
}
terminal.quit(); terminal.quit();