Squash commits for public release
This commit is contained in:
15
userland/servers/launch_server/BUILD.gn
Normal file
15
userland/servers/launch_server/BUILD.gn
Normal file
@@ -0,0 +1,15 @@
|
||||
import("//build/userland/TEMPLATE.gni")
|
||||
|
||||
xOS_executable("launch_server") {
|
||||
signexec = true
|
||||
install_path = "System/"
|
||||
sources = [
|
||||
"LaunchWatchdog.cpp",
|
||||
"main.cpp",
|
||||
]
|
||||
configs = [ "//build/userland:userland_flags" ]
|
||||
deplibs = [
|
||||
"libcxx",
|
||||
"libfoundation",
|
||||
]
|
||||
}
|
||||
41
userland/servers/launch_server/Exec.h
Normal file
41
userland/servers/launch_server/Exec.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <csignal>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
namespace LaunchServer {
|
||||
|
||||
class Exec {
|
||||
public:
|
||||
enum Flags {
|
||||
None = 0,
|
||||
RestartOnFail = (1 << 0),
|
||||
};
|
||||
|
||||
Exec(const std::string& path, Flags flag = Flags::None)
|
||||
: m_path(path)
|
||||
, m_flags(flag)
|
||||
{
|
||||
}
|
||||
~Exec() = default;
|
||||
|
||||
void launch()
|
||||
{
|
||||
m_pid = fork();
|
||||
if (m_pid == 0) {
|
||||
execlp(m_path.c_str(), m_path.c_str(), NULL);
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
bool is_alive() const { return m_pid > 0 && kill(m_pid, 0) == 0; }
|
||||
|
||||
private:
|
||||
std::string m_path;
|
||||
Flags m_flags;
|
||||
int m_pid { -1 };
|
||||
};
|
||||
|
||||
}
|
||||
13
userland/servers/launch_server/LaunchWatchdog.cpp
Normal file
13
userland/servers/launch_server/LaunchWatchdog.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "LaunchWatchdog.h"
|
||||
|
||||
namespace LaunchServer {
|
||||
|
||||
LaunchWatchdog* s_LaunchServer_LaunchWatchdog_the = nullptr;
|
||||
|
||||
LaunchWatchdog::LaunchWatchdog()
|
||||
: LFoundation::EventReceiver()
|
||||
{
|
||||
s_LaunchServer_LaunchWatchdog_the = this;
|
||||
}
|
||||
|
||||
}
|
||||
37
userland/servers/launch_server/LaunchWatchdog.h
Normal file
37
userland/servers/launch_server/LaunchWatchdog.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "Exec.h"
|
||||
#include <iostream>
|
||||
#include <libfoundation/EventReceiver.h>
|
||||
#include <vector>
|
||||
|
||||
namespace LaunchServer {
|
||||
|
||||
class LaunchWatchdog : public LFoundation::EventReceiver {
|
||||
public:
|
||||
inline static LaunchWatchdog& the()
|
||||
{
|
||||
extern LaunchWatchdog* s_LaunchServer_LaunchWatchdog_the;
|
||||
return *s_LaunchServer_LaunchWatchdog_the;
|
||||
}
|
||||
|
||||
LaunchWatchdog();
|
||||
~LaunchWatchdog() = default;
|
||||
|
||||
void tick()
|
||||
{
|
||||
for (auto& exec : m_execs) {
|
||||
if (!exec.is_alive()) {
|
||||
exec.launch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add(const Exec& exec) { m_execs.push_back(exec); }
|
||||
void add(Exec&& exec) { m_execs.push_back(std::move(exec)); }
|
||||
|
||||
private:
|
||||
std::vector<Exec> m_execs;
|
||||
};
|
||||
|
||||
}
|
||||
34
userland/servers/launch_server/main.cpp
Normal file
34
userland/servers/launch_server/main.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "LaunchWatchdog.h"
|
||||
#include <libfoundation/EventLoop.h>
|
||||
#include <libfoundation/json/Parser.h>
|
||||
#include <new>
|
||||
|
||||
void load_from_file(LaunchServer::LaunchWatchdog& launch_watchdog)
|
||||
{
|
||||
auto json_parser = LFoundation::Json::Parser("/System/launch_server_config.json");
|
||||
LFoundation::Json::Object* jobj_root = json_parser.object();
|
||||
if (jobj_root->invalid()) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
auto* jdict_root = jobj_root->cast_to<LFoundation::Json::DictObject>();
|
||||
auto* jlaunch_list = jdict_root->data()["launch"]->cast_to<LFoundation::Json::ListObject>();
|
||||
for (auto* jobj : jlaunch_list->data()) {
|
||||
const std::string& strdata = jobj->cast_to<LFoundation::Json::StringObject>()->data();
|
||||
launch_watchdog.add(LaunchServer::Exec(strdata, LaunchServer::Exec::Flags::RestartOnFail));
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
auto* event_loop = new LFoundation::EventLoop();
|
||||
auto* launch_watchdog = new LaunchServer::LaunchWatchdog();
|
||||
|
||||
load_from_file(*launch_watchdog);
|
||||
launch_watchdog->tick();
|
||||
event_loop->add(LFoundation::Timer([launch_watchdog] {
|
||||
launch_watchdog->tick();
|
||||
},
|
||||
5000, LFoundation::Timer::Repeat));
|
||||
return event_loop->run();
|
||||
}
|
||||
53
userland/servers/window_server/BUILD.gn
Normal file
53
userland/servers/window_server/BUILD.gn
Normal file
@@ -0,0 +1,53 @@
|
||||
import("//build/userland/TEMPLATE.gni")
|
||||
|
||||
xOS_executable("window_server") {
|
||||
signexec = true
|
||||
install_path = "System/"
|
||||
sources = [
|
||||
"src/Components/Base/BaseWindow.cpp",
|
||||
"src/Components/Elements/Button.cpp",
|
||||
"src/Components/LoadingScreen/LoadingScreen.cpp",
|
||||
"src/Components/MenuBar/MenuBar.cpp",
|
||||
"src/Components/Popup/Popup.cpp",
|
||||
"src/Devices/Devices.cpp",
|
||||
"src/Devices/Screen.cpp",
|
||||
"src/IPC/Connection.cpp",
|
||||
"src/IPC/ServerDecoder.cpp",
|
||||
"src/Managers/Compositor.cpp",
|
||||
"src/Managers/CursorManager.cpp",
|
||||
"src/Managers/ResourceManager.cpp",
|
||||
"src/Managers/WindowManager.cpp",
|
||||
"src/main.cpp",
|
||||
]
|
||||
|
||||
if (device_type == "desktop") {
|
||||
cflags = [ "-DTARGET_DESKTOP" ]
|
||||
sources += [
|
||||
"src/Target/Desktop/Window.cpp",
|
||||
"src/Target/Desktop/WindowFrame.cpp",
|
||||
]
|
||||
}
|
||||
if (device_type == "mobile") {
|
||||
cflags = [ "-DTARGET_MOBILE" ]
|
||||
sources += [
|
||||
"src/Components/ControlBar/ControlBar.cpp",
|
||||
"src/Target/Mobile/Window.cpp",
|
||||
]
|
||||
}
|
||||
|
||||
configs = [ "//build/userland:userland_flags" ]
|
||||
|
||||
if (host == "llvm") {
|
||||
cflags += [ "-flto" ]
|
||||
}
|
||||
|
||||
deplibs = [
|
||||
"libcxx",
|
||||
"libfoundation",
|
||||
"libipc",
|
||||
"libg",
|
||||
"libapi",
|
||||
]
|
||||
|
||||
include_dirs = [ "//servers/window_server/src/" ]
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "BaseWindow.h"
|
||||
#include "../../Managers/WindowManager.h"
|
||||
#include <utility>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
BaseWindow::BaseWindow(int connection_id, int id, CreateWindowMessage& msg)
|
||||
: m_id(id)
|
||||
, m_connection_id(connection_id)
|
||||
, m_type((WindowType)msg.type())
|
||||
, m_buffer(msg.buffer_id())
|
||||
, m_content_bitmap()
|
||||
, m_bounds(0, 0, 0, 0)
|
||||
, m_content_bounds(0, 0, 0, 0)
|
||||
, m_app_name(msg.title().string())
|
||||
, m_bundle_id(msg.bundle_id().string())
|
||||
{
|
||||
}
|
||||
|
||||
BaseWindow::BaseWindow(BaseWindow&& win)
|
||||
: m_id(win.m_id)
|
||||
, m_connection_id(win.m_connection_id)
|
||||
, m_buffer(win.m_buffer)
|
||||
, m_content_bitmap(std::move(win.m_content_bitmap))
|
||||
, m_bounds(win.m_bounds)
|
||||
, m_content_bounds(win.m_content_bounds)
|
||||
, m_app_name(std::move(win.m_app_name))
|
||||
, m_icon_path(std::move(win.m_icon_path))
|
||||
, m_bundle_id(std::move(win.m_bundle_id))
|
||||
{
|
||||
}
|
||||
|
||||
void BaseWindow::set_buffer(int buffer_id, LG::Size sz, LG::PixelBitmapFormat fmt)
|
||||
{
|
||||
m_buffer.open(buffer_id);
|
||||
m_content_bitmap = LG::PixelBitmap(m_buffer.data(), sz.width(), sz.height());
|
||||
m_content_bitmap.set_format(fmt);
|
||||
}
|
||||
|
||||
} // namespace WinServer
|
||||
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
#include "../../IPC/Connection.h"
|
||||
#include "../MenuBar/MenuItem.h"
|
||||
#include <libapi/window_server/MessageContent/MenuBar.h>
|
||||
#include <libapi/window_server/MessageContent/Window.h>
|
||||
#include <libfoundation/SharedBuffer.h>
|
||||
#include <libg/PixelBitmap.h>
|
||||
#include <libg/Rect.h>
|
||||
#include <libg/Size.h>
|
||||
#include <sys/types.h>
|
||||
#include <utility>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
typedef uint32_t WindowEventMask;
|
||||
enum WindowEvent {
|
||||
WindowStatus = (1 << 0),
|
||||
WindowCreation = (1 << 1),
|
||||
IconChange = (1 << 2),
|
||||
TitleChange = (1 << 3),
|
||||
};
|
||||
|
||||
class BaseWindow {
|
||||
public:
|
||||
BaseWindow(int connection_id, int id, CreateWindowMessage& msg);
|
||||
BaseWindow(BaseWindow&& win);
|
||||
~BaseWindow() = default;
|
||||
|
||||
void set_buffer(int buffer_id, LG::Size sz, LG::PixelBitmapFormat fmt);
|
||||
|
||||
inline int id() const { return m_id; }
|
||||
inline int connection_id() const { return m_connection_id; }
|
||||
inline WindowType type() const { return m_type; }
|
||||
inline WindowEventMask event_mask() const { return m_event_mask; }
|
||||
inline void set_event_mask(WindowEventMask mask) { m_event_mask = mask; }
|
||||
|
||||
inline LFoundation::SharedBuffer<LG::Color>& buffer() { return m_buffer; }
|
||||
inline LG::PixelBitmap& content_bitmap() { return m_content_bitmap; }
|
||||
inline const LG::PixelBitmap& content_bitmap() const { return m_content_bitmap; }
|
||||
|
||||
inline LG::Rect& content_bounds() { return m_content_bounds; }
|
||||
inline const LG::Rect& content_bounds() const { return m_content_bounds; }
|
||||
|
||||
inline LG::Rect& bounds() { return m_bounds; }
|
||||
inline const LG::Rect& bounds() const { return m_bounds; }
|
||||
|
||||
inline const std::string& app_name() const { return m_app_name; }
|
||||
inline const std::string& bundle_id() const { return m_bundle_id; }
|
||||
|
||||
inline const std::string& app_title() const { return m_app_title; }
|
||||
inline void set_app_title(const std::string& title) { m_app_title = title, did_app_title_change(); }
|
||||
virtual void did_app_title_change() { }
|
||||
|
||||
inline const std::string& icon_path() const { return m_icon_path; }
|
||||
inline void set_icon_path(std::string&& name) { m_icon_path = std::move(name), did_icon_path_change(); }
|
||||
inline void set_icon_path(const std::string& name) { m_icon_path = name, did_icon_path_change(); }
|
||||
virtual void did_icon_path_change() { }
|
||||
|
||||
inline bool visible() const { return m_visible; }
|
||||
inline void set_visible(bool vis) { m_visible = vis; }
|
||||
|
||||
inline void set_needs_display(const LG::Rect& rect) const
|
||||
{
|
||||
DisplayMessage msg(connection_id(), rect);
|
||||
Connection::the().send_async_message(msg);
|
||||
}
|
||||
|
||||
inline void offset_by(int x_offset, int y_offset)
|
||||
{
|
||||
bounds().offset_by(x_offset, y_offset);
|
||||
content_bounds().offset_by(x_offset, y_offset);
|
||||
}
|
||||
|
||||
virtual void did_size_change(const LG::Size& size) { }
|
||||
|
||||
protected:
|
||||
int m_id { -1 };
|
||||
int m_connection_id { -1 };
|
||||
bool m_visible { true };
|
||||
WindowType m_type { WindowType::Standard };
|
||||
WindowEventMask m_event_mask { 0 };
|
||||
LG::Rect m_bounds;
|
||||
LG::Rect m_content_bounds;
|
||||
LG::PixelBitmap m_content_bitmap;
|
||||
std::string m_app_name {};
|
||||
std::string m_app_title {};
|
||||
std::string m_icon_path {};
|
||||
std::string m_bundle_id {};
|
||||
LFoundation::SharedBuffer<LG::Color> m_buffer;
|
||||
};
|
||||
|
||||
} // namespace WinServer
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "ControlBar.h"
|
||||
#include "../../Devices/Screen.h"
|
||||
#include "../../Managers/Compositor.h"
|
||||
#include <libg/ImageLoaders/PNGLoader.h>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
ControlBar* s_WinServer_ControlBar_the = nullptr;
|
||||
|
||||
ControlBar::ControlBar()
|
||||
: m_bounds(0, Screen::the().bounds().height() - height(), Screen::the().bounds().width(), height())
|
||||
{
|
||||
s_WinServer_ControlBar_the = this;
|
||||
LG::PNG::PNGLoader loader;
|
||||
m_menu_icon = loader.load_from_file("/res/system/mobile/control.png");
|
||||
int x = width() / 2 - m_menu_icon.width() / 2;
|
||||
int y = Screen::the().bounds().height() - height() / 2 - m_menu_icon.height() / 2;
|
||||
m_button_bounds = LG::Rect(x, y, m_menu_icon.width(), m_menu_icon.height());
|
||||
}
|
||||
|
||||
} // namespace WinServer
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#include <libfoundation/Logger.h>
|
||||
#include <libg/Context.h>
|
||||
#include <libg/PixelBitmap.h>
|
||||
#include <libg/Point.h>
|
||||
#include <vector>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
class ControlBar {
|
||||
public:
|
||||
inline static ControlBar& the()
|
||||
{
|
||||
extern ControlBar* s_WinServer_ControlBar_the;
|
||||
return *s_WinServer_ControlBar_the;
|
||||
}
|
||||
|
||||
ControlBar();
|
||||
|
||||
static constexpr size_t height() { return 20; }
|
||||
static constexpr size_t padding() { return 4; }
|
||||
static constexpr size_t menubar_content_offset() { return 20; }
|
||||
|
||||
size_t width() const { return m_bounds.width(); }
|
||||
LG::Rect& bounds() { return m_bounds; }
|
||||
const LG::Rect& bounds() const { return m_bounds; }
|
||||
const LG::Rect& control_button_bounds() const { return m_button_bounds; }
|
||||
|
||||
[[gnu::always_inline]] inline void draw(LG::Context& ctx)
|
||||
{
|
||||
ctx.draw({ control_button_bounds().min_x(), control_button_bounds().min_y() }, m_menu_icon);
|
||||
}
|
||||
|
||||
private:
|
||||
LG::Rect m_bounds;
|
||||
LG::Rect m_button_bounds;
|
||||
LG::PixelBitmap m_menu_icon;
|
||||
};
|
||||
|
||||
} // namespace WinServer
|
||||
@@ -0,0 +1,37 @@
|
||||
#include "Button.h"
|
||||
#include "../Helpers/TextDrawer.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
void Button::recalc_dims()
|
||||
{
|
||||
size_t new_width = 0;
|
||||
size_t new_height = 0;
|
||||
if (m_is_icon_set) {
|
||||
new_height = m_icon.height();
|
||||
new_width += m_icon.width();
|
||||
if (m_title.size()) {
|
||||
new_width += 4;
|
||||
}
|
||||
}
|
||||
bounds().set_width(new_width + text_width());
|
||||
bounds().set_height(std::max(new_height, text_height()));
|
||||
}
|
||||
|
||||
size_t Button::text_width()
|
||||
{
|
||||
return Helpers::text_width(m_title, font());
|
||||
}
|
||||
|
||||
void Button::display(LG::Context& ctx, LG::Point<int> pt)
|
||||
{
|
||||
if (m_is_icon_set) {
|
||||
ctx.draw(pt, m_icon);
|
||||
pt.offset_by(m_icon.width() + 4, 0);
|
||||
}
|
||||
|
||||
Helpers::draw_text(ctx, pt, m_title, font());
|
||||
}
|
||||
|
||||
} // namespace WinServer
|
||||
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <libg/Color.h>
|
||||
#include <libg/Context.h>
|
||||
#include <string>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
class Button {
|
||||
public:
|
||||
Button() = default;
|
||||
~Button() = default;
|
||||
|
||||
void display();
|
||||
void set_title(const std::string& title) { m_title = title, recalc_dims(); }
|
||||
void set_title(std::string&& title) { m_title = std::move(title), recalc_dims(); }
|
||||
const std::string& title() const { return m_title; }
|
||||
|
||||
void set_font(const LG::Font& font) { m_font = font, recalc_dims(); }
|
||||
void set_icon(const LG::Glyph& icon) { m_is_icon_set = true, m_icon = icon, recalc_dims(); }
|
||||
|
||||
void set_title_color(const LG::Color& color) { m_title_color = color; }
|
||||
const LG::Color& title_color() const { return m_title_color; }
|
||||
|
||||
inline const LG::Font& font() const { return m_font; }
|
||||
|
||||
inline LG::Rect& bounds() { return m_bounds; }
|
||||
inline const LG::Rect& bounds() const { return m_bounds; }
|
||||
|
||||
void display(LG::Context& ctx, LG::Point<int> pt);
|
||||
|
||||
private:
|
||||
void recalc_dims();
|
||||
size_t text_width();
|
||||
inline size_t text_height() const { return font().size(); }
|
||||
|
||||
LG::Rect m_bounds {};
|
||||
std::string m_title {};
|
||||
LG::Font m_font { LG::Font::system_font() };
|
||||
LG::Color m_title_color;
|
||||
LG::Glyph m_icon;
|
||||
|
||||
bool m_is_icon_set { false };
|
||||
};
|
||||
|
||||
} // namespace WinServer
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <libg/Color.h>
|
||||
#include <libg/Context.h>
|
||||
#include <libg/Font.h>
|
||||
#include <string_view>
|
||||
|
||||
namespace WinServer {
|
||||
namespace Helpers {
|
||||
|
||||
[[gnu::always_inline]] inline static size_t text_width(const std::string_view& text, const LG::Font& f)
|
||||
{
|
||||
size_t width = 0;
|
||||
|
||||
for (int i = 0; i < text.size(); i++) {
|
||||
width += f.glyph(text[i]).advance();
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
[[gnu::always_inline]] inline static void draw_text(LG::Context& ctx, LG::Point<int> pt, const std::string_view& text, const LG::Font& f)
|
||||
{
|
||||
for (int i = 0; i < text.size(); i++) {
|
||||
ctx.draw(pt, f.glyph(text[i]));
|
||||
pt.offset_by(f.glyph(text[i]).advance(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Helpers
|
||||
} // namespace WinServer
|
||||
@@ -0,0 +1,95 @@
|
||||
#include "LoadingScreen.h"
|
||||
#include "../Helpers/TextDrawer.h"
|
||||
#include <libfoundation/Memory.h>
|
||||
#include <libg/Context.h>
|
||||
#include <libg/ImageLoaders/PNGLoader.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
LoadingScreen* s_WinServer_LoadingScreen_the = nullptr;
|
||||
|
||||
LoadingScreen::LoadingScreen()
|
||||
: m_screen(Screen::the())
|
||||
{
|
||||
s_WinServer_LoadingScreen_the = this;
|
||||
run_intro_animation();
|
||||
}
|
||||
|
||||
void LoadingScreen::display_status_bar(int progress, int out_of)
|
||||
{
|
||||
LG::Context ctx(m_screen.write_bitmap());
|
||||
int widthp = (progress * progress_line_width()) / out_of;
|
||||
|
||||
ctx.set_fill_color(LG::Color(20, 20, 20));
|
||||
ctx.fill_rounded(LG::Rect(m_progress_line_min_x, m_progress_line_min_y, progress_line_width(), progress_line_height()), LG::CornerMask(4));
|
||||
|
||||
ctx.set_fill_color(LG::Color::White);
|
||||
ctx.fill_rounded(LG::Rect(m_progress_line_min_x, m_progress_line_min_y, widthp, progress_line_height()), LG::CornerMask(4));
|
||||
|
||||
m_screen.swap_buffers();
|
||||
}
|
||||
|
||||
void LoadingScreen::animation_frame(LG::Context& ctx, LG::Point<int> pt, int cur_frame, int total_frames)
|
||||
{
|
||||
// Applying a cubic-easing.
|
||||
int animation_window = total_frames / m_logo_text.size();
|
||||
int animation_frames_per_char = 2 * animation_window;
|
||||
int frame = total_frames - cur_frame;
|
||||
frame = total_frames - frame * frame * frame / (total_frames * total_frames);
|
||||
|
||||
for (int i = 0; i < m_logo_text.size(); i++) {
|
||||
int end_frame = animation_window * (i + 1);
|
||||
int start_frame = std::max(0, end_frame - animation_frames_per_char);
|
||||
|
||||
// Knowing animation bounds for current char and current frame, we calculate of animation
|
||||
// at this frame. This data is used to calcualte alpha and offset for the current char.
|
||||
int animation_progress = std::max(0, std::min(frame - start_frame, animation_frames_per_char));
|
||||
int alpha = (255 * (animation_progress)) / animation_frames_per_char;
|
||||
|
||||
// Offset is calculated based on alpha, maybe we could move out this consts.
|
||||
pt.offset_by((alpha / 30) - 8, 0);
|
||||
|
||||
ctx.set_fill_color(LG::Color(255, 255, 255, alpha));
|
||||
ctx.draw(pt, m_font.glyph(m_logo_text[i]));
|
||||
pt.offset_by(m_font.glyph(m_logo_text[i]).advance(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void LoadingScreen::run_intro_animation()
|
||||
{
|
||||
const int frames = 102; // 1.7s * 60fps
|
||||
size_t text_height = 64;
|
||||
size_t text_width = Helpers::text_width(m_logo_text, m_font);
|
||||
|
||||
// Preparing area for animation
|
||||
int animation_spread = 10;
|
||||
int content_min_x = m_screen.bounds().mid_x() - (text_width / 2);
|
||||
int content_min_y = m_screen.bounds().mid_y() - ((text_height + progress_line_height()) / 2);
|
||||
auto animation_rect = LG::Rect({ content_min_x - animation_spread, content_min_y, text_width + 2 * animation_spread, text_height });
|
||||
LG::Context ctx(m_screen.display_bitmap());
|
||||
|
||||
for (int frame = 0; frame < frames; frame++) {
|
||||
ctx.set_fill_color(LG::Color::Black);
|
||||
ctx.fill(animation_rect);
|
||||
animation_frame(ctx, { content_min_x, content_min_y }, frame, frames);
|
||||
m_screen.swap_buffers();
|
||||
|
||||
// Copying current progress to backing storage.
|
||||
auto* buf1_ptr = reinterpret_cast<uint32_t*>(&m_screen.display_bitmap()[content_min_y][content_min_x]);
|
||||
auto* buf2_ptr = reinterpret_cast<uint32_t*>(&m_screen.write_bitmap()[content_min_y][content_min_x]);
|
||||
for (int j = 0; j < text_height; j++) {
|
||||
LFoundation::fast_copy(buf2_ptr, buf1_ptr, text_width);
|
||||
buf1_ptr += m_screen.width();
|
||||
buf2_ptr += m_screen.width();
|
||||
}
|
||||
|
||||
// Going to sleep until the next frame.
|
||||
usleep(900000 / 60);
|
||||
}
|
||||
|
||||
m_progress_line_min_x = m_screen.bounds().mid_x() - (progress_line_width() / 2);
|
||||
m_progress_line_min_y = content_min_y + text_height;
|
||||
}
|
||||
|
||||
} // namespace WinServer
|
||||
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
#include "../../Devices/Screen.h"
|
||||
#include <libg/Color.h>
|
||||
#include <libg/Context.h>
|
||||
#include <libg/Font.h>
|
||||
#include <libg/PixelBitmap.h>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
template <typename T, int Cost = 1>
|
||||
class AfterComponentLoadProgress {
|
||||
public:
|
||||
static const int cost = Cost;
|
||||
static const size_t progress;
|
||||
};
|
||||
|
||||
static size_t total_cost = 0;
|
||||
template <int Cost>
|
||||
inline size_t __calc_total_cost()
|
||||
{
|
||||
return total_cost += Cost;
|
||||
}
|
||||
|
||||
template <typename T, int Cost>
|
||||
const size_t AfterComponentLoadProgress<T, Cost>::progress = __calc_total_cost<Cost>();
|
||||
|
||||
class LoadingScreen {
|
||||
public:
|
||||
inline static LoadingScreen& the()
|
||||
{
|
||||
extern LoadingScreen* s_WinServer_LoadingScreen_the;
|
||||
return *s_WinServer_LoadingScreen_the;
|
||||
}
|
||||
|
||||
inline static void destroy_the()
|
||||
{
|
||||
extern LoadingScreen* s_WinServer_LoadingScreen_the;
|
||||
delete s_WinServer_LoadingScreen_the;
|
||||
s_WinServer_LoadingScreen_the = nullptr;
|
||||
}
|
||||
|
||||
LoadingScreen();
|
||||
~LoadingScreen() = default;
|
||||
|
||||
template <typename T, int cost = 1>
|
||||
inline void move_progress() { display_status_bar(AfterComponentLoadProgress<T, cost>::progress, total_cost); }
|
||||
|
||||
private:
|
||||
static constexpr int progress_line_height() { return 4; }
|
||||
static constexpr int progress_line_width() { return 128; }
|
||||
|
||||
void display_status_bar(int current_progress, int max_progress);
|
||||
|
||||
void animation_frame(LG::Context& ctx, LG::Point<int> pt, int frame, int total_frames);
|
||||
void run_intro_animation();
|
||||
|
||||
std::string_view m_logo_text { "xOS" };
|
||||
|
||||
Screen& m_screen;
|
||||
int m_progress_line_min_x { 0 };
|
||||
int m_progress_line_min_y { 0 };
|
||||
LG::PixelBitmap m_logo;
|
||||
|
||||
LG::Font& m_font { LG::Font::system_bold_font(32) };
|
||||
};
|
||||
|
||||
} // namespace WinServer
|
||||
@@ -0,0 +1,112 @@
|
||||
#include "MenuBar.h"
|
||||
#include "../../Managers/Compositor.h"
|
||||
#include <libg/ImageLoaders/PNGLoader.h>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
MenuBar* s_WinServer_MenuBar_the = nullptr;
|
||||
|
||||
MenuBar::MenuBar()
|
||||
: m_background_color(LG::Color::Opaque)
|
||||
, m_bounds(0, 0, Screen::the().bounds().width(), height())
|
||||
, m_popup(Popup::the())
|
||||
{
|
||||
s_WinServer_MenuBar_the = this;
|
||||
LG::PNG::PNGLoader loader;
|
||||
}
|
||||
|
||||
void MenuBar::invalidate_widget(BaseWidget* wg)
|
||||
{
|
||||
for (int i = 0; i < m_widgets.size(); i++) {
|
||||
if (m_widgets[i] == wg) {
|
||||
size_t widget_min_x = widget_start_offset(i);
|
||||
Compositor::the().invalidate(LG::Rect(widget_min_x, 0, m_widgets[i]->width(), height()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MenuItemAnswer MenuBar::widget_recieve_mouse_status_change(const CursorManager& cursor_manager, size_t wind)
|
||||
{
|
||||
if (wind >= m_widgets.size()) {
|
||||
return MenuItemAnswer::Bad;
|
||||
}
|
||||
|
||||
MenuItemAnswer answer = MenuItemAnswer::Empty;
|
||||
size_t widget_min_x = widget_start_offset(wind);
|
||||
if (cursor_manager.pressed<CursorManager::Params::LeftButton>()) {
|
||||
answer = m_widgets[wind]->mouse_down(cursor_manager.x() - widget_min_x, cursor_manager.y());
|
||||
} else {
|
||||
answer = m_widgets[wind]->mouse_up();
|
||||
}
|
||||
|
||||
if (answer & MenuItemAnswer::Bad) {
|
||||
return answer;
|
||||
}
|
||||
if (answer & MenuItemAnswer::InvalidateMe) {
|
||||
Compositor::the().invalidate(LG::Rect(widget_min_x, 0, m_widgets[wind]->width(), height()));
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
MenuItemAnswer MenuBar::panel_item_recieve_mouse_status_change(const CursorManager& cursor_manager, size_t ind)
|
||||
{
|
||||
if (!m_menubar_content) {
|
||||
return MenuItemAnswer::Bad;
|
||||
}
|
||||
|
||||
auto& content = *m_menubar_content;
|
||||
if (ind >= content.size()) {
|
||||
return MenuItemAnswer::Bad;
|
||||
}
|
||||
|
||||
MenuItemAnswer answer = MenuItemAnswer::Empty;
|
||||
size_t item_min_x = panel_item_start_offset(ind);
|
||||
if (cursor_manager.pressed<CursorManager::Params::LeftButton>()) {
|
||||
answer = content[ind].mouse_down(cursor_manager.x() - item_min_x, cursor_manager.y());
|
||||
} else {
|
||||
answer = content[ind].mouse_up();
|
||||
}
|
||||
|
||||
if (answer & MenuItemAnswer::Bad) {
|
||||
return answer;
|
||||
}
|
||||
if (answer & MenuItemAnswer::InvalidateMe) {
|
||||
Compositor::the().invalidate(LG::Rect(item_min_x, 0, content[ind].width(), height()));
|
||||
}
|
||||
if (answer & MenuItemAnswer::PopupShow) {
|
||||
popup_will_be_shown(ind);
|
||||
}
|
||||
if (answer & MenuItemAnswer::PopupClose) {
|
||||
popup_will_be_closed();
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
void MenuBar::invalidate_menubar_panel(Compositor& compositor)
|
||||
{
|
||||
if (menubar_content()) {
|
||||
size_t inv_len = menubar_panel_width(*m_menubar_content);
|
||||
compositor.invalidate(menubar_panel_bounds());
|
||||
}
|
||||
}
|
||||
|
||||
void MenuBar::invalidate_menubar_panel()
|
||||
{
|
||||
invalidate_menubar_panel(Compositor::the());
|
||||
}
|
||||
|
||||
void MenuBar::set_style(StatusBarStyle ts)
|
||||
{
|
||||
set_background_color(ts.color());
|
||||
|
||||
if (ts.dark_text()) {
|
||||
m_text_color = LG::Color::DarkSystemText;
|
||||
} else {
|
||||
m_text_color = LG::Color::LightSystemText;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace WinServer
|
||||
266
userland/servers/window_server/src/Components/MenuBar/MenuBar.h
Normal file
266
userland/servers/window_server/src/Components/MenuBar/MenuBar.h
Normal file
@@ -0,0 +1,266 @@
|
||||
#pragma once
|
||||
#include "../../Managers/CursorManager.h"
|
||||
#include "MenuItem.h"
|
||||
#include "Widgets/BaseWidget.h"
|
||||
#include <libapi/window_server/MessageContent/MenuBar.h>
|
||||
#include <libfoundation/EventLoop.h>
|
||||
#include <libfoundation/Logger.h>
|
||||
#include <libg/Context.h>
|
||||
#include <libg/PixelBitmap.h>
|
||||
#include <libg/Point.h>
|
||||
#include <vector>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
struct PopupContext {
|
||||
|
||||
void discard() { opened = false; }
|
||||
|
||||
bool opened { false };
|
||||
int invoker_id { 0 };
|
||||
};
|
||||
|
||||
class WindowManager;
|
||||
|
||||
class MenuBar {
|
||||
friend WindowManager;
|
||||
|
||||
public:
|
||||
inline static MenuBar& the()
|
||||
{
|
||||
extern MenuBar* s_WinServer_MenuBar_the;
|
||||
return *s_WinServer_MenuBar_the;
|
||||
}
|
||||
|
||||
MenuBar();
|
||||
|
||||
static constexpr size_t height() { return 24; }
|
||||
static constexpr size_t padding() { return 8; }
|
||||
static constexpr size_t menubar_content_offset() { return padding() + 2; }
|
||||
static constexpr int popup_x_offset() { return -8; }
|
||||
static constexpr int text_y_offset() { return 2; }
|
||||
|
||||
void set_background_color(const LG::Color& clr) { m_background_color = clr; }
|
||||
void set_style(StatusBarStyle style);
|
||||
|
||||
size_t width() const { return m_bounds.width(); }
|
||||
LG::Rect& bounds() { return m_bounds; }
|
||||
const LG::Rect& bounds() const { return m_bounds; }
|
||||
|
||||
template <class T, class... Args>
|
||||
T& add_widget(Args&&... args)
|
||||
{
|
||||
T* widget = new T(args...);
|
||||
m_widgets.push_back(widget);
|
||||
return *widget;
|
||||
}
|
||||
|
||||
inline LFoundation::EventLoop& event_loop() { return LFoundation::EventLoop::the(); }
|
||||
void invalidate_widget(BaseWidget* wg);
|
||||
|
||||
inline bool is_hovered() const { return m_hovered; }
|
||||
inline PopupContext& popup_context() { return m_popup_context; }
|
||||
inline const PopupContext& popup_context() const { return m_popup_context; }
|
||||
inline LG::Rect menubar_panel_bounds() { return LG::Rect(menubar_content_offset() + popup_x_offset(), text_y_offset(), menubar_panel_width(*m_menubar_content) - popup_x_offset(), height() - 3); }
|
||||
|
||||
void draw_panel_items(LG::Context& ctx);
|
||||
void draw_widgets(LG::Context& ctx);
|
||||
[[gnu::always_inline]] inline void draw(LG::Context& ctx)
|
||||
{
|
||||
ctx.set_fill_color(m_background_color);
|
||||
ctx.fill({ 0, 0, MenuBar::width(), MenuBar::height() });
|
||||
|
||||
ctx.set_fill_color(LG::Color::LightSystemOpaque);
|
||||
if (m_menubar_content) {
|
||||
draw_panel_items(ctx);
|
||||
}
|
||||
|
||||
draw_widgets(ctx);
|
||||
}
|
||||
|
||||
inline void on_mouse_move(const CursorManager& cursor_manager) { m_hovered = true; }
|
||||
inline void on_mouse_leave(const CursorManager& cursor_manager) { m_hovered = false; }
|
||||
|
||||
void on_mouse_status_change(const CursorManager& cursor_manager);
|
||||
|
||||
inline std::vector<MenuDir>* menubar_content() const { return m_menubar_content; }
|
||||
|
||||
void set_menubar_content(std::vector<MenuDir>* mc);
|
||||
void set_menubar_content(std::vector<MenuDir>* mc, Compositor& compositor);
|
||||
|
||||
inline void popup_will_be_shown(int invoker_id)
|
||||
{
|
||||
auto& content = *m_menubar_content;
|
||||
popup_context().invoker_id = invoker_id;
|
||||
m_popup.show({ (int)panel_item_start_offset(invoker_id) + popup_x_offset(), height() + 2 }, content[invoker_id].items());
|
||||
}
|
||||
|
||||
inline void popup_will_be_closed()
|
||||
{
|
||||
popup_context().discard();
|
||||
m_popup.hide();
|
||||
}
|
||||
|
||||
private:
|
||||
// Widgets
|
||||
MenuItemAnswer widget_recieve_mouse_status_change(const CursorManager& cursor_manager, size_t wind);
|
||||
size_t widget_start_offset(size_t index);
|
||||
int find_widget(int x, int y);
|
||||
|
||||
// MenuBar Panel
|
||||
void invalidate_menubar_panel();
|
||||
void invalidate_menubar_panel(Compositor& compositor);
|
||||
MenuItemAnswer panel_item_recieve_mouse_status_change(const CursorManager& cursor_manager, size_t ind);
|
||||
static inline size_t menubar_panel_width(const std::vector<MenuDir>& items)
|
||||
{
|
||||
size_t width = 0;
|
||||
for (int ind = 0; ind < items.size(); ind++) {
|
||||
width += padding();
|
||||
width += items[ind].width();
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
size_t panel_item_start_offset(size_t index);
|
||||
int find_menubar_panel_item(int x, int y);
|
||||
|
||||
LG::Rect m_bounds;
|
||||
std::vector<MenuDir>* m_menubar_content { nullptr };
|
||||
std::vector<BaseWidget*> m_widgets;
|
||||
Popup& m_popup;
|
||||
PopupContext m_popup_context;
|
||||
LG::Color m_background_color;
|
||||
LG::Color m_text_color;
|
||||
|
||||
bool m_hovered { false };
|
||||
};
|
||||
|
||||
// Implementation
|
||||
|
||||
inline void MenuBar::draw_panel_items(LG::Context& ctx)
|
||||
{
|
||||
if (!m_menubar_content) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto offset = ctx.draw_offset();
|
||||
size_t start_offset = menubar_content_offset();
|
||||
auto& content = *m_menubar_content;
|
||||
|
||||
for (int ind = 0; ind < content.size(); ind++) {
|
||||
ctx.set_fill_color(m_text_color);
|
||||
ctx.set_draw_offset(LG::Point<int>(start_offset, text_y_offset()));
|
||||
content[ind].draw(ctx);
|
||||
start_offset += content[ind].width();
|
||||
start_offset += padding();
|
||||
}
|
||||
|
||||
ctx.set_draw_offset(offset);
|
||||
}
|
||||
|
||||
inline void MenuBar::draw_widgets(LG::Context& ctx)
|
||||
{
|
||||
auto offset = ctx.draw_offset();
|
||||
size_t start_offset = MenuBar::width() - padding();
|
||||
|
||||
for (int wind = m_widgets.size() - 1; wind >= 0; wind--) {
|
||||
ctx.set_fill_color(m_text_color);
|
||||
start_offset -= padding();
|
||||
start_offset -= m_widgets[wind]->width();
|
||||
ctx.set_draw_offset(LG::Point<int>(start_offset, text_y_offset()));
|
||||
m_widgets[wind]->draw(ctx);
|
||||
}
|
||||
|
||||
ctx.set_draw_offset(offset);
|
||||
}
|
||||
|
||||
inline void MenuBar::on_mouse_status_change(const CursorManager& cursor_manager)
|
||||
{
|
||||
// Checking recievers
|
||||
int target_widget = find_widget(cursor_manager.x(), cursor_manager.y());
|
||||
if (target_widget >= 0) {
|
||||
widget_recieve_mouse_status_change(cursor_manager, (size_t)target_widget);
|
||||
return;
|
||||
}
|
||||
|
||||
int target_panel_item = find_menubar_panel_item(cursor_manager.x(), cursor_manager.y());
|
||||
if (target_panel_item >= 0) {
|
||||
panel_item_recieve_mouse_status_change(cursor_manager, (size_t)target_panel_item);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
inline void MenuBar::set_menubar_content(std::vector<MenuDir>* mc)
|
||||
{
|
||||
invalidate_menubar_panel();
|
||||
m_menubar_content = mc;
|
||||
invalidate_menubar_panel();
|
||||
popup_will_be_closed();
|
||||
}
|
||||
|
||||
inline void MenuBar::set_menubar_content(std::vector<MenuDir>* mc, Compositor& compositor)
|
||||
{
|
||||
invalidate_menubar_panel(compositor);
|
||||
m_menubar_content = mc;
|
||||
invalidate_menubar_panel(compositor);
|
||||
popup_will_be_closed();
|
||||
}
|
||||
|
||||
inline size_t MenuBar::widget_start_offset(size_t index)
|
||||
{
|
||||
size_t start_offset = MenuBar::width() - padding();
|
||||
for (int wind = m_widgets.size() - 1; wind >= (int)index; wind--) {
|
||||
start_offset -= padding();
|
||||
start_offset -= m_widgets[wind]->width();
|
||||
}
|
||||
return start_offset;
|
||||
}
|
||||
|
||||
inline int MenuBar::find_widget(int x, int y)
|
||||
{
|
||||
size_t start_offset = MenuBar::width() - padding();
|
||||
for (int wind = m_widgets.size() - 1; wind >= 0; wind--) {
|
||||
start_offset -= padding();
|
||||
int end_offset = start_offset;
|
||||
start_offset -= m_widgets[wind]->width();
|
||||
if (start_offset <= x && x <= end_offset) {
|
||||
return wind;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
inline size_t MenuBar::panel_item_start_offset(size_t index)
|
||||
{
|
||||
if (!m_menubar_content) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto& content = *m_menubar_content;
|
||||
size_t start_offset = menubar_content_offset();
|
||||
for (int ind = 0; ind < index; ind++) {
|
||||
start_offset += padding();
|
||||
start_offset += content[ind].width();
|
||||
}
|
||||
return start_offset;
|
||||
}
|
||||
|
||||
inline int MenuBar::find_menubar_panel_item(int x, int y)
|
||||
{
|
||||
if (!m_menubar_content) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto& content = *m_menubar_content;
|
||||
size_t start_offset = menubar_content_offset();
|
||||
for (int ind = 0; ind < content.size(); ind++) {
|
||||
int end_offset = start_offset + content[ind].width();
|
||||
if (start_offset <= x && x <= end_offset) {
|
||||
return ind;
|
||||
}
|
||||
start_offset = end_offset + padding();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // namespace WinServer
|
||||
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
#include "../Helpers/TextDrawer.h"
|
||||
#include "../Popup/Popup.h"
|
||||
#include "MenuItemAnswer.h"
|
||||
#include <libfoundation/Logger.h>
|
||||
#include <libg/Context.h>
|
||||
#include <libg/Font.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
class MenuDir {
|
||||
public:
|
||||
MenuDir(const std::string& title, int id)
|
||||
: m_title(title)
|
||||
, m_id(id)
|
||||
{
|
||||
}
|
||||
|
||||
~MenuDir() = default;
|
||||
|
||||
inline void set_font(LG::Font& f) { m_font = f; }
|
||||
inline void add_item(PopupItem&& item) { m_items.push_back(std::move(item)); }
|
||||
inline void add_item(const PopupItem& item) { m_items.push_back(item); }
|
||||
|
||||
inline int id() const { return m_id; }
|
||||
inline void set_title(const std::string& title) { m_title = title; }
|
||||
inline void set_title(std::string&& title) { m_title = std::move(title); }
|
||||
inline const std::string& title() const { return m_title; }
|
||||
inline const PopupData& items() const { return m_items; }
|
||||
inline PopupData& items() { return m_items; }
|
||||
|
||||
inline size_t width() const { return Helpers::text_width(m_title, m_font); }
|
||||
|
||||
[[gnu::always_inline]] inline void draw(LG::Context& ctx)
|
||||
{
|
||||
ctx.set_fill_color(LG::Color::Black);
|
||||
Helpers::draw_text(ctx, { 0, 6 }, m_title, m_font);
|
||||
}
|
||||
|
||||
inline MenuItemAnswer mouse_down(int x, int y)
|
||||
{
|
||||
m_active = true;
|
||||
return MenuItemAnswer(MenuItemAnswer::InvalidateMe | MenuItemAnswer::PopupShow);
|
||||
}
|
||||
|
||||
inline MenuItemAnswer mouse_up()
|
||||
{
|
||||
return MenuItemAnswer::InvalidateMe;
|
||||
}
|
||||
|
||||
private:
|
||||
int m_id { -1 };
|
||||
bool m_active { false };
|
||||
std::string m_title;
|
||||
LG::Font& m_font { LG::Font::system_font() };
|
||||
PopupData m_items;
|
||||
};
|
||||
|
||||
} // namespace WinServer
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
// Common for widget and panel item.
|
||||
enum MenuItemAnswer {
|
||||
Empty = 0x0,
|
||||
Bad = 0x1, // Bad mark
|
||||
InvalidateMe = 0x2, // Asks to invalidate menu item
|
||||
PopupShow = 0x4, // Asks to show popup. MenuBar will call popup_rect()
|
||||
PopupClose = 0x8, // Asks to close popup.
|
||||
};
|
||||
|
||||
}; // namespace WinServer
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include "../MenuItemAnswer.h"
|
||||
#include <libg/Context.h>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
class BaseWidget {
|
||||
public:
|
||||
BaseWidget() = default;
|
||||
virtual ~BaseWidget() = default;
|
||||
|
||||
size_t height() { return 20; }
|
||||
virtual size_t width() { return 0; }
|
||||
virtual void draw(LG::Context& ctx) { }
|
||||
virtual MenuItemAnswer mouse_down(int x, int y) { return MenuItemAnswer::Empty; }
|
||||
virtual MenuItemAnswer mouse_up() { return MenuItemAnswer::Empty; }
|
||||
virtual void popup_rect(LG::Rect& r) { }
|
||||
};
|
||||
|
||||
} // namespace WinServer
|
||||
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
#include "../../../Helpers/TextDrawer.h"
|
||||
#include "../../MenuBar.h"
|
||||
#include "../BaseWidget.h"
|
||||
#include <ctime>
|
||||
#include <libfoundation/Logger.h>
|
||||
#include <libg/Font.h>
|
||||
|
||||
#define DATA_BUF 32
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
class Clock : public BaseWidget {
|
||||
public:
|
||||
Clock()
|
||||
{
|
||||
m_date = static_cast<char*>(malloc(DATA_BUF));
|
||||
memset(m_date, 0, DATA_BUF);
|
||||
update_time();
|
||||
MenuBar::the().event_loop().add(LFoundation::Timer([this] {
|
||||
this->update_time();
|
||||
},
|
||||
5000, LFoundation::Timer::Repeat));
|
||||
}
|
||||
|
||||
~Clock()
|
||||
{
|
||||
free(m_date);
|
||||
}
|
||||
|
||||
void update_time()
|
||||
{
|
||||
std::time_t new_time = std::time(nullptr);
|
||||
if (new_time / 60 != cur_time / 60) {
|
||||
cur_time = new_time;
|
||||
std::strftime(m_date, DATA_BUF, "%a %R", std::localtime(&cur_time));
|
||||
MenuBar::the().invalidate_widget(this);
|
||||
}
|
||||
}
|
||||
|
||||
size_t width() override { return 50; }
|
||||
void draw(LG::Context& ctx) override
|
||||
{
|
||||
size_t twidth = Helpers::text_width(m_date, m_font);
|
||||
Helpers::draw_text(ctx, { int(width() - twidth) / 2, 6 }, m_date, m_font);
|
||||
}
|
||||
|
||||
MenuItemAnswer mouse_down(int x, int y) override
|
||||
{
|
||||
m_clicked = true;
|
||||
return MenuItemAnswer::InvalidateMe;
|
||||
}
|
||||
|
||||
MenuItemAnswer mouse_up() override
|
||||
{
|
||||
m_clicked = false;
|
||||
return MenuItemAnswer::InvalidateMe;
|
||||
}
|
||||
|
||||
void popup_rect(LG::Rect& r) override { }
|
||||
|
||||
private:
|
||||
bool m_clicked { false };
|
||||
char* m_date { nullptr };
|
||||
std::time_t cur_time { 0 };
|
||||
LG::Font& m_font { LG::Font::system_font() };
|
||||
};
|
||||
|
||||
} // namespace WinServer
|
||||
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
#include "../../../Helpers/TextDrawer.h"
|
||||
#include "../../MenuBar.h"
|
||||
#include "../BaseWidget.h"
|
||||
#include <ctime>
|
||||
#include <libfoundation/Logger.h>
|
||||
#include <libg/Font.h>
|
||||
#include <libg/ImageLoaders/PNGLoader.h>
|
||||
|
||||
#define DATA_BUF 32
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
class ControlPanelToggle : public BaseWidget {
|
||||
public:
|
||||
ControlPanelToggle()
|
||||
{
|
||||
LG::PNG::PNGLoader loader;
|
||||
m_icon = loader.load_from_file("/res/system/contol_center.png");
|
||||
}
|
||||
|
||||
~ControlPanelToggle() = default;
|
||||
|
||||
constexpr size_t width() override { return 20; }
|
||||
constexpr size_t height() { return 12; }
|
||||
void draw(LG::Context& ctx) override
|
||||
{
|
||||
ctx.draw({ 4, 4 }, m_icon);
|
||||
}
|
||||
|
||||
MenuItemAnswer mouse_down(int x, int y) override
|
||||
{
|
||||
m_clicked = true;
|
||||
return MenuItemAnswer::InvalidateMe;
|
||||
}
|
||||
|
||||
MenuItemAnswer mouse_up() override
|
||||
{
|
||||
m_clicked = false;
|
||||
return MenuItemAnswer::InvalidateMe;
|
||||
}
|
||||
|
||||
void popup_rect(LG::Rect& r) override { }
|
||||
|
||||
private:
|
||||
LG::PixelBitmap m_icon;
|
||||
bool m_clicked { false };
|
||||
};
|
||||
|
||||
} // namespace WinServer
|
||||
@@ -0,0 +1,73 @@
|
||||
#include "Popup.h"
|
||||
#include "../../Constants/Colors.h"
|
||||
#include "../../Managers/WindowManager.h"
|
||||
#include "../Helpers/TextDrawer.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
Popup* s_WinServer_Popup_the = nullptr;
|
||||
|
||||
Popup::Popup()
|
||||
{
|
||||
s_WinServer_Popup_the = this;
|
||||
}
|
||||
|
||||
void Popup::on_set_data()
|
||||
{
|
||||
size_t max_width = 0;
|
||||
size_t max_height = 0;
|
||||
for (auto& item : m_data) {
|
||||
max_width = std::max(max_width, Helpers::text_width(item.text, m_font));
|
||||
max_height += m_font.size() + spacing();
|
||||
}
|
||||
max_width = std::max(max_width, min_width());
|
||||
bounds().set_width(max_width + 2 * spacing());
|
||||
bounds().set_height(max_height + spacing());
|
||||
}
|
||||
|
||||
void Popup::draw(LG::Context& ctx)
|
||||
{
|
||||
if (!visible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.set_fill_color(LG::Color::LightSystemOpaque);
|
||||
ctx.fill_rounded(bounds(), LG::CornerMask(LG::CornerMask::SystemRadius));
|
||||
|
||||
ctx.set_fill_color(Color::Shadow);
|
||||
ctx.draw_box_shading(bounds(), LG::Shading(LG::Shading::Type::Box, 0, LG::Shading::SystemSpread), LG::CornerMask(LG::CornerMask::SystemRadius));
|
||||
|
||||
const size_t line_height = (m_font.size() + spacing());
|
||||
int height = bounds().min_y() + spacing();
|
||||
|
||||
for (int i = 0; i < m_data.size(); i++) {
|
||||
if (i == m_hovered_item) {
|
||||
ctx.set_fill_color(LG::Color::White);
|
||||
ctx.fill_rounded(LG::Rect(bounds().min_x() + 4, height - spacing() / 2, bounds().width() - 8, line_height), LG::CornerMask(2));
|
||||
}
|
||||
ctx.set_fill_color(LG::Color::DarkSystemText);
|
||||
Helpers::draw_text(ctx, { bounds().min_x() + spacing(), height }, m_data[i].text, m_font);
|
||||
height += line_height;
|
||||
}
|
||||
}
|
||||
|
||||
void Popup::set_preferred_origin(const LG::Point<int>& origin)
|
||||
{
|
||||
auto& wm = WindowManager::the();
|
||||
LG::Point<int> pos;
|
||||
int x = origin.x();
|
||||
int y = origin.y();
|
||||
|
||||
if (origin.y() + bounds().height() > wm.visible_area().max_y()) {
|
||||
y = origin.y() - bounds().height();
|
||||
}
|
||||
if (origin.x() + bounds().width() > wm.visible_area().max_x()) {
|
||||
x = origin.x() - bounds().width();
|
||||
}
|
||||
pos.set_x(x);
|
||||
pos.set_y(y);
|
||||
m_bounds.set_origin(std::move(pos));
|
||||
}
|
||||
|
||||
} // namespace WinServer
|
||||
147
userland/servers/window_server/src/Components/Popup/Popup.h
Normal file
147
userland/servers/window_server/src/Components/Popup/Popup.h
Normal file
@@ -0,0 +1,147 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../Managers/Compositor.h"
|
||||
#include "../../Managers/CursorManager.h"
|
||||
#include <functional>
|
||||
#include <libg/Color.h>
|
||||
#include <libg/Context.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
struct PopupItem {
|
||||
int id;
|
||||
std::string text;
|
||||
std::function<void(int)> callback;
|
||||
|
||||
static const int InternalId = -1;
|
||||
};
|
||||
using PopupData = std::vector<PopupItem>;
|
||||
|
||||
class Popup {
|
||||
public:
|
||||
inline static Popup& the()
|
||||
{
|
||||
extern Popup* s_WinServer_Popup_the;
|
||||
return *s_WinServer_Popup_the;
|
||||
}
|
||||
|
||||
Popup();
|
||||
~Popup() = default;
|
||||
|
||||
constexpr int spacing() const { return 8; }
|
||||
constexpr size_t min_width() const { return 180u; }
|
||||
|
||||
void set_preferred_origin(const LG::Point<int>& origin);
|
||||
|
||||
inline LG::Rect& bounds() { return m_bounds; }
|
||||
inline const LG::Rect& bounds() const { return m_bounds; }
|
||||
|
||||
inline LG::Rect draw_frame() const
|
||||
{
|
||||
const int shadow_spread = LG::Shading::SystemSpread;
|
||||
return LG::Rect(bounds().min_x() - shadow_spread, bounds().min_y() - shadow_spread, bounds().width() + 2 * shadow_spread, bounds().height() + 2 * shadow_spread);
|
||||
}
|
||||
|
||||
inline void set_visible(bool vis)
|
||||
{
|
||||
if (m_visible != vis) {
|
||||
Compositor::the().invalidate(draw_frame());
|
||||
}
|
||||
m_visible = vis;
|
||||
}
|
||||
inline bool visible() const { return m_visible; }
|
||||
|
||||
void show(const LG::Point<int>& origin, PopupData& data)
|
||||
{
|
||||
set_visible(false);
|
||||
set_data(data);
|
||||
set_preferred_origin(origin);
|
||||
set_visible(true);
|
||||
}
|
||||
|
||||
void hide()
|
||||
{
|
||||
m_hovered_item = HoveredItem::No;
|
||||
set_visible(false);
|
||||
}
|
||||
|
||||
void on_set_data();
|
||||
void set_data(const PopupData& data) { m_data = data, on_set_data(); }
|
||||
void set_data(PopupData&& data) { m_data = std::move(data), on_set_data(); }
|
||||
|
||||
void on_mouse_move(const CursorManager& cursor_manager)
|
||||
{
|
||||
// A simple implemetation to get hover effect and clicks.
|
||||
if (!visible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t data_size = m_data.size();
|
||||
const size_t line_height = (m_font.size() + 8);
|
||||
int prev_hovered_item = m_hovered_item;
|
||||
int rel_y = cursor_manager.y() - bounds().min_y();
|
||||
rel_y -= 4; // Offset of the first element;
|
||||
m_hovered_item = rel_y / line_height;
|
||||
|
||||
if (m_hovered_item >= data_size) {
|
||||
m_hovered_item = HoveredItem::No;
|
||||
}
|
||||
|
||||
if (m_hovered_item != prev_hovered_item && prev_hovered_item != HoveredItem::No) {
|
||||
Compositor::the().invalidate(LG::Rect(bounds().min_x(), bounds().min_y() + 4 + line_height * prev_hovered_item, bounds().width(), line_height));
|
||||
}
|
||||
|
||||
if (m_hovered_item != prev_hovered_item && m_hovered_item != HoveredItem::No) {
|
||||
Compositor::the().invalidate(LG::Rect(bounds().min_x(), bounds().min_y() + 4 + line_height * m_hovered_item, bounds().width(), line_height));
|
||||
}
|
||||
}
|
||||
|
||||
inline void on_mouse_status_change(const CursorManager& cursor_manager)
|
||||
{
|
||||
if (m_hovered_item == HoveredItem::No) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!visible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& data = m_data;
|
||||
if (cursor_manager.pressed<CursorManager::Params::LeftButton>()) {
|
||||
data[m_hovered_item].callback((int)data[m_hovered_item].id);
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
void on_mouse_leave(const CursorManager& cursor_manager)
|
||||
{
|
||||
if (m_hovered_item == HoveredItem::No) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!visible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t line_height = (m_font.size() + 8);
|
||||
Compositor::the().invalidate(LG::Rect(bounds().min_x(), bounds().min_y() + 4 + line_height * m_hovered_item, bounds().width(), line_height));
|
||||
m_hovered_item = HoveredItem::No;
|
||||
}
|
||||
|
||||
void draw(LG::Context& ctx);
|
||||
|
||||
private:
|
||||
enum HoveredItem {
|
||||
No = -1,
|
||||
};
|
||||
|
||||
LG::Rect m_bounds { 0, 0, 0, 0 };
|
||||
bool m_visible { false };
|
||||
PopupData m_data;
|
||||
int m_hovered_item { HoveredItem::No };
|
||||
LG::Font& m_font { LG::Font::system_font() };
|
||||
};
|
||||
|
||||
} // namespace WinServer
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
enum class ViolationClass {
|
||||
Ignorable,
|
||||
Moderate,
|
||||
Serious,
|
||||
};
|
||||
|
||||
} // namespace WinServer
|
||||
12
userland/servers/window_server/src/Constants/Colors.h
Normal file
12
userland/servers/window_server/src/Constants/Colors.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
enum Color {
|
||||
Shadow = 0xf4000000,
|
||||
InactiveText = 0x7B7E78,
|
||||
};
|
||||
|
||||
} // namespace WinServer
|
||||
35
userland/servers/window_server/src/Devices/Devices.cpp
Normal file
35
userland/servers/window_server/src/Devices/Devices.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "Devices.h"
|
||||
#include <fcntl.h>
|
||||
#include <libfoundation/Logger.h>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
Devices* s_WinServer_Devices_the = nullptr;
|
||||
|
||||
Devices::Devices()
|
||||
{
|
||||
s_WinServer_Devices_the = this;
|
||||
m_mouse_fd = open("/dev/mouse", O_RDONLY);
|
||||
if (m_mouse_fd < 0) {
|
||||
Logger::debug << "Can't open mouse" << std::endl;
|
||||
} else {
|
||||
LFoundation::EventLoop::the().add(
|
||||
m_mouse_fd, [] {
|
||||
Devices::the().pump_mouse();
|
||||
},
|
||||
nullptr);
|
||||
}
|
||||
|
||||
m_keyboard_fd = open("/dev/kbd", O_RDONLY);
|
||||
if (m_keyboard_fd < 0) {
|
||||
Logger::debug << "Can't open keyboard" << std::endl;
|
||||
} else {
|
||||
LFoundation::EventLoop::the().add(
|
||||
m_keyboard_fd, [] {
|
||||
Devices::the().pump_keyboard();
|
||||
},
|
||||
nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace WinServer
|
||||
59
userland/servers/window_server/src/Devices/Devices.h
Normal file
59
userland/servers/window_server/src/Devices/Devices.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
#include "../IPC/Event.h"
|
||||
#include "../Managers/WindowManager.h"
|
||||
#include <libfoundation/EventLoop.h>
|
||||
#include <memory>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
class Devices {
|
||||
public:
|
||||
inline static Devices& the()
|
||||
{
|
||||
extern Devices* s_WinServer_Devices_the;
|
||||
return *s_WinServer_Devices_the;
|
||||
}
|
||||
|
||||
Devices();
|
||||
~Devices() = default;
|
||||
|
||||
inline void pump_mouse() const
|
||||
{
|
||||
LFoundation::EventLoop& el = LFoundation::EventLoop::the();
|
||||
WindowManager& wm = WindowManager::the();
|
||||
|
||||
char buf[512];
|
||||
int read_cnt = read(m_mouse_fd, buf, sizeof(buf));
|
||||
if (read_cnt <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* packet_buf = reinterpret_cast<MousePacket*>(buf);
|
||||
for (int offset = 0, cnt = 0; offset < read_cnt; offset += sizeof(MousePacket), cnt++) {
|
||||
el.add(wm, new MouseEvent(packet_buf[cnt]));
|
||||
}
|
||||
}
|
||||
|
||||
inline void pump_keyboard() const
|
||||
{
|
||||
LFoundation::EventLoop& el = LFoundation::EventLoop::the();
|
||||
WindowManager& wm = WindowManager::the();
|
||||
|
||||
char buf[512];
|
||||
int read_cnt = read(m_keyboard_fd, buf, sizeof(buf));
|
||||
if (read_cnt <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* packet_buf = reinterpret_cast<KeyboardPacket*>(buf);
|
||||
for (int offset = 0, cnt = 0; offset < read_cnt; offset += sizeof(KeyboardPacket), cnt++) {
|
||||
el.add(wm, new KeyboardEvent(packet_buf[cnt]));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int m_mouse_fd;
|
||||
int m_keyboard_fd;
|
||||
};
|
||||
|
||||
} // namespace WinServer
|
||||
42
userland/servers/window_server/src/Devices/Screen.cpp
Normal file
42
userland/servers/window_server/src/Devices/Screen.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "Screen.h"
|
||||
#include "../Managers/Compositor.h"
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <utility>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
Screen* s_WinServer_Screen_the = nullptr;
|
||||
|
||||
Screen::Screen()
|
||||
: m_depth(4)
|
||||
, m_write_bitmap()
|
||||
, m_display_bitmap()
|
||||
{
|
||||
s_WinServer_Screen_the = this;
|
||||
m_screen_fd = open("/dev/bga", O_RDWR);
|
||||
m_bounds = LG::Rect(0, 0, ioctl(m_screen_fd, BGA_GET_WIDTH, 0), ioctl(m_screen_fd, BGA_GET_HEIGHT, 0));
|
||||
|
||||
size_t screen_buffer_size = width() * height() * depth();
|
||||
auto* first_buffer = reinterpret_cast<LG::Color*>(mmap(NULL, 1, PROT_READ | PROT_WRITE, MAP_SHARED, m_screen_fd, 0));
|
||||
auto* second_buffer = reinterpret_cast<LG::Color*>(reinterpret_cast<uint8_t*>(first_buffer) + screen_buffer_size);
|
||||
|
||||
m_display_bitmap = LG::PixelBitmap(first_buffer, width(), height());
|
||||
m_write_bitmap = LG::PixelBitmap(second_buffer, width(), height());
|
||||
|
||||
m_display_bitmap_ptr = &m_display_bitmap;
|
||||
m_write_bitmap_ptr = &m_write_bitmap;
|
||||
|
||||
m_active_buffer = 0;
|
||||
}
|
||||
|
||||
void Screen::swap_buffers()
|
||||
{
|
||||
m_write_bitmap_ptr.swap(m_display_bitmap_ptr);
|
||||
m_active_buffer ^= 1;
|
||||
ioctl(m_screen_fd, BGA_SWAP_BUFFERS, m_active_buffer);
|
||||
}
|
||||
|
||||
} // namespace WinServer
|
||||
45
userland/servers/window_server/src/Devices/Screen.h
Normal file
45
userland/servers/window_server/src/Devices/Screen.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
#include <libg/Color.h>
|
||||
#include <libg/PixelBitmap.h>
|
||||
#include <memory>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
class Screen {
|
||||
public:
|
||||
inline static Screen& the()
|
||||
{
|
||||
extern Screen* s_WinServer_Screen_the;
|
||||
return *s_WinServer_Screen_the;
|
||||
}
|
||||
|
||||
Screen();
|
||||
|
||||
void swap_buffers();
|
||||
|
||||
inline size_t width() { return m_bounds.width(); }
|
||||
inline size_t height() const { return m_bounds.height(); }
|
||||
inline LG::Rect& bounds() { return m_bounds; }
|
||||
inline const LG::Rect& bounds() const { return m_bounds; }
|
||||
inline uint32_t depth() const { return m_depth; }
|
||||
|
||||
inline LG::PixelBitmap& write_bitmap() { return *m_write_bitmap_ptr; }
|
||||
inline const LG::PixelBitmap& write_bitmap() const { return *m_write_bitmap_ptr; }
|
||||
inline LG::PixelBitmap& display_bitmap() { return *m_display_bitmap_ptr; }
|
||||
inline const LG::PixelBitmap& display_bitmap() const { return *m_display_bitmap_ptr; }
|
||||
|
||||
private:
|
||||
int m_screen_fd;
|
||||
LG::Rect m_bounds;
|
||||
uint32_t m_depth;
|
||||
|
||||
int m_active_buffer;
|
||||
|
||||
LG::PixelBitmap m_write_bitmap;
|
||||
LG::PixelBitmap m_display_bitmap;
|
||||
|
||||
std::unique_ptr<LG::PixelBitmap> m_write_bitmap_ptr { nullptr };
|
||||
std::unique_ptr<LG::PixelBitmap> m_display_bitmap_ptr { nullptr };
|
||||
};
|
||||
|
||||
} // namespace WinServer
|
||||
46
userland/servers/window_server/src/IPC/Connection.cpp
Normal file
46
userland/servers/window_server/src/IPC/Connection.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include "Connection.h"
|
||||
#include "Event.h"
|
||||
#include <libfoundation/EventLoop.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#define WINSERVER_REQUEST_SOCKET_PATH "/tmp/winserver_requests.sock"
|
||||
#define WINSERVER_REQUEST_SOCKET_PATH_SIZE sizeof(WINSERVER_REQUEST_SOCKET_PATH)
|
||||
|
||||
#define WINSERVER_RESPONSE_SOCKET_PATH "/tmp/winserver_response.sock"
|
||||
#define WINSERVER_RESPONSE_SOCKET_PATH_SIZE sizeof(WINSERVER_RESPONSE_SOCKET_PATH)
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
Connection* s_WinServer_Connection_the = nullptr;
|
||||
|
||||
Connection::Connection()
|
||||
: m_connection(LIPC::DoubleSidedConnection(socket(PF_LOCAL, 0, 0), socket(PF_LOCAL, 0, 0)))
|
||||
, m_server_decoder()
|
||||
, m_client_decoder()
|
||||
, m_connection_with_clients(m_connection, m_server_decoder, m_client_decoder)
|
||||
{
|
||||
s_WinServer_Connection_the = this;
|
||||
int err1 = bind(m_connection.c2s_fd(), WINSERVER_REQUEST_SOCKET_PATH, WINSERVER_REQUEST_SOCKET_PATH_SIZE);
|
||||
int err2 = bind(m_connection.s2c_fd(), WINSERVER_RESPONSE_SOCKET_PATH, WINSERVER_RESPONSE_SOCKET_PATH_SIZE);
|
||||
|
||||
if (!err1 && !err2) {
|
||||
LFoundation::EventLoop::the().add(
|
||||
m_connection.c2s_fd(), [] {
|
||||
Connection::the().listen();
|
||||
},
|
||||
nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::receive_event(std::unique_ptr<LFoundation::Event> event)
|
||||
{
|
||||
switch (event->type()) {
|
||||
case WinServer::Event::Type::SendEvent: {
|
||||
std::unique_ptr<SendEvent> send_event = std::move(event);
|
||||
m_connection_with_clients.send_message(*send_event->message());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace WinServer
|
||||
37
userland/servers/window_server/src/IPC/Connection.h
Normal file
37
userland/servers/window_server/src/IPC/Connection.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include "ServerDecoder.h"
|
||||
#include <libapi/window_server/Connections/WSConnection.h>
|
||||
#include <libfoundation/EventReceiver.h>
|
||||
#include <libipc/DoubleSidedConnection.h>
|
||||
#include <libipc/ServerConnection.h>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
class Connection : public LFoundation::EventReceiver {
|
||||
public:
|
||||
inline static Connection& the()
|
||||
{
|
||||
extern Connection* s_WinServer_Connection_the;
|
||||
return *s_WinServer_Connection_the;
|
||||
}
|
||||
|
||||
Connection();
|
||||
|
||||
inline void listen()
|
||||
{
|
||||
m_connection_with_clients.pump_messages();
|
||||
}
|
||||
|
||||
inline bool send_async_message(const Message& msg) const { return m_connection_with_clients.send_message(msg); }
|
||||
inline int alloc_connection() { return ++m_connections_number; }
|
||||
void receive_event(std::unique_ptr<LFoundation::Event> event) override;
|
||||
|
||||
private:
|
||||
LIPC::DoubleSidedConnection m_connection;
|
||||
int m_connections_number { 0 };
|
||||
ServerConnection<WindowServerDecoder, BaseWindowClientDecoder> m_connection_with_clients;
|
||||
WindowServerDecoder m_server_decoder;
|
||||
BaseWindowClientDecoder m_client_decoder;
|
||||
};
|
||||
|
||||
} // namespace WinServer
|
||||
102
userland/servers/window_server/src/IPC/Event.h
Normal file
102
userland/servers/window_server/src/IPC/Event.h
Normal file
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
#include <libfoundation/Event.h>
|
||||
#include <libipc/Message.h>
|
||||
#include <memory>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
class Event : public LFoundation::Event {
|
||||
public:
|
||||
enum Type {
|
||||
Invalid = 0x1000,
|
||||
MouseEvent,
|
||||
KeyboardEvent,
|
||||
SendEvent,
|
||||
Other,
|
||||
};
|
||||
|
||||
explicit Event(int type)
|
||||
: LFoundation::Event(type)
|
||||
{
|
||||
}
|
||||
|
||||
~Event() = default;
|
||||
};
|
||||
|
||||
struct MousePacket {
|
||||
int16_t x_offset;
|
||||
int16_t y_offset;
|
||||
uint16_t button_states;
|
||||
int16_t wheel_data;
|
||||
};
|
||||
|
||||
struct KeyboardPacket {
|
||||
uint32_t key;
|
||||
};
|
||||
|
||||
class MouseEvent : public WinServer::Event {
|
||||
public:
|
||||
explicit MouseEvent(const MousePacket& packet)
|
||||
: WinServer::Event(WinServer::Event::Type::MouseEvent)
|
||||
, m_packet(packet)
|
||||
{
|
||||
}
|
||||
|
||||
~MouseEvent() = default;
|
||||
|
||||
const MousePacket& packet() const { return m_packet; }
|
||||
MousePacket& packet() { return m_packet; }
|
||||
|
||||
private:
|
||||
MousePacket m_packet;
|
||||
};
|
||||
|
||||
class KeyboardEvent : public WinServer::Event {
|
||||
public:
|
||||
explicit KeyboardEvent(const KeyboardPacket& packet)
|
||||
: WinServer::Event(WinServer::Event::Type::KeyboardEvent)
|
||||
, m_packet(packet)
|
||||
{
|
||||
}
|
||||
|
||||
~KeyboardEvent() = default;
|
||||
|
||||
const KeyboardPacket& packet() const { return m_packet; }
|
||||
KeyboardPacket& packet() { return m_packet; }
|
||||
|
||||
private:
|
||||
KeyboardPacket m_packet;
|
||||
};
|
||||
|
||||
class SendEvent : public WinServer::Event {
|
||||
public:
|
||||
explicit SendEvent(Message* msg)
|
||||
: WinServer::Event(WinServer::Event::Type::SendEvent)
|
||||
, m_message(msg)
|
||||
{
|
||||
}
|
||||
|
||||
SendEvent(SendEvent&& ev)
|
||||
: WinServer::Event(WinServer::Event::Type::SendEvent)
|
||||
, m_message(std::move(ev.m_message))
|
||||
{
|
||||
}
|
||||
|
||||
SendEvent& operator=(SendEvent&& ev)
|
||||
{
|
||||
m_message = std::move(ev.m_message);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~SendEvent() = default;
|
||||
|
||||
const std::unique_ptr<Message>& message() const { return m_message; }
|
||||
std::unique_ptr<Message>& message() { return m_message; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<Message> m_message;
|
||||
};
|
||||
|
||||
} // namespace WinServer
|
||||
220
userland/servers/window_server/src/IPC/ServerDecoder.cpp
Normal file
220
userland/servers/window_server/src/IPC/ServerDecoder.cpp
Normal file
@@ -0,0 +1,220 @@
|
||||
#include "ServerDecoder.h"
|
||||
#include "../Components/Security/Violations.h"
|
||||
#include "../Managers/WindowManager.h"
|
||||
#include "../Target/Generic/Window.h"
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
std::unique_ptr<Message> WindowServerDecoder::handle(GreetMessage& msg)
|
||||
{
|
||||
return new GreetMessageReply(msg.key(), Connection::the().alloc_connection());
|
||||
}
|
||||
|
||||
#ifdef TARGET_DESKTOP
|
||||
std::unique_ptr<Message> WindowServerDecoder::handle(CreateWindowMessage& msg)
|
||||
{
|
||||
auto& wm = WindowManager::the();
|
||||
auto& compositor = Compositor::the();
|
||||
int win_id = wm.next_win_id();
|
||||
auto* window = new Desktop::Window(msg.key(), win_id, msg);
|
||||
window->set_app_title(msg.title().move_string());
|
||||
window->set_icon_path(msg.icon_path().move_string());
|
||||
window->set_style(StatusBarStyle(msg.menubar_style(), msg.color()));
|
||||
wm.add_window(window);
|
||||
if (window->type() == WindowType::Standard) {
|
||||
// After moving windows, we have to invalidate bounds() to make sure that
|
||||
// the whole window is rendered (coords are changes after move).
|
||||
wm.move_window(window, 8 * win_id, MenuBar::height() + 8 * win_id);
|
||||
compositor.invalidate(window->bounds());
|
||||
}
|
||||
|
||||
wm.notify_window_icon_changed(window->id());
|
||||
wm.notify_window_title_changed(window->id());
|
||||
return new CreateWindowMessageReply(msg.key(), win_id);
|
||||
}
|
||||
#elif TARGET_MOBILE
|
||||
std::unique_ptr<Message> WindowServerDecoder::handle(CreateWindowMessage& msg)
|
||||
{
|
||||
auto& wm = WindowManager::the();
|
||||
int win_id = wm.next_win_id();
|
||||
auto* window = new Mobile::Window(msg.key(), win_id, msg);
|
||||
window->set_style(StatusBarStyle(msg.menubar_style(), msg.color()));
|
||||
window->set_icon_path(msg.icon_path().move_string());
|
||||
window->set_style(StatusBarStyle(msg.menubar_style(), msg.color()));
|
||||
wm.add_window(window);
|
||||
wm.move_window(window, 0, MenuBar::height());
|
||||
wm.notify_window_icon_changed(window->id());
|
||||
wm.notify_window_title_changed(window->id());
|
||||
return new CreateWindowMessageReply(msg.key(), win_id);
|
||||
}
|
||||
#endif
|
||||
|
||||
std::unique_ptr<Message> WindowServerDecoder::handle(SetBufferMessage& msg)
|
||||
{
|
||||
auto* window = WindowManager::the().window(msg.window_id());
|
||||
if (!window) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LG::Size new_size = { msg.bounds().width(), msg.bounds().height() };
|
||||
window->did_size_change(new_size);
|
||||
window->set_buffer(msg.buffer_id(), new_size, LG::PixelBitmapFormat(msg.format()));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Message> WindowServerDecoder::handle(DestroyWindowMessage& msg)
|
||||
{
|
||||
auto& wm = WindowManager::the();
|
||||
auto* window = wm.window(msg.window_id());
|
||||
if (!window) {
|
||||
wm.on_window_misbehave(*window, ViolationClass::Ignorable);
|
||||
return new DestroyWindowMessageReply(msg.key(), 1);
|
||||
}
|
||||
|
||||
if (window->connection_id() != msg.key()) {
|
||||
wm.on_window_misbehave(*window, ViolationClass::Serious);
|
||||
return new DestroyWindowMessageReply(msg.key(), 1);
|
||||
}
|
||||
wm.remove_window(window);
|
||||
return new DestroyWindowMessageReply(msg.key(), 0);
|
||||
}
|
||||
|
||||
std::unique_ptr<Message> WindowServerDecoder::handle(InvalidateMessage& msg)
|
||||
{
|
||||
auto& wm = WindowManager::the();
|
||||
auto* window = wm.window(msg.window_id());
|
||||
if (!window) {
|
||||
return nullptr;
|
||||
}
|
||||
auto rect = msg.rect();
|
||||
rect.offset_by(window->content_bounds().origin());
|
||||
rect.intersect(window->content_bounds());
|
||||
Compositor::the().invalidate(rect);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef TARGET_DESKTOP
|
||||
std::unique_ptr<Message> WindowServerDecoder::handle(SetTitleMessage& msg)
|
||||
{
|
||||
auto& wm = WindowManager::the();
|
||||
auto* window = wm.window(msg.window_id());
|
||||
if (!window) {
|
||||
return nullptr;
|
||||
}
|
||||
window->set_app_title(msg.title().string());
|
||||
|
||||
auto& compositor = Compositor::the();
|
||||
compositor.invalidate(compositor.menu_bar().bounds());
|
||||
wm.notify_window_title_changed(window->id());
|
||||
return nullptr;
|
||||
}
|
||||
#elif TARGET_MOBILE
|
||||
std::unique_ptr<Message> WindowServerDecoder::handle(SetTitleMessage& msg)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::unique_ptr<Message> WindowServerDecoder::handle(SetBarStyleMessage& msg)
|
||||
{
|
||||
auto& wm = WindowManager::the();
|
||||
auto* window = wm.window(msg.window_id());
|
||||
if (!window) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
window->set_style(StatusBarStyle(msg.menubar_style(), msg.color()));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef TARGET_DESKTOP
|
||||
std::unique_ptr<Message> WindowServerDecoder::handle(MenuBarCreateMenuMessage& msg)
|
||||
{
|
||||
auto& wm = WindowManager::the();
|
||||
auto* window = wm.window(msg.window_id());
|
||||
if (!window) {
|
||||
return new MenuBarCreateMenuMessageReply(msg.key(), -1, 0);
|
||||
}
|
||||
|
||||
int id = window->menubar_content().size();
|
||||
window->menubar_content().push_back(MenuDir(msg.title().string(), id));
|
||||
window->on_menubar_change();
|
||||
return new MenuBarCreateMenuMessageReply(msg.key(), 0, id);
|
||||
}
|
||||
|
||||
std::unique_ptr<Message> WindowServerDecoder::handle(MenuBarCreateItemMessage& msg)
|
||||
{
|
||||
auto& wm = WindowManager::the();
|
||||
auto* window = wm.window(msg.window_id());
|
||||
if (!window) {
|
||||
return new MenuBarCreateItemMessageReply(msg.key(), -1);
|
||||
}
|
||||
|
||||
auto menu_id = msg.menu_id();
|
||||
if (menu_id == 0 || window->menubar_content().size() <= menu_id) {
|
||||
return new MenuBarCreateItemMessageReply(msg.key(), -2);
|
||||
}
|
||||
|
||||
auto callback = [window, menu_id](int item_id) { LFoundation::EventLoop::the().add(Connection::the(),
|
||||
new SendEvent(new MenuBarActionMessage(window->connection_id(), window->id(), menu_id, item_id))); };
|
||||
window->menubar_content()[menu_id].add_item(PopupItem { msg.item_id(), msg.title().string(), callback });
|
||||
// TODO: Currently we don't redraw popup after a new item was added.
|
||||
return new MenuBarCreateItemMessageReply(msg.key(), 0);
|
||||
}
|
||||
#elif TARGET_MOBILE
|
||||
std::unique_ptr<Message> WindowServerDecoder::handle(MenuBarCreateMenuMessage& msg)
|
||||
{
|
||||
return new MenuBarCreateMenuMessageReply(msg.key(), -100, 0);
|
||||
}
|
||||
|
||||
std::unique_ptr<Message> WindowServerDecoder::handle(MenuBarCreateItemMessage& msg)
|
||||
{
|
||||
return new MenuBarCreateItemMessageReply(msg.key(), -100);
|
||||
}
|
||||
#endif
|
||||
|
||||
std::unique_ptr<Message> WindowServerDecoder::handle(PopupShowMenuMessage& msg)
|
||||
{
|
||||
auto& wm = WindowManager::the();
|
||||
auto* window = wm.window(msg.window_id());
|
||||
|
||||
if (!window) {
|
||||
return new PopupShowMenuMessageReply(msg.key(), -1, -1);
|
||||
}
|
||||
|
||||
std::vector<std::string> res;
|
||||
for (int i = 0; i < msg.data().vector().size(); i++) {
|
||||
res.push_back(msg.data().vector()[i].move_string());
|
||||
}
|
||||
|
||||
PopupData popup_data;
|
||||
int item_id = 0;
|
||||
int menu_id = 0;
|
||||
|
||||
for (auto& title : res) {
|
||||
auto callback = [window, menu_id](int item_id) { LFoundation::EventLoop::the().add(Connection::the(),
|
||||
new SendEvent(new PopupActionMessage(window->connection_id(), window->id(), menu_id, item_id))); };
|
||||
popup_data.push_back(PopupItem { item_id, title, callback });
|
||||
item_id++;
|
||||
}
|
||||
|
||||
wm.popup().show({ msg.point() }, popup_data);
|
||||
return new PopupShowMenuMessageReply(msg.key(), 0, 0);
|
||||
}
|
||||
|
||||
std::unique_ptr<Message> WindowServerDecoder::handle(AskBringToFrontMessage& msg)
|
||||
{
|
||||
auto& wm = WindowManager::the();
|
||||
auto* window = wm.window(msg.window_id());
|
||||
auto* target_window = wm.window(msg.target_window_id());
|
||||
if (!window || !target_window) {
|
||||
return nullptr;
|
||||
}
|
||||
if (window->type() == WindowType::Homescreen) {
|
||||
// Only dock can ask for that now.
|
||||
wm.ask_to_set_active_window(*target_window);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace WinServer
|
||||
26
userland/servers/window_server/src/IPC/ServerDecoder.h
Normal file
26
userland/servers/window_server/src/IPC/ServerDecoder.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include "../Managers/Compositor.h"
|
||||
#include <libapi/window_server/Connections/WSConnection.h>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
class WindowServerDecoder : public BaseWindowServerDecoder {
|
||||
public:
|
||||
WindowServerDecoder() = default;
|
||||
~WindowServerDecoder() = default;
|
||||
|
||||
using BaseWindowServerDecoder::handle;
|
||||
virtual std::unique_ptr<Message> handle(GreetMessage& msg) override;
|
||||
virtual std::unique_ptr<Message> handle(CreateWindowMessage& msg) override;
|
||||
virtual std::unique_ptr<Message> handle(DestroyWindowMessage& msg) override;
|
||||
virtual std::unique_ptr<Message> handle(SetBarStyleMessage& msg) override;
|
||||
virtual std::unique_ptr<Message> handle(SetTitleMessage& msg) override;
|
||||
virtual std::unique_ptr<Message> handle(SetBufferMessage& msg) override;
|
||||
virtual std::unique_ptr<Message> handle(InvalidateMessage& msg) override;
|
||||
virtual std::unique_ptr<Message> handle(MenuBarCreateMenuMessage& msg) override;
|
||||
virtual std::unique_ptr<Message> handle(MenuBarCreateItemMessage& msg) override;
|
||||
virtual std::unique_ptr<Message> handle(PopupShowMenuMessage& msg) override;
|
||||
virtual std::unique_ptr<Message> handle(AskBringToFrontMessage& msg) override;
|
||||
};
|
||||
|
||||
} // namespace WinServer
|
||||
159
userland/servers/window_server/src/Managers/Compositor.cpp
Normal file
159
userland/servers/window_server/src/Managers/Compositor.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
#include "Compositor.h"
|
||||
#include "../Components/Base/BaseWindow.h"
|
||||
#include "../Components/ControlBar/ControlBar.h"
|
||||
#include "../Components/MenuBar/MenuBar.h"
|
||||
#include "../Components/Popup/Popup.h"
|
||||
#include "../Devices/Screen.h"
|
||||
#include "../Managers/CursorManager.h"
|
||||
#include "../Managers/ResourceManager.h"
|
||||
#include "../Managers/WindowManager.h"
|
||||
#include <libfoundation/EventLoop.h>
|
||||
#include <libfoundation/Memory.h>
|
||||
#include <libg/Context.h>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
Compositor* s_WinServer_Compositor_the = nullptr;
|
||||
|
||||
Compositor::Compositor()
|
||||
: m_cursor_manager(CursorManager::the())
|
||||
, m_resource_manager(ResourceManager::the())
|
||||
, m_popup(Popup::the())
|
||||
, m_menu_bar(MenuBar::the())
|
||||
#ifdef TARGET_MOBILE
|
||||
, m_control_bar(ControlBar::the())
|
||||
#endif // TARGET_MOBILE
|
||||
{
|
||||
s_WinServer_Compositor_the = this;
|
||||
invalidate(Screen::the().bounds());
|
||||
LFoundation::EventLoop::the().add(LFoundation::Timer([] {
|
||||
Compositor::the().refresh();
|
||||
},
|
||||
1000 / 60, LFoundation::Timer::Repeat));
|
||||
}
|
||||
|
||||
void Compositor::copy_changes_to_second_buffer(const std::vector<LG::Rect>& areas)
|
||||
{
|
||||
auto& screen = Screen::the();
|
||||
|
||||
for (int i = 0; i < areas.size(); i++) {
|
||||
auto bounds = areas[i].intersection(screen.bounds());
|
||||
auto* buf1_ptr = reinterpret_cast<uint32_t*>(&screen.display_bitmap()[bounds.min_y()][bounds.min_x()]);
|
||||
auto* buf2_ptr = reinterpret_cast<uint32_t*>(&screen.write_bitmap()[bounds.min_y()][bounds.min_x()]);
|
||||
for (int j = 0; j < bounds.height(); j++) {
|
||||
LFoundation::fast_copy(buf2_ptr, buf1_ptr, bounds.width());
|
||||
buf1_ptr += screen.width();
|
||||
buf2_ptr += screen.width();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[gnu::flatten]] void Compositor::refresh()
|
||||
{
|
||||
if (m_invalidated_areas.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& screen = Screen::the();
|
||||
auto& wm = WindowManager::the();
|
||||
auto invalidated_areas = std::move(m_invalidated_areas);
|
||||
LG::Context ctx(screen.write_bitmap());
|
||||
|
||||
auto is_window_area_invalidated = [&](const std::vector<LG::Rect>& areas, const LG::Rect& area) -> bool {
|
||||
for (int i = 0; i < areas.size(); i++) {
|
||||
if (area.intersects(areas[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
auto draw_wallpaper_for_area = [&](const LG::Rect& area) {
|
||||
ctx.add_clip(area);
|
||||
ctx.draw({ 0, 0 }, m_resource_manager.background());
|
||||
ctx.reset_clip();
|
||||
};
|
||||
|
||||
#ifdef TARGET_DESKTOP
|
||||
auto draw_window = [&](Desktop::Window& window, const LG::Rect& area) {
|
||||
ctx.add_clip(area);
|
||||
ctx.add_clip(window.bounds());
|
||||
window.frame().draw(ctx);
|
||||
ctx.draw_rounded(window.content_bounds().origin(), window.content_bitmap(), window.corner_mask());
|
||||
ctx.reset_clip();
|
||||
};
|
||||
#elif TARGET_MOBILE
|
||||
auto draw_window = [&](Mobile::Window& window, const LG::Rect& area) {
|
||||
ctx.add_clip(area);
|
||||
ctx.add_clip(window.bounds());
|
||||
ctx.draw(window.content_bounds().origin(), window.content_bitmap());
|
||||
ctx.reset_clip();
|
||||
};
|
||||
#endif // TARGET_DESKTOP
|
||||
|
||||
auto& windows = wm.windows();
|
||||
auto top_std_window = windows.rbegin();
|
||||
#ifdef TARGET_DESKTOP
|
||||
for (int i = 0; i < invalidated_areas.size(); i++) {
|
||||
draw_wallpaper_for_area(invalidated_areas[i]);
|
||||
}
|
||||
#elif TARGET_MOBILE
|
||||
// Standard windows could not be transparent in mobile view, thus
|
||||
// no need to render everything behind the first standard window.
|
||||
for (auto it = windows.rbegin(); it != windows.rend(); it++) {
|
||||
top_std_window = it;
|
||||
}
|
||||
|
||||
if (top_std_window == windows.rend() || (*top_std_window)->type() != WindowType::Standard) {
|
||||
for (int i = 0; i < invalidated_areas.size(); i++) {
|
||||
draw_wallpaper_for_area(invalidated_areas[i]);
|
||||
}
|
||||
}
|
||||
#endif // TARGET_DESKTOP
|
||||
|
||||
for (auto it = top_std_window; it != windows.rend(); it++) {
|
||||
auto& window = *(*it);
|
||||
if (window.visible() && is_window_area_invalidated(invalidated_areas, window.bounds())) {
|
||||
for (int i = 0; i < invalidated_areas.size(); i++) {
|
||||
draw_window(window, invalidated_areas[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_popup.visible()) {
|
||||
for (int i = 0; i < invalidated_areas.size(); i++) {
|
||||
ctx.add_clip(invalidated_areas[i]);
|
||||
m_popup.draw(ctx);
|
||||
ctx.reset_clip();
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < invalidated_areas.size(); i++) {
|
||||
if (m_menu_bar.bounds().intersects(invalidated_areas[i])) {
|
||||
ctx.add_clip(invalidated_areas[i]);
|
||||
m_menu_bar.draw(ctx);
|
||||
ctx.reset_clip();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TARGET_MOBILE
|
||||
for (int i = 0; i < invalidated_areas.size(); i++) {
|
||||
ctx.add_clip(invalidated_areas[i]);
|
||||
m_control_bar.draw(ctx);
|
||||
ctx.reset_clip();
|
||||
}
|
||||
#endif // TARGET_MOBILE
|
||||
|
||||
auto mouse_draw_position = m_cursor_manager.draw_position();
|
||||
auto& current_mouse_bitmap = m_cursor_manager.current_cursor();
|
||||
for (int i = 0; i < invalidated_areas.size(); i++) {
|
||||
ctx.add_clip(invalidated_areas[i]);
|
||||
ctx.draw(mouse_draw_position, current_mouse_bitmap);
|
||||
ctx.reset_clip();
|
||||
}
|
||||
|
||||
screen.swap_buffers();
|
||||
copy_changes_to_second_buffer(invalidated_areas);
|
||||
}
|
||||
|
||||
} // namespace WinServer
|
||||
95
userland/servers/window_server/src/Managers/Compositor.h
Normal file
95
userland/servers/window_server/src/Managers/Compositor.h
Normal file
@@ -0,0 +1,95 @@
|
||||
#pragma once
|
||||
#include "../IPC/ServerDecoder.h"
|
||||
#include <libapi/window_server/Connections/WSConnection.h>
|
||||
#include <libipc/ServerConnection.h>
|
||||
#include <vector>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
class CursorManager;
|
||||
class ResourceManager;
|
||||
class MenuBar;
|
||||
#ifdef TARGET_MOBILE
|
||||
class ControlBar;
|
||||
#endif // TARGET_MOBILE
|
||||
class Popup;
|
||||
|
||||
class Compositor {
|
||||
public:
|
||||
inline static Compositor& the()
|
||||
{
|
||||
extern Compositor* s_WinServer_Compositor_the;
|
||||
return *s_WinServer_Compositor_the;
|
||||
}
|
||||
|
||||
Compositor();
|
||||
|
||||
void refresh();
|
||||
|
||||
void optimized_invalidate_insert(std::vector<LG::Rect>& data, const LG::Rect& inv_area)
|
||||
{
|
||||
auto area = inv_area;
|
||||
bool intersects = false;
|
||||
int int_index = 0;
|
||||
for (auto& rect : data) {
|
||||
if (rect.contains(area)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& rect : data) {
|
||||
if (rect.intersects(area)) {
|
||||
intersects = true;
|
||||
break;
|
||||
}
|
||||
int_index++;
|
||||
}
|
||||
|
||||
if (!intersects) {
|
||||
data.push_back(area);
|
||||
return;
|
||||
}
|
||||
|
||||
data[int_index].unite(area);
|
||||
for (int i = int_index + 1; i < data.size(); i++) {
|
||||
if (data[int_index].intersects(data[i])) {
|
||||
data[int_index].unite(data[i]);
|
||||
std::swap(data[i], data.back());
|
||||
data.pop_back();
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void invalidate(const LG::Rect& area) { optimized_invalidate_insert(m_invalidated_areas, area); }
|
||||
inline CursorManager& cursor_manager() { return m_cursor_manager; }
|
||||
inline const CursorManager& cursor_manager() const { return m_cursor_manager; }
|
||||
inline ResourceManager& resource_manager() { return m_resource_manager; }
|
||||
inline const ResourceManager& resource_manager() const { return m_resource_manager; }
|
||||
inline Popup& popup() { return m_popup; }
|
||||
inline const Popup& popup() const { return m_popup; }
|
||||
inline MenuBar& menu_bar() { return m_menu_bar; }
|
||||
inline const MenuBar& menu_bar() const { return m_menu_bar; }
|
||||
#ifdef TARGET_MOBILE
|
||||
inline ControlBar& control_bar()
|
||||
{
|
||||
return m_control_bar;
|
||||
}
|
||||
inline const ControlBar& control_bar() const { return m_control_bar; }
|
||||
#endif // TARGET_MOBILE
|
||||
|
||||
private:
|
||||
void copy_changes_to_second_buffer(const std::vector<LG::Rect>& areas);
|
||||
|
||||
std::vector<LG::Rect> m_invalidated_areas;
|
||||
MenuBar& m_menu_bar;
|
||||
Popup& m_popup;
|
||||
CursorManager& m_cursor_manager;
|
||||
ResourceManager& m_resource_manager;
|
||||
|
||||
#ifdef TARGET_MOBILE
|
||||
ControlBar& m_control_bar;
|
||||
#endif // TARGET_MOBILE
|
||||
};
|
||||
|
||||
} // namespace WinServer
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "CursorManager.h"
|
||||
#include <libg/ImageLoaders/PNGLoader.h>
|
||||
|
||||
#ifdef TARGET_DESKTOP
|
||||
#define CURSOR_PATH "/res/system/arrow.png"
|
||||
#elif TARGET_MOBILE
|
||||
#define CURSOR_PATH "/res/system/mobile/cursor.png"
|
||||
#endif
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
CursorManager* s_WinServer_CursorManager_the = nullptr;
|
||||
|
||||
CursorManager::CursorManager()
|
||||
: m_screen(Screen::the())
|
||||
{
|
||||
s_WinServer_CursorManager_the = this;
|
||||
LG::PNG::PNGLoader loader;
|
||||
m_std_cursor = loader.load_from_file(CURSOR_PATH);
|
||||
}
|
||||
|
||||
} // namespace WinServer
|
||||
213
userland/servers/window_server/src/Managers/CursorManager.h
Normal file
213
userland/servers/window_server/src/Managers/CursorManager.h
Normal file
@@ -0,0 +1,213 @@
|
||||
#pragma once
|
||||
#include "../Devices/Screen.h"
|
||||
#include "../IPC/Event.h"
|
||||
#include <algorithm>
|
||||
#include <libg/PixelBitmap.h>
|
||||
#include <libg/Point.h>
|
||||
|
||||
#ifdef TARGET_DESKTOP
|
||||
#define CURSOR_OFFSET (2)
|
||||
#elif TARGET_MOBILE
|
||||
#define CURSOR_OFFSET (6)
|
||||
#endif
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
class CursorManager {
|
||||
public:
|
||||
enum class Params {
|
||||
X,
|
||||
Y,
|
||||
LeftButton,
|
||||
RightButton,
|
||||
OffsetX,
|
||||
OffsetY,
|
||||
Wheel,
|
||||
|
||||
// Only for getters of conditions changes.
|
||||
Coords,
|
||||
Buttons,
|
||||
};
|
||||
|
||||
inline static CursorManager& the()
|
||||
{
|
||||
extern CursorManager* s_WinServer_CursorManager_the;
|
||||
return *s_WinServer_CursorManager_the;
|
||||
}
|
||||
|
||||
CursorManager();
|
||||
~CursorManager() = default;
|
||||
|
||||
inline const LG::PixelBitmap& current_cursor() const { return std_cursor(); }
|
||||
inline const LG::PixelBitmap& std_cursor() const { return m_std_cursor; }
|
||||
inline LG::Point<int> draw_position() { return { m_mouse_x - CURSOR_OFFSET, m_mouse_y - CURSOR_OFFSET }; }
|
||||
|
||||
inline int x() const
|
||||
{
|
||||
return m_mouse_x;
|
||||
}
|
||||
inline int y() const { return m_mouse_y; }
|
||||
|
||||
template <Params param>
|
||||
constexpr int get();
|
||||
|
||||
template <Params param>
|
||||
constexpr bool pressed() const;
|
||||
|
||||
template <Params param>
|
||||
constexpr bool is_changed();
|
||||
inline void clear_changed(uint32_t val = 0) { m_mask_changed_objects = val; }
|
||||
|
||||
template <Params param, typename Value>
|
||||
inline constexpr void set(Value val)
|
||||
{
|
||||
if constexpr (param == CursorManager::Params::X) {
|
||||
val = std::max(std::min(val, (int)m_screen.width() - 1), 0);
|
||||
m_mouse_offset_x = val - m_mouse_x;
|
||||
m_mouse_x = val;
|
||||
set_changed<CursorManager::Params::X>();
|
||||
} else if constexpr (param == CursorManager::Params::Y) {
|
||||
val = std::max(std::min(val, (int)m_screen.height() - 1), 0);
|
||||
m_mouse_offset_y = val - m_mouse_y;
|
||||
m_mouse_y = val;
|
||||
set_changed<CursorManager::Params::Y>();
|
||||
} else if constexpr (param == CursorManager::Params::OffsetX) {
|
||||
if (val != 0) {
|
||||
set<CursorManager::Params::X>(m_mouse_x + val);
|
||||
} else {
|
||||
m_mouse_offset_x = 0;
|
||||
}
|
||||
} else if constexpr (param == CursorManager::Params::OffsetY) {
|
||||
if (val != 0) {
|
||||
set<CursorManager::Params::Y>(m_mouse_y + val);
|
||||
} else {
|
||||
m_mouse_offset_y = 0;
|
||||
}
|
||||
} else if constexpr (param == CursorManager::Params::LeftButton) {
|
||||
if (m_mouse_left_button_pressed != val) {
|
||||
set_changed<CursorManager::Params::LeftButton>();
|
||||
}
|
||||
m_mouse_left_button_pressed = val;
|
||||
} else if constexpr (param == CursorManager::Params::RightButton) {
|
||||
if (m_mouse_right_button_pressed != val) {
|
||||
set_changed<CursorManager::Params::RightButton>();
|
||||
}
|
||||
m_mouse_right_button_pressed = val;
|
||||
} else if constexpr (param == CursorManager::Params::Wheel) {
|
||||
if (val != 0) {
|
||||
set_changed<CursorManager::Params::Wheel>();
|
||||
}
|
||||
m_wheel = val;
|
||||
} else {
|
||||
[]<bool flag = false>() { static_assert(flag, "Could not call set() with such param!"); }
|
||||
();
|
||||
}
|
||||
}
|
||||
|
||||
template <Params param>
|
||||
inline constexpr void set_changed()
|
||||
{
|
||||
if constexpr (param == CursorManager::Params::X) {
|
||||
m_mask_changed_objects |= CursorManager::ChangedValues::MouseCoords;
|
||||
} else if constexpr (param == CursorManager::Params::Y) {
|
||||
m_mask_changed_objects |= CursorManager::ChangedValues::MouseCoords;
|
||||
} else if constexpr (param == CursorManager::Params::LeftButton) {
|
||||
m_mask_changed_objects |= CursorManager::ChangedValues::LeftButton;
|
||||
} else if constexpr (param == CursorManager::Params::RightButton) {
|
||||
m_mask_changed_objects |= CursorManager::ChangedValues::RightButton;
|
||||
} else if constexpr (param == CursorManager::Params::Wheel) {
|
||||
m_mask_changed_objects |= CursorManager::ChangedValues::Wheel;
|
||||
} else {
|
||||
[]<bool flag = false>() { static_assert(flag, "Could not set_changed() for the param!"); }
|
||||
();
|
||||
}
|
||||
}
|
||||
|
||||
void update_position(MouseEvent* mouse_event)
|
||||
{
|
||||
clear_changed();
|
||||
set<Params::OffsetX>(mouse_event->packet().x_offset);
|
||||
set<Params::OffsetY>(-mouse_event->packet().y_offset);
|
||||
set<Params::LeftButton>((mouse_event->packet().button_states & 1));
|
||||
set<Params::RightButton>((mouse_event->packet().button_states & 2) >> 1);
|
||||
set<Params::Wheel>(mouse_event->packet().wheel_data);
|
||||
}
|
||||
|
||||
private:
|
||||
enum ChangedValues {
|
||||
MouseCoords = 0x1,
|
||||
LeftButton = 0x2,
|
||||
RightButton = 0x4,
|
||||
Wheel = 0x8,
|
||||
};
|
||||
|
||||
int m_mouse_x { 0 };
|
||||
int m_mouse_y { 0 };
|
||||
int m_mouse_offset_x { 0 };
|
||||
int m_mouse_offset_y { 0 };
|
||||
int m_wheel { 0 };
|
||||
bool m_mouse_left_button_pressed { false };
|
||||
bool m_mouse_right_button_pressed { false };
|
||||
uint32_t m_mask_changed_objects { 0 };
|
||||
bool m_mouse_changed_button_status { false };
|
||||
|
||||
Screen& m_screen;
|
||||
LG::PixelBitmap m_std_cursor;
|
||||
};
|
||||
|
||||
template <CursorManager::Params param>
|
||||
inline constexpr int CursorManager::get()
|
||||
{
|
||||
if constexpr (param == CursorManager::Params::X) {
|
||||
return m_mouse_x;
|
||||
} else if constexpr (param == CursorManager::Params::Y) {
|
||||
return m_mouse_y;
|
||||
} else if constexpr (param == CursorManager::Params::OffsetX) {
|
||||
return m_mouse_offset_x;
|
||||
} else if constexpr (param == CursorManager::Params::OffsetY) {
|
||||
return m_mouse_offset_y;
|
||||
} else if constexpr (param == CursorManager::Params::Wheel) {
|
||||
return m_wheel;
|
||||
} else {
|
||||
[]<bool flag = false>() { static_assert(flag, "Could call get() only for coords-like params!"); }
|
||||
();
|
||||
}
|
||||
}
|
||||
|
||||
template <CursorManager::Params param>
|
||||
inline constexpr bool CursorManager::pressed() const
|
||||
{
|
||||
if constexpr (param == CursorManager::Params::LeftButton) {
|
||||
return m_mouse_left_button_pressed;
|
||||
} else if constexpr (param == CursorManager::Params::RightButton) {
|
||||
return m_mouse_right_button_pressed;
|
||||
} else {
|
||||
[]<bool flag = false>() { static_assert(flag, "Could call pressed() only for buttons!"); }
|
||||
();
|
||||
}
|
||||
}
|
||||
|
||||
template <CursorManager::Params param>
|
||||
inline constexpr bool CursorManager::is_changed()
|
||||
{
|
||||
if constexpr (param == CursorManager::Params::X) {
|
||||
return (m_mask_changed_objects & CursorManager::ChangedValues::MouseCoords);
|
||||
} else if constexpr (param == CursorManager::Params::Y) {
|
||||
return (m_mask_changed_objects & CursorManager::ChangedValues::MouseCoords);
|
||||
} else if constexpr (param == CursorManager::Params::LeftButton) {
|
||||
return (m_mask_changed_objects & CursorManager::ChangedValues::LeftButton);
|
||||
} else if constexpr (param == CursorManager::Params::RightButton) {
|
||||
return (m_mask_changed_objects & CursorManager::ChangedValues::RightButton);
|
||||
} else if constexpr (param == CursorManager::Params::Coords) {
|
||||
return (m_mask_changed_objects & CursorManager::ChangedValues::MouseCoords);
|
||||
} else if constexpr (param == CursorManager::Params::Buttons) {
|
||||
return (m_mask_changed_objects & CursorManager::ChangedValues::LeftButton) | (m_mask_changed_objects & CursorManager::ChangedValues::RightButton);
|
||||
} else if constexpr (param == CursorManager::Params::Wheel) {
|
||||
return (m_mask_changed_objects & CursorManager::ChangedValues::Wheel);
|
||||
} else {
|
||||
[]<bool flag = false>() { static_assert(flag, "Could not call is_changed() for the param!"); }
|
||||
();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace WinServer
|
||||
69
userland/servers/window_server/src/Managers/InitManager.h
Normal file
69
userland/servers/window_server/src/Managers/InitManager.h
Normal file
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Components/ControlBar/ControlBar.h"
|
||||
#include "../Components/LoadingScreen/LoadingScreen.h"
|
||||
#include "../Components/MenuBar/MenuBar.h"
|
||||
#include "../Components/MenuBar/Widgets/Clock/Clock.h"
|
||||
#include "../Components/MenuBar/Widgets/ControlPanelToggle/ControlPanelToggle.h"
|
||||
#include "../Components/Popup/Popup.h"
|
||||
#include "../Devices/Devices.h"
|
||||
#include "../Devices/Screen.h"
|
||||
#include "../IPC/Connection.h"
|
||||
#include "Compositor.h"
|
||||
#include "CursorManager.h"
|
||||
#include "ResourceManager.h"
|
||||
#include "WindowManager.h"
|
||||
#include <cstdlib>
|
||||
#include <libfoundation/EventLoop.h>
|
||||
#include <new>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
class InitManager {
|
||||
public:
|
||||
InitManager() = delete;
|
||||
~InitManager() = delete;
|
||||
|
||||
static void load_screen()
|
||||
{
|
||||
nice(-3);
|
||||
new WinServer::Screen();
|
||||
new WinServer::LoadingScreen();
|
||||
}
|
||||
|
||||
template <class T, int Cost = 1, class... Args>
|
||||
static constexpr inline void load_core_component(Args&&... args)
|
||||
{
|
||||
new T(std::forward<Args>(args)...);
|
||||
WinServer::LoadingScreen::the().move_progress<T, Cost>();
|
||||
}
|
||||
|
||||
template <class T, int Cost = 1, class... Args>
|
||||
static constexpr inline void add_widget(Args&&... args)
|
||||
{
|
||||
WinServer::MenuBar::the().add_widget<T>(std::forward<Args>(args)...);
|
||||
WinServer::LoadingScreen::the().move_progress<T, Cost>();
|
||||
}
|
||||
|
||||
static inline int launch_app(const char* path, int uid = 0)
|
||||
{
|
||||
int pid = fork();
|
||||
if (!pid) {
|
||||
setuid(uid);
|
||||
setgid(uid);
|
||||
for (int i = 3; i < 32; i++) {
|
||||
close(i);
|
||||
}
|
||||
|
||||
execlp(path, path, NULL);
|
||||
std::abort();
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
#include "ResourceManager.h"
|
||||
#include <libg/ImageLoaders/PNGLoader.h>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
ResourceManager* s_WinServer_ResourceManager_the = nullptr;
|
||||
|
||||
ResourceManager::ResourceManager()
|
||||
{
|
||||
s_WinServer_ResourceManager_the = this;
|
||||
LG::PNG::PNGLoader loader;
|
||||
m_background = loader.load_from_file("/res/wallpapers/abstract.png");
|
||||
}
|
||||
|
||||
} // namespace WinServer
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include <libg/PixelBitmap.h>
|
||||
#include <libg/Point.h>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
class ResourceManager {
|
||||
public:
|
||||
inline static ResourceManager& the()
|
||||
{
|
||||
extern ResourceManager* s_WinServer_ResourceManager_the;
|
||||
return *s_WinServer_ResourceManager_the;
|
||||
}
|
||||
|
||||
ResourceManager();
|
||||
|
||||
inline const LG::PixelBitmap& background() const { return m_background; }
|
||||
|
||||
private:
|
||||
LG::PixelBitmap m_background;
|
||||
};
|
||||
|
||||
} // namespace WinServer
|
||||
599
userland/servers/window_server/src/Managers/WindowManager.cpp
Normal file
599
userland/servers/window_server/src/Managers/WindowManager.cpp
Normal file
@@ -0,0 +1,599 @@
|
||||
#include "WindowManager.h"
|
||||
#include "../Devices/Screen.h"
|
||||
#include "../Managers/CursorManager.h"
|
||||
#include <libapi/window_server/MessageContent/MouseAction.h>
|
||||
#include <libfoundation/KeyboardMapping.h>
|
||||
#include <libfoundation/Logger.h>
|
||||
|
||||
// #define WM_DEBUG
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
static PopupData WindowPopupData {};
|
||||
|
||||
WindowManager* s_WinServer_WindowManager_the = nullptr;
|
||||
|
||||
WindowManager::WindowManager()
|
||||
: m_screen(Screen::the())
|
||||
, m_connection(Connection::the())
|
||||
, m_compositor(Compositor::the())
|
||||
, m_cursor_manager(CursorManager::the())
|
||||
, m_event_loop(LFoundation::EventLoop::the())
|
||||
, m_std_menubar_content()
|
||||
, m_visible_area(m_screen.bounds())
|
||||
{
|
||||
s_WinServer_WindowManager_the = this;
|
||||
shrink_visible_area(menu_bar().height(), 0);
|
||||
#ifdef TARGET_DESKTOP
|
||||
menu_bar().set_background_color(LG::Color::LightSystemOpaque128);
|
||||
#endif // TARGET_DESKTOP
|
||||
}
|
||||
|
||||
void WindowManager::setup_dock(Window* window)
|
||||
{
|
||||
#ifdef TARGET_DESKTOP
|
||||
window->make_frameless();
|
||||
window->bounds().set_y(m_screen.bounds().max_y() - window->bounds().height() + 1);
|
||||
window->content_bounds().set_y(m_screen.bounds().max_y() - window->bounds().height() + 1);
|
||||
shrink_visible_area(0, window->bounds().height());
|
||||
#endif // TARGET_DESKTOP
|
||||
window->set_event_mask(WindowEvent::IconChange | WindowEvent::WindowStatus | WindowEvent::WindowCreation | WindowEvent::TitleChange);
|
||||
m_dock.set_window(window);
|
||||
}
|
||||
|
||||
void WindowManager::setup_applist(Window* window)
|
||||
{
|
||||
#ifdef TARGET_DESKTOP
|
||||
window->make_frameless();
|
||||
const size_t coorx = (visible_area().max_x() - window->bounds().width()) / 2;
|
||||
const size_t coory = visible_area().max_y() - window->bounds().height() - 8;
|
||||
window->bounds().set_x(coorx);
|
||||
window->content_bounds().set_x(coorx);
|
||||
window->bounds().set_y(coory);
|
||||
window->content_bounds().set_y(coory);
|
||||
#endif // TARGET_DESKTOP
|
||||
m_applist.set_window(window);
|
||||
minimize_window(*window);
|
||||
}
|
||||
|
||||
void WindowManager::add_system_window(Window* window)
|
||||
{
|
||||
switch (window->type()) {
|
||||
case WindowType::Homescreen:
|
||||
setup_dock(window);
|
||||
break;
|
||||
|
||||
case WindowType::AppList:
|
||||
setup_applist(window);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void WindowManager::add_window(Window* window)
|
||||
{
|
||||
m_windows.push_back(window);
|
||||
set_active_window(window);
|
||||
|
||||
if (window->type() != WindowType::Standard) {
|
||||
add_system_window(window);
|
||||
}
|
||||
notify_window_creation(window->id());
|
||||
}
|
||||
|
||||
void WindowManager::remove_attention_from_window(Window* window)
|
||||
{
|
||||
if (movable_window() == window) {
|
||||
m_movable_window = nullptr;
|
||||
}
|
||||
if (active_window() == window) {
|
||||
set_active_window(nullptr);
|
||||
}
|
||||
if (hovered_window() == window) {
|
||||
set_hovered_window(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void WindowManager::on_window_became_invisible(Window* window)
|
||||
{
|
||||
if (window->type() == WindowType::Standard && active_window() == window) {
|
||||
#ifdef TARGET_DESKTOP
|
||||
menu_bar().set_menubar_content(nullptr, m_compositor);
|
||||
#elif TARGET_MOBILE
|
||||
menu_bar().set_style(StatusBarStyle::StandardOpaque);
|
||||
m_compositor.invalidate(menu_bar().bounds());
|
||||
#endif
|
||||
}
|
||||
m_compositor.invalidate(window->bounds());
|
||||
}
|
||||
|
||||
void WindowManager::remove_window(Window* window_ptr)
|
||||
{
|
||||
notify_window_status_changed(window_ptr->id(), WindowStatusUpdateType::Removed);
|
||||
m_windows.erase(std::find(m_windows.begin(), m_windows.end(), window_ptr));
|
||||
on_window_became_invisible(window_ptr);
|
||||
remove_attention_from_window(window_ptr);
|
||||
delete window_ptr;
|
||||
}
|
||||
|
||||
void WindowManager::minimize_window(Window& window)
|
||||
{
|
||||
Window* window_ptr = &window;
|
||||
notify_window_status_changed(window.id(), WindowStatusUpdateType::Minimized);
|
||||
window.set_visible(false);
|
||||
m_windows.erase(std::find(m_windows.begin(), m_windows.end(), window_ptr));
|
||||
m_windows.push_back(window_ptr);
|
||||
on_window_became_invisible(window_ptr);
|
||||
remove_attention_from_window(window_ptr);
|
||||
}
|
||||
|
||||
void WindowManager::resize_window(Window& window, const LG::Size& size)
|
||||
{
|
||||
window.did_size_change(size);
|
||||
send_event(new ResizeMessage(window.connection_id(), window.id(), LG::Rect(0, 0, size.width(), size.height())));
|
||||
m_compositor.invalidate(window.bounds());
|
||||
}
|
||||
|
||||
void WindowManager::maximize_window(Window& window)
|
||||
{
|
||||
size_t fullscreen_h = m_screen.height();
|
||||
fullscreen_h = visible_area().height();
|
||||
|
||||
const size_t vertical_borders = Desktop::WindowFrame::std_top_border_size() + Desktop::WindowFrame::std_bottom_border_size();
|
||||
const size_t horizontal_borders = Desktop::WindowFrame::std_left_border_size() + Desktop::WindowFrame::std_right_border_size();
|
||||
|
||||
move_window(&window, -window.bounds().min_x(), -(window.bounds().min_y() - menu_bar().height()));
|
||||
resize_window(window, { m_screen.width() - horizontal_borders, fullscreen_h - vertical_borders });
|
||||
}
|
||||
|
||||
void WindowManager::start_window_move(Window& window)
|
||||
{
|
||||
m_movable_window = &window;
|
||||
}
|
||||
|
||||
WindowManager::Window* WindowManager::top_window_in_view(WindowType type) const
|
||||
{
|
||||
for (auto it = m_windows.begin(); it != m_windows.end(); it++) {
|
||||
auto* window = (*it);
|
||||
if (window->type() == type) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef TARGET_DESKTOP
|
||||
void WindowManager::on_active_window_will_change()
|
||||
{
|
||||
if (!m_active_window) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_active_window->type() == WindowType::AppList) {
|
||||
m_active_window->set_visible(false);
|
||||
on_window_became_invisible(m_active_window);
|
||||
}
|
||||
}
|
||||
|
||||
void WindowManager::on_active_window_did_change()
|
||||
{
|
||||
}
|
||||
|
||||
void WindowManager::bring_system_windows_to_front()
|
||||
{
|
||||
if (m_dock.has_value()) {
|
||||
do_bring_to_front(*m_dock.window());
|
||||
}
|
||||
}
|
||||
|
||||
void WindowManager::bring_to_front(Window& window)
|
||||
{
|
||||
auto* prev_window = top_window_in_view(WindowType::Standard);
|
||||
do_bring_to_front(window);
|
||||
bring_system_windows_to_front();
|
||||
|
||||
window.set_visible(true);
|
||||
window.frame().set_active(true);
|
||||
m_compositor.invalidate(window.bounds());
|
||||
if (prev_window && prev_window->id() != window.id()) {
|
||||
prev_window->frame().set_active(false);
|
||||
prev_window->frame().invalidate(m_compositor);
|
||||
}
|
||||
if (window.type() == WindowType::Standard) {
|
||||
menu_bar().set_menubar_content(&window.menubar_content(), m_compositor);
|
||||
} else {
|
||||
menu_bar().set_menubar_content(nullptr, m_compositor);
|
||||
}
|
||||
}
|
||||
#elif TARGET_MOBILE
|
||||
void WindowManager::on_active_window_will_change()
|
||||
{
|
||||
}
|
||||
|
||||
void WindowManager::on_active_window_did_change()
|
||||
{
|
||||
// If current active_window has become NULL, try to restore the lastest.
|
||||
if (active_window() == nullptr) {
|
||||
if (auto top_window = m_windows.begin(); top_window != m_windows.end()) {
|
||||
m_active_window = *top_window;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WindowManager::bring_system_windows_to_front()
|
||||
{
|
||||
}
|
||||
|
||||
void WindowManager::bring_to_front(Window& window)
|
||||
{
|
||||
do_bring_to_front(window);
|
||||
bring_system_windows_to_front();
|
||||
window.set_visible(true);
|
||||
m_active_window = &window;
|
||||
m_compositor.invalidate(window.bounds());
|
||||
if (window.type() == WindowType::Standard) {
|
||||
menu_bar().set_style(window.style());
|
||||
m_compositor.invalidate(menu_bar().bounds());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TARGET_DESKTOP
|
||||
bool WindowManager::continue_window_move()
|
||||
{
|
||||
if (!movable_window()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_cursor_manager.pressed<CursorManager::Params::LeftButton>()) {
|
||||
m_movable_window = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto bounds = movable_window()->bounds();
|
||||
m_compositor.invalidate(movable_window()->bounds());
|
||||
move_window(movable_window(), m_cursor_manager.get<CursorManager::Params::OffsetX>(), m_cursor_manager.get<CursorManager::Params::OffsetY>());
|
||||
bounds.unite(movable_window()->bounds());
|
||||
m_compositor.invalidate(bounds);
|
||||
return true;
|
||||
}
|
||||
#endif // TARGET_DESKTOP
|
||||
|
||||
void WindowManager::update_mouse_position(std::unique_ptr<LFoundation::Event> mouse_event)
|
||||
{
|
||||
auto invalidate_bounds = m_cursor_manager.current_cursor().bounds();
|
||||
invalidate_bounds.origin().set(m_cursor_manager.draw_position());
|
||||
m_compositor.invalidate(invalidate_bounds);
|
||||
|
||||
m_cursor_manager.update_position((WinServer::MouseEvent*)mouse_event.get());
|
||||
|
||||
invalidate_bounds.origin().set(m_cursor_manager.draw_position());
|
||||
m_compositor.invalidate(invalidate_bounds);
|
||||
}
|
||||
|
||||
#ifdef TARGET_DESKTOP
|
||||
void WindowManager::receive_mouse_event(std::unique_ptr<LFoundation::Event> event)
|
||||
{
|
||||
update_mouse_position(std::move(event));
|
||||
if (continue_window_move()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Checking and dispatching mouse move for Popup.
|
||||
if (popup().visible() && popup().bounds().contains(m_cursor_manager.x(), m_cursor_manager.y())) {
|
||||
popup().on_mouse_move(m_cursor_manager);
|
||||
if (m_cursor_manager.is_changed<CursorManager::Params::Buttons>()) {
|
||||
popup().on_mouse_status_change(m_cursor_manager);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
popup().on_mouse_leave(m_cursor_manager);
|
||||
if (m_cursor_manager.pressed<CursorManager::Params::LeftButton>()) {
|
||||
popup().set_visible(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Checking and dispatching mouse move for MenuBar.
|
||||
if (menu_bar().bounds().contains(m_cursor_manager.x(), m_cursor_manager.y())) {
|
||||
menu_bar().on_mouse_move(m_cursor_manager);
|
||||
if (m_cursor_manager.is_changed<CursorManager::Params::Buttons>()) {
|
||||
menu_bar().on_mouse_status_change(m_cursor_manager);
|
||||
}
|
||||
return;
|
||||
} else if (menu_bar().is_hovered()) {
|
||||
menu_bar().on_mouse_leave(m_cursor_manager);
|
||||
}
|
||||
|
||||
Window* curr_hovered_window = nullptr;
|
||||
Window* window_under_mouse_ptr = nullptr;
|
||||
|
||||
for (auto* window_ptr : m_windows) {
|
||||
auto& window = *window_ptr;
|
||||
if (!window.visible()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (window.bounds().contains(m_cursor_manager.x(), m_cursor_manager.y())) {
|
||||
window_under_mouse_ptr = window_ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_cursor_manager.pressed<CursorManager::Params::LeftButton>() || m_cursor_manager.pressed<CursorManager::Params::RightButton>()) {
|
||||
if (!window_under_mouse_ptr && m_active_window) {
|
||||
menu_bar().set_menubar_content(nullptr, m_compositor);
|
||||
m_compositor.invalidate(m_active_window->bounds());
|
||||
m_active_window->frame().set_active(false);
|
||||
set_active_window(nullptr);
|
||||
} else if (m_active_window != window_under_mouse_ptr) {
|
||||
set_active_window(window_under_mouse_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
if (!window_under_mouse_ptr) {
|
||||
if (hovered_window()) {
|
||||
send_event(new MouseLeaveMessage(hovered_window()->connection_id(), hovered_window()->id(), 0, 0));
|
||||
}
|
||||
return;
|
||||
}
|
||||
auto& window = *window_under_mouse_ptr;
|
||||
|
||||
if (window.content_bounds().contains(m_cursor_manager.x(), m_cursor_manager.y())) {
|
||||
if (window.type() == WindowType::Standard && active_window() != &window) {
|
||||
curr_hovered_window = nullptr;
|
||||
} else {
|
||||
if (m_cursor_manager.is_changed<CursorManager::Params::Coords>()) {
|
||||
LG::Point<int> point(m_cursor_manager.x(), m_cursor_manager.y());
|
||||
point.offset_by(-window.content_bounds().origin());
|
||||
send_event(new MouseMoveMessage(window.connection_id(), window.id(), point.x(), point.y()));
|
||||
}
|
||||
curr_hovered_window = &window;
|
||||
}
|
||||
} else if (m_cursor_manager.is_changed<CursorManager::Params::LeftButton>() && m_cursor_manager.pressed<CursorManager::Params::LeftButton>()) {
|
||||
auto tap_point = LG::Point<int>(m_cursor_manager.x() - window.frame().bounds().min_x(), m_cursor_manager.y() - window.frame().bounds().min_y());
|
||||
window.frame().receive_tap_event(tap_point);
|
||||
start_window_move(window);
|
||||
}
|
||||
|
||||
if (hovered_window() && hovered_window() != curr_hovered_window) {
|
||||
send_event(new MouseLeaveMessage(hovered_window()->connection_id(), hovered_window()->id(), 0, 0));
|
||||
}
|
||||
set_hovered_window(curr_hovered_window);
|
||||
|
||||
if (hovered_window() && m_cursor_manager.is_changed<CursorManager::Params::Buttons>()) {
|
||||
LG::Point<int> point(m_cursor_manager.x(), m_cursor_manager.y());
|
||||
point.offset_by(-window.content_bounds().origin());
|
||||
|
||||
auto buttons_state = MouseActionState();
|
||||
if (m_cursor_manager.is_changed<CursorManager::Params::LeftButton>()) {
|
||||
// TODO: May be remove if?
|
||||
if (m_cursor_manager.pressed<CursorManager::Params::LeftButton>()) {
|
||||
buttons_state.set(MouseActionType::LeftMouseButtonPressed);
|
||||
} else {
|
||||
buttons_state.set(MouseActionType::LeftMouseButtonReleased);
|
||||
}
|
||||
}
|
||||
|
||||
send_event(new MouseActionMessage(window.connection_id(), window.id(), buttons_state.state(), point.x(), point.y()));
|
||||
}
|
||||
|
||||
if (hovered_window() && m_cursor_manager.is_changed<CursorManager::Params::Wheel>()) {
|
||||
auto* window = hovered_window();
|
||||
auto data = m_cursor_manager.get<CursorManager::Params::Wheel>();
|
||||
send_event(new MouseWheelMessage(window->connection_id(), window->id(), data, m_cursor_manager.x(), m_cursor_manager.y()));
|
||||
}
|
||||
}
|
||||
#elif TARGET_MOBILE
|
||||
void WindowManager::receive_mouse_event(std::unique_ptr<LFoundation::Event> event)
|
||||
{
|
||||
update_mouse_position(std::move(event));
|
||||
|
||||
if (m_compositor.control_bar().control_button_bounds().contains(m_cursor_manager.x(), m_cursor_manager.y()) && active_window()) {
|
||||
if (m_cursor_manager.pressed<CursorManager::Params::LeftButton>()) {
|
||||
switch (active_window()->type()) {
|
||||
case WindowType::Standard:
|
||||
remove_window(active_window());
|
||||
break;
|
||||
case WindowType::AppList:
|
||||
minimize_window(*active_window());
|
||||
break;
|
||||
|
||||
case WindowType::Homescreen:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Tap emulation
|
||||
if (m_cursor_manager.is_changed<CursorManager::Params::Buttons>() && active_window()) {
|
||||
auto window = active_window();
|
||||
auto buttons_state = MouseActionState();
|
||||
LG::Point<int> point(m_cursor_manager.x(), m_cursor_manager.y());
|
||||
point.offset_by(-window->content_bounds().origin());
|
||||
if (m_cursor_manager.is_changed<CursorManager::Params::LeftButton>()) {
|
||||
// TODO: May be remove if?
|
||||
if (m_cursor_manager.pressed<CursorManager::Params::LeftButton>()) {
|
||||
buttons_state.set(MouseActionType::LeftMouseButtonPressed);
|
||||
} else {
|
||||
buttons_state.set(MouseActionType::LeftMouseButtonReleased);
|
||||
}
|
||||
}
|
||||
send_event(new MouseActionMessage(window->connection_id(), window->id(), buttons_state.state(), point.x(), point.y()));
|
||||
}
|
||||
|
||||
if (active_window()) {
|
||||
auto window = active_window();
|
||||
if (m_cursor_manager.pressed<CursorManager::Params::LeftButton>()) {
|
||||
if (m_cursor_manager.is_changed<CursorManager::Params::Coords>()) {
|
||||
LG::Point<int> point(m_cursor_manager.x(), m_cursor_manager.y());
|
||||
point.offset_by(-window->content_bounds().origin());
|
||||
send_event(new MouseMoveMessage(window->connection_id(), window->id(), point.x(), point.y()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // TARGET_MOBILE
|
||||
|
||||
void WindowManager::receive_keyboard_event(std::unique_ptr<LFoundation::Event> event)
|
||||
{
|
||||
auto* keyboard_event = reinterpret_cast<KeyboardEvent*>(event.release());
|
||||
if (active_window()) {
|
||||
auto window = active_window();
|
||||
send_event(new KeyboardMessage(window->connection_id(), window->id(), keyboard_event->packet().key));
|
||||
}
|
||||
delete keyboard_event;
|
||||
}
|
||||
|
||||
void WindowManager::receive_event(std::unique_ptr<LFoundation::Event> event)
|
||||
{
|
||||
switch (event->type()) {
|
||||
case WinServer::Event::Type::MouseEvent:
|
||||
receive_mouse_event(std::move(event));
|
||||
break;
|
||||
case WinServer::Event::Type::KeyboardEvent:
|
||||
receive_keyboard_event(std::move(event));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Notifiers
|
||||
|
||||
bool WindowManager::notify_listner_about_window_creation(const Window& win, int changed_window_id)
|
||||
{
|
||||
#ifdef WM_DEBUG
|
||||
Logger::debug << "notify_listner_about_window_status " << win.id() << " that " << changed_window_id << " " << type << std::endl;
|
||||
#endif
|
||||
auto* changed_window_ptr = window(changed_window_id);
|
||||
if (!changed_window_ptr) {
|
||||
return false;
|
||||
}
|
||||
send_event(new NotifyWindowCreateMessage(win.connection_id(), win.id(), changed_window_ptr->bundle_id(), changed_window_ptr->icon_path(), changed_window_id, changed_window_ptr->type()));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WindowManager::notify_listner_about_window_status(const Window& win, int changed_window_id, WindowStatusUpdateType type)
|
||||
{
|
||||
#ifdef WM_DEBUG
|
||||
Logger::debug << "notify_listner_about_window_status " << win.id() << " that " << changed_window_id << " " << type << std::endl;
|
||||
#endif
|
||||
auto* changed_window_ptr = window(changed_window_id);
|
||||
if (!changed_window_ptr) {
|
||||
return false;
|
||||
}
|
||||
send_event(new NotifyWindowStatusChangedMessage(win.connection_id(), win.id(), changed_window_id, (int)type));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WindowManager::notify_listner_about_changed_icon(const Window& win, int changed_window_id)
|
||||
{
|
||||
#ifdef WM_DEBUG
|
||||
Logger::debug << "notify_listner_about_changed_icon " << win.id() << " that " << changed_window_id << std::endl;
|
||||
#endif
|
||||
auto* changed_window_ptr = window(changed_window_id);
|
||||
if (!changed_window_ptr) {
|
||||
return false;
|
||||
}
|
||||
send_event(new NotifyWindowIconChangedMessage(win.connection_id(), win.id(), changed_window_id, changed_window_ptr->icon_path()));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WindowManager::notify_listner_about_changed_title(const Window& win, int changed_window_id)
|
||||
{
|
||||
#ifdef WM_DEBUG
|
||||
Logger::debug << "notify_listner_about_changed_title " << win.id() << " that " << changed_window_id << std::endl;
|
||||
#endif
|
||||
auto* changed_window_ptr = window(changed_window_id);
|
||||
if (!changed_window_ptr) {
|
||||
return false;
|
||||
}
|
||||
send_event(new NotifyWindowTitleChangedMessage(win.connection_id(), win.id(), changed_window_id, changed_window_ptr->app_title()));
|
||||
return true;
|
||||
}
|
||||
|
||||
void WindowManager::notify_window_creation(int changed_window_id)
|
||||
{
|
||||
for (auto* window_ptr : m_windows) {
|
||||
auto& window = *window_ptr;
|
||||
if (window.event_mask() & WindowEvent::WindowCreation) {
|
||||
notify_listner_about_window_creation(window, changed_window_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WindowManager::notify_window_status_changed(int changed_window_id, WindowStatusUpdateType type)
|
||||
{
|
||||
for (auto* window_ptr : m_windows) {
|
||||
auto& window = *window_ptr;
|
||||
if (window.event_mask() & WindowEvent::WindowStatus) {
|
||||
notify_listner_about_window_status(window, changed_window_id, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WindowManager::notify_window_icon_changed(int changed_window_id)
|
||||
{
|
||||
for (auto* window_ptr : m_windows) {
|
||||
auto& window = *window_ptr;
|
||||
if (window.event_mask() & WindowEvent::IconChange) {
|
||||
notify_listner_about_changed_icon(window, changed_window_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WindowManager::notify_window_title_changed(int changed_window_id)
|
||||
{
|
||||
for (auto* window_ptr : m_windows) {
|
||||
auto& window = *window_ptr;
|
||||
if (window.event_mask() & WindowEvent::TitleChange) {
|
||||
notify_listner_about_changed_title(window, changed_window_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TARGET_DESKTOP
|
||||
void WindowManager::on_window_style_change(Window& window)
|
||||
{
|
||||
if (window.visible()) {
|
||||
window.frame().invalidate(m_compositor);
|
||||
}
|
||||
}
|
||||
#elif TARGET_MOBILE
|
||||
void WindowManager::on_window_style_change(Window& window)
|
||||
{
|
||||
if (active_window() == &window && window.type() == WindowType::Standard) {
|
||||
menu_bar().set_style(window.style());
|
||||
m_compositor.invalidate(menu_bar().bounds());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void WindowManager::on_window_menubar_change(Window& window)
|
||||
{
|
||||
if (m_active_window == &window) {
|
||||
menu_bar().invalidate_menubar_panel(m_compositor);
|
||||
}
|
||||
}
|
||||
|
||||
void WindowManager::on_window_misbehave(Window& window, ViolationClass viocls)
|
||||
{
|
||||
switch (viocls) {
|
||||
case ViolationClass::Ignorable:
|
||||
case ViolationClass::Moderate:
|
||||
break;
|
||||
|
||||
case ViolationClass::Serious:
|
||||
// TODO: Currently we only remove the window, but all apps
|
||||
// should be stopped with with a signal.
|
||||
remove_window(&window);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace WinServer
|
||||
178
userland/servers/window_server/src/Managers/WindowManager.h
Normal file
178
userland/servers/window_server/src/Managers/WindowManager.h
Normal file
@@ -0,0 +1,178 @@
|
||||
#pragma once
|
||||
#include "../Components/ControlBar/ControlBar.h"
|
||||
#include "../Components/MenuBar/MenuBar.h"
|
||||
#include "../Components/Security/Violations.h"
|
||||
#include "../Devices/Screen.h"
|
||||
#include "../IPC/Connection.h"
|
||||
#include "../IPC/Event.h"
|
||||
#include "../IPC/ServerDecoder.h"
|
||||
#include "../Managers/Compositor.h"
|
||||
#include "../SystemApps/SystemApp.h"
|
||||
#include "../Target/Generic/Window.h"
|
||||
#include <algorithm>
|
||||
#include <libapi/window_server/Connections/WSConnection.h>
|
||||
#include <libfoundation/EventLoop.h>
|
||||
#include <libfoundation/EventReceiver.h>
|
||||
#include <libipc/ServerConnection.h>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
class WindowManager : public LFoundation::EventReceiver {
|
||||
#ifdef TARGET_DESKTOP
|
||||
using Window = WinServer::Desktop::Window;
|
||||
#elif TARGET_MOBILE
|
||||
using Window = WinServer::Mobile::Window;
|
||||
#endif
|
||||
|
||||
public:
|
||||
inline static WindowManager& the()
|
||||
{
|
||||
extern WindowManager* s_WinServer_WindowManager_the;
|
||||
return *s_WinServer_WindowManager_the;
|
||||
}
|
||||
|
||||
WindowManager();
|
||||
|
||||
void add_window(Window* window);
|
||||
void remove_window(Window* window);
|
||||
|
||||
void resize_window(Window& window, const LG::Size& size);
|
||||
void close_window(Window& window) { send_event(new WindowCloseRequestMessage(window.connection_id(), window.id())); }
|
||||
void minimize_window(Window& window);
|
||||
void maximize_window(Window& window);
|
||||
|
||||
template <typename Callback>
|
||||
void minimize_windows(Callback callback)
|
||||
{
|
||||
std::vector<Window*> hided;
|
||||
for (auto* window : m_windows) {
|
||||
if (window && window->type() == WindowType::Standard && callback(window)) {
|
||||
window->set_visible(false);
|
||||
hided.push_back(window);
|
||||
on_window_became_invisible(window);
|
||||
remove_attention_from_window(window);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* window : hided) {
|
||||
m_windows.erase(std::find(m_windows.begin(), m_windows.end(), window));
|
||||
m_windows.push_back(window);
|
||||
}
|
||||
}
|
||||
|
||||
Window* top_window_in_view(WindowType type) const;
|
||||
inline Window* window(int id)
|
||||
{
|
||||
for (auto* window : m_windows) {
|
||||
if (window->id() == id) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline void move_window(Window* window, int x_offset, int y_offset)
|
||||
{
|
||||
y_offset = std::max(y_offset, (int)visible_area().min_y() - (int)Desktop::WindowFrame::std_top_border_frame_size() - window->bounds().min_y());
|
||||
if (m_dock.has_value()) [[likely]] {
|
||||
y_offset = std::min(y_offset, (int)(visible_area().max_y() - window->content_bounds().min_y()));
|
||||
}
|
||||
window->bounds().offset_by(x_offset, y_offset);
|
||||
window->content_bounds().offset_by(x_offset, y_offset);
|
||||
}
|
||||
|
||||
inline void do_bring_to_front(Window& window)
|
||||
{
|
||||
auto* window_ptr = &window;
|
||||
m_windows.erase(std::find(m_windows.begin(), m_windows.end(), window_ptr));
|
||||
m_windows.push_front(window_ptr);
|
||||
}
|
||||
void bring_to_front(Window& window);
|
||||
|
||||
inline std::list<Window*>& windows() { return m_windows; }
|
||||
inline const std::list<Window*>& windows() const { return m_windows; }
|
||||
inline int next_win_id() { return ++m_next_win_id; }
|
||||
|
||||
const LG::Rect& visible_area() const { return m_visible_area; }
|
||||
void shrink_visible_area(int top, int bottom) { m_visible_area.set_y(m_visible_area.min_y() + top), m_visible_area.set_height(m_visible_area.height() - top - bottom); }
|
||||
|
||||
void receive_event(std::unique_ptr<LFoundation::Event> event) override;
|
||||
|
||||
// Notifiers
|
||||
bool notify_listner_about_window_creation(const Window& window, int changed_window_id);
|
||||
bool notify_listner_about_window_status(const Window& window, int changed_window_id, WindowStatusUpdateType type);
|
||||
bool notify_listner_about_changed_icon(const Window&, int changed_window_id);
|
||||
bool notify_listner_about_changed_title(const Window&, int changed_window_id);
|
||||
|
||||
void notify_window_creation(int changed_window_id);
|
||||
void notify_window_status_changed(int changed_window_id, WindowStatusUpdateType type);
|
||||
void notify_window_icon_changed(int changed_window_id);
|
||||
void notify_window_title_changed(int changed_window_id);
|
||||
|
||||
inline void ask_to_set_active_window(Window* win) { set_active_window(win); }
|
||||
inline void ask_to_set_active_window(Window& win) { set_active_window(win); }
|
||||
|
||||
void on_window_misbehave(Window& window, ViolationClass);
|
||||
void on_window_style_change(Window& win);
|
||||
void on_window_menubar_change(Window& window);
|
||||
|
||||
// Popup & Menubar
|
||||
inline Popup& popup() { return m_compositor.popup(); }
|
||||
inline const Popup& popup() const { return m_compositor.popup(); }
|
||||
inline MenuBar& menu_bar() { return m_compositor.menu_bar(); }
|
||||
inline const MenuBar& menu_bar() const { return m_compositor.menu_bar(); }
|
||||
|
||||
private:
|
||||
void add_system_window(Window* window);
|
||||
void bring_system_windows_to_front();
|
||||
void setup_dock(Window* window);
|
||||
void setup_applist(Window* window);
|
||||
|
||||
void remove_attention_from_window(Window* window);
|
||||
|
||||
void start_window_move(Window& window);
|
||||
bool continue_window_move();
|
||||
|
||||
void update_mouse_position(std::unique_ptr<LFoundation::Event> mouse_event);
|
||||
void receive_mouse_event(std::unique_ptr<LFoundation::Event> event);
|
||||
void receive_keyboard_event(std::unique_ptr<LFoundation::Event> event);
|
||||
|
||||
inline Window* movable_window() { return m_movable_window; }
|
||||
inline Window* hovered_window() { return m_hovered_window; }
|
||||
inline void set_hovered_window(Window* win) { m_hovered_window = win; }
|
||||
|
||||
inline Window* active_window() { return m_active_window; }
|
||||
inline void set_active_window(Window* win) { on_active_window_will_change(), bring_to_front(*win), m_active_window = win, on_active_window_did_change(); }
|
||||
inline void set_active_window(Window& win) { on_active_window_will_change(), bring_to_front(win), m_active_window = &win, on_active_window_did_change(); }
|
||||
inline void set_active_window(std::nullptr_t) { on_active_window_will_change(), m_active_window = nullptr, on_active_window_did_change(); }
|
||||
|
||||
void on_window_became_invisible(Window* window);
|
||||
void on_active_window_will_change();
|
||||
void on_active_window_did_change();
|
||||
|
||||
inline void send_event(Message* msg) { m_event_loop.add(m_connection, new SendEvent(msg)); }
|
||||
|
||||
std::list<Window*> m_windows;
|
||||
|
||||
Screen& m_screen;
|
||||
Connection& m_connection;
|
||||
Compositor& m_compositor;
|
||||
CursorManager& m_cursor_manager;
|
||||
LFoundation::EventLoop& m_event_loop;
|
||||
std::vector<MenuDir> m_std_menubar_content;
|
||||
|
||||
LG::Rect m_visible_area;
|
||||
|
||||
SystemApp m_dock;
|
||||
SystemApp m_applist;
|
||||
|
||||
// TODO: implement with std::weak_ptr.
|
||||
Window* m_movable_window {};
|
||||
Window* m_active_window {};
|
||||
Window* m_hovered_window {};
|
||||
int m_next_win_id { 0 };
|
||||
};
|
||||
|
||||
} // namespace WinServer
|
||||
24
userland/servers/window_server/src/SystemApps/SystemApp.h
Normal file
24
userland/servers/window_server/src/SystemApps/SystemApp.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Target/Generic/Window.h"
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
class SystemApp {
|
||||
public:
|
||||
SystemApp() = default;
|
||||
~SystemApp() = default;
|
||||
|
||||
void set_window(Window* win) { m_window = win; }
|
||||
Window* window() { return m_window; }
|
||||
const Window* window() const { return m_window; }
|
||||
|
||||
bool has_value() const { return !!m_window; }
|
||||
operator bool() const { return has_value(); }
|
||||
|
||||
private:
|
||||
bool visible;
|
||||
Window* m_window {};
|
||||
};
|
||||
|
||||
}
|
||||
76
userland/servers/window_server/src/Target/Desktop/Window.cpp
Normal file
76
userland/servers/window_server/src/Target/Desktop/Window.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include "Window.h"
|
||||
#include "../../Managers/WindowManager.h"
|
||||
#include <utility>
|
||||
|
||||
namespace WinServer::Desktop {
|
||||
|
||||
Window::Window(int connection_id, int id, CreateWindowMessage& msg)
|
||||
: BaseWindow(connection_id, id, msg)
|
||||
, m_frame(*this)
|
||||
{
|
||||
m_bounds = LG::Rect(0, 0, msg.width() + frame().left_border_size() + frame().right_border_size(), msg.height() + frame().top_border_size() + frame().bottom_border_size());
|
||||
m_content_bounds = LG::Rect(m_frame.left_border_size(), m_frame.top_border_size(), msg.width(), msg.height());
|
||||
m_content_bitmap = LG::PixelBitmap(m_buffer.data(), content_bounds().width(), content_bounds().height());
|
||||
|
||||
// Creating standard menubar directory entry.
|
||||
m_menubar_content.push_back(MenuDir(m_app_name, 0));
|
||||
m_menubar_content[0].add_item(PopupItem { PopupItem::InternalId, "Minimize others", [this](int) { WindowManager::the().minimize_windows([this](Window* win) { return win != this; }); } });
|
||||
m_menubar_content[0].add_item(PopupItem { PopupItem::InternalId, "Minimize", [this](int) { WindowManager::the().minimize_window(*this); } });
|
||||
m_menubar_content[0].add_item(PopupItem { PopupItem::InternalId, "Close", [this](int) { WindowManager::the().close_window(*this); } });
|
||||
}
|
||||
|
||||
Window::Window(Window&& win)
|
||||
: BaseWindow(std::move(win))
|
||||
, m_frame(*this, std::move(win.m_frame.control_panel_buttons()), std::move(win.m_frame.window_control_buttons()))
|
||||
, m_corner_mask(std::move(win.m_corner_mask))
|
||||
, m_menubar_content(std::move(win.m_menubar_content))
|
||||
{
|
||||
}
|
||||
|
||||
void Window::make_frame()
|
||||
{
|
||||
uint32_t x = m_bounds.min_x();
|
||||
uint32_t y = m_bounds.min_y();
|
||||
uint32_t content_width = m_content_bounds.width();
|
||||
uint32_t content_height = m_content_bounds.height();
|
||||
m_bounds = LG::Rect(x, y, content_width + frame().left_border_size() + frame().right_border_size(), content_height + frame().top_border_size() + frame().bottom_border_size());
|
||||
m_content_bounds = LG::Rect(x + m_frame.left_border_size(), y + m_frame.top_border_size(), content_width, content_height);
|
||||
m_frame.set_visible(true);
|
||||
}
|
||||
|
||||
void Window::make_frameless()
|
||||
{
|
||||
uint32_t x = m_bounds.min_x();
|
||||
uint32_t y = m_bounds.min_y();
|
||||
uint32_t content_width = m_content_bounds.width();
|
||||
uint32_t content_height = m_content_bounds.height();
|
||||
m_bounds = LG::Rect(x, y, content_width, content_height);
|
||||
m_content_bounds = LG::Rect(x, y, content_width, content_height);
|
||||
m_frame.set_visible(false);
|
||||
}
|
||||
|
||||
void Window::recalc_bounds(const LG::Size& size)
|
||||
{
|
||||
m_content_bounds.set_width(size.width());
|
||||
m_content_bounds.set_height(size.height());
|
||||
|
||||
m_bounds.set_width(size.width() + frame().left_border_size() + frame().right_border_size());
|
||||
m_bounds.set_height(size.height() + frame().top_border_size() + frame().bottom_border_size());
|
||||
}
|
||||
|
||||
void Window::did_size_change(const LG::Size& size)
|
||||
{
|
||||
recalc_bounds(size);
|
||||
}
|
||||
|
||||
void Window::on_style_change()
|
||||
{
|
||||
WindowManager::the().on_window_style_change(*this);
|
||||
}
|
||||
|
||||
void Window::on_menubar_change()
|
||||
{
|
||||
WindowManager::the().on_window_menubar_change(*this);
|
||||
}
|
||||
|
||||
} // namespace WinServer
|
||||
47
userland/servers/window_server/src/Target/Desktop/Window.h
Normal file
47
userland/servers/window_server/src/Target/Desktop/Window.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#include "../../Components/Base/BaseWindow.h"
|
||||
#include "../../Components/MenuBar/MenuItem.h"
|
||||
#include "../../IPC/Connection.h"
|
||||
#include "WindowFrame.h"
|
||||
#include <libfoundation/SharedBuffer.h>
|
||||
#include <libg/PixelBitmap.h>
|
||||
#include <libg/Rect.h>
|
||||
#include <sys/types.h>
|
||||
#include <utility>
|
||||
|
||||
namespace WinServer::Desktop {
|
||||
|
||||
class Window : public BaseWindow {
|
||||
public:
|
||||
Window(int connection_id, int id, CreateWindowMessage& msg);
|
||||
Window(Window&& win);
|
||||
|
||||
inline WindowFrame& frame() { return m_frame; }
|
||||
inline const WindowFrame& frame() const { return m_frame; }
|
||||
|
||||
inline const LG::CornerMask& corner_mask() const { return m_corner_mask; }
|
||||
|
||||
inline std::vector<MenuDir>& menubar_content() { return m_menubar_content; }
|
||||
inline const std::vector<MenuDir>& menubar_content() const { return m_menubar_content; }
|
||||
|
||||
void on_menubar_change();
|
||||
|
||||
virtual void did_app_title_change() override { m_frame.on_set_app_title(); }
|
||||
virtual void did_icon_path_change() override { m_frame.on_set_icon(); }
|
||||
virtual void did_size_change(const LG::Size& size) override;
|
||||
|
||||
inline void set_style(StatusBarStyle style) { m_frame.set_style(style), on_style_change(); }
|
||||
|
||||
void make_frame();
|
||||
void make_frameless();
|
||||
|
||||
private:
|
||||
void recalc_bounds(const LG::Size& size);
|
||||
void on_style_change();
|
||||
|
||||
WindowFrame m_frame;
|
||||
LG::CornerMask m_corner_mask { LG::CornerMask::SystemRadius, LG::CornerMask::NonMasked, LG::CornerMask::Masked };
|
||||
std::vector<MenuDir> m_menubar_content;
|
||||
};
|
||||
|
||||
} // namespace WinServer
|
||||
@@ -0,0 +1,232 @@
|
||||
#include "WindowFrame.h"
|
||||
#include "../../Components/Elements/Button.h"
|
||||
#include "../../Managers/WindowManager.h"
|
||||
#include "Window.h"
|
||||
#include <libg/Font.h>
|
||||
#include <libg/ImageLoaders/PNGLoader.h>
|
||||
#include <libg/Rect.h>
|
||||
#include <utility>
|
||||
|
||||
#define CONTROL_PANEL_CLOSE 0x0
|
||||
#define CONTROL_PANEL_MAXIMIZE 0x1
|
||||
#define CONTROL_PANEL_MINIMIZE 0x2
|
||||
|
||||
namespace WinServer::Desktop {
|
||||
|
||||
static const uint32_t s_close_button_glyph_data[10] = {
|
||||
0b1100000011,
|
||||
0b1110000111,
|
||||
0b0111001110,
|
||||
0b0011111100,
|
||||
0b0001111000,
|
||||
0b0001111000,
|
||||
0b0011111100,
|
||||
0b0111001110,
|
||||
0b1110000111,
|
||||
0b1100000011,
|
||||
};
|
||||
|
||||
static const uint32_t s_maximise_button_glyph_data[10] = {
|
||||
0b1111100000,
|
||||
0b1111000000,
|
||||
0b1110000000,
|
||||
0b1100000000,
|
||||
0b1000000000,
|
||||
0b0000000001,
|
||||
0b0000000011,
|
||||
0b0000000111,
|
||||
0b0000001111,
|
||||
0b0000011111
|
||||
};
|
||||
|
||||
static const uint32_t s_minimise_button_glyph_data[10] = {
|
||||
0b0000000000,
|
||||
0b0000000000,
|
||||
0b0000000000,
|
||||
0b0000000000,
|
||||
0b0000000000,
|
||||
0b0000000000,
|
||||
0b0000000000,
|
||||
0b0000000000,
|
||||
0b1111111111,
|
||||
0b1111111111
|
||||
};
|
||||
|
||||
WindowFrame::WindowFrame(Window& window)
|
||||
: m_window(window)
|
||||
, m_window_control_buttons()
|
||||
, m_control_panel_buttons()
|
||||
{
|
||||
set_style(StatusBarStyle::StandardLight);
|
||||
LG::GlyphMetrics metrics = {
|
||||
.width = 10,
|
||||
.height = 10,
|
||||
.top_bearing = 10,
|
||||
.left_bearing = 0,
|
||||
.baseline = 0,
|
||||
.advance = 10,
|
||||
.font_size = 10
|
||||
};
|
||||
|
||||
auto* close = new Button();
|
||||
close->set_icon(LG::Glyph(s_close_button_glyph_data, metrics, LG::Glyph::ConstDataMarker {}));
|
||||
auto* maximize = new Button();
|
||||
maximize->set_icon(LG::Glyph(s_maximise_button_glyph_data, metrics, LG::Glyph::ConstDataMarker {}));
|
||||
auto* minimize = new Button();
|
||||
minimize->set_icon(LG::Glyph(s_minimise_button_glyph_data, metrics, LG::Glyph::ConstDataMarker {}));
|
||||
|
||||
close->set_title_color(LG::Color(196, 128, 128));
|
||||
|
||||
m_window_control_buttons.push_back(close);
|
||||
m_window_control_buttons.push_back(maximize);
|
||||
m_window_control_buttons.push_back(minimize);
|
||||
}
|
||||
|
||||
WindowFrame::WindowFrame(Window& window, std::vector<Button*>&& control_panel_buttons, std::vector<Button*>&& window_control_buttons)
|
||||
: m_window(window)
|
||||
, m_window_control_buttons(std::move(window_control_buttons))
|
||||
, m_control_panel_buttons(std::move(control_panel_buttons))
|
||||
{
|
||||
}
|
||||
|
||||
void WindowFrame::on_set_app_title()
|
||||
{
|
||||
m_app_title_width = Helpers::text_width(m_window.app_title(), LG::Font::system_font());
|
||||
if (style().show_text()) {
|
||||
WinServer::Compositor::the().invalidate(bounds());
|
||||
}
|
||||
}
|
||||
|
||||
void WindowFrame::add_control(const std::string& title)
|
||||
{
|
||||
auto* new_control = new Button();
|
||||
new_control->set_title(title);
|
||||
m_control_panel_buttons.push_back(new_control);
|
||||
WinServer::Compositor::the().invalidate(bounds());
|
||||
}
|
||||
|
||||
void WindowFrame::draw(LG::Context& ctx)
|
||||
{
|
||||
if (!visible()) {
|
||||
return;
|
||||
}
|
||||
int x = m_window.bounds().min_x();
|
||||
int y = m_window.bounds().min_y();
|
||||
size_t width = m_window.bounds().width();
|
||||
size_t height = m_window.bounds().height();
|
||||
|
||||
int left_x = x + left_border_size();
|
||||
int right_x = x + width - right_border_size();
|
||||
int bottom_y = y + height - bottom_border_size();
|
||||
|
||||
// Drawing frame and shadings
|
||||
int shadow_spread = LG::Shading::SystemSpread;
|
||||
ctx.set_fill_color(style().color());
|
||||
ctx.fill_rounded(LG::Rect(x + left_border_size(), y + std_top_border_frame_size(), width - 2 * left_border_size(), top_border_size() - std_top_border_frame_size()), LG::CornerMask(4, true, false));
|
||||
ctx.set_fill_color(Color::Shadow);
|
||||
const auto shading_rect = LG::Rect(x + left_border_size(), y + std_top_border_frame_size(), width - 2 * left_border_size(), height - std_top_border_frame_size() - std_bottom_border_size());
|
||||
ctx.draw_box_shading(shading_rect, LG::Shading(LG::Shading::Type::Box, 0, shadow_spread), LG::CornerMask(LG::CornerMask::SystemRadius));
|
||||
|
||||
// Drawing labels, icons.
|
||||
// Drawing positions are calculated using a start of the frame.
|
||||
ctx.set_fill_color(m_text_colors[(int)active()]);
|
||||
if (style().show_text()) {
|
||||
Helpers::draw_text(ctx, { left_x + spacing(), y + text_y_offset() }, m_window.app_title(), LG::Font::system_font());
|
||||
}
|
||||
|
||||
int start_controls_offset = m_app_title_width + 2 * spacing();
|
||||
int start_controls = left_x + start_controls_offset;
|
||||
for (int i = 0; i < m_control_panel_buttons.size(); i++) {
|
||||
m_control_panel_buttons[i]->display(ctx, { start_controls, y + text_y_offset() });
|
||||
start_controls += spacing() + m_control_panel_buttons[i]->bounds().width();
|
||||
}
|
||||
|
||||
int start_buttons = right_x - spacing() - m_window_control_buttons[0]->bounds().width();
|
||||
for (int i = 0; i < m_window_control_buttons.size(); i++) {
|
||||
if (active() && i == 0) {
|
||||
ctx.set_fill_color(m_window_control_buttons[i]->title_color());
|
||||
} else {
|
||||
ctx.set_fill_color(m_text_colors[(int)active()]);
|
||||
}
|
||||
|
||||
m_window_control_buttons[i]->display(ctx, { start_buttons, y + button_y_offset() });
|
||||
start_buttons += -spacing() - m_window_control_buttons[i]->bounds().width();
|
||||
}
|
||||
}
|
||||
|
||||
void WindowFrame::invalidate(WinServer::Compositor& compositor) const
|
||||
{
|
||||
if (!visible()) {
|
||||
return;
|
||||
}
|
||||
int x = m_window.bounds().min_x();
|
||||
int y = m_window.bounds().min_y();
|
||||
size_t width = m_window.bounds().width();
|
||||
size_t height = m_window.bounds().height();
|
||||
int right_x = x + width - right_border_size();
|
||||
int bottom_y = y + height - bottom_border_size();
|
||||
compositor.invalidate(LG::Rect(x, y, width, top_border_size()));
|
||||
compositor.invalidate(LG::Rect(x, y, left_border_size(), height));
|
||||
compositor.invalidate(LG::Rect(right_x, y, right_border_size(), height));
|
||||
compositor.invalidate(LG::Rect(x, bottom_y, width, bottom_border_size()));
|
||||
}
|
||||
|
||||
void WindowFrame::receive_tap_event(const LG::Point<int>& tap)
|
||||
{
|
||||
// Calculating buttons' positions
|
||||
size_t width = m_window.bounds().width();
|
||||
int right_x = width - right_border_size();
|
||||
int start_buttons = right_x - spacing() - m_window_control_buttons[0]->bounds().width();
|
||||
for (int button_id = 0; button_id < m_window_control_buttons.size(); button_id++) {
|
||||
auto button_frame = m_window_control_buttons[button_id]->bounds();
|
||||
button_frame.offset_by(start_buttons, button_y_offset());
|
||||
if (button_frame.contains(tap)) {
|
||||
handle_control_panel_tap(button_id);
|
||||
return;
|
||||
}
|
||||
start_buttons += -spacing() - button_frame.width();
|
||||
}
|
||||
}
|
||||
|
||||
const LG::Rect WindowFrame::bounds() const
|
||||
{
|
||||
const auto& bounds = m_window.bounds();
|
||||
return LG::Rect(bounds.min_x(), bounds.min_y(), bounds.width(), top_border_size());
|
||||
}
|
||||
|
||||
void WindowFrame::handle_control_panel_tap(int button_id)
|
||||
{
|
||||
auto& wm = WindowManager::the();
|
||||
switch (button_id) {
|
||||
case CONTROL_PANEL_CLOSE:
|
||||
wm.close_window(m_window);
|
||||
break;
|
||||
case CONTROL_PANEL_MAXIMIZE:
|
||||
wm.maximize_window(m_window);
|
||||
break;
|
||||
case CONTROL_PANEL_MINIMIZE:
|
||||
wm.minimize_window(m_window);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void WindowFrame::set_style(StatusBarStyle ts)
|
||||
{
|
||||
m_style = ts;
|
||||
|
||||
if (m_style.dark_text()) {
|
||||
m_text_colors[0] = Color::InactiveText;
|
||||
m_text_colors[1] = LG::Color::DarkSystemText;
|
||||
} else {
|
||||
m_text_colors[0] = Color::InactiveText;
|
||||
m_text_colors[1] = LG::Color::LightSystemText;
|
||||
}
|
||||
}
|
||||
|
||||
void WindowFrame::on_set_icon()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace WinServer
|
||||
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
#include "../../Components/Elements/Button.h"
|
||||
#include "../../Constants/Colors.h"
|
||||
#include "../../IPC/Event.h"
|
||||
#include <libapi/window_server/MessageContent/MenuBar.h>
|
||||
#include <libfoundation/EventReceiver.h>
|
||||
#include <libg/Context.h>
|
||||
#include <libg/PixelBitmap.h>
|
||||
|
||||
namespace WinServer {
|
||||
class Compositor;
|
||||
}
|
||||
|
||||
namespace WinServer::Desktop {
|
||||
|
||||
class Window;
|
||||
|
||||
class WindowFrame {
|
||||
public:
|
||||
explicit WindowFrame(Window& window);
|
||||
WindowFrame(Window& window, std::vector<Button*>&& control_panel_buttons, std::vector<Button*>&& window_control_buttons);
|
||||
~WindowFrame() = default;
|
||||
|
||||
void draw(LG::Context&);
|
||||
static constexpr size_t std_app_header_size() { return 26; }
|
||||
static constexpr size_t std_top_border_frame_size() { return 4; }
|
||||
static constexpr size_t std_top_border_size() { return std_top_border_frame_size() + std_app_header_size(); }
|
||||
static constexpr size_t std_bottom_border_size() { return 4; }
|
||||
static constexpr size_t std_left_border_size() { return 4; }
|
||||
static constexpr size_t std_right_border_size() { return 4; }
|
||||
inline size_t top_border_size() const { return m_top_border_size; }
|
||||
inline size_t bottom_border_size() const { return std_bottom_border_size(); }
|
||||
inline size_t left_border_size() const { return std_left_border_size(); }
|
||||
inline size_t right_border_size() const { return std_right_border_size(); }
|
||||
|
||||
const LG::Rect bounds() const;
|
||||
|
||||
void receive_tap_event(const LG::Point<int>& tap);
|
||||
|
||||
void on_set_app_title();
|
||||
void add_control(const std::string& title);
|
||||
|
||||
inline std::vector<Button*>& window_control_buttons() { return m_window_control_buttons; }
|
||||
inline const std::vector<Button*>& window_control_buttons() const { return m_window_control_buttons; }
|
||||
|
||||
inline std::vector<Button*>& control_panel_buttons() { return m_control_panel_buttons; }
|
||||
inline const std::vector<Button*>& control_panel_buttons() const { return m_control_panel_buttons; }
|
||||
void handle_control_panel_tap(int button_id);
|
||||
|
||||
inline StatusBarStyle style() const { return m_style; }
|
||||
void set_style(StatusBarStyle ts);
|
||||
|
||||
void set_visible(bool visible)
|
||||
{
|
||||
m_top_border_size = visible ? std_top_border_size() : 0;
|
||||
m_visible = visible;
|
||||
}
|
||||
|
||||
bool visible() const { return m_visible; }
|
||||
void set_active(bool active) { m_active = active; }
|
||||
bool active() const { return m_active; }
|
||||
|
||||
void invalidate(WinServer::Compositor& compositor) const;
|
||||
|
||||
void on_set_icon();
|
||||
|
||||
static constexpr int spacing() { return 8; }
|
||||
static constexpr int icon_width() { return 12; }
|
||||
static constexpr int icon_y_offset() { return 7 + std_top_border_frame_size(); }
|
||||
static constexpr int text_y_offset() { return 9 + std_top_border_frame_size(); }
|
||||
static constexpr int button_y_offset() { return 8 + std_top_border_frame_size(); }
|
||||
|
||||
private:
|
||||
Window& m_window;
|
||||
std::vector<Button*> m_window_control_buttons;
|
||||
std::vector<Button*> m_control_panel_buttons;
|
||||
LG::Color m_text_colors[2];
|
||||
LG::Color m_color { LG::Color::LightSystemBackground };
|
||||
size_t m_top_border_size { std_top_border_size() };
|
||||
size_t m_app_title_width { 0 };
|
||||
bool m_visible { true };
|
||||
bool m_active { true };
|
||||
|
||||
StatusBarStyle m_style;
|
||||
};
|
||||
|
||||
} // namespace WinServer
|
||||
14
userland/servers/window_server/src/Target/Generic/Window.h
Normal file
14
userland/servers/window_server/src/Target/Generic/Window.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Desktop/Window.h"
|
||||
#include "../Mobile/Window.h"
|
||||
|
||||
namespace WinServer {
|
||||
|
||||
#ifdef TARGET_DESKTOP
|
||||
using Window = Desktop::Window;
|
||||
#elif TARGET_MOBILE
|
||||
using Window = Mobile::Window;
|
||||
#endif
|
||||
|
||||
} // namespace WinServer
|
||||
25
userland/servers/window_server/src/Target/Mobile/Window.cpp
Normal file
25
userland/servers/window_server/src/Target/Mobile/Window.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "Window.h"
|
||||
#include "../../Managers/WindowManager.h"
|
||||
#include <utility>
|
||||
|
||||
namespace WinServer::Mobile {
|
||||
|
||||
Window::Window(int connection_id, int id, CreateWindowMessage& msg)
|
||||
: BaseWindow(connection_id, id, msg)
|
||||
{
|
||||
m_bounds = LG::Rect(0, 0, msg.width(), msg.height());
|
||||
m_content_bounds = LG::Rect(0, 0, msg.width(), msg.height());
|
||||
m_content_bitmap = LG::PixelBitmap(m_buffer.data(), content_bounds().width(), content_bounds().height());
|
||||
}
|
||||
|
||||
Window::Window(Window&& win)
|
||||
: BaseWindow(std::move(win))
|
||||
{
|
||||
}
|
||||
|
||||
void Window::on_style_change()
|
||||
{
|
||||
WindowManager::the().on_window_style_change(*this);
|
||||
}
|
||||
|
||||
} // namespace WinServer
|
||||
27
userland/servers/window_server/src/Target/Mobile/Window.h
Normal file
27
userland/servers/window_server/src/Target/Mobile/Window.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include "../../Components/Base/BaseWindow.h"
|
||||
#include "../../Components/MenuBar/MenuItem.h"
|
||||
#include "../../IPC/Connection.h"
|
||||
#include <libfoundation/SharedBuffer.h>
|
||||
#include <libg/PixelBitmap.h>
|
||||
#include <libg/Rect.h>
|
||||
#include <sys/types.h>
|
||||
#include <utility>
|
||||
|
||||
namespace WinServer::Mobile {
|
||||
|
||||
class Window : public BaseWindow {
|
||||
public:
|
||||
Window(int connection_id, int id, CreateWindowMessage& msg);
|
||||
Window(Window&& win);
|
||||
|
||||
inline void set_style(StatusBarStyle ts) { m_style = ts, on_style_change(); }
|
||||
inline StatusBarStyle style() { return m_style; }
|
||||
|
||||
private:
|
||||
void on_style_change();
|
||||
|
||||
StatusBarStyle m_style { StatusBarStyle::StandardLight };
|
||||
};
|
||||
|
||||
} // namespace WinServer
|
||||
32
userland/servers/window_server/src/main.cpp
Normal file
32
userland/servers/window_server/src/main.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "Managers/InitManager.h"
|
||||
|
||||
#ifdef TARGET_DESKTOP
|
||||
#define LAUNCH_PATH "/System/dock"
|
||||
#elif TARGET_MOBILE
|
||||
#define LAUNCH_PATH "/System/homescreen"
|
||||
#endif
|
||||
|
||||
int main()
|
||||
{
|
||||
WinServer::InitManager::load_screen();
|
||||
|
||||
auto* event_loop = new LFoundation::EventLoop();
|
||||
WinServer::InitManager::load_core_component<WinServer::Connection>();
|
||||
WinServer::InitManager::load_core_component<WinServer::CursorManager>();
|
||||
WinServer::InitManager::load_core_component<WinServer::ResourceManager, 4>();
|
||||
WinServer::InitManager::load_core_component<WinServer::Popup>();
|
||||
WinServer::InitManager::load_core_component<WinServer::MenuBar>();
|
||||
#ifdef TARGET_MOBILE
|
||||
WinServer::InitManager::load_core_component<WinServer::ControlBar>();
|
||||
#endif
|
||||
WinServer::InitManager::load_core_component<WinServer::Compositor>();
|
||||
WinServer::InitManager::load_core_component<WinServer::WindowManager>();
|
||||
WinServer::InitManager::load_core_component<WinServer::Devices>();
|
||||
|
||||
WinServer::InitManager::add_widget<WinServer::Clock>();
|
||||
|
||||
WinServer::InitManager::launch_app(LAUNCH_PATH, 10);
|
||||
|
||||
WinServer::LoadingScreen::destroy_the();
|
||||
return event_loop->run();
|
||||
}
|
||||
Reference in New Issue
Block a user