blob: a5bd2b0ded97da6971236475c719ff9fca130a27 [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)
Pierre Ossman7450a6f2018-02-27 15:55:44 +010061 : xim(NULL), dpy(d)
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000062{
63 imageCleanup.images.push_back(this);
64}
65
66Image::Image(Display *d, int width, int height)
Pierre Ossman7450a6f2018-02-27 15:55:44 +010067 : xim(NULL), dpy(d)
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000068{
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));
Pierre Ossman7450a6f2018-02-27 15:55:44 +010076
77 if (vis->c_class != TrueColor) {
78 vlog.error("pseudocolour not supported");
79 exit(1);
80 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000081
82 xim = XCreateImage(dpy, vis, DefaultDepth(dpy, DefaultScreen(dpy)),
83 ZPixmap, 0, 0, width, height, BitmapPad(dpy), 0);
84
85 xim->data = (char *)malloc(xim->bytes_per_line * xim->height);
86 if (xim->data == NULL) {
87 vlog.error("malloc() failed");
88 exit(1);
89 }
90}
91
92Image::~Image()
93{
94 imageCleanup.images.remove(this);
95
96 // XDestroyImage will free xim->data if necessary
97 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
Constantin Kaplinskycffb6912007-09-03 10:17:19 +0000106void Image::get(Window wnd, int x, int y, int w, int h,
107 int dst_x, int dst_y)
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000108{
Constantin Kaplinskycffb6912007-09-03 10:17:19 +0000109 XGetSubImage(dpy, wnd, x, y, w, h, AllPlanes, ZPixmap, xim, dst_x, dst_y);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000110}
111
112//
113// Copying pixels from one image to another.
114//
115// FIXME: Use Point and Rect structures?
116// FIXME: Too many similar methods?
117//
118
119inline
120void Image::copyPixels(XImage *src,
121 int dst_x, int dst_y,
122 int src_x, int src_y,
123 int w, int h)
124{
125 const char *srcOffset =
126 src->data + (src_y * src->bytes_per_line +
127 src_x * (src->bits_per_pixel / 8));
128 char *dstOffset =
129 xim->data + (dst_y * xim->bytes_per_line +
130 dst_x * (xim->bits_per_pixel / 8));
131
132 int rowLength = w * (xim->bits_per_pixel / 8);
133
134 for (int i = 0; i < h ; i++) {
135 memcpy(dstOffset, srcOffset, rowLength);
136 srcOffset += src->bytes_per_line;
137 dstOffset += xim->bytes_per_line;
138 }
139}
140
141void Image::updateRect(XImage *src, int dst_x, int dst_y)
142{
143 // Limit width and height at destination image size.
144 int w = src->width;
145 if (dst_x + w > xim->width)
146 w = xim->width - dst_x;
147 int h = src->height;
148 if (dst_y + h > xim->height)
149 h = xim->height - dst_y;
150
151 copyPixels(src, dst_x, dst_y, 0, 0, w, h);
152}
153
154void Image::updateRect(Image *src, int dst_x, int dst_y)
155{
156 updateRect(src->xim, dst_x, dst_y);
157}
158
159void Image::updateRect(XImage *src, int dst_x, int dst_y, int w, int h)
160{
161 // Correct width and height if necessary.
162 if (w > src->width)
163 w = src->width;
164 if (dst_x + w > xim->width)
165 w = xim->width - dst_x;
166 if (h > src->height)
167 h = src->height;
168 if (dst_y + h > xim->height)
169 h = xim->height - dst_y;
170
171 copyPixels(src, dst_x, dst_y, 0, 0, w, h);
172}
173
174void Image::updateRect(Image *src, int dst_x, int dst_y, int w, int h)
175{
176 updateRect(src->xim, dst_x, dst_y, w, h);
177}
178
179void Image::updateRect(XImage *src, int dst_x, int dst_y,
180 int src_x, int src_y, int w, int h)
181{
182 // Correct width and height if necessary.
183 if (src_x + w > src->width)
184 w = src->width - src_x;
185 if (dst_x + w > xim->width)
186 w = xim->width - dst_x;
187 if (src_y + h > src->height)
188 h = src->height - src_y;
189 if (dst_y + h > xim->height)
190 h = xim->height - dst_y;
191
192 copyPixels(src, dst_x, dst_y, src_x, src_y, w, h);
193}
194
195void Image::updateRect(Image *src, int dst_x, int dst_y,
196 int src_x, int src_y, int w, int h)
197{
198 updateRect(src->xim, dst_x, dst_y, src_x, src_y, w, h);
199}
200
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000201//
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
Pierre Ossman7450a6f2018-02-27 15:55:44 +0100246 if (visual->c_class != TrueColor) {
247 vlog.error("pseudocolour not supported");
248 exit(1);
249 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000250
251 shminfo = new XShmSegmentInfo;
252
253 xim = XShmCreateImage(dpy, visual, depth, ZPixmap, 0, shminfo,
254 width, height);
255 if (xim == NULL) {
256 vlog.error("XShmCreateImage() failed");
257 delete shminfo;
258 shminfo = NULL;
259 return;
260 }
261
262 shminfo->shmid = shmget(IPC_PRIVATE,
263 xim->bytes_per_line * xim->height,
264 IPC_CREAT|0777);
265 if (shminfo->shmid == -1) {
266 perror("shmget");
267 vlog.error("shmget() failed (%d bytes requested)",
268 int(xim->bytes_per_line * xim->height));
269 XDestroyImage(xim);
270 xim = NULL;
271 delete shminfo;
272 shminfo = NULL;
273 return;
274 }
275
276 shminfo->shmaddr = xim->data = (char *)shmat(shminfo->shmid, 0, 0);
277 if (shminfo->shmaddr == (char *)-1) {
278 perror("shmat");
279 vlog.error("shmat() failed (%d bytes requested)",
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 vlog.error("XShmAttach() failed");
297 shmdt(shminfo->shmaddr);
298 shmctl(shminfo->shmid, IPC_RMID, 0);
299 XDestroyImage(xim);
300 xim = NULL;
301 delete shminfo;
302 shminfo = NULL;
303 return;
304 }
305}
306
307ShmImage::~ShmImage()
308{
309 // FIXME: Destroy image as described in MIT-SHM documentation.
310 if (shminfo != NULL) {
311 shmdt(shminfo->shmaddr);
312 shmctl(shminfo->shmid, IPC_RMID, 0);
313 delete shminfo;
314 }
315}
316
317void ShmImage::get(Window wnd, int x, int y)
318{
319 XShmGetImage(dpy, wnd, xim, x, y, AllPlanes);
320}
321
Constantin Kaplinskycffb6912007-09-03 10:17:19 +0000322void ShmImage::get(Window wnd, int x, int y, int w, int h,
323 int dst_x, int dst_y)
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000324{
Peter Åstrand (astrand)46fab932018-01-15 15:17:39 +0100325 // XShmGetImage is faster, but can only retrieve the entire
326 // window. Use it for large reads.
327 if (x == dst_x && y == dst_y && (long)w * h > (long)xim->width * xim->height / 4) {
328 XShmGetImage(dpy, wnd, xim, 0, 0, AllPlanes);
329 } else {
330 XGetSubImage(dpy, wnd, x, y, w, h, AllPlanes, ZPixmap, xim, dst_x, dst_y);
331 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000332}
333
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000334//
335// ImageFactory class implementation
336//
337// FIXME: Make ImageFactory always create images of the same class?
338//
339
Peter Åstrand (astrand)dcd0b132017-10-16 15:18:00 +0200340ImageFactory::ImageFactory(bool allowShm)
341 : mayUseShm(allowShm)
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000342{
343}
344
345ImageFactory::~ImageFactory()
346{
347}
348
349Image *ImageFactory::newImage(Display *d, int width, int height)
350{
351 Image *image = NULL;
352
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000353 // Now, try to use shared memory image.
354
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000355 if (mayUseShm) {
356 image = new ShmImage(d, width, height);
357 if (image->xim != NULL) {
358 return image;
359 }
360
361 delete image;
362 vlog.error("Failed to create SHM image, falling back to Xlib image");
363 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000364
365 // Fall back to Xlib image.
366
367 image = new Image(d, width, height);
368 return image;
369}