1 /**
2  * Screen module for dcell provides the common interface for different implementations of KVM type devices.
3  *
4  * Copyright: Copyright 2022 Garrett D'Amore
5  * Authors: Garrett D'Amore
6  * License:
7  *   Distributed under the Boost Software License, Version 1.0.
8  *   (See accompanying file LICENSE or https://www.boost.org/LICENSE_1_0.txt)
9  *   SPDX-License-Identifier: BSL-1.0
10  */
11 module dcell.screen;
12 
13 import core.time;
14 import std.concurrency;
15 
16 public import dcell.cell;
17 public import dcell.cursor;
18 public import dcell.key;
19 public import dcell.event;
20 public import dcell.mouse;
21 
22 /**
23  * Screen is implemented by different platforms to provide a common interface for building
24  * text best full-screen user interfaces.
25  */
26 interface Screen
27 {
28     /**
29      * Clears the screen.  This doesn't take effect until
30      * the show function is called.
31      */
32     void clear();
33 
34     /**
35      * Retrive the contents for a given address.  This is taken from
36      * the backing draw buffer, and won't necessarily reflect what is
37      * displayed to the user until show is called.
38      */
39     ref Cell opIndex(size_t x, size_t y);
40 
41     /** Convenience for indexing */
42     final ref Cell opIndex(Coord pos)
43     {
44         return this[pos.x, pos.y];
45     }
46 
47     /**
48      * Set the content for for a given location.  This won't necessarily
49      * take effect until the show function is called.
50      */
51     void opIndexAssign(Cell, size_t x, size_t y);
52 
53 
54     /** Convenience for indexing. */
55     final void opIndexAssign(Cell c, Coord pos)
56     {
57         this[pos.x, pos.y] = c;
58     }
59 
60     /** Support $ operation in indices. */
61     size_t opDollar(size_t dim)() {
62         static if (dim == 0) {
63             return size().x;
64         } else {
65             return size().y;
66         }
67     }
68 
69     /**
70      * Show the cursor at its current location.
71      */
72     void showCursor(Cursor);
73 
74     /**
75      * Move the cursor to the given location, and show
76      * it using the appropriate style.
77      *
78      * Params:
79      *  pos = position of the cursor
80      *  cur = cursor style
81      */
82     void showCursor(Coord pos, Cursor cur = Cursor.current);
83 
84     /**
85      * It would be nice to know if a given key is supported by
86      * a terminal.  Note that this is best-effort, and some terminals
87      * may present the ability to support a key, without actually having
88      * such a physical key and some combinations may be suppressed by
89      * the emulator or the environment the emulator runs in.
90      *
91      * Returns: true if the key appears to be supported on the terminal.
92      */
93     bool hasKey(Key);
94 
95     /**
96      * Obtain the terminal window size.
97      * The x, y represent the number of columns and rows.
98      * The highest valid address will be coord.x-1, coord.y-1.
99      *
100      * Returns: terminal dimensions
101      */
102     Coord size();
103 
104     /**
105      * If start was called without a Tid to send to, then events
106      * are delivered into a queue that can be polled via this API.
107      * This function is thread safe.
108      * Params:
109      *   dur = maximum time to wait, if no event is available then EventType.none is returned.
110      * Returns:
111      *   The event, which will be EventType.none if it times out, or EventType.closed if it is stopped.
112      */
113     Event receiveEvent(Duration dur);
114 
115     /**
116      * Receive events, withotu a timeout.  This only works if start
117      * was called without a tid.
118      */
119     Event receiveEvent();
120 
121     /**
122      * Enable backeted paste mode mode.  Bracketed paste mode
123      * will pasted content in a single event, and is therefore
124      * distinguishable from individually typed characters.
125      *
126      * Params: 
127      *   b = true to enable bracketed paste, false for disable
128      */
129     void enablePaste(bool b);
130 
131     /**
132      * Do we have a mouse? This may be overly optimitistic for some
133      * terminals, but it is a good first guess.
134      *
135      * Returns: true if the terminal is thought to support mouse events
136      */
137     bool hasMouse();
138 
139     /**
140      * Enable mouse mode.  This can cause terminals/emulators
141      * to behave differently -- for example affecting the ability
142      * to scroll or use copy/paste.
143      *
144      * Params:
145      *   en = mouse events to report (mask)
146      */
147     void enableMouse(MouseEnable en);
148 
149     /**
150      * Enable typical mouse features. This enables tracking, but
151      * leaves drag events disabled.  Enabling mouse drag will impair
152      * use of copy-paste at the OS level, which many users tend to
153      * find frustrating.
154      */
155     final void enableMouse()
156     {
157         enableMouse(MouseEnable.buttons | MouseEnable.motion);
158     }
159 
160     /**
161      * Disable all mouse handling/capture.
162      */
163     final void disableMouse()
164     {
165         enableMouse(MouseEnable.disable);
166     }
167 
168     /**
169      * If the terminal supports color, this returns the
170      * the number of colors.
171      *
172      * Returns: the number of colors supported (max 256), or 0 if monochrome
173      */
174     int colors();
175 
176     /**
177      * Show content on the screen, doing so efficiently.
178      */
179     void show();
180 
181     /**
182      * Update the screen, writing every cell.  This should be done
183      * to repair screen damage, for example.
184      */
185     void sync();
186 
187     /**
188      * Emit a beep or bell.  This is done immediately.
189      */
190     void beep();
191 
192     /**
193      * Attempt to resize the terminal.  YMMV.
194      */
195     void setSize(Coord);
196 
197     /**
198      * Set the default style used when clearning the screen, etc.
199      */
200     void setStyle(Style);
201 
202     /**
203      * Fill the entire screen with the given content and style.
204      * Content is not drawn until the show() or sync() functions are called.
205      */
206     void fill(string s, Style style);
207 
208     /**
209      * Fill the entire screen with the given content, but preserve the style.
210      */
211     void fill(string s);
212 
213     /**
214      * Applications should call this in response to receiving
215      * a resize event.  (This can't be done automatically to
216      * avoid thread safety issues.)
217      */
218     void resize();
219 
220     /**
221      * Start should be called to start processing.  Once this begins,
222      * events (see event.d) will be delivered to the caller via
223      * std.concurrency.send().  Additioanlly, this may change terminal
224      * settings to put the input stream into raw mode, etc.
225      */
226     void start();
227     void start(Tid);
228 
229     /**
230      * Stop is called to stop processing on teh screen.  The terminal
231      * settings will be restored, and the screen may be cleared. Input
232      * events will no longer be delivered.  This should be called when
233      * the program is exited, or if suspending (to run a subshell process
234      * interactively for example).
235      */
236     void stop();
237 }