mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 13:48:12 +00:00
Port Terminal to LibGUI.
To facilitate listening for action on arbitrary file descriptors, I've added a GNotifier class. It's quite simple but very useful: GNotifier notifier(fd, GNotifier::Read); notifier.on_ready_to_read = [this] (GNotifier& fd) { // read from fd or whatever else you like :^) }; The callback will get invoked by GEventLoop when select() says we have something to read on the fd.
This commit is contained in:
parent
ae4811fbae
commit
53d34a0885
15 changed files with 268 additions and 151 deletions
|
@ -23,7 +23,7 @@ LDFLAGS = -static --strip-debug -melf_i386 -e _start --gc-sections
|
|||
all: $(APP)
|
||||
|
||||
$(APP): $(OBJS)
|
||||
$(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../../LibC/LibC.a
|
||||
$(LD) -o $(APP) $(LDFLAGS) $(OBJS) ../../LibGUI/LibGUI.a ../../LibC/LibC.a
|
||||
|
||||
.cpp.o:
|
||||
@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $<
|
||||
|
|
|
@ -8,40 +8,17 @@
|
|||
#include <LibC/stdlib.h>
|
||||
#include <LibC/unistd.h>
|
||||
#include <LibC/stdio.h>
|
||||
#include <LibC/gui.h>
|
||||
#include <LibGUI/GWindow.h>
|
||||
#include <Kernel/KeyCode.h>
|
||||
|
||||
//#define TERMINAL_DEBUG
|
||||
|
||||
void Terminal::create_window()
|
||||
Terminal::Terminal(int ptm_fd)
|
||||
: m_ptm_fd(ptm_fd)
|
||||
, m_font(Font::default_font())
|
||||
{
|
||||
m_pixel_width = m_columns * font().glyph_width() + m_inset * 2;
|
||||
m_pixel_height = (m_rows * (font().glyph_height() + m_line_spacing)) + (m_inset * 2) - m_line_spacing;
|
||||
set_fill_with_background_color(false);
|
||||
|
||||
GUI_WindowParameters params;
|
||||
params.rect = { { 300, 300 }, { m_pixel_width, m_pixel_height } };
|
||||
params.background_color = 0x000000;
|
||||
strcpy(params.title, "Terminal");
|
||||
m_window_id = gui_create_window(¶ms);
|
||||
ASSERT(m_window_id > 0);
|
||||
if (m_window_id < 0) {
|
||||
perror("gui_create_window");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// NOTE: We never release the backing store.
|
||||
GUI_WindowBackingStoreInfo info;
|
||||
int rc = gui_get_window_backing_store(m_window_id, &info);
|
||||
if (rc < 0) {
|
||||
perror("gui_get_window_backing_store");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
m_backing = GraphicsBitmap::create_wrapper(info.size, info.pixels);
|
||||
}
|
||||
|
||||
Terminal::Terminal()
|
||||
: m_font(Font::default_font())
|
||||
{
|
||||
m_line_height = font().glyph_height() + m_line_spacing;
|
||||
|
||||
set_size(80, 25);
|
||||
|
@ -54,6 +31,12 @@ Terminal::Terminal()
|
|||
m_lines = new Line*[rows()];
|
||||
for (size_t i = 0; i < rows(); ++i)
|
||||
m_lines[i] = new Line(columns());
|
||||
|
||||
m_pixel_width = m_columns * font().glyph_width() + m_inset * 2;
|
||||
m_pixel_height = (m_rows * (font().glyph_height() + m_line_spacing)) + (m_inset * 2) - m_line_spacing;
|
||||
|
||||
set_size_policy(SizePolicy::Fixed, SizePolicy::Fixed);
|
||||
set_preferred_size({ m_pixel_width, m_pixel_height });
|
||||
}
|
||||
|
||||
Terminal::Line::Line(word columns)
|
||||
|
@ -61,7 +44,6 @@ Terminal::Line::Line(word columns)
|
|||
{
|
||||
characters = new byte[length];
|
||||
attributes = new Attribute[length];
|
||||
did_paint = false;
|
||||
memset(characters, ' ', length);
|
||||
}
|
||||
|
||||
|
@ -623,13 +605,49 @@ bool Terminal::Line::has_only_one_background_color() const
|
|||
return true;
|
||||
}
|
||||
|
||||
void Terminal::paint()
|
||||
void Terminal::event(GEvent& event)
|
||||
{
|
||||
if (event.type() == GEvent::WindowBecameActive || event.type() == GEvent::WindowBecameInactive) {
|
||||
m_in_active_window = event.type() == GEvent::WindowBecameActive;
|
||||
invalidate_cursor();
|
||||
update();
|
||||
}
|
||||
return GWidget::event(event);
|
||||
}
|
||||
|
||||
void Terminal::keydown_event(GKeyEvent& event)
|
||||
{
|
||||
char ch = !event.text().is_empty() ? event.text()[0] : 0;
|
||||
if (event.ctrl()) {
|
||||
if (ch >= 'a' && ch <= 'z') {
|
||||
ch = ch - 'a' + 1;
|
||||
} else if (ch == '\\') {
|
||||
ch = 0x1c;
|
||||
}
|
||||
}
|
||||
switch (event.key()) {
|
||||
case KeyCode::Key_Up:
|
||||
write(m_ptm_fd, "\033[A", 3);
|
||||
break;
|
||||
case KeyCode::Key_Down:
|
||||
write(m_ptm_fd, "\033[B", 3);
|
||||
break;
|
||||
case KeyCode::Key_Right:
|
||||
write(m_ptm_fd, "\033[C", 3);
|
||||
break;
|
||||
case KeyCode::Key_Left:
|
||||
write(m_ptm_fd, "\033[D", 3);
|
||||
break;
|
||||
default:
|
||||
write(m_ptm_fd, &ch, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Terminal::paint_event(GPaintEvent&)
|
||||
{
|
||||
Rect rect { 0, 0, m_pixel_width, m_pixel_height };
|
||||
Painter painter(*m_backing);
|
||||
|
||||
for (size_t i = 0; i < rows(); ++i)
|
||||
line(i).did_paint = false;
|
||||
Painter painter(*this);
|
||||
|
||||
if (m_rows_to_scroll_backing_store && m_rows_to_scroll_backing_store < m_rows) {
|
||||
int first_scanline = m_inset;
|
||||
|
@ -637,11 +655,11 @@ void Terminal::paint()
|
|||
int num_rows_to_memcpy = m_rows - m_rows_to_scroll_backing_store;
|
||||
int scanlines_to_copy = (num_rows_to_memcpy * m_line_height) - m_line_spacing;
|
||||
fast_dword_copy(
|
||||
m_backing->scanline(first_scanline),
|
||||
m_backing->scanline(second_scanline),
|
||||
painter.target()->scanline(first_scanline),
|
||||
painter.target()->scanline(second_scanline),
|
||||
scanlines_to_copy * m_pixel_width
|
||||
);
|
||||
m_need_full_invalidation = true;
|
||||
m_need_full_flush = true;
|
||||
line(max(0, m_cursor_row - m_rows_to_scroll_backing_store)).dirty = true;
|
||||
}
|
||||
m_rows_to_scroll_backing_store = 0;
|
||||
|
@ -660,7 +678,6 @@ void Terminal::paint()
|
|||
for (word column = 0; column < m_columns; ++column) {
|
||||
bool should_reverse_fill_for_cursor = m_in_active_window && row == m_cursor_row && column == m_cursor_column;
|
||||
auto& attribute = line.attributes[column];
|
||||
line.did_paint = true;
|
||||
char ch = line.characters[column];
|
||||
auto character_rect = glyph_rect(row, column);
|
||||
if (!has_only_one_background_color || should_reverse_fill_for_cursor) {
|
||||
|
@ -678,71 +695,34 @@ void Terminal::paint()
|
|||
painter.draw_rect(cell_rect, lookup_color(line(m_cursor_row).attributes[m_cursor_column].foreground_color));
|
||||
}
|
||||
|
||||
line(m_cursor_row).did_paint = true;
|
||||
|
||||
if (m_belling) {
|
||||
m_need_full_invalidation = true;
|
||||
if (m_belling)
|
||||
painter.draw_rect(rect, Color::Red);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_need_full_invalidation) {
|
||||
did_paint();
|
||||
m_need_full_invalidation = false;
|
||||
void Terminal::set_window_title(String&& title)
|
||||
{
|
||||
auto* w = window();
|
||||
if (!w)
|
||||
return;
|
||||
}
|
||||
|
||||
Rect painted_rect;
|
||||
for (int i = 0; i < m_rows; ++i) {
|
||||
if (line(i).did_paint)
|
||||
painted_rect = painted_rect.united(row_rect(i));
|
||||
}
|
||||
did_paint(painted_rect);
|
||||
}
|
||||
|
||||
void Terminal::did_paint(const Rect& a_rect)
|
||||
{
|
||||
GUI_Rect rect = a_rect;
|
||||
int rc = gui_notify_paint_finished(m_window_id, a_rect.is_null() ? nullptr : &rect);
|
||||
if (rc < 0) {
|
||||
perror("gui_notify_paint_finished");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void Terminal::update()
|
||||
{
|
||||
Rect rect;
|
||||
for (int i = 0; i < m_rows; ++i) {
|
||||
if (line(i).did_paint)
|
||||
rect = rect.united(row_rect(i));
|
||||
}
|
||||
GUI_Rect gui_rect = rect;
|
||||
int rc = gui_invalidate_window(m_window_id, rect.is_null() ? nullptr : &gui_rect);
|
||||
if (rc < 0) {
|
||||
perror("gui_invalidate_window");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void Terminal::set_window_title(const String& title)
|
||||
{
|
||||
int rc = gui_set_window_title(m_window_id, title.characters(), title.length());
|
||||
if (rc < 0) {
|
||||
perror("gui_set_window_title");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void Terminal::set_in_active_window(bool b)
|
||||
{
|
||||
if (m_in_active_window == b)
|
||||
return;
|
||||
m_in_active_window = b;
|
||||
invalidate_cursor();
|
||||
update();
|
||||
w->set_title(move(title));
|
||||
}
|
||||
|
||||
void Terminal::invalidate_cursor()
|
||||
{
|
||||
line(m_cursor_row).dirty = true;
|
||||
}
|
||||
|
||||
void Terminal::flush_dirty_lines()
|
||||
{
|
||||
if (m_need_full_flush) {
|
||||
update();
|
||||
m_need_full_flush = false;
|
||||
return;
|
||||
}
|
||||
Rect rect;
|
||||
for (int i = 0; i < m_rows; ++i) {
|
||||
if (line(i).dirty)
|
||||
rect = rect.united(row_rect(i));
|
||||
}
|
||||
update(rect);
|
||||
}
|
||||
|
|
|
@ -5,31 +5,33 @@
|
|||
#include <AK/Vector.h>
|
||||
#include <SharedGraphics/GraphicsBitmap.h>
|
||||
#include <SharedGraphics/Rect.h>
|
||||
#include <LibGUI/GWidget.h>
|
||||
|
||||
class Font;
|
||||
|
||||
class Terminal {
|
||||
class Terminal final : public GWidget {
|
||||
public:
|
||||
Terminal();
|
||||
~Terminal();
|
||||
explicit Terminal(int ptm_fd);
|
||||
virtual ~Terminal() override;
|
||||
|
||||
void create_window();
|
||||
void paint();
|
||||
void on_char(byte);
|
||||
|
||||
void set_in_active_window(bool);
|
||||
void update();
|
||||
void flush_dirty_lines();
|
||||
|
||||
private:
|
||||
virtual void event(GEvent&) override;
|
||||
virtual void paint_event(GPaintEvent&) override;
|
||||
virtual void keydown_event(GKeyEvent&) override;
|
||||
virtual const char* class_name() const override { return "Terminal"; }
|
||||
|
||||
Font& font() { return *m_font; }
|
||||
void scroll_up();
|
||||
void newline();
|
||||
void set_cursor(unsigned row, unsigned column);
|
||||
void put_character_at(unsigned row, unsigned column, byte ch);
|
||||
void invalidate_cursor();
|
||||
void did_paint(const Rect& = Rect());
|
||||
void invalidate_window(const Rect& = Rect());
|
||||
void set_window_title(const String&);
|
||||
void set_window_title(String&&);
|
||||
|
||||
void inject_string(const String&);
|
||||
void unimplemented_escape();
|
||||
|
@ -83,7 +85,6 @@ private:
|
|||
bool has_only_one_background_color() const;
|
||||
byte* characters { nullptr };
|
||||
Attribute* attributes { nullptr };
|
||||
bool did_paint { false };
|
||||
bool dirty { false };
|
||||
word length { 0 };
|
||||
};
|
||||
|
@ -125,9 +126,6 @@ private:
|
|||
byte* m_horizontal_tabs { nullptr };
|
||||
bool m_belling { false };
|
||||
|
||||
int m_window_id { 0 };
|
||||
RetainPtr<GraphicsBitmap> m_backing;
|
||||
|
||||
int m_pixel_width { 0 };
|
||||
int m_pixel_height { 0 };
|
||||
int m_rows_to_scroll_backing_store { 0 };
|
||||
|
@ -136,8 +134,10 @@ private:
|
|||
int m_line_spacing { 4 };
|
||||
int m_line_height { 0 };
|
||||
|
||||
int m_ptm_fd { -1 };
|
||||
|
||||
bool m_in_active_window { false };
|
||||
bool m_need_full_invalidation { false };
|
||||
bool m_need_full_flush { false };
|
||||
|
||||
RetainPtr<Font> m_font;
|
||||
};
|
||||
|
|
|
@ -5,14 +5,15 @@
|
|||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
#include <SharedGraphics/Font.h>
|
||||
#include <SharedGraphics/GraphicsBitmap.h>
|
||||
#include <SharedGraphics/Painter.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/select.h>
|
||||
#include <LibC/gui.h>
|
||||
#include "Terminal.h"
|
||||
#include <Kernel/KeyCode.h>
|
||||
#include <LibGUI/GEventLoop.h>
|
||||
#include <LibGUI/GNotifier.h>
|
||||
#include <LibGUI/GWidget.h>
|
||||
#include <LibGUI/GWindow.h>
|
||||
|
||||
static void make_shell(int ptm_fd)
|
||||
{
|
||||
|
@ -52,11 +53,6 @@ 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/ptmx", O_RDWR);
|
||||
|
@ -67,16 +63,39 @@ int main(int, char**)
|
|||
|
||||
make_shell(ptm_fd);
|
||||
|
||||
int event_fd = open("/dev/gui_events", O_RDONLY);
|
||||
if (event_fd < 0) {
|
||||
perror("open");
|
||||
return 1;
|
||||
}
|
||||
GEventLoop loop;
|
||||
|
||||
Terminal terminal;
|
||||
terminal.create_window();
|
||||
terminal.update();
|
||||
auto* window = new GWindow;
|
||||
window->set_should_exit_app_on_close(true);
|
||||
|
||||
Terminal terminal(ptm_fd);
|
||||
window->set_main_widget(&terminal);
|
||||
|
||||
GNotifier ptm_notifier(ptm_fd, GNotifier::Read);
|
||||
ptm_notifier.on_ready_to_read = [&terminal] (GNotifier& notifier) {
|
||||
byte buffer[BUFSIZ];
|
||||
ssize_t nread = read(notifier.fd(), buffer, sizeof(buffer));
|
||||
if (nread < 0) {
|
||||
dbgprintf("Terminal read error: %s\n", strerror(errno));
|
||||
perror("read(ptm)");
|
||||
GEventLoop::main().exit(1);
|
||||
return;
|
||||
}
|
||||
if (nread == 0) {
|
||||
dbgprintf("Terminal: EOF on master pty, closing.\n");
|
||||
GEventLoop::main().exit(0);
|
||||
return;
|
||||
}
|
||||
for (ssize_t i = 0; i < nread; ++i)
|
||||
terminal.on_char(buffer[i]);
|
||||
terminal.flush_dirty_lines();
|
||||
};
|
||||
|
||||
window->show();
|
||||
|
||||
return loop.exec();
|
||||
|
||||
#if 0
|
||||
for (;;) {
|
||||
fd_set rfds;
|
||||
FD_ZERO(&rfds);
|
||||
|
@ -112,11 +131,12 @@ int main(int, char**)
|
|||
perror("read(event)");
|
||||
return 1;
|
||||
}
|
||||
|
||||
assert(nread != 0);
|
||||
assert(nread == sizeof(event));
|
||||
|
||||
if (event.type == GUI_Event::Type::Paint) {
|
||||
terminal.paint();
|
||||
terminal.update();
|
||||
} else if (event.type == GUI_Event::Type::KeyDown) {
|
||||
char ch = event.key.character;
|
||||
if (event.key.ctrl) {
|
||||
|
@ -151,5 +171,6 @@ int main(int, char**)
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "GEvent.h"
|
||||
#include "GObject.h"
|
||||
#include "GWindow.h"
|
||||
#include <LibGUI/GNotifier.h>
|
||||
#include <LibC/unistd.h>
|
||||
#include <LibC/stdio.h>
|
||||
#include <LibC/fcntl.h>
|
||||
|
@ -60,7 +61,7 @@ int GEventLoop::exec()
|
|||
auto* receiver = queued_event.receiver;
|
||||
auto& event = *queued_event.event;
|
||||
#ifdef GEVENTLOOP_DEBUG
|
||||
dbgprintf("GEventLoop: GObject{%p} event %u (%s)\n", receiver, (unsigned)event.type(), event.name());
|
||||
dbgprintf("GEventLoop: %s{%p} event %u\n", receiver->class_name(), receiver, (unsigned)event.type());
|
||||
#endif
|
||||
if (!receiver) {
|
||||
switch (event.type()) {
|
||||
|
@ -149,12 +150,31 @@ void GEventLoop::handle_mouse_event(const GUI_Event& event, GWindow& window)
|
|||
void GEventLoop::wait_for_event()
|
||||
{
|
||||
fd_set rfds;
|
||||
fd_set wfds;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(m_event_fd, &rfds);
|
||||
FD_ZERO(&wfds);
|
||||
|
||||
int max_fd = 0;
|
||||
auto add_fd_to_set = [&max_fd] (int fd, fd_set& set){
|
||||
FD_SET(fd, &set);
|
||||
if (fd > max_fd)
|
||||
max_fd = fd;
|
||||
};
|
||||
|
||||
add_fd_to_set(m_event_fd, rfds);
|
||||
for (auto& notifier : m_notifiers) {
|
||||
if (notifier->event_mask() & GNotifier::Read)
|
||||
add_fd_to_set(notifier->fd(), rfds);
|
||||
if (notifier->event_mask() & GNotifier::Write)
|
||||
add_fd_to_set(notifier->fd(), wfds);
|
||||
if (notifier->event_mask() & GNotifier::Exceptional)
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
struct timeval timeout = { 0, 0 };
|
||||
if (!m_timers.is_empty())
|
||||
get_next_timer_expiration(timeout);
|
||||
int rc = select(m_event_fd + 1, &rfds, nullptr, nullptr, (m_queued_events.is_empty() && m_timers.is_empty()) ? nullptr : &timeout);
|
||||
int rc = select(m_event_fd + 1, &rfds, &wfds, nullptr, (m_queued_events.is_empty() && m_timers.is_empty()) ? nullptr : &timeout);
|
||||
if (rc < 0) {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
@ -175,6 +195,17 @@ void GEventLoop::wait_for_event()
|
|||
}
|
||||
}
|
||||
|
||||
for (auto& notifier : m_notifiers) {
|
||||
if (FD_ISSET(notifier->fd(), &rfds)) {
|
||||
if (notifier->on_ready_to_read)
|
||||
notifier->on_ready_to_read(*notifier);
|
||||
}
|
||||
if (FD_ISSET(notifier->fd(), &wfds)) {
|
||||
if (notifier->on_ready_to_write)
|
||||
notifier->on_ready_to_write(*notifier);
|
||||
}
|
||||
}
|
||||
|
||||
if (!FD_ISSET(m_event_fd, &rfds))
|
||||
return;
|
||||
|
||||
|
@ -266,3 +297,13 @@ bool GEventLoop::unregister_timer(int timer_id)
|
|||
m_timers.remove(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GEventLoop::register_notifier(Badge<GNotifier>, GNotifier& notifier)
|
||||
{
|
||||
m_notifiers.set(¬ifier);
|
||||
}
|
||||
|
||||
void GEventLoop::unregister_notifier(Badge<GNotifier>, GNotifier& notifier)
|
||||
{
|
||||
m_notifiers.remove(¬ifier);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "GEvent.h"
|
||||
#include <AK/Badge.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/Vector.h>
|
||||
|
||||
class GObject;
|
||||
class GNotifier;
|
||||
class GWindow;
|
||||
struct GUI_Event;
|
||||
|
||||
|
@ -27,6 +29,9 @@ public:
|
|||
int register_timer(GObject&, int milliseconds, bool should_reload);
|
||||
bool unregister_timer(int timer_id);
|
||||
|
||||
void register_notifier(Badge<GNotifier>, GNotifier&);
|
||||
void unregister_notifier(Badge<GNotifier>, GNotifier&);
|
||||
|
||||
void exit(int);
|
||||
|
||||
private:
|
||||
|
@ -64,4 +69,5 @@ private:
|
|||
};
|
||||
|
||||
HashMap<int, OwnPtr<EventLoopTimer>> m_timers;
|
||||
HashTable<GNotifier*> m_notifiers;
|
||||
};
|
||||
|
|
15
LibGUI/GNotifier.cpp
Normal file
15
LibGUI/GNotifier.cpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
#include <LibGUI/GNotifier.h>
|
||||
#include <LibGUI/GEventLoop.h>
|
||||
|
||||
GNotifier::GNotifier(int fd, unsigned event_mask)
|
||||
: m_fd(fd)
|
||||
, m_event_mask(event_mask)
|
||||
{
|
||||
GEventLoop::main().register_notifier(Badge<GNotifier>(), *this);
|
||||
}
|
||||
|
||||
GNotifier::~GNotifier()
|
||||
{
|
||||
GEventLoop::main().unregister_notifier(Badge<GNotifier>(), *this);
|
||||
}
|
||||
|
25
LibGUI/GNotifier.h
Normal file
25
LibGUI/GNotifier.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Function.h>
|
||||
|
||||
class GNotifier {
|
||||
public:
|
||||
enum Event {
|
||||
None = 0,
|
||||
Read = 1,
|
||||
Write = 2,
|
||||
Exceptional = 4,
|
||||
};
|
||||
GNotifier(int fd, unsigned event_mask);
|
||||
~GNotifier();
|
||||
|
||||
Function<void(GNotifier&)> on_ready_to_read;
|
||||
Function<void(GNotifier&)> on_ready_to_write;
|
||||
|
||||
int fd() const { return m_fd; }
|
||||
unsigned event_mask() const { return m_event_mask; }
|
||||
|
||||
private:
|
||||
int m_fd { -1 };
|
||||
unsigned m_event_mask { 0 };
|
||||
};
|
|
@ -34,16 +34,11 @@ void GWidget::set_relative_rect(const Rect& rect)
|
|||
update();
|
||||
}
|
||||
|
||||
void GWidget::repaint(const Rect& rect)
|
||||
{
|
||||
// FIXME: Implement.
|
||||
}
|
||||
|
||||
void GWidget::event(GEvent& event)
|
||||
{
|
||||
switch (event.type()) {
|
||||
case GEvent::Paint:
|
||||
m_has_pending_paint_event = false;
|
||||
m_pending_paint_event_rects.clear();
|
||||
return handle_paint_event(static_cast<GPaintEvent&>(event));
|
||||
case GEvent::Resize:
|
||||
return handle_resize_event(static_cast<GResizeEvent&>(event));
|
||||
|
@ -171,14 +166,21 @@ void GWidget::focusout_event(GEvent&)
|
|||
}
|
||||
|
||||
void GWidget::update()
|
||||
{
|
||||
update(rect());
|
||||
}
|
||||
|
||||
void GWidget::update(const Rect& rect)
|
||||
{
|
||||
auto* w = window();
|
||||
if (!w)
|
||||
return;
|
||||
if (m_has_pending_paint_event)
|
||||
return;
|
||||
m_has_pending_paint_event = true;
|
||||
w->update(window_relative_rect());
|
||||
for (auto& pending_rect : m_pending_paint_event_rects) {
|
||||
if (pending_rect.contains(rect))
|
||||
return;
|
||||
}
|
||||
m_pending_paint_event_rects.append(rect);
|
||||
w->update(rect.translated(window_relative_rect().location()));
|
||||
}
|
||||
|
||||
Rect GWidget::window_relative_rect() const
|
||||
|
|
|
@ -58,7 +58,7 @@ public:
|
|||
Size size() const { return m_relative_rect.size(); }
|
||||
|
||||
void update();
|
||||
void repaint(const Rect&);
|
||||
void update(const Rect&);
|
||||
|
||||
virtual bool accepts_focus() const { return false; }
|
||||
|
||||
|
@ -136,6 +136,7 @@ private:
|
|||
SizePolicy m_vertical_size_policy { SizePolicy::Fill };
|
||||
Size m_preferred_size;
|
||||
|
||||
bool m_has_pending_paint_event { false };
|
||||
Vector<Rect> m_pending_paint_event_rects;
|
||||
|
||||
bool m_fill_with_background_color { true };
|
||||
};
|
||||
|
|
|
@ -163,13 +163,17 @@ void GWindow::event(GEvent& event)
|
|||
}
|
||||
|
||||
if (event.is_key_event()) {
|
||||
if (!m_focused_widget)
|
||||
return;
|
||||
return m_focused_widget->event(event);
|
||||
if (m_focused_widget)
|
||||
return m_focused_widget->event(event);
|
||||
if (m_main_widget)
|
||||
return m_main_widget->event(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.type() == GEvent::WindowBecameActive || event.type() == GEvent::WindowBecameInactive) {
|
||||
m_is_active = event.type() == GEvent::WindowBecameActive;
|
||||
if (m_main_widget)
|
||||
m_main_widget->event(event);
|
||||
if (m_focused_widget)
|
||||
m_focused_widget->update();
|
||||
return;
|
||||
|
@ -202,9 +206,18 @@ void GWindow::set_main_widget(GWidget* widget)
|
|||
if (m_main_widget == widget)
|
||||
return;
|
||||
m_main_widget = widget;
|
||||
m_main_widget->set_relative_rect({ 0, 0, width(), height() });
|
||||
if (widget)
|
||||
widget->set_window(this);
|
||||
if (m_main_widget) {
|
||||
auto new_window_rect = rect();
|
||||
if (m_main_widget->horizontal_size_policy() == SizePolicy::Fixed)
|
||||
new_window_rect.set_width(m_main_widget->preferred_size().width());
|
||||
if (m_main_widget->vertical_size_policy() == SizePolicy::Fixed)
|
||||
new_window_rect.set_height(m_main_widget->preferred_size().height());
|
||||
set_rect(new_window_rect);
|
||||
m_main_widget->set_relative_rect({ { }, new_window_rect.size() });
|
||||
m_main_widget->set_window(this);
|
||||
if (m_main_widget->accepts_focus())
|
||||
m_main_widget->set_focus(true);
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ public:
|
|||
int height() const { return rect().height(); }
|
||||
|
||||
Rect rect() const;
|
||||
Size size() const { return rect().size(); }
|
||||
void set_rect(const Rect&);
|
||||
void set_rect(int x, int y, int width, int height) { set_rect({ x, y, width, height }); }
|
||||
|
||||
|
@ -58,6 +59,8 @@ public:
|
|||
void set_should_exit_app_on_close(bool b) { m_should_exit_app_on_close = b; }
|
||||
|
||||
private:
|
||||
virtual const char* class_name() const override { return "GWindow"; }
|
||||
|
||||
RetainPtr<GraphicsBitmap> m_backing;
|
||||
int m_window_id { 0 };
|
||||
bool m_is_active { false };
|
||||
|
|
|
@ -13,6 +13,7 @@ LIBGUI_OBJS = \
|
|||
GLabel.o \
|
||||
GListBox.o \
|
||||
GObject.o \
|
||||
GNotifier.o \
|
||||
GTextBox.o \
|
||||
GScrollBar.o \
|
||||
GStatusBar.o \
|
||||
|
|
|
@ -52,6 +52,8 @@ public:
|
|||
void translate(int dx, int dy) { m_translation.move_by(dx, dy); }
|
||||
void translate(const Point& delta) { m_translation.move_by(delta); }
|
||||
|
||||
GraphicsBitmap* target() { return m_target.ptr(); }
|
||||
|
||||
private:
|
||||
void set_pixel_with_draw_op(dword& pixel, const Color&);
|
||||
void fill_rect_with_draw_op(const Rect&, Color);
|
||||
|
|
|
@ -99,6 +99,13 @@ public:
|
|||
return rect;
|
||||
}
|
||||
|
||||
Rect translated(const Point& delta) const
|
||||
{
|
||||
Rect rect = *this;
|
||||
rect.move_by(delta);
|
||||
return rect;
|
||||
}
|
||||
|
||||
bool contains(int x, int y) const
|
||||
{
|
||||
return x >= m_location.x() && x <= right() && y >= m_location.y() && y <= bottom();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue