231 lines
7.8 KiB
C++
231 lines
7.8 KiB
C++
/*
|
|
* 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>();
|
|
|
|
// The default policy will create a dumb window instance when requested
|
|
// from the manager.
|
|
auto platform = platform::create();
|
|
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>();
|
|
|
|
// The default policy will create a dumb window instance when requested
|
|
// from the manager.
|
|
auto platform = platform::create();
|
|
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>();
|
|
|
|
// The default policy will create a dumb window instance when requested
|
|
// from the manager.
|
|
auto platform = platform::create();
|
|
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>();
|
|
|
|
// The default policy will create a dumb window instance when requested
|
|
// from the manager.
|
|
auto platform = platform::create();
|
|
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
|