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