vt_core.c revision 258781
1219888Sed/*-
2257547Sray * Copyright (c) 2009, 2013 The FreeBSD Foundation
3219888Sed * All rights reserved.
4219888Sed *
5219888Sed * This software was developed by Ed Schouten under sponsorship from the
6219888Sed * FreeBSD Foundation.
7219888Sed *
8257547Sray * Portions of this software were developed by Oleksandr Rybalko
9257547Sray * under sponsorship from the FreeBSD Foundation.
10257547Sray *
11219888Sed * Redistribution and use in source and binary forms, with or without
12219888Sed * modification, are permitted provided that the following conditions
13219888Sed * are met:
14219888Sed * 1. Redistributions of source code must retain the above copyright
15219888Sed *    notice, this list of conditions and the following disclaimer.
16219888Sed * 2. Redistributions in binary form must reproduce the above copyright
17219888Sed *    notice, this list of conditions and the following disclaimer in the
18219888Sed *    documentation and/or other materials provided with the distribution.
19219888Sed *
20219888Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21219888Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22219888Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23219888Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24219888Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25219888Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26219888Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27219888Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28219888Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29219888Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30219888Sed * SUCH DAMAGE.
31219888Sed */
32219888Sed
33219888Sed#include <sys/cdefs.h>
34219888Sed__FBSDID("$FreeBSD: user/ed/newcons/sys/dev/vt/vt_core.c 258781 2013-11-30 22:46:43Z nwhitehorn $");
35219888Sed
36219888Sed#include <sys/param.h>
37219888Sed#include <sys/consio.h>
38219888Sed#include <sys/eventhandler.h>
39219888Sed#include <sys/fbio.h>
40219888Sed#include <sys/kbio.h>
41219888Sed#include <sys/kdb.h>
42219888Sed#include <sys/kernel.h>
43219888Sed#include <sys/lock.h>
44219888Sed#include <sys/malloc.h>
45219888Sed#include <sys/mutex.h>
46256145Sray#include <sys/proc.h>
47219888Sed#include <sys/reboot.h>
48219888Sed#include <sys/systm.h>
49219888Sed#include <sys/terminal.h>
50219888Sed
51219888Sed#include <dev/kbd/kbdreg.h>
52219888Sed#include <dev/vt/vt.h>
53219888Sed
54219888Sedstatic tc_bell_t	vtterm_bell;
55219888Sedstatic tc_cursor_t	vtterm_cursor;
56219888Sedstatic tc_putchar_t	vtterm_putchar;
57219888Sedstatic tc_fill_t	vtterm_fill;
58219888Sedstatic tc_copy_t	vtterm_copy;
59219888Sedstatic tc_param_t	vtterm_param;
60219888Sedstatic tc_done_t	vtterm_done;
61219888Sed
62219888Sedstatic tc_cnprobe_t	vtterm_cnprobe;
63219888Sedstatic tc_cngetc_t	vtterm_cngetc;
64219888Sed
65219888Sedstatic tc_opened_t	vtterm_opened;
66219888Sedstatic tc_ioctl_t	vtterm_ioctl;
67219888Sed
68219888Sedconst struct terminal_class vt_termclass = {
69219888Sed	.tc_bell	= vtterm_bell,
70219888Sed	.tc_cursor	= vtterm_cursor,
71219888Sed	.tc_putchar	= vtterm_putchar,
72219888Sed	.tc_fill	= vtterm_fill,
73219888Sed	.tc_copy	= vtterm_copy,
74219888Sed	.tc_param	= vtterm_param,
75219888Sed	.tc_done	= vtterm_done,
76219888Sed
77219888Sed	.tc_cnprobe	= vtterm_cnprobe,
78219888Sed	.tc_cngetc	= vtterm_cngetc,
79219888Sed
80219888Sed	.tc_opened	= vtterm_opened,
81219888Sed	.tc_ioctl	= vtterm_ioctl,
82219888Sed};
83219888Sed
84219888Sed/*
85257296Snwhitehorn * Use a constant timer of 25 Hz to redraw the screen.
86219888Sed *
87219888Sed * XXX: In theory we should only fire up the timer when there is really
88219888Sed * activity. Unfortunately we cannot always start timers. We really
89219888Sed * don't want to process kernel messages synchronously, because it
90219888Sed * really slows down the system.
91219888Sed */
92257296Snwhitehorn#define	VT_TIMERFREQ	25
93219888Sed
94219888Sed/* Bell pitch/duration. */
95219888Sed#define VT_BELLDURATION	((5 * hz + 99) / 100)
96219888Sed#define VT_BELLPITCH	800
97219888Sed
98219888Sed#define	VT_LOCK(vd)	mtx_lock(&(vd)->vd_lock)
99219888Sed#define	VT_UNLOCK(vd)	mtx_unlock(&(vd)->vd_lock)
100219888Sed
101219888Sed#define	VT_UNIT(vw)	((vw)->vw_device->vd_unit * VT_MAXWINDOWS + \
102219888Sed			(vw)->vw_number)
103219888Sed
104256145Sray/* XXX while syscons is here. */
105256145Srayint sc_txtmouse_no_retrace_wait;
106256145Sray
107256145Sraystatic SYSCTL_NODE(_kern, OID_AUTO, vt, CTLFLAG_RD, 0, "Newcons parameters");
108256145SrayVT_SYSCTL_INT(debug, 0, "Newcons debug level");
109256145SrayVT_SYSCTL_INT(deadtimer, 15, "Time to wait busy process in VT_PROCESS mode");
110258023SrayVT_SYSCTL_INT(suspendswitch, 1, "Switch to VT0 before suspend");
111256145Sray
112219888Sedstatic unsigned int vt_unit = 0;
113219888Sedstatic MALLOC_DEFINE(M_VT, "vt", "vt device");
114256145Sraystruct vt_device *main_vd = NULL;
115219888Sed
116219888Sed/* Boot logo. */
117219888Sedextern unsigned int vt_logo_width;
118219888Sedextern unsigned int vt_logo_height;
119219888Sedextern unsigned int vt_logo_depth;
120219888Sedextern unsigned char vt_logo_image[];
121219888Sed
122219888Sed/* Font. */
123219888Sedextern struct vt_font vt_font_default;
124257986Srayextern struct mouse_cursor vt_default_mouse_pointer;
125219888Sed
126256145Sraystatic int signal_vt_rel(struct vt_window *);
127256145Sraystatic int signal_vt_acq(struct vt_window *);
128256145Sraystatic int finish_vt_rel(struct vt_window *, int, int *);
129256145Sraystatic int finish_vt_acq(struct vt_window *);
130256145Sraystatic int vt_window_switch(struct vt_window *);
131256145Sraystatic int vt_late_window_switch(struct vt_window *);
132256145Sraystatic int vt_proc_alive(struct vt_window *);
133256145Sraystatic void vt_resize(struct vt_device *);
134256145Sray
135219888Sedstatic void
136256145Srayvt_switch_timer(void *arg)
137256145Sray{
138256145Sray
139256145Sray	vt_late_window_switch((struct vt_window *)arg);
140256145Sray}
141256145Sray
142256145Sraystatic int
143256145Srayvt_window_preswitch(struct vt_window *vw, struct vt_window *curvw)
144256145Sray{
145256145Sray
146256145Sray	DPRINTF(40, "%s\n", __func__);
147256145Sray	curvw->vw_switch_to = vw;
148256145Sray	/* Set timer to allow switch in case when process hang. */
149256145Sray	callout_reset(&vw->vw_proc_dead_timer, hz * vt_deadtimer,
150256145Sray	    vt_switch_timer, (void *)vw);
151256145Sray	/* Notify process about vt switch attempt. */
152256145Sray	DPRINTF(30, "%s: Notify process.\n", __func__);
153256145Sray	signal_vt_rel(curvw);
154256145Sray
155256145Sray	return (0);
156256145Sray}
157256145Sray
158256145Sraystatic int
159256145Srayvt_window_postswitch(struct vt_window *vw)
160256145Sray{
161256145Sray
162256145Sray	signal_vt_acq(vw);
163256145Sray	return (0);
164256145Sray}
165256145Sray
166256145Sray/* vt_late_window_switch will done VT switching for regular case. */
167256145Sraystatic int
168256145Srayvt_late_window_switch(struct vt_window *vw)
169256145Sray{
170256145Sray	int ret;
171256145Sray
172256145Sray	callout_stop(&vw->vw_proc_dead_timer);
173256145Sray
174256145Sray	ret = vt_window_switch(vw);
175256145Sray	if (ret)
176256145Sray		return (ret);
177256145Sray
178256145Sray	/* Notify owner process about terminal availability. */
179256145Sray	if (vw->vw_smode.mode == VT_PROCESS) {
180256145Sray		ret = vt_window_postswitch(vw);
181256145Sray	}
182256145Sray	return (ret);
183256145Sray}
184256145Sray
185256145Sray/* Switch window. */
186256145Sraystatic int
187256145Srayvt_proc_window_switch(struct vt_window *vw)
188256145Sray{
189256145Sray	struct vt_window *curvw;
190256145Sray	struct vt_device *vd;
191256145Sray	int ret;
192256145Sray
193256145Sray	if (vw->vw_flags & VWF_VTYLOCK)
194256145Sray		return (EBUSY);
195256145Sray
196256145Sray	vd = vw->vw_device;
197256145Sray	curvw = vd->vd_curwindow;
198256145Sray
199256145Sray	/* Ask current process permitions to switch away. */
200256145Sray	if (curvw->vw_smode.mode == VT_PROCESS) {
201256145Sray		DPRINTF(30, "%s: VT_PROCESS ", __func__);
202256145Sray		if (vt_proc_alive(curvw) == FALSE) {
203256145Sray			DPRINTF(30, "Dead. Cleaning.");
204256145Sray			/* Dead */
205256145Sray		} else {
206256145Sray			DPRINTF(30, "%s: Signaling process.\n", __func__);
207256145Sray			/* Alive, try to ask him. */
208256145Sray			ret = vt_window_preswitch(vw, curvw);
209256145Sray			/* Wait for process answer or timeout. */
210256145Sray			return (ret);
211256145Sray		}
212256145Sray		DPRINTF(30, "\n");
213256145Sray	}
214256145Sray
215256145Sray	ret = vt_late_window_switch(vw);
216256145Sray	return (ret);
217256145Sray}
218256145Sray
219256145Sray/* Switch window ignoring process locking. */
220256145Sraystatic int
221219888Sedvt_window_switch(struct vt_window *vw)
222219888Sed{
223219888Sed	struct vt_device *vd = vw->vw_device;
224256145Sray	struct vt_window *curvw = vd->vd_curwindow;
225219888Sed	keyboard_t *kbd;
226219888Sed
227219888Sed	VT_LOCK(vd);
228256145Sray	if (curvw == vw) {
229256145Sray		/* Nothing to do. */
230219888Sed		VT_UNLOCK(vd);
231256145Sray		return (0);
232219888Sed	}
233256145Sray	if (!(vw->vw_flags & (VWF_OPENED|VWF_CONSOLE))) {
234256145Sray		VT_UNLOCK(vd);
235256145Sray		return (EINVAL);
236256145Sray	}
237256145Sray
238219888Sed	vd->vd_curwindow = vw;
239219888Sed	vd->vd_flags |= VDF_INVALID;
240219888Sed	cv_broadcast(&vd->vd_winswitch);
241219888Sed	VT_UNLOCK(vd);
242219888Sed
243256145Sray	if (vd->vd_driver->vd_postswitch)
244256145Sray		vd->vd_driver->vd_postswitch(vd);
245256145Sray
246219888Sed	/* Restore per-window keyboard mode. */
247219888Sed	mtx_lock(&Giant);
248219888Sed	kbd = kbd_get_keyboard(vd->vd_keyboard);
249256145Sray	if (kbd != NULL) {
250219888Sed		kbdd_ioctl(kbd, KDSKBMODE, (void *)&vw->vw_kbdmode);
251256145Sray	}
252219888Sed	mtx_unlock(&Giant);
253256145Sray	DPRINTF(10, "%s(ttyv%d) done\n", __func__, vw->vw_number);
254256145Sray
255256145Sray	return (0);
256219888Sed}
257219888Sed
258219888Sedstatic inline void
259219888Sedvt_termsize(struct vt_device *vd, struct vt_font *vf, term_pos_t *size)
260219888Sed{
261219888Sed
262219888Sed	size->tp_row = vd->vd_height;
263219888Sed	size->tp_col = vd->vd_width;
264219888Sed	if (vf != NULL) {
265219888Sed		size->tp_row /= vf->vf_height;
266219888Sed		size->tp_col /= vf->vf_width;
267219888Sed	}
268219888Sed}
269219888Sed
270219888Sedstatic inline void
271219888Sedvt_winsize(struct vt_device *vd, struct vt_font *vf, struct winsize *size)
272219888Sed{
273219888Sed
274219888Sed	size->ws_row = size->ws_ypixel = vd->vd_height;
275219888Sed	size->ws_col = size->ws_xpixel = vd->vd_width;
276219888Sed	if (vf != NULL) {
277219888Sed		size->ws_row /= vf->vf_height;
278219888Sed		size->ws_col /= vf->vf_width;
279219888Sed	}
280219888Sed}
281219888Sed
282257076Sraystatic void
283257076Srayvt_scroll(struct vt_window *vw, int offset, int whence)
284257076Sray{
285257076Sray	int diff;
286257076Sray	term_pos_t size;
287257076Sray
288257076Sray	if ((vw->vw_flags & VWF_SCROLL) == 0)
289257076Sray		return;
290257076Sray
291257076Sray	vt_termsize(vw->vw_device, vw->vw_font, &size);
292257076Sray
293257076Sray	diff = vthistory_seek(&vw->vw_buf, offset, whence);
294257076Sray	/*
295257076Sray	 * Offset changed, please update Nth lines on sceen.
296257076Sray	 * +N - Nth lines at top;
297257076Sray	 * -N - Nth lines at bottom.
298257076Sray	 */
299257076Sray
300257076Sray	if (diff < -size.tp_row || diff > size.tp_row) {
301257076Sray		vw->vw_device->vd_flags |= VDF_INVALID;
302257076Sray		return;
303257076Sray	}
304257076Sray	vw->vw_device->vd_flags |= VDF_INVALID; /*XXX*/
305257076Sray}
306257076Sray
307219888Sedstatic int
308257076Srayvt_machine_kbdevent(int c)
309257076Sray{
310257076Sray
311257076Sray	switch (c) {
312257076Sray	case SPCLKEY | DBG:
313257076Sray		kdb_enter(KDB_WHY_BREAK, "manual escape to debugger");
314257076Sray		return (1);
315257076Sray	case SPCLKEY | RBT:
316257076Sray		/* XXX: Make this configurable! */
317257076Sray		shutdown_nice(0);
318257076Sray		return (1);
319257076Sray	case SPCLKEY | HALT:
320257076Sray		shutdown_nice(RB_HALT);
321257076Sray		return (1);
322257076Sray	case SPCLKEY | PDWN:
323257076Sray		shutdown_nice(RB_HALT|RB_POWEROFF);
324257076Sray		return (1);
325257076Sray	};
326257076Sray
327257076Sray	return (0);
328257076Sray}
329257076Sray
330257076Sraystatic void
331257076Srayvt_scrollmode_kbdevent(struct vt_window *vw, int c, int console)
332257076Sray{
333257076Sray	struct vt_device *vd;
334257076Sray	term_pos_t size;
335257076Sray
336257076Sray	vd = vw->vw_device;
337257076Sray	/* Only special keys handled in ScrollLock mode */
338257076Sray	if ((c & SPCLKEY) == 0)
339257076Sray		return;
340257076Sray
341257076Sray	c &= ~SPCLKEY;
342257076Sray
343257076Sray	if (console == 0) {
344257076Sray		if (c >= F_SCR && c <= MIN(L_SCR, F_SCR + VT_MAXWINDOWS - 1)) {
345257076Sray			vw = vd->vd_windows[c - F_SCR];
346257076Sray			if (vw != NULL)
347257076Sray				vt_proc_window_switch(vw);
348257076Sray			return;
349257076Sray		}
350257076Sray		VT_LOCK(vd);
351257076Sray	}
352257076Sray
353257076Sray	switch (c) {
354257076Sray	case SLK: {
355257076Sray		/* Turn scrolling off. */
356257076Sray		vt_scroll(vw, 0, VHS_END);
357257076Sray		VTBUF_SLCK_DISABLE(&vw->vw_buf);
358258408Sray		vw->vw_flags &= ~VWF_SCROLL;
359257076Sray		break;
360257076Sray	}
361257076Sray	case FKEY | F(49): /* Home key. */
362258506Sray		vt_scroll(vw, 0, VHS_SET);
363257076Sray		break;
364257076Sray	case FKEY | F(50): /* Arrow up. */
365257076Sray		vt_scroll(vw, -1, VHS_CUR);
366257076Sray		break;
367257076Sray	case FKEY | F(51): /* Page up. */
368257076Sray		vt_termsize(vd, vw->vw_font, &size);
369257076Sray		vt_scroll(vw, -size.tp_row, VHS_CUR);
370257076Sray		break;
371257076Sray	case FKEY | F(57): /* End key. */
372258506Sray		vt_scroll(vw, 0, VHS_END);
373257076Sray		break;
374257076Sray	case FKEY | F(58): /* Arrow down. */
375257076Sray		vt_scroll(vw, 1, VHS_CUR);
376257076Sray		break;
377257076Sray	case FKEY | F(59): /* Page down. */
378257076Sray		vt_termsize(vd, vw->vw_font, &size);
379257076Sray		vt_scroll(vw, size.tp_row, VHS_CUR);
380257076Sray		break;
381257076Sray	}
382257076Sray
383257076Sray	if (console == 0)
384257076Sray		VT_UNLOCK(vd);
385257076Sray}
386257076Sray
387257076Sraystatic int
388257294Snwhitehornvt_processkey(keyboard_t *kbd, struct vt_device *vd, int c)
389219888Sed{
390219888Sed	struct vt_window *vw = vd->vd_curwindow;
391257294Snwhitehorn	int state = 0;
392219888Sed
393258165Sray#if VT_ALT_TO_ESC_HACK
394258165Sray	if (c & RELKEY) {
395258165Sray		switch (c & ~RELKEY) {
396258165Sray		case (SPCLKEY | RALT):
397258165Sray		case (SPCLKEY | LALT):
398258165Sray			vd->vd_kbstate &= ~ALKED;
399258165Sray		}
400258165Sray		/* Other keys ignored for RELKEY event. */
401258165Sray		return (0);
402258165Sray	} else {
403258165Sray		switch (c & ~RELKEY) {
404258165Sray		case (SPCLKEY | RALT):
405258165Sray		case (SPCLKEY | LALT):
406258165Sray			vd->vd_kbstate |= ALKED;
407258165Sray		}
408258165Sray	}
409258165Sray#else
410219888Sed	if (c & RELKEY)
411258165Sray		/* Other keys ignored for RELKEY event. */
412219888Sed		return (0);
413258165Sray#endif
414219888Sed
415257076Sray	if (vt_machine_kbdevent(c))
416257076Sray		return (0);
417257076Sray
418257076Sray	if (vw->vw_flags & VWF_SCROLL) {
419257076Sray		vt_scrollmode_kbdevent(vw, c, 0/* Not a console */);
420257076Sray		/* Scroll mode keys handled, nothing to do more. */
421257076Sray		return (0);
422257076Sray	}
423257076Sray
424219888Sed	if (c & SPCLKEY) {
425219888Sed		c &= ~SPCLKEY;
426219888Sed
427219888Sed		if (c >= F_SCR && c <= MIN(L_SCR, F_SCR + VT_MAXWINDOWS - 1)) {
428219888Sed			vw = vd->vd_windows[c - F_SCR];
429219888Sed			if (vw != NULL)
430256145Sray				vt_proc_window_switch(vw);
431219888Sed			return (0);
432219888Sed		}
433219888Sed
434219888Sed		switch (c) {
435219888Sed		case SLK: {
436219888Sed
437219888Sed			kbdd_ioctl(kbd, KDGKBSTATE, (caddr_t)&state);
438219888Sed			VT_LOCK(vd);
439219888Sed			if (state & SLKED) {
440219888Sed				/* Turn scrolling on. */
441258408Sray				vw->vw_flags |= VWF_SCROLL;
442256145Sray				VTBUF_SLCK_ENABLE(&vw->vw_buf);
443219888Sed			} else {
444219888Sed				/* Turn scrolling off. */
445258408Sray				vw->vw_flags &= ~VWF_SCROLL;
446256145Sray				VTBUF_SLCK_DISABLE(&vw->vw_buf);
447257076Sray				vt_scroll(vw, 0, VHS_END);
448219888Sed			}
449219888Sed			VT_UNLOCK(vd);
450219888Sed			break;
451219888Sed		}
452219888Sed		case FKEY | F(1):  case FKEY | F(2):  case FKEY | F(3):
453219888Sed		case FKEY | F(4):  case FKEY | F(5):  case FKEY | F(6):
454219888Sed		case FKEY | F(7):  case FKEY | F(8):  case FKEY | F(9):
455219888Sed		case FKEY | F(10): case FKEY | F(11): case FKEY | F(12):
456219888Sed			/* F1 through F12 keys. */
457219888Sed			terminal_input_special(vw->vw_terminal,
458219888Sed			    TKEY_F1 + c - (FKEY | F(1)));
459219888Sed			break;
460219888Sed		case FKEY | F(49): /* Home key. */
461219888Sed			terminal_input_special(vw->vw_terminal, TKEY_HOME);
462219888Sed			break;
463219888Sed		case FKEY | F(50): /* Arrow up. */
464219888Sed			terminal_input_special(vw->vw_terminal, TKEY_UP);
465219888Sed			break;
466219888Sed		case FKEY | F(51): /* Page up. */
467219888Sed			terminal_input_special(vw->vw_terminal, TKEY_PAGE_UP);
468219888Sed			break;
469219888Sed		case FKEY | F(53): /* Arrow left. */
470219888Sed			terminal_input_special(vw->vw_terminal, TKEY_LEFT);
471219888Sed			break;
472219888Sed		case FKEY | F(55): /* Arrow right. */
473219888Sed			terminal_input_special(vw->vw_terminal, TKEY_RIGHT);
474219888Sed			break;
475219888Sed		case FKEY | F(57): /* End key. */
476219888Sed			terminal_input_special(vw->vw_terminal, TKEY_END);
477219888Sed			break;
478219888Sed		case FKEY | F(58): /* Arrow down. */
479219888Sed			terminal_input_special(vw->vw_terminal, TKEY_DOWN);
480219888Sed			break;
481219888Sed		case FKEY | F(59): /* Page down. */
482219888Sed			terminal_input_special(vw->vw_terminal, TKEY_PAGE_DOWN);
483219888Sed			break;
484219888Sed		case FKEY | F(60): /* Insert key. */
485219888Sed			terminal_input_special(vw->vw_terminal, TKEY_INSERT);
486219888Sed			break;
487219888Sed		case FKEY | F(61): /* Delete key. */
488219888Sed			terminal_input_special(vw->vw_terminal, TKEY_DELETE);
489219888Sed			break;
490219888Sed		}
491219888Sed	} else if (KEYFLAGS(c) == 0) {
492219888Sed		/* Don't do UTF-8 conversion when doing raw mode. */
493258165Sray		if (vw->vw_kbdmode == K_XLATE) {
494258165Sray#if VT_ALT_TO_ESC_HACK
495258165Sray			if (vd->vd_kbstate & ALKED) {
496258165Sray				/*
497258165Sray				 * Prepend ESC sequence if one of ALT keys down.
498258165Sray				 */
499258165Sray				terminal_input_char(vw->vw_terminal, 0x1b);
500258165Sray			}
501258165Sray#endif
502258165Sray
503256145Sray			terminal_input_char(vw->vw_terminal, KEYCHAR(c));
504258165Sray		} else
505219888Sed			terminal_input_raw(vw->vw_terminal, c);
506219888Sed	}
507219888Sed	return (0);
508219888Sed}
509219888Sed
510219888Sedstatic int
511257294Snwhitehornvt_kbdevent(keyboard_t *kbd, int event, void *arg)
512257294Snwhitehorn{
513257294Snwhitehorn	struct vt_device *vd = arg;
514257294Snwhitehorn	int c;
515257294Snwhitehorn
516257294Snwhitehorn	switch (event) {
517257294Snwhitehorn	case KBDIO_KEYINPUT:
518257294Snwhitehorn		break;
519257294Snwhitehorn	case KBDIO_UNLOADING:
520257294Snwhitehorn		mtx_lock(&Giant);
521257294Snwhitehorn		vd->vd_keyboard = -1;
522257294Snwhitehorn		kbd_release(kbd, (void *)&vd->vd_keyboard);
523257294Snwhitehorn		mtx_unlock(&Giant);
524257294Snwhitehorn		return (0);
525257294Snwhitehorn	default:
526257294Snwhitehorn		return (EINVAL);
527257294Snwhitehorn	}
528257294Snwhitehorn
529257294Snwhitehorn	while ((c = kbdd_read_char(kbd, 0)) != NOKEY)
530257294Snwhitehorn		vt_processkey(kbd, vd, c);
531257294Snwhitehorn
532257294Snwhitehorn	return (0);
533257294Snwhitehorn}
534257294Snwhitehorn
535257294Snwhitehornstatic int
536219888Sedvt_allocate_keyboard(struct vt_device *vd)
537219888Sed{
538219888Sed	int		 idx0, idx;
539219888Sed	keyboard_t	*k0, *k;
540219888Sed	keyboard_info_t	 ki;
541219888Sed
542219888Sed	idx0 = kbd_allocate("kbdmux", -1, (void *)&vd->vd_keyboard,
543219888Sed	    vt_kbdevent, vd);
544256145Sray	/* XXX: kb_token lost */
545256145Sray	vd->vd_keyboard = idx0;
546219888Sed	if (idx0 != -1) {
547256145Sray		DPRINTF(20, "%s: kbdmux allocated, idx = %d\n", __func__, idx0);
548219888Sed		k0 = kbd_get_keyboard(idx0);
549219888Sed
550219888Sed		for (idx = kbd_find_keyboard2("*", -1, 0);
551219888Sed		     idx != -1;
552219888Sed		     idx = kbd_find_keyboard2("*", -1, idx + 1)) {
553219888Sed			k = kbd_get_keyboard(idx);
554219888Sed
555219888Sed			if (idx == idx0 || KBD_IS_BUSY(k))
556219888Sed				continue;
557219888Sed
558219888Sed			bzero(&ki, sizeof(ki));
559219888Sed			strcpy(ki.kb_name, k->kb_name);
560219888Sed			ki.kb_unit = k->kb_unit;
561219888Sed
562219888Sed			kbdd_ioctl(k0, KBADDKBD, (caddr_t) &ki);
563219888Sed		}
564256145Sray	} else {
565256145Sray		DPRINTF(20, "%s: no kbdmux allocated\n", __func__);
566219888Sed		idx0 = kbd_allocate("*", -1, (void *)&vd->vd_keyboard,
567219888Sed		    vt_kbdevent, vd);
568256145Sray	}
569256145Sray	DPRINTF(20, "%s: vd_keyboard = %d\n", __func__, vd->vd_keyboard);
570219888Sed
571219888Sed	return (idx0);
572219888Sed}
573219888Sed
574219888Sedstatic void
575219888Sedvtterm_bell(struct terminal *tm)
576219888Sed{
577219888Sed
578219888Sed	sysbeep(1193182 / VT_BELLPITCH, VT_BELLDURATION);
579219888Sed}
580219888Sed
581219888Sedstatic void
582219888Sedvtterm_cursor(struct terminal *tm, const term_pos_t *p)
583219888Sed{
584219888Sed	struct vt_window *vw = tm->tm_softc;
585219888Sed
586219888Sed	vtbuf_cursor_position(&vw->vw_buf, p);
587219888Sed}
588219888Sed
589219888Sedstatic void
590219888Sedvtterm_putchar(struct terminal *tm, const term_pos_t *p, term_char_t c)
591219888Sed{
592219888Sed	struct vt_window *vw = tm->tm_softc;
593219888Sed
594219888Sed	vtbuf_putchar(&vw->vw_buf, p, c);
595219888Sed}
596219888Sed
597219888Sedstatic void
598219888Sedvtterm_fill(struct terminal *tm, const term_rect_t *r, term_char_t c)
599219888Sed{
600219888Sed	struct vt_window *vw = tm->tm_softc;
601219888Sed
602256145Sray	vtbuf_fill_locked(&vw->vw_buf, r, c);
603219888Sed}
604219888Sed
605219888Sedstatic void
606219888Sedvtterm_copy(struct terminal *tm, const term_rect_t *r,
607219888Sed    const term_pos_t *p)
608219888Sed{
609219888Sed	struct vt_window *vw = tm->tm_softc;
610219888Sed
611219888Sed	vtbuf_copy(&vw->vw_buf, r, p);
612219888Sed}
613219888Sed
614219888Sedstatic void
615219888Sedvtterm_param(struct terminal *tm, int cmd, unsigned int arg)
616219888Sed{
617219888Sed	struct vt_window *vw = tm->tm_softc;
618219888Sed
619219888Sed	switch (cmd) {
620219888Sed	case TP_SHOWCURSOR:
621219888Sed		vtbuf_cursor_visibility(&vw->vw_buf, arg);
622219888Sed		break;
623219888Sed	}
624219888Sed}
625219888Sed
626219888Sedstatic inline void
627219888Sedvt_determine_colors(term_char_t c, int cursor,
628219888Sed    term_color_t *fg, term_color_t *bg)
629219888Sed{
630219888Sed
631219888Sed	*fg = TCHAR_FGCOLOR(c);
632219888Sed	if (TCHAR_FORMAT(c) & TF_BOLD)
633219888Sed		*fg = TCOLOR_LIGHT(*fg);
634219888Sed	*bg = TCHAR_BGCOLOR(c);
635219888Sed
636219888Sed	if (TCHAR_FORMAT(c) & TF_REVERSE) {
637219888Sed		term_color_t tmp;
638219888Sed
639219888Sed		tmp = *fg;
640219888Sed		*fg = *bg;
641219888Sed		*bg = tmp;
642219888Sed	}
643219888Sed
644219888Sed	if (cursor) {
645219888Sed		*fg = *bg;
646219888Sed		*bg = TC_WHITE;
647219888Sed	}
648219888Sed}
649219888Sed
650219888Sedstatic void
651219888Sedvt_bitblt_char(struct vt_device *vd, struct vt_font *vf, term_char_t c,
652219888Sed    int iscursor, unsigned int row, unsigned int col)
653219888Sed{
654219888Sed	term_color_t fg, bg;
655219888Sed
656219888Sed	vt_determine_colors(c, iscursor, &fg, &bg);
657219888Sed
658219888Sed	if (vf != NULL) {
659219888Sed		const uint8_t *src;
660219888Sed		vt_axis_t top, left;
661219888Sed
662219888Sed		src = vtfont_lookup(vf, c);
663219888Sed
664219888Sed		/*
665219888Sed		 * Align the terminal to the centre of the screen.
666219888Sed		 * Fonts may not always be able to fill the entire
667219888Sed		 * screen.
668219888Sed		 */
669257979Sray		top = row * vf->vf_height + vd->vd_offset.tp_row;
670257979Sray		left = col * vf->vf_width + vd->vd_offset.tp_col;
671219888Sed
672257988Sray		vd->vd_driver->vd_bitbltchr(vd, src, NULL, 0, top, left,
673219888Sed		    vf->vf_width, vf->vf_height, fg, bg);
674219888Sed	} else {
675219888Sed		vd->vd_driver->vd_putchar(vd, TCHAR_CHARACTER(c),
676219888Sed		    row, col, fg, bg);
677219888Sed	}
678219888Sed}
679219888Sed
680219888Sedstatic void
681219888Sedvt_flush(struct vt_device *vd)
682219888Sed{
683219888Sed	struct vt_window *vw = vd->vd_curwindow;
684219888Sed	struct vt_font *vf = vw->vw_font;
685219888Sed	struct vt_bufmask tmask;
686257981Sray	struct mouse_cursor *m;
687256145Sray	unsigned int row, col;
688257981Sray	term_rect_t tarea;
689257981Sray	term_pos_t size;
690256145Sray	term_char_t *r;
691257988Sray	int bpl, h, w;
692219888Sed
693219888Sed	if (vd->vd_flags & VDF_SPLASH || vw->vw_flags & VWF_BUSY)
694219888Sed		return;
695219888Sed
696219888Sed	vtbuf_undirty(&vw->vw_buf, &tarea, &tmask);
697219888Sed	vt_termsize(vd, vf, &size);
698219888Sed
699219888Sed	/* Force a full redraw when the screen contents are invalid. */
700256145Sray	if (vd->vd_flags & VDF_INVALID) {
701219888Sed		tarea.tr_begin.tp_row = tarea.tr_begin.tp_col = 0;
702219888Sed		tarea.tr_end = size;
703219888Sed		tmask.vbm_row = tmask.vbm_col = VBM_DIRTY;
704219888Sed
705219888Sed		vd->vd_flags &= ~VDF_INVALID;
706219888Sed	}
707219888Sed
708258327Sray	if ((vw->vw_flags & VWF_MOUSE_HIDE) == 0) {
709258327Sray		/* Mark last mouse position as dirty to erase. */
710258327Sray		vtbuf_mouse_cursor_position(&vw->vw_buf, vd->vd_mdirtyx,
711258327Sray		    vd->vd_mdirtyy);
712258327Sray	}
713257981Sray
714219888Sed	for (row = tarea.tr_begin.tp_row; row < tarea.tr_end.tp_row; row++) {
715219888Sed		if (!VTBUF_DIRTYROW(&tmask, row))
716219888Sed			continue;
717256145Sray		r = VTBUF_GET_ROW(&vw->vw_buf, row);
718219888Sed		for (col = tarea.tr_begin.tp_col;
719219888Sed		    col < tarea.tr_end.tp_col; col++) {
720219888Sed			if (!VTBUF_DIRTYCOL(&tmask, col))
721219888Sed				continue;
722219888Sed
723256145Sray			vt_bitblt_char(vd, vf, r[col],
724256903Sray			    VTBUF_ISCURSOR(&vw->vw_buf, row, col), row, col);
725219888Sed		}
726219888Sed	}
727257981Sray
728258327Sray	/* Mouse disabled. */
729258327Sray	if (vw->vw_flags & VWF_MOUSE_HIDE)
730258327Sray		return;
731258327Sray
732258110Sray	/* No mouse for DDB. */
733258110Sray	if (kdb_active || panicstr != NULL)
734258110Sray		return;
735258110Sray
736257981Sray	if ((vd->vd_flags & (VDF_MOUSECURSOR|VDF_TEXTMODE)) ==
737257981Sray	    VDF_MOUSECURSOR) {
738257981Sray		m = &vt_default_mouse_pointer;
739257988Sray		bpl = (m->w + 7) >> 3; /* Bytes per sorce line. */
740257981Sray		w = m->w;
741257981Sray		h = m->h;
742257981Sray
743257981Sray		if ((vd->vd_mx + m->w) > (size.tp_col * vf->vf_width))
744257981Sray			w = (size.tp_col * vf->vf_width) - vd->vd_mx - 1;
745257981Sray		if ((vd->vd_my + m->h) > (size.tp_row * vf->vf_height))
746257981Sray			h = (size.tp_row * vf->vf_height) - vd->vd_my - 1;
747257981Sray
748257988Sray		vd->vd_driver->vd_bitbltchr(vd, m->map, m->mask, bpl,
749257981Sray		    vd->vd_offset.tp_row + vd->vd_my,
750257981Sray		    vd->vd_offset.tp_col + vd->vd_mx,
751257981Sray		    w, h, TC_WHITE, TC_BLACK);
752257981Sray		/* Save point of last mouse cursor to erase it later. */
753257981Sray		vd->vd_mdirtyx = vd->vd_mx / vf->vf_width;
754257981Sray		vd->vd_mdirtyy = vd->vd_my / vf->vf_height;
755257981Sray	}
756219888Sed}
757219888Sed
758219888Sedstatic void
759219888Sedvt_timer(void *arg)
760219888Sed{
761257387Sray	struct vt_device *vd;
762219888Sed
763257387Sray	vd = arg;
764257387Sray	/* Update screen if required. */
765219888Sed	vt_flush(vd);
766257387Sray	/* Schedule for next update. */
767219888Sed	callout_schedule(&vd->vd_timer, hz / VT_TIMERFREQ);
768219888Sed}
769219888Sed
770219888Sedstatic void
771219888Sedvtterm_done(struct terminal *tm)
772219888Sed{
773219888Sed	struct vt_window *vw = tm->tm_softc;
774219888Sed	struct vt_device *vd = vw->vw_device;
775219888Sed
776219888Sed	if (kdb_active || panicstr != NULL) {
777219888Sed		/* Switch to the debugger. */
778219888Sed		if (vd->vd_curwindow != vw) {
779219888Sed			vd->vd_curwindow = vw;
780219888Sed			vd->vd_flags |= VDF_INVALID;
781257626Sray			if (vd->vd_driver->vd_postswitch)
782257626Sray				vd->vd_driver->vd_postswitch(vd);
783219888Sed		}
784219888Sed		vd->vd_flags &= ~VDF_SPLASH;
785219888Sed		vt_flush(vd);
786219888Sed	} else if (!(vd->vd_flags & VDF_ASYNC)) {
787219888Sed		vt_flush(vd);
788219888Sed	}
789219888Sed}
790219888Sed
791219888Sedstatic void
792256145Srayvtterm_splash(struct vt_device *vd)
793256145Sray{
794256145Sray	vt_axis_t top, left;
795256145Sray
796256145Sray	/* Display a nice boot splash. */
797257722Sray	if (!(vd->vd_flags & VDF_TEXTMODE) && (boothowto & RB_MUTE)) {
798256145Sray
799256145Sray		top = (vd->vd_height - vt_logo_height) / 2;
800256145Sray		left = (vd->vd_width - vt_logo_width) / 2;
801256145Sray		switch (vt_logo_depth) {
802256145Sray		case 1:
803256145Sray			/* XXX: Unhardcode colors! */
804257988Sray			vd->vd_driver->vd_bitbltchr(vd, vt_logo_image, NULL, 0,
805257988Sray			    top, left, vt_logo_width, vt_logo_height, 0xf, 0x0);
806256145Sray		}
807256145Sray		vd->vd_flags |= VDF_SPLASH;
808256145Sray	}
809256145Sray}
810256145Sray
811256145Sraystatic void
812219888Sedvtterm_cnprobe(struct terminal *tm, struct consdev *cp)
813219888Sed{
814219888Sed	struct vt_window *vw = tm->tm_softc;
815219888Sed	struct vt_device *vd = vw->vw_device;
816219888Sed	struct winsize wsz;
817219888Sed
818256145Sray	if (vd->vd_flags & VDF_INITIALIZED)
819256145Sray		/* Initialization already done. */
820256145Sray		return;
821256145Sray
822219888Sed	cp->cn_pri = vd->vd_driver->vd_init(vd);
823219888Sed	if (cp->cn_pri == CN_DEAD) {
824219888Sed		vd->vd_flags |= VDF_DEAD;
825219888Sed		return;
826219888Sed	}
827219888Sed
828230469Snwhitehorn	/* Initialize any early-boot keyboard drivers */
829230469Snwhitehorn	kbd_configure(KB_CONF_PROBE_ONLY);
830230469Snwhitehorn
831219888Sed	vd->vd_unit = atomic_fetchadd_int(&vt_unit, 1);
832256145Sray	vd->vd_windows[VT_CONSWINDOW] = vw;
833219888Sed	sprintf(cp->cn_name, "ttyv%r", VT_UNIT(vw));
834219888Sed
835219888Sed	if (!(vd->vd_flags & VDF_TEXTMODE))
836219888Sed		vw->vw_font = vtfont_ref(&vt_font_default);
837219888Sed
838219888Sed	vtbuf_init_early(&vw->vw_buf);
839219888Sed	vt_winsize(vd, vw->vw_font, &wsz);
840219888Sed	terminal_set_winsize(tm, &wsz);
841219888Sed
842256145Sray	vtterm_splash(vd);
843219888Sed
844256145Sray	vd->vd_flags |= VDF_INITIALIZED;
845256145Sray	main_vd = vd;
846219888Sed}
847219888Sed
848219888Sedstatic int
849219888Sedvtterm_cngetc(struct terminal *tm)
850219888Sed{
851219888Sed	struct vt_window *vw = tm->tm_softc;
852219888Sed	struct vt_device *vd = vw->vw_device;
853219888Sed	keyboard_t *kbd;
854257076Sray	int state;
855219888Sed	u_int c;
856219888Sed
857257076Sray	if (vw->vw_kbdsq && *vw->vw_kbdsq)
858257076Sray		return (*vw->vw_kbdsq++);
859257076Sray
860257076Sray	state = 0;
861219888Sed	/* Make sure the splash screen is not there. */
862219888Sed	if (vd->vd_flags & VDF_SPLASH) {
863256145Sray		/* Remove splash */
864219888Sed		vd->vd_flags &= ~VDF_SPLASH;
865256145Sray		/* Mark screen as invalid to force update */
866256145Sray		vd->vd_flags |= VDF_INVALID;
867219888Sed		vt_flush(vd);
868219888Sed	}
869219888Sed
870219888Sed	/* Stripped down keyboard handler. */
871219888Sed	kbd = kbd_get_keyboard(vd->vd_keyboard);
872219888Sed	if (kbd == NULL)
873219888Sed		return (-1);
874219888Sed
875256145Sray	/* Force keyboard input mode to K_XLATE */
876256145Sray	c = K_XLATE;
877256145Sray	kbdd_ioctl(kbd, KDSKBMODE, (void *)&c);
878256145Sray
879219888Sed	/* Switch the keyboard to polling to make it work here. */
880219888Sed	kbdd_poll(kbd, TRUE);
881219888Sed	c = kbdd_read_char(kbd, 0);
882219888Sed	kbdd_poll(kbd, FALSE);
883219888Sed	if (c & RELKEY)
884219888Sed		return (-1);
885256145Sray
886257076Sray	if (vw->vw_flags & VWF_SCROLL) {
887257076Sray		vt_scrollmode_kbdevent(vw, c, 1/* Console mode */);
888257076Sray		vt_flush(vd);
889257076Sray		return (-1);
890257076Sray	}
891257076Sray
892219888Sed	/* Stripped down handling of vt_kbdevent(), without locking, etc. */
893219888Sed	if (c & SPCLKEY) {
894219888Sed		switch (c) {
895257076Sray		case SPCLKEY | SLK:
896219888Sed			kbdd_ioctl(kbd, KDGKBSTATE, (caddr_t)&state);
897219888Sed			if (state & SLKED) {
898219888Sed				/* Turn scrolling on. */
899258408Sray				vw->vw_flags |= VWF_SCROLL;
900256145Sray				VTBUF_SLCK_ENABLE(&vw->vw_buf);
901219888Sed			} else {
902219888Sed				/* Turn scrolling off. */
903257076Sray				vt_scroll(vw, 0, VHS_END);
904258408Sray				vw->vw_flags &= ~VWF_SCROLL;
905256145Sray				VTBUF_SLCK_DISABLE(&vw->vw_buf);
906219888Sed			}
907219888Sed			break;
908257076Sray		/* XXX: KDB can handle history. */
909257076Sray		case SPCLKEY | FKEY | F(50): /* Arrow up. */
910257076Sray			vw->vw_kbdsq = "\x1b[A";
911219888Sed			break;
912257076Sray		case SPCLKEY | FKEY | F(58): /* Arrow down. */
913257076Sray			vw->vw_kbdsq = "\x1b[B";
914219888Sed			break;
915257076Sray		case SPCLKEY | FKEY | F(55): /* Arrow right. */
916257076Sray			vw->vw_kbdsq = "\x1b[C";
917219888Sed			break;
918257076Sray		case SPCLKEY | FKEY | F(53): /* Arrow left. */
919257076Sray			vw->vw_kbdsq = "\x1b[D";
920219888Sed			break;
921219888Sed		}
922219888Sed
923219888Sed		/* Force refresh to make scrollback work. */
924219888Sed		vt_flush(vd);
925219888Sed	} else if (KEYFLAGS(c) == 0) {
926219888Sed		return KEYCHAR(c);
927219888Sed	}
928256145Sray
929257076Sray	if (vw->vw_kbdsq && *vw->vw_kbdsq)
930258166Sray		return (*vw->vw_kbdsq++);
931257076Sray
932219888Sed	return (-1);
933219888Sed}
934219888Sed
935219888Sedstatic void
936219888Sedvtterm_opened(struct terminal *tm, int opened)
937219888Sed{
938219888Sed	struct vt_window *vw = tm->tm_softc;
939219888Sed	struct vt_device *vd = vw->vw_device;
940219888Sed
941219888Sed	VT_LOCK(vd);
942219888Sed	vd->vd_flags &= ~VDF_SPLASH;
943219888Sed	if (opened)
944258408Sray		vw->vw_flags |= VWF_OPENED;
945256145Sray	else {
946258408Sray		vw->vw_flags &= ~VWF_OPENED;
947256145Sray		/* TODO: finish ACQ/REL */
948256145Sray	}
949219888Sed	VT_UNLOCK(vd);
950219888Sed}
951219888Sed
952219888Sedstatic int
953219888Sedvt_change_font(struct vt_window *vw, struct vt_font *vf)
954219888Sed{
955219888Sed	struct vt_device *vd = vw->vw_device;
956219888Sed	struct terminal *tm = vw->vw_terminal;
957219888Sed	term_pos_t size;
958219888Sed	struct winsize wsz;
959219888Sed
960219888Sed	/*
961219888Sed	 * Changing fonts.
962219888Sed	 *
963219888Sed	 * Changing fonts is a little tricky.  We must prevent
964219888Sed	 * simultaneous access to the device, so we must stop
965219888Sed	 * the display timer and the terminal from accessing.
966219888Sed	 * We need to switch fonts and grow our screen buffer.
967219888Sed	 *
968219888Sed	 * XXX: Right now the code uses terminal_mute() to
969219888Sed	 * prevent data from reaching the console driver while
970219888Sed	 * resizing the screen buffer.  This isn't elegant...
971219888Sed	 */
972219888Sed
973219888Sed	VT_LOCK(vd);
974219888Sed	if (vw->vw_flags & VWF_BUSY) {
975219888Sed		/* Another process is changing the font. */
976219888Sed		VT_UNLOCK(vd);
977219888Sed		return (EBUSY);
978219888Sed	}
979219888Sed	if (vw->vw_font == NULL) {
980219888Sed		/* Our device doesn't need fonts. */
981219888Sed		VT_UNLOCK(vd);
982219888Sed		return (ENOTTY);
983219888Sed	}
984258408Sray	vw->vw_flags |= VWF_BUSY;
985219888Sed	VT_UNLOCK(vd);
986219888Sed
987219888Sed	vt_termsize(vd, vf, &size);
988219888Sed	vt_winsize(vd, vf, &wsz);
989257978Sray	/* Save offset to font aligned area. */
990257978Sray	vd->vd_offset.tp_col = (vd->vd_width % vf->vf_width) / 2;
991257978Sray	vd->vd_offset.tp_row = (vd->vd_height % vf->vf_height) / 2;
992219888Sed
993219888Sed	/* Grow the screen buffer and terminal. */
994219888Sed	terminal_mute(tm, 1);
995256145Sray	vtbuf_grow(&vw->vw_buf, &size, vw->vw_buf.vb_history_size);
996256903Sray	terminal_set_winsize_blank(tm, &wsz, 0);
997219888Sed	terminal_mute(tm, 0);
998219888Sed
999219888Sed	/* Actually apply the font to the current window. */
1000219888Sed	VT_LOCK(vd);
1001219888Sed	vtfont_unref(vw->vw_font);
1002219888Sed	vw->vw_font = vtfont_ref(vf);
1003219888Sed
1004219888Sed	/* Force a full redraw the next timer tick. */
1005219888Sed	if (vd->vd_curwindow == vw)
1006219888Sed		vd->vd_flags |= VDF_INVALID;
1007258408Sray	vw->vw_flags &= ~VWF_BUSY;
1008219888Sed	VT_UNLOCK(vd);
1009219888Sed	return (0);
1010219888Sed}
1011219888Sed
1012219888Sedstatic int
1013256145Srayvt_proc_alive(struct vt_window *vw)
1014256145Sray{
1015256145Sray	struct proc *p;
1016256145Sray
1017256145Sray	if (vw->vw_smode.mode != VT_PROCESS)
1018256145Sray		return FALSE;
1019256145Sray
1020256145Sray	if (vw->vw_proc) {
1021256145Sray		if ((p = pfind(vw->vw_pid)) != NULL)
1022256145Sray			PROC_UNLOCK(p);
1023256145Sray		if (vw->vw_proc == p)
1024256145Sray			return TRUE;
1025256145Sray		vw->vw_proc = NULL;
1026256145Sray		vw->vw_smode.mode = VT_AUTO;
1027256145Sray		DPRINTF(1, "vt controlling process %d died\n", vw->vw_pid);
1028256145Sray		vw->vw_pid = 0;
1029256145Sray	}
1030256145Sray	return FALSE;
1031256145Sray}
1032256145Sray
1033256145Sraystatic int
1034256145Sraysignal_vt_rel(struct vt_window *vw)
1035256145Sray{
1036257815Sray
1037256145Sray	if (vw->vw_smode.mode != VT_PROCESS)
1038256145Sray		return FALSE;
1039256145Sray	if (vw->vw_proc == NULL || vt_proc_alive(vw) == FALSE) {
1040256145Sray		vw->vw_proc = NULL;
1041256145Sray		vw->vw_pid = 0;
1042256145Sray		return TRUE;
1043256145Sray	}
1044258408Sray	vw->vw_flags |= VWF_SWWAIT_REL;
1045256145Sray	PROC_LOCK(vw->vw_proc);
1046256145Sray	kern_psignal(vw->vw_proc, vw->vw_smode.relsig);
1047256145Sray	PROC_UNLOCK(vw->vw_proc);
1048256145Sray	DPRINTF(1, "sending relsig to %d\n", vw->vw_pid);
1049256145Sray	return TRUE;
1050256145Sray}
1051256145Sray
1052256145Sraystatic int
1053256145Sraysignal_vt_acq(struct vt_window *vw)
1054256145Sray{
1055257815Sray
1056256145Sray	if (vw->vw_smode.mode != VT_PROCESS)
1057256145Sray		return FALSE;
1058256145Sray	if (vw == vw->vw_device->vd_windows[VT_CONSWINDOW])
1059256145Sray		cnavailable(vw->vw_terminal->consdev, FALSE);
1060256145Sray	if (vw->vw_proc == NULL || vt_proc_alive(vw) == FALSE) {
1061256145Sray		vw->vw_proc = NULL;
1062256145Sray		vw->vw_pid = 0;
1063256145Sray		return TRUE;
1064256145Sray	}
1065258408Sray	vw->vw_flags |= VWF_SWWAIT_ACQ;
1066256145Sray	PROC_LOCK(vw->vw_proc);
1067256145Sray	kern_psignal(vw->vw_proc, vw->vw_smode.acqsig);
1068256145Sray	PROC_UNLOCK(vw->vw_proc);
1069256145Sray	DPRINTF(1, "sending acqsig to %d\n", vw->vw_pid);
1070256145Sray	return TRUE;
1071256145Sray}
1072256145Sray
1073256145Sraystatic int
1074256145Srayfinish_vt_rel(struct vt_window *vw, int release, int *s)
1075256145Sray{
1076257815Sray
1077256145Sray	if (vw->vw_flags & VWF_SWWAIT_REL) {
1078258408Sray		vw->vw_flags &= ~VWF_SWWAIT_REL;
1079256145Sray		if (release) {
1080256145Sray			callout_drain(&vw->vw_proc_dead_timer);
1081256145Sray			vt_late_window_switch(vw->vw_switch_to);
1082256145Sray		}
1083256145Sray		return 0;
1084256145Sray	}
1085256145Sray	return EINVAL;
1086256145Sray}
1087256145Sray
1088256145Sraystatic int
1089256145Srayfinish_vt_acq(struct vt_window *vw)
1090256145Sray{
1091257815Sray
1092256145Sray	if (vw->vw_flags & VWF_SWWAIT_ACQ) {
1093258408Sray		vw->vw_flags &= ~VWF_SWWAIT_ACQ;
1094256145Sray		return 0;
1095256145Sray	}
1096256145Sray	return EINVAL;
1097256145Sray}
1098256145Sray
1099257977Srayvoid
1100257977Srayvt_mouse_event(int type, int x, int y, int event, int cnt)
1101257977Sray{
1102257977Sray	struct vt_device *vd;
1103257977Sray	struct vt_window *vw;
1104257977Sray	struct vt_font *vf;
1105257977Sray	term_pos_t size;
1106258090Sray	term_char_t *buf;
1107258090Sray	int i, len, mark;
1108257977Sray
1109257977Sray	vd = main_vd;
1110257977Sray	vw = vd->vd_curwindow;
1111257977Sray	vf = vw->vw_font;
1112258781Snwhitehorn	mark = 0;
1113257977Sray
1114258327Sray	if (vw->vw_flags & VWF_MOUSE_HIDE)
1115258327Sray		return; /* Mouse disabled. */
1116258327Sray
1117257977Sray	if (vf == NULL)	/* Text mode. */
1118257977Sray		return;
1119257977Sray
1120257977Sray	/*
1121257977Sray	 * TODO: add flag about pointer position changed, to not redraw chars
1122257977Sray	 * under mouse pointer when nothing changed.
1123257977Sray	 */
1124257977Sray
1125257977Sray	switch (type) {
1126257977Sray	case MOUSE_ACTION:
1127257977Sray	case MOUSE_MOTION_EVENT:
1128257977Sray		/* Movement */
1129257977Sray		x += vd->vd_mx;
1130257977Sray		y += vd->vd_my;
1131257977Sray
1132257977Sray		vt_termsize(vd, vf, &size);
1133257977Sray
1134257977Sray		/* Apply limits. */
1135257977Sray		x = MAX(x, 0);
1136257977Sray		y = MAX(y, 0);
1137257977Sray		x = MIN(x, (size.tp_col * vf->vf_width) - 1);
1138257977Sray		y = MIN(y, (size.tp_row * vf->vf_height) - 1);
1139257977Sray
1140257977Sray		vd->vd_mx = x;
1141257977Sray		vd->vd_my = y;
1142258090Sray		if ((vd->vd_mstate & MOUSE_BUTTON1DOWN) &&
1143258130Sray		    (vtbuf_set_mark(&vw->vw_buf, VTB_MARK_MOVE,
1144258090Sray			vd->vd_mx / vf->vf_width,
1145258090Sray			vd->vd_my / vf->vf_height) == 1)) {
1146258090Sray
1147258090Sray			/*
1148258090Sray			 * We have something marked to copy, so update pointer
1149258090Sray			 * to window with selection.
1150258090Sray			 */
1151258090Sray			vd->vd_markedwin = vw;
1152258090Sray		}
1153257977Sray		return; /* Done */
1154257977Sray	case MOUSE_BUTTON_EVENT:
1155257977Sray		/* Buttons */
1156257977Sray		break;
1157257977Sray	default:
1158257977Sray		return; /* Done */
1159257977Sray	}
1160257977Sray
1161257977Sray	switch (event) {
1162257977Sray	case MOUSE_BUTTON1DOWN:
1163257977Sray		switch (cnt % 4) {
1164257977Sray		case 0:	/* up */
1165257977Sray			mark = VTB_MARK_END;
1166257977Sray			break;
1167257977Sray		case 1: /* single click: start cut operation */
1168257977Sray			mark = VTB_MARK_START;
1169257977Sray			break;
1170257977Sray		case 2:	/* double click: cut a word */
1171257977Sray			mark = VTB_MARK_WORD;
1172257977Sray			break;
1173257977Sray		case 3:	/* triple click: cut a line */
1174257977Sray			mark = VTB_MARK_ROW;
1175257977Sray			break;
1176257977Sray		}
1177257977Sray		break;
1178257977Sray	case VT_MOUSE_PASTEBUTTON:
1179258112Sray		switch (cnt) {
1180257977Sray		case 0:	/* up */
1181257977Sray			break;
1182257977Sray		default:
1183258090Sray			if (vd->vd_markedwin == NULL)
1184258090Sray				return;
1185258090Sray			/* Get current selecton size in bytes. */
1186258090Sray			len = vtbuf_get_marked_len(&vd->vd_markedwin->vw_buf);
1187258090Sray			if (len <= 0)
1188258090Sray				return;
1189258090Sray
1190258090Sray			buf = malloc(len, M_VT, M_WAITOK | M_ZERO);
1191258090Sray			/* Request cupy/paste buffer data, no more than `len' */
1192258090Sray			vtbuf_extract_marked(&vd->vd_markedwin->vw_buf, buf,
1193258090Sray			    len);
1194258090Sray
1195258090Sray			len /= sizeof(term_char_t);
1196258090Sray			for (i = 0; i < len; i++ ) {
1197258090Sray				if (buf[i] == '\0')
1198258090Sray					continue;
1199258090Sray				terminal_input_char(vw->vw_terminal, buf[i]);
1200258090Sray			}
1201258090Sray
1202258090Sray			/* Done, so cleanup. */
1203258090Sray			free(buf, M_VT);
1204257977Sray			break;
1205257977Sray		}
1206257977Sray		return; /* Done */
1207257977Sray	case VT_MOUSE_EXTENDBUTTON:
1208258112Sray		switch (cnt) {
1209257977Sray		case 0:	/* up */
1210257977Sray			if (!(vd->vd_mstate & MOUSE_BUTTON1DOWN))
1211258130Sray				mark = VTB_MARK_EXTEND;
1212257977Sray			else
1213257977Sray				mark = 0;
1214257977Sray			break;
1215257977Sray		default:
1216257977Sray			mark = VTB_MARK_EXTEND;
1217257977Sray			break;
1218257977Sray		}
1219257977Sray		break;
1220257977Sray	default:
1221257977Sray		return; /* Done */
1222257977Sray	}
1223257977Sray
1224257977Sray	/* Save buttons state. */
1225257977Sray	if (cnt > 0)
1226257977Sray		vd->vd_mstate |= event;
1227257977Sray	else
1228257977Sray		vd->vd_mstate &= ~event;
1229257977Sray
1230258090Sray	if (vtbuf_set_mark(&vw->vw_buf, mark, vd->vd_mx / vf->vf_width,
1231258090Sray	    vd->vd_my / vf->vf_height) == 1) {
1232258090Sray		/*
1233258090Sray		 * We have something marked to copy, so update pointer to
1234258090Sray		 * window with selection.
1235258090Sray		 */
1236258090Sray		vd->vd_markedwin = vw;
1237258090Sray	}
1238257977Sray}
1239257977Sray
1240258327Srayvoid
1241258327Srayvt_mouse_state(int show)
1242258327Sray{
1243258327Sray	struct vt_device *vd;
1244258327Sray	struct vt_window *vw;
1245258327Sray
1246258327Sray	vd = main_vd;
1247258327Sray	vw = vd->vd_curwindow;
1248258327Sray
1249258327Sray	switch (show) {
1250258327Sray	case VT_MOUSE_HIDE:
1251258409Sray		vw->vw_flags |= VWF_MOUSE_HIDE;
1252258327Sray		break;
1253258327Sray	case VT_MOUSE_SHOW:
1254258409Sray		vw->vw_flags &= ~VWF_MOUSE_HIDE;
1255258327Sray		break;
1256258327Sray	}
1257258327Sray}
1258258327Sray
1259256145Sraystatic int
1260219888Sedvtterm_ioctl(struct terminal *tm, u_long cmd, caddr_t data,
1261219888Sed    struct thread *td)
1262219888Sed{
1263219888Sed	struct vt_window *vw = tm->tm_softc;
1264219888Sed	struct vt_device *vd = vw->vw_device;
1265256145Sray	int error, s;
1266219888Sed
1267219888Sed	switch (cmd) {
1268219888Sed	case GIO_KEYMAP:
1269219888Sed	case PIO_KEYMAP:
1270219888Sed	case GIO_DEADKEYMAP:
1271219888Sed	case PIO_DEADKEYMAP:
1272219888Sed	case GETFKEY:
1273219888Sed	case SETFKEY:
1274219888Sed	case KDGKBINFO: {
1275219888Sed		keyboard_t *kbd;
1276256145Sray		error = 0;
1277219888Sed
1278219888Sed		mtx_lock(&Giant);
1279219888Sed		kbd = kbd_get_keyboard(vd->vd_keyboard);
1280219888Sed		if (kbd != NULL)
1281219888Sed			error = kbdd_ioctl(kbd, cmd, data);
1282219888Sed		mtx_unlock(&Giant);
1283219888Sed		if (error == ENOIOCTL)
1284219888Sed			return (ENODEV);
1285219888Sed		return (error);
1286219888Sed	}
1287256145Sray	case KDGKBMODE: {
1288256145Sray		int mode = -1;
1289256145Sray		keyboard_t *kbd;
1290256145Sray
1291256145Sray		mtx_lock(&Giant);
1292256145Sray		kbd = kbd_get_keyboard(vd->vd_keyboard);
1293256145Sray		if (kbd != NULL) {
1294256145Sray			kbdd_ioctl(kbd, KDGKBMODE, (void *)&mode);
1295256145Sray		}
1296256145Sray		mtx_unlock(&Giant);
1297256145Sray		DPRINTF(20, "mode %d, vw_kbdmode %d\n", mode, vw->vw_kbdmode);
1298256145Sray		*(int *)data = mode;
1299256145Sray		return (0);
1300256145Sray	}
1301219888Sed	case KDSKBMODE: {
1302219888Sed		int mode;
1303219888Sed
1304219888Sed		mode = *(int *)data;
1305219888Sed		switch (mode) {
1306219888Sed		case K_XLATE:
1307219888Sed		case K_RAW:
1308219888Sed		case K_CODE:
1309219888Sed			vw->vw_kbdmode = mode;
1310219888Sed			if (vw == vd->vd_curwindow) {
1311219888Sed				keyboard_t *kbd;
1312256145Sray				error = 0;
1313219888Sed
1314257983Sray				DPRINTF(20, "%s: vd_keyboard = %d\n", __func__,
1315257983Sray				    vd->vd_keyboard);
1316219888Sed				mtx_lock(&Giant);
1317219888Sed				kbd = kbd_get_keyboard(vd->vd_keyboard);
1318256145Sray				if (kbd != NULL) {
1319256145Sray					DPRINTF(20, "kbdd_ioctl(KDSKBMODE, %d)\n", mode);
1320256145Sray					error = kbdd_ioctl(kbd, KDSKBMODE,
1321219888Sed					    (void *)&mode);
1322256145Sray				}
1323219888Sed				mtx_unlock(&Giant);
1324256145Sray				if (error)
1325257983Sray					DPRINTF(20, "kbdd_ioctl(KDSKBMODE) "
1326257983Sray					    "return %d\n", error);
1327219888Sed			}
1328219888Sed			return (0);
1329219888Sed		default:
1330219888Sed			return (EINVAL);
1331219888Sed		}
1332219888Sed	}
1333219888Sed	case CONS_BLANKTIME:
1334219888Sed		/* XXX */
1335219888Sed		return (0);
1336219888Sed	case CONS_GET:
1337219888Sed		/* XXX */
1338219888Sed		*(int *)data = M_CG640x480;
1339219888Sed		return (0);
1340219888Sed	case CONS_GETINFO: {
1341219888Sed		vid_info_t *vi = (vid_info_t *)data;
1342219888Sed
1343219888Sed		vi->m_num = vd->vd_curwindow->vw_number + 1;
1344219888Sed		/* XXX: other fields! */
1345219888Sed		return (0);
1346219888Sed	}
1347219888Sed	case CONS_GETVERS:
1348219888Sed		*(int *)data = 0x200;
1349219888Sed		return 0;
1350219888Sed	case CONS_MODEINFO:
1351219888Sed		/* XXX */
1352219888Sed		return (0);
1353219888Sed	case CONS_MOUSECTL: {
1354219888Sed		mouse_info_t *mouse = (mouse_info_t*)data;
1355219888Sed
1356219888Sed		/*
1357219888Sed		 * This has no effect on vt(4).  We don't draw any mouse
1358219888Sed		 * cursor.  Just ignore MOUSE_HIDE and MOUSE_SHOW to
1359219888Sed		 * prevent excessive errors.  All the other commands
1360219888Sed		 * should not be applied to individual TTYs, but only to
1361219888Sed		 * consolectl.
1362219888Sed		 */
1363219888Sed		switch (mouse->operation) {
1364219888Sed		case MOUSE_HIDE:
1365257982Sray			vd->vd_flags &= ~VDF_MOUSECURSOR;
1366257982Sray			return (0);
1367219888Sed		case MOUSE_SHOW:
1368257982Sray			vd->vd_mx = vd->vd_width / 2;
1369257982Sray			vd->vd_my = vd->vd_height / 2;
1370257982Sray			vd->vd_flags |= VDF_MOUSECURSOR;
1371219888Sed			return (0);
1372219888Sed		default:
1373219888Sed			return (EINVAL);
1374219888Sed		}
1375219888Sed	}
1376219888Sed	case PIO_VFONT: {
1377219888Sed		struct vt_font *vf;
1378219888Sed
1379219888Sed		error = vtfont_load((void *)data, &vf);
1380219888Sed		if (error != 0)
1381219888Sed			return (error);
1382219888Sed
1383219888Sed		error = vt_change_font(vw, vf);
1384219888Sed		vtfont_unref(vf);
1385219888Sed		return (error);
1386219888Sed	}
1387219888Sed	case GIO_SCRNMAP: {
1388219888Sed		scrmap_t *sm = (scrmap_t *)data;
1389219888Sed		int i;
1390219888Sed
1391219888Sed		/* We don't have screen maps, so return a handcrafted one. */
1392219888Sed		for (i = 0; i < 256; i++)
1393219888Sed			sm->scrmap[i] = i;
1394219888Sed		return (0);
1395219888Sed	}
1396219888Sed	case KDGETLED:
1397219888Sed		/* XXX */
1398219888Sed		return (0);
1399219888Sed	case KDSETLED:
1400219888Sed		/* XXX */
1401219888Sed		return (0);
1402219888Sed	case KDSETMODE:
1403219888Sed		/* XXX */
1404219888Sed		return (0);
1405219888Sed	case KDSETRAD:
1406219888Sed		/* XXX */
1407219888Sed		return (0);
1408256145Sray	case VT_ACTIVATE: {
1409256145Sray		int win;
1410256145Sray		win = *(int *)data - 1;
1411256145Sray		DPRINTF(5, "%s%d: VT_ACTIVATE ttyv%d ", SC_DRIVER_NAME, VT_UNIT(vw), win);
1412256145Sray		if ((win > VT_MAXWINDOWS) || (win < 0))
1413256145Sray			return (EINVAL);
1414256145Sray		return (vt_proc_window_switch(vd->vd_windows[win]));
1415256145Sray	}
1416219888Sed	case VT_GETACTIVE:
1417219888Sed		*(int *)data = vd->vd_curwindow->vw_number + 1;
1418219888Sed		return (0);
1419219888Sed	case VT_GETINDEX:
1420219888Sed		*(int *)data = vw->vw_number + 1;
1421219888Sed		return (0);
1422256145Sray	case VT_LOCKSWITCH:
1423256145Sray		/* TODO: Check current state, switching can be in progress. */
1424256145Sray		if ((*(int *)data) & 0x01)
1425258408Sray			vw->vw_flags |= VWF_VTYLOCK;
1426256145Sray		else
1427258408Sray			vw->vw_flags &= ~VWF_VTYLOCK;
1428219888Sed	case VT_OPENQRY: {
1429219888Sed		unsigned int i;
1430219888Sed
1431219888Sed		VT_LOCK(vd);
1432219888Sed		for (i = 0; i < VT_MAXWINDOWS; i++) {
1433219888Sed			vw = vd->vd_windows[i];
1434219888Sed			if (vw == NULL)
1435219888Sed				continue;
1436219888Sed			if (!(vw->vw_flags & VWF_OPENED)) {
1437219888Sed				*(int *)data = vw->vw_number + 1;
1438219888Sed				VT_UNLOCK(vd);
1439219888Sed				return (0);
1440219888Sed			}
1441219888Sed		}
1442219888Sed		VT_UNLOCK(vd);
1443219888Sed		return (EINVAL);
1444219888Sed	}
1445219888Sed	case VT_WAITACTIVE: {
1446219888Sed		unsigned int i;
1447256145Sray		error = 0;
1448219888Sed
1449219888Sed		i = *(unsigned int *)data;
1450219888Sed		if (i > VT_MAXWINDOWS)
1451219888Sed			return (EINVAL);
1452219888Sed		if (i != 0)
1453219888Sed			vw = vd->vd_windows[i - 1];
1454219888Sed
1455219888Sed		VT_LOCK(vd);
1456219888Sed		while (vd->vd_curwindow != vw && error == 0)
1457219888Sed			error = cv_wait_sig(&vd->vd_winswitch, &vd->vd_lock);
1458219888Sed		VT_UNLOCK(vd);
1459219888Sed		return (error);
1460219888Sed	}
1461256145Sray	case VT_SETMODE:    	/* set screen switcher mode */
1462256145Sray	{
1463256145Sray		struct vt_mode *mode;
1464256145Sray		struct proc *p1;
1465256145Sray
1466256145Sray		mode = (struct vt_mode *)data;
1467256145Sray		DPRINTF(5, "%s%d: VT_SETMODE ", SC_DRIVER_NAME, VT_UNIT(vw));
1468256145Sray		if (vw->vw_smode.mode == VT_PROCESS) {
1469256145Sray			p1 = pfind(vw->vw_pid);
1470256145Sray			if (vw->vw_proc == p1 && vw->vw_proc != td->td_proc) {
1471256145Sray				if (p1)
1472256145Sray					PROC_UNLOCK(p1);
1473256145Sray				DPRINTF(5, "error EPERM\n");
1474256145Sray				return (EPERM);
1475256145Sray			}
1476256145Sray			if (p1)
1477256145Sray				PROC_UNLOCK(p1);
1478256145Sray		}
1479256145Sray		if (mode->mode == VT_AUTO) {
1480256145Sray			vw->vw_smode.mode = VT_AUTO;
1481256145Sray			vw->vw_proc = NULL;
1482256145Sray			vw->vw_pid = 0;
1483256145Sray			DPRINTF(5, "VT_AUTO, ");
1484256145Sray			if (vw == vw->vw_device->vd_windows[VT_CONSWINDOW])
1485256145Sray				cnavailable(vw->vw_terminal->consdev, TRUE);
1486256145Sray			/* were we in the middle of the vty switching process? */
1487256145Sray			if (finish_vt_rel(vw, TRUE, &s) == 0)
1488256145Sray				DPRINTF(5, "reset WAIT_REL, ");
1489256145Sray			if (finish_vt_acq(vw) == 0)
1490256145Sray				DPRINTF(5, "reset WAIT_ACQ, ");
1491256145Sray			return (0);
1492256145Sray		} else if (mode->mode == VT_PROCESS) {
1493256145Sray			if (!ISSIGVALID(mode->relsig) ||
1494256145Sray			    !ISSIGVALID(mode->acqsig) ||
1495256145Sray			    !ISSIGVALID(mode->frsig)) {
1496256145Sray				DPRINTF(5, "error EINVAL\n");
1497256145Sray				return (EINVAL);
1498256145Sray			}
1499256145Sray			DPRINTF(5, "VT_PROCESS %d, ", td->td_proc->p_pid);
1500256145Sray			bcopy(data, &vw->vw_smode, sizeof(struct vt_mode));
1501256145Sray			vw->vw_proc = td->td_proc;
1502256145Sray			vw->vw_pid = vw->vw_proc->p_pid;
1503256145Sray			if (vw == vw->vw_device->vd_windows[VT_CONSWINDOW])
1504256145Sray				cnavailable(vw->vw_terminal->consdev, FALSE);
1505256145Sray		} else {
1506256145Sray			DPRINTF(5, "VT_SETMODE failed, unknown mode %d\n",
1507256145Sray			    mode->mode);
1508256145Sray			return (EINVAL);
1509256145Sray		}
1510256145Sray		DPRINTF(5, "\n");
1511256145Sray		return 0;
1512219888Sed	}
1513219888Sed
1514256145Sray	case VT_GETMODE:	/* get screen switcher mode */
1515256145Sray		bcopy(&vw->vw_smode, data, sizeof(struct vt_mode));
1516256145Sray		return 0;
1517256145Sray
1518256145Sray	case VT_RELDISP:	/* screen switcher ioctl */
1519256145Sray		/*
1520256145Sray		 * This must be the current vty which is in the VT_PROCESS
1521256145Sray		 * switching mode...
1522256145Sray		 */
1523256145Sray		if ((vw != vd->vd_curwindow) || (vw->vw_smode.mode !=
1524256145Sray		    VT_PROCESS)) {
1525256145Sray			return EINVAL;
1526256145Sray		}
1527256145Sray		/* ...and this process is controlling it. */
1528256145Sray		if (vw->vw_proc != td->td_proc) {
1529256145Sray			return EPERM;
1530256145Sray		}
1531256145Sray		error = EINVAL;
1532256145Sray		switch(*(int *)data) {
1533256145Sray		case VT_FALSE:	/* user refuses to release screen, abort */
1534256145Sray			if ((error = finish_vt_rel(vw, FALSE, &s)) == 0)
1535256145Sray				DPRINTF(5, "%s%d: VT_RELDISP: VT_FALSE\n", SC_DRIVER_NAME,
1536256145Sray				    VT_UNIT(vw));
1537256145Sray			break;
1538256145Sray		case VT_TRUE:	/* user has released screen, go on */
1539256145Sray			/* finish_vt_rel(..., TRUE, ...) should not be locked */
1540256145Sray			if (vw->vw_flags & VWF_SWWAIT_REL) {
1541256145Sray				if ((error = finish_vt_rel(vw, TRUE, &s)) == 0)
1542256145Sray					DPRINTF(5, "%s%d: VT_RELDISP: VT_TRUE\n",
1543256145Sray					    SC_DRIVER_NAME, VT_UNIT(vw));
1544256145Sray			} else {
1545256145Sray				error = EINVAL;
1546256145Sray			}
1547256145Sray			return (error);
1548256145Sray		case VT_ACKACQ:	/* acquire acknowledged, switch completed */
1549256145Sray			if ((error = finish_vt_acq(vw)) == 0)
1550256145Sray				DPRINTF(5, "%s%d: VT_RELDISP: VT_ACKACQ\n", SC_DRIVER_NAME,
1551256145Sray				    VT_UNIT(vw));
1552256145Sray			break;
1553256145Sray		default:
1554256145Sray			break;
1555256145Sray		}
1556256145Sray		return error;
1557256145Sray	}
1558256145Sray
1559219888Sed	return (ENOIOCTL);
1560219888Sed}
1561219888Sed
1562219888Sedstatic struct vt_window *
1563219888Sedvt_allocate_window(struct vt_device *vd, unsigned int window)
1564219888Sed{
1565219888Sed	struct vt_window *vw;
1566219888Sed	struct terminal *tm;
1567219888Sed	term_pos_t size;
1568219888Sed	struct winsize wsz;
1569219888Sed
1570219888Sed	vw = malloc(sizeof *vw, M_VT, M_WAITOK|M_ZERO);
1571219888Sed	vw->vw_device = vd;
1572219888Sed	vw->vw_number = window;
1573219888Sed	vw->vw_kbdmode = K_XLATE;
1574219888Sed
1575219888Sed	if (!(vd->vd_flags & VDF_TEXTMODE))
1576219888Sed		vw->vw_font = vtfont_ref(&vt_font_default);
1577256145Sray
1578219888Sed	vt_termsize(vd, vw->vw_font, &size);
1579219888Sed	vt_winsize(vd, vw->vw_font, &wsz);
1580219888Sed	vtbuf_init(&vw->vw_buf, &size);
1581219888Sed
1582219888Sed	tm = vw->vw_terminal = terminal_alloc(&vt_termclass, vw);
1583219888Sed	terminal_set_winsize(tm, &wsz);
1584219888Sed	vd->vd_windows[window] = vw;
1585256145Sray	callout_init(&vw->vw_proc_dead_timer, 0);
1586219888Sed
1587219888Sed	return (vw);
1588219888Sed}
1589219888Sed
1590219888Sedvoid
1591219888Sedvt_upgrade(struct vt_device *vd)
1592219888Sed{
1593219888Sed	struct vt_window *vw;
1594219888Sed	unsigned int i;
1595219888Sed
1596256145Sray	/* Device didn't pass vd_init() or already upgraded. */
1597256145Sray	if (vd->vd_flags & (VDF_ASYNC|VDF_DEAD))
1598219888Sed		return;
1599256145Sray	vd->vd_flags |= VDF_ASYNC;
1600219888Sed
1601219888Sed	mtx_init(&vd->vd_lock, "vtdev", NULL, MTX_DEF);
1602219888Sed	cv_init(&vd->vd_winswitch, "vtwswt");
1603219888Sed
1604256145Sray	/* Init 25 Hz timer. */
1605219888Sed	callout_init_mtx(&vd->vd_timer, &vd->vd_lock, 0);
1606219888Sed
1607219888Sed	for (i = 0; i < VT_MAXWINDOWS; i++) {
1608219888Sed		vw = vd->vd_windows[i];
1609219888Sed		if (vw == NULL) {
1610219888Sed			/* New window. */
1611219888Sed			vw = vt_allocate_window(vd, i);
1612256145Sray		}
1613256145Sray		if (i == VT_CONSWINDOW) {
1614219888Sed			/* Console window. */
1615219888Sed			EVENTHANDLER_REGISTER(shutdown_pre_sync,
1616219888Sed			    vt_window_switch, vw, SHUTDOWN_PRI_DEFAULT);
1617219888Sed		}
1618219888Sed		terminal_maketty(vw->vw_terminal, "v%r", VT_UNIT(vw));
1619219888Sed	}
1620256145Sray	if (vd->vd_curwindow == NULL)
1621256145Sray		vd->vd_curwindow = vd->vd_windows[VT_CONSWINDOW];
1622219888Sed
1623219888Sed	/* Attach keyboard. */
1624219888Sed	vt_allocate_keyboard(vd);
1625256145Sray	DPRINTF(20, "%s: vd_keyboard = %d\n", __func__, vd->vd_keyboard);
1626256145Sray
1627256145Sray	/* Start timer when everything ready. */
1628256145Sray	callout_reset(&vd->vd_timer, hz / VT_TIMERFREQ, vt_timer, vd);
1629219888Sed}
1630219888Sed
1631256145Sraystatic void
1632256145Srayvt_resize(struct vt_device *vd)
1633256145Sray{
1634256145Sray	struct vt_window *vw;
1635256145Sray	int i;
1636256145Sray
1637256145Sray	for (i = 0; i < VT_MAXWINDOWS; i++) {
1638256145Sray		vw = vd->vd_windows[i];
1639256145Sray		/* Resize terminal windows */
1640256145Sray		vt_change_font(vw, vw->vw_font);
1641256145Sray	}
1642256145Sray}
1643256145Sray
1644219888Sedvoid
1645219888Sedvt_allocate(struct vt_driver *drv, void *softc)
1646219888Sed{
1647219888Sed	struct vt_device *vd;
1648256903Sray	struct winsize wsz;
1649219888Sed
1650256145Sray	if (main_vd == NULL) {
1651256145Sray		main_vd = malloc(sizeof *vd, M_VT, M_WAITOK|M_ZERO);
1652256903Sray		printf("%s: VT initialize with new VT driver.\n", __func__);
1653256145Sray	} else {
1654256145Sray		/*
1655256145Sray		 * Check if have rights to replace current driver. For example:
1656256145Sray		 * it is bad idea to replace KMS driver with generic VGA one.
1657256145Sray		 */
1658256903Sray		if (drv->vd_priority <= main_vd->vd_driver->vd_priority) {
1659256903Sray			printf("%s: Driver priority %d too low. Current %d\n ",
1660256903Sray			    __func__, drv->vd_priority,
1661256903Sray			    main_vd->vd_driver->vd_priority);
1662256145Sray			return;
1663256903Sray		}
1664256903Sray		printf("%s: Replace existing VT driver.\n", __func__);
1665256145Sray	}
1666256145Sray	vd = main_vd;
1667256145Sray
1668256527Sray	/* Stop vt_flush periodic task. */
1669256145Sray	if (vd->vd_curwindow != NULL)
1670256145Sray		callout_drain(&vd->vd_timer);
1671256145Sray
1672219888Sed	vd->vd_driver = drv;
1673219888Sed	vd->vd_softc = softc;
1674219888Sed	vd->vd_driver->vd_init(vd);
1675256145Sray
1676219888Sed	vt_upgrade(vd);
1677256145Sray
1678256145Sray	/* Refill settings with new sizes. */
1679256145Sray	vt_resize(vd);
1680256145Sray
1681256145Sray	if (vd->vd_flags & VDF_SPLASH)
1682256145Sray		vtterm_splash(vd);
1683256145Sray
1684256145Sray	if (vd->vd_curwindow != NULL)
1685256145Sray		callout_schedule(&vd->vd_timer, hz / VT_TIMERFREQ);
1686256145Sray
1687256145Sray	termcn_cnregister(vd->vd_windows[VT_CONSWINDOW]->vw_terminal);
1688256903Sray
1689256903Sray	/* Update console window sizes to actual. */
1690256903Sray	vt_winsize(vd, vd->vd_windows[VT_CONSWINDOW]->vw_font, &wsz);
1691256903Sray	terminal_set_winsize(vd->vd_windows[VT_CONSWINDOW]->vw_terminal, &wsz);
1692219888Sed}
1693257815Sray
1694257815Srayvoid
1695257815Srayvt_suspend()
1696257815Sray{
1697257815Sray
1698258023Sray	if (vt_suspendswitch == 0)
1699258023Sray		return;
1700257815Sray	/* Save current window. */
1701257815Sray	main_vd->vd_savedwindow = main_vd->vd_curwindow;
1702257815Sray	/* Ask holding process to free window and switch to console window */
1703257815Sray	vt_proc_window_switch(main_vd->vd_windows[VT_CONSWINDOW]);
1704257815Sray}
1705257815Sray
1706257815Srayvoid
1707257815Srayvt_resume()
1708257815Sray{
1709257815Sray
1710258023Sray	if (vt_suspendswitch == 0)
1711258023Sray		return;
1712257815Sray	/* Switch back to saved window */
1713257815Sray	if (main_vd->vd_savedwindow != NULL)
1714257815Sray		vt_proc_window_switch(main_vd->vd_savedwindow);
1715257815Sray	main_vd->vd_savedwindow = NULL;
1716257815Sray}
1717