The Pedigree Project 0.1
tui.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 <Font.h>
21#include <Terminal.h>
22#include <Xterm.h>
23#include <signal.h>
24#include <stdint.h>
25#include <string.h>
26#include <tui.h>
27#include <unistd.h>
28
29#include <pedigree/log.h>
30#include <sys/select.h>
31
32struct TuiLocal {
33 Terminal* pTerminal = nullptr;
34
35 size_t nWidth = 0;
36 size_t nHeight = 0;
37
38 bool bKeyPressed = false;
39 bool bRunning = false;
40
41 cairo_t* pCairo = nullptr;
42 cairo_surface_t* pSurface = nullptr;
43
44 Font* pNormalFont = nullptr;
45 Font* pBoldFont = nullptr;
46};
47
49 : m_LocalData(nullptr), m_pWidget(nullptr), m_pRedrawer(pRedrawer) {
50 m_LocalData = new TuiLocal;
51}
52
53Tui::Tui(Widget* widget) : Tui(static_cast<TuiRedrawer*>(nullptr)) {
54 m_pWidget = widget;
55}
56
57Tui::~Tui() {
58 delete m_LocalData->pTerminal;
59 delete m_LocalData->pBoldFont;
60 delete m_LocalData->pNormalFont;
61
62 cairo_surface_destroy(m_LocalData->pSurface);
63 cairo_destroy(m_LocalData->pCairo);
64
65 delete m_LocalData;
66}
67
68bool Tui::initialise(size_t width, size_t height) {
69 m_LocalData->nWidth = width;
70 m_LocalData->nHeight = height;
71
72 if (!m_LocalData->pCairo) {
73 pedigree_log(LOG_ALERT, "TUI: cairo instance is not yet valid!");
74 return false;
75 }
76
77 cairo_set_line_cap(m_LocalData->pCairo, CAIRO_LINE_CAP_SQUARE);
78 cairo_set_line_join(m_LocalData->pCairo, CAIRO_LINE_JOIN_MITER);
79 cairo_set_antialias(m_LocalData->pCairo, CAIRO_ANTIALIAS_NONE);
80 cairo_set_line_width(m_LocalData->pCairo, 1.0);
81
82 cairo_set_operator(m_LocalData->pCairo, CAIRO_OPERATOR_SOURCE);
83 cairo_set_source_rgba(m_LocalData->pCairo, 0, 0, 0, 1.0);
84 cairo_paint(m_LocalData->pCairo);
85
86 if (!m_LocalData->pNormalFont) {
87 m_LocalData->pNormalFont = new Font(m_LocalData->pCairo, 14, "DejaVu Sans Mono 10", true, 0);
88 if (!m_LocalData->pNormalFont) {
89 pedigree_log(LOG_EMERG, "Error: Normal font not loaded!");
90 return false;
91 }
92 }
93
94 if (!m_LocalData->pBoldFont) {
95 m_LocalData->pBoldFont = new Font(m_LocalData->pCairo, 14, "DejaVu Sans Mono Bold 10", true, 0);
96 if (!m_LocalData->pBoldFont) {
97 pedigree_log(LOG_EMERG, "Error: Bold font not loaded!");
98 return false;
99 }
100 }
101
102 if (m_LocalData->pTerminal) {
103 delete m_LocalData->pTerminal;
104 }
105
106 char newTermName[256];
107 sprintf(newTermName, "Console%d", getpid());
108
109 DirtyRectangle rect;
110
111 m_LocalData->pTerminal = new Terminal(newTermName, m_LocalData->nWidth, m_LocalData->nHeight, 0,
112 0, 0, m_LocalData->pCairo, m_pWidget, this,
113 m_LocalData->pNormalFont, m_LocalData->pBoldFont);
114 m_LocalData->pTerminal->setCairo(m_LocalData->pCairo, m_LocalData->pSurface);
115 if (!m_LocalData->pTerminal->initialise()) {
116 delete m_LocalData->pTerminal;
117 m_LocalData->pTerminal = nullptr;
118 } else {
119 m_LocalData->pTerminal->setActive(true, rect);
120 m_LocalData->pTerminal->redrawAll(rect);
121 }
122
123 rect.point(0, 0);
124 rect.point(m_LocalData->nWidth, m_LocalData->nHeight);
125
126 if (!m_LocalData->pTerminal) {
127 pedigree_log(LOG_ALERT, "TUI: couldn't start up a terminal - failing gracefully...");
128 m_LocalData->pBoldFont->render("There are no pseudo-terminals available.", 5, 5, 0xFFFFFF,
129 0x000000, false);
130 m_LocalData->pBoldFont->render("Press any key to close this window.", 5,
131 m_LocalData->pBoldFont->getHeight() + 5, 0xFFFFFF, 0x000000,
132 false);
133
134 redraw(rect);
135
136 m_LocalData->bKeyPressed = false;
137 while (!m_LocalData->bKeyPressed) {
138 if (m_pWidget) {
140 } else {
141 // yield??
142 }
143 }
144
145 return false;
146 }
147
148 redraw(rect);
149
150 return true;
151}
152
153void Tui::setCursorStyle(bool filled) {
154 if (m_LocalData->pTerminal) {
155 DirtyRectangle dirty;
156 m_LocalData->pTerminal->setCursorStyle(filled);
157 m_LocalData->pTerminal->showCursor(dirty);
158 redraw(dirty);
159 }
160}
161
162void Tui::recreateSurfaces(void* fb) {
163 if (!(m_LocalData->nWidth && m_LocalData->nHeight)) {
164 // don't yet know the size of the framebuffer
165 return;
166 }
167
168 if (m_LocalData->pSurface) {
169 cairo_surface_destroy(m_LocalData->pSurface);
170 cairo_destroy(m_LocalData->pCairo);
171 }
172
173 // Wipe out the framebuffer before we do much with it.
174 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, m_LocalData->nWidth);
175 memset(fb, 0, m_LocalData->nHeight * stride);
176
177 m_LocalData->pSurface = cairo_image_surface_create_for_data(
178 (uint8_t*)fb, CAIRO_FORMAT_ARGB32, m_LocalData->nWidth, m_LocalData->nHeight, stride);
179 m_LocalData->pCairo = cairo_create(m_LocalData->pSurface);
180
181 if (m_LocalData->pTerminal) {
182 m_LocalData->pTerminal->setCairo(m_LocalData->pCairo, m_LocalData->pSurface);
183 }
184
185 if (m_LocalData->pNormalFont) {
186 m_LocalData->pNormalFont->updateCairo(m_LocalData->pCairo);
187 }
188
189 if (m_LocalData->pBoldFont) {
190 m_LocalData->pBoldFont->updateCairo(m_LocalData->pCairo);
191 }
192}
193
194void Tui::resize(size_t newWidth, size_t newHeight) {
195 m_LocalData->nWidth = newWidth;
196 m_LocalData->nHeight = newHeight;
197
198 if (!(m_LocalData->pTerminal && m_LocalData->pCairo)) {
199 // Nothing more to do for us.
200 return;
201 }
202
203 // Wipe out the framebuffer, start over.
204 cairo_set_operator(m_LocalData->pCairo, CAIRO_OPERATOR_SOURCE);
205 cairo_set_source_rgba(m_LocalData->pCairo, 0, 0, 0, 0.8);
206 cairo_rectangle(m_LocalData->pCairo, 0, 0, m_LocalData->nWidth, m_LocalData->nHeight);
207 cairo_fill(m_LocalData->pCairo);
208
209 if (m_LocalData->pTerminal) {
210 m_LocalData->pTerminal->renewBuffer(newWidth, newHeight);
211
212 DirtyRectangle rect;
213 m_LocalData->pTerminal->redrawAll(rect);
214 m_LocalData->pTerminal->showCursor(rect);
215 redraw(rect);
216
217 kill(m_LocalData->pTerminal->getPid(), SIGWINCH);
218 }
219}
220
221void Tui::run() {
222 size_t maxBuffSz = 32768;
223 char buffer[32768];
224
225 m_LocalData->bRunning = true;
226 while (m_LocalData->bRunning) {
227 int n = 0;
228
229 fd_set fds;
230 FD_ZERO(&fds);
231
232 if (m_pWidget) {
233 n = std::max(m_pWidget->getSocket(), n);
234 FD_SET(m_pWidget->getSocket(), &fds);
235 }
236
237 if (m_LocalData->pTerminal) {
238 if (!m_LocalData->pTerminal->isAlive()) {
239 m_LocalData->bRunning = false;
240 } else {
241 int fd = m_LocalData->pTerminal->getSelectFd();
242 FD_SET(fd, &fds);
243 n = std::max(fd, n);
244 }
245 }
246
247 if (!m_LocalData->bRunning) {
248 continue;
249 }
250
251 int nReady = select(n + 1, &fds, NULL, NULL, 0);
252 if (nReady <= 0) {
253 continue;
254 }
255
256 // Check for widget events.
257 if (m_pWidget) {
258 if (FD_ISSET(m_pWidget->getSocket(), &fds)) {
259 // Dispatch callbacks.
261
262 // Don't do redraw processing if this was the only descriptor
263 // that was found readable.
264 if (nReady == 1) {
265 continue;
266 }
267 }
268 }
269
270 bool bShouldRedraw = false;
271
272 DirtyRectangle dirtyRect;
273 if (m_LocalData->pTerminal) {
274 int fd = m_LocalData->pTerminal->getSelectFd();
275 if (FD_ISSET(fd, &fds)) {
276 // Something to read.
277 ssize_t len = read(fd, buffer, maxBuffSz);
278 if (len > 0) {
279 m_LocalData->pTerminal->write(buffer, static_cast<size_t>(len), dirtyRect);
280 bShouldRedraw = true;
281 }
282 }
283 }
284
285 if (bShouldRedraw) {
286 redraw(dirtyRect);
287 }
288 }
289
290 pedigree_log(LOG_INFO, "TUI shutting down cleanly.");
291}
292
293void Tui::stop() {
294 m_LocalData->bRunning = false;
295}
296
297void Tui::keyInput(uint64_t key) {
298 if (!m_LocalData->pTerminal)
299 return;
300
301 // CTRL + key -> unprintable characters
302 if ((key & Keyboard::Ctrl) && !(key & Keyboard::Special)) {
303 key &= 0x1F;
304 }
305
306 m_LocalData->pTerminal->processKey(key);
307}
308
310 if (rect.getX() == ~0UL && rect.getY() == ~0UL && rect.getX2() == 0 && rect.getY2() == 0) {
311 return;
312 }
313
314 PedigreeGraphics::Rect rt(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
315 if (m_LocalData->pSurface) {
316 cairo_surface_flush(m_LocalData->pSurface);
317 }
318
319 if (m_pWidget) {
320 m_pWidget->redraw(rt);
321 } else if (m_pRedrawer) {
322 m_pRedrawer->redraw(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
323 }
324}
Definition Font.h:32
bool initialise()
Definition Terminal.cc:70
void write(const char *pStr, size_t length, DirtyRectangle &rect)
Definition Terminal.cc:239
int getSelectFd() const
Definition Terminal.h:65
bool isAlive()
Definition Terminal.cc:198
void processKey(uint64_t key)
Definition Terminal.cc:220
void renewBuffer(size_t nWidth, size_t nHeight)
Definition Terminal.cc:210
Definition tui.h:35
void stop()
Stops the TUI main loop.
Definition tui.cc:293
void recreateSurfaces(void *fb)
Re-create rendering surfaces from the newest framebuffer.
Definition tui.cc:162
bool initialise(size_t width, size_t height)
(Re-)initialise the terminal
Definition tui.cc:68
void keyInput(uint64_t key)
Handles a key press with all Pedigree input special flags.
Definition tui.cc:297
void setCursorStyle(bool filled)
Set the cursor fill state (e.g. box outline vs shaded box)
Definition tui.cc:153
void run()
Runs the TUI main loop.
Definition tui.cc:221
Tui(TuiRedrawer *pRedrawer)
Default constructor which builds without using a widget.
Definition tui.cc:48
void resize(size_t newWidth, size_t newHeight)
Handle a resize of the terminal.
Definition tui.cc:194
void redraw(DirtyRectangle &rect)
Performs a redraw.
Definition tui.cc:309
bool redraw(PedigreeGraphics::Rect &rt)
Definition Widget.cc:217
static void checkForEvents(bool bAsync=false)
Definition Widget.cc:323
int getSocket() const
Definition Widget.h:147