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