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 "sc.h" 33 #include "opt_syscons.h" 34 35 #if NSC > 0 36 37 #ifndef SC_NO_HISTORY 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/tty.h> 42 #include <sys/kernel.h> 43 #include <sys/malloc.h> 44 45 #include <machine/console.h> 46 47 #include <dev/syscons/syscons.h> 48 49 #if !defined(SC_MAX_HISTORY_SIZE) 50 #define SC_MAX_HISTORY_SIZE (1000 * MAXCONS * NSC) 51 #endif 52 53 #if !defined(SC_HISTORY_SIZE) 54 #define SC_HISTORY_SIZE (ROW * 4) 55 #endif 56 57 #if (SC_HISTORY_SIZE * MAXCONS * NSC) > SC_MAX_HISTORY_SIZE 58 #undef SC_MAX_HISTORY_SIZE 59 #define SC_MAX_HISTORY_SIZE (SC_HISTORY_SIZE * MAXCONS * NSC) 60 #endif 61 62 /* local variables */ 63 static int extra_history_size 64 = SC_MAX_HISTORY_SIZE - SC_HISTORY_SIZE*MAXCONS; 65 66 /* local functions */ 67 static void history_to_screen(scr_stat *scp); 68 69 /* allocate a history buffer */ 70 int 71 sc_alloc_history_buffer(scr_stat *scp, int lines, int prev_ysize, int wait) 72 { 73 /* 74 * syscons unconditionally allocates buffers upto 75 * SC_HISTORY_SIZE lines or scp->ysize lines, whichever 76 * is larger. A value greater than that is allowed, 77 * subject to extra_history_size. 78 */ 79 sc_vtb_t *history; 80 sc_vtb_t *prev_history; 81 int cur_lines; /* current buffer size */ 82 int min_lines; /* guaranteed buffer size */ 83 int delta; /* lines to put back */ 84 85 if (lines <= 0) 86 lines = SC_HISTORY_SIZE; /* use the default value */ 87 88 /* make it at least as large as the screen size */ 89 lines = imax(lines, scp->ysize); 90 91 /* remove the history buffer while we update it */ 92 history = prev_history = scp->history; 93 scp->history = NULL; 94 95 /* calculate the amount of lines to put back to extra_history_size */ 96 delta = 0; 97 if (prev_history) { 98 cur_lines = sc_vtb_rows(history); 99 min_lines = imax(SC_HISTORY_SIZE, prev_ysize); 100 if (cur_lines > min_lines) 101 delta = cur_lines - min_lines; 102 } 103 104 /* lines upto min_lines are always allowed. */ 105 min_lines = imax(SC_HISTORY_SIZE, scp->ysize); 106 if (lines > min_lines) { 107 if (lines - min_lines > extra_history_size + delta) { 108 /* too many lines are requested */ 109 scp->history = prev_history; 110 return EINVAL; 111 } 112 } 113 114 /* destroy the previous buffer and allocate a new one */ 115 if (prev_history == NULL) { 116 history = (sc_vtb_t *)malloc(sizeof(*history), 117 M_DEVBUF, 118 (wait) ? M_WAITOK : M_NOWAIT); 119 } else { 120 extra_history_size += delta; 121 sc_vtb_destroy(prev_history); 122 } 123 if (history != NULL) { 124 if (lines > min_lines) 125 extra_history_size -= lines - min_lines; 126 sc_vtb_init(history, VTB_RINGBUFFER, scp->xsize, lines, 127 NULL, wait); 128 } 129 130 scp->history_pos = 0; 131 scp->history = history; 132 133 return 0; 134 } 135 136 void 137 sc_free_history_buffer(scr_stat *scp, int prev_ysize) 138 { 139 sc_vtb_t *history; 140 int cur_lines; /* current buffer size */ 141 int min_lines; /* guaranteed buffer size */ 142 143 history = scp->history; 144 scp->history = NULL; 145 if (history == NULL) 146 return; 147 148 cur_lines = sc_vtb_rows(history); 149 min_lines = imax(SC_HISTORY_SIZE, prev_ysize); 150 extra_history_size += (cur_lines > min_lines) ? cur_lines - min_lines : 0; 151 152 sc_vtb_destroy(history); 153 free(history, M_DEVBUF); 154 } 155 156 /* copy entire screen into the top of the history buffer */ 157 void 158 sc_hist_save(scr_stat *scp) 159 { 160 sc_vtb_append(&scp->vtb, 0, scp->history, scp->xsize*scp->ysize); 161 scp->history_pos = sc_vtb_tail(scp->history); 162 } 163 164 /* restore the screen by copying from the history buffer */ 165 int 166 sc_hist_restore(scr_stat *scp) 167 { 168 int ret; 169 170 if (scp->history_pos != sc_vtb_tail(scp->history)) { 171 scp->history_pos = sc_vtb_tail(scp->history); 172 history_to_screen(scp); 173 ret = 0; 174 } else { 175 ret = 1; 176 } 177 sc_vtb_seek(scp->history, sc_vtb_pos(scp->history, 178 sc_vtb_tail(scp->history), 179 -scp->xsize*scp->ysize)); 180 return ret; 181 } 182 183 /* copy screen-full of saved lines */ 184 static void 185 history_to_screen(scr_stat *scp) 186 { 187 int pos; 188 int i; 189 190 pos = scp->history_pos; 191 for (i = 1; i <= scp->ysize; ++i) { 192 pos = sc_vtb_pos(scp->history, pos, -scp->xsize); 193 sc_vtb_copy(scp->history, pos, 194 &scp->vtb, scp->xsize*(scp->ysize - i), 195 scp->xsize); 196 } 197 mark_all(scp); 198 } 199 200 /* go to the tail of the history buffer */ 201 void 202 sc_hist_home(scr_stat *scp) 203 { 204 scp->history_pos = sc_vtb_tail(scp->history); 205 history_to_screen(scp); 206 } 207 208 /* go to the top of the history buffer */ 209 void 210 sc_hist_end(scr_stat *scp) 211 { 212 scp->history_pos = sc_vtb_pos(scp->history, sc_vtb_tail(scp->history), 213 scp->xsize*scp->ysize); 214 history_to_screen(scp); 215 } 216 217 /* move one line up */ 218 int 219 sc_hist_up_line(scr_stat *scp) 220 { 221 if (sc_vtb_pos(scp->history, scp->history_pos, -(scp->xsize*scp->ysize)) 222 == sc_vtb_tail(scp->history)) 223 return -1; 224 scp->history_pos = sc_vtb_pos(scp->history, scp->history_pos, 225 -scp->xsize); 226 history_to_screen(scp); 227 return 0; 228 } 229 230 /* move one line down */ 231 int 232 sc_hist_down_line(scr_stat *scp) 233 { 234 if (scp->history_pos == sc_vtb_tail(scp->history)) 235 return -1; 236 scp->history_pos = sc_vtb_pos(scp->history, scp->history_pos, 237 scp->xsize); 238 history_to_screen(scp); 239 return 0; 240 } 241 242 int 243 sc_hist_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, 244 struct proc *p) 245 { 246 scr_stat *scp; 247 int error; 248 249 switch (cmd) { 250 251 case CONS_HISTORY: /* set history size */ 252 scp = sc_get_scr_stat(tp->t_dev); 253 if (*(int *)data <= 0) 254 return EINVAL; 255 if (scp->status & BUFFER_SAVED) 256 return EBUSY; 257 DPRINTF(5, ("lines:%d, ysize:%d, pool:%d\n", 258 *(int *)data, scp->ysize, extra_history_size)); 259 error = sc_alloc_history_buffer(scp, 260 imax(*(int *)data, scp->ysize), 261 scp->ysize, TRUE); 262 DPRINTF(5, ("error:%d, rows:%d, pool:%d\n", error, 263 sc_vtb_rows(scp->history), extra_history_size)); 264 return error; 265 } 266 267 return ENOIOCTL; 268 } 269 270 #endif /* SC_NO_HISTORY */ 271 272 #endif /* NSC */ 273