Add windowing channel support to Linux embedder (#163180)
Implement flutter/windowing for the Linux embedder.
This commit is contained in:
parent
11d33f203e
commit
d8a57c61dd
@ -43869,6 +43869,11 @@ ORIGIN: ../../../flutter/shell/platform/linux/fl_view_test.cc + ../../../flutter
|
||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_window_state_monitor.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_window_state_monitor.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_window_state_monitor_test.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_windowing_channel.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_windowing_channel.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_windowing_handler.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_windowing_handler.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_windowing_handler_test.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/linux/key_mapping.g.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/linux/key_mapping.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/linux/key_mapping_test.cc + ../../../flutter/LICENSE
|
||||
@ -46813,6 +46818,11 @@ FILE: ../../../flutter/shell/platform/linux/fl_view_test.cc
|
||||
FILE: ../../../flutter/shell/platform/linux/fl_window_state_monitor.cc
|
||||
FILE: ../../../flutter/shell/platform/linux/fl_window_state_monitor.h
|
||||
FILE: ../../../flutter/shell/platform/linux/fl_window_state_monitor_test.cc
|
||||
FILE: ../../../flutter/shell/platform/linux/fl_windowing_channel.cc
|
||||
FILE: ../../../flutter/shell/platform/linux/fl_windowing_channel.h
|
||||
FILE: ../../../flutter/shell/platform/linux/fl_windowing_handler.cc
|
||||
FILE: ../../../flutter/shell/platform/linux/fl_windowing_handler.h
|
||||
FILE: ../../../flutter/shell/platform/linux/fl_windowing_handler_test.cc
|
||||
FILE: ../../../flutter/shell/platform/linux/key_mapping.g.cc
|
||||
FILE: ../../../flutter/shell/platform/linux/key_mapping.h
|
||||
FILE: ../../../flutter/shell/platform/linux/key_mapping_test.cc
|
||||
|
@ -1,8 +1,10 @@
|
||||
InheritParentConfig: true
|
||||
|
||||
# EnumCastOutOfRange warns about some common usages of GTK macros
|
||||
# Malloc generates false positives with g_autofree usage.
|
||||
Checks: >-
|
||||
-clang-analyzer-optin.core.EnumCastOutOfRange
|
||||
-clang-analyzer-optin.core.EnumCastOutOfRange,
|
||||
-clang-analyzer-unix.Malloc
|
||||
|
||||
CheckOptions:
|
||||
- key: readability-identifier-naming.EnumConstantCase
|
||||
|
@ -158,6 +158,8 @@ source_set("flutter_linux_sources") {
|
||||
"fl_view.cc",
|
||||
"fl_view_accessible.cc",
|
||||
"fl_window_state_monitor.cc",
|
||||
"fl_windowing_channel.cc",
|
||||
"fl_windowing_handler.cc",
|
||||
"key_mapping.g.cc",
|
||||
]
|
||||
|
||||
@ -248,6 +250,7 @@ executable("flutter_linux_unittests") {
|
||||
"fl_view_accessible_test.cc",
|
||||
"fl_view_test.cc",
|
||||
"fl_window_state_monitor_test.cc",
|
||||
"fl_windowing_handler_test.cc",
|
||||
"key_mapping_test.cc",
|
||||
"testing/fl_mock_binary_messenger.cc",
|
||||
"testing/fl_test.cc",
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <gdk/gdkx.h>
|
||||
#endif
|
||||
|
||||
#include "flutter/shell/platform/linux/fl_engine_private.h"
|
||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h"
|
||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_plugin_registry.h"
|
||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_view.h"
|
||||
@ -31,6 +32,15 @@ G_DEFINE_TYPE_WITH_CODE(FlApplication,
|
||||
GTK_TYPE_APPLICATION,
|
||||
G_ADD_PRIVATE(FlApplication))
|
||||
|
||||
// Called when the platform creates a window.
|
||||
static GtkWindow* create_window_cb(FlApplication* self, FlView* view) {
|
||||
GtkWindow* window;
|
||||
g_signal_emit(self, fl_application_signals[SIGNAL_CREATE_WINDOW], 0, view,
|
||||
&window);
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
// Called when the first frame is received.
|
||||
static void first_frame_cb(FlApplication* self, FlView* view) {
|
||||
GtkWidget* window = gtk_widget_get_toplevel(GTK_WIDGET(view));
|
||||
@ -94,6 +104,11 @@ static void fl_application_activate(GApplication* application) {
|
||||
self);
|
||||
gtk_widget_show(GTK_WIDGET(view));
|
||||
|
||||
FlWindowingHandler* windowing_handler =
|
||||
fl_engine_get_windowing_handler(fl_view_get_engine(view));
|
||||
g_signal_connect_swapped(windowing_handler, "create_window",
|
||||
G_CALLBACK(create_window_cb), self);
|
||||
|
||||
GtkWindow* window;
|
||||
g_signal_emit(self, fl_application_signals[SIGNAL_CREATE_WINDOW], 0, view,
|
||||
&window);
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "flutter/shell/platform/linux/fl_settings_handler.h"
|
||||
#include "flutter/shell/platform/linux/fl_texture_gl_private.h"
|
||||
#include "flutter/shell/platform/linux/fl_texture_registrar_private.h"
|
||||
#include "flutter/shell/platform/linux/fl_windowing_handler.h"
|
||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_plugin_registry.h"
|
||||
|
||||
// Unique number associated with platform tasks.
|
||||
@ -58,6 +59,9 @@ struct _FlEngine {
|
||||
// Implements the flutter/platform channel.
|
||||
FlPlatformHandler* platform_handler;
|
||||
|
||||
// Implements the flutter/windowing channel.
|
||||
FlWindowingHandler* windowing_handler;
|
||||
|
||||
// Process keyboard events.
|
||||
FlKeyboardManager* keyboard_manager;
|
||||
|
||||
@ -487,6 +491,7 @@ static void fl_engine_dispose(GObject* object) {
|
||||
g_clear_object(&self->binary_messenger);
|
||||
g_clear_object(&self->settings_handler);
|
||||
g_clear_object(&self->platform_handler);
|
||||
g_clear_object(&self->windowing_handler);
|
||||
g_clear_object(&self->keyboard_manager);
|
||||
g_clear_object(&self->text_input_handler);
|
||||
g_clear_object(&self->keyboard_handler);
|
||||
@ -702,6 +707,7 @@ gboolean fl_engine_start(FlEngine* self, GError** error) {
|
||||
fl_settings_handler_start(self->settings_handler, settings);
|
||||
|
||||
self->platform_handler = fl_platform_handler_new(self->binary_messenger);
|
||||
self->windowing_handler = fl_windowing_handler_new(self);
|
||||
|
||||
setup_keyboard(self);
|
||||
|
||||
@ -1295,6 +1301,11 @@ void fl_engine_request_app_exit(FlEngine* self) {
|
||||
fl_platform_handler_request_app_exit(self->platform_handler);
|
||||
}
|
||||
|
||||
FlWindowingHandler* fl_engine_get_windowing_handler(FlEngine* self) {
|
||||
g_return_val_if_fail(FL_IS_ENGINE(self), nullptr);
|
||||
return self->windowing_handler;
|
||||
}
|
||||
|
||||
FlKeyboardManager* fl_engine_get_keyboard_manager(FlEngine* self) {
|
||||
g_return_val_if_fail(FL_IS_ENGINE(self), nullptr);
|
||||
return self->keyboard_manager;
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "flutter/shell/platform/linux/fl_renderer.h"
|
||||
#include "flutter/shell/platform/linux/fl_task_runner.h"
|
||||
#include "flutter/shell/platform/linux/fl_text_input_handler.h"
|
||||
#include "flutter/shell/platform/linux/fl_windowing_handler.h"
|
||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h"
|
||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h"
|
||||
|
||||
@ -576,13 +577,23 @@ void fl_engine_update_accessibility_features(FlEngine* engine, int32_t flags);
|
||||
*/
|
||||
void fl_engine_request_app_exit(FlEngine* engine);
|
||||
|
||||
/**
|
||||
* fl_engine_get_windowing_handler:
|
||||
* @engine: an #FlEngine.
|
||||
*
|
||||
* Gets the windowing handler used by this engine.
|
||||
*
|
||||
* Returns: an #FlWindowingHandler.
|
||||
*/
|
||||
FlWindowingHandler* fl_engine_get_windowing_handler(FlEngine* engine);
|
||||
|
||||
/**
|
||||
* fl_engine_get_keyboard_manager:
|
||||
* @engine: an #FlEngine.
|
||||
*
|
||||
* Gets the keyboard manager used by this engine.
|
||||
*
|
||||
* Returns: a #FlKeyboardManager.
|
||||
* Returns: an #FlKeyboardManager.
|
||||
*/
|
||||
FlKeyboardManager* fl_engine_get_keyboard_manager(FlEngine* engine);
|
||||
|
||||
@ -592,7 +603,7 @@ FlKeyboardManager* fl_engine_get_keyboard_manager(FlEngine* engine);
|
||||
*
|
||||
* Gets the text input handler used by this engine.
|
||||
*
|
||||
* Returns: a #FlTextInputHandler.
|
||||
* Returns: an #FlTextInputHandler.
|
||||
*/
|
||||
FlTextInputHandler* fl_engine_get_text_input_handler(FlEngine* engine);
|
||||
|
||||
@ -602,7 +613,7 @@ FlTextInputHandler* fl_engine_get_text_input_handler(FlEngine* engine);
|
||||
*
|
||||
* Gets the mouse cursor handler used by this engine.
|
||||
*
|
||||
* Returns: a #FlMouseCursorHandler.
|
||||
* Returns: an #FlMouseCursorHandler.
|
||||
*/
|
||||
FlMouseCursorHandler* fl_engine_get_mouse_cursor_handler(FlEngine* engine);
|
||||
|
||||
|
@ -765,7 +765,7 @@ G_MODULE_EXPORT FlView* fl_view_new(FlDartProject* project) {
|
||||
fl_engine_set_update_semantics_handler(self->engine, update_semantics_cb,
|
||||
self, nullptr);
|
||||
self->on_pre_engine_restart_cb_id =
|
||||
g_signal_connect_swapped(engine, "on-pre-engine-restart",
|
||||
g_signal_connect_swapped(self->engine, "on-pre-engine-restart",
|
||||
G_CALLBACK(on_pre_engine_restart_cb), self);
|
||||
|
||||
g_signal_connect_swapped(self->gl_area, "create-context",
|
||||
@ -790,7 +790,7 @@ G_MODULE_EXPORT FlView* fl_view_new_for_engine(FlEngine* engine) {
|
||||
g_signal_connect_swapped(engine, "on-pre-engine-restart",
|
||||
G_CALLBACK(on_pre_engine_restart_cb), self);
|
||||
|
||||
self->view_id = fl_engine_add_view(self->engine, 1, 1, 1.0, self->cancellable,
|
||||
self->view_id = fl_engine_add_view(engine, 1, 1, 1.0, self->cancellable,
|
||||
view_added_cb, self);
|
||||
fl_renderer_add_renderable(FL_RENDERER(self->renderer), self->view_id,
|
||||
FL_RENDERABLE(self));
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "flutter/shell/platform/linux/fl_engine_private.h"
|
||||
#include "flutter/shell/platform/linux/testing/fl_test.h"
|
||||
#include "flutter/shell/platform/linux/testing/fl_test_gtk_logs.h"
|
||||
#include "flutter/shell/platform/linux/testing/mock_window.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
@ -136,8 +137,8 @@ TEST(FlViewTest, ViewDestroy) {
|
||||
int64_t implicit_view_id = fl_view_get_id(implicit_view);
|
||||
int64_t secondary_view_id = fl_view_get_id(secondary_view);
|
||||
|
||||
gtk_widget_destroy(GTK_WIDGET(secondary_view));
|
||||
gtk_widget_destroy(GTK_WIDGET(implicit_view));
|
||||
fl_gtk_widget_destroy(GTK_WIDGET(secondary_view));
|
||||
fl_gtk_widget_destroy(GTK_WIDGET(implicit_view));
|
||||
|
||||
EXPECT_EQ(removed_views->len, 2u);
|
||||
EXPECT_EQ(GPOINTER_TO_INT(g_ptr_array_index(removed_views, 0)),
|
||||
@ -165,6 +166,6 @@ TEST(FlViewTest, ViewDestroyError) {
|
||||
|
||||
FlView* secondary_view = fl_view_new_for_engine(engine);
|
||||
|
||||
gtk_widget_destroy(GTK_WIDGET(secondary_view));
|
||||
gtk_widget_destroy(GTK_WIDGET(implicit_view));
|
||||
fl_gtk_widget_destroy(GTK_WIDGET(secondary_view));
|
||||
fl_gtk_widget_destroy(GTK_WIDGET(implicit_view));
|
||||
}
|
||||
|
296
engine/src/flutter/shell/platform/linux/fl_windowing_channel.cc
Normal file
296
engine/src/flutter/shell/platform/linux/fl_windowing_channel.cc
Normal file
@ -0,0 +1,296 @@
|
||||
// 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 "flutter/shell/platform/linux/fl_windowing_channel.h"
|
||||
|
||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h"
|
||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h"
|
||||
|
||||
static constexpr char kChannelName[] = "flutter/windowing";
|
||||
static constexpr char kBadArgumentsError[] = "Bad Arguments";
|
||||
|
||||
static constexpr char kCreateRegularMethod[] = "createRegular";
|
||||
static constexpr char kModifyRegularMethod[] = "modifyRegular";
|
||||
static constexpr char kDestroyWindowMethod[] = "destroyWindow";
|
||||
|
||||
static constexpr char kSizeKey[] = "size";
|
||||
static constexpr char kMinSizeKey[] = "minSize";
|
||||
static constexpr char kMaxSizeKey[] = "maxSize";
|
||||
static constexpr char kTitleKey[] = "title";
|
||||
static constexpr char kStateKey[] = "state";
|
||||
static constexpr char kViewIdKey[] = "viewId";
|
||||
|
||||
struct _FlWindowingChannel {
|
||||
GObject parent_instance;
|
||||
|
||||
FlMethodChannel* channel;
|
||||
|
||||
// Handlers for incoming method calls.
|
||||
FlWindowingChannelVTable* vtable;
|
||||
|
||||
// User data to pass to method call handlers.
|
||||
gpointer user_data;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(FlWindowingChannel, fl_windowing_channel, G_TYPE_OBJECT)
|
||||
|
||||
// Returns TRUE if [args] is a valid size argument.
|
||||
static gboolean is_valid_size_argument(FlValue* value) {
|
||||
return fl_value_get_type(value) == FL_VALUE_TYPE_LIST &&
|
||||
fl_value_get_length(value) == 2 &&
|
||||
fl_value_get_type(fl_value_get_list_value(value, 0)) ==
|
||||
FL_VALUE_TYPE_FLOAT &&
|
||||
fl_value_get_type(fl_value_get_list_value(value, 1)) ==
|
||||
FL_VALUE_TYPE_FLOAT;
|
||||
}
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FlWindowingSize, g_free)
|
||||
|
||||
static FlWindowingSize* parse_size_value(FlValue* value) {
|
||||
FlWindowingSize* size = g_new0(FlWindowingSize, 1);
|
||||
size->width = fl_value_get_float(fl_value_get_list_value(value, 0));
|
||||
size->height = fl_value_get_float(fl_value_get_list_value(value, 1));
|
||||
return size;
|
||||
}
|
||||
|
||||
static gboolean parse_window_state_value(FlValue* value, FlWindowState* state) {
|
||||
if (fl_value_get_type(value) != FL_VALUE_TYPE_STRING) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
const gchar* text = fl_value_get_string(value);
|
||||
if (strcmp(text, "WindowState.restored") == 0) {
|
||||
*state = FL_WINDOW_STATE_RESTORED;
|
||||
return TRUE;
|
||||
} else if (strcmp(text, "WindowState.maximized") == 0) {
|
||||
*state = FL_WINDOW_STATE_MAXIMIZED;
|
||||
return TRUE;
|
||||
} else if (strcmp(text, "WindowState.minimized") == 0) {
|
||||
*state = FL_WINDOW_STATE_MINIMIZED;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static const gchar* window_state_to_string(FlWindowState state) {
|
||||
switch (state) {
|
||||
case FL_WINDOW_STATE_UNDEFINED:
|
||||
return nullptr;
|
||||
case FL_WINDOW_STATE_RESTORED:
|
||||
return "WindowState.restored";
|
||||
case FL_WINDOW_STATE_MAXIMIZED:
|
||||
return "WindowState.maximized";
|
||||
case FL_WINDOW_STATE_MINIMIZED:
|
||||
return "WindowState.minimized";
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Called when a regular window should be created.
|
||||
static FlMethodResponse* create_regular(FlWindowingChannel* self,
|
||||
FlValue* args) {
|
||||
if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) {
|
||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||
kBadArgumentsError, "Argument map missing or malformed", nullptr));
|
||||
}
|
||||
|
||||
FlValue* size_value = fl_value_lookup_string(args, kSizeKey);
|
||||
if (size_value == nullptr || !is_valid_size_argument(size_value)) {
|
||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||
kBadArgumentsError, "Missing/invalid size argument", nullptr));
|
||||
}
|
||||
g_autoptr(FlWindowingSize) size = parse_size_value(size_value);
|
||||
|
||||
FlValue* min_size_value = fl_value_lookup_string(args, kMinSizeKey);
|
||||
g_autoptr(FlWindowingSize) min_size = nullptr;
|
||||
if (min_size_value != nullptr) {
|
||||
if (!is_valid_size_argument(min_size_value)) {
|
||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||
kBadArgumentsError, "Invalid minSize argument", nullptr));
|
||||
}
|
||||
min_size = parse_size_value(min_size_value);
|
||||
}
|
||||
|
||||
FlValue* max_size_value = fl_value_lookup_string(args, kMaxSizeKey);
|
||||
g_autoptr(FlWindowingSize) max_size = nullptr;
|
||||
if (max_size_value != nullptr) {
|
||||
if (!is_valid_size_argument(max_size_value)) {
|
||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||
kBadArgumentsError, "Invalid maxSize argument", nullptr));
|
||||
}
|
||||
max_size = parse_size_value(max_size_value);
|
||||
}
|
||||
|
||||
FlValue* title_value = fl_value_lookup_string(args, kTitleKey);
|
||||
const gchar* title = nullptr;
|
||||
if (title_value != nullptr) {
|
||||
if (fl_value_get_type(title_value) != FL_VALUE_TYPE_STRING) {
|
||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||
kBadArgumentsError, "Invalid title argument", nullptr));
|
||||
}
|
||||
title = fl_value_get_string(title_value);
|
||||
}
|
||||
FlWindowState state = FL_WINDOW_STATE_UNDEFINED;
|
||||
FlValue* state_value = fl_value_lookup_string(args, kStateKey);
|
||||
if (state_value != nullptr) {
|
||||
if (!parse_window_state_value(state_value, &state)) {
|
||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||
kBadArgumentsError, "Invalid state argument", nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
return self->vtable->create_regular(size, min_size, max_size, title, state,
|
||||
self->user_data);
|
||||
}
|
||||
|
||||
// Called when a regular window should be created.
|
||||
static FlMethodResponse* modify_regular(FlWindowingChannel* self,
|
||||
FlValue* args) {
|
||||
if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) {
|
||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||
kBadArgumentsError, "Argument map missing or malformed", nullptr));
|
||||
}
|
||||
|
||||
FlValue* view_id_value = fl_value_lookup_string(args, kViewIdKey);
|
||||
if (view_id_value == nullptr ||
|
||||
fl_value_get_type(view_id_value) != FL_VALUE_TYPE_INT) {
|
||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||
kBadArgumentsError, "Missing/invalid viewId argument", nullptr));
|
||||
}
|
||||
int64_t view_id = fl_value_get_int(view_id_value);
|
||||
|
||||
g_autoptr(FlWindowingSize) size = nullptr;
|
||||
FlValue* size_value = fl_value_lookup_string(args, kSizeKey);
|
||||
if (size_value != nullptr) {
|
||||
if (!is_valid_size_argument(size_value)) {
|
||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||
kBadArgumentsError, "Invalid size argument", nullptr));
|
||||
}
|
||||
size = parse_size_value(size_value);
|
||||
}
|
||||
FlValue* title_value = fl_value_lookup_string(args, kTitleKey);
|
||||
const gchar* title = nullptr;
|
||||
if (title_value != nullptr) {
|
||||
if (fl_value_get_type(title_value) != FL_VALUE_TYPE_STRING) {
|
||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||
kBadArgumentsError, "Invalid title argument", nullptr));
|
||||
}
|
||||
title = fl_value_get_string(title_value);
|
||||
}
|
||||
FlWindowState state = FL_WINDOW_STATE_UNDEFINED;
|
||||
FlValue* state_value = fl_value_lookup_string(args, kStateKey);
|
||||
if (state_value != nullptr) {
|
||||
if (!parse_window_state_value(state_value, &state)) {
|
||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||
kBadArgumentsError, "Invalid state argument", nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
return self->vtable->modify_regular(view_id, size, title, state,
|
||||
self->user_data);
|
||||
}
|
||||
|
||||
// Called when a window should be destroyed.
|
||||
static FlMethodResponse* destroy_window(FlWindowingChannel* self,
|
||||
FlValue* args) {
|
||||
if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) {
|
||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||
kBadArgumentsError, "Argument map missing or malformed", nullptr));
|
||||
}
|
||||
|
||||
FlValue* view_id_value = fl_value_lookup_string(args, kViewIdKey);
|
||||
if (view_id_value == nullptr ||
|
||||
fl_value_get_type(view_id_value) != FL_VALUE_TYPE_INT) {
|
||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||
kBadArgumentsError, "Missing/invalid viewId argument", nullptr));
|
||||
}
|
||||
int64_t view_id = fl_value_get_int(view_id_value);
|
||||
|
||||
return self->vtable->destroy_window(view_id, self->user_data);
|
||||
}
|
||||
|
||||
// Called when a method call is received from Flutter.
|
||||
static void method_call_cb(FlMethodChannel* channel,
|
||||
FlMethodCall* method_call,
|
||||
gpointer user_data) {
|
||||
FlWindowingChannel* self = FL_WINDOWING_CHANNEL(user_data);
|
||||
|
||||
const gchar* method = fl_method_call_get_name(method_call);
|
||||
FlValue* args = fl_method_call_get_args(method_call);
|
||||
g_autoptr(FlMethodResponse) response = nullptr;
|
||||
|
||||
if (strcmp(method, kCreateRegularMethod) == 0) {
|
||||
response = create_regular(self, args);
|
||||
} else if (strcmp(method, kModifyRegularMethod) == 0) {
|
||||
response = modify_regular(self, args);
|
||||
} else if (strcmp(method, kDestroyWindowMethod) == 0) {
|
||||
response = destroy_window(self, args);
|
||||
} else {
|
||||
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
|
||||
}
|
||||
|
||||
if (response != nullptr) {
|
||||
g_autoptr(GError) error = nullptr;
|
||||
if (!fl_method_call_respond(method_call, response, &error)) {
|
||||
g_warning("Failed to send method call response: %s", error->message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fl_windowing_channel_dispose(GObject* object) {
|
||||
FlWindowingChannel* self = FL_WINDOWING_CHANNEL(object);
|
||||
|
||||
g_clear_object(&self->channel);
|
||||
|
||||
G_OBJECT_CLASS(fl_windowing_channel_parent_class)->dispose(object);
|
||||
}
|
||||
|
||||
static void fl_windowing_channel_class_init(FlWindowingChannelClass* klass) {
|
||||
G_OBJECT_CLASS(klass)->dispose = fl_windowing_channel_dispose;
|
||||
}
|
||||
|
||||
static void fl_windowing_channel_init(FlWindowingChannel* self) {}
|
||||
|
||||
FlWindowingChannel* fl_windowing_channel_new(FlBinaryMessenger* messenger,
|
||||
FlWindowingChannelVTable* vtable,
|
||||
gpointer user_data) {
|
||||
FlWindowingChannel* self = FL_WINDOWING_CHANNEL(
|
||||
g_object_new(fl_windowing_channel_get_type(), nullptr));
|
||||
|
||||
self->vtable = vtable;
|
||||
self->user_data = user_data;
|
||||
|
||||
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
|
||||
self->channel =
|
||||
fl_method_channel_new(messenger, kChannelName, FL_METHOD_CODEC(codec));
|
||||
fl_method_channel_set_method_call_handler(self->channel, method_call_cb, self,
|
||||
nullptr);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
FlMethodResponse* fl_windowing_channel_make_create_regular_response(
|
||||
int64_t view_id,
|
||||
FlWindowingSize* size,
|
||||
FlWindowState state) {
|
||||
g_autoptr(FlValue) result = fl_value_new_map();
|
||||
fl_value_set_string_take(result, kViewIdKey, fl_value_new_int(view_id));
|
||||
g_autoptr(FlValue) size_value = fl_value_new_list();
|
||||
fl_value_append_take(size_value, fl_value_new_float(size->width));
|
||||
fl_value_append_take(size_value, fl_value_new_float(size->height));
|
||||
fl_value_set_string(result, kSizeKey, size_value);
|
||||
fl_value_set_string_take(result, kStateKey,
|
||||
fl_value_new_string(window_state_to_string(state)));
|
||||
return FL_METHOD_RESPONSE(fl_method_success_response_new(result));
|
||||
}
|
||||
|
||||
FlMethodResponse* fl_windowing_channel_make_modify_regular_response() {
|
||||
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
||||
}
|
||||
|
||||
FlMethodResponse* fl_windowing_channel_make_destroy_window_response() {
|
||||
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
||||
}
|
@ -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_LINUX_FL_WINDOWING_CHANNEL_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_LINUX_FL_WINDOWING_CHANNEL_H_
|
||||
|
||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h"
|
||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_response.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
G_DECLARE_FINAL_TYPE(FlWindowingChannel,
|
||||
fl_windowing_channel,
|
||||
FL,
|
||||
WINDOWING_CHANNEL,
|
||||
GObject);
|
||||
|
||||
// States that a window can be in.
|
||||
typedef enum {
|
||||
FL_WINDOW_STATE_UNDEFINED,
|
||||
FL_WINDOW_STATE_RESTORED,
|
||||
FL_WINDOW_STATE_MAXIMIZED,
|
||||
FL_WINDOW_STATE_MINIMIZED
|
||||
} FlWindowState;
|
||||
|
||||
// Size dimensions used in windowing channel.
|
||||
typedef struct {
|
||||
double width;
|
||||
double height;
|
||||
} FlWindowingSize;
|
||||
|
||||
/**
|
||||
* FlWindowingChannel:
|
||||
*
|
||||
* #FlWindowingChannel is a channel that implements the shell side
|
||||
* of SystemChannels.windowing from the Flutter services library.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
FlMethodResponse* (*create_regular)(FlWindowingSize* size,
|
||||
FlWindowingSize* min_size,
|
||||
FlWindowingSize* max_size,
|
||||
const gchar* title,
|
||||
FlWindowState state,
|
||||
gpointer user_data);
|
||||
FlMethodResponse* (*modify_regular)(int64_t view_id,
|
||||
FlWindowingSize* size,
|
||||
const gchar* title,
|
||||
FlWindowState state,
|
||||
gpointer user_data);
|
||||
FlMethodResponse* (*destroy_window)(int64_t view_id, gpointer user_data);
|
||||
} FlWindowingChannelVTable;
|
||||
|
||||
/**
|
||||
* fl_windowing_channel_new:
|
||||
* @messenger: an #FlBinaryMessenger
|
||||
* @vtable: callbacks for incoming method calls.
|
||||
* @user_data: data to pass in callbacks.
|
||||
*
|
||||
* Creates a new channel that sends handled windowing requests from the
|
||||
* platform.
|
||||
*
|
||||
* Returns: a new #FlWindowingChannel
|
||||
*/
|
||||
FlWindowingChannel* fl_windowing_channel_new(FlBinaryMessenger* messenger,
|
||||
FlWindowingChannelVTable* vtable,
|
||||
gpointer user_data);
|
||||
|
||||
FlMethodResponse* fl_windowing_channel_make_create_regular_response(
|
||||
int64_t view_id,
|
||||
FlWindowingSize* size,
|
||||
FlWindowState state);
|
||||
|
||||
FlMethodResponse* fl_windowing_channel_make_modify_regular_response();
|
||||
|
||||
FlMethodResponse* fl_windowing_channel_make_destroy_window_response();
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_WINDOWING_CHANNEL_H_
|
275
engine/src/flutter/shell/platform/linux/fl_windowing_handler.cc
Normal file
275
engine/src/flutter/shell/platform/linux/fl_windowing_handler.cc
Normal file
@ -0,0 +1,275 @@
|
||||
// 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 "flutter/shell/platform/linux/fl_windowing_handler.h"
|
||||
|
||||
#include "flutter/shell/platform/linux/fl_windowing_channel.h"
|
||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_view.h"
|
||||
|
||||
typedef struct {
|
||||
GWeakRef engine;
|
||||
|
||||
FlWindowingChannel* channel;
|
||||
|
||||
GHashTable* windows_by_view_id;
|
||||
} FlWindowingHandlerPrivate;
|
||||
|
||||
enum { SIGNAL_CREATE_WINDOW, LAST_SIGNAL };
|
||||
|
||||
static guint signals[LAST_SIGNAL];
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE(FlWindowingHandler,
|
||||
fl_windowing_handler,
|
||||
G_TYPE_OBJECT)
|
||||
|
||||
typedef struct {
|
||||
GtkWindow* window;
|
||||
FlView* view;
|
||||
guint first_frame_cb_id;
|
||||
} WindowData;
|
||||
|
||||
static WindowData* window_data_new(GtkWindow* window, FlView* view) {
|
||||
WindowData* data = g_new0(WindowData, 1);
|
||||
data->window = GTK_WINDOW(g_object_ref(window));
|
||||
data->view = FL_VIEW(g_object_ref(view));
|
||||
return data;
|
||||
}
|
||||
|
||||
static void window_data_free(WindowData* data) {
|
||||
g_signal_handler_disconnect(data->view, data->first_frame_cb_id);
|
||||
g_object_unref(data->window);
|
||||
g_object_unref(data->view);
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
// Called when the first frame is received.
|
||||
static void first_frame_cb(FlView* view, WindowData* data) {
|
||||
gtk_window_present(data->window);
|
||||
}
|
||||
|
||||
static WindowData* get_window_data(FlWindowingHandler* self, int64_t view_id) {
|
||||
FlWindowingHandlerPrivate* priv =
|
||||
reinterpret_cast<FlWindowingHandlerPrivate*>(
|
||||
fl_windowing_handler_get_instance_private(self));
|
||||
|
||||
return static_cast<WindowData*>(
|
||||
g_hash_table_lookup(priv->windows_by_view_id, GINT_TO_POINTER(view_id)));
|
||||
}
|
||||
|
||||
static GtkWindow* fl_windowing_handler_create_window(
|
||||
FlWindowingHandler* handler,
|
||||
FlView* view) {
|
||||
GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
|
||||
gtk_widget_show(GTK_WIDGET(view));
|
||||
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
|
||||
|
||||
return GTK_WINDOW(window);
|
||||
}
|
||||
|
||||
static FlMethodResponse* create_regular(FlWindowingSize* size,
|
||||
FlWindowingSize* min_size,
|
||||
FlWindowingSize* max_size,
|
||||
const gchar* title,
|
||||
FlWindowState state,
|
||||
gpointer user_data) {
|
||||
FlWindowingHandler* self = FL_WINDOWING_HANDLER(user_data);
|
||||
FlWindowingHandlerPrivate* priv =
|
||||
reinterpret_cast<FlWindowingHandlerPrivate*>(
|
||||
fl_windowing_handler_get_instance_private(self));
|
||||
|
||||
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&priv->engine));
|
||||
if (engine == nullptr) {
|
||||
return FL_METHOD_RESPONSE(
|
||||
fl_method_error_response_new("Internal error", "No engine", nullptr));
|
||||
}
|
||||
|
||||
FlView* view = fl_view_new_for_engine(engine);
|
||||
|
||||
GtkWindow* window;
|
||||
g_signal_emit(self, signals[SIGNAL_CREATE_WINDOW], 0, view, &window);
|
||||
if (window == nullptr) {
|
||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||
"Internal error", "Failed to create window", nullptr));
|
||||
}
|
||||
|
||||
gtk_window_set_default_size(GTK_WINDOW(window), size->width, size->height);
|
||||
if (title != nullptr) {
|
||||
gtk_window_set_title(GTK_WINDOW(window), title);
|
||||
}
|
||||
switch (state) {
|
||||
case FL_WINDOW_STATE_MAXIMIZED:
|
||||
gtk_window_maximize(GTK_WINDOW(window));
|
||||
break;
|
||||
case FL_WINDOW_STATE_MINIMIZED:
|
||||
gtk_window_iconify(GTK_WINDOW(window));
|
||||
break;
|
||||
case FL_WINDOW_STATE_RESTORED:
|
||||
case FL_WINDOW_STATE_UNDEFINED:
|
||||
break;
|
||||
}
|
||||
|
||||
GdkGeometry geometry;
|
||||
GdkWindowHints geometry_mask = static_cast<GdkWindowHints>(0);
|
||||
if (min_size != nullptr) {
|
||||
geometry.min_width = min_size->width;
|
||||
geometry.min_height = min_size->height;
|
||||
geometry_mask =
|
||||
static_cast<GdkWindowHints>(geometry_mask | GDK_HINT_MIN_SIZE);
|
||||
}
|
||||
if (max_size != nullptr) {
|
||||
geometry.max_width = max_size->width;
|
||||
geometry.max_height = max_size->height;
|
||||
geometry_mask =
|
||||
static_cast<GdkWindowHints>(geometry_mask | GDK_HINT_MAX_SIZE);
|
||||
}
|
||||
if (geometry_mask != 0) {
|
||||
gtk_window_set_geometry_hints(GTK_WINDOW(window), nullptr, &geometry,
|
||||
geometry_mask);
|
||||
}
|
||||
|
||||
WindowData* data = window_data_new(GTK_WINDOW(window), view);
|
||||
data->first_frame_cb_id =
|
||||
g_signal_connect(view, "first-frame", G_CALLBACK(first_frame_cb), data);
|
||||
|
||||
// Make the resources for the view so rendering can start.
|
||||
// We'll show the view when we have the first frame.
|
||||
gtk_widget_realize(GTK_WIDGET(view));
|
||||
|
||||
g_hash_table_insert(priv->windows_by_view_id,
|
||||
GINT_TO_POINTER(fl_view_get_id(view)), data);
|
||||
|
||||
// We don't know the current size and dimensions, so just reflect back what
|
||||
// was requested.
|
||||
FlWindowingSize* initial_size = size;
|
||||
FlWindowState initial_state = state;
|
||||
if (initial_state == FL_WINDOW_STATE_UNDEFINED) {
|
||||
initial_state = FL_WINDOW_STATE_RESTORED;
|
||||
}
|
||||
|
||||
return fl_windowing_channel_make_create_regular_response(
|
||||
fl_view_get_id(view), initial_size, initial_state);
|
||||
}
|
||||
|
||||
static FlMethodResponse* modify_regular(int64_t view_id,
|
||||
FlWindowingSize* size,
|
||||
const gchar* title,
|
||||
FlWindowState state,
|
||||
gpointer user_data) {
|
||||
FlWindowingHandler* self = FL_WINDOWING_HANDLER(user_data);
|
||||
|
||||
WindowData* data = get_window_data(self, view_id);
|
||||
if (data == nullptr) {
|
||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||
"Bad Arguments", "No window with given view ID", nullptr));
|
||||
}
|
||||
|
||||
if (size != nullptr) {
|
||||
gtk_window_resize(data->window, size->width, size->height);
|
||||
}
|
||||
|
||||
if (title != nullptr) {
|
||||
gtk_window_set_title(data->window, title);
|
||||
}
|
||||
|
||||
GdkWindowState window_state;
|
||||
switch (state) {
|
||||
case FL_WINDOW_STATE_RESTORED:
|
||||
if (gtk_window_is_maximized(data->window)) {
|
||||
gtk_window_unmaximize(data->window);
|
||||
}
|
||||
window_state =
|
||||
gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(data->window)));
|
||||
if (window_state & GDK_WINDOW_STATE_ICONIFIED) {
|
||||
gtk_window_deiconify(data->window);
|
||||
}
|
||||
break;
|
||||
case FL_WINDOW_STATE_MAXIMIZED:
|
||||
gtk_window_maximize(data->window);
|
||||
break;
|
||||
case FL_WINDOW_STATE_MINIMIZED:
|
||||
gtk_window_iconify(data->window);
|
||||
break;
|
||||
case FL_WINDOW_STATE_UNDEFINED:
|
||||
break;
|
||||
}
|
||||
|
||||
return fl_windowing_channel_make_modify_regular_response();
|
||||
}
|
||||
|
||||
static FlMethodResponse* destroy_window(int64_t view_id, gpointer user_data) {
|
||||
FlWindowingHandler* self = FL_WINDOWING_HANDLER(user_data);
|
||||
FlWindowingHandlerPrivate* priv =
|
||||
reinterpret_cast<FlWindowingHandlerPrivate*>(
|
||||
fl_windowing_handler_get_instance_private(self));
|
||||
|
||||
WindowData* data = get_window_data(self, view_id);
|
||||
if (data == nullptr) {
|
||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||
"Bad Arguments", "No window with given view ID", nullptr));
|
||||
}
|
||||
|
||||
gtk_widget_destroy(GTK_WIDGET(data->window));
|
||||
|
||||
g_hash_table_remove(priv->windows_by_view_id, GINT_TO_POINTER(view_id));
|
||||
|
||||
return fl_windowing_channel_make_destroy_window_response();
|
||||
}
|
||||
|
||||
static void fl_windowing_handler_dispose(GObject* object) {
|
||||
FlWindowingHandler* self = FL_WINDOWING_HANDLER(object);
|
||||
FlWindowingHandlerPrivate* priv =
|
||||
reinterpret_cast<FlWindowingHandlerPrivate*>(
|
||||
fl_windowing_handler_get_instance_private(self));
|
||||
|
||||
g_weak_ref_clear(&priv->engine);
|
||||
g_clear_object(&priv->channel);
|
||||
g_clear_pointer(&priv->windows_by_view_id, g_hash_table_unref);
|
||||
|
||||
G_OBJECT_CLASS(fl_windowing_handler_parent_class)->dispose(object);
|
||||
}
|
||||
|
||||
static void fl_windowing_handler_class_init(FlWindowingHandlerClass* klass) {
|
||||
G_OBJECT_CLASS(klass)->dispose = fl_windowing_handler_dispose;
|
||||
|
||||
klass->create_window = fl_windowing_handler_create_window;
|
||||
|
||||
signals[SIGNAL_CREATE_WINDOW] = g_signal_new(
|
||||
"create-window", fl_windowing_handler_get_type(), G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET(FlWindowingHandlerClass, create_window),
|
||||
g_signal_accumulator_first_wins, nullptr, nullptr, GTK_TYPE_WINDOW, 1,
|
||||
fl_view_get_type());
|
||||
}
|
||||
|
||||
static void fl_windowing_handler_init(FlWindowingHandler* self) {
|
||||
FlWindowingHandlerPrivate* priv =
|
||||
reinterpret_cast<FlWindowingHandlerPrivate*>(
|
||||
fl_windowing_handler_get_instance_private(self));
|
||||
|
||||
priv->windows_by_view_id =
|
||||
g_hash_table_new_full(g_direct_hash, g_direct_equal, nullptr,
|
||||
reinterpret_cast<GDestroyNotify>(window_data_free));
|
||||
}
|
||||
|
||||
static FlWindowingChannelVTable windowing_channel_vtable = {
|
||||
.create_regular = create_regular,
|
||||
.modify_regular = modify_regular,
|
||||
.destroy_window = destroy_window,
|
||||
};
|
||||
|
||||
FlWindowingHandler* fl_windowing_handler_new(FlEngine* engine) {
|
||||
g_return_val_if_fail(FL_IS_ENGINE(engine), nullptr);
|
||||
|
||||
FlWindowingHandler* self = FL_WINDOWING_HANDLER(
|
||||
g_object_new(fl_windowing_handler_get_type(), nullptr));
|
||||
FlWindowingHandlerPrivate* priv =
|
||||
reinterpret_cast<FlWindowingHandlerPrivate*>(
|
||||
fl_windowing_handler_get_instance_private(self));
|
||||
|
||||
g_weak_ref_init(&priv->engine, engine);
|
||||
priv->channel = fl_windowing_channel_new(
|
||||
fl_engine_get_binary_messenger(engine), &windowing_channel_vtable, self);
|
||||
|
||||
return self;
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
// 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_LINUX_FL_WINDOWING_HANDLER_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_LINUX_FL_WINDOWING_HANDLER_H_
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h"
|
||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_view.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
G_DECLARE_DERIVABLE_TYPE(FlWindowingHandler,
|
||||
fl_windowing_handler,
|
||||
FL,
|
||||
WINDOWING_HANDLER,
|
||||
GObject);
|
||||
|
||||
struct _FlWindowingHandlerClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
GtkWindow* (*create_window)(FlWindowingHandler* handler, FlView* view);
|
||||
};
|
||||
|
||||
/**
|
||||
* FlWindowingHandler:
|
||||
*
|
||||
* #FlWindowingHandler is a handler that implements the shell side
|
||||
* of SystemChannels.windowing from the Flutter services library.
|
||||
*/
|
||||
|
||||
/**
|
||||
* fl_windowing_handler_new:
|
||||
* @engine: an #FlEngine.
|
||||
*
|
||||
* Creates a new handler that implements SystemChannels.windowing from the
|
||||
* Flutter services library.
|
||||
*
|
||||
* Returns: a new #FlWindowingHandler
|
||||
*/
|
||||
FlWindowingHandler* fl_windowing_handler_new(FlEngine* engine);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_WINDOWING_HANDLER_H_
|
@ -0,0 +1,640 @@
|
||||
// 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 "flutter/shell/platform/linux/fl_windowing_handler.h"
|
||||
#include "flutter/shell/platform/linux/fl_binary_messenger_private.h"
|
||||
#include "flutter/shell/platform/linux/fl_engine_private.h"
|
||||
#include "flutter/shell/platform/linux/testing/fl_mock_binary_messenger.h"
|
||||
#include "flutter/shell/platform/linux/testing/fl_test_gtk_logs.h"
|
||||
#include "flutter/shell/platform/linux/testing/mock_window.h"
|
||||
#include "flutter/testing/testing.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
static void set_size_arg(FlValue* args,
|
||||
const gchar* name,
|
||||
double width,
|
||||
double height) {
|
||||
g_autoptr(FlValue) size_value = fl_value_new_list();
|
||||
fl_value_append_take(size_value, fl_value_new_float(width));
|
||||
fl_value_append_take(size_value, fl_value_new_float(height));
|
||||
fl_value_set_string(args, name, size_value);
|
||||
}
|
||||
|
||||
static FlValue* make_create_regular_args(double width, double height) {
|
||||
FlValue* args = fl_value_new_map();
|
||||
set_size_arg(args, "size", width, height);
|
||||
return args;
|
||||
}
|
||||
|
||||
static int64_t parse_create_regular_response(FlMethodResponse* response) {
|
||||
EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
|
||||
|
||||
FlValue* result = fl_method_success_response_get_result(
|
||||
FL_METHOD_SUCCESS_RESPONSE(response));
|
||||
EXPECT_EQ(fl_value_get_type(result), FL_VALUE_TYPE_MAP);
|
||||
|
||||
FlValue* view_id_value = fl_value_lookup_string(result, "viewId");
|
||||
EXPECT_NE(view_id_value, nullptr);
|
||||
EXPECT_EQ(fl_value_get_type(view_id_value), FL_VALUE_TYPE_INT);
|
||||
int64_t view_id = fl_value_get_int(view_id_value);
|
||||
EXPECT_GT(view_id, 0);
|
||||
|
||||
FlValue* size_value = fl_value_lookup_string(result, "size");
|
||||
EXPECT_NE(size_value, nullptr);
|
||||
EXPECT_EQ(fl_value_get_type(size_value), FL_VALUE_TYPE_LIST);
|
||||
EXPECT_EQ(fl_value_get_length(size_value), 2u);
|
||||
EXPECT_EQ(fl_value_get_type(fl_value_get_list_value(size_value, 0)),
|
||||
FL_VALUE_TYPE_FLOAT);
|
||||
EXPECT_EQ(fl_value_get_type(fl_value_get_list_value(size_value, 1)),
|
||||
FL_VALUE_TYPE_FLOAT);
|
||||
|
||||
FlValue* state_value = fl_value_lookup_string(result, "state");
|
||||
EXPECT_NE(state_value, nullptr);
|
||||
EXPECT_EQ(fl_value_get_type(state_value), FL_VALUE_TYPE_STRING);
|
||||
|
||||
return view_id;
|
||||
}
|
||||
|
||||
static FlValue* make_modify_regular_args(int64_t view_id) {
|
||||
FlValue* args = fl_value_new_map();
|
||||
fl_value_set_string_take(args, "viewId", fl_value_new_int(view_id));
|
||||
return args;
|
||||
}
|
||||
|
||||
static FlValue* make_destroy_window_args(int64_t view_id) {
|
||||
FlValue* args = fl_value_new_map();
|
||||
fl_value_set_string_take(args, "viewId", fl_value_new_int(view_id));
|
||||
return args;
|
||||
}
|
||||
|
||||
TEST(FlWindowingHandlerTest, CreateRegular) {
|
||||
flutter::testing::fl_ensure_gtk_init();
|
||||
::testing::NiceMock<flutter::testing::MockWindow> mock_window;
|
||||
|
||||
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
|
||||
g_autoptr(FlEngine) engine =
|
||||
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
|
||||
g_autoptr(FlWindowingHandler) handler = fl_windowing_handler_new(engine);
|
||||
|
||||
EXPECT_CALL(mock_window, gtk_window_new);
|
||||
EXPECT_CALL(mock_window, gtk_window_set_default_size(::testing::_, 800, 600));
|
||||
|
||||
g_autoptr(FlValue) args = make_create_regular_args(800, 600);
|
||||
|
||||
gboolean called = FALSE;
|
||||
fl_mock_binary_messenger_invoke_standard_method(
|
||||
messenger, "flutter/windowing", "createRegular", args,
|
||||
[](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
|
||||
gpointer user_data) {
|
||||
parse_create_regular_response(response);
|
||||
gboolean* called = static_cast<gboolean*>(user_data);
|
||||
*called = TRUE;
|
||||
},
|
||||
&called);
|
||||
EXPECT_TRUE(called);
|
||||
|
||||
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
|
||||
}
|
||||
|
||||
TEST(FlWindowingHandlerTest, CreateRegularMinSize) {
|
||||
flutter::testing::fl_ensure_gtk_init();
|
||||
::testing::NiceMock<flutter::testing::MockWindow> mock_window;
|
||||
|
||||
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
|
||||
g_autoptr(FlEngine) engine =
|
||||
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
|
||||
g_autoptr(FlWindowingHandler) handler = fl_windowing_handler_new(engine);
|
||||
|
||||
EXPECT_CALL(mock_window, gtk_window_new);
|
||||
EXPECT_CALL(mock_window,
|
||||
gtk_window_set_geometry_hints(
|
||||
::testing::_, nullptr,
|
||||
::testing::Pointee(::testing::AllOf(
|
||||
::testing::Field(&GdkGeometry::min_width, 100),
|
||||
::testing::Field(&GdkGeometry::min_height, 200))),
|
||||
GDK_HINT_MIN_SIZE));
|
||||
|
||||
g_autoptr(FlValue) args = make_create_regular_args(800, 600);
|
||||
set_size_arg(args, "minSize", 100, 200);
|
||||
|
||||
gboolean called = FALSE;
|
||||
fl_mock_binary_messenger_invoke_standard_method(
|
||||
messenger, "flutter/windowing", "createRegular", args,
|
||||
[](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
|
||||
gpointer user_data) {
|
||||
parse_create_regular_response(response);
|
||||
gboolean* called = static_cast<gboolean*>(user_data);
|
||||
*called = TRUE;
|
||||
},
|
||||
&called);
|
||||
EXPECT_TRUE(called);
|
||||
|
||||
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
|
||||
}
|
||||
|
||||
TEST(FlWindowingHandlerTest, CreateRegularMaxSize) {
|
||||
flutter::testing::fl_ensure_gtk_init();
|
||||
::testing::NiceMock<flutter::testing::MockWindow> mock_window;
|
||||
|
||||
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
|
||||
g_autoptr(FlEngine) engine =
|
||||
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
|
||||
g_autoptr(FlWindowingHandler) handler = fl_windowing_handler_new(engine);
|
||||
|
||||
EXPECT_CALL(mock_window, gtk_window_new);
|
||||
EXPECT_CALL(mock_window,
|
||||
gtk_window_set_geometry_hints(
|
||||
::testing::_, nullptr,
|
||||
::testing::Pointee(::testing::AllOf(
|
||||
::testing::Field(&GdkGeometry::max_width, 1000),
|
||||
::testing::Field(&GdkGeometry::max_height, 2000))),
|
||||
GDK_HINT_MAX_SIZE));
|
||||
|
||||
g_autoptr(FlValue) args = make_create_regular_args(800, 600);
|
||||
set_size_arg(args, "maxSize", 1000, 2000);
|
||||
|
||||
gboolean called = FALSE;
|
||||
fl_mock_binary_messenger_invoke_standard_method(
|
||||
messenger, "flutter/windowing", "createRegular", args,
|
||||
[](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
|
||||
gpointer user_data) {
|
||||
parse_create_regular_response(response);
|
||||
gboolean* called = static_cast<gboolean*>(user_data);
|
||||
*called = TRUE;
|
||||
},
|
||||
&called);
|
||||
EXPECT_TRUE(called);
|
||||
|
||||
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
|
||||
}
|
||||
|
||||
TEST(FlWindowingHandlerTest, CreateRegularWithTitle) {
|
||||
flutter::testing::fl_ensure_gtk_init();
|
||||
::testing::NiceMock<flutter::testing::MockWindow> mock_window;
|
||||
|
||||
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
|
||||
g_autoptr(FlEngine) engine =
|
||||
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
|
||||
g_autoptr(FlWindowingHandler) handler = fl_windowing_handler_new(engine);
|
||||
|
||||
EXPECT_CALL(mock_window, gtk_window_new);
|
||||
EXPECT_CALL(mock_window,
|
||||
gtk_window_set_title(::testing::_, ::testing::StrEq("TITLE")));
|
||||
|
||||
g_autoptr(FlValue) args = make_create_regular_args(800, 600);
|
||||
fl_value_set_string_take(args, "title", fl_value_new_string("TITLE"));
|
||||
|
||||
gboolean called = FALSE;
|
||||
fl_mock_binary_messenger_invoke_standard_method(
|
||||
messenger, "flutter/windowing", "createRegular", args,
|
||||
[](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
|
||||
gpointer user_data) {
|
||||
parse_create_regular_response(response);
|
||||
gboolean* called = static_cast<gboolean*>(user_data);
|
||||
*called = TRUE;
|
||||
},
|
||||
&called);
|
||||
EXPECT_TRUE(called);
|
||||
|
||||
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
|
||||
}
|
||||
|
||||
TEST(FlWindowingHandlerTest, CreateRegularMaximized) {
|
||||
flutter::testing::fl_ensure_gtk_init();
|
||||
::testing::NiceMock<flutter::testing::MockWindow> mock_window;
|
||||
|
||||
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
|
||||
g_autoptr(FlEngine) engine =
|
||||
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
|
||||
g_autoptr(FlWindowingHandler) handler = fl_windowing_handler_new(engine);
|
||||
|
||||
EXPECT_CALL(mock_window, gtk_window_new);
|
||||
EXPECT_CALL(mock_window, gtk_window_maximize(::testing::_));
|
||||
|
||||
g_autoptr(FlValue) args = make_create_regular_args(800, 600);
|
||||
fl_value_set_string_take(args, "state",
|
||||
fl_value_new_string("WindowState.maximized"));
|
||||
|
||||
gboolean called = FALSE;
|
||||
fl_mock_binary_messenger_invoke_standard_method(
|
||||
messenger, "flutter/windowing", "createRegular", args,
|
||||
[](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
|
||||
gpointer user_data) {
|
||||
parse_create_regular_response(response);
|
||||
gboolean* called = static_cast<gboolean*>(user_data);
|
||||
*called = TRUE;
|
||||
},
|
||||
&called);
|
||||
EXPECT_TRUE(called);
|
||||
|
||||
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
|
||||
}
|
||||
|
||||
TEST(FlWindowingHandlerTest, CreateRegularMinimized) {
|
||||
flutter::testing::fl_ensure_gtk_init();
|
||||
::testing::NiceMock<flutter::testing::MockWindow> mock_window;
|
||||
|
||||
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
|
||||
g_autoptr(FlEngine) engine =
|
||||
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
|
||||
g_autoptr(FlWindowingHandler) handler = fl_windowing_handler_new(engine);
|
||||
|
||||
EXPECT_CALL(mock_window, gtk_window_new);
|
||||
EXPECT_CALL(mock_window, gtk_window_iconify(::testing::_));
|
||||
|
||||
g_autoptr(FlValue) args = make_create_regular_args(800, 600);
|
||||
fl_value_set_string_take(args, "state",
|
||||
fl_value_new_string("WindowState.minimized"));
|
||||
|
||||
gboolean called = FALSE;
|
||||
fl_mock_binary_messenger_invoke_standard_method(
|
||||
messenger, "flutter/windowing", "createRegular", args,
|
||||
[](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
|
||||
gpointer user_data) {
|
||||
parse_create_regular_response(response);
|
||||
gboolean* called = static_cast<gboolean*>(user_data);
|
||||
*called = TRUE;
|
||||
},
|
||||
&called);
|
||||
EXPECT_TRUE(called);
|
||||
|
||||
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
|
||||
}
|
||||
|
||||
TEST(FlWindowingHandlerTest, ModifyRegularSize) {
|
||||
flutter::testing::fl_ensure_gtk_init();
|
||||
::testing::NiceMock<flutter::testing::MockWindow> mock_window;
|
||||
|
||||
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
|
||||
g_autoptr(FlEngine) engine =
|
||||
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
|
||||
g_autoptr(FlWindowingHandler) handler = fl_windowing_handler_new(engine);
|
||||
|
||||
EXPECT_CALL(mock_window, gtk_window_new);
|
||||
EXPECT_CALL(mock_window, gtk_window_resize(::testing::_, 1024, 768));
|
||||
|
||||
g_autoptr(FlValue) create_args = make_create_regular_args(800, 600);
|
||||
|
||||
int64_t view_id = -1;
|
||||
fl_mock_binary_messenger_invoke_standard_method(
|
||||
messenger, "flutter/windowing", "createRegular", create_args,
|
||||
[](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
|
||||
gpointer user_data) {
|
||||
int64_t* view_id = static_cast<int64_t*>(user_data);
|
||||
*view_id = parse_create_regular_response(response);
|
||||
},
|
||||
&view_id);
|
||||
EXPECT_GT(view_id, 0);
|
||||
|
||||
g_autoptr(FlValue) modify_args = make_modify_regular_args(view_id);
|
||||
set_size_arg(modify_args, "size", 1024, 768);
|
||||
|
||||
gboolean modify_called = FALSE;
|
||||
fl_mock_binary_messenger_invoke_standard_method(
|
||||
messenger, "flutter/windowing", "modifyRegular", modify_args,
|
||||
[](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
|
||||
gpointer user_data) {
|
||||
EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
|
||||
gboolean* called = static_cast<gboolean*>(user_data);
|
||||
*called = TRUE;
|
||||
},
|
||||
&modify_called);
|
||||
EXPECT_TRUE(modify_called);
|
||||
|
||||
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
|
||||
}
|
||||
|
||||
TEST(FlWindowingHandlerTest, ModifyRegularTitle) {
|
||||
flutter::testing::fl_ensure_gtk_init();
|
||||
::testing::NiceMock<flutter::testing::MockWindow> mock_window;
|
||||
|
||||
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
|
||||
g_autoptr(FlEngine) engine =
|
||||
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
|
||||
g_autoptr(FlWindowingHandler) handler = fl_windowing_handler_new(engine);
|
||||
|
||||
EXPECT_CALL(mock_window, gtk_window_new);
|
||||
EXPECT_CALL(mock_window,
|
||||
gtk_window_set_title(::testing::_, ::testing::StrEq("TITLE")));
|
||||
|
||||
g_autoptr(FlValue) create_args = make_create_regular_args(800, 600);
|
||||
|
||||
int64_t view_id = -1;
|
||||
fl_mock_binary_messenger_invoke_standard_method(
|
||||
messenger, "flutter/windowing", "createRegular", create_args,
|
||||
[](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
|
||||
gpointer user_data) {
|
||||
int64_t* view_id = static_cast<int64_t*>(user_data);
|
||||
*view_id = parse_create_regular_response(response);
|
||||
},
|
||||
&view_id);
|
||||
EXPECT_GT(view_id, 0);
|
||||
|
||||
g_autoptr(FlValue) modify_args = make_modify_regular_args(view_id);
|
||||
fl_value_set_string_take(modify_args, "title", fl_value_new_string("TITLE"));
|
||||
|
||||
gboolean modify_called = FALSE;
|
||||
fl_mock_binary_messenger_invoke_standard_method(
|
||||
messenger, "flutter/windowing", "modifyRegular", modify_args,
|
||||
[](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
|
||||
gpointer user_data) {
|
||||
EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
|
||||
gboolean* called = static_cast<gboolean*>(user_data);
|
||||
*called = TRUE;
|
||||
},
|
||||
&modify_called);
|
||||
EXPECT_TRUE(modify_called);
|
||||
|
||||
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
|
||||
}
|
||||
|
||||
TEST(FlWindowingHandlerTest, ModifyRegularMaximize) {
|
||||
flutter::testing::fl_ensure_gtk_init();
|
||||
::testing::NiceMock<flutter::testing::MockWindow> mock_window;
|
||||
|
||||
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
|
||||
g_autoptr(FlEngine) engine =
|
||||
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
|
||||
g_autoptr(FlWindowingHandler) handler = fl_windowing_handler_new(engine);
|
||||
|
||||
EXPECT_CALL(mock_window, gtk_window_new);
|
||||
|
||||
g_autoptr(FlValue) create_args = make_create_regular_args(800, 600);
|
||||
|
||||
int64_t view_id = -1;
|
||||
fl_mock_binary_messenger_invoke_standard_method(
|
||||
messenger, "flutter/windowing", "createRegular", create_args,
|
||||
[](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
|
||||
gpointer user_data) {
|
||||
int64_t* view_id = static_cast<int64_t*>(user_data);
|
||||
*view_id = parse_create_regular_response(response);
|
||||
},
|
||||
&view_id);
|
||||
EXPECT_GT(view_id, 0);
|
||||
|
||||
EXPECT_CALL(mock_window, gtk_window_maximize(::testing::_));
|
||||
|
||||
g_autoptr(FlValue) modify_args = make_modify_regular_args(view_id);
|
||||
fl_value_set_string_take(modify_args, "state",
|
||||
fl_value_new_string("WindowState.maximized"));
|
||||
|
||||
gboolean modify_called = FALSE;
|
||||
fl_mock_binary_messenger_invoke_standard_method(
|
||||
messenger, "flutter/windowing", "modifyRegular", modify_args,
|
||||
[](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
|
||||
gpointer user_data) {
|
||||
EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
|
||||
gboolean* called = static_cast<gboolean*>(user_data);
|
||||
*called = TRUE;
|
||||
},
|
||||
&modify_called);
|
||||
EXPECT_TRUE(modify_called);
|
||||
|
||||
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
|
||||
}
|
||||
|
||||
TEST(FlWindowingHandlerTest, ModifyRegularUnmaximize) {
|
||||
flutter::testing::fl_ensure_gtk_init();
|
||||
::testing::NiceMock<flutter::testing::MockWindow> mock_window;
|
||||
|
||||
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
|
||||
g_autoptr(FlEngine) engine =
|
||||
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
|
||||
g_autoptr(FlWindowingHandler) handler = fl_windowing_handler_new(engine);
|
||||
|
||||
EXPECT_CALL(mock_window, gtk_window_new);
|
||||
EXPECT_CALL(mock_window, gtk_window_maximize(::testing::_));
|
||||
|
||||
g_autoptr(FlValue) create_args = make_create_regular_args(800, 600);
|
||||
fl_value_set_string_take(create_args, "state",
|
||||
fl_value_new_string("WindowState.maximized"));
|
||||
|
||||
int64_t view_id = -1;
|
||||
fl_mock_binary_messenger_invoke_standard_method(
|
||||
messenger, "flutter/windowing", "createRegular", create_args,
|
||||
[](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
|
||||
gpointer user_data) {
|
||||
int64_t* view_id = static_cast<int64_t*>(user_data);
|
||||
*view_id = parse_create_regular_response(response);
|
||||
},
|
||||
&view_id);
|
||||
EXPECT_GT(view_id, 0);
|
||||
|
||||
EXPECT_CALL(mock_window, gtk_window_is_maximized(::testing::_))
|
||||
.WillOnce(::testing::Return(TRUE));
|
||||
EXPECT_CALL(mock_window, gtk_window_unmaximize(::testing::_));
|
||||
|
||||
g_autoptr(FlValue) modify_args = make_modify_regular_args(view_id);
|
||||
fl_value_set_string_take(modify_args, "state",
|
||||
fl_value_new_string("WindowState.restored"));
|
||||
|
||||
gboolean modify_called = FALSE;
|
||||
fl_mock_binary_messenger_invoke_standard_method(
|
||||
messenger, "flutter/windowing", "modifyRegular", modify_args,
|
||||
[](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
|
||||
gpointer user_data) {
|
||||
EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
|
||||
gboolean* called = static_cast<gboolean*>(user_data);
|
||||
*called = TRUE;
|
||||
},
|
||||
&modify_called);
|
||||
EXPECT_TRUE(modify_called);
|
||||
|
||||
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
|
||||
}
|
||||
|
||||
TEST(FlWindowingHandlerTest, ModifyRegularMinimize) {
|
||||
flutter::testing::fl_ensure_gtk_init();
|
||||
::testing::NiceMock<flutter::testing::MockWindow> mock_window;
|
||||
|
||||
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
|
||||
g_autoptr(FlEngine) engine =
|
||||
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
|
||||
g_autoptr(FlWindowingHandler) handler = fl_windowing_handler_new(engine);
|
||||
|
||||
EXPECT_CALL(mock_window, gtk_window_new);
|
||||
|
||||
g_autoptr(FlValue) create_args = make_create_regular_args(800, 600);
|
||||
|
||||
int64_t view_id = -1;
|
||||
fl_mock_binary_messenger_invoke_standard_method(
|
||||
messenger, "flutter/windowing", "createRegular", create_args,
|
||||
[](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
|
||||
gpointer user_data) {
|
||||
int64_t* view_id = static_cast<int64_t*>(user_data);
|
||||
*view_id = parse_create_regular_response(response);
|
||||
},
|
||||
&view_id);
|
||||
EXPECT_GT(view_id, 0);
|
||||
|
||||
EXPECT_CALL(mock_window, gtk_window_iconify(::testing::_));
|
||||
|
||||
g_autoptr(FlValue) modify_args = make_modify_regular_args(view_id);
|
||||
fl_value_set_string_take(modify_args, "state",
|
||||
fl_value_new_string("WindowState.minimized"));
|
||||
|
||||
gboolean modify_called = FALSE;
|
||||
fl_mock_binary_messenger_invoke_standard_method(
|
||||
messenger, "flutter/windowing", "modifyRegular", modify_args,
|
||||
[](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
|
||||
gpointer user_data) {
|
||||
EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
|
||||
gboolean* called = static_cast<gboolean*>(user_data);
|
||||
*called = TRUE;
|
||||
},
|
||||
&modify_called);
|
||||
EXPECT_TRUE(modify_called);
|
||||
|
||||
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
|
||||
}
|
||||
|
||||
TEST(FlWindowingHandlerTest, ModifyRegularUnminimize) {
|
||||
flutter::testing::fl_ensure_gtk_init();
|
||||
::testing::NiceMock<flutter::testing::MockWindow> mock_window;
|
||||
|
||||
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
|
||||
g_autoptr(FlEngine) engine =
|
||||
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
|
||||
g_autoptr(FlWindowingHandler) handler = fl_windowing_handler_new(engine);
|
||||
|
||||
EXPECT_CALL(mock_window, gtk_window_new);
|
||||
EXPECT_CALL(mock_window, gtk_window_iconify(::testing::_));
|
||||
|
||||
g_autoptr(FlValue) create_args = make_create_regular_args(800, 600);
|
||||
fl_value_set_string_take(create_args, "state",
|
||||
fl_value_new_string("WindowState.minimized"));
|
||||
|
||||
int64_t view_id = -1;
|
||||
fl_mock_binary_messenger_invoke_standard_method(
|
||||
messenger, "flutter/windowing", "createRegular", create_args,
|
||||
[](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
|
||||
gpointer user_data) {
|
||||
int64_t* view_id = static_cast<int64_t*>(user_data);
|
||||
*view_id = parse_create_regular_response(response);
|
||||
},
|
||||
&view_id);
|
||||
EXPECT_GT(view_id, 0);
|
||||
|
||||
EXPECT_CALL(mock_window, gdk_window_get_state(::testing::_))
|
||||
.WillOnce(::testing::Return(GDK_WINDOW_STATE_ICONIFIED));
|
||||
EXPECT_CALL(mock_window, gtk_window_deiconify(::testing::_));
|
||||
|
||||
g_autoptr(FlValue) modify_args = make_modify_regular_args(view_id);
|
||||
fl_value_set_string_take(modify_args, "state",
|
||||
fl_value_new_string("WindowState.restored"));
|
||||
|
||||
gboolean modify_called = FALSE;
|
||||
fl_mock_binary_messenger_invoke_standard_method(
|
||||
messenger, "flutter/windowing", "modifyRegular", modify_args,
|
||||
[](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
|
||||
gpointer user_data) {
|
||||
EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
|
||||
gboolean* called = static_cast<gboolean*>(user_data);
|
||||
*called = TRUE;
|
||||
},
|
||||
&modify_called);
|
||||
EXPECT_TRUE(modify_called);
|
||||
|
||||
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
|
||||
}
|
||||
|
||||
TEST(FlWindowingHandlerTest, ModifyUnknownWindow) {
|
||||
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
|
||||
g_autoptr(FlEngine) engine =
|
||||
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
|
||||
g_autoptr(FlWindowingHandler) handler = fl_windowing_handler_new(engine);
|
||||
|
||||
g_autoptr(FlValue) args = make_modify_regular_args(99);
|
||||
|
||||
gboolean called = FALSE;
|
||||
fl_mock_binary_messenger_invoke_standard_method(
|
||||
messenger, "flutter/windowing", "modifyRegular", args,
|
||||
[](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
|
||||
gpointer user_data) {
|
||||
EXPECT_TRUE(FL_IS_METHOD_ERROR_RESPONSE(response));
|
||||
EXPECT_STREQ(fl_method_error_response_get_code(
|
||||
FL_METHOD_ERROR_RESPONSE(response)),
|
||||
"Bad Arguments");
|
||||
EXPECT_STREQ(fl_method_error_response_get_message(
|
||||
FL_METHOD_ERROR_RESPONSE(response)),
|
||||
"No window with given view ID");
|
||||
gboolean* called = static_cast<gboolean*>(user_data);
|
||||
*called = TRUE;
|
||||
},
|
||||
&called);
|
||||
EXPECT_TRUE(called);
|
||||
|
||||
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
|
||||
}
|
||||
|
||||
TEST(FlWindowingHandlerTest, DestroyWindow) {
|
||||
flutter::testing::fl_ensure_gtk_init();
|
||||
::testing::NiceMock<flutter::testing::MockWindow> mock_window;
|
||||
|
||||
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
|
||||
g_autoptr(FlEngine) engine =
|
||||
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
|
||||
g_autoptr(FlWindowingHandler) handler = fl_windowing_handler_new(engine);
|
||||
|
||||
EXPECT_CALL(mock_window, gtk_window_new);
|
||||
EXPECT_CALL(mock_window, gtk_widget_destroy);
|
||||
|
||||
g_autoptr(FlValue) create_args = make_create_regular_args(800, 600);
|
||||
|
||||
int64_t view_id = -1;
|
||||
fl_mock_binary_messenger_invoke_standard_method(
|
||||
messenger, "flutter/windowing", "createRegular", create_args,
|
||||
[](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
|
||||
gpointer user_data) {
|
||||
int64_t* view_id = static_cast<int64_t*>(user_data);
|
||||
*view_id = parse_create_regular_response(response);
|
||||
},
|
||||
&view_id);
|
||||
EXPECT_GT(view_id, 0);
|
||||
|
||||
g_autoptr(FlValue) destroy_args = make_destroy_window_args(view_id);
|
||||
gboolean destroy_called = FALSE;
|
||||
fl_mock_binary_messenger_invoke_standard_method(
|
||||
messenger, "flutter/windowing", "destroyWindow", destroy_args,
|
||||
[](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
|
||||
gpointer user_data) {
|
||||
EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
|
||||
gboolean* called = static_cast<gboolean*>(user_data);
|
||||
*called = TRUE;
|
||||
},
|
||||
&destroy_called);
|
||||
EXPECT_TRUE(destroy_called);
|
||||
|
||||
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
|
||||
}
|
||||
|
||||
TEST(FlWindowingHandlerTest, DestroyUnknownWindow) {
|
||||
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
|
||||
g_autoptr(FlEngine) engine =
|
||||
fl_engine_new_with_binary_messenger(FL_BINARY_MESSENGER(messenger));
|
||||
g_autoptr(FlWindowingHandler) handler = fl_windowing_handler_new(engine);
|
||||
|
||||
g_autoptr(FlValue) args = make_destroy_window_args(99);
|
||||
gboolean called = FALSE;
|
||||
fl_mock_binary_messenger_invoke_standard_method(
|
||||
messenger, "flutter/windowing", "destroyWindow", args,
|
||||
[](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
|
||||
gpointer user_data) {
|
||||
EXPECT_TRUE(FL_IS_METHOD_ERROR_RESPONSE(response));
|
||||
EXPECT_STREQ(fl_method_error_response_get_code(
|
||||
FL_METHOD_ERROR_RESPONSE(response)),
|
||||
"Bad Arguments");
|
||||
EXPECT_STREQ(fl_method_error_response_get_message(
|
||||
FL_METHOD_ERROR_RESPONSE(response)),
|
||||
"No window with given view ID");
|
||||
gboolean* called = static_cast<gboolean*>(user_data);
|
||||
*called = TRUE;
|
||||
},
|
||||
&called);
|
||||
EXPECT_TRUE(called);
|
||||
|
||||
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
|
||||
}
|
@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "flutter/shell/platform/linux/testing/mock_window.h"
|
||||
|
||||
using namespace flutter::testing;
|
||||
@ -50,3 +52,60 @@ GdkCursor* gdk_cursor_new_from_name(GdkDisplay* display, const gchar* name) {
|
||||
}
|
||||
|
||||
void gdk_window_set_cursor(GdkWindow* window, GdkCursor* cursor) {}
|
||||
|
||||
GtkWidget* gtk_window_new(GtkWindowType type) {
|
||||
GtkWindow* window = GTK_WINDOW(g_object_new(gtk_window_get_type(), nullptr));
|
||||
mock->gtk_window_new(window, type);
|
||||
return GTK_WIDGET(window);
|
||||
}
|
||||
|
||||
void gtk_window_set_default_size(GtkWindow* window, gint width, gint height) {
|
||||
mock->gtk_window_set_default_size(window, width, height);
|
||||
}
|
||||
|
||||
void gtk_window_set_title(GtkWindow* window, const gchar* title) {
|
||||
mock->gtk_window_set_title(window, title);
|
||||
}
|
||||
|
||||
void gtk_window_set_geometry_hints(GtkWindow* window,
|
||||
GtkWidget* widget,
|
||||
GdkGeometry* geometry,
|
||||
GdkWindowHints geometry_mask) {
|
||||
mock->gtk_window_set_geometry_hints(window, widget, geometry, geometry_mask);
|
||||
}
|
||||
|
||||
void gtk_window_resize(GtkWindow* window, gint width, gint height) {
|
||||
mock->gtk_window_resize(window, width, height);
|
||||
}
|
||||
|
||||
void gtk_window_maximize(GtkWindow* window) {
|
||||
mock->gtk_window_maximize(window);
|
||||
}
|
||||
|
||||
void gtk_window_unmaximize(GtkWindow* window) {
|
||||
mock->gtk_window_unmaximize(window);
|
||||
}
|
||||
|
||||
gboolean gtk_window_is_maximized(GtkWindow* window) {
|
||||
return mock->gtk_window_is_maximized(window);
|
||||
}
|
||||
|
||||
void gtk_window_iconify(GtkWindow* window) {
|
||||
mock->gtk_window_iconify(window);
|
||||
}
|
||||
|
||||
void gtk_window_deiconify(GtkWindow* window) {
|
||||
mock->gtk_window_deiconify(window);
|
||||
}
|
||||
|
||||
void gtk_widget_show(GtkWidget* widget) {}
|
||||
|
||||
void gtk_widget_destroy(GtkWidget* widget) {
|
||||
mock->gtk_widget_destroy(widget);
|
||||
}
|
||||
|
||||
void fl_gtk_widget_destroy(GtkWidget* widget) {
|
||||
void (*destroy)(GtkWidget*) = reinterpret_cast<void (*)(GtkWidget*)>(
|
||||
dlsym(RTLD_NEXT, "gtk_widget_destroy"));
|
||||
destroy(widget);
|
||||
}
|
||||
|
@ -18,9 +18,34 @@ class MockWindow {
|
||||
~MockWindow();
|
||||
|
||||
MOCK_METHOD(GdkWindowState, gdk_window_get_state, (GdkWindow * window));
|
||||
MOCK_METHOD(void, gtk_window_new, (GtkWindow * window, GtkWindowType type));
|
||||
MOCK_METHOD(void,
|
||||
gtk_window_set_default_size,
|
||||
(GtkWindow * window, gint width, gint height));
|
||||
MOCK_METHOD(void,
|
||||
gtk_window_set_title,
|
||||
(GtkWindow * window, const gchar* title));
|
||||
MOCK_METHOD(void,
|
||||
gtk_window_set_geometry_hints,
|
||||
(GtkWindow * window,
|
||||
GtkWidget* widget,
|
||||
GdkGeometry* geometry,
|
||||
GdkWindowHints geometry_mask));
|
||||
MOCK_METHOD(void,
|
||||
gtk_window_resize,
|
||||
(GtkWindow * window, gint width, gint height));
|
||||
MOCK_METHOD(void, gtk_window_maximize, (GtkWindow * window));
|
||||
MOCK_METHOD(void, gtk_window_unmaximize, (GtkWindow * window));
|
||||
MOCK_METHOD(gboolean, gtk_window_is_maximized, (GtkWindow * window));
|
||||
MOCK_METHOD(void, gtk_window_iconify, (GtkWindow * window));
|
||||
MOCK_METHOD(void, gtk_window_deiconify, (GtkWindow * window));
|
||||
MOCK_METHOD(void, gtk_widget_destroy, (GtkWidget * widget));
|
||||
};
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
// Calls original gtk_widget_destroy.
|
||||
void fl_gtk_widget_destroy(GtkWidget* widget);
|
||||
|
||||
#endif // FLUTTER_SHELL_PLATFORM_LINUX_TESTING_MOCK_WINDOW_H_
|
||||
|
Loading…
x
Reference in New Issue
Block a user