gui-win32: fix encoding and decoding of clipboard strings

On Windows, the clipboard strings are encoded in UTF-16.
However, Drawterm considers them as Rune. It used to work
when a Rune was a short, but a Rune is now a int.
The solution is to implement Rune16 functions to
handle UTF-16 strings.

The Rune16 functions were written by Charles Forsyth
as part of Inferno. Balaji Srinivasa integrated them
into Drawterm.
This commit is contained in:
David du Colombier 2015-06-17 22:40:15 +02:00
parent 164f930802
commit 8de603bff1
5 changed files with 188 additions and 46 deletions

View File

@ -7,7 +7,8 @@ OFILES=\
cload.$O\
draw.$O\
load.$O\
screen.$O
screen.$O\
r16.$O
default: $(LIB)
$(LIB): $(OFILES)

168
gui-win32/r16.c Normal file
View File

@ -0,0 +1,168 @@
#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <u.h>
#include <libc.h>
#include "r16.h"
#define Bit(i) (7-(i))
/* N 0's preceded by i 1's, T(Bit(2)) is 1100 0000 */
#define T(i) (((1 << (Bit(i)+1))-1) ^ 0xFF)
/* 0000 0000 0000 0111 1111 1111 */
#define RuneX(i) ((1 << (Bit(i) + ((i)-1)*Bitx))-1)
enum
{
Bitx = Bit(1),
Tx = T(1), /* 1000 0000 */
Rune1 = (1<<(Bit(0)+0*Bitx))-1, /* 0000 0000 0000 0000 0111 1111 */
Maskx = (1<<Bitx)-1, /* 0011 1111 */
Testx = Maskx ^ 0xFF, /* 1100 0000 */
SurrogateMin = 0xD800,
SurrogateMax = 0xDFFF,
Bad = Runeerror,
};
Rune16*
runes16dup(Rune16 *r)
{
int n;
Rune16 *s;
n = runes16len(r) + 1;
s = malloc(n * sizeof(Rune16));
memmove(s, r, n * sizeof(Rune16));
return s;
}
int
runes16len(Rune16 *r)
{
int n;
n = 0;
while(*r++ != 0)
n++;
return n;
}
char*
runes16toutf(char *p, Rune16 *r, int nc)
{
char *op, *ep;
int n, c;
Rune rc;
op = p;
ep = p + nc;
while(c = *r++) {
n = 1;
if(c >= Runeself)
n = runelen(c);
if(p + n >= ep)
break;
rc = c;
if(c < Runeself)
*p++ = c;
else
p += runetochar(p, &rc);
}
*p = '\0';
return op;
}
int
rune16nlen(Rune16 *r, int nrune)
{
int nb, i;
Rune c;
nb = 0;
while(nrune--) {
c = *r++;
if(c <= Rune1){
nb++;
} else {
for(i = 2; i < UTFmax + 1; i++)
if(c <= RuneX(i) || i == UTFmax){
nb += i;
break;
}
}
}
return nb;
}
Rune16*
utftorunes16(Rune16 *r, char *p, int nc)
{
Rune16 *or, *er;
Rune rc;
or = r;
er = r + nc;
while(*p != '\0' && r + 1 < er){
p += chartorune(&rc, p);
*r++ = rc; /* we'll ignore surrogate pairs */
}
*r = '\0';
return or;
}
int
runes16cmp(Rune16 *s1, Rune16 *s2)
{
Rune16 r1, r2;
for(;;) {
r1 = *s1++;
r2 = *s2++;
if(r1 != r2) {
if(r1 > r2)
return 1;
return -1;
}
if(r1 == 0)
return 0;
}
}
wchar_t *
widen(char *s)
{
int n;
wchar_t *ws;
n = utflen(s) + 1;
ws = smalloc(n*sizeof(wchar_t));
utftorunes16(ws, s, n);
return ws;
}
char *
narrowen(wchar_t *ws)
{
char *s;
int n;
n = widebytes(ws);
s = smalloc(n);
runes16toutf(s, ws, n);
return s;
}
int
widebytes(wchar_t *ws)
{
int n = 0;
while (*ws)
n += runelen(*ws++);
return n+1;
}

11
gui-win32/r16.h Normal file
View File

@ -0,0 +1,11 @@
typedef unsigned short Rune16;
wchar_t *widen(char *s);
char *narrowen(wchar_t *ws);
int widebytes(wchar_t *ws);
int runes16len(Rune16*);
int rune16nlen(Rune16*, int);
Rune16* runes16dup(Rune16*);
Rune16* utftorunes16(Rune16*, char*, int);
char* runes16toutf(char*, Rune16*, int);
int runes16cmp(Rune16*, Rune16*);

View File

@ -14,6 +14,7 @@
#include <memdraw.h>
#include "screen.h"
#include "keyboard.h"
#include "r16.h"
Memimage *gscreen;
Screeninfo screen;
@ -544,14 +545,14 @@ setcolor(ulong index, ulong red, ulong green, ulong blue)
uchar*
clipreadunicode(HANDLE h)
{
Rune *p;
Rune16 *p;
int n;
uchar *q;
p = GlobalLock(h);
n = wstrutflen(p)+1;
n = rune16nlen(p, runes16len(p)+1);
q = malloc(n);
wstrtoutf(q, p, n);
runes16toutf(q, p, n);
GlobalUnlock(h);
return q;
@ -598,7 +599,7 @@ clipwrite(char *buf)
{
HANDLE h;
char *p, *e;
Rune *rp;
Rune16 *rp;
int n = strlen(buf);
if(!OpenClipboard(window)) {
@ -616,11 +617,7 @@ clipwrite(char *buf)
if(h == NULL)
panic("out of memory");
rp = GlobalLock(h);
p = buf;
e = p+n;
while(p<e)
p += chartorune(rp++, p);
*rp = 0;
utftorunes16(rp, buf, n+1);
GlobalUnlock(h);
SetClipboardData(CF_UNICODETEXT, h);

View File

@ -1,35 +0,0 @@
#include <u.h>
#include <libc.h>
int
wstrutflen(Rune *s)
{
int n;
for(n=0; *s; n+=runelen(*s),s++)
;
return n;
}
int
wstrtoutf(char *s, Rune *t, int n)
{
int i;
char *s0;
s0 = s;
if(n <= 0)
return wstrutflen(t)+1;
while(*t) {
if(n < UTFmax+1 && n < runelen(*t)+1) {
*s = 0;
return i+wstrutflen(t)+1;
}
i = runetochar(s, t);
s += i;
n -= i;
t++;
}
*s = 0;
return s-s0;
}