2 * resizecons.c - change console video mode
8 * 1. Get svgalib, make restoretextmode, put it somewhere in your path.
9 * 2. Put vga=ask in /etc/lilo/config, boot your machine a number of times,
10 * each time with a different vga mode, and run the command
11 * "restoretextmode -w COLSxROWS".
12 * For me this resulted in the files 80x25, 80x28, 80x50, 80x60, 100x40,
13 * 132x25, 132x28, 132x44. Put these files in /usr/lib/kbd/videomodes
14 * (or in your current dir).
15 * 3. Now "resizecons COLSxROWS" will change your video mode. (Assuming you
16 * have an appropriate kernel, and svgalib works for your video card.)
18 * Note: this is experimental, but it works for me. Comments are welcome.
19 * You may have to be root to get the appropriate ioperm permissions.
20 * It is not safe to make this program suid root.
24 * Harm Hanemaaijer added the -lines option, which reprograms the
25 * number of scanlines. He writes:
27 * Added -lines option, which reprograms the number of scanlines and
28 * the font height of the VGA hardware with register I/O, so that
29 * switching is possible between textmodes with different numbers
30 * of lines, in a VGA compatible way. It should work for 132 column
31 * modes also, except that number of columns cannot be changed.
33 * Standard VGA textmode uses a 400 scanline screen which is refreshed
34 * at 70 Hz. The following modes are supported that use this vertical
35 * resolution (C is the number of columns, usually 80 or 132).
40 * C x 36 11 (non-standard height)
41 * C x 44 9 (8-line fonts are a good match)
44 * The following modes are supported with a 480 scanline resolution,
45 * refresh at 60 Hz. Some not quite VGA compatible displays may not
46 * support this (it uses the same vertical timing as standard VGA
47 * 640x480x16 graphics mode).
52 * C x 40 12 (non-standard height)
55 * Two 12-line fonts are already in the consolefonts directory,
56 * namely lat1-12.psfu.gz and lat2-12.psfu.gz.
57 * For the 36 lines mode (11 line font), lat1-10.psfu.gz and lat2-10.psfu.gz
60 * hhanemaa@cs.ruu.nl - 941028
64 * In the consolefonts directory there is 'default8x9' font file but
65 * no 'default8x8'. Why is this? The standard VGA BIOS has an 8-line
66 * font, and they are much more common in SVGA modes (e.g. 50 and 60
67 * row modes). It is true that standard VGA textmode uses effectively
68 * 9 pixel wide characters, but that has nothing to do with the font
79 #include <sys/ioctl.h>
80 #if (__GNU_LIBRARY__ >= 6)
83 #include <linux/types.h>
84 #include <linux/termios.h>
93 #define MODE_RESTORETEXTMODE 0
94 #define MODE_VGALINES 1
96 static void usage(void);
98 /* VGA textmode register tweaking. */
99 static void vga_init_io(void);
100 static void vga_400_scanlines(void);
101 static void vga_480_scanlines(void);
102 static void vga_set_fontheight(int);
103 static int vga_get_fontheight(void);
104 static void vga_set_cursor(int, int);
105 static void vga_set_verticaldisplayend_lowbyte(int);
107 char *dirpath[] = { "", DATADIR "/" VIDEOMODEDIR "/", 0};
108 char *suffixes[] = { "", 0 };
111 main(int argc, char **argv) {
112 int rr, cc, fd, i, mode;
113 struct vt_sizes vtsizes;
114 struct vt_stat vtstat;
115 struct winsize winsize;
117 char tty[12], cmd[80], infile[1024];
121 set_progname(argv[0]);
123 setlocale(LC_ALL, "");
124 bindtextdomain(PACKAGE, LOCALEDIR);
130 if (argc == 2 && !strcmp(argv[1], "-V"))
131 print_version_and_exit();
133 rr = 0; /* make gcc happy */
135 mode = MODE_RESTORETEXTMODE;
136 if (argc == 3 && strcmp(argv[1], "-lines") == 0) {
137 mode = MODE_VGALINES;
141 if (argc == 2 && (p = index(argv[1], 'x')) != 0)
148 if (mode == MODE_RESTORETEXTMODE) {
149 /* prepare for: restoretextmode -r 80x25 */
150 sprintf(infile, "%dx%d", cc, rr);
151 fin = findfile(infile, dirpath, suffixes);
153 fprintf(stderr, _("resizecons: cannot find videomode file %s\n"),
162 if(ioctl(fd, TIOCGWINSZ, &winsize)) {
163 perror("TIOCGWINSZ");
167 if (mode == MODE_VGALINES) {
168 /* Get the number of columns. */
170 if (rr != 25 && rr != 28 && rr !=30 && rr != 34 && rr != 36
171 && rr != 40 && rr != 44 && rr != 50 && rr != 60) {
172 fprintf(stderr, _("Invalid number of lines\n"));
177 if(ioctl(fd, VT_GETSTATE, &vtstat)) {
178 perror("VT_GETSTATE");
184 vtsizes.v_scrollsize = 0;
186 vga_init_io(); /* maybe only if (mode == MODE_VGALINES) */
188 if(ioctl(fd, VT_RESIZE, &vtsizes)) {
193 if (mode == MODE_VGALINES) {
194 /* Program the VGA registers. */
198 if (winsize.ws_row == 25 || winsize.ws_row == 28 ||
199 winsize.ws_row == 36 || winsize.ws_row == 44 ||
200 winsize.ws_row == 50)
204 if (rr == 25 || rr == 28 || rr == 36 || rr == 44 || rr == 50)
208 /* Switch to 400 or 480 scanline vertical timing if required. */
209 if (scanlines_old != 400 && scanlines_new == 400)
211 if (scanlines_old != 480 && scanlines_new == 480)
214 case 25 : fontheight = 16; break;
215 case 28 : fontheight = 14; break;
216 case 30 : fontheight = 16; break;
217 case 34 : fontheight = 14; break;
218 case 36 : fontheight = 12; break;
219 case 40 : fontheight = 12; break;
220 case 44 : fontheight = 9; break;
221 case 50 : fontheight = 8; break;
222 case 60 : fontheight = 8; break;
223 default : fontheight = 8; break;
225 /* Set the VGA character height. */
226 vga_set_fontheight(fontheight);
227 /* Set the line offsets within a character cell of the cursor. */
228 if (fontheight >= 10)
229 vga_set_cursor(fontheight - 3, fontheight - 2);
231 vga_set_cursor(fontheight - 2, fontheight - 1);
233 * If there are a few unused scanlines at the bottom of the
234 * screen, make sure they are not displayed (otherwise
235 * there is a annoying changing partial line at the bottom).
237 vga_set_verticaldisplayend_lowbyte((fontheight * rr - 1) & 0xff);
238 printf(_("Old mode: %dx%d New mode: %dx%d\n"), winsize.ws_col,
239 winsize.ws_row, cc, rr);
240 printf(_("Old #scanlines: %d New #scanlines: %d Character height: %d\n"),
241 scanlines_old, scanlines_new, fontheight);
244 if (mode == MODE_RESTORETEXTMODE) {
245 /* do: restoretextmode -r 25x80 */
246 sprintf(cmd, "restoretextmode -r %s\n", pathname);
250 perror("restoretextmode");
251 fprintf(stderr, _("resizecons: the command `%s' failed\n"), cmd);
257 * for i in /dev/tty[0-9] /dev/tty[0-9][0-9]
259 * stty rows $rr cols $cc < $i
261 * kill -SIGWINCH `cat /tmp/selection.pid`
266 if (vtstat.v_state & (1<<i)) {
267 sprintf(tty, "/dev/tty%d", i);
268 fd = open(tty, O_RDONLY);
269 if (fd < 0 && errno == ENOENT) {
270 sprintf(tty, "/dev/vc/%d", i);
271 fd = open(tty, O_RDONLY);
274 if(ioctl(fd, TIOCSWINSZ, &winsize))
275 perror("TIOCSWINSZ");
281 /* Try to tell selection about the change */
282 /* [may be a security risk?] */
283 if ((fd = open("/tmp/selection.pid", O_RDONLY)) >= 0) {
285 int n = read(fd, buf, sizeof(buf));
297 /* do: setfont default8x16 */
298 /* (other people might wish other fonts - this should be settable) */
300 /* We read the VGA font height register to be sure. */
301 /* There isn't much consistency in this. */
302 switch (vga_get_fontheight()) {
304 case 9 : defaultfont = "default8x9"; break;
305 case 10 : defaultfont = "lat1-10"; break;
307 case 12 : defaultfont = "lat1-12"; break;
309 case 14 : defaultfont = "iso01.14"; break;
312 default : defaultfont = "default8x16"; break;
315 sprintf(cmd, "setfont %s", defaultfont);
320 fprintf(stderr, "resizecons: the command `%s' failed\n", cmd);
324 fprintf(stderr, _("resizecons: don't forget to change TERM "
325 "(maybe to con%dx%d or linux-%dx%d)\n"),
327 if (getenv("LINES") || getenv("COLUMNS"))
329 "Also the variables LINES and COLUMNS may need adjusting.\n");
338 "call is: resizecons COLSxROWS or: resizecons COLS ROWS\n"
339 "or: resizecons -lines ROWS, with ROWS one of 25, 28, 30, 34,"
340 " 36, 40, 44, 50, 60\n"));
345 * The code below is used only with the option `-lines ROWS', and is
346 * very hardware dependent, and requires root privileges.
349 /* Port I/O macros. Note that these are not compatible with the ones */
350 /* defined in the kernel header files. */
352 static inline void outb( int port, int value )
354 __asm__ volatile ("outb %0,%1"
355 : : "a" ((unsigned char)value), "d" ((unsigned short)port));
358 static inline int inb( int port )
361 __asm__ volatile ("inb %1,%0"
363 : "d" ((unsigned short)port));
368 /* VGA textmode register tweaking functions. */
372 static void vga_init_io() {
375 _("resizecons: cannot get I/O permissions.\n"));
379 if ((inb(0x3cc) & 0x01) == 0)
383 static void vga_set_fontheight( int h ) {
384 outb(crtcport, 0x09);
385 outb(crtcport + 1, (inb(crtcport + 1) & 0xe0) | (h - 1));
388 static int vga_get_fontheight() {
389 outb(crtcport, 0x09);
390 return (inb(crtcport + 1) & 0x1f) + 1;
393 static void vga_set_cursor( int top, int bottom ) {
394 outb(crtcport, 0x0a);
395 outb(crtcport + 1, (inb(crtcport + 1) & 0xc0) | top);
396 outb(crtcport, 0x0b);
397 outb(crtcport + 1, (inb(crtcport + 1) & 0xe0) | bottom);
400 static void vga_set_verticaldisplayend_lowbyte( int byte ) {
401 /* CRTC register 0x12 */
402 /* vertical display end */
403 outb(crtcport, 0x12);
404 outb(crtcport + 1, byte);
407 static void vga_480_scanlines() {
408 /* CRTC register 0x11 */
409 /* vertical sync end (also unlocks CR0-7) */
410 outb(crtcport, 0x11);
411 outb(crtcport + 1, 0x0c);
413 /* CRTC register 0x06 */
415 outb(crtcport, 0x06);
416 outb(crtcport + 1, 0x0b);
418 /* CRTC register 0x07 */
419 /* (vertical) overflow */
420 outb(crtcport, 0x07);
421 outb(crtcport + 1, 0x3e);
423 /* CRTC register 0x10 */
424 /* vertical sync start */
425 outb(crtcport, 0x10);
426 outb(crtcport + 1, 0xea);
428 /* CRTC register 0x12 */
429 /* vertical display end */
430 outb(crtcport, 0x12);
431 outb(crtcport + 1, 0xdf);
433 /* CRTC register 0x15 */
434 /* vertical blank start */
435 outb(crtcport, 0x15);
436 outb(crtcport + 1, 0xe7);
438 /* CRTC register 0x16 */
439 /* vertical blank end */
440 outb(crtcport, 0x16);
441 outb(crtcport + 1, 0x04);
443 /* Misc Output register */
444 /* Preserver clock select bits and set correct sync polarity */
445 outb(0x3c2, (inb(0x3cc) & 0x0d) | 0xe2);
448 static void vga_400_scanlines() {
449 /* CRTC register 0x11 */
450 /* vertical sync end (also unlocks CR0-7) */
451 outb(crtcport, 0x11);
452 outb(crtcport + 1, 0x0e);
454 /* CRTC register 0x06 */
456 outb(crtcport, 0x06);
457 outb(crtcport + 1, 0xbf);
459 /* CRTC register 0x07 */
460 /* (vertical) overflow */
461 outb(crtcport, 0x07);
462 outb(crtcport + 1, 0x1f);
464 /* CRTC register 0x10 */
465 /* vertical sync start */
466 outb(crtcport, 0x10);
467 outb(crtcport + 1, 0x9c);
469 /* CRTC register 0x12 */
470 /* vertical display end */
471 outb(crtcport, 0x12);
472 outb(crtcport + 1, 0x8f);
474 /* CRTC register 0x15 */
475 /* vertical blank start */
476 outb(crtcport, 0x15);
477 outb(crtcport + 1, 0x96);
479 /* CRTC register 0x16 */
480 /* vertical blank end */
481 outb(crtcport, 0x16);
482 outb(crtcport + 1, 0xb9);
484 /* Misc Output register */
485 /* Preserver clock select bits and set correct sync polarity */
486 outb(0x3c2, (inb(0x3cc) & 0x0d) | 0x62);