mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-22 04:25:13 +00:00
Implement basic support for POSIX-style select().
Now we can block on both the PTY *and* the GUI event stream in Terminal.
This commit is contained in:
parent
46181cf023
commit
10387beda7
Notes:
sideshowbarker
2024-07-19 16:01:56 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/10387beda76
8 changed files with 164 additions and 30 deletions
|
@ -1909,3 +1909,78 @@ DisplayInfo Process::get_display_info()
|
|||
info.framebuffer = m_display_framebuffer_region->linearAddress.asPtr();
|
||||
return info;
|
||||
}
|
||||
|
||||
int Process::sys$select(const Syscall::SC_select_params* params)
|
||||
{
|
||||
if (!validate_read_typed(params))
|
||||
return -EFAULT;
|
||||
if (params->writefds && !validate_read_typed(params->writefds))
|
||||
return -EFAULT;
|
||||
if (params->readfds && !validate_read_typed(params->readfds))
|
||||
return -EFAULT;
|
||||
if (params->exceptfds && !validate_read_typed(params->exceptfds))
|
||||
return -EFAULT;
|
||||
if (params->timeout && !validate_read_typed(params->timeout))
|
||||
return -EFAULT;
|
||||
int nfds = params->nfds;
|
||||
fd_set* writefds = params->writefds;
|
||||
fd_set* readfds = params->readfds;
|
||||
fd_set* exceptfds = params->exceptfds;
|
||||
auto* timeout = params->timeout;
|
||||
|
||||
// FIXME: Implement exceptfds support.
|
||||
ASSERT(!exceptfds);
|
||||
|
||||
// FIXME: Implement timeout support.
|
||||
ASSERT(!timeout);
|
||||
|
||||
if (nfds < 0)
|
||||
return -EINVAL;
|
||||
|
||||
// FIXME: Return -EBADF if one of the fd sets contains an invalid fd.
|
||||
// FIXME: Return -EINTR if a signal is caught.
|
||||
// FIXME: Return -EINVAL if timeout is invalid.
|
||||
|
||||
auto transfer_fds = [nfds] (fd_set* set, auto& vector) {
|
||||
if (!set)
|
||||
return;
|
||||
vector.clear_with_capacity();
|
||||
auto bitmap = Bitmap::wrap((byte*)set, FD_SETSIZE);
|
||||
for (int i = 0; i < nfds; ++i) {
|
||||
if (bitmap.get(i))
|
||||
vector.append(i);
|
||||
}
|
||||
};
|
||||
|
||||
transfer_fds(writefds, m_select_write_fds);
|
||||
transfer_fds(readfds, m_select_read_fds);
|
||||
|
||||
block(BlockedSelect);
|
||||
Scheduler::yield();
|
||||
|
||||
int markedfds = 0;
|
||||
|
||||
if (readfds) {
|
||||
memset(readfds, 0, sizeof(fd_set));
|
||||
auto bitmap = Bitmap::wrap((byte*)readfds, FD_SETSIZE);
|
||||
for (int fd : m_select_read_fds) {
|
||||
if (m_fds[fd].descriptor->has_data_available_for_reading(*this)) {
|
||||
bitmap.set(fd, true);
|
||||
++markedfds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (writefds) {
|
||||
memset(writefds, 0, sizeof(fd_set));
|
||||
auto bitmap = Bitmap::wrap((byte*)writefds, FD_SETSIZE);
|
||||
for (int fd : m_select_write_fds) {
|
||||
if (m_fds[fd].descriptor->can_write(*this)) {
|
||||
bitmap.set(fd, true);
|
||||
++markedfds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return markedfds;
|
||||
}
|
||||
|
|
|
@ -69,6 +69,7 @@ public:
|
|||
BlockedRead,
|
||||
BlockedWrite,
|
||||
BlockedSignal,
|
||||
BlockedSelect,
|
||||
};
|
||||
|
||||
enum RingLevel {
|
||||
|
@ -81,7 +82,7 @@ public:
|
|||
|
||||
bool is_blocked() const
|
||||
{
|
||||
return m_state == BlockedSleep || m_state == BlockedWait || m_state == BlockedRead || m_state == BlockedSignal;
|
||||
return m_state == BlockedSleep || m_state == BlockedWait || m_state == BlockedRead || m_state == BlockedSignal || m_state == BlockedSelect;
|
||||
}
|
||||
|
||||
PageDirectory& page_directory() { return *m_page_directory; }
|
||||
|
@ -156,6 +157,7 @@ public:
|
|||
void* sys$mmap(const Syscall::SC_mmap_params*);
|
||||
int sys$munmap(void*, size_t size);
|
||||
int sys$set_mmap_name(void*, size_t, const char*);
|
||||
int sys$select(const Syscall::SC_select_params*);
|
||||
ssize_t sys$get_dir_entries(int fd, void*, size_t);
|
||||
int sys$getcwd(char*, size_t);
|
||||
int sys$chdir(const char*);
|
||||
|
@ -304,6 +306,8 @@ private:
|
|||
dword m_timesScheduled { 0 };
|
||||
pid_t m_waitee_pid { -1 };
|
||||
int m_blocked_fd { -1 };
|
||||
Vector<int> m_select_read_fds;
|
||||
Vector<int> m_select_write_fds;
|
||||
size_t m_max_open_file_descriptors { 16 };
|
||||
SignalActionData m_signal_action_data[32];
|
||||
dword m_pending_signals { 0 };
|
||||
|
@ -394,6 +398,7 @@ static inline const char* toString(Process::State state)
|
|||
case Process::BlockedRead: return "Read";
|
||||
case Process::BlockedWrite: return "Write";
|
||||
case Process::BlockedSignal: return "Signal";
|
||||
case Process::BlockedSelect: return "Select";
|
||||
case Process::BeingInspected: return "Inspect";
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
|
|
|
@ -64,6 +64,22 @@ bool Scheduler::pick_next()
|
|||
return true;
|
||||
}
|
||||
|
||||
if (process.state() == Process::BlockedSelect) {
|
||||
for (int fd : process.m_select_read_fds) {
|
||||
if (process.m_fds[fd].descriptor->has_data_available_for_reading(process)) {
|
||||
process.unblock();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (int fd : process.m_select_write_fds) {
|
||||
if (process.m_fds[fd].descriptor->can_write(process)) {
|
||||
process.unblock();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (process.state() == Process::Skip1SchedulerPass) {
|
||||
process.set_state(Process::Skip0SchedulerPasses);
|
||||
return true;
|
||||
|
|
|
@ -98,6 +98,8 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2,
|
|||
return current->sys$waitpid((pid_t)arg1, (int*)arg2, (int)arg3);
|
||||
case Syscall::SC_mmap:
|
||||
return (dword)current->sys$mmap((const SC_mmap_params*)arg1);
|
||||
case Syscall::SC_select:
|
||||
return current->sys$select((const SC_select_params*)arg1);
|
||||
case Syscall::SC_munmap:
|
||||
return current->sys$munmap((void*)arg1, (size_t)arg2);
|
||||
case Syscall::SC_gethostname:
|
||||
|
|
|
@ -71,8 +71,11 @@
|
|||
__ENUMERATE_SYSCALL(gui_destroy_window) \
|
||||
__ENUMERATE_SYSCALL(gui_get_window_backing_store) \
|
||||
__ENUMERATE_SYSCALL(gui_invalidate_window) \
|
||||
__ENUMERATE_SYSCALL(select) \
|
||||
|
||||
|
||||
struct fd_set;
|
||||
|
||||
namespace Syscall {
|
||||
|
||||
enum Function {
|
||||
|
@ -102,6 +105,14 @@ struct SC_mmap_params {
|
|||
int32_t offset; // FIXME: 64-bit off_t?
|
||||
};
|
||||
|
||||
struct SC_select_params {
|
||||
int nfds;
|
||||
fd_set* readfds;
|
||||
fd_set* writefds;
|
||||
fd_set* exceptfds;
|
||||
struct timeval* timeout;
|
||||
};
|
||||
|
||||
void initialize();
|
||||
int sync();
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ LIBC_OBJS = \
|
|||
math.o \
|
||||
utime.o \
|
||||
gui.o \
|
||||
sys/select.o \
|
||||
entry.o
|
||||
|
||||
OBJS = $(AK_OBJS) $(WIDGETS_OBJS) $(LIBC_OBJS)
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <Widgets/GraphicsBitmap.h>
|
||||
#include <Widgets/Painter.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/select.h>
|
||||
#include <gui.h>
|
||||
#include "Terminal.h"
|
||||
|
||||
|
@ -53,9 +54,14 @@ static void make_shell(int ptm_fd)
|
|||
}
|
||||
}
|
||||
|
||||
static int max(int a, int b)
|
||||
{
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
int ptm_fd = open("/dev/ptm0", O_RDWR | O_NONBLOCK);
|
||||
int ptm_fd = open("/dev/ptm0", O_RDWR);
|
||||
if (ptm_fd < 0) {
|
||||
perror("open");
|
||||
return 1;
|
||||
|
@ -63,7 +69,7 @@ int main(int, char**)
|
|||
|
||||
make_shell(ptm_fd);
|
||||
|
||||
int event_fd = open("/dev/gui_events", O_RDONLY | O_NONBLOCK);
|
||||
int event_fd = open("/dev/gui_events", O_RDONLY);
|
||||
if (event_fd < 0) {
|
||||
perror("open");
|
||||
return 1;
|
||||
|
@ -74,38 +80,53 @@ int main(int, char**)
|
|||
terminal.paint();
|
||||
|
||||
for (;;) {
|
||||
byte buffer[1024];
|
||||
ssize_t ptm_nread = read(ptm_fd, buffer, sizeof(buffer));
|
||||
if (ptm_nread > 0) {
|
||||
for (ssize_t i = 0; i < ptm_nread; ++i)
|
||||
fd_set rfds;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(ptm_fd, &rfds);
|
||||
FD_SET(event_fd, &rfds);
|
||||
int nfds = select(max(ptm_fd, event_fd) + 1, &rfds, nullptr, nullptr, nullptr);
|
||||
if (nfds < 0) {
|
||||
dbgprintf("Terminal(%u) select() failed :( errno=%d\n", getpid(), errno);
|
||||
}
|
||||
|
||||
if (FD_ISSET(ptm_fd, &rfds)) {
|
||||
byte buffer[1024];
|
||||
ssize_t nread = read(ptm_fd, buffer, sizeof(buffer));
|
||||
if (nread < 0) {
|
||||
perror("read(ptm)");
|
||||
continue;
|
||||
}
|
||||
assert(nread != 0);
|
||||
for (ssize_t i = 0; i < nread; ++i)
|
||||
terminal.on_char(buffer[i]);
|
||||
terminal.paint();
|
||||
}
|
||||
|
||||
GUI_Event event;
|
||||
ssize_t nread = read(event_fd, &event, sizeof(event));
|
||||
if (nread < 0) {
|
||||
perror("read");
|
||||
return 1;
|
||||
}
|
||||
if (nread == 0)
|
||||
continue;
|
||||
assert(nread == sizeof(event));
|
||||
dbgprintf("(Terminal:%d) ", getpid());
|
||||
switch (event.type) {
|
||||
case GUI_Event::Type::Paint: dbgprintf("WID=%x Paint [%d,%d %dx%d]\n", event.window_id, event.paint.rect.location.x, event.paint.rect.location.y, event.paint.rect.size.width, event.paint.rect.size.height); break;
|
||||
case GUI_Event::Type::MouseDown: dbgprintf("WID=%x MouseDown %d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y); break;
|
||||
case GUI_Event::Type::MouseUp: dbgprintf("WID=%x MouseUp %d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y); break;
|
||||
case GUI_Event::Type::MouseMove: dbgprintf("WID=%x MouseMove %d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y); break;
|
||||
case GUI_Event::Type::KeyDown: dbgprintf("WID=%x KeyDown 0x%b (%c)\n", event.window_id, event.key.character, event.key.character); break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
if (FD_ISSET(event_fd, &rfds)) {
|
||||
GUI_Event event;
|
||||
ssize_t nread = read(event_fd, &event, sizeof(event));
|
||||
if (nread < 0) {
|
||||
perror("read(event)");
|
||||
return 1;
|
||||
}
|
||||
assert(nread != 0);
|
||||
assert(nread == sizeof(event));
|
||||
dbgprintf("(Terminal:%d) ", getpid());
|
||||
switch (event.type) {
|
||||
case GUI_Event::Type::Paint: dbgprintf("WID=%x Paint [%d,%d %dx%d]\n", event.window_id, event.paint.rect.location.x, event.paint.rect.location.y, event.paint.rect.size.width, event.paint.rect.size.height); break;
|
||||
case GUI_Event::Type::MouseDown: dbgprintf("WID=%x MouseDown %d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y); break;
|
||||
case GUI_Event::Type::MouseUp: dbgprintf("WID=%x MouseUp %d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y); break;
|
||||
case GUI_Event::Type::MouseMove: dbgprintf("WID=%x MouseMove %d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y); break;
|
||||
case GUI_Event::Type::KeyDown: dbgprintf("WID=%x KeyDown 0x%b (%c)\n", event.window_id, event.key.character, event.key.character); break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
if (event.type == GUI_Event::Type::Paint) {
|
||||
terminal.paint();
|
||||
} else if (event.type == GUI_Event::Type::KeyDown) {
|
||||
write(ptm_fd, &event.key.character, 1);
|
||||
if (event.type == GUI_Event::Type::Paint) {
|
||||
terminal.paint();
|
||||
} else if (event.type == GUI_Event::Type::KeyDown) {
|
||||
write(ptm_fd, &event.key.character, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
extern "C" {
|
||||
|
||||
#define FD_SETSIZE 64
|
||||
struct fd_set { unsigned char bits[FD_SETSIZE / 8]; };
|
||||
|
||||
namespace Unix {
|
||||
|
||||
#define WNOHANG 1
|
||||
|
|
Loading…
Add table
Reference in a new issue