First public contribution.
1 // Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
2 // All rights reserved.
3 // This component and the accompanying materials are made available
4 // under the terms of "Eclipse Public License v1.0"
5 // which accompanies this distribution, and is available
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
24 const TInt KGroupId = 0x100039e7;
29 // Active object to listen to Wserv events. Only used when
30 // application thread has installed an active scheduler
32 NONSHARABLE_CLASS(CWsKeyReader): public CActive
35 CWsKeyReader(CWsConsoleMux* aMux);
43 CWsConsoleMux* iMux; //uses
46 // Master console to dispatch key events to relevant console in situation where
47 // a single application thread owns multiple instances of console (CConsoleBase).
48 // Reference counted as only a single instance of this class will be created
51 NONSHARABLE_CLASS(CWsConsoleMux): public CBase
56 static CWsConsoleMux* Static();
59 void MakeRequest(CWsConsole* aConsole);
60 void CancelRequest(CWsConsole* aConsole);
61 void RequestComplete();
63 inline RWsSession& Session();
64 inline RWindowGroup& Group();
65 inline CWindowGc& Gc();
66 inline CWsScreenDevice& Screen();
69 static CWsConsoleMux* NewL();
77 CWsScreenDevice* iScr;
81 CWsKeyReader* iReader;
84 // Console that uses direct Wserv API. Each console will own a window and
85 // a bitmap for its backing store, but will be sharing Wserv session, window group,
86 // screen device and window drawing context
88 NONSHARABLE_CLASS(CWsConsole): public CConsoleBase
90 friend class CWsConsoleMux;
94 virtual ~CWsConsole();
97 virtual TInt Create(const TDesC &aTitle,TSize aSize);
98 virtual void Read(TRequestStatus &aStatus);
99 virtual void ReadCancel();
100 virtual void Write(const TDesC &aDes);
101 virtual TPoint CursorPos() const;
102 virtual void SetCursorPosAbs(const TPoint &aPoint);
103 virtual void SetCursorPosRel(const TPoint &aPoint);
104 virtual void SetCursorHeight(TInt aPercentage);
105 virtual void SetTitle(const TDesC &aTitle);
106 virtual void ClearScreen();
107 virtual void ClearToEndOfLine();
108 virtual TSize ScreenSize() const;
109 virtual TKeyCode KeyCode() const;
110 virtual TUint KeyModifiers() const;
114 TRequestStatus* ReadStatus();
115 void SetKey(TUint aCode, TUint aModifier);
122 void CarriageReturn();
126 void WriteChar(TText);
136 CFbsBitmapDevice* iBitDev;
138 // console stuff in chars dimension
139 TPoint iCursorPos; //The position of the cursor is on the baseline
150 TTextCursor iTextCursor;
154 TRequestStatus* iReadStatus;
158 // CWsKeyReader implementation
162 CWsKeyReader::CWsKeyReader(CWsConsoleMux* aMux):
163 CActive(CActive::EPriorityStandard),
166 CActiveScheduler::Add(this);
169 void CWsKeyReader::MakeRequest()
172 iMux->Session().EventReady(&iStatus);
175 void CWsKeyReader::RunL()
177 // let master console decide what to do
178 iMux->RequestComplete();
181 void CWsKeyReader::DoCancel()
183 iMux->Session().EventReadyCancel();
187 // CWsConsoleMux implementation
191 TInt CWsConsoleMux::Open()
193 CWsConsoleMux* mux = (CWsConsoleMux*)Dll::Tls();
195 // not the first time, simply increment refcount
202 // first time, let's create master console and
204 TRAPD(err, mux = CWsConsoleMux::NewL());
210 err = Dll::SetTls(mux);
221 void CWsConsoleMux::Close()
223 CWsConsoleMux* mux = (CWsConsoleMux*)Dll::Tls();
229 // destroy master console if this is the last use from
230 // this thread and reset TLS
231 if (--mux->iRefCount == 0)
238 CWsConsoleMux* CWsConsoleMux::Static()
240 return (CWsConsoleMux*)Dll::Tls();
243 CWsConsoleMux::CWsConsoleMux()
247 CWsConsoleMux::~CWsConsoleMux()
256 CWsConsoleMux* CWsConsoleMux::NewL()
258 CWsConsoleMux* mux = new(ELeave) CWsConsoleMux;
259 CleanupStack::PushL(mux);
261 CleanupStack::Pop(mux);
266 void CWsConsoleMux::ConstructL()
268 TInt err = iWs.Connect();
269 User::LeaveIfError(err);
271 iScr = new(ELeave) CWsScreenDevice(iWs);
272 err = iScr->Construct();
273 User::LeaveIfError(err);
275 iGc = new(ELeave) CWindowGc(iScr);
276 err = iGc->Construct();
277 User::LeaveIfError(err);
279 iGroup = RWindowGroup(iWs);
280 err = iGroup.Construct(KGroupId, ETrue);
281 User::LeaveIfError(err);
283 // we will be using active object to listen to Wserv events
284 // only if this thread already has active scheduler
286 if (CActiveScheduler::Current())
288 iReader = new(ELeave) CWsKeyReader(this);
292 RWsSession& CWsConsoleMux::Session()
297 RWindowGroup& CWsConsoleMux::Group()
302 CWindowGc& CWsConsoleMux::Gc()
307 CWsScreenDevice& CWsConsoleMux::Screen()
312 void CWsConsoleMux::MakeRequest(CWsConsole* aConsole)
316 // client does not have scheduler, ideally we should use
317 // secondary thread to make this asynchronous.
319 // however, we can get away with this because application
320 // that doesn't use active scheduler will be calling asynchronous console read
321 // and immediately followed by WaitForRequest e.g.
326 // console->Read(status);
327 // User::WaitForRequest(status);
333 // keep going until we got key event
339 User::WaitForRequest(s);
343 while (event.Type() != EEventKey);
345 aConsole->SetKey(event.Key()->iCode, event.Key()->iModifiers);
346 TRequestStatus* pS = aConsole->ReadStatus();
347 User::RequestComplete(pS, KErrNone);
352 iReader->MakeRequest();
356 // called from key listener RunL
358 void CWsConsoleMux::RequestComplete()
363 if (event.Type() == EEventKey)
365 iConsole->SetKey(event.Key()->iCode, event.Key()->iModifiers);
366 TRequestStatus* pS = iConsole->ReadStatus();
367 User::RequestComplete(pS, KErrNone);
372 // keep going until we got key event
374 iReader->MakeRequest();
378 void CWsConsoleMux::CancelRequest(CWsConsole* aConsole)
380 if (iReader && iConsole == aConsole)
387 // CWsConsole implementation
391 CWsConsole::CWsConsole()
395 CWsConsole::~CWsConsole()
399 iBitGc->DiscardFont();
403 iBitDev->ReleaseFont(iFont);
410 iMux->Session().Flush();
412 CWsConsoleMux::Close();
415 TInt CWsConsole::Create(const TDesC& /*aTitle*/, TSize /*aSize*/)
417 // we must not push this object to cleanup stack during construction because
418 // caller will explicitly call delete on this object when Create() returns error
420 TRAPD(err, ConstructL());
424 void CWsConsole::ConstructL()
426 TInt err = CWsConsoleMux::Open();
427 User::LeaveIfError(err);
429 iMux = CWsConsoleMux::Static();
431 iWindowSize = iMux->Screen().SizeInPixels();
433 iBitmap = new(ELeave) CFbsBitmap;
434 err = iBitmap->Create(iWindowSize, EGray2);
435 User::LeaveIfError(err);
437 iBitDev = CFbsBitmapDevice::NewL(iBitmap);
438 err = iBitDev->CreateContext(iBitGc);
439 User::LeaveIfError(err);
441 iBitGc->SetPenColor(KRgbBlack);
442 iBitGc->SetBrushStyle(CGraphicsContext::ESolidBrush);
443 iBitGc->SetBrushColor(KRgbWhite);
446 TFontSpec fs(_L("DejaVu Sans Mono"), 12);
447 err = iBitDev->GetNearestFontToDesignHeightInPixels(iFont, fs);
448 User::LeaveIfError(err);
450 iBitGc->UseFont(iFont);
452 // uses monospace font to get uniform glpyh size
453 iCharSize.iHeight = iFont->FontMaxAscent() + iFont->FontMaxDescent();
454 iCharSize.iWidth = iFont->MaxCharWidthInPixels();
455 iFontAscent = iFont->FontMaxAscent();
456 iFontDescent= iFont->FontMaxDescent();
458 // cursor represent char position in character-based area
459 // eshell is using 0,0 based
460 iCursorPos = TPoint(1,1);
461 // console size in number of characters and lines
462 iSizeInChars.iWidth = iWindowSize.iWidth / iCharSize.iWidth;
463 iSizeInChars.iHeight = iWindowSize.iHeight / iCharSize.iHeight;
465 iTextCursor.iType = TTextCursor::ETypeRectangle;
466 iTextCursor.iHeight = iCharSize.iHeight;
467 iTextCursor.iAscent = iFontAscent;
468 iTextCursor.iWidth = iCharSize.iWidth;
469 iTextCursor.iFlags = 0;
470 iTextCursor.iColor = KRgbWhite;
473 iWin = RWindow(iMux->Session());
474 err = iWin.Construct(iMux->Group(), (TInt)this);
475 User::LeaveIfError(err);
478 iMux->Session().Flush();
481 TRequestStatus* CWsConsole::ReadStatus()
486 void CWsConsole::SetKey(TUint aCode, TUint aModifier)
488 iKeyCode = (TKeyCode)aCode;
489 iModifiers = aModifier;
492 TPoint CWsConsole::WritePos()
494 return TPoint((iCursorPos.iX - 1) * iCharSize.iWidth, (iCursorPos.iY - 1) * iCharSize.iHeight + iFontAscent);
497 void CWsConsole::Read(TRequestStatus &aStatus)
499 iReadStatus = &aStatus;
500 *iReadStatus = KRequestPending;
501 iMux->MakeRequest(this);
504 void CWsConsole::ReadCancel()
506 iMux->CancelRequest(this);
510 void CWsConsole::CarriageReturn()
515 void CWsConsole::LineFeed()
518 if (iCursorPos.iY < iSizeInChars.iHeight)
528 void CWsConsole::Left()
530 if (iCursorPos != TPoint(1,1))
532 if (iCursorPos.iX == 1)
534 iCursorPos.iX += iSizeInChars.iWidth;
541 void CWsConsole::Right()
544 if (iCursorPos.iX > iSizeInChars.iWidth)
550 void CWsConsole::Write(const TDesC& aDes)
552 const TInt length = aDes.Length();
558 for (TInt i=0; i<length; ++i)
563 RDebug::Print(_L("WSCON: End of command line!"));
580 void CWsConsole::WriteChar(TText aChar)
584 // use draw text box to clear the glpyh box
585 TPoint writePos = WritePos();
586 TRect r(writePos.iX, writePos.iY-iFontAscent,writePos.iX+iCharSize.iWidth, writePos.iY+iFontDescent);
587 iBitGc->DrawText(s, r, iFontAscent);
591 TPoint CWsConsole::CursorPos() const
593 // eshell is using 0,0 based
594 return iCursorPos - TPoint(1,1);
597 void CWsConsole::SetCursorPosAbs(const TPoint& aPoint)
599 // eshell is using 0,0 based
600 iCursorPos = aPoint + TPoint(1,1);
601 iMux->Group().SetTextCursor(iWin, WritePos(), iTextCursor);
604 void CWsConsole::SetCursorPosRel(const TPoint& aPoint)
606 iCursorPos += aPoint;
607 iMux->Group().SetTextCursor(iWin, WritePos(), iTextCursor);
610 void CWsConsole::SetCursorHeight(TInt aPercentage)
612 if (aPercentage == 0)
615 iTextCursor.iHeight = 0;
617 else if (aPercentage == 100)
620 iTextCursor.iHeight = iCharSize.iHeight;
621 iTextCursor.iAscent = iFontAscent;
626 iTextCursor.iHeight = 1;
627 iTextCursor.iAscent = 0;
630 iMux->Group().SetTextCursor(iWin, WritePos(), iTextCursor);
633 void CWsConsole::SetTitle(const TDesC& /*aTitle*/)
637 void CWsConsole::ClearScreen()
640 iCursorPos.iX = iCursorPos.iY = 1;
644 void CWsConsole::ClearToEndOfLine()
646 iMux->Group().CancelTextCursor();
647 TPoint writePos = WritePos();
648 TRect r(writePos.iX, writePos.iY-iFontAscent, iWindowSize.iWidth, writePos.iY+iFontDescent);
651 iMux->Group().SetTextCursor(iWin, WritePos(), iTextCursor);
654 TSize CWsConsole::ScreenSize() const
659 TKeyCode CWsConsole::KeyCode() const
664 TUint CWsConsole::KeyModifiers() const
669 void CWsConsole::DrawNow()
673 iMux->Gc().Activate(iWin);
674 iMux->Gc().BitBlt(TPoint(0,0), iBitmap);
675 iMux->Gc().Deactivate();
677 iMux->Session().Flush();
680 void CWsConsole::Scroll()
682 TRect r(0, iCharSize.iHeight, iWindowSize.iWidth, iSizeInChars.iHeight * iCharSize.iHeight);
684 iBitGc->CopyRect(TPoint(0, -iCharSize.iHeight), r);
686 TRect rect(0, WritePos().iY-iFontAscent, iWindowSize.iWidth, iWindowSize.iHeight);
690 extern "C" EXPORT_C TAny *NewConsole()
692 return new CWsConsole;