blob: 880fa5f98f41fe4265270c2089e2d02a448e38d7 [file] [log] [blame]
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +00001/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +00002 * Copyright (C) 2004-2005 Constantin Kaplinsky. All Rights Reserved.
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +00003 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19//
20// Image.cxx
21//
22
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000023#include <stdio.h>
24#include <sys/types.h>
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +000025
26#ifdef HAVE_MITSHM
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000027#include <sys/ipc.h>
28#include <sys/shm.h>
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +000029#endif
30
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000031#include "Image.h"
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +000032
33//
34// ImageCleanup is used to delete Image instances automatically on
35// program shutdown. This is important for shared memory images.
36//
37
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000038#include <list>
39
40class ImageCleanup {
41public:
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +000042 std::list<Image *> images;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000043
44 ~ImageCleanup()
45 {
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +000046 // DEBUG:
47 // fprintf(stderr, "~ImageCleanup() called\n");
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000048
49 while (!images.empty()) {
50 delete images.front();
51 }
52 }
53};
54
55ImageCleanup imageCleanup;
56
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +000057//
58// Image class implementation.
59//
60
61Image::Image(Display *d)
62 : xim(NULL), dpy(d), trueColor(true)
63{
64 imageCleanup.images.push_back(this);
65}
66
67Image::Image(Display *d, int width, int height)
68 : xim(NULL), dpy(d), trueColor(true)
69{
70 imageCleanup.images.push_back(this);
71 Init(width, height);
72}
73
74void Image::Init(int width, int height)
75{
76 Visual* vis = DefaultVisual(dpy, DefaultScreen(dpy));
77 trueColor = (vis->c_class == TrueColor);
78
79 xim = XCreateImage(dpy, vis, DefaultDepth(dpy, DefaultScreen(dpy)),
80 ZPixmap, 0, 0, width, height, BitmapPad(dpy), 0);
81
82 xim->data = (char *)malloc(xim->bytes_per_line * xim->height);
83 if (xim->data == NULL) {
84 fprintf(stderr, "malloc() failed\n");
85 exit(1);
86 }
87}
88
89Image::~Image()
90{
91 // DEBUG:
92 // fprintf(stderr, "~Image() called\n");
93
94 imageCleanup.images.remove(this);
95 if (xim != NULL)
96 XDestroyImage(xim);
97}
98
99void Image::get(Window wnd, int x, int y)
100{
101 get(wnd, x, y, xim->width, xim->height);
102}
103
104void Image::get(Window wnd, int x, int y, int w, int h)
105{
106 XGetSubImage(dpy, wnd, x, y, w, h, AllPlanes, ZPixmap, xim, 0, 0);
107}
108
109void Image::updateRect(Image *src, int dst_x, int dst_y)
110{
111 updateRect(src->xim, dst_x, dst_y);
112}
113
114void Image::updateRect(XImage *src, int dst_x, int dst_y)
115{
116 // Limit width and height at destination image size
117 int w = src->width;
118 if (dst_x + w > xim->width)
119 w = xim->width - dst_x;
120 int h = src->height;
121 if (dst_y + h > xim->height)
122 h = xim->height - dst_y;
123
124 // Copy pixels
125 const char *srcOffset = src->data;
126 char *dstOffset = xim->data + (dst_y * xim->bytes_per_line +
127 dst_x * (xim->bits_per_pixel / 8));
128 int lineLength = w * (xim->bits_per_pixel / 8);
129 for (int i = 0; i < h ; i++) {
130 memcpy(dstOffset, srcOffset, lineLength);
131 srcOffset += src->bytes_per_line;
132 dstOffset += xim->bytes_per_line;
133 }
134}
135
136#ifdef HAVE_MITSHM
137
138//
139// ShmImage class implementation.
140//
141
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000142static bool caughtShmError = false;
143
144static int ShmCreationXErrorHandler(Display *dpy, XErrorEvent *error)
145{
146 caughtShmError = true;
147 return 0;
148}
149
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000150ShmImage::ShmImage(Display *d)
151 : Image(d), shminfo(NULL)
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000152{
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000153}
154
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000155ShmImage::ShmImage(Display *d, int width, int height)
156 : Image(d), shminfo(NULL)
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000157{
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000158 Init(width, height);
159}
160
161// FIXME: Remove duplication of cleanup operations.
162void ShmImage::Init(int width, int height, const XVisualInfo *vinfo)
163{
164 int major, minor;
165 Bool pixmaps;
166
167 if (!XShmQueryVersion(dpy, &major, &minor, &pixmaps)) {
168 fprintf(stderr, "XShmQueryVersion() failed\n");
169 return;
170 }
171
172 Visual *visual;
173 int depth;
174
175 if (vinfo == NULL) {
176 visual = DefaultVisual(dpy, DefaultScreen(dpy));
177 depth = DefaultDepth(dpy, DefaultScreen(dpy));
178 } else {
179 visual = vinfo->visual;
180 depth = vinfo->depth;
181 }
182
183 trueColor = (visual->c_class == TrueColor);
184
185 shminfo = new XShmSegmentInfo;
186
187 xim = XShmCreateImage(dpy, visual, depth, ZPixmap, 0, shminfo,
188 width, height);
189 if (xim == NULL) {
190 fprintf(stderr, "XShmCreateImage() failed\n");
191 delete shminfo;
192 shminfo = NULL;
193 return;
194 }
195
196 shminfo->shmid = shmget(IPC_PRIVATE,
197 xim->bytes_per_line * xim->height,
198 IPC_CREAT|0777);
199 if (shminfo->shmid == -1) {
200 perror("shmget");
201 fprintf(stderr,
202 "shmget() failed (%d bytes requested)\n",
203 int(xim->bytes_per_line * xim->height));
204 XDestroyImage(xim);
205 xim = NULL;
206 delete shminfo;
207 shminfo = NULL;
208 return;
209 }
210
211 shminfo->shmaddr = xim->data = (char *)shmat(shminfo->shmid, 0, 0);
212 if (shminfo->shmaddr == (char *)-1) {
213 perror("shmat");
214 fprintf(stderr,
215 "shmat() failed (%d bytes requested)\n",
216 int(xim->bytes_per_line * xim->height));
217 shmctl(shminfo->shmid, IPC_RMID, 0);
218 XDestroyImage(xim);
219 xim = NULL;
220 delete shminfo;
221 shminfo = NULL;
222 return;
223 }
224
225 shminfo->readOnly = False;
226
227 XErrorHandler oldHdlr = XSetErrorHandler(ShmCreationXErrorHandler);
228 XShmAttach(dpy, shminfo);
229 XSync(dpy, False);
230 XSetErrorHandler(oldHdlr);
231 if (caughtShmError) {
232 fprintf(stderr, "XShmAttach() failed\n");
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000233 shmdt(shminfo->shmaddr);
234 shmctl(shminfo->shmid, IPC_RMID, 0);
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000235 XDestroyImage(xim);
236 xim = NULL;
237 delete shminfo;
238 shminfo = NULL;
239 return;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000240 }
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000241
242 // DEBUG:
243 // fprintf(stderr,
244 // "Using shared memory XImage (%d bytes image data)\n",
245 // int(xim->bytes_per_line * xim->height));
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000246}
247
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000248ShmImage::~ShmImage()
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000249{
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000250 // DEBUG:
251 // fprintf(stderr,"~ShmImage called\n");
252
253 // FIXME: Destroy image as described in MIT-SHM documentation.
254 if (shminfo != NULL) {
255 shmdt(shminfo->shmaddr);
256 shmctl(shminfo->shmid, IPC_RMID, 0);
257 delete shminfo;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000258 }
259}
260
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000261void ShmImage::get(Window wnd, int x, int y)
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000262{
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000263 XShmGetImage(dpy, wnd, xim, x, y, AllPlanes);
264}
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000265
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000266void ShmImage::get(Window wnd, int x, int y, int w, int h)
267{
268 // FIXME: Use SHM for this as well?
269 XGetSubImage(dpy, wnd, x, y, w, h, AllPlanes, ZPixmap, xim, 0, 0);
270}
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000271
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000272#ifdef HAVE_READDISPLAY
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000273
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000274//
275// IrixOverlayShmImage class implementation.
276//
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000277
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000278IrixOverlayShmImage::IrixOverlayShmImage(Display *d)
279 : ShmImage(d), readDisplayBuf(NULL)
280{
281}
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000282
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000283IrixOverlayShmImage::IrixOverlayShmImage(Display *d, int width, int height)
284 : ShmImage(d), readDisplayBuf(NULL)
285{
286 Init(width, height);
287}
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000288
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000289void IrixOverlayShmImage::Init(int width, int height)
290{
291 // First determine the pixel format used by XReadDisplay.
292 XVisualInfo vinfo;
293 if (!getOverlayVisualInfo(&vinfo))
294 return;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000295
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000296 // Create an SHM image of the same format.
297 ShmImage::Init(width, height, &vinfo);
298 if (xim == NULL)
299 return;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000300
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000301 // FIXME: Check if the extension is available at run time.
302 // FIXME: Does XShmCreateReadDisplayBuf() require some cleanup?
303 readDisplayBuf = XShmCreateReadDisplayBuf(dpy, NULL, shminfo, width, height);
304}
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000305
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000306bool IrixOverlayShmImage::getOverlayVisualInfo(XVisualInfo *vinfo_ret)
307{
308 // First, get an image in the format returned by XReadDisplay.
309 unsigned long hints = 0, hints_ret;
310 XImage *testImage = XReadDisplay(dpy, DefaultRootWindow(dpy),
311 0, 0, 8, 8, hints, &hints_ret);
312 if (testImage == NULL)
313 return false;
314
315 // Fill in a template for matching visuals.
316 XVisualInfo tmpl;
317 tmpl.c_class = TrueColor;
318 tmpl.depth = 24;
319 tmpl.red_mask = testImage->red_mask;
320 tmpl.green_mask = testImage->green_mask;
321 tmpl.blue_mask = testImage->blue_mask;
322
323 // List fields in template that make sense.
324 long mask = (VisualClassMask |
325 VisualRedMaskMask |
326 VisualGreenMaskMask |
327 VisualBlueMaskMask);
328
329 // We don't need that image any more.
330 XDestroyImage(testImage);
331
332 // Now, get a list of matching visuals available.
333 int nVisuals;
334 XVisualInfo *vinfo = XGetVisualInfo(dpy, mask, &tmpl, &nVisuals);
335 if (vinfo == NULL || nVisuals <= 0) {
336 if (vinfo != NULL) {
337 XFree(vinfo);
338 }
339 return false;
340 }
341
342 // Use first visual from the list.
343 *vinfo_ret = vinfo[0];
344
345 XFree(vinfo);
346
347 return true;
348}
349
350IrixOverlayShmImage::~IrixOverlayShmImage()
351{
352 // DEBUG:
353 // fprintf(stderr,"~IrixOverlayShmImage called\n");
354}
355
356void IrixOverlayShmImage::get(Window wnd, int x, int y)
357{
358 XRectangle rect;
359 unsigned long hints = XRD_TRANSPARENT | XRD_READ_POINTER;
360
361 rect.x = x;
362 rect.y = y;
363 rect.width = xim->width;
364 rect.height = xim->height;
365
366 XShmReadDisplayRects(dpy, wnd,
367 &rect, 1, readDisplayBuf, -x, -y,
368 hints, &hints);
369}
370
371void IrixOverlayShmImage::get(Window wnd, int x, int y, int w, int h)
372{
373 // FIXME: Use XReadDisplay extension here as well!
374 XGetSubImage(dpy, wnd, x, y, w, h, AllPlanes, ZPixmap, xim, 0, 0);
375}
376
377#endif // HAVE_READDISPLAY
378#endif // HAVE_MITSHM
379
380#ifdef HAVE_SUN_OVL
381
382//
383// SolarisOverlayImage class implementation
384//
385
386SolarisOverlayImage::SolarisOverlayImage(Display *d)
387 : Image(d)
388{
389}
390
391SolarisOverlayImage::SolarisOverlayImage(Display *d, int width, int height)
392 : Image(d)
393{
394 Init(width, height);
395}
396
397void SolarisOverlayImage::Init(int width, int height)
398{
399 // FIXME: Check if the extension is available at run time.
400 // FIXME: Maybe just read a small (e.g. 8x8) screen area then
401 // reallocate xim->data[] and correct width and height?
402 xim = XReadScreen(dpy, DefaultRootWindow(dpy), 0, 0, width, height, True);
403 if (xim == NULL) {
404 fprintf(stderr, "XReadScreen() failed\n");
405 return;
406 }
407}
408
409SolarisOverlayImage::~SolarisOverlayImage()
410{
411 // DEBUG:
412 // fprintf(stderr, "~SolarisOverlayImage() called\n");
413}
414
415void SolarisOverlayImage::get(Window wnd, int x, int y)
416{
417 get(wnd, x, y, xim->width, xim->height);
418}
419
420void SolarisOverlayImage::get(Window wnd, int x, int y, int w, int h)
421{
422 XImage *tmp_xim = XReadScreen(dpy, wnd, x, y, w, h, True);
423 if (tmp_xim == NULL)
424 return;
425
426 updateRect(tmp_xim, 0, 0);
427
428 XDestroyImage(tmp_xim);
429}
430
431#endif // HAVE_SUN_OVL
432
433//
434// ImageFactory class implementation
435//
436
437// Prepare useful shortcuts for compile-time options.
438#if defined(HAVE_READDISPLAY) && defined(HAVE_MITSHM)
439#define HAVE_SHM_READDISPLAY
440#endif
441#if defined(HAVE_SHM_READDISPLAY) || defined(HAVE_SUN_OVL)
442#define HAVE_OVERLAY_EXT
443#endif
444
445ImageFactory::ImageFactory(bool allowShm, bool allowOverlay)
446 : mayUseShm(allowShm), mayUseOverlay(allowOverlay)
447{
448}
449
450ImageFactory::~ImageFactory()
451{
452}
453
454Image *ImageFactory::newImage(Display *d, int width, int height)
455{
456 Image *image = NULL;
457
458 // First, try to create an image with overlay support.
459
460#ifdef HAVE_OVERLAY_EXT
461 if (mayUseOverlay) {
462#if defined(HAVE_SHM_READDISPLAY)
463 if (mayUseShm) {
464 image = new IrixOverlayShmImage(d, width, height);
465 if (image->xim != NULL) {
466 fprintf(stderr, "Using IRIX overlay image with SHM support\n");
467 return image;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000468 }
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000469 }
470#elif defined(HAVE_SUN_OVL)
471 image = new SolarisOverlayImage(d, width, height);
472 if (image->xim != NULL) {
473 fprintf(stderr, "Using Solaris overlay image\n");
474 return image;
475 }
476#endif
477 if (image != NULL) {
478 delete image;
479 fprintf(stderr,
480 "Failed to create overlay image, trying other options\n");
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000481 }
482 }
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000483#endif // HAVE_OVERLAY_EXT
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000484
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000485 // Now, try to use shared memory image.
486
487#ifdef HAVE_MITSHM
488 if (mayUseShm) {
489 image = new ShmImage(d, width, height);
490 if (image->xim != NULL) {
491 fprintf(stderr, "Using shared memory image\n");
492 return image;
493 }
494
495 delete image;
496 fprintf(stderr,
497 "Failed to create SHM image, falling back to Xlib image\n");
498 }
499#endif // HAVE_MITSHM
500
501 // Fall back to Xlib image.
502
503 fprintf(stderr, "Using Xlib-based image\n");
504 image = new Image(d, width, height);
505 return image;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000506}