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