1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
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/. */
6 #include "gfxXlibSurface.h"
9 #include "cairo-xlib.h"
10 #include <X11/Xlibint.h> /* For XESetCloseDisplay */
11 #undef max // Xlibint.h defines this and it breaks std::max
12 #undef min // Xlibint.h defines this and it breaks std::min
16 #include "nsAlgorithm.h"
17 #include "mozilla/gfx/2D.h"
18 #include "mozilla/Preferences.h"
20 #include "mozilla/CheckedInt.h"
22 using namespace mozilla
;
23 using namespace mozilla::gfx
;
25 gfxXlibSurface::gfxXlibSurface(Display
* dpy
, Drawable drawable
, Visual
* visual
)
26 : mPixmapTaken(false),
27 mDisplay(XlibDisplay::Borrow(dpy
)),
29 const gfx::IntSize size
= DoSizeQuery();
30 cairo_surface_t
* surf
=
31 cairo_xlib_surface_create(dpy
, drawable
, visual
, size
.width
, size
.height
);
35 gfxXlibSurface::gfxXlibSurface(Display
* dpy
, Drawable drawable
, Visual
* visual
,
36 const gfx::IntSize
& size
)
37 : gfxXlibSurface(XlibDisplay::Borrow(dpy
), drawable
, visual
, size
) {}
39 gfxXlibSurface::gfxXlibSurface(const std::shared_ptr
<XlibDisplay
>& dpy
,
40 Drawable drawable
, Visual
* visual
,
41 const gfx::IntSize
& size
)
42 : mPixmapTaken(false), mDisplay(dpy
), mDrawable(drawable
) {
43 NS_ASSERTION(Factory::CheckSurfaceSize(size
, XLIB_IMAGE_SIDE_SIZE_LIMIT
),
46 cairo_surface_t
* surf
= cairo_xlib_surface_create(*dpy
, drawable
, visual
,
47 size
.width
, size
.height
);
51 gfxXlibSurface::gfxXlibSurface(cairo_surface_t
* csurf
) : mPixmapTaken(false) {
52 MOZ_ASSERT(cairo_surface_status(csurf
) == 0,
53 "Not expecting an error surface");
55 mDrawable
= cairo_xlib_surface_get_drawable(csurf
);
56 mDisplay
= XlibDisplay::Borrow(cairo_xlib_surface_get_display(csurf
));
61 gfxXlibSurface::~gfxXlibSurface() {
62 // gfxASurface's destructor calls RecordMemoryFreed().
64 XFreePixmap(*mDisplay
, mDrawable
);
68 static Drawable
CreatePixmap(Screen
* screen
, const gfx::IntSize
& size
,
69 unsigned int depth
, Drawable relatedDrawable
) {
70 if (!Factory::CheckSurfaceSize(size
, XLIB_IMAGE_SIDE_SIZE_LIMIT
))
73 if (relatedDrawable
== X11None
) {
74 relatedDrawable
= RootWindowOfScreen(screen
);
76 Display
* dpy
= DisplayOfScreen(screen
);
77 // X gives us a fatal error if we try to create a pixmap of width
79 return XCreatePixmap(dpy
, relatedDrawable
, std::max(1, size
.width
),
80 std::max(1, size
.height
), depth
);
83 void gfxXlibSurface::TakePixmap() {
84 NS_ASSERTION(!mPixmapTaken
, "I already own the Pixmap!");
87 // The bit depth returned from Cairo is technically int, but this is
88 // the last place we'd be worried about that scenario.
89 unsigned int bitDepth
= cairo_xlib_surface_get_depth(CairoSurface());
90 MOZ_ASSERT((bitDepth
% 8) == 0, "Memory used not recorded correctly");
92 // Divide by 8 because surface_get_depth gives us the number of *bits* per
94 gfx::IntSize size
= GetSize();
95 CheckedInt32 totalBytes
=
96 CheckedInt32(size
.width
) * CheckedInt32(size
.height
) * (bitDepth
/ 8);
98 // Don't do anything in the "else" case. We could add INT32_MAX, but that
99 // would overflow the memory used counter. It would also mean we tried for
100 // a 2G image. For now, we'll just assert,
101 MOZ_ASSERT(totalBytes
.isValid(), "Did not expect to exceed 2Gb image");
102 if (totalBytes
.isValid()) {
103 RecordMemoryUsed(totalBytes
.value());
107 Drawable
gfxXlibSurface::ReleasePixmap() {
108 NS_ASSERTION(mPixmapTaken
, "I don't own the Pixmap!");
109 mPixmapTaken
= false;
114 static cairo_user_data_key_t gDestroyPixmapKey
;
116 struct DestroyPixmapClosure
{
117 DestroyPixmapClosure(Drawable d
, Screen
* s
) : mPixmap(d
), mScreen(s
) {}
122 static void DestroyPixmap(void* data
) {
123 DestroyPixmapClosure
* closure
= static_cast<DestroyPixmapClosure
*>(data
);
124 XFreePixmap(DisplayOfScreen(closure
->mScreen
), closure
->mPixmap
);
129 cairo_surface_t
* gfxXlibSurface::CreateCairoSurface(Screen
* screen
,
131 const gfx::IntSize
& size
,
132 Drawable relatedDrawable
) {
133 Drawable drawable
= CreatePixmap(screen
, size
, DepthOfVisual(screen
, visual
),
135 if (!drawable
) return nullptr;
137 cairo_surface_t
* surface
= cairo_xlib_surface_create(
138 DisplayOfScreen(screen
), drawable
, visual
, size
.width
, size
.height
);
139 if (cairo_surface_status(surface
)) {
140 cairo_surface_destroy(surface
);
141 XFreePixmap(DisplayOfScreen(screen
), drawable
);
145 DestroyPixmapClosure
* closure
= new DestroyPixmapClosure(drawable
, screen
);
146 cairo_surface_set_user_data(surface
, &gDestroyPixmapKey
, closure
,
152 already_AddRefed
<gfxXlibSurface
> gfxXlibSurface::Create(
153 Screen
* screen
, Visual
* visual
, const gfx::IntSize
& size
,
154 Drawable relatedDrawable
) {
155 return Create(XlibDisplay::Borrow(DisplayOfScreen(screen
)), screen
, visual
,
156 size
, relatedDrawable
);
160 already_AddRefed
<gfxXlibSurface
> gfxXlibSurface::Create(
161 const std::shared_ptr
<XlibDisplay
>& display
, Screen
* screen
, Visual
* visual
,
162 const gfx::IntSize
& size
, Drawable relatedDrawable
) {
163 MOZ_ASSERT(*display
== DisplayOfScreen(screen
));
165 Drawable drawable
= CreatePixmap(screen
, size
, DepthOfVisual(screen
, visual
),
167 if (!drawable
) return nullptr;
169 RefPtr
<gfxXlibSurface
> result
=
170 new gfxXlibSurface(display
, drawable
, visual
, size
);
171 result
->TakePixmap();
173 if (result
->CairoStatus() != 0) return nullptr;
175 return result
.forget();
178 void gfxXlibSurface::Finish() { gfxASurface::Finish(); }
180 const gfx::IntSize
gfxXlibSurface::GetSize() const {
181 if (!mSurfaceValid
) return gfx::IntSize(0, 0);
183 return gfx::IntSize(cairo_xlib_surface_get_width(mSurface
),
184 cairo_xlib_surface_get_height(mSurface
));
187 const gfx::IntSize
gfxXlibSurface::DoSizeQuery() {
188 // figure out width/height/depth
190 int x_ignore
, y_ignore
;
191 unsigned int bwidth_ignore
, width
, height
, depth
;
193 XGetGeometry(*mDisplay
, mDrawable
, &root_ignore
, &x_ignore
, &y_ignore
, &width
,
194 &height
, &bwidth_ignore
, &depth
);
196 return gfx::IntSize(width
, height
);
201 static bool GetColormapAndVisual(Screen
* screen
, Visual
* visual
,
203 Visual
** visualForColormap
);
206 struct ColormapEntry
{
207 // The Screen is needed here because colormaps (and their visuals) may
208 // only be used on one Screen
216 explicit DisplayInfo(Display
* display
) : mDisplay(display
) {}
218 nsTArray
<ColormapEntry
> mColormapEntries
;
221 // Comparator for finding the DisplayInfo
224 bool Equals(const DisplayInfo
& info
, const Display
* display
) const {
225 return info
.mDisplay
== display
;
229 static int DisplayClosing(Display
* display
, XExtCodes
* codes
);
231 nsTArray
<DisplayInfo
> mDisplays
;
232 static DisplayTable
* sDisplayTable
;
235 DisplayTable
* DisplayTable::sDisplayTable
;
237 // Pixmaps don't have a particular associated visual but the pixel values are
238 // interpreted according to a visual/colormap pairs.
240 // cairo is designed for surfaces with either TrueColor visuals or the
241 // default visual (which may not be true color). TrueColor visuals don't
242 // really need a colormap because the visual indicates the pixel format,
243 // and cairo uses the default visual with the default colormap, so cairo
244 // surfaces don't need an explicit colormap.
246 // However, some toolkits (e.g. GDK) need a colormap even with TrueColor
247 // visuals. We can create a colormap for these visuals, but it will use about
248 // 20kB of memory in the server, so we use the default colormap when
249 // suitable and share colormaps between surfaces. Another reason for
250 // minimizing colormap turnover is that the plugin process must leak resources
251 // for each new colormap id when using older GDK libraries (bug 569775).
253 // Only the format of the pixels is important for rendering to Pixmaps, so if
254 // the format of a visual matches that of the surface, then that visual can be
255 // used for rendering to the surface. Multiple visuals can match the same
256 // format (but have different GLX properties), so the visual returned may
257 // differ from the visual passed in. Colormaps are tied to a visual, so
258 // should only be used with their visual.
261 bool DisplayTable::GetColormapAndVisual(Screen
* aScreen
, Visual
* aVisual
,
263 Visual
** aVisualForColormap
)
266 Display
* display
= DisplayOfScreen(aScreen
);
268 // Use the default colormap if the default visual matches.
269 Visual
* defaultVisual
= DefaultVisualOfScreen(aScreen
);
270 if (aVisual
== defaultVisual
) {
271 *aColormap
= DefaultColormapOfScreen(aScreen
);
272 *aVisualForColormap
= defaultVisual
;
276 // Only supporting TrueColor non-default visuals
277 if (!aVisual
|| aVisual
->c_class
!= TrueColor
) return false;
279 if (!sDisplayTable
) {
280 sDisplayTable
= new DisplayTable();
283 nsTArray
<DisplayInfo
>* displays
= &sDisplayTable
->mDisplays
;
284 size_t d
= displays
->IndexOf(display
, 0, FindDisplay());
286 if (d
== displays
->NoIndex
) {
287 d
= displays
->Length();
288 // Register for notification of display closing, when this info
290 XExtCodes
* codes
= XAddExtension(display
);
291 if (!codes
) return false;
293 XESetCloseDisplay(display
, codes
->extension
, DisplayClosing
);
294 // Add a new DisplayInfo.
295 displays
->AppendElement(display
);
298 nsTArray
<ColormapEntry
>* entries
= &displays
->ElementAt(d
).mColormapEntries
;
300 // Only a small number of formats are expected to be used, so just do a
301 // simple linear search.
302 for (uint32_t i
= 0; i
< entries
->Length(); ++i
) {
303 const ColormapEntry
& entry
= entries
->ElementAt(i
);
304 if (aVisual
== entry
.mVisual
) {
305 *aColormap
= entry
.mColormap
;
306 *aVisualForColormap
= entry
.mVisual
;
311 // No existing entry. Create a colormap and add an entry.
313 XCreateColormap(display
, RootWindowOfScreen(aScreen
), aVisual
, AllocNone
);
314 ColormapEntry
* newEntry
= entries
->AppendElement();
315 newEntry
->mScreen
= aScreen
;
316 newEntry
->mVisual
= aVisual
;
317 newEntry
->mColormap
= colormap
;
319 *aColormap
= colormap
;
320 *aVisualForColormap
= aVisual
;
325 int DisplayTable::DisplayClosing(Display
* display
, XExtCodes
* codes
) {
326 // No need to free the colormaps explicitly as they will be released when
327 // the connection is closed.
328 sDisplayTable
->mDisplays
.RemoveElement(display
, FindDisplay());
329 if (sDisplayTable
->mDisplays
.Length() == 0) {
330 delete sDisplayTable
;
331 sDisplayTable
= nullptr;
337 bool gfxXlibSurface::GetColormapAndVisual(cairo_surface_t
* aXlibSurface
,
340 Screen
* screen
= cairo_xlib_surface_get_screen(aXlibSurface
);
341 Visual
* visual
= cairo_xlib_surface_get_visual(aXlibSurface
);
343 return DisplayTable::GetColormapAndVisual(screen
, visual
, aColormap
, aVisual
);
346 bool gfxXlibSurface::GetColormapAndVisual(Colormap
* aColormap
,
348 if (!mSurfaceValid
) return false;
350 return GetColormapAndVisual(CairoSurface(), aColormap
, aVisual
);
354 int gfxXlibSurface::DepthOfVisual(const Screen
* screen
, const Visual
* visual
) {
355 for (int d
= 0; d
< screen
->ndepths
; d
++) {
356 const Depth
& d_info
= screen
->depths
[d
];
357 if (visual
>= &d_info
.visuals
[0] &&
358 visual
< &d_info
.visuals
[d_info
.nvisuals
])
362 NS_ERROR("Visual not on Screen.");
367 Visual
* gfxXlibSurface::FindVisual(Screen
* screen
, gfxImageFormat format
) {
369 unsigned long red_mask
, green_mask
, blue_mask
;
371 case gfx::SurfaceFormat::A8R8G8B8_UINT32
:
377 case gfx::SurfaceFormat::X8R8G8B8_UINT32
:
383 case gfx::SurfaceFormat::R5G6B5_UINT16
:
389 case gfx::SurfaceFormat::A8
:
394 for (int d
= 0; d
< screen
->ndepths
; d
++) {
395 const Depth
& d_info
= screen
->depths
[d
];
396 if (d_info
.depth
!= depth
) continue;
398 for (int v
= 0; v
< d_info
.nvisuals
; v
++) {
399 Visual
* visual
= &d_info
.visuals
[v
];
401 if (visual
->c_class
== TrueColor
&& visual
->red_mask
== red_mask
&&
402 visual
->green_mask
== green_mask
&& visual
->blue_mask
== blue_mask
)
410 Screen
* gfxXlibSurface::XScreen() {
411 return cairo_xlib_surface_get_screen(CairoSurface());