1 /**
2  * Screen module for dcell provides the common interface for different implementations of KVM type devices.
3  *
4  * Copyright: Copyright 2025 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 
15 public import dcell.cell;
16 public import dcell.cursor;
17 public import dcell.key;
18 public import dcell.event;
19 public import dcell.mouse;
20 
21 /**
22  * Screen is implemented by different platforms to provide a common interface for building
23  * text best full-screen user interfaces.
24  */
25 interface Screen
26 {
27     /**
28      * Clears the screen.  This doesn't take effect until
29      * the show function is called.
30      */
31     void clear() @safe;
32 
33     /**
34      * Retrieve the contents for a given address.  This is taken from
35      * the backing draw buffer, and won't necessarily reflect what is
36      * displayed to the user until show is called.
37      */
38     ref Cell opIndex(size_t x, size_t y) @safe;
39 
40     /** Convenience for indexing */
41     final ref Cell opIndex(Coord pos) @safe
42     {
43         return this[pos.x, pos.y];
44     }
45 
46     /**
47      * Set the content for for a given location.  This won't necessarily
48      * take effect until the show function is called.
49      */
50     void opIndexAssign(Cell, size_t x, size_t y) @safe;
51 
52     /** Convenience for indexing. */
53     final void opIndexAssign(Cell c, Coord pos) @safe
54     {
55         this[pos.x, pos.y] = c;
56     }
57 
58     /** Support $ operation in indices. */
59     size_t opDollar(size_t dim)() @safe
60     {
61         static if (dim == 0)
62         {
63             return size().x;
64         }
65         else
66         {
67             return size().y;
68         }
69     }
70 
71     /**
72      * Show the cursor at its current location.
73      */
74     void showCursor(Cursor) @safe;
75 
76     /**
77      * Move the cursor to the given location, and show
78      * it using the appropriate style.
79      *
80      * Params:
81      *  pos = position of the cursor
82      *  cur = cursor style
83      */
84     void showCursor(Coord pos, Cursor cur = Cursor.current) @safe;
85 
86     /**
87      * Obtain the terminal window size.
88      * The x, y represent the number of columns and rows.
89      * The highest valid address will be coord.x-1, coord.y-1.
90      *
91      * Returns: terminal dimensions
92      */
93     Coord size() @safe;
94 
95     /**
96      * Wait for at least one event to be posted, for up to the given time.
97      * Params:
98      *   timeout = maximum duration to wait for an event to arrive
99      *   resched = if no event was posted, the caller should make another
100      *             attempt no later than this in order to handle incompletely parsed events
101      *
102      * Returns: true if at least one event is available, false otherwise.
103      */
104     bool waitForEvent(Duration timeout, ref Duration resched) @safe;
105 
106     /**
107      * Wait for at least one event to be posted.
108      * This simpler version can be used when the caller is in a simple poll/handle
109      * loop (typical for some simple applications.)
110      *
111      * Params:
112      *   timeout = maximum duration to wait for an event to arrive
113      *
114      * Returns: True if at least one event is available, false otherwise.
115      */
116     final bool waitForEvent(Duration timeout = Duration.max) @safe
117     {
118         Duration resched;
119         return waitForEvent(timeout, resched);
120     }
121 
122     /**
123      * Obtain the list of events.  The returned value is both an input range of `Event`,
124      * (for receiving events), and an output range of `Event`.
125      */
126     EventQ events() @safe;
127 
128     /**
129      * Enable bracketed paste mode mode.  Bracketed paste mode
130      * will pasted content in a single event, and is therefore
131      * distinguishable from individually typed characters.
132      *
133      * Params:
134      *   b = true to enable bracketed paste, false for disable
135      */
136     void enablePaste(bool b) @safe;
137 
138     /**
139      * Enable mouse mode.  This can cause terminals/emulators
140      * to behave differently -- for example affecting the ability
141      * to scroll or use copy/paste.
142      *
143      * Params:
144      *   en = mouse events to report (mask)
145      */
146     void enableMouse(MouseEnable en) @safe;
147 
148     /**
149      * Enable typical mouse features. This enables tracking, but
150      * leaves drag events disabled.  Enabling mouse drag will impair
151      * use of copy-paste at the OS level, which many users tend to
152      * find frustrating.
153      */
154     final void enableMouse() @safe
155     {
156         enableMouse(MouseEnable.buttons | MouseEnable.motion);
157     }
158 
159     /**
160      * Disable all mouse handling/capture.
161      */
162     final void disableMouse() @safe
163     {
164         enableMouse(MouseEnable.disable);
165     }
166 
167     /**
168      * Enable or disable the alternate screen. This must be called
169      * before start().  Note that this is best effort -- not every
170      * terminal actually supports this.  This is on by default.
171      * It can be disabled by setting DCELL_ALTSCREEN=disable in the
172      * environment.
173      */
174     void enableAlternateScreen(bool on) @safe;
175 
176     /**
177      * Set the title of the window. This only works for emulators running
178      * in a windowing environment, and is not universally supported.
179      * Setting an empty string as the title may let the emulator set
180      * a specific default, but it may also leave it empty - it depends
181      * on the specific terminal implementation.
182      */
183     void setTitle(string) @safe;
184 
185     /**
186      * If the terminal supports color, this returns the
187      * the number of colors.
188      *
189      * Returns: the number of colors supported (max 256), or 0 if monochrome
190      */
191     int colors() nothrow @safe;
192 
193     /**
194      * Show content on the screen, doing so efficiently.
195      */
196     void show() @safe;
197 
198     /**
199      * Update the screen, writing every cell.  This should be done
200      * to repair screen damage, for example.
201      */
202     void sync() @safe;
203 
204     /**
205      * Emit a beep or bell.  This is done immediately.
206      */
207     void beep() @safe;
208 
209     /**
210      * Attempt to resize the terminal.  YMMV.
211      */
212     void setSize(Coord) @safe;
213 
214     /**
215      * Fill the entire screen with the given content and style.
216      * Content is not drawn until the show() or sync() functions are called.
217      */
218     void fill(string s, Style style) @safe;
219 
220     /**
221      * Fill the entire screen with the given content, but preserve the style.
222      */
223     void fill(string s) @safe;
224 
225     /**
226      * Applications should call this in response to receiving
227      * a resize event.  (This can't be done automatically to
228      * avoid thread safety issues.)
229      */
230     void resize() @safe;
231 
232     /**
233      * Start sets up the terminal.  This changes terminal
234      * settings to put the input stream into raw mode, etc.
235      */
236     void start() @safe;
237 
238     /**
239      * Stop is called to stop processing on the screen.  The terminal
240      * settings will be restored, and the screen may be cleared.
241      *
242      * This should be called when the program is exited, or if suspending
243      * (to run a sub-shell process interactively for example).
244      */
245     void stop() @safe;
246 
247     /**
248      * The style property is used when writing content to the screen
249      * using the simpler write() API.
250      */
251     @property ref Style style() @safe;
252     @property Style style(const(Style)) @safe;
253 
254     /**
255      * The position property is used when writing content to the screen
256      * when using the simpler write() API.  The position will advance as
257      * content is written.
258      */
259     @property Coord position() const @safe;
260     @property Coord position(const(Coord)) @safe;
261 
262     /**
263      * Write content to the screen.  The content will be displayed using the
264      * position and the style currently set. It will wrap at the edge of the
265      * display if it is too long.
266      */
267     void write(string) @safe;
268     void write(wstring) @safe;
269     void write(dstring) @safe;
270 
271     /**
272      * Post arbitrary data to the system clipboard.
273      * It's up to the recipient to decode the data meaningfully.
274      * Terminals may prevent this for security reasons.
275      */
276     void setClipboard(const(ubyte[])) @safe;
277 
278     /**
279      * Request the clipboard contents.  It may be ignored.
280      *
281      * If the terminal is willing, it will post the clipboard contents using a
282      * `PasteEvent` with the clipboard content as the `binary`. (This may or may
283      * not be valid UTF-8. In most terminals it seems that only string data is posted,
284      * such as names of files rather than binary file content.)
285      *
286      * Terminals may prevent this for security reasons.
287      */
288     void getClipboard() @safe;
289 }