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 }