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

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

Tetris should be able to detect screen cursor control capability

  • Property mode set to 100644
File size: 8.8 KB
RevLine 
[79ae36dd]1/*
[09f41d3]2 * Copyright (c) 2024 Jiri Svoboda
[79ae36dd]3 * Copyright (c) 2011 Martin Decky
4 * All rights reserved.
[e9a3c52]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 *
[79ae36dd]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.
[e9a3c52]44 *
45 */
46
[b2951e2]47/** @addtogroup tetris
[ebe70f1]48 * @{
[b2951e2]49 */
50/** @file
51 */
52
[e9a3c52]53/*
54 * Tetris screen control.
55 */
56
[87822ce]57#include <errno.h>
[e9a3c52]58#include <stdio.h>
59#include <stdlib.h>
[19f857a]60#include <str.h>
[0c25c10]61#include <vfs/vfs.h>
[f1b4e74]62#include <async.h>
[3e6a98c5]63#include <stdbool.h>
[9f1362d4]64#include <io/console.h>
65#include <io/style.h>
[e9a3c52]66#include "screen.h"
67#include "tetris.h"
68
[ebe70f1]69#define STOP (B_COLS - 3)
70
71static cell curscreen[B_SIZE]; /* non-zero => standout (or otherwise marked) */
[e9a3c52]72static int curscore;
[ebe70f1]73static int isset; /* true => terminal is in game mode */
74
[e116461]75static bool use_rgb; /* true => use RGB colors */
76static bool use_color; /* true => use indexed colors */
[50cfa6c]77
[ebe70f1]78static const struct shape *lastshape;
[f1b4e74]79
[bd41ac52]80static usec_t timeleft = 0;
[79ae36dd]81
[899bdfd]82bool size_changed;
[79ae36dd]83console_ctrl_t *console;
84
[e9a3c52]85/*
[f1b4e74]86 * putstr() is for unpadded strings (either as in termcap(5) or
[0c25c10]87 * simply literal strings);
[e9a3c52]88 */
[a000878c]89static inline void putstr(const char *s)
[59ed572]90{
91 while (*s)
92 putchar(*(s++));
93}
[e9a3c52]94
[ebe70f1]95static void start_standout(uint32_t color)
[e9a3c52]96{
[e116461]97 uint8_t bg;
98 uint8_t attr;
99
[79ae36dd]100 console_flush(console);
[e116461]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 }
[f1b4e74]114}
[e9a3c52]115
[f1b4e74]116static void resume_normal(void)
117{
[79ae36dd]118 console_flush(console);
119 console_set_style(console, STYLE_NORMAL);
[e9a3c52]120}
121
[9996ed5]122void clear_screen(void)
123{
[79ae36dd]124 console_clear(console);
[0cc4313]125 moveto(0, 0);
[9996ed5]126}
127
[e9a3c52]128/*
[f1b4e74]129 * Clear the screen, forgetting the current contents in the process.
[e9a3c52]130 */
[ebe70f1]131void scr_clear(void)
[f1b4e74]132{
[d6cc453]133 resume_normal();
[79ae36dd]134 console_clear(console);
[f1b4e74]135 curscore = -1;
[ebe70f1]136 memset(curscreen, 0, sizeof(curscreen));
[f1b4e74]137}
[b917098]138
[e9a3c52]139/*
[f1b4e74]140 * Set up screen
[e9a3c52]141 */
[ebe70f1]142void scr_init(void)
[e9a3c52]143{
[79ae36dd]144 console_cursor_visibility(console, 0);
[f1b4e74]145 resume_normal();
[09f41d3]146 scr_set();
[e9a3c52]147}
148
[96b02eb9]149void moveto(sysarg_t r, sysarg_t c)
[f1b4e74]150{
[79ae36dd]151 console_flush(console);
152 console_set_pos(console, c, r);
[f1b4e74]153}
154
[9996ed5]155winsize_t winsize;
[f1b4e74]156
[b7fd2a0]157static errno_t get_display_size(winsize_t *ws)
[f1b4e74]158{
[79ae36dd]159 return console_get_size(console, &ws->ws_col, &ws->ws_row);
[f1b4e74]160}
[e9a3c52]161
[e116461]162static void get_display_color_sup(bool *rgb, bool *color)
[50cfa6c]163{
[96b02eb9]164 sysarg_t ccap;
[b7fd2a0]165 errno_t rc = console_get_color_cap(console, &ccap);
[a35b458]166
[e116461]167 if (rc != EOK) {
168 *rgb = false;
169 *color = false;
170 return;
171 }
[a35b458]172
[09f41d3]173 if ((ccap & CONSOLE_CAP_CURSORCTL) == 0) {
174 stop("Your screen does not support cursor control.\n");
175 return;
176 }
[e116461]177 *rgb = ((ccap & CONSOLE_CAP_RGB) == CONSOLE_CAP_RGB);
178 *color = ((ccap & CONSOLE_CAP_INDEXED) == CONSOLE_CAP_INDEXED);
[50cfa6c]179}
180
[e9a3c52]181/*
182 * Set up screen mode.
183 */
[ebe70f1]184void scr_set(void)
[e9a3c52]185{
[9996ed5]186 winsize_t ws;
[a35b458]187
[ebe70f1]188 Rows = 0;
189 Cols = 0;
[a35b458]190
[f1b4e74]191 if (get_display_size(&ws) == 0) {
[e9a3c52]192 Rows = ws.ws_row;
193 Cols = ws.ws_col;
194 }
[50cfa6c]195
[e116461]196 get_display_color_sup(&use_rgb, &use_color);
[a35b458]197
[ebe70f1]198 if ((Rows < MINROWS) || (Cols < MINCOLS)) {
[e9a3c52]199 char smallscr[55];
[a35b458]200
[86029498]201 snprintf(smallscr, sizeof(smallscr),
[09f41d3]202 "the screen is too small (must be at least %dx%d)\n",
[e9a3c52]203 MINROWS, MINCOLS);
204 stop(smallscr);
205 }
206 isset = 1;
[a35b458]207
[e9a3c52]208 scr_clear();
209}
210
211/*
212 * End screen mode.
213 */
[ebe70f1]214void scr_end(void)
[e9a3c52]215{
[79ae36dd]216 console_cursor_visibility(console, 1);
[e9a3c52]217}
218
[a000878c]219void stop(const char *why)
[e9a3c52]220{
221 if (isset)
222 scr_end();
[a35b458]223
[f538ef3]224 fprintf(stderr, "aborting: %s", why);
[899bdfd]225 exit(1);
[e9a3c52]226}
227
228/*
229 * Update the screen.
230 */
[ebe70f1]231void scr_update(void)
[e9a3c52]232{
[ebe70f1]233 cell *bp;
234 cell *sp;
235 cell so;
236 cell cur_so = 0;
237 int i;
238 int j;
239 int ccol;
[a35b458]240
[ebe70f1]241 /* Always leave cursor after last displayed point */
[e9a3c52]242 curscreen[D_LAST * B_COLS - 1] = -1;
[a35b458]243
[e9a3c52]244 if (score != curscore) {
[f1b4e74]245 moveto(0, 0);
[86029498]246 printf("Score: %d", score);
[e9a3c52]247 curscore = score;
248 }
[a35b458]249
[ebe70f1]250 /* Draw preview of next pattern */
251 if ((showpreview) && (nextshape != lastshape)) {
[e9a3c52]252 int i;
[ebe70f1]253 static int r = 5, c = 2;
[e9a3c52]254 int tr, tc, t;
[a35b458]255
[e9a3c52]256 lastshape = nextshape;
[a35b458]257
[ebe70f1]258 /* Clean */
[f1b4e74]259 resume_normal();
[ebe70f1]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(" ");
[a35b458]268
[ebe70f1]269 moveto(r - 3, c - 2);
[e9a3c52]270 putstr("Next shape:");
[a35b458]271
[ebe70f1]272 /* Draw */
273 start_standout(nextshape->color);
[e9a3c52]274 moveto(r, 2 * c);
[f1b4e74]275 putstr(" ");
[e9a3c52]276 for (i = 0; i < 3; i++) {
277 t = c + r * B_COLS;
278 t += nextshape->off[i];
[a35b458]279
[e9a3c52]280 tr = t / B_COLS;
281 tc = t % B_COLS;
[a35b458]282
[1433ecda]283 moveto(tr, 2 * tc);
[f1b4e74]284 putstr(" ");
[e9a3c52]285 }
[f1b4e74]286 resume_normal();
[e9a3c52]287 }
[a35b458]288
[e9a3c52]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;
[a35b458]296
[e9a3c52]297 *sp = so;
298 if (i != ccol) {
[86029498]299 if (cur_so) {
300 resume_normal();
301 cur_so = 0;
302 }
[e9a3c52]303 moveto(RTOD(j), CTOD(i));
304 }
[a35b458]305
[f1b4e74]306 if (so != cur_so) {
307 if (so)
[ebe70f1]308 start_standout(so);
[f1b4e74]309 else
310 resume_normal();
311 cur_so = so;
312 }
313 putstr(" ");
[a35b458]314
[e9a3c52]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 */
[a35b458]324
[ebe70f1]325 if ((i > STOP) || (sp[1] != bp[1]) || (so != bp[1]))
[e9a3c52]326 continue;
[a35b458]327
[e9a3c52]328 if (sp[2] != bp[2])
329 sp[1] = -1;
[ebe70f1]330 else if ((i < STOP) && (so == bp[2]) && (sp[3] != bp[3])) {
[e9a3c52]331 sp[2] = -1;
332 sp[1] = -1;
333 }
334 }
335 }
[a35b458]336
[86029498]337 if (cur_so)
338 resume_normal();
[a35b458]339
[79ae36dd]340 console_flush(console);
[e9a3c52]341}
342
343/*
[ebe70f1]344 * Write a message (set != 0), or clear the same message (set == 0).
[e9a3c52]345 * (We need its length in case we have to overwrite with blanks.)
346 */
[9f1362d4]347void scr_msg(char *s, bool set)
[e9a3c52]348{
[92fd52d7]349 int l = str_size(s);
[a35b458]350
[d6cc453]351 moveto(Rows - 2, ((Cols - l) >> 1) - 1);
[a35b458]352
[d6cc453]353 if (set)
354 putstr(s);
355 else
356 while (--l >= 0)
357 (void) putchar(' ');
[e9a3c52]358}
[b2951e2]359
[79ae36dd]360/** Sleep for the current turn time
361 *
362 * Eat any input that might be available.
363 *
364 */
365void tsleep(void)
366{
[bd41ac52]367 usec_t timeout = fallrate;
[87822ce]368 errno_t rc;
[a35b458]369
[79ae36dd]370 while (timeout > 0) {
[07b7c48]371 cons_event_t event;
[a35b458]372
[87822ce]373 rc = console_get_event_timeout(console, &event, &timeout);
374 if (rc == ETIMEOUT)
[79ae36dd]375 break;
[87822ce]376 if (rc != EOK)
377 exit(1);
[79ae36dd]378 }
379}
380
381/** Get char with timeout
382 *
383 */
384int tgetchar(void)
385{
[87822ce]386 errno_t rc;
387
[79ae36dd]388 /*
389 * Reset timeleft to fallrate whenever it is not positive
390 * and increase speed.
391 */
[a35b458]392
[79ae36dd]393 if (timeleft <= 0) {
394 faster();
395 timeleft = fallrate;
396 }
[a35b458]397
[79ae36dd]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 */
[a35b458]404
[28a5ebd]405 char32_t c = 0;
[a35b458]406
[79ae36dd]407 while (c == 0) {
[07b7c48]408 cons_event_t event;
[a35b458]409
[87822ce]410 rc = console_get_event_timeout(console, &event, &timeleft);
411 if (rc == ETIMEOUT) {
[79ae36dd]412 timeleft = 0;
413 return -1;
414 }
[87822ce]415 if (rc != EOK)
416 exit(1);
[a35b458]417
[899bdfd]418 if (event.type == CEV_RESIZE)
419 size_changed = true;
420
[07b7c48]421 if (event.type == CEV_KEY && event.ev.key.type == KEY_PRESS)
422 c = event.ev.key.c;
[79ae36dd]423 }
[a35b458]424
[79ae36dd]425 return (int) c;
426}
427
428/** Get char without timeout
429 *
430 */
431int twait(void)
432{
[28a5ebd]433 char32_t c = 0;
[87822ce]434 errno_t rc;
[a35b458]435
[79ae36dd]436 while (c == 0) {
[07b7c48]437 cons_event_t event;
[a35b458]438
[87822ce]439 rc = console_get_event(console, &event);
440 if (rc == ETIMEOUT)
[79ae36dd]441 return -1;
[87822ce]442 if (rc != EOK)
443 exit(1);
[a35b458]444
[07b7c48]445 if (event.type == CEV_KEY && event.ev.key.type == KEY_PRESS)
446 c = event.ev.key.c;
[79ae36dd]447 }
[a35b458]448
[79ae36dd]449 return (int) c;
450}
451
[b2951e2]452/** @}
453 */
Note: See TracBrowser for help on using the repository browser.