Skip to content

Commit 0400b5d

Browse files
authored
[linux/windows] Separate out a C channel API (#259)
Eliminates PluginRegistrar and BinaryMessenger from the embedder.h API surface, moving them up to the FlutterWindowController level. In their place, adds new low-level messaging APIs, which are based closely on the Flutter engine embedder APIs (including an equivalent message struct), in some cases wrapping them directly. Unlike the engine APIs, they allow per-channel callbacks, so individual plugins can use the API. Since PluginHandler is no longer the source of truth for callback registration, only an adaptor, it's now fine to have multiple instances of them, all passing through to the underlying C API. Part of #230
1 parent 7f87e67 commit 0400b5d

11 files changed

+336
-86
lines changed

library/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ published_shared_library("flutter_embedder") {
3838
if (is_linux || is_win) {
3939
sources += [
4040
"common/engine_method_result.cc",
41+
"common/internal/incoming_message_dispatcher.cc",
42+
"common/internal/incoming_message_dispatcher.h",
4143
"common/internal/plugin_handler.cc",
4244
"common/internal/plugin_handler.h",
4345
"common/internal/text_input_model.cc",

library/common/glfw/embedder.cc

Lines changed: 59 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "library/common/glfw/key_event_handler.h"
3333
#include "library/common/glfw/keyboard_hook_handler.h"
3434
#include "library/common/glfw/text_input_plugin.h"
35+
#include "library/common/internal/incoming_message_dispatcher.h"
3536
#include "library/common/internal/plugin_handler.h"
3637

3738
#ifdef __linux__
@@ -61,7 +62,11 @@ struct FlutterEmbedderState {
6162
// The handle to the Flutter engine instance.
6263
FlutterEngine engine;
6364

64-
// The helper class managing plugin registration and messaging.
65+
// Message dispatch manager for messages from the Flutter engine.
66+
std::unique_ptr<flutter_desktop_embedding::IncomingMessageDispatcher>
67+
message_dispatcher;
68+
69+
// The helper class managing plugin registration.
6570
std::unique_ptr<flutter_desktop_embedding::PluginHandler> plugin_handler;
6671

6772
// Handlers for keyboard events from GLFW.
@@ -83,6 +88,18 @@ static FlutterEmbedderState *GetSavedEmbedderState(GLFWwindow *window) {
8388
glfwGetWindowUserPointer(window));
8489
}
8590

91+
// Converts a FlutterPlatformMessage to an equivalent FlutterEmbedderMessage.
92+
static flutter_desktop_embedding::FlutterEmbedderMessage
93+
ConvertToEmbedderMessage(const FlutterPlatformMessage &engine_message) {
94+
flutter_desktop_embedding::FlutterEmbedderMessage embedder_message = {};
95+
embedder_message.struct_size = sizeof(embedder_message);
96+
embedder_message.channel = engine_message.channel;
97+
embedder_message.message = engine_message.message;
98+
embedder_message.message_size = engine_message.message_size;
99+
embedder_message.response_handle = engine_message.response_handle;
100+
return embedder_message;
101+
}
102+
86103
// Returns the number of screen coordinates per inch for the main monitor.
87104
// If the information is unavailable, returns a default value that assumes
88105
// that a screen coordinate is one dp.
@@ -207,8 +224,10 @@ static void GLFWOnFlutterPlatformMessage(const FlutterPlatformMessage *message,
207224

208225
GLFWwindow *window = reinterpret_cast<GLFWwindow *>(user_data);
209226
auto state = GetSavedEmbedderState(window);
210-
state->plugin_handler->HandleMethodCallMessage(
211-
message, [window] { GLFWClearEventCallbacks(window); },
227+
228+
auto embedder_message = ConvertToEmbedderMessage(*message);
229+
state->message_dispatcher->HandleMessage(
230+
embedder_message, [window] { GLFWClearEventCallbacks(window); },
212231
[window] { GLFWAssignEventCallbacks(window); });
213232
}
214233

@@ -313,14 +332,6 @@ bool FlutterInit() {
313332

314333
void FlutterTerminate() { glfwTerminate(); }
315334

316-
PluginRegistrar *GetRegistrarForPlugin(FlutterWindowRef flutter_window,
317-
const std::string &plugin_name) {
318-
// Currently, PluginHandler acts as the registrar for all plugins, so the
319-
// name is ignored. It is part of the API to reduce churn in the future when
320-
// aligning more closely with the Flutter registrar system.
321-
return flutter_window->plugin_handler.get();
322-
}
323-
324335
FlutterWindowRef CreateFlutterWindow(
325336
size_t initial_width, size_t initial_height, const std::string &assets_path,
326337
const std::string &icu_data_path,
@@ -348,7 +359,9 @@ FlutterWindowRef CreateFlutterWindow(
348359
state->window = window;
349360
glfwSetWindowUserPointer(window, state);
350361
state->engine = engine;
351-
state->plugin_handler = std::make_unique<PluginHandler>(engine);
362+
state->message_dispatcher =
363+
std::make_unique<IncomingMessageDispatcher>(state);
364+
state->plugin_handler = std::make_unique<PluginHandler>(state);
352365

353366
// Set up the keyboard handlers.
354367
state->keyboard_hook_handlers.push_back(
@@ -392,4 +405,38 @@ void FlutterWindowLoop(FlutterWindowRef flutter_window) {
392405
glfwDestroyWindow(window);
393406
}
394407

408+
void FlutterEmbedderSendMessage(FlutterWindowRef flutter_window,
409+
const char *channel, const uint8_t *message,
410+
const size_t message_size) {
411+
FlutterPlatformMessage platform_message = {
412+
sizeof(FlutterPlatformMessage),
413+
channel,
414+
message,
415+
message_size,
416+
};
417+
418+
FlutterEngineSendPlatformMessage(flutter_window->engine, &platform_message);
419+
}
420+
421+
void FlutterEmbedderSendMessageResponse(
422+
FlutterWindowRef flutter_window,
423+
const FlutterEmbedderMessageResponseHandle *handle, const uint8_t *data,
424+
size_t data_length) {
425+
FlutterEngineSendPlatformMessageResponse(flutter_window->engine, handle, data,
426+
data_length);
427+
}
428+
429+
void FlutterEmbedderSetMessageCallback(FlutterWindowRef flutter_window,
430+
const char *channel,
431+
FlutterEmbedderMessageCallback callback,
432+
void *user_data) {
433+
flutter_window->message_dispatcher->SetMessageCallback(channel, callback,
434+
user_data);
435+
}
436+
437+
void FlutterEmbedderEnableInputBlocking(FlutterWindowRef flutter_window,
438+
const char *channel) {
439+
flutter_window->message_dispatcher->EnableInputBlockingForChannel(channel);
440+
}
441+
395442
} // namespace flutter_desktop_embedding

library/common/glfw/flutter_window_controller.cc

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
#include <iostream>
1818

19+
#include "library/common/internal/plugin_handler.h"
20+
1921
namespace flutter_desktop_embedding {
2022

2123
FlutterWindowController::FlutterWindowController(std::string &icu_data_path)
@@ -56,8 +58,14 @@ PluginRegistrar *FlutterWindowController::GetRegistrarForPlugin(
5658
if (!window_) {
5759
return nullptr;
5860
}
59-
return flutter_desktop_embedding::GetRegistrarForPlugin(window_, plugin_name);
60-
}
61+
if (!plugin_handler_) {
62+
plugin_handler_ = std::make_unique<PluginHandler>(window_);
63+
}
64+
// Currently, PluginHandler acts as the registrar for all plugins, so the
65+
// name is ignored. It is part of the API to reduce churn in the future when
66+
// aligning more closely with the Flutter registrar system.
67+
return plugin_handler_.get();
68+
} // namespace flutter_desktop_embedding
6169

6270
void FlutterWindowController::RunEventLoop() {
6371
if (window_) {
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright 2019 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
#include "library/common/internal/incoming_message_dispatcher.h"
15+
16+
namespace flutter_desktop_embedding {
17+
18+
IncomingMessageDispatcher::IncomingMessageDispatcher(FlutterWindowRef window)
19+
: window_(window) {}
20+
21+
IncomingMessageDispatcher::~IncomingMessageDispatcher() {}
22+
23+
void IncomingMessageDispatcher::HandleMessage(
24+
const FlutterEmbedderMessage &message,
25+
std::function<void(void)> input_block_cb,
26+
std::function<void(void)> input_unblock_cb) {
27+
std::string channel(message.channel);
28+
29+
// Find the handler for the channel; if there isn't one, report the failure.
30+
if (callbacks_.find(channel) == callbacks_.end()) {
31+
FlutterEmbedderSendMessageResponse(window_, message.response_handle,
32+
nullptr, 0);
33+
return;
34+
}
35+
auto &callback_info = callbacks_[channel];
36+
FlutterEmbedderMessageCallback message_callback = callback_info.first;
37+
38+
// Process the call, handling input blocking if requested.
39+
bool block_input = input_blocking_channels_.count(channel) > 0;
40+
if (block_input) {
41+
input_block_cb();
42+
}
43+
message_callback(window_, &message, callback_info.second);
44+
if (block_input) {
45+
input_unblock_cb();
46+
}
47+
}
48+
49+
void IncomingMessageDispatcher::SetMessageCallback(
50+
const std::string &channel, FlutterEmbedderMessageCallback callback,
51+
void *user_data) {
52+
if (!callback) {
53+
callbacks_.erase(channel);
54+
return;
55+
}
56+
callbacks_[channel] = std::make_pair(callback, user_data);
57+
}
58+
59+
void IncomingMessageDispatcher::EnableInputBlockingForChannel(
60+
const std::string &channel) {
61+
input_blocking_channels_.insert(channel);
62+
}
63+
64+
} // namespace flutter_desktop_embedding
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright 2018 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
#ifndef LIBRARY_COMMON_INTERNAL_INCOMING_MESSAGE_DISPATCHER_H_
15+
#define LIBRARY_COMMON_INTERNAL_INCOMING_MESSAGE_DISPATCHER_H_
16+
17+
#include <functional>
18+
#include <map>
19+
#include <set>
20+
#include <string>
21+
#include <utility>
22+
23+
#include "library/include/flutter_desktop_embedding/glfw/embedder.h"
24+
25+
namespace flutter_desktop_embedding {
26+
27+
// Manages per-channel registration of callbacks for handling messages from the
28+
// Flutter engine, and dispatching incoming messages to those handlers.
29+
class IncomingMessageDispatcher {
30+
public:
31+
// Creates a new IncomingMessageDispatcher. |window| must remain valid as long
32+
// as this object exists.
33+
explicit IncomingMessageDispatcher(FlutterWindowRef window);
34+
virtual ~IncomingMessageDispatcher();
35+
36+
// Prevent copying.
37+
IncomingMessageDispatcher(IncomingMessageDispatcher const &) = delete;
38+
IncomingMessageDispatcher &operator=(IncomingMessageDispatcher const &) =
39+
delete;
40+
41+
// Routes |message| to to the registered handler for its channel, if any.
42+
//
43+
// If input blocking has been enabled on that channel, wraps the call to the
44+
// handler with calls to the given callbacks to block and then unblock input.
45+
//
46+
// If no handler is registered for the message's channel, sends a
47+
// NotImplemented response to the engine.
48+
void HandleMessage(const FlutterEmbedderMessage &message,
49+
std::function<void(void)> input_block_cb = [] {},
50+
std::function<void(void)> input_unblock_cb = [] {});
51+
52+
// Registers a message callback for incoming messages from the Flutter
53+
// side on the specified channel. |callback| will be called with the message
54+
// and |user_data| any time a message arrives on that channel.
55+
//
56+
// Replaces any existing callback. Pass a null callback to unregister the
57+
// existing callback.
58+
void SetMessageCallback(const std::string &channel,
59+
FlutterEmbedderMessageCallback callback,
60+
void *user_data);
61+
62+
// Enables input blocking on the given channel name.
63+
//
64+
// If set, then the parent window should disable input callbacks
65+
// while waiting for the handler for messages on that channel to run.
66+
void EnableInputBlockingForChannel(const std::string &channel);
67+
68+
private:
69+
// Handle for interacting with the embedding API.
70+
// TODO: Provide an interface for the specific functionality needed.
71+
FlutterWindowRef window_;
72+
73+
// A map from channel names to the FlutterEmbedderMessageCallback that should
74+
// be called for incoming messages on that channel, along with the void* user
75+
// data to pass to it.
76+
std::map<std::string, std::pair<FlutterEmbedderMessageCallback, void *>>
77+
callbacks_;
78+
79+
// Channel names for which input blocking should be enabled during the call to
80+
// that channel's handler.
81+
std::set<std::string> input_blocking_channels_;
82+
};
83+
84+
} // namespace flutter_desktop_embedding
85+
86+
#endif // LIBRARY_COMMON_INTERNAL_INCOMING_MESSAGE_DISPATCHER_H_

0 commit comments

Comments
 (0)