win32.cc (13068B)
1 #include <windows.h> 2 #include <windowsx.h> 3 #include <Winsock2.h> 4 #include <stdio.h> 5 6 #include "common.h" 7 #include "input.h" 8 #include "script.h" 9 #include "ui.h" 10 11 #include "platform_network.h" 12 13 #define WINDOW_CLASSNAME "MudGangsterClass" 14 15 struct { 16 HWND hwnd; 17 HDC hdc; 18 19 HDC back_buffer; 20 HBITMAP back_buffer_bitmap; 21 22 int max_width, max_height; 23 } UI; 24 25 struct Socket { 26 TCPSocket sock; 27 bool in_use; 28 }; 29 30 static Socket sockets[ 128 ]; 31 32 void * platform_connect( const char ** err, const char * host, int port ) { 33 size_t idx; 34 { 35 bool ok = false; 36 for( size_t i = 0; i < ARRAY_COUNT( sockets ); i++ ) { 37 if( !sockets[ i ].in_use ) { 38 idx = i; 39 ok = true; 40 break; 41 } 42 } 43 44 if( !ok ) { 45 *err = "too many connections"; 46 return NULL; 47 } 48 } 49 50 NetAddress addr; 51 { 52 bool ok = dns_first( host, &addr ); 53 if( !ok ) { 54 *err = "couldn't resolve hostname"; // TODO: error from dns_first 55 return NULL; 56 } 57 } 58 addr.port = checked_cast< u16 >( port ); 59 60 TCPSocket sock; 61 bool ok = net_new_tcp( &sock, addr, err ); 62 if( !ok ) 63 return NULL; 64 65 sockets[ idx ].sock = sock; 66 sockets[ idx ].in_use = true; 67 68 WSAAsyncSelect( sock.fd, UI.hwnd, 12345, FD_READ | FD_CLOSE ); 69 70 return &sockets[ idx ]; 71 } 72 73 void platform_send( void * vsock, const char * data, size_t len ) { 74 Socket * sock = ( Socket * ) vsock; 75 net_send( sock->sock, data, len ); 76 } 77 78 void platform_close( void * vsock ) { 79 Socket * sock = ( Socket * ) vsock; 80 net_destroy( &sock->sock ); 81 sock->in_use = false; 82 } 83 84 static Socket * socket_from_fd( int fd ) { 85 for( Socket & sock : sockets ) { 86 if( sock.in_use && sock.sock.fd == fd ) { 87 return &sock; 88 } 89 } 90 91 return NULL; 92 } 93 94 struct MudFont { 95 int ascent; 96 int width, height; 97 HFONT regular; 98 HFONT bold; 99 }; 100 101 struct { 102 COLORREF bg; 103 COLORREF status_bg; 104 COLORREF cursor; 105 106 MudFont font; 107 HFONT dc_font; 108 109 union { 110 struct { 111 COLORREF black; 112 COLORREF red; 113 COLORREF green; 114 COLORREF yellow; 115 COLORREF blue; 116 COLORREF magenta; 117 COLORREF cyan; 118 COLORREF white; 119 120 COLORREF lblack; 121 COLORREF lred; 122 COLORREF lgreen; 123 COLORREF lyellow; 124 COLORREF lblue; 125 COLORREF lmagenta; 126 COLORREF lcyan; 127 COLORREF lwhite; 128 129 COLORREF system; 130 } Colours; 131 132 COLORREF colours[ 2 ][ 8 ]; 133 }; 134 } Style; 135 136 static COLORREF get_colour( Colour colour, bool bold ) { 137 switch( colour ) { 138 case SYSTEM: 139 return Style.Colours.system; 140 case COLOUR_BG: 141 return Style.bg; 142 case COLOUR_STATUSBG: 143 return Style.status_bg; 144 case COLOUR_CURSOR: 145 return Style.cursor; 146 } 147 148 return Style.colours[ bold ][ colour ]; 149 } 150 151 void platform_fill_rect( int left, int top, int width, int height, Colour colour, bool bold ) { 152 ZoneScoped; 153 154 // TODO: preallocate these 155 HBRUSH brush = CreateSolidBrush( get_colour( colour, bold ) ); 156 RECT r = { left, top, left + width, top + height }; 157 FillRect( UI.back_buffer, &r, brush ); 158 DeleteObject( brush ); 159 } 160 161 void platform_draw_char( int left, int top, char c, Colour colour, bool bold, bool force_bold_font ) { 162 ZoneScoped; 163 164 SelectObject( UI.back_buffer, ( bold || force_bold_font ? Style.font.bold : Style.font.regular ) ); 165 SetTextColor( UI.back_buffer, get_colour( colour, bold ) ); 166 TextOutA( UI.back_buffer, left, top + SPACING, &c, 1 ); 167 } 168 169 void platform_make_dirty( int left, int top, int width, int height ) { 170 ZoneScoped; 171 172 RECT r = { left, top, left + width, top + height }; 173 InvalidateRect( UI.hwnd, &r, FALSE ); 174 } 175 176 void ui_get_font_size( int * fw, int * fh ) { 177 *fw = Style.font.width; 178 *fh = Style.font.height; 179 } 180 181 void ui_urgent() { 182 FlashWindow( UI.hwnd, FALSE ); 183 } 184 185 bool ui_set_font( const char * name, int size ) { 186 HFONT regular = CreateFontA( size, 0, 0, 0, FW_REGULAR, 187 FALSE, FALSE, FALSE, DEFAULT_CHARSET, 188 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, FIXED_PITCH, 189 name ); 190 191 if( regular == NULL ) 192 return false; 193 194 HFONT bold = CreateFontA( size, 0, 0, 0, FW_BOLD, 195 FALSE, FALSE, FALSE, DEFAULT_CHARSET, 196 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, FIXED_PITCH, 197 name ); 198 199 if( bold == NULL ) { 200 DeleteObject( regular ); 201 return false; 202 } 203 204 if( Style.font.regular != NULL ) { 205 SelectObject( UI.hdc, Style.dc_font ); 206 DeleteObject( Style.font.regular ); 207 DeleteObject( Style.font.bold ); 208 } 209 210 Style.font.regular = regular; 211 Style.font.bold = bold; 212 Style.dc_font = ( HFONT ) SelectObject( UI.hdc, Style.font.regular ); 213 214 TEXTMETRIC metrics; 215 GetTextMetrics( UI.hdc, &metrics ); 216 217 Style.font.height = metrics.tmHeight; 218 Style.font.width = metrics.tmAveCharWidth; 219 Style.font.ascent = metrics.tmAscent; 220 221 ui_update_layout(); 222 ui_redraw_everything(); 223 224 return true; 225 } 226 227 void platform_set_clipboard( const char * str, size_t len ) { 228 if( OpenClipboard( NULL ) == FALSE ) 229 return; 230 231 HGLOBAL mem = GlobalAlloc( GMEM_MOVEABLE, len ); 232 if( mem == NULL ) { 233 CloseClipboard(); 234 return; 235 } 236 237 void * p = GlobalLock( mem ); 238 if( p == NULL ) 239 FATAL( "GlobalLock" ); 240 241 memcpy( p, str, len ); 242 GlobalUnlock( mem ); 243 244 EmptyClipboard(); 245 SetClipboardData( CF_TEXT, mem ); 246 CloseClipboard(); 247 } 248 249 static LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) { 250 ZoneScoped; 251 252 switch( msg ) { 253 case WM_CREATE: { 254 UI.hdc = GetDC( hwnd ); 255 UI.back_buffer = CreateCompatibleDC( UI.hdc ); 256 257 SetBkMode( UI.back_buffer, TRANSPARENT ); 258 259 Style.bg = RGB( 0x1a, 0x1a, 0x1a ); 260 Style.status_bg = RGB( 0x33, 0x33, 0x33 ); 261 Style.cursor = RGB( 0x00, 0xff, 0x00 ); 262 Style.Colours.system = RGB( 0xff, 0xff, 0xff ); 263 264 Style.Colours.black = RGB( 0x1a, 0x1a, 0x1a ); 265 Style.Colours.red = RGB( 0xca, 0x44, 0x33 ); 266 Style.Colours.green = RGB( 0x17, 0x8a, 0x3a ); 267 Style.Colours.yellow = RGB( 0xdc, 0x7c, 0x2a ); 268 Style.Colours.blue = RGB( 0x41, 0x5e, 0x87 ); 269 Style.Colours.magenta = RGB( 0x5e, 0x46, 0x8c ); 270 Style.Colours.cyan = RGB( 0x35, 0x78, 0x9b ); 271 Style.Colours.white = RGB( 0xb6, 0xc2, 0xc4 ); 272 273 Style.Colours.lblack = RGB( 0x66, 0x66, 0x66 ); 274 Style.Colours.lred = RGB( 0xff, 0x29, 0x54 ); 275 Style.Colours.lgreen = RGB( 0x5d, 0xd0, 0x30 ); 276 Style.Colours.lyellow = RGB( 0xfa, 0xfc, 0x4f ); 277 Style.Colours.lblue = RGB( 0x35, 0x81, 0xe1 ); 278 Style.Colours.lmagenta = RGB( 0x87, 0x5f, 0xff ); 279 Style.Colours.lcyan = RGB( 0x29, 0xfb, 0xff ); 280 Style.Colours.lwhite = RGB( 0xce, 0xdb, 0xde ); 281 282 ui_set_font( "", 14 ); 283 } break; 284 285 case WM_SIZE: { 286 int width = LOWORD( lParam ); 287 int height = HIWORD( lParam ); 288 289 int old_max_width = UI.max_width; 290 int old_max_height = UI.max_height; 291 292 UI.max_width = max( UI.max_width, width ); 293 UI.max_height = max( UI.max_height, height ); 294 295 if( UI.max_width != old_max_width || UI.max_height != old_max_height ) { 296 if( old_max_width != -1 ) { 297 DeleteObject( UI.back_buffer_bitmap ); 298 } 299 UI.back_buffer_bitmap = CreateCompatibleBitmap( UI.hdc, UI.max_width, UI.max_height ); 300 SelectObject( UI.back_buffer, UI.back_buffer_bitmap ); 301 } 302 303 ui_resize( width, height ); 304 ui_redraw_everything(); 305 } break; 306 307 case WM_PAINT: { 308 RECT r; 309 GetUpdateRect( UI.hwnd, &r, FALSE ); 310 PAINTSTRUCT ps; 311 HDC hdc = BeginPaint( UI.hwnd, &ps ); 312 BitBlt( UI.hdc, r.left, r.top, r.right - r.left, r.bottom - r.top, UI.back_buffer, r.left, r.top, SRCCOPY ); 313 EndPaint( UI.hwnd, &ps ); 314 } break; 315 316 case WM_ERASEBKGND: 317 return TRUE; 318 319 case WM_LBUTTONDOWN: { 320 ui_mouse_down( GET_X_LPARAM( lParam ), GET_Y_LPARAM( lParam ) ); 321 } break; 322 323 case WM_MOUSEMOVE: { 324 ui_mouse_move( GET_X_LPARAM( lParam ), GET_Y_LPARAM( lParam ) ); 325 } break; 326 327 case WM_LBUTTONUP: { 328 ui_mouse_up( GET_X_LPARAM( lParam ), GET_Y_LPARAM( lParam ) ); 329 } break; 330 331 case WM_CLOSE: { 332 DestroyWindow( hwnd ); 333 } break; 334 335 case WM_DESTROY: { 336 PostQuitMessage( 0 ); 337 } break; 338 339 case WM_TIMER: { 340 script_fire_intervals(); 341 } break; 342 343 case WM_CHAR: { 344 if( wParam >= ' ' && wParam < 127 ) { 345 char c = char( wParam ); 346 input_add( &c, 1 ); 347 } 348 } break; 349 350 case WM_KEYDOWN: 351 case WM_SYSKEYDOWN: { 352 #define ADD_MACRO( key, name ) \ 353 case key: \ 354 script_doMacro( name, sizeof( name ) - 1, shift, ctrl, alt ); \ 355 break 356 357 bool shift = ( GetKeyState( VK_SHIFT ) & 0x8000 ) != 0; 358 bool ctrl = ( GetKeyState( VK_CONTROL ) & 0x8000 ) != 0; 359 bool alt = ( GetKeyState( VK_MENU ) & 0x8000 ) != 0; 360 361 switch( wParam ) { 362 case VK_BACK: 363 input_backspace(); 364 break; 365 366 case VK_DELETE: 367 input_delete(); 368 break; 369 370 case VK_RETURN: 371 input_return(); 372 break; 373 374 case VK_LEFT: 375 input_left(); 376 break; 377 378 case VK_RIGHT: 379 input_right(); 380 break; 381 382 case VK_UP: 383 input_up(); 384 break; 385 386 case VK_DOWN: 387 input_down(); 388 break; 389 390 case VK_PRIOR: 391 if( shift ) 392 ui_scroll( 1 ); 393 else 394 ui_page_up(); 395 break; 396 397 case VK_NEXT: 398 if( shift ) 399 ui_scroll( -1 ); 400 else 401 ui_page_down(); 402 break; 403 404 ADD_MACRO( VK_NUMPAD0, "kp0" ); 405 ADD_MACRO( VK_NUMPAD1, "kp1" ); 406 ADD_MACRO( VK_NUMPAD2, "kp2" ); 407 ADD_MACRO( VK_NUMPAD3, "kp3" ); 408 ADD_MACRO( VK_NUMPAD4, "kp4" ); 409 ADD_MACRO( VK_NUMPAD5, "kp5" ); 410 ADD_MACRO( VK_NUMPAD6, "kp6" ); 411 ADD_MACRO( VK_NUMPAD7, "kp7" ); 412 ADD_MACRO( VK_NUMPAD8, "kp8" ); 413 ADD_MACRO( VK_NUMPAD9, "kp9" ); 414 415 ADD_MACRO( VK_MULTIPLY, "kp*" ); 416 ADD_MACRO( VK_DIVIDE, "kp/" ); 417 ADD_MACRO( VK_SUBTRACT, "kp-" ); 418 ADD_MACRO( VK_ADD, "kp+" ); 419 ADD_MACRO( VK_DECIMAL, "kp." ); 420 421 ADD_MACRO( VK_F1, "f1" ); 422 ADD_MACRO( VK_F2, "f2" ); 423 ADD_MACRO( VK_F3, "f3" ); 424 ADD_MACRO( VK_F4, "f4" ); 425 ADD_MACRO( VK_F5, "f5" ); 426 ADD_MACRO( VK_F6, "f6" ); 427 ADD_MACRO( VK_F7, "f7" ); 428 ADD_MACRO( VK_F8, "f8" ); 429 ADD_MACRO( VK_F9, "f9" ); 430 ADD_MACRO( VK_F10, "f10" ); 431 ADD_MACRO( VK_F11, "f11" ); 432 ADD_MACRO( VK_F12, "f12" ); 433 434 default: { 435 char c = MapVirtualKeyA( wParam, MAPVK_VK_TO_CHAR ); 436 if( c == 0 ) 437 break; 438 c = tolower( c ); 439 if( ctrl && c == 'v' && !shift && !alt ) { 440 if( OpenClipboard( NULL ) == TRUE ) { 441 const char * clipboard = ( const char * ) GetClipboardData( CF_TEXT ); 442 if( clipboard != NULL ) 443 input_add( clipboard, strlen( clipboard ) ); 444 CloseClipboard(); 445 } 446 } 447 else if( ctrl || alt ) { 448 script_doMacro( &c, 1, shift, ctrl, alt ); 449 } 450 } break; 451 } 452 453 #undef ADD_MACRO 454 } break; 455 456 case 12345: { 457 if( WSAGETSELECTERROR( lParam ) ) { 458 printf( "bye\n" ); 459 break; 460 } 461 462 SOCKET fd = ( SOCKET ) wParam; 463 Socket * sock = socket_from_fd( fd ); 464 if( sock == NULL ) 465 break; 466 467 assert( WSAGETSELECTEVENT( lParam ) == FD_CLOSE || WSAGETSELECTEVENT( lParam ) == FD_READ ); 468 469 while( true ) { 470 char buf[ 2048 ]; 471 int n = recv( fd, buf, sizeof( buf ), 0 ); 472 if( n > 0 ) { 473 script_socketData( sock, buf, n ); 474 } 475 else if( n == 0 ) { 476 script_socketData( sock, NULL, n ); 477 break; 478 } 479 else { 480 int err = WSAGetLastError(); 481 if( err != WSAEWOULDBLOCK ) 482 FATAL( "shit %d", err ); 483 break; 484 } 485 } 486 } break; 487 488 default: 489 return DefWindowProc( hwnd, msg, wParam, lParam ); 490 } 491 492 ui_redraw_dirty(); 493 494 return 0; 495 } 496 497 static bool is_macro( const MSG * msg ) { 498 if( msg->message != WM_KEYDOWN && msg->message != WM_KEYUP 499 && msg->message != WM_SYSKEYDOWN && msg->message != WM_SYSKEYUP ) 500 return false; 501 502 WPARAM macro_vks[] = { 503 VK_NUMPAD0, 504 VK_NUMPAD1, 505 VK_NUMPAD2, 506 VK_NUMPAD3, 507 VK_NUMPAD4, 508 VK_NUMPAD5, 509 VK_NUMPAD6, 510 VK_NUMPAD7, 511 VK_NUMPAD8, 512 VK_NUMPAD9, 513 VK_MULTIPLY, 514 VK_DIVIDE, 515 VK_SUBTRACT, 516 VK_ADD, 517 VK_DECIMAL, 518 }; 519 520 for( WPARAM vk : macro_vks ) { 521 if( msg->wParam == vk ) 522 return true; 523 } 524 525 bool ctrl = ( GetKeyState( VK_CONTROL ) & 0x8000 ) != 0; 526 bool alt = ( GetKeyState( VK_MENU ) & 0x8000 ) != 0; 527 528 return ctrl || alt; 529 } 530 531 int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { 532 for( Socket & s : sockets ) { 533 s.in_use = false; 534 } 535 536 UI = { }; 537 UI.max_width = -1; 538 539 Style = { }; 540 541 WNDCLASSEX wc = { }; 542 wc.cbSize = sizeof( WNDCLASSEX ); 543 wc.lpfnWndProc = WndProc; 544 wc.hInstance = hInstance; 545 wc.hCursor = LoadCursor( NULL, IDC_ARROW ); 546 wc.lpszClassName = WINDOW_CLASSNAME; 547 wc.hbrBackground = CreateSolidBrush( RGB( 0x1a, 0x1a, 0x1a ) ); 548 549 if( !RegisterClassEx( &wc ) ) { 550 MessageBox( NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK ); 551 return 0; 552 } 553 554 UI.hwnd = CreateWindowExA( 555 NULL, 556 WINDOW_CLASSNAME, 557 "Mud Gangster", 558 WS_OVERLAPPEDWINDOW | WS_MAXIMIZE | WS_VISIBLE, 559 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 560 NULL, NULL, hInstance, NULL ); 561 562 if( UI.hwnd == NULL ) { 563 MessageBox( NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK ); 564 return 0; 565 } 566 567 ShowWindow( UI.hwnd, SW_MAXIMIZE ); 568 UpdateWindow( UI.hwnd ); 569 SetTimer( UI.hwnd, 1, 500, NULL ); 570 571 net_init(); 572 ui_init(); 573 script_init(); 574 575 FrameMark; 576 577 MSG msg; 578 while( GetMessage( &msg, NULL, 0, 0 ) > 0 ) { 579 if( !is_macro( &msg ) ) 580 TranslateMessage( &msg ); 581 DispatchMessage( &msg ); 582 } 583 584 script_term(); 585 ui_term(); 586 net_term(); 587 588 // TODO: clean up UI stuff 589 590 return 0; 591 }