wsdisplay_vcons.c revision 1.6 1 /* $NetBSD: wsdisplay_vcons.c,v 1.6 2006/04/15 17:48:24 jmmv 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 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the NetBSD
18 * Foundation, Inc. and its contributors.
19 * 4. Neither the name of The NetBSD Foundation nor the names of its
20 * contributors may be used to endorse or promote products derived
21 * from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
27 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: wsdisplay_vcons.c,v 1.6 2006/04/15 17:48:24 jmmv Exp $");
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/buf.h>
43 #include <sys/device.h>
44 #include <sys/ioctl.h>
45 #include <sys/malloc.h>
46 #include <sys/mman.h>
47 #include <sys/tty.h>
48 #include <sys/conf.h>
49 #include <sys/proc.h>
50 #include <sys/kthread.h>
51 #include <sys/tprintf.h>
52
53 #include <dev/wscons/wsdisplayvar.h>
54 #include <dev/wscons/wsconsio.h>
55 #include <dev/wsfont/wsfont.h>
56 #include <dev/rasops/rasops.h>
57
58 #include <dev/wscons/wsdisplay_vconsvar.h>
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, caddr_t, 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 static void vcons_do_switch(struct vcons_data *);
71
72 /* methods that work only on text buffers */
73 static void vcons_copycols_buffer(void *, int, int, int, int);
74 static void vcons_erasecols_buffer(void *, int, int, int, long);
75 static void vcons_copyrows_buffer(void *, int, int, int);
76 static void vcons_eraserows_buffer(void *, int, int, long);
77 static void vcons_putchar_buffer(void *, int, int, u_int, long);
78
79 /*
80 * actual wrapper methods which call both the _buffer ones above and the
81 * driver supplied ones to do the drawing
82 */
83 static void vcons_copycols(void *, int, int, int, int);
84 static void vcons_erasecols(void *, int, int, int, long);
85 static void vcons_copyrows(void *, int, int, int);
86 static void vcons_eraserows(void *, int, int, long);
87 static void vcons_putchar(void *, int, int, u_int, long);
88 static void vcons_cursor(void *, int, int, int);
89
90 /* support for readin/writing text buffers. For wsmoused */
91 static int vcons_putwschar(struct vcons_screen *, struct wsdisplay_char *);
92 static int vcons_getwschar(struct vcons_screen *, struct wsdisplay_char *);
93
94 static void vcons_lock(struct vcons_screen *);
95 static void vcons_unlock(struct vcons_screen *);
96
97
98 int
99 vcons_init(struct vcons_data *vd, void *cookie, struct wsscreen_descr *def,
100 struct wsdisplay_accessops *ao)
101 {
102
103 /* zero out everything so we can rely on untouched fields being 0 */
104 memset(vd, 0, sizeof(struct vcons_data));
105
106 vd->cookie = cookie;
107
108 vd->init_screen = vcons_dummy_init_screen;
109 vd->show_screen_cb = NULL;
110
111 /* keep a copy of the accessops that we replace below with our
112 * own wrappers */
113 vd->ioctl = ao->ioctl;
114
115 /* configure the accessops */
116 ao->ioctl = vcons_ioctl;
117 ao->alloc_screen = vcons_alloc_screen;
118 ao->free_screen = vcons_free_screen;
119 ao->show_screen = vcons_show_screen;
120
121 LIST_INIT(&vd->screens);
122 vd->active = NULL;
123 vd->wanted = NULL;
124 vd->currenttype = def;
125 callout_init(&vd->switch_callout);
126
127 /*
128 * a lock to serialize access to the framebuffer.
129 * when switching screens we need to make sure there's no rasops
130 * operation in progress
131 */
132 #ifdef DIAGNOSTIC
133 vd->switch_poll_count = 0;
134 #endif
135 return 0;
136 }
137
138 static void
139 vcons_lock(struct vcons_screen *scr)
140 {
141 #ifdef VCONS_PARANOIA
142 int s;
143
144 s = splhigh();
145 #endif
146 SCREEN_BUSY(scr);
147 #ifdef VCONS_PARANOIA
148 splx(s);
149 #endif
150 }
151
152 static void
153 vcons_unlock(struct vcons_screen *scr)
154 {
155 #ifdef VCONS_PARANOIA
156 int s;
157
158 s = splhigh();
159 #endif
160 SCREEN_IDLE(scr);
161 #ifdef VCONS_PARANOIA
162 splx(s);
163 #endif
164 }
165
166 static void
167 vcons_dummy_init_screen(void *cookie, struct vcons_screen *scr, int exists,
168 long *defattr)
169 {
170
171 /*
172 * default init_screen() method.
173 * Needs to be overwritten so we bitch and whine in case anyone ends
174 * up in here.
175 */
176 printf("vcons_init_screen: dummy function called. Your driver is "
177 "supposed to supply a replacement for proper operation\n");
178 }
179
180 int
181 vcons_init_screen(struct vcons_data *vd, struct vcons_screen *scr,
182 int existing, long *defattr)
183 {
184 struct rasops_info *ri = &scr->scr_ri;
185 int cnt, i;
186
187 scr->scr_cookie = vd->cookie;
188 scr->scr_vd = scr->scr_origvd = vd;
189 SCREEN_IDLE(scr);
190
191 /*
192 * call the driver-supplied init_screen function which is expected
193 * to set up rasops_info, override cursor() and probably others
194 */
195 vd->init_screen(vd->cookie, scr, existing, defattr);
196
197 /*
198 * save the non virtual console aware rasops and replace them with
199 * our wrappers
200 */
201 vd->eraserows = ri->ri_ops.eraserows;
202 vd->copyrows = ri->ri_ops.copyrows;
203 vd->erasecols = ri->ri_ops.erasecols;
204 vd->copycols = ri->ri_ops.copycols;
205 vd->putchar = ri->ri_ops.putchar;
206 vd->cursor = ri->ri_ops.cursor;
207
208 ri->ri_ops.eraserows = vcons_eraserows;
209 ri->ri_ops.copyrows = vcons_copyrows;
210 ri->ri_ops.erasecols = vcons_erasecols;
211 ri->ri_ops.copycols = vcons_copycols;
212 ri->ri_ops.putchar = vcons_putchar;
213 ri->ri_ops.cursor = vcons_cursor;
214 ri->ri_hw = scr;
215
216 /*
217 * we allocate both chars and attributes in one chunk, attributes first
218 * because they have the (potentially) bigger alignment
219 */
220 cnt = ri->ri_rows * ri->ri_cols;
221 scr->scr_attrs = (long *)malloc(cnt * (sizeof(long) +
222 sizeof(uint16_t)), M_DEVBUF, M_WAITOK);
223 if (scr->scr_attrs == NULL)
224 return ENOMEM;
225
226 scr->scr_chars = (uint16_t *)&scr->scr_attrs[cnt];
227
228 ri->ri_ops.allocattr(ri, WS_DEFAULT_FG, WS_DEFAULT_BG, 0, defattr);
229 scr->scr_defattr = *defattr;
230
231 /*
232 * fill the attribute buffer with *defattr, chars with 0x20
233 * since we don't know if the driver tries to mimic firmware output or
234 * reset everything we do nothing to VRAM here, any driver that feels
235 * the need to clear screen or something will have to do it on its own
236 * Additional screens will start out in the background anyway so
237 * cleaning or not only really affects the initial console screen
238 */
239 for (i = 0; i < cnt; i++) {
240 scr->scr_attrs[i] = *defattr;
241 scr->scr_chars[i] = 0x20;
242 }
243
244 if(vd->active == NULL) {
245 vd->active = scr;
246 SCREEN_VISIBLE(scr);
247 }
248
249 if (existing) {
250 SCREEN_VISIBLE(scr);
251 vd->active = scr;
252 } else {
253 SCREEN_INVISIBLE(scr);
254 }
255
256 LIST_INSERT_HEAD(&vd->screens, scr, next);
257 return 0;
258 }
259
260 static void
261 vcons_do_switch(struct vcons_data *vd)
262 {
263 struct vcons_screen *scr, *oldscr;
264
265 scr = vd->wanted;
266 if (!scr) {
267 printf("vcons_switch_screen: disappeared\n");
268 vd->switch_cb(vd->switch_cb_arg, EIO, 0);
269 return;
270 }
271 oldscr = vd->active; /* can be NULL! */
272
273 /*
274 * if there's an old, visible screen we mark it invisible and wait
275 * until it's not busy so we can safely switch
276 */
277 if (oldscr != NULL) {
278 SCREEN_INVISIBLE(oldscr);
279 if (SCREEN_IS_BUSY(oldscr)) {
280 callout_reset(&vd->switch_callout, 1,
281 (void(*)(void *))vcons_do_switch, vd);
282 #ifdef DIAGNOSTIC
283 /* bitch if we wait too long */
284 vd->switch_poll_count++;
285 if (vd->switch_poll_count > 100) {
286 panic("vcons: screen still busy");
287 }
288 #endif
289 return;
290 }
291 /* invisible screen -> no visible cursor image */
292 oldscr->scr_ri.ri_flg &= ~RI_CURSOR;
293 #ifdef DIAGNOSTIC
294 vd->switch_poll_count = 0;
295 #endif
296 }
297
298 if (scr == oldscr)
299 return;
300
301 #ifdef DIAGNOSTIC
302 if (SCREEN_IS_VISIBLE(scr))
303 panic("vcons_switch_screen: already active");
304 #endif
305
306 #ifdef notyet
307 if (vd->currenttype != type) {
308 vcons_set_screentype(vd, type);
309 vd->currenttype = type;
310 }
311 #endif
312
313 SCREEN_VISIBLE(scr);
314 vd->active = scr;
315 vd->wanted = NULL;
316
317 if (vd->show_screen_cb != NULL)
318 vd->show_screen_cb(scr);
319
320 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0)
321 vcons_redraw_screen(scr);
322
323 if (vd->switch_cb)
324 vd->switch_cb(vd->switch_cb_arg, 0, 0);
325 }
326
327 void
328 vcons_redraw_screen(struct vcons_screen *scr)
329 {
330 uint16_t *charptr = scr->scr_chars;
331 long *attrptr = scr->scr_attrs;
332 struct rasops_info *ri = &scr->scr_ri;
333 int i, j, offset;
334
335 vcons_lock(scr);
336 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
337
338 /*
339 * only clear the screen when RI_FULLCLEAR is set since we're
340 * going to overwrite every single character cell anyway
341 */
342 if (ri->ri_flg & RI_FULLCLEAR) {
343 scr->scr_vd->eraserows(ri, 0, ri->ri_rows,
344 scr->scr_defattr);
345 }
346
347 /* redraw the screen */
348 offset = 0;
349 for (i = 0; i < ri->ri_rows; i++) {
350 for (j = 0; j < ri->ri_cols; j++) {
351 /*
352 * no need to use the wrapper function - we
353 * don't change any characters or attributes
354 * and we already made sure the screen we're
355 * working on is visible
356 */
357 scr->scr_vd->putchar(ri, i, j,
358 charptr[offset], attrptr[offset]);
359 offset++;
360 }
361 }
362 ri->ri_flg &= ~RI_CURSOR;
363 scr->scr_vd->cursor(ri, 1, ri->ri_crow, ri->ri_ccol);
364 }
365 vcons_unlock(scr);
366 }
367
368 static int
369 vcons_ioctl(void *v, void *vs, u_long cmd, caddr_t data, int flag,
370 struct lwp *l)
371 {
372 struct vcons_data *vd = v;
373 int error;
374
375 switch (cmd) {
376 case WSDISPLAYIO_GETWSCHAR:
377 error = vcons_getwschar((struct vcons_screen *)vs,
378 (struct wsdisplay_char *)data);
379 break;
380
381 case WSDISPLAYIO_PUTWSCHAR:
382 error = vcons_putwschar((struct vcons_screen *)vs,
383 (struct wsdisplay_char *)data);
384 break;
385
386 default:
387 if (vd->ioctl != NULL)
388 error = (*vd->ioctl)(v, vs, cmd, data, flag, l);
389 else
390 error = EPASSTHROUGH;
391 }
392
393 return error;
394 }
395
396 static int
397 vcons_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
398 int *curxp, int *curyp, long *defattrp)
399 {
400 struct vcons_data *vd = v;
401 struct vcons_screen *scr;
402 int ret;
403
404 scr = malloc(sizeof(struct vcons_screen), M_DEVBUF, M_WAITOK | M_ZERO);
405 if (scr == NULL)
406 return ENOMEM;
407
408 scr->scr_flags = 0;
409 scr->scr_status = 0;
410 scr->scr_busy = 0;
411 scr->scr_type = type;
412
413 ret = vcons_init_screen(vd, scr, 0, defattrp);
414 if (ret != 0) {
415 free(scr, M_DEVBUF);
416 return ret;
417 }
418
419 if (vd->active == NULL) {
420 SCREEN_VISIBLE(scr);
421 vd->active = scr;
422 vd->currenttype = type;
423 }
424
425 *cookiep = scr;
426 *curxp = scr->scr_ri.ri_ccol;
427 *curyp = scr->scr_ri.ri_crow;
428 return 0;
429 }
430
431 static void
432 vcons_free_screen(void *v, void *cookie)
433 {
434 struct vcons_data *vd = v;
435 struct vcons_screen *scr = cookie;
436
437 vcons_lock(scr);
438 /* there should be no rasops activity here */
439
440 LIST_REMOVE(scr, next);
441
442 if ((scr->scr_flags & VCONS_SCREEN_IS_STATIC) == 0) {
443 free(scr->scr_attrs, M_DEVBUF);
444 free(scr, M_DEVBUF);
445 } else {
446 /*
447 * maybe we should just restore the old rasops_info methods
448 * and free the character/attribute buffer here?
449 */
450 #ifdef VCONS_DEBUG
451 panic("vcons_free_screen: console");
452 #else
453 printf("vcons_free_screen: console\n");
454 #endif
455 }
456
457 if (vd->active == scr)
458 vd->active = NULL;
459 }
460
461 static int
462 vcons_show_screen(void *v, void *cookie, int waitok,
463 void (*cb)(void *, int, int), void *cb_arg)
464 {
465 struct vcons_data *vd = v;
466 struct vcons_screen *scr;
467
468 scr = cookie;
469 if (scr == vd->active)
470 return 0;
471
472 vd->wanted = scr;
473 vd->switch_cb = cb;
474 vd->switch_cb_arg = cb_arg;
475 if (cb) {
476 callout_reset(&vd->switch_callout, 0,
477 (void(*)(void *))vcons_do_switch, vd);
478 return EAGAIN;
479 }
480
481 vcons_do_switch(vd);
482 return 0;
483 }
484
485 /* wrappers for rasops_info methods */
486
487 static void
488 vcons_copycols_buffer(void *cookie, int row, int srccol, int dstcol, int ncols)
489 {
490 struct rasops_info *ri = cookie;
491 struct vcons_screen *scr = ri->ri_hw;
492 int from = srccol + row * ri->ri_cols;
493 int to = dstcol + row * ri->ri_cols;
494
495 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from],
496 ncols * sizeof(long));
497 memmove(&scr->scr_chars[to], &scr->scr_chars[from],
498 ncols * sizeof(uint16_t));
499 }
500
501 static void
502 vcons_copycols(void *cookie, int row, int srccol, int dstcol, int ncols)
503 {
504 struct rasops_info *ri = cookie;
505 struct vcons_screen *scr = ri->ri_hw;
506
507 vcons_copycols_buffer(cookie, row, srccol, dstcol, ncols);
508
509 vcons_lock(scr);
510 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
511 scr->scr_vd->copycols(cookie, row, srccol, dstcol, ncols);
512 }
513 vcons_unlock(scr);
514 }
515
516 static void
517 vcons_erasecols_buffer(void *cookie, int row, int startcol, int ncols, long fillattr)
518 {
519 struct rasops_info *ri = cookie;
520 struct vcons_screen *scr = ri->ri_hw;
521 int start = startcol + row * ri->ri_cols;
522 int end = start + ncols, i;
523
524 for (i = start; i < end; i++) {
525 scr->scr_attrs[i] = fillattr;
526 scr->scr_chars[i] = 0x20;
527 }
528 }
529
530 static void
531 vcons_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr)
532 {
533 struct rasops_info *ri = cookie;
534 struct vcons_screen *scr = ri->ri_hw;
535
536 vcons_erasecols_buffer(cookie, row, startcol, ncols, fillattr);
537
538 vcons_lock(scr);
539 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
540 scr->scr_vd->erasecols(cookie, row, startcol, ncols,
541 fillattr);
542 }
543 vcons_unlock(scr);
544 }
545
546 static void
547 vcons_copyrows_buffer(void *cookie, int srcrow, int dstrow, int nrows)
548 {
549 struct rasops_info *ri = cookie;
550 struct vcons_screen *scr = ri->ri_hw;
551 int from, to, len;
552
553 from = ri->ri_cols * srcrow;
554 to = ri->ri_cols * dstrow;
555 len = ri->ri_cols * nrows;
556
557 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from],
558 len * sizeof(long));
559 memmove(&scr->scr_chars[to], &scr->scr_chars[from],
560 len * sizeof(uint16_t));
561 }
562
563 static void
564 vcons_copyrows(void *cookie, int srcrow, int dstrow, int nrows)
565 {
566 struct rasops_info *ri = cookie;
567 struct vcons_screen *scr = ri->ri_hw;
568
569 vcons_copyrows_buffer(cookie, srcrow, dstrow, nrows);
570
571 vcons_lock(scr);
572 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
573 scr->scr_vd->copyrows(cookie, srcrow, dstrow, nrows);
574 }
575 vcons_unlock(scr);
576 }
577
578 static void
579 vcons_eraserows_buffer(void *cookie, int row, int nrows, long fillattr)
580 {
581 struct rasops_info *ri = cookie;
582 struct vcons_screen *scr = ri->ri_hw;
583 int start, end, i;
584
585 start = ri->ri_cols * row;
586 end = ri->ri_cols * (row + nrows);
587
588 for (i = start; i < end; i++) {
589 scr->scr_attrs[i] = fillattr;
590 scr->scr_chars[i] = 0x20;
591 }
592 }
593
594 static void
595 vcons_eraserows(void *cookie, int row, int nrows, long fillattr)
596 {
597 struct rasops_info *ri = cookie;
598 struct vcons_screen *scr = ri->ri_hw;
599
600 vcons_eraserows_buffer(cookie, row, nrows, fillattr);
601
602 vcons_lock(scr);
603 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
604 scr->scr_vd->eraserows(cookie, row, nrows, fillattr);
605 }
606 vcons_unlock(scr);
607 }
608
609 static void
610 vcons_putchar_buffer(void *cookie, int row, int col, u_int c, long attr)
611 {
612 struct rasops_info *ri = cookie;
613 struct vcons_screen *scr = ri->ri_hw;
614 int pos;
615
616 if ((row >= 0) && (row < ri->ri_rows) && (col >= 0) &&
617 (col < ri->ri_cols)) {
618 pos = col + row * ri->ri_cols;
619 scr->scr_attrs[pos] = attr;
620 scr->scr_chars[pos] = c;
621 }
622 }
623
624 static void
625 vcons_putchar(void *cookie, int row, int col, u_int c, long attr)
626 {
627 struct rasops_info *ri = cookie;
628 struct vcons_screen *scr = ri->ri_hw;
629
630 vcons_putchar_buffer(cookie, row, col, c, attr);
631
632 vcons_lock(scr);
633 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
634 scr->scr_vd->putchar(cookie, row, col, c, attr);
635 }
636 vcons_unlock(scr);
637 }
638
639 static void
640 vcons_cursor(void *cookie, int on, int row, int col)
641 {
642 struct rasops_info *ri = cookie;
643 struct vcons_screen *scr = ri->ri_hw;
644
645 vcons_lock(scr);
646 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
647 scr->scr_vd->cursor(cookie, on, row, col);
648 } else {
649 scr->scr_ri.ri_crow = row;
650 scr->scr_ri.ri_ccol = col;
651 }
652 vcons_unlock(scr);
653 }
654
655 /* methods to read/write characters via ioctl() */
656
657 static int
658 vcons_putwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc)
659 {
660 long attr;
661 struct rasops_info *ri;
662
663 KASSERT(scr != NULL && wsc != NULL);
664
665 ri = &scr->scr_ri;
666
667 ri->ri_ops.allocattr(ri, wsc->foreground, wsc->background,
668 wsc->flags, &attr);
669 vcons_putchar(ri, wsc->row, wsc->col, wsc->letter, attr);
670 return 0;
671 }
672
673 static int
674 vcons_getwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc)
675 {
676 int offset;
677 long attr;
678 struct rasops_info *ri;
679
680 KASSERT(scr != NULL && wsc != NULL);
681
682 ri = &scr->scr_ri;
683 offset = ri->ri_cols * wsc->row + wsc->col;
684 wsc->letter = scr->scr_chars[offset];
685 attr = scr->scr_attrs[offset];
686
687 /*
688 * this is ugly. We need to break up an attribute into colours and
689 * flags but there's no rasops method to do that so we must rely on
690 * the 'canonical' encoding.
691 */
692 wsc->foreground = (attr & 0xff000000) >> 24;
693 wsc->background = (attr & 0x00ff0000) >> 16;
694 wsc->flags = (attr & 0x0000ff00) >> 8;
695 return 0;
696 }
697