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 + -
显示快捷键?