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