29 #define WIN32_LEAN_AND_MEAN
30 #if ! defined (_WIN32_WINNT) && ! defined (NTDDI_VERSION)
31 #define _WIN32_WINNT 0x0500
36 #include <versionhelpers.h>
38 #include <QApplication>
42 #include <QGridLayout>
43 #include <QPaintEvent>
45 #include <QResizeEvent>
52 #include <QMessageBox>
53 #include <QDragEnterEvent>
63 #define LOGFILENAME "QConsole.log"
65 #define HIDDEN_CONSOLE
68 # pragma warning(disable : 4996)
106 QString esc =
"\x1b";
109 if (ev->key () == Qt::Key_Delete)
111 else if (!ev->text ().isEmpty ())
150 case Qt::Key_PageDown:
198 void log (
const char* fmt, ...);
202 const char* devName);
217 void setCursorColor (
bool useForegroundColor,
const QColor& color);
276 if (end.y () < begin.y ()
277 || (end.y () == begin.y () && end.x () < begin.x ()))
278 std::swap (begin, end);
284 : q (parent), m_command (cmd), m_auto_scroll (true), m_cursorBlinking (false),
285 m_hasBlinkingCursor (true), m_cursorType (
BlockCursor),
286 m_beginSelection (0, 0), m_endSelection (0, 0), m_settingSelection (false),
287 m_process (nullptr), m_inWheelEvent (false)
292 log (
"Detaching from existing console (if any)...\n");
294 log (
"Closing standard IO...\n");
299 #ifdef HIDDEN_CONSOLE
300 HWINSTA hOrigSta, hNewSta;
303 hOrigSta = GetProcessWindowStation ();
304 hNewSta = CreateWindowStation (
nullptr, 0, GENERIC_ALL,
nullptr);
305 log (
"Current Windows station: %p.\nNew Windows station: %p.\n", hOrigSta,
307 if (! SetProcessWindowStation (hNewSta))
308 log (
"Failed to switch to new Windows station.\n");
310 if (! AllocConsole ())
311 log (
"Failed to create new console.\n");
312 #ifdef HIDDEN_CONSOLE
313 if (! SetProcessWindowStation (hOrigSta))
314 log (
"Failed to restore original Windows station.\n");
315 if (! CloseWindowStation (hNewSta))
316 log (
"Failed to close new Windows station.\n");
319 log (
"New (hidden) console created.\n");
325 log (
"Standard input/output/error set up.\n");
327 *stdin = *(fdopen (0,
"rb"));
328 *stdout = *(fdopen (1,
"wb"));
329 *stderr = *(fdopen (2,
"wb"));
331 log (
"POSIX standard streams created.\n");
333 setvbuf (stdin,
nullptr, _IONBF, 0);
334 setvbuf (stdout,
nullptr, _IONBF, 0);
335 setvbuf (stderr,
nullptr, _IONBF, 0);
337 log (
"POSIX standard stream buffers adjusted.\n");
339 HANDLE hStdOut = GetStdHandle (STD_OUTPUT_HANDLE);
341 log (
"Console allocated: hStdOut: %p\n", hStdOut);
347 #ifdef HIDDEN_CONSOLE
351 CONSOLE_SCREEN_BUFFER_INFO sbi;
353 GetConsoleScreenBufferInfo (hStdOut, &sbi);
354 m_bufferSize = QSize (sbi.dwSize.X, qMax (sbi.dwSize.Y, (SHORT)500));
356 sbi.srWindow.Right - sbi.srWindow.Left + 1,
357 sbi.srWindow.Bottom - sbi.srWindow.Top + 1);
358 m_cursorPos = QPoint (sbi.dwCursorPosition.X, sbi.dwCursorPosition.Y);
360 log (
"Initial console parameters:\n");
363 log (
" window: (%d, %d) -> (%d, %d) [%d x %d]\n",
368 wchar_t titleBuf[260];
369 GetConsoleTitleW (titleBuf,
sizeof (titleBuf));
370 q->setWindowTitle (QString::fromWCharArray (titleBuf));
372 m_font.setFamily (
"Lucida Console");
374 m_font.setStyleHint (QFont::TypeWriter);
383 QGridLayout* l =
new QGridLayout (parent);
384 l->setContentsMargins (0, 0, 0, 0);
390 if (IsWindows7OrGreater ())
392 SetConsoleCP (65001);
393 SetConsoleOutputCP (65001);
402 SetConsoleTextAttribute (
m_stdOut, 0x07);
415 parent->setFocusPolicy (Qt::StrongFocus);
468 const char* name,
const char* devName)
470 log (
"Opening %s...\n", devName);
472 int fd = open (devName, _O_RDWR | _O_BINARY);
478 log (
"Opened %s is not at target file descriptor %d, "
479 "duplicating...\n", name, targetFd);
480 if (
dup2 (fd, targetFd) == -1)
481 log (
"Failed to duplicate file descriptor: errno=%d.\n", errno);
482 if (close (fd) == -1)
483 log (
"Failed to close original file descriptor: errno=%d.\n",
487 log (
"%s opened and assigned to file descriptor %d.\n", devName, fd);
488 if (! SetStdHandle (stdHandleId, (HANDLE) _get_osfhandle (targetFd)))
489 log (
"Failed to re-assign %s: error=%08x.\n", name, GetLastError ());
492 log (
"Failed to open %s: errno=%d.\n", devName, errno);
513 COORD bufSize, bufCoord;
517 nr = end.y () - begin.y () + 1;
526 bufRect.Top = begin.y ();
527 bufRect.Bottom = end.y ();
529 if (ReadConsoleOutput (
m_stdOut, buf, bufSize, bufCoord, &bufRect))
531 int start_pos = begin.x ();
532 int end_pos = (nr - 1) *
m_bufferSize.width () + end.x ();
533 int lastNonSpace = -1;
535 for (
int i = start_pos; i <= end_pos; i++)
539 if (lastNonSpace >= 0)
540 selection.truncate (lastNonSpace);
541 selection.append (
'\n');
542 lastNonSpace = selection.length ();
545 QChar c (buf[i].Char.UnicodeChar);
549 selection.append (c);
551 lastNonSpace = selection.length ();
554 if (lastNonSpace >= 0)
555 selection.truncate (lastNonSpace);
606 QPalette palette (color);
607 palette.setColor(QPalette::Base, color);
629 CONSOLE_SCREEN_BUFFER_INFO sbi;
630 GetConsoleScreenBufferInfo (
m_stdOut, &sbi);
638 int cx2,
int cy2,
int cw,
int ch)
642 int ascent = p.fontMetrics ().ascent ();
644 int y = ascent + cy1 * ch;;
646 for (
int j = cy1; j <= cy2; j++, y += ch)
649 bool hasChar =
false;
653 for (
int i = cx1; i <= cx2; i++)
655 CHAR_INFO* ci = &(
m_buffer[stride*j+i]);
657 if ((ci->Attributes & 0x00ff) != attr)
663 if (hasChar || (attr & 0x00f0))
666 p.fillRect (
x, y-ascent,
len * cw, ch, p.brush ());
674 attr = (ci->Attributes & 0x00ff);
675 p.setBrush (
m_colors[(attr >> 4) & 0x000f]);
680 if (ci->Char.UnicodeChar != L
' ')
684 if (
len != 0 && (hasChar || (attr & 0x00f0)))
691 p.fillRect (
x, y-ascent,
len * cw, ch, p.brush ());
708 QPoint begin = cellpos;
709 QPoint end = cellpos;
717 begin.ry () -= verticalScrollOffset;
718 end.ry () -= verticalScrollOffset;
720 begin.rx () -= horizontalScrollOffset;
721 end.rx () -= horizontalScrollOffset;
724 if (QChar(
m_buffer[begin.y ()*stride + begin.x ()].Char.UnicodeChar).isSpace () ==
false)
727 while(begin.x () > 0 &&
728 QChar(
m_buffer[begin.y ()*stride + begin.x () -1].Char.UnicodeChar).isSpace() ==
false)
734 QChar(
m_buffer[end.y ()*stride + end.x () +1].Char.UnicodeChar).isSpace() ==
false)
741 while(begin.x () > 0 &&
742 QChar(
m_buffer[begin.y ()*stride + begin.x () -1].Char.UnicodeChar).isSpace())
748 QChar(
m_buffer[end.y ()*stride + end.x () +1].Char.UnicodeChar).isSpace ())
755 begin.ry () += verticalScrollOffset;
756 end.ry () += verticalScrollOffset;
758 begin.rx () += horizontalScrollOffset;
759 end.rx () += horizontalScrollOffset;
776 int cx2,
int cy2,
int cw,
int ch)
783 bool haveSelection = (begin != end);
791 begin.ry () -= verticalScrollOffset;
792 end.ry () -= verticalScrollOffset;
794 begin.rx () -= horizontalScrollOffset;
795 end.rx () -= horizontalScrollOffset;
797 int ascent = p.fontMetrics ().ascent ();
800 int y = ascent + cy1 * ch;;
801 for (
int j = cy1; j <= cy2; j++, y += ch)
803 int charsThisLine = 0;
805 bool hasChar =
false;
808 for (
int i = cx1; i <= cx2; i++)
810 CHAR_INFO* ci = &(
m_buffer[stride*j+i]);
812 if ((ci->Attributes & 0x00ff) != attr)
817 charsThisLine +=
len;
823 attr = (ci->Attributes & 0x00ff);
828 if (ci->Char.UnicodeChar != L
' ')
832 if (
len != 0 && (hasChar || (attr & 0x00f0)))
833 charsThisLine +=
len;
835 if (haveSelection && j >= begin.y () && j <= end.y ())
837 int selectionBegin = j == begin.y () ? begin.x (): 0;
839 int len = ((j == end.y () && end.x () < charsThisLine)
840 ? end.x () - selectionBegin + 1
841 : stride - selectionBegin);
843 p.fillRect (selectionBegin * cw, y-ascent,
len * cw, ch,
865 p.fillRect (rect, color);
871 int penWidth = qMax (1, p.pen().width());
873 p.drawRect (rect.adjusted (penWidth/2, penWidth/2,
874 - penWidth/2 - penWidth%2,
875 - penWidth/2 - penWidth%2));
880 p.drawLine (rect.left (), rect.bottom (),
881 rect.right (), rect.bottom ());
885 p.drawLine (rect.left (), rect.top (),
886 rect.left (), rect.bottom ());
894 int cx2,
int cy2,
int cw,
int ch)
902 s.reserve (cx2 - cx1 + 1);
904 int ascent = p.fontMetrics ().ascent ();
907 int y = ascent + cy1 * ch;;
908 for (
int j = cy1; j <= cy2; j++, y += ch)
912 bool hasChar =
false;
916 for (
int i = cx1; i <= cx2; i++)
918 CHAR_INFO* ci = &(
m_buffer[stride*j+i]);
920 if ((ci->Attributes & 0x00ff) != attr)
926 if (hasChar || (attr & 0x00f0))
927 p.drawText (
x, y, s);
929 x += (s.length () * cw);
934 attr = (ci->Attributes & 0x00ff);
939 s.append (ci->Char.UnicodeChar);
940 if (ci->Char.UnicodeChar != L
' ')
944 if (! s.isEmpty () && (hasChar || (attr & 0x00f0)))
950 p.drawText (
x, y, s);
962 if (close (fd) == -1)
963 log (
"Failed to close file descriptor %d: errno=%d.\n", fd, errno);
964 if (! CloseHandle (GetStdHandle (stdHandleId)))
965 log (
"Failed to close Win32 %s: error=%08x.\n", name, GetLastError ());
972 #ifdef DEBUG_QCONSOLE
979 vfprintf (flog, fmt, l);
1001 m_charSize.rwidth () = fm.averageCharWidth ();
1004 m_consoleRect.setWidth (winSize.width () / fm.averageCharWidth ());
1005 m_consoleRect.setHeight (winSize.height () / fm.lineSpacing ());
1024 qputenv (
"LINES", QByteArray::number (
m_consoleRect.height ()));
1025 qputenv (
"COLUMNS", QByteArray::number (
m_consoleRect.width ()));
1036 log (
"Console resized:\n");
1037 log (
" widget size: %d x %d\n", winSize.width (), winSize.height ());
1040 log (
" window: (%d, %d) -> (%d, %d) [%d x %d]\n",
1056 CONSOLE_SCREEN_BUFFER_INFO sbi;
1059 GetConsoleScreenBufferInfo (hStdOut, &sbi);
1071 if (bs.X * bs.Y > sbi.dwSize.X * sbi.dwSize.Y)
1073 SetConsoleScreenBufferSize (hStdOut, bs);
1074 SetConsoleWindowInfo (hStdOut, TRUE, &sr);
1078 SetConsoleWindowInfo (hStdOut, TRUE, &sr);
1079 SetConsoleScreenBufferSize (hStdOut, bs);
1082 log (
"Sync'ing console parameters:\n");
1083 log (
" buffer size: %d x %d\n", bs.X, bs.Y);
1084 log (
" window: (%d, %d) -> (%d, %d)\n",
1085 sr.Left, sr.Top, sr.Right, sr.Bottom);
1113 log (
"ReadConsoleOutput (%d,%d) -> (%d,%d)\n",
r.Left,
r.Top,
r.Right,
r.Bottom);
1115 qCritical (
"cannot read console output");
1131 log (
"Horizontal scrollbar parameters updated: %d/%d/%d/%d\n",
1149 log (
"Vertical scrollbar parameters updated: %d/%d/%d/%d\n",
1172 log (
"Scrolling window horizontally: (%d, %d) -> (%d, %d) [%d x %d]\n",
1173 r.Left,
r.Top,
r.Right,
r.Bottom,
1174 r.Right -
r.Left + 1,
r.Bottom -
r.Top + 1);
1176 if (SetConsoleWindowInfo (hStdOut, TRUE, &
r))
1199 log (
"Scrolling window vertically: (%d, %d) -> (%d, %d) [%d x %d]\n",
1200 r.Left,
r.Top,
r.Right,
r.Bottom,
1201 r.Right -
r.Left + 1,
r.Bottom -
r.Top + 1);
1203 if (SetConsoleWindowInfo (hStdOut, TRUE, &
r))
1207 CONSOLE_SCREEN_BUFFER_INFO sbi;
1208 if (GetConsoleScreenBufferInfo (hStdOut, &sbi))
1234 CONSOLE_SCREEN_BUFFER_INFO sbi;
1235 HANDLE hStdOut = GetStdHandle (STD_OUTPUT_HANDLE);
1237 static wchar_t titleBuf[260];
1239 GetConsoleTitleW (titleBuf,
sizeof (titleBuf));
1240 QString title = QString::fromWCharArray (titleBuf);
1244 q->setWindowTitle (title);
1248 if (GetConsoleScreenBufferInfo (hStdOut, &sbi))
1250 bool need_console_sync =
false;
1260 need_console_sync =
true;
1286 log (
"--> Console window changed\n");
1288 sbi.srWindow.Right - sbi.srWindow.Left + 1,
1289 sbi.srWindow.Bottom - sbi.srWindow.Top + 1);
1297 if (need_console_sync)
1327 cmd = qgetenv (
"COMSPEC").constData ();
1329 if (! cmd.isEmpty ())
1332 PROCESS_INFORMATION
pi;
1334 ZeroMemory (&si,
sizeof (si));
1335 si.cb =
sizeof (si);
1336 ZeroMemory (&
pi,
sizeof (
pi));
1338 if (CreateProcessW (
nullptr,
1339 (LPWSTR)cmd.unicode (),
1349 CloseHandle (
pi.hThread);
1351 WaitForSingleObject (
m_process, INFINITE);
1365 #define TEXT_CHUNK_SIZE 512
1372 int len = s.length ();
1374 DWORD nEvents = 0, written;
1375 HANDLE hStdIn = GetStdHandle (STD_INPUT_HANDLE);
1377 ZeroMemory (events,
sizeof (events));
1379 for (
int i = 0; i <
len; i++)
1383 if (c == L
'\r' || c == L
'\n')
1385 if (c == L
'\r' && i < (
len - 1) && s.at (i+1) == L
'\n')
1389 events[nEvents].EventType = KEY_EVENT;
1390 events[nEvents].Event.KeyEvent.bKeyDown = TRUE;
1391 events[nEvents].Event.KeyEvent.wRepeatCount = 1;
1392 events[nEvents].Event.KeyEvent.wVirtualKeyCode =
1394 events[nEvents].Event.KeyEvent.wVirtualScanCode = 0;
1395 events[nEvents].Event.KeyEvent.uChar.UnicodeChar = c.unicode ();
1396 events[nEvents].Event.KeyEvent.dwControlKeyState = 0;
1399 WriteConsoleInput (hStdIn, events, nEvents, &written);
1401 ZeroMemory (events,
sizeof (events));
1406 events[nEvents].EventType = KEY_EVENT;
1407 events[nEvents].Event.KeyEvent.bKeyDown = TRUE;
1408 events[nEvents].Event.KeyEvent.wRepeatCount = 1;
1409 events[nEvents].Event.KeyEvent.wVirtualKeyCode =
1410 LOBYTE (VkKeyScan (c.unicode ()));
1411 events[nEvents].Event.KeyEvent.wVirtualScanCode = 0;
1412 events[nEvents].Event.KeyEvent.uChar.UnicodeChar = c.unicode ();
1413 events[nEvents].Event.KeyEvent.dwControlKeyState = 0;
1418 || (nEvents > 0 && i == (
len - 1)))
1420 WriteConsoleInput (hStdIn, events, nEvents, &written);
1422 ZeroMemory (events,
sizeof (events));
1445 installEventFilter (
this);
1447 setAcceptDrops (
true);
1473 else if (event->button () == Qt::LeftButton)
1495 if (event->button () == Qt::LeftButton)
1504 QTimer::singleShot (QApplication::doubleClickInterval (),
this,
1512 if (event->button () == Qt::LeftButton)
1540 QRect updateRect =
event->rect ();
1543 int cx1 = updateRect.left () / cw;
1544 int cy1 = updateRect.top () / ch;
1545 int cx2 = qMin (
d->
m_consoleRect.width () - 1, updateRect.right () / cw);
1546 int cy2 = qMin (
d->
m_consoleRect.height () - 1, updateRect.bottom () / ch);
1555 d->
drawText (p, cx1, cy1, cx2, cy2, cw, ch);
1639 QWidget::focusInEvent (event);
1649 QWidget::focusOutEvent (event);
1656 if (event->type () == QEvent::KeyPress)
1658 QKeyEvent* k =
static_cast<QKeyEvent*
>(event);
1659 if (k->key () == Qt::Key_Tab)
1682 QWidget::keyPressEvent (event);
1735 const QColor& color)
1759 d->
log (
"emit set_screen_size_signal (%d, %d)\n", columns, lines);
1768 if(!hasFocus())
return;
1770 QClipboard *clipboard = QApplication::clipboard ();
1774 if (selection.isEmpty ())
1781 clipboard->setText (selection);
1790 if(!hasFocus())
return;
1792 QString text = QApplication::clipboard()->text (QClipboard::Clipboard);
1794 if (! text.isEmpty ())
1796 if (text.contains (
"\t"))
1798 qWarning (
"Tabs replaced with spaces in pasted text before processing");
1799 text.replace (
"\t",
" ");
1810 if(!hasFocus())
return;
1829 if (event->mimeData ()->hasUrls ())
1831 event->acceptProposedAction();
1841 if (event->mimeData ()->hasUrls ())
1843 foreach (QUrl url, event->mimeData ()->urls ())
1845 if(dropText.length () > 0)
1847 dropText += url.toLocalFile ();
static QString translateKey(QKeyEvent *ev)
static void maybeSwapPoints(QPoint &begin, QPoint &end)
QColor backgroundColor(void) const
void log(const char *fmt,...)
void updateConsoleView(bool grab=true)
void drawText(QPainter &p, int cx1, int cy1, int cx2, int cy2, int cw, int ch)
void updateSelection(void)
QScrollBar * m_horizontalScrollBar
QScrollBar * m_verticalScrollBar
void monitorConsole(void)
QTimer * m_consoleWatcher
void setCursorColor(bool useForegroundColor, const QColor &color)
void setupStandardIO(DWORD stdHandleId, int fd, const char *name, const char *devName)
void clearSelection(void)
QConsoleThread * m_consoleThread
QColor foregroundColor(void) const
QColor cursorColor(void) const
KeyboardCursorType m_cursorType
void setHorizontalScrollValue(int value)
void syncConsoleParameters(void)
QPoint posToCell(const QPoint &pt)
QConsoleView * m_consoleView
static const int BLINK_DELAY
void drawSelection(QPainter &p, int cx1, int cy1, int cx2, int cy2, int cw, int ch)
void drawTextBackground(QPainter &p, int cx1, int cy1, int cx2, int cy2, int cw, int ch)
void setForegroundColor(const QColor &color)
QTimer * m_blinkCursorTimer
QConsolePrivate(QWinTerminalImpl *parent, const QString &cmd=QString())
void updateHorizontalScrollBar(void)
void setScrollBufferSize(int value)
void selectWord(const QPoint &cellPos)
void selectLine(const QPoint &cellPos)
void closeStandardIO(int fd, DWORD stdHandleId, const char *name)
void setVerticalScrollValue(int value)
void setBackgroundColor(const QColor &color)
void grabConsoleBuffer(CHAR_INFO *buf=0)
QColor selectionColor(void) const
void updateVerticalScrollBar(void)
void setSelectionColor(const QColor &color)
void drawCursor(QPainter &p)
void updateConsoleSize(bool sync=false, bool allow_smaller_width=false)
QString getSelection(void)
void sendConsoleText(const QString &s)
QConsoleThread(QWinTerminalImpl *console)
void resizeEvent(QResizeEvent *event)
void paintEvent(QPaintEvent *event)
QConsoleView(QWinTerminalImpl *parent=0)
void report_status_message(const QString &)
void set_screen_size_signal(int, int)
void terminal_interrupt(void)
void setCursorColor(bool useForegoundColor, const QColor &color)
void dropEvent(QDropEvent *event)
void setForegroundColor(const QColor &color)
friend class QConsoleView
void mouseReleaseEvent(QMouseEvent *event)
void init_terminal_size(void)
void setSize(int columns, int lines)
void sendText(const QString &s)
void pasteClipboard(void)
void dragEnterEvent(QDragEnterEvent *event)
void updateSelection(void)
void setTerminalFont(const QFont &font)
void horizontalScrollValueChanged(int value)
void blinkCursorEvent(void)
void tripleClickTimeout(void)
QWinTerminalImpl(octave::base_qobject &, QWidget *parent)
void setBlinkingCursorState(bool blink)
void setScrollBufferSize(int value)
void mouseDoubleClickEvent(QMouseEvent *event)
void mousePressEvent(QMouseEvent *event)
void has_extra_interrupt(bool)
void setCursorType(CursorType type, bool blinking)
void monitorConsole(void)
void focusInEvent(QFocusEvent *)
void wheelEvent(QWheelEvent *)
void verticalScrollValueChanged(int value)
friend class QConsoleThread
bool eventFilter(QObject *obj, QEvent *ev)
void viewResizeEvent(QConsoleView *, QResizeEvent *)
void viewPaintEvent(QConsoleView *, QPaintEvent *)
void setBackgroundColor(const QColor &color)
void setSelectionColor(const QColor &color)
void setBlinkingCursor(bool blink)
void mouseMoveEvent(QMouseEvent *event)
void titleChanged(const QString &)
void keyPressEvent(QKeyEvent *)
void focusOutEvent(QFocusEvent *)
void mouseTripleClickEvent(QMouseEvent *event)
F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE * d
F77_RET_T const F77_DBLE * x
F77_RET_T const F77_DBLE const F77_DBLE * f
std::FILE * fopen(const std::string &filename, const std::string &mode)
std::complex< double > w(std::complex< double > z, double relerr=0)
int dup2(int old_fd, int new_fd)