blob: d78a98e1e99edfc06467dbb796aa52ff0259e03b [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 Kaplinskyaf1891c2005-09-29 06:18:28 +000031#include <x0vncserver/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);
Constantin Kaplinskya1d2cef2006-04-17 08:46:22 +000095
96 // XDestroyImage will free xim->data if necessary
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +000097 if (xim != NULL)
98 XDestroyImage(xim);
99}
100
101void Image::get(Window wnd, int x, int y)
102{
103 get(wnd, x, y, xim->width, xim->height);
104}
105
106void Image::get(Window wnd, int x, int y, int w, int h)
107{
108 XGetSubImage(dpy, wnd, x, y, w, h, AllPlanes, ZPixmap, xim, 0, 0);
109}
110
Constantin Kaplinskyce676c62006-02-08 13:36:58 +0000111//
112// Copying pixels from one image to another.
113//
114// FIXME: Use Point and Rect structures?
115// FIXME: Too many similar methods?
116//
117
118inline
119void Image::copyPixels(XImage *src,
120 int dst_x, int dst_y,
121 int src_x, int src_y,
122 int w, int h)
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000123{
Constantin Kaplinskyce676c62006-02-08 13:36:58 +0000124 const char *srcOffset =
125 src->data + (src_y * src->bytes_per_line +
126 src_x * (src->bits_per_pixel / 8));
127 char *dstOffset =
128 xim->data + (dst_y * xim->bytes_per_line +
129 dst_x * (xim->bits_per_pixel / 8));
130
131 int rowLength = w * (xim->bits_per_pixel / 8);
132
133 for (int i = 0; i < h ; i++) {
134 memcpy(dstOffset, srcOffset, rowLength);
135 srcOffset += src->bytes_per_line;
136 dstOffset += xim->bytes_per_line;
137 }
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000138}
139
140void Image::updateRect(XImage *src, int dst_x, int dst_y)
141{
Constantin Kaplinskyce676c62006-02-08 13:36:58 +0000142 // Limit width and height at destination image size.
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000143 int w = src->width;
144 if (dst_x + w > xim->width)
145 w = xim->width - dst_x;
146 int h = src->height;
147 if (dst_y + h > xim->height)
148 h = xim->height - dst_y;
149
Constantin Kaplinskyce676c62006-02-08 13:36:58 +0000150 copyPixels(src, dst_x, dst_y, 0, 0, w, h);
151}
152
153void Image::updateRect(Image *src, int dst_x, int dst_y)
154{
155 updateRect(src->xim, dst_x, dst_y);
156}
157
158void Image::updateRect(XImage *src, int dst_x, int dst_y, int w, int h)
159{
160 // Correct width and height if necessary.
161 if (w > src->width)
162 w = src->width;
163 if (dst_x + w > xim->width)
164 w = xim->width - dst_x;
165 if (h > src->height)
166 h = src->height;
167 if (dst_y + h > xim->height)
168 h = xim->height - dst_y;
169
170 copyPixels(src, dst_x, dst_y, 0, 0, w, h);
171}
172
173void Image::updateRect(Image *src, int dst_x, int dst_y, int w, int h)
174{
175 updateRect(src->xim, dst_x, dst_y, w, h);
176}
177
178void Image::updateRect(XImage *src, int dst_x, int dst_y,
179 int src_x, int src_y, int w, int h)
180{
181 // Correct width and height if necessary.
182 if (src_x + w > src->width)
183 w = src->width - src_x;
184 if (dst_x + w > xim->width)
185 w = xim->width - dst_x;
186 if (src_y + h > src->height)
187 h = src->height - src_y;
188 if (dst_y + h > xim->height)
189 h = xim->height - dst_y;
190
191 copyPixels(src, dst_x, dst_y, src_x, src_y, w, h);
192}
193
194void Image::updateRect(Image *src, int dst_x, int dst_y,
195 int src_x, int src_y, int w, int h)
196{
197 updateRect(src->xim, dst_x, dst_y, src_x, src_y, w, h);
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000198}
199
200#ifdef HAVE_MITSHM
201
202//
203// ShmImage class implementation.
204//
205
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000206static bool caughtShmError = false;
207
208static int ShmCreationXErrorHandler(Display *dpy, XErrorEvent *error)
209{
210 caughtShmError = true;
211 return 0;
212}
213
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000214ShmImage::ShmImage(Display *d)
215 : Image(d), shminfo(NULL)
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000216{
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000217}
218
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000219ShmImage::ShmImage(Display *d, int width, int height)
220 : Image(d), shminfo(NULL)
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000221{
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000222 Init(width, height);
223}
224
225// FIXME: Remove duplication of cleanup operations.
226void ShmImage::Init(int width, int height, const XVisualInfo *vinfo)
227{
228 int major, minor;
229 Bool pixmaps;
230
231 if (!XShmQueryVersion(dpy, &major, &minor, &pixmaps)) {
232 fprintf(stderr, "XShmQueryVersion() failed\n");
233 return;
234 }
235
236 Visual *visual;
237 int depth;
238
239 if (vinfo == NULL) {
240 visual = DefaultVisual(dpy, DefaultScreen(dpy));
241 depth = DefaultDepth(dpy, DefaultScreen(dpy));
242 } else {
243 visual = vinfo->visual;
244 depth = vinfo->depth;
245 }
246
247 trueColor = (visual->c_class == TrueColor);
248
249 shminfo = new XShmSegmentInfo;
250
251 xim = XShmCreateImage(dpy, visual, depth, ZPixmap, 0, shminfo,
252 width, height);
253 if (xim == NULL) {
254 fprintf(stderr, "XShmCreateImage() failed\n");
255 delete shminfo;
256 shminfo = NULL;
257 return;
258 }
259
260 shminfo->shmid = shmget(IPC_PRIVATE,
261 xim->bytes_per_line * xim->height,
262 IPC_CREAT|0777);
263 if (shminfo->shmid == -1) {
264 perror("shmget");
265 fprintf(stderr,
266 "shmget() failed (%d bytes requested)\n",
267 int(xim->bytes_per_line * xim->height));
268 XDestroyImage(xim);
269 xim = NULL;
270 delete shminfo;
271 shminfo = NULL;
272 return;
273 }
274
275 shminfo->shmaddr = xim->data = (char *)shmat(shminfo->shmid, 0, 0);
276 if (shminfo->shmaddr == (char *)-1) {
277 perror("shmat");
278 fprintf(stderr,
279 "shmat() failed (%d bytes requested)\n",
280 int(xim->bytes_per_line * xim->height));
281 shmctl(shminfo->shmid, IPC_RMID, 0);
282 XDestroyImage(xim);
283 xim = NULL;
284 delete shminfo;
285 shminfo = NULL;
286 return;
287 }
288
289 shminfo->readOnly = False;
290
291 XErrorHandler oldHdlr = XSetErrorHandler(ShmCreationXErrorHandler);
292 XShmAttach(dpy, shminfo);
293 XSync(dpy, False);
294 XSetErrorHandler(oldHdlr);
295 if (caughtShmError) {
296 fprintf(stderr, "XShmAttach() failed\n");
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000297 shmdt(shminfo->shmaddr);
298 shmctl(shminfo->shmid, IPC_RMID, 0);
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000299 XDestroyImage(xim);
300 xim = NULL;
301 delete shminfo;
302 shminfo = NULL;
303 return;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000304 }
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000305
306 // DEBUG:
307 // fprintf(stderr,
308 // "Using shared memory XImage (%d bytes image data)\n",
309 // int(xim->bytes_per_line * xim->height));
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000310}
311
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000312ShmImage::~ShmImage()
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000313{
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000314 // DEBUG:
315 // fprintf(stderr,"~ShmImage called\n");
316
317 // FIXME: Destroy image as described in MIT-SHM documentation.
318 if (shminfo != NULL) {
319 shmdt(shminfo->shmaddr);
320 shmctl(shminfo->shmid, IPC_RMID, 0);
321 delete shminfo;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000322 }
323}
324
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000325void ShmImage::get(Window wnd, int x, int y)
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000326{
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000327 XShmGetImage(dpy, wnd, xim, x, y, AllPlanes);
328}
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000329
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000330void ShmImage::get(Window wnd, int x, int y, int w, int h)
331{
332 // FIXME: Use SHM for this as well?
333 XGetSubImage(dpy, wnd, x, y, w, h, AllPlanes, ZPixmap, xim, 0, 0);
334}
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000335
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000336#ifdef HAVE_READDISPLAY
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000337
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000338//
339// IrixOverlayShmImage class implementation.
340//
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000341
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000342IrixOverlayShmImage::IrixOverlayShmImage(Display *d)
343 : ShmImage(d), readDisplayBuf(NULL)
344{
345}
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000346
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000347IrixOverlayShmImage::IrixOverlayShmImage(Display *d, int width, int height)
348 : ShmImage(d), readDisplayBuf(NULL)
349{
350 Init(width, height);
351}
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000352
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000353void IrixOverlayShmImage::Init(int width, int height)
354{
355 // First determine the pixel format used by XReadDisplay.
356 XVisualInfo vinfo;
357 if (!getOverlayVisualInfo(&vinfo))
358 return;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000359
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000360 // Create an SHM image of the same format.
361 ShmImage::Init(width, height, &vinfo);
362 if (xim == NULL)
363 return;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000364
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000365 // FIXME: Check if the extension is available at run time.
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000366 readDisplayBuf = XShmCreateReadDisplayBuf(dpy, NULL, shminfo, width, height);
367}
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000368
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000369bool IrixOverlayShmImage::getOverlayVisualInfo(XVisualInfo *vinfo_ret)
370{
371 // First, get an image in the format returned by XReadDisplay.
372 unsigned long hints = 0, hints_ret;
373 XImage *testImage = XReadDisplay(dpy, DefaultRootWindow(dpy),
374 0, 0, 8, 8, hints, &hints_ret);
375 if (testImage == NULL)
376 return false;
377
378 // Fill in a template for matching visuals.
379 XVisualInfo tmpl;
380 tmpl.c_class = TrueColor;
381 tmpl.depth = 24;
382 tmpl.red_mask = testImage->red_mask;
383 tmpl.green_mask = testImage->green_mask;
384 tmpl.blue_mask = testImage->blue_mask;
385
386 // List fields in template that make sense.
387 long mask = (VisualClassMask |
388 VisualRedMaskMask |
389 VisualGreenMaskMask |
390 VisualBlueMaskMask);
391
392 // We don't need that image any more.
393 XDestroyImage(testImage);
394
395 // Now, get a list of matching visuals available.
396 int nVisuals;
397 XVisualInfo *vinfo = XGetVisualInfo(dpy, mask, &tmpl, &nVisuals);
398 if (vinfo == NULL || nVisuals <= 0) {
399 if (vinfo != NULL) {
400 XFree(vinfo);
401 }
402 return false;
403 }
404
405 // Use first visual from the list.
406 *vinfo_ret = vinfo[0];
407
408 XFree(vinfo);
409
410 return true;
411}
412
413IrixOverlayShmImage::~IrixOverlayShmImage()
414{
415 // DEBUG:
416 // fprintf(stderr,"~IrixOverlayShmImage called\n");
Constantin Kaplinskyedb5b242006-03-06 05:24:45 +0000417
418 if (readDisplayBuf != NULL)
419 XShmDestroyReadDisplayBuf(readDisplayBuf);
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000420}
421
422void IrixOverlayShmImage::get(Window wnd, int x, int y)
423{
Constantin Kaplinskye2dc5282006-03-06 05:12:32 +0000424 get(wnd, x, y, xim->width, xim->height);
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000425}
426
427void IrixOverlayShmImage::get(Window wnd, int x, int y, int w, int h)
428{
Constantin Kaplinskye2dc5282006-03-06 05:12:32 +0000429 XRectangle rect;
430 unsigned long hints = XRD_TRANSPARENT | XRD_READ_POINTER;
431
432 rect.x = x;
433 rect.y = y;
434 rect.width = w;
435 rect.height = h;
436
437 XShmReadDisplayRects(dpy, wnd,
438 &rect, 1, readDisplayBuf, -x, -y,
439 hints, &hints);
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000440}
441
442#endif // HAVE_READDISPLAY
443#endif // HAVE_MITSHM
444
445#ifdef HAVE_SUN_OVL
446
447//
448// SolarisOverlayImage class implementation
449//
450
451SolarisOverlayImage::SolarisOverlayImage(Display *d)
452 : Image(d)
453{
454}
455
456SolarisOverlayImage::SolarisOverlayImage(Display *d, int width, int height)
457 : Image(d)
458{
459 Init(width, height);
460}
461
462void SolarisOverlayImage::Init(int width, int height)
463{
464 // FIXME: Check if the extension is available at run time.
465 // FIXME: Maybe just read a small (e.g. 8x8) screen area then
466 // reallocate xim->data[] and correct width and height?
467 xim = XReadScreen(dpy, DefaultRootWindow(dpy), 0, 0, width, height, True);
468 if (xim == NULL) {
469 fprintf(stderr, "XReadScreen() failed\n");
470 return;
471 }
472}
473
474SolarisOverlayImage::~SolarisOverlayImage()
475{
476 // DEBUG:
477 // fprintf(stderr, "~SolarisOverlayImage() called\n");
478}
479
480void SolarisOverlayImage::get(Window wnd, int x, int y)
481{
482 get(wnd, x, y, xim->width, xim->height);
483}
484
485void SolarisOverlayImage::get(Window wnd, int x, int y, int w, int h)
486{
487 XImage *tmp_xim = XReadScreen(dpy, wnd, x, y, w, h, True);
488 if (tmp_xim == NULL)
489 return;
490
491 updateRect(tmp_xim, 0, 0);
492
493 XDestroyImage(tmp_xim);
494}
495
496#endif // HAVE_SUN_OVL
497
498//
499// ImageFactory class implementation
500//
Constantin Kaplinskyce676c62006-02-08 13:36:58 +0000501// FIXME: Make ImageFactory always create images of the same class?
502//
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000503
504// Prepare useful shortcuts for compile-time options.
505#if defined(HAVE_READDISPLAY) && defined(HAVE_MITSHM)
506#define HAVE_SHM_READDISPLAY
507#endif
508#if defined(HAVE_SHM_READDISPLAY) || defined(HAVE_SUN_OVL)
509#define HAVE_OVERLAY_EXT
510#endif
511
512ImageFactory::ImageFactory(bool allowShm, bool allowOverlay)
513 : mayUseShm(allowShm), mayUseOverlay(allowOverlay)
514{
515}
516
517ImageFactory::~ImageFactory()
518{
519}
520
521Image *ImageFactory::newImage(Display *d, int width, int height)
522{
523 Image *image = NULL;
524
525 // First, try to create an image with overlay support.
Constantin Kaplinskyce676c62006-02-08 13:36:58 +0000526 // FIXME: Replace fprintf() with proper logging.
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000527
528#ifdef HAVE_OVERLAY_EXT
529 if (mayUseOverlay) {
530#if defined(HAVE_SHM_READDISPLAY)
531 if (mayUseShm) {
532 image = new IrixOverlayShmImage(d, width, height);
533 if (image->xim != NULL) {
534 fprintf(stderr, "Using IRIX overlay image with SHM support\n");
535 return image;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000536 }
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000537 }
538#elif defined(HAVE_SUN_OVL)
539 image = new SolarisOverlayImage(d, width, height);
540 if (image->xim != NULL) {
541 fprintf(stderr, "Using Solaris overlay image\n");
542 return image;
543 }
544#endif
545 if (image != NULL) {
546 delete image;
547 fprintf(stderr,
548 "Failed to create overlay image, trying other options\n");
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000549 }
550 }
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000551#endif // HAVE_OVERLAY_EXT
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000552
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000553 // Now, try to use shared memory image.
554
555#ifdef HAVE_MITSHM
556 if (mayUseShm) {
557 image = new ShmImage(d, width, height);
558 if (image->xim != NULL) {
559 fprintf(stderr, "Using shared memory image\n");
560 return image;
561 }
562
563 delete image;
564 fprintf(stderr,
565 "Failed to create SHM image, falling back to Xlib image\n");
566 }
567#endif // HAVE_MITSHM
568
569 // Fall back to Xlib image.
570
571 fprintf(stderr, "Using Xlib-based image\n");
572 image = new Image(d, width, height);
573 return image;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000574}