xref: /inferno-os/emu/Nt/win.c (revision 06155dbb53eb0585800b239acd6e45b946c6e0cf)
1 #define Unknown WUnknown
2 #define Colormap	WColormap
3 #define Cursor		WCursor
4 #define Display		WDisplay
5 #define Drawable	WDrawable
6 #define Font		WFont
7 #define GC		WGC
8 #define Point		WPoint
9 #define Rectangle	WRectangle
10 #define Screen		WScreen
11 #define Visual		WVisual
12 #define Window		WWindow
13 
14 #include	<windows.h>
15 
16 #undef Colormap
17 #undef Cursor
18 #undef Display
19 #undef XDrawable
20 #undef Font
21 #undef GC
22 #undef Point
23 #undef Rectangle
24 #undef Screen
25 #undef Visual
26 #undef Window
27 #undef Unknown
28 
29 #include	"dat.h"
30 #include	"fns.h"
31 #include	"error.h"
32 #include	<draw.h>
33 #include	"keyboard.h"
34 #include	"cursor.h"
35 
36 extern ulong displaychan;
37 
38 extern	char*	runestoutf(char*, Rune*, int);
39 extern	int	bytesperline(Rectangle, int);
40 extern	int	main(int argc, char **argv);
41 static	void	dprint(char*, ...);
42 static	DWORD WINAPI	winproc(LPVOID);
43 
44 static	HINSTANCE	inst;
45 static	HINSTANCE	previnst;
46 static	int		cmdshow;
47 static	HWND		window;
48 static	HDC		screen;
49 static	HPALETTE	palette;
50 static	int		maxxsize;
51 static	int		maxysize;
52 static	int		attached;
53 static	int		isunicode = 1;
54 static	HCURSOR		hcursor;
55 
56 char	*argv0 = "inferno";
57 static	ulong	*data;
58 
59 extern	DWORD	PlatformId;
60 char*	gkscanid = "emu_win32vk";
61 
62 int WINAPI
63 WinMain(HINSTANCE winst, HINSTANCE wprevinst, LPSTR cmdline, int wcmdshow)
64 {
65 	inst = winst;
66 	previnst = wprevinst;
67 	cmdshow = wcmdshow;
68 
69 	/* cmdline passed into WinMain does not contain name of executable.
70 	 * The globals __argc and __argv to include this info - like UNIX
71 	 */
72 	main(__argc, __argv);
73 	return 0;
74 }
75 
76 static void
77 dprint(char *fmt, ...)
78 {
79 	va_list arg;
80 	char buf[128];
81 
82 	va_start(arg, fmt);
83 	vseprint(buf, buf+sizeof(buf), fmt, (LPSTR)arg);
84 	va_end(arg);
85 	OutputDebugString("inferno: ");
86 	OutputDebugString(buf);
87 }
88 
89 static void
90 graphicscmap(PALETTEENTRY *pal)
91 {
92 	int r, g, b, cr, cg, cb, v, p;
93 	int num, den;
94 	int i, j;
95 	for(r=0,i=0;r!=4;r++) for(v=0;v!=4;v++,i+=16){
96 		for(g=0,j=v-r;g!=4;g++) for(b=0;b!=4;b++,j++){
97 			den=r;
98 			if(g>den) den=g;
99 			if(b>den) den=b;
100 			if(den==0)	/* divide check -- pick grey shades */
101 				cr=cg=cb=v*17;
102 			else{
103 				num=17*(4*den+v);
104 				cr=r*num/den;
105 				cg=g*num/den;
106 				cb=b*num/den;
107 			}
108 			p = i+(j&15);
109 			pal[p].peRed = cr*0x01010101;
110 			pal[p].peGreen = cg*0x01010101;
111 			pal[p].peBlue = cb*0x01010101;
112 			pal[p].peFlags = 0;
113 		}
114 	}
115 }
116 
117 static void
118 graphicsgmap(PALETTEENTRY *pal, int d)
119 {
120 	int i, j, s, m, p;
121 
122 	s = 8-d;
123 	m = 1;
124 	while(--d >= 0)
125 		m *= 2;
126 	m = 255/(m-1);
127 	for(i=0; i < 256; i++){
128 		j = (i>>s)*m;
129 		p = 255-i;
130 		pal[p].peRed = pal[p].peGreen = pal[p].peBlue = (255-j)*0x01010101;
131 		pal[p].peFlags = 0;
132 	}
133 }
134 
135 static ulong
136 autochan(void)
137 {
138 	HDC dc;
139 	int bpp;
140 
141 	dc = GetDC(NULL);
142 	if (dc == NULL)
143 		return CMAP8;
144 
145 	bpp = GetDeviceCaps(dc, BITSPIXEL);
146 	if (bpp < 15)
147 		return CMAP8;
148 	if (bpp < 24)
149 		return RGB15;
150 	if (bpp < 32)
151 		return RGB24;
152 	return XRGB32;
153 }
154 
155 uchar*
156 attachscreen(Rectangle *r, ulong *chan, int *d, int *width, int *softscreen)
157 {
158 	int i, k;
159 	ulong c;
160 	DWORD h;
161 	RECT bs;
162 	RGBQUAD *rgb;
163 	HBITMAP bits;
164 	BITMAPINFO *bmi;
165 	LOGPALETTE *logpal;
166 	PALETTEENTRY *pal;
167 	int bsh, bsw, sx, sy;
168 
169 	if(attached)
170 		goto Return;
171 
172 	/* Compute bodersizes */
173 	memset(&bs, 0, sizeof(bs));
174 	AdjustWindowRect(&bs, WS_OVERLAPPEDWINDOW, 0);
175 	bsw = bs.right - bs.left;
176 	bsh = bs.bottom - bs.top;
177 	sx = GetSystemMetrics(SM_CXFULLSCREEN) - bsw;
178 	Xsize -= Xsize % 4;	/* Round down */
179 	if(Xsize > sx)
180 		Xsize = sx;
181 	sy = GetSystemMetrics(SM_CYFULLSCREEN) - bsh + 20;
182 	if(Ysize > sy)
183 		Ysize = sy;
184 
185 	logpal = malloc(sizeof(LOGPALETTE) + 256*sizeof(PALETTEENTRY));
186 	if(logpal == nil)
187 		return nil;
188 	logpal->palVersion = 0x300;
189 	logpal->palNumEntries = 256;
190 	pal = logpal->palPalEntry;
191 
192 	c = displaychan;
193 	if(c == 0)
194 		c = autochan();
195 	k = 8;
196 	if(TYPE(c) == CGrey){
197 		graphicsgmap(pal, NBITS(c));
198 		c = GREY8;
199 	}else{
200 		if(c == RGB15)
201 			k = 16;
202 		else if(c == RGB24)
203 			k = 24;
204 		else if(c == XRGB32)
205 			k = 32;
206 		else
207 			c = CMAP8;
208 		graphicscmap(pal);
209 	}
210 
211 	palette = CreatePalette(logpal);
212 
213 	if(k == 8)
214 		bmi = malloc(sizeof(BITMAPINFOHEADER) + 256*sizeof(RGBQUAD));
215 	else
216 		bmi = malloc(sizeof(BITMAPINFOHEADER));
217 	if(bmi == nil)
218 		return nil;
219 	bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
220 	bmi->bmiHeader.biWidth = Xsize;
221 	bmi->bmiHeader.biHeight = -Ysize;	/* - => origin upper left */
222 	bmi->bmiHeader.biPlanes = 1;	/* always 1 */
223 	bmi->bmiHeader.biBitCount = k;
224 	bmi->bmiHeader.biCompression = BI_RGB;
225 	bmi->bmiHeader.biSizeImage = 0;	/* Xsize*Ysize*(k/8) */
226 	bmi->bmiHeader.biXPelsPerMeter = 0;
227 	bmi->bmiHeader.biYPelsPerMeter = 0;
228 	bmi->bmiHeader.biClrUsed = 0;
229 	bmi->bmiHeader.biClrImportant = 0;	/* number of important colors: 0 means all */
230 
231 	if(k == 8){
232 		rgb = bmi->bmiColors;
233 		for(i = 0; i < 256; i++){
234 			rgb[i].rgbRed = pal[i].peRed;
235 			rgb[i].rgbGreen = pal[i].peGreen;
236 			rgb[i].rgbBlue = pal[i].peBlue;
237 		}
238 	}
239 
240 	screen = CreateCompatibleDC(NULL);
241 	if(screen == nil){
242 		fprint(2, "screen dc nil\n");
243 		return nil;
244 	}
245 
246 	if(SelectPalette(screen, palette, 1) == nil){
247 		fprint(2, "select pallete failed\n");
248 	}
249 	i = RealizePalette(screen);
250 	GdiFlush();
251 	bits = CreateDIBSection(screen, bmi, DIB_RGB_COLORS, &data, nil, 0);
252 	if(bits == nil){
253 		fprint(2, "CreateDIBSection failed\n");
254 		return nil;
255 	}
256 
257 	SelectObject(screen, bits);
258 	GdiFlush();
259 	CreateThread(0, 16384, winproc, nil, 0, &h);
260 	attached = 1;
261 
262     Return:
263 	r->min.x = 0;
264 	r->min.y = 0;
265 	r->max.x = Xsize;
266 	r->max.y = Ysize;
267 	displaychan = c;
268 	*chan = c;
269 	*d = k;
270 	*width = (Xsize/4)*(k/8);
271 	*softscreen = 1;
272 	return (uchar*)data;
273 }
274 
275 void
276 flushmemscreen(Rectangle r)
277 {
278 	RECT wr;
279 
280 	if(r.max.x<=r.min.x || r.max.y<=r.min.y)
281 		return;
282 	wr.left = r.min.x;
283 	wr.top = r.min.y;
284 	wr.right = r.max.x;
285 	wr.bottom = r.max.y;
286 	InvalidateRect(window, &wr, 0);
287 }
288 
289 static void
290 scancode(WPARAM wparam, LPARAM lparam, int keyup)
291 {
292 	uchar buf[2];
293 
294 	if(!(lparam & (1<<30))) {		/* don't auto-repeat chars */
295 		buf[0] = wparam;
296 		buf[1] = wparam >> 8;
297 		if (keyup)
298 			buf[1] |= 0x80;
299 		qproduce(gkscanq, buf, sizeof buf);
300 	}
301 }
302 
303 LRESULT CALLBACK
304 WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
305 {
306 	PAINTSTRUCT paint;
307 	HDC hdc;
308 	LPMINMAXINFO mmi;
309 	LONG x, y, w, h, b;
310 	HCURSOR dcurs;
311 
312 	switch(msg) {
313 	case WM_SETCURSOR:
314 		/* User set */
315 		if(hcursor != NULL) {
316 			SetCursor(hcursor);
317 			break;
318 		}
319 		/* Pick the default */
320 		dcurs = LoadCursor(NULL, IDC_ARROW);
321 		SetCursor(dcurs);
322 		break;
323 	case WM_LBUTTONDBLCLK:
324 		b = (1<<8) | 1;
325 		goto process;
326 	case WM_MBUTTONDBLCLK:
327 		b = (1<<8) | 2;
328 		goto process;
329 	case WM_RBUTTONDBLCLK:
330 		b = (1<<8) | 4;
331 		goto process;
332 	case WM_MOUSEMOVE:
333 	case WM_LBUTTONUP:
334 	case WM_MBUTTONUP:
335 	case WM_RBUTTONUP:
336 	case WM_LBUTTONDOWN:
337 	case WM_MBUTTONDOWN:
338 	case WM_RBUTTONDOWN:
339 		b = 0;
340 	process:
341 		x = LOWORD(lparam);
342 		y = HIWORD(lparam);
343 		if(wparam & MK_LBUTTON)
344 			b |= 1;
345 		if(wparam & MK_MBUTTON)
346 			b |= 2;
347 		if(wparam & MK_RBUTTON) {
348 			if(wparam & MK_CONTROL)
349 				b |= 2;  //simulate middle button
350 			else
351 				b |= 4;  //right button
352 		}
353 		mousetrack(b, x, y, 0);
354 		break;
355 	case WM_SYSKEYDOWN:
356 		if(gkscanq)
357 			scancode(wparam, lparam, 0);
358 		break;
359 	case WM_SYSKEYUP:
360 		if(gkscanq)
361 			scancode(wparam, lparam, 1);
362 		else if(wparam == VK_MENU)
363 			gkbdputc(gkbdq, Latin);
364 		break;
365 	case WM_KEYDOWN:
366 		if(gkscanq) {
367 			scancode(wparam, lparam, 0);
368 			break;
369 		}
370 		switch(wparam) {
371 		default:
372 			return 0;
373 		case VK_HOME:
374 			wparam = Home;
375 			break;
376 		case VK_END:
377 			wparam = End;
378 			break;
379 		case VK_UP:
380 			wparam = Up;
381 			break;
382 		case VK_DOWN:
383 			wparam = Down;
384 			break;
385 		case VK_LEFT:
386 			wparam = Left;
387 			break;
388 		case VK_RIGHT:
389 			wparam = Right;
390 			break;
391 		case VK_PRIOR:	/* VK_PAGE_UP */
392 			wparam = Pgup;
393 			break;
394 		case VK_NEXT:		/* VK_PAGE_DOWN */
395 			wparam = Pgdown;
396 			break;
397 		case VK_PRINT:
398 			wparam = Print;
399 			break;
400 		case VK_SCROLL:
401 			wparam = Scroll;
402 			break;
403 		case VK_PAUSE:
404 			wparam = Pause;
405 			break;
406 		case VK_INSERT:
407 			wparam = Ins;
408 			break;
409 		case VK_DELETE:
410 			wparam = Del;
411 			break;
412 /*
413 		case VK_TAB:
414 			if(GetKeyState(VK_SHIFT)<0)
415 				wparam = BackTab;
416 			else
417 				wparam = '\t';
418 			break;
419 */
420 		}
421 		gkbdputc(gkbdq, wparam);
422 		break;
423 	case WM_KEYUP:
424 		if(gkscanq)
425 			scancode(wparam, lparam, 1);
426 		break;
427 	case WM_CHAR:
428 		if(gkscanq)
429 			break;
430 		switch(wparam) {
431 		case '\n':
432 		  	wparam = '\r';
433 		  	break;
434 		case '\r':
435 		  	wparam = '\n';
436 		  	break;
437 		case '\t':
438 			if(GetKeyState(VK_SHIFT)<0)
439 				wparam = BackTab;
440 			else
441 				wparam = '\t';
442 			break;
443 		}
444 		if(lparam & KF_ALTDOWN)
445 		    	wparam = APP | (wparam & 0xFF);
446 		gkbdputc(gkbdq, wparam);
447 		break;
448 	case WM_CLOSE:
449 		// no longer used?
450 		//m.b = 128;
451 		//m.modify = 1;
452 		//mousetrack(128, 0, 0, 1);
453 		DestroyWindow(hwnd);
454 		break;
455 	case WM_DESTROY:
456 		PostQuitMessage(0);
457 		cleanexit(0);
458 		break;
459 	case WM_PALETTECHANGED:
460 		if((HWND)wparam == hwnd)
461 			break;
462 	/* fall through */
463 	case WM_QUERYNEWPALETTE:
464 		hdc = GetDC(hwnd);
465 		SelectPalette(hdc, palette, 0);
466 		if(RealizePalette(hdc) != 0)
467 			InvalidateRect(hwnd, nil, 0);
468 		ReleaseDC(hwnd, hdc);
469 		break;
470 	case WM_PAINT:
471 		hdc = BeginPaint(hwnd, &paint);
472 		SelectPalette(hdc, palette, 0);
473 		RealizePalette(hdc);
474 		x = paint.rcPaint.left;
475 		y = paint.rcPaint.top;
476 		w = paint.rcPaint.right - x;
477 		h = paint.rcPaint.bottom - y;
478 		BitBlt(hdc, x, y, w, h, screen, x, y, SRCCOPY);
479 		EndPaint(hwnd, &paint);
480 		break;
481 	case WM_GETMINMAXINFO:
482 		mmi = (LPMINMAXINFO)lparam;
483 		mmi->ptMaxSize.x = maxxsize;
484 		mmi->ptMaxSize.y = maxysize;
485 		mmi->ptMaxTrackSize.x = maxxsize;
486 		mmi->ptMaxTrackSize.y = maxysize;
487 		break;
488 	case WM_SYSCHAR:
489 	case WM_COMMAND:
490 	case WM_CREATE:
491 	case WM_SETFOCUS:
492 	case WM_DEVMODECHANGE:
493 	case WM_WININICHANGE:
494 	case WM_INITMENU:
495 	default:
496 		if(isunicode)
497 			return DefWindowProcW(hwnd, msg, wparam, lparam);
498 		return DefWindowProcA(hwnd, msg, wparam, lparam);
499 	}
500 	return 0;
501 }
502 
503 static DWORD WINAPI
504 winproc(LPVOID x)
505 {
506 	MSG msg;
507 	RECT size;
508 	WNDCLASSW wc;
509 	WNDCLASSA wca;
510 	DWORD ws;
511 
512 	if(!previnst){
513 		wc.style = CS_DBLCLKS;
514 		wc.lpfnWndProc = WindowProc;
515 		wc.cbClsExtra = 0;
516 		wc.cbWndExtra = 0;
517 		wc.hInstance = inst;
518 		wc.hIcon = LoadIcon(inst, MAKEINTRESOURCE(100));
519 		wc.hCursor = NULL;
520 		wc.hbrBackground = GetStockObject(WHITE_BRUSH);
521 
522 		wc.lpszMenuName = 0;
523 		wc.lpszClassName = L"inferno";
524 
525 		if(RegisterClassW(&wc) == 0){
526 			wca.style = wc.style;
527 			wca.lpfnWndProc = wc.lpfnWndProc;
528 			wca.cbClsExtra = wc.cbClsExtra;
529 			wca.cbWndExtra = wc.cbWndExtra;
530 			wca.hInstance = wc.hInstance;
531 			wca.hIcon = wc.hIcon;
532 			wca.hCursor = wc.hCursor;
533 			wca.hbrBackground = wc.hbrBackground;
534 
535 			wca.lpszMenuName = 0;
536 			wca.lpszClassName = "inferno";
537 			isunicode = 0;
538 
539 			RegisterClassA(&wca);
540 		}
541 	}
542 
543 	size.left = 0;
544 	size.top = 0;
545 	size.right = Xsize;
546 	size.bottom = Ysize;
547 
548 	ws = WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX;
549 
550 	if(AdjustWindowRect(&size, ws, 0)) {
551 		maxxsize = size.right - size.left;
552 		maxysize = size.bottom - size.top;
553 	}else{
554 		maxxsize = Xsize + 40;
555 		maxysize = Ysize + 40;
556 	}
557 
558 	if(isunicode) {
559 		window = CreateWindowExW(
560 			0,			/* extended style */
561 			L"inferno",		/* class */
562 			L"Inferno",		/* caption */
563 			ws,	/* style */
564 			CW_USEDEFAULT,		/* init. x pos */
565 			CW_USEDEFAULT,		/* init. y pos */
566 			maxxsize,		/* init. x size */
567 			maxysize,		/* init. y size */
568 			NULL,			/* parent window (actually owner window for overlapped) */
569 			NULL,			/* menu handle */
570 			inst,			/* program handle */
571 			NULL			/* create parms */
572 			);
573 	}else{
574 		window = CreateWindowExA(
575 			0,			/* extended style */
576 			"inferno",		/* class */
577 			"Inferno",		/* caption */
578 			ws,	/* style */
579 			CW_USEDEFAULT,		/* init. x pos */
580 			CW_USEDEFAULT,		/* init. y pos */
581 			maxxsize,		/* init. x size */
582 			maxysize,		/* init. y size */
583 			NULL,			/* parent window (actually owner window for overlapped) */
584 			NULL,			/* menu handle */
585 			inst,			/* program handle */
586 			NULL			/* create parms */
587 			);
588 	}
589 
590 	if(window == nil){
591 		fprint(2, "can't make window\n");
592 		ExitThread(0);
593 	}
594 
595 	SetForegroundWindow(window);
596 	ShowWindow(window, cmdshow);
597 	UpdateWindow(window);
598 	// CloseWindow(window);
599 
600 	if(isunicode) {
601 		while(GetMessageW(&msg, NULL, 0, 0)) {
602 			TranslateMessage(&msg);
603 			DispatchMessageW(&msg);
604 		}
605 	}else{
606 		while(GetMessageA(&msg, NULL, 0, 0)) {
607 			TranslateMessage(&msg);
608 			DispatchMessageA(&msg);
609 		}
610 	}
611 	attached = 0;
612 	ExitThread(msg.wParam);
613 	return 0;
614 }
615 
616 void
617 setpointer(int x, int y)
618 {
619 	POINT pt;
620 
621 	pt.x = x; pt.y = y;
622 	ClientToScreen(window, &pt);
623 	SetCursorPos(pt.x, pt.y);
624 }
625 
626 void
627 drawcursor(Drawcursor* c)
628 {
629 	HCURSOR nh, oh;
630 	Rectangle ir;
631 	int i, h, j, bpl, ch, cw;
632 	uchar *bs, *bc, *and, *xor, *cand, *cxor;
633 
634 	/* Set the default system cursor */
635 	if(c->data == nil) {
636 		oh = hcursor;
637 		hcursor = NULL;
638 		if(oh != NULL) {
639 			SendMessage(window, WM_SETCURSOR, (int)window, 0);
640 			DestroyCursor(oh);
641 		}
642 		return;
643 	}
644 
645 	ir.min.x = c->minx;
646 	ir.min.y = c->miny;
647 	ir.max.x = c->maxx;
648 	ir.max.y = c->maxy;
649 	bpl = bytesperline(ir, 1);
650 
651 	h = (c->maxy-c->miny)/2;
652 
653 	ch = GetSystemMetrics(SM_CYCURSOR);
654 	cw = (GetSystemMetrics(SM_CXCURSOR)+7)/8;
655 
656 	i = ch*cw;
657 	and = malloc(2*i);
658 	if(and == nil)
659 		return;
660 	xor = and + i;
661 	memset(and, 0xff, i);
662 	memset(xor, 0, i);
663 
664 	cand = and;
665 	cxor = xor;
666 	bc = c->data;
667 	bs = c->data + h*bpl;
668 
669 	for(i = 0; i < ch && i < h; i++) {
670 		for(j = 0; j < cw && j < bpl; j++) {
671 			cand[j] = ~(bs[j] | bc[j]);
672 			cxor[j] = ~bs[j] & bc[j];
673 		}
674 		cand += cw;
675 		cxor += cw;
676 		bs += bpl;
677 		bc += bpl;
678 	}
679 	nh = CreateCursor(inst, -c->hotx, -c->hoty, 8*cw, ch, and, xor);
680 	if(nh != NULL) {
681 		oh = hcursor;
682 		hcursor = nh;
683 		SendMessage(window, WM_SETCURSOR, (int)window, 0);
684 		if(oh != NULL)
685 			DestroyCursor(oh);
686 	}else{
687 		print("CreateCursor error %d\n", GetLastError());
688 		print("CXCURSOR=%d\n", GetSystemMetrics(SM_CXCURSOR));
689 		print("CYCURSOR=%d\n", GetSystemMetrics(SM_CYCURSOR));
690 	}
691 	free(and);
692 }
693 
694 /*
695  * thanks to drawterm for these
696  */
697 
698 static char*
699 clipreadunicode(HANDLE h)
700 {
701 	Rune *p;
702 	int n;
703 	char *q;
704 
705 	p = GlobalLock(h);
706 	n = runenlen(p, runestrlen(p)+1);
707 	q = malloc(n);
708 	if(q != nil)
709 		runestoutf(q, p, n);
710 	GlobalUnlock(h);
711 
712 	if(q == nil)
713 		error(Enovmem);
714 	return q;
715 }
716 
717 static char *
718 clipreadutf(HANDLE h)
719 {
720 	uchar *p;
721 
722 	p = GlobalLock(h);
723 	p = strdup(p);
724 	GlobalUnlock(h);
725 
726 	if(p == nil)
727 		error(Enovmem);
728 	return p;
729 }
730 
731 char*
732 clipread(void)
733 {
734 	HANDLE h;
735 	char *p;
736 
737 	if(!OpenClipboard(window))
738 		return strdup("");
739 
740 	if((h = GetClipboardData(CF_UNICODETEXT)))
741 		p = clipreadunicode(h);
742 	else if((h = GetClipboardData(CF_TEXT)))
743 		p = clipreadutf(h);
744 	else
745 		p = strdup("");
746 
747 	CloseClipboard();
748 	return p;
749 }
750 
751 int
752 clipwrite(char *buf)
753 {
754 	HANDLE h;
755 	char *p, *e;
756 	Rune *rp;
757 	int n;
758 
759 	n = 0;
760 	if(buf != nil)
761 		n = strlen(buf);
762 	if(!OpenClipboard(window))
763 		return -1;
764 
765 	if(!EmptyClipboard()){
766 		CloseClipboard();
767 		return -1;
768 	}
769 
770 	h = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, (n+1)*sizeof(Rune));
771 	if(h == NULL)
772 		error(Enovmem);
773 	rp = GlobalLock(h);
774 	p = buf;
775 	e = p+n;
776 	while(p<e)
777 		p += chartorune(rp++, p);
778 	*rp = 0;
779 	GlobalUnlock(h);
780 
781 	SetClipboardData(CF_UNICODETEXT, h);
782 
783 	h = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, n+1);
784 	if(h == NULL)
785 		error(Enovmem);
786 	p = GlobalLock(h);
787 	memmove(p, buf, n);
788 	p[n] = 0;
789 	GlobalUnlock(h);
790 
791 	SetClipboardData(CF_TEXT, h);
792 
793 	CloseClipboard();
794 	return n;
795 }
796