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