xref: /inferno-os/emu/Nt/win.c (revision 4a062f32e3dd93bf371feb27f3ccc28d7930099e)
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 #include	"r16.h"
36 
37 extern ulong displaychan;
38 
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
WinMain(HINSTANCE winst,HINSTANCE wprevinst,LPSTR cmdline,int wcmdshow)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
dprint(char * fmt,...)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
graphicscmap(PALETTEENTRY * pal)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
graphicsgmap(PALETTEENTRY * pal,int d)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
autochan(void)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*
attachscreen(Rectangle * r,ulong * chan,int * d,int * width,int * softscreen)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
flushmemscreen(Rectangle r)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
scancode(WPARAM wparam,LPARAM lparam,int keyup)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
WindowProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam)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 	POINT m;
312 
313 	switch(msg) {
314 	case WM_SETCURSOR:
315 		/* User set */
316 		if(hcursor != NULL) {
317 			SetCursor(hcursor);
318 			break;
319 		}
320 		/* Pick the default */
321 		dcurs = LoadCursor(NULL, IDC_ARROW);
322 		SetCursor(dcurs);
323 		break;
324 	case WM_MOUSEWHEEL:
325 		if((int)wparam>0)
326 			b = 8;
327 		else
328 			b = 16;
329 		m.x = LOWORD(lparam);
330 		m.y = HIWORD(lparam);
331 		ScreenToClient(hwnd, &m);
332 		goto mok;
333 	case WM_LBUTTONDBLCLK:
334 		b = (1<<8) | 1;
335 		goto process;
336 	case WM_MBUTTONDBLCLK:
337 		b = (1<<8) | 2;
338 		goto process;
339 	case WM_RBUTTONDBLCLK:
340 		b = (1<<8) | 4;
341 		goto process;
342 	case WM_MOUSEMOVE:
343 	case WM_LBUTTONUP:
344 	case WM_MBUTTONUP:
345 	case WM_RBUTTONUP:
346 	case WM_LBUTTONDOWN:
347 	case WM_MBUTTONDOWN:
348 	case WM_RBUTTONDOWN:
349 		b = 0;
350 	process:
351 		m.x = LOWORD(lparam);
352 		m.y = HIWORD(lparam);
353 	mok:
354 		if(wparam & MK_LBUTTON)
355 			b |= 1;
356 		if(wparam & MK_MBUTTON)
357 			b |= 2;
358 		if(wparam & MK_RBUTTON) {
359 			if(wparam & MK_CONTROL)
360 				b |= 2;  //simulate middle button
361 			else
362 				b |= 4;  //right button
363 		}
364 		mousetrack(b, m.x, m.y, 0);
365 		break;
366 	case WM_SYSKEYDOWN:
367 		if(gkscanq)
368 			scancode(wparam, lparam, 0);
369 		break;
370 	case WM_SYSKEYUP:
371 		if(gkscanq)
372 			scancode(wparam, lparam, 1);
373 		else if(wparam == VK_MENU)
374 			gkbdputc(gkbdq, Latin);
375 		break;
376 	case WM_KEYDOWN:
377 		if(gkscanq) {
378 			scancode(wparam, lparam, 0);
379 			break;
380 		}
381 		switch(wparam) {
382 		default:
383 			return 0;
384 		case VK_HOME:
385 			wparam = Home;
386 			break;
387 		case VK_END:
388 			wparam = End;
389 			break;
390 		case VK_UP:
391 			wparam = Up;
392 			break;
393 		case VK_DOWN:
394 			wparam = Down;
395 			break;
396 		case VK_LEFT:
397 			wparam = Left;
398 			break;
399 		case VK_RIGHT:
400 			wparam = Right;
401 			break;
402 		case VK_PRIOR:	/* VK_PAGE_UP */
403 			wparam = Pgup;
404 			break;
405 		case VK_NEXT:		/* VK_PAGE_DOWN */
406 			wparam = Pgdown;
407 			break;
408 		case VK_PRINT:
409 			wparam = Print;
410 			break;
411 		case VK_SCROLL:
412 			wparam = Scroll;
413 			break;
414 		case VK_PAUSE:
415 			wparam = Pause;
416 			break;
417 		case VK_INSERT:
418 			wparam = Ins;
419 			break;
420 		case VK_DELETE:
421 			wparam = Del;
422 			break;
423 /*
424 		case VK_TAB:
425 			if(GetKeyState(VK_SHIFT)<0)
426 				wparam = BackTab;
427 			else
428 				wparam = '\t';
429 			break;
430 */
431 		}
432 		gkbdputc(gkbdq, wparam);
433 		break;
434 	case WM_KEYUP:
435 		if(gkscanq)
436 			scancode(wparam, lparam, 1);
437 		break;
438 	case WM_CHAR:
439 		if(gkscanq)
440 			break;
441 		switch(wparam) {
442 		case '\n':
443 		  	wparam = '\r';
444 		  	break;
445 		case '\r':
446 		  	wparam = '\n';
447 		  	break;
448 		case '\t':
449 			if(GetKeyState(VK_SHIFT)<0)
450 				wparam = BackTab;
451 			else
452 				wparam = '\t';
453 			break;
454 		}
455 		if(lparam & KF_ALTDOWN)
456 		    	wparam = APP | (wparam & 0xFF);
457 		gkbdputc(gkbdq, wparam);
458 		break;
459 	case WM_CLOSE:
460 		DestroyWindow(hwnd);
461 		break;
462 	case WM_DESTROY:
463 		PostQuitMessage(0);
464 		cleanexit(0);
465 		break;
466 	case WM_PALETTECHANGED:
467 		if((HWND)wparam == hwnd)
468 			break;
469 	/* fall through */
470 	case WM_QUERYNEWPALETTE:
471 		hdc = GetDC(hwnd);
472 		SelectPalette(hdc, palette, 0);
473 		if(RealizePalette(hdc) != 0)
474 			InvalidateRect(hwnd, nil, 0);
475 		ReleaseDC(hwnd, hdc);
476 		break;
477 	case WM_PAINT:
478 		hdc = BeginPaint(hwnd, &paint);
479 		SelectPalette(hdc, palette, 0);
480 		RealizePalette(hdc);
481 		x = paint.rcPaint.left;
482 		y = paint.rcPaint.top;
483 		w = paint.rcPaint.right - x;
484 		h = paint.rcPaint.bottom - y;
485 		BitBlt(hdc, x, y, w, h, screen, x, y, SRCCOPY);
486 		EndPaint(hwnd, &paint);
487 		break;
488 	case WM_GETMINMAXINFO:
489 		mmi = (LPMINMAXINFO)lparam;
490 		mmi->ptMaxSize.x = maxxsize;
491 		mmi->ptMaxSize.y = maxysize;
492 		mmi->ptMaxTrackSize.x = maxxsize;
493 		mmi->ptMaxTrackSize.y = maxysize;
494 		break;
495 	case WM_SYSCHAR:
496 	case WM_COMMAND:
497 	case WM_CREATE:
498 	case WM_SETFOCUS:
499 	case WM_DEVMODECHANGE:
500 	case WM_WININICHANGE:
501 	case WM_INITMENU:
502 	default:
503 		if(isunicode)
504 			return DefWindowProcW(hwnd, msg, wparam, lparam);
505 		return DefWindowProcA(hwnd, msg, wparam, lparam);
506 	}
507 	return 0;
508 }
509 
510 static DWORD WINAPI
winproc(LPVOID x)511 winproc(LPVOID x)
512 {
513 	MSG msg;
514 	RECT size;
515 	WNDCLASSW wc;
516 	WNDCLASSA wca;
517 	DWORD ws;
518 
519 	if(!previnst){
520 		wc.style = CS_DBLCLKS;
521 		wc.lpfnWndProc = WindowProc;
522 		wc.cbClsExtra = 0;
523 		wc.cbWndExtra = 0;
524 		wc.hInstance = inst;
525 		wc.hIcon = LoadIcon(inst, MAKEINTRESOURCE(100));
526 		wc.hCursor = NULL;
527 		wc.hbrBackground = GetStockObject(WHITE_BRUSH);
528 
529 		wc.lpszMenuName = 0;
530 		wc.lpszClassName = L"inferno";
531 
532 		if(RegisterClassW(&wc) == 0){
533 			wca.style = wc.style;
534 			wca.lpfnWndProc = wc.lpfnWndProc;
535 			wca.cbClsExtra = wc.cbClsExtra;
536 			wca.cbWndExtra = wc.cbWndExtra;
537 			wca.hInstance = wc.hInstance;
538 			wca.hIcon = wc.hIcon;
539 			wca.hCursor = wc.hCursor;
540 			wca.hbrBackground = wc.hbrBackground;
541 
542 			wca.lpszMenuName = 0;
543 			wca.lpszClassName = "inferno";
544 			isunicode = 0;
545 
546 			RegisterClassA(&wca);
547 		}
548 	}
549 
550 	size.left = 0;
551 	size.top = 0;
552 	size.right = Xsize;
553 	size.bottom = Ysize;
554 
555 	ws = WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX;
556 
557 	if(AdjustWindowRect(&size, ws, 0)) {
558 		maxxsize = size.right - size.left;
559 		maxysize = size.bottom - size.top;
560 	}else{
561 		maxxsize = Xsize + 40;
562 		maxysize = Ysize + 40;
563 	}
564 
565 	if(isunicode) {
566 		window = CreateWindowExW(
567 			0,			/* extended style */
568 			L"inferno",		/* class */
569 			L"Inferno",		/* caption */
570 			ws,	/* style */
571 			CW_USEDEFAULT,		/* init. x pos */
572 			CW_USEDEFAULT,		/* init. y pos */
573 			maxxsize,		/* init. x size */
574 			maxysize,		/* init. y size */
575 			NULL,			/* parent window (actually owner window for overlapped) */
576 			NULL,			/* menu handle */
577 			inst,			/* program handle */
578 			NULL			/* create parms */
579 			);
580 	}else{
581 		window = CreateWindowExA(
582 			0,			/* extended style */
583 			"inferno",		/* class */
584 			"Inferno",		/* caption */
585 			ws,	/* style */
586 			CW_USEDEFAULT,		/* init. x pos */
587 			CW_USEDEFAULT,		/* init. y pos */
588 			maxxsize,		/* init. x size */
589 			maxysize,		/* init. y size */
590 			NULL,			/* parent window (actually owner window for overlapped) */
591 			NULL,			/* menu handle */
592 			inst,			/* program handle */
593 			NULL			/* create parms */
594 			);
595 	}
596 
597 	if(window == nil){
598 		fprint(2, "can't make window\n");
599 		ExitThread(0);
600 	}
601 
602 	SetForegroundWindow(window);
603 	ShowWindow(window, cmdshow);
604 	UpdateWindow(window);
605 	// CloseWindow(window);
606 
607 	if(isunicode) {
608 		while(GetMessageW(&msg, NULL, 0, 0)) {
609 			TranslateMessage(&msg);
610 			DispatchMessageW(&msg);
611 		}
612 	}else{
613 		while(GetMessageA(&msg, NULL, 0, 0)) {
614 			TranslateMessage(&msg);
615 			DispatchMessageA(&msg);
616 		}
617 	}
618 	attached = 0;
619 	ExitThread(msg.wParam);
620 	return 0;
621 }
622 
623 void
setpointer(int x,int y)624 setpointer(int x, int y)
625 {
626 	POINT pt;
627 
628 	pt.x = x; pt.y = y;
629 	ClientToScreen(window, &pt);
630 	SetCursorPos(pt.x, pt.y);
631 }
632 
633 void
drawcursor(Drawcursor * c)634 drawcursor(Drawcursor* c)
635 {
636 	HCURSOR nh, oh;
637 	Rectangle ir;
638 	int i, h, j, bpl, ch, cw;
639 	uchar *bs, *bc, *and, *xor, *cand, *cxor;
640 
641 	/* Set the default system cursor */
642 	if(c->data == nil) {
643 		oh = hcursor;
644 		hcursor = NULL;
645 		if(oh != NULL) {
646 			SendMessage(window, WM_SETCURSOR, (int)window, 0);
647 			DestroyCursor(oh);
648 		}
649 		return;
650 	}
651 
652 	ir.min.x = c->minx;
653 	ir.min.y = c->miny;
654 	ir.max.x = c->maxx;
655 	ir.max.y = c->maxy;
656 	bpl = bytesperline(ir, 1);
657 
658 	h = (c->maxy-c->miny)/2;
659 
660 	ch = GetSystemMetrics(SM_CYCURSOR);
661 	cw = (GetSystemMetrics(SM_CXCURSOR)+7)/8;
662 
663 	i = ch*cw;
664 	and = malloc(2*i);
665 	if(and == nil)
666 		return;
667 	xor = and + i;
668 	memset(and, 0xff, i);
669 	memset(xor, 0, i);
670 
671 	cand = and;
672 	cxor = xor;
673 	bc = c->data;
674 	bs = c->data + h*bpl;
675 
676 	for(i = 0; i < ch && i < h; i++) {
677 		for(j = 0; j < cw && j < bpl; j++) {
678 			cand[j] = ~(bs[j] | bc[j]);
679 			cxor[j] = ~bs[j] & bc[j];
680 		}
681 		cand += cw;
682 		cxor += cw;
683 		bs += bpl;
684 		bc += bpl;
685 	}
686 	nh = CreateCursor(inst, -c->hotx, -c->hoty, 8*cw, ch, and, xor);
687 	if(nh != NULL) {
688 		oh = hcursor;
689 		hcursor = nh;
690 		SendMessage(window, WM_SETCURSOR, (int)window, 0);
691 		if(oh != NULL)
692 			DestroyCursor(oh);
693 	}else{
694 		print("CreateCursor error %d\n", GetLastError());
695 		print("CXCURSOR=%d\n", GetSystemMetrics(SM_CXCURSOR));
696 		print("CYCURSOR=%d\n", GetSystemMetrics(SM_CYCURSOR));
697 	}
698 	free(and);
699 }
700 
701 /*
702  * thanks to drawterm for these
703  */
704 
705 static char*
clipreadunicode(HANDLE h)706 clipreadunicode(HANDLE h)
707 {
708 	Rune16 *p;
709 	int n;
710 	char *q;
711 
712 	p = GlobalLock(h);
713 	n = rune16nlen(p, runes16len(p)+1);
714 	q = malloc(n);
715 	if(q != nil)
716 		runes16toutf(q, p, n);
717 	GlobalUnlock(h);
718 
719 	if(q == nil)
720 		error(Enovmem);
721 	return q;
722 }
723 
724 static char *
clipreadutf(HANDLE h)725 clipreadutf(HANDLE h)
726 {
727 	uchar *p;
728 
729 	p = GlobalLock(h);
730 	p = strdup(p);
731 	GlobalUnlock(h);
732 
733 	if(p == nil)
734 		error(Enovmem);
735 	return p;
736 }
737 
738 char*
clipread(void)739 clipread(void)
740 {
741 	HANDLE h;
742 	char *p;
743 
744 	if(!OpenClipboard(window))
745 		return strdup("");
746 
747 	if((h = GetClipboardData(CF_UNICODETEXT)))
748 		p = clipreadunicode(h);
749 	else if((h = GetClipboardData(CF_TEXT)))
750 		p = clipreadutf(h);
751 	else
752 		p = strdup("");
753 
754 	CloseClipboard();
755 	return p;
756 }
757 
758 int
clipwrite(char * buf)759 clipwrite(char *buf)
760 {
761 	HANDLE h;
762 	char *p;
763 	Rune16 *rp;
764 	int n;
765 
766 	n = 0;
767 	if(buf != nil)
768 		n = strlen(buf);
769 	if(!OpenClipboard(window))
770 		return -1;
771 
772 	if(!EmptyClipboard()){
773 		CloseClipboard();
774 		return -1;
775 	}
776 
777 	h = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, (n+1)*sizeof(Rune16));
778 	if(h == NULL)
779 		error(Enovmem);
780 	rp = GlobalLock(h);
781 	utftorunes16(rp, buf, n+1);
782 	GlobalUnlock(h);
783 
784 	SetClipboardData(CF_UNICODETEXT, h);
785 
786 	h = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, n+1);
787 	if(h == NULL)
788 		error(Enovmem);
789 	p = GlobalLock(h);
790 	memmove(p, buf, n);
791 	p[n] = 0;
792 	GlobalUnlock(h);
793 
794 	SetClipboardData(CF_TEXT, h);
795 
796 	CloseClipboard();
797 	return n;
798 }
799