1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/graphics/windowing/windowserver/minigui/src/econswserv.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,693 @@
1.4 +// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
1.5 +// All rights reserved.
1.6 +// This component and the accompanying materials are made available
1.7 +// under the terms of "Eclipse Public License v1.0"
1.8 +// which accompanies this distribution, and is available
1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
1.10 +//
1.11 +// Initial Contributors:
1.12 +// Nokia Corporation - initial contribution.
1.13 +//
1.14 +// Contributors:
1.15 +//
1.16 +// Description:
1.17 +//
1.18 +
1.19 +#include <e32cons.h>
1.20 +#include <e32keys.h>
1.21 +#include <e32base.h>
1.22 +#include <w32std.h>
1.23 +#include <gdi.h>
1.24 +#include <bitstd.h>
1.25 +#include <bitdev.h>
1.26 +
1.27 +const TInt KGroupId = 0x100039e7;
1.28 +
1.29 +class CWsConsoleMux;
1.30 +class CWsConsole;
1.31 +
1.32 +// Active object to listen to Wserv events. Only used when
1.33 +// application thread has installed an active scheduler
1.34 +//
1.35 +NONSHARABLE_CLASS(CWsKeyReader): public CActive
1.36 + {
1.37 +public:
1.38 + CWsKeyReader(CWsConsoleMux* aMux);
1.39 + void MakeRequest();
1.40 +
1.41 +protected:
1.42 + void RunL();
1.43 + void DoCancel();
1.44 +
1.45 +private:
1.46 + CWsConsoleMux* iMux; //uses
1.47 + };
1.48 +
1.49 +// Master console to dispatch key events to relevant console in situation where
1.50 +// a single application thread owns multiple instances of console (CConsoleBase).
1.51 +// Reference counted as only a single instance of this class will be created
1.52 +// for each thread.
1.53 +//
1.54 +NONSHARABLE_CLASS(CWsConsoleMux): public CBase
1.55 + {
1.56 +public:
1.57 + static TInt Open();
1.58 + static void Close();
1.59 + static CWsConsoleMux* Static();
1.60 + ~CWsConsoleMux();
1.61 +
1.62 + void MakeRequest(CWsConsole* aConsole);
1.63 + void CancelRequest(CWsConsole* aConsole);
1.64 + void RequestComplete();
1.65 +
1.66 + inline RWsSession& Session();
1.67 + inline RWindowGroup& Group();
1.68 + inline CWindowGc& Gc();
1.69 + inline CWsScreenDevice& Screen();
1.70 +
1.71 +private:
1.72 + static CWsConsoleMux* NewL();
1.73 + CWsConsoleMux();
1.74 + void ConstructL();
1.75 +
1.76 +private:
1.77 + TInt iRefCount;
1.78 + RWsSession iWs;
1.79 + RWindowGroup iGroup;
1.80 + CWsScreenDevice* iScr;
1.81 + CWindowGc* iGc;
1.82 +
1.83 + CWsConsole* iConsole;
1.84 + CWsKeyReader* iReader;
1.85 + };
1.86 +
1.87 +// Console that uses direct Wserv API. Each console will own a window and
1.88 +// a bitmap for its backing store, but will be sharing Wserv session, window group,
1.89 +// screen device and window drawing context
1.90 +//
1.91 +NONSHARABLE_CLASS(CWsConsole): public CConsoleBase
1.92 + {
1.93 + friend class CWsConsoleMux;
1.94 +
1.95 +public:
1.96 + CWsConsole();
1.97 + virtual ~CWsConsole();
1.98 +
1.99 + //from CConsoleBase
1.100 + virtual TInt Create(const TDesC &aTitle,TSize aSize);
1.101 + virtual void Read(TRequestStatus &aStatus);
1.102 + virtual void ReadCancel();
1.103 + virtual void Write(const TDesC &aDes);
1.104 + virtual TPoint CursorPos() const;
1.105 + virtual void SetCursorPosAbs(const TPoint &aPoint);
1.106 + virtual void SetCursorPosRel(const TPoint &aPoint);
1.107 + virtual void SetCursorHeight(TInt aPercentage);
1.108 + virtual void SetTitle(const TDesC &aTitle);
1.109 + virtual void ClearScreen();
1.110 + virtual void ClearToEndOfLine();
1.111 + virtual TSize ScreenSize() const;
1.112 + virtual TKeyCode KeyCode() const;
1.113 + virtual TUint KeyModifiers() const;
1.114 +
1.115 +private:
1.116 + // for CWsConsoleMux
1.117 + TRequestStatus* ReadStatus();
1.118 + void SetKey(TUint aCode, TUint aModifier);
1.119 +
1.120 + // internal
1.121 + void ConstructL();
1.122 + void DrawNow();
1.123 + void Scroll();
1.124 + TPoint WritePos();
1.125 + void CarriageReturn();
1.126 + void LineFeed();
1.127 + void Left();
1.128 + void Right();
1.129 + void WriteChar(TText);
1.130 +
1.131 +private:
1.132 + // window stuff
1.133 + RWindow iWin;
1.134 + TSize iWindowSize;
1.135 +
1.136 + // back buffer
1.137 + CFbsBitmap* iBitmap;
1.138 + CFbsBitGc* iBitGc;
1.139 + CFbsBitmapDevice* iBitDev;
1.140 +
1.141 + // console stuff in chars dimension
1.142 + TPoint iCursorPos; //The position of the cursor is on the baseline
1.143 + TSize iSizeInChars;
1.144 +
1.145 + // font stuff
1.146 + CFont* iFont;
1.147 + TSize iCharSize;
1.148 + TInt iFontAscent;
1.149 + TInt iFontDescent;
1.150 +
1.151 + TKeyCode iKeyCode;
1.152 + TUint iModifiers;
1.153 + TTextCursor iTextCursor;
1.154 +
1.155 + // master console
1.156 + CWsConsoleMux* iMux;
1.157 + TRequestStatus* iReadStatus;
1.158 + };
1.159 +
1.160 +//
1.161 +// CWsKeyReader implementation
1.162 +//
1.163 +//
1.164 +//
1.165 +CWsKeyReader::CWsKeyReader(CWsConsoleMux* aMux):
1.166 + CActive(CActive::EPriorityStandard),
1.167 + iMux(aMux)
1.168 + {
1.169 + CActiveScheduler::Add(this);
1.170 + }
1.171 +
1.172 +void CWsKeyReader::MakeRequest()
1.173 + {
1.174 + SetActive();
1.175 + iMux->Session().EventReady(&iStatus);
1.176 + }
1.177 +
1.178 +void CWsKeyReader::RunL()
1.179 + {
1.180 + // let master console decide what to do
1.181 + iMux->RequestComplete();
1.182 + }
1.183 +
1.184 +void CWsKeyReader::DoCancel()
1.185 + {
1.186 + iMux->Session().EventReadyCancel();
1.187 + }
1.188 +
1.189 +//
1.190 +// CWsConsoleMux implementation
1.191 +//
1.192 +//
1.193 +//
1.194 +TInt CWsConsoleMux::Open()
1.195 + {
1.196 + CWsConsoleMux* mux = (CWsConsoleMux*)Dll::Tls();
1.197 +
1.198 + // not the first time, simply increment refcount
1.199 + if (mux)
1.200 + {
1.201 + ++mux->iRefCount;
1.202 + return KErrNone;
1.203 + }
1.204 +
1.205 + // first time, let's create master console and
1.206 + // stick it to TLS
1.207 + TRAPD(err, mux = CWsConsoleMux::NewL());
1.208 + if (err != KErrNone)
1.209 + {
1.210 + return err;
1.211 + }
1.212 +
1.213 + err = Dll::SetTls(mux);
1.214 + if (err != KErrNone)
1.215 + {
1.216 + delete mux;
1.217 + return err;
1.218 + }
1.219 +
1.220 + ++mux->iRefCount;
1.221 + return KErrNone;
1.222 + }
1.223 +
1.224 +void CWsConsoleMux::Close()
1.225 + {
1.226 + CWsConsoleMux* mux = (CWsConsoleMux*)Dll::Tls();
1.227 + if (!mux)
1.228 + {
1.229 + return;
1.230 + }
1.231 +
1.232 + // destroy master console if this is the last use from
1.233 + // this thread and reset TLS
1.234 + if (--mux->iRefCount == 0)
1.235 + {
1.236 + delete mux;
1.237 + Dll::FreeTls();
1.238 + }
1.239 + }
1.240 +
1.241 +CWsConsoleMux* CWsConsoleMux::Static()
1.242 + {
1.243 + return (CWsConsoleMux*)Dll::Tls();
1.244 + }
1.245 +
1.246 +CWsConsoleMux::CWsConsoleMux()
1.247 + {
1.248 + }
1.249 +
1.250 +CWsConsoleMux::~CWsConsoleMux()
1.251 + {
1.252 + delete iReader;
1.253 + delete iGc;
1.254 + delete iScr;
1.255 + iGroup.Close();
1.256 + iWs.Close();
1.257 + }
1.258 +
1.259 +CWsConsoleMux* CWsConsoleMux::NewL()
1.260 + {
1.261 + CWsConsoleMux* mux = new(ELeave) CWsConsoleMux;
1.262 + CleanupStack::PushL(mux);
1.263 + mux->ConstructL();
1.264 + CleanupStack::Pop(mux);
1.265 +
1.266 + return mux;
1.267 + }
1.268 +
1.269 +void CWsConsoleMux::ConstructL()
1.270 + {
1.271 + TInt err = iWs.Connect();
1.272 + User::LeaveIfError(err);
1.273 +
1.274 + iScr = new(ELeave) CWsScreenDevice(iWs);
1.275 + err = iScr->Construct();
1.276 + User::LeaveIfError(err);
1.277 +
1.278 + iGc = new(ELeave) CWindowGc(iScr);
1.279 + err = iGc->Construct();
1.280 + User::LeaveIfError(err);
1.281 +
1.282 + iGroup = RWindowGroup(iWs);
1.283 + err = iGroup.Construct(KGroupId, ETrue);
1.284 + User::LeaveIfError(err);
1.285 +
1.286 + // we will be using active object to listen to Wserv events
1.287 + // only if this thread already has active scheduler
1.288 + //
1.289 + if (CActiveScheduler::Current())
1.290 + {
1.291 + iReader = new(ELeave) CWsKeyReader(this);
1.292 + }
1.293 + }
1.294 +
1.295 +RWsSession& CWsConsoleMux::Session()
1.296 + {
1.297 + return iWs;
1.298 + }
1.299 +
1.300 +RWindowGroup& CWsConsoleMux::Group()
1.301 + {
1.302 + return iGroup;
1.303 + }
1.304 +
1.305 +CWindowGc& CWsConsoleMux::Gc()
1.306 + {
1.307 + return *iGc;
1.308 + }
1.309 +
1.310 +CWsScreenDevice& CWsConsoleMux::Screen()
1.311 + {
1.312 + return *iScr;
1.313 + }
1.314 +
1.315 +void CWsConsoleMux::MakeRequest(CWsConsole* aConsole)
1.316 + {
1.317 + if (!iReader)
1.318 + {
1.319 + // client does not have scheduler, ideally we should use
1.320 + // secondary thread to make this asynchronous.
1.321 + //
1.322 + // however, we can get away with this because application
1.323 + // that doesn't use active scheduler will be calling asynchronous console read
1.324 + // and immediately followed by WaitForRequest e.g.
1.325 + //
1.326 + // while (1)
1.327 + // {
1.328 + // ...
1.329 + // console->Read(status);
1.330 + // User::WaitForRequest(status);
1.331 + // ...
1.332 + // }
1.333 + //
1.334 + TWsEvent event;
1.335 +
1.336 + // keep going until we got key event
1.337 + do
1.338 + {
1.339 + TRequestStatus s;
1.340 + s = KRequestPending;
1.341 + iWs.EventReady(&s);
1.342 + User::WaitForRequest(s);
1.343 +
1.344 + iWs.GetEvent(event);
1.345 + }
1.346 + while (event.Type() != EEventKey);
1.347 +
1.348 + aConsole->SetKey(event.Key()->iCode, event.Key()->iModifiers);
1.349 + TRequestStatus* pS = aConsole->ReadStatus();
1.350 + User::RequestComplete(pS, KErrNone);
1.351 + }
1.352 + else
1.353 + {
1.354 + iConsole = aConsole;
1.355 + iReader->MakeRequest();
1.356 + }
1.357 + }
1.358 +
1.359 +// called from key listener RunL
1.360 +//
1.361 +void CWsConsoleMux::RequestComplete()
1.362 + {
1.363 + TWsEvent event;
1.364 + iWs.GetEvent(event);
1.365 +
1.366 + if (event.Type() == EEventKey)
1.367 + {
1.368 + iConsole->SetKey(event.Key()->iCode, event.Key()->iModifiers);
1.369 + TRequestStatus* pS = iConsole->ReadStatus();
1.370 + User::RequestComplete(pS, KErrNone);
1.371 + iConsole = NULL;
1.372 + }
1.373 + else
1.374 + {
1.375 + // keep going until we got key event
1.376 + //
1.377 + iReader->MakeRequest();
1.378 + }
1.379 + }
1.380 +
1.381 +void CWsConsoleMux::CancelRequest(CWsConsole* aConsole)
1.382 + {
1.383 + if (iReader && iConsole == aConsole)
1.384 + {
1.385 + iReader->Cancel();
1.386 + }
1.387 + }
1.388 +
1.389 +//
1.390 +// CWsConsole implementation
1.391 +//
1.392 +//
1.393 +//
1.394 +CWsConsole::CWsConsole()
1.395 + {
1.396 + }
1.397 +
1.398 +CWsConsole::~CWsConsole()
1.399 + {
1.400 + if(iBitGc)
1.401 + {
1.402 + iBitGc->DiscardFont();
1.403 + }
1.404 + if(iBitDev)
1.405 + {
1.406 + iBitDev->ReleaseFont(iFont);
1.407 + }
1.408 +
1.409 + delete iBitGc;
1.410 + delete iBitDev;
1.411 + delete iBitmap;
1.412 + iWin.Close();
1.413 + iMux->Session().Flush();
1.414 +
1.415 + CWsConsoleMux::Close();
1.416 + }
1.417 +
1.418 +TInt CWsConsole::Create(const TDesC& /*aTitle*/, TSize /*aSize*/)
1.419 + {
1.420 + // we must not push this object to cleanup stack during construction because
1.421 + // caller will explicitly call delete on this object when Create() returns error
1.422 + //
1.423 + TRAPD(err, ConstructL());
1.424 + return err;
1.425 + }
1.426 +
1.427 +void CWsConsole::ConstructL()
1.428 + {
1.429 + TInt err = CWsConsoleMux::Open();
1.430 + User::LeaveIfError(err);
1.431 +
1.432 + iMux = CWsConsoleMux::Static();
1.433 +
1.434 + iWindowSize = iMux->Screen().SizeInPixels();
1.435 +
1.436 + iBitmap = new(ELeave) CFbsBitmap;
1.437 + err = iBitmap->Create(iWindowSize, EGray2);
1.438 + User::LeaveIfError(err);
1.439 +
1.440 + iBitDev = CFbsBitmapDevice::NewL(iBitmap);
1.441 + err = iBitDev->CreateContext(iBitGc);
1.442 + User::LeaveIfError(err);
1.443 +
1.444 + iBitGc->SetPenColor(KRgbBlack);
1.445 + iBitGc->SetBrushStyle(CGraphicsContext::ESolidBrush);
1.446 + iBitGc->SetBrushColor(KRgbWhite);
1.447 + iBitGc->Clear();
1.448 +
1.449 + TFontSpec fs(_L("DejaVu Sans Mono"), 12);
1.450 + err = iBitDev->GetNearestFontToDesignHeightInPixels(iFont, fs);
1.451 + User::LeaveIfError(err);
1.452 +
1.453 + iBitGc->UseFont(iFont);
1.454 +
1.455 + // uses monospace font to get uniform glpyh size
1.456 + iCharSize.iHeight = iFont->FontMaxAscent() + iFont->FontMaxDescent();
1.457 + iCharSize.iWidth = iFont->MaxCharWidthInPixels();
1.458 + iFontAscent = iFont->FontMaxAscent();
1.459 + iFontDescent= iFont->FontMaxDescent();
1.460 +
1.461 + // cursor represent char position in character-based area
1.462 + // eshell is using 0,0 based
1.463 + iCursorPos = TPoint(1,1);
1.464 + // console size in number of characters and lines
1.465 + iSizeInChars.iWidth = iWindowSize.iWidth / iCharSize.iWidth;
1.466 + iSizeInChars.iHeight = iWindowSize.iHeight / iCharSize.iHeight;
1.467 +
1.468 + iTextCursor.iType = TTextCursor::ETypeRectangle;
1.469 + iTextCursor.iHeight = iCharSize.iHeight;
1.470 + iTextCursor.iAscent = iFontAscent;
1.471 + iTextCursor.iWidth = iCharSize.iWidth;
1.472 + iTextCursor.iFlags = 0;
1.473 + iTextCursor.iColor = KRgbWhite;
1.474 +
1.475 +
1.476 + iWin = RWindow(iMux->Session());
1.477 + err = iWin.Construct(iMux->Group(), (TInt)this);
1.478 + User::LeaveIfError(err);
1.479 +
1.480 + iWin.Activate();
1.481 + iMux->Session().Flush();
1.482 + }
1.483 +
1.484 +TRequestStatus* CWsConsole::ReadStatus()
1.485 + {
1.486 + return iReadStatus;
1.487 + }
1.488 +
1.489 +void CWsConsole::SetKey(TUint aCode, TUint aModifier)
1.490 + {
1.491 + iKeyCode = (TKeyCode)aCode;
1.492 + iModifiers = aModifier;
1.493 + }
1.494 +
1.495 +TPoint CWsConsole::WritePos()
1.496 + {
1.497 + return TPoint((iCursorPos.iX - 1) * iCharSize.iWidth, (iCursorPos.iY - 1) * iCharSize.iHeight + iFontAscent);
1.498 + }
1.499 +
1.500 +void CWsConsole::Read(TRequestStatus &aStatus)
1.501 + {
1.502 + iReadStatus = &aStatus;
1.503 + *iReadStatus = KRequestPending;
1.504 + iMux->MakeRequest(this);
1.505 + }
1.506 +
1.507 +void CWsConsole::ReadCancel()
1.508 + {
1.509 + iMux->CancelRequest(this);
1.510 + iReadStatus = NULL;
1.511 + }
1.512 +
1.513 +void CWsConsole::CarriageReturn()
1.514 + {
1.515 + iCursorPos.iX = 1;
1.516 + }
1.517 +
1.518 +void CWsConsole::LineFeed()
1.519 + {
1.520 + CarriageReturn();
1.521 + if (iCursorPos.iY < iSizeInChars.iHeight)
1.522 + {
1.523 + ++iCursorPos.iY;
1.524 + }
1.525 + else
1.526 + {
1.527 + Scroll();
1.528 + }
1.529 + }
1.530 +
1.531 +void CWsConsole::Left()
1.532 + {
1.533 + if (iCursorPos != TPoint(1,1))
1.534 + {
1.535 + if (iCursorPos.iX == 1)
1.536 + {
1.537 + iCursorPos.iX += iSizeInChars.iWidth;
1.538 + --iCursorPos.iY;
1.539 + }
1.540 + --iCursorPos.iX;
1.541 + }
1.542 + }
1.543 +
1.544 +void CWsConsole::Right()
1.545 + {
1.546 + ++iCursorPos.iX;
1.547 + if (iCursorPos.iX > iSizeInChars.iWidth)
1.548 + {
1.549 + LineFeed();
1.550 + }
1.551 + }
1.552 +
1.553 +void CWsConsole::Write(const TDesC& aDes)
1.554 + {
1.555 + const TInt length = aDes.Length();
1.556 + if (length == 0)
1.557 + {
1.558 + return;
1.559 + }
1.560 +
1.561 + for (TInt i=0; i<length; ++i)
1.562 + {
1.563 + switch (aDes[i])
1.564 + {
1.565 + case 0x07:
1.566 + RDebug::Print(_L("WSCON: End of command line!"));
1.567 + break;
1.568 + case 0x0a:
1.569 + LineFeed();
1.570 + break;
1.571 + case 0x0d:
1.572 + CarriageReturn();
1.573 + break;
1.574 + default:
1.575 + WriteChar(aDes[i]);
1.576 + break;
1.577 + }
1.578 + }
1.579 +
1.580 + DrawNow();
1.581 + }
1.582 +
1.583 +void CWsConsole::WriteChar(TText aChar)
1.584 + {
1.585 + TBuf<1> s;
1.586 + s.Append(aChar);
1.587 + // use draw text box to clear the glpyh box
1.588 + TPoint writePos = WritePos();
1.589 + TRect r(writePos.iX, writePos.iY-iFontAscent,writePos.iX+iCharSize.iWidth, writePos.iY+iFontDescent);
1.590 + iBitGc->DrawText(s, r, iFontAscent);
1.591 + Right();
1.592 + }
1.593 +
1.594 +TPoint CWsConsole::CursorPos() const
1.595 + {
1.596 + // eshell is using 0,0 based
1.597 + return iCursorPos - TPoint(1,1);
1.598 + }
1.599 +
1.600 +void CWsConsole::SetCursorPosAbs(const TPoint& aPoint)
1.601 + {
1.602 + // eshell is using 0,0 based
1.603 + iCursorPos = aPoint + TPoint(1,1);
1.604 + iMux->Group().SetTextCursor(iWin, WritePos(), iTextCursor);
1.605 + }
1.606 +
1.607 +void CWsConsole::SetCursorPosRel(const TPoint& aPoint)
1.608 + {
1.609 + iCursorPos += aPoint;
1.610 + iMux->Group().SetTextCursor(iWin, WritePos(), iTextCursor);
1.611 + }
1.612 +
1.613 +void CWsConsole::SetCursorHeight(TInt aPercentage)
1.614 + {
1.615 + if (aPercentage == 0)
1.616 + {
1.617 + // none
1.618 + iTextCursor.iHeight = 0;
1.619 + }
1.620 + else if (aPercentage == 100)
1.621 + {
1.622 + // insert
1.623 + iTextCursor.iHeight = iCharSize.iHeight;
1.624 + iTextCursor.iAscent = iFontAscent;
1.625 + }
1.626 + else
1.627 + {
1.628 + // normal
1.629 + iTextCursor.iHeight = 1;
1.630 + iTextCursor.iAscent = 0;
1.631 + }
1.632 +
1.633 + iMux->Group().SetTextCursor(iWin, WritePos(), iTextCursor);
1.634 + }
1.635 +
1.636 +void CWsConsole::SetTitle(const TDesC& /*aTitle*/)
1.637 + {
1.638 + }
1.639 +
1.640 +void CWsConsole::ClearScreen()
1.641 + {
1.642 + iBitGc->Clear();
1.643 + iCursorPos.iX = iCursorPos.iY = 1;
1.644 + DrawNow();
1.645 + }
1.646 +
1.647 +void CWsConsole::ClearToEndOfLine()
1.648 + {
1.649 + iMux->Group().CancelTextCursor();
1.650 + TPoint writePos = WritePos();
1.651 + TRect r(writePos.iX, writePos.iY-iFontAscent, iWindowSize.iWidth, writePos.iY+iFontDescent);
1.652 + iBitGc->Clear(r);
1.653 + DrawNow();
1.654 + iMux->Group().SetTextCursor(iWin, WritePos(), iTextCursor);
1.655 + }
1.656 +
1.657 +TSize CWsConsole::ScreenSize() const
1.658 + {
1.659 + return iSizeInChars;
1.660 + }
1.661 +
1.662 +TKeyCode CWsConsole::KeyCode() const
1.663 + {
1.664 + return iKeyCode;
1.665 + }
1.666 +
1.667 +TUint CWsConsole::KeyModifiers() const
1.668 + {
1.669 + return iModifiers;
1.670 + }
1.671 +
1.672 +void CWsConsole::DrawNow()
1.673 + {
1.674 + iWin.Invalidate();
1.675 + iWin.BeginRedraw();
1.676 + iMux->Gc().Activate(iWin);
1.677 + iMux->Gc().BitBlt(TPoint(0,0), iBitmap);
1.678 + iMux->Gc().Deactivate();
1.679 + iWin.EndRedraw();
1.680 + iMux->Session().Flush();
1.681 + }
1.682 +
1.683 +void CWsConsole::Scroll()
1.684 + {
1.685 + TRect r(0, iCharSize.iHeight, iWindowSize.iWidth, iSizeInChars.iHeight * iCharSize.iHeight);
1.686 +
1.687 + iBitGc->CopyRect(TPoint(0, -iCharSize.iHeight), r);
1.688 + // clear last line
1.689 + TRect rect(0, WritePos().iY-iFontAscent, iWindowSize.iWidth, iWindowSize.iHeight);
1.690 + iBitGc->Clear(rect);
1.691 + }
1.692 +
1.693 +extern "C" EXPORT_C TAny *NewConsole()
1.694 + {
1.695 + return new CWsConsole;
1.696 + }