blob: a1d7687ed681119493527af556480aa180a84bd8 [file] [log] [blame]
Pierre Ossman13500692011-06-13 11:23:08 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossmanac13abe2014-02-07 14:46:26 +01002 * Copyright 2011-2014 Pierre Ossman for Cendio AB
Pierre Ossman13500692011-06-13 11:23:08 +00003 *
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
Peter Åstrandc359f362011-08-23 12:04:46 +000020#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
Pierre Ossmanc9dd3a42015-11-18 16:24:16 +010024#include <assert.h>
Pierre Ossman13500692011-06-13 11:23:08 +000025#include <stdlib.h>
26
Pierre Ossmanc9dd3a42015-11-18 16:24:16 +010027#include <FL/Fl.H>
Pierre Ossman13500692011-06-13 11:23:08 +000028#include <FL/x.H>
29
30#include <rfb/LogWriter.h>
31#include <rfb/Exception.h>
32
Pierre Ossman8ca4c1d2014-09-22 12:54:26 +020033#include "i18n.h"
Pierre Ossman13500692011-06-13 11:23:08 +000034#include "X11PixelBuffer.h"
35
36using namespace rfb;
37
Pierre Ossmanac13abe2014-02-07 14:46:26 +010038static rfb::LogWriter vlog("X11PixelBuffer");
Pierre Ossman13500692011-06-13 11:23:08 +000039
Pierre Ossmanc9dd3a42015-11-18 16:24:16 +010040std::list<X11PixelBuffer*> X11PixelBuffer::shmList;
41
Pierre Ossman13500692011-06-13 11:23:08 +000042static PixelFormat display_pf()
43{
44 int i;
45
46 int bpp;
47 int trueColour, bigEndian;
48 int redShift, greenShift, blueShift;
49 int redMax, greenMax, blueMax;
50
51 int nformats;
52 XPixmapFormatValues* format;
53
54 // Might not be open at this point
55 fl_open_display();
56
57 format = XListPixmapFormats(fl_display, &nformats);
58
59 for (i = 0; i < nformats; i++)
60 if (format[i].depth == fl_visual->depth) break;
61
62 if (i == nformats)
Pierre Ossman744e55c2014-12-03 14:00:54 +010063 // TRANSLATORS: "pixmap" is an X11 concept and may not be suitable
64 // to translate.
Pierre Ossman86750632014-10-10 13:33:19 +020065 throw rfb::Exception(_("Display lacks pixmap format for default depth"));
Pierre Ossman13500692011-06-13 11:23:08 +000066
67 switch (format[i].bits_per_pixel) {
68 case 8:
69 case 16:
70 case 32:
71 bpp = format[i].bits_per_pixel;
72 break;
73 default:
Pierre Ossman744e55c2014-12-03 14:00:54 +010074 // TRANSLATORS: "pixmap" is an X11 concept and may not be suitable
75 // to translate.
Pierre Ossman86750632014-10-10 13:33:19 +020076 throw rfb::Exception(_("Couldn't find suitable pixmap format"));
Pierre Ossman13500692011-06-13 11:23:08 +000077 }
78
79 XFree(format);
80
81 bigEndian = (ImageByteOrder(fl_display) == MSBFirst);
82 trueColour = (fl_visual->c_class == TrueColor);
83
84 if (!trueColour)
Pierre Ossman86750632014-10-10 13:33:19 +020085 throw rfb::Exception(_("Only true colour displays supported"));
Pierre Ossman13500692011-06-13 11:23:08 +000086
Pierre Ossman2aef35c2014-12-03 14:01:56 +010087 vlog.info(_("Using default colormap and visual, TrueColor, depth %d."),
Pierre Ossman13500692011-06-13 11:23:08 +000088 fl_visual->depth);
89
90 redShift = ffs(fl_visual->red_mask) - 1;
91 greenShift = ffs(fl_visual->green_mask) - 1;
92 blueShift = ffs(fl_visual->blue_mask) - 1;
93 redMax = fl_visual->red_mask >> redShift;
94 greenMax = fl_visual->green_mask >> greenShift;
95 blueMax = fl_visual->blue_mask >> blueShift;
96
97 return PixelFormat(bpp, fl_visual->depth, bigEndian, trueColour,
98 redMax, greenMax, blueMax,
99 redShift, greenShift, blueShift);
100}
101
Pierre Ossmanac13abe2014-02-07 14:46:26 +0100102X11PixelBuffer::X11PixelBuffer(int width, int height) :
Pierre Ossman2e5a1062014-01-30 17:57:27 +0100103 PlatformPixelBuffer(display_pf(), width, height, NULL, 0),
Pierre Ossman919ae452016-05-04 17:51:19 +0200104 shminfo(NULL), xim(NULL), pendingPutImage(0), pendingDrawable(0)
Pierre Ossman13500692011-06-13 11:23:08 +0000105{
106 // Might not be open at this point
107 fl_open_display();
108
109 if (!setupShm()) {
110 xim = XCreateImage(fl_display, fl_visual->visual, fl_visual->depth,
111 ZPixmap, 0, 0, width, height, BitmapPad(fl_display), 0);
Pierre Ossmand1a853b2014-10-10 13:37:35 +0200112 if (!xim)
Pierre Ossmanfefd9002016-12-17 14:04:39 +0100113 throw rfb::Exception("XCreateImage");
Pierre Ossman13500692011-06-13 11:23:08 +0000114
115 xim->data = (char*)malloc(xim->bytes_per_line * xim->height);
Pierre Ossmand1a853b2014-10-10 13:37:35 +0200116 if (!xim->data)
117 throw rfb::Exception(_("Not enough memory for framebuffer"));
Pierre Ossmanfefd9002016-12-17 14:04:39 +0100118
119 vlog.debug("Using standard XImage");
Pierre Ossman13500692011-06-13 11:23:08 +0000120 }
121
122 data = (rdr::U8*)xim->data;
Pierre Ossman2e5a1062014-01-30 17:57:27 +0100123 stride = xim->bytes_per_line / (getPF().bpp/8);
Pierre Ossman13500692011-06-13 11:23:08 +0000124}
125
126
Pierre Ossmanac13abe2014-02-07 14:46:26 +0100127X11PixelBuffer::~X11PixelBuffer()
Pierre Ossman13500692011-06-13 11:23:08 +0000128{
129 if (shminfo) {
130 vlog.debug("Freeing shared memory XImage");
Pierre Ossmanc9dd3a42015-11-18 16:24:16 +0100131 shmList.remove(this);
132 Fl::remove_system_handler(handleSystemEvent);
Pierre Ossman13500692011-06-13 11:23:08 +0000133 shmdt(shminfo->shmaddr);
134 shmctl(shminfo->shmid, IPC_RMID, 0);
135 delete shminfo;
136 shminfo = NULL;
137 }
138
139 // XDestroyImage() will free(xim->data) if appropriate
140 if (xim)
141 XDestroyImage(xim);
142 xim = NULL;
143}
144
145
Pierre Ossmanac13abe2014-02-07 14:46:26 +0100146void X11PixelBuffer::draw(int src_x, int src_y, int x, int y, int w, int h)
Pierre Ossman13500692011-06-13 11:23:08 +0000147{
Pierre Ossmanc9dd3a42015-11-18 16:24:16 +0100148 if (shminfo) {
149 XShmPutImage(fl_display, fl_window, fl_gc, xim, src_x, src_y, x, y, w, h, True);
150 pendingPutImage++;
Pierre Ossman919ae452016-05-04 17:51:19 +0200151 assert((pendingPutImage == 1) || (pendingDrawable == fl_window));
152 pendingDrawable = fl_window;
Pierre Ossmanc9dd3a42015-11-18 16:24:16 +0100153 } else {
Pierre Ossman13500692011-06-13 11:23:08 +0000154 XPutImage(fl_display, fl_window, fl_gc, xim, src_x, src_y, x, y, w, h);
Pierre Ossmanc9dd3a42015-11-18 16:24:16 +0100155 }
Pierre Ossman13500692011-06-13 11:23:08 +0000156}
157
Pierre Ossmanc9dd3a42015-11-18 16:24:16 +0100158bool X11PixelBuffer::isRendering(void)
159{
160 return pendingPutImage > 0;
161}
Pierre Ossman13500692011-06-13 11:23:08 +0000162
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000163static bool caughtError;
164
165static int XShmAttachErrorHandler(Display *dpy, XErrorEvent *error)
166{
167 caughtError = true;
168 return 0;
169}
Pierre Ossman13500692011-06-13 11:23:08 +0000170
Pierre Ossmanac13abe2014-02-07 14:46:26 +0100171int X11PixelBuffer::setupShm()
Pierre Ossman13500692011-06-13 11:23:08 +0000172{
173 int major, minor;
174 Bool pixmaps;
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000175 XErrorHandler old_handler;
Tim Waughb17c9c42014-10-16 14:53:17 +0100176 const char *display_name = XDisplayName (NULL);
177
178 /* Don't use MIT-SHM on remote displays */
179 if (*display_name && *display_name != ':')
180 return 0;
Pierre Ossman13500692011-06-13 11:23:08 +0000181
182 if (!XShmQueryVersion(fl_display, &major, &minor, &pixmaps))
183 return 0;
184
185 shminfo = new XShmSegmentInfo;
186
187 xim = XShmCreateImage(fl_display, fl_visual->visual, fl_visual->depth,
188 ZPixmap, 0, shminfo, width(), height());
189 if (!xim)
190 goto free_shminfo;
191
192 shminfo->shmid = shmget(IPC_PRIVATE,
193 xim->bytes_per_line * xim->height,
194 IPC_CREAT|0777);
195 if (shminfo->shmid == -1)
196 goto free_xim;
197
198 shminfo->shmaddr = xim->data = (char*)shmat(shminfo->shmid, 0, 0);
199 if (shminfo->shmaddr == (char *)-1)
200 goto free_shm;
201
202 shminfo->readOnly = True;
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000203
204 // This is the only way we can detect that shared memory won't work
205 // (e.g. because we're accessing a remote X11 server)
206 caughtError = false;
207 old_handler = XSetErrorHandler(XShmAttachErrorHandler);
208
Tim Waugh7c56b4c2014-10-17 10:28:55 +0100209 if (!XShmAttach(fl_display, shminfo)) {
210 XSetErrorHandler(old_handler);
211 goto free_shmaddr;
212 }
213
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000214 XSync(fl_display, False);
215
216 XSetErrorHandler(old_handler);
217
218 if (caughtError)
219 goto free_shmaddr;
Pierre Ossman13500692011-06-13 11:23:08 +0000220
Pierre Ossmanc9dd3a42015-11-18 16:24:16 +0100221 // FLTK is a bit stupid and unreliable if you register the same
222 // callback with different data values.
223 Fl::add_system_handler(handleSystemEvent, NULL);
224 shmList.push_back(this);
225
Pierre Ossman13500692011-06-13 11:23:08 +0000226 vlog.debug("Using shared memory XImage");
227
228 return 1;
229
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000230free_shmaddr:
231 shmdt(shminfo->shmaddr);
232
Pierre Ossman13500692011-06-13 11:23:08 +0000233free_shm:
234 shmctl(shminfo->shmid, IPC_RMID, 0);
235
236free_xim:
237 XDestroyImage(xim);
238 xim = NULL;
239
240free_shminfo:
241 delete shminfo;
242 shminfo = NULL;
243
244 return 0;
245}
Pierre Ossmanc9dd3a42015-11-18 16:24:16 +0100246
247int X11PixelBuffer::handleSystemEvent(void* event, void* data)
248{
249 XEvent* xevent;
250 XShmCompletionEvent* shmevent;
251
252 std::list<X11PixelBuffer*>::iterator iter;
253
254 xevent = (XEvent*)event;
255 assert(xevent);
256
Pierre Ossman9927d252016-05-04 17:52:49 +0200257 if (xevent->type != XShmGetEventBase(fl_display) + ShmCompletion)
Pierre Ossmanc9dd3a42015-11-18 16:24:16 +0100258 return 0;
259
260 shmevent = (XShmCompletionEvent*)event;
261
Pierre Ossman919ae452016-05-04 17:51:19 +0200262 if (shmevent->send_event)
263 return 0;
264
Pierre Ossmanc9dd3a42015-11-18 16:24:16 +0100265 for (iter = shmList.begin();iter != shmList.end();++iter) {
266 if (shmevent->shmseg != (*iter)->shminfo->shmseg)
267 continue;
268
Pierre Ossman919ae452016-05-04 17:51:19 +0200269 /* HP has a buggy X server on their thin clients that sends bogus
270 * extra events with an incorrect drawable id */
271 if (shmevent->drawable != (*iter)->pendingDrawable)
272 continue;
273
Pierre Ossmanc9dd3a42015-11-18 16:24:16 +0100274 (*iter)->pendingPutImage--;
275 assert((*iter)->pendingPutImage >= 0);
276
277 return 1;
278 }
279
280 return 0;
281}