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