fl_view.cc 10.6 KB
Newer Older
1 2 3 4 5 6
// 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/public/flutter_linux/fl_view.h"

7
#include "flutter/shell/platform/linux/fl_engine_private.h"
R
Robert Ancell 已提交
8
#include "flutter/shell/platform/linux/fl_key_event_plugin.h"
9
#include "flutter/shell/platform/linux/fl_plugin_registrar_private.h"
10
#include "flutter/shell/platform/linux/fl_renderer_x11.h"
R
Robert Ancell 已提交
11
#include "flutter/shell/platform/linux/fl_text_input_plugin.h"
12
#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h"
13
#include "flutter/shell/platform/linux/public/flutter_linux/fl_plugin_registry.h"
14

15 16
#include <gdk/gdkx.h>

17
static constexpr int kMicrosecondsPerMillisecond = 1000;
18 19 20 21

struct _FlView {
  GtkWidget parent_instance;

22
  // Project being run.
23
  FlDartProject* project;
24

25
  // Rendering output.
26
  FlRendererX11* renderer;
27

28
  // Engine running @project.
29
  FlEngine* engine;
30

31
  // Pointer button state recorded for sending status updates.
32
  int64_t button_state;
R
Robert Ancell 已提交
33 34 35

  // Flutter system channel handlers.
  FlKeyEventPlugin* key_event_plugin;
R
Robert Ancell 已提交
36
  FlTextInputPlugin* text_input_plugin;
37 38 39 40
};

enum { PROP_FLUTTER_PROJECT = 1, PROP_LAST };

41 42 43 44 45 46 47 48 49
static void fl_view_plugin_registry_iface_init(
    FlPluginRegistryInterface* iface);

G_DEFINE_TYPE_WITH_CODE(
    FlView,
    fl_view,
    GTK_TYPE_WIDGET,
    G_IMPLEMENT_INTERFACE(fl_plugin_registry_get_type(),
                          fl_view_plugin_registry_iface_init))
50

R
Robert Ancell 已提交
51
// Converts a GDK button event into a Flutter event and sends it to the engine.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
static gboolean fl_view_send_pointer_button_event(FlView* self,
                                                  GdkEventButton* event) {
  int64_t button;
  switch (event->button) {
    case 1:
      button = kFlutterPointerButtonMousePrimary;
      break;
    case 2:
      button = kFlutterPointerButtonMouseMiddle;
      break;
    case 3:
      button = kFlutterPointerButtonMouseSecondary;
      break;
    default:
      return FALSE;
  }
  int old_button_state = self->button_state;
69
  FlutterPointerPhase phase;
70
  if (event->type == GDK_BUTTON_PRESS) {
71
    // Drop the event if Flutter already thinks the button is down.
72 73 74 75
    if ((self->button_state & button) != 0)
      return FALSE;
    self->button_state ^= button;

76
    phase = old_button_state == 0 ? kDown : kMove;
77
  } else if (event->type == GDK_BUTTON_RELEASE) {
78
    // Drop the event if Flutter already thinks the button is up.
79 80 81 82
    if ((self->button_state & button) == 0)
      return FALSE;
    self->button_state ^= button;

83
    phase = self->button_state == 0 ? kUp : kMove;
84 85
  }

86
  if (self->engine == nullptr)
87 88
    return FALSE;

89
  fl_engine_send_mouse_pointer_event(self->engine, phase,
90 91
                                     event->time * kMicrosecondsPerMillisecond,
                                     event->x, event->y, self->button_state);
92 93 94
  return TRUE;
}

95
// Implements FlPluginRegistry::get_registrar_for_plugin.
96 97 98 99 100 101 102 103 104 105 106 107 108 109
static FlPluginRegistrar* fl_view_get_registrar_for_plugin(
    FlPluginRegistry* registry,
    const gchar* name) {
  FlView* self = FL_VIEW(registry);

  return fl_plugin_registrar_new(self,
                                 fl_engine_get_binary_messenger(self->engine));
}

static void fl_view_plugin_registry_iface_init(
    FlPluginRegistryInterface* iface) {
  iface->get_registrar_for_plugin = fl_view_get_registrar_for_plugin;
}

110 111 112 113 114
static void fl_view_constructed(GObject* object) {
  FlView* self = FL_VIEW(object);

  self->renderer = fl_renderer_x11_new();
  self->engine = fl_engine_new(self->project, FL_RENDERER(self->renderer));
R
Robert Ancell 已提交
115

116
  // Create system channel handlers.
R
Robert Ancell 已提交
117 118
  FlBinaryMessenger* messenger = fl_engine_get_binary_messenger(self->engine);
  self->key_event_plugin = fl_key_event_plugin_new(messenger);
R
Robert Ancell 已提交
119
  self->text_input_plugin = fl_text_input_plugin_new(messenger);
120 121
}

122 123 124 125 126 127 128 129
static void fl_view_set_property(GObject* object,
                                 guint prop_id,
                                 const GValue* value,
                                 GParamSpec* pspec) {
  FlView* self = FL_VIEW(object);

  switch (prop_id) {
    case PROP_FLUTTER_PROJECT:
130
      g_set_object(&self->project,
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
                   static_cast<FlDartProject*>(g_value_get_object(value)));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
      break;
  }
}

static void fl_view_get_property(GObject* object,
                                 guint prop_id,
                                 GValue* value,
                                 GParamSpec* pspec) {
  FlView* self = FL_VIEW(object);

  switch (prop_id) {
    case PROP_FLUTTER_PROJECT:
147
      g_value_set_object(value, self->project);
148 149 150 151 152 153 154 155 156 157
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
      break;
  }
}

static void fl_view_dispose(GObject* object) {
  FlView* self = FL_VIEW(object);

158
  g_clear_object(&self->project);
159
  g_clear_object(&self->renderer);
160
  g_clear_object(&self->engine);
R
Robert Ancell 已提交
161
  g_clear_object(&self->key_event_plugin);
R
Robert Ancell 已提交
162
  g_clear_object(&self->text_input_plugin);
163 164 165 166

  G_OBJECT_CLASS(fl_view_parent_class)->dispose(object);
}

167
// Implements GtkWidget::realize.
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
static void fl_view_realize(GtkWidget* widget) {
  FlView* self = FL_VIEW(widget);

  gtk_widget_set_realized(widget, TRUE);

  GtkAllocation allocation;
  gtk_widget_get_allocation(widget, &allocation);

  GdkWindowAttr window_attributes;
  window_attributes.window_type = GDK_WINDOW_CHILD;
  window_attributes.x = allocation.x;
  window_attributes.y = allocation.y;
  window_attributes.width = allocation.width;
  window_attributes.height = allocation.height;
  window_attributes.wclass = GDK_INPUT_OUTPUT;
  window_attributes.visual = gtk_widget_get_visual(widget);
  window_attributes.event_mask =
185
      gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK |
R
Robert Ancell 已提交
186 187
      GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK |
      GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK;
188 189 190 191 192 193 194 195 196

  gint window_attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;

  GdkWindow* window =
      gdk_window_new(gtk_widget_get_parent_window(widget), &window_attributes,
                     window_attributes_mask);
  gtk_widget_register_window(widget, window);
  gtk_widget_set_window(widget, window);

197
  Window xid = gdk_x11_window_get_xid(gtk_widget_get_window(GTK_WIDGET(self)));
198 199
  fl_renderer_x11_set_xid(self->renderer, xid);

200 201
  g_autoptr(GError) error = nullptr;
  if (!fl_engine_start(self->engine, &error))
202
    g_warning("Failed to start Flutter engine: %s", error->message);
203 204
}

205
// Implements GtkWidget::size-allocate.
206 207 208 209 210 211
static void fl_view_size_allocate(GtkWidget* widget,
                                  GtkAllocation* allocation) {
  FlView* self = FL_VIEW(widget);

  gtk_widget_set_allocation(widget, allocation);

212
  if (gtk_widget_get_realized(widget) && gtk_widget_get_has_window(widget)) {
213 214 215
    gdk_window_move_resize(gtk_widget_get_window(widget), allocation->x,
                           allocation->y, allocation->width,
                           allocation->height);
216
  }
217

218
  // TODO(robert-ancell): This pixel ratio won't work on hidpi displays.
219 220
  fl_engine_send_window_metrics_event(self->engine, allocation->width,
                                      allocation->height, 1);
221 222
}

223
// Implements GtkWidget::button_press_event.
224 225 226 227
static gboolean fl_view_button_press_event(GtkWidget* widget,
                                           GdkEventButton* event) {
  FlView* self = FL_VIEW(widget);

228
  // Flutter doesn't handle double and triple click events.
229 230 231 232 233 234 235
  if (event->type == GDK_DOUBLE_BUTTON_PRESS ||
      event->type == GDK_TRIPLE_BUTTON_PRESS)
    return FALSE;

  return fl_view_send_pointer_button_event(self, event);
}

236
// Implements GtkWidget::button_release_event.
237 238 239 240 241 242 243
static gboolean fl_view_button_release_event(GtkWidget* widget,
                                             GdkEventButton* event) {
  FlView* self = FL_VIEW(widget);

  return fl_view_send_pointer_button_event(self, event);
}

244
// Implements GtkWidget::motion_notify_event.
245 246 247 248
static gboolean fl_view_motion_notify_event(GtkWidget* widget,
                                            GdkEventMotion* event) {
  FlView* self = FL_VIEW(widget);

249
  if (self->engine == nullptr)
250 251
    return FALSE;

252 253 254 255
  fl_engine_send_mouse_pointer_event(self->engine,
                                     self->button_state != 0 ? kMove : kHover,
                                     event->time * kMicrosecondsPerMillisecond,
                                     event->x, event->y, self->button_state);
256 257 258 259

  return TRUE;
}

260
// Implements GtkWidget::key_press_event.
R
Robert Ancell 已提交
261 262 263
static gboolean fl_view_key_press_event(GtkWidget* widget, GdkEventKey* event) {
  FlView* self = FL_VIEW(widget);

R
Robert Ancell 已提交
264 265 266
  if (fl_text_input_plugin_filter_keypress(self->text_input_plugin, event))
    return TRUE;

R
Robert Ancell 已提交
267 268 269 270 271
  fl_key_event_plugin_send_key_event(self->key_event_plugin, event);

  return TRUE;
}

272
// Implements GtkWidget::key_release_event.
R
Robert Ancell 已提交
273 274 275 276
static gboolean fl_view_key_release_event(GtkWidget* widget,
                                          GdkEventKey* event) {
  FlView* self = FL_VIEW(widget);

R
Robert Ancell 已提交
277 278 279
  if (fl_text_input_plugin_filter_keypress(self->text_input_plugin, event))
    return TRUE;

R
Robert Ancell 已提交
280 281 282 283 284
  fl_key_event_plugin_send_key_event(self->key_event_plugin, event);

  return TRUE;
}

285
static void fl_view_class_init(FlViewClass* klass) {
286
  G_OBJECT_CLASS(klass)->constructed = fl_view_constructed;
287 288 289 290 291
  G_OBJECT_CLASS(klass)->set_property = fl_view_set_property;
  G_OBJECT_CLASS(klass)->get_property = fl_view_get_property;
  G_OBJECT_CLASS(klass)->dispose = fl_view_dispose;
  GTK_WIDGET_CLASS(klass)->realize = fl_view_realize;
  GTK_WIDGET_CLASS(klass)->size_allocate = fl_view_size_allocate;
292 293 294
  GTK_WIDGET_CLASS(klass)->button_press_event = fl_view_button_press_event;
  GTK_WIDGET_CLASS(klass)->button_release_event = fl_view_button_release_event;
  GTK_WIDGET_CLASS(klass)->motion_notify_event = fl_view_motion_notify_event;
R
Robert Ancell 已提交
295 296
  GTK_WIDGET_CLASS(klass)->key_press_event = fl_view_key_press_event;
  GTK_WIDGET_CLASS(klass)->key_release_event = fl_view_key_release_event;
297 298 299 300 301 302 303 304 305 306

  g_object_class_install_property(
      G_OBJECT_CLASS(klass), PROP_FLUTTER_PROJECT,
      g_param_spec_object(
          "flutter-project", "flutter-project", "Flutter project in use",
          fl_dart_project_get_type(),
          static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
                                   G_PARAM_STATIC_STRINGS)));
}

R
Robert Ancell 已提交
307 308 309
static void fl_view_init(FlView* self) {
  gtk_widget_set_can_focus(GTK_WIDGET(self), TRUE);
}
310 311 312 313 314

G_MODULE_EXPORT FlView* fl_view_new(FlDartProject* project) {
  return static_cast<FlView*>(
      g_object_new(fl_view_get_type(), "flutter-project", project, nullptr));
}
315 316 317 318 319

G_MODULE_EXPORT FlEngine* fl_view_get_engine(FlView* view) {
  g_return_val_if_fail(FL_IS_VIEW(view), nullptr);
  return view->engine;
}