blob: ef7237895af3d7572fa4226d86b3cf86e8fb6f5f [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.
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000364 readDisplayBuf = XShmCreateReadDisplayBuf(dpy, NULL, shminfo, width, height);
365}
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000366
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000367bool IrixOverlayShmImage::getOverlayVisualInfo(XVisualInfo *vinfo_ret)
368{
369 // First, get an image in the format returned by XReadDisplay.
370 unsigned long hints = 0, hints_ret;
371 XImage *testImage = XReadDisplay(dpy, DefaultRootWindow(dpy),
372 0, 0, 8, 8, hints, &hints_ret);
373 if (testImage == NULL)
374 return false;
375
376 // Fill in a template for matching visuals.
377 XVisualInfo tmpl;
378 tmpl.c_class = TrueColor;
379 tmpl.depth = 24;
380 tmpl.red_mask = testImage->red_mask;
381 tmpl.green_mask = testImage->green_mask;
382 tmpl.blue_mask = testImage->blue_mask;
383
384 // List fields in template that make sense.
385 long mask = (VisualClassMask |
386 VisualRedMaskMask |
387 VisualGreenMaskMask |
388 VisualBlueMaskMask);
389
390 // We don't need that image any more.
391 XDestroyImage(testImage);
392
393 // Now, get a list of matching visuals available.
394 int nVisuals;
395 XVisualInfo *vinfo = XGetVisualInfo(dpy, mask, &tmpl, &nVisuals);
396 if (vinfo == NULL || nVisuals <= 0) {
397 if (vinfo != NULL) {
398 XFree(vinfo);
399 }
400 return false;
401 }
402
403 // Use first visual from the list.
404 *vinfo_ret = vinfo[0];
405
406 XFree(vinfo);
407
408 return true;
409}
410
411IrixOverlayShmImage::~IrixOverlayShmImage()
412{
413 // DEBUG:
414 // fprintf(stderr,"~IrixOverlayShmImage called\n");
Constantin Kaplinskyedb5b242006-03-06 05:24:45 +0000415
416 if (readDisplayBuf != NULL)
417 XShmDestroyReadDisplayBuf(readDisplayBuf);
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000418}
419
420void IrixOverlayShmImage::get(Window wnd, int x, int y)
421{
Constantin Kaplinskye2dc5282006-03-06 05:12:32 +0000422 get(wnd, x, y, xim->width, xim->height);
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000423}
424
425void IrixOverlayShmImage::get(Window wnd, int x, int y, int w, int h)
426{
Constantin Kaplinskye2dc5282006-03-06 05:12:32 +0000427 XRectangle rect;
428 unsigned long hints = XRD_TRANSPARENT | XRD_READ_POINTER;
429
430 rect.x = x;
431 rect.y = y;
432 rect.width = w;
433 rect.height = h;
434
435 XShmReadDisplayRects(dpy, wnd,
436 &rect, 1, readDisplayBuf, -x, -y,
437 hints, &hints);
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000438}
439
440#endif // HAVE_READDISPLAY
441#endif // HAVE_MITSHM
442
443#ifdef HAVE_SUN_OVL
444
445//
446// SolarisOverlayImage class implementation
447//
448
449SolarisOverlayImage::SolarisOverlayImage(Display *d)
450 : Image(d)
451{
452}
453
454SolarisOverlayImage::SolarisOverlayImage(Display *d, int width, int height)
455 : Image(d)
456{
457 Init(width, height);
458}
459
460void SolarisOverlayImage::Init(int width, int height)
461{
462 // FIXME: Check if the extension is available at run time.
463 // FIXME: Maybe just read a small (e.g. 8x8) screen area then
464 // reallocate xim->data[] and correct width and height?
465 xim = XReadScreen(dpy, DefaultRootWindow(dpy), 0, 0, width, height, True);
466 if (xim == NULL) {
467 fprintf(stderr, "XReadScreen() failed\n");
468 return;
469 }
470}
471
472SolarisOverlayImage::~SolarisOverlayImage()
473{
474 // DEBUG:
475 // fprintf(stderr, "~SolarisOverlayImage() called\n");
476}
477
478void SolarisOverlayImage::get(Window wnd, int x, int y)
479{
480 get(wnd, x, y, xim->width, xim->height);
481}
482
483void SolarisOverlayImage::get(Window wnd, int x, int y, int w, int h)
484{
485 XImage *tmp_xim = XReadScreen(dpy, wnd, x, y, w, h, True);
486 if (tmp_xim == NULL)
487 return;
488
489 updateRect(tmp_xim, 0, 0);
490
491 XDestroyImage(tmp_xim);
492}
493
494#endif // HAVE_SUN_OVL
495
496//
497// ImageFactory class implementation
498//
Constantin Kaplinskyce676c62006-02-08 13:36:58 +0000499// FIXME: Make ImageFactory always create images of the same class?
500//
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000501
502// Prepare useful shortcuts for compile-time options.
503#if defined(HAVE_READDISPLAY) && defined(HAVE_MITSHM)
504#define HAVE_SHM_READDISPLAY
505#endif
506#if defined(HAVE_SHM_READDISPLAY) || defined(HAVE_SUN_OVL)
507#define HAVE_OVERLAY_EXT
508#endif
509
510ImageFactory::ImageFactory(bool allowShm, bool allowOverlay)
511 : mayUseShm(allowShm), mayUseOverlay(allowOverlay)
512{
513}
514
515ImageFactory::~ImageFactory()
516{
517}
518
519Image *ImageFactory::newImage(Display *d, int width, int height)
520{
521 Image *image = NULL;
522
523 // First, try to create an image with overlay support.
Constantin Kaplinskyce676c62006-02-08 13:36:58 +0000524 // FIXME: Replace fprintf() with proper logging.
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000525
526#ifdef HAVE_OVERLAY_EXT
527 if (mayUseOverlay) {
528#if defined(HAVE_SHM_READDISPLAY)
529 if (mayUseShm) {
530 image = new IrixOverlayShmImage(d, width, height);
531 if (image->xim != NULL) {
532 fprintf(stderr, "Using IRIX overlay image with SHM support\n");
533 return image;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000534 }
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000535 }
536#elif defined(HAVE_SUN_OVL)
537 image = new SolarisOverlayImage(d, width, height);
538 if (image->xim != NULL) {
539 fprintf(stderr, "Using Solaris overlay image\n");
540 return image;
541 }
542#endif
543 if (image != NULL) {
544 delete image;
545 fprintf(stderr,
546 "Failed to create overlay image, trying other options\n");
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000547 }
548 }
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000549#endif // HAVE_OVERLAY_EXT
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000550
Constantin Kaplinskyd31fd312005-09-08 19:29:02 +0000551 // Now, try to use shared memory image.
552
553#ifdef HAVE_MITSHM
554 if (mayUseShm) {
555 image = new ShmImage(d, width, height);
556 if (image->xim != NULL) {
557 fprintf(stderr, "Using shared memory image\n");
558 return image;
559 }
560
561 delete image;
562 fprintf(stderr,
563 "Failed to create SHM image, falling back to Xlib image\n");
564 }
565#endif // HAVE_MITSHM
566
567 // Fall back to Xlib image.
568
569 fprintf(stderr, "Using Xlib-based image\n");
570 image = new Image(d, width, height);
571 return image;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000572}