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