blob: d84357c42d5927fe7938c2d8bce70f3f03de0d9a [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 Ossman13500692011-06-13 11:23:08 +000024#include <assert.h>
25#include <stdlib.h>
26
27#include <FL/x.H>
28
29#include <rfb/LogWriter.h>
30#include <rfb/Exception.h>
31
Pierre Ossman8ca4c1d2014-09-22 12:54:26 +020032#include "i18n.h"
Pierre Ossman13500692011-06-13 11:23:08 +000033#include "X11PixelBuffer.h"
34
35using namespace rfb;
36
Pierre Ossmanac13abe2014-02-07 14:46:26 +010037static rfb::LogWriter vlog("X11PixelBuffer");
Pierre Ossman13500692011-06-13 11:23:08 +000038
39static PixelFormat display_pf()
40{
41 int i;
42
43 int bpp;
44 int trueColour, bigEndian;
45 int redShift, greenShift, blueShift;
46 int redMax, greenMax, blueMax;
47
48 int nformats;
49 XPixmapFormatValues* format;
50
51 // Might not be open at this point
52 fl_open_display();
53
54 format = XListPixmapFormats(fl_display, &nformats);
55
56 for (i = 0; i < nformats; i++)
57 if (format[i].depth == fl_visual->depth) break;
58
59 if (i == nformats)
Pierre Ossman86750632014-10-10 13:33:19 +020060 throw rfb::Exception(_("Display lacks pixmap format for default depth"));
Pierre Ossman13500692011-06-13 11:23:08 +000061
62 switch (format[i].bits_per_pixel) {
63 case 8:
64 case 16:
65 case 32:
66 bpp = format[i].bits_per_pixel;
67 break;
68 default:
Pierre Ossman86750632014-10-10 13:33:19 +020069 throw rfb::Exception(_("Couldn't find suitable pixmap format"));
Pierre Ossman13500692011-06-13 11:23:08 +000070 }
71
72 XFree(format);
73
74 bigEndian = (ImageByteOrder(fl_display) == MSBFirst);
75 trueColour = (fl_visual->c_class == TrueColor);
76
77 if (!trueColour)
Pierre Ossman86750632014-10-10 13:33:19 +020078 throw rfb::Exception(_("Only true colour displays supported"));
Pierre Ossman13500692011-06-13 11:23:08 +000079
Pierre Ossman8ca4c1d2014-09-22 12:54:26 +020080 vlog.info(_("Using default colormap and visual, %sdepth %d."),
Pierre Ossman13500692011-06-13 11:23:08 +000081 (fl_visual->c_class == TrueColor) ? "TrueColor, " :
82 ((fl_visual->c_class == PseudoColor) ? "PseudoColor, " : ""),
83 fl_visual->depth);
84
85 redShift = ffs(fl_visual->red_mask) - 1;
86 greenShift = ffs(fl_visual->green_mask) - 1;
87 blueShift = ffs(fl_visual->blue_mask) - 1;
88 redMax = fl_visual->red_mask >> redShift;
89 greenMax = fl_visual->green_mask >> greenShift;
90 blueMax = fl_visual->blue_mask >> blueShift;
91
92 return PixelFormat(bpp, fl_visual->depth, bigEndian, trueColour,
93 redMax, greenMax, blueMax,
94 redShift, greenShift, blueShift);
95}
96
Pierre Ossmanac13abe2014-02-07 14:46:26 +010097X11PixelBuffer::X11PixelBuffer(int width, int height) :
Pierre Ossman2e5a1062014-01-30 17:57:27 +010098 PlatformPixelBuffer(display_pf(), width, height, NULL, 0),
Pierre Ossman13500692011-06-13 11:23:08 +000099 shminfo(NULL), xim(NULL)
100{
101 // Might not be open at this point
102 fl_open_display();
103
104 if (!setupShm()) {
105 xim = XCreateImage(fl_display, fl_visual->visual, fl_visual->depth,
106 ZPixmap, 0, 0, width, height, BitmapPad(fl_display), 0);
107 assert(xim);
108
109 xim->data = (char*)malloc(xim->bytes_per_line * xim->height);
110 assert(xim->data);
111 }
112
113 data = (rdr::U8*)xim->data;
Pierre Ossman2e5a1062014-01-30 17:57:27 +0100114 stride = xim->bytes_per_line / (getPF().bpp/8);
Pierre Ossman13500692011-06-13 11:23:08 +0000115}
116
117
Pierre Ossmanac13abe2014-02-07 14:46:26 +0100118X11PixelBuffer::~X11PixelBuffer()
Pierre Ossman13500692011-06-13 11:23:08 +0000119{
120 if (shminfo) {
121 vlog.debug("Freeing shared memory XImage");
122 shmdt(shminfo->shmaddr);
123 shmctl(shminfo->shmid, IPC_RMID, 0);
124 delete shminfo;
125 shminfo = NULL;
126 }
127
128 // XDestroyImage() will free(xim->data) if appropriate
129 if (xim)
130 XDestroyImage(xim);
131 xim = NULL;
132}
133
134
Pierre Ossmanac13abe2014-02-07 14:46:26 +0100135void X11PixelBuffer::draw(int src_x, int src_y, int x, int y, int w, int h)
Pierre Ossman13500692011-06-13 11:23:08 +0000136{
137 if (shminfo)
138 XShmPutImage(fl_display, fl_window, fl_gc, xim, src_x, src_y, x, y, w, h, False);
139 else
140 XPutImage(fl_display, fl_window, fl_gc, xim, src_x, src_y, x, y, w, h);
141}
142
143
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000144static bool caughtError;
145
146static int XShmAttachErrorHandler(Display *dpy, XErrorEvent *error)
147{
148 caughtError = true;
149 return 0;
150}
Pierre Ossman13500692011-06-13 11:23:08 +0000151
Pierre Ossmanac13abe2014-02-07 14:46:26 +0100152int X11PixelBuffer::setupShm()
Pierre Ossman13500692011-06-13 11:23:08 +0000153{
154 int major, minor;
155 Bool pixmaps;
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000156 XErrorHandler old_handler;
157 Status status;
Pierre Ossman13500692011-06-13 11:23:08 +0000158
159 if (!XShmQueryVersion(fl_display, &major, &minor, &pixmaps))
160 return 0;
161
162 shminfo = new XShmSegmentInfo;
163
164 xim = XShmCreateImage(fl_display, fl_visual->visual, fl_visual->depth,
165 ZPixmap, 0, shminfo, width(), height());
166 if (!xim)
167 goto free_shminfo;
168
169 shminfo->shmid = shmget(IPC_PRIVATE,
170 xim->bytes_per_line * xim->height,
171 IPC_CREAT|0777);
172 if (shminfo->shmid == -1)
173 goto free_xim;
174
175 shminfo->shmaddr = xim->data = (char*)shmat(shminfo->shmid, 0, 0);
176 if (shminfo->shmaddr == (char *)-1)
177 goto free_shm;
178
179 shminfo->readOnly = True;
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000180
181 // This is the only way we can detect that shared memory won't work
182 // (e.g. because we're accessing a remote X11 server)
183 caughtError = false;
184 old_handler = XSetErrorHandler(XShmAttachErrorHandler);
185
Pierre Ossman13500692011-06-13 11:23:08 +0000186 XShmAttach(fl_display, shminfo);
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000187 XSync(fl_display, False);
188
189 XSetErrorHandler(old_handler);
190
191 if (caughtError)
192 goto free_shmaddr;
Pierre Ossman13500692011-06-13 11:23:08 +0000193
194 vlog.debug("Using shared memory XImage");
195
196 return 1;
197
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000198free_shmaddr:
199 shmdt(shminfo->shmaddr);
200
Pierre Ossman13500692011-06-13 11:23:08 +0000201free_shm:
202 shmctl(shminfo->shmid, IPC_RMID, 0);
203
204free_xim:
205 XDestroyImage(xim);
206 xim = NULL;
207
208free_shminfo:
209 delete shminfo;
210 shminfo = NULL;
211
212 return 0;
213}