diff --git a/.gitignore b/.gitignore index 0706bb2..f08ed1e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ out/ sdk/ +.vscode diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e55be28 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.11) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "MinSizeRel" CACHE STRING "Choose the type of build, options are: Debug, Release, or MinSizeRel." FORCE) + message(STATUS "CMAKE_BUILD_TYPE not set, defaulting to MinSizeRel.") +endif() + +project(flutter LANGUAGES CXX) + +message(STATUS "Generator .............. ${CMAKE_GENERATOR}") +message(STATUS "Build Type ............. ${CMAKE_BUILD_TYPE}") + +include_directories( + ${CMAKE_SYSROOT}/opt/vc/include + ${CMAKE_SYSROOT}/opt/vc/include/interface/vcos/pthreads + ${CMAKE_SYSROOT}/opt/vc/include/interface/vmcs_host/linux + ${CMAKE_CURRENT_SOURCE_DIR}/../../../engine-prefix/src/engine/src/third_party/rapidjson/include + ${CMAKE_CURRENT_SOURCE_DIR}/flutter/cpp_client_wrapper/include/flutter + ${ENGINE_INCLUDE_DIR}) + +link_directories( + ${CMAKE_SYSROOT}/opt/vc/lib + ${ENGINE_LIBRARIES_DIR}) + +add_definitions( + -DSTANDALONE -D_LINUX -DTARGET_POSIX -D_REENTRANT + -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 + -DHAVE_LIBBCM_HOST -DUSE_EXTERNAL_LIBBCM_HOST + -fPIC -DPIC + -DUSE_RAPID_JSON + -DGLFW_INCLUDE_ES2 + -DUSE_VCHIQ_ARM -DHAVE_LIBOPENMAX=2 + -DUSE_EXTERNAL_OMX -DOMX -DOMX_SKIP64BIT) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -U_FORTIFY_SOURCE -Wall -g -ftree-vectorize -pipe") + + +set(cxx_sources + flutter/main.cc + flutter/utils.cc + flutter/cpp_client_wrapper/engine_method_result.cc + flutter/cpp_client_wrapper/flutter_window_controller.cc + flutter/cpp_client_wrapper/json_message_codec.cc + flutter/cpp_client_wrapper/plugin_registrar.cc + flutter/cpp_client_wrapper/standard_codec.cc +) + +add_executable(flutter ${cxx_sources}) +target_link_libraries(flutter ts brcmGLESv2 brcmEGL bcm_host flutter_engine pthread dl flutter_linux) + +install(TARGETS flutter RUNTIME DESTINATION bin) diff --git a/flutter/BUILD.gn b/flutter/BUILD.gn index 4370bbf..b96fa5c 100644 --- a/flutter/BUILD.gn +++ b/flutter/BUILD.gn @@ -21,6 +21,7 @@ executable("flutter") { libs = [ "rt", + "ts", "brcmEGL", "brcmGLESv2", "flutter_engine", diff --git a/flutter/cpp_client_wrapper/README b/flutter/cpp_client_wrapper/README new file mode 100644 index 0000000..a7f8764 --- /dev/null +++ b/flutter/cpp_client_wrapper/README @@ -0,0 +1,9 @@ +This code is intended to be built into plugins and applications to provide +higher-level, C++ abstractions for interacting with the Flutter library. + +Over time, the goal is to move more of this code into the library in a way that +provides a usable ABI (e.g., does not use standard libary in the interfaces). + +Note that this wrapper is still in early stages. Expect significant churn in +both the APIs and the structure of the wrapper (e.g., the exact set of files +that need to be built). diff --git a/flutter/cpp_client_wrapper/byte_stream_wrappers.h b/flutter/cpp_client_wrapper/byte_stream_wrappers.h new file mode 100644 index 0000000..96ab0b0 --- /dev/null +++ b/flutter/cpp_client_wrapper/byte_stream_wrappers.h @@ -0,0 +1,105 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_BYTE_STREAM_WRAPPERS_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_BYTE_STREAM_WRAPPERS_H_ + +// Utility classes for interacting with a buffer of bytes as a stream, for use +// in message channel codecs. + +#include +#include +#include +#include + +namespace flutter { + +// Wraps an array of bytes with utility methods for treating it as a readable +// stream. +class ByteBufferStreamReader { + public: + // Createa a reader reading from |bytes|, which must have a length of |size|. + // |bytes| must remain valid for the lifetime of this object. + explicit ByteBufferStreamReader(const uint8_t* bytes, size_t size) + : bytes_(bytes), size_(size) {} + + // Reads and returns the next byte from the stream. + uint8_t ReadByte() { + if (location_ >= size_) { + std::cerr << "Invalid read in StandardCodecByteStreamReader" << std::endl; + return 0; + } + return bytes_[location_++]; + } + + // Reads the next |length| bytes from the stream into |buffer|. The caller + // is responsible for ensuring that |buffer| is large enough. + void ReadBytes(uint8_t* buffer, size_t length) { + if (location_ + length > size_) { + std::cerr << "Invalid read in StandardCodecByteStreamReader" << std::endl; + return; + } + std::memcpy(buffer, &bytes_[location_], length); + location_ += length; + } + + // Advances the read cursor to the next multiple of |alignment| relative to + // the start of the wrapped byte buffer, unless it is already aligned. + void ReadAlignment(uint8_t alignment) { + uint8_t mod = location_ % alignment; + if (mod) { + location_ += alignment - mod; + } + } + + private: + // The buffer to read from. + const uint8_t* bytes_; + // The total size of the buffer. + size_t size_; + // The current read location. + size_t location_ = 0; +}; + +// Wraps an array of bytes with utility methods for treating it as a writable +// stream. +class ByteBufferStreamWriter { + public: + // Createa a writter that writes into |buffer|. + // |buffer| must remain valid for the lifetime of this object. + explicit ByteBufferStreamWriter(std::vector* buffer) + : bytes_(buffer) { + assert(buffer); + } + + // Writes |byte| to the wrapped buffer. + void WriteByte(uint8_t byte) { bytes_->push_back(byte); } + + // Writes the next |length| bytes from |bytes| into the wrapped buffer. + // The caller is responsible for ensuring that |buffer| is large enough. + void WriteBytes(const uint8_t* bytes, size_t length) { + assert(length > 0); + bytes_->insert(bytes_->end(), bytes, bytes + length); + } + + // Writes 0s until the next multiple of |alignment| relative to + // the start of the wrapped byte buffer, unless the write positition is + // already aligned. + void WriteAlignment(uint8_t alignment) { + uint8_t mod = bytes_->size() % alignment; + if (mod) { + for (int i = 0; i < alignment - mod; ++i) { + WriteByte(0); + } + } + } + + private: + // The buffer to write to. + std::vector* bytes_; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_BYTE_STREAM_WRAPPERS_H_ diff --git a/flutter/cpp_client_wrapper/engine_method_result.cc b/flutter/cpp_client_wrapper/engine_method_result.cc new file mode 100644 index 0000000..03e17a8 --- /dev/null +++ b/flutter/cpp_client_wrapper/engine_method_result.cc @@ -0,0 +1,45 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "include/flutter/engine_method_result.h" + +#include +#include + +namespace flutter { +namespace internal { + +ReplyManager::ReplyManager(BinaryReply reply_handler) + : reply_handler_(std::move(reply_handler)) { + assert(reply_handler_); +} + +ReplyManager::~ReplyManager() { + if (reply_handler_) { + // Warn, rather than send a not-implemented response, since the engine may + // no longer be valid at this point. + std::cerr + << "Warning: Failed to respond to a message. This is a memory leak." + << std::endl; + } +} + +void ReplyManager::SendResponseData(const std::vector* data) { + if (!reply_handler_) { + std::cerr + << "Error: Only one of Success, Error, or NotImplemented can be " + "called," + << " and it can be called exactly once. Ignoring duplicate result." + << std::endl; + return; + } + + const uint8_t* message = data && !data->empty() ? data->data() : nullptr; + size_t message_size = data ? data->size() : 0; + reply_handler_(message, message_size); + reply_handler_ = nullptr; +} + +} // namespace internal +} // namespace flutter diff --git a/flutter/cpp_client_wrapper/flutter_window_controller.cc b/flutter/cpp_client_wrapper/flutter_window_controller.cc new file mode 100644 index 0000000..4253597 --- /dev/null +++ b/flutter/cpp_client_wrapper/flutter_window_controller.cc @@ -0,0 +1,81 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "include/flutter/flutter_window_controller.h" + +#include +#include + +namespace flutter { + +FlutterWindowController::FlutterWindowController( + const std::string& icu_data_path) + : icu_data_path_(icu_data_path) { + init_succeeded_ = FlutterDesktopInit(); +} + +FlutterWindowController::~FlutterWindowController() { + if (controller_) { + FlutterDesktopDestroyWindow(controller_); + } + if (init_succeeded_) { + FlutterDesktopTerminate(); + } +} + +bool FlutterWindowController::CreateWindow( + int width, + int height, + const std::string& title, + const std::string& assets_path, + const std::vector& arguments) { + if (!init_succeeded_) { + std::cerr << "Could not create window; FlutterDesktopInit failed." + << std::endl; + return false; + } + + if (controller_) { + std::cerr << "Only one Flutter window can exist at a time." << std::endl; + return false; + } + + std::vector engine_arguments; + std::transform( + arguments.begin(), arguments.end(), std::back_inserter(engine_arguments), + [](const std::string& arg) -> const char* { return arg.c_str(); }); + size_t arg_count = engine_arguments.size(); + + controller_ = FlutterDesktopCreateWindow( + width, height, title.c_str(), assets_path.c_str(), icu_data_path_.c_str(), + arg_count > 0 ? &engine_arguments[0] : nullptr, arg_count); + if (!controller_) { + std::cerr << "Failed to create window." << std::endl; + return false; + } + window_ = + std::make_unique(FlutterDesktopGetWindow(controller_)); + return true; +} + +FlutterDesktopPluginRegistrarRef FlutterWindowController::GetRegistrarForPlugin( + const std::string& plugin_name) { + if (!controller_) { + std::cerr << "Cannot get plugin registrar without a window; call " + "CreateWindow first." + << std::endl; + return nullptr; + } + return FlutterDesktopGetPluginRegistrar(controller_, plugin_name.c_str()); +} + +void FlutterWindowController::RunEventLoop() { + if (controller_) { + FlutterDesktopRunWindowLoop(controller_); + } + window_ = nullptr; + controller_ = nullptr; +} + +} // namespace flutter diff --git a/flutter/cpp_client_wrapper/include/flutter/basic_message_channel.h b/flutter/cpp_client_wrapper/include/flutter/basic_message_channel.h new file mode 100644 index 0000000..df03714 --- /dev/null +++ b/flutter/cpp_client_wrapper/include/flutter/basic_message_channel.h @@ -0,0 +1,96 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_BASIC_MESSAGE_CHANNEL_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_BASIC_MESSAGE_CHANNEL_H_ + +#include +#include + +#include "binary_messenger.h" +#include "message_codec.h" + +namespace flutter { + +// A message reply callback. +// +// Used for submitting a reply back to a Flutter message sender. +template +using MessageReply = std::function; + +// A handler for receiving a message from the Flutter engine. +// +// Implementations must asynchronously call reply exactly once with the reply +// to the message. +template +using MessageHandler = + std::function reply)>; + +// A channel for communicating with the Flutter engine by sending asynchronous +// messages. +template +class BasicMessageChannel { + public: + // Creates an instance that sends and receives method calls on the channel + // named |name|, encoded with |codec| and dispatched via |messenger|. + BasicMessageChannel(BinaryMessenger* messenger, + const std::string& name, + const MessageCodec* codec) + : messenger_(messenger), name_(name), codec_(codec) {} + + ~BasicMessageChannel() = default; + + // Prevent copying. + BasicMessageChannel(BasicMessageChannel const&) = delete; + BasicMessageChannel& operator=(BasicMessageChannel const&) = delete; + + // Sends a message to the Flutter engine on this channel. + void Send(const T& message) { + std::unique_ptr> raw_message = + codec_->EncodeMessage(message); + messenger_->Send(name_, raw_message->data(), raw_message->size()); + } + + // TODO: Add support for a version of Send expecting a reply once + // https://github.com/flutter/flutter/issues/18852 is fixed. + + // Registers a handler that should be called any time a message is + // received on this channel. + void SetMessageHandler(MessageHandler handler) const { + const auto* codec = codec_; + std::string channel_name = name_; + BinaryMessageHandler binary_handler = [handler, codec, channel_name]( + const uint8_t* binary_message, + const size_t binary_message_size, + BinaryReply binary_reply) { + // Use this channel's codec to decode the message and build a reply + // handler. + std::unique_ptr message = + codec->DecodeMessage(binary_message, binary_message_size); + if (!message) { + std::cerr << "Unable to decode message on channel " << channel_name + << std::endl; + binary_reply(nullptr, 0); + return; + } + + MessageReply unencoded_reply = [binary_reply, + codec](const T& unencoded_response) { + auto binary_response = codec->EncodeMessage(unencoded_response); + binary_reply(binary_response->data(), binary_response->size()); + }; + handler(*message, std::move(unencoded_reply)); + }; + messenger_->SetMessageHandler(name_, std::move(binary_handler)); + } + + private: + BinaryMessenger* messenger_; + std::string name_; + const MessageCodec* codec_; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_BASIC_MESSAGE_CHANNEL_H_ diff --git a/flutter/cpp_client_wrapper/include/flutter/binary_messenger.h b/flutter/cpp_client_wrapper/include/flutter/binary_messenger.h new file mode 100644 index 0000000..ae91941 --- /dev/null +++ b/flutter/cpp_client_wrapper/include/flutter/binary_messenger.h @@ -0,0 +1,56 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_BINARY_MESSENGER_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_BINARY_MESSENGER_H_ + +#include +#include + +// TODO: Consider adding absl as a dependency and using absl::Span for all of +// the message/message_size pairs. +namespace flutter { + +// A binary message reply callback. +// +// Used for submitting a binary reply back to a Flutter message sender. +typedef std::function + BinaryReply; + +// A message handler callback. +// +// Used for receiving messages from Flutter and providing an asynchronous reply. +typedef std::function< + void(const uint8_t* message, const size_t message_size, BinaryReply reply)> + BinaryMessageHandler; + +// A protocol for a class that handles communication of binary data on named +// channels to and from the Flutter engine. +class BinaryMessenger { + public: + virtual ~BinaryMessenger() = default; + + // Sends a binary message to the Flutter side on the specified channel, + // expecting no reply. + // + // TODO: Consider adding absl as a dependency and using absl::Span. + virtual void Send(const std::string& channel, + const uint8_t* message, + const size_t message_size) const = 0; + + // TODO: Add support for a version of Send expecting a reply once + // https://github.com/flutter/flutter/issues/18852 is fixed. + + // Registers a message handler for incoming binary messages from the Flutter + // side on the specified channel. + // + // Replaces any existing handler. Provide a null handler to unregister the + // existing handler. + virtual void SetMessageHandler(const std::string& channel, + BinaryMessageHandler handler) = 0; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_BINARY_MESSENGER_H_ diff --git a/flutter/cpp_client_wrapper/include/flutter/encodable_value.h b/flutter/cpp_client_wrapper/include/flutter/encodable_value.h new file mode 100644 index 0000000..03d9803 --- /dev/null +++ b/flutter/cpp_client_wrapper/include/flutter/encodable_value.h @@ -0,0 +1,569 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_ENCODABLE_VALUE_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_ENCODABLE_VALUE_H_ + +#include +#include +#include +#include +#include +#include + +namespace flutter { + +static_assert(sizeof(double) == 8, "EncodableValue requires a 64-bit double"); + +class EncodableValue; +// Convenience type aliases for list and map EncodableValue types. +using EncodableList = std::vector; +using EncodableMap = std::map; + +// An object that can contain any value or collection type supported by +// Flutter's standard method codec. +// +// For details, see: +// https://api.flutter.dev/flutter/services/StandardMessageCodec-class.html +// +// As an example, the following Dart structure: +// { +// 'flag': true, +// 'name': 'Thing', +// 'values': [1, 2.0, 4], +// } +// would correspond to: +// EncodableValue(EncodableMap{ +// {EncodableValue("flag"), EncodableValue(true)}, +// {EncodableValue("name"), EncodableValue("Thing")}, +// {EncodableValue("values"), EncodableValue(EncodableList{ +// EncodableValue(1), +// EncodableValue(2.0), +// EncodableValue(4), +// })}, +// }) +class EncodableValue { + public: + // Possible types for an EncodableValue to reperesent. + enum class Type { + kNull, // A null value. + kBool, // A boolean value. + kInt, // A 32-bit integer. + kLong, // A 64-bit integer. + kDouble, // A 64-bit floating point number. + kString, // A string. + kByteList, // A list of bytes. + kIntList, // A list of 32-bit integers. + kLongList, // A list of 64-bit integers. + kDoubleList, // A list of 64-bit floating point numbers. + kList, // A list of EncodableValues. + kMap, // A mapping from EncodableValues to EncodableValues. + }; + + // Creates an instance representing a null value. + EncodableValue() {} + + // Creates an instance representing a bool value. + explicit EncodableValue(bool value) : bool_(value), type_(Type::kBool) {} + + // Creates an instance representing a 32-bit integer value. + explicit EncodableValue(int32_t value) : int_(value), type_(Type::kInt) {} + + // Creates an instance representing a 64-bit integer value. + explicit EncodableValue(int64_t value) : long_(value), type_(Type::kLong) {} + + // Creates an instance representing a 64-bit floating point value. + explicit EncodableValue(double value) + : double_(value), type_(Type::kDouble) {} + + // Creates an instance representing a string value. + explicit EncodableValue(const char* value) + : string_(new std::string(value)), type_(Type::kString) {} + + // Creates an instance representing a string value. + explicit EncodableValue(const std::string& value) + : string_(new std::string(value)), type_(Type::kString) {} + + // Creates an instance representing a list of bytes. + explicit EncodableValue(std::vector list) + : byte_list_(new std::vector(std::move(list))), + type_(Type::kByteList) {} + + // Creates an instance representing a list of 32-bit integers. + explicit EncodableValue(std::vector list) + : int_list_(new std::vector(std::move(list))), + type_(Type::kIntList) {} + + // Creates an instance representing a list of 64-bit integers. + explicit EncodableValue(std::vector list) + : long_list_(new std::vector(std::move(list))), + type_(Type::kLongList) {} + + // Creates an instance representing a list of 64-bit floating point values. + explicit EncodableValue(std::vector list) + : double_list_(new std::vector(std::move(list))), + type_(Type::kDoubleList) {} + + // Creates an instance representing a list of EncodableValues. + explicit EncodableValue(EncodableList list) + : list_(new EncodableList(std::move(list))), type_(Type::kList) {} + + // Creates an instance representing a map from EncodableValues to + // EncodableValues. + explicit EncodableValue(EncodableMap map) + : map_(new EncodableMap(std::move(map))), type_(Type::kMap) {} + + // Convience constructor for creating default value of the given type. + // + // Collections types will be empty, numeric types will be 0, strings will be + // empty, and booleans will be false. For non-collection types, prefer using + // the value-based constructor with an explicit value for clarity. + explicit EncodableValue(Type type) : type_(type) { + switch (type_) { + case Type::kNull: + break; + case Type::kBool: + bool_ = false; + break; + case Type::kInt: + int_ = 0; + break; + case Type::kLong: + long_ = 0; + break; + case Type::kDouble: + double_ = 0.0; + break; + case Type::kString: + string_ = new std::string(); + break; + case Type::kByteList: + byte_list_ = new std::vector(); + break; + case Type::kIntList: + int_list_ = new std::vector(); + break; + case Type::kLongList: + long_list_ = new std::vector(); + break; + case Type::kDoubleList: + double_list_ = new std::vector(); + break; + case Type::kList: + list_ = new std::vector(); + break; + case Type::kMap: + map_ = new std::map(); + break; + } + } + + ~EncodableValue() { DestroyValue(); } + + EncodableValue(const EncodableValue& other) { + DestroyValue(); + + type_ = other.type_; + switch (type_) { + case Type::kNull: + break; + case Type::kBool: + bool_ = other.bool_; + break; + case Type::kInt: + int_ = other.int_; + break; + case Type::kLong: + long_ = other.long_; + break; + case Type::kDouble: + double_ = other.double_; + break; + case Type::kString: + string_ = new std::string(*other.string_); + break; + case Type::kByteList: + byte_list_ = new std::vector(*other.byte_list_); + break; + case Type::kIntList: + int_list_ = new std::vector(*other.int_list_); + break; + case Type::kLongList: + long_list_ = new std::vector(*other.long_list_); + break; + case Type::kDoubleList: + double_list_ = new std::vector(*other.double_list_); + break; + case Type::kList: + list_ = new std::vector(*other.list_); + break; + case Type::kMap: + map_ = new std::map(*other.map_); + break; + } + } + + EncodableValue(EncodableValue&& other) { *this = std::move(other); } + + EncodableValue& operator=(const EncodableValue& other) { + if (&other == this) { + return *this; + } + using std::swap; + EncodableValue temp(other); + swap(*this, temp); + return *this; + } + + EncodableValue& operator=(EncodableValue&& other) { + if (&other == this) { + return *this; + } + DestroyValue(); + + type_ = other.type_; + switch (type_) { + case Type::kNull: + break; + case Type::kBool: + bool_ = other.bool_; + break; + case Type::kInt: + int_ = other.int_; + break; + case Type::kLong: + long_ = other.long_; + break; + case Type::kDouble: + double_ = other.double_; + break; + case Type::kString: + string_ = other.string_; + break; + case Type::kByteList: + byte_list_ = other.byte_list_; + break; + case Type::kIntList: + int_list_ = other.int_list_; + break; + case Type::kLongList: + long_list_ = other.long_list_; + break; + case Type::kDoubleList: + double_list_ = other.double_list_; + break; + case Type::kList: + list_ = other.list_; + break; + case Type::kMap: + map_ = other.map_; + break; + } + // Ensure that destruction doesn't run on the source of the move. + other.type_ = Type::kNull; + return *this; + } + + // Allow assigning any value type that can be used for a constructor. + template + EncodableValue& operator=(const T& value) { + *this = EncodableValue(value); + return *this; + } + + // This operator exists only to provide a stable ordering for use as a + // std::map key. It does not attempt to provide useful ordering semantics. + // Notably: + // - Numeric values are not guaranteed any ordering across numeric types. + // E.g., 1 as a Long may sort after 100 as an Int. + // - Collection types use pointer equality, rather than value. This means that + // multiple collections with the same values will end up as separate keys + // in a map (consistent with default Dart Map behavior). + bool operator<(const EncodableValue& other) const { + if (type_ != other.type_) { + return type_ < other.type_; + } + switch (type_) { + case Type::kNull: + return false; + case Type::kBool: + return bool_ < other.bool_; + case Type::kInt: + return int_ < other.int_; + case Type::kLong: + return long_ < other.long_; + case Type::kDouble: + return double_ < other.double_; + case Type::kString: + return *string_ < *other.string_; + case Type::kByteList: + case Type::kIntList: + case Type::kLongList: + case Type::kDoubleList: + case Type::kList: + case Type::kMap: + return this < &other; + } + assert(false); + return false; + } + + // Returns the bool value this object represents. + // + // It is a programming error to call this unless IsBool() is true. + bool BoolValue() const { + assert(IsBool()); + return bool_; + } + + // Returns the 32-bit integer value this object represents. + // + // It is a programming error to call this unless IsInt() is true. + int32_t IntValue() const { + assert(IsInt()); + return int_; + } + + // Returns the 64-bit integer value this object represents. + // + // It is a programming error to call this unless IsLong() or IsInt() is true. + // + // Note that calling this function on an Int value is the only case where + // a *Value() function can be called without the corresponding Is*() being + // true. This is to simplify handling objects received from Flutter where the + // values may be larger than 32-bit, since they have the same type on the Dart + // side, but will be either 32-bit or 64-bit here depending on the value. + int64_t LongValue() const { + assert(IsLong() || IsInt()); + if (IsLong()) { + return long_; + } + return int_; + } + + // Returns the double value this object represents. + // + // It is a programming error to call this unless IsDouble() is true. + double DoubleValue() const { + assert(IsDouble()); + return double_; + } + + // Returns the string value this object represents. + // + // It is a programming error to call this unless IsString() is true. + const std::string& StringValue() const { + assert(IsString()); + return *string_; + } + + // Returns the byte list this object represents. + // + // It is a programming error to call this unless IsByteList() is true. + const std::vector& ByteListValue() const { + assert(IsByteList()); + return *byte_list_; + } + + // Returns the byte list this object represents. + // + // It is a programming error to call this unless IsByteList() is true. + std::vector& ByteListValue() { + assert(IsByteList()); + return *byte_list_; + } + + // Returns the 32-bit integer list this object represents. + // + // It is a programming error to call this unless IsIntList() is true. + const std::vector& IntListValue() const { + assert(IsIntList()); + return *int_list_; + } + + // Returns the 32-bit integer list this object represents. + // + // It is a programming error to call this unless IsIntList() is true. + std::vector& IntListValue() { + assert(IsIntList()); + return *int_list_; + } + + // Returns the 64-bit integer list this object represents. + // + // It is a programming error to call this unless IsLongList() is true. + const std::vector& LongListValue() const { + assert(IsLongList()); + return *long_list_; + } + + // Returns the 64-bit integer list this object represents. + // + // It is a programming error to call this unless IsLongList() is true. + std::vector& LongListValue() { + assert(IsLongList()); + return *long_list_; + } + + // Returns the double list this object represents. + // + // It is a programming error to call this unless IsDoubleList() is true. + const std::vector& DoubleListValue() const { + assert(IsDoubleList()); + return *double_list_; + } + + // Returns the double list this object represents. + // + // It is a programming error to call this unless IsDoubleList() is true. + std::vector& DoubleListValue() { + assert(IsDoubleList()); + return *double_list_; + } + + // Returns the list of EncodableValues this object represents. + // + // It is a programming error to call this unless IsList() is true. + const EncodableList& ListValue() const { + assert(IsList()); + return *list_; + } + + // Returns the list of EncodableValues this object represents. + // + // It is a programming error to call this unless IsList() is true. + EncodableList& ListValue() { + assert(IsList()); + return *list_; + } + + // Returns the map of EncodableValue : EncodableValue pairs this object + // represent. + // + // It is a programming error to call this unless IsMap() is true. + const EncodableMap& MapValue() const { + assert(IsMap()); + return *map_; + } + + // Returns the map of EncodableValue : EncodableValue pairs this object + // represent. + // + // It is a programming error to call this unless IsMap() is true. + EncodableMap& MapValue() { + assert(IsMap()); + return *map_; + } + + // Returns true if this represents a null value. + bool IsNull() const { return type_ == Type::kNull; } + + // Returns true if this represents a bool value. + bool IsBool() const { return type_ == Type::kBool; } + + // Returns true if this represents a 32-bit integer value. + bool IsInt() const { return type_ == Type::kInt; } + + // Returns true if this represents a 64-bit integer value. + bool IsLong() const { return type_ == Type::kLong; } + + // Returns true if this represents a double value. + bool IsDouble() const { return type_ == Type::kDouble; } + + // Returns true if this represents a string value. + bool IsString() const { return type_ == Type::kString; } + + // Returns true if this represents a list of bytes. + bool IsByteList() const { return type_ == Type::kByteList; } + + // Returns true if this represents a list of 32-bit integers. + bool IsIntList() const { return type_ == Type::kIntList; } + + // Returns true if this represents a list of 64-bit integers. + bool IsLongList() const { return type_ == Type::kLongList; } + + // Returns true if this represents a list of doubles. + bool IsDoubleList() const { return type_ == Type::kDoubleList; } + + // Returns true if this represents a list of EncodableValues. + bool IsList() const { return type_ == Type::kList; } + + // Returns true if this represents a map of EncodableValue : EncodableValue + // pairs. + bool IsMap() const { return type_ == Type::kMap; } + + // Returns the type this value represents. + // + // This is primarily intended for use with switch(); for individual checks, + // prefer an Is*() call. + Type type() const { return type_; } + + private: + // Performs any cleanup necessary for the active union value. This must be + // called before assigning a new value, and on object destruction. + // + // After calling this, type_ will alway be kNull. + void DestroyValue() { + switch (type_) { + case Type::kNull: + case Type::kBool: + case Type::kInt: + case Type::kLong: + case Type::kDouble: + break; + case Type::kString: + delete string_; + break; + case Type::kByteList: + delete byte_list_; + break; + case Type::kIntList: + delete int_list_; + break; + case Type::kLongList: + delete long_list_; + break; + case Type::kDoubleList: + delete double_list_; + break; + case Type::kList: + delete list_; + break; + case Type::kMap: + delete map_; + break; + } + + type_ = Type::kNull; + } + + // The anonymous union that stores the represented value. Accessing any of + // these entries other than the one that corresponds to the current value of + // |type_| has undefined behavior. + // + // Pointers are used for the non-POD types to avoid making the overall size + // of the union unnecessarily large. + // + // TODO: Replace this with std::variant once c++17 is available. + union { + bool bool_; + int32_t int_; + int64_t long_; + double double_; + std::string* string_; + std::vector* byte_list_; + std::vector* int_list_; + std::vector* long_list_; + std::vector* double_list_; + std::vector* list_; + std::map* map_; + }; + + // The currently active union entry. + Type type_ = Type::kNull; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_ENCODABLE_VALUE_H_ diff --git a/flutter/cpp_client_wrapper/include/flutter/engine_method_result.h b/flutter/cpp_client_wrapper/include/flutter/engine_method_result.h new file mode 100644 index 0000000..9c28a85 --- /dev/null +++ b/flutter/cpp_client_wrapper/include/flutter/engine_method_result.h @@ -0,0 +1,86 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_ENGINE_METHOD_RESULT_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_ENGINE_METHOD_RESULT_H_ + +#include +#include +#include + +#include "binary_messenger.h" +#include "method_codec.h" +#include "method_result.h" + +namespace flutter { + +namespace internal { +// Manages the one-time sending of response data. This is an internal helper +// class for EngineMethodResult, separated out since the implementation doesn't +// vary based on the template type. +class ReplyManager { + public: + ReplyManager(BinaryReply reply_handler_); + ~ReplyManager(); + + // Prevent copying. + ReplyManager(ReplyManager const&) = delete; + ReplyManager& operator=(ReplyManager const&) = delete; + + // Sends the given response data (which must either be nullptr, which + // indicates an unhandled method, or a response serialized with |codec_|) to + // the engine. + void SendResponseData(const std::vector* data); + + private: + BinaryReply reply_handler_; +}; +} // namespace internal + +// Implemention of MethodResult that sends a response to the Flutter engine +// exactly once, encoded using a given codec. +template +class EngineMethodResult : public MethodResult { + public: + // Creates a result object that will send results to |reply_handler|, encoded + // using |codec|. The |codec| pointer must remain valid for as long as this + // object exists. + EngineMethodResult(BinaryReply reply_handler, const MethodCodec* codec) + : reply_manager_( + std::make_unique(std::move(reply_handler))), + codec_(codec) {} + + ~EngineMethodResult() = default; + + protected: + // |flutter::MethodResult| + void SuccessInternal(const T* result) override { + std::unique_ptr> data = + codec_->EncodeSuccessEnvelope(result); + reply_manager_->SendResponseData(data.get()); + } + + // |flutter::MethodResult| + void ErrorInternal(const std::string& error_code, + const std::string& error_message, + const T* error_details) override { + std::unique_ptr> data = + codec_->EncodeErrorEnvelope(error_code, error_message, error_details); + reply_manager_->SendResponseData(data.get()); + } + + // |flutter::MethodResult| + void NotImplementedInternal() override { + reply_manager_->SendResponseData(nullptr); + } + + private: + std::unique_ptr reply_manager_; + + const MethodCodec* codec_; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_ENGINE_METHOD_RESULT_H_ diff --git a/flutter/cpp_client_wrapper/include/flutter/flutter_window.h b/flutter/cpp_client_wrapper/include/flutter/flutter_window.h new file mode 100644 index 0000000..a788bc7 --- /dev/null +++ b/flutter/cpp_client_wrapper/include/flutter/flutter_window.h @@ -0,0 +1,92 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_GLFW_CLIENT_WRAPPER_INCLUDE_FLUTTER_FLUTTER_WINDOW_H_ +#define FLUTTER_SHELL_PLATFORM_GLFW_CLIENT_WRAPPER_INCLUDE_FLUTTER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "plugin_registrar.h" + +namespace flutter { + +// A data type for window position and size. +struct WindowFrame { + int left; + int top; + int width; + int height; +}; + +// A window displaying Flutter content. +class FlutterWindow { + public: + explicit FlutterWindow(FlutterDesktopWindowRef window) : window_(window) {} + + ~FlutterWindow() = default; + + // Prevent copying. + FlutterWindow(FlutterWindow const&) = delete; + FlutterWindow& operator=(FlutterWindow const&) = delete; + + // Enables or disables hover tracking. + // + // If hover is enabled, mouse movement will send hover events to the Flutter + // engine, rather than only tracking the mouse while the button is pressed. + // Defaults to off. + void SetHoverEnabled(bool enabled) { + FlutterDesktopWindowSetHoverEnabled(window_, enabled); + } + + // Sets the displayed title of the window. + void SetTitle(const std::string& title) { + FlutterDesktopWindowSetTitle(window_, title.c_str()); + } + + // Sets the displayed icon for the window. + // + // The pixel format is 32-bit RGBA. The provided image data only needs to be + // valid for the duration of the call to this method. Pass a nullptr to revert + // to the default icon. + void SetIcon(uint8_t* pixel_data, int width, int height) { + FlutterDesktopWindowSetIcon(window_, pixel_data, width, height); + } + + // Returns the frame of the window, including any decoration (e.g., title + // bar), in screen coordinates. + WindowFrame GetFrame() { + WindowFrame frame = {}; + FlutterDesktopWindowGetFrame(window_, &frame.left, &frame.top, &frame.width, + &frame.height); + return frame; + } + + // Set the frame of the window, including any decoration (e.g., title + // bar), in screen coordinates. + void SetFrame(const WindowFrame& frame) { + FlutterDesktopWindowSetFrame(window_, frame.left, frame.top, frame.width, + frame.height); + } + + // Returns the number of pixels per screen coordinate for the window. + // + // Flutter uses pixel coordinates, so this is the ratio of positions and sizes + // seen by Flutter as compared to the screen. + double GetScaleFactor() { + return FlutterDesktopWindowGetScaleFactor(window_); + } + + private: + // Handle for interacting with the C API's window. + // + // Note: window_ is conceptually owned by the controller, not this object. + FlutterDesktopWindowRef window_; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_GLFW_CLIENT_WRAPPER_INCLUDE_FLUTTER_FLUTTER_WINDOW_H_ diff --git a/flutter/cpp_client_wrapper/include/flutter/flutter_window_controller.h b/flutter/cpp_client_wrapper/include/flutter/flutter_window_controller.h new file mode 100644 index 0000000..5c54b42 --- /dev/null +++ b/flutter/cpp_client_wrapper/include/flutter/flutter_window_controller.h @@ -0,0 +1,90 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_GLFW_CLIENT_WRAPPER_INCLUDE_FLUTTER_FLUTTER_WINDOW_CONTROLLER_H_ +#define FLUTTER_SHELL_PLATFORM_GLFW_CLIENT_WRAPPER_INCLUDE_FLUTTER_FLUTTER_WINDOW_CONTROLLER_H_ + +#include +#include +#include + +#include + +#include "flutter_window.h" +#include "plugin_registrar.h" + +namespace flutter { + +// A controller for a window displaying Flutter content. +// +// This is the primary wrapper class for the desktop C API. +// If you use this class, you should not call any of the setup or teardown +// methods in the C API directly, as this class will do that internally. +// +// Note: This is an early implementation (using GLFW internally) which +// requires control of the application's event loop, and is thus useful +// primarily for building a simple one-window shell hosting a Flutter +// application. The final implementation and API will be very different. +class FlutterWindowController { + public: + // There must be only one instance of this class in an application at any + // given time, as Flutter does not support multiple engines in one process, + // or multiple views in one engine. + explicit FlutterWindowController(const std::string& icu_data_path); + + ~FlutterWindowController(); + + // Prevent copying. + FlutterWindowController(FlutterWindowController const&) = delete; + FlutterWindowController& operator=(FlutterWindowController const&) = delete; + + // Creates and displays a window for displaying Flutter content. + // + // The |assets_path| is the path to the flutter_assets folder for the Flutter + // application to be run. |icu_data_path| is the path to the icudtl.dat file + // for the version of Flutter you are using. + // + // The |arguments| are passed to the Flutter engine. See: + // https://github.com/flutter/engine/blob/master/shell/common/switches.h for + // for details. Not all arguments will apply to desktop. + // + // Only one Flutter window can exist at a time; see constructor comment. + bool CreateWindow(int width, + int height, + const std::string& title, + const std::string& assets_path, + const std::vector& arguments); + + // Returns the FlutterDesktopPluginRegistrarRef to register a plugin with the + // given name. + // + // The name must be unique across the application. + FlutterDesktopPluginRegistrarRef GetRegistrarForPlugin( + const std::string& plugin_name); + + // The FlutterWindow managed by this controller, if any. Returns nullptr + // before CreateWindow is called, and after RunEventLoop returns; + FlutterWindow* window() { return window_.get(); } + + // Loops on Flutter window events until the window closes. + void RunEventLoop(); + + private: + // The path to the ICU data file. Set at creation time since it is the same + // for any window created. + std::string icu_data_path_; + + // Whether or not FlutterDesktopInit succeeded at creation time. + bool init_succeeded_ = false; + + // The owned FlutterWindow, if any. + std::unique_ptr window_; + + // Handle for interacting with the C API's window controller, if any. + FlutterDesktopWindowControllerRef controller_ = nullptr; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_GLFW_CLIENT_WRAPPER_INCLUDE_FLUTTER_FLUTTER_WINDOW_CONTROLLER_H_ diff --git a/flutter/cpp_client_wrapper/include/flutter/json_message_codec.h b/flutter/cpp_client_wrapper/include/flutter/json_message_codec.h new file mode 100644 index 0000000..ba1aa2f --- /dev/null +++ b/flutter/cpp_client_wrapper/include/flutter/json_message_codec.h @@ -0,0 +1,42 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_MESSAGE_CODEC_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_MESSAGE_CODEC_H_ + +#include "json_type.h" +#include "message_codec.h" + +namespace flutter { + +// A message encoding/decoding mechanism for communications to/from the +// Flutter engine via JSON channels. +class JsonMessageCodec : public MessageCodec { + public: + // Returns the shared instance of the codec. + static const JsonMessageCodec& GetInstance(); + + ~JsonMessageCodec() = default; + + // Prevent copying. + JsonMessageCodec(JsonMessageCodec const&) = delete; + JsonMessageCodec& operator=(JsonMessageCodec const&) = delete; + + protected: + // Instances should be obtained via GetInstance. + JsonMessageCodec() = default; + + // |flutter::MessageCodec| + std::unique_ptr DecodeMessageInternal( + const uint8_t* binary_message, + const size_t message_size) const override; + + // |flutter::MessageCodec| + std::unique_ptr> EncodeMessageInternal( + const JsonValueType& message) const override; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_MESSAGE_CODEC_H_ diff --git a/flutter/cpp_client_wrapper/include/flutter/json_method_codec.h b/flutter/cpp_client_wrapper/include/flutter/json_method_codec.h new file mode 100644 index 0000000..92af820 --- /dev/null +++ b/flutter/cpp_client_wrapper/include/flutter/json_method_codec.h @@ -0,0 +1,52 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_METHOD_CODEC_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_METHOD_CODEC_H_ + +#include "json_type.h" +#include "method_call.h" +#include "method_codec.h" + +namespace flutter { + +// An implementation of MethodCodec that uses JSON strings as the serialization. +class JsonMethodCodec : public MethodCodec { + public: + // Returns the shared instance of the codec. + static const JsonMethodCodec& GetInstance(); + + ~JsonMethodCodec() = default; + + // Prevent copying. + JsonMethodCodec(JsonMethodCodec const&) = delete; + JsonMethodCodec& operator=(JsonMethodCodec const&) = delete; + + protected: + // Instances should be obtained via GetInstance. + JsonMethodCodec() = default; + + // |flutter::MethodCodec| + std::unique_ptr> DecodeMethodCallInternal( + const uint8_t* message, + const size_t message_size) const override; + + // |flutter::MethodCodec| + std::unique_ptr> EncodeMethodCallInternal( + const MethodCall& method_call) const override; + + // |flutter::MethodCodec| + std::unique_ptr> EncodeSuccessEnvelopeInternal( + const JsonValueType* result) const override; + + // |flutter::MethodCodec| + std::unique_ptr> EncodeErrorEnvelopeInternal( + const std::string& error_code, + const std::string& error_message, + const JsonValueType* error_details) const override; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_METHOD_CODEC_H_ diff --git a/flutter/cpp_client_wrapper/include/flutter/json_type.h b/flutter/cpp_client_wrapper/include/flutter/json_type.h new file mode 100644 index 0000000..476cee7 --- /dev/null +++ b/flutter/cpp_client_wrapper/include/flutter/json_type.h @@ -0,0 +1,26 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_TYPE_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_TYPE_H_ + +// By default, the Json codecs use jsoncpp, but a version using RapidJSON is +// implemented as well. To use the latter, set USE_RAPID_JSON. +// +// When writing code using the JSON codec classes, do not use JsonValueType; +// instead use the underlying type for the library you have selected directly. + +#ifdef USE_RAPID_JSON +#include + +// The APIs often pass owning references, which in RapidJSON must include the +// allocator, so the value type for the APIs is Document rather than Value. +using JsonValueType = rapidjson::Document; +#else +#include + +using JsonValueType = Json::Value; +#endif + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_TYPE_H_ \ No newline at end of file diff --git a/flutter/cpp_client_wrapper/include/flutter/message_codec.h b/flutter/cpp_client_wrapper/include/flutter/message_codec.h new file mode 100644 index 0000000..386d0d0 --- /dev/null +++ b/flutter/cpp_client_wrapper/include/flutter/message_codec.h @@ -0,0 +1,62 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_MESSAGE_CODEC_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_MESSAGE_CODEC_H_ + +#include +#include +#include + +namespace flutter { + +// Translates between a binary message and higher-level method call and +// response/error objects. +template +class MessageCodec { + public: + MessageCodec() = default; + + virtual ~MessageCodec() = default; + + // Prevent copying. + MessageCodec(MessageCodec const&) = delete; + MessageCodec& operator=(MessageCodec const&) = delete; + + // Returns the message encoded in |binary_message|, or nullptr if it cannot be + // decoded by this codec. + std::unique_ptr DecodeMessage(const uint8_t* binary_message, + const size_t message_size) const { + return std::move(DecodeMessageInternal(binary_message, message_size)); + } + + // Returns the message encoded in |binary_message|, or nullptr if it cannot be + // decoded by this codec. + std::unique_ptr DecodeMessage( + const std::vector& binary_message) const { + size_t size = binary_message.size(); + const uint8_t* data = size > 0 ? &binary_message[0] : nullptr; + return std::move(DecodeMessageInternal(data, size)); + } + + // Returns a binary encoding of the given |message|, or nullptr if the + // message cannot be serialized by this codec. + std::unique_ptr> EncodeMessage(const T& message) const { + return std::move(EncodeMessageInternal(message)); + } + + protected: + // Implementation of the public interface, to be provided by subclasses. + virtual std::unique_ptr DecodeMessageInternal( + const uint8_t* binary_message, + const size_t message_size) const = 0; + + // Implementation of the public interface, to be provided by subclasses. + virtual std::unique_ptr> EncodeMessageInternal( + const T& message) const = 0; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_MESSAGE_CODEC_H_ diff --git a/flutter/cpp_client_wrapper/include/flutter/method_call.h b/flutter/cpp_client_wrapper/include/flutter/method_call.h new file mode 100644 index 0000000..80274e7 --- /dev/null +++ b/flutter/cpp_client_wrapper/include/flutter/method_call.h @@ -0,0 +1,41 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_TYPED_METHOD_CALL_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_TYPED_METHOD_CALL_H_ + +#include +#include + +namespace flutter { + +// An object encapsulating a method call from Flutter whose arguments are of +// type T. +template +class MethodCall { + public: + // Creates a MethodCall with the given name and arguments. + MethodCall(const std::string& method_name, std::unique_ptr arguments) + : method_name_(method_name), arguments_(std::move(arguments)) {} + + virtual ~MethodCall() = default; + + // Prevent copying. + MethodCall(MethodCall const&) = delete; + MethodCall& operator=(MethodCall const&) = delete; + + // The name of the method being called. + const std::string& method_name() const { return method_name_; } + + // The arguments to the method call, or NULL if there are none. + const T* arguments() const { return arguments_.get(); } + + private: + std::string method_name_; + std::unique_ptr arguments_; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_TYPED_METHOD_CALL_H_ diff --git a/flutter/cpp_client_wrapper/include/flutter/method_channel.h b/flutter/cpp_client_wrapper/include/flutter/method_channel.h new file mode 100644 index 0000000..e1f1640 --- /dev/null +++ b/flutter/cpp_client_wrapper/include/flutter/method_channel.h @@ -0,0 +1,90 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_METHOD_CHANNEL_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_METHOD_CHANNEL_H_ + +#include +#include + +#include "binary_messenger.h" +#include "engine_method_result.h" +#include "method_call.h" +#include "method_codec.h" +#include "method_result.h" + +namespace flutter { + +// A handler for receiving a method call from the Flutter engine. +// +// Implementations must asynchronously call exactly one of the methods on +// |result| to indicate the result of the method call. +template +using MethodCallHandler = + std::function& call, + std::unique_ptr> result)>; + +// A channel for communicating with the Flutter engine using invocation of +// asynchronous methods. +template +class MethodChannel { + public: + // Creates an instance that sends and receives method calls on the channel + // named |name|, encoded with |codec| and dispatched via |messenger|. + MethodChannel(BinaryMessenger* messenger, + const std::string& name, + const MethodCodec* codec) + : messenger_(messenger), name_(name), codec_(codec) {} + + ~MethodChannel() = default; + + // Prevent copying. + MethodChannel(MethodChannel const&) = delete; + MethodChannel& operator=(MethodChannel const&) = delete; + + // Sends a message to the Flutter engine on this channel. + void InvokeMethod(const std::string& method, std::unique_ptr arguments) { + MethodCall method_call(method, std::move(arguments)); + std::unique_ptr> message = + codec_->EncodeMethodCall(method_call); + messenger_->Send(name_, message->data(), message->size()); + } + + // TODO: Add support for a version of InvokeMethod expecting a reply once + // https://github.com/flutter/flutter/issues/18852 is fixed. + + // Registers a handler that should be called any time a method call is + // received on this channel. + void SetMethodCallHandler(MethodCallHandler handler) const { + const auto* codec = codec_; + std::string channel_name = name_; + BinaryMessageHandler binary_handler = [handler, codec, channel_name]( + const uint8_t* message, + const size_t message_size, + BinaryReply reply) { + // Use this channel's codec to decode the call and build a result handler. + auto result = + std::make_unique>(std::move(reply), codec); + std::unique_ptr> method_call = + codec->DecodeMethodCall(message, message_size); + if (!method_call) { + std::cerr << "Unable to construct method call from message on channel " + << channel_name << std::endl; + result->NotImplemented(); + return; + } + handler(*method_call, std::move(result)); + }; + messenger_->SetMessageHandler(name_, std::move(binary_handler)); + } + + private: + BinaryMessenger* messenger_; + std::string name_; + const MethodCodec* codec_; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_METHOD_CHANNEL_H_ diff --git a/flutter/cpp_client_wrapper/include/flutter/method_codec.h b/flutter/cpp_client_wrapper/include/flutter/method_codec.h new file mode 100644 index 0000000..6f9e09a --- /dev/null +++ b/flutter/cpp_client_wrapper/include/flutter/method_codec.h @@ -0,0 +1,93 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_METHOD_CODEC_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_METHOD_CODEC_H_ + +#include +#include +#include + +#include "method_call.h" + +namespace flutter { + +// Translates between a binary message and higher-level method call and +// response/error objects. +template +class MethodCodec { + public: + MethodCodec() = default; + + virtual ~MethodCodec() = default; + + // Prevent copying. + MethodCodec(MethodCodec const&) = delete; + MethodCodec& operator=(MethodCodec const&) = delete; + + // Returns the MethodCall encoded in |message|, or nullptr if it cannot be + // decoded. + std::unique_ptr> DecodeMethodCall( + const uint8_t* message, + const size_t message_size) const { + return std::move(DecodeMethodCallInternal(message, message_size)); + } + + // Returns the MethodCall encoded in |message|, or nullptr if it cannot be + // decoded. + std::unique_ptr> DecodeMethodCall( + const std::vector& message) const { + size_t size = message.size(); + const uint8_t* data = size > 0 ? &message[0] : nullptr; + return std::move(DecodeMethodCallInternal(data, size)); + } + + // Returns a binary encoding of the given |method_call|, or nullptr if the + // method call cannot be serialized by this codec. + std::unique_ptr> EncodeMethodCall( + const MethodCall& method_call) const { + return std::move(EncodeMethodCallInternal(method_call)); + } + + // Returns a binary encoding of |result|. |result| must be a type supported + // by the codec. + std::unique_ptr> EncodeSuccessEnvelope( + const T* result = nullptr) const { + return std::move(EncodeSuccessEnvelopeInternal(result)); + } + + // Returns a binary encoding of |error|. The |error_details| must be a type + // supported by the codec. + std::unique_ptr> EncodeErrorEnvelope( + const std::string& error_code, + const std::string& error_message = "", + const T* error_details = nullptr) const { + return std::move( + EncodeErrorEnvelopeInternal(error_code, error_message, error_details)); + } + + protected: + // Implementation of the public interface, to be provided by subclasses. + virtual std::unique_ptr> DecodeMethodCallInternal( + const uint8_t* message, + const size_t message_size) const = 0; + + // Implementation of the public interface, to be provided by subclasses. + virtual std::unique_ptr> EncodeMethodCallInternal( + const MethodCall& method_call) const = 0; + + // Implementation of the public interface, to be provided by subclasses. + virtual std::unique_ptr> EncodeSuccessEnvelopeInternal( + const T* result) const = 0; + + // Implementation of the public interface, to be provided by subclasses. + virtual std::unique_ptr> EncodeErrorEnvelopeInternal( + const std::string& error_code, + const std::string& error_message, + const T* error_details) const = 0; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_METHOD_CODEC_H_ diff --git a/flutter/cpp_client_wrapper/include/flutter/method_result.h b/flutter/cpp_client_wrapper/include/flutter/method_result.h new file mode 100644 index 0000000..e3bf572 --- /dev/null +++ b/flutter/cpp_client_wrapper/include/flutter/method_result.h @@ -0,0 +1,58 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_METHOD_RESULT_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_METHOD_RESULT_H_ + +#include + +namespace flutter { + +// Encapsulates a result sent back to the Flutter engine in response to a +// MethodCall. Only one method should be called on any given instance. +template +class MethodResult { + public: + MethodResult() = default; + + virtual ~MethodResult() = default; + + // Prevent copying. + MethodResult(MethodResult const&) = delete; + MethodResult& operator=(MethodResult const&) = delete; + + // Sends a success response, indicating that the call completed successfully. + // An optional value can be provided as part of the success message. + void Success(const T* result = nullptr) { SuccessInternal(result); } + + // Sends an error response, indicating that the call was understood but + // handling failed in some way. A string error code must be provided, and in + // addition an optional user-readable error_message and/or details object can + // be included. + void Error(const std::string& error_code, + const std::string& error_message = "", + const T* error_details = nullptr) { + ErrorInternal(error_code, error_message, error_details); + } + + // Sends a not-implemented response, indicating that the method either was not + // recognized, or has not been implemented. + void NotImplemented() { NotImplementedInternal(); } + + protected: + // Implementation of the public interface, to be provided by subclasses. + virtual void SuccessInternal(const T* result) = 0; + + // Implementation of the public interface, to be provided by subclasses. + virtual void ErrorInternal(const std::string& error_code, + const std::string& error_message, + const T* error_details) = 0; + + // Implementation of the public interface, to be provided by subclasses. + virtual void NotImplementedInternal() = 0; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_METHOD_RESULT_H_ diff --git a/flutter/cpp_client_wrapper/include/flutter/plugin_registrar.h b/flutter/cpp_client_wrapper/include/flutter/plugin_registrar.h new file mode 100644 index 0000000..5fbe1b5 --- /dev/null +++ b/flutter/cpp_client_wrapper/include/flutter/plugin_registrar.h @@ -0,0 +1,74 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_H_ + +#include +#include +#include + +#include + +#include "binary_messenger.h" + +namespace flutter { + +class Plugin; + +// A object managing the registration of a plugin for various events. +// +// Currently this class has very limited functionality, but is expected to +// expand over time to more closely match the functionality of +// the Flutter mobile plugin APIs' plugin registrars. +class PluginRegistrar { + public: + // Creates a new PluginRegistrar. |core_registrar| and the messenger it + // provides must remain valid as long as this object exists. + explicit PluginRegistrar(FlutterDesktopPluginRegistrarRef core_registrar); + + virtual ~PluginRegistrar(); + + // Prevent copying. + PluginRegistrar(PluginRegistrar const&) = delete; + PluginRegistrar& operator=(PluginRegistrar const&) = delete; + + // Returns the messenger to use for creating channels to communicate with the + // Flutter engine. + // + // This pointer will remain valid for the lifetime of this instance. + BinaryMessenger* messenger() { return messenger_.get(); } + + // Takes ownership of |plugin|. + // + // Plugins are not required to call this method if they have other lifetime + // management, but this is a convient place for plugins to be owned to ensure + // that they stay valid for any registered callbacks. + void AddPlugin(std::unique_ptr plugin); + + // Enables input blocking on the given channel name. + // + // If set, then the parent window should disable input callbacks + // while waiting for the handler for messages on that channel to run. + void EnableInputBlockingForChannel(const std::string& channel); + + private: + // Handle for interacting with the C API's registrar. + FlutterDesktopPluginRegistrarRef registrar_; + + std::unique_ptr messenger_; + + // Plugins registered for ownership. + std::set> plugins_; +}; + +// A plugin that can be registered for ownership by a PluginRegistrar. +class Plugin { + public: + virtual ~Plugin() = default; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_H_ diff --git a/flutter/cpp_client_wrapper/include/flutter/plugin_registrar_glfw.h b/flutter/cpp_client_wrapper/include/flutter/plugin_registrar_glfw.h new file mode 100644 index 0000000..a2a9da9 --- /dev/null +++ b/flutter/cpp_client_wrapper/include/flutter/plugin_registrar_glfw.h @@ -0,0 +1,44 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_GLFW_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_GLFW_H_ +#define FLUTTER_SHELL_PLATFORM_GLFW_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_GLFW_H_ + +#include + +#include + +#include "flutter_window.h" +#include "plugin_registrar.h" + +namespace flutter { + +// An extension to PluginRegistrar providing access to GLFW-shell-specific +// functionality. +class PluginRegistrarGlfw : public PluginRegistrar { + public: + // Creates a new PluginRegistrar. |core_registrar| and the messenger it + // provides must remain valid as long as this object exists. + explicit PluginRegistrarGlfw(FlutterDesktopPluginRegistrarRef core_registrar) + : PluginRegistrar(core_registrar) { + window_ = std::make_unique( + FlutterDesktopRegistrarGetWindow(core_registrar)); + } + + virtual ~PluginRegistrarGlfw() = default; + + // Prevent copying. + PluginRegistrarGlfw(PluginRegistrarGlfw const&) = delete; + PluginRegistrarGlfw& operator=(PluginRegistrarGlfw const&) = delete; + + FlutterWindow* window() { return window_.get(); } + + private: + // The owned FlutterWindow, if any. + std::unique_ptr window_; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_GLFW_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_GLFW_H_ diff --git a/flutter/cpp_client_wrapper/include/flutter/standard_message_codec.h b/flutter/cpp_client_wrapper/include/flutter/standard_message_codec.h new file mode 100644 index 0000000..75644c7 --- /dev/null +++ b/flutter/cpp_client_wrapper/include/flutter/standard_message_codec.h @@ -0,0 +1,42 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_STANDARD_MESSAGE_CODEC_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_STANDARD_MESSAGE_CODEC_H_ + +#include "encodable_value.h" +#include "message_codec.h" + +namespace flutter { + +// A binary message encoding/decoding mechanism for communications to/from the +// Flutter engine via message channels. +class StandardMessageCodec : public MessageCodec { + public: + // Returns the shared instance of the codec. + static const StandardMessageCodec& GetInstance(); + + ~StandardMessageCodec(); + + // Prevent copying. + StandardMessageCodec(StandardMessageCodec const&) = delete; + StandardMessageCodec& operator=(StandardMessageCodec const&) = delete; + + protected: + // Instances should be obtained via GetInstance. + StandardMessageCodec(); + + // |flutter::MessageCodec| + std::unique_ptr DecodeMessageInternal( + const uint8_t* binary_message, + const size_t message_size) const override; + + // |flutter::MessageCodec| + std::unique_ptr> EncodeMessageInternal( + const EncodableValue& message) const override; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_STANDARD_MESSAGE_CODEC_H_ diff --git a/flutter/cpp_client_wrapper/include/flutter/standard_method_codec.h b/flutter/cpp_client_wrapper/include/flutter/standard_method_codec.h new file mode 100644 index 0000000..3b47411 --- /dev/null +++ b/flutter/cpp_client_wrapper/include/flutter/standard_method_codec.h @@ -0,0 +1,52 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_STANDARD_METHOD_CODEC_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_STANDARD_METHOD_CODEC_H_ + +#include "encodable_value.h" +#include "method_call.h" +#include "method_codec.h" + +namespace flutter { + +// An implementation of MethodCodec that uses a binary serialization. +class StandardMethodCodec : public MethodCodec { + public: + // Returns the shared instance of the codec. + static const StandardMethodCodec& GetInstance(); + + ~StandardMethodCodec() = default; + + // Prevent copying. + StandardMethodCodec(StandardMethodCodec const&) = delete; + StandardMethodCodec& operator=(StandardMethodCodec const&) = delete; + + protected: + // Instances should be obtained via GetInstance. + StandardMethodCodec() = default; + + // |flutter::MethodCodec| + std::unique_ptr> DecodeMethodCallInternal( + const uint8_t* message, + const size_t message_size) const override; + + // |flutter::MethodCodec| + std::unique_ptr> EncodeMethodCallInternal( + const MethodCall& method_call) const override; + + // |flutter::MethodCodec| + std::unique_ptr> EncodeSuccessEnvelopeInternal( + const EncodableValue* result) const override; + + // |flutter::MethodCodec| + std::unique_ptr> EncodeErrorEnvelopeInternal( + const std::string& error_code, + const std::string& error_message, + const EncodableValue* error_details) const override; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_STANDARD_METHOD_CODEC_H_ diff --git a/flutter/cpp_client_wrapper/json_message_codec.cc b/flutter/cpp_client_wrapper/json_message_codec.cc new file mode 100644 index 0000000..5610c47 --- /dev/null +++ b/flutter/cpp_client_wrapper/json_message_codec.cc @@ -0,0 +1,70 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "include/flutter/json_message_codec.h" + +#include +#include + +#ifdef USE_RAPID_JSON +#include "rapidjson/error/en.h" +#include "rapidjson/stringbuffer.h" +#include "rapidjson/writer.h" +#endif + +namespace flutter { + +// static +const JsonMessageCodec& JsonMessageCodec::GetInstance() { + static JsonMessageCodec sInstance; + return sInstance; +} + +std::unique_ptr> JsonMessageCodec::EncodeMessageInternal( + const JsonValueType& message) const { +#ifdef USE_RAPID_JSON + // TODO: Look into alternate writers that would avoid the buffer copy. + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + message.Accept(writer); + const char* buffer_start = buffer.GetString(); + return std::make_unique>( + buffer_start, buffer_start + buffer.GetSize()); +#else + Json::StreamWriterBuilder writer_builder; + std::string serialization = Json::writeString(writer_builder, message); + return std::make_unique>(serialization.begin(), + serialization.end()); +#endif +} + +std::unique_ptr JsonMessageCodec::DecodeMessageInternal( + const uint8_t* binary_message, + const size_t message_size) const { + auto raw_message = reinterpret_cast(binary_message); + auto json_message = std::make_unique(); + std::string parse_errors; + bool parsing_successful = false; +#ifdef USE_RAPID_JSON + rapidjson::ParseResult result = + json_message->Parse(raw_message, message_size); + parsing_successful = result == rapidjson::ParseErrorCode::kParseErrorNone; + if (!parsing_successful) { + parse_errors = rapidjson::GetParseError_En(result.Code()); + } +#else + Json::CharReaderBuilder reader_builder; + std::unique_ptr parser(reader_builder.newCharReader()); + parsing_successful = parser->parse(raw_message, raw_message + message_size, + json_message.get(), &parse_errors); +#endif + if (!parsing_successful) { + std::cerr << "Unable to parse JSON message:" << std::endl + << parse_errors << std::endl; + return nullptr; + } + return json_message; +} + +} // namespace flutter diff --git a/flutter/cpp_client_wrapper/json_method_codec.cc b/flutter/cpp_client_wrapper/json_method_codec.cc new file mode 100644 index 0000000..6c9528f --- /dev/null +++ b/flutter/cpp_client_wrapper/json_method_codec.cc @@ -0,0 +1,140 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "include/flutter/json_method_codec.h" + +#include "include/flutter/json_message_codec.h" + +namespace flutter { + +namespace { +// Keys used in MethodCall encoding. +constexpr char kMessageMethodKey[] = "method"; +constexpr char kMessageArgumentsKey[] = "args"; +} // namespace + +// static +const JsonMethodCodec& JsonMethodCodec::GetInstance() { + static JsonMethodCodec sInstance; + return sInstance; +} + +std::unique_ptr> +JsonMethodCodec::DecodeMethodCallInternal(const uint8_t* message, + const size_t message_size) const { + std::unique_ptr json_message = + JsonMessageCodec::GetInstance().DecodeMessage(message, message_size); + if (!json_message) { + return nullptr; + } + +#if USE_RAPID_JSON + auto method_name_iter = json_message->FindMember(kMessageMethodKey); + if (method_name_iter == json_message->MemberEnd()) { + return nullptr; + } + if (!method_name_iter->value.IsString()) { + return nullptr; + } + std::string method_name(method_name_iter->value.GetString()); + auto arguments_iter = json_message->FindMember(kMessageArgumentsKey); + std::unique_ptr arguments; + if (arguments_iter != json_message->MemberEnd()) { + // Pull the arguments subtree up to the root of json_message. This is + // destructive to json_message, but the full value is no longer needed, and + // this avoids a subtree copy. + // Note: The static_cast is for compatibility with RapidJSON 1.1; master + // already allows swapping a Document with a Value directly. Once there is + // a new RapidJSON release (at which point clients can be expected to have + // that change in the version they depend on) remove the cast. + static_cast(json_message.get()) + ->Swap(arguments_iter->value); + // Swap it into |arguments|. This moves the allocator ownership, so that + // the data won't be deleted when json_message goes out of scope. + arguments = std::make_unique(); + arguments->Swap(*json_message); + } + return std::make_unique>( + method_name, std::move(arguments)); +#else + Json::Value method = (*json_message)[kMessageMethodKey]; + if (method.isNull()) { + return nullptr; + } + return std::make_unique>( + method.asString(), + std::make_unique((*json_message)[kMessageArgumentsKey])); +#endif +} + +std::unique_ptr> JsonMethodCodec::EncodeMethodCallInternal( + const MethodCall& method_call) const { +#if USE_RAPID_JSON + // TODO: Consider revisiting the codec APIs to avoid the need to copy + // everything when doing encoding (e.g., by having a version that takes + // owership of the object to encode, so that it can be moved instead). + rapidjson::Document message(rapidjson::kObjectType); + auto& allocator = message.GetAllocator(); + rapidjson::Value name(method_call.method_name(), allocator); + rapidjson::Value arguments; + if (method_call.arguments()) { + arguments.CopyFrom(*method_call.arguments(), allocator); + } + message.AddMember(kMessageMethodKey, name, allocator); + message.AddMember(kMessageArgumentsKey, arguments, allocator); +#else + Json::Value message(Json::objectValue); + message[kMessageMethodKey] = method_call.method_name(); + const Json::Value* arguments = method_call.arguments(); + message[kMessageArgumentsKey] = arguments ? *arguments : Json::Value(); +#endif + + return JsonMessageCodec::GetInstance().EncodeMessage(message); +} + +std::unique_ptr> +JsonMethodCodec::EncodeSuccessEnvelopeInternal( + const JsonValueType* result) const { +#if USE_RAPID_JSON + rapidjson::Document envelope; + envelope.SetArray(); + rapidjson::Value result_value; + if (result) { + result_value.CopyFrom(*result, envelope.GetAllocator()); + } + envelope.PushBack(result_value, envelope.GetAllocator()); +#else + Json::Value envelope(Json::arrayValue); + envelope.append(result == nullptr ? Json::Value() : *result); +#endif + + return JsonMessageCodec::GetInstance().EncodeMessage(envelope); +} + +std::unique_ptr> +JsonMethodCodec::EncodeErrorEnvelopeInternal( + const std::string& error_code, + const std::string& error_message, + const JsonValueType* error_details) const { +#if USE_RAPID_JSON + rapidjson::Document envelope(rapidjson::kArrayType); + auto& allocator = envelope.GetAllocator(); + envelope.PushBack(rapidjson::Value(error_code, allocator), allocator); + envelope.PushBack(rapidjson::Value(error_message, allocator), allocator); + rapidjson::Value details_value; + if (error_details) { + details_value.CopyFrom(*error_details, allocator); + } + envelope.PushBack(details_value, allocator); +#else + Json::Value envelope(Json::arrayValue); + envelope.append(error_code); + envelope.append(error_message.empty() ? Json::Value() : error_message); + envelope.append(error_details == nullptr ? Json::Value() : *error_details); +#endif + + return JsonMessageCodec::GetInstance().EncodeMessage(envelope); +} + +} // namespace flutter diff --git a/flutter/cpp_client_wrapper/plugin_registrar.cc b/flutter/cpp_client_wrapper/plugin_registrar.cc new file mode 100644 index 0000000..46c1173 --- /dev/null +++ b/flutter/cpp_client_wrapper/plugin_registrar.cc @@ -0,0 +1,125 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "include/flutter/plugin_registrar.h" + +#include "include/flutter/engine_method_result.h" +#include "include/flutter/method_channel.h" + +#include +#include + +namespace flutter { + +namespace { + +// Passes |message| to |user_data|, which must be a BinaryMessageHandler, along +// with a BinaryReply that will send a response on |message|'s response handle. +// +// This serves as an adaptor between the function-pointer-based message callback +// interface provided by the C API and the std::function-based message handler +// interface of BinaryMessenger. +void ForwardToHandler(FlutterDesktopMessengerRef messenger, + const FlutterDesktopMessage* message, + void* user_data) { + auto* response_handle = message->response_handle; + BinaryReply reply_handler = [messenger, response_handle]( + const uint8_t* reply, + const size_t reply_size) mutable { + if (!response_handle) { + std::cerr << "Error: Response can be set only once. Ignoring " + "duplicate response." + << std::endl; + return; + } + FlutterDesktopMessengerSendResponse(messenger, response_handle, reply, + reply_size); + // The engine frees the response handle once + // FlutterDesktopSendMessageResponse is called. + response_handle = nullptr; + }; + + const BinaryMessageHandler& message_handler = + *static_cast(user_data); + + message_handler(message->message, message->message_size, + std::move(reply_handler)); +} + +} // namespace + +// Wrapper around a FlutterDesktopMessengerRef that implements the +// BinaryMessenger API. +class BinaryMessengerImpl : public BinaryMessenger { + public: + explicit BinaryMessengerImpl(FlutterDesktopMessengerRef core_messenger) + : messenger_(core_messenger) {} + + virtual ~BinaryMessengerImpl() = default; + + // Prevent copying. + BinaryMessengerImpl(BinaryMessengerImpl const&) = delete; + BinaryMessengerImpl& operator=(BinaryMessengerImpl const&) = delete; + + // |flutter::BinaryMessenger| + void Send(const std::string& channel, + const uint8_t* message, + const size_t message_size) const override; + + // |flutter::BinaryMessenger| + void SetMessageHandler(const std::string& channel, + BinaryMessageHandler handler) override; + + private: + // Handle for interacting with the C API. + FlutterDesktopMessengerRef messenger_; + + // A map from channel names to the BinaryMessageHandler that should be called + // for incoming messages on that channel. + std::map handlers_; +}; + +void BinaryMessengerImpl::Send(const std::string& channel, + const uint8_t* message, + const size_t message_size) const { + FlutterDesktopMessengerSend(messenger_, channel.c_str(), message, + message_size); +} + +void BinaryMessengerImpl::SetMessageHandler(const std::string& channel, + BinaryMessageHandler handler) { + if (!handler) { + handlers_.erase(channel); + FlutterDesktopMessengerSetCallback(messenger_, channel.c_str(), nullptr, + nullptr); + return; + } + // Save the handler, to keep it alive. + handlers_[channel] = std::move(handler); + BinaryMessageHandler* message_handler = &handlers_[channel]; + // Set an adaptor callback that will invoke the handler. + FlutterDesktopMessengerSetCallback(messenger_, channel.c_str(), + ForwardToHandler, message_handler); +} + +// PluginRegistrar: + +PluginRegistrar::PluginRegistrar(FlutterDesktopPluginRegistrarRef registrar) + : registrar_(registrar) { + auto core_messenger = FlutterDesktopRegistrarGetMessenger(registrar_); + messenger_ = std::make_unique(core_messenger); +} + +PluginRegistrar::~PluginRegistrar() {} + +void PluginRegistrar::AddPlugin(std::unique_ptr plugin) { + plugins_.insert(std::move(plugin)); +} + +void PluginRegistrar::EnableInputBlockingForChannel( + const std::string& channel) { + FlutterDesktopRegistrarEnableInputBlocking(registrar_, channel.c_str()); +} + +} // namespace flutter diff --git a/flutter/cpp_client_wrapper/standard_codec.cc b/flutter/cpp_client_wrapper/standard_codec.cc new file mode 100644 index 0000000..6fbb63e --- /dev/null +++ b/flutter/cpp_client_wrapper/standard_codec.cc @@ -0,0 +1,378 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains what would normally be standard_codec_serializer.cc, +// standard_message_codec.cc, and standard_method_codec.cc. They are grouped +// together to simplify use of the client wrapper, since the common case is +// that any client that needs one of these files needs all three. + +#include "include/flutter/standard_message_codec.h" +#include "include/flutter/standard_method_codec.h" +#include "standard_codec_serializer.h" + +#include +#include +#include +#include +#include +#include + +namespace flutter { + +// ===== standard_codec_serializer.h ===== + +namespace { + +// The order/values here must match the constants in message_codecs.dart. +enum class EncodedType { + kNull = 0, + kTrue, + kFalse, + kInt32, + kInt64, + kLargeInt, // No longer used. If encountered, treat as kString. + kFloat64, + kString, + kUInt8List, + kInt32List, + kInt64List, + kFloat64List, + kList, + kMap, +}; + +// Returns the encoded type that should be written when serializing |value|. +EncodedType EncodedTypeForValue(const EncodableValue& value) { + switch (value.type()) { + case EncodableValue::Type::kNull: + return EncodedType::kNull; + case EncodableValue::Type::kBool: + return value.BoolValue() ? EncodedType::kTrue : EncodedType::kFalse; + case EncodableValue::Type::kInt: + return EncodedType::kInt32; + case EncodableValue::Type::kLong: + return EncodedType::kInt64; + case EncodableValue::Type::kDouble: + return EncodedType::kFloat64; + case EncodableValue::Type::kString: + return EncodedType::kString; + case EncodableValue::Type::kByteList: + return EncodedType::kUInt8List; + case EncodableValue::Type::kIntList: + return EncodedType::kInt32List; + case EncodableValue::Type::kLongList: + return EncodedType::kInt64List; + case EncodableValue::Type::kDoubleList: + return EncodedType::kFloat64List; + case EncodableValue::Type::kList: + return EncodedType::kList; + case EncodableValue::Type::kMap: + return EncodedType::kMap; + } + assert(false); + return EncodedType::kNull; +} + +} // namespace + +StandardCodecSerializer::StandardCodecSerializer() = default; + +StandardCodecSerializer::~StandardCodecSerializer() = default; + +EncodableValue StandardCodecSerializer::ReadValue( + ByteBufferStreamReader* stream) const { + EncodedType type = static_cast(stream->ReadByte()); + ; + switch (type) { + case EncodedType::kNull: + return EncodableValue(); + case EncodedType::kTrue: + return EncodableValue(true); + case EncodedType::kFalse: + return EncodableValue(false); + case EncodedType::kInt32: { + int32_t int_value = 0; + stream->ReadBytes(reinterpret_cast(&int_value), 4); + return EncodableValue(int_value); + } + case EncodedType::kInt64: { + int64_t long_value = 0; + stream->ReadBytes(reinterpret_cast(&long_value), 8); + return EncodableValue(long_value); + } + case EncodedType::kFloat64: { + double double_value = 0; + stream->ReadAlignment(8); + stream->ReadBytes(reinterpret_cast(&double_value), 8); + return EncodableValue(double_value); + } + case EncodedType::kLargeInt: + case EncodedType::kString: { + int32_t size = ReadSize(stream); + std::string string_value; + string_value.resize(size); + stream->ReadBytes(reinterpret_cast(&string_value[0]), size); + return EncodableValue(string_value); + } + case EncodedType::kUInt8List: + return ReadVector(stream); + case EncodedType::kInt32List: + return ReadVector(stream); + case EncodedType::kInt64List: + return ReadVector(stream); + case EncodedType::kFloat64List: + return ReadVector(stream); + case EncodedType::kList: { + int32_t length = ReadSize(stream); + EncodableList list_value; + list_value.reserve(length); + for (int32_t i = 0; i < length; ++i) { + list_value.push_back(ReadValue(stream)); + } + return EncodableValue(list_value); + } + case EncodedType::kMap: { + int32_t length = ReadSize(stream); + EncodableMap map_value; + for (int32_t i = 0; i < length; ++i) { + EncodableValue key = ReadValue(stream); + EncodableValue value = ReadValue(stream); + map_value.emplace(std::move(key), std::move(value)); + } + return EncodableValue(map_value); + } + } + std::cerr << "Unknown type in StandardCodecSerializer::ReadValue: " + << static_cast(type) << std::endl; + return EncodableValue(); +} + +void StandardCodecSerializer::WriteValue(const EncodableValue& value, + ByteBufferStreamWriter* stream) const { + stream->WriteByte(static_cast(EncodedTypeForValue(value))); + switch (value.type()) { + case EncodableValue::Type::kNull: + case EncodableValue::Type::kBool: + // Null and bool are encoded directly in the type. + break; + case EncodableValue::Type::kInt: { + int32_t int_value = value.IntValue(); + stream->WriteBytes(reinterpret_cast(&int_value), 4); + break; + } + case EncodableValue::Type::kLong: { + int64_t long_value = value.LongValue(); + stream->WriteBytes(reinterpret_cast(&long_value), 8); + break; + } + case EncodableValue::Type::kDouble: { + stream->WriteAlignment(8); + double double_value = value.DoubleValue(); + stream->WriteBytes(reinterpret_cast(&double_value), 8); + break; + } + case EncodableValue::Type::kString: { + const auto& string_value = value.StringValue(); + size_t size = string_value.size(); + WriteSize(size, stream); + stream->WriteBytes(reinterpret_cast(string_value.data()), + size); + break; + } + case EncodableValue::Type::kByteList: + WriteVector(value.ByteListValue(), stream); + break; + case EncodableValue::Type::kIntList: + WriteVector(value.IntListValue(), stream); + break; + case EncodableValue::Type::kLongList: + WriteVector(value.LongListValue(), stream); + break; + case EncodableValue::Type::kDoubleList: + WriteVector(value.DoubleListValue(), stream); + break; + case EncodableValue::Type::kList: + WriteSize(value.ListValue().size(), stream); + for (const auto& item : value.ListValue()) { + WriteValue(item, stream); + } + break; + case EncodableValue::Type::kMap: + WriteSize(value.MapValue().size(), stream); + for (const auto& pair : value.MapValue()) { + WriteValue(pair.first, stream); + WriteValue(pair.second, stream); + } + break; + } +} + +uint32_t StandardCodecSerializer::ReadSize( + ByteBufferStreamReader* stream) const { + uint8_t byte = stream->ReadByte(); + if (byte < 254) { + return byte; + } else if (byte == 254) { + uint16_t value; + stream->ReadBytes(reinterpret_cast(&value), 2); + return value; + } else { + uint32_t value; + stream->ReadBytes(reinterpret_cast(&value), 4); + return value; + } +} + +void StandardCodecSerializer::WriteSize(uint32_t size, + ByteBufferStreamWriter* stream) const { + if (size < 254) { + stream->WriteByte(static_cast(size)); + } else if (size <= 0xffff) { + stream->WriteByte(254); + uint16_t value = static_cast(size); + stream->WriteBytes(reinterpret_cast(&value), 2); + } else { + stream->WriteByte(255); + stream->WriteBytes(reinterpret_cast(&size), 4); + } +} + +template +EncodableValue StandardCodecSerializer::ReadVector( + ByteBufferStreamReader* stream) const { + int32_t count = ReadSize(stream); + std::vector vector; + vector.resize(count); + size_t type_size = sizeof(T); + if (type_size > 1) { + stream->ReadAlignment(type_size); + } + stream->ReadBytes(reinterpret_cast(vector.data()), + count * type_size); + return EncodableValue(vector); +} + +template +void StandardCodecSerializer::WriteVector( + const std::vector vector, + ByteBufferStreamWriter* stream) const { + size_t count = vector.size(); + WriteSize(count, stream); + size_t type_size = sizeof(T); + if (type_size > 1) { + stream->WriteAlignment(type_size); + } + stream->WriteBytes(reinterpret_cast(vector.data()), + count * type_size); +} + +// ===== standard_message_codec.h ===== + +// static +const StandardMessageCodec& StandardMessageCodec::GetInstance() { + static StandardMessageCodec sInstance; + return sInstance; +} + +StandardMessageCodec::StandardMessageCodec() = default; + +StandardMessageCodec::~StandardMessageCodec() = default; + +std::unique_ptr StandardMessageCodec::DecodeMessageInternal( + const uint8_t* binary_message, + const size_t message_size) const { + StandardCodecSerializer serializer; + ByteBufferStreamReader stream(binary_message, message_size); + return std::make_unique(serializer.ReadValue(&stream)); +} + +std::unique_ptr> +StandardMessageCodec::EncodeMessageInternal( + const EncodableValue& message) const { + StandardCodecSerializer serializer; + auto encoded = std::make_unique>(); + ByteBufferStreamWriter stream(encoded.get()); + serializer.WriteValue(message, &stream); + return encoded; +} + +// ===== standard_method_codec.h ===== + +// static +const StandardMethodCodec& StandardMethodCodec::GetInstance() { + static StandardMethodCodec sInstance; + return sInstance; +} + +std::unique_ptr> +StandardMethodCodec::DecodeMethodCallInternal(const uint8_t* message, + const size_t message_size) const { + StandardCodecSerializer serializer; + ByteBufferStreamReader stream(message, message_size); + EncodableValue method_name = serializer.ReadValue(&stream); + if (!method_name.IsString()) { + std::cerr << "Invalid method call; method name is not a string." + << std::endl; + return nullptr; + } + auto arguments = + std::make_unique(serializer.ReadValue(&stream)); + return std::make_unique>(method_name.StringValue(), + std::move(arguments)); +} + +std::unique_ptr> +StandardMethodCodec::EncodeMethodCallInternal( + const MethodCall& method_call) const { + StandardCodecSerializer serializer; + auto encoded = std::make_unique>(); + ByteBufferStreamWriter stream(encoded.get()); + serializer.WriteValue(EncodableValue(method_call.method_name()), &stream); + if (method_call.arguments()) { + serializer.WriteValue(*method_call.arguments(), &stream); + } else { + serializer.WriteValue(EncodableValue(), &stream); + } + return encoded; +} + +std::unique_ptr> +StandardMethodCodec::EncodeSuccessEnvelopeInternal( + const EncodableValue* result) const { + StandardCodecSerializer serializer; + auto encoded = std::make_unique>(); + ByteBufferStreamWriter stream(encoded.get()); + stream.WriteByte(0); + if (result) { + serializer.WriteValue(*result, &stream); + } else { + serializer.WriteValue(EncodableValue(), &stream); + } + return encoded; +} + +std::unique_ptr> +StandardMethodCodec::EncodeErrorEnvelopeInternal( + const std::string& error_code, + const std::string& error_message, + const EncodableValue* error_details) const { + StandardCodecSerializer serializer; + auto encoded = std::make_unique>(); + ByteBufferStreamWriter stream(encoded.get()); + stream.WriteByte(1); + serializer.WriteValue(EncodableValue(error_code), &stream); + if (error_message.empty()) { + serializer.WriteValue(EncodableValue(), &stream); + } else { + serializer.WriteValue(EncodableValue(error_message), &stream); + } + if (error_details) { + serializer.WriteValue(*error_details, &stream); + } else { + serializer.WriteValue(EncodableValue(), &stream); + } + return encoded; +} + +} // namespace flutter diff --git a/flutter/cpp_client_wrapper/standard_codec_serializer.h b/flutter/cpp_client_wrapper/standard_codec_serializer.h new file mode 100644 index 0000000..07499fd --- /dev/null +++ b/flutter/cpp_client_wrapper/standard_codec_serializer.h @@ -0,0 +1,54 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_ENCODABLE_VALUE_SERIALIZER_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_ENCODABLE_VALUE_SERIALIZER_H_ + +#include "byte_stream_wrappers.h" +#include "include/flutter/encodable_value.h" + +namespace flutter { + +// Encapsulates the logic for encoding/decoding EncodableValues to/from the +// standard codec binary representation. +class StandardCodecSerializer { + public: + StandardCodecSerializer(); + ~StandardCodecSerializer(); + + // Prevent copying. + StandardCodecSerializer(StandardCodecSerializer const&) = delete; + StandardCodecSerializer& operator=(StandardCodecSerializer const&) = delete; + + // Reads and returns the next value from |stream|. + EncodableValue ReadValue(ByteBufferStreamReader* stream) const; + + // Writes the encoding of |value| to |stream|. + void WriteValue(const EncodableValue& value, + ByteBufferStreamWriter* stream) const; + + protected: + // Reads the variable-length size from the current position in |stream|. + uint32_t ReadSize(ByteBufferStreamReader* stream) const; + + // Writes the variable-length size encoding to |stream|. + void WriteSize(uint32_t size, ByteBufferStreamWriter* stream) const; + + // Reads a fixed-type list whose values are of type T from the current + // position in |stream|, and returns it as the corresponding EncodableValue. + // |T| must correspond to one of the support list value types of + // EncodableValue. + template + EncodableValue ReadVector(ByteBufferStreamReader* stream) const; + + // Writes |vector| to |stream| as a fixed-type list. |T| must correspond to + // one of the support list value types of EncodableValue. + template + void WriteVector(const std::vector vector, + ByteBufferStreamWriter* stream) const; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_ENCODABLE_VALUE_SERIALIZER_H_ diff --git a/flutter/flutter_application.cc b/flutter/flutter_application.cc deleted file mode 100644 index 62ab491..0000000 --- a/flutter/flutter_application.cc +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter_application.h" - -#include - -#include -#include -#include - -#include "utils.h" - -namespace flutter { - -static_assert(FLUTTER_ENGINE_VERSION == 1, ""); - -static const char *kICUDataFileName = "icudtl.dat"; - -static std::string GetICUDataPath() { - auto exe_dir = GetExecutableDirectory(); - if (exe_dir == "") { - return ""; - } - std::stringstream stream; - stream << exe_dir << kICUDataFileName; - - auto icu_path = stream.str(); - - if (!FileExistsAtPath(icu_path.c_str())) { - FLWAY_ERROR << "Could not find " << icu_path << std::endl; - return ""; - } - - return icu_path; -} - -FlutterApplication::FlutterApplication( - std::string bundle_path, const std::vector &command_line_args, - RenderDelegate &render_delegate) - : render_delegate_(render_delegate) { - if (!FlutterAssetBundleIsValid(bundle_path)) { - FLWAY_ERROR << "Flutter asset bundle was not valid." << std::endl; - return; - } - - FlutterRendererConfig config = {}; - config.type = kOpenGL; - config.open_gl.struct_size = sizeof(config.open_gl); - config.open_gl.make_current = [](void *userdata) -> bool { - return reinterpret_cast(userdata) - ->render_delegate_.OnApplicationContextMakeCurrent(); - }; - config.open_gl.clear_current = [](void *userdata) -> bool { - return reinterpret_cast(userdata) - ->render_delegate_.OnApplicationContextClearCurrent(); - }; - config.open_gl.present = [](void *userdata) -> bool { - return reinterpret_cast(userdata) - ->render_delegate_.OnApplicationPresent(); - }; - config.open_gl.fbo_callback = [](void *userdata) -> uint32_t { - return reinterpret_cast(userdata) - ->render_delegate_.OnApplicationGetOnscreenFBO(); - }; - config.open_gl.gl_proc_resolver = - [&render_delegate_](void *userdata, const char *name) -> void * { - return reinterpret_cast(userdata) - ->render_delegate_.GetProcAddress(name); - }; - - auto icu_data_path = GetICUDataPath(); - - if (icu_data_path == "") { - FLWAY_ERROR << "Could not find ICU data. It should be placed next to the " - "executable but it wasn't there." - << std::endl; - return; - } - - std::vector command_line_args_c; - - for (const auto &arg : command_line_args) { - command_line_args_c.push_back(arg.c_str()); - } - - FlutterProjectArgs args = { - .struct_size = sizeof(FlutterProjectArgs), - .assets_path = bundle_path.c_str(), - .main_path = "", - .packages_path = "", - .icu_data_path = icu_data_path.c_str(), - .command_line_argc = static_cast(command_line_args_c.size()), - .command_line_argv = command_line_args_c.data(), - }; - - FlutterEngine engine = nullptr; - auto result = FlutterEngineRun(FLUTTER_ENGINE_VERSION, &config, &args, - this /* userdata */, &engine_); - - if (result != kSuccess) { - FLWAY_ERROR << "Could not run the Flutter engine" << std::endl; - return; - } - - valid_ = true; -} - -FlutterApplication::~FlutterApplication() { - if (engine_ == nullptr) { - return; - } - - auto result = FlutterEngineShutdown(engine_); - - if (result != kSuccess) { - FLWAY_ERROR << "Could not shutdown the Flutter engine." << std::endl; - } -} - -bool FlutterApplication::IsValid() const { return valid_; } - -bool FlutterApplication::SetWindowSize(size_t width, size_t height) { - FlutterWindowMetricsEvent event = {}; - event.struct_size = sizeof(event); - event.width = width; - event.height = height; - event.pixel_ratio = 1.0; - return FlutterEngineSendWindowMetricsEvent(engine_, &event) == kSuccess; -} - -void FlutterApplication::ProcessEvents() { - __FlutterEngineFlushPendingTasksNow(); -} - -bool FlutterApplication::SendPointerEvent(int button, int x, int y) { - if (!valid_) { - FLWAY_ERROR << "Pointer events on an invalid application." << std::endl; - return false; - } - - // Simple hover event. Nothing to do. - if (last_button_ == 0 && button == 0) { - return true; - } - - FlutterPointerPhase phase = kCancel; - - if (last_button_ == 0 && button != 0) { - phase = kDown; - } else if (last_button_ == button) { - phase = kMove; - } else { - phase = kUp; - } - - last_button_ = button; - return SendFlutterPointerEvent(phase, x, y); -} - -bool FlutterApplication::SendFlutterPointerEvent(FlutterPointerPhase phase, - double x, double y) { - FlutterPointerEvent event = {}; - event.struct_size = sizeof(event); - event.phase = phase; - event.x = x; - event.y = y; - event.timestamp = - std::chrono::duration_cast( - std::chrono::high_resolution_clock::now().time_since_epoch()) - .count(); - return FlutterEngineSendPointerEvent(engine_, &event, 1) == kSuccess; -} - -void FlutterApplication::ReadInputEvents() { - // TODO(chinmaygarde): Fill this in for touch screen and not just devices that - // fake mice. - ::sleep(INT_MAX); -} - -} // namespace flutter diff --git a/flutter/flutter_application.h b/flutter/flutter_application.h deleted file mode 100644 index cdce262..0000000 --- a/flutter/flutter_application.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#pragma once - -#include - -#include -#include - -#include "macros.h" - -namespace flutter { - -class FlutterApplication { -public: - class RenderDelegate { - public: - virtual bool OnApplicationContextMakeCurrent() = 0; - - virtual bool OnApplicationContextClearCurrent() = 0; - - virtual bool OnApplicationPresent() = 0; - - virtual uint32_t OnApplicationGetOnscreenFBO() = 0; - - virtual void *GetProcAddress(const char *) = 0; - }; - - FlutterApplication(std::string bundle_path, - const std::vector &args, - RenderDelegate &render_delegate); - - ~FlutterApplication(); - - bool IsValid() const; - - void ProcessEvents(); - - bool SetWindowSize(size_t width, size_t height); - - bool SendPointerEvent(int button, int x, int y); - - void ReadInputEvents(); - -private: - bool valid_; - RenderDelegate &render_delegate_; - FlutterEngine engine_ = nullptr; - int last_button_ = 0; - - bool SendFlutterPointerEvent(FlutterPointerPhase phase, double x, double y); - - FLWAY_DISALLOW_COPY_AND_ASSIGN(FlutterApplication); -}; - -} // namespace flutter diff --git a/flutter/flutter_export.h b/flutter/flutter_export.h new file mode 100644 index 0000000..2f38091 --- /dev/null +++ b/flutter/flutter_export.h @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_PUBLIC_FLUTTER_EXPORT_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_PUBLIC_FLUTTER_EXPORT_H_ + +#ifdef FLUTTER_DESKTOP_LIBRARY +// Add visibility/export annotations when building the library. + +#ifdef _WIN32 +#define FLUTTER_EXPORT __declspec(dllexport) +#else +#define FLUTTER_EXPORT __attribute__((visibility("default"))) +#endif + +#else // FLUTTER_DESKTOP_LIBRARY + +// Add import annotations when consuming the library. +#ifdef _WIN32 +#define FLUTTER_EXPORT __declspec(dllimport) +#else +#define FLUTTER_EXPORT +#endif + +#endif // FLUTTER_DESKTOP_LIBRARY + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_PUBLIC_FLUTTER_EXPORT_H_ \ No newline at end of file diff --git a/flutter/flutter_glfw.h b/flutter/flutter_glfw.h new file mode 100644 index 0000000..f4c718f --- /dev/null +++ b/flutter/flutter_glfw.h @@ -0,0 +1,174 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_GLFW_PUBLIC_FLUTTER_GLFW_H_ +#define FLUTTER_SHELL_PLATFORM_GLFW_PUBLIC_FLUTTER_GLFW_H_ + +#include +#include + +#include "flutter_export.h" +#include "flutter_messenger.h" +#include "flutter_plugin_registrar.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +// Opaque reference to a Flutter window controller. +typedef struct FlutterDesktopWindowControllerState* + FlutterDesktopWindowControllerRef; + +// Opaque reference to a Flutter window. +typedef struct FlutterDesktopWindow* FlutterDesktopWindowRef; + +// Opaque reference to a Flutter engine instance. +typedef struct FlutterDesktopEngineState* FlutterDesktopEngineRef; + +// Sets up the library's graphic context. Must be called before any other +// methods. +// +// Note: Internally, this library uses GLFW, which does not support multiple +// copies within the same process. Internally this calls glfwInit, which will +// fail if you have called glfwInit elsewhere in the process. +FLUTTER_EXPORT bool FlutterDesktopInit(); + +// Tears down library state. Must be called before the process terminates. +FLUTTER_EXPORT void FlutterDesktopTerminate(); + +// Creates a Window running a Flutter Application. +// +// FlutterDesktopInit() must be called prior to this function. +// +// The |assets_path| is the path to the flutter_assets folder for the Flutter +// application to be run. |icu_data_path| is the path to the icudtl.dat file +// for the version of Flutter you are using. +// +// The |arguments| are passed to the Flutter engine. See: +// https://github.com/flutter/engine/blob/master/shell/common/switches.h for +// for details. Not all arguments will apply to desktop. +// +// Returns a null pointer in the event of an error. Otherwise, the pointer is +// valid until FlutterDesktopRunWindowLoop has been called and returned, or +// FlutterDesktopDestroyWindow is called. +// Note that calling FlutterDesktopCreateWindow without later calling +// one of those two methods on the returned reference is a memory leak. +FLUTTER_EXPORT FlutterDesktopWindowControllerRef +FlutterDesktopCreateWindow(int initial_width, + int initial_height, + const char* title, + const char* assets_path, + const char* icu_data_path, + const char** arguments, + size_t argument_count); + +// Shuts down the engine instance associated with |controller|, and cleans up +// associated state. +// +// |controller| is no longer valid after this call. +FLUTTER_EXPORT void FlutterDesktopDestroyWindow( + FlutterDesktopWindowControllerRef controller); + +// Loops on Flutter window events until the window is closed. +// +// Once this function returns, |controller| is no longer valid, and must not be +// be used again, as it calls FlutterDesktopDestroyWindow internally. +// +// TODO: Replace this with a method that allows running the runloop +// incrementally. +FLUTTER_EXPORT void FlutterDesktopRunWindowLoop( + FlutterDesktopWindowControllerRef controller); + +// Returns the window handle for the window associated with +// FlutterDesktopWindowControllerRef. +// +// Its lifetime is the same as the |controller|'s. +FLUTTER_EXPORT FlutterDesktopWindowRef +FlutterDesktopGetWindow(FlutterDesktopWindowControllerRef controller); + +// Returns the plugin registrar handle for the plugin with the given name. +// +// The name must be unique across the application. +FLUTTER_EXPORT FlutterDesktopPluginRegistrarRef +FlutterDesktopGetPluginRegistrar(FlutterDesktopWindowControllerRef controller, + const char* plugin_name); + +// Enables or disables hover tracking. +// +// If hover is enabled, mouse movement will send hover events to the Flutter +// engine, rather than only tracking the mouse while the button is pressed. +// Defaults to on. +FLUTTER_EXPORT void FlutterDesktopWindowSetHoverEnabled( + FlutterDesktopWindowRef flutter_window, + bool enabled); + +// Sets the displayed title for |flutter_window|. +FLUTTER_EXPORT void FlutterDesktopWindowSetTitle( + FlutterDesktopWindowRef flutter_window, + const char* title); + +// Sets the displayed icon for |flutter_window|. +// +// The pixel format is 32-bit RGBA. The provided image data only needs to be +// valid for the duration of the call to this method. Pass a nullptr to revert +// to the default icon. +FLUTTER_EXPORT void FlutterDesktopWindowSetIcon( + FlutterDesktopWindowRef flutter_window, + uint8_t* pixel_data, + int width, + int height); + +// Gets the position and size of |flutter_window| in screen coordinates. +FLUTTER_EXPORT void FlutterDesktopWindowGetFrame( + FlutterDesktopWindowRef flutter_window, + int* x, + int* y, + int* width, + int* height); + +// Sets the position and size of |flutter_window| in screen coordinates. +FLUTTER_EXPORT void FlutterDesktopWindowSetFrame( + FlutterDesktopWindowRef flutter_window, + int x, + int y, + int width, + int height); + +// Returns the scale factor--the number of pixels per screen coordinate--for +// |flutter_window|. +FLUTTER_EXPORT double FlutterDesktopWindowGetScaleFactor( + FlutterDesktopWindowRef flutter_window); + +// Runs an instance of a headless Flutter engine. +// +// The |assets_path| is the path to the flutter_assets folder for the Flutter +// application to be run. |icu_data_path| is the path to the icudtl.dat file +// for the version of Flutter you are using. +// +// The |arguments| are passed to the Flutter engine. See: +// https://github.com/flutter/engine/blob/master/shell/common/switches.h for +// for details. Not all arguments will apply to desktop. +// +// Returns a null pointer in the event of an error. +FLUTTER_EXPORT FlutterDesktopEngineRef +FlutterDesktopRunEngine(const char* assets_path, + const char* icu_data_path, + const char** arguments, + size_t argument_count); + +// Shuts down the given engine instance. Returns true if the shutdown was +// successful. |engine_ref| is no longer valid after this call. +FLUTTER_EXPORT bool FlutterDesktopShutDownEngine( + FlutterDesktopEngineRef engine_ref); + +// Returns the window associated with this registrar's engine instance. +// This is a GLFW shell-specific extension to flutter_plugin_registrar.h +FLUTTER_EXPORT FlutterDesktopWindowRef +FlutterDesktopRegistrarGetWindow(FlutterDesktopPluginRegistrarRef registrar); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_SHELL_PLATFORM_GLFW_PUBLIC_FLUTTER_GLFW_H_ diff --git a/flutter/flutter_messenger.h b/flutter/flutter_messenger.h new file mode 100644 index 0000000..ff9f434 --- /dev/null +++ b/flutter/flutter_messenger.h @@ -0,0 +1,81 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_PUBLIC_FLUTTER_MESSENGER_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_PUBLIC_FLUTTER_MESSENGER_H_ + +#include +#include + +#include "flutter_export.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +// Opaque reference to a Flutter engine messenger. +typedef struct FlutterDesktopMessenger* FlutterDesktopMessengerRef; + +// Opaque handle for tracking responses to messages. +typedef struct _FlutterPlatformMessageResponseHandle + FlutterDesktopMessageResponseHandle; + +// A message received from Flutter. +typedef struct { + // Size of this struct as created by Flutter. + size_t struct_size; + // The name of the channel used for this message. + const char* channel; + // The raw message data. + const uint8_t* message; + // The length of |message|. + size_t message_size; + // The response handle. If non-null, the receiver of this message must call + // FlutterDesktopSendMessageResponse exactly once with this handle. + const FlutterDesktopMessageResponseHandle* response_handle; +} FlutterDesktopMessage; + +// Function pointer type for message handler callback registration. +// +// The user data will be whatever was passed to FlutterDesktopSetMessageHandler +// for the channel the message is received on. +typedef void (*FlutterDesktopMessageCallback)( + FlutterDesktopMessengerRef /* messenger */, + const FlutterDesktopMessage* /* message*/, + void* /* user data */); + +// Sends a binary message to the Flutter side on the specified channel. +FLUTTER_EXPORT void FlutterDesktopMessengerSend( + FlutterDesktopMessengerRef messenger, + const char* channel, + const uint8_t* message, + const size_t message_size); + +// Sends a reply to a FlutterDesktopMessage for the given response handle. +// +// Once this has been called, |handle| is invalid and must not be used again. +FLUTTER_EXPORT void FlutterDesktopMessengerSendResponse( + FlutterDesktopMessengerRef messenger, + const FlutterDesktopMessageResponseHandle* handle, + const uint8_t* data, + size_t data_length); + +// Registers a callback function for incoming binary messages from the Flutter +// side on the specified channel. +// +// Replaces any existing callback. Provide a null handler to unregister the +// existing callback. +// +// If |user_data| is provided, it will be passed in |callback| calls. +FLUTTER_EXPORT void FlutterDesktopMessengerSetCallback( + FlutterDesktopMessengerRef messenger, + const char* channel, + FlutterDesktopMessageCallback callback, + void* user_data); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_PUBLIC_FLUTTER_MESSENGER_H_ diff --git a/flutter/flutter_plugin_registrar.h b/flutter/flutter_plugin_registrar.h new file mode 100644 index 0000000..1caa3ce --- /dev/null +++ b/flutter/flutter_plugin_registrar.h @@ -0,0 +1,42 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_PUBLIC_FLUTTER_PLUGIN_REGISTRAR_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_PUBLIC_FLUTTER_PLUGIN_REGISTRAR_H_ + +#include +#include + +#include "flutter_export.h" +#include "flutter_messenger.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +// Opaque reference to a plugin registrar. +typedef struct FlutterDesktopPluginRegistrar* FlutterDesktopPluginRegistrarRef; + +// Returns the engine messenger associated with this registrar. +FLUTTER_EXPORT FlutterDesktopMessengerRef +FlutterDesktopRegistrarGetMessenger(FlutterDesktopPluginRegistrarRef registrar); + +// Enables input blocking on the given channel. +// +// If set, then the Flutter window will disable input callbacks +// while waiting for the handler for messages on that channel to run. This is +// useful if handling the message involves showing a modal window, for instance. +// +// This must be called after FlutterDesktopSetMessageHandler, as setting a +// handler on a channel will reset the input blocking state back to the +// default of disabled. +FLUTTER_EXPORT void FlutterDesktopRegistrarEnableInputBlocking( + FlutterDesktopPluginRegistrarRef registrar, + const char* channel); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_PUBLIC_FLUTTER_PLUGIN_REGISTRAR_H_ diff --git a/flutter/main.cc b/flutter/main.cc index 9e118c3..f27d453 100644 --- a/flutter/main.cc +++ b/flutter/main.cc @@ -1,93 +1,122 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include #include -#include +#include +#include +#include #include - -#include "flutter_application.h" -#include "pi_display.h" +#include #include "utils.h" - -namespace flutter { - -static void PrintUsage() { - std::cerr << "Flutter Raspberry Pi" << std::endl << std::endl; - std::cerr << "========================" << std::endl; - std::cerr << "Usage: `" << GetExecutableName() - << " `" << std::endl - << std::endl; - std::cerr << R"~( -This utility runs an instance of a Flutter application and renders using -Video Core APIs. - -The Flutter tools can be obtained at https://flutter.io/ - -asset_bundle_path: The Flutter application code needs to be snapshotted using - the Flutter tools and the assets packaged in the appropriate - location. This can be done for any Flutter application by - running `flutter build bundle` while in the directory of a - valid Flutter project. This should package all the code and - assets in the "build/flutter_assets" directory. Specify this - directory as the first argument to this utility. - - flutter_flags: Typically empty. These extra flags are passed directly to the - Flutter engine. To see all supported flags, run - `flutter_tester --help` using the test binary included in the - Flutter tools. -)~" << std::endl; -} - -static bool Main(std::vector args) { - if (args.size() == 0) { - std::cerr << " " << std::endl; - PrintUsage(); - return false; - } - - const auto asset_bundle_path = args[0]; - - if (!FlutterAssetBundleIsValid(asset_bundle_path)) { - std::cerr << " " << std::endl; - PrintUsage(); - return false; +#include + +namespace { + +// Returns the path of the directory containing this executable, or an empty +// string if the directory cannot be found. +//std::string GetExecutableDirectory() { +// char buffer[PATH_MAX + 1]; +// ssize_t length = readlink("/proc/self/exe", buffer, sizeof(buffer)); +// if (length > PATH_MAX) { +// std::cerr << "Couldn't locate executable" << std::endl; +// return ""; +// } +// std::string executable_path(buffer, length); +// size_t last_separator_position = executable_path.find_last_of('/'); +// if (last_separator_position == std::string::npos) { +// std::cerr << "Unabled to find parent directory of " << executable_path +// << std::endl; +// return ""; +// } +// return executable_path.substr(0, last_separator_position); +//} + + +static const char *kICUDataFileName = "icudtl.dat"; + +static std::string GetICUDataPath() { + auto exe_dir = flutter::GetExecutableDirectory(); + if (exe_dir == "") { + return ""; } + std::stringstream stream; + stream << exe_dir << kICUDataFileName; - PiDisplay display; + auto icu_path = stream.str(); - if (!display.IsValid()) { - FLWAY_ERROR << "Could not initialize the display." << std::endl; - return false; + if (!flutter::FileExistsAtPath(icu_path.c_str())) { + FLWAY_ERROR << "Could not find " << icu_path << std::endl; + return ""; } - FLWAY_LOG << "Display Size: " << display.GetWidth() << " x " - << display.GetHeight() << std::endl; - - FlutterApplication application(asset_bundle_path, args, display); - if (!application.IsValid()) { - FLWAY_ERROR << "Flutter application was not valid." << std::endl; - return false; - } - - if (!application.SetWindowSize(display.GetWidth(), display.GetHeight())) { - FLWAY_ERROR << "Could not update Flutter application size." << std::endl; - return false; - } - - application.ReadInputEvents(); - - return true; + return icu_path; } -} // namespace flutter - -int main(int argc, char *argv[]) { - std::vector args; - for (int i = 1; i < argc; ++i) { - args.push_back(argv[i]); +} // namespace + + + +int main(int argc, char **argv) { + + std::vector argss; + for (int i = 1; i < argc; ++i) { + argss.push_back(argv[i]); + } + std::vector args = std::move(argss); + + // Resources are located relative to the executable. +// std::string base_directory = GetExecutableDirectory(); +// if (base_directory.empty()) { +// base_directory = "."; +// } +// std::string data_directory = base_directory + "/build"; +// std::string assets_path = data_directory + "/flutter_assets"; +// std::string icu_data_path = data_directory + "/icudtl.dat"; + std::string icu_data_path = GetICUDataPath(); + std::cout << "icu_data_path: " << icu_data_path << std::endl; + const auto assets_path = args[0]; + std::cout << "assets_path: " << assets_path << std::endl; + if (!flutter::FlutterAssetBundleIsValid(assets_path)) { + std::cerr << "Warn: " << std::endl; +// PrintUsage(); +// return EXIT_FAILURE; + } + +// std::cout << "Base dir: " << base_directory << std::endl; + + // Arguments for the Flutter Engine. +// std::vector arguments; +#ifdef NDEBUG + args.push_back("--disable-dart-asserts"); +#endif + args.erase(args.begin()); + std::cout << "Arguments for the Flutter Engine:"; + for (unsigned i=0; i -#include - -namespace flutter { - -PiDisplay::PiDisplay() { - bcm_host_init(); - - // Setup the EGL Display. - { - auto display = ::eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (display == EGL_NO_DISPLAY) { - FLWAY_ERROR << "Could not get the EGL display." << std::endl; - return; - } - - if (::eglInitialize(display, nullptr, nullptr) != EGL_TRUE) { - FLWAY_ERROR << "Could not initialize the EGL display." << std::endl; - return; - } - - display_ = display; - } - - // Choose an EGL config. - EGLConfig config = {0}; - - { - EGLint num_config = 0; - const EGLint attribute_list[] = {EGL_RED_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_BLUE_SIZE, 8, - EGL_ALPHA_SIZE, 8, - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_NONE}; - - if (::eglChooseConfig(display_, attribute_list, &config, 1, &num_config) != - EGL_TRUE) { - FLWAY_ERROR << "Could not choose an EGL config." << std::endl; - return; - } - } - - // Create the EGL context. - { - const EGLint context_attributes[] = { - EGL_CONTEXT_CLIENT_VERSION, // - 2, // - EGL_NONE // - }; - auto context = ::eglCreateContext(display_, config, EGL_NO_CONTEXT, - context_attributes); - - if (context == EGL_NO_CONTEXT) { - FLWAY_ERROR << "Could not create the EGL context." << std::endl; - return; - } - - context_ = context; - } - - // Query the size of the current display. - { - uint32_t display_width = 0; - uint32_t display_height = 0; - - if (graphics_get_display_size(0 /* LCD */, &display_width, - &display_height) < 0) { - FLWAY_ERROR << "Could not query display size." << std::endl; - return; - } - - if (display_height <= 0 || display_width <= 0) { - FLWAY_ERROR << "Invalid display size: " << display_width << " x " - << display_height << std::endl; - return; - } - - display_width_ = display_width; - display_height_ = display_height; - } - - // VideoCore Fu. - // TODO(chinmaygarde): There is insufficient error handler in this block on - // all the call to vc_. - { - dispman_display_ = ::vc_dispmanx_display_open(0 /* LCD */); - - DISPMANX_UPDATE_HANDLE_T dispman_update = ::vc_dispmanx_update_start(0); - - const VC_RECT_T destination_rect = { - .x = 0, - .y = 0, - .width = display_width_, - .height = display_height_, - }; - - const VC_RECT_T source_rect = { - .x = 0, - .y = 0, - .width = display_width_ << 16, - .height = display_height_ << 16, - }; - - dispman_element_ = - ::vc_dispmanx_element_add(dispman_update, // update - dispman_display_, // display - 0, // layer - &destination_rect, // destination rect - 0, // source handle - &source_rect, // source rect - DISPMANX_PROTECTION_NONE, // protections - 0, // alpha - 0, // clang - DISPMANX_NO_ROTATE // transform - ); - - native_window_.element = dispman_element_; - native_window_.width = display_width_; - native_window_.height = display_height_; - - ::vc_dispmanx_update_submit_sync(dispman_update); - } - - // Create the EGL window surface. - { - auto surface = - ::eglCreateWindowSurface(display_, config, &native_window_, nullptr); - if (surface == EGL_NO_SURFACE) { - FLWAY_ERROR << "Could not create EGL surface." << std::endl; - return; - } - - surface_ = surface; - } - - valid_ = true; -} - -PiDisplay::~PiDisplay() { - if (surface_ != EGL_NO_SURFACE) { - ::eglDestroySurface(display_, surface_); - surface_ = EGL_NO_SURFACE; - } - - // TODO(chinmaygarde): There is insufficient error handling here and the - // element resource lifecycle is unclear. - ::vc_dispmanx_display_close(dispman_display_); - - if (context_ != EGL_NO_CONTEXT) { - ::eglDestroyContext(display_, context_); - context_ = EGL_NO_CONTEXT; - } - - if (display_ != EGL_NO_DISPLAY) { - ::eglTerminate(display_); - display_ = EGL_NO_DISPLAY; - } - - ::bcm_host_deinit(); -} - -bool PiDisplay::IsValid() const { return valid_; } - -size_t PiDisplay::GetWidth() const { return display_width_; } - -size_t PiDisplay::GetHeight() const { return display_height_; } - -// |FlutterApplication::RenderDelegate| -bool PiDisplay::OnApplicationContextMakeCurrent() { - if (!valid_) { - FLWAY_ERROR << "Cannot make an invalid display current." << std::endl; - return false; - } - if (::eglMakeCurrent(display_, surface_, surface_, context_) != EGL_TRUE) { - FLWAY_ERROR << "Could not make the context current." << std::endl; - - return false; - } - return true; -} - -// |FlutterApplication::RenderDelegate| -bool PiDisplay::OnApplicationContextClearCurrent() { - if (!valid_) { - FLWAY_ERROR << "Cannot clear an invalid display." << std::endl; - return false; - } - if (::eglMakeCurrent(display_, EGL_NO_SURFACE, EGL_NO_SURFACE, - EGL_NO_CONTEXT) != EGL_TRUE) { - FLWAY_ERROR << "Could not clear the current context." << std::endl; - return false; - } - return true; -} - -// |FlutterApplication::RenderDelegate| -bool PiDisplay::OnApplicationPresent() { - if (!valid_) { - FLWAY_ERROR << "Cannot present an invalid display." << std::endl; - return false; - } - - if (::eglSwapBuffers(display_, surface_) != EGL_TRUE) { - FLWAY_ERROR << "Could not swap buffers to present the screen." << std::endl; - return false; - } - - return true; -} - -// |FlutterApplication::RenderDelegate| -uint32_t PiDisplay::OnApplicationGetOnscreenFBO() { - // Just FBO0. - return 0; -} - -// |FlutterApplication::RenderDelegate| -void *PiDisplay::GetProcAddress(const char *) { - if (name == nullptr) { - return nullptr; - } - - if (auto address = dlsym(RTLD_DEFAULT, name)) { - return address; - } - - return nullptr; -} - -} // namespace flutter diff --git a/flutter/pi_display.h b/flutter/pi_display.h deleted file mode 100644 index 0134e6c..0000000 --- a/flutter/pi_display.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2018 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#pragma once - -#include -#include -#include - -#include "flutter_application.h" -#include "macros.h" - -namespace flutter { - -class PiDisplay : public FlutterApplication::RenderDelegate { -public: - PiDisplay(); - - ~PiDisplay(); - - bool IsValid() const; - - size_t GetWidth() const; - - size_t GetHeight() const; - -private: - int32_t display_width_ = 0; - int32_t display_height_ = 0; - EGLDisplay display_ = EGL_NO_DISPLAY; - EGLContext context_ = EGL_NO_CONTEXT; - EGLSurface surface_ = EGL_NO_SURFACE; - DISPMANX_DISPLAY_HANDLE_T dispman_display_ = {0}; - DISPMANX_ELEMENT_HANDLE_T dispman_element_ = {0}; - - EGL_DISPMANX_WINDOW_T native_window_ = {}; - - bool valid_ = false; - - // |FlutterApplication::RenderDelegate| - bool OnApplicationContextMakeCurrent() override; - - // |FlutterApplication::RenderDelegate| - bool OnApplicationContextClearCurrent() override; - - // |FlutterApplication::RenderDelegate| - bool OnApplicationPresent() override; - - // |FlutterApplication::RenderDelegate| - uint32_t OnApplicationGetOnscreenFBO() override; - - // |FlutterApplication::RenderDelegate| - void *GetProcAddress(const char *) override; - - FLWAY_DISALLOW_COPY_AND_ASSIGN(PiDisplay); -}; - -} // namespace flutter