Merge pull request #53 from morphis/f/single-window-mode
Implement single window mode option in parallel to multi-window support
This commit is contained in:
commit
ee47e098df
45 changed files with 976 additions and 297 deletions
|
|
@ -73,10 +73,13 @@ void AndroidApiSkeleton::launch_application(anbox::protobuf::bridge::LaunchAppli
|
|||
std::vector<std::string> argv = {
|
||||
"/system/bin/am",
|
||||
"start",
|
||||
// Launch any application always in the freeform stack
|
||||
"--stack", "2",
|
||||
};
|
||||
|
||||
if (request->has_stack()) {
|
||||
argv.push_back("--stack");
|
||||
argv.push_back(std::to_string(request->stack()));
|
||||
}
|
||||
|
||||
if (request->has_launch_bounds()) {
|
||||
argv.push_back("--launch-bounds");
|
||||
std::stringstream launch_bounds;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -181,12 +185,14 @@ set(SOURCES
|
|||
anbox/ubuntu/audio_sink.cpp
|
||||
|
||||
anbox/dbus/interface.h
|
||||
anbox/dbus/codecs.h
|
||||
anbox/dbus/skeleton/service.cpp
|
||||
anbox/dbus/skeleton/application_manager.cpp
|
||||
anbox/dbus/stub/application_manager.cpp
|
||||
|
||||
anbox/application/launcher_storage.cpp
|
||||
anbox/application/database.cpp
|
||||
anbox/application/manager.h
|
||||
|
||||
anbox/cmds/version.cpp
|
||||
anbox/cmds/session_manager.cpp
|
||||
|
|
|
|||
71
src/anbox/application/manager.h
Normal file
71
src/anbox/application/manager.h
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* 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_APPLICATION_MANAGER_H_
|
||||
#define ANBOX_APPLICATION_MANAGER_H_
|
||||
|
||||
#include "anbox/android/intent.h"
|
||||
#include "anbox/do_not_copy_or_move.h"
|
||||
#include "anbox/graphics/rect.h"
|
||||
#include "anbox/wm/stack.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <core/property.h>
|
||||
|
||||
namespace anbox {
|
||||
namespace application {
|
||||
class Manager : public DoNotCopyOrMove {
|
||||
public:
|
||||
virtual void launch(const android::Intent &intent,
|
||||
const graphics::Rect &launch_bounds = graphics::Rect::Invalid,
|
||||
const wm::Stack::Id &stack = wm::Stack::Id::Default) = 0;
|
||||
|
||||
virtual core::Property<bool>& ready() = 0;
|
||||
};
|
||||
|
||||
class RestrictedManager : public Manager {
|
||||
public:
|
||||
RestrictedManager(
|
||||
const std::shared_ptr<Manager> &other,
|
||||
const wm::Stack::Id &launch_stack = wm::Stack::Id::Invalid) :
|
||||
other_(other),
|
||||
launch_stack_(launch_stack) {}
|
||||
|
||||
virtual ~RestrictedManager() {}
|
||||
|
||||
void launch(const android::Intent &intent,
|
||||
const graphics::Rect &launch_bounds = graphics::Rect::Invalid,
|
||||
const wm::Stack::Id &stack = wm::Stack::Id::Default) override {
|
||||
auto selected_stack = stack;
|
||||
// If we have a static launch stack set use that one instead of
|
||||
// the one the caller gave us.
|
||||
if (launch_stack_ != wm::Stack::Id::Invalid)
|
||||
selected_stack = launch_stack_;
|
||||
other_->launch(intent, launch_bounds, selected_stack);
|
||||
}
|
||||
|
||||
core::Property<bool>& ready() override { return other_->ready(); }
|
||||
|
||||
private:
|
||||
std::shared_ptr<Manager> other_;
|
||||
wm::Stack::Id launch_stack_;
|
||||
};
|
||||
} // namespace application
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
#include "anbox/logger.h"
|
||||
#include "anbox/rpc/channel.h"
|
||||
#include "anbox/utils.h"
|
||||
#include "anbox/wm/stack.h"
|
||||
|
||||
#include "anbox_bridge.pb.h"
|
||||
#include "anbox_rpc.pb.h"
|
||||
|
|
@ -46,7 +47,8 @@ void AndroidApiStub::ensure_rpc_channel() {
|
|||
}
|
||||
|
||||
void AndroidApiStub::launch(const android::Intent &intent,
|
||||
const graphics::Rect &launch_bounds) {
|
||||
const graphics::Rect &launch_bounds,
|
||||
const wm::Stack::Id &stack) {
|
||||
ensure_rpc_channel();
|
||||
|
||||
auto c = std::make_shared<Request<protobuf::rpc::Void>>();
|
||||
|
|
@ -57,6 +59,20 @@ void AndroidApiStub::launch(const android::Intent &intent,
|
|||
launch_wait_handle_.expect_result();
|
||||
}
|
||||
|
||||
switch (stack) {
|
||||
case wm::Stack::Id::Default:
|
||||
message.set_stack(::anbox::protobuf::bridge::LaunchApplication_Stack_DEFAULT);
|
||||
break;
|
||||
case wm::Stack::Id::Fullscreen:
|
||||
message.set_stack(::anbox::protobuf::bridge::LaunchApplication_Stack_FULLSCREEN);
|
||||
break;
|
||||
case wm::Stack::Id::Freeform:
|
||||
message.set_stack(::anbox::protobuf::bridge::LaunchApplication_Stack_FREEFORM);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (launch_bounds != graphics::Rect::Invalid) {
|
||||
auto rect = message.mutable_launch_bounds();
|
||||
rect->set_left(launch_bounds_.left());
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
#ifndef ANBOX_BRIDGE_ANDROID_API_STUB_H_
|
||||
#define ANBOX_BRIDGE_ANDROID_API_STUB_H_
|
||||
|
||||
#include "anbox/application_manager.h"
|
||||
#include "anbox/application/manager.h"
|
||||
#include "anbox/common/wait_handle.h"
|
||||
#include "anbox/graphics/rect.h"
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ namespace rpc {
|
|||
class Channel;
|
||||
} // namespace rpc
|
||||
namespace bridge {
|
||||
class AndroidApiStub : public anbox::ApplicationManager {
|
||||
class AndroidApiStub : public anbox::application::Manager {
|
||||
public:
|
||||
AndroidApiStub();
|
||||
~AndroidApiStub();
|
||||
|
|
@ -48,7 +48,10 @@ class AndroidApiStub : public anbox::ApplicationManager {
|
|||
void resize_task(const std::int32_t &id, const anbox::graphics::Rect &rect,
|
||||
const std::int32_t &resize_mode);
|
||||
|
||||
void launch(const android::Intent &intent, const graphics::Rect &launch_bounds = graphics::Rect::Invalid) override;
|
||||
void launch(const android::Intent &intent,
|
||||
const graphics::Rect &launch_bounds = graphics::Rect::Invalid,
|
||||
const wm::Stack::Id &stack = wm::Stack::Id::Default) override;
|
||||
|
||||
core::Property<bool>& ready() override;
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ std::ostream& operator<<(std::ostream& out,
|
|||
// We are imposing size constraints to ensure a consistent CLI layout.
|
||||
typedef SizeConstrainedString<20> Name;
|
||||
typedef SizeConstrainedString<60> Usage;
|
||||
typedef SizeConstrainedString<80> Description;
|
||||
typedef SizeConstrainedString<100> Description;
|
||||
|
||||
/// @brief Flag models an input parameter to a command.
|
||||
class Flag : public DoNotCopyOrMove {
|
||||
|
|
|
|||
|
|
@ -50,10 +50,12 @@ anbox::cmds::Launch::Launch()
|
|||
flag(cli::make_flag(cli::Name{"package"},
|
||||
cli::Description{"Package the intent should go to"},
|
||||
intent_.package));
|
||||
flag(cli::make_flag(
|
||||
cli::Name{"component"},
|
||||
cli::Description{"Component of a package the intent should go"},
|
||||
intent_.component));
|
||||
flag(cli::make_flag(cli::Name{"component"},
|
||||
cli::Description{"Component of a package the intent should go"},
|
||||
intent_.component));
|
||||
flag(cli::make_flag(cli::Name{"stack"},
|
||||
cli::Description{"Which window stack the activity should be started on. Possible: default, fullscreen, freeform"},
|
||||
stack_));
|
||||
|
||||
action([this](const cli::Command::Context&) {
|
||||
auto trap = core::posix::trap_signals_for_process({core::posix::Signal::sig_term, core::posix::Signal::sig_int});
|
||||
|
|
@ -82,7 +84,7 @@ anbox::cmds::Launch::Launch()
|
|||
dispatcher->dispatch([&]() {
|
||||
if (stub->ready()) {
|
||||
try {
|
||||
stub->launch(intent_);
|
||||
stub->launch(intent_, graphics::Rect::Invalid, stack_);
|
||||
success = true;
|
||||
} catch (std::exception &err) {
|
||||
ERROR("err %s", err.what());
|
||||
|
|
@ -97,7 +99,7 @@ anbox::cmds::Launch::Launch()
|
|||
if (!ready)
|
||||
return;
|
||||
try {
|
||||
stub->launch(intent_);
|
||||
stub->launch(intent_, graphics::Rect::Invalid, stack_);
|
||||
success = true;
|
||||
} catch (std::exception &err) {
|
||||
ERROR("Failed to launch activity: %s", err.what());
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include <memory>
|
||||
|
||||
#include "anbox/android/intent.h"
|
||||
#include "anbox/wm/stack.h"
|
||||
#include "anbox/cli.h"
|
||||
|
||||
namespace anbox {
|
||||
|
|
@ -33,6 +34,7 @@ class Launch : public cli::CommandWithFlagsAndAction {
|
|||
|
||||
private:
|
||||
android::Intent intent_;
|
||||
wm::Stack::Id stack_;
|
||||
};
|
||||
} // namespace cmds
|
||||
} // namespace anbox
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
@ -54,6 +55,8 @@
|
|||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace {
|
||||
const anbox::graphics::Rect default_single_window_size{0, 0, 1024, 768};
|
||||
|
||||
class NullConnectionCreator : public anbox::network::ConnectionCreator<
|
||||
boost::asio::local::stream_protocol> {
|
||||
public:
|
||||
|
|
@ -86,7 +89,8 @@ anbox::cmds::SessionManager::BusFactory anbox::cmds::SessionManager::session_bus
|
|||
anbox::cmds::SessionManager::SessionManager(const BusFactory &bus_factory)
|
||||
: CommandWithFlagsAndAction{cli::Name{"session-manager"}, cli::Usage{"session-manager"},
|
||||
cli::Description{"Run the the anbox session manager"}},
|
||||
bus_factory_(bus_factory) {
|
||||
bus_factory_(bus_factory),
|
||||
window_size_(default_single_window_size) {
|
||||
// Just for the purpose to allow QtMir (or unity8) to find this on our
|
||||
// /proc/*/cmdline
|
||||
// for proper confinement etc.
|
||||
|
|
@ -96,6 +100,12 @@ 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_));
|
||||
flag(cli::make_flag(cli::Name{"window-size"},
|
||||
cli::Description{"Size of the window in single window mode, e.g. --window-size=1024,768"},
|
||||
window_size_));
|
||||
|
||||
action([this](const cli::Command::Context &) {
|
||||
auto trap = core::posix::trap_signals_for_process(
|
||||
|
|
@ -135,19 +145,39 @@ 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 app_manager = std::static_pointer_cast<application::Manager>(android_api_stub);
|
||||
if (!single_window_) {
|
||||
// When we're not running single window mode we need to restrict ourself to
|
||||
// only launch applications in freeform mode as otherwise the window tracking
|
||||
// doesn't work.
|
||||
app_manager = std::make_shared<application::RestrictedManager>(
|
||||
android_api_stub, wm::Stack::Id::Freeform);
|
||||
}
|
||||
|
||||
auto display_frame = graphics::Rect::Invalid;
|
||||
if (single_window_)
|
||||
display_frame = window_size_;
|
||||
|
||||
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, display_frame, app_db);
|
||||
else
|
||||
window_manager = std::make_shared<wm::MultiWindowManager>(policy, android_api_stub, app_db);
|
||||
|
||||
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();
|
||||
|
|
@ -198,7 +228,7 @@ anbox::cmds::SessionManager::SessionManager(const BusFactory &bus_factory)
|
|||
auto bus = bus_factory_();
|
||||
bus->install_executor(core::dbus::asio::make_executor(bus, rt->service()));
|
||||
|
||||
auto skeleton = anbox::dbus::skeleton::Service::create_for_bus(bus, android_api_stub);
|
||||
auto skeleton = anbox::dbus::skeleton::Service::create_for_bus(bus, app_manager);
|
||||
|
||||
rt->start();
|
||||
trap->run();
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <core/dbus/bus.h>
|
||||
|
||||
#include "anbox/graphics/gl_renderer_server.h"
|
||||
#include "anbox/graphics/rect.h"
|
||||
|
||||
namespace anbox {
|
||||
namespace cmds {
|
||||
|
|
@ -42,6 +43,8 @@ class SessionManager : public cli::CommandWithFlagsAndAction {
|
|||
BusFactory bus_factory_;
|
||||
std::string desktop_file_hint_;
|
||||
graphics::GLRendererServer::Config::Driver gles_driver_;
|
||||
bool single_window_ = false;
|
||||
graphics::Rect window_size_;
|
||||
};
|
||||
} // namespace cmds
|
||||
} // namespace anbox
|
||||
|
|
|
|||
47
src/anbox/dbus/codecs.h
Normal file
47
src/anbox/dbus/codecs.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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 Lesser General Public License as published by
|
||||
* the Free Software Foundation; version 3.
|
||||
*
|
||||
* 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ANBOX_DBUS_CODECS_H_
|
||||
#define ANBOX_DBUS_CODECS_H_
|
||||
|
||||
#include "anbox/wm/stack.h"
|
||||
|
||||
#include <core/dbus/codec.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace core {
|
||||
namespace dbus {
|
||||
template<>
|
||||
struct Codec<anbox::wm::Stack::Id> {
|
||||
inline static void encode_argument(Message::Writer &out, const anbox::wm::Stack::Id &stack) {
|
||||
std::stringstream ss;
|
||||
ss << stack;
|
||||
auto s = ss.str();
|
||||
out.push_stringn(s.c_str(), s.length());
|
||||
}
|
||||
|
||||
inline static void decode_argument(Message::Reader &in, anbox::wm::Stack::Id &stack) {
|
||||
std::stringstream ss;
|
||||
ss << in.pop_string();
|
||||
ss >> stack;
|
||||
}
|
||||
};
|
||||
} // namespace dbus
|
||||
} // namespace core
|
||||
|
||||
#endif
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
#include "anbox/dbus/skeleton/application_manager.h"
|
||||
#include "anbox/android/intent.h"
|
||||
#include "anbox/dbus/interface.h"
|
||||
#include "anbox/dbus/codecs.h"
|
||||
#include "anbox/logger.h"
|
||||
|
||||
#include <core/property.h>
|
||||
|
|
@ -27,7 +28,7 @@ namespace dbus {
|
|||
namespace skeleton {
|
||||
ApplicationManager::ApplicationManager(
|
||||
const core::dbus::Bus::Ptr &bus, const core::dbus::Object::Ptr &object,
|
||||
const std::shared_ptr<anbox::ApplicationManager> &impl)
|
||||
const std::shared_ptr<anbox::application::Manager> &impl)
|
||||
: bus_(bus), object_(object), impl_(impl),
|
||||
properties_{ object_->get_property<anbox::dbus::interface::ApplicationManager::Properties::Ready>() },
|
||||
signals_{ object_->get_signal<core::dbus::interfaces::Properties::Signals::PropertiesChanged>() } {
|
||||
|
|
@ -51,10 +52,13 @@ ApplicationManager::ApplicationManager(
|
|||
reader >> bottom;
|
||||
graphics::Rect launch_bounds{left, top, right, bottom};
|
||||
|
||||
wm::Stack::Id stack = wm::Stack::Id::Default;
|
||||
reader >> stack;
|
||||
|
||||
core::dbus::Message::Ptr reply;
|
||||
|
||||
try {
|
||||
launch(intent, launch_bounds);
|
||||
launch(intent, launch_bounds, stack);
|
||||
reply = core::dbus::Message::make_method_return(msg);
|
||||
} catch (std::exception const &err) {
|
||||
reply = core::dbus::Message::make_error(msg, "org.anbox.Error.Failed",
|
||||
|
|
@ -88,8 +92,10 @@ void ApplicationManager::on_property_value_changed(const typename Property::Valu
|
|||
dict, the_empty_list_of_invalidated_properties));
|
||||
}
|
||||
|
||||
void ApplicationManager::launch(const android::Intent &intent, const graphics::Rect &launch_bounds) {
|
||||
impl_->launch(intent, launch_bounds);
|
||||
void ApplicationManager::launch(const android::Intent &intent,
|
||||
const graphics::Rect &launch_bounds,
|
||||
const wm::Stack::Id &stack) {
|
||||
impl_->launch(intent, launch_bounds, stack);
|
||||
}
|
||||
|
||||
core::Property<bool>& ApplicationManager::ready() {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
#ifndef ANBOX_DBUS_SKELETON_APPLICATION_MANAGER_H_
|
||||
#define ANBOX_DBUS_SKELETON_APPLICATION_MANAGER_H_
|
||||
|
||||
#include "anbox/application_manager.h"
|
||||
#include "anbox/application/manager.h"
|
||||
|
||||
#include <core/dbus/bus.h>
|
||||
#include <core/dbus/object.h>
|
||||
|
|
@ -30,14 +30,17 @@
|
|||
namespace anbox {
|
||||
namespace dbus {
|
||||
namespace skeleton {
|
||||
class ApplicationManager : public anbox::ApplicationManager {
|
||||
class ApplicationManager : public anbox::application::Manager {
|
||||
public:
|
||||
ApplicationManager(const core::dbus::Bus::Ptr &bus,
|
||||
const core::dbus::Object::Ptr &object,
|
||||
const std::shared_ptr<anbox::ApplicationManager> &impl);
|
||||
const std::shared_ptr<anbox::application::Manager> &impl);
|
||||
~ApplicationManager();
|
||||
|
||||
void launch(const android::Intent &intent, const graphics::Rect &launch_bounds = graphics::Rect::Invalid) override;
|
||||
void launch(const android::Intent &intent,
|
||||
const graphics::Rect &launch_bounds = graphics::Rect::Invalid,
|
||||
const wm::Stack::Id &stack = wm::Stack::Id::Default) override;
|
||||
|
||||
core::Property<bool>& ready() override;
|
||||
|
||||
private:
|
||||
|
|
@ -47,7 +50,7 @@ class ApplicationManager : public anbox::ApplicationManager {
|
|||
core::dbus::Bus::Ptr bus_;
|
||||
core::dbus::Service::Ptr service_;
|
||||
core::dbus::Object::Ptr object_;
|
||||
std::shared_ptr<anbox::ApplicationManager> impl_;
|
||||
std::shared_ptr<anbox::application::Manager> impl_;
|
||||
struct {
|
||||
std::shared_ptr<core::dbus::Property<anbox::dbus::interface::ApplicationManager::Properties::Ready>> ready;
|
||||
} properties_;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ namespace dbus {
|
|||
namespace skeleton {
|
||||
std::shared_ptr<Service> Service::create_for_bus(
|
||||
const core::dbus::Bus::Ptr &bus,
|
||||
const std::shared_ptr<anbox::ApplicationManager> &application_manager) {
|
||||
const std::shared_ptr<anbox::application::Manager> &application_manager) {
|
||||
auto service = core::dbus::Service::add_service(
|
||||
bus, anbox::dbus::interface::Service::name());
|
||||
auto object =
|
||||
|
|
@ -35,7 +35,7 @@ std::shared_ptr<Service> Service::create_for_bus(
|
|||
Service::Service(
|
||||
const core::dbus::Bus::Ptr &bus, const core::dbus::Service::Ptr &service,
|
||||
const core::dbus::Object::Ptr &object,
|
||||
const std::shared_ptr<anbox::ApplicationManager> &application_manager)
|
||||
const std::shared_ptr<anbox::application::Manager> &application_manager)
|
||||
: bus_(bus),
|
||||
service_(service),
|
||||
object_(object),
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
#ifndef ANBOX_DBUS_SKELETON_SERVICE_H_
|
||||
#define ANBOX_DBUS_SKELETON_SERVICE_H_
|
||||
|
||||
#include "anbox/application_manager.h"
|
||||
#include "anbox/application/manager.h"
|
||||
#include "anbox/do_not_copy_or_move.h"
|
||||
|
||||
#include <core/dbus/bus.h>
|
||||
|
|
@ -33,19 +33,19 @@ class Service : public DoNotCopyOrMove {
|
|||
public:
|
||||
static std::shared_ptr<Service> create_for_bus(
|
||||
const core::dbus::Bus::Ptr &bus,
|
||||
const std::shared_ptr<anbox::ApplicationManager> &application_manager);
|
||||
const std::shared_ptr<anbox::application::Manager> &application_manager);
|
||||
|
||||
Service(
|
||||
const core::dbus::Bus::Ptr &bus, const core::dbus::Service::Ptr &service,
|
||||
const core::dbus::Object::Ptr &object,
|
||||
const std::shared_ptr<anbox::ApplicationManager> &application_manager);
|
||||
const std::shared_ptr<anbox::application::Manager> &application_manager);
|
||||
~Service();
|
||||
|
||||
private:
|
||||
core::dbus::Bus::Ptr bus_;
|
||||
core::dbus::Service::Ptr service_;
|
||||
core::dbus::Object::Ptr object_;
|
||||
std::shared_ptr<ApplicationManager> application_manager_;
|
||||
std::shared_ptr<application::Manager> application_manager_;
|
||||
};
|
||||
} // namespace skeleton
|
||||
} // namespace dbus
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include "anbox/dbus/stub/application_manager.h"
|
||||
#include "anbox/dbus/interface.h"
|
||||
#include "anbox/dbus/codecs.h"
|
||||
#include "anbox/logger.h"
|
||||
|
||||
namespace anbox {
|
||||
|
|
@ -41,13 +42,15 @@ ApplicationManager::ApplicationManager(const core::dbus::Bus::Ptr &bus,
|
|||
|
||||
ApplicationManager::~ApplicationManager() {}
|
||||
|
||||
void ApplicationManager::launch(const android::Intent &intent, const graphics::Rect &launch_bounds) {
|
||||
void ApplicationManager::launch(const android::Intent &intent,
|
||||
const graphics::Rect &launch_bounds,
|
||||
const wm::Stack::Id &stack) {
|
||||
auto result = object_->invoke_method_synchronously<
|
||||
anbox::dbus::interface::ApplicationManager::Methods::Launch,
|
||||
anbox::dbus::interface::ApplicationManager::Methods::Launch::ResultType>(
|
||||
intent.action, intent.uri, intent.type, intent.flags, intent.package,
|
||||
intent.component, launch_bounds.left(), launch_bounds.top(),
|
||||
launch_bounds.right(), launch_bounds.bottom());
|
||||
launch_bounds.right(), launch_bounds.bottom(), stack);
|
||||
|
||||
if (result.is_error()) throw std::runtime_error(result.error().print());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
#ifndef ANBOX_DBUS_SKELETON_APPLICATION_MANAGER_H_
|
||||
#define ANBOX_DBUS_SKELETON_APPLICATION_MANAGER_H_
|
||||
|
||||
#include "anbox/application_manager.h"
|
||||
#include "anbox/application/manager.h"
|
||||
|
||||
#include <core/dbus/bus.h>
|
||||
#include <core/dbus/object.h>
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
namespace anbox {
|
||||
namespace dbus {
|
||||
namespace stub {
|
||||
class ApplicationManager : public anbox::ApplicationManager {
|
||||
class ApplicationManager : public anbox::application::Manager {
|
||||
public:
|
||||
static std::shared_ptr<ApplicationManager> create_for_bus(
|
||||
const core::dbus::Bus::Ptr &bus);
|
||||
|
|
@ -39,7 +39,10 @@ class ApplicationManager : public anbox::ApplicationManager {
|
|||
const core::dbus::Object::Ptr &object);
|
||||
~ApplicationManager();
|
||||
|
||||
void launch(const android::Intent &intent, const graphics::Rect &launch_bounds = graphics::Rect::Invalid) override;
|
||||
void launch(const android::Intent &intent,
|
||||
const graphics::Rect &launch_bounds = graphics::Rect::Invalid,
|
||||
const wm::Stack::Id &stack = wm::Stack::Id::Default) override;
|
||||
|
||||
core::Property<bool>& ready() override;
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
86
src/anbox/graphics/multi_window_composer_strategy.cpp
Normal file
86
src/anbox/graphics/multi_window_composer_strategy.cpp
Normal 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
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2016 Simon Fels <morphis@gravedo.de>
|
||||
* 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
|
||||
|
|
@ -15,23 +15,26 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#ifndef ANBOX_APPLICATION_MANAGER_H_
|
||||
#define ANBOX_APPLICATION_MANAGER_H_
|
||||
#ifndef ANBOX_GRAPHICS_MULTI_WINDOW_COMPOSER_STRATEGY_H_
|
||||
#define ANBOX_GRAPHICS_MULTI_WINDOW_COMPOSER_STRATEGY_H_
|
||||
|
||||
#include "anbox/android/intent.h"
|
||||
#include "anbox/do_not_copy_or_move.h"
|
||||
#include "anbox/graphics/rect.h"
|
||||
#include "anbox/graphics/layer_composer.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <core/property.h>
|
||||
#include <memory>
|
||||
|
||||
namespace anbox {
|
||||
class ApplicationManager : public DoNotCopyOrMove {
|
||||
namespace graphics {
|
||||
class MultiWindowComposerStrategy : public LayerComposer::Strategy {
|
||||
public:
|
||||
virtual void launch(const android::Intent &intent, const graphics::Rect &launch_bounds = graphics::Rect::Invalid) = 0;
|
||||
virtual core::Property<bool>& ready() = 0;
|
||||
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
|
||||
|
|
@ -16,10 +16,13 @@
|
|||
*/
|
||||
|
||||
#include "anbox/graphics/rect.h"
|
||||
#include "anbox/utils.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
namespace anbox {
|
||||
namespace graphics {
|
||||
const Rect Rect::Invalid{-1, -1, -1, -1};
|
||||
|
|
@ -51,5 +54,34 @@ std::ostream &operator<<(std::ostream &out, const Rect &rect) {
|
|||
<< "," << rect.bottom() << "} {" << rect.width() << ","
|
||||
<< rect.height() << "}";
|
||||
}
|
||||
|
||||
std::istream& operator>>(std::istream& in, anbox::graphics::Rect &rect)
|
||||
try {
|
||||
std::string str;
|
||||
in >> str;
|
||||
auto tokens = anbox::utils::string_split(str, ',');
|
||||
|
||||
switch (tokens.size()) {
|
||||
case 2: {
|
||||
rect = anbox::graphics::Rect(0, 0,
|
||||
boost::lexical_cast<std::int32_t>(tokens[0]),
|
||||
boost::lexical_cast<std::int32_t>(tokens[1]));
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
rect = anbox::graphics::Rect(
|
||||
boost::lexical_cast<std::int32_t>(tokens[0]),
|
||||
boost::lexical_cast<std::int32_t>(tokens[1]),
|
||||
boost::lexical_cast<std::int32_t>(tokens[2]),
|
||||
boost::lexical_cast<std::int32_t>(tokens[3]));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return in;
|
||||
} catch (...) {
|
||||
return in;
|
||||
}
|
||||
} // namespace graphics
|
||||
} // namespace anbox
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ class Rect {
|
|||
};
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const Rect &rect);
|
||||
std::istream& operator>>(std::istream& in, anbox::graphics::Rect &rect);
|
||||
} // namespace graphics
|
||||
} // namespace anbox
|
||||
|
||||
|
|
|
|||
50
src/anbox/graphics/single_window_composer_strategy.cpp
Normal file
50
src/anbox/graphics/single_window_composer_strategy.cpp
Normal 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
|
||||
40
src/anbox/graphics/single_window_composer_strategy.h
Normal file
40
src/anbox/graphics/single_window_composer_strategy.h
Normal 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
|
||||
|
|
@ -34,6 +34,12 @@ message Notification {
|
|||
message LaunchApplication {
|
||||
required Intent intent = 1;
|
||||
optional Rect launch_bounds = 2;
|
||||
enum Stack {
|
||||
DEFAULT = 0;
|
||||
FULLSCREEN = 1;
|
||||
FREEFORM = 2;
|
||||
}
|
||||
optional Stack stack = 3 [default = DEFAULT];
|
||||
}
|
||||
|
||||
message SetFocusedTask {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
121
src/anbox/wm/multi_window_manager.cpp
Normal file
121
src/anbox/wm/multi_window_manager.cpp
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* 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/application/database.h"
|
||||
#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::Id::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
|
||||
64
src/anbox/wm/multi_window_manager.h
Normal file
64
src/anbox/wm/multi_window_manager.h
Normal 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
|
||||
75
src/anbox/wm/single_window_manager.cpp
Normal file
75
src/anbox/wm/single_window_manager.cpp
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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 graphics::Rect &window_size,
|
||||
const std::shared_ptr<application::Database> &app_db)
|
||||
: platform_policy_(policy), window_size_(window_size), app_db_(app_db) {}
|
||||
|
||||
SingleWindowManager::~SingleWindowManager() {}
|
||||
|
||||
void SingleWindowManager::setup() {
|
||||
window_ = platform_policy_->create_window(0, window_size_, "Anbox - Android in a Box");
|
||||
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
|
||||
63
src/anbox/wm/single_window_manager.h
Normal file
63
src/anbox/wm/single_window_manager.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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 graphics::Rect &window_size,
|
||||
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_;
|
||||
graphics::Rect window_size_;
|
||||
std::shared_ptr<application::Database> app_db_;
|
||||
std::shared_ptr<Window> window_;
|
||||
};
|
||||
} // namespace wm
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
@ -19,9 +19,33 @@
|
|||
|
||||
namespace anbox {
|
||||
namespace wm {
|
||||
Stack::Id Stack::Invalid = -1;
|
||||
Stack::Id Stack::Default = 0;
|
||||
Stack::Id Stack::Fullscreen = 1;
|
||||
Stack::Id Stack::Freeform = 2;
|
||||
std::ostream &operator<<(std::ostream &out, const Stack::Id &stack) {
|
||||
switch (stack) {
|
||||
case anbox::wm::Stack::Id::Default:
|
||||
out << "default";
|
||||
break;
|
||||
case anbox::wm::Stack::Id::Fullscreen:
|
||||
out << "fullscreen";
|
||||
break;
|
||||
case anbox::wm::Stack::Id::Freeform:
|
||||
out << "freeform";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::istream& operator>>(std::istream& in, Stack::Id &stack) {
|
||||
std::string s;
|
||||
in >> s;
|
||||
if (s == "default")
|
||||
stack = anbox::wm::Stack::Id::Default;
|
||||
else if (s == "fullscreen")
|
||||
stack = anbox::wm::Stack::Id::Fullscreen;
|
||||
else if (s == "freeform")
|
||||
stack = anbox::wm::Stack::Id::Freeform;
|
||||
return in;
|
||||
}
|
||||
} // namespace wm
|
||||
} // namespace anbox
|
||||
|
|
|
|||
|
|
@ -18,23 +18,28 @@
|
|||
#ifndef ANBOX_WM_STACK_H_
|
||||
#define ANBOX_WM_STACK_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <ostream>
|
||||
|
||||
namespace anbox {
|
||||
namespace wm {
|
||||
class Stack {
|
||||
public:
|
||||
typedef std::int32_t Id;
|
||||
|
||||
static Id Invalid;
|
||||
static Id Default;
|
||||
static Id Fullscreen;
|
||||
static Id Freeform;
|
||||
enum class Id {
|
||||
Invalid = -1,
|
||||
Default = 0,
|
||||
Fullscreen = 1,
|
||||
Freeform = 2,
|
||||
};
|
||||
|
||||
Stack() = delete;
|
||||
Stack(const Stack&) = delete;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream &out, Stack::Id const &stack);
|
||||
std::istream& operator>>(std::istream &in, Stack::Id &stack);
|
||||
} // namespace wm
|
||||
} // namespace anbox
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ class Window {
|
|||
Task::Id task_;
|
||||
graphics::Rect frame_;
|
||||
std::string title_;
|
||||
bool attached_ = false;
|
||||
};
|
||||
} // namespace wm
|
||||
} // namespace anbox
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ WindowState::WindowState()
|
|||
frame_(graphics::Rect::Invalid),
|
||||
package_name_(""),
|
||||
task_(Task::Invalid),
|
||||
stack_(Stack::Invalid) {}
|
||||
stack_(Stack::Id::Invalid) {}
|
||||
|
||||
WindowState::WindowState(const Display::Id &display, bool has_surface,
|
||||
const graphics::Rect &frame,
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
|
|
@ -54,12 +55,12 @@ TEST(LayerComposer, FindsNoSuitableWindowForLayer) {
|
|||
graphics::Rect{0, 0, 1024, 768},
|
||||
"org.anbox.test.1",
|
||||
wm::Task::Id{1},
|
||||
wm::Stack::Freeform,
|
||||
wm::Stack::Id::Freeform,
|
||||
};
|
||||
|
||||
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},
|
||||
|
|
@ -88,7 +89,7 @@ TEST(LayerComposer, MapsLayersToWindows) {
|
|||
graphics::Rect{0, 0, 1024, 768},
|
||||
"org.anbox.foo",
|
||||
wm::Task::Id{1},
|
||||
wm::Stack::Freeform,
|
||||
wm::Stack::Id::Freeform,
|
||||
};
|
||||
|
||||
auto second_window = wm::WindowState{
|
||||
|
|
@ -97,12 +98,12 @@ TEST(LayerComposer, MapsLayersToWindows) {
|
|||
graphics::Rect{300, 400, 1324, 1168},
|
||||
"org.anbox.bar",
|
||||
wm::Task::Id{2},
|
||||
wm::Stack::Freeform,
|
||||
wm::Stack::Id::Freeform,
|
||||
};
|
||||
|
||||
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},
|
||||
|
|
@ -148,12 +149,12 @@ TEST(LayerComposer, WindowPartiallyOffscreen) {
|
|||
graphics::Rect{-100, -100, 924, 668},
|
||||
"org.anbox.foo",
|
||||
wm::Task::Id{1},
|
||||
wm::Stack::Freeform,
|
||||
wm::Stack::Id::Freeform,
|
||||
};
|
||||
|
||||
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},
|
||||
|
|
@ -193,12 +194,12 @@ TEST(LayerComposer, PopupShouldNotCauseWindowLayerOffset) {
|
|||
graphics::Rect{1120, 270, 2144, 1038},
|
||||
"org.anbox.foo",
|
||||
wm::Task::Id{3},
|
||||
wm::Stack::Freeform,
|
||||
wm::Stack::Id::Freeform,
|
||||
};
|
||||
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue