mirror of
https://github.com/miracle-wm-org/miracle-wm.git
synced 2024-11-22 19:58:51 +03:00
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:
parent
bb15d31711
commit
bd17e5b260
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
349
src/container_group_container.cpp
Normal file
349
src/container_group_container.cpp
Normal 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
|
96
src/container_group_container.h
Normal file
96
src/container_group_container.h
Normal 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
23
src/feature_flags.h
Normal 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
|
291
src/floating_tree_container.cpp
Normal file
291
src/floating_tree_container.cpp
Normal 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
|
96
src/floating_tree_container.h
Normal file
96
src/floating_tree_container.h
Normal 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
|
@ -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>();
|
||||
}
|
@ -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
|
10
src/ipc.cpp
10
src/ipc.cpp
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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()) });
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
14
src/output.h
14
src/output.h
@ -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);
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
151
src/policy.cpp
151
src/policy.cpp
@ -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;
|
||||
}
|
||||
|
||||
|
14
src/policy.h
14
src/policy.h
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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)));
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 }
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
|
@ -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&);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user