xref: /inferno-os/emu/Nt/win.c (revision 7ef44d652ae9e5e1f5b3465d73684e4a54de73c0)
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
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  	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
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
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
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*
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 *
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*
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
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