Reformat all code and use the Google cpp code style

This commit is contained in:
Simon Fels 2016-12-02 17:36:54 +01:00
commit b10382d2d8
222 changed files with 8739 additions and 9281 deletions

8
.clang-format Normal file
View file

@ -0,0 +1,8 @@
# Defines the Chromium style for automatic reformatting.
# http://clang.llvm.org/docs/ClangFormatStyleOptions.html
BasedOnStyle: Google
# This defaults to 'Auto'. Explicitly set it for a while, so that
# 'vector<vector<int> >' in existing files gets formatted to
# 'vector<vector<int>>'. ('Auto' means that clang-format will only use
# 'int>>' if the file already contains at least one such instance.)
Standard: Cpp11

3
scripts/clean-format.sh Executable file
View file

@ -0,0 +1,3 @@
#!/bin/sh
find src -name "*.h" | xargs clang-format -style=file -i
find src -name "*.cpp" | xargs clang-format -style=file -i

View file

@ -21,21 +21,18 @@
namespace anbox { namespace anbox {
namespace android { namespace android {
std::ostream& operator<<(std::ostream &out, const Intent &intent) std::ostream &operator<<(std::ostream &out, const Intent &intent) {
{ out << "["
out << "[" << "action=" << intent.action << " "
<< "action=" << intent.action << " " << "uri=" << intent.uri << " "
<< "uri=" << intent.uri << " " << "type=" << intent.type << " "
<< "type=" << intent.type << " " << "flags=" << intent.flags << " "
<< "flags=" << intent.flags << " " << "package=" << intent.package << " "
<< "package=" << intent.package << " " << "component=" << intent.component << " "
<< "component=" << intent.component << " " << "categories=[ ";
<< "categories=[ "; for (const auto &category : intent.categories) out << category << " ";
for (const auto &category : intent.categories) out << "]]";
out << category << " "; return out;
out << "]]";
return out;
} }
} // namespace android } // namespace android
} // namespace anbox } // namespace anbox

View file

@ -24,18 +24,17 @@
namespace anbox { namespace anbox {
namespace android { namespace android {
struct Intent { struct Intent {
std::string action; std::string action;
std::string uri; std::string uri;
std::string type; std::string type;
int flags = 0; int flags = 0;
std::string package; std::string package;
std::string component; std::string component;
std::vector<std::string> categories; std::vector<std::string> categories;
}; };
std::ostream& operator<<(std::ostream &out, const Intent &intent); std::ostream &operator<<(std::ostream &out, const Intent &intent);
} // namespace android } // namespace android
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -19,53 +19,51 @@
#include "anbox/utils.h" #include "anbox/utils.h"
#include <algorithm> #include <algorithm>
#include <iostream>
#include <fstream> #include <fstream>
#include <iostream>
namespace fs = boost::filesystem; namespace fs = boost::filesystem;
namespace anbox { namespace anbox {
namespace application { namespace application {
LauncherStorage::LauncherStorage(const fs::path &path) : LauncherStorage::LauncherStorage(const fs::path &path) : path_(path) {}
path_(path) {
}
LauncherStorage::~LauncherStorage() { LauncherStorage::~LauncherStorage() {}
}
void LauncherStorage::add(const Item &item) { void LauncherStorage::add(const Item &item) {
if (!fs::exists(path_)) if (!fs::exists(path_)) fs::create_directories(path_);
fs::create_directories(path_);
auto package_name = item.package; auto package_name = item.package;
std::replace(package_name.begin(), package_name.end(), '.', '-'); std::replace(package_name.begin(), package_name.end(), '.', '-');
const auto item_path = path_ / utils::string_format("anbox-%s.desktop", package_name); const auto item_path =
std::string exec = "anbox launch "; path_ / utils::string_format("anbox-%s.desktop", package_name);
std::string exec = "anbox launch ";
if (!item.launch_intent.action.empty()) if (!item.launch_intent.action.empty())
exec += utils::string_format("--action=%s ", item.launch_intent.action); exec += utils::string_format("--action=%s ", item.launch_intent.action);
if (!item.launch_intent.type.empty()) if (!item.launch_intent.type.empty())
exec += utils::string_format("--type=%s ", item.launch_intent.type); exec += utils::string_format("--type=%s ", item.launch_intent.type);
if (!item.launch_intent.uri.empty()) if (!item.launch_intent.uri.empty())
exec += utils::string_format("--uri=%s ", item.launch_intent.uri); exec += utils::string_format("--uri=%s ", item.launch_intent.uri);
if (!item.launch_intent.package.empty()) if (!item.launch_intent.package.empty())
exec += utils::string_format("--package=%s ", item.launch_intent.package); exec += utils::string_format("--package=%s ", item.launch_intent.package);
if (!item.launch_intent.component.empty()) if (!item.launch_intent.component.empty())
exec += utils::string_format("--component=%s ", item.launch_intent.component); exec +=
utils::string_format("--component=%s ", item.launch_intent.component);
std::ofstream f(item_path.string()); std::ofstream f(item_path.string());
f << "[Desktop Entry]" << std::endl f << "[Desktop Entry]" << std::endl
<< "Name=" << item.package << std::endl << "Name=" << item.package << std::endl
<< "Exec=" << exec << std::endl << "Exec=" << exec << std::endl
<< "Terminal=false" << std::endl << "Terminal=false" << std::endl
<< "Type=Application" << std::endl << "Type=Application" << std::endl
<< "Encoding=UTF-8" << std::endl; << "Encoding=UTF-8" << std::endl;
f.close(); f.close();
} }
} // namespace application } // namespace application
} // namespace anbox } // namespace anbox

View file

@ -28,22 +28,22 @@
namespace anbox { namespace anbox {
namespace application { namespace application {
class LauncherStorage { class LauncherStorage {
public: public:
LauncherStorage(const boost::filesystem::path &path); LauncherStorage(const boost::filesystem::path &path);
~LauncherStorage(); ~LauncherStorage();
struct Item { struct Item {
std::string name; std::string name;
std::string package; std::string package;
android::Intent launch_intent; android::Intent launch_intent;
}; };
void add(const Item &item); void add(const Item &item);
private: private:
boost::filesystem::path path_; boost::filesystem::path path_;
}; };
} // namespace application } // namespace application
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -18,16 +18,16 @@
#ifndef ANBOX_APPLICATION_MANAGER_H_ #ifndef ANBOX_APPLICATION_MANAGER_H_
#define ANBOX_APPLICATION_MANAGER_H_ #define ANBOX_APPLICATION_MANAGER_H_
#include "anbox/do_not_copy_or_move.h"
#include "anbox/android/intent.h" #include "anbox/android/intent.h"
#include "anbox/do_not_copy_or_move.h"
#include <string> #include <string>
namespace anbox { namespace anbox {
class ApplicationManager : public DoNotCopyOrMove { class ApplicationManager : public DoNotCopyOrMove {
public: public:
virtual void launch(const android::Intent &intent) = 0; virtual void launch(const android::Intent &intent) = 0;
}; };
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -16,13 +16,13 @@
*/ */
#include "anbox/bridge/android_api_stub.h" #include "anbox/bridge/android_api_stub.h"
#include "anbox/rpc/channel.h"
#include "anbox/utils.h"
#include "anbox/config.h" #include "anbox/config.h"
#include "anbox/logger.h" #include "anbox/logger.h"
#include "anbox/rpc/channel.h"
#include "anbox/utils.h"
#include "anbox_rpc.pb.h"
#include "anbox_bridge.pb.h" #include "anbox_bridge.pb.h"
#include "anbox_rpc.pb.h"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
@ -30,101 +30,90 @@ namespace fs = boost::filesystem;
namespace anbox { namespace anbox {
namespace bridge { namespace bridge {
AndroidApiStub::AndroidApiStub() { AndroidApiStub::AndroidApiStub() {}
AndroidApiStub::~AndroidApiStub() {}
void AndroidApiStub::set_rpc_channel(
const std::shared_ptr<rpc::Channel> &channel) {
channel_ = channel;
} }
AndroidApiStub::~AndroidApiStub() { void AndroidApiStub::reset_rpc_channel() { channel_.reset(); }
}
void AndroidApiStub::set_rpc_channel(const std::shared_ptr<rpc::Channel> &channel) {
channel_ = channel;
}
void AndroidApiStub::reset_rpc_channel() {
channel_.reset();
}
void AndroidApiStub::ensure_rpc_channel() { void AndroidApiStub::ensure_rpc_channel() {
if (!channel_) if (!channel_) throw std::runtime_error("No remote client connected");
throw std::runtime_error("No remote client connected");
} }
void AndroidApiStub::launch(const android::Intent &intent) { void AndroidApiStub::launch(const android::Intent &intent) {
ensure_rpc_channel(); ensure_rpc_channel();
auto c = std::make_shared<Request<protobuf::rpc::Void>>(); auto c = std::make_shared<Request<protobuf::rpc::Void>>();
protobuf::bridge::LaunchApplication message; protobuf::bridge::LaunchApplication message;
{ {
std::lock_guard<decltype(mutex_)> lock(mutex_); std::lock_guard<decltype(mutex_)> lock(mutex_);
launch_wait_handle_.expect_result(); launch_wait_handle_.expect_result();
} }
auto launch_intent = message.mutable_intent(); auto launch_intent = message.mutable_intent();
if (!intent.action.empty()) if (!intent.action.empty()) launch_intent->set_action(intent.action);
launch_intent->set_action(intent.action);
if (!intent.uri.empty()) if (!intent.uri.empty()) launch_intent->set_uri(intent.uri);
launch_intent->set_uri(intent.uri);
if (!intent.type.empty()) if (!intent.type.empty()) launch_intent->set_type(intent.type);
launch_intent->set_type(intent.type);
if (!intent.package.empty()) if (!intent.package.empty()) launch_intent->set_package(intent.package);
launch_intent->set_package(intent.package);
if (!intent.component.empty()) if (!intent.component.empty()) launch_intent->set_component(intent.component);
launch_intent->set_component(intent.component);
for (const auto &category : intent.categories) { for (const auto &category : intent.categories) {
auto c = launch_intent->add_categories(); auto c = launch_intent->add_categories();
*c = category; *c = category;
} }
channel_->call_method("launch_application", channel_->call_method(
&message, "launch_application", &message, c->response.get(),
c->response.get(), google::protobuf::NewCallback(this, &AndroidApiStub::application_launched,
google::protobuf::NewCallback(this, &AndroidApiStub::application_launched, c.get())); c.get()));
launch_wait_handle_.wait_for_all(); launch_wait_handle_.wait_for_all();
if (c->response->has_error()) if (c->response->has_error()) throw std::runtime_error(c->response->error());
throw std::runtime_error(c->response->error());
} }
void AndroidApiStub::application_launched(Request<protobuf::rpc::Void> *request) { void AndroidApiStub::application_launched(
(void) request; Request<protobuf::rpc::Void> *request) {
launch_wait_handle_.result_received(); (void)request;
launch_wait_handle_.result_received();
} }
void AndroidApiStub::set_focused_task(const std::int32_t &id) { void AndroidApiStub::set_focused_task(const std::int32_t &id) {
ensure_rpc_channel(); ensure_rpc_channel();
auto c = std::make_shared<Request<protobuf::rpc::Void>>(); auto c = std::make_shared<Request<protobuf::rpc::Void>>();
protobuf::bridge::SetFocusedTask message; protobuf::bridge::SetFocusedTask message;
message.set_id(id); message.set_id(id);
{ {
std::lock_guard<decltype(mutex_)> lock(mutex_); std::lock_guard<decltype(mutex_)> lock(mutex_);
set_focused_task_handle_.expect_result(); set_focused_task_handle_.expect_result();
} }
channel_->call_method("set_focused_task", channel_->call_method("set_focused_task", &message, c->response.get(),
&message, google::protobuf::NewCallback(
c->response.get(), this, &AndroidApiStub::focused_task_set, c.get()));
google::protobuf::NewCallback(this, &AndroidApiStub::focused_task_set, c.get()));
set_focused_task_handle_.wait_for_all(); set_focused_task_handle_.wait_for_all();
if (c->response->has_error()) if (c->response->has_error()) throw std::runtime_error(c->response->error());
throw std::runtime_error(c->response->error());
} }
void AndroidApiStub::focused_task_set(Request<protobuf::rpc::Void> *request) { void AndroidApiStub::focused_task_set(Request<protobuf::rpc::Void> *request) {
(void) request; (void)request;
set_focused_task_handle_.result_received(); set_focused_task_handle_.result_received();
} }
} // namespace bridge } // namespace bridge
} // namespace anbox } // namespace anbox

View file

@ -28,43 +28,43 @@ namespace anbox {
namespace protobuf { namespace protobuf {
namespace rpc { namespace rpc {
class Void; class Void;
} // namespace bridge } // namespace bridge
} // namespace protobuf } // namespace protobuf
namespace rpc { namespace rpc {
class Channel; class Channel;
} // namespace rpc } // namespace rpc
namespace bridge { namespace bridge {
class AndroidApiStub : public anbox::ApplicationManager { class AndroidApiStub : public anbox::ApplicationManager {
public: public:
AndroidApiStub(); AndroidApiStub();
~AndroidApiStub(); ~AndroidApiStub();
void set_rpc_channel(const std::shared_ptr<rpc::Channel> &channel); void set_rpc_channel(const std::shared_ptr<rpc::Channel> &channel);
void reset_rpc_channel(); void reset_rpc_channel();
void launch(const android::Intent &intent) override; void launch(const android::Intent &intent) override;
void set_focused_task(const std::int32_t &id); void set_focused_task(const std::int32_t &id);
private: private:
void ensure_rpc_channel(); void ensure_rpc_channel();
template<typename Response> template <typename Response>
struct Request { struct Request {
Request() : response(std::make_shared<Response>()), success(true) { } Request() : response(std::make_shared<Response>()), success(true) {}
std::shared_ptr<Response> response; std::shared_ptr<Response> response;
bool success; bool success;
}; };
void application_launched(Request<protobuf::rpc::Void> *request); void application_launched(Request<protobuf::rpc::Void> *request);
void focused_task_set(Request<protobuf::rpc::Void> *request); void focused_task_set(Request<protobuf::rpc::Void> *request);
mutable std::mutex mutex_; mutable std::mutex mutex_;
std::shared_ptr<rpc::Channel> channel_; std::shared_ptr<rpc::Channel> channel_;
common::WaitHandle launch_wait_handle_; common::WaitHandle launch_wait_handle_;
common::WaitHandle set_focused_task_handle_; common::WaitHandle set_focused_task_handle_;
}; };
} // namespace bridge } // namespace bridge
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -17,83 +17,86 @@
#include "anbox/bridge/platform_api_skeleton.h" #include "anbox/bridge/platform_api_skeleton.h"
#include "anbox/application/launcher_storage.h" #include "anbox/application/launcher_storage.h"
#include "anbox/logger.h"
#include "anbox/wm/manager.h" #include "anbox/wm/manager.h"
#include "anbox/wm/window_state.h" #include "anbox/wm/window_state.h"
#include "anbox/logger.h"
#include "anbox_bridge.pb.h" #include "anbox_bridge.pb.h"
namespace anbox { namespace anbox {
namespace bridge { namespace bridge {
PlatformApiSkeleton::PlatformApiSkeleton(const std::shared_ptr<rpc::PendingCallCache> &pending_calls, PlatformApiSkeleton::PlatformApiSkeleton(
const std::shared_ptr<wm::Manager> &window_manager, const std::shared_ptr<rpc::PendingCallCache> &pending_calls,
const std::shared_ptr<application::LauncherStorage> &launcher_storage) : const std::shared_ptr<wm::Manager> &window_manager,
pending_calls_(pending_calls), const std::shared_ptr<application::LauncherStorage> &launcher_storage)
window_manager_(window_manager), : pending_calls_(pending_calls),
launcher_storage_(launcher_storage) { window_manager_(window_manager),
launcher_storage_(launcher_storage) {}
PlatformApiSkeleton::~PlatformApiSkeleton() {}
void PlatformApiSkeleton::handle_boot_finished_event(
const anbox::protobuf::bridge::BootFinishedEvent &event) {
(void)event;
if (boot_finished_handler_) boot_finished_handler_();
} }
PlatformApiSkeleton::~PlatformApiSkeleton() { void PlatformApiSkeleton::handle_window_state_update_event(
const anbox::protobuf::bridge::WindowStateUpdateEvent &event) {
auto convert_window_state = [](
const ::anbox::protobuf::bridge::WindowStateUpdateEvent_WindowState
&window) {
return wm::WindowState(
wm::Display::Id(window.display_id()), window.has_surface(),
graphics::Rect(window.frame_left(), window.frame_top(),
window.frame_right(), window.frame_bottom()),
window.package_name(), wm::Task::Id(window.task_id()),
wm::Stack::Id(window.stack_id()));
};
wm::WindowState::List updated;
for (int n = 0; n < event.windows_size(); n++) {
const auto window = event.windows(n);
updated.push_back(convert_window_state(window));
}
wm::WindowState::List removed;
for (int n = 0; n < event.removed_windows_size(); n++) {
const auto window = event.removed_windows(n);
removed.push_back(convert_window_state(window));
}
window_manager_->apply_window_state_update(updated, removed);
} }
void PlatformApiSkeleton::handle_boot_finished_event(const anbox::protobuf::bridge::BootFinishedEvent &event) { void PlatformApiSkeleton::handle_application_list_update_event(
(void) event; const anbox::protobuf::bridge::ApplicationListUpdateEvent &event) {
for (int n = 0; n < event.applications_size(); n++) {
application::LauncherStorage::Item item;
if (boot_finished_handler_) const auto app = event.applications(n);
boot_finished_handler_(); item.name = app.name();
item.package = app.package();
const auto li = app.launch_intent();
item.launch_intent.action = li.action();
item.launch_intent.uri = li.uri();
item.launch_intent.type = li.uri();
item.launch_intent.package = li.package();
item.launch_intent.component = li.component();
for (int m = 0; m < li.categories_size(); m++)
item.launch_intent.categories.push_back(li.categories(m));
// If the item is already stored it will be updated
launcher_storage_->add(item);
}
} }
void PlatformApiSkeleton::handle_window_state_update_event(const anbox::protobuf::bridge::WindowStateUpdateEvent &event) { void PlatformApiSkeleton::register_boot_finished_handler(
auto convert_window_state = [](const ::anbox::protobuf::bridge::WindowStateUpdateEvent_WindowState &window) { const std::function<void()> &action) {
return wm::WindowState( boot_finished_handler_ = action;
wm::Display::Id(window.display_id()),
window.has_surface(),
graphics::Rect(window.frame_left(), window.frame_top(), window.frame_right(), window.frame_bottom()),
window.package_name(),
wm::Task::Id(window.task_id()),
wm::Stack::Id(window.stack_id()));
};
wm::WindowState::List updated;
for (int n = 0; n < event.windows_size(); n++) {
const auto window = event.windows(n);
updated.push_back(convert_window_state(window));
}
wm::WindowState::List removed;
for (int n = 0; n < event.removed_windows_size(); n++) {
const auto window = event.removed_windows(n);
removed.push_back(convert_window_state(window));
}
window_manager_->apply_window_state_update(updated, removed);
} }
} // namespace bridge
void PlatformApiSkeleton::handle_application_list_update_event(const anbox::protobuf::bridge::ApplicationListUpdateEvent &event) { } // namespace anbox
for (int n = 0; n < event.applications_size(); n++) {
application::LauncherStorage::Item item;
const auto app = event.applications(n);
item.name = app.name();
item.package = app.package();
const auto li = app.launch_intent();
item.launch_intent.action = li.action();
item.launch_intent.uri = li.uri();
item.launch_intent.type = li.uri();
item.launch_intent.package = li.package();
item.launch_intent.component = li.component();
for (int m = 0; m < li.categories_size(); m++)
item.launch_intent.categories.push_back(li.categories(m));
// If the item is already stored it will be updated
launcher_storage_->add(item);
}
}
void PlatformApiSkeleton::register_boot_finished_handler(const std::function<void()> &action) {
boot_finished_handler_ = action;
}
} // namespace bridge
} // namespace anbox

View file

@ -23,50 +23,54 @@
namespace google { namespace google {
namespace protobuf { namespace protobuf {
class Closure; class Closure;
} // namespace protobuf } // namespace protobuf
} // namespace google } // namespace google
namespace anbox { namespace anbox {
namespace protobuf { namespace protobuf {
namespace rpc { namespace rpc {
class Void; class Void;
} // namespace rpc } // namespace rpc
namespace bridge { namespace bridge {
class BootFinishedEvent; class BootFinishedEvent;
class WindowStateUpdateEvent; class WindowStateUpdateEvent;
class ApplicationListUpdateEvent; class ApplicationListUpdateEvent;
} // namespace bridge } // namespace bridge
} // namespace protobuf } // namespace protobuf
namespace rpc { namespace rpc {
class PendingCallCache; class PendingCallCache;
} // namespace rpc } // namespace rpc
namespace wm { namespace wm {
class Manager; class Manager;
} // namespace wm } // namespace wm
namespace application { namespace application {
class LauncherStorage; class LauncherStorage;
} // namespace application } // namespace application
namespace bridge { namespace bridge {
class PlatformApiSkeleton { class PlatformApiSkeleton {
public: public:
PlatformApiSkeleton(const std::shared_ptr<rpc::PendingCallCache> &pending_calls, PlatformApiSkeleton(
const std::shared_ptr<wm::Manager> &window_manager, const std::shared_ptr<rpc::PendingCallCache> &pending_calls,
const std::shared_ptr<application::LauncherStorage> &launcher_storage); const std::shared_ptr<wm::Manager> &window_manager,
virtual ~PlatformApiSkeleton(); const std::shared_ptr<application::LauncherStorage> &launcher_storage);
virtual ~PlatformApiSkeleton();
void handle_boot_finished_event(const anbox::protobuf::bridge::BootFinishedEvent &event); void handle_boot_finished_event(
void handle_window_state_update_event(const anbox::protobuf::bridge::WindowStateUpdateEvent &event); const anbox::protobuf::bridge::BootFinishedEvent &event);
void handle_application_list_update_event(const anbox::protobuf::bridge::ApplicationListUpdateEvent &event); void handle_window_state_update_event(
const anbox::protobuf::bridge::WindowStateUpdateEvent &event);
void handle_application_list_update_event(
const anbox::protobuf::bridge::ApplicationListUpdateEvent &event);
void register_boot_finished_handler(const std::function<void()> &action); void register_boot_finished_handler(const std::function<void()> &action);
private: private:
std::shared_ptr<rpc::PendingCallCache> pending_calls_; std::shared_ptr<rpc::PendingCallCache> pending_calls_;
std::shared_ptr<wm::Manager> window_manager_; std::shared_ptr<wm::Manager> window_manager_;
std::shared_ptr<application::LauncherStorage> launcher_storage_; std::shared_ptr<application::LauncherStorage> launcher_storage_;
std::function<void()> boot_finished_handler_; std::function<void()> boot_finished_handler_;
}; };
} // namespace bridge } // namespace bridge
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -17,41 +17,40 @@
#include "anbox/bridge/platform_message_processor.h" #include "anbox/bridge/platform_message_processor.h"
#include "anbox/bridge/platform_api_skeleton.h" #include "anbox/bridge/platform_api_skeleton.h"
#include "anbox/rpc/template_message_processor.h"
#include "anbox/logger.h" #include "anbox/logger.h"
#include "anbox/rpc/template_message_processor.h"
#include "anbox_bridge.pb.h" #include "anbox_bridge.pb.h"
namespace anbox { namespace anbox {
namespace bridge { namespace bridge {
PlatformMessageProcessor::PlatformMessageProcessor(const std::shared_ptr<network::MessageSender> &sender, PlatformMessageProcessor::PlatformMessageProcessor(
const std::shared_ptr<PlatformApiSkeleton> &server, const std::shared_ptr<network::MessageSender> &sender,
const std::shared_ptr<rpc::PendingCallCache> &pending_calls) : const std::shared_ptr<PlatformApiSkeleton> &server,
rpc::MessageProcessor(sender, pending_calls), const std::shared_ptr<rpc::PendingCallCache> &pending_calls)
server_(server) { : rpc::MessageProcessor(sender, pending_calls), server_(server) {}
PlatformMessageProcessor::~PlatformMessageProcessor() {}
void PlatformMessageProcessor::dispatch(rpc::Invocation const &invocation) {}
void PlatformMessageProcessor::process_event_sequence(
const std::string &raw_events) {
anbox::protobuf::bridge::EventSequence seq;
if (!seq.ParseFromString(raw_events)) {
WARNING("Failed to parse events from raw string");
return;
}
if (seq.has_boot_finished())
server_->handle_boot_finished_event(seq.boot_finished());
if (seq.has_window_state_update())
server_->handle_window_state_update_event(seq.window_state_update());
if (seq.has_application_list_update())
server_->handle_application_list_update_event(
seq.application_list_update());
} }
} // namespace anbox
PlatformMessageProcessor::~PlatformMessageProcessor() { } // namespace network
}
void PlatformMessageProcessor::dispatch(rpc::Invocation const& invocation) {
}
void PlatformMessageProcessor::process_event_sequence(const std::string &raw_events) {
anbox::protobuf::bridge::EventSequence seq;
if (!seq.ParseFromString(raw_events)) {
WARNING("Failed to parse events from raw string");
return;
}
if (seq.has_boot_finished())
server_->handle_boot_finished_event(seq.boot_finished());
if (seq.has_window_state_update())
server_->handle_window_state_update_event(seq.window_state_update());
if (seq.has_application_list_update())
server_->handle_application_list_update_event(seq.application_list_update());
}
} // namespace anbox
} // namespace network

View file

@ -24,19 +24,20 @@ namespace anbox {
namespace bridge { namespace bridge {
class PlatformApiSkeleton; class PlatformApiSkeleton;
class PlatformMessageProcessor : public rpc::MessageProcessor { class PlatformMessageProcessor : public rpc::MessageProcessor {
public: public:
PlatformMessageProcessor(const std::shared_ptr<network::MessageSender> &sender, PlatformMessageProcessor(
const std::shared_ptr<PlatformApiSkeleton> &server, const std::shared_ptr<network::MessageSender> &sender,
const std::shared_ptr<rpc::PendingCallCache> &pending_calls); const std::shared_ptr<PlatformApiSkeleton> &server,
~PlatformMessageProcessor(); const std::shared_ptr<rpc::PendingCallCache> &pending_calls);
~PlatformMessageProcessor();
void dispatch(rpc::Invocation const& invocation) override; void dispatch(rpc::Invocation const &invocation) override;
void process_event_sequence(const std::string &event) override; void process_event_sequence(const std::string &event) override;
private: private:
std::shared_ptr<PlatformApiSkeleton> server_; std::shared_ptr<PlatformApiSkeleton> server_;
}; };
} // namespace anbox } // namespace anbox
} // namespace network } // namespace network
#endif #endif

View file

@ -28,258 +28,216 @@ namespace po = boost::program_options;
namespace { namespace {
namespace pattern { namespace pattern {
static constexpr const char* help_for_command_with_subcommands = static constexpr const char* help_for_command_with_subcommands =
"NAME:\n" "NAME:\n"
" %1% - %2%\n" " %1% - %2%\n"
"\n" "\n"
"USAGE:\n" "USAGE:\n"
" %3% [command options] [arguments...]"; " %3% [command options] [arguments...]";
static constexpr const char* commands = "COMMANDS:"; static constexpr const char* commands = "COMMANDS:";
static constexpr const char* command = " %1% %2%"; static constexpr const char* command = " %1% %2%";
static constexpr const char* options = "OPTIONS:"; static constexpr const char* options = "OPTIONS:";
static constexpr const char* option = " --%1% %2%"; static constexpr const char* option = " --%1% %2%";
} }
void add_to_desc_for_flags(po::options_description& desc, const std::set<cli::Flag::Ptr>& flags) void add_to_desc_for_flags(po::options_description& desc,
{ const std::set<cli::Flag::Ptr>& flags) {
for (auto flag : flags) for (auto flag : flags) {
{ auto v = po::value<std::string>()->notifier(
auto v = po::value<std::string>()->notifier([flag](const std::string& s) [flag](const std::string& s) { flag->notify(s); });
{ desc.add_options()(flag->name().as_string().c_str(), v,
flag->notify(s); flag->description().as_string().c_str());
}); }
desc.add_options()(flag->name().as_string().c_str(), v, flag->description().as_string().c_str());
}
} }
} }
std::vector<std::string> cli::args(int argc, char **argv) std::vector<std::string> cli::args(int argc, char** argv) {
{ std::vector<std::string> result;
std::vector<std::string> result; for (int i = 1; i < argc; i++) result.push_back(argv[i]);
for (int i = 1; i < argc; i++) result.push_back(argv[i]); return result;
return result;
} }
const cli::Name& cli::Flag::name() const const cli::Name& cli::Flag::name() const { return name_; }
{
return name_;
}
const cli::Description& cli::Flag::description() const const cli::Description& cli::Flag::description() const { return description_; }
{
return description_;
}
cli::Flag::Flag(const Name& name, const Description& description) cli::Flag::Flag(const Name& name, const Description& description)
: name_{name}, : name_{name}, description_{description} {}
description_{description}
{ cli::Command::FlagsWithInvalidValue::FlagsWithInvalidValue()
: std::runtime_error{"Flags with invalid value"} {}
cli::Command::FlagsMissing::FlagsMissing()
: std::runtime_error{"Flags are missing in command invocation"} {}
cli::Name cli::Command::name() const { return name_; }
cli::Usage cli::Command::usage() const { return usage_; }
cli::Description cli::Command::description() const { return description_; }
cli::Command::Command(const cli::Name& name, const cli::Usage& usage,
const cli::Description& description)
: name_(name), usage_(usage), description_(description) {}
cli::CommandWithSubcommands::CommandWithSubcommands(
const Name& name, const Usage& usage, const Description& description)
: Command{name, usage, description} {
command(std::make_shared<cmd::Help>(*this));
} }
cli::Command::FlagsWithInvalidValue::FlagsWithInvalidValue() : std::runtime_error{"Flags with invalid value"} cli::CommandWithSubcommands& cli::CommandWithSubcommands::command(
{ const Command::Ptr& command) {
commands_[command->name().as_string()] = command;
return *this;
} }
cli::Command::FlagsMissing::FlagsMissing() : std::runtime_error{"Flags are missing in command invocation"} cli::CommandWithSubcommands& cli::CommandWithSubcommands::flag(
{ const Flag::Ptr& flag) {
flags_.insert(flag);
return *this;
} }
cli::Name cli::Command::name() const void cli::CommandWithSubcommands::help(std::ostream& out) {
{ out << boost::format(pattern::help_for_command_with_subcommands) %
return name_; name().as_string() % usage().as_string() % name().as_string()
<< std::endl;
if (flags_.size() > 0) {
out << std::endl << pattern::options << std::endl;
for (const auto& flag : flags_)
out << boost::format(pattern::option) % flag->name() % flag->description()
<< std::endl;
}
if (commands_.size() > 0) {
out << std::endl << pattern::commands << std::endl;
for (const auto& cmd : commands_) {
if (cmd.second)
out << boost::format(pattern::command) % cmd.second->name() %
cmd.second->description()
<< std::endl;
}
}
} }
cli::Usage cli::Command::usage() const int cli::CommandWithSubcommands::run(const cli::Command::Context& ctxt) {
{ po::positional_options_description pdesc;
return usage_; pdesc.add("command", 1);
}
cli::Description cli::Command::description() const po::options_description desc("Options");
{ desc.add_options()("command", po::value<std::string>()->required(),
return description_; "the command to be executed");
}
cli::Command::Command(const cli::Name& name, const cli::Usage& usage, const cli::Description& description) add_to_desc_for_flags(desc, flags_);
: name_(name),
usage_(usage),
description_(description)
{
}
cli::CommandWithSubcommands::CommandWithSubcommands(const Name& name, const Usage& usage, const Description& description) try {
: Command{name, usage, description} po::variables_map vm;
{ auto parsed = po::command_line_parser(ctxt.args)
command(std::make_shared<cmd::Help>(*this)); .options(desc)
} .positional(pdesc)
.style(po::command_line_style::unix_style)
.allow_unregistered()
.run();
cli::CommandWithSubcommands& cli::CommandWithSubcommands::command(const Command::Ptr& command) po::store(parsed, vm);
{ po::notify(vm);
commands_[command->name().as_string()] = command;
return *this;
}
cli::CommandWithSubcommands& cli::CommandWithSubcommands::flag(const Flag::Ptr& flag) auto cmd = commands_[vm["command"].as<std::string>()];
{ if (!cmd) {
flags_.insert(flag); ctxt.cout << "Unknown command '" << vm["command"].as<std::string>() << "'"
return *this; << std::endl;
} help(ctxt.cout);
return EXIT_FAILURE;
void cli::CommandWithSubcommands::help(std::ostream& out)
{
out << boost::format(pattern::help_for_command_with_subcommands)
% name().as_string() % usage().as_string()
% name().as_string() << std::endl;
if (flags_.size() > 0)
{
out << std::endl << pattern::options << std::endl;
for (const auto& flag : flags_)
out << boost::format(pattern::option) % flag->name() % flag->description() << std::endl;
}
if (commands_.size() > 0)
{
out << std::endl << pattern::commands << std::endl;
for (const auto& cmd : commands_) {
if (cmd.second)
out << boost::format(pattern::command) % cmd.second->name() % cmd.second->description() << std::endl;
}
}
}
int cli::CommandWithSubcommands::run(const cli::Command::Context& ctxt)
{
po::positional_options_description pdesc;
pdesc.add("command", 1);
po::options_description desc("Options");
desc.add_options()("command", po::value<std::string>()->required(), "the command to be executed");
add_to_desc_for_flags(desc, flags_);
try
{
po::variables_map vm;
auto parsed = po::command_line_parser(ctxt.args)
.options(desc)
.positional(pdesc)
.style(po::command_line_style::unix_style)
.allow_unregistered()
.run();
po::store(parsed, vm);
po::notify(vm);
auto cmd = commands_[vm["command"].as<std::string>()];
if (!cmd)
{
ctxt.cout << "Unknown command '" << vm["command"].as<std::string>() << "'" << std::endl;
help(ctxt.cout);
return EXIT_FAILURE;
}
return cmd->run(cli::Command::Context
{
ctxt.cin,
ctxt.cout,
po::collect_unrecognized(parsed.options, po::include_positional)
});
}
catch (const po::error& e)
{
ctxt.cout << e.what() << std::endl;
help(ctxt.cout);
return EXIT_FAILURE;
} }
return cmd->run(cli::Command::Context{
ctxt.cin, ctxt.cout,
po::collect_unrecognized(parsed.options, po::include_positional)});
} catch (const po::error& e) {
ctxt.cout << e.what() << std::endl;
help(ctxt.cout);
return EXIT_FAILURE; return EXIT_FAILURE;
}
return EXIT_FAILURE;
} }
cli::CommandWithFlagsAndAction::CommandWithFlagsAndAction(const Name& name, const Usage& usage, const Description& description) cli::CommandWithFlagsAndAction::CommandWithFlagsAndAction(
: Command{name, usage, description} const Name& name, const Usage& usage, const Description& description)
{ : Command{name, usage, description} {}
cli::CommandWithFlagsAndAction& cli::CommandWithFlagsAndAction::flag(
const Flag::Ptr& flag) {
flags_.insert(flag);
return *this;
} }
cli::CommandWithFlagsAndAction& cli::CommandWithFlagsAndAction::flag(const Flag::Ptr& flag) cli::CommandWithFlagsAndAction& cli::CommandWithFlagsAndAction::action(
{ const Action& action) {
flags_.insert(flag); action_ = action;
return *this; return *this;
} }
cli::CommandWithFlagsAndAction& cli::CommandWithFlagsAndAction::action(const Action& action) int cli::CommandWithFlagsAndAction::run(const Context& ctxt) {
{ po::options_description cd(name().as_string());
action_ = action;
return *this;
}
int cli::CommandWithFlagsAndAction::run(const Context& ctxt) bool help_requested{false};
{ cd.add_options()("help", po::bool_switch(&help_requested),
po::options_description cd(name().as_string()); "produces a help message");
bool help_requested{false}; add_to_desc_for_flags(cd, flags_);
cd.add_options()("help", po::bool_switch(&help_requested), "produces a help message");
add_to_desc_for_flags(cd, flags_); try {
po::variables_map vm;
auto parsed = po::command_line_parser(ctxt.args)
.options(cd)
.style(po::command_line_style::unix_style)
.allow_unregistered()
.run();
po::store(parsed, vm);
po::notify(vm);
try if (help_requested) {
{ help(ctxt.cout);
po::variables_map vm; return EXIT_SUCCESS;
auto parsed = po::command_line_parser(ctxt.args).options(cd).style(po::command_line_style::unix_style).allow_unregistered().run();
po::store(parsed, vm);
po::notify(vm);
if (help_requested)
{
help(ctxt.cout);
return EXIT_SUCCESS;
}
return action_(cli::Command::Context
{
ctxt.cin,
ctxt.cout,
po::collect_unrecognized(parsed.options, po::exclude_positional)
});
}
catch (const po::error& e)
{
ctxt.cout << e.what() << std::endl;
help(ctxt.cout);
return EXIT_FAILURE;
} }
return action_(cli::Command::Context{
ctxt.cin, ctxt.cout,
po::collect_unrecognized(parsed.options, po::exclude_positional)});
} catch (const po::error& e) {
ctxt.cout << e.what() << std::endl;
help(ctxt.cout);
return EXIT_FAILURE; return EXIT_FAILURE;
}
return EXIT_FAILURE;
} }
void cli::CommandWithFlagsAndAction::help(std::ostream& out) void cli::CommandWithFlagsAndAction::help(std::ostream& out) {
{ out << boost::format(pattern::help_for_command_with_subcommands) %
out << boost::format(pattern::help_for_command_with_subcommands) name().as_string() % description().as_string() % name().as_string()
% name().as_string() % description().as_string() << std::endl;
% name().as_string() << std::endl;
if (flags_.size() > 0) if (flags_.size() > 0) {
{ out << std::endl << boost::format(pattern::options) << std::endl;
out << std::endl << boost::format(pattern::options) << std::endl; for (const auto& flag : flags_)
for (const auto& flag : flags_) out << boost::format(pattern::option) % flag->name() % flag->description()
out << boost::format(pattern::option) % flag->name() % flag->description() << std::endl; << std::endl;
} }
} }
cli::cmd::Help::Help(Command& cmd) cli::cmd::Help::Help(Command& cmd)
: Command{cli::Name{"help"}, cli::Usage{"prints a short help message"}, cli::Description{"prints a short help message"}}, : Command{cli::Name{"help"}, cli::Usage{"prints a short help message"},
command{cmd} cli::Description{"prints a short help message"}},
{ command{cmd} {}
}
// From Command // From Command
int cli::cmd::Help::run(const Context &context) int cli::cmd::Help::run(const Context& context) {
{ command.help(context.cout);
command.help(context.cout); return EXIT_FAILURE;
return EXIT_FAILURE;
} }
void cli::cmd::Help::help(std::ostream &out) void cli::cmd::Help::help(std::ostream& out) { command.help(out); }
{
command.help(out);
}

View file

@ -34,46 +34,38 @@
namespace anbox { namespace anbox {
namespace cli { namespace cli {
template<std::size_t max> template <std::size_t max>
class SizeConstrainedString class SizeConstrainedString {
{ public:
public: SizeConstrainedString(const std::string& s) : s{s} {
SizeConstrainedString(const std::string& s) : s{s} if (s.size() > max)
{ throw std::logic_error{"Max size exceeded " + std::to_string(max)};
if(s.size() > max) }
throw std::logic_error{"Max size exceeded " + std::to_string(max)};
}
const std::string& as_string() const const std::string& as_string() const { return s; }
{
return s;
}
operator std::string() const operator std::string() const { return s; }
{
return s;
}
private: private:
std::string s; std::string s;
}; };
template<std::size_t max> template <std::size_t max>
bool operator<(const SizeConstrainedString<max>& lhs, const SizeConstrainedString<max>& rhs) bool operator<(const SizeConstrainedString<max>& lhs,
{ const SizeConstrainedString<max>& rhs) {
return lhs.as_string() < rhs.as_string(); return lhs.as_string() < rhs.as_string();
} }
template<std::size_t max> template <std::size_t max>
bool operator==(const SizeConstrainedString<max>& lhs, const SizeConstrainedString<max>& rhs) bool operator==(const SizeConstrainedString<max>& lhs,
{ const SizeConstrainedString<max>& rhs) {
return lhs.as_string() == rhs.as_string(); return lhs.as_string() == rhs.as_string();
} }
template<std::size_t max> template <std::size_t max>
std::ostream& operator<<(std::ostream& out, const SizeConstrainedString<max>& scs) std::ostream& operator<<(std::ostream& out,
{ const SizeConstrainedString<max>& scs) {
return out << std::setw(max) << std::left << scs.as_string(); return out << std::setw(max) << std::left << scs.as_string();
} }
// We are imposing size constraints to ensure a consistent CLI layout. // We are imposing size constraints to ensure a consistent CLI layout.
@ -82,246 +74,238 @@ typedef SizeConstrainedString<60> Usage;
typedef SizeConstrainedString<60> Description; typedef SizeConstrainedString<60> Description;
/// @brief Flag models an input parameter to a command. /// @brief Flag models an input parameter to a command.
class Flag : public DoNotCopyOrMove class Flag : public DoNotCopyOrMove {
{ public:
public: // Safe us some typing.
// Safe us some typing. typedef std::shared_ptr<Flag> Ptr;
typedef std::shared_ptr<Flag> Ptr;
/// @brief notify announces a new value to the flag. /// @brief notify announces a new value to the flag.
virtual void notify(const std::string& value) = 0; virtual void notify(const std::string& value) = 0;
/// @brief name returns the name of the Flag. /// @brief name returns the name of the Flag.
const Name& name() const; const Name& name() const;
/// @brief description returns a human-readable description of the flag. /// @brief description returns a human-readable description of the flag.
const Description& description() const; const Description& description() const;
protected: protected:
/// @brief Flag creates a new instance, initializing name and description /// @brief Flag creates a new instance, initializing name and description
/// from the given values. /// from the given values.
Flag(const Name& name, const Description& description); Flag(const Name& name, const Description& description);
private: private:
Name name_; Name name_;
Description description_; Description description_;
}; };
/// @brief TypedFlag implements Flag relying on operator<< and operator>> to read/write values to/from strings. /// @brief TypedFlag implements Flag relying on operator<< and operator>> to
template<typename T> /// read/write values to/from strings.
class TypedFlag : public Flag template <typename T>
{ class TypedFlag : public Flag {
public: public:
typedef std::shared_ptr<TypedFlag<T>> Ptr; typedef std::shared_ptr<TypedFlag<T>> Ptr;
TypedFlag(const Name& name, const Description& description) : Flag{name, description} TypedFlag(const Name& name, const Description& description)
{ : Flag{name, description} {}
}
/// @brief value installs the given value in the flag. /// @brief value installs the given value in the flag.
TypedFlag& value(const T& value) TypedFlag& value(const T& value) {
{ value_ = value;
value_ = value; return *this;
return *this; }
}
/// @brief value returns the optional value associated with the flag. /// @brief value returns the optional value associated with the flag.
const Optional<T>& value() const const Optional<T>& value() const { return value_; }
{
return value_;
}
/// @brief notify tries to unwrap a value of type T from value. /// @brief notify tries to unwrap a value of type T from value.
void notify(const std::string& s) override void notify(const std::string& s) override {
{ std::stringstream ss{s};
std::stringstream ss{s}; T value;
T value; ss >> value; ss >> value;
value_ = value; value_ = value;
} }
private: private:
Optional<T> value_; Optional<T> value_;
}; };
/// @brief TypedReferenceFlag implements Flag, relying on operator<</>> to convert to/from string representations, /// @brief TypedReferenceFlag implements Flag, relying on operator<</>> to
/// convert to/from string representations,
/// updating the given mutable reference to a value of type T. /// updating the given mutable reference to a value of type T.
template<typename T> template <typename T>
class TypedReferenceFlag : public Flag class TypedReferenceFlag : public Flag {
{ public:
public: // Safe us some typing.
// Safe us some typing. typedef std::shared_ptr<TypedReferenceFlag<T>> Ptr;
typedef std::shared_ptr<TypedReferenceFlag<T>> Ptr;
/// @brief TypedReferenceFlag initializes a new instance with name, description and value. /// @brief TypedReferenceFlag initializes a new instance with name,
TypedReferenceFlag(const Name& name, const Description& description, T& value) /// description and value.
: Flag{name, description}, TypedReferenceFlag(const Name& name, const Description& description, T& value)
value_{value} : Flag{name, description}, value_{value} {}
{
}
/// @brief notify tries to unwrap a value of type T from value, /// @brief notify tries to unwrap a value of type T from value,
/// relying on operator>> to read from given string s. /// relying on operator>> to read from given string s.
void notify(const std::string& s) override void notify(const std::string& s) override {
{ std::stringstream ss{s};
std::stringstream ss{s}; ss >> value_.get();
ss >> value_.get(); }
}
private: private:
std::reference_wrapper<T> value_; std::reference_wrapper<T> value_;
}; };
/// @brief OptionalTypedReferenceFlag handles Optional<T> references, making sure that /// @brief OptionalTypedReferenceFlag handles Optional<T> references, making
/// a value is always read on notify, even if the Optional<T> wasn't initialized previously. /// sure that
template<typename T> /// a value is always read on notify, even if the Optional<T> wasn't initialized
class OptionalTypedReferenceFlag : public Flag /// previously.
{ template <typename T>
public: class OptionalTypedReferenceFlag : public Flag {
typedef std::shared_ptr<OptionalTypedReferenceFlag<T>> Ptr; public:
typedef std::shared_ptr<OptionalTypedReferenceFlag<T>> Ptr;
OptionalTypedReferenceFlag(const Name& name, const Description& description, Optional<T>& value) OptionalTypedReferenceFlag(const Name& name, const Description& description,
: Flag{name, description}, Optional<T>& value)
value_{value} : Flag{name, description}, value_{value} {}
{
}
/// @brief notify tries to unwrap a value of type T from value. /// @brief notify tries to unwrap a value of type T from value.
void notify(const std::string& s) override void notify(const std::string& s) override {
{ std::stringstream ss{s};
std::stringstream ss{s}; T value; ss >> value; T value;
value_.get() = value; ss >> value;
} value_.get() = value;
}
private: private:
std::reference_wrapper<Optional<T>> value_; std::reference_wrapper<Optional<T>> value_;
}; };
/// @brief Command abstracts an individual command available from the daemon. /// @brief Command abstracts an individual command available from the daemon.
class Command : public DoNotCopyOrMove class Command : public DoNotCopyOrMove {
{ public:
public: // Safe us some typing
// Safe us some typing typedef std::shared_ptr<Command> Ptr;
typedef std::shared_ptr<Command> Ptr;
/// @brief FlagsMissing is thrown if at least one required flag is missing. /// @brief FlagsMissing is thrown if at least one required flag is missing.
struct FlagsMissing : public std::runtime_error struct FlagsMissing : public std::runtime_error {
{ /// @brief FlagsMissing initializes a new instance.
/// @brief FlagsMissing initializes a new instance. FlagsMissing();
FlagsMissing(); };
};
/// @brief FlagsWithWrongValue is thrown if a value passed on the command line is invalid. /// @brief FlagsWithWrongValue is thrown if a value passed on the command line
struct FlagsWithInvalidValue : public std::runtime_error /// is invalid.
{ struct FlagsWithInvalidValue : public std::runtime_error {
/// @brief FlagsWithInvalidValue initializes a new instance. /// @brief FlagsWithInvalidValue initializes a new instance.
FlagsWithInvalidValue(); FlagsWithInvalidValue();
}; };
/// @brief Context bundles information passed to Command::run invocations. /// @brief Context bundles information passed to Command::run invocations.
struct Context struct Context {
{ std::istream& cin; ///< The std::istream that should be used for reading.
std::istream& cin; ///< The std::istream that should be used for reading. std::ostream& cout; ///< The std::ostream that should be used for writing.
std::ostream& cout; ///< The std::ostream that should be used for writing. std::vector<std::string> args; ///< The command line args.
std::vector<std::string> args; ///< The command line args. };
};
/// @brief name returns the Name of the command. /// @brief name returns the Name of the command.
virtual Name name() const; virtual Name name() const;
/// @brief usage returns a short usage string for the command. /// @brief usage returns a short usage string for the command.
virtual Usage usage() const; virtual Usage usage() const;
/// @brief description returns a longer string explaining the command. /// @brief description returns a longer string explaining the command.
virtual Description description() const; virtual Description description() const;
/// @brief run puts the command to execution. /// @brief run puts the command to execution.
virtual int run(const Context& context) = 0; virtual int run(const Context& context) = 0;
/// @brief help prints information about a command to out. /// @brief help prints information about a command to out.
virtual void help(std::ostream& out) = 0; virtual void help(std::ostream& out) = 0;
protected: protected:
/// @brief Command initializes a new instance with the given name, usage and description. /// @brief Command initializes a new instance with the given name, usage and
Command(const Name& name, const Usage& usage, const Description& description); /// description.
Command(const Name& name, const Usage& usage, const Description& description);
/// @brief name adjusts the name of the command to n. /// @brief name adjusts the name of the command to n.
// virtual void name(const Name& n); // virtual void name(const Name& n);
/// @brief usage adjusts the usage string of the comand to u. /// @brief usage adjusts the usage string of the comand to u.
// virtual void usage(const Usage& u); // virtual void usage(const Usage& u);
/// @brief description adjusts the description string of the command to d. /// @brief description adjusts the description string of the command to d.
// virtual void description(const Description& d); // virtual void description(const Description& d);
private: private:
Name name_; Name name_;
Usage usage_; Usage usage_;
Description description_; Description description_;
}; };
/// @brief CommandWithSubcommands implements Command, selecting one of a set of actions. /// @brief CommandWithSubcommands implements Command, selecting one of a set of
class CommandWithSubcommands : public Command /// actions.
{ class CommandWithSubcommands : public Command {
public: public:
typedef std::shared_ptr<CommandWithSubcommands> Ptr; typedef std::shared_ptr<CommandWithSubcommands> Ptr;
typedef std::function<int(const Context&)> Action; typedef std::function<int(const Context&)> Action;
/// @brief CommandWithSubcommands initializes a new instance with the given name, usage and description /// @brief CommandWithSubcommands initializes a new instance with the given
CommandWithSubcommands(const Name& name, const Usage& usage, const Description& description); /// name, usage and description
CommandWithSubcommands(const Name& name, const Usage& usage,
const Description& description);
/// @brief command adds the given command to the set of known commands. /// @brief command adds the given command to the set of known commands.
CommandWithSubcommands& command(const Command::Ptr& command); CommandWithSubcommands& command(const Command::Ptr& command);
/// @brief flag adds the given flag to the set of known flags. /// @brief flag adds the given flag to the set of known flags.
CommandWithSubcommands& flag(const Flag::Ptr& flag); CommandWithSubcommands& flag(const Flag::Ptr& flag);
// From Command // From Command
int run(const Context& context) override; int run(const Context& context) override;
void help(std::ostream &out) override; void help(std::ostream& out) override;
private: private:
std::unordered_map<std::string, Command::Ptr> commands_; std::unordered_map<std::string, Command::Ptr> commands_;
std::set<Flag::Ptr> flags_; std::set<Flag::Ptr> flags_;
}; };
/// @brief CommandWithFlagsAction implements Command, executing an Action after handling /// @brief CommandWithFlagsAction implements Command, executing an Action after
class CommandWithFlagsAndAction : public Command /// handling
{ class CommandWithFlagsAndAction : public Command {
public: public:
typedef std::shared_ptr<CommandWithFlagsAndAction> Ptr; typedef std::shared_ptr<CommandWithFlagsAndAction> Ptr;
typedef std::function<int(const Context&)> Action; typedef std::function<int(const Context&)> Action;
/// @brief CommandWithFlagsAndAction initializes a new instance with the given name, usage and description /// @brief CommandWithFlagsAndAction initializes a new instance with the given
CommandWithFlagsAndAction(const Name& name, const Usage& usage, const Description& description); /// name, usage and description
CommandWithFlagsAndAction(const Name& name, const Usage& usage,
const Description& description);
/// @brief flag adds the given flag to the set of known flags. /// @brief flag adds the given flag to the set of known flags.
CommandWithFlagsAndAction& flag(const Flag::Ptr& flag); CommandWithFlagsAndAction& flag(const Flag::Ptr& flag);
/// @brief action installs the given action. /// @brief action installs the given action.
CommandWithFlagsAndAction& action(const Action& action); CommandWithFlagsAndAction& action(const Action& action);
// From Command // From Command
int run(const Context& context) override; int run(const Context& context) override;
void help(std::ostream &out) override; void help(std::ostream& out) override;
private: private:
std::set<Flag::Ptr> flags_; std::set<Flag::Ptr> flags_;
Action action_; Action action_;
}; };
namespace cmd namespace cmd {
{
/// @brief HelpFor prints a help message for the given command on execution. /// @brief HelpFor prints a help message for the given command on execution.
class Help : public Command class Help : public Command {
{ public:
public: /// @brief HelpFor initializes a new instance with the given reference to a
/// @brief HelpFor initializes a new instance with the given reference to a cmd. /// cmd.
explicit Help(Command& cmd); explicit Help(Command& cmd);
// From Command // From Command
int run(const Context &context) override; int run(const Context& context) override;
void help(std::ostream &out) override; void help(std::ostream& out) override;
private: private:
/// @cond /// @cond
Command& command; Command& command;
/// @endcond /// @endcond
}; };
} }
@ -329,28 +313,31 @@ private:
std::vector<std::string> args(int argc, char** argv); std::vector<std::string> args(int argc, char** argv);
/// @brief make_flag returns a flag with the given name and description. /// @brief make_flag returns a flag with the given name and description.
template<typename T> template <typename T>
typename TypedFlag<T>::Ptr make_flag(const Name& name, const Description& description) typename TypedFlag<T>::Ptr make_flag(const Name& name,
{ const Description& description) {
return std::make_shared<TypedFlag<T>>(name, description); return std::make_shared<TypedFlag<T>>(name, description);
} }
/// @brief make_flag returns a flag with the given name and description, notifying updates to value. /// @brief make_flag returns a flag with the given name and description,
template<typename T> /// notifying updates to value.
typename TypedReferenceFlag<T>::Ptr make_flag(const Name& name, const Description& desc, T& value) template <typename T>
{ typename TypedReferenceFlag<T>::Ptr make_flag(const Name& name,
return std::make_shared<TypedReferenceFlag<T>>(name, desc, value); const Description& desc,
T& value) {
return std::make_shared<TypedReferenceFlag<T>>(name, desc, value);
} }
/// @brief make_flag returns a flag with the given name and description, updating the given optional value. /// @brief make_flag returns a flag with the given name and description,
template<typename T> /// updating the given optional value.
typename OptionalTypedReferenceFlag<T>::Ptr make_flag(const Name& name, const Description& desc, Optional<T>& value) template <typename T>
{ typename OptionalTypedReferenceFlag<T>::Ptr make_flag(const Name& name,
return std::make_shared<OptionalTypedReferenceFlag<T>>(name, desc, value); const Description& desc,
Optional<T>& value) {
return std::make_shared<OptionalTypedReferenceFlag<T>>(name, desc, value);
} }
} // namespace cli } // namespace cli
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -17,29 +17,30 @@
#include "anbox/cmds/container_manager.h" #include "anbox/cmds/container_manager.h"
#include "anbox/container/service.h" #include "anbox/container/service.h"
#include "anbox/runtime.h"
#include "anbox/logger.h" #include "anbox/logger.h"
#include "anbox/runtime.h"
#include "core/posix/signal.h" #include "core/posix/signal.h"
anbox::cmds::ContainerManager::ContainerManager() anbox::cmds::ContainerManager::ContainerManager()
: CommandWithFlagsAndAction{cli::Name{"container-manager"}, cli::Usage{"container-manager"}, cli::Description{"Start the container manager service"}} : CommandWithFlagsAndAction{
{ cli::Name{"container-manager"}, cli::Usage{"container-manager"},
action([](const cli::Command::Context& ctxt) { cli::Description{"Start the container manager service"}} {
auto trap = core::posix::trap_signals_for_process({core::posix::Signal::sig_term, action([](const cli::Command::Context& ctxt) {
core::posix::Signal::sig_int}); auto trap = core::posix::trap_signals_for_process(
trap->signal_raised().connect([trap](const core::posix::Signal &signal) { {core::posix::Signal::sig_term, core::posix::Signal::sig_int});
INFO("Signal %i received. Good night.", static_cast<int>(signal)); trap->signal_raised().connect([trap](const core::posix::Signal& signal) {
trap->stop(); INFO("Signal %i received. Good night.", static_cast<int>(signal));
}); trap->stop();
auto rt = Runtime::create();
auto service = container::Service::create(rt);
rt->start();
trap->run();
rt->stop();
return 0;
}); });
auto rt = Runtime::create();
auto service = container::Service::create(rt);
rt->start();
trap->run();
rt->stop();
return 0;
});
} }

View file

@ -27,10 +27,10 @@
namespace anbox { namespace anbox {
namespace cmds { namespace cmds {
class ContainerManager : public cli::CommandWithFlagsAndAction { class ContainerManager : public cli::CommandWithFlagsAndAction {
public: public:
ContainerManager(); ContainerManager();
}; };
} // namespace cmds } // namespace cmds
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -25,22 +25,27 @@
namespace fs = boost::filesystem; namespace fs = boost::filesystem;
anbox::cmds::Install::Install() anbox::cmds::Install::Install()
: CommandWithFlagsAndAction{cli::Name{"install"}, cli::Usage{"install"}, cli::Description{"Install specified application in the Android container"}} : CommandWithFlagsAndAction{
{ cli::Name{"install"}, cli::Usage{"install"},
flag(cli::make_flag(cli::Name{"apk"}, cli::Description{"Path to APK to install"}, apk_)); cli::Description{
action([this](const cli::Command::Context&) { "Install specified application in the Android container"}} {
if (apk_.length() == 0) flag(cli::make_flag(cli::Name{"apk"},
BOOST_THROW_EXCEPTION(std::runtime_error("No APK to install specified")); cli::Description{"Path to APK to install"}, apk_));
action([this](const cli::Command::Context&) {
if (apk_.length() == 0)
BOOST_THROW_EXCEPTION(std::runtime_error("No APK to install specified"));
if (!fs::is_regular_file(apk_)) if (!fs::is_regular_file(apk_))
BOOST_THROW_EXCEPTION(std::runtime_error("Specified APK file does not exist")); BOOST_THROW_EXCEPTION(
std::runtime_error("Specified APK file does not exist"));
auto bus = std::make_shared<core::dbus::Bus>(core::dbus::WellKnownBus::session); auto bus =
bus->install_executor(core::dbus::asio::make_executor(bus)); std::make_shared<core::dbus::Bus>(core::dbus::WellKnownBus::session);
auto stub = dbus::stub::ApplicationManager::create_for_bus(bus); bus->install_executor(core::dbus::asio::make_executor(bus));
auto stub = dbus::stub::ApplicationManager::create_for_bus(bus);
stub->install(apk_); stub->install(apk_);
return EXIT_SUCCESS; return EXIT_SUCCESS;
}); });
} }

View file

@ -27,13 +27,13 @@
namespace anbox { namespace anbox {
namespace cmds { namespace cmds {
class Install : public cli::CommandWithFlagsAndAction { class Install : public cli::CommandWithFlagsAndAction {
public: public:
Install(); Install();
private: private:
std::string apk_; std::string apk_;
}; };
} // namespace cmds } // namespace cmds
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -25,21 +25,34 @@
namespace fs = boost::filesystem; namespace fs = boost::filesystem;
anbox::cmds::Launch::Launch() anbox::cmds::Launch::Launch()
: CommandWithFlagsAndAction{cli::Name{"launch"}, cli::Usage{"launch"}, cli::Description{"Launch an Activity by sending an intent"}} : CommandWithFlagsAndAction{
{ cli::Name{"launch"}, cli::Usage{"launch"},
flag(cli::make_flag(cli::Name{"action"}, cli::Description{"Action of the intent"}, intent_.action)); cli::Description{"Launch an Activity by sending an intent"}} {
flag(cli::make_flag(cli::Name{"type"}, cli::Description{"MIME type for the intent"}, intent_.type)); flag(cli::make_flag(cli::Name{"action"},
flag(cli::make_flag(cli::Name{"uri"}, cli::Description{"URI used as data within the intent"}, intent_.uri)); cli::Description{"Action of the intent"},
flag(cli::make_flag(cli::Name{"package"}, cli::Description{"Package the intent should go to"}, intent_.package)); intent_.action));
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{"type"},
cli::Description{"MIME type for the intent"},
intent_.type));
flag(cli::make_flag(cli::Name{"uri"},
cli::Description{"URI used as data within the intent"},
intent_.uri));
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));
action([this](const cli::Command::Context&) { action([this](const cli::Command::Context&) {
auto bus = std::make_shared<core::dbus::Bus>(core::dbus::WellKnownBus::session); auto bus =
bus->install_executor(core::dbus::asio::make_executor(bus)); std::make_shared<core::dbus::Bus>(core::dbus::WellKnownBus::session);
auto stub = dbus::stub::ApplicationManager::create_for_bus(bus); bus->install_executor(core::dbus::asio::make_executor(bus));
auto stub = dbus::stub::ApplicationManager::create_for_bus(bus);
stub->launch(intent_); stub->launch(intent_);
return EXIT_SUCCESS; return EXIT_SUCCESS;
}); });
} }

View file

@ -22,19 +22,19 @@
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include "anbox/cli.h"
#include "anbox/android/intent.h" #include "anbox/android/intent.h"
#include "anbox/cli.h"
namespace anbox { namespace anbox {
namespace cmds { namespace cmds {
class Launch : public cli::CommandWithFlagsAndAction { class Launch : public cli::CommandWithFlagsAndAction {
public: public:
Launch(); Launch();
private: private:
android::Intent intent_; android::Intent intent_;
}; };
} // namespace cmds } // namespace cmds
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -19,95 +19,103 @@
#include "core/posix/signal.h" #include "core/posix/signal.h"
#include "anbox/logger.h" #include "anbox/application/launcher_storage.h"
#include "anbox/runtime.h"
#include "anbox/config.h"
#include "anbox/common/dispatcher.h"
#include "anbox/cmds/run.h"
#include "anbox/network/published_socket_connector.h"
#include "anbox/qemu/pipe_connection_creator.h"
#include "anbox/graphics/gl_renderer_server.h"
#include "anbox/input/manager.h"
#include "anbox/rpc/connection_creator.h"
#include "anbox/rpc/channel.h"
#include "anbox/bridge/platform_message_processor.h"
#include "anbox/bridge/android_api_stub.h" #include "anbox/bridge/android_api_stub.h"
#include "anbox/bridge/platform_api_skeleton.h" #include "anbox/bridge/platform_api_skeleton.h"
#include "anbox/dbus/skeleton/service.h" #include "anbox/bridge/platform_message_processor.h"
#include "anbox/cmds/run.h"
#include "anbox/common/dispatcher.h"
#include "anbox/config.h"
#include "anbox/container/client.h" #include "anbox/container/client.h"
#include "anbox/wm/manager.h" #include "anbox/dbus/skeleton/service.h"
#include "anbox/graphics/gl_renderer_server.h"
#include "anbox/input/manager.h"
#include "anbox/logger.h"
#include "anbox/network/published_socket_connector.h"
#include "anbox/qemu/pipe_connection_creator.h"
#include "anbox/rpc/channel.h"
#include "anbox/rpc/connection_creator.h"
#include "anbox/runtime.h"
#include "anbox/ubuntu/platform_policy.h" #include "anbox/ubuntu/platform_policy.h"
#include "anbox/application/launcher_storage.h" #include "anbox/wm/manager.h"
#include "external/xdg/xdg.h" #include "external/xdg/xdg.h"
#include <sys/prctl.h> #include <sys/prctl.h>
#include <core/dbus/bus.h>
#include <core/dbus/asio/executor.h> #include <core/dbus/asio/executor.h>
#include <core/dbus/bus.h>
namespace fs = boost::filesystem; namespace fs = boost::filesystem;
namespace { namespace {
class NullConnectionCreator : public anbox::network::ConnectionCreator<boost::asio::local::stream_protocol> { class NullConnectionCreator : public anbox::network::ConnectionCreator<
public: boost::asio::local::stream_protocol> {
void create_connection_for( public:
std::shared_ptr<boost::asio::local::stream_protocol::socket> const& socket) override { void create_connection_for(
WARNING("Not implemented"); std::shared_ptr<boost::asio::local::stream_protocol::socket> const
socket->close(); &socket) override {
} WARNING("Not implemented");
socket->close();
}
}; };
} }
anbox::cmds::Run::BusFactory anbox::cmds::Run::session_bus_factory() { anbox::cmds::Run::BusFactory anbox::cmds::Run::session_bus_factory() {
return []() { return []() {
return std::make_shared<core::dbus::Bus>(core::dbus::WellKnownBus::session); return std::make_shared<core::dbus::Bus>(core::dbus::WellKnownBus::session);
}; };
} }
anbox::cmds::Run::Run(const BusFactory& bus_factory) anbox::cmds::Run::Run(const BusFactory &bus_factory)
: CommandWithFlagsAndAction{cli::Name{"run"}, cli::Usage{"run"}, cli::Description{"Run the the anbox system"}}, : CommandWithFlagsAndAction{cli::Name{"run"}, cli::Usage{"run"},
bus_factory_(bus_factory) cli::Description{"Run the the anbox system"}},
{ bus_factory_(bus_factory) {
// Just for the purpose to allow QtMir (or unity8) to find this on our /proc/*/cmdline // Just for the purpose to allow QtMir (or unity8) to find this on our
// for proper confinement etc. // /proc/*/cmdline
flag(cli::make_flag(cli::Name{"desktop_file_hint"}, cli::Description{"Desktop file hint for QtMir/Unity8"}, desktop_file_hint_)); // for proper confinement etc.
flag(cli::make_flag(cli::Name{"icon"}, cli::Description{"Icon of the application to run"}, icon_)); flag(cli::make_flag(cli::Name{"desktop_file_hint"},
cli::Description{"Desktop file hint for QtMir/Unity8"},
desktop_file_hint_));
flag(cli::make_flag(cli::Name{"icon"},
cli::Description{"Icon of the application to run"},
icon_));
action([this](const cli::Command::Context &ctx) { action([this](const cli::Command::Context &ctx) {
auto trap = core::posix::trap_signals_for_process({core::posix::Signal::sig_term, auto trap = core::posix::trap_signals_for_process(
core::posix::Signal::sig_int}); {core::posix::Signal::sig_term, core::posix::Signal::sig_int});
trap->signal_raised().connect([trap](const core::posix::Signal &signal) { trap->signal_raised().connect([trap](const core::posix::Signal &signal) {
INFO("Signal %i received. Good night.", static_cast<int>(signal)); INFO("Signal %i received. Good night.", static_cast<int>(signal));
trap->stop(); trap->stop();
}); });
utils::ensure_paths({ utils::ensure_paths({
config::socket_path(), config::socket_path(), config::host_input_device_path(),
config::host_input_device_path(), });
});
auto rt = Runtime::create(); auto rt = Runtime::create();
auto dispatcher = anbox::common::create_dispatcher_for_runtime(rt); auto dispatcher = anbox::common::create_dispatcher_for_runtime(rt);
auto input_manager = std::make_shared<input::Manager>(rt); auto input_manager = std::make_shared<input::Manager>(rt);
auto android_api_stub = std::make_shared<bridge::AndroidApiStub>(); auto android_api_stub = std::make_shared<bridge::AndroidApiStub>();
auto policy = std::make_shared<ubuntu::PlatformPolicy>(input_manager, android_api_stub); auto policy = std::make_shared<ubuntu::PlatformPolicy>(input_manager,
// FIXME this needs to be removed and solved differently behind the scenes android_api_stub);
registerDisplayManager(policy); // FIXME this needs to be removed and solved differently behind the scenes
registerDisplayManager(policy);
auto window_manager = std::make_shared<wm::Manager>(policy); auto window_manager = std::make_shared<wm::Manager>(policy);
auto launcher_storage = std::make_shared<application::LauncherStorage>( auto launcher_storage = std::make_shared<application::LauncherStorage>(
xdg::data().home() / "applications"); xdg::data().home() / "applications");
auto renderer = std::make_shared<graphics::GLRendererServer>(window_manager); auto renderer =
renderer->start(); std::make_shared<graphics::GLRendererServer>(window_manager);
renderer->start();
// Socket which will be used by the qemud service inside the Android // Socket which will be used by the qemud service inside the Android
// container for things like sensors, vibrtator etc. // container for things like sensors, vibrtator etc.
#if 0 #if 0
auto qemud_connector = std::make_shared<network::PublishedSocketConnector>( auto qemud_connector = std::make_shared<network::PublishedSocketConnector>(
utils::string_format("%s/qemud", config::socket_path()), utils::string_format("%s/qemud", config::socket_path()),
@ -115,60 +123,59 @@ anbox::cmds::Run::Run(const BusFactory& bus_factory)
std::make_shared<NullConnectionCreator>()); std::make_shared<NullConnectionCreator>());
#endif #endif
// The qemu pipe is used as a very fast communication channel between guest // The qemu pipe is used as a very fast communication channel between guest
// and host for things like the GLES emulation/translation, the RIL or ADB. // and host for things like the GLES emulation/translation, the RIL or ADB.
auto qemu_pipe_connector = std::make_shared<network::PublishedSocketConnector>( auto qemu_pipe_connector =
utils::string_format("%s/qemu_pipe", config::socket_path()), std::make_shared<network::PublishedSocketConnector>(
rt, utils::string_format("%s/qemu_pipe", config::socket_path()), rt,
std::make_shared<qemu::PipeConnectionCreator>(rt, std::make_shared<qemu::PipeConnectionCreator>(
renderer->socket_path(), rt, renderer->socket_path(), icon_));
icon_));
auto bridge_connector = std::make_shared<network::PublishedSocketConnector>(
utils::string_format("%s/anbox_bridge", config::socket_path()), rt,
std::make_shared<rpc::ConnectionCreator>(
rt, [&](const std::shared_ptr<network::MessageSender> &sender) {
auto pending_calls = std::make_shared<rpc::PendingCallCache>();
auto rpc_channel =
std::make_shared<rpc::Channel>(pending_calls, sender);
// This is safe as long as we only support a single client. If we
// support
// more than one one day we need proper dispatching to the right
// one.
android_api_stub->set_rpc_channel(rpc_channel);
auto bridge_connector = std::make_shared<network::PublishedSocketConnector>( auto server = std::make_shared<bridge::PlatformApiSkeleton>(
utils::string_format("%s/anbox_bridge", config::socket_path()), pending_calls, window_manager, launcher_storage);
rt, server->register_boot_finished_handler(
std::make_shared<rpc::ConnectionCreator>(rt, [&]() { DEBUG("Android successfully booted"); });
[&](const std::shared_ptr<network::MessageSender> &sender) { return std::make_shared<bridge::PlatformMessageProcessor>(
auto pending_calls = std::make_shared<rpc::PendingCallCache>(); sender, server, pending_calls);
auto rpc_channel = std::make_shared<rpc::Channel>(pending_calls, sender); }));
// This is safe as long as we only support a single client. If we support
// more than one one day we need proper dispatching to the right one.
android_api_stub->set_rpc_channel(rpc_channel);
auto server = std::make_shared<bridge::PlatformApiSkeleton>(pending_calls, container::Client container(rt);
window_manager, container::Configuration container_configuration;
launcher_storage); container_configuration.bind_mounts = {
server->register_boot_finished_handler([&]() { // { qemud_connector->socket_file(), "/dev/qemud" },
DEBUG("Android successfully booted"); {qemu_pipe_connector->socket_file(), "/dev/qemu_pipe"},
}); {bridge_connector->socket_file(), "/dev/anbox_bridge"},
return std::make_shared<bridge::PlatformMessageProcessor>(sender, server, pending_calls); {config::host_input_device_path(), "/dev/input"},
})); {"/dev/binder", "/dev/binder"},
{"/dev/ashmem", "/dev/ashmem"},
};
container::Client container(rt); dispatcher->dispatch(
container::Configuration container_configuration; [&]() { container.start_container(container_configuration); });
container_configuration.bind_mounts = {
// { qemud_connector->socket_file(), "/dev/qemud" },
{ qemu_pipe_connector->socket_file(), "/dev/qemu_pipe" },
{ bridge_connector->socket_file(), "/dev/anbox_bridge" },
{ config::host_input_device_path(), "/dev/input" },
{ "/dev/binder", "/dev/binder" },
{ "/dev/ashmem", "/dev/ashmem" },
};
dispatcher->dispatch([&]() { auto bus = bus_factory_();
container.start_container(container_configuration); bus->install_executor(core::dbus::asio::make_executor(bus, rt->service()));
});
auto bus = bus_factory_(); auto skeleton =
bus->install_executor(core::dbus::asio::make_executor(bus, rt->service())); anbox::dbus::skeleton::Service::create_for_bus(bus, android_api_stub);
auto skeleton = anbox::dbus::skeleton::Service::create_for_bus(bus, android_api_stub); rt->start();
trap->run();
rt->stop();
rt->start(); return EXIT_SUCCESS;
trap->run(); });
rt->stop();
return EXIT_SUCCESS;
});
} }

View file

@ -29,19 +29,19 @@
namespace anbox { namespace anbox {
namespace cmds { namespace cmds {
class Run : public cli::CommandWithFlagsAndAction { class Run : public cli::CommandWithFlagsAndAction {
public: public:
typedef std::function<core::dbus::Bus::Ptr()> BusFactory; typedef std::function<core::dbus::Bus::Ptr()> BusFactory;
static BusFactory session_bus_factory(); static BusFactory session_bus_factory();
Run(const BusFactory& bus_factory = session_bus_factory()); Run(const BusFactory& bus_factory = session_bus_factory());
private: private:
BusFactory bus_factory_; BusFactory bus_factory_;
std::string desktop_file_hint_; std::string desktop_file_hint_;
std::string icon_; std::string icon_;
}; };
} // namespace cmds } // namespace cmds
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -21,13 +21,13 @@
#include "anbox/version.h" #include "anbox/version.h"
anbox::cmds::Version::Version() anbox::cmds::Version::Version()
: CommandWithFlagsAndAction{cli::Name{"version"}, cli::Usage{"version"}, cli::Description{"print the version of the daemon"}} : CommandWithFlagsAndAction{
{ cli::Name{"version"}, cli::Usage{"version"},
action([](const cli::Command::Context& ctxt) cli::Description{"print the version of the daemon"}} {
{ action([](const cli::Command::Context& ctxt) {
std::uint32_t major, minor, patch; std::uint32_t major, minor, patch;
anbox::version(major, minor, patch); anbox::version(major, minor, patch);
ctxt.cout << "anbox " << major << "." << minor << "." << patch << std::endl; ctxt.cout << "anbox " << major << "." << minor << "." << patch << std::endl;
return 0; return 0;
}); });
} }

View file

@ -29,10 +29,10 @@
namespace anbox { namespace anbox {
namespace cmds { namespace cmds {
class Version : public cli::CommandWithFlagsAndAction { class Version : public cli::CommandWithFlagsAndAction {
public: public:
Version(); Version();
}; };
} // namespace cmds } // namespace cmds
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -21,23 +21,20 @@
namespace { namespace {
struct AsioStrandDispatcher : public anbox::common::Dispatcher { struct AsioStrandDispatcher : public anbox::common::Dispatcher {
public: public:
AsioStrandDispatcher(const std::shared_ptr<anbox::Runtime>& rt) AsioStrandDispatcher(const std::shared_ptr<anbox::Runtime>& rt)
: rt{rt}, : rt{rt}, strand{rt->service()} {}
strand{rt->service()} {
}
void dispatch(const Task &task) override { void dispatch(const Task& task) override { strand.post(task); }
strand.post(task);
}
private: private:
std::shared_ptr<anbox::Runtime> rt; std::shared_ptr<anbox::Runtime> rt;
boost::asio::io_service::strand strand; boost::asio::io_service::strand strand;
}; };
} }
std::shared_ptr<anbox::common::Dispatcher> anbox::common::create_dispatcher_for_runtime( std::shared_ptr<anbox::common::Dispatcher>
const std::shared_ptr<anbox::Runtime>& rt) { anbox::common::create_dispatcher_for_runtime(
return std::make_shared<AsioStrandDispatcher>(rt); const std::shared_ptr<anbox::Runtime>& rt) {
return std::make_shared<AsioStrandDispatcher>(rt);
} }

View file

@ -28,16 +28,17 @@
namespace anbox { namespace anbox {
namespace common { namespace common {
class Dispatcher : public DoNotCopyOrMove { class Dispatcher : public DoNotCopyOrMove {
public: public:
typedef std::function<void()> Task; typedef std::function<void()> Task;
virtual void dispatch(const Task& task) = 0; virtual void dispatch(const Task& task) = 0;
protected: protected:
Dispatcher() = default; Dispatcher() = default;
}; };
std::shared_ptr<Dispatcher> create_dispatcher_for_runtime(const std::shared_ptr<Runtime>&); std::shared_ptr<Dispatcher> create_dispatcher_for_runtime(
} // namespace common const std::shared_ptr<Runtime>&);
} // namespace anbox } // namespace common
} // namespace anbox
#endif #endif

View file

@ -21,41 +21,27 @@
#include <algorithm> #include <algorithm>
namespace anbox { namespace anbox {
Fd::Fd() : Fd::Fd() : Fd{invalid} {}
Fd{invalid}
{ Fd::Fd(IntOwnedFd fd) : fd{std::make_shared<int>(fd.int_owned_fd)} {}
Fd::Fd(int raw_fd)
: fd{new int{raw_fd},
[](int* fd) {
if (!fd) return;
if (*fd > Fd::invalid) ::close(*fd);
delete fd;
}} {}
Fd::Fd(Fd&& other) : fd{std::move(other.fd)} {}
Fd& Fd::operator=(Fd other) {
std::swap(fd, other.fd);
return *this;
} }
Fd::Fd(IntOwnedFd fd) : Fd::operator int() const {
fd{std::make_shared<int>(fd.int_owned_fd)} if (fd) return *fd;
{ return invalid;
} }
} // namespace anbox
Fd::Fd(int raw_fd) :
fd{new int{raw_fd},
[](int* fd)
{
if (!fd) return;
if (*fd > Fd::invalid) ::close(*fd);
delete fd;
}}
{
}
Fd::Fd(Fd&& other) :
fd{std::move(other.fd)}
{
}
Fd& Fd::operator=(Fd other)
{
std::swap(fd, other.fd);
return *this;
}
Fd::operator int() const
{
if (fd) return *fd;
return invalid;
}
} // namespace anbox

View file

@ -22,29 +22,29 @@
#include <memory> #include <memory>
namespace anbox { namespace anbox {
struct IntOwnedFd struct IntOwnedFd {
{ int int_owned_fd;
int int_owned_fd;
}; };
class Fd class Fd {
{ public:
public: // transfer ownership of the POD-int to the object. The int no longer needs
//transfer ownership of the POD-int to the object. The int no longer needs close()ing, // close()ing,
//and has the lifetime of the Fd object. // and has the lifetime of the Fd object.
explicit Fd(int fd); explicit Fd(int fd);
explicit Fd(IntOwnedFd); explicit Fd(IntOwnedFd);
static int const invalid{-1}; static int const invalid{-1};
Fd(); //Initializes fd to the anbox::Fd::invalid; Fd(); // Initializes fd to the anbox::Fd::invalid;
Fd(Fd&&); Fd(Fd&&);
Fd(Fd const&) = default; Fd(Fd const&) = default;
Fd& operator=(Fd); Fd& operator=(Fd);
//bit of a convenient kludge. take care not to close or otherwise destroy the FD. // bit of a convenient kludge. take care not to close or otherwise destroy the
operator int() const; // FD.
operator int() const;
private: private:
std::shared_ptr<int> fd; std::shared_ptr<int> fd;
}; };
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -19,13 +19,13 @@
#ifndef ANBOX_COMMON_FD_SETS_H_ #ifndef ANBOX_COMMON_FD_SETS_H_
#define ANBOX_COMMON_FD_SETS_H_ #define ANBOX_COMMON_FD_SETS_H_
#include <vector>
#include <initializer_list> #include <initializer_list>
#include <vector>
#include "anbox/common/fd.h" #include "anbox/common/fd.h"
namespace anbox { namespace anbox {
typedef std::vector<std::vector<Fd>> FdSets; typedef std::vector<std::vector<Fd>> FdSets;
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -24,38 +24,35 @@
namespace anbox { namespace anbox {
template <size_t BuiltInBufferSize> template <size_t BuiltInBufferSize>
class VariableLengthArray class VariableLengthArray {
{ public:
public: explicit VariableLengthArray(size_t size) : size_{size} {
explicit VariableLengthArray(size_t size) : size_{size} /* Don't call resize if the initial values of member variables are valid */
{ if (size > BuiltInBufferSize) resize(size);
/* Don't call resize if the initial values of member variables are valid */ }
if (size > BuiltInBufferSize) resize(size);
}
void resize(size_t size) void resize(size_t size) {
{ if (size > BuiltInBufferSize)
if (size > BuiltInBufferSize) effective_buffer = BufferUPtr{new unsigned char[size], heap_deleter};
effective_buffer = BufferUPtr{new unsigned char[size], heap_deleter}; else
else effective_buffer = BufferUPtr{builtin_buffer, null_deleter};
effective_buffer = BufferUPtr{builtin_buffer, null_deleter};
size_ = size; size_ = size;
} }
unsigned char* data() const { return effective_buffer.get(); } unsigned char* data() const { return effective_buffer.get(); }
size_t size() const { return size_; } size_t size() const { return size_; }
private: private:
typedef std::unique_ptr<unsigned char,void (*)(unsigned char*)> BufferUPtr; typedef std::unique_ptr<unsigned char, void (*)(unsigned char*)> BufferUPtr;
static void null_deleter(unsigned char*) {} static void null_deleter(unsigned char*) {}
static void heap_deleter(unsigned char* b) { delete[] b; } static void heap_deleter(unsigned char* b) { delete[] b; }
unsigned char builtin_buffer[BuiltInBufferSize]; unsigned char builtin_buffer[BuiltInBufferSize];
BufferUPtr effective_buffer{builtin_buffer, null_deleter}; BufferUPtr effective_buffer{builtin_buffer, null_deleter};
size_t size_; size_t size_;
}; };
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -21,72 +21,59 @@
namespace anbox { namespace anbox {
namespace common { namespace common {
WaitHandle::WaitHandle() : WaitHandle::WaitHandle()
guard(), : guard(), wait_condition(), expecting(0), received(0) {}
wait_condition(),
expecting(0), WaitHandle::~WaitHandle() {}
received(0)
{ void WaitHandle::expect_result() {
std::lock_guard<std::mutex> lock(guard);
expecting++;
} }
WaitHandle::~WaitHandle() void WaitHandle::result_received() {
{ std::lock_guard<std::mutex> lock(guard);
}
void WaitHandle::expect_result() received++;
{ wait_condition.notify_all();
std::lock_guard<std::mutex> lock(guard);
expecting++;
}
void WaitHandle::result_received()
{
std::lock_guard<std::mutex> lock(guard);
received++;
wait_condition.notify_all();
} }
void WaitHandle::wait_for_all() // wait for all results you expect void WaitHandle::wait_for_all() // wait for all results you expect
{ {
std::unique_lock<std::mutex> lock(guard); std::unique_lock<std::mutex> lock(guard);
wait_condition.wait(lock, [&]{ return received == expecting; }); wait_condition.wait(lock, [&] { return received == expecting; });
received = 0; received = 0;
expecting = 0; expecting = 0;
} }
void WaitHandle::wait_for_pending(std::chrono::milliseconds limit) void WaitHandle::wait_for_pending(std::chrono::milliseconds limit) {
{ std::unique_lock<std::mutex> lock(guard);
std::unique_lock<std::mutex> lock(guard);
wait_condition.wait_for(lock, limit, [&]{ return received == expecting; }); wait_condition.wait_for(lock, limit, [&] { return received == expecting; });
} }
void WaitHandle::wait_for_one() // wait for any single result void WaitHandle::wait_for_one() // wait for any single result
{ {
std::unique_lock<std::mutex> lock(guard); std::unique_lock<std::mutex> lock(guard);
wait_condition.wait(lock, [&]{ return received != 0; }); wait_condition.wait(lock, [&] { return received != 0; });
--received; --received;
--expecting; --expecting;
} }
bool WaitHandle::has_result() bool WaitHandle::has_result() {
{ std::lock_guard<std::mutex> lock(guard);
std::lock_guard<std::mutex> lock(guard);
return received > 0; return received > 0;
} }
bool WaitHandle::is_pending() bool WaitHandle::is_pending() {
{ std::unique_lock<std::mutex> lock(guard);
std::unique_lock<std::mutex> lock(guard); return expecting > 0 && received != expecting;
return expecting > 0 && received != expecting;
} }
} // namespace common } // namespace common
} // namespace anbox } // namespace anbox

View file

@ -26,29 +26,28 @@
namespace anbox { namespace anbox {
namespace common { namespace common {
struct WaitHandle struct WaitHandle {
{ public:
public: WaitHandle();
WaitHandle(); ~WaitHandle();
~WaitHandle();
void expect_result(); void expect_result();
void result_received(); void result_received();
void wait_for_all(); void wait_for_all();
void wait_for_one(); void wait_for_one();
void wait_for_pending(std::chrono::milliseconds limit); void wait_for_pending(std::chrono::milliseconds limit);
bool has_result(); bool has_result();
bool is_pending(); bool is_pending();
private: private:
std::mutex guard; std::mutex guard;
std::condition_variable wait_condition; std::condition_variable wait_condition;
int expecting; int expecting;
int received; int received;
}; };
} // namespace common } // namespace common
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -27,76 +27,81 @@ namespace fs = boost::filesystem;
namespace anbox { namespace anbox {
namespace config { namespace config {
std::string in_snap_dir(const std::string &path) { std::string in_snap_dir(const std::string &path) {
return utils::prefix_dir_from_env(path, "SNAP"); return utils::prefix_dir_from_env(path, "SNAP");
} }
std::string in_snap_data_dir(const std::string &path) { std::string in_snap_data_dir(const std::string &path) {
return utils::prefix_dir_from_env(path, "SNAP_COMMON"); return utils::prefix_dir_from_env(path, "SNAP_COMMON");
} }
std::string in_snap_user_data_dir(const std::string &path) { std::string in_snap_user_data_dir(const std::string &path) {
return utils::prefix_dir_from_env(path, "SNAP_USER_COMMON"); return utils::prefix_dir_from_env(path, "SNAP_USER_COMMON");
} }
std::string home_dir() { std::string home_dir() {
static std::string path; static std::string path;
if (path.empty()) { if (path.empty()) {
path = utils::get_env_value("HOME", ""); path = utils::get_env_value("HOME", "");
if (path.empty()) if (path.empty())
BOOST_THROW_EXCEPTION(std::runtime_error("No home directory specified")); BOOST_THROW_EXCEPTION(std::runtime_error("No home directory specified"));
} }
return path; return path;
} }
std::string runtime_dir() { std::string runtime_dir() {
static std::string path; static std::string path;
if (path.empty()) { if (path.empty()) {
path = utils::get_env_value("XDG_RUNTIME_DIR", ""); path = utils::get_env_value("XDG_RUNTIME_DIR", "");
if (path.empty()) if (path.empty())
BOOST_THROW_EXCEPTION(std::runtime_error("No runtime directory specified")); BOOST_THROW_EXCEPTION(
} std::runtime_error("No runtime directory specified"));
return path; }
return path;
} }
std::string state_dir() { std::string state_dir() {
static std::string path = "/var/lib"; static std::string path = "/var/lib";
return path; return path;
} }
std::string log_path() { std::string log_path() {
static std::string path = in_snap_data_dir(utils::string_format("%s/anbox/", state_dir())); static std::string path =
return path; in_snap_data_dir(utils::string_format("%s/anbox/", state_dir()));
return path;
} }
std::string socket_path() { std::string socket_path() {
static std::string path =
static std::string path = utils::string_format("%s/anbox/sockets", runtime_dir()); utils::string_format("%s/anbox/sockets", runtime_dir());
return path; return path;
} }
std::string data_path() { std::string data_path() {
static std::string path = utils::string_format("%s/.anbox/data", home_dir()); static std::string path = utils::string_format("%s/.anbox/data", home_dir());
return path; return path;
} }
std::string rootfs_path() { std::string rootfs_path() {
static std::string path = in_snap_data_dir(utils::string_format("%s/anbox/rootfs", state_dir())); static std::string path =
return path; in_snap_data_dir(utils::string_format("%s/anbox/rootfs", state_dir()));
return path;
} }
std::string container_config_path() { std::string container_config_path() {
static std::string path = in_snap_data_dir(utils::string_format("%s/anbox/containers", state_dir())); static std::string path = in_snap_data_dir(
return path; utils::string_format("%s/anbox/containers", state_dir()));
return path;
} }
std::string container_socket_path() { std::string container_socket_path() {
std::string path = "/run/anbox-container.socket"; std::string path = "/run/anbox-container.socket";
return path; return path;
} }
std::string host_input_device_path() { std::string host_input_device_path() {
static std::string path = utils::string_format("%s/anbox/input-devices", runtime_dir()); static std::string path =
return path; utils::string_format("%s/anbox/input-devices", runtime_dir());
return path;
} }
} // namespace config } // namespace config
} // namespace anbox } // namespace anbox

View file

@ -32,7 +32,7 @@ std::string socket_path();
std::string container_config_path(); std::string container_config_path();
std::string container_socket_path(); std::string container_socket_path();
std::string host_input_device_path(); std::string host_input_device_path();
} // namespace config } // namespace config
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -16,53 +16,50 @@
*/ */
#include "anbox/container/client.h" #include "anbox/container/client.h"
#include "anbox/config.h"
#include "anbox/container/management_api_stub.h" #include "anbox/container/management_api_stub.h"
#include "anbox/logger.h"
#include "anbox/network/local_socket_messenger.h" #include "anbox/network/local_socket_messenger.h"
#include "anbox/rpc/pending_call_cache.h"
#include "anbox/rpc/channel.h" #include "anbox/rpc/channel.h"
#include "anbox/rpc/message_processor.h" #include "anbox/rpc/message_processor.h"
#include "anbox/config.h" #include "anbox/rpc/pending_call_cache.h"
#include "anbox/logger.h"
namespace ba = boost::asio; namespace ba = boost::asio;
namespace bs = boost::system; namespace bs = boost::system;
namespace anbox { namespace anbox {
namespace container { namespace container {
Client::Client(const std::shared_ptr<Runtime> &rt) : Client::Client(const std::shared_ptr<Runtime> &rt)
messenger_(std::make_shared<network::LocalSocketMessenger>(config::container_socket_path(), rt)), : messenger_(std::make_shared<network::LocalSocketMessenger>(
pending_calls_(std::make_shared<rpc::PendingCallCache>()), config::container_socket_path(), rt)),
rpc_channel_(std::make_shared<rpc::Channel>(pending_calls_, messenger_)), pending_calls_(std::make_shared<rpc::PendingCallCache>()),
management_api_(std::make_shared<ManagementApiStub>(rpc_channel_)), rpc_channel_(std::make_shared<rpc::Channel>(pending_calls_, messenger_)),
processor_(std::make_shared<rpc::MessageProcessor>(messenger_, pending_calls_)) { management_api_(std::make_shared<ManagementApiStub>(rpc_channel_)),
processor_(
read_next_message(); std::make_shared<rpc::MessageProcessor>(messenger_, pending_calls_)) {
read_next_message();
} }
Client::~Client() { Client::~Client() {}
}
void Client::start_container(const Configuration &configuration) { void Client::start_container(const Configuration &configuration) {
management_api_->start_container(configuration); management_api_->start_container(configuration);
} }
void Client::read_next_message() void Client::read_next_message() {
{ auto callback = std::bind(&Client::on_read_size, this, std::placeholders::_1,
auto callback = std::bind(&Client::on_read_size, std::placeholders::_2);
this, std::placeholders::_1, std::placeholders::_2); messenger_->async_receive_msg(callback, ba::buffer(buffer_));
messenger_->async_receive_msg(callback, ba::buffer(buffer_));
} }
void Client::on_read_size(const boost::system::error_code& error, std::size_t bytes_read) void Client::on_read_size(const boost::system::error_code &error,
{ std::size_t bytes_read) {
if (error) if (error) BOOST_THROW_EXCEPTION(std::runtime_error(error.message()));
BOOST_THROW_EXCEPTION(std::runtime_error(error.message()));
std::vector<std::uint8_t> data(bytes_read); std::vector<std::uint8_t> data(bytes_read);
std::copy(buffer_.data(), buffer_.data() + bytes_read, data.data()); std::copy(buffer_.data(), buffer_.data() + bytes_read, data.data());
if (processor_->process_data(data)) if (processor_->process_data(data)) read_next_message();
read_next_message();
} }
} // namespace container } // namespace container
} // namespace anbox } // namespace anbox

View file

@ -18,39 +18,40 @@
#ifndef ANBOX_CONTAINER_CLIENT_H_ #ifndef ANBOX_CONTAINER_CLIENT_H_
#define ANBOX_CONTAINER_CLIENT_H_ #define ANBOX_CONTAINER_CLIENT_H_
#include "anbox/runtime.h"
#include "anbox/container/configuration.h" #include "anbox/container/configuration.h"
#include "anbox/runtime.h"
namespace anbox { namespace anbox {
namespace rpc { namespace rpc {
class PendingCallCache; class PendingCallCache;
class Channel; class Channel;
class MessageProcessor; class MessageProcessor;
} // namespace rpc } // namespace rpc
namespace network { namespace network {
class LocalSocketMessenger; class LocalSocketMessenger;
} // namespace network } // namespace network
namespace container { namespace container {
class ManagementApiStub; class ManagementApiStub;
class Client { class Client {
public: public:
Client(const std::shared_ptr<Runtime> &rt); Client(const std::shared_ptr<Runtime> &rt);
~Client(); ~Client();
void start_container(const Configuration &configuration); void start_container(const Configuration &configuration);
private: private:
void read_next_message(); void read_next_message();
void on_read_size(const boost::system::error_code& ec, std::size_t bytes_read); void on_read_size(const boost::system::error_code &ec,
std::size_t bytes_read);
std::shared_ptr<network::LocalSocketMessenger> messenger_; std::shared_ptr<network::LocalSocketMessenger> messenger_;
std::shared_ptr<rpc::PendingCallCache> pending_calls_; std::shared_ptr<rpc::PendingCallCache> pending_calls_;
std::shared_ptr<rpc::Channel> rpc_channel_; std::shared_ptr<rpc::Channel> rpc_channel_;
std::shared_ptr<ManagementApiStub> management_api_; std::shared_ptr<ManagementApiStub> management_api_;
std::shared_ptr<rpc::MessageProcessor> processor_; std::shared_ptr<rpc::MessageProcessor> processor_;
std::array<std::uint8_t, 8192> buffer_; std::array<std::uint8_t, 8192> buffer_;
}; };
} // namespace container } // namespace container
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -18,15 +18,15 @@
#ifndef ANBOX_CONTAINER_CONFIGURATION_H_ #ifndef ANBOX_CONTAINER_CONFIGURATION_H_
#define ANBOX_CONTAINER_CONFIGURATION_H_ #define ANBOX_CONTAINER_CONFIGURATION_H_
#include <string>
#include <map> #include <map>
#include <string>
namespace anbox { namespace anbox {
namespace container { namespace container {
struct Configuration { struct Configuration {
std::map<std::string,std::string> bind_mounts; std::map<std::string, std::string> bind_mounts;
}; };
} // namespace container } // namespace container
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -19,7 +19,6 @@
namespace anbox { namespace anbox {
namespace container { namespace container {
Container::~Container() { Container::~Container() {}
} } // namespace container
} // namespace container } // namespace anbox
} // namespace anbox

View file

@ -20,30 +20,30 @@
#include "anbox/container/configuration.h" #include "anbox/container/configuration.h"
#include <string>
#include <map> #include <map>
#include <string>
namespace anbox { namespace anbox {
namespace container { namespace container {
class Container { class Container {
public: public:
virtual ~Container(); virtual ~Container();
enum class State { enum class State {
inactive, inactive,
running, running,
}; };
// Start the container in background // Start the container in background
virtual void start(const Configuration &configuration) = 0; virtual void start(const Configuration &configuration) = 0;
// Stop a running container // Stop a running container
virtual void stop() = 0; virtual void stop() = 0;
// Get the current container state // Get the current container state
virtual State state() = 0; virtual State state() = 0;
}; };
} // namespace container } // namespace container
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -16,158 +16,168 @@
*/ */
#include "anbox/container/lxc_container.h" #include "anbox/container/lxc_container.h"
#include "anbox/utils.h"
#include "anbox/config.h" #include "anbox/config.h"
#include "anbox/logger.h" #include "anbox/logger.h"
#include "anbox/utils.h"
#include <stdexcept>
#include <map> #include <map>
#include <stdexcept>
#include <boost/throw_exception.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/throw_exception.hpp>
#include <sys/wait.h>
#include <sys/capability.h> #include <sys/capability.h>
#include <sys/types.h>
#include <sys/prctl.h> #include <sys/prctl.h>
#include <sys/types.h>
#include <sys/wait.h>
namespace fs = boost::filesystem; namespace fs = boost::filesystem;
namespace anbox { namespace anbox {
namespace container { namespace container {
LxcContainer::LxcContainer() : LxcContainer::LxcContainer() : state_(State::inactive), container_(nullptr) {
state_(State::inactive), utils::ensure_paths({
container_(nullptr) { config::container_config_path(), config::log_path(),
utils::ensure_paths({ });
config::container_config_path(),
config::log_path(),
});
} }
LxcContainer::~LxcContainer() { LxcContainer::~LxcContainer() {
DEBUG(""); DEBUG("");
stop(); stop();
if (container_) if (container_) lxc_container_put(container_);
lxc_container_put(container_);
} }
void LxcContainer::start(const Configuration &configuration) { void LxcContainer::start(const Configuration &configuration) {
if (getuid() != 0) if (getuid() != 0)
BOOST_THROW_EXCEPTION(std::runtime_error("You have to start the container as root")); BOOST_THROW_EXCEPTION(
std::runtime_error("You have to start the container as root"));
if (container_ && container_->is_running(container_)) { if (container_ && container_->is_running(container_)) {
BOOST_THROW_EXCEPTION(std::runtime_error("Container already started, stopping it now")); BOOST_THROW_EXCEPTION(
container_->stop(container_); std::runtime_error("Container already started, stopping it now"));
} container_->stop(container_);
}
if (!container_) { if (!container_) {
DEBUG("Containers are stored in %s", config::container_config_path()); DEBUG("Containers are stored in %s", config::container_config_path());
// Remove container config to be be able to rewrite it // Remove container config to be be able to rewrite it
::unlink(utils::string_format("%s/default/config", config::container_config_path()).c_str()); ::unlink(utils::string_format("%s/default/config",
config::container_config_path())
.c_str());
container_ = lxc_container_new("default", config::container_config_path().c_str()); container_ =
if (!container_) lxc_container_new("default", config::container_config_path().c_str());
BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create LXC container instance")); if (!container_)
BOOST_THROW_EXCEPTION(
std::runtime_error("Failed to create LXC container instance"));
// If container is still running (for example after a crash) we stop it here to ensure // If container is still running (for example after a crash) we stop it here
// its configuration is synchronized. // to ensure
if (container_->is_running(container_)) // its configuration is synchronized.
container_->stop(container_); if (container_->is_running(container_)) container_->stop(container_);
} }
// We drop all not needed capabilities // We drop all not needed capabilities
set_config_item("lxc.cap.drop", "mac_admin mac_override sys_time sys_module sys_rawio"); set_config_item("lxc.cap.drop",
"mac_admin mac_override sys_time sys_module sys_rawio");
// We can mount proc/sys as rw here as we will run the container unprivileged in the end // We can mount proc/sys as rw here as we will run the container unprivileged
set_config_item("lxc.mount.auto", "proc:mixed sys:mixed cgroup:mixed"); // in the end
set_config_item("lxc.mount.auto", "proc:mixed sys:mixed cgroup:mixed");
set_config_item("lxc.autodev", "1"); set_config_item("lxc.autodev", "1");
set_config_item("lxc.pts", "1024"); set_config_item("lxc.pts", "1024");
set_config_item("lxc.tty", "0"); set_config_item("lxc.tty", "0");
set_config_item("lxc.utsname", "anbox"); set_config_item("lxc.utsname", "anbox");
set_config_item("lxc.group.devices.deny",""); set_config_item("lxc.group.devices.deny", "");
set_config_item("lxc.group.devices.allow",""); set_config_item("lxc.group.devices.allow", "");
// We can't move bind-mounts, so don't use /dev/lxc/ // We can't move bind-mounts, so don't use /dev/lxc/
set_config_item("lxc.devttydir", ""); set_config_item("lxc.devttydir", "");
set_config_item("lxc.environment", "PATH=/system/bin:/system/sbin:/system/xbin"); set_config_item("lxc.environment",
"PATH=/system/bin:/system/sbin:/system/xbin");
set_config_item("lxc.init_cmd", "/anbox-init.sh"); set_config_item("lxc.init_cmd", "/anbox-init.sh");
set_config_item("lxc.rootfs.backend", "dir"); set_config_item("lxc.rootfs.backend", "dir");
DEBUG("Using rootfs path %s", config::rootfs_path()); DEBUG("Using rootfs path %s", config::rootfs_path());
set_config_item("lxc.rootfs", config::rootfs_path()); set_config_item("lxc.rootfs", config::rootfs_path());
set_config_item("lxc.loglevel", "0"); set_config_item("lxc.loglevel", "0");
set_config_item("lxc.logfile", utils::string_format("%s/container.log", config::log_path()).c_str()); set_config_item(
"lxc.logfile",
utils::string_format("%s/container.log", config::log_path()).c_str());
set_config_item("lxc.network.type", "veth"); set_config_item("lxc.network.type", "veth");
set_config_item("lxc.network.flags", "up"); set_config_item("lxc.network.flags", "up");
set_config_item("lxc.network.link", "anboxbr0"); set_config_item("lxc.network.link", "anboxbr0");
#if 0 #if 0
// Android uses namespaces as well so we have to allow nested namespaces for LXC // Android uses namespaces as well so we have to allow nested namespaces for LXC
// which are otherwise forbidden by AppArmor. // which are otherwise forbidden by AppArmor.
set_config_item("lxc.aa_profile", "lxc-container-default-with-nesting"); set_config_item("lxc.aa_profile", "lxc-container-default-with-nesting");
#else #else
// FIXME: when using the nested profile we still get various denials from things // FIXME: when using the nested profile we still get various denials from
// Android tries to do but isn't allowed to. We need to look into those and see // things
// how we can switch back to a confined way of running the container. // Android tries to do but isn't allowed to. We need to look into those and
set_config_item("lxc.aa_profile", "unconfined"); // see
// how we can switch back to a confined way of running the container.
set_config_item("lxc.aa_profile", "unconfined");
#endif #endif
for (const auto &bind_mount : configuration.bind_mounts) { for (const auto &bind_mount : configuration.bind_mounts) {
std::string create_type = "file"; std::string create_type = "file";
if (fs::is_directory(bind_mount.first)) if (fs::is_directory(bind_mount.first)) create_type = "dir";
create_type = "dir";
auto target_path = bind_mount.second; auto target_path = bind_mount.second;
// LXC wants target paths relative to the container rootfs so // LXC wants target paths relative to the container rootfs so
// prividing an absolute path doesn't work. // prividing an absolute path doesn't work.
if (utils::string_starts_with(target_path, "/")) if (utils::string_starts_with(target_path, "/")) target_path.erase(0, 1);
target_path.erase(0, 1);
set_config_item("lxc.mount.entry", set_config_item(
utils::string_format("%s %s none bind,create=%s,optional 0 0", "lxc.mount.entry",
bind_mount.first, target_path, create_type)); utils::string_format("%s %s none bind,create=%s,optional 0 0",
} bind_mount.first, target_path, create_type));
}
if (!container_->save_config(container_, nullptr)) if (!container_->save_config(container_, nullptr))
BOOST_THROW_EXCEPTION(std::runtime_error("Failed to save container configuration")); BOOST_THROW_EXCEPTION(
std::runtime_error("Failed to save container configuration"));
if (not container_->start(container_, 0, nullptr)) if (not container_->start(container_, 0, nullptr))
BOOST_THROW_EXCEPTION(std::runtime_error("Failed to start container")); BOOST_THROW_EXCEPTION(std::runtime_error("Failed to start container"));
state_ = Container::State::running; state_ = Container::State::running;
DEBUG("Container successfully started"); DEBUG("Container successfully started");
} }
void LxcContainer::stop() { void LxcContainer::stop() {
if (not container_ || not container_->is_running(container_)) if (not container_ || not container_->is_running(container_))
BOOST_THROW_EXCEPTION(std::runtime_error("Cannot stop container as it is not running")); BOOST_THROW_EXCEPTION(
std::runtime_error("Cannot stop container as it is not running"));
if (not container_->stop(container_)) if (not container_->stop(container_))
BOOST_THROW_EXCEPTION(std::runtime_error("Failed to stop container")); BOOST_THROW_EXCEPTION(std::runtime_error("Failed to stop container"));
state_ = Container::State::inactive; state_ = Container::State::inactive;
DEBUG("Container successfully stopped"); DEBUG("Container successfully stopped");
} }
void LxcContainer::set_config_item(const std::string &key, const std::string &value) { void LxcContainer::set_config_item(const std::string &key,
if (!container_->set_config_item(container_, key.c_str(), value.c_str())) const std::string &value) {
BOOST_THROW_EXCEPTION(std::runtime_error("Failed to configure LXC container")); if (!container_->set_config_item(container_, key.c_str(), value.c_str()))
BOOST_THROW_EXCEPTION(
std::runtime_error("Failed to configure LXC container"));
} }
Container::State LxcContainer::state() { Container::State LxcContainer::state() { return state_; }
return state_; } // namespace container
} } // namespace anbox
} // namespace container
} // namespace anbox

View file

@ -27,21 +27,21 @@
namespace anbox { namespace anbox {
namespace container { namespace container {
class LxcContainer : public Container { class LxcContainer : public Container {
public: public:
LxcContainer(); LxcContainer();
~LxcContainer(); ~LxcContainer();
void start(const Configuration &configuration) override; void start(const Configuration &configuration) override;
void stop() override; void stop() override;
State state() override; State state() override;
private: private:
void set_config_item(const std::string &key, const std::string &value); void set_config_item(const std::string &key, const std::string &value);
State state_; State state_;
lxc_container *container_; lxc_container *container_;
}; };
} // namespace container } // namespace container
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -24,22 +24,22 @@
namespace anbox { namespace anbox {
namespace container { namespace container {
ManagementApiMessageProcessor::ManagementApiMessageProcessor(const std::shared_ptr<network::MessageSender> &sender, ManagementApiMessageProcessor::ManagementApiMessageProcessor(
const std::shared_ptr<rpc::PendingCallCache> &pending_calls, const std::shared_ptr<network::MessageSender> &sender,
const std::shared_ptr<ManagementApiSkeleton> &server) : const std::shared_ptr<rpc::PendingCallCache> &pending_calls,
rpc::MessageProcessor(sender, pending_calls), const std::shared_ptr<ManagementApiSkeleton> &server)
server_(server) { : rpc::MessageProcessor(sender, pending_calls), server_(server) {}
ManagementApiMessageProcessor::~ManagementApiMessageProcessor() {}
void ManagementApiMessageProcessor::dispatch(
rpc::Invocation const &invocation) {
if (invocation.method_name() == "start_container")
invoke(this, server_.get(), &ManagementApiSkeleton::start_container,
invocation);
} }
ManagementApiMessageProcessor::~ManagementApiMessageProcessor() { void ManagementApiMessageProcessor::process_event_sequence(
} const std::string &) {}
} // namespace container
void ManagementApiMessageProcessor::dispatch(rpc::Invocation const& invocation) { } // namespace anbox
if (invocation.method_name() == "start_container")
invoke(this, server_.get(), &ManagementApiSkeleton::start_container, invocation);
}
void ManagementApiMessageProcessor::process_event_sequence(const std::string&) {
}
} // namespace container
} // namespace anbox

View file

@ -24,19 +24,20 @@ namespace anbox {
namespace container { namespace container {
class ManagementApiSkeleton; class ManagementApiSkeleton;
class ManagementApiMessageProcessor : public rpc::MessageProcessor { class ManagementApiMessageProcessor : public rpc::MessageProcessor {
public: public:
ManagementApiMessageProcessor(const std::shared_ptr<network::MessageSender> &sender, ManagementApiMessageProcessor(
const std::shared_ptr<rpc::PendingCallCache> &pending_calls, const std::shared_ptr<network::MessageSender> &sender,
const std::shared_ptr<ManagementApiSkeleton> &server); const std::shared_ptr<rpc::PendingCallCache> &pending_calls,
~ManagementApiMessageProcessor(); const std::shared_ptr<ManagementApiSkeleton> &server);
~ManagementApiMessageProcessor();
void dispatch(rpc::Invocation const& invocation) override; void dispatch(rpc::Invocation const &invocation) override;
void process_event_sequence(const std::string &event) override; void process_event_sequence(const std::string &event) override;
private: private:
std::shared_ptr<ManagementApiSkeleton> server_; std::shared_ptr<ManagementApiSkeleton> server_;
}; };
} // namespace anbox } // namespace anbox
} // namespace network } // namespace network
#endif #endif

View file

@ -16,54 +16,52 @@
*/ */
#include "anbox/container/management_api_skeleton.h" #include "anbox/container/management_api_skeleton.h"
#include "anbox/container/container.h"
#include "anbox/container/configuration.h" #include "anbox/container/configuration.h"
#include "anbox/container/container.h"
#include "anbox/defer_action.h" #include "anbox/defer_action.h"
#include "anbox/utils.h"
#include "anbox/logger.h" #include "anbox/logger.h"
#include "anbox/utils.h"
#include "anbox_rpc.pb.h"
#include "anbox_container.pb.h" #include "anbox_container.pb.h"
#include "anbox_rpc.pb.h"
namespace anbox { namespace anbox {
namespace container { namespace container {
ManagementApiSkeleton::ManagementApiSkeleton(const std::shared_ptr<rpc::PendingCallCache> &pending_calls, ManagementApiSkeleton::ManagementApiSkeleton(
const std::shared_ptr<Container> &container) : const std::shared_ptr<rpc::PendingCallCache> &pending_calls,
pending_calls_(pending_calls), const std::shared_ptr<Container> &container)
container_(container) { : pending_calls_(pending_calls), container_(container) {}
}
ManagementApiSkeleton::~ManagementApiSkeleton() { ManagementApiSkeleton::~ManagementApiSkeleton() {}
}
void ManagementApiSkeleton::start_container(anbox::protobuf::container::StartContainer const *request,
anbox::protobuf::rpc::Void *response,
google::protobuf::Closure *done) {
if (container_->state() == Container::State::running) {
response->set_error("Container is already running");
done->Run();
return;
}
Configuration container_configuration;
const auto configuration = request->configuration();
for (int n = 0; n < configuration.bind_mounts_size(); n++) {
const auto bind_mount = configuration.bind_mounts(n);
container_configuration.bind_mounts.insert({ bind_mount.source(), bind_mount.target() });
}
try {
container_->start(container_configuration);
}
catch (std::exception &err) {
response->set_error(utils::string_format("Failed to start container: %s", err.what()));
}
DEBUG("");
void ManagementApiSkeleton::start_container(
anbox::protobuf::container::StartContainer const *request,
anbox::protobuf::rpc::Void *response, google::protobuf::Closure *done) {
if (container_->state() == Container::State::running) {
response->set_error("Container is already running");
done->Run(); done->Run();
return;
}
Configuration container_configuration;
const auto configuration = request->configuration();
for (int n = 0; n < configuration.bind_mounts_size(); n++) {
const auto bind_mount = configuration.bind_mounts(n);
container_configuration.bind_mounts.insert(
{bind_mount.source(), bind_mount.target()});
}
try {
container_->start(container_configuration);
} catch (std::exception &err) {
response->set_error(
utils::string_format("Failed to start container: %s", err.what()));
}
DEBUG("");
done->Run();
} }
} // namespace container } // namespace container
} // namespace anbox } // namespace anbox

View file

@ -23,38 +23,39 @@
namespace google { namespace google {
namespace protobuf { namespace protobuf {
class Closure; class Closure;
} // namespace protobuf } // namespace protobuf
} // namespace google } // namespace google
namespace anbox { namespace anbox {
namespace protobuf { namespace protobuf {
namespace rpc { namespace rpc {
class Void; class Void;
} // namespace rpc } // namespace rpc
namespace container { namespace container {
class StartContainer; class StartContainer;
} // namespace container } // namespace container
} // namespace protobuf } // namespace protobuf
namespace rpc { namespace rpc {
class PendingCallCache; class PendingCallCache;
} // namespace rpc } // namespace rpc
namespace container { namespace container {
class Container; class Container;
class ManagementApiSkeleton { class ManagementApiSkeleton {
public: public:
ManagementApiSkeleton(const std::shared_ptr<rpc::PendingCallCache> &pending_calls, ManagementApiSkeleton(
const std::shared_ptr<Container> &container); const std::shared_ptr<rpc::PendingCallCache> &pending_calls,
~ManagementApiSkeleton(); const std::shared_ptr<Container> &container);
~ManagementApiSkeleton();
void start_container(anbox::protobuf::container::StartContainer const *request, void start_container(
anbox::protobuf::rpc::Void *response, anbox::protobuf::container::StartContainer const *request,
google::protobuf::Closure *done); anbox::protobuf::rpc::Void *response, google::protobuf::Closure *done);
private: private:
std::shared_ptr<rpc::PendingCallCache> pending_calls_; std::shared_ptr<rpc::PendingCallCache> pending_calls_;
std::shared_ptr<Container> container_; std::shared_ptr<Container> container_;
}; };
} // namespace container } // namespace container
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -16,55 +16,54 @@
*/ */
#include "anbox/container/management_api_stub.h" #include "anbox/container/management_api_stub.h"
#include "anbox/rpc/channel.h"
#include "anbox/logger.h" #include "anbox/logger.h"
#include "anbox/rpc/channel.h"
#include "anbox_rpc.pb.h"
#include "anbox_container.pb.h" #include "anbox_container.pb.h"
#include "anbox_rpc.pb.h"
namespace anbox { namespace anbox {
namespace container { namespace container {
ManagementApiStub::ManagementApiStub(const std::shared_ptr<rpc::Channel> &channel) : ManagementApiStub::ManagementApiStub(
channel_(channel) { const std::shared_ptr<rpc::Channel> &channel)
} : channel_(channel) {}
ManagementApiStub::~ManagementApiStub() { ManagementApiStub::~ManagementApiStub() {}
}
void ManagementApiStub::start_container(const Configuration &configuration) { void ManagementApiStub::start_container(const Configuration &configuration) {
auto c = std::make_shared<Request<protobuf::rpc::Void>>(); auto c = std::make_shared<Request<protobuf::rpc::Void>>();
protobuf::container::StartContainer message; protobuf::container::StartContainer message;
auto message_configuration = new protobuf::container::Configuration; auto message_configuration = new protobuf::container::Configuration;
for (const auto item : configuration.bind_mounts) { for (const auto item : configuration.bind_mounts) {
auto bind_mount_message = message_configuration->add_bind_mounts(); auto bind_mount_message = message_configuration->add_bind_mounts();
bind_mount_message->set_source(item.first); bind_mount_message->set_source(item.first);
bind_mount_message->set_target(item.second); bind_mount_message->set_target(item.second);
} }
message.set_allocated_configuration(message_configuration); message.set_allocated_configuration(message_configuration);
{ {
std::lock_guard<decltype(mutex_)> lock(mutex_); std::lock_guard<decltype(mutex_)> lock(mutex_);
start_wait_handle_.expect_result(); start_wait_handle_.expect_result();
} }
channel_->call_method("start_container", channel_->call_method(
&message, "start_container", &message, c->response.get(),
c->response.get(), google::protobuf::NewCallback(this, &ManagementApiStub::container_started,
google::protobuf::NewCallback(this, &ManagementApiStub::container_started, c.get())); c.get()));
start_wait_handle_.wait_for_all(); start_wait_handle_.wait_for_all();
if (c->response->has_error()) if (c->response->has_error()) throw std::runtime_error(c->response->error());
throw std::runtime_error(c->response->error());
} }
void ManagementApiStub::container_started(Request<protobuf::rpc::Void> *request) { void ManagementApiStub::container_started(
(void) request; Request<protobuf::rpc::Void> *request) {
DEBUG(""); (void)request;
start_wait_handle_.result_received(); DEBUG("");
start_wait_handle_.result_received();
} }
} // namespace container } // namespace container
} // namespace anbox } // namespace anbox

View file

@ -18,9 +18,9 @@
#ifndef ANBOX_CONTAINER_MANAGEMENT_API_STUB_H_ #ifndef ANBOX_CONTAINER_MANAGEMENT_API_STUB_H_
#define ANBOX_CONTAINER_MANAGEMENT_API_STUB_H_ #define ANBOX_CONTAINER_MANAGEMENT_API_STUB_H_
#include "anbox/do_not_copy_or_move.h"
#include "anbox/container/configuration.h"
#include "anbox/common/wait_handle.h" #include "anbox/common/wait_handle.h"
#include "anbox/container/configuration.h"
#include "anbox/do_not_copy_or_move.h"
#include <memory> #include <memory>
@ -28,34 +28,34 @@ namespace anbox {
namespace protobuf { namespace protobuf {
namespace rpc { namespace rpc {
class Void; class Void;
} // namespace rpc } // namespace rpc
} // namespace protobuf } // namespace protobuf
namespace rpc { namespace rpc {
class Channel; class Channel;
} // namespace rpc } // namespace rpc
namespace container { namespace container {
class ManagementApiStub : public DoNotCopyOrMove { class ManagementApiStub : public DoNotCopyOrMove {
public: public:
ManagementApiStub(const std::shared_ptr<rpc::Channel> &channel); ManagementApiStub(const std::shared_ptr<rpc::Channel> &channel);
~ManagementApiStub(); ~ManagementApiStub();
void start_container(const Configuration &configuration); void start_container(const Configuration &configuration);
private: private:
template<typename Response> template <typename Response>
struct Request { struct Request {
Request() : response(std::make_shared<Response>()), success(true) { } Request() : response(std::make_shared<Response>()), success(true) {}
std::shared_ptr<Response> response; std::shared_ptr<Response> response;
bool success; bool success;
}; };
void container_started(Request<protobuf::rpc::Void> *request); void container_started(Request<protobuf::rpc::Void> *request);
mutable std::mutex mutex_; mutable std::mutex mutex_;
std::shared_ptr<rpc::Channel> channel_; std::shared_ptr<rpc::Channel> channel_;
common::WaitHandle start_wait_handle_; common::WaitHandle start_wait_handle_;
}; };
} // namespace container } // namespace container
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -16,76 +16,77 @@
*/ */
#include "anbox/container/service.h" #include "anbox/container/service.h"
#include "anbox/config.h"
#include "anbox/container/lxc_container.h"
#include "anbox/container/management_api_message_processor.h"
#include "anbox/container/management_api_skeleton.h"
#include "anbox/logger.h"
#include "anbox/network/delegate_connection_creator.h" #include "anbox/network/delegate_connection_creator.h"
#include "anbox/network/delegate_message_processor.h" #include "anbox/network/delegate_message_processor.h"
#include "anbox/network/local_socket_messenger.h" #include "anbox/network/local_socket_messenger.h"
#include "anbox/qemu/null_message_processor.h" #include "anbox/qemu/null_message_processor.h"
#include "anbox/rpc/pending_call_cache.h"
#include "anbox/rpc/channel.h" #include "anbox/rpc/channel.h"
#include "anbox/container/lxc_container.h" #include "anbox/rpc/pending_call_cache.h"
#include "anbox/container/management_api_message_processor.h"
#include "anbox/container/management_api_skeleton.h"
#include "anbox/config.h"
#include "anbox/logger.h"
namespace anbox { namespace anbox {
namespace container { namespace container {
std::shared_ptr<Service> Service::create(const std::shared_ptr<Runtime> &rt) { std::shared_ptr<Service> Service::create(const std::shared_ptr<Runtime> &rt) {
auto sp = std::make_shared<Service>(rt); auto sp = std::make_shared<Service>(rt);
auto delegate_connector = std::make_shared<network::DelegateConnectionCreator<boost::asio::local::stream_protocol>>( auto delegate_connector = std::make_shared<
[sp](std::shared_ptr<boost::asio::local::stream_protocol::socket> const &socket) { network::DelegateConnectionCreator<boost::asio::local::stream_protocol>>(
sp->new_client(socket); [sp](std::shared_ptr<boost::asio::local::stream_protocol::socket> const
}); &socket) { sp->new_client(socket); });
sp->connector_ = std::make_shared<network::PublishedSocketConnector>( sp->connector_ = std::make_shared<network::PublishedSocketConnector>(
config::container_socket_path(), rt, delegate_connector); config::container_socket_path(), rt, delegate_connector);
// Make sure others can connect to our socket // Make sure others can connect to our socket
::chmod(config::container_socket_path().c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); ::chmod(config::container_socket_path().c_str(),
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
DEBUG("Everything setup. Waiting for incoming connections."); DEBUG("Everything setup. Waiting for incoming connections.");
return sp; return sp;
} }
Service::Service(const std::shared_ptr<Runtime> &rt) : Service::Service(const std::shared_ptr<Runtime> &rt)
dispatcher_(anbox::common::create_dispatcher_for_runtime(rt)), : dispatcher_(anbox::common::create_dispatcher_for_runtime(rt)),
next_connection_id_(0), next_connection_id_(0),
connections_(std::make_shared<network::Connections<network::SocketConnection>>()) { connections_(
std::make_shared<network::Connections<network::SocketConnection>>()) {
} }
Service::~Service() { Service::~Service() { DEBUG(""); }
DEBUG("");
int Service::next_id() { return next_connection_id_++; }
void Service::new_client(
std::shared_ptr<boost::asio::local::stream_protocol::socket> const
&socket) {
if (connections_->size() >= 1) {
socket->close();
return;
}
auto const messenger =
std::make_shared<network::LocalSocketMessenger>(socket);
DEBUG("Got connection from pid %d", messenger->creds().pid());
auto pending_calls = std::make_shared<rpc::PendingCallCache>();
auto rpc_channel = std::make_shared<rpc::Channel>(pending_calls, messenger);
auto server = std::make_shared<container::ManagementApiSkeleton>(
pending_calls, std::make_shared<LxcContainer>());
auto processor = std::make_shared<container::ManagementApiMessageProcessor>(
messenger, pending_calls, server);
auto const &connection = std::make_shared<network::SocketConnection>(
messenger, messenger, next_id(), connections_, processor);
connection->set_name("container-service");
connections_->add(connection);
connection->read_next_message();
} }
} // namespace container
int Service::next_id() { } // namespace anbox
return next_connection_id_++;
}
void Service::new_client(std::shared_ptr<boost::asio::local::stream_protocol::socket> const &socket) {
if (connections_->size() >= 1) {
socket->close();
return;
}
auto const messenger = std::make_shared<network::LocalSocketMessenger>(socket);
DEBUG("Got connection from pid %d", messenger->creds().pid());
auto pending_calls = std::make_shared<rpc::PendingCallCache>();
auto rpc_channel = std::make_shared<rpc::Channel>(pending_calls, messenger);
auto server = std::make_shared<container::ManagementApiSkeleton>(
pending_calls, std::make_shared<LxcContainer>());
auto processor = std::make_shared<container::ManagementApiMessageProcessor>(
messenger, pending_calls, server);
auto const& connection = std::make_shared<network::SocketConnection>(
messenger, messenger, next_id(), connections_, processor);
connection->set_name("container-service");
connections_->add(connection);
connection->read_next_message();
}
} // namespace container
} // namespace anbox

View file

@ -18,34 +18,35 @@
#ifndef ANBOX_CONTAINER_SERVICE_H_ #ifndef ANBOX_CONTAINER_SERVICE_H_
#define ANBOX_CONTAINER_SERVICE_H_ #define ANBOX_CONTAINER_SERVICE_H_
#include "anbox/network/published_socket_connector.h"
#include "anbox/network/connections.h"
#include "anbox/network/socket_connection.h"
#include "anbox/network/credentials.h"
#include "anbox/container/container.h"
#include "anbox/common/dispatcher.h" #include "anbox/common/dispatcher.h"
#include "anbox/container/container.h"
#include "anbox/network/connections.h"
#include "anbox/network/credentials.h"
#include "anbox/network/published_socket_connector.h"
#include "anbox/network/socket_connection.h"
#include "anbox/runtime.h" #include "anbox/runtime.h"
namespace anbox { namespace anbox {
namespace container { namespace container {
class Service : public std::enable_shared_from_this<Service> { class Service : public std::enable_shared_from_this<Service> {
public: public:
static std::shared_ptr<Service> create(const std::shared_ptr<Runtime> &rt); static std::shared_ptr<Service> create(const std::shared_ptr<Runtime> &rt);
Service(const std::shared_ptr<Runtime> &rt); Service(const std::shared_ptr<Runtime> &rt);
~Service(); ~Service();
private: private:
int next_id(); int next_id();
void new_client(std::shared_ptr<boost::asio::local::stream_protocol::socket> const &socket); void new_client(std::shared_ptr<
boost::asio::local::stream_protocol::socket> const &socket);
std::shared_ptr<common::Dispatcher> dispatcher_; std::shared_ptr<common::Dispatcher> dispatcher_;
std::shared_ptr<network::PublishedSocketConnector> connector_; std::shared_ptr<network::PublishedSocketConnector> connector_;
std::atomic<int> next_connection_id_; std::atomic<int> next_connection_id_;
std::shared_ptr<network::Connections<network::SocketConnection>> connections_; std::shared_ptr<network::Connections<network::SocketConnection>> connections_;
std::shared_ptr<Container> backend_; std::shared_ptr<Container> backend_;
}; };
} // namespace container } // namespace container
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -15,38 +15,36 @@
* *
*/ */
#include <sys/prctl.h>
#include <signal.h> #include <signal.h>
#include <sys/prctl.h>
#include "anbox/logger.h"
#include "anbox/daemon.h"
#include "anbox/config.h" #include "anbox/config.h"
#include "anbox/daemon.h"
#include "anbox/logger.h"
#include "anbox/cmds/version.h"
#include "anbox/cmds/run.h"
#include "anbox/cmds/launch.h"
#include "anbox/cmds/container_manager.h" #include "anbox/cmds/container_manager.h"
#include "anbox/cmds/launch.h"
#include "anbox/cmds/run.h"
#include "anbox/cmds/version.h"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
namespace fs = boost::filesystem; namespace fs = boost::filesystem;
namespace anbox { namespace anbox {
Daemon::Daemon() : Daemon::Daemon()
cmd{cli::Name{"anbox"}, cli::Usage{"anbox"}, cli::Description{"The Android in a Box runtime"}} { : cmd{cli::Name{"anbox"}, cli::Usage{"anbox"},
cli::Description{"The Android in a Box runtime"}} {
cmd.command(std::make_shared<cmds::Version>()) cmd.command(std::make_shared<cmds::Version>())
.command(std::make_shared<cmds::Run>()) .command(std::make_shared<cmds::Run>())
.command(std::make_shared<cmds::Launch>()) .command(std::make_shared<cmds::Launch>())
.command(std::make_shared<cmds::ContainerManager>()); .command(std::make_shared<cmds::ContainerManager>());
} }
int Daemon::Run(const std::vector<std::string> &arguments) int Daemon::Run(const std::vector<std::string> &arguments) try {
try { return cmd.run({std::cin, std::cout, arguments});
return cmd.run({std::cin, std::cout, arguments}); } catch (std::exception &err) {
ERROR("%s", err.what());
return EXIT_FAILURE;
} }
catch(std::exception &err) { } // namespace anbox
ERROR("%s", err.what());
return EXIT_FAILURE;
}
} // namespace anbox

View file

@ -18,22 +18,22 @@
#ifndef ANBOX_DAEMON_H_ #ifndef ANBOX_DAEMON_H_
#define ANBOX_DAEMON_H_ #define ANBOX_DAEMON_H_
#include <vector>
#include <string> #include <string>
#include <vector>
#include "anbox/do_not_copy_or_move.h"
#include "anbox/cli.h" #include "anbox/cli.h"
#include "anbox/do_not_copy_or_move.h"
namespace anbox { namespace anbox {
class Daemon : public DoNotCopyOrMove { class Daemon : public DoNotCopyOrMove {
public: public:
Daemon(); Daemon();
int Run(const std::vector<std::string> &arguments); int Run(const std::vector<std::string> &arguments);
private: private:
cli::CommandWithSubcommands cmd; cli::CommandWithSubcommands cmd;
}; };
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -20,42 +20,45 @@
#include <core/dbus/macros.h> #include <core/dbus/macros.h>
#include <string>
#include <chrono> #include <chrono>
#include <string>
namespace anbox { namespace anbox {
namespace dbus { namespace dbus {
namespace interface { namespace interface {
struct Service { struct Service {
static inline std::string name() { return "org.anbox"; } static inline std::string name() { return "org.anbox"; }
static inline std::string path() { return "/"; } static inline std::string path() { return "/"; }
}; };
struct ApplicationManager { struct ApplicationManager {
static inline std::string name() { return "org.anbox.ApplicationManager"; } static inline std::string name() { return "org.anbox.ApplicationManager"; }
struct Methods { struct Methods {
struct Launch { struct Launch {
static inline std::string name() { return "Launch"; } static inline std::string name() { return "Launch"; }
typedef anbox::dbus::interface::ApplicationManager Interface; typedef anbox::dbus::interface::ApplicationManager Interface;
typedef void ResultType; typedef void ResultType;
static inline std::chrono::milliseconds default_timeout() { return std::chrono::seconds{1}; } static inline std::chrono::milliseconds default_timeout() {
}; return std::chrono::seconds{1};
}
}; };
};
}; };
} // namespace interface } // namespace interface
} // namespace dbus } // namespace dbus
} // namespace anbox } // namespace anbox
namespace core { namespace core {
namespace dbus { namespace dbus {
namespace traits { namespace traits {
template<> struct Service<anbox::dbus::interface::ApplicationManager> { template <>
static inline const std::string& interface_name() { struct Service<anbox::dbus::interface::ApplicationManager> {
static const std::string s{"org.anbox.ApplicationManager"}; static inline const std::string& interface_name() {
return s; static const std::string s{"org.anbox.ApplicationManager"};
} return s;
}
}; };
} // namespace traits } // namespace traits
} // namespace dbus } // namespace dbus
} // namespace core } // namespace core
#endif #endif

View file

@ -16,22 +16,20 @@
*/ */
#include "anbox/dbus/skeleton/application_manager.h" #include "anbox/dbus/skeleton/application_manager.h"
#include "anbox/dbus/interface.h"
#include "anbox/android/intent.h" #include "anbox/android/intent.h"
#include "anbox/dbus/interface.h"
#include "anbox/logger.h" #include "anbox/logger.h"
namespace anbox { namespace anbox {
namespace dbus { namespace dbus {
namespace skeleton { namespace skeleton {
ApplicationManager::ApplicationManager(const core::dbus::Bus::Ptr &bus, ApplicationManager::ApplicationManager(
const core::dbus::Object::Ptr& object, const core::dbus::Bus::Ptr &bus, const core::dbus::Object::Ptr &object,
const std::shared_ptr<anbox::ApplicationManager> &impl) : const std::shared_ptr<anbox::ApplicationManager> &impl)
bus_(bus), : bus_(bus), object_(object), impl_(impl) {
object_(object), object_->install_method_handler<
impl_(impl) { anbox::dbus::interface::ApplicationManager::Methods::Launch>(
[this](const core::dbus::Message::Ptr &msg) {
object_->install_method_handler<anbox::dbus::interface::ApplicationManager::Methods::Launch>(
[this](const core::dbus::Message::Ptr &msg) {
auto reader = msg->reader(); auto reader = msg->reader();
android::Intent intent; android::Intent intent;
@ -45,25 +43,22 @@ ApplicationManager::ApplicationManager(const core::dbus::Bus::Ptr &bus,
core::dbus::Message::Ptr reply; core::dbus::Message::Ptr reply;
try { try {
launch(intent); launch(intent);
reply = core::dbus::Message::make_method_return(msg); reply = core::dbus::Message::make_method_return(msg);
} } catch (std::exception const &err) {
catch (std::exception const &err) { reply = core::dbus::Message::make_error(msg, "org.anbox.Error.Failed",
reply = core::dbus::Message::make_error(msg, err.what());
"org.anbox.Error.Failed",
err.what());
} }
bus_->send(reply); bus_->send(reply);
}); });
} }
ApplicationManager::~ApplicationManager() { ApplicationManager::~ApplicationManager() {}
}
void ApplicationManager::launch(const android::Intent &intent) { void ApplicationManager::launch(const android::Intent &intent) {
impl_->launch(intent); impl_->launch(intent);
} }
} // namespace skeleton } // namespace skeleton
} // namespace dbus } // namespace dbus
} // namespace anbox } // namespace anbox

View file

@ -28,22 +28,22 @@ namespace anbox {
namespace dbus { namespace dbus {
namespace skeleton { namespace skeleton {
class ApplicationManager : public anbox::ApplicationManager { class ApplicationManager : public anbox::ApplicationManager {
public: public:
ApplicationManager(const core::dbus::Bus::Ptr &bus, ApplicationManager(const core::dbus::Bus::Ptr &bus,
const core::dbus::Object::Ptr& object, const core::dbus::Object::Ptr &object,
const std::shared_ptr<anbox::ApplicationManager> &impl); const std::shared_ptr<anbox::ApplicationManager> &impl);
~ApplicationManager(); ~ApplicationManager();
void launch(const android::Intent &intent) override; void launch(const android::Intent &intent) override;
private: private:
core::dbus::Bus::Ptr bus_; core::dbus::Bus::Ptr bus_;
core::dbus::Service::Ptr service_; core::dbus::Service::Ptr service_;
core::dbus::Object::Ptr object_; core::dbus::Object::Ptr object_;
std::shared_ptr<anbox::ApplicationManager> impl_; std::shared_ptr<anbox::ApplicationManager> impl_;
}; };
} // namespace skeleton } // namespace skeleton
} // namespace dbus } // namespace dbus
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -16,31 +16,33 @@
*/ */
#include "anbox/dbus/skeleton/service.h" #include "anbox/dbus/skeleton/service.h"
#include "anbox/dbus/skeleton/application_manager.h"
#include "anbox/dbus/interface.h" #include "anbox/dbus/interface.h"
#include "anbox/dbus/skeleton/application_manager.h"
namespace anbox { namespace anbox {
namespace dbus { namespace dbus {
namespace skeleton { namespace skeleton {
std::shared_ptr<Service> Service::create_for_bus(const core::dbus::Bus::Ptr &bus, std::shared_ptr<Service> Service::create_for_bus(
const std::shared_ptr<anbox::ApplicationManager> &application_manager) { const core::dbus::Bus::Ptr &bus,
auto service = core::dbus::Service::add_service(bus, anbox::dbus::interface::Service::name()); const std::shared_ptr<anbox::ApplicationManager> &application_manager) {
auto object = service->add_object_for_path(anbox::dbus::interface::Service::path()); auto service = core::dbus::Service::add_service(
return std::make_shared<Service>(bus, service, object, application_manager); bus, anbox::dbus::interface::Service::name());
auto object =
service->add_object_for_path(anbox::dbus::interface::Service::path());
return std::make_shared<Service>(bus, service, object, application_manager);
} }
Service::Service(const core::dbus::Bus::Ptr &bus, Service::Service(
const core::dbus::Service::Ptr& service, const core::dbus::Bus::Ptr &bus, const core::dbus::Service::Ptr &service,
const core::dbus::Object::Ptr& object, const core::dbus::Object::Ptr &object,
const std::shared_ptr<anbox::ApplicationManager> &application_manager) : const std::shared_ptr<anbox::ApplicationManager> &application_manager)
bus_(bus), : bus_(bus),
service_(service), service_(service),
object_(object), object_(object),
application_manager_(std::make_shared<ApplicationManager>(bus_, object_, application_manager)) { application_manager_(std::make_shared<ApplicationManager>(
} bus_, object_, application_manager)) {}
Service::~Service() { Service::~Service() {}
} } // namespace skeleton
} // namespace skeleton } // namespace dbus
} // namespace dbus } // namespace anbox
} // namespace anbox

View file

@ -18,8 +18,8 @@
#ifndef ANBOX_DBUS_SKELETON_SERVICE_H_ #ifndef ANBOX_DBUS_SKELETON_SERVICE_H_
#define ANBOX_DBUS_SKELETON_SERVICE_H_ #define ANBOX_DBUS_SKELETON_SERVICE_H_
#include "anbox/do_not_copy_or_move.h"
#include "anbox/application_manager.h" #include "anbox/application_manager.h"
#include "anbox/do_not_copy_or_move.h"
#include <core/dbus/bus.h> #include <core/dbus/bus.h>
#include <core/dbus/object.h> #include <core/dbus/object.h>
@ -30,24 +30,25 @@ namespace dbus {
namespace skeleton { namespace skeleton {
class ApplicationManager; class ApplicationManager;
class Service : public DoNotCopyOrMove { class Service : public DoNotCopyOrMove {
public: public:
static std::shared_ptr<Service> create_for_bus(const core::dbus::Bus::Ptr &bus, static std::shared_ptr<Service> create_for_bus(
const std::shared_ptr<anbox::ApplicationManager> &application_manager); const core::dbus::Bus::Ptr &bus,
const std::shared_ptr<anbox::ApplicationManager> &application_manager);
Service(const core::dbus::Bus::Ptr &bus, Service(
const core::dbus::Service::Ptr& service, const core::dbus::Bus::Ptr &bus, const core::dbus::Service::Ptr &service,
const core::dbus::Object::Ptr& object, const core::dbus::Object::Ptr &object,
const std::shared_ptr<anbox::ApplicationManager> &application_manager); const std::shared_ptr<anbox::ApplicationManager> &application_manager);
~Service(); ~Service();
private: private:
core::dbus::Bus::Ptr bus_; core::dbus::Bus::Ptr bus_;
core::dbus::Service::Ptr service_; core::dbus::Service::Ptr service_;
core::dbus::Object::Ptr object_; core::dbus::Object::Ptr object_;
std::shared_ptr<ApplicationManager> application_manager_; std::shared_ptr<ApplicationManager> application_manager_;
}; };
} // namespace skeleton } // namespace skeleton
} // namespace dbus } // namespace dbus
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -22,37 +22,31 @@
namespace anbox { namespace anbox {
namespace dbus { namespace dbus {
namespace stub { namespace stub {
std::shared_ptr<ApplicationManager> ApplicationManager::create_for_bus(const core::dbus::Bus::Ptr &bus) { std::shared_ptr<ApplicationManager> ApplicationManager::create_for_bus(
auto service = core::dbus::Service::use_service(bus, anbox::dbus::interface::Service::name()); const core::dbus::Bus::Ptr &bus) {
auto object = service->add_object_for_path(anbox::dbus::interface::Service::path()); auto service = core::dbus::Service::use_service(
return std::make_shared<ApplicationManager>(bus, service, object); bus, anbox::dbus::interface::Service::name());
auto object =
service->add_object_for_path(anbox::dbus::interface::Service::path());
return std::make_shared<ApplicationManager>(bus, service, object);
} }
ApplicationManager::ApplicationManager(const core::dbus::Bus::Ptr &bus, ApplicationManager::ApplicationManager(const core::dbus::Bus::Ptr &bus,
const core::dbus::Service::Ptr& service, const core::dbus::Service::Ptr &service,
const core::dbus::Object::Ptr& object) : const core::dbus::Object::Ptr &object)
bus_(bus), : bus_(bus), service_(service), object_(object) {}
service_(service),
object_(object) {
}
ApplicationManager::~ApplicationManager() { ApplicationManager::~ApplicationManager() {}
}
void ApplicationManager::launch(const android::Intent &intent) { void ApplicationManager::launch(const android::Intent &intent) {
auto result = object_->invoke_method_synchronously< auto result = object_->invoke_method_synchronously<
anbox::dbus::interface::ApplicationManager::Methods::Launch, anbox::dbus::interface::ApplicationManager::Methods::Launch,
anbox::dbus::interface::ApplicationManager::Methods::Launch::ResultType>( anbox::dbus::interface::ApplicationManager::Methods::Launch::ResultType>(
intent.action, intent.action, intent.uri, intent.type, intent.flags, intent.package,
intent.uri, intent.component);
intent.type,
intent.flags,
intent.package,
intent.component);
if (result.is_error()) if (result.is_error()) throw std::runtime_error(result.error().print());
throw std::runtime_error(result.error().print());
} }
} // namespace skeleton } // namespace skeleton
} // namespace dbus } // namespace dbus
} // namespace anbox } // namespace anbox

View file

@ -28,23 +28,24 @@ namespace anbox {
namespace dbus { namespace dbus {
namespace stub { namespace stub {
class ApplicationManager : public anbox::ApplicationManager { class ApplicationManager : public anbox::ApplicationManager {
public: public:
static std::shared_ptr<ApplicationManager> create_for_bus(const core::dbus::Bus::Ptr &bus); static std::shared_ptr<ApplicationManager> create_for_bus(
const core::dbus::Bus::Ptr &bus);
ApplicationManager(const core::dbus::Bus::Ptr &bus, ApplicationManager(const core::dbus::Bus::Ptr &bus,
const core::dbus::Service::Ptr& service, const core::dbus::Service::Ptr &service,
const core::dbus::Object::Ptr& object); const core::dbus::Object::Ptr &object);
~ApplicationManager(); ~ApplicationManager();
void launch(const android::Intent &intent) override; void launch(const android::Intent &intent) override;
private: private:
core::dbus::Bus::Ptr bus_; core::dbus::Bus::Ptr bus_;
core::dbus::Service::Ptr service_; core::dbus::Service::Ptr service_;
core::dbus::Object::Ptr object_; core::dbus::Object::Ptr object_;
}; };
} // namespace stub } // namespace stub
} // namespace dbus } // namespace dbus
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -25,20 +25,17 @@
namespace anbox { namespace anbox {
class DeferAction : public DoNotCopyOrMove { class DeferAction : public DoNotCopyOrMove {
public: public:
DeferAction(const std::function<void()> action) : DeferAction(const std::function<void()> action) : action_(action) {}
action_(action) {
}
~DeferAction() { ~DeferAction() {
if (action_) if (action_) action_();
action_(); }
}
private: private:
std::function<void()> action_; std::function<void()> action_;
}; };
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -20,19 +20,17 @@
namespace anbox { namespace anbox {
class DoNotCopyOrMove class DoNotCopyOrMove {
{ public:
public: DoNotCopyOrMove(const DoNotCopyOrMove&) = delete;
DoNotCopyOrMove(const DoNotCopyOrMove&) = delete; DoNotCopyOrMove(DoNotCopyOrMove&&) = delete;
DoNotCopyOrMove(DoNotCopyOrMove&&) = delete; virtual ~DoNotCopyOrMove() = default;
virtual ~DoNotCopyOrMove() = default; DoNotCopyOrMove& operator=(const DoNotCopyOrMove&) = delete;
DoNotCopyOrMove& operator=(const DoNotCopyOrMove&) = delete; DoNotCopyOrMove& operator=(DoNotCopyOrMove&&) = delete;
DoNotCopyOrMove& operator=(DoNotCopyOrMove&&) = delete;
protected: protected:
DoNotCopyOrMove() = default; DoNotCopyOrMove() = default;
}; };
} }
#endif #endif

View file

@ -21,19 +21,21 @@
namespace anbox { namespace anbox {
namespace graphics { namespace graphics {
/** /**
* @brief Defines different types of density being used in an Android system. See the * @brief Defines different types of density being used in an Android system.
* documentation in frameworks/base/core/java/android/util/DisplayMetrics.java * See the
* documentation in
* frameworks/base/core/java/android/util/DisplayMetrics.java
* of the Android source tree which defines the different types. * of the Android source tree which defines the different types.
*/ */
enum class DensityType { enum class DensityType {
low = 120, low = 120,
medium = 160, medium = 160,
tv = 213, tv = 213,
high = 240, high = 240,
xhigh = 360, xhigh = 360,
xxhigh = 480, xxhigh = 480,
}; };
} // namespace graphics } // namespace graphics
} // namespace anbox } // namespace anbox
#endif #endif

View file

@ -30,23 +30,23 @@ namespace {
// implemented as unsigned integers. These convenience template functions // implemented as unsigned integers. These convenience template functions
// help casting between them safely without generating compiler warnings. // help casting between them safely without generating compiler warnings.
inline void* SafePointerFromUInt(unsigned int handle) { inline void* SafePointerFromUInt(unsigned int handle) {
return (void*)(uintptr_t)(handle); return (void*)(uintptr_t)(handle);
} }
inline unsigned int SafeUIntFromPointer(const void* ptr) { inline unsigned int SafeUIntFromPointer(const void* ptr) {
#if 1 #if 1
// Ignore the assert below to avoid crashing when running older // Ignore the assert below to avoid crashing when running older
// system images, which might have buggy encoder libraries. Print // system images, which might have buggy encoder libraries. Print
// an error message though. // an error message though.
if ((uintptr_t)(ptr) != (unsigned int)(uintptr_t)(ptr)) { if ((uintptr_t)(ptr) != (unsigned int)(uintptr_t)(ptr)) {
fprintf(stderr, "EmuGL:WARNING: bad generic pointer %p\n", ptr); fprintf(stderr, "EmuGL:WARNING: bad generic pointer %p\n", ptr);
} }
#else #else
// Assertion error if the pointer contains a value that does not fit // Assertion error if the pointer contains a value that does not fit
// in an unsigned integer! // in an unsigned integer!
assert((uintptr_t)(ptr) == (unsigned int)(uintptr_t)(ptr)); assert((uintptr_t)(ptr) == (unsigned int)(uintptr_t)(ptr));
#endif #endif
return (unsigned int)(uintptr_t)(ptr); return (unsigned int)(uintptr_t)(ptr);
} }
// Lazily create and bind a framebuffer object to the current host context. // Lazily create and bind a framebuffer object to the current host context.
@ -55,31 +55,28 @@ inline unsigned int SafeUIntFromPointer(const void* ptr) {
// on creation only. I.e. all rendering operations will target it. // on creation only. I.e. all rendering operations will target it.
// returns true in case of success, false on failure. // returns true in case of success, false on failure.
bool bindFbo(GLuint* fbo, GLuint tex) { bool bindFbo(GLuint* fbo, GLuint tex) {
if (*fbo) { if (*fbo) {
// fbo already exist - just bind // fbo already exist - just bind
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, *fbo);
return true;
}
s_gles2.glGenFramebuffers(1, fbo);
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, *fbo); s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, *fbo);
s_gles2.glFramebufferTexture2D(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0_OES,
GL_TEXTURE_2D, tex, 0);
GLenum status = s_gles2.glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE_OES) {
ERR("ColorBuffer::bindFbo: FBO not complete: %#x\n", status);
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0);
s_gles2.glDeleteFramebuffers(1, fbo);
*fbo = 0;
return false;
}
return true; return true;
}
s_gles2.glGenFramebuffers(1, fbo);
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, *fbo);
s_gles2.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0_OES,
GL_TEXTURE_2D, tex, 0);
GLenum status = s_gles2.glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE_OES) {
ERR("ColorBuffer::bindFbo: FBO not complete: %#x\n", status);
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0);
s_gles2.glDeleteFramebuffers(1, fbo);
*fbo = 0;
return false;
}
return true;
} }
void unbindFbo() { void unbindFbo() { s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0); }
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
// Helper class to use a ColorBuffer::Helper context. // Helper class to use a ColorBuffer::Helper context.
// Usage is pretty simple: // Usage is pretty simple:
@ -93,302 +90,268 @@ void unbindFbo() {
// } // automatically calls m_helper->teardownContext(); // } // automatically calls m_helper->teardownContext();
// //
class ScopedHelperContext { class ScopedHelperContext {
public: public:
ScopedHelperContext(ColorBuffer::Helper* helper) : mHelper(helper) { ScopedHelperContext(ColorBuffer::Helper* helper) : mHelper(helper) {
if (!helper->setupContext()) { if (!helper->setupContext()) {
mHelper = NULL; mHelper = NULL;
}
} }
}
bool isOk() const { return mHelper != NULL; } bool isOk() const { return mHelper != NULL; }
~ScopedHelperContext() { ~ScopedHelperContext() { release(); }
release();
void release() {
if (mHelper) {
mHelper->teardownContext();
mHelper = NULL;
} }
}
void release() { private:
if (mHelper) { ColorBuffer::Helper* mHelper;
mHelper->teardownContext();
mHelper = NULL;
}
}
private:
ColorBuffer::Helper* mHelper;
}; };
} // namespace } // namespace
// static // static
ColorBuffer* ColorBuffer::create(EGLDisplay p_display, ColorBuffer* ColorBuffer::create(EGLDisplay p_display, int p_width,
int p_width, int p_height, GLenum p_internalFormat,
int p_height, bool has_eglimage_texture_2d, Helper* helper) {
GLenum p_internalFormat, GLenum texInternalFormat = 0;
bool has_eglimage_texture_2d,
Helper* helper) {
GLenum texInternalFormat = 0;
switch (p_internalFormat) { switch (p_internalFormat) {
case GL_RGB: case GL_RGB:
case GL_RGB565_OES: case GL_RGB565_OES:
texInternalFormat = GL_RGB; texInternalFormat = GL_RGB;
break; break;
case GL_RGBA: case GL_RGBA:
case GL_RGB5_A1_OES: case GL_RGB5_A1_OES:
case GL_RGBA4_OES: case GL_RGBA4_OES:
texInternalFormat = GL_RGBA; texInternalFormat = GL_RGBA;
break; break;
default: default:
return NULL; return NULL;
break; break;
} }
ScopedHelperContext context(helper); ScopedHelperContext context(helper);
if (!context.isOk()) { if (!context.isOk()) {
return NULL; return NULL;
} }
ColorBuffer *cb = new ColorBuffer(p_display, helper); ColorBuffer* cb = new ColorBuffer(p_display, helper);
s_gles2.glGenTextures(1, &cb->m_tex); s_gles2.glGenTextures(1, &cb->m_tex);
s_gles2.glBindTexture(GL_TEXTURE_2D, cb->m_tex); s_gles2.glBindTexture(GL_TEXTURE_2D, cb->m_tex);
int nComp = (texInternalFormat == GL_RGB ? 3 : 4); int nComp = (texInternalFormat == GL_RGB ? 3 : 4);
char* zBuff = static_cast<char*>(::calloc(nComp * p_width * p_height, 1)); char* zBuff = static_cast<char*>(::calloc(nComp * p_width * p_height, 1));
s_gles2.glTexImage2D(GL_TEXTURE_2D, s_gles2.glTexImage2D(GL_TEXTURE_2D, 0, texInternalFormat, p_width, p_height,
0, 0, texInternalFormat, GL_UNSIGNED_BYTE, zBuff);
texInternalFormat, ::free(zBuff);
p_width,
p_height,
0,
texInternalFormat,
GL_UNSIGNED_BYTE,
zBuff);
::free(zBuff);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// //
// create another texture for that colorbuffer for blit // create another texture for that colorbuffer for blit
// //
s_gles2.glGenTextures(1, &cb->m_blitTex); s_gles2.glGenTextures(1, &cb->m_blitTex);
s_gles2.glBindTexture(GL_TEXTURE_2D, cb->m_blitTex); s_gles2.glBindTexture(GL_TEXTURE_2D, cb->m_blitTex);
s_gles2.glTexImage2D(GL_TEXTURE_2D, s_gles2.glTexImage2D(GL_TEXTURE_2D, 0, texInternalFormat, p_width, p_height,
0, 0, texInternalFormat, GL_UNSIGNED_BYTE, NULL);
texInternalFormat,
p_width,
p_height,
0,
texInternalFormat,
GL_UNSIGNED_BYTE,
NULL);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
cb->m_width = p_width; cb->m_width = p_width;
cb->m_height = p_height; cb->m_height = p_height;
cb->m_internalFormat = texInternalFormat; cb->m_internalFormat = texInternalFormat;
if (has_eglimage_texture_2d) { if (has_eglimage_texture_2d) {
cb->m_eglImage = s_egl.eglCreateImageKHR( cb->m_eglImage = s_egl.eglCreateImageKHR(
p_display, p_display, s_egl.eglGetCurrentContext(), EGL_GL_TEXTURE_2D_KHR,
s_egl.eglGetCurrentContext(), (EGLClientBuffer)SafePointerFromUInt(cb->m_tex), NULL);
EGL_GL_TEXTURE_2D_KHR,
(EGLClientBuffer)SafePointerFromUInt(cb->m_tex),
NULL);
cb->m_blitEGLImage = s_egl.eglCreateImageKHR( cb->m_blitEGLImage = s_egl.eglCreateImageKHR(
p_display, p_display, s_egl.eglGetCurrentContext(), EGL_GL_TEXTURE_2D_KHR,
s_egl.eglGetCurrentContext(), (EGLClientBuffer)SafePointerFromUInt(cb->m_blitTex), NULL);
EGL_GL_TEXTURE_2D_KHR, }
(EGLClientBuffer)SafePointerFromUInt(cb->m_blitTex),
NULL);
}
cb->m_resizer = new TextureResize(p_width, p_height); cb->m_resizer = new TextureResize(p_width, p_height);
return cb; return cb;
} }
ColorBuffer::ColorBuffer(EGLDisplay display, Helper* helper) : ColorBuffer::ColorBuffer(EGLDisplay display, Helper* helper)
m_tex(0), : m_tex(0),
m_blitTex(0), m_blitTex(0),
m_eglImage(NULL), m_eglImage(NULL),
m_blitEGLImage(NULL), m_blitEGLImage(NULL),
m_fbo(0), m_fbo(0),
m_internalFormat(0), m_internalFormat(0),
m_display(display), m_display(display),
m_helper(helper) {} m_helper(helper) {}
ColorBuffer::~ColorBuffer() { ColorBuffer::~ColorBuffer() {
ScopedHelperContext context(m_helper); ScopedHelperContext context(m_helper);
if (m_blitEGLImage) { if (m_blitEGLImage) {
s_egl.eglDestroyImageKHR(m_display, m_blitEGLImage); s_egl.eglDestroyImageKHR(m_display, m_blitEGLImage);
} }
if (m_eglImage) { if (m_eglImage) {
s_egl.eglDestroyImageKHR(m_display, m_eglImage); s_egl.eglDestroyImageKHR(m_display, m_eglImage);
} }
if (m_fbo) { if (m_fbo) {
s_gles2.glDeleteFramebuffers(1, &m_fbo); s_gles2.glDeleteFramebuffers(1, &m_fbo);
} }
GLuint tex[2] = {m_tex, m_blitTex}; GLuint tex[2] = {m_tex, m_blitTex};
s_gles2.glDeleteTextures(2, tex); s_gles2.glDeleteTextures(2, tex);
delete m_resizer; delete m_resizer;
} }
void ColorBuffer::readPixels(int x, void ColorBuffer::readPixels(int x, int y, int width, int height,
int y, GLenum p_format, GLenum p_type, void* pixels) {
int width, ScopedHelperContext context(m_helper);
int height, if (!context.isOk()) {
GLenum p_format, return;
GLenum p_type, }
void* pixels) {
ScopedHelperContext context(m_helper);
if (!context.isOk()) {
return;
}
if (bindFbo(&m_fbo, m_tex)) { if (bindFbo(&m_fbo, m_tex)) {
s_gles2.glReadPixels(x, y, width, height, p_format, p_type, pixels); s_gles2.glReadPixels(x, y, width, height, p_format, p_type, pixels);
unbindFbo();
}
}
void ColorBuffer::subUpdate(int x,
int y,
int width,
int height,
GLenum p_format,
GLenum p_type,
void* pixels) {
ScopedHelperContext context(m_helper);
if (!context.isOk()) {
return;
}
s_gles2.glBindTexture(GL_TEXTURE_2D, m_tex);
s_gles2.glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
s_gles2.glTexSubImage2D(
GL_TEXTURE_2D, 0, x, y, width, height, p_format, p_type, pixels);
}
bool ColorBuffer::blitFromCurrentReadBuffer()
{
RenderThreadInfo *tInfo = RenderThreadInfo::get();
if (!tInfo->currContext.Ptr()) {
// no Current context
return false;
}
// Copy the content of the current read surface into m_blitEGLImage.
// This is done by creating a temporary texture, bind it to the EGLImage
// then call glCopyTexSubImage2D().
GLuint tmpTex;
GLint currTexBind;
if (tInfo->currContext->isGL2()) {
s_gles2.glGetIntegerv(GL_TEXTURE_BINDING_2D, &currTexBind);
s_gles2.glGenTextures(1,&tmpTex);
s_gles2.glBindTexture(GL_TEXTURE_2D, tmpTex);
s_gles2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_blitEGLImage);
s_gles2.glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0,
m_width, m_height);
s_gles2.glDeleteTextures(1, &tmpTex);
s_gles2.glBindTexture(GL_TEXTURE_2D, currTexBind);
}
else {
s_gles1.glGetIntegerv(GL_TEXTURE_BINDING_2D, &currTexBind);
s_gles1.glGenTextures(1,&tmpTex);
s_gles1.glBindTexture(GL_TEXTURE_2D, tmpTex);
s_gles1.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_blitEGLImage);
s_gles1.glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0,
m_width, m_height);
s_gles1.glDeleteTextures(1, &tmpTex);
s_gles1.glBindTexture(GL_TEXTURE_2D, currTexBind);
}
ScopedHelperContext context(m_helper);
if (!context.isOk()) {
return false;
}
if (!bindFbo(&m_fbo, m_tex)) {
return false;
}
// Save current viewport and match it to the current colorbuffer size.
GLint vport[4] = { 0, };
s_gles2.glGetIntegerv(GL_VIEWPORT, vport);
s_gles2.glViewport(0, 0, m_width, m_height);
// render m_blitTex
m_helper->getTextureDraw()->draw(m_blitTex);
// Restore previous viewport.
s_gles2.glViewport(vport[0], vport[1], vport[2], vport[3]);
unbindFbo(); unbindFbo();
}
}
return true; void ColorBuffer::subUpdate(int x, int y, int width, int height,
GLenum p_format, GLenum p_type, void* pixels) {
ScopedHelperContext context(m_helper);
if (!context.isOk()) {
return;
}
s_gles2.glBindTexture(GL_TEXTURE_2D, m_tex);
s_gles2.glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
s_gles2.glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, p_format,
p_type, pixels);
}
bool ColorBuffer::blitFromCurrentReadBuffer() {
RenderThreadInfo* tInfo = RenderThreadInfo::get();
if (!tInfo->currContext.Ptr()) {
// no Current context
return false;
}
// Copy the content of the current read surface into m_blitEGLImage.
// This is done by creating a temporary texture, bind it to the EGLImage
// then call glCopyTexSubImage2D().
GLuint tmpTex;
GLint currTexBind;
if (tInfo->currContext->isGL2()) {
s_gles2.glGetIntegerv(GL_TEXTURE_BINDING_2D, &currTexBind);
s_gles2.glGenTextures(1, &tmpTex);
s_gles2.glBindTexture(GL_TEXTURE_2D, tmpTex);
s_gles2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_blitEGLImage);
s_gles2.glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_width,
m_height);
s_gles2.glDeleteTextures(1, &tmpTex);
s_gles2.glBindTexture(GL_TEXTURE_2D, currTexBind);
} else {
s_gles1.glGetIntegerv(GL_TEXTURE_BINDING_2D, &currTexBind);
s_gles1.glGenTextures(1, &tmpTex);
s_gles1.glBindTexture(GL_TEXTURE_2D, tmpTex);
s_gles1.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_blitEGLImage);
s_gles1.glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_width,
m_height);
s_gles1.glDeleteTextures(1, &tmpTex);
s_gles1.glBindTexture(GL_TEXTURE_2D, currTexBind);
}
ScopedHelperContext context(m_helper);
if (!context.isOk()) {
return false;
}
if (!bindFbo(&m_fbo, m_tex)) {
return false;
}
// Save current viewport and match it to the current colorbuffer size.
GLint vport[4] = {
0,
};
s_gles2.glGetIntegerv(GL_VIEWPORT, vport);
s_gles2.glViewport(0, 0, m_width, m_height);
// render m_blitTex
m_helper->getTextureDraw()->draw(m_blitTex);
// Restore previous viewport.
s_gles2.glViewport(vport[0], vport[1], vport[2], vport[3]);
unbindFbo();
return true;
} }
bool ColorBuffer::bindToTexture() { bool ColorBuffer::bindToTexture() {
if (!m_eglImage) { if (!m_eglImage) {
return false; return false;
} }
RenderThreadInfo *tInfo = RenderThreadInfo::get(); RenderThreadInfo* tInfo = RenderThreadInfo::get();
if (!tInfo->currContext.Ptr()) { if (!tInfo->currContext.Ptr()) {
return false; return false;
} }
if (tInfo->currContext->isGL2()) { if (tInfo->currContext->isGL2()) {
s_gles2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage); s_gles2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage);
} } else {
else { s_gles1.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage);
s_gles1.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage); }
} return true;
return true;
} }
bool ColorBuffer::bindToRenderbuffer() { bool ColorBuffer::bindToRenderbuffer() {
if (!m_eglImage) { if (!m_eglImage) {
return false; return false;
} }
RenderThreadInfo *tInfo = RenderThreadInfo::get(); RenderThreadInfo* tInfo = RenderThreadInfo::get();
if (!tInfo->currContext.Ptr()) { if (!tInfo->currContext.Ptr()) {
return false; return false;
} }
if (tInfo->currContext->isGL2()) { if (tInfo->currContext->isGL2()) {
s_gles2.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES, m_eglImage); s_gles2.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES,
} m_eglImage);
else { } else {
s_gles1.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES, m_eglImage); s_gles1.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES,
} m_eglImage);
return true; }
return true;
} }
void ColorBuffer::readback(unsigned char* img) { void ColorBuffer::readback(unsigned char* img) {
ScopedHelperContext context(m_helper); ScopedHelperContext context(m_helper);
if (!context.isOk()) { if (!context.isOk()) {
return; return;
} }
if (bindFbo(&m_fbo, m_tex)) { if (bindFbo(&m_fbo, m_tex)) {
s_gles2.glReadPixels( s_gles2.glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE,
0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, img); img);
unbindFbo(); unbindFbo();
} }
} }
void ColorBuffer::bind() { void ColorBuffer::bind() {
const auto id = m_resizer->update(m_tex); const auto id = m_resizer->update(m_tex);
s_gles2.glBindTexture(GL_TEXTURE_2D, id); s_gles2.glBindTexture(GL_TEXTURE_2D, id);
} }

View file

@ -55,96 +55,84 @@ class TextureResize;
// As an additional twist. // As an additional twist.
class ColorBuffer { class ColorBuffer {
public: public:
// Helper interface class used during ColorBuffer operations. This is // Helper interface class used during ColorBuffer operations. This is
// introduced to remove coupling from the FrameBuffer class implementation. // introduced to remove coupling from the FrameBuffer class implementation.
class Helper { class Helper {
public: public:
Helper() {} Helper() {}
virtual ~Helper() {} virtual ~Helper() {}
virtual bool setupContext() = 0; virtual bool setupContext() = 0;
virtual void teardownContext() = 0; virtual void teardownContext() = 0;
virtual TextureDraw* getTextureDraw() const = 0; virtual TextureDraw* getTextureDraw() const = 0;
}; };
// Create a new ColorBuffer instance. // Create a new ColorBuffer instance.
// |p_display| is the host EGLDisplay handle. // |p_display| is the host EGLDisplay handle.
// |p_width| and |p_height| are the buffer's dimensions in pixels. // |p_width| and |p_height| are the buffer's dimensions in pixels.
// |p_internalFormat| is the internal pixel format to use, valid values // |p_internalFormat| is the internal pixel format to use, valid values
// are: GL_RGB, GL_RGB565, GL_RGBA, GL_RGB5_A1_OES and GL_RGBA4_OES. // are: GL_RGB, GL_RGB565, GL_RGBA, GL_RGB5_A1_OES and GL_RGBA4_OES.
// Implementation is free to use something else though. // Implementation is free to use something else though.
// |has_eglimage_texture_2d| should be true iff the display supports // |has_eglimage_texture_2d| should be true iff the display supports
// the EGL_KHR_gl_texture_2D_image extension. // the EGL_KHR_gl_texture_2D_image extension.
// Returns NULL on failure. // Returns NULL on failure.
static ColorBuffer* create(EGLDisplay p_display, static ColorBuffer* create(EGLDisplay p_display, int p_width, int p_height,
int p_width, GLenum p_internalFormat,
int p_height, bool has_eglimage_texture_2d, Helper* helper);
GLenum p_internalFormat,
bool has_eglimage_texture_2d,
Helper* helper);
// Destructor. // Destructor.
~ColorBuffer(); ~ColorBuffer();
// Return ColorBuffer width and height in pixels // Return ColorBuffer width and height in pixels
GLuint getWidth() const { return m_width; } GLuint getWidth() const { return m_width; }
GLuint getHeight() const { return m_height; } GLuint getHeight() const { return m_height; }
// Read the ColorBuffer instance's pixel values into host memory. // Read the ColorBuffer instance's pixel values into host memory.
void readPixels(int x, void readPixels(int x, int y, int width, int height, GLenum p_format,
int y, GLenum p_type, void* pixels);
int width,
int height,
GLenum p_format,
GLenum p_type,
void *pixels);
// Update the ColorBuffer instance's pixel values from host memory. // Update the ColorBuffer instance's pixel values from host memory.
void subUpdate(int x, void subUpdate(int x, int y, int width, int height, GLenum p_format,
int y, GLenum p_type, void* pixels);
int width,
int height,
GLenum p_format,
GLenum p_type,
void *pixels);
// Bind the current context's EGL_TEXTURE_2D texture to this ColorBuffer's // Bind the current context's EGL_TEXTURE_2D texture to this ColorBuffer's
// EGLImage. This is intended to implement glEGLImageTargetTexture2DOES() // EGLImage. This is intended to implement glEGLImageTargetTexture2DOES()
// for all GLES versions. // for all GLES versions.
bool bindToTexture(); bool bindToTexture();
// Bind the current context's EGL_RENDERBUFFER_OES render buffer to this // Bind the current context's EGL_RENDERBUFFER_OES render buffer to this
// ColorBuffer's EGLImage. This is intended to implement // ColorBuffer's EGLImage. This is intended to implement
// glEGLImageTargetRenderbufferStorageOES() for all GLES versions. // glEGLImageTargetRenderbufferStorageOES() for all GLES versions.
bool bindToRenderbuffer(); bool bindToRenderbuffer();
// Copy the content of the current context's read surface to this // Copy the content of the current context's read surface to this
// ColorBuffer. This is used from WindowSurface::flushColorBuffer(). // ColorBuffer. This is used from WindowSurface::flushColorBuffer().
// Return true on success, false on failure (e.g. no current context). // Return true on success, false on failure (e.g. no current context).
bool blitFromCurrentReadBuffer(); bool blitFromCurrentReadBuffer();
// Read the content of the whole ColorBuffer as 32-bit RGBA pixels. // Read the content of the whole ColorBuffer as 32-bit RGBA pixels.
// |img| must be a buffer large enough (i.e. width * height * 4). // |img| must be a buffer large enough (i.e. width * height * 4).
void readback(unsigned char* img); void readback(unsigned char* img);
void bind(); void bind();
private:
ColorBuffer(); // no default constructor.
explicit ColorBuffer(EGLDisplay display, Helper* helper); private:
ColorBuffer(); // no default constructor.
private: explicit ColorBuffer(EGLDisplay display, Helper* helper);
GLuint m_tex;
GLuint m_blitTex; private:
EGLImageKHR m_eglImage; GLuint m_tex;
EGLImageKHR m_blitEGLImage; GLuint m_blitTex;
GLuint m_width; EGLImageKHR m_eglImage;
GLuint m_height; EGLImageKHR m_blitEGLImage;
GLuint m_fbo; GLuint m_width;
GLenum m_internalFormat; GLuint m_height;
EGLDisplay m_display; GLuint m_fbo;
Helper* m_helper; GLenum m_internalFormat;
TextureResize * m_resizer; EGLDisplay m_display;
Helper* m_helper;
TextureResize* m_resizer;
}; };
typedef emugl::SmartPtr<ColorBuffer> ColorBufferPtr; typedef emugl::SmartPtr<ColorBuffer> ColorBufferPtr;

View file

@ -21,22 +21,18 @@ namespace {
std::shared_ptr<DisplayManager> display_mgr; std::shared_ptr<DisplayManager> display_mgr;
class NullDisplayManager : public DisplayManager { class NullDisplayManager : public DisplayManager {
public: public:
DisplayInfo display_info() const override { DisplayInfo display_info() const override { return {1280, 720}; }
return {1280, 720};
}
}; };
} }
DisplayManager::~DisplayManager() { DisplayManager::~DisplayManager() {}
}
std::shared_ptr<DisplayManager> DisplayManager::get() { std::shared_ptr<DisplayManager> DisplayManager::get() {
if (!display_mgr) if (!display_mgr) display_mgr = std::make_shared<NullDisplayManager>();
display_mgr = std::make_shared<NullDisplayManager>(); return display_mgr;
return display_mgr;
} }
void registerDisplayManager(const std::shared_ptr<DisplayManager> &mgr) { void registerDisplayManager(const std::shared_ptr<DisplayManager> &mgr) {
display_mgr = mgr; display_mgr = mgr;
} }

View file

@ -21,17 +21,17 @@
#include <memory> #include <memory>
class DisplayManager { class DisplayManager {
public: public:
virtual ~DisplayManager(); virtual ~DisplayManager();
struct DisplayInfo { struct DisplayInfo {
int horizontal_resolution; int horizontal_resolution;
int vertical_resolution; int vertical_resolution;
}; };
virtual DisplayInfo display_info() const = 0; virtual DisplayInfo display_info() const = 0;
static std::shared_ptr<DisplayManager> get(); static std::shared_ptr<DisplayManager> get();
}; };
void registerDisplayManager(const std::shared_ptr<DisplayManager> &mgr); void registerDisplayManager(const std::shared_ptr<DisplayManager> &mgr);

View file

@ -14,61 +14,54 @@
* limitations under the License. * limitations under the License.
*/ */
#include "ReadBuffer.h" #include "ReadBuffer.h"
#include <string.h>
#include <assert.h> #include <assert.h>
#include <limits.h> #include <limits.h>
#include <string.h>
#include "ErrorLog.h" #include "ErrorLog.h"
ReadBuffer::ReadBuffer(size_t bufsize) ReadBuffer::ReadBuffer(size_t bufsize) {
{ m_size = bufsize;
m_size = bufsize; m_buf = (unsigned char*)malloc(m_size * sizeof(unsigned char));
m_buf = (unsigned char*)malloc(m_size*sizeof(unsigned char)); m_validData = 0;
m_validData = 0; m_readPtr = m_buf;
m_readPtr = m_buf;
} }
ReadBuffer::~ReadBuffer() ReadBuffer::~ReadBuffer() { free(m_buf); }
{
free(m_buf);
}
int ReadBuffer::getData(IOStream *stream) int ReadBuffer::getData(IOStream* stream) {
{ if (stream == NULL) return -1;
if(stream == NULL) if ((m_validData > 0) && (m_readPtr > m_buf)) {
return -1; memmove(m_buf, m_readPtr, m_validData);
if ((m_validData > 0) && (m_readPtr > m_buf)) { }
memmove(m_buf, m_readPtr, m_validData); // get fresh data into the buffer;
size_t len = m_size - m_validData;
if (len == 0) {
// we need to inc our buffer
size_t new_size = m_size * 2;
unsigned char* new_buf;
if (new_size < m_size) { // overflow check
new_size = INT_MAX;
} }
// get fresh data into the buffer;
size_t len = m_size - m_validData;
if (len==0) {
//we need to inc our buffer
size_t new_size = m_size*2;
unsigned char* new_buf;
if (new_size < m_size) { // overflow check
new_size = INT_MAX;
}
new_buf = (unsigned char*)realloc(m_buf, new_size); new_buf = (unsigned char*)realloc(m_buf, new_size);
if (!new_buf) { if (!new_buf) {
ERR("Failed to alloc %zu bytes for ReadBuffer\n", new_size); ERR("Failed to alloc %zu bytes for ReadBuffer\n", new_size);
return -1; return -1;
}
m_size = new_size;
m_buf = new_buf;
len = m_size - m_validData;
} }
m_readPtr = m_buf; m_size = new_size;
if (NULL != stream->read(m_buf + m_validData, &len)) { m_buf = new_buf;
m_validData += len; len = m_size - m_validData;
return len; }
} m_readPtr = m_buf;
return -1; if (NULL != stream->read(m_buf + m_validData, &len)) {
m_validData += len;
return len;
}
return -1;
} }
void ReadBuffer::consume(size_t amount) void ReadBuffer::consume(size_t amount) {
{ assert(amount <= m_validData);
assert(amount <= m_validData); m_validData -= amount;
m_validData -= amount; m_readPtr += amount;
m_readPtr += amount;
} }

View file

@ -19,17 +19,19 @@
#include "IOStream.h" #include "IOStream.h"
class ReadBuffer { class ReadBuffer {
public: public:
ReadBuffer(size_t bufSize); ReadBuffer(size_t bufSize);
~ReadBuffer(); ~ReadBuffer();
int getData(IOStream *stream); // get fresh data from the stream int getData(IOStream *stream); // get fresh data from the stream
unsigned char *buf() { return m_readPtr; } // return the next read location unsigned char *buf() { return m_readPtr; } // return the next read location
size_t validData() { return m_validData; } // return the amount of valid data in readptr size_t validData() {
void consume(size_t amount); // notify that 'amount' data has been consumed; return m_validData;
private: } // return the amount of valid data in readptr
unsigned char *m_buf; void consume(size_t amount); // notify that 'amount' data has been consumed;
unsigned char *m_readPtr; private:
size_t m_size; unsigned char *m_buf;
size_t m_validData; unsigned char *m_readPtr;
size_t m_size;
size_t m_validData;
}; };
#endif #endif

View file

@ -41,210 +41,200 @@ static char s_renderAddr[256];
static RenderWindow* s_renderWindow = NULL; static RenderWindow* s_renderWindow = NULL;
static IOStream *createRenderThread(int p_stream_buffer_size, static IOStream* createRenderThread(int p_stream_buffer_size,
unsigned int clientFlags); unsigned int clientFlags);
RENDER_APICALL int RENDER_APIENTRY initLibrary(void) RENDER_APICALL int RENDER_APIENTRY initLibrary(void) {
{ //
// // Load EGL Plugin
// Load EGL Plugin //
// if (!init_egl_dispatch()) {
if (!init_egl_dispatch()) { // Failed to load EGL
// Failed to load EGL printf("Failed to init_egl_dispatch\n");
printf("Failed to init_egl_dispatch\n"); return false;
return false; }
}
// //
// Load GLES Plugin // Load GLES Plugin
// //
if (!gles1_dispatch_init(&s_gles1)) { if (!gles1_dispatch_init(&s_gles1)) {
// Failed to load GLES // Failed to load GLES
ERR("Failed to gles1_dispatch_init\n"); ERR("Failed to gles1_dispatch_init\n");
return false; return false;
} }
/* failure to init the GLES2 dispatch table is not fatal */ /* failure to init the GLES2 dispatch table is not fatal */
if (!gles2_dispatch_init(&s_gles2)) { if (!gles2_dispatch_init(&s_gles2)) {
ERR("Failed to gles2_dispatch_init\n"); ERR("Failed to gles2_dispatch_init\n");
return false; return false;
} }
return true; return true;
} }
RENDER_APICALL int RENDER_APIENTRY initOpenGLRenderer( RENDER_APICALL int RENDER_APIENTRY initOpenGLRenderer(
EGLNativeDisplayType native_display, char* addr, size_t addrLen, EGLNativeDisplayType native_display, char* addr, size_t addrLen,
emugl_logger_struct logfuncs, emugl_crash_func_t crashfunc) { emugl_logger_struct logfuncs, emugl_crash_func_t crashfunc) {
set_emugl_crash_reporter(crashfunc); set_emugl_crash_reporter(crashfunc);
set_emugl_logger(logfuncs.coarse); set_emugl_logger(logfuncs.coarse);
set_emugl_cxt_logger(logfuncs.fine); set_emugl_cxt_logger(logfuncs.fine);
// //
// Fail if renderer is already initialized // Fail if renderer is already initialized
// //
if (s_renderThread) { if (s_renderThread) {
return false;
}
// kUseThread is used to determine whether the RenderWindow should use
// a separate thread to manage its subwindow GL/GLES context.
// For now, this feature is disabled entirely for the following
// reasons:
//
// - It must be disabled on Windows at all times, otherwise the main window becomes
// unresponsive after a few seconds of user interaction (e.g. trying to
// move it over the desktop). Probably due to the subtle issues around
// input on this platform (input-queue is global, message-queue is
// per-thread). Also, this messes considerably the display of the
// main window when running the executable under Wine.
//
// - On Linux/XGL and OSX/Cocoa, this used to be necessary to avoid corruption
// issues with the GL state of the main window when using the SDL UI.
// After the switch to Qt, this is no longer necessary and may actually cause
// undesired interactions between the UI thread and the RenderWindow thread:
// for example, in a multi-monitor setup the context might be recreated when
// dragging the window between monitors, triggering a Qt-specific callback
// in the context of RenderWindow thread, which will become blocked on the UI
// thread, which may in turn be blocked on something else.
bool kUseThread = false;
//
// initialize the renderer and listen to connections
// on a thread in the current process.
//
s_renderWindow = new RenderWindow(native_display, kUseThread);
if (!s_renderWindow) {
ERR("Could not create rendering window class");
GL_LOG("Could not create rendering window class");
return false;
}
if (!s_renderWindow->isValid()) {
ERR("Could not initialize emulated framebuffer\n");
delete s_renderWindow;
s_renderWindow = NULL;
return false;
}
s_renderThread = RenderServer::create(addr, addrLen);
if (!s_renderThread) {
return false;
}
strncpy(s_renderAddr, addr, sizeof(s_renderAddr));
s_renderThread->start();
GL_LOG("OpenGL renderer initialized successfully");
return true;
}
RENDER_APICALL void RENDER_APIENTRY getHardwareStrings(
const char** vendor,
const char** renderer,
const char** version) {
if (s_renderWindow &&
s_renderWindow->getHardwareStrings(vendor, renderer, version)) {
return;
}
*vendor = *renderer = *version = NULL;
}
RENDER_APICALL int RENDER_APIENTRY stopOpenGLRenderer(void)
{
bool ret = false;
// open a dummy connection to the renderer to make it
// realize the exit request.
// (send the exit request in clientFlags)
IOStream *dummy = createRenderThread(8, IOSTREAM_CLIENT_EXIT_SERVER);
if (!dummy) return false;
if (s_renderThread) {
// wait for the thread to exit
ret = s_renderThread->wait(NULL);
delete s_renderThread;
s_renderThread = NULL;
}
if (s_renderWindow != NULL) {
delete s_renderWindow;
s_renderWindow = NULL;
}
delete dummy;
return ret;
}
RENDER_APICALL bool RENDER_APIENTRY showOpenGLSubwindow(
FBNativeWindowType window_id,
int wx,
int wy,
int ww,
int wh,
int fbw,
int fbh,
float dpr,
float zRot)
{
RenderWindow* window = s_renderWindow;
if (window) {
return window->setupSubWindow(window_id,wx,wy,ww,wh,fbw,fbh,dpr,zRot);
}
// XXX: should be implemented by sending the renderer process
// a request
ERR("%s not implemented for separate renderer process !!!\n",
__FUNCTION__);
return false; return false;
}
// kUseThread is used to determine whether the RenderWindow should use
// a separate thread to manage its subwindow GL/GLES context.
// For now, this feature is disabled entirely for the following
// reasons:
//
// - It must be disabled on Windows at all times, otherwise the main window
// becomes
// unresponsive after a few seconds of user interaction (e.g. trying to
// move it over the desktop). Probably due to the subtle issues around
// input on this platform (input-queue is global, message-queue is
// per-thread). Also, this messes considerably the display of the
// main window when running the executable under Wine.
//
// - On Linux/XGL and OSX/Cocoa, this used to be necessary to avoid corruption
// issues with the GL state of the main window when using the SDL UI.
// After the switch to Qt, this is no longer necessary and may actually
// cause
// undesired interactions between the UI thread and the RenderWindow thread:
// for example, in a multi-monitor setup the context might be recreated when
// dragging the window between monitors, triggering a Qt-specific callback
// in the context of RenderWindow thread, which will become blocked on the
// UI
// thread, which may in turn be blocked on something else.
bool kUseThread = false;
//
// initialize the renderer and listen to connections
// on a thread in the current process.
//
s_renderWindow = new RenderWindow(native_display, kUseThread);
if (!s_renderWindow) {
ERR("Could not create rendering window class");
GL_LOG("Could not create rendering window class");
return false;
}
if (!s_renderWindow->isValid()) {
ERR("Could not initialize emulated framebuffer\n");
delete s_renderWindow;
s_renderWindow = NULL;
return false;
}
s_renderThread = RenderServer::create(addr, addrLen);
if (!s_renderThread) {
return false;
}
strncpy(s_renderAddr, addr, sizeof(s_renderAddr));
s_renderThread->start();
GL_LOG("OpenGL renderer initialized successfully");
return true;
} }
RENDER_APICALL bool RENDER_APIENTRY destroyOpenGLSubwindow(void) RENDER_APICALL void RENDER_APIENTRY getHardwareStrings(const char** vendor,
{ const char** renderer,
RenderWindow* window = s_renderWindow; const char** version) {
if (s_renderWindow &&
s_renderWindow->getHardwareStrings(vendor, renderer, version)) {
return;
}
*vendor = *renderer = *version = NULL;
}
if (window) { RENDER_APICALL int RENDER_APIENTRY stopOpenGLRenderer(void) {
return window->removeSubWindow(); bool ret = false;
}
// XXX: should be implemented by sending the renderer process // open a dummy connection to the renderer to make it
// a request // realize the exit request.
ERR("%s not implemented for separate renderer process !!!\n", // (send the exit request in clientFlags)
__FUNCTION__); IOStream* dummy = createRenderThread(8, IOSTREAM_CLIENT_EXIT_SERVER);
return false; if (!dummy) return false;
if (s_renderThread) {
// wait for the thread to exit
ret = s_renderThread->wait(NULL);
delete s_renderThread;
s_renderThread = NULL;
}
if (s_renderWindow != NULL) {
delete s_renderWindow;
s_renderWindow = NULL;
}
delete dummy;
return ret;
}
RENDER_APICALL bool RENDER_APIENTRY
showOpenGLSubwindow(FBNativeWindowType window_id, int wx, int wy, int ww,
int wh, int fbw, int fbh, float dpr, float zRot) {
RenderWindow* window = s_renderWindow;
if (window) {
return window->setupSubWindow(window_id, wx, wy, ww, wh, fbw, fbh, dpr,
zRot);
}
// XXX: should be implemented by sending the renderer process
// a request
ERR("%s not implemented for separate renderer process !!!\n", __FUNCTION__);
return false;
}
RENDER_APICALL bool RENDER_APIENTRY destroyOpenGLSubwindow(void) {
RenderWindow* window = s_renderWindow;
if (window) {
return window->removeSubWindow();
}
// XXX: should be implemented by sending the renderer process
// a request
ERR("%s not implemented for separate renderer process !!!\n", __FUNCTION__);
return false;
} }
#define DEFAULT_STREAM_MODE RENDER_API_STREAM_MODE_UNIX #define DEFAULT_STREAM_MODE RENDER_API_STREAM_MODE_UNIX
int gRendererStreamMode = DEFAULT_STREAM_MODE; int gRendererStreamMode = DEFAULT_STREAM_MODE;
IOStream *createRenderThread(int p_stream_buffer_size, unsigned int clientFlags) IOStream* createRenderThread(int p_stream_buffer_size,
{ unsigned int clientFlags) {
SocketStream* stream = NULL; SocketStream* stream = NULL;
if (gRendererStreamMode == RENDER_API_STREAM_MODE_TCP) { if (gRendererStreamMode == RENDER_API_STREAM_MODE_TCP) {
stream = new TcpStream(p_stream_buffer_size); stream = new TcpStream(p_stream_buffer_size);
} else { } else {
stream = new UnixStream(p_stream_buffer_size); stream = new UnixStream(p_stream_buffer_size);
} }
if (!stream) { if (!stream) {
ERR("createRenderThread failed to create stream\n"); ERR("createRenderThread failed to create stream\n");
return NULL; return NULL;
} }
if (stream->connect(s_renderAddr) < 0) { if (stream->connect(s_renderAddr) < 0) {
ERR("createRenderThread failed to connect\n"); ERR("createRenderThread failed to connect\n");
delete stream; delete stream;
return NULL; return NULL;
} }
// //
// send clientFlags to the renderer // send clientFlags to the renderer
// //
unsigned int *pClientFlags = unsigned int* pClientFlags =
(unsigned int *)stream->allocBuffer(sizeof(unsigned int)); (unsigned int*)stream->allocBuffer(sizeof(unsigned int));
*pClientFlags = clientFlags; *pClientFlags = clientFlags;
stream->commitBuffer(sizeof(unsigned int)); stream->commitBuffer(sizeof(unsigned int));
return stream; return stream;
} }

View file

@ -17,33 +17,24 @@
#include "OpenGLESDispatch/EGLDispatch.h" #include "OpenGLESDispatch/EGLDispatch.h"
RenderContext* RenderContext::create(EGLDisplay display, RenderContext* RenderContext::create(EGLDisplay display, EGLConfig config,
EGLConfig config, EGLContext sharedContext, bool isGl2) {
EGLContext sharedContext, const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, isGl2 ? 2 : 1,
bool isGl2) { EGL_NONE};
const EGLint contextAttribs[] = { EGLContext context =
EGL_CONTEXT_CLIENT_VERSION, isGl2 ? 2 : 1, s_egl.eglCreateContext(display, config, sharedContext, contextAttribs);
EGL_NONE if (context == EGL_NO_CONTEXT) {
}; return NULL;
EGLContext context = s_egl.eglCreateContext( }
display, config, sharedContext, contextAttribs);
if (context == EGL_NO_CONTEXT) {
return NULL;
}
return new RenderContext(display, context, isGl2); return new RenderContext(display, context, isGl2);
} }
RenderContext::RenderContext(EGLDisplay display, RenderContext::RenderContext(EGLDisplay display, EGLContext context, bool isGl2)
EGLContext context, : mDisplay(display), mContext(context), mIsGl2(isGl2), mContextData() {}
bool isGl2) :
mDisplay(display),
mContext(context),
mIsGl2(isGl2),
mContextData() {}
RenderContext::~RenderContext() { RenderContext::~RenderContext() {
if (mContext != EGL_NO_CONTEXT) { if (mContext != EGL_NO_CONTEXT) {
s_egl.eglDestroyContext(mDisplay, mContext); s_egl.eglDestroyContext(mDisplay, mContext);
} }
} }

View file

@ -16,8 +16,8 @@
#ifndef _LIBRENDER_RENDER_CONTEXT_H #ifndef _LIBRENDER_RENDER_CONTEXT_H
#define _LIBRENDER_RENDER_CONTEXT_H #define _LIBRENDER_RENDER_CONTEXT_H
#include "emugl/common/smart_ptr.h"
#include "GLDecoderContextData.h" #include "GLDecoderContextData.h"
#include "emugl/common/smart_ptr.h"
#include <EGL/egl.h> #include <EGL/egl.h>
@ -25,43 +25,39 @@
// EGLContext, associated with an GLDecoderContextData instance that is // EGLContext, associated with an GLDecoderContextData instance that is
// used to store copies of guest-side arrays. // used to store copies of guest-side arrays.
class RenderContext { class RenderContext {
public: public:
// Create a new RenderContext instance. // Create a new RenderContext instance.
// |display| is the host EGLDisplay handle. // |display| is the host EGLDisplay handle.
// |config| is the host EGLConfig to use. // |config| is the host EGLConfig to use.
// |sharedContext| is either EGL_NO_CONTEXT of a host EGLContext handle. // |sharedContext| is either EGL_NO_CONTEXT of a host EGLContext handle.
// |isGl2| is true iff the new context will be used with GLESv2, or // |isGl2| is true iff the new context will be used with GLESv2, or
// GLESv1 otherwise. // GLESv1 otherwise.
static RenderContext *create(EGLDisplay display, static RenderContext* create(EGLDisplay display, EGLConfig config,
EGLConfig config, EGLContext sharedContext, bool isGL2 = false);
EGLContext sharedContext,
bool isGL2 = false);
// Destructor. // Destructor.
~RenderContext(); ~RenderContext();
// Retrieve host EGLContext value. // Retrieve host EGLContext value.
EGLContext getEGLContext() const { return mContext; } EGLContext getEGLContext() const { return mContext; }
// Return true iff this is a GLESv2 context. // Return true iff this is a GLESv2 context.
bool isGL2() const { return mIsGl2; } bool isGL2() const { return mIsGl2; }
// Retrieve GLDecoderContextData instance reference for this // Retrieve GLDecoderContextData instance reference for this
// RenderContext instance. // RenderContext instance.
GLDecoderContextData& decoderContextData() { return mContextData; } GLDecoderContextData& decoderContextData() { return mContextData; }
private: private:
RenderContext(); RenderContext();
RenderContext(EGLDisplay display, RenderContext(EGLDisplay display, EGLContext context, bool isGl2);
EGLContext context,
bool isGl2);
private: private:
EGLDisplay mDisplay; EGLDisplay mDisplay;
EGLContext mContext; EGLContext mContext;
bool mIsGl2; bool mIsGl2;
GLDecoderContextData mContextData; GLDecoderContextData mContextData;
}; };
typedef emugl::SmartPtr<RenderContext> RenderContextPtr; typedef emugl::SmartPtr<RenderContext> RenderContextPtr;

View file

@ -15,12 +15,12 @@
*/ */
#include "RenderControl.h" #include "RenderControl.h"
#include "DispatchTables.h"
#include "RendererConfig.h"
#include "Renderer.h"
#include "RenderThreadInfo.h"
#include "ChecksumCalculatorThreadInfo.h" #include "ChecksumCalculatorThreadInfo.h"
#include "DispatchTables.h"
#include "DisplayManager.h" #include "DisplayManager.h"
#include "RenderThreadInfo.h"
#include "Renderer.h"
#include "RendererConfig.h"
#include "OpenGLESDispatch/EGLDispatch.h" #include "OpenGLESDispatch/EGLDispatch.h"
@ -33,471 +33,428 @@
static const GLint rendererVersion = 1; static const GLint rendererVersion = 1;
static std::shared_ptr<anbox::graphics::LayerComposer> composer; static std::shared_ptr<anbox::graphics::LayerComposer> composer;
void registerLayerComposer(const std::shared_ptr<anbox::graphics::LayerComposer> &c) void registerLayerComposer(
{ const std::shared_ptr<anbox::graphics::LayerComposer> &c) {
composer = c; composer = c;
} }
static GLint rcGetRendererVersion() static GLint rcGetRendererVersion() { return rendererVersion; }
{
return rendererVersion; static EGLint rcGetEGLVersion(EGLint *major, EGLint *minor) {
Renderer *fb = Renderer::get();
if (!fb) {
return EGL_FALSE;
}
*major = (EGLint)fb->getCaps().eglMajor;
*minor = (EGLint)fb->getCaps().eglMinor;
return EGL_TRUE;
} }
static EGLint rcGetEGLVersion(EGLint* major, EGLint* minor) static EGLint rcQueryEGLString(EGLenum name, void *buffer, EGLint bufferSize) {
{ Renderer *fb = Renderer::get();
Renderer *fb = Renderer::get(); if (!fb) {
if (!fb) { return 0;
return EGL_FALSE; }
}
*major = (EGLint)fb->getCaps().eglMajor;
*minor = (EGLint)fb->getCaps().eglMinor;
return EGL_TRUE; const char *str = s_egl.eglQueryString(fb->getDisplay(), name);
if (!str) {
return 0;
}
int len = strlen(str) + 1;
if (!buffer || len > bufferSize) {
return -len;
}
strcpy((char *)buffer, str);
return len;
} }
static EGLint rcQueryEGLString(EGLenum name, void* buffer, EGLint bufferSize) static EGLint rcGetGLString(EGLenum name, void *buffer, EGLint bufferSize) {
{ RenderThreadInfo *tInfo = RenderThreadInfo::get();
Renderer *fb = Renderer::get(); std::string result;
if (!fb) {
return 0; if (tInfo && tInfo->currContext) {
const char *str = nullptr;
if (tInfo->currContext->isGL2())
str = reinterpret_cast<const char *>(s_gles2.glGetString(name));
else
str = reinterpret_cast<const char *>(s_gles1.glGetString(name));
if (str) result += str;
}
// We're forcing version 2.0 no matter what the host provides as
// our emulation layer isn't prepared for anything newer (yet).
// This goes in parallel with filtering the extension set for
// any unwanted extensions. If we don't force the right version
// here certain parts of the system will assume API conditions
// which aren't met.
if (name == GL_VERSION)
result = "OpenGL ES 2.0";
else if (name == GL_EXTENSIONS) {
std::string approved_extensions = result;
std::vector<std::string> unsupported_extensions = {
// Leaving this enabled gives crippeled text rendering when
// using the host mesa GLES drivers.
"GL_EXT_unpack_subimage",
};
for (const auto &extension : unsupported_extensions) {
size_t start_pos = approved_extensions.find(extension);
if (start_pos == std::string::npos) continue;
approved_extensions.replace(start_pos, extension.length(), "");
} }
const char *str = s_egl.eglQueryString(fb->getDisplay(), name); result = approved_extensions;
if (!str) { }
return 0;
}
int len = strlen(str) + 1; int nextBufferSize = result.size() + 1;
if (!buffer || len > bufferSize) {
return -len;
}
strcpy((char *)buffer, str); if (!buffer || nextBufferSize > bufferSize) return -nextBufferSize;
return len;
snprintf(static_cast<char *>(buffer), nextBufferSize, "%s", result.c_str());
return nextBufferSize;
} }
static EGLint rcGetGLString(EGLenum name, void* buffer, EGLint bufferSize) static EGLint rcGetNumConfigs(uint32_t *p_numAttribs) {
{ int numConfigs = 0, numAttribs = 0;
RenderThreadInfo *tInfo = RenderThreadInfo::get();
std::string result;
if (tInfo && tInfo->currContext) { Renderer::get()->getConfigs()->getPackInfo(&numConfigs, &numAttribs);
const char *str = nullptr; if (p_numAttribs) {
if (tInfo->currContext->isGL2()) *p_numAttribs = static_cast<uint32_t>(numAttribs);
str = reinterpret_cast<const char*>(s_gles2.glGetString(name)); }
else return numConfigs;
str = reinterpret_cast<const char*>(s_gles1.glGetString(name));
if (str)
result += str;
}
// We're forcing version 2.0 no matter what the host provides as
// our emulation layer isn't prepared for anything newer (yet).
// This goes in parallel with filtering the extension set for
// any unwanted extensions. If we don't force the right version
// here certain parts of the system will assume API conditions
// which aren't met.
if (name == GL_VERSION)
result = "OpenGL ES 2.0";
else if (name == GL_EXTENSIONS) {
std::string approved_extensions = result;
std::vector<std::string> unsupported_extensions = {
// Leaving this enabled gives crippeled text rendering when
// using the host mesa GLES drivers.
"GL_EXT_unpack_subimage",
};
for (const auto &extension : unsupported_extensions) {
size_t start_pos = approved_extensions.find(extension);
if(start_pos == std::string::npos)
continue;
approved_extensions.replace(start_pos, extension.length(), "");
}
result = approved_extensions;
}
int nextBufferSize = result.size() + 1;
if (!buffer || nextBufferSize > bufferSize)
return -nextBufferSize;
snprintf(static_cast<char*>(buffer), nextBufferSize, "%s", result.c_str());
return nextBufferSize;
} }
static EGLint rcGetNumConfigs(uint32_t* p_numAttribs) static EGLint rcGetConfigs(uint32_t bufSize, GLuint *buffer) {
{ GLuint bufferSize = (GLuint)bufSize;
int numConfigs = 0, numAttribs = 0; return Renderer::get()->getConfigs()->packConfigs(bufferSize, buffer);
Renderer::get()->getConfigs()->getPackInfo(&numConfigs, &numAttribs);
if (p_numAttribs) {
*p_numAttribs = static_cast<uint32_t>(numAttribs);
}
return numConfigs;
} }
static EGLint rcGetConfigs(uint32_t bufSize, GLuint* buffer) static EGLint rcChooseConfig(EGLint *attribs, uint32_t attribs_size,
{ uint32_t *configs, uint32_t configs_size) {
GLuint bufferSize = (GLuint)bufSize; Renderer *fb = Renderer::get();
return Renderer::get()->getConfigs()->packConfigs(bufferSize, buffer); if (!fb || attribs_size == 0) {
return 0;
}
return fb->getConfigs()->chooseConfig(attribs, (EGLint *)configs,
(EGLint)configs_size);
} }
static EGLint rcChooseConfig(EGLint *attribs, static EGLint rcGetFBParam(EGLint param) {
uint32_t attribs_size, Renderer *fb = Renderer::get();
uint32_t *configs, if (!fb) {
uint32_t configs_size) return 0;
{ }
Renderer *fb = Renderer::get();
if (!fb || attribs_size==0) {
return 0;
}
return fb->getConfigs()->chooseConfig( EGLint ret = 0;
attribs, (EGLint*)configs, (EGLint)configs_size);
switch (param) {
case FB_WIDTH:
ret = DisplayManager::get()->display_info().horizontal_resolution;
break;
case FB_HEIGHT:
ret = DisplayManager::get()->display_info().vertical_resolution;
break;
case FB_XDPI:
ret = 72; // XXX: should be implemented
break;
case FB_YDPI:
ret = 72; // XXX: should be implemented
break;
case FB_FPS:
ret = 60;
break;
case FB_MIN_SWAP_INTERVAL:
ret = 1; // XXX: should be implemented
break;
case FB_MAX_SWAP_INTERVAL:
ret = 1; // XXX: should be implemented
break;
default:
break;
}
return ret;
} }
static EGLint rcGetFBParam(EGLint param) static uint32_t rcCreateContext(uint32_t config, uint32_t share,
{ uint32_t glVersion) {
Renderer *fb = Renderer::get(); Renderer *fb = Renderer::get();
if (!fb) { if (!fb) {
return 0; return 0;
} }
EGLint ret = 0; // To make it consistent with the guest, create GLES2 context when GL
// version==2 or 3
switch(param) { HandleType ret =
case FB_WIDTH: fb->createRenderContext(config, share, glVersion == 2 || glVersion == 3);
ret = DisplayManager::get()->display_info().horizontal_resolution; return ret;
break;
case FB_HEIGHT:
ret = DisplayManager::get()->display_info().vertical_resolution;
break;
case FB_XDPI:
ret = 72; // XXX: should be implemented
break;
case FB_YDPI:
ret = 72; // XXX: should be implemented
break;
case FB_FPS:
ret = 60;
break;
case FB_MIN_SWAP_INTERVAL:
ret = 1; // XXX: should be implemented
break;
case FB_MAX_SWAP_INTERVAL:
ret = 1; // XXX: should be implemented
break;
default:
break;
}
return ret;
} }
static uint32_t rcCreateContext(uint32_t config, static void rcDestroyContext(uint32_t context) {
uint32_t share, uint32_t glVersion) Renderer *fb = Renderer::get();
{ if (!fb) {
Renderer *fb = Renderer::get(); return;
if (!fb) { }
return 0;
}
// To make it consistent with the guest, create GLES2 context when GL fb->DestroyRenderContext(context);
// version==2 or 3
HandleType ret = fb->createRenderContext(config, share, glVersion == 2 || glVersion == 3);
return ret;
} }
static void rcDestroyContext(uint32_t context) static uint32_t rcCreateWindowSurface(uint32_t config, uint32_t width,
{ uint32_t height) {
Renderer *fb = Renderer::get(); Renderer *fb = Renderer::get();
if (!fb) { if (!fb) {
return; return 0;
} }
fb->DestroyRenderContext(context); return fb->createWindowSurface(config, width, height);
} }
static uint32_t rcCreateWindowSurface(uint32_t config, static void rcDestroyWindowSurface(uint32_t windowSurface) {
uint32_t width, uint32_t height) Renderer *fb = Renderer::get();
{ if (!fb) {
Renderer *fb = Renderer::get(); return;
if (!fb) { }
return 0;
}
return fb->createWindowSurface(config, width, height); fb->DestroyWindowSurface(windowSurface);
} }
static void rcDestroyWindowSurface(uint32_t windowSurface) static uint32_t rcCreateColorBuffer(uint32_t width, uint32_t height,
{ GLenum internalFormat) {
Renderer *fb = Renderer::get(); Renderer *fb = Renderer::get();
if (!fb) { if (!fb) {
return; return 0;
} }
fb->DestroyWindowSurface( windowSurface ); return fb->createColorBuffer(width, height, internalFormat);
} }
static uint32_t rcCreateColorBuffer(uint32_t width, static int rcOpenColorBuffer2(uint32_t colorbuffer) {
uint32_t height, GLenum internalFormat) Renderer *fb = Renderer::get();
{ if (!fb) {
Renderer *fb = Renderer::get(); return -1;
if (!fb) { }
return 0; return fb->openColorBuffer(colorbuffer);
}
return fb->createColorBuffer(width, height, internalFormat);
}
static int rcOpenColorBuffer2(uint32_t colorbuffer)
{
Renderer *fb = Renderer::get();
if (!fb) {
return -1;
}
return fb->openColorBuffer( colorbuffer );
} }
// Deprecated, kept for compatibility with old system images only. // Deprecated, kept for compatibility with old system images only.
// Use rcOpenColorBuffer2 instead. // Use rcOpenColorBuffer2 instead.
static void rcOpenColorBuffer(uint32_t colorbuffer) static void rcOpenColorBuffer(uint32_t colorbuffer) {
{ (void)rcOpenColorBuffer2(colorbuffer);
(void) rcOpenColorBuffer2(colorbuffer);
} }
static void rcCloseColorBuffer(uint32_t colorbuffer) static void rcCloseColorBuffer(uint32_t colorbuffer) {
{ Renderer *fb = Renderer::get();
Renderer *fb = Renderer::get(); if (!fb) {
if (!fb) { return;
return; }
} fb->closeColorBuffer(colorbuffer);
fb->closeColorBuffer( colorbuffer );
} }
static int rcFlushWindowColorBuffer(uint32_t windowSurface) static int rcFlushWindowColorBuffer(uint32_t windowSurface) {
{ Renderer *fb = Renderer::get();
Renderer *fb = Renderer::get(); if (!fb) {
if (!fb) { return -1;
return -1; }
} if (!fb->flushWindowSurfaceColorBuffer(windowSurface)) {
if (!fb->flushWindowSurfaceColorBuffer(windowSurface)) { return -1;
return -1; }
} return 0;
return 0;
} }
static void rcSetWindowColorBuffer(uint32_t windowSurface, static void rcSetWindowColorBuffer(uint32_t windowSurface,
uint32_t colorBuffer) uint32_t colorBuffer) {
{ Renderer *fb = Renderer::get();
Renderer *fb = Renderer::get(); if (!fb) {
if (!fb) { return;
return; }
} fb->setWindowSurfaceColorBuffer(windowSurface, colorBuffer);
fb->setWindowSurfaceColorBuffer(windowSurface, colorBuffer);
} }
static EGLint rcMakeCurrent(uint32_t context, static EGLint rcMakeCurrent(uint32_t context, uint32_t drawSurf,
uint32_t drawSurf, uint32_t readSurf) uint32_t readSurf) {
{ Renderer *fb = Renderer::get();
Renderer *fb = Renderer::get(); if (!fb) {
if (!fb) { return EGL_FALSE;
return EGL_FALSE; }
}
bool ret = fb->bindContext(context, drawSurf, readSurf); bool ret = fb->bindContext(context, drawSurf, readSurf);
return (ret ? EGL_TRUE : EGL_FALSE); return (ret ? EGL_TRUE : EGL_FALSE);
} }
static void rcFBPost(uint32_t colorBuffer) static void rcFBPost(uint32_t colorBuffer) { WARNING("Not implemented"); }
{
WARNING("Not implemented"); static void rcFBSetSwapInterval(EGLint interval) {
// XXX: TBD - should be implemented
} }
static void rcFBSetSwapInterval(EGLint interval) static void rcBindTexture(uint32_t colorBuffer) {
{ Renderer *fb = Renderer::get();
// XXX: TBD - should be implemented if (!fb) {
return;
}
fb->bindColorBufferToTexture(colorBuffer);
} }
static void rcBindTexture(uint32_t colorBuffer) static void rcBindRenderbuffer(uint32_t colorBuffer) {
{ Renderer *fb = Renderer::get();
Renderer *fb = Renderer::get(); if (!fb) {
if (!fb) { return;
return; }
}
fb->bindColorBufferToTexture(colorBuffer); fb->bindColorBufferToRenderbuffer(colorBuffer);
} }
static void rcBindRenderbuffer(uint32_t colorBuffer) static EGLint rcColorBufferCacheFlush(uint32_t colorBuffer, EGLint postCount,
{ int forRead) {
Renderer *fb = Renderer::get(); // XXX: TBD - should be implemented
if (!fb) { return 0;
return;
}
fb->bindColorBufferToRenderbuffer(colorBuffer);
} }
static EGLint rcColorBufferCacheFlush(uint32_t colorBuffer, static void rcReadColorBuffer(uint32_t colorBuffer, GLint x, GLint y,
EGLint postCount, int forRead) GLint width, GLint height, GLenum format,
{ GLenum type, void *pixels) {
// XXX: TBD - should be implemented Renderer *fb = Renderer::get();
return 0; if (!fb) {
return;
}
fb->readColorBuffer(colorBuffer, x, y, width, height, format, type, pixels);
} }
static void rcReadColorBuffer(uint32_t colorBuffer, static int rcUpdateColorBuffer(uint32_t colorBuffer, GLint x, GLint y,
GLint x, GLint y, GLint width, GLint height, GLenum format,
GLint width, GLint height, GLenum type, void *pixels) {
GLenum format, GLenum type, void* pixels) Renderer *fb = Renderer::get();
{ if (!fb) {
Renderer *fb = Renderer::get(); return -1;
if (!fb) { }
return;
}
fb->readColorBuffer(colorBuffer, x, y, width, height, format, type, pixels); fb->updateColorBuffer(colorBuffer, x, y, width, height, format, type, pixels);
return 0;
} }
static int rcUpdateColorBuffer(uint32_t colorBuffer, static uint32_t rcCreateClientImage(uint32_t context, EGLenum target,
GLint x, GLint y, GLuint buffer) {
GLint width, GLint height, Renderer *fb = Renderer::get();
GLenum format, GLenum type, void* pixels) if (!fb) {
{
Renderer *fb = Renderer::get();
if (!fb) {
return -1;
}
fb->updateColorBuffer(colorBuffer, x, y, width, height, format, type, pixels);
return 0; return 0;
}
return fb->createClientImage(context, target, buffer);
} }
static uint32_t rcCreateClientImage(uint32_t context, EGLenum target, GLuint buffer) static int rcDestroyClientImage(uint32_t image) {
{ Renderer *fb = Renderer::get();
Renderer *fb = Renderer::get(); if (!fb) {
if (!fb) { return 0;
return 0; }
}
return fb->createClientImage(context, target, buffer); return fb->destroyClientImage(image);
}
static int rcDestroyClientImage(uint32_t image)
{
Renderer *fb = Renderer::get();
if (!fb) {
return 0;
}
return fb->destroyClientImage(image);
} }
static void rcSelectChecksumCalculator(uint32_t protocol, uint32_t reserved) { static void rcSelectChecksumCalculator(uint32_t protocol, uint32_t reserved) {
ChecksumCalculatorThreadInfo::setVersion(protocol); ChecksumCalculatorThreadInfo::setVersion(protocol);
} }
int rcGetNumDisplays() { int rcGetNumDisplays() { return 1; }
return 1;
}
int rcGetDisplayWidth(uint32_t display_id) { int rcGetDisplayWidth(uint32_t display_id) {
printf("%s: display_id=%d\n", __func__, display_id); printf("%s: display_id=%d\n", __func__, display_id);
return DisplayManager::get()->display_info().horizontal_resolution; return DisplayManager::get()->display_info().horizontal_resolution;
} }
int rcGetDisplayHeight(uint32_t display_id) { int rcGetDisplayHeight(uint32_t display_id) {
printf("%s: display_id=%d\n", __func__, display_id); printf("%s: display_id=%d\n", __func__, display_id);
return DisplayManager::get()->display_info().vertical_resolution; return DisplayManager::get()->display_info().vertical_resolution;
} }
int rcGetDisplayDpiX(uint32_t display_id) { int rcGetDisplayDpiX(uint32_t display_id) {
printf("%s: display_id=%d\n", __func__, display_id); printf("%s: display_id=%d\n", __func__, display_id);
return 120; return 120;
} }
int rcGetDisplayDpiY(uint32_t display_id) { int rcGetDisplayDpiY(uint32_t display_id) {
printf("%s: display_id=%d\n", __func__, display_id); printf("%s: display_id=%d\n", __func__, display_id);
return 120; return 120;
} }
int rcGetDisplayVsyncPeriod(uint32_t display_id) { int rcGetDisplayVsyncPeriod(uint32_t display_id) {
printf("%s: display_id=%d\n", __func__, display_id); printf("%s: display_id=%d\n", __func__, display_id);
return 1; return 1;
} }
static std::vector<Renderable> frame_layers; static std::vector<Renderable> frame_layers;
bool is_layer_blacklisted(const std::string &name) { bool is_layer_blacklisted(const std::string &name) {
static std::vector<std::string> blacklist = { static std::vector<std::string> blacklist = {
"Sprite", "Sprite",
}; };
return std::find(blacklist.begin(), blacklist.end(), name) != blacklist.end(); return std::find(blacklist.begin(), blacklist.end(), name) != blacklist.end();
} }
void rcPostLayer(const char *name, uint32_t color_buffer, void rcPostLayer(const char *name, uint32_t color_buffer,
int32_t sourceCropLeft, int32_t sourceCropTop, int32_t sourceCropLeft, int32_t sourceCropTop,
int32_t sourceCropRight, int32_t sourceCropBottom, int32_t sourceCropRight, int32_t sourceCropBottom,
int32_t displayFrameLeft, int32_t displayFrameTop, int32_t displayFrameLeft, int32_t displayFrameTop,
int32_t displayFrameRight, int32_t displayFrameBottom) int32_t displayFrameRight, int32_t displayFrameBottom) {
{ Renderable r{
Renderable r{ name,
name, color_buffer,
color_buffer, {displayFrameLeft, displayFrameTop, displayFrameRight,
{displayFrameLeft, displayFrameTop, displayFrameRight, displayFrameBottom}, displayFrameBottom},
{sourceCropLeft, sourceCropTop, sourceCropRight, sourceCropBottom} {sourceCropLeft, sourceCropTop, sourceCropRight, sourceCropBottom}};
}; frame_layers.push_back(r);
frame_layers.push_back(r);
} }
void rcPostAllLayersDone() void rcPostAllLayersDone() {
{ if (composer) composer->submit_layers(frame_layers);
if (composer)
composer->submit_layers(frame_layers);
frame_layers.clear(); frame_layers.clear();
} }
void initRenderControlContext(renderControl_decoder_context_t *dec) void initRenderControlContext(renderControl_decoder_context_t *dec) {
{ dec->rcGetRendererVersion = rcGetRendererVersion;
dec->rcGetRendererVersion = rcGetRendererVersion; dec->rcGetEGLVersion = rcGetEGLVersion;
dec->rcGetEGLVersion = rcGetEGLVersion; dec->rcQueryEGLString = rcQueryEGLString;
dec->rcQueryEGLString = rcQueryEGLString; dec->rcGetGLString = rcGetGLString;
dec->rcGetGLString = rcGetGLString; dec->rcGetNumConfigs = rcGetNumConfigs;
dec->rcGetNumConfigs = rcGetNumConfigs; dec->rcGetConfigs = rcGetConfigs;
dec->rcGetConfigs = rcGetConfigs; dec->rcChooseConfig = rcChooseConfig;
dec->rcChooseConfig = rcChooseConfig; dec->rcGetFBParam = rcGetFBParam;
dec->rcGetFBParam = rcGetFBParam; dec->rcCreateContext = rcCreateContext;
dec->rcCreateContext = rcCreateContext; dec->rcDestroyContext = rcDestroyContext;
dec->rcDestroyContext = rcDestroyContext; dec->rcCreateWindowSurface = rcCreateWindowSurface;
dec->rcCreateWindowSurface = rcCreateWindowSurface; dec->rcDestroyWindowSurface = rcDestroyWindowSurface;
dec->rcDestroyWindowSurface = rcDestroyWindowSurface; dec->rcCreateColorBuffer = rcCreateColorBuffer;
dec->rcCreateColorBuffer = rcCreateColorBuffer; dec->rcOpenColorBuffer = rcOpenColorBuffer;
dec->rcOpenColorBuffer = rcOpenColorBuffer; dec->rcCloseColorBuffer = rcCloseColorBuffer;
dec->rcCloseColorBuffer = rcCloseColorBuffer; dec->rcSetWindowColorBuffer = rcSetWindowColorBuffer;
dec->rcSetWindowColorBuffer = rcSetWindowColorBuffer; dec->rcFlushWindowColorBuffer = rcFlushWindowColorBuffer;
dec->rcFlushWindowColorBuffer = rcFlushWindowColorBuffer; dec->rcMakeCurrent = rcMakeCurrent;
dec->rcMakeCurrent = rcMakeCurrent; dec->rcFBPost = rcFBPost;
dec->rcFBPost = rcFBPost; dec->rcFBSetSwapInterval = rcFBSetSwapInterval;
dec->rcFBSetSwapInterval = rcFBSetSwapInterval; dec->rcBindTexture = rcBindTexture;
dec->rcBindTexture = rcBindTexture; dec->rcBindRenderbuffer = rcBindRenderbuffer;
dec->rcBindRenderbuffer = rcBindRenderbuffer; dec->rcColorBufferCacheFlush = rcColorBufferCacheFlush;
dec->rcColorBufferCacheFlush = rcColorBufferCacheFlush; dec->rcReadColorBuffer = rcReadColorBuffer;
dec->rcReadColorBuffer = rcReadColorBuffer; dec->rcUpdateColorBuffer = rcUpdateColorBuffer;
dec->rcUpdateColorBuffer = rcUpdateColorBuffer; dec->rcOpenColorBuffer2 = rcOpenColorBuffer2;
dec->rcOpenColorBuffer2 = rcOpenColorBuffer2; dec->rcCreateClientImage = rcCreateClientImage;
dec->rcCreateClientImage = rcCreateClientImage; dec->rcDestroyClientImage = rcDestroyClientImage;
dec->rcDestroyClientImage = rcDestroyClientImage; dec->rcSelectChecksumCalculator = rcSelectChecksumCalculator;
dec->rcSelectChecksumCalculator = rcSelectChecksumCalculator; dec->rcGetNumDisplays = rcGetNumDisplays;
dec->rcGetNumDisplays = rcGetNumDisplays; dec->rcGetDisplayWidth = rcGetDisplayWidth;
dec->rcGetDisplayWidth = rcGetDisplayWidth; dec->rcGetDisplayHeight = rcGetDisplayHeight;
dec->rcGetDisplayHeight = rcGetDisplayHeight; dec->rcGetDisplayDpiX = rcGetDisplayDpiX;
dec->rcGetDisplayDpiX = rcGetDisplayDpiX; dec->rcGetDisplayDpiY = rcGetDisplayDpiY;
dec->rcGetDisplayDpiY = rcGetDisplayDpiY; dec->rcGetDisplayVsyncPeriod = rcGetDisplayVsyncPeriod;
dec->rcGetDisplayVsyncPeriod = rcGetDisplayVsyncPeriod; dec->rcPostLayer = rcPostLayer;
dec->rcPostLayer = rcPostLayer; dec->rcPostAllLayersDone = rcPostAllLayersDone;
dec->rcPostAllLayersDone = rcPostAllLayersDone;
} }

View file

@ -23,10 +23,11 @@
namespace anbox { namespace anbox {
namespace graphics { namespace graphics {
class LayerComposer; class LayerComposer;
} // namespace graphics } // namespace graphics
} // namespace anbox } // namespace anbox
void initRenderControlContext(renderControl_decoder_context_t *dec); void initRenderControlContext(renderControl_decoder_context_t *dec);
void registerLayerComposer(const std::shared_ptr<anbox::graphics::LayerComposer> &c); void registerLayerComposer(
const std::shared_ptr<anbox::graphics::LayerComposer> &c);
#endif #endif

View file

@ -15,11 +15,11 @@
*/ */
#include "RenderServer.h" #include "RenderServer.h"
#include <pthread.h>
#include <signal.h>
#include "RenderThread.h" #include "RenderThread.h"
#include "TcpStream.h" #include "TcpStream.h"
#include "UnixStream.h" #include "UnixStream.h"
#include <signal.h>
#include <pthread.h>
#include "OpenglRender/render_api.h" #include "OpenglRender/render_api.h"
@ -29,123 +29,109 @@
typedef std::set<RenderThread *> RenderThreadsSet; typedef std::set<RenderThread *> RenderThreadsSet;
RenderServer::RenderServer() : RenderServer::RenderServer() : m_lock(), m_listenSock(NULL), m_exiting(false) {}
m_lock(),
m_listenSock(NULL),
m_exiting(false)
{
}
RenderServer::~RenderServer()
{
delete m_listenSock;
}
RenderServer::~RenderServer() { delete m_listenSock; }
extern "C" int gRendererStreamMode; extern "C" int gRendererStreamMode;
RenderServer *RenderServer::create(char* addr, size_t addrLen) RenderServer *RenderServer::create(char *addr, size_t addrLen) {
{ RenderServer *server = new RenderServer();
RenderServer *server = new RenderServer(); if (!server) {
if (!server) { return NULL;
return NULL; }
}
if (gRendererStreamMode == RENDER_API_STREAM_MODE_TCP) { if (gRendererStreamMode == RENDER_API_STREAM_MODE_TCP) {
server->m_listenSock = new TcpStream(); server->m_listenSock = new TcpStream();
} else { } else {
server->m_listenSock = new UnixStream(); server->m_listenSock = new UnixStream();
} }
char addrstr[SocketStream::MAX_ADDRSTR_LEN]; char addrstr[SocketStream::MAX_ADDRSTR_LEN];
if (server->m_listenSock->listen(addrstr) < 0) { if (server->m_listenSock->listen(addrstr) < 0) {
ERR("RenderServer::create failed to listen\n"); ERR("RenderServer::create failed to listen\n");
delete server; delete server;
return NULL; return NULL;
} }
size_t len = strlen(addrstr) + 1; size_t len = strlen(addrstr) + 1;
if (len > addrLen) { if (len > addrLen) {
ERR("RenderServer address name too big for provided buffer: %zu > %zu\n", ERR("RenderServer address name too big for provided buffer: %zu > %zu\n",
len, addrLen); len, addrLen);
delete server; delete server;
return NULL; return NULL;
} }
memcpy(addr, addrstr, len); memcpy(addr, addrstr, len);
return server; return server;
} }
intptr_t RenderServer::main() intptr_t RenderServer::main() {
{ RenderThreadsSet threads;
RenderThreadsSet threads;
while(1) { while (1) {
SocketStream *stream = m_listenSock->accept(); SocketStream *stream = m_listenSock->accept();
if (!stream) { if (!stream) {
fprintf(stderr,"Error accepting gles connection, ignoring.\n"); fprintf(stderr, "Error accepting gles connection, ignoring.\n");
continue; continue;
} }
unsigned int clientFlags; unsigned int clientFlags;
if (!stream->readFully(&clientFlags, sizeof(unsigned int))) { if (!stream->readFully(&clientFlags, sizeof(unsigned int))) {
fprintf(stderr,"Error reading clientFlags\n"); fprintf(stderr, "Error reading clientFlags\n");
delete stream; delete stream;
continue; continue;
} }
// check if we have been requested to exit while waiting on accept // check if we have been requested to exit while waiting on accept
if ((clientFlags & IOSTREAM_CLIENT_EXIT_SERVER) != 0) { if ((clientFlags & IOSTREAM_CLIENT_EXIT_SERVER) != 0) {
m_exiting = true; m_exiting = true;
delete stream; delete stream;
break; break;
} }
RenderThread *rt = RenderThread::create(stream, &m_lock); RenderThread *rt = RenderThread::create(stream, &m_lock);
if (!rt) { if (!rt) {
fprintf(stderr,"Failed to create RenderThread\n"); fprintf(stderr, "Failed to create RenderThread\n");
delete stream; delete stream;
} else if (!rt->start()) { } else if (!rt->start()) {
fprintf(stderr,"Failed to start RenderThread\n"); fprintf(stderr, "Failed to start RenderThread\n");
delete rt; delete rt;
delete stream; delete stream;
}
//
// remove from the threads list threads which are
// no longer running
//
for (RenderThreadsSet::iterator n,t = threads.begin();
t != threads.end();
t = n) {
// first find next iterator
n = t;
n++;
// delete and erase the current iterator
// if thread is no longer running
if ((*t)->isFinished()) {
delete (*t);
threads.erase(t);
}
}
// if the thread has been created and started, insert it to the list
if (rt)
threads.insert(rt);
} }
// //
// Wait for all threads to finish // remove from the threads list threads which are
// no longer running
// //
for (RenderThreadsSet::iterator t = threads.begin(); for (RenderThreadsSet::iterator n, t = threads.begin(); t != threads.end();
t != threads.end(); t = n) {
t++) { // first find next iterator
(*t)->forceStop(); n = t;
(*t)->wait(NULL); n++;
// delete and erase the current iterator
// if thread is no longer running
if ((*t)->isFinished()) {
delete (*t); delete (*t);
threads.erase(t);
}
} }
threads.clear();
return 0; // if the thread has been created and started, insert it to the list
if (rt) threads.insert(rt);
}
//
// Wait for all threads to finish
//
for (RenderThreadsSet::iterator t = threads.begin(); t != threads.end();
t++) {
(*t)->forceStop();
(*t)->wait(NULL);
delete (*t);
}
threads.clear();
return 0;
} }

View file

@ -20,23 +20,22 @@
#include "emugl/common/mutex.h" #include "emugl/common/mutex.h"
#include "emugl/common/thread.h" #include "emugl/common/thread.h"
class RenderServer : public emugl::Thread class RenderServer : public emugl::Thread {
{ public:
public: static RenderServer *create(char *addr, size_t addrLen);
static RenderServer *create(char* addr, size_t addrLen); virtual ~RenderServer();
virtual ~RenderServer();
virtual intptr_t main(); virtual intptr_t main();
bool isExiting() const { return m_exiting; } bool isExiting() const { return m_exiting; }
private: private:
RenderServer(); RenderServer();
private: private:
emugl::Mutex m_lock; emugl::Mutex m_lock;
SocketStream *m_listenSock; SocketStream *m_listenSock;
bool m_exiting; bool m_exiting;
}; };
#endif #endif

View file

@ -15,107 +15,102 @@
*/ */
#include "RenderThread.h" #include "RenderThread.h"
#include "Renderer.h"
#include "ReadBuffer.h" #include "ReadBuffer.h"
#include "RenderControl.h" #include "RenderControl.h"
#include "RenderThreadInfo.h" #include "RenderThreadInfo.h"
#include "Renderer.h"
#include "TimeUtils.h" #include "TimeUtils.h"
#include "OpenGLESDispatch/EGLDispatch.h"
#include "OpenGLESDispatch/GLESv2Dispatch.h"
#include "OpenGLESDispatch/GLESv1Dispatch.h"
#include "../../../shared/OpenglCodecCommon/ChecksumCalculatorThreadInfo.h" #include "../../../shared/OpenglCodecCommon/ChecksumCalculatorThreadInfo.h"
#include "OpenGLESDispatch/EGLDispatch.h"
#include "OpenGLESDispatch/GLESv1Dispatch.h"
#include "OpenGLESDispatch/GLESv2Dispatch.h"
#define STREAM_BUFFER_SIZE 4*1024*1024 #define STREAM_BUFFER_SIZE 4 * 1024 * 1024
RenderThread::RenderThread(IOStream *stream, emugl::Mutex *lock) : RenderThread::RenderThread(IOStream *stream, emugl::Mutex *lock)
emugl::Thread(), : emugl::Thread(), m_lock(lock), m_stream(stream) {}
m_lock(lock),
m_stream(stream) {}
RenderThread::~RenderThread() { RenderThread::~RenderThread() { delete m_stream; }
delete m_stream;
}
// static // static
RenderThread* RenderThread::create(IOStream *stream, emugl::Mutex *lock) { RenderThread *RenderThread::create(IOStream *stream, emugl::Mutex *lock) {
return new RenderThread(stream, lock); return new RenderThread(stream, lock);
} }
void RenderThread::forceStop() { void RenderThread::forceStop() { m_stream->forceStop(); }
m_stream->forceStop();
}
intptr_t RenderThread::main() { intptr_t RenderThread::main() {
RenderThreadInfo tInfo; RenderThreadInfo tInfo;
ChecksumCalculatorThreadInfo tChecksumInfo; ChecksumCalculatorThreadInfo tChecksumInfo;
// //
// initialize decoders // initialize decoders
// //
tInfo.m_glDec.initGL(gles1_dispatch_get_proc_func, NULL); tInfo.m_glDec.initGL(gles1_dispatch_get_proc_func, NULL);
tInfo.m_gl2Dec.initGL(gles2_dispatch_get_proc_func, NULL); tInfo.m_gl2Dec.initGL(gles2_dispatch_get_proc_func, NULL);
initRenderControlContext(&tInfo.m_rcDec); initRenderControlContext(&tInfo.m_rcDec);
ReadBuffer readBuf(STREAM_BUFFER_SIZE); ReadBuffer readBuf(STREAM_BUFFER_SIZE);
while (1) {
int stat = readBuf.getData(m_stream);
if (stat <= 0) {
break;
}
bool progress;
do {
progress = false;
m_lock->lock();
//
// try to process some of the command buffer using the GLESv1 decoder
//
size_t last = tInfo.m_glDec.decode(readBuf.buf(), readBuf.validData(), m_stream);
if (last > 0) {
progress = true;
readBuf.consume(last);
}
//
// try to process some of the command buffer using the GLESv2 decoder
//
last = tInfo.m_gl2Dec.decode(readBuf.buf(), readBuf.validData(), m_stream);
if (last > 0) {
progress = true;
readBuf.consume(last);
}
//
// try to process some of the command buffer using the
// renderControl decoder
//
last = tInfo.m_rcDec.decode(readBuf.buf(), readBuf.validData(), m_stream);
if (last > 0) {
readBuf.consume(last);
progress = true;
}
m_lock->unlock();
} while( progress );
while (1) {
int stat = readBuf.getData(m_stream);
if (stat <= 0) {
break;
} }
// bool progress;
// Release references to the current thread's context/surfaces if any do {
// progress = false;
Renderer::get()->bindContext(0, 0, 0);
if (tInfo.currContext || tInfo.currDrawSurf || tInfo.currReadSurf) {
fprintf(stderr, "ERROR: RenderThread exiting with current context/surfaces\n");
}
Renderer::get()->drainWindowSurface(); m_lock->lock();
//
// try to process some of the command buffer using the GLESv1 decoder
//
size_t last =
tInfo.m_glDec.decode(readBuf.buf(), readBuf.validData(), m_stream);
if (last > 0) {
progress = true;
readBuf.consume(last);
}
Renderer::get()->drainRenderContext(); //
// try to process some of the command buffer using the GLESv2 decoder
//
last =
tInfo.m_gl2Dec.decode(readBuf.buf(), readBuf.validData(), m_stream);
if (last > 0) {
progress = true;
readBuf.consume(last);
}
return 0; //
// try to process some of the command buffer using the
// renderControl decoder
//
last = tInfo.m_rcDec.decode(readBuf.buf(), readBuf.validData(), m_stream);
if (last > 0) {
readBuf.consume(last);
progress = true;
}
m_lock->unlock();
} while (progress);
}
//
// Release references to the current thread's context/surfaces if any
//
Renderer::get()->bindContext(0, 0, 0);
if (tInfo.currContext || tInfo.currDrawSurf || tInfo.currReadSurf) {
fprintf(stderr,
"ERROR: RenderThread exiting with current context/surfaces\n");
}
Renderer::get()->drainWindowSurface();
Renderer::get()->drainRenderContext();
return 0;
} }

View file

@ -24,35 +24,35 @@
// A class used to model a thread of the RenderServer. Each one of them // A class used to model a thread of the RenderServer. Each one of them
// handles a single guest client / protocol byte stream. // handles a single guest client / protocol byte stream.
class RenderThread : public emugl::Thread { class RenderThread : public emugl::Thread {
public: public:
// Create a new RenderThread instance. // Create a new RenderThread instance.
// |stream| is an input stream that will be read from the thread, // |stream| is an input stream that will be read from the thread,
// and deleted by it when it exits. // and deleted by it when it exits.
// |mutex| is a pointer to a shared mutex used to serialize // |mutex| is a pointer to a shared mutex used to serialize
// decoding operations between all threads. // decoding operations between all threads.
// TODO(digit): Why is this needed here? Shouldn't this be handled // TODO(digit): Why is this needed here? Shouldn't this be handled
// by the decoders themselves or at a lower-level? // by the decoders themselves or at a lower-level?
static RenderThread* create(IOStream* stream, emugl::Mutex* mutex); static RenderThread* create(IOStream* stream, emugl::Mutex* mutex);
// Destructor. // Destructor.
virtual ~RenderThread(); virtual ~RenderThread();
// Returns true iff the thread has finished. // Returns true iff the thread has finished.
// Note that this also means that the thread's stack has been // Note that this also means that the thread's stack has been
bool isFinished() { return tryWait(NULL); } bool isFinished() { return tryWait(NULL); }
// Force a thread to stop. // Force a thread to stop.
void forceStop(); void forceStop();
private: private:
RenderThread(); // No default constructor RenderThread(); // No default constructor
RenderThread(IOStream* stream, emugl::Mutex* mutex); RenderThread(IOStream* stream, emugl::Mutex* mutex);
virtual intptr_t main(); virtual intptr_t main();
emugl::Mutex* m_lock; emugl::Mutex* m_lock;
IOStream* m_stream; IOStream* m_stream;
}; };
#endif #endif

View file

@ -22,22 +22,18 @@
namespace { namespace {
class ThreadInfoStore : public ::emugl::ThreadStore { class ThreadInfoStore : public ::emugl::ThreadStore {
public: public:
ThreadInfoStore() : ::emugl::ThreadStore(NULL) {} ThreadInfoStore() : ::emugl::ThreadStore(NULL) {}
}; };
} // namespace } // namespace
static ::emugl::LazyInstance<ThreadInfoStore> s_tls = LAZY_INSTANCE_INIT; static ::emugl::LazyInstance<ThreadInfoStore> s_tls = LAZY_INSTANCE_INIT;
RenderThreadInfo::RenderThreadInfo() { RenderThreadInfo::RenderThreadInfo() { s_tls->set(this); }
s_tls->set(this);
}
RenderThreadInfo::~RenderThreadInfo() { RenderThreadInfo::~RenderThreadInfo() { s_tls->set(NULL); }
s_tls->set(NULL);
}
RenderThreadInfo* RenderThreadInfo::get() { RenderThreadInfo* RenderThreadInfo::get() {
return static_cast<RenderThreadInfo*>(s_tls->get()); return static_cast<RenderThreadInfo*>(s_tls->get());
} }

View file

@ -16,10 +16,10 @@
#ifndef _LIB_OPENGL_RENDER_THREAD_INFO_H #ifndef _LIB_OPENGL_RENDER_THREAD_INFO_H
#define _LIB_OPENGL_RENDER_THREAD_INFO_H #define _LIB_OPENGL_RENDER_THREAD_INFO_H
#include "RenderContext.h"
#include "WindowSurface.h"
#include "GLESv1Decoder.h" #include "GLESv1Decoder.h"
#include "GLESv2Decoder.h" #include "GLESv2Decoder.h"
#include "RenderContext.h"
#include "WindowSurface.h"
#include "renderControl_dec.h" #include "renderControl_dec.h"
#include <set> #include <set>
@ -30,31 +30,31 @@ typedef std::set<HandleType> WindowSurfaceSet;
// A class used to model the state of each RenderThread related // A class used to model the state of each RenderThread related
struct RenderThreadInfo { struct RenderThreadInfo {
// Create new instance. Only call this once per thread. // Create new instance. Only call this once per thread.
// Future callls to get() will return this instance until // Future callls to get() will return this instance until
// it is destroyed. // it is destroyed.
RenderThreadInfo(); RenderThreadInfo();
// Destructor. // Destructor.
~RenderThreadInfo(); ~RenderThreadInfo();
// Return the current thread's instance, if any, or NULL. // Return the current thread's instance, if any, or NULL.
static RenderThreadInfo* get(); static RenderThreadInfo* get();
// Current EGL context, draw surface and read surface. // Current EGL context, draw surface and read surface.
RenderContextPtr currContext; RenderContextPtr currContext;
WindowSurfacePtr currDrawSurf; WindowSurfacePtr currDrawSurf;
WindowSurfacePtr currReadSurf; WindowSurfacePtr currReadSurf;
// Decoder states. // Decoder states.
GLESv1Decoder m_glDec; GLESv1Decoder m_glDec;
GLESv2Decoder m_gl2Dec; GLESv2Decoder m_gl2Dec;
renderControl_decoder_context_t m_rcDec; renderControl_decoder_context_t m_rcDec;
// all the contexts that are created by this render thread // all the contexts that are created by this render thread
ThreadContextSet m_contextSet; ThreadContextSet m_contextSet;
// all the window surfaces that are created by this render thread // all the window surfaces that are created by this render thread
WindowSurfaceSet m_windowSet; WindowSurfaceSet m_windowSet;
}; };
#endif #endif

View file

@ -14,51 +14,51 @@
#include "RenderWindow.h" #include "RenderWindow.h"
#include "Renderer.h"
#include "emugl/common/logging.h" #include "emugl/common/logging.h"
#include "emugl/common/message_channel.h" #include "emugl/common/message_channel.h"
#include "emugl/common/mutex.h" #include "emugl/common/mutex.h"
#include "emugl/common/thread.h" #include "emugl/common/thread.h"
#include "Renderer.h"
#include <pthread.h>
#include <signal.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <signal.h>
#include <pthread.h>
#define DEBUG 0 #define DEBUG 0
#if DEBUG #if DEBUG
# define D(...) my_debug(__PRETTY_FUNCTION__, __LINE__, __VA_ARGS__) #define D(...) my_debug(__PRETTY_FUNCTION__, __LINE__, __VA_ARGS__)
#else #else
# define D(...) ((void)0) #define D(...) ((void)0)
#endif #endif
namespace { namespace {
#if DEBUG #if DEBUG
void my_debug(const char* function, int line, const char* format, ...) { void my_debug(const char* function, int line, const char* format, ...) {
static ::emugl::Mutex mutex; static ::emugl::Mutex mutex;
va_list args; va_list args;
va_start(args, format); va_start(args, format);
mutex.lock(); mutex.lock();
fprintf(stderr, "%s:%d:", function, line); fprintf(stderr, "%s:%d:", function, line);
vfprintf(stderr, format, args); vfprintf(stderr, format, args);
mutex.unlock(); mutex.unlock();
va_end(args); va_end(args);
} }
#endif #endif
// List of possible commands to send to the render window thread from // List of possible commands to send to the render window thread from
// the main one. // the main one.
enum Command { enum Command {
CMD_INITIALIZE, CMD_INITIALIZE,
CMD_SET_POST_CALLBACK, CMD_SET_POST_CALLBACK,
CMD_SETUP_SUBWINDOW, CMD_SETUP_SUBWINDOW,
CMD_REMOVE_SUBWINDOW, CMD_REMOVE_SUBWINDOW,
CMD_SET_ROTATION, CMD_SET_ROTATION,
CMD_SET_TRANSLATION, CMD_SET_TRANSLATION,
CMD_REPAINT, CMD_REPAINT,
CMD_FINALIZE, CMD_FINALIZE,
}; };
} // namespace } // namespace
@ -66,74 +66,73 @@ enum Command {
// A single message sent from the main thread to the render window thread. // A single message sent from the main thread to the render window thread.
// |cmd| determines which fields are valid to read. // |cmd| determines which fields are valid to read.
struct RenderWindowMessage { struct RenderWindowMessage {
Command cmd; Command cmd;
union { union {
// CMD_INITIALIZE // CMD_INITIALIZE
struct { struct {
EGLNativeDisplayType nativeDisplay; EGLNativeDisplayType nativeDisplay;
} init; } init;
// CMD_SET_POST_CALLBACK // CMD_SET_POST_CALLBACK
struct { struct {
OnPostFn on_post; OnPostFn on_post;
void* on_post_context; void* on_post_context;
} set_post_callback; } set_post_callback;
// CMD_SETUP_SUBWINDOW // CMD_SETUP_SUBWINDOW
struct { struct {
FBNativeWindowType parent; FBNativeWindowType parent;
int wx; int wx;
int wy; int wy;
int ww; int ww;
int wh; int wh;
int fbw; int fbw;
int fbh; int fbh;
float dpr; float dpr;
float rotation; float rotation;
} subwindow; } subwindow;
// CMD_SET_TRANSLATION; // CMD_SET_TRANSLATION;
struct { struct {
float px; float px;
float py; float py;
} trans; } trans;
// CMD_SET_ROTATION // CMD_SET_ROTATION
float rotation; float rotation;
// result of operations. // result of operations.
bool result; bool result;
}; };
// Process the current message, and updates its |result| field. // Process the current message, and updates its |result| field.
// Returns true on success, or false on failure. // Returns true on success, or false on failure.
bool process() const { bool process() const {
const RenderWindowMessage& msg = *this; const RenderWindowMessage& msg = *this;
Renderer* fb; Renderer* fb;
bool result = false; bool result = false;
switch (msg.cmd) { switch (msg.cmd) {
case CMD_INITIALIZE: case CMD_INITIALIZE:
D("CMD_INITIALIZE\n"); D("CMD_INITIALIZE\n");
GL_LOG("RenderWindow: CMD_INITIALIZE"); GL_LOG("RenderWindow: CMD_INITIALIZE");
result = Renderer::initialize(msg.init.nativeDisplay); result = Renderer::initialize(msg.init.nativeDisplay);
break; break;
case CMD_FINALIZE: case CMD_FINALIZE:
D("CMD_FINALIZE\n"); D("CMD_FINALIZE\n");
// this command may be issued even when frame buffer is not // this command may be issued even when frame buffer is not
// yet created (e.g. if CMD_INITIALIZE failed), // yet created (e.g. if CMD_INITIALIZE failed),
// so make sure we check if it is there before finalizing // so make sure we check if it is there before finalizing
if (const auto fb = Renderer::get()) { if (const auto fb = Renderer::get()) {
fb->finalize(); fb->finalize();
}
result = true;
break;
default:
;
} }
return result; result = true;
break;
default:;
} }
return result;
}
}; };
// Simple synchronization structure used to exchange data between the // Simple synchronization structure used to exchange data between the
@ -159,43 +158,43 @@ struct RenderWindowMessage {
// canWriteCmd.signal() // canWriteCmd.signal()
// //
class RenderWindowChannel { class RenderWindowChannel {
public: public:
RenderWindowChannel() : mIn(), mOut() {} RenderWindowChannel() : mIn(), mOut() {}
~RenderWindowChannel() {} ~RenderWindowChannel() {}
// Send a message from the main thread. // Send a message from the main thread.
// Note that the content of |msg| is copied into the channel. // Note that the content of |msg| is copied into the channel.
// Returns with the command's result (true or false). // Returns with the command's result (true or false).
bool sendMessageAndGetResult(const RenderWindowMessage& msg) { bool sendMessageAndGetResult(const RenderWindowMessage& msg) {
D("msg.cmd=%d\n", msg.cmd); D("msg.cmd=%d\n", msg.cmd);
mIn.send(msg); mIn.send(msg);
D("waiting for result\n"); D("waiting for result\n");
bool result = false; bool result = false;
mOut.receive(&result); mOut.receive(&result);
D("result=%s\n", result ? "success" : "failure"); D("result=%s\n", result ? "success" : "failure");
return result; return result;
} }
// Receive a message from the render window thread. // Receive a message from the render window thread.
// On exit, |*msg| gets a copy of the message. The caller // On exit, |*msg| gets a copy of the message. The caller
// must always call sendResult() after processing the message. // must always call sendResult() after processing the message.
void receiveMessage(RenderWindowMessage* msg) { void receiveMessage(RenderWindowMessage* msg) {
D("entering\n"); D("entering\n");
mIn.receive(msg); mIn.receive(msg);
D("message cmd=%d\n", msg->cmd); D("message cmd=%d\n", msg->cmd);
} }
// Send result from the render window thread to the main one. // Send result from the render window thread to the main one.
// Must always be called after receiveMessage(). // Must always be called after receiveMessage().
void sendResult(bool result) { void sendResult(bool result) {
D("waiting to send result (%s)\n", result ? "success" : "failure"); D("waiting to send result (%s)\n", result ? "success" : "failure");
mOut.send(result); mOut.send(result);
D("result sent\n"); D("result sent\n");
} }
private: private:
emugl::MessageChannel<RenderWindowMessage, 16U> mIn; emugl::MessageChannel<RenderWindowMessage, 16U> mIn;
emugl::MessageChannel<bool, 16U> mOut; emugl::MessageChannel<bool, 16U> mOut;
}; };
namespace { namespace {
@ -207,162 +206,152 @@ namespace {
// The thread ends with a CMD_FINALIZE. // The thread ends with a CMD_FINALIZE.
// //
class RenderWindowThread : public emugl::Thread { class RenderWindowThread : public emugl::Thread {
public: public:
RenderWindowThread(RenderWindowChannel* channel) : mChannel(channel) {} RenderWindowThread(RenderWindowChannel* channel) : mChannel(channel) {}
virtual intptr_t main() { virtual intptr_t main() {
D("Entering render window thread thread\n"); D("Entering render window thread thread\n");
sigset_t set; sigset_t set;
sigfillset(&set); sigfillset(&set);
pthread_sigmask(SIG_SETMASK, &set, NULL); pthread_sigmask(SIG_SETMASK, &set, NULL);
bool running = true; bool running = true;
while (running) { while (running) {
RenderWindowMessage msg; RenderWindowMessage msg;
D("Waiting for message from main thread\n"); D("Waiting for message from main thread\n");
mChannel->receiveMessage(&msg); mChannel->receiveMessage(&msg);
bool result = msg.process(); bool result = msg.process();
if (msg.cmd == CMD_FINALIZE) { if (msg.cmd == CMD_FINALIZE) {
running = false; running = false;
} }
D("Sending result (%s) to main thread\n", result ? "success" : "failure"); D("Sending result (%s) to main thread\n", result ? "success" : "failure");
mChannel->sendResult(result); mChannel->sendResult(result);
}
D("Exiting thread\n");
return 0;
} }
D("Exiting thread\n");
return 0;
}
private: private:
RenderWindowChannel* mChannel; RenderWindowChannel* mChannel;
}; };
} // namespace } // namespace
RenderWindow::RenderWindow(EGLNativeDisplayType native_display, RenderWindow::RenderWindow(EGLNativeDisplayType native_display, bool use_thread)
bool use_thread) : : mValid(false), mHasSubWindow(false), mThread(NULL), mChannel(NULL) {
mValid(false), if (use_thread) {
mHasSubWindow(false), mChannel = new RenderWindowChannel();
mThread(NULL), mThread = new RenderWindowThread(mChannel);
mChannel(NULL) { mThread->start();
if (use_thread) { }
mChannel = new RenderWindowChannel();
mThread = new RenderWindowThread(mChannel);
mThread->start();
}
RenderWindowMessage msg; RenderWindowMessage msg;
msg.cmd = CMD_INITIALIZE; msg.cmd = CMD_INITIALIZE;
msg.init.nativeDisplay = native_display; msg.init.nativeDisplay = native_display;
mValid = processMessage(msg); mValid = processMessage(msg);
} }
RenderWindow::~RenderWindow() { RenderWindow::~RenderWindow() {
D("Entering\n"); D("Entering\n");
removeSubWindow(); removeSubWindow();
D("Sending CMD_FINALIZE\n"); D("Sending CMD_FINALIZE\n");
RenderWindowMessage msg; RenderWindowMessage msg;
msg.cmd = CMD_FINALIZE; msg.cmd = CMD_FINALIZE;
(void) processMessage(msg); (void)processMessage(msg);
if (mThread) { if (mThread) {
mThread->wait(NULL); mThread->wait(NULL);
delete mThread; delete mThread;
delete mChannel; delete mChannel;
} }
} }
bool RenderWindow::getHardwareStrings(const char** vendor, bool RenderWindow::getHardwareStrings(const char** vendor,
const char** renderer, const char** renderer,
const char** version) { const char** version) {
D("Entering\n"); D("Entering\n");
// TODO(digit): Move this to render window thread. // TODO(digit): Move this to render window thread.
Renderer* fb = Renderer::get(); Renderer* fb = Renderer::get();
if (!fb) { if (!fb) {
D("No framebuffer!\n"); D("No framebuffer!\n");
return false; return false;
} }
fb->getGLStrings(vendor, renderer, version); fb->getGLStrings(vendor, renderer, version);
D("Exiting vendor=[%s] renderer=[%s] version=[%s]\n", D("Exiting vendor=[%s] renderer=[%s] version=[%s]\n", *vendor, *renderer,
*vendor, *renderer, *version); *version);
return true; return true;
} }
bool RenderWindow::setupSubWindow(FBNativeWindowType window, bool RenderWindow::setupSubWindow(FBNativeWindowType window, int wx, int wy,
int wx, int ww, int wh, int fbw, int fbh, float dpr,
int wy,
int ww,
int wh,
int fbw,
int fbh,
float dpr,
float zRot) { float zRot) {
D("Entering mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false"); D("Entering mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false");
RenderWindowMessage msg; RenderWindowMessage msg;
msg.cmd = CMD_SETUP_SUBWINDOW; msg.cmd = CMD_SETUP_SUBWINDOW;
msg.subwindow.parent = window; msg.subwindow.parent = window;
msg.subwindow.wx = wx; msg.subwindow.wx = wx;
msg.subwindow.wy = wy; msg.subwindow.wy = wy;
msg.subwindow.ww = ww; msg.subwindow.ww = ww;
msg.subwindow.wh = wh; msg.subwindow.wh = wh;
msg.subwindow.fbw = fbw; msg.subwindow.fbw = fbw;
msg.subwindow.fbh = fbh; msg.subwindow.fbh = fbh;
msg.subwindow.dpr = dpr; msg.subwindow.dpr = dpr;
msg.subwindow.rotation = zRot; msg.subwindow.rotation = zRot;
mHasSubWindow = processMessage(msg); mHasSubWindow = processMessage(msg);
D("Exiting mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false"); D("Exiting mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false");
return mHasSubWindow; return mHasSubWindow;
} }
bool RenderWindow::removeSubWindow() { bool RenderWindow::removeSubWindow() {
D("Entering mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false"); D("Entering mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false");
if (!mHasSubWindow) { if (!mHasSubWindow) {
return false; return false;
} }
mHasSubWindow = false; mHasSubWindow = false;
RenderWindowMessage msg; RenderWindowMessage msg;
msg.cmd = CMD_REMOVE_SUBWINDOW; msg.cmd = CMD_REMOVE_SUBWINDOW;
bool result = processMessage(msg); bool result = processMessage(msg);
D("Exiting result=%s\n", result ? "success" : "failure"); D("Exiting result=%s\n", result ? "success" : "failure");
return result; return result;
} }
void RenderWindow::setRotation(float zRot) { void RenderWindow::setRotation(float zRot) {
D("Entering rotation=%f\n", zRot); D("Entering rotation=%f\n", zRot);
RenderWindowMessage msg; RenderWindowMessage msg;
msg.cmd = CMD_SET_ROTATION; msg.cmd = CMD_SET_ROTATION;
msg.rotation = zRot; msg.rotation = zRot;
(void) processMessage(msg); (void)processMessage(msg);
D("Exiting\n"); D("Exiting\n");
} }
void RenderWindow::setTranslation(float px, float py) { void RenderWindow::setTranslation(float px, float py) {
D("Entering translation=%f,%f\n", px, py); D("Entering translation=%f,%f\n", px, py);
RenderWindowMessage msg; RenderWindowMessage msg;
msg.cmd = CMD_SET_TRANSLATION; msg.cmd = CMD_SET_TRANSLATION;
msg.trans.px = px; msg.trans.px = px;
msg.trans.py = py; msg.trans.py = py;
(void) processMessage(msg); (void)processMessage(msg);
D("Exiting\n"); D("Exiting\n");
} }
void RenderWindow::repaint() { void RenderWindow::repaint() {
D("Entering\n"); D("Entering\n");
RenderWindowMessage msg; RenderWindowMessage msg;
msg.cmd = CMD_REPAINT; msg.cmd = CMD_REPAINT;
(void) processMessage(msg); (void)processMessage(msg);
D("Exiting\n"); D("Exiting\n");
} }
bool RenderWindow::processMessage(const RenderWindowMessage& msg) { bool RenderWindow::processMessage(const RenderWindowMessage& msg) {
if (mChannel) { if (mChannel) {
return mChannel->sendMessageAndGetResult(msg); return mChannel->sendMessageAndGetResult(msg);
} else { } else {
return msg.process(); return msg.process();
} }
} }

View file

@ -47,84 +47,76 @@ struct RenderWindowMessage;
// 6) Call repaint() to force a repaint(). // 6) Call repaint() to force a repaint().
// //
class RenderWindow { class RenderWindow {
public: public:
// Create new instance. |width| and |height| are the dimensions of the // Create new instance. |width| and |height| are the dimensions of the
// emulated accelerated framebuffer. |use_thread| can be true to force // emulated accelerated framebuffer. |use_thread| can be true to force
// the use of a separate thread, which might be required on some platforms // the use of a separate thread, which might be required on some platforms
// to avoid GL-realted corruption issues in the main window. Call // to avoid GL-realted corruption issues in the main window. Call
// isValid() after construction to verify that it worked properly. // isValid() after construction to verify that it worked properly.
// //
// |use_sub_window| is true if the client will call setupSubWindow(), // |use_sub_window| is true if the client will call setupSubWindow(),
// and false if it will call setPostCallback(). // and false if it will call setPostCallback().
// //
// Note that this call doesn't display anything, it just initializes // Note that this call doesn't display anything, it just initializes
// the library, use setupSubWindow() to display something. // the library, use setupSubWindow() to display something.
RenderWindow(EGLNativeDisplayType native_display, bool use_thread); RenderWindow(EGLNativeDisplayType native_display, bool use_thread);
// Destructor. This will automatically call removeSubWindow() is needed. // Destructor. This will automatically call removeSubWindow() is needed.
~RenderWindow(); ~RenderWindow();
// Returns true if the RenderWindow instance is valid, which really // Returns true if the RenderWindow instance is valid, which really
// means that the constructor succeeded. // means that the constructor succeeded.
bool isValid() const { return mValid; } bool isValid() const { return mValid; }
// Return misc. GL strings to the caller. On success, return true and sets // Return misc. GL strings to the caller. On success, return true and sets
// |*vendor| to the GL vendor string, |*renderer| to the GL renderer one, // |*vendor| to the GL vendor string, |*renderer| to the GL renderer one,
// and |*version| to the GL version one. On failure, return false. // and |*version| to the GL version one. On failure, return false.
bool getHardwareStrings(const char** vendor, bool getHardwareStrings(const char** vendor, const char** renderer,
const char** renderer, const char** version);
const char** version);
// Start displaying the emulated framebuffer using a sub-window of a // Start displaying the emulated framebuffer using a sub-window of a
// parent |window| id. |wx|, |wy|, |ww| and |wh| are the position // parent |window| id. |wx|, |wy|, |ww| and |wh| are the position
// and dimension of the sub-window, relative to its parent. // and dimension of the sub-window, relative to its parent.
// |fbw| and |fbh| are the dimensions of the underlying guest framebuffer. // |fbw| and |fbh| are the dimensions of the underlying guest framebuffer.
// |dpr| is the device pixel ratio for the monitor, which is required for // |dpr| is the device pixel ratio for the monitor, which is required for
// higher-density displays (such as retina). // higher-density displays (such as retina).
// |rotation| is a clockwise-rotation for the content. Only multiples of // |rotation| is a clockwise-rotation for the content. Only multiples of
// 90. are accepted. Returns true on success, false otherwise. // 90. are accepted. Returns true on success, false otherwise.
// //
// If the subwindow already exists, this function will update // If the subwindow already exists, this function will update
// the dimensions of the subwindow, backing framebuffer, and rendering // the dimensions of the subwindow, backing framebuffer, and rendering
// pipeline to reflect the new values. // pipeline to reflect the new values.
// //
// One can call removeSubWindow() to remove the sub-window. // One can call removeSubWindow() to remove the sub-window.
bool setupSubWindow(FBNativeWindowType window, bool setupSubWindow(FBNativeWindowType window, int wx, int wy, int ww, int wh,
int wx, int fbw, int fbh, float dpr, float rotation);
int wy,
int ww,
int wh,
int fbw,
int fbh,
float dpr,
float rotation);
// Remove the sub-window created by calling setupSubWindow(). // Remove the sub-window created by calling setupSubWindow().
// Note that this doesn't discard the content of the emulated framebuffer, // Note that this doesn't discard the content of the emulated framebuffer,
// it just hides it from the main window. Returns true on success, false // it just hides it from the main window. Returns true on success, false
// otherwise. // otherwise.
bool removeSubWindow(); bool removeSubWindow();
// Change the display rotation on the fly. |zRot| is a clockwise rotation // Change the display rotation on the fly. |zRot| is a clockwise rotation
// angle in degrees. Only multiples of 90. are accepted. // angle in degrees. Only multiples of 90. are accepted.
void setRotation(float zRot); void setRotation(float zRot);
// Change the display translation. |px|,|py| are numbers between 0 and 1, // Change the display translation. |px|,|py| are numbers between 0 and 1,
// with (0,0) indicating "align the bottom left of the framebuffer with the // with (0,0) indicating "align the bottom left of the framebuffer with the
// bottom left of the subwindow", and (1,1) indicating "align the top right of // bottom left of the subwindow", and (1,1) indicating "align the top right of
// the framebuffer with the top right of the subwindow." // the framebuffer with the top right of the subwindow."
void setTranslation(float px, float py); void setTranslation(float px, float py);
// Force a repaint of the whole content into the sub-window. // Force a repaint of the whole content into the sub-window.
void repaint(); void repaint();
private: private:
bool processMessage(const RenderWindowMessage& msg); bool processMessage(const RenderWindowMessage& msg);
bool mValid; bool mValid;
bool mHasSubWindow; bool mHasSubWindow;
emugl::Thread* mThread; emugl::Thread* mThread;
RenderWindowChannel* mChannel; RenderWindowChannel* mChannel;
}; };
#endif // ANDROID_EMUGL_LIBRENDER_RENDER_WINDOW_H #endif // ANDROID_EMUGL_LIBRENDER_RENDER_WINDOW_H

View file

@ -16,56 +16,34 @@
#include "Renderable.h" #include "Renderable.h"
Renderable::Renderable(const std::string &name, Renderable::Renderable(const std::string &name, const std::uint32_t &buffer,
const std::uint32_t &buffer,
const anbox::graphics::Rect &screen_position, const anbox::graphics::Rect &screen_position,
const anbox::graphics::Rect &crop, const anbox::graphics::Rect &crop,
const glm::mat4 &transformation, const glm::mat4 &transformation, const float &alpha)
const float &alpha) : : name_(name),
name_(name), buffer_(buffer),
buffer_(buffer), screen_position_(screen_position),
screen_position_(screen_position), crop_(crop),
crop_(crop), transformation_(transformation),
transformation_(transformation), alpha_(alpha) {}
alpha_(alpha)
{ Renderable::~Renderable() {}
std::string Renderable::name() const { return name_; }
std::uint32_t Renderable::buffer() const { return buffer_; }
anbox::graphics::Rect Renderable::screen_position() const {
return screen_position_;
} }
Renderable::~Renderable() anbox::graphics::Rect Renderable::crop() const { return crop_; }
{
}
std::string Renderable::name() const glm::mat4 Renderable::transformation() const { return transformation_; }
{
return name_;
}
std::uint32_t Renderable::buffer() const float Renderable::alpha() const { return alpha_; }
{
return buffer_;
}
anbox::graphics::Rect Renderable::screen_position() const void Renderable::set_screen_position(
{ const anbox::graphics::Rect &screen_position) {
return screen_position_; screen_position_ = screen_position;
}
anbox::graphics::Rect Renderable::crop() const
{
return crop_;
}
glm::mat4 Renderable::transformation() const
{
return transformation_;
}
float Renderable::alpha() const
{
return alpha_;
}
void Renderable::set_screen_position(const anbox::graphics::Rect &screen_position)
{
screen_position_ = screen_position;
} }

View file

@ -26,33 +26,30 @@
#include <glm/glm.hpp> #include <glm/glm.hpp>
class Renderable class Renderable {
{ public:
public: Renderable(const std::string &name, const std::uint32_t &buffer,
Renderable(const std::string &name, const anbox::graphics::Rect &screen_position,
const std::uint32_t &buffer, const anbox::graphics::Rect &crop = {},
const anbox::graphics::Rect &screen_position, const glm::mat4 &transformation = {}, const float &alpha = 1.0f);
const anbox::graphics::Rect &crop = {}, ~Renderable();
const glm::mat4 &transformation = {},
const float &alpha = 1.0f);
~Renderable();
std::string name() const; std::string name() const;
std::uint32_t buffer() const; std::uint32_t buffer() const;
anbox::graphics::Rect screen_position() const; anbox::graphics::Rect screen_position() const;
anbox::graphics::Rect crop() const; anbox::graphics::Rect crop() const;
glm::mat4 transformation() const; glm::mat4 transformation() const;
float alpha() const; float alpha() const;
void set_screen_position(const anbox::graphics::Rect &screen_position); void set_screen_position(const anbox::graphics::Rect &screen_position);
private: private:
std::string name_; std::string name_;
std::uint32_t buffer_; std::uint32_t buffer_;
anbox::graphics::Rect screen_position_; anbox::graphics::Rect screen_position_;
anbox::graphics::Rect crop_; anbox::graphics::Rect crop_;
glm::mat4 transformation_; glm::mat4 transformation_;
float alpha_; float alpha_;
}; };
typedef std::vector<Renderable> RenderableList; typedef std::vector<Renderable> RenderableList;

File diff suppressed because it is too large Load diff

View file

@ -17,18 +17,18 @@
#define _LIBRENDER_FRAMEBUFFER_H #define _LIBRENDER_FRAMEBUFFER_H
#include "ColorBuffer.h" #include "ColorBuffer.h"
#include "emugl/common/mutex.h"
#include "RendererConfig.h"
#include "RenderContext.h" #include "RenderContext.h"
#include "RendererConfig.h"
#include "TextureDraw.h" #include "TextureDraw.h"
#include "WindowSurface.h" #include "WindowSurface.h"
#include "emugl/common/mutex.h"
#include "OpenglRender/render_api.h" #include "OpenglRender/render_api.h"
#include "Renderable.h" #include "Renderable.h"
#include "anbox/graphics/program_family.h"
#include "anbox/graphics/primitives.h" #include "anbox/graphics/primitives.h"
#include "anbox/graphics/program_family.h"
#include <EGL/egl.h> #include <EGL/egl.h>
@ -41,11 +41,12 @@
typedef uint32_t HandleType; typedef uint32_t HandleType;
struct ColorBufferRef { struct ColorBufferRef {
ColorBufferPtr cb; ColorBufferPtr cb;
uint32_t refcount; // number of client-side references uint32_t refcount; // number of client-side references
}; };
typedef std::map<HandleType, RenderContextPtr> RenderContextMap; typedef std::map<HandleType, RenderContextPtr> RenderContextMap;
typedef std::map<HandleType, std::pair<WindowSurfacePtr, HandleType> > WindowSurfaceMap; typedef std::map<HandleType, std::pair<WindowSurfacePtr, HandleType>>
WindowSurfaceMap;
typedef std::map<HandleType, ColorBufferRef> ColorBufferMap; typedef std::map<HandleType, ColorBufferRef> ColorBufferMap;
// A structure used to list the capabilities of the underlying EGL // A structure used to list the capabilities of the underlying EGL
@ -57,10 +58,10 @@ typedef std::map<HandleType, ColorBufferRef> ColorBufferMap;
// |eglMajor| and |eglMinor| are the major and minor version numbers of // |eglMajor| and |eglMinor| are the major and minor version numbers of
// the underlying EGL implementation. // the underlying EGL implementation.
struct RendererCaps { struct RendererCaps {
bool has_eglimage_texture_2d; bool has_eglimage_texture_2d;
bool has_eglimage_renderbuffer; bool has_eglimage_renderbuffer;
EGLint eglMajor; EGLint eglMajor;
EGLint eglMinor; EGLint eglMinor;
}; };
struct RendererWindow; struct RendererWindow;
@ -73,252 +74,251 @@ struct RendererWindow;
// and which must be previously setup by calling initialize(). // and which must be previously setup by calling initialize().
// //
class Renderer { class Renderer {
public: public:
// Initialize the global instance. // Initialize the global instance.
// |width| and |height| are the dimensions of the emulator GPU display // |width| and |height| are the dimensions of the emulator GPU display
// in pixels. |useSubWindow| is true to indicate that the caller // in pixels. |useSubWindow| is true to indicate that the caller
// will use setupSubWindow() to let EmuGL display the GPU content in its // will use setupSubWindow() to let EmuGL display the GPU content in its
// own sub-windows. If false, this means the caller will use // own sub-windows. If false, this means the caller will use
// setPostCallback() instead to retrieve the content. // setPostCallback() instead to retrieve the content.
// Returns true on success, false otherwise. // Returns true on success, false otherwise.
static bool initialize(EGLNativeDisplayType nativeDisplay); static bool initialize(EGLNativeDisplayType nativeDisplay);
// Finalize the instance. // Finalize the instance.
void finalize(); void finalize();
// Return a pointer to the global instance. initialize() must be called // Return a pointer to the global instance. initialize() must be called
// previously, or this will return NULL. // previously, or this will return NULL.
static Renderer *get() { return s_renderer; } static Renderer* get() { return s_renderer; }
// Return the capabilities of the underlying display. // Return the capabilities of the underlying display.
const RendererCaps &getCaps() const { return m_caps; } const RendererCaps& getCaps() const { return m_caps; }
// Return the list of configs available from this display. // Return the list of configs available from this display.
const RendererConfigList* getConfigs() const { return m_configs; } const RendererConfigList* getConfigs() const { return m_configs; }
// Set a callback that will be called each time the emulated GPU content // Set a callback that will be called each time the emulated GPU content
// is updated. This can be relatively slow with host-based GPU emulation, // is updated. This can be relatively slow with host-based GPU emulation,
// so only do this when you need to. // so only do this when you need to.
void setPostCallback(OnPostFn onPost, void* onPostContext); void setPostCallback(OnPostFn onPost, void* onPostContext);
// Retrieve the GL strings of the underlying EGL/GLES implementation. // Retrieve the GL strings of the underlying EGL/GLES implementation.
// On return, |*vendor|, |*renderer| and |*version| will point to strings // On return, |*vendor|, |*renderer| and |*version| will point to strings
// that are owned by the instance (and must not be freed by the caller). // that are owned by the instance (and must not be freed by the caller).
void getGLStrings(const char** vendor, void getGLStrings(const char** vendor, const char** renderer,
const char** renderer, const char** version) const {
const char** version) const { *vendor = m_glVendor;
*vendor = m_glVendor; *renderer = m_glRenderer;
*renderer = m_glRenderer; *version = m_glVersion;
*version = m_glVersion; }
}
RendererWindow* createNativeWindow(EGLNativeWindowType native_window); RendererWindow* createNativeWindow(EGLNativeWindowType native_window);
void destroyNativeWindow(RendererWindow *window); void destroyNativeWindow(RendererWindow* window);
void destroyNativeWindow(EGLNativeWindowType native_window); void destroyNativeWindow(EGLNativeWindowType native_window);
// Create a new RenderContext instance for this display instance. // Create a new RenderContext instance for this display instance.
// |p_config| is the index of one of the configs returned by getConfigs(). // |p_config| is the index of one of the configs returned by getConfigs().
// |p_share| is either EGL_NO_CONTEXT or the handle of a shared context. // |p_share| is either EGL_NO_CONTEXT or the handle of a shared context.
// |p_isGL2| is true to create a GLES 2.x context, or false for a GLES 1.x // |p_isGL2| is true to create a GLES 2.x context, or false for a GLES 1.x
// one. // one.
// Return a new handle value, which will be 0 in case of error. // Return a new handle value, which will be 0 in case of error.
HandleType createRenderContext( HandleType createRenderContext(int p_config, HandleType p_share,
int p_config, HandleType p_share, bool p_isGL2 = false); bool p_isGL2 = false);
// Create a new WindowSurface instance from this display instance. // Create a new WindowSurface instance from this display instance.
// |p_config| is the index of one of the configs returned by getConfigs(). // |p_config| is the index of one of the configs returned by getConfigs().
// |p_width| and |p_height| are the window dimensions in pixels. // |p_width| and |p_height| are the window dimensions in pixels.
// Return a new handle value, or 0 in case of error. // Return a new handle value, or 0 in case of error.
HandleType createWindowSurface(int p_config, int p_width, int p_height); HandleType createWindowSurface(int p_config, int p_width, int p_height);
// Create a new ColorBuffer instance from this display instance. // Create a new ColorBuffer instance from this display instance.
// |p_width| and |p_height| are its dimensions in pixels. // |p_width| and |p_height| are its dimensions in pixels.
// |p_internalFormat| is the pixel format. See ColorBuffer::create() for // |p_internalFormat| is the pixel format. See ColorBuffer::create() for
// list of valid values. Note that ColorBuffer instances are reference- // list of valid values. Note that ColorBuffer instances are reference-
// counted. Use openColorBuffer / closeColorBuffer to operate on the // counted. Use openColorBuffer / closeColorBuffer to operate on the
// internal count. // internal count.
HandleType createColorBuffer( HandleType createColorBuffer(int p_width, int p_height,
int p_width, int p_height, GLenum p_internalFormat); GLenum p_internalFormat);
// Call this function when a render thread terminates to destroy all // Call this function when a render thread terminates to destroy all
// the remaining contexts it created. Necessary to avoid leaking host // the remaining contexts it created. Necessary to avoid leaking host
// contexts when a guest application crashes, for example. // contexts when a guest application crashes, for example.
void drainRenderContext(); void drainRenderContext();
// Call this function when a render thread terminates to destroy all // Call this function when a render thread terminates to destroy all
// remaining window surfqce it created. Necessary to avoid leaking // remaining window surfqce it created. Necessary to avoid leaking
// host buffers when a guest application crashes, for example. // host buffers when a guest application crashes, for example.
void drainWindowSurface(); void drainWindowSurface();
// Destroy a given RenderContext instance. |p_context| is its handle // Destroy a given RenderContext instance. |p_context| is its handle
// value as returned by createRenderContext(). // value as returned by createRenderContext().
void DestroyRenderContext(HandleType p_context); void DestroyRenderContext(HandleType p_context);
// Destroy a given WindowSurface instance. |p_surcace| is its handle // Destroy a given WindowSurface instance. |p_surcace| is its handle
// value as returned by createWindowSurface(). // value as returned by createWindowSurface().
void DestroyWindowSurface(HandleType p_surface); void DestroyWindowSurface(HandleType p_surface);
// Increment the reference count associated with a given ColorBuffer // Increment the reference count associated with a given ColorBuffer
// instance. |p_colorbuffer| is its handle value as returned by // instance. |p_colorbuffer| is its handle value as returned by
// createColorBuffer(). // createColorBuffer().
int openColorBuffer(HandleType p_colorbuffer); int openColorBuffer(HandleType p_colorbuffer);
// Decrement the reference count associated with a given ColorBuffer // Decrement the reference count associated with a given ColorBuffer
// instance. |p_colorbuffer| is its handle value as returned by // instance. |p_colorbuffer| is its handle value as returned by
// createColorBuffer(). Note that if the reference count reaches 0, // createColorBuffer(). Note that if the reference count reaches 0,
// the instance is destroyed automatically. // the instance is destroyed automatically.
void closeColorBuffer(HandleType p_colorbuffer); void closeColorBuffer(HandleType p_colorbuffer);
// Equivalent for eglMakeCurrent() for the current display. // Equivalent for eglMakeCurrent() for the current display.
// |p_context|, |p_drawSurface| and |p_readSurface| are the handle values // |p_context|, |p_drawSurface| and |p_readSurface| are the handle values
// of the context, the draw surface and the read surface, respectively. // of the context, the draw surface and the read surface, respectively.
// Returns true on success, false on failure. // Returns true on success, false on failure.
// Note: if all handle values are 0, this is an unbind operation. // Note: if all handle values are 0, this is an unbind operation.
bool bindContext(HandleType p_context, bool bindContext(HandleType p_context, HandleType p_drawSurface,
HandleType p_drawSurface, HandleType p_readSurface);
HandleType p_readSurface);
// Attach a ColorBuffer to a WindowSurface instance. // Attach a ColorBuffer to a WindowSurface instance.
// See the documentation for WindowSurface::setColorBuffer(). // See the documentation for WindowSurface::setColorBuffer().
// |p_surface| is the target WindowSurface's handle value. // |p_surface| is the target WindowSurface's handle value.
// |p_colorbuffer| is the ColorBuffer handle value. // |p_colorbuffer| is the ColorBuffer handle value.
// Returns true on success, false otherwise. // Returns true on success, false otherwise.
bool setWindowSurfaceColorBuffer( bool setWindowSurfaceColorBuffer(HandleType p_surface,
HandleType p_surface, HandleType p_colorbuffer); HandleType p_colorbuffer);
// Copy the content of a WindowSurface's Pbuffer to its attached // Copy the content of a WindowSurface's Pbuffer to its attached
// ColorBuffer. See the documentation for WindowSurface::flushColorBuffer() // ColorBuffer. See the documentation for WindowSurface::flushColorBuffer()
// |p_surface| is the target WindowSurface's handle value. // |p_surface| is the target WindowSurface's handle value.
// Returns true on success, false on failure. // Returns true on success, false on failure.
bool flushWindowSurfaceColorBuffer(HandleType p_surface); bool flushWindowSurfaceColorBuffer(HandleType p_surface);
// Bind the current context's EGL_TEXTURE_2D texture to a ColorBuffer // Bind the current context's EGL_TEXTURE_2D texture to a ColorBuffer
// instance's EGLImage. This is intended to implement // instance's EGLImage. This is intended to implement
// glEGLImageTargetTexture2DOES() for all GLES versions. // glEGLImageTargetTexture2DOES() for all GLES versions.
// |p_colorbuffer| is the ColorBuffer's handle value. // |p_colorbuffer| is the ColorBuffer's handle value.
// Returns true on success, false on failure. // Returns true on success, false on failure.
bool bindColorBufferToTexture(HandleType p_colorbuffer); bool bindColorBufferToTexture(HandleType p_colorbuffer);
// Bind the current context's EGL_RENDERBUFFER_OES render buffer to this // Bind the current context's EGL_RENDERBUFFER_OES render buffer to this
// ColorBuffer's EGLImage. This is intended to implement // ColorBuffer's EGLImage. This is intended to implement
// glEGLImageTargetRenderbufferStorageOES() for all GLES versions. // glEGLImageTargetRenderbufferStorageOES() for all GLES versions.
// |p_colorbuffer| is the ColorBuffer's handle value. // |p_colorbuffer| is the ColorBuffer's handle value.
// Returns true on success, false on failure. // Returns true on success, false on failure.
bool bindColorBufferToRenderbuffer(HandleType p_colorbuffer); bool bindColorBufferToRenderbuffer(HandleType p_colorbuffer);
// Read the content of a given ColorBuffer into client memory. // Read the content of a given ColorBuffer into client memory.
// |p_colorbuffer| is the ColorBuffer's handle value. Similar // |p_colorbuffer| is the ColorBuffer's handle value. Similar
// to glReadPixels(), this can be a slow operation. // to glReadPixels(), this can be a slow operation.
// |x|, |y|, |width| and |height| are the position and dimensions of // |x|, |y|, |width| and |height| are the position and dimensions of
// a rectangle whose pixel values will be transfered to the host. // a rectangle whose pixel values will be transfered to the host.
// |format| indicates the format of the pixel data, e.g. GL_RGB or GL_RGBA. // |format| indicates the format of the pixel data, e.g. GL_RGB or GL_RGBA.
// |type| is the type of pixel data, e.g. GL_UNSIGNED_BYTE. // |type| is the type of pixel data, e.g. GL_UNSIGNED_BYTE.
// |pixels| is the address of a caller-provided buffer that will be filled // |pixels| is the address of a caller-provided buffer that will be filled
// with the pixel data. // with the pixel data.
void readColorBuffer(HandleType p_colorbuffer, void readColorBuffer(HandleType p_colorbuffer, int x, int y, int width,
int x, int y, int width, int height, int height, GLenum format, GLenum type, void* pixels);
GLenum format, GLenum type, void *pixels);
// Update the content of a given ColorBuffer from client data. // Update the content of a given ColorBuffer from client data.
// |p_colorbuffer| is the ColorBuffer's handle value. Similar // |p_colorbuffer| is the ColorBuffer's handle value. Similar
// to glReadPixels(), this can be a slow operation. // to glReadPixels(), this can be a slow operation.
// |x|, |y|, |width| and |height| are the position and dimensions of // |x|, |y|, |width| and |height| are the position and dimensions of
// a rectangle whose pixel values will be transfered to the GPU // a rectangle whose pixel values will be transfered to the GPU
// |format| indicates the format of the pixel data, e.g. GL_RGB or GL_RGBA. // |format| indicates the format of the pixel data, e.g. GL_RGB or GL_RGBA.
// |type| is the type of pixel data, e.g. GL_UNSIGNED_BYTE. // |type| is the type of pixel data, e.g. GL_UNSIGNED_BYTE.
// |pixels| is the address of a buffer containing the new pixel data. // |pixels| is the address of a buffer containing the new pixel data.
// Returns true on success, false otherwise. // Returns true on success, false otherwise.
bool updateColorBuffer(HandleType p_colorbuffer, bool updateColorBuffer(HandleType p_colorbuffer, int x, int y, int width,
int x, int y, int width, int height, int height, GLenum format, GLenum type, void* pixels);
GLenum format, GLenum type, void *pixels);
bool draw(EGLNativeWindowType native_window, const anbox::graphics::Rect &window_frame, const RenderableList &renderables); bool draw(EGLNativeWindowType native_window,
const anbox::graphics::Rect& window_frame,
const RenderableList& renderables);
// Return the host EGLDisplay used by this instance. // Return the host EGLDisplay used by this instance.
EGLDisplay getDisplay() const { return m_eglDisplay; } EGLDisplay getDisplay() const { return m_eglDisplay; }
// Return a TextureDraw instance that can be used with this surfaces // Return a TextureDraw instance that can be used with this surfaces
// and windows created by this instance. // and windows created by this instance.
TextureDraw* getTextureDraw() const { return m_textureDraw; } TextureDraw* getTextureDraw() const { return m_textureDraw; }
HandleType createClientImage(HandleType context, EGLenum target, GLuint buffer); HandleType createClientImage(HandleType context, EGLenum target,
EGLBoolean destroyClientImage(HandleType image); GLuint buffer);
EGLBoolean destroyClientImage(HandleType image);
// Used internally. // Used internally.
bool bind_locked(); bool bind_locked();
bool unbind_locked(); bool unbind_locked();
private: private:
Renderer(); Renderer();
~Renderer(); ~Renderer();
HandleType genHandle(); HandleType genHandle();
bool bindWindow_locked(RendererWindow *window); bool bindWindow_locked(RendererWindow* window);
void setupViewport(RendererWindow *window, const anbox::graphics::Rect &rect); void setupViewport(RendererWindow* window, const anbox::graphics::Rect& rect);
struct Program; struct Program;
void draw(RendererWindow *window, const Renderable &renderable, const Program &prog); void draw(RendererWindow* window, const Renderable& renderable,
void tessellate(std::vector<anbox::graphics::Primitive>& primitives, const Program& prog);
const anbox::graphics::Rect &buf_size, void tessellate(std::vector<anbox::graphics::Primitive>& primitives,
const Renderable &renderable); const anbox::graphics::Rect& buf_size,
const Renderable& renderable);
private: private:
static Renderer *s_renderer; static Renderer* s_renderer;
static HandleType s_nextHandle; static HandleType s_nextHandle;
emugl::Mutex m_lock; emugl::Mutex m_lock;
RendererConfigList* m_configs; RendererConfigList* m_configs;
FBNativeWindowType m_nativeWindow; FBNativeWindowType m_nativeWindow;
RendererCaps m_caps; RendererCaps m_caps;
EGLDisplay m_eglDisplay; EGLDisplay m_eglDisplay;
RenderContextMap m_contexts; RenderContextMap m_contexts;
WindowSurfaceMap m_windows; WindowSurfaceMap m_windows;
ColorBufferMap m_colorbuffers; ColorBufferMap m_colorbuffers;
ColorBuffer::Helper* m_colorBufferHelper; ColorBuffer::Helper* m_colorBufferHelper;
EGLContext m_eglContext; EGLContext m_eglContext;
EGLSurface m_pbufSurface; EGLSurface m_pbufSurface;
EGLContext m_pbufContext; EGLContext m_pbufContext;
EGLContext m_prevContext; EGLContext m_prevContext;
EGLSurface m_prevReadSurf; EGLSurface m_prevReadSurf;
EGLSurface m_prevDrawSurf; EGLSurface m_prevDrawSurf;
TextureDraw* m_textureDraw; TextureDraw* m_textureDraw;
EGLConfig m_eglConfig; EGLConfig m_eglConfig;
HandleType m_lastPostedColorBuffer; HandleType m_lastPostedColorBuffer;
int m_statsNumFrames; int m_statsNumFrames;
long long m_statsStartTime; long long m_statsStartTime;
bool m_fpsStats; bool m_fpsStats;
const char* m_glVendor; const char* m_glVendor;
const char* m_glRenderer; const char* m_glRenderer;
const char* m_glVersion; const char* m_glVersion;
std::map<EGLNativeWindowType,RendererWindow*> m_nativeWindows; std::map<EGLNativeWindowType, RendererWindow*> m_nativeWindows;
anbox::graphics::ProgramFamily m_family; anbox::graphics::ProgramFamily m_family;
struct Program struct Program {
{ GLuint id = 0;
GLuint id = 0; GLint tex_uniform = -1;
GLint tex_uniform = -1; GLint position_attr = -1;
GLint position_attr = -1; GLint texcoord_attr = -1;
GLint texcoord_attr = -1; GLint center_uniform = -1;
GLint center_uniform = -1; GLint display_transform_uniform = -1;
GLint display_transform_uniform = -1; GLint transform_uniform = -1;
GLint transform_uniform = -1; GLint screen_to_gl_coords_uniform = -1;
GLint screen_to_gl_coords_uniform = -1; GLint alpha_uniform = -1;
GLint alpha_uniform = -1; mutable long long last_used_frameno = 0;
mutable long long last_used_frameno = 0;
Program(GLuint program_id); Program(GLuint program_id);
Program() {} Program() {}
}; };
Program m_defaultProgram, m_alphaProgram; Program m_defaultProgram, m_alphaProgram;
std::vector<anbox::graphics::Primitive> m_primitives; std::vector<anbox::graphics::Primitive> m_primitives;
static const GLchar* const vshader; static const GLchar* const vshader;
static const GLchar* const defaultFShader; static const GLchar* const defaultFShader;
static const GLchar* const alphaFShader; static const GLchar* const alphaFShader;
}; };
#endif #endif

View file

@ -21,255 +21,223 @@
namespace { namespace {
#define E(...) fprintf(stderr, __VA_ARGS__) #define E(...) fprintf(stderr, __VA_ARGS__)
const GLuint kConfigAttributes[] = { const GLuint kConfigAttributes[] = {
EGL_DEPTH_SIZE, // must be first - see getDepthSize() EGL_DEPTH_SIZE, // must be first - see getDepthSize()
EGL_STENCIL_SIZE, // must be second - see getStencilSize() EGL_STENCIL_SIZE, // must be second - see getStencilSize()
EGL_RENDERABLE_TYPE,// must be third - see getRenderableType() EGL_RENDERABLE_TYPE, // must be third - see getRenderableType()
EGL_SURFACE_TYPE, // must be fourth - see getSurfaceType() EGL_SURFACE_TYPE, // must be fourth - see getSurfaceType()
EGL_CONFIG_ID, // must be fifth - see chooseConfig() EGL_CONFIG_ID, // must be fifth - see chooseConfig()
EGL_BUFFER_SIZE, EGL_BUFFER_SIZE, EGL_ALPHA_SIZE, EGL_BLUE_SIZE, EGL_GREEN_SIZE,
EGL_ALPHA_SIZE, EGL_RED_SIZE, EGL_CONFIG_CAVEAT, EGL_LEVEL, EGL_MAX_PBUFFER_HEIGHT,
EGL_BLUE_SIZE, EGL_MAX_PBUFFER_PIXELS, EGL_MAX_PBUFFER_WIDTH, EGL_NATIVE_RENDERABLE,
EGL_GREEN_SIZE, EGL_NATIVE_VISUAL_ID, EGL_NATIVE_VISUAL_TYPE, EGL_SAMPLES,
EGL_RED_SIZE, EGL_SAMPLE_BUFFERS, EGL_TRANSPARENT_TYPE, EGL_TRANSPARENT_BLUE_VALUE,
EGL_CONFIG_CAVEAT, EGL_TRANSPARENT_GREEN_VALUE, EGL_TRANSPARENT_RED_VALUE,
EGL_LEVEL, EGL_BIND_TO_TEXTURE_RGB, EGL_BIND_TO_TEXTURE_RGBA, EGL_MIN_SWAP_INTERVAL,
EGL_MAX_PBUFFER_HEIGHT, EGL_MAX_SWAP_INTERVAL, EGL_LUMINANCE_SIZE, EGL_ALPHA_MASK_SIZE,
EGL_MAX_PBUFFER_PIXELS,
EGL_MAX_PBUFFER_WIDTH,
EGL_NATIVE_RENDERABLE,
EGL_NATIVE_VISUAL_ID,
EGL_NATIVE_VISUAL_TYPE,
EGL_SAMPLES,
EGL_SAMPLE_BUFFERS,
EGL_TRANSPARENT_TYPE,
EGL_TRANSPARENT_BLUE_VALUE,
EGL_TRANSPARENT_GREEN_VALUE,
EGL_TRANSPARENT_RED_VALUE,
EGL_BIND_TO_TEXTURE_RGB,
EGL_BIND_TO_TEXTURE_RGBA,
EGL_MIN_SWAP_INTERVAL,
EGL_MAX_SWAP_INTERVAL,
EGL_LUMINANCE_SIZE,
EGL_ALPHA_MASK_SIZE,
EGL_COLOR_BUFFER_TYPE, EGL_COLOR_BUFFER_TYPE,
//EGL_MATCH_NATIVE_PIXMAP, // EGL_MATCH_NATIVE_PIXMAP,
EGL_CONFORMANT EGL_CONFORMANT};
};
const size_t kConfigAttributesLen = const size_t kConfigAttributesLen =
sizeof(kConfigAttributes) / sizeof(kConfigAttributes[0]); sizeof(kConfigAttributes) / sizeof(kConfigAttributes[0]);
bool isCompatibleHostConfig(EGLConfig config, EGLDisplay display) { bool isCompatibleHostConfig(EGLConfig config, EGLDisplay display) {
// Filter out configs which do not support pbuffers, since they // Filter out configs which do not support pbuffers, since they
// are used to implement window surfaces. // are used to implement window surfaces.
EGLint surfaceType; EGLint surfaceType;
s_egl.eglGetConfigAttrib( s_egl.eglGetConfigAttrib(display, config, EGL_SURFACE_TYPE, &surfaceType);
display, config, EGL_SURFACE_TYPE, &surfaceType); if (!(surfaceType & EGL_PBUFFER_BIT)) {
if (!(surfaceType & EGL_PBUFFER_BIT)) { return false;
return false; }
}
// Filter out configs that do not support RGB pixel values. // Filter out configs that do not support RGB pixel values.
EGLint redSize = 0, greenSize = 0, blueSize = 0, alphaSize = 0; EGLint redSize = 0, greenSize = 0, blueSize = 0, alphaSize = 0;
s_egl.eglGetConfigAttrib( s_egl.eglGetConfigAttrib(display, config, EGL_RED_SIZE, &redSize);
display, config,EGL_RED_SIZE, &redSize); s_egl.eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &greenSize);
s_egl.eglGetConfigAttrib( s_egl.eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &blueSize);
display, config, EGL_GREEN_SIZE, &greenSize); s_egl.eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &alphaSize);
s_egl.eglGetConfigAttrib(
display, config, EGL_BLUE_SIZE, &blueSize);
s_egl.eglGetConfigAttrib(
display, config, EGL_ALPHA_SIZE, &alphaSize);
if (!redSize || !greenSize || !blueSize || !alphaSize) { if (!redSize || !greenSize || !blueSize || !alphaSize) {
return false; return false;
} }
return true; return true;
} }
} // namespace } // namespace
RendererConfig::~RendererConfig() { RendererConfig::~RendererConfig() { delete[] mAttribValues; }
delete [] mAttribValues;
RendererConfig::RendererConfig(EGLConfig hostConfig, EGLDisplay hostDisplay)
: mEglConfig(hostConfig), mAttribValues(NULL) {
mAttribValues = new GLint[kConfigAttributesLen];
for (size_t i = 0; i < kConfigAttributesLen; ++i) {
mAttribValues[i] = 0;
s_egl.eglGetConfigAttrib(hostDisplay, hostConfig, kConfigAttributes[i],
&mAttribValues[i]);
// This implementation supports guest window surfaces by wrapping
// them around host Pbuffers, so always report it to the guest.
if (kConfigAttributes[i] == EGL_SURFACE_TYPE) {
mAttribValues[i] |= EGL_WINDOW_BIT;
}
}
} }
RendererConfig::RendererConfig(EGLConfig hostConfig, EGLDisplay hostDisplay) : RendererConfigList::RendererConfigList(EGLDisplay display)
mEglConfig(hostConfig), mAttribValues(NULL) { : mCount(0), mConfigs(NULL), mDisplay(display) {
mAttribValues = new GLint[kConfigAttributesLen]; if (display == EGL_NO_DISPLAY) {
for (size_t i = 0; i < kConfigAttributesLen; ++i) { E("%s: Invalid display value %p (EGL_NO_DISPLAY)\n", __FUNCTION__,
mAttribValues[i] = 0; (void*)display);
s_egl.eglGetConfigAttrib(hostDisplay, return;
hostConfig, }
kConfigAttributes[i],
&mAttribValues[i]);
// This implementation supports guest window surfaces by wrapping EGLint numHostConfigs = 0;
// them around host Pbuffers, so always report it to the guest. if (!s_egl.eglGetConfigs(display, NULL, 0, &numHostConfigs)) {
if (kConfigAttributes[i] == EGL_SURFACE_TYPE) { E("%s: Could not get number of host EGL configs\n", __FUNCTION__);
mAttribValues[i] |= EGL_WINDOW_BIT; return;
} }
EGLConfig* hostConfigs = new EGLConfig[numHostConfigs];
s_egl.eglGetConfigs(display, hostConfigs, numHostConfigs, &numHostConfigs);
mConfigs = new RendererConfig*[numHostConfigs];
for (EGLint i = 0; i < numHostConfigs; ++i) {
// Filter out configs that are not compatible with our implementation.
if (!isCompatibleHostConfig(hostConfigs[i], display)) {
continue;
} }
} mConfigs[mCount] = new RendererConfig(hostConfigs[i], display);
mCount++;
}
RendererConfigList::RendererConfigList(EGLDisplay display) : delete[] hostConfigs;
mCount(0), mConfigs(NULL), mDisplay(display) {
if (display == EGL_NO_DISPLAY) {
E("%s: Invalid display value %p (EGL_NO_DISPLAY)\n",
__FUNCTION__, (void*)display);
return;
}
EGLint numHostConfigs = 0;
if (!s_egl.eglGetConfigs(display, NULL, 0, &numHostConfigs)) {
E("%s: Could not get number of host EGL configs\n", __FUNCTION__);
return;
}
EGLConfig* hostConfigs = new EGLConfig[numHostConfigs];
s_egl.eglGetConfigs(display, hostConfigs, numHostConfigs, &numHostConfigs);
mConfigs = new RendererConfig*[numHostConfigs];
for (EGLint i = 0; i < numHostConfigs; ++i) {
// Filter out configs that are not compatible with our implementation.
if (!isCompatibleHostConfig(hostConfigs[i], display)) {
continue;
}
mConfigs[mCount] = new RendererConfig(hostConfigs[i], display);
mCount++;
}
delete [] hostConfigs;
} }
RendererConfigList::~RendererConfigList() { RendererConfigList::~RendererConfigList() {
for (int n = 0; n < mCount; ++n) { for (int n = 0; n < mCount; ++n) {
delete mConfigs[n]; delete mConfigs[n];
} }
delete [] mConfigs; delete[] mConfigs;
} }
int RendererConfigList::chooseConfig(const EGLint* attribs, int RendererConfigList::chooseConfig(const EGLint* attribs, EGLint* configs,
EGLint* configs, EGLint configsSize) const {
EGLint configsSize) const { EGLint numHostConfigs = 0;
EGLint numHostConfigs = 0; if (!s_egl.eglGetConfigs(mDisplay, NULL, 0, &numHostConfigs)) {
if (!s_egl.eglGetConfigs(mDisplay, NULL, 0, &numHostConfigs)) { E("%s: Could not get number of host EGL configs\n", __FUNCTION__);
E("%s: Could not get number of host EGL configs\n", __FUNCTION__); return 0;
return 0; }
EGLConfig* matchedConfigs = new EGLConfig[numHostConfigs];
// If EGL_SURFACE_TYPE appears in |attribs|, the value passed to
// eglChooseConfig should be forced to EGL_PBUFFER_BIT because that's
// what it used by the current implementation, exclusively. This forces
// the rewrite of |attribs| into a new array.
bool hasSurfaceType = false;
bool mustReplaceSurfaceType = false;
int numAttribs = 0;
while (attribs[numAttribs] != EGL_NONE) {
if (attribs[numAttribs] == EGL_SURFACE_TYPE) {
hasSurfaceType = true;
if (attribs[numAttribs + 1] != EGL_PBUFFER_BIT) {
mustReplaceSurfaceType = true;
}
} }
numAttribs += 2;
}
EGLConfig* matchedConfigs = new EGLConfig[numHostConfigs]; EGLint* newAttribs = NULL;
// If EGL_SURFACE_TYPE appears in |attribs|, the value passed to if (mustReplaceSurfaceType) {
// eglChooseConfig should be forced to EGL_PBUFFER_BIT because that's // There is at least on EGL_SURFACE_TYPE in |attribs|. Copy the
// what it used by the current implementation, exclusively. This forces // array and replace all values with EGL_PBUFFER_BIT
// the rewrite of |attribs| into a new array. newAttribs = new GLint[numAttribs + 1];
bool hasSurfaceType = false; memcpy(newAttribs, attribs, numAttribs * sizeof(GLint));
bool mustReplaceSurfaceType = false; newAttribs[numAttribs] = EGL_NONE;
int numAttribs = 0; for (int n = 0; n < numAttribs; n += 2) {
while (attribs[numAttribs] != EGL_NONE) { if (newAttribs[n] == EGL_SURFACE_TYPE) {
if (attribs[numAttribs] == EGL_SURFACE_TYPE) { newAttribs[n + 1] = EGL_PBUFFER_BIT;
hasSurfaceType = true; }
if (attribs[numAttribs + 1] != EGL_PBUFFER_BIT) {
mustReplaceSurfaceType = true;
}
}
numAttribs += 2;
} }
} else if (!hasSurfaceType) {
// There is no EGL_SURFACE_TYPE in |attribs|, then add one entry
// with the value EGL_PBUFFER_BIT.
newAttribs = new GLint[numAttribs + 3];
memcpy(newAttribs, attribs, numAttribs * sizeof(GLint));
newAttribs[numAttribs] = EGL_SURFACE_TYPE;
newAttribs[numAttribs + 1] = EGL_PBUFFER_BIT;
newAttribs[numAttribs + 2] = EGL_NONE;
}
EGLint* newAttribs = NULL; if (!s_egl.eglChooseConfig(mDisplay, newAttribs ? newAttribs : attribs,
matchedConfigs, numHostConfigs, &numHostConfigs)) {
numHostConfigs = 0;
}
if (mustReplaceSurfaceType) { delete[] newAttribs;
// There is at least on EGL_SURFACE_TYPE in |attribs|. Copy the
// array and replace all values with EGL_PBUFFER_BIT int result = 0;
newAttribs = new GLint[numAttribs + 1]; for (int n = 0; n < numHostConfigs; ++n) {
memcpy(newAttribs, attribs, numAttribs * sizeof(GLint)); // Don't count or write more than |configsSize| items if |configs|
newAttribs[numAttribs] = EGL_NONE; // is not NULL.
for (int n = 0; n < numAttribs; n += 2) { if (configs && configsSize > 0 && result >= configsSize) {
if (newAttribs[n] == EGL_SURFACE_TYPE) { break;
newAttribs[n + 1] = EGL_PBUFFER_BIT;
}
}
} else if (!hasSurfaceType) {
// There is no EGL_SURFACE_TYPE in |attribs|, then add one entry
// with the value EGL_PBUFFER_BIT.
newAttribs = new GLint[numAttribs + 3];
memcpy(newAttribs, attribs, numAttribs * sizeof(GLint));
newAttribs[numAttribs] = EGL_SURFACE_TYPE;
newAttribs[numAttribs + 1] = EGL_PBUFFER_BIT;
newAttribs[numAttribs + 2] = EGL_NONE;
} }
// Skip incompatible host configs.
if (!s_egl.eglChooseConfig(mDisplay, if (!isCompatibleHostConfig(matchedConfigs[n], mDisplay)) {
newAttribs ? newAttribs : attribs, continue;
matchedConfigs,
numHostConfigs,
&numHostConfigs)) {
numHostConfigs = 0;
} }
// Find the FbConfig with the same EGL_CONFIG_ID
delete [] newAttribs; EGLint hostConfigId;
s_egl.eglGetConfigAttrib(mDisplay, matchedConfigs[n], EGL_CONFIG_ID,
int result = 0; &hostConfigId);
for (int n = 0; n < numHostConfigs; ++n) { for (int k = 0; k < mCount; ++k) {
// Don't count or write more than |configsSize| items if |configs| int guestConfigId = mConfigs[k]->getConfigId();
// is not NULL. if (guestConfigId == hostConfigId) {
if (configs && configsSize > 0 && result >= configsSize) { // There is a match. Write it to |configs| if it is not NULL.
break; if (configs && result < configsSize) {
} configs[result] = (uint32_t)k;
// Skip incompatible host configs.
if (!isCompatibleHostConfig(matchedConfigs[n], mDisplay)) {
continue;
}
// Find the FbConfig with the same EGL_CONFIG_ID
EGLint hostConfigId;
s_egl.eglGetConfigAttrib(
mDisplay, matchedConfigs[n], EGL_CONFIG_ID, &hostConfigId);
for (int k = 0; k < mCount; ++k) {
int guestConfigId = mConfigs[k]->getConfigId();
if (guestConfigId == hostConfigId) {
// There is a match. Write it to |configs| if it is not NULL.
if (configs && result < configsSize) {
configs[result] = (uint32_t)k;
}
result ++;
break;
}
} }
result++;
break;
}
} }
}
delete [] matchedConfigs; delete[] matchedConfigs;
return result; return result;
} }
void RendererConfigList::getPackInfo(EGLint* numConfigs, void RendererConfigList::getPackInfo(EGLint* numConfigs,
EGLint* numAttributes) const { EGLint* numAttributes) const {
if (numConfigs) { if (numConfigs) {
*numConfigs = mCount; *numConfigs = mCount;
} }
if (numAttributes) { if (numAttributes) {
*numAttributes = static_cast<EGLint>(kConfigAttributesLen); *numAttributes = static_cast<EGLint>(kConfigAttributesLen);
} }
} }
EGLint RendererConfigList::packConfigs(GLuint bufferByteSize, GLuint* buffer) const { EGLint RendererConfigList::packConfigs(GLuint bufferByteSize,
GLuint numAttribs = static_cast<GLuint>(kConfigAttributesLen); GLuint* buffer) const {
GLuint kGLuintSize = static_cast<GLuint>(sizeof(GLuint)); GLuint numAttribs = static_cast<GLuint>(kConfigAttributesLen);
GLuint neededByteSize = (mCount + 1) * numAttribs * kGLuintSize; GLuint kGLuintSize = static_cast<GLuint>(sizeof(GLuint));
if (!buffer || bufferByteSize < neededByteSize) { GLuint neededByteSize = (mCount + 1) * numAttribs * kGLuintSize;
return -neededByteSize; if (!buffer || bufferByteSize < neededByteSize) {
} return -neededByteSize;
// Write to the buffer the config attribute ids, followed for each one }
// of the configs, their values. // Write to the buffer the config attribute ids, followed for each one
memcpy(buffer, kConfigAttributes, kConfigAttributesLen * kGLuintSize); // of the configs, their values.
memcpy(buffer, kConfigAttributes, kConfigAttributesLen * kGLuintSize);
for (int i = 0; i < mCount; ++i) { for (int i = 0; i < mCount; ++i) {
memcpy(buffer + (i + 1) * kConfigAttributesLen, memcpy(buffer + (i + 1) * kConfigAttributesLen, mConfigs[i]->mAttribValues,
mConfigs[i]->mAttribValues, kConfigAttributesLen * kGLuintSize);
kConfigAttributesLen * kGLuintSize); }
} return mCount;
return mCount;
} }

View file

@ -33,43 +33,43 @@
// an FbConfigList from the host EGLDisplay, and use its size() and get() // an FbConfigList from the host EGLDisplay, and use its size() and get()
// methods to access it. // methods to access it.
class RendererConfig { class RendererConfig {
public: public:
// Destructor // Destructor
~RendererConfig(); ~RendererConfig();
// Retrieve host EGLConfig. // Retrieve host EGLConfig.
EGLConfig getEglConfig() const { return mEglConfig; } EGLConfig getEglConfig() const { return mEglConfig; }
// Get depth size in bits. // Get depth size in bits.
GLuint getDepthSize() const { return getAttribValue(0); } GLuint getDepthSize() const { return getAttribValue(0); }
// Get stencil size in bits. // Get stencil size in bits.
GLuint getStencilSize() const { return getAttribValue(1); } GLuint getStencilSize() const { return getAttribValue(1); }
// Get renderable type mask. // Get renderable type mask.
GLuint getRenderableType() const { return getAttribValue(2); } GLuint getRenderableType() const { return getAttribValue(2); }
// Get surface type mask. // Get surface type mask.
GLuint getSurfaceType() const { return getAttribValue(3); } GLuint getSurfaceType() const { return getAttribValue(3); }
// Get the EGL_CONFIG_ID value. This is the same as the one of the // Get the EGL_CONFIG_ID value. This is the same as the one of the
// underlying host EGLConfig handle. // underlying host EGLConfig handle.
GLint getConfigId() const { return (GLint)getAttribValue(4); } GLint getConfigId() const { return (GLint)getAttribValue(4); }
private: private:
RendererConfig(); RendererConfig();
RendererConfig(RendererConfig& other); RendererConfig(RendererConfig& other);
explicit RendererConfig(EGLConfig hostConfig, EGLDisplay hostDisplay); explicit RendererConfig(EGLConfig hostConfig, EGLDisplay hostDisplay);
friend class RendererConfigList; friend class RendererConfigList;
GLuint getAttribValue(int n) const { GLuint getAttribValue(int n) const {
return mAttribValues ? mAttribValues[n] : 0U; return mAttribValues ? mAttribValues[n] : 0U;
} }
EGLConfig mEglConfig; EGLConfig mEglConfig;
GLint* mAttribValues; GLint* mAttribValues;
}; };
// A class to model the list of FbConfig for a given EGLDisplay, this is // A class to model the list of FbConfig for a given EGLDisplay, this is
@ -92,72 +92,71 @@ private:
// 5) Use getPackInfo() and packConfigs() to retrieve information about // 5) Use getPackInfo() and packConfigs() to retrieve information about
// available configs to the guest. // available configs to the guest.
class RendererConfigList { class RendererConfigList {
public: public:
// Create a new list of FbConfig instance, by querying all compatible // Create a new list of FbConfig instance, by querying all compatible
// host configs from |display|. A compatible config is one that supports // host configs from |display|. A compatible config is one that supports
// Pbuffers and RGB pixel values. // Pbuffers and RGB pixel values.
// //
// After construction, call empty() to check if there are items. // After construction, call empty() to check if there are items.
// An empty list means there was an error during construction. // An empty list means there was an error during construction.
explicit RendererConfigList(EGLDisplay display); explicit RendererConfigList(EGLDisplay display);
// Destructor. // Destructor.
~RendererConfigList(); ~RendererConfigList();
// Return true iff the list is empty. true means there was an error // Return true iff the list is empty. true means there was an error
// during construction. // during construction.
bool empty() const { return mCount == 0; } bool empty() const { return mCount == 0; }
// Return the number of FbConfig instances in the list. // Return the number of FbConfig instances in the list.
// Each instance is identified by a number from 0 to N-1, // Each instance is identified by a number from 0 to N-1,
// where N is the result of this function. // where N is the result of this function.
size_t size() const { return static_cast<size_t>(mCount); } size_t size() const { return static_cast<size_t>(mCount); }
// Retrieve the FbConfig instance associated with |guestId|, // Retrieve the FbConfig instance associated with |guestId|,
// which must be an integer between 0 and |size() - 1|. Returns // which must be an integer between 0 and |size() - 1|. Returns
// NULL in case of failure. // NULL in case of failure.
const RendererConfig* get(int guestId) const { const RendererConfig* get(int guestId) const {
if (guestId >= 0 && guestId < mCount) { if (guestId >= 0 && guestId < mCount) {
return mConfigs[guestId]; return mConfigs[guestId];
} else { } else {
return NULL; return NULL;
}
} }
}
// Use |attribs| a list of EGL attribute name/values terminated by // Use |attribs| a list of EGL attribute name/values terminated by
// EGL_NONE, to select a set of matching FbConfig instances. // EGL_NONE, to select a set of matching FbConfig instances.
// //
// On success, returns the number of matching instances. // On success, returns the number of matching instances.
// If |configs| is not NULL, it will be populated with the guest IDs // If |configs| is not NULL, it will be populated with the guest IDs
// of the matched FbConfig instances. // of the matched FbConfig instances.
// //
// |configsSize| is the number of entries in the |configs| array. The // |configsSize| is the number of entries in the |configs| array. The
// function will never write more than |configsSize| entries into // function will never write more than |configsSize| entries into
// |configsSize|. // |configsSize|.
EGLint chooseConfig(const EGLint* attribs, EGLint chooseConfig(const EGLint* attribs, EGLint* configs,
EGLint* configs, EGLint configsSize) const;
EGLint configsSize) const;
// Retrieve information that can be sent to the guest before packed // Retrieve information that can be sent to the guest before packed
// config list information. If |numConfigs| is NULL, then |*numConfigs| // config list information. If |numConfigs| is NULL, then |*numConfigs|
// will be set on return to the number of config instances. // will be set on return to the number of config instances.
// If |numAttribs| is not NULL, then |*numAttribs| will be set on return // If |numAttribs| is not NULL, then |*numAttribs| will be set on return
// to the number of attribute values cached by each FbConfig instance. // to the number of attribute values cached by each FbConfig instance.
void getPackInfo(EGLint* mumConfigs, EGLint* numAttribs) const; void getPackInfo(EGLint* mumConfigs, EGLint* numAttribs) const;
// Write the full list information into an array of EGLuint items. // Write the full list information into an array of EGLuint items.
// |buffer| is the output buffer that will receive the data. // |buffer| is the output buffer that will receive the data.
// |bufferByteSize| is teh buffer size in bytes. // |bufferByteSize| is teh buffer size in bytes.
// On success, this returns // On success, this returns
EGLint packConfigs(GLuint bufferByteSize, GLuint* buffer) const; EGLint packConfigs(GLuint bufferByteSize, GLuint* buffer) const;
private: private:
RendererConfigList(); RendererConfigList();
RendererConfigList(const RendererConfigList& other); RendererConfigList(const RendererConfigList& other);
int mCount; int mCount;
RendererConfig** mConfigs; RendererConfig** mConfigs;
EGLDisplay mDisplay; EGLDisplay mDisplay;
}; };
#endif // _LIBRENDER_FB_CONFIG_H #endif // _LIBRENDER_FB_CONFIG_H

View file

@ -15,152 +15,130 @@
*/ */
#include "SocketStream.h" #include "SocketStream.h"
#include <errno.h> #include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/un.h> #include <sys/un.h>
#include <unistd.h>
SocketStream::SocketStream(size_t bufSize) : SocketStream::SocketStream(size_t bufSize)
IOStream(bufSize), : IOStream(bufSize), m_sock(-1), m_bufsize(bufSize), m_buf(NULL) {}
m_sock(-1),
m_bufsize(bufSize), SocketStream::SocketStream(int sock, size_t bufSize)
m_buf(NULL) : IOStream(bufSize), m_sock(sock), m_bufsize(bufSize), m_buf(NULL) {}
{
SocketStream::~SocketStream() {
if (m_sock >= 0) {
forceStop();
if (close(m_sock) < 0) perror("Closing SocketStream failed");
// DBG("SocketStream::~close @ %d \n", m_sock);
m_sock = -1;
}
if (m_buf != NULL) {
free(m_buf);
m_buf = NULL;
}
} }
SocketStream::SocketStream(int sock, size_t bufSize) : void *SocketStream::allocBuffer(size_t minSize) {
IOStream(bufSize), size_t allocSize = (m_bufsize < minSize ? minSize : m_bufsize);
m_sock(sock), if (!m_buf) {
m_bufsize(bufSize), m_buf = (unsigned char *)malloc(allocSize);
m_buf(NULL) } else if (m_bufsize < allocSize) {
{ unsigned char *p = (unsigned char *)realloc(m_buf, allocSize);
} if (p != NULL) {
m_buf = p;
SocketStream::~SocketStream() m_bufsize = allocSize;
{ } else {
if (m_sock >= 0) { ERR("%s: realloc (%zu) failed\n", __FUNCTION__, allocSize);
forceStop(); free(m_buf);
if(close(m_sock) < 0) m_buf = NULL;
perror("Closing SocketStream failed"); m_bufsize = 0;
// DBG("SocketStream::~close @ %d \n", m_sock);
m_sock = -1;
} }
if (m_buf != NULL) { }
free(m_buf);
m_buf = NULL;
}
}
return m_buf;
void *SocketStream::allocBuffer(size_t minSize)
{
size_t allocSize = (m_bufsize < minSize ? minSize : m_bufsize);
if (!m_buf) {
m_buf = (unsigned char *)malloc(allocSize);
}
else if (m_bufsize < allocSize) {
unsigned char *p = (unsigned char *)realloc(m_buf, allocSize);
if (p != NULL) {
m_buf = p;
m_bufsize = allocSize;
} else {
ERR("%s: realloc (%zu) failed\n", __FUNCTION__, allocSize);
free(m_buf);
m_buf = NULL;
m_bufsize = 0;
}
}
return m_buf;
}; };
int SocketStream::commitBuffer(size_t size) int SocketStream::commitBuffer(size_t size) { return writeFully(m_buf, size); }
{
return writeFully(m_buf, size);
}
int SocketStream::writeFully(const void* buffer, size_t size) int SocketStream::writeFully(const void *buffer, size_t size) {
{ if (!valid()) return -1;
if (!valid()) return -1;
size_t res = size; size_t res = size;
int retval = 0; int retval = 0;
while (res > 0) { while (res > 0) {
ssize_t stat = ::send(m_sock, (const char *)buffer + (size - res), res, 0); ssize_t stat = ::send(m_sock, (const char *)buffer + (size - res), res, 0);
if (stat < 0) { if (stat < 0) {
if (errno != EINTR) { if (errno != EINTR) {
retval = stat; retval = stat;
ERR("%s: failed: %s\n", __FUNCTION__, strerror(errno)); ERR("%s: failed: %s\n", __FUNCTION__, strerror(errno));
break;
}
} else {
res -= stat;
}
}
return retval;
}
const unsigned char *SocketStream::readFully(void *buf, size_t len)
{
if (!valid()) return NULL;
if (!buf) {
return NULL; // do not allow NULL buf in that implementation
}
size_t res = len;
while (res > 0) {
ssize_t stat = ::recv(m_sock, (char *)(buf) + len - res, res, 0);
if (stat > 0) {
res -= stat;
continue;
}
if (stat == 0 || errno != EINTR) { // client shutdown or error
return NULL;
}
}
return (const unsigned char *)buf;
}
const unsigned char *SocketStream::read( void *buf, size_t *inout_len)
{
if (!valid()) return NULL;
if (!buf) {
return NULL; // do not allow NULL buf in that implementation
}
int n;
do {
n = this->recv(buf, *inout_len);
} while( n < 0 && errno == EINTR );
if (n > 0) {
*inout_len = n;
return (const unsigned char *)buf;
}
return NULL;
}
int SocketStream::recv(void *buf, size_t len)
{
if (!valid()) return int(ERR_INVALID_SOCKET);
int res = 0;
while(true) {
res = ::recv(m_sock, (char *)buf, len, 0);
if (res < 0) {
if (errno == EINTR) {
continue;
}
}
break; break;
}
} else {
res -= stat;
} }
return res; }
return retval;
}
const unsigned char *SocketStream::readFully(void *buf, size_t len) {
if (!valid()) return NULL;
if (!buf) {
return NULL; // do not allow NULL buf in that implementation
}
size_t res = len;
while (res > 0) {
ssize_t stat = ::recv(m_sock, (char *)(buf) + len - res, res, 0);
if (stat > 0) {
res -= stat;
continue;
}
if (stat == 0 || errno != EINTR) { // client shutdown or error
return NULL;
}
}
return (const unsigned char *)buf;
}
const unsigned char *SocketStream::read(void *buf, size_t *inout_len) {
if (!valid()) return NULL;
if (!buf) {
return NULL; // do not allow NULL buf in that implementation
}
int n;
do {
n = this->recv(buf, *inout_len);
} while (n < 0 && errno == EINTR);
if (n > 0) {
*inout_len = n;
return (const unsigned char *)buf;
}
return NULL;
}
int SocketStream::recv(void *buf, size_t len) {
if (!valid()) return int(ERR_INVALID_SOCKET);
int res = 0;
while (true) {
res = ::recv(m_sock, (char *)buf, len, 0);
if (res < 0) {
if (errno == EINTR) {
continue;
}
}
break;
}
return res;
} }
void SocketStream::forceStop() { void SocketStream::forceStop() {
// Shutdown socket to force read/write errors. // Shutdown socket to force read/write errors.
::shutdown(m_sock, SHUT_RDWR); ::shutdown(m_sock, SHUT_RDWR);
} }

View file

@ -20,34 +20,34 @@
#include "IOStream.h" #include "IOStream.h"
class SocketStream : public IOStream { class SocketStream : public IOStream {
public: public:
typedef enum { ERR_INVALID_SOCKET = -1000 } SocketStreamError; typedef enum { ERR_INVALID_SOCKET = -1000 } SocketStreamError;
static const size_t MAX_ADDRSTR_LEN = 256; static const size_t MAX_ADDRSTR_LEN = 256;
explicit SocketStream(size_t bufsize = 10000); explicit SocketStream(size_t bufsize = 10000);
virtual ~SocketStream(); virtual ~SocketStream();
virtual int listen(char addrstr[MAX_ADDRSTR_LEN]) = 0; virtual int listen(char addrstr[MAX_ADDRSTR_LEN]) = 0;
virtual SocketStream *accept() = 0; virtual SocketStream *accept() = 0;
virtual int connect(const char* addr) = 0; virtual int connect(const char *addr) = 0;
virtual void *allocBuffer(size_t minSize); virtual void *allocBuffer(size_t minSize);
virtual int commitBuffer(size_t size); virtual int commitBuffer(size_t size);
virtual const unsigned char *readFully(void *buf, size_t len); virtual const unsigned char *readFully(void *buf, size_t len);
virtual const unsigned char *read(void *buf, size_t *inout_len); virtual const unsigned char *read(void *buf, size_t *inout_len);
bool valid() { return m_sock >= 0; } bool valid() { return m_sock >= 0; }
virtual int recv(void *buf, size_t len); virtual int recv(void *buf, size_t len);
virtual int writeFully(const void *buf, size_t len); virtual int writeFully(const void *buf, size_t len);
virtual void forceStop(); virtual void forceStop();
protected: protected:
int m_sock; int m_sock;
size_t m_bufsize; size_t m_bufsize;
unsigned char *m_buf; unsigned char *m_buf;
SocketStream(int sock, size_t bufSize); SocketStream(int sock, size_t bufSize);
}; };
#endif /* __SOCKET_STREAM_H */ #endif /* __SOCKET_STREAM_H */

View file

@ -19,8 +19,8 @@
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
@ -29,46 +29,42 @@
TcpStream::TcpStream(size_t bufSize) : SocketStream(bufSize) {} TcpStream::TcpStream(size_t bufSize) : SocketStream(bufSize) {}
TcpStream::TcpStream(int sock, size_t bufSize) : TcpStream::TcpStream(int sock, size_t bufSize) : SocketStream(sock, bufSize) {
SocketStream(sock, bufSize) { // disable Nagle algorithm to improve bandwidth of small
// disable Nagle algorithm to improve bandwidth of small // packets which are quite common in our implementation.
// packets which are quite common in our implementation. emugl::socketTcpDisableNagle(sock);
emugl::socketTcpDisableNagle(sock);
} }
int TcpStream::listen(char addrstr[MAX_ADDRSTR_LEN]) { int TcpStream::listen(char addrstr[MAX_ADDRSTR_LEN]) {
m_sock = emugl::socketTcpLoopbackServer(0, SOCK_STREAM); m_sock = emugl::socketTcpLoopbackServer(0, SOCK_STREAM);
if (!valid()) if (!valid()) return int(ERR_INVALID_SOCKET);
return int(ERR_INVALID_SOCKET);
int port = emugl::socketGetPort(m_sock); int port = emugl::socketGetPort(m_sock);
if (port < 0) { if (port < 0) {
::close(m_sock); ::close(m_sock);
return int(ERR_INVALID_SOCKET); return int(ERR_INVALID_SOCKET);
} }
snprintf(addrstr, MAX_ADDRSTR_LEN - 1, "%hu", port); snprintf(addrstr, MAX_ADDRSTR_LEN - 1, "%hu", port);
addrstr[MAX_ADDRSTR_LEN-1] = '\0'; addrstr[MAX_ADDRSTR_LEN - 1] = '\0';
return 0; return 0;
} }
SocketStream * TcpStream::accept() { SocketStream* TcpStream::accept() {
int clientSock = emugl::socketAccept(m_sock); int clientSock = emugl::socketAccept(m_sock);
if (clientSock < 0) if (clientSock < 0) return NULL;
return NULL;
return new TcpStream(clientSock, m_bufsize); return new TcpStream(clientSock, m_bufsize);
} }
int TcpStream::connect(const char* addr) { int TcpStream::connect(const char* addr) {
int port = atoi(addr); int port = atoi(addr);
m_sock = emugl::socketTcpLoopbackClient(port, SOCK_STREAM); m_sock = emugl::socketTcpLoopbackClient(port, SOCK_STREAM);
return valid() ? 0 : -1; return valid() ? 0 : -1;
} }
int TcpStream::connect(const char* hostname, unsigned short port) int TcpStream::connect(const char* hostname, unsigned short port) {
{ m_sock = emugl::socketTcpClient(hostname, port, SOCK_STREAM);
m_sock = emugl::socketTcpClient(hostname, port, SOCK_STREAM); return valid() ? 0 : -1;
return valid() ? 0 : -1;
} }

View file

@ -19,14 +19,15 @@
#include "SocketStream.h" #include "SocketStream.h"
class TcpStream : public SocketStream { class TcpStream : public SocketStream {
public: public:
explicit TcpStream(size_t bufsize = 10000); explicit TcpStream(size_t bufsize = 10000);
virtual int listen(char addrstr[MAX_ADDRSTR_LEN]); virtual int listen(char addrstr[MAX_ADDRSTR_LEN]);
virtual SocketStream *accept(); virtual SocketStream* accept();
virtual int connect(const char* addr); virtual int connect(const char* addr);
int connect(const char* hostname, unsigned short port); int connect(const char* hostname, unsigned short port);
private:
TcpStream(int sock, size_t bufSize); private:
TcpStream(int sock, size_t bufSize);
}; };
#endif #endif

View file

@ -21,7 +21,7 @@
#include <vector> #include <vector>
#include <stdio.h> #include <stdio.h>
#define ERR(...) fprintf(stderr, __VA_ARGS__) #define ERR(...) fprintf(stderr, __VA_ARGS__)
// M_PI isn't defined in C++ (when strict ISO compliance is enabled) // M_PI isn't defined in C++ (when strict ISO compliance is enabled)
#ifndef M_PI #ifndef M_PI
@ -35,25 +35,25 @@ namespace {
// |shaderText| is a 0-terminated C string for the shader source to use. // |shaderText| is a 0-terminated C string for the shader source to use.
// On success, return the handle of the new compiled shader, or 0 on failure. // On success, return the handle of the new compiled shader, or 0 on failure.
GLuint createShader(GLint shaderType, const char* shaderText) { GLuint createShader(GLint shaderType, const char* shaderText) {
// Create new shader handle and attach source. // Create new shader handle and attach source.
GLuint shader = s_gles2.glCreateShader(shaderType); GLuint shader = s_gles2.glCreateShader(shaderType);
if (!shader) { if (!shader) {
return 0; return 0;
} }
const GLchar* text = static_cast<const GLchar*>(shaderText); const GLchar* text = static_cast<const GLchar*>(shaderText);
const GLint textLen = ::strlen(shaderText); const GLint textLen = ::strlen(shaderText);
s_gles2.glShaderSource(shader, 1, &text, &textLen); s_gles2.glShaderSource(shader, 1, &text, &textLen);
// Compiler the shader. // Compiler the shader.
GLint success; GLint success;
s_gles2.glCompileShader(shader); s_gles2.glCompileShader(shader);
s_gles2.glGetShaderiv(shader, GL_COMPILE_STATUS, &success); s_gles2.glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (success == GL_FALSE) { if (success == GL_FALSE) {
s_gles2.glDeleteShader(shader); s_gles2.glDeleteShader(shader);
return 0; return 0;
} }
return shader; return shader;
} }
// No scaling / projection since we want to fill the whole viewport with // No scaling / projection since we want to fill the whole viewport with
@ -80,173 +80,156 @@ const char kFragmentShaderSource[] =
// Hard-coded arrays of vertex information. // Hard-coded arrays of vertex information.
struct Vertex { struct Vertex {
float pos[3]; float pos[3];
float coord[2]; float coord[2];
}; };
const Vertex kVertices[] = { const Vertex kVertices[] = {
{{ +1, -1, +0 }, { +1, +1 }}, {{+1, -1, +0}, {+1, +1}},
{{ +1, +1, +0 }, { +1, +0 }}, {{+1, +1, +0}, {+1, +0}},
{{ -1, +1, +0 }, { +0, +0 }}, {{-1, +1, +0}, {+0, +0}},
{{ -1, -1, +0 }, { +0, +1 }}, {{-1, -1, +0}, {+0, +1}},
}; };
const GLubyte kIndices[] = { 0, 1, 2, 2, 3, 0 }; const GLubyte kIndices[] = {0, 1, 2, 2, 3, 0};
const GLint kIndicesLen = sizeof(kIndices) / sizeof(kIndices[0]); const GLint kIndicesLen = sizeof(kIndices) / sizeof(kIndices[0]);
} // namespace } // namespace
TextureDraw::TextureDraw(EGLDisplay display) : TextureDraw::TextureDraw(EGLDisplay display)
mDisplay(display), : mDisplay(display),
mVertexShader(0), mVertexShader(0),
mFragmentShader(0), mFragmentShader(0),
mProgram(0), mProgram(0),
mPositionSlot(-1), mPositionSlot(-1),
mInCoordSlot(-1), mInCoordSlot(-1),
mTextureSlot(-1), mTextureSlot(-1),
mRotationSlot(-1), mRotationSlot(-1),
mTranslationSlot(-1) { mTranslationSlot(-1) {
// Create shaders and program. // Create shaders and program.
mVertexShader = createShader(GL_VERTEX_SHADER, kVertexShaderSource); mVertexShader = createShader(GL_VERTEX_SHADER, kVertexShaderSource);
mFragmentShader = createShader(GL_FRAGMENT_SHADER, kFragmentShaderSource); mFragmentShader = createShader(GL_FRAGMENT_SHADER, kFragmentShaderSource);
mProgram = s_gles2.glCreateProgram(); mProgram = s_gles2.glCreateProgram();
s_gles2.glAttachShader(mProgram, mVertexShader); s_gles2.glAttachShader(mProgram, mVertexShader);
s_gles2.glAttachShader(mProgram, mFragmentShader); s_gles2.glAttachShader(mProgram, mFragmentShader);
GLint success; GLint success;
s_gles2.glLinkProgram(mProgram); s_gles2.glLinkProgram(mProgram);
s_gles2.glGetProgramiv(mProgram, GL_LINK_STATUS, &success); s_gles2.glGetProgramiv(mProgram, GL_LINK_STATUS, &success);
if (success == GL_FALSE) { if (success == GL_FALSE) {
GLchar messages[256]; GLchar messages[256];
s_gles2.glGetProgramInfoLog( s_gles2.glGetProgramInfoLog(mProgram, sizeof(messages), 0, &messages[0]);
mProgram, sizeof(messages), 0, &messages[0]); ERR("%s: Could not create/link program: %s\n", __FUNCTION__, messages);
ERR("%s: Could not create/link program: %s\n", __FUNCTION__, messages); s_gles2.glDeleteProgram(mProgram);
s_gles2.glDeleteProgram(mProgram); mProgram = 0;
mProgram = 0; return;
return; }
}
s_gles2.glUseProgram(mProgram); s_gles2.glUseProgram(mProgram);
// Retrieve attribute/uniform locations. // Retrieve attribute/uniform locations.
mPositionSlot = s_gles2.glGetAttribLocation(mProgram, "position"); mPositionSlot = s_gles2.glGetAttribLocation(mProgram, "position");
s_gles2.glEnableVertexAttribArray(mPositionSlot); s_gles2.glEnableVertexAttribArray(mPositionSlot);
mInCoordSlot = s_gles2.glGetAttribLocation(mProgram, "inCoord"); mInCoordSlot = s_gles2.glGetAttribLocation(mProgram, "inCoord");
s_gles2.glEnableVertexAttribArray(mInCoordSlot); s_gles2.glEnableVertexAttribArray(mInCoordSlot);
mTextureSlot = s_gles2.glGetUniformLocation(mProgram, "texture"); mTextureSlot = s_gles2.glGetUniformLocation(mProgram, "texture");
// Create vertex and index buffers. // Create vertex and index buffers.
s_gles2.glGenBuffers(1, &mVertexBuffer); s_gles2.glGenBuffers(1, &mVertexBuffer);
s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
s_gles2.glBufferData( s_gles2.glBufferData(GL_ARRAY_BUFFER, sizeof(kVertices), kVertices,
GL_ARRAY_BUFFER, sizeof(kVertices), kVertices, GL_STATIC_DRAW); GL_STATIC_DRAW);
s_gles2.glGenBuffers(1, &mIndexBuffer); s_gles2.glGenBuffers(1, &mIndexBuffer);
s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer); s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
s_gles2.glBufferData(GL_ELEMENT_ARRAY_BUFFER, s_gles2.glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(kIndices), kIndices,
sizeof(kIndices), GL_STATIC_DRAW);
kIndices,
GL_STATIC_DRAW);
} }
bool TextureDraw::draw(GLuint texture) { bool TextureDraw::draw(GLuint texture) {
if (!mProgram) { if (!mProgram) {
ERR("%s: no program\n", __FUNCTION__); ERR("%s: no program\n", __FUNCTION__);
return false; return false;
} }
// TODO(digit): Save previous program state. // TODO(digit): Save previous program state.
GLenum err; GLenum err;
s_gles2.glUseProgram(mProgram); s_gles2.glUseProgram(mProgram);
err = s_gles2.glGetError(); err = s_gles2.glGetError();
if (err != GL_NO_ERROR) { if (err != GL_NO_ERROR) {
ERR("%s: Could not use program error=0x%x\n", ERR("%s: Could not use program error=0x%x\n", __FUNCTION__, err);
__FUNCTION__, err); }
}
// Setup the |position| attribute values.
s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
err = s_gles2.glGetError();
if (err != GL_NO_ERROR) {
ERR("%s: Could not bind GL_ARRAY_BUFFER error=0x%x\n", __FUNCTION__, err);
}
// Setup the |position| attribute values. s_gles2.glEnableVertexAttribArray(mPositionSlot);
s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); s_gles2.glVertexAttribPointer(mPositionSlot, 3, GL_FLOAT, GL_FALSE,
err = s_gles2.glGetError(); sizeof(Vertex), 0);
if (err != GL_NO_ERROR) { err = s_gles2.glGetError();
ERR("%s: Could not bind GL_ARRAY_BUFFER error=0x%x\n", if (err != GL_NO_ERROR) {
__FUNCTION__, err); ERR("%s: Could glVertexAttribPointer with mPositionSlot error=0x%x\n",
} __FUNCTION__, err);
}
s_gles2.glEnableVertexAttribArray(mPositionSlot); // Setup the |inCoord| attribute values.
s_gles2.glVertexAttribPointer(mPositionSlot, s_gles2.glEnableVertexAttribArray(mInCoordSlot);
3, s_gles2.glVertexAttribPointer(
GL_FLOAT, mInCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
GL_FALSE, reinterpret_cast<GLvoid*>(static_cast<uintptr_t>(sizeof(float) * 3)));
sizeof(Vertex),
0);
err = s_gles2.glGetError();
if (err != GL_NO_ERROR) {
ERR("%s: Could glVertexAttribPointer with mPositionSlot error=0x%x\n",
__FUNCTION__, err);
}
// Setup the |inCoord| attribute values. // setup the |texture| uniform value.
s_gles2.glEnableVertexAttribArray(mInCoordSlot); s_gles2.glActiveTexture(GL_TEXTURE0);
s_gles2.glVertexAttribPointer(mInCoordSlot, s_gles2.glBindTexture(GL_TEXTURE_2D, texture);
2, s_gles2.glUniform1i(mTextureSlot, 0);
GL_FLOAT,
GL_FALSE,
sizeof(Vertex),
reinterpret_cast<GLvoid*>(
static_cast<uintptr_t>(
sizeof(float) * 3)));
// setup the |texture| uniform value. // Validate program, just to be sure.
s_gles2.glActiveTexture(GL_TEXTURE0); s_gles2.glValidateProgram(mProgram);
s_gles2.glBindTexture(GL_TEXTURE_2D, texture); GLint validState = 0;
s_gles2.glUniform1i(mTextureSlot, 0); s_gles2.glGetProgramiv(mProgram, GL_VALIDATE_STATUS, &validState);
if (validState == GL_FALSE) {
GLchar messages[256];
s_gles2.glGetProgramInfoLog(mProgram, sizeof(messages), 0, &messages[0]);
ERR("%s: Could not run program: %s\n", __FUNCTION__, messages);
return false;
}
// Validate program, just to be sure. // Do the rendering.
s_gles2.glValidateProgram(mProgram); s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
GLint validState = 0; err = s_gles2.glGetError();
s_gles2.glGetProgramiv(mProgram, GL_VALIDATE_STATUS, &validState); if (err != GL_NO_ERROR) {
if (validState == GL_FALSE) { ERR("%s: Could not glBindBuffer(GL_ELEMENT_ARRAY_BUFFER) error=0x%x\n",
GLchar messages[256]; __FUNCTION__, err);
s_gles2.glGetProgramInfoLog( }
mProgram, sizeof(messages), 0, &messages[0]);
ERR("%s: Could not run program: %s\n", __FUNCTION__, messages);
return false;
}
// Do the rendering. s_gles2.glDrawElements(GL_TRIANGLES, kIndicesLen, GL_UNSIGNED_BYTE, 0);
s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer); err = s_gles2.glGetError();
err = s_gles2.glGetError(); if (err != GL_NO_ERROR) {
if (err != GL_NO_ERROR) { ERR("%s: Could not glDrawElements() error=0x%x\n", __FUNCTION__, err);
ERR("%s: Could not glBindBuffer(GL_ELEMENT_ARRAY_BUFFER) error=0x%x\n", }
__FUNCTION__, err);
}
s_gles2.glDrawElements(GL_TRIANGLES, kIndicesLen, GL_UNSIGNED_BYTE, 0); // TODO(digit): Restore previous program state.
err = s_gles2.glGetError();
if (err != GL_NO_ERROR) {
ERR("%s: Could not glDrawElements() error=0x%x\n",
__FUNCTION__, err);
}
// TODO(digit): Restore previous program state. return true;
return true;
} }
TextureDraw::~TextureDraw() { TextureDraw::~TextureDraw() {
s_gles2.glDeleteBuffers(1, &mIndexBuffer); s_gles2.glDeleteBuffers(1, &mIndexBuffer);
s_gles2.glDeleteBuffers(1, &mVertexBuffer); s_gles2.glDeleteBuffers(1, &mVertexBuffer);
if (mFragmentShader) { if (mFragmentShader) {
s_gles2.glDeleteShader(mFragmentShader); s_gles2.glDeleteShader(mFragmentShader);
} }
if (mVertexShader) { if (mVertexShader) {
s_gles2.glDeleteShader(mVertexShader); s_gles2.glDeleteShader(mVertexShader);
} }
} }

View file

@ -30,29 +30,29 @@
// framebuffer with texture content. // framebuffer with texture content.
// //
class TextureDraw { class TextureDraw {
public: public:
// Create a new instance. // Create a new instance.
TextureDraw(EGLDisplay display); TextureDraw(EGLDisplay display);
// Destructor // Destructor
~TextureDraw(); ~TextureDraw();
// Fill the current framebuffer with the content of |texture|, which must // Fill the current framebuffer with the content of |texture|, which must
// be the name of a GLES 2.x texture object. // be the name of a GLES 2.x texture object.
bool draw(GLuint texture); bool draw(GLuint texture);
private: private:
EGLDisplay mDisplay; EGLDisplay mDisplay;
GLuint mVertexShader; GLuint mVertexShader;
GLuint mFragmentShader; GLuint mFragmentShader;
GLuint mProgram; GLuint mProgram;
GLint mPositionSlot; GLint mPositionSlot;
GLint mInCoordSlot; GLint mInCoordSlot;
GLint mTextureSlot; GLint mTextureSlot;
GLint mRotationSlot; GLint mRotationSlot;
GLint mTranslationSlot; GLint mTranslationSlot;
GLuint mVertexBuffer; GLuint mVertexBuffer;
GLuint mIndexBuffer; GLuint mIndexBuffer;
}; };
#endif // TEXTURE_DRAW_H #endif // TEXTURE_DRAW_H

View file

@ -22,7 +22,7 @@
#include <string> #include <string>
#include <utility> #include <utility>
#define ERR(...) fprintf(stderr, __VA_ARGS__) #define ERR(...) fprintf(stderr, __VA_ARGS__)
#define MAX_FACTOR_POWER 4 #define MAX_FACTOR_POWER 4
static const char kCommonShaderSource[] = static const char kCommonShaderSource[] =
@ -126,219 +126,237 @@ const char kFragmentShaderSource[] =
static const float kVertexData[] = {-1, -1, 3, -1, -1, 3}; static const float kVertexData[] = {-1, -1, 3, -1, -1, 3};
static void detachShaders(GLuint program) { static void detachShaders(GLuint program) {
GLuint shaders[2] = {}; GLuint shaders[2] = {};
GLsizei count = 0; GLsizei count = 0;
s_gles2.glGetAttachedShaders(program, 2, &count, shaders); s_gles2.glGetAttachedShaders(program, 2, &count, shaders);
if (s_gles2.glGetError() == GL_NO_ERROR) { if (s_gles2.glGetError() == GL_NO_ERROR) {
for (GLsizei i = 0; i < count; i++) { for (GLsizei i = 0; i < count; i++) {
s_gles2.glDetachShader(program, shaders[i]); s_gles2.glDetachShader(program, shaders[i]);
s_gles2.glDeleteShader(shaders[i]); s_gles2.glDeleteShader(shaders[i]);
}
} }
}
} }
static GLuint createShader(GLenum type, const std::initializer_list<const char*>& source) { static GLuint createShader(GLenum type,
GLint success, infoLength; const std::initializer_list<const char*>& source) {
GLint success, infoLength;
GLuint shader = s_gles2.glCreateShader(type); GLuint shader = s_gles2.glCreateShader(type);
if (shader) { if (shader) {
s_gles2.glShaderSource(shader, source.size(), source.begin(), nullptr); s_gles2.glShaderSource(shader, source.size(), source.begin(), nullptr);
s_gles2.glCompileShader(shader); s_gles2.glCompileShader(shader);
s_gles2.glGetShaderiv(shader, GL_COMPILE_STATUS, &success); s_gles2.glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (success == GL_FALSE) { if (success == GL_FALSE) {
s_gles2.glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLength); s_gles2.glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLength);
std::string infoLog(infoLength + 1, '\0'); std::string infoLog(infoLength + 1, '\0');
s_gles2.glGetShaderInfoLog(shader, infoLength, nullptr, &infoLog[0]); s_gles2.glGetShaderInfoLog(shader, infoLength, nullptr, &infoLog[0]);
ERR("%s shader compile failed:\n%s\n", ERR("%s shader compile failed:\n%s\n",
(type == GL_VERTEX_SHADER) ? "Vertex" : "Fragment", (type == GL_VERTEX_SHADER) ? "Vertex" : "Fragment", infoLog.c_str());
infoLog.c_str()); s_gles2.glDeleteShader(shader);
s_gles2.glDeleteShader(shader); shader = 0;
shader = 0;
}
} }
return shader; }
return shader;
} }
static void attachShaders(TextureResize::Framebuffer* fb, const char* factorDefine, static void attachShaders(TextureResize::Framebuffer* fb,
const char* dimensionDefine, GLuint width, GLuint height) { const char* factorDefine, const char* dimensionDefine,
GLuint width, GLuint height) {
std::ostringstream dimensionConst;
dimensionConst << "const vec2 kDimension = vec2(" << width << ", " << height
<< ");\n";
std::ostringstream dimensionConst; GLuint vShader = createShader(
dimensionConst << "const vec2 kDimension = vec2(" << width << ", " << height << ");\n"; GL_VERTEX_SHADER, {factorDefine, dimensionDefine, kCommonShaderSource,
dimensionConst.str().c_str(), kVertexShaderSource});
GLuint fShader = createShader(
GL_FRAGMENT_SHADER, {factorDefine, dimensionDefine, kCommonShaderSource,
kFragmentShaderSource});
GLuint vShader = createShader(GL_VERTEX_SHADER, { if (!vShader || !fShader) {
factorDefine, dimensionDefine, return;
kCommonShaderSource, dimensionConst.str().c_str(), kVertexShaderSource }
});
GLuint fShader = createShader(GL_FRAGMENT_SHADER, {
factorDefine, dimensionDefine, kCommonShaderSource, kFragmentShaderSource
});
if (!vShader || !fShader) { s_gles2.glAttachShader(fb->program, vShader);
return; s_gles2.glAttachShader(fb->program, fShader);
} s_gles2.glLinkProgram(fb->program);
s_gles2.glAttachShader(fb->program, vShader); s_gles2.glUseProgram(fb->program);
s_gles2.glAttachShader(fb->program, fShader); fb->aPosition = s_gles2.glGetAttribLocation(fb->program, "aPosition");
s_gles2.glLinkProgram(fb->program); fb->uTexture = s_gles2.glGetUniformLocation(fb->program, "uTexture");
s_gles2.glUseProgram(fb->program);
fb->aPosition = s_gles2.glGetAttribLocation(fb->program, "aPosition");
fb->uTexture = s_gles2.glGetUniformLocation(fb->program, "uTexture");
} }
TextureResize::TextureResize(GLuint width, GLuint height) : TextureResize::TextureResize(GLuint width, GLuint height)
mWidth(width), : mWidth(width),
mHeight(height), mHeight(height),
mFactor(1), mFactor(1),
mFBWidth({0,}), mFBWidth({
mFBHeight({0,}) { 0,
s_gles2.glGenTextures(1, &mFBWidth.texture); }),
s_gles2.glBindTexture(GL_TEXTURE_2D, mFBWidth.texture); mFBHeight({
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 0,
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); }) {
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); s_gles2.glGenTextures(1, &mFBWidth.texture);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); s_gles2.glBindTexture(GL_TEXTURE_2D, mFBWidth.texture);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
s_gles2.glGenTextures(1, &mFBHeight.texture); s_gles2.glGenTextures(1, &mFBHeight.texture);
s_gles2.glBindTexture(GL_TEXTURE_2D, mFBHeight.texture); s_gles2.glBindTexture(GL_TEXTURE_2D, mFBHeight.texture);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
s_gles2.glGenFramebuffers(1, &mFBWidth.framebuffer); s_gles2.glGenFramebuffers(1, &mFBWidth.framebuffer);
s_gles2.glGenFramebuffers(1, &mFBHeight.framebuffer); s_gles2.glGenFramebuffers(1, &mFBHeight.framebuffer);
mFBWidth.program = s_gles2.glCreateProgram(); mFBWidth.program = s_gles2.glCreateProgram();
mFBHeight.program = s_gles2.glCreateProgram(); mFBHeight.program = s_gles2.glCreateProgram();
s_gles2.glGenBuffers(1, &mVertexBuffer); s_gles2.glGenBuffers(1, &mVertexBuffer);
s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
s_gles2.glBufferData(GL_ARRAY_BUFFER, sizeof(kVertexData), kVertexData, GL_STATIC_DRAW); s_gles2.glBufferData(GL_ARRAY_BUFFER, sizeof(kVertexData), kVertexData,
GL_STATIC_DRAW);
} }
TextureResize::~TextureResize() { TextureResize::~TextureResize() {
GLuint fb[2] = {mFBWidth.framebuffer, mFBHeight.framebuffer}; GLuint fb[2] = {mFBWidth.framebuffer, mFBHeight.framebuffer};
s_gles2.glDeleteFramebuffers(2, fb); s_gles2.glDeleteFramebuffers(2, fb);
GLuint tex[2] = {mFBWidth.texture, mFBHeight.texture}; GLuint tex[2] = {mFBWidth.texture, mFBHeight.texture};
s_gles2.glDeleteTextures(2, tex); s_gles2.glDeleteTextures(2, tex);
s_gles2.glDeleteProgram(mFBWidth.program); s_gles2.glDeleteProgram(mFBWidth.program);
s_gles2.glDeleteProgram(mFBHeight.program); s_gles2.glDeleteProgram(mFBHeight.program);
s_gles2.glDeleteBuffers(1, &mVertexBuffer); s_gles2.glDeleteBuffers(1, &mVertexBuffer);
} }
GLuint TextureResize::update(GLuint texture) { GLuint TextureResize::update(GLuint texture) {
// Store the viewport. The viewport is clobbered due to the framebuffers. // Store the viewport. The viewport is clobbered due to the framebuffers.
GLint vport[4] = { 0, }; GLint vport[4] = {
s_gles2.glGetIntegerv(GL_VIEWPORT, vport); 0,
};
s_gles2.glGetIntegerv(GL_VIEWPORT, vport);
// Correctly deal with rotated screens. // Correctly deal with rotated screens.
GLint tWidth = vport[2], tHeight = vport[3]; GLint tWidth = vport[2], tHeight = vport[3];
if ((mWidth < mHeight) != (tWidth < tHeight)) { if ((mWidth < mHeight) != (tWidth < tHeight)) {
std::swap(tWidth, tHeight); std::swap(tWidth, tHeight);
} }
// Compute the scaling factor needed to get an image just larger than the target viewport. // Compute the scaling factor needed to get an image just larger than the
unsigned int factor = 1; // target viewport.
for (int i = 0, w = mWidth / 2, h = mHeight / 2; unsigned int factor = 1;
i < MAX_FACTOR_POWER && w >= tWidth && h >= tHeight; for (int i = 0, w = mWidth / 2, h = mHeight / 2;
i++, w /= 2, h /= 2, factor *= 2) { i < MAX_FACTOR_POWER && w >= tWidth && h >= tHeight;
} i++, w /= 2, h /= 2, factor *= 2) {
}
// No resizing needed. // No resizing needed.
if (factor == 1) { if (factor == 1) {
return texture; return texture;
} }
s_gles2.glGetError(); // Clear any GL errors. s_gles2.glGetError(); // Clear any GL errors.
setupFramebuffers(factor); setupFramebuffers(factor);
resize(texture); resize(texture);
s_gles2.glViewport(vport[0], vport[1], vport[2], vport[3]); // Restore the viewport. s_gles2.glViewport(vport[0], vport[1], vport[2],
vport[3]); // Restore the viewport.
// If there was an error while resizing, just use the unscaled texture. // If there was an error while resizing, just use the unscaled texture.
GLenum error = s_gles2.glGetError(); GLenum error = s_gles2.glGetError();
if (error != GL_NO_ERROR) { if (error != GL_NO_ERROR) {
ERR("GL error while resizing: 0x%x (ignored)\n", error); ERR("GL error while resizing: 0x%x (ignored)\n", error);
return texture; return texture;
} }
return mFBHeight.texture; return mFBHeight.texture;
} }
void TextureResize::setupFramebuffers(unsigned int factor) { void TextureResize::setupFramebuffers(unsigned int factor) {
if (factor == mFactor) { if (factor == mFactor) {
// The factor hasn't changed, no need to update the framebuffers. // The factor hasn't changed, no need to update the framebuffers.
return; return;
} }
// Update the framebuffer sizes to match the new factor. // Update the framebuffer sizes to match the new factor.
s_gles2.glBindTexture(GL_TEXTURE_2D, mFBWidth.texture); s_gles2.glBindTexture(GL_TEXTURE_2D, mFBWidth.texture);
s_gles2.glTexImage2D( s_gles2.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mWidth / factor, mHeight, 0,
GL_TEXTURE_2D, 0, GL_RGBA, mWidth / factor, mHeight, 0, GL_RGBA, GL_FLOAT, nullptr); GL_RGBA, GL_FLOAT, nullptr);
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBWidth.framebuffer); s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBWidth.framebuffer);
s_gles2.glFramebufferTexture2D( s_gles2.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mFBWidth.texture, 0); GL_TEXTURE_2D, mFBWidth.texture, 0);
s_gles2.glBindTexture(GL_TEXTURE_2D, mFBHeight.texture); s_gles2.glBindTexture(GL_TEXTURE_2D, mFBHeight.texture);
s_gles2.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mWidth / factor, mHeight / factor, 0, GL_RGBA, s_gles2.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mWidth / factor,
GL_UNSIGNED_BYTE, nullptr); mHeight / factor, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBHeight.framebuffer); s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBHeight.framebuffer);
s_gles2.glFramebufferTexture2D( s_gles2.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mFBHeight.texture, 0); GL_TEXTURE_2D, mFBHeight.texture, 0);
// Update the shaders to the new factor. First detach the old shaders... // Update the shaders to the new factor. First detach the old shaders...
detachShaders(mFBWidth.program); detachShaders(mFBWidth.program);
detachShaders(mFBHeight.program); detachShaders(mFBHeight.program);
// ... then attach the new ones. // ... then attach the new ones.
std::ostringstream factorDefine; std::ostringstream factorDefine;
factorDefine << "#define FACTOR " << factor << "\n"; factorDefine << "#define FACTOR " << factor << "\n";
attachShaders(&mFBWidth, factorDefine.str().c_str(), "#define HORIZONTAL\n", mWidth, mHeight); attachShaders(&mFBWidth, factorDefine.str().c_str(), "#define HORIZONTAL\n",
attachShaders(&mFBHeight, factorDefine.str().c_str(), "#define VERTICAL\n", mWidth, mHeight); mWidth, mHeight);
attachShaders(&mFBHeight, factorDefine.str().c_str(), "#define VERTICAL\n",
mWidth, mHeight);
mFactor = factor; mFactor = factor;
} }
void TextureResize::resize(GLuint texture) { void TextureResize::resize(GLuint texture) {
s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
s_gles2.glActiveTexture(GL_TEXTURE0); s_gles2.glActiveTexture(GL_TEXTURE0);
// First scale the horizontal dimension by rendering the input texture to a scaled framebuffer. // First scale the horizontal dimension by rendering the input texture to a
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBWidth.framebuffer); // scaled framebuffer.
s_gles2.glViewport(0, 0, mWidth / mFactor, mHeight); s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBWidth.framebuffer);
s_gles2.glUseProgram(mFBWidth.program); s_gles2.glViewport(0, 0, mWidth / mFactor, mHeight);
s_gles2.glEnableVertexAttribArray(mFBWidth.aPosition); s_gles2.glUseProgram(mFBWidth.program);
s_gles2.glVertexAttribPointer(mFBWidth.aPosition, 2, GL_FLOAT, GL_FALSE, 0, 0); s_gles2.glEnableVertexAttribArray(mFBWidth.aPosition);
s_gles2.glBindTexture(GL_TEXTURE_2D, texture); s_gles2.glVertexAttribPointer(mFBWidth.aPosition, 2, GL_FLOAT, GL_FALSE, 0,
0);
s_gles2.glBindTexture(GL_TEXTURE_2D, texture);
// Store the current texture filters and set to nearest for scaling. // Store the current texture filters and set to nearest for scaling.
GLint mag_filter, min_filter; GLint mag_filter, min_filter;
s_gles2.glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, &mag_filter); s_gles2.glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
s_gles2.glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, &min_filter); &mag_filter);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); s_gles2.glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); &min_filter);
s_gles2.glUniform1i(mFBWidth.uTexture, 0); s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
s_gles2.glDrawArrays(GL_TRIANGLES, 0, sizeof(kVertexData) / (2 * sizeof(float))); s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
s_gles2.glUniform1i(mFBWidth.uTexture, 0);
s_gles2.glDrawArrays(GL_TRIANGLES, 0,
sizeof(kVertexData) / (2 * sizeof(float)));
// Restore the previous texture filters. // Restore the previous texture filters.
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter); s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter); s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
// Secondly, scale the vertical dimension using the second framebuffer. // Secondly, scale the vertical dimension using the second framebuffer.
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBHeight.framebuffer); s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBHeight.framebuffer);
s_gles2.glViewport(0, 0, mWidth / mFactor, mHeight / mFactor); s_gles2.glViewport(0, 0, mWidth / mFactor, mHeight / mFactor);
s_gles2.glUseProgram(mFBHeight.program); s_gles2.glUseProgram(mFBHeight.program);
s_gles2.glEnableVertexAttribArray(mFBHeight.aPosition); s_gles2.glEnableVertexAttribArray(mFBHeight.aPosition);
s_gles2.glVertexAttribPointer(mFBHeight.aPosition, 2, GL_FLOAT, GL_FALSE, 0, 0); s_gles2.glVertexAttribPointer(mFBHeight.aPosition, 2, GL_FLOAT, GL_FALSE, 0,
s_gles2.glBindTexture(GL_TEXTURE_2D, mFBWidth.texture); 0);
s_gles2.glUniform1i(mFBHeight.uTexture, 0); s_gles2.glBindTexture(GL_TEXTURE_2D, mFBWidth.texture);
s_gles2.glDrawArrays(GL_TRIANGLES, 0, sizeof(kVertexData) / (2 * sizeof(float))); s_gles2.glUniform1i(mFBHeight.uTexture, 0);
s_gles2.glDrawArrays(GL_TRIANGLES, 0,
sizeof(kVertexData) / (2 * sizeof(float)));
// Clear the bindings. // Clear the bindings.
s_gles2.glBindBuffer(GL_ARRAY_BUFFER, 0); s_gles2.glBindBuffer(GL_ARRAY_BUFFER, 0);
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0); s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0);
s_gles2.glBindTexture(GL_TEXTURE_2D, 0); s_gles2.glBindTexture(GL_TEXTURE_2D, 0);
} }

View file

@ -19,33 +19,33 @@
#include <GLES2/gl2.h> #include <GLES2/gl2.h>
class TextureResize { class TextureResize {
public: public:
TextureResize(GLuint width, GLuint height); TextureResize(GLuint width, GLuint height);
~TextureResize(); ~TextureResize();
// Scales the given texture for the current viewport and returns the scaled // Scales the given texture for the current viewport and returns the scaled
// texture. May return the input if no scaling is required. // texture. May return the input if no scaling is required.
GLuint update(GLuint texture); GLuint update(GLuint texture);
struct Framebuffer { struct Framebuffer {
GLuint texture; GLuint texture;
GLuint framebuffer; GLuint framebuffer;
GLuint program; GLuint program;
GLuint aPosition; GLuint aPosition;
GLuint uTexture; GLuint uTexture;
}; };
private: private:
void setupFramebuffers(unsigned int factor); void setupFramebuffers(unsigned int factor);
void resize(GLuint texture); void resize(GLuint texture);
private: private:
GLuint mWidth; GLuint mWidth;
GLuint mHeight; GLuint mHeight;
unsigned int mFactor; unsigned int mFactor;
Framebuffer mFBWidth; Framebuffer mFBWidth;
Framebuffer mFBHeight; Framebuffer mFBHeight;
GLuint mVertexBuffer; GLuint mVertexBuffer;
}; };
#endif #endif

View file

@ -15,21 +15,16 @@
*/ */
#include "TimeUtils.h" #include "TimeUtils.h"
#include <stdlib.h> #include <stdlib.h>
#include <sys/time.h> #include <sys/time.h>
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
long long GetCurrentTimeMS() long long GetCurrentTimeMS() {
{ struct timespec now;
struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now);
clock_gettime(CLOCK_MONOTONIC, &now); long long iDiff = (now.tv_sec * 1000LL) + now.tv_nsec / 1000000LL;
long long iDiff = (now.tv_sec * 1000LL) + now.tv_nsec/1000000LL; return iDiff;
return iDiff;
} }
void TimeSleepMS(int p_mili) void TimeSleepMS(int p_mili) { usleep(p_mili * 1000); }
{
usleep(p_mili * 1000);
}

View file

@ -20,155 +20,141 @@
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <sys/un.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/un.h>
/* Not all systems define PATH_MAX, those who don't generally don't /* Not all systems define PATH_MAX, those who don't generally don't
* have a limit on the maximum path size, so use a value that is * have a limit on the maximum path size, so use a value that is
* large enough for our very limited needs. * large enough for our very limited needs.
*/ */
#ifndef PATH_MAX #ifndef PATH_MAX
#define PATH_MAX 128 #define PATH_MAX 128
#endif #endif
UnixStream::UnixStream(size_t bufSize) : UnixStream::UnixStream(size_t bufSize)
SocketStream(bufSize), : SocketStream(bufSize), bound_socket_path(NULL) {}
bound_socket_path(NULL)
{
}
UnixStream::UnixStream(int sock, size_t bufSize) : UnixStream::UnixStream(int sock, size_t bufSize)
SocketStream(sock, bufSize), : SocketStream(sock, bufSize), bound_socket_path(NULL) {}
bound_socket_path(NULL)
{
}
UnixStream::~UnixStream() UnixStream::~UnixStream() {
{ if (bound_socket_path != NULL) {
if (bound_socket_path != NULL) { int ret = 0;
int ret = 0; do {
do { ret = unlink(bound_socket_path);
ret = unlink(bound_socket_path); } while (ret < 0 && errno == EINTR);
} while (ret < 0 && errno == EINTR); if (ret != 0) {
if(ret != 0) { ERR("Failed to unlink UNIX socket at \"%s\"\n", bound_socket_path);
ERR("Failed to unlink UNIX socket at \"%s\"\n", bound_socket_path); perror("UNIX socket could not be unlinked");
perror("UNIX socket could not be unlinked");
}
free(bound_socket_path);
} }
free(bound_socket_path);
}
} }
/* Initialize a sockaddr_un with the appropriate values corresponding /* Initialize a sockaddr_un with the appropriate values corresponding
* to a given 'virtual port'. Returns 0 on success, -1 on error. * to a given 'virtual port'. Returns 0 on success, -1 on error.
*/ */
static int static int make_unix_path(char *path, size_t pathlen, int port_number) {
make_unix_path(char *path, size_t pathlen, int port_number) char tmp[PATH_MAX]; // temp directory
{ int ret = 0;
char tmp[PATH_MAX]; // temp directory
int ret = 0;
// First, create user-specific temp directory if needed // First, create user-specific temp directory if needed
const char* user = getenv("XDG_RUNTIME_DIR"); const char *user = getenv("XDG_RUNTIME_DIR");
if (user != NULL) { if (user != NULL) {
struct stat st; struct stat st;
snprintf(tmp, sizeof(tmp), "%s/anbox", user); snprintf(tmp, sizeof(tmp), "%s/anbox", user);
do { do {
ret = ::lstat(tmp, &st); ret = ::lstat(tmp, &st);
} while (ret < 0 && errno == EINTR); } while (ret < 0 && errno == EINTR);
if (ret < 0 && errno == ENOENT) { if (ret < 0 && errno == ENOENT) {
do { do {
ret = ::mkdir(tmp, 0766); ret = ::mkdir(tmp, 0766);
} while (ret < 0 && errno == EINTR); } while (ret < 0 && errno == EINTR);
if (ret < 0) { if (ret < 0) {
ERR("Could not create temp directory: %s", tmp); ERR("Could not create temp directory: %s", tmp);
user = NULL; // will fall-back to /tmp user = NULL; // will fall-back to /tmp
} }
} } else if (ret < 0) {
else if (ret < 0) { user = NULL; // will fallback to /tmp
user = NULL; // will fallback to /tmp
}
} }
}
if (user == NULL) { // fallback to /tmp in case of error if (user == NULL) { // fallback to /tmp in case of error
snprintf(tmp, sizeof(tmp), "/tmp"); snprintf(tmp, sizeof(tmp), "/tmp");
}
// Now, initialize it properly
snprintf(path, pathlen, "%s/qemu-gles-%d", tmp, port_number);
// If the emulator is killed, it can leave the socket file behind.
// Since the filename has PID in it, we can be sure that this socket
// is not supposed to be here and delete it, to prevent EADDRINUSE
// later in bind()
if (::access(path, F_OK) == 0) {
ret = ::remove(path);
if (ret < 0) {
ERR("Failed to remove stale socket file at %s: %s\n", path,
strerror(errno));
} else {
DBG("Stale socket file at %s was removed.\n", path);
} }
}
// Now, initialize it properly return 0;
snprintf(path, pathlen, "%s/qemu-gles-%d", tmp, port_number);
// If the emulator is killed, it can leave the socket file behind.
// Since the filename has PID in it, we can be sure that this socket
// is not supposed to be here and delete it, to prevent EADDRINUSE
// later in bind()
if (::access(path, F_OK) == 0) {
ret = ::remove(path);
if (ret < 0) {
ERR("Failed to remove stale socket file at %s: %s\n", path, strerror(errno));
} else {
DBG("Stale socket file at %s was removed.\n", path);
}
}
return 0;
} }
int UnixStream::listen(char addrstr[MAX_ADDRSTR_LEN]) {
if (make_unix_path(addrstr, MAX_ADDRSTR_LEN, getpid()) < 0) {
return -1;
}
int UnixStream::listen(char addrstr[MAX_ADDRSTR_LEN]) m_sock = emugl::socketLocalServer(addrstr, SOCK_STREAM);
{
if (make_unix_path(addrstr, MAX_ADDRSTR_LEN, getpid()) < 0) {
return -1;
}
m_sock = emugl::socketLocalServer(addrstr, SOCK_STREAM); if (!valid()) return int(ERR_INVALID_SOCKET);
if (!valid()) bound_socket_path = strdup(addrstr);
return int(ERR_INVALID_SOCKET); if (bound_socket_path == NULL) {
ERR("WARNING: UNIX socket at \"%s\" should be manually removed \n",
addrstr);
return -1;
}
bound_socket_path = strdup(addrstr); return 0;
if(bound_socket_path == NULL) {
ERR("WARNING: UNIX socket at \"%s\" should be manually removed \n",
addrstr);
return -1;
}
return 0;
} }
SocketStream * UnixStream::accept() SocketStream *UnixStream::accept() {
{ int clientSock = -1;
int clientSock = -1;
while (true) { while (true) {
struct sockaddr_un addr; struct sockaddr_un addr;
socklen_t len = sizeof(addr); socklen_t len = sizeof(addr);
clientSock = ::accept(m_sock, (sockaddr *)&addr, &len); clientSock = ::accept(m_sock, (sockaddr *)&addr, &len);
// DBG("UnixStream::accept @ %d \n", clientSock); // DBG("UnixStream::accept @ %d \n", clientSock);
if (clientSock < 0 && errno == EINTR) { if (clientSock < 0 && errno == EINTR) {
continue; continue;
}
break;
} }
break;
}
UnixStream *clientStream = NULL; UnixStream *clientStream = NULL;
if (clientSock >= 0) { if (clientSock >= 0) {
clientStream = new UnixStream(clientSock, m_bufsize); clientStream = new UnixStream(clientSock, m_bufsize);
} }
return clientStream; return clientStream;
} }
int UnixStream::connect(const char* addr) int UnixStream::connect(const char *addr) {
{ m_sock = emugl::socketLocalClient(addr, SOCK_STREAM);
m_sock = emugl::socketLocalClient(addr, SOCK_STREAM); // DBG("UnixStream::connect @ %d \n", m_sock);
// DBG("UnixStream::connect @ %d \n", m_sock); if (!valid()) return -1;
if (!valid()) return -1;
return 0; return 0;
} }

View file

@ -19,15 +19,16 @@
#include "SocketStream.h" #include "SocketStream.h"
class UnixStream : public SocketStream { class UnixStream : public SocketStream {
public: public:
explicit UnixStream(size_t bufsize = 10000); explicit UnixStream(size_t bufsize = 10000);
~UnixStream(); ~UnixStream();
virtual int listen(char addrstr[MAX_ADDRSTR_LEN]); virtual int listen(char addrstr[MAX_ADDRSTR_LEN]);
virtual SocketStream *accept(); virtual SocketStream *accept();
virtual int connect(const char* addr); virtual int connect(const char *addr);
private:
char *bound_socket_path; private:
UnixStream(int sock, size_t bufSize); char *bound_socket_path;
UnixStream(int sock, size_t bufSize);
}; };
#endif #endif

View file

@ -25,163 +25,150 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
WindowSurface::WindowSurface(EGLDisplay display, EGLConfig config)
WindowSurface::WindowSurface(EGLDisplay display, : mSurface(NULL),
EGLConfig config) : mAttachedColorBuffer(NULL),
mSurface(NULL), mReadContext(NULL),
mAttachedColorBuffer(NULL), mDrawContext(NULL),
mReadContext(NULL), mWidth(0),
mDrawContext(NULL), mHeight(0),
mWidth(0), mConfig(config),
mHeight(0), mDisplay(display) {}
mConfig(config),
mDisplay(display) {}
WindowSurface::~WindowSurface() { WindowSurface::~WindowSurface() {
if (mSurface) { if (mSurface) {
s_egl.eglDestroySurface(mDisplay, mSurface); s_egl.eglDestroySurface(mDisplay, mSurface);
} }
} }
WindowSurface *WindowSurface::create(EGLDisplay display, WindowSurface *WindowSurface::create(EGLDisplay display, EGLConfig config,
EGLConfig config, int p_width, int p_height) {
int p_width, // allocate space for the WindowSurface object
int p_height) { WindowSurface *win = new WindowSurface(display, config);
// allocate space for the WindowSurface object if (!win) {
WindowSurface *win = new WindowSurface(display, config); return NULL;
if (!win) { }
return NULL;
}
// Create a pbuffer to be used as the egl surface // Create a pbuffer to be used as the egl surface
// for that window. // for that window.
if (!win->resize(p_width, p_height)) { if (!win->resize(p_width, p_height)) {
delete win; delete win;
return NULL; return NULL;
} }
return win; return win;
} }
void WindowSurface::setColorBuffer(ColorBufferPtr p_colorBuffer) { void WindowSurface::setColorBuffer(ColorBufferPtr p_colorBuffer) {
mAttachedColorBuffer = p_colorBuffer; mAttachedColorBuffer = p_colorBuffer;
// resize the window if the attached color buffer is of different // resize the window if the attached color buffer is of different
// size. // size.
unsigned int cbWidth = mAttachedColorBuffer->getWidth(); unsigned int cbWidth = mAttachedColorBuffer->getWidth();
unsigned int cbHeight = mAttachedColorBuffer->getHeight(); unsigned int cbHeight = mAttachedColorBuffer->getHeight();
if (cbWidth != mWidth || cbHeight != mHeight) { if (cbWidth != mWidth || cbHeight != mHeight) {
resize(cbWidth, cbHeight); resize(cbWidth, cbHeight);
} }
} }
void WindowSurface::bind(RenderContextPtr p_ctx, BindType p_bindType) { void WindowSurface::bind(RenderContextPtr p_ctx, BindType p_bindType) {
if (p_bindType == BIND_READ) { if (p_bindType == BIND_READ) {
mReadContext = p_ctx; mReadContext = p_ctx;
} else if (p_bindType == BIND_DRAW) { } else if (p_bindType == BIND_DRAW) {
mDrawContext = p_ctx; mDrawContext = p_ctx;
} else if (p_bindType == BIND_READDRAW) { } else if (p_bindType == BIND_READDRAW) {
mReadContext = p_ctx; mReadContext = p_ctx;
mDrawContext = p_ctx; mDrawContext = p_ctx;
} }
} }
bool WindowSurface::flushColorBuffer() { bool WindowSurface::flushColorBuffer() {
if (!mAttachedColorBuffer.Ptr()) { if (!mAttachedColorBuffer.Ptr()) {
return true;
}
if (!mWidth || !mHeight) {
return false;
}
if (mAttachedColorBuffer->getWidth() != mWidth ||
mAttachedColorBuffer->getHeight() != mHeight) {
// XXX: should never happen - how this needs to be handled?
fprintf(stderr, "Dimensions do not match\n");
return false;
}
if (!mDrawContext.Ptr()) {
fprintf(stderr, "Draw context is NULL\n");
return false;
}
// Make the surface current
EGLContext prevContext = s_egl.eglGetCurrentContext();
EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
if (!s_egl.eglMakeCurrent(mDisplay,
mSurface,
mSurface,
mDrawContext->getEGLContext())) {
fprintf(stderr, "Error making draw context current\n");
return false;
}
mAttachedColorBuffer->blitFromCurrentReadBuffer();
// restore current context/surface
s_egl.eglMakeCurrent(mDisplay, prevDrawSurf, prevReadSurf, prevContext);
return true; return true;
}
if (!mWidth || !mHeight) {
return false;
}
if (mAttachedColorBuffer->getWidth() != mWidth ||
mAttachedColorBuffer->getHeight() != mHeight) {
// XXX: should never happen - how this needs to be handled?
fprintf(stderr, "Dimensions do not match\n");
return false;
}
if (!mDrawContext.Ptr()) {
fprintf(stderr, "Draw context is NULL\n");
return false;
}
// Make the surface current
EGLContext prevContext = s_egl.eglGetCurrentContext();
EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
if (!s_egl.eglMakeCurrent(mDisplay, mSurface, mSurface,
mDrawContext->getEGLContext())) {
fprintf(stderr, "Error making draw context current\n");
return false;
}
mAttachedColorBuffer->blitFromCurrentReadBuffer();
// restore current context/surface
s_egl.eglMakeCurrent(mDisplay, prevDrawSurf, prevReadSurf, prevContext);
return true;
} }
bool WindowSurface::resize(unsigned int p_width, unsigned int p_height) bool WindowSurface::resize(unsigned int p_width, unsigned int p_height) {
{ if (mSurface && mWidth == p_width && mHeight == p_height) {
if (mSurface && mWidth == p_width && mHeight == p_height) { // no need to resize
// no need to resize
return true;
}
EGLContext prevContext = s_egl.eglGetCurrentContext();
EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
EGLSurface prevPbuf = mSurface;
bool needRebindContext = mSurface &&
(prevReadSurf == mSurface ||
prevDrawSurf == mSurface);
if (needRebindContext) {
s_egl.eglMakeCurrent(
mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
//
// Destroy previous surface
//
if (mSurface) {
s_egl.eglDestroySurface(mDisplay, mSurface);
mSurface = NULL;
}
//
// Create pbuffer surface.
//
const EGLint pbufAttribs[5] = {
EGL_WIDTH, (EGLint) p_width, EGL_HEIGHT, (EGLint) p_height, EGL_NONE,
};
mSurface = s_egl.eglCreatePbufferSurface(mDisplay,
mConfig,
pbufAttribs);
if (mSurface == EGL_NO_SURFACE) {
fprintf(stderr, "Renderer error: failed to create/resize pbuffer!!\n");
return false;
}
mWidth = p_width;
mHeight = p_height;
if (needRebindContext) {
s_egl.eglMakeCurrent(
mDisplay,
(prevDrawSurf == prevPbuf) ? mSurface : prevDrawSurf,
(prevReadSurf == prevPbuf) ? mSurface : prevReadSurf,
prevContext);
}
return true; return true;
}
EGLContext prevContext = s_egl.eglGetCurrentContext();
EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
EGLSurface prevPbuf = mSurface;
bool needRebindContext =
mSurface && (prevReadSurf == mSurface || prevDrawSurf == mSurface);
if (needRebindContext) {
s_egl.eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
}
//
// Destroy previous surface
//
if (mSurface) {
s_egl.eglDestroySurface(mDisplay, mSurface);
mSurface = NULL;
}
//
// Create pbuffer surface.
//
const EGLint pbufAttribs[5] = {
EGL_WIDTH, (EGLint)p_width, EGL_HEIGHT, (EGLint)p_height, EGL_NONE,
};
mSurface = s_egl.eglCreatePbufferSurface(mDisplay, mConfig, pbufAttribs);
if (mSurface == EGL_NO_SURFACE) {
fprintf(stderr, "Renderer error: failed to create/resize pbuffer!!\n");
return false;
}
mWidth = p_width;
mHeight = p_height;
if (needRebindContext) {
s_egl.eglMakeCurrent(
mDisplay, (prevDrawSurf == prevPbuf) ? mSurface : prevDrawSurf,
(prevReadSurf == prevPbuf) ? mSurface : prevReadSurf, prevContext);
}
return true;
} }

View file

@ -27,75 +27,69 @@
// A class used to model a guest-side window surface. The implementation // A class used to model a guest-side window surface. The implementation
// uses a host Pbuffer to act as the EGL rendering surface instead. // uses a host Pbuffer to act as the EGL rendering surface instead.
class WindowSurface { class WindowSurface {
public: public:
// Create a new WindowSurface instance. // Create a new WindowSurface instance.
// |display| is the host EGLDisplay value. // |display| is the host EGLDisplay value.
// |config| is the host EGLConfig value. // |config| is the host EGLConfig value.
// |width| and |height| are the initial size of the Pbuffer. // |width| and |height| are the initial size of the Pbuffer.
// Return a new WindowSurface instance on success, or NULL on failure. // Return a new WindowSurface instance on success, or NULL on failure.
static WindowSurface* create(EGLDisplay display, static WindowSurface* create(EGLDisplay display, EGLConfig config, int width,
EGLConfig config, int height);
int width,
int height);
// Destructor. // Destructor.
~WindowSurface(); ~WindowSurface();
// Retrieve the host EGLSurface of the WindowSurface's Pbuffer. // Retrieve the host EGLSurface of the WindowSurface's Pbuffer.
EGLSurface getEGLSurface() const { return mSurface; } EGLSurface getEGLSurface() const { return mSurface; }
// Attach a ColorBuffer to this WindowSurface. // Attach a ColorBuffer to this WindowSurface.
// Once attached, calling flushColorBuffer() will copy the Pbuffer's // Once attached, calling flushColorBuffer() will copy the Pbuffer's
// pixels to the color buffer. // pixels to the color buffer.
// //
// IMPORTANT: This automatically resizes the Pbuffer's to the ColorBuffer's // IMPORTANT: This automatically resizes the Pbuffer's to the ColorBuffer's
// dimensions. Potentially losing pixel values in the process. // dimensions. Potentially losing pixel values in the process.
void setColorBuffer(ColorBufferPtr p_colorBuffer); void setColorBuffer(ColorBufferPtr p_colorBuffer);
// Copy the Pbuffer's pixels to the attached color buffer. // Copy the Pbuffer's pixels to the attached color buffer.
// Returns true on success, or false on error (e.g. if there is no // Returns true on success, or false on error (e.g. if there is no
// attached color buffer). // attached color buffer).
bool flushColorBuffer(); bool flushColorBuffer();
// Used by bind() below. // Used by bind() below.
enum BindType { enum BindType { BIND_READ, BIND_DRAW, BIND_READDRAW };
BIND_READ,
BIND_DRAW,
BIND_READDRAW
};
// TODO(digit): What is this used for exactly? For example, the // TODO(digit): What is this used for exactly? For example, the
// mReadContext is never used by this class. The mDrawContext is only // mReadContext is never used by this class. The mDrawContext is only
// used temporarily during flushColorBuffer() operation, and could be // used temporarily during flushColorBuffer() operation, and could be
// passed as a parameter to the function instead. Maybe this is only used // passed as a parameter to the function instead. Maybe this is only used
// to increment reference counts on the smart pointers. // to increment reference counts on the smart pointers.
// //
// Bind a context to the WindowSurface (huh? Normally you would bind a // Bind a context to the WindowSurface (huh? Normally you would bind a
// surface to the context, not the other way around) // surface to the context, not the other way around)
// //
// |p_ctx| is a RenderContext pointer. // |p_ctx| is a RenderContext pointer.
// |p_bindType| is the type of bind. For BIND_READ, this assigns |p_ctx| // |p_bindType| is the type of bind. For BIND_READ, this assigns |p_ctx|
// to mReadContext, for BIND_DRAW, it assigns it to mDrawContext, and for // to mReadContext, for BIND_DRAW, it assigns it to mDrawContext, and for
// for BIND_READDRAW, it assigns it to both. // for BIND_READDRAW, it assigns it to both.
void bind(RenderContextPtr p_ctx, BindType p_bindType); void bind(RenderContextPtr p_ctx, BindType p_bindType);
private: private:
WindowSurface(); WindowSurface();
WindowSurface(const WindowSurface& other); WindowSurface(const WindowSurface& other);
explicit WindowSurface(EGLDisplay display, EGLConfig config); explicit WindowSurface(EGLDisplay display, EGLConfig config);
bool resize(unsigned int p_width, unsigned int p_height); bool resize(unsigned int p_width, unsigned int p_height);
private: private:
EGLSurface mSurface; EGLSurface mSurface;
ColorBufferPtr mAttachedColorBuffer; ColorBufferPtr mAttachedColorBuffer;
RenderContextPtr mReadContext; RenderContextPtr mReadContext;
RenderContextPtr mDrawContext; RenderContextPtr mDrawContext;
GLuint mWidth; GLuint mWidth;
GLuint mHeight; GLuint mHeight;
EGLConfig mConfig; EGLConfig mConfig;
EGLDisplay mDisplay; EGLDisplay mDisplay;
}; };
typedef emugl::SmartPtr<WindowSurface> WindowSurfacePtr; typedef emugl::SmartPtr<WindowSurface> WindowSurfacePtr;

Some files were not shown because too many files have changed in this diff Show more