Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ 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(
Expand All @@ -25,20 +27,25 @@ add_definitions(
-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/flutter_application.cc
flutter/main.cc
flutter/pi_display.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)
target_link_libraries(flutter ts brcmGLESv2 brcmEGL bcm_host flutter_engine pthread dl flutter_linux)

install(TARGETS flutter RUNTIME DESTINATION bin)
9 changes: 9 additions & 0 deletions flutter/cpp_client_wrapper/README
Original file line number Diff line number Diff line change
@@ -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).
105 changes: 105 additions & 0 deletions flutter/cpp_client_wrapper/byte_stream_wrappers.h
Original file line number Diff line number Diff line change
@@ -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 <cstdint>
#include <cstring>
#include <iostream>
#include <vector>

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<uint8_t>* 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<uint8_t>* bytes_;
};

} // namespace flutter

#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_BYTE_STREAM_WRAPPERS_H_
45 changes: 45 additions & 0 deletions flutter/cpp_client_wrapper/engine_method_result.cc
Original file line number Diff line number Diff line change
@@ -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 <assert.h>
#include <iostream>

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<uint8_t>* 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
81 changes: 81 additions & 0 deletions flutter/cpp_client_wrapper/flutter_window_controller.cc
Original file line number Diff line number Diff line change
@@ -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 <algorithm>
#include <iostream>

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<std::string>& 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<const char*> 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<FlutterWindow>(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
96 changes: 96 additions & 0 deletions flutter/cpp_client_wrapper/include/flutter/basic_message_channel.h
Original file line number Diff line number Diff line change
@@ -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 <iostream>
#include <string>

#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 <typename T>
using MessageReply = std::function<void(const T& reply)>;

// A handler for receiving a message from the Flutter engine.
//
// Implementations must asynchronously call reply exactly once with the reply
// to the message.
template <typename T>
using MessageHandler =
std::function<void(const T& message, MessageReply<T> reply)>;

// A channel for communicating with the Flutter engine by sending asynchronous
// messages.
template <typename T>
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<T>* 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<std::vector<uint8_t>> 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<T> 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<T> 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<T> 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<T>* codec_;
};

} // namespace flutter

#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_BASIC_MESSAGE_CHANNEL_H_
Loading