Implement single window mode option in parallel to multi-window support

For debugging and development purposes it's sometimes useful to run the
full Android system in a single and statically sized window. This is not
going to be a widely used mode but can be helpful at times.

Also makes our window/layer management a bit more modular to allow easy
additions of new strategies/impementations.
This commit is contained in:
Simon Fels 2017-01-27 19:48:29 +01:00
commit 9e6909c65a
25 changed files with 699 additions and 233 deletions

View file

@ -120,6 +120,8 @@ set(SOURCES
anbox/graphics/density.h
anbox/graphics/rect.cpp
anbox/graphics/layer_composer.cpp
anbox/graphics/multi_window_composer_strategy.cpp
anbox/graphics/single_window_composer_strategy.cpp
anbox/graphics/program_family.cpp
anbox/graphics/primitives.h
anbox/graphics/renderer.h
@ -149,6 +151,8 @@ set(SOURCES
anbox/wm/task.cpp
anbox/wm/stack.cpp
anbox/wm/manager.cpp
anbox/wm/single_window_manager.cpp
anbox/wm/multi_window_manager.cpp
anbox/wm/window_state.cpp
anbox/wm/window.cpp

View file

@ -41,7 +41,8 @@
#include "anbox/rpc/connection_creator.h"
#include "anbox/runtime.h"
#include "anbox/ubuntu/platform_policy.h"
#include "anbox/wm/manager.h"
#include "anbox/wm/multi_window_manager.h"
#include "anbox/wm/single_window_manager.h"
#include "external/xdg/xdg.h"
@ -96,6 +97,9 @@ anbox::cmds::SessionManager::SessionManager(const BusFactory &bus_factory)
flag(cli::make_flag(cli::Name{"gles-driver"},
cli::Description{"Which GLES driver to use. Possible values are 'host' or'translator'"},
gles_driver_));
flag(cli::make_flag(cli::Name{"single-window"},
cli::Description{"Start in single window mode."},
single_window_));
action([this](const cli::Command::Context &) {
auto trap = core::posix::trap_signals_for_process(
@ -135,19 +139,30 @@ anbox::cmds::SessionManager::SessionManager(const BusFactory &bus_factory)
auto android_api_stub = std::make_shared<bridge::AndroidApiStub>();
auto policy = std::make_shared<ubuntu::PlatformPolicy>(input_manager,
android_api_stub);
auto display_frame = graphics::Rect::Invalid;
if (single_window_)
display_frame = {0, 0, 1024, 768};
auto policy = std::make_shared<ubuntu::PlatformPolicy>(input_manager, display_frame, single_window_);
// FIXME this needs to be removed and solved differently behind the scenes
registerDisplayManager(policy);
auto app_db = std::make_shared<application::Database>();
auto window_manager = std::make_shared<wm::Manager>(policy, app_db);
std::shared_ptr<wm::Manager> window_manager;
if (single_window_)
window_manager = std::make_shared<wm::SingleWindowManager>(policy, app_mgr);
else
window_manager = std::make_shared<wm::MultiWindowManager>(policy, android_api_stub, app_mgr);
auto gl_server = std::make_shared<graphics::GLRendererServer>(
graphics::GLRendererServer::Config{gles_driver_}, window_manager);
graphics::GLRendererServer::Config{gles_driver_, single_window_}, window_manager);
policy->set_window_manager(window_manager);
policy->set_renderer(gl_server->renderer());
window_manager->setup();
auto audio_server = std::make_shared<audio::Server>(rt, policy);
const auto socket_path = SystemConfiguration::instance().socket_dir();

View file

@ -42,6 +42,7 @@ class SessionManager : public cli::CommandWithFlagsAndAction {
BusFactory bus_factory_;
std::string desktop_file_hint_;
graphics::GLRendererServer::Config::Driver gles_driver_;
bool single_window_ = false;
};
} // namespace cmds
} // namespace anbox

View file

@ -404,8 +404,7 @@ void rcPostLayer(const char *name, uint32_t color_buffer,
Renderable r{
name,
color_buffer,
{displayFrameLeft, displayFrameTop, displayFrameRight,
displayFrameBottom},
{displayFrameLeft, displayFrameTop, displayFrameRight, displayFrameBottom},
{sourceCropLeft, sourceCropTop, sourceCropRight, sourceCropBottom}};
frame_layers.push_back(r);
}

View file

@ -20,6 +20,8 @@
#include "anbox/graphics/emugl/RenderControl.h"
#include "anbox/graphics/emugl/Renderer.h"
#include "anbox/graphics/layer_composer.h"
#include "anbox/graphics/multi_window_composer_strategy.h"
#include "anbox/graphics/single_window_composer_strategy.h"
#include "anbox/logger.h"
#include "anbox/wm/manager.h"
@ -64,9 +66,15 @@ void logger_write(const emugl::LogLevel &level, const char *format, ...) {
namespace anbox {
namespace graphics {
GLRendererServer::GLRendererServer(const Config &config, const std::shared_ptr<wm::Manager> &wm)
: renderer_(std::make_shared<::Renderer>()),
wm_(wm),
composer_(std::make_shared<LayerComposer>(renderer_, wm)) {
: renderer_(std::make_shared<::Renderer>()) {
std::shared_ptr<LayerComposer::Strategy> composer_strategy;
if (config.single_window)
composer_strategy = std::make_shared<SingleWindowComposerStrategy>(wm);
else
composer_strategy = std::make_shared<MultiWindowComposerStrategy>(wm);
composer_ = std::make_shared<LayerComposer>(renderer_, composer_strategy);
auto gl_libs = emugl::default_gl_libraries(true);

View file

@ -37,6 +37,7 @@ class GLRendererServer {
struct Config {
enum class Driver { Translator, Host };
Driver driver;
bool single_window;
};
GLRendererServer(const Config &config, const std::shared_ptr<wm::Manager> &wm);

View file

@ -22,72 +22,17 @@
namespace anbox {
namespace graphics {
LayerComposer::LayerComposer(const std::shared_ptr<Renderer> renderer,
const std::shared_ptr<wm::Manager> &wm)
: renderer_(renderer), wm_(wm) {}
LayerComposer::LayerComposer(const std::shared_ptr<Renderer> renderer, const std::shared_ptr<Strategy> &strategy)
: renderer_(renderer), strategy_(strategy) {}
LayerComposer::~LayerComposer() {}
void LayerComposer::submit_layers(const RenderableList &renderables) {
std::map<std::shared_ptr<wm::Window>, RenderableList> win_layers;
for (const auto &renderable : renderables) {
// Ignore all surfaces which are not meant for a task
if (!utils::string_starts_with(renderable.name(), "org.anbox.surface."))
continue;
wm::Task::Id task_id = 0;
if (sscanf(renderable.name().c_str(), "org.anbox.surface.%d", &task_id) !=
1 ||
!task_id)
continue;
auto w = wm_->find_window_for_task(task_id);
if (!w) continue;
if (win_layers.find(w) == win_layers.end()) {
win_layers.insert({w, {renderable}});
continue;
}
win_layers[w].push_back(renderable);
}
for (const auto &w : win_layers) {
const auto &window = w.first;
const auto &renderables = w.second;
RenderableList final_renderables;
auto new_window_frame = Rect::Invalid;
auto max_layer_area = -1;
for (auto &r : renderables) {
const auto layer_area = r.screen_position().width() * r.screen_position().height();
// We always prioritize layers which are lower in the list we got
// from SurfaceFlinger as they are already ordered.
if (layer_area <= max_layer_area)
continue;
max_layer_area = layer_area;
new_window_frame = r.screen_position();
}
for (auto &r : renderables) {
// As we get absolute display coordinates from the Android hwcomposer we
// need to recalculate all layer coordinates into relatives ones to the
// window they are drawn into.
auto rect = Rect{
r.screen_position().left() - new_window_frame.left() + r.crop().left(),
r.screen_position().top() - new_window_frame.top() + r.crop().top(),
r.screen_position().right() - new_window_frame.left() + r.crop().left(),
r.screen_position().bottom() - new_window_frame.top() + r.crop().top()};
auto new_renderable = r;
new_renderable.set_screen_position(rect);
final_renderables.push_back(new_renderable);
}
renderer_->draw(window->native_handle(), Rect{0, 0, window->frame().width(),
window->frame().height()},
final_renderables);
auto win_layers = strategy_->process_layers(renderables);
for (auto &w : win_layers) {
renderer_->draw(w.first->native_handle(),
Rect{0, 0, w.first->frame().width(), w.first->frame().height()},
w.second);
}
}
} // namespace graphics

View file

@ -21,23 +21,33 @@
#include "anbox/graphics/renderer.h"
#include <memory>
#include <map>
namespace anbox {
namespace wm {
class Manager;
class Window;
} // namespace wm
namespace graphics {
class LayerComposer {
public:
class Strategy {
public:
typedef std::map<std::shared_ptr<wm::Window>, RenderableList> WindowRenderableList;
virtual ~Strategy() {}
virtual WindowRenderableList process_layers(const RenderableList &renderables) = 0;
};
LayerComposer(const std::shared_ptr<Renderer> renderer,
const std::shared_ptr<wm::Manager> &wm);
const std::shared_ptr<Strategy> &strategy);
~LayerComposer();
void submit_layers(const RenderableList &renderables);
private:
std::shared_ptr<Renderer> renderer_;
std::shared_ptr<wm::Manager> wm_;
std::shared_ptr<Strategy> strategy_;
};
} // namespace graphics
} // namespace anbox

View file

@ -0,0 +1,86 @@
/*
* Copyright (C) 2017 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/>.
*
*/
#include "anbox/graphics/multi_window_composer_strategy.h"
#include "anbox/wm/manager.h"
#include "anbox/utils.h"
namespace anbox {
namespace graphics {
MultiWindowComposerStrategy::MultiWindowComposerStrategy(const std::shared_ptr<wm::Manager> &wm) : wm_(wm) {}
std::map<std::shared_ptr<wm::Window>, RenderableList> MultiWindowComposerStrategy::process_layers(const RenderableList &renderables) {
WindowRenderableList win_layers;
for (const auto &renderable : renderables) {
// Ignore all surfaces which are not meant for a task
if (!utils::string_starts_with(renderable.name(), "org.anbox.surface."))
continue;
wm::Task::Id task_id = 0;
if (sscanf(renderable.name().c_str(), "org.anbox.surface.%d", &task_id) != 1 || !task_id)
continue;
auto w = wm_->find_window_for_task(task_id);
if (!w) continue;
if (win_layers.find(w) == win_layers.end()) {
win_layers.insert({w, {renderable}});
continue;
}
win_layers[w].push_back(renderable);
}
for (auto &w : win_layers) {
const auto &renderables = w.second;
RenderableList final_renderables;
auto new_window_frame = Rect::Invalid;
auto max_layer_area = -1;
for (auto &r : renderables) {
const auto layer_area = r.screen_position().width() * r.screen_position().height();
// We always prioritize layers which are lower in the list we got
// from SurfaceFlinger as they are already ordered.
if (layer_area < max_layer_area)
continue;
max_layer_area = layer_area;
new_window_frame = r.screen_position();
}
for (auto &r : renderables) {
// As we get absolute display coordinates from the Android hwcomposer we
// need to recalculate all layer coordinates into relatives ones to the
// window they are drawn into.
auto rect = Rect{
r.screen_position().left() - new_window_frame.left() + r.crop().left(),
r.screen_position().top() - new_window_frame.top() + r.crop().top(),
r.screen_position().right() - new_window_frame.left() + r.crop().left(),
r.screen_position().bottom() - new_window_frame.top() + r.crop().top()};
auto new_renderable = r;
new_renderable.set_screen_position(rect);
final_renderables.push_back(new_renderable);
}
w.second = final_renderables;
}
return win_layers;
}
} // namespace graphics
} // namespace anbox

View file

@ -0,0 +1,40 @@
/*
* Copyright (C) 2017 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/>.
*
*/
#ifndef ANBOX_GRAPHICS_MULTI_WINDOW_COMPOSER_STRATEGY_H_
#define ANBOX_GRAPHICS_MULTI_WINDOW_COMPOSER_STRATEGY_H_
#include "anbox/graphics/layer_composer.h"
#include <memory>
namespace anbox {
namespace graphics {
class MultiWindowComposerStrategy : public LayerComposer::Strategy {
public:
MultiWindowComposerStrategy(const std::shared_ptr<wm::Manager> &wm);
~MultiWindowComposerStrategy() = default;
WindowRenderableList process_layers(const RenderableList &renderables) override;
private:
std::shared_ptr<wm::Manager> wm_;
};
} // namespace graphics
} // namespace anbox
#endif

View file

@ -0,0 +1,50 @@
/*
* Copyright (C) 2017 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/>.
*
*/
#include "anbox/graphics/single_window_composer_strategy.h"
#include "anbox/wm/manager.h"
#include "anbox/utils.h"
#include "anbox/logger.h"
namespace {
const constexpr char *sprite_name{"Sprite"};
}
namespace anbox {
namespace graphics {
SingleWindowComposerStrategy::SingleWindowComposerStrategy(const std::shared_ptr<wm::Manager> &wm) : wm_(wm) {}
std::map<std::shared_ptr<wm::Window>, RenderableList> SingleWindowComposerStrategy::process_layers(const RenderableList &renderables) {
WindowRenderableList win_layers;
// FIXME there will be only one window in single-window mode ever so it
// doesn't matter which task
auto window = wm_->find_window_for_task(0);
// Filter out any unwanted layers like the one responsible for the mouse
// cursor which we don't want to render.
RenderableList final_renderables;
for (const auto &r : renderables) {
if (r.name() == sprite_name)
continue;
final_renderables.push_back(r);
}
win_layers.insert({window, final_renderables});
return win_layers;
}
} // namespace graphics
} // namespace anbox

View file

@ -0,0 +1,40 @@
/*
* Copyright (C) 2017 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/>.
*
*/
#ifndef ANBOX_GRAPHICS_SINGLE_WINDOW_COMPOSER_STRATEGY_H_
#define ANBOX_GRAPHICS_SINGLE_WINDOW_COMPOSER_STRATEGY_H_
#include "anbox/graphics/layer_composer.h"
#include <memory>
namespace anbox {
namespace graphics {
class SingleWindowComposerStrategy : public LayerComposer::Strategy {
public:
SingleWindowComposerStrategy(const std::shared_ptr<wm::Manager> &wm);
~SingleWindowComposerStrategy() = default;
WindowRenderableList process_layers(const RenderableList &renderables) override;
private:
std::shared_ptr<wm::Manager> wm_;
};
} // namespace graphics
} // namespace anbox
#endif

View file

@ -18,13 +18,13 @@
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch-default"
#include "anbox/ubuntu/platform_policy.h"
#include "anbox/bridge/android_api_stub.h"
#include "anbox/input/device.h"
#include "anbox/input/manager.h"
#include "anbox/logger.h"
#include "anbox/ubuntu/keycode_converter.h"
#include "anbox/ubuntu/window.h"
#include "anbox/ubuntu/audio_sink.h"
#include "anbox/wm/manager.h"
#include <boost/throw_exception.hpp>
@ -36,32 +36,38 @@ namespace anbox {
namespace ubuntu {
PlatformPolicy::PlatformPolicy(
const std::shared_ptr<input::Manager> &input_manager,
const std::shared_ptr<bridge::AndroidApiStub> &android_api)
const graphics::Rect &static_display_frame,
bool single_window)
: input_manager_(input_manager),
android_api_(android_api),
event_thread_running_(false) {
event_thread_running_(false),
single_window_(single_window) {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_EVENTS) < 0) {
const auto message = utils::string_format("Failed to initialize SDL: %s", SDL_GetError());
BOOST_THROW_EXCEPTION(std::runtime_error(message));
}
auto display_frame = graphics::Rect::Invalid;
for (auto n = 0; n < SDL_GetNumVideoDisplays(); n++) {
SDL_Rect r;
if (SDL_GetDisplayBounds(n, &r) != 0) continue;
if (static_display_frame == graphics::Rect::Invalid) {
for (auto n = 0; n < SDL_GetNumVideoDisplays(); n++) {
SDL_Rect r;
if (SDL_GetDisplayBounds(n, &r) != 0) continue;
graphics::Rect frame{r.x, r.y, r.x + r.w, r.y + r.h};
graphics::Rect frame{r.x, r.y, r.x + r.w, r.y + r.h};
if (display_frame == graphics::Rect::Invalid)
display_frame = frame;
else
display_frame.merge(frame);
}
if (display_frame == graphics::Rect::Invalid)
display_frame = frame;
else
display_frame.merge(frame);
BOOST_THROW_EXCEPTION(
std::runtime_error("No valid display configuration found"));
} else {
display_frame = static_display_frame;
window_size_immutable_ = true;
}
if (display_frame == graphics::Rect::Invalid)
BOOST_THROW_EXCEPTION(
std::runtime_error("No valid display configuration found"));
display_info_.horizontal_resolution = display_frame.width();
display_info_.vertical_resolution = display_frame.height();
@ -101,6 +107,10 @@ void PlatformPolicy::set_renderer(const std::shared_ptr<Renderer> &renderer) {
renderer_ = renderer;
}
void PlatformPolicy::set_window_manager(const std::shared_ptr<wm::Manager> &window_manager) {
window_manager_ = window_manager;
}
void PlatformPolicy::process_events() {
event_thread_running_ = true;
@ -153,25 +163,31 @@ void PlatformPolicy::process_input_event(const SDL_Event &event) {
mouse_events.push_back({EV_SYN, SYN_REPORT, 0});
break;
case SDL_MOUSEMOTION:
// As we get only absolute coordindates relative to our window we have to
// calculate the correct position based on the current focused window
window = SDL_GetWindowFromID(event.window.windowID);
if (!window) break;
if (!single_window_) {
// As we get only absolute coordindates relative to our window we have to
// calculate the correct position based on the current focused window
window = SDL_GetWindowFromID(event.window.windowID);
if (!window) break;
SDL_GetWindowPosition(window, &x, &y);
x += event.motion.x;
y += event.motion.y;
SDL_GetWindowPosition(window, &x, &y);
x += event.motion.x;
y += event.motion.y;
} else {
// When running the whole Android system in a single window we don't
// need to reacalculate and the pointer position as they are already
// relative to our window.
x = event.motion.x;
y = event.motion.y;
}
// NOTE: Sending relative move events doesn't really work and we have
// changes
// in libinputflinger to take ABS_X/ABS_Y instead for absolute position
// events.
// changes in libinputflinger to take ABS_X/ABS_Y instead for absolute
// position events.
mouse_events.push_back({EV_ABS, ABS_X, x});
mouse_events.push_back({EV_ABS, ABS_Y, y});
// We're sending relative position updates here too but they will be only
// used
// by the Android side EventHub/InputReader to determine if the cursor was
// moved. They are not used to find out the exact position.
// used by the Android side EventHub/InputReader to determine if the cursor
// was moved. They are not used to find out the exact position.
mouse_events.push_back({EV_REL, REL_X, event.motion.xrel});
mouse_events.push_back({EV_REL, REL_Y, event.motion.yrel});
mouse_events.push_back({EV_SYN, SYN_REPORT, 0});
@ -214,7 +230,7 @@ std::shared_ptr<wm::Window> PlatformPolicy::create_window(
}
auto id = next_window_id();
auto w = std::make_shared<Window>(renderer_, id, task, shared_from_this(), frame, title);
auto w = std::make_shared<Window>(renderer_, id, task, shared_from_this(), frame, title, !window_size_immutable_);
windows_.insert({id, w});
return w;
}
@ -225,7 +241,8 @@ void PlatformPolicy::window_deleted(const Window::Id &id) {
WARNING("Got window removed event for unknown window (id %d)", id);
return;
}
if (auto window = w->second.lock()) android_api_->remove_task(window->task());
if (auto window = w->second.lock())
window_manager_->remove_task(window->task());
windows_.erase(w);
}
@ -234,7 +251,7 @@ void PlatformPolicy::window_wants_focus(const Window::Id &id) {
if (w == windows_.end()) return;
if (auto window = w->second.lock())
android_api_->set_focused_task(window->task());
window_manager_->set_focused_task(window->task());
}
void PlatformPolicy::window_moved(const Window::Id &id, const std::int32_t &x,
@ -246,7 +263,7 @@ void PlatformPolicy::window_moved(const Window::Id &id, const std::int32_t &x,
auto new_frame = window->frame();
new_frame.translate(x, y);
window->update_frame(new_frame);
android_api_->resize_task(window->task(), new_frame, 3);
window_manager_->resize_task(window->task(), new_frame, 3);
}
}
@ -264,7 +281,7 @@ void PlatformPolicy::window_resized(const Window::Id &id,
// representing this window and then we're back to the original size of
// the task.
window->update_frame(new_frame);
android_api_->resize_task(window->task(), new_frame, 3);
window_manager_->resize_task(window->task(), new_frame, 3);
}
}

View file

@ -35,9 +35,9 @@ namespace input {
class Device;
class Manager;
} // namespace input
namespace bridge {
class AndroidApiStub;
} // namespace bridge
namespace wm {
class Manager;
} // namespace wm
namespace ubuntu {
class PlatformPolicy : public std::enable_shared_from_this<PlatformPolicy>,
public platform::Policy,
@ -45,7 +45,8 @@ class PlatformPolicy : public std::enable_shared_from_this<PlatformPolicy>,
public DisplayManager {
public:
PlatformPolicy(const std::shared_ptr<input::Manager> &input_manager,
const std::shared_ptr<bridge::AndroidApiStub> &android_api);
const graphics::Rect &static_display_frame = graphics::Rect::Invalid,
bool single_window = false);
~PlatformPolicy();
std::shared_ptr<wm::Window> create_window(
@ -63,6 +64,7 @@ class PlatformPolicy : public std::enable_shared_from_this<PlatformPolicy>,
DisplayInfo display_info() const override;
void set_renderer(const std::shared_ptr<Renderer> &renderer);
void set_window_manager(const std::shared_ptr<wm::Manager> &window_manager);
void set_clipboard_data(const ClipboardData &data) override;
ClipboardData get_clipboard_data() override;
@ -78,7 +80,7 @@ class PlatformPolicy : public std::enable_shared_from_this<PlatformPolicy>,
std::shared_ptr<Renderer> renderer_;
std::shared_ptr<input::Manager> input_manager_;
std::shared_ptr<bridge::AndroidApiStub> android_api_;
std::shared_ptr<wm::Manager> window_manager_;
// We don't own the windows anymore after the got created by us so we
// need to be careful once we try to use them again.
std::map<Window::Id, std::weak_ptr<Window>> windows_;
@ -88,6 +90,8 @@ class PlatformPolicy : public std::enable_shared_from_this<PlatformPolicy>,
std::shared_ptr<input::Device> pointer_;
std::shared_ptr<input::Device> keyboard_;
DisplayManager::DisplayInfo display_info_;
bool window_size_immutable_ = false;
bool single_window_ = false;
};
} // namespace wm
} // namespace anbox

View file

@ -40,7 +40,8 @@ Window::Window(const std::shared_ptr<Renderer> &renderer,
const Id &id, const wm::Task::Id &task,
const std::shared_ptr<Observer> &observer,
const graphics::Rect &frame,
const std::string &title)
const std::string &title,
bool resizable)
: wm::Window(renderer, task, frame, title),
id_(id),
observer_(observer),
@ -48,12 +49,21 @@ Window::Window(const std::shared_ptr<Renderer> &renderer,
native_window_(0) {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
window_ = SDL_CreateWindow(title.c_str(), frame.left(), frame.top(),
// NOTE: We don't furce GL initialization of the window as this will
// be take care of by the Renderer when we attach to it. On EGL
// initializing GL here will cause a surface to be created and the
// renderer will attempt to create one too which will not work as
// only a single surface per EGLNativeWindowType is supported.
std::uint32_t flags = 0;
if (resizable)
flags |= SDL_WINDOW_RESIZABLE;
window_ = SDL_CreateWindow(title.c_str(),
frame.left(), frame.top(),
frame.width(), frame.height(),
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
flags);
if (!window_) {
const auto message =
utils::string_format("Failed to create window: %s", SDL_GetError());
const auto message = utils::string_format("Failed to create window: %s", SDL_GetError());
BOOST_THROW_EXCEPTION(std::runtime_error(message));
}
@ -77,6 +87,8 @@ Window::Window(const std::shared_ptr<Renderer> &renderer,
ERROR("Unknown subsystem (%d)", info.subsystem);
BOOST_THROW_EXCEPTION(std::runtime_error("SDL subsystem not suported"));
}
SDL_ShowWindow(window_);
}
Window::~Window() {

View file

@ -51,7 +51,8 @@ class Window : public std::enable_shared_from_this<Window>, public wm::Window {
const Id &id, const wm::Task::Id &task,
const std::shared_ptr<Observer> &observer,
const graphics::Rect &frame,
const std::string &title);
const std::string &title,
bool resizable);
~Window();
void process_event(const SDL_Event &event);

View file

@ -16,93 +16,9 @@
*/
#include "anbox/wm/manager.h"
#include "anbox/application/database.h"
#include "anbox/platform/policy.h"
#include "anbox/logger.h"
#include <algorithm>
namespace anbox {
namespace wm {
Manager::Manager(const std::shared_ptr<platform::Policy> &policy,
const std::shared_ptr<application::Database> &app_db)
: platform_policy_(policy), app_db_(app_db) {}
Manager::~Manager() {}
void Manager::apply_window_state_update(const WindowState::List &updated,
const WindowState::List &removed) {
std::lock_guard<std::mutex> l(mutex_);
// Base on the update we get from the Android WindowManagerService we will
// create
// different window instances with the properties supplied. Incoming layer
// updates
// from SurfaceFlinger will be mapped later into those windows and eventually
// composited there via GLES (e.g. for popups, ..)
std::map<Task::Id, WindowState::List> task_updates;
for (const auto &window : updated) {
// Ignore all windows which are not part of the freeform task stack
if (window.stack() != Stack::Freeform) continue;
// And also those which don't have a surface mapped at the moment
if (!window.has_surface()) continue;
// If we know that task already we first collect all window updates
// for it so we can apply all of them together.
auto w = windows_.find(window.task());
if (w != windows_.end()) {
auto t = task_updates.find(window.task());
if (t == task_updates.end())
task_updates.insert({window.task(), {window}});
else
task_updates[window.task()].push_back(window);
continue;
}
auto title = window.package_name();
auto app = app_db_->find_by_package(window.package_name());
if (app.valid())
title = app.name;
auto platform_window = platform_policy_->create_window(window.task(), window.frame(), title);
platform_window->attach();
windows_.insert({window.task(), platform_window});
}
// Send updates we collected per task down to the corresponding window
// so that they can update themself.
for (const auto &u : task_updates) {
auto w = windows_.find(u.first);
if (w == windows_.end()) continue;
w->second->update_state(u.second);
}
// As final step we process all windows we need to remove as they
// got killed on the other side. We need to respect here that we
// also get removals for windows which are part of a task which is
// still in use by other windows.
for (const auto &window : removed) {
auto w = windows_.find(window.task());
if (w == windows_.end()) continue;
if (task_updates.find(window.task()) == task_updates.end()) {
auto platform_window = w->second;
platform_window->release();
windows_.erase(w);
}
}
}
std::shared_ptr<Window> Manager::find_window_for_task(const Task::Id &task) {
std::lock_guard<std::mutex> l(mutex_);
for (const auto &w : windows_) {
if (w.second->task() == task) return w.second;
}
return nullptr;
}
} // namespace wm
} // namespace anbox
} // namespace wm
} // namespace anbox

View file

@ -26,29 +26,22 @@
#include <mutex>
namespace anbox {
namespace application {
class Database;
} // namespace application
namespace platform {
class Policy;
} // namespace platform
namespace wm {
class Manager {
public:
Manager(const std::shared_ptr<platform::Policy> &policy,
const std::shared_ptr<application::Database> &app_db);
~Manager();
virtual ~Manager();
void apply_window_state_update(const WindowState::List &updated,
const WindowState::List &removed);
virtual void setup() {}
std::shared_ptr<Window> find_window_for_task(const Task::Id &task);
virtual void apply_window_state_update(const WindowState::List &updated, const WindowState::List &removed) = 0;
private:
std::mutex mutex_;
std::shared_ptr<platform::Policy> platform_policy_;
std::shared_ptr<application::Database> app_db_;
std::map<Task::Id, std::shared_ptr<Window>> windows_;
virtual void resize_task(const Task::Id &task, const anbox::graphics::Rect &rect,
const std::int32_t &resize_mode) = 0;
virtual void set_focused_task(const Task::Id &task) = 0;
virtual void remove_task(const Task::Id &task) = 0;
// FIXME only applies for the multi-window case
virtual std::shared_ptr<Window> find_window_for_task(const Task::Id &task) = 0;
};
} // namespace wm
} // namespace anbox

View file

@ -0,0 +1,120 @@
/*
* 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/>.
*
*/
#include "anbox/wm/multi_window_manager.h"
#include "anbox/platform/policy.h"
#include "anbox/bridge/android_api_stub.h"
#include "anbox/logger.h"
#include <algorithm>
namespace anbox {
namespace wm {
MultiWindowManager::MultiWindowManager(const std::shared_ptr<platform::Policy> &policy,
const std::shared_ptr<bridge::AndroidApiStub> &android_api_stub,
const std::shared_ptr<application::Database> &app_db)
: platform_policy_(policy), android_api_stub_(android_api_stub), app_db_(app_db) {}
MultiWindowManager::~MultiWindowManager() {}
void MultiWindowManager::apply_window_state_update(const WindowState::List &updated,
const WindowState::List &removed) {
std::lock_guard<std::mutex> l(mutex_);
// Base on the update we get from the Android WindowManagerService we will
// create different window instances with the properties supplied. Incoming
// layer updates from SurfaceFlinger will be mapped later into those windows
// and eventually composited there via GLES (e.g. for popups, ..)
std::map<Task::Id, WindowState::List> task_updates;
for (const auto &window : updated) {
// Ignore all windows which are not part of the freeform task stack
if (window.stack() != Stack::Freeform) continue;
// And also those which don't have a surface mapped at the moment
if (!window.has_surface()) continue;
// If we know that task already we first collect all window updates
// for it so we can apply all of them together.
auto w = windows_.find(window.task());
if (w != windows_.end()) {
auto t = task_updates.find(window.task());
if (t == task_updates.end())
task_updates.insert({window.task(), {window}});
else
task_updates[window.task()].push_back(window);
continue;
}
auto title = window.package_name();
auto app = app_db_->find_by_package(window.package_name());
if (app.valid())
title = app.name;
auto platform_window = platform_policy_->create_window(window.task(), window.frame(), title);
platform_window->attach();
windows_.insert({window.task(), platform_window});
}
// Send updates we collected per task down to the corresponding window
// so that they can update themself.
for (const auto &u : task_updates) {
auto w = windows_.find(u.first);
if (w == windows_.end()) continue;
w->second->update_state(u.second);
}
// As final step we process all windows we need to remove as they
// got killed on the other side. We need to respect here that we
// also get removals for windows which are part of a task which is
// still in use by other windows.
for (const auto &window : removed) {
auto w = windows_.find(window.task());
if (w == windows_.end()) continue;
if (task_updates.find(window.task()) == task_updates.end()) {
auto platform_window = w->second;
platform_window->release();
windows_.erase(w);
}
}
}
std::shared_ptr<Window> MultiWindowManager::find_window_for_task(const Task::Id &task) {
std::lock_guard<std::mutex> l(mutex_);
for (const auto &w : windows_) {
if (w.second->task() == task) return w.second;
}
return nullptr;
}
void MultiWindowManager::resize_task(const Task::Id &task, const anbox::graphics::Rect &rect,
const std::int32_t &resize_mode) {
android_api_stub_->resize_task(task, rect, resize_mode);
}
void MultiWindowManager::set_focused_task(const Task::Id &task) {
android_api_stub_->set_focused_task(task);
}
void MultiWindowManager::remove_task(const Task::Id &task) {
android_api_stub_->remove_task(task);
}
} // namespace wm
} // namespace anbox

View file

@ -0,0 +1,64 @@
/*
* 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/>.
*
*/
#ifndef ANBOX_WM_MULTI_WINDOW_MANAGER_H_
#define ANBOX_WM_MULTI_WINDOW_MANAGER_H_
#include "anbox/wm/manager.h"
#include <map>
#include <memory>
#include <mutex>
namespace anbox {
namespace application {
class Database;
} // namespace application
namespace bridge {
class AndroidApiStub;
} // namespace bridge
namespace platform {
class Policy;
} // namespace platform
namespace wm {
class MultiWindowManager : public Manager {
public:
MultiWindowManager(const std::shared_ptr<platform::Policy> &policy,
const std::shared_ptr<bridge::AndroidApiStub> &android_api_stub,
const std::shared_ptr<application::Database> &app_db);
~MultiWindowManager();
void apply_window_state_update(const WindowState::List &updated, const WindowState::List &removed) override;
std::shared_ptr<Window> find_window_for_task(const Task::Id &task) override;
void resize_task(const Task::Id &task, const anbox::graphics::Rect &rect,
const std::int32_t &resize_mode) override;
void set_focused_task(const Task::Id &task) override;
void remove_task(const Task::Id &task) override;
private:
std::mutex mutex_;
std::shared_ptr<platform::Policy> platform_policy_;
std::shared_ptr<bridge::AndroidApiStub> android_api_stub_;
std::shared_ptr<application::Database> app_db_;
std::map<Task::Id, std::shared_ptr<Window>> windows_;
};
} // namespace wm
} // namespace anbox
#endif

View file

@ -0,0 +1,74 @@
/*
* 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/>.
*
*/
#include "anbox/wm/single_window_manager.h"
#include "anbox/platform/policy.h"
#include "anbox/logger.h"
#include <algorithm>
#include <sys/types.h>
#include <signal.h>
namespace anbox {
namespace wm {
SingleWindowManager::SingleWindowManager(const std::shared_ptr<platform::Policy> &policy,
const std::shared_ptr<application::Database> &app_db)
: platform_policy_(policy), app_db_(app_db) {}
SingleWindowManager::~SingleWindowManager() {}
void SingleWindowManager::setup() {
window_ = platform_policy_->create_window(0, {0, 0, 1024, 768}, "Android");
if (!window_->attach())
WARNING("Failed to attach window to renderer");
}
void SingleWindowManager::apply_window_state_update(const WindowState::List &updated, const WindowState::List &removed) {
(void)updated;
(void)removed;
}
std::shared_ptr<Window> SingleWindowManager::find_window_for_task(const Task::Id &task) {
(void)task;
return window_;
}
void SingleWindowManager::resize_task(const Task::Id &task, const anbox::graphics::Rect &rect,
const std::int32_t &resize_mode) {
(void)task;
(void)rect;
(void)resize_mode;
}
void SingleWindowManager::set_focused_task(const Task::Id &task) {
(void)task;
}
void SingleWindowManager::remove_task(const Task::Id &task) {
if (task != 0) {
WARNING("Window with invalid task id was closed");
return;
}
// FIXME easiest to terminate is sending ourself the terminate signal
// which will be then processed by the main loop and terminate the whole
// application.
kill(getpid(), SIGTERM);
}
} // namespace wm
} // namespace anbox

View file

@ -0,0 +1,61 @@
/*
* 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/>.
*
*/
#ifndef ANBOX_WM_SINGLE_WINDOW_MANAGER_H_
#define ANBOX_WM_SINGLE_WINDOW_MANAGER_H_
#include "anbox/wm/manager.h"
#include <map>
#include <memory>
#include <mutex>
namespace anbox {
namespace application {
class Database;
} // namespace application
namespace platform {
class Policy;
} // namespace platform
namespace wm {
class Window;
class SingleWindowManager : public Manager {
public:
SingleWindowManager(const std::shared_ptr<platform::Policy> &policy,
const std::shared_ptr<application::Database> &app_db);
~SingleWindowManager();
void setup() override;
void apply_window_state_update(const WindowState::List &updated, const WindowState::List &removed) override;
std::shared_ptr<Window> find_window_for_task(const Task::Id &task) override;
void resize_task(const Task::Id &task, const anbox::graphics::Rect &rect,
const std::int32_t &resize_mode) override;
void set_focused_task(const Task::Id &task) override;
void remove_task(const Task::Id &task) override;
private:
std::shared_ptr<platform::Policy> platform_policy_;
std::shared_ptr<application::Database> app_db_;
std::shared_ptr<Window> window_;
};
} // namespace wm
} // namespace anbox
#endif

View file

@ -24,7 +24,9 @@ namespace wm {
Window::Window(const std::shared_ptr<Renderer> &renderer, const Task::Id &task, const graphics::Rect &frame, const std::string &title)
: renderer_(renderer), task_(task), frame_(frame), title_(title) {}
Window::~Window() {}
Window::~Window() {
release();
}
void Window::update_state(const WindowState::List &states) {
(void)states;
@ -47,11 +49,12 @@ std::string Window::title() const { return title_; }
bool Window::attach() {
if (!renderer_)
return false;
return renderer_->createNativeWindow(native_handle());
attached_ = renderer_->createNativeWindow(native_handle());
return attached_;
}
void Window::release() {
if (!renderer_)
if (!renderer_ || !attached_)
return;
renderer_->destroyNativeWindow(native_handle());
}

View file

@ -64,6 +64,7 @@ class Window {
Task::Id task_;
graphics::Rect frame_;
std::string title_;
bool attached_ = false;
};
} // namespace wm
} // namespace anbox

View file

@ -22,10 +22,11 @@
#include "anbox/application/database.h"
#include "anbox/platform/default_policy.h"
#include "anbox/wm/manager.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;
@ -46,7 +47,7 @@ TEST(LayerComposer, FindsNoSuitableWindowForLayer) {
// from the manager.
auto platform_policy = std::make_shared<platform::DefaultPolicy>();
auto app_db = std::make_shared<application::Database>();
auto wm = std::make_shared<wm::Manager>(platform_policy, app_db);
auto wm = std::make_shared<wm::MultiWindowManager>(platform_policy, nullptr, app_db);
auto single_window = wm::WindowState{
wm::Display::Id{1},
@ -59,7 +60,7 @@ TEST(LayerComposer, FindsNoSuitableWindowForLayer) {
wm->apply_window_state_update({single_window}, {});
LayerComposer composer(renderer, wm);
LayerComposer composer(renderer, std::make_shared<MultiWindowComposerStrategy>(wm));
// A single renderable which has a different task id then the window we know
// about
@ -80,7 +81,7 @@ TEST(LayerComposer, MapsLayersToWindows) {
// from the manager.
auto platform_policy = std::make_shared<platform::DefaultPolicy>();
auto app_db = std::make_shared<application::Database>();
auto wm = std::make_shared<wm::Manager>(platform_policy, app_db);
auto wm = std::make_shared<wm::MultiWindowManager>(platform_policy, nullptr, app_db);
auto first_window = wm::WindowState{
wm::Display::Id{1},
@ -102,7 +103,7 @@ TEST(LayerComposer, MapsLayersToWindows) {
wm->apply_window_state_update({first_window, second_window}, {});
LayerComposer composer(renderer, wm);
LayerComposer composer(renderer, std::make_shared<MultiWindowComposerStrategy>(wm));
// A single renderable which has a different task id then the window we know
// about
@ -140,7 +141,7 @@ TEST(LayerComposer, WindowPartiallyOffscreen) {
// from the manager.
auto platform_policy = std::make_shared<platform::DefaultPolicy>();
auto app_db = std::make_shared<application::Database>();
auto wm = std::make_shared<wm::Manager>(platform_policy, app_db);
auto wm = std::make_shared<wm::MultiWindowManager>(platform_policy, nullptr, app_db);
auto window = wm::WindowState{
wm::Display::Id{1},
@ -153,7 +154,7 @@ TEST(LayerComposer, WindowPartiallyOffscreen) {
wm->apply_window_state_update({window}, {});
LayerComposer composer(renderer, wm);
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
@ -185,7 +186,7 @@ TEST(LayerComposer, PopupShouldNotCauseWindowLayerOffset) {
// from the manager.
auto platform_policy = std::make_shared<platform::DefaultPolicy>();
auto app_db = std::make_shared<application::Database>();
auto wm = std::make_shared<wm::Manager>(platform_policy, app_db);
auto wm = std::make_shared<wm::MultiWindowManager>(platform_policy, nullptr, app_db);
auto window = wm::WindowState{
wm::Display::Id{1},
@ -198,7 +199,7 @@ TEST(LayerComposer, PopupShouldNotCauseWindowLayerOffset) {
wm->apply_window_state_update({window}, {});
LayerComposer composer(renderer, wm);
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