|
1 // vtc_base.cpp |
|
2 // |
|
3 // Copyright (c) 2009 - 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 |
|
13 #include "vtc_base.h" |
|
14 #include <fshell/common.mmh> |
|
15 |
|
16 _LIT(KIniFileName, "\\system\\console\\vt100.ini"); |
|
17 _LIT(KIniDesciptionFile, "\\resource\\vt100.idf"); |
|
18 _LIT(KAttConsoleSizeDetect, "console_size_detect"); |
|
19 |
|
20 _LIT(KNewLine, "\r\n"); |
|
21 |
|
22 class TOverflowTruncate : public TDes16Overflow |
|
23 { |
|
24 public: |
|
25 virtual void Overflow(TDes16&) {} |
|
26 }; |
|
27 |
|
28 EXPORT_C CVtcConsoleBase::CVtcConsoleBase() |
|
29 { |
|
30 //SetDebug(ETrue); // debug |
|
31 } |
|
32 |
|
33 EXPORT_C CVtcConsoleBase::~CVtcConsoleBase() |
|
34 { |
|
35 delete iIniFile; |
|
36 delete iInputController; |
|
37 delete iOutputController; |
|
38 delete iUnderlyingConsole; // In case of leave during construction, this might still be non-null |
|
39 } |
|
40 |
|
41 EXPORT_C TInt CVtcConsoleBase::Create(const TDesC& aTitle, TSize /*aSize*/) |
|
42 { |
|
43 TRAPD(err, ConstructL(aTitle)); |
|
44 if (err) |
|
45 { |
|
46 TBuf<512> message; |
|
47 message.Format(_L("Failed to create console (%d)."), err); |
|
48 |
|
49 if (iUnderlyingConsole && (LazyConsole::IsConstructed(iUnderlyingConsole) || !LazyConsole::IsLazy(iUnderlyingConsole))) |
|
50 // if we have an underlyconsole, which is either not lazy or is lazy but already constructed, then print the error to it. |
|
51 { |
|
52 iUnderlyingConsole->Write(message); |
|
53 iUnderlyingConsole->Write(KNewLine); |
|
54 } |
|
55 else |
|
56 // else display a dialog |
|
57 { |
|
58 RNotifier notifier; |
|
59 if (notifier.Connect() == KErrNone) |
|
60 { |
|
61 TInt buttonVal; |
|
62 TRequestStatus notifierStatus; |
|
63 notifier.Notify(_L("vt100"), message, _L("OK"), KNullDesC, buttonVal, notifierStatus); |
|
64 User::WaitForRequest(notifierStatus); |
|
65 notifier.Close(); |
|
66 } |
|
67 } |
|
68 } |
|
69 Message(EDebug, _L("VT100 console create completed with err=%d"), err); |
|
70 return err; |
|
71 } |
|
72 |
|
73 EXPORT_C void CVtcConsoleBase::ConstructL(const TDesC&) |
|
74 { |
|
75 iIniFile = LtkUtils::CIniFile::NewL(KIniFileName, KIniDesciptionFile); |
|
76 |
|
77 TSize screenSize(80, 24); // If sizeDetect is not specified, we default to (and only support) 80x24 |
|
78 if (iIniFile->GetBool(KAttConsoleSizeDetect)) |
|
79 { |
|
80 DetectScreenSizeL(screenSize); |
|
81 } |
|
82 iOutputController = CVtConsoleOutputController::NewL(*this, *iIniFile, screenSize); |
|
83 iInputController = CVtConsoleInputController::NewL(*this, *iIniFile); |
|
84 ClearScreen(); |
|
85 delete iUnderlyingConsole; |
|
86 iUnderlyingConsole = NULL; |
|
87 } |
|
88 |
|
89 EXPORT_C TInt CVtcConsoleBase::Extension_(TUint aExtensionId, TAny*& a0, TAny* a1) |
|
90 { |
|
91 if (aExtensionId == ConsoleMode::KSetConsoleModeExtension) |
|
92 { |
|
93 ConsoleMode::TMode mode = (ConsoleMode::TMode)(TInt)a1; |
|
94 iInputController->SetMode(mode); |
|
95 iOutputController->SetMode(mode); |
|
96 return KErrNone; |
|
97 } |
|
98 else if (aExtensionId == UnderlyingConsole::KSetUnderlyingConsoleExtension) |
|
99 { |
|
100 iUnderlyingConsole = (CConsoleBase*)a1; |
|
101 return KErrNone; |
|
102 } |
|
103 else if (aExtensionId == ConsoleAttributes::KSetConsoleAttributesExtension) |
|
104 { |
|
105 ConsoleAttributes::TAttributes* attributes = (ConsoleAttributes::TAttributes*)a1; |
|
106 return iOutputController->SetAttributes(attributes->iAttributes, attributes->iForegroundColor, attributes->iBackgroundColor); |
|
107 } |
|
108 else |
|
109 { |
|
110 return CConsoleBase::Extension_(aExtensionId, a0, a1); |
|
111 } |
|
112 } |
|
113 |
|
114 EXPORT_C void CVtcConsoleBase::Message(TVerbosity aVerbosity, TRefByValue<const TDesC> aFmt, ...) |
|
115 { |
|
116 if (Debug() || (aVerbosity == EInformation) || (aVerbosity == EError)) |
|
117 { |
|
118 TOverflowTruncate overflow; |
|
119 VA_LIST list; |
|
120 VA_START(list, aFmt); |
|
121 TBuf<0x100> buf; |
|
122 buf.AppendFormatList(aFmt, list, &overflow); |
|
123 |
|
124 if (iUnderlyingConsole) |
|
125 { |
|
126 iUnderlyingConsole->Write(buf); |
|
127 iUnderlyingConsole->Write(KNewLine); |
|
128 } |
|
129 else |
|
130 { |
|
131 User::InfoPrint(buf); |
|
132 } |
|
133 } |
|
134 } |
|
135 |
|
136 EXPORT_C TBool CVtcConsoleBase::Debug() |
|
137 { |
|
138 return iDebug; |
|
139 } |
|
140 |
|
141 EXPORT_C void CVtcConsoleBase::SetDebug(TBool aDebug) |
|
142 { |
|
143 iDebug = aDebug; |
|
144 } |
|
145 |
|
146 EXPORT_C void CVtcConsoleBase::Read(TRequestStatus& aStatus) |
|
147 { |
|
148 iInputController->GetKeyPress(iKeyPress, aStatus); |
|
149 } |
|
150 |
|
151 EXPORT_C void CVtcConsoleBase::ReadCancel() |
|
152 { |
|
153 iInputController->CancelGetKeyPress(); |
|
154 } |
|
155 |
|
156 EXPORT_C void CVtcConsoleBase::Write(const TDesC& aDes) |
|
157 { |
|
158 iOutputController->Write(aDes); |
|
159 } |
|
160 |
|
161 EXPORT_C TPoint CVtcConsoleBase::CursorPos() const |
|
162 { |
|
163 TPoint pos; |
|
164 iOutputController->GetCursorPos(pos); |
|
165 return pos; |
|
166 } |
|
167 |
|
168 EXPORT_C void CVtcConsoleBase::SetCursorPosAbs(const TPoint& aPoint) |
|
169 { |
|
170 iOutputController->SetCursorPosAbs(aPoint); |
|
171 } |
|
172 |
|
173 EXPORT_C void CVtcConsoleBase::SetCursorPosRel(const TPoint& aPoint) |
|
174 { |
|
175 iOutputController->SetCursorPosRel(aPoint); |
|
176 } |
|
177 |
|
178 EXPORT_C void CVtcConsoleBase::SetCursorHeight(TInt aPercentage) |
|
179 { |
|
180 iOutputController->SetCursorHeight(aPercentage); |
|
181 } |
|
182 |
|
183 EXPORT_C void CVtcConsoleBase::SetTitle(const TDesC& aDes) |
|
184 { |
|
185 iOutputController->SetTitle(aDes); |
|
186 } |
|
187 |
|
188 EXPORT_C void CVtcConsoleBase::ClearScreen() |
|
189 { |
|
190 iOutputController->ClearScreen(); |
|
191 } |
|
192 |
|
193 EXPORT_C void CVtcConsoleBase::ClearToEndOfLine() |
|
194 { |
|
195 iOutputController->ClearToEndOfLine(); |
|
196 } |
|
197 |
|
198 EXPORT_C TSize CVtcConsoleBase::ScreenSize() const |
|
199 { |
|
200 TSize size; |
|
201 iOutputController->GetScreenSize(size); |
|
202 return size; |
|
203 } |
|
204 |
|
205 EXPORT_C TKeyCode CVtcConsoleBase::KeyCode() const |
|
206 { |
|
207 return iKeyPress.iCode; |
|
208 } |
|
209 |
|
210 EXPORT_C TUint CVtcConsoleBase::KeyModifiers() const |
|
211 { |
|
212 return iKeyPress.iModifiers; |
|
213 } |
|
214 |
|
215 void CVtcConsoleBase::DetectScreenSizeL(TSize& aSize) |
|
216 { |
|
217 TInt err; |
|
218 do |
|
219 { |
|
220 // If we get a KErrCorrupt error during the detect, we restart it from the top |
|
221 // This is because KErrCorrupt is (hopefully) only ever returned as a result of the DSR in ReadCursorPos failing, in which case it is worth retrying. |
|
222 // If it's any other error, the console is actually dead so we should bail |
|
223 err = DoDetectScreenSize(aSize); |
|
224 Message(EDebug, _L("DoDetectScreenSize returned %d"), err); |
|
225 } |
|
226 #ifdef FSHELL_PLATFORM_OPP // Temporary OPP specific change - the drivers for the mid-sized prototype currently complete requests with KErrAbort just before power management sends the device to sleep. |
|
227 while ((err == KErrCorrupt) || (err == KErrAbort)); |
|
228 #else |
|
229 while (err == KErrCorrupt); |
|
230 #endif |
|
231 User::LeaveIfError(err); |
|
232 } |
|
233 |
|
234 TInt CVtcConsoleBase::DoDetectScreenSize(TSize& aSize) |
|
235 { |
|
236 Message(EDebug, _L("Beginning VT100 console size detect")); |
|
237 _LIT8(KSpace, " "); |
|
238 _LIT8(KNewLine, "\r\n"); |
|
239 _LIT8(KResetCursorPosAbs, "\x1b[1;1H"); |
|
240 TInt err = Output(KResetCursorPosAbs); |
|
241 if (err) return err; |
|
242 |
|
243 #ifdef FSHELL_VT100_WORK_AROUND_TERATERM_CURSOR_BUG |
|
244 // Note, at the point where the cursor has just wrap to the next line, TeraTerm reports |
|
245 // the same cursor position has just before is wrapped. When the cursor is moved to the |
|
246 // right again, TeraTerm corrects its reckoning of the cursor position. The following |
|
247 // code works around this bug by keeping track of the previous cursor position and noticing |
|
248 // if it doesn't increment. This is treated as though the cursor had wrapped. |
|
249 |
|
250 TInt previousCursorPosX = -1; |
|
251 for (TInt x = 0; ; ++x) |
|
252 { |
|
253 err = Output(KSpace); |
|
254 if (err) return err; |
|
255 TPoint pos; |
|
256 TInt err = ReadCursorPos(pos); |
|
257 if (err) return err; |
|
258 TInt cursorPosX = pos.iX; |
|
259 if ((cursorPosX == 0) || (cursorPosX == previousCursorPosX)) |
|
260 { |
|
261 aSize.iWidth = x + 1; |
|
262 break; |
|
263 } |
|
264 previousCursorPosX = cursorPosX; |
|
265 } |
|
266 #else |
|
267 for (TInt x = 0; ; ++x) |
|
268 { |
|
269 err = Output(KSpace); |
|
270 if (err) return err; |
|
271 TPoint pos; |
|
272 err = ReadCursorPos(pos); |
|
273 if (err) return err; |
|
274 if (pos.iX == 0) |
|
275 { |
|
276 aSize.iWidth = x + 1; |
|
277 break; |
|
278 } |
|
279 } |
|
280 #endif |
|
281 err = Output(KResetCursorPosAbs); |
|
282 if (err) return err; |
|
283 TInt prevYPos = 0; |
|
284 for (TInt y = 0; ; ++y) |
|
285 { |
|
286 err = Output(KNewLine); |
|
287 if (err) return err; |
|
288 TPoint pos; |
|
289 TInt err = ReadCursorPos(pos); |
|
290 if (err) return err; |
|
291 if (pos.iY == prevYPos) |
|
292 { |
|
293 aSize.iHeight = y; |
|
294 break; |
|
295 } |
|
296 else |
|
297 { |
|
298 prevYPos = y; |
|
299 } |
|
300 } |
|
301 err = Output(KResetCursorPosAbs); |
|
302 Message(EDebug, _L("Completed VT100 console size detect = %dx%d"), aSize.iWidth, aSize.iHeight); |
|
303 return err; |
|
304 } |
|
305 |
|
306 TInt CVtcConsoleBase::ReadCursorPos(TPoint& aPosition) |
|
307 { |
|
308 // Ideally this functionality should be moved into the input and output controllers, but currently |
|
309 // this is problematic because: |
|
310 // |
|
311 // 1) TeraTerm contains bugs in the way it reports cursor position that need to be worked around. |
|
312 // See FSHELL_VT100_WORK_AROUND_TERATERM_CURSOR_BUG. |
|
313 // |
|
314 // 2) The CVtConsoleInputController state machine would need some fairly heavy changes to be able |
|
315 // to cope with ANSI Device Status Report escape sequences. |
|
316 // |
|
317 // 3) The fact that CVtConsoleInputController and CVtConsoleOutputController run in the same thread |
|
318 // means that implementing CVtcConsoleBase::CursorPos (which is synchronous) would be tricky. |
|
319 // Possible solutions are a) using CActiveSchedulerWait (which is almost always a bad idea) and |
|
320 // b) putting CVtConsoleInputController in a separate thread (which would be a fair bit of work, |
|
321 // and we've been there before...). |
|
322 // |
|
323 // For the time being then, vt100.dll has its own console size detection code (a duplication of |
|
324 // the version in iosrv) and uses this function only during construction to initialize the size of |
|
325 // the output controller's cursor tracker. From that point on, the cursor position is always retrieved |
|
326 // via the cursor tracker (the horrible thing that it is). |
|
327 |
|
328 // Note, this function can only safely be used before the input and output controllers are created |
|
329 // because in uses the MConsoleInput and MConsoleOutput interfaces directly. |
|
330 ASSERT((iOutputController == NULL) && (iInputController == NULL)); |
|
331 |
|
332 _LIT8(KDeviceStatusReport, "\x1b[6n"); |
|
333 //Message(EDebug, _L("Sending VT100 DSR")); |
|
334 TInt err = Output(KDeviceStatusReport); |
|
335 if (err < 0) |
|
336 { |
|
337 Message(EDebug, _L("Error %d sending DSR"), err); |
|
338 return err; |
|
339 } |
|
340 |
|
341 TPoint pos; |
|
342 TBuf8<32> buf; |
|
343 TPtr8 ptr(const_cast<TUint8*>(buf.Ptr()), 0, buf.MaxLength()); |
|
344 FOREVER |
|
345 { |
|
346 TRequestStatus status; |
|
347 //Message(EDebug, _L("Waiting for input")); |
|
348 Input(ptr, status); |
|
349 User::WaitForRequest(status); |
|
350 err = status.Int(); |
|
351 if (err < 0) |
|
352 { |
|
353 Message(EDebug, _L("Error getting input %d"), err); |
|
354 return err; |
|
355 } |
|
356 |
|
357 buf.SetLength(buf.Length() + ptr.Length()); |
|
358 ptr.Set(const_cast<TUint8*>(buf.Ptr()) + buf.Length(), 0, buf.MaxLength() - buf.Length()); |
|
359 if (ptr.MaxLength() == 0) return KErrOverflow; // Not sure how this could happen, maybe if the terminal returned really really long but otherwise valid numbers for the x and y pos |
|
360 |
|
361 // The sequence we're looking for is \x1b[nnn;nnnR |
|
362 TLex8 lex(buf); |
|
363 if (lex.Eos()) continue; |
|
364 if (lex.Get() != 0x1b) return KErrCorrupt; |
|
365 if (lex.Eos()) continue; |
|
366 if (lex.Get() != '[') return KErrCorrupt; |
|
367 if (lex.Eos()) continue; |
|
368 err = lex.Val(pos.iY); |
|
369 if (err) return err; |
|
370 if (lex.Eos()) continue; |
|
371 if (lex.Get() != ';') return KErrCorrupt; |
|
372 if (lex.Eos()) continue; |
|
373 err = lex.Val(pos.iX); |
|
374 if (err) return err; |
|
375 if (lex.Eos()) continue; |
|
376 if (lex.Get() != 'R') return KErrCorrupt; |
|
377 // If we reach here, we've successfully read the whole sequence |
|
378 |
|
379 // I assume we subtract one here because the VT100 indexes are 1-based and we use zero-based? -TomS |
|
380 pos.iX--; |
|
381 pos.iY--; |
|
382 aPosition = pos; |
|
383 return KErrNone; |
|
384 } |
|
385 } |
|
386 |
|
387 EXPORT_C TInt CVtcConsoleBase::ReadKeywordValuePair(TLex& aLex, TPtrC& aKeyword, TPtrC& aValue) |
|
388 { |
|
389 TLexMark mark; |
|
390 aLex.SkipSpaceAndMark(mark); |
|
391 while (!aLex.Eos() && !aLex.Peek().IsSpace() && (aLex.Peek() != '=')) |
|
392 { |
|
393 aLex.Get(); |
|
394 } |
|
395 aKeyword.Set(aLex.MarkedToken(mark)); |
|
396 aLex.SkipSpace(); |
|
397 if (aLex.Get() != '=') |
|
398 { |
|
399 return KErrArgument; |
|
400 } |
|
401 aLex.SkipSpaceAndMark(mark); |
|
402 while (!aLex.Eos() && (aLex.Peek() != ',')) |
|
403 { |
|
404 aLex.Get(); |
|
405 } |
|
406 aValue.Set(aLex.MarkedToken(mark)); |
|
407 if (aLex.Peek() == ',') |
|
408 { |
|
409 aLex.Get(); |
|
410 } |
|
411 return KErrNone; |
|
412 } |
|
413 |