source: mainline/uspace/app/tetris/screen.c

Last change on this file was 09f41d3, checked in by Jiri Svoboda <jiri@…>, 9 months ago

Tetris should be able to detect screen cursor control capability

  • Property mode set to 100644
File size: 8.8 KB
Line 
1/*
2 * Copyright (c) 2024 Jiri Svoboda
3 * Copyright (c) 2011 Martin Decky
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** Attributations
31 *
32 * screen.c 8.1 (Berkeley) 5/31/93
33 * NetBSD: screen.c,v 1.4 1995/04/29 01:11:36 mycroft
34 * OpenBSD: screen.c,v 1.13 2006/04/20 03:25:36 ray
35 *
36 * Based upon BSD Tetris
37 *
38 * Copyright (c) 1992, 1993
39 * The Regents of the University of California.
40 * Distributed under BSD license.
41 *
42 * This code is derived from software contributed to Berkeley by
43 * Chris Torek and Darren F. Provine.
44 *
45 */
46
47/** @addtogroup tetris
48 * @{
49 */
50/** @file
51 */
52
53/*
54 * Tetris screen control.
55 */
56
57#include <errno.h>
58#include <stdio.h>
59#include <stdlib.h>
60#include <str.h>
61#include <vfs/vfs.h>
62#include <async.h>
63#include <stdbool.h>
64#include <io/console.h>
65#include <io/style.h>
66#include "screen.h"
67#include "tetris.h"
68
69#define STOP (B_COLS - 3)
70
71static cell curscreen[B_SIZE]; /* non-zero => standout (or otherwise marked) */
72static int curscore;
73static int isset; /* true => terminal is in game mode */
74
75static bool use_rgb; /* true => use RGB colors */
76static bool use_color; /* true => use indexed colors */
77
78static const struct shape *lastshape;
79
80static usec_t timeleft = 0;
81
82bool size_changed;
83console_ctrl_t *console;
84
85/*
86 * putstr() is for unpadded strings (either as in termcap(5) or
87 * simply literal strings);
88 */
89static inline void putstr(const char *s)
90{
91 while (*s)
92 putchar(*(s++));
93}
94
95static void start_standout(uint32_t color)
96{
97 uint8_t bg;
98 uint8_t attr;
99
100 console_flush(console);
101 if (use_rgb) {
102 console_set_rgb_color(console, color, 0xffffff);
103 } else if (use_color) {
104 bg = 0x00;
105 attr = 0;
106 if ((color & 0xff0000) != 0)
107 bg |= 0x4;
108 if ((color & 0x00ff00) != 0)
109 bg |= 0x2;
110 if ((color & 0x0000ff) != 0)
111 bg |= 0x1;
112 console_set_color(console, bg, 0x00, attr);
113 }
114}
115
116static void resume_normal(void)
117{
118 console_flush(console);
119 console_set_style(console, STYLE_NORMAL);
120}
121
122void clear_screen(void)
123{
124 console_clear(console);
125 moveto(0, 0);
126}
127
128/*
129 * Clear the screen, forgetting the current contents in the process.
130 */
131void scr_clear(void)
132{
133 resume_normal();
134 console_clear(console);
135 curscore = -1;
136 memset(curscreen, 0, sizeof(curscreen));
137}
138
139/*
140 * Set up screen
141 */
142void scr_init(void)
143{
144 console_cursor_visibility(console, 0);
145 resume_normal();
146 scr_set();
147}
148
149void moveto(sysarg_t r, sysarg_t c)
150{
151 console_flush(console);
152 console_set_pos(console, c, r);
153}
154
155winsize_t winsize;
156
157static errno_t get_display_size(winsize_t *ws)
158{
159 return console_get_size(console, &ws->ws_col, &ws->ws_row);
160}
161
162static void get_display_color_sup(bool *rgb, bool *color)
163{
164 sysarg_t ccap;
165 errno_t rc = console_get_color_cap(console, &ccap);
166
167 if (rc != EOK) {
168 *rgb = false;
169 *color = false;
170 return;
171 }
172
173 if ((ccap & CONSOLE_CAP_CURSORCTL) == 0) {
174 stop("Your screen does not support cursor control.\n");
175 return;
176 }
177 *rgb = ((ccap & CONSOLE_CAP_RGB) == CONSOLE_CAP_RGB);
178 *color = ((ccap & CONSOLE_CAP_INDEXED) == CONSOLE_CAP_INDEXED);
179}
180
181/*
182 * Set up screen mode.
183 */
184void scr_set(void)
185{
186 winsize_t ws;
187
188 Rows = 0;
189 Cols = 0;
190
191 if (get_display_size(&ws) == 0) {
192 Rows = ws.ws_row;
193 Cols = ws.ws_col;
194 }
195
196 get_display_color_sup(&use_rgb, &use_color);
197
198 if ((Rows < MINROWS) || (Cols < MINCOLS)) {
199 char smallscr[55];
200
201 snprintf(smallscr, sizeof(smallscr),
202 "the screen is too small (must be at least %dx%d)\n",
203 MINROWS, MINCOLS);
204 stop(smallscr);
205 }
206 isset = 1;
207
208 scr_clear();
209}
210
211/*
212 * End screen mode.
213 */
214void scr_end(void)
215{
216 console_cursor_visibility(console, 1);
217}
218
219void stop(const char *why)
220{
221 if (isset)
222 scr_end();
223
224 fprintf(stderr, "aborting: %s", why);
225 exit(1);
226}
227
228/*
229 * Update the screen.
230 */
231void scr_update(void)
232{
233 cell *bp;
234 cell *sp;
235 cell so;
236 cell cur_so = 0;
237 int i;
238 int j;
239 int ccol;
240
241 /* Always leave cursor after last displayed point */
242 curscreen[D_LAST * B_COLS - 1] = -1;
243
244 if (score != curscore) {
245 moveto(0, 0);
246 printf("Score: %d", score);
247 curscore = score;
248 }
249
250 /* Draw preview of next pattern */
251 if ((showpreview) && (nextshape != lastshape)) {
252 int i;
253 static int r = 5, c = 2;
254 int tr, tc, t;
255
256 lastshape = nextshape;
257
258 /* Clean */
259 resume_normal();
260 moveto(r - 1, c - 1);
261 putstr(" ");
262 moveto(r, c - 1);
263 putstr(" ");
264 moveto(r + 1, c - 1);
265 putstr(" ");
266 moveto(r + 2, c - 1);
267 putstr(" ");
268
269 moveto(r - 3, c - 2);
270 putstr("Next shape:");
271
272 /* Draw */
273 start_standout(nextshape->color);
274 moveto(r, 2 * c);
275 putstr(" ");
276 for (i = 0; i < 3; i++) {
277 t = c + r * B_COLS;
278 t += nextshape->off[i];
279
280 tr = t / B_COLS;
281 tc = t % B_COLS;
282
283 moveto(tr, 2 * tc);
284 putstr(" ");
285 }
286 resume_normal();
287 }
288
289 bp = &board[D_FIRST * B_COLS];
290 sp = &curscreen[D_FIRST * B_COLS];
291 for (j = D_FIRST; j < D_LAST; j++) {
292 ccol = -1;
293 for (i = 0; i < B_COLS; bp++, sp++, i++) {
294 if (*sp == (so = *bp))
295 continue;
296
297 *sp = so;
298 if (i != ccol) {
299 if (cur_so) {
300 resume_normal();
301 cur_so = 0;
302 }
303 moveto(RTOD(j), CTOD(i));
304 }
305
306 if (so != cur_so) {
307 if (so)
308 start_standout(so);
309 else
310 resume_normal();
311 cur_so = so;
312 }
313 putstr(" ");
314
315 ccol = i + 1;
316 /*
317 * Look ahead a bit, to avoid extra motion if
318 * we will be redrawing the cell after the next.
319 * Motion probably takes four or more characters,
320 * so we save even if we rewrite two cells
321 * `unnecessarily'. Skip it all, though, if
322 * the next cell is a different color.
323 */
324
325 if ((i > STOP) || (sp[1] != bp[1]) || (so != bp[1]))
326 continue;
327
328 if (sp[2] != bp[2])
329 sp[1] = -1;
330 else if ((i < STOP) && (so == bp[2]) && (sp[3] != bp[3])) {
331 sp[2] = -1;
332 sp[1] = -1;
333 }
334 }
335 }
336
337 if (cur_so)
338 resume_normal();
339
340 console_flush(console);
341}
342
343/*
344 * Write a message (set != 0), or clear the same message (set == 0).
345 * (We need its length in case we have to overwrite with blanks.)
346 */
347void scr_msg(char *s, bool set)
348{
349 int l = str_size(s);
350
351 moveto(Rows - 2, ((Cols - l) >> 1) - 1);
352
353 if (set)
354 putstr(s);
355 else
356 while (--l >= 0)
357 (void) putchar(' ');
358}
359
360/** Sleep for the current turn time
361 *
362 * Eat any input that might be available.
363 *
364 */
365void tsleep(void)
366{
367 usec_t timeout = fallrate;
368 errno_t rc;
369
370 while (timeout > 0) {
371 cons_event_t event;
372
373 rc = console_get_event_timeout(console, &event, &timeout);
374 if (rc == ETIMEOUT)
375 break;
376 if (rc != EOK)
377 exit(1);
378 }
379}
380
381/** Get char with timeout
382 *
383 */
384int tgetchar(void)
385{
386 errno_t rc;
387
388 /*
389 * Reset timeleft to fallrate whenever it is not positive
390 * and increase speed.
391 */
392
393 if (timeleft <= 0) {
394 faster();
395 timeleft = fallrate;
396 }
397
398 /*
399 * Wait to see if there is any input. If so, take it and
400 * update timeleft so that the next call to tgetchar()
401 * will not wait as long. If there is no input,
402 * make timeleft zero and return -1.
403 */
404
405 char32_t c = 0;
406
407 while (c == 0) {
408 cons_event_t event;
409
410 rc = console_get_event_timeout(console, &event, &timeleft);
411 if (rc == ETIMEOUT) {
412 timeleft = 0;
413 return -1;
414 }
415 if (rc != EOK)
416 exit(1);
417
418 if (event.type == CEV_RESIZE)
419 size_changed = true;
420
421 if (event.type == CEV_KEY && event.ev.key.type == KEY_PRESS)
422 c = event.ev.key.c;
423 }
424
425 return (int) c;
426}
427
428/** Get char without timeout
429 *
430 */
431int twait(void)
432{
433 char32_t c = 0;
434 errno_t rc;
435
436 while (c == 0) {
437 cons_event_t event;
438
439 rc = console_get_event(console, &event);
440 if (rc == ETIMEOUT)
441 return -1;
442 if (rc != EOK)
443 exit(1);
444
445 if (event.type == CEV_KEY && event.ev.key.type == KEY_PRESS)
446 c = event.ev.key.c;
447 }
448
449 return (int) c;
450}
451
452/** @}
453 */
Note: See TracBrowser for help on using the repository browser.