xref: /plan9/sys/src/cmd/unix/drawterm/gui-win32/screen.c (revision 353937820181d9ceece9f7e799bfae37fdcac66e)
1 #define _WIN32_WINNT 0x0500
2 #include	<windows.h>
3 
4 #undef Rectangle
5 #define Rectangle _Rectangle
6 
7 #include "u.h"
8 #include "lib.h"
9 #include "kern/dat.h"
10 #include "kern/fns.h"
11 #include "error.h"
12 #include "user.h"
13 #include <draw.h>
14 #include <memdraw.h>
15 #include "screen.h"
16 #include "keyboard.h"
17 
18 Memimage	*gscreen;
19 Screeninfo	screen;
20 
21 extern int mousequeue;
22 static int depth;
23 
24 static	HINSTANCE	inst;
25 static	HWND		window;
26 static	HPALETTE	palette;
27 static	LOGPALETTE	*logpal;
28 static  Lock		gdilock;
29 static 	BITMAPINFO	*bmi;
30 static	HCURSOR		hcursor;
31 
32 static void	winproc(void *);
33 static LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
34 static void	paletteinit(void);
35 static void	bmiinit(void);
36 
37 static int readybit;
38 static Rendez	rend;
39 
40 Point	ZP;
41 
42 static int
isready(void * a)43 isready(void*a)
44 {
45 	return readybit;
46 }
47 
48 void
screeninit(void)49 screeninit(void)
50 {
51 	int fmt;
52 	int dx, dy;
53 
54 	memimageinit();
55 	if(depth == 0)
56 		depth = GetDeviceCaps(GetDC(NULL), BITSPIXEL);
57 	switch(depth){
58 	case 32:
59 		screen.dibtype = DIB_RGB_COLORS;
60 		screen.depth = 32;
61 		fmt = XRGB32;
62 		break;
63 	case 24:
64 		screen.dibtype = DIB_RGB_COLORS;
65 		screen.depth = 24;
66 		fmt = RGB24;
67 		break;
68 	case 16:
69 		screen.dibtype = DIB_RGB_COLORS;
70 		screen.depth = 16;
71 		fmt = RGB15;	/* [sic] */
72 		break;
73 	case 8:
74 	default:
75 		screen.dibtype = DIB_PAL_COLORS;
76 		screen.depth = 8;
77 		depth = 8;
78 		fmt = CMAP8;
79 		break;
80 	}
81 	dx = GetDeviceCaps(GetDC(NULL), HORZRES);
82 	dy = GetDeviceCaps(GetDC(NULL), VERTRES);
83 
84 	gscreen = allocmemimage(Rect(0,0,dx,dy), fmt);
85 	kproc("winscreen", winproc, 0);
86 	ksleep(&rend, isready, 0);
87 }
88 
89 uchar*
attachscreen(Rectangle * r,ulong * chan,int * depth,int * width,int * softscreen,void ** X)90 attachscreen(Rectangle *r, ulong *chan, int *depth, int *width, int *softscreen, void **X)
91 {
92 	*r = gscreen->r;
93 	*chan = gscreen->chan;
94 	*depth = gscreen->depth;
95 	*width = gscreen->width;
96 	*softscreen = 1;
97 
98 	return gscreen->data->bdata;
99 }
100 
101 void
flushmemscreen(Rectangle r)102 flushmemscreen(Rectangle r)
103 {
104 	screenload(r, gscreen->depth, byteaddr(gscreen, ZP), ZP,
105 		gscreen->width*sizeof(ulong));
106 //	Sleep(100);
107 }
108 
109 void
screenload(Rectangle r,int depth,uchar * p,Point pt,int step)110 screenload(Rectangle r, int depth, uchar *p, Point pt, int step)
111 {
112 	int dx, dy, delx;
113 	HDC hdc;
114 	RECT winr;
115 
116 	if(depth != gscreen->depth)
117 		panic("screenload: bad ldepth");
118 
119 	/*
120 	 * Sometimes we do get rectangles that are off the
121 	 * screen to the negative axes, for example, when
122 	 * dragging around a window border in a Move operation.
123 	 */
124 	if(rectclip(&r, gscreen->r) == 0)
125 		return;
126 
127 	if((step&3) != 0 || ((pt.x*depth)%32) != 0 || ((ulong)p&3) != 0)
128 		panic("screenload: bad params %d %d %ux", step, pt.x, p);
129 	dx = r.max.x - r.min.x;
130 	dy = r.max.y - r.min.y;
131 
132 	if(dx <= 0 || dy <= 0)
133 		return;
134 
135 	if(depth == 24)
136 		delx = r.min.x % 4;
137 	else
138 		delx = r.min.x & (31/depth);
139 
140 	p += (r.min.y-pt.y)*step;
141 	p += ((r.min.x-delx-pt.x)*depth)>>3;
142 
143 	if(GetWindowRect(window, &winr)==0)
144 		return;
145 	if(rectclip(&r, Rect(0, 0, winr.right-winr.left, winr.bottom-winr.top))==0)
146 		return;
147 
148 	lock(&gdilock);
149 
150 	hdc = GetDC(window);
151 	SelectPalette(hdc, palette, 0);
152 	RealizePalette(hdc);
153 
154 //FillRect(hdc,(void*)&r, GetStockObject(BLACK_BRUSH));
155 //GdiFlush();
156 //Sleep(100);
157 
158 	bmi->bmiHeader.biWidth = (step*8)/depth;
159 	bmi->bmiHeader.biHeight = -dy;	/* - => origin upper left */
160 
161 	StretchDIBits(hdc, r.min.x, r.min.y, dx, dy,
162 		delx, 0, dx, dy, p, bmi, screen.dibtype, SRCCOPY);
163 
164 	ReleaseDC(window, hdc);
165 
166 	GdiFlush();
167 
168 	unlock(&gdilock);
169 }
170 
171 static void
winproc(void * a)172 winproc(void *a)
173 {
174 	WNDCLASS wc;
175 	MSG msg;
176 
177 	inst = GetModuleHandle(NULL);
178 
179 	paletteinit();
180 	bmiinit();
181 	terminit();
182 
183 	wc.style = 0;
184 	wc.lpfnWndProc = WindowProc;
185 	wc.cbClsExtra = 0;
186 	wc.cbWndExtra = 0;
187 	wc.hInstance = inst;
188 	wc.hIcon = LoadIcon(inst, NULL);
189 	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
190 	wc.hbrBackground = GetStockObject(WHITE_BRUSH);
191 	wc.lpszMenuName = 0;
192 	wc.lpszClassName = L"9pmgraphics";
193 	RegisterClass(&wc);
194 
195 	window = CreateWindowEx(
196 		0,			/* extended style */
197 		L"9pmgraphics",		/* class */
198 		L"drawterm screen",		/* caption */
199 		WS_OVERLAPPEDWINDOW,    /* style */
200 		CW_USEDEFAULT,		/* init. x pos */
201 		CW_USEDEFAULT,		/* init. y pos */
202 		CW_USEDEFAULT,		/* init. x size */
203 		CW_USEDEFAULT,		/* init. y size */
204 		NULL,			/* parent window (actually owner window for overlapped)*/
205 		NULL,			/* menu handle */
206 		inst,			/* program handle */
207 		NULL			/* create parms */
208 		);
209 
210 	if(window == nil)
211 		panic("can't make window\n");
212 
213 	ShowWindow(window, SW_SHOWDEFAULT);
214 	UpdateWindow(window);
215 
216 	readybit = 1;
217 	wakeup(&rend);
218 
219 	screen.reshaped = 0;
220 
221 	while(GetMessage(&msg, NULL, 0, 0)) {
222 		TranslateMessage(&msg);
223 		DispatchMessage(&msg);
224 	}
225 //	MessageBox(0, "winproc", "exits", MB_OK);
226 	ExitProcess(0);
227 }
228 
229 int
col(int v,int n)230 col(int v, int n)
231 {
232 	int i, c;
233 
234 	c = 0;
235 	for(i = 0; i < 8; i += n)
236 		c |= v << (16-(n+i));
237 	return c >> 8;
238 }
239 
240 
241 void
paletteinit(void)242 paletteinit(void)
243 {
244 	PALETTEENTRY *pal;
245 	int r, g, b, cr, cg, cb, v;
246 	int num, den;
247 	int i, j;
248 
249 	logpal = mallocz(sizeof(LOGPALETTE) + 256*sizeof(PALETTEENTRY), 1);
250 	if(logpal == nil)
251 		panic("out of memory");
252 	logpal->palVersion = 0x300;
253 	logpal->palNumEntries = 256;
254 	pal = logpal->palPalEntry;
255 
256 	for(r=0,i=0; r<4; r++) {
257 		for(v=0; v<4; v++,i+=16){
258 			for(g=0,j=v-r; g<4; g++) {
259 				for(b=0; b<4; b++,j++){
260 					den=r;
261 					if(g>den)
262 						den=g;
263 					if(b>den)
264 						den=b;
265 					/* divide check -- pick grey shades */
266 					if(den==0)
267 						cr=cg=cb=v*17;
268 					else{
269 						num=17*(4*den+v);
270 						cr=r*num/den;
271 						cg=g*num/den;
272 						cb=b*num/den;
273 					}
274 					pal[i+(j&15)].peRed = cr;
275 					pal[i+(j&15)].peGreen = cg;
276 					pal[i+(j&15)].peBlue = cb;
277 					pal[i+(j&15)].peFlags = 0;
278 				}
279 			}
280 		}
281 	}
282 	palette = CreatePalette(logpal);
283 }
284 
285 
286 void
getcolor(ulong i,ulong * r,ulong * g,ulong * b)287 getcolor(ulong i, ulong *r, ulong *g, ulong *b)
288 {
289 	PALETTEENTRY *pal;
290 
291 	pal = logpal->palPalEntry;
292 	*r = pal[i].peRed;
293 	*g = pal[i].peGreen;
294 	*b = pal[i].peBlue;
295 }
296 
297 void
bmiinit(void)298 bmiinit(void)
299 {
300 	ushort *p;
301 	int i;
302 
303 	bmi = mallocz(sizeof(BITMAPINFOHEADER) + 256*sizeof(RGBQUAD), 1);
304 	if(bmi == 0)
305 		panic("out of memory");
306 	bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
307 	bmi->bmiHeader.biWidth = 0;
308 	bmi->bmiHeader.biHeight = 0;	/* - => origin upper left */
309 	bmi->bmiHeader.biPlanes = 1;
310 	bmi->bmiHeader.biBitCount = depth;
311 	bmi->bmiHeader.biCompression = BI_RGB;
312 	bmi->bmiHeader.biSizeImage = 0;
313 	bmi->bmiHeader.biXPelsPerMeter = 0;
314 	bmi->bmiHeader.biYPelsPerMeter = 0;
315 	bmi->bmiHeader.biClrUsed = 0;
316 	bmi->bmiHeader.biClrImportant = 0;	/* number of important colors: 0 means all */
317 
318 	p = (ushort*)bmi->bmiColors;
319 	for(i = 0; i < 256; i++)
320 		p[i] = i;
321 }
322 
323 LRESULT CALLBACK
WindowProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam)324 WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
325 {
326 	PAINTSTRUCT paint;
327 	HDC hdc;
328 	LONG x, y, b;
329 	int i;
330 	Rectangle r;
331 
332 	switch(msg) {
333 	case WM_CREATE:
334 		break;
335 	case WM_SETCURSOR:
336 		/* User set */
337 		if(hcursor != NULL) {
338 			SetCursor(hcursor);
339 			return 1;
340 		}
341 		return DefWindowProc(hwnd, msg, wparam, lparam);
342 	case WM_MOUSEWHEEL:
343 		if ((int)(wparam & 0xFFFF0000)>0)
344 			b|=8;
345 		else
346 			b|=16;
347 	case WM_MOUSEMOVE:
348 	case WM_LBUTTONUP:
349 	case WM_MBUTTONUP:
350 	case WM_RBUTTONUP:
351 	case WM_LBUTTONDOWN:
352 	case WM_MBUTTONDOWN:
353 	case WM_RBUTTONDOWN:
354 		x = LOWORD(lparam);
355 		y = HIWORD(lparam);
356 		b = 0;
357 		if(wparam & MK_LBUTTON)
358 			b = 1;
359 		if(wparam & MK_MBUTTON)
360 			b |= 2;
361 		if(wparam & MK_RBUTTON) {
362 			if(wparam & MK_SHIFT)
363 				b |= 2;
364 			else
365 				b |= 4;
366 		}
367 		lock(&mouse.lk);
368 		i = mouse.wi;
369 		if(mousequeue) {
370 			if(i == mouse.ri || mouse.lastb != b || mouse.trans) {
371 				mouse.wi = (i+1)%Mousequeue;
372 				if(mouse.wi == mouse.ri)
373 					mouse.ri = (mouse.ri+1)%Mousequeue;
374 				mouse.trans = mouse.lastb != b;
375 			} else {
376 				i = (i-1+Mousequeue)%Mousequeue;
377 			}
378 		} else {
379 			mouse.wi = (i+1)%Mousequeue;
380 			mouse.ri = i;
381 		}
382 		mouse.queue[i].xy.x = x;
383 		mouse.queue[i].xy.y = y;
384 		mouse.queue[i].buttons = b;
385 		mouse.queue[i].msec = ticks();
386 		mouse.lastb = b;
387 		unlock(&mouse.lk);
388 		wakeup(&mouse.r);
389 		break;
390 
391 	case WM_CHAR:
392 		/* repeat count is lparam & 0xf */
393 		switch(wparam){
394 		case '\n':
395 			wparam = '\r';
396 			break;
397 		case '\r':
398 			wparam = '\n';
399 			break;
400 		}
401 		kbdputc(kbdq, wparam);
402 		break;
403 
404 	case WM_SYSKEYUP:
405 		break;
406 	case WM_SYSKEYDOWN:
407 	case WM_KEYDOWN:
408 		switch(wparam) {
409 		case VK_MENU:
410 			kbdputc(kbdq, Kalt);
411 			break;
412 		case VK_INSERT:
413 			kbdputc(kbdq, Kins);
414 			break;
415 		case VK_DELETE:
416 //			kbdputc(kbdq, Kdel);
417 			kbdputc(kbdq, 0x7f);	// should have Kdel in keyboard.h
418 			break;
419 		case VK_UP:
420 			kbdputc(kbdq, Kup);
421 			break;
422 		case VK_DOWN:
423 			kbdputc(kbdq, Kdown);
424 			break;
425 		case VK_LEFT:
426 			kbdputc(kbdq, Kleft);
427 			break;
428 		case VK_RIGHT:
429 			kbdputc(kbdq, Kright);
430 			break;
431 		}
432 		break;
433 
434 	case WM_CLOSE:
435 		DestroyWindow(hwnd);
436 		break;
437 
438 	case WM_DESTROY:
439 		PostQuitMessage(0);
440 		break;
441 
442 	case WM_PALETTECHANGED:
443 		if((HWND)wparam == hwnd)
444 			break;
445 	/* fall through */
446 	case WM_QUERYNEWPALETTE:
447 		hdc = GetDC(hwnd);
448 		SelectPalette(hdc, palette, 0);
449 		if(RealizePalette(hdc) != 0)
450 			InvalidateRect(hwnd, nil, 0);
451 		ReleaseDC(hwnd, hdc);
452 		break;
453 
454 	case WM_PAINT:
455 		hdc = BeginPaint(hwnd, &paint);
456 		r.min.x = paint.rcPaint.left;
457 		r.min.y = paint.rcPaint.top;
458 		r.max.x = paint.rcPaint.right;
459 		r.max.y = paint.rcPaint.bottom;
460 		flushmemscreen(r);
461 		EndPaint(hwnd, &paint);
462 		break;
463 	case WM_COMMAND:
464 	case WM_SETFOCUS:
465 	case WM_DEVMODECHANGE:
466 	case WM_WININICHANGE:
467 	case WM_INITMENU:
468 	default:
469 		return DefWindowProc(hwnd, msg, wparam, lparam);
470 	}
471 	return 0;
472 }
473 
474 void
mouseset(Point xy)475 mouseset(Point xy)
476 {
477 	POINT pt;
478 
479 	pt.x = xy.x;
480 	pt.y = xy.y;
481 	MapWindowPoints(window, 0, &pt, 1);
482 	SetCursorPos(pt.x, pt.y);
483 }
484 
485 void
setcursor(void)486 setcursor(void)
487 {
488 	HCURSOR nh;
489 	int x, y, h, w;
490 	uchar *sp, *cp;
491 	uchar *and, *xor;
492 
493 	h = GetSystemMetrics(SM_CYCURSOR);
494 	w = (GetSystemMetrics(SM_CXCURSOR)+7)/8;
495 
496 	and = mallocz(h*w, 1);
497 	memset(and, 0xff, h*w);
498 	xor = mallocz(h*w, 1);
499 
500 	lock(&cursor.lk);
501 	for(y=0,sp=cursor.set,cp=cursor.clr; y<16; y++) {
502 		for(x=0; x<2; x++) {
503 			and[y*w+x] = ~(*sp|*cp);
504 			xor[y*w+x] = ~*sp & *cp;
505 			cp++;
506 			sp++;
507 		}
508 	}
509 	nh = CreateCursor(inst, -cursor.offset.x, -cursor.offset.y,
510 			GetSystemMetrics(SM_CXCURSOR), h,
511 			and, xor);
512 	if(nh != NULL) {
513 		SetCursor(nh);
514 		if(hcursor != NULL)
515 			DestroyCursor(hcursor);
516 		hcursor = nh;
517 	}
518 	unlock(&cursor.lk);
519 
520 	free(and);
521 	free(xor);
522 
523 	PostMessage(window, WM_SETCURSOR, (int)window, 0);
524 }
525 
526 void
cursorarrow(void)527 cursorarrow(void)
528 {
529 	if(hcursor != 0) {
530 		DestroyCursor(hcursor);
531 		hcursor = 0;
532 	}
533 	SetCursor(LoadCursor(0, IDC_ARROW));
534 	PostMessage(window, WM_SETCURSOR, (int)window, 0);
535 }
536 
537 
538 void
setcolor(ulong index,ulong red,ulong green,ulong blue)539 setcolor(ulong index, ulong red, ulong green, ulong blue)
540 {
541 }
542 
543 
544 uchar*
clipreadunicode(HANDLE h)545 clipreadunicode(HANDLE h)
546 {
547 	Rune *p;
548 	int n;
549 	uchar *q;
550 
551 	p = GlobalLock(h);
552 	n = wstrutflen(p)+1;
553 	q = malloc(n);
554 	wstrtoutf(q, p, n);
555 	GlobalUnlock(h);
556 
557 	return q;
558 }
559 
560 uchar *
clipreadutf(HANDLE h)561 clipreadutf(HANDLE h)
562 {
563 	uchar *p;
564 
565 	p = GlobalLock(h);
566 	p = strdup(p);
567 	GlobalUnlock(h);
568 
569 	return p;
570 }
571 
572 char*
clipread(void)573 clipread(void)
574 {
575 	HANDLE h;
576 	uchar *p;
577 
578 	if(!OpenClipboard(window)) {
579 		oserror();
580 		return strdup("");
581 	}
582 
583 	if((h = GetClipboardData(CF_UNICODETEXT)))
584 		p = clipreadunicode(h);
585 	else if((h = GetClipboardData(CF_TEXT)))
586 		p = clipreadutf(h);
587 	else {
588 		oserror();
589 		p = strdup("");
590 	}
591 
592 	CloseClipboard();
593 	return p;
594 }
595 
596 int
clipwrite(char * buf)597 clipwrite(char *buf)
598 {
599 	HANDLE h;
600 	char *p, *e;
601 	Rune *rp;
602 	int n = strlen(buf);
603 
604 	if(!OpenClipboard(window)) {
605 		oserror();
606 		return -1;
607 	}
608 
609 	if(!EmptyClipboard()) {
610 		oserror();
611 		CloseClipboard();
612 		return -1;
613 	}
614 
615 	h = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, (n+1)*sizeof(Rune));
616 	if(h == NULL)
617 		panic("out of memory");
618 	rp = GlobalLock(h);
619 	p = buf;
620 	e = p+n;
621 	while(p<e)
622 		p += chartorune(rp++, p);
623 	*rp = 0;
624 	GlobalUnlock(h);
625 
626 	SetClipboardData(CF_UNICODETEXT, h);
627 
628 	h = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, n+1);
629 	if(h == NULL)
630 		panic("out of memory");
631 	p = GlobalLock(h);
632 	memcpy(p, buf, n);
633 	p[n] = 0;
634 	GlobalUnlock(h);
635 
636 	SetClipboardData(CF_TEXT, h);
637 
638 	CloseClipboard();
639 	return n;
640 }
641 
642 int
atlocalconsole(void)643 atlocalconsole(void)
644 {
645 	return 1;
646 }
647