/* * Copyright (C) 2016 Simon Fels * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, 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 . * */ // Both includes need to go first as otherwise they can conflict with EGL.h // being included by the following includes. #include #include #include "anbox/application/database.h" #include "anbox/platform/base_platform.h" #include "anbox/wm/multi_window_manager.h" #include "anbox/wm/window_state.h" #include "anbox/graphics/layer_composer.h" #include "anbox/graphics/multi_window_composer_strategy.h" using namespace ::testing; namespace { class MockRenderer : public anbox::graphics::Renderer { public: MOCK_METHOD3(draw, bool(EGLNativeWindowType, const anbox::graphics::Rect&, const RenderableList&)); }; } namespace anbox { namespace graphics { TEST(LayerComposer, FindsNoSuitableWindowForLayer) { auto renderer = std::make_shared(); platform::Configuration config; // The default policy will create a dumb window instance when requested // from the manager. auto platform = platform::create(std::string(), nullptr, config); auto app_db = std::make_shared(); auto wm = std::make_shared(platform, nullptr, app_db); auto single_window = wm::WindowState{ wm::Display::Id{1}, true, graphics::Rect{0, 0, 1024, 768}, "org.anbox.test.1", wm::Task::Id{1}, wm::Stack::Id::Freeform, }; wm->apply_window_state_update({single_window}, {}); LayerComposer composer(renderer, std::make_shared(wm)); // A single renderable which has a different task id then the window we know // about RenderableList renderables = { {"org.anbox.surface.2", 0, 1.0f, {0, 0, 1024, 768}, {0, 0, 1024, 768}}, }; // The renderer should not be called for a layer which doesn't exist EXPECT_CALL(*renderer, draw(_, _, _)).Times(0); composer.submit_layers(renderables); } TEST(LayerComposer, MapsLayersToWindows) { auto renderer = std::make_shared(); platform::Configuration config; // The default policy will create a dumb window instance when requested // from the manager. auto platform = platform::create(std::string(), nullptr, config); auto app_db = std::make_shared(); auto wm = std::make_shared(platform, nullptr, app_db); auto first_window = wm::WindowState{ wm::Display::Id{1}, true, graphics::Rect{0, 0, 1024, 768}, "org.anbox.foo", wm::Task::Id{1}, wm::Stack::Id::Freeform, }; auto second_window = wm::WindowState{ wm::Display::Id{1}, true, graphics::Rect{300, 400, 1324, 1168}, "org.anbox.bar", wm::Task::Id{2}, wm::Stack::Id::Freeform, }; wm->apply_window_state_update({first_window, second_window}, {}); LayerComposer composer(renderer, std::make_shared(wm)); // A single renderable which has a different task id then the window we know // about RenderableList renderables = { {"org.anbox.surface.1", 0, 1.0f, {0, 0, 1024, 768}, {0, 0, 1024, 768}}, {"org.anbox.surface.2", 1, 1.0f, {0, 0, 1024, 768}, {0, 0, 1024, 768}}, }; RenderableList first_window_renderables{ {"org.anbox.surface.1", 0, 1.0f, {0, 0, 1024, 768}, {0, 0, 1024, 768}}, }; RenderableList second_window_renderables{ {"org.anbox.surface.2", 1, 1.0f, {0, 0, 1024, 768}, {0, 0, 1024, 768}}, }; EXPECT_CALL(*renderer, draw(_, Rect{0, 0, first_window.frame().width(), first_window.frame().height()}, first_window_renderables)) .Times(1) .WillOnce(Return(true)); EXPECT_CALL(*renderer, draw(_, Rect{0, 0, second_window.frame().width(), second_window.frame().height()}, second_window_renderables)) .Times(1) .WillOnce(Return(true)); composer.submit_layers(renderables); } TEST(LayerComposer, WindowPartiallyOffscreen) { auto renderer = std::make_shared(); platform::Configuration config; // The default policy will create a dumb window instance when requested // from the manager. auto platform = platform::create(std::string(), nullptr, config); auto app_db = std::make_shared(); auto wm = std::make_shared(platform, nullptr, app_db); auto window = wm::WindowState{ wm::Display::Id{1}, true, graphics::Rect{-100, -100, 924, 668}, "org.anbox.foo", wm::Task::Id{1}, wm::Stack::Id::Freeform, }; wm->apply_window_state_update({window}, {}); LayerComposer composer(renderer, std::make_shared(wm)); // Window is build out of two layers where one is placed inside the other // but the layer covering the whole window is placed with its top left // origin outside of the visible display area. RenderableList renderables = { {"org.anbox.surface.1", 0, 1.0f, {-100, -100, 924, 668}, {0, 0, 1024, 768}}, {"org.anbox.surface.1", 1, 1.0f, {0, 0, 100, 200}, {0, 0, 100, 200}}, }; RenderableList expected_renderables{ {"org.anbox.surface.1", 0, 1.0f, {0, 0, 1024, 768}, {0, 0, 1024, 768}}, {"org.anbox.surface.1", 1, 1.0f, {100, 100, 200, 300}, {0, 0, 100, 200}}, }; EXPECT_CALL(*renderer, draw(_, Rect{0, 0, window.frame().width(), window.frame().height()}, expected_renderables)) .Times(1) .WillOnce(Return(true)); composer.submit_layers(renderables); } TEST(LayerComposer, PopupShouldNotCauseWindowLayerOffset) { auto renderer = std::make_shared(); platform::Configuration config; // The default policy will create a dumb window instance when requested // from the manager. auto platform = platform::create(std::string(), nullptr, config); auto app_db = std::make_shared(); auto wm = std::make_shared(platform, nullptr, app_db); auto window = wm::WindowState{ wm::Display::Id{1}, true, graphics::Rect{1120, 270, 2144, 1038}, "org.anbox.foo", wm::Task::Id{3}, wm::Stack::Id::Freeform, }; wm->apply_window_state_update({window}, {}); LayerComposer composer(renderer, std::make_shared(wm)); // Having two renderables where the second smaller one overlaps the bigger // one and goes a bit offscreen. This should be still placed correctly and // the small layer should go offscreen as it is supposed to. This typically // happens when a popup window appears which has a shadow attached which goes // out of the window area. In our case this is not possible as the area the // window has available is static. RenderableList renderables = { {"org.anbox.surface.3", 0, 1.0f, {1120,270,2144,1038}, {0, 0, 1024, 768}}, {"org.anbox.surface.3", 1, 1.0f, {1904, 246, 2164, 406}, {0, 0, 260, 160}}, }; RenderableList expected_renderables{ {"org.anbox.surface.3", 0, 1.0f, {0, 0, 1024, 768}, {0, 0, 1024, 768}}, {"org.anbox.surface.3", 1, 1.0f, {784, -24, 1044, 136}, {0, 0, 260, 160}}, }; EXPECT_CALL(*renderer, draw(_, Rect{0, 0, window.frame().width(), window.frame().height()}, expected_renderables)) .Times(1) .WillOnce(Return(true)); composer.submit_layers(renderables); } } // namespace graphics } // namespace anbox