summaryrefslogtreecommitdiffstats
path: root/drivers/tty/vt
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/vt')
-rw-r--r--drivers/tty/vt/vc_screen.c90
-rw-r--r--drivers/tty/vt/vt.c308
2 files changed, 373 insertions, 25 deletions
diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c
index e4a66e1fd05f..2384ea85ffaf 100644
--- a/drivers/tty/vt/vc_screen.c
+++ b/drivers/tty/vt/vc_screen.c
@@ -10,6 +10,12 @@
* Attribute/character pair is in native endianity.
* [minor: N+128]
*
+ * /dev/vcsuN: similar to /dev/vcsaN but using 4-byte unicode values
+ * instead of 1-byte screen glyph values.
+ * [minor: N+64]
+ *
+ * /dev/vcsuaN: same idea as /dev/vcsaN for unicode (not yet implemented).
+ *
* This replaces screendump and part of selection, so that the system
* administrator can control access using file system permissions.
*
@@ -51,6 +57,26 @@
#define CON_BUF_SIZE (CONFIG_BASE_SMALL ? 256 : PAGE_SIZE)
+/*
+ * Our minor space:
+ *
+ * 0 ... 63 glyph mode without attributes
+ * 64 ... 127 unicode mode without attributes
+ * 128 ... 191 glyph mode with attributes
+ * 192 ... 255 unused (reserved for unicode with attributes)
+ *
+ * This relies on MAX_NR_CONSOLES being <= 63, meaning 63 actual consoles
+ * with minors 0, 64, 128 and 192 being proxies for the foreground console.
+ */
+#if MAX_NR_CONSOLES > 63
+#warning "/dev/vcs* devices may not accommodate more than 63 consoles"
+#endif
+
+#define console(inode) (iminor(inode) & 63)
+#define use_unicode(inode) (iminor(inode) & 64)
+#define use_attributes(inode) (iminor(inode) & 128)
+
+
struct vcs_poll_data {
struct notifier_block notifier;
unsigned int cons_num;
@@ -102,7 +128,7 @@ vcs_poll_data_get(struct file *file)
poll = kzalloc(sizeof(*poll), GFP_KERNEL);
if (!poll)
return NULL;
- poll->cons_num = iminor(file_inode(file)) & 127;
+ poll->cons_num = console(file_inode(file));
init_waitqueue_head(&poll->waitq);
poll->notifier.notifier_call = vcs_notifier;
if (register_vt_notifier(&poll->notifier) != 0) {
@@ -140,7 +166,7 @@ vcs_poll_data_get(struct file *file)
static struct vc_data*
vcs_vc(struct inode *inode, int *viewed)
{
- unsigned int currcons = iminor(inode) & 127;
+ unsigned int currcons = console(inode);
WARN_CONSOLE_UNLOCKED();
@@ -164,7 +190,6 @@ static int
vcs_size(struct inode *inode)
{
int size;
- int minor = iminor(inode);
struct vc_data *vc;
WARN_CONSOLE_UNLOCKED();
@@ -175,8 +200,12 @@ vcs_size(struct inode *inode)
size = vc->vc_rows * vc->vc_cols;
- if (minor & 128)
+ if (use_attributes(inode)) {
+ if (use_unicode(inode))
+ return -EOPNOTSUPP;
size = 2*size + HEADER_SIZE;
+ } else if (use_unicode(inode))
+ size *= 4;
return size;
}
@@ -197,12 +226,10 @@ static ssize_t
vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
struct inode *inode = file_inode(file);
- unsigned int currcons = iminor(inode);
struct vc_data *vc;
struct vcs_poll_data *poll;
- long pos;
- long attr, read;
- int col, maxcol, viewed;
+ long pos, read;
+ int attr, uni_mode, row, col, maxcol, viewed;
unsigned short *org = NULL;
ssize_t ret;
char *con_buf;
@@ -218,7 +245,8 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
*/
console_lock();
- attr = (currcons & 128);
+ uni_mode = use_unicode(inode);
+ attr = use_attributes(inode);
ret = -ENXIO;
vc = vcs_vc(inode, &viewed);
if (!vc)
@@ -227,6 +255,10 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
ret = -EINVAL;
if (pos < 0)
goto unlock_out;
+ /* we enforce 32-bit alignment for pos and count in unicode mode */
+ if (uni_mode && (pos | count) & 3)
+ goto unlock_out;
+
poll = file->private_data;
if (count && poll)
poll->seen_last_update = true;
@@ -266,7 +298,28 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
con_buf_start = con_buf0 = con_buf;
orig_count = this_round;
maxcol = vc->vc_cols;
- if (!attr) {
+ if (uni_mode) {
+ unsigned int nr;
+
+ ret = vc_uniscr_check(vc);
+ if (ret)
+ break;
+ p /= 4;
+ row = p / vc->vc_cols;
+ col = p % maxcol;
+ nr = maxcol - col;
+ do {
+ if (nr > this_round/4)
+ nr = this_round/4;
+ vc_uniscr_copy_line(vc, con_buf0, viewed,
+ row, col, nr);
+ con_buf0 += nr * 4;
+ this_round -= nr * 4;
+ row++;
+ col = 0;
+ nr = maxcol;
+ } while (this_round);
+ } else if (!attr) {
org = screen_pos(vc, p, viewed);
col = p % maxcol;
p += maxcol - col;
@@ -375,7 +428,6 @@ static ssize_t
vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
struct inode *inode = file_inode(file);
- unsigned int currcons = iminor(inode);
struct vc_data *vc;
long pos;
long attr, size, written;
@@ -396,7 +448,7 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
*/
console_lock();
- attr = (currcons & 128);
+ attr = use_attributes(inode);
ret = -ENXIO;
vc = vcs_vc(inode, &viewed);
if (!vc)
@@ -593,9 +645,15 @@ vcs_fasync(int fd, struct file *file, int on)
static int
vcs_open(struct inode *inode, struct file *filp)
{
- unsigned int currcons = iminor(inode) & 127;
+ unsigned int currcons = console(inode);
+ bool attr = use_attributes(inode);
+ bool uni_mode = use_unicode(inode);
int ret = 0;
-
+
+ /* we currently don't support attributes in unicode mode */
+ if (attr && uni_mode)
+ return -EOPNOTSUPP;
+
console_lock();
if(currcons && !vc_cons_allocated(currcons-1))
ret = -ENXIO;
@@ -628,6 +686,8 @@ void vcs_make_sysfs(int index)
{
device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 1), NULL,
"vcs%u", index + 1);
+ device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 65), NULL,
+ "vcsu%u", index + 1);
device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 129), NULL,
"vcsa%u", index + 1);
}
@@ -635,6 +695,7 @@ void vcs_make_sysfs(int index)
void vcs_remove_sysfs(int index)
{
device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 1));
+ device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 65));
device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 129));
}
@@ -647,6 +708,7 @@ int __init vcs_init(void)
vc_class = class_create(THIS_MODULE, "vc");
device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 0), NULL, "vcs");
+ device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 64), NULL, "vcsu");
device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 128), NULL, "vcsa");
for (i = 0; i < MIN_NR_CONSOLES; i++)
vcs_make_sysfs(i);
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 15eb6c829d39..846dfedb657d 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -317,6 +317,259 @@ void schedule_console_callback(void)
schedule_work(&console_work);
}
+/*
+ * Code to manage unicode-based screen buffers
+ */
+
+#ifdef NO_VC_UNI_SCREEN
+/* this disables and optimizes related code away at compile time */
+#define get_vc_uniscr(vc) NULL
+#else
+#define get_vc_uniscr(vc) vc->vc_uni_screen
+#endif
+
+typedef uint32_t char32_t;
+
+/*
+ * Our screen buffer is preceded by an array of line pointers so that
+ * scrolling only implies some pointer shuffling.
+ */
+struct uni_screen {
+ char32_t *lines[0];
+};
+
+static struct uni_screen *vc_uniscr_alloc(unsigned int cols, unsigned int rows)
+{
+ struct uni_screen *uniscr;
+ void *p;
+ unsigned int memsize, i;
+
+ /* allocate everything in one go */
+ memsize = cols * rows * sizeof(char32_t);
+ memsize += rows * sizeof(char32_t *);
+ p = kmalloc(memsize, GFP_KERNEL);
+ if (!p)
+ return NULL;
+
+ /* initial line pointers */
+ uniscr = p;
+ p = uniscr->lines + rows;
+ for (i = 0; i < rows; i++) {
+ uniscr->lines[i] = p;
+ p += cols * sizeof(char32_t);
+ }
+ return uniscr;
+}
+
+static void vc_uniscr_set(struct vc_data *vc, struct uni_screen *new_uniscr)
+{
+ kfree(vc->vc_uni_screen);
+ vc->vc_uni_screen = new_uniscr;
+}
+
+static void vc_uniscr_putc(struct vc_data *vc, char32_t uc)
+{
+ struct uni_screen *uniscr = get_vc_uniscr(vc);
+
+ if (uniscr)
+ uniscr->lines[vc->vc_y][vc->vc_x] = uc;
+}
+
+static void vc_uniscr_insert(struct vc_data *vc, unsigned int nr)
+{
+ struct uni_screen *uniscr = get_vc_uniscr(vc);
+
+ if (uniscr) {
+ char32_t *ln = uniscr->lines[vc->vc_y];
+ unsigned int x = vc->vc_x, cols = vc->vc_cols;
+
+ memmove(&ln[x + nr], &ln[x], (cols - x - nr) * sizeof(*ln));
+ memset32(&ln[x], ' ', nr);
+ }
+}
+
+static void vc_uniscr_delete(struct vc_data *vc, unsigned int nr)
+{
+ struct uni_screen *uniscr = get_vc_uniscr(vc);
+
+ if (uniscr) {
+ char32_t *ln = uniscr->lines[vc->vc_y];
+ unsigned int x = vc->vc_x, cols = vc->vc_cols;
+
+ memcpy(&ln[x], &ln[x + nr], (cols - x - nr) * sizeof(*ln));
+ memset32(&ln[cols - nr], ' ', nr);
+ }
+}
+
+static void vc_uniscr_clear_line(struct vc_data *vc, unsigned int x,
+ unsigned int nr)
+{
+ struct uni_screen *uniscr = get_vc_uniscr(vc);
+
+ if (uniscr) {
+ char32_t *ln = uniscr->lines[vc->vc_y];
+
+ memset32(&ln[x], ' ', nr);
+ }
+}
+
+static void vc_uniscr_clear_lines(struct vc_data *vc, unsigned int y,
+ unsigned int nr)
+{
+ struct uni_screen *uniscr = get_vc_uniscr(vc);
+
+ if (uniscr) {
+ unsigned int cols = vc->vc_cols;
+
+ while (nr--)
+ memset32(uniscr->lines[y++], ' ', cols);
+ }
+}
+
+static void vc_uniscr_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
+ enum con_scroll dir, unsigned int nr)
+{
+ struct uni_screen *uniscr = get_vc_uniscr(vc);
+
+ if (uniscr) {
+ unsigned int s, d, rescue, clear;
+ char32_t *save[nr];
+
+ s = clear = t;
+ d = t + nr;
+ rescue = b - nr;
+ if (dir == SM_UP) {
+ swap(s, d);
+ swap(clear, rescue);
+ }
+ memcpy(save, uniscr->lines + rescue, nr * sizeof(*save));
+ memmove(uniscr->lines + d, uniscr->lines + s,
+ (b - t - nr) * sizeof(*uniscr->lines));
+ memcpy(uniscr->lines + clear, save, nr * sizeof(*save));
+ vc_uniscr_clear_lines(vc, clear, nr);
+ }
+}
+
+static void vc_uniscr_copy_area(struct uni_screen *dst,
+ unsigned int dst_cols,
+ unsigned int dst_rows,
+ struct uni_screen *src,
+ unsigned int src_cols,
+ unsigned int src_top_row,
+ unsigned int src_bot_row)
+{
+ unsigned int dst_row = 0;
+
+ if (!dst)
+ return;
+
+ while (src_top_row < src_bot_row) {
+ char32_t *src_line = src->lines[src_top_row];
+ char32_t *dst_line = dst->lines[dst_row];
+
+ memcpy(dst_line, src_line, src_cols * sizeof(char32_t));
+ if (dst_cols - src_cols)
+ memset32(dst_line + src_cols, ' ', dst_cols - src_cols);
+ src_top_row++;
+ dst_row++;
+ }
+ while (dst_row < dst_rows) {
+ char32_t *dst_line = dst->lines[dst_row];
+
+ memset32(dst_line, ' ', dst_cols);
+ dst_row++;
+ }
+}
+
+/*
+ * Called from vcs_read() to make sure unicode screen retrieval is possible.
+ * This will initialize the unicode screen buffer if not already done.
+ * This returns 0 if OK, or a negative error code otherwise.
+ * In particular, -ENODATA is returned if the console is not in UTF-8 mode.
+ */
+int vc_uniscr_check(struct vc_data *vc)
+{
+ struct uni_screen *uniscr;
+ unsigned short *p;
+ int x, y, mask;
+
+ if (__is_defined(NO_VC_UNI_SCREEN))
+ return -EOPNOTSUPP;
+
+ WARN_CONSOLE_UNLOCKED();
+
+ if (!vc->vc_utf)
+ return -ENODATA;
+
+ if (vc->vc_uni_screen)
+ return 0;
+
+ uniscr = vc_uniscr_alloc(vc->vc_cols, vc->vc_rows);
+ if (!uniscr)
+ return -ENOMEM;
+
+ /*
+ * Let's populate it initially with (imperfect) reverse translation.
+ * This is the next best thing we can do short of having it enabled
+ * from the start even when no users rely on this functionality. True
+ * unicode content will be available after a complete screen refresh.
+ */
+ p = (unsigned short *)vc->vc_origin;
+ mask = vc->vc_hi_font_mask | 0xff;
+ for (y = 0; y < vc->vc_rows; y++) {
+ char32_t *line = uniscr->lines[y];
+ for (x = 0; x < vc->vc_cols; x++) {
+ u16 glyph = scr_readw(p++) & mask;
+ line[x] = inverse_translate(vc, glyph, true);
+ }
+ }
+
+ vc->vc_uni_screen = uniscr;
+ return 0;
+}
+
+/*
+ * Called from vcs_read() to get the unicode data from the screen.
+ * This must be preceded by a successful call to vc_uniscr_check() once
+ * the console lock has been taken.
+ */
+void vc_uniscr_copy_line(struct vc_data *vc, void *dest, int viewed,
+ unsigned int row, unsigned int col, unsigned int nr)
+{
+ struct uni_screen *uniscr = get_vc_uniscr(vc);
+ int offset = row * vc->vc_size_row + col * 2;
+ unsigned long pos;
+
+ BUG_ON(!uniscr);
+
+ pos = (unsigned long)screenpos(vc, offset, viewed);
+ if (pos >= vc->vc_origin && pos < vc->vc_scr_end) {
+ /*
+ * Desired position falls in the main screen buffer.
+ * However the actual row/col might be different if
+ * scrollback is active.
+ */
+ row = (pos - vc->vc_origin) / vc->vc_size_row;
+ col = ((pos - vc->vc_origin) % vc->vc_size_row) / 2;
+ memcpy(dest, &uniscr->lines[row][col], nr * sizeof(char32_t));
+ } else {
+ /*
+ * Scrollback is active. For now let's simply backtranslate
+ * the screen glyphs until the unicode screen buffer does
+ * synchronize with console display drivers for a scrollback
+ * buffer of its own.
+ */
+ u16 *p = (u16 *)pos;
+ int mask = vc->vc_hi_font_mask | 0xff;
+ char32_t *uni_buf = dest;
+ while (nr--) {
+ u16 glyph = scr_readw(p++) & mask;
+ *uni_buf++ = inverse_translate(vc, glyph, true);
+ }
+ }
+}
+
+
static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
enum con_scroll dir, unsigned int nr)
{
@@ -326,6 +579,7 @@ static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
nr = b - t - 1;
if (b > vc->vc_rows || t >= b || nr < 1)
return;
+ vc_uniscr_scroll(vc, t, b, dir, nr);
if (con_is_visible(vc) && vc->vc_sw->con_scroll(vc, t, b, dir, nr))
return;
@@ -533,6 +787,7 @@ static void insert_char(struct vc_data *vc, unsigned int nr)
{
unsigned short *p = (unsigned short *) vc->vc_pos;
+ vc_uniscr_insert(vc, nr);
scr_memmovew(p + nr, p, (vc->vc_cols - vc->vc_x - nr) * 2);
scr_memsetw(p, vc->vc_video_erase_char, nr * 2);
vc->vc_need_wrap = 0;
@@ -545,6 +800,7 @@ static void delete_char(struct vc_data *vc, unsigned int nr)
{
unsigned short *p = (unsigned short *) vc->vc_pos;
+ vc_uniscr_delete(vc, nr);
scr_memcpyw(p, p + nr, (vc->vc_cols - vc->vc_x - nr) * 2);
scr_memsetw(p + vc->vc_cols - vc->vc_x - nr, vc->vc_video_erase_char,
nr * 2);
@@ -845,10 +1101,11 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
{
unsigned long old_origin, new_origin, new_scr_end, rlth, rrem, err = 0;
unsigned long end;
- unsigned int old_rows, old_row_size;
+ unsigned int old_rows, old_row_size, first_copied_row;
unsigned int new_cols, new_rows, new_row_size, new_screen_size;
unsigned int user;
unsigned short *newscreen;
+ struct uni_screen *new_uniscr = NULL;
WARN_CONSOLE_UNLOCKED();
@@ -875,6 +1132,14 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
if (!newscreen)
return -ENOMEM;
+ if (get_vc_uniscr(vc)) {
+ new_uniscr = vc_uniscr_alloc(new_cols, new_rows);
+ if (!new_uniscr) {
+ kfree(newscreen);
+ return -ENOMEM;
+ }
+ }
+
if (vc == sel_cons)
clear_selection();
@@ -884,6 +1149,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
err = resize_screen(vc, new_cols, new_rows, user);
if (err) {
kfree(newscreen);
+ kfree(new_uniscr);
return err;
}
@@ -904,18 +1170,24 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
* Cursor near the bottom, copy contents from the
* bottom of buffer
*/
- old_origin += (old_rows - new_rows) * old_row_size;
+ first_copied_row = (old_rows - new_rows);
} else {
/*
* Cursor is in no man's land, copy 1/2 screenful
* from the top and bottom of cursor position
*/
- old_origin += (vc->vc_y - new_rows/2) * old_row_size;
+ first_copied_row = (vc->vc_y - new_rows/2);
}
- }
-
+ old_origin += first_copied_row * old_row_size;
+ } else
+ first_copied_row = 0;
end = old_origin + old_row_size * min(old_rows, new_rows);
+ vc_uniscr_copy_area(new_uniscr, new_cols, new_rows,
+ get_vc_uniscr(vc), rlth/2, first_copied_row,
+ min(old_rows, new_rows));
+ vc_uniscr_set(vc, new_uniscr);
+
update_attr(vc);
while (old_origin < end) {
@@ -1013,6 +1285,7 @@ struct vc_data *vc_deallocate(unsigned int currcons)
vc->vc_sw->con_deinit(vc);
put_pid(vc->vt_pid);
module_put(vc->vc_sw->owner);
+ vc_uniscr_set(vc, NULL);
kfree(vc->vc_screenbuf);
vc_cons[currcons].d = NULL;
}
@@ -1171,15 +1444,22 @@ static void csi_J(struct vc_data *vc, int vpar)
switch (vpar) {
case 0: /* erase from cursor to end of display */
+ vc_uniscr_clear_line(vc, vc->vc_x,
+ vc->vc_cols - vc->vc_x);
+ vc_uniscr_clear_lines(vc, vc->vc_y + 1,
+ vc->vc_rows - vc->vc_y - 1);
count = (vc->vc_scr_end - vc->vc_pos) >> 1;
start = (unsigned short *)vc->vc_pos;
break;
case 1: /* erase from start to cursor */
+ vc_uniscr_clear_line(vc, 0, vc->vc_x + 1);
+ vc_uniscr_clear_lines(vc, 0, vc->vc_y);
count = ((vc->vc_pos - vc->vc_origin) >> 1) + 1;
start = (unsigned short *)vc->vc_origin;
break;
case 2: /* erase whole display */
case 3: /* (and scrollback buffer later) */
+ vc_uniscr_clear_lines(vc, 0, vc->vc_rows);
count = vc->vc_cols * vc->vc_rows;
start = (unsigned short *)vc->vc_origin;
break;
@@ -1200,25 +1480,27 @@ static void csi_J(struct vc_data *vc, int vpar)
static void csi_K(struct vc_data *vc, int vpar)
{
unsigned int count;
- unsigned short * start;
+ unsigned short *start = (unsigned short *)vc->vc_pos;
+ int offset;
switch (vpar) {
case 0: /* erase from cursor to end of line */
+ offset = 0;
count = vc->vc_cols - vc->vc_x;
- start = (unsigned short *)vc->vc_pos;
break;
case 1: /* erase from start of line to cursor */
- start = (unsigned short *)(vc->vc_pos - (vc->vc_x << 1));
+ offset = -vc->vc_x;
count = vc->vc_x + 1;
break;
case 2: /* erase whole line */
- start = (unsigned short *)(vc->vc_pos - (vc->vc_x << 1));
+ offset = -vc->vc_x;
count = vc->vc_cols;
break;
default:
return;
}
- scr_memsetw(start, vc->vc_video_erase_char, 2 * count);
+ vc_uniscr_clear_line(vc, vc->vc_x + offset, count);
+ scr_memsetw(start + offset, vc->vc_video_erase_char, 2 * count);
vc->vc_need_wrap = 0;
if (con_should_update(vc))
do_update_region(vc, (unsigned long) start, count);
@@ -1232,6 +1514,7 @@ static void csi_X(struct vc_data *vc, int vpar) /* erase the following vpar posi
vpar++;
count = (vpar > vc->vc_cols - vc->vc_x) ? (vc->vc_cols - vc->vc_x) : vpar;
+ vc_uniscr_clear_line(vc, vc->vc_x, count);
scr_memsetw((unsigned short *)vc->vc_pos, vc->vc_video_erase_char, 2 * count);
if (con_should_update(vc))
vc->vc_sw->con_clear(vc, vc->vc_y, vc->vc_x, 1, count);
@@ -2188,7 +2471,7 @@ static void con_flush(struct vc_data *vc, unsigned long draw_from,
/* acquires console_lock */
static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int count)
{
- int c, tc, ok, n = 0, draw_x = -1;
+ int c, next_c, tc, ok, n = 0, draw_x = -1;
unsigned int currcons;
unsigned long draw_from = 0, draw_to = 0;
struct vc_data *vc;
@@ -2382,6 +2665,7 @@ rescan_last_byte:
con_flush(vc, draw_from, draw_to, &draw_x);
}
+ next_c = c;
while (1) {
if (vc->vc_need_wrap || vc->vc_decim)
con_flush(vc, draw_from, draw_to,
@@ -2392,6 +2676,7 @@ rescan_last_byte:
}
if (vc->vc_decim)
insert_char(vc, 1);
+ vc_uniscr_putc(vc, next_c);
scr_writew(himask ?
((vc_attr << 8) & ~himask) + ((tc & 0x100) ? himask : 0) + (tc & 0xff) :
(vc_attr << 8) + tc,
@@ -2412,6 +2697,7 @@ rescan_last_byte:
tc = conv_uni_to_pc(vc, ' '); /* A space is printed in the second column */
if (tc < 0) tc = ' ';
+ next_c = ' ';
}
notify_write(vc, c);