--- a/include/linux/consolemap.h 2003-05-04 19:53:56.000000000 -0400 +++ b/include/linux/consolemap.h 2003-05-26 15:17:45.000000000 -0400 @@ -10,6 +10,6 @@ struct vc_data; -extern unsigned char inverse_translate(struct vc_data *conp, int glyph); +extern u16 inverse_translate(struct vc_data *conp, int glyph, int use_unicode); extern unsigned short *set_translate(int m,int currcons); extern int conv_uni_to_pc(struct vc_data *conp, long ucs); --- a/drivers/char/consolemap.c 2003-05-04 19:53:36.000000000 -0400 +++ b/drivers/char/consolemap.c 2003-05-31 13:57:53.000000000 -0400 @@ -178,6 +178,7 @@ unsigned long refcount; unsigned long sum; unsigned char *inverse_translations[4]; + u16 *inverse_trans_unicode; int readonly; }; @@ -208,6 +209,40 @@ } } +static void set_inverse_trans_unicode(struct vc_data *conp, + struct uni_pagedir *p) +{ + int i, j, k, glyph; + u16 **p1, *p2; + u16 *q; + + q = p->inverse_trans_unicode; + if (!q) { + q = p->inverse_trans_unicode = + kmalloc(MAX_GLYPH * sizeof(u16), GFP_KERNEL); + if (!q) + return; + } + memset(q, 0, MAX_GLYPH * sizeof(u16)); + + for (i = 0; i < 32; i++) { + p1 = p->uni_pgdir[i]; + if (!p1) + continue; + for (j = 0; j < 32; j++) { + p2 = p1[j]; + if (!p2) + continue; + for (k = 0; k < 64; k++) { + glyph = p2[k]; + if (glyph >= 0 && glyph < MAX_GLYPH + && q[glyph] < 32) + q[glyph] = (i << 11) + (j << 6) + k; + } + } + } +} + unsigned short *set_translate(int m,int currcons) { inv_translate[currcons] = m; @@ -218,19 +253,29 @@ * Inverse translation is impossible for several reasons: * 1. The font<->character maps are not 1-1. * 2. The text may have been written while a different translation map - * was active, or using Unicode. + * was active. * Still, it is now possible to a certain extent to cut and paste non-ASCII. */ -unsigned char inverse_translate(struct vc_data *conp, int glyph) +u16 inverse_translate(struct vc_data *conp, int glyph, int use_unicode) { struct uni_pagedir *p; + int m; if (glyph < 0 || glyph >= MAX_GLYPH) return 0; - else if (!(p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc) || - !p->inverse_translations[inv_translate[conp->vc_num]]) + else if (!(p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc)) return glyph; - else - return p->inverse_translations[inv_translate[conp->vc_num]][glyph]; + else if (use_unicode) { + if (!p->inverse_trans_unicode) + return glyph; + else + return p->inverse_trans_unicode[glyph]; + } else { + m = inv_translate[conp->vc_num]; + if (!p->inverse_translations[m]) + return glyph; + else + return p->inverse_translations[m][glyph]; + } } static void update_user_maps(void) @@ -362,6 +407,10 @@ kfree(p->inverse_translations[i]); p->inverse_translations[i] = NULL; } + if (p->inverse_trans_unicode) { + kfree(p->inverse_trans_unicode); + p->inverse_trans_unicode = NULL; + } } void con_free_unimap(int con) @@ -522,6 +571,7 @@ for (i = 0; i <= 3; i++) set_inverse_transl(conp, p, i); /* Update all inverse translations */ + set_inverse_trans_unicode(conp, p); return err; } @@ -574,6 +624,7 @@ for (i = 0; i <= 3; i++) set_inverse_transl(conp, p, i); /* Update all inverse translations */ + set_inverse_trans_unicode(conp, p); dflt = p; return err; } --- a/drivers/char/selection.c 2003-05-04 19:53:42.000000000 -0400 +++ b/drivers/char/selection.c 2003-05-31 13:54:17.000000000 -0400 @@ -20,6 +20,7 @@ #include +#include #include #include #include @@ -36,6 +37,7 @@ /* Variables for selection control. */ /* Use a dynamic buffer, instead of static (Dec 1994) */ int sel_cons; /* must not be disallocated */ +static int use_unicode; static volatile int sel_start = -1; /* cleared by clear_selection */ static int sel_end; static int sel_buffer_lth; @@ -56,10 +58,11 @@ complement_pos(sel_cons, where); } -static unsigned char +static u16 sel_pos(int n) { - return inverse_translate(vc_cons[sel_cons].d, screen_glyph(sel_cons, n)); + return inverse_translate(vc_cons[sel_cons].d, screen_glyph(sel_cons, n), + use_unicode); } /* remove the current selection highlight, if any, @@ -88,8 +91,8 @@ 0xFF7FFFFF /* latin-1 accented letters, not division sign */ }; -static inline int inword(const unsigned char c) { - return ( inwordLut[c>>5] >> (c & 0x1F) ) & 1; +static inline int inword(const u16 c) { + return c > 0xff || (( inwordLut[c>>5] >> (c & 0x1F) ) & 1); } /* set inwordLut contents. Invoked by ioctl(). */ @@ -110,13 +113,36 @@ return (v > u) ? u : v; } +/* stores the char in UTF8 and returns the number of bytes used (1-3) */ +int store_utf8(u16 c, char *p) +{ + if (c < 0x80) { + /* 0******* */ + p[0] = c; + return 1; + } else if (c < 0x800) { + /* 110***** 10****** */ + p[0] = 0xc0 | (c >> 6); + p[1] = 0x80 | (c & 0x3f); + return 2; + } else { + /* 1110**** 10****** 10****** */ + p[0] = 0xe0 | (c >> 12); + p[1] = 0x80 | ((c >> 6) & 0x3f); + p[2] = 0x80 | (c & 0x3f); + return 3; + } +} + /* set the current selection. Invoked by ioctl() or by kernel code. */ int set_selection(const unsigned long arg, struct tty_struct *tty, int user) { int sel_mode, new_sel_start, new_sel_end, spc; char *bp, *obp; - int i, ps, pe; + int i, ps, pe, multiplier; + u16 c; unsigned int currcons = fg_console; + struct kbd_struct *kbd = kbd_table + fg_console; unblank_screen(); poke_blanked_console(); @@ -170,6 +196,7 @@ clear_selection(); sel_cons = fg_console; } + use_unicode = kbd && kbd->kbdmode == VC_UNICODE; switch (sel_mode) { @@ -252,7 +279,8 @@ sel_end = new_sel_end; /* Allocate a new buffer before freeing the old one ... */ - bp = kmalloc((sel_end-sel_start)/2+1, GFP_KERNEL); + multiplier = use_unicode ? 3 : 1; /* chars can take up to 3 bytes */ + bp = kmalloc((sel_end-sel_start)/2*multiplier+1, GFP_KERNEL); if (!bp) { printk(KERN_WARNING "selection: kmalloc() failed\n"); clear_selection(); @@ -264,8 +292,12 @@ obp = bp; for (i = sel_start; i <= sel_end; i += 2) { - *bp = sel_pos(i); - if (!isspace(*bp++)) + c = sel_pos(i); + if (use_unicode) + bp += store_utf8(c, bp); + else + *bp++ = c; + if (!isspace(c)) obp = bp; if (! ((i + 2) % video_size_row)) { /* strip trailing blanks from line and add newline,