summaryrefslogtreecommitdiffstats
path: root/client/x11/red_pixmap_cairo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'client/x11/red_pixmap_cairo.cpp')
-rw-r--r--client/x11/red_pixmap_cairo.cpp197
1 files changed, 197 insertions, 0 deletions
diff --git a/client/x11/red_pixmap_cairo.cpp b/client/x11/red_pixmap_cairo.cpp
new file mode 100644
index 00000000..c1454865
--- /dev/null
+++ b/client/x11/red_pixmap_cairo.cpp
@@ -0,0 +1,197 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "common.h"
+#include "red_pixmap_cairo.h"
+#include "debug.h"
+#include "utils.h"
+#include "pixels_source_p.h"
+#include "x_platform.h"
+#include <sys/shm.h>
+
+
+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;
+
+
+ try {
+ cairo_format_t cairo_format;
+
+ if (win) {
+ vinfo = XPlatform::get_vinfo()[win->get_screen_num()];
+ }
+
+ if (vinfo && XShmQueryExtension(XPlatform::get_display())) {
+ 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");
+ }
+
+ /*
+ * Workaround for weird problem that is seen in Fedora 10/11,
+ * spice get bad access error, but I am not sure if the bug is in spice or in X
+ * for now just use this workaround.
+ */
+ XSync(XPlatform::get_display(), False);
+
+ ((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 (!(vinfo && XShmQueryExtension(XPlatform::get_display()))) {
+ ((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 (vinfo && XShmQueryExtension(XPlatform::get_display())) {
+ 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);
+ shmdt(shminfo->shmaddr);
+ delete shminfo;
+ }
+}
+