4 * ANSI terminal emulation class.
6 * Portable Windows Library
8 * Copyright (c) 1993-1998 Equivalence Pty. Ltd.
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
25 * All Rights Reserved.
27 * Contributor(s): ______________________________________.
30 * Revision 1.41 2001/09/10 02:51:23 robertj
31 * Major change to fix problem with error codes being corrupted in a
32 * PChannel when have simultaneous reads and writes in threads.
34 * Revision 1.40 1998/12/20 09:14:37 robertj
35 * Added pragma implementation for GNU compiler.
37 * Revision 1.39 1998/12/12 00:44:13 robertj
38 * Added capability to get text lines out of scroll back buffer.
40 * Revision 1.38 1998/11/30 04:52:12 robertj
41 * New directory structure
43 * Revision 1.37 1998/10/16 11:08:14 robertj
46 * Revision 1.36 1998/09/23 06:29:33 robertj
47 * Added open source copyright license.
49 * Revision 1.35 1998/09/18 13:09:33 robertj
50 * Fixed clearing of very bottom of window.
52 * Revision 1.34 1998/09/17 04:24:45 robertj
53 * Fixed update problems requiring screen refresh to see output text.
55 * Revision 1.33 1998/09/04 06:57:02 robertj
56 * Fixed crash if window was realy, really wide.
58 * Revision 1.32 1997/04/27 05:50:20 robertj
61 * Revision 1.31 1996/12/21 07:01:35 robertj
62 * Fixed flush on close problem in ANSI terminal stream.
64 * Revision 1.30 1996/06/03 10:07:31 robertj
65 * Fixed bug in auto scroll to cursor position on output.
66 * Fixed bug in DEL character processing (no screen update).
68 * Revision 1.29 1996/05/15 10:14:40 robertj
69 * Rewrite of terminal to make descendant only require single character output function.
71 * Revision 1.28 1996/04/30 12:33:45 robertj
72 * Changed "inPixels" boolean to enum for three coordinate systems.
74 * Revision 1.27 1996/04/29 12:18:24 robertj
75 * Added local echo capability to terminal.
77 * Revision 1.26 1995/12/10 11:36:16 robertj
78 * Fixed bug in display of window bigger than virtual screen.
79 * Fixed warning in GNU compiler.
81 * Revision 1.25 1995/11/20 11:04:09 robertj
82 * Fixed bugs in non-adjustable rows and columns mode.
83 * Implemented horizontal scrolling.
85 * Revision 1.24 1995/11/09 12:19:50 robertj
86 * Removed GNU warning.
88 * Revision 1.23 1995/08/24 12:35:51 robertj
89 * Implemented copy/paste.
91 * Revision 1.22 1995/07/31 12:15:47 robertj
92 * Removed PContainer from PChannel ancestor.
94 * Revision 1.21 1995/07/02 01:20:58 robertj
95 * Removed OnDoubleClick() and added BOOL to OnMouseDown() for double click.
97 * Revision 1.20 1995/06/17 11:11:09 robertj
98 * Changed OnKeyDown to return BOOL indicating OnKeyInput is to be used.
100 * Revision 1.19 1995/06/04 08:41:42 robertj
101 * Some minor speedups.
103 * Revision 1.18 1995/04/25 11:28:06 robertj
104 * Fixed Borland compiler warnings.
106 * Revision 1.17 1995/04/01 08:30:08 robertj
107 * Changed default end of line wrap option.
108 * Fixed loss of focus problem.
110 * Revision 1.16 1995/01/07 04:39:44 robertj
111 * Redesigned font enumeration code and changed font styles.
113 * Revision 1.15 1995/01/06 10:43:53 robertj
114 * Changed PRealFont usage from pointer to reference.
116 * Revision 1.14 1994/12/05 11:20:22 robertj
117 * Removed Normalise() function from PRect as all rectangles should be inherently normal.
119 * Revision 1.13 1994/11/28 12:35:42 robertj
120 * Fixed closure of channel not clearing the keyboard stream.
121 * Moved saved cursor position variables to PAnsiTerminal.
123 * Revision 1.12 1994/10/30 11:52:02 robertj
124 * Fixed bug in auto scrolling vertical scroll bar position.
126 * Revision 1.11 1994/10/23 03:40:23 robertj
127 * Fixed scrolling wrong direction.
128 * Added ReturnOnLineFeed option.
129 * Added capability of setting cursor type.
130 * Improved the channel.
132 * Revision 1.10 1994/09/25 10:48:32 robertj
133 * Added boundary condition tests.
135 * Revision 1.9 1994/08/21 23:43:02 robertj
138 * Revision 1.8 1994/08/01 03:41:24 robertj
139 * Use of PNEW instead of new for heap debugging. Need undef for Unix end.
141 * Revision 1.7 1994/07/27 05:58:07 robertj
144 * Revision 1.6 1994/07/25 03:38:07 robertj
145 * Changed variables to int for better portability.
147 * Revision 1.5 1994/07/21 12:33:49 robertj
148 * Moved cooperative threads to common.
150 * Revision 1.4 1994/07/17 10:46:06 robertj
151 * Major redesign to get list instead of array for line data.
153 * Revision 1.3 1994/06/25 11:55:15 robertj
154 * Unix version synchronisation.
156 * Revision 1.2 1994/04/20 12:17:44 robertj
157 * some implementation
159 * Revision 1.1 1994/04/01 14:27:01 robertj
164 #pragma implementation "ansiterm.h"
168 #include <pwclib/ansiterm.h>
174 PTerminal::PTerminal(PScroller
* parent
, ostream
* kbd
)
175 : PScrollable(parent
)
178 PScrollable::SetFont(PFont("Courier", 10));
179 SetCursor(PCursor(PSTD_ID_CURSOR_IBEAM
));
180 variableRowColumns
= TRUE
;
182 lineFeedOnReturn
= FALSE
;
183 returnOnLineFeed
= FALSE
;
189 columnModeSelection
= FALSE
;
197 PTerminal::Row::Row(Attribute attribute
)
199 for (PINDEX i
= 0; i
< MaxColumns
; i
++)
200 attrib
[i
] = attribute
;
201 memset(ascii
, ' ', sizeof(ascii
));
205 void PTerminal::Reset()
207 cellWidth
= font
.GetAvgWidth(TRUE
);
208 cellHeight
= font
.GetHeight(TRUE
);
209 SetScaleFactor(cellWidth
, cellHeight
);
211 cursorRow
= cursorColumn
= 0;
212 caret
.SetDimensions(blockCursor
? cellWidth
: 1, cellHeight
);
213 SetCaretPos(0, 0, PixelCoords
);
215 PDim dim
= GetDimensions(PixelCoords
);
216 columnsVisible
= dim
.Width()/cellWidth
;
217 if (columnsVisible
> Row::MaxColumns
)
218 columnsVisible
= Row::MaxColumns
;
219 rowsVisible
= dim
.Height()/cellHeight
;
221 if (variableRowColumns
) {
222 columns
= columnsVisible
> 1 ? columnsVisible
: 2;
223 rows
= rowsVisible
> 1 ? rowsVisible
: 2;
229 attribute
.bold
= FALSE
;
230 attribute
.underline
= FALSE
;
231 attribute
.blink
= FALSE
;
232 attribute
.inverse
= FALSE
;
233 attribute
.selected
= FALSE
;
234 attribute
.fgColor
= 8; // Window fg colour
235 attribute
.bkColor
= 9; // Window bk colour
238 for (PINDEX i
= 0; i
< rows
; i
++)
239 rowData
.Append(new Row(attribute
));
241 SetScrollRange(PRect(0, 0, columns
, rows
));
243 anchor
= selection
= 0;
249 void PTerminal::SetFont(const PFont
& newFont
, BOOL toChildren
)
251 PScrollable::SetFont(newFont
, toChildren
);
253 cellWidth
= font
.GetAvgWidth(TRUE
);
254 cellHeight
= font
.GetHeight(TRUE
);
255 SetScaleFactor(cellWidth
, cellHeight
);
257 caret
.SetDimensions(blockCursor
? cellWidth
: 1, cellHeight
);
259 SetDimensions(GetDimensions(PixelCoords
), PixelCoords
);
264 void PTerminal::_SetDimensions(PDIMENSION width
, PDIMENSION height
,
265 CoordinateSystem coords
)
267 PScrollable::_SetDimensions(width
, height
, coords
);
269 PDim dim
= GetDimensions(PixelCoords
);
270 columnsVisible
= dim
.Width()/cellWidth
;
271 if (columnsVisible
> Row::MaxColumns
)
272 columnsVisible
= Row::MaxColumns
;
273 rowsVisible
= dim
.Height()/cellHeight
;
275 if (variableRowColumns
) {
276 variableRowColumns
= FALSE
;
277 SetColumns(columnsVisible
);
278 SetRows(rowsVisible
);
279 variableRowColumns
= TRUE
;
282 SetScrollRange(PRect(0, 0, columns
, rowData
.GetSize()));
286 void PTerminal::Row::AdjustSelection(PINDEX start
, PINDEX finish
, BOOL select
)
288 for (PINDEX col
= start
; col
< finish
; col
++)
289 attrib
[col
].selected
= select
;
293 void PTerminal::AdjustSelection(PCanvas
& canvas
,
294 PINDEX start
, PINDEX finish
, BOOL select
)
299 if (start
> finish
) {
305 PINDEX firstRow
= start
/Row::MaxColumns
;
306 PINDEX lastRow
= finish
/Row::MaxColumns
;
307 PINDEX firstCol
= start
%Row::MaxColumns
;
308 PINDEX lastCol
= finish
%Row::MaxColumns
;
310 if (columnModeSelection
) {
311 if (firstCol
> lastCol
) {
312 PINDEX tmp
= lastCol
;
316 for (PINDEX row
= firstRow
; row
<= lastRow
; row
++) {
317 rowData
[row
].AdjustSelection(0, Row::MaxColumns
, FALSE
);
319 rowData
[row
].AdjustSelection(firstCol
, lastCol
, select
);
320 rowData
[row
].Draw(this, canvas
, row
- origin
.Y(), 0);
324 for (PINDEX row
= firstRow
; row
<= lastRow
; row
++) {
325 rowData
[row
].AdjustSelection(firstCol
,
326 row
< lastRow
? (int)Row::MaxColumns
: lastCol
, select
);
327 rowData
[row
].Draw(this, canvas
, row
- origin
.Y(), 0);
334 void PTerminal::OnMouseTrack(PCanvas
* canvas
,
335 const PPoint
& where
, BOOL lastTrack
)
337 PINDEX pos
= origin
.Y()*Row::MaxColumns
+ origin
.X();
339 pos
+= (where
.Y()/cellHeight
)*Row::MaxColumns
;
341 pos
+= (where
.X() + cellWidth
/2)/cellWidth
;
342 if (pos
>= rowData
.GetSize()*Row::MaxColumns
)
343 pos
= rowData
.GetSize()*Row::MaxColumns
- 1;
345 if (pos
== selection
)
348 if ((pos
>= anchor
&& selection
< anchor
) ||
349 (pos
<= anchor
&& selection
> anchor
)) {
350 AdjustSelection(*canvas
, anchor
, selection
, FALSE
);
354 if (columnModeSelection
) {
355 if ((pos
> anchor
&& pos
< selection
) ||
356 (pos
< anchor
&& pos
> selection
))
357 AdjustSelection(*canvas
, selection
, pos
, FALSE
);
358 AdjustSelection(*canvas
, anchor
, pos
, TRUE
);
361 AdjustSelection(*canvas
, pos
, selection
,
362 (pos
> anchor
&& pos
> selection
) || (pos
< anchor
&& pos
< selection
));
367 UpdateCommandSources();
371 void PTerminal::OnMouseDown(PKeyCode button
, const PPoint
& where
, BOOL
)
375 PCanvas
* canvas
= StartMouseTrack(TRUE
);
377 PINDEX pos
= (origin
.Y() + where
.Y()/cellHeight
)*Row::MaxColumns
+
378 origin
.X() + (where
.X()+cellWidth
/2)/cellWidth
;
379 if (pos
>= rowData
.GetSize()*Row::MaxColumns
)
380 pos
= rowData
.GetSize()*Row::MaxColumns
- 1;
382 if (HasSelection() && button
.IsModifier(PKeyCode::Shift
)) {
383 if ((anchor
> selection
&& pos
> anchor
) ||
384 (anchor
< selection
&& pos
< anchor
)) {
389 OnMouseTrack(canvas
, where
, FALSE
);
392 AdjustSelection(*canvas
, anchor
, selection
, FALSE
);
393 anchor
= selection
= pos
;
394 columnModeSelection
= button
.IsModifier(PKeyCode::Control
);
399 void PTerminal::OnKeyInput(const PString
& str
)
401 if (keyboard
!= NULL
) {
410 void PTerminal::Row::Draw(PTerminal
* term
,
411 PCanvas
& canvas
, PINDEX line
, PINDEX lastCol
)
413 static PColour colour
[10] = {
422 PApplication::Current().GetWindowFgColour(),
423 PApplication::Current().GetWindowBkColour()
425 PDIMENSION origins
[MaxColumns
];
427 for (int i
= 0; i
< MaxColumns
; i
++)
428 origins
[i
] = term
->cellWidth
;
430 WORD lastAttr
= *(WORD
*)&attrib
[0];
431 PINDEX col
= lastCol
;
432 while (lastCol
< term
->columnsVisible
) {
434 if (lastAttr
!= *(WORD
*)&attrib
[col
] || col
>= term
->columnsVisible
) {
436 Attribute attr
= attrib
[lastCol
];
437 if (attr
.inverse
^ attr
.selected
) {
438 canvas
.SetTextFgColour(colour
[attr
.bkColor
]);
439 canvas
.SetTextBkColour(colour
[attr
.fgColor
]);
442 canvas
.SetTextFgColour(colour
[attr
.fgColor
]);
443 canvas
.SetTextBkColour(colour
[attr
.bkColor
]);
445 WORD styles
= PFont::Regular
;
447 styles
|= PFont::Bold
;
449 styles
|= PFont::Underline
;
450 PFont
newFont(term
->font
.GetFacename(), term
->font
.GetSize(), styles
);
451 canvas
.SetFont(newFont
);
452 canvas
.DrawTextLine(lastCol
*term
->cellWidth
,
453 line
*term
->cellHeight
, &ascii
[lastCol
], origins
, col
- lastCol
);
455 lastAttr
= *(WORD
*)&attrib
[col
];
462 void PTerminal::OnRedraw(PCanvas
& canvas
)
466 canvas
.SetMappingRect(canvas
.GetViewportRect());
467 canvas
.SetOrigin(PPoint(origin
.X()*scaleFactor
.X(), 0));
469 PINDEX lastRow
= PMIN(rowData
.GetSize(), rowsVisible
);
470 PINDEX bottom
= origin
.Y() + lastRow
;
472 canvas
.SetPenFgColour(GetBackgroundColour());
473 canvas
.DrawRect(0, lastRow
*cellHeight
, 30000, 30000);
475 for (PINDEX line
= 1; line
<= lastRow
; line
++)
476 rowData
[bottom
- line
].Draw(this, canvas
, lastRow
- line
, 0);
482 PString
PTerminal::GetRow(PINDEX idx
) const
484 if (idx
>= GetTotalRows())
487 return rowData
[idx
].GetText();
491 BOOL
PTerminal::HasSelection() const
493 return anchor
!= selection
;
497 PString
PTerminal::Row::GetText() const
499 PINDEX len
= sizeof(ascii
)-1;
500 while (len
> 0 && ascii
[len
] == ' ')
503 return PString(ascii
, len
);
507 PString
PTerminal::Row::GetSelection() const
510 while (firstCol
< MaxColumns
&& !attrib
[firstCol
].selected
)
513 PINDEX lastCol
= MaxColumns
- 1;
514 while (lastCol
> firstCol
&& (!attrib
[lastCol
].selected
|| ascii
[lastCol
] == ' '))
517 if (lastCol
>= firstCol
)
518 return PString(&ascii
[firstCol
], lastCol
- firstCol
+ 1);
524 PString
PTerminal::GetSelection() const
528 if (HasSelection()) {
529 PINDEX row
= PMIN(anchor
, selection
)/Row::MaxColumns
;
530 PINDEX finish
= PMAX(anchor
, selection
)/Row::MaxColumns
;
531 while (row
<= finish
)
532 str
+= rowData
[row
++].GetSelection() + "\r\n";
539 void PTerminal::SetSelection(PINDEX startRow
, PINDEX startColumn
,
540 PINDEX finishRow
, PINDEX finishColumn
)
542 anchor
= startRow
*Row::MaxColumns
+ startColumn
;
543 selection
= finishRow
*Row::MaxColumns
+ finishColumn
;
548 void PTerminal::SetKeyboardStream(ostream
* kbd
)
554 void PTerminal::SetMaxSavedRows(PINDEX newRows
)
556 maxSavedRows
= newRows
;
557 if (maxSavedRows
!= 0 && rowData
.GetSize() > maxSavedRows
+rows
)
558 rowData
.SetSize(maxSavedRows
+rows
);
562 void PTerminal::SetVariableRowColumns(BOOL b
)
564 variableRowColumns
= b
;
565 if (variableRowColumns
)
566 SetDimensions(GetDimensions(PixelCoords
), PixelCoords
);
568 SetRows(rowsVisible
> 0 ? rowsVisible
: 25);
569 SetColumns(columnsVisible
> 1 ? columnsVisible
: (int)Row::MaxColumns
);
574 void PTerminal::SetRows(PINDEX newRows
)
576 if (variableRowColumns
) {
577 SetDimensions(GetDimensions(PixelCoords
), PixelCoords
);
584 if (scrollTop
>= newRows
)
585 scrollTop
= newRows
- 1;
586 if (scrollBottom
== rows
|| scrollBottom
> newRows
)
587 scrollBottom
= newRows
;
590 AdjustCaretPosition((int)newRows
- (int)rows
, 0);
592 for (PINDEX i
= rows
; i
< newRows
; i
++)
593 rowData
.Append(new Row(attribute
));
594 SetScrollRange(PRect(0, 0, columns
, rowData
.GetSize()));
601 void PTerminal::SetColumns(PINDEX newColumns
)
603 if (variableRowColumns
) {
604 SetDimensions(GetDimensions(PixelCoords
), PixelCoords
);
610 else if (newColumns
> Row::MaxColumns
)
611 newColumns
= Row::MaxColumns
;
613 if (columns
!= newColumns
)
614 SetScrollRange(PRect(0, 0, columns
, rowData
.GetSize()));
615 columns
= newColumns
;
619 void PTerminal::AdjustCaretPosition(int dLine
, int dCol
)
621 cursorColumn
+= dCol
;
622 if ((int)cursorColumn
< 0)
624 else if (cursorColumn
>= columns
)
625 cursorColumn
= columns
-1;
628 if ((int)cursorRow
< (int)scrollTop
) // Need signed compare here
629 cursorRow
= scrollTop
;
630 else if (cursorRow
>= scrollBottom
)
631 cursorRow
= scrollBottom
-1;
633 SetCaretPos(cursorColumn
*cellWidth
,
634 blockCursor
? cursorRow
*cellHeight
: (cursorRow
+1)*cellHeight
-1,
639 void PTerminal::MoveCaretPosition(PDrawCanvas
& canvas
, int dLine
, int dCol
)
641 PINDEX absRow
= rowData
.GetSize() - rows
+ cursorRow
;
643 rowData
[absRow
].Draw(this, canvas
, absRow
- origin
.Y(), updateColumn
);
644 AdjustCaretPosition(dLine
, dCol
);
645 updateColumn
= cursorColumn
;
647 if (absRow
< (PINDEX
)origin
.Y() ||
648 absRow
>= (PINDEX
)origin
.Y()+rowsVisible
||
649 cursorColumn
< (PINDEX
)origin
.X() ||
650 cursorColumn
>= (PINDEX
)origin
.X()+columnsVisible
)
651 ScrollTo(cursorColumn
-columnsVisible
, cursorRow
);
655 void PTerminal::SetBlockCursor(BOOL b
)
658 caret
.SetDimensions(blockCursor
? cellWidth
: 1, cellHeight
);
662 PTerminal::Row
& PTerminal::GetCursorRow()
664 return rowData
[rowData
.GetSize() - rows
+ cursorRow
];
668 void PTerminal::ScrollLines(PDrawCanvas
& canvas
,
669 PINDEX top
, PINDEX bottom
, int lines
)
671 PAssert(lines
!= 0 && bottom
> top
, PInvalidParameter
);
674 Update(); // Make sure interactor is up to date (fully redrawn)
675 MoveCaretPosition(canvas
, 0, 0);
677 PINDEX totalRows
= rowData
.GetSize();
678 int topRow
= totalRows
- rows
+ top
;
679 int bottomRow
= totalRows
- rows
+ bottom
;
680 int absLines
= PABS(lines
);
681 if (absLines
> (int)(bottom
- top
))
682 absLines
= bottom
- top
;
684 if (lines
> 0 && top
== 0) {
685 while (absLines
-- > 0)
686 rowData
.InsertAt(bottomRow
, new Row(attribute
));
687 if (maxSavedRows
!= 0 && rowData
.GetSize() > maxSavedRows
+rows
)
688 rowData
.SetSize(maxSavedRows
+rows
);
689 if (rowData
.GetSize() != totalRows
) {
690 SetScrollRange(PRect(0, 0, columns
, rowData
.GetSize()));
691 ScrollTo(0, rowData
.GetSize());
695 PRect
scrollRect(0, top
*cellHeight
, 30000, (bottom
- top
)*cellHeight
);
696 PDIMENSION scrollAmount
= absLines
*cellHeight
;
697 BOOL doScroll
= scrollAmount
< scrollRect
.Height();
699 while (absLines
-- > 0) {
700 rowData
.InsertAt(bottomRow
, new Row(attribute
));
701 rowData
.RemoveAt(topRow
);
704 PDrawCanvas
canvas(this, TRUE
);
705 canvas
.Scroll(0, -(PORDINATE
)scrollAmount
, scrollRect
);
706 scrollRect
.SetTop(scrollRect
.Bottom() - scrollAmount
);
710 while (absLines
-- > 0) {
711 rowData
.RemoveAt(bottomRow
-1);
712 rowData
.InsertAt(topRow
, new Row(attribute
));
715 PDrawCanvas
canvas(this, TRUE
);
716 canvas
.Scroll(0, scrollAmount
, scrollRect
);
717 scrollRect
.SetHeight(scrollAmount
);
720 Invalidate(scrollRect
, PixelCoords
);
727 void PTerminal::Row::Scroll(PTerminal
* term
, PCanvas
& canvas
,
728 PINDEX line
, PINDEX left
, PINDEX right
, int columns
)
730 PAssert(columns
!= 0 && right
> left
, PInvalidParameter
);
732 if (right
>= MaxColumns
)
733 right
= MaxColumns
-1;
735 PINDEX absCols
= PABS(columns
);
736 if (absCols
> right
- left
)
737 absCols
= right
- left
;
740 memmove(&attrib
[left
+absCols
], &attrib
[left
], (right
-left
-absCols
)*sizeof(attrib
[0]));
741 for (PINDEX i
= 0; i
< absCols
; i
++)
742 attrib
[left
+i
] = term
->attribute
;
743 memmove(&ascii
[left
+absCols
], &ascii
[left
], (right
-left
-absCols
)*sizeof(ascii
[0]));
744 memset(&ascii
[left
], ' ', absCols
*sizeof(ascii
[0]));
747 memmove(&attrib
[left
], &attrib
[left
+absCols
], (right
-left
-absCols
)*sizeof(attrib
[0]));
748 for (PINDEX i
= 0; i
< absCols
; i
++)
749 attrib
[right
-absCols
+i
] = term
->attribute
;
750 memmove(&ascii
[left
], &ascii
[left
+absCols
], (right
-left
-absCols
)*sizeof(ascii
[0]));
751 memset(&ascii
[right
-absCols
], ' ', absCols
*sizeof(ascii
[0]));
754 Draw(term
, canvas
, line
, left
);
758 void PTerminal::Write(const char * buf
, PINDEX len
)
760 if (len
<= 0 || (len
== 1 && *buf
== '\0'))
763 PDrawCanvas
updateCanvas(this, TRUE
);
764 updateCanvas
.SetOrigin(PPoint(origin
.X()*scaleFactor
.X(), 0));
767 MoveCaretPosition(updateCanvas
, 0, 0);
769 for (PINDEX i
= 0; i
< len
; i
++)
770 ProcessCharacter(updateCanvas
, *buf
++);
772 MoveCaretPosition(updateCanvas
, 0, 0);
777 void PTerminal::ProcessCharacter(PDrawCanvas
& canvas
, char ch
)
785 MoveCaretPosition(canvas
, 0, -1);
789 MoveCaretPosition(canvas
, -1, 0);
793 MoveCaretPosition(canvas
, 0, 1);
797 MoveCaretPosition(canvas
, 0, 8 - cursorColumn
%8);
801 MoveCaretPosition(canvas
, 0, -(int)cursorColumn
);
802 if (!lineFeedOnReturn
)
804 // Else do line feed case
807 if (cursorRow
< scrollBottom
-1)
808 MoveCaretPosition(canvas
, 1, 0);
810 ScrollLines(canvas
, scrollTop
, scrollBottom
, 1);
811 if (returnOnLineFeed
)
812 MoveCaretPosition(canvas
, 0, -(int)cursorColumn
);
816 if (cursorColumn
> 0) {
817 MoveCaretPosition(canvas
, 0, -1);
818 GetCursorRow().SetCell(cursorColumn
, ' ', attribute
);
823 GetCursorRow().SetCell(cursorColumn
, ch
, attribute
);
825 if (cursorColumn
< columns
-1)
826 AdjustCaretPosition(0, 1);
828 ProcessCharacter(canvas
, '\r');
829 ProcessCharacter(canvas
, '\n');
836 ///////////////////////////////////////////////////////////////////////////////
838 PAnsiTerminal::PAnsiTerminal(PScroller
* parent
, ostream
* kbd
)
839 : PTerminal(parent
, kbd
)
845 void PAnsiTerminal::Reset()
849 inEscapeSequence
= FALSE
;
850 savedCursorRow
= savedCursorColumn
= 0;
854 BOOL
PAnsiTerminal::OnKeyDown(PKeyCode key
, unsigned repeat
)
856 static const struct {
857 PKeyCode::Value value
;
858 const char * sequence
;
860 { PKeyCode::F1
, "\033OP" },
861 { PKeyCode::F2
, "\033OQ" },
862 { PKeyCode::F3
, "\033OR" },
863 { PKeyCode::F4
, "\033OS" },
864 { PKeyCode::Home
, "\033[H" },
865 { PKeyCode::Separator
,"\033Ol" },
866 { PKeyCode::Subtract
, "\033Om" },
867 { PKeyCode::Decimal
, "\033On" },
868 { PKeyCode::KP0
, "\033Op" },
869 { PKeyCode::KP1
, "\033Oq" },
870 { PKeyCode::KP2
, "\033Or" },
871 { PKeyCode::KP3
, "\033Os" },
872 { PKeyCode::KP4
, "\033Ot" },
873 { PKeyCode::KP5
, "\033Ou" },
874 { PKeyCode::KP6
, "\033Ov" },
875 { PKeyCode::KP7
, "\033Ow" },
876 { PKeyCode::KP8
, "\033Ox" },
877 { PKeyCode::KP9
, "\033Oy" },
878 { PKeyCode::Up
, "\033[A" },
879 { PKeyCode::Down
, "\033[B" },
880 { PKeyCode::Left
, "\033[D" },
881 { PKeyCode::Right
, "\033[C" },
882 { PKeyCode::Delete
, "\177" },
885 if (keyboard
!= NULL
&& key
.GetModifiers() == PKeyCode::NoModifier
) {
886 for (PINDEX i
= 0; i
< PARRAYSIZE(ansikeys
); i
++) {
887 if (key
.GetValue() == ansikeys
[i
].value
) {
889 *keyboard
<< ansikeys
[i
].sequence
;
890 } while (repeat
-- > 0);
896 return PTerminal::OnKeyDown(key
, repeat
);
900 void PAnsiTerminal::ProcessCharacter(PDrawCanvas
& canvas
, char ch
)
902 if (!inEscapeSequence
) {
904 PTerminal::ProcessCharacter(canvas
, ch
);
906 inEscapeSequence
= TRUE
;
909 memset(parameter
, 0, sizeof(parameter
));
914 if (inParameter
&& ch
>= '0' && ch
<= '9') {
915 parameter
[numParameters
] = parameter
[numParameters
]*10 + ch
-'0';
920 case '\033' : // <esc>
923 memset(parameter
, 0, sizeof(parameter
));
933 case ';' : // <esc>[0;1;2; etc
937 case 'h' : // <esc>[0h Set Mode
938 case 'l' : // <esc>[0l Reset Mode
939 switch (parameter
[0]) {
944 lineFeedOnReturn
= ch
== 'h';
949 case 'r' : // <esc>[0;24r Set scroll region
950 if (parameter
[0] != 0)
952 if (parameter
[1] == 0)
954 if (parameter
[0] < parameter
[1]) {
955 scrollTop
= PMIN(parameter
[0], rows
-1);
956 scrollBottom
= PMIN(parameter
[1], rows
);
960 case 'A' : // <esc>[0A Cursor Up
961 MoveCaretPosition(canvas
, -PMAX(parameter
[0], 1), 0);
964 case 'B' : // <esc>[0B Cursor Down
965 MoveCaretPosition(canvas
, PMAX(parameter
[0], 1), 0);
968 case 'C' : // <esc>[0C Cursor Forward
969 MoveCaretPosition(canvas
, 0, PMAX(parameter
[0], 1));
973 if (inParameter
) // <esc>[0D Cursor Backward
974 MoveCaretPosition(canvas
, 0, -PMAX(parameter
[0], 1));
975 else // <esc>D Scroll Forward
976 ScrollLines(canvas
, scrollTop
, scrollBottom
, 1);
979 case 'H' : // <esc>[1;1H Cursor motion
980 case 'f' : // <esc>[1;1f Set active position
981 MoveCaretPosition(canvas
,
982 parameter
[0]-cursorRow
-1, parameter
[1]-cursorColumn
-1);
985 case 'J' : // <esc>[0J Erase display
986 switch (parameter
[0]) {
987 case 0 : // <esc>[0J To end
988 if (cursorRow
!= 0 || cursorColumn
!= 0) {
989 GetCursorRow().Scroll(this,
990 canvas
, cursorRow
, cursorColumn
, INT_MAX
, INT_MAX
);
991 if (cursorRow
+1 < rows
)
992 ScrollLines(canvas
, cursorRow
+1, rows
, INT_MAX
);
995 // Do whole screen if cursor is homed
997 case 2 : // <esc>[2J Whole screen
998 ScrollLines(canvas
, 0, rows
, INT_MAX
);
1001 case 1 : // <esc>[1J To beginning
1002 GetCursorRow().Scroll(this,
1003 canvas
, cursorRow
, 0, cursorColumn
+1, INT_MAX
);
1005 ScrollLines(canvas
, 0, cursorRow
, INT_MAX
);
1010 case 'K' : // <esc>[0K Erase Line
1011 switch (parameter
[0]) {
1012 case 0 : // <esc>[0K To end
1013 GetCursorRow().Scroll(this,
1014 canvas
, cursorRow
, cursorColumn
, INT_MAX
, INT_MAX
);
1017 case 1 : // <esc>[1K To beginning
1018 GetCursorRow().Scroll(this,
1019 canvas
, cursorRow
, 0, cursorColumn
+1, INT_MAX
);
1022 case 2 : // <esc>[2K Whole
1023 GetCursorRow().Scroll(this, canvas
, cursorRow
, 0, INT_MAX
, INT_MAX
);
1028 case 'L' : // <esc>[1L Insert Lines
1029 ScrollLines(canvas
, cursorRow
, scrollBottom
, -PMAX(parameter
[0], 1));
1033 if (inParameter
) // <esc>[1M Delete Lines
1034 ScrollLines(canvas
, cursorRow
, scrollBottom
, PMAX(parameter
[0], 1));
1035 else // <esc>M Scroll Backward
1036 ScrollLines(canvas
, scrollTop
, scrollBottom
, -1);
1039 case '@' : // <esc>[1@ Insert Character
1040 GetCursorRow().Scroll(this,
1041 canvas
, cursorRow
, cursorColumn
, INT_MAX
, -PMAX(parameter
[0], 1));
1044 case 'P' : // <esc>[1P Delete Character
1045 GetCursorRow().Scroll(this,
1046 canvas
, cursorRow
, cursorColumn
, INT_MAX
, PMAX(parameter
[0], 1));
1052 // Else is <esc>7 Save Cursor Position
1054 case 's' : // <esc>[s Save Cursor Position
1055 savedCursorRow
= cursorRow
;
1056 savedCursorColumn
= cursorColumn
;
1062 // Else is <esc>8 Restore Cursor Position
1064 case 'u' : // <esc>[u Restore Cursor Position
1065 cursorRow
= savedCursorRow
;
1066 cursorColumn
= savedCursorColumn
;
1067 MoveCaretPosition(canvas
, 0, 0);
1070 case 'm' : { // <esc>[0m Set Graphic Rendition
1071 for (register PINDEX i
= 0; i
<= numParameters
; i
++) {
1072 switch (parameter
[i
]) {
1074 attribute
.bold
= FALSE
;
1075 attribute
.underline
= FALSE
;
1076 attribute
.blink
= FALSE
;
1077 attribute
.inverse
= FALSE
;
1078 attribute
.fgColor
= 8; // Window fg colour
1079 attribute
.bkColor
= 9; // Window bk colour
1082 attribute
.bold
= TRUE
;
1085 attribute
.underline
= TRUE
;
1088 attribute
.blink
= TRUE
;
1091 attribute
.inverse
= TRUE
;
1095 if (parameter
[i
] >= 30 && parameter
[i
] <= 39)
1096 attribute
.fgColor
= parameter
[i
]-30;
1097 else if (parameter
[i
] >= 40 && parameter
[i
] <= 49)
1098 attribute
.bkColor
= parameter
[i
]-40;
1104 case 'n' : // <esc>[n Device Status Report
1105 if (keyboard
!= NULL
) {
1106 *keyboard
<< "\033[" << (cursorRow
+1) << ';' << (cursorColumn
+1) << 'R';
1112 inEscapeSequence
= FALSE
;
1117 ///////////////////////////////////////////////////////////////////////////////
1119 PTerminalChannel::PTerminalChannel()
1125 PTerminalChannel::PTerminalChannel(PTerminal
& term
)
1131 PTerminalChannel::~PTerminalChannel()
1137 BOOL
PTerminalChannel::IsOpen() const
1139 return terminal
!= NULL
;
1143 PString
PTerminalChannel::GetName() const
1146 return terminal
->GetClass();
1152 BOOL
PTerminalChannel::Read(void * buf
, PINDEX len
)
1156 return SetErrorValues(NotOpen
, EBADF
, LastReadError
);
1159 readBuf
.read((char *)buf
, len
);
1160 lastReadCount
= readBuf
.gcount();
1161 if (lastReadCount
> 0)
1164 ConvertOSError(0, LastReadError
);
1169 BOOL
PTerminalChannel::Write(const void * buf
, PINDEX len
)
1173 return SetErrorValues(NotOpen
, EBADF
, LastWriteError
);
1176 terminal
->Write((const char *)buf
, len
);
1177 lastWriteCount
= len
;
1182 BOOL
PTerminalChannel::Close()
1185 return SetErrorValues(NotOpen
, EBADF
);
1189 terminal
->SetKeyboardStream(NULL
);
1191 return ConvertOSError(0);
1195 BOOL
PTerminalChannel::Open(PTerminal
& term
)
1198 term
.SetKeyboardStream(&readBuf
);
1199 return ConvertOSError(0);
1205 // End Of File ///////////////////////////////////////////////////////////////