1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 1997 Sandro Sigala, Brescia, Italy.
5 * Copyright (c) 1997 Chris Shenton
6 * Copyright (c) 1995 S��ren Schmidt
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer
14 *    in this position and unchanged.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * $FreeBSD$
31 */
32
33#include <sys/param.h>
34#include <sys/systm.h>
35#include <sys/module.h>
36#include <sys/malloc.h>
37#include <sys/jail.h>
38#include <sys/kernel.h>
39#include <sys/sysctl.h>
40#include <sys/consio.h>
41#include <sys/fbio.h>
42
43#include <machine/pc/display.h>
44
45#include <dev/fb/fbreg.h>
46#include <dev/fb/splashreg.h>
47#include <dev/syscons/syscons.h>
48
49#define DAEMON_MAX_WIDTH	32
50#define DAEMON_MAX_HEIGHT	19
51
52static u_char *message;
53static int messagelen;
54static int blanked;
55static int attr_mask;
56
57#define	ATTR(attr)	(((attr) & attr_mask) << 8)
58
59/* Who is the author of this ASCII pic? */
60
61static u_char *daemon_pic[] = {
62        "             ,        ,",
63	"            /(        )`",
64	"            \\ \\___   / |",
65	"            /- _  `-/  '",
66	"           (/\\/ \\ \\   /\\",
67	"           / /   | `    \\",
68	"           O O   ) /    |",
69	"           `-^--'`<     '",
70	"          (_.)  _  )   /",
71	"           `.___/`    /",
72	"             `-----' /",
73	"<----.     __ / __   \\",
74	"<----|====O)))==) \\) /====",
75	"<----'    `--' `.__,' \\",
76	"             |        |",
77	"              \\       /       /\\",
78	"         ______( (_  / \\______/",
79	"       ,'  ,-----'   |",
80	"       `--{__________)",
81	NULL
82};
83
84static u_char *daemon_attr[] = {
85        "             R        R",
86	"            RR        RR",
87	"            R RRRR   R R",
88	"            RR W  RRR  R",
89	"           RWWW W R   RR",
90	"           W W   W R    R",
91	"           B B   W R    R",
92	"           WWWWWWRR     R",
93	"          RRRR  R  R   R",
94	"           RRRRRRR    R",
95	"             RRRRRRR R",
96	"YYYYYY     RR R RR   R",
97	"YYYYYYYYYYRRRRYYR RR RYYYY",
98	"YYYYYY    RRRR RRRRRR R",
99	"             R        R",
100	"              R       R       RR",
101	"         CCCCCCR RR  R RRRRRRRR",
102	"       CC  CCCCCCC   C",
103	"       CCCCCCCCCCCCCCC",
104	NULL
105};
106
107/*
108 * Reverse a graphics character, or return unaltered if no mirror;
109 * should do alphanumerics too, but I'm too lazy. <cshenton@it.hq.nasa.gov>
110 */
111
112static u_char
113xflip_symbol(u_char symbol)
114{
115	static const u_char lchars[] = "`'(){}[]\\/<>";
116	static const u_char rchars[] = "'`)(}{][/\\><";
117	int pos;
118
119	for (pos = 0; lchars[pos] != '\0'; pos++)
120		if (lchars[pos] == symbol)
121			return rchars[pos];
122
123	return symbol;
124}
125
126static void
127clear_daemon(sc_softc_t *sc, int xpos, int ypos, int dxdir, int xoff, int yoff,
128	    int xlen, int ylen)
129{
130	int y;
131
132	if (xlen <= 0)
133		return;
134	for (y = yoff; y < ylen; y++) {
135		sc_vtb_erase(&sc->cur_scp->scr,
136			     (ypos + y)*sc->cur_scp->xsize + xpos + xoff,
137			     xlen - xoff,
138			     sc->scr_map[0x20], ATTR(FG_LIGHTGREY | BG_BLACK));
139	}
140}
141
142static void
143draw_daemon(sc_softc_t *sc, int xpos, int ypos, int dxdir, int xoff, int yoff,
144	    int xlen, int ylen)
145{
146	int x, y;
147	int px;
148	int attr;
149
150	for (y = yoff; y < ylen; y++) {
151		if (dxdir < 0)
152			px = xoff;
153		else
154			px = DAEMON_MAX_WIDTH - xlen;
155		if (px >= strlen(daemon_pic[y]))
156			continue;
157		for (x = xoff; (x < xlen) && (daemon_pic[y][px] != '\0'); x++, px++) {
158			switch (daemon_attr[y][px]) {
159			case 'R': attr = FG_LIGHTRED | BG_BLACK; break;
160			case 'Y': attr = FG_YELLOW | BG_BLACK; break;
161			case 'B': attr = FG_LIGHTBLUE | BG_BLACK; break;
162			case 'W': attr = FG_LIGHTGREY | BG_BLACK; break;
163			case 'C': attr = FG_CYAN | BG_BLACK; break;
164			default: attr = FG_WHITE | BG_BLACK; break;
165			}
166			if (dxdir < 0) {	/* Moving left */
167				sc_vtb_putc(&sc->cur_scp->scr,
168					    (ypos + y)*sc->cur_scp->xsize
169						 + xpos + x,
170					    sc->scr_map[daemon_pic[y][px]],
171					    ATTR(attr));
172			} else {		/* Moving right */
173				sc_vtb_putc(&sc->cur_scp->scr,
174					    (ypos + y)*sc->cur_scp->xsize
175						+ xpos + DAEMON_MAX_WIDTH
176						- px - 1,
177					    sc->scr_map[xflip_symbol(daemon_pic[y][px])],
178					    ATTR(attr));
179			}
180		}
181	}
182}
183
184static void
185clear_string(sc_softc_t *sc, int xpos, int ypos, int xoff, char *s, int len)
186{
187	if (len <= 0)
188		return;
189	sc_vtb_erase(&sc->cur_scp->scr,
190		     ypos*sc->cur_scp->xsize + xpos + xoff, len - xoff,
191		     sc->scr_map[0x20], ATTR(FG_LIGHTGREY | BG_BLACK));
192}
193
194static void
195draw_string(sc_softc_t *sc, int xpos, int ypos, int xoff, u_char *s, int len)
196{
197	int x;
198
199	for (x = xoff; x < len; x++)
200		sc_vtb_putc(&sc->cur_scp->scr,
201			    ypos*sc->cur_scp->xsize + xpos + x,
202			    sc->scr_map[s[x]], ATTR(FG_LIGHTGREEN | BG_BLACK));
203}
204
205static int
206daemon_saver(video_adapter_t *adp, int blank)
207{
208	static int txpos = 10, typos = 10;
209	static int txdir = -1, tydir = -1;
210	static int dxpos = 0, dypos = 0;
211	static int dxdir = 1, dydir = 1;
212	static int moved_daemon = 0;
213	static int xoff, yoff, toff;
214	static int xlen, ylen, tlen;
215	sc_softc_t *sc;
216	scr_stat *scp;
217	int min, max;
218
219	sc = sc_find_softc(adp, NULL);
220	if (sc == NULL)
221		return EAGAIN;
222	scp = sc->cur_scp;
223
224	if (blank) {
225		if (adp->va_info.vi_flags & V_INFO_GRAPHICS)
226			return EAGAIN;
227		if (blanked == 0) {
228			/* clear the screen and set the border color */
229			sc_vtb_clear(&scp->scr, sc->scr_map[0x20],
230				     ATTR(FG_LIGHTGREY | BG_BLACK));
231			vidd_set_hw_cursor(adp, -1, -1);
232			sc_set_border(scp, 0);
233			xlen = ylen = tlen = 0;
234		}
235		if (blanked++ < 2)
236			return 0;
237		blanked = 1;
238
239 		clear_daemon(sc, dxpos, dypos, dxdir, xoff, yoff, xlen, ylen);
240		clear_string(sc, txpos, typos, toff, message, tlen);
241
242		if (++moved_daemon) {
243			/*
244			 * The daemon picture may be off the screen, if
245			 * screen size is chagened while the screen
246			 * saver is inactive. Make sure the origin of
247			 * the picture is between min and max.
248			 */
249			if (scp->xsize <= DAEMON_MAX_WIDTH) {
250				/*
251				 * If the screen width is too narrow, we
252				 * allow part of the picture go off
253				 * the screen so that the daemon won't
254				 * flip too often.
255				 */
256				min = scp->xsize - DAEMON_MAX_WIDTH - 10;
257				max = 10;
258			} else {
259				min = 0;
260				max = scp->xsize - DAEMON_MAX_WIDTH;
261			}
262			if (dxpos <= min) {
263				dxpos = min;
264				dxdir = 1;
265			} else if (dxpos >= max) {
266				dxpos = max;
267				dxdir = -1;
268			}
269
270			if (scp->ysize <= DAEMON_MAX_HEIGHT) {
271				min = scp->ysize - DAEMON_MAX_HEIGHT - 10;
272				max = 10;
273			} else {
274				min = 0;
275				max = scp->ysize - DAEMON_MAX_HEIGHT;
276			}
277			if (dypos <= min) {
278				dypos = min;
279				dydir = 1;
280			} else if (dypos >= max) {
281				dypos = max;
282				dydir = -1;
283			}
284
285			moved_daemon = -1;
286			dxpos += dxdir; dypos += dydir;
287
288			/* clip the picture */
289			xoff = 0;
290			xlen = DAEMON_MAX_WIDTH;
291			if (dxpos + xlen <= 0)
292				xlen = 0;
293			else if (dxpos < 0)
294				xoff = -dxpos;
295			if (dxpos >= scp->xsize)
296				xlen = 0;
297			else if (dxpos + xlen > scp->xsize)
298				xlen = scp->xsize - dxpos;
299			yoff = 0;
300			ylen = DAEMON_MAX_HEIGHT;
301			if (dypos + ylen <= 0)
302				ylen = 0;
303			else if (dypos < 0)
304				yoff = -dypos;
305			if (dypos >= scp->ysize)
306				ylen = 0;
307			else if (dypos + ylen > scp->ysize)
308				ylen = scp->ysize - dypos;
309		}
310
311		if (scp->xsize <= messagelen) {
312			min = scp->xsize - messagelen - 10;
313			max = 10;
314		} else {
315			min = 0;
316			max = scp->xsize - messagelen;
317		}
318		if (txpos <= min) {
319			txpos = min;
320			txdir = 1;
321		} else if (txpos >= max) {
322			txpos = max;
323			txdir = -1;
324		}
325		if (typos <= 0) {
326			typos = 0;
327			tydir = 1;
328		} else if (typos >= scp->ysize - 1) {
329			typos = scp->ysize - 1;
330			tydir = -1;
331		}
332		txpos += txdir; typos += tydir;
333
334		toff = 0;
335		tlen = messagelen;
336		if (txpos + tlen <= 0)
337			tlen = 0;
338		else if (txpos < 0)
339			toff = -txpos;
340		if (txpos >= scp->xsize)
341			tlen = 0;
342		else if (txpos + tlen > scp->xsize)
343			tlen = scp->xsize - txpos;
344
345 		draw_daemon(sc, dxpos, dypos, dxdir, xoff, yoff, xlen, ylen);
346		draw_string(sc, txpos, typos, toff, message, tlen);
347	} else
348		blanked = 0;
349
350	return 0;
351}
352
353static int
354daemon_init(video_adapter_t *adp)
355{
356	size_t hostlen;
357
358	mtx_lock(&prison0.pr_mtx);
359	for (;;) {
360		hostlen = strlen(prison0.pr_hostname);
361		mtx_unlock(&prison0.pr_mtx);
362
363		messagelen = hostlen + 3 + strlen(ostype) + 1 +
364		    strlen(osrelease);
365		message = malloc(messagelen + 1, M_DEVBUF, M_WAITOK);
366		mtx_lock(&prison0.pr_mtx);
367		if (hostlen < strlen(prison0.pr_hostname)) {
368			free(message, M_DEVBUF);
369			continue;
370		}
371		break;
372	}
373	sprintf(message, "%s - %s %s", prison0.pr_hostname, ostype, osrelease);
374	mtx_unlock(&prison0.pr_mtx);
375	blanked = 0;
376	attr_mask = ~0;
377
378	return 0;
379}
380
381static int
382daemon_term(video_adapter_t *adp)
383{
384	free(message, M_DEVBUF);
385	return 0;
386}
387
388static scrn_saver_t daemon_module = {
389	"daemon_saver", daemon_init, daemon_term, daemon_saver, NULL,
390};
391
392SAVER_MODULE(daemon_saver, daemon_module);
393