diff options
author | Nicolas Pitre <nicolas.pitre@linaro.org> | 2018-06-26 23:56:41 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-06-28 08:38:12 -0400 |
commit | d21b0be246bf3bbf569e6e239f56abb529c7154e (patch) | |
tree | d3d40eba5bb90d02875e55f49e624e9d3b7a82f9 | |
parent | d8ae7242718738ee1bf9bfdd632d2a4b150fdd26 (diff) |
vt: introduce unicode mode for /dev/vcs
Now that the core vt code knows how to preserve unicode values for each
displayed character, it is then possible to let user space access it via
/dev/vcs*.
Unicode characters are presented as 32 bit values in native endianity
via the /dev/vcsu* devices, mimicking the simple /dev/vcs* devices.
Unicode with attributes (similarly to /dev/vcsa*) is not supported at
the moment.
Data is available only as long as the console is in UTF-8 mode. ENODATA
is returned otherwise.
This was tested with the latest development version (to become
version 5.7) of BRLTTY. Amongst other things, this allows ⠋⠕⠗ ⠞⠓⠊⠎
⠃⠗⠁⠊⠇⠇⠑⠀⠞⠑⠭⠞⠀to appear directly on braille displays regardless of the
console font being used.
Signed-off-by: Nicolas Pitre <nico@linaro.org>
Tested-by: Dave Mielke <Dave@mielke.cc>
Acked-by: Adam Borowski <kilobyte@angband.pl>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/tty/vt/vc_screen.c | 89 | ||||
-rw-r--r-- | drivers/tty/vt/vt.c | 61 | ||||
-rw-r--r-- | include/linux/selection.h | 5 |
3 files changed, 141 insertions, 14 deletions
diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c index e4a66e1fd05f..9c44252e52a3 100644 --- a/drivers/tty/vt/vc_screen.c +++ b/drivers/tty/vt/vc_screen.c | |||
@@ -10,6 +10,12 @@ | |||
10 | * Attribute/character pair is in native endianity. | 10 | * Attribute/character pair is in native endianity. |
11 | * [minor: N+128] | 11 | * [minor: N+128] |
12 | * | 12 | * |
13 | * /dev/vcsuN: similar to /dev/vcsaN but using 4-byte unicode values | ||
14 | * instead of 1-byte screen glyph values. | ||
15 | * [minor: N+64] | ||
16 | * | ||
17 | * /dev/vcsuaN: same idea as /dev/vcsaN for unicode (not yet implemented). | ||
18 | * | ||
13 | * This replaces screendump and part of selection, so that the system | 19 | * This replaces screendump and part of selection, so that the system |
14 | * administrator can control access using file system permissions. | 20 | * administrator can control access using file system permissions. |
15 | * | 21 | * |
@@ -51,6 +57,26 @@ | |||
51 | 57 | ||
52 | #define CON_BUF_SIZE (CONFIG_BASE_SMALL ? 256 : PAGE_SIZE) | 58 | #define CON_BUF_SIZE (CONFIG_BASE_SMALL ? 256 : PAGE_SIZE) |
53 | 59 | ||
60 | /* | ||
61 | * Our minor space: | ||
62 | * | ||
63 | * 0 ... 63 glyph mode without attributes | ||
64 | * 64 ... 127 unicode mode without attributes | ||
65 | * 128 ... 191 glyph mode with attributes | ||
66 | * 192 ... 255 unused (reserved for unicode with attributes) | ||
67 | * | ||
68 | * This relies on MAX_NR_CONSOLES being <= 63, meaning 63 actual consoles | ||
69 | * with minors 0, 64, 128 and 192 being proxies for the foreground console. | ||
70 | */ | ||
71 | #if MAX_NR_CONSOLES > 63 | ||
72 | #warning "/dev/vcs* devices may not accommodate more than 63 consoles" | ||
73 | #endif | ||
74 | |||
75 | #define console(inode) (iminor(inode) & 63) | ||
76 | #define use_unicode(inode) (iminor(inode) & 64) | ||
77 | #define use_attributes(inode) (iminor(inode) & 128) | ||
78 | |||
79 | |||
54 | struct vcs_poll_data { | 80 | struct vcs_poll_data { |
55 | struct notifier_block notifier; | 81 | struct notifier_block notifier; |
56 | unsigned int cons_num; | 82 | unsigned int cons_num; |
@@ -102,7 +128,7 @@ vcs_poll_data_get(struct file *file) | |||
102 | poll = kzalloc(sizeof(*poll), GFP_KERNEL); | 128 | poll = kzalloc(sizeof(*poll), GFP_KERNEL); |
103 | if (!poll) | 129 | if (!poll) |
104 | return NULL; | 130 | return NULL; |
105 | poll->cons_num = iminor(file_inode(file)) & 127; | 131 | poll->cons_num = console(file_inode(file)); |
106 | init_waitqueue_head(&poll->waitq); | 132 | init_waitqueue_head(&poll->waitq); |
107 | poll->notifier.notifier_call = vcs_notifier; | 133 | poll->notifier.notifier_call = vcs_notifier; |
108 | if (register_vt_notifier(&poll->notifier) != 0) { | 134 | if (register_vt_notifier(&poll->notifier) != 0) { |
@@ -140,7 +166,7 @@ vcs_poll_data_get(struct file *file) | |||
140 | static struct vc_data* | 166 | static struct vc_data* |
141 | vcs_vc(struct inode *inode, int *viewed) | 167 | vcs_vc(struct inode *inode, int *viewed) |
142 | { | 168 | { |
143 | unsigned int currcons = iminor(inode) & 127; | 169 | unsigned int currcons = console(inode); |
144 | 170 | ||
145 | WARN_CONSOLE_UNLOCKED(); | 171 | WARN_CONSOLE_UNLOCKED(); |
146 | 172 | ||
@@ -164,7 +190,6 @@ static int | |||
164 | vcs_size(struct inode *inode) | 190 | vcs_size(struct inode *inode) |
165 | { | 191 | { |
166 | int size; | 192 | int size; |
167 | int minor = iminor(inode); | ||
168 | struct vc_data *vc; | 193 | struct vc_data *vc; |
169 | 194 | ||
170 | WARN_CONSOLE_UNLOCKED(); | 195 | WARN_CONSOLE_UNLOCKED(); |
@@ -175,8 +200,12 @@ vcs_size(struct inode *inode) | |||
175 | 200 | ||
176 | size = vc->vc_rows * vc->vc_cols; | 201 | size = vc->vc_rows * vc->vc_cols; |
177 | 202 | ||
178 | if (minor & 128) | 203 | if (use_attributes(inode)) { |
204 | if (use_unicode(inode)) | ||
205 | return -EOPNOTSUPP; | ||
179 | size = 2*size + HEADER_SIZE; | 206 | size = 2*size + HEADER_SIZE; |
207 | } else if (use_unicode(inode)) | ||
208 | size *= 4; | ||
180 | return size; | 209 | return size; |
181 | } | 210 | } |
182 | 211 | ||
@@ -197,12 +226,10 @@ static ssize_t | |||
197 | vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | 226 | vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) |
198 | { | 227 | { |
199 | struct inode *inode = file_inode(file); | 228 | struct inode *inode = file_inode(file); |
200 | unsigned int currcons = iminor(inode); | ||
201 | struct vc_data *vc; | 229 | struct vc_data *vc; |
202 | struct vcs_poll_data *poll; | 230 | struct vcs_poll_data *poll; |
203 | long pos; | 231 | long pos, read; |
204 | long attr, read; | 232 | int attr, uni_mode, row, col, maxcol, viewed; |
205 | int col, maxcol, viewed; | ||
206 | unsigned short *org = NULL; | 233 | unsigned short *org = NULL; |
207 | ssize_t ret; | 234 | ssize_t ret; |
208 | char *con_buf; | 235 | char *con_buf; |
@@ -218,7 +245,8 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | |||
218 | */ | 245 | */ |
219 | console_lock(); | 246 | console_lock(); |
220 | 247 | ||
221 | attr = (currcons & 128); | 248 | uni_mode = use_unicode(inode); |
249 | attr = use_attributes(inode); | ||
222 | ret = -ENXIO; | 250 | ret = -ENXIO; |
223 | vc = vcs_vc(inode, &viewed); | 251 | vc = vcs_vc(inode, &viewed); |
224 | if (!vc) | 252 | if (!vc) |
@@ -227,6 +255,10 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | |||
227 | ret = -EINVAL; | 255 | ret = -EINVAL; |
228 | if (pos < 0) | 256 | if (pos < 0) |
229 | goto unlock_out; | 257 | goto unlock_out; |
258 | /* we enforce 32-bit alignment for pos and count in unicode mode */ | ||
259 | if (uni_mode && (pos | count) & 3) | ||
260 | goto unlock_out; | ||
261 | |||
230 | poll = file->private_data; | 262 | poll = file->private_data; |
231 | if (count && poll) | 263 | if (count && poll) |
232 | poll->seen_last_update = true; | 264 | poll->seen_last_update = true; |
@@ -266,7 +298,27 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | |||
266 | con_buf_start = con_buf0 = con_buf; | 298 | con_buf_start = con_buf0 = con_buf; |
267 | orig_count = this_round; | 299 | orig_count = this_round; |
268 | maxcol = vc->vc_cols; | 300 | maxcol = vc->vc_cols; |
269 | if (!attr) { | 301 | if (uni_mode) { |
302 | unsigned int nr; | ||
303 | |||
304 | ret = vc_uniscr_check(vc); | ||
305 | if (ret) | ||
306 | break; | ||
307 | p /= 4; | ||
308 | row = p / vc->vc_cols; | ||
309 | col = p % maxcol; | ||
310 | nr = maxcol - col; | ||
311 | do { | ||
312 | if (nr > this_round/4) | ||
313 | nr = this_round/4; | ||
314 | vc_uniscr_copy_line(vc, con_buf0, row, col, nr); | ||
315 | con_buf0 += nr * 4; | ||
316 | this_round -= nr * 4; | ||
317 | row++; | ||
318 | col = 0; | ||
319 | nr = maxcol; | ||
320 | } while (this_round); | ||
321 | } else if (!attr) { | ||
270 | org = screen_pos(vc, p, viewed); | 322 | org = screen_pos(vc, p, viewed); |
271 | col = p % maxcol; | 323 | col = p % maxcol; |
272 | p += maxcol - col; | 324 | p += maxcol - col; |
@@ -375,7 +427,6 @@ static ssize_t | |||
375 | vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) | 427 | vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) |
376 | { | 428 | { |
377 | struct inode *inode = file_inode(file); | 429 | struct inode *inode = file_inode(file); |
378 | unsigned int currcons = iminor(inode); | ||
379 | struct vc_data *vc; | 430 | struct vc_data *vc; |
380 | long pos; | 431 | long pos; |
381 | long attr, size, written; | 432 | long attr, size, written; |
@@ -396,7 +447,7 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) | |||
396 | */ | 447 | */ |
397 | console_lock(); | 448 | console_lock(); |
398 | 449 | ||
399 | attr = (currcons & 128); | 450 | attr = use_attributes(inode); |
400 | ret = -ENXIO; | 451 | ret = -ENXIO; |
401 | vc = vcs_vc(inode, &viewed); | 452 | vc = vcs_vc(inode, &viewed); |
402 | if (!vc) | 453 | if (!vc) |
@@ -593,9 +644,15 @@ vcs_fasync(int fd, struct file *file, int on) | |||
593 | static int | 644 | static int |
594 | vcs_open(struct inode *inode, struct file *filp) | 645 | vcs_open(struct inode *inode, struct file *filp) |
595 | { | 646 | { |
596 | unsigned int currcons = iminor(inode) & 127; | 647 | unsigned int currcons = console(inode); |
648 | bool attr = use_attributes(inode); | ||
649 | bool uni_mode = use_unicode(inode); | ||
597 | int ret = 0; | 650 | int ret = 0; |
598 | 651 | ||
652 | /* we currently don't support attributes in unicode mode */ | ||
653 | if (attr && uni_mode) | ||
654 | return -EOPNOTSUPP; | ||
655 | |||
599 | console_lock(); | 656 | console_lock(); |
600 | if(currcons && !vc_cons_allocated(currcons-1)) | 657 | if(currcons && !vc_cons_allocated(currcons-1)) |
601 | ret = -ENXIO; | 658 | ret = -ENXIO; |
@@ -628,6 +685,8 @@ void vcs_make_sysfs(int index) | |||
628 | { | 685 | { |
629 | device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 1), NULL, | 686 | device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 1), NULL, |
630 | "vcs%u", index + 1); | 687 | "vcs%u", index + 1); |
688 | device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 65), NULL, | ||
689 | "vcsu%u", index + 1); | ||
631 | device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 129), NULL, | 690 | device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 129), NULL, |
632 | "vcsa%u", index + 1); | 691 | "vcsa%u", index + 1); |
633 | } | 692 | } |
@@ -635,6 +694,7 @@ void vcs_make_sysfs(int index) | |||
635 | void vcs_remove_sysfs(int index) | 694 | void vcs_remove_sysfs(int index) |
636 | { | 695 | { |
637 | device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 1)); | 696 | device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 1)); |
697 | device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 65)); | ||
638 | device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 129)); | 698 | device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 129)); |
639 | } | 699 | } |
640 | 700 | ||
@@ -647,6 +707,7 @@ int __init vcs_init(void) | |||
647 | vc_class = class_create(THIS_MODULE, "vc"); | 707 | vc_class = class_create(THIS_MODULE, "vc"); |
648 | 708 | ||
649 | device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 0), NULL, "vcs"); | 709 | device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 0), NULL, "vcs"); |
710 | device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 64), NULL, "vcsu"); | ||
650 | device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 128), NULL, "vcsa"); | 711 | device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 128), NULL, "vcsa"); |
651 | for (i = 0; i < MIN_NR_CONSOLES; i++) | 712 | for (i = 0; i < MIN_NR_CONSOLES; i++) |
652 | vcs_make_sysfs(i); | 713 | vcs_make_sysfs(i); |
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 7b636638b36d..062ce6be7957 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c | |||
@@ -481,6 +481,67 @@ static void vc_uniscr_copy_area(struct uni_screen *dst, | |||
481 | } | 481 | } |
482 | } | 482 | } |
483 | 483 | ||
484 | /* | ||
485 | * Called from vcs_read() to make sure unicode screen retrieval is possible. | ||
486 | * This will initialize the unicode screen buffer if not already done. | ||
487 | * This returns 0 if OK, or a negative error code otherwise. | ||
488 | * In particular, -ENODATA is returned if the console is not in UTF-8 mode. | ||
489 | */ | ||
490 | int vc_uniscr_check(struct vc_data *vc) | ||
491 | { | ||
492 | struct uni_screen *uniscr; | ||
493 | unsigned short *p; | ||
494 | int x, y, mask; | ||
495 | |||
496 | if (__is_defined(NO_VC_UNI_SCREEN)) | ||
497 | return -EOPNOTSUPP; | ||
498 | |||
499 | WARN_CONSOLE_UNLOCKED(); | ||
500 | |||
501 | if (!vc->vc_utf) | ||
502 | return -ENODATA; | ||
503 | |||
504 | if (vc->vc_uni_screen) | ||
505 | return 0; | ||
506 | |||
507 | uniscr = vc_uniscr_alloc(vc->vc_cols, vc->vc_rows); | ||
508 | if (!uniscr) | ||
509 | return -ENOMEM; | ||
510 | |||
511 | /* | ||
512 | * Let's populate it initially with (imperfect) reverse translation. | ||
513 | * This is the next best thing we can do short of having it enabled | ||
514 | * from the start even when no users rely on this functionality. True | ||
515 | * unicode content will be available after a complete screen refresh. | ||
516 | */ | ||
517 | p = (unsigned short *)vc->vc_origin; | ||
518 | mask = vc->vc_hi_font_mask | 0xff; | ||
519 | for (y = 0; y < vc->vc_rows; y++) { | ||
520 | char32_t *line = uniscr->lines[y]; | ||
521 | for (x = 0; x < vc->vc_cols; x++) { | ||
522 | u16 glyph = scr_readw(p++) & mask; | ||
523 | line[x] = inverse_translate(vc, glyph, true); | ||
524 | } | ||
525 | } | ||
526 | |||
527 | vc->vc_uni_screen = uniscr; | ||
528 | return 0; | ||
529 | } | ||
530 | |||
531 | /* | ||
532 | * Called from vcs_read() to get the unicode data from the screen. | ||
533 | * This must be preceded by a successful call to vc_uniscr_check() once | ||
534 | * the console lock has been taken. | ||
535 | */ | ||
536 | void vc_uniscr_copy_line(struct vc_data *vc, void *dest, | ||
537 | unsigned int row, unsigned int col, unsigned int nr) | ||
538 | { | ||
539 | struct uni_screen *uniscr = get_vc_uniscr(vc); | ||
540 | |||
541 | BUG_ON(!uniscr); | ||
542 | memcpy(dest, &uniscr->lines[row][col], nr * sizeof(char32_t)); | ||
543 | } | ||
544 | |||
484 | 545 | ||
485 | static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b, | 546 | static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b, |
486 | enum con_scroll dir, unsigned int nr) | 547 | enum con_scroll dir, unsigned int nr) |
diff --git a/include/linux/selection.h b/include/linux/selection.h index 5b278ce99d8d..2b34df9f1e26 100644 --- a/include/linux/selection.h +++ b/include/linux/selection.h | |||
@@ -42,4 +42,9 @@ extern u16 vcs_scr_readw(struct vc_data *vc, const u16 *org); | |||
42 | extern void vcs_scr_writew(struct vc_data *vc, u16 val, u16 *org); | 42 | extern void vcs_scr_writew(struct vc_data *vc, u16 val, u16 *org); |
43 | extern void vcs_scr_updated(struct vc_data *vc); | 43 | extern void vcs_scr_updated(struct vc_data *vc); |
44 | 44 | ||
45 | extern int vc_uniscr_check(struct vc_data *vc); | ||
46 | extern void vc_uniscr_copy_line(struct vc_data *vc, void *dest, | ||
47 | unsigned int row, unsigned int col, | ||
48 | unsigned int nr); | ||
49 | |||
45 | #endif | 50 | #endif |