diff --git a/Widgets/AbstractScreen.cpp b/Widgets/AbstractScreen.cpp new file mode 100644 index 0000000000..d9216de1e1 --- /dev/null +++ b/Widgets/AbstractScreen.cpp @@ -0,0 +1,35 @@ +#include "AbstractScreen.h" +#include "EventLoop.h" +#include "Event.h" +#include "Widget.h" +#include + +static AbstractScreen* s_the; + +AbstractScreen& AbstractScreen::the() +{ + ASSERT(s_the); + return *s_the; +} + +AbstractScreen::AbstractScreen(unsigned width, unsigned height) + : m_width(width) + , m_height(height) +{ + ASSERT(!s_the); + s_the = this; +} + +AbstractScreen::~AbstractScreen() +{ +} + +void AbstractScreen::setRootWidget(Widget* widget) +{ + // FIXME: Should we support switching root widgets? + ASSERT(!m_rootWidget); + ASSERT(widget); + + m_rootWidget = widget; + EventLoop::main().postEvent(m_rootWidget, make()); +} diff --git a/Widgets/AbstractScreen.h b/Widgets/AbstractScreen.h new file mode 100644 index 0000000000..a607221777 --- /dev/null +++ b/Widgets/AbstractScreen.h @@ -0,0 +1,25 @@ +#pragma once + +class Widget; + +class AbstractScreen { +public: + virtual ~AbstractScreen(); + + unsigned width() const { return m_width; } + unsigned height() const { return m_height; } + + void setRootWidget(Widget*); + + static AbstractScreen& the(); + +protected: + AbstractScreen(unsigned width, unsigned height); + +private: + unsigned m_width { 0 }; + unsigned m_height { 0 }; + + Widget* m_rootWidget { nullptr }; +}; + diff --git a/Widgets/Event.h b/Widgets/Event.h new file mode 100644 index 0000000000..28297a9b6c --- /dev/null +++ b/Widgets/Event.h @@ -0,0 +1,118 @@ +#pragma once + +#include + +static const char* eventNames[] = { + "Invalid", + "Quit", + "Show", + "Hide", + "Paint", + "MouseMove", + "MouseDown", + "MouseUp", + "KeyDown", + "KeyUp", +}; + +class Event { +public: + enum Type { + Invalid = 0, + Quit, + Show, + Hide, + Paint, + MouseMove, + MouseDown, + MouseUp, + KeyDown, + KeyUp, + }; + + Event() { } + ~Event() { } + + Type type() const { return m_type; } + + const char* name() const { return eventNames[(unsigned)m_type]; } + +protected: + explicit Event(Type type) : m_type(type) { } + +private: + Type m_type { Invalid }; +}; + +class QuitEvent final : public Event { +public: + QuitEvent() + : Event(Event::Quit) + { + } +}; + +class PaintEvent final : public Event { +public: + PaintEvent() + : Event(Event::Paint) + { + } +}; + +class ShowEvent final : public Event { +public: + ShowEvent() + : Event(Event::Show) + { + } +}; + +class HideEvent final : public Event { +public: + HideEvent() + : Event(Event::Hide) + { + } +}; + +enum class MouseButton : byte { + None = 0, + Left, + Right, +}; + +class KeyEvent : public Event { +public: + KeyEvent(Type type, int key) + : Event(type) + , m_key(key) + { + } + + int key() const { return m_key; } + +private: + int m_key { 0 }; +}; + +class MouseEvent : public Event { +public: + MouseEvent(Type type, int x, int y, MouseButton button = MouseButton::None) + : Event(type) + , m_x(x) + , m_y(y) + , m_button(button) + { + } + + int x() const { return m_x; } + int y() const { return m_y; } + MouseButton button() const { return m_button; } + +private: + int m_x { 0 }; + int m_y { 0 }; + MouseButton m_button { MouseButton::None }; +}; + diff --git a/Widgets/EventLoop.cpp b/Widgets/EventLoop.cpp new file mode 100644 index 0000000000..aa227eb99b --- /dev/null +++ b/Widgets/EventLoop.cpp @@ -0,0 +1,52 @@ +#include "EventLoop.h" +#include "Event.h" +#include "Object.h" + +static EventLoop* s_mainEventLoop; + +EventLoop::EventLoop() +{ + if (!s_mainEventLoop) + s_mainEventLoop = this; +} + +EventLoop::~EventLoop() +{ +} + +EventLoop& EventLoop::main() +{ + ASSERT(s_mainEventLoop); + return *s_mainEventLoop; +} + +int EventLoop::exec() +{ + for (;;) { + if (m_queuedEvents.isEmpty()) + waitForEvent(); + auto events = std::move(m_queuedEvents); + for (auto& queuedEvent : events) { + auto* receiver = queuedEvent.receiver; + auto& event = *queuedEvent.event; + printf("EventLoop: Object{%p} event %u (%s)\n", receiver, (unsigned)event.type(), event.name()); + if (!receiver) { + switch (event.type()) { + case Event::Quit: + return 0; + default: + printf("event type %u with no receiver :(\n", event.type()); + return 1; + } + } else { + receiver->event(event); + } + } + } +} + +void EventLoop::postEvent(Object* receiver, OwnPtr&& event) +{ + m_queuedEvents.append({ receiver, std::move(event) }); +} + diff --git a/Widgets/EventLoop.h b/Widgets/EventLoop.h new file mode 100644 index 0000000000..ca8b912bc0 --- /dev/null +++ b/Widgets/EventLoop.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +class Event; +class Object; + +class EventLoop { +public: + virtual ~EventLoop(); + + int exec(); + + virtual void waitForEvent() = 0; + + void postEvent(Object* receiver, OwnPtr&&); + + static EventLoop& main(); + +protected: + EventLoop(); + +private: + struct QueuedEvent { + Object* receiver { nullptr }; + OwnPtr event; + }; + Vector m_queuedEvents; +}; diff --git a/Widgets/EventLoopSDL.cpp b/Widgets/EventLoopSDL.cpp new file mode 100644 index 0000000000..8092a67bd0 --- /dev/null +++ b/Widgets/EventLoopSDL.cpp @@ -0,0 +1,24 @@ +#include "EventLoopSDL.h" +#include "Event.h" +#include + +EventLoopSDL::EventLoopSDL() +{ +} + +EventLoopSDL::~EventLoopSDL() +{ +} + +void EventLoopSDL::waitForEvent() +{ + SDL_Event sdlEvent; + while (SDL_WaitEvent(&sdlEvent) != 0) { + switch (sdlEvent.type) { + case SDL_QUIT: + postEvent(nullptr, make()); + return; + } + } +} + diff --git a/Widgets/EventLoopSDL.h b/Widgets/EventLoopSDL.h new file mode 100644 index 0000000000..b5ac349060 --- /dev/null +++ b/Widgets/EventLoopSDL.h @@ -0,0 +1,13 @@ +#pragma once + +#include "EventLoop.h" + +class EventLoopSDL final : public EventLoop { +public: + EventLoopSDL(); + virtual ~EventLoopSDL() override; + +private: + virtual void waitForEvent() override; +}; + diff --git a/Widgets/FrameBuffer.o b/Widgets/FrameBuffer.o new file mode 100644 index 0000000000..73b4ce2597 Binary files /dev/null and b/Widgets/FrameBuffer.o differ diff --git a/Widgets/FrameBufferSDL.cpp b/Widgets/FrameBufferSDL.cpp new file mode 100644 index 0000000000..cc43c41706 --- /dev/null +++ b/Widgets/FrameBufferSDL.cpp @@ -0,0 +1,49 @@ +#include "FrameBufferSDL.h" +#include + +FrameBufferSDL::FrameBufferSDL(unsigned width, unsigned height) + : AbstractScreen(width, height) +{ + initializeSDL(); +} + +FrameBufferSDL::~FrameBufferSDL() +{ + SDL_DestroyWindow(m_window); + m_surface = nullptr; + m_window = nullptr; + + SDL_Quit(); +} + +void FrameBufferSDL::show() +{ +} + +void FrameBufferSDL::initializeSDL() +{ + if (m_window) + return; + + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + ASSERT_NOT_REACHED(); + } + + m_window = SDL_CreateWindow( + "FrameBuffer", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + width(), + height(), + SDL_WINDOW_SHOWN); + + ASSERT(m_window); + + m_surface = SDL_GetWindowSurface(m_window); + ASSERT(m_surface); + + SDL_FillRect(m_surface, nullptr, SDL_MapRGB(m_surface->format, 0xff, 0xff, 0xff)); + + SDL_UpdateWindowSurface(m_window); +} + diff --git a/Widgets/FrameBufferSDL.h b/Widgets/FrameBufferSDL.h new file mode 100644 index 0000000000..9d58894427 --- /dev/null +++ b/Widgets/FrameBufferSDL.h @@ -0,0 +1,19 @@ +#pragma once + +#include "AbstractScreen.h" +#include + +class FrameBufferSDL final : public AbstractScreen { +public: + FrameBufferSDL(unsigned width, unsigned height); + virtual ~FrameBufferSDL() override; + + void show(); + +private: + void initializeSDL(); + + SDL_Window* m_window { nullptr }; + SDL_Surface* m_surface { nullptr }; +}; + diff --git a/Widgets/Makefile b/Widgets/Makefile new file mode 100644 index 0000000000..58c3678f0c --- /dev/null +++ b/Widgets/Makefile @@ -0,0 +1,37 @@ +PROGRAM = test + +AK_OBJS = \ + ../AK/String.o \ + ../AK/StringImpl.o \ + ../AK/SimpleMalloc.o \ + ../AK/kmalloc.o + +VFS_OBJS = \ + AbstractScreen.o \ + FrameBufferSDL.o \ + EventLoop.o \ + EventLoopSDL.o \ + Object.o \ + Widget.o \ + RootWidget.o \ + test.o + +OBJS = $(AK_OBJS) $(VFS_OBJS) + +CXXFLAGS = -std=c++17 -O0 -W -Wall -Wextra -Wconversion -I. -I.. -g `sdl2-config --cflags` + +LDFLAGS = `sdl2-config --libs` + +#test.o: BlockDevice.h FileBackedBlockDevice.h FileSystem.h Ext2FileSystem.h VirtualFileSystem.h FileHandle.h + +all: $(PROGRAM) + +.cpp.o: + $(CXX) $(CXXFLAGS) -o $@ -c $< + +clean: + rm -f $(OBJS) $(PROGRAM) + +$(PROGRAM): $(OBJS) + $(CXX) $(LDFLAGS) -o $@ $(OBJS) + diff --git a/Widgets/Object.cpp b/Widgets/Object.cpp new file mode 100644 index 0000000000..bf7fa7469b --- /dev/null +++ b/Widgets/Object.cpp @@ -0,0 +1,23 @@ +#include "Object.h" +#include "Event.h" +#include + +Object::Object(Object* parent) + : m_parent(parent) +{ +} + +Object::~Object() +{ +} + +void Object::event(Event& event) +{ + switch (event.type()) { + case Event::Invalid: + ASSERT_NOT_REACHED(); + break; + default: + break; + } +} diff --git a/Widgets/Object.h b/Widgets/Object.h new file mode 100644 index 0000000000..4b0d0a64f9 --- /dev/null +++ b/Widgets/Object.h @@ -0,0 +1,14 @@ +#pragma once + +class Event; + +class Object { +public: + Object(Object* parent = nullptr); + virtual ~Object(); + + virtual void event(Event&); + +private: + Object* m_parent { nullptr }; +}; diff --git a/Widgets/RootWidget.cpp b/Widgets/RootWidget.cpp new file mode 100644 index 0000000000..b1b54a5682 --- /dev/null +++ b/Widgets/RootWidget.cpp @@ -0,0 +1,23 @@ +#include "RootWidget.h" +#include + +RootWidget::RootWidget() +{ +} + +RootWidget::~RootWidget() +{ +} + +void RootWidget::onPaint(PaintEvent& event) +{ + printf("RootWidget::onPaint\n"); + Widget::onPaint(event); +} + +void RootWidget::onMouseMove(MouseEvent& event) +{ + printf("RootWidget::onMouseMove: x=%d, y=%d\n", event.x(), event.y()); + Widget::onMouseMove(event); +} + diff --git a/Widgets/RootWidget.h b/Widgets/RootWidget.h new file mode 100644 index 0000000000..c025ed688d --- /dev/null +++ b/Widgets/RootWidget.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Widget.h" + +class RootWidget final : public Widget { +public: + RootWidget(); + virtual ~RootWidget() override; + +private: + virtual void onPaint(PaintEvent&) override; + virtual void onMouseMove(MouseEvent&) override; +}; diff --git a/Widgets/Widget.cpp b/Widgets/Widget.cpp new file mode 100644 index 0000000000..3a1ec8e0f8 --- /dev/null +++ b/Widgets/Widget.cpp @@ -0,0 +1,76 @@ +#include "Widget.h" +#include "Event.h" +#include "EventLoop.h" +#include + +Widget::Widget(Widget* parent) + : Object(parent) +{ +} + +Widget::~Widget() +{ +} + +void Widget::event(Event& event) +{ + switch (event.type()) { + case Event::Paint: + return onPaint(static_cast(event)); + case Event::Show: + return onShow(static_cast(event)); + case Event::Hide: + return onHide(static_cast(event)); + case Event::KeyDown: + return onKeyDown(static_cast(event)); + case Event::KeyUp: + return onKeyUp(static_cast(event)); + case Event::MouseMove: + return onMouseMove(static_cast(event)); + case Event::MouseDown: + return onMouseDown(static_cast(event)); + case Event::MouseUp: + return onMouseUp(static_cast(event)); + default: + return Object::event(event); + } +} + +void Widget::onPaint(PaintEvent&) +{ +} + +void Widget::onShow(ShowEvent&) +{ + update(); +} + +void Widget::onHide(HideEvent&) +{ +} + +void Widget::onKeyDown(KeyEvent&) +{ +} + +void Widget::onKeyUp(KeyEvent&) +{ +} + +void Widget::onMouseDown(MouseEvent&) +{ +} + +void Widget::onMouseUp(MouseEvent&) +{ +} + +void Widget::onMouseMove(MouseEvent&) +{ +} + +void Widget::update() +{ + EventLoop::main().postEvent(this, make()); +} + diff --git a/Widgets/Widget.h b/Widgets/Widget.h new file mode 100644 index 0000000000..be6966300d --- /dev/null +++ b/Widgets/Widget.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Event.h" +#include "Object.h" + +class Widget : public Object { +public: + explicit Widget(Widget* parent = nullptr); + virtual ~Widget(); + + virtual void event(Event&); + virtual void onPaint(PaintEvent&); + virtual void onShow(ShowEvent&); + virtual void onHide(HideEvent&); + virtual void onKeyDown(KeyEvent&); + virtual void onKeyUp(KeyEvent&); + virtual void onMouseMove(MouseEvent&); + virtual void onMouseDown(MouseEvent&); + virtual void onMouseUp(MouseEvent&); + + void update(); + +private: + int m_x { 0 }; + int m_y { 0 }; +}; diff --git a/Widgets/test.cpp b/Widgets/test.cpp new file mode 100644 index 0000000000..b42c7a8275 --- /dev/null +++ b/Widgets/test.cpp @@ -0,0 +1,17 @@ +#include "FrameBufferSDL.h" +#include "EventLoopSDL.h" +#include "RootWidget.h" +#include + +int main(int c, char** v) +{ + FrameBufferSDL fb(800, 600); + fb.show(); + + EventLoopSDL loop; + + RootWidget w; + fb.setRootWidget(&w); + + return loop.exec(); +}