1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 04:37:34 +00:00

Tear out or duplicate what's unique for WindowServer from Widgets.

This turned into a huge refactoring that somehow also includes
making locks recursive/reentrant.
This commit is contained in:
Andreas Kling 2019-01-16 16:03:50 +01:00
parent e655aebd70
commit f7ca6d254d
30 changed files with 757 additions and 308 deletions

1
WindowServer/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
*.o

151
WindowServer/WSEvent.h Normal file
View file

@ -0,0 +1,151 @@
#pragma once
#include <Widgets/Point.h>
#include <Widgets/Rect.h>
#include <AK/AKString.h>
#include <AK/Types.h>
static const char* WSEvent_names[] = {
"Invalid",
"Show",
"Hide",
"Paint",
"MouseMove",
"MouseDown",
"MouseUp",
"KeyDown",
"KeyUp",
"Timer",
"WM_Compose",
"WM_Invalidate",
};
class WSEvent {
public:
enum Type {
Invalid = 0,
Show,
Hide,
Paint,
MouseMove,
MouseDown,
MouseUp,
KeyDown,
KeyUp,
Timer,
WM_Compose,
WM_Invalidate,
};
WSEvent() { }
explicit WSEvent(Type type) : m_type(type) { }
virtual ~WSEvent() { }
Type type() const { return m_type; }
const char* name() const { return WSEvent_names[(unsigned)m_type]; }
bool isMouseEvent() const { return m_type == MouseMove || m_type == MouseDown || m_type == MouseUp; }
bool isKeyEvent() const { return m_type == KeyUp || m_type == KeyDown; }
bool isPaintEvent() const { return m_type == Paint; }
private:
Type m_type { Invalid };
};
class PaintEvent final : public WSEvent {
public:
explicit PaintEvent(const Rect& rect = Rect())
: WSEvent(WSEvent::Paint)
, m_rect(rect)
{
}
const Rect& rect() const { return m_rect; }
private:
friend class WSWindowManager;
Rect m_rect;
};
class ShowEvent final : public WSEvent {
public:
ShowEvent()
: WSEvent(WSEvent::Show)
{
}
};
class HideEvent final : public WSEvent {
public:
HideEvent()
: WSEvent(WSEvent::Hide)
{
}
};
enum class MouseButton : byte {
None = 0,
Left,
Middle,
Right,
};
enum KeyboardKey {
Invalid,
LeftArrow,
RightArrow,
UpArrow,
DownArrow,
Backspace,
Return,
};
class KeyEvent final : public WSEvent {
public:
KeyEvent(Type type, int key)
: WSEvent(type)
, m_key(key)
{
}
int key() const { return m_key; }
bool ctrl() const { return m_ctrl; }
bool alt() const { return m_alt; }
bool shift() const { return m_shift; }
String text() const { return m_text; }
private:
friend class WSEventLoop;
friend class WSScreen;
int m_key { 0 };
bool m_ctrl { false };
bool m_alt { false };
bool m_shift { false };
String m_text;
};
class MouseEvent final : public WSEvent {
public:
MouseEvent(Type type, int x, int y, MouseButton button = MouseButton::None)
: WSEvent(type)
, m_position(x, y)
, m_button(button)
{
}
Point position() const { return m_position; }
int x() const { return m_position.x(); }
int y() const { return m_position.y(); }
MouseButton button() const { return m_button; }
private:
Point m_position;
MouseButton m_button { MouseButton::None };
};
class TimerEvent final : public WSEvent {
public:
TimerEvent() : WSEvent(WSEvent::Timer) { }
~TimerEvent() { }
};

View file

@ -0,0 +1,98 @@
#include "WSEventLoop.h"
#include "WSEvent.h"
#include "WSEventReceiver.h"
#include "WSWindowManager.h"
#include "WSScreen.h"
#include "PS2MouseDevice.h"
#include "Scheduler.h"
//#define WSEVENTLOOP_DEBUG
static WSEventLoop* s_the;
void WSEventLoop::initialize()
{
s_the = nullptr;
}
WSEventLoop::WSEventLoop()
{
if (!s_the)
s_the = this;
}
WSEventLoop::~WSEventLoop()
{
}
WSEventLoop& WSEventLoop::the()
{
ASSERT(s_the);
return *s_the;
}
int WSEventLoop::exec()
{
m_server_process = current;
m_running = true;
for (;;) {
if (m_queued_events.is_empty())
waitForEvent();
Vector<QueuedEvent> events;
{
LOCKER(m_lock);
events = move(m_queued_events);
}
for (auto& queued_event : events) {
auto* receiver = queued_event.receiver;
auto& event = *queued_event.event;
#ifdef WSEVENTLOOP_DEBUG
dbgprintf("WSEventLoop: receiver{%p} event %u (%s)\n", receiver, (unsigned)event.type(), event.name());
#endif
if (!receiver) {
dbgprintf("WSEvent type %u with no receiver :(\n", event.type());
ASSERT_NOT_REACHED();
return 1;
} else {
receiver->event(event);
}
}
}
}
void WSEventLoop::post_event(WSEventReceiver* receiver, OwnPtr<WSEvent>&& event)
{
//ASSERT_INTERRUPTS_ENABLED();
LOCKER(m_lock);
#ifdef WSEVENTLOOP_DEBUG
dbgprintf("WSEventLoop::post_event: {%u} << receiver=%p, event=%p\n", m_queued_events.size(), receiver, event.ptr());
#endif
m_queued_events.append({ receiver, move(event) });
}
void WSEventLoop::waitForEvent()
{
auto& mouse = PS2MouseDevice::the();
auto& screen = WSScreen::the();
bool prev_left_button = screen.left_mouse_button_pressed();
bool prev_right_button = screen.right_mouse_button_pressed();
int dx = 0;
int dy = 0;
while (mouse.can_read(*m_server_process)) {
signed_byte data[3];
ssize_t nread = mouse.read(*m_server_process, (byte*)data, 3);
ASSERT(nread == 3);
bool left_button = data[0] & 1;
bool right_button = data[0] & 2;
dx += data[1];
dy += -data[2];
if (left_button != prev_left_button || right_button != prev_right_button || !mouse.can_read(*m_server_process)) {
prev_left_button = left_button;
prev_right_button = right_button;
screen.on_receive_mouse_data(dx, dy, left_button, right_button);
dx = 0;
dy = 0;
}
}
}

View file

@ -0,0 +1,40 @@
#pragma once
#include "WSEvent.h"
#include <AK/Lock.h>
#include <AK/OwnPtr.h>
#include <AK/Vector.h>
class WSEventReceiver;
class Process;
class WSEventLoop {
public:
WSEventLoop();
~WSEventLoop();
int exec();
void post_event(WSEventReceiver* receiver, OwnPtr<WSEvent>&&);
static WSEventLoop& the();
static void initialize();
bool running() const { return m_running; }
Process& server_process() { return *m_server_process; }
private:
void waitForEvent();
SpinLock m_lock;
struct QueuedEvent {
WSEventReceiver* receiver { nullptr };
OwnPtr<WSEvent> event;
};
Vector<QueuedEvent> m_queued_events;
Process* m_server_process { nullptr };
bool m_running { false };
};

View file

@ -0,0 +1,10 @@
#include "WSEventReceiver.h"
#include <AK/Assertions.h>
WSEventReceiver::WSEventReceiver()
{
}
WSEventReceiver::~WSEventReceiver()
{
}

View file

@ -0,0 +1,13 @@
#pragma once
#include <AK/Weakable.h>
class WSEvent;
class WSEventReceiver : public Weakable<WSEventReceiver> {
public:
WSEventReceiver();
virtual ~WSEventReceiver();
virtual void event(WSEvent&) = 0;
};

View file

@ -0,0 +1,46 @@
#include "WSFrameBuffer.h"
#include <Widgets/GraphicsBitmap.h>
#include <AK/Assertions.h>
WSFrameBuffer* s_the;
void WSFrameBuffer::initialize()
{
s_the = nullptr;
}
WSFrameBuffer& WSFrameBuffer::the()
{
ASSERT(s_the);
return *s_the;
}
WSFrameBuffer::WSFrameBuffer(unsigned width, unsigned height)
: WSScreen(width, height)
{
ASSERT(!s_the);
s_the = this;
}
WSFrameBuffer::WSFrameBuffer(RGBA32* data, unsigned width, unsigned height)
: WSScreen(width, height)
, m_data(data)
{
ASSERT(!s_the);
s_the = this;
}
WSFrameBuffer::~WSFrameBuffer()
{
}
void WSFrameBuffer::show()
{
}
RGBA32* WSFrameBuffer::scanline(int y)
{
unsigned pitch = sizeof(RGBA32) * width();
return reinterpret_cast<RGBA32*>(((byte*)m_data) + (y * pitch));
}

View file

@ -0,0 +1,25 @@
#pragma once
#include "WSScreen.h"
#include <Widgets/Color.h>
class GraphicsBitmap;
class WSFrameBuffer final : public WSScreen {
public:
WSFrameBuffer(unsigned width, unsigned height);
WSFrameBuffer(RGBA32*, unsigned width, unsigned height);
virtual ~WSFrameBuffer() override;
void show();
static WSFrameBuffer& the();
RGBA32* scanline(int y);
static void initialize();
private:
RGBA32* m_data { nullptr };
};

117
WindowServer/WSScreen.cpp Normal file
View file

@ -0,0 +1,117 @@
#include "WSScreen.h"
#include "WSEventLoop.h"
#include "WSEvent.h"
#include "WSWindowManager.h"
#include <AK/Assertions.h>
static WSScreen* s_the;
void WSScreen::initialize()
{
s_the = nullptr;
}
WSScreen& WSScreen::the()
{
ASSERT(s_the);
return *s_the;
}
WSScreen::WSScreen(unsigned width, unsigned height)
: m_width(width)
, m_height(height)
{
ASSERT(!s_the);
s_the = this;
m_cursor_location = rect().center();
Keyboard::the().set_client(this);
}
WSScreen::~WSScreen()
{
}
void WSScreen::on_receive_mouse_data(int dx, int dy, bool left_button, bool right_button)
{
auto prev_location = m_cursor_location;
m_cursor_location.moveBy(dx, dy);
m_cursor_location.constrain(rect());
if (m_cursor_location.x() >= width())
m_cursor_location.setX(width() - 1);
if (m_cursor_location.y() >= height())
m_cursor_location.setY(height() - 1);
if (m_cursor_location != prev_location) {
auto event = make<MouseEvent>(WSEvent::MouseMove, m_cursor_location.x(), m_cursor_location.y());
WSEventLoop::the().post_event(&WSWindowManager::the(), move(event));
}
bool prev_left_button = m_left_mouse_button_pressed;
bool prev_right_button = m_right_mouse_button_pressed;
m_left_mouse_button_pressed = left_button;
m_right_mouse_button_pressed = right_button;
if (prev_left_button != left_button) {
auto event = make<MouseEvent>(left_button ? WSEvent::MouseDown : WSEvent::MouseUp, m_cursor_location.x(), m_cursor_location.y(), MouseButton::Left);
WSEventLoop::the().post_event(&WSWindowManager::the(), move(event));
}
if (prev_right_button != right_button) {
auto event = make<MouseEvent>(right_button ? WSEvent::MouseDown : WSEvent::MouseUp, m_cursor_location.x(), m_cursor_location.y(), MouseButton::Right);
WSEventLoop::the().post_event(&WSWindowManager::the(), move(event));
}
if (m_cursor_location != prev_location || prev_left_button != left_button)
WSWindowManager::the().draw_cursor();
}
void WSScreen::on_key_pressed(Keyboard::Key key)
{
auto event = make<KeyEvent>(WSEvent::KeyDown, 0);
int key_code = 0;
switch (key.character) {
case 8: key_code = KeyboardKey::Backspace; break;
case 10: key_code = KeyboardKey::Return; break;
}
event->m_key = key_code;
if (key.character) {
char buf[] = { 0, 0 };
char& ch = buf[0];
ch = key.character;
if (key.shift()) {
if (ch >= 'a' && ch <= 'z') {
ch &= ~0x20;
} else {
switch (ch) {
case '1': ch = '!'; break;
case '2': ch = '@'; break;
case '3': ch = '#'; break;
case '4': ch = '$'; break;
case '5': ch = '%'; break;
case '6': ch = '^'; break;
case '7': ch = '&'; break;
case '8': ch = '*'; break;
case '9': ch = '('; break;
case '0': ch = ')'; break;
case '-': ch = '_'; break;
case '=': ch = '+'; break;
case '`': ch = '~'; break;
case ',': ch = '<'; break;
case '.': ch = '>'; break;
case '/': ch = '?'; break;
case '[': ch = '{'; break;
case ']': ch = '}'; break;
case '\\': ch = '|'; break;
case '\'': ch = '"'; break;
case ';': ch = ':'; break;
}
}
}
event->m_text = buf;
}
event->m_shift = key.shift();
event->m_ctrl = key.ctrl();
event->m_alt = key.alt();
WSEventLoop::the().post_event(&WSWindowManager::the(), move(event));
}

41
WindowServer/WSScreen.h Normal file
View file

@ -0,0 +1,41 @@
#pragma once
#include <Widgets/Rect.h>
#include <Widgets/Size.h>
#include <Kernel/Keyboard.h>
class WSScreen : public KeyboardClient {
public:
virtual ~WSScreen();
int width() const { return m_width; }
int height() const { return m_height; }
static WSScreen& the();
Size size() const { return { width(), height() }; }
Rect rect() const { return { 0, 0, width(), height() }; }
static void initialize();
Point cursor_location() const { return m_cursor_location; }
bool left_mouse_button_pressed() const { return m_left_mouse_button_pressed; }
bool right_mouse_button_pressed() const { return m_right_mouse_button_pressed; }
void on_receive_mouse_data(int dx, int dy, bool left_button, bool right_button);
protected:
WSScreen(unsigned width, unsigned height);
private:
// ^KeyboardClient
virtual void on_key_pressed(Keyboard::Key) final;
int m_width { 0 };
int m_height { 0 };
Point m_cursor_location;
bool m_left_mouse_button_pressed { false };
bool m_right_mouse_button_pressed { false };
};

89
WindowServer/WSWindow.cpp Normal file
View file

@ -0,0 +1,89 @@
#include "WSWindow.h"
#include "WSWindowManager.h"
#include "WSEvent.h"
#include "WSEventLoop.h"
#include "Process.h"
WSWindow::WSWindow(Process& process, int window_id)
: m_process(process)
, m_window_id(window_id)
{
WSWindowManager::the().addWindow(*this);
}
WSWindow::~WSWindow()
{
WSWindowManager::the().removeWindow(*this);
}
void WSWindow::set_title(String&& title)
{
if (m_title == title)
return;
m_title = move(title);
WSWindowManager::the().notifyTitleChanged(*this);
}
void WSWindow::set_rect(const Rect& rect)
{
if (m_rect == rect)
return;
auto oldRect = m_rect;
m_rect = rect;
m_backing = GraphicsBitmap::create(m_process, m_rect.size());
WSWindowManager::the().notifyRectChanged(*this, oldRect, m_rect);
}
// FIXME: Just use the same types.
static GUI_MouseButton to_api(MouseButton button)
{
switch (button) {
case MouseButton::None: return GUI_MouseButton::NoButton;
case MouseButton::Left: return GUI_MouseButton::Left;
case MouseButton::Right: return GUI_MouseButton::Right;
case MouseButton::Middle: return GUI_MouseButton::Middle;
}
}
void WSWindow::event(WSEvent& event)
{
GUI_Event gui_event;
gui_event.window_id = window_id();
switch (event.type()) {
case WSEvent::Paint:
gui_event.type = GUI_Event::Type::Paint;
gui_event.paint.rect = static_cast<PaintEvent&>(event).rect();
break;
case WSEvent::MouseMove:
gui_event.type = GUI_Event::Type::MouseMove;
gui_event.mouse.position = static_cast<MouseEvent&>(event).position();
break;
case WSEvent::MouseDown:
gui_event.type = GUI_Event::Type::MouseDown;
gui_event.mouse.position = static_cast<MouseEvent&>(event).position();
gui_event.mouse.button = to_api(static_cast<MouseEvent&>(event).button());
break;
case WSEvent::MouseUp:
gui_event.type = GUI_Event::Type::MouseUp;
gui_event.mouse.position = static_cast<MouseEvent&>(event).position();
gui_event.mouse.button = to_api(static_cast<MouseEvent&>(event).button());
break;
case WSEvent::KeyDown:
gui_event.type = GUI_Event::Type::KeyDown;
gui_event.key.character = static_cast<KeyEvent&>(event).text()[0];
break;
case WSEvent::WM_Invalidate:
WSWindowManager::the().invalidate(*this);
return;
}
if (gui_event.type == GUI_Event::Type::Invalid)
return;
{
LOCKER(m_process.gui_events_lock());
m_process.gui_events().append(move(gui_event));
}
}

55
WindowServer/WSWindow.h Normal file
View file

@ -0,0 +1,55 @@
#pragma once
#include <Widgets/Rect.h>
#include <Widgets/GraphicsBitmap.h>
#include <AK/AKString.h>
#include <AK/InlineLinkedList.h>
#include "WSEventReceiver.h"
class Process;
class WSWindow final : public WSEventReceiver, public InlineLinkedListNode<WSWindow> {
public:
WSWindow(Process&, int window_id);
virtual ~WSWindow() override;
int window_id() const { return m_window_id; }
String title() const { return m_title; }
void set_title(String&&);
int x() const { return m_rect.x(); }
int y() const { return m_rect.y(); }
int width() const { return m_rect.width(); }
int height() const { return m_rect.height(); }
const Rect& rect() const { return m_rect; }
void set_rect(const Rect&);
void set_rect_without_repaint(const Rect& rect) { m_rect = rect; }
Point position() const { return m_rect.location(); }
void set_position(const Point& position) { set_rect({ position.x(), position.y(), width(), height() }); }
void set_position_without_repaint(const Point& position) { set_rect_without_repaint({ position.x(), position.y(), width(), height() }); }
virtual void event(WSEvent&) override;
bool is_being_dragged() const { return m_is_being_dragged; }
void set_is_being_dragged(bool b) { m_is_being_dragged = b; }
GraphicsBitmap* backing() { return m_backing.ptr(); }
// For InlineLinkedList.
// FIXME: Maybe make a ListHashSet and then WSWindowManager can just use that.
WSWindow* m_next { nullptr };
WSWindow* m_prev { nullptr };
private:
String m_title;
Rect m_rect;
bool m_is_being_dragged { false };
RetainPtr<GraphicsBitmap> m_backing;
Process& m_process;
int m_window_id { -1 };
};

View file

@ -0,0 +1,403 @@
#include "WSWindowManager.h"
#include "WSWindow.h"
#include "WSScreen.h"
#include "WSEventLoop.h"
#include "WSFrameBuffer.h"
#include "Process.h"
#include "MemoryManager.h"
#include <Widgets/Painter.h>
#include <Widgets/CharacterBitmap.h>
#include <AK/StdLibExtras.h>
//#define DEBUG_FLUSH_YELLOW
static const int windowTitleBarHeight = 16;
static inline Rect titleBarRectForWindow(const Rect& window)
{
return {
window.x() - 1,
window.y() - windowTitleBarHeight,
window.width() + 2,
windowTitleBarHeight
};
}
static inline Rect titleBarTitleRectForWindow(const Rect& window)
{
auto titleBarRect = titleBarRectForWindow(window);
return {
titleBarRect.x() + 2,
titleBarRect.y(),
titleBarRect.width() - 4,
titleBarRect.height()
};
}
static inline Rect borderRectForWindow(const Rect& window)
{
auto titleBarRect = titleBarRectForWindow(window);
return { titleBarRect.x() - 1,
titleBarRect.y() - 1,
titleBarRect.width() + 2,
windowTitleBarHeight + window.height() + 3
};
}
static inline Rect outerRectForWindow(const Rect& window)
{
auto rect = borderRectForWindow(window);
rect.inflate(2, 2);
return rect;
}
static WSWindowManager* s_the_window_manager;
WSWindowManager& WSWindowManager::the()
{
if (!s_the_window_manager)
s_the_window_manager = new WSWindowManager;
return *s_the_window_manager;
}
void WSWindowManager::initialize()
{
s_the_window_manager = nullptr;
}
static const char* cursor_bitmap_inner_ascii = {
" # "
" ## "
" ### "
" #### "
" ##### "
" ###### "
" ####### "
" ######## "
" ######### "
" ########## "
" ###### "
" ## ## "
" # ## "
" ## "
" ## "
" ## "
" "
};
static const char* cursor_bitmap_outer_ascii = {
"## "
"# # "
"# # "
"# # "
"# # "
"# # "
"# # "
"# # "
"# # "
"# # "
"# #### "
"# ## # "
"# # # # "
"## # # "
" # # "
" # # "
" ## "
};
WSWindowManager::WSWindowManager()
: m_framebuffer(WSFrameBuffer::the())
, m_screen_rect(m_framebuffer.rect())
{
auto size = m_screen_rect.size();
m_front_bitmap = GraphicsBitmap::create_wrapper(size, m_framebuffer.scanline(0));
auto* region = current->allocate_region(LinearAddress(), size.width() * size.height() * sizeof(RGBA32), "BackBitmap", true, true, true);
m_back_bitmap = GraphicsBitmap::create_wrapper(m_screen_rect.size(), (RGBA32*)region->linearAddress.get());
m_front_painter = make<Painter>(*m_front_bitmap);
m_back_painter = make<Painter>(*m_back_bitmap);
m_activeWindowBorderColor = Color(0, 64, 192);
m_activeWindowTitleColor = Color::White;
m_inactiveWindowBorderColor = Color(64, 64, 64);
m_inactiveWindowTitleColor = Color::White;
m_cursor_bitmap_inner = CharacterBitmap::createFromASCII(cursor_bitmap_inner_ascii, 12, 17);
m_cursor_bitmap_outer = CharacterBitmap::createFromASCII(cursor_bitmap_outer_ascii, 12, 17);
invalidate();
compose();
}
WSWindowManager::~WSWindowManager()
{
}
void WSWindowManager::paintWindowFrame(WSWindow& window)
{
//printf("[WM] paintWindowFrame {%p}, rect: %d,%d %dx%d\n", &window, window.rect().x(), window.rect().y(), window.rect().width(), window.rect().height());
auto titleBarRect = titleBarRectForWindow(window.rect());
auto titleBarTitleRect = titleBarTitleRectForWindow(window.rect());
auto outerRect = outerRectForWindow(window.rect());
auto borderRect = borderRectForWindow(window.rect());
Rect inner_border_rect {
window.x() - 1,
window.y() - 1,
window.width() + 2,
window.height() + 2
};
auto titleColor = &window == activeWindow() ? m_activeWindowTitleColor : m_inactiveWindowTitleColor;
auto borderColor = &window == activeWindow() ? m_activeWindowBorderColor : m_inactiveWindowBorderColor;
m_back_painter->draw_rect(borderRect, Color::MidGray);
m_back_painter->draw_rect(outerRect, borderColor);
m_back_painter->fill_rect(titleBarRect, borderColor);
m_back_painter->draw_rect(inner_border_rect, borderColor);
m_back_painter->draw_text(titleBarTitleRect, window.title(), Painter::TextAlignment::CenterLeft, titleColor);
}
void WSWindowManager::addWindow(WSWindow& window)
{
m_windows.set(&window);
m_windows_in_order.append(&window);
if (!activeWindow())
setActiveWindow(&window);
}
void WSWindowManager::move_to_front(WSWindow& window)
{
m_windows_in_order.remove(&window);
m_windows_in_order.append(&window);
}
void WSWindowManager::removeWindow(WSWindow& window)
{
if (!m_windows.contains(&window))
return;
invalidate(window);
m_windows.remove(&window);
m_windows_in_order.remove(&window);
if (!activeWindow() && !m_windows.is_empty())
setActiveWindow(*m_windows.begin());
}
void WSWindowManager::notifyTitleChanged(WSWindow& window)
{
printf("[WM] WSWindow{%p} title set to '%s'\n", &window, window.title().characters());
}
void WSWindowManager::notifyRectChanged(WSWindow& window, const Rect& old_rect, const Rect& new_rect)
{
printf("[WM] WSWindow %p rect changed (%d,%d %dx%d) -> (%d,%d %dx%d)\n", &window, old_rect.x(), old_rect.y(), old_rect.width(), old_rect.height(), new_rect.x(), new_rect.y(), new_rect.width(), new_rect.height());
ASSERT_INTERRUPTS_ENABLED();
LOCKER(m_lock);
invalidate(outerRectForWindow(old_rect));
invalidate(outerRectForWindow(new_rect));
}
void WSWindowManager::handleTitleBarMouseEvent(WSWindow& window, MouseEvent& event)
{
if (event.type() == WSEvent::MouseDown && event.button() == MouseButton::Left) {
printf("[WM] Begin dragging WSWindow{%p}\n", &window);
m_dragWindow = window.makeWeakPtr();;
m_dragOrigin = event.position();
m_dragWindowOrigin = window.position();
m_dragStartRect = outerRectForWindow(window.rect());
window.set_is_being_dragged(true);
return;
}
}
void WSWindowManager::processMouseEvent(MouseEvent& event)
{
if (event.type() == WSEvent::MouseUp && event.button() == MouseButton::Left) {
if (m_dragWindow) {
printf("[WM] Finish dragging WSWindow{%p}\n", m_dragWindow.ptr());
invalidate(m_dragStartRect);
invalidate(*m_dragWindow);
m_dragWindow->set_is_being_dragged(false);
m_dragEndRect = outerRectForWindow(m_dragWindow->rect());
m_dragWindow = nullptr;
return;
}
}
if (event.type() == WSEvent::MouseMove) {
if (m_dragWindow) {
auto old_window_rect = m_dragWindow->rect();
Point pos = m_dragWindowOrigin;
printf("[WM] Dragging [origin: %d,%d] now: %d,%d\n", m_dragOrigin.x(), m_dragOrigin.y(), event.x(), event.y());
pos.moveBy(event.x() - m_dragOrigin.x(), event.y() - m_dragOrigin.y());
m_dragWindow->set_position_without_repaint(pos);
invalidate(outerRectForWindow(old_window_rect));
invalidate(outerRectForWindow(m_dragWindow->rect()));
return;
}
}
for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) {
if (titleBarRectForWindow(window->rect()).contains(event.position())) {
if (event.type() == WSEvent::MouseDown) {
move_to_front(*window);
setActiveWindow(window);
}
handleTitleBarMouseEvent(*window, event);
return;
}
if (window->rect().contains(event.position())) {
if (event.type() == WSEvent::MouseDown) {
move_to_front(*window);
setActiveWindow(window);
}
// FIXME: Re-use the existing event instead of crafting a new one?
auto localEvent = make<MouseEvent>(event.type(), event.x() - window->rect().x(), event.y() - window->rect().y(), event.button());
window->event(*localEvent);
return;
}
}
}
void WSWindowManager::compose()
{
auto invalidated_rects = move(m_invalidated_rects);
printf("[WM] compose #%u (%u rects)\n", ++m_recompose_count, invalidated_rects.size());
dbgprintf("kmalloc stats: alloc:%u free:%u eternal:%u\n", sum_alloc, sum_free, kmalloc_sum_eternal);
auto any_window_contains_rect = [this] (const Rect& r) {
for (auto* window = m_windows_in_order.head(); window; window = window->next()) {
if (outerRectForWindow(window->rect()).contains(r))
return true;
}
return false;
};
for (auto& r : invalidated_rects) {
if (any_window_contains_rect(r))
continue;
//dbgprintf("Repaint root %d,%d %dx%d\n", r.x(), r.y(), r.width(), r.height());
m_back_painter->fill_rect(r, Color(0, 72, 96));
}
for (auto* window = m_windows_in_order.head(); window; window = window->next()) {
if (!window->backing())
continue;
paintWindowFrame(*window);
m_back_painter->blit(window->position(), *window->backing());
}
for (auto& r : invalidated_rects)
flush(r);
draw_cursor();
}
void WSWindowManager::draw_cursor()
{
ASSERT_INTERRUPTS_ENABLED();
LOCKER(m_lock);
auto cursor_location = m_framebuffer.cursor_location();
Rect cursor_rect { cursor_location.x(), cursor_location.y(), (int)m_cursor_bitmap_inner->width(), (int)m_cursor_bitmap_inner->height() };
flush(m_last_cursor_rect.united(cursor_rect));
Color inner_color = Color::White;
Color outer_color = Color::Black;
if (m_framebuffer.left_mouse_button_pressed())
swap(inner_color, outer_color);
m_front_painter->draw_bitmap(cursor_location, *m_cursor_bitmap_inner, inner_color);
m_front_painter->draw_bitmap(cursor_location, *m_cursor_bitmap_outer, outer_color);
m_last_cursor_rect = cursor_rect;
}
void WSWindowManager::event(WSEvent& event)
{
ASSERT_INTERRUPTS_ENABLED();
LOCKER(m_lock);
if (event.isMouseEvent())
return processMouseEvent(static_cast<MouseEvent&>(event));
if (event.isKeyEvent()) {
// FIXME: This is a good place to hook key events globally. :)
if (m_activeWindow)
return m_activeWindow->event(event);
return;
}
if (event.type() == WSEvent::WM_Compose) {
m_pending_compose_event = false;
compose();
return;
}
}
void WSWindowManager::setActiveWindow(WSWindow* window)
{
if (window == m_activeWindow.ptr())
return;
if (auto* previously_active_window = m_activeWindow.ptr())
invalidate(*previously_active_window);
m_activeWindow = window->makeWeakPtr();
if (m_activeWindow)
invalidate(*m_activeWindow);
}
void WSWindowManager::invalidate()
{
m_invalidated_rects.clear_with_capacity();
m_invalidated_rects.append(m_screen_rect);
}
void WSWindowManager::invalidate(const Rect& a_rect)
{
auto rect = Rect::intersection(a_rect, m_screen_rect);
if (rect.is_empty())
return;
for (auto& r : m_invalidated_rects) {
if (r.contains(rect))
return;
if (r.intersects(rect)) {
// Unite with the existing dirty rect.
// FIXME: It would be much nicer to compute the exact rects needing repaint.
r = r.united(rect);
return;
}
}
m_invalidated_rects.append(rect);
if (!m_pending_compose_event) {
ASSERT_INTERRUPTS_ENABLED();
WSEventLoop::the().post_event(this, make<WSEvent>(WSEvent::WM_Compose));
m_pending_compose_event = true;
}
}
void WSWindowManager::invalidate(const WSWindow& window)
{
ASSERT_INTERRUPTS_ENABLED();
LOCKER(m_lock);
invalidate(outerRectForWindow(window.rect()));
}
void WSWindowManager::flush(const Rect& a_rect)
{
auto rect = Rect::intersection(a_rect, m_screen_rect);
RGBA32* front_ptr = m_front_bitmap->scanline(rect.y()) + rect.x();
const RGBA32* back_ptr = m_back_bitmap->scanline(rect.y()) + rect.x();
size_t pitch = m_back_bitmap->pitch();
#ifdef DEBUG_FLUSH_YELLOW
m_front_painter->fill_rect(rect, Color::Yellow);
#endif
for (int y = 0; y < rect.height(); ++y) {
fast_dword_copy(front_ptr, back_ptr, rect.width());
front_ptr = (RGBA32*)((byte*)front_ptr + pitch);
back_ptr = (const RGBA32*)((const byte*)back_ptr + pitch);
}
}

View file

@ -0,0 +1,95 @@
#pragma once
#include <Widgets/Rect.h>
#include <Widgets/Color.h>
#include <Widgets/Painter.h>
#include <AK/HashTable.h>
#include <AK/InlineLinkedList.h>
#include <AK/WeakPtr.h>
#include <AK/Lock.h>
#include "WSEventReceiver.h"
class WSFrameBuffer;
class MouseEvent;
class PaintEvent;
class WSWindow;
class CharacterBitmap;
class GraphicsBitmap;
class WSWindowManager : public WSEventReceiver {
public:
static WSWindowManager& the();
void addWindow(WSWindow&);
void removeWindow(WSWindow&);
void notifyTitleChanged(WSWindow&);
void notifyRectChanged(WSWindow&, const Rect& oldRect, const Rect& newRect);
WSWindow* activeWindow() { return m_activeWindow.ptr(); }
void move_to_front(WSWindow&);
static void initialize();
void draw_cursor();
void invalidate(const WSWindow&);
void invalidate(const Rect&);
void invalidate();
void flush(const Rect&);
private:
WSWindowManager();
virtual ~WSWindowManager() override;
void processMouseEvent(MouseEvent&);
void handleTitleBarMouseEvent(WSWindow&, MouseEvent&);
void setActiveWindow(WSWindow*);
virtual void event(WSEvent&) override;
void compose();
void paintWindowFrame(WSWindow&);
WSFrameBuffer& m_framebuffer;
Rect m_screen_rect;
Color m_activeWindowBorderColor;
Color m_activeWindowTitleColor;
Color m_inactiveWindowBorderColor;
Color m_inactiveWindowTitleColor;
HashTable<WSWindow*> m_windows;
InlineLinkedList<WSWindow> m_windows_in_order;
WeakPtr<WSWindow> m_activeWindow;
WeakPtr<WSWindow> m_dragWindow;
Point m_dragOrigin;
Point m_dragWindowOrigin;
Rect m_lastDragRect;
Rect m_dragStartRect;
Rect m_dragEndRect;
Rect m_last_cursor_rect;
unsigned m_recompose_count { 0 };
RetainPtr<GraphicsBitmap> m_front_bitmap;
RetainPtr<GraphicsBitmap> m_back_bitmap;
Vector<Rect> m_invalidated_rects;
bool m_pending_compose_event { false };
RetainPtr<CharacterBitmap> m_cursor_bitmap_inner;
RetainPtr<CharacterBitmap> m_cursor_bitmap_outer;
OwnPtr<Painter> m_back_painter;
OwnPtr<Painter> m_front_painter;
mutable SpinLock m_lock;
};

25
WindowServer/main.cpp Normal file
View file

@ -0,0 +1,25 @@
#include "Process.h"
#include <Widgets/Font.h>
#include <WindowServer/WSFrameBuffer.h>
#include <WindowServer/WSWindowManager.h>
#include <WindowServer/WSEventLoop.h>
#include <WindowServer/WSWindow.h>
// NOTE: This actually runs as a kernel process.
// I'd like to change this eventually.
void WindowServer_main()
{
auto info = current->get_display_info();
dbgprintf("Screen is %ux%ux%ubpp\n", info.width, info.height, info.bpp);
WSFrameBuffer framebuffer((dword*)info.framebuffer, info.width, info.height);
WSWindowManager::the();
dbgprintf("Entering WindowServer main loop.\n");
WSEventLoop::the().exec();
ASSERT_NOT_REACHED();
}