The Pedigree Project  0.1
Terminal.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 "Terminal.h"
21 #include "environment.h"
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <pwd.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/ioctl.h>
29 #include <sys/klog.h>
30 #include <sys/wait.h>
31 #include <termios.h>
32 #include <unistd.h>
33 #include <utmp.h>
34 
35 #include "pedigree/native/graphics/Graphics.h"
36 
37 #include <cairo/cairo.h>
38 
39 extern PedigreeGraphics::Framebuffer *g_pFramebuffer;
40 
41 Terminal::Terminal(
42  char *pName, size_t nWidth, size_t nHeight, size_t offsetLeft,
43  size_t offsetTop, rgb_t *pBackground, cairo_t *pCairo,
44  class Widget *pWidget, class Tui *pTui, class Font *pNormalFont,
45  class Font *pBoldFont)
46  : m_pBuffer(0), m_pFramebuffer(0), m_pXterm(0), m_Len(0),
47  m_WriteBufferLen(0), m_bHasPendingRequest(false), m_PendingRequestSz(0),
48  m_Pid(0), m_OffsetLeft(offsetLeft), m_OffsetTop(offsetTop), m_Cancel(0),
49  m_WriteInProgress(0)
50 {
51  cairo_save(pCairo);
52  cairo_set_operator(pCairo, CAIRO_OPERATOR_SOURCE);
53 
54  cairo_set_source_rgba(pCairo, 0, 0, 0, 0.8);
55 
56  cairo_rectangle(pCairo, m_OffsetLeft, m_OffsetTop, nWidth, nHeight);
57  cairo_fill(pCairo);
58 
59  cairo_restore(pCairo);
60 
61  strncpy(m_pName, pName, 256);
62  m_pName[255] = 0;
63 
64 #ifndef NEW_XTERM
65  m_pXterm = new Xterm(
66  0, nWidth, nHeight, m_OffsetLeft, m_OffsetTop, this, pWidget, pTui,
67  pNormalFont, pBoldFont);
68 #else
70  mode.width = nWidth - 1;
71  mode.height = nHeight - offsetTop - 1;
72  mode.pf.mRed = 0xFF;
73  mode.pf.mGreen = 0xFF;
74  mode.pf.mBlue = 0xFF;
75  mode.pf.pRed = 16;
76  mode.pf.pGreen = 8;
77  mode.pf.pBlue = 0;
78  mode.pf.nBpp = 24;
79  mode.pf.nPitch = nWidth * 3;
80  m_pXterm = new Vt100(
81  mode, reinterpret_cast<uint8_t *>(m_pBuffer) + nWidth * offsetTop);
82 #endif
83 }
84 
86 {
87  m_MasterPty = posix_openpt(O_RDWR | O_NOCTTY);
88  if (m_MasterPty < 0)
89  {
90  klog(LOG_INFO, "TUI: Couldn't create terminal: %s", strerror(errno));
91  return false;
92  }
93 
94 #ifdef TARGET_LINUX
95  grantpt(m_MasterPty);
96  unlockpt(m_MasterPty);
97 #endif
98  char slavename[16] = {0};
99  strncpy(slavename, ptsname(m_MasterPty), 16);
100  slavename[15] = 0;
101 
102  struct winsize ptySize;
103  memset(&ptySize, 0, sizeof(ptySize));
104  ptySize.ws_row = m_pXterm->getRows();
105  ptySize.ws_col = m_pXterm->getCols();
106  ioctl(m_MasterPty, TIOCSWINSZ, &ptySize);
107 
108  // Fire up a shell session.
109  int pid = m_Pid = fork();
110  if (pid == -1)
111  {
112  klog(LOG_INFO, "TUI: Couldn't fork: %s", strerror(errno));
113  DirtyRectangle rect;
114  write("Couldn't fork: ", rect);
115  write(strerror(errno), rect);
116  redrawAll(rect);
117  return false;
118  }
119  else if (pid == 0)
120  {
121  close(0);
122  close(1);
123  close(2);
124  close(m_MasterPty);
125 
126  // Create a new session for the shell, which will also wipe any
127  // existing CTTY.
128  setsid();
129 
130  // Open the slave terminal with correct rights.
131  int n = open(slavename, O_RDONLY);
132  open(slavename, O_WRONLY);
133  open(slavename, O_WRONLY);
134 
135  if (n < 0)
136  {
137  klog(LOG_INFO, "opening %s failed", slavename);
138  klog(
139  LOG_INFO, "opening stdin failed %d %s", errno, strerror(errno));
140  }
141 
142  // Mark opened slave as our ctty.
143  klog(LOG_INFO, "Trying to set CTTY");
144  ioctl(1, TIOCSCTTY, 0);
145 
146  // Set ourselves as the terminal's foreground process group.
147  tcsetpgrp(1, getpgrp());
148 
149  // We emulate an xterm, so ensure that's set in the environment.
150  setenv("TERM", "xterm", 1);
151 
152  // Get current user's shell.
153  struct passwd *pw = getpwuid(getuid());
154 
155  // Program to run.
156  const char *prog = pw->pw_shell;
157  if (!prog)
158  {
159  prog = getenv("SHELL");
160  if (!prog)
161  {
162  // Fall back to bash
163  klog(
164  LOG_WARNING,
165  "$SHELL unset, falling back to /applications/bash");
166  prog = "/applications/bash";
167  }
168  }
169 
170 // Create utmpx entry.
171 #ifndef TARGET_LINUX
172  struct utmpx ut;
174  ut.ut_type = USER_PROCESS;
175  ut.ut_pid = getpid();
176  const char *ttyid = slavename + strlen("/dev/");
177  strncpy(ut.ut_id, ttyid + strlen("tty"), UT_LINESIZE);
178  strncpy(ut.ut_line, ttyid, UT_LINESIZE);
179  strncpy(ut.ut_user, pw->pw_name, UT_NAMESIZE);
180  gettimeofday(&ut.ut_tv, NULL);
181 
182  setutxent();
183  pututxline(&ut);
184  endutxent();
185 #endif
186 
187  // Launch the shell now.
188  execl(prog, prog, NULL);
189  klog(
190  LOG_ALERT,
191  "Launching shell failed (next line is the error in errno...)");
192  klog(LOG_ALERT, "error: %s", strerror(errno));
193 
194  DirtyRectangle rect;
195  write("Couldn't load shell for this terminal... ", rect);
196  write(strerror(errno), rect);
197  write(
198  "\r\n\r\nYour installation of Pedigree may not be complete, or you "
199  "may have hit a bug.",
200  rect);
201  redrawAll(rect);
202 
203  exit(1);
204  }
205 
206  m_Pid = pid;
207 
208  return true;
209 }
210 
211 Terminal::~Terminal()
212 {
213  if (m_Pid)
214  {
215  // Kill child.
216  kill(m_Pid, SIGTERM);
217 
218  // Reap child.
219  waitpid(m_Pid, 0, 0);
220  }
221 
222  delete m_pXterm;
223 }
224 
226 {
227  if (m_Pid)
228  {
229  // Is our child still alive?
230  pid_t result = waitpid(m_Pid, 0, WNOHANG);
231  if (result == m_Pid)
232  {
233  m_Pid = 0;
234  return false;
235  }
236  }
237  return true;
238 }
239 
240 void Terminal::renewBuffer(size_t nWidth, size_t nHeight)
241 {
242  m_pXterm->resize(nWidth, nHeight, 0);
243 
245  struct winsize ptySize;
246  ptySize.ws_row = m_pXterm->getRows();
247  ptySize.ws_col = m_pXterm->getCols();
248  ioctl(m_MasterPty, TIOCSWINSZ, &ptySize);
249 }
250 
251 void Terminal::processKey(uint64_t key)
252 {
253  m_pXterm->processKey(key);
254 }
255 
257 {
258  if (m_Len > 0)
259  {
260  char c = m_pQueue[0];
261  for (size_t i = 0; i < m_Len - 1; i++)
262  m_pQueue[i] = m_pQueue[i + 1];
263  m_Len--;
264  return c;
265  }
266  else
267  return 0;
268 }
269 
271 {
272  m_Len = 0;
273 }
274 
275 void Terminal::write(const char *pStr, DirtyRectangle &rect)
276 {
277  m_pXterm->hideCursor(rect);
278 
279  bool bWasAlreadyRunning = m_WriteInProgress;
280  m_WriteInProgress = true;
281  // klog(LOG_NOTICE, "Beginning write...");
282  while (!m_Cancel && (*pStr || m_WriteBufferLen))
283  {
284  // Fill the buffer.
285  while (*pStr && !m_Cancel)
286  {
287  if (m_WriteBufferLen < 4)
288  m_pWriteBuffer[m_WriteBufferLen++] = *pStr++;
289  else
290  break;
291  }
292  if (m_Cancel) // Check break point from above loop
293  break;
294  // Begin UTF-8 -> UTF-32 conversion.
297  uint32_t utf32;
298  size_t nBytes;
299  if ((m_pWriteBuffer[0] & 0x80) == 0x00)
300  {
301  utf32 = static_cast<uint32_t>(m_pWriteBuffer[0]);
302  nBytes = 1;
303  }
304  else if ((m_pWriteBuffer[0] & 0xE0) == 0xC0)
305  {
306  if (m_WriteBufferLen < 2)
307  return;
308  utf32 = ((static_cast<uint32_t>(m_pWriteBuffer[0]) & 0x1F) << 6) |
309  (static_cast<uint32_t>(m_pWriteBuffer[1]) & 0x3F);
310  nBytes = 2;
311  }
312  else if ((m_pWriteBuffer[0] & 0xF0) == 0xE0)
313  {
314  if (m_WriteBufferLen < 3)
315  return;
316  utf32 = ((static_cast<uint32_t>(m_pWriteBuffer[0]) & 0x0F) << 12) |
317  ((static_cast<uint32_t>(m_pWriteBuffer[1]) & 0x3F) << 6) |
318  (static_cast<uint32_t>(m_pWriteBuffer[2]) & 0x3F);
319  nBytes = 3;
320  }
321  else if ((m_pWriteBuffer[0] & 0xF8) == 0xF0)
322  {
323  if (m_WriteBufferLen < 4)
324  return;
325  utf32 = ((static_cast<uint32_t>(m_pWriteBuffer[0]) & 0x0F) << 18) |
326  ((static_cast<uint32_t>(m_pWriteBuffer[1]) & 0x3F) << 12) |
327  ((static_cast<uint32_t>(m_pWriteBuffer[2]) & 0x3F) << 6) |
328  (static_cast<uint32_t>(m_pWriteBuffer[3]) & 0x3F);
329  nBytes = 4;
330  }
331  else
332  {
333  m_WriteBufferLen = 0;
334  continue;
335  }
336 
337  memmove(m_pWriteBuffer, &m_pWriteBuffer[nBytes], 4 - nBytes);
338  m_WriteBufferLen -= nBytes;
339 
340 // End UTF-8 -> UTF-32 conversion.
341 #ifndef NEW_XTERM
342  m_pXterm->write(utf32, rect);
343 #else
344  rect.point(m_OffsetLeft, m_OffsetTop);
345  rect.point(
346  m_pXterm->getCols() * 8 + m_OffsetLeft,
347  m_pXterm->getRows() * 16 + m_OffsetTop);
348  m_pXterm->write(static_cast<uint8_t>(utf32 & 0xFF));
349 #endif
350  }
351  // klog(LOG_NOTICE, "Completed write [%scancelled]...", m_Cancel ? "" : "not
352  // ");
353 
354  if (!bWasAlreadyRunning)
355  {
356  if (m_Cancel)
357  m_Cancel = 0;
358  m_WriteInProgress = false;
359  }
360 
361  m_pXterm->showCursor(rect);
362 }
363 
364 void Terminal::addToQueue(char c, bool bFlush)
365 {
366  // Don't allow keys to be pressed past the buffer's size
367  if ((c != 0) && (m_Len >= 256))
368  {
369  return;
370  }
371  if ((c == 0) && (!m_Len))
372  {
373  return;
374  }
375 
376  if (c)
377  m_pQueue[m_Len++] = c;
378 
379  if (bFlush)
380  {
381  ssize_t result = ::write(m_MasterPty, m_pQueue, m_Len);
382  if (result >= 0)
383  {
384  size_t missing = m_Len - result;
385  if (missing)
386  memmove(m_pQueue, &m_pQueue[result], missing);
387  m_Len = missing;
388  }
389  else
390  {
391  klog(LOG_ALERT, "Terminal::addToQueue flush failed");
392  }
393  }
394 }
395 
396 void Terminal::setActive(bool b, DirtyRectangle &rect)
397 {
398  // Force complete redraw
399  // m_pFramebuffer->redraw(0, 0, m_pFramebuffer->getWidth(),
400  // m_pFramebuffer->getHeight(), false);
401 
402  // if (b)
403  // Syscall::setCurrentBuffer(m_pBuffer);
404 }
void processKey(uint64_t key)
Definition: Terminal.cc:251
Definition: tui.h:36
char getFromQueue()
Definition: Terminal.cc:256
void write(const char *pStr, DirtyRectangle &rect)
Definition: Terminal.cc:275
uint8_t mRed
Red mask.
Definition: Display.h:57
Definition: Font.h:33
bool initialise()
Definition: Terminal.cc:85
uint8_t pGreen
Position of green field.
Definition: Display.h:60
uint8_t nBpp
Bits per pixel (total).
Definition: Display.h:65
Definition: Xterm.h:44
void clearQueue()
Definition: Terminal.cc:270
void renewBuffer(size_t nWidth, size_t nHeight)
Definition: Terminal.cc:240
uint32_t nPitch
Bytes per scanline.
Definition: Display.h:66
bool isAlive()
Definition: Terminal.cc:225
Definition: Widget.h:63
uint8_t pBlue
Position of blue field.
Definition: Display.h:62
uint8_t mGreen
Green mask.
Definition: Display.h:59
uint8_t mBlue
Blue mask.
Definition: Display.h:61
uint8_t pRed
Position of red field.
Definition: Display.h:58