MauiKit Terminal

Pty.cpp
1/*
2 SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7// Own
8#include "Pty.h"
9
10// System
11#include <csignal>
12#include <sys/ioctl.h> //ioctl() and TIOCSWINSZ
13#include <termios.h>
14
15// Qt
16#include <QDebug>
17#include <QStringList>
18#include <qplatformdefs.h>
19
20// KDE
21#include <KPtyDevice>
22#include <kpty_version.h>
23
24using Konsole::Pty;
25
27 : Pty(-1, aParent)
28{
29}
30
31Pty::Pty(int masterFd, QObject *aParent)
32 : KPtyProcess(masterFd, aParent)
33{
34#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
35 // Must call parent class child process modifier, as it sets file descriptors ...etc
36 auto parentChildProcModifier = KPtyProcess::childProcessModifier();
37 setChildProcessModifier([parentChildProcModifier = std::move(parentChildProcModifier)]() {
38 if (parentChildProcModifier) {
39 parentChildProcModifier();
40 }
41
42 // reset all signal handlers
43 // this ensures that terminal applications respond to
44 // signals generated via key sequences such as Ctrl+C
45 // (which sends SIGINT)
46 struct sigaction action;
47 sigemptyset(&action.sa_mask);
48 action.sa_handler = SIG_DFL;
49 action.sa_flags = 0;
50 for (int signal = 1; signal < NSIG; signal++) {
51 sigaction(signal, &action, nullptr);
52 }
53 });
54#endif
55
56 _windowColumns = 0;
57 _windowLines = 0;
58 _windowWidth = 0;
59 _windowHeight = 0;
60 _eraseChar = 0;
61 _xonXoff = true;
62 _utf8 = true;
63
64 setEraseChar(_eraseChar);
65 setFlowControlEnabled(_xonXoff);
66 setUtf8Mode(_utf8);
67
68 setWindowSize(_windowColumns, _windowLines, _windowWidth, _windowHeight);
69
70 setUseUtmp(true);
72
73 connect(pty(), &KPtyDevice::readyRead, this, &Konsole::Pty::dataReceived);
74}
75
76Pty::~Pty() = default;
77
78void Pty::sendData(const QByteArray &data)
79{
80 if (data.isEmpty()) {
81 return;
82 }
83
84 if (pty()->write(data) == -1) {
85 qDebug() << "Could not send input data to terminal process.";
86 return;
87 }
88}
89
90void Pty::dataReceived()
91{
92 QByteArray data = pty()->readAll();
93 if (data.isEmpty()) {
94 return;
95 }
96
97 Q_EMIT receivedData(data.constData(), data.size());
98}
99
100void Pty::setWindowSize(int columns, int lines, int width, int height)
101{
102 _windowColumns = columns;
103 _windowLines = lines;
104 _windowWidth = width;
105 _windowHeight = height;
106
107 if (pty()->masterFd() >= 0) {
108#if KPTY_VERSION >= QT_VERSION_CHECK(5, 93, 0)
109 pty()->setWinSize(_windowLines, _windowColumns, _windowHeight, _windowWidth);
110#else
111 struct winsize w;
112 w.ws_xpixel = _windowWidth;
113 w.ws_ypixel = _windowHeight;
114 w.ws_col = _windowColumns;
115 w.ws_row = _windowLines;
116 ioctl(pty()->masterFd(), TIOCSWINSZ, &w);
117#endif
118 }
119}
120
122{
123 return {_windowColumns, _windowLines};
124}
125
127{
128 return {_windowWidth, _windowHeight};
129}
130
132{
133 _xonXoff = enable;
134
135 if (pty()->masterFd() >= 0) {
136 struct ::termios ttmode;
137 pty()->tcGetAttr(&ttmode);
138 if (enable) {
139 ttmode.c_iflag |= (IXOFF | IXON);
140 } else {
141 ttmode.c_iflag &= ~(IXOFF | IXON);
142 }
143
144 if (!pty()->tcSetAttr(&ttmode)) {
145 qDebug() << "Unable to set terminal attributes.";
146 }
147 }
148}
149
151{
152 if (pty()->masterFd() >= 0) {
153 struct ::termios ttmode;
154 pty()->tcGetAttr(&ttmode);
155 return ((ttmode.c_iflag & IXOFF) != 0U) && ((ttmode.c_iflag & IXON) != 0U);
156 } else {
157 qDebug() << "Unable to get flow control status, terminal not connected.";
158 return _xonXoff;
159 }
160}
161
162void Pty::setUtf8Mode(bool enable)
163{
164#if defined(IUTF8) // XXX not a reasonable place to check it.
165 _utf8 = enable;
166
167 if (pty()->masterFd() >= 0) {
168 struct ::termios ttmode;
169 pty()->tcGetAttr(&ttmode);
170 if (enable) {
171 ttmode.c_iflag |= IUTF8;
172 } else {
173 ttmode.c_iflag &= ~IUTF8;
174 }
175
176 if (!pty()->tcSetAttr(&ttmode)) {
177 qDebug() << "Unable to set terminal attributes.";
178 }
179 }
180#else
181 Q_UNUSED(enable)
182#endif
183}
184
185void Pty::setEraseChar(char eChar)
186{
187 _eraseChar = eChar;
188
189 if (pty()->masterFd() >= 0) {
190 struct ::termios ttmode;
191 pty()->tcGetAttr(&ttmode);
192 ttmode.c_cc[VERASE] = eChar;
193
194 if (!pty()->tcSetAttr(&ttmode)) {
195 qDebug() << "Unable to set terminal attributes.";
196 }
197 }
198}
199
200char Pty::eraseChar() const
201{
202 if (pty()->masterFd() >= 0) {
203 struct ::termios ttyAttributes;
204 pty()->tcGetAttr(&ttyAttributes);
205 return ttyAttributes.c_cc[VERASE];
206 } else {
207 qDebug() << "Unable to get erase char attribute, terminal not connected.";
208 return _eraseChar;
209 }
210}
211
213{
214 QString pwd = dir;
215
216 // remove trailing slash in the path when appropriate
217 // example: /usr/share/icons/ ==> /usr/share/icons
218 if (pwd.length() > 1 && pwd.endsWith(QLatin1Char('/'))) {
219 pwd.chop(1);
220 }
221
223
224 // setting PWD to "." will cause problem for bash & zsh
225 if (pwd != QLatin1String(".")) {
226 setEnv(QStringLiteral("PWD"), pwd);
227 }
228}
229
230void Pty::addEnvironmentVariables(const QStringList &environmentVariables)
231{
232 bool isTermEnvAdded = false;
233
234 for (const QString &pair : environmentVariables) {
235 // split on the first '=' character
236 const int separator = pair.indexOf(QLatin1Char('='));
237
238 if (separator >= 0) {
239 QString variable = pair.left(separator);
240 QString value = pair.mid(separator + 1);
241
242 setEnv(variable, value);
243
244 if (variable == QLatin1String("TERM")) {
245 isTermEnvAdded = true;
246 }
247 }
248 }
249
250 // extra safeguard to make sure $TERM is always set
251 if (!isTermEnvAdded) {
252 setEnv(QStringLiteral("TERM"), QStringLiteral("xterm-256color"));
253 }
254}
255
256int Pty::start(const QString &programName, const QStringList &programArguments, const QStringList &environmentList)
257{
258 clearProgram();
259
260 setProgram(programName, programArguments);
261
262 addEnvironmentVariables(environmentList);
263
264 // unless the LANGUAGE environment variable has been set explicitly
265 // set it to a null string
266 // this fixes the problem where KCatalog sets the LANGUAGE environment
267 // variable during the application's startup to something which
268 // differs from LANG,LC_* etc. and causes programs run from
269 // the terminal to display messages in the wrong language
270 //
271 // this can happen if LANG contains a language which KDE
272 // does not have a translation for
273 //
274 // BR:149300
275 setEnv(QStringLiteral("LANGUAGE"), QString(), false /* do not overwrite existing value if any */);
276
278
279 if (waitForStarted()) {
280 return 0;
281 } else {
282 return -1;
283 }
284}
285
286void Pty::setWriteable(bool writeable)
287{
288 QT_STATBUF sbuf;
289 if (QT_STAT(pty()->ttyName(), &sbuf) == 0) {
290 if (writeable) {
291 if (::chmod(pty()->ttyName(), sbuf.st_mode | S_IWGRP) < 0) {
292 qDebug() << "Could not set writeable on " << pty()->ttyName();
293 }
294 } else {
295 if (::chmod(pty()->ttyName(), sbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) {
296 qDebug() << "Could not unset writeable on " << pty()->ttyName();
297 }
298 }
299 } else {
300 qDebug() << "Could not stat " << pty()->ttyName();
301 }
302}
303
305{
306 pty()->close();
307}
308
310{
311 const int master_fd = pty()->masterFd();
312
313 if (master_fd >= 0) {
314 int foregroundPid = tcgetpgrp(master_fd);
315
316 if (foregroundPid != -1) {
317 return foregroundPid;
318 }
319 }
320
321 return 0;
322}
323
324#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
325void Pty::setupChildProcess()
326{
327 KPtyProcess::setupChildProcess();
328
329 // reset all signal handlers
330 // this ensures that terminal applications respond to
331 // signals generated via key sequences such as Ctrl+C
332 // (which sends SIGINT)
333 struct sigaction action;
334 sigemptyset(&action.sa_mask);
335 action.sa_handler = SIG_DFL;
336 action.sa_flags = 0;
337 for (int signal = 1; signal < NSIG; signal++) {
338 sigaction(signal, &action, nullptr);
339 }
340}
341#endif
void start()
void clearProgram()
void setEnv(const QString &name, const QString &value, bool overwrite=true)
void setProgram(const QString &exe, const QStringList &args=QStringList())
void close() override
void setUseUtmp(bool value)
void setPtyChannels(PtyChannels channels)
KPtyDevice * pty() const
int masterFd() const
bool tcGetAttr(struct ::termios *ttmode) const
const char * ttyName() const
bool setWinSize(int lines, int columns)
The Pty class is used to start the terminal process, send data to it, receive data from it and manipu...
Definition Pty.h:33
QSize windowSize() const
Returns the size of the window used by this teletype in characters.
Definition Pty.cpp:121
bool flowControlEnabled() const
Queries the terminal state and returns true if Xon/Xoff flow control is enabled.
Definition Pty.cpp:150
void receivedData(const char *buffer, int length)
Emitted when a new block of data is received from the teletype.
void setInitialWorkingDirectory(const QString &dir)
Sets the initial working directory.
Definition Pty.cpp:212
void setEraseChar(char eraseChar)
Sets the special character for erasing previous not-yet-erased character.
Definition Pty.cpp:185
void closePty()
Close the underlying pty master/slave pair.
Definition Pty.cpp:304
QSize pixelSize() const
Returns the size of the window used by this teletype in pixels.
Definition Pty.cpp:126
Pty(QObject *parent=nullptr)
Constructs a new Pty.
Definition Pty.cpp:26
void setWriteable(bool writeable)
Control whether the pty device is writeable by group members.
Definition Pty.cpp:286
void setFlowControlEnabled(bool on)
Enables or disables Xon/Xoff flow control.
Definition Pty.cpp:131
void setWindowSize(int columns, int lines, int width, int height)
Sets the size of the window (in columns and lines of characters, and width and height in pixels) used...
Definition Pty.cpp:100
void sendData(const QByteArray &data)
Sends data to the process currently controlling the teletype ( whose id is returned by foregroundProc...
Definition Pty.cpp:78
int foregroundProcessGroup() const
Returns the process id of the teletype's current foreground process.
Definition Pty.cpp:309
void setUtf8Mode(bool on)
Put the pty into UTF-8 mode on systems which support it.
Definition Pty.cpp:162
const char * constData() const const
bool isEmpty() const const
qsizetype size() const const
QByteArray readAll()
void readyRead()
qint64 write(const QByteArray &data)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
std::function< void()> childProcessModifier()> childProcessModifier() const const
void setChildProcessModifier(const std::function< void()> &modifier)
void setWorkingDirectory(const QString &dir)
bool waitForStarted(int msecs)
void chop(qsizetype n)
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
QString left(qsizetype n) const const
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:10:32 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.