--- /dev/null
+/*
+ * Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+// Both includes need to go first as otherwise they can conflict with EGL.h
+// being included by the following includes.
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#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<MockRenderer>();
+
+ 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<application::Database>();
+ auto wm = std::make_shared<wm::MultiWindowManager>(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<MultiWindowComposerStrategy>(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<MockRenderer>();
+
+ 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<application::Database>();
+ auto wm = std::make_shared<wm::MultiWindowManager>(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<MultiWindowComposerStrategy>(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<MockRenderer>();
+
+ 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<application::Database>();
+ auto wm = std::make_shared<wm::MultiWindowManager>(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<MultiWindowComposerStrategy>(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<MockRenderer>();
+
+ 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<application::Database>();
+ auto wm = std::make_shared<wm::MultiWindowManager>(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<MultiWindowComposerStrategy>(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