summaryrefslogtreecommitdiff
path: root/core/apps/embeddedkonsole/TEmulation.cpp
Unidiff
Diffstat (limited to 'core/apps/embeddedkonsole/TEmulation.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--core/apps/embeddedkonsole/TEmulation.cpp363
1 files changed, 363 insertions, 0 deletions
diff --git a/core/apps/embeddedkonsole/TEmulation.cpp b/core/apps/embeddedkonsole/TEmulation.cpp
new file mode 100644
index 0000000..6f3ad32
--- a/dev/null
+++ b/core/apps/embeddedkonsole/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
92TEmulation::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
125TEmulation::~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
135void TEmulation::setScreen(int n)
136{
137 scr = screen[n&1];
138}
139
140void TEmulation::setHistory(bool on)
141{
142 screen[0]->setScroll(on);
143 if (!connected) return;
144 showBulk();
145}
146
147bool TEmulation::history()
148{
149 return screen[0]->hasScroll();
150}
151
152void 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
161void TEmulation::setKeytrans(int no)
162{
163 keytrans = KeyTrans::find(no);
164}
165
166void 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
183void 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
208void 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
233void 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
250void TEmulation::onSelectionBegin(const int x, const int y) {
251 if (!connected) return;
252 scr->setSelBeginXY(x,y);
253 showBulk();
254}
255
256void TEmulation::onSelectionExtend(const int x, const int y) {
257 if (!connected) return;
258 scr->setSelExtentXY(x,y);
259 showBulk();
260}
261
262void 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
268void 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
282void TEmulation::bulkNewline()
283{
284 bulk_nlcnt += 1;
285 bulk_incnt = 0; // reset bulk counter since `nl' rule applies
286}
287
288/*!
289*/
290
291void 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
307void TEmulation::bulkStart()
308{
309 if (bulk_timer.isActive()) bulk_timer.stop();
310}
311
312void 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
320void 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
342void 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
351void TEmulation::onHistoryCursorChange(int cursor)
352{
353 if (!connected) return;
354 scr->setHistCursor(cursor);
355 showBulk();
356}
357
358void 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}