Squash commits for public release

This commit is contained in:
2025-02-12 09:54:05 -05:00
commit 7118adc514
1108 changed files with 80873 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
APPS += TERMINAL
TERMINAL_NAME = terminal
TERMINAL_LIBS = cxx ui
TERMINAL_INSTALL_PATH = bin/

View File

@@ -0,0 +1,62 @@
#include "TerminalView.h"
#include "TerminalViewController.h"
#include <csignal>
#include <libui/AppDelegate.h>
static int shell_pid = 0;
int setup_shell()
{
int ptmx = posix_openpt(O_RDONLY);
if (ptmx < 0) {
std::abort();
}
int f = fork();
if (f == 0) {
char* pname = ptsname(ptmx);
if (!pname) {
return -1;
}
close(0);
close(1);
close(2);
open(pname, O_RDONLY);
open(pname, O_WRONLY);
open(pname, O_WRONLY);
execlp("/bin/tinysh", "/bin/tinysh", NULL);
std::abort();
}
shell_pid = f;
return ptmx;
}
class AppDelegate : public UI::AppDelegate {
public:
AppDelegate() = default;
virtual ~AppDelegate() = default;
LG::Size preferred_desktop_window_size() const override { return LG::Size(400, 300); }
const char* icon_path() const override { return "/res/icons/apps/terminal.icon"; }
bool application() override
{
int ptmx = setup_shell();
auto style = StatusBarStyle(LG::Color(58, 58, 64)).set_light_text();
auto& window = std::xos::construct<UI::Window>("Terminal", window_size(), icon_path(), style);
auto& superview = window.create_superview<TerminalView, TerminalViewController>(ptmx);
window.set_focused_view(superview);
return true;
}
void application_will_terminate() override
{
std::kill(shell_pid, 9);
}
private:
};
SET_APP_DELEGATE(AppDelegate);

View File

@@ -0,0 +1,16 @@
import("//build/userland/TEMPLATE.gni")
xOS_application("terminal") {
display_name = "Terminal"
sources = [
"AppDelegate.cpp",
"TerminalView.cpp",
]
configs = [ "//build/userland:userland_flags" ]
deplibs = [
"libcxx",
"libfoundation",
"libg",
"libui",
]
}

View File

@@ -0,0 +1,229 @@
#include "TerminalView.h"
#include <algorithm>
#include <libfoundation/EventLoop.h>
#include <libfoundation/KeyboardMapping.h>
#include <libg/Color.h>
#include <libui/Context.h>
TerminalView::TerminalView(UI::View* superview, const LG::Rect& frame, int ptmx)
: UI::View(superview, frame)
, m_ptmx(ptmx)
{
recalc_dimensions(frame);
}
TerminalView::TerminalView(UI::View* superview, UI::Window* window, const LG::Rect& frame, int ptmx)
: UI::View(superview, window, frame)
, m_ptmx(ptmx)
{
recalc_dimensions(frame);
LFoundation::EventLoop::the().add(LFoundation::Timer([this] {
this->m_cursor_visible = !this->m_cursor_visible;
this->invalidate_cursor_glyph();
},
400, LFoundation::Timer::Repeat));
}
void TerminalView::recalc_dimensions(const LG::Rect& frame)
{
m_max_rows = (frame.height() - padding() - UI::SafeArea::Bottom) / glyph_height();
m_max_cols = (frame.width() - 2 * padding()) / glyph_width();
// FIXME: Add copy and resize on window resize.
char* new_data = (char*)malloc(m_max_rows * m_max_cols);
memset(new_data, 0, m_max_rows * m_max_cols);
if (m_display_data) {
free(m_display_data);
}
m_display_data = new_data;
}
void TerminalView::display(const LG::Rect& rect)
{
LG::Context ctx = UI::graphics_current_context();
ctx.add_clip(rect);
ctx.set_fill_color(background_color());
ctx.fill(bounds());
ctx.set_fill_color(cursor_color());
auto cursor_left_corner = pos_on_screen();
ctx.fill(LG::Rect(cursor_left_corner.x(), cursor_left_corner.y(), cursor_width(), glyph_height()));
auto& f = font();
ctx.set_fill_color(font_color());
LG::Point<int> text_start { padding(), padding() };
for (int i = 0; i < m_max_rows; i++) {
for (int j = 0; j < m_max_cols; j++) {
int idx = i * m_max_cols + j;
ctx.draw(text_start, f.glyph(m_display_data[idx]));
text_start.offset_by(glyph_width(), 0);
}
text_start.set_x(padding());
text_start.offset_by(0, glyph_height());
}
}
void TerminalView::scroll_line()
{
data_do_new_line();
set_needs_display();
}
void TerminalView::data_do_new_line()
{
char* data_plus_line = m_display_data + (m_max_cols);
char* data_end_minus_line = m_display_data + (m_max_rows - 1) * m_max_cols;
memmove(m_display_data, data_plus_line, (m_max_rows - 1) * m_max_cols);
memset(data_end_minus_line, 0, m_max_cols);
}
WindowStatus TerminalView::cursor_positions_do_new_line()
{
m_col = 0;
m_row++;
if (m_row == m_max_rows) {
m_row--;
return DoNewLine;
}
return Normal;
}
WindowStatus TerminalView::cursor_position_move_right()
{
m_col++;
if (m_col == m_max_cols) {
return cursor_positions_do_new_line();
}
return Normal;
}
WindowStatus TerminalView::cursor_position_move_left()
{
m_col--;
if (m_col == 0 && m_row > 0) {
m_row--;
}
return Normal;
}
void TerminalView::new_line()
{
will_move_cursor();
WindowStatus status = cursor_positions_do_new_line();
switch (status) {
case DoNewLine:
scroll_line();
break;
case Normal:
break;
}
did_move_cursor();
}
void TerminalView::increment_counter()
{
will_move_cursor();
m_col++;
if (m_col == m_max_cols) {
new_line();
}
did_move_cursor();
}
void TerminalView::decrement_counter()
{
will_move_cursor();
m_col--;
if (m_col == 0) {
m_row--;
}
did_move_cursor();
}
void TerminalView::put_char(char c)
{
auto pt = pos_on_screen();
set_needs_display(LG::Rect(pt.x(), pt.y(), glyph_width(), glyph_height()));
m_display_data[pos_in_data()] = c;
}
void TerminalView::push_back_char(char c)
{
if (c == '\n') {
new_line();
return;
}
put_char(c);
increment_counter();
}
void TerminalView::put_text(const std::string& data)
{
auto current_pos = pos_on_screen();
LG::Point<int> top_left_update_location { current_pos.x(), current_pos.y() };
LG::Point<int> bottom_right_update_location { current_pos.x(), current_pos.y() };
auto set_to_redraw_full_screen = [&]() {
data_do_new_line();
top_left_update_location = { bounds().min_x(), bounds().min_y() };
bottom_right_update_location = { bounds().max_x(), bounds().max_y() };
};
will_move_cursor();
int n = data.size();
for (int i = 0; i < n; i++) {
char c = data[i];
if (c == '\n') {
auto status = cursor_positions_do_new_line();
if (status == DoNewLine) {
set_to_redraw_full_screen();
}
} else {
auto pt = pos_on_screen();
data_set_char(c);
auto status = cursor_position_move_right();
if (status == DoNewLine) {
set_to_redraw_full_screen();
} else {
top_left_update_location.set_x(std::min(top_left_update_location.x(), pt.x()));
top_left_update_location.set_y(std::min(top_left_update_location.y(), pt.y()));
bottom_right_update_location.set_x(std::max(bottom_right_update_location.x(), pt.x() + glyph_width()));
bottom_right_update_location.set_y(std::max(bottom_right_update_location.y(), pt.y() + glyph_height()));
}
}
}
auto w = bottom_right_update_location.x() - top_left_update_location.x() + 1;
auto h = bottom_right_update_location.y() - top_left_update_location.y() + 1;
set_needs_display(LG::Rect(top_left_update_location.x(), top_left_update_location.y(), w, h));
did_move_cursor();
}
void TerminalView::send_input()
{
write(ptmx(), m_input.c_str(), m_input.size());
m_input.clear();
}
void TerminalView::receive_keyup_event(UI::KeyUpEvent&)
{
}
void TerminalView::receive_keydown_event(UI::KeyDownEvent& event)
{
// FIXME: More symbols and static size of font
if (event.key() == LFoundation::Keycode::KEY_BACKSPACE) {
if (m_input.size()) {
m_input.pop_back();
decrement_counter();
put_char(' ');
}
} else if (event.key() == LFoundation::Keycode::KEY_RETURN) {
m_input.push_back('\n');
push_back_char('\n');
send_input();
} else if (event.key() < 128) {
m_input.push_back(char(event.key()));
push_back_char(char(event.key()));
}
}

View File

@@ -0,0 +1,84 @@
#pragma once
#include <libg/Font.h>
#include <libui/View.h>
#include <string>
enum WindowStatus {
Normal,
DoNewLine,
};
class TerminalView : public UI::View {
UI_OBJECT();
public:
TerminalView(UI::View* superview, const LG::Rect&, int ptmx);
TerminalView(UI::View* superview, UI::Window* window, const LG::Rect&, int ptmx);
const LG::Color& font_color() const { return m_font_color; }
const LG::Color cursor_color() const { return m_cursor_visible ? LG::Color(80, 80, 80, 255) : background_color(); }
const LG::Color& background_color() const { return m_background_color; }
inline const LG::Font& font() const { return *m_font_ptr; }
inline int glyph_width() const { return font().glyph('.').width(); }
inline int glyph_height() const { return font().size() + 2; }
inline LG::Point<int> pos_on_screen() const { return { (int)m_col * glyph_width() + padding(), (int)m_row * glyph_height() + padding() }; }
inline int pos_in_data() const { return m_max_cols * m_row + m_col; }
void put_char(char c);
void put_text(const std::string& data);
void display(const LG::Rect& rect) override;
void receive_keyup_event(UI::KeyUpEvent&) override;
void receive_keydown_event(UI::KeyDownEvent&) override;
int ptmx() const { return m_ptmx; }
private:
void terminal_init();
WindowStatus cursor_positions_do_new_line();
WindowStatus cursor_position_move_right();
WindowStatus cursor_position_move_left();
void data_do_new_line();
inline void data_set_char(char c)
{
m_display_data[pos_in_data()] = c;
}
void scroll_line();
void new_line();
void increment_counter();
void decrement_counter();
void recalc_dimensions(const LG::Rect&);
void push_back_char(char c);
void send_input();
inline void invalidate_cursor_glyph()
{
auto pt = pos_on_screen();
set_needs_display(LG::Rect(pt.x(), pt.y(), cursor_width() + spacing(), glyph_height()));
}
inline void will_move_cursor() { invalidate_cursor_glyph(); }
inline void did_move_cursor() { invalidate_cursor_glyph(); }
constexpr int padding() const { return 2; }
constexpr int spacing() const { return 2; }
constexpr int cursor_width() const { return 5; }
int m_ptmx { -1 };
std::string m_input {};
bool m_cursor_visible { true };
LG::Color m_background_color { LG::Color(47, 47, 53) };
LG::Color m_font_color { LG::Color::LightSystemText };
LG::Font* m_font_ptr { LG::Font::load_from_file("/res/fonts/Liza.font/10/regular.font") };
size_t m_max_cols { 0 };
size_t m_max_rows { 0 };
size_t m_col { 0 };
size_t m_row { 0 };
char* m_display_data { nullptr };
};

View File

@@ -0,0 +1,34 @@
#pragma once
#include "TerminalView.h"
#include <libui/ViewController.h>
#include <memory>
#include <sys/types.h>
#include <unistd.h>
class TerminalViewController : public UI::ViewController<TerminalView> {
public:
TerminalViewController(TerminalView& view)
: UI::ViewController<TerminalView>(view)
{
}
virtual ~TerminalViewController() = default;
void init_listners()
{
LFoundation::EventLoop::the().add(
view().ptmx(), [this] {
char text[256];
int cnt = read(view().ptmx(), text, 255);
text[cnt] = '\0';
view().put_text(std::string(text, cnt));
},
nullptr);
}
void view_did_load() override
{
init_listners();
}
private:
};