1 /* $OpenBSD: wsdisplay.c,v 1.154 2024/11/05 16:53:30 miod Exp $ */ 2 /* $NetBSD: wsdisplay.c,v 1.82 2005/02/27 00:27:52 perry Exp $ */ 3 4 /* 5 * Copyright (c) 1996, 1997 Christopher G. Demetriou. 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 Christopher G. Demetriou 18 * for the NetBSD Project. 19 * 4. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include <sys/param.h> 35 #include <sys/conf.h> 36 #include <sys/device.h> 37 #include <sys/ioctl.h> 38 #include <sys/kernel.h> 39 #include <sys/malloc.h> 40 #include <sys/syslog.h> 41 #include <sys/systm.h> 42 #include <sys/task.h> 43 #include <sys/tty.h> 44 #include <sys/signalvar.h> 45 #include <sys/errno.h> 46 #include <sys/fcntl.h> 47 #include <sys/vnode.h> 48 #include <sys/timeout.h> 49 50 #include <dev/wscons/wscons_features.h> 51 #include <dev/wscons/wsconsio.h> 52 #include <dev/wscons/wsdisplayvar.h> 53 #include <dev/wscons/wsksymvar.h> 54 #include <dev/wscons/wsksymdef.h> 55 #include <dev/wscons/wsemulvar.h> 56 #include <dev/wscons/wscons_callbacks.h> 57 #include <dev/cons.h> 58 59 #include "wsdisplay.h" 60 #include "wskbd.h" 61 #include "wsmux.h" 62 63 #if NWSKBD > 0 64 #include <dev/wscons/wseventvar.h> 65 #include <dev/wscons/wsmuxvar.h> 66 #endif 67 68 #ifdef DDB 69 #include <ddb/db_output.h> 70 #endif 71 72 #include "wsmoused.h" 73 74 struct wsscreen_internal { 75 const struct wsdisplay_emulops *emulops; 76 void *emulcookie; 77 78 const struct wsscreen_descr *scrdata; 79 80 const struct wsemul_ops *wsemul; 81 void *wsemulcookie; 82 }; 83 84 struct wsscreen { 85 struct wsscreen_internal *scr_dconf; 86 87 struct task scr_emulbell_task; 88 89 struct tty *scr_tty; 90 int scr_hold_screen; /* hold tty output */ 91 92 int scr_flags; 93 #define SCR_OPEN 1 /* is it open? */ 94 #define SCR_WAITACTIVE 2 /* someone waiting on activation */ 95 #define SCR_GRAPHICS 4 /* graphics mode, no text (emulation) output */ 96 #define SCR_DUMBFB 8 /* in use as dumb fb (iff SCR_GRAPHICS) */ 97 98 #ifdef WSDISPLAY_COMPAT_USL 99 const struct wscons_syncops *scr_syncops; 100 void *scr_synccookie; 101 #endif 102 103 #ifdef WSDISPLAY_COMPAT_RAWKBD 104 int scr_rawkbd; 105 #endif 106 107 struct wsdisplay_softc *sc; 108 109 #ifdef HAVE_WSMOUSED_SUPPORT 110 /* mouse console support via wsmoused(8) */ 111 u_int mouse; /* mouse cursor position */ 112 u_int cursor; /* selection cursor position (if 113 different from mouse cursor pos) */ 114 u_int cpy_start; /* position of the copy start mark*/ 115 u_int cpy_end; /* position of the copy end mark */ 116 u_int orig_start; /* position of the original sel. start*/ 117 u_int orig_end; /* position of the original sel. end */ 118 119 u_int mouse_flags; /* flags, status of the mouse */ 120 #define MOUSE_VISIBLE 0x01 /* flag, the mouse cursor is visible */ 121 #define SEL_EXISTS 0x02 /* flag, a selection exists */ 122 #define SEL_IN_PROGRESS 0x04 /* flag, a selection is in progress */ 123 #define SEL_EXT_AFTER 0x08 /* flag, selection is extended after */ 124 #define BLANK_TO_EOL 0x10 /* flag, there are only blanks 125 characters to eol */ 126 #define SEL_BY_CHAR 0x20 /* flag, select character by character*/ 127 #define SEL_BY_WORD 0x40 /* flag, select word by word */ 128 #define SEL_BY_LINE 0x80 /* flag, select line by line */ 129 130 #define IS_MOUSE_VISIBLE(scr) ((scr)->mouse_flags & MOUSE_VISIBLE) 131 #define IS_SEL_EXISTS(scr) ((scr)->mouse_flags & SEL_EXISTS) 132 #define IS_SEL_IN_PROGRESS(scr) ((scr)->mouse_flags & SEL_IN_PROGRESS) 133 #define IS_SEL_EXT_AFTER(scr) ((scr)->mouse_flags & SEL_EXT_AFTER) 134 #define IS_BLANK_TO_EOL(scr) ((scr)->mouse_flags & BLANK_TO_EOL) 135 #define IS_SEL_BY_CHAR(scr) ((scr)->mouse_flags & SEL_BY_CHAR) 136 #define IS_SEL_BY_WORD(scr) ((scr)->mouse_flags & SEL_BY_WORD) 137 #define IS_SEL_BY_LINE(scr) ((scr)->mouse_flags & SEL_BY_LINE) 138 #endif /* HAVE_WSMOUSED_SUPPORT */ 139 }; 140 141 struct wsscreen *wsscreen_attach(struct wsdisplay_softc *, int, const char *, 142 const struct wsscreen_descr *, void *, int, int, uint32_t); 143 void wsscreen_detach(struct wsscreen *); 144 int wsdisplay_addscreen(struct wsdisplay_softc *, int, const char *, 145 const char *); 146 int wsdisplay_getscreen(struct wsdisplay_softc *, 147 struct wsdisplay_addscreendata *); 148 void wsdisplay_resume_device(struct device *); 149 void wsdisplay_suspend_device(struct device *); 150 void wsdisplay_addscreen_print(struct wsdisplay_softc *, int, int); 151 void wsdisplay_closescreen(struct wsdisplay_softc *, struct wsscreen *); 152 int wsdisplay_delscreen(struct wsdisplay_softc *, int, int); 153 int wsdisplay_driver_ioctl(struct wsdisplay_softc *, u_long, caddr_t, 154 int, struct proc *); 155 156 void wsdisplay_burner_setup(struct wsdisplay_softc *, struct wsscreen *); 157 void wsdisplay_burner(void *v); 158 159 struct wsdisplay_softc { 160 struct device sc_dv; 161 162 const struct wsdisplay_accessops *sc_accessops; 163 void *sc_accesscookie; 164 165 const struct wsscreen_list *sc_scrdata; 166 167 struct wsscreen *sc_scr[WSDISPLAY_MAXSCREEN]; 168 int sc_focusidx; /* available only if sc_focus isn't null */ 169 struct wsscreen *sc_focus; 170 171 struct taskq *sc_taskq; 172 173 #ifdef HAVE_BURNER_SUPPORT 174 struct timeout sc_burner; 175 int sc_burnoutintvl; /* delay before blanking (ms) */ 176 int sc_burninintvl; /* delay before unblanking (ms) */ 177 int sc_burnout; /* current sc_burner delay (ms) */ 178 int sc_burnman; /* nonzero if screen blanked */ 179 int sc_burnflags; 180 #endif 181 182 int sc_isconsole; 183 184 int sc_flags; 185 #define SC_SWITCHPENDING 0x01 186 #define SC_PASTE_AVAIL 0x02 187 int sc_screenwanted, sc_oldscreen; /* valid with SC_SWITCHPENDING */ 188 int sc_resumescreen; /* if set, can't switch until resume. */ 189 190 #if NWSKBD > 0 191 struct wsevsrc *sc_input; 192 #ifdef WSDISPLAY_COMPAT_RAWKBD 193 int sc_rawkbd; 194 #endif 195 #endif /* NWSKBD > 0 */ 196 197 #ifdef HAVE_WSMOUSED_SUPPORT 198 char *sc_copybuffer; 199 u_int sc_copybuffer_size; 200 #endif 201 }; 202 203 extern struct cfdriver wsdisplay_cd; 204 205 /* Autoconfiguration definitions. */ 206 int wsdisplay_match(struct device *, void *, void *); 207 void wsdisplay_attach(struct device *, struct device *, void *); 208 int wsdisplay_detach(struct device *, int); 209 210 int wsdisplay_activate(struct device *, int); 211 212 void wsdisplay_emulbell_task(void *); 213 214 struct cfdriver wsdisplay_cd = { 215 NULL, "wsdisplay", DV_TTY 216 }; 217 218 const struct cfattach wsdisplay_ca = { 219 sizeof(struct wsdisplay_softc), wsdisplay_match, 220 wsdisplay_attach, wsdisplay_detach, wsdisplay_activate 221 }; 222 223 void wsdisplaystart(struct tty *); 224 int wsdisplayparam(struct tty *, struct termios *); 225 226 /* Internal macros, functions, and variables. */ 227 #define WSDISPLAYUNIT(dev) (minor(dev) >> 8) 228 #define WSDISPLAYSCREEN(dev) (minor(dev) & 0xff) 229 #define ISWSDISPLAYCTL(dev) (WSDISPLAYSCREEN(dev) == 255) 230 #define WSDISPLAYMINOR(unit, screen) (((unit) << 8) | (screen)) 231 232 #define WSSCREEN_HAS_TTY(scr) ((scr)->scr_tty != NULL) 233 234 void wsdisplay_kbdholdscr(struct wsscreen *, int); 235 236 #ifdef WSDISPLAY_COMPAT_RAWKBD 237 int wsdisplay_update_rawkbd(struct wsdisplay_softc *, struct wsscreen *); 238 #endif 239 240 int wsdisplay_console_initted; 241 struct wsdisplay_softc *wsdisplay_console_device; 242 struct wsscreen_internal wsdisplay_console_conf; 243 244 int wsdisplay_getc_dummy(dev_t); 245 void wsdisplay_pollc(dev_t, int); 246 247 int wsdisplay_cons_pollmode; 248 void (*wsdisplay_cons_kbd_pollc)(dev_t, int); 249 250 struct consdev wsdisplay_cons = { 251 NULL, NULL, wsdisplay_getc_dummy, wsdisplay_cnputc, 252 wsdisplay_pollc, NULL, NODEV, CN_LOWPRI 253 }; 254 255 /* 256 * Function pointers for wsconsctl parameter handling. 257 * These are used for firmware-provided display brightness control. 258 */ 259 int (*ws_get_param)(struct wsdisplay_param *); 260 int (*ws_set_param)(struct wsdisplay_param *); 261 262 263 #ifndef WSDISPLAY_DEFAULTSCREENS 264 #define WSDISPLAY_DEFAULTSCREENS 1 265 #endif 266 int wsdisplay_defaultscreens = WSDISPLAY_DEFAULTSCREENS; 267 268 int wsdisplay_switch1(void *, int, int); 269 int wsdisplay_switch2(void *, int, int); 270 int wsdisplay_switch3(void *, int, int); 271 272 int wsdisplay_clearonclose; 273 274 struct wsscreen * 275 wsscreen_attach(struct wsdisplay_softc *sc, int console, const char *emul, 276 const struct wsscreen_descr *type, void *cookie, int ccol, int crow, 277 uint32_t defattr) 278 { 279 struct wsscreen_internal *dconf; 280 struct wsscreen *scr; 281 282 scr = malloc(sizeof(*scr), M_DEVBUF, M_ZERO | M_NOWAIT); 283 if (!scr) 284 return (NULL); 285 286 if (console) { 287 dconf = &wsdisplay_console_conf; 288 /* 289 * Tell the emulation about the callback argument. 290 * The other stuff is already there. 291 */ 292 (void)(*dconf->wsemul->attach)(1, 0, 0, 0, 0, scr, 0); 293 } else { /* not console */ 294 dconf = malloc(sizeof(*dconf), M_DEVBUF, M_NOWAIT); 295 if (dconf == NULL) 296 goto fail; 297 dconf->emulops = type->textops; 298 dconf->emulcookie = cookie; 299 if (dconf->emulops == NULL || 300 (dconf->wsemul = wsemul_pick(emul)) == NULL) 301 goto fail; 302 dconf->wsemulcookie = (*dconf->wsemul->attach)(0, type, cookie, 303 ccol, crow, scr, defattr); 304 if (dconf->wsemulcookie == NULL) 305 goto fail; 306 dconf->scrdata = type; 307 } 308 309 task_set(&scr->scr_emulbell_task, wsdisplay_emulbell_task, scr); 310 scr->scr_dconf = dconf; 311 scr->scr_tty = ttymalloc(0); 312 scr->sc = sc; 313 return (scr); 314 315 fail: 316 if (dconf != NULL) 317 free(dconf, M_DEVBUF, sizeof(*dconf)); 318 free(scr, M_DEVBUF, sizeof(*scr)); 319 return (NULL); 320 } 321 322 void 323 wsscreen_detach(struct wsscreen *scr) 324 { 325 int ccol, crow; /* XXX */ 326 327 if (WSSCREEN_HAS_TTY(scr)) { 328 timeout_del(&scr->scr_tty->t_rstrt_to); 329 ttyfree(scr->scr_tty); 330 } 331 (*scr->scr_dconf->wsemul->detach)(scr->scr_dconf->wsemulcookie, 332 &ccol, &crow); 333 taskq_del_barrier(scr->sc->sc_taskq, &scr->scr_emulbell_task); 334 free(scr->scr_dconf, M_DEVBUF, sizeof(*scr->scr_dconf)); 335 free(scr, M_DEVBUF, sizeof(*scr)); 336 } 337 338 const struct wsscreen_descr * 339 wsdisplay_screentype_pick(const struct wsscreen_list *scrdata, const char *name) 340 { 341 int i; 342 const struct wsscreen_descr *scr; 343 344 KASSERT(scrdata->nscreens > 0); 345 346 if (name == NULL || *name == '\0') 347 return (scrdata->screens[0]); 348 349 for (i = 0; i < scrdata->nscreens; i++) { 350 scr = scrdata->screens[i]; 351 if (!strncmp(name, scr->name, WSSCREEN_NAME_SIZE)) 352 return (scr); 353 } 354 355 return (0); 356 } 357 358 /* 359 * print info about attached screen 360 */ 361 void 362 wsdisplay_addscreen_print(struct wsdisplay_softc *sc, int idx, int count) 363 { 364 printf("%s: screen %d", sc->sc_dv.dv_xname, idx); 365 if (count > 1) 366 printf("-%d", idx + (count-1)); 367 printf(" added (%s, %s emulation)\n", 368 sc->sc_scr[idx]->scr_dconf->scrdata->name, 369 sc->sc_scr[idx]->scr_dconf->wsemul->name); 370 } 371 372 int 373 wsdisplay_addscreen(struct wsdisplay_softc *sc, int idx, 374 const char *screentype, const char *emul) 375 { 376 const struct wsscreen_descr *scrdesc; 377 int error; 378 void *cookie; 379 int ccol, crow; 380 uint32_t defattr; 381 struct wsscreen *scr; 382 int s; 383 384 if (idx < 0 || idx >= WSDISPLAY_MAXSCREEN) 385 return (EINVAL); 386 if (sc->sc_scr[idx] != NULL) 387 return (EBUSY); 388 389 scrdesc = wsdisplay_screentype_pick(sc->sc_scrdata, screentype); 390 if (!scrdesc) 391 return (ENXIO); 392 error = (*sc->sc_accessops->alloc_screen)(sc->sc_accesscookie, 393 scrdesc, &cookie, &ccol, &crow, &defattr); 394 if (error) 395 return (error); 396 397 scr = wsscreen_attach(sc, 0, emul, scrdesc, 398 cookie, ccol, crow, defattr); 399 if (scr == NULL) { 400 (*sc->sc_accessops->free_screen)(sc->sc_accesscookie, cookie); 401 return (ENXIO); 402 } 403 404 sc->sc_scr[idx] = scr; 405 406 /* if no screen has focus yet, activate the first we get */ 407 s = spltty(); 408 if (!sc->sc_focus) { 409 (*sc->sc_accessops->show_screen)(sc->sc_accesscookie, 410 scr->scr_dconf->emulcookie, 0, 0, 0); 411 sc->sc_focusidx = idx; 412 sc->sc_focus = scr; 413 } 414 splx(s); 415 416 #ifdef HAVE_WSMOUSED_SUPPORT 417 allocate_copybuffer(sc); /* enlarge the copy buffer if necessary */ 418 #endif 419 return (0); 420 } 421 422 int 423 wsdisplay_getscreen(struct wsdisplay_softc *sc, 424 struct wsdisplay_addscreendata *sd) 425 { 426 struct wsscreen *scr; 427 428 if (sd->idx < 0 && sc->sc_focus) 429 sd->idx = sc->sc_focusidx; 430 431 if (sd->idx < 0 || sd->idx >= WSDISPLAY_MAXSCREEN) 432 return (EINVAL); 433 434 scr = sc->sc_scr[sd->idx]; 435 if (scr == NULL) 436 return (ENXIO); 437 438 strlcpy(sd->screentype, scr->scr_dconf->scrdata->name, 439 WSSCREEN_NAME_SIZE); 440 strlcpy(sd->emul, scr->scr_dconf->wsemul->name, WSEMUL_NAME_SIZE); 441 442 return (0); 443 } 444 445 void 446 wsdisplay_closescreen(struct wsdisplay_softc *sc, struct wsscreen *scr) 447 { 448 int maj, mn, idx; 449 450 /* hangup */ 451 if (WSSCREEN_HAS_TTY(scr)) { 452 struct tty *tp = scr->scr_tty; 453 (*linesw[tp->t_line].l_modem)(tp, 0); 454 } 455 456 /* locate the major number */ 457 for (maj = 0; maj < nchrdev; maj++) 458 if (cdevsw[maj].d_open == wsdisplayopen) 459 break; 460 /* locate the screen index */ 461 for (idx = 0; idx < WSDISPLAY_MAXSCREEN; idx++) 462 if (scr == sc->sc_scr[idx]) 463 break; 464 #ifdef DIAGNOSTIC 465 if (idx == WSDISPLAY_MAXSCREEN) 466 panic("wsdisplay_forceclose: bad screen"); 467 #endif 468 469 /* nuke the vnodes */ 470 mn = WSDISPLAYMINOR(sc->sc_dv.dv_unit, idx); 471 vdevgone(maj, mn, mn, VCHR); 472 } 473 474 int 475 wsdisplay_delscreen(struct wsdisplay_softc *sc, int idx, int flags) 476 { 477 struct wsscreen *scr; 478 int s; 479 void *cookie; 480 481 if (idx < 0 || idx >= WSDISPLAY_MAXSCREEN) 482 return (EINVAL); 483 if ((scr = sc->sc_scr[idx]) == NULL) 484 return (ENXIO); 485 486 if (scr->scr_dconf == &wsdisplay_console_conf || 487 #ifdef WSDISPLAY_COMPAT_USL 488 scr->scr_syncops || 489 #endif 490 ((scr->scr_flags & SCR_OPEN) && !(flags & WSDISPLAY_DELSCR_FORCE))) 491 return (EBUSY); 492 493 wsdisplay_closescreen(sc, scr); 494 495 /* 496 * delete pointers, so neither device entries 497 * nor keyboard input can reference it anymore 498 */ 499 s = spltty(); 500 if (sc->sc_focus == scr) { 501 sc->sc_focus = NULL; 502 #ifdef WSDISPLAY_COMPAT_RAWKBD 503 wsdisplay_update_rawkbd(sc, 0); 504 #endif 505 } 506 sc->sc_scr[idx] = NULL; 507 splx(s); 508 509 /* 510 * Wake up processes waiting for the screen to 511 * be activated. Sleepers must check whether 512 * the screen still exists. 513 */ 514 if (scr->scr_flags & SCR_WAITACTIVE) 515 wakeup(scr); 516 517 /* save a reference to the graphics screen */ 518 cookie = scr->scr_dconf->emulcookie; 519 520 wsscreen_detach(scr); 521 522 (*sc->sc_accessops->free_screen)(sc->sc_accesscookie, cookie); 523 524 if ((flags & WSDISPLAY_DELSCR_QUIET) == 0) 525 printf("%s: screen %d deleted\n", sc->sc_dv.dv_xname, idx); 526 return (0); 527 } 528 529 /* 530 * Autoconfiguration functions. 531 */ 532 int 533 wsdisplay_match(struct device *parent, void *match, void *aux) 534 { 535 struct cfdata *cf = match; 536 struct wsemuldisplaydev_attach_args *ap = aux; 537 538 if (cf->wsemuldisplaydevcf_console != WSEMULDISPLAYDEVCF_CONSOLE_UNK) { 539 /* 540 * If console-ness of device specified, either match 541 * exactly (at high priority), or fail. 542 */ 543 if (cf->wsemuldisplaydevcf_console != 0 && ap->console != 0) 544 return (10); 545 else 546 return (0); 547 } 548 549 if (cf->wsemuldisplaydevcf_primary != WSEMULDISPLAYDEVCF_PRIMARY_UNK) { 550 /* 551 * If primary-ness of device specified, either match 552 * exactly (at high priority), or fail. 553 */ 554 if (cf->wsemuldisplaydevcf_primary != 0 && ap->primary != 0) 555 return (10); 556 else 557 return (0); 558 } 559 560 /* If console-ness and primary-ness unspecified, it wins. */ 561 return (1); 562 } 563 564 int 565 wsdisplay_activate(struct device *self, int act) 566 { 567 int ret = 0; 568 569 switch (act) { 570 case DVACT_POWERDOWN: 571 wsdisplay_switchtoconsole(); 572 break; 573 } 574 575 return (ret); 576 } 577 578 /* 579 * Detach a display. 580 */ 581 int 582 wsdisplay_detach(struct device *self, int flags) 583 { 584 struct wsdisplay_softc *sc = (struct wsdisplay_softc *)self; 585 int i; 586 int rc; 587 588 /* We don't support detaching the console display yet. */ 589 if (sc->sc_isconsole) 590 return (EBUSY); 591 592 /* Delete all screens managed by this display */ 593 for (i = 0; i < WSDISPLAY_MAXSCREEN; i++) 594 if (sc->sc_scr[i] != NULL) { 595 if ((rc = wsdisplay_delscreen(sc, i, 596 WSDISPLAY_DELSCR_QUIET | (flags & DETACH_FORCE ? 597 WSDISPLAY_DELSCR_FORCE : 0))) != 0) 598 return (rc); 599 } 600 601 #ifdef HAVE_BURNER_SUPPORT 602 timeout_del(&sc->sc_burner); 603 #endif 604 605 #if NWSKBD > 0 606 if (sc->sc_input != NULL) { 607 #if NWSMUX > 0 608 /* 609 * If we are the display of the mux we are attached to, 610 * disconnect all input devices from us. 611 */ 612 if (sc->sc_input->me_dispdv == &sc->sc_dv) { 613 if ((rc = wsmux_set_display((struct wsmux_softc *) 614 sc->sc_input, NULL)) != 0) 615 return (rc); 616 } 617 618 /* 619 * XXX 620 * If we created a standalone mux (dmux), we should destroy it 621 * there, but there is currently no support for this in wsmux. 622 */ 623 #else 624 if ((rc = wskbd_set_display((struct device *)sc->sc_input, 625 NULL)) != 0) 626 return (rc); 627 #endif 628 } 629 #endif 630 631 taskq_destroy(sc->sc_taskq); 632 633 return (0); 634 } 635 636 /* Print function (for parent devices). */ 637 int 638 wsemuldisplaydevprint(void *aux, const char *pnp) 639 { 640 #if 0 /* -Wunused */ 641 struct wsemuldisplaydev_attach_args *ap = aux; 642 #endif 643 644 if (pnp) 645 printf("wsdisplay at %s", pnp); 646 #if 0 /* don't bother; it's ugly */ 647 printf(" console %d", ap->console); 648 #endif 649 650 return (UNCONF); 651 } 652 653 /* Submatch function (for parent devices). */ 654 int 655 wsemuldisplaydevsubmatch(struct device *parent, void *match, void *aux) 656 { 657 extern struct cfdriver wsdisplay_cd; 658 struct cfdata *cf = match; 659 660 /* only allow wsdisplay to attach */ 661 if (cf->cf_driver == &wsdisplay_cd) 662 return ((*cf->cf_attach->ca_match)(parent, match, aux)); 663 664 return (0); 665 } 666 667 void 668 wsdisplay_attach(struct device *parent, struct device *self, void *aux) 669 { 670 struct wsdisplay_softc *sc = (struct wsdisplay_softc *)self; 671 struct wsemuldisplaydev_attach_args *ap = aux; 672 u_int defaultscreens = ap->defaultscreens; 673 int i, start = 0; 674 #if NWSKBD > 0 675 struct wsevsrc *kme; 676 #if NWSMUX > 0 677 int kbdmux = sc->sc_dv.dv_cfdata->wsemuldisplaydevcf_mux; 678 struct wsmux_softc *mux; 679 680 if (kbdmux >= 0) 681 mux = wsmux_getmux(kbdmux); 682 else 683 mux = wsmux_create("dmux", sc->sc_dv.dv_unit); 684 /* XXX panic()ing isn't nice, but attach cannot fail */ 685 if (mux == NULL) 686 panic("wsdisplay_common_attach: no memory"); 687 sc->sc_input = &mux->sc_base; 688 689 if (kbdmux >= 0) 690 printf(" mux %d", kbdmux); 691 #else 692 #if 0 /* not worth keeping, especially since the default value is not -1... */ 693 if (kbdmux >= 0) 694 printf(" (mux ignored)"); 695 #endif 696 #endif /* NWSMUX > 0 */ 697 #endif /* NWSKBD > 0 */ 698 699 sc->sc_isconsole = ap->console; 700 sc->sc_resumescreen = WSDISPLAY_NULLSCREEN; 701 702 sc->sc_taskq = taskq_create(sc->sc_dv.dv_xname, 1, IPL_TTY, 0); 703 704 if (ap->console) { 705 KASSERT(wsdisplay_console_initted); 706 KASSERT(wsdisplay_console_device == NULL); 707 708 sc->sc_scr[0] = wsscreen_attach(sc, 1, 0, 0, 0, 0, 0, 0); 709 if (sc->sc_scr[0] == NULL) 710 return; 711 wsdisplay_console_device = sc; 712 713 printf(": console (%s, %s emulation)", 714 wsdisplay_console_conf.scrdata->name, 715 wsdisplay_console_conf.wsemul->name); 716 717 #if NWSKBD > 0 718 kme = wskbd_set_console_display(&sc->sc_dv, sc->sc_input); 719 if (kme != NULL) 720 printf(", using %s", kme->me_dv.dv_xname); 721 #if NWSMUX == 0 722 sc->sc_input = kme; 723 #endif 724 #endif 725 726 sc->sc_focusidx = 0; 727 sc->sc_focus = sc->sc_scr[0]; 728 start = 1; 729 } 730 printf("\n"); 731 732 #if NWSKBD > 0 && NWSMUX > 0 733 /* 734 * If this mux did not have a display device yet, volunteer for 735 * the job. 736 */ 737 if (mux->sc_displaydv == NULL) 738 wsmux_set_display(mux, &sc->sc_dv); 739 #endif 740 741 sc->sc_accessops = ap->accessops; 742 sc->sc_accesscookie = ap->accesscookie; 743 sc->sc_scrdata = ap->scrdata; 744 745 /* 746 * Set up a number of virtual screens if wanted. The 747 * WSDISPLAYIO_ADDSCREEN ioctl is more flexible, so this code 748 * is for special cases like installation kernels, as well as 749 * sane multihead defaults. 750 */ 751 if (defaultscreens == 0) 752 defaultscreens = wsdisplay_defaultscreens; 753 for (i = start; i < defaultscreens; i++) { 754 if (wsdisplay_addscreen(sc, i, 0, 0)) 755 break; 756 } 757 758 if (i > start) 759 wsdisplay_addscreen_print(sc, start, i-start); 760 761 #ifdef HAVE_BURNER_SUPPORT 762 sc->sc_burnoutintvl = WSDISPLAY_DEFBURNOUT_MSEC; 763 sc->sc_burninintvl = WSDISPLAY_DEFBURNIN_MSEC; 764 sc->sc_burnflags = WSDISPLAY_BURN_OUTPUT | WSDISPLAY_BURN_KBD | 765 WSDISPLAY_BURN_MOUSE; 766 timeout_set(&sc->sc_burner, wsdisplay_burner, sc); 767 sc->sc_burnout = sc->sc_burnoutintvl; 768 wsdisplay_burn(sc, sc->sc_burnflags); 769 #endif 770 771 #if NWSKBD > 0 && NWSMUX == 0 772 if (ap->console == 0) { 773 /* 774 * In the non-wsmux world, always connect wskbd0 and wsdisplay0 775 * together. 776 */ 777 extern struct cfdriver wskbd_cd; 778 779 if (wskbd_cd.cd_ndevs != 0 && sc->sc_dv.dv_unit == 0) { 780 if (wsdisplay_set_kbd(&sc->sc_dv, 781 (struct wsevsrc *)wskbd_cd.cd_devs[0]) == 0) 782 wskbd_set_display(wskbd_cd.cd_devs[0], 783 &sc->sc_dv); 784 } 785 } 786 #endif 787 788 if (ap->console && cn_tab == &wsdisplay_cons) { 789 int maj; 790 791 /* locate the major number */ 792 for (maj = 0; maj < nchrdev; maj++) 793 if (cdevsw[maj].d_open == wsdisplayopen) 794 break; 795 796 cn_tab->cn_dev = makedev(maj, WSDISPLAYMINOR(self->dv_unit, 0)); 797 } 798 } 799 800 void 801 wsdisplay_cnattach(const struct wsscreen_descr *type, void *cookie, int ccol, 802 int crow, uint32_t defattr) 803 { 804 const struct wsemul_ops *wsemul; 805 const struct wsdisplay_emulops *emulops; 806 807 KASSERT(type->nrows > 0); 808 KASSERT(type->ncols > 0); 809 KASSERT(crow < type->nrows); 810 KASSERT(ccol < type->ncols); 811 812 wsdisplay_console_conf.emulops = emulops = type->textops; 813 wsdisplay_console_conf.emulcookie = cookie; 814 wsdisplay_console_conf.scrdata = type; 815 816 #ifdef WSEMUL_DUMB 817 /* 818 * If the emulops structure is crippled, force a dumb emulation. 819 */ 820 if (emulops->cursor == NULL || 821 emulops->copycols == NULL || emulops->copyrows == NULL || 822 emulops->erasecols == NULL || emulops->eraserows == NULL) 823 wsemul = wsemul_pick("dumb"); 824 else 825 #endif 826 wsemul = wsemul_pick(""); 827 wsdisplay_console_conf.wsemul = wsemul; 828 wsdisplay_console_conf.wsemulcookie = 829 (*wsemul->cnattach)(type, cookie, ccol, crow, defattr); 830 831 if (!wsdisplay_console_initted) 832 cn_tab = &wsdisplay_cons; 833 834 wsdisplay_console_initted = 1; 835 836 #ifdef DDB 837 db_resize(type->ncols, type->nrows); 838 #endif 839 } 840 841 /* 842 * Tty and cdevsw functions. 843 */ 844 int 845 wsdisplayopen(dev_t dev, int flag, int mode, struct proc *p) 846 { 847 struct wsdisplay_softc *sc; 848 struct tty *tp; 849 int unit, newopen, error; 850 struct wsscreen *scr; 851 852 unit = WSDISPLAYUNIT(dev); 853 if (unit >= wsdisplay_cd.cd_ndevs || /* make sure it was attached */ 854 (sc = wsdisplay_cd.cd_devs[unit]) == NULL) 855 return (ENXIO); 856 857 if (ISWSDISPLAYCTL(dev)) 858 return (0); 859 860 if (WSDISPLAYSCREEN(dev) >= WSDISPLAY_MAXSCREEN) 861 return (ENXIO); 862 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 863 return (ENXIO); 864 865 if (WSSCREEN_HAS_TTY(scr)) { 866 tp = scr->scr_tty; 867 tp->t_oproc = wsdisplaystart; 868 tp->t_param = wsdisplayparam; 869 tp->t_dev = dev; 870 newopen = (tp->t_state & TS_ISOPEN) == 0; 871 if (newopen) { 872 ttychars(tp); 873 tp->t_iflag = TTYDEF_IFLAG; 874 tp->t_oflag = TTYDEF_OFLAG; 875 tp->t_cflag = TTYDEF_CFLAG; 876 tp->t_lflag = TTYDEF_LFLAG; 877 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 878 wsdisplayparam(tp, &tp->t_termios); 879 ttsetwater(tp); 880 } else if ((tp->t_state & TS_XCLUDE) != 0 && 881 suser(p) != 0) 882 return (EBUSY); 883 tp->t_state |= TS_CARR_ON; 884 885 error = ((*linesw[tp->t_line].l_open)(dev, tp, p)); 886 if (error) 887 return (error); 888 889 if (newopen) { 890 /* set window sizes as appropriate, and reset 891 the emulation */ 892 tp->t_winsize.ws_row = scr->scr_dconf->scrdata->nrows; 893 tp->t_winsize.ws_col = scr->scr_dconf->scrdata->ncols; 894 } 895 } 896 897 scr->scr_flags |= SCR_OPEN; 898 return (0); 899 } 900 901 int 902 wsdisplayclose(dev_t dev, int flag, int mode, struct proc *p) 903 { 904 struct wsdisplay_softc *sc; 905 struct tty *tp; 906 int unit; 907 struct wsscreen *scr; 908 909 unit = WSDISPLAYUNIT(dev); 910 sc = wsdisplay_cd.cd_devs[unit]; 911 912 if (ISWSDISPLAYCTL(dev)) 913 return (0); 914 915 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 916 return (ENXIO); 917 918 if (WSSCREEN_HAS_TTY(scr)) { 919 if (scr->scr_hold_screen) { 920 int s; 921 922 /* XXX RESET KEYBOARD LEDS, etc. */ 923 s = spltty(); /* avoid conflict with keyboard */ 924 wsdisplay_kbdholdscr(scr, 0); 925 splx(s); 926 } 927 tp = scr->scr_tty; 928 (*linesw[tp->t_line].l_close)(tp, flag, p); 929 ttyclose(tp); 930 } 931 932 #ifdef WSDISPLAY_COMPAT_USL 933 if (scr->scr_syncops) 934 (*scr->scr_syncops->destroy)(scr->scr_synccookie); 935 #endif 936 937 scr->scr_flags &= ~SCR_GRAPHICS; 938 (*scr->scr_dconf->wsemul->reset)(scr->scr_dconf->wsemulcookie, 939 WSEMUL_RESET); 940 if (wsdisplay_clearonclose) 941 (*scr->scr_dconf->wsemul->reset) 942 (scr->scr_dconf->wsemulcookie, WSEMUL_CLEARSCREEN); 943 944 #ifdef WSDISPLAY_COMPAT_RAWKBD 945 if (scr->scr_rawkbd) { 946 int kbmode = WSKBD_TRANSLATED; 947 (void) wsdisplay_internal_ioctl(sc, scr, WSKBDIO_SETMODE, 948 (caddr_t)&kbmode, FWRITE, p); 949 } 950 #endif 951 952 scr->scr_flags &= ~SCR_OPEN; 953 954 #ifdef HAVE_WSMOUSED_SUPPORT 955 /* remove the selection at logout */ 956 if (sc->sc_copybuffer != NULL) 957 explicit_bzero(sc->sc_copybuffer, sc->sc_copybuffer_size); 958 CLR(sc->sc_flags, SC_PASTE_AVAIL); 959 #endif 960 961 return (0); 962 } 963 964 int 965 wsdisplayread(dev_t dev, struct uio *uio, int flag) 966 { 967 struct wsdisplay_softc *sc; 968 struct tty *tp; 969 int unit; 970 struct wsscreen *scr; 971 972 unit = WSDISPLAYUNIT(dev); 973 sc = wsdisplay_cd.cd_devs[unit]; 974 975 if (ISWSDISPLAYCTL(dev)) 976 return (0); 977 978 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 979 return (ENXIO); 980 981 if (!WSSCREEN_HAS_TTY(scr)) 982 return (ENODEV); 983 984 tp = scr->scr_tty; 985 return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); 986 } 987 988 int 989 wsdisplaywrite(dev_t dev, struct uio *uio, int flag) 990 { 991 struct wsdisplay_softc *sc; 992 struct tty *tp; 993 int unit; 994 struct wsscreen *scr; 995 996 unit = WSDISPLAYUNIT(dev); 997 sc = wsdisplay_cd.cd_devs[unit]; 998 999 if (ISWSDISPLAYCTL(dev)) 1000 return (0); 1001 1002 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 1003 return (ENXIO); 1004 1005 if (!WSSCREEN_HAS_TTY(scr)) 1006 return (ENODEV); 1007 1008 tp = scr->scr_tty; 1009 return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); 1010 } 1011 1012 struct tty * 1013 wsdisplaytty(dev_t dev) 1014 { 1015 struct wsdisplay_softc *sc; 1016 int unit; 1017 struct wsscreen *scr; 1018 1019 unit = WSDISPLAYUNIT(dev); 1020 sc = wsdisplay_cd.cd_devs[unit]; 1021 1022 if (ISWSDISPLAYCTL(dev)) 1023 panic("wsdisplaytty() on ctl device"); 1024 1025 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 1026 return (NULL); 1027 1028 return (scr->scr_tty); 1029 } 1030 1031 int 1032 wsdisplayioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 1033 { 1034 struct wsdisplay_softc *sc; 1035 struct tty *tp; 1036 int unit, error; 1037 struct wsscreen *scr; 1038 1039 unit = WSDISPLAYUNIT(dev); 1040 sc = wsdisplay_cd.cd_devs[unit]; 1041 1042 #ifdef WSDISPLAY_COMPAT_USL 1043 error = wsdisplay_usl_ioctl1(sc, cmd, data, flag, p); 1044 if (error >= 0) 1045 return (error); 1046 #endif 1047 1048 if (ISWSDISPLAYCTL(dev)) { 1049 switch (cmd) { 1050 case WSDISPLAYIO_GTYPE: 1051 case WSDISPLAYIO_GETSCREENTYPE: 1052 /* pass to the first screen */ 1053 dev = makedev(major(dev), WSDISPLAYMINOR(unit, 0)); 1054 break; 1055 default: 1056 return (wsdisplay_cfg_ioctl(sc, cmd, data, flag, p)); 1057 } 1058 } 1059 1060 if (WSDISPLAYSCREEN(dev) >= WSDISPLAY_MAXSCREEN) 1061 return (ENODEV); 1062 1063 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 1064 return (ENXIO); 1065 1066 if (WSSCREEN_HAS_TTY(scr)) { 1067 tp = scr->scr_tty; 1068 1069 /* printf("disc\n"); */ 1070 /* do the line discipline ioctls first */ 1071 error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); 1072 if (error >= 0) 1073 return (error); 1074 1075 /* printf("tty\n"); */ 1076 /* then the tty ioctls */ 1077 error = ttioctl(tp, cmd, data, flag, p); 1078 if (error >= 0) 1079 return (error); 1080 } 1081 1082 #ifdef WSDISPLAY_COMPAT_USL 1083 error = wsdisplay_usl_ioctl2(sc, scr, cmd, data, flag, p); 1084 if (error >= 0) 1085 return (error); 1086 #endif 1087 1088 error = wsdisplay_internal_ioctl(sc, scr, cmd, data, flag, p); 1089 return (error != -1 ? error : ENOTTY); 1090 } 1091 1092 int 1093 wsdisplay_param(struct device *dev, u_long cmd, struct wsdisplay_param *dp) 1094 { 1095 struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev; 1096 return wsdisplay_driver_ioctl(sc, cmd, (caddr_t)dp, 0, NULL); 1097 } 1098 1099 int 1100 wsdisplay_internal_ioctl(struct wsdisplay_softc *sc, struct wsscreen *scr, 1101 u_long cmd, caddr_t data, int flag, struct proc *p) 1102 { 1103 int error; 1104 1105 #if NWSKBD > 0 1106 struct wsevsrc *inp; 1107 1108 #ifdef WSDISPLAY_COMPAT_RAWKBD 1109 switch (cmd) { 1110 case WSKBDIO_SETMODE: 1111 if ((flag & FWRITE) == 0) 1112 return (EACCES); 1113 scr->scr_rawkbd = (*(int *)data == WSKBD_RAW); 1114 return (wsdisplay_update_rawkbd(sc, scr)); 1115 case WSKBDIO_GETMODE: 1116 *(int *)data = (scr->scr_rawkbd ? 1117 WSKBD_RAW : WSKBD_TRANSLATED); 1118 return (0); 1119 } 1120 #endif 1121 inp = sc->sc_input; 1122 if (inp != NULL) { 1123 error = wsevsrc_display_ioctl(inp, cmd, data, flag, p); 1124 if (error >= 0) 1125 return (error); 1126 } 1127 #endif /* NWSKBD > 0 */ 1128 1129 switch (cmd) { 1130 case WSDISPLAYIO_SMODE: 1131 case WSDISPLAYIO_USEFONT: 1132 #ifdef HAVE_BURNER_SUPPORT 1133 case WSDISPLAYIO_SVIDEO: 1134 case WSDISPLAYIO_SBURNER: 1135 #endif 1136 case WSDISPLAYIO_SETSCREEN: 1137 if ((flag & FWRITE) == 0) 1138 return (EACCES); 1139 } 1140 1141 switch (cmd) { 1142 case WSDISPLAYIO_GMODE: 1143 if (scr->scr_flags & SCR_GRAPHICS) { 1144 if (scr->scr_flags & SCR_DUMBFB) 1145 *(u_int *)data = WSDISPLAYIO_MODE_DUMBFB; 1146 else 1147 *(u_int *)data = WSDISPLAYIO_MODE_MAPPED; 1148 } else 1149 *(u_int *)data = WSDISPLAYIO_MODE_EMUL; 1150 return (0); 1151 1152 case WSDISPLAYIO_SMODE: 1153 #define d (*(int *)data) 1154 if (d != WSDISPLAYIO_MODE_EMUL && 1155 d != WSDISPLAYIO_MODE_MAPPED && 1156 d != WSDISPLAYIO_MODE_DUMBFB) 1157 return (EINVAL); 1158 1159 scr->scr_flags &= ~SCR_GRAPHICS; 1160 if (d == WSDISPLAYIO_MODE_MAPPED || 1161 d == WSDISPLAYIO_MODE_DUMBFB) { 1162 scr->scr_flags |= SCR_GRAPHICS | 1163 ((d == WSDISPLAYIO_MODE_DUMBFB) ? SCR_DUMBFB : 0); 1164 1165 /* clear cursor */ 1166 (*scr->scr_dconf->wsemul->reset) 1167 (scr->scr_dconf->wsemulcookie, WSEMUL_CLEARCURSOR); 1168 } 1169 1170 #ifdef HAVE_BURNER_SUPPORT 1171 wsdisplay_burner_setup(sc, scr); 1172 #endif 1173 1174 (void)(*sc->sc_accessops->ioctl)(sc->sc_accesscookie, cmd, data, 1175 flag, p); 1176 1177 return (0); 1178 #undef d 1179 1180 case WSDISPLAYIO_USEFONT: 1181 #define d ((struct wsdisplay_font *)data) 1182 if (!sc->sc_accessops->load_font) 1183 return (EINVAL); 1184 d->data = NULL; 1185 error = (*sc->sc_accessops->load_font)(sc->sc_accesscookie, 1186 scr->scr_dconf->emulcookie, d); 1187 if (!error) 1188 (*scr->scr_dconf->wsemul->reset) 1189 (scr->scr_dconf->wsemulcookie, WSEMUL_SYNCFONT); 1190 return (error); 1191 #undef d 1192 #ifdef HAVE_BURNER_SUPPORT 1193 case WSDISPLAYIO_GVIDEO: 1194 *(u_int *)data = !sc->sc_burnman; 1195 break; 1196 1197 case WSDISPLAYIO_SVIDEO: 1198 if (*(u_int *)data != WSDISPLAYIO_VIDEO_OFF && 1199 *(u_int *)data != WSDISPLAYIO_VIDEO_ON) 1200 return (EINVAL); 1201 if (sc->sc_accessops->burn_screen == NULL) 1202 return (EOPNOTSUPP); 1203 (*sc->sc_accessops->burn_screen)(sc->sc_accesscookie, 1204 *(u_int *)data, sc->sc_burnflags); 1205 sc->sc_burnman = *(u_int *)data == WSDISPLAYIO_VIDEO_OFF; 1206 break; 1207 1208 case WSDISPLAYIO_GBURNER: 1209 #define d ((struct wsdisplay_burner *)data) 1210 d->on = sc->sc_burninintvl; 1211 d->off = sc->sc_burnoutintvl; 1212 d->flags = sc->sc_burnflags; 1213 return (0); 1214 1215 case WSDISPLAYIO_SBURNER: 1216 { 1217 struct wsscreen *active; 1218 1219 if (d->flags & ~(WSDISPLAY_BURN_VBLANK | WSDISPLAY_BURN_KBD | 1220 WSDISPLAY_BURN_MOUSE | WSDISPLAY_BURN_OUTPUT)) 1221 return EINVAL; 1222 1223 error = 0; 1224 sc->sc_burnflags = d->flags; 1225 /* disable timeout if necessary */ 1226 if (d->off==0 || (sc->sc_burnflags & (WSDISPLAY_BURN_OUTPUT | 1227 WSDISPLAY_BURN_KBD | WSDISPLAY_BURN_MOUSE)) == 0) { 1228 if (sc->sc_burnout) 1229 timeout_del(&sc->sc_burner); 1230 } 1231 1232 active = sc->sc_focus; 1233 if (active == NULL) 1234 active = scr; 1235 1236 if (d->on) { 1237 sc->sc_burninintvl = d->on; 1238 if (sc->sc_burnman) { 1239 sc->sc_burnout = sc->sc_burninintvl; 1240 /* reinit timeout if changed */ 1241 if ((active->scr_flags & SCR_GRAPHICS) == 0) 1242 wsdisplay_burn(sc, sc->sc_burnflags); 1243 } 1244 } 1245 sc->sc_burnoutintvl = d->off; 1246 if (!sc->sc_burnman) { 1247 sc->sc_burnout = sc->sc_burnoutintvl; 1248 /* reinit timeout if changed */ 1249 if ((active->scr_flags & SCR_GRAPHICS) == 0) 1250 wsdisplay_burn(sc, sc->sc_burnflags); 1251 } 1252 return (error); 1253 } 1254 #undef d 1255 #endif /* HAVE_BURNER_SUPPORT */ 1256 case WSDISPLAYIO_GETSCREEN: 1257 return (wsdisplay_getscreen(sc, 1258 (struct wsdisplay_addscreendata *)data)); 1259 1260 case WSDISPLAYIO_SETSCREEN: 1261 return (wsdisplay_switch((void *)sc, *(int *)data, 1)); 1262 1263 case WSDISPLAYIO_GETSCREENTYPE: 1264 #define d ((struct wsdisplay_screentype *)data) 1265 if (d->idx < 0 || d->idx >= sc->sc_scrdata->nscreens) 1266 return(EINVAL); 1267 1268 d->nidx = sc->sc_scrdata->nscreens; 1269 strlcpy(d->name, sc->sc_scrdata->screens[d->idx]->name, 1270 WSSCREEN_NAME_SIZE); 1271 d->ncols = sc->sc_scrdata->screens[d->idx]->ncols; 1272 d->nrows = sc->sc_scrdata->screens[d->idx]->nrows; 1273 d->fontwidth = sc->sc_scrdata->screens[d->idx]->fontwidth; 1274 d->fontheight = sc->sc_scrdata->screens[d->idx]->fontheight; 1275 return (0); 1276 #undef d 1277 case WSDISPLAYIO_GETEMULTYPE: 1278 #define d ((struct wsdisplay_emultype *)data) 1279 if (wsemul_getname(d->idx) == NULL) 1280 return(EINVAL); 1281 strlcpy(d->name, wsemul_getname(d->idx), WSEMUL_NAME_SIZE); 1282 return (0); 1283 #undef d 1284 } 1285 1286 /* check ioctls for display */ 1287 return wsdisplay_driver_ioctl(sc, cmd, data, flag, p); 1288 } 1289 1290 int 1291 wsdisplay_driver_ioctl(struct wsdisplay_softc *sc, u_long cmd, caddr_t data, 1292 int flag, struct proc *p) 1293 { 1294 int error; 1295 1296 error = ((*sc->sc_accessops->ioctl)(sc->sc_accesscookie, cmd, data, 1297 flag, p)); 1298 /* Do not report parameters with empty ranges to userland. */ 1299 if (error == 0 && cmd == WSDISPLAYIO_GETPARAM) { 1300 struct wsdisplay_param *dp = (struct wsdisplay_param *)data; 1301 switch (dp->param) { 1302 case WSDISPLAYIO_PARAM_BACKLIGHT: 1303 case WSDISPLAYIO_PARAM_BRIGHTNESS: 1304 case WSDISPLAYIO_PARAM_CONTRAST: 1305 if (dp->min == dp->max) 1306 error = ENOTTY; 1307 break; 1308 } 1309 } 1310 1311 return error; 1312 } 1313 1314 int 1315 wsdisplay_cfg_ioctl(struct wsdisplay_softc *sc, u_long cmd, caddr_t data, 1316 int flag, struct proc *p) 1317 { 1318 int error; 1319 void *buf; 1320 size_t fontsz; 1321 #if NWSKBD > 0 1322 struct wsevsrc *inp; 1323 #endif 1324 1325 switch (cmd) { 1326 #ifdef HAVE_WSMOUSED_SUPPORT 1327 case WSDISPLAYIO_WSMOUSED: 1328 error = wsmoused(sc, data, flag, p); 1329 return (error); 1330 #endif 1331 case WSDISPLAYIO_ADDSCREEN: 1332 #define d ((struct wsdisplay_addscreendata *)data) 1333 if ((error = wsdisplay_addscreen(sc, d->idx, 1334 d->screentype, d->emul)) == 0) 1335 wsdisplay_addscreen_print(sc, d->idx, 0); 1336 return (error); 1337 #undef d 1338 case WSDISPLAYIO_DELSCREEN: 1339 #define d ((struct wsdisplay_delscreendata *)data) 1340 return (wsdisplay_delscreen(sc, d->idx, d->flags)); 1341 #undef d 1342 case WSDISPLAYIO_GETSCREEN: 1343 return (wsdisplay_getscreen(sc, 1344 (struct wsdisplay_addscreendata *)data)); 1345 case WSDISPLAYIO_SETSCREEN: 1346 return (wsdisplay_switch((void *)sc, *(int *)data, 1)); 1347 case WSDISPLAYIO_LDFONT: 1348 #define d ((struct wsdisplay_font *)data) 1349 if (!sc->sc_accessops->load_font) 1350 return (EINVAL); 1351 if (d->fontheight > 64 || d->stride > 8) /* 64x64 pixels */ 1352 return (EINVAL); 1353 if (d->numchars > 65536) /* unicode plane */ 1354 return (EINVAL); 1355 fontsz = d->fontheight * d->stride * d->numchars; 1356 if (fontsz > WSDISPLAY_MAXFONTSZ) 1357 return (EINVAL); 1358 1359 buf = malloc(fontsz, M_DEVBUF, M_WAITOK); 1360 error = copyin(d->data, buf, fontsz); 1361 if (error) { 1362 free(buf, M_DEVBUF, fontsz); 1363 return (error); 1364 } 1365 d->data = buf; 1366 error = 1367 (*sc->sc_accessops->load_font)(sc->sc_accesscookie, 0, d); 1368 if (error) 1369 free(buf, M_DEVBUF, fontsz); 1370 return (error); 1371 1372 case WSDISPLAYIO_LSFONT: 1373 if (!sc->sc_accessops->list_font) 1374 return (EINVAL); 1375 error = 1376 (*sc->sc_accessops->list_font)(sc->sc_accesscookie, d); 1377 return (error); 1378 1379 case WSDISPLAYIO_DELFONT: 1380 return (EINVAL); 1381 #undef d 1382 1383 #if NWSKBD > 0 1384 case WSMUXIO_ADD_DEVICE: 1385 #define d ((struct wsmux_device *)data) 1386 if (d->idx == -1 && d->type == WSMUX_KBD) 1387 d->idx = wskbd_pickfree(); 1388 #undef d 1389 /* FALLTHROUGH */ 1390 case WSMUXIO_INJECTEVENT: 1391 case WSMUXIO_REMOVE_DEVICE: 1392 case WSMUXIO_LIST_DEVICES: 1393 inp = sc->sc_input; 1394 if (inp == NULL) 1395 return (ENXIO); 1396 return (wsevsrc_ioctl(inp, cmd, data, flag,p)); 1397 #endif /* NWSKBD > 0 */ 1398 1399 } 1400 return (EINVAL); 1401 } 1402 1403 paddr_t 1404 wsdisplaymmap(dev_t dev, off_t offset, int prot) 1405 { 1406 struct wsdisplay_softc *sc = wsdisplay_cd.cd_devs[WSDISPLAYUNIT(dev)]; 1407 struct wsscreen *scr; 1408 1409 if (ISWSDISPLAYCTL(dev)) 1410 return (-1); 1411 1412 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 1413 return (-1); 1414 1415 if (!(scr->scr_flags & SCR_GRAPHICS)) 1416 return (-1); 1417 1418 /* pass mmap to display */ 1419 return ((*sc->sc_accessops->mmap)(sc->sc_accesscookie, offset, prot)); 1420 } 1421 1422 int 1423 wsdisplaykqfilter(dev_t dev, struct knote *kn) 1424 { 1425 struct wsdisplay_softc *sc = wsdisplay_cd.cd_devs[WSDISPLAYUNIT(dev)]; 1426 struct wsscreen *scr; 1427 1428 if (ISWSDISPLAYCTL(dev)) 1429 return (ENXIO); 1430 1431 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 1432 return (ENXIO); 1433 1434 if (!WSSCREEN_HAS_TTY(scr)) 1435 return (ENXIO); 1436 1437 return (ttkqfilter(dev, kn)); 1438 } 1439 1440 void 1441 wsdisplaystart(struct tty *tp) 1442 { 1443 struct wsdisplay_softc *sc; 1444 struct wsscreen *scr; 1445 int s, n, done, unit; 1446 u_char *buf; 1447 1448 unit = WSDISPLAYUNIT(tp->t_dev); 1449 if (unit >= wsdisplay_cd.cd_ndevs || 1450 (sc = wsdisplay_cd.cd_devs[unit]) == NULL) 1451 return; 1452 1453 s = spltty(); 1454 if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) { 1455 splx(s); 1456 return; 1457 } 1458 if (tp->t_outq.c_cc == 0) 1459 goto low; 1460 1461 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(tp->t_dev)]) == NULL) { 1462 splx(s); 1463 return; 1464 } 1465 if (scr->scr_hold_screen) { 1466 tp->t_state |= TS_TIMEOUT; 1467 splx(s); 1468 return; 1469 } 1470 tp->t_state |= TS_BUSY; 1471 splx(s); 1472 1473 /* 1474 * Drain output from ring buffer. 1475 * The output will normally be in one contiguous chunk, but when the 1476 * ring wraps, it will be in two pieces.. one at the end of the ring, 1477 * the other at the start. For performance, rather than loop here, 1478 * we output one chunk, see if there's another one, and if so, output 1479 * it too. 1480 */ 1481 1482 n = ndqb(&tp->t_outq, 0); 1483 buf = tp->t_outq.c_cf; 1484 1485 if (!(scr->scr_flags & SCR_GRAPHICS)) { 1486 #ifdef HAVE_BURNER_SUPPORT 1487 wsdisplay_burn(sc, WSDISPLAY_BURN_OUTPUT); 1488 #endif 1489 #ifdef HAVE_WSMOUSED_SUPPORT 1490 if (scr == sc->sc_focus) 1491 mouse_remove(scr); 1492 #endif 1493 done = (*scr->scr_dconf->wsemul->output) 1494 (scr->scr_dconf->wsemulcookie, buf, n, 0); 1495 } else 1496 done = n; 1497 ndflush(&tp->t_outq, done); 1498 1499 if (done == n) { 1500 if ((n = ndqb(&tp->t_outq, 0)) > 0) { 1501 buf = tp->t_outq.c_cf; 1502 1503 if (!(scr->scr_flags & SCR_GRAPHICS)) { 1504 done = (*scr->scr_dconf->wsemul->output) 1505 (scr->scr_dconf->wsemulcookie, buf, n, 0); 1506 } else 1507 done = n; 1508 ndflush(&tp->t_outq, done); 1509 } 1510 } 1511 1512 s = spltty(); 1513 tp->t_state &= ~TS_BUSY; 1514 /* Come back if there's more to do */ 1515 if (tp->t_outq.c_cc) { 1516 tp->t_state |= TS_TIMEOUT; 1517 timeout_add(&tp->t_rstrt_to, (hz > 128) ? (hz / 128) : 1); 1518 } 1519 low: 1520 ttwakeupwr(tp); 1521 splx(s); 1522 } 1523 1524 int 1525 wsdisplaystop(struct tty *tp, int flag) 1526 { 1527 int s; 1528 1529 s = spltty(); 1530 if (ISSET(tp->t_state, TS_BUSY)) 1531 if (!ISSET(tp->t_state, TS_TTSTOP)) 1532 SET(tp->t_state, TS_FLUSH); 1533 splx(s); 1534 1535 return (0); 1536 } 1537 1538 /* Set line parameters. */ 1539 int 1540 wsdisplayparam(struct tty *tp, struct termios *t) 1541 { 1542 1543 tp->t_ispeed = t->c_ispeed; 1544 tp->t_ospeed = t->c_ospeed; 1545 tp->t_cflag = t->c_cflag; 1546 return (0); 1547 } 1548 1549 /* 1550 * Callbacks for the emulation code. 1551 */ 1552 void 1553 wsdisplay_emulbell(void *v) 1554 { 1555 struct wsscreen *scr = v; 1556 1557 if (scr == NULL) /* console, before real attach */ 1558 return; 1559 1560 if (scr->scr_flags & SCR_GRAPHICS) /* can this happen? */ 1561 return; 1562 1563 task_add(scr->sc->sc_taskq, &scr->scr_emulbell_task); 1564 } 1565 1566 void 1567 wsdisplay_emulbell_task(void *v) 1568 { 1569 struct wsscreen *scr = v; 1570 1571 (void)wsdisplay_internal_ioctl(scr->sc, scr, WSKBDIO_BELL, NULL, 1572 FWRITE, NULL); 1573 } 1574 1575 #if !defined(WSEMUL_NO_VT100) 1576 void 1577 wsdisplay_emulinput(void *v, const u_char *data, u_int count) 1578 { 1579 struct wsscreen *scr = v; 1580 struct tty *tp; 1581 1582 if (v == NULL) /* console, before real attach */ 1583 return; 1584 1585 if (scr->scr_flags & SCR_GRAPHICS) /* XXX can't happen */ 1586 return; 1587 if (!WSSCREEN_HAS_TTY(scr)) 1588 return; 1589 1590 tp = scr->scr_tty; 1591 while (count-- > 0) 1592 (*linesw[tp->t_line].l_rint)(*data++, tp); 1593 } 1594 #endif 1595 1596 /* 1597 * Calls from the keyboard interface. 1598 */ 1599 void 1600 wsdisplay_kbdinput(struct device *dev, kbd_t layout, keysym_t *ks, int num) 1601 { 1602 struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev; 1603 struct wsscreen *scr; 1604 const u_char *dp; 1605 int count; 1606 struct tty *tp; 1607 1608 scr = sc->sc_focus; 1609 if (!scr || !WSSCREEN_HAS_TTY(scr)) 1610 return; 1611 1612 1613 tp = scr->scr_tty; 1614 for (; num > 0; num--) { 1615 count = (*scr->scr_dconf->wsemul->translate) 1616 (scr->scr_dconf->wsemulcookie, layout, *ks++, &dp); 1617 while (count-- > 0) 1618 (*linesw[tp->t_line].l_rint)(*dp++, tp); 1619 } 1620 } 1621 1622 #ifdef WSDISPLAY_COMPAT_RAWKBD 1623 void 1624 wsdisplay_rawkbdinput(struct device *dev, u_char *buf, int num) 1625 { 1626 struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev; 1627 struct wsscreen *scr; 1628 struct tty *tp; 1629 1630 scr = sc->sc_focus; 1631 if (!scr || !WSSCREEN_HAS_TTY(scr)) 1632 return; 1633 1634 tp = scr->scr_tty; 1635 while (num-- > 0) 1636 (*linesw[tp->t_line].l_rint)(*buf++, tp); 1637 } 1638 int 1639 wsdisplay_update_rawkbd(struct wsdisplay_softc *sc, struct wsscreen *scr) 1640 { 1641 #if NWSKBD > 0 1642 int s, raw, data, error; 1643 struct wsevsrc *inp; 1644 1645 s = spltty(); 1646 1647 raw = (scr ? scr->scr_rawkbd : 0); 1648 1649 if (scr != sc->sc_focus || sc->sc_rawkbd == raw) { 1650 splx(s); 1651 return (0); 1652 } 1653 1654 data = raw ? WSKBD_RAW : WSKBD_TRANSLATED; 1655 inp = sc->sc_input; 1656 if (inp == NULL) { 1657 splx(s); 1658 return (ENXIO); 1659 } 1660 error = wsevsrc_display_ioctl(inp, WSKBDIO_SETMODE, &data, FWRITE, 0); 1661 if (!error) 1662 sc->sc_rawkbd = raw; 1663 splx(s); 1664 return (error); 1665 #else 1666 return (0); 1667 #endif 1668 } 1669 #endif 1670 1671 int 1672 wsdisplay_switch3(void *arg, int error, int waitok) 1673 { 1674 struct wsdisplay_softc *sc = arg; 1675 int no; 1676 struct wsscreen *scr; 1677 1678 #ifdef WSDISPLAY_COMPAT_USL 1679 if (!ISSET(sc->sc_flags, SC_SWITCHPENDING)) { 1680 printf("wsdisplay_switch3: not switching\n"); 1681 return (EINVAL); 1682 } 1683 1684 no = sc->sc_screenwanted; 1685 if (no < 0 || no >= WSDISPLAY_MAXSCREEN) 1686 panic("wsdisplay_switch3: invalid screen %d", no); 1687 scr = sc->sc_scr[no]; 1688 if (!scr) { 1689 printf("wsdisplay_switch3: screen %d disappeared\n", no); 1690 error = ENXIO; 1691 } 1692 1693 if (error) { 1694 /* try to recover, avoid recursion */ 1695 1696 if (sc->sc_oldscreen == WSDISPLAY_NULLSCREEN) { 1697 printf("wsdisplay_switch3: giving up\n"); 1698 sc->sc_focus = NULL; 1699 #ifdef WSDISPLAY_COMPAT_RAWKBD 1700 wsdisplay_update_rawkbd(sc, 0); 1701 #endif 1702 CLR(sc->sc_flags, SC_SWITCHPENDING); 1703 return (error); 1704 } 1705 1706 sc->sc_screenwanted = sc->sc_oldscreen; 1707 sc->sc_oldscreen = WSDISPLAY_NULLSCREEN; 1708 return (wsdisplay_switch1(arg, 0, waitok)); 1709 } 1710 #else 1711 /* 1712 * If we do not have syncops support, we come straight from 1713 * wsdisplay_switch2 which has already validated our arguments 1714 * and did not sleep. 1715 */ 1716 no = sc->sc_screenwanted; 1717 scr = sc->sc_scr[no]; 1718 #endif 1719 1720 CLR(sc->sc_flags, SC_SWITCHPENDING); 1721 1722 #ifdef HAVE_BURNER_SUPPORT 1723 if (!error) 1724 wsdisplay_burner_setup(sc, scr); 1725 #endif 1726 1727 if (!error && (scr->scr_flags & SCR_WAITACTIVE)) 1728 wakeup(scr); 1729 return (error); 1730 } 1731 1732 int 1733 wsdisplay_switch2(void *arg, int error, int waitok) 1734 { 1735 struct wsdisplay_softc *sc = arg; 1736 int no; 1737 struct wsscreen *scr; 1738 1739 if (!ISSET(sc->sc_flags, SC_SWITCHPENDING)) { 1740 printf("wsdisplay_switch2: not switching\n"); 1741 return (EINVAL); 1742 } 1743 1744 no = sc->sc_screenwanted; 1745 if (no < 0 || no >= WSDISPLAY_MAXSCREEN) 1746 panic("wsdisplay_switch2: invalid screen %d", no); 1747 scr = sc->sc_scr[no]; 1748 if (!scr) { 1749 printf("wsdisplay_switch2: screen %d disappeared\n", no); 1750 error = ENXIO; 1751 } 1752 1753 if (error) { 1754 /* try to recover, avoid recursion */ 1755 1756 if (sc->sc_oldscreen == WSDISPLAY_NULLSCREEN) { 1757 printf("wsdisplay_switch2: giving up\n"); 1758 sc->sc_focus = NULL; 1759 CLR(sc->sc_flags, SC_SWITCHPENDING); 1760 return (error); 1761 } 1762 1763 sc->sc_screenwanted = sc->sc_oldscreen; 1764 sc->sc_oldscreen = WSDISPLAY_NULLSCREEN; 1765 return (wsdisplay_switch1(arg, 0, waitok)); 1766 } 1767 1768 sc->sc_focusidx = no; 1769 sc->sc_focus = scr; 1770 1771 #ifdef WSDISPLAY_COMPAT_RAWKBD 1772 (void) wsdisplay_update_rawkbd(sc, scr); 1773 #endif 1774 /* keyboard map??? */ 1775 1776 #ifdef WSDISPLAY_COMPAT_USL 1777 #define wsswitch_cb3 ((void (*)(void *, int, int))wsdisplay_switch3) 1778 if (scr->scr_syncops) { 1779 error = (*scr->scr_syncops->attach)(scr->scr_synccookie, waitok, 1780 sc->sc_isconsole && wsdisplay_cons_pollmode ? 1781 0 : wsswitch_cb3, sc); 1782 if (error == EAGAIN) { 1783 /* switch will be done asynchronously */ 1784 return (0); 1785 } 1786 } 1787 #endif 1788 1789 return (wsdisplay_switch3(sc, error, waitok)); 1790 } 1791 1792 int 1793 wsdisplay_switch1(void *arg, int error, int waitok) 1794 { 1795 struct wsdisplay_softc *sc = arg; 1796 int no; 1797 struct wsscreen *scr; 1798 1799 if (!ISSET(sc->sc_flags, SC_SWITCHPENDING)) { 1800 printf("wsdisplay_switch1: not switching\n"); 1801 return (EINVAL); 1802 } 1803 1804 no = sc->sc_screenwanted; 1805 if (no == WSDISPLAY_NULLSCREEN) { 1806 CLR(sc->sc_flags, SC_SWITCHPENDING); 1807 if (!error) { 1808 sc->sc_focus = NULL; 1809 } 1810 wakeup(sc); 1811 return (error); 1812 } 1813 if (no < 0 || no >= WSDISPLAY_MAXSCREEN) 1814 panic("wsdisplay_switch1: invalid screen %d", no); 1815 scr = sc->sc_scr[no]; 1816 if (!scr) { 1817 printf("wsdisplay_switch1: screen %d disappeared\n", no); 1818 error = ENXIO; 1819 } 1820 1821 if (error) { 1822 CLR(sc->sc_flags, SC_SWITCHPENDING); 1823 return (error); 1824 } 1825 1826 #define wsswitch_cb2 ((void (*)(void *, int, int))wsdisplay_switch2) 1827 error = (*sc->sc_accessops->show_screen)(sc->sc_accesscookie, 1828 scr->scr_dconf->emulcookie, waitok, 1829 sc->sc_isconsole && wsdisplay_cons_pollmode ? 0 : wsswitch_cb2, sc); 1830 if (error == EAGAIN) { 1831 /* switch will be done asynchronously */ 1832 return (0); 1833 } 1834 1835 return (wsdisplay_switch2(sc, error, waitok)); 1836 } 1837 1838 int 1839 wsdisplay_switch(struct device *dev, int no, int waitok) 1840 { 1841 struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev; 1842 int s, res = 0; 1843 struct wsscreen *scr; 1844 1845 if (no != WSDISPLAY_NULLSCREEN) { 1846 if (no < 0 || no >= WSDISPLAY_MAXSCREEN) 1847 return (EINVAL); 1848 if (sc->sc_scr[no] == NULL) 1849 return (ENXIO); 1850 } 1851 1852 s = spltty(); 1853 1854 while (sc->sc_resumescreen != WSDISPLAY_NULLSCREEN && res == 0) 1855 res = tsleep_nsec(&sc->sc_resumescreen, PCATCH, "wsrestore", 1856 INFSLP); 1857 if (res) { 1858 splx(s); 1859 return (res); 1860 } 1861 1862 if ((sc->sc_focus && no == sc->sc_focusidx) || 1863 (sc->sc_focus == NULL && no == WSDISPLAY_NULLSCREEN)) { 1864 splx(s); 1865 return (0); 1866 } 1867 1868 if (ISSET(sc->sc_flags, SC_SWITCHPENDING)) { 1869 splx(s); 1870 return (EBUSY); 1871 } 1872 1873 SET(sc->sc_flags, SC_SWITCHPENDING); 1874 sc->sc_screenwanted = no; 1875 1876 splx(s); 1877 1878 scr = sc->sc_focus; 1879 if (!scr) { 1880 sc->sc_oldscreen = WSDISPLAY_NULLSCREEN; 1881 return (wsdisplay_switch1(sc, 0, waitok)); 1882 } else 1883 sc->sc_oldscreen = sc->sc_focusidx; 1884 1885 #ifdef WSDISPLAY_COMPAT_USL 1886 #define wsswitch_cb1 ((void (*)(void *, int, int))wsdisplay_switch1) 1887 if (scr->scr_syncops) { 1888 res = (*scr->scr_syncops->detach)(scr->scr_synccookie, waitok, 1889 sc->sc_isconsole && wsdisplay_cons_pollmode ? 1890 0 : wsswitch_cb1, sc); 1891 if (res == EAGAIN) { 1892 /* switch will be done asynchronously */ 1893 return (0); 1894 } 1895 } else if (scr->scr_flags & SCR_GRAPHICS) { 1896 /* no way to save state */ 1897 res = EBUSY; 1898 } 1899 #endif 1900 1901 #ifdef HAVE_WSMOUSED_SUPPORT 1902 mouse_remove(scr); 1903 #endif 1904 1905 return (wsdisplay_switch1(sc, res, waitok)); 1906 } 1907 1908 void 1909 wsdisplay_reset(struct device *dev, enum wsdisplay_resetops op) 1910 { 1911 struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev; 1912 struct wsscreen *scr; 1913 1914 scr = sc->sc_focus; 1915 1916 if (!scr) 1917 return; 1918 1919 switch (op) { 1920 case WSDISPLAY_RESETEMUL: 1921 (*scr->scr_dconf->wsemul->reset)(scr->scr_dconf->wsemulcookie, 1922 WSEMUL_RESET); 1923 break; 1924 case WSDISPLAY_RESETCLOSE: 1925 wsdisplay_closescreen(sc, scr); 1926 break; 1927 } 1928 } 1929 1930 #ifdef WSDISPLAY_COMPAT_USL 1931 /* 1932 * Interface for (external) VT switch / process synchronization code 1933 */ 1934 int 1935 wsscreen_attach_sync(struct wsscreen *scr, const struct wscons_syncops *ops, 1936 void *cookie) 1937 { 1938 if (scr->scr_syncops) { 1939 /* 1940 * The screen is already claimed. 1941 * Check if the owner is still alive. 1942 */ 1943 if ((*scr->scr_syncops->check)(scr->scr_synccookie)) 1944 return (EBUSY); 1945 } 1946 scr->scr_syncops = ops; 1947 scr->scr_synccookie = cookie; 1948 return (0); 1949 } 1950 1951 int 1952 wsscreen_detach_sync(struct wsscreen *scr) 1953 { 1954 if (!scr->scr_syncops) 1955 return (EINVAL); 1956 scr->scr_syncops = NULL; 1957 return (0); 1958 } 1959 1960 int 1961 wsscreen_lookup_sync(struct wsscreen *scr, 1962 const struct wscons_syncops *ops, /* used as ID */ 1963 void **cookiep) 1964 { 1965 if (!scr->scr_syncops || ops != scr->scr_syncops) 1966 return (EINVAL); 1967 *cookiep = scr->scr_synccookie; 1968 return (0); 1969 } 1970 #endif 1971 1972 /* 1973 * Interface to virtual screen stuff 1974 */ 1975 int 1976 wsdisplay_maxscreenidx(struct wsdisplay_softc *sc) 1977 { 1978 return (WSDISPLAY_MAXSCREEN - 1); 1979 } 1980 1981 int 1982 wsdisplay_screenstate(struct wsdisplay_softc *sc, int idx) 1983 { 1984 if (idx < 0 || idx >= WSDISPLAY_MAXSCREEN) 1985 return (EINVAL); 1986 if (!sc->sc_scr[idx]) 1987 return (ENXIO); 1988 return ((sc->sc_scr[idx]->scr_flags & SCR_OPEN) ? EBUSY : 0); 1989 } 1990 1991 int 1992 wsdisplay_getactivescreen(struct wsdisplay_softc *sc) 1993 { 1994 return (sc->sc_focus ? sc->sc_focusidx : WSDISPLAY_NULLSCREEN); 1995 } 1996 1997 int 1998 wsscreen_switchwait(struct wsdisplay_softc *sc, int no) 1999 { 2000 struct wsscreen *scr; 2001 int s, res = 0; 2002 2003 if (no == WSDISPLAY_NULLSCREEN) { 2004 s = spltty(); 2005 while (sc->sc_focus && res == 0) { 2006 res = tsleep_nsec(sc, PCATCH, "wswait", INFSLP); 2007 } 2008 splx(s); 2009 return (res); 2010 } 2011 2012 if (no < 0 || no >= WSDISPLAY_MAXSCREEN) 2013 return (ENXIO); 2014 scr = sc->sc_scr[no]; 2015 if (!scr) 2016 return (ENXIO); 2017 2018 s = spltty(); 2019 if (scr != sc->sc_focus) { 2020 scr->scr_flags |= SCR_WAITACTIVE; 2021 res = tsleep_nsec(scr, PCATCH, "wswait2", INFSLP); 2022 if (scr != sc->sc_scr[no]) 2023 res = ENXIO; /* disappeared in the meantime */ 2024 else 2025 scr->scr_flags &= ~SCR_WAITACTIVE; 2026 } 2027 splx(s); 2028 return (res); 2029 } 2030 2031 void 2032 wsdisplay_kbdholdscr(struct wsscreen *scr, int hold) 2033 { 2034 if (hold) 2035 scr->scr_hold_screen = 1; 2036 else { 2037 scr->scr_hold_screen = 0; 2038 timeout_add(&scr->scr_tty->t_rstrt_to, 0); /* "immediate" */ 2039 } 2040 } 2041 2042 void 2043 wsdisplay_kbdholdscreen(struct device *dev, int hold) 2044 { 2045 struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev; 2046 struct wsscreen *scr; 2047 2048 scr = sc->sc_focus; 2049 if (scr != NULL && WSSCREEN_HAS_TTY(scr)) 2050 wsdisplay_kbdholdscr(scr, hold); 2051 } 2052 2053 #if NWSKBD > 0 2054 void 2055 wsdisplay_set_console_kbd(struct wsevsrc *src) 2056 { 2057 if (wsdisplay_console_device == NULL) { 2058 src->me_dispdv = NULL; 2059 return; 2060 } 2061 #if NWSMUX > 0 2062 if (wsmux_attach_sc((struct wsmux_softc *) 2063 wsdisplay_console_device->sc_input, src)) { 2064 src->me_dispdv = NULL; 2065 return; 2066 } 2067 #else 2068 wsdisplay_console_device->sc_input = src; 2069 #endif 2070 src->me_dispdv = &wsdisplay_console_device->sc_dv; 2071 } 2072 2073 #if NWSMUX == 0 2074 int 2075 wsdisplay_set_kbd(struct device *disp, struct wsevsrc *kbd) 2076 { 2077 struct wsdisplay_softc *sc = (struct wsdisplay_softc *)disp; 2078 2079 if (sc->sc_input != NULL) 2080 return (EBUSY); 2081 2082 sc->sc_input = kbd; 2083 2084 return (0); 2085 } 2086 #endif 2087 2088 #endif /* NWSKBD > 0 */ 2089 2090 /* 2091 * Console interface. 2092 */ 2093 void 2094 wsdisplay_cnputc(dev_t dev, int i) 2095 { 2096 struct wsscreen_internal *dc; 2097 char c = i; 2098 2099 if (!wsdisplay_console_initted) 2100 return; 2101 2102 if (wsdisplay_console_device != NULL && 2103 (wsdisplay_console_device->sc_scr[0] != NULL) && 2104 (wsdisplay_console_device->sc_scr[0]->scr_flags & SCR_GRAPHICS)) 2105 return; 2106 2107 dc = &wsdisplay_console_conf; 2108 #ifdef HAVE_BURNER_SUPPORT 2109 /*wsdisplay_burn(wsdisplay_console_device, WSDISPLAY_BURN_OUTPUT);*/ 2110 #endif 2111 (void)(*dc->wsemul->output)(dc->wsemulcookie, &c, 1, 1); 2112 } 2113 2114 int 2115 wsdisplay_getc_dummy(dev_t dev) 2116 { 2117 /* panic? */ 2118 return (0); 2119 } 2120 2121 void 2122 wsdisplay_pollc(dev_t dev, int on) 2123 { 2124 2125 wsdisplay_cons_pollmode = on; 2126 2127 /* notify to fb drivers */ 2128 if (wsdisplay_console_device != NULL && 2129 wsdisplay_console_device->sc_accessops->pollc != NULL) 2130 (*wsdisplay_console_device->sc_accessops->pollc) 2131 (wsdisplay_console_device->sc_accesscookie, on); 2132 2133 /* notify to kbd drivers */ 2134 if (wsdisplay_cons_kbd_pollc) 2135 (*wsdisplay_cons_kbd_pollc)(dev, on); 2136 } 2137 2138 void 2139 wsdisplay_set_cons_kbd(int (*get)(dev_t), void (*poll)(dev_t, int), 2140 void (*bell)(dev_t, u_int, u_int, u_int)) 2141 { 2142 wsdisplay_cons.cn_getc = get; 2143 wsdisplay_cons.cn_bell = bell; 2144 wsdisplay_cons_kbd_pollc = poll; 2145 } 2146 2147 void 2148 wsdisplay_unset_cons_kbd(void) 2149 { 2150 wsdisplay_cons.cn_getc = wsdisplay_getc_dummy; 2151 wsdisplay_cons.cn_bell = NULL; 2152 wsdisplay_cons_kbd_pollc = NULL; 2153 } 2154 2155 /* 2156 * Switch the console display to its first screen. 2157 */ 2158 void 2159 wsdisplay_switchtoconsole(void) 2160 { 2161 struct wsdisplay_softc *sc; 2162 struct wsscreen *scr; 2163 2164 if (wsdisplay_console_device != NULL && cn_tab == &wsdisplay_cons) { 2165 sc = wsdisplay_console_device; 2166 if ((scr = sc->sc_scr[0]) == NULL) 2167 return; 2168 (*sc->sc_accessops->show_screen)(sc->sc_accesscookie, 2169 scr->scr_dconf->emulcookie, 0, NULL, NULL); 2170 } 2171 } 2172 2173 /* 2174 * Switch the console display to its ddb screen, avoiding locking 2175 * where we can. 2176 */ 2177 void 2178 wsdisplay_enter_ddb(void) 2179 { 2180 struct wsdisplay_softc *sc; 2181 struct wsscreen *scr; 2182 2183 if (wsdisplay_console_device != NULL && cn_tab == &wsdisplay_cons) { 2184 sc = wsdisplay_console_device; 2185 if ((scr = sc->sc_scr[0]) == NULL) 2186 return; 2187 if (sc->sc_accessops->enter_ddb) { 2188 (*sc->sc_accessops->enter_ddb)(sc->sc_accesscookie, 2189 scr->scr_dconf->emulcookie); 2190 } else { 2191 (*sc->sc_accessops->show_screen)(sc->sc_accesscookie, 2192 scr->scr_dconf->emulcookie, 0, NULL, NULL); 2193 } 2194 } 2195 } 2196 2197 /* 2198 * Deal with the xserver doing driver in userland and thus screwing up suspend 2199 * and resume by switching away from it at suspend/resume time. 2200 * 2201 * these functions must be called from the MD suspend callback, since we may 2202 * need to sleep if we have a user (probably an X server) on a vt. therefore 2203 * this can't be a config_suspend() hook. 2204 */ 2205 void 2206 wsdisplay_suspend(void) 2207 { 2208 int i; 2209 2210 for (i = 0; i < wsdisplay_cd.cd_ndevs; i++) 2211 if (wsdisplay_cd.cd_devs[i] != NULL) 2212 wsdisplay_suspend_device(wsdisplay_cd.cd_devs[i]); 2213 } 2214 2215 void 2216 wsdisplay_suspend_device(struct device *dev) 2217 { 2218 struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev; 2219 struct wsscreen *scr; 2220 int active, idx, ret = 0, s; 2221 2222 if ((active = wsdisplay_getactivescreen(sc)) == WSDISPLAY_NULLSCREEN) 2223 return; 2224 2225 scr = sc->sc_scr[active]; 2226 /* 2227 * We want to switch out of graphics mode for the suspend 2228 */ 2229 retry: 2230 idx = WSDISPLAY_MAXSCREEN; 2231 if (scr->scr_flags & SCR_GRAPHICS) { 2232 for (idx = 0; idx < WSDISPLAY_MAXSCREEN; idx++) { 2233 if (sc->sc_scr[idx] == NULL || sc->sc_scr[idx] == scr) 2234 continue; 2235 2236 if ((sc->sc_scr[idx]->scr_flags & SCR_GRAPHICS) == 0) 2237 break; 2238 } 2239 } 2240 2241 /* if we don't have anything to switch to, we can't do anything */ 2242 if (idx == WSDISPLAY_MAXSCREEN) 2243 return; 2244 2245 /* 2246 * we do a lot of magic here because we need to know that the 2247 * switch has completed before we return 2248 */ 2249 ret = wsdisplay_switch((struct device *)sc, idx, 1); 2250 if (ret == EBUSY) { 2251 /* XXX sleep on what's going on */ 2252 goto retry; 2253 } else if (ret) 2254 return; 2255 2256 s = spltty(); 2257 sc->sc_resumescreen = active; /* block other vt switches until resume */ 2258 splx(s); 2259 /* 2260 * This will either return ENXIO (invalid (shouldn't happen) or 2261 * wsdisplay disappeared (problem solved)), or EINTR/ERESTART. 2262 * Not much we can do about the latter since we can't return to 2263 * userland. 2264 */ 2265 (void)wsscreen_switchwait(sc, idx); 2266 } 2267 2268 void 2269 wsdisplay_resume(void) 2270 { 2271 int i; 2272 2273 for (i = 0; i < wsdisplay_cd.cd_ndevs; i++) 2274 if (wsdisplay_cd.cd_devs[i] != NULL) 2275 wsdisplay_resume_device(wsdisplay_cd.cd_devs[i]); 2276 } 2277 2278 void 2279 wsdisplay_resume_device(struct device *dev) 2280 { 2281 struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev; 2282 int idx, s; 2283 2284 if (sc->sc_resumescreen != WSDISPLAY_NULLSCREEN) { 2285 s = spltty(); 2286 idx = sc->sc_resumescreen; 2287 sc->sc_resumescreen = WSDISPLAY_NULLSCREEN; 2288 wakeup(&sc->sc_resumescreen); 2289 splx(s); 2290 (void)wsdisplay_switch((struct device *)sc, idx, 1); 2291 } 2292 } 2293 2294 #ifdef HAVE_SCROLLBACK_SUPPORT 2295 void 2296 wsscrollback(void *arg, int op) 2297 { 2298 struct wsdisplay_softc *sc = arg; 2299 int lines; 2300 2301 if (sc->sc_focus == NULL) 2302 return; 2303 2304 if (op == WSDISPLAY_SCROLL_RESET) 2305 lines = 0; 2306 else { 2307 lines = sc->sc_focus->scr_dconf->scrdata->nrows - 1; 2308 if (op == WSDISPLAY_SCROLL_BACKWARD) 2309 lines = -lines; 2310 } 2311 2312 if (sc->sc_accessops->scrollback) { 2313 (*sc->sc_accessops->scrollback)(sc->sc_accesscookie, 2314 sc->sc_focus->scr_dconf->emulcookie, lines); 2315 } 2316 } 2317 #endif 2318 2319 #ifdef HAVE_BURNER_SUPPORT 2320 /* 2321 * Update screen burner behaviour after either a screen focus change or 2322 * a screen mode change. 2323 * This is needed to allow X11 to manage screen blanking without any 2324 * interference from the kernel. 2325 */ 2326 void 2327 wsdisplay_burner_setup(struct wsdisplay_softc *sc, struct wsscreen *scr) 2328 { 2329 if (scr->scr_flags & SCR_GRAPHICS) { 2330 /* enable video _immediately_ if it needs to be... */ 2331 if (sc->sc_burnman) 2332 wsdisplay_burner(sc); 2333 /* ...and disable the burner while X is running */ 2334 if (sc->sc_burnout) { 2335 timeout_del(&sc->sc_burner); 2336 sc->sc_burnout = 0; 2337 } 2338 } else { 2339 /* reenable the burner after exiting from X */ 2340 if (!sc->sc_burnman) { 2341 sc->sc_burnout = sc->sc_burnoutintvl; 2342 wsdisplay_burn(sc, sc->sc_burnflags); 2343 } 2344 } 2345 } 2346 2347 void 2348 wsdisplay_burn(void *v, u_int flags) 2349 { 2350 struct wsdisplay_softc *sc = v; 2351 2352 if ((flags & sc->sc_burnflags & (WSDISPLAY_BURN_OUTPUT | 2353 WSDISPLAY_BURN_KBD | WSDISPLAY_BURN_MOUSE)) && 2354 sc->sc_accessops->burn_screen) { 2355 if (sc->sc_burnout) 2356 timeout_add_msec(&sc->sc_burner, sc->sc_burnout); 2357 if (sc->sc_burnman) 2358 sc->sc_burnout = 0; 2359 } 2360 } 2361 2362 void 2363 wsdisplay_burner(void *v) 2364 { 2365 struct wsdisplay_softc *sc = v; 2366 int s; 2367 2368 if (sc->sc_accessops->burn_screen) { 2369 (*sc->sc_accessops->burn_screen)(sc->sc_accesscookie, 2370 sc->sc_burnman, sc->sc_burnflags); 2371 s = spltty(); 2372 if (sc->sc_burnman) { 2373 sc->sc_burnout = sc->sc_burnoutintvl; 2374 timeout_add_msec(&sc->sc_burner, sc->sc_burnout); 2375 } else 2376 sc->sc_burnout = sc->sc_burninintvl; 2377 sc->sc_burnman = !sc->sc_burnman; 2378 splx(s); 2379 } 2380 } 2381 #endif 2382 2383 int 2384 wsdisplay_get_param(struct wsdisplay_softc *sc, struct wsdisplay_param *dp) 2385 { 2386 int error = ENXIO; 2387 int i; 2388 2389 if (sc != NULL) 2390 return wsdisplay_param(&sc->sc_dv, WSDISPLAYIO_GETPARAM, dp); 2391 2392 for (i = 0; i < wsdisplay_cd.cd_ndevs; i++) { 2393 sc = wsdisplay_cd.cd_devs[i]; 2394 if (sc == NULL) 2395 continue; 2396 error = wsdisplay_param(&sc->sc_dv, WSDISPLAYIO_GETPARAM, dp); 2397 if (error == 0) 2398 break; 2399 } 2400 2401 if (error && ws_get_param) 2402 error = ws_get_param(dp); 2403 2404 return error; 2405 } 2406 2407 int 2408 wsdisplay_set_param(struct wsdisplay_softc *sc, struct wsdisplay_param *dp) 2409 { 2410 int error = ENXIO; 2411 int i; 2412 2413 if (sc != NULL) 2414 return wsdisplay_param(&sc->sc_dv, WSDISPLAYIO_SETPARAM, dp); 2415 2416 for (i = 0; i < wsdisplay_cd.cd_ndevs; i++) { 2417 sc = wsdisplay_cd.cd_devs[i]; 2418 if (sc == NULL) 2419 continue; 2420 error = wsdisplay_param(&sc->sc_dv, WSDISPLAYIO_SETPARAM, dp); 2421 if (error == 0) 2422 break; 2423 } 2424 2425 if (error && ws_set_param) 2426 error = ws_set_param(dp); 2427 2428 return error; 2429 } 2430 2431 void 2432 wsdisplay_brightness_step(struct device *dev, int dir) 2433 { 2434 struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev; 2435 struct wsdisplay_param dp; 2436 int delta, new; 2437 2438 dp.param = WSDISPLAYIO_PARAM_BRIGHTNESS; 2439 if (wsdisplay_get_param(sc, &dp)) 2440 return; 2441 2442 /* Use a step size of approximately 5%. */ 2443 delta = max(1, ((dp.max - dp.min) * 5) / 100); 2444 new = dp.curval; 2445 2446 if (dir > 0) { 2447 if (delta > dp.max - dp.curval) 2448 new = dp.max; 2449 else 2450 new += delta; 2451 } else if (dir < 0) { 2452 if (delta > dp.curval - dp.min) 2453 new = dp.min; 2454 else 2455 new -= delta; 2456 } 2457 2458 if (dp.curval == new) 2459 return; 2460 2461 dp.curval = new; 2462 wsdisplay_set_param(sc, &dp); 2463 } 2464 2465 void 2466 wsdisplay_brightness_zero(struct device *dev) 2467 { 2468 struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev; 2469 struct wsdisplay_param dp; 2470 2471 dp.param = WSDISPLAYIO_PARAM_BRIGHTNESS; 2472 if (wsdisplay_get_param(sc, &dp)) 2473 return; 2474 2475 dp.curval = dp.min; 2476 wsdisplay_set_param(sc, &dp); 2477 } 2478 2479 void 2480 wsdisplay_brightness_cycle(struct device *dev) 2481 { 2482 struct wsdisplay_softc *sc = (struct wsdisplay_softc *)dev; 2483 struct wsdisplay_param dp; 2484 2485 dp.param = WSDISPLAYIO_PARAM_BRIGHTNESS; 2486 if (wsdisplay_get_param(sc, &dp)) 2487 return; 2488 2489 if (dp.curval == dp.max) 2490 wsdisplay_brightness_zero(dev); 2491 else 2492 wsdisplay_brightness_step(dev, 1); 2493 } 2494 2495 #ifdef HAVE_WSMOUSED_SUPPORT 2496 /* 2497 * wsmoused(8) support functions 2498 */ 2499 2500 /* 2501 * Main function, called from wsdisplay_cfg_ioctl. 2502 */ 2503 int 2504 wsmoused(struct wsdisplay_softc *sc, caddr_t data, int flag, struct proc *p) 2505 { 2506 struct wscons_event mouse_event = *(struct wscons_event *)data; 2507 2508 if (IS_MOTION_EVENT(mouse_event.type)) { 2509 if (sc->sc_focus != NULL) 2510 motion_event(sc->sc_focus, mouse_event.type, 2511 mouse_event.value); 2512 return 0; 2513 } 2514 if (IS_BUTTON_EVENT(mouse_event.type)) { 2515 if (sc->sc_focus != NULL) { 2516 /* XXX tv_sec contains the number of clicks */ 2517 if (mouse_event.type == 2518 WSCONS_EVENT_MOUSE_DOWN) { 2519 button_event(sc->sc_focus, 2520 mouse_event.value, 2521 mouse_event.time.tv_sec); 2522 } else 2523 button_event(sc->sc_focus, 2524 mouse_event.value, 0); 2525 } 2526 return (0); 2527 } 2528 if (IS_CTRL_EVENT(mouse_event.type)) { 2529 return ctrl_event(sc, mouse_event.type, 2530 mouse_event.value, p); 2531 } 2532 return -1; 2533 } 2534 2535 /* 2536 * Mouse motion events 2537 */ 2538 void 2539 motion_event(struct wsscreen *scr, u_int type, int value) 2540 { 2541 switch (type) { 2542 case WSCONS_EVENT_MOUSE_DELTA_X: 2543 mouse_moverel(scr, value, 0); 2544 break; 2545 case WSCONS_EVENT_MOUSE_DELTA_Y: 2546 mouse_moverel(scr, 0, -value); 2547 break; 2548 #ifdef HAVE_SCROLLBACK_SUPPORT 2549 case WSCONS_EVENT_MOUSE_DELTA_Z: 2550 mouse_zaxis(scr, value); 2551 break; 2552 #endif 2553 default: 2554 break; 2555 } 2556 } 2557 2558 /* 2559 * Button clicks events 2560 */ 2561 void 2562 button_event(struct wsscreen *scr, int button, int clicks) 2563 { 2564 switch (button) { 2565 case MOUSE_COPY_BUTTON: 2566 switch (clicks % 4) { 2567 case 0: /* button is up */ 2568 mouse_copy_end(scr); 2569 mouse_copy_selection(scr); 2570 break; 2571 case 1: /* single click */ 2572 mouse_copy_start(scr); 2573 mouse_copy_selection(scr); 2574 break; 2575 case 2: /* double click */ 2576 mouse_copy_word(scr); 2577 mouse_copy_selection(scr); 2578 break; 2579 case 3: /* triple click */ 2580 mouse_copy_line(scr); 2581 mouse_copy_selection(scr); 2582 break; 2583 } 2584 break; 2585 case MOUSE_PASTE_BUTTON: 2586 if (clicks != 0) 2587 mouse_paste(scr); 2588 break; 2589 case MOUSE_EXTEND_BUTTON: 2590 if (clicks != 0) 2591 mouse_copy_extend_after(scr); 2592 break; 2593 default: 2594 break; 2595 } 2596 } 2597 2598 /* 2599 * Control events 2600 */ 2601 int 2602 ctrl_event(struct wsdisplay_softc *sc, u_int type, int value, struct proc *p) 2603 { 2604 struct wsscreen *scr; 2605 int i; 2606 2607 switch (type) { 2608 case WSCONS_EVENT_WSMOUSED_OFF: 2609 CLR(sc->sc_flags, SC_PASTE_AVAIL); 2610 return (0); 2611 case WSCONS_EVENT_WSMOUSED_ON: 2612 if (!sc->sc_accessops->getchar) 2613 /* no wsmoused(8) support in the display driver */ 2614 return (1); 2615 allocate_copybuffer(sc); 2616 CLR(sc->sc_flags, SC_PASTE_AVAIL); 2617 2618 for (i = 0 ; i < WSDISPLAY_DEFAULTSCREENS ; i++) 2619 if ((scr = sc->sc_scr[i]) != NULL) { 2620 scr->mouse = 2621 (WS_NCOLS(scr) * WS_NROWS(scr)) / 2; 2622 scr->cursor = scr->mouse; 2623 scr->cpy_start = 0; 2624 scr->cpy_end = 0; 2625 scr->orig_start = 0; 2626 scr->orig_end = 0; 2627 scr->mouse_flags = 0; 2628 } 2629 return (0); 2630 default: /* can't happen, really */ 2631 return 0; 2632 } 2633 } 2634 2635 void 2636 mouse_moverel(struct wsscreen *scr, int dx, int dy) 2637 { 2638 struct wsscreen_internal *dconf = scr->scr_dconf; 2639 u_int old_mouse = scr->mouse; 2640 int mouse_col = scr->mouse % N_COLS(dconf); 2641 int mouse_row = scr->mouse / N_COLS(dconf); 2642 2643 /* update position */ 2644 if (mouse_col + dx >= MAXCOL(dconf)) 2645 mouse_col = MAXCOL(dconf); 2646 else { 2647 if (mouse_col + dx <= 0) 2648 mouse_col = 0; 2649 else 2650 mouse_col += dx; 2651 } 2652 if (mouse_row + dy >= MAXROW(dconf)) 2653 mouse_row = MAXROW(dconf); 2654 else { 2655 if (mouse_row + dy <= 0) 2656 mouse_row = 0; 2657 else 2658 mouse_row += dy; 2659 } 2660 scr->mouse = mouse_row * N_COLS(dconf) + mouse_col; 2661 2662 /* if we have moved */ 2663 if (old_mouse != scr->mouse) { 2664 /* XXX unblank screen if display.ms_act */ 2665 if (ISSET(scr->mouse_flags, SEL_IN_PROGRESS)) { 2666 /* selection in progress */ 2667 mouse_copy_extend(scr); 2668 } else { 2669 inverse_char(scr, scr->mouse); 2670 if (ISSET(scr->mouse_flags, MOUSE_VISIBLE)) 2671 inverse_char(scr, old_mouse); 2672 else 2673 SET(scr->mouse_flags, MOUSE_VISIBLE); 2674 } 2675 } 2676 } 2677 2678 void 2679 inverse_char(struct wsscreen *scr, u_int pos) 2680 { 2681 struct wsscreen_internal *dconf = scr->scr_dconf; 2682 struct wsdisplay_charcell cell; 2683 int fg, bg, ul; 2684 int flags; 2685 int tmp; 2686 uint32_t attr; 2687 2688 GETCHAR(scr, pos, &cell); 2689 2690 (*dconf->emulops->unpack_attr)(dconf->emulcookie, cell.attr, &fg, 2691 &bg, &ul); 2692 2693 /* 2694 * Display the mouse cursor as a color inverted cell whenever 2695 * possible. If this is not possible, ask for the video reverse 2696 * attribute. 2697 */ 2698 flags = 0; 2699 if (dconf->scrdata->capabilities & WSSCREEN_WSCOLORS) { 2700 flags |= WSATTR_WSCOLORS; 2701 tmp = fg; 2702 fg = bg; 2703 bg = tmp; 2704 } else if (dconf->scrdata->capabilities & WSSCREEN_REVERSE) { 2705 flags |= WSATTR_REVERSE; 2706 } 2707 if ((*dconf->emulops->pack_attr)(dconf->emulcookie, fg, bg, flags | 2708 (ul ? WSATTR_UNDERLINE : 0), &attr) == 0) { 2709 cell.attr = attr; 2710 PUTCHAR(dconf, pos, cell.uc, cell.attr); 2711 } 2712 } 2713 2714 void 2715 inverse_region(struct wsscreen *scr, u_int start, u_int end) 2716 { 2717 struct wsscreen_internal *dconf = scr->scr_dconf; 2718 u_int current_pos; 2719 u_int abs_end; 2720 2721 /* sanity check, useful because 'end' can be (u_int)-1 */ 2722 abs_end = N_COLS(dconf) * N_ROWS(dconf); 2723 if (end > abs_end) 2724 return; 2725 current_pos = start; 2726 while (current_pos <= end) 2727 inverse_char(scr, current_pos++); 2728 } 2729 2730 /* 2731 * Return the number of contiguous blank characters between the right margin 2732 * if border == 1 or between the next non-blank character and the current mouse 2733 * cursor if border == 0 2734 */ 2735 u_int 2736 skip_spc_right(struct wsscreen *scr, int border) 2737 { 2738 struct wsscreen_internal *dconf = scr->scr_dconf; 2739 struct wsdisplay_charcell cell; 2740 u_int current = scr->cpy_end; 2741 u_int mouse_col = scr->cpy_end % N_COLS(dconf); 2742 u_int limit = current + (N_COLS(dconf) - mouse_col - 1); 2743 u_int res = 0; 2744 2745 while (GETCHAR(scr, current, &cell) == 0 && cell.uc == ' ' && 2746 current <= limit) { 2747 current++; 2748 res++; 2749 } 2750 if (border == BORDER) { 2751 if (current > limit) 2752 return (res - 1); 2753 else 2754 return (0); 2755 } else { 2756 if (res != 0) 2757 return (res - 1); 2758 else 2759 return (res); 2760 } 2761 } 2762 2763 /* 2764 * Return the number of contiguous blank characters between the first of the 2765 * contiguous blank characters and the current mouse cursor 2766 */ 2767 u_int 2768 skip_spc_left(struct wsscreen *scr) 2769 { 2770 struct wsscreen_internal *dconf = scr->scr_dconf; 2771 struct wsdisplay_charcell cell; 2772 u_int current = scr->cpy_start; 2773 u_int mouse_col = scr->mouse % N_COLS(dconf); 2774 u_int limit = current - mouse_col; 2775 u_int res = 0; 2776 2777 while (GETCHAR(scr, current, &cell) == 0 && cell.uc == ' ' && 2778 current >= limit) { 2779 current--; 2780 res++; 2781 } 2782 if (res != 0) 2783 res--; 2784 return (res); 2785 } 2786 2787 /* 2788 * Class of characters 2789 * Stolen from xterm sources of the Xfree project (see cvs tag below) 2790 * $TOG: button.c /main/76 1997/07/30 16:56:19 kaleb $ 2791 */ 2792 static const int charClass[256] = { 2793 /* NUL SOH STX ETX EOT ENQ ACK BEL */ 2794 32, 1, 1, 1, 1, 1, 1, 1, 2795 /* BS HT NL VT NP CR SO SI */ 2796 1, 32, 1, 1, 1, 1, 1, 1, 2797 /* DLE DC1 DC2 DC3 DC4 NAK SYN ETB */ 2798 1, 1, 1, 1, 1, 1, 1, 1, 2799 /* CAN EM SUB ESC FS GS RS US */ 2800 1, 1, 1, 1, 1, 1, 1, 1, 2801 /* SP ! " # $ % & ' */ 2802 32, 33, 34, 35, 36, 37, 38, 39, 2803 /* ( ) * + , - . / */ 2804 40, 41, 42, 43, 44, 45, 46, 47, 2805 /* 0 1 2 3 4 5 6 7 */ 2806 48, 48, 48, 48, 48, 48, 48, 48, 2807 /* 8 9 : ; < = > ? */ 2808 48, 48, 58, 59, 60, 61, 62, 63, 2809 /* @ A B C D E F G */ 2810 64, 48, 48, 48, 48, 48, 48, 48, 2811 /* H I J K L M N O */ 2812 48, 48, 48, 48, 48, 48, 48, 48, 2813 /* P Q R S T U V W */ 2814 48, 48, 48, 48, 48, 48, 48, 48, 2815 /* X Y Z [ \ ] ^ _ */ 2816 48, 48, 48, 91, 92, 93, 94, 48, 2817 /* ` a b c d e f g */ 2818 96, 48, 48, 48, 48, 48, 48, 48, 2819 /* h i j k l m n o */ 2820 48, 48, 48, 48, 48, 48, 48, 48, 2821 /* p q r s t u v w */ 2822 48, 48, 48, 48, 48, 48, 48, 48, 2823 /* x y z { | } ~ DEL */ 2824 48, 48, 48, 123, 124, 125, 126, 1, 2825 /* x80 x81 x82 x83 IND NEL SSA ESA */ 2826 1, 1, 1, 1, 1, 1, 1, 1, 2827 /* HTS HTJ VTS PLD PLU RI SS2 SS3 */ 2828 1, 1, 1, 1, 1, 1, 1, 1, 2829 /* DCS PU1 PU2 STS CCH MW SPA EPA */ 2830 1, 1, 1, 1, 1, 1, 1, 1, 2831 /* x98 x99 x9A CSI ST OSC PM APC */ 2832 1, 1, 1, 1, 1, 1, 1, 1, 2833 /* - i c/ L ox Y- | So */ 2834 160, 161, 162, 163, 164, 165, 166, 167, 2835 /* .. c0 ip << _ R0 - */ 2836 168, 169, 170, 171, 172, 173, 174, 175, 2837 /* o +- 2 3 ' u q| . */ 2838 176, 177, 178, 179, 180, 181, 182, 183, 2839 /* , 1 2 >> 1/4 1/2 3/4 ? */ 2840 184, 185, 186, 187, 188, 189, 190, 191, 2841 /* A` A' A^ A~ A: Ao AE C, */ 2842 48, 48, 48, 48, 48, 48, 48, 48, 2843 /* E` E' E^ E: I` I' I^ I: */ 2844 48, 48, 48, 48, 48, 48, 48, 48, 2845 /* D- N~ O` O' O^ O~ O: X */ 2846 48, 48, 48, 48, 48, 48, 48, 216, 2847 /* O/ U` U' U^ U: Y' P B */ 2848 48, 48, 48, 48, 48, 48, 48, 48, 2849 /* a` a' a^ a~ a: ao ae c, */ 2850 48, 48, 48, 48, 48, 48, 48, 48, 2851 /* e` e' e^ e: i` i' i^ i: */ 2852 48, 48, 48, 48, 48, 48, 48, 48, 2853 /* d n~ o` o' o^ o~ o: -: */ 2854 48, 48, 48, 48, 48, 48, 48, 248, 2855 /* o/ u` u' u^ u: y' P y: */ 2856 48, 48, 48, 48, 48, 48, 48, 48 2857 }; 2858 2859 /* 2860 * Find the first blank beginning after the current cursor position 2861 */ 2862 u_int 2863 skip_char_right(struct wsscreen *scr, u_int offset) 2864 { 2865 struct wsscreen_internal *dconf = scr->scr_dconf; 2866 struct wsdisplay_charcell cell; 2867 u_int current = offset; 2868 u_int limit = current + 2869 (N_COLS(dconf) - (scr->mouse % N_COLS(dconf)) - 1); 2870 u_int class; 2871 u_int res = 0; 2872 2873 GETCHAR(scr, current, &cell); 2874 class = charClass[cell.uc & 0xff]; 2875 while (GETCHAR(scr, current, &cell) == 0 && 2876 charClass[cell.uc & 0xff] == class && current <= limit) { 2877 current++; 2878 res++; 2879 } 2880 if (res != 0) 2881 res--; 2882 return (res); 2883 } 2884 2885 /* 2886 * Find the first non-blank character before the cursor position 2887 */ 2888 u_int 2889 skip_char_left(struct wsscreen *scr, u_int offset) 2890 { 2891 struct wsscreen_internal *dconf = scr->scr_dconf; 2892 struct wsdisplay_charcell cell; 2893 u_int current = offset; 2894 u_int limit = current - (scr->mouse % N_COLS(dconf)); 2895 u_int class; 2896 u_int res = 0; 2897 2898 GETCHAR(scr, current, &cell); 2899 class = charClass[cell.uc & 0xff]; 2900 while (GETCHAR(scr, current, &cell) == 0 && 2901 charClass[cell.uc & 0xff] == class && current >= limit) { 2902 current--; 2903 res++; 2904 } 2905 if (res != 0) 2906 res--; 2907 return (res); 2908 } 2909 2910 /* 2911 * Compare character classes 2912 */ 2913 u_int 2914 class_cmp(struct wsscreen *scr, u_int first, u_int second) 2915 { 2916 struct wsdisplay_charcell cell; 2917 u_int first_class; 2918 u_int second_class; 2919 2920 if (GETCHAR(scr, first, &cell) != 0) 2921 return (1); 2922 first_class = charClass[cell.uc & 0xff]; 2923 if (GETCHAR(scr, second, &cell) != 0) 2924 return (1); 2925 second_class = charClass[cell.uc & 0xff]; 2926 2927 if (first_class != second_class) 2928 return (1); 2929 else 2930 return (0); 2931 } 2932 2933 /* 2934 * Beginning of a copy operation 2935 */ 2936 void 2937 mouse_copy_start(struct wsscreen *scr) 2938 { 2939 u_int right; 2940 2941 /* if no selection, then that's the first one */ 2942 SET(scr->sc->sc_flags, SC_PASTE_AVAIL); 2943 2944 /* remove the previous selection */ 2945 if (ISSET(scr->mouse_flags, SEL_EXISTS)) 2946 remove_selection(scr); 2947 2948 /* initial show of the cursor */ 2949 if (!ISSET(scr->mouse_flags, MOUSE_VISIBLE)) 2950 inverse_char(scr, scr->mouse); 2951 2952 scr->cpy_start = scr->cpy_end = scr->mouse; 2953 scr->orig_start = scr->cpy_start; 2954 scr->orig_end = scr->cpy_end; 2955 scr->cursor = scr->cpy_end + 1; /* init value */ 2956 2957 /* useful later, in mouse_copy_extend */ 2958 right = skip_spc_right(scr, BORDER); 2959 if (right) 2960 SET(scr->mouse_flags, BLANK_TO_EOL); 2961 2962 SET(scr->mouse_flags, SEL_IN_PROGRESS | SEL_EXISTS | SEL_BY_CHAR); 2963 CLR(scr->mouse_flags, SEL_BY_WORD | SEL_BY_LINE); 2964 CLR(scr->mouse_flags, MOUSE_VISIBLE); /* cursor hidden in selection */ 2965 } 2966 2967 /* 2968 * Copy of the word under the cursor 2969 */ 2970 void 2971 mouse_copy_word(struct wsscreen *scr) 2972 { 2973 struct wsdisplay_charcell cell; 2974 u_int right; 2975 u_int left; 2976 2977 if (ISSET(scr->mouse_flags, SEL_EXISTS)) 2978 remove_selection(scr); 2979 2980 if (ISSET(scr->mouse_flags, MOUSE_VISIBLE)) 2981 inverse_char(scr, scr->mouse); 2982 2983 scr->cpy_start = scr->cpy_end = scr->mouse; 2984 2985 if (GETCHAR(scr, scr->mouse, &cell) == 0 && 2986 IS_ALPHANUM(cell.uc)) { 2987 right = skip_char_right(scr, scr->cpy_end); 2988 left = skip_char_left(scr, scr->cpy_start); 2989 } else { 2990 right = skip_spc_right(scr, NO_BORDER); 2991 left = skip_spc_left(scr); 2992 } 2993 2994 scr->cpy_start -= left; 2995 scr->cpy_end += right; 2996 scr->orig_start = scr->cpy_start; 2997 scr->orig_end = scr->cpy_end; 2998 scr->cursor = scr->cpy_end + 1; /* init value, never happen */ 2999 inverse_region(scr, scr->cpy_start, scr->cpy_end); 3000 3001 SET(scr->mouse_flags, SEL_IN_PROGRESS | SEL_EXISTS | SEL_BY_WORD); 3002 CLR(scr->mouse_flags, SEL_BY_CHAR | SEL_BY_LINE); 3003 /* mouse cursor hidden in the selection */ 3004 CLR(scr->mouse_flags, BLANK_TO_EOL | MOUSE_VISIBLE); 3005 } 3006 3007 /* 3008 * Copy of the current line 3009 */ 3010 void 3011 mouse_copy_line(struct wsscreen *scr) 3012 { 3013 struct wsscreen_internal *dconf = scr->scr_dconf; 3014 u_int row = scr->mouse / N_COLS(dconf); 3015 3016 if (ISSET(scr->mouse_flags, SEL_EXISTS)) 3017 remove_selection(scr); 3018 3019 if (ISSET(scr->mouse_flags, MOUSE_VISIBLE)) 3020 inverse_char(scr, scr->mouse); 3021 3022 scr->cpy_start = row * N_COLS(dconf); 3023 scr->cpy_end = scr->cpy_start + (N_COLS(dconf) - 1); 3024 scr->orig_start = scr->cpy_start; 3025 scr->orig_end = scr->cpy_end; 3026 scr->cursor = scr->cpy_end + 1; 3027 inverse_region(scr, scr->cpy_start, scr->cpy_end); 3028 3029 SET(scr->mouse_flags, SEL_IN_PROGRESS | SEL_EXISTS | SEL_BY_LINE); 3030 CLR(scr->mouse_flags, SEL_BY_CHAR | SEL_BY_WORD); 3031 /* mouse cursor hidden in the selection */ 3032 CLR(scr->mouse_flags, BLANK_TO_EOL | MOUSE_VISIBLE); 3033 } 3034 3035 /* 3036 * End of a copy operation 3037 */ 3038 void 3039 mouse_copy_end(struct wsscreen *scr) 3040 { 3041 CLR(scr->mouse_flags, SEL_IN_PROGRESS); 3042 if (ISSET(scr->mouse_flags, SEL_BY_WORD) || 3043 ISSET(scr->mouse_flags, SEL_BY_LINE)) { 3044 if (scr->cursor != scr->cpy_end + 1) 3045 inverse_char(scr, scr->cursor); 3046 scr->cursor = scr->cpy_end + 1; 3047 } 3048 } 3049 3050 3051 /* 3052 * Generic selection extend function 3053 */ 3054 void 3055 mouse_copy_extend(struct wsscreen *scr) 3056 { 3057 if (ISSET(scr->mouse_flags, SEL_BY_CHAR)) 3058 mouse_copy_extend_char(scr); 3059 if (ISSET(scr->mouse_flags, SEL_BY_WORD)) 3060 mouse_copy_extend_word(scr); 3061 if (ISSET(scr->mouse_flags, SEL_BY_LINE)) 3062 mouse_copy_extend_line(scr); 3063 } 3064 3065 /* 3066 * Extend a selected region, character by character 3067 */ 3068 void 3069 mouse_copy_extend_char(struct wsscreen *scr) 3070 { 3071 u_int right; 3072 3073 if (!ISSET(scr->mouse_flags, SEL_EXT_AFTER)) { 3074 if (ISSET(scr->mouse_flags, BLANK_TO_EOL)) { 3075 /* 3076 * First extension of selection. We handle special 3077 * cases of blank characters to eol 3078 */ 3079 3080 right = skip_spc_right(scr, BORDER); 3081 if (scr->mouse > scr->orig_start) { 3082 /* the selection goes to the lower part of 3083 the screen */ 3084 3085 /* remove the previous cursor, start of 3086 selection is now next line */ 3087 inverse_char(scr, scr->cpy_start); 3088 scr->cpy_start += (right + 1); 3089 scr->cpy_end = scr->cpy_start; 3090 scr->orig_start = scr->cpy_start; 3091 /* simulate the initial mark */ 3092 inverse_char(scr, scr->cpy_start); 3093 } else { 3094 /* the selection goes to the upper part 3095 of the screen */ 3096 /* remove the previous cursor, start of 3097 selection is now at the eol */ 3098 inverse_char(scr, scr->cpy_start); 3099 scr->orig_start += (right + 1); 3100 scr->cpy_start = scr->orig_start - 1; 3101 scr->cpy_end = scr->orig_start - 1; 3102 /* simulate the initial mark */ 3103 inverse_char(scr, scr->cpy_start); 3104 } 3105 CLR(scr->mouse_flags, BLANK_TO_EOL); 3106 } 3107 3108 if (scr->mouse < scr->orig_start && 3109 scr->cpy_end >= scr->orig_start) { 3110 /* we go to the upper part of the screen */ 3111 3112 /* reverse the old selection region */ 3113 remove_selection(scr); 3114 scr->cpy_end = scr->orig_start - 1; 3115 scr->cpy_start = scr->orig_start; 3116 } 3117 if (scr->cpy_start < scr->orig_start && 3118 scr->mouse >= scr->orig_start) { 3119 /* we go to the lower part of the screen */ 3120 3121 /* reverse the old selection region */ 3122 3123 remove_selection(scr); 3124 scr->cpy_start = scr->orig_start; 3125 scr->cpy_end = scr->orig_start - 1; 3126 } 3127 /* restore flags cleared in remove_selection() */ 3128 SET(scr->mouse_flags, SEL_IN_PROGRESS | SEL_EXISTS); 3129 } 3130 3131 if (scr->mouse >= scr->orig_start) { 3132 /* lower part of the screen */ 3133 if (scr->mouse > scr->cpy_end) { 3134 /* extending selection */ 3135 inverse_region(scr, scr->cpy_end + 1, scr->mouse); 3136 } else { 3137 /* reducing selection */ 3138 inverse_region(scr, scr->mouse + 1, scr->cpy_end); 3139 } 3140 scr->cpy_end = scr->mouse; 3141 } else { 3142 /* upper part of the screen */ 3143 if (scr->mouse < scr->cpy_start) { 3144 /* extending selection */ 3145 inverse_region(scr, scr->mouse, scr->cpy_start - 1); 3146 } else { 3147 /* reducing selection */ 3148 inverse_region(scr, scr->cpy_start, scr->mouse - 1); 3149 } 3150 scr->cpy_start = scr->mouse; 3151 } 3152 } 3153 3154 /* 3155 * Extend a selected region, word by word 3156 */ 3157 void 3158 mouse_copy_extend_word(struct wsscreen *scr) 3159 { 3160 u_int old_cpy_end; 3161 u_int old_cpy_start; 3162 3163 if (!ISSET(scr->mouse_flags, SEL_EXT_AFTER)) { 3164 /* remove cursor in selection (black one) */ 3165 if (scr->cursor != scr->cpy_end + 1) 3166 inverse_char(scr, scr->cursor); 3167 3168 /* now, switch between lower and upper part of the screen */ 3169 if (scr->mouse < scr->orig_start && 3170 scr->cpy_end >= scr->orig_start) { 3171 /* going to the upper part of the screen */ 3172 inverse_region(scr, scr->orig_end + 1, scr->cpy_end); 3173 scr->cpy_end = scr->orig_end; 3174 } 3175 3176 if (scr->mouse > scr->orig_end && 3177 scr->cpy_start <= scr->orig_start) { 3178 /* going to the lower part of the screen */ 3179 inverse_region(scr, scr->cpy_start, 3180 scr->orig_start - 1); 3181 scr->cpy_start = scr->orig_start; 3182 } 3183 } 3184 3185 if (scr->mouse >= scr->orig_start) { 3186 /* lower part of the screen */ 3187 if (scr->mouse > scr->cpy_end) { 3188 /* extending selection */ 3189 old_cpy_end = scr->cpy_end; 3190 scr->cpy_end = scr->mouse + 3191 skip_char_right(scr, scr->mouse); 3192 inverse_region(scr, old_cpy_end + 1, scr->cpy_end); 3193 } else { 3194 if (class_cmp(scr, scr->mouse, scr->mouse + 1)) { 3195 /* reducing selection (remove last word) */ 3196 old_cpy_end = scr->cpy_end; 3197 scr->cpy_end = scr->mouse; 3198 inverse_region(scr, scr->cpy_end + 1, 3199 old_cpy_end); 3200 } else { 3201 old_cpy_end = scr->cpy_end; 3202 scr->cpy_end = scr->mouse + 3203 skip_char_right(scr, scr->mouse); 3204 if (scr->cpy_end != old_cpy_end) { 3205 /* reducing selection, from the end of 3206 * next word */ 3207 inverse_region(scr, scr->cpy_end + 1, 3208 old_cpy_end); 3209 } 3210 } 3211 } 3212 } else { 3213 /* upper part of the screen */ 3214 if (scr->mouse < scr->cpy_start) { 3215 /* extending selection */ 3216 old_cpy_start = scr->cpy_start; 3217 scr->cpy_start = scr->mouse - 3218 skip_char_left(scr, scr->mouse); 3219 inverse_region(scr, scr->cpy_start, old_cpy_start - 1); 3220 } else { 3221 if (class_cmp(scr, scr->mouse - 1, scr->mouse)) { 3222 /* reducing selection (remove last word) */ 3223 old_cpy_start = scr->cpy_start; 3224 scr->cpy_start = scr->mouse; 3225 inverse_region(scr, old_cpy_start, 3226 scr->cpy_start - 1); 3227 } else { 3228 old_cpy_start = scr->cpy_start; 3229 scr->cpy_start = scr->mouse - 3230 skip_char_left(scr, scr->mouse); 3231 if (scr->cpy_start != old_cpy_start) { 3232 inverse_region(scr, old_cpy_start, 3233 scr->cpy_start - 1); 3234 } 3235 } 3236 } 3237 } 3238 3239 if (!ISSET(scr->mouse_flags, SEL_EXT_AFTER)) { 3240 /* display new cursor */ 3241 scr->cursor = scr->mouse; 3242 inverse_char(scr, scr->cursor); 3243 } 3244 } 3245 3246 /* 3247 * Extend a selected region, line by line 3248 */ 3249 void 3250 mouse_copy_extend_line(struct wsscreen *scr) 3251 { 3252 struct wsscreen_internal *dconf = scr->scr_dconf; 3253 u_int old_row; 3254 u_int new_row; 3255 u_int old_cpy_start; 3256 u_int old_cpy_end; 3257 3258 if (!ISSET(scr->mouse_flags, SEL_EXT_AFTER)) { 3259 /* remove cursor in selection (black one) */ 3260 if (scr->cursor != scr->cpy_end + 1) 3261 inverse_char(scr, scr->cursor); 3262 3263 /* now, switch between lower and upper part of the screen */ 3264 if (scr->mouse < scr->orig_start && 3265 scr->cpy_end >= scr->orig_start) { 3266 /* going to the upper part of the screen */ 3267 inverse_region(scr, scr->orig_end + 1, scr->cpy_end); 3268 scr->cpy_end = scr->orig_end; 3269 } 3270 3271 if (scr->mouse > scr->orig_end && 3272 scr->cpy_start <= scr->orig_start) { 3273 /* going to the lower part of the screen */ 3274 inverse_region(scr, scr->cpy_start, 3275 scr->orig_start - 1); 3276 scr->cpy_start = scr->orig_start; 3277 } 3278 } 3279 3280 if (scr->mouse >= scr->orig_start) { 3281 /* lower part of the screen */ 3282 if (scr->cursor == scr->cpy_end + 1) 3283 scr->cursor = scr->cpy_end; 3284 old_row = scr->cursor / N_COLS(dconf); 3285 new_row = scr->mouse / N_COLS(dconf); 3286 old_cpy_end = scr->cpy_end; 3287 scr->cpy_end = new_row * N_COLS(dconf) + MAXCOL(dconf); 3288 if (new_row > old_row) 3289 inverse_region(scr, old_cpy_end + 1, scr->cpy_end); 3290 else if (new_row < old_row) 3291 inverse_region(scr, scr->cpy_end + 1, old_cpy_end); 3292 } else { 3293 /* upper part of the screen */ 3294 old_row = scr->cursor / N_COLS(dconf); 3295 new_row = scr->mouse / N_COLS(dconf); 3296 old_cpy_start = scr->cpy_start; 3297 scr->cpy_start = new_row * N_COLS(dconf); 3298 if (new_row < old_row) 3299 inverse_region(scr, scr->cpy_start, old_cpy_start - 1); 3300 else if (new_row > old_row) 3301 inverse_region(scr, old_cpy_start, scr->cpy_start - 1); 3302 } 3303 3304 if (!ISSET(scr->mouse_flags, SEL_EXT_AFTER)) { 3305 /* display new cursor */ 3306 scr->cursor = scr->mouse; 3307 inverse_char(scr, scr->cursor); 3308 } 3309 } 3310 3311 /* 3312 * Add an extension to a selected region, word by word 3313 */ 3314 void 3315 mouse_copy_extend_after(struct wsscreen *scr) 3316 { 3317 u_int start_dist; 3318 u_int end_dist; 3319 3320 if (ISSET(scr->mouse_flags, SEL_EXISTS)) { 3321 SET(scr->mouse_flags, SEL_EXT_AFTER); 3322 mouse_hide(scr); /* hide current cursor */ 3323 3324 if (scr->cpy_start > scr->mouse) 3325 start_dist = scr->cpy_start - scr->mouse; 3326 else 3327 start_dist = scr->mouse - scr->cpy_start; 3328 if (scr->mouse > scr->cpy_end) 3329 end_dist = scr->mouse - scr->cpy_end; 3330 else 3331 end_dist = scr->cpy_end - scr->mouse; 3332 if (start_dist < end_dist) { 3333 /* upper part of the screen*/ 3334 scr->orig_start = scr->mouse + 1; 3335 /* only used in mouse_copy_extend_line() */ 3336 scr->cursor = scr->cpy_start; 3337 } else { 3338 /* lower part of the screen */ 3339 scr->orig_start = scr->mouse; 3340 /* only used in mouse_copy_extend_line() */ 3341 scr->cursor = scr->cpy_end; 3342 } 3343 if (ISSET(scr->mouse_flags, SEL_BY_CHAR)) 3344 mouse_copy_extend_char(scr); 3345 if (ISSET(scr->mouse_flags, SEL_BY_WORD)) 3346 mouse_copy_extend_word(scr); 3347 if (ISSET(scr->mouse_flags, SEL_BY_LINE)) 3348 mouse_copy_extend_line(scr); 3349 mouse_copy_selection(scr); 3350 } 3351 } 3352 3353 void 3354 mouse_hide(struct wsscreen *scr) 3355 { 3356 if (ISSET(scr->mouse_flags, MOUSE_VISIBLE)) { 3357 inverse_char(scr, scr->mouse); 3358 CLR(scr->mouse_flags, MOUSE_VISIBLE); 3359 } 3360 } 3361 3362 /* 3363 * Remove a previously selected region 3364 */ 3365 void 3366 remove_selection(struct wsscreen *scr) 3367 { 3368 if (ISSET(scr->mouse_flags, SEL_EXT_AFTER)) { 3369 /* reset the flag indicating an extension of selection */ 3370 CLR(scr->mouse_flags, SEL_EXT_AFTER); 3371 } 3372 inverse_region(scr, scr->cpy_start, scr->cpy_end); 3373 CLR(scr->mouse_flags, SEL_IN_PROGRESS | SEL_EXISTS); 3374 } 3375 3376 /* 3377 * Put the current visual selection in the selection buffer 3378 */ 3379 void 3380 mouse_copy_selection(struct wsscreen *scr) 3381 { 3382 struct wsscreen_internal *dconf = scr->scr_dconf; 3383 struct wsdisplay_charcell cell; 3384 u_int current = 0; 3385 u_int blank = current; 3386 u_int buf_end = (N_COLS(dconf) + 1) * N_ROWS(dconf); 3387 u_int sel_cur; 3388 u_int sel_end; 3389 3390 sel_cur = scr->cpy_start; 3391 sel_end = scr->cpy_end; 3392 3393 while (sel_cur <= sel_end && current < buf_end - 1) { 3394 if (GETCHAR(scr, sel_cur, &cell) != 0) 3395 break; 3396 scr->sc->sc_copybuffer[current] = cell.uc; 3397 if (!IS_SPACE(cell.uc)) 3398 blank = current + 1; /* first blank after non-blank */ 3399 current++; 3400 if (sel_cur % N_COLS(dconf) == MAXCOL(dconf)) { 3401 /* 3402 * If we are on the last column of the screen, 3403 * insert a carriage return. 3404 */ 3405 scr->sc->sc_copybuffer[blank] = '\r'; 3406 current = ++blank; 3407 } 3408 sel_cur++; 3409 } 3410 3411 scr->sc->sc_copybuffer[current] = '\0'; 3412 } 3413 3414 /* 3415 * Paste the current selection 3416 */ 3417 void 3418 mouse_paste(struct wsscreen *scr) 3419 { 3420 char *current = scr->sc->sc_copybuffer; 3421 struct tty *tp; 3422 u_int len; 3423 3424 if (ISSET(scr->sc->sc_flags, SC_PASTE_AVAIL)) { 3425 if (!WSSCREEN_HAS_TTY(scr)) 3426 return; 3427 3428 tp = scr->scr_tty; 3429 for (len = strlen(scr->sc->sc_copybuffer); len != 0; len--) 3430 (*linesw[tp->t_line].l_rint)(*current++, tp); 3431 } 3432 } 3433 3434 #ifdef HAVE_SCROLLBACK_SUPPORT 3435 /* 3436 * Handle the z axis. 3437 * The z axis (roller or wheel) is mapped by default to scrollback. 3438 */ 3439 void 3440 mouse_zaxis(struct wsscreen *scr, int z) 3441 { 3442 if (z < 0) 3443 wsscrollback(scr->sc, WSDISPLAY_SCROLL_BACKWARD); 3444 else 3445 wsscrollback(scr->sc, WSDISPLAY_SCROLL_FORWARD); 3446 } 3447 #endif 3448 3449 /* 3450 * Allocate the copy buffer. The size is: 3451 * (cols + 1) * (rows) 3452 * (+1 for '\n' at the end of lines), 3453 * where cols and rows are the maximum of column and rows of all screens. 3454 */ 3455 void 3456 allocate_copybuffer(struct wsdisplay_softc *sc) 3457 { 3458 int nscreens = sc->sc_scrdata->nscreens; 3459 int i, s; 3460 const struct wsscreen_descr **screens_list = sc->sc_scrdata->screens; 3461 const struct wsscreen_descr *current; 3462 u_int size = sc->sc_copybuffer_size; 3463 3464 s = spltty(); 3465 for (i = 0; i < nscreens; i++) { 3466 current = *screens_list; 3467 if ((current->ncols + 1) * current->nrows > size) 3468 size = (current->ncols + 1) * current->nrows; 3469 screens_list++; 3470 } 3471 if (size != sc->sc_copybuffer_size && sc->sc_copybuffer_size != 0) { 3472 bzero(sc->sc_copybuffer, sc->sc_copybuffer_size); 3473 free(sc->sc_copybuffer, M_DEVBUF, sc->sc_copybuffer_size); 3474 } 3475 if ((sc->sc_copybuffer = (char *)malloc(size, M_DEVBUF, M_NOWAIT)) == 3476 NULL) { 3477 printf("%s: couldn't allocate copy buffer\n", 3478 sc->sc_dv.dv_xname); 3479 size = 0; 3480 } 3481 sc->sc_copybuffer_size = size; 3482 splx(s); 3483 } 3484 3485 /* Remove selection and cursor on current screen */ 3486 void 3487 mouse_remove(struct wsscreen *scr) 3488 { 3489 if (ISSET(scr->mouse_flags, SEL_EXISTS)) 3490 remove_selection(scr); 3491 3492 mouse_hide(scr); 3493 } 3494 3495 #endif /* HAVE_WSMOUSED_SUPPORT */ 3496