vt.c

来自「linux 内核源代码」· C语言 代码 · 共 2,565 行 · 第 1/5 页

C
2,565
字号
	unsigned long draw_from = 0, draw_to = 0;	struct vc_data *vc;	unsigned char vc_attr;	uint8_t rescan;	uint8_t inverse;	uint8_t width;	u16 himask, charmask;	const unsigned char *orig_buf = NULL;	int orig_count;	if (in_interrupt())		return count;	might_sleep();	acquire_console_sem();	vc = tty->driver_data;	if (vc == NULL) {		printk(KERN_ERR "vt: argh, driver_data is NULL !\n");		release_console_sem();		return 0;	}	currcons = vc->vc_num;	if (!vc_cons_allocated(currcons)) {	    /* could this happen? */	    static int error = 0;	    if (!error) {		error = 1;		printk("con_write: tty %d not allocated\n", currcons+1);	    }	    release_console_sem();	    return 0;	}	release_console_sem();	orig_buf = buf;	orig_count = count;	/* At this point 'buf' is guaranteed to be a kernel buffer	 * and therefore no access to userspace (and therefore sleeping)	 * will be needed.  The con_buf_mtx serializes all tty based	 * console rendering and vcs write/read operations.  We hold	 * the console spinlock during the entire write.	 */	acquire_console_sem();	vc = tty->driver_data;	if (vc == NULL) {		printk(KERN_ERR "vt: argh, driver_data _became_ NULL !\n");		release_console_sem();		goto out;	}	himask = vc->vc_hi_font_mask;	charmask = himask ? 0x1ff : 0xff;	/* undraw cursor first */	if (IS_FG(vc))		hide_cursor(vc);	while (!tty->stopped && count) {		int orig = *buf;		c = orig;		buf++;		n++;		count--;		rescan = 0;		inverse = 0;		width = 1;		/* Do no translation at all in control states */		if (vc->vc_state != ESnormal) {			tc = c;		} else if (vc->vc_utf && !vc->vc_disp_ctrl) {		    /* Combine UTF-8 into Unicode in vc_utf_char.		     * vc_utf_count is the number of continuation bytes still		     * expected to arrive.		     * vc_npar is the number of continuation bytes arrived so		     * far		     */rescan_last_byte:		    if ((c & 0xc0) == 0x80) {			/* Continuation byte received */			static const uint32_t utf8_length_changes[] = { 0x0000007f, 0x000007ff, 0x0000ffff, 0x001fffff, 0x03ffffff, 0x7fffffff };			if (vc->vc_utf_count) {			    vc->vc_utf_char = (vc->vc_utf_char << 6) | (c & 0x3f);			    vc->vc_npar++;			    if (--vc->vc_utf_count) {				/* Still need some bytes */				continue;			    }			    /* Got a whole character */			    c = vc->vc_utf_char;			    /* Reject overlong sequences */			    if (c <= utf8_length_changes[vc->vc_npar - 1] ||					c > utf8_length_changes[vc->vc_npar])				c = 0xfffd;			} else {			    /* Unexpected continuation byte */			    vc->vc_utf_count = 0;			    c = 0xfffd;			}		    } else {			/* Single ASCII byte or first byte of a sequence received */			if (vc->vc_utf_count) {			    /* Continuation byte expected */			    rescan = 1;			    vc->vc_utf_count = 0;			    c = 0xfffd;			} else if (c > 0x7f) {			    /* First byte of a multibyte sequence received */			    vc->vc_npar = 0;			    if ((c & 0xe0) == 0xc0) {				vc->vc_utf_count = 1;				vc->vc_utf_char = (c & 0x1f);			    } else if ((c & 0xf0) == 0xe0) {				vc->vc_utf_count = 2;				vc->vc_utf_char = (c & 0x0f);			    } else if ((c & 0xf8) == 0xf0) {				vc->vc_utf_count = 3;				vc->vc_utf_char = (c & 0x07);			    } else if ((c & 0xfc) == 0xf8) {				vc->vc_utf_count = 4;				vc->vc_utf_char = (c & 0x03);			    } else if ((c & 0xfe) == 0xfc) {				vc->vc_utf_count = 5;				vc->vc_utf_char = (c & 0x01);			    } else {				/* 254 and 255 are invalid */				c = 0xfffd;			    }			    if (vc->vc_utf_count) {				/* Still need some bytes */				continue;			    }			}			/* Nothing to do if an ASCII byte was received */		    }		    /* End of UTF-8 decoding. */		    /* c is the received character, or U+FFFD for invalid sequences. */		    /* Replace invalid Unicode code points with U+FFFD too */		    if ((c >= 0xd800 && c <= 0xdfff) || c == 0xfffe || c == 0xffff)			c = 0xfffd;		    tc = c;		} else {	/* no utf or alternate charset mode */		    tc = vc->vc_translate[vc->vc_toggle_meta ? (c | 0x80) : c];		}                /* If the original code was a control character we                 * only allow a glyph to be displayed if the code is                 * not normally used (such as for cursor movement) or                 * if the disp_ctrl mode has been explicitly enabled.                 * Certain characters (as given by the CTRL_ALWAYS                 * bitmap) are always displayed as control characters,                 * as the console would be pretty useless without                 * them; to display an arbitrary font position use the                 * direct-to-font zone in UTF-8 mode.                 */                ok = tc && (c >= 32 ||			    !(vc->vc_disp_ctrl ? (CTRL_ALWAYS >> c) & 1 :				  vc->vc_utf || ((CTRL_ACTION >> c) & 1)))			&& (c != 127 || vc->vc_disp_ctrl)			&& (c != 128+27);		if (vc->vc_state == ESnormal && ok) {			if (vc->vc_utf && !vc->vc_disp_ctrl) {				if (is_double_width(c))					width = 2;			}			/* Now try to find out how to display it */			tc = conv_uni_to_pc(vc, tc);			if (tc & ~charmask) {				if (tc == -1 || tc == -2) {				    continue; /* nothing to display */				}				/* Glyph not found */				if ((!(vc->vc_utf && !vc->vc_disp_ctrl) || c < 128) && !(c & ~charmask)) {				    /* In legacy mode use the glyph we get by a 1:1 mapping.				       This would make absolutely no sense with Unicode in mind,				       but do this for ASCII characters since a font may lack				       Unicode mapping info and we don't want to end up with				       having question marks only. */				    tc = c;				} else {				    /* Display U+FFFD. If it's not found, display an inverse question mark. */				    tc = conv_uni_to_pc(vc, 0xfffd);				    if (tc < 0) {					inverse = 1;					tc = conv_uni_to_pc(vc, '?');					if (tc < 0) tc = '?';				    }				}			}			if (!inverse) {				vc_attr = vc->vc_attr;			} else {				/* invert vc_attr */				if (!vc->vc_can_do_color) {					vc_attr = (vc->vc_attr) ^ 0x08;				} else if (vc->vc_hi_font_mask == 0x100) {					vc_attr = ((vc->vc_attr) & 0x11) | (((vc->vc_attr) & 0xe0) >> 4) | (((vc->vc_attr) & 0x0e) << 4);				} else {					vc_attr = ((vc->vc_attr) & 0x88) | (((vc->vc_attr) & 0x70) >> 4) | (((vc->vc_attr) & 0x07) << 4);				}				FLUSH			}			while (1) {				if (vc->vc_need_wrap || vc->vc_decim)					FLUSH				if (vc->vc_need_wrap) {					cr(vc);					lf(vc);				}				if (vc->vc_decim)					insert_char(vc, 1);				scr_writew(himask ?					     ((vc_attr << 8) & ~himask) + ((tc & 0x100) ? himask : 0) + (tc & 0xff) :					     (vc_attr << 8) + tc,					   (u16 *) vc->vc_pos);				if (DO_UPDATE(vc) && draw_x < 0) {					draw_x = vc->vc_x;					draw_from = vc->vc_pos;				}				if (vc->vc_x == vc->vc_cols - 1) {					vc->vc_need_wrap = vc->vc_decawm;					draw_to = vc->vc_pos + 2;				} else {					vc->vc_x++;					draw_to = (vc->vc_pos += 2);				}				if (!--width) break;				tc = conv_uni_to_pc(vc, ' '); /* A space is printed in the second column */				if (tc < 0) tc = ' ';			}			notify_write(vc, c);			if (inverse) {				FLUSH			}			if (rescan) {				rescan = 0;				inverse = 0;				width = 1;				c = orig;				goto rescan_last_byte;			}			continue;		}		FLUSH		do_con_trol(tty, vc, orig);	}	FLUSH	console_conditional_schedule();	release_console_sem();out:	notify_update(vc);	return n;#undef FLUSH}/* * This is the console switching callback. * * Doing console switching in a process context allows * us to do the switches asynchronously (needed when we want * to switch due to a keyboard interrupt).  Synchronization * with other console code and prevention of re-entrancy is * ensured with console_sem. */static void console_callback(struct work_struct *ignored){	acquire_console_sem();	if (want_console >= 0) {		if (want_console != fg_console &&		    vc_cons_allocated(want_console)) {			hide_cursor(vc_cons[fg_console].d);			change_console(vc_cons[want_console].d);			/* we only changed when the console had already			   been allocated - a new console is not created			   in an interrupt routine */		}		want_console = -1;	}	if (do_poke_blanked_console) { /* do not unblank for a LED change */		do_poke_blanked_console = 0;		poke_blanked_console();	}	if (scrollback_delta) {		struct vc_data *vc = vc_cons[fg_console].d;		clear_selection();		if (vc->vc_mode == KD_TEXT)			vc->vc_sw->con_scrolldelta(vc, scrollback_delta);		scrollback_delta = 0;	}	if (blank_timer_expired) {		do_blank_screen(0);		blank_timer_expired = 0;	}	notify_update(vc_cons[fg_console].d);	release_console_sem();}int set_console(int nr){	struct vc_data *vc = vc_cons[fg_console].d;	if (!vc_cons_allocated(nr) || vt_dont_switch ||		(vc->vt_mode.mode == VT_AUTO && vc->vc_mode == KD_GRAPHICS)) {		/*		 * Console switch will fail in console_callback() or		 * change_console() so there is no point scheduling		 * the callback		 *		 * Existing set_console() users don't check the return		 * value so this shouldn't break anything		 */		return -EINVAL;	}	want_console = nr;	schedule_console_callback();	return 0;}struct tty_driver *console_driver;#ifdef CONFIG_VT_CONSOLE/* *	Console on virtual terminal * * The console must be locked when we get here. */static void vt_console_print(struct console *co, const char *b, unsigned count){	struct vc_data *vc = vc_cons[fg_console].d;	unsigned char c;	static unsigned long printing;	const ushort *start;	ushort cnt = 0;	ushort myx;	/* console busy or not yet initialized */	if (!printable || test_and_set_bit(0, &printing))		return;	if (kmsg_redirect && vc_cons_allocated(kmsg_redirect - 1))		vc = vc_cons[kmsg_redirect - 1].d;	/* read `x' only after setting currcons properly (otherwise	   the `x' macro will read the x of the foreground console). */	myx = vc->vc_x;	if (!vc_cons_allocated(fg_console)) {		/* impossible */		/* printk("vt_console_print: tty %d not allocated ??\n", currcons+1); */		goto quit;	}	if (vc->vc_mode != KD_TEXT)		goto quit;	/* undraw cursor first */	if (IS_FG(vc))		hide_cursor(vc);	start = (ushort *)vc->vc_pos;	/* Contrived structure to try to emulate original need_wrap behaviour	 * Problems caused when we have need_wrap set on '\n' character */	while (count--) {		c = *b++;		if (c == 10 || c == 13 || c == 8 || vc->vc_need_wrap) {			if (cnt > 0) {				if (CON_IS_VISIBLE(vc))					vc->vc_sw->con_putcs(vc, start, cnt, vc->vc_y, vc->vc_x);				vc->vc_x += cnt;				if (vc->vc_need_wrap)					vc->vc_x--;				cnt = 0;			}			if (c == 8) {		/* backspace */				bs(vc);				start = (ushort *)vc->vc_pos;				myx = vc->vc_x;				continue;			}			if (c != 13)				lf(vc);			cr(vc);			start = (ushort *)vc->vc_pos;			myx = vc->vc_x;			if (c == 10 || c == 13)				continue;		}		scr_writew((vc->vc_attr << 8) + c, (unsigned short *)vc->vc_pos);		notify_write(vc, c);		cnt++;		if (myx == vc->vc_cols - 1) {			vc->vc_need_wrap = 1;			continue;		}		vc->vc_pos += 2;		myx++;	}	if (cnt > 0) {		if (CON_IS_VISIBLE(vc))			vc->vc_sw->con_putcs(vc, start, cnt, vc->vc_y, vc->vc_x);		vc->vc_x += cnt;		if (vc->vc_x == vc->vc_cols) {			vc->vc_x--;			vc->vc_need_wrap = 1;		}	}	set_cursor(vc);	notify_update(vc);quit:	clear_bit(0, &printing);}static struct tty_driver *vt_console_device(struct console *c, int *index){	*index = c->index ? c->index-1 : fg_console;	return console_driver;}static struct console vt_console_driver = {	.name		= "tty",	.write		= vt_console_print,	.device		= vt_console_device,	.unblank	= unblank_screen,	.flags		= CON_PRINTBUFFER,	.index		= -1,};#endif/* *	Handling of Linux-specific VC ioctls *//* * Generally a bit racy with respect to console_sem(). * * There are some functions which don't need it. * * There are some functions which can sleep for arbitrary periods * (paste_selection) but we don't need the lock there anyway. * * set_selection has locking, and definitely needs it */int tioclinux(struct tty_struct *tty, unsigned long arg){	char type, data;	char __user *p = (char __user *)arg;	int lines;	int ret;	if (tty->driver->type != TTY_DRIVER_TYPE_CONSOLE)		return -EINVAL;	if (current->signal->tty != tty && !capable(CAP_SYS_ADMIN))		return -EPERM;	if (get_user(type, p))		return -EFAULT;	ret = 0;	switch (type)	{		case TIOCL_SETSEL:			acquire_console_sem();			ret = set_selection((struct tiocl_selection __user *)(p+1), tty);			release_console_sem();			break;		case TIOCL_PASTESEL:			ret = paste_selection(tty);			break;		case TIOCL_UNBLANKSCREEN:			acquire_console_sem();			unblank_screen();			release_console_sem();			break;		case TIOCL_SELLOADLUT:			ret = sel_loadlut(p);			break;		case TIOCL_GETSHIFTSTATE:				/*	 * Make it possible to react to Shift+Mousebutton.	 * Note that 'shift_state' is an undocumented	 * kernel-internal variable; programs not closely	 * related to the kernel should not use this.	 */	 		data = shift_state;			ret = __put_user(data, p);			break;		case TIOCL_GETMOUSEREPORTING:			data = mouse_reporting();			ret = __put_user(data, p);			break;		case TIO

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?