source: trunk/tools/neercs/term/pty.cpp @ 2176

Last change on this file since 2176 was 2176, checked in by sam, 7 years ago

neercs: fix rendering issues and tweak timings so that large fullscreen
apps such as cacademo no longer appear to lag.

File size: 5.1 KB
Line 
1/*
2 *  neercs        console-based window manager
3 *  Copyright (c) 2006-2012 Sam Hocevar <sam@hocevar.net>
4 *                All Rights Reserved
5 *
6 *  This program is free software. It comes without any warranty, to
7 *  the extent permitted by applicable law. You can redistribute it
8 *  and/or modify it under the terms of the Do What The Fuck You Want
9 *  To Public License, Version 2, as published by Sam Hocevar. See
10 *  http://sam.zoy.org/wtfpl/COPYING for more details.
11 */
12
13#if defined HAVE_CONFIG_H
14#   include "config.h"
15#endif
16
17#if defined HAVE_FORKPTY
18#   define _XOPEN_SOURCE
19#   include <stdlib.h>
20#   include <stdio.h>
21#   include <string.h>
22#   include <sys/ioctl.h>
23#   include <sys/types.h>
24#   include <termios.h>
25#   if defined HAVE_PTY_H
26#       include <pty.h>             /* for openpty and forkpty */
27#   elif defined HAVE_UTIL_H
28#       include <util.h>            /* for OS X, OpenBSD and NetBSD */
29#   elif defined HAVE_LIBUTIL_H
30#       include <libutil.h>         /* for FreeBSD */
31#   endif
32#   include <unistd.h>
33#   include <fcntl.h>
34#endif
35
36#include "core.h"
37#include "loldebug.h"
38
39using namespace std;
40using namespace lol;
41
42#include "../neercs.h"
43
44Pty::Pty()
45  : m_fd(-1),
46    m_pid(-1),
47    m_eof(false),
48    m_unread_data(0),
49    m_unread_len(0),
50    m_size(-1, -1)
51{
52    ;
53}
54
55Pty::~Pty()
56{
57#if defined HAVE_FORKPTY
58    delete[] m_unread_data;
59
60    if (m_fd >= 0)
61    {
62        close((int)m_fd);
63    }
64#endif
65}
66
67void Pty::Run(char const *command, ivec2 size)
68{
69#if defined HAVE_FORKPTY
70    int fd;
71    pid_t pid;
72
73    m_pid = -1;
74    m_fd = -1;
75
76    pid = forkpty(&fd, NULL, NULL, NULL);
77    if (pid < 0)
78    {
79        fprintf(stderr, "forkpty() error\n");
80        return;
81    }
82    else if (pid == 0)
83    {
84        SetWindowSize(size, 0);
85
86        /* putenv() eats the string, they need to be writable */
87        static char tmp1[] = "CACA_DRIVER=slang";
88        static char tmp2[] = "TERM=xterm";
89        putenv(tmp1);
90        putenv(tmp2);
91
92        m_argv[0] = command;
93        m_argv[1] = NULL;
94        /* The following const cast is valid. The Open Group Base
95         * Specification guarantees that the objects are completely
96         * constant. */
97        execvp(command, const_cast<char * const *>(m_argv));
98        fprintf(stderr, "execvp() error\n");
99        return;
100    }
101
102    fcntl(fd, F_SETFL, O_NDELAY);
103
104    m_pid = pid;
105    m_fd = fd;
106#endif
107}
108
109bool Pty::IsEof() const
110{
111    return m_eof;
112}
113
114/* Read data from the PTY. We only perform one read() call so that the
115 * caller can decide whether to ask for more data or not. This lets us
116 * prioritise data in some way. */
117size_t Pty::ReadData(char *data, size_t maxlen)
118{
119#if defined HAVE_FORKPTY
120    size_t sent = 0;
121
122    /* Do we have data from previous call? */
123    if (m_unread_len)
124    {
125        size_t tocopy = min(maxlen, m_unread_len);
126
127        memcpy(data, m_unread_data, tocopy);
128
129        data += tocopy;
130        sent += tocopy;
131        maxlen -= tocopy;
132
133        if (tocopy < m_unread_len)
134        {
135            m_unread_len -= tocopy;
136            memmove(m_unread_data, m_unread_data + tocopy, m_unread_len);
137        }
138        else
139        {
140            delete[] m_unread_data;
141            m_unread_data = 0;
142            m_unread_len = 0;
143        }
144    }
145
146    fd_set fdset;
147    int maxfd = -1;
148
149    FD_ZERO(&fdset);
150    if (m_fd >= 0)
151    {
152        FD_SET((int)m_fd, &fdset);
153        maxfd = std::max(maxfd, (int)m_fd);
154    }
155
156    if (maxfd >= 0)
157    {
158        struct timeval tv;
159        tv.tv_sec = 0;
160        tv.tv_usec = 50000;
161        int ret = select(maxfd + 1, &fdset, NULL, NULL, &tv);
162
163        if (ret < 0)
164        {
165            Log::Error("cannot read from PTY\n");
166            m_eof = true;
167            return 0;
168        }
169        else if (ret)
170        {
171            if (FD_ISSET((int)m_fd, &fdset))
172            {
173                ssize_t nr = read((int)m_fd, data, maxlen);
174
175                /* Data available but zero-length read: EOF */
176                if (nr <= 0)
177                    m_eof = true;
178                else
179                    sent += nr;
180
181                if (sent >= 0)
182                    return sent;
183            }
184        }
185    }
186#endif
187
188    return 0;
189}
190
191void Pty::UnreadData(char *data, size_t len)
192{
193#if defined HAVE_FORKPTY
194    /* Prepare unread buffer */
195    if (m_unread_data)
196    {
197        char *tmp = new char[m_unread_len + len];
198        memcpy(tmp + len, m_unread_data, m_unread_len);
199        delete[] m_unread_data;
200        m_unread_data = tmp;
201        m_unread_len += len;
202    }
203    else
204    {
205        m_unread_data = new char[len];
206        m_unread_len = len;
207    }
208
209    /* Copy data to the unread buffer */
210    memcpy(m_unread_data, data, len);
211#endif
212}
213
214size_t Pty::WriteData(char const *data, size_t len)
215{
216#if defined HAVE_FORKPTY
217    /* FIXME: can we be more naive than that? */
218    return write((int)m_fd, data, len);
219#endif
220
221    return 0;
222}
223
224void Pty::SetWindowSize(ivec2 size, int64_t fd /* = -1 */)
225{
226#if defined HAVE_FORKPTY
227    if (m_size == size)
228        return;
229
230    if (fd < 0)
231        fd = m_fd;
232
233    m_size = size;
234
235    struct winsize ws;
236
237    memset(&ws, 0, sizeof(ws));
238    ws.ws_row = size.y;
239    ws.ws_col = size.x;
240    ioctl((int)fd, TIOCSWINSZ, (char *)&ws);
241#endif
242}
Note: See TracBrowser for help on using the repository browser.