/*
Copyright (C) 2009 Red Hat, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "common.h"
#include "red_pixmap_cairo.h"
#include "debug.h"
#include "utils.h"
#include "pixels_source_p.h"
#include "x_platform.h"
#include
RedPixmapCairo::RedPixmapCairo(int width, int height, RedPixmap::Format format,
bool top_bottom, rgb32_t* pallet, RedWindow *win)
: RedPixmap(width, height, format, top_bottom, pallet)
{
cairo_surface_t* cairo_surf = NULL;
cairo_t* cairo = NULL;
ASSERT(format == RedPixmap::ARGB32 || format == RedPixmap::RGB32 || format == RedPixmap::A1);
ASSERT(sizeof(RedDrawable_p) <= PIXELES_SOURCE_OPAQUE_SIZE);
XImage *image = NULL;
XShmSegmentInfo *shminfo = NULL;
_data = NULL;
XVisualInfo *vinfo = NULL;
bool using_shm = false;
try {
cairo_format_t cairo_format;
if (win) {
vinfo = XPlatform::get_vinfo()[win->get_screen_num()];
}
using_shm = vinfo && XPlatform::is_x_shm_avail();
if (using_shm) {
int depth;
switch (format) {
case RedPixmap::ARGB32:
case RedPixmap::RGB32:
depth = XPlatform::get_vinfo()[0]->depth;
cairo_format = format == RedPixmap::ARGB32 ? CAIRO_FORMAT_ARGB32 :
CAIRO_FORMAT_RGB24;
break;
case RedPixmap::A1:
depth = 1;
cairo_format = CAIRO_FORMAT_A1;
break;
default:
THROW("unsupported format %d", format);
}
shminfo = new XShmSegmentInfo;
shminfo->shmid = -1;
shminfo->shmaddr = 0;
((PixelsSource_p*)get_opaque())->type = PIXELS_SOURCE_TYPE_XSHM_DRAWABLE;
memset(shminfo, 0, sizeof(XShmSegmentInfo));
image = XShmCreateImage(XPlatform::get_display(), vinfo->visual,
depth, ZPixmap, NULL, shminfo, width, height);
if (!image) {
THROW("XShmCreateImage failed");
}
shminfo->shmid = shmget(IPC_PRIVATE, height * _stride, IPC_CREAT | 0777);
if (shminfo->shmid < 0) {
THROW("shmget failed");
}
shminfo->shmaddr = (char *)shmat(shminfo->shmid, 0, 0);
if (shmctl(shminfo->shmid, IPC_RMID, NULL) == -1) {
LOG_WARN("shmctl IPC_RMID failed %s (%d)", strerror(errno), errno);
}
if (!shminfo->shmaddr) {
THROW("shmat failed");
}
shminfo->readOnly = False;
if (!XShmAttach(XPlatform::get_display(), shminfo)) {
THROW("XShmAttach failed");
}
((PixelsSource_p*)get_opaque())->x_shm_drawable.x_image = image;
((PixelsSource_p*)get_opaque())->x_shm_drawable.shminfo = shminfo;
_data = (uint8_t *)shminfo->shmaddr;
image->data = (char *)_data;
} else {
image = new XImage;
_data = new uint8_t[height * _stride];
((PixelsSource_p*)get_opaque())->type = PIXELS_SOURCE_TYPE_PIXMAP;
((PixelsSource_p*)get_opaque())->pixmap.x_image = image;
memset(image, 0, sizeof(*image));
image->width = _width;
image->height = _height;
image->data = (char*)_data;
image->byte_order = LSBFirst;
image->bitmap_unit = 32;
image->bitmap_bit_order = LSBFirst;
image->bitmap_pad = 32;
image->bytes_per_line = _stride;
switch (format) {
case RedPixmap::ARGB32:
case RedPixmap::RGB32:
image->depth = XPlatform::get_vinfo()[0]->depth;
image->format = ZPixmap;
image->bits_per_pixel = 32;
image->red_mask = 0x00ff0000;
image->green_mask = 0x0000ff00;
image->blue_mask = 0x000000ff;
cairo_format = format == RedPixmap::ARGB32 ? CAIRO_FORMAT_ARGB32 :
CAIRO_FORMAT_RGB24;
break;
case RedPixmap::A1:
image->depth = 1;
image->format = XYBitmap;
cairo_format = CAIRO_FORMAT_A1;
break;
default:
THROW("unsupported format %d", format);
}
if (!XInitImage(image)) {
THROW("init image failed");
}
}
cairo_surf = cairo_image_surface_create_for_data(_data, cairo_format, _width, _height,
_stride);
if (cairo_surface_status(cairo_surf) != CAIRO_STATUS_SUCCESS) {
THROW("surf create failed");
}
cairo = cairo_create(cairo_surf);
cairo_surface_destroy(cairo_surf);
if (cairo_status(cairo) != CAIRO_STATUS_SUCCESS) {
THROW("cairo create failed failed");
}
if (!using_shm) {
((PixelsSource_p*)get_opaque())->pixmap.cairo_surf = cairo_surf;
} else {
((PixelsSource_p*)get_opaque())->x_shm_drawable.cairo_surf = cairo_surf;
}
((RedDrawable_p*)get_opaque())->cairo = cairo;
} catch (...) {
if (using_shm) {
if (image) {
XDestroyImage(image);
}
if (shminfo) {
if (shminfo->shmid >= 0) {
shmctl(shminfo->shmid, IPC_RMID, NULL);
}
if (shminfo->shmaddr) {
shmdt(shminfo->shmaddr);
}
}
} else {
delete image;
delete _data;
}
throw;
}
}
RedPixmapCairo::~RedPixmapCairo()
{
cairo_destroy(((RedDrawable_p*)get_opaque())->cairo);
if (((PixelsSource_p*)get_opaque())->type == PIXELS_SOURCE_TYPE_PIXMAP) {
delete ((PixelsSource_p*)get_opaque())->pixmap.x_image;
delete _data;
} else {
XShmSegmentInfo *shminfo = ((PixelsSource_p*)get_opaque())->x_shm_drawable.shminfo;
XShmDetach(XPlatform::get_display(), shminfo);
XDestroyImage(((PixelsSource_p*)get_opaque())->x_shm_drawable.x_image);
XSync(XPlatform::get_display(), False);
shmdt(shminfo->shmaddr);
delete shminfo;
}
}