1 /*
2     SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
3     SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de>
4 
5     SPDX-License-Identifier: GPL-2.0-or-later
6 */
7 
8 #ifndef TERMINALDISPLAY_H
9 #define TERMINALDISPLAY_H
10 
11 // Qt
12 #include <QColor>
13 #include <QPointer>
14 #include <QWidget>
15 
16 #include <memory>
17 
18 // Konsole
19 #include "../characters/Character.h"
20 #include "Enumeration.h"
21 #include "ScreenWindow.h"
22 #include "ScrollState.h"
23 #include "colorscheme/ColorScheme.h"
24 #include "konsoleprivate_export.h"
25 #include "widgets/TerminalHeaderBar.h"
26 
27 #include "TerminalBell.h"
28 
29 class QDrag;
30 class QDragEnterEvent;
31 class QDropEvent;
32 class QLabel;
33 class QTimer;
34 class QEvent;
35 class QVBoxLayout;
36 class QKeyEvent;
37 class QShowEvent;
38 class QHideEvent;
39 class QTimerEvent;
40 class KMessageWidget;
41 
42 namespace Konsole
43 {
44 class TerminalPainter;
45 class TerminalScrollBar;
46 class TerminalColor;
47 class TerminalFont;
48 
49 class KonsolePrintManager;
50 
51 class FilterChain;
52 class TerminalImageFilterChain;
53 class SessionController;
54 class IncrementalSearchBar;
55 class HotSpot;
56 class Profile;
57 
58 /**
59  * A widget which displays output from a terminal emulation and sends input keypresses and mouse activity
60  * to the terminal.
61  *
62  * When the terminal emulation receives new output from the program running in the terminal,
63  * it will update the display by calling updateImage().
64  *
65  * TODO More documentation
66  */
67 class KONSOLEPRIVATE_EXPORT TerminalDisplay : public QWidget
68 {
69     Q_OBJECT
70 
71 public:
72     /** Constructs a new terminal display widget with the specified parent. */
73     explicit TerminalDisplay(QWidget *parent = nullptr);
74     ~TerminalDisplay() override;
75 
76     void showDragTarget(const QPoint &cursorPos);
77     void hideDragTarget();
78 
79     void applyProfile(const QExplicitlySharedDataPointer<Profile> &profile);
80 
81     /**
82      * Sets the seed used to generate random colors for the display
83      * (in color schemes that support them).
84      */
85     void setRandomSeed(uint randomSeed);
86     /**
87      * Returns the seed used to generate random colors for the display
88      * (in color schemes that support them).
89      */
90     uint randomSeed() const;
91 
92     /** Sets the opacity of the terminal display. */
93     void setOpacity(qreal opacity);
94 
95     /** Sets the background picture */
96     void setWallpaper(const ColorSchemeWallpaper::Ptr &p);
97 
98     /**
99      * Returns the display's filter chain.  When the image for the display is updated,
100      * the text is passed through each filter in the chain.  Each filter can define
101      * hotspots which correspond to certain strings (such as URLs or particular words).
102      * Depending on the type of the hotspots created by the filter ( returned by Filter::Hotspot::type() )
103      * the view will draw visual cues such as underlines on mouse-over for links or translucent
104      * rectangles for markers.
105      *
106      * To add a new filter to the view, call:
107      *      viewWidget->filterChain()->addFilter( filterObject );
108      */
109     FilterChain *filterChain() const;
110 
111     /**
112      * Updates the filters in the display's filter chain.  This will cause
113      * the hotspots to be updated to match the current image.
114      *
115      * WARNING:  This function can be expensive depending on the
116      * image size and number of filters in the filterChain()
117      *
118      * TODO - This API does not really allow efficient usage.  Revise it so
119      * that the processing can be done in a better way.
120      *
121      * eg:
122      *      - Area of interest may be known ( eg. mouse cursor hovering
123      *      over an area )
124      */
125     void processFilters();
126 
127     /**
128      * Returns a list of menu actions created by the filters for the content
129      * at the given @p position.
130      */
131     QSharedPointer<HotSpot> filterActions(const QPoint &position);
132 
133     /** Specifies whether or not the cursor can blink. */
134     void setBlinkingCursorEnabled(bool blink);
135 
136     /** Specifies whether or not text can blink. */
137     void setBlinkingTextEnabled(bool blink);
138 
139     void setSessionController(SessionController *controller);
140     SessionController *sessionController();
141     Session::Ptr session() const;
142 
143     /**
144      * Sets the shape of the keyboard cursor.  This is the cursor drawn
145      * at the position in the terminal where keyboard input will appear.
146      *
147      * In addition the terminal display widget also has a cursor for
148      * the mouse pointer, which can be set using the QWidget::setCursor()
149      * method.
150      *
151      * Defaults to BlockCursor
152      */
153     void setKeyboardCursorShape(Enum::CursorShapeEnum shape);
154 
155     /**
156      * Sets the Cursor Style (DECSCUSR) via escape sequences
157      * @p shape cursor shape
158      * @p isBlinking if true, the cursor will be set to blink
159      */
160     void setCursorStyle(Enum::CursorShapeEnum shape, bool isBlinking);
161     /**
162      * Resets the cursor style to the current profile cursor shape and
163      * blinking settings
164      */
165     void resetCursorStyle();
166 
167     /**
168      * Returns the number of lines of text which can be displayed in the widget.
169      *
170      * This will depend upon the height of the widget and the current font.
171      * See fontHeight()
172      */
lines()173     int lines() const
174     {
175         return _lines;
176     }
177 
178     /**
179      * Returns the number of characters of text which can be displayed on
180      * each line in the widget.
181      *
182      * This will depend upon the width of the widget and the current font.
183      * See fontWidth()
184      */
columns()185     int columns() const
186     {
187         return _columns;
188     }
189 
usedColumns()190     int usedColumns() const
191     {
192         return _usedColumns;
193     }
194 
195     void setSize(int columns, int lines);
196     void propagateSize();
197 
198     // reimplemented
199     QSize sizeHint() const override;
200 
201     /**
202      * Sets which characters, in addition to letters and numbers,
203      * are regarded as being part of a word for the purposes
204      * of selecting words in the display by double clicking on them.
205      *
206      * The word boundaries occur at the first and last characters which
207      * are either a letter, number, or a character in @p wc
208      *
209      * @param wc An array of characters which are to be considered parts
210      * of a word ( in addition to letters and numbers ).
211      */
212     void setWordCharacters(const QString &wc);
213 
headerBar()214     TerminalHeaderBar *headerBar() const
215     {
216         return _headerBar;
217     }
218 
219     void resetCursor();
220 
contentRect()221     QRect contentRect() const
222     {
223         return _contentRect;
224     }
openLinksByDirectClick()225     bool openLinksByDirectClick() const
226     {
227         return _openLinksByDirectClick;
228     }
229 
230     /**
231      * Sets the terminal screen section which is displayed in this widget.
232      * When updateImage() is called, the display fetches the latest character image from the
233      * the associated terminal screen window.
234      *
235      * In terms of the model-view paradigm, the ScreenWindow is the model which is rendered
236      * by the TerminalDisplay.
237      */
238     void setScreenWindow(ScreenWindow *window);
239     /** Returns the terminal screen section which is displayed in this widget.  See setScreenWindow() */
screenWindow()240     QPointer<ScreenWindow> screenWindow() const
241     {
242         return _screenWindow;
243     }
244 
245     // Select the current line.
246     void selectCurrentLine();
247 
248     /**
249      * Selects everything in the terminal
250      */
251     void selectAll();
252 
253     bool bracketedPasteMode() const;
254 
255     /**
256      * Returns true if the flow control warning box is enabled.
257      * See outputSuspended() and setFlowControlWarningEnabled()
258      */
flowControlWarningEnabled()259     bool flowControlWarningEnabled() const
260     {
261         return _flowControlWarningEnabled;
262     }
263 
264     /** See setUsesMouseTracking() */
265     bool usesMouseTracking() const;
266 
hasCompositeFocus()267     bool hasCompositeFocus() const
268     {
269         return _hasCompositeFocus;
270     }
271 
272     // maps an area in the character image to an area on the widget
273     QRect imageToWidget(const QRect &imageArea) const;
274     QRect widgetToImage(const QRect &widgetArea) const;
275 
276     // maps a point on the widget to the position ( ie. line and column )
277     // of the character at that point. When the edge is true, it maps to
278     // a character which left edge is closest to the point.
279     QPair<int, int> getCharacterPosition(const QPoint &widgetPoint, bool edge) const;
280 
281     // toggle the header bar Minimize/Maximize button.
282     void setExpandedMode(bool expand);
283 
scrollBar()284     TerminalScrollBar *scrollBar() const
285     {
286         return _scrollBar;
287     }
288 
terminalColor()289     TerminalColor *terminalColor() const
290     {
291         return _terminalColor;
292     }
293 
terminalFont()294     TerminalFont *terminalFont() const
295     {
296         return _terminalFont.get();
297     }
298 
cursorBlinking()299     bool cursorBlinking() const
300     {
301         return _cursorBlinking;
302     }
303 
textBlinking()304     bool textBlinking() const
305     {
306         return _textBlinking;
307     }
308 
cursorShape()309     Enum::CursorShapeEnum cursorShape() const
310     {
311         return _cursorShape;
312     }
313 
bidiEnabled()314     bool bidiEnabled() const
315     {
316         return _bidiEnabled;
317     }
318 
wallpaper()319     ColorSchemeWallpaper::Ptr wallpaper() const
320     {
321         return _wallpaper;
322     }
323 
324     struct InputMethodData {
325         QString preeditString;
326         QRect previousPreeditRect;
327     };
328 
329     // returns true if the cursor's position is on display.
330     bool isCursorOnDisplay() const;
331 
332     // returns the position of the cursor in columns and lines
333     QPoint cursorPosition() const;
334 
335     // returns 0 - not selecting, 1 - pending selection (button pressed but no movement yet), 2 - selecting
336     int selectionState() const;
337 
338 public Q_SLOTS:
339     /**
340      * Scrolls current ScreenWindow
341      *
342      * it's needed for proper handling scroll commands in the Vt102Emulation class
343      */
344     void scrollScreenWindow(enum ScreenWindow::RelativeScrollMode mode, int amount);
345 
346     /**
347      * Causes the terminal display to fetch the latest character image from the associated
348      * terminal screen ( see setScreenWindow() ) and redraw the display.
349      */
350     void updateImage();
351 
352     void setAutoCopySelectedText(bool enabled);
353 
354     void setCopyTextAsHTML(bool enabled);
355 
356     void setMiddleClickPasteMode(Enum::MiddleClickPasteModeEnum mode);
357 
358     /** Copies the selected text to the X11 Selection. */
359     void copyToX11Selection();
360 
361     /** Copies the selected text to the system clipboard. */
362     void copyToClipboard();
363 
364     /**
365      * Pastes the content of the clipboard into the display.
366      *
367      * URLs of local files are treated specially:
368      *  - The scheme part, "file://", is removed from each URL
369      *  - The URLs are pasted as a space-separated list of file paths
370      */
371     void pasteFromClipboard(bool appendEnter = false);
372     /**
373      * Pastes the content of the X11 selection into the
374      * display.
375      */
376     void pasteFromX11Selection(bool appendEnter = false);
377 
378     /**
379      * Changes whether the flow control warning box should be shown when the flow control
380      * stop key (Ctrl+S) are pressed.
381      */
382     void setFlowControlWarningEnabled(bool enable);
383     /**
384      * Causes the widget to display or hide a message informing the user that terminal
385      * output has been suspended (by using the flow control key combination Ctrl+S)
386      *
387      * @param suspended True if terminal output has been suspended and the warning message should
388      *                     be shown or false to indicate that terminal output has been resumed and that
389      *                     the warning message should disappear.
390      */
391     void outputSuspended(bool suspended);
392 
393     /**
394      * Sets whether the program currently running in the terminal is interested
395      * in Mouse Tracking events.
396      *
397      * When set to true, Konsole will send Mouse Tracking events.
398      *
399      * The user interaction needed to create text selections will change
400      * also, and the user will be required to hold down the Shift key to
401      * create a selection or perform other mouse activities inside the view
402      * area, since the program running in the terminal is being allowed
403      * to handle normal mouse events itself.
404      *
405      * @param on Set to true if the program running in the terminal is
406      * interested in Mouse Tracking events or false otherwise.
407      */
408     void setUsesMouseTracking(bool on);
409 
410     void setBracketedPasteMode(bool on);
411 
412     /**
413      * Shows a notification that a bell event has occurred in the terminal.
414      * TODO: More documentation here
415      */
416     void bell(const QString &message);
417 
418     /**
419      * Sets the display's contents margins.
420      */
421     void setMargin(int margin);
422 
423     /**
424      * Sets whether the contents are centered between the margins.
425      */
426     void setCenterContents(bool enable);
427 
428     /**
429      * Return the current color scheme
430      */
colorScheme()431     ColorScheme const *colorScheme() const
432     {
433         return _colorScheme;
434     }
435 
droppedEdge()436     Qt::Edge droppedEdge() const
437     {
438         return _overlayEdge;
439     }
440 
441     // Used to show/hide the message widget
442     void updateReadOnlyState(bool readonly);
443     IncrementalSearchBar *searchBar() const;
444 
445     // Used for requestPrint
446     void printScreen();
447     Character getCursorCharacter(int column, int line);
448 
449     int loc(int x, int y) const;
450 
451 Q_SIGNALS:
452     void requestToggleExpansion();
453     void requestMoveToNewTab(TerminalDisplay *display);
454 
455     /**
456      * Emitted when the user presses a key whilst the terminal widget has focus.
457      */
458     void keyPressedSignal(QKeyEvent *event);
459 
460     /**
461      * A mouse event occurred.
462      * @param button The mouse button (0 for left button, 1 for middle button, 2 for right button, 3 for release)
463      * @param column The character column where the event occurred
464      * @param line The character row where the event occurred
465      * @param eventType The type of event.  0 for a mouse press / release or 1 for mouse motion
466      */
467     void mouseSignal(int button, int column, int line, int eventType);
468     void changedContentSizeSignal(int height, int width);
469 
470     /**
471      * Emitted when the user right clicks on the display, or right-clicks
472      * with the Shift key held down if usesMouseTracking() is true.
473      *
474      * This can be used to display a context menu.
475      */
476     void configureRequest(const QPoint &position);
477 
478     /**
479      * When a shortcut which is also a valid terminal key sequence is pressed while
480      * the terminal widget  has focus, this signal is emitted to allow the host to decide
481      * whether the shortcut should be overridden.
482      * When the shortcut is overridden, the key sequence will be sent to the terminal emulation instead
483      * and the action associated with the shortcut will not be triggered.
484      *
485      * @p override is set to false by default and the shortcut will be triggered as normal.
486      */
487     void overrideShortcutCheck(QKeyEvent *keyEvent, bool &override);
488 
489     void sendStringToEmu(const QByteArray &local8BitString);
490 
491     void compositeFocusChanged(bool focused);
492 
493     void peekPrimaryRequested(bool doPeek);
494 
495     void drawContents(Character *image,
496                       QPainter &paint,
497                       const QRect &rect,
498                       bool printerFriendly,
499                       int imageSize,
500                       bool bidiEnabled,
501                       QVector<LineProperty> lineProperties);
502     void drawCurrentResultRect(QPainter &painter, QRect searchResultRect);
503 
504     void highlightScrolledLines(QPainter &painter, bool isTimerActive, QRect rect);
505     QRegion highlightScrolledLinesRegion(bool nothingChanged, TerminalScrollBar *scrollBar);
506 
507     void drawBackground(QPainter &painter, const QRect &rect, const QColor &backgroundColor, bool useOpacitySetting);
508     void drawCharacters(QPainter &painter,
509                         const QRect &rect,
510                         const QString &text,
511                         const Character *style,
512                         const QColor &characterColor,
513                         const LineProperty lineProperty);
514     void drawInputMethodPreeditString(QPainter &painter, const QRect &rect, TerminalDisplay::InputMethodData &inputMethodData, Character *image);
515 
516 protected:
517     // events
518     bool event(QEvent *event) override;
519     void paintEvent(QPaintEvent *pe) override;
520     void showEvent(QShowEvent *event) override;
521     void hideEvent(QHideEvent *event) override;
522     void resizeEvent(QResizeEvent *event) override;
523     void contextMenuEvent(QContextMenuEvent *event) override;
524     void focusInEvent(QFocusEvent *event) override;
525     void focusOutEvent(QFocusEvent *event) override;
526     void keyPressEvent(QKeyEvent *event) override;
527     void keyReleaseEvent(QKeyEvent *event) override;
528     void leaveEvent(QEvent *event) override;
529     void mouseDoubleClickEvent(QMouseEvent *ev) override;
530     void mousePressEvent(QMouseEvent *ev) override;
531     void mouseReleaseEvent(QMouseEvent *ev) override;
532     void mouseMoveEvent(QMouseEvent *ev) override;
533     void wheelEvent(QWheelEvent *ev) override;
534     bool focusNextPrevChild(bool next) override;
535 
536     void extendSelection(const QPoint &position);
537 
538     // drag and drop
539     void dragEnterEvent(QDragEnterEvent *event) override;
540     void dropEvent(QDropEvent *event) override;
541     void doDrag();
542     enum DragState {
543         diNone,
544         diPending,
545         diDragging,
546     };
547 
548     struct DragInfo {
549         DragState state;
550         QPoint start;
551         QDrag *dragObject;
552     } _dragInfo;
553 
554     // classifies the 'ch' into one of three categories
555     // and returns a character to indicate which category it is in
556     //
557     //     - A space (returns ' ')
558     //     - Part of a word (returns 'a')
559     //     - Other characters (returns the input character)
560     QChar charClass(const Character &ch) const;
561 
562     void clearImage();
563 
564     void mouseTripleClickEvent(QMouseEvent *ev);
565     void selectLine(QPoint pos, bool entireLine);
566 
567     // reimplemented
568     void inputMethodEvent(QInputMethodEvent *event) override;
569     QVariant inputMethodQuery(Qt::InputMethodQuery query) const override;
570 
571 protected Q_SLOTS:
572 
573     void blinkTextEvent();
574     void blinkCursorEvent();
575 
576 private Q_SLOTS:
577 
578     void viewScrolledByUser();
579 
580     void dismissOutputSuspendedMessage();
581 
582 private:
583     Q_DISABLE_COPY(TerminalDisplay)
584 
585     // the area where the preedit string for input methods will be draw
586     QRect preeditRect() const;
587 
588     // shows a notification window in the middle of the widget indicating the terminal's
589     // current size in columns and lines
590     void showResizeNotification();
591 
592     void calcGeometry();
593     void updateImageSize();
594     void makeImage();
595 
596     void paintFilters(QPainter &painter);
597 
598     void setupHeaderVisibility();
599 
600     // redraws the cursor
601     void updateCursor();
602 
603     bool handleShortcutOverrideEvent(QKeyEvent *keyEvent);
604 
605     void doPaste(QString text, bool appendReturn);
606 
607     void processMidButtonClick(QMouseEvent *ev);
608 
609     QPoint findLineStart(const QPoint &pnt);
610     QPoint findLineEnd(const QPoint &pnt);
611     QPoint findWordStart(const QPoint &pnt);
612     QPoint findWordEnd(const QPoint &pnt);
613 
614     // Uses the current settings for trimming whitespace and preserving linebreaks to create a proper flag value for Screen
615     Screen::DecodingOptions currentDecodingOptions();
616 
617     // Boilerplate setup for MessageWidget
618     KMessageWidget *createMessageWidget(const QString &text);
619 
620     // the window onto the terminal screen which this display
621     // is currently showing.
622     QPointer<ScreenWindow> _screenWindow;
623 
624     bool _bellMasked;
625 
626     QVBoxLayout *_verticalLayout;
627 
628     int _lines; // the number of lines that can be displayed in the widget
629     int _columns; // the number of columns that can be displayed in the widget
630 
631     // Character line and character column as per a previous call to
632     // getCharacterPosition() in mouseMoveEvent().
633     int _prevCharacterLine;
634     int _prevCharacterColumn;
635 
636     int _usedLines; // the number of lines that are actually being used, this will be less
637     // than 'lines' if the character image provided with setImage() is smaller
638     // than the maximum image size which can be displayed
639 
640     int _usedColumns; // the number of columns that are actually being used, this will be less
641     // than 'columns' if the character image provided with setImage() is smaller
642     // than the maximum image size which can be displayed
643 
644     QRect _contentRect;
645     Character *_image; // [lines][columns]
646     // only the area [usedLines][usedColumns] in the image contains valid data
647 
648     int _imageSize;
649     QVector<LineProperty> _lineProperties;
650 
651     QColor _colorTable[TABLE_COLORS];
652 
653     uint _randomSeed;
654 
655     bool _resizing;
656     bool _showTerminalSizeHint;
657     bool _bidiEnabled;
658     bool _usesMouseTracking;
659     bool _bracketedPasteMode;
660 
661     QPoint _iPntSel; // initial selection point
662     QPoint _pntSel; // current selection point
663     QPoint _tripleSelBegin; // help avoid flicker
664     int _actSel; // selection state
665     bool _wordSelectionMode;
666     bool _lineSelectionMode;
667     bool _preserveLineBreaks;
668     bool _columnSelectionMode;
669 
670     bool _autoCopySelectedText;
671     bool _copyTextAsHTML;
672     Enum::MiddleClickPasteModeEnum _middleClickPasteMode;
673 
674     QString _wordCharacters;
675     TerminalBell _bell;
676 
677     bool _allowBlinkingText; // allow text to blink
678     bool _allowBlinkingCursor; // allow cursor to blink
679     bool _textBlinking; // text is blinking, hide it when drawing
680     bool _cursorBlinking; // cursor is blinking, hide it when drawing
681     bool _hasTextBlinker; // has characters to blink
682     QTimer *_blinkTextTimer;
683     QTimer *_blinkCursorTimer;
684 
685     bool _openLinksByDirectClick; // Open URL and hosts by single mouse click
686 
687     bool _ctrlRequiredForDrag; // require Ctrl key for drag selected text
688     bool _dropUrlsAsText; // always paste URLs as text without showing copy/move menu
689 
690     Enum::TripleClickModeEnum _tripleClickMode;
691     bool _possibleTripleClick; // is set in mouseDoubleClickEvent and deleted
692     // after QApplication::doubleClickInterval() delay
693 
694     QLabel *_resizeWidget;
695     QTimer *_resizeTimer;
696 
697     bool _flowControlWarningEnabled;
698 
699     // widgets related to the warning message that appears when the user presses Ctrl+S to suspend
700     // terminal output - informing them what has happened and how to resume output
701     KMessageWidget *_outputSuspendedMessageWidget;
702 
703     QSize _size;
704 
705     ColorScheme const *_colorScheme;
706     ColorSchemeWallpaper::Ptr _wallpaper;
707 
708     // list of filters currently applied to the display.  used for links and
709     // search highlight
710     TerminalImageFilterChain *_filterChain;
711     bool _filterUpdateRequired;
712 
713     Enum::CursorShapeEnum _cursorShape;
714 
715     InputMethodData _inputMethodData;
716 
717     // the delay in milliseconds between redrawing blinking text
718     static const int TEXT_BLINK_DELAY = 500;
719 
720     // the duration of the size hint in milliseconds
721     static const int SIZE_HINT_DURATION = 1000;
722 
723     SessionController *_sessionController;
724 
725     bool _trimLeadingSpaces; // trim leading spaces in selected text
726     bool _trimTrailingSpaces; // trim trailing spaces in selected text
727     bool _mouseWheelZoom; // enable mouse wheel zooming or not
728 
729     int _margin; // the contents margin
730     bool _centerContents; // center the contents between margins
731 
732     KMessageWidget *_readOnlyMessageWidget; // Message shown at the top when read-only mode gets activated
733 
734     // Needed to know whether the mode really changed between update calls
735     bool _readOnly;
736 
737     bool _dimWhenInactive;
738     int _dimValue;
739 
740     ScrollState _scrollWheelState;
741     IncrementalSearchBar *_searchBar;
742     TerminalHeaderBar *_headerBar;
743     QRect _searchResultRect;
744     friend class TerminalDisplayAccessible;
745 
746     bool _drawOverlay;
747     Qt::Edge _overlayEdge;
748 
749     bool _hasCompositeFocus;
750     bool _displayVerticalLine;
751     int _displayVerticalLineAtChar;
752 
753     QKeySequence _peekPrimaryShortcut;
754 
755     TerminalPainter *_terminalPainter;
756     TerminalScrollBar *_scrollBar;
757     TerminalColor *_terminalColor;
758     std::unique_ptr<TerminalFont> _terminalFont;
759 
760     std::unique_ptr<KonsolePrintManager> _printManager;
761 };
762 
763 }
764 
765 #endif // TERMINALDISPLAY_H
766