blob: 3675eb57a6074ce3075e77ced16507cacccd4ad5 [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
32#include "X11PixelBuffer.h"
33
34using namespace rfb;
35
Pierre Ossmanac13abe2014-02-07 14:46:26 +010036static rfb::LogWriter vlog("X11PixelBuffer");
Pierre Ossman13500692011-06-13 11:23:08 +000037
38static PixelFormat display_pf()
39{
40 int i;
41
42 int bpp;
43 int trueColour, bigEndian;
44 int redShift, greenShift, blueShift;
45 int redMax, greenMax, blueMax;
46
47 int nformats;
48 XPixmapFormatValues* format;
49
50 // Might not be open at this point
51 fl_open_display();
52
53 format = XListPixmapFormats(fl_display, &nformats);
54
55 for (i = 0; i < nformats; i++)
56 if (format[i].depth == fl_visual->depth) break;
57
58 if (i == nformats)
59 throw rfb::Exception("Error: display lacks pixmap format for default depth");
60
61 switch (format[i].bits_per_pixel) {
62 case 8:
63 case 16:
64 case 32:
65 bpp = format[i].bits_per_pixel;
66 break;
67 default:
68 throw rfb::Exception("Error: couldn't find suitable pixmap format");
69 }
70
71 XFree(format);
72
73 bigEndian = (ImageByteOrder(fl_display) == MSBFirst);
74 trueColour = (fl_visual->c_class == TrueColor);
75
76 if (!trueColour)
77 throw rfb::Exception("Error: only true colour displays supported");
78
79 vlog.info("Using default colormap and visual, %sdepth %d.",
80 (fl_visual->c_class == TrueColor) ? "TrueColor, " :
81 ((fl_visual->c_class == PseudoColor) ? "PseudoColor, " : ""),
82 fl_visual->depth);
83
84 redShift = ffs(fl_visual->red_mask) - 1;
85 greenShift = ffs(fl_visual->green_mask) - 1;
86 blueShift = ffs(fl_visual->blue_mask) - 1;
87 redMax = fl_visual->red_mask >> redShift;
88 greenMax = fl_visual->green_mask >> greenShift;
89 blueMax = fl_visual->blue_mask >> blueShift;
90
91 return PixelFormat(bpp, fl_visual->depth, bigEndian, trueColour,
92 redMax, greenMax, blueMax,
93 redShift, greenShift, blueShift);
94}
95
Pierre Ossmanac13abe2014-02-07 14:46:26 +010096X11PixelBuffer::X11PixelBuffer(int width, int height) :
97 PlatformPixelBuffer(display_pf(), width, height, NULL),
Pierre Ossman13500692011-06-13 11:23:08 +000098 shminfo(NULL), xim(NULL)
99{
100 // Might not be open at this point
101 fl_open_display();
102
103 if (!setupShm()) {
104 xim = XCreateImage(fl_display, fl_visual->visual, fl_visual->depth,
105 ZPixmap, 0, 0, width, height, BitmapPad(fl_display), 0);
106 assert(xim);
107
108 xim->data = (char*)malloc(xim->bytes_per_line * xim->height);
109 assert(xim->data);
110 }
111
112 data = (rdr::U8*)xim->data;
113}
114
115
Pierre Ossmanac13abe2014-02-07 14:46:26 +0100116X11PixelBuffer::~X11PixelBuffer()
Pierre Ossman13500692011-06-13 11:23:08 +0000117{
118 if (shminfo) {
119 vlog.debug("Freeing shared memory XImage");
120 shmdt(shminfo->shmaddr);
121 shmctl(shminfo->shmid, IPC_RMID, 0);
122 delete shminfo;
123 shminfo = NULL;
124 }
125
126 // XDestroyImage() will free(xim->data) if appropriate
127 if (xim)
128 XDestroyImage(xim);
129 xim = NULL;
130}
131
132
Pierre Ossmanac13abe2014-02-07 14:46:26 +0100133void X11PixelBuffer::draw(int src_x, int src_y, int x, int y, int w, int h)
Pierre Ossman13500692011-06-13 11:23:08 +0000134{
135 if (shminfo)
136 XShmPutImage(fl_display, fl_window, fl_gc, xim, src_x, src_y, x, y, w, h, False);
137 else
138 XPutImage(fl_display, fl_window, fl_gc, xim, src_x, src_y, x, y, w, h);
139}
140
141
Pierre Ossmanac13abe2014-02-07 14:46:26 +0100142int X11PixelBuffer::getStride() const
Pierre Ossman13500692011-06-13 11:23:08 +0000143{
144 return xim->bytes_per_line / (getPF().bpp/8);
145}
146
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000147static bool caughtError;
148
149static int XShmAttachErrorHandler(Display *dpy, XErrorEvent *error)
150{
151 caughtError = true;
152 return 0;
153}
Pierre Ossman13500692011-06-13 11:23:08 +0000154
Pierre Ossmanac13abe2014-02-07 14:46:26 +0100155int X11PixelBuffer::setupShm()
Pierre Ossman13500692011-06-13 11:23:08 +0000156{
157 int major, minor;
158 Bool pixmaps;
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000159 XErrorHandler old_handler;
160 Status status;
Pierre Ossman13500692011-06-13 11:23:08 +0000161
162 if (!XShmQueryVersion(fl_display, &major, &minor, &pixmaps))
163 return 0;
164
165 shminfo = new XShmSegmentInfo;
166
167 xim = XShmCreateImage(fl_display, fl_visual->visual, fl_visual->depth,
168 ZPixmap, 0, shminfo, width(), height());
169 if (!xim)
170 goto free_shminfo;
171
172 shminfo->shmid = shmget(IPC_PRIVATE,
173 xim->bytes_per_line * xim->height,
174 IPC_CREAT|0777);
175 if (shminfo->shmid == -1)
176 goto free_xim;
177
178 shminfo->shmaddr = xim->data = (char*)shmat(shminfo->shmid, 0, 0);
179 if (shminfo->shmaddr == (char *)-1)
180 goto free_shm;
181
182 shminfo->readOnly = True;
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000183
184 // This is the only way we can detect that shared memory won't work
185 // (e.g. because we're accessing a remote X11 server)
186 caughtError = false;
187 old_handler = XSetErrorHandler(XShmAttachErrorHandler);
188
Pierre Ossman13500692011-06-13 11:23:08 +0000189 XShmAttach(fl_display, shminfo);
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000190 XSync(fl_display, False);
191
192 XSetErrorHandler(old_handler);
193
194 if (caughtError)
195 goto free_shmaddr;
Pierre Ossman13500692011-06-13 11:23:08 +0000196
197 vlog.debug("Using shared memory XImage");
198
199 return 1;
200
Pierre Ossman4d01a9e2011-06-17 08:35:22 +0000201free_shmaddr:
202 shmdt(shminfo->shmaddr);
203
Pierre Ossman13500692011-06-13 11:23:08 +0000204free_shm:
205 shmctl(shminfo->shmid, IPC_RMID, 0);
206
207free_xim:
208 XDestroyImage(xim);
209 xim = NULL;
210
211free_shminfo:
212 delete shminfo;
213 shminfo = NULL;
214
215 return 0;
216}