Compare commits
10 Commits
3021fffb07
...
a48d6fe001
Author | SHA1 | Date | |
---|---|---|---|
a48d6fe001 | |||
76be0058da | |||
63a30cc056 | |||
a1c0aab4e0 | |||
7b5d7d4ac6 | |||
fbb238cb5d | |||
967affdffe | |||
60cc9dcd0e | |||
5d992ceb99 | |||
bc20e1a31b |
@ -45,8 +45,10 @@ endif()
|
||||
|
||||
add_subdirectory(libluna)
|
||||
add_subdirectory(libos)
|
||||
add_subdirectory(libui)
|
||||
add_subdirectory(libc)
|
||||
add_subdirectory(kernel)
|
||||
add_subdirectory(apps)
|
||||
add_subdirectory(tests)
|
||||
add_subdirectory(shell)
|
||||
add_subdirectory(wind)
|
||||
|
@ -1,4 +0,0 @@
|
||||
Name=motd
|
||||
Description=Show the message of the day to the user.
|
||||
Command=/usr/bin/cat /etc/motd
|
||||
Wait=true
|
@ -1,6 +0,0 @@
|
||||
Name=listen
|
||||
Description=Start a Unix domain socket test server.
|
||||
Command=/usr/bin/socket-test
|
||||
StandardOutput=/dev/uart0
|
||||
StandardError=/dev/uart0
|
||||
Restart=true
|
@ -1,4 +1,6 @@
|
||||
Name=login
|
||||
Description=Start the command-line login program.
|
||||
Command=/usr/bin/login
|
||||
Description=Start the display server.
|
||||
Command=/usr/bin/wind --user=selene
|
||||
StandardOutput=/dev/uart0
|
||||
StandardError=/dev/uart0
|
||||
Restart=true
|
||||
|
@ -43,8 +43,8 @@ static void process_mouse_event(u8 data)
|
||||
packet.buttons = 0;
|
||||
|
||||
u8 flags = g_mouse_packet[0];
|
||||
if (flags & PS2_MOUSE_X_SIGN) packet.xdelta = -packet.xdelta;
|
||||
if (flags & PS2_MOUSE_Y_SIGN) packet.ydelta = -packet.ydelta;
|
||||
if (flags & PS2_MOUSE_X_SIGN) packet.xdelta = -(256 - packet.xdelta);
|
||||
if (flags & PS2_MOUSE_Y_SIGN) packet.ydelta = -(256 - packet.ydelta);
|
||||
|
||||
if (flags & PS2_MOUSE_MIDDLE_BTN) packet.buttons |= moon::MouseButton::Middle;
|
||||
if (flags & PS2_MOUSE_RIGHT_BTN) packet.buttons |= moon::MouseButton::Right;
|
||||
|
@ -65,6 +65,10 @@ class Socket : public VFS::FileInode
|
||||
m_metadata.nlinks--;
|
||||
}
|
||||
|
||||
virtual bool can_accept_connections() const = 0;
|
||||
|
||||
virtual bool can_read_data() const = 0;
|
||||
|
||||
virtual ~Socket() = default;
|
||||
|
||||
protected:
|
||||
|
@ -17,6 +17,16 @@ class UnixSocket : public Socket
|
||||
return (m_state == Connected || m_state == Reset) && !m_data.size();
|
||||
}
|
||||
|
||||
bool can_read_data() const override
|
||||
{
|
||||
return (m_state == Connected || m_state == Reset) && m_data.size();
|
||||
}
|
||||
|
||||
bool can_accept_connections() const override
|
||||
{
|
||||
return !m_listen_queue.is_empty();
|
||||
}
|
||||
|
||||
Result<usize> send(const u8*, usize, int) override;
|
||||
Result<usize> recv(u8*, usize, int) const override;
|
||||
|
||||
|
@ -100,7 +100,7 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
|
||||
{
|
||||
auto& descriptor = current->fd_table[i];
|
||||
if (!descriptor.has_value()) continue;
|
||||
if (descriptor->description->flags & O_CLOEXEC) { descriptor = {}; }
|
||||
if (descriptor->flags & O_CLOEXEC) { descriptor = {}; }
|
||||
}
|
||||
|
||||
if (VFS::is_setuid(inode)) current->auth.euid = current->auth.suid = inode->metadata().uid;
|
||||
|
@ -124,28 +124,28 @@ Result<u64> sys_fcntl(Registers*, SyscallArgs args)
|
||||
|
||||
current->fd_table[new_fd] = descriptor;
|
||||
|
||||
if (is_cloexec) current->fd_table[new_fd]->flags() |= O_CLOEXEC;
|
||||
if (is_cloexec) current->fd_table[new_fd]->flags |= O_CLOEXEC;
|
||||
else
|
||||
current->fd_table[new_fd]->flags() &= ~O_CLOEXEC;
|
||||
current->fd_table[new_fd]->flags &= ~O_CLOEXEC;
|
||||
|
||||
return (u64)new_fd;
|
||||
}
|
||||
case F_GETFD: return (u64) !!(descriptor.flags() & O_CLOEXEC);
|
||||
case F_GETFD: return (u64) !!(descriptor.flags & O_CLOEXEC);
|
||||
case F_SETFD: {
|
||||
int arg = (int)args[2];
|
||||
if (arg == FD_CLOEXEC) descriptor.flags() |= O_CLOEXEC;
|
||||
if (arg == FD_CLOEXEC) descriptor.flags |= O_CLOEXEC;
|
||||
else
|
||||
descriptor.flags() &= ~O_CLOEXEC;
|
||||
descriptor.flags &= ~O_CLOEXEC;
|
||||
return 0;
|
||||
}
|
||||
case F_GETFL: return (u64)(descriptor.flags() & ~O_CLOEXEC);
|
||||
case F_GETFL: return (u64)descriptor.status_flags();
|
||||
case F_SETFL: {
|
||||
int arg = (int)args[2];
|
||||
|
||||
descriptor.flags() &= ~(O_APPEND | O_NONBLOCK);
|
||||
descriptor.status_flags() &= ~(O_APPEND | O_NONBLOCK);
|
||||
arg &= (O_APPEND | O_NONBLOCK);
|
||||
|
||||
descriptor.flags() |= arg;
|
||||
descriptor.status_flags() |= arg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -189,7 +189,7 @@ Result<u64> sys_dup2(Registers*, SyscallArgs args)
|
||||
if (newfd == oldfd) return (u64)newfd;
|
||||
|
||||
current->fd_table[newfd] = descriptor;
|
||||
current->fd_table[newfd]->flags() &= ~O_CLOEXEC;
|
||||
current->fd_table[newfd]->flags &= ~O_CLOEXEC;
|
||||
|
||||
return (u64)newfd;
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include <bits/open-flags.h>
|
||||
|
||||
// These flags are needed after open(), the rest only affect open().
|
||||
constexpr int FLAGS_TO_KEEP = O_RDWR | O_APPEND | O_NONBLOCK | O_CLOEXEC;
|
||||
constexpr int FLAGS_TO_KEEP = O_RDWR | O_APPEND | O_NONBLOCK;
|
||||
|
||||
Result<u64> sys_openat(Registers*, SyscallArgs args)
|
||||
{
|
||||
@ -87,7 +87,8 @@ Result<u64> sys_openat(Registers*, SyscallArgs args)
|
||||
kdbgln("openat: opening file %s from dirfd %d, flags %d, mode %#o = fd %d", path.chars(), dirfd, flags, mode, fd);
|
||||
#endif
|
||||
|
||||
current->fd_table[fd] = FileDescriptor { TRY(make_shared<OpenFileDescription>(inode, flags & FLAGS_TO_KEEP)), 0 };
|
||||
current->fd_table[fd] =
|
||||
FileDescriptor { TRY(make_shared<OpenFileDescription>(inode, flags & FLAGS_TO_KEEP)), 0, flags & O_CLOEXEC };
|
||||
|
||||
return (u64)fd;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "Log.h"
|
||||
#include "fs/VFS.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "net/Socket.h"
|
||||
#include "sys/Syscall.h"
|
||||
#include "thread/Scheduler.h"
|
||||
#include <bits/poll.h>
|
||||
@ -43,12 +44,27 @@ Result<u64> sys_poll(Registers* regs, SyscallArgs args)
|
||||
auto& inode = inodes[i];
|
||||
if (!inode) continue;
|
||||
|
||||
if (kfds[i].events & POLLIN && !inode->will_block_if_read())
|
||||
if (kfds[i].events & POLLIN)
|
||||
{
|
||||
if (inode->type() == VFS::InodeType::Socket)
|
||||
{
|
||||
auto socket = (Socket*)inode.ptr();
|
||||
if (socket->can_read_data() || socket->can_accept_connections())
|
||||
{
|
||||
fds_with_events++;
|
||||
kfds[i].revents |= POLLIN;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!inode->will_block_if_read())
|
||||
{
|
||||
fds_with_events++;
|
||||
kfds[i].revents |= POLLIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!fds_with_events && (timeout > 0 || infinite))
|
||||
{
|
||||
|
@ -41,6 +41,7 @@ struct FileDescriptor
|
||||
{
|
||||
SharedPtr<OpenFileDescription> description;
|
||||
usize offset { 0 };
|
||||
int flags { 0 };
|
||||
|
||||
bool should_append();
|
||||
bool should_block();
|
||||
@ -52,7 +53,7 @@ struct FileDescriptor
|
||||
return description->inode;
|
||||
}
|
||||
|
||||
int& flags()
|
||||
int& status_flags()
|
||||
{
|
||||
return description->flags;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ template <typename T, usize Size> class CircularQueue
|
||||
{
|
||||
}
|
||||
|
||||
bool is_empty()
|
||||
bool is_empty() const
|
||||
{
|
||||
return m_tail.load() == m_head.load();
|
||||
}
|
||||
@ -76,7 +76,7 @@ template <typename T> class DynamicCircularQueue
|
||||
if (m_data) free_impl(m_data);
|
||||
}
|
||||
|
||||
bool is_empty()
|
||||
bool is_empty() const
|
||||
{
|
||||
return m_tail.load() == m_head.load();
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ set(SOURCES
|
||||
src/Path.cpp
|
||||
src/Mode.cpp
|
||||
src/Prompt.cpp
|
||||
src/LocalServer.cpp
|
||||
)
|
||||
|
||||
add_library(os ${SOURCES})
|
||||
|
132
libos/include/os/LocalServer.h
Normal file
132
libos/include/os/LocalServer.h
Normal file
@ -0,0 +1,132 @@
|
||||
#pragma once
|
||||
#include <luna/Result.h>
|
||||
#include <luna/SharedPtr.h>
|
||||
#include <luna/StringView.h>
|
||||
|
||||
namespace os
|
||||
{
|
||||
/**
|
||||
* @brief A local domain server, used to communicate between processes on the same machine.
|
||||
*/
|
||||
class LocalServer : public Shareable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Create a new server object and bind it to a local address.
|
||||
*
|
||||
* @param path The path to use for the server socket.
|
||||
* @param blocking Whether the server should block if no connections are available when calling accept().
|
||||
* @return Result<SharedPtr<LocalServer>> An error, or a new server object.
|
||||
*/
|
||||
static Result<SharedPtr<LocalServer>> create(StringView path, bool blocking);
|
||||
|
||||
/**
|
||||
* @brief Activate the server and start listening for connections.
|
||||
*
|
||||
* @param backlog The number of unaccepted connections to keep.
|
||||
* @return Result<void> Whether the operation succeded.
|
||||
*/
|
||||
Result<void> listen(int backlog);
|
||||
|
||||
/**
|
||||
* @brief Return the underlying socket file descriptor used by this object.
|
||||
*
|
||||
* @return int The file descriptor.
|
||||
*/
|
||||
int fd() const
|
||||
{
|
||||
return m_fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief An interface to communicate with clients connected to a local server.
|
||||
*/
|
||||
class Client : public Shareable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Read arbitrary data from the client. The call will block if there is no data and the parent server
|
||||
* object has not been created as non-blocking.
|
||||
*
|
||||
* @param buf The buffer to read data into.
|
||||
* @param length The maximum amount of bytes to read.
|
||||
* @return Result<usize> An error, or the number of bytes read.
|
||||
*/
|
||||
Result<usize> recv(u8* buf, usize length);
|
||||
|
||||
/**
|
||||
* @brief Read an object from the client. The call will block if there is no data and the parent server
|
||||
* object has not been created as non-blocking.
|
||||
*
|
||||
* @tparam T The type of the object.
|
||||
* @param out A reference to the object to read data into.
|
||||
* @return Result<void> Whether the operation succeded.
|
||||
*/
|
||||
template <typename T> Result<void> recv_typed(T& out)
|
||||
{
|
||||
TRY(recv((u8*)&out, sizeof(T)));
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send arbitrary data to the client.
|
||||
*
|
||||
* @param buf The buffer to send data from.
|
||||
* @param length The amount of bytes to send.
|
||||
* @return Result<usize> An error, or the number of bytes actually sent.
|
||||
*/
|
||||
Result<usize> send(const u8* buf, usize length);
|
||||
|
||||
/**
|
||||
* @brief Send an object to the client.
|
||||
*
|
||||
* @tparam T The type of the object.
|
||||
* @param out A reference to the object to send data from.
|
||||
* @return Result<void> Whether the operation succeded.
|
||||
*/
|
||||
template <typename T> Result<void> send_typed(const T& out)
|
||||
{
|
||||
TRY(send((const u8*)&out, sizeof(T)));
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disconnect from the attached client.
|
||||
*
|
||||
* This will make any further reads on the client return ECONNRESET, and will make this object invalid.
|
||||
*/
|
||||
void disconnect();
|
||||
|
||||
/**
|
||||
* @brief Return the underlying socket file descriptor used by this object.
|
||||
*
|
||||
* @return int The file descriptor.
|
||||
*/
|
||||
int fd() const
|
||||
{
|
||||
return m_fd;
|
||||
}
|
||||
|
||||
Client(int fd);
|
||||
~Client();
|
||||
|
||||
private:
|
||||
int m_fd;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Accept a new incoming connection and return a handle to it. If there are no incoming connections,
|
||||
* accept() either blocks until there is one (if the object was created with blocking=true), or returns EAGAIN
|
||||
* (if the object was created with blocking=false).
|
||||
*
|
||||
* @return Result<SharedPtr<Client>> An error, or a handle to the new connection.
|
||||
*/
|
||||
Result<SharedPtr<Client>> accept();
|
||||
|
||||
~LocalServer();
|
||||
|
||||
private:
|
||||
int m_fd;
|
||||
bool m_blocking;
|
||||
};
|
||||
}
|
87
libos/src/LocalServer.cpp
Normal file
87
libos/src/LocalServer.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <os/FileSystem.h>
|
||||
#include <os/LocalServer.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace os
|
||||
{
|
||||
Result<SharedPtr<LocalServer>> LocalServer::create(StringView path, bool blocking)
|
||||
{
|
||||
auto server = TRY(make_shared<LocalServer>());
|
||||
|
||||
(void)os::FileSystem::remove(path); // We explicitly ignore any error here, either it doesn't exist (which is
|
||||
// fine), or it cannot be removed, which will make bind() fail later.
|
||||
|
||||
int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (sockfd < 0) return err(errno);
|
||||
|
||||
struct sockaddr_un un;
|
||||
un.sun_family = AF_UNIX;
|
||||
strncpy(un.sun_path, path.chars(), sizeof(un.sun_path));
|
||||
|
||||
if (bind(sockfd, (struct sockaddr*)&un, sizeof(un)) < 0)
|
||||
{
|
||||
close(sockfd);
|
||||
return err(errno);
|
||||
}
|
||||
|
||||
if (!blocking) { fcntl(sockfd, F_SETFL, O_NONBLOCK); }
|
||||
server->m_blocking = blocking;
|
||||
|
||||
fcntl(sockfd, F_SETFD, FD_CLOEXEC);
|
||||
|
||||
server->m_fd = sockfd;
|
||||
return server;
|
||||
}
|
||||
|
||||
Result<void> LocalServer::listen(int backlog)
|
||||
{
|
||||
if (::listen(m_fd, backlog) < 0) return err(errno);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<SharedPtr<LocalServer::Client>> LocalServer::accept()
|
||||
{
|
||||
int fd = ::accept(m_fd, nullptr, nullptr);
|
||||
if (fd < 0) return err(errno);
|
||||
if (!m_blocking) fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
return make_shared<Client>(fd);
|
||||
}
|
||||
|
||||
LocalServer::~LocalServer()
|
||||
{
|
||||
close(m_fd);
|
||||
}
|
||||
|
||||
LocalServer::Client::Client(int fd) : m_fd(fd)
|
||||
{
|
||||
}
|
||||
|
||||
LocalServer::Client::~Client()
|
||||
{
|
||||
if (m_fd >= 0) close(m_fd);
|
||||
}
|
||||
|
||||
Result<usize> LocalServer::Client::recv(u8* buf, usize length)
|
||||
{
|
||||
ssize_t nread = read(m_fd, buf, length);
|
||||
if (nread < 0) return err(errno);
|
||||
return nread;
|
||||
}
|
||||
|
||||
Result<usize> LocalServer::Client::send(const u8* buf, usize length)
|
||||
{
|
||||
ssize_t nwrite = write(m_fd, buf, length);
|
||||
if (nwrite < 0) return err(errno);
|
||||
return nwrite;
|
||||
}
|
||||
|
||||
void LocalServer::Client::disconnect()
|
||||
{
|
||||
close(m_fd);
|
||||
m_fd = -1;
|
||||
}
|
||||
}
|
15
libui/CMakeLists.txt
Normal file
15
libui/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
# The UI and graphics library for Luna.
|
||||
|
||||
file(GLOB HEADERS include/ui/*.h)
|
||||
|
||||
set(SOURCES
|
||||
${HEADERS}
|
||||
src/Canvas.cpp
|
||||
src/Color.cpp
|
||||
src/Rect.cpp
|
||||
)
|
||||
|
||||
add_library(ui ${SOURCES})
|
||||
target_compile_options(ui PRIVATE ${COMMON_FLAGS})
|
||||
target_include_directories(ui PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include/)
|
||||
target_include_directories(ui PUBLIC ${LUNA_BASE}/usr/include)
|
56
libui/include/ui/Canvas.h
Normal file
56
libui/include/ui/Canvas.h
Normal file
@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
#include <luna/Result.h>
|
||||
#include <luna/Types.h>
|
||||
#include <ui/Color.h>
|
||||
#include <ui/Point.h>
|
||||
#include <ui/Rect.h>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
/**
|
||||
* @brief A drawable surface.
|
||||
*/
|
||||
struct Canvas
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int stride;
|
||||
u8* ptr;
|
||||
|
||||
/**
|
||||
* @brief Create a new Canvas object.
|
||||
*
|
||||
* @param ptr The memory to use for the canvas. It must be of at least width * height * 4 bytes of length.
|
||||
* @param width The width of the canvas.
|
||||
* @param height The height of the canvas.
|
||||
* @return Canvas The new Canvas object.
|
||||
*/
|
||||
static Canvas create(u8* ptr, int width, int height);
|
||||
|
||||
/**
|
||||
* @brief Return a new Canvas that represents a subsection of the current one.
|
||||
*
|
||||
* @param rect The dimensions of the new canvas. If these exceed the bounds of the current canvas, they will be
|
||||
* clamped.
|
||||
* @return Canvas The new Canvas object.
|
||||
*/
|
||||
Canvas subcanvas(Rect rect);
|
||||
|
||||
/**
|
||||
* @brief Return the dimensions of the current canvas.
|
||||
*
|
||||
* @return Rect This canvas's dimensions, as a Rect object.
|
||||
*/
|
||||
Rect rect()
|
||||
{
|
||||
return Rect { .pos = { 0, 0 }, .width = width, .height = height };
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fill the entire canvas with one color.
|
||||
*
|
||||
* @param color The color to use.
|
||||
*/
|
||||
void fill(Color color);
|
||||
};
|
||||
};
|
63
libui/include/ui/Color.h
Normal file
63
libui/include/ui/Color.h
Normal file
@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
#include <luna/Types.h>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
/**
|
||||
* @brief A 32-bit ARGB color.
|
||||
*/
|
||||
struct Color
|
||||
{
|
||||
union {
|
||||
u32 raw;
|
||||
u8 colors[4];
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Construct a new color from a 32-bit ARGB integer.
|
||||
*
|
||||
* @param raw The integer representing the color.
|
||||
* @return constexpr Color The new color.
|
||||
*/
|
||||
static constexpr Color from_u32(u32 raw)
|
||||
{
|
||||
return Color { .raw = raw };
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct a new color from its separate RGBA values (from 0 to 255).
|
||||
*
|
||||
* @param red The red value.
|
||||
* @param green The green value.
|
||||
* @param blue The blue value.
|
||||
* @param alpha The alpha value.
|
||||
* @return constexpr Color The new color.
|
||||
*/
|
||||
static constexpr Color from_rgba(u8 red, u8 green, u8 blue, u8 alpha)
|
||||
{
|
||||
return Color { .colors = { blue, green, red, alpha } };
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct a new color from its separate RGB values (from 0 to 255).
|
||||
*
|
||||
* @param red The red value.
|
||||
* @param green The green value.
|
||||
* @param blue The blue value.
|
||||
* @return constexpr Color The new color.
|
||||
*/
|
||||
static constexpr Color from_rgb(u8 red, u8 green, u8 blue)
|
||||
{
|
||||
return from_rgba(red, green, blue, 0xff);
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr Color WHITE = Color::from_rgb(0xff, 0xff, 0xff);
|
||||
static constexpr Color BLACK = Color::from_rgb(0x00, 0x00, 0x00);
|
||||
|
||||
static constexpr Color BLUE = Color::from_rgb(0x00, 0x00, 0xff);
|
||||
static constexpr Color GREEN = Color::from_rgb(0x00, 0xff, 0x00);
|
||||
static constexpr Color RED = Color::from_rgb(0xff, 0x00, 0x00);
|
||||
|
||||
static constexpr Color CYAN = Color::from_rgb(0x00, 0xff, 0xff);
|
||||
};
|
13
libui/include/ui/Point.h
Normal file
13
libui/include/ui/Point.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
namespace ui
|
||||
{
|
||||
/**
|
||||
* @brief A point in 2D space.
|
||||
*/
|
||||
struct Point
|
||||
{
|
||||
int x { 0 };
|
||||
int y { 0 };
|
||||
};
|
||||
}
|
39
libui/include/ui/Rect.h
Normal file
39
libui/include/ui/Rect.h
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include <ui/Point.h>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
/**
|
||||
* @brief A simple rectangle.
|
||||
*/
|
||||
struct Rect
|
||||
{
|
||||
Point pos;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
/**
|
||||
* @brief Check if a point is contained in this rectangle.
|
||||
*
|
||||
* @param point The point to check.
|
||||
* @return true The point is contained inside the rectangle.
|
||||
* @return false The point is not contained inside the rectangle.
|
||||
*/
|
||||
bool contains(Point point);
|
||||
|
||||
/**
|
||||
* @brief Normalize a point to fit inside this rectangle.
|
||||
*
|
||||
* @param point The original point.
|
||||
* @return Point The normalized point.
|
||||
*/
|
||||
Point normalize(Point point);
|
||||
|
||||
/**
|
||||
* @brief Return a copy of this rectangle with no negative values (normalized to 0).
|
||||
*
|
||||
* @return Rect The new rectangle.
|
||||
*/
|
||||
Rect absolute();
|
||||
};
|
||||
}
|
45
libui/src/Canvas.cpp
Normal file
45
libui/src/Canvas.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
#include <string.h>
|
||||
#include <ui/Canvas.h>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
Canvas Canvas::create(u8* ptr, int width, int height)
|
||||
{
|
||||
return Canvas { .width = width, .height = height, .stride = width, .ptr = ptr };
|
||||
}
|
||||
|
||||
Canvas Canvas::subcanvas(Rect rect)
|
||||
{
|
||||
if (rect.pos.x < 0)
|
||||
{
|
||||
rect.pos.x = 0;
|
||||
rect.width = rect.width + rect.pos.x;
|
||||
}
|
||||
if (rect.pos.y < 0)
|
||||
{
|
||||
rect.pos.y = 0;
|
||||
rect.height = rect.height + rect.pos.y;
|
||||
}
|
||||
if (rect.pos.x + rect.width > width) rect.width = width - rect.pos.x;
|
||||
if (rect.pos.y + rect.height > height) rect.height = height - rect.pos.y;
|
||||
|
||||
u8* p = ptr + rect.pos.x * sizeof(Color) + (rect.pos.y * sizeof(Color) * stride);
|
||||
|
||||
return Canvas { .width = rect.width, .height = rect.height, .stride = stride, .ptr = p };
|
||||
}
|
||||
|
||||
void Canvas::fill(Color color)
|
||||
{
|
||||
u8* p = ptr;
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
u32* colorp = (u32*)p;
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
*colorp = color.raw;
|
||||
colorp++;
|
||||
}
|
||||
p += stride * sizeof(Color);
|
||||
}
|
||||
}
|
||||
}
|
0
libui/src/Color.cpp
Normal file
0
libui/src/Color.cpp
Normal file
24
libui/src/Rect.cpp
Normal file
24
libui/src/Rect.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
#include <ui/Rect.h>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
bool Rect::contains(Point point)
|
||||
{
|
||||
return (point.x >= pos.x) && (point.y >= pos.y) && (point.x <= (pos.x + width)) &&
|
||||
(point.y <= (pos.y + height));
|
||||
}
|
||||
|
||||
Point Rect::normalize(Point point)
|
||||
{
|
||||
if (point.x < pos.x) point.x = pos.x;
|
||||
if (point.y < pos.y) point.y = pos.y;
|
||||
if (point.x > pos.x + width) point.x = pos.x + width;
|
||||
if (point.y > pos.y + height) point.y = pos.y + height;
|
||||
return point;
|
||||
}
|
||||
|
||||
Rect Rect::absolute()
|
||||
{
|
||||
return Rect { ui::Point { pos.x < 0 ? 0 : pos.x, pos.y < 0 ? 0 : pos.y }, width, height };
|
||||
}
|
||||
};
|
@ -8,10 +8,12 @@ cd $LUNA_ROOT
|
||||
mkdir -p $LUNA_BASE
|
||||
mkdir -p $LUNA_BASE/usr/include
|
||||
mkdir -p $LUNA_BASE/usr/include/luna
|
||||
mkdir -p $LUNA_BASE/usr/include/ui
|
||||
mkdir -p $LUNA_BASE/usr/include/os
|
||||
mkdir -p $LUNA_BASE/usr/include/moon
|
||||
|
||||
cp --preserve=timestamps -RT libc/include/ $LUNA_BASE/usr/include
|
||||
cp --preserve=timestamps -RT libluna/include/luna/ $LUNA_BASE/usr/include/luna
|
||||
cp --preserve=timestamps -RT libui/include/ui/ $LUNA_BASE/usr/include/ui
|
||||
cp --preserve=timestamps -RT libos/include/os/ $LUNA_BASE/usr/include/os
|
||||
cp --preserve=timestamps -RT kernel/src/api/ $LUNA_BASE/usr/include/moon
|
||||
|
@ -4,7 +4,7 @@ source $(dirname $0)/env.sh
|
||||
|
||||
cd $LUNA_ROOT
|
||||
|
||||
FOLDERS=(kernel libc libos libluna apps shell tests)
|
||||
FOLDERS=(kernel libc libos libui libluna apps shell tests)
|
||||
|
||||
SOURCES=($(find ${FOLDERS[@]} -type f -name "*.cpp"))
|
||||
SOURCES+=($(find ${FOLDERS[@]} -type f -name "*.h"))
|
||||
|
16
wind/CMakeLists.txt
Normal file
16
wind/CMakeLists.txt
Normal file
@ -0,0 +1,16 @@
|
||||
set(SOURCES
|
||||
main.cpp
|
||||
Screen.h
|
||||
Screen.cpp
|
||||
Mouse.h
|
||||
Mouse.cpp
|
||||
Window.h
|
||||
Window.cpp
|
||||
)
|
||||
|
||||
add_executable(wind ${SOURCES})
|
||||
target_compile_options(wind PRIVATE -Os ${COMMON_FLAGS} -Wno-write-strings)
|
||||
add_dependencies(wind libc)
|
||||
target_include_directories(wind PRIVATE ${LUNA_BASE}/usr/include ${CMAKE_CURRENT_LIST_DIR})
|
||||
target_link_libraries(wind PRIVATE os ui)
|
||||
install(TARGETS wind DESTINATION ${LUNA_BASE}/usr/bin)
|
53
wind/Mouse.cpp
Normal file
53
wind/Mouse.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
#include "Mouse.h"
|
||||
#include <os/File.h>
|
||||
|
||||
Mouse::Mouse(ui::Canvas& screen)
|
||||
{
|
||||
m_position.x = screen.width / 2;
|
||||
m_position.y = screen.height / 2;
|
||||
m_screen_rect = screen.rect();
|
||||
}
|
||||
|
||||
void Mouse::draw(ui::Canvas& screen)
|
||||
{
|
||||
auto canvas = screen.subcanvas(ui::Rect { m_position, 10, 10 });
|
||||
canvas.fill(ui::WHITE);
|
||||
}
|
||||
|
||||
void Mouse::update(const moon::MousePacket& packet)
|
||||
{
|
||||
m_position.x += packet.xdelta;
|
||||
m_position.y -= packet.ydelta;
|
||||
m_position = m_screen_rect.normalize(m_position);
|
||||
|
||||
if ((packet.buttons & moon::MouseButton::Left) && !m_dragging_window)
|
||||
{
|
||||
g_windows.for_each_reversed([this](Window* window) {
|
||||
if (!this->m_dragging_window && window->surface.contains(this->m_position))
|
||||
{
|
||||
this->m_dragging_window = window;
|
||||
this->m_initial_drag_position = ui::Point { this->m_position.x - window->surface.pos.x,
|
||||
this->m_position.y - window->surface.pos.y };
|
||||
os::println("Started drag: window at (%d,%d,%d,%d) with offset (%d,%d)", window->surface.pos.x,
|
||||
window->surface.pos.y, window->surface.width, window->surface.height,
|
||||
this->m_initial_drag_position.x, this->m_initial_drag_position.y);
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (m_dragging_window && !(packet.buttons & moon::MouseButton::Left))
|
||||
{
|
||||
os::println("Stopped drag: window at (%d,%d,%d,%d) with offset (%d,%d)", m_dragging_window->surface.pos.x,
|
||||
m_dragging_window->surface.pos.y, m_dragging_window->surface.width,
|
||||
m_dragging_window->surface.height, this->m_initial_drag_position.x,
|
||||
this->m_initial_drag_position.y);
|
||||
m_dragging_window = nullptr;
|
||||
}
|
||||
|
||||
if (m_dragging_window)
|
||||
{
|
||||
m_dragging_window->surface.pos =
|
||||
ui::Point { m_position.x - m_initial_drag_position.x, m_position.y - m_initial_drag_position.y };
|
||||
m_dragging_window->surface = m_dragging_window->surface.absolute();
|
||||
m_dragging_window->focus();
|
||||
}
|
||||
}
|
22
wind/Mouse.h
Normal file
22
wind/Mouse.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "Screen.h"
|
||||
#include "Window.h"
|
||||
#include <moon/Mouse.h>
|
||||
#include <ui/Canvas.h>
|
||||
|
||||
class Mouse
|
||||
{
|
||||
public:
|
||||
Mouse(ui::Canvas& screen);
|
||||
|
||||
void update(const moon::MousePacket& packet);
|
||||
|
||||
void draw(ui::Canvas& screen);
|
||||
|
||||
private:
|
||||
ui::Point m_position;
|
||||
ui::Rect m_screen_rect;
|
||||
|
||||
Window* m_dragging_window = nullptr;
|
||||
ui::Point m_initial_drag_position;
|
||||
};
|
32
wind/Screen.cpp
Normal file
32
wind/Screen.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
#include "Screen.h"
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
Result<Screen> Screen::open()
|
||||
{
|
||||
int fd = ::open("/dev/fb0", O_RDWR);
|
||||
if (fd < 0) return err(errno);
|
||||
|
||||
int width = ioctl(fd, FB_GET_WIDTH);
|
||||
int height = ioctl(fd, FB_GET_HEIGHT);
|
||||
|
||||
void* p = mmap(nullptr, width * height * BYTES_PER_PIXEL, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
close(fd);
|
||||
|
||||
if (p == MAP_FAILED) { return err(errno); }
|
||||
|
||||
Screen screen;
|
||||
|
||||
screen.m_canvas = ui::Canvas::create((u8*)p, width, height);
|
||||
screen.m_size = width * height * BYTES_PER_PIXEL;
|
||||
|
||||
return screen;
|
||||
}
|
||||
|
||||
void Screen::sync()
|
||||
{
|
||||
msync(m_canvas.ptr, size(), MS_SYNC);
|
||||
}
|
27
wind/Screen.h
Normal file
27
wind/Screen.h
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <luna/OwnedPtr.h>
|
||||
#include <ui/Canvas.h>
|
||||
|
||||
constexpr int BYTES_PER_PIXEL = 4;
|
||||
|
||||
class Screen
|
||||
{
|
||||
public:
|
||||
static Result<Screen> open();
|
||||
|
||||
ui::Canvas& canvas()
|
||||
{
|
||||
return m_canvas;
|
||||
}
|
||||
|
||||
int size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
void sync();
|
||||
|
||||
private:
|
||||
ui::Canvas m_canvas;
|
||||
int m_size;
|
||||
};
|
20
wind/Window.cpp
Normal file
20
wind/Window.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
#include "Window.h"
|
||||
|
||||
LinkedList<Window> g_windows;
|
||||
|
||||
void Window::draw(ui::Canvas& screen)
|
||||
{
|
||||
screen.subcanvas(surface).fill(color);
|
||||
}
|
||||
|
||||
void Window::focus()
|
||||
{
|
||||
// Bring the window to the front of the list.
|
||||
g_windows.remove(this);
|
||||
g_windows.append(this);
|
||||
}
|
||||
|
||||
Window::Window(ui::Rect r, ui::Color c) : surface(r), color(c)
|
||||
{
|
||||
g_windows.append(this);
|
||||
}
|
19
wind/Window.h
Normal file
19
wind/Window.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include <luna/LinkedList.h>
|
||||
#include <ui/Canvas.h>
|
||||
#include <ui/Color.h>
|
||||
#include <ui/Rect.h>
|
||||
|
||||
struct Window : public LinkedListNode<Window>
|
||||
{
|
||||
ui::Rect surface;
|
||||
ui::Color color;
|
||||
|
||||
Window(ui::Rect, ui::Color);
|
||||
|
||||
void focus();
|
||||
|
||||
void draw(ui::Canvas& screen);
|
||||
};
|
||||
|
||||
extern LinkedList<Window> g_windows;
|
117
wind/main.cpp
Normal file
117
wind/main.cpp
Normal file
@ -0,0 +1,117 @@
|
||||
#include "Mouse.h"
|
||||
#include "Screen.h"
|
||||
#include "Window.h"
|
||||
#include <errno.h>
|
||||
#include <moon/Keyboard.h>
|
||||
#include <os/ArgumentParser.h>
|
||||
#include <os/File.h>
|
||||
#include <os/LocalServer.h>
|
||||
#include <pwd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/poll.h>
|
||||
#include <unistd.h>
|
||||
|
||||
Result<int> luna_main(int argc, char** argv)
|
||||
{
|
||||
StringView socket_path = "/tmp/wind.sock";
|
||||
StringView user;
|
||||
|
||||
os::ArgumentParser parser;
|
||||
parser.add_description("The display server for Luna's graphical user interface."_sv);
|
||||
parser.add_system_program_info("wind"_sv);
|
||||
parser.add_value_argument(socket_path, 's', "socket"_sv, "the path for the local IPC socket"_sv);
|
||||
parser.add_value_argument(user, 'u', "user"_sv, "the user to run as"_sv);
|
||||
parser.parse(argc, argv);
|
||||
|
||||
if (geteuid() != 0)
|
||||
{
|
||||
os::eprintln("error: wind must be run as root to initialize resources, run with --user=<USERNAME> to drop "
|
||||
"privileges afterwards");
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto mouse = TRY(os::File::open("/dev/mouse", os::File::ReadOnly));
|
||||
mouse->set_buffer(os::File::NotBuffered);
|
||||
mouse->set_close_on_exec();
|
||||
|
||||
auto keyboard = TRY(os::File::open("/dev/kbd", os::File::ReadOnly));
|
||||
keyboard->set_buffer(os::File::NotBuffered);
|
||||
keyboard->set_close_on_exec();
|
||||
|
||||
auto screen = TRY(Screen::open());
|
||||
|
||||
auto server = TRY(os::LocalServer::create(socket_path, false));
|
||||
TRY(server->listen(20));
|
||||
|
||||
Mouse mouse_pointer { screen.canvas() };
|
||||
|
||||
ioctl(STDIN_FILENO, TTYSETGFX, 1);
|
||||
|
||||
setpgid(0, 0);
|
||||
|
||||
int fd = open("/dev/null", O_RDONLY);
|
||||
if (fd >= 0)
|
||||
{
|
||||
dup2(fd, STDIN_FILENO);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
clearenv();
|
||||
|
||||
if (!user.is_empty())
|
||||
{
|
||||
auto* pwd = getpwnam(user.chars());
|
||||
if (pwd)
|
||||
{
|
||||
setgid(pwd->pw_gid);
|
||||
setuid(pwd->pw_uid);
|
||||
}
|
||||
}
|
||||
|
||||
ui::Color background = ui::BLACK;
|
||||
|
||||
TRY(make<Window>(ui::Rect { 200, 200, 600, 400 }, ui::GREEN));
|
||||
TRY(make<Window>(ui::Rect { 100, 100, 300, 200 }, ui::RED));
|
||||
TRY(make<Window>(ui::Rect { 600, 130, 350, 250 }, ui::CYAN));
|
||||
|
||||
Vector<SharedPtr<os::LocalServer::Client>> clients;
|
||||
|
||||
while (1)
|
||||
{
|
||||
screen.canvas().fill(background);
|
||||
for (auto* window : g_windows) window->draw(screen.canvas());
|
||||
mouse_pointer.draw(screen.canvas());
|
||||
screen.sync();
|
||||
|
||||
struct pollfd fds[] = {
|
||||
{ .fd = mouse->fd(), .events = POLLIN, .revents = 0 },
|
||||
{ .fd = keyboard->fd(), .events = POLLIN, .revents = 0 },
|
||||
{ .fd = server->fd(), .events = POLLIN, .revents = 0 },
|
||||
};
|
||||
|
||||
int rc = poll(fds, 3, 1000);
|
||||
if (!rc) continue;
|
||||
if (rc < 0) { os::println("poll: error: %s", strerror(errno)); }
|
||||
|
||||
if (fds[0].revents & POLLIN)
|
||||
{
|
||||
moon::MousePacket packet;
|
||||
TRY(mouse->read_typed(packet));
|
||||
mouse_pointer.update(packet);
|
||||
}
|
||||
if (fds[1].revents & POLLIN)
|
||||
{
|
||||
moon::KeyboardPacket packet;
|
||||
TRY(keyboard->read_typed(packet));
|
||||
background = ui::Color::from_rgb(0x00, 0x00, packet.key * 2);
|
||||
}
|
||||
if (fds[2].revents & POLLIN)
|
||||
{
|
||||
auto client = TRY(server->accept());
|
||||
os::println("wind: New client connected!");
|
||||
TRY(clients.try_append(client));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user