Squash commits for public release

This commit is contained in:
2025-02-12 09:54:05 -05:00
commit 7118adc514
1108 changed files with 80873 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
APPS += ABOUT
ABOUT_NAME = about
ABOUT_LIBS = cxx ui
ABOUT_INSTALL_PATH = bin/

View File

@@ -0,0 +1,64 @@
#pragma once
#include <libg/Font.h>
#include <libui/Label.h>
#include <libui/View.h>
#include <string>
class AboutLineView : public UI::View {
UI_OBJECT();
public:
AboutLineView(UI::View* superview, const LG::Rect& frame, std::string title, std::string content)
: UI::View(superview, frame)
, m_title(title)
, m_content(content)
{
const int spacing = 4;
set_background_color(LG::Color::LightSystemBackground);
auto& label = add_subview<UI::Label>(LG::Rect(0, 0, 16, 16));
label.set_text_color(LG::Color::DarkSystemText);
label.set_text(m_title);
label.set_width(label.preferred_width());
auto& target_label = add_subview<UI::Label>(LG::Rect(0, 0, 16, 16));
target_label.set_text_color(LG::Color::DarkSystemText);
target_label.set_text(m_content);
target_label.set_width(target_label.preferred_width());
add_constraint(UI::Constraint(target_label, UI::Constraint::Attribute::Left, UI::Constraint::Relation::Equal, label, UI::Constraint::Attribute::Right, 1, spacing));
add_constraint(UI::Constraint(target_label, UI::Constraint::Attribute::Top, UI::Constraint::Relation::Equal, label, UI::Constraint::Attribute::Top, 1, 0));
set_width(label.preferred_width() + spacing + target_label.preferred_width());
}
AboutLineView(UI::View* superview, UI::Window* window, const LG::Rect& frame, std::string title, std::string content)
: UI::View(superview, window, frame)
, m_title(title)
, m_content(content)
{
const int spacing = 4;
set_background_color(LG::Color::LightSystemBackground);
auto& label = add_subview<UI::Label>(LG::Rect(0, 0, 16, 16));
label.set_text_color(LG::Color::DarkSystemText);
label.set_text(m_title);
label.set_width(label.preferred_width());
auto& target_label = add_subview<UI::Label>(LG::Rect(0, 0, 16, 16));
target_label.set_text_color(LG::Color::DarkSystemText);
target_label.set_text(m_content);
target_label.set_width(target_label.preferred_width());
add_constraint(UI::Constraint(target_label, UI::Constraint::Attribute::Left, UI::Constraint::Relation::Equal, label, UI::Constraint::Attribute::Right, 1, spacing));
add_constraint(UI::Constraint(target_label, UI::Constraint::Attribute::Top, UI::Constraint::Relation::Equal, label, UI::Constraint::Attribute::Top, 1, 0));
set_width(label.preferred_width() + spacing + target_label.preferred_width());
}
~AboutLineView() = default;
private:
std::string m_title {};
std::string m_content {};
};

View File

@@ -0,0 +1,29 @@
#include "ViewController.h"
#include <libui/AppDelegate.h>
#include <libui/MenuBar.h>
class AppDelegate : public UI::AppDelegate {
public:
AppDelegate() = default;
virtual ~AppDelegate() = default;
LG::Size preferred_desktop_window_size() const override { return LG::Size(200, 210); }
const char* icon_path() const override { return "/res/icons/apps/about.icon"; }
virtual bool application() override
{
auto style = StatusBarStyle(LG::Color(231, 240, 250)).set_hide_text();
auto& window = std::xos::construct<UI::Window>("About", window_size(), icon_path(), style);
auto& superview = window.create_superview<UI::View, ViewController>();
auto demo_menu = UI::Menu("Demo");
demo_menu.add_item(UI::MenuItem("Say hello", [] { Logger::debug << "Hello!" << std::endl; }));
window.menubar().add_menu(std::move(demo_menu));
return true;
}
private:
};
SET_APP_DELEGATE(AppDelegate);

View File

@@ -0,0 +1,17 @@
import("//build/userland/TEMPLATE.gni")
xOS_application("about") {
display_name = "About"
sources = [
"AppDelegate.cpp",
]
configs = [
"//build/userland:userland_flags",
]
deplibs = [
"libcxx",
"libfoundation",
"libg",
"libui"
]
}

View File

@@ -0,0 +1,78 @@
#pragma once
#include "AboutLineView.h"
#include <libui/App.h>
#include <libui/Button.h>
#include <libui/Label.h>
#include <libui/View.h>
#include <libui/ViewController.h>
#include <libui/Window.h>
#include <memory>
#include <sys/types.h>
#include <sys/utsname.h>
class ViewController : public UI::ViewController<UI::View> {
public:
ViewController(UI::View& view)
: UI::ViewController<UI::View>(view)
{
}
virtual ~ViewController() = default;
void view_did_load() override
{
view().set_background_color(LG::Color::LightSystemBackground);
utsname_t uts;
int rc = uname(&uts);
auto& header = view().add_subview<UI::View>(LG::Rect(0, 0, 0, 0));
header.set_background_color(LG::Color(231, 240, 250));
auto& label = header.add_subview<UI::Label>(LG::Rect(0, 0, 16, 22));
label.set_text("About");
label.set_text_color(LG::Color(35, 70, 106));
label.set_font(LG::Font::system_bold_font(LG::Font::SystemTitleSize));
label.set_width(label.preferred_width());
auto& name_label = view().add_subview<AboutLineView>(LG::Rect(0, 0, 16, 16), "Name:", uts.sysname);
auto& cpu_label = view().add_subview<AboutLineView>(LG::Rect(0, 0, 16, 16), "CPU:", uts.machine);
auto& version_label = view().add_subview<AboutLineView>(LG::Rect(0, 0, 16, 16), "Version:", uts.release);
auto& button = view().add_subview<UI::Button>(LG::Rect(0, 0, 10, 10));
button.set_background_color(LG::Color::LightSystemButton);
button.set_title("System info");
button.set_title_color(LG::Color::DarkSystemText);
auto& footer = view().add_subview<UI::Label>(LG::Rect(0, 0, 16, 16));
footer.set_text_color(LG::Color::DarkSystemText);
footer.set_text("(c) 2020-2023");
footer.set_width(footer.preferred_width());
view().add_constraint(UI::Constraint(header, UI::Constraint::Attribute::Left, UI::Constraint::Relation::Equal, 0));
view().add_constraint(UI::Constraint(header, UI::Constraint::Attribute::Top, UI::Constraint::Relation::Equal, 0));
view().add_constraint(UI::Constraint(header, UI::Constraint::Attribute::Right, UI::Constraint::Relation::Equal, 0));
view().add_constraint(UI::Constraint(header, UI::Constraint::Attribute::Height, UI::Constraint::Relation::Equal, 60));
view().add_constraint(UI::Constraint(label, UI::Constraint::Attribute::Left, UI::Constraint::Relation::Equal, UI::SafeArea::Left));
view().add_constraint(UI::Constraint(label, UI::Constraint::Attribute::CenterY, UI::Constraint::Relation::Equal, header, UI::Constraint::Attribute::CenterY, 1, 0));
view().add_constraint(UI::Constraint(name_label, UI::Constraint::Attribute::Left, UI::Constraint::Relation::Equal, UI::SafeArea::Left));
view().add_constraint(UI::Constraint(name_label, UI::Constraint::Attribute::Top, UI::Constraint::Relation::Equal, header, UI::Constraint::Attribute::Bottom, 1, UI::Padding::AfterTitle));
view().add_constraint(UI::Constraint(cpu_label, UI::Constraint::Attribute::Left, UI::Constraint::Relation::Equal, UI::SafeArea::Left));
view().add_constraint(UI::Constraint(cpu_label, UI::Constraint::Attribute::Top, UI::Constraint::Relation::Equal, name_label, UI::Constraint::Attribute::Bottom, 1, 4));
view().add_constraint(UI::Constraint(version_label, UI::Constraint::Attribute::Left, UI::Constraint::Relation::Equal, UI::SafeArea::Left));
view().add_constraint(UI::Constraint(version_label, UI::Constraint::Attribute::Top, UI::Constraint::Relation::Equal, cpu_label, UI::Constraint::Attribute::Bottom, 1, 4));
view().add_constraint(UI::Constraint(button, UI::Constraint::Attribute::Left, UI::Constraint::Relation::Equal, UI::SafeArea::Left));
view().add_constraint(UI::Constraint(button, UI::Constraint::Attribute::Top, UI::Constraint::Relation::Equal, version_label, UI::Constraint::Attribute::Bottom, 1, 8));
view().add_constraint(UI::Constraint(footer, UI::Constraint::Attribute::CenterX, UI::Constraint::Relation::Equal, view(), UI::Constraint::Attribute::CenterX, 1, 0));
view().add_constraint(UI::Constraint(footer, UI::Constraint::Attribute::Bottom, UI::Constraint::Relation::Equal, view(), UI::Constraint::Attribute::Bottom, 1, -UI::SafeArea::Bottom));
view().set_needs_layout();
}
private:
};

View File

@@ -0,0 +1,23 @@
#include "ViewController.h"
#include <libui/AppDelegate.h>
class AppDelegate : public UI::AppDelegate {
public:
AppDelegate() = default;
virtual ~AppDelegate() = default;
LG::Size preferred_desktop_window_size() const override { return LG::Size(220, 210); }
const char* icon_path() const override { return "/res/icons/apps/activity_monitor.icon"; }
virtual bool application() override
{
auto style = StatusBarStyle(LG::Color(222, 232, 227)).set_hide_text();
auto& window = std::xos::construct<UI::Window>("Monitor", window_size(), icon_path(), style);
auto& superview = window.create_superview<UI::View, ViewController>();
return true;
}
private:
};
SET_APP_DELEGATE(AppDelegate);

View File

@@ -0,0 +1,16 @@
import("//build/userland/TEMPLATE.gni")
xOS_application("activity_monitor") {
display_name = "Monitor"
sources = [
"AppDelegate.cpp",
"GraphView.cpp",
]
configs = [ "//build/userland:userland_flags" ]
deplibs = [
"libcxx",
"libfoundation",
"libg",
"libui",
]
}

View File

@@ -0,0 +1,40 @@
#include "GraphView.h"
#include <algorithm>
#include <iostream>
#include <libfoundation/EventLoop.h>
#include <libfoundation/KeyboardMapping.h>
#include <libg/Color.h>
#include <libui/Context.h>
GraphView::GraphView(UI::View* superview, const LG::Rect& frame, int data_size)
: UI::View(superview, frame)
, m_data(data_size)
{
for (int i = 0; i < data_size; i++) {
m_data.push_back(0);
}
}
void GraphView::display(const LG::Rect& rect)
{
LG::Context ctx = UI::graphics_current_context();
ctx.add_clip(rect);
ctx.set_fill_color(LG::Color(233, 233, 233));
ctx.fill(bounds());
ctx.set_fill_color(LG::Color(14, 72, 19));
size_t left_padding = bounds().width();
size_t height = bounds().height();
size_t column_width = 3;
for (int i = m_data.size() - 1; i >= 0; i--) {
left_padding -= column_width;
size_t column_height = (m_data[i] * height) / 100;
ctx.fill(LG::Rect(left_padding, height - column_height, column_width, column_height));
if (left_padding < column_width) {
break;
}
}
}

View File

@@ -0,0 +1,25 @@
#pragma once
#include <libg/Font.h>
#include <libui/View.h>
#include <string>
class GraphView : public UI::View {
UI_OBJECT();
public:
GraphView(UI::View* superview, const LG::Rect&, int data_size);
void display(const LG::Rect& rect) override;
void add_new_value(int val)
{
for (int i = 0; i < m_data.size() - 1; i++) {
m_data[i] = m_data[i + 1];
}
m_data[m_data.size() - 1] = val;
set_needs_display();
}
private:
std::vector<int> m_data;
};

View File

@@ -0,0 +1,150 @@
#pragma once
#include "GraphView.h"
#include <libfoundation/ProcessInfo.h>
#include <libui/App.h>
#include <libui/Button.h>
#include <libui/Label.h>
#include <libui/StackView.h>
#include <libui/View.h>
#include <libui/ViewController.h>
#include <libui/Window.h>
#include <memory>
#include <sys/types.h>
static char buf[256];
class ViewController : public UI::ViewController<UI::View> {
public:
ViewController(UI::View& view)
: UI::ViewController<UI::View>(view)
, m_cpu_count(LFoundation::ProcessInfo::the().processor_count())
{
}
virtual ~ViewController() = default;
inline int cpu_count() const { return m_cpu_count; }
void view_did_load() override
{
state.cpu_load.resize(cpu_count());
state.cpu_old_user_time.resize(cpu_count());
state.cpu_old_system_time.resize(cpu_count());
state.cpu_old_idle_time.resize(cpu_count());
view().set_background_color(LG::Color::LightSystemBackground);
auto& header = view().add_subview<UI::View>(LG::Rect(0, 0, 0, 0));
header.set_background_color(LG::Color(222, 232, 227));
auto& label = header.add_subview<UI::Label>(LG::Rect(0, 0, 16, 22));
label.set_text_color(LG::Color(14, 72, 19));
label.set_text("Monitor");
label.set_font(LG::Font::system_bold_font(LG::Font::SystemTitleSize));
label.set_width(label.preferred_width());
auto& switch_tab = view().add_subview<UI::StackView>(LG::Rect(0, 0, 102, 18));
switch_tab.set_spacing(8);
switch_tab.layer().set_corner_mask(LG::CornerMask(4));
switch_tab.layer().set_shading(LG::Shading(LG::Shading::Box));
switch_tab.set_spacing(4);
auto& cpu_tab = switch_tab.add_arranged_subview<UI::Button>();
cpu_tab.set_content_edge_insets(UI::EdgeInsets(4, 10, 4, 10));
cpu_tab.set_background_color(LG::Color(248, 250, 231));
cpu_tab.set_title("CPU");
cpu_tab.set_title_color(LG::Color::DarkSystemText);
auto& mem_tab = switch_tab.add_arranged_subview<UI::Button>();
mem_tab.set_content_edge_insets(UI::EdgeInsets(4, 10, 4, 10));
mem_tab.set_background_color(LG::Color::White);
mem_tab.set_title("Memory");
mem_tab.set_title_color(LG::Color::DarkSystemText);
auto& cpu_label = view().add_subview<UI::Label>(LG::Rect(0, 0, 180, 16));
auto& cpu_graphs_stackview = view().add_subview<UI::StackView>(LG::Rect(0, 0, 184, 100));
cpu_graphs_stackview.set_distribution(UI::StackView::Distribution::FillEqually);
cpu_graphs_stackview.set_spacing(10);
view().add_constraint(UI::Constraint(header, UI::Constraint::Attribute::Left, UI::Constraint::Relation::Equal, 0));
view().add_constraint(UI::Constraint(header, UI::Constraint::Attribute::Top, UI::Constraint::Relation::Equal, 0));
view().add_constraint(UI::Constraint(header, UI::Constraint::Attribute::Right, UI::Constraint::Relation::Equal, 0));
view().add_constraint(UI::Constraint(header, UI::Constraint::Attribute::Height, UI::Constraint::Relation::Equal, 60));
view().add_constraint(UI::Constraint(label, UI::Constraint::Attribute::Left, UI::Constraint::Relation::Equal, UI::SafeArea::Left));
view().add_constraint(UI::Constraint(label, UI::Constraint::Attribute::CenterY, UI::Constraint::Relation::Equal, header, UI::Constraint::Attribute::CenterY, 1, 0));
view().add_constraint(UI::Constraint(switch_tab, UI::Constraint::Attribute::Left, UI::Constraint::Relation::Equal, UI::SafeArea::Left));
view().add_constraint(UI::Constraint(switch_tab, UI::Constraint::Attribute::CenterY, UI::Constraint::Relation::Equal, header, UI::Constraint::Attribute::Bottom, 1, 0));
view().add_constraint(UI::Constraint(cpu_label, UI::Constraint::Attribute::Left, UI::Constraint::Relation::Equal, UI::SafeArea::Left));
view().add_constraint(UI::Constraint(cpu_label, UI::Constraint::Attribute::Bottom, UI::Constraint::Relation::Equal, UI::SafeArea::Bottom));
view().add_constraint(UI::Constraint(cpu_graphs_stackview, UI::Constraint::Attribute::Left, UI::Constraint::Relation::Equal, UI::SafeArea::Left));
view().add_constraint(UI::Constraint(cpu_graphs_stackview, UI::Constraint::Attribute::Right, UI::Constraint::Relation::Equal, UI::SafeArea::Right));
view().add_constraint(UI::Constraint(cpu_graphs_stackview, UI::Constraint::Attribute::Top, UI::Constraint::Relation::Equal, switch_tab, UI::Constraint::Attribute::Bottom, 1, 8));
view().add_constraint(UI::Constraint(cpu_graphs_stackview, UI::Constraint::Attribute::Bottom, UI::Constraint::Relation::Equal, cpu_label, UI::Constraint::Attribute::Top, 1, -8));
for (int i = 0; i < cpu_count(); i++) {
auto& graph = cpu_graphs_stackview.add_arranged_subview<GraphView>(200);
cpu_graphs_stackview.add_constraint(UI::Constraint(graph, UI::Constraint::Attribute::Height, UI::Constraint::Relation::Equal, cpu_graphs_stackview, UI::Constraint::Attribute::Height, 1, 0));
cpu_graphs.push_back(&graph);
}
view().set_needs_layout();
UI::App::the().event_loop().add(LFoundation::Timer([&] {
update_data();
cpu_label.set_text(std::string("Load ") + std::to_string(state.cpu_load[0]) + "%");
cpu_label.set_needs_display();
},
1000, LFoundation::Timer::Repeat));
}
int update_cpu_load()
{
int fd_proc_stat = open("/proc/stat", O_RDONLY);
int offset = 0;
read(fd_proc_stat, buf, sizeof(buf));
for (int i = 0; i < cpu_count(); i++) {
int user_time, system_time, idle_time;
int num;
offset += sscanf(buf + offset, "cpu%d %d 0 %d %d\n", &num, &user_time, &system_time, &idle_time);
int diff_user_time = user_time - state.cpu_old_user_time[i];
int diff_system_time = system_time - state.cpu_old_system_time[i];
int diff_idle_time = idle_time - state.cpu_old_idle_time[i];
state.cpu_old_user_time[i] = user_time;
state.cpu_old_system_time[i] = system_time;
state.cpu_old_idle_time[i] = idle_time;
if (diff_user_time + diff_system_time + diff_idle_time == 0) {
state.cpu_load[i] = 0;
} else {
state.cpu_load[i] = (diff_user_time + diff_system_time) * 100 / (diff_user_time + diff_system_time + diff_idle_time);
}
cpu_graphs[i]->add_new_value(state.cpu_load[i]);
}
close(fd_proc_stat);
return 0;
}
void update_data()
{
update_cpu_load();
}
private:
int m_cpu_count;
int fd_proc_stat;
std::vector<GraphView*> cpu_graphs;
struct State {
std::vector<int> cpu_load;
std::vector<int> cpu_old_user_time;
std::vector<int> cpu_old_system_time;
std::vector<int> cpu_old_idle_time;
};
State state;
};

View File

@@ -0,0 +1,5 @@
APPS += CALCULATOR
ABOUT_NAME = calculator
ABOUT_LIBS = cxx ui
ABOUT_INSTALL_PATH = bin/

View File

@@ -0,0 +1,22 @@
#include "ViewController.h"
#include <libui/AppDelegate.h>
class AppDelegate : public UI::AppDelegate {
public:
AppDelegate() = default;
virtual ~AppDelegate() = default;
LG::Size preferred_desktop_window_size() const override { return LG::Size(240, 340); }
const char* icon_path() const override { return "/res/icons/apps/calculator.icon"; }
virtual bool application() override
{
auto& window = std::xos::construct<UI::Window>("Calculator", window_size(), icon_path());
auto& superview = window.create_superview<UI::View, ViewController>();
return true;
}
private:
};
SET_APP_DELEGATE(AppDelegate);

View File

@@ -0,0 +1,17 @@
import("//build/userland/TEMPLATE.gni")
xOS_application("calculator") {
display_name = "Calculator"
sources = [
"AppDelegate.cpp",
]
configs = [
"//build/userland:userland_flags",
]
deplibs = [
"libcxx",
"libfoundation",
"libg",
"libui"
]
}

View File

@@ -0,0 +1,179 @@
#pragma once
#include <libui/App.h>
#include <libui/Button.h>
#include <libui/Label.h>
#include <libui/StackView.h>
#include <libui/View.h>
#include <libui/ViewController.h>
#include <libui/Window.h>
#include <memory>
#include <sys/types.h>
#include <sys/utsname.h>
// TODO: Add enum for colors.
class ViewController : public UI::ViewController<UI::View> {
public:
ViewController(UI::View& view)
: UI::ViewController<UI::View>(view)
{
}
virtual ~ViewController() = default;
static constexpr const char* button_titles[20] = {
"c",
"+/-",
"%",
"/",
"7",
"8",
"9",
"*",
"4",
"5",
"6",
"-",
"1",
"2",
"3",
"+",
"0",
"00",
",",
"=",
};
constexpr size_t button_size() { return 48; }
constexpr size_t spacing_size() { return 8; }
bool is_operation(char a) { return a == '*' || a == '/' || a == '+' || a == '-'; }
void on_button_click(UI::Button* sender)
{
// Check if it is a number
if (sender->title().size() == 1 && '0' <= sender->title()[0] && sender->title()[0] <= '9') {
m_current_number.push_back(sender->title()[0]);
m_answer_label_ptr->set_text(m_current_number);
return;
}
// Check if it is an operation
if (sender->title().size() == 1 && is_operation(sender->title()[0])) {
var_number1 = std::atoi(m_current_number.c_str());
m_current_number.clear();
m_operation = sender->title()[0];
m_active_operation_button = sender;
sender->set_background_color(LG::Color(165, 201, 242));
return;
}
if (sender->title().size() == 1 && sender->title()[0] == '=') {
int var_number2 = std::atoi(m_current_number.c_str());
switch (m_operation) {
case '+':
var_number1 = var_number1 + var_number2;
m_current_number = std::to_string(var_number1);
m_answer_label_ptr->set_text(m_current_number);
m_active_operation_button->set_background_color(LG::Color(231, 240, 250));
break;
case '-':
var_number1 = var_number1 - var_number2;
m_current_number = std::to_string(var_number1);
m_answer_label_ptr->set_text(m_current_number);
m_active_operation_button->set_background_color(LG::Color(231, 240, 250));
break;
case '*':
var_number1 = var_number1 * var_number2;
m_current_number = std::to_string(var_number1);
m_answer_label_ptr->set_text(m_current_number);
m_active_operation_button->set_background_color(LG::Color(231, 240, 250));
break;
case '/':
m_active_operation_button->set_background_color(LG::Color(231, 240, 250));
if (var_number2 == 0) {
m_current_number = "";
m_answer_label_ptr->set_text("Error");
return;
}
var_number1 = var_number1 / var_number2;
m_current_number = std::to_string(var_number1);
m_answer_label_ptr->set_text(m_current_number);
break;
default:
m_current_number = "";
m_answer_label_ptr->set_text("Error");
}
return;
}
if (sender->title().size() == 1 && sender->title()[0] == 'c') {
m_operation = ' ';
m_active_operation_button = nullptr;
m_answer_label_ptr->set_text("0");
m_current_number.clear();
return;
}
}
void view_did_load() override
{
view().set_background_color(LG::Color::LightSystemBackground);
auto& main_stackview = view().add_subview<UI::StackView>(LG::Rect(0, 0, 0, button_size() * 5 + spacing_size() * 4));
main_stackview.set_background_color(LG::Color::LightSystemBackground);
main_stackview.set_axis(UI::LayoutConstraints::Axis::Vertical);
main_stackview.set_spacing(spacing_size());
view().add_constraint(UI::Constraint(main_stackview, UI::Constraint::Attribute::Left, UI::Constraint::Relation::Equal, UI::SafeArea::Left));
view().add_constraint(UI::Constraint(main_stackview, UI::Constraint::Attribute::Right, UI::Constraint::Relation::Equal, UI::SafeArea::Right));
view().add_constraint(UI::Constraint(main_stackview, UI::Constraint::Attribute::Bottom, UI::Constraint::Relation::Equal, UI::SafeArea::Bottom));
for (int i = 0; i < 5; i++) {
auto& stackview = main_stackview.add_arranged_subview<UI::StackView>();
stackview.set_background_color(LG::Color::LightSystemBackground);
stackview.set_distribution(UI::StackView::Distribution::EqualSpacing);
main_stackview.add_constraint(UI::Constraint(stackview, UI::Constraint::Attribute::Left, UI::Constraint::Relation::Equal, main_stackview, UI::Constraint::Attribute::Left, 1, 0));
main_stackview.add_constraint(UI::Constraint(stackview, UI::Constraint::Attribute::Right, UI::Constraint::Relation::Equal, main_stackview, UI::Constraint::Attribute::Right, 1, 0));
main_stackview.add_constraint(UI::Constraint(stackview, UI::Constraint::Attribute::Height, UI::Constraint::Relation::Equal, button_size()));
for (int j = 0; j < 4; j++) {
auto& view1 = stackview.add_arranged_subview<UI::Button>();
if (j == 3 && i == 4) {
view1.set_background_color(LG::Color(198, 166, 242));
view1.set_title_color(LG::Color::White);
} else if (j == 3 || i == 0) {
view1.set_background_color(LG::Color(231, 240, 250));
view1.set_title_color(LG::Color(35, 70, 106));
} else {
view1.set_background_color(LG::Color::LightSystemButton);
view1.set_title_color(LG::Color::DarkSystemText);
}
view1.set_font(LG::Font::system_bold_font(14));
view1.set_title(button_titles[i * 4 + j]);
view1.set_alignment(UI::Text::Alignment::Center);
view1.add_target([this](UI::View* sender) { this->on_button_click(static_cast<UI::Button*>(sender)); }, UI::Event::Type::MouseUpEvent);
view1.layer().set_corner_mask(LG::CornerMask(button_size() / 2));
stackview.add_constraint(UI::Constraint(view1, UI::Constraint::Attribute::Height, UI::Constraint::Relation::Equal, button_size()));
stackview.add_constraint(UI::Constraint(view1, UI::Constraint::Attribute::Width, UI::Constraint::Relation::Equal, button_size()));
}
}
auto& answer_label = view().add_subview<UI::Label>(LG::Rect(0, 0, 0, 40));
answer_label.set_text("0");
answer_label.set_text_color(LG::Color::DarkSystemText);
answer_label.set_alignment(UI::Text::Alignment::Right);
answer_label.set_font(LG::Font::system_font(36));
m_answer_label_ptr = &answer_label;
view().add_constraint(UI::Constraint(answer_label, UI::Constraint::Attribute::Left, UI::Constraint::Relation::Equal, UI::SafeArea::Left));
view().add_constraint(UI::Constraint(answer_label, UI::Constraint::Attribute::Right, UI::Constraint::Relation::Equal, UI::SafeArea::Right));
view().add_constraint(UI::Constraint(answer_label, UI::Constraint::Attribute::Bottom, UI::Constraint::Relation::Equal, main_stackview, UI::Constraint::Attribute::Top, 1, -8));
view().set_needs_layout();
}
private:
UI::Label* m_answer_label_ptr { nullptr };
std::string m_current_number;
int var_number1 { 0 };
char m_operation;
UI::Button* m_active_operation_button { nullptr };
};

View File

@@ -0,0 +1,5 @@
APPS += TERMINAL
TERMINAL_NAME = terminal
TERMINAL_LIBS = cxx ui
TERMINAL_INSTALL_PATH = bin/

View File

@@ -0,0 +1,62 @@
#include "TerminalView.h"
#include "TerminalViewController.h"
#include <csignal>
#include <libui/AppDelegate.h>
static int shell_pid = 0;
int setup_shell()
{
int ptmx = posix_openpt(O_RDONLY);
if (ptmx < 0) {
std::abort();
}
int f = fork();
if (f == 0) {
char* pname = ptsname(ptmx);
if (!pname) {
return -1;
}
close(0);
close(1);
close(2);
open(pname, O_RDONLY);
open(pname, O_WRONLY);
open(pname, O_WRONLY);
execlp("/bin/tinysh", "/bin/tinysh", NULL);
std::abort();
}
shell_pid = f;
return ptmx;
}
class AppDelegate : public UI::AppDelegate {
public:
AppDelegate() = default;
virtual ~AppDelegate() = default;
LG::Size preferred_desktop_window_size() const override { return LG::Size(400, 300); }
const char* icon_path() const override { return "/res/icons/apps/terminal.icon"; }
bool application() override
{
int ptmx = setup_shell();
auto style = StatusBarStyle(LG::Color(58, 58, 64)).set_light_text();
auto& window = std::xos::construct<UI::Window>("Terminal", window_size(), icon_path(), style);
auto& superview = window.create_superview<TerminalView, TerminalViewController>(ptmx);
window.set_focused_view(superview);
return true;
}
void application_will_terminate() override
{
std::kill(shell_pid, 9);
}
private:
};
SET_APP_DELEGATE(AppDelegate);

View File

@@ -0,0 +1,16 @@
import("//build/userland/TEMPLATE.gni")
xOS_application("terminal") {
display_name = "Terminal"
sources = [
"AppDelegate.cpp",
"TerminalView.cpp",
]
configs = [ "//build/userland:userland_flags" ]
deplibs = [
"libcxx",
"libfoundation",
"libg",
"libui",
]
}

View File

@@ -0,0 +1,229 @@
#include "TerminalView.h"
#include <algorithm>
#include <libfoundation/EventLoop.h>
#include <libfoundation/KeyboardMapping.h>
#include <libg/Color.h>
#include <libui/Context.h>
TerminalView::TerminalView(UI::View* superview, const LG::Rect& frame, int ptmx)
: UI::View(superview, frame)
, m_ptmx(ptmx)
{
recalc_dimensions(frame);
}
TerminalView::TerminalView(UI::View* superview, UI::Window* window, const LG::Rect& frame, int ptmx)
: UI::View(superview, window, frame)
, m_ptmx(ptmx)
{
recalc_dimensions(frame);
LFoundation::EventLoop::the().add(LFoundation::Timer([this] {
this->m_cursor_visible = !this->m_cursor_visible;
this->invalidate_cursor_glyph();
},
400, LFoundation::Timer::Repeat));
}
void TerminalView::recalc_dimensions(const LG::Rect& frame)
{
m_max_rows = (frame.height() - padding() - UI::SafeArea::Bottom) / glyph_height();
m_max_cols = (frame.width() - 2 * padding()) / glyph_width();
// FIXME: Add copy and resize on window resize.
char* new_data = (char*)malloc(m_max_rows * m_max_cols);
memset(new_data, 0, m_max_rows * m_max_cols);
if (m_display_data) {
free(m_display_data);
}
m_display_data = new_data;
}
void TerminalView::display(const LG::Rect& rect)
{
LG::Context ctx = UI::graphics_current_context();
ctx.add_clip(rect);
ctx.set_fill_color(background_color());
ctx.fill(bounds());
ctx.set_fill_color(cursor_color());
auto cursor_left_corner = pos_on_screen();
ctx.fill(LG::Rect(cursor_left_corner.x(), cursor_left_corner.y(), cursor_width(), glyph_height()));
auto& f = font();
ctx.set_fill_color(font_color());
LG::Point<int> text_start { padding(), padding() };
for (int i = 0; i < m_max_rows; i++) {
for (int j = 0; j < m_max_cols; j++) {
int idx = i * m_max_cols + j;
ctx.draw(text_start, f.glyph(m_display_data[idx]));
text_start.offset_by(glyph_width(), 0);
}
text_start.set_x(padding());
text_start.offset_by(0, glyph_height());
}
}
void TerminalView::scroll_line()
{
data_do_new_line();
set_needs_display();
}
void TerminalView::data_do_new_line()
{
char* data_plus_line = m_display_data + (m_max_cols);
char* data_end_minus_line = m_display_data + (m_max_rows - 1) * m_max_cols;
memmove(m_display_data, data_plus_line, (m_max_rows - 1) * m_max_cols);
memset(data_end_minus_line, 0, m_max_cols);
}
WindowStatus TerminalView::cursor_positions_do_new_line()
{
m_col = 0;
m_row++;
if (m_row == m_max_rows) {
m_row--;
return DoNewLine;
}
return Normal;
}
WindowStatus TerminalView::cursor_position_move_right()
{
m_col++;
if (m_col == m_max_cols) {
return cursor_positions_do_new_line();
}
return Normal;
}
WindowStatus TerminalView::cursor_position_move_left()
{
m_col--;
if (m_col == 0 && m_row > 0) {
m_row--;
}
return Normal;
}
void TerminalView::new_line()
{
will_move_cursor();
WindowStatus status = cursor_positions_do_new_line();
switch (status) {
case DoNewLine:
scroll_line();
break;
case Normal:
break;
}
did_move_cursor();
}
void TerminalView::increment_counter()
{
will_move_cursor();
m_col++;
if (m_col == m_max_cols) {
new_line();
}
did_move_cursor();
}
void TerminalView::decrement_counter()
{
will_move_cursor();
m_col--;
if (m_col == 0) {
m_row--;
}
did_move_cursor();
}
void TerminalView::put_char(char c)
{
auto pt = pos_on_screen();
set_needs_display(LG::Rect(pt.x(), pt.y(), glyph_width(), glyph_height()));
m_display_data[pos_in_data()] = c;
}
void TerminalView::push_back_char(char c)
{
if (c == '\n') {
new_line();
return;
}
put_char(c);
increment_counter();
}
void TerminalView::put_text(const std::string& data)
{
auto current_pos = pos_on_screen();
LG::Point<int> top_left_update_location { current_pos.x(), current_pos.y() };
LG::Point<int> bottom_right_update_location { current_pos.x(), current_pos.y() };
auto set_to_redraw_full_screen = [&]() {
data_do_new_line();
top_left_update_location = { bounds().min_x(), bounds().min_y() };
bottom_right_update_location = { bounds().max_x(), bounds().max_y() };
};
will_move_cursor();
int n = data.size();
for (int i = 0; i < n; i++) {
char c = data[i];
if (c == '\n') {
auto status = cursor_positions_do_new_line();
if (status == DoNewLine) {
set_to_redraw_full_screen();
}
} else {
auto pt = pos_on_screen();
data_set_char(c);
auto status = cursor_position_move_right();
if (status == DoNewLine) {
set_to_redraw_full_screen();
} else {
top_left_update_location.set_x(std::min(top_left_update_location.x(), pt.x()));
top_left_update_location.set_y(std::min(top_left_update_location.y(), pt.y()));
bottom_right_update_location.set_x(std::max(bottom_right_update_location.x(), pt.x() + glyph_width()));
bottom_right_update_location.set_y(std::max(bottom_right_update_location.y(), pt.y() + glyph_height()));
}
}
}
auto w = bottom_right_update_location.x() - top_left_update_location.x() + 1;
auto h = bottom_right_update_location.y() - top_left_update_location.y() + 1;
set_needs_display(LG::Rect(top_left_update_location.x(), top_left_update_location.y(), w, h));
did_move_cursor();
}
void TerminalView::send_input()
{
write(ptmx(), m_input.c_str(), m_input.size());
m_input.clear();
}
void TerminalView::receive_keyup_event(UI::KeyUpEvent&)
{
}
void TerminalView::receive_keydown_event(UI::KeyDownEvent& event)
{
// FIXME: More symbols and static size of font
if (event.key() == LFoundation::Keycode::KEY_BACKSPACE) {
if (m_input.size()) {
m_input.pop_back();
decrement_counter();
put_char(' ');
}
} else if (event.key() == LFoundation::Keycode::KEY_RETURN) {
m_input.push_back('\n');
push_back_char('\n');
send_input();
} else if (event.key() < 128) {
m_input.push_back(char(event.key()));
push_back_char(char(event.key()));
}
}

View File

@@ -0,0 +1,84 @@
#pragma once
#include <libg/Font.h>
#include <libui/View.h>
#include <string>
enum WindowStatus {
Normal,
DoNewLine,
};
class TerminalView : public UI::View {
UI_OBJECT();
public:
TerminalView(UI::View* superview, const LG::Rect&, int ptmx);
TerminalView(UI::View* superview, UI::Window* window, const LG::Rect&, int ptmx);
const LG::Color& font_color() const { return m_font_color; }
const LG::Color cursor_color() const { return m_cursor_visible ? LG::Color(80, 80, 80, 255) : background_color(); }
const LG::Color& background_color() const { return m_background_color; }
inline const LG::Font& font() const { return *m_font_ptr; }
inline int glyph_width() const { return font().glyph('.').width(); }
inline int glyph_height() const { return font().size() + 2; }
inline LG::Point<int> pos_on_screen() const { return { (int)m_col * glyph_width() + padding(), (int)m_row * glyph_height() + padding() }; }
inline int pos_in_data() const { return m_max_cols * m_row + m_col; }
void put_char(char c);
void put_text(const std::string& data);
void display(const LG::Rect& rect) override;
void receive_keyup_event(UI::KeyUpEvent&) override;
void receive_keydown_event(UI::KeyDownEvent&) override;
int ptmx() const { return m_ptmx; }
private:
void terminal_init();
WindowStatus cursor_positions_do_new_line();
WindowStatus cursor_position_move_right();
WindowStatus cursor_position_move_left();
void data_do_new_line();
inline void data_set_char(char c)
{
m_display_data[pos_in_data()] = c;
}
void scroll_line();
void new_line();
void increment_counter();
void decrement_counter();
void recalc_dimensions(const LG::Rect&);
void push_back_char(char c);
void send_input();
inline void invalidate_cursor_glyph()
{
auto pt = pos_on_screen();
set_needs_display(LG::Rect(pt.x(), pt.y(), cursor_width() + spacing(), glyph_height()));
}
inline void will_move_cursor() { invalidate_cursor_glyph(); }
inline void did_move_cursor() { invalidate_cursor_glyph(); }
constexpr int padding() const { return 2; }
constexpr int spacing() const { return 2; }
constexpr int cursor_width() const { return 5; }
int m_ptmx { -1 };
std::string m_input {};
bool m_cursor_visible { true };
LG::Color m_background_color { LG::Color(47, 47, 53) };
LG::Color m_font_color { LG::Color::LightSystemText };
LG::Font* m_font_ptr { LG::Font::load_from_file("/res/fonts/Liza.font/10/regular.font") };
size_t m_max_cols { 0 };
size_t m_max_rows { 0 };
size_t m_col { 0 };
size_t m_row { 0 };
char* m_display_data { nullptr };
};

View File

@@ -0,0 +1,34 @@
#pragma once
#include "TerminalView.h"
#include <libui/ViewController.h>
#include <memory>
#include <sys/types.h>
#include <unistd.h>
class TerminalViewController : public UI::ViewController<TerminalView> {
public:
TerminalViewController(TerminalView& view)
: UI::ViewController<TerminalView>(view)
{
}
virtual ~TerminalViewController() = default;
void init_listners()
{
LFoundation::EventLoop::the().add(
view().ptmx(), [this] {
char text[256];
int cnt = read(view().ptmx(), text, 255);
text[cnt] = '\0';
view().put_text(std::string(text, cnt));
},
nullptr);
}
void view_did_load() override
{
init_listners();
}
private:
};

View 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",
]
}

View 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 };
};
}

View 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;
}
}

View 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;
};
}

View 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();
}

View 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/" ]
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -0,0 +1,11 @@
#pragma once
namespace WinServer {
enum class ViolationClass {
Ignorable,
Moderate,
Serious,
};
} // namespace WinServer

View File

@@ -0,0 +1,12 @@
#pragma once
#include <sys/types.h>
namespace WinServer {
enum Color {
Shadow = 0xf4000000,
InactiveText = 0x7B7E78,
};
} // namespace WinServer

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@@ -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

View 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

View 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:
};
};

View File

@@ -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

View File

@@ -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

View 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

View 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

View 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 {};
};
}

View 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

View 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

View File

@@ -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

View File

@@ -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

View 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

View 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

View 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

View 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();
}

View File

@@ -0,0 +1,5 @@
APPS += XSH
XSH_NAME = xsh
XSH_LIBS = c
XSH_INSTALL_PATH = bin/

View File

@@ -0,0 +1,8 @@
import("//build/userland/TEMPLATE.gni")
xOS_executable("xsh") {
install_path = "bin/"
sources = [ "main.c" ]
configs = [ "//build/userland:userland_flags" ]
deplibs = [ "libc" ]
}

190
userland/shell/xsh/main.c Normal file
View File

@@ -0,0 +1,190 @@
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <unistd.h>
#define true (1)
#define false (0)
char* _cmd_app;
char* _cmd_buffer;
char** _cmd_parsed_buffer;
static int _cmd_buffer_position = 0;
static int _cmd_parsed_buffer_position = 0;
static int running_job = 0;
uint32_t _is_cmd_internal();
void _cmd_buffer_clear();
void _cmd_loop();
void _cmd_loop_start();
void _cmd_loop_end();
void _cmd_input();
void _cmd_processor();
char _cmd_is_ascii(uint32_t key);
char _cmd_cmp_command(const char*);
int16_t _cmd_find_cmd_handler();
uint32_t _cmd_getkeycode();
enum internal_cmd_code {
CMD_NONE = 0,
CMD_CD,
CMD_SET,
CMD_UNSET,
CMD_ENV,
};
uint32_t _is_cmd_internal()
{
if (memcmp(_cmd_buffer, "cd", 2) == 0) {
return CMD_CD;
}
if (memcmp(_cmd_buffer, "set", 3) == 0) {
return CMD_SET;
}
if (memcmp(_cmd_buffer, "env", 3) == 0) {
return CMD_ENV;
}
if (memcmp(_cmd_buffer, "unset", 5) == 0) {
return CMD_UNSET;
}
return CMD_NONE;
}
int _cmd_do_internal(uint32_t code)
{
if (code == CMD_CD) {
return chdir(_cmd_parsed_buffer[1]);
}
if (code == CMD_SET) {
char* eqpos = strchr(_cmd_parsed_buffer[1], '=');
if (!eqpos) {
return 0;
}
*eqpos = '\0';
return setenv(_cmd_parsed_buffer[1], (char*)(eqpos + 1), 1);
}
if (code == CMD_UNSET) {
return unsetenv(_cmd_parsed_buffer[1]);
}
if (code == CMD_ENV) {
for (int i = 0; environ[i]; i++) {
const char* envv = environ[i];
char* eqpos = strchr(envv, '=');
if (!eqpos) {
continue;
}
size_t len = strlen(envv);
write(STDOUT, envv, len);
write(STDOUT, "\n", 1);
}
}
return 0;
}
void _cmd_buffer_clear()
{
_cmd_buffer_position = 0;
}
void _cmd_loop()
{
for (;;) {
_cmd_loop_start();
_cmd_input();
_cmd_processor();
_cmd_loop_end();
}
}
void _cmd_input()
{
_cmd_buffer_position = read(STDIN, _cmd_buffer, 256);
while (_cmd_buffer_position < 0)
_cmd_buffer_position = read(STDIN, _cmd_buffer, 256);
}
void _cmd_processor()
{
_cmd_parsed_buffer_position = 0;
char is_prev_space = true;
for (int i = 0; i < _cmd_buffer_position; i++) {
if (_cmd_buffer[i] == ' ') {
if (is_prev_space == false) {
/* null terminator when args are sent */
_cmd_buffer[i] = '\0';
}
is_prev_space = true;
} else {
if (is_prev_space) {
_cmd_parsed_buffer[_cmd_parsed_buffer_position++] = (char*)((char*)_cmd_buffer + i);
}
is_prev_space = false;
}
}
/* Remove \n */
_cmd_buffer[_cmd_buffer_position - 1] = '\0';
_cmd_parsed_buffer[_cmd_parsed_buffer_position] = 0;
/* We try to launch an app */
uint32_t cmd = _is_cmd_internal();
if (cmd == CMD_NONE) {
uint32_t namelen = strlen(_cmd_parsed_buffer[0]);
memcpy(_cmd_app + 5, _cmd_buffer, namelen + 1);
int res = fork();
if (!res) {
// We don't pass an app name to args.
execve(_cmd_app, &_cmd_parsed_buffer[0], environ);
exit(-1);
} else {
running_job = res;
wait(res);
}
} else {
_cmd_do_internal(cmd);
}
}
void _cmd_loop_start()
{
write(1, "> ", 2);
}
void _cmd_loop_end()
{
_cmd_buffer_clear();
write(1, "\n", 1);
}
char _cmd_is_ascii(uint32_t key)
{
return 32 <= key && key <= 126;
}
void inter(int no)
{
kill(running_job, 9);
return;
}
int main()
{
sigaction(3, inter);
ioctl(0, TIOCSPGRP, getpgid(getpid()));
ioctl(0, TIOCGPGRP, 0);
_cmd_app = malloc(256);
_cmd_buffer = malloc(256);
_cmd_parsed_buffer = malloc(256 * sizeof(char*));
memcpy(_cmd_app, "/bin/", 5);
_cmd_loop();
return 0;
}

View File

@@ -0,0 +1,24 @@
#include "AppListView.h"
#include "AppListViewController.h"
#include "AppListWindow.h"
#include <libui/AppDelegate.h>
class AppDelegate : public UI::AppDelegate {
public:
AppDelegate() = default;
virtual ~AppDelegate() = default;
LG::Size preferred_desktop_window_size() const override { return LG::Size(320, 400); }
bool application() override
{
auto& window = std::xos::construct<AppListWindow>(window_size());
window.set_bitmap_format(LG::PixelBitmapFormat::RGBA);
auto& dock_view = window.create_superview<AppListView, AppListViewController>();
return true;
}
private:
};
SET_APP_DELEGATE(AppDelegate);

View File

@@ -0,0 +1,27 @@
#pragma once
#include <libg/PixelBitmap.h>
#include <list>
#include <string>
class AppEntity {
public:
AppEntity() = default;
void set_icon(LG::PixelBitmap&& icon) { m_icon = std::move(icon); }
const LG::PixelBitmap& icon() const { return m_icon; }
void set_title(const std::string& title) { m_title = title; }
const std::string& title() const { return m_title; }
void set_path_to_exec(const std::string& path) { m_path_to_exec = path; }
const std::string& path_to_exec() const { return m_path_to_exec; }
void set_bundle_id(const std::string& bid) { m_bundle_id = bid; }
const std::string& bundle_id() const { return m_bundle_id; }
private:
LG::PixelBitmap m_icon;
std::string m_title {};
std::string m_path_to_exec {};
std::string m_bundle_id {};
};

View File

@@ -0,0 +1,99 @@
#include "AppListView.h"
#include "IconView.h"
#include <algorithm>
#include <cstdlib>
#include <libfoundation/EventLoop.h>
#include <libfoundation/KeyboardMapping.h>
#include <libg/Color.h>
#include <libg/ImageLoaders/PNGLoader.h>
#include <libui/App.h>
#include <libui/CollectionView.h>
#include <libui/Context.h>
#include <libui/StackView.h>
#include <libui/TextField.h>
#include <unistd.h>
static AppListView* this_view;
AppListView::AppListView(UI::View* superview, const LG::Rect& frame)
: UI::View(superview, frame)
{
// Not used any more, reinit
// auto& dock_stack_view = add_subview<UI::StackView>(bounds());
// dock_stack_view.set_spacing(padding());
// dock_stack_view.set_background_color(LG::Color::Opaque);
// dock_stack_view.set_axis(UI::LayoutConstraints::Axis::Horizontal);
// dock_stack_view.set_distribution(UI::StackView::Distribution::EqualCentering);
// m_dock_stackview = &dock_stack_view;
}
AppListView::AppListView(UI::View* superview, UI::Window* window, const LG::Rect& frame)
: UI::View(superview, window, frame)
{
// Use manual layouting since layout is really simple here.
auto& header = add_subview<UI::View>(LG::Rect(0, 0, bounds().width(), 30 + 2 * padding()));
header.set_background_color(LG::Color::White);
header.layer().set_corner_mask(LG::CornerMask(8, LG::CornerMask::Masked, LG::CornerMask::NonMasked));
auto& searchbar = add_subview<UI::TextField>(LG::Rect(padding(), padding(), bounds().width() - padding() * 2, 30));
searchbar.layer().set_corner_mask(LG::CornerMask(8));
searchbar.set_background_color(LG::Color(0xE7F0FA));
searchbar.set_placeholder_text("SEARCH");
auto& applist_grid_view = add_subview<UI::CollectionView>(LG::Rect(0, 2 * padding() + 30, bounds().width(), bounds().height() - (2 * padding() + 30)));
applist_grid_view.set_data_source([this](int id) -> View* {
return this->view_streamer(id);
});
m_applist_grid_view = &applist_grid_view;
}
void AppListView::display(const LG::Rect& rect)
{
LG::Context ctx = UI::graphics_current_context();
ctx.add_clip(rect);
ctx.set_fill_color(background_color());
ctx.fill_rounded(bounds(), layer().corner_mask());
ctx.set_fill_color(LG::Color(120, 120, 120, 50));
ctx.draw_shading(LG::Rect(0, 30 + 2 * padding(), bounds().width(), 4), LG::Shading(LG::Shading::Type::TopToBottom));
}
UI::View* AppListView::view_streamer(int id)
{
if (id * items_per_row() >= m_app_entities.size()) {
return nullptr;
}
size_t calc_padding = (m_applist_grid_view->bounds().width() - 2 * padding() - (icon_view_size() * items_per_row())) / (items_per_row() - 1);
UI::StackView& dock_stack_view = m_applist_grid_view->add_subview<UI::StackView>(LG::Rect(padding(), 0, m_applist_grid_view->bounds().width() - 2 * padding(), icon_view_size() + calc_padding));
dock_stack_view.set_spacing(calc_padding);
dock_stack_view.set_background_color(LG::Color::Opaque);
dock_stack_view.set_axis(UI::LayoutConstraints::Axis::Horizontal);
dock_stack_view.set_distribution(UI::StackView::Distribution::Standard);
size_t rem = std::min(items_per_row(), (int)m_app_entities.size() - id * items_per_row());
for (int i = 0; i < rem; i++) {
auto& icon_view = dock_stack_view.add_arranged_subview<IconView>();
icon_view.set_title(m_app_entities[id * items_per_row() + i].title());
icon_view.entity() = m_app_entities[id * items_per_row() + i];
icon_view.add_constraint(UI::Constraint(icon_view, UI::Constraint::Attribute::Height, UI::Constraint::Relation::Equal, icon_view_size()));
icon_view.add_constraint(UI::Constraint(icon_view, UI::Constraint::Attribute::Width, UI::Constraint::Relation::Equal, icon_view_size()));
}
return &dock_stack_view;
}
void AppListView::register_entity(const AppEntity& ent)
{
m_app_entities.push_back(ent);
m_applist_grid_view->reload_data();
m_applist_grid_view->invalidate_row((m_app_entities.size() - 1) / items_per_row());
}
void AppListView::register_entity(AppEntity&& ent)
{
m_app_entities.push_back(std::move(ent));
m_applist_grid_view->reload_data();
m_applist_grid_view->invalidate_row((m_app_entities.size() - 1) / items_per_row());
}

View File

@@ -0,0 +1,32 @@
#pragma once
#include "AppEntity.h"
#include "IconView.h"
#include <libg/Font.h>
#include <libui/CollectionView.h>
#include <libui/View.h>
#include <list>
#include <string>
class AppListView : public UI::View {
UI_OBJECT();
public:
AppListView(UI::View* superview, const LG::Rect& frame);
AppListView(UI::View* superview, UI::Window* window, const LG::Rect& frame);
static constexpr size_t padding() { return 16; }
static constexpr int icon_size() { return 32; }
static constexpr int icon_view_size() { return (int)64; }
static constexpr int items_per_row() { return (int)4; }
void display(const LG::Rect& rect) override;
void register_entity(const AppEntity& ent);
void register_entity(AppEntity&& ent);
private:
UI::View* view_streamer(int id);
UI::CollectionView* m_applist_grid_view {};
std::vector<AppEntity> m_app_entities {};
};

View File

@@ -0,0 +1,82 @@
#pragma once
#include "AppListView.h"
#include <libfoundation/FileManager.h>
#include <libfoundation/json/Parser.h>
#include <libg/ImageLoaders/PNGLoader.h>
#include <libui/App.h>
#include <libui/Button.h>
#include <libui/Label.h>
#include <libui/View.h>
#include <libui/ViewController.h>
#include <libui/Window.h>
#include <memory>
#include <sys/types.h>
char contentdir[256];
class AppListViewController : public UI::ViewController<AppListView> {
public:
AppListViewController(AppListView& view)
: UI::ViewController<AppListView>(view)
{
}
virtual ~AppListViewController() = default;
void load_application(const std::string& content_dir)
{
auto json_parser = LFoundation::Json::Parser(content_dir + "info.json");
LFoundation::Json::Object* jobj_root = json_parser.object();
if (jobj_root->invalid()) {
return;
}
auto* jdict_root = jobj_root->cast_to<LFoundation::Json::DictObject>();
const std::string& bundle_id = jdict_root->data()["bundle_id"]->cast_to<LFoundation::Json::StringObject>()->data();
AppEntity new_ent;
LG::PNG::PNGLoader loader;
std::string icon_path = jdict_root->data()["icon_path"]->cast_to<LFoundation::Json::StringObject>()->data();
new_ent.set_icon(loader.load_from_file(icon_path + "/32x32.png"));
std::string rel_exec_path = jdict_root->data()["exec_rel_path"]->cast_to<LFoundation::Json::StringObject>()->data();
new_ent.set_path_to_exec(content_dir + rel_exec_path);
new_ent.set_title(jdict_root->data()["name"]->cast_to<LFoundation::Json::StringObject>()->data());
new_ent.set_bundle_id(jdict_root->data()["bundle_id"]->cast_to<LFoundation::Json::StringObject>()->data());
view().register_entity(std::move(new_ent));
delete jdict_root;
}
void load_application_list()
{
auto local_fm = LFoundation::FileManager();
local_fm.foreach_object("/Applications", [this](const char* name) {
sprintf(contentdir, "/Applications/%s/Content/", name);
load_application(contentdir);
});
}
virtual void view_did_load() override
{
view().set_background_color(LG::Color::LightSystemOpaque);
view().layer().set_corner_mask(LG::CornerMask(4, LG::CornerMask::Masked, LG::CornerMask::NonMasked));
load_application_list();
#if 0
for (int i = 0; i < 32; i++) {
AppEntity new_ent;
LG::PNG::PNGLoader loader;
new_ent.set_icon(loader.load_from_file("/res/icons/apps/about.icon/32x32.png"));
new_ent.set_path_to_exec("/Applications/about.app/Content/about");
new_ent.set_title("TestApp");
new_ent.set_bundle_id("com.x.test");
view().register_entity(std::move(new_ent));
}
#endif
view().set_needs_display();
}
private:
};

View File

@@ -0,0 +1,7 @@
#include "AppListWindow.h"
#include "AppListView.h"
void AppListWindow::receive_event(std::unique_ptr<LFoundation::Event> event)
{
Window::receive_event(std::move(event));
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include <libg/Size.h>
#include <libui/Screen.h>
#include <libui/Window.h>
class AppListWindow : public UI::Window {
public:
AppListWindow(const LG::Size& size)
: UI::Window("AppList", size, UI::WindowType::AppList)
{
}
void receive_event(std::unique_ptr<LFoundation::Event> event) override;
};

View File

@@ -0,0 +1,18 @@
import("//build/userland/TEMPLATE.gni")
xOS_executable("applist") {
install_path = "System/"
sources = [
"AppDelegate.cpp",
"AppListView.cpp",
"AppListWindow.cpp",
"IconView.cpp",
]
configs = [ "//build/userland:userland_flags" ]
deplibs = [
"libcxx",
"libfoundation",
"libg",
"libui",
]
}

View File

@@ -0,0 +1,38 @@
#include "IconView.h"
#include "AppListView.h"
#include <libui/App.h>
#include <libui/Context.h>
#include <libui/Label.h>
#include <libui/Screen.h>
#include <libui/Window.h>
IconView::IconView(View* superview, const LG::Rect& frame)
: View(superview, frame)
{
m_label = &add_subview<UI::Label>(LG::Rect(0, AppListView::icon_view_size() - 12, AppListView::icon_view_size(), 12));
m_label->set_alignment(UI::Text::Alignment::Center);
}
void IconView::display(const LG::Rect& rect)
{
const int padding = 4;
const int offset_x = (AppListView::icon_view_size() - AppListView::icon_size()) / 2;
const int offset_y = (AppListView::icon_view_size() - AppListView::icon_size()) / 2;
LG::Context ctx = UI::graphics_current_context();
ctx.add_clip(rect);
auto icon_rect = LG::Rect(offset_x, offset_y, AppListView::icon_size(), AppListView::icon_size());
ctx.draw(icon_rect.origin(), m_launch_entity.icon());
if (is_hovered()) {
ctx.set_fill_color(LG::Color::LightSystemOpaque128);
ctx.fill(icon_rect);
}
ctx.set_fill_color(LG::Color(120, 129, 133, 40));
ctx.draw_box_shading(icon_rect, LG::Shading(LG::Shading::Type::Box, 0, 3), LG::CornerMask(6));
}
void IconView::on_click()
{
launch();
}

View File

@@ -0,0 +1,60 @@
#pragma once
#include "AppEntity.h"
#include <libg/Font.h>
#include <libui/Label.h>
#include <libui/PopupMenu.h>
#include <libui/View.h>
#include <list>
#include <string>
#include <unistd.h>
class IconView : public UI::View {
UI_OBJECT();
public:
IconView(View* superview, const LG::Rect& frame);
void display(const LG::Rect& rect) override;
void mouse_up() override { on_click(); }
static constexpr size_t underline_height() { return 4; }
void set_title(const std::string& title)
{
if (!m_label) {
return;
}
m_label->set_text(title);
}
AppEntity& entity() { return m_launch_entity; }
const AppEntity& entity() const { return m_launch_entity; }
virtual void mouse_entered(const LG::Point<int>& location) override
{
View::mouse_entered(location);
set_needs_display();
}
virtual void mouse_exited() override
{
View::mouse_exited();
set_needs_display();
}
private:
void on_click();
void launch()
{
if (fork() == 0) {
for (int i = 3; i < 32; i++) {
close(i);
}
execlp(m_launch_entity.path_to_exec().c_str(), m_launch_entity.path_to_exec().c_str(), NULL);
std::abort();
}
}
UI::Label* m_label;
AppEntity m_launch_entity;
};

View File

@@ -0,0 +1,5 @@
APPS += DOCK
DOCK_NAME = dock
DOCK_LIBS = cxx ui
DOCK_INSTALL_PATH = bin/

View File

@@ -0,0 +1,24 @@
#include "DockView.h"
#include "DockViewController.h"
#include "DockWindow.h"
#include <libui/AppDelegate.h>
class AppDelegate : public UI::AppDelegate {
public:
AppDelegate() = default;
virtual ~AppDelegate() = default;
LG::Size preferred_desktop_window_size() const override { return LG::Size(400, 300); }
bool application() override
{
auto& window = std::xos::construct<DockWindow>();
window.set_bitmap_format(LG::PixelBitmapFormat::RGBA);
auto& dock_view = window.create_superview<DockView, DockViewController>();
return true;
}
private:
};
SET_APP_DELEGATE(AppDelegate);

View File

@@ -0,0 +1,44 @@
#include "AppListView.h"
#include "DockView.h"
#include <libg/ImageLoaders/PNGLoader.h>
#include <libui/App.h>
#include <libui/Context.h>
#include <libui/Label.h>
#include <libui/Screen.h>
#include <libui/Window.h>
AppListView::AppListView(View* superview, const LG::Rect& frame)
: View(superview, frame)
{
LG::PNG::PNGLoader loader;
m_icon = loader.load_from_file("/res/system/app_list_32.png");
}
void AppListView::display(const LG::Rect& rect)
{
const int padding = 4;
const int offset_x = (DockView::icon_view_size() - m_icon.width()) / 2;
const int offset_y = (DockView::icon_view_size() - m_icon.height()) / 2;
LG::Context ctx = UI::graphics_current_context();
ctx.add_clip(rect);
auto icon_rect = LG::Rect(offset_x, offset_y, DockView::icon_size(), DockView::icon_size());
ctx.draw(icon_rect.origin(), m_icon);
if (is_hovered()) {
ctx.set_fill_color(LG::Color::LightSystemOpaque128);
ctx.fill(icon_rect);
}
}
void AppListView::on_click()
{
int this_window_id = window()->id();
if (m_target_window_id == INVALID) {
return;
}
auto& app = UI::App::the();
AskBringToFrontMessage msg(app.connection().key(), this_window_id, m_target_window_id);
app.connection().send_async_message(msg);
}

View File

@@ -0,0 +1,41 @@
#pragma once
#include "DockEntity.h"
#include "WindowEntity.h"
#include <libg/Font.h>
#include <libui/Label.h>
#include <libui/PopupMenu.h>
#include <libui/View.h>
#include <list>
#include <string>
#include <unistd.h>
class AppListView : public UI::View {
UI_OBJECT();
static constexpr int INVALID = -1;
public:
AppListView(View* superview, const LG::Rect& frame);
void set_target_window_id(int winid) { m_target_window_id = winid; }
void display(const LG::Rect& rect) override;
void mouse_up() override { on_click(); }
virtual void mouse_entered(const LG::Point<int>& location) override
{
View::mouse_entered(location);
set_needs_display();
}
virtual void mouse_exited() override
{
View::mouse_exited();
set_needs_display();
}
private:
void on_click();
int m_target_window_id { INVALID };
LG::PixelBitmap m_icon;
};

View File

@@ -0,0 +1,19 @@
import("//build/userland/TEMPLATE.gni")
xOS_executable("dock") {
install_path = "System/"
sources = [
"AppDelegate.cpp",
"AppListView.cpp",
"DockView.cpp",
"DockWindow.cpp",
"IconView.cpp",
]
configs = [ "//build/userland:userland_flags" ]
deplibs = [
"libcxx",
"libfoundation",
"libg",
"libui",
]
}

View File

@@ -0,0 +1,29 @@
#pragma once
#include "WindowEntity.h"
#include <libg/PixelBitmap.h>
#include <list>
#include <string>
class DockEntity {
public:
DockEntity() = default;
void set_icon(LG::PixelBitmap&& icon) { m_icon = std::move(icon); }
const LG::PixelBitmap& icon() const { return m_icon; }
void set_path_to_exec(const std::string& path) { m_path_to_exec = path; }
const std::string& path_to_exec() const { return m_path_to_exec; }
void set_bundle_id(const std::string& bid) { m_bundle_id = bid; }
const std::string& bundle_id() const { return m_bundle_id; }
void add_window(const WindowEntity& went) { m_windows.push_front(went); }
const std::list<WindowEntity>& windows() const { return m_windows; }
std::list<WindowEntity>& windows() { return m_windows; }
private:
LG::PixelBitmap m_icon;
std::string m_path_to_exec {};
std::string m_bundle_id {};
std::list<WindowEntity> m_windows {};
};

View File

@@ -0,0 +1,162 @@
#include "DockView.h"
#include "AppListView.h"
#include "IconView.h"
#include <algorithm>
#include <cstdlib>
#include <libfoundation/EventLoop.h>
#include <libfoundation/KeyboardMapping.h>
#include <libg/Color.h>
#include <libg/ImageLoaders/PNGLoader.h>
#include <libui/App.h>
#include <libui/Context.h>
#include <unistd.h>
static DockView* this_view;
DockView::DockView(UI::View* superview, const LG::Rect& frame)
: UI::View(superview, frame)
{
init_fill_bounds();
init_subviews();
}
DockView::DockView(UI::View* superview, UI::Window* window, const LG::Rect& frame)
: UI::View(superview, window, frame)
{
init_fill_bounds();
init_subviews();
}
void DockView::init_fill_bounds()
{
m_fill_bounds = bounds();
m_fill_bounds.set_x(bounds().mid_x());
m_fill_bounds.set_width(0);
fill_bounds_expand(2 * padding());
}
void DockView::init_subviews()
{
auto& dock_stack_view = add_subview<UI::StackView>(bounds());
dock_stack_view.set_spacing(padding());
dock_stack_view.set_background_color(LG::Color::Opaque);
dock_stack_view.set_axis(UI::LayoutConstraints::Axis::Horizontal);
dock_stack_view.set_distribution(UI::StackView::Distribution::EqualCentering);
m_dock_stackview = &dock_stack_view;
add_system_buttons();
}
void DockView::add_system_buttons()
{
fill_bounds_expand(icon_view_size() + padding());
auto& icon_view = m_dock_stackview->add_arranged_subview<AppListView>();
icon_view.add_constraint(UI::Constraint(icon_view, UI::Constraint::Attribute::Height, UI::Constraint::Relation::Equal, icon_view_size()));
icon_view.add_constraint(UI::Constraint(icon_view, UI::Constraint::Attribute::Width, UI::Constraint::Relation::Equal, icon_view_size()));
m_applist_view = &icon_view;
}
void DockView::display(const LG::Rect& rect)
{
LG::Context ctx = UI::graphics_current_context();
ctx.add_clip(rect);
ctx.set_fill_color(background_color());
ctx.fill_rounded(fill_bounds(), LG::CornerMask(4, LG::CornerMask::Masked, LG::CornerMask::NonMasked));
}
void DockView::new_dock_entity(const std::string& exec_path, const std::string& icon_path, const std::string& bundle_id)
{
LG::PNG::PNGLoader loader;
fill_bounds_expand(icon_view_size() + padding());
auto& icon_view = m_dock_stackview->add_arranged_subview<IconView>();
icon_view.add_constraint(UI::Constraint(icon_view, UI::Constraint::Attribute::Height, UI::Constraint::Relation::Equal, icon_view_size()));
icon_view.add_constraint(UI::Constraint(icon_view, UI::Constraint::Attribute::Width, UI::Constraint::Relation::Equal, icon_view_size()));
icon_view.entity().set_icon(loader.load_from_file(icon_path + "/32x32.png"));
icon_view.entity().set_path_to_exec(std::move(exec_path));
icon_view.entity().set_bundle_id(std::move(bundle_id));
m_icon_views.push_back(&icon_view);
set_needs_layout();
}
WindowEntity* DockView::find_window_entry(int window_id)
{
for (auto* view : m_icon_views) {
for (auto& wins : view->entity().windows()) {
if (wins.window_id() == window_id) {
return &wins;
}
}
}
return nullptr;
}
void DockView::on_window_create(const std::string& bundle_id, const std::string& icon_path, int window_id, int window_type)
{
// Don't add an icon of dock (self).
if (window()->id() == window_id) {
return;
}
if (window_type == WindowType::AppList) {
if (m_applist_view) {
m_applist_view->set_target_window_id(window_id);
}
return;
}
for (auto* view : m_icon_views) {
if (view->entity().bundle_id() == bundle_id) {
view->entity().add_window(WindowEntity(window_id));
set_needs_display();
return;
}
}
new_dock_entity("", icon_path, bundle_id);
for (auto* view : m_icon_views) {
if (view->entity().bundle_id() == bundle_id) {
view->entity().add_window(WindowEntity(window_id));
set_needs_display();
return;
}
}
}
void DockView::on_window_minimize(int window_id)
{
auto* ent = find_window_entry(window_id);
if (!ent) {
return;
}
ent->set_minimized(true);
set_needs_display();
}
void DockView::on_window_remove(int window_id)
{
// TODO: Currently icons are not properly deleted.
for (auto* view : m_icon_views) {
for (auto& wins : view->entity().windows()) {
if (wins.window_id() == window_id) {
auto& win = view->entity().windows();
win.erase(std::find(win.begin(), win.end(), WindowEntity(window_id)));
set_needs_display();
return;
}
}
}
}
void DockView::set_icon(int window_id, const std::string& path)
{
}
void DockView::set_title(int window_id, const std::string& title)
{
auto* ent = find_window_entry(window_id);
if (!ent) {
return;
}
ent->set_title(title);
}

View File

@@ -0,0 +1,49 @@
#pragma once
#include "AppListView.h"
#include "DockEntity.h"
#include "IconView.h"
#include "WindowEntity.h"
#include <libg/Font.h>
#include <libui/StackView.h>
#include <libui/View.h>
#include <list>
#include <string>
class DockView : public UI::View {
UI_OBJECT();
public:
DockView(UI::View* superview, const LG::Rect& frame);
DockView(UI::View* superview, UI::Window* window, const LG::Rect& frame);
static constexpr size_t padding() { return 4; }
static constexpr size_t dock_view_height() { return 50; }
static constexpr int icon_size() { return 32; }
static constexpr int icon_view_size() { return (int)dock_view_height(); }
void display(const LG::Rect& rect) override;
WindowEntity* find_window_entry(int window_id);
void on_window_create(const std::string& bundle_id, const std::string& icon_path, int window_id, int window_type);
void on_window_remove(int window_id);
void on_window_minimize(int window_id);
void set_icon(int window_id, const std::string& path);
void set_title(int window_id, const std::string& title);
void add_system_buttons();
void new_dock_entity(const std::string& exec_path, const std::string& icon_path, const std::string& bundle_id);
private:
void init_subviews();
void init_fill_bounds();
void fill_bounds_expand(size_t l) { m_fill_bounds.set_width(m_fill_bounds.width() + l), m_fill_bounds.set_x(m_fill_bounds.min_x() - l / 2); }
const LG::Rect& fill_bounds() const { return m_fill_bounds; }
void launch(const DockEntity& ent);
LG::Rect m_fill_bounds {};
UI::StackView* m_dock_stackview {};
AppListView* m_applist_view {};
std::list<IconView*> m_icon_views {};
};

View File

@@ -0,0 +1,31 @@
#pragma once
#include "DockView.h"
#include <libui/App.h>
#include <libui/Button.h>
#include <libui/Label.h>
#include <libui/View.h>
#include <libui/ViewController.h>
#include <libui/Window.h>
#include <memory>
#include <sys/types.h>
class DockViewController : public UI::ViewController<DockView> {
public:
DockViewController(DockView& view)
: UI::ViewController<DockView>(view)
{
}
virtual ~DockViewController() = default;
virtual void view_did_load() override
{
view().set_background_color(LG::Color::LightSystemOpaque);
view().new_dock_entity("/Applications/about.app/Content/about", "/res/icons/apps/about.icon", "com.x.about");
view().new_dock_entity("/Applications/terminal.app/Content/terminal", "/res/icons/apps/terminal.icon", "com.x.terminal");
view().new_dock_entity("/Applications/activity_monitor.app/Content/activity_monitor", "/res/icons/apps/activity_monitor.icon", "com.x.activity_monitor");
view().new_dock_entity("/Applications/calculator.app/Content/calculator", "/res/icons/apps/calculator.icon", "com.x.calculator");
view().set_needs_display();
}
private:
};

View File

@@ -0,0 +1,42 @@
#include "DockWindow.h"
#include "DockView.h"
void DockWindow::receive_event(std::unique_ptr<LFoundation::Event> event)
{
switch (event->type()) {
case UI::Event::Type::NotifyWindowCreateEvent: {
UI::NotifyWindowCreateEvent& own_event = *(UI::NotifyWindowCreateEvent*)event.get();
DockView* it = (DockView*)superview();
it->on_window_create(own_event.bundle_id(), own_event.icon_path(), own_event.window_id(), own_event.window_type());
break;
}
case UI::Event::Type::NotifyWindowStatusChangedEvent: {
UI::NotifyWindowStatusChangedEvent& own_event = *(UI::NotifyWindowStatusChangedEvent*)event.get();
DockView* it = (DockView*)superview();
if (own_event.type() == UI::WindowStatusUpdateType::Removed) {
it->on_window_remove(own_event.changed_window_id());
}
if (own_event.type() == UI::WindowStatusUpdateType::Minimized) {
it->on_window_minimize(own_event.changed_window_id());
}
break;
}
case UI::Event::Type::NotifyWindowIconChangedEvent: {
UI::NotifyWindowIconChangedEvent& own_event = *(UI::NotifyWindowIconChangedEvent*)event.get();
DockView* it = (DockView*)superview();
it->set_icon(own_event.changed_window_id(), own_event.icon_path());
break;
}
case UI::Event::Type::NotifyWindowTitleChangedEvent: {
UI::NotifyWindowTitleChangedEvent& own_event = *(UI::NotifyWindowTitleChangedEvent*)event.get();
DockView* it = (DockView*)superview();
it->set_title(own_event.changed_window_id(), own_event.title());
break;
}
}
Window::receive_event(std::move(event));
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include <libg/Size.h>
#include <libui/Screen.h>
#include <libui/Window.h>
class DockWindow : public UI::Window {
public:
DockWindow()
: UI::Window("Dock", LG::Size(UI::Screen::main().bounds().width(), 50), UI::WindowType::Homescreen)
{
if (fork() == 0) {
for (int i = 3; i < 32; i++) {
close(i);
}
execlp("/System/applist", "/System/applist", NULL);
std::abort();
}
}
void receive_event(std::unique_ptr<LFoundation::Event> event) override;
};

View File

@@ -0,0 +1,74 @@
#include "IconView.h"
#include "DockView.h"
#include <libui/App.h>
#include <libui/Context.h>
#include <libui/Label.h>
#include <libui/Screen.h>
#include <libui/Window.h>
IconView::IconView(View* superview, const LG::Rect& frame)
: View(superview, frame)
{
}
void IconView::display(const LG::Rect& rect)
{
const int padding = 4;
const int offset_x = (DockView::icon_view_size() - DockView::icon_size()) / 2;
const int offset_y = (DockView::icon_view_size() - DockView::icon_size()) / 2;
LG::Context ctx = UI::graphics_current_context();
ctx.add_clip(rect);
auto icon_rect = LG::Rect(offset_x, offset_y, DockView::icon_size(), DockView::icon_size());
ctx.draw(icon_rect.origin(), m_launch_entity.icon());
if (is_hovered()) {
ctx.set_fill_color(LG::Color::LightSystemOpaque128);
ctx.fill(icon_rect);
}
ctx.set_fill_color(LG::Color(120, 129, 133, 40));
ctx.draw_box_shading(icon_rect, LG::Shading(LG::Shading::Type::Box, 0, 3), LG::CornerMask(6));
ctx.set_fill_color(LG::Color(163, 174, 190));
const int underline_y = DockView::icon_view_size() - underline_height() - padding;
if (entity().windows().size() > 0) {
const int len = 8;
ctx.fill({ (DockView::icon_view_size() - len) / 2, underline_y, len, underline_height() });
}
}
void IconView::on_click()
{
if (entity().windows().empty()) {
launch();
return;
} else {
auto demo_menu = UI::Menu();
for (auto& win : entity().windows()) {
auto title = win.title();
int this_window_id = window()->id();
int target_window_id = win.window_id();
if (win.is_minimized()) {
title += " - minimized";
}
demo_menu.add_item(UI::MenuItem(title, [this, this_window_id, target_window_id] {
for (auto& win : entity().windows()) {
if (win.window_id() == target_window_id) {
win.set_minimized(false);
break;
}
}
auto& app = UI::App::the();
AskBringToFrontMessage msg(app.connection().key(), this_window_id, target_window_id);
app.connection().send_async_message(msg);
}));
}
demo_menu.add_item(UI::MenuItem("New window", [this] {
launch();
}));
window()->popup_manager().show({ frame().min_x(), (int)UI::Screen::main().bounds().height() - (int)DockView::dock_view_height() - 4 }, demo_menu);
}
}

View File

@@ -0,0 +1,61 @@
#pragma once
#include "DockEntity.h"
#include "WindowEntity.h"
#include <libg/Font.h>
#include <libui/Label.h>
#include <libui/PopupMenu.h>
#include <libui/View.h>
#include <list>
#include <string>
#include <unistd.h>
class IconView : public UI::View {
UI_OBJECT();
public:
IconView(View* superview, const LG::Rect& frame);
void display(const LG::Rect& rect) override;
void mouse_up() override { on_click(); }
static constexpr size_t underline_height() { return 2; }
void set_title(const std::string& title)
{
if (!m_label) {
return;
}
m_label->set_text(title);
}
DockEntity& entity() { return m_launch_entity; }
const DockEntity& entity() const { return m_launch_entity; }
virtual void mouse_entered(const LG::Point<int>& location) override
{
View::mouse_entered(location);
set_needs_display();
}
virtual void mouse_exited() override
{
View::mouse_exited();
set_needs_display();
}
private:
void on_click();
void launch()
{
if (fork() == 0) {
for (int i = 3; i < 32; i++) {
close(i);
}
execlp(m_launch_entity.path_to_exec().c_str(), m_launch_entity.path_to_exec().c_str(), NULL);
std::abort();
}
}
UI::Label* m_label;
DockEntity m_launch_entity;
};

Some files were not shown because too many files have changed in this diff Show More