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