feature: (partial) beginning implementation of container groups for multi-selection behind a feature flag (#202)

- Begin an implementation for FloatingTreeContainer
- Hide FloatingTreeContainer behind a feature flag
- Added the ability to apply filters on rendering
- Further refactoring of how Containers are handled in general
This commit is contained in:
Matthew Kosarek 2024-08-18 09:52:59 -04:00 committed by GitHub
parent bb15d31711
commit bd17e5b260
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 1386 additions and 370 deletions

View File

@ -67,8 +67,12 @@ add_library(miracle-wm-implementation
src/program_factory.cpp
src/mode_observer.cpp
src/debug_helper.h
src/floating_container.cpp
src/floating_window_container.cpp
src/shell_component_container.cpp
src/container_group_container.cpp
src/container_group_container.h
src/floating_tree_container.cpp
src/floating_tree_container.h
)
add_executable(miracle-wm

View File

@ -24,22 +24,30 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
namespace miracle
{
class Container;
class Output;
enum class WindowManagerMode
{
normal = 0,
/// While resizing, only the window that was selected during
/// While [resizing], only the window that was selected during
/// resize can be selected. If that window closes, resize
/// is completed.
resizing
resizing,
/// While [selecting], only [Container]s selected with the multi-select
/// keybind/mousebind can be selected or deselected.
selecting
};
struct CompositorState
{
mir::geometry::Point cursor_position;
WindowManagerMode mode = WindowManagerMode::normal;
std::shared_ptr<Output> active_output;
std::shared_ptr<Container> active;
mir::geometry::Point cursor_position;
uint32_t modifiers = 0;
bool has_clicked_floating_window = false;
};
}

View File

@ -18,8 +18,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define MIR_LOG_COMPONENT "container"
#include "container.h"
#include "floating_container.h"
#include "floating_window_container.h"
#include "leaf_container.h"
#include "container_group_container.h"
#include "node_common.h"
#include "output.h"
#include "parent_container.h"
@ -33,7 +34,7 @@ ContainerType miracle::container_type_from_string(std::string const& str)
if (str == "tiled")
return ContainerType::leaf;
else if (str == "floating")
return ContainerType::floating;
return ContainerType::floating_window;
else if (str == "shell")
return ContainerType::shell;
else
@ -70,9 +71,14 @@ std::shared_ptr<ParentContainer> Container::as_parent(std::shared_ptr<Container>
return std::dynamic_pointer_cast<ParentContainer>(container);
}
std::shared_ptr<FloatingContainer> Container::as_floating(std::shared_ptr<Container> const& container)
std::shared_ptr<FloatingWindowContainer> Container::as_floating(std::shared_ptr<Container> const& container)
{
return std::dynamic_pointer_cast<FloatingContainer>(container);
return std::dynamic_pointer_cast<FloatingWindowContainer>(container);
}
std::shared_ptr<ContainerGroupContainer> Container::as_group(std::shared_ptr<Container> const& container)
{
return std::dynamic_pointer_cast<ContainerGroupContainer>(container);
}
bool Container::is_leaf()

View File

@ -36,7 +36,8 @@ class MiracleConfig;
class TilingWindowTree;
class LeafContainer;
class ParentContainer;
class FloatingContainer;
class FloatingWindowContainer;
class ContainerGroupContainer;
class Workspace;
class Output;
@ -44,9 +45,11 @@ enum class ContainerType
{
none,
leaf,
floating,
floating_window,
floating_tree,
shell,
parent
parent,
group
};
ContainerType container_type_from_string(std::string const& str);
@ -61,8 +64,8 @@ class Container : public std::enable_shared_from_this<Container>
public:
virtual ContainerType get_type() const = 0;
virtual void restore_state(MirWindowState) = 0;
virtual std::optional<MirWindowState> restore_state() = 0;
virtual void show() = 0;
virtual void hide() = 0;
/// Commits any changes made to this node to the screen. This must
/// be call for changes to be pushed to the scene. Additionally,
@ -118,7 +121,8 @@ public:
static std::shared_ptr<LeafContainer> as_leaf(std::shared_ptr<Container> const&);
static std::shared_ptr<ParentContainer> as_parent(std::shared_ptr<Container> const&);
static std::shared_ptr<FloatingContainer> as_floating(std::shared_ptr<Container> const&);
static std::shared_ptr<FloatingWindowContainer> as_floating(std::shared_ptr<Container> const&);
static std::shared_ptr<ContainerGroupContainer> as_group(std::shared_ptr<Container> const&);
protected:
[[nodiscard]] std::array<bool, (size_t)Direction::MAX> get_neighbors() const;

View File

@ -0,0 +1,349 @@
/**
Copyright (C) 2024 Matthew Kosarek
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**/
#include "container_group_container.h"
#include "workspace.h"
#include "output.h"
#include "compositor_state.h"
namespace miracle
{
ContainerGroupContainer::ContainerGroupContainer(CompositorState& state)
: state{state}
{
}
void ContainerGroupContainer::add(std::shared_ptr<Container> const& container)
{
containers.push_back(container);
}
void ContainerGroupContainer::remove(std::shared_ptr<Container> const& container)
{
containers.erase(std::remove_if(
containers.begin(),
containers.end(),
[&](std::weak_ptr<Container> const& weak_container)
{
return weak_container.expired() || weak_container.lock() == container;
}
), containers.end());
}
bool ContainerGroupContainer::contains(std::shared_ptr<Container const> const& container) const
{
for (auto const& other : containers)
{
if (!other.expired() && other.lock() == container)
return true;
}
return false;
}
ContainerType ContainerGroupContainer::get_type() const
{
return ContainerType::group;
}
void ContainerGroupContainer::show()
{
for (auto const& container : containers)
{
if (auto c = container.lock())
c->show();
}
}
void ContainerGroupContainer::hide()
{
for (auto const& container : containers)
{
if (auto c = container.lock())
c->hide();
}
}
void ContainerGroupContainer::commit_changes()
{
for (auto const& container : containers)
{
if (auto c = container.lock())
c->commit_changes();
}
}
mir::geometry::Rectangle ContainerGroupContainer::get_logical_area() const
{
return {};
}
void ContainerGroupContainer::set_logical_area(mir::geometry::Rectangle const &rectangle)
{
}
mir::geometry::Rectangle ContainerGroupContainer::get_visible_area() const
{
return {};
}
void ContainerGroupContainer::constrain()
{
for (auto const& container : containers)
{
if (auto c = container.lock())
c->constrain();
}
}
std::weak_ptr<ParentContainer> ContainerGroupContainer::get_parent() const
{
return {};
}
void ContainerGroupContainer::set_parent(std::shared_ptr<ParentContainer> const &ptr)
{
throw std::logic_error("Cannot set-parent on ContainerGroup");
}
size_t ContainerGroupContainer::get_min_height() const
{
return 0;
}
size_t ContainerGroupContainer::get_min_width() const
{
return 0;
}
void ContainerGroupContainer::handle_ready()
{
throw std::logic_error("handle_ready should not be called on a ContainerGroup");
}
void ContainerGroupContainer::handle_modify(miral::WindowSpecification const &specification)
{
for (auto const& container : containers)
{
if (auto c = container.lock())
c->handle_modify(specification);
}
}
void ContainerGroupContainer::handle_request_move(MirInputEvent const *input_event)
{
for (auto const& container : containers)
{
if (auto c = container.lock())
c->handle_request_move(input_event);
}
}
void ContainerGroupContainer::handle_request_resize(MirInputEvent const *input_event, MirResizeEdge edge)
{
for (auto const& container : containers)
{
if (auto c = container.lock())
c->handle_request_resize(input_event, edge);
}
}
void ContainerGroupContainer::handle_raise()
{
for (auto const& container : containers)
{
if (auto c = container.lock())
c->handle_raise();
}
}
bool ContainerGroupContainer::resize(Direction direction)
{
bool result = true;
for (auto const& container : containers)
{
if (auto c = container.lock())
result &= c->resize(direction);
}
return result;
}
bool ContainerGroupContainer::toggle_fullscreen()
{
bool result = true;
for (auto const& container : containers)
{
if (auto c = container.lock())
result &= c->toggle_fullscreen();
}
return result;
}
void ContainerGroupContainer::request_horizontal_layout()
{
for (auto const& container : containers)
{
if (auto c = container.lock())
c->request_horizontal_layout();
}
}
void ContainerGroupContainer::request_vertical_layout()
{
for (auto const& container : containers)
{
if (auto c = container.lock())
c->request_vertical_layout();
}
}
void ContainerGroupContainer::toggle_layout()
{
for (auto const& container : containers)
{
if (auto c = container.lock())
c->toggle_layout();
}
}
void ContainerGroupContainer::on_open()
{
}
void ContainerGroupContainer::on_focus_gained()
{
}
void ContainerGroupContainer::on_focus_lost()
{
}
void ContainerGroupContainer::on_move_to(mir::geometry::Point const &top_left)
{
}
mir::geometry::Rectangle
ContainerGroupContainer::confirm_placement(MirWindowState state, mir::geometry::Rectangle const &rectangle)
{
return {};
}
Workspace* ContainerGroupContainer::get_workspace() const
{
return state.active_output->get_active_workspace().get();
}
Output* ContainerGroupContainer::get_output() const
{
return state.active_output.get();
}
glm::mat4 ContainerGroupContainer::get_transform() const
{
return glm::mat4(1.f);
}
void ContainerGroupContainer::set_transform(glm::mat4 transform)
{
}
glm::mat4 ContainerGroupContainer::get_workspace_transform() const
{
return glm::mat4(1.f);
}
glm::mat4 ContainerGroupContainer::get_output_transform() const
{
return glm::mat4(1.f);
}
uint32_t ContainerGroupContainer::animation_handle() const
{
return 0;
}
void ContainerGroupContainer::animation_handle(uint32_t uint_32)
{
}
bool ContainerGroupContainer::is_focused() const
{
return state.active.get() == this;
}
bool ContainerGroupContainer::is_fullscreen() const
{
return false;
}
std::optional<miral::Window> ContainerGroupContainer::window() const
{
return std::nullopt;
}
bool ContainerGroupContainer::select_next(Direction direction)
{
return false;
}
bool ContainerGroupContainer::pinned() const
{
return false;
}
bool ContainerGroupContainer::pinned(bool b)
{
return false;
}
bool ContainerGroupContainer::move(Direction direction)
{
bool result = true;
for (auto const& container : containers)
{
if (auto c = container.lock())
result &= c->move(direction);
}
return result;
}
bool ContainerGroupContainer::move_by(Direction direction, int pixels)
{
bool result = true;
for (auto const& container : containers)
{
if (auto c = container.lock())
result &= c->move_by(direction, pixels);
}
return result;
}
bool ContainerGroupContainer::move_to(int x, int y)
{
bool result = true;
for (auto const& container : containers)
{
if (auto c = container.lock())
result &= c->move_to(x, y);
}
return result;
}
} // miracle

View File

@ -0,0 +1,96 @@
/**
Copyright (C) 2024 Matthew Kosarek
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef MIRACLE_WM_CONTAINER_GROUP_CONTAINER_H
#define MIRACLE_WM_CONTAINER_GROUP_CONTAINER_H
#include "container.h"
#include <vector>
#include <memory>
namespace miracle
{
class CompositorState;
/// A [Container] that contains [Container]s. This is often
/// used in a temporary way when mulitple [Container]s are selected
/// at once. The [ContainerGroupContainer] is incapable of performing
/// some actions by design. It weakly owns its members, meaning that
/// [Container]s may be removed from underneath it.
class ContainerGroupContainer : public Container
{
public:
ContainerGroupContainer(CompositorState&);
void add(std::shared_ptr<Container> const&);
void remove(std::shared_ptr<Container> const&);
bool contains(std::shared_ptr<Container const> const&) const;
[[nodiscard]] std::vector<std::weak_ptr<Container>> const& get_containers() const { return containers; }
ContainerType get_type() const override;
void show() override;
void hide() override;
void commit_changes() override;
mir::geometry::Rectangle get_logical_area() const override;
void set_logical_area(mir::geometry::Rectangle const &rectangle) override;
mir::geometry::Rectangle get_visible_area() const override;
void constrain() override;
std::weak_ptr<ParentContainer> get_parent() const override;
void set_parent(std::shared_ptr<ParentContainer> const &ptr) override;
size_t get_min_height() const override;
size_t get_min_width() const override;
void handle_ready() override;
void handle_modify(miral::WindowSpecification const &specification) override;
void handle_request_move(MirInputEvent const *input_event) override;
void handle_request_resize(MirInputEvent const *input_event, MirResizeEdge edge) override;
void handle_raise() override;
bool resize(Direction direction) override;
bool toggle_fullscreen() override;
void request_horizontal_layout() override;
void request_vertical_layout() override;
void toggle_layout() override;
void on_open() override;
void on_focus_gained() override;
void on_focus_lost() override;
void on_move_to(mir::geometry::Point const &top_left) override;
mir::geometry::Rectangle
confirm_placement(MirWindowState state, mir::geometry::Rectangle const &rectangle) override;
Workspace *get_workspace() const override;
Output *get_output() const override;
glm::mat4 get_transform() const override;
void set_transform(glm::mat4 transform) override;
glm::mat4 get_workspace_transform() const override;
glm::mat4 get_output_transform() const override;
uint32_t animation_handle() const override;
void animation_handle(uint32_t uint_32) override;
bool is_focused() const override;
bool is_fullscreen() const override;
std::optional<miral::Window> window() const override;
bool select_next(Direction direction) override;
bool pinned() const override;
bool pinned(bool b) override;
bool move(Direction direction) override;
bool move_by(Direction direction, int pixels) override;
bool move_to(int x, int y) override;
private:
std::vector<std::weak_ptr<Container>> containers;
CompositorState& state;
};
} // miracle
#endif //MIRACLE_WM_CONTAINER_GROUP_CONTAINER_H

23
src/feature_flags.h Normal file
View File

@ -0,0 +1,23 @@
/**
Copyright (C) 2024 Matthew Kosarek
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef MIRACLE_WM_FEATURE_FLAGS_H
#define MIRACLE_WM_FEATURE_FLAGS_H
#define MIRACLE_FEATURE_FLAG_MULTI_SELECT false
#endif //MIRACLE_WM_FEATURE_FLAGS_H

View File

@ -0,0 +1,291 @@
/**
Copyright (C) 2024 Matthew Kosarek
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**/
#include "floating_tree_container.h"
#include "tiling_window_tree.h"
#include "workspace.h"
namespace
{
class FloatingTreeTilingWindowTreeInterface : public miracle::TilingWindowTreeInterface
{
public:
explicit FloatingTreeTilingWindowTreeInterface(miracle::Workspace* workspace) :
workspace { workspace }
{
}
std::vector<miral::Zone> const& get_zones() override
{
return zones;
}
miracle::Workspace* get_workspace() const override
{
return workspace;
}
private:
miracle::Workspace* workspace;
std::vector<miral::Zone> zones;
};
}
namespace miracle
{
FloatingTreeContainer::FloatingTreeContainer(
Workspace* workspace,
WindowController& window_controller,
CompositorState const& compositor_state,
std::shared_ptr<MiracleConfig> const& config)
: tree{std::make_unique<TilingWindowTree>(
std::make_unique<FloatingTreeTilingWindowTreeInterface>(workspace),
window_controller,
compositor_state,
config,
geom::Rectangle{geom::Point{100, 100}, geom::Size{640, 480}}
)},
workspace_{workspace}
{
}
ContainerType FloatingTreeContainer::get_type() const
{
return ContainerType::floating_tree;
}
void FloatingTreeContainer::show()
{
tree->show();
}
void FloatingTreeContainer::hide()
{
tree->hide();
}
void FloatingTreeContainer::commit_changes()
{
}
mir::geometry::Rectangle FloatingTreeContainer::get_logical_area() const
{
return tree->get_area();
}
void FloatingTreeContainer::set_logical_area(mir::geometry::Rectangle const &rectangle)
{
tree->set_area(rectangle);
}
mir::geometry::Rectangle FloatingTreeContainer::get_visible_area() const
{
return get_logical_area();
}
void FloatingTreeContainer::constrain()
{
}
std::weak_ptr<ParentContainer> FloatingTreeContainer::get_parent() const
{
return {};
}
void FloatingTreeContainer::set_parent(std::shared_ptr<ParentContainer> const &ptr)
{
throw std::logic_error("FloatingTreeContainer::set_parent: invalid operation");
}
size_t FloatingTreeContainer::get_min_height() const
{
return 0;
}
size_t FloatingTreeContainer::get_min_width() const
{
return 0;
}
void FloatingTreeContainer::handle_ready()
{
}
void FloatingTreeContainer::handle_modify(miral::WindowSpecification const &specification)
{
}
void FloatingTreeContainer::handle_request_move(MirInputEvent const *input_event)
{
// TODO
}
void FloatingTreeContainer::handle_request_resize(MirInputEvent const *input_event, MirResizeEdge edge)
{
// TODO
}
void FloatingTreeContainer::handle_raise()
{
}
bool FloatingTreeContainer::resize(Direction direction)
{
// TODO
return false;
}
bool FloatingTreeContainer::toggle_fullscreen()
{
// TODO:
return false;
}
void FloatingTreeContainer::request_horizontal_layout()
{
}
void FloatingTreeContainer::request_vertical_layout()
{
}
void FloatingTreeContainer::toggle_layout()
{
}
void FloatingTreeContainer::on_open()
{
}
void FloatingTreeContainer::on_focus_gained()
{
}
void FloatingTreeContainer::on_focus_lost()
{
}
void FloatingTreeContainer::on_move_to(mir::geometry::Point const &top_left)
{
auto area = tree->get_area();
area.top_left = top_left;
tree->set_area(area);
}
mir::geometry::Rectangle
FloatingTreeContainer::confirm_placement(MirWindowState state, mir::geometry::Rectangle const &rectangle)
{
return rectangle;
}
Workspace *FloatingTreeContainer::get_workspace() const
{
return workspace_;
}
Output *FloatingTreeContainer::get_output() const
{
return workspace_->get_output();
}
glm::mat4 FloatingTreeContainer::get_transform() const
{
return glm::mat4(1.f);
}
void FloatingTreeContainer::set_transform(glm::mat4 transform)
{
}
glm::mat4 FloatingTreeContainer::get_workspace_transform() const
{
return glm::mat4(1.f);
}
glm::mat4 FloatingTreeContainer::get_output_transform() const
{
return glm::mat4(1.f);
}
uint32_t FloatingTreeContainer::animation_handle() const
{
return 0;
}
void FloatingTreeContainer::animation_handle(uint32_t uint_32)
{
}
bool FloatingTreeContainer::is_focused() const
{
// TODO:
return false;
}
bool FloatingTreeContainer::is_fullscreen() const
{
// TODO:
return false;
}
std::optional<miral::Window> FloatingTreeContainer::window() const
{
return {};
}
bool FloatingTreeContainer::select_next(Direction direction)
{
return false;
}
bool FloatingTreeContainer::pinned() const
{
// TODO:
return false;
}
bool FloatingTreeContainer::pinned(bool b)
{
// TODO:
return false;
}
bool FloatingTreeContainer::move(Direction direction)
{
// TODO:
return false;
}
bool FloatingTreeContainer::move_by(Direction direction, int pixels)
{
// TODO:
return false;
}
bool FloatingTreeContainer::move_to(int x, int y)
{
auto area = tree->get_area();
area.top_left = geom::Point{x, y};
tree->set_area(area);
return true;
}
} // miracle

View File

@ -0,0 +1,96 @@
/**
Copyright (C) 2024 Matthew Kosarek
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef MIRACLE_WM_FLOATING_TREE_CONTAINER_H
#define MIRACLE_WM_FLOATING_TREE_CONTAINER_H
#include "container.h"
#include <memory>
namespace miracle
{
class TilingWindowTree;
class Workspace;
class WindowController;
class CompositorState;
class MiracleConfig;
class FloatingTreeContainer : public Container
{
public:
FloatingTreeContainer(
Workspace*,
WindowController&,
CompositorState const&,
std::shared_ptr<MiracleConfig> const&
);
[[nodiscard]] TilingWindowTree* get_tree() const { return tree.get(); }
ContainerType get_type() const override;
void show() override;
void hide() override;
void commit_changes() override;
mir::geometry::Rectangle get_logical_area() const override;
void set_logical_area(mir::geometry::Rectangle const &rectangle) override;
mir::geometry::Rectangle get_visible_area() const override;
void constrain() override;
std::weak_ptr<ParentContainer> get_parent() const override;
void set_parent(std::shared_ptr<ParentContainer> const &ptr) override;
size_t get_min_height() const override;
size_t get_min_width() const override;
void handle_ready() override;
void handle_modify(miral::WindowSpecification const &specification) override;
void handle_request_move(MirInputEvent const *input_event) override;
void handle_request_resize(MirInputEvent const *input_event, MirResizeEdge edge) override;
void handle_raise() override;
bool resize(Direction direction) override;
bool toggle_fullscreen() override;
void request_horizontal_layout() override;
void request_vertical_layout() override;
void toggle_layout() override;
void on_open() override;
void on_focus_gained() override;
void on_focus_lost() override;
void on_move_to(mir::geometry::Point const &top_left) override;
mir::geometry::Rectangle
confirm_placement(MirWindowState state, mir::geometry::Rectangle const &rectangle) override;
Workspace *get_workspace() const override;
Output *get_output() const override;
glm::mat4 get_transform() const override;
void set_transform(glm::mat4 transform) override;
glm::mat4 get_workspace_transform() const override;
glm::mat4 get_output_transform() const override;
uint32_t animation_handle() const override;
void animation_handle(uint32_t uint_32) override;
bool is_focused() const override;
bool is_fullscreen() const override;
std::optional<miral::Window> window() const override;
bool select_next(Direction direction) override;
bool pinned() const override;
bool pinned(bool b) override;
bool move(Direction direction) override;
bool move_by(Direction direction, int pixels) override;
bool move_to(int x, int y) override;
private:
std::unique_ptr<TilingWindowTree> tree;
Workspace* workspace_;
};
} // miracle
#endif //MIRACLE_WM_FLOATING_TREE_CONTAINER_H

View File

@ -17,7 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define MIR_LOG_COMPONENT "floating_container"
#include "floating_container.h"
#include "floating_window_container.h"
#include "compositor_state.h"
#include "leaf_container.h"
#include "output.h"
@ -28,7 +28,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
using namespace miracle;
FloatingContainer::FloatingContainer(
FloatingWindowContainer::FloatingWindowContainer(
miral::Window const& window,
std::shared_ptr<miral::MinimalWindowManager> const& wm,
WindowController& window_controller,
@ -42,21 +42,21 @@ FloatingContainer::FloatingContainer(
{
}
void FloatingContainer::commit_changes()
void FloatingWindowContainer::commit_changes()
{
}
mir::geometry::Rectangle FloatingContainer::get_logical_area() const
mir::geometry::Rectangle FloatingWindowContainer::get_logical_area() const
{
return get_visible_area();
}
void FloatingContainer::set_logical_area(mir::geometry::Rectangle const& rectangle)
void FloatingWindowContainer::set_logical_area(mir::geometry::Rectangle const& rectangle)
{
window_controller.set_rectangle(window_, get_visible_area(), rectangle);
}
mir::geometry::Rectangle FloatingContainer::get_visible_area() const
mir::geometry::Rectangle FloatingWindowContainer::get_visible_area() const
{
return {
window_.top_left(),
@ -64,59 +64,59 @@ mir::geometry::Rectangle FloatingContainer::get_visible_area() const
};
}
void FloatingContainer::constrain() { }
void FloatingWindowContainer::constrain() { }
void FloatingContainer::set_parent(std::shared_ptr<ParentContainer> const& parent)
void FloatingWindowContainer::set_parent(std::shared_ptr<ParentContainer> const& parent)
{
throw std::logic_error("FloatingContainer cannot have a parent");
}
size_t FloatingContainer::get_min_height() const
size_t FloatingWindowContainer::get_min_height() const
{
return 1;
}
size_t FloatingContainer::get_min_width() const
size_t FloatingWindowContainer::get_min_width() const
{
return 1;
}
void FloatingContainer::handle_ready()
void FloatingWindowContainer::handle_ready()
{
auto& info = window_controller.info_for(window_);
wm->handle_window_ready(info);
}
void FloatingContainer::handle_modify(miral::WindowSpecification const& modifications)
void FloatingWindowContainer::handle_modify(miral::WindowSpecification const& modifications)
{
auto& info = window_controller.info_for(window_);
wm->handle_modify_window(info, modifications);
}
void FloatingContainer::handle_request_move(MirInputEvent const* input_event)
void FloatingWindowContainer::handle_request_move(MirInputEvent const* input_event)
{
wm->handle_request_move(
window_controller.info_for(window_), input_event);
}
void FloatingContainer::handle_request_resize(
void FloatingWindowContainer::handle_request_resize(
MirInputEvent const* input_event, MirResizeEdge edge)
{
wm->handle_request_resize(
window_controller.info_for(window_), input_event, edge);
}
void FloatingContainer::handle_raise()
void FloatingWindowContainer::handle_raise()
{
wm->handle_raise_window(window_controller.info_for(window_));
}
void FloatingContainer::on_open()
void FloatingWindowContainer::on_open()
{
window_controller.open(window_);
}
void FloatingContainer::on_focus_gained()
void FloatingWindowContainer::on_focus_gained()
{
if (get_output()->get_active_workspace()->get_workspace() != workspace_->get_workspace())
return;
@ -124,17 +124,17 @@ void FloatingContainer::on_focus_gained()
wm->advise_focus_gained(window_controller.info_for(window_));
}
void FloatingContainer::on_focus_lost()
void FloatingWindowContainer::on_focus_lost()
{
wm->advise_focus_lost(window_controller.info_for(window_));
}
void FloatingContainer::on_move_to(geom::Point const& top_left)
void FloatingWindowContainer::on_move_to(geom::Point const& top_left)
{
wm->advise_move_to(window_controller.info_for(window_), top_left);
}
mir::geometry::Rectangle FloatingContainer::confirm_placement(
mir::geometry::Rectangle FloatingWindowContainer::confirm_placement(
MirWindowState state, mir::geometry::Rectangle const& placement)
{
return wm->confirm_placement_on_display(
@ -143,107 +143,122 @@ mir::geometry::Rectangle FloatingContainer::confirm_placement(
placement);
}
bool FloatingContainer::pinned() const
bool FloatingWindowContainer::pinned() const
{
return is_pinned;
}
bool FloatingContainer::pinned(bool in)
bool FloatingWindowContainer::pinned(bool in)
{
is_pinned = in;
return true;
}
std::optional<miral::Window> FloatingContainer::window() const
std::optional<miral::Window> FloatingWindowContainer::window() const
{
return window_;
}
bool FloatingContainer::resize(Direction direction)
bool FloatingWindowContainer::resize(Direction direction)
{
return false;
}
bool FloatingContainer::toggle_fullscreen()
bool FloatingWindowContainer::toggle_fullscreen()
{
return false;
}
void FloatingContainer::request_horizontal_layout()
void FloatingWindowContainer::request_horizontal_layout()
{
}
void FloatingContainer::request_vertical_layout()
void FloatingWindowContainer::request_vertical_layout()
{
}
void FloatingContainer::toggle_layout()
void FloatingWindowContainer::toggle_layout()
{
}
void FloatingContainer::restore_state(MirWindowState state)
void FloatingWindowContainer::show()
{
restore_state_ = state;
if (is_pinned)
{
window_controller.raise(window_);
return;
}
if (restore_state_)
{
miral::WindowSpecification spec;
spec.state() = restore_state_.value();
window_controller.modify(window_, spec);
window_controller.raise(window_);
restore_state_.reset();
}
}
std::optional<MirWindowState> FloatingContainer::restore_state()
void FloatingWindowContainer::hide()
{
auto state = restore_state_;
restore_state_.reset();
return state;
restore_state_ = window_controller.info_for(window_).state();
miral::WindowSpecification spec;
spec.state() = mir_window_state_hidden;
window_controller.modify(window_, spec);
window_controller.send_to_back(window_);
}
Workspace* FloatingContainer::get_workspace() const
Workspace* FloatingWindowContainer::get_workspace() const
{
return workspace_;
}
void FloatingContainer::set_workspace(Workspace* workspace)
void FloatingWindowContainer::set_workspace(Workspace* workspace)
{
workspace_ = workspace;
}
Output* FloatingContainer::get_output() const
Output* FloatingWindowContainer::get_output() const
{
return workspace_->get_output();
}
glm::mat4 FloatingContainer::get_transform() const
glm::mat4 FloatingWindowContainer::get_transform() const
{
return transform;
}
void FloatingContainer::set_transform(glm::mat4 transform_)
void FloatingWindowContainer::set_transform(glm::mat4 transform_)
{
transform = transform_;
}
uint32_t FloatingContainer::animation_handle() const
uint32_t FloatingWindowContainer::animation_handle() const
{
return animation_handle_;
}
void FloatingContainer::animation_handle(uint32_t handle)
void FloatingWindowContainer::animation_handle(uint32_t handle)
{
animation_handle_ = handle;
}
bool FloatingContainer::is_focused() const
bool FloatingWindowContainer::is_focused() const
{
return state.active.get() == this;
}
bool FloatingContainer::is_fullscreen() const
bool FloatingWindowContainer::is_fullscreen() const
{
return window_controller.is_fullscreen(window_);
}
ContainerType FloatingContainer::get_type() const
ContainerType FloatingWindowContainer::get_type() const
{
return ContainerType::floating;
return ContainerType::floating_window;
}
glm::mat4 FloatingContainer::get_workspace_transform() const
glm::mat4 FloatingWindowContainer::get_workspace_transform() const
{
if (is_pinned)
return glm::mat4(1.f);
@ -255,7 +270,7 @@ glm::mat4 FloatingContainer::get_workspace_transform() const
glm::vec3(workspace_rect.top_left.x.as_int(), workspace_rect.top_left.y.as_int(), 0));
}
glm::mat4 FloatingContainer::get_output_transform() const
glm::mat4 FloatingWindowContainer::get_output_transform() const
{
if (is_pinned)
return glm::mat4(1.f);
@ -263,17 +278,17 @@ glm::mat4 FloatingContainer::get_output_transform() const
return get_output()->get_transform();
}
bool FloatingContainer::select_next(Direction)
bool FloatingWindowContainer::select_next(Direction)
{
return false;
}
bool FloatingContainer::move(Direction direction)
bool FloatingWindowContainer::move(Direction direction)
{
return move_by(direction, 10);
}
bool FloatingContainer::move_by(Direction direction, int pixels)
bool FloatingWindowContainer::move_by(Direction direction, int pixels)
{
auto& info = window_controller.info_for(window_);
auto prev_pos = window_.top_left();
@ -309,7 +324,7 @@ bool FloatingContainer::move_by(Direction direction, int pixels)
return true;
}
bool FloatingContainer::move_to(int x, int y)
bool FloatingWindowContainer::move_to(int x, int y)
{
miral::WindowSpecification spec;
spec.top_left() = { x, y };
@ -317,7 +332,7 @@ bool FloatingContainer::move_to(int x, int y)
return true;
}
std::weak_ptr<ParentContainer> FloatingContainer::get_parent() const
std::weak_ptr<ParentContainer> FloatingWindowContainer::get_parent() const
{
return std::weak_ptr<ParentContainer>();
}

View File

@ -15,8 +15,8 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**/
#ifndef MIRACLE_WM_FLOATING_CONTAINER_H
#define MIRACLE_WM_FLOATING_CONTAINER_H
#ifndef MIRACLE_WM_FLOATING_WINDOW_CONTAINER_H
#define MIRACLE_WM_FLOATING_WINDOW_CONTAINER_H
#include "container.h"
#include <miral/minimal_window_manager.h>
@ -28,10 +28,10 @@ class WindowController;
class CompositorState;
/// Contains a single floating window
class FloatingContainer : public Container
class FloatingWindowContainer : public Container
{
public:
FloatingContainer(
FloatingWindowContainer(
miral::Window const&,
std::shared_ptr<miral::MinimalWindowManager> const& wm,
WindowController& window_controller,
@ -64,8 +64,8 @@ public:
bool pinned() const override;
bool pinned(bool) override;
[[nodiscard]] std::optional<miral::Window> window() const override;
void restore_state(MirWindowState state) override;
std::optional<MirWindowState> restore_state() override;
void show() override;
void hide() override;
Workspace* get_workspace() const override;
void set_workspace(Workspace*);
Output* get_output() const override;
@ -100,4 +100,4 @@ private:
} // miracle
#endif // MIRACLE_WM_FLOATING_CONTAINER_H
#endif // MIRACLE_WM_FLOATING_WINDOW_CONTAINER_H

View File

@ -159,6 +159,10 @@ json mode_to_json(WindowManagerMode mode)
return {
{ "name", "resize" }
};
case WindowManagerMode::selecting:
return {
{ "name", "selecting" }
};
default:
{
mir::fatal_error("handle_command: unknown binding state: %d", (int)mode);
@ -181,6 +185,11 @@ json mode_event_to_json(WindowManagerMode mode)
{ "change", "resize" },
{ "pango_markup", true }
};
case WindowManagerMode::selecting:
return {
{ "change", "selecting" },
{ "pango_markup", true }
};
default:
{
mir::fatal_error("handle_command: unknown binding state: %d", (int)mode);
@ -566,6 +575,7 @@ void Ipc::handle_command(miracle::Ipc::IpcClient& client, uint32_t payload_lengt
json response;
response.push_back("default");
response.push_back("resize");
response.push_back("selecting");
send_reply(client, payload_type, to_string(response));
return;
}

View File

@ -17,13 +17,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "leaf_container.h"
#include "compositor_state.h"
#include "mir_toolkit/common.h"
#include "miracle_config.h"
#include "output.h"
#include "parent_container.h"
#include "tiling_window_tree.h"
#include "window_helpers.h"
#include "workspace.h"
#include "container_group_container.h"
#include <mir_toolkit/common.h>
#include <cmath>
using namespace miracle;
@ -177,12 +179,15 @@ void LeafContainer::show()
{
next_state = before_shown_state;
before_shown_state.reset();
commit_changes();
window_controller.raise(window_);
}
void LeafContainer::hide()
{
before_shown_state = window_controller.get_state(window_);
next_state = mir_window_state_hidden;
commit_changes();
}
bool LeafContainer::toggle_fullscreen()
@ -277,18 +282,6 @@ void LeafContainer::set_tree(TilingWindowTree* tree_)
tree = tree_;
}
void LeafContainer::restore_state(MirWindowState state)
{
restore_state_ = state;
}
std::optional<MirWindowState> LeafContainer::restore_state()
{
auto state = restore_state_;
restore_state_.reset();
return state;
}
Workspace* LeafContainer::get_workspace() const
{
return tree->get_workspace();
@ -321,7 +314,14 @@ void LeafContainer::animation_handle(uint32_t handle)
bool LeafContainer::is_focused() const
{
return state.active.get() == this;
if (state.active.get() == this || parent.lock()->is_focused())
return true;
auto group = Container::as_group(state.active);
if (!group)
return false;
return group->contains(shared_from_this());
}
ContainerType LeafContainer::get_type() const

View File

@ -53,8 +53,6 @@ public:
std::weak_ptr<ParentContainer> get_parent() const override;
void set_parent(std::shared_ptr<ParentContainer> const&) override;
void set_state(MirWindowState state);
void show();
void hide();
bool is_fullscreen() const override;
void constrain() override;
size_t get_min_width() const override;
@ -80,8 +78,8 @@ public:
[[nodiscard]] TilingWindowTree* get_tree() const { return tree; }
[[nodiscard]] std::optional<miral::Window> window() const override { return window_; }
void commit_changes() override;
void restore_state(MirWindowState state) override;
std::optional<MirWindowState> restore_state() override;
void show() override;
void hide() override;
Workspace* get_workspace() const override;
Output* get_output() const override;
glm::mat4 get_transform() const override;
@ -110,7 +108,6 @@ private:
std::optional<MirWindowState> before_shown_state;
std::optional<MirWindowState> next_state;
NodeLayoutDirection tentative_direction = NodeLayoutDirection::none;
std::optional<MirWindowState> restore_state_;
glm::mat4 transform = glm::mat4(1.f);
uint32_t animation_handle_ = 0;
};

View File

@ -24,6 +24,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "renderer.h"
#include "surface_tracker.h"
#include "version.h"
#include "compositor_state.h"
#include <libnotify/notify.h>
#include <mir/log.h>
@ -65,6 +66,7 @@ int main(int argc, char const* argv[])
{
PRINT_OPENING_MESSAGE(MIRACLE_VERSION_STRING);
MirRunner runner { argc, argv };
miracle::CompositorState compositor_state;
std::function<void()> shutdown_hook { [] { } };
runner.add_stop_callback([&]
@ -85,7 +87,7 @@ int main(int argc, char const* argv[])
{
options = new WindowManagerOptions {
add_window_manager_policy<miracle::Policy>(
"tiling", auto_restarting_launcher, runner, config, surface_tracker, server)
"tiling", auto_restarting_launcher, runner, config, surface_tracker, server, compositor_state)
};
(*options)(server);
});
@ -130,7 +132,7 @@ int main(int argc, char const* argv[])
}),
CustomRenderer([&](std::unique_ptr<mir::graphics::gl::OutputSurface> x, std::shared_ptr<mir::graphics::GLRenderingProvider> y)
{
return std::make_unique<miracle::Renderer>(std::move(y), std::move(x), config, surface_tracker);
return std::make_unique<miracle::Renderer>(std::move(y), std::move(x), config, surface_tracker, compositor_state);
}),
miroil::OpenGLContext(new miracle::GLConfig()) });
}

View File

@ -823,7 +823,7 @@ void FilesystemConfiguration::_load()
{
auto num = workspace["number"].as<int>();
auto type = container_type_from_string(workspace["layout"].as<std::string>());
if (type != ContainerType::leaf && type != ContainerType::floating)
if (type != ContainerType::leaf && type != ContainerType::floating_window)
{
mir::log_error("layout should be 'tiled' or 'floating': L%d:%d", workspace["layout"].Mark().line, workspace["layout"].Mark().column);
continue;
@ -956,6 +956,11 @@ void FilesystemConfiguration::try_process_change()
}
}
uint FilesystemConfiguration::get_primary_modifier() const
{
return primary_modifier;
}
uint FilesystemConfiguration::parse_modifier(std::string const& stringified_action_key)
{
if (stringified_action_key == "alt")

View File

@ -132,6 +132,15 @@ struct WorkspaceConfig
ContainerType layout = ContainerType::leaf;
};
enum class RenderFilter : int
{
none,
grayscale,
protanopia,
deuteranopia,
tritanopia
};
class MiracleConfig
{
public:
@ -158,6 +167,7 @@ public:
virtual int register_listener(std::function<void(miracle::MiracleConfig&)> const&, int priority) = 0;
virtual void unregister_listener(int handle) = 0;
virtual void try_process_change() = 0;
virtual uint get_primary_modifier() const = 0;
};
class FilesystemConfiguration : public MiracleConfig
@ -186,6 +196,7 @@ public:
int register_listener(std::function<void(miracle::MiracleConfig&)> const&, int priority) override;
void unregister_listener(int handle) override;
void try_process_change() override;
[[nodiscard]] uint get_primary_modifier() const override;
private:
struct ChangeListener

View File

@ -21,7 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "output.h"
#include "animator.h"
#include "compositor_state.h"
#include "floating_container.h"
#include "floating_window_container.h"
#include "leaf_container.h"
#include "vector_helpers.h"
#include "window_helpers.h"
@ -71,45 +71,37 @@ std::shared_ptr<Workspace> const& Output::get_active_workspace() const
throw std::runtime_error("get_active_workspace: unable to find the active workspace. We shouldn't be here!");
}
bool Output::handle_pointer_event(const MirPointerEvent* event)
std::shared_ptr<Container> Output::intersect(const MirPointerEvent* event)
{
auto x = miral::toolkit::mir_pointer_event_axis_value(event, MirPointerAxis::mir_pointer_axis_x);
auto y = miral::toolkit::mir_pointer_event_axis_value(event, MirPointerAxis::mir_pointer_axis_y);
if (get_active_workspace_num() < 0)
return false;
if (select_window_from_point(static_cast<int>(x), static_cast<int>(y)))
return true;
auto const action = mir_pointer_event_action(event);
if (has_clicked_floating_window || get_active_workspace()->has_floating_window(state.active))
{
if (action == mir_pointer_action_button_down)
has_clicked_floating_window = true;
else if (action == mir_pointer_action_button_up)
has_clicked_floating_window = false;
return floating_window_manager->handle_pointer_event(event);
mir::log_error("Output::handle_pointer_event: unexpectedly trying to handle a pointer event when we lack workspaces");
return nullptr;
}
return false;
return get_active_workspace()->select_from_point(x, y);
}
ContainerType Output::allocate_position(
AllocationHint Output::allocate_position(
miral::ApplicationInfo const& app_info,
miral::WindowSpecification& requested_specification,
ContainerType hint)
AllocationHint hint)
{
auto ideal_type = hint == ContainerType::none ? window_helpers::get_ideal_type(requested_specification) : hint;
if (ideal_type == ContainerType::shell)
return ContainerType::shell;
hint.container_type = hint.container_type == ContainerType::none
? window_helpers::get_ideal_type(requested_specification)
: hint.container_type;
if (hint.container_type == ContainerType::shell)
return hint;
return get_active_workspace()->allocate_position(app_info, requested_specification, ideal_type);
return get_active_workspace()->allocate_position(app_info, requested_specification, hint);
}
std::shared_ptr<Container> Output::create_container(
miral::WindowInfo const& window_info, ContainerType type) const
miral::WindowInfo const& window_info, AllocationHint const& hint) const
{
return get_active_workspace()->create_container(window_info, type);
return get_active_workspace()->create_container(window_info, hint);
}
void Output::delete_container(std::shared_ptr<miracle::Container> const &container)
@ -118,10 +110,6 @@ void Output::delete_container(std::shared_ptr<miracle::Container> const &contain
workspace->delete_container(container);
}
bool Output::select_window_from_point(int x, int y) const
{
return get_active_workspace()->select_window_from_point(x, y);
}
void Output::advise_new_workspace(int workspace)
{
@ -318,7 +306,7 @@ void Output::request_toggle_active_float()
state.active->get_workspace()->toggle_floating(state.active);
}
void Output::add_immediately(miral::Window& window, ContainerType hint)
void Output::add_immediately(miral::Window& window, AllocationHint hint)
{
auto& prev_info = window_controller.info_for(window);
WindowSpecification spec = window_helpers::copy_from(prev_info);
@ -327,9 +315,9 @@ void Output::add_immediately(miral::Window& window, ContainerType hint)
if (spec.state() == mir_window_state_hidden)
spec.state() = mir_window_state_restored;
ContainerType type = allocate_position(tools.info_for(window.application()), spec, hint);
AllocationHint result = allocate_position(tools.info_for(window.application()), spec, hint);
tools.modify_window(window, spec);
auto container = create_container(window_controller.info_for(window), type);
auto container = create_container(window_controller.info_for(window), result);
container->handle_ready();
}

View File

@ -51,11 +51,14 @@ public:
Animator&);
~Output() = default;
bool handle_pointer_event(MirPointerEvent const* event);
ContainerType allocate_position(miral::ApplicationInfo const& app_info, miral::WindowSpecification& requested_specification, ContainerType hint = ContainerType::none);
[[nodiscard]] std::shared_ptr<Container> create_container(miral::WindowInfo const& window_info, ContainerType type) const;
std::shared_ptr<Container> intersect(MirPointerEvent const* event);
AllocationHint allocate_position(
miral::ApplicationInfo const& app_info,
miral::WindowSpecification& requested_specification,
AllocationHint hint = AllocationHint());
[[nodiscard]] std::shared_ptr<Container> create_container(
miral::WindowInfo const& window_info, AllocationHint const& hint) const;
void delete_container(std::shared_ptr<miracle::Container> const &container);
[[nodiscard]] bool select_window_from_point(int x, int y) const;
void advise_new_workspace(int workspace);
void advise_workspace_deleted(int workspace);
bool advise_workspace_active(int workspace);
@ -71,7 +74,7 @@ public:
/// with the provided type. This is a deviation away from the typical
/// window-adding flow where you first call 'place_new_window' followed
/// by 'create_container'.
void add_immediately(miral::Window& window, ContainerType hint = ContainerType::none);
void add_immediately(miral::Window& window, AllocationHint hint = AllocationHint());
/// Takes an existing [Container] object and places it in an appropriate position
/// on the active [Workspace].
@ -110,7 +113,6 @@ private:
std::vector<miral::Zone> application_zone_list;
bool is_active_ = false;
AnimationHandle handle;
bool has_clicked_floating_window = false;
/// The position of the output for scrolling across workspaces
glm::vec2 position_offset = glm::vec2(0.f);

View File

@ -612,13 +612,16 @@ ContainerType ParentContainer::get_type() const
return ContainerType::parent;
}
void ParentContainer::restore_state(MirWindowState state)
void ParentContainer::show()
{
for (auto const& c : sub_nodes)
c->show();
}
std::optional<MirWindowState> ParentContainer::restore_state()
void ParentContainer::hide()
{
return std::nullopt;
for (auto const& c : sub_nodes)
c->hide();
}
void ParentContainer::on_open()

View File

@ -85,8 +85,8 @@ public:
mir::geometry::Rectangle
confirm_placement(MirWindowState state, mir::geometry::Rectangle const& rectangle) override;
ContainerType get_type() const override;
void restore_state(MirWindowState state) override;
std::optional<MirWindowState> restore_state() override;
void show() override;
void hide() override;
void on_open() override;
Workspace* get_workspace() const override;
Output* get_output() const override;

View File

@ -20,9 +20,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "policy.h"
#include "miracle_config.h"
#include "shell_component_container.h"
#include "window_helpers.h"
#include "window_tools_accessor.h"
#include "workspace_manager.h"
#include "container_group_container.h"
#include "feature_flags.h"
#include <iostream>
#include <mir/geometry/rectangle.h>
@ -48,8 +49,10 @@ Policy::Policy(
miral::MirRunner& runner,
std::shared_ptr<MiracleConfig> const& config,
SurfaceTracker& surface_tracker,
mir::Server const& server) :
mir::Server const& server,
CompositorState& compositor_state) :
window_manager_tools { tools },
state{compositor_state},
floating_window_manager(std::make_shared<miral::MinimalWindowManager>(tools, config->get_input_event_modifier())),
external_client_launcher { external_client_launcher },
runner { runner },
@ -82,6 +85,7 @@ bool Policy::handle_keyboard_event(MirKeyboardEvent const* event)
auto const action = miral::toolkit::mir_keyboard_event_action(event);
auto const scan_code = miral::toolkit::mir_keyboard_event_scan_code(event);
auto const modifiers = miral::toolkit::mir_keyboard_event_modifiers(event) & MODIFIER_MASK;
state.modifiers = modifiers;
auto custom_key_command = config->matches_custom_key_command(action, scan_code, modifiers);
if (custom_key_command != nullptr)
@ -197,6 +201,7 @@ bool Policy::handle_pointer_event(MirPointerEvent const* event)
{
auto x = miral::toolkit::mir_pointer_event_axis_value(event, MirPointerAxis::mir_pointer_axis_x);
auto y = miral::toolkit::mir_pointer_event_axis_value(event, MirPointerAxis::mir_pointer_axis_y);
auto action = miral::toolkit::mir_pointer_event_action(event);
state.cursor_position = { x, y };
// Select the output first
@ -204,20 +209,83 @@ bool Policy::handle_pointer_event(MirPointerEvent const* event)
{
if (output->point_is_in_output(static_cast<int>(x), static_cast<int>(y)))
{
if (active_output != output)
if (state.active_output != output)
{
if (active_output)
active_output->set_is_active(false);
active_output = output;
active_output->set_is_active(true);
if (state.active_output)
state.active_output->set_is_active(false);
state.active_output = output;
state.active_output->set_is_active(true);
workspace_manager.request_focus(output->get_active_workspace_num());
}
break;
}
}
if (active_output && state.mode != WindowManagerMode::resizing)
return active_output->handle_pointer_event(event);
if (state.active_output && state.mode != WindowManagerMode::resizing)
{
if (MIRACLE_FEATURE_FLAG_MULTI_SELECT && action == mir_pointer_action_button_down)
{
if (state.modifiers == config->get_primary_modifier())
{
// We clicked while holding the modifier, so we're probably in the middle of a multi-selection.
if (state.mode != WindowManagerMode::selecting)
{
state.mode = WindowManagerMode::selecting;
group_selection = std::make_shared<ContainerGroupContainer>(state);
state.active = group_selection;
mode_observer_registrar.advise_changed(state.mode);
}
}
else if (state.mode == WindowManagerMode::selecting)
{
// We clicked while we were in selection mode, so let's stop being in selection mode
// TODO: Would it be better to check what we clicked in case it's in the group? Then we wouldn't
// exit selection mode in this case.
state.mode = WindowManagerMode::normal;
mode_observer_registrar.advise_changed(state.mode);
}
}
// Get Container intersection. Depending on the state, do something with that Container
std::shared_ptr<Container> intersected = state.active_output->intersect(event);
switch (state.mode)
{
case WindowManagerMode::normal:
{
bool has_changed_selected = false;
if (intersected)
{
if (auto window = intersected->window().value())
{
if (state.active != intersected)
{
window_controller.select_active_window(window);
has_changed_selected = true;
}
}
}
if (state.has_clicked_floating_window || state.active->get_type() == ContainerType::floating_window)
{
if (action == mir_pointer_action_button_down)
state.has_clicked_floating_window = true;
else if (action == mir_pointer_action_button_up)
state.has_clicked_floating_window = false;
return floating_window_manager->handle_pointer_event(event);
}
return has_changed_selected;
}
case WindowManagerMode::selecting:
{
if (intersected && action == mir_pointer_action_button_down)
group_selection->add(intersected);
return true;
}
default:
return false;
}
}
return false;
}
@ -226,15 +294,15 @@ auto Policy::place_new_window(
const miral::ApplicationInfo& app_info,
const miral::WindowSpecification& requested_specification) -> miral::WindowSpecification
{
if (!active_output)
if (!state.active_output)
{
mir::log_warning("place_new_window: no output available");
return requested_specification;
}
auto new_spec = requested_specification;
pending_output = active_output;
pending_type = active_output->allocate_position(app_info, new_spec);
pending_output = state.active_output;
pending_allocation = state.active_output->allocate_position(app_info, new_spec, {});
return new_spec;
}
@ -262,12 +330,12 @@ void Policy::advise_new_window(miral::WindowInfo const& window_info)
return;
}
auto container = shared_output->create_container(window_info, pending_type);
auto container = shared_output->create_container(window_info, pending_allocation);
container->animation_handle(animator.register_animateable());
container->on_open();
pending_type = ContainerType::none;
pending_allocation.container_type = ContainerType::none;
pending_output.reset();
surface_tracker.add(window_info.window());
@ -294,8 +362,19 @@ void Policy::advise_focus_gained(const miral::WindowInfo& window_info)
return;
}
state.active = container;
container->on_focus_gained();
switch (state.mode)
{
case WindowManagerMode::selecting:
group_selection->add(container);
container->on_focus_gained();
break;
default:
{
state.active = container;
container->on_focus_gained();
break;
}
}
}
void Policy::advise_focus_lost(const miral::WindowInfo& window_info)
@ -359,8 +438,8 @@ void Policy::advise_output_create(miral::Output const& output)
floating_window_manager, state, config, window_controller, animator);
workspace_manager.request_first_available_workspace(output_content);
output_list.push_back(output_content);
if (active_output == nullptr)
active_output = output_content;
if (state.active_output == nullptr)
state.active_output = output_content;
// Let's rehome some orphan windows if we need to
if (!orphaned_window_list.empty())
@ -368,7 +447,7 @@ void Policy::advise_output_create(miral::Output const& output)
mir::log_info("Policy::advise_output_create: orphaned windows are being added to the new output, num=%zu", orphaned_window_list.size());
for (auto& window : orphaned_window_list)
{
active_output->add_immediately(window);
state.active_output->add_immediately(window);
}
orphaned_window_list.clear();
}
@ -418,14 +497,14 @@ void Policy::advise_output_delete(miral::Output const& output)
remove_workspaces();
mir::log_info("Policy::advise_output_delete: final output has been removed and windows have been orphaned");
active_output = nullptr;
state.active_output = nullptr;
}
else
{
active_output = output_list.front();
state.active_output = output_list.front();
for (auto& window : other_output->collect_all_windows())
{
active_output->add_immediately(window);
state.active_output->add_immediately(window);
}
remove_workspaces();
@ -542,20 +621,13 @@ void Policy::advise_application_zone_delete(miral::Zone const& application_zone)
void Policy::try_toggle_resize_mode()
{
if (!active_output)
if (!state.active)
{
state.mode = WindowManagerMode::normal;
return;
}
auto container = state.active;
if (!container)
{
state.mode = WindowManagerMode::normal;
return;
}
if (container->get_type() != ContainerType::leaf)
if (state.active->get_type() != ContainerType::leaf)
{
state.mode = WindowManagerMode::normal;
return;
@ -662,9 +734,6 @@ bool Policy::try_select(miracle::Direction direction)
bool Policy::try_close_window()
{
if (!active_output)
return false;
if (!state.active)
return false;
@ -698,10 +767,10 @@ bool Policy::select_workspace(int number)
if (state.mode == WindowManagerMode::resizing)
return false;
if (!active_output)
if (!state.active_output)
return false;
workspace_manager.request_workspace(active_output, number);
workspace_manager.request_workspace(state.active_output, number);
return true;
}
@ -710,17 +779,17 @@ bool Policy::move_active_to_workspace(int number)
if (state.mode == WindowManagerMode::resizing)
return false;
if (!active_output || !state.active)
if (!state.active)
return false;
if (state.active->is_fullscreen())
return false;
auto to_move = state.active;
active_output->delete_container(state.active);
state.active->get_output()->delete_container(state.active);
state.active = nullptr;
auto screen_to_move_to = workspace_manager.request_workspace(active_output, number);
auto screen_to_move_to = workspace_manager.request_workspace(state.active_output, number);
screen_to_move_to->graft(to_move);
return true;
}
@ -730,10 +799,10 @@ bool Policy::toggle_floating()
if (state.mode == WindowManagerMode::resizing)
return false;
if (!active_output)
if (!state.active_output)
return false;
active_output->request_toggle_active_float();
state.active_output->request_toggle_active_float();
return true;
}

View File

@ -48,6 +48,7 @@ namespace miracle
{
class Container;
class ContainerGroupContainer;
class Policy : public miral::WindowManagementPolicy
{
@ -58,7 +59,8 @@ public:
miral::MirRunner&,
std::shared_ptr<MiracleConfig> const&,
SurfaceTracker&,
mir::Server const&);
mir::Server const&,
CompositorState&);
~Policy() override;
// Interactions with the engine
@ -118,16 +120,16 @@ public:
// Getters
std::shared_ptr<Output> const& get_active_output() { return active_output; }
std::vector<std::shared_ptr<Output>> const& get_output_list() { return output_list; }
[[nodiscard]] std::shared_ptr<Output> const& get_active_output() const { return state.active_output; }
[[nodiscard]] std::vector<std::shared_ptr<Output>> const& get_output_list() const { return output_list; }
[[nodiscard]] geom::Point const& get_cursor_position() const { return state.cursor_position; }
[[nodiscard]] CompositorState const& get_state() const { return state; }
private:
std::shared_ptr<Output> active_output;
CompositorState& state;
std::vector<std::shared_ptr<Output>> output_list;
std::weak_ptr<Output> pending_output;
ContainerType pending_type;
AllocationHint pending_allocation;
std::vector<Window> orphaned_window_list;
miral::WindowManagerTools window_manager_tools;
std::shared_ptr<miral::MinimalWindowManager> floating_window_manager;
@ -142,7 +144,7 @@ private:
WindowManagerToolsWindowController window_controller;
I3CommandExecutor i3_command_executor;
SurfaceTracker& surface_tracker;
CompositorState state;
std::shared_ptr<ContainerGroupContainer> group_selection;
};
}

View File

@ -44,6 +44,21 @@ void main() {
v_texcoord = texcoord;
}
)";
const GLchar* const mode_scale_integration = R"(
uniform int mode;
vec4 resolve_color(vec4 v) {
if (mode == 1) {
float color = 0.299 * v.x + 0.587 * v.y + 0.114 * v.z;
return vec4(color, color, color, v.w);
}
else {
return v;
}
}
)";
}
miracle::ProgramData::ProgramData(GLuint program_id)
@ -87,6 +102,10 @@ miracle::ProgramData::ProgramData(GLuint program_id)
if (alpha_uniform < 0)
mir::log_warning("Program is missing alpha_uniform");
mode_uniform = glGetUniformLocation(id, "mode");
if (mode_uniform < 0)
mir::log_warning("Program is missing mode_uniform");
outline_color_uniform = glGetUniformLocation(id, "outline_color");
if (outline_color_uniform < 0)
mir::log_warning("Program is missing outline_color_uniform");
@ -135,9 +154,10 @@ mir::graphics::gl::Program& miracle::ProgramFactory::compile_fragment_shader(
<< "\n"
<< fragment_fragment
<< "\n"
<< mode_scale_integration
<< "varying vec2 v_texcoord;\n"
"void main() {\n"
" gl_FragColor = sample_to_rgba(v_texcoord);\n"
" gl_FragColor = resolve_color(sample_to_rgba(v_texcoord));\n"
"}\n";
std::stringstream alpha_fragment;
@ -150,23 +170,26 @@ mir::graphics::gl::Program& miracle::ProgramFactory::compile_fragment_shader(
<< "\n"
<< fragment_fragment
<< "\n"
<< mode_scale_integration
<< "varying vec2 v_texcoord;\n"
"uniform float alpha;\n"
"void main() {\n"
" gl_FragColor = alpha * sample_to_rgba(v_texcoord);\n"
" gl_FragColor = alpha * resolve_color(sample_to_rgba(v_texcoord));\n"
"}\n";
const GLchar* const outline_shader_src = R"(
#ifdef GL_ES
precision mediump float;
#endif
uniform float alpha;
uniform vec4 outline_color;
void main() {
gl_FragColor = alpha * outline_color;
}
)";
std::stringstream outline_shader_src;
outline_shader_src
<< "\n"
<< "#ifdef GL_ES\n"
"precision mediump float;\n"
"#endif\n"
<< "\n"
<< mode_scale_integration
<< "uniform float alpha;\n"
<< "uniform vec4 outline_color;\n"
<< "void main() {\n"
<< " gl_FragColor = alpha * resolve_color(outline_color);\n"
<< "}\n";
// GL shader compilation is *not* threadsafe, and requires external synchronisation
std::lock_guard lock { compilation_mutex };
@ -178,7 +201,7 @@ void main() {
compile_shader(GL_FRAGMENT_SHADER, alpha_fragment.str().c_str())
};
ShaderHandle const outline_shader {
compile_shader(GL_FRAGMENT_SHADER, outline_shader_src)
compile_shader(GL_FRAGMENT_SHADER, outline_shader_src.str().c_str())
};
programs.emplace_back(id, std::make_unique<miracle::Program>(link_shader(vertex_shader, opaque_shader), link_shader(vertex_shader, alpha_shader), link_shader(vertex_shader, outline_shader)));

View File

@ -81,6 +81,7 @@ struct ProgramData
GLint transform_uniform = -1;
GLint screen_to_gl_coords_uniform = -1;
GLint alpha_uniform = -1;
GLint mode_uniform = -1;
GLint outline_color_uniform = -1;
mutable long long last_used_frameno = 0;

View File

@ -22,6 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "miracle_config.h"
#include "program_factory.h"
#include "tessellation_helpers.h"
#include "compositor_state.h"
#include "container.h"
#include "window_tools_accessor.h"
@ -132,7 +133,8 @@ Renderer::Renderer(
std::shared_ptr<mir::graphics::GLRenderingProvider> gl_interface,
std::unique_ptr<mir::graphics::gl::OutputSurface> output,
std::shared_ptr<MiracleConfig> const& config,
SurfaceTracker& surface_tracker) :
SurfaceTracker& surface_tracker,
CompositorState const& compositor_state) :
output_surface { make_output_current(std::move(output)) },
clear_color { 0.0f, 0.0f, 0.0f, 1.0f },
program_factory { std::make_unique<ProgramFactory>() },
@ -140,7 +142,8 @@ Renderer::Renderer(
screen_to_gl_coords(1),
gl_interface { std::move(gl_interface) },
config { config },
surface_tracker { surface_tracker }
surface_tracker { surface_tracker },
compositor_state { compositor_state }
{
// http://directx.com/2014/06/egl-understanding-eglchooseconfig-then-ignoring-it/
eglBindAPI(EGL_OPENGL_ES_API);
@ -220,7 +223,7 @@ Renderer::DrawData Renderer::get_draw_data(mir::graphics::Renderable const& rend
auto tools = WindowToolsAccessor::get_instance().get_tools();
auto& info = tools.info_for(window);
auto userdata = static_pointer_cast<Container>(info.userdata());
data.needs_outline = (userdata->get_type() == ContainerType::leaf || userdata->get_type() == ContainerType::floating)
data.needs_outline = (userdata->get_type() == ContainerType::leaf || userdata->get_type() == ContainerType::floating_window)
&& !info.parent();
data.workspace_transform = userdata->get_output_transform() * userdata->get_workspace_transform();
data.is_focused = userdata->is_focused();
@ -366,6 +369,16 @@ miracle::Renderer::DrawData Renderer::draw(
if (prog->alpha_uniform >= 0)
glUniform1f(prog->alpha_uniform, renderable.alpha());
switch (compositor_state.mode)
{
case WindowManagerMode::selecting:
glUniform1i(prog->mode_uniform, (int)(data.is_focused ? RenderFilter::none : RenderFilter::grayscale));
break;
default:
glUniform1i(prog->mode_uniform, (int)RenderFilter::none);
break;
}
glUniformMatrix4fv(prog->workspace_transform_uniform, 1, GL_FALSE,
glm::value_ptr(data.workspace_transform));
@ -477,7 +490,7 @@ miracle::Renderer::DrawData Renderer::draw(
true,
false,
data.workspace_transform,
false,
data.is_focused,
{ true,
color,
border_config.size }

View File

@ -47,6 +47,7 @@ namespace graphics::gl
namespace miracle
{
class MiracleConfig;
class CompositorState;
class Renderer : public mir::renderer::Renderer
{
@ -54,7 +55,8 @@ public:
Renderer(std::shared_ptr<mir::graphics::GLRenderingProvider> gl_interface,
std::unique_ptr<mir::graphics::gl::OutputSurface> output,
std::shared_ptr<MiracleConfig> const& config,
SurfaceTracker& surface_tracker);
SurfaceTracker& surface_tracker,
CompositorState const& compositor_state);
~Renderer() override = default;
// These are called with a valid GL context:
@ -117,6 +119,7 @@ private:
std::shared_ptr<mir::graphics::GLRenderingProvider> const gl_interface;
std::shared_ptr<MiracleConfig> config;
SurfaceTracker& surface_tracker;
CompositorState const& compositor_state;
};
}

View File

@ -29,13 +29,12 @@ ShellComponentContainer::ShellComponentContainer(
{
}
void ShellComponentContainer::restore_state(MirWindowState state)
void ShellComponentContainer::show()
{
}
std::optional<MirWindowState> ShellComponentContainer::restore_state()
void ShellComponentContainer::hide()
{
return std::nullopt;
}
void ShellComponentContainer::commit_changes()

View File

@ -33,8 +33,8 @@ public:
std::weak_ptr<ParentContainer> get_parent() const override;
void restore_state(MirWindowState state) override;
std::optional<MirWindowState> restore_state() override;
void show() override;
void hide() override;
void commit_changes() override;
mir::geometry::Rectangle get_logical_area() const override;
void set_logical_area(mir::geometry::Rectangle const& rectangle) override;

View File

@ -36,10 +36,11 @@ TilingWindowTree::TilingWindowTree(
std::unique_ptr<TilingWindowTreeInterface> tree_interface,
WindowController& window_controller,
CompositorState const& state,
std::shared_ptr<MiracleConfig> const& config) :
std::shared_ptr<MiracleConfig> const& config,
geom::Rectangle const& area) :
root_lane { std::make_shared<ParentContainer>(
window_controller,
tree_interface->get_area(),
area,
config,
this,
nullptr,
@ -154,6 +155,11 @@ void TilingWindowTree::set_area(geom::Rectangle const& new_area)
root_lane->commit_changes();
}
geom::Rectangle TilingWindowTree::get_area() const
{
return root_lane->get_logical_area();
}
std::shared_ptr<LeafContainer> TilingWindowTree::select_window_from_point(int x, int y)
{
if (is_active_window_fullscreen)
@ -719,15 +725,7 @@ void TilingWindowTree::hide()
}
is_hidden = true;
foreach_node([&](auto node)
{
auto leaf_node = Container::as_leaf(node);
if (leaf_node)
{
leaf_node->hide();
leaf_node->commit_changes();
}
});
root_lane->hide();
}
std::shared_ptr<LeafContainer> TilingWindowTree::show()
@ -738,24 +736,18 @@ std::shared_ptr<LeafContainer> TilingWindowTree::show()
return nullptr;
}
root_lane->show();
is_hidden = false;
std::shared_ptr<LeafContainer> fullscreen_node = nullptr;
foreach_node([&](auto node)
{
auto leaf_node = Container::as_leaf(node);
if (leaf_node)
{
leaf_node->show();
leaf_node->commit_changes();
if (leaf_node->is_fullscreen())
fullscreen_node = leaf_node;
else
window_controller.raise(leaf_node->window().value());
}
// TODO: This check is probably unnecessary
std::shared_ptr<Container> fullscreen_node = nullptr;
foreach_node([&](auto const& container)
{
if (container->is_fullscreen())
fullscreen_node = container;
});
return fullscreen_node;
return Container::as_leaf(fullscreen_node);
}
bool TilingWindowTree::is_empty()

View File

@ -43,7 +43,6 @@ class Workspace;
class TilingWindowTreeInterface
{
public:
virtual geom::Rectangle const& get_area() = 0;
virtual std::vector<miral::Zone> const& get_zones() = 0;
virtual Workspace* get_workspace() const = 0;
};
@ -55,7 +54,8 @@ public:
std::unique_ptr<TilingWindowTreeInterface> tree_interface,
WindowController&,
CompositorState const&,
std::shared_ptr<MiracleConfig> const& options);
std::shared_ptr<MiracleConfig> const& options,
geom::Rectangle const& area);
~TilingWindowTree();
/// Place a window in the specified container if one is provided.
@ -103,6 +103,8 @@ public:
/// Called when the physical display is resized.
void set_area(geom::Rectangle const& new_area);
geom::Rectangle get_area() const;
std::shared_ptr<LeafContainer> select_window_from_point(int x, int y);
bool advise_fullscreen_container(LeafContainer&);

View File

@ -25,9 +25,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "tiling_window_tree.h"
#include "window_helpers.h"
#include "parent_container.h"
#include "floating_container.h"
#include "floating_window_container.h"
#include "floating_tree_container.h"
#include "shell_component_container.h"
#include "container_group_container.h"
#include <mir/log.h>
#include <mir/scene/surface.h>
#include <miral/zone.h>
@ -45,11 +47,6 @@ public:
{
}
geom::Rectangle const& get_area() override
{
return screen->get_area();
}
std::vector<miral::Zone> const& get_zones() override
{
return screen->get_app_zones();
@ -84,7 +81,7 @@ Workspace::Workspace(
floating_window_manager { floating_window_manager },
tree(std::make_shared<TilingWindowTree>(
std::make_unique<OutputTilingWindowTreeInterface>(output, this),
window_controller, state, config))
window_controller, state, config, output->get_area()))
{
}
@ -103,45 +100,47 @@ void Workspace::recalculate_area()
tree->recalculate_root_node_area();
}
ContainerType Workspace::allocate_position(
AllocationHint Workspace::allocate_position(
miral::ApplicationInfo const& app_info,
miral::WindowSpecification& requested_specification,
ContainerType hint)
AllocationHint const& hint)
{
// If there's no ideal layout type, use the one provided by the workspace
auto layout = hint == ContainerType::none
auto layout = hint.container_type == ContainerType::none
? config->get_workspace_config(workspace).layout
: hint;
: hint.container_type;
switch (layout)
{
case ContainerType::leaf:
{
requested_specification = tree->place_new_window(requested_specification, get_layout_container());
return ContainerType::leaf;
auto placement_tree = hint.placement_tree ? hint.placement_tree : tree.get();
requested_specification = placement_tree->place_new_window(requested_specification, get_layout_container());
return { ContainerType::leaf, placement_tree };
}
case ContainerType::floating:
case ContainerType::floating_window:
{
requested_specification = floating_window_manager->place_new_window(app_info, requested_specification);
requested_specification.server_side_decorated() = false;
return ContainerType::floating;
return { ContainerType::floating_window };
}
default:
return layout;
return { layout };
}
}
std::shared_ptr<Container> Workspace::create_container(
miral::WindowInfo const& window_info, ContainerType type)
miral::WindowInfo const& window_info,
AllocationHint const& hint)
{
std::shared_ptr<Container> container = nullptr;
switch (type)
switch (hint.container_type)
{
case ContainerType::leaf:
{
container = tree->confirm_window(window_info, get_layout_container());
container = hint.placement_tree->confirm_window(window_info, get_layout_container());
break;
}
case ContainerType::floating:
case ContainerType::floating_window:
{
floating_window_manager->advise_new_window(window_info);
container = add_floating_window(window_info.window());
@ -156,7 +155,7 @@ std::shared_ptr<Container> Workspace::create_container(
container = std::make_shared<ShellComponentContainer>(window_info.window(), window_controller);
break;
default:
mir::log_error("Unsupported window type: %d", (int)type);
mir::log_error("Unsupported window type: %d", (int)hint.container_type);
break;
}
@ -168,9 +167,9 @@ std::shared_ptr<Container> Workspace::create_container(
// TODO: hack
// Warning: We need to advise fullscreen only after we've associated the userdata() appropriately
if (type == ContainerType::leaf && window_helpers::is_window_fullscreen(window_info.state()))
if (hint.container_type == ContainerType::leaf && window_helpers::is_window_fullscreen(window_info.state()))
{
tree->advise_fullscreen_container(*Container::as_leaf(container));
hint.placement_tree->advise_fullscreen_container(*Container::as_leaf(container));
}
return container;
}
@ -193,10 +192,11 @@ void Workspace::delete_container(std::shared_ptr<Container> const &container)
{
case ContainerType::leaf:
{
// TODO: Get the tree for this container
tree->advise_delete_window(container);
break;
}
case ContainerType::floating:
case ContainerType::floating_window:
{
auto floating = Container::as_floating(container);
floating_window_manager->advise_delete_window(window_controller.info_for(floating->window().value()));
@ -214,29 +214,7 @@ void Workspace::show()
{
auto fullscreen_node = tree->show();
for (auto const& floating : floating_windows)
{
// Pinned windows don't require restoration
if (floating->pinned())
{
tools.raise_tree(floating->window().value());
continue;
}
auto container = window_controller.get_container(floating->window().value());
if (!container)
{
mir::log_error("show: floating window lacks container");
continue;
}
if (auto restore_state = container->restore_state())
{
miral::WindowSpecification spec;
spec.state() = restore_state.value();
tools.modify_window(floating->window().value(), spec);
tools.raise_tree(floating->window().value());
}
}
floating->show();
// TODO: ugh that's ugly. Fullscreen nodes should show above floating nodes
if (fullscreen_node)
@ -246,6 +224,14 @@ void Workspace::show()
}
}
void Workspace::hide()
{
tree->hide();
for (auto const& floating : floating_windows)
floating->hide();
}
void Workspace::for_each_window(std::function<void(std::shared_ptr<Container>)> const& f)
{
for (auto const& window : floating_windows)
@ -266,113 +252,116 @@ void Workspace::for_each_window(std::function<void(std::shared_ptr<Container>)>
});
}
bool Workspace::select_window_from_point(int x, int y)
std::shared_ptr<Container> Workspace::select_from_point(int x, int y)
{
if (tree->has_fullscreen_window())
return false;
for (auto const& floating : floating_windows)
{
auto window = floating->window().value();
geom::Rectangle window_area(window.top_left(), window.size());
if (floating == state.active && window_area.contains(geom::Point(x, y)))
return false;
else if (window_area.contains(geom::Point(x, y)))
{
window_controller.select_active_window(window);
return true;
}
if (window_area.contains(geom::Point(x, y)))
return floating;
}
auto node = tree->select_window_from_point(x, y);
if (node && node != state.active)
{
window_controller.select_active_window(node->window().value());
return true;
}
return false;
return tree->select_window_from_point(x, y);
}
void Workspace::toggle_floating(std::shared_ptr<Container> const& container)
{
ContainerType new_type = ContainerType::none;
auto window = container->window();
if (!window)
return;
auto const handle_ready = [&](
miral::Window const& window,
AllocationHint const& result)
{
auto& info = window_controller.info_for(window);
auto new_container = create_container(info, result);
new_container->handle_ready();
window_controller.select_active_window(state.active->window().value());
};
switch (container->get_type())
{
case ContainerType::leaf:
{
if (tree->has_fullscreen_window())
{
mir::log_warning("request_toggle_active_float: cannot float fullscreen window");
auto window = container->window();
if (!window)
return;
}
// First, remove the window from the tiling window tree
// First, remove the container
delete_container(window_controller.get_container(*window));
// Next, ask the floating window manager to place the new window
// Next, place the new container
auto& prev_info = window_controller.info_for(*window);
auto spec = window_helpers::copy_from(prev_info);
spec.top_left() = geom::Point { window->top_left().x.as_int() + 20, window->top_left().y.as_int() + 20 };
window_controller.noclip(*window);
auto new_spec = floating_window_manager->place_new_window(
tools.info_for(window->application()),
spec);
tools.modify_window(*window, new_spec);
auto result = allocate_position(tools.info_for(window->application()), spec, { ContainerType::floating_window });
window_controller.modify(*window, spec);
new_type = ContainerType::floating;
// Finally, declare it ready
handle_ready(*window, result);
break;
}
case ContainerType::floating:
case ContainerType::floating_window:
{
// First, remove the floating window
auto window = container->window();
if (!window)
return;
// First, remove the container
delete_container(window_controller.get_container(*window));
// Next, ask the tiling tree to place the new window
// Next, place the container
auto& prev_info = window_controller.info_for(*window);
miral::WindowSpecification spec = window_helpers::copy_from(prev_info);
auto new_spec = tree->place_new_window(spec, nullptr);
tools.modify_window(*window, new_spec);
auto result = allocate_position(tools.info_for(window->application()), spec, { ContainerType::leaf, tree.get() });
window_controller.modify(*window, spec);
new_type = ContainerType::leaf;
// Finally, declare it ready
handle_ready(*window, result);
break;
}
case ContainerType::group:
{
auto group = Container::as_group(container);
auto tree_container = std::make_shared<FloatingTreeContainer>(
this,
window_controller,
state,
config
);
// Delete all containers in the group and add them to the new tree
for (auto const& c : group->get_containers())
{
if (auto s = c.lock())
{
delete_container(s);
auto window = s->window();
if (!window)
continue;
auto& prev_info = window_controller.info_for(*window);
miral::WindowSpecification spec = window_helpers::copy_from(prev_info);
auto result = allocate_position(tools.info_for(window->application()), spec, { ContainerType::leaf, tree_container->get_tree() });
window_controller.modify(*window, spec);
handle_ready(*window, result);
}
}
floating_trees.push_back(tree_container);
break;
}
case ContainerType::floating_tree:
{
// TODO: !
break;
}
default:
mir::log_warning("toggle_floating: has no effect on window of type: %d", (int)container->get_type());
return;
}
// In all cases, advise a new window and pretend like it is ready again
auto& info = window_controller.info_for(*window);
auto new_container = create_container(info, new_type);
new_container->handle_ready();
window_controller.select_active_window(state.active->window().value());
}
void Workspace::hide()
{
tree->hide();
for (auto const& floating : floating_windows)
{
auto window = floating->window().value();
auto container = window_controller.get_container(window);
if (!container)
{
mir::log_error("hide: floating window lacks container");
continue;
}
container->restore_state(tools.info_for(window).state());
miral::WindowSpecification spec;
spec.state() = mir_window_state_hidden;
tools.modify_window(window, spec);
window_controller.send_to_back(window);
}
}
void Workspace::transfer_pinned_windows_to(std::shared_ptr<Workspace> const& other)
@ -409,9 +398,9 @@ bool Workspace::has_floating_window(std::shared_ptr<Container> const& container)
return false;
}
std::shared_ptr<FloatingContainer> Workspace::add_floating_window(miral::Window const& window)
std::shared_ptr<FloatingWindowContainer> Workspace::add_floating_window(miral::Window const& window)
{
auto floating = std::make_shared<FloatingContainer>(
auto floating = std::make_shared<FloatingWindowContainer>(
window, floating_window_manager, window_controller, this, state);
floating_windows.push_back(floating);
return floating;
@ -446,7 +435,7 @@ void Workspace::graft(std::shared_ptr<Container> const& container)
{
switch (container->get_type())
{
case ContainerType::floating:
case ContainerType::floating_window:
{
auto floating = Container::as_floating(container);
floating->set_workspace(this);

View File

@ -35,7 +35,14 @@ class TilingWindowTree;
class WindowController;
class CompositorState;
class ParentContainer;
class FloatingContainer;
class FloatingWindowContainer;
class FloatingTreeContainer;
struct AllocationHint
{
ContainerType container_type = ContainerType::none;
TilingWindowTree* placement_tree = nullptr;
};
class Workspace
{
@ -53,22 +60,22 @@ public:
void set_area(mir::geometry::Rectangle const&);
void recalculate_area();
ContainerType allocate_position(
AllocationHint allocate_position(
miral::ApplicationInfo const& app_info,
miral::WindowSpecification& requested_specification,
ContainerType hint);
AllocationHint const& hint);
std::shared_ptr<Container> create_container(
miral::WindowInfo const& window_info, ContainerType type);
miral::WindowInfo const& window_info, AllocationHint const& type);
void handle_ready_hack(LeafContainer& container);
void delete_container(std::shared_ptr<Container> const &container);
void show();
void hide();
void transfer_pinned_windows_to(std::shared_ptr<Workspace> const& other);
void for_each_window(std::function<void(std::shared_ptr<Container>)> const&);
bool select_window_from_point(int x, int y);
std::shared_ptr<Container> select_from_point(int x, int y);
void toggle_floating(std::shared_ptr<Container> const&);
bool has_floating_window(std::shared_ptr<Container> const&);
std::shared_ptr<FloatingContainer> add_floating_window(miral::Window const&);
std::shared_ptr<FloatingWindowContainer> add_floating_window(miral::Window const&);
Output* get_output();
void trigger_rerender();
[[nodiscard]] bool is_empty() const;
@ -80,7 +87,8 @@ private:
miral::WindowManagerTools tools;
std::shared_ptr<TilingWindowTree> tree;
int workspace;
std::vector<std::shared_ptr<FloatingContainer>> floating_windows;
std::vector<std::shared_ptr<FloatingWindowContainer>> floating_windows;
std::vector<std::shared_ptr<FloatingTreeContainer>> floating_trees;
WindowController& window_controller;
CompositorState const& state;
std::shared_ptr<MiracleConfig> config;

View File

@ -116,6 +116,11 @@ namespace test
void try_process_change() override { }
uint get_primary_modifier() const override
{
return 0;
}
private:
miracle::BorderConfig border_config;
std::array<AnimationDefinition, (int)AnimateableEvent::max> animations;

View File

@ -27,14 +27,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
using namespace miracle;
namespace
{
geom::Rectangle r {
geom::Point(0, 0),
geom::Size(1280, 720)
};
}
class SimpleTilingWindowTreeInterface : public TilingWindowTreeInterface
{
public:
geom::Rectangle const& get_area() override
{
return r;
}
std::vector<miral::Zone> const& get_zones() override
{
return zones;
@ -46,10 +49,6 @@ public:
}
private:
geom::Rectangle r {
geom::Point(0, 0),
geom::Size(1280, 720)
};
std::vector<miral::Zone> zones = { r };
};
@ -105,7 +104,8 @@ public:
std::make_unique<SimpleTilingWindowTreeInterface>(),
window_controller,
state,
std::make_shared<test::StubConfiguration>())
std::make_shared<test::StubConfiguration>(),
r)
{
}