2
* Copyright (c) 1997 - 2001 Hansj�rg Malthaner
4
* This file is part of the Simutrans project under the artistic licence.
8
#error "Only Windows has GDI!"
19
// windows Bibliotheken DirectDraw 5.x
21
// windows.h defines min and max macros which we don't want
33
# define WM_MOUSEWHEEL 0x020A
35
#ifndef GET_WHEEL_DELTA_WPARAM
36
# define GET_WHEEL_DELTA_WPARAM(wparam) ((short)HIWORD(wparam))
39
// 16 Bit may be much slower than 15 unfourtunately on some hardware
42
// for redraws in another thread
43
//#define MULTI_THREAD
47
#include "simsys_w32_png.h"
48
#include "simversion.h"
55
typedef unsigned char PIXVAL;
56
#elif COLOUR_DEPTH == 16
57
typedef unsigned short PIXVAL;
59
# error unknown COLOUR_DEPTH
63
static bool is_fullscreen = false;
64
static bool is_not_top = false;
66
static RECT WindowSize = { 0, 0, 0, 0 };
68
static HINSTANCE hInstance;
70
static BITMAPINFO* AllDib;
71
static PIXVAL* AllDibData;
73
volatile HDC hdc = NULL;
75
const wchar_t* const title =
78
#define TOW(x) TOW_(x)
79
L"Simutrans " WIDE_VERSION_NUMBER
84
L"" SAVEGAME_PREFIX " " VERSION_NUMBER " - " VERSION_DATE
86
" - r" QUOTEME(REVISION)
94
HANDLE hFlushThread=0;
95
#define WAIT_FOR_SCREEN() {while(hdc) Sleep(5);}
100
#define WAIT_FOR_SCREEN()
106
* Hier sind die Basisfunktionen zur Initialisierung der
107
* Schnittstelle untergebracht
110
int dr_os_init(const int* /*parameter*/)
112
// prepare for next event
113
sys_event.type = SIM_NOEVENT;
120
resolution dr_query_screen_resolution()
123
res.w = GetSystemMetrics(SM_CXSCREEN);
124
res.h = GetSystemMetrics(SM_CYSCREEN);
130
int dr_os_open(int const w, int const h, int fullscreen)
132
MaxSize.right = (w+15)&0x7FF0;
133
MaxSize.bottom = h+1;
137
// try to force display mode and size
141
settings.dmSize = sizeof(settings);
142
settings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
143
#if COLOUR_DEPTH != 16 || defined USE_16BIT_DIB
144
settings.dmBitsPerPel = COLOUR_DEPTH;
146
settings.dmBitsPerPel = 16;
148
settings.dmPelsWidth = w;
149
settings.dmPelsHeight = h;
150
settings.dmDisplayFrequency = 0;
152
if( ChangeDisplaySettings(&settings, CDS_TEST)!=DISP_CHANGE_SUCCESSFUL ) {
153
ChangeDisplaySettings( NULL, 0 );
154
printf( "dr_os_open()::Could not reduce color depth to 16 Bit in fullscreen." );
158
ChangeDisplaySettings(&settings, CDS_FULLSCREEN);
160
is_fullscreen = fullscreen;
163
hwnd = CreateWindowEx(
169
NULL, NULL, hInstance, NULL
175
CW_USEDEFAULT, CW_USEDEFAULT,
176
w + GetSystemMetrics(SM_CXFRAME),
177
h - 1 + 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION),
178
NULL, NULL, hInstance, NULL
181
ShowWindow(hwnd, SW_SHOW);
183
WindowSize.right = w;
184
WindowSize.bottom = h;
186
#if COLOUR_DEPTH == 8
187
DWORD const clr = 224 + 15;
188
AllDib = MALLOCF(BITMAPINFO, bmiColors, 256);
191
AllDib = MALLOCF(BITMAPINFO, bmiColors, 3);
193
BITMAPINFOHEADER& header = AllDib->bmiHeader;
194
header.biSize = sizeof(BITMAPINFOHEADER);
198
header.biBitCount = COLOUR_DEPTH;
199
header.biCompression = BI_RGB;
200
header.biSizeImage = 0;
201
header.biXPelsPerMeter = 0;
202
header.biYPelsPerMeter = 0;
203
header.biClrUsed = clr;
204
header.biClrImportant = clr;
205
#if COLOUR_DEPTH == 16
206
DWORD* const masks = (DWORD*)AllDib->bmiColors;
207
# ifdef USE_16BIT_DIB
208
header.biCompression = BI_BITFIELDS;
209
masks[0] = 0x0000F800;
210
masks[1] = 0x000007E0;
211
masks[2] = 0x0000001F;
219
return MaxSize.right;
223
int dr_os_close(void)
232
ChangeDisplaySettings(NULL, 0);
238
int dr_textur_resize(unsigned short** const textur, int w, int const h)
242
// some cards need those alignments
243
w = (w + 15) & 0x7FF0;
248
if(w>MaxSize.right || h>=MaxSize.bottom) {
249
// since the query routines that return the desktop data do not take into account a change of resolution
253
MaxSize.bottom = h+1;
254
AllDibData = MALLOCN(PIXVAL, MaxSize.right * MaxSize.bottom);
255
*textur = (unsigned short*)AllDibData;
258
AllDib->bmiHeader.biWidth = w;
259
AllDib->bmiHeader.biHeight = h;
260
WindowSize.right = w;
261
WindowSize.bottom = h;
266
unsigned short *dr_textur_init()
268
size_t const n = MaxSize.right * MaxSize.bottom;
269
AllDibData = MALLOCN(PIXVAL, n);
271
MEMZERON(AllDibData, n);
272
return (unsigned short*)AllDibData;
277
* Transform a 24 bit RGB color into the system format.
278
* @return converted color value
279
* @author Hj. Malthaner
281
unsigned int get_system_color(unsigned int r, unsigned int g, unsigned int b)
284
return ((r & 0x00F8) << 8) | ((g & 0x00FC) << 3) | (b >> 3);
286
return ((r & 0x00F8) << 7) | ((g & 0x00F8) << 2) | (b >> 3); // 15 Bit
291
void dr_setRGB8multi(int first, int count, unsigned char* data)
293
// set color in DibHeader ...
294
RGBQUAD *pRGB = (RGBQUAD *)((char *)AllDib + sizeof(BITMAPINFOHEADER));
295
for( int i=first; i<first+count; i++ ) {
296
pRGB[i].rgbRed = data[i*3];
297
pRGB[i].rgbGreen = data[i*3+1];
298
pRGB[i].rgbBlue = data[i*3+2];
304
// multhreaded screen copy ...
305
DWORD WINAPI dr_flush_screen(LPVOID lpParam)
308
// wait for finish of thread
310
display_flush_buffer();
311
ReleaseDC(hwnd, hdc);
313
// suspend myself after one update
314
SuspendThread( hFlushThread );
320
void dr_prepare_flush()
324
if(hFlushThread==0) {
326
hFlushThread = CreateThread( NULL, 0, dr_flush_screen, 0, CREATE_SUSPENDED, &id );
335
// just let the thread do its work
336
ResumeThread( hFlushThread );
340
display_flush_buffer();
341
ReleaseDC(hwnd, hdc);
348
void dr_textur(int xp, int yp, int w, int h)
350
// make really sure we are not beyond screen coordinates
351
h = min( yp+h, WindowSize.bottom ) - yp;
353
w = min( xp+w, WindowSize.right ) - xp;
357
AllDib->bmiHeader.biHeight = h + 1;
358
StretchDIBits(hdc, xp, yp, w, h, xp, h + 1, w, -h, AllDibData + yp * WindowSize.right, AllDib, DIB_RGB_COLORS, SRCCOPY);
363
// move cursor to the specified location
364
void move_pointer(int x, int y)
368
ClientToScreen(hwnd, &pt);
369
SetCursorPos(pt.x, pt.y);
373
// set the mouse cursor (pointer/load)
374
void set_pointer(int loading)
376
SetCursor(LoadCursor(NULL, loading != 0 ? IDC_WAIT : IDC_ARROW));
381
* Some wrappers can save screenshots.
382
* @return 1 on success, 0 if not implemented for a particular wrapper and -1
384
* @author Hj. Malthaner
386
int dr_screenshot(const char *filename)
388
#if COLOUR_DEPTH != 16 || defined USE_16BIT_DIB
389
int const bpp = COLOUR_DEPTH;
393
if (!dr_screenshot_png(filename, display_get_width() - 1, WindowSize.bottom + 1, AllDib->bmiHeader.biWidth, (unsigned short*)AllDibData, bpp)) {
394
#if COLOUR_DEPTH != 8
395
// not successful => save as BMP
396
FILE *fBmp = fopen(filename, "wb");
400
// since the number of drawn pixel can be smaller than the actual width => only use the drawn pixel for bitmap
401
LONG const old_width = AllDib->bmiHeader.biWidth;
402
AllDib->bmiHeader.biWidth = display_get_width() - 1;
403
AllDib->bmiHeader.biHeight = WindowSize.bottom + 1;
405
bf.bfType = 0x4d42; //"BM"
408
bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(DWORD)*3;
409
bf.bfSize = (bf.bfOffBits + AllDib->bmiHeader.biHeight * AllDib->bmiHeader.biWidth * 2L + 3L) / 4L;
410
fwrite(&bf, sizeof(BITMAPFILEHEADER), 1, fBmp);
411
fwrite(AllDib, sizeof(AllDib->bmiHeader) + sizeof(*AllDib->bmiColors) * 3, 1, fBmp);
413
for (LONG i = 0; i < AllDib->bmiHeader.biHeight; ++i) {
414
// row must be alsway even number of pixel
415
fwrite(AllDibData + (AllDib->bmiHeader.biHeight - 1 - i) * old_width, (AllDib->bmiHeader.biWidth + 1) & 0xFFFE, 2, fBmp);
417
AllDib->bmiHeader.biWidth = old_width;
431
* Hier sind die Funktionen zur Messageverarbeitung
434
static inline unsigned int ModifierKeys()
437
(GetKeyState(VK_SHIFT) < 0 ? 1 : 0) |
438
(GetKeyState(VK_CONTROL) < 0 ? 2 : 0); // highest bit set or return value<0 -> key is pressed
441
struct sys_event sys_event;
443
/* Windows eventhandler: does most of the work */
444
LRESULT WINAPI WindowProc(HWND this_hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
446
static int last_mb = 0; // last mouse button state
449
case WM_ACTIVATE: // may check, if we have to restore color depth
451
// avoid double calls
452
static bool while_handling = false;
456
while_handling = true;
458
if(LOWORD(wParam)!=WA_INACTIVE && is_not_top) {
459
// try to force display mode and size
463
settings.dmSize = sizeof(settings);
464
settings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
465
#if COLOUR_DEPTH != 16 || defined USE_16BIT_DIB
466
settings.dmBitsPerPel = COLOUR_DEPTH;
468
settings.dmBitsPerPel = 15;
470
settings.dmPelsWidth = MaxSize.right;
471
settings.dmPelsHeight = MaxSize.bottom;
472
settings.dmDisplayFrequency = 0;
474
// should be alsway sucessful, since it worked as least once ...
475
ChangeDisplaySettings(&settings, CDS_FULLSCREEN);
479
// must reshow window, otherwise startbar will be topmost ...
480
hwnd = CreateWindowEx(
485
MaxSize.right, MaxSize.bottom,
486
NULL, NULL, hInstance, NULL
488
ShowWindow( hwnd, SW_SHOW );
489
DestroyWindow( this_hwnd );
490
while_handling = false;
493
else if(LOWORD(wParam)==WA_INACTIVE && !is_not_top) {
496
ChangeDisplaySettings( NULL, 0 );
501
while_handling = false;
505
case WM_GETMINMAXINFO:
507
LPMINMAXINFO lpmmi = (LPMINMAXINFO) lParam;
508
lpmmi->ptMaxPosition.x = 0;
509
lpmmi->ptMaxPosition.y = 0;
513
case WM_LBUTTONDOWN: /* originally ButtonPress */
514
sys_event.type = SIM_MOUSE_BUTTONS;
515
sys_event.code = SIM_MOUSE_LEFTBUTTON;
516
sys_event.key_mod = ModifierKeys();
517
sys_event.mb = last_mb = (wParam&3);
518
sys_event.mx = LOWORD(lParam);
519
sys_event.my = HIWORD(lParam);
522
case WM_LBUTTONUP: /* originally ButtonRelease */
523
sys_event.type = SIM_MOUSE_BUTTONS;
524
sys_event.code = SIM_MOUSE_LEFTUP;
525
sys_event.key_mod = ModifierKeys();
526
sys_event.mb = last_mb = (wParam&3);
527
sys_event.mx = LOWORD(lParam);
528
sys_event.my = HIWORD(lParam);
531
case WM_RBUTTONDOWN: /* originally ButtonPress */
532
sys_event.type = SIM_MOUSE_BUTTONS;
533
sys_event.code = SIM_MOUSE_RIGHTBUTTON;
534
sys_event.key_mod = ModifierKeys();
535
sys_event.mb = last_mb = (wParam&3);
536
sys_event.mx = LOWORD(lParam);
537
sys_event.my = HIWORD(lParam);
540
case WM_RBUTTONUP: /* originally ButtonRelease */
541
sys_event.type = SIM_MOUSE_BUTTONS;
542
sys_event.code = SIM_MOUSE_RIGHTUP;
543
sys_event.key_mod = ModifierKeys();
544
sys_event.mb = last_mb = (wParam&3);
545
sys_event.mx = LOWORD(lParam);
546
sys_event.my = HIWORD(lParam);
550
sys_event.type = SIM_MOUSE_MOVE;
551
sys_event.code = SIM_MOUSE_MOVED;
552
sys_event.key_mod = ModifierKeys();
553
sys_event.mb = last_mb = (wParam&3);
554
sys_event.mx = LOWORD(lParam);
555
sys_event.my = HIWORD(lParam);
559
sys_event.type = SIM_MOUSE_BUTTONS;
560
sys_event.code = GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? SIM_MOUSE_WHEELUP : SIM_MOUSE_WHEELDOWN;
561
sys_event.key_mod = ModifierKeys();
562
/* the returned coordinate in LPARAM are absolute coordinates, which will deeply confuse simutrans
563
* we just reuse the coordinates we used the last time but not chaning mx/my ...
567
case WM_SIZE: // resize client area
569
sys_event.type = SIM_SYSTEM;
570
sys_event.code = SIM_SYSTEM_RESIZE;
572
sys_event.mx = LOWORD(lParam) + 1;
573
if (sys_event.mx <= 0) {
577
sys_event.my = HIWORD(lParam);
578
if (sys_event.my <= 1) {
590
hdcp = BeginPaint(hwnd, &ps);
591
AllDib->bmiHeader.biHeight = WindowSize.bottom;
592
StretchDIBits(hdcp, 0, 0, WindowSize.right, WindowSize.bottom, 0, WindowSize.bottom + 1, WindowSize.right, -WindowSize.bottom, AllDibData, AllDib, DIB_RGB_COLORS, SRCCOPY);
593
EndPaint(this_hwnd, &ps);
597
case WM_KEYDOWN: { /* originally KeyPress */
598
// check for not numlock!
599
int numlock = (GetKeyState(VK_NUMLOCK) & 1) == 0;
601
sys_event.type = SIM_KEYBOARD;
603
sys_event.key_mod = ModifierKeys();
606
// do low level special stuff here
608
case VK_NUMPAD0: sys_event.code = '0'; break;
609
case VK_NUMPAD1: sys_event.code = '1'; break;
610
case VK_NUMPAD3: sys_event.code = '3'; break;
611
case VK_NUMPAD7: sys_event.code = '7'; break;
612
case VK_NUMPAD9: sys_event.code = '9'; break;
613
case VK_NUMPAD2: sys_event.code = SIM_KEY_DOWN; break;
614
case VK_NUMPAD4: sys_event.code = SIM_KEY_LEFT; break;
615
case VK_NUMPAD6: sys_event.code = SIM_KEY_RIGHT; break;
616
case VK_NUMPAD8: sys_event.code = SIM_KEY_UP; break;
617
case VK_PAUSE: sys_event.code = 16; break; // Pause -> ^P
618
case VK_SEPARATOR: sys_event.code = 127; break; // delete
620
// check for numlock!
621
if (sys_event.code != 0) break;
624
// do low level special stuff here
626
case VK_LEFT: sys_event.code = SIM_KEY_LEFT; break;
627
case VK_RIGHT: sys_event.code = SIM_KEY_RIGHT; break;
628
case VK_UP: sys_event.code = SIM_KEY_UP; break;
629
case VK_DOWN: sys_event.code = SIM_KEY_DOWN; break;
630
case VK_PRIOR: sys_event.code = '>'; break;
631
case VK_NEXT: sys_event.code = '<'; break;
632
case VK_DELETE: sys_event.code = 127; break;
633
case VK_HOME: sys_event.code = SIM_KEY_HOME; break;
634
case VK_END: sys_event.code = SIM_KEY_END; break;
637
if (sys_event.code == 0 && wParam >= VK_F1 && wParam <= VK_F15) {
638
sys_event.code = wParam - VK_F1 + SIM_KEY_F1;
639
//printf("WindowsEvent: Key %i, (state %i)\n", sys_event.code, sys_event.key_mod);
642
if (sys_event.code != 0) return 0;
643
sys_event.type = SIM_NOEVENT;
648
case WM_CHAR: /* originally KeyPress */
649
sys_event.type = SIM_KEYBOARD;
650
sys_event.code = wParam;
651
sys_event.key_mod = ModifierKeys();
655
if (AllDibData != NULL) {
656
sys_event.type = SIM_SYSTEM;
657
sys_event.code = SIM_SYSTEM_QUIT;
663
sys_event.type = SIM_SYSTEM;
664
sys_event.code = SIM_SYSTEM_QUIT;
665
if (AllDibData == NULL) {
673
return DefWindowProc(this_hwnd, msg, wParam, lParam);
679
static void internal_GetEvents(int wait)
682
// wait for keybord/mouse event
683
GetMessage(&msg, NULL, 0, 0);
684
TranslateMessage(&msg);
685
DispatchMessage(&msg);
686
} while (wait && sys_event.type == SIM_NOEVENT);
692
// already even processed?
693
if(sys_event.type==SIM_NOEVENT) {
694
internal_GetEvents(TRUE);
699
void GetEventsNoWait()
701
if (sys_event.type==SIM_NOEVENT && PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
702
internal_GetEvents(FALSE);
707
void show_pointer(int yesno)
709
static int state=true;
718
void ex_ord_update_mx_my()
720
// evt. called before update
725
unsigned long dr_time(void)
727
return timeGetTime();
733
void dr_sleep(uint32 millisec)
740
bool dr_fatal_notify(const char* msg, int choices)
743
MessageBoxA( hwnd, msg, "Fatal Error", MB_ICONEXCLAMATION|MB_OK );
747
return MessageBoxA( hwnd, msg, "Fatal Error", MB_ICONEXCLAMATION|MB_RETRYCANCEL )==IDRETRY;
752
int CALLBACK WinMain(HINSTANCE const hInstance, HINSTANCE, LPSTR, int)
756
wc.lpszClassName = L"Simu";
757
wc.style = CS_HREDRAW | CS_VREDRAW;
758
wc.lpfnWndProc = WindowProc;
761
wc.hInstance = hInstance;
762
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(100));
763
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
764
wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND + 1);
765
wc.lpszMenuName = NULL;
769
int const argc = __argc;
770
char** const argv = __argv;
772
GetModuleFileNameA(hInstance, pathname, lengthof(pathname));
775
GetWindowRect(GetDesktopWindow(), &MaxSize);
777
// maybe set timer to 1ms intervall on Win2k upwards ...
779
OSVERSIONINFO osinfo;
780
osinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
781
if (GetVersionEx(&osinfo) && osinfo.dwPlatformId==VER_PLATFORM_WIN32_NT) {
786
simu_main(argc, argv);
791
TerminateThread( hFlushThread, 0 );