]>
Commit | Line | Data |
---|---|---|
f59e1e88 MT |
1 | Submitted by: Alexander E. Patrakov <patrakov@ums.usu.ru> |
2 | Signed-off-by: Alexander E. Patrakov <patrakov@ums.usu.ru> | |
3 | Date: 2005-10-18 | |
4 | Initial Package Version: 2.6.15 | |
5 | Upstream Status: Rejected: they say it modifies the meaning of an existing ioctl | |
6 | Origin: http://chris.heathens.co.nz/linux/downloads/patches-2.6.4-cdh1.tar.gz | |
7 | Porting to linux-2.6.16 by Alexander E. Patrakov | |
8 | Description: This patch fixes dead keys and copy/paste of non-ASCII characters | |
9 | in UTF-8 mode on Linux console. | |
10 | See more details about the original patch at: | |
11 | http://chris.heathens.co.nz/linux/utf8.html | |
12 | ||
13 | diff -ur linux-2.6.15-rc6.orig/drivers/char/consolemap.c linux-2.6.15-rc6.my/drivers/char/consolemap.c | |
14 | --- linux-2.6.15-rc6.orig/drivers/char/consolemap.c 2005-12-25 10:00:12.000000000 +0500 | |
15 | +++ linux-2.6.15-rc6.my/drivers/char/consolemap.c 2005-12-25 10:01:22.000000000 +0500 | |
16 | @@ -178,6 +178,7 @@ | |
17 | unsigned long refcount; | |
18 | unsigned long sum; | |
19 | unsigned char *inverse_translations[4]; | |
20 | + u16 *inverse_trans_unicode; | |
21 | int readonly; | |
22 | }; | |
23 | ||
24 | @@ -208,6 +209,41 @@ | |
25 | } | |
26 | } | |
27 | ||
28 | +static void set_inverse_trans_unicode(struct vc_data *conp, | |
29 | + struct uni_pagedir *p) | |
30 | +{ | |
31 | + int i, j, k, glyph; | |
32 | + u16 **p1, *p2; | |
33 | + u16 *q; | |
34 | + | |
35 | + if (!p) return; | |
36 | + q = p->inverse_trans_unicode; | |
37 | + if (!q) { | |
38 | + q = p->inverse_trans_unicode = | |
39 | + kmalloc(MAX_GLYPH * sizeof(u16), GFP_KERNEL); | |
40 | + if (!q) | |
41 | + return; | |
42 | + } | |
43 | + memset(q, 0, MAX_GLYPH * sizeof(u16)); | |
44 | + | |
45 | + for (i = 0; i < 32; i++) { | |
46 | + p1 = p->uni_pgdir[i]; | |
47 | + if (!p1) | |
48 | + continue; | |
49 | + for (j = 0; j < 32; j++) { | |
50 | + p2 = p1[j]; | |
51 | + if (!p2) | |
52 | + continue; | |
53 | + for (k = 0; k < 64; k++) { | |
54 | + glyph = p2[k]; | |
55 | + if (glyph >= 0 && glyph < MAX_GLYPH | |
56 | + && q[glyph] < 32) | |
57 | + q[glyph] = (i << 11) + (j << 6) + k; | |
58 | + } | |
59 | + } | |
60 | + } | |
61 | +} | |
62 | + | |
63 | unsigned short *set_translate(int m, struct vc_data *vc) | |
64 | { | |
65 | inv_translate[vc->vc_num] = m; | |
66 | @@ -218,19 +254,29 @@ | |
67 | * Inverse translation is impossible for several reasons: | |
68 | * 1. The font<->character maps are not 1-1. | |
69 | * 2. The text may have been written while a different translation map | |
70 | - * was active, or using Unicode. | |
71 | + * was active. | |
72 | * Still, it is now possible to a certain extent to cut and paste non-ASCII. | |
73 | */ | |
74 | -unsigned char inverse_translate(struct vc_data *conp, int glyph) | |
75 | +u16 inverse_translate(struct vc_data *conp, int glyph, int use_unicode) | |
76 | { | |
77 | struct uni_pagedir *p; | |
78 | + int m; | |
79 | if (glyph < 0 || glyph >= MAX_GLYPH) | |
80 | return 0; | |
81 | - else if (!(p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc) || | |
82 | - !p->inverse_translations[inv_translate[conp->vc_num]]) | |
83 | + else if (!(p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc)) | |
84 | return glyph; | |
85 | - else | |
86 | - return p->inverse_translations[inv_translate[conp->vc_num]][glyph]; | |
87 | + else if (use_unicode) { | |
88 | + if (!p->inverse_trans_unicode) | |
89 | + return glyph; | |
90 | + else | |
91 | + return p->inverse_trans_unicode[glyph]; | |
92 | + } else { | |
93 | + m = inv_translate[conp->vc_num]; | |
94 | + if (!p->inverse_translations[m]) | |
95 | + return glyph; | |
96 | + else | |
97 | + return p->inverse_translations[m][glyph]; | |
98 | + } | |
99 | } | |
100 | ||
101 | static void update_user_maps(void) | |
102 | @@ -244,6 +290,7 @@ | |
103 | p = (struct uni_pagedir *)*vc_cons[i].d->vc_uni_pagedir_loc; | |
104 | if (p && p != q) { | |
105 | set_inverse_transl(vc_cons[i].d, p, USER_MAP); | |
106 | + set_inverse_trans_unicode(vc_cons[i].d, p); | |
107 | q = p; | |
108 | } | |
109 | } | |
110 | @@ -354,6 +401,10 @@ | |
111 | kfree(p->inverse_translations[i]); | |
112 | p->inverse_translations[i] = NULL; | |
113 | } | |
114 | + if (p->inverse_trans_unicode) { | |
115 | + kfree(p->inverse_trans_unicode); | |
116 | + p->inverse_trans_unicode = NULL; | |
117 | + } | |
118 | } | |
119 | ||
120 | void con_free_unimap(struct vc_data *vc) | |
121 | @@ -512,6 +563,7 @@ | |
122 | ||
123 | for (i = 0; i <= 3; i++) | |
124 | set_inverse_transl(vc, p, i); /* Update all inverse translations */ | |
125 | + set_inverse_trans_unicode(vc, p); | |
126 | ||
127 | return err; | |
128 | } | |
129 | @@ -562,6 +614,7 @@ | |
130 | ||
131 | for (i = 0; i <= 3; i++) | |
132 | set_inverse_transl(vc, p, i); /* Update all inverse translations */ | |
133 | + set_inverse_trans_unicode(vc, p); | |
134 | dflt = p; | |
135 | return err; | |
136 | } | |
137 | @@ -618,6 +671,19 @@ | |
138 | p->readonly = rdonly; | |
139 | } | |
140 | ||
141 | +/* may be called during an interrupt */ | |
142 | +u32 conv_8bit_to_uni(unsigned char c) | |
143 | +{ | |
144 | + /* | |
145 | + * Always use USER_MAP. This function is used by the keyboard, | |
146 | + * which shouldn't be affected by G0/G1 switching, etc. | |
147 | + * If the user map still contains default values, i.e. the | |
148 | + * direct-to-font mapping, then assume user is using Latin1. | |
149 | + */ | |
150 | + unsigned short uni = translations[USER_MAP][c]; | |
151 | + return uni == (0xf000 | c) ? c : uni; | |
152 | +} | |
153 | + | |
154 | int | |
155 | conv_uni_to_pc(struct vc_data *conp, long ucs) | |
156 | { | |
157 | diff -ur linux-2.6.15-rc6.orig/drivers/char/keyboard.c linux-2.6.15-rc6.my/drivers/char/keyboard.c | |
158 | --- linux-2.6.15-rc6.orig/drivers/char/keyboard.c 2005-12-25 10:00:12.000000000 +0500 | |
159 | +++ linux-2.6.15-rc6.my/drivers/char/keyboard.c 2005-12-25 10:01:22.000000000 +0500 | |
160 | @@ -34,6 +34,7 @@ | |
161 | #include <linux/init.h> | |
162 | #include <linux/slab.h> | |
163 | ||
164 | +#include <linux/consolemap.h> | |
165 | #include <linux/kbd_kern.h> | |
166 | #include <linux/kbd_diacr.h> | |
167 | #include <linux/vt_kern.h> | |
168 | @@ -329,10 +330,9 @@ | |
169 | * Many other routines do put_queue, but I think either | |
170 | * they produce ASCII, or they produce some user-assigned | |
171 | * string, and in both cases we might assume that it is | |
172 | - * in utf-8 already. UTF-8 is defined for words of up to 31 bits, | |
173 | - * but we need only 16 bits here | |
174 | + * in utf-8 already. | |
175 | */ | |
176 | -static void to_utf8(struct vc_data *vc, ushort c) | |
177 | +static void to_utf8(struct vc_data *vc, uint c) | |
178 | { | |
179 | if (c < 0x80) | |
180 | /* 0******* */ | |
181 | @@ -341,14 +341,33 @@ | |
182 | /* 110***** 10****** */ | |
183 | put_queue(vc, 0xc0 | (c >> 6)); | |
184 | put_queue(vc, 0x80 | (c & 0x3f)); | |
185 | - } else { | |
186 | + } else if (c < 0x10000) { | |
187 | + if (c >= 0xD800 && c < 0xE000) | |
188 | + return; | |
189 | + if (c == 0xFFFF) | |
190 | + return; | |
191 | /* 1110**** 10****** 10****** */ | |
192 | put_queue(vc, 0xe0 | (c >> 12)); | |
193 | put_queue(vc, 0x80 | ((c >> 6) & 0x3f)); | |
194 | put_queue(vc, 0x80 | (c & 0x3f)); | |
195 | + } else if (c < 0x110000) { | |
196 | + /* 11110*** 10****** 10****** 10****** */ | |
197 | + put_queue(vc, 0xf0 | (c >> 18)); | |
198 | + put_queue(vc, 0x80 | ((c >> 12) & 0x3f)); | |
199 | + put_queue(vc, 0x80 | ((c >> 6) & 0x3f)); | |
200 | + put_queue(vc, 0x80 | (c & 0x3f)); | |
201 | } | |
202 | } | |
203 | ||
204 | +static void put_8bit(struct vc_data *vc, u8 c) | |
205 | +{ | |
206 | + if (kbd->kbdmode != VC_UNICODE || c < 32 || c == 127) | |
207 | + /* Don't translate control chars */ | |
208 | + put_queue(vc, c); | |
209 | + else | |
210 | + to_utf8(vc, conv_8bit_to_uni(c)); | |
211 | +} | |
212 | + | |
213 | /* | |
214 | * Called after returning from RAW mode or when changing consoles - recompute | |
215 | * shift_down[] and shift_state from key_down[] maybe called when keymap is | |
216 | @@ -409,7 +428,7 @@ | |
217 | if (ch == ' ' || ch == d) | |
218 | return d; | |
219 | ||
220 | - put_queue(vc, d); | |
221 | + put_8bit(vc, d); | |
222 | return ch; | |
223 | } | |
224 | ||
225 | @@ -419,7 +438,7 @@ | |
226 | static void fn_enter(struct vc_data *vc, struct pt_regs *regs) | |
227 | { | |
228 | if (diacr) { | |
229 | - put_queue(vc, diacr); | |
230 | + put_8bit(vc, diacr); | |
231 | diacr = 0; | |
232 | } | |
233 | put_queue(vc, 13); | |
234 | @@ -628,7 +647,7 @@ | |
235 | diacr = value; | |
236 | return; | |
237 | } | |
238 | - put_queue(vc, value); | |
239 | + put_8bit(vc, value); | |
240 | } | |
241 | ||
242 | /* | |
243 | @@ -774,7 +793,7 @@ | |
244 | /* kludge */ | |
245 | if (up_flag && shift_state != old_state && npadch != -1) { | |
246 | if (kbd->kbdmode == VC_UNICODE) | |
247 | - to_utf8(vc, npadch & 0xffff); | |
248 | + to_utf8(vc, npadch); | |
249 | else | |
250 | put_queue(vc, npadch & 0xff); | |
251 | npadch = -1; | |
252 | diff -ur linux-2.6.15-rc6.orig/drivers/char/selection.c linux-2.6.15-rc6.my/drivers/char/selection.c | |
253 | --- linux-2.6.15-rc6.orig/drivers/char/selection.c 2005-12-25 10:00:12.000000000 +0500 | |
254 | +++ linux-2.6.15-rc6.my/drivers/char/selection.c 2005-12-25 10:01:22.000000000 +0500 | |
255 | @@ -20,6 +20,7 @@ | |
256 | ||
257 | #include <asm/uaccess.h> | |
258 | ||
259 | +#include <linux/kbd_kern.h> | |
260 | #include <linux/vt_kern.h> | |
261 | #include <linux/consolemap.h> | |
262 | #include <linux/selection.h> | |
263 | @@ -34,6 +35,7 @@ | |
264 | /* Variables for selection control. */ | |
265 | /* Use a dynamic buffer, instead of static (Dec 1994) */ | |
266 | struct vc_data *sel_cons; /* must not be disallocated */ | |
267 | +static int use_unicode; | |
268 | static volatile int sel_start = -1; /* cleared by clear_selection */ | |
269 | static int sel_end; | |
270 | static int sel_buffer_lth; | |
271 | @@ -54,10 +56,11 @@ | |
272 | complement_pos(sel_cons, where); | |
273 | } | |
274 | ||
275 | -static unsigned char | |
276 | +static u16 | |
277 | sel_pos(int n) | |
278 | { | |
279 | - return inverse_translate(sel_cons, screen_glyph(sel_cons, n)); | |
280 | + return inverse_translate(sel_cons, screen_glyph(sel_cons, n), | |
281 | + use_unicode); | |
282 | } | |
283 | ||
284 | /* remove the current selection highlight, if any, | |
285 | @@ -86,8 +89,8 @@ | |
286 | 0xFF7FFFFF /* latin-1 accented letters, not division sign */ | |
287 | }; | |
288 | ||
289 | -static inline int inword(const unsigned char c) { | |
290 | - return ( inwordLut[c>>5] >> (c & 0x1F) ) & 1; | |
291 | +static inline int inword(const u16 c) { | |
292 | + return c > 0xff || (( inwordLut[c>>5] >> (c & 0x1F) ) & 1); | |
293 | } | |
294 | ||
295 | /* set inwordLut contents. Invoked by ioctl(). */ | |
296 | @@ -108,13 +111,36 @@ | |
297 | return (v > u) ? u : v; | |
298 | } | |
299 | ||
300 | +/* stores the char in UTF8 and returns the number of bytes used (1-3) */ | |
301 | +int store_utf8(u16 c, char *p) | |
302 | +{ | |
303 | + if (c < 0x80) { | |
304 | + /* 0******* */ | |
305 | + p[0] = c; | |
306 | + return 1; | |
307 | + } else if (c < 0x800) { | |
308 | + /* 110***** 10****** */ | |
309 | + p[0] = 0xc0 | (c >> 6); | |
310 | + p[1] = 0x80 | (c & 0x3f); | |
311 | + return 2; | |
312 | + } else { | |
313 | + /* 1110**** 10****** 10****** */ | |
314 | + p[0] = 0xe0 | (c >> 12); | |
315 | + p[1] = 0x80 | ((c >> 6) & 0x3f); | |
316 | + p[2] = 0x80 | (c & 0x3f); | |
317 | + return 3; | |
318 | + } | |
319 | +} | |
320 | + | |
321 | /* set the current selection. Invoked by ioctl() or by kernel code. */ | |
322 | int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *tty) | |
323 | { | |
324 | struct vc_data *vc = vc_cons[fg_console].d; | |
325 | int sel_mode, new_sel_start, new_sel_end, spc; | |
326 | char *bp, *obp; | |
327 | - int i, ps, pe; | |
328 | + int i, ps, pe, multiplier; | |
329 | + u16 c; | |
330 | + struct kbd_struct *kbd = kbd_table + fg_console; | |
331 | ||
332 | poke_blanked_console(); | |
333 | ||
334 | @@ -158,7 +184,8 @@ | |
335 | clear_selection(); | |
336 | sel_cons = vc_cons[fg_console].d; | |
337 | } | |
338 | - | |
339 | + use_unicode = kbd && kbd->kbdmode == VC_UNICODE; | |
340 | + | |
341 | switch (sel_mode) | |
342 | { | |
343 | case TIOCL_SELCHAR: /* character-by-character selection */ | |
344 | @@ -240,7 +267,8 @@ | |
345 | sel_end = new_sel_end; | |
346 | ||
347 | /* Allocate a new buffer before freeing the old one ... */ | |
348 | - bp = kmalloc((sel_end-sel_start)/2+1, GFP_KERNEL); | |
349 | + multiplier = use_unicode ? 3 : 1; /* chars can take up to 3 bytes */ | |
350 | + bp = kmalloc((sel_end-sel_start)/2*multiplier+1, GFP_KERNEL); | |
351 | if (!bp) { | |
352 | printk(KERN_WARNING "selection: kmalloc() failed\n"); | |
353 | clear_selection(); | |
354 | @@ -251,8 +279,12 @@ | |
355 | ||
356 | obp = bp; | |
357 | for (i = sel_start; i <= sel_end; i += 2) { | |
358 | - *bp = sel_pos(i); | |
359 | - if (!isspace(*bp++)) | |
360 | + c = sel_pos(i); | |
361 | + if (use_unicode) | |
362 | + bp += store_utf8(c, bp); | |
363 | + else | |
364 | + *bp++ = c; | |
365 | + if (!isspace(c)) | |
366 | obp = bp; | |
367 | if (! ((i + 2) % vc->vc_size_row)) { | |
368 | /* strip trailing blanks from line and add newline, | |
369 | diff -ur linux-2.6.15-rc6.orig/include/linux/consolemap.h linux-2.6.15-rc6.my/include/linux/consolemap.h | |
370 | --- linux-2.6.15-rc6.orig/include/linux/consolemap.h 2005-12-25 10:00:13.000000000 +0500 | |
371 | +++ linux-2.6.15-rc6.my/include/linux/consolemap.h 2005-12-25 10:01:22.000000000 +0500 | |
372 | @@ -10,6 +10,7 @@ | |
373 | ||
374 | struct vc_data; | |
375 | ||
376 | -extern unsigned char inverse_translate(struct vc_data *conp, int glyph); | |
377 | +extern u16 inverse_translate(struct vc_data *conp, int glyph, int use_unicode); | |
378 | extern unsigned short *set_translate(int m, struct vc_data *vc); | |
379 | extern int conv_uni_to_pc(struct vc_data *conp, long ucs); | |
380 | +extern u32 conv_8bit_to_uni(unsigned char c); |