blob: f998c6a5d46dca92c4fc2565e5140fcde70e1da7 [file] [log] [blame]
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +00001/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
2 * Copyright (C) 2004-2005 Constantin Kaplinsky. All Rights Reserved.
3 *
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
23#include <stdio.h>
Adam Tkac04b7fd22008-03-19 16:14:48 +000024#include <stdlib.h>
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000025#include <sys/types.h>
26
27#ifdef HAVE_MITSHM
28#include <sys/ipc.h>
29#include <sys/shm.h>
30#endif
31
32#include <rfb/LogWriter.h>
33#include <x0vncserver/Image.h>
34
35//
36// ImageCleanup is used to delete Image instances automatically on
37// program shutdown. This is important for shared memory images.
38//
39
40#include <list>
41
42class ImageCleanup {
43public:
44 std::list<Image *> images;
45
46 ~ImageCleanup()
47 {
48 while (!images.empty()) {
49 delete images.front();
50 }
51 }
52};
53
54ImageCleanup imageCleanup;
55
56//
57// Image class implementation.
58//
59
60static rfb::LogWriter vlog("Image");
61
62Image::Image(Display *d)
63 : xim(NULL), dpy(d), trueColor(true)
64{
65 imageCleanup.images.push_back(this);
66}
67
68Image::Image(Display *d, int width, int height)
69 : xim(NULL), dpy(d), trueColor(true)
70{
71 imageCleanup.images.push_back(this);
72 Init(width, height);
73}
74
75void Image::Init(int width, int height)
76{
77 Visual* vis = DefaultVisual(dpy, DefaultScreen(dpy));
78 trueColor = (vis->c_class == TrueColor);
79
80 xim = XCreateImage(dpy, vis, DefaultDepth(dpy, DefaultScreen(dpy)),
81 ZPixmap, 0, 0, width, height, BitmapPad(dpy), 0);
82
83 xim->data = (char *)malloc(xim->bytes_per_line * xim->height);
84 if (xim->data == NULL) {
85 vlog.error("malloc() failed");
86 exit(1);
87 }
88}
89
90Image::~Image()
91{
92 imageCleanup.images.remove(this);
93
94 // XDestroyImage will free xim->data if necessary
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
Constantin Kaplinskycffb6912007-09-03 10:17:19 +0000104void Image::get(Window wnd, int x, int y, int w, int h,
105 int dst_x, int dst_y)
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000106{
Constantin Kaplinskycffb6912007-09-03 10:17:19 +0000107 XGetSubImage(dpy, wnd, x, y, w, h, AllPlanes, ZPixmap, xim, dst_x, dst_y);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000108}
109
110//
111// Copying pixels from one image to another.
112//
113// FIXME: Use Point and Rect structures?
114// FIXME: Too many similar methods?
115//
116
117inline
118void Image::copyPixels(XImage *src,
119 int dst_x, int dst_y,
120 int src_x, int src_y,
121 int w, int h)
122{
123 const char *srcOffset =
124 src->data + (src_y * src->bytes_per_line +
125 src_x * (src->bits_per_pixel / 8));
126 char *dstOffset =
127 xim->data + (dst_y * xim->bytes_per_line +
128 dst_x * (xim->bits_per_pixel / 8));
129
130 int rowLength = w * (xim->bits_per_pixel / 8);
131
132 for (int i = 0; i < h ; i++) {
133 memcpy(dstOffset, srcOffset, rowLength);
134 srcOffset += src->bytes_per_line;
135 dstOffset += xim->bytes_per_line;
136 }
137}
138
139void Image::updateRect(XImage *src, int dst_x, int dst_y)
140{
141 // Limit width and height at destination image size.
142 int w = src->width;
143 if (dst_x + w > xim->width)
144 w = xim->width - dst_x;
145 int h = src->height;
146 if (dst_y + h > xim->height)
147 h = xim->height - dst_y;
148
149 copyPixels(src, dst_x, dst_y, 0, 0, w, h);
150}
151
152void Image::updateRect(Image *src, int dst_x, int dst_y)
153{
154 updateRect(src->xim, dst_x, dst_y);
155}
156
157void Image::updateRect(XImage *src, int dst_x, int dst_y, int w, int h)
158{
159 // Correct width and height if necessary.
160 if (w > src->width)
161 w = src->width;
162 if (dst_x + w > xim->width)
163 w = xim->width - dst_x;
164 if (h > src->height)
165 h = src->height;
166 if (dst_y + h > xim->height)
167 h = xim->height - dst_y;
168
169 copyPixels(src, dst_x, dst_y, 0, 0, w, h);
170}
171
172void Image::updateRect(Image *src, int dst_x, int dst_y, int w, int h)
173{
174 updateRect(src->xim, dst_x, dst_y, w, h);
175}
176
177void Image::updateRect(XImage *src, int dst_x, int dst_y,
178 int src_x, int src_y, int w, int h)
179{
180 // Correct width and height if necessary.
181 if (src_x + w > src->width)
182 w = src->width - src_x;
183 if (dst_x + w > xim->width)
184 w = xim->width - dst_x;
185 if (src_y + h > src->height)
186 h = src->height - src_y;
187 if (dst_y + h > xim->height)
188 h = xim->height - dst_y;
189
190 copyPixels(src, dst_x, dst_y, src_x, src_y, w, h);
191}
192
193void Image::updateRect(Image *src, int dst_x, int dst_y,
194 int src_x, int src_y, int w, int h)
195{
196 updateRect(src->xim, dst_x, dst_y, src_x, src_y, w, h);
197}
198
199#ifdef HAVE_MITSHM
200
201//
202// ShmImage class implementation.
203//
204
205static bool caughtShmError = false;
206
207static int ShmCreationXErrorHandler(Display *dpy, XErrorEvent *error)
208{
209 caughtShmError = true;
210 return 0;
211}
212
213ShmImage::ShmImage(Display *d)
214 : Image(d), shminfo(NULL)
215{
216}
217
218ShmImage::ShmImage(Display *d, int width, int height)
219 : Image(d), shminfo(NULL)
220{
221 Init(width, height);
222}
223
224// FIXME: Remove duplication of cleanup operations.
225void ShmImage::Init(int width, int height, const XVisualInfo *vinfo)
226{
227 int major, minor;
228 Bool pixmaps;
229
230 if (!XShmQueryVersion(dpy, &major, &minor, &pixmaps)) {
231 vlog.error("XShmQueryVersion() failed");
232 return;
233 }
234
235 Visual *visual;
236 int depth;
237
238 if (vinfo == NULL) {
239 visual = DefaultVisual(dpy, DefaultScreen(dpy));
240 depth = DefaultDepth(dpy, DefaultScreen(dpy));
241 } else {
242 visual = vinfo->visual;
243 depth = vinfo->depth;
244 }
245
246 trueColor = (visual->c_class == TrueColor);
247
248 shminfo = new XShmSegmentInfo;
249
250 xim = XShmCreateImage(dpy, visual, depth, ZPixmap, 0, shminfo,
251 width, height);
252 if (xim == NULL) {
253 vlog.error("XShmCreateImage() failed");
254 delete shminfo;
255 shminfo = NULL;
256 return;
257 }
258
259 shminfo->shmid = shmget(IPC_PRIVATE,
260 xim->bytes_per_line * xim->height,
261 IPC_CREAT|0777);
262 if (shminfo->shmid == -1) {
263 perror("shmget");
264 vlog.error("shmget() failed (%d bytes requested)",
265 int(xim->bytes_per_line * xim->height));
266 XDestroyImage(xim);
267 xim = NULL;
268 delete shminfo;
269 shminfo = NULL;
270 return;
271 }
272
273 shminfo->shmaddr = xim->data = (char *)shmat(shminfo->shmid, 0, 0);
274 if (shminfo->shmaddr == (char *)-1) {
275 perror("shmat");
276 vlog.error("shmat() failed (%d bytes requested)",
277 int(xim->bytes_per_line * xim->height));
278 shmctl(shminfo->shmid, IPC_RMID, 0);
279 XDestroyImage(xim);
280 xim = NULL;
281 delete shminfo;
282 shminfo = NULL;
283 return;
284 }
285
286 shminfo->readOnly = False;
287
288 XErrorHandler oldHdlr = XSetErrorHandler(ShmCreationXErrorHandler);
289 XShmAttach(dpy, shminfo);
290 XSync(dpy, False);
291 XSetErrorHandler(oldHdlr);
292 if (caughtShmError) {
293 vlog.error("XShmAttach() failed");
294 shmdt(shminfo->shmaddr);
295 shmctl(shminfo->shmid, IPC_RMID, 0);
296 XDestroyImage(xim);
297 xim = NULL;
298 delete shminfo;
299 shminfo = NULL;
300 return;
301 }
302}
303
304ShmImage::~ShmImage()
305{
306 // FIXME: Destroy image as described in MIT-SHM documentation.
307 if (shminfo != NULL) {
308 shmdt(shminfo->shmaddr);
309 shmctl(shminfo->shmid, IPC_RMID, 0);
310 delete shminfo;
311 }
312}
313
314void ShmImage::get(Window wnd, int x, int y)
315{
316 XShmGetImage(dpy, wnd, xim, x, y, AllPlanes);
317}
318
Constantin Kaplinskycffb6912007-09-03 10:17:19 +0000319void ShmImage::get(Window wnd, int x, int y, int w, int h,
320 int dst_x, int dst_y)
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000321{
322 // FIXME: Use SHM for this as well?
Constantin Kaplinskycffb6912007-09-03 10:17:19 +0000323 XGetSubImage(dpy, wnd, x, y, w, h, AllPlanes, ZPixmap, xim, dst_x, dst_y);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000324}
325
326#ifdef HAVE_READDISPLAY
327
328//
329// IrixOverlayShmImage class implementation.
330//
331
332IrixOverlayShmImage::IrixOverlayShmImage(Display *d)
333 : ShmImage(d), readDisplayBuf(NULL)
334{
335}
336
337IrixOverlayShmImage::IrixOverlayShmImage(Display *d, int width, int height)
338 : ShmImage(d), readDisplayBuf(NULL)
339{
340 Init(width, height);
341}
342
343void IrixOverlayShmImage::Init(int width, int height)
344{
345 // First determine the pixel format used by XReadDisplay.
346 XVisualInfo vinfo;
347 if (!getOverlayVisualInfo(&vinfo))
348 return;
349
350 // Create an SHM image of the same format.
351 ShmImage::Init(width, height, &vinfo);
352 if (xim == NULL)
353 return;
354
355 // FIXME: Check if the extension is available at run time.
356 readDisplayBuf = XShmCreateReadDisplayBuf(dpy, NULL, shminfo, width, height);
357}
358
359bool IrixOverlayShmImage::getOverlayVisualInfo(XVisualInfo *vinfo_ret)
360{
361 // First, get an image in the format returned by XReadDisplay.
362 unsigned long hints = 0, hints_ret;
363 XImage *testImage = XReadDisplay(dpy, DefaultRootWindow(dpy),
364 0, 0, 8, 8, hints, &hints_ret);
365 if (testImage == NULL)
366 return false;
367
368 // Fill in a template for matching visuals.
369 XVisualInfo tmpl;
370 tmpl.c_class = TrueColor;
371 tmpl.depth = 24;
372 tmpl.red_mask = testImage->red_mask;
373 tmpl.green_mask = testImage->green_mask;
374 tmpl.blue_mask = testImage->blue_mask;
375
376 // List fields in template that make sense.
377 long mask = (VisualClassMask |
378 VisualRedMaskMask |
379 VisualGreenMaskMask |
380 VisualBlueMaskMask);
381
382 // We don't need that image any more.
383 XDestroyImage(testImage);
384
385 // Now, get a list of matching visuals available.
386 int nVisuals;
387 XVisualInfo *vinfo = XGetVisualInfo(dpy, mask, &tmpl, &nVisuals);
388 if (vinfo == NULL || nVisuals <= 0) {
389 if (vinfo != NULL) {
390 XFree(vinfo);
391 }
392 return false;
393 }
394
395 // Use first visual from the list.
396 *vinfo_ret = vinfo[0];
397
398 XFree(vinfo);
399
400 return true;
401}
402
403IrixOverlayShmImage::~IrixOverlayShmImage()
404{
405 if (readDisplayBuf != NULL)
406 XShmDestroyReadDisplayBuf(readDisplayBuf);
407}
408
409void IrixOverlayShmImage::get(Window wnd, int x, int y)
410{
411 get(wnd, x, y, xim->width, xim->height);
412}
413
Constantin Kaplinskycffb6912007-09-03 10:17:19 +0000414void IrixOverlayShmImage::get(Window wnd, int x, int y, int w, int h,
415 int dst_x, int dst_y)
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000416{
417 XRectangle rect;
418 unsigned long hints = XRD_TRANSPARENT | XRD_READ_POINTER;
419
420 rect.x = x;
421 rect.y = y;
422 rect.width = w;
423 rect.height = h;
424
425 XShmReadDisplayRects(dpy, wnd,
Constantin Kaplinskycffb6912007-09-03 10:17:19 +0000426 &rect, 1, readDisplayBuf,
427 dst_x - x, dst_y - y,
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000428 hints, &hints);
429}
430
431#endif // HAVE_READDISPLAY
432#endif // HAVE_MITSHM
433
434#ifdef HAVE_SUN_OVL
435
436//
437// SolarisOverlayImage class implementation
438//
439
440SolarisOverlayImage::SolarisOverlayImage(Display *d)
441 : Image(d)
442{
443}
444
445SolarisOverlayImage::SolarisOverlayImage(Display *d, int width, int height)
446 : Image(d)
447{
448 Init(width, height);
449}
450
451void SolarisOverlayImage::Init(int width, int height)
452{
453 // FIXME: Check if the extension is available at run time.
454 // FIXME: Maybe just read a small (e.g. 8x8) screen area then
455 // reallocate xim->data[] and correct width and height?
456 xim = XReadScreen(dpy, DefaultRootWindow(dpy), 0, 0, width, height, True);
457 if (xim == NULL) {
458 vlog.error("XReadScreen() failed");
459 return;
460 }
461}
462
463SolarisOverlayImage::~SolarisOverlayImage()
464{
465}
466
467void SolarisOverlayImage::get(Window wnd, int x, int y)
468{
469 get(wnd, x, y, xim->width, xim->height);
470}
471
Constantin Kaplinskycffb6912007-09-03 10:17:19 +0000472void SolarisOverlayImage::get(Window wnd, int x, int y, int w, int h,
473 int dst_x, int dst_y)
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000474{
475 XImage *tmp_xim = XReadScreen(dpy, wnd, x, y, w, h, True);
476 if (tmp_xim == NULL)
477 return;
478
Constantin Kaplinskycffb6912007-09-03 10:17:19 +0000479 updateRect(tmp_xim, dst_x, dst_y);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000480
481 XDestroyImage(tmp_xim);
482}
483
484#endif // HAVE_SUN_OVL
485
486//
487// ImageFactory class implementation
488//
489// FIXME: Make ImageFactory always create images of the same class?
490//
491
492// Prepare useful shortcuts for compile-time options.
493#if defined(HAVE_READDISPLAY) && defined(HAVE_MITSHM)
494#define HAVE_SHM_READDISPLAY
495#endif
496#if defined(HAVE_SHM_READDISPLAY) || defined(HAVE_SUN_OVL)
497#define HAVE_OVERLAY_EXT
498#endif
499
500ImageFactory::ImageFactory(bool allowShm, bool allowOverlay)
501 : mayUseShm(allowShm), mayUseOverlay(allowOverlay)
502{
503}
504
505ImageFactory::~ImageFactory()
506{
507}
508
509Image *ImageFactory::newImage(Display *d, int width, int height)
510{
511 Image *image = NULL;
512
513 // First, try to create an image with overlay support.
514
515#ifdef HAVE_OVERLAY_EXT
516 if (mayUseOverlay) {
517#if defined(HAVE_SHM_READDISPLAY)
518 if (mayUseShm) {
519 image = new IrixOverlayShmImage(d, width, height);
520 if (image->xim != NULL) {
521 return image;
522 }
523 }
524#elif defined(HAVE_SUN_OVL)
525 image = new SolarisOverlayImage(d, width, height);
526 if (image->xim != NULL) {
527 return image;
528 }
529#endif
530 if (image != NULL) {
531 delete image;
532 vlog.error("Failed to create overlay image, trying other options");
533 }
534 }
535#endif // HAVE_OVERLAY_EXT
536
537 // Now, try to use shared memory image.
538
539#ifdef HAVE_MITSHM
540 if (mayUseShm) {
541 image = new ShmImage(d, width, height);
542 if (image->xim != NULL) {
543 return image;
544 }
545
546 delete image;
547 vlog.error("Failed to create SHM image, falling back to Xlib image");
548 }
549#endif // HAVE_MITSHM
550
551 // Fall back to Xlib image.
552
553 image = new Image(d, width, height);
554 return image;
555}