23 #include <QApplication>
27 #include <QHBoxLayout>
28 #include <QPaintEvent>
30 #include <QResizeEvent>
37 #include <QMessageBox>
43 #define WIN32_LEAN_AND_MEAN
44 #if ! defined (_WIN32_WINNT) && ! defined (NTDDI_VERSION)
45 #define _WIN32_WINNT 0x0500
57 #define LOGFILENAME "QConsole.log"
59 #define HIDDEN_CONSOLE
62 # pragma warning(disable : 4996)
100 QString esc =
"\x1b";
103 if (ev->key () == Qt::Key_Delete)
105 else if (!ev->text ().isEmpty ())
144 case Qt::Key_PageDown:
187 void log (
const char* fmt, ...);
191 const char* devName);
206 void setCursorColor (
bool useForegroundColor,
const QColor& color);
211 void drawSelection (QPainter& p,
int cx1,
int cy1,
int cx2,
int cy2,
216 void drawText (QPainter& p,
int cx1,
int cy1,
int cx2,
int cy2,
262 if (end.y () < begin.y ()
263 || (end.y () == begin.y () && end.x () < begin.x ()))
270 : q (parent), m_command (cmd), m_hasBlinkingCursor (true),
271 m_cursorType (
BlockCursor), m_beginSelection (0, 0),
272 m_endSelection (0, 0), m_settingSelection (false),
273 m_process (NULL), m_inWheelEvent (false)
278 log (
"Detaching from existing console (if any)...\n");
280 log (
"Closing standard IO...\n");
285 #ifdef HIDDEN_CONSOLE
286 HWINSTA hOrigSta, hNewSta;
289 hOrigSta = GetProcessWindowStation ();
290 hNewSta = CreateWindowStation (NULL, 0, GENERIC_ALL, NULL);
291 log (
"Current Windows station: %p.\nNew Windows station: %p.\n", hOrigSta,
293 if (! SetProcessWindowStation (hNewSta))
294 log (
"Failed to switch to new Windows station.\n");
296 if (! AllocConsole ())
297 log (
"Failed to create new console.\n");
298 #ifdef HIDDEN_CONSOLE
299 if (! SetProcessWindowStation (hOrigSta))
300 log (
"Failed to restore original Windows station.\n");
301 if (! CloseWindowStation (hNewSta))
302 log (
"Failed to close new Windows station.\n");
305 log (
"New (hidden) console created.\n");
311 log (
"Standard input/output/error set up.\n");
313 *stdin = *(fdopen (0,
"rb"));
314 *stdout = *(fdopen (1,
"wb"));
315 *stderr = *(fdopen (2,
"wb"));
317 log (
"POSIX standard streams created.\n");
319 setvbuf (stdin, NULL, _IONBF, 0);
320 setvbuf (stdout, NULL, _IONBF, 0);
321 setvbuf (stderr, NULL, _IONBF, 0);
323 log (
"POSIX standard stream buffers adjusted.\n");
325 HANDLE hStdOut = GetStdHandle (STD_OUTPUT_HANDLE);
327 log (
"Console allocated: hStdOut: %p\n", hStdOut);
333 #ifdef HIDDEN_CONSOLE
337 CONSOLE_SCREEN_BUFFER_INFO sbi;
339 GetConsoleScreenBufferInfo (hStdOut, &sbi);
340 m_bufferSize = QSize (sbi.dwSize.X, qMax (sbi.dwSize.Y, (SHORT)500));
342 sbi.srWindow.Right - sbi.srWindow.Left + 1,
343 sbi.srWindow.Bottom - sbi.srWindow.Top + 1);
344 m_cursorPos = QPoint (sbi.dwCursorPosition.X, sbi.dwCursorPosition.Y);
346 log (
"Initial console parameters:\n");
349 log (
" window: (%d, %d) -> (%d, %d) [%d x %d]\n",
354 wchar_t titleBuf[260];
355 GetConsoleTitleW (titleBuf,
sizeof (titleBuf));
356 q->setWindowTitle (QString::fromWCharArray (titleBuf));
358 m_font.setFamily (
"Lucida Console");
360 m_font.setStyleHint (QFont::TypeWriter);
365 m_scrollBar =
new QScrollBar (Qt::Vertical, parent);
367 QHBoxLayout* l =
new QHBoxLayout (parent);
368 l->setContentsMargins (0, 0, 0, 0);
379 SetConsoleTextAttribute (
m_stdOut, 0xF0);
394 parent->setFocusPolicy (Qt::StrongFocus);
407 QObject::connect (
m_scrollBar, SIGNAL (valueChanged (
int)),
443 const char* name,
const char* devName)
445 log (
"Opening %s...\n", devName);
447 int fd = open (devName, _O_RDWR | _O_BINARY);
453 log (
"Opened %s is not at target file descriptor %d, "
454 "duplicating...\n", name, targetFd);
455 if (dup2 (fd, targetFd) == -1)
456 log (
"Failed to duplicate file descriptor: errno=%d.\n", errno);
457 if (close (fd) == -1)
458 log (
"Failed to close original file descriptor: errno=%d.\n",
462 log (
"%s opened and assigned to file descriptor %d.\n", devName, fd);
463 if (! SetStdHandle (stdHandleId, (HANDLE) _get_osfhandle (targetFd)))
464 log (
"Failed to re-assign %s: error=%08x.\n", name, GetLastError ());
467 log (
"Failed to open %s: errno=%d.\n", devName, errno);
488 COORD bufSize, bufCoord;
492 nr = end.y () - begin.y () + 1;
501 bufRect.Top = begin.y ();
502 bufRect.Bottom = end.y ();
504 if (ReadConsoleOutput (
m_stdOut, buf, bufSize, bufCoord, &bufRect))
506 int start_pos = begin.x ();
507 int end_pos = (nr - 1) *
m_bufferSize.width () + end.x ();
508 int lastNonSpace = -1;
510 for (
int i = start_pos; i <= end_pos; i++)
514 if (lastNonSpace >= 0)
515 selection.truncate (lastNonSpace);
516 selection.append (
'\n');
517 lastNonSpace = selection.length ();
520 QChar c (buf[i].Char.UnicodeChar);
522 selection.append (c);
524 lastNonSpace = selection.length ();
527 if (lastNonSpace >= 0)
528 selection.truncate (lastNonSpace);
597 int cx2,
int cy2,
int cw,
int ch)
601 int ascent = p.fontMetrics ().ascent ();
603 int y = ascent + cy1 * ch;;
605 for (
int j = cy1; j <= cy2; j++, y += ch)
608 bool hasChar =
false;
612 for (
int i = cx1; i <= cx2; i++)
614 CHAR_INFO* ci = &(
m_buffer[stride*j+i]);
616 if ((ci->Attributes & 0x00ff) != attr)
622 if (hasChar || (attr & 0x00f0))
625 p.fillRect (x, y-ascent, len * cw, ch, p.brush ());
633 attr = (ci->Attributes & 0x00ff);
634 p.setBrush (
m_colors[(attr >> 4) & 0x000f]);
639 if (ci->Char.UnicodeChar != L
' ')
643 if (len != 0 && (hasChar || (attr & 0x00f0)))
650 p.fillRect (x, y-ascent, len * cw, ch, p.brush ());
658 int cx2,
int cy2,
int cw,
int ch)
665 bool haveSelection = (begin != end);
672 begin.ry () -= scrollOffset;
673 end.ry () -= scrollOffset;
675 int ascent = p.fontMetrics ().ascent ();
678 int y = ascent + cy1 * ch;;
679 for (
int j = cy1; j <= cy2; j++, y += ch)
681 int charsThisLine = 0;
683 bool hasChar =
false;
686 for (
int i = cx1; i <= cx2; i++)
688 CHAR_INFO* ci = &(
m_buffer[stride*j+i]);
690 if ((ci->Attributes & 0x00ff) != attr)
695 charsThisLine += len;
701 attr = (ci->Attributes & 0x00ff);
706 if (ci->Char.UnicodeChar != L
' ')
710 if (len != 0 && (hasChar || (attr & 0x00f0)))
711 charsThisLine += len;
713 if (haveSelection && j >= begin.y () && j <= end.y ())
715 int selectionBegin = j == begin.y () ? begin.x (): 0;
717 int len = ((j == end.y () && end.x () < charsThisLine)
718 ? end.x () - selectionBegin + 1
719 : stride - selectionBegin);
721 p.fillRect (selectionBegin * cw, y-ascent, len * cw, ch,
743 p.fillRect (rect, color);
749 int penWidth = qMax (1, p.pen().width());
751 p.drawRect (rect.adjusted (penWidth/2, penWidth/2,
752 - penWidth/2 - penWidth%2,
753 - penWidth/2 - penWidth%2));
758 p.drawLine (rect.left (), rect.bottom (),
759 rect.right (), rect.bottom ());
763 p.drawLine (rect.left (), rect.top (),
764 rect.left (), rect.bottom ());
772 int cx2,
int cy2,
int cw,
int ch)
780 s.reserve (cx2 - cx1 + 1);
782 int ascent = p.fontMetrics ().ascent ();
785 int y = ascent + cy1 * ch;;
786 for (
int j = cy1; j <= cy2; j++, y += ch)
790 bool hasChar =
false;
794 for (
int i = cx1; i <= cx2; i++)
796 CHAR_INFO* ci = &(
m_buffer[stride*j+i]);
798 if ((ci->Attributes & 0x00ff) != attr)
804 if (hasChar || (attr & 0x00f0))
805 p.drawText (x, y, s);
807 x += (s.length () * cw);
812 attr = (ci->Attributes & 0x00ff);
817 s.append (ci->Char.UnicodeChar);
818 if (ci->Char.UnicodeChar != L
' ')
822 if (! s.isEmpty () && (hasChar || (attr & 0x00f0)))
828 p.drawText (x, y, s);
840 if (close (fd) == -1)
841 log (
"Failed to close file descriptor %d: errno=%d.\n", fd, errno);
842 if (! CloseHandle (GetStdHandle (stdHandleId)))
843 log (
"Failed to close Win32 %s: error=%08x.\n", name, GetLastError ());
850 #ifdef DEBUG_QCONSOLE
857 vfprintf (flog, fmt, l);
879 m_charSize.rwidth () = fm.averageCharWidth ();
882 m_consoleRect.setWidth (winSize.width () / fm.averageCharWidth ());
883 m_consoleRect.setHeight (winSize.height () / fm.lineSpacing ());
893 log (
"Console resized:\n");
894 log (
" widget size: %d x %d\n", winSize.width (), winSize.height ());
897 log (
" window: (%d, %d) -> (%d, %d) [%d x %d]\n",
912 CONSOLE_SCREEN_BUFFER_INFO sbi;
915 GetConsoleScreenBufferInfo (hStdOut, &sbi);
922 sr.Left = sbi.srWindow.Left;
923 sr.Right = sbi.srWindow.Right;
927 if (bs.Y > sbi.dwSize.Y)
929 SetConsoleScreenBufferSize (hStdOut, bs);
930 SetConsoleWindowInfo (hStdOut, TRUE, &sr);
934 SetConsoleWindowInfo (hStdOut, TRUE, &sr);
935 SetConsoleScreenBufferSize (hStdOut, bs);
942 if (bs.X > sbi.dwSize.X)
944 SetConsoleScreenBufferSize (hStdOut, bs);
945 SetConsoleWindowInfo (hStdOut, TRUE, &sr);
949 SetConsoleWindowInfo (hStdOut, TRUE, &sr);
950 SetConsoleScreenBufferSize (hStdOut, bs);
953 log (
"Sync'ing console parameters:\n");
954 log (
" buffer size: %d x %d\n", bs.X, bs.Y);
955 log (
" window: (%d, %d) -> (%d, %d)\n",
956 sr.Left, sr.Top, sr.Right, sr.Bottom);
987 qCritical (
"cannot read console output");
1003 log (
"Scrollbar parameters updated: %d/%d/%d/%d\n",
1026 log (
"Scrolling window: (%d, %d) -> (%d, %d) [%d x %d]\n",
1027 r.Left, r.Top, r.Right, r.Bottom,
1028 r.Right - r.Left + 1, r.Bottom - r.Top + 1);
1030 if (SetConsoleWindowInfo (hStdOut, TRUE, &r))
1051 CONSOLE_SCREEN_BUFFER_INFO sbi;
1052 HANDLE hStdOut = GetStdHandle (STD_OUTPUT_HANDLE);
1054 static wchar_t titleBuf[260];
1056 GetConsoleTitleW (titleBuf,
sizeof (titleBuf));
1057 QString title = QString::fromWCharArray (titleBuf);
1061 q->setWindowTitle (title);
1065 if (GetConsoleScreenBufferInfo (hStdOut, &sbi))
1099 sbi.srWindow.Right - sbi.srWindow.Left + 1,
1100 sbi.srWindow.Bottom - sbi.srWindow.Top + 1);
1129 cmd = qgetenv (
"COMSPEC").constData ();
1131 if (! cmd.isEmpty ())
1134 PROCESS_INFORMATION pi;
1136 ZeroMemory (&si,
sizeof (si));
1137 si.cb =
sizeof (si);
1138 ZeroMemory (&pi,
sizeof (pi));
1140 if (CreateProcessW (NULL,
1141 (LPWSTR)cmd.unicode (),
1151 CloseHandle (pi.hThread);
1167 #define TEXT_CHUNK_SIZE 512
1172 int len = s.length ();
1174 DWORD nEvents = 0, written;
1175 HANDLE hStdIn = GetStdHandle (STD_INPUT_HANDLE);
1177 ZeroMemory (events,
sizeof (events));
1179 for (
int i = 0; i < len; i++)
1183 if (c == L
'\r' || c == L
'\n')
1185 if (c == L
'\r' && i < (len - 1) && s.at (i+1) == L
'\n')
1189 WriteConsoleInput (hStdIn, events, nEvents, &written);
1191 ZeroMemory (events,
sizeof (events));
1198 events[nEvents].EventType = KEY_EVENT;
1199 events[nEvents].Event.KeyEvent.bKeyDown = TRUE;
1200 events[nEvents].Event.KeyEvent.wRepeatCount = 1;
1201 events[nEvents].Event.KeyEvent.wVirtualKeyCode =
1202 LOBYTE (VkKeyScan (c.unicode ()));
1203 events[nEvents].Event.KeyEvent.wVirtualScanCode = 0;
1204 events[nEvents].Event.KeyEvent.uChar.UnicodeChar = c.unicode ();
1205 events[nEvents].Event.KeyEvent.dwControlKeyState = 0;
1210 || (nEvents > 0 && i == (len - 1)))
1212 WriteConsoleInput (hStdIn, events, nEvents, &written);
1214 ZeroMemory (events,
sizeof (events));
1235 installEventFilter (
this);
1238 parent, SLOT (set_global_shortcuts (
bool)));
1267 if (event->button () == Qt::LeftButton)
1277 if (event->button () == Qt::LeftButton)
1304 QRect updateRect =
event->rect ();
1306 int cx1 = updateRect.left () / cw;
1307 int cy1 = updateRect.top () / ch;
1308 int cx2 = qMin (
d->
m_consoleRect.width () - 1, updateRect.right () / cw);
1309 int cy2 = qMin (
d->
m_consoleRect.height () - 1, updateRect.bottom () / ch);
1318 d->
drawText (p, cx1, cy1, cx2, cy2, cw, ch);
1411 if (event->type () == QEvent::KeyPress)
1413 QKeyEvent* k =
static_cast<QKeyEvent*
>(event);
1414 if (k->key () == Qt::Key_Tab)
1490 const QColor& color)
1516 if(!hasFocus())
return;
1518 QClipboard *clipboard = QApplication::clipboard ();
1522 if (selection.isEmpty ())
1526 clipboard->setText (selection);
1536 if(!hasFocus())
return;
1538 QString
text = QApplication::clipboard()->
text (QClipboard::Clipboard);
1540 if (! text.isEmpty ())