Compare commits

...

17 Commits

Author SHA1 Message Date
Bananymous b90cfa8e5c test-window: poll events instead of sleeping 2025-05-13 10:19:13 +03:00
Bananymous e7f0cd0c4b stat: Fix return value when one of stats fails 2025-05-13 10:19:13 +03:00
Bananymous 0661b339a0 test-tcp: Fix name resolution
Comparison was the wrong way around so hostname lookup always failed.
2025-05-13 10:19:13 +03:00
Bananymous d1bb72f6fb Kernel: Don't allow CPU load printing underflow 2025-05-13 10:19:13 +03:00
Bananymous 1bcd1edbf5 Kernel/LibC: Implement basic epoll
This implementation is on top of inodes instead of fds as linux does it.
If I start finding ports/software that relies on epoll allowing
duplicate inodes, I will do what linux does.

I'm probably missing multiple epoll_notify's which may cause hangs but
the system seems to work fine :dd:
2025-05-13 10:18:05 +03:00
Bananymous 143a00626b Kernel: Fix fork cmdline copying and copy environ 2025-05-13 10:16:21 +03:00
Bananymous 0eb981f51c Kernel: Fix Random.h includes 2025-05-13 10:16:21 +03:00
Bananymous 6cc01349f7 Kernel: Fix unblocking recv through read 2025-05-13 10:16:21 +03:00
Bananymous a7bd4acd46 Kernel: Remove static variables from virtual tty 2025-05-13 10:16:21 +03:00
Bananymous 8e0a56b49a Kernel: Implement loopback network interface 2025-05-13 10:16:21 +03:00
Bananymous a8844ddd28 WindowServer: Send mouse release to same window as mouse press 2025-05-13 10:16:21 +03:00
Bananymous c18fefd5f3 BuildSystem: Cleanup cmake library linking 2025-05-07 18:42:37 +03:00
Bananymous 3040940e35 ports/tcc: Fix 32 bit build and add version name
tcc was using "Linux" for machine name in version string if it was not
explicitly specified.
2025-05-07 18:33:39 +03:00
Bananymous df3d2d57c3 ports/quake2: Update to new LibGUI interface 2025-05-07 17:23:15 +03:00
Bananymous 92d26f2216 ports/doom: Update to new LibGUI interface 2025-05-07 17:21:29 +03:00
Bananymous 0380c68f89 ports/tinygb: Update to new LibGUI interface 2025-05-07 17:21:29 +03:00
Bananymous 2a6dc6a7b7 LibGUI: Add missing Texture files 2025-05-07 17:21:29 +03:00
75 changed files with 1144 additions and 192 deletions

View File

@ -29,12 +29,12 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# include headers of ${library} to ${target}
function(banan_include_headers target library)
target_include_directories(${target} PRIVATE $<TARGET_PROPERTY:${library},SOURCE_DIR>/include)
target_include_directories(${target} PUBLIC $<TARGET_PROPERTY:${library},SOURCE_DIR>/include)
endfunction()
# include headers and link ${library} to ${target}
function(banan_link_library target library)
target_link_libraries(${target} PRIVATE ${library})
target_link_libraries(${target} PUBLIC ${library})
banan_include_headers(${target} ${library})
endfunction()

View File

@ -17,6 +17,7 @@ set(KERNEL_SOURCES
kernel/Device/RandomDevice.cpp
kernel/Device/ZeroDevice.cpp
kernel/ELF.cpp
kernel/Epoll.cpp
kernel/Errors.cpp
kernel/FS/DevFS/FileSystem.cpp
kernel/FS/Ext2/FileSystem.cpp
@ -55,6 +56,7 @@ set(KERNEL_SOURCES
kernel/Networking/E1000/E1000.cpp
kernel/Networking/E1000/E1000E.cpp
kernel/Networking/IPv4Layer.cpp
kernel/Networking/Loopback.cpp
kernel/Networking/NetworkInterface.cpp
kernel/Networking/NetworkLayer.cpp
kernel/Networking/NetworkManager.cpp

View File

@ -3,7 +3,7 @@
namespace Kernel
{
class DebugDevice : public CharacterDevice
class DebugDevice final : public CharacterDevice
{
public:
static BAN::ErrorOr<BAN::RefPtr<DebugDevice>> create(mode_t, uid_t, gid_t);
@ -24,6 +24,7 @@ namespace Kernel
virtual bool can_read_impl() const override { return false; }
virtual bool can_write_impl() const override { return true; }
virtual bool has_error_impl() const override { return false; }
virtual bool has_hangup_impl() const override { return false; }
private:
const dev_t m_rdev;

View File

@ -20,6 +20,7 @@ namespace Kernel
NVMeController,
NVMeNamespace,
Ethernet,
Loopback,
TmpFS,
};

View File

@ -38,6 +38,7 @@ namespace Kernel
virtual bool can_read_impl() const override { return true; }
virtual bool can_write_impl() const override { return true; }
virtual bool has_error_impl() const override { return false; }
virtual bool has_hangup_impl() const override { return false; }
private:
FramebufferDevice(mode_t mode, uid_t uid, gid_t gid, dev_t rdev, paddr_t paddr, uint32_t width, uint32_t height, uint32_t pitch, uint8_t bpp);

View File

@ -5,7 +5,7 @@
namespace Kernel
{
class NullDevice : public CharacterDevice
class NullDevice final : public CharacterDevice
{
public:
static BAN::ErrorOr<BAN::RefPtr<NullDevice>> create(mode_t, uid_t, gid_t);
@ -26,6 +26,7 @@ namespace Kernel
virtual bool can_read_impl() const override { return false; }
virtual bool can_write_impl() const override { return true; }
virtual bool has_error_impl() const override { return false; }
virtual bool has_hangup_impl() const override { return false; }
private:
const dev_t m_rdev;

View File

@ -3,7 +3,7 @@
namespace Kernel
{
class RandomDevice : public CharacterDevice
class RandomDevice final : public CharacterDevice
{
public:
static BAN::ErrorOr<BAN::RefPtr<RandomDevice>> create(mode_t, uid_t, gid_t);
@ -24,6 +24,7 @@ namespace Kernel
virtual bool can_read_impl() const override { return true; }
virtual bool can_write_impl() const override { return false; }
virtual bool has_error_impl() const override { return false; }
virtual bool has_hangup_impl() const override { return false; }
private:
const dev_t m_rdev;

View File

@ -3,7 +3,7 @@
namespace Kernel
{
class ZeroDevice : public CharacterDevice
class ZeroDevice final : public CharacterDevice
{
public:
static BAN::ErrorOr<BAN::RefPtr<ZeroDevice>> create(mode_t, uid_t, gid_t);
@ -24,6 +24,7 @@ namespace Kernel
virtual bool can_read_impl() const override { return true; }
virtual bool can_write_impl() const override { return false; }
virtual bool has_error_impl() const override { return false; }
virtual bool has_hangup_impl() const override { return false; }
private:
const dev_t m_rdev;

View File

@ -0,0 +1,68 @@
#pragma once
#include <BAN/CircularQueue.h>
#include <BAN/HashMap.h>
#include <BAN/HashSet.h>
#include <kernel/FS/Inode.h>
#include <sys/epoll.h>
namespace Kernel
{
class Epoll final : public Inode
{
public:
static BAN::ErrorOr<BAN::RefPtr<Epoll>> create();
~Epoll();
BAN::ErrorOr<void> ctl(int op, BAN::RefPtr<Inode> inode, epoll_event event);
BAN::ErrorOr<size_t> wait(BAN::Span<epoll_event> events, uint64_t waketime_ns);
void notify(BAN::RefPtr<Inode> inode, uint32_t event);
private:
Epoll() = default;
public:
ino_t ino() const override { return 0; }
Mode mode() const override { return { Mode::IRUSR | Mode::IWUSR }; }
nlink_t nlink() const override { return 0; }
uid_t uid() const override { return 0; }
gid_t gid() const override { return 0; }
off_t size() const override { return 0; }
timespec atime() const override { return {}; }
timespec mtime() const override { return {}; }
timespec ctime() const override { return {}; }
blksize_t blksize() const override { return PAGE_SIZE; }
blkcnt_t blocks() const override { return 0; }
dev_t dev() const override { return 0; }
dev_t rdev() const override { return 0; }
bool is_epoll() const override { return true; }
const FileSystem* filesystem() const override { return nullptr; }
bool can_read_impl() const override { return false; }
bool can_write_impl() const override { return false; }
bool has_error_impl() const override { return false; }
bool has_hangup_impl() const override { return false; }
BAN::ErrorOr<void> fsync_impl() override { return {}; }
private:
struct InodeRefPtrHash
{
BAN::hash_t operator()(const BAN::RefPtr<Inode>& inode)
{
return BAN::hash<const Inode*>()(inode.ptr());
}
};
private:
ThreadBlocker m_thread_blocker;
BAN::HashMap<BAN::RefPtr<Inode>, uint32_t, InodeRefPtrHash> m_ready_events;
BAN::HashMap<BAN::RefPtr<Inode>, epoll_event, InodeRefPtrHash> m_listening_events;
};
}

View File

@ -51,6 +51,7 @@ namespace Kernel
virtual bool can_read_impl() const override { return true; }
virtual bool can_write_impl() const override { return true; }
virtual bool has_error_impl() const override { return false; }
virtual bool has_hangup_impl() const override { return false; }
private:
// Returns maximum number of data blocks in use

View File

@ -49,6 +49,7 @@ namespace Kernel
virtual bool can_read_impl() const override { return true; }
virtual bool can_write_impl() const override { return true; }
virtual bool has_error_impl() const override { return false; }
virtual bool has_hangup_impl() const override { return false; }
private:
FATInode(FATFS& fs, const FAT::DirectoryEntry& entry, ino_t ino, uint32_t block_count)

View File

@ -1,6 +1,7 @@
#pragma once
#include <BAN/ByteSpan.h>
#include <BAN/LinkedList.h>
#include <BAN/RefPtr.h>
#include <BAN/String.h>
#include <BAN/StringView.h>
@ -19,9 +20,8 @@
namespace Kernel
{
class FileSystem;
class FileBackedRegion;
class FileSystem;
class SharedFileData;
class Inode : public BAN::RefCounted<Inode>
@ -85,6 +85,7 @@ namespace Kernel
virtual dev_t rdev() const = 0;
virtual bool is_device() const { return false; }
virtual bool is_epoll() const { return false; }
virtual bool is_pipe() const { return false; }
virtual bool is_tty() const { return false; }
@ -123,9 +124,14 @@ namespace Kernel
bool can_read() const;
bool can_write() const;
bool has_error() const;
bool has_hangup() const;
BAN::ErrorOr<long> ioctl(int request, void* arg);
BAN::ErrorOr<void> add_epoll(class Epoll*);
void del_epoll(class Epoll*);
void epoll_notify(uint32_t event);
protected:
// Directory API
virtual BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode_impl(BAN::StringView) { return BAN::Error::from_errno(ENOTSUP); }
@ -160,6 +166,7 @@ namespace Kernel
virtual bool can_read_impl() const = 0;
virtual bool can_write_impl() const = 0;
virtual bool has_error_impl() const = 0;
virtual bool has_hangup_impl() const = 0;
virtual BAN::ErrorOr<long> ioctl_impl(int, void*) { return BAN::Error::from_errno(ENOTSUP); }
@ -168,6 +175,7 @@ namespace Kernel
private:
BAN::WeakPtr<SharedFileData> m_shared_region;
BAN::LinkedList<class Epoll*> m_epolls;
friend class FileBackedRegion;
friend class OpenFileDescriptorSet;
friend class SharedFileData;

View File

@ -40,6 +40,7 @@ namespace Kernel
virtual bool can_read_impl() const override { return m_buffer_size > 0; }
virtual bool can_write_impl() const override { return true; }
virtual bool has_error_impl() const override { return false; }
virtual bool has_hangup_impl() const override { return m_writing_count == 0; }
private:
Pipe(const Credentials&);

View File

@ -47,6 +47,7 @@ namespace Kernel
virtual bool can_read_impl() const override { return true; }
virtual bool can_write_impl() const override { return false; }
virtual bool has_error_impl() const override { return false; }
virtual bool has_hangup_impl() const override { return false; }
private:
ProcROProcessInode(Process&, size_t (Process::*)(off_t, BAN::ByteSpan) const, TmpFileSystem&, const TmpInodeInfo&);
@ -72,6 +73,7 @@ namespace Kernel
virtual bool can_read_impl() const override { return true; }
virtual bool can_write_impl() const override { return false; }
virtual bool has_error_impl() const override { return false; }
virtual bool has_hangup_impl() const override { return false; }
private:
ProcROInode(size_t (*callback)(off_t, BAN::ByteSpan), TmpFileSystem&, const TmpInodeInfo&);

View File

@ -80,6 +80,7 @@ namespace Kernel
virtual bool can_read_impl() const override { return true; }
virtual bool can_write_impl() const override { return true; }
virtual bool has_error_impl() const override { return false; }
virtual bool has_hangup_impl() const override { return false; }
private:
TmpFileInode(TmpFileSystem&, ino_t, const TmpInodeInfo&);
@ -102,6 +103,7 @@ namespace Kernel
virtual bool can_read_impl() const override { return false; }
virtual bool can_write_impl() const override { return false; }
virtual bool has_error_impl() const override { return false; }
virtual bool has_hangup_impl() const override { return false; }
private:
TmpSocketInode(TmpFileSystem&, ino_t, const TmpInodeInfo&);
@ -123,6 +125,7 @@ namespace Kernel
virtual bool can_read_impl() const override { return false; }
virtual bool can_write_impl() const override { return false; }
virtual bool has_error_impl() const override { return false; }
virtual bool has_hangup_impl() const override { return false; }
private:
TmpSymlinkInode(TmpFileSystem&, ino_t, const TmpInodeInfo&);
@ -153,6 +156,7 @@ namespace Kernel
virtual bool can_read_impl() const override { return false; }
virtual bool can_write_impl() const override { return false; }
virtual bool has_error_impl() const override { return false; }
virtual bool has_hangup_impl() const override { return false; }
private:
template<TmpFuncs::for_each_valid_entry_callback F>

View File

@ -31,7 +31,7 @@ namespace Kernel
bool can_read_impl() const override { SpinLockGuard _(m_event_lock); return m_event_count > 0; }
bool can_write_impl() const override { return false; }
bool has_error_impl() const override { return false; }
bool has_hangup_impl() const override { return false; }
private:
BAN::ErrorOr<size_t> read_non_block(BAN::ByteSpan);
@ -64,7 +64,7 @@ namespace Kernel
public:
static BAN::ErrorOr<BAN::RefPtr<KeyboardDevice>> create(mode_t mode, uid_t uid, gid_t gid);
void notify() { m_thread_blocker.unblock(); }
void notify();
private:
KeyboardDevice(mode_t mode, uid_t uid, gid_t gid);
@ -73,6 +73,7 @@ namespace Kernel
bool can_read_impl() const override;
bool can_write_impl() const override { return false; }
bool has_error_impl() const override { return false; }
bool has_hangup_impl() const override { return false; }
BAN::StringView name() const final override { return m_name; }
dev_t rdev() const final override { return m_rdev; }
@ -90,7 +91,7 @@ namespace Kernel
public:
static BAN::ErrorOr<BAN::RefPtr<MouseDevice>> create(mode_t mode, uid_t uid, gid_t gid);
void notify() { m_thread_blocker.unblock(); }
void notify();
private:
MouseDevice(mode_t mode, uid_t uid, gid_t gid);
@ -99,6 +100,7 @@ namespace Kernel
bool can_read_impl() const override;
bool can_write_impl() const override { return false; }
bool has_error_impl() const override { return false; }
bool has_hangup_impl() const override { return false; }
BAN::StringView name() const final override { return m_name; }
dev_t rdev() const final override { return m_rdev; }

View File

@ -28,13 +28,14 @@ namespace Kernel
virtual bool link_up() override { return m_link_up; }
virtual int link_speed() override;
virtual size_t payload_mtu() const { return E1000_RX_BUFFER_SIZE - sizeof(EthernetHeader); }
virtual size_t payload_mtu() const override { return E1000_RX_BUFFER_SIZE - sizeof(EthernetHeader); }
virtual void handle_irq() final override;
protected:
E1000(PCI::Device& pci_device)
: m_pci_device(pci_device)
: NetworkInterface(Type::Ethernet)
, m_pci_device(pci_device)
{ }
BAN::ErrorOr<void> initialize();
@ -49,6 +50,7 @@ namespace Kernel
virtual bool can_read_impl() const override { return false; }
virtual bool can_write_impl() const override { return false; }
virtual bool has_error_impl() const override { return false; }
virtual bool has_hangup_impl() const override { return false; }
private:
BAN::ErrorOr<void> read_mac_address();

View File

@ -0,0 +1,40 @@
#pragma once
#include <kernel/Networking/NetworkInterface.h>
namespace Kernel
{
class LoopbackInterface : public NetworkInterface
{
public:
static constexpr size_t buffer_size = BAN::numeric_limits<uint16_t>::max() + 1;
public:
static BAN::ErrorOr<BAN::RefPtr<LoopbackInterface>> create();
BAN::MACAddress get_mac_address() const override { return {}; }
bool link_up() override { return true; }
int link_speed() override { return 1000; }
size_t payload_mtu() const override { return buffer_size - sizeof(EthernetHeader); }
protected:
LoopbackInterface()
: NetworkInterface(Type::Loopback)
{}
BAN::ErrorOr<void> send_bytes(BAN::MACAddress destination, EtherType protocol, BAN::ConstByteSpan) override;
bool can_read_impl() const override { return false; }
bool can_write_impl() const override { return false; }
bool has_error_impl() const override { return false; }
bool has_hangup_impl() const override { return false; }
private:
SpinLock m_buffer_lock;
BAN::UniqPtr<VirtualRange> m_buffer;
};
}

View File

@ -32,10 +32,11 @@ namespace Kernel
enum class Type
{
Ethernet,
Loopback,
};
public:
NetworkInterface();
NetworkInterface(Type);
virtual ~NetworkInterface() {}
virtual BAN::MACAddress get_mac_address() const = 0;
@ -49,6 +50,8 @@ namespace Kernel
BAN::IPv4Address get_gateway() const { return m_gateway; }
void set_gateway(BAN::IPv4Address new_gateway) { m_gateway = new_gateway; }
Type type() const { return m_type; }
virtual bool link_up() = 0;
virtual int link_speed() = 0;

View File

@ -22,7 +22,7 @@ namespace Kernel
BAN::ErrorOr<void> add_interface(PCI::Device& pci_device);
BAN::Vector<BAN::RefPtr<NetworkInterface>> interfaces() { return m_interfaces; }
BAN::Vector<BAN::RefPtr<NetworkInterface>>& interfaces() { return m_interfaces; }
BAN::ErrorOr<BAN::RefPtr<Socket>> create_socket(Socket::Domain, Socket::Type, mode_t, uid_t, gid_t);
@ -31,6 +31,8 @@ namespace Kernel
private:
NetworkManager() {}
BAN::ErrorOr<void> add_interface(BAN::RefPtr<NetworkInterface>);
private:
BAN::UniqPtr<IPv4Layer> m_ipv4_layer;
BAN::Vector<BAN::RefPtr<NetworkInterface>> m_interfaces;

View File

@ -26,7 +26,8 @@ namespace Kernel
protected:
RTL8169(PCI::Device& pci_device)
: m_pci_device(pci_device)
: NetworkInterface(Type::Ethernet)
, m_pci_device(pci_device)
{ }
BAN::ErrorOr<void> initialize();
@ -35,6 +36,7 @@ namespace Kernel
virtual bool can_read_impl() const override { return false; }
virtual bool can_write_impl() const override { return false; }
virtual bool has_error_impl() const override { return false; }
virtual bool has_hangup_impl() const override { return false; }
private:
BAN::ErrorOr<void> reset();

View File

@ -67,6 +67,7 @@ namespace Kernel
virtual bool can_read_impl() const override;
virtual bool can_write_impl() const override;
virtual bool has_error_impl() const override { return false; }
virtual bool has_hangup_impl() const override;
private:
enum class State

View File

@ -40,6 +40,7 @@ namespace Kernel
virtual bool can_read_impl() const override { return !m_packets.empty(); }
virtual bool can_write_impl() const override { return true; }
virtual bool has_error_impl() const override { return false; }
virtual bool has_hangup_impl() const override { return false; }
private:
UDPSocket(NetworkLayer&, const Socket::Info&);

View File

@ -1,5 +1,6 @@
#pragma once
#include <BAN/CircularQueue.h>
#include <BAN/Queue.h>
#include <BAN/WeakPtr.h>
#include <kernel/FS/Socket.h>
@ -28,6 +29,7 @@ namespace Kernel
virtual bool can_read_impl() const override;
virtual bool can_write_impl() const override;
virtual bool has_error_impl() const override { return false; }
virtual bool has_hangup_impl() const override;
private:
UnixDomainSocket(Socket::Type, const Socket::Info&);
@ -48,7 +50,7 @@ namespace Kernel
mutable BAN::Atomic<bool> target_closed { false };
BAN::WeakPtr<UnixDomainSocket> connection;
BAN::Queue<BAN::RefPtr<UnixDomainSocket>> pending_connections;
ThreadBlocker pending_thread_blocker;
ThreadBlocker pending_thread_blocker;
SpinLock pending_lock;
};
@ -67,7 +69,7 @@ namespace Kernel
size_t m_packet_size_total { 0 };
BAN::UniqPtr<VirtualRange> m_packet_buffer;
SpinLock m_packet_lock;
ThreadBlocker m_packet_thread_blocker;
ThreadBlocker m_packet_thread_blocker;
friend class BAN::RefPtr<UnixDomainSocket>;
};

View File

@ -24,6 +24,8 @@
#include <sys/time.h>
#include <termios.h>
struct epoll_event;
namespace Kernel
{
@ -55,7 +57,7 @@ namespace Kernel
bool is_session_leader() const { return pid() == sid(); }
const char* name() const { return m_cmdline.empty() ? "" : m_cmdline.front().data(); }
const char* name() const { return m_cmdline.empty() ? "<unknown>" : m_cmdline.front().data(); }
const Credentials& credentials() const { return m_credentials; }
@ -130,6 +132,10 @@ namespace Kernel
BAN::ErrorOr<long> sys_pselect(sys_pselect_t* arguments);
BAN::ErrorOr<long> sys_epoll_create1(int flags);
BAN::ErrorOr<long> sys_epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);
BAN::ErrorOr<long> sys_epoll_pwait2(int epfd, struct epoll_event* events, int maxevents, const struct timespec* timeout, const sigset_t* sigmask);
BAN::ErrorOr<long> sys_pipe(int fildes[2]);
BAN::ErrorOr<long> sys_dup2(int fildes, int fildes2);

View File

@ -1,5 +1,7 @@
#pragma once
#include <BAN/Traits.h>
#include <stdint.h>
namespace Kernel

View File

@ -27,6 +27,7 @@ namespace Kernel
virtual bool can_read_impl() const override { return false; }
virtual bool can_write_impl() const override { return false; }
virtual bool has_error_impl() const override { return false; }
virtual bool has_hangup_impl() const override { return false; }
private:
NVMeController(PCI::Device& pci_device);

View File

@ -17,7 +17,7 @@ namespace Kernel
virtual uint64_t total_size() const override { return m_block_size * m_block_count; }
virtual dev_t rdev() const override { return m_rdev; }
virtual BAN::StringView name() const { return m_name; }
virtual BAN::StringView name() const override { return m_name; }
private:
NVMeNamespace(NVMeController&, uint32_t ns_index, uint32_t nsid, uint64_t block_count, uint32_t block_size);

View File

@ -53,6 +53,7 @@ namespace Kernel
virtual bool can_read_impl() const override { return true; }
virtual bool can_write_impl() const override { return true; }
virtual bool has_error_impl() const override { return false; }
virtual bool has_hangup_impl() const override { return false; }
private:
const dev_t m_rdev;

View File

@ -47,6 +47,7 @@ namespace Kernel
virtual bool can_read_impl() const override { return true; }
virtual bool can_write_impl() const override { return true; }
virtual bool has_error_impl() const override { return false; }
virtual bool has_hangup_impl() const override { return false; }
private:
Mutex m_mutex;

View File

@ -29,6 +29,7 @@ namespace Kernel
bool can_read_impl() const override { SpinLockGuard _(m_buffer_lock); return m_buffer_size > 0; }
bool can_write_impl() const override { SpinLockGuard _(m_buffer_lock); return m_buffer_size < m_buffer->size(); }
bool has_error_impl() const override { return false; }
bool has_hangup_impl() const override { return !m_slave.valid(); }
BAN::ErrorOr<long> ioctl_impl(int, void*) override;
@ -63,6 +64,8 @@ namespace Kernel
protected:
bool putchar_impl(uint8_t ch) override;
bool has_hangup_impl() const override { return !m_master.valid(); }
BAN::ErrorOr<long> ioctl_impl(int, void*) override;
private:

View File

@ -54,6 +54,7 @@ namespace Kernel
virtual bool can_read_impl() const override { return m_output.flush; }
virtual bool can_write_impl() const override { return true; }
virtual bool has_error_impl() const override { return false; }
virtual bool has_hangup_impl() const override { return false; }
protected:
TTY(mode_t mode, uid_t uid, gid_t gid);

View File

@ -94,6 +94,10 @@ namespace Kernel
uint32_t m_column { 0 };
Cell* m_buffer { nullptr };
bool m_last_cursor_shown { false };
uint32_t m_last_cursor_row { static_cast<uint32_t>(-1) };
uint32_t m_last_cursor_column { static_cast<uint32_t>(-1) };
const Palette& m_palette;
TerminalDriver::Color m_foreground;

View File

@ -73,6 +73,7 @@ namespace Kernel::ACPI
bool can_read_impl() const override { return true; }
bool can_write_impl() const override { return false; }
bool has_error_impl() const override { return false; }
bool has_hangup_impl() const override { return false; }
private:
BatteryInfoInode(AML::Namespace& acpi_namespace, AML::Scope&& battery_path, AML::NameString&& method, size_t index, ino_t ino, const TmpInodeInfo& info)

137
kernel/kernel/Epoll.cpp Normal file
View File

@ -0,0 +1,137 @@
#include <kernel/Epoll.h>
#include <kernel/Lock/LockGuard.h>
#include <kernel/Timer/Timer.h>
namespace Kernel
{
BAN::ErrorOr<BAN::RefPtr<Epoll>> Epoll::create()
{
auto* epoll_ptr = new Epoll();
if (epoll_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
return BAN::RefPtr<Epoll>::adopt(epoll_ptr);
}
Epoll::~Epoll()
{
for (auto [inode, _] : m_listening_events)
inode->del_epoll(this);
}
BAN::ErrorOr<void> Epoll::ctl(int op, BAN::RefPtr<Inode> inode, epoll_event event)
{
LockGuard _(m_mutex);
auto it = m_listening_events.find(inode);
switch (op)
{
case EPOLL_CTL_ADD:
if (it != m_listening_events.end())
return BAN::Error::from_errno(EEXIST);
TRY(m_listening_events.reserve(m_listening_events.size() + 1));
TRY(m_ready_events.reserve(m_listening_events.size() + 1));
TRY(inode->add_epoll(this));
MUST(m_listening_events.insert(inode, event));
MUST(m_ready_events.insert(inode, event.events));
return {};
case EPOLL_CTL_MOD:
if (it == m_listening_events.end())
return BAN::Error::from_errno(ENOENT);
MUST(m_ready_events.emplace_or_assign(inode, event.events));
it->value = event;
return {};
case EPOLL_CTL_DEL:
if (it == m_listening_events.end())
return BAN::Error::from_errno(ENOENT);
m_listening_events.remove(it);
m_ready_events.remove(inode);
inode->del_epoll(this);
return {};
}
return BAN::Error::from_errno(EINVAL);
}
BAN::ErrorOr<size_t> Epoll::wait(BAN::Span<epoll_event> event_span, uint64_t waketime_ns)
{
size_t count = 0;
for (;;)
{
{
LockGuard _(m_mutex);
for (auto it = m_ready_events.begin(); it != m_ready_events.end() && count < event_span.size();)
{
auto& [inode, events] = *it;
auto& listen = m_listening_events[inode];
const uint32_t listen_mask = (listen.events & (EPOLLIN | EPOLLOUT)) | EPOLLERR | EPOLLHUP;
events &= listen_mask;
#define CHECK_EVENT_BIT(mask, func) \
if ((events & mask) && !inode->func()) \
events &= ~mask;
CHECK_EVENT_BIT(EPOLLIN, can_read);
CHECK_EVENT_BIT(EPOLLOUT, can_write);
CHECK_EVENT_BIT(EPOLLERR, has_error);
CHECK_EVENT_BIT(EPOLLHUP, has_hangup);
#undef CHECK_EVENT_BIT
if (events == 0)
{
m_ready_events.remove(it);
it = m_ready_events.begin();
continue;
}
event_span[count++] = {
.events = events,
.data = listen.data,
};
if (listen.events & EPOLLONESHOT)
listen.events = 0;
if (listen.events & EPOLLET)
events &= ~listen_mask;
it++;
}
}
if (count)
break;
const uint64_t current_ns = SystemTimer::get().ns_since_boot();
if (current_ns >= waketime_ns)
break;
const uint64_t timeout_ns = BAN::Math::min<uint64_t>(100'000'000, waketime_ns - current_ns);
TRY(Thread::current().block_or_eintr_or_timeout_ns(m_thread_blocker, timeout_ns, false));
}
return count;
}
void Epoll::notify(BAN::RefPtr<Inode> inode, uint32_t event)
{
LockGuard _(m_mutex);
auto listen_it = m_listening_events.find(inode);
if (listen_it == m_listening_events.end())
return;
event &= (listen_it->value.events & (EPOLLIN | EPOLLOUT)) | EPOLLERR | EPOLLHUP;
if (event == 0)
return;
if (auto ready_it = m_ready_events.find(inode); ready_it != m_ready_events.end())
ready_it->value |= event;
else
MUST(m_ready_events.insert(inode, event));
m_thread_blocker.unblock();
}
}

View File

@ -1,3 +1,4 @@
#include <kernel/Epoll.h>
#include <kernel/FS/Inode.h>
#include <kernel/Lock/LockGuard.h>
#include <kernel/Memory/FileBackedRegion.h>
@ -249,10 +250,39 @@ namespace Kernel
return has_error_impl();
}
bool Inode::has_hangup() const
{
LockGuard _(m_mutex);
return has_hangup_impl();
}
BAN::ErrorOr<long> Inode::ioctl(int request, void* arg)
{
LockGuard _(m_mutex);
return ioctl_impl(request, arg);
}
BAN::ErrorOr<void> Inode::add_epoll(class Epoll* epoll)
{
TRY(m_epolls.push_back(epoll));
return {};
}
void Inode::del_epoll(class Epoll* epoll)
{
for (auto it = m_epolls.begin(); it != m_epolls.end(); it++)
{
if (*it != epoll)
continue;
m_epolls.remove(it);
break;
}
}
void Inode::epoll_notify(uint32_t event)
{
for (auto* epoll : m_epolls)
epoll->notify(this, event);
}
}

View File

@ -3,7 +3,7 @@
#include <kernel/Thread.h>
#include <kernel/Timer/Timer.h>
#include <kernel/Process.h>
#include <sys/epoll.h>
namespace Kernel
{
@ -36,8 +36,10 @@ namespace Kernel
{
auto old_writing_count = m_writing_count.fetch_sub(1);
ASSERT(old_writing_count > 0);
if (old_writing_count == 1)
m_thread_blocker.unblock();
if (old_writing_count != 1)
return;
epoll_notify(EPOLLHUP);
m_thread_blocker.unblock();
}
BAN::ErrorOr<size_t> Pipe::read_impl(off_t, BAN::ByteSpan buffer)
@ -69,6 +71,8 @@ namespace Kernel
m_atime = SystemTimer::get().real_time();
epoll_notify(EPOLLOUT);
m_thread_blocker.unblock();
return to_copy;
@ -103,6 +107,8 @@ namespace Kernel
m_mtime = current_time;
m_ctime = current_time;
epoll_notify(EPOLLIN);
m_thread_blocker.unblock();
return to_copy;

View File

@ -226,7 +226,6 @@ namespace Kernel
}
/* SOCKET INODE */
BAN::ErrorOr<BAN::RefPtr<TmpSocketInode>> TmpSocketInode::create_new(TmpFileSystem& fs, mode_t mode, uid_t uid, gid_t gid)
{
auto info = create_inode_info(Mode::IFSOCK | mode, uid, gid);

View File

@ -6,6 +6,7 @@
#include <LibInput/KeyEvent.h>
#include <LibInput/MouseEvent.h>
#include <sys/epoll.h>
#include <sys/sysmacros.h>
namespace Kernel
@ -85,54 +86,58 @@ namespace Kernel
void InputDevice::add_event(BAN::ConstByteSpan event)
{
SpinLockGuard _(m_event_lock);
ASSERT(event.size() == m_event_size);
if (m_type == Type::Mouse && m_event_count > 0)
{
const size_t last_index = (m_event_head + m_max_event_count - 1) % m_max_event_count;
SpinLockGuard _(m_event_lock);
ASSERT(event.size() == m_event_size);
auto& last_event = *reinterpret_cast<LibInput::MouseEvent*>(&m_event_buffer[last_index * m_event_size]);
auto& curr_event = event.as<const LibInput::MouseEvent>();
if (last_event.type == LibInput::MouseEventType::MouseMoveEvent && curr_event.type == LibInput::MouseEventType::MouseMoveEvent)
if (m_type == Type::Mouse && m_event_count > 0)
{
last_event.move_event.rel_x += curr_event.move_event.rel_x;
last_event.move_event.rel_y += curr_event.move_event.rel_y;
return;
}
if (last_event.type == LibInput::MouseEventType::MouseScrollEvent && curr_event.type == LibInput::MouseEventType::MouseScrollEvent)
{
last_event.scroll_event.scroll += curr_event.scroll_event.scroll;
return;
}
}
const size_t last_index = (m_event_head + m_max_event_count - 1) % m_max_event_count;
if (m_type == Type::Keyboard)
{
auto& key_event = event.as<const LibInput::RawKeyEvent>();
if (key_event.modifier & LibInput::KeyEvent::Modifier::Pressed)
{
switch (key_event.keycode)
auto& last_event = *reinterpret_cast<LibInput::MouseEvent*>(&m_event_buffer[last_index * m_event_size]);
auto& curr_event = event.as<const LibInput::MouseEvent>();
if (last_event.type == LibInput::MouseEventType::MouseMoveEvent && curr_event.type == LibInput::MouseEventType::MouseMoveEvent)
{
case LibInput::keycode_function(1):
Processor::toggle_should_print_cpu_load();
break;
case LibInput::keycode_function(12):
Kernel::panic("Keyboard kernel panic :)");
break;
last_event.move_event.rel_x += curr_event.move_event.rel_x;
last_event.move_event.rel_y += curr_event.move_event.rel_y;
return;
}
if (last_event.type == LibInput::MouseEventType::MouseScrollEvent && curr_event.type == LibInput::MouseEventType::MouseScrollEvent)
{
last_event.scroll_event.scroll += curr_event.scroll_event.scroll;
return;
}
}
if (m_type == Type::Keyboard)
{
auto& key_event = event.as<const LibInput::RawKeyEvent>();
if (key_event.modifier & LibInput::KeyEvent::Modifier::Pressed)
{
switch (key_event.keycode)
{
case LibInput::keycode_function(1):
Processor::toggle_should_print_cpu_load();
break;
case LibInput::keycode_function(12):
Kernel::panic("Keyboard kernel panic :)");
break;
}
}
}
if (m_event_count == m_max_event_count)
{
m_event_tail = (m_event_tail + 1) % m_max_event_count;
m_event_count--;
}
memcpy(&m_event_buffer[m_event_head * m_event_size], event.data(), m_event_size);
m_event_head = (m_event_head + 1) % m_max_event_count;
m_event_count++;
}
if (m_event_count == m_max_event_count)
{
m_event_tail = (m_event_tail + 1) % m_max_event_count;
m_event_count--;
}
memcpy(&m_event_buffer[m_event_head * m_event_size], event.data(), m_event_size);
m_event_head = (m_event_head + 1) % m_max_event_count;
m_event_count++;
epoll_notify(EPOLLIN);
m_event_thread_blocker.unblock();
if (m_type == Type::Keyboard && s_keyboard_device)
@ -197,6 +202,12 @@ namespace Kernel
, m_name("keyboard"_sv)
{}
void KeyboardDevice::notify()
{
epoll_notify(EPOLLIN);
m_thread_blocker.unblock();
}
BAN::ErrorOr<size_t> KeyboardDevice::read_impl(off_t, BAN::ByteSpan buffer)
{
if (buffer.size() < sizeof(LibInput::RawKeyEvent))
@ -243,6 +254,12 @@ namespace Kernel
, m_name("mouse"_sv)
{}
void MouseDevice::notify()
{
epoll_notify(EPOLLIN);
m_thread_blocker.unblock();
}
BAN::ErrorOr<size_t> MouseDevice::read_impl(off_t, BAN::ByteSpan buffer)
{
if (buffer.size() < sizeof(LibInput::MouseEvent))

View File

@ -44,10 +44,22 @@ namespace Kernel
if (ipv4_address == s_broadcast_ipv4)
return s_broadcast_mac;
const auto netmask = interface.get_netmask();
const bool same_subnet = ipv4_address.mask(netmask) == interface.get_ipv4_address().mask(netmask);
if (interface.type() == NetworkInterface::Type::Loopback)
{
if (!same_subnet)
return BAN::Error::from_errno(EADDRNOTAVAIL);
return BAN::MACAddress {};
}
ASSERT(interface.type() == NetworkInterface::Type::Ethernet);
if (interface.get_ipv4_address() == BAN::IPv4Address { 0 })
return BAN::Error::from_errno(EINVAL);
if (interface.get_ipv4_address().mask(interface.get_netmask()) != ipv4_address.mask(interface.get_netmask()))
if (!same_subnet)
ipv4_address = interface.get_gateway();
{

View File

@ -107,9 +107,6 @@ namespace Kernel
BAN::ErrorOr<void> IPv4Layer::bind_socket_to_address(BAN::RefPtr<NetworkSocket> socket, const sockaddr* address, socklen_t address_len)
{
if (NetworkManager::get().interfaces().empty())
return BAN::Error::from_errno(EADDRNOTAVAIL);
if (!address || address_len < (socklen_t)sizeof(sockaddr_in))
return BAN::Error::from_errno(EINVAL);
if (address->sa_family != AF_INET)
@ -119,6 +116,22 @@ namespace Kernel
const uint16_t port = BAN::host_to_network_endian(sockaddr_in.sin_port);
if (port == NetworkSocket::PORT_NONE)
return bind_socket_to_unused(socket, address, address_len);
const auto ipv4 = BAN::IPv4Address { sockaddr_in.sin_addr.s_addr };
BAN::RefPtr<NetworkInterface> bind_interface;
for (auto interface : NetworkManager::get().interfaces())
{
if (interface->type() != NetworkInterface::Type::Loopback)
bind_interface = interface;
const auto netmask = interface->get_netmask();
if (ipv4.mask(netmask) != interface->get_ipv4_address().mask(netmask))
continue;
bind_interface = interface;
break;
}
if (!bind_interface)
return BAN::Error::from_errno(EADDRNOTAVAIL);
SpinLockGuard _(m_bound_socket_lock);
@ -126,9 +139,7 @@ namespace Kernel
return BAN::Error::from_errno(EADDRINUSE);
TRY(m_bound_sockets.insert(port, TRY(socket->get_weak_ptr())));
// FIXME: actually determine proper interface
auto interface = NetworkManager::get().interfaces().front();
socket->bind_interface_and_port(interface.ptr(), port);
socket->bind_interface_and_port(bind_interface.ptr(), port);
return {};
}

View File

@ -0,0 +1,49 @@
#include <kernel/Networking/Loopback.h>
#include <kernel/Networking/NetworkManager.h>
namespace Kernel
{
BAN::ErrorOr<BAN::RefPtr<LoopbackInterface>> LoopbackInterface::create()
{
auto* loopback_ptr = new LoopbackInterface();
if (loopback_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto loopback = BAN::RefPtr<LoopbackInterface>::adopt(loopback_ptr);
loopback->m_buffer = TRY(VirtualRange::create_to_vaddr_range(
PageTable::kernel(),
KERNEL_OFFSET,
BAN::numeric_limits<vaddr_t>::max(),
buffer_size,
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
true
));
loopback->set_ipv4_address({ 127, 0, 0, 1 });
loopback->set_netmask({ 255, 0, 0, 0 });
return loopback;
}
BAN::ErrorOr<void> LoopbackInterface::send_bytes(BAN::MACAddress destination, EtherType protocol, BAN::ConstByteSpan buffer)
{
ASSERT(buffer.size() + sizeof(EthernetHeader) <= buffer_size);
SpinLockGuard _(m_buffer_lock);
uint8_t* buffer_vaddr = reinterpret_cast<uint8_t*>(m_buffer->vaddr());
auto& ethernet_header = *reinterpret_cast<EthernetHeader*>(buffer_vaddr);
ethernet_header.dst_mac = destination;
ethernet_header.src_mac = get_mac_address();
ethernet_header.ether_type = protocol;
memcpy(buffer_vaddr + sizeof(EthernetHeader), buffer.data(), buffer.size());
NetworkManager::get().on_receive(*this, BAN::ConstByteSpan {
buffer_vaddr,
buffer.size() + sizeof(EthernetHeader)
});
return {};
}
}

View File

@ -9,22 +9,39 @@
namespace Kernel
{
static dev_t get_network_rdev_minor()
static BAN::Atomic<dev_t> s_ethernet_rdev_minor = 0;
static BAN::Atomic<dev_t> s_loopback_rdev_minor = 0;
static dev_t get_rdev(NetworkInterface::Type type)
{
static dev_t minor = 0;
return minor++;
switch (type)
{
case NetworkInterface::Type::Ethernet:
return makedev(DeviceNumber::Ethernet, s_ethernet_rdev_minor++);
case NetworkInterface::Type::Loopback:
return makedev(DeviceNumber::Ethernet, s_loopback_rdev_minor++);
}
ASSERT_NOT_REACHED();
}
NetworkInterface::NetworkInterface()
NetworkInterface::NetworkInterface(Type type)
: CharacterDevice(0400, 0, 0)
, m_type(Type::Ethernet)
, m_rdev(makedev(DeviceNumber::Ethernet, get_network_rdev_minor()))
, m_type(type)
, m_rdev(get_rdev(type))
{
ASSERT(minor(m_rdev) < 10);
ASSERT(m_type == Type::Ethernet);
strcpy(m_name, "ethx");
m_name[3] = minor(m_rdev) + '0';
switch (type)
{
case Type::Ethernet:
strcpy(m_name, "ethx");
m_name[3] = minor(m_rdev) + '0';
break;
case Type::Loopback:
strcpy(m_name, "lox");
m_name[2] = minor(m_rdev) + '0';
break;
}
}
}

View File

@ -4,6 +4,7 @@
#include <kernel/Networking/E1000/E1000.h>
#include <kernel/Networking/E1000/E1000E.h>
#include <kernel/Networking/ICMP.h>
#include <kernel/Networking/Loopback.h>
#include <kernel/Networking/NetworkManager.h>
#include <kernel/Networking/RTL8169/RTL8169.h>
#include <kernel/Networking/TCPSocket.h>
@ -19,6 +20,7 @@ namespace Kernel
{
ASSERT(!s_instance);
auto manager = TRY(BAN::UniqPtr<NetworkManager>::create());
TRY(manager->add_interface(TRY(LoopbackInterface::create())));
manager->m_ipv4_layer = TRY(IPv4Layer::create());
s_instance = BAN::move(manager);
return {};
@ -62,9 +64,13 @@ namespace Kernel
ASSERT(interface);
return add_interface(interface);
}
BAN::ErrorOr<void> NetworkManager::add_interface(BAN::RefPtr<NetworkInterface> interface)
{
TRY(m_interfaces.push_back(interface));
DevFileSystem::get().add_device(interface);
return {};
}

View File

@ -6,6 +6,7 @@
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/epoll.h>
namespace Kernel
{
@ -271,6 +272,11 @@ namespace Kernel
return m_send_window.data_size < m_send_window.buffer->size();
}
bool TCPSocket::has_hangup_impl() const
{
return m_has_connected && m_state != State::Established;
}
BAN::ErrorOr<size_t> TCPSocket::return_with_maybe_zero()
{
ASSERT(m_state != State::Established);
@ -577,6 +583,8 @@ namespace Kernel
memcpy(buffer + m_recv_window.data_size, payload.data(), payload.size());
m_recv_window.data_size += payload.size();
epoll_notify(EPOLLIN);
dprintln_if(DEBUG_TCP, "Received {} bytes", payload.size());
if (m_next_flags == 0)
@ -726,6 +734,8 @@ namespace Kernel
m_send_window.current_seq += to_send;
i += to_send;
epoll_notify(EPOLLOUT);
}
m_send_window.last_send_ms = current_ms;

View File

@ -2,6 +2,8 @@
#include <kernel/Networking/UDPSocket.h>
#include <kernel/Thread.h>
#include <sys/epoll.h>
namespace Kernel
{
@ -70,6 +72,8 @@ namespace Kernel
m_packets.emplace(packet_info);
m_packet_total_size += payload.size();
epoll_notify(EPOLLIN);
m_packet_thread_blocker.unblock();
}

View File

@ -5,6 +5,7 @@
#include <kernel/Scheduler.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/un.h>
namespace Kernel
@ -62,6 +63,7 @@ namespace Kernel
if (auto connection = connection_info.connection.lock(); connection && connection->m_info.has<ConnectionInfo>())
{
connection->m_info.get<ConnectionInfo>().target_closed = true;
connection->epoll_notify(EPOLLHUP);
connection->m_packet_thread_blocker.unblock();
}
}
@ -172,6 +174,8 @@ namespace Kernel
TRY(Thread::current().block_or_eintr_indefinite(target_info.pending_thread_blocker));
}
target->epoll_notify(EPOLLIN);
while (!connection_info.connection_done)
Processor::yield();
@ -263,6 +267,8 @@ namespace Kernel
if (!is_streaming())
m_packet_sizes.push(packet.size());
epoll_notify(EPOLLIN);
m_packet_thread_blocker.unblock();
m_packet_lock.unlock(state);
return {};
@ -295,6 +301,17 @@ namespace Kernel
return true;
}
bool UnixDomainSocket::has_hangup_impl() const
{
if (m_info.has<ConnectionInfo>())
{
auto& connection_info = m_info.get<ConnectionInfo>();
return connection_info.target_closed;
}
return false;
}
BAN::ErrorOr<size_t> UnixDomainSocket::sendto_impl(BAN::ConstByteSpan message, const sockaddr* address, socklen_t address_len)
{
if (message.size() > s_packet_buffer_size)
@ -390,6 +407,8 @@ namespace Kernel
m_packet_thread_blocker.unblock();
m_packet_lock.unlock(state);
epoll_notify(EPOLLOUT);
return nread;
}

View File

@ -323,8 +323,6 @@ namespace Kernel
LockGuard _(m_mutex);
TRY(validate_fd(fd));
auto& open_file = m_open_files[fd];
if (open_file.inode()->mode().ifsock())
return recvfrom(fd, buffer, nullptr, nullptr);
if (!(open_file.status_flags() & O_RDONLY))
return BAN::Error::from_errno(EBADF);
inode = open_file.inode();

View File

@ -2,6 +2,7 @@
#include <BAN/StringView.h>
#include <kernel/ACPI/ACPI.h>
#include <kernel/ELF.h>
#include <kernel/Epoll.h>
#include <kernel/FS/DevFS/FileSystem.h>
#include <kernel/FS/ProcFS/FileSystem.h>
#include <kernel/FS/VirtualFileSystem.h>
@ -572,6 +573,11 @@ namespace Kernel
for (size_t i = 0; i < m_cmdline.size(); i++)
TRY(cmdline[i].append(m_cmdline[i]));
BAN::Vector<BAN::String> environ;
TRY(environ.resize(m_environ.size()));
for (size_t i = 0; i < m_environ.size(); i++)
TRY(environ[i].append(m_environ[i]));
auto open_file_descriptors = TRY(BAN::UniqPtr<OpenFileDescriptorSet>::create(m_credentials));
TRY(open_file_descriptors->clone_from(m_open_file_descriptors));
@ -583,7 +589,8 @@ namespace Kernel
Process* forked = create_process(m_credentials, m_pid, m_sid, m_pgrp);
forked->m_controlling_terminal = m_controlling_terminal;
forked->m_working_directory = BAN::move(working_directory);
forked->m_cmdline = BAN::move(m_cmdline);
forked->m_cmdline = BAN::move(cmdline);
forked->m_environ = BAN::move(environ);
forked->m_page_table = BAN::move(page_table);
forked->m_open_file_descriptors = BAN::move(*open_file_descriptors);
forked->m_mapped_regions = BAN::move(mapped_regions);
@ -1446,21 +1453,19 @@ namespace Kernel
return TRY(inode->ioctl(request, arg));
}
BAN::ErrorOr<long> Process::sys_pselect(sys_pselect_t* _arguments)
BAN::ErrorOr<long> Process::sys_pselect(sys_pselect_t* user_arguments)
{
sys_pselect_t arguments;
{
LockGuard _(m_process_lock);
TRY(validate_pointer_access(_arguments, sizeof(sys_pselect_t), false));
arguments = *_arguments;
TRY(validate_pointer_access(user_arguments, sizeof(sys_pselect_t), false));
arguments = *user_arguments;
}
MemoryRegion* readfd_region = nullptr;
MemoryRegion* writefd_region = nullptr;
MemoryRegion* errorfd_region = nullptr;
MemoryRegion* timeout_region = nullptr;
MemoryRegion* sigmask_region = nullptr;
BAN::ScopeGuard _([&] {
if (readfd_region)
@ -1469,75 +1474,57 @@ namespace Kernel
writefd_region->unpin();
if (errorfd_region)
errorfd_region->unpin();
if (timeout_region)
timeout_region->unpin();
if (sigmask_region)
sigmask_region->unpin();
});
readfd_region = TRY(validate_and_pin_pointer_access(arguments.readfds, sizeof(fd_set), true));
writefd_region = TRY(validate_and_pin_pointer_access(arguments.writefds, sizeof(fd_set), true));
errorfd_region = TRY(validate_and_pin_pointer_access(arguments.errorfds, sizeof(fd_set), true));
timeout_region = TRY(validate_and_pin_pointer_access(arguments.timeout, sizeof(timespec), false));
sigmask_region = TRY(validate_and_pin_pointer_access(arguments.sigmask, sizeof(sigset_t), false));
const auto old_sigmask = Thread::current().m_signal_block_mask;
if (arguments.sigmask)
{
LockGuard _(m_process_lock);
TRY(validate_pointer_access(arguments.sigmask, sizeof(sigset_t), false));
Thread::current().m_signal_block_mask = *arguments.sigmask;
}
BAN::ScopeGuard sigmask_restore([old_sigmask] { Thread::current().m_signal_block_mask = old_sigmask; });
uint64_t timedout_ns = SystemTimer::get().ns_since_boot();
uint64_t waketime_ns = BAN::numeric_limits<uint64_t>::max();
if (arguments.timeout)
{
timedout_ns += arguments.timeout->tv_sec * 1'000'000'000;
timedout_ns += arguments.timeout->tv_nsec;
LockGuard _(m_process_lock);
TRY(validate_pointer_access(arguments.timeout, sizeof(timespec), false));
waketime_ns =
SystemTimer::get().ns_since_boot() +
(arguments.timeout->tv_sec * 1'000'000'000) +
arguments.timeout->tv_nsec;
}
fd_set readfds; FD_ZERO(&readfds);
fd_set writefds; FD_ZERO(&writefds);
fd_set errorfds; FD_ZERO(&errorfds);
int set_bits = 0;
for (;;)
auto epoll = TRY(Epoll::create());
for (int fd = 0; fd < user_arguments->nfds; fd++)
{
auto update_fds =
[&](int fd, fd_set* source, fd_set* dest, bool (Inode::*func)() const)
{
if (source == nullptr)
return;
uint32_t events = 0;
if (arguments.readfds && FD_ISSET(fd, arguments.readfds))
events |= EPOLLIN;
if (arguments.writefds && FD_ISSET(fd, arguments.writefds))
events |= EPOLLOUT;
if (arguments.errorfds && FD_ISSET(fd, arguments.errorfds))
events |= EPOLLERR;
if (events == 0)
continue;
if (!FD_ISSET(fd, source))
return;
auto inode_or_error = m_open_file_descriptors.inode_of(fd);
if (inode_or_error.is_error())
continue;
auto inode_or_error = m_open_file_descriptors.inode_of(fd);
if (inode_or_error.is_error())
return;
auto inode = inode_or_error.release_value();
if ((inode.ptr()->*func)())
{
FD_SET(fd, dest);
set_bits++;
}
};
for (int i = 0; i < arguments.nfds; i++)
{
update_fds(i, arguments.readfds, &readfds, &Inode::can_read);
update_fds(i, arguments.writefds, &writefds, &Inode::can_write);
update_fds(i, arguments.errorfds, &errorfds, &Inode::has_error);
}
if (set_bits > 0)
break;
if (arguments.timeout && SystemTimer::get().ns_since_boot() >= timedout_ns)
break;
// FIXME: implement some multi thread blocker system?
TRY(Thread::current().sleep_or_eintr_ms(1));
TRY(epoll->ctl(EPOLL_CTL_ADD, inode_or_error.release_value(), { .events = events, .data = { .fd = fd }}));
}
BAN::Vector<epoll_event> event_buffer;
TRY(event_buffer.resize(user_arguments->nfds));
const size_t waited_events = TRY(epoll->wait(event_buffer.span(), waketime_ns));
if (arguments.readfds)
FD_ZERO(arguments.readfds);
if (arguments.writefds)
@ -1545,17 +1532,98 @@ namespace Kernel
if (arguments.errorfds)
FD_ZERO(arguments.errorfds);
for (int i = 0; i < arguments.nfds; i++)
for (size_t i = 0; i < waited_events; i++)
{
if (arguments.readfds && FD_ISSET(i, &readfds))
FD_SET(i, arguments.readfds);
if (arguments.writefds && FD_ISSET(i, &writefds))
FD_SET(i, arguments.writefds);
if (arguments.errorfds && FD_ISSET(i, &errorfds))
FD_SET(i, arguments.errorfds);
const int fd = event_buffer[i].data.fd;
if (arguments.readfds && event_buffer[i].events & (EPOLLIN | EPOLLHUP))
FD_SET(fd, arguments.readfds);
if (arguments.writefds && event_buffer[i].events & (EPOLLOUT))
FD_SET(fd, arguments.writefds);
if (arguments.errorfds && event_buffer[i].events & (EPOLLERR))
FD_SET(fd, arguments.errorfds);
}
return set_bits;
return waited_events;
}
BAN::ErrorOr<long> Process::sys_epoll_create1(int flags)
{
if (flags && (flags & ~EPOLL_CLOEXEC))
return BAN::Error::from_errno(EINVAL);
if (flags & EPOLL_CLOEXEC)
flags = O_CLOEXEC;
VirtualFileSystem::File epoll_file;
epoll_file.inode = TRY(Epoll::create());
TRY(epoll_file.canonical_path.append("<epoll>"_sv));
return TRY(m_open_file_descriptors.open(BAN::move(epoll_file), flags | O_RDWR));
}
BAN::ErrorOr<long> Process::sys_epoll_ctl(int epfd, int op, int fd, epoll_event* user_event)
{
if (epfd == fd)
return BAN::Error::from_errno(EINVAL);
if (op != EPOLL_CTL_DEL && user_event == nullptr)
return BAN::Error::from_errno(EINVAL);
auto epoll_inode = TRY(m_open_file_descriptors.inode_of(epfd));
if (!epoll_inode->is_epoll())
return BAN::Error::from_errno(EINVAL);
auto inode = TRY(m_open_file_descriptors.inode_of(fd));
epoll_event event {};
if (user_event)
{
LockGuard _(m_process_lock);
TRY(validate_pointer_access(user_event, sizeof(epoll_event), false));
event = *user_event;
}
TRY(static_cast<Epoll*>(epoll_inode.ptr())->ctl(op, inode, event));
return 0;
}
BAN::ErrorOr<long> Process::sys_epoll_pwait2(int epfd, epoll_event* events, int maxevents, const timespec* timeout, const sigset_t* sigmask)
{
(void)sigmask;
if (maxevents <= 0)
return BAN::Error::from_errno(EINVAL);
auto epoll_inode = TRY(m_open_file_descriptors.inode_of(epfd));
if (!epoll_inode->is_epoll())
return BAN::Error::from_errno(EINVAL);
uint64_t waketime_ns = BAN::numeric_limits<uint64_t>::max();
if (timeout)
{
LockGuard _(m_process_lock);
TRY(validate_pointer_access(timeout, sizeof(timespec), false));
waketime_ns =
SystemTimer::get().ns_since_boot() +
(timeout->tv_sec * 1'000'000'000) +
timeout->tv_nsec;
}
auto* events_region = TRY(validate_and_pin_pointer_access(events, maxevents * sizeof(epoll_event), true));
BAN::ScopeGuard _([events_region] {
if (events_region)
events_region->unpin();
});
const auto old_sigmask = Thread::current().m_signal_block_mask;
if (sigmask)
{
LockGuard _(m_process_lock);
TRY(validate_pointer_access(sigmask, sizeof(sigset_t), false));
Thread::current().m_signal_block_mask = *sigmask;
}
BAN::ScopeGuard sigmask_restore([old_sigmask] { Thread::current().m_signal_block_mask = old_sigmask; });
return TRY(static_cast<Epoll*>(epoll_inode.ptr())->wait(BAN::Span<epoll_event>(events, maxevents), waketime_ns));
}
BAN::ErrorOr<long> Process::sys_pipe(int fildes[2])

View File

@ -352,7 +352,7 @@ namespace Kernel
if (s_should_print_cpu_load && g_terminal_driver)
{
const uint64_t duration_ns = current_ns - processor_info.m_last_update_ns;
const uint64_t load_x1000 = 100'000 * (duration_ns - processor_info.m_idle_ns) / duration_ns;
const uint64_t load_x1000 = 100'000 * (duration_ns - BAN::Math::min(processor_info.m_idle_ns, duration_ns)) / duration_ns;
uint32_t x = g_terminal_driver->width() - 16;
uint32_t y = current_id().as_u32();

View File

@ -4,6 +4,7 @@
#include <BAN/ScopeGuard.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <sys/sysmacros.h>
@ -95,6 +96,8 @@ namespace Kernel
reinterpret_cast<uint8_t*>(m_buffer->vaddr())[(m_buffer_tail + m_buffer_size) % m_buffer->size()] = ch;
m_buffer_size++;
epoll_notify(EPOLLIN);
m_buffer_blocker.unblock();
return true;
@ -127,6 +130,8 @@ namespace Kernel
m_buffer_size -= to_copy;
m_buffer_tail = (m_buffer_tail + to_copy) % m_buffer->size();
epoll_notify(EPOLLOUT);
m_buffer_lock.unlock(state);
return to_copy;
@ -137,7 +142,6 @@ namespace Kernel
auto slave = m_slave.lock();
if (!slave)
return BAN::Error::from_errno(ENODEV);
for (size_t i = 0; i < buffer.size(); i++)
slave->handle_input_byte(buffer[i]);
return buffer.size();

View File

@ -15,6 +15,7 @@
#include <string.h>
#include <stropts.h>
#include <sys/banan-os.h>
#include <sys/epoll.h>
#include <sys/sysmacros.h>
namespace Kernel
@ -40,6 +41,7 @@ namespace Kernel
bool can_read_impl() const override { return false; }
bool can_write_impl() const override { return false; }
bool has_error_impl() const override { return false; }
bool has_hangup_impl() const override { return false; }
private:
DevTTY(mode_t mode, uid_t uid, gid_t gid)
@ -238,6 +240,7 @@ namespace Kernel
if (ch == '\x04' && (m_termios.c_lflag & ICANON))
{
m_output.flush = true;
epoll_notify(EPOLLIN);
m_output.thread_blocker.unblock();
return;
}
@ -280,6 +283,7 @@ namespace Kernel
if (ch == '\n' || !(m_termios.c_lflag & ICANON))
{
m_output.flush = true;
epoll_notify(EPOLLIN);
m_output.thread_blocker.unblock();
}
}

View File

@ -571,17 +571,14 @@ namespace Kernel
void VirtualTTY::update_cursor()
{
static bool last_shown = !m_cursor_shown;
if (m_cursor_shown != last_shown)
if (m_cursor_shown != m_last_cursor_shown)
m_terminal_driver->set_cursor_shown(m_cursor_shown);
last_shown = m_cursor_shown;
m_last_cursor_shown = m_cursor_shown;
static uint32_t last_column = -1;
static uint32_t last_row = -1;
if (last_column != m_column || last_row != m_row)
if (m_last_cursor_column != m_column || m_last_cursor_row != m_row)
m_terminal_driver->set_cursor_position(m_column, m_row);
last_column = m_column;
last_row = m_row;
m_last_cursor_column = m_column;
m_last_cursor_row = m_row;
}
}

View File

@ -5,7 +5,7 @@ VERSION='git'
DOWNLOAD_URL="https://github.com/ozkl/doomgeneric.git#613f870b6fa83ede448a247de5a2571092fa729d"
configure() {
:
make --directory doomgeneric clean
}
build() {

View File

@ -79,7 +79,7 @@ new file mode 100644
index 0000000..9161771
--- /dev/null
+++ b/doomgeneric/doomgeneric_banan_os.cpp
@@ -0,0 +1,138 @@
@@ -0,0 +1,139 @@
+extern "C"
+{
+#include "doomgeneric.h"
@ -166,9 +166,10 @@ index 0000000..9161771
+
+void DG_DrawFrame()
+{
+ auto& texture = s_window->texture();
+ for (size_t y = 0; y < DOOMGENERIC_RESY; y++)
+ for (size_t x = 0; x < DOOMGENERIC_RESX; x++)
+ s_window->set_pixel(x, y, 0xFF000000 | DG_ScreenBuffer[y * DOOMGENERIC_RESX + x]);
+ texture.set_pixel(x, y, 0xFF000000 | DG_ScreenBuffer[y * DOOMGENERIC_RESX + x]);
+ s_window->invalidate();
+ s_window->poll_events();
+}

View File

@ -25,20 +25,21 @@ diff --git a/doomgeneric/doomgeneric_banan_os.cpp b/doomgeneric/doomgeneric_bana
index d00c30d..9d13b43 100644
--- a/doomgeneric/doomgeneric_banan_os.cpp
+++ b/doomgeneric/doomgeneric_banan_os.cpp
@@ -80,13 +80,13 @@ void DG_Init()
@@ -80,14 +80,13 @@ void DG_Init()
s_key_write_index = (s_key_write_index + 1) % s_key_queue_size;
}
);
+
+ ASSERT(DG_ScreenBuffer == nullptr);
+ DG_ScreenBuffer = s_window->pixels().data();
+ DG_ScreenBuffer = s_window->texture().pixels().data();
}
void DG_DrawFrame()
{
- auto& texture = s_window->texture();
- for (size_t y = 0; y < DOOMGENERIC_RESY; y++)
- for (size_t x = 0; x < DOOMGENERIC_RESX; x++)
- s_window->set_pixel(x, y, 0xFF000000 | DG_ScreenBuffer[y * DOOMGENERIC_RESX + x]);
- texture.set_pixel(x, y, 0xFF000000 | DG_ScreenBuffer[y * DOOMGENERIC_RESX + x]);
s_window->invalidate();
s_window->poll_events();
}

View File

@ -198,7 +198,7 @@ new file mode 100644
index 0000000..c7d7e16
--- /dev/null
+++ b/port_soft_banan_os.cpp
@@ -0,0 +1,277 @@
@@ -0,0 +1,278 @@
+#include <LibGUI/Window.h>
+#include <LibInput/KeyEvent.h>
+#include <LibInput/MouseEvent.h>
@ -397,11 +397,12 @@ index 0000000..c7d7e16
+
+void SWimp_EndFrame(void)
+{
+ auto& texture = s_window->texture();
+ const uint32_t width = s_window->width();
+ const uint32_t height = s_window->height();
+ for (int y = 0; y < height; y++)
+ for (int x = 0; x < width; x++)
+ s_window->set_pixel(x, y, s_palette[s_buffer[y * width + x]].as_argb());
+ texture.set_pixel(x, y, s_palette[s_buffer[y * width + x]].as_argb());
+ s_window->invalidate();
+}
+

View File

@ -4,11 +4,16 @@ NAME='tcc'
VERSION='0.9.27'
DOWNLOAD_URL="https://download.savannah.gnu.org/releases/tinycc/tcc-$VERSION.tar.bz2#de23af78fca90ce32dff2dd45b3432b2334740bb9bb7b05bf60fdbfc396ceb9c"
tcc_arch=$BANAN_ARCH
if [ $tcc_arch = 'i686' ]; then
tcc_arch='i386'
fi
configure() {
./configure \
--prefix=/usr \
--sysroot=$BANAN_SYSROOT \
--cpu=$BANAN_ARCH \
--cpu=$tcc_arch \
--enable-cross \
--cross-prefix=$BANAN_TOOLCHAIN_TRIPLE- \
--sysincludepaths=/usr/include:/usr/lib/tcc/include \
@ -19,10 +24,10 @@ configure() {
build() {
touch $BANAN_SYSROOT/usr/include/sys/ucontext.h
make -j$(nproc) cross-$BANAN_ARCH $BANAN_ARCH-libtcc1-usegcc=yes || exit 1
make -j$(nproc) cross-$tcc_arch $tcc_arch-libtcc1-usegcc=yes || exit 1
}
install() {
make install-unx DESTDIR=$BANAN_SYSROOT || exit 1
ln -sf $BANAN_ARCH-tcc $BANAN_SYSROOT/usr/bin/tcc
ln -sf $tcc_arch-tcc $BANAN_SYSROOT/usr/bin/tcc
}

View File

@ -1,6 +1,6 @@
diff -ruN tcc-0.9.27/configure tcc-0.9.27-banan_os/configure
--- tcc-0.9.27/configure 2017-12-17 10:27:05.000000000 +0200
+++ tcc-0.9.27-banan_os/configure 2024-08-25 18:43:00.329466309 +0300
+++ tcc-0.9.27-banan_os/configure 2025-05-07 18:10:09.779137536 +0300
@@ -49,8 +49,11 @@
gcc_minor=0
@ -16,7 +16,7 @@ diff -ruN tcc-0.9.27/configure tcc-0.9.27-banan_os/configure
DLLSUF=".dylib"
diff -ruN tcc-0.9.27/Makefile tcc-0.9.27-banan_os/Makefile
--- tcc-0.9.27/Makefile 2017-12-17 10:27:05.000000000 +0200
+++ tcc-0.9.27-banan_os/Makefile 2024-08-25 18:43:44.996196450 +0300
+++ tcc-0.9.27-banan_os/Makefile 2025-05-07 18:10:09.779389036 +0300
@@ -30,7 +30,9 @@
CFGWIN = -win
NATIVE_TARGET = $(ARCH)-win$(if $(findstring arm,$(ARCH)),ce,32)
@ -28,3 +28,15 @@ diff -ruN tcc-0.9.27/Makefile tcc-0.9.27-banan_os/Makefile
ifneq ($(CONFIG_ldl),no)
LIBS+=-ldl
endif
diff -ruN tcc-0.9.27/tcc.c tcc-0.9.27-banan_os/tcc.c
--- tcc-0.9.27/tcc.c 2017-12-17 10:27:05.000000000 +0200
+++ tcc-0.9.27-banan_os/tcc.c 2025-05-07 18:09:16.870611889 +0300
@@ -162,6 +162,8 @@
" Darwin"
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
" FreeBSD"
+#elif defined(__banan_os__)
+ " banan-os"
#else
" Linux"
#endif

View File

@ -49,7 +49,7 @@ new file mode 100644
index 0000000..94f249e
--- /dev/null
+++ b/src/platform/banan-os/main.cpp
@@ -0,0 +1,362 @@
@@ -0,0 +1,364 @@
+
+/* tinygb - a tiny gameboy emulator
+ (c) 2022 by jewel */
@ -203,12 +203,13 @@ index 0000000..94f249e
+
+void update_window(uint32_t *framebuffer)
+{
+ auto pixels = s_window->texture().pixels();
+ for (int i = 0; i < scaled_h; i++)
+ {
+ uint32_t* src = &framebuffer[i * scaled_w];
+ uint32_t* dst = using_sgb_border
+ ? &s_window->pixels()[(i + gb_y) * s_window->width() + gb_x]
+ : &s_window->pixels()[i * s_window->width()];
+ ? &pixels[(i + gb_y) * s_window->width() + gb_x]
+ : &pixels[i * s_window->width()];
+ memcpy(dst, src, scaled_w * 4);
+ }
+
@ -222,10 +223,11 @@ index 0000000..94f249e
+
+void update_border(uint32_t *framebuffer)
+{
+ auto pixels = s_window->texture().pixels();
+ for (int i = 0; i < sgb_scaled_h; i++)
+ {
+ uint32_t* src = &framebuffer[i * sgb_scaled_w];
+ uint32_t* dst = &s_window->pixels()[i * s_window->width()];
+ uint32_t* dst = &pixels[i * s_window->width()];
+ memcpy(dst, src, sgb_scaled_w*4);
+ }
+}

View File

@ -30,6 +30,7 @@ set(LIBC_SOURCES
string.cpp
strings.cpp
sys/banan-os.cpp
sys/epoll.cpp
sys/ioctl.cpp
sys/mman.cpp
sys/resource.cpp

View File

@ -0,0 +1,49 @@
#ifndef _SYS_EPOLL_H
#define _SYS_EPOLL_H 1
#include <sys/cdefs.h>
__BEGIN_DECLS
#include <stdint.h>
#include <signal.h>
union epoll_data
{
void* ptr;
int fd;
uint32_t u32;
uint64_t u64;
};
typedef union epoll_data epoll_data_t;
struct epoll_event
{
uint32_t events;
epoll_data_t data;
};
#define EPOLL_CTL_ADD 0
#define EPOLL_CTL_MOD 1
#define EPOLL_CTL_DEL 2
#define EPOLLIN 0x01
#define EPOLLOUT 0x02
#define EPOLLPRI 0x04
#define EPOLLERR 0x08
#define EPOLLHUP 0x10
#define EPOLLET 0x20
#define EPOLLONESHOT 0x40
#define EPOLL_CLOEXEC 1
int epoll_create(int size);
int epoll_create1(int flags);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);
int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);
int epoll_pwait(int epfd, struct epoll_event* events, int maxevents, int timeout, const sigset_t* sigmask);
int epoll_pwait2(int epfd, struct epoll_event* events, int maxevents, const struct timespec* timeout, const sigset_t* sigmask);
__END_DECLS
#endif

View File

@ -98,6 +98,9 @@ __BEGIN_DECLS
O(SYS_PTHREAD_EXIT, pthread_exit) \
O(SYS_PTHREAD_JOIN, pthread_join) \
O(SYS_PTHREAD_SELF, pthread_self) \
O(SYS_EPOLL_CREATE1, epoll_create1) \
O(SYS_EPOLL_CTL, epoll_ctl) \
O(SYS_EPOLL_PWAIT2, epoll_pwait2) \
enum Syscall
{

View File

@ -0,0 +1,49 @@
#include <errno.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/syscall.h>
int epoll_create(int size)
{
if (size <= 0)
{
errno = EINVAL;
return -1;
}
return epoll_create1(0);
}
int epoll_create1(int flags)
{
return syscall(SYS_EPOLL_CREATE1, flags);
}
int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event)
{
return syscall(SYS_EPOLL_CTL, epfd, op, fd, event);
}
int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout)
{
return epoll_pwait(epfd, events, maxevents, timeout, nullptr);
}
int epoll_pwait(int epfd, struct epoll_event* events, int maxevents, int timeout, const sigset_t* sigmask)
{
timespec ts;
timespec* ts_ptr = nullptr;
if (timeout >= 0)
{
ts.tv_sec = static_cast<time_t>(timeout / 1000),
ts.tv_nsec = (timeout % 1000) * 1'000'000,
ts_ptr = &ts;
}
return epoll_pwait2(epfd, events, maxevents, ts_ptr, sigmask);
}
int epoll_pwait2(int epfd, struct epoll_event* events, int maxevents, const struct timespec* timeout, const sigset_t* sigmask)
{
return syscall(SYS_EPOLL_PWAIT2, epfd, events, maxevents, timeout, sigmask);
}

View File

@ -4,8 +4,8 @@ set(LIBGUI_SOURCES
)
add_library(libfont ${LIBGUI_SOURCES})
banan_link_library(libfont libc)
banan_link_library(libfont ban)
banan_link_library(libfont libc)
banan_install_headers(libfont)
install(TARGETS libfont OPTIONAL)

View File

@ -4,10 +4,10 @@ set(LIBGUI_SOURCES
)
add_library(libgui ${LIBGUI_SOURCES})
banan_include_headers(libgui ban)
banan_include_headers(libgui libfont)
banan_include_headers(libgui libinput)
banan_link_library(libgui ban)
banan_link_library(libgui libc)
banan_link_library(libgui libfont)
banan_link_library(libgui libinput)
banan_install_headers(libgui)
install(TARGETS libgui OPTIONAL)

View File

@ -0,0 +1,185 @@
#include <LibGUI/Texture.h>
#include <LibFont/Font.h>
namespace LibGUI
{
BAN::ErrorOr<Texture> Texture::create(uint32_t width, uint32_t height, uint32_t color)
{
if (BAN::Math::will_addition_overflow(width, height))
return BAN::Error::from_errno(EOVERFLOW);
BAN::Vector<uint32_t> pixels;
TRY(pixels.resize(width * height, color));
return Texture(BAN::move(pixels), width, height, color);
}
BAN::ErrorOr<void> Texture::resize(uint32_t new_width, uint32_t new_height)
{
if (BAN::Math::will_addition_overflow(new_width, new_height))
return BAN::Error::from_errno(EOVERFLOW);
BAN::Vector<uint32_t> pixels;
TRY(pixels.resize(new_width * new_height, m_bg_color));
const uint32_t max_x = BAN::Math::min(new_width, m_width);
const uint32_t max_y = BAN::Math::min(new_height, m_height);
for (uint32_t y = 0; y < max_y; y++)
for (uint32_t x = 0; x < max_x; x++)
pixels[y * new_width + x] = m_pixels[y * m_width + x];
m_width = new_width;
m_height = new_height;
m_pixels = BAN::move(pixels);
return {};
}
void Texture::fill_rect(int32_t x, int32_t y, uint32_t width, uint32_t height, uint32_t color)
{
if (!clamp_to_texture(x, y, width, height))
return;
for (uint32_t y_off = 0; y_off < height; y_off++)
for (uint32_t x_off = 0; x_off < width; x_off++)
set_pixel(x + x_off, y + y_off, color);
}
void Texture::copy_texture(const Texture& texture, int32_t x, int32_t y)
{
uint32_t width = texture.width();
uint32_t height = texture.height();
if (!clamp_to_texture(x, y, width, height))
return;
for (uint32_t y_off = 0; y_off < height; y_off++)
for (uint32_t x_off = 0; x_off < width; x_off++)
set_pixel(x + x_off, y + y_off, texture.get_pixel(x_off, y_off));
}
void Texture::draw_character(uint32_t codepoint, const LibFont::Font& font, int32_t tl_x, int32_t tl_y, uint32_t color)
{
if (tl_y + (int32_t)font.height() < 0 || tl_y >= (int32_t)height())
return;
if (tl_x + (int32_t)font.width() < 0 || tl_x >= (int32_t)width())
return;
auto glyph = font.glyph(codepoint);
if (glyph == nullptr)
return;
for (int32_t off_y = 0; off_y < (int32_t)font.height(); off_y++)
{
if (tl_y + off_y < 0)
continue;
uint32_t abs_y = tl_y + off_y;
if (abs_y >= height())
break;
for (int32_t off_x = 0; off_x < (int32_t)font.width(); off_x++)
{
if (tl_x + off_x < 0)
continue;
uint32_t abs_x = tl_x + off_x;
if (abs_x >= width())
break;
const uint8_t bitmask = 1 << (font.width() - off_x - 1);
if (glyph[off_y * font.pitch()] & bitmask)
set_pixel(abs_x, abs_y, color);
}
}
}
void Texture::draw_text(BAN::StringView text, const LibFont::Font& font, int32_t tl_x, int32_t tl_y, uint32_t color)
{
for (size_t i = 0; i < text.size(); i++)
draw_character(text[i], font, tl_x + (int32_t)(i * font.width()), tl_y, color);
}
void Texture::shift_vertical(int32_t amount, uint32_t fill_color)
{
const uint32_t amount_abs = BAN::Math::abs(amount);
if (amount_abs == 0 || amount_abs >= height())
return;
uint32_t* dst = (amount > 0) ? m_pixels.data() + width() * amount_abs : m_pixels.data();
uint32_t* src = (amount < 0) ? m_pixels.data() + width() * amount_abs : m_pixels.data();
memmove(dst, src, width() * (height() - amount_abs) * 4);
const uint32_t y_lo = (amount < 0) ? height() - amount_abs : 0;
const uint32_t y_hi = (amount < 0) ? height() : amount_abs;
for (uint32_t y = y_lo; y < y_hi; y++)
for (uint32_t x = 0; x < width(); x++)
set_pixel(x, y, fill_color);
}
void Texture::copy_horizontal_slice(int32_t dst_y, int32_t src_y, uint32_t uamount, uint32_t fill_color)
{
int32_t amount = uamount;
if (dst_y < 0)
{
amount -= -dst_y;
src_y += -dst_y;
dst_y = 0;
}
amount = BAN::Math::min<int32_t>(amount, height() - dst_y);
if (amount <= 0)
return;
const int32_t copy_src_y = BAN::Math::clamp<int32_t>(src_y, 0, height());
const int32_t copy_amount = BAN::Math::clamp<int32_t>(src_y + amount, 0, height()) - copy_src_y;
if (copy_amount > 0)
{
memmove(
&m_pixels[width() * (dst_y + (copy_src_y - src_y))],
&m_pixels[width() * copy_src_y],
copy_amount * width() * 4
);
}
const uint32_t fill_y_off = (src_y < copy_src_y) ? 0 : copy_amount;
const uint32_t fill_amount = amount - copy_amount;
for (uint32_t i = 0; i < fill_amount; i++)
for (uint32_t x = 0; x < width(); x++)
set_pixel(x, dst_y + fill_y_off + i, fill_color);
}
void Texture::copy_rect(int32_t dst_x, int32_t dst_y, int32_t src_x, int32_t src_y, uint32_t width, uint32_t height, uint32_t fill_color)
{
fill_rect(dst_x, dst_y, width, height, fill_color);
if (!clamp_to_texture(dst_x, dst_y, width, height))
return;
if (!clamp_to_texture(src_x, src_y, width, height))
return;
const bool copy_dir = dst_y < src_y;
for (uint32_t i = 0; i < height; i++)
{
const uint32_t y_off = copy_dir ? i : height - i - 1;
memmove(
&m_pixels[(dst_y + y_off) * this->width()],
&m_pixels[(src_y + y_off) * this->width()],
width * 4
);
}
}
bool Texture::clamp_to_texture(int32_t& signed_x, int32_t& signed_y, uint32_t& width, uint32_t& height) const
{
const int32_t min_x = BAN::Math::max<int32_t>(signed_x, 0);
const int32_t min_y = BAN::Math::max<int32_t>(signed_y, 0);
const int32_t max_x = BAN::Math::min<int32_t>(this->width(), signed_x + (int32_t)width);
const int32_t max_y = BAN::Math::min<int32_t>(this->height(), signed_y + (int32_t)height);
if (min_x >= max_x)
return false;
if (min_y >= max_y)
return false;
signed_x = min_x;
signed_y = min_y;
width = max_x - min_x;
height = max_y - min_y;
return true;
}
}

View File

@ -0,0 +1,81 @@
#pragma once
#include <BAN/StringView.h>
#include <stdint.h>
namespace LibFont { class Font; }
namespace LibGUI
{
class Texture
{
public:
static BAN::ErrorOr<Texture> create(uint32_t width, uint32_t height, uint32_t color);
BAN::ErrorOr<void> resize(uint32_t width, uint32_t height);
void set_pixel(uint32_t x, uint32_t y, uint32_t color)
{
ASSERT(x < m_width);
ASSERT(y < m_height);
m_pixels[y * m_width + x] = color;
}
uint32_t get_pixel(uint32_t x, uint32_t y) const
{
ASSERT(x < m_width);
ASSERT(y < m_height);
return m_pixels[y * m_width + x];
}
BAN::Span<uint32_t> pixels() { return m_pixels.span(); }
void fill_rect(int32_t x, int32_t y, uint32_t width, uint32_t height, uint32_t color);
void fill(uint32_t color) { return fill_rect(0, 0, width(), height(), color); }
void copy_texture(const Texture& texture, int32_t x, int32_t y);
void draw_character(uint32_t codepoint, const LibFont::Font& font, int32_t x, int32_t y, uint32_t color);
void draw_text(BAN::StringView text, const LibFont::Font& font, int32_t x, int32_t y, uint32_t color);
// shift whole vertically by amount pixels, sign determines the direction
// fill_color is used to fill "new" data
void shift_vertical(int32_t amount, uint32_t fill_color);
// copy horizontal slice [src_y, src_y + amount[ to [dst_y, dst_y + amount[
// fill_color is used when copying data outside of window bounds
void copy_horizontal_slice(int32_t dst_y, int32_t src_y, uint32_t amount, uint32_t fill_color);
// copy rect (src_x, src_y, width, height) to (dst_x, dst_y, width, height)
// fill_color is used when copying data outside of window bounds
void copy_rect(int32_t dst_x, int32_t dst_y, int32_t src_x, int32_t src_y, uint32_t width, uint32_t height, uint32_t fill_color);
uint32_t width() const { return m_width; }
uint32_t height() const { return m_height; }
// used on resize to fill empty space
void set_bg_color(uint32_t bg_color) { m_bg_color = bg_color; }
private:
Texture() = default;
Texture(BAN::Vector<uint32_t>&& pixels, uint32_t width, uint32_t height, uint32_t color)
: m_pixels(BAN::move(pixels))
, m_width(width)
, m_height(height)
, m_bg_color(color)
{}
bool clamp_to_texture(int32_t& x, int32_t& y, uint32_t& width, uint32_t& height) const;
private:
BAN::Vector<uint32_t> m_pixels;
uint32_t m_width { 0 };
uint32_t m_height { 0 };
uint32_t m_bg_color { 0xFFFFFFFF };
friend class Window;
};
}

View File

@ -5,8 +5,8 @@ set(LIBIMAGE_SOURCES
)
add_library(libimage ${LIBIMAGE_SOURCES})
banan_link_library(libimage libc)
banan_link_library(libimage ban)
banan_link_library(libimage libc)
banan_install_headers(libimage)
install(TARGETS libimage OPTIONAL)

View File

@ -475,16 +475,20 @@ void WindowServer::on_mouse_button(LibInput::MouseButtonEvent event)
return;
}
BAN::RefPtr<Window> target_window;
for (size_t i = m_client_windows.size(); i > 0; i--)
const size_t button_idx = static_cast<size_t>(event.button);
if (button_idx >= m_mouse_button_windows.size())
{
if (m_client_windows[i - 1]->full_area().contains(m_cursor))
{
target_window = m_client_windows[i - 1];
break;
}
dwarnln("invalid mouse button {}", button_idx);
return;
}
BAN::RefPtr<Window> target_window;
if (!event.pressed)
target_window = m_mouse_button_windows[button_idx];
for (size_t i = m_client_windows.size(); i > 0 && !target_window; i--)
if (m_client_windows[i - 1]->full_area().contains(m_cursor))
target_window = m_client_windows[i - 1];
switch (m_state)
{
case State::Normal:
@ -530,7 +534,7 @@ void WindowServer::on_mouse_button(LibInput::MouseButtonEvent event)
[[fallthrough]];
case State::Fullscreen:
if (target_window && target_window->client_area().contains(m_cursor))
if (target_window && (!event.pressed || target_window->client_area().contains(m_cursor)))
{
LibGUI::EventPacket::MouseButtonEvent packet;
packet.event.button = event.button;
@ -542,6 +546,7 @@ void WindowServer::on_mouse_button(LibInput::MouseButtonEvent event)
dwarnln("could not send mouse button event: {}", ret.error());
return;
}
m_mouse_button_windows[button_idx] = event.pressed ? target_window : nullptr;
}
break;
case State::Moving:

View File

@ -96,6 +96,7 @@ private:
State m_state { State::Normal };
bool m_is_mod_key_held { false };
BAN::RefPtr<Window> m_focused_window;
BAN::Array<BAN::RefPtr<Window>, 5> m_mouse_button_windows;
Position m_cursor;
Rectangle m_non_full_screen_rect;

View File

@ -16,12 +16,15 @@ void print_timestamp(timespec ts)
int main(int argc, char** argv)
{
int ret = 0;
for (int i = 1; i < argc; i++)
{
struct stat st;
if (stat(argv[i], &st) == -1)
{
perror("stat");
ret = 1;
continue;
}
@ -72,4 +75,6 @@ int main(int argc, char** argv)
printf("Modify: "); print_timestamp(st.st_mtim); printf("\n");
printf("Change: "); print_timestamp(st.st_ctim); printf("\n");
}
return ret;
}

View File

@ -25,7 +25,7 @@ in_addr_t get_ipv4_address(const char* query)
return -1;
for (addrinfo* ai = result; ai; ai = ai->ai_next)
if (ai->ai_family != AF_INET)
if (ai->ai_family == AF_INET)
return reinterpret_cast<sockaddr_in*>(ai->ai_addr)->sin_addr.s_addr;
return -1;

View File

@ -4,7 +4,6 @@ set(SOURCES
add_executable(test-window ${SOURCES})
banan_include_headers(test-window ban)
banan_include_headers(test-window libinput)
banan_link_library(test-window libc)
banan_link_library(test-window libgui)

View File

@ -55,11 +55,7 @@ int main()
while (running)
{
window->wait_events();
window->poll_events();
timespec duration;
duration.tv_sec = 0;
duration.tv_nsec = 16'666'666;
nanosleep(&duration, nullptr);
}
}