michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #if defined(MOZ_WIDGET_GTK) michael@0: #include michael@0: #include michael@0: #elif defined(MOZ_WIDGET_QT) michael@0: #include michael@0: #endif michael@0: michael@0: #include "nsShmImage.h" michael@0: #include "gfxPlatform.h" michael@0: #include "gfxImageSurface.h" michael@0: michael@0: #ifdef MOZ_HAVE_SHMIMAGE michael@0: michael@0: using namespace mozilla::ipc; michael@0: michael@0: // If XShm isn't available to our client, we'll try XShm once, fail, michael@0: // set this to false and then never try again. michael@0: static bool gShmAvailable = true; michael@0: bool nsShmImage::UseShm() michael@0: { michael@0: return gfxPlatform::GetPlatform()-> michael@0: ScreenReferenceSurface()->GetType() == gfxSurfaceType::Image michael@0: && gShmAvailable; michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsShmImage::Create(const gfxIntSize& aSize, michael@0: Visual* aVisual, unsigned int aDepth) michael@0: { michael@0: Display* dpy = DISPLAY(); michael@0: michael@0: nsRefPtr shm = new nsShmImage(); michael@0: shm->mImage = XShmCreateImage(dpy, aVisual, aDepth, michael@0: ZPixmap, nullptr, michael@0: &(shm->mInfo), michael@0: aSize.width, aSize.height); michael@0: if (!shm->mImage) { michael@0: return nullptr; michael@0: } michael@0: michael@0: size_t size = SharedMemory::PageAlignedSize( michael@0: shm->mImage->bytes_per_line * shm->mImage->height); michael@0: shm->mSegment = new SharedMemorySysV(); michael@0: if (!shm->mSegment->Create(size) || !shm->mSegment->Map(size)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: shm->mInfo.shmid = shm->mSegment->GetHandle(); michael@0: shm->mInfo.shmaddr = michael@0: shm->mImage->data = static_cast(shm->mSegment->memory()); michael@0: shm->mInfo.readOnly = False; michael@0: michael@0: int xerror = 0; michael@0: #if defined(MOZ_WIDGET_GTK) michael@0: gdk_error_trap_push(); michael@0: Status attachOk = XShmAttach(dpy, &shm->mInfo); michael@0: XSync(dpy, False); michael@0: xerror = gdk_error_trap_pop(); michael@0: #elif defined(MOZ_WIDGET_QT) michael@0: Status attachOk = XShmAttach(dpy, &shm->mInfo); michael@0: #endif michael@0: michael@0: if (!attachOk || xerror) { michael@0: // Assume XShm isn't available, and don't attempt to use it michael@0: // again. michael@0: gShmAvailable = false; michael@0: return nullptr; michael@0: } michael@0: michael@0: shm->mXAttached = true; michael@0: shm->mSize = aSize; michael@0: switch (shm->mImage->depth) { michael@0: case 32: michael@0: if ((shm->mImage->red_mask == 0xff0000) && michael@0: (shm->mImage->green_mask == 0xff00) && michael@0: (shm->mImage->blue_mask == 0xff)) { michael@0: shm->mFormat = gfxImageFormat::ARGB32; michael@0: break; michael@0: } michael@0: goto unsupported; michael@0: case 24: michael@0: // Only xRGB is supported. michael@0: if ((shm->mImage->red_mask == 0xff0000) && michael@0: (shm->mImage->green_mask == 0xff00) && michael@0: (shm->mImage->blue_mask == 0xff)) { michael@0: shm->mFormat = gfxImageFormat::RGB24; michael@0: break; michael@0: } michael@0: goto unsupported; michael@0: case 16: michael@0: shm->mFormat = gfxImageFormat::RGB16_565; break; michael@0: unsupported: michael@0: default: michael@0: NS_WARNING("Unsupported XShm Image format!"); michael@0: gShmAvailable = false; michael@0: return nullptr; michael@0: } michael@0: return shm.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsShmImage::AsSurface() michael@0: { michael@0: return nsRefPtr( michael@0: new gfxImageSurface(static_cast(mSegment->memory()), michael@0: mSize, michael@0: mImage->bytes_per_line, michael@0: mFormat) michael@0: ).forget(); michael@0: } michael@0: michael@0: #if (MOZ_WIDGET_GTK == 2) michael@0: void michael@0: nsShmImage::Put(GdkWindow* aWindow, GdkRectangle* aRects, GdkRectangle* aEnd) michael@0: { michael@0: GdkDrawable* gd; michael@0: gint dx, dy; michael@0: gdk_window_get_internal_paint_info(aWindow, &gd, &dx, &dy); michael@0: michael@0: Display* dpy = gdk_x11_get_default_xdisplay(); michael@0: Drawable d = GDK_DRAWABLE_XID(gd); michael@0: michael@0: GC gc = XCreateGC(dpy, d, 0, nullptr); michael@0: for (GdkRectangle* r = aRects; r < aEnd; r++) { michael@0: XShmPutImage(dpy, d, gc, mImage, michael@0: r->x, r->y, michael@0: r->x - dx, r->y - dy, michael@0: r->width, r->height, michael@0: False); michael@0: } michael@0: XFreeGC(dpy, gc); michael@0: michael@0: // FIXME/bug 597336: we need to ensure that the shm image isn't michael@0: // scribbled over before all its pending XShmPutImage()s complete. michael@0: // However, XSync() is an unnecessarily heavyweight michael@0: // synchronization mechanism; other options are possible. If this michael@0: // XSync is shown to hurt responsiveness, we need to explore the michael@0: // other options. michael@0: XSync(dpy, False); michael@0: } michael@0: michael@0: #elif (MOZ_WIDGET_GTK == 3) michael@0: void michael@0: nsShmImage::Put(GdkWindow* aWindow, cairo_rectangle_list_t* aRects) michael@0: { michael@0: Display* dpy = gdk_x11_get_default_xdisplay(); michael@0: Drawable d = GDK_WINDOW_XID(aWindow); michael@0: int dx = 0, dy = 0; michael@0: michael@0: GC gc = XCreateGC(dpy, d, 0, nullptr); michael@0: cairo_rectangle_t r; michael@0: for (int i = 0; i < aRects->num_rectangles; i++) { michael@0: r = aRects->rectangles[i]; michael@0: XShmPutImage(dpy, d, gc, mImage, michael@0: r.x, r.y, michael@0: r.x - dx, r.y - dy, michael@0: r.width, r.height, michael@0: False); michael@0: } michael@0: michael@0: XFreeGC(dpy, gc); michael@0: michael@0: // FIXME/bug 597336: we need to ensure that the shm image isn't michael@0: // scribbled over before all its pending XShmPutImage()s complete. michael@0: // However, XSync() is an unnecessarily heavyweight michael@0: // synchronization mechanism; other options are possible. If this michael@0: // XSync is shown to hurt responsiveness, we need to explore the michael@0: // other options. michael@0: XSync(dpy, False); michael@0: } michael@0: michael@0: #elif defined(MOZ_WIDGET_QT) michael@0: void michael@0: nsShmImage::Put(QWindow* aWindow, QRect& aRect) michael@0: { michael@0: Display* dpy = gfxQtPlatform::GetXDisplay(aWindow); michael@0: Drawable d = aWindow->winId(); michael@0: michael@0: GC gc = XCreateGC(dpy, d, 0, nullptr); michael@0: // Avoid out of bounds painting michael@0: QRect inter = aRect.intersected(aWindow->geometry()); michael@0: XShmPutImage(dpy, d, gc, mImage, michael@0: inter.x(), inter.y(), michael@0: inter.x(), inter.y(), michael@0: inter.width(), inter.height(), michael@0: False); michael@0: XFreeGC(dpy, gc); michael@0: } michael@0: #endif michael@0: michael@0: already_AddRefed michael@0: nsShmImage::EnsureShmImage(const gfxIntSize& aSize, Visual* aVisual, unsigned int aDepth, michael@0: nsRefPtr& aImage) michael@0: { michael@0: if (!aImage || aImage->Size() != aSize) { michael@0: // Because we XSync() after XShmAttach() to trap errors, we michael@0: // know that the X server has the old image's memory mapped michael@0: // into its address space, so it's OK to destroy the old image michael@0: // here even if there are outstanding Puts. The Detach is michael@0: // ordered after the Puts. michael@0: aImage = nsShmImage::Create(aSize, aVisual, aDepth); michael@0: } michael@0: return !aImage ? nullptr : aImage->AsSurface(); michael@0: } michael@0: michael@0: #endif // defined(MOZ_X11) && defined(MOZ_HAVE_SHAREDMEMORYSYSV)