blob: a3f8ebfd39411d5c58101bf2ccdeee486e2d2932 [file] [log] [blame]
Pierre Ossman13500692011-06-13 11:23:08 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 * Copyright 2011 Pierre Ossman <ossman@cendio.se> for Cendio AB
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#include <assert.h>
21#include <stdlib.h>
22
23#include <FL/x.H>
24
25#include <rfb/LogWriter.h>
26#include <rfb/Exception.h>
27
28#include "X11PixelBuffer.h"
29
30using namespace rfb;
31
32static rfb::LogWriter vlog("PlatformPixelBuffer");
33
34static PixelFormat display_pf()
35{
36 int i;
37
38 int bpp;
39 int trueColour, bigEndian;
40 int redShift, greenShift, blueShift;
41 int redMax, greenMax, blueMax;
42
43 int nformats;
44 XPixmapFormatValues* format;
45
46 // Might not be open at this point
47 fl_open_display();
48
49 format = XListPixmapFormats(fl_display, &nformats);
50
51 for (i = 0; i < nformats; i++)
52 if (format[i].depth == fl_visual->depth) break;
53
54 if (i == nformats)
55 throw rfb::Exception("Error: display lacks pixmap format for default depth");
56
57 switch (format[i].bits_per_pixel) {
58 case 8:
59 case 16:
60 case 32:
61 bpp = format[i].bits_per_pixel;
62 break;
63 default:
64 throw rfb::Exception("Error: couldn't find suitable pixmap format");
65 }
66
67 XFree(format);
68
69 bigEndian = (ImageByteOrder(fl_display) == MSBFirst);
70 trueColour = (fl_visual->c_class == TrueColor);
71
72 if (!trueColour)
73 throw rfb::Exception("Error: only true colour displays supported");
74
75 vlog.info("Using default colormap and visual, %sdepth %d.",
76 (fl_visual->c_class == TrueColor) ? "TrueColor, " :
77 ((fl_visual->c_class == PseudoColor) ? "PseudoColor, " : ""),
78 fl_visual->depth);
79
80 redShift = ffs(fl_visual->red_mask) - 1;
81 greenShift = ffs(fl_visual->green_mask) - 1;
82 blueShift = ffs(fl_visual->blue_mask) - 1;
83 redMax = fl_visual->red_mask >> redShift;
84 greenMax = fl_visual->green_mask >> greenShift;
85 blueMax = fl_visual->blue_mask >> blueShift;
86
87 return PixelFormat(bpp, fl_visual->depth, bigEndian, trueColour,
88 redMax, greenMax, blueMax,
89 redShift, greenShift, blueShift);
90}
91
92PlatformPixelBuffer::PlatformPixelBuffer(int width, int height) :
93 FullFramePixelBuffer(display_pf(), width, height, NULL, NULL),
94 shminfo(NULL), xim(NULL)
95{
96 // Might not be open at this point
97 fl_open_display();
98
99 if (!setupShm()) {
100 xim = XCreateImage(fl_display, fl_visual->visual, fl_visual->depth,
101 ZPixmap, 0, 0, width, height, BitmapPad(fl_display), 0);
102 assert(xim);
103
104 xim->data = (char*)malloc(xim->bytes_per_line * xim->height);
105 assert(xim->data);
106 }
107
108 data = (rdr::U8*)xim->data;
109}
110
111
112PlatformPixelBuffer::~PlatformPixelBuffer()
113{
114 if (shminfo) {
115 vlog.debug("Freeing shared memory XImage");
116 shmdt(shminfo->shmaddr);
117 shmctl(shminfo->shmid, IPC_RMID, 0);
118 delete shminfo;
119 shminfo = NULL;
120 }
121
122 // XDestroyImage() will free(xim->data) if appropriate
123 if (xim)
124 XDestroyImage(xim);
125 xim = NULL;
126}
127
128
129void PlatformPixelBuffer::draw(int src_x, int src_y, int x, int y, int w, int h)
130{
131 if (shminfo)
132 XShmPutImage(fl_display, fl_window, fl_gc, xim, src_x, src_y, x, y, w, h, False);
133 else
134 XPutImage(fl_display, fl_window, fl_gc, xim, src_x, src_y, x, y, w, h);
135}
136
137
138int PlatformPixelBuffer::getStride() const
139{
140 return xim->bytes_per_line / (getPF().bpp/8);
141}
142
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000143static bool caughtError;
144
145static int XShmAttachErrorHandler(Display *dpy, XErrorEvent *error)
146{
147 caughtError = true;
148 return 0;
149}
Pierre Ossman13500692011-06-13 11:23:08 +0000150
151int PlatformPixelBuffer::setupShm()
152{
153 int major, minor;
154 Bool pixmaps;
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000155 XErrorHandler old_handler;
156 Status status;
Pierre Ossman13500692011-06-13 11:23:08 +0000157
158 if (!XShmQueryVersion(fl_display, &major, &minor, &pixmaps))
159 return 0;
160
161 shminfo = new XShmSegmentInfo;
162
163 xim = XShmCreateImage(fl_display, fl_visual->visual, fl_visual->depth,
164 ZPixmap, 0, shminfo, width(), height());
165 if (!xim)
166 goto free_shminfo;
167
168 shminfo->shmid = shmget(IPC_PRIVATE,
169 xim->bytes_per_line * xim->height,
170 IPC_CREAT|0777);
171 if (shminfo->shmid == -1)
172 goto free_xim;
173
174 shminfo->shmaddr = xim->data = (char*)shmat(shminfo->shmid, 0, 0);
175 if (shminfo->shmaddr == (char *)-1)
176 goto free_shm;
177
178 shminfo->readOnly = True;
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000179
180 // This is the only way we can detect that shared memory won't work
181 // (e.g. because we're accessing a remote X11 server)
182 caughtError = false;
183 old_handler = XSetErrorHandler(XShmAttachErrorHandler);
184
Pierre Ossman13500692011-06-13 11:23:08 +0000185 XShmAttach(fl_display, shminfo);
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000186 XSync(fl_display, False);
187
188 XSetErrorHandler(old_handler);
189
190 if (caughtError)
191 goto free_shmaddr;
Pierre Ossman13500692011-06-13 11:23:08 +0000192
193 vlog.debug("Using shared memory XImage");
194
195 return 1;
196
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000197free_shmaddr:
198 shmdt(shminfo->shmaddr);
199
Pierre Ossman13500692011-06-13 11:23:08 +0000200free_shm:
201 shmctl(shminfo->shmid, IPC_RMID, 0);
202
203free_xim:
204 XDestroyImage(xim);
205 xim = NULL;
206
207free_shminfo:
208 delete shminfo;
209 shminfo = NULL;
210
211 return 0;
212}