blob: ca3d6429455759437537bd0553af2e4975518903 [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
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000322//
323// ImageFactory class implementation
324//
325// FIXME: Make ImageFactory always create images of the same class?
326//
327
Peter Åstrand (astrand)dcd0b132017-10-16 15:18:00 +0200328ImageFactory::ImageFactory(bool allowShm)
329 : mayUseShm(allowShm)
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000330{
331}
332
333ImageFactory::~ImageFactory()
334{
335}
336
337Image *ImageFactory::newImage(Display *d, int width, int height)
338{
339 Image *image = NULL;
340
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000341 // Now, try to use shared memory image.
342
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000343 if (mayUseShm) {
344 image = new ShmImage(d, width, height);
345 if (image->xim != NULL) {
346 return image;
347 }
348
349 delete image;
350 vlog.error("Failed to create SHM image, falling back to Xlib image");
351 }
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000352
353 // Fall back to Xlib image.
354
355 image = new Image(d, width, height);
356 return image;
357}