1 /*- 2 * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp> 3 * Copyright (c) 1992-1998 S�ren Schmidt 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer, 11 * without modification, immediately at the beginning of the file. 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. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * 29 * $FreeBSD$ 30 */ 31 32 #include "opt_syscons.h" 33 34 #ifndef SC_NO_HISTORY 35 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/conf.h> 39 #include <sys/consio.h> 40 #include <sys/tty.h> 41 #include <sys/kernel.h> 42 #include <sys/malloc.h> 43 44 #include <machine/pc/display.h> 45 46 #include <dev/syscons/syscons.h> 47 48 /* 49 * XXX Placeholder. 50 * This calculations should be dynamically scaled by number of seperate sc 51 * devices. A base value of 'extra_history_size' should be defined for 52 * each syscons unit, and added and subtracted from the dynamic 53 * 'extra_history_size' as units are added and removed. This way, each time 54 * a new syscons unit goes online, extra_history_size is automatically bumped. 55 */ 56 #define MAXSC 1 57 58 #if !defined(SC_MAX_HISTORY_SIZE) 59 #define SC_MAX_HISTORY_SIZE (1000 * MAXCONS * MAXSC) 60 #endif 61 62 #if !defined(SC_HISTORY_SIZE) 63 #define SC_HISTORY_SIZE (ROW * 4) 64 #endif 65 66 #if (SC_HISTORY_SIZE * MAXCONS * MAXSC) > SC_MAX_HISTORY_SIZE 67 #undef SC_MAX_HISTORY_SIZE 68 #define SC_MAX_HISTORY_SIZE (SC_HISTORY_SIZE * MAXCONS * MAXSC) 69 #endif 70 71 /* local variables */ 72 static int extra_history_size 73 = SC_MAX_HISTORY_SIZE - SC_HISTORY_SIZE*MAXCONS; 74 75 /* local functions */ 76 static void copy_history(sc_vtb_t *from, sc_vtb_t *to); 77 static void history_to_screen(scr_stat *scp); 78 79 /* allocate a history buffer */ 80 int 81 sc_alloc_history_buffer(scr_stat *scp, int lines, int prev_ysize, int wait) 82 { 83 /* 84 * syscons unconditionally allocates buffers upto 85 * SC_HISTORY_SIZE lines or scp->ysize lines, whichever 86 * is larger. A value greater than that is allowed, 87 * subject to extra_history_size. 88 */ 89 sc_vtb_t *history; 90 sc_vtb_t *prev_history; 91 int cur_lines; /* current buffer size */ 92 int min_lines; /* guaranteed buffer size */ 93 int delta; /* lines to put back */ 94 95 if (lines <= 0) 96 lines = SC_HISTORY_SIZE; /* use the default value */ 97 98 /* make it at least as large as the screen size */ 99 lines = imax(lines, scp->ysize); 100 101 /* remove the history buffer while we update it */ 102 history = prev_history = scp->history; 103 scp->history = NULL; 104 105 /* calculate the amount of lines to put back to extra_history_size */ 106 delta = 0; 107 if (prev_history) { 108 cur_lines = sc_vtb_rows(history); 109 min_lines = imax(SC_HISTORY_SIZE, prev_ysize); 110 if (cur_lines > min_lines) 111 delta = cur_lines - min_lines; 112 } 113 114 /* lines upto min_lines are always allowed. */ 115 min_lines = imax(SC_HISTORY_SIZE, scp->ysize); 116 if (lines > min_lines) { 117 if (lines - min_lines > extra_history_size + delta) { 118 /* too many lines are requested */ 119 scp->history = prev_history; 120 return EINVAL; 121 } 122 } 123 124 /* allocate a new buffer */ 125 history = (sc_vtb_t *)malloc(sizeof(*history), 126 M_DEVBUF, 127 (wait) ? M_WAITOK : M_NOWAIT); 128 if (history != NULL) { 129 if (lines > min_lines) 130 extra_history_size -= lines - min_lines; 131 /* XXX error check? */ 132 sc_vtb_init(history, VTB_RINGBUFFER, scp->xsize, lines, 133 NULL, wait); 134 /* FIXME: XXX no good? */ 135 sc_vtb_clear(history, scp->sc->scr_map[0x20], 136 SC_NORM_ATTR << 8); 137 if (prev_history != NULL) 138 copy_history(prev_history, history); 139 scp->history_pos = sc_vtb_tail(history); 140 } else { 141 scp->history_pos = 0; 142 } 143 144 /* destroy the previous buffer */ 145 if (prev_history != NULL) { 146 extra_history_size += delta; 147 sc_vtb_destroy(prev_history); 148 free(prev_history, M_DEVBUF); 149 } 150 151 scp->history = history; 152 153 return 0; 154 } 155 156 static void 157 copy_history(sc_vtb_t *from, sc_vtb_t *to) 158 { 159 int lines; 160 int cols; 161 int cols1; 162 int cols2; 163 int pos; 164 int i; 165 166 lines = sc_vtb_rows(from); 167 cols1 = sc_vtb_cols(from); 168 cols2 = sc_vtb_cols(to); 169 cols = imin(cols1, cols2); 170 pos = sc_vtb_tail(from); 171 for (i = 0; i < lines; ++i) { 172 sc_vtb_append(from, pos, to, cols); 173 if (cols < cols2) 174 sc_vtb_seek(to, sc_vtb_pos(to, 175 sc_vtb_tail(to), 176 cols2 - cols)); 177 pos = sc_vtb_pos(from, pos, cols1); 178 } 179 } 180 181 void 182 sc_free_history_buffer(scr_stat *scp, int prev_ysize) 183 { 184 sc_vtb_t *history; 185 int cur_lines; /* current buffer size */ 186 int min_lines; /* guaranteed buffer size */ 187 188 history = scp->history; 189 scp->history = NULL; 190 if (history == NULL) 191 return; 192 193 cur_lines = sc_vtb_rows(history); 194 min_lines = imax(SC_HISTORY_SIZE, prev_ysize); 195 extra_history_size += (cur_lines > min_lines) ? 196 cur_lines - min_lines : 0; 197 198 sc_vtb_destroy(history); 199 free(history, M_DEVBUF); 200 } 201 202 /* copy entire screen into the top of the history buffer */ 203 void 204 sc_hist_save(scr_stat *scp) 205 { 206 sc_vtb_append(&scp->vtb, 0, scp->history, scp->xsize*scp->ysize); 207 scp->history_pos = sc_vtb_tail(scp->history); 208 } 209 210 /* restore the screen by copying from the history buffer */ 211 int 212 sc_hist_restore(scr_stat *scp) 213 { 214 int ret; 215 216 if (scp->history_pos != sc_vtb_tail(scp->history)) { 217 scp->history_pos = sc_vtb_tail(scp->history); 218 history_to_screen(scp); 219 ret = 0; 220 } else { 221 ret = 1; 222 } 223 sc_vtb_seek(scp->history, sc_vtb_pos(scp->history, 224 sc_vtb_tail(scp->history), 225 -scp->xsize*scp->ysize)); 226 return ret; 227 } 228 229 /* copy screen-full of saved lines */ 230 static void 231 history_to_screen(scr_stat *scp) 232 { 233 int pos; 234 int i; 235 236 pos = scp->history_pos; 237 for (i = 1; i <= scp->ysize; ++i) { 238 pos = sc_vtb_pos(scp->history, pos, -scp->xsize); 239 sc_vtb_copy(scp->history, pos, 240 &scp->vtb, scp->xsize*(scp->ysize - i), 241 scp->xsize); 242 } 243 mark_all(scp); 244 } 245 246 /* go to the tail of the history buffer */ 247 void 248 sc_hist_home(scr_stat *scp) 249 { 250 scp->history_pos = sc_vtb_tail(scp->history); 251 history_to_screen(scp); 252 } 253 254 /* go to the top of the history buffer */ 255 void 256 sc_hist_end(scr_stat *scp) 257 { 258 scp->history_pos = sc_vtb_pos(scp->history, sc_vtb_tail(scp->history), 259 scp->xsize*scp->ysize); 260 history_to_screen(scp); 261 } 262 263 /* move one line up */ 264 int 265 sc_hist_up_line(scr_stat *scp) 266 { 267 if (sc_vtb_pos(scp->history, scp->history_pos, -(scp->xsize*scp->ysize)) 268 == sc_vtb_tail(scp->history)) 269 return -1; 270 scp->history_pos = sc_vtb_pos(scp->history, scp->history_pos, 271 -scp->xsize); 272 history_to_screen(scp); 273 return 0; 274 } 275 276 /* move one line down */ 277 int 278 sc_hist_down_line(scr_stat *scp) 279 { 280 if (scp->history_pos == sc_vtb_tail(scp->history)) 281 return -1; 282 scp->history_pos = sc_vtb_pos(scp->history, scp->history_pos, 283 scp->xsize); 284 history_to_screen(scp); 285 return 0; 286 } 287 288 int 289 sc_hist_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, 290 struct thread *td) 291 { 292 scr_stat *scp; 293 int error; 294 295 switch (cmd) { 296 297 case CONS_HISTORY: /* set history size */ 298 scp = SC_STAT(tp->t_dev); 299 if (*(int *)data <= 0) 300 return EINVAL; 301 if (scp->status & BUFFER_SAVED) 302 return EBUSY; 303 DPRINTF(5, ("lines:%d, ysize:%d, pool:%d\n", 304 *(int *)data, scp->ysize, extra_history_size)); 305 error = sc_alloc_history_buffer(scp, 306 imax(*(int *)data, scp->ysize), 307 scp->ysize, TRUE); 308 DPRINTF(5, ("error:%d, rows:%d, pool:%d\n", error, 309 sc_vtb_rows(scp->history), extra_history_size)); 310 return error; 311 312 case CONS_CLRHIST: 313 scp = SC_STAT(tp->t_dev); 314 sc_vtb_clear(scp->history, scp->sc->scr_map[0x20], 315 SC_NORM_ATTR << 8); 316 return 0; 317 } 318 319 return ENOIOCTL; 320 } 321 322 #endif /* SC_NO_HISTORY */ 323