blob: e23f77719494a705f49186b050a65138a7b578b6 [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
40static PixelFormat display_pf()
41{
42 int i;
43
44 int bpp;
45 int trueColour, bigEndian;
46 int redShift, greenShift, blueShift;
47 int redMax, greenMax, blueMax;
48
49 int nformats;
50 XPixmapFormatValues* format;
51
52 // Might not be open at this point
53 fl_open_display();
54
55 format = XListPixmapFormats(fl_display, &nformats);
56
57 for (i = 0; i < nformats; i++)
58 if (format[i].depth == fl_visual->depth) break;
59
60 if (i == nformats)
Pierre Ossman744e55c2014-12-03 14:00:54 +010061 // TRANSLATORS: "pixmap" is an X11 concept and may not be suitable
62 // to translate.
Pierre Ossman86750632014-10-10 13:33:19 +020063 throw rfb::Exception(_("Display lacks pixmap format for default depth"));
Pierre Ossman13500692011-06-13 11:23:08 +000064
65 switch (format[i].bits_per_pixel) {
66 case 8:
67 case 16:
68 case 32:
69 bpp = format[i].bits_per_pixel;
70 break;
71 default:
Pierre Ossman744e55c2014-12-03 14:00:54 +010072 // TRANSLATORS: "pixmap" is an X11 concept and may not be suitable
73 // to translate.
Pierre Ossman86750632014-10-10 13:33:19 +020074 throw rfb::Exception(_("Couldn't find suitable pixmap format"));
Pierre Ossman13500692011-06-13 11:23:08 +000075 }
76
77 XFree(format);
78
79 bigEndian = (ImageByteOrder(fl_display) == MSBFirst);
80 trueColour = (fl_visual->c_class == TrueColor);
81
82 if (!trueColour)
Pierre Ossman86750632014-10-10 13:33:19 +020083 throw rfb::Exception(_("Only true colour displays supported"));
Pierre Ossman13500692011-06-13 11:23:08 +000084
Pierre Ossman2aef35c2014-12-03 14:01:56 +010085 vlog.info(_("Using default colormap and visual, TrueColor, depth %d."),
Pierre Ossman13500692011-06-13 11:23:08 +000086 fl_visual->depth);
87
88 redShift = ffs(fl_visual->red_mask) - 1;
89 greenShift = ffs(fl_visual->green_mask) - 1;
90 blueShift = ffs(fl_visual->blue_mask) - 1;
91 redMax = fl_visual->red_mask >> redShift;
92 greenMax = fl_visual->green_mask >> greenShift;
93 blueMax = fl_visual->blue_mask >> blueShift;
94
95 return PixelFormat(bpp, fl_visual->depth, bigEndian, trueColour,
96 redMax, greenMax, blueMax,
97 redShift, greenShift, blueShift);
98}
99
Pierre Ossmanac13abe2014-02-07 14:46:26 +0100100X11PixelBuffer::X11PixelBuffer(int width, int height) :
Pierre Ossman2e5a1062014-01-30 17:57:27 +0100101 PlatformPixelBuffer(display_pf(), width, height, NULL, 0),
Pierre Ossman0f626ad2017-01-11 18:32:16 +0100102 shminfo(NULL), xim(NULL)
Pierre Ossman13500692011-06-13 11:23:08 +0000103{
104 // Might not be open at this point
105 fl_open_display();
106
107 if (!setupShm()) {
108 xim = XCreateImage(fl_display, fl_visual->visual, fl_visual->depth,
109 ZPixmap, 0, 0, width, height, BitmapPad(fl_display), 0);
Pierre Ossmand1a853b2014-10-10 13:37:35 +0200110 if (!xim)
Pierre Ossmanfefd9002016-12-17 14:04:39 +0100111 throw rfb::Exception("XCreateImage");
Pierre Ossman13500692011-06-13 11:23:08 +0000112
113 xim->data = (char*)malloc(xim->bytes_per_line * xim->height);
Pierre Ossmand1a853b2014-10-10 13:37:35 +0200114 if (!xim->data)
115 throw rfb::Exception(_("Not enough memory for framebuffer"));
Pierre Ossmanfefd9002016-12-17 14:04:39 +0100116
117 vlog.debug("Using standard XImage");
Pierre Ossman13500692011-06-13 11:23:08 +0000118 }
119
120 data = (rdr::U8*)xim->data;
Pierre Ossman2e5a1062014-01-30 17:57:27 +0100121 stride = xim->bytes_per_line / (getPF().bpp/8);
Pierre Ossman13500692011-06-13 11:23:08 +0000122}
123
124
Pierre Ossmanac13abe2014-02-07 14:46:26 +0100125X11PixelBuffer::~X11PixelBuffer()
Pierre Ossman13500692011-06-13 11:23:08 +0000126{
127 if (shminfo) {
128 vlog.debug("Freeing shared memory XImage");
129 shmdt(shminfo->shmaddr);
130 shmctl(shminfo->shmid, IPC_RMID, 0);
131 delete shminfo;
132 shminfo = NULL;
133 }
134
135 // XDestroyImage() will free(xim->data) if appropriate
136 if (xim)
137 XDestroyImage(xim);
138 xim = NULL;
139}
140
141
Pierre Ossmanac13abe2014-02-07 14:46:26 +0100142void X11PixelBuffer::draw(int src_x, int src_y, int x, int y, int w, int h)
Pierre Ossman13500692011-06-13 11:23:08 +0000143{
Pierre Ossmanc9dd3a42015-11-18 16:24:16 +0100144 if (shminfo) {
Pierre Ossman0f626ad2017-01-11 18:32:16 +0100145 XShmPutImage(fl_display, fl_window, fl_gc, xim, src_x, src_y, x, y, w, h, False);
146 // Need to make sure the X server has finished reading the
147 // shared memory before we return
148 XSync(fl_display, False);
Pierre Ossmanc9dd3a42015-11-18 16:24:16 +0100149 } else {
Pierre Ossman13500692011-06-13 11:23:08 +0000150 XPutImage(fl_display, fl_window, fl_gc, xim, src_x, src_y, x, y, w, h);
Pierre Ossmanc9dd3a42015-11-18 16:24:16 +0100151 }
Pierre Ossman13500692011-06-13 11:23:08 +0000152}
153
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000154static bool caughtError;
155
156static int XShmAttachErrorHandler(Display *dpy, XErrorEvent *error)
157{
158 caughtError = true;
159 return 0;
160}
Pierre Ossman13500692011-06-13 11:23:08 +0000161
Pierre Ossmanac13abe2014-02-07 14:46:26 +0100162int X11PixelBuffer::setupShm()
Pierre Ossman13500692011-06-13 11:23:08 +0000163{
164 int major, minor;
165 Bool pixmaps;
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000166 XErrorHandler old_handler;
Tim Waughb17c9c42014-10-16 14:53:17 +0100167 const char *display_name = XDisplayName (NULL);
168
169 /* Don't use MIT-SHM on remote displays */
170 if (*display_name && *display_name != ':')
171 return 0;
Pierre Ossman13500692011-06-13 11:23:08 +0000172
173 if (!XShmQueryVersion(fl_display, &major, &minor, &pixmaps))
174 return 0;
175
176 shminfo = new XShmSegmentInfo;
177
178 xim = XShmCreateImage(fl_display, fl_visual->visual, fl_visual->depth,
179 ZPixmap, 0, shminfo, width(), height());
180 if (!xim)
181 goto free_shminfo;
182
183 shminfo->shmid = shmget(IPC_PRIVATE,
184 xim->bytes_per_line * xim->height,
185 IPC_CREAT|0777);
186 if (shminfo->shmid == -1)
187 goto free_xim;
188
189 shminfo->shmaddr = xim->data = (char*)shmat(shminfo->shmid, 0, 0);
TSUKAHARA Ken1d2556f2017-01-20 00:21:39 +0900190 shmctl(shminfo->shmid, IPC_RMID, 0); // to avoid memory leakage
Pierre Ossman13500692011-06-13 11:23:08 +0000191 if (shminfo->shmaddr == (char *)-1)
TSUKAHARA Ken1d2556f2017-01-20 00:21:39 +0900192 goto free_xim;
Pierre Ossman13500692011-06-13 11:23:08 +0000193
194 shminfo->readOnly = True;
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000195
196 // This is the only way we can detect that shared memory won't work
197 // (e.g. because we're accessing a remote X11 server)
198 caughtError = false;
199 old_handler = XSetErrorHandler(XShmAttachErrorHandler);
200
Tim Waugh7c56b4c2014-10-17 10:28:55 +0100201 if (!XShmAttach(fl_display, shminfo)) {
202 XSetErrorHandler(old_handler);
203 goto free_shmaddr;
204 }
205
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000206 XSync(fl_display, False);
207
208 XSetErrorHandler(old_handler);
209
210 if (caughtError)
211 goto free_shmaddr;
Pierre Ossman13500692011-06-13 11:23:08 +0000212
213 vlog.debug("Using shared memory XImage");
214
215 return 1;
216
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000217free_shmaddr:
218 shmdt(shminfo->shmaddr);
219
Pierre Ossman13500692011-06-13 11:23:08 +0000220free_xim:
221 XDestroyImage(xim);
222 xim = NULL;
223
224free_shminfo:
225 delete shminfo;
226 shminfo = NULL;
227
228 return 0;
229}