mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 02:09:05 +02:00 
			
		
		
		
	The few cases where we still use Xlib most likely benefit little from Xrender. Lets drop the support for it. Differential Revision: https://phabricator.services.mozilla.com/D126970
		
			
				
	
	
		
			412 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			412 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 | 
						|
 * This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
#include "gfxXlibSurface.h"
 | 
						|
 | 
						|
#include "cairo.h"
 | 
						|
#include "cairo-xlib.h"
 | 
						|
#include <X11/Xlibint.h> /* For XESetCloseDisplay */
 | 
						|
#undef max               // Xlibint.h defines this and it breaks std::max
 | 
						|
#undef min               // Xlibint.h defines this and it breaks std::min
 | 
						|
#undef Data
 | 
						|
 | 
						|
#include "nsTArray.h"
 | 
						|
#include "nsAlgorithm.h"
 | 
						|
#include "mozilla/gfx/2D.h"
 | 
						|
#include "mozilla/Preferences.h"
 | 
						|
#include <algorithm>
 | 
						|
#include "mozilla/CheckedInt.h"
 | 
						|
 | 
						|
using namespace mozilla;
 | 
						|
using namespace mozilla::gfx;
 | 
						|
 | 
						|
gfxXlibSurface::gfxXlibSurface(Display* dpy, Drawable drawable, Visual* visual)
 | 
						|
    : mPixmapTaken(false),
 | 
						|
      mDisplay(XlibDisplay::Borrow(dpy)),
 | 
						|
      mDrawable(drawable) {
 | 
						|
  const gfx::IntSize size = DoSizeQuery();
 | 
						|
  cairo_surface_t* surf =
 | 
						|
      cairo_xlib_surface_create(dpy, drawable, visual, size.width, size.height);
 | 
						|
  Init(surf);
 | 
						|
}
 | 
						|
 | 
						|
gfxXlibSurface::gfxXlibSurface(Display* dpy, Drawable drawable, Visual* visual,
 | 
						|
                               const gfx::IntSize& size)
 | 
						|
    : gfxXlibSurface(XlibDisplay::Borrow(dpy), drawable, visual, size) {}
 | 
						|
 | 
						|
gfxXlibSurface::gfxXlibSurface(const std::shared_ptr<XlibDisplay>& dpy,
 | 
						|
                               Drawable drawable, Visual* visual,
 | 
						|
                               const gfx::IntSize& size)
 | 
						|
    : mPixmapTaken(false), mDisplay(dpy), mDrawable(drawable) {
 | 
						|
  NS_ASSERTION(Factory::CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT),
 | 
						|
               "Bad size");
 | 
						|
 | 
						|
  cairo_surface_t* surf = cairo_xlib_surface_create(*dpy, drawable, visual,
 | 
						|
                                                    size.width, size.height);
 | 
						|
  Init(surf);
 | 
						|
}
 | 
						|
 | 
						|
gfxXlibSurface::gfxXlibSurface(cairo_surface_t* csurf) : mPixmapTaken(false) {
 | 
						|
  MOZ_ASSERT(cairo_surface_status(csurf) == 0,
 | 
						|
             "Not expecting an error surface");
 | 
						|
 | 
						|
  mDrawable = cairo_xlib_surface_get_drawable(csurf);
 | 
						|
  mDisplay = XlibDisplay::Borrow(cairo_xlib_surface_get_display(csurf));
 | 
						|
 | 
						|
  Init(csurf, true);
 | 
						|
}
 | 
						|
 | 
						|
gfxXlibSurface::~gfxXlibSurface() {
 | 
						|
  // gfxASurface's destructor calls RecordMemoryFreed().
 | 
						|
  if (mPixmapTaken) {
 | 
						|
    XFreePixmap(*mDisplay, mDrawable);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static Drawable CreatePixmap(Screen* screen, const gfx::IntSize& size,
 | 
						|
                             unsigned int depth, Drawable relatedDrawable) {
 | 
						|
  if (!Factory::CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT))
 | 
						|
    return X11None;
 | 
						|
 | 
						|
  if (relatedDrawable == X11None) {
 | 
						|
    relatedDrawable = RootWindowOfScreen(screen);
 | 
						|
  }
 | 
						|
  Display* dpy = DisplayOfScreen(screen);
 | 
						|
  // X gives us a fatal error if we try to create a pixmap of width
 | 
						|
  // or height 0
 | 
						|
  return XCreatePixmap(dpy, relatedDrawable, std::max(1, size.width),
 | 
						|
                       std::max(1, size.height), depth);
 | 
						|
}
 | 
						|
 | 
						|
void gfxXlibSurface::TakePixmap() {
 | 
						|
  NS_ASSERTION(!mPixmapTaken, "I already own the Pixmap!");
 | 
						|
  mPixmapTaken = true;
 | 
						|
 | 
						|
  // The bit depth returned from Cairo is technically int, but this is
 | 
						|
  // the last place we'd be worried about that scenario.
 | 
						|
  unsigned int bitDepth = cairo_xlib_surface_get_depth(CairoSurface());
 | 
						|
  MOZ_ASSERT((bitDepth % 8) == 0, "Memory used not recorded correctly");
 | 
						|
 | 
						|
  // Divide by 8 because surface_get_depth gives us the number of *bits* per
 | 
						|
  // pixel.
 | 
						|
  gfx::IntSize size = GetSize();
 | 
						|
  CheckedInt32 totalBytes =
 | 
						|
      CheckedInt32(size.width) * CheckedInt32(size.height) * (bitDepth / 8);
 | 
						|
 | 
						|
  // Don't do anything in the "else" case.  We could add INT32_MAX, but that
 | 
						|
  // would overflow the memory used counter.  It would also mean we tried for
 | 
						|
  // a 2G image.  For now, we'll just assert,
 | 
						|
  MOZ_ASSERT(totalBytes.isValid(), "Did not expect to exceed 2Gb image");
 | 
						|
  if (totalBytes.isValid()) {
 | 
						|
    RecordMemoryUsed(totalBytes.value());
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
Drawable gfxXlibSurface::ReleasePixmap() {
 | 
						|
  NS_ASSERTION(mPixmapTaken, "I don't own the Pixmap!");
 | 
						|
  mPixmapTaken = false;
 | 
						|
  RecordMemoryFreed();
 | 
						|
  return mDrawable;
 | 
						|
}
 | 
						|
 | 
						|
static cairo_user_data_key_t gDestroyPixmapKey;
 | 
						|
 | 
						|
struct DestroyPixmapClosure {
 | 
						|
  DestroyPixmapClosure(Drawable d, Screen* s) : mPixmap(d), mScreen(s) {}
 | 
						|
  Drawable mPixmap;
 | 
						|
  Screen* mScreen;
 | 
						|
};
 | 
						|
 | 
						|
static void DestroyPixmap(void* data) {
 | 
						|
  DestroyPixmapClosure* closure = static_cast<DestroyPixmapClosure*>(data);
 | 
						|
  XFreePixmap(DisplayOfScreen(closure->mScreen), closure->mPixmap);
 | 
						|
  delete closure;
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
cairo_surface_t* gfxXlibSurface::CreateCairoSurface(Screen* screen,
 | 
						|
                                                    Visual* visual,
 | 
						|
                                                    const gfx::IntSize& size,
 | 
						|
                                                    Drawable relatedDrawable) {
 | 
						|
  Drawable drawable = CreatePixmap(screen, size, DepthOfVisual(screen, visual),
 | 
						|
                                   relatedDrawable);
 | 
						|
  if (!drawable) return nullptr;
 | 
						|
 | 
						|
  cairo_surface_t* surface = cairo_xlib_surface_create(
 | 
						|
      DisplayOfScreen(screen), drawable, visual, size.width, size.height);
 | 
						|
  if (cairo_surface_status(surface)) {
 | 
						|
    cairo_surface_destroy(surface);
 | 
						|
    XFreePixmap(DisplayOfScreen(screen), drawable);
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  DestroyPixmapClosure* closure = new DestroyPixmapClosure(drawable, screen);
 | 
						|
  cairo_surface_set_user_data(surface, &gDestroyPixmapKey, closure,
 | 
						|
                              DestroyPixmap);
 | 
						|
  return surface;
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
already_AddRefed<gfxXlibSurface> gfxXlibSurface::Create(
 | 
						|
    Screen* screen, Visual* visual, const gfx::IntSize& size,
 | 
						|
    Drawable relatedDrawable) {
 | 
						|
  return Create(XlibDisplay::Borrow(DisplayOfScreen(screen)), screen, visual,
 | 
						|
                size, relatedDrawable);
 | 
						|
};
 | 
						|
 | 
						|
/* static */
 | 
						|
already_AddRefed<gfxXlibSurface> gfxXlibSurface::Create(
 | 
						|
    const std::shared_ptr<XlibDisplay>& display, Screen* screen, Visual* visual,
 | 
						|
    const gfx::IntSize& size, Drawable relatedDrawable) {
 | 
						|
  MOZ_ASSERT(*display == DisplayOfScreen(screen));
 | 
						|
 | 
						|
  Drawable drawable = CreatePixmap(screen, size, DepthOfVisual(screen, visual),
 | 
						|
                                   relatedDrawable);
 | 
						|
  if (!drawable) return nullptr;
 | 
						|
 | 
						|
  RefPtr<gfxXlibSurface> result =
 | 
						|
      new gfxXlibSurface(display, drawable, visual, size);
 | 
						|
  result->TakePixmap();
 | 
						|
 | 
						|
  if (result->CairoStatus() != 0) return nullptr;
 | 
						|
 | 
						|
  return result.forget();
 | 
						|
}
 | 
						|
 | 
						|
void gfxXlibSurface::Finish() { gfxASurface::Finish(); }
 | 
						|
 | 
						|
const gfx::IntSize gfxXlibSurface::GetSize() const {
 | 
						|
  if (!mSurfaceValid) return gfx::IntSize(0, 0);
 | 
						|
 | 
						|
  return gfx::IntSize(cairo_xlib_surface_get_width(mSurface),
 | 
						|
                      cairo_xlib_surface_get_height(mSurface));
 | 
						|
}
 | 
						|
 | 
						|
const gfx::IntSize gfxXlibSurface::DoSizeQuery() {
 | 
						|
  // figure out width/height/depth
 | 
						|
  Window root_ignore;
 | 
						|
  int x_ignore, y_ignore;
 | 
						|
  unsigned int bwidth_ignore, width, height, depth;
 | 
						|
 | 
						|
  XGetGeometry(*mDisplay, mDrawable, &root_ignore, &x_ignore, &y_ignore, &width,
 | 
						|
               &height, &bwidth_ignore, &depth);
 | 
						|
 | 
						|
  return gfx::IntSize(width, height);
 | 
						|
}
 | 
						|
 | 
						|
class DisplayTable {
 | 
						|
 public:
 | 
						|
  static bool GetColormapAndVisual(Screen* screen, Visual* visual,
 | 
						|
                                   Colormap* colormap,
 | 
						|
                                   Visual** visualForColormap);
 | 
						|
 | 
						|
 private:
 | 
						|
  struct ColormapEntry {
 | 
						|
    // The Screen is needed here because colormaps (and their visuals) may
 | 
						|
    // only be used on one Screen
 | 
						|
    Screen* mScreen;
 | 
						|
    Visual* mVisual;
 | 
						|
    Colormap mColormap;
 | 
						|
  };
 | 
						|
 | 
						|
  class DisplayInfo {
 | 
						|
   public:
 | 
						|
    explicit DisplayInfo(Display* display) : mDisplay(display) {}
 | 
						|
    Display* mDisplay;
 | 
						|
    nsTArray<ColormapEntry> mColormapEntries;
 | 
						|
  };
 | 
						|
 | 
						|
  // Comparator for finding the DisplayInfo
 | 
						|
  class FindDisplay {
 | 
						|
   public:
 | 
						|
    bool Equals(const DisplayInfo& info, const Display* display) const {
 | 
						|
      return info.mDisplay == display;
 | 
						|
    }
 | 
						|
  };
 | 
						|
 | 
						|
  static int DisplayClosing(Display* display, XExtCodes* codes);
 | 
						|
 | 
						|
  nsTArray<DisplayInfo> mDisplays;
 | 
						|
  static DisplayTable* sDisplayTable;
 | 
						|
};
 | 
						|
 | 
						|
DisplayTable* DisplayTable::sDisplayTable;
 | 
						|
 | 
						|
// Pixmaps don't have a particular associated visual but the pixel values are
 | 
						|
// interpreted according to a visual/colormap pairs.
 | 
						|
//
 | 
						|
// cairo is designed for surfaces with either TrueColor visuals or the
 | 
						|
// default visual (which may not be true color).  TrueColor visuals don't
 | 
						|
// really need a colormap because the visual indicates the pixel format,
 | 
						|
// and cairo uses the default visual with the default colormap, so cairo
 | 
						|
// surfaces don't need an explicit colormap.
 | 
						|
//
 | 
						|
// However, some toolkits (e.g. GDK) need a colormap even with TrueColor
 | 
						|
// visuals.  We can create a colormap for these visuals, but it will use about
 | 
						|
// 20kB of memory in the server, so we use the default colormap when
 | 
						|
// suitable and share colormaps between surfaces.  Another reason for
 | 
						|
// minimizing colormap turnover is that the plugin process must leak resources
 | 
						|
// for each new colormap id when using older GDK libraries (bug 569775).
 | 
						|
//
 | 
						|
// Only the format of the pixels is important for rendering to Pixmaps, so if
 | 
						|
// the format of a visual matches that of the surface, then that visual can be
 | 
						|
// used for rendering to the surface.  Multiple visuals can match the same
 | 
						|
// format (but have different GLX properties), so the visual returned may
 | 
						|
// differ from the visual passed in.  Colormaps are tied to a visual, so
 | 
						|
// should only be used with their visual.
 | 
						|
 | 
						|
/* static */
 | 
						|
bool DisplayTable::GetColormapAndVisual(Screen* aScreen, Visual* aVisual,
 | 
						|
                                        Colormap* aColormap,
 | 
						|
                                        Visual** aVisualForColormap)
 | 
						|
 | 
						|
{
 | 
						|
  Display* display = DisplayOfScreen(aScreen);
 | 
						|
 | 
						|
  // Use the default colormap if the default visual matches.
 | 
						|
  Visual* defaultVisual = DefaultVisualOfScreen(aScreen);
 | 
						|
  if (aVisual == defaultVisual) {
 | 
						|
    *aColormap = DefaultColormapOfScreen(aScreen);
 | 
						|
    *aVisualForColormap = defaultVisual;
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  // Only supporting TrueColor non-default visuals
 | 
						|
  if (!aVisual || aVisual->c_class != TrueColor) return false;
 | 
						|
 | 
						|
  if (!sDisplayTable) {
 | 
						|
    sDisplayTable = new DisplayTable();
 | 
						|
  }
 | 
						|
 | 
						|
  nsTArray<DisplayInfo>* displays = &sDisplayTable->mDisplays;
 | 
						|
  size_t d = displays->IndexOf(display, 0, FindDisplay());
 | 
						|
 | 
						|
  if (d == displays->NoIndex) {
 | 
						|
    d = displays->Length();
 | 
						|
    // Register for notification of display closing, when this info
 | 
						|
    // becomes invalid.
 | 
						|
    XExtCodes* codes = XAddExtension(display);
 | 
						|
    if (!codes) return false;
 | 
						|
 | 
						|
    XESetCloseDisplay(display, codes->extension, DisplayClosing);
 | 
						|
    // Add a new DisplayInfo.
 | 
						|
    displays->AppendElement(display);
 | 
						|
  }
 | 
						|
 | 
						|
  nsTArray<ColormapEntry>* entries = &displays->ElementAt(d).mColormapEntries;
 | 
						|
 | 
						|
  // Only a small number of formats are expected to be used, so just do a
 | 
						|
  // simple linear search.
 | 
						|
  for (uint32_t i = 0; i < entries->Length(); ++i) {
 | 
						|
    const ColormapEntry& entry = entries->ElementAt(i);
 | 
						|
    if (aVisual == entry.mVisual) {
 | 
						|
      *aColormap = entry.mColormap;
 | 
						|
      *aVisualForColormap = entry.mVisual;
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // No existing entry.  Create a colormap and add an entry.
 | 
						|
  Colormap colormap =
 | 
						|
      XCreateColormap(display, RootWindowOfScreen(aScreen), aVisual, AllocNone);
 | 
						|
  ColormapEntry* newEntry = entries->AppendElement();
 | 
						|
  newEntry->mScreen = aScreen;
 | 
						|
  newEntry->mVisual = aVisual;
 | 
						|
  newEntry->mColormap = colormap;
 | 
						|
 | 
						|
  *aColormap = colormap;
 | 
						|
  *aVisualForColormap = aVisual;
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
int DisplayTable::DisplayClosing(Display* display, XExtCodes* codes) {
 | 
						|
  // No need to free the colormaps explicitly as they will be released when
 | 
						|
  // the connection is closed.
 | 
						|
  sDisplayTable->mDisplays.RemoveElement(display, FindDisplay());
 | 
						|
  if (sDisplayTable->mDisplays.Length() == 0) {
 | 
						|
    delete sDisplayTable;
 | 
						|
    sDisplayTable = nullptr;
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
bool gfxXlibSurface::GetColormapAndVisual(cairo_surface_t* aXlibSurface,
 | 
						|
                                          Colormap* aColormap,
 | 
						|
                                          Visual** aVisual) {
 | 
						|
  Screen* screen = cairo_xlib_surface_get_screen(aXlibSurface);
 | 
						|
  Visual* visual = cairo_xlib_surface_get_visual(aXlibSurface);
 | 
						|
 | 
						|
  return DisplayTable::GetColormapAndVisual(screen, visual, aColormap, aVisual);
 | 
						|
}
 | 
						|
 | 
						|
bool gfxXlibSurface::GetColormapAndVisual(Colormap* aColormap,
 | 
						|
                                          Visual** aVisual) {
 | 
						|
  if (!mSurfaceValid) return false;
 | 
						|
 | 
						|
  return GetColormapAndVisual(CairoSurface(), aColormap, aVisual);
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
int gfxXlibSurface::DepthOfVisual(const Screen* screen, const Visual* visual) {
 | 
						|
  for (int d = 0; d < screen->ndepths; d++) {
 | 
						|
    const Depth& d_info = screen->depths[d];
 | 
						|
    if (visual >= &d_info.visuals[0] &&
 | 
						|
        visual < &d_info.visuals[d_info.nvisuals])
 | 
						|
      return d_info.depth;
 | 
						|
  }
 | 
						|
 | 
						|
  NS_ERROR("Visual not on Screen.");
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
Visual* gfxXlibSurface::FindVisual(Screen* screen, gfxImageFormat format) {
 | 
						|
  int depth;
 | 
						|
  unsigned long red_mask, green_mask, blue_mask;
 | 
						|
  switch (format) {
 | 
						|
    case gfx::SurfaceFormat::A8R8G8B8_UINT32:
 | 
						|
      depth = 32;
 | 
						|
      red_mask = 0xff0000;
 | 
						|
      green_mask = 0xff00;
 | 
						|
      blue_mask = 0xff;
 | 
						|
      break;
 | 
						|
    case gfx::SurfaceFormat::X8R8G8B8_UINT32:
 | 
						|
      depth = 24;
 | 
						|
      red_mask = 0xff0000;
 | 
						|
      green_mask = 0xff00;
 | 
						|
      blue_mask = 0xff;
 | 
						|
      break;
 | 
						|
    case gfx::SurfaceFormat::R5G6B5_UINT16:
 | 
						|
      depth = 16;
 | 
						|
      red_mask = 0xf800;
 | 
						|
      green_mask = 0x7e0;
 | 
						|
      blue_mask = 0x1f;
 | 
						|
      break;
 | 
						|
    case gfx::SurfaceFormat::A8:
 | 
						|
    default:
 | 
						|
      return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  for (int d = 0; d < screen->ndepths; d++) {
 | 
						|
    const Depth& d_info = screen->depths[d];
 | 
						|
    if (d_info.depth != depth) continue;
 | 
						|
 | 
						|
    for (int v = 0; v < d_info.nvisuals; v++) {
 | 
						|
      Visual* visual = &d_info.visuals[v];
 | 
						|
 | 
						|
      if (visual->c_class == TrueColor && visual->red_mask == red_mask &&
 | 
						|
          visual->green_mask == green_mask && visual->blue_mask == blue_mask)
 | 
						|
        return visual;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
Screen* gfxXlibSurface::XScreen() {
 | 
						|
  return cairo_xlib_surface_get_screen(CairoSurface());
 | 
						|
}
 |