blob: 475cee19ee283342df63291c0cc59556ca40604f [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);
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
Constantin Kaplinskyce676c62006-02-08 13:36:58 +0000109//
110// Copying pixels from one image to another.
111//
112// FIXME: Use Point and Rect structures?
113// FIXME: Too many similar methods?
114//
115
116inline
117void Image::copyPixels(XImage *src,
118 int dst_x, int dst_y,
119 int src_x, int src_y,
120 int w, int h)
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000121{
Constantin Kaplinskyce676c62006-02-08 13:36:58 +0000122 const char *srcOffset =
123 src->data + (src_y * src->bytes_per_line +
124 src_x * (src->bits_per_pixel / 8));
125 char *dstOffset =
126 xim->data + (dst_y * xim->bytes_per_line +
127 dst_x * (xim->bits_per_pixel / 8));
128
129 int rowLength = w * (xim->bits_per_pixel / 8);
130
131 for (int i = 0; i < h ; i++) {
132 memcpy(dstOffset, srcOffset, rowLength);
133 srcOffset += src->bytes_per_line;
134 dstOffset += xim->bytes_per_line;
135 }
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000136}
137
138void Image::updateRect(XImage *src, int dst_x, int dst_y)
139{
Constantin Kaplinskyce676c62006-02-08 13:36:58 +0000140 // Limit width and height at destination image size.
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000141 int w = src->width;
142 if (dst_x + w > xim->width)
143 w = xim->width - dst_x;
144 int h = src->height;
145 if (dst_y + h > xim->height)
146 h = xim->height - dst_y;
147
Constantin Kaplinskyce676c62006-02-08 13:36:58 +0000148 copyPixels(src, dst_x, dst_y, 0, 0, w, h);
149}
150
151void Image::updateRect(Image *src, int dst_x, int dst_y)
152{
153 updateRect(src->xim, dst_x, dst_y);
154}
155
156void Image::updateRect(XImage *src, int dst_x, int dst_y, int w, int h)
157{
158 // Correct width and height if necessary.
159 if (w > src->width)
160 w = src->width;
161 if (dst_x + w > xim->width)
162 w = xim->width - dst_x;
163 if (h > src->height)
164 h = src->height;
165 if (dst_y + h > xim->height)
166 h = xim->height - dst_y;
167
168 copyPixels(src, dst_x, dst_y, 0, 0, w, h);
169}
170
171void Image::updateRect(Image *src, int dst_x, int dst_y, int w, int h)
172{
173 updateRect(src->xim, dst_x, dst_y, w, h);
174}
175
176void Image::updateRect(XImage *src, int dst_x, int dst_y,
177 int src_x, int src_y, int w, int h)
178{
179 // Correct width and height if necessary.
180 if (src_x + w > src->width)
181 w = src->width - src_x;
182 if (dst_x + w > xim->width)
183 w = xim->width - dst_x;
184 if (src_y + h > src->height)
185 h = src->height - src_y;
186 if (dst_y + h > xim->height)
187 h = xim->height - dst_y;
188
189 copyPixels(src, dst_x, dst_y, src_x, src_y, w, h);
190}
191
192void Image::updateRect(Image *src, int dst_x, int dst_y,
193 int src_x, int src_y, int w, int h)
194{
195 updateRect(src->xim, dst_x, dst_y, src_x, src_y, w, h);
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000196}
197
198#ifdef HAVE_MITSHM
199
200//
201// ShmImage class implementation.
202//
203
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000204static bool caughtShmError = false;
205
206static int ShmCreationXErrorHandler(Display *dpy, XErrorEvent *error)
207{
208 caughtShmError = true;
209 return 0;
210}
211
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000212ShmImage::ShmImage(Display *d)
213 : Image(d), shminfo(NULL)
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000214{
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000215}
216
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000217ShmImage::ShmImage(Display *d, int width, int height)
218 : Image(d), shminfo(NULL)
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000219{
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000220 Init(width, height);
221}
222
223// FIXME: Remove duplication of cleanup operations.
224void ShmImage::Init(int width, int height, const XVisualInfo *vinfo)
225{
226 int major, minor;
227 Bool pixmaps;
228
229 if (!XShmQueryVersion(dpy, &major, &minor, &pixmaps)) {
230 fprintf(stderr, "XShmQueryVersion() failed\n");
231 return;
232 }
233
234 Visual *visual;
235 int depth;
236
237 if (vinfo == NULL) {
238 visual = DefaultVisual(dpy, DefaultScreen(dpy));
239 depth = DefaultDepth(dpy, DefaultScreen(dpy));
240 } else {
241 visual = vinfo->visual;
242 depth = vinfo->depth;
243 }
244
245 trueColor = (visual->c_class == TrueColor);
246
247 shminfo = new XShmSegmentInfo;
248
249 xim = XShmCreateImage(dpy, visual, depth, ZPixmap, 0, shminfo,
250 width, height);
251 if (xim == NULL) {
252 fprintf(stderr, "XShmCreateImage() failed\n");
253 delete shminfo;
254 shminfo = NULL;
255 return;
256 }
257
258 shminfo->shmid = shmget(IPC_PRIVATE,
259 xim->bytes_per_line * xim->height,
260 IPC_CREAT|0777);
261 if (shminfo->shmid == -1) {
262 perror("shmget");
263 fprintf(stderr,
264 "shmget() failed (%d bytes requested)\n",
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 fprintf(stderr,
277 "shmat() failed (%d bytes requested)\n",
278 int(xim->bytes_per_line * xim->height));
279 shmctl(shminfo->shmid, IPC_RMID, 0);
280 XDestroyImage(xim);
281 xim = NULL;
282 delete shminfo;
283 shminfo = NULL;
284 return;
285 }
286
287 shminfo->readOnly = False;
288
289 XErrorHandler oldHdlr = XSetErrorHandler(ShmCreationXErrorHandler);
290 XShmAttach(dpy, shminfo);
291 XSync(dpy, False);
292 XSetErrorHandler(oldHdlr);
293 if (caughtShmError) {
294 fprintf(stderr, "XShmAttach() failed\n");
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000295 shmdt(shminfo->shmaddr);
296 shmctl(shminfo->shmid, IPC_RMID, 0);
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000297 XDestroyImage(xim);
298 xim = NULL;
299 delete shminfo;
300 shminfo = NULL;
301 return;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000302 }
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000303
304 // DEBUG:
305 // fprintf(stderr,
306 // "Using shared memory XImage (%d bytes image data)\n",
307 // int(xim->bytes_per_line * xim->height));
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000308}
309
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000310ShmImage::~ShmImage()
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000311{
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000312 // DEBUG:
313 // fprintf(stderr,"~ShmImage called\n");
314
315 // FIXME: Destroy image as described in MIT-SHM documentation.
316 if (shminfo != NULL) {
317 shmdt(shminfo->shmaddr);
318 shmctl(shminfo->shmid, IPC_RMID, 0);
319 delete shminfo;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000320 }
321}
322
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000323void ShmImage::get(Window wnd, int x, int y)
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000324{
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000325 XShmGetImage(dpy, wnd, xim, x, y, AllPlanes);
326}
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000327
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000328void ShmImage::get(Window wnd, int x, int y, int w, int h)
329{
330 // FIXME: Use SHM for this as well?
331 XGetSubImage(dpy, wnd, x, y, w, h, AllPlanes, ZPixmap, xim, 0, 0);
332}
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000333
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000334#ifdef HAVE_READDISPLAY
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000335
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000336//
337// IrixOverlayShmImage class implementation.
338//
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000339
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000340IrixOverlayShmImage::IrixOverlayShmImage(Display *d)
341 : ShmImage(d), readDisplayBuf(NULL)
342{
343}
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000344
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000345IrixOverlayShmImage::IrixOverlayShmImage(Display *d, int width, int height)
346 : ShmImage(d), readDisplayBuf(NULL)
347{
348 Init(width, height);
349}
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000350
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000351void IrixOverlayShmImage::Init(int width, int height)
352{
353 // First determine the pixel format used by XReadDisplay.
354 XVisualInfo vinfo;
355 if (!getOverlayVisualInfo(&vinfo))
356 return;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000357
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000358 // Create an SHM image of the same format.
359 ShmImage::Init(width, height, &vinfo);
360 if (xim == NULL)
361 return;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000362
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000363 // FIXME: Check if the extension is available at run time.
364 // FIXME: Does XShmCreateReadDisplayBuf() require some cleanup?
365 readDisplayBuf = XShmCreateReadDisplayBuf(dpy, NULL, shminfo, width, height);
366}
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000367
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000368bool IrixOverlayShmImage::getOverlayVisualInfo(XVisualInfo *vinfo_ret)
369{
370 // First, get an image in the format returned by XReadDisplay.
371 unsigned long hints = 0, hints_ret;
372 XImage *testImage = XReadDisplay(dpy, DefaultRootWindow(dpy),
373 0, 0, 8, 8, hints, &hints_ret);
374 if (testImage == NULL)
375 return false;
376
377 // Fill in a template for matching visuals.
378 XVisualInfo tmpl;
379 tmpl.c_class = TrueColor;
380 tmpl.depth = 24;
381 tmpl.red_mask = testImage->red_mask;
382 tmpl.green_mask = testImage->green_mask;
383 tmpl.blue_mask = testImage->blue_mask;
384
385 // List fields in template that make sense.
386 long mask = (VisualClassMask |
387 VisualRedMaskMask |
388 VisualGreenMaskMask |
389 VisualBlueMaskMask);
390
391 // We don't need that image any more.
392 XDestroyImage(testImage);
393
394 // Now, get a list of matching visuals available.
395 int nVisuals;
396 XVisualInfo *vinfo = XGetVisualInfo(dpy, mask, &tmpl, &nVisuals);
397 if (vinfo == NULL || nVisuals <= 0) {
398 if (vinfo != NULL) {
399 XFree(vinfo);
400 }
401 return false;
402 }
403
404 // Use first visual from the list.
405 *vinfo_ret = vinfo[0];
406
407 XFree(vinfo);
408
409 return true;
410}
411
412IrixOverlayShmImage::~IrixOverlayShmImage()
413{
414 // DEBUG:
415 // fprintf(stderr,"~IrixOverlayShmImage called\n");
416}
417
418void IrixOverlayShmImage::get(Window wnd, int x, int y)
419{
Constantin Kaplinskye2dc5282006-03-06 05:12:32 +0000420 get(wnd, x, y, xim->width, xim->height);
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000421}
422
423void IrixOverlayShmImage::get(Window wnd, int x, int y, int w, int h)
424{
Constantin Kaplinskye2dc5282006-03-06 05:12:32 +0000425 XRectangle rect;
426 unsigned long hints = XRD_TRANSPARENT | XRD_READ_POINTER;
427
428 rect.x = x;
429 rect.y = y;
430 rect.width = w;
431 rect.height = h;
432
433 XShmReadDisplayRects(dpy, wnd,
434 &rect, 1, readDisplayBuf, -x, -y,
435 hints, &hints);
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000436}
437
438#endif // HAVE_READDISPLAY
439#endif // HAVE_MITSHM
440
441#ifdef HAVE_SUN_OVL
442
443//
444// SolarisOverlayImage class implementation
445//
446
447SolarisOverlayImage::SolarisOverlayImage(Display *d)
448 : Image(d)
449{
450}
451
452SolarisOverlayImage::SolarisOverlayImage(Display *d, int width, int height)
453 : Image(d)
454{
455 Init(width, height);
456}
457
458void SolarisOverlayImage::Init(int width, int height)
459{
460 // FIXME: Check if the extension is available at run time.
461 // FIXME: Maybe just read a small (e.g. 8x8) screen area then
462 // reallocate xim->data[] and correct width and height?
463 xim = XReadScreen(dpy, DefaultRootWindow(dpy), 0, 0, width, height, True);
464 if (xim == NULL) {
465 fprintf(stderr, "XReadScreen() failed\n");
466 return;
467 }
468}
469
470SolarisOverlayImage::~SolarisOverlayImage()
471{
472 // DEBUG:
473 // fprintf(stderr, "~SolarisOverlayImage() called\n");
474}
475
476void SolarisOverlayImage::get(Window wnd, int x, int y)
477{
478 get(wnd, x, y, xim->width, xim->height);
479}
480
481void SolarisOverlayImage::get(Window wnd, int x, int y, int w, int h)
482{
483 XImage *tmp_xim = XReadScreen(dpy, wnd, x, y, w, h, True);
484 if (tmp_xim == NULL)
485 return;
486
487 updateRect(tmp_xim, 0, 0);
488
489 XDestroyImage(tmp_xim);
490}
491
492#endif // HAVE_SUN_OVL
493
494//
495// ImageFactory class implementation
496//
Constantin Kaplinskyce676c62006-02-08 13:36:58 +0000497// FIXME: Make ImageFactory always create images of the same class?
498//
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000499
500// Prepare useful shortcuts for compile-time options.
501#if defined(HAVE_READDISPLAY) && defined(HAVE_MITSHM)
502#define HAVE_SHM_READDISPLAY
503#endif
504#if defined(HAVE_SHM_READDISPLAY) || defined(HAVE_SUN_OVL)
505#define HAVE_OVERLAY_EXT
506#endif
507
508ImageFactory::ImageFactory(bool allowShm, bool allowOverlay)
509 : mayUseShm(allowShm), mayUseOverlay(allowOverlay)
510{
511}
512
513ImageFactory::~ImageFactory()
514{
515}
516
517Image *ImageFactory::newImage(Display *d, int width, int height)
518{
519 Image *image = NULL;
520
521 // First, try to create an image with overlay support.
Constantin Kaplinskyce676c62006-02-08 13:36:58 +0000522 // FIXME: Replace fprintf() with proper logging.
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000523
524#ifdef HAVE_OVERLAY_EXT
525 if (mayUseOverlay) {
526#if defined(HAVE_SHM_READDISPLAY)
527 if (mayUseShm) {
528 image = new IrixOverlayShmImage(d, width, height);
529 if (image->xim != NULL) {
530 fprintf(stderr, "Using IRIX overlay image with SHM support\n");
531 return image;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000532 }
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000533 }
534#elif defined(HAVE_SUN_OVL)
535 image = new SolarisOverlayImage(d, width, height);
536 if (image->xim != NULL) {
537 fprintf(stderr, "Using Solaris overlay image\n");
538 return image;
539 }
540#endif
541 if (image != NULL) {
542 delete image;
543 fprintf(stderr,
544 "Failed to create overlay image, trying other options\n");
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000545 }
546 }
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000547#endif // HAVE_OVERLAY_EXT
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000548
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000549 // Now, try to use shared memory image.
550
551#ifdef HAVE_MITSHM
552 if (mayUseShm) {
553 image = new ShmImage(d, width, height);
554 if (image->xim != NULL) {
555 fprintf(stderr, "Using shared memory image\n");
556 return image;
557 }
558
559 delete image;
560 fprintf(stderr,
561 "Failed to create SHM image, falling back to Xlib image\n");
562 }
563#endif // HAVE_MITSHM
564
565 // Fall back to Xlib image.
566
567 fprintf(stderr, "Using Xlib-based image\n");
568 image = new Image(d, width, height);
569 return image;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000570}