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, &registry_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