Compare commits

...

8 Commits

Author SHA1 Message Date
a202ef1f34
terminal: Use pseudoterminals and add keyboard support
All checks were successful
continuous-integration/drone/pr Build is passing
2023-09-16 11:48:31 +02:00
35633fc65f
libc: Add pseudoterminal-related functions 2023-09-16 11:48:31 +02:00
d7db4e6147
init: Mount /dev/pts on startup 2023-09-16 11:48:31 +02:00
fd46d134aa
kernel: Add pseudoterminals and a /dev/pts filesystem 2023-09-16 11:48:30 +02:00
fbfa5328d7
wind: Stop tracking windows after they're closed 2023-09-16 11:45:51 +02:00
d3cc4d109b
wind+libui: Add support for keyboard events 2023-09-16 11:45:19 +02:00
65b7518e50
apps: Remove gclient 2023-09-16 11:42:19 +02:00
b134d2d0e0
taskbar: Add a button to open terminal instead of gclient 2023-09-16 11:41:51 +02:00
46 changed files with 1438 additions and 109 deletions

View File

@ -40,8 +40,6 @@ luna_app(kill.cpp kill)
luna_app(gol.cpp gol) luna_app(gol.cpp gol)
luna_app(touch.cpp touch) luna_app(touch.cpp touch)
luna_app(free.cpp free) luna_app(free.cpp free)
luna_app(gclient.cpp gclient)
target_link_libraries(gclient PUBLIC ui)
luna_app(about.cpp about) luna_app(about.cpp about)
target_link_libraries(about PUBLIC ui) target_link_libraries(about PUBLIC ui)
luna_app(taskbar.cpp taskbar) luna_app(taskbar.cpp taskbar)

View File

@ -1,67 +0,0 @@
#include <ui/App.h>
#include <ui/Layout.h>
struct ColorWidget : public ui::Widget
{
public:
ColorWidget(ui::Color first, ui::Color second) : m_color(first), m_first_color(first), m_second_color(second)
{
}
Result<ui::EventResult> handle_mouse_move(ui::Point) override
{
auto old_color = m_color;
m_color = m_second_color;
return old_color.raw == m_second_color.raw ? ui::EventResult::DidNotHandle : ui::EventResult::DidHandle;
}
Result<ui::EventResult> handle_mouse_leave() override
{
auto old_color = m_color;
m_color = m_first_color;
return old_color.raw == m_first_color.raw ? ui::EventResult::DidNotHandle : ui::EventResult::DidHandle;
}
Result<void> draw(ui::Canvas& canvas) override
{
canvas.fill(m_color);
return {};
}
private:
ui::Color m_color;
ui::Color m_first_color;
ui::Color m_second_color;
};
Result<int> luna_main(int argc, char** argv)
{
ui::App app;
TRY(app.init(argc, argv));
auto* window = TRY(ui::Window::create(ui::Rect { 200, 200, 400, 300 }));
app.set_main_window(window);
window->set_title("Test Window");
window->set_background(ui::CYAN);
ui::HorizontalLayout layout;
window->set_main_widget(layout);
ColorWidget green(ui::GREEN, ui::WHITE);
layout.add_widget(green);
ColorWidget blue(ui::BLUE, ui::GRAY);
layout.add_widget(blue);
ui::VerticalLayout sublayout;
layout.add_widget(sublayout);
ColorWidget red(ui::RED, ui::CYAN);
sublayout.add_widget(red);
ColorWidget white(ui::WHITE, ui::GREEN);
sublayout.add_widget(white);
window->draw();
return app.run();
}

View File

@ -339,6 +339,13 @@ static void mount_shmfs()
if (chmod("/dev/shm", 01777) < 0) exit(255); if (chmod("/dev/shm", 01777) < 0) exit(255);
} }
static void mount_devpts()
{
if (mkdir("/dev/pts", 0755) < 0) exit(255);
if (mount("/dev/pts", "devpts", "devpts") < 0) exit(255);
}
Result<int> sysinit(StringView path) Result<int> sysinit(StringView path)
{ {
if (getpid() != 1) if (getpid() != 1)
@ -359,6 +366,7 @@ Result<int> sysinit(StringView path)
mount_tmpfs(); mount_tmpfs();
mount_shmfs(); mount_shmfs();
mount_devpts();
umask(022); umask(022);

View File

@ -31,18 +31,18 @@ Result<int> luna_main(int argc, char** argv)
ui::HorizontalLayout layout(ui::AdjustHeight::Yes, ui::AdjustWidth::No); ui::HorizontalLayout layout(ui::AdjustHeight::Yes, ui::AdjustWidth::No);
window->set_main_widget(layout); window->set_main_widget(layout);
ui::Button start_button({ 0, 0, 50, 50 }); ui::Button term_button({ 0, 0, 50, 50 });
layout.add_widget(start_button); layout.add_widget(term_button);
ui::Container start_container({ 0, 0, 50, 50 }, ui::VerticalAlignment::Center, ui::HorizontalAlignment::Center); ui::Container term_container({ 0, 0, 50, 50 }, ui::VerticalAlignment::Center, ui::HorizontalAlignment::Center);
start_button.set_widget(start_container); term_button.set_widget(term_container);
start_button.set_action([] { term_button.set_action([] {
StringView args[] = { "/usr/bin/gclient" }; StringView args[] = { "/usr/bin/terminal" };
os::Process::spawn("/usr/bin/gclient", Slice<StringView> { args, 1 }, false); os::Process::spawn("/usr/bin/terminal", Slice<StringView> { args, 1 }, false);
}); });
auto start_image = TRY(ui::ImageWidget::load("/usr/share/icons/32x32/start-icon.tga")); auto term_image = TRY(ui::ImageWidget::load("/usr/share/icons/32x32/app-terminal.tga"));
start_container.set_widget(*start_image); term_container.set_widget(*term_image);
ui::Button about_button({ 0, 0, 50, 50 }); ui::Button about_button({ 0, 0, 50, 50 });
layout.add_widget(about_button); layout.add_widget(about_button);

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -57,6 +57,8 @@ set(SOURCES
src/net/UnixSocket.cpp src/net/UnixSocket.cpp
src/fs/tmpfs/FileSystem.cpp src/fs/tmpfs/FileSystem.cpp
src/fs/tmpfs/Inode.cpp src/fs/tmpfs/Inode.cpp
src/fs/devpts/FileSystem.cpp
src/fs/devpts/Inode.cpp
src/fs/ext2/FileSystem.cpp src/fs/ext2/FileSystem.cpp
src/fs/ext2/Inode.cpp src/fs/ext2/Inode.cpp
src/fs/devices/DeviceRegistry.cpp src/fs/devices/DeviceRegistry.cpp
@ -69,6 +71,9 @@ set(SOURCES
src/fs/devices/UARTDevice.cpp src/fs/devices/UARTDevice.cpp
src/fs/devices/MouseDevice.cpp src/fs/devices/MouseDevice.cpp
src/fs/devices/KeyboardDevice.cpp src/fs/devices/KeyboardDevice.cpp
src/fs/devices/PTYMultiplexer.cpp
src/fs/devices/MasterPTY.cpp
src/fs/devices/SlavePTY.cpp
src/fs/InitRD.cpp src/fs/InitRD.cpp
src/binfmt/ELF.cpp src/binfmt/ELF.cpp
src/binfmt/BinaryFormat.cpp src/binfmt/BinaryFormat.cpp

View File

@ -7,6 +7,7 @@
#include "fs/devices/KeyboardDevice.h" #include "fs/devices/KeyboardDevice.h"
#include "fs/devices/MouseDevice.h" #include "fs/devices/MouseDevice.h"
#include "fs/devices/NullDevice.h" #include "fs/devices/NullDevice.h"
#include "fs/devices/PTYMultiplexer.h"
#include "fs/devices/UARTDevice.h" #include "fs/devices/UARTDevice.h"
#include "fs/devices/ZeroDevice.h" #include "fs/devices/ZeroDevice.h"
#include "fs/tmpfs/FileSystem.h" #include "fs/tmpfs/FileSystem.h"
@ -92,6 +93,12 @@ namespace DeviceRegistry
for (const auto& descriptor : g_available_devices) TRY(create_special_device_inode(fs, descriptor)); for (const auto& descriptor : g_available_devices) TRY(create_special_device_inode(fs, descriptor));
auto multiplexer = TRY(make_shared<PTYMultiplexer>());
multiplexer->set_fs(*fs);
multiplexer->set_inode_number(TRY(fs->allocate_inode_number()));
TRY(fs->root_inode()->add_entry(multiplexer, "ptmx"));
return fs; return fs;
} }
} }

View File

@ -17,6 +17,7 @@ namespace DeviceRegistry
DiskPartition = 5, DiskPartition = 5,
Serial = 6, Serial = 6,
Input = 7, Input = 7,
Terminal = 8,
}; };
Result<SharedPtr<Device>> fetch_special_device(u32 major, u32 minor); Result<SharedPtr<Device>> fetch_special_device(u32 major, u32 minor);

View File

@ -0,0 +1,125 @@
#include "fs/devices/MasterPTY.h"
#include "Pledge.h"
#include "fs/devices/DeviceRegistry.h"
#include "fs/devices/PTYMultiplexer.h"
#include "fs/devpts/FileSystem.h"
#include "memory/MemoryManager.h"
#include "thread/Scheduler.h"
Result<SharedPtr<VFS::Inode>> MasterPTY::create_pair(int index)
{
auto master = TRY(make_shared<MasterPTY>());
auto slave = TRY(make_shared<SlavePTY>());
auto name = TRY(String::format("%d"_sv, index));
for (auto& fs : g_devpts_instances) { fs->root_inode()->add_entry(slave, name.chars()); }
slave->m_name = move(name);
master->m_metadata.mode = 0666;
master->m_index = index;
master->m_slave = slave;
master->m_metadata.devid = luna_dev_makedev(DeviceRegistry::Terminal, 0);
master->m_settings.c_lflag = ECHO | ECHOE | ECHOCTL | ISIG | ICANON;
master->m_settings.c_cc[VEOF] = '\4';
master->m_settings.c_cc[VERASE] = '\b';
master->m_settings.c_cc[VINTR] = '\3';
master->m_settings.c_cc[VQUIT] = '\x1c';
master->m_window.ws_col = 80;
master->m_window.ws_row = 25;
slave->m_master = master.ptr();
slave->m_metadata.devid = luna_dev_makedev(DeviceRegistry::Terminal, index + 1);
slave->m_metadata.uid = Scheduler::current()->auth.euid;
slave->m_metadata.gid = Scheduler::current()->auth.egid;
slave->m_metadata.mode = 0620;
return (SharedPtr<VFS::Inode>)master;
}
Result<void> MasterPTY::handle_background_process_group(bool can_succeed, int signo) const
{
if (!m_foreground_process_group.has_value()) return {};
auto foreground_pgrp = m_foreground_process_group.value();
auto* current = Scheduler::current();
if (current->pgid == foreground_pgrp) return {};
if ((current->signal_mask.get(signo - 1)) || (current->signal_handlers[signo - 1].sa_handler == SIG_IGN))
{
if (can_succeed) return {};
return err(EIO);
}
current->send_signal(signo);
if (can_succeed) return err(EINTR);
return err(EIO);
}
Result<usize> MasterPTY::read(u8* buf, usize, usize length) const
{
length = m_buffer.dequeue_data(buf, length);
return length;
}
Result<usize> MasterPTY::write(const u8* buf, usize, usize length)
{
TRY(m_slave->m_buffer.append_data(buf, length));
return length;
}
Result<u64> MasterPTY::ioctl(int request, void* arg)
{
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_tty));
switch (request)
{
case TCGETS: {
return MemoryManager::copy_to_user_typed((struct termios*)arg, &m_settings) ? 0 : err(EFAULT);
}
case TCSETS: {
if (!MemoryManager::copy_from_user_typed((const struct termios*)arg, &m_settings)) return err(EFAULT);
return 0;
}
case TIOCSPGRP: {
pid_t pgid;
if (!MemoryManager::copy_from_user_typed((const pid_t*)arg, &pgid)) return err(EFAULT);
bool pgid_exists = false;
Scheduler::for_each_in_process_group(pgid, [&pgid_exists](Thread*) {
pgid_exists = true;
return false;
});
if (!pgid_exists) return err(EPERM);
m_foreground_process_group = pgid;
return 0;
}
case TIOCGPGRP: {
pid_t pgid = m_foreground_process_group.value_or((pid_t)next_thread_id());
if (!MemoryManager::copy_to_user_typed((pid_t*)arg, &pgid)) return err(EFAULT);
return 0;
}
case TIOCGWINSZ: {
if (!MemoryManager::copy_to_user_typed((struct winsize*)arg, &m_window)) return err(EFAULT);
return 0;
}
case TIOCGPTN: {
if (!MemoryManager::copy_to_user_typed((int*)arg, &m_index)) return err(EFAULT);
return 0;
}
default: return err(EINVAL);
}
}
MasterPTY::~MasterPTY()
{
m_slave->m_master = nullptr;
for (auto& fs : g_devpts_instances) { fs->root_inode()->remove_entry(m_slave->m_name.chars()); }
PTYMultiplexer::did_remove_pty(m_index);
}

View File

@ -0,0 +1,75 @@
#pragma once
#include "fs/VFS.h"
#include "fs/devices/SlavePTY.h"
#include <bits/termios.h>
#include <luna/Buffer.h>
class MasterPTY : public VFS::DeviceInode
{
public:
MasterPTY() = default;
static Result<SharedPtr<VFS::Inode>> create_pair(int index);
VFS::InodeType type() const override
{
return VFS::InodeType::CharacterDevice;
}
Result<u64> query_shared_memory(off_t, usize) override
{
return err(ENOTSUP);
}
VFS::FileSystem* fs() const override
{
return nullptr;
}
Result<usize> read(u8* buf, usize offset, usize length) const override;
Result<usize> write(const u8* buf, usize offset, usize length) override;
Result<u64> ioctl(int request, void* arg) override;
Result<u64> isatty() const override
{
return 1;
}
Result<void> truncate(usize) override
{
// POSIX says truncate is for regular files, but doesn't tell us what error to return for non-regular files.
return err(EINVAL);
}
bool will_block_if_read() const override
{
return m_buffer.is_empty();
}
void did_link() override
{
m_metadata.nlinks++;
}
void did_unlink() override
{
m_metadata.nlinks--;
}
virtual ~MasterPTY();
private:
struct termios m_settings;
mutable Buffer m_buffer;
SharedPtr<SlavePTY> m_slave;
mutable Option<pid_t> m_foreground_process_group;
struct winsize m_window;
Result<void> handle_background_process_group(bool can_succeed, int signo) const;
int m_index;
friend class SlavePTY;
};

View File

@ -0,0 +1,36 @@
#include "fs/devices/PTYMultiplexer.h"
Bitset<u64> PTYMultiplexer::m_available_indexes = 0;
PTYMultiplexer::PTYMultiplexer()
{
m_metadata.devid = luna_dev_makedev(DeviceRegistry::Terminal, 0);
m_metadata.mode = 0666;
}
Result<SharedPtr<VFS::Inode>> PTYMultiplexer::open()
{
int index = -1;
for (int i = 0; i < 64; i++)
{
if (!m_available_indexes.get(i))
{
index = i;
m_available_indexes.set(i, true);
break;
}
}
if (index == -1) return err(ENOSPC);
return MasterPTY::create_pair(index);
}
void PTYMultiplexer::init()
{
m_available_indexes.clear();
}
void PTYMultiplexer::did_remove_pty(int index)
{
m_available_indexes.set(index, false);
}

View File

@ -0,0 +1,80 @@
#pragma once
#include "fs/VFS.h"
#include "fs/devices/DeviceRegistry.h"
#include "fs/devices/MasterPTY.h"
#include <luna/Bitset.h>
class PTYMultiplexer : public VFS::DeviceInode
{
public:
PTYMultiplexer();
VFS::InodeType type() const override
{
return VFS::InodeType::CharacterDevice;
}
void set_fs(VFS::FileSystem& fs)
{
m_fs = &fs;
}
void set_inode_number(usize inum)
{
m_metadata.inum = inum;
}
Result<u64> query_shared_memory(off_t, usize) override
{
unreachable();
}
Result<SharedPtr<VFS::Inode>> open() override;
VFS::FileSystem* fs() const override
{
return m_fs;
}
Result<usize> read(u8*, usize, usize) const override
{
unreachable();
}
Result<usize> write(const u8*, usize, usize) override
{
unreachable();
}
Result<void> truncate(usize) override
{
// POSIX says truncate is for regular files, but doesn't tell us what error to return for non-regular files.
return err(EINVAL);
}
bool will_block_if_read() const override
{
unreachable();
}
void did_link() override
{
m_metadata.nlinks++;
}
void did_unlink() override
{
m_metadata.nlinks--;
}
static void init();
static void did_remove_pty(int index);
virtual ~PTYMultiplexer() = default;
private:
VFS::FileSystem* m_fs;
static Bitset<u64> m_available_indexes;
};

View File

@ -0,0 +1,71 @@
#include "fs/devices/SlavePTY.h"
#include "Pledge.h"
#include "fs/devices/MasterPTY.h"
#include "memory/MemoryManager.h"
#include "thread/Scheduler.h"
Result<usize> SlavePTY::read(u8* buf, usize, usize length) const
{
if (!m_master) return err(EIO);
TRY(m_master->handle_background_process_group(false, SIGTTIN));
length = m_buffer.dequeue_data(buf, length);
return length;
}
Result<usize> SlavePTY::write(const u8* buf, usize, usize length)
{
if (!m_master) return err(EIO);
if (m_master->m_settings.c_lflag & TOSTOP) TRY(m_master->handle_background_process_group(true, SIGTTOU));
TRY(m_master->m_buffer.append_data(buf, length));
return length;
}
Result<u64> SlavePTY::ioctl(int request, void* arg)
{
auto* current = Scheduler::current();
TRY(check_pledge(current, Promise::p_tty));
if (!m_master) return err(EIO);
switch (request)
{
case TCGETS: {
return MemoryManager::copy_to_user_typed((struct termios*)arg, &m_master->m_settings) ? 0 : err(EFAULT);
}
case TCSETS: {
if (!MemoryManager::copy_from_user_typed((const struct termios*)arg, &m_master->m_settings)) return err(EFAULT);
return 0;
}
case TIOCSPGRP: {
pid_t pgid;
if (!MemoryManager::copy_from_user_typed((const pid_t*)arg, &pgid)) return err(EFAULT);
bool pgid_exists = false;
Scheduler::for_each_in_process_group(pgid, [&pgid_exists](Thread*) {
pgid_exists = true;
return false;
});
if (!pgid_exists) return err(EPERM);
m_master->m_foreground_process_group = pgid;
return 0;
}
case TIOCGPGRP: {
pid_t pgid = m_master->m_foreground_process_group.value_or((pid_t)next_thread_id());
if (!MemoryManager::copy_to_user_typed((pid_t*)arg, &pgid)) return err(EFAULT);
return 0;
}
case TIOCGWINSZ: {
if (!MemoryManager::copy_to_user_typed((struct winsize*)arg, &m_master->m_window)) return err(EFAULT);
return 0;
}
default: return err(EINVAL);
}
}

View File

@ -0,0 +1,69 @@
#pragma once
#include "fs/VFS.h"
#include <luna/Buffer.h>
#include <luna/String.h>
class MasterPTY;
class SlavePTY : public VFS::DeviceInode
{
public:
SlavePTY() = default;
VFS::InodeType type() const override
{
return VFS::InodeType::CharacterDevice;
}
Result<u64> query_shared_memory(off_t, usize) override
{
return err(ENOTSUP);
}
VFS::FileSystem* fs() const override
{
return nullptr;
}
Result<usize> read(u8* buf, usize offset, usize length) const override;
Result<usize> write(const u8* buf, usize offset, usize length) override;
Result<u64> ioctl(int request, void* arg) override;
Result<u64> isatty() const override
{
return 1;
}
Result<void> truncate(usize) override
{
// POSIX says truncate is for regular files, but doesn't tell us what error to return for non-regular files.
return err(EINVAL);
}
bool will_block_if_read() const override
{
return m_buffer.is_empty();
}
void did_link() override
{
m_metadata.nlinks++;
}
void did_unlink() override
{
m_metadata.nlinks--;
}
virtual ~SlavePTY() = default;
private:
mutable Buffer m_buffer;
MasterPTY* m_master;
String m_name;
friend class MasterPTY;
};

View File

@ -0,0 +1,62 @@
#include "fs/devpts/FileSystem.h"
#include "arch/Timer.h"
#include "fs/devices/DeviceRegistry.h"
#include "fs/devpts/Inode.h"
#include <luna/Alloc.h>
#include <luna/CString.h>
#include <luna/Ignore.h>
Vector<VFS::FileSystem*> g_devpts_instances;
namespace DevPTS
{
Result<SharedPtr<VFS::FileSystem>> FileSystem::create()
{
SharedPtr<FileSystem> fs = TRY(adopt_shared_if_nonnull(new (std::nothrow) FileSystem()));
SharedPtr<RootInode> root = TRY(make_shared<RootInode>());
TRY(root->add_entry(root, "."));
TRY(root->add_entry(root, ".."));
root->set_self(root, {});
root->set_fs(*fs, {});
root->set_inode_number();
root->m_metadata.mode = 0755;
root->m_metadata.atime = root->m_metadata.ctime = root->m_metadata.mtime = *Timer::realtime_clock();
fs->set_root(root);
TRY(g_devpts_instances.try_append(fs.ptr()));
return (SharedPtr<VFS::FileSystem>)fs;
}
Result<u64> FileSystem::allocate_inode_number()
{
return err(ENOTSUP);
}
FileSystem::FileSystem()
{
m_host_device_id = DeviceRegistry::next_null_device_id();
}
Result<void> FileSystem::set_mount_dir(SharedPtr<VFS::Inode> parent)
{
return m_root_inode->replace_entry(parent, "..");
}
Result<void> FileSystem::reset_mount_dir()
{
return m_root_inode->replace_entry(m_root_inode, "..");
}
void FileSystem::set_root(SharedPtr<VFS::Inode> root)
{
m_root_inode = root;
}
FileSystem::~FileSystem()
{
g_devpts_instances.remove_first_matching([this](VFS::FileSystem* ptr) { return ptr == this; });
}
}

View File

@ -0,0 +1,61 @@
#pragma once
#include "fs/VFS.h"
#include "fs/devices/DeviceRegistry.h"
namespace DevPTS
{
class FileSystem : public VFS::FileSystem
{
public:
SharedPtr<VFS::Inode> root_inode() const override
{
return m_root_inode;
}
Result<SharedPtr<VFS::Inode>> create_file_inode(mode_t) override
{
return err(ENOTSUP);
}
Result<SharedPtr<VFS::Inode>> create_dir_inode(SharedPtr<VFS::Inode>, mode_t) override
{
return err(ENOTSUP);
}
Result<SharedPtr<VFS::Inode>> create_device_inode(u32, u32, mode_t) override
{
return err(ENOTSUP);
}
Result<SharedPtr<VFS::Inode>> create_symlink_inode(StringView) override
{
return err(ENOTSUP);
}
Result<u64> allocate_inode_number() override;
Result<void> set_mount_dir(SharedPtr<VFS::Inode> parent) override;
Result<void> reset_mount_dir() override;
static Result<SharedPtr<VFS::FileSystem>> create();
dev_t host_device_id() const override
{
return m_host_device_id;
}
virtual ~FileSystem();
private:
FileSystem();
void set_root(SharedPtr<VFS::Inode> root);
SharedPtr<VFS::Inode> m_root_inode;
dev_t m_host_device_id;
};
}
extern Vector<VFS::FileSystem*> g_devpts_instances;

View File

@ -0,0 +1,78 @@
#include "fs/devpts/Inode.h"
namespace DevPTS
{
Result<SharedPtr<VFS::Inode>> RootInode::find(const char* name) const
{
for (const auto& entry : m_entries)
{
if (!strcmp(name, entry.name.chars())) return entry.inode;
}
return err(ENOENT);
}
Result<void> RootInode::replace_entry(SharedPtr<VFS::Inode> inode, const char* name)
{
for (auto& entry : m_entries)
{
if (!strcmp(name, entry.name.chars()))
{
entry.inode = inode;
return {};
}
}
return err(ENOENT);
}
Option<VFS::DirectoryEntry> RootInode::get(usize index) const
{
if (index >= m_entries.size()) return {};
return m_entries[index];
}
Result<void> RootInode::add_entry(SharedPtr<VFS::Inode> inode, const char* name)
{
if (find(name).has_value()) return err(EEXIST);
VFS::DirectoryEntry entry { inode, name };
TRY(m_entries.try_append(move(entry)));
inode->did_link();
m_metadata.mtime = *Timer::realtime_clock();
return {};
}
Result<void> RootInode::remove_entry(const char* name)
{
SharedPtr<VFS::Inode> inode = TRY(find(name));
if (inode->type() == VFS::InodeType::Directory && inode->entries() != 2) return err(ENOTEMPTY);
if (inode->is_mountpoint()) return err(EBUSY);
m_entries.remove_first_matching(
[&](const VFS::DirectoryEntry& entry) { return !strcmp(entry.name.chars(), name); });
inode->did_unlink();
m_metadata.mtime = *Timer::realtime_clock();
return {};
}
Result<SharedPtr<VFS::Inode>> RootInode::create_file(const char*, mode_t)
{
return err(ENOTSUP);
}
Result<SharedPtr<VFS::Inode>> RootInode::create_subdirectory(const char*, mode_t)
{
return err(ENOTSUP);
}
}

View File

@ -0,0 +1,93 @@
#pragma once
#include "fs/VFS.h"
#include "fs/devices/DeviceRegistry.h"
#include "fs/devpts/FileSystem.h"
namespace DevPTS
{
class RootInode : public VFS::Inode
{
public:
RootInode() = default;
void set_fs(FileSystem& fs, Badge<FileSystem>)
{
m_fs = &fs;
}
void set_inode_number()
{
m_metadata.inum = 2;
}
void set_self(SharedPtr<VFS::Inode> self, Badge<FileSystem>)
{
m_self = self;
}
Result<SharedPtr<VFS::Inode>> find(const char* name) const override;
Option<VFS::DirectoryEntry> get(usize index) const override;
Result<usize> read(u8*, usize, usize) const override
{
return err(EISDIR);
}
Result<usize> write(const u8*, usize, usize) override
{
return err(EISDIR);
}
Result<void> truncate(usize) override
{
return err(EISDIR);
}
bool will_block_if_read() const override
{
return false;
}
VFS::FileSystem* fs() const override
{
return m_fs;
}
VFS::InodeType type() const override
{
return VFS::InodeType::Directory;
}
void did_link() override
{
}
void did_unlink() override
{
m_self = {};
m_entries.clear();
}
usize entries() const override
{
return m_entries.size();
}
Result<void> remove_entry(const char* name) override;
Result<SharedPtr<VFS::Inode>> create_file(const char* name, mode_t mode) override;
Result<SharedPtr<VFS::Inode>> create_subdirectory(const char* name, mode_t mode) override;
Result<void> add_entry(SharedPtr<VFS::Inode> inode, const char* name);
Result<void> replace_entry(SharedPtr<VFS::Inode> inode, const char* name);
virtual ~RootInode() = default;
private:
VFS::FileSystem* m_fs;
SharedPtr<VFS::Inode> m_self;
Vector<VFS::DirectoryEntry> m_entries;
friend class FileSystem;
};
}

View File

@ -6,6 +6,7 @@
#include "config.h" #include "config.h"
#include "fs/InitRD.h" #include "fs/InitRD.h"
#include "fs/devices/DeviceRegistry.h" #include "fs/devices/DeviceRegistry.h"
#include "fs/devices/PTYMultiplexer.h"
#include "fs/tmpfs/FileSystem.h" #include "fs/tmpfs/FileSystem.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "thread/Scheduler.h" #include "thread/Scheduler.h"
@ -94,6 +95,7 @@ extern "C" [[noreturn]] void _start()
Thread::init(); Thread::init();
Scheduler::init(); Scheduler::init();
PTYMultiplexer::init();
Scheduler::new_kernel_thread(init, "[kinit]"); Scheduler::new_kernel_thread(init, "[kinit]");

View File

@ -1,5 +1,6 @@
#include "Pledge.h" #include "Pledge.h"
#include "fs/VFS.h" #include "fs/VFS.h"
#include "fs/devpts/FileSystem.h"
#include "fs/ext2/FileSystem.h" #include "fs/ext2/FileSystem.h"
#include "fs/tmpfs/FileSystem.h" #include "fs/tmpfs/FileSystem.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
@ -26,6 +27,8 @@ Result<u64> sys_mount(Registers*, SyscallArgs args)
SharedPtr<VFS::FileSystem> fs; SharedPtr<VFS::FileSystem> fs;
if (fstype.view() == "tmpfs") fs = TRY(TmpFS::FileSystem::create()); if (fstype.view() == "tmpfs") fs = TRY(TmpFS::FileSystem::create());
else if (fstype.view() == "devpts")
fs = TRY(DevPTS::FileSystem::create());
else if (fstype.view() == "devfs") else if (fstype.view() == "devfs")
fs = TRY(DeviceRegistry::create_devfs_instance()); fs = TRY(DeviceRegistry::create_devfs_instance());
else if (fstype.view() == "ext2") else if (fstype.view() == "ext2")

View File

@ -45,5 +45,6 @@ struct winsize
#define TIOCSPGRP 2 #define TIOCSPGRP 2
#define TIOCGPGRP 3 #define TIOCGPGRP 3
#define TIOCGWINSZ 4 #define TIOCGWINSZ 4
#define TIOCGPTN 5
#endif #endif

View File

@ -148,6 +148,18 @@ extern "C"
/* Create a unique file from a template string whose last 6 bytes must be XXXXXX. */ /* Create a unique file from a template string whose last 6 bytes must be XXXXXX. */
int mkstemp(char* _template); int mkstemp(char* _template);
/* Create a new pseudoterminal pair. */
int posix_openpt(int flags);
/* Set the credentials of a pseudoterminal master. */
int grantpt(int fd);
/* Unlock a pseudoterminal master. */
int unlockpt(int fd);
/* Return the name of the slave associated with a pseudoterminal master. */
char* ptsname(int fd);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -1,4 +1,5 @@
#include <bits/errno-return.h> #include <bits/errno-return.h>
#include <bits/termios.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <limits.h> #include <limits.h>
@ -7,8 +8,10 @@
#include <luna/Sort.h> #include <luna/Sort.h>
#include <luna/Utf8.h> #include <luna/Utf8.h>
#include <signal.h> #include <signal.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <sys/wait.h> #include <sys/wait.h>
@ -300,4 +303,31 @@ extern "C"
{ {
return strtod(str, nullptr); return strtod(str, nullptr);
} }
int posix_openpt(int flags)
{
return open("/dev/ptmx", flags);
}
int grantpt(int)
{
return 0;
}
int unlockpt(int)
{
return 0;
}
char* ptsname(int fd)
{
static char buffer[4096];
int index;
if (ioctl(fd, TIOCGPTN, &index) < 0) return nullptr;
snprintf(buffer, sizeof(buffer), "/dev/pts/%d", index);
return buffer;
}
} }

View File

@ -24,6 +24,7 @@ namespace ui
Result<EventResult> handle_mouse_leave() override; Result<EventResult> handle_mouse_leave() override;
Result<EventResult> handle_mouse_down(Point position, int buttons) override; Result<EventResult> handle_mouse_down(Point position, int buttons) override;
Result<EventResult> handle_mouse_up(Point position, int buttons) override; Result<EventResult> handle_mouse_up(Point position, int buttons) override;
Result<EventResult> handle_key_event(const ui::KeyEventRequest& request) override;
Result<void> draw(Canvas& canvas) override; Result<void> draw(Canvas& canvas) override;
private: private:

View File

@ -24,6 +24,7 @@ namespace ui
Result<EventResult> handle_mouse_leave() override; Result<EventResult> handle_mouse_leave() override;
Result<EventResult> handle_mouse_down(Point position, int buttons) override; Result<EventResult> handle_mouse_down(Point position, int buttons) override;
Result<EventResult> handle_mouse_up(Point position, int buttons) override; Result<EventResult> handle_mouse_up(Point position, int buttons) override;
Result<EventResult> handle_key_event(const ui::KeyEventRequest& request) override;
Result<void> draw(Canvas& canvas) override; Result<void> draw(Canvas& canvas) override;
private: private:

14
libui/include/ui/Key.h Normal file
View File

@ -0,0 +1,14 @@
#pragma once
#include <moon/Keyboard.h>
namespace ui
{
enum Modifier
{
Mod_Shift = (1 << 0),
Mod_Alt = (1 << 1),
Mod_Super = (1 << 2),
Mod_AltGr = (1 << 3),
Mod_Ctrl = (1 << 4)
};
}

View File

@ -34,6 +34,7 @@ namespace ui
Result<EventResult> handle_mouse_leave() override; Result<EventResult> handle_mouse_leave() override;
Result<EventResult> handle_mouse_down(Point position, int buttons) override; Result<EventResult> handle_mouse_down(Point position, int buttons) override;
Result<EventResult> handle_mouse_up(Point position, int buttons) override; Result<EventResult> handle_mouse_up(Point position, int buttons) override;
Result<EventResult> handle_key_event(const ui::KeyEventRequest& request) override;
Result<void> draw(Canvas& canvas) override; Result<void> draw(Canvas& canvas) override;
@ -55,6 +56,7 @@ namespace ui
Result<EventResult> handle_mouse_leave() override; Result<EventResult> handle_mouse_leave() override;
Result<EventResult> handle_mouse_down(Point position, int buttons) override; Result<EventResult> handle_mouse_down(Point position, int buttons) override;
Result<EventResult> handle_mouse_up(Point position, int buttons) override; Result<EventResult> handle_mouse_up(Point position, int buttons) override;
Result<EventResult> handle_key_event(const ui::KeyEventRequest& request) override;
Result<void> draw(Canvas& canvas) override; Result<void> draw(Canvas& canvas) override;

View File

@ -13,6 +13,7 @@
#include <ui/Canvas.h> #include <ui/Canvas.h>
#include <ui/Point.h> #include <ui/Point.h>
#include <ui/Rect.h> #include <ui/Rect.h>
#include <ui/ipc/Client.h>
namespace ui namespace ui
{ {
@ -50,6 +51,12 @@ namespace ui
return EventResult::DidNotHandle; return EventResult::DidNotHandle;
} }
virtual Result<EventResult> handle_key_event(const ui::KeyEventRequest& request)
{
ignore(request);
return EventResult::DidNotHandle;
}
virtual Result<void> draw(Canvas& canvas); virtual Result<void> draw(Canvas& canvas);
void set_window(Window* window, Rect rect, Badge<Window>) void set_window(Window* window, Rect rect, Badge<Window>)

View File

@ -49,6 +49,7 @@ namespace ui
Result<ui::EventResult> handle_mouse_leave(); Result<ui::EventResult> handle_mouse_leave();
Result<ui::EventResult> handle_mouse_move(ui::Point position); Result<ui::EventResult> handle_mouse_move(ui::Point position);
Result<ui::EventResult> handle_mouse_buttons(ui::Point position, int buttons); Result<ui::EventResult> handle_mouse_buttons(ui::Point position, int buttons);
Result<ui::EventResult> handle_key_event(const ui::KeyEventRequest& request);
int id() const int id() const
{ {

View File

@ -9,7 +9,9 @@
#pragma once #pragma once
#include <os/IPC.h> #include <os/IPC.h>
#include <ui/Key.h>
#include <ui/Point.h> #include <ui/Point.h>
#include <ui/Rect.h>
namespace ui namespace ui
{ {
@ -20,7 +22,8 @@ namespace ui
WINDOW_CLOSE_REQUEST_ID, WINDOW_CLOSE_REQUEST_ID,
MOUSE_EVENT_REQUEST_ID, MOUSE_EVENT_REQUEST_ID,
MOUSE_LEAVE_REQUEST_ID, MOUSE_LEAVE_REQUEST_ID,
GET_SCREEN_RECT_RESPONSE_ID GET_SCREEN_RECT_RESPONSE_ID,
KEY_EVENT_REQUEST_ID,
}; };
struct CreateWindowResponse struct CreateWindowResponse
@ -60,4 +63,18 @@ namespace ui
Rect rect; Rect rect;
}; };
struct KeyEventRequest
{
static constexpr u8 ID = KEY_EVENT_REQUEST_ID;
int window;
bool pressed;
char letter;
char key;
moon::KeyCode code;
int modifiers;
};
} }

View File

@ -132,6 +132,14 @@ namespace ui
window->draw(); window->draw();
return {}; return {};
} }
case KEY_EVENT_REQUEST_ID: {
KeyEventRequest request;
READ_MESSAGE(request);
auto* window = find_window(request.window);
if (window->handle_key_event(request).value_or(ui::EventResult::DidNotHandle) == ui::EventResult::DidHandle)
window->draw();
return {};
}
default: fail("Unexpected IPC request from server!"); default: fail("Unexpected IPC request from server!");
} }
} }

View File

@ -61,6 +61,11 @@ namespace ui
return m_child->handle_mouse_up(position, buttons); return m_child->handle_mouse_up(position, buttons);
} }
Result<EventResult> Button::handle_key_event(const ui::KeyEventRequest& request)
{
return m_child->handle_key_event(request);
}
Result<void> Button::draw(Canvas& canvas) Result<void> Button::draw(Canvas& canvas)
{ {
return m_child->draw(canvas); return m_child->draw(canvas);

View File

@ -47,6 +47,11 @@ namespace ui
return ui::EventResult::DidNotHandle; return ui::EventResult::DidNotHandle;
} }
Result<EventResult> Container::handle_key_event(const ui::KeyEventRequest& request)
{
return m_widget->handle_key_event(request);
}
Result<void> Container::draw(Canvas& canvas) Result<void> Container::draw(Canvas& canvas)
{ {
auto rect = ui::Rect { m_widget->rect().pos.x - m_rect.pos.x, m_widget->rect().pos.y - m_rect.pos.y, auto rect = ui::Rect { m_widget->rect().pos.x - m_rect.pos.x, m_widget->rect().pos.y - m_rect.pos.y,

View File

@ -64,6 +64,19 @@ namespace ui
return ui::EventResult::DidNotHandle; return ui::EventResult::DidNotHandle;
} }
Result<EventResult> HorizontalLayout::handle_key_event(const ui::KeyEventRequest& request)
{
EventResult result = ui::EventResult::DidNotHandle;
for (auto widget : m_widgets)
{
auto rc = TRY(widget->handle_key_event(request));
if (rc == ui::EventResult::DidHandle) result = rc;
}
return result;
}
Result<void> HorizontalLayout::draw(Canvas& canvas) Result<void> HorizontalLayout::draw(Canvas& canvas)
{ {
for (auto widget : m_widgets) for (auto widget : m_widgets)
@ -159,6 +172,19 @@ namespace ui
return ui::EventResult::DidNotHandle; return ui::EventResult::DidNotHandle;
} }
Result<EventResult> VerticalLayout::handle_key_event(const ui::KeyEventRequest& request)
{
EventResult result = ui::EventResult::DidNotHandle;
for (auto widget : m_widgets)
{
auto rc = TRY(widget->handle_key_event(request));
if (rc == ui::EventResult::DidHandle) result = rc;
}
return result;
}
Result<void> VerticalLayout::draw(Canvas& canvas) Result<void> VerticalLayout::draw(Canvas& canvas)
{ {
for (auto widget : m_widgets) for (auto widget : m_widgets)

View File

@ -114,4 +114,10 @@ namespace ui
m_old_mouse_buttons = buttons; m_old_mouse_buttons = buttons;
return result; return result;
} }
Result<ui::EventResult> Window::handle_key_event(const ui::KeyEventRequest& request)
{
if (!m_main_widget) return ui::EventResult::DidNotHandle;
return m_main_widget->handle_key_event(request);
}
} }

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();

View File

@ -8,6 +8,8 @@ set(SOURCES
Window.cpp Window.cpp
IPC.cpp IPC.cpp
IPC.h IPC.h
Keyboard.cpp
Keyboard.h
Client.h Client.h
) )

View File

@ -1,4 +1,5 @@
#include "IPC.h" #include "IPC.h"
#include "Mouse.h"
#include "Screen.h" #include "Screen.h"
#include <luna/Alignment.h> #include <luna/Alignment.h>
#include <luna/String.h> #include <luna/String.h>
@ -129,6 +130,7 @@ static Result<void> handle_close_window_message(Client& client)
auto* window = client.windows[request.window]; auto* window = client.windows[request.window];
client.windows[request.window] = nullptr; client.windows[request.window] = nullptr;
g_windows.remove(window); g_windows.remove(window);
Mouse::the().window_did_close(window);
delete window; delete window;
return {}; return {};

305
wind/Keyboard.cpp Normal file
View File

@ -0,0 +1,305 @@
#include "Keyboard.h"
#include <luna/CType.h>
static const char table[] = {
// Function keys
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
// System keys
'\x1b',
'\0',
'\0',
'\0',
'\0',
// Modifier keys
'\0',
'\0',
'\0',
'\0', // or AltGr on some keyboards
'\0',
'\0',
// Navigation keys
'\t',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
// Editing keys
'\b',
'\n',
'\0',
'\x7f',
'\n',
// Lock keys
'\0',
'\0',
'\0',
// Keypad keys
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'.',
'+',
'-',
'*',
'/',
// Character keys (depending on keyboard layout), examples in US QWERTY
'`', // `
'1', // 1
'2', // 2
'3', // 3
'4', // 4
'5', // 5
'6', // 6
'7', // 7
'8', // 8
'9', // 9
'0', // 0
'-', // -
'=', // =
'q', // Q
'w', // W
'e', // E
'r', // R
't', // T
'y', // Y
'u', // U
'i', // I
'o', // O
'p', // P
'[', // [
']', // ]
'a', // A
's', // S
'd', // D
'f', // F
'g', // G
'h', // H
'j', // J
'k', // K
'l', // L
';', // ;
'\'', // '
'#', // #
'\\', // Backslash
'z', // Z
'x', // X
'c', // C
'v', // V
'b', // B
'n', // N
'm', // M
',', // ,
'.', // .
'/', // /
' ', // Space
// Unknown key
'\0',
};
static const char shift_table[] = {
// Function keys
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
// System keys
'\x1b',
'\0',
'\0',
'\0',
'\0',
// Modifier keys
'\0',
'\0',
'\0',
'\0', // or AltGr on some keyboards
'\0',
'\0',
// Navigation keys
'\t',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
'\0',
// Editing keys
'\b',
'\n',
'\0',
'\x7f',
'\n',
// Lock keys
'\0',
'\0',
'\0',
// Keypad keys
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'.',
'+',
'-',
'*',
'/',
// Character keys (depending on keyboard layout), examples in US QWERTY
'~', // `
'!',
'@',
'#',
'$',
'%',
'^',
'&',
'*',
'(',
')',
'_',
'+',
'Q',
'W',
'E',
'R',
'T',
'Y',
'U',
'I',
'O',
'P',
'{',
'}',
'A',
'S',
'D',
'F',
'G',
'H',
'J',
'K',
'L',
':',
'"',
' ', // #
'|', // Backslash
'Z',
'X',
'C',
'V',
'B',
'N',
'M',
'<',
'>',
'?',
' ', // Space
// Unknown key
'\0',
};
namespace wind::Keyboard
{
static bool g_caps_lock = false;
static bool g_right_shift = false;
static bool g_left_shift = false;
static bool g_right_control = false;
static bool g_left_control = false;
static bool g_altgr = false;
static bool g_alt = false;
static bool g_super = false;
ui::KeyEventRequest decode_keyboard_event(moon::KeyCode code, bool released)
{
ui::KeyEventRequest request;
request.code = code;
request.pressed = !released;
request.modifiers = 0;
switch (code)
{
case moon::K_CapsLock:
if (!released) { g_caps_lock = !g_caps_lock; }
break;
case moon::K_RightShift: g_right_shift = !released; break;
case moon::K_LeftShift: g_left_shift = !released; break;
case moon::K_RightControl: g_right_control = !released; break;
case moon::K_LeftControl: g_left_control = !released; break;
case moon::K_RightAlt: g_altgr = !released; break;
case moon::K_LeftAlt: g_alt = !released; break;
case moon::K_Super: g_super = !released; break;
default: break;
}
if ((g_caps_lock && !(g_left_shift || g_right_shift)) || (g_left_shift || g_right_shift))
{
request.modifiers |= ui::Mod_Shift;
}
if (g_right_control || g_left_control) request.modifiers |= ui::Mod_Ctrl;
if (g_alt) request.modifiers |= ui::Mod_Alt;
if (g_altgr) request.modifiers |= ui::Mod_AltGr;
if (g_super) request.modifiers |= ui::Mod_Super;
request.key = table[code];
if (request.modifiers & ui::Mod_Ctrl)
{
char letter;
if (request.modifiers & ui::Mod_Shift) letter = shift_table[code];
else
letter = table[code];
if (_islower(letter)) letter = (char)_toupper(letter);
if (_isupper(letter)) letter = 0x40;
if (letter == '@') letter = 0x40;
if (letter > 'Z' && letter < '`') letter = 0x40;
if (letter == '?') letter = 0x7f;
request.letter = letter;
return request;
}
if (request.modifiers & ui::Mod_Shift) request.letter = shift_table[code];
else
request.letter = table[code];
return request;
}
}

10
wind/Keyboard.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include <ui/ipc/Client.h>
namespace wind
{
namespace Keyboard
{
ui::KeyEventRequest decode_keyboard_event(moon::KeyCode code, bool released);
}
}

View File

@ -7,6 +7,8 @@
static SharedPtr<ui::Image> g_mouse_cursor; static SharedPtr<ui::Image> g_mouse_cursor;
static Mouse* s_mouse;
Mouse::Mouse(ui::Canvas& screen) Mouse::Mouse(ui::Canvas& screen)
{ {
m_position.x = screen.width / 2; m_position.x = screen.width / 2;
@ -14,6 +16,14 @@ Mouse::Mouse(ui::Canvas& screen)
m_screen_rect = screen.rect(); m_screen_rect = screen.rect();
g_mouse_cursor = ui::Image::load("/usr/share/cursors/default.tga").value_or({}); g_mouse_cursor = ui::Image::load("/usr/share/cursors/default.tga").value_or({});
s_mouse = this;
}
Mouse& Mouse::the()
{
check(s_mouse);
return *s_mouse;
} }
void Mouse::draw(ui::Canvas& screen) void Mouse::draw(ui::Canvas& screen)
@ -110,3 +120,10 @@ void Mouse::update(const moon::MousePacket& packet)
m_active_window = new_active_window; m_active_window = new_active_window;
} }
} }
void Mouse::window_did_close(Window* window)
{
if (m_dragging_window == window) { m_dragging_window = nullptr; }
if (m_active_window == window) { m_active_window = nullptr; }
}

View File

@ -13,6 +13,10 @@ class Mouse
void draw(ui::Canvas& screen); void draw(ui::Canvas& screen);
void window_did_close(Window* window);
static Mouse& the();
private: private:
ui::Point m_position; ui::Point m_position;
ui::Rect m_screen_rect; ui::Rect m_screen_rect;

View File

@ -1,6 +1,7 @@
#define CLIENT_IMPLEMENTATION #define CLIENT_IMPLEMENTATION
#include "Client.h" #include "Client.h"
#include "IPC.h" #include "IPC.h"
#include "Keyboard.h"
#include "Mouse.h" #include "Mouse.h"
#include "Screen.h" #include "Screen.h"
#include "Window.h" #include "Window.h"
@ -155,8 +156,14 @@ Result<int> luna_main(int argc, char** argv)
{ {
moon::KeyboardPacket packet; moon::KeyboardPacket packet;
TRY(keyboard->read_typed(packet)); TRY(keyboard->read_typed(packet));
os::println("%s key %d", packet.released ? "Released" : "Pressed", packet.key);
if (!packet.released && packet.key == moon::K_Tab) debug(clients); if (!packet.released && packet.key == moon::K_Tab) debug(clients);
auto request = wind::Keyboard::decode_keyboard_event((moon::KeyCode)packet.key, packet.released);
if (g_windows.last().has_value())
{
auto* window = g_windows.last().value();
request.window = window->id;
os::IPC::send_async(window->client->conn, request);
}
} }
for (usize i = 0; i < clients.size(); i++) for (usize i = 0; i < clients.size(); i++)
{ {
@ -172,6 +179,7 @@ Result<int> luna_main(int argc, char** argv)
if (window) if (window)
{ {
g_windows.remove(window); g_windows.remove(window);
mouse_pointer.window_did_close(window);
delete window; delete window;
} }
} }