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 android {
|
||||
std::ostream& operator<<(std::ostream &out, const Intent &intent)
|
||||
{
|
||||
out << "["
|
||||
<< "action=" << intent.action << " "
|
||||
<< "uri=" << intent.uri << " "
|
||||
<< "type=" << intent.type << " "
|
||||
<< "flags=" << intent.flags << " "
|
||||
<< "package=" << intent.package << " "
|
||||
<< "component=" << intent.component << " "
|
||||
<< "categories=[ ";
|
||||
for (const auto &category : intent.categories)
|
||||
out << category << " ";
|
||||
out << "]]";
|
||||
return out;
|
||||
std::ostream &operator<<(std::ostream &out, const Intent &intent) {
|
||||
out << "["
|
||||
<< "action=" << intent.action << " "
|
||||
<< "uri=" << intent.uri << " "
|
||||
<< "type=" << intent.type << " "
|
||||
<< "flags=" << intent.flags << " "
|
||||
<< "package=" << intent.package << " "
|
||||
<< "component=" << intent.component << " "
|
||||
<< "categories=[ ";
|
||||
for (const auto &category : intent.categories) out << category << " ";
|
||||
out << "]]";
|
||||
return out;
|
||||
}
|
||||
} // namespace android
|
||||
} // namespace anbox
|
||||
|
||||
} // namespace android
|
||||
} // namespace anbox
|
||||
|
|
|
|||
|
|
@ -24,18 +24,17 @@
|
|||
namespace anbox {
|
||||
namespace android {
|
||||
struct Intent {
|
||||
std::string action;
|
||||
std::string uri;
|
||||
std::string type;
|
||||
int flags = 0;
|
||||
std::string package;
|
||||
std::string component;
|
||||
std::vector<std::string> categories;
|
||||
std::string action;
|
||||
std::string uri;
|
||||
std::string type;
|
||||
int flags = 0;
|
||||
std::string package;
|
||||
std::string component;
|
||||
std::vector<std::string> categories;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream &out, const Intent &intent);
|
||||
} // namespace android
|
||||
} // namespace anbox
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const Intent &intent);
|
||||
} // namespace android
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -19,53 +19,51 @@
|
|||
#include "anbox/utils.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace anbox {
|
||||
namespace application {
|
||||
LauncherStorage::LauncherStorage(const fs::path &path) :
|
||||
path_(path) {
|
||||
}
|
||||
LauncherStorage::LauncherStorage(const fs::path &path) : path_(path) {}
|
||||
|
||||
LauncherStorage::~LauncherStorage() {
|
||||
}
|
||||
LauncherStorage::~LauncherStorage() {}
|
||||
|
||||
void LauncherStorage::add(const Item &item) {
|
||||
if (!fs::exists(path_))
|
||||
fs::create_directories(path_);
|
||||
if (!fs::exists(path_)) fs::create_directories(path_);
|
||||
|
||||
auto package_name = item.package;
|
||||
std::replace(package_name.begin(), package_name.end(), '.', '-');
|
||||
auto package_name = item.package;
|
||||
std::replace(package_name.begin(), package_name.end(), '.', '-');
|
||||
|
||||
const auto item_path = path_ / utils::string_format("anbox-%s.desktop", package_name);
|
||||
std::string exec = "anbox launch ";
|
||||
const auto item_path =
|
||||
path_ / utils::string_format("anbox-%s.desktop", package_name);
|
||||
std::string exec = "anbox launch ";
|
||||
|
||||
if (!item.launch_intent.action.empty())
|
||||
exec += utils::string_format("--action=%s ", item.launch_intent.action);
|
||||
if (!item.launch_intent.action.empty())
|
||||
exec += utils::string_format("--action=%s ", item.launch_intent.action);
|
||||
|
||||
if (!item.launch_intent.type.empty())
|
||||
exec += utils::string_format("--type=%s ", item.launch_intent.type);
|
||||
if (!item.launch_intent.type.empty())
|
||||
exec += utils::string_format("--type=%s ", item.launch_intent.type);
|
||||
|
||||
if (!item.launch_intent.uri.empty())
|
||||
exec += utils::string_format("--uri=%s ", item.launch_intent.uri);
|
||||
if (!item.launch_intent.uri.empty())
|
||||
exec += utils::string_format("--uri=%s ", item.launch_intent.uri);
|
||||
|
||||
if (!item.launch_intent.package.empty())
|
||||
exec += utils::string_format("--package=%s ", item.launch_intent.package);
|
||||
if (!item.launch_intent.package.empty())
|
||||
exec += utils::string_format("--package=%s ", item.launch_intent.package);
|
||||
|
||||
if (!item.launch_intent.component.empty())
|
||||
exec += utils::string_format("--component=%s ", item.launch_intent.component);
|
||||
if (!item.launch_intent.component.empty())
|
||||
exec +=
|
||||
utils::string_format("--component=%s ", item.launch_intent.component);
|
||||
|
||||
std::ofstream f(item_path.string());
|
||||
f << "[Desktop Entry]" << std::endl
|
||||
<< "Name=" << item.package << std::endl
|
||||
<< "Exec=" << exec << std::endl
|
||||
<< "Terminal=false" << std::endl
|
||||
<< "Type=Application" << std::endl
|
||||
<< "Encoding=UTF-8" << std::endl;
|
||||
f.close();
|
||||
std::ofstream f(item_path.string());
|
||||
f << "[Desktop Entry]" << std::endl
|
||||
<< "Name=" << item.package << std::endl
|
||||
<< "Exec=" << exec << std::endl
|
||||
<< "Terminal=false" << std::endl
|
||||
<< "Type=Application" << std::endl
|
||||
<< "Encoding=UTF-8" << std::endl;
|
||||
f.close();
|
||||
}
|
||||
} // namespace application
|
||||
} // namespace anbox
|
||||
} // namespace application
|
||||
} // namespace anbox
|
||||
|
|
|
|||
|
|
@ -28,22 +28,22 @@
|
|||
namespace anbox {
|
||||
namespace application {
|
||||
class LauncherStorage {
|
||||
public:
|
||||
LauncherStorage(const boost::filesystem::path &path);
|
||||
~LauncherStorage();
|
||||
public:
|
||||
LauncherStorage(const boost::filesystem::path &path);
|
||||
~LauncherStorage();
|
||||
|
||||
struct Item {
|
||||
std::string name;
|
||||
std::string package;
|
||||
android::Intent launch_intent;
|
||||
};
|
||||
struct Item {
|
||||
std::string name;
|
||||
std::string package;
|
||||
android::Intent launch_intent;
|
||||
};
|
||||
|
||||
void add(const Item &item);
|
||||
void add(const Item &item);
|
||||
|
||||
private:
|
||||
boost::filesystem::path path_;
|
||||
private:
|
||||
boost::filesystem::path path_;
|
||||
};
|
||||
} // namespace application
|
||||
} // namespace anbox
|
||||
} // namespace application
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -18,16 +18,16 @@
|
|||
#ifndef ANBOX_APPLICATION_MANAGER_H_
|
||||
#define ANBOX_APPLICATION_MANAGER_H_
|
||||
|
||||
#include "anbox/do_not_copy_or_move.h"
|
||||
#include "anbox/android/intent.h"
|
||||
#include "anbox/do_not_copy_or_move.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace anbox {
|
||||
class ApplicationManager : public DoNotCopyOrMove {
|
||||
public:
|
||||
virtual void launch(const android::Intent &intent) = 0;
|
||||
public:
|
||||
virtual void launch(const android::Intent &intent) = 0;
|
||||
};
|
||||
} // namespace anbox
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -16,13 +16,13 @@
|
|||
*/
|
||||
|
||||
#include "anbox/bridge/android_api_stub.h"
|
||||
#include "anbox/rpc/channel.h"
|
||||
#include "anbox/utils.h"
|
||||
#include "anbox/config.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_rpc.pb.h"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
|
|
@ -30,101 +30,90 @@ namespace fs = boost::filesystem;
|
|||
|
||||
namespace anbox {
|
||||
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::set_rpc_channel(const std::shared_ptr<rpc::Channel> &channel) {
|
||||
channel_ = channel;
|
||||
}
|
||||
|
||||
void AndroidApiStub::reset_rpc_channel() {
|
||||
channel_.reset();
|
||||
}
|
||||
void AndroidApiStub::reset_rpc_channel() { channel_.reset(); }
|
||||
|
||||
void AndroidApiStub::ensure_rpc_channel() {
|
||||
if (!channel_)
|
||||
throw std::runtime_error("No remote client connected");
|
||||
if (!channel_) throw std::runtime_error("No remote client connected");
|
||||
}
|
||||
|
||||
void AndroidApiStub::launch(const android::Intent &intent) {
|
||||
ensure_rpc_channel();
|
||||
ensure_rpc_channel();
|
||||
|
||||
auto c = std::make_shared<Request<protobuf::rpc::Void>>();
|
||||
protobuf::bridge::LaunchApplication message;
|
||||
auto c = std::make_shared<Request<protobuf::rpc::Void>>();
|
||||
protobuf::bridge::LaunchApplication message;
|
||||
|
||||
{
|
||||
std::lock_guard<decltype(mutex_)> lock(mutex_);
|
||||
launch_wait_handle_.expect_result();
|
||||
}
|
||||
{
|
||||
std::lock_guard<decltype(mutex_)> lock(mutex_);
|
||||
launch_wait_handle_.expect_result();
|
||||
}
|
||||
|
||||
auto launch_intent = message.mutable_intent();
|
||||
auto launch_intent = message.mutable_intent();
|
||||
|
||||
if (!intent.action.empty())
|
||||
launch_intent->set_action(intent.action);
|
||||
if (!intent.action.empty()) launch_intent->set_action(intent.action);
|
||||
|
||||
if (!intent.uri.empty())
|
||||
launch_intent->set_uri(intent.uri);
|
||||
if (!intent.uri.empty()) launch_intent->set_uri(intent.uri);
|
||||
|
||||
if (!intent.type.empty())
|
||||
launch_intent->set_type(intent.type);
|
||||
if (!intent.type.empty()) launch_intent->set_type(intent.type);
|
||||
|
||||
if (!intent.package.empty())
|
||||
launch_intent->set_package(intent.package);
|
||||
if (!intent.package.empty()) launch_intent->set_package(intent.package);
|
||||
|
||||
if (!intent.component.empty())
|
||||
launch_intent->set_component(intent.component);
|
||||
if (!intent.component.empty()) launch_intent->set_component(intent.component);
|
||||
|
||||
for (const auto &category : intent.categories) {
|
||||
auto c = launch_intent->add_categories();
|
||||
*c = category;
|
||||
}
|
||||
for (const auto &category : intent.categories) {
|
||||
auto c = launch_intent->add_categories();
|
||||
*c = category;
|
||||
}
|
||||
|
||||
channel_->call_method("launch_application",
|
||||
&message,
|
||||
c->response.get(),
|
||||
google::protobuf::NewCallback(this, &AndroidApiStub::application_launched, c.get()));
|
||||
channel_->call_method(
|
||||
"launch_application", &message, c->response.get(),
|
||||
google::protobuf::NewCallback(this, &AndroidApiStub::application_launched,
|
||||
c.get()));
|
||||
|
||||
launch_wait_handle_.wait_for_all();
|
||||
launch_wait_handle_.wait_for_all();
|
||||
|
||||
if (c->response->has_error())
|
||||
throw std::runtime_error(c->response->error());
|
||||
if (c->response->has_error()) throw std::runtime_error(c->response->error());
|
||||
}
|
||||
|
||||
void AndroidApiStub::application_launched(Request<protobuf::rpc::Void> *request) {
|
||||
(void) request;
|
||||
launch_wait_handle_.result_received();
|
||||
void AndroidApiStub::application_launched(
|
||||
Request<protobuf::rpc::Void> *request) {
|
||||
(void)request;
|
||||
launch_wait_handle_.result_received();
|
||||
}
|
||||
|
||||
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;
|
||||
message.set_id(id);
|
||||
protobuf::bridge::SetFocusedTask message;
|
||||
message.set_id(id);
|
||||
|
||||
{
|
||||
std::lock_guard<decltype(mutex_)> lock(mutex_);
|
||||
set_focused_task_handle_.expect_result();
|
||||
}
|
||||
{
|
||||
std::lock_guard<decltype(mutex_)> lock(mutex_);
|
||||
set_focused_task_handle_.expect_result();
|
||||
}
|
||||
|
||||
channel_->call_method("set_focused_task",
|
||||
&message,
|
||||
c->response.get(),
|
||||
google::protobuf::NewCallback(this, &AndroidApiStub::focused_task_set, c.get()));
|
||||
channel_->call_method("set_focused_task", &message, c->response.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())
|
||||
throw std::runtime_error(c->response->error());
|
||||
if (c->response->has_error()) throw std::runtime_error(c->response->error());
|
||||
}
|
||||
|
||||
void AndroidApiStub::focused_task_set(Request<protobuf::rpc::Void> *request) {
|
||||
(void) request;
|
||||
set_focused_task_handle_.result_received();
|
||||
(void)request;
|
||||
set_focused_task_handle_.result_received();
|
||||
}
|
||||
} // namespace bridge
|
||||
} // namespace anbox
|
||||
} // namespace bridge
|
||||
} // namespace anbox
|
||||
|
|
|
|||
|
|
@ -28,43 +28,43 @@ namespace anbox {
|
|||
namespace protobuf {
|
||||
namespace rpc {
|
||||
class Void;
|
||||
} // namespace bridge
|
||||
} // namespace protobuf
|
||||
} // namespace bridge
|
||||
} // namespace protobuf
|
||||
namespace rpc {
|
||||
class Channel;
|
||||
} // namespace rpc
|
||||
} // namespace rpc
|
||||
namespace bridge {
|
||||
class AndroidApiStub : public anbox::ApplicationManager {
|
||||
public:
|
||||
AndroidApiStub();
|
||||
~AndroidApiStub();
|
||||
public:
|
||||
AndroidApiStub();
|
||||
~AndroidApiStub();
|
||||
|
||||
void set_rpc_channel(const std::shared_ptr<rpc::Channel> &channel);
|
||||
void reset_rpc_channel();
|
||||
void set_rpc_channel(const std::shared_ptr<rpc::Channel> &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:
|
||||
void ensure_rpc_channel();
|
||||
private:
|
||||
void ensure_rpc_channel();
|
||||
|
||||
template<typename Response>
|
||||
struct Request {
|
||||
Request() : response(std::make_shared<Response>()), success(true) { }
|
||||
std::shared_ptr<Response> response;
|
||||
bool success;
|
||||
};
|
||||
template <typename Response>
|
||||
struct Request {
|
||||
Request() : response(std::make_shared<Response>()), success(true) {}
|
||||
std::shared_ptr<Response> response;
|
||||
bool success;
|
||||
};
|
||||
|
||||
void application_launched(Request<protobuf::rpc::Void> *request);
|
||||
void focused_task_set(Request<protobuf::rpc::Void> *request);
|
||||
void application_launched(Request<protobuf::rpc::Void> *request);
|
||||
void focused_task_set(Request<protobuf::rpc::Void> *request);
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
std::shared_ptr<rpc::Channel> channel_;
|
||||
common::WaitHandle launch_wait_handle_;
|
||||
common::WaitHandle set_focused_task_handle_;
|
||||
mutable std::mutex mutex_;
|
||||
std::shared_ptr<rpc::Channel> channel_;
|
||||
common::WaitHandle launch_wait_handle_;
|
||||
common::WaitHandle set_focused_task_handle_;
|
||||
};
|
||||
} // namespace bridge
|
||||
} // namespace anbox
|
||||
} // namespace bridge
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -17,83 +17,86 @@
|
|||
|
||||
#include "anbox/bridge/platform_api_skeleton.h"
|
||||
#include "anbox/application/launcher_storage.h"
|
||||
#include "anbox/logger.h"
|
||||
#include "anbox/wm/manager.h"
|
||||
#include "anbox/wm/window_state.h"
|
||||
#include "anbox/logger.h"
|
||||
|
||||
#include "anbox_bridge.pb.h"
|
||||
|
||||
namespace anbox {
|
||||
namespace bridge {
|
||||
PlatformApiSkeleton::PlatformApiSkeleton(const std::shared_ptr<rpc::PendingCallCache> &pending_calls,
|
||||
const std::shared_ptr<wm::Manager> &window_manager,
|
||||
const std::shared_ptr<application::LauncherStorage> &launcher_storage) :
|
||||
pending_calls_(pending_calls),
|
||||
window_manager_(window_manager),
|
||||
launcher_storage_(launcher_storage) {
|
||||
PlatformApiSkeleton::PlatformApiSkeleton(
|
||||
const std::shared_ptr<rpc::PendingCallCache> &pending_calls,
|
||||
const std::shared_ptr<wm::Manager> &window_manager,
|
||||
const std::shared_ptr<application::LauncherStorage> &launcher_storage)
|
||||
: pending_calls_(pending_calls),
|
||||
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) event;
|
||||
void PlatformApiSkeleton::handle_application_list_update_event(
|
||||
const anbox::protobuf::bridge::ApplicationListUpdateEvent &event) {
|
||||
for (int n = 0; n < event.applications_size(); n++) {
|
||||
application::LauncherStorage::Item item;
|
||||
|
||||
if (boot_finished_handler_)
|
||||
boot_finished_handler_();
|
||||
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::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::register_boot_finished_handler(
|
||||
const std::function<void()> &action) {
|
||||
boot_finished_handler_ = action;
|
||||
}
|
||||
|
||||
void PlatformApiSkeleton::handle_application_list_update_event(const anbox::protobuf::bridge::ApplicationListUpdateEvent &event) {
|
||||
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
|
||||
} // namespace bridge
|
||||
} // namespace anbox
|
||||
|
|
|
|||
|
|
@ -23,50 +23,54 @@
|
|||
namespace google {
|
||||
namespace protobuf {
|
||||
class Closure;
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
||||
|
||||
namespace anbox {
|
||||
namespace protobuf {
|
||||
namespace rpc {
|
||||
class Void;
|
||||
} // namespace rpc
|
||||
} // namespace rpc
|
||||
namespace bridge {
|
||||
class BootFinishedEvent;
|
||||
class WindowStateUpdateEvent;
|
||||
class ApplicationListUpdateEvent;
|
||||
} // namespace bridge
|
||||
} // namespace protobuf
|
||||
} // namespace bridge
|
||||
} // namespace protobuf
|
||||
namespace rpc {
|
||||
class PendingCallCache;
|
||||
} // namespace rpc
|
||||
} // namespace rpc
|
||||
namespace wm {
|
||||
class Manager;
|
||||
} // namespace wm
|
||||
} // namespace wm
|
||||
namespace application {
|
||||
class LauncherStorage;
|
||||
} // namespace application
|
||||
} // namespace application
|
||||
namespace bridge {
|
||||
class PlatformApiSkeleton {
|
||||
public:
|
||||
PlatformApiSkeleton(const std::shared_ptr<rpc::PendingCallCache> &pending_calls,
|
||||
const std::shared_ptr<wm::Manager> &window_manager,
|
||||
const std::shared_ptr<application::LauncherStorage> &launcher_storage);
|
||||
virtual ~PlatformApiSkeleton();
|
||||
public:
|
||||
PlatformApiSkeleton(
|
||||
const std::shared_ptr<rpc::PendingCallCache> &pending_calls,
|
||||
const std::shared_ptr<wm::Manager> &window_manager,
|
||||
const std::shared_ptr<application::LauncherStorage> &launcher_storage);
|
||||
virtual ~PlatformApiSkeleton();
|
||||
|
||||
void handle_boot_finished_event(const anbox::protobuf::bridge::BootFinishedEvent &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 handle_boot_finished_event(
|
||||
const anbox::protobuf::bridge::BootFinishedEvent &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:
|
||||
std::shared_ptr<rpc::PendingCallCache> pending_calls_;
|
||||
std::shared_ptr<wm::Manager> window_manager_;
|
||||
std::shared_ptr<application::LauncherStorage> launcher_storage_;
|
||||
std::function<void()> boot_finished_handler_;
|
||||
private:
|
||||
std::shared_ptr<rpc::PendingCallCache> pending_calls_;
|
||||
std::shared_ptr<wm::Manager> window_manager_;
|
||||
std::shared_ptr<application::LauncherStorage> launcher_storage_;
|
||||
std::function<void()> boot_finished_handler_;
|
||||
};
|
||||
} // namespace bridge
|
||||
} // namespace anbox
|
||||
} // namespace bridge
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -17,41 +17,40 @@
|
|||
|
||||
#include "anbox/bridge/platform_message_processor.h"
|
||||
#include "anbox/bridge/platform_api_skeleton.h"
|
||||
#include "anbox/rpc/template_message_processor.h"
|
||||
#include "anbox/logger.h"
|
||||
#include "anbox/rpc/template_message_processor.h"
|
||||
|
||||
#include "anbox_bridge.pb.h"
|
||||
|
||||
namespace anbox {
|
||||
namespace bridge {
|
||||
PlatformMessageProcessor::PlatformMessageProcessor(const std::shared_ptr<network::MessageSender> &sender,
|
||||
const std::shared_ptr<PlatformApiSkeleton> &server,
|
||||
const std::shared_ptr<rpc::PendingCallCache> &pending_calls) :
|
||||
rpc::MessageProcessor(sender, pending_calls),
|
||||
server_(server) {
|
||||
PlatformMessageProcessor::PlatformMessageProcessor(
|
||||
const std::shared_ptr<network::MessageSender> &sender,
|
||||
const std::shared_ptr<PlatformApiSkeleton> &server,
|
||||
const std::shared_ptr<rpc::PendingCallCache> &pending_calls)
|
||||
: 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());
|
||||
}
|
||||
|
||||
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
|
||||
} // namespace network
|
||||
} // namespace anbox
|
||||
} // namespace network
|
||||
|
|
|
|||
|
|
@ -24,19 +24,20 @@ namespace anbox {
|
|||
namespace bridge {
|
||||
class PlatformApiSkeleton;
|
||||
class PlatformMessageProcessor : public rpc::MessageProcessor {
|
||||
public:
|
||||
PlatformMessageProcessor(const std::shared_ptr<network::MessageSender> &sender,
|
||||
const std::shared_ptr<PlatformApiSkeleton> &server,
|
||||
const std::shared_ptr<rpc::PendingCallCache> &pending_calls);
|
||||
~PlatformMessageProcessor();
|
||||
public:
|
||||
PlatformMessageProcessor(
|
||||
const std::shared_ptr<network::MessageSender> &sender,
|
||||
const std::shared_ptr<PlatformApiSkeleton> &server,
|
||||
const std::shared_ptr<rpc::PendingCallCache> &pending_calls);
|
||||
~PlatformMessageProcessor();
|
||||
|
||||
void dispatch(rpc::Invocation const& invocation) override;
|
||||
void process_event_sequence(const std::string &event) override;
|
||||
void dispatch(rpc::Invocation const &invocation) override;
|
||||
void process_event_sequence(const std::string &event) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<PlatformApiSkeleton> server_;
|
||||
private:
|
||||
std::shared_ptr<PlatformApiSkeleton> server_;
|
||||
};
|
||||
} // namespace anbox
|
||||
} // namespace network
|
||||
} // namespace anbox
|
||||
} // namespace network
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -28,258 +28,216 @@ namespace po = boost::program_options;
|
|||
namespace {
|
||||
namespace pattern {
|
||||
static constexpr const char* help_for_command_with_subcommands =
|
||||
"NAME:\n"
|
||||
" %1% - %2%\n"
|
||||
"\n"
|
||||
"USAGE:\n"
|
||||
" %3% [command options] [arguments...]";
|
||||
"NAME:\n"
|
||||
" %1% - %2%\n"
|
||||
"\n"
|
||||
"USAGE:\n"
|
||||
" %3% [command options] [arguments...]";
|
||||
|
||||
static constexpr const char* commands = "COMMANDS:";
|
||||
static constexpr const char* command = " %1% %2%";
|
||||
static constexpr const char* commands = "COMMANDS:";
|
||||
static constexpr const char* command = " %1% %2%";
|
||||
|
||||
static constexpr const char* options = "OPTIONS:";
|
||||
static constexpr const char* option = " --%1% %2%";
|
||||
static constexpr const char* options = "OPTIONS:";
|
||||
static constexpr const char* option = " --%1% %2%";
|
||||
}
|
||||
|
||||
void add_to_desc_for_flags(po::options_description& desc, const std::set<cli::Flag::Ptr>& flags)
|
||||
{
|
||||
for (auto flag : flags)
|
||||
{
|
||||
auto v = po::value<std::string>()->notifier([flag](const std::string& s)
|
||||
{
|
||||
flag->notify(s);
|
||||
});
|
||||
desc.add_options()(flag->name().as_string().c_str(), v, flag->description().as_string().c_str());
|
||||
}
|
||||
void add_to_desc_for_flags(po::options_description& desc,
|
||||
const std::set<cli::Flag::Ptr>& flags) {
|
||||
for (auto flag : flags) {
|
||||
auto v = po::value<std::string>()->notifier(
|
||||
[flag](const std::string& s) { flag->notify(s); });
|
||||
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> result;
|
||||
for (int i = 1; i < argc; i++) result.push_back(argv[i]);
|
||||
return result;
|
||||
std::vector<std::string> cli::args(int argc, char** argv) {
|
||||
std::vector<std::string> result;
|
||||
for (int i = 1; i < argc; i++) result.push_back(argv[i]);
|
||||
return result;
|
||||
}
|
||||
|
||||
const cli::Name& cli::Flag::name() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
const cli::Name& cli::Flag::name() const { return name_; }
|
||||
|
||||
const cli::Description& cli::Flag::description() const
|
||||
{
|
||||
return description_;
|
||||
}
|
||||
const cli::Description& cli::Flag::description() const { return description_; }
|
||||
|
||||
cli::Flag::Flag(const Name& name, const Description& description)
|
||||
: name_{name},
|
||||
description_{description}
|
||||
{
|
||||
: name_{name}, 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
|
||||
{
|
||||
return name_;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cli::Usage cli::Command::usage() const
|
||||
{
|
||||
return usage_;
|
||||
}
|
||||
int cli::CommandWithSubcommands::run(const cli::Command::Context& ctxt) {
|
||||
po::positional_options_description pdesc;
|
||||
pdesc.add("command", 1);
|
||||
|
||||
cli::Description cli::Command::description() const
|
||||
{
|
||||
return description_;
|
||||
}
|
||||
po::options_description desc("Options");
|
||||
desc.add_options()("command", po::value<std::string>()->required(),
|
||||
"the command to be executed");
|
||||
|
||||
cli::Command::Command(const cli::Name& name, const cli::Usage& usage, const cli::Description& description)
|
||||
: name_(name),
|
||||
usage_(usage),
|
||||
description_(description)
|
||||
{
|
||||
}
|
||||
add_to_desc_for_flags(desc, flags_);
|
||||
|
||||
cli::CommandWithSubcommands::CommandWithSubcommands(const Name& name, const Usage& usage, const Description& description)
|
||||
: Command{name, usage, description}
|
||||
{
|
||||
command(std::make_shared<cmd::Help>(*this));
|
||||
}
|
||||
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();
|
||||
|
||||
cli::CommandWithSubcommands& cli::CommandWithSubcommands::command(const Command::Ptr& command)
|
||||
{
|
||||
commands_[command->name().as_string()] = command;
|
||||
return *this;
|
||||
}
|
||||
po::store(parsed, vm);
|
||||
po::notify(vm);
|
||||
|
||||
cli::CommandWithSubcommands& cli::CommandWithSubcommands::flag(const Flag::Ptr& flag)
|
||||
{
|
||||
flags_.insert(flag);
|
||||
return *this;
|
||||
}
|
||||
|
||||
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;
|
||||
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 EXIT_FAILURE;
|
||||
}
|
||||
|
||||
cli::CommandWithFlagsAndAction::CommandWithFlagsAndAction(const Name& name, const Usage& usage, const Description& description)
|
||||
: Command{name, usage, description}
|
||||
{
|
||||
cli::CommandWithFlagsAndAction::CommandWithFlagsAndAction(
|
||||
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)
|
||||
{
|
||||
flags_.insert(flag);
|
||||
return *this;
|
||||
cli::CommandWithFlagsAndAction& cli::CommandWithFlagsAndAction::action(
|
||||
const Action& action) {
|
||||
action_ = action;
|
||||
return *this;
|
||||
}
|
||||
|
||||
cli::CommandWithFlagsAndAction& cli::CommandWithFlagsAndAction::action(const Action& action)
|
||||
{
|
||||
action_ = action;
|
||||
return *this;
|
||||
}
|
||||
int cli::CommandWithFlagsAndAction::run(const Context& ctxt) {
|
||||
po::options_description cd(name().as_string());
|
||||
|
||||
int cli::CommandWithFlagsAndAction::run(const Context& ctxt)
|
||||
{
|
||||
po::options_description cd(name().as_string());
|
||||
bool help_requested{false};
|
||||
cd.add_options()("help", po::bool_switch(&help_requested),
|
||||
"produces a help message");
|
||||
|
||||
bool help_requested{false};
|
||||
cd.add_options()("help", po::bool_switch(&help_requested), "produces a help message");
|
||||
add_to_desc_for_flags(cd, flags_);
|
||||
|
||||
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
|
||||
{
|
||||
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);
|
||||
|
||||
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;
|
||||
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 EXIT_FAILURE;
|
||||
}
|
||||
|
||||
void cli::CommandWithFlagsAndAction::help(std::ostream& out)
|
||||
{
|
||||
out << boost::format(pattern::help_for_command_with_subcommands)
|
||||
% name().as_string() % description().as_string()
|
||||
% name().as_string() << std::endl;
|
||||
void cli::CommandWithFlagsAndAction::help(std::ostream& out) {
|
||||
out << boost::format(pattern::help_for_command_with_subcommands) %
|
||||
name().as_string() % description().as_string() % name().as_string()
|
||||
<< std::endl;
|
||||
|
||||
if (flags_.size() > 0)
|
||||
{
|
||||
out << std::endl << boost::format(pattern::options) << std::endl;
|
||||
for (const auto& flag : flags_)
|
||||
out << boost::format(pattern::option) % flag->name() % flag->description() << std::endl;
|
||||
}
|
||||
if (flags_.size() > 0) {
|
||||
out << std::endl << boost::format(pattern::options) << std::endl;
|
||||
for (const auto& flag : flags_)
|
||||
out << boost::format(pattern::option) % flag->name() % flag->description()
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
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{cmd}
|
||||
{
|
||||
}
|
||||
: Command{cli::Name{"help"}, cli::Usage{"prints a short help message"},
|
||||
cli::Description{"prints a short help message"}},
|
||||
command{cmd} {}
|
||||
|
||||
// From Command
|
||||
int cli::cmd::Help::run(const Context &context)
|
||||
{
|
||||
command.help(context.cout);
|
||||
return EXIT_FAILURE;
|
||||
int cli::cmd::Help::run(const Context& context) {
|
||||
command.help(context.cout);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
void cli::cmd::Help::help(std::ostream &out)
|
||||
{
|
||||
command.help(out);
|
||||
}
|
||||
void cli::cmd::Help::help(std::ostream& out) { command.help(out); }
|
||||
|
|
|
|||
455
src/anbox/cli.h
455
src/anbox/cli.h
|
|
@ -34,46 +34,38 @@
|
|||
namespace anbox {
|
||||
namespace cli {
|
||||
|
||||
template<std::size_t max>
|
||||
class SizeConstrainedString
|
||||
{
|
||||
public:
|
||||
SizeConstrainedString(const std::string& s) : s{s}
|
||||
{
|
||||
if(s.size() > max)
|
||||
throw std::logic_error{"Max size exceeded " + std::to_string(max)};
|
||||
}
|
||||
template <std::size_t max>
|
||||
class SizeConstrainedString {
|
||||
public:
|
||||
SizeConstrainedString(const std::string& s) : s{s} {
|
||||
if (s.size() > max)
|
||||
throw std::logic_error{"Max size exceeded " + std::to_string(max)};
|
||||
}
|
||||
|
||||
const std::string& as_string() const
|
||||
{
|
||||
return s;
|
||||
}
|
||||
const std::string& as_string() const { return s; }
|
||||
|
||||
operator std::string() const
|
||||
{
|
||||
return s;
|
||||
}
|
||||
operator std::string() const { return s; }
|
||||
|
||||
private:
|
||||
std::string s;
|
||||
private:
|
||||
std::string s;
|
||||
};
|
||||
|
||||
template<std::size_t max>
|
||||
bool operator<(const SizeConstrainedString<max>& lhs, const SizeConstrainedString<max>& rhs)
|
||||
{
|
||||
return lhs.as_string() < rhs.as_string();
|
||||
template <std::size_t max>
|
||||
bool operator<(const SizeConstrainedString<max>& lhs,
|
||||
const SizeConstrainedString<max>& rhs) {
|
||||
return lhs.as_string() < rhs.as_string();
|
||||
}
|
||||
|
||||
template<std::size_t max>
|
||||
bool operator==(const SizeConstrainedString<max>& lhs, const SizeConstrainedString<max>& rhs)
|
||||
{
|
||||
return lhs.as_string() == rhs.as_string();
|
||||
template <std::size_t max>
|
||||
bool operator==(const SizeConstrainedString<max>& lhs,
|
||||
const SizeConstrainedString<max>& rhs) {
|
||||
return lhs.as_string() == rhs.as_string();
|
||||
}
|
||||
|
||||
template<std::size_t max>
|
||||
std::ostream& operator<<(std::ostream& out, const SizeConstrainedString<max>& scs)
|
||||
{
|
||||
return out << std::setw(max) << std::left << scs.as_string();
|
||||
template <std::size_t max>
|
||||
std::ostream& operator<<(std::ostream& out,
|
||||
const SizeConstrainedString<max>& scs) {
|
||||
return out << std::setw(max) << std::left << scs.as_string();
|
||||
}
|
||||
|
||||
// We are imposing size constraints to ensure a consistent CLI layout.
|
||||
|
|
@ -82,246 +74,238 @@ typedef SizeConstrainedString<60> Usage;
|
|||
typedef SizeConstrainedString<60> Description;
|
||||
|
||||
/// @brief Flag models an input parameter to a command.
|
||||
class Flag : public DoNotCopyOrMove
|
||||
{
|
||||
public:
|
||||
// Safe us some typing.
|
||||
typedef std::shared_ptr<Flag> Ptr;
|
||||
class Flag : public DoNotCopyOrMove {
|
||||
public:
|
||||
// Safe us some typing.
|
||||
typedef std::shared_ptr<Flag> Ptr;
|
||||
|
||||
/// @brief notify announces a new value to the flag.
|
||||
virtual void notify(const std::string& value) = 0;
|
||||
/// @brief name returns the name of the Flag.
|
||||
const Name& name() const;
|
||||
/// @brief description returns a human-readable description of the flag.
|
||||
const Description& description() const;
|
||||
/// @brief notify announces a new value to the flag.
|
||||
virtual void notify(const std::string& value) = 0;
|
||||
/// @brief name returns the name of the Flag.
|
||||
const Name& name() const;
|
||||
/// @brief description returns a human-readable description of the flag.
|
||||
const Description& description() const;
|
||||
|
||||
protected:
|
||||
/// @brief Flag creates a new instance, initializing name and description
|
||||
/// from the given values.
|
||||
Flag(const Name& name, const Description& description);
|
||||
protected:
|
||||
/// @brief Flag creates a new instance, initializing name and description
|
||||
/// from the given values.
|
||||
Flag(const Name& name, const Description& description);
|
||||
|
||||
private:
|
||||
Name name_;
|
||||
Description description_;
|
||||
private:
|
||||
Name name_;
|
||||
Description description_;
|
||||
};
|
||||
|
||||
/// @brief TypedFlag implements Flag relying on operator<< and operator>> to read/write values to/from strings.
|
||||
template<typename T>
|
||||
class TypedFlag : public Flag
|
||||
{
|
||||
public:
|
||||
typedef std::shared_ptr<TypedFlag<T>> Ptr;
|
||||
/// @brief TypedFlag implements Flag relying on operator<< and operator>> to
|
||||
/// read/write values to/from strings.
|
||||
template <typename T>
|
||||
class TypedFlag : public Flag {
|
||||
public:
|
||||
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.
|
||||
TypedFlag& value(const T& value)
|
||||
{
|
||||
value_ = value;
|
||||
return *this;
|
||||
}
|
||||
/// @brief value installs the given value in the flag.
|
||||
TypedFlag& value(const T& value) {
|
||||
value_ = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @brief value returns the optional value associated with the flag.
|
||||
const Optional<T>& value() const
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
/// @brief value returns the optional value associated with the flag.
|
||||
const Optional<T>& value() const { return value_; }
|
||||
|
||||
/// @brief notify tries to unwrap a value of type T from value.
|
||||
void notify(const std::string& s) override
|
||||
{
|
||||
std::stringstream ss{s};
|
||||
T value; ss >> value;
|
||||
value_ = value;
|
||||
}
|
||||
/// @brief notify tries to unwrap a value of type T from value.
|
||||
void notify(const std::string& s) override {
|
||||
std::stringstream ss{s};
|
||||
T value;
|
||||
ss >> value;
|
||||
value_ = value;
|
||||
}
|
||||
|
||||
private:
|
||||
Optional<T> value_;
|
||||
private:
|
||||
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.
|
||||
template<typename T>
|
||||
class TypedReferenceFlag : public Flag
|
||||
{
|
||||
public:
|
||||
// Safe us some typing.
|
||||
typedef std::shared_ptr<TypedReferenceFlag<T>> Ptr;
|
||||
template <typename T>
|
||||
class TypedReferenceFlag : public Flag {
|
||||
public:
|
||||
// Safe us some typing.
|
||||
typedef std::shared_ptr<TypedReferenceFlag<T>> Ptr;
|
||||
|
||||
/// @brief TypedReferenceFlag initializes a new instance with name, description and value.
|
||||
TypedReferenceFlag(const Name& name, const Description& description, T& value)
|
||||
: Flag{name, description},
|
||||
value_{value}
|
||||
{
|
||||
}
|
||||
/// @brief TypedReferenceFlag initializes a new instance with name,
|
||||
/// description and value.
|
||||
TypedReferenceFlag(const Name& name, const Description& description, T& value)
|
||||
: Flag{name, description}, value_{value} {}
|
||||
|
||||
/// @brief notify tries to unwrap a value of type T from value,
|
||||
/// relying on operator>> to read from given string s.
|
||||
void notify(const std::string& s) override
|
||||
{
|
||||
std::stringstream ss{s};
|
||||
ss >> value_.get();
|
||||
}
|
||||
/// @brief notify tries to unwrap a value of type T from value,
|
||||
/// relying on operator>> to read from given string s.
|
||||
void notify(const std::string& s) override {
|
||||
std::stringstream ss{s};
|
||||
ss >> value_.get();
|
||||
}
|
||||
|
||||
private:
|
||||
std::reference_wrapper<T> value_;
|
||||
private:
|
||||
std::reference_wrapper<T> value_;
|
||||
};
|
||||
|
||||
/// @brief OptionalTypedReferenceFlag handles Optional<T> references, making sure that
|
||||
/// a value is always read on notify, even if the Optional<T> wasn't initialized previously.
|
||||
template<typename T>
|
||||
class OptionalTypedReferenceFlag : public Flag
|
||||
{
|
||||
public:
|
||||
typedef std::shared_ptr<OptionalTypedReferenceFlag<T>> Ptr;
|
||||
/// @brief OptionalTypedReferenceFlag handles Optional<T> references, making
|
||||
/// sure that
|
||||
/// a value is always read on notify, even if the Optional<T> wasn't initialized
|
||||
/// previously.
|
||||
template <typename T>
|
||||
class OptionalTypedReferenceFlag : public Flag {
|
||||
public:
|
||||
typedef std::shared_ptr<OptionalTypedReferenceFlag<T>> Ptr;
|
||||
|
||||
OptionalTypedReferenceFlag(const Name& name, const Description& description, Optional<T>& value)
|
||||
: Flag{name, description},
|
||||
value_{value}
|
||||
{
|
||||
}
|
||||
OptionalTypedReferenceFlag(const Name& name, const Description& description,
|
||||
Optional<T>& value)
|
||||
: Flag{name, description}, value_{value} {}
|
||||
|
||||
/// @brief notify tries to unwrap a value of type T from value.
|
||||
void notify(const std::string& s) override
|
||||
{
|
||||
std::stringstream ss{s}; T value; ss >> value;
|
||||
value_.get() = value;
|
||||
}
|
||||
/// @brief notify tries to unwrap a value of type T from value.
|
||||
void notify(const std::string& s) override {
|
||||
std::stringstream ss{s};
|
||||
T value;
|
||||
ss >> value;
|
||||
value_.get() = value;
|
||||
}
|
||||
|
||||
private:
|
||||
std::reference_wrapper<Optional<T>> value_;
|
||||
private:
|
||||
std::reference_wrapper<Optional<T>> value_;
|
||||
};
|
||||
|
||||
/// @brief Command abstracts an individual command available from the daemon.
|
||||
class Command : public DoNotCopyOrMove
|
||||
{
|
||||
public:
|
||||
// Safe us some typing
|
||||
typedef std::shared_ptr<Command> Ptr;
|
||||
class Command : public DoNotCopyOrMove {
|
||||
public:
|
||||
// Safe us some typing
|
||||
typedef std::shared_ptr<Command> Ptr;
|
||||
|
||||
/// @brief FlagsMissing is thrown if at least one required flag is missing.
|
||||
struct FlagsMissing : public std::runtime_error
|
||||
{
|
||||
/// @brief FlagsMissing initializes a new instance.
|
||||
FlagsMissing();
|
||||
};
|
||||
/// @brief FlagsMissing is thrown if at least one required flag is missing.
|
||||
struct FlagsMissing : public std::runtime_error {
|
||||
/// @brief FlagsMissing initializes a new instance.
|
||||
FlagsMissing();
|
||||
};
|
||||
|
||||
/// @brief FlagsWithWrongValue is thrown if a value passed on the command line is invalid.
|
||||
struct FlagsWithInvalidValue : public std::runtime_error
|
||||
{
|
||||
/// @brief FlagsWithInvalidValue initializes a new instance.
|
||||
FlagsWithInvalidValue();
|
||||
};
|
||||
/// @brief FlagsWithWrongValue is thrown if a value passed on the command line
|
||||
/// is invalid.
|
||||
struct FlagsWithInvalidValue : public std::runtime_error {
|
||||
/// @brief FlagsWithInvalidValue initializes a new instance.
|
||||
FlagsWithInvalidValue();
|
||||
};
|
||||
|
||||
/// @brief Context bundles information passed to Command::run invocations.
|
||||
struct Context
|
||||
{
|
||||
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::vector<std::string> args; ///< The command line args.
|
||||
};
|
||||
/// @brief Context bundles information passed to Command::run invocations.
|
||||
struct Context {
|
||||
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::vector<std::string> args; ///< The command line args.
|
||||
};
|
||||
|
||||
/// @brief name returns the Name of the command.
|
||||
virtual Name name() const;
|
||||
/// @brief name returns the Name of the command.
|
||||
virtual Name name() const;
|
||||
|
||||
/// @brief usage returns a short usage string for the command.
|
||||
virtual Usage usage() const;
|
||||
/// @brief usage returns a short usage string for the command.
|
||||
virtual Usage usage() const;
|
||||
|
||||
/// @brief description returns a longer string explaining the command.
|
||||
virtual Description description() const;
|
||||
/// @brief description returns a longer string explaining the command.
|
||||
virtual Description description() const;
|
||||
|
||||
/// @brief run puts the command to execution.
|
||||
virtual int run(const Context& context) = 0;
|
||||
/// @brief run puts the command to execution.
|
||||
virtual int run(const Context& context) = 0;
|
||||
|
||||
/// @brief help prints information about a command to out.
|
||||
virtual void help(std::ostream& out) = 0;
|
||||
/// @brief help prints information about a command to out.
|
||||
virtual void help(std::ostream& out) = 0;
|
||||
|
||||
protected:
|
||||
/// @brief Command initializes a new instance with the given name, usage and description.
|
||||
Command(const Name& name, const Usage& usage, const Description& description);
|
||||
protected:
|
||||
/// @brief Command initializes a new instance with the given name, usage and
|
||||
/// description.
|
||||
Command(const Name& name, const Usage& usage, const Description& description);
|
||||
|
||||
/// @brief name adjusts the name of the command to n.
|
||||
// virtual void name(const Name& n);
|
||||
/// @brief usage adjusts the usage string of the comand to u.
|
||||
// virtual void usage(const Usage& u);
|
||||
/// @brief description adjusts the description string of the command to d.
|
||||
// virtual void description(const Description& d);
|
||||
/// @brief name adjusts the name of the command to n.
|
||||
// virtual void name(const Name& n);
|
||||
/// @brief usage adjusts the usage string of the comand to u.
|
||||
// virtual void usage(const Usage& u);
|
||||
/// @brief description adjusts the description string of the command to d.
|
||||
// virtual void description(const Description& d);
|
||||
|
||||
private:
|
||||
Name name_;
|
||||
Usage usage_;
|
||||
Description description_;
|
||||
private:
|
||||
Name name_;
|
||||
Usage usage_;
|
||||
Description description_;
|
||||
};
|
||||
|
||||
/// @brief CommandWithSubcommands implements Command, selecting one of a set of actions.
|
||||
class CommandWithSubcommands : public Command
|
||||
{
|
||||
public:
|
||||
typedef std::shared_ptr<CommandWithSubcommands> Ptr;
|
||||
typedef std::function<int(const Context&)> Action;
|
||||
/// @brief CommandWithSubcommands implements Command, selecting one of a set of
|
||||
/// actions.
|
||||
class CommandWithSubcommands : public Command {
|
||||
public:
|
||||
typedef std::shared_ptr<CommandWithSubcommands> Ptr;
|
||||
typedef std::function<int(const Context&)> Action;
|
||||
|
||||
/// @brief CommandWithSubcommands initializes a new instance with the given name, usage and description
|
||||
CommandWithSubcommands(const Name& name, const Usage& usage, const Description& description);
|
||||
/// @brief CommandWithSubcommands initializes a new instance with the given
|
||||
/// 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.
|
||||
CommandWithSubcommands& command(const Command::Ptr& command);
|
||||
/// @brief command adds the given command to the set of known commands.
|
||||
CommandWithSubcommands& command(const Command::Ptr& command);
|
||||
|
||||
/// @brief flag adds the given flag to the set of known flags.
|
||||
CommandWithSubcommands& flag(const Flag::Ptr& flag);
|
||||
/// @brief flag adds the given flag to the set of known flags.
|
||||
CommandWithSubcommands& flag(const Flag::Ptr& flag);
|
||||
|
||||
// From Command
|
||||
int run(const Context& context) override;
|
||||
void help(std::ostream &out) override;
|
||||
// From Command
|
||||
int run(const Context& context) override;
|
||||
void help(std::ostream& out) override;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, Command::Ptr> commands_;
|
||||
std::set<Flag::Ptr> flags_;
|
||||
private:
|
||||
std::unordered_map<std::string, Command::Ptr> commands_;
|
||||
std::set<Flag::Ptr> flags_;
|
||||
};
|
||||
|
||||
/// @brief CommandWithFlagsAction implements Command, executing an Action after handling
|
||||
class CommandWithFlagsAndAction : public Command
|
||||
{
|
||||
public:
|
||||
typedef std::shared_ptr<CommandWithFlagsAndAction> Ptr;
|
||||
typedef std::function<int(const Context&)> Action;
|
||||
/// @brief CommandWithFlagsAction implements Command, executing an Action after
|
||||
/// handling
|
||||
class CommandWithFlagsAndAction : public Command {
|
||||
public:
|
||||
typedef std::shared_ptr<CommandWithFlagsAndAction> Ptr;
|
||||
typedef std::function<int(const Context&)> Action;
|
||||
|
||||
/// @brief CommandWithFlagsAndAction initializes a new instance with the given name, usage and description
|
||||
CommandWithFlagsAndAction(const Name& name, const Usage& usage, const Description& description);
|
||||
/// @brief CommandWithFlagsAndAction initializes a new instance with the given
|
||||
/// 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.
|
||||
CommandWithFlagsAndAction& flag(const Flag::Ptr& flag);
|
||||
/// @brief flag adds the given flag to the set of known flags.
|
||||
CommandWithFlagsAndAction& flag(const Flag::Ptr& flag);
|
||||
|
||||
/// @brief action installs the given action.
|
||||
CommandWithFlagsAndAction& action(const Action& action);
|
||||
/// @brief action installs the given action.
|
||||
CommandWithFlagsAndAction& action(const Action& action);
|
||||
|
||||
// From Command
|
||||
int run(const Context& context) override;
|
||||
void help(std::ostream &out) override;
|
||||
// From Command
|
||||
int run(const Context& context) override;
|
||||
void help(std::ostream& out) override;
|
||||
|
||||
private:
|
||||
std::set<Flag::Ptr> flags_;
|
||||
Action action_;
|
||||
private:
|
||||
std::set<Flag::Ptr> flags_;
|
||||
Action action_;
|
||||
};
|
||||
|
||||
namespace cmd
|
||||
{
|
||||
namespace cmd {
|
||||
/// @brief HelpFor prints a help message for the given command on execution.
|
||||
class Help : public Command
|
||||
{
|
||||
public:
|
||||
/// @brief HelpFor initializes a new instance with the given reference to a cmd.
|
||||
explicit Help(Command& cmd);
|
||||
class Help : public Command {
|
||||
public:
|
||||
/// @brief HelpFor initializes a new instance with the given reference to a
|
||||
/// cmd.
|
||||
explicit Help(Command& cmd);
|
||||
|
||||
// From Command
|
||||
int run(const Context &context) override;
|
||||
void help(std::ostream &out) override;
|
||||
// From Command
|
||||
int run(const Context& context) override;
|
||||
void help(std::ostream& out) override;
|
||||
|
||||
private:
|
||||
/// @cond
|
||||
Command& command;
|
||||
/// @endcond
|
||||
private:
|
||||
/// @cond
|
||||
Command& command;
|
||||
/// @endcond
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -329,28 +313,31 @@ private:
|
|||
std::vector<std::string> args(int argc, char** argv);
|
||||
|
||||
/// @brief make_flag returns a flag with the given name and description.
|
||||
template<typename T>
|
||||
typename TypedFlag<T>::Ptr make_flag(const Name& name, const Description& description)
|
||||
{
|
||||
return std::make_shared<TypedFlag<T>>(name, description);
|
||||
template <typename T>
|
||||
typename TypedFlag<T>::Ptr make_flag(const Name& name,
|
||||
const Description& 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.
|
||||
template<typename T>
|
||||
typename TypedReferenceFlag<T>::Ptr make_flag(const Name& name, 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,
|
||||
/// notifying updates to value.
|
||||
template <typename T>
|
||||
typename TypedReferenceFlag<T>::Ptr make_flag(const Name& name,
|
||||
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.
|
||||
template<typename T>
|
||||
typename OptionalTypedReferenceFlag<T>::Ptr make_flag(const Name& name, const Description& desc, Optional<T>& value)
|
||||
{
|
||||
return std::make_shared<OptionalTypedReferenceFlag<T>>(name, desc, value);
|
||||
/// @brief make_flag returns a flag with the given name and description,
|
||||
/// updating the given optional value.
|
||||
template <typename T>
|
||||
typename OptionalTypedReferenceFlag<T>::Ptr make_flag(const Name& name,
|
||||
const Description& desc,
|
||||
Optional<T>& value) {
|
||||
return std::make_shared<OptionalTypedReferenceFlag<T>>(name, desc, value);
|
||||
}
|
||||
|
||||
} // namespace cli
|
||||
} // namespace anbox
|
||||
|
||||
} // namespace cli
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -17,29 +17,30 @@
|
|||
|
||||
#include "anbox/cmds/container_manager.h"
|
||||
#include "anbox/container/service.h"
|
||||
#include "anbox/runtime.h"
|
||||
#include "anbox/logger.h"
|
||||
#include "anbox/runtime.h"
|
||||
|
||||
#include "core/posix/signal.h"
|
||||
|
||||
anbox::cmds::ContainerManager::ContainerManager()
|
||||
: CommandWithFlagsAndAction{cli::Name{"container-manager"}, cli::Usage{"container-manager"}, cli::Description{"Start the container manager service"}}
|
||||
{
|
||||
action([](const cli::Command::Context& ctxt) {
|
||||
auto trap = core::posix::trap_signals_for_process({core::posix::Signal::sig_term,
|
||||
core::posix::Signal::sig_int});
|
||||
trap->signal_raised().connect([trap](const core::posix::Signal &signal) {
|
||||
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;
|
||||
: CommandWithFlagsAndAction{
|
||||
cli::Name{"container-manager"}, cli::Usage{"container-manager"},
|
||||
cli::Description{"Start the container manager service"}} {
|
||||
action([](const cli::Command::Context& ctxt) {
|
||||
auto trap = core::posix::trap_signals_for_process(
|
||||
{core::posix::Signal::sig_term, core::posix::Signal::sig_int});
|
||||
trap->signal_raised().connect([trap](const core::posix::Signal& signal) {
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,10 +27,10 @@
|
|||
namespace anbox {
|
||||
namespace cmds {
|
||||
class ContainerManager : public cli::CommandWithFlagsAndAction {
|
||||
public:
|
||||
ContainerManager();
|
||||
public:
|
||||
ContainerManager();
|
||||
};
|
||||
} // namespace cmds
|
||||
} // namespace anbox
|
||||
} // namespace cmds
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -25,22 +25,27 @@
|
|||
namespace fs = boost::filesystem;
|
||||
|
||||
anbox::cmds::Install::Install()
|
||||
: CommandWithFlagsAndAction{cli::Name{"install"}, cli::Usage{"install"}, cli::Description{"Install specified application in the Android container"}}
|
||||
{
|
||||
flag(cli::make_flag(cli::Name{"apk"}, 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"));
|
||||
: CommandWithFlagsAndAction{
|
||||
cli::Name{"install"}, cli::Usage{"install"},
|
||||
cli::Description{
|
||||
"Install specified application in the Android container"}} {
|
||||
flag(cli::make_flag(cli::Name{"apk"},
|
||||
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_))
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error("Specified APK file does not exist"));
|
||||
if (!fs::is_regular_file(apk_))
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::runtime_error("Specified APK file does not exist"));
|
||||
|
||||
auto bus = std::make_shared<core::dbus::Bus>(core::dbus::WellKnownBus::session);
|
||||
bus->install_executor(core::dbus::asio::make_executor(bus));
|
||||
auto stub = dbus::stub::ApplicationManager::create_for_bus(bus);
|
||||
auto bus =
|
||||
std::make_shared<core::dbus::Bus>(core::dbus::WellKnownBus::session);
|
||||
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 cmds {
|
||||
class Install : public cli::CommandWithFlagsAndAction {
|
||||
public:
|
||||
Install();
|
||||
public:
|
||||
Install();
|
||||
|
||||
private:
|
||||
std::string apk_;
|
||||
private:
|
||||
std::string apk_;
|
||||
};
|
||||
} // namespace cmds
|
||||
} // namespace anbox
|
||||
} // namespace cmds
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -25,21 +25,34 @@
|
|||
namespace fs = boost::filesystem;
|
||||
|
||||
anbox::cmds::Launch::Launch()
|
||||
: CommandWithFlagsAndAction{cli::Name{"launch"}, cli::Usage{"launch"}, cli::Description{"Launch an Activity by sending an intent"}}
|
||||
{
|
||||
flag(cli::make_flag(cli::Name{"action"}, cli::Description{"Action of the intent"}, intent_.action));
|
||||
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));
|
||||
: CommandWithFlagsAndAction{
|
||||
cli::Name{"launch"}, cli::Usage{"launch"},
|
||||
cli::Description{"Launch an Activity by sending an intent"}} {
|
||||
flag(cli::make_flag(cli::Name{"action"},
|
||||
cli::Description{"Action of the intent"},
|
||||
intent_.action));
|
||||
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&) {
|
||||
auto bus = std::make_shared<core::dbus::Bus>(core::dbus::WellKnownBus::session);
|
||||
bus->install_executor(core::dbus::asio::make_executor(bus));
|
||||
auto stub = dbus::stub::ApplicationManager::create_for_bus(bus);
|
||||
action([this](const cli::Command::Context&) {
|
||||
auto bus =
|
||||
std::make_shared<core::dbus::Bus>(core::dbus::WellKnownBus::session);
|
||||
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 <memory>
|
||||
|
||||
#include "anbox/cli.h"
|
||||
#include "anbox/android/intent.h"
|
||||
#include "anbox/cli.h"
|
||||
|
||||
namespace anbox {
|
||||
namespace cmds {
|
||||
class Launch : public cli::CommandWithFlagsAndAction {
|
||||
public:
|
||||
Launch();
|
||||
public:
|
||||
Launch();
|
||||
|
||||
private:
|
||||
android::Intent intent_;
|
||||
private:
|
||||
android::Intent intent_;
|
||||
};
|
||||
} // namespace cmds
|
||||
} // namespace anbox
|
||||
} // namespace cmds
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -19,95 +19,103 @@
|
|||
|
||||
#include "core/posix/signal.h"
|
||||
|
||||
#include "anbox/logger.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/application/launcher_storage.h"
|
||||
#include "anbox/bridge/android_api_stub.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/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/application/launcher_storage.h"
|
||||
#include "anbox/wm/manager.h"
|
||||
|
||||
#include "external/xdg/xdg.h"
|
||||
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#include <core/dbus/bus.h>
|
||||
#include <core/dbus/asio/executor.h>
|
||||
#include <core/dbus/bus.h>
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace {
|
||||
class NullConnectionCreator : public anbox::network::ConnectionCreator<boost::asio::local::stream_protocol> {
|
||||
public:
|
||||
void create_connection_for(
|
||||
std::shared_ptr<boost::asio::local::stream_protocol::socket> const& socket) override {
|
||||
WARNING("Not implemented");
|
||||
socket->close();
|
||||
}
|
||||
class NullConnectionCreator : public anbox::network::ConnectionCreator<
|
||||
boost::asio::local::stream_protocol> {
|
||||
public:
|
||||
void create_connection_for(
|
||||
std::shared_ptr<boost::asio::local::stream_protocol::socket> const
|
||||
&socket) override {
|
||||
WARNING("Not implemented");
|
||||
socket->close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
anbox::cmds::Run::BusFactory anbox::cmds::Run::session_bus_factory() {
|
||||
return []() {
|
||||
return std::make_shared<core::dbus::Bus>(core::dbus::WellKnownBus::session);
|
||||
};
|
||||
return []() {
|
||||
return std::make_shared<core::dbus::Bus>(core::dbus::WellKnownBus::session);
|
||||
};
|
||||
}
|
||||
|
||||
anbox::cmds::Run::Run(const BusFactory& bus_factory)
|
||||
: CommandWithFlagsAndAction{cli::Name{"run"}, cli::Usage{"run"}, 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
|
||||
// for proper confinement etc.
|
||||
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_));
|
||||
anbox::cmds::Run::Run(const BusFactory &bus_factory)
|
||||
: CommandWithFlagsAndAction{cli::Name{"run"}, cli::Usage{"run"},
|
||||
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
|
||||
// for proper confinement etc.
|
||||
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) {
|
||||
auto trap = core::posix::trap_signals_for_process({core::posix::Signal::sig_term,
|
||||
core::posix::Signal::sig_int});
|
||||
trap->signal_raised().connect([trap](const core::posix::Signal &signal) {
|
||||
INFO("Signal %i received. Good night.", static_cast<int>(signal));
|
||||
trap->stop();
|
||||
});
|
||||
action([this](const cli::Command::Context &ctx) {
|
||||
auto trap = core::posix::trap_signals_for_process(
|
||||
{core::posix::Signal::sig_term, core::posix::Signal::sig_int});
|
||||
trap->signal_raised().connect([trap](const core::posix::Signal &signal) {
|
||||
INFO("Signal %i received. Good night.", static_cast<int>(signal));
|
||||
trap->stop();
|
||||
});
|
||||
|
||||
utils::ensure_paths({
|
||||
config::socket_path(),
|
||||
config::host_input_device_path(),
|
||||
});
|
||||
utils::ensure_paths({
|
||||
config::socket_path(), config::host_input_device_path(),
|
||||
});
|
||||
|
||||
auto rt = Runtime::create();
|
||||
auto dispatcher = anbox::common::create_dispatcher_for_runtime(rt);
|
||||
auto rt = Runtime::create();
|
||||
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);
|
||||
// FIXME this needs to be removed and solved differently behind the scenes
|
||||
registerDisplayManager(policy);
|
||||
auto policy = std::make_shared<ubuntu::PlatformPolicy>(input_manager,
|
||||
android_api_stub);
|
||||
// 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>(
|
||||
xdg::data().home() / "applications");
|
||||
auto launcher_storage = std::make_shared<application::LauncherStorage>(
|
||||
xdg::data().home() / "applications");
|
||||
|
||||
auto renderer = std::make_shared<graphics::GLRendererServer>(window_manager);
|
||||
renderer->start();
|
||||
auto renderer =
|
||||
std::make_shared<graphics::GLRendererServer>(window_manager);
|
||||
renderer->start();
|
||||
|
||||
// Socket which will be used by the qemud service inside the Android
|
||||
// container for things like sensors, vibrtator etc.
|
||||
// Socket which will be used by the qemud service inside the Android
|
||||
// container for things like sensors, vibrtator etc.
|
||||
#if 0
|
||||
auto qemud_connector = std::make_shared<network::PublishedSocketConnector>(
|
||||
utils::string_format("%s/qemud", config::socket_path()),
|
||||
|
|
@ -115,60 +123,59 @@ anbox::cmds::Run::Run(const BusFactory& bus_factory)
|
|||
std::make_shared<NullConnectionCreator>());
|
||||
#endif
|
||||
|
||||
// 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.
|
||||
auto qemu_pipe_connector = std::make_shared<network::PublishedSocketConnector>(
|
||||
utils::string_format("%s/qemu_pipe", config::socket_path()),
|
||||
rt,
|
||||
std::make_shared<qemu::PipeConnectionCreator>(rt,
|
||||
renderer->socket_path(),
|
||||
icon_));
|
||||
// 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.
|
||||
auto qemu_pipe_connector =
|
||||
std::make_shared<network::PublishedSocketConnector>(
|
||||
utils::string_format("%s/qemu_pipe", config::socket_path()), rt,
|
||||
std::make_shared<qemu::PipeConnectionCreator>(
|
||||
rt, renderer->socket_path(), 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>(
|
||||
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 server = std::make_shared<bridge::PlatformApiSkeleton>(
|
||||
pending_calls, window_manager, launcher_storage);
|
||||
server->register_boot_finished_handler(
|
||||
[&]() { DEBUG("Android successfully booted"); });
|
||||
return std::make_shared<bridge::PlatformMessageProcessor>(
|
||||
sender, server, pending_calls);
|
||||
}));
|
||||
|
||||
auto server = std::make_shared<bridge::PlatformApiSkeleton>(pending_calls,
|
||||
window_manager,
|
||||
launcher_storage);
|
||||
server->register_boot_finished_handler([&]() {
|
||||
DEBUG("Android successfully booted");
|
||||
});
|
||||
return std::make_shared<bridge::PlatformMessageProcessor>(sender, server, pending_calls);
|
||||
}));
|
||||
container::Client container(rt);
|
||||
container::Configuration 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"},
|
||||
};
|
||||
|
||||
container::Client container(rt);
|
||||
container::Configuration 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(
|
||||
[&]() { container.start_container(container_configuration); });
|
||||
|
||||
dispatcher->dispatch([&]() {
|
||||
container.start_container(container_configuration);
|
||||
});
|
||||
auto bus = bus_factory_();
|
||||
bus->install_executor(core::dbus::asio::make_executor(bus, rt->service()));
|
||||
|
||||
auto bus = bus_factory_();
|
||||
bus->install_executor(core::dbus::asio::make_executor(bus, rt->service()));
|
||||
auto skeleton =
|
||||
anbox::dbus::skeleton::Service::create_for_bus(bus, android_api_stub);
|
||||
|
||||
auto skeleton = anbox::dbus::skeleton::Service::create_for_bus(bus, android_api_stub);
|
||||
rt->start();
|
||||
trap->run();
|
||||
rt->stop();
|
||||
|
||||
rt->start();
|
||||
trap->run();
|
||||
rt->stop();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
});
|
||||
return EXIT_SUCCESS;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,19 +29,19 @@
|
|||
namespace anbox {
|
||||
namespace cmds {
|
||||
class Run : public cli::CommandWithFlagsAndAction {
|
||||
public:
|
||||
typedef std::function<core::dbus::Bus::Ptr()> BusFactory;
|
||||
public:
|
||||
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:
|
||||
BusFactory bus_factory_;
|
||||
std::string desktop_file_hint_;
|
||||
std::string icon_;
|
||||
private:
|
||||
BusFactory bus_factory_;
|
||||
std::string desktop_file_hint_;
|
||||
std::string icon_;
|
||||
};
|
||||
} // namespace cmds
|
||||
} // namespace anbox
|
||||
} // namespace cmds
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -21,13 +21,13 @@
|
|||
#include "anbox/version.h"
|
||||
|
||||
anbox::cmds::Version::Version()
|
||||
: CommandWithFlagsAndAction{cli::Name{"version"}, cli::Usage{"version"}, cli::Description{"print the version of the daemon"}}
|
||||
{
|
||||
action([](const cli::Command::Context& ctxt)
|
||||
{
|
||||
std::uint32_t major, minor, patch;
|
||||
anbox::version(major, minor, patch);
|
||||
ctxt.cout << "anbox " << major << "." << minor << "." << patch << std::endl;
|
||||
return 0;
|
||||
});
|
||||
: CommandWithFlagsAndAction{
|
||||
cli::Name{"version"}, cli::Usage{"version"},
|
||||
cli::Description{"print the version of the daemon"}} {
|
||||
action([](const cli::Command::Context& ctxt) {
|
||||
std::uint32_t major, minor, patch;
|
||||
anbox::version(major, minor, patch);
|
||||
ctxt.cout << "anbox " << major << "." << minor << "." << patch << std::endl;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,10 +29,10 @@
|
|||
namespace anbox {
|
||||
namespace cmds {
|
||||
class Version : public cli::CommandWithFlagsAndAction {
|
||||
public:
|
||||
Version();
|
||||
public:
|
||||
Version();
|
||||
};
|
||||
} // namespace cmds
|
||||
} // namespace anbox
|
||||
} // namespace cmds
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -21,23 +21,20 @@
|
|||
|
||||
namespace {
|
||||
struct AsioStrandDispatcher : public anbox::common::Dispatcher {
|
||||
public:
|
||||
AsioStrandDispatcher(const std::shared_ptr<anbox::Runtime>& rt)
|
||||
: rt{rt},
|
||||
strand{rt->service()} {
|
||||
}
|
||||
public:
|
||||
AsioStrandDispatcher(const std::shared_ptr<anbox::Runtime>& rt)
|
||||
: rt{rt}, strand{rt->service()} {}
|
||||
|
||||
void dispatch(const Task &task) override {
|
||||
strand.post(task);
|
||||
}
|
||||
void dispatch(const Task& task) override { strand.post(task); }
|
||||
|
||||
private:
|
||||
std::shared_ptr<anbox::Runtime> rt;
|
||||
boost::asio::io_service::strand strand;
|
||||
private:
|
||||
std::shared_ptr<anbox::Runtime> rt;
|
||||
boost::asio::io_service::strand strand;
|
||||
};
|
||||
}
|
||||
|
||||
std::shared_ptr<anbox::common::Dispatcher> anbox::common::create_dispatcher_for_runtime(
|
||||
const std::shared_ptr<anbox::Runtime>& rt) {
|
||||
return std::make_shared<AsioStrandDispatcher>(rt);
|
||||
std::shared_ptr<anbox::common::Dispatcher>
|
||||
anbox::common::create_dispatcher_for_runtime(
|
||||
const std::shared_ptr<anbox::Runtime>& rt) {
|
||||
return std::make_shared<AsioStrandDispatcher>(rt);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,16 +28,17 @@
|
|||
namespace anbox {
|
||||
namespace common {
|
||||
class Dispatcher : public DoNotCopyOrMove {
|
||||
public:
|
||||
typedef std::function<void()> Task;
|
||||
virtual void dispatch(const Task& task) = 0;
|
||||
public:
|
||||
typedef std::function<void()> Task;
|
||||
virtual void dispatch(const Task& task) = 0;
|
||||
|
||||
protected:
|
||||
Dispatcher() = default;
|
||||
protected:
|
||||
Dispatcher() = default;
|
||||
};
|
||||
|
||||
std::shared_ptr<Dispatcher> create_dispatcher_for_runtime(const std::shared_ptr<Runtime>&);
|
||||
} // namespace common
|
||||
} // namespace anbox
|
||||
std::shared_ptr<Dispatcher> create_dispatcher_for_runtime(
|
||||
const std::shared_ptr<Runtime>&);
|
||||
} // namespace common
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -21,41 +21,27 @@
|
|||
#include <algorithm>
|
||||
|
||||
namespace anbox {
|
||||
Fd::Fd() :
|
||||
Fd{invalid}
|
||||
{
|
||||
Fd::Fd() : 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{std::make_shared<int>(fd.int_owned_fd)}
|
||||
{
|
||||
Fd::operator int() const {
|
||||
if (fd) return *fd;
|
||||
return invalid;
|
||||
}
|
||||
|
||||
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
|
||||
} // namespace anbox
|
||||
|
|
|
|||
|
|
@ -22,29 +22,29 @@
|
|||
#include <memory>
|
||||
|
||||
namespace anbox {
|
||||
struct IntOwnedFd
|
||||
{
|
||||
int int_owned_fd;
|
||||
struct IntOwnedFd {
|
||||
int int_owned_fd;
|
||||
};
|
||||
class Fd
|
||||
{
|
||||
public:
|
||||
//transfer ownership of the POD-int to the object. The int no longer needs close()ing,
|
||||
//and has the lifetime of the Fd object.
|
||||
explicit Fd(int fd);
|
||||
explicit Fd(IntOwnedFd);
|
||||
static int const invalid{-1};
|
||||
Fd(); //Initializes fd to the anbox::Fd::invalid;
|
||||
Fd(Fd&&);
|
||||
Fd(Fd const&) = default;
|
||||
Fd& operator=(Fd);
|
||||
class Fd {
|
||||
public:
|
||||
// transfer ownership of the POD-int to the object. The int no longer needs
|
||||
// close()ing,
|
||||
// and has the lifetime of the Fd object.
|
||||
explicit Fd(int fd);
|
||||
explicit Fd(IntOwnedFd);
|
||||
static int const invalid{-1};
|
||||
Fd(); // Initializes fd to the anbox::Fd::invalid;
|
||||
Fd(Fd&&);
|
||||
Fd(Fd const&) = default;
|
||||
Fd& operator=(Fd);
|
||||
|
||||
//bit of a convenient kludge. take care not to close or otherwise destroy the FD.
|
||||
operator int() const;
|
||||
// bit of a convenient kludge. take care not to close or otherwise destroy the
|
||||
// FD.
|
||||
operator int() const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<int> fd;
|
||||
private:
|
||||
std::shared_ptr<int> fd;
|
||||
};
|
||||
} // namespace anbox
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@
|
|||
#ifndef ANBOX_COMMON_FD_SETS_H_
|
||||
#define ANBOX_COMMON_FD_SETS_H_
|
||||
|
||||
#include <vector>
|
||||
#include <initializer_list>
|
||||
#include <vector>
|
||||
|
||||
#include "anbox/common/fd.h"
|
||||
|
||||
namespace anbox {
|
||||
typedef std::vector<std::vector<Fd>> FdSets;
|
||||
} // namespace anbox
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -24,38 +24,35 @@
|
|||
|
||||
namespace anbox {
|
||||
template <size_t BuiltInBufferSize>
|
||||
class VariableLengthArray
|
||||
{
|
||||
public:
|
||||
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);
|
||||
}
|
||||
class VariableLengthArray {
|
||||
public:
|
||||
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);
|
||||
}
|
||||
|
||||
void resize(size_t size)
|
||||
{
|
||||
if (size > BuiltInBufferSize)
|
||||
effective_buffer = BufferUPtr{new unsigned char[size], heap_deleter};
|
||||
else
|
||||
effective_buffer = BufferUPtr{builtin_buffer, null_deleter};
|
||||
void resize(size_t size) {
|
||||
if (size > BuiltInBufferSize)
|
||||
effective_buffer = BufferUPtr{new unsigned char[size], heap_deleter};
|
||||
else
|
||||
effective_buffer = BufferUPtr{builtin_buffer, null_deleter};
|
||||
|
||||
size_ = size;
|
||||
}
|
||||
size_ = size;
|
||||
}
|
||||
|
||||
unsigned char* data() const { return effective_buffer.get(); }
|
||||
size_t size() const { return size_; }
|
||||
unsigned char* data() const { return effective_buffer.get(); }
|
||||
size_t size() const { return size_; }
|
||||
|
||||
private:
|
||||
typedef std::unique_ptr<unsigned char,void (*)(unsigned char*)> BufferUPtr;
|
||||
private:
|
||||
typedef std::unique_ptr<unsigned char, void (*)(unsigned char*)> BufferUPtr;
|
||||
|
||||
static void null_deleter(unsigned char*) {}
|
||||
static void heap_deleter(unsigned char* b) { delete[] b; }
|
||||
static void null_deleter(unsigned char*) {}
|
||||
static void heap_deleter(unsigned char* b) { delete[] b; }
|
||||
|
||||
unsigned char builtin_buffer[BuiltInBufferSize];
|
||||
BufferUPtr effective_buffer{builtin_buffer, null_deleter};
|
||||
size_t size_;
|
||||
unsigned char builtin_buffer[BuiltInBufferSize];
|
||||
BufferUPtr effective_buffer{builtin_buffer, null_deleter};
|
||||
size_t size_;
|
||||
};
|
||||
} // namespace anbox
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -21,72 +21,59 @@
|
|||
|
||||
namespace anbox {
|
||||
namespace common {
|
||||
WaitHandle::WaitHandle() :
|
||||
guard(),
|
||||
wait_condition(),
|
||||
expecting(0),
|
||||
received(0)
|
||||
{
|
||||
WaitHandle::WaitHandle()
|
||||
: guard(), wait_condition(), expecting(0), received(0) {}
|
||||
|
||||
WaitHandle::~WaitHandle() {}
|
||||
|
||||
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()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(guard);
|
||||
|
||||
expecting++;
|
||||
}
|
||||
|
||||
void WaitHandle::result_received()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(guard);
|
||||
|
||||
received++;
|
||||
wait_condition.notify_all();
|
||||
received++;
|
||||
wait_condition.notify_all();
|
||||
}
|
||||
|
||||
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;
|
||||
expecting = 0;
|
||||
received = 0;
|
||||
expecting = 0;
|
||||
}
|
||||
|
||||
void WaitHandle::wait_for_pending(std::chrono::milliseconds limit)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(guard);
|
||||
void WaitHandle::wait_for_pending(std::chrono::milliseconds limit) {
|
||||
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
|
||||
{
|
||||
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;
|
||||
--expecting;
|
||||
--received;
|
||||
--expecting;
|
||||
}
|
||||
|
||||
bool WaitHandle::has_result()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(guard);
|
||||
bool WaitHandle::has_result() {
|
||||
std::lock_guard<std::mutex> lock(guard);
|
||||
|
||||
return received > 0;
|
||||
return received > 0;
|
||||
}
|
||||
|
||||
bool WaitHandle::is_pending()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(guard);
|
||||
return expecting > 0 && received != expecting;
|
||||
bool WaitHandle::is_pending() {
|
||||
std::unique_lock<std::mutex> lock(guard);
|
||||
return expecting > 0 && received != expecting;
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace anbox
|
||||
} // namespace common
|
||||
} // namespace anbox
|
||||
|
|
|
|||
|
|
@ -26,29 +26,28 @@
|
|||
|
||||
namespace anbox {
|
||||
namespace common {
|
||||
struct WaitHandle
|
||||
{
|
||||
public:
|
||||
WaitHandle();
|
||||
~WaitHandle();
|
||||
struct WaitHandle {
|
||||
public:
|
||||
WaitHandle();
|
||||
~WaitHandle();
|
||||
|
||||
void expect_result();
|
||||
void result_received();
|
||||
void wait_for_all();
|
||||
void wait_for_one();
|
||||
void wait_for_pending(std::chrono::milliseconds limit);
|
||||
void expect_result();
|
||||
void result_received();
|
||||
void wait_for_all();
|
||||
void wait_for_one();
|
||||
void wait_for_pending(std::chrono::milliseconds limit);
|
||||
|
||||
bool has_result();
|
||||
bool is_pending();
|
||||
bool has_result();
|
||||
bool is_pending();
|
||||
|
||||
private:
|
||||
std::mutex guard;
|
||||
std::condition_variable wait_condition;
|
||||
private:
|
||||
std::mutex guard;
|
||||
std::condition_variable wait_condition;
|
||||
|
||||
int expecting;
|
||||
int received;
|
||||
int expecting;
|
||||
int received;
|
||||
};
|
||||
} // namespace common
|
||||
} // namespace anbox
|
||||
} // namespace common
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -27,76 +27,81 @@ namespace fs = boost::filesystem;
|
|||
namespace anbox {
|
||||
namespace config {
|
||||
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) {
|
||||
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) {
|
||||
return utils::prefix_dir_from_env(path, "SNAP_USER_COMMON");
|
||||
return utils::prefix_dir_from_env(path, "SNAP_USER_COMMON");
|
||||
}
|
||||
|
||||
std::string home_dir() {
|
||||
static std::string path;
|
||||
if (path.empty()) {
|
||||
path = utils::get_env_value("HOME", "");
|
||||
if (path.empty())
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error("No home directory specified"));
|
||||
}
|
||||
return path;
|
||||
static std::string path;
|
||||
if (path.empty()) {
|
||||
path = utils::get_env_value("HOME", "");
|
||||
if (path.empty())
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error("No home directory specified"));
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string runtime_dir() {
|
||||
static std::string path;
|
||||
if (path.empty()) {
|
||||
path = utils::get_env_value("XDG_RUNTIME_DIR", "");
|
||||
if (path.empty())
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error("No runtime directory specified"));
|
||||
}
|
||||
return path;
|
||||
static std::string path;
|
||||
if (path.empty()) {
|
||||
path = utils::get_env_value("XDG_RUNTIME_DIR", "");
|
||||
if (path.empty())
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::runtime_error("No runtime directory specified"));
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string state_dir() {
|
||||
static std::string path = "/var/lib";
|
||||
return path;
|
||||
static std::string path = "/var/lib";
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string log_path() {
|
||||
static std::string path = in_snap_data_dir(utils::string_format("%s/anbox/", state_dir()));
|
||||
return path;
|
||||
static std::string path =
|
||||
in_snap_data_dir(utils::string_format("%s/anbox/", state_dir()));
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string socket_path() {
|
||||
|
||||
static std::string path = utils::string_format("%s/anbox/sockets", runtime_dir());
|
||||
return path;
|
||||
static std::string path =
|
||||
utils::string_format("%s/anbox/sockets", runtime_dir());
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string data_path() {
|
||||
static std::string path = utils::string_format("%s/.anbox/data", home_dir());
|
||||
return path;
|
||||
static std::string path = utils::string_format("%s/.anbox/data", home_dir());
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string rootfs_path() {
|
||||
static std::string path = in_snap_data_dir(utils::string_format("%s/anbox/rootfs", state_dir()));
|
||||
return path;
|
||||
static std::string path =
|
||||
in_snap_data_dir(utils::string_format("%s/anbox/rootfs", state_dir()));
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string container_config_path() {
|
||||
static std::string path = in_snap_data_dir(utils::string_format("%s/anbox/containers", state_dir()));
|
||||
return path;
|
||||
static std::string path = in_snap_data_dir(
|
||||
utils::string_format("%s/anbox/containers", state_dir()));
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string container_socket_path() {
|
||||
std::string path = "/run/anbox-container.socket";
|
||||
return path;
|
||||
std::string path = "/run/anbox-container.socket";
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string host_input_device_path() {
|
||||
static std::string path = utils::string_format("%s/anbox/input-devices", runtime_dir());
|
||||
return path;
|
||||
static std::string path =
|
||||
utils::string_format("%s/anbox/input-devices", runtime_dir());
|
||||
return path;
|
||||
}
|
||||
} // namespace config
|
||||
} // namespace anbox
|
||||
} // namespace config
|
||||
} // namespace anbox
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ std::string socket_path();
|
|||
std::string container_config_path();
|
||||
std::string container_socket_path();
|
||||
std::string host_input_device_path();
|
||||
} // namespace config
|
||||
} // namespace anbox
|
||||
} // namespace config
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -16,53 +16,50 @@
|
|||
*/
|
||||
|
||||
#include "anbox/container/client.h"
|
||||
#include "anbox/config.h"
|
||||
#include "anbox/container/management_api_stub.h"
|
||||
#include "anbox/logger.h"
|
||||
#include "anbox/network/local_socket_messenger.h"
|
||||
#include "anbox/rpc/pending_call_cache.h"
|
||||
#include "anbox/rpc/channel.h"
|
||||
#include "anbox/rpc/message_processor.h"
|
||||
#include "anbox/config.h"
|
||||
#include "anbox/logger.h"
|
||||
#include "anbox/rpc/pending_call_cache.h"
|
||||
|
||||
namespace ba = boost::asio;
|
||||
namespace bs = boost::system;
|
||||
|
||||
namespace anbox {
|
||||
namespace container {
|
||||
Client::Client(const std::shared_ptr<Runtime> &rt) :
|
||||
messenger_(std::make_shared<network::LocalSocketMessenger>(config::container_socket_path(), rt)),
|
||||
pending_calls_(std::make_shared<rpc::PendingCallCache>()),
|
||||
rpc_channel_(std::make_shared<rpc::Channel>(pending_calls_, messenger_)),
|
||||
management_api_(std::make_shared<ManagementApiStub>(rpc_channel_)),
|
||||
processor_(std::make_shared<rpc::MessageProcessor>(messenger_, pending_calls_)) {
|
||||
|
||||
read_next_message();
|
||||
Client::Client(const std::shared_ptr<Runtime> &rt)
|
||||
: messenger_(std::make_shared<network::LocalSocketMessenger>(
|
||||
config::container_socket_path(), rt)),
|
||||
pending_calls_(std::make_shared<rpc::PendingCallCache>()),
|
||||
rpc_channel_(std::make_shared<rpc::Channel>(pending_calls_, messenger_)),
|
||||
management_api_(std::make_shared<ManagementApiStub>(rpc_channel_)),
|
||||
processor_(
|
||||
std::make_shared<rpc::MessageProcessor>(messenger_, pending_calls_)) {
|
||||
read_next_message();
|
||||
}
|
||||
|
||||
Client::~Client() {
|
||||
}
|
||||
Client::~Client() {}
|
||||
|
||||
void Client::start_container(const Configuration &configuration) {
|
||||
management_api_->start_container(configuration);
|
||||
management_api_->start_container(configuration);
|
||||
}
|
||||
|
||||
void Client::read_next_message()
|
||||
{
|
||||
auto callback = std::bind(&Client::on_read_size,
|
||||
this, std::placeholders::_1, std::placeholders::_2);
|
||||
messenger_->async_receive_msg(callback, ba::buffer(buffer_));
|
||||
void Client::read_next_message() {
|
||||
auto callback = std::bind(&Client::on_read_size, this, std::placeholders::_1,
|
||||
std::placeholders::_2);
|
||||
messenger_->async_receive_msg(callback, ba::buffer(buffer_));
|
||||
}
|
||||
|
||||
void Client::on_read_size(const boost::system::error_code& error, std::size_t bytes_read)
|
||||
{
|
||||
if (error)
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error(error.message()));
|
||||
void Client::on_read_size(const boost::system::error_code &error,
|
||||
std::size_t bytes_read) {
|
||||
if (error) BOOST_THROW_EXCEPTION(std::runtime_error(error.message()));
|
||||
|
||||
std::vector<std::uint8_t> data(bytes_read);
|
||||
std::copy(buffer_.data(), buffer_.data() + bytes_read, data.data());
|
||||
std::vector<std::uint8_t> data(bytes_read);
|
||||
std::copy(buffer_.data(), buffer_.data() + bytes_read, data.data());
|
||||
|
||||
if (processor_->process_data(data))
|
||||
read_next_message();
|
||||
if (processor_->process_data(data)) read_next_message();
|
||||
}
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
|
|
|
|||
|
|
@ -18,39 +18,40 @@
|
|||
#ifndef ANBOX_CONTAINER_CLIENT_H_
|
||||
#define ANBOX_CONTAINER_CLIENT_H_
|
||||
|
||||
#include "anbox/runtime.h"
|
||||
#include "anbox/container/configuration.h"
|
||||
#include "anbox/runtime.h"
|
||||
|
||||
namespace anbox {
|
||||
namespace rpc {
|
||||
class PendingCallCache;
|
||||
class Channel;
|
||||
class MessageProcessor;
|
||||
} // namespace rpc
|
||||
} // namespace rpc
|
||||
namespace network {
|
||||
class LocalSocketMessenger;
|
||||
} // namespace network
|
||||
} // namespace network
|
||||
namespace container {
|
||||
class ManagementApiStub;
|
||||
class Client {
|
||||
public:
|
||||
Client(const std::shared_ptr<Runtime> &rt);
|
||||
~Client();
|
||||
public:
|
||||
Client(const std::shared_ptr<Runtime> &rt);
|
||||
~Client();
|
||||
|
||||
void start_container(const Configuration &configuration);
|
||||
void start_container(const Configuration &configuration);
|
||||
|
||||
private:
|
||||
void read_next_message();
|
||||
void on_read_size(const boost::system::error_code& ec, std::size_t bytes_read);
|
||||
private:
|
||||
void read_next_message();
|
||||
void on_read_size(const boost::system::error_code &ec,
|
||||
std::size_t bytes_read);
|
||||
|
||||
std::shared_ptr<network::LocalSocketMessenger> messenger_;
|
||||
std::shared_ptr<rpc::PendingCallCache> pending_calls_;
|
||||
std::shared_ptr<rpc::Channel> rpc_channel_;
|
||||
std::shared_ptr<ManagementApiStub> management_api_;
|
||||
std::shared_ptr<rpc::MessageProcessor> processor_;
|
||||
std::array<std::uint8_t, 8192> buffer_;
|
||||
std::shared_ptr<network::LocalSocketMessenger> messenger_;
|
||||
std::shared_ptr<rpc::PendingCallCache> pending_calls_;
|
||||
std::shared_ptr<rpc::Channel> rpc_channel_;
|
||||
std::shared_ptr<ManagementApiStub> management_api_;
|
||||
std::shared_ptr<rpc::MessageProcessor> processor_;
|
||||
std::array<std::uint8_t, 8192> buffer_;
|
||||
};
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -18,15 +18,15 @@
|
|||
#ifndef ANBOX_CONTAINER_CONFIGURATION_H_
|
||||
#define ANBOX_CONTAINER_CONFIGURATION_H_
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace anbox {
|
||||
namespace container {
|
||||
struct Configuration {
|
||||
std::map<std::string,std::string> bind_mounts;
|
||||
std::map<std::string, std::string> bind_mounts;
|
||||
};
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
namespace anbox {
|
||||
namespace container {
|
||||
Container::~Container() {
|
||||
}
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
Container::~Container() {}
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
|
|
|
|||
|
|
@ -20,30 +20,30 @@
|
|||
|
||||
#include "anbox/container/configuration.h"
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace anbox {
|
||||
namespace container {
|
||||
class Container {
|
||||
public:
|
||||
virtual ~Container();
|
||||
public:
|
||||
virtual ~Container();
|
||||
|
||||
enum class State {
|
||||
inactive,
|
||||
running,
|
||||
};
|
||||
enum class State {
|
||||
inactive,
|
||||
running,
|
||||
};
|
||||
|
||||
// Start the container in background
|
||||
virtual void start(const Configuration &configuration) = 0;
|
||||
// Start the container in background
|
||||
virtual void start(const Configuration &configuration) = 0;
|
||||
|
||||
// Stop a running container
|
||||
virtual void stop() = 0;
|
||||
// Stop a running container
|
||||
virtual void stop() = 0;
|
||||
|
||||
// Get the current container state
|
||||
virtual State state() = 0;
|
||||
// Get the current container state
|
||||
virtual State state() = 0;
|
||||
};
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -16,158 +16,168 @@
|
|||
*/
|
||||
|
||||
#include "anbox/container/lxc_container.h"
|
||||
#include "anbox/utils.h"
|
||||
#include "anbox/config.h"
|
||||
#include "anbox/logger.h"
|
||||
#include "anbox/utils.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <sys/capability.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace anbox {
|
||||
namespace container {
|
||||
LxcContainer::LxcContainer() :
|
||||
state_(State::inactive),
|
||||
container_(nullptr) {
|
||||
utils::ensure_paths({
|
||||
config::container_config_path(),
|
||||
config::log_path(),
|
||||
});
|
||||
LxcContainer::LxcContainer() : state_(State::inactive), container_(nullptr) {
|
||||
utils::ensure_paths({
|
||||
config::container_config_path(), config::log_path(),
|
||||
});
|
||||
}
|
||||
|
||||
LxcContainer::~LxcContainer() {
|
||||
DEBUG("");
|
||||
DEBUG("");
|
||||
|
||||
stop();
|
||||
stop();
|
||||
|
||||
if (container_)
|
||||
lxc_container_put(container_);
|
||||
if (container_) lxc_container_put(container_);
|
||||
}
|
||||
|
||||
void LxcContainer::start(const Configuration &configuration) {
|
||||
if (getuid() != 0)
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error("You have to start the container as root"));
|
||||
if (getuid() != 0)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::runtime_error("You have to start the container as root"));
|
||||
|
||||
if (container_ && container_->is_running(container_)) {
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error("Container already started, stopping it now"));
|
||||
container_->stop(container_);
|
||||
}
|
||||
if (container_ && container_->is_running(container_)) {
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::runtime_error("Container already started, stopping it now"));
|
||||
container_->stop(container_);
|
||||
}
|
||||
|
||||
if (!container_) {
|
||||
DEBUG("Containers are stored in %s", config::container_config_path());
|
||||
if (!container_) {
|
||||
DEBUG("Containers are stored in %s", config::container_config_path());
|
||||
|
||||
// Remove container config to be be able to rewrite it
|
||||
::unlink(utils::string_format("%s/default/config", config::container_config_path()).c_str());
|
||||
// Remove container config to be be able to rewrite it
|
||||
::unlink(utils::string_format("%s/default/config",
|
||||
config::container_config_path())
|
||||
.c_str());
|
||||
|
||||
container_ = lxc_container_new("default", config::container_config_path().c_str());
|
||||
if (!container_)
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create LXC container instance"));
|
||||
container_ =
|
||||
lxc_container_new("default", config::container_config_path().c_str());
|
||||
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
|
||||
// its configuration is synchronized.
|
||||
if (container_->is_running(container_))
|
||||
container_->stop(container_);
|
||||
}
|
||||
// If container is still running (for example after a crash) we stop it here
|
||||
// to ensure
|
||||
// its configuration is synchronized.
|
||||
if (container_->is_running(container_)) container_->stop(container_);
|
||||
}
|
||||
|
||||
// We drop all not needed capabilities
|
||||
set_config_item("lxc.cap.drop", "mac_admin mac_override sys_time sys_module sys_rawio");
|
||||
// We drop all not needed capabilities
|
||||
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
|
||||
set_config_item("lxc.mount.auto", "proc:mixed sys:mixed cgroup:mixed");
|
||||
// We can mount proc/sys as rw here as we will run the container unprivileged
|
||||
// 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.pts", "1024");
|
||||
set_config_item("lxc.tty", "0");
|
||||
set_config_item("lxc.utsname", "anbox");
|
||||
set_config_item("lxc.autodev", "1");
|
||||
set_config_item("lxc.pts", "1024");
|
||||
set_config_item("lxc.tty", "0");
|
||||
set_config_item("lxc.utsname", "anbox");
|
||||
|
||||
set_config_item("lxc.group.devices.deny","");
|
||||
set_config_item("lxc.group.devices.allow","");
|
||||
set_config_item("lxc.group.devices.deny", "");
|
||||
set_config_item("lxc.group.devices.allow", "");
|
||||
|
||||
// We can't move bind-mounts, so don't use /dev/lxc/
|
||||
set_config_item("lxc.devttydir", "");
|
||||
// We can't move bind-mounts, so don't use /dev/lxc/
|
||||
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.rootfs.backend", "dir");
|
||||
set_config_item("lxc.init_cmd", "/anbox-init.sh");
|
||||
set_config_item("lxc.rootfs.backend", "dir");
|
||||
|
||||
DEBUG("Using rootfs path %s", config::rootfs_path());
|
||||
set_config_item("lxc.rootfs", config::rootfs_path());
|
||||
DEBUG("Using rootfs path %s", config::rootfs_path());
|
||||
set_config_item("lxc.rootfs", config::rootfs_path());
|
||||
|
||||
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.loglevel", "0");
|
||||
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.flags", "up");
|
||||
set_config_item("lxc.network.link", "anboxbr0");
|
||||
set_config_item("lxc.network.type", "veth");
|
||||
set_config_item("lxc.network.flags", "up");
|
||||
set_config_item("lxc.network.link", "anboxbr0");
|
||||
|
||||
#if 0
|
||||
// Android uses namespaces as well so we have to allow nested namespaces for LXC
|
||||
// which are otherwise forbidden by AppArmor.
|
||||
set_config_item("lxc.aa_profile", "lxc-container-default-with-nesting");
|
||||
#else
|
||||
// FIXME: when using the nested profile we still get various denials from things
|
||||
// Android tries to do but isn't allowed to. We need to look into those and see
|
||||
// how we can switch back to a confined way of running the container.
|
||||
set_config_item("lxc.aa_profile", "unconfined");
|
||||
// FIXME: when using the nested profile we still get various denials from
|
||||
// things
|
||||
// Android tries to do but isn't allowed to. We need to look into those and
|
||||
// see
|
||||
// how we can switch back to a confined way of running the container.
|
||||
set_config_item("lxc.aa_profile", "unconfined");
|
||||
#endif
|
||||
|
||||
for (const auto &bind_mount : configuration.bind_mounts) {
|
||||
std::string create_type = "file";
|
||||
for (const auto &bind_mount : configuration.bind_mounts) {
|
||||
std::string create_type = "file";
|
||||
|
||||
if (fs::is_directory(bind_mount.first))
|
||||
create_type = "dir";
|
||||
if (fs::is_directory(bind_mount.first)) create_type = "dir";
|
||||
|
||||
auto target_path = bind_mount.second;
|
||||
// LXC wants target paths relative to the container rootfs so
|
||||
// prividing an absolute path doesn't work.
|
||||
if (utils::string_starts_with(target_path, "/"))
|
||||
target_path.erase(0, 1);
|
||||
auto target_path = bind_mount.second;
|
||||
// LXC wants target paths relative to the container rootfs so
|
||||
// prividing an absolute path doesn't work.
|
||||
if (utils::string_starts_with(target_path, "/")) target_path.erase(0, 1);
|
||||
|
||||
set_config_item("lxc.mount.entry",
|
||||
utils::string_format("%s %s none bind,create=%s,optional 0 0",
|
||||
bind_mount.first, target_path, create_type));
|
||||
}
|
||||
set_config_item(
|
||||
"lxc.mount.entry",
|
||||
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))
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error("Failed to save container configuration"));
|
||||
if (!container_->save_config(container_, nullptr))
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::runtime_error("Failed to save container configuration"));
|
||||
|
||||
if (not container_->start(container_, 0, nullptr))
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error("Failed to start container"));
|
||||
if (not container_->start(container_, 0, nullptr))
|
||||
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() {
|
||||
if (not container_ || not container_->is_running(container_))
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error("Cannot stop container as it is not running"));
|
||||
if (not container_ || not container_->is_running(container_))
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::runtime_error("Cannot stop container as it is not running"));
|
||||
|
||||
if (not container_->stop(container_))
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error("Failed to stop container"));
|
||||
if (not container_->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) {
|
||||
if (!container_->set_config_item(container_, key.c_str(), value.c_str()))
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error("Failed to configure LXC container"));
|
||||
void LxcContainer::set_config_item(const std::string &key,
|
||||
const std::string &value) {
|
||||
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() {
|
||||
return state_;
|
||||
}
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
Container::State LxcContainer::state() { return state_; }
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
|
|
|
|||
|
|
@ -27,21 +27,21 @@
|
|||
namespace anbox {
|
||||
namespace container {
|
||||
class LxcContainer : public Container {
|
||||
public:
|
||||
LxcContainer();
|
||||
~LxcContainer();
|
||||
public:
|
||||
LxcContainer();
|
||||
~LxcContainer();
|
||||
|
||||
void start(const Configuration &configuration) override;
|
||||
void stop() override;
|
||||
State state() override;
|
||||
void start(const Configuration &configuration) override;
|
||||
void stop() override;
|
||||
State state() override;
|
||||
|
||||
private:
|
||||
void set_config_item(const std::string &key, const std::string &value);
|
||||
private:
|
||||
void set_config_item(const std::string &key, const std::string &value);
|
||||
|
||||
State state_;
|
||||
lxc_container *container_;
|
||||
State state_;
|
||||
lxc_container *container_;
|
||||
};
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -24,22 +24,22 @@
|
|||
|
||||
namespace anbox {
|
||||
namespace container {
|
||||
ManagementApiMessageProcessor::ManagementApiMessageProcessor(const std::shared_ptr<network::MessageSender> &sender,
|
||||
const std::shared_ptr<rpc::PendingCallCache> &pending_calls,
|
||||
const std::shared_ptr<ManagementApiSkeleton> &server) :
|
||||
rpc::MessageProcessor(sender, pending_calls),
|
||||
server_(server) {
|
||||
ManagementApiMessageProcessor::ManagementApiMessageProcessor(
|
||||
const std::shared_ptr<network::MessageSender> &sender,
|
||||
const std::shared_ptr<rpc::PendingCallCache> &pending_calls,
|
||||
const std::shared_ptr<ManagementApiSkeleton> &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::dispatch(rpc::Invocation const& invocation) {
|
||||
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
|
||||
void ManagementApiMessageProcessor::process_event_sequence(
|
||||
const std::string &) {}
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
|
|
|
|||
|
|
@ -24,19 +24,20 @@ namespace anbox {
|
|||
namespace container {
|
||||
class ManagementApiSkeleton;
|
||||
class ManagementApiMessageProcessor : public rpc::MessageProcessor {
|
||||
public:
|
||||
ManagementApiMessageProcessor(const std::shared_ptr<network::MessageSender> &sender,
|
||||
const std::shared_ptr<rpc::PendingCallCache> &pending_calls,
|
||||
const std::shared_ptr<ManagementApiSkeleton> &server);
|
||||
~ManagementApiMessageProcessor();
|
||||
public:
|
||||
ManagementApiMessageProcessor(
|
||||
const std::shared_ptr<network::MessageSender> &sender,
|
||||
const std::shared_ptr<rpc::PendingCallCache> &pending_calls,
|
||||
const std::shared_ptr<ManagementApiSkeleton> &server);
|
||||
~ManagementApiMessageProcessor();
|
||||
|
||||
void dispatch(rpc::Invocation const& invocation) override;
|
||||
void process_event_sequence(const std::string &event) override;
|
||||
void dispatch(rpc::Invocation const &invocation) override;
|
||||
void process_event_sequence(const std::string &event) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<ManagementApiSkeleton> server_;
|
||||
private:
|
||||
std::shared_ptr<ManagementApiSkeleton> server_;
|
||||
};
|
||||
} // namespace anbox
|
||||
} // namespace network
|
||||
} // namespace anbox
|
||||
} // namespace network
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -16,54 +16,52 @@
|
|||
*/
|
||||
|
||||
#include "anbox/container/management_api_skeleton.h"
|
||||
#include "anbox/container/container.h"
|
||||
#include "anbox/container/configuration.h"
|
||||
#include "anbox/container/container.h"
|
||||
#include "anbox/defer_action.h"
|
||||
#include "anbox/utils.h"
|
||||
#include "anbox/logger.h"
|
||||
#include "anbox/utils.h"
|
||||
|
||||
#include "anbox_rpc.pb.h"
|
||||
#include "anbox_container.pb.h"
|
||||
#include "anbox_rpc.pb.h"
|
||||
|
||||
namespace anbox {
|
||||
namespace container {
|
||||
ManagementApiSkeleton::ManagementApiSkeleton(const std::shared_ptr<rpc::PendingCallCache> &pending_calls,
|
||||
const std::shared_ptr<Container> &container) :
|
||||
pending_calls_(pending_calls),
|
||||
container_(container) {
|
||||
}
|
||||
ManagementApiSkeleton::ManagementApiSkeleton(
|
||||
const std::shared_ptr<rpc::PendingCallCache> &pending_calls,
|
||||
const std::shared_ptr<Container> &container)
|
||||
: pending_calls_(pending_calls), container_(container) {}
|
||||
|
||||
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("");
|
||||
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("");
|
||||
|
||||
done->Run();
|
||||
}
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
|
|
|
|||
|
|
@ -23,38 +23,39 @@
|
|||
namespace google {
|
||||
namespace protobuf {
|
||||
class Closure;
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
||||
|
||||
namespace anbox {
|
||||
namespace protobuf {
|
||||
namespace rpc {
|
||||
class Void;
|
||||
} // namespace rpc
|
||||
} // namespace rpc
|
||||
namespace container {
|
||||
class StartContainer;
|
||||
} // namespace container
|
||||
} // namespace protobuf
|
||||
} // namespace container
|
||||
} // namespace protobuf
|
||||
namespace rpc {
|
||||
class PendingCallCache;
|
||||
} // namespace rpc
|
||||
} // namespace rpc
|
||||
namespace container {
|
||||
class Container;
|
||||
class ManagementApiSkeleton {
|
||||
public:
|
||||
ManagementApiSkeleton(const std::shared_ptr<rpc::PendingCallCache> &pending_calls,
|
||||
const std::shared_ptr<Container> &container);
|
||||
~ManagementApiSkeleton();
|
||||
public:
|
||||
ManagementApiSkeleton(
|
||||
const std::shared_ptr<rpc::PendingCallCache> &pending_calls,
|
||||
const std::shared_ptr<Container> &container);
|
||||
~ManagementApiSkeleton();
|
||||
|
||||
void start_container(anbox::protobuf::container::StartContainer const *request,
|
||||
anbox::protobuf::rpc::Void *response,
|
||||
google::protobuf::Closure *done);
|
||||
void start_container(
|
||||
anbox::protobuf::container::StartContainer const *request,
|
||||
anbox::protobuf::rpc::Void *response, google::protobuf::Closure *done);
|
||||
|
||||
private:
|
||||
std::shared_ptr<rpc::PendingCallCache> pending_calls_;
|
||||
std::shared_ptr<Container> container_;
|
||||
private:
|
||||
std::shared_ptr<rpc::PendingCallCache> pending_calls_;
|
||||
std::shared_ptr<Container> container_;
|
||||
};
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -16,55 +16,54 @@
|
|||
*/
|
||||
|
||||
#include "anbox/container/management_api_stub.h"
|
||||
#include "anbox/rpc/channel.h"
|
||||
#include "anbox/logger.h"
|
||||
#include "anbox/rpc/channel.h"
|
||||
|
||||
#include "anbox_rpc.pb.h"
|
||||
#include "anbox_container.pb.h"
|
||||
#include "anbox_rpc.pb.h"
|
||||
|
||||
namespace anbox {
|
||||
namespace container {
|
||||
ManagementApiStub::ManagementApiStub(const std::shared_ptr<rpc::Channel> &channel) :
|
||||
channel_(channel) {
|
||||
}
|
||||
ManagementApiStub::ManagementApiStub(
|
||||
const std::shared_ptr<rpc::Channel> &channel)
|
||||
: channel_(channel) {}
|
||||
|
||||
ManagementApiStub::~ManagementApiStub() {
|
||||
}
|
||||
ManagementApiStub::~ManagementApiStub() {}
|
||||
|
||||
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;
|
||||
auto message_configuration = new protobuf::container::Configuration;
|
||||
protobuf::container::StartContainer message;
|
||||
auto message_configuration = new protobuf::container::Configuration;
|
||||
|
||||
for (const auto item : configuration.bind_mounts) {
|
||||
auto bind_mount_message = message_configuration->add_bind_mounts();
|
||||
bind_mount_message->set_source(item.first);
|
||||
bind_mount_message->set_target(item.second);
|
||||
}
|
||||
for (const auto item : configuration.bind_mounts) {
|
||||
auto bind_mount_message = message_configuration->add_bind_mounts();
|
||||
bind_mount_message->set_source(item.first);
|
||||
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_);
|
||||
start_wait_handle_.expect_result();
|
||||
}
|
||||
{
|
||||
std::lock_guard<decltype(mutex_)> lock(mutex_);
|
||||
start_wait_handle_.expect_result();
|
||||
}
|
||||
|
||||
channel_->call_method("start_container",
|
||||
&message,
|
||||
c->response.get(),
|
||||
google::protobuf::NewCallback(this, &ManagementApiStub::container_started, c.get()));
|
||||
channel_->call_method(
|
||||
"start_container", &message, c->response.get(),
|
||||
google::protobuf::NewCallback(this, &ManagementApiStub::container_started,
|
||||
c.get()));
|
||||
|
||||
start_wait_handle_.wait_for_all();
|
||||
start_wait_handle_.wait_for_all();
|
||||
|
||||
if (c->response->has_error())
|
||||
throw std::runtime_error(c->response->error());
|
||||
if (c->response->has_error()) throw std::runtime_error(c->response->error());
|
||||
}
|
||||
|
||||
void ManagementApiStub::container_started(Request<protobuf::rpc::Void> *request) {
|
||||
(void) request;
|
||||
DEBUG("");
|
||||
start_wait_handle_.result_received();
|
||||
void ManagementApiStub::container_started(
|
||||
Request<protobuf::rpc::Void> *request) {
|
||||
(void)request;
|
||||
DEBUG("");
|
||||
start_wait_handle_.result_received();
|
||||
}
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@
|
|||
#ifndef 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/container/configuration.h"
|
||||
#include "anbox/do_not_copy_or_move.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
|
@ -28,34 +28,34 @@ namespace anbox {
|
|||
namespace protobuf {
|
||||
namespace rpc {
|
||||
class Void;
|
||||
} // namespace rpc
|
||||
} // namespace protobuf
|
||||
} // namespace rpc
|
||||
} // namespace protobuf
|
||||
namespace rpc {
|
||||
class Channel;
|
||||
} // namespace rpc
|
||||
} // namespace rpc
|
||||
namespace container {
|
||||
class ManagementApiStub : public DoNotCopyOrMove {
|
||||
public:
|
||||
ManagementApiStub(const std::shared_ptr<rpc::Channel> &channel);
|
||||
~ManagementApiStub();
|
||||
public:
|
||||
ManagementApiStub(const std::shared_ptr<rpc::Channel> &channel);
|
||||
~ManagementApiStub();
|
||||
|
||||
void start_container(const Configuration &configuration);
|
||||
void start_container(const Configuration &configuration);
|
||||
|
||||
private:
|
||||
template<typename Response>
|
||||
struct Request {
|
||||
Request() : response(std::make_shared<Response>()), success(true) { }
|
||||
std::shared_ptr<Response> response;
|
||||
bool success;
|
||||
};
|
||||
private:
|
||||
template <typename Response>
|
||||
struct Request {
|
||||
Request() : response(std::make_shared<Response>()), success(true) {}
|
||||
std::shared_ptr<Response> response;
|
||||
bool success;
|
||||
};
|
||||
|
||||
void container_started(Request<protobuf::rpc::Void> *request);
|
||||
void container_started(Request<protobuf::rpc::Void> *request);
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
std::shared_ptr<rpc::Channel> channel_;
|
||||
common::WaitHandle start_wait_handle_;
|
||||
mutable std::mutex mutex_;
|
||||
std::shared_ptr<rpc::Channel> channel_;
|
||||
common::WaitHandle start_wait_handle_;
|
||||
};
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -16,76 +16,77 @@
|
|||
*/
|
||||
|
||||
#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_message_processor.h"
|
||||
#include "anbox/network/local_socket_messenger.h"
|
||||
#include "anbox/qemu/null_message_processor.h"
|
||||
#include "anbox/rpc/pending_call_cache.h"
|
||||
#include "anbox/rpc/channel.h"
|
||||
#include "anbox/container/lxc_container.h"
|
||||
#include "anbox/container/management_api_message_processor.h"
|
||||
#include "anbox/container/management_api_skeleton.h"
|
||||
#include "anbox/config.h"
|
||||
#include "anbox/logger.h"
|
||||
#include "anbox/rpc/pending_call_cache.h"
|
||||
|
||||
namespace anbox {
|
||||
namespace container {
|
||||
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>>(
|
||||
[sp](std::shared_ptr<boost::asio::local::stream_protocol::socket> const &socket) {
|
||||
sp->new_client(socket);
|
||||
});
|
||||
auto delegate_connector = std::make_shared<
|
||||
network::DelegateConnectionCreator<boost::asio::local::stream_protocol>>(
|
||||
[sp](std::shared_ptr<boost::asio::local::stream_protocol::socket> const
|
||||
&socket) { sp->new_client(socket); });
|
||||
|
||||
sp->connector_ = std::make_shared<network::PublishedSocketConnector>(
|
||||
config::container_socket_path(), rt, delegate_connector);
|
||||
sp->connector_ = std::make_shared<network::PublishedSocketConnector>(
|
||||
config::container_socket_path(), rt, delegate_connector);
|
||||
|
||||
// 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);
|
||||
// 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);
|
||||
|
||||
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) :
|
||||
dispatcher_(anbox::common::create_dispatcher_for_runtime(rt)),
|
||||
next_connection_id_(0),
|
||||
connections_(std::make_shared<network::Connections<network::SocketConnection>>()) {
|
||||
Service::Service(const std::shared_ptr<Runtime> &rt)
|
||||
: dispatcher_(anbox::common::create_dispatcher_for_runtime(rt)),
|
||||
next_connection_id_(0),
|
||||
connections_(
|
||||
std::make_shared<network::Connections<network::SocketConnection>>()) {
|
||||
}
|
||||
|
||||
Service::~Service() {
|
||||
DEBUG("");
|
||||
Service::~Service() { 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();
|
||||
}
|
||||
|
||||
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
|
||||
} // namespace anbox
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
|
|
|
|||
|
|
@ -18,34 +18,35 @@
|
|||
#ifndef 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/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"
|
||||
|
||||
namespace anbox {
|
||||
namespace container {
|
||||
class Service : public std::enable_shared_from_this<Service> {
|
||||
public:
|
||||
static std::shared_ptr<Service> create(const std::shared_ptr<Runtime> &rt);
|
||||
public:
|
||||
static std::shared_ptr<Service> create(const std::shared_ptr<Runtime> &rt);
|
||||
|
||||
Service(const std::shared_ptr<Runtime> &rt);
|
||||
~Service();
|
||||
Service(const std::shared_ptr<Runtime> &rt);
|
||||
~Service();
|
||||
|
||||
private:
|
||||
int next_id();
|
||||
void new_client(std::shared_ptr<boost::asio::local::stream_protocol::socket> const &socket);
|
||||
private:
|
||||
int next_id();
|
||||
void new_client(std::shared_ptr<
|
||||
boost::asio::local::stream_protocol::socket> const &socket);
|
||||
|
||||
std::shared_ptr<common::Dispatcher> dispatcher_;
|
||||
std::shared_ptr<network::PublishedSocketConnector> connector_;
|
||||
std::atomic<int> next_connection_id_;
|
||||
std::shared_ptr<network::Connections<network::SocketConnection>> connections_;
|
||||
std::shared_ptr<Container> backend_;
|
||||
std::shared_ptr<common::Dispatcher> dispatcher_;
|
||||
std::shared_ptr<network::PublishedSocketConnector> connector_;
|
||||
std::atomic<int> next_connection_id_;
|
||||
std::shared_ptr<network::Connections<network::SocketConnection>> connections_;
|
||||
std::shared_ptr<Container> backend_;
|
||||
};
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
} // namespace container
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -15,38 +15,36 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <sys/prctl.h>
|
||||
#include <signal.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#include "anbox/logger.h"
|
||||
#include "anbox/daemon.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/launch.h"
|
||||
#include "anbox/cmds/run.h"
|
||||
#include "anbox/cmds/version.h"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace anbox {
|
||||
Daemon::Daemon() :
|
||||
cmd{cli::Name{"anbox"}, cli::Usage{"anbox"}, cli::Description{"The Android in a Box runtime"}} {
|
||||
|
||||
cmd.command(std::make_shared<cmds::Version>())
|
||||
.command(std::make_shared<cmds::Run>())
|
||||
.command(std::make_shared<cmds::Launch>())
|
||||
.command(std::make_shared<cmds::ContainerManager>());
|
||||
Daemon::Daemon()
|
||||
: cmd{cli::Name{"anbox"}, cli::Usage{"anbox"},
|
||||
cli::Description{"The Android in a Box runtime"}} {
|
||||
cmd.command(std::make_shared<cmds::Version>())
|
||||
.command(std::make_shared<cmds::Run>())
|
||||
.command(std::make_shared<cmds::Launch>())
|
||||
.command(std::make_shared<cmds::ContainerManager>());
|
||||
}
|
||||
|
||||
int Daemon::Run(const std::vector<std::string> &arguments)
|
||||
try {
|
||||
return cmd.run({std::cin, std::cout, arguments});
|
||||
int Daemon::Run(const std::vector<std::string> &arguments) try {
|
||||
return cmd.run({std::cin, std::cout, arguments});
|
||||
} catch (std::exception &err) {
|
||||
ERROR("%s", err.what());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
catch(std::exception &err) {
|
||||
ERROR("%s", err.what());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
} // namespace anbox
|
||||
} // namespace anbox
|
||||
|
|
|
|||
|
|
@ -18,22 +18,22 @@
|
|||
#ifndef ANBOX_DAEMON_H_
|
||||
#define ANBOX_DAEMON_H_
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "anbox/do_not_copy_or_move.h"
|
||||
#include "anbox/cli.h"
|
||||
#include "anbox/do_not_copy_or_move.h"
|
||||
|
||||
namespace anbox {
|
||||
class Daemon : public DoNotCopyOrMove {
|
||||
public:
|
||||
Daemon();
|
||||
public:
|
||||
Daemon();
|
||||
|
||||
int Run(const std::vector<std::string> &arguments);
|
||||
int Run(const std::vector<std::string> &arguments);
|
||||
|
||||
private:
|
||||
cli::CommandWithSubcommands cmd;
|
||||
private:
|
||||
cli::CommandWithSubcommands cmd;
|
||||
};
|
||||
} // namespace anbox
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -20,42 +20,45 @@
|
|||
|
||||
#include <core/dbus/macros.h>
|
||||
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
namespace anbox {
|
||||
namespace dbus {
|
||||
namespace interface {
|
||||
struct Service {
|
||||
static inline std::string name() { return "org.anbox"; }
|
||||
static inline std::string path() { return "/"; }
|
||||
static inline std::string name() { return "org.anbox"; }
|
||||
static inline std::string path() { return "/"; }
|
||||
};
|
||||
struct ApplicationManager {
|
||||
static inline std::string name() { return "org.anbox.ApplicationManager"; }
|
||||
struct Methods {
|
||||
struct Launch {
|
||||
static inline std::string name() { return "Launch"; }
|
||||
typedef anbox::dbus::interface::ApplicationManager Interface;
|
||||
typedef void ResultType;
|
||||
static inline std::chrono::milliseconds default_timeout() { return std::chrono::seconds{1}; }
|
||||
};
|
||||
static inline std::string name() { return "org.anbox.ApplicationManager"; }
|
||||
struct Methods {
|
||||
struct Launch {
|
||||
static inline std::string name() { return "Launch"; }
|
||||
typedef anbox::dbus::interface::ApplicationManager Interface;
|
||||
typedef void ResultType;
|
||||
static inline std::chrono::milliseconds default_timeout() {
|
||||
return std::chrono::seconds{1};
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
} // namespace interface
|
||||
} // namespace dbus
|
||||
} // namespace anbox
|
||||
} // namespace interface
|
||||
} // namespace dbus
|
||||
} // namespace anbox
|
||||
|
||||
namespace core {
|
||||
namespace dbus {
|
||||
namespace traits {
|
||||
template<> struct Service<anbox::dbus::interface::ApplicationManager> {
|
||||
static inline const std::string& interface_name() {
|
||||
static const std::string s{"org.anbox.ApplicationManager"};
|
||||
return s;
|
||||
}
|
||||
template <>
|
||||
struct Service<anbox::dbus::interface::ApplicationManager> {
|
||||
static inline const std::string& interface_name() {
|
||||
static const std::string s{"org.anbox.ApplicationManager"};
|
||||
return s;
|
||||
}
|
||||
};
|
||||
} // namespace traits
|
||||
} // namespace dbus
|
||||
} // namespace core
|
||||
} // namespace traits
|
||||
} // namespace dbus
|
||||
} // namespace core
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -16,22 +16,20 @@
|
|||
*/
|
||||
|
||||
#include "anbox/dbus/skeleton/application_manager.h"
|
||||
#include "anbox/dbus/interface.h"
|
||||
#include "anbox/android/intent.h"
|
||||
#include "anbox/dbus/interface.h"
|
||||
#include "anbox/logger.h"
|
||||
|
||||
namespace anbox {
|
||||
namespace dbus {
|
||||
namespace skeleton {
|
||||
ApplicationManager::ApplicationManager(const core::dbus::Bus::Ptr &bus,
|
||||
const core::dbus::Object::Ptr& object,
|
||||
const std::shared_ptr<anbox::ApplicationManager> &impl) :
|
||||
bus_(bus),
|
||||
object_(object),
|
||||
impl_(impl) {
|
||||
|
||||
object_->install_method_handler<anbox::dbus::interface::ApplicationManager::Methods::Launch>(
|
||||
[this](const core::dbus::Message::Ptr &msg) {
|
||||
ApplicationManager::ApplicationManager(
|
||||
const core::dbus::Bus::Ptr &bus, const core::dbus::Object::Ptr &object,
|
||||
const std::shared_ptr<anbox::ApplicationManager> &impl)
|
||||
: bus_(bus), object_(object), impl_(impl) {
|
||||
object_->install_method_handler<
|
||||
anbox::dbus::interface::ApplicationManager::Methods::Launch>(
|
||||
[this](const core::dbus::Message::Ptr &msg) {
|
||||
auto reader = msg->reader();
|
||||
|
||||
android::Intent intent;
|
||||
|
|
@ -45,25 +43,22 @@ ApplicationManager::ApplicationManager(const core::dbus::Bus::Ptr &bus,
|
|||
core::dbus::Message::Ptr reply;
|
||||
|
||||
try {
|
||||
launch(intent);
|
||||
reply = core::dbus::Message::make_method_return(msg);
|
||||
}
|
||||
catch (std::exception const &err) {
|
||||
reply = core::dbus::Message::make_error(msg,
|
||||
"org.anbox.Error.Failed",
|
||||
err.what());
|
||||
launch(intent);
|
||||
reply = core::dbus::Message::make_method_return(msg);
|
||||
} catch (std::exception const &err) {
|
||||
reply = core::dbus::Message::make_error(msg, "org.anbox.Error.Failed",
|
||||
err.what());
|
||||
}
|
||||
|
||||
bus_->send(reply);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ApplicationManager::~ApplicationManager() {
|
||||
}
|
||||
ApplicationManager::~ApplicationManager() {}
|
||||
|
||||
void ApplicationManager::launch(const android::Intent &intent) {
|
||||
impl_->launch(intent);
|
||||
impl_->launch(intent);
|
||||
}
|
||||
} // namespace skeleton
|
||||
} // namespace dbus
|
||||
} // namespace anbox
|
||||
} // namespace skeleton
|
||||
} // namespace dbus
|
||||
} // namespace anbox
|
||||
|
|
|
|||
|
|
@ -28,22 +28,22 @@ namespace anbox {
|
|||
namespace dbus {
|
||||
namespace skeleton {
|
||||
class ApplicationManager : public anbox::ApplicationManager {
|
||||
public:
|
||||
ApplicationManager(const core::dbus::Bus::Ptr &bus,
|
||||
const core::dbus::Object::Ptr& object,
|
||||
const std::shared_ptr<anbox::ApplicationManager> &impl);
|
||||
~ApplicationManager();
|
||||
public:
|
||||
ApplicationManager(const core::dbus::Bus::Ptr &bus,
|
||||
const core::dbus::Object::Ptr &object,
|
||||
const std::shared_ptr<anbox::ApplicationManager> &impl);
|
||||
~ApplicationManager();
|
||||
|
||||
void launch(const android::Intent &intent) override;
|
||||
void launch(const android::Intent &intent) override;
|
||||
|
||||
private:
|
||||
core::dbus::Bus::Ptr bus_;
|
||||
core::dbus::Service::Ptr service_;
|
||||
core::dbus::Object::Ptr object_;
|
||||
std::shared_ptr<anbox::ApplicationManager> impl_;
|
||||
private:
|
||||
core::dbus::Bus::Ptr bus_;
|
||||
core::dbus::Service::Ptr service_;
|
||||
core::dbus::Object::Ptr object_;
|
||||
std::shared_ptr<anbox::ApplicationManager> impl_;
|
||||
};
|
||||
} // namespace skeleton
|
||||
} // namespace dbus
|
||||
} // namespace anbox
|
||||
} // namespace skeleton
|
||||
} // namespace dbus
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -16,31 +16,33 @@
|
|||
*/
|
||||
|
||||
#include "anbox/dbus/skeleton/service.h"
|
||||
#include "anbox/dbus/skeleton/application_manager.h"
|
||||
#include "anbox/dbus/interface.h"
|
||||
#include "anbox/dbus/skeleton/application_manager.h"
|
||||
|
||||
namespace anbox {
|
||||
namespace dbus {
|
||||
namespace skeleton {
|
||||
std::shared_ptr<Service> Service::create_for_bus(const core::dbus::Bus::Ptr &bus,
|
||||
const std::shared_ptr<anbox::ApplicationManager> &application_manager) {
|
||||
auto service = core::dbus::Service::add_service(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);
|
||||
std::shared_ptr<Service> Service::create_for_bus(
|
||||
const core::dbus::Bus::Ptr &bus,
|
||||
const std::shared_ptr<anbox::ApplicationManager> &application_manager) {
|
||||
auto service = core::dbus::Service::add_service(
|
||||
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,
|
||||
const core::dbus::Service::Ptr& service,
|
||||
const core::dbus::Object::Ptr& object,
|
||||
const std::shared_ptr<anbox::ApplicationManager> &application_manager) :
|
||||
bus_(bus),
|
||||
service_(service),
|
||||
object_(object),
|
||||
application_manager_(std::make_shared<ApplicationManager>(bus_, object_, application_manager)) {
|
||||
}
|
||||
Service::Service(
|
||||
const core::dbus::Bus::Ptr &bus, const core::dbus::Service::Ptr &service,
|
||||
const core::dbus::Object::Ptr &object,
|
||||
const std::shared_ptr<anbox::ApplicationManager> &application_manager)
|
||||
: bus_(bus),
|
||||
service_(service),
|
||||
object_(object),
|
||||
application_manager_(std::make_shared<ApplicationManager>(
|
||||
bus_, object_, application_manager)) {}
|
||||
|
||||
Service::~Service() {
|
||||
}
|
||||
} // namespace skeleton
|
||||
} // namespace dbus
|
||||
} // namespace anbox
|
||||
Service::~Service() {}
|
||||
} // namespace skeleton
|
||||
} // namespace dbus
|
||||
} // namespace anbox
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@
|
|||
#ifndef 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/do_not_copy_or_move.h"
|
||||
|
||||
#include <core/dbus/bus.h>
|
||||
#include <core/dbus/object.h>
|
||||
|
|
@ -30,24 +30,25 @@ namespace dbus {
|
|||
namespace skeleton {
|
||||
class ApplicationManager;
|
||||
class Service : public DoNotCopyOrMove {
|
||||
public:
|
||||
static std::shared_ptr<Service> create_for_bus(const core::dbus::Bus::Ptr &bus,
|
||||
const std::shared_ptr<anbox::ApplicationManager> &application_manager);
|
||||
public:
|
||||
static std::shared_ptr<Service> create_for_bus(
|
||||
const core::dbus::Bus::Ptr &bus,
|
||||
const std::shared_ptr<anbox::ApplicationManager> &application_manager);
|
||||
|
||||
Service(const core::dbus::Bus::Ptr &bus,
|
||||
const core::dbus::Service::Ptr& service,
|
||||
const core::dbus::Object::Ptr& object,
|
||||
const std::shared_ptr<anbox::ApplicationManager> &application_manager);
|
||||
~Service();
|
||||
Service(
|
||||
const core::dbus::Bus::Ptr &bus, const core::dbus::Service::Ptr &service,
|
||||
const core::dbus::Object::Ptr &object,
|
||||
const std::shared_ptr<anbox::ApplicationManager> &application_manager);
|
||||
~Service();
|
||||
|
||||
private:
|
||||
core::dbus::Bus::Ptr bus_;
|
||||
core::dbus::Service::Ptr service_;
|
||||
core::dbus::Object::Ptr object_;
|
||||
std::shared_ptr<ApplicationManager> application_manager_;
|
||||
private:
|
||||
core::dbus::Bus::Ptr bus_;
|
||||
core::dbus::Service::Ptr service_;
|
||||
core::dbus::Object::Ptr object_;
|
||||
std::shared_ptr<ApplicationManager> application_manager_;
|
||||
};
|
||||
} // namespace skeleton
|
||||
} // namespace dbus
|
||||
} // namespace anbox
|
||||
} // namespace skeleton
|
||||
} // namespace dbus
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -22,37 +22,31 @@
|
|||
namespace anbox {
|
||||
namespace dbus {
|
||||
namespace stub {
|
||||
std::shared_ptr<ApplicationManager> ApplicationManager::create_for_bus(const core::dbus::Bus::Ptr &bus) {
|
||||
auto service = core::dbus::Service::use_service(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);
|
||||
std::shared_ptr<ApplicationManager> ApplicationManager::create_for_bus(
|
||||
const core::dbus::Bus::Ptr &bus) {
|
||||
auto service = core::dbus::Service::use_service(
|
||||
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,
|
||||
const core::dbus::Service::Ptr& service,
|
||||
const core::dbus::Object::Ptr& object) :
|
||||
bus_(bus),
|
||||
service_(service),
|
||||
object_(object) {
|
||||
}
|
||||
const core::dbus::Service::Ptr &service,
|
||||
const core::dbus::Object::Ptr &object)
|
||||
: bus_(bus), service_(service), object_(object) {}
|
||||
|
||||
ApplicationManager::~ApplicationManager() {
|
||||
}
|
||||
ApplicationManager::~ApplicationManager() {}
|
||||
|
||||
void ApplicationManager::launch(const android::Intent &intent) {
|
||||
auto result = object_->invoke_method_synchronously<
|
||||
anbox::dbus::interface::ApplicationManager::Methods::Launch,
|
||||
anbox::dbus::interface::ApplicationManager::Methods::Launch::ResultType>(
|
||||
intent.action,
|
||||
intent.uri,
|
||||
intent.type,
|
||||
intent.flags,
|
||||
intent.package,
|
||||
intent.component);
|
||||
auto result = object_->invoke_method_synchronously<
|
||||
anbox::dbus::interface::ApplicationManager::Methods::Launch,
|
||||
anbox::dbus::interface::ApplicationManager::Methods::Launch::ResultType>(
|
||||
intent.action, intent.uri, intent.type, intent.flags, intent.package,
|
||||
intent.component);
|
||||
|
||||
if (result.is_error())
|
||||
throw std::runtime_error(result.error().print());
|
||||
if (result.is_error()) throw std::runtime_error(result.error().print());
|
||||
}
|
||||
} // namespace skeleton
|
||||
} // namespace dbus
|
||||
} // namespace anbox
|
||||
} // namespace skeleton
|
||||
} // namespace dbus
|
||||
} // namespace anbox
|
||||
|
|
|
|||
|
|
@ -28,23 +28,24 @@ namespace anbox {
|
|||
namespace dbus {
|
||||
namespace stub {
|
||||
class ApplicationManager : public anbox::ApplicationManager {
|
||||
public:
|
||||
static std::shared_ptr<ApplicationManager> create_for_bus(const core::dbus::Bus::Ptr &bus);
|
||||
public:
|
||||
static std::shared_ptr<ApplicationManager> create_for_bus(
|
||||
const core::dbus::Bus::Ptr &bus);
|
||||
|
||||
ApplicationManager(const core::dbus::Bus::Ptr &bus,
|
||||
const core::dbus::Service::Ptr& service,
|
||||
const core::dbus::Object::Ptr& object);
|
||||
~ApplicationManager();
|
||||
ApplicationManager(const core::dbus::Bus::Ptr &bus,
|
||||
const core::dbus::Service::Ptr &service,
|
||||
const core::dbus::Object::Ptr &object);
|
||||
~ApplicationManager();
|
||||
|
||||
void launch(const android::Intent &intent) override;
|
||||
void launch(const android::Intent &intent) override;
|
||||
|
||||
private:
|
||||
core::dbus::Bus::Ptr bus_;
|
||||
core::dbus::Service::Ptr service_;
|
||||
core::dbus::Object::Ptr object_;
|
||||
private:
|
||||
core::dbus::Bus::Ptr bus_;
|
||||
core::dbus::Service::Ptr service_;
|
||||
core::dbus::Object::Ptr object_;
|
||||
};
|
||||
} // namespace stub
|
||||
} // namespace dbus
|
||||
} // namespace anbox
|
||||
} // namespace stub
|
||||
} // namespace dbus
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -25,20 +25,17 @@
|
|||
namespace anbox {
|
||||
|
||||
class DeferAction : public DoNotCopyOrMove {
|
||||
public:
|
||||
DeferAction(const std::function<void()> action) :
|
||||
action_(action) {
|
||||
}
|
||||
public:
|
||||
DeferAction(const std::function<void()> action) : action_(action) {}
|
||||
|
||||
~DeferAction() {
|
||||
if (action_)
|
||||
action_();
|
||||
}
|
||||
~DeferAction() {
|
||||
if (action_) action_();
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void()> action_;
|
||||
private:
|
||||
std::function<void()> action_;
|
||||
};
|
||||
|
||||
} // namespace anbox
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -20,19 +20,17 @@
|
|||
|
||||
namespace anbox {
|
||||
|
||||
class DoNotCopyOrMove
|
||||
{
|
||||
public:
|
||||
DoNotCopyOrMove(const DoNotCopyOrMove&) = delete;
|
||||
DoNotCopyOrMove(DoNotCopyOrMove&&) = delete;
|
||||
virtual ~DoNotCopyOrMove() = default;
|
||||
DoNotCopyOrMove& operator=(const DoNotCopyOrMove&) = delete;
|
||||
DoNotCopyOrMove& operator=(DoNotCopyOrMove&&) = delete;
|
||||
class DoNotCopyOrMove {
|
||||
public:
|
||||
DoNotCopyOrMove(const DoNotCopyOrMove&) = delete;
|
||||
DoNotCopyOrMove(DoNotCopyOrMove&&) = delete;
|
||||
virtual ~DoNotCopyOrMove() = default;
|
||||
DoNotCopyOrMove& operator=(const DoNotCopyOrMove&) = delete;
|
||||
DoNotCopyOrMove& operator=(DoNotCopyOrMove&&) = delete;
|
||||
|
||||
protected:
|
||||
DoNotCopyOrMove() = default;
|
||||
protected:
|
||||
DoNotCopyOrMove() = default;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -21,19 +21,21 @@
|
|||
namespace anbox {
|
||||
namespace graphics {
|
||||
/**
|
||||
* @brief Defines different types of density being used in an Android system. See the
|
||||
* documentation in frameworks/base/core/java/android/util/DisplayMetrics.java
|
||||
* @brief Defines different types of density being used in an Android system.
|
||||
* See the
|
||||
* documentation in
|
||||
* frameworks/base/core/java/android/util/DisplayMetrics.java
|
||||
* of the Android source tree which defines the different types.
|
||||
*/
|
||||
enum class DensityType {
|
||||
low = 120,
|
||||
medium = 160,
|
||||
tv = 213,
|
||||
high = 240,
|
||||
xhigh = 360,
|
||||
xxhigh = 480,
|
||||
low = 120,
|
||||
medium = 160,
|
||||
tv = 213,
|
||||
high = 240,
|
||||
xhigh = 360,
|
||||
xxhigh = 480,
|
||||
};
|
||||
} // namespace graphics
|
||||
} // namespace anbox
|
||||
} // namespace graphics
|
||||
} // namespace anbox
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -30,23 +30,23 @@ namespace {
|
|||
// implemented as unsigned integers. These convenience template functions
|
||||
// help casting between them safely without generating compiler warnings.
|
||||
inline void* SafePointerFromUInt(unsigned int handle) {
|
||||
return (void*)(uintptr_t)(handle);
|
||||
return (void*)(uintptr_t)(handle);
|
||||
}
|
||||
|
||||
inline unsigned int SafeUIntFromPointer(const void* ptr) {
|
||||
#if 1
|
||||
// Ignore the assert below to avoid crashing when running older
|
||||
// system images, which might have buggy encoder libraries. Print
|
||||
// an error message though.
|
||||
if ((uintptr_t)(ptr) != (unsigned int)(uintptr_t)(ptr)) {
|
||||
fprintf(stderr, "EmuGL:WARNING: bad generic pointer %p\n", ptr);
|
||||
}
|
||||
// Ignore the assert below to avoid crashing when running older
|
||||
// system images, which might have buggy encoder libraries. Print
|
||||
// an error message though.
|
||||
if ((uintptr_t)(ptr) != (unsigned int)(uintptr_t)(ptr)) {
|
||||
fprintf(stderr, "EmuGL:WARNING: bad generic pointer %p\n", ptr);
|
||||
}
|
||||
#else
|
||||
// Assertion error if the pointer contains a value that does not fit
|
||||
// in an unsigned integer!
|
||||
assert((uintptr_t)(ptr) == (unsigned int)(uintptr_t)(ptr));
|
||||
// Assertion error if the pointer contains a value that does not fit
|
||||
// in an unsigned integer!
|
||||
assert((uintptr_t)(ptr) == (unsigned int)(uintptr_t)(ptr));
|
||||
#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.
|
||||
|
|
@ -55,31 +55,28 @@ inline unsigned int SafeUIntFromPointer(const void* ptr) {
|
|||
// on creation only. I.e. all rendering operations will target it.
|
||||
// returns true in case of success, false on failure.
|
||||
bool bindFbo(GLuint* fbo, GLuint tex) {
|
||||
if (*fbo) {
|
||||
// fbo already exist - just bind
|
||||
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, *fbo);
|
||||
return true;
|
||||
}
|
||||
|
||||
s_gles2.glGenFramebuffers(1, fbo);
|
||||
if (*fbo) {
|
||||
// fbo already exist - just bind
|
||||
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;
|
||||
}
|
||||
|
||||
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() {
|
||||
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
void unbindFbo() { s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0); }
|
||||
|
||||
// Helper class to use a ColorBuffer::Helper context.
|
||||
// Usage is pretty simple:
|
||||
|
|
@ -93,302 +90,268 @@ void unbindFbo() {
|
|||
// } // automatically calls m_helper->teardownContext();
|
||||
//
|
||||
class ScopedHelperContext {
|
||||
public:
|
||||
ScopedHelperContext(ColorBuffer::Helper* helper) : mHelper(helper) {
|
||||
if (!helper->setupContext()) {
|
||||
mHelper = NULL;
|
||||
}
|
||||
public:
|
||||
ScopedHelperContext(ColorBuffer::Helper* helper) : mHelper(helper) {
|
||||
if (!helper->setupContext()) {
|
||||
mHelper = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool isOk() const { return mHelper != NULL; }
|
||||
bool isOk() const { return mHelper != NULL; }
|
||||
|
||||
~ScopedHelperContext() {
|
||||
release();
|
||||
~ScopedHelperContext() { release(); }
|
||||
|
||||
void release() {
|
||||
if (mHelper) {
|
||||
mHelper->teardownContext();
|
||||
mHelper = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void release() {
|
||||
if (mHelper) {
|
||||
mHelper->teardownContext();
|
||||
mHelper = NULL;
|
||||
}
|
||||
}
|
||||
private:
|
||||
ColorBuffer::Helper* mHelper;
|
||||
private:
|
||||
ColorBuffer::Helper* mHelper;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
ColorBuffer* ColorBuffer::create(EGLDisplay p_display,
|
||||
int p_width,
|
||||
int p_height,
|
||||
GLenum p_internalFormat,
|
||||
bool has_eglimage_texture_2d,
|
||||
Helper* helper) {
|
||||
GLenum texInternalFormat = 0;
|
||||
ColorBuffer* ColorBuffer::create(EGLDisplay p_display, int p_width,
|
||||
int p_height, GLenum p_internalFormat,
|
||||
bool has_eglimage_texture_2d, Helper* helper) {
|
||||
GLenum texInternalFormat = 0;
|
||||
|
||||
switch (p_internalFormat) {
|
||||
case GL_RGB:
|
||||
case GL_RGB565_OES:
|
||||
texInternalFormat = GL_RGB;
|
||||
break;
|
||||
switch (p_internalFormat) {
|
||||
case GL_RGB:
|
||||
case GL_RGB565_OES:
|
||||
texInternalFormat = GL_RGB;
|
||||
break;
|
||||
|
||||
case GL_RGBA:
|
||||
case GL_RGB5_A1_OES:
|
||||
case GL_RGBA4_OES:
|
||||
texInternalFormat = GL_RGBA;
|
||||
break;
|
||||
case GL_RGBA:
|
||||
case GL_RGB5_A1_OES:
|
||||
case GL_RGBA4_OES:
|
||||
texInternalFormat = GL_RGBA;
|
||||
break;
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
ScopedHelperContext context(helper);
|
||||
if (!context.isOk()) {
|
||||
return NULL;
|
||||
}
|
||||
ScopedHelperContext context(helper);
|
||||
if (!context.isOk()) {
|
||||
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.glBindTexture(GL_TEXTURE_2D, cb->m_tex);
|
||||
s_gles2.glGenTextures(1, &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));
|
||||
s_gles2.glTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
texInternalFormat,
|
||||
p_width,
|
||||
p_height,
|
||||
0,
|
||||
texInternalFormat,
|
||||
GL_UNSIGNED_BYTE,
|
||||
zBuff);
|
||||
::free(zBuff);
|
||||
char* zBuff = static_cast<char*>(::calloc(nComp * p_width * p_height, 1));
|
||||
s_gles2.glTexImage2D(GL_TEXTURE_2D, 0, texInternalFormat, 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_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_T, GL_CLAMP_TO_EDGE);
|
||||
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_WRAP_S, 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
|
||||
//
|
||||
s_gles2.glGenTextures(1, &cb->m_blitTex);
|
||||
s_gles2.glBindTexture(GL_TEXTURE_2D, cb->m_blitTex);
|
||||
s_gles2.glTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
texInternalFormat,
|
||||
p_width,
|
||||
p_height,
|
||||
0,
|
||||
texInternalFormat,
|
||||
GL_UNSIGNED_BYTE,
|
||||
NULL);
|
||||
//
|
||||
// create another texture for that colorbuffer for blit
|
||||
//
|
||||
s_gles2.glGenTextures(1, &cb->m_blitTex);
|
||||
s_gles2.glBindTexture(GL_TEXTURE_2D, cb->m_blitTex);
|
||||
s_gles2.glTexImage2D(GL_TEXTURE_2D, 0, 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_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.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);
|
||||
|
||||
cb->m_width = p_width;
|
||||
cb->m_height = p_height;
|
||||
cb->m_internalFormat = texInternalFormat;
|
||||
cb->m_width = p_width;
|
||||
cb->m_height = p_height;
|
||||
cb->m_internalFormat = texInternalFormat;
|
||||
|
||||
if (has_eglimage_texture_2d) {
|
||||
cb->m_eglImage = s_egl.eglCreateImageKHR(
|
||||
p_display,
|
||||
s_egl.eglGetCurrentContext(),
|
||||
EGL_GL_TEXTURE_2D_KHR,
|
||||
(EGLClientBuffer)SafePointerFromUInt(cb->m_tex),
|
||||
NULL);
|
||||
if (has_eglimage_texture_2d) {
|
||||
cb->m_eglImage = s_egl.eglCreateImageKHR(
|
||||
p_display, s_egl.eglGetCurrentContext(), EGL_GL_TEXTURE_2D_KHR,
|
||||
(EGLClientBuffer)SafePointerFromUInt(cb->m_tex), NULL);
|
||||
|
||||
cb->m_blitEGLImage = s_egl.eglCreateImageKHR(
|
||||
p_display,
|
||||
s_egl.eglGetCurrentContext(),
|
||||
EGL_GL_TEXTURE_2D_KHR,
|
||||
(EGLClientBuffer)SafePointerFromUInt(cb->m_blitTex),
|
||||
NULL);
|
||||
}
|
||||
cb->m_blitEGLImage = s_egl.eglCreateImageKHR(
|
||||
p_display, s_egl.eglGetCurrentContext(), 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) :
|
||||
m_tex(0),
|
||||
m_blitTex(0),
|
||||
m_eglImage(NULL),
|
||||
m_blitEGLImage(NULL),
|
||||
m_fbo(0),
|
||||
m_internalFormat(0),
|
||||
m_display(display),
|
||||
m_helper(helper) {}
|
||||
ColorBuffer::ColorBuffer(EGLDisplay display, Helper* helper)
|
||||
: m_tex(0),
|
||||
m_blitTex(0),
|
||||
m_eglImage(NULL),
|
||||
m_blitEGLImage(NULL),
|
||||
m_fbo(0),
|
||||
m_internalFormat(0),
|
||||
m_display(display),
|
||||
m_helper(helper) {}
|
||||
|
||||
ColorBuffer::~ColorBuffer() {
|
||||
ScopedHelperContext context(m_helper);
|
||||
ScopedHelperContext context(m_helper);
|
||||
|
||||
if (m_blitEGLImage) {
|
||||
s_egl.eglDestroyImageKHR(m_display, m_blitEGLImage);
|
||||
}
|
||||
if (m_eglImage) {
|
||||
s_egl.eglDestroyImageKHR(m_display, m_eglImage);
|
||||
}
|
||||
if (m_blitEGLImage) {
|
||||
s_egl.eglDestroyImageKHR(m_display, m_blitEGLImage);
|
||||
}
|
||||
if (m_eglImage) {
|
||||
s_egl.eglDestroyImageKHR(m_display, m_eglImage);
|
||||
}
|
||||
|
||||
if (m_fbo) {
|
||||
s_gles2.glDeleteFramebuffers(1, &m_fbo);
|
||||
}
|
||||
if (m_fbo) {
|
||||
s_gles2.glDeleteFramebuffers(1, &m_fbo);
|
||||
}
|
||||
|
||||
GLuint tex[2] = {m_tex, m_blitTex};
|
||||
s_gles2.glDeleteTextures(2, tex);
|
||||
GLuint tex[2] = {m_tex, m_blitTex};
|
||||
s_gles2.glDeleteTextures(2, tex);
|
||||
|
||||
delete m_resizer;
|
||||
delete m_resizer;
|
||||
}
|
||||
|
||||
void ColorBuffer::readPixels(int x,
|
||||
int y,
|
||||
int width,
|
||||
int height,
|
||||
GLenum p_format,
|
||||
GLenum p_type,
|
||||
void* pixels) {
|
||||
ScopedHelperContext context(m_helper);
|
||||
if (!context.isOk()) {
|
||||
return;
|
||||
}
|
||||
void ColorBuffer::readPixels(int x, int y, int width, int height,
|
||||
GLenum p_format, GLenum p_type, void* pixels) {
|
||||
ScopedHelperContext context(m_helper);
|
||||
if (!context.isOk()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bindFbo(&m_fbo, m_tex)) {
|
||||
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]);
|
||||
if (bindFbo(&m_fbo, m_tex)) {
|
||||
s_gles2.glReadPixels(x, y, width, height, p_format, p_type, pixels);
|
||||
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() {
|
||||
if (!m_eglImage) {
|
||||
return false;
|
||||
}
|
||||
RenderThreadInfo *tInfo = RenderThreadInfo::get();
|
||||
if (!tInfo->currContext.Ptr()) {
|
||||
return false;
|
||||
}
|
||||
if (tInfo->currContext->isGL2()) {
|
||||
s_gles2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage);
|
||||
}
|
||||
else {
|
||||
s_gles1.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage);
|
||||
}
|
||||
return true;
|
||||
if (!m_eglImage) {
|
||||
return false;
|
||||
}
|
||||
RenderThreadInfo* tInfo = RenderThreadInfo::get();
|
||||
if (!tInfo->currContext.Ptr()) {
|
||||
return false;
|
||||
}
|
||||
if (tInfo->currContext->isGL2()) {
|
||||
s_gles2.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage);
|
||||
} else {
|
||||
s_gles1.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ColorBuffer::bindToRenderbuffer() {
|
||||
if (!m_eglImage) {
|
||||
return false;
|
||||
}
|
||||
RenderThreadInfo *tInfo = RenderThreadInfo::get();
|
||||
if (!tInfo->currContext.Ptr()) {
|
||||
return false;
|
||||
}
|
||||
if (tInfo->currContext->isGL2()) {
|
||||
s_gles2.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES, m_eglImage);
|
||||
}
|
||||
else {
|
||||
s_gles1.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES, m_eglImage);
|
||||
}
|
||||
return true;
|
||||
if (!m_eglImage) {
|
||||
return false;
|
||||
}
|
||||
RenderThreadInfo* tInfo = RenderThreadInfo::get();
|
||||
if (!tInfo->currContext.Ptr()) {
|
||||
return false;
|
||||
}
|
||||
if (tInfo->currContext->isGL2()) {
|
||||
s_gles2.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES,
|
||||
m_eglImage);
|
||||
} else {
|
||||
s_gles1.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER_OES,
|
||||
m_eglImage);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ColorBuffer::readback(unsigned char* img) {
|
||||
ScopedHelperContext context(m_helper);
|
||||
if (!context.isOk()) {
|
||||
return;
|
||||
}
|
||||
if (bindFbo(&m_fbo, m_tex)) {
|
||||
s_gles2.glReadPixels(
|
||||
0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, img);
|
||||
unbindFbo();
|
||||
}
|
||||
ScopedHelperContext context(m_helper);
|
||||
if (!context.isOk()) {
|
||||
return;
|
||||
}
|
||||
if (bindFbo(&m_fbo, m_tex)) {
|
||||
s_gles2.glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
img);
|
||||
unbindFbo();
|
||||
}
|
||||
}
|
||||
|
||||
void ColorBuffer::bind() {
|
||||
const auto id = m_resizer->update(m_tex);
|
||||
s_gles2.glBindTexture(GL_TEXTURE_2D, id);
|
||||
const auto id = m_resizer->update(m_tex);
|
||||
s_gles2.glBindTexture(GL_TEXTURE_2D, id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,96 +55,84 @@ class TextureResize;
|
|||
// As an additional twist.
|
||||
|
||||
class ColorBuffer {
|
||||
public:
|
||||
// Helper interface class used during ColorBuffer operations. This is
|
||||
// introduced to remove coupling from the FrameBuffer class implementation.
|
||||
class Helper {
|
||||
public:
|
||||
Helper() {}
|
||||
virtual ~Helper() {}
|
||||
virtual bool setupContext() = 0;
|
||||
virtual void teardownContext() = 0;
|
||||
virtual TextureDraw* getTextureDraw() const = 0;
|
||||
};
|
||||
public:
|
||||
// Helper interface class used during ColorBuffer operations. This is
|
||||
// introduced to remove coupling from the FrameBuffer class implementation.
|
||||
class Helper {
|
||||
public:
|
||||
Helper() {}
|
||||
virtual ~Helper() {}
|
||||
virtual bool setupContext() = 0;
|
||||
virtual void teardownContext() = 0;
|
||||
virtual TextureDraw* getTextureDraw() const = 0;
|
||||
};
|
||||
|
||||
// Create a new ColorBuffer instance.
|
||||
// |p_display| is the host EGLDisplay handle.
|
||||
// |p_width| and |p_height| are the buffer's dimensions in pixels.
|
||||
// |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.
|
||||
// Implementation is free to use something else though.
|
||||
// |has_eglimage_texture_2d| should be true iff the display supports
|
||||
// the EGL_KHR_gl_texture_2D_image extension.
|
||||
// Returns NULL on failure.
|
||||
static ColorBuffer* create(EGLDisplay p_display,
|
||||
int p_width,
|
||||
int p_height,
|
||||
GLenum p_internalFormat,
|
||||
bool has_eglimage_texture_2d,
|
||||
Helper* helper);
|
||||
// Create a new ColorBuffer instance.
|
||||
// |p_display| is the host EGLDisplay handle.
|
||||
// |p_width| and |p_height| are the buffer's dimensions in pixels.
|
||||
// |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.
|
||||
// Implementation is free to use something else though.
|
||||
// |has_eglimage_texture_2d| should be true iff the display supports
|
||||
// the EGL_KHR_gl_texture_2D_image extension.
|
||||
// Returns NULL on failure.
|
||||
static ColorBuffer* create(EGLDisplay p_display, int p_width, int p_height,
|
||||
GLenum p_internalFormat,
|
||||
bool has_eglimage_texture_2d, Helper* helper);
|
||||
|
||||
// Destructor.
|
||||
~ColorBuffer();
|
||||
// Destructor.
|
||||
~ColorBuffer();
|
||||
|
||||
// Return ColorBuffer width and height in pixels
|
||||
GLuint getWidth() const { return m_width; }
|
||||
GLuint getHeight() const { return m_height; }
|
||||
// Return ColorBuffer width and height in pixels
|
||||
GLuint getWidth() const { return m_width; }
|
||||
GLuint getHeight() const { return m_height; }
|
||||
|
||||
// Read the ColorBuffer instance's pixel values into host memory.
|
||||
void readPixels(int x,
|
||||
int y,
|
||||
int width,
|
||||
int height,
|
||||
GLenum p_format,
|
||||
GLenum p_type,
|
||||
void *pixels);
|
||||
// Read the ColorBuffer instance's pixel values into host memory.
|
||||
void readPixels(int x, int y, int width, int height, GLenum p_format,
|
||||
GLenum p_type, void* pixels);
|
||||
|
||||
// Update the ColorBuffer instance's pixel values from host memory.
|
||||
void subUpdate(int x,
|
||||
int y,
|
||||
int width,
|
||||
int height,
|
||||
GLenum p_format,
|
||||
GLenum p_type,
|
||||
void *pixels);
|
||||
// Update the ColorBuffer instance's pixel values from host memory.
|
||||
void subUpdate(int x, int y, 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
|
||||
// EGLImage. This is intended to implement glEGLImageTargetTexture2DOES()
|
||||
// for all GLES versions.
|
||||
bool bindToTexture();
|
||||
// Bind the current context's EGL_TEXTURE_2D texture to this ColorBuffer's
|
||||
// EGLImage. This is intended to implement glEGLImageTargetTexture2DOES()
|
||||
// for all GLES versions.
|
||||
bool bindToTexture();
|
||||
|
||||
// Bind the current context's EGL_RENDERBUFFER_OES render buffer to this
|
||||
// ColorBuffer's EGLImage. This is intended to implement
|
||||
// glEGLImageTargetRenderbufferStorageOES() for all GLES versions.
|
||||
bool bindToRenderbuffer();
|
||||
// Bind the current context's EGL_RENDERBUFFER_OES render buffer to this
|
||||
// ColorBuffer's EGLImage. This is intended to implement
|
||||
// glEGLImageTargetRenderbufferStorageOES() for all GLES versions.
|
||||
bool bindToRenderbuffer();
|
||||
|
||||
// Copy the content of the current context's read surface to this
|
||||
// ColorBuffer. This is used from WindowSurface::flushColorBuffer().
|
||||
// Return true on success, false on failure (e.g. no current context).
|
||||
bool blitFromCurrentReadBuffer();
|
||||
// Copy the content of the current context's read surface to this
|
||||
// ColorBuffer. This is used from WindowSurface::flushColorBuffer().
|
||||
// Return true on success, false on failure (e.g. no current context).
|
||||
bool blitFromCurrentReadBuffer();
|
||||
|
||||
// Read the content of the whole ColorBuffer as 32-bit RGBA pixels.
|
||||
// |img| must be a buffer large enough (i.e. width * height * 4).
|
||||
void readback(unsigned char* img);
|
||||
// Read the content of the whole ColorBuffer as 32-bit RGBA pixels.
|
||||
// |img| must be a buffer large enough (i.e. width * height * 4).
|
||||
void readback(unsigned char* img);
|
||||
|
||||
void bind();
|
||||
private:
|
||||
ColorBuffer(); // no default constructor.
|
||||
void bind();
|
||||
|
||||
explicit ColorBuffer(EGLDisplay display, Helper* helper);
|
||||
private:
|
||||
ColorBuffer(); // no default constructor.
|
||||
|
||||
private:
|
||||
GLuint m_tex;
|
||||
GLuint m_blitTex;
|
||||
EGLImageKHR m_eglImage;
|
||||
EGLImageKHR m_blitEGLImage;
|
||||
GLuint m_width;
|
||||
GLuint m_height;
|
||||
GLuint m_fbo;
|
||||
GLenum m_internalFormat;
|
||||
EGLDisplay m_display;
|
||||
Helper* m_helper;
|
||||
TextureResize * m_resizer;
|
||||
explicit ColorBuffer(EGLDisplay display, Helper* helper);
|
||||
|
||||
private:
|
||||
GLuint m_tex;
|
||||
GLuint m_blitTex;
|
||||
EGLImageKHR m_eglImage;
|
||||
EGLImageKHR m_blitEGLImage;
|
||||
GLuint m_width;
|
||||
GLuint m_height;
|
||||
GLuint m_fbo;
|
||||
GLenum m_internalFormat;
|
||||
EGLDisplay m_display;
|
||||
Helper* m_helper;
|
||||
TextureResize* m_resizer;
|
||||
};
|
||||
|
||||
typedef emugl::SmartPtr<ColorBuffer> ColorBufferPtr;
|
||||
|
|
|
|||
|
|
@ -21,22 +21,18 @@ namespace {
|
|||
std::shared_ptr<DisplayManager> display_mgr;
|
||||
|
||||
class NullDisplayManager : public DisplayManager {
|
||||
public:
|
||||
DisplayInfo display_info() const override {
|
||||
return {1280, 720};
|
||||
}
|
||||
public:
|
||||
DisplayInfo display_info() const override { return {1280, 720}; }
|
||||
};
|
||||
}
|
||||
|
||||
DisplayManager::~DisplayManager() {
|
||||
}
|
||||
DisplayManager::~DisplayManager() {}
|
||||
|
||||
std::shared_ptr<DisplayManager> DisplayManager::get() {
|
||||
if (!display_mgr)
|
||||
display_mgr = std::make_shared<NullDisplayManager>();
|
||||
return display_mgr;
|
||||
if (!display_mgr) display_mgr = std::make_shared<NullDisplayManager>();
|
||||
return display_mgr;
|
||||
}
|
||||
|
||||
void registerDisplayManager(const std::shared_ptr<DisplayManager> &mgr) {
|
||||
display_mgr = mgr;
|
||||
display_mgr = mgr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,17 +21,17 @@
|
|||
#include <memory>
|
||||
|
||||
class DisplayManager {
|
||||
public:
|
||||
virtual ~DisplayManager();
|
||||
public:
|
||||
virtual ~DisplayManager();
|
||||
|
||||
struct DisplayInfo {
|
||||
int horizontal_resolution;
|
||||
int vertical_resolution;
|
||||
};
|
||||
struct DisplayInfo {
|
||||
int horizontal_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);
|
||||
|
|
|
|||
|
|
@ -14,61 +14,54 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
#include "ReadBuffer.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include "ErrorLog.h"
|
||||
|
||||
ReadBuffer::ReadBuffer(size_t bufsize)
|
||||
{
|
||||
m_size = bufsize;
|
||||
m_buf = (unsigned char*)malloc(m_size*sizeof(unsigned char));
|
||||
m_validData = 0;
|
||||
m_readPtr = m_buf;
|
||||
ReadBuffer::ReadBuffer(size_t bufsize) {
|
||||
m_size = bufsize;
|
||||
m_buf = (unsigned char*)malloc(m_size * sizeof(unsigned char));
|
||||
m_validData = 0;
|
||||
m_readPtr = m_buf;
|
||||
}
|
||||
|
||||
ReadBuffer::~ReadBuffer()
|
||||
{
|
||||
free(m_buf);
|
||||
}
|
||||
ReadBuffer::~ReadBuffer() { free(m_buf); }
|
||||
|
||||
int ReadBuffer::getData(IOStream *stream)
|
||||
{
|
||||
if(stream == NULL)
|
||||
return -1;
|
||||
if ((m_validData > 0) && (m_readPtr > m_buf)) {
|
||||
memmove(m_buf, m_readPtr, m_validData);
|
||||
int ReadBuffer::getData(IOStream* stream) {
|
||||
if (stream == NULL) return -1;
|
||||
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);
|
||||
if (!new_buf) {
|
||||
ERR("Failed to alloc %zu bytes for ReadBuffer\n", new_size);
|
||||
return -1;
|
||||
}
|
||||
m_size = new_size;
|
||||
m_buf = new_buf;
|
||||
len = m_size - m_validData;
|
||||
new_buf = (unsigned char*)realloc(m_buf, new_size);
|
||||
if (!new_buf) {
|
||||
ERR("Failed to alloc %zu bytes for ReadBuffer\n", new_size);
|
||||
return -1;
|
||||
}
|
||||
m_readPtr = m_buf;
|
||||
if (NULL != stream->read(m_buf + m_validData, &len)) {
|
||||
m_validData += len;
|
||||
return len;
|
||||
}
|
||||
return -1;
|
||||
m_size = new_size;
|
||||
m_buf = new_buf;
|
||||
len = m_size - m_validData;
|
||||
}
|
||||
m_readPtr = m_buf;
|
||||
if (NULL != stream->read(m_buf + m_validData, &len)) {
|
||||
m_validData += len;
|
||||
return len;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ReadBuffer::consume(size_t amount)
|
||||
{
|
||||
assert(amount <= m_validData);
|
||||
m_validData -= amount;
|
||||
m_readPtr += amount;
|
||||
void ReadBuffer::consume(size_t amount) {
|
||||
assert(amount <= m_validData);
|
||||
m_validData -= amount;
|
||||
m_readPtr += amount;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,17 +19,19 @@
|
|||
#include "IOStream.h"
|
||||
|
||||
class ReadBuffer {
|
||||
public:
|
||||
ReadBuffer(size_t bufSize);
|
||||
~ReadBuffer();
|
||||
int getData(IOStream *stream); // get fresh data from the stream
|
||||
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
|
||||
void consume(size_t amount); // notify that 'amount' data has been consumed;
|
||||
private:
|
||||
unsigned char *m_buf;
|
||||
unsigned char *m_readPtr;
|
||||
size_t m_size;
|
||||
size_t m_validData;
|
||||
public:
|
||||
ReadBuffer(size_t bufSize);
|
||||
~ReadBuffer();
|
||||
int getData(IOStream *stream); // get fresh data from the stream
|
||||
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
|
||||
void consume(size_t amount); // notify that 'amount' data has been consumed;
|
||||
private:
|
||||
unsigned char *m_buf;
|
||||
unsigned char *m_readPtr;
|
||||
size_t m_size;
|
||||
size_t m_validData;
|
||||
};
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -41,210 +41,200 @@ static char s_renderAddr[256];
|
|||
|
||||
static RenderWindow* s_renderWindow = NULL;
|
||||
|
||||
static IOStream *createRenderThread(int p_stream_buffer_size,
|
||||
static IOStream* createRenderThread(int p_stream_buffer_size,
|
||||
unsigned int clientFlags);
|
||||
|
||||
RENDER_APICALL int RENDER_APIENTRY initLibrary(void)
|
||||
{
|
||||
//
|
||||
// Load EGL Plugin
|
||||
//
|
||||
if (!init_egl_dispatch()) {
|
||||
// Failed to load EGL
|
||||
printf("Failed to init_egl_dispatch\n");
|
||||
return false;
|
||||
}
|
||||
RENDER_APICALL int RENDER_APIENTRY initLibrary(void) {
|
||||
//
|
||||
// Load EGL Plugin
|
||||
//
|
||||
if (!init_egl_dispatch()) {
|
||||
// Failed to load EGL
|
||||
printf("Failed to init_egl_dispatch\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Load GLES Plugin
|
||||
//
|
||||
if (!gles1_dispatch_init(&s_gles1)) {
|
||||
// Failed to load GLES
|
||||
ERR("Failed to gles1_dispatch_init\n");
|
||||
return false;
|
||||
}
|
||||
//
|
||||
// Load GLES Plugin
|
||||
//
|
||||
if (!gles1_dispatch_init(&s_gles1)) {
|
||||
// Failed to load GLES
|
||||
ERR("Failed to gles1_dispatch_init\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* failure to init the GLES2 dispatch table is not fatal */
|
||||
if (!gles2_dispatch_init(&s_gles2)) {
|
||||
ERR("Failed to gles2_dispatch_init\n");
|
||||
return false;
|
||||
}
|
||||
/* failure to init the GLES2 dispatch table is not fatal */
|
||||
if (!gles2_dispatch_init(&s_gles2)) {
|
||||
ERR("Failed to gles2_dispatch_init\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
RENDER_APICALL int RENDER_APIENTRY initOpenGLRenderer(
|
||||
EGLNativeDisplayType native_display, char* addr, size_t addrLen,
|
||||
emugl_logger_struct logfuncs, emugl_crash_func_t crashfunc) {
|
||||
set_emugl_crash_reporter(crashfunc);
|
||||
set_emugl_logger(logfuncs.coarse);
|
||||
set_emugl_cxt_logger(logfuncs.fine);
|
||||
//
|
||||
// Fail if renderer is already initialized
|
||||
//
|
||||
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__);
|
||||
EGLNativeDisplayType native_display, char* addr, size_t addrLen,
|
||||
emugl_logger_struct logfuncs, emugl_crash_func_t crashfunc) {
|
||||
set_emugl_crash_reporter(crashfunc);
|
||||
set_emugl_logger(logfuncs.coarse);
|
||||
set_emugl_cxt_logger(logfuncs.fine);
|
||||
//
|
||||
// Fail if renderer is already initialized
|
||||
//
|
||||
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 bool RENDER_APIENTRY destroyOpenGLSubwindow(void)
|
||||
{
|
||||
RenderWindow* window = s_renderWindow;
|
||||
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;
|
||||
}
|
||||
|
||||
if (window) {
|
||||
return window->removeSubWindow();
|
||||
}
|
||||
RENDER_APICALL int RENDER_APIENTRY stopOpenGLRenderer(void) {
|
||||
bool ret = false;
|
||||
|
||||
// XXX: should be implemented by sending the renderer process
|
||||
// a request
|
||||
ERR("%s not implemented for separate renderer process !!!\n",
|
||||
__FUNCTION__);
|
||||
return 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;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
int gRendererStreamMode = DEFAULT_STREAM_MODE;
|
||||
|
||||
IOStream *createRenderThread(int p_stream_buffer_size, unsigned int clientFlags)
|
||||
{
|
||||
SocketStream* stream = NULL;
|
||||
IOStream* createRenderThread(int p_stream_buffer_size,
|
||||
unsigned int clientFlags) {
|
||||
SocketStream* stream = NULL;
|
||||
|
||||
if (gRendererStreamMode == RENDER_API_STREAM_MODE_TCP) {
|
||||
stream = new TcpStream(p_stream_buffer_size);
|
||||
} else {
|
||||
stream = new UnixStream(p_stream_buffer_size);
|
||||
}
|
||||
if (gRendererStreamMode == RENDER_API_STREAM_MODE_TCP) {
|
||||
stream = new TcpStream(p_stream_buffer_size);
|
||||
} else {
|
||||
stream = new UnixStream(p_stream_buffer_size);
|
||||
}
|
||||
|
||||
if (!stream) {
|
||||
ERR("createRenderThread failed to create stream\n");
|
||||
return NULL;
|
||||
}
|
||||
if (stream->connect(s_renderAddr) < 0) {
|
||||
ERR("createRenderThread failed to connect\n");
|
||||
delete stream;
|
||||
return NULL;
|
||||
}
|
||||
if (!stream) {
|
||||
ERR("createRenderThread failed to create stream\n");
|
||||
return NULL;
|
||||
}
|
||||
if (stream->connect(s_renderAddr) < 0) {
|
||||
ERR("createRenderThread failed to connect\n");
|
||||
delete stream;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//
|
||||
// send clientFlags to the renderer
|
||||
//
|
||||
unsigned int *pClientFlags =
|
||||
(unsigned int *)stream->allocBuffer(sizeof(unsigned int));
|
||||
*pClientFlags = clientFlags;
|
||||
stream->commitBuffer(sizeof(unsigned int));
|
||||
//
|
||||
// send clientFlags to the renderer
|
||||
//
|
||||
unsigned int* pClientFlags =
|
||||
(unsigned int*)stream->allocBuffer(sizeof(unsigned int));
|
||||
*pClientFlags = clientFlags;
|
||||
stream->commitBuffer(sizeof(unsigned int));
|
||||
|
||||
return stream;
|
||||
return stream;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,33 +17,24 @@
|
|||
|
||||
#include "OpenGLESDispatch/EGLDispatch.h"
|
||||
|
||||
RenderContext* RenderContext::create(EGLDisplay display,
|
||||
EGLConfig config,
|
||||
EGLContext sharedContext,
|
||||
bool isGl2) {
|
||||
const EGLint contextAttribs[] = {
|
||||
EGL_CONTEXT_CLIENT_VERSION, isGl2 ? 2 : 1,
|
||||
EGL_NONE
|
||||
};
|
||||
EGLContext context = s_egl.eglCreateContext(
|
||||
display, config, sharedContext, contextAttribs);
|
||||
if (context == EGL_NO_CONTEXT) {
|
||||
return NULL;
|
||||
}
|
||||
RenderContext* RenderContext::create(EGLDisplay display, EGLConfig config,
|
||||
EGLContext sharedContext, bool isGl2) {
|
||||
const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, isGl2 ? 2 : 1,
|
||||
EGL_NONE};
|
||||
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,
|
||||
EGLContext context,
|
||||
bool isGl2) :
|
||||
mDisplay(display),
|
||||
mContext(context),
|
||||
mIsGl2(isGl2),
|
||||
mContextData() {}
|
||||
RenderContext::RenderContext(EGLDisplay display, EGLContext context, bool isGl2)
|
||||
: mDisplay(display), mContext(context), mIsGl2(isGl2), mContextData() {}
|
||||
|
||||
RenderContext::~RenderContext() {
|
||||
if (mContext != EGL_NO_CONTEXT) {
|
||||
s_egl.eglDestroyContext(mDisplay, mContext);
|
||||
}
|
||||
if (mContext != EGL_NO_CONTEXT) {
|
||||
s_egl.eglDestroyContext(mDisplay, mContext);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@
|
|||
#ifndef _LIBRENDER_RENDER_CONTEXT_H
|
||||
#define _LIBRENDER_RENDER_CONTEXT_H
|
||||
|
||||
#include "emugl/common/smart_ptr.h"
|
||||
#include "GLDecoderContextData.h"
|
||||
#include "emugl/common/smart_ptr.h"
|
||||
|
||||
#include <EGL/egl.h>
|
||||
|
||||
|
|
@ -25,43 +25,39 @@
|
|||
// EGLContext, associated with an GLDecoderContextData instance that is
|
||||
// used to store copies of guest-side arrays.
|
||||
class RenderContext {
|
||||
public:
|
||||
// Create a new RenderContext instance.
|
||||
// |display| is the host EGLDisplay handle.
|
||||
// |config| is the host EGLConfig to use.
|
||||
// |sharedContext| is either EGL_NO_CONTEXT of a host EGLContext handle.
|
||||
// |isGl2| is true iff the new context will be used with GLESv2, or
|
||||
// GLESv1 otherwise.
|
||||
static RenderContext *create(EGLDisplay display,
|
||||
EGLConfig config,
|
||||
EGLContext sharedContext,
|
||||
bool isGL2 = false);
|
||||
public:
|
||||
// Create a new RenderContext instance.
|
||||
// |display| is the host EGLDisplay handle.
|
||||
// |config| is the host EGLConfig to use.
|
||||
// |sharedContext| is either EGL_NO_CONTEXT of a host EGLContext handle.
|
||||
// |isGl2| is true iff the new context will be used with GLESv2, or
|
||||
// GLESv1 otherwise.
|
||||
static RenderContext* create(EGLDisplay display, EGLConfig config,
|
||||
EGLContext sharedContext, bool isGL2 = false);
|
||||
|
||||
// Destructor.
|
||||
~RenderContext();
|
||||
// Destructor.
|
||||
~RenderContext();
|
||||
|
||||
// Retrieve host EGLContext value.
|
||||
EGLContext getEGLContext() const { return mContext; }
|
||||
// Retrieve host EGLContext value.
|
||||
EGLContext getEGLContext() const { return mContext; }
|
||||
|
||||
// Return true iff this is a GLESv2 context.
|
||||
bool isGL2() const { return mIsGl2; }
|
||||
// Return true iff this is a GLESv2 context.
|
||||
bool isGL2() const { return mIsGl2; }
|
||||
|
||||
// Retrieve GLDecoderContextData instance reference for this
|
||||
// RenderContext instance.
|
||||
GLDecoderContextData& decoderContextData() { return mContextData; }
|
||||
// Retrieve GLDecoderContextData instance reference for this
|
||||
// RenderContext instance.
|
||||
GLDecoderContextData& decoderContextData() { return mContextData; }
|
||||
|
||||
private:
|
||||
RenderContext();
|
||||
private:
|
||||
RenderContext();
|
||||
|
||||
RenderContext(EGLDisplay display,
|
||||
EGLContext context,
|
||||
bool isGl2);
|
||||
RenderContext(EGLDisplay display, EGLContext context, bool isGl2);
|
||||
|
||||
private:
|
||||
EGLDisplay mDisplay;
|
||||
EGLContext mContext;
|
||||
bool mIsGl2;
|
||||
GLDecoderContextData mContextData;
|
||||
private:
|
||||
EGLDisplay mDisplay;
|
||||
EGLContext mContext;
|
||||
bool mIsGl2;
|
||||
GLDecoderContextData mContextData;
|
||||
};
|
||||
|
||||
typedef emugl::SmartPtr<RenderContext> RenderContextPtr;
|
||||
|
|
|
|||
|
|
@ -15,12 +15,12 @@
|
|||
*/
|
||||
#include "RenderControl.h"
|
||||
|
||||
#include "DispatchTables.h"
|
||||
#include "RendererConfig.h"
|
||||
#include "Renderer.h"
|
||||
#include "RenderThreadInfo.h"
|
||||
#include "ChecksumCalculatorThreadInfo.h"
|
||||
#include "DispatchTables.h"
|
||||
#include "DisplayManager.h"
|
||||
#include "RenderThreadInfo.h"
|
||||
#include "Renderer.h"
|
||||
#include "RendererConfig.h"
|
||||
|
||||
#include "OpenGLESDispatch/EGLDispatch.h"
|
||||
|
||||
|
|
@ -33,471 +33,428 @@
|
|||
static const GLint rendererVersion = 1;
|
||||
static std::shared_ptr<anbox::graphics::LayerComposer> composer;
|
||||
|
||||
void registerLayerComposer(const std::shared_ptr<anbox::graphics::LayerComposer> &c)
|
||||
{
|
||||
composer = c;
|
||||
void registerLayerComposer(
|
||||
const std::shared_ptr<anbox::graphics::LayerComposer> &c) {
|
||||
composer = c;
|
||||
}
|
||||
|
||||
static GLint rcGetRendererVersion()
|
||||
{
|
||||
return rendererVersion;
|
||||
static GLint rcGetRendererVersion() { 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)
|
||||
{
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return EGL_FALSE;
|
||||
}
|
||||
*major = (EGLint)fb->getCaps().eglMajor;
|
||||
*minor = (EGLint)fb->getCaps().eglMinor;
|
||||
static EGLint rcQueryEGLString(EGLenum name, void *buffer, EGLint bufferSize) {
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return 0;
|
||||
static EGLint rcGetGLString(EGLenum name, void *buffer, EGLint bufferSize) {
|
||||
RenderThreadInfo *tInfo = RenderThreadInfo::get();
|
||||
std::string result;
|
||||
|
||||
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);
|
||||
if (!str) {
|
||||
return 0;
|
||||
}
|
||||
result = approved_extensions;
|
||||
}
|
||||
|
||||
int len = strlen(str) + 1;
|
||||
if (!buffer || len > bufferSize) {
|
||||
return -len;
|
||||
}
|
||||
int nextBufferSize = result.size() + 1;
|
||||
|
||||
strcpy((char *)buffer, str);
|
||||
return len;
|
||||
if (!buffer || nextBufferSize > bufferSize) return -nextBufferSize;
|
||||
|
||||
snprintf(static_cast<char *>(buffer), nextBufferSize, "%s", result.c_str());
|
||||
return nextBufferSize;
|
||||
}
|
||||
|
||||
static EGLint rcGetGLString(EGLenum name, void* buffer, EGLint bufferSize)
|
||||
{
|
||||
RenderThreadInfo *tInfo = RenderThreadInfo::get();
|
||||
std::string result;
|
||||
static EGLint rcGetNumConfigs(uint32_t *p_numAttribs) {
|
||||
int numConfigs = 0, numAttribs = 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(), "");
|
||||
}
|
||||
|
||||
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;
|
||||
Renderer::get()->getConfigs()->getPackInfo(&numConfigs, &numAttribs);
|
||||
if (p_numAttribs) {
|
||||
*p_numAttribs = static_cast<uint32_t>(numAttribs);
|
||||
}
|
||||
return numConfigs;
|
||||
}
|
||||
|
||||
static EGLint rcGetNumConfigs(uint32_t* p_numAttribs)
|
||||
{
|
||||
int numConfigs = 0, numAttribs = 0;
|
||||
|
||||
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) {
|
||||
GLuint bufferSize = (GLuint)bufSize;
|
||||
return Renderer::get()->getConfigs()->packConfigs(bufferSize, buffer);
|
||||
}
|
||||
|
||||
static EGLint rcGetConfigs(uint32_t bufSize, GLuint* buffer)
|
||||
{
|
||||
GLuint bufferSize = (GLuint)bufSize;
|
||||
return Renderer::get()->getConfigs()->packConfigs(bufferSize, buffer);
|
||||
static EGLint rcChooseConfig(EGLint *attribs, uint32_t attribs_size,
|
||||
uint32_t *configs, uint32_t configs_size) {
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb || attribs_size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fb->getConfigs()->chooseConfig(attribs, (EGLint *)configs,
|
||||
(EGLint)configs_size);
|
||||
}
|
||||
|
||||
static EGLint rcChooseConfig(EGLint *attribs,
|
||||
uint32_t attribs_size,
|
||||
uint32_t *configs,
|
||||
uint32_t configs_size)
|
||||
{
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb || attribs_size==0) {
|
||||
return 0;
|
||||
}
|
||||
static EGLint rcGetFBParam(EGLint param) {
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fb->getConfigs()->chooseConfig(
|
||||
attribs, (EGLint*)configs, (EGLint)configs_size);
|
||||
EGLint ret = 0;
|
||||
|
||||
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)
|
||||
{
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return 0;
|
||||
}
|
||||
static uint32_t rcCreateContext(uint32_t config, uint32_t share,
|
||||
uint32_t glVersion) {
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
EGLint ret = 0;
|
||||
|
||||
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;
|
||||
// To make it consistent with the guest, create GLES2 context when GL
|
||||
// version==2 or 3
|
||||
HandleType ret =
|
||||
fb->createRenderContext(config, share, glVersion == 2 || glVersion == 3);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t rcCreateContext(uint32_t config,
|
||||
uint32_t share, uint32_t glVersion)
|
||||
{
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return 0;
|
||||
}
|
||||
static void rcDestroyContext(uint32_t context) {
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return;
|
||||
}
|
||||
|
||||
// To make it consistent with the guest, create GLES2 context when GL
|
||||
// version==2 or 3
|
||||
HandleType ret = fb->createRenderContext(config, share, glVersion == 2 || glVersion == 3);
|
||||
return ret;
|
||||
fb->DestroyRenderContext(context);
|
||||
}
|
||||
|
||||
static void rcDestroyContext(uint32_t context)
|
||||
{
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return;
|
||||
}
|
||||
static uint32_t rcCreateWindowSurface(uint32_t config, uint32_t width,
|
||||
uint32_t height) {
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
fb->DestroyRenderContext(context);
|
||||
return fb->createWindowSurface(config, width, height);
|
||||
}
|
||||
|
||||
static uint32_t rcCreateWindowSurface(uint32_t config,
|
||||
uint32_t width, uint32_t height)
|
||||
{
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return 0;
|
||||
}
|
||||
static void rcDestroyWindowSurface(uint32_t windowSurface) {
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return;
|
||||
}
|
||||
|
||||
return fb->createWindowSurface(config, width, height);
|
||||
fb->DestroyWindowSurface(windowSurface);
|
||||
}
|
||||
|
||||
static void rcDestroyWindowSurface(uint32_t windowSurface)
|
||||
{
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return;
|
||||
}
|
||||
static uint32_t rcCreateColorBuffer(uint32_t width, uint32_t height,
|
||||
GLenum internalFormat) {
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
fb->DestroyWindowSurface( windowSurface );
|
||||
return fb->createColorBuffer(width, height, internalFormat);
|
||||
}
|
||||
|
||||
static uint32_t rcCreateColorBuffer(uint32_t width,
|
||||
uint32_t height, GLenum internalFormat)
|
||||
{
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fb->createColorBuffer(width, height, internalFormat);
|
||||
}
|
||||
|
||||
static int rcOpenColorBuffer2(uint32_t colorbuffer)
|
||||
{
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return -1;
|
||||
}
|
||||
return fb->openColorBuffer( colorbuffer );
|
||||
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.
|
||||
// Use rcOpenColorBuffer2 instead.
|
||||
static void rcOpenColorBuffer(uint32_t colorbuffer)
|
||||
{
|
||||
(void) rcOpenColorBuffer2(colorbuffer);
|
||||
static void rcOpenColorBuffer(uint32_t colorbuffer) {
|
||||
(void)rcOpenColorBuffer2(colorbuffer);
|
||||
}
|
||||
|
||||
static void rcCloseColorBuffer(uint32_t colorbuffer)
|
||||
{
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return;
|
||||
}
|
||||
fb->closeColorBuffer( colorbuffer );
|
||||
static void rcCloseColorBuffer(uint32_t colorbuffer) {
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return;
|
||||
}
|
||||
fb->closeColorBuffer(colorbuffer);
|
||||
}
|
||||
|
||||
static int rcFlushWindowColorBuffer(uint32_t windowSurface)
|
||||
{
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return -1;
|
||||
}
|
||||
if (!fb->flushWindowSurfaceColorBuffer(windowSurface)) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
static int rcFlushWindowColorBuffer(uint32_t windowSurface) {
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return -1;
|
||||
}
|
||||
if (!fb->flushWindowSurfaceColorBuffer(windowSurface)) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rcSetWindowColorBuffer(uint32_t windowSurface,
|
||||
uint32_t colorBuffer)
|
||||
{
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return;
|
||||
}
|
||||
fb->setWindowSurfaceColorBuffer(windowSurface, colorBuffer);
|
||||
uint32_t colorBuffer) {
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return;
|
||||
}
|
||||
fb->setWindowSurfaceColorBuffer(windowSurface, colorBuffer);
|
||||
}
|
||||
|
||||
static EGLint rcMakeCurrent(uint32_t context,
|
||||
uint32_t drawSurf, uint32_t readSurf)
|
||||
{
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return EGL_FALSE;
|
||||
}
|
||||
static EGLint rcMakeCurrent(uint32_t context, uint32_t drawSurf,
|
||||
uint32_t readSurf) {
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
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)
|
||||
{
|
||||
WARNING("Not implemented");
|
||||
static void rcFBPost(uint32_t colorBuffer) { WARNING("Not implemented"); }
|
||||
|
||||
static void rcFBSetSwapInterval(EGLint interval) {
|
||||
// XXX: TBD - should be implemented
|
||||
}
|
||||
|
||||
static void rcFBSetSwapInterval(EGLint interval)
|
||||
{
|
||||
// XXX: TBD - should be implemented
|
||||
static void rcBindTexture(uint32_t colorBuffer) {
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return;
|
||||
}
|
||||
|
||||
fb->bindColorBufferToTexture(colorBuffer);
|
||||
}
|
||||
|
||||
static void rcBindTexture(uint32_t colorBuffer)
|
||||
{
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return;
|
||||
}
|
||||
static void rcBindRenderbuffer(uint32_t colorBuffer) {
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return;
|
||||
}
|
||||
|
||||
fb->bindColorBufferToTexture(colorBuffer);
|
||||
fb->bindColorBufferToRenderbuffer(colorBuffer);
|
||||
}
|
||||
|
||||
static void rcBindRenderbuffer(uint32_t colorBuffer)
|
||||
{
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return;
|
||||
}
|
||||
|
||||
fb->bindColorBufferToRenderbuffer(colorBuffer);
|
||||
static EGLint rcColorBufferCacheFlush(uint32_t colorBuffer, EGLint postCount,
|
||||
int forRead) {
|
||||
// XXX: TBD - should be implemented
|
||||
return 0;
|
||||
}
|
||||
|
||||
static EGLint rcColorBufferCacheFlush(uint32_t colorBuffer,
|
||||
EGLint postCount, int forRead)
|
||||
{
|
||||
// XXX: TBD - should be implemented
|
||||
return 0;
|
||||
static void rcReadColorBuffer(uint32_t colorBuffer, GLint x, GLint y,
|
||||
GLint width, GLint height, GLenum format,
|
||||
GLenum type, void *pixels) {
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return;
|
||||
}
|
||||
|
||||
fb->readColorBuffer(colorBuffer, x, y, width, height, format, type, pixels);
|
||||
}
|
||||
|
||||
static void rcReadColorBuffer(uint32_t colorBuffer,
|
||||
GLint x, GLint y,
|
||||
GLint width, GLint height,
|
||||
GLenum format, GLenum type, void* pixels)
|
||||
{
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return;
|
||||
}
|
||||
static int rcUpdateColorBuffer(uint32_t colorBuffer, GLint x, GLint y,
|
||||
GLint width, GLint height, GLenum format,
|
||||
GLenum type, void *pixels) {
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
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,
|
||||
GLint x, GLint y,
|
||||
GLint width, GLint height,
|
||||
GLenum format, GLenum type, void* pixels)
|
||||
{
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
fb->updateColorBuffer(colorBuffer, x, y, width, height, format, type, pixels);
|
||||
static uint32_t rcCreateClientImage(uint32_t context, EGLenum target,
|
||||
GLuint buffer) {
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fb->createClientImage(context, target, buffer);
|
||||
}
|
||||
|
||||
static uint32_t rcCreateClientImage(uint32_t context, EGLenum target, GLuint buffer)
|
||||
{
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return 0;
|
||||
}
|
||||
static int rcDestroyClientImage(uint32_t image) {
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fb->createClientImage(context, target, buffer);
|
||||
}
|
||||
|
||||
static int rcDestroyClientImage(uint32_t image)
|
||||
{
|
||||
Renderer *fb = Renderer::get();
|
||||
if (!fb) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fb->destroyClientImage(image);
|
||||
return fb->destroyClientImage(image);
|
||||
}
|
||||
|
||||
static void rcSelectChecksumCalculator(uint32_t protocol, uint32_t reserved) {
|
||||
ChecksumCalculatorThreadInfo::setVersion(protocol);
|
||||
ChecksumCalculatorThreadInfo::setVersion(protocol);
|
||||
}
|
||||
|
||||
int rcGetNumDisplays() {
|
||||
return 1;
|
||||
}
|
||||
int rcGetNumDisplays() { return 1; }
|
||||
|
||||
int rcGetDisplayWidth(uint32_t display_id) {
|
||||
printf("%s: display_id=%d\n", __func__, display_id);
|
||||
return DisplayManager::get()->display_info().horizontal_resolution;
|
||||
printf("%s: display_id=%d\n", __func__, display_id);
|
||||
return DisplayManager::get()->display_info().horizontal_resolution;
|
||||
}
|
||||
|
||||
int rcGetDisplayHeight(uint32_t display_id) {
|
||||
printf("%s: display_id=%d\n", __func__, display_id);
|
||||
return DisplayManager::get()->display_info().vertical_resolution;
|
||||
printf("%s: display_id=%d\n", __func__, display_id);
|
||||
return DisplayManager::get()->display_info().vertical_resolution;
|
||||
}
|
||||
|
||||
int rcGetDisplayDpiX(uint32_t display_id) {
|
||||
printf("%s: display_id=%d\n", __func__, display_id);
|
||||
return 120;
|
||||
printf("%s: display_id=%d\n", __func__, display_id);
|
||||
return 120;
|
||||
}
|
||||
|
||||
int rcGetDisplayDpiY(uint32_t display_id) {
|
||||
printf("%s: display_id=%d\n", __func__, display_id);
|
||||
return 120;
|
||||
printf("%s: display_id=%d\n", __func__, display_id);
|
||||
return 120;
|
||||
}
|
||||
|
||||
int rcGetDisplayVsyncPeriod(uint32_t display_id) {
|
||||
printf("%s: display_id=%d\n", __func__, display_id);
|
||||
return 1;
|
||||
printf("%s: display_id=%d\n", __func__, display_id);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static std::vector<Renderable> frame_layers;
|
||||
|
||||
bool is_layer_blacklisted(const std::string &name) {
|
||||
static std::vector<std::string> blacklist = {
|
||||
"Sprite",
|
||||
};
|
||||
return std::find(blacklist.begin(), blacklist.end(), name) != blacklist.end();
|
||||
static std::vector<std::string> blacklist = {
|
||||
"Sprite",
|
||||
};
|
||||
return std::find(blacklist.begin(), blacklist.end(), name) != blacklist.end();
|
||||
}
|
||||
|
||||
void rcPostLayer(const char *name, uint32_t color_buffer,
|
||||
int32_t sourceCropLeft, int32_t sourceCropTop,
|
||||
int32_t sourceCropRight, int32_t sourceCropBottom,
|
||||
int32_t displayFrameLeft, int32_t displayFrameTop,
|
||||
int32_t displayFrameRight, int32_t displayFrameBottom)
|
||||
{
|
||||
Renderable r{
|
||||
name,
|
||||
color_buffer,
|
||||
{displayFrameLeft, displayFrameTop, displayFrameRight, displayFrameBottom},
|
||||
{sourceCropLeft, sourceCropTop, sourceCropRight, sourceCropBottom}
|
||||
};
|
||||
frame_layers.push_back(r);
|
||||
int32_t displayFrameRight, int32_t displayFrameBottom) {
|
||||
Renderable r{
|
||||
name,
|
||||
color_buffer,
|
||||
{displayFrameLeft, displayFrameTop, displayFrameRight,
|
||||
displayFrameBottom},
|
||||
{sourceCropLeft, sourceCropTop, sourceCropRight, sourceCropBottom}};
|
||||
frame_layers.push_back(r);
|
||||
}
|
||||
|
||||
void rcPostAllLayersDone()
|
||||
{
|
||||
if (composer)
|
||||
composer->submit_layers(frame_layers);
|
||||
void rcPostAllLayersDone() {
|
||||
if (composer) composer->submit_layers(frame_layers);
|
||||
|
||||
frame_layers.clear();
|
||||
frame_layers.clear();
|
||||
}
|
||||
|
||||
void initRenderControlContext(renderControl_decoder_context_t *dec)
|
||||
{
|
||||
dec->rcGetRendererVersion = rcGetRendererVersion;
|
||||
dec->rcGetEGLVersion = rcGetEGLVersion;
|
||||
dec->rcQueryEGLString = rcQueryEGLString;
|
||||
dec->rcGetGLString = rcGetGLString;
|
||||
dec->rcGetNumConfigs = rcGetNumConfigs;
|
||||
dec->rcGetConfigs = rcGetConfigs;
|
||||
dec->rcChooseConfig = rcChooseConfig;
|
||||
dec->rcGetFBParam = rcGetFBParam;
|
||||
dec->rcCreateContext = rcCreateContext;
|
||||
dec->rcDestroyContext = rcDestroyContext;
|
||||
dec->rcCreateWindowSurface = rcCreateWindowSurface;
|
||||
dec->rcDestroyWindowSurface = rcDestroyWindowSurface;
|
||||
dec->rcCreateColorBuffer = rcCreateColorBuffer;
|
||||
dec->rcOpenColorBuffer = rcOpenColorBuffer;
|
||||
dec->rcCloseColorBuffer = rcCloseColorBuffer;
|
||||
dec->rcSetWindowColorBuffer = rcSetWindowColorBuffer;
|
||||
dec->rcFlushWindowColorBuffer = rcFlushWindowColorBuffer;
|
||||
dec->rcMakeCurrent = rcMakeCurrent;
|
||||
dec->rcFBPost = rcFBPost;
|
||||
dec->rcFBSetSwapInterval = rcFBSetSwapInterval;
|
||||
dec->rcBindTexture = rcBindTexture;
|
||||
dec->rcBindRenderbuffer = rcBindRenderbuffer;
|
||||
dec->rcColorBufferCacheFlush = rcColorBufferCacheFlush;
|
||||
dec->rcReadColorBuffer = rcReadColorBuffer;
|
||||
dec->rcUpdateColorBuffer = rcUpdateColorBuffer;
|
||||
dec->rcOpenColorBuffer2 = rcOpenColorBuffer2;
|
||||
dec->rcCreateClientImage = rcCreateClientImage;
|
||||
dec->rcDestroyClientImage = rcDestroyClientImage;
|
||||
dec->rcSelectChecksumCalculator = rcSelectChecksumCalculator;
|
||||
dec->rcGetNumDisplays = rcGetNumDisplays;
|
||||
dec->rcGetDisplayWidth = rcGetDisplayWidth;
|
||||
dec->rcGetDisplayHeight = rcGetDisplayHeight;
|
||||
dec->rcGetDisplayDpiX = rcGetDisplayDpiX;
|
||||
dec->rcGetDisplayDpiY = rcGetDisplayDpiY;
|
||||
dec->rcGetDisplayVsyncPeriod = rcGetDisplayVsyncPeriod;
|
||||
dec->rcPostLayer = rcPostLayer;
|
||||
dec->rcPostAllLayersDone = rcPostAllLayersDone;
|
||||
void initRenderControlContext(renderControl_decoder_context_t *dec) {
|
||||
dec->rcGetRendererVersion = rcGetRendererVersion;
|
||||
dec->rcGetEGLVersion = rcGetEGLVersion;
|
||||
dec->rcQueryEGLString = rcQueryEGLString;
|
||||
dec->rcGetGLString = rcGetGLString;
|
||||
dec->rcGetNumConfigs = rcGetNumConfigs;
|
||||
dec->rcGetConfigs = rcGetConfigs;
|
||||
dec->rcChooseConfig = rcChooseConfig;
|
||||
dec->rcGetFBParam = rcGetFBParam;
|
||||
dec->rcCreateContext = rcCreateContext;
|
||||
dec->rcDestroyContext = rcDestroyContext;
|
||||
dec->rcCreateWindowSurface = rcCreateWindowSurface;
|
||||
dec->rcDestroyWindowSurface = rcDestroyWindowSurface;
|
||||
dec->rcCreateColorBuffer = rcCreateColorBuffer;
|
||||
dec->rcOpenColorBuffer = rcOpenColorBuffer;
|
||||
dec->rcCloseColorBuffer = rcCloseColorBuffer;
|
||||
dec->rcSetWindowColorBuffer = rcSetWindowColorBuffer;
|
||||
dec->rcFlushWindowColorBuffer = rcFlushWindowColorBuffer;
|
||||
dec->rcMakeCurrent = rcMakeCurrent;
|
||||
dec->rcFBPost = rcFBPost;
|
||||
dec->rcFBSetSwapInterval = rcFBSetSwapInterval;
|
||||
dec->rcBindTexture = rcBindTexture;
|
||||
dec->rcBindRenderbuffer = rcBindRenderbuffer;
|
||||
dec->rcColorBufferCacheFlush = rcColorBufferCacheFlush;
|
||||
dec->rcReadColorBuffer = rcReadColorBuffer;
|
||||
dec->rcUpdateColorBuffer = rcUpdateColorBuffer;
|
||||
dec->rcOpenColorBuffer2 = rcOpenColorBuffer2;
|
||||
dec->rcCreateClientImage = rcCreateClientImage;
|
||||
dec->rcDestroyClientImage = rcDestroyClientImage;
|
||||
dec->rcSelectChecksumCalculator = rcSelectChecksumCalculator;
|
||||
dec->rcGetNumDisplays = rcGetNumDisplays;
|
||||
dec->rcGetDisplayWidth = rcGetDisplayWidth;
|
||||
dec->rcGetDisplayHeight = rcGetDisplayHeight;
|
||||
dec->rcGetDisplayDpiX = rcGetDisplayDpiX;
|
||||
dec->rcGetDisplayDpiY = rcGetDisplayDpiY;
|
||||
dec->rcGetDisplayVsyncPeriod = rcGetDisplayVsyncPeriod;
|
||||
dec->rcPostLayer = rcPostLayer;
|
||||
dec->rcPostAllLayersDone = rcPostAllLayersDone;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,10 +23,11 @@
|
|||
namespace anbox {
|
||||
namespace graphics {
|
||||
class LayerComposer;
|
||||
} // namespace graphics
|
||||
} // namespace anbox
|
||||
} // namespace graphics
|
||||
} // namespace anbox
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -15,11 +15,11 @@
|
|||
*/
|
||||
#include "RenderServer.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include "RenderThread.h"
|
||||
#include "TcpStream.h"
|
||||
#include "UnixStream.h"
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "OpenglRender/render_api.h"
|
||||
|
||||
|
|
@ -29,123 +29,109 @@
|
|||
|
||||
typedef std::set<RenderThread *> RenderThreadsSet;
|
||||
|
||||
RenderServer::RenderServer() :
|
||||
m_lock(),
|
||||
m_listenSock(NULL),
|
||||
m_exiting(false)
|
||||
{
|
||||
}
|
||||
|
||||
RenderServer::~RenderServer()
|
||||
{
|
||||
delete m_listenSock;
|
||||
}
|
||||
RenderServer::RenderServer() : m_lock(), m_listenSock(NULL), m_exiting(false) {}
|
||||
|
||||
RenderServer::~RenderServer() { delete m_listenSock; }
|
||||
|
||||
extern "C" int gRendererStreamMode;
|
||||
|
||||
RenderServer *RenderServer::create(char* addr, size_t addrLen)
|
||||
{
|
||||
RenderServer *server = new RenderServer();
|
||||
if (!server) {
|
||||
return NULL;
|
||||
}
|
||||
RenderServer *RenderServer::create(char *addr, size_t addrLen) {
|
||||
RenderServer *server = new RenderServer();
|
||||
if (!server) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (gRendererStreamMode == RENDER_API_STREAM_MODE_TCP) {
|
||||
server->m_listenSock = new TcpStream();
|
||||
} else {
|
||||
server->m_listenSock = new UnixStream();
|
||||
}
|
||||
if (gRendererStreamMode == RENDER_API_STREAM_MODE_TCP) {
|
||||
server->m_listenSock = new TcpStream();
|
||||
} else {
|
||||
server->m_listenSock = new UnixStream();
|
||||
}
|
||||
|
||||
char addrstr[SocketStream::MAX_ADDRSTR_LEN];
|
||||
if (server->m_listenSock->listen(addrstr) < 0) {
|
||||
ERR("RenderServer::create failed to listen\n");
|
||||
delete server;
|
||||
return NULL;
|
||||
}
|
||||
char addrstr[SocketStream::MAX_ADDRSTR_LEN];
|
||||
if (server->m_listenSock->listen(addrstr) < 0) {
|
||||
ERR("RenderServer::create failed to listen\n");
|
||||
delete server;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t len = strlen(addrstr) + 1;
|
||||
if (len > addrLen) {
|
||||
ERR("RenderServer address name too big for provided buffer: %zu > %zu\n",
|
||||
len, addrLen);
|
||||
delete server;
|
||||
return NULL;
|
||||
}
|
||||
memcpy(addr, addrstr, len);
|
||||
size_t len = strlen(addrstr) + 1;
|
||||
if (len > addrLen) {
|
||||
ERR("RenderServer address name too big for provided buffer: %zu > %zu\n",
|
||||
len, addrLen);
|
||||
delete server;
|
||||
return NULL;
|
||||
}
|
||||
memcpy(addr, addrstr, len);
|
||||
|
||||
return server;
|
||||
return server;
|
||||
}
|
||||
|
||||
intptr_t RenderServer::main()
|
||||
{
|
||||
RenderThreadsSet threads;
|
||||
intptr_t RenderServer::main() {
|
||||
RenderThreadsSet threads;
|
||||
|
||||
while(1) {
|
||||
SocketStream *stream = m_listenSock->accept();
|
||||
if (!stream) {
|
||||
fprintf(stderr,"Error accepting gles connection, ignoring.\n");
|
||||
continue;
|
||||
}
|
||||
while (1) {
|
||||
SocketStream *stream = m_listenSock->accept();
|
||||
if (!stream) {
|
||||
fprintf(stderr, "Error accepting gles connection, ignoring.\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned int clientFlags;
|
||||
if (!stream->readFully(&clientFlags, sizeof(unsigned int))) {
|
||||
fprintf(stderr,"Error reading clientFlags\n");
|
||||
delete stream;
|
||||
continue;
|
||||
}
|
||||
unsigned int clientFlags;
|
||||
if (!stream->readFully(&clientFlags, sizeof(unsigned int))) {
|
||||
fprintf(stderr, "Error reading clientFlags\n");
|
||||
delete stream;
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if we have been requested to exit while waiting on accept
|
||||
if ((clientFlags & IOSTREAM_CLIENT_EXIT_SERVER) != 0) {
|
||||
m_exiting = true;
|
||||
delete stream;
|
||||
break;
|
||||
}
|
||||
// check if we have been requested to exit while waiting on accept
|
||||
if ((clientFlags & IOSTREAM_CLIENT_EXIT_SERVER) != 0) {
|
||||
m_exiting = true;
|
||||
delete stream;
|
||||
break;
|
||||
}
|
||||
|
||||
RenderThread *rt = RenderThread::create(stream, &m_lock);
|
||||
if (!rt) {
|
||||
fprintf(stderr,"Failed to create RenderThread\n");
|
||||
delete stream;
|
||||
} else if (!rt->start()) {
|
||||
fprintf(stderr,"Failed to start RenderThread\n");
|
||||
delete rt;
|
||||
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);
|
||||
RenderThread *rt = RenderThread::create(stream, &m_lock);
|
||||
if (!rt) {
|
||||
fprintf(stderr, "Failed to create RenderThread\n");
|
||||
delete stream;
|
||||
} else if (!rt->start()) {
|
||||
fprintf(stderr, "Failed to start RenderThread\n");
|
||||
delete rt;
|
||||
delete stream;
|
||||
}
|
||||
|
||||
//
|
||||
// Wait for all threads to finish
|
||||
// remove from the threads list threads which are
|
||||
// no longer running
|
||||
//
|
||||
for (RenderThreadsSet::iterator t = threads.begin();
|
||||
t != threads.end();
|
||||
t++) {
|
||||
(*t)->forceStop();
|
||||
(*t)->wait(NULL);
|
||||
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);
|
||||
}
|
||||
}
|
||||
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/thread.h"
|
||||
|
||||
class RenderServer : public emugl::Thread
|
||||
{
|
||||
public:
|
||||
static RenderServer *create(char* addr, size_t addrLen);
|
||||
virtual ~RenderServer();
|
||||
class RenderServer : public emugl::Thread {
|
||||
public:
|
||||
static RenderServer *create(char *addr, size_t addrLen);
|
||||
virtual ~RenderServer();
|
||||
|
||||
virtual intptr_t main();
|
||||
virtual intptr_t main();
|
||||
|
||||
bool isExiting() const { return m_exiting; }
|
||||
bool isExiting() const { return m_exiting; }
|
||||
|
||||
private:
|
||||
RenderServer();
|
||||
private:
|
||||
RenderServer();
|
||||
|
||||
private:
|
||||
emugl::Mutex m_lock;
|
||||
SocketStream *m_listenSock;
|
||||
bool m_exiting;
|
||||
private:
|
||||
emugl::Mutex m_lock;
|
||||
SocketStream *m_listenSock;
|
||||
bool m_exiting;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -15,107 +15,102 @@
|
|||
*/
|
||||
#include "RenderThread.h"
|
||||
|
||||
#include "Renderer.h"
|
||||
#include "ReadBuffer.h"
|
||||
#include "RenderControl.h"
|
||||
#include "RenderThreadInfo.h"
|
||||
#include "Renderer.h"
|
||||
#include "TimeUtils.h"
|
||||
|
||||
#include "OpenGLESDispatch/EGLDispatch.h"
|
||||
#include "OpenGLESDispatch/GLESv2Dispatch.h"
|
||||
#include "OpenGLESDispatch/GLESv1Dispatch.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) :
|
||||
emugl::Thread(),
|
||||
m_lock(lock),
|
||||
m_stream(stream) {}
|
||||
RenderThread::RenderThread(IOStream *stream, emugl::Mutex *lock)
|
||||
: emugl::Thread(), m_lock(lock), m_stream(stream) {}
|
||||
|
||||
RenderThread::~RenderThread() {
|
||||
delete m_stream;
|
||||
}
|
||||
RenderThread::~RenderThread() { delete m_stream; }
|
||||
|
||||
// static
|
||||
RenderThread* RenderThread::create(IOStream *stream, emugl::Mutex *lock) {
|
||||
return new RenderThread(stream, lock);
|
||||
RenderThread *RenderThread::create(IOStream *stream, emugl::Mutex *lock) {
|
||||
return new RenderThread(stream, lock);
|
||||
}
|
||||
|
||||
void RenderThread::forceStop() {
|
||||
m_stream->forceStop();
|
||||
}
|
||||
void RenderThread::forceStop() { m_stream->forceStop(); }
|
||||
|
||||
intptr_t RenderThread::main() {
|
||||
RenderThreadInfo tInfo;
|
||||
ChecksumCalculatorThreadInfo tChecksumInfo;
|
||||
RenderThreadInfo tInfo;
|
||||
ChecksumCalculatorThreadInfo tChecksumInfo;
|
||||
|
||||
//
|
||||
// initialize decoders
|
||||
//
|
||||
tInfo.m_glDec.initGL(gles1_dispatch_get_proc_func, NULL);
|
||||
tInfo.m_gl2Dec.initGL(gles2_dispatch_get_proc_func, NULL);
|
||||
initRenderControlContext(&tInfo.m_rcDec);
|
||||
//
|
||||
// initialize decoders
|
||||
//
|
||||
tInfo.m_glDec.initGL(gles1_dispatch_get_proc_func, NULL);
|
||||
tInfo.m_gl2Dec.initGL(gles2_dispatch_get_proc_func, NULL);
|
||||
initRenderControlContext(&tInfo.m_rcDec);
|
||||
|
||||
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 );
|
||||
ReadBuffer readBuf(STREAM_BUFFER_SIZE);
|
||||
|
||||
while (1) {
|
||||
int stat = readBuf.getData(m_stream);
|
||||
if (stat <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// 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");
|
||||
}
|
||||
bool progress;
|
||||
do {
|
||||
progress = false;
|
||||
|
||||
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
|
||||
// handles a single guest client / protocol byte stream.
|
||||
class RenderThread : public emugl::Thread {
|
||||
public:
|
||||
// Create a new RenderThread instance.
|
||||
// |stream| is an input stream that will be read from the thread,
|
||||
// and deleted by it when it exits.
|
||||
// |mutex| is a pointer to a shared mutex used to serialize
|
||||
// decoding operations between all threads.
|
||||
// TODO(digit): Why is this needed here? Shouldn't this be handled
|
||||
// by the decoders themselves or at a lower-level?
|
||||
static RenderThread* create(IOStream* stream, emugl::Mutex* mutex);
|
||||
public:
|
||||
// Create a new RenderThread instance.
|
||||
// |stream| is an input stream that will be read from the thread,
|
||||
// and deleted by it when it exits.
|
||||
// |mutex| is a pointer to a shared mutex used to serialize
|
||||
// decoding operations between all threads.
|
||||
// TODO(digit): Why is this needed here? Shouldn't this be handled
|
||||
// by the decoders themselves or at a lower-level?
|
||||
static RenderThread* create(IOStream* stream, emugl::Mutex* mutex);
|
||||
|
||||
// Destructor.
|
||||
virtual ~RenderThread();
|
||||
// Destructor.
|
||||
virtual ~RenderThread();
|
||||
|
||||
// Returns true iff the thread has finished.
|
||||
// Note that this also means that the thread's stack has been
|
||||
bool isFinished() { return tryWait(NULL); }
|
||||
// Returns true iff the thread has finished.
|
||||
// Note that this also means that the thread's stack has been
|
||||
bool isFinished() { return tryWait(NULL); }
|
||||
|
||||
// Force a thread to stop.
|
||||
void forceStop();
|
||||
// Force a thread to stop.
|
||||
void forceStop();
|
||||
|
||||
private:
|
||||
RenderThread(); // No default constructor
|
||||
private:
|
||||
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;
|
||||
IOStream* m_stream;
|
||||
emugl::Mutex* m_lock;
|
||||
IOStream* m_stream;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -22,22 +22,18 @@
|
|||
namespace {
|
||||
|
||||
class ThreadInfoStore : public ::emugl::ThreadStore {
|
||||
public:
|
||||
ThreadInfoStore() : ::emugl::ThreadStore(NULL) {}
|
||||
public:
|
||||
ThreadInfoStore() : ::emugl::ThreadStore(NULL) {}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
static ::emugl::LazyInstance<ThreadInfoStore> s_tls = LAZY_INSTANCE_INIT;
|
||||
|
||||
RenderThreadInfo::RenderThreadInfo() {
|
||||
s_tls->set(this);
|
||||
}
|
||||
RenderThreadInfo::RenderThreadInfo() { s_tls->set(this); }
|
||||
|
||||
RenderThreadInfo::~RenderThreadInfo() {
|
||||
s_tls->set(NULL);
|
||||
}
|
||||
RenderThreadInfo::~RenderThreadInfo() { s_tls->set(NULL); }
|
||||
|
||||
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
|
||||
#define _LIB_OPENGL_RENDER_THREAD_INFO_H
|
||||
|
||||
#include "RenderContext.h"
|
||||
#include "WindowSurface.h"
|
||||
#include "GLESv1Decoder.h"
|
||||
#include "GLESv2Decoder.h"
|
||||
#include "RenderContext.h"
|
||||
#include "WindowSurface.h"
|
||||
#include "renderControl_dec.h"
|
||||
|
||||
#include <set>
|
||||
|
|
@ -30,31 +30,31 @@ typedef std::set<HandleType> WindowSurfaceSet;
|
|||
|
||||
// A class used to model the state of each RenderThread related
|
||||
struct RenderThreadInfo {
|
||||
// Create new instance. Only call this once per thread.
|
||||
// Future callls to get() will return this instance until
|
||||
// it is destroyed.
|
||||
RenderThreadInfo();
|
||||
// Create new instance. Only call this once per thread.
|
||||
// Future callls to get() will return this instance until
|
||||
// it is destroyed.
|
||||
RenderThreadInfo();
|
||||
|
||||
// Destructor.
|
||||
~RenderThreadInfo();
|
||||
// Destructor.
|
||||
~RenderThreadInfo();
|
||||
|
||||
// Return the current thread's instance, if any, or NULL.
|
||||
static RenderThreadInfo* get();
|
||||
// Return the current thread's instance, if any, or NULL.
|
||||
static RenderThreadInfo* get();
|
||||
|
||||
// Current EGL context, draw surface and read surface.
|
||||
RenderContextPtr currContext;
|
||||
WindowSurfacePtr currDrawSurf;
|
||||
WindowSurfacePtr currReadSurf;
|
||||
// Current EGL context, draw surface and read surface.
|
||||
RenderContextPtr currContext;
|
||||
WindowSurfacePtr currDrawSurf;
|
||||
WindowSurfacePtr currReadSurf;
|
||||
|
||||
// Decoder states.
|
||||
GLESv1Decoder m_glDec;
|
||||
GLESv2Decoder m_gl2Dec;
|
||||
renderControl_decoder_context_t m_rcDec;
|
||||
// Decoder states.
|
||||
GLESv1Decoder m_glDec;
|
||||
GLESv2Decoder m_gl2Dec;
|
||||
renderControl_decoder_context_t m_rcDec;
|
||||
|
||||
// all the contexts that are created by this render thread
|
||||
ThreadContextSet m_contextSet;
|
||||
// all the window surfaces that are created by this render thread
|
||||
WindowSurfaceSet m_windowSet;
|
||||
// all the contexts that are created by this render thread
|
||||
ThreadContextSet m_contextSet;
|
||||
// all the window surfaces that are created by this render thread
|
||||
WindowSurfaceSet m_windowSet;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -14,51 +14,51 @@
|
|||
|
||||
#include "RenderWindow.h"
|
||||
|
||||
#include "Renderer.h"
|
||||
#include "emugl/common/logging.h"
|
||||
#include "emugl/common/message_channel.h"
|
||||
#include "emugl/common/mutex.h"
|
||||
#include "emugl/common/thread.h"
|
||||
#include "Renderer.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#define DEBUG 0
|
||||
|
||||
#if DEBUG
|
||||
# define D(...) my_debug(__PRETTY_FUNCTION__, __LINE__, __VA_ARGS__)
|
||||
#define D(...) my_debug(__PRETTY_FUNCTION__, __LINE__, __VA_ARGS__)
|
||||
#else
|
||||
# define D(...) ((void)0)
|
||||
#define D(...) ((void)0)
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
#if DEBUG
|
||||
void my_debug(const char* function, int line, const char* format, ...) {
|
||||
static ::emugl::Mutex mutex;
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
mutex.lock();
|
||||
fprintf(stderr, "%s:%d:", function, line);
|
||||
vfprintf(stderr, format, args);
|
||||
mutex.unlock();
|
||||
va_end(args);
|
||||
static ::emugl::Mutex mutex;
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
mutex.lock();
|
||||
fprintf(stderr, "%s:%d:", function, line);
|
||||
vfprintf(stderr, format, args);
|
||||
mutex.unlock();
|
||||
va_end(args);
|
||||
}
|
||||
#endif
|
||||
|
||||
// List of possible commands to send to the render window thread from
|
||||
// the main one.
|
||||
enum Command {
|
||||
CMD_INITIALIZE,
|
||||
CMD_SET_POST_CALLBACK,
|
||||
CMD_SETUP_SUBWINDOW,
|
||||
CMD_REMOVE_SUBWINDOW,
|
||||
CMD_SET_ROTATION,
|
||||
CMD_SET_TRANSLATION,
|
||||
CMD_REPAINT,
|
||||
CMD_FINALIZE,
|
||||
CMD_INITIALIZE,
|
||||
CMD_SET_POST_CALLBACK,
|
||||
CMD_SETUP_SUBWINDOW,
|
||||
CMD_REMOVE_SUBWINDOW,
|
||||
CMD_SET_ROTATION,
|
||||
CMD_SET_TRANSLATION,
|
||||
CMD_REPAINT,
|
||||
CMD_FINALIZE,
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
|
@ -66,74 +66,73 @@ enum Command {
|
|||
// A single message sent from the main thread to the render window thread.
|
||||
// |cmd| determines which fields are valid to read.
|
||||
struct RenderWindowMessage {
|
||||
Command cmd;
|
||||
union {
|
||||
// CMD_INITIALIZE
|
||||
struct {
|
||||
EGLNativeDisplayType nativeDisplay;
|
||||
} init;
|
||||
Command cmd;
|
||||
union {
|
||||
// CMD_INITIALIZE
|
||||
struct {
|
||||
EGLNativeDisplayType nativeDisplay;
|
||||
} init;
|
||||
|
||||
// CMD_SET_POST_CALLBACK
|
||||
struct {
|
||||
OnPostFn on_post;
|
||||
void* on_post_context;
|
||||
} set_post_callback;
|
||||
// CMD_SET_POST_CALLBACK
|
||||
struct {
|
||||
OnPostFn on_post;
|
||||
void* on_post_context;
|
||||
} set_post_callback;
|
||||
|
||||
// CMD_SETUP_SUBWINDOW
|
||||
struct {
|
||||
FBNativeWindowType parent;
|
||||
int wx;
|
||||
int wy;
|
||||
int ww;
|
||||
int wh;
|
||||
int fbw;
|
||||
int fbh;
|
||||
float dpr;
|
||||
float rotation;
|
||||
} subwindow;
|
||||
// CMD_SETUP_SUBWINDOW
|
||||
struct {
|
||||
FBNativeWindowType parent;
|
||||
int wx;
|
||||
int wy;
|
||||
int ww;
|
||||
int wh;
|
||||
int fbw;
|
||||
int fbh;
|
||||
float dpr;
|
||||
float rotation;
|
||||
} subwindow;
|
||||
|
||||
// CMD_SET_TRANSLATION;
|
||||
struct {
|
||||
float px;
|
||||
float py;
|
||||
} trans;
|
||||
// CMD_SET_TRANSLATION;
|
||||
struct {
|
||||
float px;
|
||||
float py;
|
||||
} trans;
|
||||
|
||||
// CMD_SET_ROTATION
|
||||
float rotation;
|
||||
// CMD_SET_ROTATION
|
||||
float rotation;
|
||||
|
||||
// result of operations.
|
||||
bool result;
|
||||
};
|
||||
// result of operations.
|
||||
bool result;
|
||||
};
|
||||
|
||||
// Process the current message, and updates its |result| field.
|
||||
// Returns true on success, or false on failure.
|
||||
bool process() const {
|
||||
const RenderWindowMessage& msg = *this;
|
||||
Renderer* fb;
|
||||
bool result = false;
|
||||
switch (msg.cmd) {
|
||||
case CMD_INITIALIZE:
|
||||
D("CMD_INITIALIZE\n");
|
||||
GL_LOG("RenderWindow: CMD_INITIALIZE");
|
||||
result = Renderer::initialize(msg.init.nativeDisplay);
|
||||
break;
|
||||
// Process the current message, and updates its |result| field.
|
||||
// Returns true on success, or false on failure.
|
||||
bool process() const {
|
||||
const RenderWindowMessage& msg = *this;
|
||||
Renderer* fb;
|
||||
bool result = false;
|
||||
switch (msg.cmd) {
|
||||
case CMD_INITIALIZE:
|
||||
D("CMD_INITIALIZE\n");
|
||||
GL_LOG("RenderWindow: CMD_INITIALIZE");
|
||||
result = Renderer::initialize(msg.init.nativeDisplay);
|
||||
break;
|
||||
|
||||
case CMD_FINALIZE:
|
||||
D("CMD_FINALIZE\n");
|
||||
// this command may be issued even when frame buffer is not
|
||||
// yet created (e.g. if CMD_INITIALIZE failed),
|
||||
// so make sure we check if it is there before finalizing
|
||||
if (const auto fb = Renderer::get()) {
|
||||
fb->finalize();
|
||||
}
|
||||
result = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
;
|
||||
case CMD_FINALIZE:
|
||||
D("CMD_FINALIZE\n");
|
||||
// this command may be issued even when frame buffer is not
|
||||
// yet created (e.g. if CMD_INITIALIZE failed),
|
||||
// so make sure we check if it is there before finalizing
|
||||
if (const auto fb = Renderer::get()) {
|
||||
fb->finalize();
|
||||
}
|
||||
return result;
|
||||
result = true;
|
||||
break;
|
||||
|
||||
default:;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// Simple synchronization structure used to exchange data between the
|
||||
|
|
@ -159,43 +158,43 @@ struct RenderWindowMessage {
|
|||
// canWriteCmd.signal()
|
||||
//
|
||||
class RenderWindowChannel {
|
||||
public:
|
||||
RenderWindowChannel() : mIn(), mOut() {}
|
||||
~RenderWindowChannel() {}
|
||||
public:
|
||||
RenderWindowChannel() : mIn(), mOut() {}
|
||||
~RenderWindowChannel() {}
|
||||
|
||||
// Send a message from the main thread.
|
||||
// Note that the content of |msg| is copied into the channel.
|
||||
// Returns with the command's result (true or false).
|
||||
bool sendMessageAndGetResult(const RenderWindowMessage& msg) {
|
||||
D("msg.cmd=%d\n", msg.cmd);
|
||||
mIn.send(msg);
|
||||
D("waiting for result\n");
|
||||
bool result = false;
|
||||
mOut.receive(&result);
|
||||
D("result=%s\n", result ? "success" : "failure");
|
||||
return result;
|
||||
}
|
||||
// Send a message from the main thread.
|
||||
// Note that the content of |msg| is copied into the channel.
|
||||
// Returns with the command's result (true or false).
|
||||
bool sendMessageAndGetResult(const RenderWindowMessage& msg) {
|
||||
D("msg.cmd=%d\n", msg.cmd);
|
||||
mIn.send(msg);
|
||||
D("waiting for result\n");
|
||||
bool result = false;
|
||||
mOut.receive(&result);
|
||||
D("result=%s\n", result ? "success" : "failure");
|
||||
return result;
|
||||
}
|
||||
|
||||
// Receive a message from the render window thread.
|
||||
// On exit, |*msg| gets a copy of the message. The caller
|
||||
// must always call sendResult() after processing the message.
|
||||
void receiveMessage(RenderWindowMessage* msg) {
|
||||
D("entering\n");
|
||||
mIn.receive(msg);
|
||||
D("message cmd=%d\n", msg->cmd);
|
||||
}
|
||||
// Receive a message from the render window thread.
|
||||
// On exit, |*msg| gets a copy of the message. The caller
|
||||
// must always call sendResult() after processing the message.
|
||||
void receiveMessage(RenderWindowMessage* msg) {
|
||||
D("entering\n");
|
||||
mIn.receive(msg);
|
||||
D("message cmd=%d\n", msg->cmd);
|
||||
}
|
||||
|
||||
// Send result from the render window thread to the main one.
|
||||
// Must always be called after receiveMessage().
|
||||
void sendResult(bool result) {
|
||||
D("waiting to send result (%s)\n", result ? "success" : "failure");
|
||||
mOut.send(result);
|
||||
D("result sent\n");
|
||||
}
|
||||
// Send result from the render window thread to the main one.
|
||||
// Must always be called after receiveMessage().
|
||||
void sendResult(bool result) {
|
||||
D("waiting to send result (%s)\n", result ? "success" : "failure");
|
||||
mOut.send(result);
|
||||
D("result sent\n");
|
||||
}
|
||||
|
||||
private:
|
||||
emugl::MessageChannel<RenderWindowMessage, 16U> mIn;
|
||||
emugl::MessageChannel<bool, 16U> mOut;
|
||||
private:
|
||||
emugl::MessageChannel<RenderWindowMessage, 16U> mIn;
|
||||
emugl::MessageChannel<bool, 16U> mOut;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
|
@ -207,162 +206,152 @@ namespace {
|
|||
// The thread ends with a CMD_FINALIZE.
|
||||
//
|
||||
class RenderWindowThread : public emugl::Thread {
|
||||
public:
|
||||
RenderWindowThread(RenderWindowChannel* channel) : mChannel(channel) {}
|
||||
public:
|
||||
RenderWindowThread(RenderWindowChannel* channel) : mChannel(channel) {}
|
||||
|
||||
virtual intptr_t main() {
|
||||
D("Entering render window thread thread\n");
|
||||
sigset_t set;
|
||||
sigfillset(&set);
|
||||
pthread_sigmask(SIG_SETMASK, &set, NULL);
|
||||
bool running = true;
|
||||
while (running) {
|
||||
RenderWindowMessage msg;
|
||||
virtual intptr_t main() {
|
||||
D("Entering render window thread thread\n");
|
||||
sigset_t set;
|
||||
sigfillset(&set);
|
||||
pthread_sigmask(SIG_SETMASK, &set, NULL);
|
||||
bool running = true;
|
||||
while (running) {
|
||||
RenderWindowMessage msg;
|
||||
|
||||
D("Waiting for message from main thread\n");
|
||||
mChannel->receiveMessage(&msg);
|
||||
D("Waiting for message from main thread\n");
|
||||
mChannel->receiveMessage(&msg);
|
||||
|
||||
bool result = msg.process();
|
||||
if (msg.cmd == CMD_FINALIZE) {
|
||||
running = false;
|
||||
}
|
||||
bool result = msg.process();
|
||||
if (msg.cmd == CMD_FINALIZE) {
|
||||
running = false;
|
||||
}
|
||||
|
||||
D("Sending result (%s) to main thread\n", result ? "success" : "failure");
|
||||
mChannel->sendResult(result);
|
||||
}
|
||||
D("Exiting thread\n");
|
||||
return 0;
|
||||
D("Sending result (%s) to main thread\n", result ? "success" : "failure");
|
||||
mChannel->sendResult(result);
|
||||
}
|
||||
D("Exiting thread\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
RenderWindowChannel* mChannel;
|
||||
private:
|
||||
RenderWindowChannel* mChannel;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
RenderWindow::RenderWindow(EGLNativeDisplayType native_display,
|
||||
bool use_thread) :
|
||||
mValid(false),
|
||||
mHasSubWindow(false),
|
||||
mThread(NULL),
|
||||
mChannel(NULL) {
|
||||
if (use_thread) {
|
||||
mChannel = new RenderWindowChannel();
|
||||
mThread = new RenderWindowThread(mChannel);
|
||||
mThread->start();
|
||||
}
|
||||
RenderWindow::RenderWindow(EGLNativeDisplayType native_display, bool use_thread)
|
||||
: mValid(false), mHasSubWindow(false), mThread(NULL), mChannel(NULL) {
|
||||
if (use_thread) {
|
||||
mChannel = new RenderWindowChannel();
|
||||
mThread = new RenderWindowThread(mChannel);
|
||||
mThread->start();
|
||||
}
|
||||
|
||||
RenderWindowMessage msg;
|
||||
msg.cmd = CMD_INITIALIZE;
|
||||
msg.init.nativeDisplay = native_display;
|
||||
mValid = processMessage(msg);
|
||||
RenderWindowMessage msg;
|
||||
msg.cmd = CMD_INITIALIZE;
|
||||
msg.init.nativeDisplay = native_display;
|
||||
mValid = processMessage(msg);
|
||||
}
|
||||
|
||||
RenderWindow::~RenderWindow() {
|
||||
D("Entering\n");
|
||||
removeSubWindow();
|
||||
D("Sending CMD_FINALIZE\n");
|
||||
RenderWindowMessage msg;
|
||||
msg.cmd = CMD_FINALIZE;
|
||||
(void) processMessage(msg);
|
||||
D("Entering\n");
|
||||
removeSubWindow();
|
||||
D("Sending CMD_FINALIZE\n");
|
||||
RenderWindowMessage msg;
|
||||
msg.cmd = CMD_FINALIZE;
|
||||
(void)processMessage(msg);
|
||||
|
||||
if (mThread) {
|
||||
mThread->wait(NULL);
|
||||
delete mThread;
|
||||
delete mChannel;
|
||||
}
|
||||
if (mThread) {
|
||||
mThread->wait(NULL);
|
||||
delete mThread;
|
||||
delete mChannel;
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderWindow::getHardwareStrings(const char** vendor,
|
||||
const char** renderer,
|
||||
const char** version) {
|
||||
D("Entering\n");
|
||||
// TODO(digit): Move this to render window thread.
|
||||
Renderer* fb = Renderer::get();
|
||||
if (!fb) {
|
||||
D("No framebuffer!\n");
|
||||
return false;
|
||||
}
|
||||
fb->getGLStrings(vendor, renderer, version);
|
||||
D("Exiting vendor=[%s] renderer=[%s] version=[%s]\n",
|
||||
*vendor, *renderer, *version);
|
||||
D("Entering\n");
|
||||
// TODO(digit): Move this to render window thread.
|
||||
Renderer* fb = Renderer::get();
|
||||
if (!fb) {
|
||||
D("No framebuffer!\n");
|
||||
return false;
|
||||
}
|
||||
fb->getGLStrings(vendor, renderer, version);
|
||||
D("Exiting vendor=[%s] renderer=[%s] version=[%s]\n", *vendor, *renderer,
|
||||
*version);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RenderWindow::setupSubWindow(FBNativeWindowType window,
|
||||
int wx,
|
||||
int wy,
|
||||
int ww,
|
||||
int wh,
|
||||
int fbw,
|
||||
int fbh,
|
||||
float dpr,
|
||||
bool RenderWindow::setupSubWindow(FBNativeWindowType window, int wx, int wy,
|
||||
int ww, int wh, int fbw, int fbh, float dpr,
|
||||
float zRot) {
|
||||
D("Entering mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false");
|
||||
D("Entering mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false");
|
||||
|
||||
RenderWindowMessage msg;
|
||||
msg.cmd = CMD_SETUP_SUBWINDOW;
|
||||
msg.subwindow.parent = window;
|
||||
msg.subwindow.wx = wx;
|
||||
msg.subwindow.wy = wy;
|
||||
msg.subwindow.ww = ww;
|
||||
msg.subwindow.wh = wh;
|
||||
msg.subwindow.fbw = fbw;
|
||||
msg.subwindow.fbh = fbh;
|
||||
msg.subwindow.dpr = dpr;
|
||||
msg.subwindow.rotation = zRot;
|
||||
RenderWindowMessage msg;
|
||||
msg.cmd = CMD_SETUP_SUBWINDOW;
|
||||
msg.subwindow.parent = window;
|
||||
msg.subwindow.wx = wx;
|
||||
msg.subwindow.wy = wy;
|
||||
msg.subwindow.ww = ww;
|
||||
msg.subwindow.wh = wh;
|
||||
msg.subwindow.fbw = fbw;
|
||||
msg.subwindow.fbh = fbh;
|
||||
msg.subwindow.dpr = dpr;
|
||||
msg.subwindow.rotation = zRot;
|
||||
|
||||
mHasSubWindow = processMessage(msg);
|
||||
D("Exiting mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false");
|
||||
return mHasSubWindow;
|
||||
mHasSubWindow = processMessage(msg);
|
||||
D("Exiting mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false");
|
||||
return mHasSubWindow;
|
||||
}
|
||||
|
||||
bool RenderWindow::removeSubWindow() {
|
||||
D("Entering mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false");
|
||||
if (!mHasSubWindow) {
|
||||
return false;
|
||||
}
|
||||
mHasSubWindow = false;
|
||||
D("Entering mHasSubWindow=%s\n", mHasSubWindow ? "true" : "false");
|
||||
if (!mHasSubWindow) {
|
||||
return false;
|
||||
}
|
||||
mHasSubWindow = false;
|
||||
|
||||
RenderWindowMessage msg;
|
||||
msg.cmd = CMD_REMOVE_SUBWINDOW;
|
||||
bool result = processMessage(msg);
|
||||
D("Exiting result=%s\n", result ? "success" : "failure");
|
||||
return result;
|
||||
RenderWindowMessage msg;
|
||||
msg.cmd = CMD_REMOVE_SUBWINDOW;
|
||||
bool result = processMessage(msg);
|
||||
D("Exiting result=%s\n", result ? "success" : "failure");
|
||||
return result;
|
||||
}
|
||||
|
||||
void RenderWindow::setRotation(float zRot) {
|
||||
D("Entering rotation=%f\n", zRot);
|
||||
RenderWindowMessage msg;
|
||||
msg.cmd = CMD_SET_ROTATION;
|
||||
msg.rotation = zRot;
|
||||
(void) processMessage(msg);
|
||||
D("Exiting\n");
|
||||
D("Entering rotation=%f\n", zRot);
|
||||
RenderWindowMessage msg;
|
||||
msg.cmd = CMD_SET_ROTATION;
|
||||
msg.rotation = zRot;
|
||||
(void)processMessage(msg);
|
||||
D("Exiting\n");
|
||||
}
|
||||
|
||||
void RenderWindow::setTranslation(float px, float py) {
|
||||
D("Entering translation=%f,%f\n", px, py);
|
||||
RenderWindowMessage msg;
|
||||
msg.cmd = CMD_SET_TRANSLATION;
|
||||
msg.trans.px = px;
|
||||
msg.trans.py = py;
|
||||
(void) processMessage(msg);
|
||||
D("Exiting\n");
|
||||
D("Entering translation=%f,%f\n", px, py);
|
||||
RenderWindowMessage msg;
|
||||
msg.cmd = CMD_SET_TRANSLATION;
|
||||
msg.trans.px = px;
|
||||
msg.trans.py = py;
|
||||
(void)processMessage(msg);
|
||||
D("Exiting\n");
|
||||
}
|
||||
|
||||
void RenderWindow::repaint() {
|
||||
D("Entering\n");
|
||||
RenderWindowMessage msg;
|
||||
msg.cmd = CMD_REPAINT;
|
||||
(void) processMessage(msg);
|
||||
D("Exiting\n");
|
||||
D("Entering\n");
|
||||
RenderWindowMessage msg;
|
||||
msg.cmd = CMD_REPAINT;
|
||||
(void)processMessage(msg);
|
||||
D("Exiting\n");
|
||||
}
|
||||
|
||||
bool RenderWindow::processMessage(const RenderWindowMessage& msg) {
|
||||
if (mChannel) {
|
||||
return mChannel->sendMessageAndGetResult(msg);
|
||||
} else {
|
||||
return msg.process();
|
||||
}
|
||||
if (mChannel) {
|
||||
return mChannel->sendMessageAndGetResult(msg);
|
||||
} else {
|
||||
return msg.process();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,84 +47,76 @@ struct RenderWindowMessage;
|
|||
// 6) Call repaint() to force a repaint().
|
||||
//
|
||||
class RenderWindow {
|
||||
public:
|
||||
// Create new instance. |width| and |height| are the dimensions of the
|
||||
// emulated accelerated framebuffer. |use_thread| can be true to force
|
||||
// the use of a separate thread, which might be required on some platforms
|
||||
// to avoid GL-realted corruption issues in the main window. Call
|
||||
// isValid() after construction to verify that it worked properly.
|
||||
//
|
||||
// |use_sub_window| is true if the client will call setupSubWindow(),
|
||||
// and false if it will call setPostCallback().
|
||||
//
|
||||
// Note that this call doesn't display anything, it just initializes
|
||||
// the library, use setupSubWindow() to display something.
|
||||
RenderWindow(EGLNativeDisplayType native_display, bool use_thread);
|
||||
public:
|
||||
// Create new instance. |width| and |height| are the dimensions of the
|
||||
// emulated accelerated framebuffer. |use_thread| can be true to force
|
||||
// the use of a separate thread, which might be required on some platforms
|
||||
// to avoid GL-realted corruption issues in the main window. Call
|
||||
// isValid() after construction to verify that it worked properly.
|
||||
//
|
||||
// |use_sub_window| is true if the client will call setupSubWindow(),
|
||||
// and false if it will call setPostCallback().
|
||||
//
|
||||
// Note that this call doesn't display anything, it just initializes
|
||||
// the library, use setupSubWindow() to display something.
|
||||
RenderWindow(EGLNativeDisplayType native_display, bool use_thread);
|
||||
|
||||
// Destructor. This will automatically call removeSubWindow() is needed.
|
||||
~RenderWindow();
|
||||
// Destructor. This will automatically call removeSubWindow() is needed.
|
||||
~RenderWindow();
|
||||
|
||||
// Returns true if the RenderWindow instance is valid, which really
|
||||
// means that the constructor succeeded.
|
||||
bool isValid() const { return mValid; }
|
||||
// Returns true if the RenderWindow instance is valid, which really
|
||||
// means that the constructor succeeded.
|
||||
bool isValid() const { return mValid; }
|
||||
|
||||
// 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,
|
||||
// and |*version| to the GL version one. On failure, return false.
|
||||
bool getHardwareStrings(const char** vendor,
|
||||
const char** renderer,
|
||||
const char** version);
|
||||
// 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,
|
||||
// and |*version| to the GL version one. On failure, return false.
|
||||
bool getHardwareStrings(const char** vendor, const char** renderer,
|
||||
const char** version);
|
||||
|
||||
// Start displaying the emulated framebuffer using a sub-window of a
|
||||
// parent |window| id. |wx|, |wy|, |ww| and |wh| are the position
|
||||
// and dimension of the sub-window, relative to its parent.
|
||||
// |fbw| and |fbh| are the dimensions of the underlying guest framebuffer.
|
||||
// |dpr| is the device pixel ratio for the monitor, which is required for
|
||||
// higher-density displays (such as retina).
|
||||
// |rotation| is a clockwise-rotation for the content. Only multiples of
|
||||
// 90. are accepted. Returns true on success, false otherwise.
|
||||
//
|
||||
// If the subwindow already exists, this function will update
|
||||
// the dimensions of the subwindow, backing framebuffer, and rendering
|
||||
// pipeline to reflect the new values.
|
||||
//
|
||||
// One can call removeSubWindow() to remove the sub-window.
|
||||
bool setupSubWindow(FBNativeWindowType window,
|
||||
int wx,
|
||||
int wy,
|
||||
int ww,
|
||||
int wh,
|
||||
int fbw,
|
||||
int fbh,
|
||||
float dpr,
|
||||
float rotation);
|
||||
// Start displaying the emulated framebuffer using a sub-window of a
|
||||
// parent |window| id. |wx|, |wy|, |ww| and |wh| are the position
|
||||
// and dimension of the sub-window, relative to its parent.
|
||||
// |fbw| and |fbh| are the dimensions of the underlying guest framebuffer.
|
||||
// |dpr| is the device pixel ratio for the monitor, which is required for
|
||||
// higher-density displays (such as retina).
|
||||
// |rotation| is a clockwise-rotation for the content. Only multiples of
|
||||
// 90. are accepted. Returns true on success, false otherwise.
|
||||
//
|
||||
// If the subwindow already exists, this function will update
|
||||
// the dimensions of the subwindow, backing framebuffer, and rendering
|
||||
// pipeline to reflect the new values.
|
||||
//
|
||||
// One can call removeSubWindow() to remove the sub-window.
|
||||
bool setupSubWindow(FBNativeWindowType window, int wx, int wy, int ww, int wh,
|
||||
int fbw, int fbh, float dpr, float rotation);
|
||||
|
||||
// Remove the sub-window created by calling setupSubWindow().
|
||||
// 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
|
||||
// otherwise.
|
||||
bool removeSubWindow();
|
||||
// Remove the sub-window created by calling setupSubWindow().
|
||||
// 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
|
||||
// otherwise.
|
||||
bool removeSubWindow();
|
||||
|
||||
// Change the display rotation on the fly. |zRot| is a clockwise rotation
|
||||
// angle in degrees. Only multiples of 90. are accepted.
|
||||
void setRotation(float zRot);
|
||||
// Change the display rotation on the fly. |zRot| is a clockwise rotation
|
||||
// angle in degrees. Only multiples of 90. are accepted.
|
||||
void setRotation(float zRot);
|
||||
|
||||
// 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
|
||||
// bottom left of the subwindow", and (1,1) indicating "align the top right of
|
||||
// the framebuffer with the top right of the subwindow."
|
||||
void setTranslation(float px, float py);
|
||||
// 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
|
||||
// bottom left of the subwindow", and (1,1) indicating "align the top right of
|
||||
// the framebuffer with the top right of the subwindow."
|
||||
void setTranslation(float px, float py);
|
||||
|
||||
// Force a repaint of the whole content into the sub-window.
|
||||
void repaint();
|
||||
// Force a repaint of the whole content into the sub-window.
|
||||
void repaint();
|
||||
|
||||
private:
|
||||
bool processMessage(const RenderWindowMessage& msg);
|
||||
private:
|
||||
bool processMessage(const RenderWindowMessage& msg);
|
||||
|
||||
bool mValid;
|
||||
bool mHasSubWindow;
|
||||
emugl::Thread* mThread;
|
||||
RenderWindowChannel* mChannel;
|
||||
bool mValid;
|
||||
bool mHasSubWindow;
|
||||
emugl::Thread* mThread;
|
||||
RenderWindowChannel* mChannel;
|
||||
};
|
||||
|
||||
#endif // ANDROID_EMUGL_LIBRENDER_RENDER_WINDOW_H
|
||||
|
|
|
|||
|
|
@ -16,56 +16,34 @@
|
|||
|
||||
#include "Renderable.h"
|
||||
|
||||
Renderable::Renderable(const std::string &name,
|
||||
const std::uint32_t &buffer,
|
||||
Renderable::Renderable(const std::string &name, const std::uint32_t &buffer,
|
||||
const anbox::graphics::Rect &screen_position,
|
||||
const anbox::graphics::Rect &crop,
|
||||
const glm::mat4 &transformation,
|
||||
const float &alpha) :
|
||||
name_(name),
|
||||
buffer_(buffer),
|
||||
screen_position_(screen_position),
|
||||
crop_(crop),
|
||||
transformation_(transformation),
|
||||
alpha_(alpha)
|
||||
{
|
||||
const glm::mat4 &transformation, const float &alpha)
|
||||
: name_(name),
|
||||
buffer_(buffer),
|
||||
screen_position_(screen_position),
|
||||
crop_(crop),
|
||||
transformation_(transformation),
|
||||
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
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
glm::mat4 Renderable::transformation() const { return transformation_; }
|
||||
|
||||
std::uint32_t Renderable::buffer() const
|
||||
{
|
||||
return buffer_;
|
||||
}
|
||||
float Renderable::alpha() const { return alpha_; }
|
||||
|
||||
anbox::graphics::Rect Renderable::screen_position() const
|
||||
{
|
||||
return 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;
|
||||
void Renderable::set_screen_position(
|
||||
const anbox::graphics::Rect &screen_position) {
|
||||
screen_position_ = screen_position;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,33 +26,30 @@
|
|||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
class Renderable
|
||||
{
|
||||
public:
|
||||
Renderable(const std::string &name,
|
||||
const std::uint32_t &buffer,
|
||||
const anbox::graphics::Rect &screen_position,
|
||||
const anbox::graphics::Rect &crop = {},
|
||||
const glm::mat4 &transformation = {},
|
||||
const float &alpha = 1.0f);
|
||||
~Renderable();
|
||||
class Renderable {
|
||||
public:
|
||||
Renderable(const std::string &name, const std::uint32_t &buffer,
|
||||
const anbox::graphics::Rect &screen_position,
|
||||
const anbox::graphics::Rect &crop = {},
|
||||
const glm::mat4 &transformation = {}, const float &alpha = 1.0f);
|
||||
~Renderable();
|
||||
|
||||
std::string name() const;
|
||||
std::uint32_t buffer() const;
|
||||
anbox::graphics::Rect screen_position() const;
|
||||
anbox::graphics::Rect crop() const;
|
||||
glm::mat4 transformation() const;
|
||||
float alpha() const;
|
||||
std::string name() const;
|
||||
std::uint32_t buffer() const;
|
||||
anbox::graphics::Rect screen_position() const;
|
||||
anbox::graphics::Rect crop() const;
|
||||
glm::mat4 transformation() 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:
|
||||
std::string name_;
|
||||
std::uint32_t buffer_;
|
||||
anbox::graphics::Rect screen_position_;
|
||||
anbox::graphics::Rect crop_;
|
||||
glm::mat4 transformation_;
|
||||
float alpha_;
|
||||
private:
|
||||
std::string name_;
|
||||
std::uint32_t buffer_;
|
||||
anbox::graphics::Rect screen_position_;
|
||||
anbox::graphics::Rect crop_;
|
||||
glm::mat4 transformation_;
|
||||
float alpha_;
|
||||
};
|
||||
|
||||
typedef std::vector<Renderable> RenderableList;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -17,18 +17,18 @@
|
|||
#define _LIBRENDER_FRAMEBUFFER_H
|
||||
|
||||
#include "ColorBuffer.h"
|
||||
#include "emugl/common/mutex.h"
|
||||
#include "RendererConfig.h"
|
||||
#include "RenderContext.h"
|
||||
#include "RendererConfig.h"
|
||||
#include "TextureDraw.h"
|
||||
#include "WindowSurface.h"
|
||||
#include "emugl/common/mutex.h"
|
||||
|
||||
#include "OpenglRender/render_api.h"
|
||||
|
||||
#include "Renderable.h"
|
||||
|
||||
#include "anbox/graphics/program_family.h"
|
||||
#include "anbox/graphics/primitives.h"
|
||||
#include "anbox/graphics/program_family.h"
|
||||
|
||||
#include <EGL/egl.h>
|
||||
|
||||
|
|
@ -41,11 +41,12 @@
|
|||
typedef uint32_t HandleType;
|
||||
|
||||
struct ColorBufferRef {
|
||||
ColorBufferPtr cb;
|
||||
uint32_t refcount; // number of client-side references
|
||||
ColorBufferPtr cb;
|
||||
uint32_t refcount; // number of client-side references
|
||||
};
|
||||
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;
|
||||
|
||||
// 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
|
||||
// the underlying EGL implementation.
|
||||
struct RendererCaps {
|
||||
bool has_eglimage_texture_2d;
|
||||
bool has_eglimage_renderbuffer;
|
||||
EGLint eglMajor;
|
||||
EGLint eglMinor;
|
||||
bool has_eglimage_texture_2d;
|
||||
bool has_eglimage_renderbuffer;
|
||||
EGLint eglMajor;
|
||||
EGLint eglMinor;
|
||||
};
|
||||
|
||||
struct RendererWindow;
|
||||
|
|
@ -73,252 +74,251 @@ struct RendererWindow;
|
|||
// and which must be previously setup by calling initialize().
|
||||
//
|
||||
class Renderer {
|
||||
public:
|
||||
// Initialize the global instance.
|
||||
// |width| and |height| are the dimensions of the emulator GPU display
|
||||
// in pixels. |useSubWindow| is true to indicate that the caller
|
||||
// will use setupSubWindow() to let EmuGL display the GPU content in its
|
||||
// own sub-windows. If false, this means the caller will use
|
||||
// setPostCallback() instead to retrieve the content.
|
||||
// Returns true on success, false otherwise.
|
||||
static bool initialize(EGLNativeDisplayType nativeDisplay);
|
||||
public:
|
||||
// Initialize the global instance.
|
||||
// |width| and |height| are the dimensions of the emulator GPU display
|
||||
// in pixels. |useSubWindow| is true to indicate that the caller
|
||||
// will use setupSubWindow() to let EmuGL display the GPU content in its
|
||||
// own sub-windows. If false, this means the caller will use
|
||||
// setPostCallback() instead to retrieve the content.
|
||||
// Returns true on success, false otherwise.
|
||||
static bool initialize(EGLNativeDisplayType nativeDisplay);
|
||||
|
||||
// Finalize the instance.
|
||||
void finalize();
|
||||
// Finalize the instance.
|
||||
void finalize();
|
||||
|
||||
// Return a pointer to the global instance. initialize() must be called
|
||||
// previously, or this will return NULL.
|
||||
static Renderer *get() { return s_renderer; }
|
||||
// Return a pointer to the global instance. initialize() must be called
|
||||
// previously, or this will return NULL.
|
||||
static Renderer* get() { return s_renderer; }
|
||||
|
||||
// Return the capabilities of the underlying display.
|
||||
const RendererCaps &getCaps() const { return m_caps; }
|
||||
// Return the capabilities of the underlying display.
|
||||
const RendererCaps& getCaps() const { return m_caps; }
|
||||
|
||||
// Return the list of configs available from this display.
|
||||
const RendererConfigList* getConfigs() const { return m_configs; }
|
||||
// Return the list of configs available from this display.
|
||||
const RendererConfigList* getConfigs() const { return m_configs; }
|
||||
|
||||
// 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,
|
||||
// so only do this when you need to.
|
||||
void setPostCallback(OnPostFn onPost, void* onPostContext);
|
||||
// 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,
|
||||
// so only do this when you need to.
|
||||
void setPostCallback(OnPostFn onPost, void* onPostContext);
|
||||
|
||||
// Retrieve the GL strings of the underlying EGL/GLES implementation.
|
||||
// On return, |*vendor|, |*renderer| and |*version| will point to strings
|
||||
// that are owned by the instance (and must not be freed by the caller).
|
||||
void getGLStrings(const char** vendor,
|
||||
const char** renderer,
|
||||
const char** version) const {
|
||||
*vendor = m_glVendor;
|
||||
*renderer = m_glRenderer;
|
||||
*version = m_glVersion;
|
||||
}
|
||||
// Retrieve the GL strings of the underlying EGL/GLES implementation.
|
||||
// On return, |*vendor|, |*renderer| and |*version| will point to strings
|
||||
// that are owned by the instance (and must not be freed by the caller).
|
||||
void getGLStrings(const char** vendor, const char** renderer,
|
||||
const char** version) const {
|
||||
*vendor = m_glVendor;
|
||||
*renderer = m_glRenderer;
|
||||
*version = m_glVersion;
|
||||
}
|
||||
|
||||
RendererWindow* createNativeWindow(EGLNativeWindowType native_window);
|
||||
void destroyNativeWindow(RendererWindow *window);
|
||||
void destroyNativeWindow(EGLNativeWindowType native_window);
|
||||
RendererWindow* createNativeWindow(EGLNativeWindowType native_window);
|
||||
void destroyNativeWindow(RendererWindow* window);
|
||||
void destroyNativeWindow(EGLNativeWindowType native_window);
|
||||
|
||||
// Create a new RenderContext instance for this display instance.
|
||||
// |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_isGL2| is true to create a GLES 2.x context, or false for a GLES 1.x
|
||||
// one.
|
||||
// Return a new handle value, which will be 0 in case of error.
|
||||
HandleType createRenderContext(
|
||||
int p_config, HandleType p_share, bool p_isGL2 = false);
|
||||
// Create a new RenderContext instance for this display instance.
|
||||
// |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_isGL2| is true to create a GLES 2.x context, or false for a GLES 1.x
|
||||
// one.
|
||||
// Return a new handle value, which will be 0 in case of error.
|
||||
HandleType createRenderContext(int p_config, HandleType p_share,
|
||||
bool p_isGL2 = false);
|
||||
|
||||
// Create a new WindowSurface instance from this display instance.
|
||||
// |p_config| is the index of one of the configs returned by getConfigs().
|
||||
// |p_width| and |p_height| are the window dimensions in pixels.
|
||||
// Return a new handle value, or 0 in case of error.
|
||||
HandleType createWindowSurface(int p_config, int p_width, int p_height);
|
||||
// Create a new WindowSurface instance from this display instance.
|
||||
// |p_config| is the index of one of the configs returned by getConfigs().
|
||||
// |p_width| and |p_height| are the window dimensions in pixels.
|
||||
// Return a new handle value, or 0 in case of error.
|
||||
HandleType createWindowSurface(int p_config, int p_width, int p_height);
|
||||
|
||||
// Create a new ColorBuffer instance from this display instance.
|
||||
// |p_width| and |p_height| are its dimensions in pixels.
|
||||
// |p_internalFormat| is the pixel format. See ColorBuffer::create() for
|
||||
// list of valid values. Note that ColorBuffer instances are reference-
|
||||
// counted. Use openColorBuffer / closeColorBuffer to operate on the
|
||||
// internal count.
|
||||
HandleType createColorBuffer(
|
||||
int p_width, int p_height, GLenum p_internalFormat);
|
||||
// Create a new ColorBuffer instance from this display instance.
|
||||
// |p_width| and |p_height| are its dimensions in pixels.
|
||||
// |p_internalFormat| is the pixel format. See ColorBuffer::create() for
|
||||
// list of valid values. Note that ColorBuffer instances are reference-
|
||||
// counted. Use openColorBuffer / closeColorBuffer to operate on the
|
||||
// internal count.
|
||||
HandleType createColorBuffer(int p_width, int p_height,
|
||||
GLenum p_internalFormat);
|
||||
|
||||
// Call this function when a render thread terminates to destroy all
|
||||
// the remaining contexts it created. Necessary to avoid leaking host
|
||||
// contexts when a guest application crashes, for example.
|
||||
void drainRenderContext();
|
||||
// Call this function when a render thread terminates to destroy all
|
||||
// the remaining contexts it created. Necessary to avoid leaking host
|
||||
// contexts when a guest application crashes, for example.
|
||||
void drainRenderContext();
|
||||
|
||||
// Call this function when a render thread terminates to destroy all
|
||||
// remaining window surfqce it created. Necessary to avoid leaking
|
||||
// host buffers when a guest application crashes, for example.
|
||||
void drainWindowSurface();
|
||||
// Call this function when a render thread terminates to destroy all
|
||||
// remaining window surfqce it created. Necessary to avoid leaking
|
||||
// host buffers when a guest application crashes, for example.
|
||||
void drainWindowSurface();
|
||||
|
||||
// Destroy a given RenderContext instance. |p_context| is its handle
|
||||
// value as returned by createRenderContext().
|
||||
void DestroyRenderContext(HandleType p_context);
|
||||
// Destroy a given RenderContext instance. |p_context| is its handle
|
||||
// value as returned by createRenderContext().
|
||||
void DestroyRenderContext(HandleType p_context);
|
||||
|
||||
// Destroy a given WindowSurface instance. |p_surcace| is its handle
|
||||
// value as returned by createWindowSurface().
|
||||
void DestroyWindowSurface(HandleType p_surface);
|
||||
// Destroy a given WindowSurface instance. |p_surcace| is its handle
|
||||
// value as returned by createWindowSurface().
|
||||
void DestroyWindowSurface(HandleType p_surface);
|
||||
|
||||
// Increment the reference count associated with a given ColorBuffer
|
||||
// instance. |p_colorbuffer| is its handle value as returned by
|
||||
// createColorBuffer().
|
||||
int openColorBuffer(HandleType p_colorbuffer);
|
||||
// Increment the reference count associated with a given ColorBuffer
|
||||
// instance. |p_colorbuffer| is its handle value as returned by
|
||||
// createColorBuffer().
|
||||
int openColorBuffer(HandleType p_colorbuffer);
|
||||
|
||||
// Decrement the reference count associated with a given ColorBuffer
|
||||
// instance. |p_colorbuffer| is its handle value as returned by
|
||||
// createColorBuffer(). Note that if the reference count reaches 0,
|
||||
// the instance is destroyed automatically.
|
||||
void closeColorBuffer(HandleType p_colorbuffer);
|
||||
// Decrement the reference count associated with a given ColorBuffer
|
||||
// instance. |p_colorbuffer| is its handle value as returned by
|
||||
// createColorBuffer(). Note that if the reference count reaches 0,
|
||||
// the instance is destroyed automatically.
|
||||
void closeColorBuffer(HandleType p_colorbuffer);
|
||||
|
||||
// Equivalent for eglMakeCurrent() for the current display.
|
||||
// |p_context|, |p_drawSurface| and |p_readSurface| are the handle values
|
||||
// of the context, the draw surface and the read surface, respectively.
|
||||
// Returns true on success, false on failure.
|
||||
// Note: if all handle values are 0, this is an unbind operation.
|
||||
bool bindContext(HandleType p_context,
|
||||
HandleType p_drawSurface,
|
||||
HandleType p_readSurface);
|
||||
// Equivalent for eglMakeCurrent() for the current display.
|
||||
// |p_context|, |p_drawSurface| and |p_readSurface| are the handle values
|
||||
// of the context, the draw surface and the read surface, respectively.
|
||||
// Returns true on success, false on failure.
|
||||
// Note: if all handle values are 0, this is an unbind operation.
|
||||
bool bindContext(HandleType p_context, HandleType p_drawSurface,
|
||||
HandleType p_readSurface);
|
||||
|
||||
// Attach a ColorBuffer to a WindowSurface instance.
|
||||
// See the documentation for WindowSurface::setColorBuffer().
|
||||
// |p_surface| is the target WindowSurface's handle value.
|
||||
// |p_colorbuffer| is the ColorBuffer handle value.
|
||||
// Returns true on success, false otherwise.
|
||||
bool setWindowSurfaceColorBuffer(
|
||||
HandleType p_surface, HandleType p_colorbuffer);
|
||||
// Attach a ColorBuffer to a WindowSurface instance.
|
||||
// See the documentation for WindowSurface::setColorBuffer().
|
||||
// |p_surface| is the target WindowSurface's handle value.
|
||||
// |p_colorbuffer| is the ColorBuffer handle value.
|
||||
// Returns true on success, false otherwise.
|
||||
bool setWindowSurfaceColorBuffer(HandleType p_surface,
|
||||
HandleType p_colorbuffer);
|
||||
|
||||
// Copy the content of a WindowSurface's Pbuffer to its attached
|
||||
// ColorBuffer. See the documentation for WindowSurface::flushColorBuffer()
|
||||
// |p_surface| is the target WindowSurface's handle value.
|
||||
// Returns true on success, false on failure.
|
||||
bool flushWindowSurfaceColorBuffer(HandleType p_surface);
|
||||
// Copy the content of a WindowSurface's Pbuffer to its attached
|
||||
// ColorBuffer. See the documentation for WindowSurface::flushColorBuffer()
|
||||
// |p_surface| is the target WindowSurface's handle value.
|
||||
// Returns true on success, false on failure.
|
||||
bool flushWindowSurfaceColorBuffer(HandleType p_surface);
|
||||
|
||||
// Bind the current context's EGL_TEXTURE_2D texture to a ColorBuffer
|
||||
// instance's EGLImage. This is intended to implement
|
||||
// glEGLImageTargetTexture2DOES() for all GLES versions.
|
||||
// |p_colorbuffer| is the ColorBuffer's handle value.
|
||||
// Returns true on success, false on failure.
|
||||
bool bindColorBufferToTexture(HandleType p_colorbuffer);
|
||||
// Bind the current context's EGL_TEXTURE_2D texture to a ColorBuffer
|
||||
// instance's EGLImage. This is intended to implement
|
||||
// glEGLImageTargetTexture2DOES() for all GLES versions.
|
||||
// |p_colorbuffer| is the ColorBuffer's handle value.
|
||||
// Returns true on success, false on failure.
|
||||
bool bindColorBufferToTexture(HandleType p_colorbuffer);
|
||||
|
||||
// Bind the current context's EGL_RENDERBUFFER_OES render buffer to this
|
||||
// ColorBuffer's EGLImage. This is intended to implement
|
||||
// glEGLImageTargetRenderbufferStorageOES() for all GLES versions.
|
||||
// |p_colorbuffer| is the ColorBuffer's handle value.
|
||||
// Returns true on success, false on failure.
|
||||
bool bindColorBufferToRenderbuffer(HandleType p_colorbuffer);
|
||||
// Bind the current context's EGL_RENDERBUFFER_OES render buffer to this
|
||||
// ColorBuffer's EGLImage. This is intended to implement
|
||||
// glEGLImageTargetRenderbufferStorageOES() for all GLES versions.
|
||||
// |p_colorbuffer| is the ColorBuffer's handle value.
|
||||
// Returns true on success, false on failure.
|
||||
bool bindColorBufferToRenderbuffer(HandleType p_colorbuffer);
|
||||
|
||||
// Read the content of a given ColorBuffer into client memory.
|
||||
// |p_colorbuffer| is the ColorBuffer's handle value. Similar
|
||||
// to glReadPixels(), this can be a slow operation.
|
||||
// |x|, |y|, |width| and |height| are the position and dimensions of
|
||||
// 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.
|
||||
// |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
|
||||
// with the pixel data.
|
||||
void readColorBuffer(HandleType p_colorbuffer,
|
||||
int x, int y, int width, int height,
|
||||
GLenum format, GLenum type, void *pixels);
|
||||
// Read the content of a given ColorBuffer into client memory.
|
||||
// |p_colorbuffer| is the ColorBuffer's handle value. Similar
|
||||
// to glReadPixels(), this can be a slow operation.
|
||||
// |x|, |y|, |width| and |height| are the position and dimensions of
|
||||
// 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.
|
||||
// |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
|
||||
// with the pixel data.
|
||||
void readColorBuffer(HandleType p_colorbuffer, int x, int y, int width,
|
||||
int height, GLenum format, GLenum type, void* pixels);
|
||||
|
||||
// Update the content of a given ColorBuffer from client data.
|
||||
// |p_colorbuffer| is the ColorBuffer's handle value. Similar
|
||||
// to glReadPixels(), this can be a slow operation.
|
||||
// |x|, |y|, |width| and |height| are the position and dimensions of
|
||||
// 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.
|
||||
// |type| is the type of pixel data, e.g. GL_UNSIGNED_BYTE.
|
||||
// |pixels| is the address of a buffer containing the new pixel data.
|
||||
// Returns true on success, false otherwise.
|
||||
bool updateColorBuffer(HandleType p_colorbuffer,
|
||||
int x, int y, int width, int height,
|
||||
GLenum format, GLenum type, void *pixels);
|
||||
// Update the content of a given ColorBuffer from client data.
|
||||
// |p_colorbuffer| is the ColorBuffer's handle value. Similar
|
||||
// to glReadPixels(), this can be a slow operation.
|
||||
// |x|, |y|, |width| and |height| are the position and dimensions of
|
||||
// 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.
|
||||
// |type| is the type of pixel data, e.g. GL_UNSIGNED_BYTE.
|
||||
// |pixels| is the address of a buffer containing the new pixel data.
|
||||
// Returns true on success, false otherwise.
|
||||
bool updateColorBuffer(HandleType p_colorbuffer, int x, int y, int width,
|
||||
int height, 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.
|
||||
EGLDisplay getDisplay() const { return m_eglDisplay; }
|
||||
// Return the host EGLDisplay used by this instance.
|
||||
EGLDisplay getDisplay() const { return m_eglDisplay; }
|
||||
|
||||
// Return a TextureDraw instance that can be used with this surfaces
|
||||
// and windows created by this instance.
|
||||
TextureDraw* getTextureDraw() const { return m_textureDraw; }
|
||||
// Return a TextureDraw instance that can be used with this surfaces
|
||||
// and windows created by this instance.
|
||||
TextureDraw* getTextureDraw() const { return m_textureDraw; }
|
||||
|
||||
HandleType createClientImage(HandleType context, EGLenum target, GLuint buffer);
|
||||
EGLBoolean destroyClientImage(HandleType image);
|
||||
HandleType createClientImage(HandleType context, EGLenum target,
|
||||
GLuint buffer);
|
||||
EGLBoolean destroyClientImage(HandleType image);
|
||||
|
||||
// Used internally.
|
||||
bool bind_locked();
|
||||
bool unbind_locked();
|
||||
// Used internally.
|
||||
bool bind_locked();
|
||||
bool unbind_locked();
|
||||
|
||||
private:
|
||||
Renderer();
|
||||
~Renderer();
|
||||
HandleType genHandle();
|
||||
private:
|
||||
Renderer();
|
||||
~Renderer();
|
||||
HandleType genHandle();
|
||||
|
||||
bool bindWindow_locked(RendererWindow *window);
|
||||
bool bindWindow_locked(RendererWindow* window);
|
||||
|
||||
void setupViewport(RendererWindow *window, const anbox::graphics::Rect &rect);
|
||||
struct Program;
|
||||
void draw(RendererWindow *window, const Renderable &renderable, const Program &prog);
|
||||
void tessellate(std::vector<anbox::graphics::Primitive>& primitives,
|
||||
const anbox::graphics::Rect &buf_size,
|
||||
const Renderable &renderable);
|
||||
void setupViewport(RendererWindow* window, const anbox::graphics::Rect& rect);
|
||||
struct Program;
|
||||
void draw(RendererWindow* window, const Renderable& renderable,
|
||||
const Program& prog);
|
||||
void tessellate(std::vector<anbox::graphics::Primitive>& primitives,
|
||||
const anbox::graphics::Rect& buf_size,
|
||||
const Renderable& renderable);
|
||||
|
||||
private:
|
||||
static Renderer *s_renderer;
|
||||
static HandleType s_nextHandle;
|
||||
emugl::Mutex m_lock;
|
||||
RendererConfigList* m_configs;
|
||||
FBNativeWindowType m_nativeWindow;
|
||||
RendererCaps m_caps;
|
||||
EGLDisplay m_eglDisplay;
|
||||
RenderContextMap m_contexts;
|
||||
WindowSurfaceMap m_windows;
|
||||
ColorBufferMap m_colorbuffers;
|
||||
ColorBuffer::Helper* m_colorBufferHelper;
|
||||
private:
|
||||
static Renderer* s_renderer;
|
||||
static HandleType s_nextHandle;
|
||||
emugl::Mutex m_lock;
|
||||
RendererConfigList* m_configs;
|
||||
FBNativeWindowType m_nativeWindow;
|
||||
RendererCaps m_caps;
|
||||
EGLDisplay m_eglDisplay;
|
||||
RenderContextMap m_contexts;
|
||||
WindowSurfaceMap m_windows;
|
||||
ColorBufferMap m_colorbuffers;
|
||||
ColorBuffer::Helper* m_colorBufferHelper;
|
||||
|
||||
EGLContext m_eglContext;
|
||||
EGLSurface m_pbufSurface;
|
||||
EGLContext m_pbufContext;
|
||||
EGLContext m_eglContext;
|
||||
EGLSurface m_pbufSurface;
|
||||
EGLContext m_pbufContext;
|
||||
|
||||
EGLContext m_prevContext;
|
||||
EGLSurface m_prevReadSurf;
|
||||
EGLSurface m_prevDrawSurf;
|
||||
TextureDraw* m_textureDraw;
|
||||
EGLConfig m_eglConfig;
|
||||
HandleType m_lastPostedColorBuffer;
|
||||
EGLContext m_prevContext;
|
||||
EGLSurface m_prevReadSurf;
|
||||
EGLSurface m_prevDrawSurf;
|
||||
TextureDraw* m_textureDraw;
|
||||
EGLConfig m_eglConfig;
|
||||
HandleType m_lastPostedColorBuffer;
|
||||
|
||||
int m_statsNumFrames;
|
||||
long long m_statsStartTime;
|
||||
bool m_fpsStats;
|
||||
int m_statsNumFrames;
|
||||
long long m_statsStartTime;
|
||||
bool m_fpsStats;
|
||||
|
||||
const char* m_glVendor;
|
||||
const char* m_glRenderer;
|
||||
const char* m_glVersion;
|
||||
const char* m_glVendor;
|
||||
const char* m_glRenderer;
|
||||
const char* m_glVersion;
|
||||
|
||||
std::map<EGLNativeWindowType,RendererWindow*> m_nativeWindows;
|
||||
std::map<EGLNativeWindowType, RendererWindow*> m_nativeWindows;
|
||||
|
||||
anbox::graphics::ProgramFamily m_family;
|
||||
struct Program
|
||||
{
|
||||
GLuint id = 0;
|
||||
GLint tex_uniform = -1;
|
||||
GLint position_attr = -1;
|
||||
GLint texcoord_attr = -1;
|
||||
GLint center_uniform = -1;
|
||||
GLint display_transform_uniform = -1;
|
||||
GLint transform_uniform = -1;
|
||||
GLint screen_to_gl_coords_uniform = -1;
|
||||
GLint alpha_uniform = -1;
|
||||
mutable long long last_used_frameno = 0;
|
||||
anbox::graphics::ProgramFamily m_family;
|
||||
struct Program {
|
||||
GLuint id = 0;
|
||||
GLint tex_uniform = -1;
|
||||
GLint position_attr = -1;
|
||||
GLint texcoord_attr = -1;
|
||||
GLint center_uniform = -1;
|
||||
GLint display_transform_uniform = -1;
|
||||
GLint transform_uniform = -1;
|
||||
GLint screen_to_gl_coords_uniform = -1;
|
||||
GLint alpha_uniform = -1;
|
||||
mutable long long last_used_frameno = 0;
|
||||
|
||||
Program(GLuint program_id);
|
||||
Program() {}
|
||||
};
|
||||
Program m_defaultProgram, m_alphaProgram;
|
||||
Program(GLuint program_id);
|
||||
Program() {}
|
||||
};
|
||||
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 defaultFShader;
|
||||
static const GLchar* const alphaFShader;
|
||||
static const GLchar* const vshader;
|
||||
static const GLchar* const defaultFShader;
|
||||
static const GLchar* const alphaFShader;
|
||||
};
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -21,255 +21,223 @@
|
|||
|
||||
namespace {
|
||||
|
||||
#define E(...) fprintf(stderr, __VA_ARGS__)
|
||||
#define E(...) fprintf(stderr, __VA_ARGS__)
|
||||
|
||||
const GLuint kConfigAttributes[] = {
|
||||
EGL_DEPTH_SIZE, // must be first - see getDepthSize()
|
||||
EGL_STENCIL_SIZE, // must be second - see getStencilSize()
|
||||
EGL_RENDERABLE_TYPE,// must be third - see getRenderableType()
|
||||
EGL_SURFACE_TYPE, // must be fourth - see getSurfaceType()
|
||||
EGL_CONFIG_ID, // must be fifth - see chooseConfig()
|
||||
EGL_BUFFER_SIZE,
|
||||
EGL_ALPHA_SIZE,
|
||||
EGL_BLUE_SIZE,
|
||||
EGL_GREEN_SIZE,
|
||||
EGL_RED_SIZE,
|
||||
EGL_CONFIG_CAVEAT,
|
||||
EGL_LEVEL,
|
||||
EGL_MAX_PBUFFER_HEIGHT,
|
||||
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_DEPTH_SIZE, // must be first - see getDepthSize()
|
||||
EGL_STENCIL_SIZE, // must be second - see getStencilSize()
|
||||
EGL_RENDERABLE_TYPE, // must be third - see getRenderableType()
|
||||
EGL_SURFACE_TYPE, // must be fourth - see getSurfaceType()
|
||||
EGL_CONFIG_ID, // must be fifth - see chooseConfig()
|
||||
EGL_BUFFER_SIZE, EGL_ALPHA_SIZE, EGL_BLUE_SIZE, EGL_GREEN_SIZE,
|
||||
EGL_RED_SIZE, EGL_CONFIG_CAVEAT, EGL_LEVEL, EGL_MAX_PBUFFER_HEIGHT,
|
||||
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_MATCH_NATIVE_PIXMAP,
|
||||
EGL_CONFORMANT
|
||||
};
|
||||
// EGL_MATCH_NATIVE_PIXMAP,
|
||||
EGL_CONFORMANT};
|
||||
|
||||
const size_t kConfigAttributesLen =
|
||||
sizeof(kConfigAttributes) / sizeof(kConfigAttributes[0]);
|
||||
sizeof(kConfigAttributes) / sizeof(kConfigAttributes[0]);
|
||||
|
||||
bool isCompatibleHostConfig(EGLConfig config, EGLDisplay display) {
|
||||
// Filter out configs which do not support pbuffers, since they
|
||||
// are used to implement window surfaces.
|
||||
EGLint surfaceType;
|
||||
s_egl.eglGetConfigAttrib(
|
||||
display, config, EGL_SURFACE_TYPE, &surfaceType);
|
||||
if (!(surfaceType & EGL_PBUFFER_BIT)) {
|
||||
return false;
|
||||
}
|
||||
// Filter out configs which do not support pbuffers, since they
|
||||
// are used to implement window surfaces.
|
||||
EGLint surfaceType;
|
||||
s_egl.eglGetConfigAttrib(display, config, EGL_SURFACE_TYPE, &surfaceType);
|
||||
if (!(surfaceType & EGL_PBUFFER_BIT)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Filter out configs that do not support RGB pixel values.
|
||||
EGLint redSize = 0, greenSize = 0, blueSize = 0, alphaSize = 0;
|
||||
s_egl.eglGetConfigAttrib(
|
||||
display, config,EGL_RED_SIZE, &redSize);
|
||||
s_egl.eglGetConfigAttrib(
|
||||
display, config, EGL_GREEN_SIZE, &greenSize);
|
||||
s_egl.eglGetConfigAttrib(
|
||||
display, config, EGL_BLUE_SIZE, &blueSize);
|
||||
s_egl.eglGetConfigAttrib(
|
||||
display, config, EGL_ALPHA_SIZE, &alphaSize);
|
||||
// Filter out configs that do not support RGB pixel values.
|
||||
EGLint redSize = 0, greenSize = 0, blueSize = 0, alphaSize = 0;
|
||||
s_egl.eglGetConfigAttrib(display, config, EGL_RED_SIZE, &redSize);
|
||||
s_egl.eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &greenSize);
|
||||
s_egl.eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &blueSize);
|
||||
s_egl.eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &alphaSize);
|
||||
|
||||
if (!redSize || !greenSize || !blueSize || !alphaSize) {
|
||||
return false;
|
||||
}
|
||||
if (!redSize || !greenSize || !blueSize || !alphaSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RendererConfig::~RendererConfig() {
|
||||
delete [] mAttribValues;
|
||||
RendererConfig::~RendererConfig() { 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) :
|
||||
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]);
|
||||
RendererConfigList::RendererConfigList(EGLDisplay display)
|
||||
: 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
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++;
|
||||
}
|
||||
|
||||
RendererConfigList::RendererConfigList(EGLDisplay display) :
|
||||
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;
|
||||
delete[] hostConfigs;
|
||||
}
|
||||
|
||||
RendererConfigList::~RendererConfigList() {
|
||||
for (int n = 0; n < mCount; ++n) {
|
||||
delete mConfigs[n];
|
||||
}
|
||||
delete [] mConfigs;
|
||||
for (int n = 0; n < mCount; ++n) {
|
||||
delete mConfigs[n];
|
||||
}
|
||||
delete[] mConfigs;
|
||||
}
|
||||
|
||||
int RendererConfigList::chooseConfig(const EGLint* attribs,
|
||||
EGLint* configs,
|
||||
EGLint configsSize) const {
|
||||
EGLint numHostConfigs = 0;
|
||||
if (!s_egl.eglGetConfigs(mDisplay, NULL, 0, &numHostConfigs)) {
|
||||
E("%s: Could not get number of host EGL configs\n", __FUNCTION__);
|
||||
return 0;
|
||||
int RendererConfigList::chooseConfig(const EGLint* attribs, EGLint* configs,
|
||||
EGLint configsSize) const {
|
||||
EGLint numHostConfigs = 0;
|
||||
if (!s_egl.eglGetConfigs(mDisplay, NULL, 0, &numHostConfigs)) {
|
||||
E("%s: Could not get number of host EGL configs\n", __FUNCTION__);
|
||||
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
|
||||
// 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;
|
||||
if (mustReplaceSurfaceType) {
|
||||
// There is at least on EGL_SURFACE_TYPE in |attribs|. Copy the
|
||||
// array and replace all values with EGL_PBUFFER_BIT
|
||||
newAttribs = new GLint[numAttribs + 1];
|
||||
memcpy(newAttribs, attribs, numAttribs * sizeof(GLint));
|
||||
newAttribs[numAttribs] = EGL_NONE;
|
||||
for (int n = 0; n < numAttribs; n += 2) {
|
||||
if (newAttribs[n] == EGL_SURFACE_TYPE) {
|
||||
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;
|
||||
}
|
||||
|
||||
EGLint* newAttribs = NULL;
|
||||
if (!s_egl.eglChooseConfig(mDisplay, newAttribs ? newAttribs : attribs,
|
||||
matchedConfigs, numHostConfigs, &numHostConfigs)) {
|
||||
numHostConfigs = 0;
|
||||
}
|
||||
|
||||
if (mustReplaceSurfaceType) {
|
||||
// There is at least on EGL_SURFACE_TYPE in |attribs|. Copy the
|
||||
// array and replace all values with EGL_PBUFFER_BIT
|
||||
newAttribs = new GLint[numAttribs + 1];
|
||||
memcpy(newAttribs, attribs, numAttribs * sizeof(GLint));
|
||||
newAttribs[numAttribs] = EGL_NONE;
|
||||
for (int n = 0; n < numAttribs; n += 2) {
|
||||
if (newAttribs[n] == EGL_SURFACE_TYPE) {
|
||||
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;
|
||||
delete[] newAttribs;
|
||||
|
||||
int result = 0;
|
||||
for (int n = 0; n < numHostConfigs; ++n) {
|
||||
// Don't count or write more than |configsSize| items if |configs|
|
||||
// is not NULL.
|
||||
if (configs && configsSize > 0 && result >= configsSize) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!s_egl.eglChooseConfig(mDisplay,
|
||||
newAttribs ? newAttribs : attribs,
|
||||
matchedConfigs,
|
||||
numHostConfigs,
|
||||
&numHostConfigs)) {
|
||||
numHostConfigs = 0;
|
||||
// Skip incompatible host configs.
|
||||
if (!isCompatibleHostConfig(matchedConfigs[n], mDisplay)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
delete [] newAttribs;
|
||||
|
||||
int result = 0;
|
||||
for (int n = 0; n < numHostConfigs; ++n) {
|
||||
// Don't count or write more than |configsSize| items if |configs|
|
||||
// is not NULL.
|
||||
if (configs && configsSize > 0 && result >= configsSize) {
|
||||
break;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete [] matchedConfigs;
|
||||
delete[] matchedConfigs;
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void RendererConfigList::getPackInfo(EGLint* numConfigs,
|
||||
EGLint* numAttributes) const {
|
||||
if (numConfigs) {
|
||||
*numConfigs = mCount;
|
||||
}
|
||||
if (numAttributes) {
|
||||
*numAttributes = static_cast<EGLint>(kConfigAttributesLen);
|
||||
}
|
||||
EGLint* numAttributes) const {
|
||||
if (numConfigs) {
|
||||
*numConfigs = mCount;
|
||||
}
|
||||
if (numAttributes) {
|
||||
*numAttributes = static_cast<EGLint>(kConfigAttributesLen);
|
||||
}
|
||||
}
|
||||
|
||||
EGLint RendererConfigList::packConfigs(GLuint bufferByteSize, GLuint* buffer) const {
|
||||
GLuint numAttribs = static_cast<GLuint>(kConfigAttributesLen);
|
||||
GLuint kGLuintSize = static_cast<GLuint>(sizeof(GLuint));
|
||||
GLuint neededByteSize = (mCount + 1) * numAttribs * kGLuintSize;
|
||||
if (!buffer || bufferByteSize < neededByteSize) {
|
||||
return -neededByteSize;
|
||||
}
|
||||
// Write to the buffer the config attribute ids, followed for each one
|
||||
// of the configs, their values.
|
||||
memcpy(buffer, kConfigAttributes, kConfigAttributesLen * kGLuintSize);
|
||||
EGLint RendererConfigList::packConfigs(GLuint bufferByteSize,
|
||||
GLuint* buffer) const {
|
||||
GLuint numAttribs = static_cast<GLuint>(kConfigAttributesLen);
|
||||
GLuint kGLuintSize = static_cast<GLuint>(sizeof(GLuint));
|
||||
GLuint neededByteSize = (mCount + 1) * numAttribs * kGLuintSize;
|
||||
if (!buffer || bufferByteSize < neededByteSize) {
|
||||
return -neededByteSize;
|
||||
}
|
||||
// Write to the buffer the config attribute ids, followed for each one
|
||||
// of the configs, their values.
|
||||
memcpy(buffer, kConfigAttributes, kConfigAttributesLen * kGLuintSize);
|
||||
|
||||
for (int i = 0; i < mCount; ++i) {
|
||||
memcpy(buffer + (i + 1) * kConfigAttributesLen,
|
||||
mConfigs[i]->mAttribValues,
|
||||
kConfigAttributesLen * kGLuintSize);
|
||||
}
|
||||
return mCount;
|
||||
for (int i = 0; i < mCount; ++i) {
|
||||
memcpy(buffer + (i + 1) * kConfigAttributesLen, mConfigs[i]->mAttribValues,
|
||||
kConfigAttributesLen * kGLuintSize);
|
||||
}
|
||||
return mCount;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,43 +33,43 @@
|
|||
// an FbConfigList from the host EGLDisplay, and use its size() and get()
|
||||
// methods to access it.
|
||||
class RendererConfig {
|
||||
public:
|
||||
// Destructor
|
||||
~RendererConfig();
|
||||
public:
|
||||
// Destructor
|
||||
~RendererConfig();
|
||||
|
||||
// Retrieve host EGLConfig.
|
||||
EGLConfig getEglConfig() const { return mEglConfig; }
|
||||
// Retrieve host EGLConfig.
|
||||
EGLConfig getEglConfig() const { return mEglConfig; }
|
||||
|
||||
// Get depth size in bits.
|
||||
GLuint getDepthSize() const { return getAttribValue(0); }
|
||||
// Get depth size in bits.
|
||||
GLuint getDepthSize() const { return getAttribValue(0); }
|
||||
|
||||
// Get stencil size in bits.
|
||||
GLuint getStencilSize() const { return getAttribValue(1); }
|
||||
// Get stencil size in bits.
|
||||
GLuint getStencilSize() const { return getAttribValue(1); }
|
||||
|
||||
// Get renderable type mask.
|
||||
GLuint getRenderableType() const { return getAttribValue(2); }
|
||||
// Get renderable type mask.
|
||||
GLuint getRenderableType() const { return getAttribValue(2); }
|
||||
|
||||
// Get surface type mask.
|
||||
GLuint getSurfaceType() const { return getAttribValue(3); }
|
||||
// Get surface type mask.
|
||||
GLuint getSurfaceType() const { return getAttribValue(3); }
|
||||
|
||||
// Get the EGL_CONFIG_ID value. This is the same as the one of the
|
||||
// underlying host EGLConfig handle.
|
||||
GLint getConfigId() const { return (GLint)getAttribValue(4); }
|
||||
// Get the EGL_CONFIG_ID value. This is the same as the one of the
|
||||
// underlying host EGLConfig handle.
|
||||
GLint getConfigId() const { return (GLint)getAttribValue(4); }
|
||||
|
||||
private:
|
||||
RendererConfig();
|
||||
RendererConfig(RendererConfig& other);
|
||||
private:
|
||||
RendererConfig();
|
||||
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 {
|
||||
return mAttribValues ? mAttribValues[n] : 0U;
|
||||
}
|
||||
GLuint getAttribValue(int n) const {
|
||||
return mAttribValues ? mAttribValues[n] : 0U;
|
||||
}
|
||||
|
||||
EGLConfig mEglConfig;
|
||||
GLint* mAttribValues;
|
||||
EGLConfig mEglConfig;
|
||||
GLint* mAttribValues;
|
||||
};
|
||||
|
||||
// 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
|
||||
// available configs to the guest.
|
||||
class RendererConfigList {
|
||||
public:
|
||||
// Create a new list of FbConfig instance, by querying all compatible
|
||||
// host configs from |display|. A compatible config is one that supports
|
||||
// Pbuffers and RGB pixel values.
|
||||
//
|
||||
// After construction, call empty() to check if there are items.
|
||||
// An empty list means there was an error during construction.
|
||||
explicit RendererConfigList(EGLDisplay display);
|
||||
public:
|
||||
// Create a new list of FbConfig instance, by querying all compatible
|
||||
// host configs from |display|. A compatible config is one that supports
|
||||
// Pbuffers and RGB pixel values.
|
||||
//
|
||||
// After construction, call empty() to check if there are items.
|
||||
// An empty list means there was an error during construction.
|
||||
explicit RendererConfigList(EGLDisplay display);
|
||||
|
||||
// Destructor.
|
||||
~RendererConfigList();
|
||||
// Destructor.
|
||||
~RendererConfigList();
|
||||
|
||||
// Return true iff the list is empty. true means there was an error
|
||||
// during construction.
|
||||
bool empty() const { return mCount == 0; }
|
||||
// Return true iff the list is empty. true means there was an error
|
||||
// during construction.
|
||||
bool empty() const { return mCount == 0; }
|
||||
|
||||
// Return the number of FbConfig instances in the list.
|
||||
// Each instance is identified by a number from 0 to N-1,
|
||||
// where N is the result of this function.
|
||||
size_t size() const { return static_cast<size_t>(mCount); }
|
||||
// Return the number of FbConfig instances in the list.
|
||||
// Each instance is identified by a number from 0 to N-1,
|
||||
// where N is the result of this function.
|
||||
size_t size() const { return static_cast<size_t>(mCount); }
|
||||
|
||||
// Retrieve the FbConfig instance associated with |guestId|,
|
||||
// which must be an integer between 0 and |size() - 1|. Returns
|
||||
// NULL in case of failure.
|
||||
const RendererConfig* get(int guestId) const {
|
||||
if (guestId >= 0 && guestId < mCount) {
|
||||
return mConfigs[guestId];
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
// Retrieve the FbConfig instance associated with |guestId|,
|
||||
// which must be an integer between 0 and |size() - 1|. Returns
|
||||
// NULL in case of failure.
|
||||
const RendererConfig* get(int guestId) const {
|
||||
if (guestId >= 0 && guestId < mCount) {
|
||||
return mConfigs[guestId];
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Use |attribs| a list of EGL attribute name/values terminated by
|
||||
// EGL_NONE, to select a set of matching FbConfig instances.
|
||||
//
|
||||
// On success, returns the number of matching instances.
|
||||
// If |configs| is not NULL, it will be populated with the guest IDs
|
||||
// of the matched FbConfig instances.
|
||||
//
|
||||
// |configsSize| is the number of entries in the |configs| array. The
|
||||
// function will never write more than |configsSize| entries into
|
||||
// |configsSize|.
|
||||
EGLint chooseConfig(const EGLint* attribs,
|
||||
EGLint* configs,
|
||||
EGLint configsSize) const;
|
||||
// Use |attribs| a list of EGL attribute name/values terminated by
|
||||
// EGL_NONE, to select a set of matching FbConfig instances.
|
||||
//
|
||||
// On success, returns the number of matching instances.
|
||||
// If |configs| is not NULL, it will be populated with the guest IDs
|
||||
// of the matched FbConfig instances.
|
||||
//
|
||||
// |configsSize| is the number of entries in the |configs| array. The
|
||||
// function will never write more than |configsSize| entries into
|
||||
// |configsSize|.
|
||||
EGLint chooseConfig(const EGLint* attribs, EGLint* configs,
|
||||
EGLint configsSize) const;
|
||||
|
||||
// Retrieve information that can be sent to the guest before packed
|
||||
// config list information. If |numConfigs| is NULL, then |*numConfigs|
|
||||
// will be set on return to the number of config instances.
|
||||
// If |numAttribs| is not NULL, then |*numAttribs| will be set on return
|
||||
// to the number of attribute values cached by each FbConfig instance.
|
||||
void getPackInfo(EGLint* mumConfigs, EGLint* numAttribs) const;
|
||||
// Retrieve information that can be sent to the guest before packed
|
||||
// config list information. If |numConfigs| is NULL, then |*numConfigs|
|
||||
// will be set on return to the number of config instances.
|
||||
// If |numAttribs| is not NULL, then |*numAttribs| will be set on return
|
||||
// to the number of attribute values cached by each FbConfig instance.
|
||||
void getPackInfo(EGLint* mumConfigs, EGLint* numAttribs) const;
|
||||
|
||||
// Write the full list information into an array of EGLuint items.
|
||||
// |buffer| is the output buffer that will receive the data.
|
||||
// |bufferByteSize| is teh buffer size in bytes.
|
||||
// On success, this returns
|
||||
EGLint packConfigs(GLuint bufferByteSize, GLuint* buffer) const;
|
||||
// Write the full list information into an array of EGLuint items.
|
||||
// |buffer| is the output buffer that will receive the data.
|
||||
// |bufferByteSize| is teh buffer size in bytes.
|
||||
// On success, this returns
|
||||
EGLint packConfigs(GLuint bufferByteSize, GLuint* buffer) const;
|
||||
|
||||
private:
|
||||
RendererConfigList();
|
||||
RendererConfigList(const RendererConfigList& other);
|
||||
private:
|
||||
RendererConfigList();
|
||||
RendererConfigList(const RendererConfigList& other);
|
||||
|
||||
int mCount;
|
||||
RendererConfig** mConfigs;
|
||||
EGLDisplay mDisplay;
|
||||
int mCount;
|
||||
RendererConfig** mConfigs;
|
||||
EGLDisplay mDisplay;
|
||||
};
|
||||
|
||||
#endif // _LIBRENDER_FB_CONFIG_H
|
||||
|
|
|
|||
|
|
@ -15,152 +15,130 @@
|
|||
*/
|
||||
#include "SocketStream.h"
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
SocketStream::SocketStream(size_t bufSize) :
|
||||
IOStream(bufSize),
|
||||
m_sock(-1),
|
||||
m_bufsize(bufSize),
|
||||
m_buf(NULL)
|
||||
{
|
||||
SocketStream::SocketStream(size_t bufSize)
|
||||
: IOStream(bufSize), m_sock(-1), m_bufsize(bufSize), m_buf(NULL) {}
|
||||
|
||||
SocketStream::SocketStream(int sock, size_t bufSize)
|
||||
: 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) :
|
||||
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;
|
||||
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;
|
||||
}
|
||||
if (m_buf != NULL) {
|
||||
free(m_buf);
|
||||
m_buf = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
return m_buf;
|
||||
};
|
||||
|
||||
int SocketStream::commitBuffer(size_t size)
|
||||
{
|
||||
return writeFully(m_buf, size);
|
||||
}
|
||||
int SocketStream::commitBuffer(size_t size) { return writeFully(m_buf, size); }
|
||||
|
||||
int SocketStream::writeFully(const void* buffer, size_t size)
|
||||
{
|
||||
if (!valid()) return -1;
|
||||
int SocketStream::writeFully(const void *buffer, size_t size) {
|
||||
if (!valid()) return -1;
|
||||
|
||||
size_t res = size;
|
||||
int retval = 0;
|
||||
size_t res = size;
|
||||
int retval = 0;
|
||||
|
||||
while (res > 0) {
|
||||
ssize_t stat = ::send(m_sock, (const char *)buffer + (size - res), res, 0);
|
||||
if (stat < 0) {
|
||||
if (errno != EINTR) {
|
||||
retval = stat;
|
||||
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;
|
||||
}
|
||||
}
|
||||
while (res > 0) {
|
||||
ssize_t stat = ::send(m_sock, (const char *)buffer + (size - res), res, 0);
|
||||
if (stat < 0) {
|
||||
if (errno != EINTR) {
|
||||
retval = stat;
|
||||
ERR("%s: failed: %s\n", __FUNCTION__, strerror(errno));
|
||||
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() {
|
||||
// Shutdown socket to force read/write errors.
|
||||
::shutdown(m_sock, SHUT_RDWR);
|
||||
// Shutdown socket to force read/write errors.
|
||||
::shutdown(m_sock, SHUT_RDWR);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,34 +20,34 @@
|
|||
#include "IOStream.h"
|
||||
|
||||
class SocketStream : public IOStream {
|
||||
public:
|
||||
typedef enum { ERR_INVALID_SOCKET = -1000 } SocketStreamError;
|
||||
static const size_t MAX_ADDRSTR_LEN = 256;
|
||||
public:
|
||||
typedef enum { ERR_INVALID_SOCKET = -1000 } SocketStreamError;
|
||||
static const size_t MAX_ADDRSTR_LEN = 256;
|
||||
|
||||
explicit SocketStream(size_t bufsize = 10000);
|
||||
virtual ~SocketStream();
|
||||
explicit SocketStream(size_t bufsize = 10000);
|
||||
virtual ~SocketStream();
|
||||
|
||||
virtual int listen(char addrstr[MAX_ADDRSTR_LEN]) = 0;
|
||||
virtual SocketStream *accept() = 0;
|
||||
virtual int connect(const char* addr) = 0;
|
||||
virtual int listen(char addrstr[MAX_ADDRSTR_LEN]) = 0;
|
||||
virtual SocketStream *accept() = 0;
|
||||
virtual int connect(const char *addr) = 0;
|
||||
|
||||
virtual void *allocBuffer(size_t minSize);
|
||||
virtual int commitBuffer(size_t size);
|
||||
virtual const unsigned char *readFully(void *buf, size_t len);
|
||||
virtual const unsigned char *read(void *buf, size_t *inout_len);
|
||||
virtual void *allocBuffer(size_t minSize);
|
||||
virtual int commitBuffer(size_t size);
|
||||
virtual const unsigned char *readFully(void *buf, size_t len);
|
||||
virtual const unsigned char *read(void *buf, size_t *inout_len);
|
||||
|
||||
bool valid() { return m_sock >= 0; }
|
||||
virtual int recv(void *buf, size_t len);
|
||||
virtual int writeFully(const void *buf, size_t len);
|
||||
bool valid() { return m_sock >= 0; }
|
||||
virtual int recv(void *buf, size_t len);
|
||||
virtual int writeFully(const void *buf, size_t len);
|
||||
|
||||
virtual void forceStop();
|
||||
virtual void forceStop();
|
||||
|
||||
protected:
|
||||
int m_sock;
|
||||
size_t m_bufsize;
|
||||
unsigned char *m_buf;
|
||||
protected:
|
||||
int m_sock;
|
||||
size_t m_bufsize;
|
||||
unsigned char *m_buf;
|
||||
|
||||
SocketStream(int sock, size_t bufSize);
|
||||
SocketStream(int sock, size_t bufSize);
|
||||
};
|
||||
|
||||
#endif /* __SOCKET_STREAM_H */
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@
|
|||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
|
|
@ -29,46 +29,42 @@
|
|||
|
||||
TcpStream::TcpStream(size_t bufSize) : SocketStream(bufSize) {}
|
||||
|
||||
TcpStream::TcpStream(int sock, size_t bufSize) :
|
||||
SocketStream(sock, bufSize) {
|
||||
// disable Nagle algorithm to improve bandwidth of small
|
||||
// packets which are quite common in our implementation.
|
||||
emugl::socketTcpDisableNagle(sock);
|
||||
TcpStream::TcpStream(int sock, size_t bufSize) : SocketStream(sock, bufSize) {
|
||||
// disable Nagle algorithm to improve bandwidth of small
|
||||
// packets which are quite common in our implementation.
|
||||
emugl::socketTcpDisableNagle(sock);
|
||||
}
|
||||
|
||||
int TcpStream::listen(char addrstr[MAX_ADDRSTR_LEN]) {
|
||||
m_sock = emugl::socketTcpLoopbackServer(0, SOCK_STREAM);
|
||||
if (!valid())
|
||||
return int(ERR_INVALID_SOCKET);
|
||||
m_sock = emugl::socketTcpLoopbackServer(0, SOCK_STREAM);
|
||||
if (!valid()) return int(ERR_INVALID_SOCKET);
|
||||
|
||||
int port = emugl::socketGetPort(m_sock);
|
||||
if (port < 0) {
|
||||
::close(m_sock);
|
||||
return int(ERR_INVALID_SOCKET);
|
||||
}
|
||||
int port = emugl::socketGetPort(m_sock);
|
||||
if (port < 0) {
|
||||
::close(m_sock);
|
||||
return int(ERR_INVALID_SOCKET);
|
||||
}
|
||||
|
||||
snprintf(addrstr, MAX_ADDRSTR_LEN - 1, "%hu", port);
|
||||
addrstr[MAX_ADDRSTR_LEN-1] = '\0';
|
||||
snprintf(addrstr, MAX_ADDRSTR_LEN - 1, "%hu", port);
|
||||
addrstr[MAX_ADDRSTR_LEN - 1] = '\0';
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SocketStream * TcpStream::accept() {
|
||||
int clientSock = emugl::socketAccept(m_sock);
|
||||
if (clientSock < 0)
|
||||
return NULL;
|
||||
SocketStream* TcpStream::accept() {
|
||||
int clientSock = emugl::socketAccept(m_sock);
|
||||
if (clientSock < 0) return NULL;
|
||||
|
||||
return new TcpStream(clientSock, m_bufsize);
|
||||
return new TcpStream(clientSock, m_bufsize);
|
||||
}
|
||||
|
||||
int TcpStream::connect(const char* addr) {
|
||||
int port = atoi(addr);
|
||||
m_sock = emugl::socketTcpLoopbackClient(port, SOCK_STREAM);
|
||||
return valid() ? 0 : -1;
|
||||
int port = atoi(addr);
|
||||
m_sock = emugl::socketTcpLoopbackClient(port, SOCK_STREAM);
|
||||
return valid() ? 0 : -1;
|
||||
}
|
||||
|
||||
int TcpStream::connect(const char* hostname, unsigned short port)
|
||||
{
|
||||
m_sock = emugl::socketTcpClient(hostname, port, SOCK_STREAM);
|
||||
return valid() ? 0 : -1;
|
||||
int TcpStream::connect(const char* hostname, unsigned short port) {
|
||||
m_sock = emugl::socketTcpClient(hostname, port, SOCK_STREAM);
|
||||
return valid() ? 0 : -1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,14 +19,15 @@
|
|||
#include "SocketStream.h"
|
||||
|
||||
class TcpStream : public SocketStream {
|
||||
public:
|
||||
explicit TcpStream(size_t bufsize = 10000);
|
||||
virtual int listen(char addrstr[MAX_ADDRSTR_LEN]);
|
||||
virtual SocketStream *accept();
|
||||
virtual int connect(const char* addr);
|
||||
int connect(const char* hostname, unsigned short port);
|
||||
private:
|
||||
TcpStream(int sock, size_t bufSize);
|
||||
public:
|
||||
explicit TcpStream(size_t bufsize = 10000);
|
||||
virtual int listen(char addrstr[MAX_ADDRSTR_LEN]);
|
||||
virtual SocketStream* accept();
|
||||
virtual int connect(const char* addr);
|
||||
int connect(const char* hostname, unsigned short port);
|
||||
|
||||
private:
|
||||
TcpStream(int sock, size_t bufSize);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
#include <vector>
|
||||
|
||||
#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)
|
||||
#ifndef M_PI
|
||||
|
|
@ -35,25 +35,25 @@ namespace {
|
|||
// |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.
|
||||
GLuint createShader(GLint shaderType, const char* shaderText) {
|
||||
// Create new shader handle and attach source.
|
||||
GLuint shader = s_gles2.glCreateShader(shaderType);
|
||||
if (!shader) {
|
||||
return 0;
|
||||
}
|
||||
const GLchar* text = static_cast<const GLchar*>(shaderText);
|
||||
const GLint textLen = ::strlen(shaderText);
|
||||
s_gles2.glShaderSource(shader, 1, &text, &textLen);
|
||||
// Create new shader handle and attach source.
|
||||
GLuint shader = s_gles2.glCreateShader(shaderType);
|
||||
if (!shader) {
|
||||
return 0;
|
||||
}
|
||||
const GLchar* text = static_cast<const GLchar*>(shaderText);
|
||||
const GLint textLen = ::strlen(shaderText);
|
||||
s_gles2.glShaderSource(shader, 1, &text, &textLen);
|
||||
|
||||
// Compiler the shader.
|
||||
GLint success;
|
||||
s_gles2.glCompileShader(shader);
|
||||
s_gles2.glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||
if (success == GL_FALSE) {
|
||||
s_gles2.glDeleteShader(shader);
|
||||
return 0;
|
||||
}
|
||||
// Compiler the shader.
|
||||
GLint success;
|
||||
s_gles2.glCompileShader(shader);
|
||||
s_gles2.glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||
if (success == GL_FALSE) {
|
||||
s_gles2.glDeleteShader(shader);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return shader;
|
||||
return shader;
|
||||
}
|
||||
|
||||
// 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.
|
||||
struct Vertex {
|
||||
float pos[3];
|
||||
float coord[2];
|
||||
float pos[3];
|
||||
float coord[2];
|
||||
};
|
||||
|
||||
const Vertex kVertices[] = {
|
||||
{{ +1, -1, +0 }, { +1, +1 }},
|
||||
{{ +1, +1, +0 }, { +1, +0 }},
|
||||
{{ -1, +1, +0 }, { +0, +0 }},
|
||||
{{ -1, -1, +0 }, { +0, +1 }},
|
||||
{{+1, -1, +0}, {+1, +1}},
|
||||
{{+1, +1, +0}, {+1, +0}},
|
||||
{{-1, +1, +0}, {+0, +0}},
|
||||
{{-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]);
|
||||
|
||||
} // namespace
|
||||
|
||||
TextureDraw::TextureDraw(EGLDisplay display) :
|
||||
mDisplay(display),
|
||||
mVertexShader(0),
|
||||
mFragmentShader(0),
|
||||
mProgram(0),
|
||||
mPositionSlot(-1),
|
||||
mInCoordSlot(-1),
|
||||
mTextureSlot(-1),
|
||||
mRotationSlot(-1),
|
||||
mTranslationSlot(-1) {
|
||||
// Create shaders and program.
|
||||
mVertexShader = createShader(GL_VERTEX_SHADER, kVertexShaderSource);
|
||||
mFragmentShader = createShader(GL_FRAGMENT_SHADER, kFragmentShaderSource);
|
||||
TextureDraw::TextureDraw(EGLDisplay display)
|
||||
: mDisplay(display),
|
||||
mVertexShader(0),
|
||||
mFragmentShader(0),
|
||||
mProgram(0),
|
||||
mPositionSlot(-1),
|
||||
mInCoordSlot(-1),
|
||||
mTextureSlot(-1),
|
||||
mRotationSlot(-1),
|
||||
mTranslationSlot(-1) {
|
||||
// Create shaders and program.
|
||||
mVertexShader = createShader(GL_VERTEX_SHADER, kVertexShaderSource);
|
||||
mFragmentShader = createShader(GL_FRAGMENT_SHADER, kFragmentShaderSource);
|
||||
|
||||
mProgram = s_gles2.glCreateProgram();
|
||||
s_gles2.glAttachShader(mProgram, mVertexShader);
|
||||
s_gles2.glAttachShader(mProgram, mFragmentShader);
|
||||
mProgram = s_gles2.glCreateProgram();
|
||||
s_gles2.glAttachShader(mProgram, mVertexShader);
|
||||
s_gles2.glAttachShader(mProgram, mFragmentShader);
|
||||
|
||||
GLint success;
|
||||
s_gles2.glLinkProgram(mProgram);
|
||||
s_gles2.glGetProgramiv(mProgram, GL_LINK_STATUS, &success);
|
||||
if (success == GL_FALSE) {
|
||||
GLchar messages[256];
|
||||
s_gles2.glGetProgramInfoLog(
|
||||
mProgram, sizeof(messages), 0, &messages[0]);
|
||||
ERR("%s: Could not create/link program: %s\n", __FUNCTION__, messages);
|
||||
s_gles2.glDeleteProgram(mProgram);
|
||||
mProgram = 0;
|
||||
return;
|
||||
}
|
||||
GLint success;
|
||||
s_gles2.glLinkProgram(mProgram);
|
||||
s_gles2.glGetProgramiv(mProgram, GL_LINK_STATUS, &success);
|
||||
if (success == GL_FALSE) {
|
||||
GLchar messages[256];
|
||||
s_gles2.glGetProgramInfoLog(mProgram, sizeof(messages), 0, &messages[0]);
|
||||
ERR("%s: Could not create/link program: %s\n", __FUNCTION__, messages);
|
||||
s_gles2.glDeleteProgram(mProgram);
|
||||
mProgram = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
s_gles2.glUseProgram(mProgram);
|
||||
s_gles2.glUseProgram(mProgram);
|
||||
|
||||
// Retrieve attribute/uniform locations.
|
||||
mPositionSlot = s_gles2.glGetAttribLocation(mProgram, "position");
|
||||
s_gles2.glEnableVertexAttribArray(mPositionSlot);
|
||||
// Retrieve attribute/uniform locations.
|
||||
mPositionSlot = s_gles2.glGetAttribLocation(mProgram, "position");
|
||||
s_gles2.glEnableVertexAttribArray(mPositionSlot);
|
||||
|
||||
mInCoordSlot = s_gles2.glGetAttribLocation(mProgram, "inCoord");
|
||||
s_gles2.glEnableVertexAttribArray(mInCoordSlot);
|
||||
mInCoordSlot = s_gles2.glGetAttribLocation(mProgram, "inCoord");
|
||||
s_gles2.glEnableVertexAttribArray(mInCoordSlot);
|
||||
|
||||
mTextureSlot = s_gles2.glGetUniformLocation(mProgram, "texture");
|
||||
mTextureSlot = s_gles2.glGetUniformLocation(mProgram, "texture");
|
||||
|
||||
// Create vertex and index buffers.
|
||||
s_gles2.glGenBuffers(1, &mVertexBuffer);
|
||||
s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
|
||||
s_gles2.glBufferData(
|
||||
GL_ARRAY_BUFFER, sizeof(kVertices), kVertices, GL_STATIC_DRAW);
|
||||
// Create vertex and index buffers.
|
||||
s_gles2.glGenBuffers(1, &mVertexBuffer);
|
||||
s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
|
||||
s_gles2.glBufferData(GL_ARRAY_BUFFER, sizeof(kVertices), kVertices,
|
||||
GL_STATIC_DRAW);
|
||||
|
||||
s_gles2.glGenBuffers(1, &mIndexBuffer);
|
||||
s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
|
||||
s_gles2.glBufferData(GL_ELEMENT_ARRAY_BUFFER,
|
||||
sizeof(kIndices),
|
||||
kIndices,
|
||||
GL_STATIC_DRAW);
|
||||
s_gles2.glGenBuffers(1, &mIndexBuffer);
|
||||
s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
|
||||
s_gles2.glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(kIndices), kIndices,
|
||||
GL_STATIC_DRAW);
|
||||
}
|
||||
|
||||
bool TextureDraw::draw(GLuint texture) {
|
||||
if (!mProgram) {
|
||||
ERR("%s: no program\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
if (!mProgram) {
|
||||
ERR("%s: no program\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(digit): Save previous program state.
|
||||
// TODO(digit): Save previous program state.
|
||||
|
||||
GLenum err;
|
||||
GLenum err;
|
||||
|
||||
s_gles2.glUseProgram(mProgram);
|
||||
err = s_gles2.glGetError();
|
||||
if (err != GL_NO_ERROR) {
|
||||
ERR("%s: Could not use program error=0x%x\n",
|
||||
__FUNCTION__, err);
|
||||
}
|
||||
s_gles2.glUseProgram(mProgram);
|
||||
err = s_gles2.glGetError();
|
||||
if (err != GL_NO_ERROR) {
|
||||
ERR("%s: Could not use program error=0x%x\n", __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.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);
|
||||
}
|
||||
s_gles2.glEnableVertexAttribArray(mPositionSlot);
|
||||
s_gles2.glVertexAttribPointer(mPositionSlot, 3, GL_FLOAT, GL_FALSE,
|
||||
sizeof(Vertex), 0);
|
||||
err = s_gles2.glGetError();
|
||||
if (err != GL_NO_ERROR) {
|
||||
ERR("%s: Could glVertexAttribPointer with mPositionSlot error=0x%x\n",
|
||||
__FUNCTION__, err);
|
||||
}
|
||||
|
||||
s_gles2.glEnableVertexAttribArray(mPositionSlot);
|
||||
s_gles2.glVertexAttribPointer(mPositionSlot,
|
||||
3,
|
||||
GL_FLOAT,
|
||||
GL_FALSE,
|
||||
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.
|
||||
s_gles2.glEnableVertexAttribArray(mInCoordSlot);
|
||||
s_gles2.glVertexAttribPointer(
|
||||
mInCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||||
reinterpret_cast<GLvoid*>(static_cast<uintptr_t>(sizeof(float) * 3)));
|
||||
|
||||
// Setup the |inCoord| attribute values.
|
||||
s_gles2.glEnableVertexAttribArray(mInCoordSlot);
|
||||
s_gles2.glVertexAttribPointer(mInCoordSlot,
|
||||
2,
|
||||
GL_FLOAT,
|
||||
GL_FALSE,
|
||||
sizeof(Vertex),
|
||||
reinterpret_cast<GLvoid*>(
|
||||
static_cast<uintptr_t>(
|
||||
sizeof(float) * 3)));
|
||||
// setup the |texture| uniform value.
|
||||
s_gles2.glActiveTexture(GL_TEXTURE0);
|
||||
s_gles2.glBindTexture(GL_TEXTURE_2D, texture);
|
||||
s_gles2.glUniform1i(mTextureSlot, 0);
|
||||
|
||||
// setup the |texture| uniform value.
|
||||
s_gles2.glActiveTexture(GL_TEXTURE0);
|
||||
s_gles2.glBindTexture(GL_TEXTURE_2D, texture);
|
||||
s_gles2.glUniform1i(mTextureSlot, 0);
|
||||
// Validate program, just to be sure.
|
||||
s_gles2.glValidateProgram(mProgram);
|
||||
GLint validState = 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.
|
||||
s_gles2.glValidateProgram(mProgram);
|
||||
GLint validState = 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;
|
||||
}
|
||||
// Do the rendering.
|
||||
s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
|
||||
err = s_gles2.glGetError();
|
||||
if (err != GL_NO_ERROR) {
|
||||
ERR("%s: Could not glBindBuffer(GL_ELEMENT_ARRAY_BUFFER) error=0x%x\n",
|
||||
__FUNCTION__, err);
|
||||
}
|
||||
|
||||
// Do the rendering.
|
||||
s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
|
||||
err = s_gles2.glGetError();
|
||||
if (err != GL_NO_ERROR) {
|
||||
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);
|
||||
err = s_gles2.glGetError();
|
||||
if (err != GL_NO_ERROR) {
|
||||
ERR("%s: Could not glDrawElements() error=0x%x\n", __FUNCTION__, err);
|
||||
}
|
||||
|
||||
s_gles2.glDrawElements(GL_TRIANGLES, kIndicesLen, GL_UNSIGNED_BYTE, 0);
|
||||
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.
|
||||
|
||||
// TODO(digit): Restore previous program state.
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
TextureDraw::~TextureDraw() {
|
||||
s_gles2.glDeleteBuffers(1, &mIndexBuffer);
|
||||
s_gles2.glDeleteBuffers(1, &mVertexBuffer);
|
||||
s_gles2.glDeleteBuffers(1, &mIndexBuffer);
|
||||
s_gles2.glDeleteBuffers(1, &mVertexBuffer);
|
||||
|
||||
if (mFragmentShader) {
|
||||
s_gles2.glDeleteShader(mFragmentShader);
|
||||
}
|
||||
if (mVertexShader) {
|
||||
s_gles2.glDeleteShader(mVertexShader);
|
||||
}
|
||||
if (mFragmentShader) {
|
||||
s_gles2.glDeleteShader(mFragmentShader);
|
||||
}
|
||||
if (mVertexShader) {
|
||||
s_gles2.glDeleteShader(mVertexShader);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,29 +30,29 @@
|
|||
// framebuffer with texture content.
|
||||
//
|
||||
class TextureDraw {
|
||||
public:
|
||||
// Create a new instance.
|
||||
TextureDraw(EGLDisplay display);
|
||||
public:
|
||||
// Create a new instance.
|
||||
TextureDraw(EGLDisplay display);
|
||||
|
||||
// Destructor
|
||||
~TextureDraw();
|
||||
// Destructor
|
||||
~TextureDraw();
|
||||
|
||||
// Fill the current framebuffer with the content of |texture|, which must
|
||||
// be the name of a GLES 2.x texture object.
|
||||
bool draw(GLuint texture);
|
||||
// Fill the current framebuffer with the content of |texture|, which must
|
||||
// be the name of a GLES 2.x texture object.
|
||||
bool draw(GLuint texture);
|
||||
|
||||
private:
|
||||
EGLDisplay mDisplay;
|
||||
GLuint mVertexShader;
|
||||
GLuint mFragmentShader;
|
||||
GLuint mProgram;
|
||||
GLint mPositionSlot;
|
||||
GLint mInCoordSlot;
|
||||
GLint mTextureSlot;
|
||||
GLint mRotationSlot;
|
||||
GLint mTranslationSlot;
|
||||
GLuint mVertexBuffer;
|
||||
GLuint mIndexBuffer;
|
||||
private:
|
||||
EGLDisplay mDisplay;
|
||||
GLuint mVertexShader;
|
||||
GLuint mFragmentShader;
|
||||
GLuint mProgram;
|
||||
GLint mPositionSlot;
|
||||
GLint mInCoordSlot;
|
||||
GLint mTextureSlot;
|
||||
GLint mRotationSlot;
|
||||
GLint mTranslationSlot;
|
||||
GLuint mVertexBuffer;
|
||||
GLuint mIndexBuffer;
|
||||
};
|
||||
|
||||
#endif // TEXTURE_DRAW_H
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#define ERR(...) fprintf(stderr, __VA_ARGS__)
|
||||
#define ERR(...) fprintf(stderr, __VA_ARGS__)
|
||||
#define MAX_FACTOR_POWER 4
|
||||
|
||||
static const char kCommonShaderSource[] =
|
||||
|
|
@ -126,219 +126,237 @@ const char kFragmentShaderSource[] =
|
|||
static const float kVertexData[] = {-1, -1, 3, -1, -1, 3};
|
||||
|
||||
static void detachShaders(GLuint program) {
|
||||
GLuint shaders[2] = {};
|
||||
GLsizei count = 0;
|
||||
s_gles2.glGetAttachedShaders(program, 2, &count, shaders);
|
||||
if (s_gles2.glGetError() == GL_NO_ERROR) {
|
||||
for (GLsizei i = 0; i < count; i++) {
|
||||
s_gles2.glDetachShader(program, shaders[i]);
|
||||
s_gles2.glDeleteShader(shaders[i]);
|
||||
}
|
||||
GLuint shaders[2] = {};
|
||||
GLsizei count = 0;
|
||||
s_gles2.glGetAttachedShaders(program, 2, &count, shaders);
|
||||
if (s_gles2.glGetError() == GL_NO_ERROR) {
|
||||
for (GLsizei i = 0; i < count; i++) {
|
||||
s_gles2.glDetachShader(program, shaders[i]);
|
||||
s_gles2.glDeleteShader(shaders[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GLuint createShader(GLenum type, const std::initializer_list<const char*>& source) {
|
||||
GLint success, infoLength;
|
||||
static GLuint createShader(GLenum type,
|
||||
const std::initializer_list<const char*>& source) {
|
||||
GLint success, infoLength;
|
||||
|
||||
GLuint shader = s_gles2.glCreateShader(type);
|
||||
if (shader) {
|
||||
s_gles2.glShaderSource(shader, source.size(), source.begin(), nullptr);
|
||||
s_gles2.glCompileShader(shader);
|
||||
s_gles2.glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||
if (success == GL_FALSE) {
|
||||
s_gles2.glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLength);
|
||||
std::string infoLog(infoLength + 1, '\0');
|
||||
s_gles2.glGetShaderInfoLog(shader, infoLength, nullptr, &infoLog[0]);
|
||||
ERR("%s shader compile failed:\n%s\n",
|
||||
(type == GL_VERTEX_SHADER) ? "Vertex" : "Fragment",
|
||||
infoLog.c_str());
|
||||
s_gles2.glDeleteShader(shader);
|
||||
shader = 0;
|
||||
}
|
||||
GLuint shader = s_gles2.glCreateShader(type);
|
||||
if (shader) {
|
||||
s_gles2.glShaderSource(shader, source.size(), source.begin(), nullptr);
|
||||
s_gles2.glCompileShader(shader);
|
||||
s_gles2.glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||
if (success == GL_FALSE) {
|
||||
s_gles2.glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLength);
|
||||
std::string infoLog(infoLength + 1, '\0');
|
||||
s_gles2.glGetShaderInfoLog(shader, infoLength, nullptr, &infoLog[0]);
|
||||
ERR("%s shader compile failed:\n%s\n",
|
||||
(type == GL_VERTEX_SHADER) ? "Vertex" : "Fragment", infoLog.c_str());
|
||||
s_gles2.glDeleteShader(shader);
|
||||
shader = 0;
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
static void attachShaders(TextureResize::Framebuffer* fb, const char* factorDefine,
|
||||
const char* dimensionDefine, GLuint width, GLuint height) {
|
||||
static void attachShaders(TextureResize::Framebuffer* fb,
|
||||
const char* factorDefine, const char* dimensionDefine,
|
||||
GLuint width, GLuint height) {
|
||||
std::ostringstream dimensionConst;
|
||||
dimensionConst << "const vec2 kDimension = vec2(" << width << ", " << height
|
||||
<< ");\n";
|
||||
|
||||
std::ostringstream dimensionConst;
|
||||
dimensionConst << "const vec2 kDimension = vec2(" << width << ", " << height << ");\n";
|
||||
GLuint vShader = createShader(
|
||||
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, {
|
||||
factorDefine, dimensionDefine,
|
||||
kCommonShaderSource, dimensionConst.str().c_str(), kVertexShaderSource
|
||||
});
|
||||
GLuint fShader = createShader(GL_FRAGMENT_SHADER, {
|
||||
factorDefine, dimensionDefine, kCommonShaderSource, kFragmentShaderSource
|
||||
});
|
||||
if (!vShader || !fShader) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!vShader || !fShader) {
|
||||
return;
|
||||
}
|
||||
s_gles2.glAttachShader(fb->program, vShader);
|
||||
s_gles2.glAttachShader(fb->program, fShader);
|
||||
s_gles2.glLinkProgram(fb->program);
|
||||
|
||||
s_gles2.glAttachShader(fb->program, vShader);
|
||||
s_gles2.glAttachShader(fb->program, fShader);
|
||||
s_gles2.glLinkProgram(fb->program);
|
||||
|
||||
s_gles2.glUseProgram(fb->program);
|
||||
fb->aPosition = s_gles2.glGetAttribLocation(fb->program, "aPosition");
|
||||
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) :
|
||||
mWidth(width),
|
||||
mHeight(height),
|
||||
mFactor(1),
|
||||
mFBWidth({0,}),
|
||||
mFBHeight({0,}) {
|
||||
s_gles2.glGenTextures(1, &mFBWidth.texture);
|
||||
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);
|
||||
TextureResize::TextureResize(GLuint width, GLuint height)
|
||||
: mWidth(width),
|
||||
mHeight(height),
|
||||
mFactor(1),
|
||||
mFBWidth({
|
||||
0,
|
||||
}),
|
||||
mFBHeight({
|
||||
0,
|
||||
}) {
|
||||
s_gles2.glGenTextures(1, &mFBWidth.texture);
|
||||
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.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_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_T, GL_CLAMP_TO_EDGE);
|
||||
s_gles2.glGenTextures(1, &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_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_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
s_gles2.glGenFramebuffers(1, &mFBWidth.framebuffer);
|
||||
s_gles2.glGenFramebuffers(1, &mFBHeight.framebuffer);
|
||||
s_gles2.glGenFramebuffers(1, &mFBWidth.framebuffer);
|
||||
s_gles2.glGenFramebuffers(1, &mFBHeight.framebuffer);
|
||||
|
||||
mFBWidth.program = s_gles2.glCreateProgram();
|
||||
mFBHeight.program = s_gles2.glCreateProgram();
|
||||
mFBWidth.program = s_gles2.glCreateProgram();
|
||||
mFBHeight.program = s_gles2.glCreateProgram();
|
||||
|
||||
s_gles2.glGenBuffers(1, &mVertexBuffer);
|
||||
s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
|
||||
s_gles2.glBufferData(GL_ARRAY_BUFFER, sizeof(kVertexData), kVertexData, GL_STATIC_DRAW);
|
||||
s_gles2.glGenBuffers(1, &mVertexBuffer);
|
||||
s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
|
||||
s_gles2.glBufferData(GL_ARRAY_BUFFER, sizeof(kVertexData), kVertexData,
|
||||
GL_STATIC_DRAW);
|
||||
}
|
||||
|
||||
TextureResize::~TextureResize() {
|
||||
GLuint fb[2] = {mFBWidth.framebuffer, mFBHeight.framebuffer};
|
||||
s_gles2.glDeleteFramebuffers(2, fb);
|
||||
GLuint fb[2] = {mFBWidth.framebuffer, mFBHeight.framebuffer};
|
||||
s_gles2.glDeleteFramebuffers(2, fb);
|
||||
|
||||
GLuint tex[2] = {mFBWidth.texture, mFBHeight.texture};
|
||||
s_gles2.glDeleteTextures(2, tex);
|
||||
GLuint tex[2] = {mFBWidth.texture, mFBHeight.texture};
|
||||
s_gles2.glDeleteTextures(2, tex);
|
||||
|
||||
s_gles2.glDeleteProgram(mFBWidth.program);
|
||||
s_gles2.glDeleteProgram(mFBHeight.program);
|
||||
s_gles2.glDeleteProgram(mFBWidth.program);
|
||||
s_gles2.glDeleteProgram(mFBHeight.program);
|
||||
|
||||
s_gles2.glDeleteBuffers(1, &mVertexBuffer);
|
||||
s_gles2.glDeleteBuffers(1, &mVertexBuffer);
|
||||
}
|
||||
|
||||
GLuint TextureResize::update(GLuint texture) {
|
||||
// Store the viewport. The viewport is clobbered due to the framebuffers.
|
||||
GLint vport[4] = { 0, };
|
||||
s_gles2.glGetIntegerv(GL_VIEWPORT, vport);
|
||||
// Store the viewport. The viewport is clobbered due to the framebuffers.
|
||||
GLint vport[4] = {
|
||||
0,
|
||||
};
|
||||
s_gles2.glGetIntegerv(GL_VIEWPORT, vport);
|
||||
|
||||
// Correctly deal with rotated screens.
|
||||
GLint tWidth = vport[2], tHeight = vport[3];
|
||||
if ((mWidth < mHeight) != (tWidth < tHeight)) {
|
||||
std::swap(tWidth, tHeight);
|
||||
}
|
||||
// Correctly deal with rotated screens.
|
||||
GLint tWidth = vport[2], tHeight = vport[3];
|
||||
if ((mWidth < mHeight) != (tWidth < tHeight)) {
|
||||
std::swap(tWidth, tHeight);
|
||||
}
|
||||
|
||||
// Compute the scaling factor needed to get an image just larger than the target viewport.
|
||||
unsigned int factor = 1;
|
||||
for (int i = 0, w = mWidth / 2, h = mHeight / 2;
|
||||
i < MAX_FACTOR_POWER && w >= tWidth && h >= tHeight;
|
||||
i++, w /= 2, h /= 2, factor *= 2) {
|
||||
}
|
||||
// Compute the scaling factor needed to get an image just larger than the
|
||||
// target viewport.
|
||||
unsigned int factor = 1;
|
||||
for (int i = 0, w = mWidth / 2, h = mHeight / 2;
|
||||
i < MAX_FACTOR_POWER && w >= tWidth && h >= tHeight;
|
||||
i++, w /= 2, h /= 2, factor *= 2) {
|
||||
}
|
||||
|
||||
// No resizing needed.
|
||||
if (factor == 1) {
|
||||
return texture;
|
||||
}
|
||||
// No resizing needed.
|
||||
if (factor == 1) {
|
||||
return texture;
|
||||
}
|
||||
|
||||
s_gles2.glGetError(); // Clear any GL errors.
|
||||
setupFramebuffers(factor);
|
||||
resize(texture);
|
||||
s_gles2.glViewport(vport[0], vport[1], vport[2], vport[3]); // Restore the viewport.
|
||||
s_gles2.glGetError(); // Clear any GL errors.
|
||||
setupFramebuffers(factor);
|
||||
resize(texture);
|
||||
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.
|
||||
GLenum error = s_gles2.glGetError();
|
||||
if (error != GL_NO_ERROR) {
|
||||
ERR("GL error while resizing: 0x%x (ignored)\n", error);
|
||||
return texture;
|
||||
}
|
||||
// If there was an error while resizing, just use the unscaled texture.
|
||||
GLenum error = s_gles2.glGetError();
|
||||
if (error != GL_NO_ERROR) {
|
||||
ERR("GL error while resizing: 0x%x (ignored)\n", error);
|
||||
return texture;
|
||||
}
|
||||
|
||||
return mFBHeight.texture;
|
||||
return mFBHeight.texture;
|
||||
}
|
||||
|
||||
void TextureResize::setupFramebuffers(unsigned int factor) {
|
||||
if (factor == mFactor) {
|
||||
// The factor hasn't changed, no need to update the framebuffers.
|
||||
return;
|
||||
}
|
||||
if (factor == mFactor) {
|
||||
// The factor hasn't changed, no need to update the framebuffers.
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the framebuffer sizes to match the new factor.
|
||||
s_gles2.glBindTexture(GL_TEXTURE_2D, mFBWidth.texture);
|
||||
s_gles2.glTexImage2D(
|
||||
GL_TEXTURE_2D, 0, GL_RGBA, mWidth / factor, mHeight, 0, GL_RGBA, GL_FLOAT, nullptr);
|
||||
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBWidth.framebuffer);
|
||||
s_gles2.glFramebufferTexture2D(
|
||||
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mFBWidth.texture, 0);
|
||||
// Update the framebuffer sizes to match the new factor.
|
||||
s_gles2.glBindTexture(GL_TEXTURE_2D, mFBWidth.texture);
|
||||
s_gles2.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mWidth / factor, mHeight, 0,
|
||||
GL_RGBA, GL_FLOAT, nullptr);
|
||||
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBWidth.framebuffer);
|
||||
s_gles2.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D, mFBWidth.texture, 0);
|
||||
|
||||
s_gles2.glBindTexture(GL_TEXTURE_2D, mFBHeight.texture);
|
||||
s_gles2.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mWidth / factor, mHeight / factor, 0, GL_RGBA,
|
||||
GL_UNSIGNED_BYTE, nullptr);
|
||||
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBHeight.framebuffer);
|
||||
s_gles2.glFramebufferTexture2D(
|
||||
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mFBHeight.texture, 0);
|
||||
s_gles2.glBindTexture(GL_TEXTURE_2D, mFBHeight.texture);
|
||||
s_gles2.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mWidth / factor,
|
||||
mHeight / factor, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBHeight.framebuffer);
|
||||
s_gles2.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D, mFBHeight.texture, 0);
|
||||
|
||||
// Update the shaders to the new factor. First detach the old shaders...
|
||||
detachShaders(mFBWidth.program);
|
||||
detachShaders(mFBHeight.program);
|
||||
// Update the shaders to the new factor. First detach the old shaders...
|
||||
detachShaders(mFBWidth.program);
|
||||
detachShaders(mFBHeight.program);
|
||||
|
||||
// ... then attach the new ones.
|
||||
std::ostringstream factorDefine;
|
||||
factorDefine << "#define FACTOR " << factor << "\n";
|
||||
attachShaders(&mFBWidth, factorDefine.str().c_str(), "#define HORIZONTAL\n", mWidth, mHeight);
|
||||
attachShaders(&mFBHeight, factorDefine.str().c_str(), "#define VERTICAL\n", mWidth, mHeight);
|
||||
// ... then attach the new ones.
|
||||
std::ostringstream factorDefine;
|
||||
factorDefine << "#define FACTOR " << factor << "\n";
|
||||
attachShaders(&mFBWidth, factorDefine.str().c_str(), "#define HORIZONTAL\n",
|
||||
mWidth, mHeight);
|
||||
attachShaders(&mFBHeight, factorDefine.str().c_str(), "#define VERTICAL\n",
|
||||
mWidth, mHeight);
|
||||
|
||||
mFactor = factor;
|
||||
mFactor = factor;
|
||||
}
|
||||
|
||||
void TextureResize::resize(GLuint texture) {
|
||||
s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
|
||||
s_gles2.glActiveTexture(GL_TEXTURE0);
|
||||
s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
|
||||
s_gles2.glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
// First scale the horizontal dimension by rendering the input texture to a scaled framebuffer.
|
||||
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBWidth.framebuffer);
|
||||
s_gles2.glViewport(0, 0, mWidth / mFactor, mHeight);
|
||||
s_gles2.glUseProgram(mFBWidth.program);
|
||||
s_gles2.glEnableVertexAttribArray(mFBWidth.aPosition);
|
||||
s_gles2.glVertexAttribPointer(mFBWidth.aPosition, 2, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
s_gles2.glBindTexture(GL_TEXTURE_2D, texture);
|
||||
// First scale the horizontal dimension by rendering the input texture to a
|
||||
// scaled framebuffer.
|
||||
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBWidth.framebuffer);
|
||||
s_gles2.glViewport(0, 0, mWidth / mFactor, mHeight);
|
||||
s_gles2.glUseProgram(mFBWidth.program);
|
||||
s_gles2.glEnableVertexAttribArray(mFBWidth.aPosition);
|
||||
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.
|
||||
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_MIN_FILTER, &min_filter);
|
||||
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.glUniform1i(mFBWidth.uTexture, 0);
|
||||
s_gles2.glDrawArrays(GL_TRIANGLES, 0, sizeof(kVertexData) / (2 * sizeof(float)));
|
||||
// Store the current texture filters and set to nearest for scaling.
|
||||
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_MIN_FILTER,
|
||||
&min_filter);
|
||||
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.glUniform1i(mFBWidth.uTexture, 0);
|
||||
s_gles2.glDrawArrays(GL_TRIANGLES, 0,
|
||||
sizeof(kVertexData) / (2 * sizeof(float)));
|
||||
|
||||
// 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_MIN_FILTER, min_filter);
|
||||
// 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_MIN_FILTER, min_filter);
|
||||
|
||||
// Secondly, scale the vertical dimension using the second framebuffer.
|
||||
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBHeight.framebuffer);
|
||||
s_gles2.glViewport(0, 0, mWidth / mFactor, mHeight / mFactor);
|
||||
s_gles2.glUseProgram(mFBHeight.program);
|
||||
s_gles2.glEnableVertexAttribArray(mFBHeight.aPosition);
|
||||
s_gles2.glVertexAttribPointer(mFBHeight.aPosition, 2, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
s_gles2.glBindTexture(GL_TEXTURE_2D, mFBWidth.texture);
|
||||
s_gles2.glUniform1i(mFBHeight.uTexture, 0);
|
||||
s_gles2.glDrawArrays(GL_TRIANGLES, 0, sizeof(kVertexData) / (2 * sizeof(float)));
|
||||
// Secondly, scale the vertical dimension using the second framebuffer.
|
||||
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBHeight.framebuffer);
|
||||
s_gles2.glViewport(0, 0, mWidth / mFactor, mHeight / mFactor);
|
||||
s_gles2.glUseProgram(mFBHeight.program);
|
||||
s_gles2.glEnableVertexAttribArray(mFBHeight.aPosition);
|
||||
s_gles2.glVertexAttribPointer(mFBHeight.aPosition, 2, GL_FLOAT, GL_FALSE, 0,
|
||||
0);
|
||||
s_gles2.glBindTexture(GL_TEXTURE_2D, mFBWidth.texture);
|
||||
s_gles2.glUniform1i(mFBHeight.uTexture, 0);
|
||||
s_gles2.glDrawArrays(GL_TRIANGLES, 0,
|
||||
sizeof(kVertexData) / (2 * sizeof(float)));
|
||||
|
||||
// Clear the bindings.
|
||||
s_gles2.glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
s_gles2.glBindTexture(GL_TEXTURE_2D, 0);
|
||||
// Clear the bindings.
|
||||
s_gles2.glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
s_gles2.glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,33 +19,33 @@
|
|||
#include <GLES2/gl2.h>
|
||||
|
||||
class TextureResize {
|
||||
public:
|
||||
TextureResize(GLuint width, GLuint height);
|
||||
~TextureResize();
|
||||
public:
|
||||
TextureResize(GLuint width, GLuint height);
|
||||
~TextureResize();
|
||||
|
||||
// Scales the given texture for the current viewport and returns the scaled
|
||||
// texture. May return the input if no scaling is required.
|
||||
GLuint update(GLuint texture);
|
||||
// Scales the given texture for the current viewport and returns the scaled
|
||||
// texture. May return the input if no scaling is required.
|
||||
GLuint update(GLuint texture);
|
||||
|
||||
struct Framebuffer {
|
||||
GLuint texture;
|
||||
GLuint framebuffer;
|
||||
GLuint program;
|
||||
GLuint aPosition;
|
||||
GLuint uTexture;
|
||||
};
|
||||
struct Framebuffer {
|
||||
GLuint texture;
|
||||
GLuint framebuffer;
|
||||
GLuint program;
|
||||
GLuint aPosition;
|
||||
GLuint uTexture;
|
||||
};
|
||||
|
||||
private:
|
||||
void setupFramebuffers(unsigned int factor);
|
||||
void resize(GLuint texture);
|
||||
private:
|
||||
void setupFramebuffers(unsigned int factor);
|
||||
void resize(GLuint texture);
|
||||
|
||||
private:
|
||||
GLuint mWidth;
|
||||
GLuint mHeight;
|
||||
unsigned int mFactor;
|
||||
Framebuffer mFBWidth;
|
||||
Framebuffer mFBHeight;
|
||||
GLuint mVertexBuffer;
|
||||
private:
|
||||
GLuint mWidth;
|
||||
GLuint mHeight;
|
||||
unsigned int mFactor;
|
||||
Framebuffer mFBWidth;
|
||||
Framebuffer mFBHeight;
|
||||
GLuint mVertexBuffer;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -15,21 +15,16 @@
|
|||
*/
|
||||
#include "TimeUtils.h"
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
long long GetCurrentTimeMS()
|
||||
{
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
long long iDiff = (now.tv_sec * 1000LL) + now.tv_nsec/1000000LL;
|
||||
return iDiff;
|
||||
long long GetCurrentTimeMS() {
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
long long iDiff = (now.tv_sec * 1000LL) + now.tv_nsec / 1000000LL;
|
||||
return iDiff;
|
||||
}
|
||||
|
||||
void TimeSleepMS(int p_mili)
|
||||
{
|
||||
usleep(p_mili * 1000);
|
||||
}
|
||||
void TimeSleepMS(int p_mili) { usleep(p_mili * 1000); }
|
||||
|
|
|
|||
|
|
@ -20,155 +20,141 @@
|
|||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
/* 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
|
||||
* large enough for our very limited needs.
|
||||
*/
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 128
|
||||
#define PATH_MAX 128
|
||||
#endif
|
||||
|
||||
UnixStream::UnixStream(size_t bufSize) :
|
||||
SocketStream(bufSize),
|
||||
bound_socket_path(NULL)
|
||||
{
|
||||
}
|
||||
UnixStream::UnixStream(size_t bufSize)
|
||||
: SocketStream(bufSize), bound_socket_path(NULL) {}
|
||||
|
||||
UnixStream::UnixStream(int sock, size_t bufSize) :
|
||||
SocketStream(sock, bufSize),
|
||||
bound_socket_path(NULL)
|
||||
{
|
||||
}
|
||||
UnixStream::UnixStream(int sock, size_t bufSize)
|
||||
: SocketStream(sock, bufSize), bound_socket_path(NULL) {}
|
||||
|
||||
UnixStream::~UnixStream()
|
||||
{
|
||||
if (bound_socket_path != NULL) {
|
||||
int ret = 0;
|
||||
do {
|
||||
ret = unlink(bound_socket_path);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
if(ret != 0) {
|
||||
ERR("Failed to unlink UNIX socket at \"%s\"\n", bound_socket_path);
|
||||
perror("UNIX socket could not be unlinked");
|
||||
}
|
||||
free(bound_socket_path);
|
||||
UnixStream::~UnixStream() {
|
||||
if (bound_socket_path != NULL) {
|
||||
int ret = 0;
|
||||
do {
|
||||
ret = unlink(bound_socket_path);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
if (ret != 0) {
|
||||
ERR("Failed to unlink UNIX socket at \"%s\"\n", bound_socket_path);
|
||||
perror("UNIX socket could not be unlinked");
|
||||
}
|
||||
free(bound_socket_path);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize a sockaddr_un with the appropriate values corresponding
|
||||
* to a given 'virtual port'. Returns 0 on success, -1 on error.
|
||||
*/
|
||||
static int
|
||||
make_unix_path(char *path, size_t pathlen, int port_number)
|
||||
{
|
||||
char tmp[PATH_MAX]; // temp directory
|
||||
int ret = 0;
|
||||
static int make_unix_path(char *path, size_t pathlen, int port_number) {
|
||||
char tmp[PATH_MAX]; // temp directory
|
||||
int ret = 0;
|
||||
|
||||
// First, create user-specific temp directory if needed
|
||||
const char* user = getenv("XDG_RUNTIME_DIR");
|
||||
if (user != NULL) {
|
||||
struct stat st;
|
||||
snprintf(tmp, sizeof(tmp), "%s/anbox", user);
|
||||
do {
|
||||
ret = ::lstat(tmp, &st);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
// First, create user-specific temp directory if needed
|
||||
const char *user = getenv("XDG_RUNTIME_DIR");
|
||||
if (user != NULL) {
|
||||
struct stat st;
|
||||
snprintf(tmp, sizeof(tmp), "%s/anbox", user);
|
||||
do {
|
||||
ret = ::lstat(tmp, &st);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
|
||||
if (ret < 0 && errno == ENOENT) {
|
||||
do {
|
||||
ret = ::mkdir(tmp, 0766);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
if (ret < 0) {
|
||||
ERR("Could not create temp directory: %s", tmp);
|
||||
user = NULL; // will fall-back to /tmp
|
||||
}
|
||||
}
|
||||
else if (ret < 0) {
|
||||
user = NULL; // will fallback to /tmp
|
||||
}
|
||||
if (ret < 0 && errno == ENOENT) {
|
||||
do {
|
||||
ret = ::mkdir(tmp, 0766);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
if (ret < 0) {
|
||||
ERR("Could not create temp directory: %s", tmp);
|
||||
user = NULL; // will fall-back to /tmp
|
||||
}
|
||||
} else if (ret < 0) {
|
||||
user = NULL; // will fallback to /tmp
|
||||
}
|
||||
}
|
||||
|
||||
if (user == NULL) { // fallback to /tmp in case of error
|
||||
snprintf(tmp, sizeof(tmp), "/tmp");
|
||||
if (user == NULL) { // fallback to /tmp in case of error
|
||||
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
|
||||
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;
|
||||
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])
|
||||
{
|
||||
if (make_unix_path(addrstr, MAX_ADDRSTR_LEN, getpid()) < 0) {
|
||||
return -1;
|
||||
}
|
||||
m_sock = emugl::socketLocalServer(addrstr, SOCK_STREAM);
|
||||
|
||||
m_sock = emugl::socketLocalServer(addrstr, SOCK_STREAM);
|
||||
if (!valid()) return int(ERR_INVALID_SOCKET);
|
||||
|
||||
if (!valid())
|
||||
return int(ERR_INVALID_SOCKET);
|
||||
bound_socket_path = strdup(addrstr);
|
||||
if (bound_socket_path == NULL) {
|
||||
ERR("WARNING: UNIX socket at \"%s\" should be manually removed \n",
|
||||
addrstr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bound_socket_path = strdup(addrstr);
|
||||
if(bound_socket_path == NULL) {
|
||||
ERR("WARNING: UNIX socket at \"%s\" should be manually removed \n",
|
||||
addrstr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SocketStream * UnixStream::accept()
|
||||
{
|
||||
int clientSock = -1;
|
||||
SocketStream *UnixStream::accept() {
|
||||
int clientSock = -1;
|
||||
|
||||
while (true) {
|
||||
struct sockaddr_un addr;
|
||||
socklen_t len = sizeof(addr);
|
||||
clientSock = ::accept(m_sock, (sockaddr *)&addr, &len);
|
||||
// DBG("UnixStream::accept @ %d \n", clientSock);
|
||||
while (true) {
|
||||
struct sockaddr_un addr;
|
||||
socklen_t len = sizeof(addr);
|
||||
clientSock = ::accept(m_sock, (sockaddr *)&addr, &len);
|
||||
// DBG("UnixStream::accept @ %d \n", clientSock);
|
||||
|
||||
if (clientSock < 0 && errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
if (clientSock < 0 && errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
UnixStream *clientStream = NULL;
|
||||
UnixStream *clientStream = NULL;
|
||||
|
||||
if (clientSock >= 0) {
|
||||
clientStream = new UnixStream(clientSock, m_bufsize);
|
||||
}
|
||||
return clientStream;
|
||||
if (clientSock >= 0) {
|
||||
clientStream = new UnixStream(clientSock, m_bufsize);
|
||||
}
|
||||
return clientStream;
|
||||
}
|
||||
|
||||
int UnixStream::connect(const char* addr)
|
||||
{
|
||||
m_sock = emugl::socketLocalClient(addr, SOCK_STREAM);
|
||||
// DBG("UnixStream::connect @ %d \n", m_sock);
|
||||
if (!valid()) return -1;
|
||||
int UnixStream::connect(const char *addr) {
|
||||
m_sock = emugl::socketLocalClient(addr, SOCK_STREAM);
|
||||
// DBG("UnixStream::connect @ %d \n", m_sock);
|
||||
if (!valid()) return -1;
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,15 +19,16 @@
|
|||
#include "SocketStream.h"
|
||||
|
||||
class UnixStream : public SocketStream {
|
||||
public:
|
||||
explicit UnixStream(size_t bufsize = 10000);
|
||||
~UnixStream();
|
||||
virtual int listen(char addrstr[MAX_ADDRSTR_LEN]);
|
||||
virtual SocketStream *accept();
|
||||
virtual int connect(const char* addr);
|
||||
private:
|
||||
char *bound_socket_path;
|
||||
UnixStream(int sock, size_t bufSize);
|
||||
public:
|
||||
explicit UnixStream(size_t bufsize = 10000);
|
||||
~UnixStream();
|
||||
virtual int listen(char addrstr[MAX_ADDRSTR_LEN]);
|
||||
virtual SocketStream *accept();
|
||||
virtual int connect(const char *addr);
|
||||
|
||||
private:
|
||||
char *bound_socket_path;
|
||||
UnixStream(int sock, size_t bufSize);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -25,163 +25,150 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
WindowSurface::WindowSurface(EGLDisplay display,
|
||||
EGLConfig config) :
|
||||
mSurface(NULL),
|
||||
mAttachedColorBuffer(NULL),
|
||||
mReadContext(NULL),
|
||||
mDrawContext(NULL),
|
||||
mWidth(0),
|
||||
mHeight(0),
|
||||
mConfig(config),
|
||||
mDisplay(display) {}
|
||||
WindowSurface::WindowSurface(EGLDisplay display, EGLConfig config)
|
||||
: mSurface(NULL),
|
||||
mAttachedColorBuffer(NULL),
|
||||
mReadContext(NULL),
|
||||
mDrawContext(NULL),
|
||||
mWidth(0),
|
||||
mHeight(0),
|
||||
mConfig(config),
|
||||
mDisplay(display) {}
|
||||
|
||||
WindowSurface::~WindowSurface() {
|
||||
if (mSurface) {
|
||||
s_egl.eglDestroySurface(mDisplay, mSurface);
|
||||
}
|
||||
if (mSurface) {
|
||||
s_egl.eglDestroySurface(mDisplay, mSurface);
|
||||
}
|
||||
}
|
||||
|
||||
WindowSurface *WindowSurface::create(EGLDisplay display,
|
||||
EGLConfig config,
|
||||
int p_width,
|
||||
int p_height) {
|
||||
// allocate space for the WindowSurface object
|
||||
WindowSurface *win = new WindowSurface(display, config);
|
||||
if (!win) {
|
||||
return NULL;
|
||||
}
|
||||
WindowSurface *WindowSurface::create(EGLDisplay display, EGLConfig config,
|
||||
int p_width, int p_height) {
|
||||
// allocate space for the WindowSurface object
|
||||
WindowSurface *win = new WindowSurface(display, config);
|
||||
if (!win) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create a pbuffer to be used as the egl surface
|
||||
// for that window.
|
||||
if (!win->resize(p_width, p_height)) {
|
||||
delete win;
|
||||
return NULL;
|
||||
}
|
||||
// Create a pbuffer to be used as the egl surface
|
||||
// for that window.
|
||||
if (!win->resize(p_width, p_height)) {
|
||||
delete win;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return win;
|
||||
return win;
|
||||
}
|
||||
|
||||
|
||||
void WindowSurface::setColorBuffer(ColorBufferPtr p_colorBuffer) {
|
||||
mAttachedColorBuffer = p_colorBuffer;
|
||||
mAttachedColorBuffer = p_colorBuffer;
|
||||
|
||||
// resize the window if the attached color buffer is of different
|
||||
// size.
|
||||
unsigned int cbWidth = mAttachedColorBuffer->getWidth();
|
||||
unsigned int cbHeight = mAttachedColorBuffer->getHeight();
|
||||
// resize the window if the attached color buffer is of different
|
||||
// size.
|
||||
unsigned int cbWidth = mAttachedColorBuffer->getWidth();
|
||||
unsigned int cbHeight = mAttachedColorBuffer->getHeight();
|
||||
|
||||
if (cbWidth != mWidth || cbHeight != mHeight) {
|
||||
resize(cbWidth, cbHeight);
|
||||
}
|
||||
if (cbWidth != mWidth || cbHeight != mHeight) {
|
||||
resize(cbWidth, cbHeight);
|
||||
}
|
||||
}
|
||||
|
||||
void WindowSurface::bind(RenderContextPtr p_ctx, BindType p_bindType) {
|
||||
if (p_bindType == BIND_READ) {
|
||||
mReadContext = p_ctx;
|
||||
} else if (p_bindType == BIND_DRAW) {
|
||||
mDrawContext = p_ctx;
|
||||
} else if (p_bindType == BIND_READDRAW) {
|
||||
mReadContext = p_ctx;
|
||||
mDrawContext = p_ctx;
|
||||
}
|
||||
if (p_bindType == BIND_READ) {
|
||||
mReadContext = p_ctx;
|
||||
} else if (p_bindType == BIND_DRAW) {
|
||||
mDrawContext = p_ctx;
|
||||
} else if (p_bindType == BIND_READDRAW) {
|
||||
mReadContext = p_ctx;
|
||||
mDrawContext = p_ctx;
|
||||
}
|
||||
}
|
||||
|
||||
bool WindowSurface::flushColorBuffer() {
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool WindowSurface::resize(unsigned int p_width, unsigned int p_height)
|
||||
{
|
||||
if (mSurface && mWidth == p_width && mHeight == p_height) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
bool WindowSurface::resize(unsigned int p_width, unsigned int p_height) {
|
||||
if (mSurface && mWidth == p_width && mHeight == p_height) {
|
||||
// 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,75 +27,69 @@
|
|||
// A class used to model a guest-side window surface. The implementation
|
||||
// uses a host Pbuffer to act as the EGL rendering surface instead.
|
||||
class WindowSurface {
|
||||
public:
|
||||
// Create a new WindowSurface instance.
|
||||
// |display| is the host EGLDisplay value.
|
||||
// |config| is the host EGLConfig value.
|
||||
// |width| and |height| are the initial size of the Pbuffer.
|
||||
// Return a new WindowSurface instance on success, or NULL on failure.
|
||||
static WindowSurface* create(EGLDisplay display,
|
||||
EGLConfig config,
|
||||
int width,
|
||||
int height);
|
||||
public:
|
||||
// Create a new WindowSurface instance.
|
||||
// |display| is the host EGLDisplay value.
|
||||
// |config| is the host EGLConfig value.
|
||||
// |width| and |height| are the initial size of the Pbuffer.
|
||||
// Return a new WindowSurface instance on success, or NULL on failure.
|
||||
static WindowSurface* create(EGLDisplay display, EGLConfig config, int width,
|
||||
int height);
|
||||
|
||||
// Destructor.
|
||||
~WindowSurface();
|
||||
// Destructor.
|
||||
~WindowSurface();
|
||||
|
||||
// Retrieve the host EGLSurface of the WindowSurface's Pbuffer.
|
||||
EGLSurface getEGLSurface() const { return mSurface; }
|
||||
// Retrieve the host EGLSurface of the WindowSurface's Pbuffer.
|
||||
EGLSurface getEGLSurface() const { return mSurface; }
|
||||
|
||||
// Attach a ColorBuffer to this WindowSurface.
|
||||
// Once attached, calling flushColorBuffer() will copy the Pbuffer's
|
||||
// pixels to the color buffer.
|
||||
//
|
||||
// IMPORTANT: This automatically resizes the Pbuffer's to the ColorBuffer's
|
||||
// dimensions. Potentially losing pixel values in the process.
|
||||
void setColorBuffer(ColorBufferPtr p_colorBuffer);
|
||||
// Attach a ColorBuffer to this WindowSurface.
|
||||
// Once attached, calling flushColorBuffer() will copy the Pbuffer's
|
||||
// pixels to the color buffer.
|
||||
//
|
||||
// IMPORTANT: This automatically resizes the Pbuffer's to the ColorBuffer's
|
||||
// dimensions. Potentially losing pixel values in the process.
|
||||
void setColorBuffer(ColorBufferPtr p_colorBuffer);
|
||||
|
||||
// Copy the Pbuffer's pixels to the attached color buffer.
|
||||
// Returns true on success, or false on error (e.g. if there is no
|
||||
// attached color buffer).
|
||||
bool flushColorBuffer();
|
||||
// Copy the Pbuffer's pixels to the attached color buffer.
|
||||
// Returns true on success, or false on error (e.g. if there is no
|
||||
// attached color buffer).
|
||||
bool flushColorBuffer();
|
||||
|
||||
// Used by bind() below.
|
||||
enum BindType {
|
||||
BIND_READ,
|
||||
BIND_DRAW,
|
||||
BIND_READDRAW
|
||||
};
|
||||
// Used by bind() below.
|
||||
enum BindType { BIND_READ, BIND_DRAW, BIND_READDRAW };
|
||||
|
||||
// TODO(digit): What is this used for exactly? For example, the
|
||||
// mReadContext is never used by this class. The mDrawContext is only
|
||||
// used temporarily during flushColorBuffer() operation, and could be
|
||||
// passed as a parameter to the function instead. Maybe this is only used
|
||||
// to increment reference counts on the smart pointers.
|
||||
//
|
||||
// Bind a context to the WindowSurface (huh? Normally you would bind a
|
||||
// surface to the context, not the other way around)
|
||||
//
|
||||
// |p_ctx| is a RenderContext pointer.
|
||||
// |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
|
||||
// for BIND_READDRAW, it assigns it to both.
|
||||
void bind(RenderContextPtr p_ctx, BindType p_bindType);
|
||||
// TODO(digit): What is this used for exactly? For example, the
|
||||
// mReadContext is never used by this class. The mDrawContext is only
|
||||
// used temporarily during flushColorBuffer() operation, and could be
|
||||
// passed as a parameter to the function instead. Maybe this is only used
|
||||
// to increment reference counts on the smart pointers.
|
||||
//
|
||||
// Bind a context to the WindowSurface (huh? Normally you would bind a
|
||||
// surface to the context, not the other way around)
|
||||
//
|
||||
// |p_ctx| is a RenderContext pointer.
|
||||
// |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
|
||||
// for BIND_READDRAW, it assigns it to both.
|
||||
void bind(RenderContextPtr p_ctx, BindType p_bindType);
|
||||
|
||||
private:
|
||||
WindowSurface();
|
||||
WindowSurface(const WindowSurface& other);
|
||||
private:
|
||||
WindowSurface();
|
||||
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:
|
||||
EGLSurface mSurface;
|
||||
ColorBufferPtr mAttachedColorBuffer;
|
||||
RenderContextPtr mReadContext;
|
||||
RenderContextPtr mDrawContext;
|
||||
GLuint mWidth;
|
||||
GLuint mHeight;
|
||||
EGLConfig mConfig;
|
||||
EGLDisplay mDisplay;
|
||||
private:
|
||||
EGLSurface mSurface;
|
||||
ColorBufferPtr mAttachedColorBuffer;
|
||||
RenderContextPtr mReadContext;
|
||||
RenderContextPtr mDrawContext;
|
||||
GLuint mWidth;
|
||||
GLuint mHeight;
|
||||
EGLConfig mConfig;
|
||||
EGLDisplay mDisplay;
|
||||
};
|
||||
|
||||
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