view libgui/qterminal/libqterminal/win32/QWinTerminalImpl.cpp @ 19627:446c46af4b42 stable

strip trailing whitespace from most source files * Makefile.am, NEWS, build-aux/common.mk, configure.ac, doc/Makefile.am, doc/doxyhtml/Makefile.am, doc/interpreter/Makefile.am, doc/interpreter/arith.txi, doc/interpreter/audio.txi, doc/interpreter/basics.txi, doc/interpreter/bugs.txi, doc/interpreter/container.txi, doc/interpreter/cp-idx.txi, doc/interpreter/data.txi, doc/interpreter/debug.txi, doc/interpreter/diagperm.txi, doc/interpreter/diffeq.txi, doc/interpreter/doccheck/README, doc/interpreter/doccheck/spellcheck, doc/interpreter/emacs.txi, doc/interpreter/errors.txi, doc/interpreter/eval.txi, doc/interpreter/expr.txi, doc/interpreter/external.txi, doc/interpreter/fn-idx.txi, doc/interpreter/func.txi, doc/interpreter/geometry.txi, doc/interpreter/geometryimages.m, doc/interpreter/gpl.txi, doc/interpreter/grammar.txi, doc/interpreter/gui.txi, doc/interpreter/image.txi, doc/interpreter/install.txi, doc/interpreter/interp.txi, doc/interpreter/interpimages.m, doc/interpreter/intro.txi, doc/interpreter/io.txi, doc/interpreter/java.txi, doc/interpreter/linalg.txi, doc/interpreter/macros.texi, doc/interpreter/matrix.txi, doc/interpreter/munge-texi.pl, doc/interpreter/nonlin.txi, doc/interpreter/numbers.txi, doc/interpreter/obsolete.txi, doc/interpreter/octave-config.1, doc/interpreter/octave.texi, doc/interpreter/oop.txi, doc/interpreter/op-idx.txi, doc/interpreter/optim.txi, doc/interpreter/package.txi, doc/interpreter/plot.txi, doc/interpreter/poly.txi, doc/interpreter/preface.txi, doc/interpreter/quad.txi, doc/interpreter/set.txi, doc/interpreter/signal.txi, doc/interpreter/sparse.txi, doc/interpreter/sparseimages.m, doc/interpreter/splineimages.m, doc/interpreter/stats.txi, doc/interpreter/stmt.txi, doc/interpreter/strings.txi, doc/interpreter/system.txi, doc/interpreter/testfun.txi, doc/interpreter/tips.txi, doc/interpreter/var.txi, doc/interpreter/vectorize.txi, doc/liboctave/Makefile.am, doc/liboctave/array.texi, doc/liboctave/bugs.texi, doc/liboctave/cp-idx.texi, doc/liboctave/dae.texi, doc/liboctave/diffeq.texi, doc/liboctave/error.texi, doc/liboctave/factor.texi, doc/liboctave/fn-idx.texi, doc/liboctave/gpl.texi, doc/liboctave/install.texi, doc/liboctave/intro.texi, doc/liboctave/liboctave.texi, doc/liboctave/matvec.texi, doc/liboctave/nleqn.texi, doc/liboctave/nlfunc.texi, doc/liboctave/ode.texi, doc/liboctave/optim.texi, doc/liboctave/preface.texi, doc/liboctave/quad.texi, doc/liboctave/range.texi, doc/refcard/Makefile.am, doc/refcard/refcard.tex, etc/HACKING, etc/NEWS.1, etc/NEWS.2, etc/NEWS.3, etc/OLD-ChangeLogs/ChangeLog, etc/OLD-ChangeLogs/doc-ChangeLog, etc/OLD-ChangeLogs/scripts-ChangeLog, etc/OLD-ChangeLogs/src-ChangeLog, etc/OLD-ChangeLogs/test-ChangeLog, etc/PROJECTS, etc/README.Cygwin, etc/README.MacOS, etc/README.MinGW, etc/README.gnuplot, etc/gdbinit, etc/icons/Makefile.am, examples/@polynomial/end.m, examples/@polynomial/subsasgn.m, examples/Makefile.am, examples/standalonebuiltin.cc, libgui/Makefile.am, libgui/qterminal/libqterminal/README, libgui/qterminal/libqterminal/unix/BlockArray.cpp, libgui/qterminal/libqterminal/unix/BlockArray.h, libgui/qterminal/libqterminal/unix/Character.h, libgui/qterminal/libqterminal/unix/CharacterColor.h, libgui/qterminal/libqterminal/unix/Emulation.cpp, libgui/qterminal/libqterminal/unix/Emulation.h, libgui/qterminal/libqterminal/unix/Filter.cpp, libgui/qterminal/libqterminal/unix/Filter.h, libgui/qterminal/libqterminal/unix/History.cpp, libgui/qterminal/libqterminal/unix/History.h, libgui/qterminal/libqterminal/unix/KeyboardTranslator.cpp, libgui/qterminal/libqterminal/unix/KeyboardTranslator.h, libgui/qterminal/libqterminal/unix/LineFont.h, libgui/qterminal/libqterminal/unix/QUnixTerminalImpl.cpp, libgui/qterminal/libqterminal/unix/QUnixTerminalImpl.h, libgui/qterminal/libqterminal/unix/Screen.cpp, libgui/qterminal/libqterminal/unix/Screen.h, libgui/qterminal/libqterminal/unix/ScreenWindow.cpp, libgui/qterminal/libqterminal/unix/ScreenWindow.h, libgui/qterminal/libqterminal/unix/TerminalCharacterDecoder.cpp, libgui/qterminal/libqterminal/unix/TerminalCharacterDecoder.h, libgui/qterminal/libqterminal/unix/Vt102Emulation.h, libgui/qterminal/libqterminal/win32/QWinTerminalImpl.cpp, libgui/qterminal/qterminal/main.cpp, libgui/src/m-editor/file-editor-tab.cc, libgui/src/octave-gui.cc, libgui/src/octave-qt-link.cc, libinterp/corefcn/data.cc, libinterp/corefcn/defun-int.h, libinterp/corefcn/det.cc, libinterp/corefcn/gl2ps-renderer.cc, libinterp/corefcn/graphics.cc, libinterp/corefcn/graphics.in.h, libinterp/corefcn/ls-mat5.cc, libinterp/corefcn/lu.cc, libinterp/corefcn/oct-tex-parser.yy, libinterp/corefcn/oct-tex-symbols.in, libinterp/corefcn/quadcc.cc, libinterp/corefcn/zfstream.cc, libinterp/dldfcn/__eigs__.cc, libinterp/dldfcn/__voronoi__.cc, libinterp/gendoc.pl, libinterp/genprops.awk, libinterp/mk-errno-list, libinterp/mk-pkg-add, libinterp/mkbuiltins, libinterp/mkdefs, libinterp/mkdocs, libinterp/mkops, libinterp/octave-value/ov-java.cc, libinterp/parse-tree/lex.ll, libinterp/parse-tree/oct-parse.in.yy, libinterp/parse-tree/octave.gperf, liboctave/Makefile.am, liboctave/array/Array.cc, liboctave/array/module.mk, liboctave/cruft/daspk/datv.f, liboctave/cruft/daspk/dcnst0.f, liboctave/cruft/daspk/dcnstr.f, liboctave/cruft/daspk/ddasic.f, liboctave/cruft/daspk/ddasid.f, liboctave/cruft/daspk/ddasik.f, liboctave/cruft/daspk/ddaspk.f, liboctave/cruft/daspk/ddstp.f, liboctave/cruft/daspk/ddwnrm.f, liboctave/cruft/daspk/dfnrmd.f, liboctave/cruft/daspk/dfnrmk.f, liboctave/cruft/daspk/dhels.f, liboctave/cruft/daspk/dheqr.f, liboctave/cruft/daspk/dinvwt.f, liboctave/cruft/daspk/dlinsd.f, liboctave/cruft/daspk/dlinsk.f, liboctave/cruft/daspk/dmatd.f, liboctave/cruft/daspk/dnedd.f, liboctave/cruft/daspk/dnedk.f, liboctave/cruft/daspk/dnsd.f, liboctave/cruft/daspk/dnsid.f, liboctave/cruft/daspk/dnsik.f, liboctave/cruft/daspk/dnsk.f, liboctave/cruft/daspk/dorth.f, liboctave/cruft/daspk/dslvd.f, liboctave/cruft/daspk/dslvk.f, liboctave/cruft/daspk/dspigm.f, liboctave/cruft/daspk/dyypnw.f, liboctave/cruft/dasrt/ddasrt.f, liboctave/cruft/dasrt/drchek.f, liboctave/cruft/dassl/ddaslv.f, liboctave/cruft/dassl/ddassl.f, liboctave/cruft/misc/blaswrap.c, liboctave/cruft/misc/module.mk, liboctave/cruft/odepack/cfode.f, liboctave/cruft/odepack/dlsode.f, liboctave/cruft/odepack/ewset.f, liboctave/cruft/odepack/intdy.f, liboctave/cruft/odepack/prepj.f, liboctave/cruft/odepack/sintdy.f, liboctave/cruft/odepack/slsode.f, liboctave/cruft/odepack/solsy.f, liboctave/cruft/odepack/ssolsy.f, liboctave/cruft/odepack/stode.f, liboctave/cruft/odepack/vnorm.f, liboctave/cruft/ranlib/Basegen.doc, liboctave/cruft/ranlib/README, liboctave/cruft/ranlib/genbet.f, liboctave/cruft/ranlib/genexp.f, liboctave/cruft/ranlib/gennch.f, liboctave/cruft/ranlib/gennf.f, liboctave/cruft/ranlib/gennor.f, liboctave/cruft/ranlib/getsd.f, liboctave/cruft/ranlib/initgn.f, liboctave/cruft/ranlib/phrtsd.f, liboctave/cruft/ranlib/randlib.fdoc, liboctave/cruft/ranlib/setsd.f, liboctave/cruft/ranlib/tstgmn.for, liboctave/cruft/ranlib/tstmid.for, liboctave/cruft/slatec-fn/atanh.f, liboctave/cruft/slatec-fn/datanh.f, liboctave/cruft/slatec-fn/xgmainc.f, liboctave/cruft/slatec-fn/xsgmainc.f, liboctave/numeric/module.mk, liboctave/operators/mk-ops.awk, liboctave/operators/mx-ops, liboctave/operators/sparse-mk-ops.awk, liboctave/operators/sparse-mx-ops, liboctave/operators/vx-ops, liboctave/util/module.mk, run-octave.in, scripts/@ftp/ftp.m, scripts/audio/wavread.m, scripts/deprecated/java_convert_matrix.m, scripts/deprecated/java_debug.m, scripts/deprecated/java_invoke.m, scripts/deprecated/java_new.m, scripts/deprecated/java_unsigned_conversion.m, scripts/deprecated/javafields.m, scripts/deprecated/javamethods.m, scripts/deprecated/shell_cmd.m, scripts/general/accumarray.m, scripts/general/display.m, scripts/general/fieldnames.m, scripts/general/interp1.m, scripts/general/interp2.m, scripts/general/interp3.m, scripts/general/isa.m, scripts/general/methods.m, scripts/general/sortrows.m, scripts/geometry/convhull.m, scripts/geometry/delaunay.m, scripts/geometry/delaunay3.m, scripts/geometry/delaunayn.m, scripts/geometry/griddata.m, scripts/geometry/griddatan.m, scripts/geometry/voronoi.m, scripts/geometry/voronoin.m, scripts/gui/guihandles.m, scripts/gui/inputdlg.m, scripts/gui/listdlg.m, scripts/gui/msgbox.m, scripts/gui/questdlg.m, scripts/gui/uigetfile.m, scripts/gui/waitbar.m, scripts/gui/warndlg.m, scripts/help/doc.m, scripts/help/help.m, scripts/help/type.m, scripts/image/bone.m, scripts/image/cmpermute.m, scripts/image/cmunique.m, scripts/image/colorcube.m, scripts/image/colormap.m, scripts/image/contrast.m, scripts/image/gray2ind.m, scripts/image/image.m, scripts/image/imshow.m, scripts/image/ind2gray.m, scripts/image/jet.m, scripts/image/rgb2ntsc.m, scripts/image/spinmap.m, scripts/io/importdata.m, scripts/io/strread.m, scripts/io/textread.m, scripts/io/textscan.m, scripts/java/java_get.m, scripts/java/java_set.m, scripts/java/javaaddpath.m, scripts/java/javaclasspath.m, scripts/java/javamem.m, scripts/linear-algebra/linsolve.m, scripts/linear-algebra/qzhess.m, scripts/miscellaneous/debug.m, scripts/miscellaneous/desktop.m, scripts/miscellaneous/dir.m, scripts/miscellaneous/dos.m, scripts/miscellaneous/edit.m, scripts/miscellaneous/fact.m, scripts/miscellaneous/getappdata.m, scripts/miscellaneous/inputname.m, scripts/miscellaneous/license.m, scripts/miscellaneous/ls_command.m, scripts/miscellaneous/run.m, scripts/miscellaneous/setfield.m, scripts/miscellaneous/unix.m, scripts/miscellaneous/ver.m, scripts/mk-pkg-add, scripts/mkdoc.pl, scripts/optimization/fminsearch.m, scripts/optimization/optimset.m, scripts/optimization/sqp.m, scripts/pkg/pkg.m, scripts/pkg/private/create_pkgadddel.m, scripts/pkg/private/fix_depends.m, scripts/pkg/private/install.m, scripts/plot/appearance/axis.m, scripts/plot/appearance/box.m, scripts/plot/appearance/clabel.m, scripts/plot/appearance/daspect.m, scripts/plot/appearance/datetick.m, scripts/plot/appearance/grid.m, scripts/plot/appearance/legend.m, scripts/plot/appearance/orient.m, scripts/plot/appearance/shading.m, scripts/plot/appearance/text.m, scripts/plot/appearance/title.m, scripts/plot/appearance/xlabel.m, scripts/plot/appearance/ylabel.m, scripts/plot/appearance/zlabel.m, scripts/plot/draw/area.m, scripts/plot/draw/bar.m, scripts/plot/draw/barh.m, scripts/plot/draw/colorbar.m, scripts/plot/draw/contour.m, scripts/plot/draw/contour3.m, scripts/plot/draw/contourf.m, scripts/plot/draw/ellipsoid.m, scripts/plot/draw/errorbar.m, scripts/plot/draw/ezcontour.m, scripts/plot/draw/ezcontourf.m, scripts/plot/draw/ezmesh.m, scripts/plot/draw/ezpolar.m, scripts/plot/draw/fill.m, scripts/plot/draw/fplot.m, scripts/plot/draw/hist.m, scripts/plot/draw/meshc.m, scripts/plot/draw/meshz.m, scripts/plot/draw/pareto.m, scripts/plot/draw/patch.m, scripts/plot/draw/peaks.m, scripts/plot/draw/pie.m, scripts/plot/draw/pie3.m, scripts/plot/draw/plot.m, scripts/plot/draw/plotyy.m, scripts/plot/draw/private/__bar__.m, scripts/plot/draw/private/__contour__.m, scripts/plot/draw/private/__errplot__.m, scripts/plot/draw/private/__ezplot__.m, scripts/plot/draw/private/__patch__.m, scripts/plot/draw/private/__stem__.m, scripts/plot/draw/rectangle.m, scripts/plot/draw/ribbon.m, scripts/plot/draw/rose.m, scripts/plot/draw/scatter.m, scripts/plot/draw/scatter3.m, scripts/plot/draw/semilogx.m, scripts/plot/draw/shrinkfaces.m, scripts/plot/draw/sombrero.m, scripts/plot/draw/sphere.m, scripts/plot/draw/stairs.m, scripts/plot/draw/stem.m, scripts/plot/draw/stemleaf.m, scripts/plot/draw/surf.m, scripts/plot/draw/surface.m, scripts/plot/draw/surfc.m, scripts/plot/draw/surfl.m, scripts/plot/draw/surfnorm.m, scripts/plot/draw/tetramesh.m, scripts/plot/draw/trimesh.m, scripts/plot/draw/triplot.m, scripts/plot/draw/trisurf.m, scripts/plot/util/__gnuplot_drawnow__.m, scripts/plot/util/__plt_get_axis_arg__.m, scripts/plot/util/axes.m, scripts/plot/util/clf.m, scripts/plot/util/copyobj.m, scripts/plot/util/figure.m, scripts/plot/util/gcbo.m, scripts/plot/util/graphics_toolkit.m, scripts/plot/util/hggroup.m, scripts/plot/util/meshgrid.m, scripts/plot/util/newplot.m, scripts/plot/util/print.m, scripts/plot/util/private/__add_default_menu__.m, scripts/plot/util/private/__fltk_print__.m, scripts/plot/util/private/__gnuplot_print__.m, scripts/plot/util/private/__print_parse_opts__.m, scripts/plot/util/refreshdata.m, scripts/plot/util/subplot.m, scripts/polynomial/conv.m, scripts/polynomial/poly.m, scripts/polynomial/polyeig.m, scripts/polynomial/polyfit.m, scripts/polynomial/polyval.m, scripts/polynomial/private/__splinefit__.m, scripts/polynomial/spline.m, scripts/prefs/prefdir.m, scripts/prefs/preferences.m, scripts/prefs/private/prefsfile.m, scripts/prefs/rmpref.m, scripts/signal/freqz.m, scripts/signal/module.mk, scripts/sparse/eigs.m, scripts/sparse/pcg.m, scripts/sparse/private/__sprand_impl__.m, scripts/sparse/sprand.m, scripts/sparse/sprandn.m, scripts/sparse/spy.m, scripts/sparse/svds.m, scripts/specfun/expint.m, scripts/specfun/factor.m, scripts/special-matrix/gallery.m, scripts/special-matrix/hankel.m, scripts/special-matrix/toeplitz.m, scripts/startup/inputrc, scripts/statistics/base/kurtosis.m, scripts/statistics/base/moment.m, scripts/statistics/base/qqplot.m, scripts/statistics/base/var.m, scripts/statistics/distributions/betarnd.m, scripts/statistics/distributions/binoinv.m, scripts/statistics/distributions/binopdf.m, scripts/statistics/distributions/binornd.m, scripts/statistics/distributions/cauchy_rnd.m, scripts/statistics/distributions/chi2rnd.m, scripts/statistics/distributions/discrete_pdf.m, scripts/statistics/distributions/discrete_rnd.m, scripts/statistics/distributions/empirical_rnd.m, scripts/statistics/distributions/exprnd.m, scripts/statistics/distributions/frnd.m, scripts/statistics/distributions/gamrnd.m, scripts/statistics/distributions/geornd.m, scripts/statistics/distributions/hygernd.m, scripts/statistics/distributions/kolmogorov_smirnov_cdf.m, scripts/statistics/distributions/laplace_cdf.m, scripts/statistics/distributions/laplace_pdf.m, scripts/statistics/distributions/logistic_cdf.m, scripts/statistics/distributions/logistic_pdf.m, scripts/statistics/distributions/lognrnd.m, scripts/statistics/distributions/nbincdf.m, scripts/statistics/distributions/nbininv.m, scripts/statistics/distributions/nbinpdf.m, scripts/statistics/distributions/nbinrnd.m, scripts/statistics/distributions/normrnd.m, scripts/statistics/distributions/poissinv.m, scripts/statistics/distributions/poissrnd.m, scripts/statistics/distributions/tinv.m, scripts/statistics/distributions/trnd.m, scripts/statistics/distributions/unidcdf.m, scripts/statistics/distributions/unidpdf.m, scripts/statistics/distributions/unidrnd.m, scripts/statistics/distributions/unifrnd.m, scripts/statistics/distributions/wblrnd.m, scripts/statistics/models/module.mk, scripts/statistics/tests/kruskal_wallis_test.m, scripts/strings/base2dec.m, scripts/strings/deblank.m, scripts/strings/dec2base.m, scripts/strings/dec2bin.m, scripts/strings/dec2hex.m, scripts/strings/mat2str.m, scripts/strings/ostrsplit.m, scripts/strings/regexptranslate.m, scripts/strings/str2num.m, scripts/strings/strcat.m, scripts/strings/strjoin.m, scripts/strings/strsplit.m, scripts/strings/strtok.m, scripts/strings/strtrim.m, scripts/strings/strtrunc.m, scripts/strings/substr.m, scripts/testfun/__run_test_suite__.m, scripts/testfun/speed.m, scripts/testfun/test.m, scripts/time/asctime.m, scripts/time/datenum.m, scripts/time/datevec.m, scripts/time/weekday.m, src/Makefile.am, test/Makefile.am, test/build-bc-overload-tests.sh, test/build-sparse-tests.sh, test/jit.tst, test/line-continue.tst: Strip trailing whitespace.
author John W. Eaton <jwe@octave.org>
date Tue, 20 Jan 2015 08:26:57 -0500
parents 6535cb2b8e23
children fe689210525c
line wrap: on
line source

/*

Copyright (C) 2011, 2013 Michael Goffioul.

This file is part of QConsole.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not,
see <http://www.gnu.org/licenses/>.

*/

#include <QApplication>
#include <QClipboard>
#include <QColor>
#include <QFont>
#include <QHBoxLayout>
#include <QPaintEvent>
#include <QPainter>
#include <QResizeEvent>
#include <QScrollBar>
#include <QtDebug>
#include <QThread>
#include <QTimer>
#include <QToolTip>
#include <QCursor>
#include <QMessageBox>

#include <fcntl.h>
#include <io.h>
#include <stdio.h>
#include <stdarg.h>
#define WIN32_LEAN_AND_MEAN
#if ! defined (_WIN32_WINNT) && ! defined (NTDDI_VERSION)
#define _WIN32_WINNT 0x0500
#endif
#include <windows.h>
#include <cstring>
#include <csignal>
#include <limits>

#include "QWinTerminalImpl.h"
#include "QTerminalColors.h"

// Uncomment to log activity to LOGFILENAME
// #define DEBUG_QCONSOLE
#define LOGFILENAME "QConsole.log"
// Uncomment to create hidden console window
#define HIDDEN_CONSOLE

#ifdef _MSC_VER
# pragma warning(disable : 4996)
#endif

//////////////////////////////////////////////////////////////////////////////

class QConsoleView : public QWidget
{
public:
  QConsoleView (QWinTerminalImpl* parent = 0) : QWidget (parent), q (parent) { }
  ~QConsoleView (void) { }

protected:
  void paintEvent (QPaintEvent* event) { q->viewPaintEvent (this, event); }
  void resizeEvent (QResizeEvent* event) { q->viewResizeEvent (this, event); }

private:
  QWinTerminalImpl* q;
};

//////////////////////////////////////////////////////////////////////////////

class QConsoleThread : public QThread
{
public:
  QConsoleThread (QWinTerminalImpl* console) : QThread (console), q (console) { }

protected:
  void run (void)
    { q->start (); }

private:
  QWinTerminalImpl* q;
};

//////////////////////////////////////////////////////////////////////////////

static QString translateKey (QKeyEvent *ev)
{
  QString esc = "\x1b";
  QString s;

  if (ev->key () == Qt::Key_Delete)
    s = esc + "[C\b";
  else if (!ev->text ().isEmpty ())
    s = ev->text ();
  else
    {

      switch (ev->key ())
        {
        case Qt::Key_Up:
          s = esc + "[A";
          break;

        case Qt::Key_Down:
          s = esc + "[B";
          break;

        case Qt::Key_Right:
          s = esc + "[C";
          break;

        case Qt::Key_Left:
          s = esc + "[D";
          break;

        case Qt::Key_Home:
          s = esc + "[H";
          break;

        case Qt::Key_End:
          s = esc + "[F";
          break;

        case Qt::Key_Insert:
          s = esc + "[2~";
          break;

        case Qt::Key_PageUp:
          s = esc + "[5~";
          break;

        case Qt::Key_PageDown:
          s = esc + "[6~";
          break;

        case Qt::Key_Escape:
          s = esc;
          break;

        default:
          break;
        }
    }

  return s;
}

class QConsolePrivate
{
  friend class QWinTerminalImpl;

public:

  enum KeyboardCursorType
    {
      BlockCursor,
      UnderlineCursor,
      IBeamCursor
    };

  QConsolePrivate (QWinTerminalImpl* parent, const QString& cmd = QString ());
  ~QConsolePrivate (void);

  void updateConsoleSize (bool sync = false);
  void syncConsoleParameters (void);
  void grabConsoleBuffer (CHAR_INFO* buf = 0);
  void updateScrollBar (void);
  void setScrollValue (int value);
  void updateConsoleView (bool grab = true);
  void monitorConsole (void);
  void startCommand (void);
  void sendConsoleText (const QString& s);
  QRect cursorRect (void);

  void log (const char* fmt, ...);

  void closeStandardIO (int fd, DWORD stdHandleId, const char* name);
  void setupStandardIO (DWORD stdHandleId, int fd, const char* name,
                        const char* devName);

  QPoint posToCell (const QPoint& pt);
  QString getSelection (void);
  void updateSelection (void);
  void clearSelection (void);

  QColor backgroundColor (void) const;
  QColor foregroundColor (void) const;
  QColor selectionColor (void) const;
  QColor cursorColor (void) const;

  void setBackgroundColor (const QColor& color);
  void setForegroundColor (const QColor& color);
  void setSelectionColor (const QColor& color);
  void setCursorColor (bool useForegroundColor, const QColor& color);

  void drawTextBackground (QPainter& p, int cx1, int cy1, int cx2, int cy2,
                           int cw, int ch);

  void drawSelection (QPainter& p, int cx1, int cy1, int cx2, int cy2,
                      int cw, int ch);

  void drawCursor (QPainter& p);

  void drawText (QPainter& p, int cx1, int cy1, int cx2, int cy2,
                 int cw, int ch);

private:
  QWinTerminalImpl* q;

private:
  QFont m_font;
  QString m_command;
  QConsoleColors m_colors;
  bool m_inWheelEvent;
  QString m_title;

  QSize m_charSize;
  QSize m_bufferSize;
  QRect m_consoleRect;
  QPoint m_cursorPos;
  bool m_cursorBlinking;
  bool m_hasBlinkingCursor;
  QTimer *m_blinkCursorTimer;
  KeyboardCursorType m_cursorType;

  QPoint m_beginSelection;
  QPoint m_endSelection;
  bool m_settingSelection;

  QColor m_selectionColor;
  QColor m_cursorColor;

  HANDLE m_stdOut;
  HWND m_consoleWindow;
  CHAR_INFO* m_buffer;
  CHAR_INFO* m_tmpBuffer;
  HANDLE m_process;

  QConsoleView* m_consoleView;
  QScrollBar* m_scrollBar;
  QTimer* m_consoleWatcher;
  QConsoleThread *m_consoleThread;

  // The delay in milliseconds between redrawing blinking text.
  static const int BLINK_DELAY = 500;
};

static void maybeSwapPoints (QPoint& begin, QPoint& end)
{
  if (end.y () < begin.y ()
      || (end.y () == begin.y () && end.x () < begin.x ()))
    qSwap (begin, end);
}

//////////////////////////////////////////////////////////////////////////////

QConsolePrivate::QConsolePrivate (QWinTerminalImpl* parent, const QString& cmd)
  : q (parent), m_command (cmd), m_cursorBlinking (false),
    m_hasBlinkingCursor (true), m_cursorType (BlockCursor),
    m_beginSelection (0, 0), m_endSelection (0, 0), m_settingSelection (false),
    m_process (NULL), m_inWheelEvent (false)
{
  log (NULL);

  // Possibly detach from any existing console
  log ("Detaching from existing console (if any)...\n");
  FreeConsole ();
  log ("Closing standard IO...\n");
  closeStandardIO (0, STD_INPUT_HANDLE, "STDIN");
  closeStandardIO (1, STD_OUTPUT_HANDLE, "STDOUT");
  closeStandardIO (2, STD_ERROR_HANDLE, "STDERR");

#ifdef HIDDEN_CONSOLE
  HWINSTA hOrigSta, hNewSta;

  // Create new (hidden) console
  hOrigSta = GetProcessWindowStation ();
  hNewSta = CreateWindowStation (NULL, 0, GENERIC_ALL, NULL);
  log ("Current Windows station: %p.\nNew Windows station: %p.\n", hOrigSta,
       hNewSta);
  if (! SetProcessWindowStation (hNewSta))
    log ("Failed to switch to new Windows station.\n");
#endif
  if (! AllocConsole ())
    log ("Failed to create new console.\n");
#ifdef HIDDEN_CONSOLE
  if (! SetProcessWindowStation (hOrigSta))
    log ("Failed to restore original Windows station.\n");
  if (! CloseWindowStation (hNewSta))
    log ("Failed to close new Windows station.\n");
#endif

  log ("New (hidden) console created.\n");

  setupStandardIO (STD_INPUT_HANDLE,  0, "STDIN",  "CONIN$");
  setupStandardIO (STD_OUTPUT_HANDLE, 1, "STDOUT", "CONOUT$");
  setupStandardIO (STD_ERROR_HANDLE,  2, "STDERR", "CONOUT$");

  log ("Standard input/output/error set up.\n");

  *stdin = *(fdopen (0, "rb"));
  *stdout = *(fdopen (1, "wb"));
  *stderr = *(fdopen (2, "wb"));

  log ("POSIX standard streams created.\n");

  setvbuf (stdin, NULL, _IONBF, 0);
  setvbuf (stdout, NULL, _IONBF, 0);
  setvbuf (stderr, NULL, _IONBF, 0);

  log ("POSIX standard stream buffers adjusted.\n");

  HANDLE hStdOut = GetStdHandle (STD_OUTPUT_HANDLE);

  log ("Console allocated: hStdOut: %p\n", hStdOut);

  m_stdOut = hStdOut;
  m_consoleWindow = GetConsoleWindow ();

  // In case the console window hasn't been created hidden...
#ifdef HIDDEN_CONSOLE
  ShowWindow (m_consoleWindow, SW_HIDE);
#endif

  CONSOLE_SCREEN_BUFFER_INFO sbi;

  GetConsoleScreenBufferInfo (hStdOut, &sbi);
  m_bufferSize = QSize (sbi.dwSize.X, qMax (sbi.dwSize.Y, (SHORT)500));
  m_consoleRect = QRect (sbi.srWindow.Left, sbi.srWindow.Top,
                         sbi.srWindow.Right - sbi.srWindow.Left + 1,
                         sbi.srWindow.Bottom - sbi.srWindow.Top + 1);
  m_cursorPos = QPoint (sbi.dwCursorPosition.X, sbi.dwCursorPosition.Y);

  log ("Initial console parameters:\n");
  log ("  buffer size: %d x %d\n", m_bufferSize.width (),
       m_bufferSize.height ());
  log ("  window: (%d, %d) -> (%d, %d) [%d x %d]\n",
       m_consoleRect.left (), m_consoleRect.top (),
       m_consoleRect.right (), m_consoleRect.bottom (),
       m_consoleRect.width (), m_consoleRect.height ());

  wchar_t titleBuf[260];
  GetConsoleTitleW (titleBuf, sizeof (titleBuf));
  q->setWindowTitle (QString::fromWCharArray (titleBuf));

  m_font.setFamily ("Lucida Console");
  m_font.setPointSize (9);
  m_font.setStyleHint (QFont::TypeWriter);

  m_buffer = m_tmpBuffer = 0;

  m_consoleView = new QConsoleView (parent);
  m_scrollBar = new QScrollBar (Qt::Vertical, parent);

  QHBoxLayout* l = new QHBoxLayout (parent);
  l->setContentsMargins (0, 0, 0, 0);
  l->setSpacing (0);
  l->addWidget (m_consoleView, 1);
  l->addWidget (m_scrollBar, 0);

  // Choose 15 (0xF) as index into the Windows console color map for the
  // background and 0 (0x0) as the index for the foreground.  This
  // selection corresponds to the indices used in the foregroundColor,
  // setForegroundColor, backgroundColor, and SetBackgroundColor
  // functions.

  SetConsoleTextAttribute (m_stdOut, 0xF0);

  // Defaults.
  setBackgroundColor (Qt::white);
  setForegroundColor (Qt::black);
  setSelectionColor (Qt::lightGray);
  setCursorColor (false, Qt::darkGray);

  // FIXME -- should we set the palette?
  QPalette palette (backgroundColor ());
  m_consoleView->setPalette (palette);

  m_consoleView->setAutoFillBackground (true);

  m_consoleView->setFont (m_font);
  parent->setFocusPolicy (Qt::StrongFocus);
  parent->winId ();

  updateScrollBar ();

  m_consoleWatcher = new QTimer (parent);
  m_consoleWatcher->setInterval (10);
  m_consoleWatcher->setSingleShot (false);

  m_blinkCursorTimer = new QTimer (parent);
  QObject::connect (m_blinkCursorTimer, SIGNAL (timeout()),
                    q, SLOT (blinkCursorEvent ()));

  QObject::connect (m_scrollBar, SIGNAL (valueChanged (int)),
                    q, SLOT (scrollValueChanged (int)));
  QObject::connect (m_consoleWatcher, SIGNAL (timeout (void)),
                    q, SLOT (monitorConsole (void)));

  m_consoleWatcher->start ();

  if (m_command.isEmpty ())
    m_consoleThread = 0;
  else
    {
      m_consoleThread = new QConsoleThread (q);
      QObject::connect (m_consoleThread, SIGNAL (finished (void)),
                        q, SIGNAL (terminated (void)));
      m_consoleThread->start ();
    }
}

//////////////////////////////////////////////////////////////////////////////

QConsolePrivate::~QConsolePrivate (void)
{
  if (m_consoleThread && m_consoleThread->isRunning () && m_process)
    {
      TerminateProcess (m_process, (UINT)-1);
      m_consoleThread->wait ();
    }
  if (m_buffer)
    delete [] m_buffer;
  if (m_tmpBuffer)
    delete [] m_tmpBuffer;
}

//////////////////////////////////////////////////////////////////////////////

void QConsolePrivate::setupStandardIO (DWORD stdHandleId, int targetFd,
                                       const char* name, const char* devName)
{
  log ("Opening %s...\n", devName);

  int fd = open (devName, _O_RDWR | _O_BINARY);

  if (fd != -1)
    {
      if (fd != targetFd)
        {
          log ("Opened %s is not at target file descriptor %d, "
               "duplicating...\n", name, targetFd);
          if (dup2 (fd, targetFd) == -1)
            log ("Failed to duplicate file descriptor: errno=%d.\n", errno);
          if (close (fd) == -1)
            log ("Failed to close original file descriptor: errno=%d.\n",
                 errno);
        }
      else
        log ("%s opened and assigned to file descriptor %d.\n", devName, fd);
      if (! SetStdHandle (stdHandleId, (HANDLE) _get_osfhandle (targetFd)))
        log ("Failed to re-assign %s: error=%08x.\n", name, GetLastError ());
    }
  else
    log ("Failed to open %s: errno=%d.\n", devName, errno);
}

QPoint QConsolePrivate::posToCell (const QPoint& p)
{
  return QPoint (m_consoleRect.left () + p.x () / m_charSize.width (),
                 m_consoleRect.top () + p.y () / m_charSize.height ());
}

QString QConsolePrivate::getSelection (void)
{
  QString selection;

  QPoint begin = m_beginSelection;
  QPoint end = m_endSelection;

  maybeSwapPoints (begin, end);

  if (begin != end)
    {
      CHAR_INFO* buf;
      COORD bufSize, bufCoord;
      SMALL_RECT bufRect;
      int nr;

      nr = end.y () - begin.y () + 1;
      buf =  new CHAR_INFO[m_bufferSize.width () * nr];
      bufSize.X = m_bufferSize.width ();
      bufSize.Y = nr;
      bufCoord.X = 0;
      bufCoord.Y = 0;

      bufRect.Left = 0;
      bufRect.Right = m_bufferSize.width ();
      bufRect.Top = begin.y ();
      bufRect.Bottom = end.y ();

      if (ReadConsoleOutput (m_stdOut, buf, bufSize, bufCoord, &bufRect))
        {
          int start_pos = begin.x ();
          int end_pos = (nr - 1) * m_bufferSize.width () + end.x ();
          int lastNonSpace = -1;

          for (int i = start_pos; i <= end_pos; i++)
            {
              if (i && (i % m_bufferSize.width ()) == 0)
                {
                  if (lastNonSpace >= 0)
                    selection.truncate (lastNonSpace);
                  selection.append ('\n');
                  lastNonSpace = selection.length ();
                }

              QChar c (buf[i].Char.UnicodeChar);

              selection.append (c);
              if (! c.isSpace ())
                lastNonSpace = selection.length ();
            }

          if (lastNonSpace >= 0)
            selection.truncate (lastNonSpace);
        }
    }

  return selection;
}

void QConsolePrivate::updateSelection (void)
{
  QPoint begin = m_beginSelection;
  QPoint end = m_endSelection;

  maybeSwapPoints (begin, end);

  begin.rx () = 0;
  end.rx () = m_consoleRect.width ();

  m_consoleView->update ();
}

void QConsolePrivate::clearSelection (void)
{
  m_beginSelection = m_endSelection = QPoint ();

  m_consoleView->update ();
}

QColor QConsolePrivate::backgroundColor (void) const
{
  return m_colors[15];
}

QColor QConsolePrivate::foregroundColor (void) const
{
  return m_colors[0];
}

QColor QConsolePrivate::selectionColor (void) const
{
  return m_selectionColor;
}

QColor QConsolePrivate::cursorColor (void) const
{
  return m_cursorColor.isValid () ? m_cursorColor : foregroundColor ();
}

void QConsolePrivate::setBackgroundColor (const QColor& color)
{
  m_colors[15] = color;
}

void QConsolePrivate::setForegroundColor (const QColor& color)
{
  m_colors[0] = color;
}

void QConsolePrivate::setSelectionColor (const QColor& color)
{
  m_selectionColor = color;
}

void QConsolePrivate::setCursorColor (bool useForegroundColor,
                                      const QColor& color)
{
  m_cursorColor = useForegroundColor ? QColor () : color;
}

void QConsolePrivate::drawTextBackground (QPainter& p, int cx1, int cy1,
                                          int cx2, int cy2, int cw, int ch)
{
  p.save ();

  int ascent = p.fontMetrics ().ascent ();
  int stride = m_consoleRect.width ();
  int y = ascent + cy1 * ch;;

  for (int j = cy1; j <= cy2; j++, y += ch)
    {
      int len = 0;
      bool hasChar = false;
      int x = cx1 * cw;
      WORD attr = 0;

      for (int i = cx1; i <= cx2; i++)
        {
          CHAR_INFO* ci = &(m_buffer[stride*j+i]);

          if ((ci->Attributes & 0x00ff) != attr)
            {
              // Character attributes changed
              if (len != 0)
                {
                  // String buffer not empty -> draw it
                  if (hasChar || (attr & 0x00f0))
                    {
                      if (attr & 0x00f0)
                        p.fillRect (x, y-ascent, len * cw, ch, p.brush ());
                    }

                  x += (len * cw);
                  len = 0;
                  hasChar = false;
                }
              // Update current brush and store current attributes
              attr = (ci->Attributes & 0x00ff);
              p.setBrush (m_colors[(attr >> 4) & 0x000f]);
            }

          // Append current character to the string buffer
          len++;
          if (ci->Char.UnicodeChar != L' ')
            hasChar = true;
        }

      if (len != 0 && (hasChar || (attr & 0x00f0)))
        {
          // Line end reached, but string buffer not empty -> draw it
          // No need to update s or x, they will be reset on the next
          // for-loop iteration

          if (attr & 0x00f0)
            p.fillRect (x, y-ascent, len * cw, ch, p.brush ());
        }
    }

  p.restore ();
}

void QConsolePrivate::drawSelection (QPainter& p, int cx1, int cy1,
                                     int cx2, int cy2, int cw, int ch)
{
  p.save ();

  QPoint begin = m_beginSelection;
  QPoint end = m_endSelection;

  bool haveSelection = (begin != end);

  if (haveSelection)
    maybeSwapPoints (begin, end);

  int scrollOffset = m_consoleRect.top ();

  begin.ry () -= scrollOffset;
  end.ry () -= scrollOffset;

  int ascent = p.fontMetrics ().ascent ();
  int stride = m_consoleRect.width ();

  int y = ascent + cy1 * ch;;
  for (int j = cy1; j <= cy2; j++, y += ch)
    {
      int charsThisLine = 0;
      int len = 0;
      bool hasChar = false;
      WORD attr = 0;

      for (int i = cx1; i <= cx2; i++)
        {
          CHAR_INFO* ci = &(m_buffer[stride*j+i]);

          if ((ci->Attributes & 0x00ff) != attr)
            {
              // Character attributes changed
              if (len != 0)
                {
                  charsThisLine += len;
                  len = 0;
                  hasChar = false;
                }

              // Store current attributes
              attr = (ci->Attributes & 0x00ff);
            }

          // Append current character to the string buffer
          len++;
          if (ci->Char.UnicodeChar != L' ')
            hasChar = true;
        }

      if (len != 0 && (hasChar || (attr & 0x00f0)))
        charsThisLine += len;

      if (haveSelection && j >= begin.y () && j <= end.y ())
        {
          int selectionBegin = j == begin.y () ? begin.x (): 0;

          int len = ((j == end.y () && end.x () < charsThisLine)
                     ? end.x () - selectionBegin + 1
                     : stride - selectionBegin);

          p.fillRect (selectionBegin * cw, y-ascent, len * cw, ch,
                      selectionColor ());
        }
    }

  p.restore ();
}

void QConsolePrivate::drawCursor (QPainter& p)
{
  if (! m_cursorBlinking)
    {
      p.save ();

      QRect rect = cursorRect ();
      QColor color = cursorColor ();

      p.setPen (color);

      if (m_cursorType == QConsolePrivate::BlockCursor)
        {
          if (q->hasFocus ())
            p.fillRect (rect, color);
          else
            {
              // draw the cursor outline, adjusting the area so that
              // it is draw entirely inside 'rect'

              int penWidth = qMax (1, p.pen().width());

              p.drawRect (rect.adjusted (penWidth/2, penWidth/2,
                                         - penWidth/2 - penWidth%2,
                                         - penWidth/2 - penWidth%2));
            }
        }
      else if (m_cursorType == QConsolePrivate::UnderlineCursor)
        {
          p.drawLine (rect.left (), rect.bottom (),
                      rect.right (), rect.bottom ());
        }
      else if (m_cursorType == QConsolePrivate::IBeamCursor)
        {
          p.drawLine (rect.left (), rect.top (),
                      rect.left (), rect.bottom ());
        }

      p.restore ();
    }
}

void QConsolePrivate::drawText (QPainter& p, int cx1, int cy1,
                                int cx2, int cy2, int cw, int ch)
{
  p.save ();

  p.setFont (m_font);
  p.setPen (foregroundColor ());

  QString s;
  s.reserve (cx2 - cx1 + 1);

  int ascent = p.fontMetrics ().ascent ();
  int stride = m_consoleRect.width ();

  int y = ascent + cy1 * ch;;
  for (int j = cy1; j <= cy2; j++, y += ch)
    {
      // Reset string buffer and starting X coordinate
      s.clear ();
      bool hasChar = false;
      int x = cx1 * cw;
      WORD attr = 0;

      for (int i = cx1; i <= cx2; i++)
        {
          CHAR_INFO* ci = &(m_buffer[stride*j+i]);

          if ((ci->Attributes & 0x00ff) != attr)
            {
              // Character attributes changed
              if (! s.isEmpty ())
                {
                  // String buffer not empty -> draw it
                  if (hasChar || (attr & 0x00f0))
                    p.drawText (x, y, s);

                  x += (s.length () * cw);
                  s.clear ();
                  hasChar = false;
                }
              // Update current pen and store current attributes
              attr = (ci->Attributes & 0x00ff);
              p.setPen (m_colors[attr & 0x000f]);
            }

          // Append current character to the string buffer
          s.append (ci->Char.UnicodeChar);
          if (ci->Char.UnicodeChar != L' ')
            hasChar = true;
        }

      if (! s.isEmpty () && (hasChar || (attr & 0x00f0)))
        {
          // Line end reached, but string buffer not empty -> draw it
          // No need to update s or x, they will be reset on the next
          // for-loop iteration

          p.drawText (x, y, s);
        }
    }

  p.restore ();
}

/////////////////////////////////////////////////////////////////////////////

void QConsolePrivate::closeStandardIO (int fd, DWORD stdHandleId,
                                       const char* name)
{
  if (close (fd) == -1)
    log ("Failed to close file descriptor %d: errno=%d.\n", fd, errno);
  if (! CloseHandle (GetStdHandle (stdHandleId)))
    log ("Failed to close Win32 %s: error=%08x.\n", name, GetLastError ());
}

//////////////////////////////////////////////////////////////////////////////

void QConsolePrivate::log (const char* fmt, ...)
{
#ifdef DEBUG_QCONSOLE
  if (fmt)
    {
      va_list l;
      FILE* flog = fopen (LOGFILENAME, "ab");

      va_start (l, fmt);
      vfprintf (flog, fmt, l);
      va_end (l);
      fclose (flog);
    }
  else
    {
      // Special case to re-initialize the log file
      FILE* flog = fopen (LOGFILENAME, "w");
      fclose (flog);
    }
#else
  Q_UNUSED (fmt);
#endif
}

//////////////////////////////////////////////////////////////////////////////

void QConsolePrivate::updateConsoleSize (bool sync)
{
  QFontMetrics fm (m_font);
  QSize winSize = m_consoleView->size ();

  m_charSize.rwidth () = fm.averageCharWidth ();
  m_charSize.rheight () = fm.lineSpacing ();

  m_consoleRect.setWidth (winSize.width () / fm.averageCharWidth ());
  m_consoleRect.setHeight (winSize.height () / fm.lineSpacing ());

  m_bufferSize.rwidth () = m_consoleRect.width ();
  m_bufferSize.rheight () = qMax (m_bufferSize.height (),
                                  m_consoleRect.height ());

  m_consoleRect.moveLeft (0);
  if (m_consoleRect.bottom () >= m_bufferSize.height ())
    m_consoleRect.moveTop (m_bufferSize.height () - m_consoleRect.height ());

  log ("Console resized:\n");
  log ("  widget size: %d x %d\n", winSize.width (), winSize.height ());
  log ("  buffer size: %d x %d\n", m_bufferSize.width (),
       m_bufferSize.height ());
  log ("  window: (%d, %d) -> (%d, %d) [%d x %d]\n",
       m_consoleRect.left (), m_consoleRect.top (),
       m_consoleRect.right (), m_consoleRect.bottom (),
       m_consoleRect.width (), m_consoleRect.height ());

  if (sync)
    syncConsoleParameters ();

  updateScrollBar ();
}

//////////////////////////////////////////////////////////////////////////////

void QConsolePrivate::syncConsoleParameters (void)
{
  CONSOLE_SCREEN_BUFFER_INFO sbi;
  HANDLE hStdOut = m_stdOut;

  GetConsoleScreenBufferInfo (hStdOut, &sbi);

  COORD bs;
  SMALL_RECT sr;

  bs.X = sbi.dwSize.X;
  bs.Y = m_bufferSize.height ();
  sr.Left   = sbi.srWindow.Left;
  sr.Right  = sbi.srWindow.Right;
  sr.Top    = m_consoleRect.top ();
  sr.Bottom = m_consoleRect.bottom ();

  if (bs.Y > sbi.dwSize.Y)
    {
      SetConsoleScreenBufferSize (hStdOut, bs);
      SetConsoleWindowInfo (hStdOut, TRUE, &sr);
    }
  else
    {
      SetConsoleWindowInfo (hStdOut, TRUE, &sr);
      SetConsoleScreenBufferSize (hStdOut, bs);
    }

  bs.X = m_bufferSize.width ();
  sr.Left  = m_consoleRect.left ();
  sr.Right = m_consoleRect.right ();

  if (bs.X > sbi.dwSize.X)
    {
      SetConsoleScreenBufferSize (hStdOut, bs);
      SetConsoleWindowInfo (hStdOut, TRUE, &sr);
    }
  else
    {
      SetConsoleWindowInfo (hStdOut, TRUE, &sr);
      SetConsoleScreenBufferSize (hStdOut, bs);
    }

  log ("Sync'ing console parameters:\n");
  log ("  buffer size: %d x %d\n", bs.X, bs.Y);
  log ("  window: (%d, %d) -> (%d, %d)\n",
       sr.Left, sr.Top, sr.Right, sr.Bottom);

  if (m_buffer)
    delete [] m_buffer;
  if (m_tmpBuffer)
    delete [] m_tmpBuffer;

  int bufSize = m_consoleRect.width () * m_consoleRect.height ();

  m_buffer = new CHAR_INFO[bufSize];
  m_tmpBuffer = new CHAR_INFO[bufSize];
}

//////////////////////////////////////////////////////////////////////////////

void QConsolePrivate::grabConsoleBuffer (CHAR_INFO* buf)
{
  COORD bs, bc;
  SMALL_RECT r;

  bs.X = m_consoleRect.width ();
  bs.Y = m_consoleRect.height ();
  bc.X = 0;
  bc.Y = 0;

  r.Left   = m_consoleRect.left ();
  r.Top    = m_consoleRect.top ();
  r.Right  = m_consoleRect.right ();
  r.Bottom = m_consoleRect.bottom ();

  if (! ReadConsoleOutput (m_stdOut, (buf ? buf : m_buffer), bs, bc, &r))
    qCritical ("cannot read console output");
}

//////////////////////////////////////////////////////////////////////////////

void QConsolePrivate::updateScrollBar (void)
{
  m_scrollBar->setMinimum (0);
  if (m_bufferSize.height () > m_consoleRect.height ())
    m_scrollBar->setMaximum (m_bufferSize.height () - m_consoleRect.height ());
  else
    m_scrollBar->setMaximum (0);
  m_scrollBar->setSingleStep (1);
  m_scrollBar->setPageStep (m_consoleRect.height ());
  m_scrollBar->setValue (m_consoleRect.top ());

  log ("Scrollbar parameters updated: %d/%d/%d/%d\n",
       m_scrollBar->minimum (), m_scrollBar->maximum (),
       m_scrollBar->singleStep (), m_scrollBar->pageStep ());
}

//////////////////////////////////////////////////////////////////////////////

void QConsolePrivate::setScrollValue (int value)
{
  if (value == m_consoleRect.top ())
    return;

  SMALL_RECT r;
  HANDLE hStdOut = m_stdOut;

  if (value + m_consoleRect.height () > m_bufferSize.height ())
    value = m_bufferSize.height () - m_consoleRect.height ();

  r.Left = m_consoleRect.left ();
  r.Top = value;
  r.Right = m_consoleRect.right ();
  r.Bottom = value + m_consoleRect.height () - 1;

  log ("Scrolling window: (%d, %d) -> (%d, %d) [%d x %d]\n",
       r.Left, r.Top, r.Right, r.Bottom,
       r.Right - r.Left + 1, r.Bottom - r.Top + 1);

  if (SetConsoleWindowInfo (hStdOut, TRUE, &r))
    {
      m_consoleRect.moveTop (value);
      updateConsoleView ();
    }
}

//////////////////////////////////////////////////////////////////////////////

void QConsolePrivate::updateConsoleView (bool grab)
{
  if (grab)
    grabConsoleBuffer ();
  m_consoleView->update ();
  m_consoleWatcher->start ();
}

//////////////////////////////////////////////////////////////////////////////

void QConsolePrivate::monitorConsole (void)
{
  CONSOLE_SCREEN_BUFFER_INFO sbi;
  HANDLE hStdOut = GetStdHandle (STD_OUTPUT_HANDLE);

  static wchar_t titleBuf[260];

  GetConsoleTitleW (titleBuf, sizeof (titleBuf));
  QString title = QString::fromWCharArray (titleBuf);

  if (title != m_title)
    {
      q->setWindowTitle (title);
      emit q->titleChanged (title);
    }

  if (GetConsoleScreenBufferInfo (hStdOut, &sbi))
    {
      if (m_bufferSize.width () != sbi.dwSize.X
          || m_bufferSize.height () != sbi.dwSize.Y)
        {
          // Buffer size changed
          m_bufferSize.rwidth () = sbi.dwSize.X;
          m_bufferSize.rheight () = sbi.dwSize.Y;
          updateScrollBar ();
        }

      if (m_cursorPos.x () != sbi.dwCursorPosition.X
          || m_cursorPos.y () != sbi.dwCursorPosition.Y)
        {
          // Cursor position changed
          m_consoleView->update
            ((m_cursorPos.x () - sbi.srWindow.Left) * m_charSize.width (),
             (m_cursorPos.y () - sbi.srWindow.Top) * m_charSize.height (),
             m_charSize.width (), m_charSize.height ());
          m_cursorPos.rx () = sbi.dwCursorPosition.X;
          m_cursorPos.ry () = sbi.dwCursorPosition.Y;
          m_consoleView->update
            ((m_cursorPos.x () - sbi.srWindow.Left) * m_charSize.width (),
             (m_cursorPos.y () - sbi.srWindow.Top) * m_charSize.height (),
             m_charSize.width (), m_charSize.height ());
        }

      if (m_consoleRect.left () != sbi.srWindow.Left
          || m_consoleRect.right () != sbi.srWindow.Right
          || m_consoleRect.top () != sbi.srWindow.Top
          || m_consoleRect.bottom () != sbi.srWindow.Bottom)
        {
          // Console window changed
          m_consoleRect = QRect (sbi.srWindow.Left, sbi.srWindow.Top,
                                 sbi.srWindow.Right - sbi.srWindow.Left + 1,
                                 sbi.srWindow.Bottom - sbi.srWindow.Top + 1);
          updateScrollBar ();
          updateConsoleView ();
          return;
        }

      if (m_tmpBuffer && m_buffer)
        {
          grabConsoleBuffer (m_tmpBuffer);
          if (memcmp (m_tmpBuffer, m_buffer,
                      sizeof (CHAR_INFO) * m_consoleRect.width () *
                      m_consoleRect.height ()))
            {
              // FIXME: compute the area to update based on the
              // difference between the 2 buffers.
              qSwap (m_buffer, m_tmpBuffer);
              updateConsoleView (false);
            }
        }
    }
}

//////////////////////////////////////////////////////////////////////////////

void QConsolePrivate::startCommand (void)
{
  QString cmd = m_command;

  if (cmd.isEmpty ())
    cmd = qgetenv ("COMSPEC").constData ();

  if (! cmd.isEmpty ())
    {
      STARTUPINFO si;
      PROCESS_INFORMATION pi;

      ZeroMemory (&si, sizeof (si));
      si.cb = sizeof (si);
      ZeroMemory (&pi, sizeof (pi));

      if (CreateProcessW (NULL,
                          (LPWSTR)cmd.unicode (),
                          NULL,
                          NULL,
                          TRUE,
                          0,
                          NULL,
                          NULL,
                          &si,
                          &pi))
        {
          CloseHandle (pi.hThread);
          m_process = pi.hProcess;
          WaitForSingleObject (m_process, INFINITE);
          CloseHandle (m_process);
          m_process = NULL;
        }
    }
}

//////////////////////////////////////////////////////////////////////////////

void QConsolePrivate::sendConsoleText (const QString& s)
{
  // Send the string in chunks of 512 characters. Each character is
  // translated into an equivalent keypress event.

#define TEXT_CHUNK_SIZE 512

  // clear any selection on inserting text
  clearSelection();

  int len = s.length ();
  INPUT_RECORD events[TEXT_CHUNK_SIZE];
  DWORD nEvents = 0, written;
  HANDLE hStdIn = GetStdHandle (STD_INPUT_HANDLE);

  ZeroMemory (events, sizeof (events));

  for (int i = 0; i < len; i++)
    {
      QChar c = s.at (i);

      if (c == L'\r' || c == L'\n')
        {
          if (c == L'\r' && i < (len - 1) && s.at (i+1) == L'\n')
            i++;

          // add new line
          events[nEvents].EventType                        = KEY_EVENT;
          events[nEvents].Event.KeyEvent.bKeyDown          = TRUE;
          events[nEvents].Event.KeyEvent.wRepeatCount      = 1;
          events[nEvents].Event.KeyEvent.wVirtualKeyCode   =
            VK_RETURN;
          events[nEvents].Event.KeyEvent.wVirtualScanCode  = 0;
          events[nEvents].Event.KeyEvent.uChar.UnicodeChar = c.unicode ();
          events[nEvents].Event.KeyEvent.dwControlKeyState = 0;
          nEvents++;

          WriteConsoleInput (hStdIn, events, nEvents, &written);
          nEvents = 0;
          ZeroMemory (events, sizeof (events));

        }
      else
        {
          events[nEvents].EventType                        = KEY_EVENT;
          events[nEvents].Event.KeyEvent.bKeyDown          = TRUE;
          events[nEvents].Event.KeyEvent.wRepeatCount      = 1;
          events[nEvents].Event.KeyEvent.wVirtualKeyCode   =
            LOBYTE (VkKeyScan (c.unicode ()));
          events[nEvents].Event.KeyEvent.wVirtualScanCode  = 0;
          events[nEvents].Event.KeyEvent.uChar.UnicodeChar = c.unicode ();
          events[nEvents].Event.KeyEvent.dwControlKeyState = 0;
          nEvents++;
        }

      if (nEvents == TEXT_CHUNK_SIZE
          || (nEvents > 0 && i == (len - 1)))
        {
          WriteConsoleInput (hStdIn, events, nEvents, &written);
          nEvents = 0;
          ZeroMemory (events, sizeof (events));
        }
    }
}

QRect
QConsolePrivate::cursorRect (void)
{
  int cw = m_charSize.width ();
  int ch = m_charSize.height ();

  return QRect ((m_cursorPos.x () - m_consoleRect.x ()) * cw,
                (m_cursorPos.y () - m_consoleRect.y ()) * ch,
                cw, ch);
}

//////////////////////////////////////////////////////////////////////////////

QWinTerminalImpl::QWinTerminalImpl (QWidget* parent)
    : QTerminal (parent), d (new QConsolePrivate (this))
{
    installEventFilter (this);

    connect (this, SIGNAL (set_global_shortcuts_signal (bool)),
           parent, SLOT (set_global_shortcuts (bool)));
}

//////////////////////////////////////////////////////////////////////////////

QWinTerminalImpl::QWinTerminalImpl (const QString& cmd, QWidget* parent)
    : QTerminal (parent), d (new QConsolePrivate (this, cmd))
{
}

//////////////////////////////////////////////////////////////////////////////

QWinTerminalImpl::~QWinTerminalImpl (void)
{
  delete d;
}

void QWinTerminalImpl::mouseMoveEvent (QMouseEvent *event)
{
  if (d->m_settingSelection)
    {
      d->m_endSelection = d->posToCell (event->pos ());

      updateSelection ();
    }
}

void QWinTerminalImpl::mousePressEvent (QMouseEvent *event)
{
  if (event->button () == Qt::LeftButton)
    {
      d->m_settingSelection = true;

      d->m_beginSelection = d->posToCell (event->pos ());
    }
}

void QWinTerminalImpl::mouseReleaseEvent (QMouseEvent *event)
{
  if (event->button () == Qt::LeftButton)
    {
      d->m_endSelection = d->posToCell (event->pos ());

      updateSelection ();

      d->m_settingSelection = false;
    }
}

//////////////////////////////////////////////////////////////////////////////

void QWinTerminalImpl::viewResizeEvent (QConsoleView*, QResizeEvent*)
{
  d->updateConsoleSize (true);
  d->grabConsoleBuffer ();
}

//////////////////////////////////////////////////////////////////////////////

void QWinTerminalImpl::viewPaintEvent (QConsoleView* w, QPaintEvent* event)
{
  QPainter p (w);

  int cw = d->m_charSize.width ();
  int ch = d->m_charSize.height ();

  QRect updateRect = event->rect ();

  int cx1 = updateRect.left () / cw;
  int cy1 = updateRect.top () / ch;
  int cx2 = qMin (d->m_consoleRect.width () - 1, updateRect.right () / cw);
  int cy2 = qMin (d->m_consoleRect.height () - 1, updateRect.bottom () / ch);

  if (cx1 > d->m_consoleRect.width () - 1
      || cy1 > d->m_consoleRect.height () - 1)
    return;

  d->drawTextBackground (p, cx1, cy1, cx2, cy2, cw, ch);
  d->drawSelection (p, cx1, cy1, cx2, cy2, cw, ch);
  d->drawCursor (p);
  d->drawText (p, cx1, cy1, cx2, cy2, cw, ch);
}

void QWinTerminalImpl::blinkCursorEvent (void)
{
  if (d->m_hasBlinkingCursor)
    d->m_cursorBlinking = ! d->m_cursorBlinking;
  else
    d->m_cursorBlinking = false;

  d->m_consoleView->update (d->cursorRect ());
}

void QWinTerminalImpl::setBlinkingCursor (bool blink)
{
  d->m_hasBlinkingCursor = blink;

  setBlinkingCursorState (blink);
}

void QWinTerminalImpl::setBlinkingCursorState (bool blink)
{
  if (blink && ! d->m_blinkCursorTimer->isActive ())
    d->m_blinkCursorTimer->start (d->BLINK_DELAY);

  if (! blink && d->m_blinkCursorTimer->isActive ())
    {
      d->m_blinkCursorTimer->stop ();

      if (d->m_cursorBlinking)
        blinkCursorEvent ();
    }
}

//////////////////////////////////////////////////////////////////////////////

void QWinTerminalImpl::wheelEvent (QWheelEvent* event)
{
  if (! d->m_inWheelEvent)
    {
      // Forward to the scrollbar (avoid recursion)
      d->m_inWheelEvent = true;
      QApplication::sendEvent (d->m_scrollBar, event);
      d->m_inWheelEvent = false;
    }
}

//////////////////////////////////////////////////////////////////////////////

void QWinTerminalImpl::scrollValueChanged (int value)
{
  d->setScrollValue (value);
}

//////////////////////////////////////////////////////////////////////////////

void QWinTerminalImpl::monitorConsole (void)
{
  d->monitorConsole ();
}

void QWinTerminalImpl::updateSelection (void)
{
  d->updateSelection ();
}

//////////////////////////////////////////////////////////////////////////////

void QWinTerminalImpl::focusInEvent (QFocusEvent* event)
{
  emit set_global_shortcuts_signal (false);   // disable some shortcuts

  setBlinkingCursorState (true);

  QWidget::focusInEvent (event);
}

void QWinTerminalImpl::focusOutEvent (QFocusEvent* event)
{
  emit set_global_shortcuts_signal (true);    // re-enable shortcuts

  // Force the cursor to be redrawn.
  d->m_cursorBlinking = true;

  setBlinkingCursorState (false);

  QWidget::focusOutEvent (event);
}

bool QWinTerminalImpl::eventFilter (QObject *obj, QEvent * event)
{
  // if a keypress, filter out tab keys so that the next/prev tabbing is
  // disabled - but we still need to pass along to the console .
  if (event->type () == QEvent::KeyPress)
  {
    QKeyEvent* k = static_cast<QKeyEvent*>(event);
    if (k->key () == Qt::Key_Tab)
    {
      sendText ("\t");
      return true;
    }
  }
  return false;
}

void QWinTerminalImpl::keyPressEvent (QKeyEvent* event)
{
  QString s = translateKey (event);
  if (!s.isEmpty ())
    sendText (s);

  if (d->m_hasBlinkingCursor)
    {
      d->m_blinkCursorTimer->start (d->BLINK_DELAY);

      if (d->m_cursorBlinking)
        blinkCursorEvent ();
    }

  QWidget::keyPressEvent (event);
}

//////////////////////////////////////////////////////////////////////////////

void QWinTerminalImpl::start (void)
{
  d->startCommand ();
}

//////////////////////////////////////////////////////////////////////////////

void QWinTerminalImpl::sendText (const QString& s)
{
  d->sendConsoleText (s);
}

void QWinTerminalImpl::setCursorType (CursorType type, bool blinking)
{
  switch (type)
    {
    case UnderlineCursor:
      d->m_cursorType = QConsolePrivate::UnderlineCursor;
      break;

    case BlockCursor:
      d->m_cursorType = QConsolePrivate::BlockCursor;
      break;

    case IBeamCursor:
      d->m_cursorType = QConsolePrivate::IBeamCursor;
      break;
    }

  setBlinkingCursor (blinking);
}

void QWinTerminalImpl::setBackgroundColor (const QColor& color)
{
  d->setBackgroundColor (color);
}

void QWinTerminalImpl::setForegroundColor (const QColor& color)
{
  d->setForegroundColor (color);
}

void QWinTerminalImpl::setSelectionColor (const QColor& color)
{
  d->setSelectionColor (color);
}

void QWinTerminalImpl::setCursorColor (bool useForegroundColor,
                                       const QColor& color)
{
  d->setCursorColor (useForegroundColor, color);
}

//////////////////////////////////////////////////////////////////////////////

void QWinTerminalImpl::setTerminalFont (const QFont& f)
{
  d->m_font = f;
  d->m_consoleView->setFont (f);
  d->updateConsoleSize (true);
}

//////////////////////////////////////////////////////////////////////////////

void QWinTerminalImpl::setSize (int columns, int lines)
{
  Q_UNUSED (columns);
  Q_UNUSED (lines);
}

//////////////////////////////////////////////////////////////////////////////

void QWinTerminalImpl::copyClipboard (void)
{
  if(!hasFocus()) return;

  QClipboard *clipboard = QApplication::clipboard ();

  QString selection = d->getSelection ();

  if (selection.isEmpty ())
    terminal_interrupt ();
  else
    {
      clipboard->setText (selection);

      emit report_status_message (tr ("copied selection to clipboard"));
    }
}

//////////////////////////////////////////////////////////////////////////////

void QWinTerminalImpl::pasteClipboard (void)
{
  if(!hasFocus()) return;

  QString text = QApplication::clipboard()->text (QClipboard::Clipboard);

  if (! text.isEmpty ())
    sendText (text);
}


//////////////////////////////////////////////////////////////////////////////

QString QWinTerminalImpl::selectedText ()
{
  QString selection = d->getSelection ();
  return selection;
}