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