1 /*  Copyright (C) 2008 e_k (e_k@users.sourceforge.net)
2     Copyright (C) 2012-2019 Jacob Dawid <jacob.dawid@cybercatalyst.com>
3 
4     This library is free software: you can redistribute it and/or
5     modify it under the terms of the GNU Library General Public
6     License as published by the Free Software Foundation; either
7     version 2 of the License, or (at your option) any later version.
8 
9     This library is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12     Library General Public License for more details.
13 
14     You should have received a copy of the GNU Library General Public License
15     along with this library; see the file COPYING.LIB.  If not, write to
16     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17     Boston, MA 02110-1301, USA.
18 */
19 
20 #include <QDebug>
21 
22 #include "unix/QUnixTerminalImpl.h"
23 #include "unix/kpty.h"
24 
25 #include <termios.h>
26 
QUnixTerminalImpl(QWidget * p)27 QUnixTerminalImpl::QUnixTerminalImpl(QWidget *p)
28     : QTerminal(p),
29       _parent (p)
30 {
31     initialize();
32 }
33 
initialize()34 void QUnixTerminalImpl::initialize()
35 {
36     m_terminalView = new TerminalView(this);
37     m_terminalView->setKeyboardCursorShape(TerminalView::UnderlineCursor);
38     m_terminalView->setBlinkingCursor(true);
39     m_terminalView->setBellMode(TerminalView::NotifyBell);
40     m_terminalView->setTerminalSizeHint(true);
41     m_terminalView->setContextMenuPolicy(Qt::CustomContextMenu);
42     m_terminalView->setTripleClickMode(TerminalView::SelectWholeLine);
43     m_terminalView->setTerminalSizeStartup(true);
44     m_terminalView->setSize(80, 40);
45     m_terminalView->setScrollBarPosition(TerminalView::ScrollBarRight);
46 
47     UrlFilter *url_filter = new UrlFilter();
48     m_terminalView->filterChain ()->addFilter (url_filter);
49 
50     UrlFilter *file_filter = new UrlFilter (Filter::Type::ErrorLink);
51     m_terminalView->filterChain ()->addFilter (file_filter);
52 
53     connect (file_filter, SIGNAL (request_edit_mfile_signal (const QString&, int)),
54              _parent, SLOT (edit_mfile (const QString&, int)));
55     connect (file_filter, SIGNAL (request_open_file_signal (const QString&, int)),
56              _parent, SLOT (open_file (const QString&, int)));
57 
58     connect(m_terminalView, SIGNAL(customContextMenuRequested(QPoint)),
59             this, SLOT(handleCustomContextMenuRequested(QPoint)));
60 
61     connect (m_terminalView, SIGNAL (interrupt_signal (void)),
62              this, SLOT (terminal_interrupt ()));
63 
64 #ifdef Q_OS_MAC
65     QFont font = QFont("Monaco");
66     font.setStyleHint(QFont::TypeWriter);
67     font.setPointSize(11);
68 #else
69     QFont font = QFont("Monospace");
70     font.setStyleHint(QFont::TypeWriter);
71     font.setPointSize(10);
72 #endif
73     setTerminalFont(font);
74     setFocusPolicy (Qt::StrongFocus);
75     setFocusProxy(m_terminalView);
76     setFocus(Qt::OtherFocusReason);
77 
78     m_kpty = new KPty();
79     m_kpty->open();
80 
81     m_terminalModel = new TerminalModel(m_kpty);
82     m_terminalModel->setAutoClose(true);
83     m_terminalModel->setCodec(QTextCodec::codecForName("UTF-8"));
84     m_terminalModel->setHistoryType(HistoryTypeBuffer (1000));
85     m_terminalModel->setDarkBackground(true);
86     m_terminalModel->setKeyBindings("");
87     m_terminalModel->run();
88     m_terminalModel->addView(m_terminalView);
89     connectToPty();
90 }
setScrollBufferSize(int value)91 void QUnixTerminalImpl::setScrollBufferSize(int value)
92 {
93   if (value > 0)
94     {
95       m_terminalModel->clearHistory ();
96       m_terminalModel->setHistoryType (HistoryTypeBuffer ( value ));
97     }
98   else
99     m_terminalModel->setHistoryType (HistoryTypeNone ());
100 }
101 
102 QList<QAction*>
get_hotspot_actions(const QPoint & at)103 QUnixTerminalImpl::get_hotspot_actions (const QPoint& at)
104 {
105   return m_terminalView->filterActions (at);
106 }
107 
connectToPty()108 void QUnixTerminalImpl::connectToPty()
109 {
110     // Store the file descriptor associated with the STDERR stream onto
111     // another temporary file descriptor for reconnect in the destructor.
112     fdstderr = dup (STDERR_FILENO);
113 
114     int fds = m_kpty->slaveFd();
115 
116     dup2 (fds, STDIN_FILENO);
117     dup2 (fds, STDOUT_FILENO);
118     dup2 (fds, STDERR_FILENO);
119 
120     if(!isatty(STDIN_FILENO)) {
121         qDebug("Error: stdin is not a tty.");
122     }
123 
124     if(!isatty(STDOUT_FILENO)) {
125         qDebug("Error: stdout is not a tty.");
126     }
127 
128     if(!isatty(STDERR_FILENO)) {
129         qDebug("Error: stderr is not a tty.");
130     }
131 }
132 
~QUnixTerminalImpl()133 QUnixTerminalImpl::~QUnixTerminalImpl()
134 {
135     delete m_terminalModel;
136     delete m_kpty;
137     delete m_terminalView;
138 
139     // Restore stderr so that any errors at exit might appear somewhere.
140     dup2 (fdstderr, STDERR_FILENO);
141 
142     emit destroyed();
143 }
144 
setTerminalFont(const QFont & font)145 void QUnixTerminalImpl::setTerminalFont(const QFont &font)
146 {
147     if(!m_terminalView)
148         return;
149     m_terminalView->setVTFont(font);
150 }
151 
setSize(int h,int v)152 void QUnixTerminalImpl::setSize(int h, int v)
153 {
154     if(!m_terminalView)
155         return;
156     m_terminalView->setSize(h, v);
157 }
158 
sendText(const QString & text)159 void QUnixTerminalImpl::sendText(const QString& text)
160 {
161     m_terminalModel->sendText(text);
162 }
163 
setCursorType(CursorType type,bool blinking)164 void QUnixTerminalImpl::setCursorType(CursorType type, bool blinking)
165 {
166     switch(type) {
167         case UnderlineCursor: m_terminalView->setKeyboardCursorShape(TerminalView::UnderlineCursor); break;
168         case BlockCursor: m_terminalView->setKeyboardCursorShape(TerminalView::BlockCursor); break;
169         case IBeamCursor: m_terminalView->setKeyboardCursorShape(TerminalView::IBeamCursor); break;
170     }
171     m_terminalView->setBlinkingCursor(blinking);
172 }
173 
174 // FIXME -- not sure how to make these work properly given the way the
175 // Unix terminal handles colors.
setBackgroundColor(const QColor & color)176 void QUnixTerminalImpl::setBackgroundColor (const QColor& color)
177   {
178     ColorEntry cols[TABLE_COLORS];
179 
180     const ColorEntry * curr_cols = m_terminalView->colorTable();
181     for(int i=0;i<TABLE_COLORS;i++)
182     {
183      cols[i] = curr_cols[i];
184     }
185 
186     cols[DEFAULT_BACK_COLOR].color = color;
187 
188     m_terminalView->setColorTable(cols);
189 
190     QString css = QString ("TerminalView {\n"
191                            "  background: %1;\n"
192                            "}\n").arg (color.name ());
193     setStyleSheet (css);
194 
195   }
setForegroundColor(const QColor & color)196 void QUnixTerminalImpl::setForegroundColor (const QColor& color)
197 {
198     ColorEntry cols[TABLE_COLORS];
199 
200     const ColorEntry * curr_cols = m_terminalView->colorTable();
201     for(int i=0;i<TABLE_COLORS;i++)
202     {
203      cols[i] = curr_cols[i];
204     }
205 
206     cols[DEFAULT_FORE_COLOR].color = color;
207 
208     m_terminalView->setColorTable(cols);
209 
210 
211 }
setSelectionColor(const QColor & color)212 void QUnixTerminalImpl::setSelectionColor (const QColor& color) { }
213 
setCursorColor(bool useForegroundColor,const QColor & color)214 void QUnixTerminalImpl::setCursorColor (bool useForegroundColor,
215                                         const QColor& color)
216 {
217   m_terminalView->setKeyboardCursorColor (useForegroundColor, color);
218 }
219 
showEvent(QShowEvent *)220 void QUnixTerminalImpl::showEvent(QShowEvent *)
221 {
222     m_terminalView->updateImage();
223     m_terminalView->repaint();
224     m_terminalView->update();
225 }
226 
resizeEvent(QResizeEvent *)227 void QUnixTerminalImpl::resizeEvent(QResizeEvent*)
228 {
229     m_terminalView->resize(this->size());
230     m_terminalView->updateImage();
231     m_terminalView->repaint();
232     m_terminalView->update();
233 }
234 
copyClipboard()235 void QUnixTerminalImpl::copyClipboard()
236 {
237     m_terminalView->copyClipboard (_extra_interrupt);
238 }
239 
pasteClipboard()240 void QUnixTerminalImpl::pasteClipboard()
241 {
242     m_terminalView->pasteClipboard();
243 }
244 
selectAll()245 void QUnixTerminalImpl::selectAll()
246 {
247     m_terminalView->selectAll();
248 }
249 
250 
selectedText()251 QString QUnixTerminalImpl::selectedText ()
252 {
253   return m_terminalView->selectedText ();
254 }
255 
256 void
has_extra_interrupt(bool extra)257 QUnixTerminalImpl::has_extra_interrupt (bool extra)
258 {
259   _extra_interrupt = extra;
260 }
261 
262 void
handle_visibility_changed(bool visible)263 QUnixTerminalImpl::handle_visibility_changed (bool visible)
264 {
265   m_terminalView->visibility_changed (visible);
266 };
267