diff options
author | Jan Engelhardt <jengelh@linux01.gwdg.de> | 2007-07-16 02:40:40 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-16 12:05:46 -0400 |
commit | 759448f459234bfcf34b82471f0dba77a9aca498 (patch) | |
tree | 61cbf8501bdad78c03e034072791fbf3e0436d43 /drivers/char/consolemap.c | |
parent | aa0ac36518be648dda3a32f0b37a8b2b546e1b24 (diff) |
Kernel utf-8 handling
This patch fixes dead keys and copy/paste of non-ASCII characters in UTF-8
mode on Linux console. See more details about the original patch at:
http://chris.heathens.co.nz/linux/utf8.html
Already posted on
(Oldest) http://lkml.org/lkml/2003/5/31/148
http://lkml.org/lkml/2005/12/24/69
(Recent) http://lkml.org/lkml/2006/8/7/75
[bunk@stusta.de: make drivers/char/selection.c:store_utf8() static]
Signed-off-by: Jan Engelhardt <jengelh@gmx.de>
Cc: Alexander E. Patrakov <patrakov@ums.usu.ru>
Cc: Dmitry Torokhov <dtor@mail.ru>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Cc: David Woodhouse <dwmw2@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/char/consolemap.c')
-rw-r--r-- | drivers/char/consolemap.c | 78 |
1 files changed, 72 insertions, 6 deletions
diff --git a/drivers/char/consolemap.c b/drivers/char/consolemap.c index fd40b959afdd..4b3916f54909 100644 --- a/drivers/char/consolemap.c +++ b/drivers/char/consolemap.c | |||
@@ -177,6 +177,7 @@ struct uni_pagedir { | |||
177 | unsigned long refcount; | 177 | unsigned long refcount; |
178 | unsigned long sum; | 178 | unsigned long sum; |
179 | unsigned char *inverse_translations[4]; | 179 | unsigned char *inverse_translations[4]; |
180 | u16 *inverse_trans_unicode; | ||
180 | int readonly; | 181 | int readonly; |
181 | }; | 182 | }; |
182 | 183 | ||
@@ -207,6 +208,41 @@ static void set_inverse_transl(struct vc_data *conp, struct uni_pagedir *p, int | |||
207 | } | 208 | } |
208 | } | 209 | } |
209 | 210 | ||
211 | static void set_inverse_trans_unicode(struct vc_data *conp, | ||
212 | struct uni_pagedir *p) | ||
213 | { | ||
214 | int i, j, k, glyph; | ||
215 | u16 **p1, *p2; | ||
216 | u16 *q; | ||
217 | |||
218 | if (!p) return; | ||
219 | q = p->inverse_trans_unicode; | ||
220 | if (!q) { | ||
221 | q = p->inverse_trans_unicode = | ||
222 | kmalloc(MAX_GLYPH * sizeof(u16), GFP_KERNEL); | ||
223 | if (!q) | ||
224 | return; | ||
225 | } | ||
226 | memset(q, 0, MAX_GLYPH * sizeof(u16)); | ||
227 | |||
228 | for (i = 0; i < 32; i++) { | ||
229 | p1 = p->uni_pgdir[i]; | ||
230 | if (!p1) | ||
231 | continue; | ||
232 | for (j = 0; j < 32; j++) { | ||
233 | p2 = p1[j]; | ||
234 | if (!p2) | ||
235 | continue; | ||
236 | for (k = 0; k < 64; k++) { | ||
237 | glyph = p2[k]; | ||
238 | if (glyph >= 0 && glyph < MAX_GLYPH | ||
239 | && q[glyph] < 32) | ||
240 | q[glyph] = (i << 11) + (j << 6) + k; | ||
241 | } | ||
242 | } | ||
243 | } | ||
244 | } | ||
245 | |||
210 | unsigned short *set_translate(int m, struct vc_data *vc) | 246 | unsigned short *set_translate(int m, struct vc_data *vc) |
211 | { | 247 | { |
212 | inv_translate[vc->vc_num] = m; | 248 | inv_translate[vc->vc_num] = m; |
@@ -217,19 +253,29 @@ unsigned short *set_translate(int m, struct vc_data *vc) | |||
217 | * Inverse translation is impossible for several reasons: | 253 | * Inverse translation is impossible for several reasons: |
218 | * 1. The font<->character maps are not 1-1. | 254 | * 1. The font<->character maps are not 1-1. |
219 | * 2. The text may have been written while a different translation map | 255 | * 2. The text may have been written while a different translation map |
220 | * was active, or using Unicode. | 256 | * was active. |
221 | * Still, it is now possible to a certain extent to cut and paste non-ASCII. | 257 | * Still, it is now possible to a certain extent to cut and paste non-ASCII. |
222 | */ | 258 | */ |
223 | unsigned char inverse_translate(struct vc_data *conp, int glyph) | 259 | u16 inverse_translate(struct vc_data *conp, int glyph, int use_unicode) |
224 | { | 260 | { |
225 | struct uni_pagedir *p; | 261 | struct uni_pagedir *p; |
262 | int m; | ||
226 | if (glyph < 0 || glyph >= MAX_GLYPH) | 263 | if (glyph < 0 || glyph >= MAX_GLYPH) |
227 | return 0; | 264 | return 0; |
228 | else if (!(p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc) || | 265 | else if (!(p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc)) |
229 | !p->inverse_translations[inv_translate[conp->vc_num]]) | ||
230 | return glyph; | 266 | return glyph; |
231 | else | 267 | else if (use_unicode) { |
232 | return p->inverse_translations[inv_translate[conp->vc_num]][glyph]; | 268 | if (!p->inverse_trans_unicode) |
269 | return glyph; | ||
270 | else | ||
271 | return p->inverse_trans_unicode[glyph]; | ||
272 | } else { | ||
273 | m = inv_translate[conp->vc_num]; | ||
274 | if (!p->inverse_translations[m]) | ||
275 | return glyph; | ||
276 | else | ||
277 | return p->inverse_translations[m][glyph]; | ||
278 | } | ||
233 | } | 279 | } |
234 | 280 | ||
235 | static void update_user_maps(void) | 281 | static void update_user_maps(void) |
@@ -243,6 +289,7 @@ static void update_user_maps(void) | |||
243 | p = (struct uni_pagedir *)*vc_cons[i].d->vc_uni_pagedir_loc; | 289 | p = (struct uni_pagedir *)*vc_cons[i].d->vc_uni_pagedir_loc; |
244 | if (p && p != q) { | 290 | if (p && p != q) { |
245 | set_inverse_transl(vc_cons[i].d, p, USER_MAP); | 291 | set_inverse_transl(vc_cons[i].d, p, USER_MAP); |
292 | set_inverse_trans_unicode(vc_cons[i].d, p); | ||
246 | q = p; | 293 | q = p; |
247 | } | 294 | } |
248 | } | 295 | } |
@@ -353,6 +400,10 @@ static void con_release_unimap(struct uni_pagedir *p) | |||
353 | kfree(p->inverse_translations[i]); | 400 | kfree(p->inverse_translations[i]); |
354 | p->inverse_translations[i] = NULL; | 401 | p->inverse_translations[i] = NULL; |
355 | } | 402 | } |
403 | if (p->inverse_trans_unicode) { | ||
404 | kfree(p->inverse_trans_unicode); | ||
405 | p->inverse_trans_unicode = NULL; | ||
406 | } | ||
356 | } | 407 | } |
357 | 408 | ||
358 | void con_free_unimap(struct vc_data *vc) | 409 | void con_free_unimap(struct vc_data *vc) |
@@ -511,6 +562,7 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) | |||
511 | 562 | ||
512 | for (i = 0; i <= 3; i++) | 563 | for (i = 0; i <= 3; i++) |
513 | set_inverse_transl(vc, p, i); /* Update all inverse translations */ | 564 | set_inverse_transl(vc, p, i); /* Update all inverse translations */ |
565 | set_inverse_trans_unicode(vc, p); | ||
514 | 566 | ||
515 | return err; | 567 | return err; |
516 | } | 568 | } |
@@ -561,6 +613,7 @@ int con_set_default_unimap(struct vc_data *vc) | |||
561 | 613 | ||
562 | for (i = 0; i <= 3; i++) | 614 | for (i = 0; i <= 3; i++) |
563 | set_inverse_transl(vc, p, i); /* Update all inverse translations */ | 615 | set_inverse_transl(vc, p, i); /* Update all inverse translations */ |
616 | set_inverse_trans_unicode(vc, p); | ||
564 | dflt = p; | 617 | dflt = p; |
565 | return err; | 618 | return err; |
566 | } | 619 | } |
@@ -617,6 +670,19 @@ void con_protect_unimap(struct vc_data *vc, int rdonly) | |||
617 | p->readonly = rdonly; | 670 | p->readonly = rdonly; |
618 | } | 671 | } |
619 | 672 | ||
673 | /* may be called during an interrupt */ | ||
674 | u32 conv_8bit_to_uni(unsigned char c) | ||
675 | { | ||
676 | /* | ||
677 | * Always use USER_MAP. This function is used by the keyboard, | ||
678 | * which shouldn't be affected by G0/G1 switching, etc. | ||
679 | * If the user map still contains default values, i.e. the | ||
680 | * direct-to-font mapping, then assume user is using Latin1. | ||
681 | */ | ||
682 | unsigned short uni = translations[USER_MAP][c]; | ||
683 | return uni == (0xf000 | c) ? c : uni; | ||
684 | } | ||
685 | |||
620 | int | 686 | int |
621 | conv_uni_to_pc(struct vc_data *conp, long ucs) | 687 | conv_uni_to_pc(struct vc_data *conp, long ucs) |
622 | { | 688 | { |