drm/linux: Port kfifo.h to DragonFly BSD
[dragonfly.git] / sys / dev / misc / syscons / scvidctl.c
1 /*-
2  * (MPSAFE)
3  *
4  * Copyright (c) 1998 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The DragonFly Project
8  * by Sascha Wildner <saw@online.de>.
9  *
10  * Simple font scaling code by Sascha Wildner and Matthew Dillon
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer as
17  *    the first lines of this file unmodified.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  * $FreeBSD: src/sys/dev/syscons/scvidctl.c,v 1.19.2.2 2000/05/05 09:16:08 nyan Exp $
34  */
35
36 #include "opt_syscons.h"
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/conf.h>
41 #include <sys/signalvar.h>
42 #include <sys/tty.h>
43 #include <sys/kernel.h>
44 #include <sys/thread2.h>
45
46 #include <machine/console.h>
47 #include <machine/framebuffer.h>
48
49 #include <dev/video/fb/fbreg.h>
50 #include "syscons.h"
51
52 SET_DECLARE(scrndr_set, const sc_renderer_t);
53
54 int
55 sc_set_text_mode(scr_stat *scp, struct tty *tp, int mode, int xsize, int ysize,
56                  int fontsize)
57 {
58     video_info_t info;
59     u_char *font;
60     int prev_ysize;
61     int new_ysize;
62     int error;
63
64     lwkt_gettoken(&vga_token);
65     if ((*vidsw[scp->sc->adapter]->get_info)(scp->sc->adp, mode, &info)) {
66         lwkt_reltoken(&vga_token);
67         return ENODEV;
68     }
69
70     /* adjust argument values */
71     if (fontsize <= 0)
72         fontsize = info.vi_cheight;
73     if (fontsize < 14) {
74         fontsize = 8;
75 #ifndef SC_NO_FONT_LOADING
76         if (!(scp->sc->fonts_loaded & FONT_8))
77             return EINVAL;
78         font = scp->sc->font_8;
79 #else
80         font = NULL;
81 #endif
82     } else if (fontsize >= 16) {
83         fontsize = 16;
84 #ifndef SC_NO_FONT_LOADING
85         if (!(scp->sc->fonts_loaded & FONT_16))
86             return EINVAL;
87         font = scp->sc->font_16;
88 #else
89         font = NULL;
90 #endif
91     } else {
92         fontsize = 14;
93 #ifndef SC_NO_FONT_LOADING
94         if (!(scp->sc->fonts_loaded & FONT_14))
95             return EINVAL;
96         font = scp->sc->font_14;
97 #else
98         font = NULL;
99 #endif
100     }
101     if ((xsize <= 0) || (xsize > info.vi_width))
102         xsize = info.vi_width;
103     if ((ysize <= 0) || (ysize > info.vi_height))
104         ysize = info.vi_height;
105
106     /* stop screen saver, etc */
107     if ((error = sc_clean_up(scp, FALSE))) {
108         lwkt_reltoken(&vga_token);
109         return error;
110     }
111
112     if (scp->sc->fbi != NULL &&
113         sc_render_match(scp, "kms", V_INFO_MM_TEXT) == NULL) {
114         lwkt_reltoken(&vga_token);
115         return ENODEV;
116     }
117     if (scp->sc->fbi == NULL &&
118         sc_render_match(scp, scp->sc->adp->va_name, V_INFO_MM_TEXT) == NULL) {
119         lwkt_reltoken(&vga_token);
120         return ENODEV;
121     }
122
123     /* set up scp */
124     new_ysize = 0;
125 #ifndef SC_NO_HISTORY
126     if (scp->history != NULL) {
127         sc_hist_save(scp);
128         new_ysize = sc_vtb_rows(scp->history); 
129     }
130 #endif
131     prev_ysize = scp->ysize;
132     /*
133      * This is a kludge to fend off scrn_update() while we
134      * muck around with scp. XXX
135      */
136     scp->status |= UNKNOWN_MODE | MOUSE_HIDDEN;
137     scp->status &= ~(GRAPHICS_MODE | PIXEL_MODE | MOUSE_VISIBLE);
138     scp->mode = mode;
139     scp->model = V_INFO_MM_TEXT;
140     scp->xsize = xsize;
141     scp->ysize = ysize;
142     scp->xoff = 0;
143     scp->yoff = 0;
144     scp->xpixel = scp->xsize*8;
145     scp->ypixel = scp->ysize*fontsize;
146     scp->font = font;
147     scp->font_height = fontsize;
148     scp->font_width = 8;
149
150     /* allocate buffers */
151     sc_alloc_scr_buffer(scp, TRUE, TRUE);
152     sc_init_emulator(scp, NULL);
153 #ifndef SC_NO_CUTPASTE
154     sc_alloc_cut_buffer(scp, FALSE);
155 #endif
156 #ifndef SC_NO_HISTORY
157     sc_alloc_history_buffer(scp, new_ysize, prev_ysize, FALSE);
158 #endif
159
160     if (scp == scp->sc->cur_scp)
161         set_mode(scp);
162     scp->status &= ~UNKNOWN_MODE;
163
164     if (tp) {
165         DPRINTF(5, ("ws_*size (%d,%d), size (%d,%d)\n",
166                 tp->t_winsize.ws_col, tp->t_winsize.ws_row,
167                 scp->xsize, scp->ysize));
168         if (tp->t_winsize.ws_col != scp->xsize ||
169             tp->t_winsize.ws_row != scp->ysize) {
170             tp->t_winsize.ws_col = scp->xsize;
171             tp->t_winsize.ws_row = scp->ysize;
172             pgsignal(tp->t_pgrp, SIGWINCH, 1);
173         }
174     }
175     lwkt_reltoken(&vga_token);
176
177     return 0;
178 }
179
180 int
181 sc_set_graphics_mode(scr_stat *scp, struct tty *tp, int mode)
182 {
183 #ifdef SC_NO_MODE_CHANGE
184     return ENODEV;
185 #else
186     video_info_t info;
187     int error;
188
189     lwkt_gettoken(&vga_token);
190     if ((*vidsw[scp->sc->adapter]->get_info)(scp->sc->adp, mode, &info)) {
191         lwkt_reltoken(&vga_token);
192         return ENODEV;
193     }
194     lwkt_reltoken(&vga_token);
195
196     /* stop screen saver, etc */
197     crit_enter();
198     if ((error = sc_clean_up(scp, FALSE))) {
199         crit_exit();
200         return error;
201     }
202
203     if (scp->sc->fbi != NULL &&
204         sc_render_match(scp, "kms", V_INFO_MM_OTHER) == NULL) {
205         crit_exit();
206         return ENODEV;
207     }
208     if (scp->sc->fbi == NULL &&
209         sc_render_match(scp, scp->sc->adp->va_name, V_INFO_MM_OTHER) == NULL) {
210         crit_exit();
211         return ENODEV;
212     }
213
214     /* set up scp */
215     scp->status |= (UNKNOWN_MODE | GRAPHICS_MODE | MOUSE_HIDDEN);
216     scp->status &= ~(PIXEL_MODE | MOUSE_VISIBLE);
217     scp->mode = mode;
218     scp->model = V_INFO_MM_OTHER;
219     /*
220      * Don't change xsize and ysize; preserve the previous vty
221      * and history buffers.
222      */
223     scp->xoff = 0;
224     scp->yoff = 0;
225     scp->xpixel = info.vi_width;
226     scp->ypixel = info.vi_height;
227     scp->font = NULL;
228     scp->font_height = 0;
229     scp->font_width = 0;
230 #ifndef SC_NO_SYSMOUSE
231     /* move the mouse cursor at the center of the screen */
232     sc_mouse_move(scp, scp->xpixel / 2, scp->ypixel / 2);
233 #endif
234     sc_init_emulator(scp, NULL);
235     crit_exit();
236
237     if (scp == scp->sc->cur_scp)
238         set_mode(scp);
239     /* clear_graphics();*/
240     refresh_ega_palette(scp);
241     scp->status &= ~UNKNOWN_MODE;
242
243     if (tp) {
244         if (tp->t_winsize.ws_xpixel != scp->xpixel ||
245             tp->t_winsize.ws_ypixel != scp->ypixel) {
246             tp->t_winsize.ws_xpixel = scp->xpixel;
247             tp->t_winsize.ws_ypixel = scp->ypixel;
248             pgsignal(tp->t_pgrp, SIGWINCH, 1);
249         }
250     }
251
252     return 0;
253 #endif /* SC_NO_MODE_CHANGE */
254 }
255
256 int
257 sc_set_pixel_mode(scr_stat *scp, struct tty *tp, int xsize, int ysize, 
258                   int fontsize)
259 {
260 #ifndef SC_PIXEL_MODE
261     return ENODEV;
262 #else
263     video_info_t info;
264     u_char *font;
265     int prev_ysize;
266     int new_ysize;
267     int error;
268
269     lwkt_gettoken(&vga_token);
270     if ((*vidsw[scp->sc->adapter]->get_info)(scp->sc->adp, scp->mode, &info)) {
271         lwkt_reltoken(&vga_token);
272         return ENODEV;          /* this shouldn't happen */
273     }
274     lwkt_reltoken(&vga_token);
275
276     /* adjust argument values */
277     if (fontsize <= 0)
278         fontsize = info.vi_cheight;
279     if (fontsize < 14) {
280         fontsize = 8;
281 #ifndef SC_NO_FONT_LOADING
282         if (!(scp->sc->fonts_loaded & FONT_8))
283             return EINVAL;
284         font = scp->sc->font_8;
285 #else
286         font = NULL;
287 #endif
288     } else if (fontsize >= 16) {
289         fontsize = 16;
290 #ifndef SC_NO_FONT_LOADING
291         if (!(scp->sc->fonts_loaded & FONT_16))
292             return EINVAL;
293         font = scp->sc->font_16;
294 #else
295         font = NULL;
296 #endif
297     } else {
298         fontsize = 14;
299 #ifndef SC_NO_FONT_LOADING
300         if (!(scp->sc->fonts_loaded & FONT_14))
301             return EINVAL;
302         font = scp->sc->font_14;
303 #else
304         font = NULL;
305 #endif
306     }
307     if (xsize <= 0)
308         xsize = info.vi_width/8;
309     if (ysize <= 0)
310         ysize = info.vi_height/fontsize;
311
312     if ((info.vi_width < xsize*8) || (info.vi_height < ysize*fontsize))
313         return EINVAL;
314
315     /*
316      * We currently support the following graphic modes:
317      *
318      * - 4 bpp planar modes whose memory size does not exceed 64K
319      * - 8 bbp packed pixel modes
320      * - 15, 16, 24 and 32 bpp direct modes with linear frame buffer
321      */
322
323     if (info.vi_mem_model == V_INFO_MM_PLANAR) {
324         if (info.vi_planes != 4)
325             return ENODEV;
326
327         /*
328          * A memory size >64K requires bank switching to access the entire
329          * screen. XXX
330          */
331
332         if (info.vi_width * info.vi_height / 8 > info.vi_window_size)
333             return ENODEV;
334     } else if (info.vi_mem_model == V_INFO_MM_PACKED) {
335         if (info.vi_depth != 8)
336             return ENODEV;
337     } else if (info.vi_mem_model == V_INFO_MM_DIRECT) {
338         if (!(info.vi_flags & V_INFO_LINEAR) &&
339             (info.vi_depth != 15) && (info.vi_depth != 16) &&
340             (info.vi_depth != 24) && (info.vi_depth != 32))
341             return ENODEV;
342     } else
343         return ENODEV;
344
345     /* stop screen saver, etc */
346     crit_enter();
347     if ((error = sc_clean_up(scp, FALSE))) {
348         crit_exit();
349         return error;
350     }
351
352     if (sc_render_match(scp, scp->sc->adp->va_name, info.vi_mem_model) == NULL) {
353         crit_exit();
354         return ENODEV;
355     }
356
357 #if 0
358     if (scp->tsw)
359         (*scp->tsw->te_term)(scp, scp->ts);
360     scp->tsw = NULL;
361     scp->ts = NULL;
362 #endif
363
364     /* set up scp */
365     new_ysize = 0;
366 #ifndef SC_NO_HISTORY
367     if (scp->history != NULL) {
368         sc_hist_save(scp);
369         new_ysize = sc_vtb_rows(scp->history);
370     }
371 #endif
372     prev_ysize = scp->ysize;
373     scp->status |= (UNKNOWN_MODE | PIXEL_MODE | MOUSE_HIDDEN);
374     scp->status &= ~(GRAPHICS_MODE | MOUSE_VISIBLE);
375     scp->model = info.vi_mem_model;
376     scp->xsize = xsize;
377     scp->ysize = ysize;
378     scp->xoff = (scp->xpixel/8 - xsize)/2;
379     scp->yoff = (scp->ypixel/fontsize - ysize)/2;
380     scp->font = font;
381     scp->font_height = fontsize;
382     scp->font_width = 8;
383
384     /* allocate buffers */
385     sc_alloc_scr_buffer(scp, TRUE, TRUE);
386     sc_init_emulator(scp, NULL);
387 #ifndef SC_NO_CUTPASTE
388     sc_alloc_cut_buffer(scp, FALSE);
389 #endif
390 #ifndef SC_NO_HISTORY
391     sc_alloc_history_buffer(scp, new_ysize, prev_ysize, FALSE);
392 #endif
393     crit_exit();
394
395     if (scp == scp->sc->cur_scp) {
396         sc_set_border(scp, scp->border);
397         sc_set_cursor_image(scp);
398     }
399
400     scp->status &= ~UNKNOWN_MODE;
401
402     if (tp) {
403         if (tp->t_winsize.ws_col != scp->xsize ||
404             tp->t_winsize.ws_row != scp->ysize) {
405             tp->t_winsize.ws_col = scp->xsize;
406             tp->t_winsize.ws_row = scp->ysize;
407             pgsignal(tp->t_pgrp, SIGWINCH, 1);
408         }
409     }
410
411     return 0;
412 #endif /* SC_PIXEL_MODE */
413 }
414
415 #define fb_ioctl(a, c, d)               \
416         (((a) == NULL) ? ENODEV :       \
417                          (*vidsw[(a)->va_index]->ioctl)((a), (c), (caddr_t)(d)))
418
419 int
420 sc_vid_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag)
421 {
422     scr_stat *scp;
423     video_adapter_t *adp;
424 #ifndef SC_NO_MODE_CHANGE
425     video_info_t info;
426 #endif
427     int error, ret;
428
429     KKASSERT(tp->t_dev);
430
431     scp = SC_STAT(tp->t_dev);
432     if (scp == NULL)            /* tp == SC_MOUSE */
433         return ENOIOCTL;
434     adp = scp->sc->adp;
435     if (adp == NULL && scp->sc->fbi == NULL)    /* shouldn't happen??? */
436         return ENODEV;
437
438     lwkt_gettoken(&vga_token);
439     switch (cmd) {
440
441     case CONS_CURRENTADP:       /* get current adapter index */
442     case FBIO_ADAPTER:
443         if (scp->sc->fbi != NULL) {
444             ret = ENODEV;
445         } else {
446             ret = fb_ioctl(adp, FBIO_ADAPTER, data);
447         }
448         lwkt_reltoken(&vga_token);
449         return ret;
450
451     case CONS_CURRENT:          /* get current adapter type */
452     case FBIO_ADPTYPE:
453         if (scp->sc->fbi != NULL) {
454             ret = ENODEV;
455         } else {
456             ret = fb_ioctl(adp, FBIO_ADPTYPE, data);
457         }
458         lwkt_reltoken(&vga_token);
459         return ret;
460
461     case CONS_ADPINFO:          /* adapter information */
462     case FBIO_ADPINFO:
463         if (scp->sc->fbi != NULL) {
464             lwkt_reltoken(&vga_token);
465             return ENODEV;
466         }
467         if (((video_adapter_info_t *)data)->va_index >= 0) {
468             adp = vid_get_adapter(((video_adapter_info_t *)data)->va_index);
469             if (adp == NULL) {
470                 lwkt_reltoken(&vga_token);
471                 return ENODEV;
472             }
473         }
474         ret = fb_ioctl(adp, FBIO_ADPINFO, data);
475         lwkt_reltoken(&vga_token);
476         return ret;
477
478     case CONS_GET:              /* get current video mode */
479     case FBIO_GETMODE:
480         *(int *)data = scp->mode;
481         lwkt_reltoken(&vga_token);
482         return 0;
483
484 #ifndef SC_NO_MODE_CHANGE
485     case CONS_SET:
486     case FBIO_SETMODE:          /* set video mode */
487         if (scp->sc->fbi != NULL) {
488                 lwkt_reltoken(&vga_token);
489                 if (*(int *)data != 0)
490                         return ENODEV;
491                 else
492                         return 0;
493         }
494         if (!(adp->va_flags & V_ADP_MODECHANGE)) {
495             lwkt_reltoken(&vga_token);
496             return ENODEV;
497         }
498         info.vi_mode = *(int *)data;
499         error = fb_ioctl(adp, FBIO_MODEINFO, &info);
500         if (error) {
501             lwkt_reltoken(&vga_token);
502             return error;
503         }
504         if (info.vi_flags & V_INFO_GRAPHICS) {
505             lwkt_reltoken(&vga_token);
506             return sc_set_graphics_mode(scp, tp, *(int *)data);
507         } else {
508             lwkt_reltoken(&vga_token);
509             return sc_set_text_mode(scp, tp, *(int *)data, 0, 0, 0);
510         }
511 #endif /* SC_NO_MODE_CHANGE */
512
513     case CONS_MODEINFO:         /* get mode information */
514     case FBIO_MODEINFO:
515         if (scp->sc->fbi != NULL) {
516             /* Fabricate some mode information for KMS framebuffer */
517             video_info_t *vinfo = (video_info_t *)data;
518             vinfo->vi_mode = 0;
519             vinfo->vi_flags = V_INFO_COLOR;
520             vinfo->vi_width = scp->xsize;
521             vinfo->vi_height = scp->ysize;
522             vinfo->vi_cwidth = scp->font_width;
523             vinfo->vi_cheight = scp->font_height;
524             vinfo->vi_window = 0;
525             vinfo->vi_window_size = 0;
526             vinfo->vi_buffer = 0;
527             vinfo->vi_buffer_size = 0;
528             vinfo->vi_mem_model = V_INFO_MM_TEXT;
529             ret = 0;
530         } else {
531             ret = fb_ioctl(adp, FBIO_MODEINFO, data);
532         }
533         lwkt_reltoken(&vga_token);
534         return ret;
535
536     case CONS_FINDMODE:         /* find a matching video mode */
537     case FBIO_FINDMODE:
538         if (scp->sc->fbi != NULL) {
539             ret = ENODEV;
540         } else {
541             ret = fb_ioctl(adp, FBIO_FINDMODE, data);
542         }
543         lwkt_reltoken(&vga_token);
544         return ret;
545
546     case CONS_SETWINORG:        /* set frame buffer window origin */
547     case FBIO_SETWINORG:
548         if (scp != scp->sc->cur_scp) {
549             lwkt_reltoken(&vga_token);
550             return ENODEV;      /* XXX */
551         }
552         if (scp->sc->fbi != NULL) {
553             lwkt_reltoken(&vga_token);
554             return ENODEV;
555         }
556         ret = fb_ioctl(adp, FBIO_SETWINORG, data);
557         lwkt_reltoken(&vga_token);
558         return ret;
559
560     case FBIO_GETWINORG:        /* get frame buffer window origin */
561         if (scp != scp->sc->cur_scp) {
562             lwkt_reltoken(&vga_token);
563             return ENODEV;      /* XXX */
564         }
565         if (scp->sc->fbi != NULL) {
566             ret = ENODEV;
567         } else {
568             ret = fb_ioctl(adp, FBIO_GETWINORG, data);
569         }
570         lwkt_reltoken(&vga_token);
571         return ret;
572
573     case FBIO_GETDISPSTART:
574     case FBIO_SETDISPSTART:
575     case FBIO_GETLINEWIDTH:
576     case FBIO_SETLINEWIDTH:
577         if (scp != scp->sc->cur_scp) {
578             lwkt_reltoken(&vga_token);
579             return ENODEV;      /* XXX */
580         }
581         if (scp->sc->fbi != NULL) {
582             if (cmd == FBIO_GETLINEWIDTH) {
583                 *(u_int *)data = scp->sc->fbi->stride;
584                 ret = 0;
585             } else {
586                 ret = ENODEV;
587             }
588         } else {
589             ret = fb_ioctl(adp, cmd, data);
590         }
591         lwkt_reltoken(&vga_token);
592         return ret;
593
594     case FBIO_BLANK:
595         if (scp != scp->sc->cur_scp) {
596             lwkt_reltoken(&vga_token);
597             return ENODEV;
598         }
599         if (scp->sc->fbi != NULL && ISGRAPHSC(scp)) {
600             if (*(int *)data == V_DISPLAY_ON)
601                 sc_stop_scrn_saver(scp->sc);
602             else
603                 sc_start_scrn_saver(scp->sc);
604             ret = 0;
605         } else {
606             ret = ENODEV;
607         }
608         lwkt_reltoken(&vga_token);
609         return ret;
610
611     case FBIO_GETPALETTE:
612     case FBIO_SETPALETTE:
613     case FBIOPUTCMAP:
614     case FBIOGETCMAP:
615     case FBIOGTYPE:
616     case FBIOGATTR:
617     case FBIOSVIDEO:
618     case FBIOGVIDEO:
619     case FBIOSCURSOR:
620     case FBIOGCURSOR:
621     case FBIOSCURPOS:
622     case FBIOGCURPOS:
623     case FBIOGCURMAX:
624         if (scp != scp->sc->cur_scp) {
625             lwkt_reltoken(&vga_token);
626             return ENODEV;      /* XXX */
627         }
628         if (scp->sc->fbi != NULL) {
629             if (cmd == FBIOGTYPE) {
630                 ((struct fbtype *)data)->fb_type = FBTYPE_DUMBFB;
631                 ((struct fbtype *)data)->fb_height = scp->sc->fbi->height;
632                 ((struct fbtype *)data)->fb_width = scp->sc->fbi->width;
633                 ((struct fbtype *)data)->fb_depth = scp->sc->fbi->depth;
634                 ((struct fbtype *)data)->fb_cmsize = 0;
635                 ((struct fbtype *)data)->fb_size =
636                     scp->sc->fbi->height * scp->sc->fbi->stride;
637                 ret = 0;
638             } else {
639                 ret = ENODEV;
640             }
641         } else {
642             ret = fb_ioctl(adp, cmd, data);
643         }
644         lwkt_reltoken(&vga_token);
645         return ret;
646
647     case KDSETMODE:             /* set current mode of this (virtual) console */
648         switch (*(int *)data) {
649         case KD_TEXT:           /* switch to TEXT (known) mode */
650             /*
651              * If scp->mode is of graphics modes, we don't know which
652              * text mode to switch back to...
653              */
654             if (scp->status & GRAPHICS_MODE) {
655                 lwkt_reltoken(&vga_token);
656                 return EINVAL;
657             }
658             /* restore fonts & palette ! */
659 #if 0
660 #ifndef SC_NO_FONT_LOADING
661             if (scp->sc->fbi == NULL && ISFONTAVAIL(adp->va_flags)
662                 && !(scp->status & (GRAPHICS_MODE | PIXEL_MODE))) {
663                 /*
664                  * FONT KLUDGE
665                  * Don't load fonts for now... XXX
666                  */
667                 if (scp->sc->fonts_loaded & FONT_8)
668                     sc_load_font(scp, 0, 8, scp->sc->font_8, 0, 256);
669                 if (scp->sc->fonts_loaded & FONT_14)
670                     sc_load_font(scp, 0, 14, scp->sc->font_14, 0, 256);
671                 if (scp->sc->fonts_loaded & FONT_16)
672                     sc_load_font(scp, 0, 16, scp->sc->font_16, 0, 256);
673             }
674 #endif /* SC_NO_FONT_LOADING */
675 #endif
676
677             if (scp->sc->fbi == NULL)
678                 load_palette(adp, scp->sc->palette);
679
680             /* move hardware cursor out of the way */
681             if (scp->sc->fbi == NULL)
682                 (*vidsw[adp->va_index]->set_hw_cursor)(adp, -1, -1);
683             /* FALL THROUGH */
684
685         case KD_TEXT1:          /* switch to TEXT (known) mode */
686             /*
687              * If scp->mode is of graphics modes, we don't know which
688              * text/pixel mode to switch back to...
689              */
690             if (scp->status & GRAPHICS_MODE) {
691                 lwkt_reltoken(&vga_token);
692                 return EINVAL;
693             }
694             crit_enter();
695             if ((error = sc_clean_up(scp, FALSE))) {
696                 crit_exit();
697                 lwkt_reltoken(&vga_token);
698                 return error;
699             }
700             scp->status |= UNKNOWN_MODE | MOUSE_HIDDEN;
701             crit_exit();
702             /* no restore fonts & palette */
703             if (scp == scp->sc->cur_scp)
704                 set_mode(scp);
705             sc_clear_screen(scp);
706             scp->status &= ~UNKNOWN_MODE;
707             lwkt_reltoken(&vga_token);
708             return 0;
709
710 #ifdef SC_PIXEL_MODE
711         case KD_PIXEL:          /* pixel (raster) display */
712             if (!(scp->status & (GRAPHICS_MODE | PIXEL_MODE))) {
713                 lwkt_reltoken(&vga_token);
714                 return EINVAL;
715             }
716             if (scp->sc->fbi != NULL) {
717                 lwkt_reltoken(&vga_token);
718                 return ENODEV;
719             }
720             if (scp->status & GRAPHICS_MODE) {
721                 lwkt_reltoken(&vga_token);
722                 return sc_set_pixel_mode(scp, tp, scp->xsize, scp->ysize, 
723                                          scp->font_height);
724             }
725             crit_enter();
726             if ((error = sc_clean_up(scp, FALSE))) {
727                 crit_exit();
728                 lwkt_reltoken(&vga_token);
729                 return error;
730             }
731             scp->status |= (UNKNOWN_MODE | PIXEL_MODE | MOUSE_HIDDEN);
732             crit_exit();
733             if (scp == scp->sc->cur_scp) {
734                 set_mode(scp);
735                 load_palette(adp, scp->sc->palette);
736             }
737             sc_clear_screen(scp);
738             scp->status &= ~UNKNOWN_MODE;
739             lwkt_reltoken(&vga_token);
740             return 0;
741 #endif /* SC_PIXEL_MODE */
742
743         case KD_GRAPHICS:       /* switch to GRAPHICS (unknown) mode */
744             crit_enter();
745             if ((error = sc_clean_up(scp, FALSE))) {
746                 crit_exit();
747                 lwkt_reltoken(&vga_token);
748                 return error;
749             }
750             scp->status |= UNKNOWN_MODE | MOUSE_HIDDEN;
751             crit_exit();
752             lwkt_reltoken(&vga_token);
753             return 0;
754
755         default:
756             lwkt_reltoken(&vga_token);
757             return EINVAL;
758         }
759         /* NOT REACHED */
760
761 #ifdef SC_PIXEL_MODE
762     case KDRASTER:              /* set pixel (raster) display mode */
763         if (scp->sc->fbi != NULL || ISUNKNOWNSC(scp) || ISTEXTSC(scp)) {
764             lwkt_reltoken(&vga_token);
765             return ENODEV;
766         }
767         lwkt_reltoken(&vga_token);
768         return sc_set_pixel_mode(scp, tp, ((int *)data)[0], ((int *)data)[1], 
769                                  ((int *)data)[2]);
770 #endif /* SC_PIXEL_MODE */
771
772     case KDGETMODE:             /* get current mode of this (virtual) console */
773         /* 
774          * From the user program's point of view, KD_PIXEL is the same 
775          * as KD_TEXT... 
776          */
777         *data = ISGRAPHSC(scp) ? KD_GRAPHICS : KD_TEXT;
778         lwkt_reltoken(&vga_token);
779         return 0;
780
781     case KDSBORDER:             /* set border color of this (virtual) console */
782         /* Only values in the range [0..15] allowed */
783         if (*(int *)data < 0 || *(int *)data > 15) {
784                 lwkt_reltoken(&vga_token);
785                 return EINVAL;
786         }
787         scp->border = *(int *)data;
788         if (scp == scp->sc->cur_scp)
789             sc_set_border(scp, scp->border);
790         lwkt_reltoken(&vga_token);
791         return 0;
792     }
793
794     lwkt_reltoken(&vga_token);
795     return ENOIOCTL;
796 }
797
798 static LIST_HEAD(, sc_renderer) sc_rndr_list = 
799         LIST_HEAD_INITIALIZER(sc_rndr_list);
800
801 int
802 sc_render_add(sc_renderer_t *rndr)
803 {
804         LIST_INSERT_HEAD(&sc_rndr_list, rndr, link);
805         return 0;
806 }
807
808 int
809 sc_render_remove(sc_renderer_t *rndr)
810 {
811         /*
812         LIST_REMOVE(rndr, link);
813         */
814         return EBUSY;   /* XXX */
815 }
816
817 sc_rndr_sw_t *
818 sc_render_match(scr_stat *scp, char *name, int model)
819 {
820         const sc_renderer_t **list;
821         const sc_renderer_t *p;
822
823         if (!LIST_EMPTY(&sc_rndr_list)) {
824                 LIST_FOREACH(p, &sc_rndr_list, link) {
825                         if ((strcmp(p->name, name) == 0) &&
826                             (model == p->model)) {
827                                 scp->status &=
828                                     ~(VR_CURSOR_ON | VR_CURSOR_BLINK);
829                                 return p->rndrsw;
830                         }
831                 }
832         } else {
833                 SET_FOREACH(list, scrndr_set) {
834                         p = *list;
835                         if ((strcmp(p->name, name) == 0) &&
836                             (model == p->model)) {
837                                 scp->status &=
838                                     ~(VR_CURSOR_ON | VR_CURSOR_BLINK);
839                                 return p->rndrsw;
840                         }
841                 }
842         }
843
844         return NULL;
845 }
846
847 #define VIRTUAL_TTY(sc, x) ((SC_DEV((sc),(x)) != NULL) ?        \
848         (SC_DEV((sc),(x))->si_tty) : NULL)
849
850 void
851 sc_update_render(scr_stat *scp)
852 {
853         sc_rndr_sw_t *rndr;
854         sc_term_sw_t *sw;
855         struct tty *tp;
856         int prev_ysize, new_ysize;
857 #ifndef SC_NO_HISTORY
858         int old_xpos, old_ypos;
859 #endif
860         int error;
861
862         sw = scp->tsw;
863         if (sw == NULL) {
864                 return;
865         }
866
867         if (scp->rndr == NULL)
868                 return;
869
870         if (scp->fbi_generation == scp->sc->fbi_generation)
871                 return;
872
873         crit_enter();
874         scp->fbi_generation = scp->sc->fbi_generation;
875         /* Needed in case we are implicitly leaving PIXEL_MODE here */
876         if (scp->model != V_INFO_MM_OTHER)
877                 scp->model = V_INFO_MM_TEXT;
878         rndr = NULL;
879         if (strcmp(sw->te_renderer, "*") != 0) {
880                 rndr = sc_render_match(scp, sw->te_renderer, scp->model);
881         }
882         if (rndr == NULL && scp->sc->fbi != NULL) {
883                 rndr = sc_render_match(scp, "kms", scp->model);
884         }
885         if (rndr == NULL) {     /* this shouldn't happen */
886                 rndr = sc_render_match(scp, scp->sc->adp->va_name, scp->model);
887                 if (rndr != NULL)
888                         scp->rndr = rndr;
889                 crit_exit();
890                 return;
891         }
892
893         scp->rndr = rndr;
894         /* Mostly copied from sc_set_text_mode */
895         if ((error = sc_clean_up(scp, FALSE))) {
896                 crit_exit();
897                 return;
898         }
899         new_ysize = 0;
900 #ifndef SC_NO_HISTORY
901         old_xpos = old_ypos = 0;
902         if (scp->history != NULL) {
903                 if (scp->xsize > 0) {
904                         old_xpos = scp->xpos;
905                         old_ypos = scp->ypos;
906                 }
907                 sc_hist_save(scp);
908                 new_ysize = sc_vtb_rows(scp->history);
909         }
910 #endif
911         prev_ysize = scp->ysize;
912         scp->status |= MOUSE_HIDDEN;
913         if (scp->status & GRAPHICS_MODE) {
914                 /*
915                  * We don't handle GRAPHICS_MODE at all when using a KMS
916                  * framebuffer, so silently switch to UNKNOWN_MODE then.
917                  */
918                 scp->status |= UNKNOWN_MODE;
919                 scp->status &= ~GRAPHICS_MODE;
920         }
921         /* Implicitly leave PIXEL_MODE, but stay in UNKNOWN mode */
922         scp->status &= ~(PIXEL_MODE | MOUSE_VISIBLE);
923         scp->xpixel = scp->sc->fbi->width;
924         scp->ypixel = scp->sc->fbi->height;
925
926         /*
927          * Assume square pixels for now
928          */
929         sc_font_scale(scp, 0, 0);
930
931         DPRINTF(1, ("kms console: scale-to %dx%d cols=%d rows=%d\n",
932                 scp->blk_width, scp->blk_height, scp->xsize, scp->ysize));
933
934         /* allocate buffers */
935         sc_alloc_scr_buffer(scp, TRUE, TRUE);
936         sc_init_emulator(scp, NULL);
937 #ifndef SC_NO_CUTPASTE
938         sc_alloc_cut_buffer(scp, FALSE);
939 #endif
940 #ifndef SC_NO_HISTORY
941         sc_alloc_history_buffer(scp, new_ysize, prev_ysize, FALSE);
942         if (scp->history != NULL) {
943                 sc_hist_getback(scp, prev_ysize);
944                 sc_move_cursor(scp, old_xpos, old_ypos);
945         }
946 #endif
947         crit_exit();
948         tp = VIRTUAL_TTY(scp->sc, scp->index);
949         if (tp == NULL)
950                 return;
951         if (tp->t_winsize.ws_col != scp->xsize ||
952             tp->t_winsize.ws_row != scp->ysize) {
953                 tp->t_winsize.ws_col = scp->xsize;
954                 tp->t_winsize.ws_row = scp->ysize;
955                 pgsignal(tp->t_pgrp, SIGWINCH, 1);
956         }
957 }