GNU Octave  4.0.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
QWinTerminalImpl.cpp
Go to the documentation of this file.
1 
2 /*
3 
4 Copyright (C) 2011, 2013 Michael Goffioul.
5 
6 This file is part of QConsole.
7 
8 This program is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program. If not,
20 see <http://www.gnu.org/licenses/>.
21 
22 */
23 
24 #include <QApplication>
25 #include <QClipboard>
26 #include <QColor>
27 #include <QFont>
28 #include <QGridLayout>
29 #include <QPaintEvent>
30 #include <QPainter>
31 #include <QResizeEvent>
32 #include <QScrollBar>
33 #include <QtDebug>
34 #include <QThread>
35 #include <QTimer>
36 #include <QToolTip>
37 #include <QCursor>
38 #include <QMessageBox>
39 #include <QDragEnterEvent>
40 #include <QDropEvent>
41 #include <QUrl>
42 
43 #include <fcntl.h>
44 #include <io.h>
45 #include <stdio.h>
46 #include <stdarg.h>
47 #define WIN32_LEAN_AND_MEAN
48 #if ! defined (_WIN32_WINNT) && ! defined (NTDDI_VERSION)
49 #define _WIN32_WINNT 0x0500
50 #endif
51 #include <windows.h>
52 #include <cstring>
53 #include <csignal>
54 #include <limits>
55 
56 #include "QWinTerminalImpl.h"
57 #include "QTerminalColors.h"
58 
59 // Uncomment to log activity to LOGFILENAME
60 // #define DEBUG_QCONSOLE
61 #define LOGFILENAME "QConsole.log"
62 // Uncomment to create hidden console window
63 #define HIDDEN_CONSOLE
64 
65 #ifdef _MSC_VER
66 # pragma warning(disable : 4996)
67 #endif
68 
69 //////////////////////////////////////////////////////////////////////////////
70 
71 class QConsoleView : public QWidget
72 {
73 public:
74  QConsoleView (QWinTerminalImpl* parent = 0) : QWidget (parent), q (parent) { }
75  ~QConsoleView (void) { }
76 
77 protected:
78  void paintEvent (QPaintEvent* event) { q->viewPaintEvent (this, event); }
79  void resizeEvent (QResizeEvent* event) { q->viewResizeEvent (this, event); }
80 
81 private:
83 };
84 
85 //////////////////////////////////////////////////////////////////////////////
86 
87 class QConsoleThread : public QThread
88 {
89 public:
90  QConsoleThread (QWinTerminalImpl* console) : QThread (console), q (console) { }
91 
92 protected:
93  void run (void)
94  { q->start (); }
95 
96 private:
98 };
99 
100 //////////////////////////////////////////////////////////////////////////////
101 
102 static QString translateKey (QKeyEvent *ev)
103 {
104  QString esc = "\x1b";
105  QString s;
106 
107  if (ev->key () == Qt::Key_Delete)
108  s = esc + "[C\b";
109  else if (!ev->text ().isEmpty ())
110  s = ev->text ();
111  else
112  {
113 
114  switch (ev->key ())
115  {
116  case Qt::Key_Up:
117  s = esc + "[A";
118  break;
119 
120  case Qt::Key_Down:
121  s = esc + "[B";
122  break;
123 
124  case Qt::Key_Right:
125  s = esc + "[C";
126  break;
127 
128  case Qt::Key_Left:
129  s = esc + "[D";
130  break;
131 
132  case Qt::Key_Home:
133  s = esc + "[H";
134  break;
135 
136  case Qt::Key_End:
137  s = esc + "[F";
138  break;
139 
140  case Qt::Key_Insert:
141  s = esc + "[2~";
142  break;
143 
144  case Qt::Key_PageUp:
145  s = esc + "[5~";
146  break;
147 
148  case Qt::Key_PageDown:
149  s = esc + "[6~";
150  break;
151 
152  case Qt::Key_Escape:
153  s = esc;
154  break;
155 
156  default:
157  break;
158  }
159  }
160 
161  return s;
162 }
163 
165 {
166  friend class QWinTerminalImpl;
167 
168 public:
169 
171  {
175  };
176 
177  QConsolePrivate (QWinTerminalImpl* parent, const QString& cmd = QString ());
178  ~QConsolePrivate (void);
179 
180  void updateConsoleSize (bool sync = false, bool allow_smaller_width = false);
181  void syncConsoleParameters (void);
182  void grabConsoleBuffer (CHAR_INFO* buf = 0);
183  void updateHorizontalScrollBar (void);
184  void updateVerticalScrollBar (void);
185  void setHorizontalScrollValue (int value);
186  void setVerticalScrollValue (int value);
187  void updateConsoleView (bool grab = true);
188  void monitorConsole (void);
189  void startCommand (void);
190  void sendConsoleText (const QString& s);
191  QRect cursorRect (void);
192  void selectAll();
193  void selectWord(const QPoint& cellPos);
194  void selectLine(const QPoint& cellPos);
195 
196  void log (const char* fmt, ...);
197 
198  void closeStandardIO (int fd, DWORD stdHandleId, const char* name);
199  void setupStandardIO (DWORD stdHandleId, int fd, const char* name,
200  const char* devName);
201 
202  QPoint posToCell (const QPoint& pt);
203  QString getSelection (void);
204  void updateSelection (void);
205  void clearSelection (void);
206 
207  QColor backgroundColor (void) const;
208  QColor foregroundColor (void) const;
209  QColor selectionColor (void) const;
210  QColor cursorColor (void) const;
211 
212  void setBackgroundColor (const QColor& color);
213  void setForegroundColor (const QColor& color);
214  void setSelectionColor (const QColor& color);
215  void setCursorColor (bool useForegroundColor, const QColor& color);
216  void setScrollBufferSize (int value);
217 
218  void drawTextBackground (QPainter& p, int cx1, int cy1, int cx2, int cy2,
219  int cw, int ch);
220 
221  void drawSelection (QPainter& p, int cx1, int cy1, int cx2, int cy2,
222  int cw, int ch);
223 
224  void drawCursor (QPainter& p);
225 
226  void drawText (QPainter& p, int cx1, int cy1, int cx2, int cy2,
227  int cw, int ch);
228 
229 private:
231 
232 private:
233  QFont m_font;
234  QString m_command;
237  QString m_title;
238 
239  QSize m_charSize;
242  QPoint m_cursorPos;
247 
251 
254 
255  HANDLE m_stdOut;
257  CHAR_INFO* m_buffer;
258  CHAR_INFO* m_tmpBuffer;
259  HANDLE m_process;
260 
263  QScrollBar* m_verticalScrollBar;
266 
267  // The delay in milliseconds between redrawing blinking text.
268  static const int BLINK_DELAY = 500;
269 };
270 
271 static void maybeSwapPoints (QPoint& begin, QPoint& end)
272 {
273  if (end.y () < begin.y ()
274  || (end.y () == begin.y () && end.x () < begin.x ()))
275  qSwap (begin, end);
276 }
277 
278 //////////////////////////////////////////////////////////////////////////////
279 
281  : q (parent), m_command (cmd), m_cursorBlinking (false),
282  m_hasBlinkingCursor (true), m_cursorType (BlockCursor),
283  m_beginSelection (0, 0), m_endSelection (0, 0), m_settingSelection (false),
284  m_process (NULL), m_inWheelEvent (false)
285 {
286  log (NULL);
287 
288  // Possibly detach from any existing console
289  log ("Detaching from existing console (if any)...\n");
290  FreeConsole ();
291  log ("Closing standard IO...\n");
292  closeStandardIO (0, STD_INPUT_HANDLE, "STDIN");
293  closeStandardIO (1, STD_OUTPUT_HANDLE, "STDOUT");
294  closeStandardIO (2, STD_ERROR_HANDLE, "STDERR");
295 
296 #ifdef HIDDEN_CONSOLE
297  HWINSTA hOrigSta, hNewSta;
298 
299  // Create new (hidden) console
300  hOrigSta = GetProcessWindowStation ();
301  hNewSta = CreateWindowStation (NULL, 0, GENERIC_ALL, NULL);
302  log ("Current Windows station: %p.\nNew Windows station: %p.\n", hOrigSta,
303  hNewSta);
304  if (! SetProcessWindowStation (hNewSta))
305  log ("Failed to switch to new Windows station.\n");
306 #endif
307  if (! AllocConsole ())
308  log ("Failed to create new console.\n");
309 #ifdef HIDDEN_CONSOLE
310  if (! SetProcessWindowStation (hOrigSta))
311  log ("Failed to restore original Windows station.\n");
312  if (! CloseWindowStation (hNewSta))
313  log ("Failed to close new Windows station.\n");
314 #endif
315 
316  log ("New (hidden) console created.\n");
317 
318  setupStandardIO (STD_INPUT_HANDLE, 0, "STDIN", "CONIN$");
319  setupStandardIO (STD_OUTPUT_HANDLE, 1, "STDOUT", "CONOUT$");
320  setupStandardIO (STD_ERROR_HANDLE, 2, "STDERR", "CONOUT$");
321 
322  log ("Standard input/output/error set up.\n");
323 
324  *stdin = *(fdopen (0, "rb"));
325  *stdout = *(fdopen (1, "wb"));
326  *stderr = *(fdopen (2, "wb"));
327 
328  log ("POSIX standard streams created.\n");
329 
330  setvbuf (stdin, NULL, _IONBF, 0);
331  setvbuf (stdout, NULL, _IONBF, 0);
332  setvbuf (stderr, NULL, _IONBF, 0);
333 
334  log ("POSIX standard stream buffers adjusted.\n");
335 
336  HANDLE hStdOut = GetStdHandle (STD_OUTPUT_HANDLE);
337 
338  log ("Console allocated: hStdOut: %p\n", hStdOut);
339 
340  m_stdOut = hStdOut;
341  m_consoleWindow = GetConsoleWindow ();
342 
343  // In case the console window hasn't been created hidden...
344 #ifdef HIDDEN_CONSOLE
345  ShowWindow (m_consoleWindow, SW_HIDE);
346 #endif
347 
348  CONSOLE_SCREEN_BUFFER_INFO sbi;
349 
350  GetConsoleScreenBufferInfo (hStdOut, &sbi);
351  m_bufferSize = QSize (sbi.dwSize.X, qMax (sbi.dwSize.Y, (SHORT)500));
352  m_consoleRect = QRect (sbi.srWindow.Left, sbi.srWindow.Top,
353  sbi.srWindow.Right - sbi.srWindow.Left + 1,
354  sbi.srWindow.Bottom - sbi.srWindow.Top + 1);
355  m_cursorPos = QPoint (sbi.dwCursorPosition.X, sbi.dwCursorPosition.Y);
356 
357  log ("Initial console parameters:\n");
358  log (" buffer size: %d x %d\n", m_bufferSize.width (),
359  m_bufferSize.height ());
360  log (" window: (%d, %d) -> (%d, %d) [%d x %d]\n",
361  m_consoleRect.left (), m_consoleRect.top (),
362  m_consoleRect.right (), m_consoleRect.bottom (),
363  m_consoleRect.width (), m_consoleRect.height ());
364 
365  wchar_t titleBuf[260];
366  GetConsoleTitleW (titleBuf, sizeof (titleBuf));
367  q->setWindowTitle (QString::fromWCharArray (titleBuf));
368 
369  m_font.setFamily ("Lucida Console");
370  m_font.setPointSize (9);
371  m_font.setStyleHint (QFont::TypeWriter);
372 
373  m_buffer = m_tmpBuffer = 0;
374 
375  m_consoleView = new QConsoleView (parent);
376  m_horizontalScrollBar = new QScrollBar (Qt::Horizontal, parent);
377  m_verticalScrollBar = new QScrollBar (Qt::Vertical, parent);
378 
379  QGridLayout* l = new QGridLayout (parent);
380  l->setContentsMargins (0, 0, 0, 0);
381  l->setSpacing (0);
382  l->addWidget (m_consoleView, 0, 0);
383  l->addWidget (m_horizontalScrollBar, 1, 0);
384  l->addWidget (m_verticalScrollBar, 0, 1);
385 
386  // Choose 15 (0xF) as index into the Windows console color map for the
387  // background and 0 (0x0) as the index for the foreground. This
388  // selection corresponds to the indices used in the foregroundColor,
389  // setForegroundColor, backgroundColor, and SetBackgroundColor
390  // functions.
391 
392  SetConsoleTextAttribute (m_stdOut, 0xF0);
393 
394  // Defaults.
395  setBackgroundColor (Qt::white);
396  setForegroundColor (Qt::black);
397  setSelectionColor (Qt::lightGray);
398  setCursorColor (false, Qt::darkGray);
399 
400  // FIXME -- should we set the palette?
401  QPalette palette (backgroundColor ());
402  m_consoleView->setPalette (palette);
403 
404  m_consoleView->setAutoFillBackground (true);
405 
406  m_consoleView->setFont (m_font);
407  parent->setFocusPolicy (Qt::StrongFocus);
408  parent->winId ();
409 
412 
413  m_consoleWatcher = new QTimer (parent);
414  m_consoleWatcher->setInterval (10);
415  m_consoleWatcher->setSingleShot (false);
416 
417  m_blinkCursorTimer = new QTimer (parent);
418  QObject::connect (m_blinkCursorTimer, SIGNAL (timeout()),
419  q, SLOT (blinkCursorEvent ()));
420 
421  QObject::connect (m_horizontalScrollBar, SIGNAL (valueChanged (int)),
422  q, SLOT (horizontalScrollValueChanged (int)));
423 
424  QObject::connect (m_verticalScrollBar, SIGNAL (valueChanged (int)),
425  q, SLOT (verticalScrollValueChanged (int)));
426 
427  QObject::connect (m_consoleWatcher, SIGNAL (timeout (void)),
428  q, SLOT (monitorConsole (void)));
429 
430  m_consoleWatcher->start ();
431 
432  if (m_command.isEmpty ())
433  m_consoleThread = 0;
434  else
435  {
437  QObject::connect (m_consoleThread, SIGNAL (finished (void)),
438  q, SIGNAL (terminated (void)));
439  m_consoleThread->start ();
440  }
441 }
442 
443 //////////////////////////////////////////////////////////////////////////////
444 
446 {
447  if (m_consoleThread && m_consoleThread->isRunning () && m_process)
448  {
449  TerminateProcess (m_process, (UINT)-1);
450  m_consoleThread->wait ();
451  }
452  if (m_buffer)
453  delete [] m_buffer;
454  if (m_tmpBuffer)
455  delete [] m_tmpBuffer;
456 }
457 
458 //////////////////////////////////////////////////////////////////////////////
459 
460 void QConsolePrivate::setupStandardIO (DWORD stdHandleId, int targetFd,
461  const char* name, const char* devName)
462 {
463  log ("Opening %s...\n", devName);
464 
465  int fd = open (devName, _O_RDWR | _O_BINARY);
466 
467  if (fd != -1)
468  {
469  if (fd != targetFd)
470  {
471  log ("Opened %s is not at target file descriptor %d, "
472  "duplicating...\n", name, targetFd);
473  if (dup2 (fd, targetFd) == -1)
474  log ("Failed to duplicate file descriptor: errno=%d.\n", errno);
475  if (close (fd) == -1)
476  log ("Failed to close original file descriptor: errno=%d.\n",
477  errno);
478  }
479  else
480  log ("%s opened and assigned to file descriptor %d.\n", devName, fd);
481  if (! SetStdHandle (stdHandleId, (HANDLE) _get_osfhandle (targetFd)))
482  log ("Failed to re-assign %s: error=%08x.\n", name, GetLastError ());
483  }
484  else
485  log ("Failed to open %s: errno=%d.\n", devName, errno);
486 }
487 
488 QPoint QConsolePrivate::posToCell (const QPoint& p)
489 {
490  return QPoint (m_consoleRect.left () + p.x () / m_charSize.width (),
491  m_consoleRect.top () + p.y () / m_charSize.height ());
492 }
493 
495 {
496  QString selection;
497 
498  QPoint begin = m_beginSelection;
499  QPoint end = m_endSelection;
500 
501  maybeSwapPoints (begin, end);
502 
503  if (begin != end)
504  {
505  CHAR_INFO* buf;
506  COORD bufSize, bufCoord;
507  SMALL_RECT bufRect;
508  int nr;
509 
510  nr = end.y () - begin.y () + 1;
511  buf = new CHAR_INFO[m_bufferSize.width () * nr];
512  bufSize.X = m_bufferSize.width ();
513  bufSize.Y = nr;
514  bufCoord.X = 0;
515  bufCoord.Y = 0;
516 
517  bufRect.Left = 0;
518  bufRect.Right = m_bufferSize.width ();
519  bufRect.Top = begin.y ();
520  bufRect.Bottom = end.y ();
521 
522  if (ReadConsoleOutput (m_stdOut, buf, bufSize, bufCoord, &bufRect))
523  {
524  int start_pos = begin.x ();
525  int end_pos = (nr - 1) * m_bufferSize.width () + end.x ();
526  int lastNonSpace = -1;
527 
528  for (int i = start_pos; i <= end_pos; i++)
529  {
530  if (i && (i % m_bufferSize.width ()) == 0)
531  {
532  if (lastNonSpace >= 0)
533  selection.truncate (lastNonSpace);
534  selection.append ('\n');
535  lastNonSpace = selection.length ();
536  }
537 
538  QChar c (buf[i].Char.UnicodeChar);
539 
540  selection.append (c);
541  if (! c.isSpace ())
542  lastNonSpace = selection.length ();
543  }
544 
545  if (lastNonSpace >= 0)
546  selection.truncate (lastNonSpace);
547  }
548  }
549 
550  return selection;
551 }
552 
554 {
555  QPoint begin = m_beginSelection;
556  QPoint end = m_endSelection;
557 
558  maybeSwapPoints (begin, end);
559 
560  begin.rx () = 0;
561  end.rx () = m_consoleRect.width ();
562 
563  m_consoleView->update ();
564 }
565 
567 {
568  m_beginSelection = m_endSelection = QPoint ();
569 
570  m_consoleView->update ();
571 }
572 
574 {
575  return m_colors[15];
576 }
577 
579 {
580  return m_colors[0];
581 }
582 
584 {
585  return m_selectionColor;
586 }
587 
588 QColor QConsolePrivate::cursorColor (void) const
589 {
590  return m_cursorColor.isValid () ? m_cursorColor : foregroundColor ();
591 }
592 
593 void QConsolePrivate::setBackgroundColor (const QColor& color)
594 {
595  m_colors[15] = color;
596 
597  QPalette palette (color);
598  m_consoleView->setPalette (palette);
599 }
600 
601 void QConsolePrivate::setForegroundColor (const QColor& color)
602 {
603  m_colors[0] = color;
604 }
605 
606 void QConsolePrivate::setSelectionColor (const QColor& color)
607 {
608  m_selectionColor = color;
609 }
610 
611 void QConsolePrivate::setCursorColor (bool useForegroundColor,
612  const QColor& color)
613 {
614  m_cursorColor = useForegroundColor ? QColor () : color;
615 }
616 
618 {
619  CONSOLE_SCREEN_BUFFER_INFO sbi;
620  GetConsoleScreenBufferInfo (m_stdOut, &sbi);
621 
622  m_bufferSize = QSize (sbi.dwSize.X, (SHORT)value);
623 
624  updateConsoleSize (true);
625 }
626 
627 void QConsolePrivate::drawTextBackground (QPainter& p, int cx1, int cy1,
628  int cx2, int cy2, int cw, int ch)
629 {
630  p.save ();
631 
632  int ascent = p.fontMetrics ().ascent ();
633  int stride = m_consoleRect.width ();
634  int y = ascent + cy1 * ch;;
635 
636  for (int j = cy1; j <= cy2; j++, y += ch)
637  {
638  int len = 0;
639  bool hasChar = false;
640  int x = cx1 * cw;
641  WORD attr = 0;
642 
643  for (int i = cx1; i <= cx2; i++)
644  {
645  CHAR_INFO* ci = &(m_buffer[stride*j+i]);
646 
647  if ((ci->Attributes & 0x00ff) != attr)
648  {
649  // Character attributes changed
650  if (len != 0)
651  {
652  // String buffer not empty -> draw it
653  if (hasChar || (attr & 0x00f0))
654  {
655  if (attr & 0x00f0)
656  p.fillRect (x, y-ascent, len * cw, ch, p.brush ());
657  }
658 
659  x += (len * cw);
660  len = 0;
661  hasChar = false;
662  }
663  // Update current brush and store current attributes
664  attr = (ci->Attributes & 0x00ff);
665  p.setBrush (m_colors[(attr >> 4) & 0x000f]);
666  }
667 
668  // Append current character to the string buffer
669  len++;
670  if (ci->Char.UnicodeChar != L' ')
671  hasChar = true;
672  }
673 
674  if (len != 0 && (hasChar || (attr & 0x00f0)))
675  {
676  // Line end reached, but string buffer not empty -> draw it
677  // No need to update s or x, they will be reset on the next
678  // for-loop iteration
679 
680  if (attr & 0x00f0)
681  p.fillRect (x, y-ascent, len * cw, ch, p.brush ());
682  }
683  }
684 
685  p.restore ();
686 }
687 
689 {
690  m_beginSelection = QPoint (0,0);
691  m_endSelection = QPoint(m_bufferSize.width (),
692  m_cursorPos.y());
693  updateSelection();
694 }
695 
696 void QConsolePrivate::selectWord (const QPoint & cellpos)
697 {
698  QPoint begin = cellpos;
699  QPoint end = cellpos;
700 
701  int stride = m_consoleRect.width ();
702 
703  int verticalScrollOffset = m_consoleRect.top ();
704  int horizontalScrollOffset = m_consoleRect.left ();
705 
706  // get begin, end in buffer offsets
707  begin.ry () -= verticalScrollOffset;
708  end.ry () -= verticalScrollOffset;
709 
710  begin.rx () -= horizontalScrollOffset;
711  end.rx () -= horizontalScrollOffset;
712 
713  // loog at current clicked on char to determinate ig getting space chunk or nonspace chunk
714  if (QChar(m_buffer[begin.y ()*stride + begin.x ()].Char.UnicodeChar).isSpace () == false)
715  {
716  // from current char, go back and fwd to find start and end of block
717  while(begin.x () > 0 &&
718  QChar(m_buffer[begin.y ()*stride + begin.x () -1].Char.UnicodeChar).isSpace() == false)
719  {
720  begin.rx () --;
721  }
722 
723  while(end.x () < m_consoleRect.width () &&
724  QChar(m_buffer[end.y ()*stride + end.x () +1].Char.UnicodeChar).isSpace() == false)
725  {
726  end.rx () ++;
727  }
728  }
729  else
730  {
731  while(begin.x () > 0 &&
732  QChar(m_buffer[begin.y ()*stride + begin.x () -1].Char.UnicodeChar).isSpace())
733  {
734  begin.rx () --;
735  }
736 
737  while(end.x () < m_consoleRect.width () &&
738  QChar(m_buffer[end.y ()*stride + end.x () +1].Char.UnicodeChar).isSpace ())
739  {
740  end.rx () ++;
741  }
742  }
743 
744  // convert console offsets to absolute cell positions
745  begin.ry () += verticalScrollOffset;
746  end.ry () += verticalScrollOffset;
747 
748  begin.rx () += horizontalScrollOffset;
749  end.rx () += horizontalScrollOffset;
750 
751  m_beginSelection = begin;
752  m_endSelection = end;
753 
754  updateSelection ();
755 }
756 
757 void QConsolePrivate::selectLine (const QPoint & cellpos)
758 {
759  m_beginSelection = QPoint (0, cellpos.y ());
760  m_endSelection = QPoint (m_bufferSize.width ()-1, cellpos.y ());
761  updateSelection ();
762 }
763 
764 
765 void QConsolePrivate::drawSelection (QPainter& p, int cx1, int cy1,
766  int cx2, int cy2, int cw, int ch)
767 {
768  p.save ();
769 
770  QPoint begin = m_beginSelection;
771  QPoint end = m_endSelection;
772 
773  bool haveSelection = (begin != end);
774 
775  if (haveSelection)
776  maybeSwapPoints (begin, end);
777 
778  int verticalScrollOffset = m_consoleRect.top ();
779  int horizontalScrollOffset = m_consoleRect.left ();
780 
781  begin.ry () -= verticalScrollOffset;
782  end.ry () -= verticalScrollOffset;
783 
784  begin.rx () -= horizontalScrollOffset;
785  end.rx () -= horizontalScrollOffset;
786 
787  int ascent = p.fontMetrics ().ascent ();
788  int stride = m_consoleRect.width ();
789 
790  int y = ascent + cy1 * ch;;
791  for (int j = cy1; j <= cy2; j++, y += ch)
792  {
793  int charsThisLine = 0;
794  int len = 0;
795  bool hasChar = false;
796  WORD attr = 0;
797 
798  for (int i = cx1; i <= cx2; i++)
799  {
800  CHAR_INFO* ci = &(m_buffer[stride*j+i]);
801 
802  if ((ci->Attributes & 0x00ff) != attr)
803  {
804  // Character attributes changed
805  if (len != 0)
806  {
807  charsThisLine += len;
808  len = 0;
809  hasChar = false;
810  }
811 
812  // Store current attributes
813  attr = (ci->Attributes & 0x00ff);
814  }
815 
816  // Append current character to the string buffer
817  len++;
818  if (ci->Char.UnicodeChar != L' ')
819  hasChar = true;
820  }
821 
822  if (len != 0 && (hasChar || (attr & 0x00f0)))
823  charsThisLine += len;
824 
825  if (haveSelection && j >= begin.y () && j <= end.y ())
826  {
827  int selectionBegin = j == begin.y () ? begin.x (): 0;
828 
829  int len = ((j == end.y () && end.x () < charsThisLine)
830  ? end.x () - selectionBegin + 1
831  : stride - selectionBegin);
832 
833  p.fillRect (selectionBegin * cw, y-ascent, len * cw, ch,
834  selectionColor ());
835  }
836  }
837 
838  p.restore ();
839 }
840 
841 void QConsolePrivate::drawCursor (QPainter& p)
842 {
843  if (! m_cursorBlinking)
844  {
845  p.save ();
846 
847  QRect rect = cursorRect ();
848  QColor color = cursorColor ();
849 
850  p.setPen (color);
851 
853  {
854  if (q->hasFocus ())
855  p.fillRect (rect, color);
856  else
857  {
858  // draw the cursor outline, adjusting the area so that
859  // it is draw entirely inside 'rect'
860 
861  int penWidth = qMax (1, p.pen().width());
862 
863  p.drawRect (rect.adjusted (penWidth/2, penWidth/2,
864  - penWidth/2 - penWidth%2,
865  - penWidth/2 - penWidth%2));
866  }
867  }
869  {
870  p.drawLine (rect.left (), rect.bottom (),
871  rect.right (), rect.bottom ());
872  }
874  {
875  p.drawLine (rect.left (), rect.top (),
876  rect.left (), rect.bottom ());
877  }
878 
879  p.restore ();
880  }
881 }
882 
883 void QConsolePrivate::drawText (QPainter& p, int cx1, int cy1,
884  int cx2, int cy2, int cw, int ch)
885 {
886  p.save ();
887 
888  p.setFont (m_font);
889  p.setPen (foregroundColor ());
890 
891  QString s;
892  s.reserve (cx2 - cx1 + 1);
893 
894  int ascent = p.fontMetrics ().ascent ();
895  int stride = m_consoleRect.width ();
896 
897  int y = ascent + cy1 * ch;;
898  for (int j = cy1; j <= cy2; j++, y += ch)
899  {
900  // Reset string buffer and starting X coordinate
901  s.clear ();
902  bool hasChar = false;
903  int x = cx1 * cw;
904  WORD attr = 0;
905 
906  for (int i = cx1; i <= cx2; i++)
907  {
908  CHAR_INFO* ci = &(m_buffer[stride*j+i]);
909 
910  if ((ci->Attributes & 0x00ff) != attr)
911  {
912  // Character attributes changed
913  if (! s.isEmpty ())
914  {
915  // String buffer not empty -> draw it
916  if (hasChar || (attr & 0x00f0))
917  p.drawText (x, y, s);
918 
919  x += (s.length () * cw);
920  s.clear ();
921  hasChar = false;
922  }
923  // Update current pen and store current attributes
924  attr = (ci->Attributes & 0x00ff);
925  p.setPen (m_colors[attr & 0x000f]);
926  }
927 
928  // Append current character to the string buffer
929  s.append (ci->Char.UnicodeChar);
930  if (ci->Char.UnicodeChar != L' ')
931  hasChar = true;
932  }
933 
934  if (! s.isEmpty () && (hasChar || (attr & 0x00f0)))
935  {
936  // Line end reached, but string buffer not empty -> draw it
937  // No need to update s or x, they will be reset on the next
938  // for-loop iteration
939 
940  p.drawText (x, y, s);
941  }
942  }
943 
944  p.restore ();
945 }
946 
947 /////////////////////////////////////////////////////////////////////////////
948 
949 void QConsolePrivate::closeStandardIO (int fd, DWORD stdHandleId,
950  const char* name)
951 {
952  if (close (fd) == -1)
953  log ("Failed to close file descriptor %d: errno=%d.\n", fd, errno);
954  if (! CloseHandle (GetStdHandle (stdHandleId)))
955  log ("Failed to close Win32 %s: error=%08x.\n", name, GetLastError ());
956 }
957 
958 //////////////////////////////////////////////////////////////////////////////
959 
960 void QConsolePrivate::log (const char* fmt, ...)
961 {
962 #ifdef DEBUG_QCONSOLE
963  if (fmt)
964  {
965  va_list l;
966  FILE* flog = fopen (LOGFILENAME, "ab");
967 
968  va_start (l, fmt);
969  vfprintf (flog, fmt, l);
970  va_end (l);
971  fclose (flog);
972  }
973  else
974  {
975  // Special case to re-initialize the log file
976  FILE* flog = fopen (LOGFILENAME, "w");
977  fclose (flog);
978  }
979 #else
980  Q_UNUSED (fmt);
981 #endif
982 }
983 
984 //////////////////////////////////////////////////////////////////////////////
985 
986 void QConsolePrivate::updateConsoleSize (bool sync, bool allow_smaller_width)
987 {
988  QFontMetrics fm = m_consoleView->fontMetrics ();
989  QSize winSize = m_consoleView->size ();
990 
991  m_charSize.rwidth () = fm.averageCharWidth ();
992  m_charSize.rheight () = fm.lineSpacing ();
993 
994  m_consoleRect.setWidth (winSize.width () / fm.averageCharWidth ());
995  m_consoleRect.setHeight (winSize.height () / fm.lineSpacing ());
996 
997  // Don't shrink the size of the buffer. That way wide lines won't be
998  // truncated and will reappear if the window is enlarged again later.
999 
1000  if (allow_smaller_width || m_consoleRect.width () > m_bufferSize.width ())
1001  m_bufferSize.rwidth () = m_consoleRect.width ();
1002 
1003  if (qMax (m_bufferSize.height (), m_consoleRect.height ())
1004  > m_bufferSize.height ())
1005  m_bufferSize.rheight () = qMax (m_bufferSize.height (),
1006  m_consoleRect.height ());
1007 
1008  // Store the terminal size in the environment. When Octave is
1009  // initialized, we ask the command editor (usually readline) to prefer
1010  // using these values rather than querying the terminal so that the
1011  // buffer size can be larger than the size of the window that the
1012  // command editor will actually use.
1013 
1014  qputenv ("LINES", QByteArray::number (m_consoleRect.height ()));
1015  qputenv ("COLUMNS", QByteArray::number (m_consoleRect.width ()));
1016 
1017  // Force the command line editor (usually readline) to notice the
1018  // change in screen size as soon as possible.
1019 
1020  q->setSize (m_consoleRect.height (), m_consoleRect.width ());
1021 
1022  m_consoleRect.moveLeft (0);
1023  if (m_consoleRect.bottom () >= m_bufferSize.height ())
1024  m_consoleRect.moveTop (m_bufferSize.height () - m_consoleRect.height ());
1025 
1026  log ("Console resized:\n");
1027  log (" widget size: %d x %d\n", winSize.width (), winSize.height ());
1028  log (" buffer size: %d x %d\n", m_bufferSize.width (),
1029  m_bufferSize.height ());
1030  log (" window: (%d, %d) -> (%d, %d) [%d x %d]\n",
1031  m_consoleRect.left (), m_consoleRect.top (),
1032  m_consoleRect.right (), m_consoleRect.bottom (),
1033  m_consoleRect.width (), m_consoleRect.height ());
1034 
1035  if (sync)
1037 
1040 }
1041 
1042 //////////////////////////////////////////////////////////////////////////////
1043 
1045 {
1046  CONSOLE_SCREEN_BUFFER_INFO sbi;
1047  HANDLE hStdOut = m_stdOut;
1048 
1049  GetConsoleScreenBufferInfo (hStdOut, &sbi);
1050 
1051  COORD bs;
1052  SMALL_RECT sr;
1053 
1054  bs.X = sbi.dwSize.X;
1055  bs.Y = m_bufferSize.height ();
1056  sr.Left = sbi.srWindow.Left;
1057  sr.Right = sbi.srWindow.Right;
1058  sr.Top = m_consoleRect.top ();
1059  sr.Bottom = m_consoleRect.bottom ();
1060 
1061  if (bs.Y > sbi.dwSize.Y)
1062  {
1063  SetConsoleScreenBufferSize (hStdOut, bs);
1064  SetConsoleWindowInfo (hStdOut, TRUE, &sr);
1065  }
1066  else
1067  {
1068  SetConsoleWindowInfo (hStdOut, TRUE, &sr);
1069  SetConsoleScreenBufferSize (hStdOut, bs);
1070  }
1071 
1072  bs.X = m_bufferSize.width ();
1073  sr.Left = m_consoleRect.left ();
1074  sr.Right = m_consoleRect.right ();
1075 
1076  if (bs.X > sbi.dwSize.X)
1077  {
1078  SetConsoleScreenBufferSize (hStdOut, bs);
1079  SetConsoleWindowInfo (hStdOut, TRUE, &sr);
1080  }
1081  else
1082  {
1083  SetConsoleWindowInfo (hStdOut, TRUE, &sr);
1084  SetConsoleScreenBufferSize (hStdOut, bs);
1085  }
1086 
1087  log ("Sync'ing console parameters:\n");
1088  log (" buffer size: %d x %d\n", bs.X, bs.Y);
1089  log (" window: (%d, %d) -> (%d, %d)\n",
1090  sr.Left, sr.Top, sr.Right, sr.Bottom);
1091 
1092  if (m_buffer)
1093  delete [] m_buffer;
1094  if (m_tmpBuffer)
1095  delete [] m_tmpBuffer;
1096 
1097  int bufSize = m_consoleRect.width () * m_consoleRect.height ();
1098 
1099  m_buffer = new CHAR_INFO[bufSize];
1100  m_tmpBuffer = new CHAR_INFO[bufSize];
1101 }
1102 
1103 //////////////////////////////////////////////////////////////////////////////
1104 
1106 {
1107  COORD bs, bc;
1108  SMALL_RECT r;
1109 
1110  bs.X = m_consoleRect.width ();
1111  bs.Y = m_consoleRect.height ();
1112  bc.X = 0;
1113  bc.Y = 0;
1114 
1115  r.Left = m_consoleRect.left ();
1116  r.Top = m_consoleRect.top ();
1117  r.Right = m_consoleRect.right ();
1118  r.Bottom = m_consoleRect.bottom ();
1119 
1120  if (! ReadConsoleOutput (m_stdOut, (buf ? buf : m_buffer), bs, bc, &r))
1121  qCritical ("cannot read console output");
1122 }
1123 
1124 //////////////////////////////////////////////////////////////////////////////
1125 
1127 {
1128  m_horizontalScrollBar->setMinimum (0);
1129  if (m_bufferSize.width () > m_consoleRect.width ())
1130  m_horizontalScrollBar->setMaximum (m_bufferSize.width () - m_consoleRect.width ());
1131  else
1132  m_horizontalScrollBar->setMaximum (0);
1133  m_horizontalScrollBar->setSingleStep (1);
1134  m_horizontalScrollBar->setPageStep (m_consoleRect.width ());
1135  m_horizontalScrollBar->setValue (m_consoleRect.left ());
1136 
1137  log ("Horizontal scrollbar parameters updated: %d/%d/%d/%d\n",
1138  m_horizontalScrollBar->minimum (),
1139  m_horizontalScrollBar->maximum (),
1140  m_horizontalScrollBar->singleStep (),
1141  m_horizontalScrollBar->pageStep ());
1142 }
1143 
1145 {
1146  m_verticalScrollBar->setMinimum (0);
1147  if (m_bufferSize.height () > m_consoleRect.height ())
1148  m_verticalScrollBar->setMaximum (m_bufferSize.height () - m_consoleRect.height ());
1149  else
1150  m_verticalScrollBar->setMaximum (0);
1151  m_verticalScrollBar->setSingleStep (1);
1152  m_verticalScrollBar->setPageStep (m_consoleRect.height ());
1153  m_verticalScrollBar->setValue (m_consoleRect.top ());
1154 
1155  log ("Vertical scrollbar parameters updated: %d/%d/%d/%d\n",
1156  m_verticalScrollBar->minimum (), m_verticalScrollBar->maximum (),
1157  m_verticalScrollBar->singleStep (), m_verticalScrollBar->pageStep ());
1158 }
1159 
1160 //////////////////////////////////////////////////////////////////////////////
1161 
1163 {
1164  if (value == m_consoleRect.left ())
1165  return;
1166 
1167  SMALL_RECT r;
1168  HANDLE hStdOut = m_stdOut;
1169 
1170  if (value + m_consoleRect.width () > m_bufferSize.width ())
1171  value = m_bufferSize.width () - m_consoleRect.width ();
1172 
1173  r.Left = value;
1174  r.Top = m_consoleRect.top ();
1175  r.Right = value + m_consoleRect.width () - 1;
1176  r.Bottom = m_consoleRect.bottom ();
1177 
1178  log ("Scrolling window horizontally: (%d, %d) -> (%d, %d) [%d x %d]\n",
1179  r.Left, r.Top, r.Right, r.Bottom,
1180  r.Right - r.Left + 1, r.Bottom - r.Top + 1);
1181 
1182  if (SetConsoleWindowInfo (hStdOut, TRUE, &r))
1183  {
1184  m_consoleRect.moveLeft (value);
1185  updateConsoleView ();
1186  }
1187 }
1188 
1190 {
1191  if (value == m_consoleRect.top ())
1192  return;
1193 
1194  SMALL_RECT r;
1195  HANDLE hStdOut = m_stdOut;
1196 
1197  if (value + m_consoleRect.height () > m_bufferSize.height ())
1198  value = m_bufferSize.height () - m_consoleRect.height ();
1199 
1200  r.Left = m_consoleRect.left ();
1201  r.Top = value;
1202  r.Right = m_consoleRect.right ();
1203  r.Bottom = value + m_consoleRect.height () - 1;
1204 
1205  log ("Scrolling window vertically: (%d, %d) -> (%d, %d) [%d x %d]\n",
1206  r.Left, r.Top, r.Right, r.Bottom,
1207  r.Right - r.Left + 1, r.Bottom - r.Top + 1);
1208 
1209  if (SetConsoleWindowInfo (hStdOut, TRUE, &r))
1210  {
1211  m_consoleRect.moveTop (value);
1212  updateConsoleView ();
1213  }
1214 }
1215 
1216 //////////////////////////////////////////////////////////////////////////////
1217 
1219 {
1220  if (grab)
1221  grabConsoleBuffer ();
1222  m_consoleView->update ();
1223  m_consoleWatcher->start ();
1224 }
1225 
1226 //////////////////////////////////////////////////////////////////////////////
1227 
1229 {
1230  CONSOLE_SCREEN_BUFFER_INFO sbi;
1231  HANDLE hStdOut = GetStdHandle (STD_OUTPUT_HANDLE);
1232 
1233  static wchar_t titleBuf[260];
1234 
1235  GetConsoleTitleW (titleBuf, sizeof (titleBuf));
1236  QString title = QString::fromWCharArray (titleBuf);
1237 
1238  if (title != m_title)
1239  {
1240  q->setWindowTitle (title);
1241  emit q->titleChanged (title);
1242  }
1243 
1244  if (GetConsoleScreenBufferInfo (hStdOut, &sbi))
1245  {
1246  if (m_bufferSize.width () != sbi.dwSize.X
1247  || m_bufferSize.height () != sbi.dwSize.Y)
1248  {
1249  // Buffer size changed
1250  m_bufferSize.rwidth () = sbi.dwSize.X;
1251  m_bufferSize.rheight () = sbi.dwSize.Y;
1254  }
1255 
1256  if (m_cursorPos.x () != sbi.dwCursorPosition.X
1257  || m_cursorPos.y () != sbi.dwCursorPosition.Y)
1258  {
1259  // Cursor position changed
1260  m_consoleView->update
1261  ((m_cursorPos.x () - sbi.srWindow.Left) * m_charSize.width (),
1262  (m_cursorPos.y () - sbi.srWindow.Top) * m_charSize.height (),
1263  m_charSize.width (), m_charSize.height ());
1264  m_cursorPos.rx () = sbi.dwCursorPosition.X;
1265  m_cursorPos.ry () = sbi.dwCursorPosition.Y;
1266  m_consoleView->update
1267  ((m_cursorPos.x () - sbi.srWindow.Left) * m_charSize.width (),
1268  (m_cursorPos.y () - sbi.srWindow.Top) * m_charSize.height (),
1269  m_charSize.width (), m_charSize.height ());
1270  }
1271 
1272  if (m_consoleRect.left () != sbi.srWindow.Left
1273  || m_consoleRect.right () != sbi.srWindow.Right
1274  || m_consoleRect.top () != sbi.srWindow.Top
1275  || m_consoleRect.bottom () != sbi.srWindow.Bottom)
1276  {
1277  // Console window changed
1278  m_consoleRect = QRect (sbi.srWindow.Left, sbi.srWindow.Top,
1279  sbi.srWindow.Right - sbi.srWindow.Left + 1,
1280  sbi.srWindow.Bottom - sbi.srWindow.Top + 1);
1283  updateConsoleView ();
1284  return;
1285  }
1286 
1287  if (m_tmpBuffer && m_buffer)
1288  {
1290  if (memcmp (m_tmpBuffer, m_buffer,
1291  sizeof (CHAR_INFO) * m_consoleRect.width () *
1292  m_consoleRect.height ()))
1293  {
1294  // FIXME: compute the area to update based on the
1295  // difference between the 2 buffers.
1296  qSwap (m_buffer, m_tmpBuffer);
1297  updateConsoleView (false);
1298  }
1299  }
1300  }
1301 }
1302 
1303 //////////////////////////////////////////////////////////////////////////////
1304 
1306 {
1307  QString cmd = m_command;
1308 
1309  if (cmd.isEmpty ())
1310  cmd = qgetenv ("COMSPEC").constData ();
1311 
1312  if (! cmd.isEmpty ())
1313  {
1314  STARTUPINFO si;
1315  PROCESS_INFORMATION pi;
1316 
1317  ZeroMemory (&si, sizeof (si));
1318  si.cb = sizeof (si);
1319  ZeroMemory (&pi, sizeof (pi));
1320 
1321  if (CreateProcessW (NULL,
1322  (LPWSTR)cmd.unicode (),
1323  NULL,
1324  NULL,
1325  TRUE,
1326  0,
1327  NULL,
1328  NULL,
1329  &si,
1330  &pi))
1331  {
1332  CloseHandle (pi.hThread);
1333  m_process = pi.hProcess;
1334  WaitForSingleObject (m_process, INFINITE);
1335  CloseHandle (m_process);
1336  m_process = NULL;
1337  }
1338  }
1339 }
1340 
1341 //////////////////////////////////////////////////////////////////////////////
1342 
1343 void QConsolePrivate::sendConsoleText (const QString& s)
1344 {
1345  // Send the string in chunks of 512 characters. Each character is
1346  // translated into an equivalent keypress event.
1347 
1348 #define TEXT_CHUNK_SIZE 512
1349 
1350  // clear any selection on inserting text
1351  clearSelection();
1352 
1353  int len = s.length ();
1354  INPUT_RECORD events[TEXT_CHUNK_SIZE];
1355  DWORD nEvents = 0, written;
1356  HANDLE hStdIn = GetStdHandle (STD_INPUT_HANDLE);
1357 
1358  ZeroMemory (events, sizeof (events));
1359 
1360  for (int i = 0; i < len; i++)
1361  {
1362  QChar c = s.at (i);
1363 
1364  if (c == L'\r' || c == L'\n')
1365  {
1366  if (c == L'\r' && i < (len - 1) && s.at (i+1) == L'\n')
1367  i++;
1368 
1369  // add new line
1370  events[nEvents].EventType = KEY_EVENT;
1371  events[nEvents].Event.KeyEvent.bKeyDown = TRUE;
1372  events[nEvents].Event.KeyEvent.wRepeatCount = 1;
1373  events[nEvents].Event.KeyEvent.wVirtualKeyCode =
1374  VK_RETURN;
1375  events[nEvents].Event.KeyEvent.wVirtualScanCode = 0;
1376  events[nEvents].Event.KeyEvent.uChar.UnicodeChar = c.unicode ();
1377  events[nEvents].Event.KeyEvent.dwControlKeyState = 0;
1378  nEvents++;
1379 
1380  WriteConsoleInput (hStdIn, events, nEvents, &written);
1381  nEvents = 0;
1382  ZeroMemory (events, sizeof (events));
1383 
1384  }
1385  else
1386  {
1387  events[nEvents].EventType = KEY_EVENT;
1388  events[nEvents].Event.KeyEvent.bKeyDown = TRUE;
1389  events[nEvents].Event.KeyEvent.wRepeatCount = 1;
1390  events[nEvents].Event.KeyEvent.wVirtualKeyCode =
1391  LOBYTE (VkKeyScan (c.unicode ()));
1392  events[nEvents].Event.KeyEvent.wVirtualScanCode = 0;
1393  events[nEvents].Event.KeyEvent.uChar.UnicodeChar = c.unicode ();
1394  events[nEvents].Event.KeyEvent.dwControlKeyState = 0;
1395  nEvents++;
1396  }
1397 
1398  if (nEvents == TEXT_CHUNK_SIZE
1399  || (nEvents > 0 && i == (len - 1)))
1400  {
1401  WriteConsoleInput (hStdIn, events, nEvents, &written);
1402  nEvents = 0;
1403  ZeroMemory (events, sizeof (events));
1404  }
1405  }
1406 }
1407 
1408 QRect
1410 {
1411  int cw = m_charSize.width ();
1412  int ch = m_charSize.height ();
1413 
1414  return QRect ((m_cursorPos.x () - m_consoleRect.x ()) * cw,
1415  (m_cursorPos.y () - m_consoleRect.y ()) * ch,
1416  cw, ch);
1417 }
1418 
1419 //////////////////////////////////////////////////////////////////////////////
1420 
1422  : QTerminal (parent), d (new QConsolePrivate (this)),
1423  allowTripleClick (false)
1424 {
1425  installEventFilter (this);
1426 
1427  connect (this, SIGNAL (set_global_shortcuts_signal (bool)),
1428  parent, SLOT (set_global_shortcuts (bool)));
1429  connect (this, SIGNAL (set_global_shortcuts_signal (bool)),
1430  this, SLOT (set_global_shortcuts (bool)));
1431 
1432  connect (this, SIGNAL (set_screen_size_signal (int, int)),
1433  parent, SLOT (set_screen_size (int, int)));
1434 
1435  setAcceptDrops (true);
1436 }
1437 
1438 //////////////////////////////////////////////////////////////////////////////
1439 
1440 QWinTerminalImpl::QWinTerminalImpl (const QString& cmd, QWidget* parent)
1441  : QTerminal (parent), d (new QConsolePrivate (this, cmd))
1442 {
1443 }
1444 
1445 //////////////////////////////////////////////////////////////////////////////
1446 
1448 {
1449  delete d;
1450 }
1451 
1452 void QWinTerminalImpl::mouseMoveEvent (QMouseEvent *event)
1453 {
1454  if (d->m_settingSelection)
1455  {
1456  d->m_endSelection = d->posToCell (event->pos ());
1457 
1458  updateSelection ();
1459  }
1460 }
1461 
1462 void QWinTerminalImpl::mousePressEvent (QMouseEvent *event)
1463 {
1464  if (allowTripleClick)
1465  {
1466  mouseTripleClickEvent (event);
1467  }
1468  else if (event->button () == Qt::LeftButton)
1469  {
1470  d->m_settingSelection = true;
1471 
1472  d->m_beginSelection = d->posToCell (event->pos ());
1473  }
1474 }
1475 
1476 void QWinTerminalImpl::mouseReleaseEvent (QMouseEvent *event)
1477 {
1478  if (event->button () == Qt::LeftButton && d->m_settingSelection)
1479  {
1480  d->m_endSelection = d->posToCell (event->pos ());
1481 
1482  updateSelection ();
1483 
1484  d->m_settingSelection = false;
1485  }
1486 }
1487 
1489 {
1490  if (event->button () == Qt::LeftButton)
1491  {
1492  // doubleclick - select word
1493  d->m_settingSelection = false;
1494 
1495  d->selectWord (d->posToCell (event->pos ()));
1496 
1497  allowTripleClick = true;
1498 
1499  QTimer::singleShot (QApplication::doubleClickInterval (),this,
1500  SLOT (tripleClickTimeout ()));
1501 
1502  }
1503 }
1504 
1506 {
1507  if (event->button () == Qt::LeftButton)
1508  {
1509  d->selectLine (d->posToCell (event->pos ()));
1510  }
1511 }
1512 
1514 {
1515  allowTripleClick = false;
1516 }
1517 
1518 //////////////////////////////////////////////////////////////////////////////
1519 
1521 {
1522  d->updateConsoleSize (true);
1523  d->grabConsoleBuffer ();
1524 }
1525 
1526 //////////////////////////////////////////////////////////////////////////////
1527 
1529 {
1530  QPainter p (w);
1531 
1532  int cw = d->m_charSize.width ();
1533  int ch = d->m_charSize.height ();
1534 
1535  QRect updateRect = event->rect ();
1536 
1537  int cx1 = updateRect.left () / cw;
1538  int cy1 = updateRect.top () / ch;
1539  int cx2 = qMin (d->m_consoleRect.width () - 1, updateRect.right () / cw);
1540  int cy2 = qMin (d->m_consoleRect.height () - 1, updateRect.bottom () / ch);
1541 
1542  if (cx1 > d->m_consoleRect.width () - 1
1543  || cy1 > d->m_consoleRect.height () - 1)
1544  return;
1545 
1546  d->drawTextBackground (p, cx1, cy1, cx2, cy2, cw, ch);
1547  d->drawSelection (p, cx1, cy1, cx2, cy2, cw, ch);
1548  d->drawCursor (p);
1549  d->drawText (p, cx1, cy1, cx2, cy2, cw, ch);
1550 }
1551 
1553 {
1554  if (d->m_hasBlinkingCursor)
1556  else
1557  d->m_cursorBlinking = false;
1558 
1559  d->m_consoleView->update (d->cursorRect ());
1560 }
1561 
1563 {
1564  d->m_hasBlinkingCursor = blink;
1565 
1566  setBlinkingCursorState (blink);
1567 }
1568 
1570 {
1571  if (blink && ! d->m_blinkCursorTimer->isActive ())
1572  d->m_blinkCursorTimer->start (d->BLINK_DELAY);
1573 
1574  if (! blink && d->m_blinkCursorTimer->isActive ())
1575  {
1576  d->m_blinkCursorTimer->stop ();
1577 
1578  if (d->m_cursorBlinking)
1579  blinkCursorEvent ();
1580  }
1581 }
1582 
1583 // Reset width of console buffer and terminal window to be the same.
1584 
1586 {
1587  d->updateConsoleSize (true, true);
1588 }
1589 
1590 //////////////////////////////////////////////////////////////////////////////
1591 
1592 void QWinTerminalImpl::wheelEvent (QWheelEvent* event)
1593 {
1594  if (! d->m_inWheelEvent)
1595  {
1596  // Forward to the scrollbar (avoid recursion)
1597  d->m_inWheelEvent = true;
1598  QApplication::sendEvent (d->m_verticalScrollBar, event);
1599  d->m_inWheelEvent = false;
1600  }
1601 }
1602 
1603 //////////////////////////////////////////////////////////////////////////////
1604 
1606 {
1607  d->setHorizontalScrollValue (value);
1608 }
1609 
1611 {
1612  d->setVerticalScrollValue (value);
1613 }
1614 
1615 //////////////////////////////////////////////////////////////////////////////
1616 
1618 {
1619  d->monitorConsole ();
1620 }
1621 
1623 {
1624  d->updateSelection ();
1625 }
1626 
1627 //////////////////////////////////////////////////////////////////////////////
1628 
1629 void QWinTerminalImpl::focusInEvent (QFocusEvent* event)
1630 {
1631  emit set_global_shortcuts_signal (false); // disable some shortcuts
1632 
1633  setBlinkingCursorState (true);
1634 
1635  QWidget::focusInEvent (event);
1636 }
1637 
1638 void QWinTerminalImpl::focusOutEvent (QFocusEvent* event)
1639 {
1640  emit set_global_shortcuts_signal (true); // re-enable shortcuts
1641 
1642  // Force the cursor to be redrawn.
1643  d->m_cursorBlinking = true;
1644 
1645  setBlinkingCursorState (false);
1646 
1647  QWidget::focusOutEvent (event);
1648 }
1649 
1650 bool QWinTerminalImpl::eventFilter (QObject *obj, QEvent * event)
1651 {
1652  // if a keypress, filter out tab keys so that the next/prev tabbing is
1653  // disabled - but we still need to pass along to the console .
1654  if (event->type () == QEvent::KeyPress)
1655  {
1656  QKeyEvent* k = static_cast<QKeyEvent*>(event);
1657  if (k->key () == Qt::Key_Tab)
1658  {
1659  sendText ("\t");
1660  return true;
1661  }
1662  }
1663  return false;
1664 }
1665 
1666 void QWinTerminalImpl::keyPressEvent (QKeyEvent* event)
1667 {
1668  QString s = translateKey (event);
1669  if (!s.isEmpty ())
1670  sendText (s);
1671 
1672  if (d->m_hasBlinkingCursor)
1673  {
1674  d->m_blinkCursorTimer->start (d->BLINK_DELAY);
1675 
1676  if (d->m_cursorBlinking)
1677  blinkCursorEvent ();
1678  }
1679 
1680  QWidget::keyPressEvent (event);
1681 }
1682 
1683 //////////////////////////////////////////////////////////////////////////////
1684 
1686 {
1687  d->startCommand ();
1688 }
1689 
1690 //////////////////////////////////////////////////////////////////////////////
1691 
1692 void QWinTerminalImpl::sendText (const QString& s)
1693 {
1694  d->sendConsoleText (s);
1695 }
1696 
1698 {
1699  switch (type)
1700  {
1701  case UnderlineCursor:
1703  break;
1704 
1705  case BlockCursor:
1707  break;
1708 
1709  case IBeamCursor:
1711  break;
1712  }
1713 
1714  setBlinkingCursor (blinking);
1715 }
1716 
1717 void QWinTerminalImpl::setBackgroundColor (const QColor& color)
1718 {
1719  d->setBackgroundColor (color);
1720 }
1721 
1722 void QWinTerminalImpl::setForegroundColor (const QColor& color)
1723 {
1724  d->setForegroundColor (color);
1725 }
1726 
1727 void QWinTerminalImpl::setSelectionColor (const QColor& color)
1728 {
1729  d->setSelectionColor (color);
1730 }
1731 
1732 void QWinTerminalImpl::setCursorColor (bool useForegroundColor,
1733  const QColor& color)
1734 {
1735  d->setCursorColor (useForegroundColor, color);
1736 }
1737 
1739 {
1740  d->setScrollBufferSize (value);
1741 }
1742 
1743 
1744 //////////////////////////////////////////////////////////////////////////////
1745 
1747 {
1748  d->m_font = f;
1749  d->m_consoleView->setFont (f);
1750  d->updateConsoleSize (true);
1751 }
1752 
1753 //////////////////////////////////////////////////////////////////////////////
1754 
1755 void QWinTerminalImpl::setSize (int columns, int lines)
1756 {
1757  d->log ("emit set_screen_size_signal (%d, %d)\n", columns, lines);
1758 
1759  emit set_screen_size_signal (columns, lines);
1760 }
1761 
1762 //////////////////////////////////////////////////////////////////////////////
1763 
1765 {
1766  if(!hasFocus()) return;
1767 
1768  QClipboard *clipboard = QApplication::clipboard ();
1769 
1770  QString selection = d->getSelection ();
1771 
1772  if (selection.isEmpty ())
1773  {
1774  if (! _extra_interrupt)
1775  terminal_interrupt ();
1776  }
1777  else
1778  {
1779  clipboard->setText (selection);
1780  emit report_status_message (tr ("copied selection to clipboard"));
1781  }
1782 }
1783 
1784 //////////////////////////////////////////////////////////////////////////////
1785 
1787 {
1788  if(!hasFocus()) return;
1789 
1790  QString text = QApplication::clipboard()->text (QClipboard::Clipboard);
1791 
1792  if (! text.isEmpty ())
1793  sendText (text);
1794 }
1795 
1796 //////////////////////////////////////////////////////////////////////////////
1797 
1799 {
1800  if(!hasFocus()) return;
1801 
1802  d->selectAll();
1803 }
1804 
1805 
1806 
1807 //////////////////////////////////////////////////////////////////////////////
1808 
1810 {
1811  QString selection = d->getSelection ();
1812  return selection;
1813 }
1814 
1815 //////////////////////////////////////////////////////////////////////////////
1816 
1817 void QWinTerminalImpl::dragEnterEvent (QDragEnterEvent *event)
1818 {
1819  if (event->mimeData ()->hasUrls ())
1820  {
1821  event->acceptProposedAction();
1822  }
1823 }
1824 
1825 //////////////////////////////////////////////////////////////////////////////
1826 
1827 void QWinTerminalImpl::dropEvent (QDropEvent *event)
1828 {
1829  QString dropText;
1830 
1831  if (event->mimeData ()->hasUrls ())
1832  {
1833  foreach (QUrl url, event->mimeData ()->urls ())
1834  {
1835  if(dropText.length () > 0)
1836  dropText += "\n";
1837  dropText += url.toLocalFile ();
1838  }
1839  sendText (dropText);
1840  }
1841 }
1842 
1843 //////////////////////////////////////////////////////////////////////////////
1844 
1846 {
1847  _extra_interrupt = extra;
1848 }
QScrollBar * m_verticalScrollBar
#define TEXT_CHUNK_SIZE
QConsoleView * m_consoleView
void horizontalScrollValueChanged(int value)
void resizeEvent(QResizeEvent *event)
void setScrollBufferSize(int value)
void setBlinkingCursor(bool blink)
QConsoleThread * m_consoleThread
void updateHorizontalScrollBar(void)
void log(const char *fmt,...)
void focusOutEvent(QFocusEvent *)
friend class QConsoleView
void drawSelection(QPainter &p, int cx1, int cy1, int cx2, int cy2, int cw, int ch)
void setCursorType(CursorType type, bool blinking)
void selectWord(const QPoint &cellPos)
CHAR_INFO * m_tmpBuffer
void tripleClickTimeout(void)
void terminal_interrupt(void)
Definition: QTerminal.h:120
void updateConsoleView(bool grab=true)
QConsolePrivate(QWinTerminalImpl *parent, const QString &cmd=QString())
void set_global_shortcuts_signal(bool)
void titleChanged(const QString &)
void syncConsoleParameters(void)
void updateSelection(void)
QColor backgroundColor(void) const
friend class QConsoleThread
void terminated(void)
QWinTerminalImpl * q
void closeStandardIO(int fd, DWORD stdHandleId, const char *name)
F77_RET_T const double const double double * d
QConsoleColors m_colors
void updateConsoleSize(bool sync=false, bool allow_smaller_width=false)
QWinTerminalImpl * q
void init_terminal_size(void)
void sendConsoleText(const QString &s)
void grabConsoleBuffer(CHAR_INFO *buf=0)
void setCursorColor(bool useForegoundColor, const QColor &color)
void setSelectionColor(const QColor &color)
F77_RET_T const double const double * f
void report_status_message(const QString &)
void set_global_shortcuts(bool focus_out)
Definition: QTerminal.cc:76
static const int BLINK_DELAY
text(const graphics_handle &mh, const graphics_handle &p)
Definition: graphics.h:8464
void verticalScrollValueChanged(int value)
std::complex< double > w(std::complex< double > z, double relerr=0)
QString getSelection(void)
void clearSelection(void)
QConsoleView(QWinTerminalImpl *parent=0)
void setHorizontalScrollValue(int value)
void setForegroundColor(const QColor &color)
QColor foregroundColor(void) const
void mouseReleaseEvent(QMouseEvent *event)
#define INFINITE
Definition: randgamma.c:93
void keyPressEvent(QKeyEvent *)
static QString translateKey(QKeyEvent *ev)
void setScrollBufferSize(int value)
void sendText(const QString &s)
QColor selectionColor(void) const
virtual void start(void)
void paintEvent(QPaintEvent *event)
void mouseTripleClickEvent(QMouseEvent *event)
void focusInEvent(QFocusEvent *)
void set_screen_size_signal(int, int)
QPoint posToCell(const QPoint &pt)
void setSize(int columns, int lines)
QConsolePrivate * d
void setBackgroundColor(const QColor &color)
void setBackgroundColor(const QColor &color)
void has_extra_interrupt(bool)
void setBlinkingCursorState(bool blink)
void viewPaintEvent(QConsoleView *, QPaintEvent *)
QColor cursorColor(void) const
QWinTerminalImpl * q
void dropEvent(QDropEvent *event)
void drawTextBackground(QPainter &p, int cx1, int cy1, int cx2, int cy2, int cw, int ch)
void setTerminalFont(const QFont &font)
void viewResizeEvent(QConsoleView *, QResizeEvent *)
bool eventFilter(QObject *obj, QEvent *ev)
void setForegroundColor(const QColor &color)
QWinTerminalImpl(QWidget *parent=0)
void dragEnterEvent(QDragEnterEvent *event)
void mouseMoveEvent(QMouseEvent *event)
#define LOGFILENAME
void drawText(QPainter &p, int cx1, int cy1, int cx2, int cy2, int cw, int ch)
QConsoleThread(QWinTerminalImpl *console)
void setVerticalScrollValue(int value)
void setSelectionColor(const QColor &color)
void mouseDoubleClickEvent(QMouseEvent *event)
void mousePressEvent(QMouseEvent *event)
static void maybeSwapPoints(QPoint &begin, QPoint &end)
void wheelEvent(QWheelEvent *)
void updateVerticalScrollBar(void)
KeyboardCursorType m_cursorType
void drawCursor(QPainter &p)
void selectLine(const QPoint &cellPos)
void setCursorColor(bool useForegroundColor, const QColor &color)
void setupStandardIO(DWORD stdHandleId, int fd, const char *name, const char *devName)
F77_RET_T const double * x
QScrollBar * m_horizontalScrollBar