wsdisplay_vcons.c revision 1.68.2.1 1 /* $NetBSD: wsdisplay_vcons.c,v 1.68.2.1 2025/08/02 05:57:08 perseant Exp $ */
2
3 /*-
4 * Copyright (c) 2005, 2006 Michael Lorenz
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: wsdisplay_vcons.c,v 1.68.2.1 2025/08/02 05:57:08 perseant Exp $");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/buf.h>
36 #include <sys/device.h>
37 #include <sys/ioctl.h>
38 #include <sys/malloc.h>
39 #include <sys/mman.h>
40 #include <sys/tty.h>
41 #include <sys/conf.h>
42 #include <sys/proc.h>
43 #include <sys/kthread.h>
44 #include <sys/tprintf.h>
45 #include <sys/atomic.h>
46 #include <sys/kmem.h>
47
48 #include <dev/wscons/wsdisplayvar.h>
49 #include <dev/wscons/wsconsio.h>
50 #include <dev/wsfont/wsfont.h>
51 #include <dev/rasops/rasops.h>
52
53 #include <dev/wscons/wsdisplay_vconsvar.h>
54
55 #ifdef _KERNEL_OPT
56 #include "opt_wsemul.h"
57 #include "opt_wsdisplay_compat.h"
58 #include "opt_vcons.h"
59 #endif
60
61 #ifdef VCONS_DEBUG
62 #define DPRINTF printf
63 #else
64 #define DPRINTF if (0) printf
65 #endif
66
67 struct vcons_data_private {
68 /* accessops */
69 int (*ioctl)(void *, void *, u_long, void *, int, struct lwp *);
70
71 /* rasops */
72 void (*copycols)(void *, int, int, int, int);
73 void (*erasecols)(void *, int, int, int, long);
74 void (*copyrows)(void *, int, int, int);
75 void (*eraserows)(void *, int, int, long);
76 void (*cursor)(void *, int, int, int);
77
78 /* virtual screen management stuff */
79 void (*switch_cb)(void *, int, int);
80 void *switch_cb_arg;
81 struct callout switch_callout;
82 uint32_t switch_pending;
83 LIST_HEAD(, vcons_screen) screens;
84 struct vcons_screen *wanted;
85 const struct wsscreen_descr *currenttype;
86 struct wsscreen_descr *defaulttype;
87 int switch_poll_count;
88
89 #ifdef VCONS_DRAW_INTR
90 int cells;
91 long *attrs;
92 uint32_t *chars;
93 int cursor_offset;
94 callout_t intr;
95 int intr_valid;
96 void *intr_softint;
97 int use_intr; /* use intr drawing when non-zero */
98 #endif
99 };
100
101 static void vcons_dummy_init_screen(void *, struct vcons_screen *, int,
102 long *);
103
104 static int vcons_ioctl(void *, void *, u_long, void *, int, struct lwp *);
105 static int vcons_alloc_screen(void *, const struct wsscreen_descr *, void **,
106 int *, int *, long *);
107 static void vcons_free_screen(void *, void *);
108 static int vcons_show_screen(void *, void *, int, void (*)(void *, int, int),
109 void *);
110 static int vcons_load_font(void *, void *, struct wsdisplay_font *);
111
112 #ifdef WSDISPLAY_SCROLLSUPPORT
113 static void vcons_scroll(void *, void *, int);
114 static void vcons_do_scroll(struct vcons_screen *);
115 #endif
116
117 static void vcons_do_switch(void *);
118
119 /* methods that work only on text buffers */
120 static void vcons_copycols_buffer(void *, int, int, int, int);
121 static void vcons_erasecols_buffer(void *, int, int, int, long);
122 static void vcons_copyrows_buffer(void *, int, int, int);
123 static void vcons_eraserows_buffer(void *, int, int, long);
124 static int vcons_putchar_buffer(void *, int, int, u_int, long);
125
126 /*
127 * actual wrapper methods which call both the _buffer ones above and the
128 * driver supplied ones to do the drawing
129 */
130 static void vcons_copycols(void *, int, int, int, int);
131 static void vcons_erasecols(void *, int, int, int, long);
132 static void vcons_copyrows(void *, int, int, int);
133 static void vcons_eraserows(void *, int, int, long);
134 static void vcons_putchar(void *, int, int, u_int, long);
135 #ifdef VCONS_DRAW_INTR
136 static void vcons_erasecols_cached(void *, int, int, int, long);
137 static void vcons_eraserows_cached(void *, int, int, long);
138 static void vcons_putchar_cached(void *, int, int, u_int, long);
139 #endif
140 static void vcons_cursor(void *, int, int, int);
141 static void vcons_cursor_noread(void *, int, int, int);
142
143 /*
144 * methods that avoid framebuffer reads
145 */
146 static void vcons_copycols_noread(void *, int, int, int, int);
147 static void vcons_copyrows_noread(void *, int, int, int);
148
149
150 /* support for reading/writing text buffers. For wsmoused */
151 static int vcons_putwschar(struct vcons_screen *, struct wsdisplay_char *);
152 static int vcons_getwschar(struct vcons_screen *, struct wsdisplay_char *);
153
154 static void vcons_lock(struct vcons_screen *);
155 static void vcons_unlock(struct vcons_screen *);
156
157 #ifdef VCONS_DRAW_INTR
158 static void vcons_intr(void *);
159 static void vcons_softintr(void *);
160 static void vcons_init_thread(void *);
161 static void vcons_invalidate_cache(struct vcons_data *);
162 #endif
163
164 static inline bool
165 vcons_use_intr(const struct vcons_screen *scr)
166 {
167 #ifdef VCONS_DRAW_INTR
168 return scr->scr_vd->private->use_intr;
169 #else
170 return false;
171 #endif
172 }
173
174 static inline void
175 vcons_dirty(struct vcons_screen *scr)
176 {
177 #ifdef VCONS_DRAW_INTR
178 membar_release();
179 atomic_inc_uint(&scr->scr_dirty);
180 #endif
181 }
182
183 static int
184 vcons_init_common(struct vcons_data *vd, void *cookie,
185 struct wsscreen_descr *def, struct wsdisplay_accessops *ao,
186 int enable_intr)
187 {
188 struct vcons_data_private *vdp;
189
190 /* zero out everything so we can rely on untouched fields being 0 */
191 memset(vd, 0, sizeof(struct vcons_data));
192
193 vd->private = vdp = kmem_zalloc(sizeof(*vdp), KM_SLEEP);
194 vd->cookie = cookie;
195
196 vd->init_screen = vcons_dummy_init_screen;
197 vd->show_screen_cb = NULL;
198
199 /* keep a copy of the accessops that we replace below with our
200 * own wrappers */
201 vdp->ioctl = ao->ioctl;
202
203 /* configure the accessops */
204 ao->ioctl = vcons_ioctl;
205 ao->alloc_screen = vcons_alloc_screen;
206 ao->free_screen = vcons_free_screen;
207 ao->show_screen = vcons_show_screen;
208 ao->load_font = vcons_load_font;
209 #ifdef WSDISPLAY_SCROLLSUPPORT
210 ao->scroll = vcons_scroll;
211 #endif
212
213 LIST_INIT(&vdp->screens);
214 vd->active = NULL;
215 vdp->wanted = NULL;
216 vdp->currenttype = def;
217 vdp->defaulttype = def;
218 callout_init(&vdp->switch_callout, 0);
219 callout_setfunc(&vdp->switch_callout, vcons_do_switch, vd);
220 #ifdef VCONS_DRAW_INTR
221 vdp->cells = 0;
222 vdp->attrs = NULL;
223 vdp->chars = NULL;
224 vdp->cursor_offset = -1;
225 #endif
226
227 /*
228 * a lock to serialize access to the framebuffer.
229 * when switching screens we need to make sure there's no rasops
230 * operation in progress
231 */
232 #ifdef DIAGNOSTIC
233 vdp->switch_poll_count = 0;
234 #endif
235 #ifdef VCONS_DRAW_INTR
236 if (enable_intr) {
237 vdp->intr_softint = softint_establish(SOFTINT_SERIAL,
238 vcons_softintr, vd);
239 callout_init(&vdp->intr, CALLOUT_MPSAFE);
240 callout_setfunc(&vdp->intr, vcons_intr, vd);
241 vdp->intr_valid = 1;
242
243 if (kthread_create(PRI_NONE, 0, NULL, vcons_init_thread, vd,
244 NULL, "vcons_init") != 0) {
245 printf("%s: unable to create thread.\n", __func__);
246 return -1;
247 }
248 }
249 #endif
250 return 0;
251 }
252
253 int
254 vcons_init(struct vcons_data *vd, void *cookie,
255 struct wsscreen_descr *def, struct wsdisplay_accessops *ao)
256 {
257 return vcons_init_common(vd, cookie, def, ao, 1);
258 }
259
260 int
261 vcons_earlyinit(struct vcons_data *vd, void *cookie,
262 struct wsscreen_descr *def, struct wsdisplay_accessops *ao)
263 {
264 return vcons_init_common(vd, cookie, def, ao, 0);
265 }
266
267 static void
268 vcons_lock(struct vcons_screen *scr)
269 {
270 #ifdef VCONS_PARANOIA
271 int s;
272
273 s = splhigh();
274 #endif
275 SCREEN_BUSY(scr);
276 #ifdef VCONS_PARANOIA
277 splx(s);
278 #endif
279 }
280
281 static void
282 vcons_unlock(struct vcons_screen *scr)
283 {
284 #ifdef VCONS_PARANOIA
285 int s;
286
287 s = splhigh();
288 #endif
289 SCREEN_IDLE(scr);
290 #ifdef VCONS_PARANOIA
291 splx(s);
292 #endif
293 }
294
295 static void
296 vcons_dummy_init_screen(void *cookie,
297 struct vcons_screen *scr, int exists,
298 long *defattr)
299 {
300
301 /*
302 * default init_screen() method.
303 * Needs to be overwritten so we bitch and whine in case anyone ends
304 * up in here.
305 */
306 printf("vcons_init_screen: dummy function called. Your driver is "
307 "supposed to supply a replacement for proper operation\n");
308 }
309
310 static int
311 vcons_alloc_buffers(struct vcons_data *vd, struct vcons_screen *scr)
312 {
313 struct rasops_info *ri = &scr->scr_ri;
314 int cnt, i;
315 #ifdef VCONS_DRAW_INTR
316 struct vcons_data_private *vdp = vd->private;
317 int size;
318 #endif
319
320 /*
321 * we allocate both chars and attributes in one chunk, attributes first
322 * because they have the (potentially) bigger alignment
323 */
324 #ifdef WSDISPLAY_SCROLLSUPPORT
325 cnt = (ri->ri_rows + WSDISPLAY_SCROLLBACK_LINES) * ri->ri_cols;
326 scr->scr_lines_in_buffer = WSDISPLAY_SCROLLBACK_LINES;
327 scr->scr_current_line = 0;
328 scr->scr_line_wanted = 0;
329 scr->scr_offset_to_zero = ri->ri_cols * WSDISPLAY_SCROLLBACK_LINES;
330 scr->scr_current_offset = scr->scr_offset_to_zero;
331 #else
332 cnt = ri->ri_rows * ri->ri_cols;
333 #endif
334 scr->scr_attrs = malloc(cnt * (sizeof(long) +
335 sizeof(uint32_t)), M_DEVBUF, M_WAITOK);
336 if (scr->scr_attrs == NULL)
337 return ENOMEM;
338
339 scr->scr_chars = (uint32_t *)&scr->scr_attrs[cnt];
340
341 /*
342 * fill the attribute buffer with *defattr, chars with 0x20
343 * since we don't know if the driver tries to mimic firmware output or
344 * reset everything we do nothing to VRAM here, any driver that feels
345 * the need to clear screen or something will have to do it on its own
346 * Additional screens will start out in the background anyway so
347 * cleaning or not only really affects the initial console screen
348 */
349 for (i = 0; i < cnt; i++) {
350 scr->scr_attrs[i] = scr->scr_defattr;
351 scr->scr_chars[i] = 0x20;
352 }
353
354 #ifdef VCONS_DRAW_INTR
355 size = ri->ri_cols * ri->ri_rows;
356 if (size > vdp->cells) {
357 if (vdp->chars != NULL)
358 free(vdp->chars, M_DEVBUF);
359 if (vdp->attrs != NULL)
360 free(vdp->attrs, M_DEVBUF);
361 vdp->cells = size;
362 vdp->chars = malloc(size * sizeof(uint32_t), M_DEVBUF,
363 M_WAITOK|M_ZERO);
364 vdp->attrs = malloc(size * sizeof(long), M_DEVBUF,
365 M_WAITOK|M_ZERO);
366 vcons_invalidate_cache(vd);
367 } else if (SCREEN_IS_VISIBLE(scr))
368 vcons_invalidate_cache(vd);
369 #endif
370 return 0;
371 }
372
373 int
374 vcons_init_screen(struct vcons_data *vd, struct vcons_screen *scr,
375 int existing, long *defattr)
376 {
377 struct vcons_data_private *vdp = vd->private;
378 struct rasops_info *ri = &scr->scr_ri;
379 int i;
380
381 scr->scr_cookie = vd->cookie;
382 scr->scr_vd = scr->scr_origvd = vd;
383 scr->scr_busy = 0;
384
385 if (scr->scr_type == NULL)
386 scr->scr_type = vdp->defaulttype;
387
388 /*
389 * call the driver-supplied init_screen function which is expected
390 * to set up rasops_info, override cursor() and probably others
391 */
392 vd->init_screen(vd->cookie, scr, existing, defattr);
393
394 /*
395 * save the non virtual console aware rasops and replace them with
396 * our wrappers
397 */
398 vdp->eraserows = ri->ri_ops.eraserows;
399 vdp->erasecols = ri->ri_ops.erasecols;
400 scr->putchar = ri->ri_ops.putchar;
401
402 if (scr->scr_flags & VCONS_NO_COPYCOLS) {
403 vdp->copycols = vcons_copycols_noread;
404 } else {
405 vdp->copycols = ri->ri_ops.copycols;
406 }
407
408 if (scr->scr_flags & VCONS_NO_COPYROWS) {
409 vdp->copyrows = vcons_copyrows_noread;
410 } else {
411 vdp->copyrows = ri->ri_ops.copyrows;
412 }
413
414 if (scr->scr_flags & VCONS_NO_CURSOR) {
415 vdp->cursor = vcons_cursor_noread;
416 } else {
417 vdp->cursor = ri->ri_ops.cursor;
418 }
419
420 ri->ri_ops.eraserows = vcons_eraserows;
421 ri->ri_ops.erasecols = vcons_erasecols;
422 ri->ri_ops.putchar = vcons_putchar;
423 ri->ri_ops.cursor = vcons_cursor;
424 ri->ri_ops.copycols = vcons_copycols;
425 ri->ri_ops.copyrows = vcons_copyrows;
426
427
428 ri->ri_hw = scr;
429
430 i = ri->ri_ops.allocattr(ri, WS_DEFAULT_FG, WS_DEFAULT_BG, 0, defattr);
431 if (i != 0) {
432 #ifdef DIAGNOSTIC
433 printf("vcons: error allocating attribute %d\n", i);
434 #endif
435 scr->scr_defattr = 0;
436 } else
437 scr->scr_defattr = *defattr;
438
439 vcons_alloc_buffers(vd, scr);
440
441 if (vd->active == NULL) {
442 vd->active = scr;
443 SCREEN_VISIBLE(scr);
444 }
445
446 if (existing) {
447 SCREEN_VISIBLE(scr);
448 vd->active = scr;
449 } else {
450 SCREEN_INVISIBLE(scr);
451 }
452
453 LIST_INSERT_HEAD(&vdp->screens, scr, next);
454 return 0;
455 }
456
457 static int
458 vcons_load_font(void *v, void *cookie, struct wsdisplay_font *f)
459 {
460 struct vcons_data *vd = v;
461 struct vcons_data_private *vdp = vd->private;
462 struct vcons_screen *scr = cookie;
463 struct rasops_info *ri;
464 struct wsdisplay_font *font;
465 char buf[64], *at;
466 int flags = WSFONT_FIND_BITMAP, fcookie, h = 0;
467
468 /* see if we're asked to add a font or use it */
469 if (scr == NULL)
470 return 0;
471
472 ri = &scr->scr_ri;
473
474 /* see if the driver knows how to handle multiple fonts */
475 if ((scr->scr_flags & VCONS_LOADFONT) == 0) {
476 return EOPNOTSUPP;
477 }
478
479 /* now see what fonts we can use */
480 if (ri->ri_flg & RI_ENABLE_ALPHA) {
481 flags |= WSFONT_FIND_ALPHA;
482 }
483
484 strncpy(buf, f->name, 63);
485 buf[63] = 0;
486 at = strchr(buf, '@');
487 if (at != NULL) {
488 int stat;
489 at[0] = 0;
490 at++;
491 DPRINTF("got '%s'\n", at);
492 h = strtoi(at, NULL, 10, 1, 99, &stat);
493 if (stat != 0) h = 0;
494 DPRINTF("looking for %d\n", h);
495 }
496 fcookie = wsfont_find(buf, 0, h, 0,
497 /* bitorder */
498 scr->scr_flags & VCONS_FONT_BITS_R2L ?
499 WSDISPLAY_FONTORDER_R2L : WSDISPLAY_FONTORDER_L2R,
500 /* byteorder */
501 scr->scr_flags & VCONS_FONT_BYTES_R2L ?
502 WSDISPLAY_FONTORDER_R2L : WSDISPLAY_FONTORDER_L2R,
503 flags);
504 if (fcookie == -1)
505 return ENOENT;
506
507 wsfont_lock(fcookie, &font);
508 if (font == NULL)
509 return EINVAL;
510
511 /* ok, we got a font. Now clear the screen with the old parameters */
512 if (SCREEN_IS_VISIBLE(scr))
513 vdp->eraserows(ri, 0, ri->ri_rows, scr->scr_defattr);
514
515 vcons_lock(vd->active);
516 #ifdef VCONS_DRAW_INTR
517 callout_halt(&vdp->intr, NULL);
518 #endif
519 /* set the new font and re-initialize things */
520 ri->ri_font = font;
521 wsfont_unlock(ri->ri_wsfcookie);
522 ri->ri_wsfcookie = fcookie;
523
524 vd->init_screen(vd->cookie, scr, 1, &scr->scr_defattr);
525 DPRINTF("caps %x %x\n", scr->scr_type->capabilities, ri->ri_caps);
526 if (scr->scr_type->capabilities & WSSCREEN_RESIZE) {
527 scr->scr_type->nrows = ri->ri_rows;
528 scr->scr_type->ncols = ri->ri_cols;
529 DPRINTF("new size %d %d\n", ri->ri_rows, ri->ri_cols);
530 }
531
532
533 /* now, throw the old buffers away */
534 if (scr->scr_attrs)
535 free(scr->scr_attrs, M_DEVBUF);
536 /* allocate new buffers */
537 vcons_alloc_buffers(vd, scr);
538
539 /* save the potentially changed ri_ops */
540 vdp->eraserows = ri->ri_ops.eraserows;
541 vdp->erasecols = ri->ri_ops.erasecols;
542 scr->putchar = ri->ri_ops.putchar;
543 vdp->cursor = ri->ri_ops.cursor;
544
545 if (scr->scr_flags & VCONS_NO_COPYCOLS) {
546 vdp->copycols = vcons_copycols_noread;
547 } else {
548 vdp->copycols = ri->ri_ops.copycols;
549 }
550
551 if (scr->scr_flags & VCONS_NO_COPYROWS) {
552 vdp->copyrows = vcons_copyrows_noread;
553 } else {
554 vdp->copyrows = ri->ri_ops.copyrows;
555 }
556
557 if (scr->scr_flags & VCONS_NO_CURSOR) {
558 vdp->cursor = vcons_cursor_noread;
559 } else {
560 vdp->cursor = ri->ri_ops.cursor;
561 }
562
563 /* and put our wrappers back */
564 ri->ri_ops.eraserows = vcons_eraserows;
565 ri->ri_ops.erasecols = vcons_erasecols;
566 ri->ri_ops.putchar = vcons_putchar;
567 ri->ri_ops.cursor = vcons_cursor;
568 ri->ri_ops.copycols = vcons_copycols;
569 ri->ri_ops.copyrows = vcons_copyrows;
570 vcons_unlock(vd->active);
571
572 /* notify things that we're about to redraw */
573 if (vd->show_screen_cb != NULL)
574 vd->show_screen_cb(scr, vd->show_screen_cookie);
575
576 #ifdef VCONS_DRAW_INTR
577 /*
578 * XXX
579 * Something(tm) craps all over VRAM somewhere up there if we're
580 * using VCONS_DRAW_INTR. Until I figure out what causes it, just
581 * redraw the screen for now.
582 */
583 vcons_redraw_screen(vd->active);
584 callout_schedule(&vdp->intr, mstohz(33));
585 #endif
586 /* no need to draw anything, wsdisplay should reset the terminal */
587
588 return 0;
589 }
590
591 static void
592 vcons_do_switch(void *arg)
593 {
594 struct vcons_data *vd = arg;
595 struct vcons_data_private *vdp = vd->private;
596 struct vcons_screen *scr, *oldscr;
597
598 scr = vdp->wanted;
599 if (!scr) {
600 printf("vcons_switch_screen: disappeared\n");
601 vdp->switch_cb(vdp->switch_cb_arg, EIO, 0);
602 return;
603 }
604 oldscr = vd->active; /* can be NULL! */
605
606 /*
607 * if there's an old, visible screen we mark it invisible and wait
608 * until it's not busy so we can safely switch
609 */
610 if (oldscr != NULL) {
611 SCREEN_INVISIBLE(oldscr);
612 if (SCREEN_IS_BUSY(oldscr)) {
613 callout_schedule(&vdp->switch_callout, 1);
614 #ifdef DIAGNOSTIC
615 /* bitch if we wait too long */
616 vdp->switch_poll_count++;
617 if (vdp->switch_poll_count > 100) {
618 panic("vcons: screen still busy");
619 }
620 #endif
621 return;
622 }
623 /* invisible screen -> no visible cursor image */
624 oldscr->scr_ri.ri_flg &= ~RI_CURSOR;
625 #ifdef DIAGNOSTIC
626 vdp->switch_poll_count = 0;
627 #endif
628 }
629
630 if (scr == oldscr)
631 return;
632
633 #ifdef DIAGNOSTIC
634 if (SCREEN_IS_VISIBLE(scr))
635 printf("vcons_switch_screen: already active");
636 #endif
637
638 #ifdef notyet
639 if (vdp->currenttype != type) {
640 vcons_set_screentype(vd, type);
641 vdp->currenttype = type;
642 }
643 #endif
644
645 SCREEN_VISIBLE(scr);
646 vd->active = scr;
647 vdp->wanted = NULL;
648
649 #ifdef VCONS_DRAW_INTR
650 vcons_invalidate_cache(vd);
651 #endif
652
653 if (vd->show_screen_cb != NULL)
654 vd->show_screen_cb(scr, vd->show_screen_cookie);
655
656 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0)
657 vcons_redraw_screen(scr);
658
659 if (vdp->switch_cb)
660 vdp->switch_cb(vdp->switch_cb_arg, 0, 0);
661 }
662
663 void
664 vcons_redraw_screen(struct vcons_screen *scr)
665 {
666 uint32_t *charptr = scr->scr_chars, c;
667 long *attrptr = scr->scr_attrs, a, last_a = 0, mask, cmp, acmp;
668 struct rasops_info *ri = &scr->scr_ri;
669 struct vcons_data *vd = scr->scr_vd;
670 struct vcons_data_private *vdp = vd->private;
671 int i, j, offset, boffset = 0, start = -1;
672
673 mask = 0x00ff00ff; /* background and flags */
674 cmp = 0xffffffff; /* never match anything */
675 vcons_lock(scr);
676 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
677
678 /*
679 * only clear the screen when RI_FULLCLEAR is set since we're
680 * going to overwrite every single character cell anyway
681 */
682 if (ri->ri_flg & RI_FULLCLEAR) {
683 vdp->eraserows(ri, 0, ri->ri_rows,
684 scr->scr_defattr);
685 cmp = scr->scr_defattr & mask;
686 }
687
688 /* redraw the screen */
689 #ifdef WSDISPLAY_SCROLLSUPPORT
690 offset = scr->scr_current_offset;
691 #else
692 offset = 0;
693 #endif
694 for (i = 0; i < ri->ri_rows; i++) {
695 start = -1;
696 for (j = 0; j < ri->ri_cols; j++) {
697 /*
698 * no need to use the wrapper function - we
699 * don't change any characters or attributes
700 * and we already made sure the screen we're
701 * working on is visible
702 */
703 c = charptr[offset];
704 a = attrptr[offset];
705 acmp = a & mask;
706 if (c == ' ') {
707 /*
708 * if we already erased the background
709 * and if this blank uses the same
710 * colour and flags we don't need to do
711 * anything here
712 */
713 if (acmp == cmp && start == -1)
714 goto next;
715 /*
716 * see if we can optimize things a
717 * little bit by drawing stretches of
718 * blanks using erasecols
719 */
720
721 if (start == -1) {
722 start = j;
723 last_a = acmp;
724 } else if (acmp != last_a) {
725 /*
726 * different attr, need to
727 * flush & restart
728 */
729 vdp->erasecols(ri, i, start,
730 j - start, last_a);
731 start = j;
732 last_a = acmp;
733 }
734 } else {
735 if (start != -1) {
736 vdp->erasecols(ri, i, start,
737 j - start, last_a);
738 start = -1;
739 }
740
741 scr->putchar(ri, i, j, c, a);
742 }
743 next:
744 #ifdef VCONS_DRAW_INTR
745 vdp->chars[boffset] = charptr[offset];
746 vdp->attrs[boffset] = attrptr[offset];
747 #endif
748 offset++;
749 boffset++;
750 }
751 /* end of the line - draw all deferred blanks, if any */
752 if (start != -1) {
753 vdp->erasecols(ri, i, start, j - start, last_a);
754 }
755 }
756 ri->ri_flg &= ~RI_CURSOR;
757 scr->scr_vd->private->cursor(ri, 1, ri->ri_crow, ri->ri_ccol);
758 #ifdef VCONS_DRAW_INTR
759 vdp->cursor_offset = ri->ri_crow * ri->ri_cols + ri->ri_ccol;
760 #endif
761 }
762 vcons_unlock(scr);
763 }
764
765 void
766 vcons_update_screen(struct vcons_screen *scr)
767 {
768 #ifdef VCONS_DRAW_INTR
769 uint32_t *charptr = scr->scr_chars;
770 long *attrptr = scr->scr_attrs;
771 struct rasops_info *ri = &scr->scr_ri;
772 struct vcons_data *vd = scr->scr_vd;
773 struct vcons_data_private *vdp = vd->private;
774 int i, j, offset, boffset = 0;
775
776 vcons_lock(scr);
777 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
778
779 /* redraw the screen */
780 #ifdef WSDISPLAY_SCROLLSUPPORT
781 offset = scr->scr_current_offset;
782 #else
783 offset = 0;
784 #endif
785 /*
786 * we mark the character cell occupied by the cursor as dirty
787 * so we don't have to deal with it
788 * notice that this isn't necessarily the position where rasops
789 * thinks it is, just where we drew it the last time
790 */
791 if (vdp->cursor_offset >= 0)
792 vdp->attrs[vdp->cursor_offset] = 0xffffffff;
793
794 for (i = 0; i < ri->ri_rows; i++) {
795 for (j = 0; j < ri->ri_cols; j++) {
796 /*
797 * no need to use the wrapper function - we
798 * don't change any characters or attributes
799 * and we already made sure the screen we're
800 * working on is visible
801 */
802 if ((vdp->chars[boffset] != charptr[offset]) ||
803 (vdp->attrs[boffset] != attrptr[offset])) {
804 scr->putchar(ri, i, j,
805 charptr[offset], attrptr[offset]);
806 vdp->chars[boffset] = charptr[offset];
807 vdp->attrs[boffset] = attrptr[offset];
808 }
809 offset++;
810 boffset++;
811 }
812 }
813 ri->ri_flg &= ~RI_CURSOR;
814 scr->scr_vd->private->cursor(ri, 1, ri->ri_crow, ri->ri_ccol);
815 vdp->cursor_offset = ri->ri_crow * ri->ri_cols + ri->ri_ccol;
816 }
817 vcons_unlock(scr);
818 #else /* !VCONS_DRAW_INTR */
819 vcons_redraw_screen(scr);
820 #endif
821 }
822
823 static int
824 vcons_ioctl(void *v, void *vs, u_long cmd, void *data, int flag,
825 struct lwp *l)
826 {
827 struct vcons_data *vd = v;
828 struct vcons_data_private *vdp = vd->private;
829 int error = 0;
830
831
832 switch (cmd) {
833 case WSDISPLAYIO_GETWSCHAR:
834 error = vcons_getwschar((struct vcons_screen *)vs,
835 (struct wsdisplay_char *)data);
836 break;
837
838 case WSDISPLAYIO_PUTWSCHAR:
839 error = vcons_putwschar((struct vcons_screen *)vs,
840 (struct wsdisplay_char *)data);
841 break;
842
843 case WSDISPLAYIO_SET_POLLING: {
844 int poll = *(int *)data;
845
846 /* first call the driver's ioctl handler */
847 if (vdp->ioctl != NULL)
848 error = (*vdp->ioctl)(v, vs, cmd, data, flag, l);
849 if (poll) {
850 vcons_enable_polling(vd);
851 vcons_hard_switch(LIST_FIRST(&vdp->screens));
852 } else
853 vcons_disable_polling(vd);
854 }
855 break;
856
857 case WSDISPLAYIO_GFONT: {
858 struct wsdisplay_getfont *gf = data;
859 size_t actual;
860 struct wsdisplay_font *font;
861 const char *fontname;
862
863 font = ((struct vcons_screen *)vs)->scr_ri.ri_font;
864 fontname = font && font->name ? font->name : "";
865 error = copyoutstr(fontname, gf->gf_name, gf->gf_size, &actual);
866 if (!error)
867 gf->gf_actual = actual;
868 }
869 break;
870
871 default:
872 if (vdp->ioctl != NULL)
873 error = (*vdp->ioctl)(v, vs, cmd, data, flag, l);
874 else
875 error = EPASSTHROUGH;
876 }
877
878 return error;
879 }
880
881 static int
882 vcons_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
883 int *curxp, int *curyp, long *defattrp)
884 {
885 struct vcons_data *vd = v;
886 struct vcons_data_private *vdp = vd->private;
887 struct vcons_screen *scr;
888 struct wsscreen_descr *t = __UNCONST(type);
889 int ret;
890
891 scr = malloc(sizeof(struct vcons_screen), M_DEVBUF, M_WAITOK | M_ZERO);
892 if (scr == NULL)
893 return ENOMEM;
894
895 scr->scr_flags = 0;
896 scr->scr_status = 0;
897 scr->scr_busy = 0;
898 scr->scr_type = __UNCONST(type);
899
900 ret = vcons_init_screen(vd, scr, 0, defattrp);
901 if (ret != 0) {
902 free(scr, M_DEVBUF);
903 return ret;
904 }
905 if (t->capabilities & WSSCREEN_RESIZE) {
906 t->nrows = scr->scr_ri.ri_rows;
907 t->ncols = scr->scr_ri.ri_cols;
908 }
909
910 if (vd->active == NULL) {
911 SCREEN_VISIBLE(scr);
912 vd->active = scr;
913 vdp->currenttype = type;
914 }
915
916 *cookiep = scr;
917 *curxp = scr->scr_ri.ri_ccol;
918 *curyp = scr->scr_ri.ri_crow;
919 return 0;
920 }
921
922 static void
923 vcons_free_screen(void *v, void *cookie)
924 {
925 struct vcons_data *vd = v;
926 struct vcons_screen *scr = cookie;
927
928 vcons_lock(scr);
929 /* there should be no rasops activity here */
930
931 LIST_REMOVE(scr, next);
932
933 if ((scr->scr_flags & VCONS_SCREEN_IS_STATIC) == 0) {
934 free(scr->scr_attrs, M_DEVBUF);
935 free(scr, M_DEVBUF);
936 } else {
937 /*
938 * maybe we should just restore the old rasops_info methods
939 * and free the character/attribute buffer here?
940 */
941 #ifdef VCONS_DEBUG
942 panic("vcons_free_screen: console");
943 #else
944 printf("vcons_free_screen: console\n");
945 #endif
946 }
947
948 if (vd->active == scr)
949 vd->active = NULL;
950 }
951
952 static int
953 vcons_show_screen(void *v, void *cookie, int waitok,
954 void (*cb)(void *, int, int), void *cb_arg)
955 {
956 struct vcons_data *vd = v;
957 struct vcons_data_private *vdp = vd->private;
958 struct vcons_screen *scr;
959
960 scr = cookie;
961 if (scr == vd->active)
962 return 0;
963
964 vdp->wanted = scr;
965 vdp->switch_cb = cb;
966 vdp->switch_cb_arg = cb_arg;
967 if (cb) {
968 callout_schedule(&vdp->switch_callout, 0);
969 return EAGAIN;
970 }
971
972 vcons_do_switch(vd);
973 return 0;
974 }
975
976 /* wrappers for rasops_info methods */
977
978 static void
979 vcons_copycols_buffer(void *cookie, int row, int srccol, int dstcol, int ncols)
980 {
981 struct rasops_info *ri = cookie;
982 struct vcons_screen *scr = ri->ri_hw;
983 int from = srccol + row * ri->ri_cols;
984 int to = dstcol + row * ri->ri_cols;
985 int offset = vcons_offset_to_zero(scr);
986
987 memmove(&scr->scr_attrs[offset + to], &scr->scr_attrs[offset + from],
988 ncols * sizeof(long));
989 memmove(&scr->scr_chars[offset + to], &scr->scr_chars[offset + from],
990 ncols * sizeof(uint32_t));
991
992 vcons_dirty(scr);
993 }
994
995 static void
996 vcons_copycols(void *cookie, int row, int srccol, int dstcol, int ncols)
997 {
998 struct rasops_info *ri = cookie;
999 struct vcons_screen *scr = ri->ri_hw;
1000
1001 vcons_copycols_buffer(cookie, row, srccol, dstcol, ncols);
1002
1003 if (vcons_use_intr(scr))
1004 return;
1005
1006 vcons_lock(scr);
1007 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
1008 #if defined(VCONS_DRAW_INTR)
1009 vcons_update_screen(scr);
1010 #else
1011 scr->scr_vd->private->copycols(cookie, row, srccol, dstcol,
1012 ncols);
1013 #endif
1014 }
1015 vcons_unlock(scr);
1016 }
1017
1018 static void
1019 vcons_copycols_noread(void *cookie, int row, int srccol, int dstcol, int ncols)
1020 {
1021 struct rasops_info *ri = cookie;
1022 struct vcons_screen *scr = ri->ri_hw;
1023 #ifdef VCONS_DRAW_INTR
1024 struct vcons_data *vd = scr->scr_vd;
1025 struct vcons_data_private *vdp = vd->private;
1026 #endif
1027
1028 vcons_lock(scr);
1029 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
1030 int pos, c, offset, ppos;
1031
1032 #ifdef WSDISPLAY_SCROLLSUPPORT
1033 offset = scr->scr_current_offset;
1034 #else
1035 offset = 0;
1036 #endif
1037 ppos = ri->ri_cols * row + dstcol;
1038 pos = ppos + offset;
1039 for (c = dstcol; c < (dstcol + ncols); c++) {
1040 #ifdef VCONS_DRAW_INTR
1041 if ((scr->scr_chars[pos] != vdp->chars[ppos]) ||
1042 (scr->scr_attrs[pos] != vdp->attrs[ppos])) {
1043 scr->putchar(cookie, row, c,
1044 scr->scr_chars[pos], scr->scr_attrs[pos]);
1045 vdp->chars[ppos] = scr->scr_chars[pos];
1046 vdp->attrs[ppos] = scr->scr_attrs[pos];
1047 }
1048 #else
1049 scr->putchar(cookie, row, c, scr->scr_chars[pos],
1050 scr->scr_attrs[pos]);
1051 #endif
1052 pos++;
1053 ppos++;
1054 }
1055 if (ri->ri_crow == row &&
1056 (ri->ri_ccol >= dstcol && ri->ri_ccol < (dstcol + ncols )))
1057 ri->ri_flg &= ~RI_CURSOR;
1058 }
1059 vcons_unlock(scr);
1060 }
1061
1062 static void
1063 vcons_erasecols_buffer(void *cookie, int row, int startcol, int ncols, long fillattr)
1064 {
1065 struct rasops_info *ri = cookie;
1066 struct vcons_screen *scr = ri->ri_hw;
1067 int start = startcol + row * ri->ri_cols;
1068 int end = start + ncols, i;
1069 int offset = vcons_offset_to_zero(scr);
1070
1071 for (i = start; i < end; i++) {
1072 scr->scr_attrs[offset + i] = fillattr;
1073 scr->scr_chars[offset + i] = 0x20;
1074 }
1075
1076 vcons_dirty(scr);
1077 }
1078
1079 #ifdef VCONS_DRAW_INTR
1080 static void
1081 vcons_erasecols_cached(void *cookie, int row, int startcol, int ncols, long fillattr)
1082 {
1083 struct rasops_info *ri = cookie;
1084 struct vcons_screen *scr = ri->ri_hw;
1085 struct vcons_data *vd = scr->scr_vd;
1086 struct vcons_data_private *vdp = vd->private;
1087 int i, pos = row * ri->ri_cols + startcol;
1088
1089 vdp->erasecols(cookie, row, startcol, ncols, fillattr);
1090 for (i = pos; i < ncols; i++) {
1091 vdp->chars[i] = scr->scr_chars[i];
1092 vdp->attrs[i] = scr->scr_attrs[i];
1093 }
1094 }
1095 #endif
1096
1097 static void
1098 vcons_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr)
1099 {
1100 struct rasops_info *ri = cookie;
1101 struct vcons_screen *scr = ri->ri_hw;
1102
1103 vcons_erasecols_buffer(cookie, row, startcol, ncols, fillattr);
1104
1105 if (vcons_use_intr(scr))
1106 return;
1107
1108 vcons_lock(scr);
1109 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
1110 #ifdef VCONS_DRAW_INTR
1111 vcons_erasecols_cached(cookie, row, startcol, ncols,
1112 fillattr);
1113 #else
1114 scr->scr_vd->private->erasecols(cookie, row, startcol, ncols,
1115 fillattr);
1116 #endif
1117 }
1118 vcons_unlock(scr);
1119 }
1120
1121 static void
1122 vcons_copyrows_buffer(void *cookie, int srcrow, int dstrow, int nrows)
1123 {
1124 struct rasops_info *ri = cookie;
1125 struct vcons_screen *scr = ri->ri_hw;
1126 int from, to, len;
1127 int offset = vcons_offset_to_zero(scr);
1128
1129 /* do we need to scroll the back buffer? */
1130 if (dstrow == 0 && offset != 0) {
1131 from = ri->ri_cols * srcrow;
1132 to = ri->ri_cols * dstrow;
1133
1134 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from],
1135 offset * sizeof(long));
1136 memmove(&scr->scr_chars[to], &scr->scr_chars[from],
1137 offset * sizeof(uint32_t));
1138 }
1139 from = ri->ri_cols * srcrow + offset;
1140 to = ri->ri_cols * dstrow + offset;
1141 len = ri->ri_cols * nrows;
1142
1143 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from],
1144 len * sizeof(long));
1145 memmove(&scr->scr_chars[to], &scr->scr_chars[from],
1146 len * sizeof(uint32_t));
1147
1148 vcons_dirty(scr);
1149 }
1150
1151 static void
1152 vcons_copyrows(void *cookie, int srcrow, int dstrow, int nrows)
1153 {
1154 struct rasops_info *ri = cookie;
1155 struct vcons_screen *scr = ri->ri_hw;
1156
1157 vcons_copyrows_buffer(cookie, srcrow, dstrow, nrows);
1158
1159 if (vcons_use_intr(scr))
1160 return;
1161
1162 vcons_lock(scr);
1163 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
1164 #if defined(VCONS_DRAW_INTR)
1165 vcons_update_screen(scr);
1166 #else
1167 scr->scr_vd->private->copyrows(cookie, srcrow, dstrow, nrows);
1168 #endif
1169 }
1170 vcons_unlock(scr);
1171 }
1172
1173 static void
1174 vcons_copyrows_noread(void *cookie, int srcrow, int dstrow, int nrows)
1175 {
1176 struct rasops_info *ri = cookie;
1177 struct vcons_screen *scr = ri->ri_hw;
1178 #ifdef VCONS_DRAW_INTR
1179 struct vcons_data *vd = scr->scr_vd;
1180 struct vcons_data_private *vdp = vd->private;
1181 #endif
1182 vcons_lock(scr);
1183 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
1184 int pos, l, c, offset, ppos;
1185
1186 #ifdef WSDISPLAY_SCROLLSUPPORT
1187 offset = scr->scr_current_offset;
1188 #else
1189 offset = 0;
1190 #endif
1191 ppos = ri->ri_cols * dstrow;
1192 pos = ppos + offset;
1193 for (l = dstrow; l < (dstrow + nrows); l++) {
1194 for (c = 0; c < ri->ri_cols; c++) {
1195 #ifdef VCONS_DRAW_INTR
1196 if ((scr->scr_chars[pos] != vdp->chars[ppos]) ||
1197 (scr->scr_attrs[pos] != vdp->attrs[ppos])) {
1198 scr->putchar(cookie, l, c,
1199 scr->scr_chars[pos], scr->scr_attrs[pos]);
1200 vdp->chars[ppos] = scr->scr_chars[pos];
1201 vdp->attrs[ppos] = scr->scr_attrs[pos];
1202 }
1203 #else
1204 scr->putchar(cookie, l, c, scr->scr_chars[pos],
1205 scr->scr_attrs[pos]);
1206 #endif
1207 pos++;
1208 ppos++;
1209 }
1210 }
1211 if (ri->ri_crow >= dstrow && ri->ri_crow < (dstrow + nrows))
1212 ri->ri_flg &= ~RI_CURSOR;
1213 }
1214 vcons_unlock(scr);
1215 }
1216
1217 static void
1218 vcons_eraserows_buffer(void *cookie, int row, int nrows, long fillattr)
1219 {
1220 struct rasops_info *ri = cookie;
1221 struct vcons_screen *scr = ri->ri_hw;
1222 int offset = vcons_offset_to_zero(scr);
1223 int start, end, i;
1224
1225 start = ri->ri_cols * row + offset;
1226 end = ri->ri_cols * (row + nrows) + offset;
1227
1228 for (i = start; i < end; i++) {
1229 scr->scr_attrs[i] = fillattr;
1230 scr->scr_chars[i] = 0x20;
1231 }
1232
1233 vcons_dirty(scr);
1234 }
1235
1236 #ifdef VCONS_DRAW_INTR
1237 static void
1238 vcons_eraserows_cached(void *cookie, int row, int nrows, long fillattr)
1239 {
1240 struct rasops_info *ri = cookie;
1241 struct vcons_screen *scr = ri->ri_hw;
1242 struct vcons_data *vd = scr->scr_vd;
1243 struct vcons_data_private *vdp = vd->private;
1244 int i, pos = row * ri->ri_cols, end = (row+nrows) * ri->ri_cols;
1245
1246 for (i = pos; i < end; i++) {
1247 vdp->chars[i] = 0x20;
1248 vdp->attrs[i] = fillattr;
1249 }
1250 vdp->eraserows(cookie, row, nrows, fillattr);
1251 }
1252 #endif
1253
1254 static void
1255 vcons_eraserows(void *cookie, int row, int nrows, long fillattr)
1256 {
1257 struct rasops_info *ri = cookie;
1258 struct vcons_screen *scr = ri->ri_hw;
1259
1260 vcons_eraserows_buffer(cookie, row, nrows, fillattr);
1261
1262 if (vcons_use_intr(scr))
1263 return;
1264
1265 vcons_lock(scr);
1266 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
1267 #ifdef VCONS_DRAW_INTR
1268 vcons_eraserows_cached(cookie, row, nrows, fillattr);
1269 #else
1270 scr->scr_vd->private->eraserows(cookie, row, nrows, fillattr);
1271 #endif
1272 }
1273 vcons_unlock(scr);
1274 }
1275
1276 static int
1277 vcons_putchar_buffer(void *cookie, int row, int col, u_int c, long attr)
1278 {
1279 struct rasops_info *ri = cookie;
1280 struct vcons_screen *scr = ri->ri_hw;
1281 int offset = vcons_offset_to_zero(scr);
1282 int pos, ret = 0;
1283
1284 if ((row >= 0) && (row < ri->ri_rows) && (col >= 0) &&
1285 (col < ri->ri_cols)) {
1286 pos = col + row * ri->ri_cols;
1287 ret = (scr->scr_attrs[pos + offset] != attr) ||
1288 (scr->scr_chars[pos + offset] != c);
1289 scr->scr_attrs[pos + offset] = attr;
1290 scr->scr_chars[pos + offset] = c;
1291 }
1292
1293 if (ret)
1294 vcons_dirty(scr);
1295 return ret;
1296 }
1297
1298 #ifdef VCONS_DRAW_INTR
1299 static void
1300 vcons_putchar_cached(void *cookie, int row, int col, u_int c, long attr)
1301 {
1302 struct rasops_info *ri = cookie;
1303 struct vcons_screen *scr = ri->ri_hw;
1304 struct vcons_data *vd = scr->scr_vd;
1305 struct vcons_data_private *vdp = vd->private;
1306 int pos = row * ri->ri_cols + col;
1307
1308 if ((vdp->chars == NULL) || (vdp->attrs == NULL)) {
1309 scr->putchar(cookie, row, col, c, attr);
1310 return;
1311 }
1312 if ((vdp->chars[pos] != c) || (vdp->attrs[pos] != attr)) {
1313 vdp->attrs[pos] = attr;
1314 vdp->chars[pos] = c;
1315 scr->putchar(cookie, row, col, c, attr);
1316 }
1317 }
1318 #endif
1319
1320 static void
1321 vcons_putchar(void *cookie, int row, int col, u_int c, long attr)
1322 {
1323 struct rasops_info *ri = cookie;
1324 struct vcons_screen *scr = ri->ri_hw;
1325 int need_draw;
1326
1327 need_draw = vcons_putchar_buffer(cookie, row, col, c, attr);
1328
1329 if (vcons_use_intr(scr))
1330 return;
1331
1332 vcons_lock(scr);
1333 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
1334 #ifdef VCONS_DRAW_INTR
1335 if (need_draw)
1336 vcons_putchar_cached(cookie, row, col, c, attr);
1337 #else
1338 if (row == ri->ri_crow && col == ri->ri_ccol) {
1339 ri->ri_flg &= ~RI_CURSOR;
1340 scr->putchar(cookie, row, col, c, attr);
1341 } else if (need_draw)
1342 scr->putchar(cookie, row, col, c, attr);
1343 #endif
1344 }
1345 vcons_unlock(scr);
1346 }
1347
1348 static void
1349 vcons_cursor(void *cookie, int on, int row, int col)
1350 {
1351 struct rasops_info *ri = cookie;
1352 struct vcons_screen *scr = ri->ri_hw;
1353
1354 if (vcons_use_intr(scr)) {
1355 vcons_lock(scr);
1356 if (scr->scr_ri.ri_crow != row || scr->scr_ri.ri_ccol != col) {
1357 scr->scr_ri.ri_crow = row;
1358 scr->scr_ri.ri_ccol = col;
1359 vcons_dirty(scr);
1360 }
1361 vcons_unlock(scr);
1362 return;
1363 }
1364
1365 vcons_lock(scr);
1366
1367 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
1368 scr->scr_vd->private->cursor(cookie, on, row, col);
1369 } else {
1370 scr->scr_ri.ri_crow = row;
1371 scr->scr_ri.ri_ccol = col;
1372 }
1373 vcons_unlock(scr);
1374 }
1375
1376 static void
1377 vcons_cursor_noread(void *cookie, int on, int row, int col)
1378 {
1379 struct rasops_info *ri = cookie;
1380 struct vcons_screen *scr = ri->ri_hw;
1381 int offset = 0, ofs;
1382
1383 #ifdef WSDISPLAY_SCROLLSUPPORT
1384 offset = scr->scr_current_offset;
1385 #endif
1386 ofs = offset + ri->ri_crow * ri->ri_cols + ri->ri_ccol;
1387 if ((ri->ri_flg & RI_CURSOR) &&
1388 (((scr->scr_flags & VCONS_DONT_READ) != VCONS_DONT_READ) || on)) {
1389 scr->putchar(cookie, ri->ri_crow, ri->ri_ccol,
1390 scr->scr_chars[ofs], scr->scr_attrs[ofs]);
1391 ri->ri_flg &= ~RI_CURSOR;
1392 }
1393 ri->ri_crow = row;
1394 ri->ri_ccol = col;
1395 ofs = offset + ri->ri_crow * ri->ri_cols + ri->ri_ccol;
1396 if (on) {
1397 scr->putchar(cookie, row, col, scr->scr_chars[ofs],
1398 #ifdef VCONS_DEBUG_CURSOR_NOREAD
1399 /* draw a red cursor so we can tell which cursor()
1400 * implementation is being used */
1401 ((scr->scr_attrs[ofs] & 0xff00ffff) ^ 0x0f000000) |
1402 0x00010000);
1403 #else
1404 scr->scr_attrs[ofs] ^ 0x0f0f0000);
1405 #endif
1406 ri->ri_flg |= RI_CURSOR;
1407 }
1408 }
1409
1410 /* methods to read/write characters via ioctl() */
1411
1412 static int
1413 vcons_putwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc)
1414 {
1415 long attr;
1416 struct rasops_info *ri;
1417 int error;
1418
1419 KASSERT(scr != NULL);
1420 KASSERT(wsc != NULL);
1421
1422 ri = &scr->scr_ri;
1423
1424 /* allow col as linear index if row == 0 */
1425 if (wsc->row == 0) {
1426 if (wsc->col < 0 || wsc->col > (ri->ri_cols * ri->ri_rows))
1427 return EINVAL;
1428 int rem;
1429 rem = wsc->col % ri->ri_cols;
1430 wsc->row = wsc->col / ri->ri_cols;
1431 DPRINTF("off %d -> %d, %d\n", wsc->col, rem, wsc->row);
1432 wsc->col = rem;
1433 } else {
1434 if (__predict_false(wsc->col < 0 || wsc->col >= ri->ri_cols))
1435 return EINVAL;
1436
1437 if (__predict_false(wsc->row < 0 || wsc->row >= ri->ri_rows))
1438 return EINVAL;
1439 }
1440
1441 error = ri->ri_ops.allocattr(ri, wsc->foreground,
1442 wsc->background, wsc->flags, &attr);
1443 if (error)
1444 return error;
1445 vcons_putchar(ri, wsc->row, wsc->col, wsc->letter, attr);
1446 DPRINTF("vcons_putwschar(%d, %d, %x, %lx\n", wsc->row, wsc->col,
1447 wsc->letter, attr);
1448 return 0;
1449 }
1450
1451 static int
1452 vcons_getwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc)
1453 {
1454 int offset;
1455 long attr;
1456 struct rasops_info *ri;
1457 int fg, bg, ul;
1458
1459 KASSERT(scr != NULL);
1460 KASSERT(wsc != NULL);
1461
1462 ri = &scr->scr_ri;
1463
1464 /* allow col as linear index if row == 0 */
1465 if (wsc->row == 0) {
1466 if (wsc->col < 0 || wsc->col > (ri->ri_cols * ri->ri_rows))
1467 return EINVAL;
1468 int rem;
1469 rem = wsc->col % ri->ri_cols;
1470 wsc->row = wsc->col / ri->ri_cols;
1471 DPRINTF("off %d -> %d, %d\n", wsc->col, rem, wsc->row);
1472 wsc->col = rem;
1473 } else {
1474 if (__predict_false(wsc->col < 0 || wsc->col >= ri->ri_cols))
1475 return EINVAL;
1476
1477 if (__predict_false(wsc->row < 0 || wsc->row >= ri->ri_rows))
1478 return EINVAL;
1479 }
1480
1481 offset = ri->ri_cols * wsc->row + wsc->col;
1482 offset += vcons_offset_to_zero(scr);
1483 wsc->letter = scr->scr_chars[offset];
1484 attr = scr->scr_attrs[offset];
1485
1486 DPRINTF("vcons_getwschar: %d, %d, %x, %lx\n", wsc->row,
1487 wsc->col, wsc->letter, attr);
1488
1489 /*
1490 * this is ugly. We need to break up an attribute into colours and
1491 * flags but there's no rasops method to do that so we must rely on
1492 * the 'canonical' encoding.
1493 */
1494
1495 /* only fetches underline attribute */
1496 /* rasops_unpack_attr(attr, &fg, &bg, &ul); */
1497 fg = (attr >> 24) & 0xf;
1498 bg = (attr >> 16) & 0xf;
1499 ul = (attr & 1);
1500
1501 wsc->foreground = fg;
1502 wsc->background = bg;
1503
1504 /* clear trashed bits and restore underline flag */
1505 attr &= ~(WSATTR_HILIT | WSATTR_BLINK | WSATTR_UNDERLINE);
1506 if (ul)
1507 attr |= WSATTR_UNDERLINE;
1508
1509 /* restore highlight boost */
1510 if (attr & WSATTR_HILIT)
1511 if (wsc->foreground >= 8)
1512 wsc->foreground -= 8;
1513
1514 /* we always use colors, even when not stored */
1515 attr |= WSATTR_WSCOLORS;
1516 return 0;
1517 }
1518
1519 int
1520 vcons_offset_to_zero(const struct vcons_screen *scr)
1521 {
1522 #ifdef WSDISPLAY_SCROLLSUPPORT
1523 return scr->scr_offset_to_zero;
1524 #else
1525 return 0;
1526 #endif
1527 }
1528
1529 #ifdef WSDISPLAY_SCROLLSUPPORT
1530
1531 static void
1532 vcons_scroll(void *cookie, void *vs, int where)
1533 {
1534 struct vcons_screen *scr = vs;
1535
1536 if (where == 0) {
1537 scr->scr_line_wanted = 0;
1538 } else {
1539 scr->scr_line_wanted = scr->scr_line_wanted - where;
1540 if (scr->scr_line_wanted < 0)
1541 scr->scr_line_wanted = 0;
1542 if (scr->scr_line_wanted > scr->scr_lines_in_buffer)
1543 scr->scr_line_wanted = scr->scr_lines_in_buffer;
1544 }
1545
1546 if (scr->scr_line_wanted != scr->scr_current_line) {
1547
1548 vcons_do_scroll(scr);
1549 }
1550 }
1551
1552 static void
1553 vcons_do_scroll(struct vcons_screen *scr)
1554 {
1555 int dist, from, to, num;
1556 int r_offset, r_start;
1557 int i, j;
1558
1559 if (scr->scr_line_wanted == scr->scr_current_line)
1560 return;
1561 dist = scr->scr_line_wanted - scr->scr_current_line;
1562 scr->scr_current_line = scr->scr_line_wanted;
1563 scr->scr_current_offset = scr->scr_ri.ri_cols *
1564 (scr->scr_lines_in_buffer - scr->scr_current_line);
1565 if (abs(dist) >= scr->scr_ri.ri_rows) {
1566 vcons_redraw_screen(scr);
1567 return;
1568 }
1569 /* scroll and redraw only what we really have to */
1570 if (dist > 0) {
1571 /* we scroll down */
1572 from = 0;
1573 to = dist;
1574 num = scr->scr_ri.ri_rows - dist;
1575 /* now the redraw parameters */
1576 r_offset = scr->scr_current_offset;
1577 r_start = 0;
1578 } else {
1579 /* scrolling up */
1580 to = 0;
1581 from = -dist;
1582 num = scr->scr_ri.ri_rows + dist;
1583 r_offset = scr->scr_current_offset + num * scr->scr_ri.ri_cols;
1584 r_start = num;
1585 }
1586 scr->scr_vd->private->copyrows(scr, from, to, num);
1587 for (i = 0; i < abs(dist); i++) {
1588 for (j = 0; j < scr->scr_ri.ri_cols; j++) {
1589 #ifdef VCONS_DRAW_INTR
1590 vcons_putchar_cached(scr, i + r_start, j,
1591 scr->scr_chars[r_offset],
1592 scr->scr_attrs[r_offset]);
1593 #else
1594 scr->putchar(scr, i + r_start, j,
1595 scr->scr_chars[r_offset],
1596 scr->scr_attrs[r_offset]);
1597 #endif
1598 r_offset++;
1599 }
1600 }
1601
1602 if (scr->scr_line_wanted == 0) {
1603 /* this was a reset - need to draw the cursor */
1604 scr->scr_ri.ri_flg &= ~RI_CURSOR;
1605 scr->scr_vd->private->cursor(scr, 1, scr->scr_ri.ri_crow,
1606 scr->scr_ri.ri_ccol);
1607 }
1608 }
1609
1610 #endif /* WSDISPLAY_SCROLLSUPPORT */
1611
1612 #ifdef VCONS_DRAW_INTR
1613 static void
1614 vcons_intr(void *cookie)
1615 {
1616 struct vcons_data *vd = cookie;
1617 struct vcons_data_private *vdp = vd->private;
1618
1619 softint_schedule(vdp->intr_softint);
1620 }
1621
1622 static void
1623 vcons_softintr(void *cookie)
1624 {
1625 struct vcons_data *vd = cookie;
1626 struct vcons_data_private *vdp = vd->private;
1627 struct vcons_screen *scr = vd->active;
1628 unsigned int dirty;
1629
1630 if (scr && vdp->use_intr) {
1631 if (!SCREEN_IS_BUSY(scr)) {
1632 dirty = atomic_swap_uint(&scr->scr_dirty, 0);
1633 membar_acquire();
1634 if (vdp->use_intr == 2) {
1635 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0) {
1636 vdp->use_intr = 1;
1637 vcons_redraw_screen(scr);
1638 }
1639 } else if (dirty > 0) {
1640 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0)
1641 vcons_update_screen(scr);
1642 }
1643 }
1644 }
1645
1646 callout_schedule(&vdp->intr, mstohz(33));
1647 }
1648
1649 static void
1650 vcons_init_thread(void *cookie)
1651 {
1652 struct vcons_data *vd = (struct vcons_data *)cookie;
1653 struct vcons_data_private *vdp = vd->private;
1654
1655 vdp->use_intr = 2;
1656 callout_schedule(&vdp->intr, mstohz(33));
1657 kthread_exit(0);
1658 }
1659 #endif /* VCONS_DRAW_INTR */
1660
1661 void
1662 vcons_enable_polling(struct vcons_data *vd)
1663 {
1664 struct vcons_screen *scr = vd->active;
1665
1666 #ifdef VCONS_DRAW_INTR
1667 struct vcons_data_private *vdp = vd->private;
1668
1669 vdp->use_intr = 0;
1670 #endif
1671
1672 if (scr && !SCREEN_IS_BUSY(scr)) {
1673 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0)
1674 vcons_redraw_screen(scr);
1675 }
1676 }
1677
1678 void
1679 vcons_disable_polling(struct vcons_data *vd)
1680 {
1681 #ifdef VCONS_DRAW_INTR
1682 struct vcons_data_private *vdp = vd->private;
1683 struct vcons_screen *scr = vd->active;
1684
1685 if (!vdp->intr_valid)
1686 return;
1687
1688 vdp->use_intr = 2;
1689 if (scr)
1690 vcons_dirty(scr);
1691 #endif
1692 }
1693
1694 void
1695 vcons_hard_switch(struct vcons_screen *scr)
1696 {
1697 struct vcons_data *vd = scr->scr_vd;
1698 struct vcons_data_private *vdp = vd->private;
1699 struct vcons_screen *oldscr = vd->active;
1700
1701 if (oldscr) {
1702 SCREEN_INVISIBLE(oldscr);
1703 oldscr->scr_ri.ri_flg &= ~RI_CURSOR;
1704 }
1705 SCREEN_VISIBLE(scr);
1706 vd->active = scr;
1707 vdp->wanted = NULL;
1708
1709 if (vd->show_screen_cb != NULL)
1710 vd->show_screen_cb(scr, vd->show_screen_cookie);
1711 }
1712
1713 #ifdef VCONS_DRAW_INTR
1714 static void
1715 vcons_invalidate_cache(struct vcons_data *vd)
1716 {
1717 struct vcons_data_private *vdp = vd->private;
1718 int i;
1719
1720 for (i = 0; i < vdp->cells; i++) {
1721 vdp->chars[i] = -1;
1722 vdp->attrs[i] = -1;
1723 }
1724 }
1725 #endif
1726