blob: db20d6f32e2333f3cc0f62f3a6f08a57feb7c331 [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{
Adam Tkac9322a442010-03-04 15:24:20 +0000167 if (tig != 0)
Pierre Ossmana2739342011-03-08 16:53:07 +0000168 tig->setColourMapEntries(0, 0);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000169}
170
171void TXImage::lookup(int index, int* r, int* g, int* b)
172{
173 *r = colourMap[index].r;
174 *g = colourMap[index].g;
175 *b = colourMap[index].b;
176}
177
178#ifdef HAVE_MITSHM
179static bool caughtError = false;
180
181static int XShmAttachErrorHandler(Display *dpy, XErrorEvent *error)
182{
183 caughtError = true;
184 return 0;
185}
186
187class TXImageCleanup {
188public:
189 std::list<TXImage*> images;
190 ~TXImageCleanup() {
191 while (!images.empty())
192 delete images.front();
193 }
194};
195
196static TXImageCleanup imageCleanup;
197#endif
198
199void TXImage::createXImage()
200{
201#ifdef HAVE_MITSHM
202 int major, minor;
203 Bool pixmaps;
204
205 if (XShmQueryVersion(dpy, &major, &minor, &pixmaps)) {
206 shminfo = new XShmSegmentInfo;
207
208 xim = XShmCreateImage(dpy, vis, depth, ZPixmap,
209 0, shminfo, width(), height());
210
211 if (xim) {
212 shminfo->shmid = shmget(IPC_PRIVATE,
213 xim->bytes_per_line * xim->height,
214 IPC_CREAT|0777);
215
216 if (shminfo->shmid != -1) {
217 shminfo->shmaddr = xim->data = (char*)shmat(shminfo->shmid, 0, 0);
218
219 if (shminfo->shmaddr != (char *)-1) {
220
221 shminfo->readOnly = False;
222
223 XErrorHandler oldHdlr = XSetErrorHandler(XShmAttachErrorHandler);
224 XShmAttach(dpy, shminfo);
225 XSync(dpy, False);
226 XSetErrorHandler(oldHdlr);
227
228 if (!caughtError) {
229 vlog.debug("Using shared memory XImage");
230 imageCleanup.images.push_back(this);
231 return;
232 }
233
234 shmdt(shminfo->shmaddr);
235 } else {
236 vlog.error("shmat failed");
237 perror("shmat");
238 }
239
240 shmctl(shminfo->shmid, IPC_RMID, 0);
241 } else {
242 vlog.error("shmget failed");
243 perror("shmget");
244 }
245
246 XDestroyImage(xim);
247 xim = 0;
248 } else {
249 vlog.error("XShmCreateImage failed");
250 }
251
252 delete shminfo;
253 shminfo = 0;
254 }
255#endif
256
257 xim = XCreateImage(dpy, vis, depth, ZPixmap,
258 0, 0, width(), height(), BitmapPad(dpy), 0);
259
260 xim->data = (char*)malloc(xim->bytes_per_line * xim->height);
261 if (!xim->data) {
262 vlog.error("malloc failed");
263 exit(1);
264 }
265}
266
267void TXImage::destroyXImage()
268{
269#ifdef HAVE_MITSHM
270 if (shminfo) {
271 vlog.debug("Freeing shared memory XImage");
272 shmdt(shminfo->shmaddr);
273 shmctl(shminfo->shmid, IPC_RMID, 0);
274 delete shminfo;
275 shminfo = 0;
276 imageCleanup.images.remove(this);
277 }
278#endif
279 // XDestroyImage() will free(xim->data) if appropriate
280 if (xim) XDestroyImage(xim);
281 xim = 0;
282}
283
284
285static bool supportedBPP(int bpp) {
286 return (bpp == 8 || bpp == 16 || bpp == 32);
287}
288
289static int depth2bpp(Display* dpy, int depth)
290{
291 int nformats;
292 XPixmapFormatValues* format = XListPixmapFormats(dpy, &nformats);
293
294 int i;
295 for (i = 0; i < nformats; i++)
296 if (format[i].depth == depth) break;
297
298 if (i == nformats || !supportedBPP(format[i].bits_per_pixel))
299 throw rfb::Exception("Error: couldn't find suitable pixmap format");
300
301 int bpp = format[i].bits_per_pixel;
302 XFree(format);
303 return bpp;
304}
305
306void TXImage::getNativePixelFormat(Visual* vis, int depth)
307{
Pierre Ossman07893cb2009-04-20 09:44:17 +0000308 int bpp;
309 int trueColour, bigEndian;
310 int redShift, greenShift, blueShift;
311 int redMax, greenMax, blueMax;
312
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000313 cube = 0;
Pierre Ossman07893cb2009-04-20 09:44:17 +0000314
315 bpp = depth2bpp(dpy, depth);
316 bigEndian = (ImageByteOrder(dpy) == MSBFirst);
317 trueColour = (vis->c_class == TrueColor);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000318
319 vlog.info("Using default colormap and visual, %sdepth %d.",
320 (vis->c_class == TrueColor) ? "TrueColor, " :
321 ((vis->c_class == PseudoColor) ? "PseudoColor, " : ""),
322 depth);
323
Pierre Ossman07893cb2009-04-20 09:44:17 +0000324 redShift = ffs(vis->red_mask) - 1;
325 greenShift = ffs(vis->green_mask) - 1;
326 blueShift = ffs(vis->blue_mask) - 1;
327 redMax = vis->red_mask >> redShift;
328 greenMax = vis->green_mask >> greenShift;
329 blueMax = vis->blue_mask >> blueShift;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000330
Pierre Ossman07893cb2009-04-20 09:44:17 +0000331 nativePF = PixelFormat(bpp, depth, bigEndian, trueColour,
332 redMax, greenMax, blueMax,
333 redShift, greenShift, blueShift);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000334
Pierre Ossman07893cb2009-04-20 09:44:17 +0000335 if (!trueColour) {
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000336 XColor xc[256];
337 cube = new rfb::ColourCube(6,6,6);
338 int r;
339 for (r = 0; r < cube->nRed; r++) {
340 for (int g = 0; g < cube->nGreen; g++) {
341 for (int b = 0; b < cube->nBlue; b++) {
342 int i = (r * cube->nGreen + g) * cube->nBlue + b;
343 xc[i].red = r * 65535 / (cube->nRed-1);
344 xc[i].green = g * 65535 / (cube->nGreen-1);
345 xc[i].blue = b * 65535 / (cube->nBlue-1);
346 }
347 }
348 }
349
350 TXWindow::getColours(dpy, xc, cube->size());
351
352 for (r = 0; r < cube->nRed; r++) {
353 for (int g = 0; g < cube->nGreen; g++) {
354 for (int b = 0; b < cube->nBlue; b++) {
355 int i = (r * cube->nGreen + g) * cube->nBlue + b;
356 cube->set(r, g, b, xc[i].pixel);
357 }
358 }
359 }
360 }
361}