blob: 95cd2b7023955dde0fb927c65887e2e668cec13d [file] [log] [blame]
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18//
19// TXImage.cxx
20//
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 <strings.h>
26#include <sys/types.h>
27#include <sys/ipc.h>
28#include <sys/shm.h>
29#include <X11/Xlib.h>
30#include <X11/Xutil.h>
31#include <list>
32#include <rfb/TransImageGetter.h>
33#include <rfb/Exception.h>
34#include <rfb/LogWriter.h>
35#include "TXWindow.h"
36#include "TXImage.h"
37
38using namespace rfb;
39
40static rfb::LogWriter vlog("TXImage");
41
42TXImage::TXImage(Display* d, int width, int height, Visual* vis_, int depth_)
43 : xim(0), dpy(d), vis(vis_), depth(depth_), tig(0), cube(0)
44{
45#ifdef HAVE_MITSHM
46 shminfo = 0;
47#endif
48 width_ = width;
49 height_ = height;
50 for (int i = 0; i < 256; i++)
51 colourMap[i].r = colourMap[i].g = colourMap[i].b = 0;
52
53 if (!vis)
54 vis = DefaultVisual(dpy,DefaultScreen(dpy));
55 if (!depth)
56 depth = DefaultDepth(dpy,DefaultScreen(dpy));
57
58 createXImage();
59 getNativePixelFormat(vis, depth);
60 colourmap = this;
61 format.bpp = 0; // just make it different to any valid format, so that...
62 setPF(nativePF); // ...setPF() always works
63}
64
65TXImage::~TXImage()
66{
67 if (data != (rdr::U8*)xim->data) delete [] data;
68 destroyXImage();
69 delete tig;
70 delete cube;
71}
72
73void TXImage::resize(int w, int h)
74{
75 if (w == width() && h == height()) return;
76
77 int oldStrideBytes = getStride() * (format.bpp/8);
78 int rowsToCopy = __rfbmin(h, height());
79 int bytesPerRow = __rfbmin(w, width()) * (format.bpp/8);
80 rdr::U8* oldData = 0;
81 bool allocData = false;
82
83 if (data != (rdr::U8*)xim->data) {
84 oldData = (rdr::U8*)data;
85 allocData = true;
86 } else {
87 oldData = new rdr::U8[xim->bytes_per_line * height()];
88 memcpy(oldData, xim->data, xim->bytes_per_line * height());
89 }
90
91 destroyXImage();
92 width_ = w;
93 height_ = h;
94 createXImage();
95
96 if (allocData)
97 data = new rdr::U8[width() * height() * (format.bpp/8)];
98 else
99 data = (rdr::U8*)xim->data;
100
101 int newStrideBytes = getStride() * (format.bpp/8);
102 for (int i = 0; i < rowsToCopy; i++)
103 memcpy((rdr::U8*)data + newStrideBytes * i, oldData + oldStrideBytes * i,
104 bytesPerRow);
105 delete [] oldData;
106}
107
108void TXImage::setPF(const PixelFormat& newPF)
109{
110 if (newPF.equal(format)) return;
111 format = newPF;
112
113 if (data != (rdr::U8*)xim->data) delete [] data;
114 delete tig;
115 tig = 0;
116
117 if (format.equal(nativePF) && format.trueColour) {
118 data = (rdr::U8*)xim->data;
119 } else {
120 data = new rdr::U8[width() * height() * (format.bpp/8)];
121 tig = new TransImageGetter();
122 tig->init(this, nativePF, 0, cube);
123 }
124}
125
126int TXImage::getStride() const
127{
128 if (data == (rdr::U8*)xim->data)
129 return xim->bytes_per_line / (xim->bits_per_pixel / 8);
130 else
131 return width();
132}
133
134void TXImage::put(Window win, GC gc, const rfb::Rect& r)
135{
136 if (r.is_empty()) return;
137 int x = r.tl.x;
138 int y = r.tl.y;
139 int w = r.width();
140 int h = r.height();
141 if (data != (rdr::U8*)xim->data) {
142 rdr::U8* ximDataStart = ((rdr::U8*)xim->data + y * xim->bytes_per_line
143 + x * (xim->bits_per_pixel / 8));
144 tig->getImage(ximDataStart, r,
145 xim->bytes_per_line / (xim->bits_per_pixel / 8));
146 }
147#ifdef HAVE_MITSHM
148 if (usingShm()) {
149 XShmPutImage(dpy, win, gc, xim, x, y, x, y, w, h, False);
150 return;
151 }
152#endif
153 XPutImage(dpy, win, gc, xim, x, y, x, y, w, h);
154}
155
156void TXImage::setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs)
157{
158 for (int i = 0; i < nColours; i++) {
159 colourMap[firstColour+i].r = rgbs[i*3];
160 colourMap[firstColour+i].g = rgbs[i*3+1];
161 colourMap[firstColour+i].b = rgbs[i*3+2];
162 }
163}
164
165void TXImage::updateColourMap()
166{
167 tig->setColourMapEntries(0, 0, 0);
168}
169
170void TXImage::lookup(int index, int* r, int* g, int* b)
171{
172 *r = colourMap[index].r;
173 *g = colourMap[index].g;
174 *b = colourMap[index].b;
175}
176
177#ifdef HAVE_MITSHM
178static bool caughtError = false;
179
180static int XShmAttachErrorHandler(Display *dpy, XErrorEvent *error)
181{
182 caughtError = true;
183 return 0;
184}
185
186class TXImageCleanup {
187public:
188 std::list<TXImage*> images;
189 ~TXImageCleanup() {
190 while (!images.empty())
191 delete images.front();
192 }
193};
194
195static TXImageCleanup imageCleanup;
196#endif
197
198void TXImage::createXImage()
199{
200#ifdef HAVE_MITSHM
201 int major, minor;
202 Bool pixmaps;
203
204 if (XShmQueryVersion(dpy, &major, &minor, &pixmaps)) {
205 shminfo = new XShmSegmentInfo;
206
207 xim = XShmCreateImage(dpy, vis, depth, ZPixmap,
208 0, shminfo, width(), height());
209
210 if (xim) {
211 shminfo->shmid = shmget(IPC_PRIVATE,
212 xim->bytes_per_line * xim->height,
213 IPC_CREAT|0777);
214
215 if (shminfo->shmid != -1) {
216 shminfo->shmaddr = xim->data = (char*)shmat(shminfo->shmid, 0, 0);
217
218 if (shminfo->shmaddr != (char *)-1) {
219
220 shminfo->readOnly = False;
221
222 XErrorHandler oldHdlr = XSetErrorHandler(XShmAttachErrorHandler);
223 XShmAttach(dpy, shminfo);
224 XSync(dpy, False);
225 XSetErrorHandler(oldHdlr);
226
227 if (!caughtError) {
228 vlog.debug("Using shared memory XImage");
229 imageCleanup.images.push_back(this);
230 return;
231 }
232
233 shmdt(shminfo->shmaddr);
234 } else {
235 vlog.error("shmat failed");
236 perror("shmat");
237 }
238
239 shmctl(shminfo->shmid, IPC_RMID, 0);
240 } else {
241 vlog.error("shmget failed");
242 perror("shmget");
243 }
244
245 XDestroyImage(xim);
246 xim = 0;
247 } else {
248 vlog.error("XShmCreateImage failed");
249 }
250
251 delete shminfo;
252 shminfo = 0;
253 }
254#endif
255
256 xim = XCreateImage(dpy, vis, depth, ZPixmap,
257 0, 0, width(), height(), BitmapPad(dpy), 0);
258
259 xim->data = (char*)malloc(xim->bytes_per_line * xim->height);
260 if (!xim->data) {
261 vlog.error("malloc failed");
262 exit(1);
263 }
264}
265
266void TXImage::destroyXImage()
267{
268#ifdef HAVE_MITSHM
269 if (shminfo) {
270 vlog.debug("Freeing shared memory XImage");
271 shmdt(shminfo->shmaddr);
272 shmctl(shminfo->shmid, IPC_RMID, 0);
273 delete shminfo;
274 shminfo = 0;
275 imageCleanup.images.remove(this);
276 }
277#endif
278 // XDestroyImage() will free(xim->data) if appropriate
279 if (xim) XDestroyImage(xim);
280 xim = 0;
281}
282
283
284static bool supportedBPP(int bpp) {
285 return (bpp == 8 || bpp == 16 || bpp == 32);
286}
287
288static int depth2bpp(Display* dpy, int depth)
289{
290 int nformats;
291 XPixmapFormatValues* format = XListPixmapFormats(dpy, &nformats);
292
293 int i;
294 for (i = 0; i < nformats; i++)
295 if (format[i].depth == depth) break;
296
297 if (i == nformats || !supportedBPP(format[i].bits_per_pixel))
298 throw rfb::Exception("Error: couldn't find suitable pixmap format");
299
300 int bpp = format[i].bits_per_pixel;
301 XFree(format);
302 return bpp;
303}
304
305void TXImage::getNativePixelFormat(Visual* vis, int depth)
306{
307 cube = 0;
308 nativePF.depth = depth;
309 nativePF.bpp = depth2bpp(dpy, depth);
310 nativePF.bigEndian = (ImageByteOrder(dpy) == MSBFirst);
311 nativePF.trueColour = (vis->c_class == TrueColor);
312
313 vlog.info("Using default colormap and visual, %sdepth %d.",
314 (vis->c_class == TrueColor) ? "TrueColor, " :
315 ((vis->c_class == PseudoColor) ? "PseudoColor, " : ""),
316 depth);
317
318 if (nativePF.trueColour) {
319
320 nativePF.redShift = ffs(vis->red_mask) - 1;
321 nativePF.greenShift = ffs(vis->green_mask) - 1;
322 nativePF.blueShift = ffs(vis->blue_mask) - 1;
323 nativePF.redMax = vis->red_mask >> nativePF.redShift;
324 nativePF.greenMax = vis->green_mask >> nativePF.greenShift;
325 nativePF.blueMax = vis->blue_mask >> nativePF.blueShift;
326
327 } else {
328
329 XColor xc[256];
330 cube = new rfb::ColourCube(6,6,6);
331 int r;
332 for (r = 0; r < cube->nRed; r++) {
333 for (int g = 0; g < cube->nGreen; g++) {
334 for (int b = 0; b < cube->nBlue; b++) {
335 int i = (r * cube->nGreen + g) * cube->nBlue + b;
336 xc[i].red = r * 65535 / (cube->nRed-1);
337 xc[i].green = g * 65535 / (cube->nGreen-1);
338 xc[i].blue = b * 65535 / (cube->nBlue-1);
339 }
340 }
341 }
342
343 TXWindow::getColours(dpy, xc, cube->size());
344
345 for (r = 0; r < cube->nRed; r++) {
346 for (int g = 0; g < cube->nGreen; g++) {
347 for (int b = 0; b < cube->nBlue; b++) {
348 int i = (r * cube->nGreen + g) * cube->nBlue + b;
349 cube->set(r, g, b, xc[i].pixel);
350 }
351 }
352 }
353 }
354}