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