23 #include <QApplication>
27 #include <QGridLayout>
28 #include <QPaintEvent>
30 #include <QResizeEvent>
37 #include <QMessageBox>
38 #include <QDragEnterEvent>
47 #define WIN32_LEAN_AND_MEAN
48 #if ! defined (_WIN32_WINNT) && ! defined (NTDDI_VERSION)
49 #define _WIN32_WINNT 0x0500
52 #include <versionhelpers.h>
61 #define LOGFILENAME "QConsole.log"
63 #define HIDDEN_CONSOLE
66 # pragma warning(disable : 4996)
104 QString esc =
"\x1b";
107 if (ev->key () == Qt::Key_Delete)
109 else if (!ev->text ().isEmpty ())
148 case Qt::Key_PageDown:
196 void log (
const char* fmt, ...);
200 const char* devName);
215 void setCursorColor (
bool useForegroundColor,
const QColor& color);
274 if (end.y () < begin.y ()
275 || (end.y () == begin.y () && end.x () < begin.x ()))
282 : q (parent), m_command (cmd), m_auto_scroll (true), m_cursorBlinking (false),
283 m_hasBlinkingCursor (true), m_cursorType (
BlockCursor),
284 m_beginSelection (0, 0), m_endSelection (0, 0), m_settingSelection (false),
285 m_process (nullptr), m_inWheelEvent (false)
290 log (
"Detaching from existing console (if any)...\n");
292 log (
"Closing standard IO...\n");
297 #ifdef HIDDEN_CONSOLE
298 HWINSTA hOrigSta, hNewSta;
301 hOrigSta = GetProcessWindowStation ();
302 hNewSta = CreateWindowStation (
nullptr, 0, GENERIC_ALL,
nullptr);
303 log (
"Current Windows station: %p.\nNew Windows station: %p.\n", hOrigSta,
305 if (! SetProcessWindowStation (hNewSta))
306 log (
"Failed to switch to new Windows station.\n");
308 if (! AllocConsole ())
309 log (
"Failed to create new console.\n");
310 #ifdef HIDDEN_CONSOLE
311 if (! SetProcessWindowStation (hOrigSta))
312 log (
"Failed to restore original Windows station.\n");
313 if (! CloseWindowStation (hNewSta))
314 log (
"Failed to close new Windows station.\n");
317 log (
"New (hidden) console created.\n");
323 log (
"Standard input/output/error set up.\n");
325 *stdin = *(fdopen (0,
"rb"));
326 *stdout = *(fdopen (1,
"wb"));
327 *stderr = *(fdopen (2,
"wb"));
329 log (
"POSIX standard streams created.\n");
331 setvbuf (stdin,
nullptr, _IONBF, 0);
332 setvbuf (stdout,
nullptr, _IONBF, 0);
333 setvbuf (stderr,
nullptr, _IONBF, 0);
335 log (
"POSIX standard stream buffers adjusted.\n");
337 HANDLE hStdOut = GetStdHandle (STD_OUTPUT_HANDLE);
339 log (
"Console allocated: hStdOut: %p\n", hStdOut);
345 #ifdef HIDDEN_CONSOLE
349 CONSOLE_SCREEN_BUFFER_INFO sbi;
351 GetConsoleScreenBufferInfo (hStdOut, &sbi);
352 m_bufferSize = QSize (sbi.dwSize.X, qMax (sbi.dwSize.Y, (SHORT)500));
354 sbi.srWindow.Right - sbi.srWindow.Left + 1,
355 sbi.srWindow.Bottom - sbi.srWindow.Top + 1);
356 m_cursorPos = QPoint (sbi.dwCursorPosition.X, sbi.dwCursorPosition.Y);
358 log (
"Initial console parameters:\n");
361 log (
" window: (%d, %d) -> (%d, %d) [%d x %d]\n",
366 wchar_t titleBuf[260];
367 GetConsoleTitleW (titleBuf,
sizeof (titleBuf));
368 q->setWindowTitle (QString::fromWCharArray (titleBuf));
370 m_font.setFamily (
"Lucida Console");
372 m_font.setStyleHint (QFont::TypeWriter);
380 QGridLayout* l =
new QGridLayout (parent);
381 l->setContentsMargins (0, 0, 0, 0);
387 if (IsWindows7OrGreater ())
389 SetConsoleCP (65001);
390 SetConsoleOutputCP (65001);
399 SetConsoleTextAttribute (
m_stdOut, 0x07);
412 parent->setFocusPolicy (Qt::StrongFocus);
466 const char*
name,
const char* devName)
468 log (
"Opening %s...\n", devName);
470 int fd = open (devName, _O_RDWR | _O_BINARY);
476 log (
"Opened %s is not at target file descriptor %d, "
477 "duplicating...\n",
name, targetFd);
478 if (
dup2 (fd, targetFd) == -1)
479 log (
"Failed to duplicate file descriptor: errno=%d.\n", errno);
480 if (close (fd) == -1)
481 log (
"Failed to close original file descriptor: errno=%d.\n",
485 log (
"%s opened and assigned to file descriptor %d.\n", devName, fd);
486 if (! SetStdHandle (stdHandleId, (HANDLE) _get_osfhandle (targetFd)))
487 log (
"Failed to re-assign %s: error=%08x.\n",
name, GetLastError ());
490 log (
"Failed to open %s: errno=%d.\n", devName, errno);
511 COORD bufSize, bufCoord;
515 nr = end.y () - begin.y () + 1;
524 bufRect.Top = begin.y ();
525 bufRect.Bottom = end.y ();
527 if (ReadConsoleOutput (
m_stdOut, buf, bufSize, bufCoord, &bufRect))
529 int start_pos = begin.x ();
530 int end_pos = (nr - 1) *
m_bufferSize.width () + end.x ();
531 int lastNonSpace = -1;
533 for (
int i = start_pos; i <= end_pos; i++)
537 if (lastNonSpace >= 0)
538 selection.truncate (lastNonSpace);
539 selection.append (
'\n');
540 lastNonSpace = selection.length ();
543 QChar c (buf[i].Char.UnicodeChar);
547 selection.append (c);
549 lastNonSpace = selection.length ();
552 if (lastNonSpace >= 0)
553 selection.truncate (lastNonSpace);
604 QPalette palette (color);
605 palette.setColor(QPalette::Base, color);
627 CONSOLE_SCREEN_BUFFER_INFO sbi;
628 GetConsoleScreenBufferInfo (
m_stdOut, &sbi);
636 int cx2,
int cy2,
int cw,
int ch)
640 int ascent = p.fontMetrics ().ascent ();
642 int y = ascent + cy1 * ch;;
644 for (
int j = cy1; j <= cy2; j++, y += ch)
647 bool hasChar =
false;
651 for (
int i = cx1; i <= cx2; i++)
653 CHAR_INFO* ci = &(
m_buffer[stride*j+i]);
655 if ((ci->Attributes & 0x00ff) != attr)
661 if (hasChar || (attr & 0x00f0))
664 p.fillRect (
x, y-ascent,
len * cw, ch, p.brush ());
672 attr = (ci->Attributes & 0x00ff);
673 p.setBrush (
m_colors[(attr >> 4) & 0x000f]);
678 if (ci->Char.UnicodeChar != L
' ')
682 if (
len != 0 && (hasChar || (attr & 0x00f0)))
689 p.fillRect (
x, y-ascent,
len * cw, ch, p.brush ());
706 QPoint begin = cellpos;
707 QPoint end = cellpos;
715 begin.ry () -= verticalScrollOffset;
716 end.ry () -= verticalScrollOffset;
718 begin.rx () -= horizontalScrollOffset;
719 end.rx () -= horizontalScrollOffset;
722 if (QChar(
m_buffer[begin.y ()*stride + begin.x ()].Char.UnicodeChar).isSpace () ==
false)
725 while(begin.x () > 0 &&
726 QChar(
m_buffer[begin.y ()*stride + begin.x () -1].Char.UnicodeChar).isSpace() ==
false)
732 QChar(
m_buffer[end.y ()*stride + end.x () +1].Char.UnicodeChar).isSpace() ==
false)
739 while(begin.x () > 0 &&
740 QChar(
m_buffer[begin.y ()*stride + begin.x () -1].Char.UnicodeChar).isSpace())
746 QChar(
m_buffer[end.y ()*stride + end.x () +1].Char.UnicodeChar).isSpace ())
753 begin.ry () += verticalScrollOffset;
754 end.ry () += verticalScrollOffset;
756 begin.rx () += horizontalScrollOffset;
757 end.rx () += horizontalScrollOffset;
774 int cx2,
int cy2,
int cw,
int ch)
781 bool haveSelection = (begin != end);
789 begin.ry () -= verticalScrollOffset;
790 end.ry () -= verticalScrollOffset;
792 begin.rx () -= horizontalScrollOffset;
793 end.rx () -= horizontalScrollOffset;
795 int ascent = p.fontMetrics ().ascent ();
798 int y = ascent + cy1 * ch;;
799 for (
int j = cy1; j <= cy2; j++, y += ch)
801 int charsThisLine = 0;
803 bool hasChar =
false;
806 for (
int i = cx1; i <= cx2; i++)
808 CHAR_INFO* ci = &(
m_buffer[stride*j+i]);
810 if ((ci->Attributes & 0x00ff) != attr)
815 charsThisLine +=
len;
821 attr = (ci->Attributes & 0x00ff);
826 if (ci->Char.UnicodeChar != L
' ')
830 if (
len != 0 && (hasChar || (attr & 0x00f0)))
831 charsThisLine +=
len;
833 if (haveSelection && j >= begin.y () && j <= end.y ())
835 int selectionBegin = j == begin.y () ? begin.x (): 0;
837 int len = ((j == end.y () && end.x () < charsThisLine)
838 ? end.x () - selectionBegin + 1
839 : stride - selectionBegin);
841 p.fillRect (selectionBegin * cw, y-ascent,
len * cw, ch,
863 p.fillRect (rect, color);
869 int penWidth = qMax (1, p.pen().width());
871 p.drawRect (rect.adjusted (penWidth/2, penWidth/2,
872 - penWidth/2 - penWidth%2,
873 - penWidth/2 - penWidth%2));
878 p.drawLine (rect.left (), rect.bottom (),
879 rect.right (), rect.bottom ());
883 p.drawLine (rect.left (), rect.top (),
884 rect.left (), rect.bottom ());
892 int cx2,
int cy2,
int cw,
int ch)
900 s.reserve (cx2 - cx1 + 1);
902 int ascent = p.fontMetrics ().ascent ();
905 int y = ascent + cy1 * ch;;
906 for (
int j = cy1; j <= cy2; j++, y += ch)
910 bool hasChar =
false;
914 for (
int i = cx1; i <= cx2; i++)
916 CHAR_INFO* ci = &(
m_buffer[stride*j+i]);
918 if ((ci->Attributes & 0x00ff) != attr)
924 if (hasChar || (attr & 0x00f0))
925 p.drawText (
x, y, s);
927 x += (s.length () * cw);
932 attr = (ci->Attributes & 0x00ff);
937 s.append (ci->Char.UnicodeChar);
938 if (ci->Char.UnicodeChar != L
' ')
942 if (! s.isEmpty () && (hasChar || (attr & 0x00f0)))
948 p.drawText (
x, y, s);
960 if (close (fd) == -1)
961 log (
"Failed to close file descriptor %d: errno=%d.\n", fd, errno);
962 if (! CloseHandle (GetStdHandle (stdHandleId)))
963 log (
"Failed to close Win32 %s: error=%08x.\n",
name, GetLastError ());
970 #ifdef DEBUG_QCONSOLE
977 vfprintf (flog, fmt, l);
999 m_charSize.rwidth () = fm.averageCharWidth ();
1002 m_consoleRect.setWidth (winSize.width () / fm.averageCharWidth ());
1003 m_consoleRect.setHeight (winSize.height () / fm.lineSpacing ());
1022 qputenv (
"LINES", QByteArray::number (
m_consoleRect.height ()));
1023 qputenv (
"COLUMNS", QByteArray::number (
m_consoleRect.width ()));
1034 log (
"Console resized:\n");
1035 log (
" widget size: %d x %d\n", winSize.width (), winSize.height ());
1038 log (
" window: (%d, %d) -> (%d, %d) [%d x %d]\n",
1054 CONSOLE_SCREEN_BUFFER_INFO sbi;
1057 GetConsoleScreenBufferInfo (hStdOut, &sbi);
1062 bs.X = sbi.dwSize.X;
1064 sr.Left = sbi.srWindow.Left;
1065 sr.Right = sbi.srWindow.Right;
1069 if (bs.Y > sbi.dwSize.Y)
1071 SetConsoleScreenBufferSize (hStdOut, bs);
1072 SetConsoleWindowInfo (hStdOut, TRUE, &sr);
1076 SetConsoleWindowInfo (hStdOut, TRUE, &sr);
1077 SetConsoleScreenBufferSize (hStdOut, bs);
1084 if (bs.X > sbi.dwSize.X)
1086 SetConsoleScreenBufferSize (hStdOut, bs);
1087 SetConsoleWindowInfo (hStdOut, TRUE, &sr);
1091 SetConsoleWindowInfo (hStdOut, TRUE, &sr);
1092 SetConsoleScreenBufferSize (hStdOut, bs);
1095 log (
"Sync'ing console parameters:\n");
1096 log (
" buffer size: %d x %d\n", bs.X, bs.Y);
1097 log (
" window: (%d, %d) -> (%d, %d)\n",
1098 sr.Left, sr.Top, sr.Right, sr.Bottom);
1128 log (
"ReadConsoleOutput (%d,%d) -> (%d,%d)\n",
r.Left,
r.Top,
r.Right,
r.Bottom);
1130 qCritical (
"cannot read console output");
1146 log (
"Horizontal scrollbar parameters updated: %d/%d/%d/%d\n",
1164 log (
"Vertical scrollbar parameters updated: %d/%d/%d/%d\n",
1187 log (
"Scrolling window horizontally: (%d, %d) -> (%d, %d) [%d x %d]\n",
1188 r.Left,
r.Top,
r.Right,
r.Bottom,
1189 r.Right -
r.Left + 1,
r.Bottom -
r.Top + 1);
1191 if (SetConsoleWindowInfo (hStdOut, TRUE, &
r))
1214 log (
"Scrolling window vertically: (%d, %d) -> (%d, %d) [%d x %d]\n",
1215 r.Left,
r.Top,
r.Right,
r.Bottom,
1216 r.Right -
r.Left + 1,
r.Bottom -
r.Top + 1);
1218 if (SetConsoleWindowInfo (hStdOut, TRUE, &
r))
1222 CONSOLE_SCREEN_BUFFER_INFO sbi;
1223 if (GetConsoleScreenBufferInfo (hStdOut, &sbi))
1249 CONSOLE_SCREEN_BUFFER_INFO sbi;
1250 HANDLE hStdOut = GetStdHandle (STD_OUTPUT_HANDLE);
1252 static wchar_t titleBuf[260];
1254 GetConsoleTitleW (titleBuf,
sizeof (titleBuf));
1255 QString title = QString::fromWCharArray (titleBuf);
1259 q->setWindowTitle (title);
1263 if (GetConsoleScreenBufferInfo (hStdOut, &sbi))
1298 log (
"--> Console window changed\n");
1300 sbi.srWindow.Right - sbi.srWindow.Left + 1,
1301 sbi.srWindow.Bottom - sbi.srWindow.Top + 1);
1331 cmd = qgetenv (
"COMSPEC").constData ();
1333 if (! cmd.isEmpty ())
1336 PROCESS_INFORMATION
pi;
1338 ZeroMemory (&si,
sizeof (si));
1339 si.cb =
sizeof (si);
1340 ZeroMemory (&
pi,
sizeof (
pi));
1342 if (CreateProcessW (
nullptr,
1343 (LPWSTR)cmd.unicode (),
1353 CloseHandle (
pi.hThread);
1355 WaitForSingleObject (
m_process, INFINITE);
1369 #define TEXT_CHUNK_SIZE 512
1376 int len = s.length ();
1378 DWORD nEvents = 0, written;
1379 HANDLE hStdIn = GetStdHandle (STD_INPUT_HANDLE);
1381 ZeroMemory (events,
sizeof (events));
1383 for (
int i = 0; i <
len; i++)
1387 if (c == L
'\r' || c == L
'\n')
1389 if (c == L
'\r' && i < (
len - 1) && s.at (i+1) == L
'\n')
1393 events[nEvents].EventType = KEY_EVENT;
1394 events[nEvents].Event.KeyEvent.bKeyDown = TRUE;
1395 events[nEvents].Event.KeyEvent.wRepeatCount = 1;
1396 events[nEvents].Event.KeyEvent.wVirtualKeyCode =
1398 events[nEvents].Event.KeyEvent.wVirtualScanCode = 0;
1399 events[nEvents].Event.KeyEvent.uChar.UnicodeChar = c.unicode ();
1400 events[nEvents].Event.KeyEvent.dwControlKeyState = 0;
1403 WriteConsoleInput (hStdIn, events, nEvents, &written);
1405 ZeroMemory (events,
sizeof (events));
1410 events[nEvents].EventType = KEY_EVENT;
1411 events[nEvents].Event.KeyEvent.bKeyDown = TRUE;
1412 events[nEvents].Event.KeyEvent.wRepeatCount = 1;
1413 events[nEvents].Event.KeyEvent.wVirtualKeyCode =
1414 LOBYTE (VkKeyScan (c.unicode ()));
1415 events[nEvents].Event.KeyEvent.wVirtualScanCode = 0;
1416 events[nEvents].Event.KeyEvent.uChar.UnicodeChar = c.unicode ();
1417 events[nEvents].Event.KeyEvent.dwControlKeyState = 0;
1422 || (nEvents > 0 && i == (
len - 1)))
1424 WriteConsoleInput (hStdIn, events, nEvents, &written);
1426 ZeroMemory (events,
sizeof (events));
1448 installEventFilter (
this);
1456 parent, SLOT (set_screen_size (
int,
int)));
1458 setAcceptDrops (
true);
1491 else if (event->button () == Qt::LeftButton)
1513 if (event->button () == Qt::LeftButton)
1522 QTimer::singleShot (QApplication::doubleClickInterval (),
this,
1530 if (event->button () == Qt::LeftButton)
1558 QRect updateRect =
event->rect ();
1561 int cx1 = updateRect.left () / cw;
1562 int cy1 = updateRect.top () / ch;
1563 int cx2 = qMin (
d->
m_consoleRect.width () - 1, updateRect.right () / cw);
1564 int cy2 = qMin (
d->
m_consoleRect.height () - 1, updateRect.bottom () / ch);
1573 d->
drawText (p, cx1, cy1, cx2, cy2, cw, ch);
1659 QWidget::focusInEvent (event);
1671 QWidget::focusOutEvent (event);
1678 if (event->type () == QEvent::KeyPress)
1680 QKeyEvent* k =
static_cast<QKeyEvent*
>(event);
1681 if (k->key () == Qt::Key_Tab)
1704 QWidget::keyPressEvent (event);
1757 const QColor& color)
1781 d->
log (
"emit set_screen_size_signal (%d, %d)\n", columns, lines);
1790 if(!hasFocus())
return;
1792 QClipboard *clipboard = QApplication::clipboard ();
1796 if (selection.isEmpty ())
1803 clipboard->setText (selection);
1812 if(!hasFocus())
return;
1814 QString
text = QApplication::clipboard()->
text (QClipboard::Clipboard);
1816 if (!
text.isEmpty ())
1824 if(!hasFocus())
return;
1843 if (event->mimeData ()->hasUrls ())
1845 event->acceptProposedAction();
1855 if (event->mimeData ()->hasUrls ())
1857 foreach (QUrl url, event->mimeData ()->urls ())
1859 if(dropText.length () > 0)
1861 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_global_shortcuts(bool focus_out)
void terminal_interrupt(void)
void setCursorColor(bool useForegoundColor, const QColor &color)
void dropEvent(QDropEvent *event)
void set_global_shortcuts_signal(bool)
void set_screen_size_signal(int, int)
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)
void setBlinkingCursorState(bool blink)
void setScrollBufferSize(int value)
void mouseDoubleClickEvent(QMouseEvent *event)
void mousePressEvent(QMouseEvent *event)
void has_extra_interrupt(bool)
QWinTerminalImpl(QWidget *parent=0)
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)
text(const graphics_handle &mh, const graphics_handle &p)
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::complex< double > w(std::complex< double > z, double relerr=0)
std::FILE * fopen(const std::string &filename, const std::string &mode)
int dup2(int old_fd, int new_fd)