Diffstat (limited to 'noncore/apps/opie-console/emulation_layer.cpp~') (more/less context) (show whitespace changes)
-rw-r--r-- | noncore/apps/opie-console/emulation_layer.cpp~ | 373 |
1 files changed, 373 insertions, 0 deletions
diff --git a/noncore/apps/opie-console/emulation_layer.cpp~ b/noncore/apps/opie-console/emulation_layer.cpp~ new file mode 100644 index 0000000..e65fd4b --- a/dev/null +++ b/noncore/apps/opie-console/emulation_layer.cpp~ | |||
@@ -0,0 +1,373 @@ | |||
1 | /* -------------------------------------------------------------------------- */ | ||
2 | /* */ | ||
3 | /* [TEmulation.cpp] Terminal Emulation Decoder */ | ||
4 | /* */ | ||
5 | /* -------------------------------------------------------------------------- */ | ||
6 | /* */ | ||
7 | /* Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> */ | ||
8 | /* */ | ||
9 | /* This file is part of Konsole - an X terminal for KDE */ | ||
10 | /* */ | ||
11 | /* -------------------------------------------------------------------------- */ | ||
12 | /* */ | ||
13 | /* Ported Konsole to Qt/Embedded */ | ||
14 | /* */ | ||
15 | /* Copyright (C) 2000 by John Ryland <jryland@trolltech.com> */ | ||
16 | /* */ | ||
17 | /* -------------------------------------------------------------------------- */ | ||
18 | /* ------------------------------------------------------------------------- */ | ||
19 | /* */ | ||
20 | /* Modified to suit opie-console */ | ||
21 | /* */ | ||
22 | /* Copyright (C) 2002 by opie developers <opie@handhelds.org> */ | ||
23 | /* */ | ||
24 | /* ------------------------------------------------------------------------- */ | ||
25 | |||
26 | /*! \class TEmulation | ||
27 | |||
28 | \brief Mediator between TEWidget and TEScreen. | ||
29 | |||
30 | This class is responsible to scan the escapes sequences of the terminal | ||
31 | emulation and to map it to their corresponding semantic complements. | ||
32 | Thus this module knows mainly about decoding escapes sequences and | ||
33 | is a stateless device w.r.t. the semantics. | ||
34 | |||
35 | It is also responsible to refresh the TEWidget by certain rules. | ||
36 | |||
37 | \sa TEWidget \sa TEScreen | ||
38 | |||
39 | \par A note on refreshing | ||
40 | |||
41 | Although the modifications to the current screen image could immediately | ||
42 | be propagated via `TEWidget' to the graphical surface, we have chosen | ||
43 | another way here. | ||
44 | |||
45 | The reason for doing so is twofold. | ||
46 | |||
47 | First, experiments show that directly displaying the operation results | ||
48 | in slowing down the overall performance of emulations. Displaying | ||
49 | individual characters using X11 creates a lot of overhead. | ||
50 | |||
51 | Second, by using the following refreshing method, the screen operations | ||
52 | can be completely separated from the displaying. This greatly simplifies | ||
53 | the programmer's task of coding and maintaining the screen operations, | ||
54 | since one need not worry about differential modifications on the | ||
55 | display affecting the operation of concern. | ||
56 | |||
57 | We use a refreshing algorithm here that has been adoped from rxvt/kvt. | ||
58 | |||
59 | By this, refreshing is driven by a timer, which is (re)started whenever | ||
60 | a new bunch of data to be interpreted by the emulation arives at `onRcvBlock'. | ||
61 | As soon as no more data arrive for `BULK_TIMEOUT' milliseconds, we trigger | ||
62 | refresh. This rule suits both bulk display operation as done by curses as | ||
63 | well as individual characters typed. | ||
64 | (BULK_TIMEOUT < 1000 / max characters received from keyboard per second). | ||
65 | |||
66 | Additionally, we trigger refreshing by newlines comming in to make visual | ||
67 | snapshots of lists as produced by `cat', `ls' and likely programs, thereby | ||
68 | producing the illusion of a permanent and immediate display operation. | ||
69 | |||
70 | As a sort of catch-all needed for cases where none of the above | ||
71 | conditions catch, the screen refresh is also triggered by a count | ||
72 | of incoming bulks (`bulk_incnt'). | ||
73 | */ | ||
74 | |||
75 | /* FIXME | ||
76 | - evtl. the bulk operations could be made more transparent. | ||
77 | */ | ||
78 | |||
79 | #include "emulation_layer.h" | ||
80 | #include "widget.h" | ||
81 | #include "TEScreen.h" | ||
82 | #include <stdio.h> | ||
83 | #include <stdlib.h> | ||
84 | #include <unistd.h> | ||
85 | #include <qkeycode.h> | ||
86 | |||
87 | |||
88 | /* ------------------------------------------------------------------------- */ | ||
89 | /* */ | ||
90 | /* EmulationLayer */ | ||
91 | /* */ | ||
92 | /* ------------------------------------------------------------------------- */ | ||
93 | |||
94 | #define CNTL(c) ((c)-'@') | ||
95 | |||
96 | /*! | ||
97 | */ | ||
98 | |||
99 | EmulationLayer::EmulationLayer(TEWidget* gui) | ||
100 | : decoder((QTextDecoder*)NULL) | ||
101 | { | ||
102 | this->gui = gui; | ||
103 | |||
104 | screen[0] = new TEScreen(gui->Lines(),gui->Columns()); | ||
105 | screen[1] = new TEScreen(gui->Lines(),gui->Columns()); | ||
106 | scr = screen[0]; | ||
107 | |||
108 | bulk_nlcnt = 0; // reset bulk newline counter | ||
109 | bulk_incnt = 0; // reset bulk counter | ||
110 | connected = FALSE; | ||
111 | |||
112 | QObject::connect(&bulk_timer, SIGNAL(timeout()), this, SLOT(showBulk()) ); | ||
113 | QObject::connect(gui,SIGNAL(changedImageSizeSignal(int,int)), | ||
114 | this,SLOT(onImageSizeChange(int,int))); | ||
115 | QObject::connect(gui,SIGNAL(changedHistoryCursor(int)), | ||
116 | this,SLOT(onHistoryCursorChange(int))); | ||
117 | QObject::connect(gui,SIGNAL(keyPressedSignal(QKeyEvent*)), | ||
118 | this,SLOT(onKeyPress(QKeyEvent*))); | ||
119 | QObject::connect(gui,SIGNAL(beginSelectionSignal(const int,const int)), | ||
120 | this,SLOT(onSelectionBegin(const int,const int)) ); | ||
121 | QObject::connect(gui,SIGNAL(extendSelectionSignal(const int,const int)), | ||
122 | this,SLOT(onSelectionExtend(const int,const int)) ); | ||
123 | QObject::connect(gui,SIGNAL(endSelectionSignal(const BOOL)), | ||
124 | this,SLOT(setSelection(const BOOL)) ); | ||
125 | QObject::connect(gui,SIGNAL(clearSelectionSignal()), | ||
126 | this,SLOT(clearSelection()) ); | ||
127 | } | ||
128 | |||
129 | /*! | ||
130 | */ | ||
131 | |||
132 | EmulationLayer::~EmulationLayer() | ||
133 | { | ||
134 | delete screen[0]; | ||
135 | delete screen[1]; | ||
136 | bulk_timer.stop(); | ||
137 | } | ||
138 | |||
139 | /*! change between primary and alternate screen | ||
140 | */ | ||
141 | |||
142 | void EmulationLayer::setScreen(int n) | ||
143 | { | ||
144 | scr = screen[n&1]; | ||
145 | } | ||
146 | |||
147 | void EmulationLayer::setHistory(bool on) | ||
148 | { | ||
149 | screen[0]->setScroll(on); | ||
150 | if (!connected) return; | ||
151 | showBulk(); | ||
152 | } | ||
153 | |||
154 | bool EmulationLayer::history() | ||
155 | { | ||
156 | return screen[0]->hasScroll(); | ||
157 | } | ||
158 | |||
159 | void EmulationLayer::setCodec(int c) | ||
160 | { | ||
161 | //FIXME: check whether we have to free codec | ||
162 | codec = c ? QTextCodec::codecForName("utf8") | ||
163 | : QTextCodec::codecForLocale(); | ||
164 | if (decoder) delete decoder; | ||
165 | decoder = codec->makeDecoder(); | ||
166 | } | ||
167 | |||
168 | void EmulationLayer::setKeytrans(int no) | ||
169 | { | ||
170 | keytrans = KeyTrans::find(no); | ||
171 | } | ||
172 | |||
173 | void EmulationLayer::setKeytrans(const char * no) | ||
174 | { | ||
175 | keytrans = KeyTrans::find(no); | ||
176 | } | ||
177 | |||
178 | // Interpreting Codes --------------------------------------------------------- | ||
179 | |||
180 | /* | ||
181 | This section deals with decoding the incoming character stream. | ||
182 | Decoding means here, that the stream is first seperated into `tokens' | ||
183 | which are then mapped to a `meaning' provided as operations by the | ||
184 | `Screen' class. | ||
185 | */ | ||
186 | |||
187 | /*! | ||
188 | */ | ||
189 | |||
190 | void EmulationLayer::onRcvChar(int c) | ||
191 | // process application unicode input to terminal | ||
192 | // this is a trivial scanner | ||
193 | { | ||
194 | c &= 0xff; | ||
195 | switch (c) | ||
196 | { | ||
197 | case '\b' : scr->BackSpace(); break; | ||
198 | case '\t' : scr->Tabulate(); break; | ||
199 | case '\n' : scr->NewLine(); break; | ||
200 | case '\r' : scr->Return(); break; | ||
201 | case 0x07 : gui->Bell(); break; | ||
202 | default : scr->ShowCharacter(c); break; | ||
203 | }; | ||
204 | } | ||
205 | |||
206 | /* ------------------------------------------------------------------------- */ | ||
207 | /* */ | ||
208 | /* Keyboard Handling */ | ||
209 | /* */ | ||
210 | /* ------------------------------------------------------------------------- */ | ||
211 | |||
212 | /*! | ||
213 | */ | ||
214 | |||
215 | void EmulationLayer::onKeyPress( QKeyEvent* ev ) | ||
216 | { | ||
217 | if (!connected) return; // someone else gets the keys | ||
218 | if (scr->getHistCursor() != scr->getHistLines()); | ||
219 | scr->setHistCursor(scr->getHistLines()); | ||
220 | if (!ev->text().isEmpty()) | ||
221 | { // A block of text | ||
222 | // Note that the text is proper unicode. | ||
223 | // We should do a conversion here, but since this | ||
224 | // routine will never be used, we simply emit plain ascii. | ||
225 | sendString( ev->text().ascii() ); //,ev->text().length()); | ||
226 | } | ||
227 | else if (ev->ascii()>0) | ||
228 | { | ||
229 | QByteArray c = QByteArray( 1 ); | ||
230 | c.at( 0 ) = ev->ascii(); | ||
231 | // ibot: qbytearray is emited not char* | ||
232 | emit sndBlock( (QByteArray) c ); | ||
233 | } | ||
234 | } | ||
235 | |||
236 | // Unblocking, Byte to Unicode translation --------------------------------- -- | ||
237 | |||
238 | /* | ||
239 | We are doing code conversion from locale to unicode first. | ||
240 | */ | ||
241 | |||
242 | void EmulationLayer::onRcvBlock(const QByteArray &s ) | ||
243 | { | ||
244 | bulkStart(); | ||
245 | bulk_incnt += 1; | ||
246 | for (int i = 0; i < s.size(); i++) | ||
247 | { | ||
248 | //TODO: ibot: maybe decoding qbytearray to unicode in io_layer? | ||
249 | QString result = decoder->toUnicode(&s[i],1); | ||
250 | int reslen = result.length(); | ||
251 | for (int j = 0; j < reslen; j++) | ||
252 | onRcvChar(result[j].unicode()); | ||
253 | if (s[i] == '\n') bulkNewline(); | ||
254 | } | ||
255 | bulkEnd(); | ||
256 | } | ||
257 | |||
258 | // Selection --------------------------------------------------------------- -- | ||
259 | |||
260 | void EmulationLayer::onSelectionBegin(const int x, const int y) { | ||
261 | if (!connected) return; | ||
262 | scr->setSelBeginXY(x,y); | ||
263 | showBulk(); | ||
264 | } | ||
265 | |||
266 | void EmulationLayer::onSelectionExtend(const int x, const int y) { | ||
267 | if (!connected) return; | ||
268 | scr->setSelExtentXY(x,y); | ||
269 | showBulk(); | ||
270 | } | ||
271 | |||
272 | void EmulationLayer::setSelection(const BOOL preserve_line_breaks) { | ||
273 | if (!connected) return; | ||
274 | QString t = scr->getSelText(preserve_line_breaks); | ||
275 | if (!t.isNull()) gui->setSelection(t); | ||
276 | } | ||
277 | |||
278 | void EmulationLayer::clearSelection() { | ||
279 | if (!connected) return; | ||
280 | scr->clearSelection(); | ||
281 | showBulk(); | ||
282 | } | ||
283 | |||
284 | // Refreshing -------------------------------------------------------------- -- | ||
285 | |||
286 | #define BULK_TIMEOUT 20 | ||
287 | |||
288 | /*! | ||
289 | called when \n comes in. Evtl. triggers showBulk at endBulk | ||
290 | */ | ||
291 | |||
292 | void EmulationLayer::bulkNewline() | ||
293 | { | ||
294 | bulk_nlcnt += 1; | ||
295 | bulk_incnt = 0; // reset bulk counter since `nl' rule applies | ||
296 | } | ||
297 | |||
298 | /*! | ||
299 | */ | ||
300 | |||
301 | void EmulationLayer::showBulk() | ||
302 | { | ||
303 | bulk_nlcnt = 0; // reset bulk newline counter | ||
304 | bulk_incnt = 0; // reset bulk counter | ||
305 | if (connected) | ||
306 | { | ||
307 | ca* image = scr->getCookedImage(); // get the image | ||
308 | gui->setImage(image, | ||
309 | scr->getLines(), | ||
310 | scr->getColumns()); // actual refresh | ||
311 | free(image); | ||
312 | //FIXME: check that we do not trigger other draw event here. | ||
313 | gui->setScroll(scr->getHistCursor(),scr->getHistLines()); | ||
314 | } | ||
315 | } | ||
316 | |||
317 | void EmulationLayer::bulkStart() | ||
318 | { | ||
319 | if (bulk_timer.isActive()) bulk_timer.stop(); | ||
320 | } | ||
321 | |||
322 | void EmulationLayer::bulkEnd() | ||
323 | { | ||
324 | if ( bulk_nlcnt > gui->Lines() || bulk_incnt > 20 ) | ||
325 | showBulk(); // resets bulk_??cnt to 0, too. | ||
326 | else | ||
327 | bulk_timer.start(BULK_TIMEOUT,TRUE); | ||
328 | } | ||
329 | |||
330 | void EmulationLayer::setConnect(bool c) | ||
331 | { | ||
332 | connected = c; | ||
333 | if ( connected) | ||
334 | { | ||
335 | onImageSizeChange(gui->Lines(), gui->Columns()); | ||
336 | showBulk(); | ||
337 | } | ||
338 | else | ||
339 | { | ||
340 | scr->clearSelection(); | ||
341 | } | ||
342 | } | ||
343 | |||
344 | // --------------------------------------------------------------------------- | ||
345 | |||
346 | /*! triggered by image size change of the TEWidget `gui'. | ||
347 | |||
348 | This event is simply propagated to the attached screens | ||
349 | and to the related serial line. | ||
350 | */ | ||
351 | |||
352 | void EmulationLayer::onImageSizeChange(int lines, int columns) | ||
353 | { | ||
354 | if (!connected) return; | ||
355 | screen[0]->resizeImage(lines,columns); | ||
356 | screen[1]->resizeImage(lines,columns); | ||
357 | showBulk(); | ||
358 | emit ImageSizeChanged(lines,columns); // propagate event to serial line | ||
359 | } | ||
360 | |||
361 | void EmulationLayer::onHistoryCursorChange(int cursor) | ||
362 | { | ||
363 | if (!connected) return; | ||
364 | scr->setHistCursor(cursor); | ||
365 | showBulk(); | ||
366 | } | ||
367 | |||
368 | void EmulationLayer::setColumns(int columns) | ||
369 | { | ||
370 | //FIXME: this goes strange ways. | ||
371 | // Can we put this straight or explain it at least? | ||
372 | emit changeColumns(columns); | ||
373 | } | ||