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