1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "WindowSurfaceWayland.h"
8
9 #include "base/message_loop.h" // for MessageLoop
10 #include "base/task.h" // for NewRunnableMethod, etc
11 #include "nsPrintfCString.h"
12 #include "mozilla/gfx/2D.h"
13 #include "mozilla/gfx/Tools.h"
14 #include "gfxPlatform.h"
15 #include "mozcontainer.h"
16 #include "nsCOMArray.h"
17 #include "mozilla/StaticMutex.h"
18
19 #include <gdk/gdkwayland.h>
20 #include <sys/mman.h>
21 #include <assert.h>
22 #include <fcntl.h>
23 #include <errno.h>
24
25 /*
26 Wayland multi-thread rendering scheme
27
28 Every rendering thread (main thread, compositor thread) contains its own
29 nsWaylandDisplay object connected to Wayland compositor (Mutter, Weston, etc.)
30
31 WindowSurfaceWayland implements WindowSurface class and draws nsWindow by
32 WindowSurface interface (Lock, Commit) to screen through nsWaylandDisplay.
33
34 ----------------------
35 | Wayland compositor |
36 ----------------------
37 ^
38 |
39 ----------------------
40 | nsWaylandDisplay |
41 ----------------------
42 ^ ^
43 | |
44 | |
45 | --------------------------------- ------------------
46 | | WindowSurfaceWayland |<------>| nsWindow |
47 | | | ------------------
48 | | ----------------------- |
49 | | | WindowBackBuffer | |
50 | | | | |
51 | | | ------------------- | |
52 | | | | WaylandShmPool | | |
53 | | | ------------------- | |
54 | | ----------------------- |
55 | | |
56 | | ----------------------- |
57 | | | WindowBackBuffer | |
58 | | | | |
59 | | | ------------------- | |
60 | | | | WaylandShmPool | | |
61 | | | ------------------- | |
62 | | ----------------------- |
63 | ---------------------------------
64 |
65 |
66 --------------------------------- ------------------
67 | WindowSurfaceWayland |<------>| nsWindow |
68 | | ------------------
69 | ----------------------- |
70 | | WindowBackBuffer | |
71 | | | |
72 | | ------------------- | |
73 | | | WaylandShmPool | | |
74 | | ------------------- | |
75 | ----------------------- |
76 | |
77 | ----------------------- |
78 | | WindowBackBuffer | |
79 | | | |
80 | | ------------------- | |
81 | | | WaylandShmPool | | |
82 | | ------------------- | |
83 | ----------------------- |
84 ---------------------------------
85
86 nsWaylandDisplay
87
88 Is our connection to Wayland display server,
89 holds our display connection (wl_display) and event queue (wl_event_queue).
90
91 nsWaylandDisplay is created for every thread which sends data to Wayland
92 compositor. Wayland events for main thread is served by default Gtk+ loop,
93 for other threads (compositor) we must create wl_event_queue and run event loop.
94
95
96 WindowSurfaceWayland
97
98 Is a Wayland implementation of WindowSurface class for WindowSurfaceProvider,
99 we implement Lock() and Commit() interfaces from WindowSurface
100 for actual drawing.
101
102 One WindowSurfaceWayland draws one nsWindow so those are tied 1:1.
103 At Wayland level it holds one wl_surface object.
104
105 To perform visualiation of nsWindow, WindowSurfaceWayland contains one
106 wl_surface and two wl_buffer objects (owned by WindowBackBuffer)
107 as we use double buffering. When nsWindow drawing is finished to wl_buffer,
108 the wl_buffer is attached to wl_surface and it's sent to Wayland compositor.
109
110
111 WindowBackBuffer
112
113 Manages one wl_buffer. It owns wl_buffer object, owns WaylandShmPool
114 (which provides shared memory) and ties them together.
115
116 Wl_buffer is a main Wayland object with actual graphics data.
117 Wl_buffer basically represent one complete window screen.
118 When double buffering is involved every window (GdkWindow for instance)
119 utilises two wl_buffers which are cycled. One is filed with data by application
120 and one is rendered by compositor.
121
122
123 WaylandShmPool
124
125 WaylandShmPool acts as a manager of shared memory for WindowBackBuffer.
126 Allocates it, holds reference to it and releases it.
127
128 We allocate shared memory (shm) by mmap(..., MAP_SHARED,...) as an interface
129 between us and wayland compositor. We draw our graphics data to the shm and
130 handle to wayland compositor by WindowBackBuffer/WindowSurfaceWayland
131 (wl_buffer/wl_surface).
132 */
133
134 namespace mozilla {
135 namespace widget {
136
137 #define BUFFER_BPP 4
138
139 // TODO: How many rendering threads do we actualy handle?
140 static nsCOMArray<nsWaylandDisplay> gWaylandDisplays;
141 static StaticMutex gWaylandDisplaysMutex;
142
143 // Each thread which is using wayland connection (wl_display) has to operate
144 // its own wl_event_queue. Main Firefox thread wl_event_queue is handled
145 // by Gtk main loop, other threads/wl_event_queue has to be handled by us.
146 //
147 // nsWaylandDisplay is our interface to wayland compositor. It provides wayland
148 // global objects as we need (wl_display, wl_shm) and operates wl_event_queue on
149 // compositor (not the main) thread.
150 static nsWaylandDisplay *WaylandDisplayGet(wl_display *aDisplay);
151 static void WaylandDisplayRelease(wl_display *aDisplay);
152 static void WaylandDisplayLoop(wl_display *aDisplay);
153
154 // TODO: is the 60pfs loop correct?
155 #define EVENT_LOOP_DELAY (1000 / 60)
156
157 // Get WaylandDisplay for given wl_display and actual calling thread.
WaylandDisplayGetLocked(wl_display * aDisplay,const StaticMutexAutoLock &)158 static nsWaylandDisplay *WaylandDisplayGetLocked(wl_display *aDisplay,
159 const StaticMutexAutoLock &) {
160 nsWaylandDisplay *waylandDisplay = nullptr;
161
162 int len = gWaylandDisplays.Count();
163 for (int i = 0; i < len; i++) {
164 if (gWaylandDisplays[i]->Matches(aDisplay)) {
165 waylandDisplay = gWaylandDisplays[i];
166 break;
167 }
168 }
169
170 if (!waylandDisplay) {
171 waylandDisplay = new nsWaylandDisplay(aDisplay);
172 gWaylandDisplays.AppendObject(waylandDisplay);
173 }
174
175 NS_ADDREF(waylandDisplay);
176 return waylandDisplay;
177 }
178
WaylandDisplayGet(wl_display * aDisplay)179 static nsWaylandDisplay *WaylandDisplayGet(wl_display *aDisplay) {
180 StaticMutexAutoLock lock(gWaylandDisplaysMutex);
181 return WaylandDisplayGetLocked(aDisplay, lock);
182 }
183
WaylandDisplayReleaseLocked(wl_display * aDisplay,const StaticMutexAutoLock &)184 static bool WaylandDisplayReleaseLocked(wl_display *aDisplay,
185 const StaticMutexAutoLock &) {
186 int len = gWaylandDisplays.Count();
187 for (int i = 0; i < len; i++) {
188 if (gWaylandDisplays[i]->Matches(aDisplay)) {
189 int rc = gWaylandDisplays[i]->Release();
190 // nsCOMArray::AppendObject()/RemoveObjectAt() also call
191 // AddRef()/Release() so remove WaylandDisplay when ref count is 1.
192 if (rc == 1) {
193 gWaylandDisplays.RemoveObjectAt(i);
194 }
195 return true;
196 }
197 }
198 MOZ_ASSERT(false, "Missing nsWaylandDisplay for this thread!");
199 return false;
200 }
201
WaylandDisplayRelease(wl_display * aDisplay)202 static void WaylandDisplayRelease(wl_display *aDisplay) {
203 StaticMutexAutoLock lock(gWaylandDisplaysMutex);
204 WaylandDisplayReleaseLocked(aDisplay, lock);
205 }
206
WaylandDisplayLoopLocked(wl_display * aDisplay,const StaticMutexAutoLock &)207 static void WaylandDisplayLoopLocked(wl_display *aDisplay,
208 const StaticMutexAutoLock &) {
209 int len = gWaylandDisplays.Count();
210 for (int i = 0; i < len; i++) {
211 if (gWaylandDisplays[i]->Matches(aDisplay)) {
212 if (gWaylandDisplays[i]->DisplayLoop()) {
213 MessageLoop::current()->PostDelayedTask(
214 NewRunnableFunction("WaylandDisplayLoop", &WaylandDisplayLoop,
215 aDisplay),
216 EVENT_LOOP_DELAY);
217 }
218 break;
219 }
220 }
221 }
222
WaylandDisplayLoop(wl_display * aDisplay)223 static void WaylandDisplayLoop(wl_display *aDisplay) {
224 MOZ_ASSERT(!NS_IsMainThread());
225 StaticMutexAutoLock lock(gWaylandDisplaysMutex);
226 WaylandDisplayLoopLocked(aDisplay, lock);
227 }
228
global_registry_handler(void * data,wl_registry * registry,uint32_t id,const char * interface,uint32_t version)229 static void global_registry_handler(void *data, wl_registry *registry,
230 uint32_t id, const char *interface,
231 uint32_t version) {
232 if (strcmp(interface, "wl_shm") == 0) {
233 auto interface = reinterpret_cast<nsWaylandDisplay *>(data);
234 auto shm = static_cast<wl_shm *>(
235 wl_registry_bind(registry, id, &wl_shm_interface, 1));
236 wl_proxy_set_queue((struct wl_proxy *)shm, interface->GetEventQueue());
237 interface->SetShm(shm);
238 }
239 }
240
global_registry_remover(void * data,wl_registry * registry,uint32_t id)241 static void global_registry_remover(void *data, wl_registry *registry,
242 uint32_t id) {}
243
244 static const struct wl_registry_listener registry_listener = {
245 global_registry_handler, global_registry_remover};
246
GetShm()247 wl_shm *nsWaylandDisplay::GetShm() {
248 MOZ_ASSERT(mThreadId == PR_GetCurrentThread());
249
250 if (!mShm) {
251 // wl_shm is not provided by Gtk so we need to query wayland directly
252 // See weston/simple-shm.c and create_display() for reference.
253 wl_registry *registry = wl_display_get_registry(mDisplay);
254 wl_registry_add_listener(registry, ®istry_listener, this);
255
256 wl_proxy_set_queue((struct wl_proxy *)registry, mEventQueue);
257 if (mEventQueue) {
258 wl_display_roundtrip_queue(mDisplay, mEventQueue);
259 } else {
260 wl_display_roundtrip(mDisplay);
261 }
262
263 MOZ_RELEASE_ASSERT(mShm, "Wayland registry query failed!");
264 }
265
266 return (mShm);
267 }
268
DisplayLoop()269 bool nsWaylandDisplay::DisplayLoop() {
270 wl_display_dispatch_queue_pending(mDisplay, mEventQueue);
271 return true;
272 }
273
Matches(wl_display * aDisplay)274 bool nsWaylandDisplay::Matches(wl_display *aDisplay) {
275 return mThreadId == PR_GetCurrentThread() && aDisplay == mDisplay;
276 }
277
278 NS_IMPL_ISUPPORTS(nsWaylandDisplay, nsISupports);
279
nsWaylandDisplay(wl_display * aDisplay)280 nsWaylandDisplay::nsWaylandDisplay(wl_display *aDisplay)
281 : mThreadId(PR_GetCurrentThread())
282 // gfx::SurfaceFormat::B8G8R8A8 is a basic Wayland format
283 // and is always present.
284 ,
285 mFormat(gfx::SurfaceFormat::B8G8R8A8),
286 mShm(nullptr),
287 mDisplay(aDisplay) {
288 if (NS_IsMainThread()) {
289 // Use default event queue in main thread operated by Gtk+.
290 mEventQueue = nullptr;
291 } else {
292 mEventQueue = wl_display_create_queue(mDisplay);
293 MessageLoop::current()->PostTask(NewRunnableFunction(
294 "WaylandDisplayLoop", &WaylandDisplayLoop, mDisplay));
295 }
296 }
297
~nsWaylandDisplay()298 nsWaylandDisplay::~nsWaylandDisplay() {
299 MOZ_ASSERT(mThreadId == PR_GetCurrentThread());
300 // Owned by Gtk+, we don't need to release
301 mDisplay = nullptr;
302
303 if (mEventQueue) {
304 wl_event_queue_destroy(mEventQueue);
305 mEventQueue = nullptr;
306 }
307 }
308
CreateTemporaryFile(int aSize)309 int WaylandShmPool::CreateTemporaryFile(int aSize) {
310 const char *tmppath = getenv("XDG_RUNTIME_DIR");
311 MOZ_RELEASE_ASSERT(tmppath, "Missing XDG_RUNTIME_DIR env variable.");
312
313 nsPrintfCString tmpname("%s/mozilla-shared-XXXXXX", tmppath);
314
315 char *filename;
316 int fd = -1;
317 int ret = 0;
318
319 if (tmpname.GetMutableData(&filename)) {
320 fd = mkstemp(filename);
321 if (fd >= 0) {
322 int flags = fcntl(fd, F_GETFD);
323 if (flags >= 0) {
324 fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
325 }
326 }
327 }
328
329 if (fd >= 0) {
330 unlink(tmpname.get());
331 } else {
332 printf_stderr("Unable to create mapping file %s\n", filename);
333 MOZ_CRASH();
334 }
335
336 #ifdef HAVE_POSIX_FALLOCATE
337 do {
338 ret = posix_fallocate(fd, 0, aSize);
339 } while (ret == EINTR);
340 if (ret != 0) {
341 close(fd);
342 }
343 #else
344 do {
345 ret = ftruncate(fd, aSize);
346 } while (ret < 0 && errno == EINTR);
347 if (ret < 0) {
348 close(fd);
349 }
350 #endif
351 MOZ_RELEASE_ASSERT(ret == 0, "Mapping file allocation failed.");
352
353 return fd;
354 }
355
WaylandShmPool(nsWaylandDisplay * aWaylandDisplay,int aSize)356 WaylandShmPool::WaylandShmPool(nsWaylandDisplay *aWaylandDisplay, int aSize)
357 : mAllocatedSize(aSize) {
358 mShmPoolFd = CreateTemporaryFile(mAllocatedSize);
359 mImageData = mmap(nullptr, mAllocatedSize, PROT_READ | PROT_WRITE, MAP_SHARED,
360 mShmPoolFd, 0);
361 MOZ_RELEASE_ASSERT(mImageData != MAP_FAILED,
362 "Unable to map drawing surface!");
363
364 mShmPool =
365 wl_shm_create_pool(aWaylandDisplay->GetShm(), mShmPoolFd, mAllocatedSize);
366
367 // We set our queue to get mShmPool events at compositor thread.
368 wl_proxy_set_queue((struct wl_proxy *)mShmPool,
369 aWaylandDisplay->GetEventQueue());
370 }
371
Resize(int aSize)372 bool WaylandShmPool::Resize(int aSize) {
373 // We do size increase only
374 if (aSize <= mAllocatedSize) return true;
375
376 if (ftruncate(mShmPoolFd, aSize) < 0) return false;
377
378 #ifdef HAVE_POSIX_FALLOCATE
379 do {
380 errno = posix_fallocate(mShmPoolFd, 0, aSize);
381 } while (errno == EINTR);
382 if (errno != 0) return false;
383 #endif
384
385 wl_shm_pool_resize(mShmPool, aSize);
386
387 munmap(mImageData, mAllocatedSize);
388
389 mImageData =
390 mmap(nullptr, aSize, PROT_READ | PROT_WRITE, MAP_SHARED, mShmPoolFd, 0);
391 if (mImageData == MAP_FAILED) return false;
392
393 mAllocatedSize = aSize;
394 return true;
395 }
396
SetImageDataFromPool(class WaylandShmPool * aSourcePool,int aImageDataSize)397 void WaylandShmPool::SetImageDataFromPool(class WaylandShmPool *aSourcePool,
398 int aImageDataSize) {
399 MOZ_ASSERT(mAllocatedSize >= aImageDataSize, "WaylandShmPool overflows!");
400 memcpy(mImageData, aSourcePool->GetImageData(), aImageDataSize);
401 }
402
~WaylandShmPool()403 WaylandShmPool::~WaylandShmPool() {
404 munmap(mImageData, mAllocatedSize);
405 wl_shm_pool_destroy(mShmPool);
406 close(mShmPoolFd);
407 }
408
buffer_release(void * data,wl_buffer * buffer)409 static void buffer_release(void *data, wl_buffer *buffer) {
410 auto surface = reinterpret_cast<WindowBackBuffer *>(data);
411 surface->Detach();
412 }
413
414 static const struct wl_buffer_listener buffer_listener = {buffer_release};
415
Create(int aWidth,int aHeight)416 void WindowBackBuffer::Create(int aWidth, int aHeight) {
417 MOZ_ASSERT(!IsAttached(), "We can't resize attached buffers.");
418
419 int newBufferSize = aWidth * aHeight * BUFFER_BPP;
420 mShmPool.Resize(newBufferSize);
421
422 mWaylandBuffer =
423 wl_shm_pool_create_buffer(mShmPool.GetShmPool(), 0, aWidth, aHeight,
424 aWidth * BUFFER_BPP, WL_SHM_FORMAT_ARGB8888);
425 wl_proxy_set_queue((struct wl_proxy *)mWaylandBuffer,
426 mWaylandDisplay->GetEventQueue());
427 wl_buffer_add_listener(mWaylandBuffer, &buffer_listener, this);
428
429 mWidth = aWidth;
430 mHeight = aHeight;
431 }
432
Release()433 void WindowBackBuffer::Release() {
434 wl_buffer_destroy(mWaylandBuffer);
435 mWidth = mHeight = 0;
436 }
437
WindowBackBuffer(nsWaylandDisplay * aWaylandDisplay,int aWidth,int aHeight)438 WindowBackBuffer::WindowBackBuffer(nsWaylandDisplay *aWaylandDisplay,
439 int aWidth, int aHeight)
440 : mShmPool(aWaylandDisplay, aWidth * aHeight * BUFFER_BPP),
441 mWaylandBuffer(nullptr),
442 mWidth(aWidth),
443 mHeight(aHeight),
444 mAttached(false),
445 mWaylandDisplay(aWaylandDisplay) {
446 Create(aWidth, aHeight);
447 }
448
~WindowBackBuffer()449 WindowBackBuffer::~WindowBackBuffer() { Release(); }
450
Resize(int aWidth,int aHeight)451 bool WindowBackBuffer::Resize(int aWidth, int aHeight) {
452 if (aWidth == mWidth && aHeight == mHeight) return true;
453
454 Release();
455 Create(aWidth, aHeight);
456
457 return (mWaylandBuffer != nullptr);
458 }
459
Attach(wl_surface * aSurface)460 void WindowBackBuffer::Attach(wl_surface *aSurface) {
461 wl_surface_attach(aSurface, mWaylandBuffer, 0, 0);
462 wl_surface_commit(aSurface);
463 wl_display_flush(mWaylandDisplay->GetDisplay());
464 mAttached = true;
465 }
466
Detach()467 void WindowBackBuffer::Detach() { mAttached = false; }
468
SetImageDataFromBackBuffer(class WindowBackBuffer * aSourceBuffer)469 bool WindowBackBuffer::SetImageDataFromBackBuffer(
470 class WindowBackBuffer *aSourceBuffer) {
471 if (!IsMatchingSize(aSourceBuffer)) {
472 Resize(aSourceBuffer->mWidth, aSourceBuffer->mHeight);
473 }
474
475 mShmPool.SetImageDataFromPool(
476 &aSourceBuffer->mShmPool,
477 aSourceBuffer->mWidth * aSourceBuffer->mHeight * BUFFER_BPP);
478 return true;
479 }
480
Lock(const LayoutDeviceIntRegion & aRegion)481 already_AddRefed<gfx::DrawTarget> WindowBackBuffer::Lock(
482 const LayoutDeviceIntRegion &aRegion) {
483 gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect();
484 gfx::IntSize lockSize(bounds.XMost(), bounds.YMost());
485
486 return gfxPlatform::CreateDrawTargetForData(
487 static_cast<unsigned char *>(mShmPool.GetImageData()), lockSize,
488 BUFFER_BPP * mWidth, mWaylandDisplay->GetSurfaceFormat());
489 }
490
frame_callback_handler(void * data,struct wl_callback * callback,uint32_t time)491 static void frame_callback_handler(void *data, struct wl_callback *callback,
492 uint32_t time) {
493 auto surface = reinterpret_cast<WindowSurfaceWayland *>(data);
494 surface->FrameCallbackHandler();
495 }
496
497 static const struct wl_callback_listener frame_listener = {
498 frame_callback_handler};
499
WindowSurfaceWayland(nsWindow * aWindow)500 WindowSurfaceWayland::WindowSurfaceWayland(nsWindow *aWindow)
501 : mWindow(aWindow),
502 mWaylandDisplay(WaylandDisplayGet(aWindow->GetWaylandDisplay())),
503 mFrontBuffer(nullptr),
504 mBackBuffer(nullptr),
505 mFrameCallback(nullptr),
506 mFrameCallbackSurface(nullptr),
507 mDisplayThreadMessageLoop(MessageLoop::current()),
508 mDelayedCommit(false),
509 mFullScreenDamage(false),
510 mIsMainThread(NS_IsMainThread()) {}
511
~WindowSurfaceWayland()512 WindowSurfaceWayland::~WindowSurfaceWayland() {
513 delete mFrontBuffer;
514 delete mBackBuffer;
515
516 if (mFrameCallback) {
517 wl_callback_destroy(mFrameCallback);
518 }
519
520 if (!mIsMainThread) {
521 // We can be destroyed from main thread even though we was created/used
522 // in compositor thread. We have to unref/delete WaylandDisplay in
523 // compositor thread then and we can't use MessageLoop::current() here.
524 mDisplayThreadMessageLoop->PostTask(
525 NewRunnableFunction("WaylandDisplayRelease", &WaylandDisplayRelease,
526 mWaylandDisplay->GetDisplay()));
527 } else {
528 WaylandDisplayRelease(mWaylandDisplay->GetDisplay());
529 }
530 }
531
UpdateScaleFactor()532 void WindowSurfaceWayland::UpdateScaleFactor() {
533 wl_surface *waylandSurface = mWindow->GetWaylandSurface();
534 if (waylandSurface) {
535 wl_surface_set_buffer_scale(waylandSurface, mWindow->GdkScaleFactor());
536 }
537 }
538
GetBufferToDraw(int aWidth,int aHeight)539 WindowBackBuffer *WindowSurfaceWayland::GetBufferToDraw(int aWidth,
540 int aHeight) {
541 if (!mFrontBuffer) {
542 mFrontBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight);
543 mBackBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight);
544 return mFrontBuffer;
545 }
546
547 if (!mFrontBuffer->IsAttached()) {
548 if (!mFrontBuffer->IsMatchingSize(aWidth, aHeight)) {
549 mFrontBuffer->Resize(aWidth, aHeight);
550 // There's a chance that scale factor has been changed
551 // when buffer size changed
552 UpdateScaleFactor();
553 }
554 return mFrontBuffer;
555 }
556
557 // Front buffer is used by compositor, draw to back buffer
558 if (mBackBuffer->IsAttached()) {
559 NS_WARNING("No drawing buffer available");
560 return nullptr;
561 }
562
563 MOZ_ASSERT(!mDelayedCommit,
564 "Uncommitted buffer switch, screen artifacts ahead.");
565
566 WindowBackBuffer *tmp = mFrontBuffer;
567 mFrontBuffer = mBackBuffer;
568 mBackBuffer = tmp;
569
570 if (mBackBuffer->IsMatchingSize(aWidth, aHeight)) {
571 // Former front buffer has the same size as a requested one.
572 // Gecko may expect a content already drawn on screen so copy
573 // existing data to the new buffer.
574 mFrontBuffer->SetImageDataFromBackBuffer(mBackBuffer);
575 // When buffer switches we need to damage whole screen
576 // (https://bugzilla.redhat.com/show_bug.cgi?id=1418260)
577 mFullScreenDamage = true;
578 } else {
579 // Former buffer has different size from the new request. Only resize
580 // the new buffer and leave gecko to render new whole content.
581 mFrontBuffer->Resize(aWidth, aHeight);
582 }
583
584 return mFrontBuffer;
585 }
586
Lock(const LayoutDeviceIntRegion & aRegion)587 already_AddRefed<gfx::DrawTarget> WindowSurfaceWayland::Lock(
588 const LayoutDeviceIntRegion &aRegion) {
589 MOZ_ASSERT(mIsMainThread == NS_IsMainThread());
590
591 // We allocate back buffer to widget size but return only
592 // portion requested by aRegion.
593 LayoutDeviceIntRect rect = mWindow->GetBounds();
594 WindowBackBuffer *buffer = GetBufferToDraw(rect.width, rect.height);
595 if (!buffer) {
596 NS_WARNING("No drawing buffer available");
597 return nullptr;
598 }
599
600 return buffer->Lock(aRegion);
601 }
602
Commit(const LayoutDeviceIntRegion & aInvalidRegion)603 void WindowSurfaceWayland::Commit(const LayoutDeviceIntRegion &aInvalidRegion) {
604 MOZ_ASSERT(mIsMainThread == NS_IsMainThread());
605
606 wl_surface *waylandSurface = mWindow->GetWaylandSurface();
607 if (!waylandSurface) {
608 // Target window is already destroyed - don't bother to render there.
609 return;
610 }
611 wl_proxy_set_queue((struct wl_proxy *)waylandSurface,
612 mWaylandDisplay->GetEventQueue());
613
614 if (mFullScreenDamage) {
615 LayoutDeviceIntRect rect = mWindow->GetBounds();
616 wl_surface_damage(waylandSurface, 0, 0, rect.width, rect.height);
617 mFullScreenDamage = false;
618 } else {
619 for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
620 const mozilla::LayoutDeviceIntRect &r = iter.Get();
621 wl_surface_damage(waylandSurface, r.x, r.y, r.width, r.height);
622 }
623 }
624
625 // Frame callback is always connected to actual wl_surface. When the surface
626 // is unmapped/deleted the frame callback is never called. Unfortunatelly
627 // we don't know if the frame callback is not going to be called.
628 // But our mozcontainer code deletes wl_surface when the GdkWindow is hidden
629 // creates a new one when is visible.
630 if (mFrameCallback && mFrameCallbackSurface == waylandSurface) {
631 // Do nothing here - we have a valid wl_surface and the buffer will be
632 // commited to compositor in next frame callback event.
633 mDelayedCommit = true;
634 return;
635 } else {
636 if (mFrameCallback) {
637 // Delete frame callback connected to obsoleted wl_surface.
638 wl_callback_destroy(mFrameCallback);
639 }
640
641 mFrameCallback = wl_surface_frame(waylandSurface);
642 wl_callback_add_listener(mFrameCallback, &frame_listener, this);
643 mFrameCallbackSurface = waylandSurface;
644
645 // There's no pending frame callback so we can draw immediately
646 // and create frame callback for possible subsequent drawing.
647 mFrontBuffer->Attach(waylandSurface);
648 mDelayedCommit = false;
649 }
650 }
651
FrameCallbackHandler()652 void WindowSurfaceWayland::FrameCallbackHandler() {
653 MOZ_ASSERT(mIsMainThread == NS_IsMainThread());
654
655 if (mFrameCallback) {
656 wl_callback_destroy(mFrameCallback);
657 mFrameCallback = nullptr;
658 mFrameCallbackSurface = nullptr;
659 }
660
661 if (mDelayedCommit) {
662 wl_surface *waylandSurface = mWindow->GetWaylandSurface();
663 if (!waylandSurface) {
664 // Target window is already destroyed - don't bother to render there.
665 NS_WARNING("No drawing buffer available");
666 return;
667 }
668 wl_proxy_set_queue((struct wl_proxy *)waylandSurface,
669 mWaylandDisplay->GetEventQueue());
670
671 // Send pending surface to compositor and register frame callback
672 // for possible subsequent drawing.
673 mFrameCallback = wl_surface_frame(waylandSurface);
674 wl_callback_add_listener(mFrameCallback, &frame_listener, this);
675 mFrameCallbackSurface = waylandSurface;
676
677 mFrontBuffer->Attach(waylandSurface);
678 mDelayedCommit = false;
679 }
680 }
681
682 } // namespace widget
683 } // namespace mozilla
684