/** 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 . **/ #include "tiling_window_tree.h" #include "compositor_state.h" #include "window_controller.h" #include "leaf_container.h" #include "window_metadata.h" #include "stub_configuration.h" #include "stub_session.h" #include "stub_surface.h" #include #include using namespace miracle; class SimpleTilingWindowTreeInterface : public TilingWindowTreeInterface { public: geom::Rectangle const& get_area() override { return r; } std::vector const& get_zones() override { return zones; } private: geom::Rectangle r{ geom::Point(0, 0), geom::Size(1280, 720) }; std::vector zones = {r}; }; class StubWindowController : public miracle::WindowController { public: StubWindowController(std::vector>>& pairs) : pairs{pairs} {} bool is_fullscreen(miral::Window const&) override { return false; } void set_rectangle(miral::Window const&, geom::Rectangle const&, geom::Rectangle const&) override {} MirWindowState get_state(miral::Window const&) override { return mir_window_state_restored; } void change_state(miral::Window const&, MirWindowState state) override {} void clip(miral::Window const&, geom::Rectangle const&) override {} void noclip(miral::Window const&) override {} void select_active_window(miral::Window const&) override {} std::shared_ptr get_metadata(miral::Window const& window) override { for (auto const& p : pairs) { if (p.first == window) return p.second; } return nullptr; } std::shared_ptr get_metadata(miral::Window const& window, TilingWindowTree const*) override { for (auto const& p : pairs) { if (p.first == window) return p.second; } return nullptr; } void raise(miral::Window const&) override {} void send_to_back(miral::Window const&) override {} void open(miral::Window const&) override {} void close(miral::Window const&) override {} void on_animation(miracle::AnimationStepResult const& result, std::shared_ptr const&) override {} void set_user_data(miral::Window const&, std::shared_ptr const&) override {} void modify(miral::Window const&, miral::WindowSpecification const&) override {} miral::WindowInfo& info_for(miral::Window const&) override {} private: std::vector>>& pairs; }; class TilingWindowTreeTest : public testing::Test { public: TilingWindowTreeTest() : tree( std::make_unique(), window_controller, state, std::make_shared() ) { } std::shared_ptr create_leaf() { miral::WindowSpecification spec; spec = tree.allocate_position(spec); auto session = std::make_shared(); sessions.push_back(session); auto surface = std::make_shared(); surfaces.push_back(surface); miral::Window window(session, surface); miral::WindowInfo info(window, spec); auto metadata = std::make_shared(WindowType::tiled, window); pairs.push_back({window, metadata}); info.userdata(metadata); auto leaf = tree.advise_new_window(info); metadata->associate_to_node(leaf); state.active_window = window; tree.advise_focus_gained(info.window()); return leaf; } CompositorState state; std::vector> sessions; std::vector> surfaces; std::vector>> pairs; StubWindowController window_controller{pairs}; TilingWindowTree tree; }; TEST_F(TilingWindowTreeTest, can_add_single_window_without_border_and_gaps) { auto leaf = create_leaf(); ASSERT_EQ(leaf->get_logical_area().size, geom::Size(1280, 720)); ASSERT_EQ(leaf->get_logical_area().top_left, geom::Point(0, 0)); } TEST_F(TilingWindowTreeTest, can_add_two_windows_horizontally_without_border_and_gaps) { auto leaf1 = create_leaf(); auto leaf2 = create_leaf(); ASSERT_EQ(leaf1->get_logical_area().size, geom::Size(1280 / 2.f, 720)); ASSERT_EQ(leaf1->get_logical_area().top_left, geom::Point(0, 0)); ASSERT_EQ(leaf2->get_logical_area().size, geom::Size(1280 / 2.f, 720)); ASSERT_EQ(leaf2->get_logical_area().top_left, geom::Point(1280 / 2.f, 0)); } TEST_F(TilingWindowTreeTest, can_add_two_windows_vertically_without_border_and_gaps) { auto leaf1 = create_leaf(); tree.request_vertical(); auto leaf2 = create_leaf(); ASSERT_EQ(leaf1->get_logical_area().size, geom::Size(1280, 720 / 2.f)); ASSERT_EQ(leaf1->get_logical_area().top_left, geom::Point(0, 0)); ASSERT_EQ(leaf2->get_logical_area().size, geom::Size(1280, 720 / 2.f)); ASSERT_EQ(leaf2->get_logical_area().top_left, geom::Point(0, 720/2.f)); } TEST_F(TilingWindowTreeTest, can_add_three_windows_horizontally_without_border_and_gaps) { auto leaf1 = create_leaf(); auto leaf2 = create_leaf(); auto leaf3 = create_leaf(); ASSERT_EQ(leaf1->get_logical_area().size, geom::Size(ceilf(1280 / 3.f), 720)); ASSERT_EQ(leaf1->get_logical_area().top_left, geom::Point(0, 0)); ASSERT_EQ(leaf2->get_logical_area().size, geom::Size(ceilf(1280 / 3.f), 720)); ASSERT_EQ(leaf2->get_logical_area().top_left, geom::Point(ceilf(1280 / 3.f), 0)); ASSERT_EQ(leaf3->get_logical_area().size, geom::Size(floorf(1280 / 3.f), 720)); ASSERT_EQ(leaf3->get_logical_area().top_left, geom::Point(floorf(1280 * (2.f / 3.f)) - 1, 0)); }