|
1 // terminalkeyboardcons.cpp |
|
2 // |
|
3 // Copyright (c) 2010 Accenture. All rights reserved. |
|
4 // This component and the accompanying materials are made available |
|
5 // under the terms of the "Eclipse Public License v1.0" |
|
6 // which accompanies this distribution, and is available |
|
7 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 // |
|
9 // Initial Contributors: |
|
10 // Accenture - Initial contribution |
|
11 // |
|
12 #include "terminalkeyboardcons.h" |
|
13 #include <fshell/common.mmh> |
|
14 #include <e32debug.h> |
|
15 #include <fshell/memoryaccess.h> |
|
16 |
|
17 const TInt KTkbdMessageId = 0xD6; |
|
18 // These are the defaults unless overridden by --console-size |
|
19 const TInt KWidth = 80; |
|
20 const TInt KHeight = 24; |
|
21 |
|
22 class CMessageWatcher : public CActive |
|
23 { |
|
24 public: |
|
25 CMessageWatcher(CTerminalKeyboardCons& aConsole) |
|
26 : CActive(CActive::EPriorityStandard), iConsole(aConsole) |
|
27 { |
|
28 CActiveScheduler::Add(this); |
|
29 iConsole.iDriver.RequestMessage(iStatus); |
|
30 SetActive(); |
|
31 } |
|
32 |
|
33 void DoCancel() |
|
34 { |
|
35 iConsole.iDriver.CancelMessage(); |
|
36 } |
|
37 |
|
38 void RunL() |
|
39 { |
|
40 iConsole.MessageReceived(iStatus.Int()); |
|
41 if (iStatus.Int() == KErrNone) |
|
42 { |
|
43 iConsole.iDriver.RequestMessage(iStatus); |
|
44 SetActive(); |
|
45 } |
|
46 } |
|
47 |
|
48 ~CMessageWatcher() |
|
49 { |
|
50 Cancel(); |
|
51 } |
|
52 |
|
53 private: |
|
54 CTerminalKeyboardCons& iConsole; |
|
55 }; |
|
56 |
|
57 |
|
58 CTerminalKeyboardCons::CTerminalKeyboardCons() |
|
59 : iTracker(TSize(KWidth, KHeight), this) |
|
60 { |
|
61 } |
|
62 |
|
63 CTerminalKeyboardCons::~CTerminalKeyboardCons() |
|
64 { |
|
65 CleanupUnderlyingConsole(); |
|
66 delete iWatcher; |
|
67 iDriver.Close(); |
|
68 iTextBuffer.Close(); |
|
69 iMungedTextBuffer.Close(); |
|
70 } |
|
71 |
|
72 TInt CTerminalKeyboardCons::Create(const TDesC& aTitle, TSize aSize) |
|
73 { |
|
74 TRAPD(err, ConstructL(aTitle, aSize)); |
|
75 HandleConsoleCreationError(_L("TerminalKeyboard"), err); |
|
76 return err; |
|
77 } |
|
78 |
|
79 void CTerminalKeyboardCons::ConstructL(const TDesC& aTitle, const TSize& aSize) |
|
80 { |
|
81 if (aTitle == _L("debug")) SetDebug(ETrue); |
|
82 if (aSize.iWidth > 10 && aSize.iHeight > 10) |
|
83 { |
|
84 // Override size now we know it, checking that it's sane-ish - be sure to do this before anything that uses the console size! |
|
85 new (&iTracker) TCursorTracker(aSize, this); |
|
86 } |
|
87 |
|
88 iTextBuffer.CreateMaxL(ScreenSize().iWidth * ScreenSize().iHeight); |
|
89 #ifdef SHOW_TEXTSHELL_BORDERS |
|
90 iMungedTextBuffer.CreateL((ScreenSize().iWidth + 2) * (ScreenSize().iHeight + 2)); |
|
91 #endif |
|
92 |
|
93 TInt err = User::LoadLogicalDevice(KTcLddDriverName); |
|
94 if (err && err != KErrAlreadyExists) |
|
95 { |
|
96 Message(EError, _L("Couldn't load LDD %S: %d"), &KTcLddDriverName, err); |
|
97 User::Leave(err); |
|
98 } |
|
99 |
|
100 // Idiotic driver only accepts connections from processes with nokia vid - like that will stop us |
|
101 TUint originalVid = RProcess().VendorId(); |
|
102 RMemoryAccess memAccess; |
|
103 User::LeaveIfError(memAccess.Open()); |
|
104 TProcessProperties props; |
|
105 props.iVid = 0x101FB657; |
|
106 RProcess me; me.Open(RProcess().Id()); |
|
107 memAccess.SetProcessProperties(me, props); |
|
108 |
|
109 err = iDriver.Open(); |
|
110 |
|
111 props.iVid = originalVid; |
|
112 memAccess.SetProcessProperties(me, props); |
|
113 me.Close(); |
|
114 memAccess.Close(); |
|
115 |
|
116 if (err) |
|
117 { |
|
118 Message(EError, _L("Couldn't open RTcDriver: %d"), err); |
|
119 User::Leave(err); |
|
120 } |
|
121 |
|
122 err = iDriver.Subscribe(KTkbdMessageId); |
|
123 if (err) |
|
124 { |
|
125 Message(EError, _L("Couldn't subscribe: %d"), err); |
|
126 User::Leave(err); |
|
127 } |
|
128 |
|
129 iWatcher = new(ELeave) CMessageWatcher(*this); |
|
130 CleanupUnderlyingConsole(); |
|
131 |
|
132 ClearScreen(); |
|
133 } |
|
134 |
|
135 |
|
136 TInt CTerminalKeyboardCons::Extension_(TUint aExtensionId, TAny*& a0, TAny* a1) |
|
137 { |
|
138 /*if (aExtensionId == ConsoleMode::KSetConsoleModeExtension) |
|
139 { |
|
140 ConsoleMode::TMode mode = (ConsoleMode::TMode)(TInt)a1; |
|
141 iInputController->SetMode(mode); |
|
142 iOutputController->SetMode(mode); |
|
143 return KErrNone; |
|
144 } |
|
145 else*/ |
|
146 /*else if (aExtensionId == ConsoleAttributes::KSetConsoleAttributesExtension) |
|
147 { |
|
148 ConsoleAttributes::TAttributes* attributes = (ConsoleAttributes::TAttributes*)a1; |
|
149 return iOutputController->SetAttributes(attributes->iAttributes, attributes->iForegroundColor, attributes->iBackgroundColor); |
|
150 }*/ |
|
151 |
|
152 TInt ret = MIosrvConsoleHelper_Extension(aExtensionId, a0, a1); |
|
153 if (ret == KErrExtensionNotSupported) ret = CConsoleBase::Extension_(aExtensionId, a0, a1); |
|
154 return ret; |
|
155 } |
|
156 |
|
157 void CTerminalKeyboardCons::MessageReceived(TInt aError) |
|
158 { |
|
159 Message(EDebug, _L("MessageReceived err=%d"), aError); |
|
160 TInt err = aError; |
|
161 if (err == KErrNone) |
|
162 { |
|
163 TPtrC8 data; |
|
164 err = iDriver.GetMessageData(data); |
|
165 if (!err && data.Length() < 1) err = KErrCorrupt; |
|
166 if (!err && !iGotKey) |
|
167 { |
|
168 TUint16 rawkey = data[0]; |
|
169 Message(EDebug, _L("CTerminalKeyboardCons got key %d"), (TInt)rawkey); |
|
170 |
|
171 // Terminal keyboard doesn't support control keys or cursor keys so we use our own two-stage sticky modifier (like unix meta key I think?) |
|
172 if (iBacktickModifierDown) |
|
173 { |
|
174 // Terminal keyboard sends 2,4,6,8 for cursors, so we translate backtick-2 (ie backtick-leftarrow) as meaning left arrow |
|
175 if (rawkey == '2') rawkey = (TUint16)EKeyUpArrow; |
|
176 else if (rawkey == '4') rawkey = (TUint16)EKeyLeftArrow; |
|
177 else if (rawkey == '6') rawkey = (TUint16)EKeyRightArrow; |
|
178 else if (rawkey == '8') rawkey = (TUint16)EKeyDownArrow; |
|
179 else if (rawkey >= 'a' && rawkey <= 'z') |
|
180 { |
|
181 // backtick-c means CTRL-C |
|
182 rawkey = rawkey - 'a'+1; |
|
183 } |
|
184 else if (rawkey == ' ') rawkey = '`'; // backtick-space is how you do a backtick |
|
185 else if (rawkey == '`') rawkey = (TUint16)EKeyEscape; // backtick-backtick is 'escape' |
|
186 else if (rawkey == '1') rawkey = (TUint16)EKeyTab; // backtick-1 is 'tab' |
|
187 iBacktickModifierDown = EFalse; |
|
188 Message(EDebug, _L("Backtick escape converted to %d"), (TInt)rawkey); |
|
189 } |
|
190 else if (rawkey == '`') |
|
191 { |
|
192 iBacktickModifierDown = ETrue; |
|
193 return; |
|
194 } |
|
195 |
|
196 iKeyCode = (TKeyCode)rawkey; // Close enough for now! |
|
197 iGotKey = ETrue; |
|
198 } |
|
199 } |
|
200 if (iClientStatus) |
|
201 { |
|
202 User::RequestComplete(iClientStatus, err); |
|
203 if (err == KErrNone) iGotKey = EFalse; |
|
204 } |
|
205 } |
|
206 |
|
207 void CTerminalKeyboardCons::Read(TRequestStatus& aStatus) |
|
208 { |
|
209 /*if (iClientStatus) |
|
210 { |
|
211 TRequestStatus* stat = &aStatus; |
|
212 User::RequestComplete(stat, KErrInUse); |
|
213 } |
|
214 else*/ |
|
215 { |
|
216 aStatus = KRequestPending; |
|
217 iClientStatus = &aStatus; |
|
218 if (iGotKey) |
|
219 { |
|
220 iGotKey = EFalse; |
|
221 User::RequestComplete(iClientStatus, KErrNone); |
|
222 } |
|
223 } |
|
224 } |
|
225 |
|
226 void CTerminalKeyboardCons::ReadCancel() |
|
227 { |
|
228 if (iClientStatus) |
|
229 { |
|
230 User::RequestComplete(iClientStatus, KErrCancel); |
|
231 } |
|
232 } |
|
233 |
|
234 void CTerminalKeyboardCons::Write(const TDesC& aDes) |
|
235 { |
|
236 for (TInt i = 0; i < aDes.Length(); i++) |
|
237 { |
|
238 TInt textBufPos = CursorPos().iY * ScreenSize().iWidth + CursorPos().iX; |
|
239 TChar ch(aDes[i]); |
|
240 iTracker.WriteChar(ch); |
|
241 if (ch.IsPrint()) iTextBuffer[textBufPos] = aDes[i]; |
|
242 } |
|
243 |
|
244 Update(); |
|
245 } |
|
246 |
|
247 TPoint CTerminalKeyboardCons::CursorPos() const |
|
248 { |
|
249 return iTracker.CursorPos(); |
|
250 } |
|
251 |
|
252 void CTerminalKeyboardCons::SetCursorPosAbs(const TPoint& aPoint) |
|
253 { |
|
254 iTracker.SetCursorPosAbs(aPoint); |
|
255 } |
|
256 |
|
257 void CTerminalKeyboardCons::SetCursorPosRel(const TPoint& aPoint) |
|
258 { |
|
259 iTracker.SetCursorPosRel(aPoint); |
|
260 } |
|
261 |
|
262 void CTerminalKeyboardCons::SetCursorHeight(TInt /*aPercentage*/) |
|
263 { |
|
264 //iOutputController->SetCursorHeight(aPercentage); |
|
265 } |
|
266 |
|
267 void CTerminalKeyboardCons::SetTitle(const TDesC& /*aDes*/) |
|
268 { |
|
269 //iOutputController->SetTitle(aDes); |
|
270 } |
|
271 |
|
272 void CTerminalKeyboardCons::ClearScreen() |
|
273 { |
|
274 SetCursorPosAbs(TPoint(0,0)); |
|
275 iTextBuffer.Fill(' '); |
|
276 Update(); |
|
277 } |
|
278 |
|
279 void CTerminalKeyboardCons::ClearToEndOfLine() |
|
280 { |
|
281 iTextBuffer.MidTPtr(CursorPos().iY * ScreenSize().iWidth + CursorPos().iX, ScreenSize().iWidth - CursorPos().iX).Fill(' '); |
|
282 Update(); |
|
283 } |
|
284 |
|
285 TSize CTerminalKeyboardCons::ScreenSize() const |
|
286 { |
|
287 return iTracker.ConsoleSize(); |
|
288 } |
|
289 |
|
290 TKeyCode CTerminalKeyboardCons::KeyCode() const |
|
291 { |
|
292 return iKeyCode; |
|
293 } |
|
294 |
|
295 TUint CTerminalKeyboardCons::KeyModifiers() const |
|
296 { |
|
297 return iKeyModifiers; |
|
298 } |
|
299 |
|
300 // All these magic vals are as per the DOS 437 codepage http://en.wikipedia.org/wiki/CP437 and not Windows-1252 like the ws_win.cpp source incorrectly states |
|
301 // See also http://en.wikipedia.org/wiki/Box_drawing_characters |
|
302 enum |
|
303 { |
|
304 EBoxHorizontalBar = 0xCD, |
|
305 EBoxVerticalBar = 0xBA, |
|
306 EBoxTopLeft = 0xC9, |
|
307 EBoxTopRight = 0xBB, |
|
308 EBoxBottomLeft = 0xC8, |
|
309 EBoxBottomRight = 0xBC, |
|
310 }; |
|
311 |
|
312 void CTerminalKeyboardCons::Update() |
|
313 { |
|
314 #ifdef SHOW_TEXTSHELL_BORDERS |
|
315 // Update munged buffer |
|
316 const TInt contentWidth = ScreenSize().iWidth; |
|
317 const TInt width = contentWidth + 2; |
|
318 iMungedTextBuffer.SetLength(width); |
|
319 iMungedTextBuffer.Fill(TChar(EBoxHorizontalBar)); |
|
320 iMungedTextBuffer[0] = EBoxTopLeft; |
|
321 iMungedTextBuffer[width-1] = EBoxTopRight; |
|
322 |
|
323 for (TInt i = 0; i < ScreenSize().iHeight; i++) |
|
324 { |
|
325 TPtrC line(iTextBuffer.Mid(i*contentWidth, contentWidth)); |
|
326 iMungedTextBuffer.AppendFormat(_L("%c%S%c"), EBoxVerticalBar, &line, EBoxVerticalBar); |
|
327 } |
|
328 |
|
329 iMungedTextBuffer.Append(EBoxBottomLeft); |
|
330 iMungedTextBuffer.SetLength(iMungedTextBuffer.Length() + contentWidth); |
|
331 iMungedTextBuffer.RightTPtr(contentWidth).Fill(EBoxHorizontalBar); |
|
332 iMungedTextBuffer.Append(EBoxBottomRight); |
|
333 |
|
334 // And send the munged buffer |
|
335 Transmit(iMungedTextBuffer, width, ScreenSize().iHeight + 2); |
|
336 #else |
|
337 // Just send it straight |
|
338 Transmit(iTextBuffer, ScreenSize().iWidth, ScreenSize().iHeight); |
|
339 #endif |
|
340 } |
|
341 |
|
342 void CTerminalKeyboardCons::Transmit(const TDesC& aBuf, TInt aWidth, TInt aHeight) |
|
343 { |
|
344 // This is how terminal keyboard does it - pretty horrible really |
|
345 |
|
346 static const TInt KMaxLen = 200; // This is what terminal keyboard uses - technically you could go as far as a total of 256 |
|
347 TInt numLinesPerPrint = KMaxLen / aWidth; |
|
348 |
|
349 TBuf<256> line; |
|
350 TInt startLine = 0; |
|
351 while (startLine < aHeight) |
|
352 { |
|
353 if (startLine == 0) |
|
354 { |
|
355 // First line has extra info |
|
356 line.Format(_L("#$STIConsole#$%02d%02X%02X"), startLine, aWidth, aHeight); |
|
357 } |
|
358 else |
|
359 { |
|
360 line.Format(_L("#$STIConsole#$%02d"), startLine); |
|
361 } |
|
362 |
|
363 for (TInt i = 0; i < numLinesPerPrint; i++) |
|
364 { |
|
365 TPtrC theContents(aBuf.Mid((startLine+i)*aWidth, aWidth)); |
|
366 //RDebug::Printf("line len=%d theContents len=%d", line.Length(), theContents.Length()); |
|
367 line.Append(theContents); |
|
368 } |
|
369 |
|
370 RDebug::Print(line); |
|
371 startLine += numLinesPerPrint; |
|
372 } |
|
373 } |
|
374 |
|
375 void CTerminalKeyboardCons::ConsoleScrolled(TInt aNumberOfLines) |
|
376 { |
|
377 TInt numChars = Abs(aNumberOfLines) * ScreenSize().iWidth; |
|
378 if (aNumberOfLines > 0) |
|
379 { |
|
380 iTextBuffer.Delete(0, numChars); |
|
381 iTextBuffer.AppendFill(' ', numChars); |
|
382 } |
|
383 else if (aNumberOfLines < 0) |
|
384 { |
|
385 iTextBuffer.SetLength(iTextBuffer.Length() - numChars); |
|
386 while (numChars--) iTextBuffer.Insert(0, _L(" ")); |
|
387 } |
|
388 } |
|
389 |
|
390 extern "C" EXPORT_C TAny* NewConsole() |
|
391 { |
|
392 return new CTerminalKeyboardCons; |
|
393 } |