1 #include "stdafx.h" |
|
2 #include "openttd.h" |
|
3 #include "functions.h" |
|
4 #include "gfx.h" |
|
5 #include "macros.h" |
|
6 #include "network.h" |
|
7 #include "variables.h" |
|
8 #include "window.h" |
|
9 #include "video/win32.h" |
|
10 #include <windows.h> |
|
11 |
|
12 static struct { |
|
13 HWND main_wnd; |
|
14 HBITMAP dib_sect; |
|
15 Pixel *bitmap_bits; |
|
16 Pixel *buffer_bits; |
|
17 Pixel *alloced_bits; |
|
18 HPALETTE gdi_palette; |
|
19 int width,height; |
|
20 int width_org, height_org; |
|
21 bool cursor_visible; |
|
22 bool switch_driver; |
|
23 bool fullscreen; |
|
24 bool double_size; |
|
25 bool has_focus; |
|
26 bool running; |
|
27 } _wnd; |
|
28 |
|
29 static void MakePalette(void) |
|
30 { |
|
31 LOGPALETTE *pal; |
|
32 uint i; |
|
33 |
|
34 pal = alloca(sizeof(LOGPALETTE) + (256-1) * sizeof(PALETTEENTRY)); |
|
35 |
|
36 pal->palVersion = 0x300; |
|
37 pal->palNumEntries = 256; |
|
38 |
|
39 for (i = 0; i != 256; i++) { |
|
40 pal->palPalEntry[i].peRed = _cur_palette[i].r; |
|
41 pal->palPalEntry[i].peGreen = _cur_palette[i].g; |
|
42 pal->palPalEntry[i].peBlue = _cur_palette[i].b; |
|
43 pal->palPalEntry[i].peFlags = 0; |
|
44 |
|
45 } |
|
46 _wnd.gdi_palette = CreatePalette(pal); |
|
47 if (_wnd.gdi_palette == NULL) |
|
48 error("CreatePalette failed!\n"); |
|
49 } |
|
50 |
|
51 static void UpdatePalette(HDC dc, uint start, uint count) |
|
52 { |
|
53 RGBQUAD rgb[256]; |
|
54 uint i; |
|
55 |
|
56 for (i = 0; i != count; i++) { |
|
57 rgb[i].rgbRed = _cur_palette[start + i].r; |
|
58 rgb[i].rgbGreen = _cur_palette[start + i].g; |
|
59 rgb[i].rgbBlue = _cur_palette[start + i].b; |
|
60 rgb[i].rgbReserved = 0; |
|
61 } |
|
62 |
|
63 SetDIBColorTable(dc, start, count, rgb); |
|
64 } |
|
65 |
|
66 bool MyShowCursor(bool show) |
|
67 { |
|
68 if (_wnd.cursor_visible == show) |
|
69 return show; |
|
70 |
|
71 _wnd.cursor_visible = show; |
|
72 ShowCursor(show); |
|
73 |
|
74 return !show; |
|
75 } |
|
76 |
|
77 typedef struct { |
|
78 byte vk_from; |
|
79 byte vk_count; |
|
80 byte map_to; |
|
81 } VkMapping; |
|
82 |
|
83 #define AS(x, z) {x, 0, z} |
|
84 #define AM(x, y, z, w) {x, y - x, z} |
|
85 |
|
86 #ifndef VK_OEM_3 |
|
87 #define VK_OEM_3 0xC0 |
|
88 #endif |
|
89 |
|
90 static const VkMapping _vk_mapping[] = { |
|
91 // Pageup stuff + up/down |
|
92 AM(VK_PRIOR,VK_DOWN, WKC_PAGEUP, WKC_DOWN), |
|
93 // Map letters & digits |
|
94 AM('A','Z','A','Z'), |
|
95 AM('0','9','0','9'), |
|
96 |
|
97 AS(VK_ESCAPE, WKC_ESC), |
|
98 AS(VK_PAUSE, WKC_PAUSE), |
|
99 AS(VK_BACK, WKC_BACKSPACE), |
|
100 AM(VK_INSERT,VK_DELETE,WKC_INSERT, WKC_DELETE), |
|
101 |
|
102 AS(VK_SPACE, WKC_SPACE), |
|
103 AS(VK_RETURN, WKC_RETURN), |
|
104 AS(VK_TAB, WKC_TAB), |
|
105 |
|
106 // Function keys |
|
107 AM(VK_F1, VK_F12, WKC_F1, WKC_F12), |
|
108 |
|
109 // Numeric part. |
|
110 // What is the virtual keycode for numeric enter?? |
|
111 AM(VK_NUMPAD0,VK_NUMPAD9, WKC_NUM_0, WKC_NUM_9), |
|
112 AS(VK_DIVIDE, WKC_NUM_DIV), |
|
113 AS(VK_MULTIPLY, WKC_NUM_MUL), |
|
114 AS(VK_SUBTRACT, WKC_NUM_MINUS), |
|
115 AS(VK_ADD, WKC_NUM_PLUS), |
|
116 AS(VK_DECIMAL, WKC_NUM_DECIMAL) |
|
117 }; |
|
118 |
|
119 static uint MapWindowsKey(uint sym) |
|
120 { |
|
121 const VkMapping *map; |
|
122 uint key = 0; |
|
123 |
|
124 for (map = _vk_mapping; map != endof(_vk_mapping); ++map) { |
|
125 if ((uint)(sym - map->vk_from) <= map->vk_count) { |
|
126 key = sym - map->vk_from + map->map_to; |
|
127 break; |
|
128 } |
|
129 } |
|
130 |
|
131 if (GetAsyncKeyState(VK_SHIFT) < 0) key |= WKC_SHIFT; |
|
132 if (GetAsyncKeyState(VK_CONTROL) < 0) key |= WKC_CTRL; |
|
133 if (GetAsyncKeyState(VK_MENU) < 0) key |= WKC_ALT; |
|
134 return key; |
|
135 } |
|
136 |
|
137 static void MakeWindow(bool full_screen); |
|
138 static bool AllocateDibSection(int w, int h); |
|
139 |
|
140 static void ClientSizeChanged(int w, int h) |
|
141 { |
|
142 if (_wnd.double_size) { |
|
143 w /= 2; |
|
144 h /= 2; |
|
145 } |
|
146 |
|
147 // allocate new dib section of the new size |
|
148 if (AllocateDibSection(w, h)) { |
|
149 // mark all palette colors dirty |
|
150 _pal_first_dirty = 0; |
|
151 _pal_last_dirty = 255; |
|
152 GameSizeChanged(); |
|
153 |
|
154 // redraw screen |
|
155 if (_wnd.running) { |
|
156 _screen.dst_ptr = _wnd.buffer_bits; |
|
157 UpdateWindows(); |
|
158 } |
|
159 } |
|
160 } |
|
161 |
|
162 extern void DoExitSave(void); |
|
163 |
|
164 #ifdef _DEBUG |
|
165 // Keep this function here.. |
|
166 // It allows you to redraw the screen from within the MSVC debugger |
|
167 int RedrawScreenDebug(void) |
|
168 { |
|
169 HDC dc,dc2; |
|
170 static int _fooctr; |
|
171 HBITMAP old_bmp; |
|
172 HPALETTE old_palette; |
|
173 |
|
174 _screen.dst_ptr = _wnd.buffer_bits; |
|
175 UpdateWindows(); |
|
176 |
|
177 dc = GetDC(_wnd.main_wnd); |
|
178 dc2 = CreateCompatibleDC(dc); |
|
179 |
|
180 old_bmp = SelectObject(dc2, _wnd.dib_sect); |
|
181 old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE); |
|
182 BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY); |
|
183 SelectPalette(dc, old_palette, TRUE); |
|
184 SelectObject(dc2, old_bmp); |
|
185 DeleteDC(dc2); |
|
186 ReleaseDC(_wnd.main_wnd, dc); |
|
187 |
|
188 return _fooctr++; |
|
189 } |
|
190 #endif |
|
191 |
|
192 static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) |
|
193 { |
|
194 switch (msg) { |
|
195 case WM_PAINT: { |
|
196 PAINTSTRUCT ps; |
|
197 HDC dc,dc2; |
|
198 HBITMAP old_bmp; |
|
199 HPALETTE old_palette; |
|
200 BeginPaint(hwnd, &ps); |
|
201 dc = ps.hdc; |
|
202 dc2 = CreateCompatibleDC(dc); |
|
203 old_bmp = SelectObject(dc2, _wnd.dib_sect); |
|
204 old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE); |
|
205 |
|
206 if (_pal_last_dirty != -1) { |
|
207 UpdatePalette(dc2, _pal_first_dirty, _pal_last_dirty - _pal_first_dirty + 1); |
|
208 _pal_last_dirty = -1; |
|
209 } |
|
210 |
|
211 BitBlt(dc, 0, 0, _wnd.width, _wnd.height, dc2, 0, 0, SRCCOPY); |
|
212 SelectPalette(dc, old_palette, TRUE); |
|
213 SelectObject(dc2, old_bmp); |
|
214 DeleteDC(dc2); |
|
215 EndPaint(hwnd, &ps); |
|
216 } |
|
217 return 0; |
|
218 |
|
219 case WM_PALETTECHANGED: |
|
220 if ((HWND)wParam == hwnd) |
|
221 return 0; |
|
222 // FALL THROUGH |
|
223 case WM_QUERYNEWPALETTE: { |
|
224 HDC hDC = GetWindowDC(hwnd); |
|
225 HPALETTE hOldPalette = SelectPalette(hDC, _wnd.gdi_palette, FALSE); |
|
226 UINT nChanged = RealizePalette(hDC); |
|
227 SelectPalette(hDC, hOldPalette, TRUE); |
|
228 ReleaseDC(hwnd, hDC); |
|
229 if (nChanged) |
|
230 InvalidateRect(hwnd, NULL, FALSE); |
|
231 return 0; |
|
232 } |
|
233 |
|
234 case WM_CLOSE: |
|
235 if (_game_mode == GM_MENU) { // do not ask to quit on the main screen |
|
236 _exit_game = true; |
|
237 } else if (_patches.autosave_on_exit) { |
|
238 DoExitSave(); |
|
239 _exit_game = true; |
|
240 } else |
|
241 AskExitGame(); |
|
242 |
|
243 return 0; |
|
244 |
|
245 case WM_LBUTTONDOWN: |
|
246 SetCapture(hwnd); |
|
247 _left_button_down = true; |
|
248 return 0; |
|
249 |
|
250 case WM_LBUTTONUP: |
|
251 ReleaseCapture(); |
|
252 _left_button_down = false; |
|
253 _left_button_clicked = false; |
|
254 return 0; |
|
255 |
|
256 case WM_RBUTTONDOWN: |
|
257 SetCapture(hwnd); |
|
258 _right_button_down = true; |
|
259 _right_button_clicked = true; |
|
260 return 0; |
|
261 |
|
262 case WM_RBUTTONUP: |
|
263 ReleaseCapture(); |
|
264 _right_button_down = false; |
|
265 return 0; |
|
266 |
|
267 case WM_MOUSEMOVE: { |
|
268 int x = (int16)LOWORD(lParam); |
|
269 int y = (int16)HIWORD(lParam); |
|
270 POINT pt; |
|
271 |
|
272 if (_wnd.double_size) { |
|
273 x /= 2; |
|
274 y /= 2; |
|
275 } |
|
276 |
|
277 if (_cursor.fix_at) { |
|
278 int dx = x - _cursor.pos.x; |
|
279 int dy = y - _cursor.pos.y; |
|
280 if (dx != 0 || dy != 0) { |
|
281 _cursor.delta.x += dx; |
|
282 _cursor.delta.y += dy; |
|
283 |
|
284 pt.x = _cursor.pos.x; |
|
285 pt.y = _cursor.pos.y; |
|
286 |
|
287 if (_wnd.double_size) { |
|
288 pt.x *= 2; |
|
289 pt.y *= 2; |
|
290 } |
|
291 ClientToScreen(hwnd, &pt); |
|
292 SetCursorPos(pt.x, pt.y); |
|
293 } |
|
294 } else { |
|
295 _cursor.delta.x += x - _cursor.pos.x; |
|
296 _cursor.delta.y += y - _cursor.pos.y; |
|
297 _cursor.pos.x = x; |
|
298 _cursor.pos.y = y; |
|
299 _cursor.dirty = true; |
|
300 } |
|
301 MyShowCursor(false); |
|
302 return 0; |
|
303 } |
|
304 |
|
305 case WM_KEYDOWN: { |
|
306 // this is the rewritten ascii input function |
|
307 // it disables windows deadkey handling --> more linux like :D |
|
308 unsigned short w = 0; |
|
309 int r = 0; |
|
310 byte ks[256]; |
|
311 unsigned int scan = 0; |
|
312 uint16 scancode = (( lParam & 0xFF0000 ) >> 16 ); |
|
313 |
|
314 GetKeyboardState(ks); |
|
315 r = ToAscii(wParam, scan, ks, &w, 0); |
|
316 if (r == 0) w = 0; // no translation was possible |
|
317 |
|
318 _pressed_key = w | MapWindowsKey(wParam) << 16; |
|
319 |
|
320 if (scancode == 41) |
|
321 _pressed_key = w | WKC_BACKQUOTE << 16; |
|
322 |
|
323 if ((_pressed_key >> 16) == ('D' | WKC_CTRL) && !_wnd.fullscreen) { |
|
324 _double_size ^= 1; |
|
325 _wnd.double_size = _double_size; |
|
326 ClientSizeChanged(_wnd.width, _wnd.height); |
|
327 MarkWholeScreenDirty(); |
|
328 } |
|
329 } break; |
|
330 |
|
331 case WM_SYSKEYDOWN: /* user presses F10 or Alt, both activating the title-menu */ |
|
332 switch (wParam) { |
|
333 case VK_RETURN: case 0x46: /* Full Screen on ALT + ENTER/F(VK_F) */ |
|
334 ToggleFullScreen(!_wnd.fullscreen); |
|
335 return 0; |
|
336 case VK_MENU: /* Just ALT */ |
|
337 return 0; // do nothing |
|
338 case VK_F10: /* F10, ignore activation of menu */ |
|
339 _pressed_key = MapWindowsKey(wParam) << 16; |
|
340 return 0; |
|
341 default: /* ALT in combination with something else */ |
|
342 _pressed_key = MapWindowsKey(wParam) << 16; |
|
343 break; |
|
344 } |
|
345 break; |
|
346 case WM_NCMOUSEMOVE: |
|
347 MyShowCursor(true); |
|
348 return 0; |
|
349 |
|
350 case WM_SIZE: { |
|
351 if (wParam != SIZE_MINIMIZED) { |
|
352 ClientSizeChanged(LOWORD(lParam), HIWORD(lParam)); |
|
353 } |
|
354 return 0; |
|
355 } |
|
356 case WM_SIZING: { |
|
357 RECT* r = (RECT*)lParam; |
|
358 RECT r2; |
|
359 int w, h; |
|
360 |
|
361 SetRect(&r2, 0, 0, 0, 0); |
|
362 AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE); |
|
363 |
|
364 w = r->right - r->left - (r2.right - r2.left); |
|
365 h = r->bottom - r->top - (r2.bottom - r2.top); |
|
366 if (_wnd.double_size) { |
|
367 w /= 2; |
|
368 h /= 2; |
|
369 } |
|
370 w = clamp(w, 64, MAX_SCREEN_WIDTH); |
|
371 h = clamp(h, 64, MAX_SCREEN_HEIGHT); |
|
372 if (_wnd.double_size) { |
|
373 w *= 2; |
|
374 h *= 2; |
|
375 } |
|
376 SetRect(&r2, 0, 0, w, h); |
|
377 |
|
378 AdjustWindowRect(&r2, GetWindowLong(hwnd, GWL_STYLE), FALSE); |
|
379 w = r2.right - r2.left; |
|
380 h = r2.bottom - r2.top; |
|
381 |
|
382 switch (wParam) { |
|
383 case WMSZ_BOTTOM: |
|
384 r->bottom = r->top + h; |
|
385 break; |
|
386 case WMSZ_BOTTOMLEFT: |
|
387 r->bottom = r->top + h; |
|
388 r->left = r->right - w; |
|
389 break; |
|
390 case WMSZ_BOTTOMRIGHT: |
|
391 r->bottom = r->top + h; |
|
392 r->right = r->left + w; |
|
393 break; |
|
394 case WMSZ_LEFT: |
|
395 r->left = r->right - w; |
|
396 break; |
|
397 case WMSZ_RIGHT: |
|
398 r->right = r->left + w; |
|
399 break; |
|
400 case WMSZ_TOP: |
|
401 r->top = r->bottom - h; |
|
402 break; |
|
403 case WMSZ_TOPLEFT: |
|
404 r->top = r->bottom - h; |
|
405 r->left = r->right - w; |
|
406 break; |
|
407 case WMSZ_TOPRIGHT: |
|
408 r->top = r->bottom - h; |
|
409 r->right = r->left + w; |
|
410 break; |
|
411 } |
|
412 return TRUE; |
|
413 } |
|
414 |
|
415 // needed for wheel |
|
416 #if !defined(WM_MOUSEWHEEL) |
|
417 # define WM_MOUSEWHEEL 0x020A |
|
418 #endif //WM_MOUSEWHEEL |
|
419 #if !defined(GET_WHEEL_DELTA_WPARAM) |
|
420 # define GET_WHEEL_DELTA_WPARAM(wparam) ((short)HIWORD(wparam)) |
|
421 #endif //GET_WHEEL_DELTA_WPARAM |
|
422 |
|
423 case WM_MOUSEWHEEL: { |
|
424 int delta = GET_WHEEL_DELTA_WPARAM(wParam); |
|
425 |
|
426 if (delta < 0) { |
|
427 _cursor.wheel++; |
|
428 } else if (delta > 0) { |
|
429 _cursor.wheel--; |
|
430 } |
|
431 return 0; |
|
432 } |
|
433 |
|
434 case WM_ACTIVATEAPP: |
|
435 _wnd.has_focus = (bool)wParam; |
|
436 break; |
|
437 } |
|
438 return DefWindowProc(hwnd, msg, wParam, lParam); |
|
439 } |
|
440 |
|
441 static void RegisterWndClass(void) |
|
442 { |
|
443 static bool registered; |
|
444 if (!registered) { |
|
445 HINSTANCE hinst = GetModuleHandle(NULL); |
|
446 WNDCLASS wnd = { |
|
447 0, |
|
448 WndProcGdi, |
|
449 0, |
|
450 0, |
|
451 hinst, |
|
452 LoadIcon(hinst, MAKEINTRESOURCE(100)), |
|
453 LoadCursor(NULL, IDC_ARROW), |
|
454 0, |
|
455 0, |
|
456 "OTTD" |
|
457 }; |
|
458 registered = true; |
|
459 if (!RegisterClass(&wnd)) |
|
460 error("RegisterClass failed"); |
|
461 } |
|
462 } |
|
463 |
|
464 extern const char _openttd_revision[]; |
|
465 |
|
466 static void MakeWindow(bool full_screen) |
|
467 { |
|
468 _fullscreen = full_screen; |
|
469 |
|
470 _wnd.double_size = _double_size && !full_screen; |
|
471 |
|
472 // recreate window? |
|
473 if ((full_screen || _wnd.fullscreen) && _wnd.main_wnd) { |
|
474 DestroyWindow(_wnd.main_wnd); |
|
475 _wnd.main_wnd = 0; |
|
476 } |
|
477 |
|
478 if (full_screen) { |
|
479 DEVMODE settings; |
|
480 memset(&settings, 0, sizeof(DEVMODE)); |
|
481 settings.dmSize = sizeof(DEVMODE); |
|
482 settings.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; |
|
483 |
|
484 if (_fullscreen_bpp) { |
|
485 settings.dmBitsPerPel = _fullscreen_bpp; |
|
486 settings.dmFields |= DM_BITSPERPEL; |
|
487 } |
|
488 settings.dmPelsWidth = _wnd.width_org; |
|
489 settings.dmPelsHeight = _wnd.height_org; |
|
490 settings.dmDisplayFrequency = _display_hz; |
|
491 if (settings.dmDisplayFrequency != 0) |
|
492 settings.dmFields |= DM_DISPLAYFREQUENCY; |
|
493 if (ChangeDisplaySettings(&settings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) { |
|
494 MakeWindow(false); |
|
495 return; |
|
496 } |
|
497 } else if (_wnd.fullscreen) { |
|
498 // restore display? |
|
499 ChangeDisplaySettings(NULL, 0); |
|
500 } |
|
501 |
|
502 { |
|
503 RECT r; |
|
504 uint style; |
|
505 int x, y, w, h; |
|
506 |
|
507 _wnd.fullscreen = full_screen; |
|
508 if (_wnd.fullscreen) { |
|
509 style = WS_POPUP | WS_VISIBLE; |
|
510 SetRect(&r, 0, 0, _wnd.width_org, _wnd.height_org); |
|
511 } else { |
|
512 style = WS_OVERLAPPEDWINDOW | WS_VISIBLE; |
|
513 SetRect(&r, 0, 0, _wnd.width, _wnd.height); |
|
514 } |
|
515 |
|
516 AdjustWindowRect(&r, style, FALSE); |
|
517 w = r.right - r.left; |
|
518 h = r.bottom - r.top; |
|
519 x = (GetSystemMetrics(SM_CXSCREEN) - w) / 2; |
|
520 y = (GetSystemMetrics(SM_CYSCREEN) - h) / 2; |
|
521 |
|
522 if (_wnd.main_wnd) { |
|
523 SetWindowPos(_wnd.main_wnd, 0, x, y, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); |
|
524 } else { |
|
525 char Windowtitle[50]; |
|
526 |
|
527 snprintf(Windowtitle, lengthof(Windowtitle), "OpenTTD %s", _openttd_revision); |
|
528 |
|
529 _wnd.main_wnd = CreateWindow("OTTD", Windowtitle, style, x, y, w, h, 0, 0, GetModuleHandle(NULL), 0); |
|
530 if (_wnd.main_wnd == NULL) |
|
531 error("CreateWindow failed"); |
|
532 } |
|
533 } |
|
534 GameSizeChanged(); // invalidate all windows, force redraw |
|
535 } |
|
536 |
|
537 static bool AllocateDibSection(int w, int h) |
|
538 { |
|
539 BITMAPINFO *bi; |
|
540 HDC dc; |
|
541 |
|
542 w = clamp(w, 64, MAX_SCREEN_WIDTH); |
|
543 h = clamp(h, 64, MAX_SCREEN_HEIGHT); |
|
544 |
|
545 if (w == _screen.width && h == _screen.height) |
|
546 return false; |
|
547 |
|
548 _screen.width = w; |
|
549 _screen.pitch = (w + 3) & ~0x3; |
|
550 _screen.height = h; |
|
551 |
|
552 if (_wnd.alloced_bits) { |
|
553 free(_wnd.alloced_bits); |
|
554 _wnd.alloced_bits = NULL; |
|
555 } |
|
556 |
|
557 bi = alloca(sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256); |
|
558 memset(bi, 0, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256); |
|
559 bi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); |
|
560 |
|
561 if (_wnd.double_size) { |
|
562 w = (w + 3) & ~0x3; |
|
563 _wnd.alloced_bits = _wnd.buffer_bits = malloc(w * h); |
|
564 w *= 2; |
|
565 h *= 2; |
|
566 } |
|
567 |
|
568 bi->bmiHeader.biWidth = _wnd.width = w; |
|
569 bi->bmiHeader.biHeight = -(_wnd.height = h); |
|
570 |
|
571 bi->bmiHeader.biPlanes = 1; |
|
572 bi->bmiHeader.biBitCount = 8; |
|
573 bi->bmiHeader.biCompression = BI_RGB; |
|
574 |
|
575 if (_wnd.dib_sect) |
|
576 DeleteObject(_wnd.dib_sect); |
|
577 |
|
578 dc = GetDC(0); |
|
579 _wnd.dib_sect = CreateDIBSection(dc, bi, DIB_RGB_COLORS, (void**)&_wnd.bitmap_bits, NULL, 0); |
|
580 if (_wnd.dib_sect == NULL) |
|
581 error("CreateDIBSection failed"); |
|
582 ReleaseDC(0, dc); |
|
583 |
|
584 if (!_wnd.double_size) |
|
585 _wnd.buffer_bits = _wnd.bitmap_bits; |
|
586 |
|
587 return true; |
|
588 } |
|
589 |
|
590 static const uint16 default_resolutions[][2] = { |
|
591 { 640, 480}, |
|
592 { 800, 600}, |
|
593 {1024, 768}, |
|
594 {1152, 864}, |
|
595 {1280, 800}, |
|
596 {1280, 960}, |
|
597 {1280, 1024}, |
|
598 {1400, 1050}, |
|
599 {1600, 1200}, |
|
600 {1680, 1050}, |
|
601 {1920, 1200} |
|
602 }; |
|
603 |
|
604 static void FindResolutions(void) |
|
605 { |
|
606 int i = 0, n = 0; |
|
607 DEVMODE dm; |
|
608 |
|
609 while (EnumDisplaySettings(NULL, i++, &dm) != 0) { |
|
610 if (dm.dmBitsPerPel == 8 && IS_INT_INSIDE(dm.dmPelsWidth, 640, MAX_SCREEN_WIDTH + 1) && |
|
611 IS_INT_INSIDE(dm.dmPelsHeight, 480, MAX_SCREEN_HEIGHT + 1)){ |
|
612 int j; |
|
613 for (j = 0; j < n; j++) { |
|
614 if (_resolutions[j][0] == dm.dmPelsWidth && _resolutions[j][1] == dm.dmPelsHeight) break; |
|
615 } |
|
616 |
|
617 /* In the previous loop we have checked already existing/added resolutions if |
|
618 * they are the same as the new ones. If this is not the case (j == n); we have |
|
619 * looped all and found none, add the new one to the list. If we have reached the |
|
620 * maximum amount of resolutions, then quit querying the display */ |
|
621 if (j == n) { |
|
622 _resolutions[j][0] = dm.dmPelsWidth; |
|
623 _resolutions[j][1] = dm.dmPelsHeight; |
|
624 if (++n == lengthof(_resolutions)) break; |
|
625 } |
|
626 } |
|
627 } |
|
628 |
|
629 /* We have found no resolutions, show the default list */ |
|
630 if (n == 0) { |
|
631 memcpy(_resolutions, default_resolutions, sizeof(default_resolutions)); |
|
632 n = lengthof(default_resolutions); |
|
633 } |
|
634 |
|
635 _num_resolutions = n; |
|
636 SortResolutions(_num_resolutions); |
|
637 } |
|
638 |
|
639 |
|
640 static const char *Win32GdiStart(const char * const *parm) |
|
641 { |
|
642 memset(&_wnd, 0, sizeof(_wnd)); |
|
643 _wnd.cursor_visible = true; |
|
644 |
|
645 RegisterWndClass(); |
|
646 |
|
647 MakePalette(); |
|
648 |
|
649 FindResolutions(); |
|
650 |
|
651 // fullscreen uses those |
|
652 _wnd.width_org = _cur_resolution[0]; |
|
653 _wnd.height_org = _cur_resolution[1]; |
|
654 |
|
655 AllocateDibSection(_cur_resolution[0], _cur_resolution[1]); |
|
656 MarkWholeScreenDirty(); |
|
657 |
|
658 MakeWindow(_fullscreen); |
|
659 |
|
660 return NULL; |
|
661 } |
|
662 |
|
663 static void Win32GdiStop(void) |
|
664 { |
|
665 if (_wnd.fullscreen) ChangeDisplaySettings(NULL, 0); |
|
666 MyShowCursor(true); |
|
667 DeleteObject(_wnd.gdi_palette); |
|
668 DeleteObject(_wnd.dib_sect); |
|
669 DestroyWindow(_wnd.main_wnd); |
|
670 } |
|
671 |
|
672 // simple upscaler by 2 |
|
673 static void filter(int left, int top, int width, int height) |
|
674 { |
|
675 uint p = _screen.pitch; |
|
676 const Pixel *s = _wnd.buffer_bits + top * p + left; |
|
677 Pixel *d = _wnd.bitmap_bits + top * p * 4 + left * 2; |
|
678 |
|
679 for (; height > 0; height--) { |
|
680 int i; |
|
681 |
|
682 for (i = 0; i != width; i++) { |
|
683 d[i * 2] = d[i * 2 + 1] = d[i * 2 + p * 2] = d[i * 2 + 1 + p * 2] = s[i]; |
|
684 } |
|
685 s += p; |
|
686 d += p * 4; |
|
687 } |
|
688 } |
|
689 |
|
690 static void Win32GdiMakeDirty(int left, int top, int width, int height) |
|
691 { |
|
692 RECT r = { left, top, left + width, top + height }; |
|
693 |
|
694 if (_wnd.double_size) { |
|
695 filter(left, top, width, height); |
|
696 r.left *= 2; |
|
697 r.top *= 2; |
|
698 r.right *= 2; |
|
699 r.bottom *= 2; |
|
700 } |
|
701 InvalidateRect(_wnd.main_wnd, &r, FALSE); |
|
702 } |
|
703 |
|
704 static void CheckPaletteAnim(void) |
|
705 { |
|
706 if (_pal_last_dirty == -1) |
|
707 return; |
|
708 InvalidateRect(_wnd.main_wnd, NULL, FALSE); |
|
709 } |
|
710 |
|
711 static int Win32GdiMainLoop(void) |
|
712 { |
|
713 MSG mesg; |
|
714 uint32 next_tick = GetTickCount() + 30, cur_ticks; |
|
715 |
|
716 _wnd.running = true; |
|
717 |
|
718 while(true) { |
|
719 while (PeekMessage(&mesg, NULL, 0, 0, PM_REMOVE)) { |
|
720 InteractiveRandom(); // randomness |
|
721 TranslateMessage(&mesg); |
|
722 DispatchMessage(&mesg); |
|
723 } |
|
724 if (_exit_game) return ML_QUIT; |
|
725 if (_wnd.switch_driver) return ML_SWITCHDRIVER; |
|
726 |
|
727 #if defined(_DEBUG) |
|
728 if (_wnd.has_focus && GetAsyncKeyState(VK_SHIFT) < 0) { |
|
729 if ( |
|
730 #else |
|
731 if (_wnd.has_focus && GetAsyncKeyState(VK_TAB) < 0) { |
|
732 /* Disable speeding up game with ALT+TAB (if syskey is pressed, the |
|
733 * real key is in the upper 16 bits (see WM_SYSKEYDOWN in WndProcGdi()) */ |
|
734 if ((_pressed_key >> 16) & WKC_TAB && |
|
735 #endif |
|
736 !_networking && _game_mode != GM_MENU) |
|
737 _fast_forward |= 2; |
|
738 } else if (_fast_forward & 2) |
|
739 _fast_forward = 0; |
|
740 |
|
741 cur_ticks = GetTickCount(); |
|
742 if ((_fast_forward && !_pause) || cur_ticks > next_tick) |
|
743 next_tick = cur_ticks; |
|
744 |
|
745 if (cur_ticks == next_tick) { |
|
746 next_tick += 30; |
|
747 _ctrl_pressed = _wnd.has_focus && GetAsyncKeyState(VK_CONTROL)<0; |
|
748 _shift_pressed = _wnd.has_focus && GetAsyncKeyState(VK_SHIFT)<0; |
|
749 _dbg_screen_rect = _wnd.has_focus && GetAsyncKeyState(VK_CAPITAL)<0; |
|
750 |
|
751 // determine which directional keys are down |
|
752 if (_wnd.has_focus) { |
|
753 _dirkeys = |
|
754 (GetAsyncKeyState(VK_LEFT) < 0 ? 1 : 0) + |
|
755 (GetAsyncKeyState(VK_UP) < 0 ? 2 : 0) + |
|
756 (GetAsyncKeyState(VK_RIGHT) < 0 ? 4 : 0) + |
|
757 (GetAsyncKeyState(VK_DOWN) < 0 ? 8 : 0); |
|
758 } else |
|
759 _dirkeys = 0; |
|
760 |
|
761 GameLoop(); |
|
762 _cursor.delta.x = _cursor.delta.y = 0; |
|
763 |
|
764 if (_force_full_redraw) |
|
765 MarkWholeScreenDirty(); |
|
766 |
|
767 GdiFlush(); |
|
768 _screen.dst_ptr = _wnd.buffer_bits; |
|
769 UpdateWindows(); |
|
770 CheckPaletteAnim(); |
|
771 } else { |
|
772 Sleep(1); |
|
773 GdiFlush(); |
|
774 _screen.dst_ptr = _wnd.buffer_bits; |
|
775 DrawTextMessage(); |
|
776 DrawMouseCursor(); |
|
777 } |
|
778 } |
|
779 } |
|
780 |
|
781 static bool Win32GdiChangeRes(int w, int h) |
|
782 { |
|
783 _wnd.width = _wnd.width_org = w; |
|
784 _wnd.height = _wnd.height_org = h; |
|
785 |
|
786 MakeWindow(_fullscreen); // _wnd.fullscreen screws up ingame resolution switching |
|
787 |
|
788 return true; |
|
789 } |
|
790 |
|
791 static void Win32GdiFullScreen(bool full_screen) {MakeWindow(full_screen);} |
|
792 |
|
793 const HalVideoDriver _win32_video_driver = { |
|
794 Win32GdiStart, |
|
795 Win32GdiStop, |
|
796 Win32GdiMakeDirty, |
|
797 Win32GdiMainLoop, |
|
798 Win32GdiChangeRes, |
|
799 Win32GdiFullScreen, |
|
800 }; |
|