Flutter Engine
The Flutter Engine
Window_unix.cpp
Go to the documentation of this file.
1/*
2* Copyright 2016 Google Inc.
3*
4* Use of this source code is governed by a BSD-style license that can be
5* found in the LICENSE file.
6*/
7
9
10#include "src/base/SkUTF.h"
13#include "tools/timer/Timer.h"
15
16extern "C" {
18}
19#include <X11/Xatom.h>
20#include <X11/Xutil.h>
21#include <X11/XKBlib.h>
22
23namespace sk_app {
24
26
27Window* Window::CreateNativeWindow(void* platformData) {
28 Display* display = (Display*)platformData;
29 SkASSERT(display);
30
31 Window_unix* window = new Window_unix();
32 if (!window->initWindow(display)) {
33 delete window;
34 return nullptr;
35 }
36
37 return window;
38}
39
40const long kEventMask = ExposureMask | StructureNotifyMask |
41 KeyPressMask | KeyReleaseMask |
42 PointerMotionMask | ButtonPressMask | ButtonReleaseMask;
43
44bool Window_unix::initWindow(Display* display) {
45 if (fRequestedDisplayParams.fMSAASampleCount != fMSAASampleCount) {
46 this->closeWindow();
47 }
48 // we already have a window
49 if (fDisplay) {
50 return true;
51 }
52 fDisplay = display;
53
54 constexpr int initialWidth = 1280;
55 constexpr int initialHeight = 960;
56
57#ifdef SK_GL
58 // Attempt to create a window that supports GL
59
60 // We prefer the more recent glXChooseFBConfig but fall back to glXChooseVisual. They have
61 // slight differences in how attributes are specified.
62 static int constexpr kChooseFBConfigAtt[] = {
63 GLX_RENDER_TYPE, GLX_RGBA_BIT,
64 GLX_DOUBLEBUFFER, True,
65 GLX_STENCIL_SIZE, 8,
66 None
67 };
68 // For some reason glXChooseVisual takes a non-const pointer to the attributes.
69 int chooseVisualAtt[] = {
70 GLX_RGBA,
71 GLX_DOUBLEBUFFER,
72 GLX_STENCIL_SIZE, 8,
73 None
74 };
75 SkASSERT(nullptr == fVisualInfo);
77 static const GLint kChooseFBConifgAttCnt = std::size(kChooseFBConfigAtt);
78 GLint msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 4];
79 memcpy(msaaChooseFBConfigAtt, kChooseFBConfigAtt, sizeof(kChooseFBConfigAtt));
80 SkASSERT(None == msaaChooseFBConfigAtt[kChooseFBConifgAttCnt - 1]);
81 msaaChooseFBConfigAtt[kChooseFBConifgAttCnt - 1] = GLX_SAMPLE_BUFFERS_ARB;
82 msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 0] = 1;
83 msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 1] = GLX_SAMPLES_ARB;
84 msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 2] = fRequestedDisplayParams.fMSAASampleCount;
85 msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 3] = None;
86 int n;
87 fFBConfig = glXChooseFBConfig(fDisplay, DefaultScreen(fDisplay), msaaChooseFBConfigAtt, &n);
88 if (n > 0) {
89 fVisualInfo = glXGetVisualFromFBConfig(fDisplay, *fFBConfig);
90 } else {
91 static const GLint kChooseVisualAttCnt = std::size(chooseVisualAtt);
92 GLint msaaChooseVisualAtt[kChooseVisualAttCnt + 4];
93 memcpy(msaaChooseVisualAtt, chooseVisualAtt, sizeof(chooseVisualAtt));
94 SkASSERT(None == msaaChooseVisualAtt[kChooseVisualAttCnt - 1]);
95 msaaChooseFBConfigAtt[kChooseVisualAttCnt - 1] = GLX_SAMPLE_BUFFERS_ARB;
96 msaaChooseFBConfigAtt[kChooseVisualAttCnt + 0] = 1;
97 msaaChooseFBConfigAtt[kChooseVisualAttCnt + 1] = GLX_SAMPLES_ARB;
98 msaaChooseFBConfigAtt[kChooseVisualAttCnt + 2] =
100 msaaChooseFBConfigAtt[kChooseVisualAttCnt + 3] = None;
101 fVisualInfo = glXChooseVisual(display, DefaultScreen(display), msaaChooseVisualAtt);
102 fFBConfig = nullptr;
103 }
104 }
105 if (nullptr == fVisualInfo) {
106 int n;
107 fFBConfig = glXChooseFBConfig(fDisplay, DefaultScreen(fDisplay), kChooseFBConfigAtt, &n);
108 if (n > 0) {
109 fVisualInfo = glXGetVisualFromFBConfig(fDisplay, *fFBConfig);
110 } else {
111 fVisualInfo = glXChooseVisual(display, DefaultScreen(display), chooseVisualAtt);
112 fFBConfig = nullptr;
113 }
114 }
115
116 if (fVisualInfo) {
117 Colormap colorMap = XCreateColormap(display,
118 RootWindow(display, fVisualInfo->screen),
119 fVisualInfo->visual,
120 AllocNone);
121 XSetWindowAttributes swa;
122 swa.colormap = colorMap;
123 swa.event_mask = kEventMask;
124 fWindow = XCreateWindow(display,
125 RootWindow(display, fVisualInfo->screen),
126 0, 0, // x, y
127 initialWidth, initialHeight,
128 0, // border width
129 fVisualInfo->depth,
130 InputOutput,
131 fVisualInfo->visual,
132 CWEventMask | CWColormap,
133 &swa);
134 }
135#endif
136 if (!fWindow) {
137 // Create a simple window instead. We will not be able to show GL
138 fWindow = XCreateSimpleWindow(display,
139 DefaultRootWindow(display),
140 0, 0, // x, y
141 initialWidth, initialHeight,
142 0, // border width
143 0, // border value
144 0); // background value
145 XSelectInput(display, fWindow, kEventMask);
146 }
147
148 if (!fWindow) {
149 return false;
150 }
151
152 fMSAASampleCount = fRequestedDisplayParams.fMSAASampleCount;
153
154 // set up to catch window delete message
155 fWmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False);
156 XSetWMProtocols(display, fWindow, &fWmDeleteMessage, 1);
157
158 // add to hashtable of windows
159 gWindowMap.add(this);
160
161 // init event variables
162 fPendingPaint = false;
163 fPendingResize = false;
164
165 return true;
166}
167
168void Window_unix::closeWindow() {
169 if (fDisplay) {
170 this->detach();
171 if (fGC) {
172 XFreeGC(fDisplay, fGC);
173 fGC = nullptr;
174 }
175 gWindowMap.remove(fWindow);
176 XDestroyWindow(fDisplay, fWindow);
177 fWindow = 0;
178 if (fFBConfig) {
179 XFree(fFBConfig);
180 fFBConfig = nullptr;
181 }
182 if (fVisualInfo) {
183 XFree(fVisualInfo);
184 fVisualInfo = nullptr;
185 }
186 fDisplay = nullptr;
187 }
188}
189
190static skui::Key get_key(KeySym keysym) {
191 static const struct {
192 KeySym fXK;
193 skui::Key fKey;
194 } gPair[] = {
195 { XK_BackSpace, skui::Key::kBack },
196 { XK_Clear, skui::Key::kBack },
197 { XK_Return, skui::Key::kOK },
198 { XK_Up, skui::Key::kUp },
199 { XK_Down, skui::Key::kDown },
200 { XK_Left, skui::Key::kLeft },
201 { XK_Right, skui::Key::kRight },
202 { XK_Tab, skui::Key::kTab },
203 { XK_Page_Up, skui::Key::kPageUp },
204 { XK_Page_Down, skui::Key::kPageDown },
205 { XK_Home, skui::Key::kHome },
206 { XK_End, skui::Key::kEnd },
207 { XK_Delete, skui::Key::kDelete },
208 { XK_Escape, skui::Key::kEscape },
209 { XK_Shift_L, skui::Key::kShift },
210 { XK_Shift_R, skui::Key::kShift },
211 { XK_Control_L, skui::Key::kCtrl },
212 { XK_Control_R, skui::Key::kCtrl },
213 { XK_Alt_L, skui::Key::kOption },
214 { XK_Alt_R, skui::Key::kOption },
215 { 'a', skui::Key::kA },
216 { 'c', skui::Key::kC },
217 { 'v', skui::Key::kV },
218 { 'x', skui::Key::kX },
219 { 'y', skui::Key::kY },
220 { 'z', skui::Key::kZ },
221 };
222 for (size_t i = 0; i < std::size(gPair); i++) {
223 if (gPair[i].fXK == keysym) {
224 return gPair[i].fKey;
225 }
226 }
227 return skui::Key::kNONE;
228}
229
230static skui::ModifierKey get_modifiers(const XEvent& event) {
231 static const struct {
232 unsigned fXMask;
233 skui::ModifierKey fSkMask;
234 } gModifiers[] = {
235 { ShiftMask, skui::ModifierKey::kShift },
236 { ControlMask, skui::ModifierKey::kControl },
237 { Mod1Mask, skui::ModifierKey::kOption },
238 };
239
241 for (size_t i = 0; i < std::size(gModifiers); ++i) {
242 if (event.xkey.state & gModifiers[i].fXMask) {
243 modifiers |= gModifiers[i].fSkMask;
244 }
245 }
246 return modifiers;
247}
248
249bool Window_unix::handleEvent(const XEvent& event) {
250 switch (event.type) {
251 case MapNotify:
252 if (!fGC) {
253 fGC = XCreateGC(fDisplay, fWindow, 0, nullptr);
254 }
255 break;
256
257 case ClientMessage:
258 if ((Atom)event.xclient.data.l[0] == fWmDeleteMessage &&
259 gWindowMap.count() == 1) {
260 return true;
261 }
262 break;
263
264 case ButtonPress:
265 switch (event.xbutton.button) {
266 case Button1:
267 this->onMouse(event.xbutton.x, event.xbutton.y,
269 break;
270 case Button4:
271 this->onMouseWheel(1.0f, 0, 0, get_modifiers(event));
272 break;
273 case Button5:
274 this->onMouseWheel(-1.0f, 0, 0, get_modifiers(event));
275 break;
276 }
277 break;
278
279 case ButtonRelease:
280 if (event.xbutton.button == Button1) {
281 this->onMouse(event.xbutton.x, event.xbutton.y,
283 }
284 break;
285
286 case MotionNotify:
287 this->onMouse(event.xmotion.x, event.xmotion.y,
289 break;
290
291 case KeyPress: {
292 int shiftLevel = (event.xkey.state & ShiftMask) ? 1 : 0;
293 KeySym keysym = XkbKeycodeToKeysym(fDisplay, event.xkey.keycode, 0, shiftLevel);
294 skui::Key key = get_key(keysym);
295 if (key != skui::Key::kNONE) {
297 if (keysym == XK_Escape) {
298 return true;
299 }
300 }
301 }
302
303 long uni = keysym2ucs(keysym);
304 if (uni != -1) {
305 (void) this->onChar((SkUnichar) uni, get_modifiers(event));
306 }
307 } break;
308
309 case KeyRelease: {
310 int shiftLevel = (event.xkey.state & ShiftMask) ? 1 : 0;
311 KeySym keysym = XkbKeycodeToKeysym(fDisplay, event.xkey.keycode,
312 0, shiftLevel);
313 skui::Key key = get_key(keysym);
314 (void) this->onKey(key, skui::InputState::kUp,
316 } break;
317
318 case SelectionClear: {
319 // Lost selection ownership
320 fClipboardText.clear();
321 } break;
322
323 case SelectionRequest: {
324 Atom UTF8 = XInternAtom(fDisplay, "UTF8_STRING", 0),
325 CLIPBOARD = XInternAtom(fDisplay, "CLIPBOARD", 0);
326
327 const XSelectionRequestEvent* xsr = &event.xselectionrequest;
328
329 XSelectionEvent xsel = {};
330 xsel.type = SelectionNotify;
331 xsel.requestor = xsr->requestor;
332 xsel.selection = xsr->selection;
333 xsel.target = xsr->target;
334 xsel.property = xsr->property;
335 xsel.time = xsr->time;
336
337 if (xsr->selection != CLIPBOARD) {
338 // A request for a different kind of selection. This shouldn't happen.
339 break;
340 }
341
342 if (fClipboardText.empty() || xsr->target != UTF8 || xsr->property == None) {
343 // We can't fulfill this request. Deny it.
344 xsel.property = None;
345 XSendEvent(fDisplay, xsr->requestor, True, NoEventMask, (XEvent*)&xsel);
346 } else {
347 // We can fulfill this request! Update the contents of the CLIPBOARD property,
348 // and let the requestor know.
349 XChangeProperty(fDisplay, xsr->requestor, xsr->property, UTF8, /*format=*/8,
350 PropModeReplace, (unsigned char*)fClipboardText.data(),
351 fClipboardText.length());
352 XSendEvent(fDisplay, xsr->requestor, True, NoEventMask, (XEvent*)&xsel);
353 }
354 } break;
355
356 default:
357 // these events should be handled in the main event loop
358 SkASSERT(event.type != Expose && event.type != ConfigureNotify);
359 break;
360 }
361
362 return false;
363}
364
365void Window_unix::setTitle(const char* title) {
366 XTextProperty textproperty;
367 if (!XStringListToTextProperty(const_cast<char**>(&title), 1, &textproperty)) {
368 return;
369 }
370 XSetWMName(fDisplay, fWindow, &textproperty);
371 XFree(textproperty.value);
372}
373
375 XMapWindow(fDisplay, fWindow);
376}
377
379 fBackend = attachType;
380
381 this->initWindow(fDisplay);
382
384 winInfo.fDisplay = fDisplay;
385 winInfo.fWindow = fWindow;
386 winInfo.fFBConfig = fFBConfig;
387 winInfo.fVisualInfo = fVisualInfo;
388
389 XWindowAttributes attrs;
390 if (XGetWindowAttributes(fDisplay, fWindow, &attrs)) {
391 winInfo.fWidth = attrs.width;
392 winInfo.fHeight = attrs.height;
393 } else {
394 winInfo.fWidth = winInfo.fHeight = 0;
395 }
396
397 switch (attachType) {
398#if defined(SK_DAWN) && defined(SK_GRAPHITE)
399 case kGraphiteDawn_BackendType:
402 break;
403#endif
404#ifdef SK_VULKAN
405 case kVulkan_BackendType:
407 break;
408#endif
409#if defined(SK_VULKAN) && defined(SK_GRAPHITE)
410 case kGraphiteVulkan_BackendType:
412 break;
413#endif
414#ifdef SK_GL
415 case kNativeGL_BackendType:
417 break;
418#endif
421 break;
422 }
423 this->onBackendCreated();
424
425 return (SkToBool(fWindowContext));
426}
427
429 XEvent event;
430 event.type = Expose;
431 event.xexpose.send_event = True;
432 event.xexpose.display = fDisplay;
433 event.xexpose.window = fWindow;
434 event.xexpose.x = 0;
435 event.xexpose.y = 0;
436 event.xexpose.width = this->width();
437 event.xexpose.height = this->height();
438 event.xexpose.count = 0;
439
440 XSendEvent(fDisplay, fWindow, False, 0, &event);
441}
442
444#if defined(SK_VULKAN)
445 // Vulkan on unix crashes if we try to reinitialize the vulkan context without remaking the
446 // window.
447 if (fBackend == kVulkan_BackendType && allowReattach) {
448 // Need to change these early, so attach() creates the window context correctly
450
451 this->detach();
452 this->attach(fBackend);
453 return;
454 }
455#endif
456
458}
459
461 Atom UTF8 = XInternAtom(fDisplay, "UTF8_STRING", 0),
462 CLIPBOARD = XInternAtom(fDisplay, "CLIPBOARD", 0),
463 XSEL_DATA = XInternAtom(fDisplay, "XSEL_DATA", 0);
464
465 // Ask for a UTF8 copy of the CLIPBOARD...
466 XEvent event;
467 XConvertSelection(fDisplay, CLIPBOARD, UTF8, XSEL_DATA, fWindow, CurrentTime);
468 XSync(fDisplay, 0);
469 XNextEvent(fDisplay, &event);
470 if (event.type == SelectionNotify &&
471 event.xselection.selection == CLIPBOARD &&
472 event.xselection.property != None) {
473
474 // We got a response
475 Atom type;
476 int format;
477 unsigned long nitems, bytes_after;
478 char* data;
479
480 // Fetch the CLIPBOARD property
481 XSelectionEvent xsel = event.xselection;
482 XGetWindowProperty(xsel.display, xsel.requestor, xsel.property, /*offset=*/0,
483 /*length=*/~0L, /*delete=*/False, AnyPropertyType, &type, &format,
484 &nitems, &bytes_after, (unsigned char**)&data);
485 SkASSERT(bytes_after == 0);
486 if (type == UTF8) {
487 fClipboardText.assign(data, nitems);
488 }
489 XFree(data);
490 XDeleteProperty(xsel.display, xsel.requestor, xsel.property);
491 }
492 return fClipboardText.c_str();
493}
494
496 fClipboardText.assign(text);
497
498 // Take ownership of the CLIPBOARD
499 Atom CLIPBOARD = XInternAtom(fDisplay, "CLIPBOARD", 0);
500 XSetSelectionOwner(fDisplay, CLIPBOARD, fWindow, CurrentTime);
501}
502
503} // namespace sk_app
#define SkASSERT(cond)
Definition: SkAssert.h:116
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
int32_t SkUnichar
Definition: SkTypes.h:175
GLenum type
void show() override
bool handleEvent(const XEvent &event)
void setTitle(const char *) override
const char * getClipboardText() override
bool attach(BackendType) override
static SkTDynamicHash< Window_unix, XWindow > gWindowMap
Definition: Window_unix.h:58
void onInval() override
bool initWindow(Display *display)
Definition: Window_unix.cpp:44
void setClipboardText(const char *) override
void setRequestedDisplayParams(const DisplayParams &, bool allowReattach) override
bool onKey(skui::Key key, skui::InputState state, skui::ModifierKey modifiers)
Definition: Window.cpp:52
bool onChar(SkUnichar c, skui::ModifierKey modifiers)
Definition: Window.cpp:48
static Window * CreateNativeWindow(void *platformData)
void detach()
Definition: Window.cpp:25
@ kRaster_BackendType
Definition: Window.h:90
virtual void setRequestedDisplayParams(const DisplayParams &, bool allowReattach=true)
Definition: Window.cpp:137
int height() const
Definition: Window.cpp:130
bool onMouseWheel(float delta, int x, int y, skui::ModifierKey modifiers)
Definition: Window.cpp:60
DisplayParams fRequestedDisplayParams
Definition: Window.h:176
std::unique_ptr< skwindow::WindowContext > fWindowContext
Definition: Window.h:179
bool onMouse(int x, int y, skui::InputState state, skui::ModifierKey modifiers)
Definition: Window.cpp:56
int width() const
Definition: Window.cpp:123
void onBackendCreated()
Definition: Window.cpp:44
const EmbeddedViewParams * params
GLFWwindow * window
Definition: main.cc:45
FlKeyEvent * event
uint32_t uint32_t * format
Definition: dart.idl:411
std::u16string text
long keysym2ucs(KeySym keysym)
Definition: keysym2ucs.c:818
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
static skui::ModifierKey get_modifiers(const XEvent &event)
static skui::Key get_key(KeySym keysym)
const long kEventMask
Definition: Window_unix.cpp:40
ModifierKey
Definition: ModifierKey.h:9
Key
Definition: Key.h:6
@ kBack
(CLR)
@ kHome
the home key - added to match android
@ kEnd
the red key
@ kOK
the center key
std::unique_ptr< WindowContext > MakeGLForXlib(const XlibWindowInfo &winInfo, const DisplayParams &params)
std::unique_ptr< WindowContext > MakeGraphiteDawnVulkanForXlib(const XlibWindowInfo &info, const DisplayParams &params)
std::unique_ptr< WindowContext > MakeGraphiteVulkanForXlib(const XlibWindowInfo &info, const DisplayParams &displayParams)
std::unique_ptr< WindowContext > MakeRasterForXlib(const XlibWindowInfo &info, const DisplayParams &params)
std::unique_ptr< WindowContext > MakeVulkanForXlib(const XlibWindowInfo &info, const DisplayParams &displayParams)
GdkModifierType state
Definition: fl_key_event.h:32
guint16 keycode
Definition: fl_key_event.h:28
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63