1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #define PANGO_ENABLE_BACKEND
7 #define PANGO_ENABLE_ENGINE
8 
9 #include "gfxPlatformGtk.h"
10 #include "prenv.h"
11 
12 #include "nsUnicharUtils.h"
13 #include "nsUnicodeProperties.h"
14 #include "gfx2DGlue.h"
15 #include "gfxFcPlatformFontList.h"
16 #include "gfxFontconfigUtils.h"
17 #include "gfxFontconfigFonts.h"
18 #include "gfxConfig.h"
19 #include "gfxContext.h"
20 #include "gfxUserFontSet.h"
21 #include "gfxUtils.h"
22 #include "gfxFT2FontBase.h"
23 #include "gfxPrefs.h"
24 #include "VsyncSource.h"
25 #include "mozilla/Atomics.h"
26 #include "mozilla/Monitor.h"
27 #include "base/task.h"
28 #include "base/thread.h"
29 #include "base/message_loop.h"
30 #include "mozilla/gfx/Logging.h"
31 
32 #include "mozilla/gfx/2D.h"
33 
34 #include "cairo.h"
35 #include <gtk/gtk.h>
36 
37 #include "gfxImageSurface.h"
38 #ifdef MOZ_X11
39 #include <gdk/gdkx.h>
40 #include "gfxXlibSurface.h"
41 #include "cairo-xlib.h"
42 #include "mozilla/Preferences.h"
43 #include "mozilla/X11Util.h"
44 
45 #ifdef GL_PROVIDER_GLX
46 #include "GLContextProvider.h"
47 #include "GLContextGLX.h"
48 #include "GLXLibrary.h"
49 #endif
50 
51 /* Undefine the Status from Xlib since it will conflict with system headers on OSX */
52 #if defined(__APPLE__) && defined(Status)
53 #undef Status
54 #endif
55 
56 #endif /* MOZ_X11 */
57 
58 #include <fontconfig/fontconfig.h>
59 
60 #include "nsMathUtils.h"
61 
62 #define GDK_PIXMAP_SIZE_MAX 32767
63 
64 #define GFX_PREF_MAX_GENERIC_SUBSTITUTIONS "gfx.font_rendering.fontconfig.max_generic_substitutions"
65 
66 using namespace mozilla;
67 using namespace mozilla::gfx;
68 using namespace mozilla::unicode;
69 
70 gfxFontconfigUtils *gfxPlatformGtk::sFontconfigUtils = nullptr;
71 
72 #if (MOZ_WIDGET_GTK == 2)
73 static cairo_user_data_key_t cairo_gdk_drawable_key;
74 #endif
75 
76 bool gfxPlatformGtk::sUseFcFontList = false;
77 
gfxPlatformGtk()78 gfxPlatformGtk::gfxPlatformGtk()
79 {
80     gtk_init(nullptr, nullptr);
81 
82     sUseFcFontList = mozilla::Preferences::GetBool("gfx.font_rendering.fontconfig.fontlist.enabled");
83     if (!sUseFcFontList && !sFontconfigUtils) {
84         sFontconfigUtils = gfxFontconfigUtils::GetFontconfigUtils();
85     }
86 
87     mMaxGenericSubstitutions = UNINITIALIZED_VALUE;
88 
89 #ifdef MOZ_X11
90     if (XRE_IsParentProcess()) {
91       if (GDK_IS_X11_DISPLAY(gdk_display_get_default()) &&
92           mozilla::Preferences::GetBool("gfx.xrender.enabled"))
93       {
94           gfxVars::SetUseXRender(true);
95       }
96     }
97 #endif
98 
99     uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO);
100     uint32_t contentMask = BackendTypeBit(BackendType::CAIRO);
101 #ifdef USE_SKIA
102     canvasMask |= BackendTypeBit(BackendType::SKIA);
103     contentMask |= BackendTypeBit(BackendType::SKIA);
104 #endif
105     InitBackendPrefs(canvasMask, BackendType::CAIRO,
106                      contentMask, BackendType::CAIRO);
107 
108 #ifdef MOZ_X11
109     if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
110       mCompositorDisplay = XOpenDisplay(nullptr);
111       MOZ_ASSERT(mCompositorDisplay, "Failed to create compositor display!");
112     } else {
113       mCompositorDisplay = nullptr;
114     }
115 #endif // MOZ_X11
116 }
117 
~gfxPlatformGtk()118 gfxPlatformGtk::~gfxPlatformGtk()
119 {
120     if (!sUseFcFontList) {
121         gfxFontconfigUtils::Shutdown();
122         sFontconfigUtils = nullptr;
123         gfxPangoFontGroup::Shutdown();
124     }
125 
126 #ifdef MOZ_X11
127     if (mCompositorDisplay) {
128       XCloseDisplay(mCompositorDisplay);
129     }
130 #endif // MOZ_X11
131 }
132 
133 void
FlushContentDrawing()134 gfxPlatformGtk::FlushContentDrawing()
135 {
136     if (gfxVars::UseXRender()) {
137         XFlush(DefaultXDisplay());
138     }
139 }
140 
141 already_AddRefed<gfxASurface>
CreateOffscreenSurface(const IntSize & aSize,gfxImageFormat aFormat)142 gfxPlatformGtk::CreateOffscreenSurface(const IntSize& aSize,
143                                        gfxImageFormat aFormat)
144 {
145     if (!Factory::AllowedSurfaceSize(aSize)) {
146         return nullptr;
147     }
148 
149     RefPtr<gfxASurface> newSurface;
150     bool needsClear = true;
151 #ifdef MOZ_X11
152     // XXX we really need a different interface here, something that passes
153     // in more context, including the display and/or target surface type that
154     // we should try to match
155     GdkScreen *gdkScreen = gdk_screen_get_default();
156     if (gdkScreen) {
157         // When forcing PaintedLayers to use image surfaces for content,
158         // force creation of gfxImageSurface surfaces.
159         if (gfxVars::UseXRender() && !UseImageOffscreenSurfaces()) {
160             Screen *screen = gdk_x11_screen_get_xscreen(gdkScreen);
161             XRenderPictFormat* xrenderFormat =
162                 gfxXlibSurface::FindRenderFormat(DisplayOfScreen(screen),
163                                                  aFormat);
164 
165             if (xrenderFormat) {
166                 newSurface = gfxXlibSurface::Create(screen, xrenderFormat,
167                                                     aSize);
168             }
169         } else {
170             // We're not going to use XRender, so we don't need to
171             // search for a render format
172             newSurface = new gfxImageSurface(aSize, aFormat);
173             // The gfxImageSurface ctor zeroes this for us, no need to
174             // waste time clearing again
175             needsClear = false;
176         }
177     }
178 #endif
179 
180     if (!newSurface) {
181         // We couldn't create a native surface for whatever reason;
182         // e.g., no display, no RENDER, bad size, etc.
183         // Fall back to image surface for the data.
184         newSurface = new gfxImageSurface(aSize, aFormat);
185     }
186 
187     if (newSurface->CairoStatus()) {
188         newSurface = nullptr; // surface isn't valid for some reason
189     }
190 
191     if (newSurface && needsClear) {
192         gfxUtils::ClearThebesSurface(newSurface);
193     }
194 
195     return newSurface.forget();
196 }
197 
198 nsresult
GetFontList(nsIAtom * aLangGroup,const nsACString & aGenericFamily,nsTArray<nsString> & aListOfFonts)199 gfxPlatformGtk::GetFontList(nsIAtom *aLangGroup,
200                             const nsACString& aGenericFamily,
201                             nsTArray<nsString>& aListOfFonts)
202 {
203     if (sUseFcFontList) {
204         gfxPlatformFontList::PlatformFontList()->GetFontList(aLangGroup,
205                                                              aGenericFamily,
206                                                              aListOfFonts);
207         return NS_OK;
208     }
209 
210     return sFontconfigUtils->GetFontList(aLangGroup,
211                                          aGenericFamily,
212                                          aListOfFonts);
213 }
214 
215 nsresult
UpdateFontList()216 gfxPlatformGtk::UpdateFontList()
217 {
218     if (sUseFcFontList) {
219         gfxPlatformFontList::PlatformFontList()->UpdateFontList();
220         return NS_OK;
221     }
222 
223     return sFontconfigUtils->UpdateFontList();
224 }
225 
226 // xxx - this is ubuntu centric, need to go through other distros and flesh
227 // out a more general list
228 static const char kFontDejaVuSans[] = "DejaVu Sans";
229 static const char kFontDejaVuSerif[] = "DejaVu Serif";
230 static const char kFontEmojiOneMozilla[] = "EmojiOne Mozilla";
231 static const char kFontFreeSans[] = "FreeSans";
232 static const char kFontFreeSerif[] = "FreeSerif";
233 static const char kFontTakaoPGothic[] = "TakaoPGothic";
234 static const char kFontDroidSansFallback[] = "Droid Sans Fallback";
235 static const char kFontWenQuanYiMicroHei[] = "WenQuanYi Micro Hei";
236 static const char kFontNanumGothic[] = "NanumGothic";
237 
238 void
GetCommonFallbackFonts(uint32_t aCh,uint32_t aNextCh,Script aRunScript,nsTArray<const char * > & aFontList)239 gfxPlatformGtk::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh,
240                                        Script aRunScript,
241                                        nsTArray<const char*>& aFontList)
242 {
243     if (aNextCh == 0xfe0fu) {
244       // if char is followed by VS16, try for a color emoji glyph
245       aFontList.AppendElement(kFontEmojiOneMozilla);
246     }
247 
248     aFontList.AppendElement(kFontDejaVuSerif);
249     aFontList.AppendElement(kFontFreeSerif);
250     aFontList.AppendElement(kFontDejaVuSans);
251     aFontList.AppendElement(kFontFreeSans);
252 
253     if (!IS_IN_BMP(aCh)) {
254         uint32_t p = aCh >> 16;
255         if (p == 1) { // try color emoji font, unless VS15 (text style) present
256             if (aNextCh != 0xfe0fu && aNextCh != 0xfe0eu) {
257                 aFontList.AppendElement(kFontEmojiOneMozilla);
258             }
259         }
260     }
261 
262     // add fonts for CJK ranges
263     // xxx - this isn't really correct, should use the same CJK font ordering
264     // as the pref font code
265     if (aCh >= 0x3000 &&
266         ((aCh < 0xe000) ||
267          (aCh >= 0xf900 && aCh < 0xfff0) ||
268          ((aCh >> 16) == 2))) {
269         aFontList.AppendElement(kFontTakaoPGothic);
270         aFontList.AppendElement(kFontDroidSansFallback);
271         aFontList.AppendElement(kFontWenQuanYiMicroHei);
272         aFontList.AppendElement(kFontNanumGothic);
273     }
274 }
275 
276 gfxPlatformFontList*
CreatePlatformFontList()277 gfxPlatformGtk::CreatePlatformFontList()
278 {
279     gfxPlatformFontList* list = new gfxFcPlatformFontList();
280     if (NS_SUCCEEDED(list->InitFontList())) {
281         return list;
282     }
283     gfxPlatformFontList::Shutdown();
284     return nullptr;
285 }
286 
287 nsresult
GetStandardFamilyName(const nsAString & aFontName,nsAString & aFamilyName)288 gfxPlatformGtk::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
289 {
290     if (sUseFcFontList) {
291         gfxPlatformFontList::PlatformFontList()->
292             GetStandardFamilyName(aFontName, aFamilyName);
293         return NS_OK;
294     }
295 
296     return sFontconfigUtils->GetStandardFamilyName(aFontName, aFamilyName);
297 }
298 
299 gfxFontGroup *
CreateFontGroup(const FontFamilyList & aFontFamilyList,const gfxFontStyle * aStyle,gfxTextPerfMetrics * aTextPerf,gfxUserFontSet * aUserFontSet,gfxFloat aDevToCssSize)300 gfxPlatformGtk::CreateFontGroup(const FontFamilyList& aFontFamilyList,
301                                 const gfxFontStyle* aStyle,
302                                 gfxTextPerfMetrics* aTextPerf,
303                                 gfxUserFontSet* aUserFontSet,
304                                 gfxFloat aDevToCssSize)
305 {
306     if (sUseFcFontList) {
307         return new gfxFontGroup(aFontFamilyList, aStyle, aTextPerf,
308                                 aUserFontSet, aDevToCssSize);
309     }
310 
311     return new gfxPangoFontGroup(aFontFamilyList, aStyle,
312                                  aUserFontSet, aDevToCssSize);
313 }
314 
315 gfxFontEntry*
LookupLocalFont(const nsAString & aFontName,uint16_t aWeight,int16_t aStretch,uint8_t aStyle)316 gfxPlatformGtk::LookupLocalFont(const nsAString& aFontName,
317                                 uint16_t aWeight,
318                                 int16_t aStretch,
319                                 uint8_t aStyle)
320 {
321     if (sUseFcFontList) {
322         gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
323         return pfl->LookupLocalFont(aFontName, aWeight, aStretch,
324                                     aStyle);
325     }
326 
327     return gfxPangoFontGroup::NewFontEntry(aFontName, aWeight,
328                                            aStretch, aStyle);
329 }
330 
331 gfxFontEntry*
MakePlatformFont(const nsAString & aFontName,uint16_t aWeight,int16_t aStretch,uint8_t aStyle,const uint8_t * aFontData,uint32_t aLength)332 gfxPlatformGtk::MakePlatformFont(const nsAString& aFontName,
333                                  uint16_t aWeight,
334                                  int16_t aStretch,
335                                  uint8_t aStyle,
336                                  const uint8_t* aFontData,
337                                  uint32_t aLength)
338 {
339     if (sUseFcFontList) {
340         gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
341         return pfl->MakePlatformFont(aFontName, aWeight, aStretch,
342                                      aStyle, aFontData, aLength);
343     }
344 
345     // passing ownership of the font data to the new font entry
346     return gfxPangoFontGroup::NewFontEntry(aFontName, aWeight,
347                                            aStretch, aStyle,
348                                            aFontData, aLength);
349 }
350 
351 bool
IsFontFormatSupported(nsIURI * aFontURI,uint32_t aFormatFlags)352 gfxPlatformGtk::IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags)
353 {
354     // check for strange format flags
355     NS_ASSERTION(!(aFormatFlags & gfxUserFontSet::FLAG_FORMAT_NOT_USED),
356                  "strange font format hint set");
357 
358     // accept supported formats
359     // Pango doesn't apply features from AAT TrueType extensions.
360     // Assume that if this is the only SFNT format specified,
361     // then AAT extensions are required for complex script support.
362     if (aFormatFlags & gfxUserFontSet::FLAG_FORMATS_COMMON) {
363         return true;
364     }
365 
366     // reject all other formats, known and unknown
367     if (aFormatFlags != 0) {
368         return false;
369     }
370 
371     // no format hint set, need to look at data
372     return true;
373 }
374 
375 static int32_t sDPI = 0;
376 
377 int32_t
GetDPI()378 gfxPlatformGtk::GetDPI()
379 {
380     if (!sDPI) {
381         // Make sure init is run so we have a resolution
382         GdkScreen *screen = gdk_screen_get_default();
383         gtk_settings_get_for_screen(screen);
384         sDPI = int32_t(round(gdk_screen_get_resolution(screen)));
385         if (sDPI <= 0) {
386             // Fall back to something sane
387             sDPI = 96;
388         }
389     }
390     return sDPI;
391 }
392 
393 double
GetDPIScale()394 gfxPlatformGtk::GetDPIScale()
395 {
396     // Integer scale factors work well with GTK window scaling, image scaling,
397     // and pixel alignment, but there is a range where 1 is too small and 2 is
398     // too big.  An additional step of 1.5 is added because this is common
399     // scale on WINNT and at this ratio the advantages of larger rendering
400     // outweigh the disadvantages from scaling and pixel mis-alignment.
401     int32_t dpi = GetDPI();
402     if (dpi < 144) {
403         return 1.0;
404     } else if (dpi < 168) {
405         return 1.5;
406     } else {
407         return round(dpi/96.0);
408     }
409 }
410 
411 bool
UseImageOffscreenSurfaces()412 gfxPlatformGtk::UseImageOffscreenSurfaces()
413 {
414     return GetDefaultContentBackend() != mozilla::gfx::BackendType::CAIRO ||
415            gfxPrefs::UseImageOffscreenSurfaces();
416 }
417 
418 gfxImageFormat
GetOffscreenFormat()419 gfxPlatformGtk::GetOffscreenFormat()
420 {
421     // Make sure there is a screen
422     GdkScreen *screen = gdk_screen_get_default();
423     if (screen && gdk_visual_get_depth(gdk_visual_get_system()) == 16) {
424         return SurfaceFormat::R5G6B5_UINT16;
425     }
426 
427     return SurfaceFormat::X8R8G8B8_UINT32;
428 }
429 
FontsPrefsChanged(const char * aPref)430 void gfxPlatformGtk::FontsPrefsChanged(const char *aPref)
431 {
432     // only checking for generic substitions, pass other changes up
433     if (strcmp(GFX_PREF_MAX_GENERIC_SUBSTITUTIONS, aPref)) {
434         gfxPlatform::FontsPrefsChanged(aPref);
435         return;
436     }
437 
438     mMaxGenericSubstitutions = UNINITIALIZED_VALUE;
439     if (sUseFcFontList) {
440         gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList();
441         pfl->ClearGenericMappings();
442         FlushFontAndWordCaches();
443     }
444 }
445 
MaxGenericSubstitions()446 uint32_t gfxPlatformGtk::MaxGenericSubstitions()
447 {
448     if (mMaxGenericSubstitutions == UNINITIALIZED_VALUE) {
449         mMaxGenericSubstitutions =
450             Preferences::GetInt(GFX_PREF_MAX_GENERIC_SUBSTITUTIONS, 3);
451         if (mMaxGenericSubstitutions < 0) {
452             mMaxGenericSubstitutions = 3;
453         }
454     }
455 
456     return uint32_t(mMaxGenericSubstitutions);
457 }
458 
459 void
GetPlatformCMSOutputProfile(void * & mem,size_t & size)460 gfxPlatformGtk::GetPlatformCMSOutputProfile(void *&mem, size_t &size)
461 {
462     mem = nullptr;
463     size = 0;
464 
465 #ifdef MOZ_X11
466     GdkDisplay *display = gdk_display_get_default();
467     if (!GDK_IS_X11_DISPLAY(display))
468         return;
469 
470     const char EDID1_ATOM_NAME[] = "XFree86_DDC_EDID1_RAWDATA";
471     const char ICC_PROFILE_ATOM_NAME[] = "_ICC_PROFILE";
472 
473     Atom edidAtom, iccAtom;
474     Display *dpy = GDK_DISPLAY_XDISPLAY(display);
475     // In xpcshell tests, we never initialize X and hence don't have a Display.
476     // In this case, there's no output colour management to be done, so we just
477     // return with nullptr.
478     if (!dpy)
479         return;
480 
481     Window root = gdk_x11_get_default_root_xwindow();
482 
483     Atom retAtom;
484     int retFormat;
485     unsigned long retLength, retAfter;
486     unsigned char *retProperty ;
487 
488     iccAtom = XInternAtom(dpy, ICC_PROFILE_ATOM_NAME, TRUE);
489     if (iccAtom) {
490         // read once to get size, once for the data
491         if (Success == XGetWindowProperty(dpy, root, iccAtom,
492                                           0, INT_MAX /* length */,
493                                           False, AnyPropertyType,
494                                           &retAtom, &retFormat, &retLength,
495                                           &retAfter, &retProperty)) {
496 
497             if (retLength > 0) {
498                 void *buffer = malloc(retLength);
499                 if (buffer) {
500                     memcpy(buffer, retProperty, retLength);
501                     mem = buffer;
502                     size = retLength;
503                 }
504             }
505 
506             XFree(retProperty);
507             if (size > 0) {
508 #ifdef DEBUG_tor
509                 fprintf(stderr,
510                         "ICM profile read from %s successfully\n",
511                         ICC_PROFILE_ATOM_NAME);
512 #endif
513                 return;
514             }
515         }
516     }
517 
518     edidAtom = XInternAtom(dpy, EDID1_ATOM_NAME, TRUE);
519     if (edidAtom) {
520         if (Success == XGetWindowProperty(dpy, root, edidAtom, 0, 32,
521                                           False, AnyPropertyType,
522                                           &retAtom, &retFormat, &retLength,
523                                           &retAfter, &retProperty)) {
524             double gamma;
525             qcms_CIE_xyY whitePoint;
526             qcms_CIE_xyYTRIPLE primaries;
527 
528             if (retLength != 128) {
529 #ifdef DEBUG_tor
530                 fprintf(stderr, "Short EDID data\n");
531 #endif
532                 return;
533             }
534 
535             // Format documented in "VESA E-EDID Implementation Guide"
536 
537             gamma = (100 + retProperty[0x17]) / 100.0;
538             whitePoint.x = ((retProperty[0x21] << 2) |
539                             (retProperty[0x1a] >> 2 & 3)) / 1024.0;
540             whitePoint.y = ((retProperty[0x22] << 2) |
541                             (retProperty[0x1a] >> 0 & 3)) / 1024.0;
542             whitePoint.Y = 1.0;
543 
544             primaries.red.x = ((retProperty[0x1b] << 2) |
545                                (retProperty[0x19] >> 6 & 3)) / 1024.0;
546             primaries.red.y = ((retProperty[0x1c] << 2) |
547                                (retProperty[0x19] >> 4 & 3)) / 1024.0;
548             primaries.red.Y = 1.0;
549 
550             primaries.green.x = ((retProperty[0x1d] << 2) |
551                                  (retProperty[0x19] >> 2 & 3)) / 1024.0;
552             primaries.green.y = ((retProperty[0x1e] << 2) |
553                                  (retProperty[0x19] >> 0 & 3)) / 1024.0;
554             primaries.green.Y = 1.0;
555 
556             primaries.blue.x = ((retProperty[0x1f] << 2) |
557                                (retProperty[0x1a] >> 6 & 3)) / 1024.0;
558             primaries.blue.y = ((retProperty[0x20] << 2) |
559                                (retProperty[0x1a] >> 4 & 3)) / 1024.0;
560             primaries.blue.Y = 1.0;
561 
562             XFree(retProperty);
563 
564 #ifdef DEBUG_tor
565             fprintf(stderr, "EDID gamma: %f\n", gamma);
566             fprintf(stderr, "EDID whitepoint: %f %f %f\n",
567                     whitePoint.x, whitePoint.y, whitePoint.Y);
568             fprintf(stderr, "EDID primaries: [%f %f %f] [%f %f %f] [%f %f %f]\n",
569                     primaries.Red.x, primaries.Red.y, primaries.Red.Y,
570                     primaries.Green.x, primaries.Green.y, primaries.Green.Y,
571                     primaries.Blue.x, primaries.Blue.y, primaries.Blue.Y);
572 #endif
573 
574             qcms_data_create_rgb_with_gamma(whitePoint, primaries, gamma, &mem, &size);
575 
576 #ifdef DEBUG_tor
577             if (size > 0) {
578                 fprintf(stderr,
579                         "ICM profile read from %s successfully\n",
580                         EDID1_ATOM_NAME);
581             }
582 #endif
583         }
584     }
585 #endif
586 }
587 
588 
589 #if (MOZ_WIDGET_GTK == 2)
590 void
SetGdkDrawable(cairo_surface_t * target,GdkDrawable * drawable)591 gfxPlatformGtk::SetGdkDrawable(cairo_surface_t *target,
592                                GdkDrawable *drawable)
593 {
594     if (cairo_surface_status(target))
595         return;
596 
597     g_object_ref(drawable);
598 
599     cairo_surface_set_user_data (target,
600                                  &cairo_gdk_drawable_key,
601                                  drawable,
602                                  g_object_unref);
603 }
604 
605 GdkDrawable *
GetGdkDrawable(cairo_surface_t * target)606 gfxPlatformGtk::GetGdkDrawable(cairo_surface_t *target)
607 {
608     if (cairo_surface_status(target))
609         return nullptr;
610 
611     GdkDrawable *result;
612 
613     result = (GdkDrawable*) cairo_surface_get_user_data (target,
614                                                          &cairo_gdk_drawable_key);
615     if (result)
616         return result;
617 
618 #ifdef MOZ_X11
619     if (cairo_surface_get_type(target) != CAIRO_SURFACE_TYPE_XLIB)
620         return nullptr;
621 
622     // try looking it up in gdk's table
623     result = (GdkDrawable*) gdk_xid_table_lookup(cairo_xlib_surface_get_drawable(target));
624     if (result) {
625         SetGdkDrawable(target, result);
626         return result;
627     }
628 #endif
629 
630     return nullptr;
631 }
632 #endif
633 
634 already_AddRefed<ScaledFont>
GetScaledFontForFont(DrawTarget * aTarget,gfxFont * aFont)635 gfxPlatformGtk::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont)
636 {
637     switch (aTarget->GetBackendType()) {
638     case BackendType::CAIRO:
639     case BackendType::SKIA:
640         if (aFont->GetType() == gfxFont::FONT_TYPE_FONTCONFIG) {
641             gfxFontconfigFontBase* fcFont = static_cast<gfxFontconfigFontBase*>(aFont);
642             return Factory::CreateScaledFontForFontconfigFont(
643                     fcFont->GetCairoScaledFont(),
644                     fcFont->GetPattern(),
645                     fcFont->GetAdjustedSize());
646         }
647         MOZ_FALLTHROUGH;
648     default:
649         return GetScaledFontForFontWithCairoSkia(aTarget, aFont);
650     }
651 }
652 
653 #ifdef GL_PROVIDER_GLX
654 
655 class GLXVsyncSource final : public VsyncSource
656 {
657 public:
GLXVsyncSource()658   GLXVsyncSource()
659   {
660     MOZ_ASSERT(NS_IsMainThread());
661     mGlobalDisplay = new GLXDisplay();
662   }
663 
~GLXVsyncSource()664   virtual ~GLXVsyncSource()
665   {
666     MOZ_ASSERT(NS_IsMainThread());
667   }
668 
GetGlobalDisplay()669   virtual Display& GetGlobalDisplay() override
670   {
671     return *mGlobalDisplay;
672   }
673 
674   class GLXDisplay final : public VsyncSource::Display
675   {
676   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GLXDisplay)
677 
678   public:
GLXDisplay()679     GLXDisplay() : mGLContext(nullptr)
680                  , mXDisplay(nullptr)
681                  , mSetupLock("GLXVsyncSetupLock")
682                  , mVsyncThread("GLXVsyncThread")
683                  , mVsyncTask(nullptr)
684                  , mVsyncEnabledLock("GLXVsyncEnabledLock")
685                  , mVsyncEnabled(false)
686     {
687     }
688 
689     // Sets up the display's GL context on a worker thread.
690     // Required as GLContexts may only be used by the creating thread.
691     // Returns true if setup was a success.
Setup()692     bool Setup()
693     {
694       MonitorAutoLock lock(mSetupLock);
695       MOZ_ASSERT(NS_IsMainThread());
696       if (!mVsyncThread.Start())
697         return false;
698 
699       RefPtr<Runnable> vsyncSetup = NewRunnableMethod(this, &GLXDisplay::SetupGLContext);
700       mVsyncThread.message_loop()->PostTask(vsyncSetup.forget());
701       // Wait until the setup has completed.
702       lock.Wait();
703       return mGLContext != nullptr;
704     }
705 
706     // Called on the Vsync thread to setup the GL context.
SetupGLContext()707     void SetupGLContext()
708     {
709         MonitorAutoLock lock(mSetupLock);
710         MOZ_ASSERT(!NS_IsMainThread());
711         MOZ_ASSERT(!mGLContext, "GLContext already setup!");
712 
713         // Create video sync timer on a separate Display to prevent locking the
714         // main thread X display.
715         mXDisplay = XOpenDisplay(nullptr);
716         if (!mXDisplay) {
717           lock.NotifyAll();
718           return;
719         }
720 
721         // Most compositors wait for vsync events on the root window.
722         Window root = DefaultRootWindow(mXDisplay);
723         int screen = DefaultScreen(mXDisplay);
724 
725         ScopedXFree<GLXFBConfig> cfgs;
726         GLXFBConfig config;
727         int visid;
728         if (!gl::GLContextGLX::FindFBConfigForWindow(mXDisplay, screen, root,
729                                                      &cfgs, &config, &visid)) {
730           lock.NotifyAll();
731           return;
732         }
733 
734         mGLContext = gl::GLContextGLX::CreateGLContext(
735             gl::CreateContextFlags::NONE,
736             gl::SurfaceCaps::Any(),
737             nullptr,
738             false,
739             mXDisplay,
740             root,
741             config,
742             false);
743 
744         if (!mGLContext) {
745           lock.NotifyAll();
746           return;
747         }
748 
749         mGLContext->MakeCurrent();
750 
751         // Test that SGI_video_sync lets us get the counter.
752         unsigned int syncCounter = 0;
753         if (gl::sGLXLibrary.xGetVideoSync(&syncCounter) != 0) {
754           mGLContext = nullptr;
755         }
756 
757         lock.NotifyAll();
758     }
759 
EnableVsync()760     virtual void EnableVsync() override
761     {
762       MOZ_ASSERT(NS_IsMainThread());
763       MOZ_ASSERT(mGLContext, "GLContext not setup!");
764 
765       MonitorAutoLock lock(mVsyncEnabledLock);
766       if (mVsyncEnabled) {
767         return;
768       }
769       mVsyncEnabled = true;
770 
771       // If the task has not nulled itself out, it hasn't yet realized
772       // that vsync was disabled earlier, so continue its execution.
773       if (!mVsyncTask) {
774         mVsyncTask = NewRunnableMethod(this, &GLXDisplay::RunVsync);
775         RefPtr<Runnable> addrefedTask = mVsyncTask;
776         mVsyncThread.message_loop()->PostTask(addrefedTask.forget());
777       }
778     }
779 
DisableVsync()780     virtual void DisableVsync() override
781     {
782       MonitorAutoLock lock(mVsyncEnabledLock);
783       mVsyncEnabled = false;
784     }
785 
IsVsyncEnabled()786     virtual bool IsVsyncEnabled() override
787     {
788       MonitorAutoLock lock(mVsyncEnabledLock);
789       return mVsyncEnabled;
790     }
791 
Shutdown()792     virtual void Shutdown() override
793     {
794       MOZ_ASSERT(NS_IsMainThread());
795       DisableVsync();
796 
797       // Cleanup thread-specific resources before shutting down.
798       RefPtr<Runnable> shutdownTask = NewRunnableMethod(this, &GLXDisplay::Cleanup);
799       mVsyncThread.message_loop()->PostTask(shutdownTask.forget());
800 
801       // Stop, waiting for the cleanup task to finish execution.
802       mVsyncThread.Stop();
803     }
804 
805   private:
~GLXDisplay()806     virtual ~GLXDisplay()
807     {
808     }
809 
RunVsync()810     void RunVsync()
811     {
812       MOZ_ASSERT(!NS_IsMainThread());
813 
814       mGLContext->MakeCurrent();
815 
816       unsigned int syncCounter = 0;
817       gl::sGLXLibrary.xGetVideoSync(&syncCounter);
818       for (;;) {
819         {
820           MonitorAutoLock lock(mVsyncEnabledLock);
821           if (!mVsyncEnabled) {
822             mVsyncTask = nullptr;
823             return;
824           }
825         }
826 
827         TimeStamp lastVsync = TimeStamp::Now();
828         bool useSoftware = false;
829 
830         // Wait until the video sync counter reaches the next value by waiting
831         // until the parity of the counter value changes.
832         unsigned int nextSync = syncCounter + 1;
833         int status;
834         if ((status = gl::sGLXLibrary.xWaitVideoSync(2, nextSync % 2, &syncCounter)) != 0) {
835           gfxWarningOnce() << "glXWaitVideoSync returned " << status;
836           useSoftware = true;
837         }
838 
839         if (syncCounter == (nextSync - 1)) {
840           gfxWarningOnce() << "glXWaitVideoSync failed to increment the sync counter.";
841           useSoftware = true;
842         }
843 
844         if (useSoftware) {
845           double remaining = (1000.f / 60.f) -
846             (TimeStamp::Now() - lastVsync).ToMilliseconds();
847           if (remaining > 0) {
848             PlatformThread::Sleep(remaining);
849           }
850         }
851 
852         lastVsync = TimeStamp::Now();
853         NotifyVsync(lastVsync);
854       }
855     }
856 
Cleanup()857     void Cleanup() {
858       MOZ_ASSERT(!NS_IsMainThread());
859 
860       mGLContext = nullptr;
861       XCloseDisplay(mXDisplay);
862     }
863 
864     // Owned by the vsync thread.
865     RefPtr<gl::GLContextGLX> mGLContext;
866     _XDisplay* mXDisplay;
867     Monitor mSetupLock;
868     base::Thread mVsyncThread;
869     RefPtr<Runnable> mVsyncTask;
870     Monitor mVsyncEnabledLock;
871     bool mVsyncEnabled;
872   };
873 private:
874   // We need a refcounted VsyncSource::Display to use chromium IPC runnables.
875   RefPtr<GLXDisplay> mGlobalDisplay;
876 };
877 
878 already_AddRefed<gfx::VsyncSource>
CreateHardwareVsyncSource()879 gfxPlatformGtk::CreateHardwareVsyncSource()
880 {
881   // Only use GLX vsync when the OpenGL compositor is being used.
882   // The extra cost of initializing a GLX context while blocking the main
883   // thread is not worth it when using basic composition.
884   if (gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
885     if (gl::sGLXLibrary.SupportsVideoSync()) {
886       RefPtr<VsyncSource> vsyncSource = new GLXVsyncSource();
887       VsyncSource::Display& display = vsyncSource->GetGlobalDisplay();
888       if (!static_cast<GLXVsyncSource::GLXDisplay&>(display).Setup()) {
889         NS_WARNING("Failed to setup GLContext, falling back to software vsync.");
890         return gfxPlatform::CreateHardwareVsyncSource();
891       }
892       return vsyncSource.forget();
893     }
894     NS_WARNING("SGI_video_sync unsupported. Falling back to software vsync.");
895   }
896   return gfxPlatform::CreateHardwareVsyncSource();
897 }
898 
899 bool
SupportsApzTouchInput() const900 gfxPlatformGtk::SupportsApzTouchInput() const
901 {
902   int value = gfxPrefs::TouchEventsEnabled();
903   return value == 1 || value == 2;
904 }
905 
906 #endif
907