1/*-
2 * Copyright (c) 2015 Conrad Meyer <cem@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/param.h>
28#include <sys/callout.h>
29#include <sys/cons.h>
30#include <sys/lock.h>
31#include <sys/kernel.h>
32#include <sys/mutex.h>
33#include <sys/smp.h>
34#include <sys/systm.h>
35#include <sys/terminal.h>
36
37#include <dev/vt/vt.h>
38
39extern const unsigned char vt_beastie_vga16[];
40extern const unsigned char vt_beastie2_vga16[];
41extern const unsigned char vt_orb_vga16[];
42
43static struct timeout_task vt_splash_cpu_fini_task;
44
45static inline unsigned char
46vt_vga2bsd(unsigned char vga)
47{
48	static const unsigned char lut[8] = {
49		0,
50		4,	/* 1 and 4 swap */
51		2,
52		6,	/* 3 and 6 swap */
53		1,	/* 4 and 1 swap */
54		5,
55		3,	/* 6 and 3 swap */
56		7,
57	};
58	unsigned int bright;
59
60	bright = (vga & 0x8);
61	return (lut[vga & 0x7] | bright);
62}
63
64static void
65vt_draw_2_vga16_px(struct vt_device *vd, vt_axis_t x, vt_axis_t y,
66    unsigned char color)
67{
68
69	vd->vd_driver->vd_setpixel(vd, x, y, vt_vga2bsd(color >> 4));
70	vd->vd_driver->vd_setpixel(vd, x + 1, y, vt_vga2bsd(color & 0xf));
71}
72
73static void
74vt_draw_1_logo(struct vt_device *vd, vt_axis_t top, vt_axis_t left)
75{
76	const unsigned char rle_sent = 0x16, *data;
77	unsigned int xy, run, runcolor, i;
78
79	switch (vt_splash_cpu_style) {
80	case VT_LOGOS_DRAW_ALT_BEASTIE:
81		data = vt_beastie2_vga16;
82		break;
83	case VT_LOGOS_DRAW_ORB:
84		data = vt_orb_vga16;
85		break;
86	case VT_LOGOS_DRAW_BEASTIE:
87		/* FALLTHROUGH */
88	default:
89		data = vt_beastie_vga16;
90		break;
91	}
92
93	/* Decode basic RLE (gets us to 30-40% of uncompressed data size): */
94	for (i = 0, xy = 0; xy < vt_logo_sprite_height * vt_logo_sprite_width;) {
95		if (data[i] == rle_sent) {
96			runcolor = data[i + 1];
97			run = data[i + 2];
98
99			for (; run; run--, xy += 2)
100				vt_draw_2_vga16_px(vd,
101				    left + (xy % vt_logo_sprite_width),
102				    top + (xy / vt_logo_sprite_width),
103				    runcolor);
104
105			i += 3;
106		} else {
107			vt_draw_2_vga16_px(vd, left + (xy % vt_logo_sprite_width),
108			    top + (xy / vt_logo_sprite_width), data[i]);
109
110			i++;
111			xy += 2;
112		}
113	}
114}
115
116void
117vtterm_draw_cpu_logos(struct vt_device *vd)
118{
119	unsigned int ncpu, i;
120	vt_axis_t left;
121	struct terminal *tm = vd->vd_curwindow->vw_terminal;
122	const teken_attr_t *a;
123
124	if (vt_splash_ncpu)
125		ncpu = vt_splash_ncpu;
126	else {
127		ncpu = mp_ncpus;
128		if (ncpu < 1)
129			ncpu = 1;
130	}
131
132	a = teken_get_curattr(&tm->tm_emulator);
133	if (vd->vd_driver->vd_drawrect)
134		vd->vd_driver->vd_drawrect(vd, 0, 0, vd->vd_width - 1,
135		    vt_logo_sprite_height - 1, 1, a->ta_bgcolor);
136	/*
137	 * Blank is okay because we only ever draw beasties on full screen
138	 * refreshes.
139	 */
140	else if (vd->vd_driver->vd_blank)
141		vd->vd_driver->vd_blank(vd, a->ta_bgcolor);
142
143	ncpu = MIN(ncpu, vd->vd_width / vt_logo_sprite_width);
144	for (i = 0, left = 0; i < ncpu; left += vt_logo_sprite_width, i++)
145		vt_draw_1_logo(vd, 0, left);
146}
147
148static void
149vt_fini_logos(void *dummy __unused, int pending __unused)
150{
151	struct vt_device *vd;
152	struct vt_window *vw;
153	struct terminal *tm;
154	struct vt_font *vf;
155	struct winsize wsz;
156	term_pos_t size;
157	unsigned int i;
158
159	if (!vt_draw_logo_cpus)
160		return;
161	if (!vty_enabled(VTY_VT))
162		return;
163	if (!vt_splash_cpu)
164		return;
165
166	vd = &vt_consdev;
167	VT_LOCK(vd);
168	if ((vd->vd_flags & (VDF_DEAD | VDF_TEXTMODE)) != 0) {
169		VT_UNLOCK(vd);
170		return;
171	}
172	vt_draw_logo_cpus = 0;
173	VT_UNLOCK(vd);
174
175	for (i = 0; i < VT_MAXWINDOWS; i++) {
176		vw = vd->vd_windows[i];
177		if (vw == NULL)
178			continue;
179		tm = vw->vw_terminal;
180		vf = vw->vw_font;
181		if (vf == NULL)
182			continue;
183
184		vt_termsize(vd, vf, &size);
185		vt_winsize(vd, vf, &wsz);
186
187		/* Resize screen buffer and terminal. */
188		terminal_mute(tm, 1);
189		vtbuf_grow(&vw->vw_buf, &size, vw->vw_buf.vb_history_size);
190		terminal_set_winsize_blank(tm, &wsz, 0, NULL);
191		terminal_set_cursor(tm, &vw->vw_buf.vb_cursor);
192		terminal_mute(tm, 0);
193
194		VT_LOCK(vd);
195		vt_compute_drawable_area(vw);
196
197		if (vd->vd_curwindow == vw) {
198			vd->vd_flags |= VDF_INVALID;
199			vt_resume_flush_timer(vw, 0);
200		}
201		VT_UNLOCK(vd);
202	}
203}
204
205static void
206vt_init_logos(void *dummy)
207{
208	struct vt_device *vd;
209	struct vt_window *vw;
210	struct terminal *tm;
211	struct vt_font *vf;
212	struct winsize wsz;
213	term_pos_t size;
214
215	if (!vty_enabled(VTY_VT))
216		return;
217	if (!vt_splash_cpu)
218		return;
219
220	vd = &vt_consdev;
221	if (vd == NULL)
222		return;
223	vw = vd->vd_curwindow;
224	if (vw == NULL)
225		return;
226	tm = vw->vw_terminal;
227	if (tm == NULL)
228		return;
229	vf = vw->vw_font;
230	if (vf == NULL)
231		return;
232
233	VT_LOCK(vd);
234	if ((vd->vd_flags & VDF_INITIALIZED) == 0)
235		goto out;
236	if ((vd->vd_flags & (VDF_DEAD | VDF_TEXTMODE)) != 0)
237		goto out;
238	if (vd->vd_height <= vt_logo_sprite_height)
239		goto out;
240
241	vt_draw_logo_cpus = 1;
242	VT_UNLOCK(vd);
243
244	vt_termsize(vd, vf, &size);
245	vt_winsize(vd, vf, &wsz);
246
247	/* Resize screen buffer and terminal. */
248	terminal_mute(tm, 1);
249	vtbuf_grow(&vw->vw_buf, &size, vw->vw_buf.vb_history_size);
250	terminal_set_winsize_blank(tm, &wsz, 0, NULL);
251	terminal_set_cursor(tm, &vw->vw_buf.vb_cursor);
252	terminal_mute(tm, 0);
253
254	VT_LOCK(vd);
255	vt_compute_drawable_area(vw);
256
257	if (vd->vd_curwindow == vw) {
258		vd->vd_flags |= VDF_INVALID;
259		vt_resume_flush_timer(vw, 0);
260	}
261
262	TIMEOUT_TASK_INIT(taskqueue_thread, &vt_splash_cpu_fini_task, 0,
263	    vt_fini_logos, NULL);
264	taskqueue_enqueue_timeout(taskqueue_thread, &vt_splash_cpu_fini_task,
265	    vt_splash_cpu_duration * hz);
266
267out:
268	VT_UNLOCK(vd);
269}
270SYSINIT(vt_logos, SI_SUB_TASKQ, SI_ORDER_ANY, vt_init_logos, NULL);
271