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