The Pedigree Project 0.1
fb.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <errno.h>
21#include <fcntl.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <unistd.h>
26
27#include "pedigree_fb.h"
28#include <pedigree/fb.h>
29#include <pedigree/log.h>
30#include <sys/ioctl.h>
31#include <sys/kd.h>
32#include <sys/mman.h>
33
34namespace {
35Framebuffer* terminalFramebuffer = nullptr;
36
37sigset_t terminalSignals() {
38 sigset_t signals;
39 sigemptyset(&signals);
40 sigaddset(&signals, SIGUSR1);
41 sigaddset(&signals, SIGUSR2);
42 return signals;
43}
44} // namespace
45
46Framebuffer::Framebuffer()
47 : m_pFramebuffer(0),
48 m_FramebufferSize(0),
49 m_Format(),
50 m_Width(0),
51 m_Height(0),
52 m_BytesPerLine(0),
53 m_Fb(-1),
54 m_bStoredMode(false),
55 m_StoredMode() {}
56
57Framebuffer::~Framebuffer() {
58 releaseTerminal();
59 if (m_pFramebuffer) {
60 munmap(m_pFramebuffer, m_FramebufferSize);
61 m_pFramebuffer = 0;
62 m_FramebufferSize = 0;
63 }
64
65 if (m_Fb >= 0) {
66 close(m_Fb);
67 m_Fb = -1;
68 }
69}
70
71Framebuffer::FrameGuard::FrameGuard(const Framebuffer& framebuffer)
72 : m_Masked(false), m_Active(false) {
73 if (framebuffer.m_Terminal < 0) {
74 m_Active = true;
75 return;
76 }
77 const sigset_t signals = terminalSignals();
78 m_Masked = sigprocmask(SIG_BLOCK, &signals, &m_PreviousMask) == 0;
79 m_Active = m_Masked && framebuffer.m_TerminalActive;
80}
81
82Framebuffer::FrameGuard::~FrameGuard() {
83 if (m_Masked) {
84 sigprocmask(SIG_SETMASK, &m_PreviousMask, nullptr);
85 }
86}
87
89 if (m_Terminal >= 0) {
90 return true;
91 }
92 if (terminalFramebuffer) {
93 return false;
94 }
95 int terminal = open("/dev/tty0", O_RDWR | O_NOCTTY | O_CLOEXEC);
96 if (terminal < 0) {
97 return false;
98 }
99 vt_stat state = {};
100 if (ioctl(terminal, VT_GETSTATE, &state) < 0 || !state.v_active) {
101 close(terminal);
102 return false;
103 }
104 const unsigned number = state.v_active;
105 close(terminal);
106 char path[32];
107 snprintf(path, sizeof(path), "/dev/tty%u", number);
108 terminal = open(path, O_RDWR | O_NOCTTY | O_CLOEXEC);
109 if (terminal < 0) {
110 return false;
111 }
112 if (ioctl(terminal, VT_GETMODE, &m_PreviousTerminalMode) < 0 ||
113 m_PreviousTerminalMode.mode != VT_AUTO ||
114 ioctl(terminal, KDGETMODE, &m_PreviousDisplayMode) < 0) {
115 close(terminal);
116 return false;
117 }
118 const sigset_t signals = terminalSignals();
119 if (sigprocmask(SIG_BLOCK, &signals, &m_PreviousTerminalMask) < 0) {
120 close(terminal);
121 return false;
122 }
123 struct sigaction action = {};
124 action.sa_handler = terminalSignal;
125 action.sa_mask = signals;
126 if (sigaction(SIGUSR1, &action, &m_PreviousReleaseAction) < 0) {
127 close(terminal);
128 sigprocmask(SIG_SETMASK, &m_PreviousTerminalMask, nullptr);
129 return false;
130 }
131 if (sigaction(SIGUSR2, &action, &m_PreviousAcquireAction) < 0) {
132 sigaction(SIGUSR1, &m_PreviousReleaseAction, nullptr);
133 close(terminal);
134 sigprocmask(SIG_SETMASK, &m_PreviousTerminalMask, nullptr);
135 return false;
136 }
137 m_Terminal = terminal;
138 terminalFramebuffer = this;
139 vt_mode mode = {};
140 mode.mode = VT_PROCESS;
141 mode.relsig = SIGUSR1;
142 mode.acqsig = SIGUSR2;
143 if (ioctl(terminal, VT_SETMODE, &mode) < 0 || ioctl(terminal, KDSETMODE, KD_GRAPHICS) < 0 ||
144 ioctl(terminal, VT_GETSTATE, &state) < 0) {
145 releaseTerminal();
146 return false;
147 }
148 m_TerminalActive = state.v_active == number;
149 sigprocmask(SIG_UNBLOCK, &signals, nullptr);
150 return true;
151}
152
153void Framebuffer::terminalSignal(int signal) {
154 const int previousError = errno;
155 Framebuffer* framebuffer = terminalFramebuffer;
156 if (framebuffer) {
157 if (signal == SIGUSR1) {
158 framebuffer->m_TerminalActive = 0;
159 if (ioctl(framebuffer->m_Terminal, VT_RELDISP, 1) < 0) {
160 framebuffer->m_TerminalActive = 1;
161 }
162 } else if (signal == SIGUSR2) {
163 if (ioctl(framebuffer->m_Terminal, VT_RELDISP, VT_ACKACQ) == 0) {
164 framebuffer->m_TerminalActive = 1;
165 framebuffer->m_TerminalRedraw = 1;
166 }
167 }
168 }
169 errno = previousError;
170}
171
173 FrameGuard guard(*this);
174 const bool redraw = guard && m_TerminalRedraw;
175 if (redraw) {
176 m_TerminalRedraw = 0;
177 }
178 return redraw;
179}
180
181void Framebuffer::releaseTerminal() {
182 if (m_Terminal < 0) {
183 return;
184 }
185 const sigset_t signals = terminalSignals();
186 sigset_t mask;
187 sigprocmask(SIG_BLOCK, &signals, &mask);
188 ioctl(m_Terminal, KDSETMODE, m_PreviousDisplayMode);
189 ioctl(m_Terminal, VT_SETMODE, &m_PreviousTerminalMode);
190 // Notifications queued before ownership was returned must not reach the
191 // application's previous handlers after this object is destroyed.
192 const timespec timeout = {};
193 while (sigtimedwait(&signals, nullptr, &timeout) >= 0) {
194 }
195 terminalFramebuffer = nullptr;
196 sigaction(SIGUSR1, &m_PreviousReleaseAction, nullptr);
197 sigaction(SIGUSR2, &m_PreviousAcquireAction, nullptr);
198 close(m_Terminal);
199 m_Terminal = -1;
200 const int notifications[] = {SIGUSR1, SIGUSR2};
201 for (const int signal : notifications) {
202 if (sigismember(&m_PreviousTerminalMask, signal)) {
203 sigaddset(&mask, signal);
204 } else {
205 sigdelset(&mask, signal);
206 }
207 }
208 sigprocmask(SIG_SETMASK, &mask, nullptr);
209}
210
212 // Grab a framebuffer to use.
213 m_Fb = open("/dev/fb", O_RDWR);
214 if (m_Fb < 0) {
215 pedigree_log(LOG_INFO, "libfb: no framebuffer device");
216 fprintf(stderr, "libfb: couldn't open framebuffer device");
217 return false;
218 }
219
220 return true;
221}
222
224 if (m_bStoredMode)
225 return;
226
227 // Grab the current mode so we can restore it if we die.
228 pedigree_fb_mode current_mode;
229 int result = ioctl(m_Fb, PEDIGREE_FB_GETMODE, &current_mode);
230 if (result == 0) {
231 m_bStoredMode = true;
232 m_StoredMode = current_mode;
233 }
234}
235
237 if (!m_bStoredMode)
238 return;
239
240 // Restore old graphics mode.
241 pedigree_fb_modeset old_mode = {m_StoredMode.width, m_StoredMode.height, m_StoredMode.depth};
242 ioctl(m_Fb, PEDIGREE_FB_SETMODE, &old_mode);
243
244 m_bStoredMode = false;
245}
246
247int Framebuffer::mapMode(const pedigree_fb_mode& set_mode) {
248 // All good, framebuffer already exists.
249 if (m_pFramebuffer)
250 return 0;
251
252 if (!set_mode.width || !set_mode.height || !set_mode.depth) {
253 pedigree_log(LOG_INFO, "libfb: current mode is not a graphics mode");
254 return EXIT_FAILURE;
255 }
256
257 m_Width = set_mode.width;
258 m_Height = set_mode.height;
259
260 m_Format = CAIRO_FORMAT_ARGB32;
261 if (set_mode.format == PEDIGREE_FB_FORMAT_RGB24) {
262 if (set_mode.bytes_per_pixel != 4) {
263 fprintf(stderr,
264 "libfb: error: incompatible framebuffer format (bytes "
265 "per pixel)\n");
266 return EXIT_FAILURE;
267 }
268 } else if (set_mode.format == PEDIGREE_FB_FORMAT_RGB565) {
269 m_Format = CAIRO_FORMAT_RGB16_565;
270 } else if (set_mode.format > PEDIGREE_FB_FORMAT_RGB32) {
271 fprintf(stderr,
272 "libfb: error: incompatible framebuffer format (possibly "
273 "BGR or similar)\n");
274 return EXIT_FAILURE;
275 }
276
277 const size_t minimumStride = cairo_format_stride_for_width(m_Format, set_mode.width);
278 const size_t stride = set_mode.bytes_per_line ? set_mode.bytes_per_line : minimumStride;
279 if (stride < minimumStride || set_mode.height > SIZE_MAX / stride) {
280 pedigree_log(LOG_CRIT, "libfb: invalid framebuffer stride");
281 return EXIT_FAILURE;
282 }
283
284 // Map the framebuffer in to our address space.
285 pedigree_log(LOG_INFO, "Mapping /dev/fb in (sz=%zx)...", stride * set_mode.height);
286 m_pFramebuffer = mmap(0, stride * set_mode.height, PROT_READ | PROT_WRITE, MAP_SHARED, m_Fb, 0);
287 pedigree_log(LOG_INFO, "Got %p...", m_pFramebuffer);
288
289 if (m_pFramebuffer == MAP_FAILED) {
290 m_pFramebuffer = 0;
291 pedigree_log(LOG_CRIT, "libfb: couldn't map framebuffer into address space");
292 return EXIT_FAILURE;
293 } else {
294 pedigree_log(LOG_INFO, "libfb: mapped framebuffer at %p", m_pFramebuffer);
295 }
296
297 m_FramebufferSize = stride * set_mode.height;
298 m_BytesPerLine = stride;
299
300 return 0;
301}
302
304 if (m_pFramebuffer)
305 return 0;
306
307 pedigree_fb_mode current_mode;
308 if (ioctl(m_Fb, PEDIGREE_FB_GETMODE, &current_mode) < 0) {
309 pedigree_log(LOG_INFO, "libfb: can't get current mode info");
310 return EXIT_FAILURE;
311 }
312
313 return mapMode(current_mode);
314}
315
316int Framebuffer::enterMode(size_t desiredW, size_t desiredH, size_t desiredBpp) {
317 // All good, framebuffer already exists.
318 if (m_pFramebuffer)
319 return 0;
320
321 // Can we set the graphics mode we want?
322 pedigree_fb_modeset mode = {desiredW, desiredH, desiredBpp};
323 int result = ioctl(m_Fb, PEDIGREE_FB_SETMODE, &mode);
324 if (result < 0) {
325 // No! Bad!
329 pedigree_log(LOG_INFO, "libfb: can't set the desired mode");
330 fprintf(stderr, "libfb: could not set desired mode (%zux%zu) in any colour depth.\n",
331 mode.width, mode.height);
332 return EXIT_FAILURE;
333 }
334
335 pedigree_fb_mode set_mode;
336 result = ioctl(m_Fb, PEDIGREE_FB_GETMODE, &set_mode);
337 if (result < 0) {
338 pedigree_log(LOG_INFO, "libfb: can't get mode info");
339 fprintf(stderr, "libfb: could not get mode information after setting mode.\n");
340
341 // Back to text.
342 memset(&mode, 0, sizeof(mode));
343 ioctl(m_Fb, PEDIGREE_FB_SETMODE, &mode);
344 return EXIT_FAILURE;
345 }
346
347 return mapMode(set_mode);
348}
349
350void Framebuffer::flush(size_t x, size_t y, size_t w, size_t h) {
351 // Submit a redraw to the graphics card.
352 pedigree_fb_rect fbdirty = {x, y, w, h};
353 ioctl(m_Fb, PEDIGREE_FB_REDRAW, &fbdirty);
354}
bool initialise()
Definition fb.cc:211
void flush(size_t x, size_t y, size_t w, size_t h)
Definition fb.cc:350
bool takeRedraw()
Definition fb.cc:172
void restoreMode()
Definition fb.cc:236
void storeMode()
Definition fb.cc:223
bool claimTerminal()
Definition fb.cc:88
int enterMode(size_t desiredW, size_t desiredH, size_t desiredBpp)
Definition fb.cc:316
void redraw(size_t x=~0UL, size_t y=~0UL, size_t w=~0UL, size_t h=~0UL, bool bChild=false)
int useCurrentMode()
Definition fb.cc:303
All zeroes = 'revert to text mode'.
Definition fb.h:51