Reformat all code and use the Google cpp code style
This commit is contained in:
parent
09811d780c
commit
b10382d2d8
222 changed files with 8739 additions and 9281 deletions
8
.clang-format
Normal file
8
.clang-format
Normal 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
3
scripts/clean-format.sh
Executable 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
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
455
src/anbox/cli.h
455
src/anbox/cli.h
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@
|
||||||
|
|
||||||
namespace anbox {
|
namespace anbox {
|
||||||
namespace container {
|
namespace container {
|
||||||
Container::~Container() {
|
Container::~Container() {}
|
||||||
}
|
} // namespace container
|
||||||
} // namespace container
|
} // namespace anbox
|
||||||
} // namespace anbox
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue