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