123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 |
- /* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
- * Copyright (C) 2004-2005 Constantin Kaplinsky. All Rights Reserved.
- *
- * This 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 software 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 software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
- //
- // Image.cxx
- //
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/types.h>
-
- #include <sys/ipc.h>
- #include <sys/shm.h>
-
- #include <rfb/LogWriter.h>
- #include <x0vncserver/Image.h>
-
- //
- // ImageCleanup is used to delete Image instances automatically on
- // program shutdown. This is important for shared memory images.
- //
-
- #include <list>
-
- class ImageCleanup {
- public:
- std::list<Image *> images;
-
- ~ImageCleanup()
- {
- while (!images.empty()) {
- delete images.front();
- }
- }
- };
-
- ImageCleanup imageCleanup;
-
- //
- // Image class implementation.
- //
-
- static rfb::LogWriter vlog("Image");
-
- Image::Image(Display *d)
- : xim(NULL), dpy(d)
- {
- imageCleanup.images.push_back(this);
- }
-
- Image::Image(Display *d, int width, int height)
- : xim(NULL), dpy(d)
- {
- imageCleanup.images.push_back(this);
- Init(width, height);
- }
-
- void Image::Init(int width, int height)
- {
- Visual* vis = DefaultVisual(dpy, DefaultScreen(dpy));
-
- if (vis->c_class != TrueColor) {
- vlog.error("pseudocolour not supported");
- exit(1);
- }
-
- xim = XCreateImage(dpy, vis, DefaultDepth(dpy, DefaultScreen(dpy)),
- ZPixmap, 0, 0, width, height, BitmapPad(dpy), 0);
-
- xim->data = (char *)malloc(xim->bytes_per_line * xim->height);
- if (xim->data == NULL) {
- vlog.error("malloc() failed");
- exit(1);
- }
- }
-
- Image::~Image()
- {
- imageCleanup.images.remove(this);
-
- // XDestroyImage will free xim->data if necessary
- if (xim != NULL)
- XDestroyImage(xim);
- }
-
- void Image::get(Window wnd, int x, int y)
- {
- get(wnd, x, y, xim->width, xim->height);
- }
-
- void Image::get(Window wnd, int x, int y, int w, int h,
- int dst_x, int dst_y)
- {
- XGetSubImage(dpy, wnd, x, y, w, h, AllPlanes, ZPixmap, xim, dst_x, dst_y);
- }
-
- //
- // Copying pixels from one image to another.
- //
- // FIXME: Use Point and Rect structures?
- // FIXME: Too many similar methods?
- //
-
- inline
- void Image::copyPixels(XImage *src,
- int dst_x, int dst_y,
- int src_x, int src_y,
- int w, int h)
- {
- const char *srcOffset =
- src->data + (src_y * src->bytes_per_line +
- src_x * (src->bits_per_pixel / 8));
- char *dstOffset =
- xim->data + (dst_y * xim->bytes_per_line +
- dst_x * (xim->bits_per_pixel / 8));
-
- int rowLength = w * (xim->bits_per_pixel / 8);
-
- for (int i = 0; i < h ; i++) {
- memcpy(dstOffset, srcOffset, rowLength);
- srcOffset += src->bytes_per_line;
- dstOffset += xim->bytes_per_line;
- }
- }
-
- void Image::updateRect(XImage *src, int dst_x, int dst_y)
- {
- // Limit width and height at destination image size.
- int w = src->width;
- if (dst_x + w > xim->width)
- w = xim->width - dst_x;
- int h = src->height;
- if (dst_y + h > xim->height)
- h = xim->height - dst_y;
-
- copyPixels(src, dst_x, dst_y, 0, 0, w, h);
- }
-
- void Image::updateRect(Image *src, int dst_x, int dst_y)
- {
- updateRect(src->xim, dst_x, dst_y);
- }
-
- void Image::updateRect(XImage *src, int dst_x, int dst_y, int w, int h)
- {
- // Correct width and height if necessary.
- if (w > src->width)
- w = src->width;
- if (dst_x + w > xim->width)
- w = xim->width - dst_x;
- if (h > src->height)
- h = src->height;
- if (dst_y + h > xim->height)
- h = xim->height - dst_y;
-
- copyPixels(src, dst_x, dst_y, 0, 0, w, h);
- }
-
- void Image::updateRect(Image *src, int dst_x, int dst_y, int w, int h)
- {
- updateRect(src->xim, dst_x, dst_y, w, h);
- }
-
- void Image::updateRect(XImage *src, int dst_x, int dst_y,
- int src_x, int src_y, int w, int h)
- {
- // Correct width and height if necessary.
- if (src_x + w > src->width)
- w = src->width - src_x;
- if (dst_x + w > xim->width)
- w = xim->width - dst_x;
- if (src_y + h > src->height)
- h = src->height - src_y;
- if (dst_y + h > xim->height)
- h = xim->height - dst_y;
-
- copyPixels(src, dst_x, dst_y, src_x, src_y, w, h);
- }
-
- void Image::updateRect(Image *src, int dst_x, int dst_y,
- int src_x, int src_y, int w, int h)
- {
- updateRect(src->xim, dst_x, dst_y, src_x, src_y, w, h);
- }
-
- //
- // ShmImage class implementation.
- //
-
- static bool caughtShmError = false;
-
- static int ShmCreationXErrorHandler(Display *dpy, XErrorEvent *error)
- {
- caughtShmError = true;
- return 0;
- }
-
- ShmImage::ShmImage(Display *d)
- : Image(d), shminfo(NULL)
- {
- }
-
- ShmImage::ShmImage(Display *d, int width, int height)
- : Image(d), shminfo(NULL)
- {
- Init(width, height);
- }
-
- // FIXME: Remove duplication of cleanup operations.
- void ShmImage::Init(int width, int height, const XVisualInfo *vinfo)
- {
- int major, minor;
- Bool pixmaps;
-
- if (!XShmQueryVersion(dpy, &major, &minor, &pixmaps)) {
- vlog.error("XShmQueryVersion() failed");
- return;
- }
-
- Visual *visual;
- int depth;
-
- if (vinfo == NULL) {
- visual = DefaultVisual(dpy, DefaultScreen(dpy));
- depth = DefaultDepth(dpy, DefaultScreen(dpy));
- } else {
- visual = vinfo->visual;
- depth = vinfo->depth;
- }
-
- if (visual->c_class != TrueColor) {
- vlog.error("pseudocolour not supported");
- exit(1);
- }
-
- shminfo = new XShmSegmentInfo;
-
- xim = XShmCreateImage(dpy, visual, depth, ZPixmap, 0, shminfo,
- width, height);
- if (xim == NULL) {
- vlog.error("XShmCreateImage() failed");
- delete shminfo;
- shminfo = NULL;
- return;
- }
-
- shminfo->shmid = shmget(IPC_PRIVATE,
- xim->bytes_per_line * xim->height,
- IPC_CREAT|0777);
- if (shminfo->shmid == -1) {
- perror("shmget");
- vlog.error("shmget() failed (%d bytes requested)",
- int(xim->bytes_per_line * xim->height));
- XDestroyImage(xim);
- xim = NULL;
- delete shminfo;
- shminfo = NULL;
- return;
- }
-
- shminfo->shmaddr = xim->data = (char *)shmat(shminfo->shmid, 0, 0);
- if (shminfo->shmaddr == (char *)-1) {
- perror("shmat");
- vlog.error("shmat() failed (%d bytes requested)",
- int(xim->bytes_per_line * xim->height));
- shmctl(shminfo->shmid, IPC_RMID, 0);
- XDestroyImage(xim);
- xim = NULL;
- delete shminfo;
- shminfo = NULL;
- return;
- }
-
- shminfo->readOnly = False;
-
- XErrorHandler oldHdlr = XSetErrorHandler(ShmCreationXErrorHandler);
- XShmAttach(dpy, shminfo);
- XSync(dpy, False);
- XSetErrorHandler(oldHdlr);
- if (caughtShmError) {
- vlog.error("XShmAttach() failed");
- shmdt(shminfo->shmaddr);
- shmctl(shminfo->shmid, IPC_RMID, 0);
- XDestroyImage(xim);
- xim = NULL;
- delete shminfo;
- shminfo = NULL;
- return;
- }
- }
-
- ShmImage::~ShmImage()
- {
- // FIXME: Destroy image as described in MIT-SHM documentation.
- if (shminfo != NULL) {
- shmdt(shminfo->shmaddr);
- shmctl(shminfo->shmid, IPC_RMID, 0);
- delete shminfo;
- }
- }
-
- void ShmImage::get(Window wnd, int x, int y)
- {
- XShmGetImage(dpy, wnd, xim, x, y, AllPlanes);
- }
-
- void ShmImage::get(Window wnd, int x, int y, int w, int h,
- int dst_x, int dst_y)
- {
- // XShmGetImage is faster, but can only retrieve the entire
- // window. Use it for large reads.
- if (x == dst_x && y == dst_y && (long)w * h > (long)xim->width * xim->height / 4) {
- XShmGetImage(dpy, wnd, xim, 0, 0, AllPlanes);
- } else {
- XGetSubImage(dpy, wnd, x, y, w, h, AllPlanes, ZPixmap, xim, dst_x, dst_y);
- }
- }
-
- //
- // ImageFactory class implementation
- //
- // FIXME: Make ImageFactory always create images of the same class?
- //
-
- ImageFactory::ImageFactory(bool allowShm)
- : mayUseShm(allowShm)
- {
- }
-
- ImageFactory::~ImageFactory()
- {
- }
-
- Image *ImageFactory::newImage(Display *d, int width, int height)
- {
- Image *image = NULL;
-
- // Now, try to use shared memory image.
-
- if (mayUseShm) {
- image = new ShmImage(d, width, height);
- if (image->xim != NULL) {
- return image;
- }
-
- delete image;
- vlog.error("Failed to create SHM image, falling back to Xlib image");
- }
-
- // Fall back to Xlib image.
-
- image = new Image(d, width, height);
- return image;
- }
|