ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/Display_WIN32.h
Revision: 1.5
Committed: 2009-01-11T11:32:38Z (15 years, 2 months ago) by cebix
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.4: +2 -2 lines
Log Message:
const correctness

File Contents

# Content
1 /*
2 * Display_WIN32.h - C64 graphics display, emulator window handling,
3 * WIN32 specific stuff
4 *
5 * Frodo (C) 1994-1997,2002-2009 Christian Bauer
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 #include <shellapi.h>
23 #include <commctrl.h>
24
25 #include "C64.h"
26 #include "SAM.h"
27 #include "Version.h"
28 #include "VIC.h"
29 #include "resource.h"
30
31 #define NAME "Frodo"
32 #define TITLE (IsFrodoSC ? "FrodoSC" : "Frodo")
33
34 #ifdef DEBUG
35
36 class TimeScope
37 {
38
39 public:
40 TimeScope(const char *s)
41 {
42 tag = s;
43 QueryPerformanceCounter(&liStart);
44 }
45 ~TimeScope()
46 {
47 QueryPerformanceCounter(&liFinish);
48 OutputTime();
49 }
50
51 private:
52 void
53 OutputTime()
54 {
55 LARGE_INTEGER liFreq;
56
57 QueryPerformanceFrequency(&liFreq);
58 Debug("%s: %.0f usec\n", tag,
59 double(liFinish.LowPart - liStart.LowPart)
60 /liFreq.LowPart*1000000);
61 }
62 const char *tag;
63 LARGE_INTEGER liStart, liFinish;
64 };
65
66 #define TIMESCOPE(var, tag) TimeScope var(tag)
67
68 #else
69
70 #define TIMESCOPE(var, tag)
71
72 #endif
73
74 /*
75 C64 keyboard matrix:
76
77 Bit 7 6 5 4 3 2 1 0
78 0 CUD F5 F3 F1 F7 CLR RET DEL
79 1 SHL E S Z 4 A W 3
80 2 X T F C 6 D R 5
81 3 V U H B 8 G Y 7
82 4 N O K M 0 J I 9
83 5 , @ : . - L P +
84 6 / ^ = SHR HOM ; * £
85 7 R/S Q C= SPC 2 CTL <- 1
86 */
87
88 #define MATRIX(a,b) (((a) << 3) | (b))
89
90 #define KEY_F9 256
91 #define KEY_F10 257
92 #define KEY_F11 258
93 #define KEY_F12 259
94
95 #define KEY_FIRE 260
96 #define KEY_JUP 261
97 #define KEY_JDN 262
98 #define KEY_JLF 263
99 #define KEY_JRT 264
100 #define KEY_JUPLF 265
101 #define KEY_JUPRT 266
102 #define KEY_JDNLF 267
103 #define KEY_JDNRT 268
104 #define KEY_CENTER 269
105
106 #define KEY_NUMLOCK 270
107
108 #define KEY_KPPLUS 271
109 #define KEY_KPMINUS 272
110 #define KEY_KPMULT 273
111 #define KEY_KPDIV 274
112 #define KEY_KPENTER 275
113 #define KEY_KPPERIOD 276
114
115 #define KEY_PAUSE 277
116 #define KEY_ALTENTER 278
117 #define KEY_CTRLENTER 279
118
119 #define VK_bracketleft 0xdb
120 #define VK_bracketright 0xdd
121 #define VK_comma 0xbc
122 #define VK_period 0xbe
123 #define VK_slash 0xbf
124 #define VK_semicolon 0xba
125 #define VK_grave 0xc0
126 #define VK_minus 0xbd
127 #define VK_equal 0xbb
128 #define VK_quote 0xde
129 #define VK_backslash 0xdc
130
131 static C64Display *TheDisplay;
132 static int keystate[256];
133 static UBYTE rev_matrix[8], key_matrix[8];
134 static int quit = 0;
135 static int numlock = 0;
136 static int joystate = 0xff;
137
138 static RECT rcScreen;
139 static RECT rcLast;
140 static RECT rcWindow;
141 static RECT rcWork;
142 static BOOL need_new_color_table = FALSE;
143 static int view_x, view_y;
144
145 static int led_rows = 16;
146
147 static HCURSOR invisible_cursor;
148 static HCURSOR arrow_cursor;
149
150 static HFONT led_font;
151
152 static HPEN led_highlight;
153 static HPEN led_shadow;
154
155 static HBRUSH led_brush;
156 static HBRUSH off_brush;
157 static HBRUSH error_off_brush;
158 static HBRUSH on_brush;
159 static HBRUSH error_on_brush;
160
161 // Not fully working yet.
162 #ifdef WORKBUFFER_BITMAP
163 static BOOL workbuffer_bitmap = FALSE;
164 static BOOL workbuffer_locked = FALSE;
165 static DDSURFACEDESC bitmap_ddsd;
166 #endif
167
168 C64Display::DisplayMode default_modes[] = {
169 { 320, 200, 8 },
170 { 320, 240, 8 },
171 { 512, 384, 8 },
172 { 640, 400, 8 },
173 { 640, 480, 8 },
174 { 320, 200, 16 },
175 { 320, 240, 16 },
176 { 512, 384, 16 },
177 { 640, 400, 16 },
178 { 640, 480, 16 },
179 };
180 static int num_default_modes =
181 sizeof(default_modes)/sizeof(C64Display::DisplayMode);
182
183 static C64Display::DisplayMode *display_modes = NULL;
184 static int num_display_modes = 0;
185 static int max_display_modes = 16;
186
187 int C64Display::GetNumDisplayModes() const
188 {
189 if (num_display_modes == 0)
190 return num_default_modes;
191 return num_display_modes;
192 }
193
194 const C64Display::DisplayMode *C64Display::GetDisplayModes() const
195 {
196 if (num_display_modes == 0)
197 return default_modes;
198 return display_modes;
199 }
200
201 long ShowRequester(const char *str, const char *button1, const char *button2)
202 {
203 if (!TheDisplay) {
204 MessageBox(hwnd, str, "Frodo", MB_OK | MB_ICONSTOP);
205 return FALSE;
206 }
207 return TheDisplay->ShowRequester(str, button1, button2);
208 }
209
210 /*
211 * Display constructor: Create window/screen
212 */
213
214 C64Display::C64Display(C64 *the_c64) : TheC64(the_c64)
215 {
216 in_constructor = TRUE;
217 in_destructor = FALSE;
218
219 TheDisplay = this;
220 speed_index = 0;
221
222 pDD = NULL;
223 pPrimary = NULL;
224 pBack = NULL;
225 pWork = NULL;
226 pClipper = NULL;
227 pPalette = NULL;
228 active = FALSE;
229 paused = FALSE;
230 waiting = FALSE;
231 show_leds = ThePrefs.ShowLEDs;
232 full_screen = ThePrefs.DisplayType == DISPTYPE_SCREEN;
233
234 // Turn LEDs off.
235 for (int i = 0; i < 4; i++)
236 led_state[i] = old_led_state[i] = LED_OFF;
237
238 // Allocate chunky buffer to draw into.
239 chunky_buf = new UBYTE[DISPLAY_X * DISPLAY_Y];
240
241 CalcViewPort();
242
243 ResetKeyboardState();
244
245 if (!MakeWindow()) {
246 ShowRequester("Failed to create window.", "Quit");
247 Quit();
248 }
249 else {
250 WindowTitle();
251
252 if (!StartDirectDraw()) {
253 ShowRequester(failure_message, "Quit");
254 Quit();
255 }
256 else
257 draw_led_bar();
258 }
259
260 in_constructor = FALSE;
261 }
262
263 /*
264 * Display destructor
265 */
266
267 C64Display::~C64Display()
268 {
269 in_destructor = TRUE;
270
271 Debug("~C64Display\n");
272
273 StopDirectDraw();
274
275 // Offer to save now that we are not in full screen mode.
276 OfferSave();
277
278 // Free the display modes table.
279 delete[] display_modes;
280
281 // Free chunky buffer
282 delete chunky_buf;
283
284 // Destroy the main window.
285 DestroyWindow(hwnd);
286
287 // Drain the window message queue.
288 for (;;)
289 {
290 MSG msg;
291 if (!GetMessage(&msg, NULL, 0, 0))
292 break;
293 if (ThePrefs.SystemKeys)
294 TranslateMessage(&msg);
295 DispatchMessage(&msg);
296 }
297
298 DeleteObjects();
299
300 in_destructor = FALSE;
301 }
302
303 /*
304 * Prefs may have changed
305 */
306
307 void C64Display::NewPrefs(Prefs *prefs)
308 {
309 }
310
311 void C64Display::DeleteObjects()
312 {
313 // Delete objects we created.
314 DeleteObject(led_highlight);
315 DeleteObject(led_shadow);
316 DeleteObject(led_font);
317 DeleteObject(led_brush);
318 DeleteObject(off_brush);
319 DeleteObject(error_off_brush);
320 DeleteObject(on_brush);
321 DeleteObject(error_on_brush);
322 }
323
324 BOOL C64Display::CalcViewPort()
325 {
326 int old_view_x = view_x, old_view_y = view_y;
327 const char *view_port = ThePrefs.ViewPort;
328 if (view_port[0] == '\0' ||
329 stricmp(view_port, "Default") == 0)
330 view_port = NULL;
331 if (!view_port || sscanf(view_port, "%dx%d", &view_x, &view_y) != 2) {
332 view_x = DISPLAY_X;
333 view_y = DISPLAY_Y;
334 }
335 SetRect(&rcWork, 0, 0, view_x, view_y);
336 if (view_x != old_view_x || view_y != old_view_y)
337 return TRUE;
338 return FALSE;
339 }
340
341
342 BOOL C64Display::ResizeWindow(int side, RECT *pRect)
343 {
344 // Compute size of non-client borders.
345 DWORD style = GetWindowLong(hwnd, GWL_STYLE);
346 RECT rc;
347 SetRect(&rc, 0, 0, view_x, view_y);
348 BOOL has_menu = GetMenu(hwnd) != NULL;
349 AdjustWindowRect(&rc, style, has_menu);
350 if (ThePrefs.ShowLEDs)
351 rc.bottom += led_rows;
352 int nc_x = rc.right - rc.left - view_x;
353 int nc_y = rc.bottom - rc.top - view_y;
354
355 // Compute client area corresponding to resizing.
356 int old_x = pRect->right - pRect->left - nc_x;
357 int old_y = pRect->bottom - pRect->top - nc_y;
358
359 // Compute nearest integral scaling numerators.
360 int d = ThePrefs.ScalingDenominator;
361 int x = (old_x + view_x/d/2)/(view_x/d);
362 if (x == 0)
363 x = 1;
364 int y = (old_y + view_y/4)/(view_y/d);
365 if (y == 0)
366 y = 1;
367
368 // When resizing corners make the scale factors agree.
369 switch (side) {
370 case WMSZ_BOTTOMRIGHT:
371 case WMSZ_BOTTOMLEFT:
372 case WMSZ_TOPRIGHT:
373 case WMSZ_TOPLEFT:
374 if (x < y)
375 y = x;
376 else
377 x = y;
378 }
379
380 // Compute the quantized size of the window area.
381 int new_x = x*(view_x/d) + nc_x;
382 int new_y = y*(view_y/d) + nc_y;
383
384 // Adjust the resizing rectangle.
385 switch (side) {
386
387 case WMSZ_BOTTOMRIGHT:
388 case WMSZ_BOTTOMLEFT:
389 case WMSZ_BOTTOM:
390 pRect->bottom = pRect->top + new_y;
391 break;
392
393 case WMSZ_TOPRIGHT:
394 case WMSZ_TOPLEFT:
395 case WMSZ_TOP:
396 pRect->top = pRect->bottom - new_y;
397 break;
398 }
399 switch (side) {
400
401 case WMSZ_TOPRIGHT:
402 case WMSZ_BOTTOMRIGHT:
403 case WMSZ_RIGHT:
404 pRect->right = pRect->left + new_x;
405 break;
406
407 case WMSZ_TOPLEFT:
408 case WMSZ_BOTTOMLEFT:
409 case WMSZ_LEFT:
410 pRect->left = pRect->right - new_x;
411 break;
412 }
413
414 return TRUE;
415 }
416
417 /*
418 * Update speedometer
419 */
420
421 void C64Display::Speedometer(int speed)
422 {
423 Debug("speed = %d %%\n", speed);
424 speed_index = speed;
425
426 if (full_screen)
427 return;
428
429 if (!ThePrefs.ShowLEDs) {
430 WindowTitle();
431 return;
432 }
433
434 if (speed_index == 0)
435 return;
436
437 HDC hdc = GetDC(hwnd);
438 RECT rc;
439 GetClientRect(hwnd, &rc);
440 rc.top = rc.bottom - led_rows;
441 rc.right = rc.left + (rc.right - rc.left)/5;
442 FillRect(hdc, &rc, led_brush);
443 SelectObject(hdc, led_font);
444 SetTextAlign(hdc, TA_TOP | TA_LEFT);
445 SetBkMode(hdc, TRANSPARENT);
446 SetTextColor(hdc, (COLORREF) GetSysColor(COLOR_MENUTEXT));
447 char str[128];
448 if (IsFrodoSC)
449 sprintf(str, "%d%%", speed_index);
450 else
451 sprintf(str, "%d%%", speed_index);
452 int x = rc.left + 4;
453 int y = rc.top + 2;
454 TextOut(hdc, x, y, str, strlen(str));
455 ReleaseDC(hwnd, hdc);
456 }
457
458
459 /*
460 * Return pointer to bitmap data
461 */
462
463 UBYTE *C64Display::BitmapBase()
464 {
465 #ifdef WORKBUFFER_BITMAP
466 if (colors_depth == 8 && pWork) {
467 if (workbuffer_locked) {
468 pWork->Unlock(NULL);
469 workbuffer_locked = FALSE;
470 }
471 HRESULT ddrval;
472 for (;;) {
473 bitmap_ddsd.dwSize = sizeof(bitmap_ddsd);
474 ddrval = pWork->Lock(NULL, &bitmap_ddsd, 0, NULL);
475 if (ddrval != DDERR_WASSTILLDRAWING)
476 break;
477 }
478 if (ddrval == DD_OK) {
479 workbuffer_locked = TRUE;
480 workbuffer_bitmap = TRUE;
481 return (UBYTE *) bitmap_ddsd.lpSurface;
482 }
483 }
484 workbuffer_bitmap = FALSE;
485 #endif
486 return chunky_buf;
487 }
488
489
490 /*
491 * Return number of bytes per row
492 */
493
494 int C64Display::BitmapXMod()
495 {
496 #ifdef WORKBUFFER_BITMAP
497 if (workbuffer_locked)
498 return bitmap_ddsd.lPitch;
499 #endif
500 return DISPLAY_X;
501 }
502
503
504 /*
505 * Freshen keyboard state
506 */
507
508 void C64Display::PollKeyboard(UBYTE *CIA_key_matrix, UBYTE *CIA_rev_matrix, UBYTE *joystick)
509 {
510 //Debug("Display::PollKeyboard\n");
511
512 #ifdef WORKBUFFER_BITMAP
513 if (workbuffer_locked) {
514 pWork->Unlock(NULL);
515 workbuffer_locked = FALSE;
516 }
517 #endif
518
519 for (;;)
520 {
521 MSG msg;
522 if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
523 break;
524 if (ThePrefs.SystemKeys)
525 TranslateMessage(&msg);
526 DispatchMessage(&msg);
527 }
528
529 *joystick = joystate;
530 memcpy(CIA_key_matrix, key_matrix, sizeof(key_matrix));
531 memcpy(CIA_rev_matrix, rev_matrix, sizeof(rev_matrix));
532 }
533
534 /*
535 * Check if NumLock is down (for switching the joystick keyboard emulation)
536 */
537
538 bool C64Display::NumLock()
539 {
540 return numlock;
541 }
542
543
544 /*
545 * Allocate C64 colors
546 */
547
548 void C64Display::InitColors(UBYTE *array)
549 {
550 if (colors_depth == 8) {
551 for (int i = 0; i < 256; i++)
552 array[i] = colors[i & 0x0f];
553 }
554 else {
555 for (int i = 0; i < 256; i++)
556 array[i] = i & 0x0f;
557 }
558 }
559
560
561 long C64Display::ShowRequester(const char *str, const char *button1, const char *button2)
562 {
563 // This could be a lot nicer but quick and dirty is fine with me.
564 char message[1024];
565 strcpy(message, str);
566 strcat(message, "\nPress OK to ");
567 strcat(message, button1);
568 if (button2) {
569 strcat(message, ", Cancel to ");
570 strcat(message, button2);
571 }
572 strcat(message, ".");
573 UINT type;
574 if (button2)
575 type = MB_OKCANCEL | MB_ICONQUESTION;
576 else
577 type = MB_OK | MB_ICONSTOP;
578 Pause();
579 if (full_screen)
580 StopDirectDraw();
581 int result = MessageBox(hwnd, message, NAME " Error", type);
582 if (full_screen)
583 StartDirectDraw();
584 Resume();
585 if (result == IDCANCEL)
586 return TRUE;
587 return FALSE;
588 }
589
590 void C64Display::WaitUntilActive()
591 {
592 Debug("waiting until not paused...\n");
593 waiting = TRUE;
594 WindowTitle();
595 for (;;) {
596
597 // Check for termination condition.
598 if (!paused || quit)
599 break;
600
601 // Process message queue.
602 MSG msg;
603 if (GetMessage(&msg, NULL, 0, 0) != TRUE)
604 break;
605
606 // Always translate system keys while paused.
607 TranslateMessage(&msg);
608 DispatchMessage(&msg);
609 }
610 waiting = FALSE;
611 Debug("...done waiting\n");
612 WindowTitle();
613 ResetKeyboardState();
614 }
615
616 void C64Display::ResetKeyboardState()
617 {
618 memset(keystate, 0, sizeof(keystate));
619 memset(key_matrix, 0xff, sizeof(key_matrix));
620 memset(rev_matrix, 0xff, sizeof(rev_matrix));
621 joystate = 0xff;
622 }
623
624 BOOL C64Display::MakeWindow()
625 {
626 // Set up and register window class.
627 WNDCLASS wc;
628 wc.style = CS_HREDRAW | CS_VREDRAW;
629 wc.lpfnWndProc = StaticWindowProc;
630 wc.cbClsExtra = 0;
631 wc.cbWndExtra = 0;
632 wc.hInstance = hInstance;
633 wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(FRODO_ICON));
634 wc.hCursor = NULL;
635 wc.hbrBackground = NULL;
636 wc.lpszMenuName = MAKEINTRESOURCE(IDR_MAIN_MENU);
637 wc.lpszClassName = NAME;
638 RegisterClass(&wc);
639
640 // Set up our preferred styles for our window depending on the mode.
641 windowed_style = WS_VISIBLE | WS_SYSMENU | WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_THICKFRAME;
642 fullscreen_style = WS_POPUP | WS_VISIBLE;
643
644 // Compute the initial window size.
645 DWORD style = windowed_style;
646 RECT rc;
647 int n = ThePrefs.ScalingNumerator;
648 int d = ThePrefs.ScalingDenominator;
649 SetRect(&rc, 0, 0, n*view_x/d, n*view_y/d);
650 BOOL has_menu = wc.lpszMenuName != NULL;
651 AdjustWindowRect(&rc, style, has_menu);
652 if (ThePrefs.ShowLEDs)
653 rc.bottom += led_rows;
654 int x_size = rc.right - rc.left;
655 int y_size = rc.bottom - rc.top;
656
657 // Create the window and save the initial position.
658 hwnd = CreateWindowEx(0, NAME, TITLE, style, CW_USEDEFAULT, 0, x_size, y_size, NULL, NULL, hInstance, NULL);
659 GetWindowRect(hwnd, &rcLast);
660 SetRect(&rcScreen, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
661
662 // Load cursors.
663 invisible_cursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_INVISIBLE));
664 arrow_cursor = LoadCursor(0, MAKEINTRESOURCE(IDC_ARROW));
665
666 // Create fonts, pens, brushes, etc.
667 CreateObjects();
668
669 if (!hwnd)
670 return FALSE;
671
672 ShowWindow(hwnd, nCmdShow);
673 UpdateWindow(hwnd);
674
675 return TRUE;
676 }
677
678 void C64Display::CreateObjects()
679 {
680 // Create fonts.
681 led_font = CreateFont(14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
682 VARIABLE_PITCH | FF_SWISS, "");
683
684 // Create pens.
685 led_highlight = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_3DHIGHLIGHT));
686 led_shadow = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_3DSHADOW));
687
688 // Create brushes.
689 LOGBRUSH logbrush;
690 logbrush.lbStyle = BS_SOLID;
691 logbrush.lbHatch = 0;
692 logbrush.lbColor = GetSysColor(COLOR_MENU);
693 led_brush = CreateBrushIndirect(&logbrush);
694 logbrush.lbColor = RGB(0x00, 0x00, 0x00); // black
695 off_brush = CreateBrushIndirect(&logbrush);
696 logbrush.lbColor = RGB(0x00, 0x00, 0x00); // black
697 error_off_brush = CreateBrushIndirect(&logbrush);
698 logbrush.lbColor = RGB(0x00, 0xff, 0x00); // green
699 on_brush = CreateBrushIndirect(&logbrush);
700 logbrush.lbColor = RGB(0xff, 0x00, 0x00); // red
701 error_on_brush = CreateBrushIndirect(&logbrush);
702 }
703
704 HRESULT CALLBACK C64Display::StaticWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
705 {
706 return TheDisplay->WindowProc(hWnd, message, wParam, lParam);
707 }
708
709 long C64Display::WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
710 {
711 Debug("window message: 0x%x\n", message);
712
713 switch (message) {
714
715 case WM_SYSCOLORCHANGE:
716 DeleteObjects();
717 CreateObjects();
718 InvalidateRect(hwnd, NULL, FALSE);
719 break;
720
721 case WM_MOUSEMOVE:
722 SetCursor(ThePrefs.HideCursor ? invisible_cursor : arrow_cursor);
723 break;
724
725 case WM_MENUCHAR:
726 // Eat Alt-foo characters so that it doesn't beep.
727 if (HIWORD(wParam) == 0)
728 return MAKELONG(0, 1);
729 break;
730
731 case WM_ENTERSIZEMOVE:
732 Pause();
733 break;
734
735 case WM_EXITSIZEMOVE:
736 Resume();
737 break;
738
739 case WM_SIZING:
740 ResizeWindow(wParam, (RECT *) lParam);
741 return TRUE;
742
743 case WM_SIZE:
744 case WM_MOVE:
745 if (full_screen)
746 SetRect(&rcWindow, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
747 else {
748 GetClientRect(hWnd, &rcWindow);
749 if (ThePrefs.ShowLEDs)
750 rcWindow.bottom -= led_rows;
751 ClientToScreen(hWnd, (LPPOINT) &rcWindow);
752 ClientToScreen(hWnd, (LPPOINT) &rcWindow + 1);
753
754 // Align the client rect to a four-byte
755 // boundary because this can triple the
756 // speed of a memcpy to the display.
757 int align_to = 4;
758 int misalignment = rcWindow.left % align_to;
759 if (misalignment == 0)
760 Update();
761 else {
762 if (misalignment > align_to/2)
763 misalignment -= align_to;
764 RECT rc;
765 GetWindowRect(hwnd, &rc);
766 MoveWindow(hwnd, rc.left - misalignment,
767 rc.top, rc.right - rc.left,
768 rc.bottom - rc.top, TRUE);
769 }
770 }
771 break;
772
773 case WM_DISPLAYCHANGE:
774 if (!full_screen)
775 ResumeDirectDraw();
776 SetRect(&rcScreen, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
777 break;
778
779 case WM_ACTIVATE:
780 Debug("WM_ACTIVATE\n");
781 {
782 int old_active = active;
783 active = LOWORD(wParam) != WA_INACTIVE;
784 if (ThePrefs.AutoPause && active != old_active) {
785 if (!active)
786 Pause();
787 else
788 Resume();
789 }
790 }
791 if (active) {
792 ResumeDirectDraw();
793 ResetKeyboardState();
794 }
795
796 // Kick the message loop since this was sent to us, not posted.
797 PostMessage(hWnd, WM_USER, 0, 0);
798 break;
799
800 case WM_COMMAND:
801 {
802 int id = LOWORD(wParam);
803 switch (id) {
804
805 case ID_FILE_NEW:
806 {
807 OfferSave();
808 Prefs *prefs = new Prefs;
809 TheC64->NewPrefs(prefs);
810 ThePrefs = *prefs;
811 ThePrefsOnDisk = ThePrefs;
812 delete prefs;
813 strcpy(TheApp->prefs_path, "Untitled.fpr");
814 NewPrefs();
815 }
816 break;
817
818 case ID_FILE_OPEN:
819 Pause();
820 OfferSave();
821 if (FileNameDialog(TheApp->prefs_path)) {
822 Prefs *prefs = new Prefs;
823 prefs->Load(TheApp->prefs_path);
824 TheC64->NewPrefs(prefs);
825 ThePrefs = *prefs;
826 delete prefs;
827 NewPrefs();
828 }
829 Resume();
830 break;
831
832 case ID_FILE_SAVE:
833 ThePrefs.Save(TheApp->prefs_path);
834 break;
835
836 case ID_FILE_SAVEAS:
837 Pause();
838 if (FileNameDialog(TheApp->prefs_path, TRUE)) {
839 ThePrefs.Save(TheApp->prefs_path);
840 WindowTitle();
841 }
842 Resume();
843 break;
844
845 case ID_FILE_EX:
846 PostMessage(hWnd, WM_CLOSE, 0, 0);
847 break;
848
849 case ID_TOOLS_PREFERENCES:
850 Pause();
851 TheApp->RunPrefsEditor();
852 NewPrefs();
853 Resume();
854 break;
855
856 case ID_TOOLS_FULLSCREEN:
857 Pause();
858 StopDirectDraw();
859 full_screen = !full_screen;
860 if (!StartDirectDraw()) {
861 StopDirectDraw();
862 full_screen = !full_screen;
863 StartDirectDraw();
864 if (!full_screen)
865 ShowRequester(failure_message, "Continue");
866 }
867 Resume();
868 CheckMenuItem(GetMenu(hWnd), ID_TOOLS_FULLSCREEN, full_screen ? MF_CHECKED : MF_UNCHECKED);
869 if (paused)
870 Update();
871 break;
872
873 case ID_TOOLS_RESETDIRECTDRAW:
874 ResetDirectDraw();
875 break;
876
877 case ID_TOOLS_PAUSE:
878 if (!paused)
879 Pause();
880 else {
881 // XXX: Shouldn't happen but be safe.
882 while (paused)
883 Resume();
884 ResetKeyboardState();
885 }
886 CheckMenuItem(GetMenu(hWnd), ID_TOOLS_PAUSE, paused ? MF_CHECKED : MF_UNCHECKED);
887 break;
888
889 case ID_TOOLS_RESETC64:
890 TheC64->Reset();
891 break;
892
893 case ID_TOOLS_INSERTNEXTDISK:
894 InsertNextDisk();
895 break;
896
897 case ID_TOOLS_SAM:
898 Pause();
899 MessageBox(hWnd, "SAM not yet implemented.", NAME, MB_OK);
900 Resume();
901 break;
902
903 case ID_HELP_CONTENTS:
904 case ID_HELP_KEYBOARD:
905 case ID_HELP_SETTINGS:
906 {
907 const char *html;
908 switch (id) {
909 case ID_HELP_CONTENTS: html = "Main"; break;
910 case ID_HELP_KEYBOARD: html = "keyboard"; break;
911 case ID_HELP_SETTINGS: html = "settings"; break;
912 }
913 char helpfile[256];
914 sprintf(helpfile, "%s\\Docs\\%s.html", AppDirPath, html);
915 ShellExecute(0, 0, helpfile, 0, 0, SW_NORMAL);
916 }
917 break;
918
919 case ID_HELP_ABOUT:
920 {
921 Pause();
922 char message[256];
923 sprintf(message, "%s by %s\n%s by %s",
924 VERSION_STRING,
925 "Christian Bauer",
926 "WIN32 port",
927 "J. Richard Sladkey");
928 MessageBox(hWnd, message, NAME, MB_OK);
929 Resume();
930 }
931 break;
932 }
933 ResetKeyboardState();
934 }
935 break;
936
937 case WM_CLOSE:
938 Quit();
939 return 0;
940
941 case WM_DESTROY:
942 PostQuitMessage(0);
943 break;
944
945 case WM_QUERYNEWPALETTE:
946 if (!full_screen && pPalette && pPrimary) {
947 SetPalettes();
948 BuildColorTable();
949 if (!active)
950 Update();
951 }
952 break;
953
954 case WM_PALETTECHANGED:
955 if (!full_screen) {
956 if ((HWND) wParam != hWnd) {
957 need_new_color_table = TRUE;
958 InvalidateRect(hwnd, NULL, FALSE);
959 }
960 }
961 break;
962
963 case WM_PAINT:
964 if (!full_screen)
965 {
966 PAINTSTRUCT ps;
967 HDC hdc = BeginPaint(hWnd, &ps);
968 EndPaint(hWnd, &ps);
969 if (need_new_color_table) {
970 BuildColorTable();
971 need_new_color_table = FALSE;
972 }
973 if (paused)
974 Update();
975 draw_led_bar();
976 Speedometer(speed_index);
977 return 0;
978 }
979 break;
980
981 case WM_ENTERMENULOOP:
982 Pause();
983 break;
984
985 case WM_EXITMENULOOP:
986 Resume();
987 ResetKeyboardState();
988 break;
989
990 case WM_SYSKEYDOWN:
991 case WM_KEYDOWN:
992 Debug("Display::WindowProc: KEYDOWN: 0x%x\n", wParam);
993 {
994 int kc = VirtKey2C64(wParam, lParam);
995 switch (kc) {
996
997 case KEY_PAUSE:
998 PostMessage(hWnd, WM_COMMAND, ID_TOOLS_PAUSE, 0);
999 break;
1000
1001 case KEY_KPPLUS:
1002 if (ThePrefs.SkipFrames < 10)
1003 ThePrefs.SkipFrames++;
1004 break;
1005
1006 case KEY_KPMINUS:
1007 if (ThePrefs.SkipFrames > 1)
1008 ThePrefs.SkipFrames--;
1009 break;
1010
1011 case KEY_KPMULT:
1012 ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed;
1013 break;
1014
1015 case KEY_KPDIV:
1016 {
1017 Prefs *prefs = new Prefs(ThePrefs);
1018 prefs->Emul1541Proc = !prefs->Emul1541Proc;
1019 TheC64->NewPrefs(prefs);
1020 ThePrefs = *prefs;
1021 delete prefs;
1022 }
1023 break;
1024
1025 case KEY_KPPERIOD:
1026 ThePrefs.JoystickSwap = !ThePrefs.JoystickSwap;
1027 break;
1028
1029 case KEY_F9:
1030 PostMessage(hWnd, WM_COMMAND, ID_TOOLS_INSERTNEXTDISK, 0);
1031 break;
1032
1033 case KEY_ALTENTER:
1034 PostMessage(hWnd, WM_COMMAND, ID_TOOLS_FULLSCREEN, 0);
1035 break;
1036
1037 case KEY_CTRLENTER:
1038 PostMessage(hWnd, WM_COMMAND, ID_TOOLS_RESETDIRECTDRAW, 0);
1039 break;
1040
1041 case KEY_F10:
1042 PostMessage(hWnd, WM_CLOSE, 0, 0);
1043 break;
1044
1045 case KEY_F11:
1046 if (!paused)
1047 TheC64->NMI();
1048 break;
1049
1050 case KEY_F12:
1051 if (!paused)
1052 TheC64->Reset();
1053 break;
1054
1055 case KEY_FIRE:
1056 joystate &= ~0x10;
1057 break;
1058
1059 case KEY_JUP:
1060 joystate |= 0x02;
1061 joystate &= ~0x01;
1062 break;
1063
1064 case KEY_JDN:
1065 joystate |= 0x01;
1066 joystate &= ~0x02;
1067 break;
1068
1069 case KEY_JLF:
1070 joystate |= 0x08;
1071 joystate &= ~0x04;
1072 break;
1073
1074 case KEY_JRT:
1075 joystate |= 0x04;
1076 joystate &= ~0x08;
1077 break;
1078
1079 case KEY_JUPLF:
1080 joystate |= 0x0a;
1081 joystate &= ~0x05;
1082 break;
1083
1084 case KEY_JUPRT:
1085 joystate |= 0x06;
1086 joystate &= ~0x09;
1087 break;
1088
1089 case KEY_JDNLF:
1090 joystate |= 0x09;
1091 joystate &= ~0x06;
1092 break;
1093
1094 case KEY_JDNRT:
1095 joystate |= 0x05;
1096 joystate &= ~0x0a;
1097 break;
1098
1099 case KEY_CENTER:
1100 joystate |= 0x0f;
1101 break;
1102
1103 default:
1104 if (kc < 0 || kc >= 256)
1105 break;
1106 if (keystate[kc])
1107 break;
1108 keystate[kc] = 1;
1109 int c64_byte = kc >> 3;
1110 int c64_bit = kc & 7;
1111 int shifted = kc & 128;
1112 c64_byte &= 7;
1113 if (shifted) {
1114 key_matrix[6] &= 0xef;
1115 rev_matrix[4] &= 0xbf;
1116 }
1117 key_matrix[c64_byte] &= ~(1 << c64_bit);
1118 rev_matrix[c64_bit] &= ~(1 << c64_byte);
1119 break;
1120 }
1121 return 0;
1122 }
1123 break;
1124
1125 case WM_SYSKEYUP:
1126 case WM_KEYUP:
1127 Debug("Display::WindowProc: KEYUP: 0x%x\n", wParam);
1128 {
1129 int kc = VirtKey2C64(wParam, lParam);
1130 switch (kc) {
1131
1132 case KEY_FIRE:
1133 joystate |= 0x10;
1134 break;
1135
1136 case KEY_JUP:
1137 joystate |= 0x01;
1138 break;
1139
1140 case KEY_JDN:
1141 joystate |= 0x02;
1142 break;
1143
1144 case KEY_JLF:
1145 joystate |= 0x04;
1146 break;
1147
1148 case KEY_JRT:
1149 joystate |= 0x08;
1150 break;
1151
1152 case KEY_JUPLF:
1153 joystate |= 0x05;
1154 break;
1155
1156 case KEY_JUPRT:
1157 joystate |= 0x09;
1158 break;
1159
1160 case KEY_JDNLF:
1161 joystate |= 0x06;
1162 break;
1163
1164 case KEY_JDNRT:
1165 joystate |= 0x0a;
1166 break;
1167
1168 default:
1169 if (kc < 0 || kc >= 256)
1170 break;
1171 if (!keystate[kc])
1172 break;
1173 keystate[kc] = 0;
1174 int c64_byte = kc >> 3;
1175 int c64_bit = kc & 7;
1176 int shifted = kc & 128;
1177 c64_byte &= 7;
1178 if (shifted) {
1179 key_matrix[6] |= 0x10;
1180 rev_matrix[4] |= 0x40;
1181 }
1182 key_matrix[c64_byte] |= (1 << c64_bit);
1183 rev_matrix[c64_bit] |= (1 << c64_byte);
1184 break;
1185 }
1186 return 0;
1187 }
1188 break;
1189 }
1190
1191 return DefWindowProc(hWnd, message, wParam, lParam);
1192 }
1193
1194 int C64Display::VirtKey2C64(int virtkey, DWORD keydata)
1195 {
1196 int ext = keydata & 0x01000000;
1197 int sc = (keydata & 0x00ff0000) >> 16;
1198 int result = -1;
1199
1200 switch (virtkey) {
1201
1202 case VK_NUMPAD0: numlock = 1; return KEY_FIRE;
1203 case VK_NUMPAD1: numlock = 1; return KEY_JDNLF;
1204 case VK_NUMPAD2: numlock = 1; return KEY_JDN;
1205 case VK_NUMPAD3: numlock = 1; return KEY_JDNRT;
1206 case VK_NUMPAD4: numlock = 1; return KEY_JLF;
1207 case VK_NUMPAD5: numlock = 1; return KEY_CENTER;
1208 case VK_NUMPAD6: numlock = 1; return KEY_JRT;
1209 case VK_NUMPAD7: numlock = 1; return KEY_JUPLF;
1210 case VK_NUMPAD8: numlock = 1; return KEY_JUP;
1211 case VK_NUMPAD9: numlock = 1; return KEY_JUPRT;
1212
1213 case VK_NUMLOCK: return KEY_NUMLOCK;
1214 case VK_MULTIPLY: return KEY_KPMULT;
1215 case VK_DIVIDE: return KEY_KPDIV;
1216 case VK_SUBTRACT: return KEY_KPMINUS;
1217 case VK_ADD: return KEY_KPPLUS;
1218 case VK_DECIMAL: return KEY_KPPERIOD;
1219
1220 case VK_F9: return KEY_F9;
1221 case VK_F10: return KEY_F10;
1222 case VK_F11: return KEY_F11;
1223 case VK_F12: return KEY_F12;
1224 case VK_PAUSE: return KEY_PAUSE;
1225
1226 case VK_BACK: return MATRIX(0,0);
1227 case VK_DELETE: return ext ? MATRIX(0,0) : /*KP*/ KEY_KPPERIOD;
1228 case VK_TAB: return -1;
1229 case VK_RETURN:
1230 if ((GetKeyState(VK_MENU) & 0x8000))
1231 return KEY_ALTENTER;
1232 if ((GetKeyState(VK_CONTROL) & 0x8000))
1233 return KEY_CTRLENTER;
1234 return ext ? /*KP*/ MATRIX(0,1) : MATRIX(0,1);
1235 case VK_SPACE: return MATRIX(7,4);
1236 case VK_ESCAPE: return MATRIX(7,7);
1237 case VK_INSERT: if (!ext) numlock = 0; return ext ? MATRIX(0,0) | 0x80 : /*KP*/ KEY_FIRE;
1238 case VK_HOME: if (!ext) numlock = 0; return ext ? MATRIX(6,3) : /*KP*/ KEY_JUPLF;
1239 case VK_END: if (!ext) numlock = 0; return ext ? MATRIX(6,0) : /*KP*/ KEY_JDNLF;
1240 case VK_PRIOR: if (!ext) numlock = 0; return ext ? MATRIX(6,6) : /*KP*/ KEY_JUPRT;
1241 case VK_NEXT: if (!ext) numlock = 0; return ext ? MATRIX(6,5) : /*KP*/ KEY_JDNRT;
1242 case VK_CLEAR: return KEY_CENTER;
1243
1244 case VK_SHIFT: return sc == 0x36 ? /*R*/ MATRIX(6,4) : MATRIX(1,7);
1245 case VK_CONTROL: return ext ? /*R*/ MATRIX(7,5) : MATRIX(7,2);
1246 case VK_MENU: return ext ? /*R*/ MATRIX(7,5) : MATRIX(7,5);
1247
1248 case VK_UP: if (!ext) numlock = 0; return ext ? MATRIX(0,7) | 0x80 : /*KP*/ KEY_JUP;
1249 case VK_DOWN: if (!ext) numlock = 0; return ext ? MATRIX(0,7) : /*KP*/ KEY_JDN;
1250 case VK_LEFT: if (!ext) numlock = 0; return ext ? MATRIX(0,2) | 0x80 : /*KP*/ KEY_JLF;
1251 case VK_RIGHT: if (!ext) numlock = 0; return ext ? MATRIX(0,2) : /*KP*/ KEY_JRT;
1252
1253 case VK_F1: return MATRIX(0,4);
1254 case VK_F2: return MATRIX(0,4) | 0x80;
1255 case VK_F3: return MATRIX(0,5);
1256 case VK_F4: return MATRIX(0,5) | 0x80;
1257 case VK_F5: return MATRIX(0,6);
1258 case VK_F6: return MATRIX(0,6) | 0x80;
1259 case VK_F7: return MATRIX(0,3);
1260 case VK_F8: return MATRIX(0,3) | 0x80;
1261
1262 case '0': return MATRIX(4,3);
1263 case '1': return MATRIX(7,0);
1264 case '2': return MATRIX(7,3);
1265 case '3': return MATRIX(1,0);
1266 case '4': return MATRIX(1,3);
1267 case '5': return MATRIX(2,0);
1268 case '6': return MATRIX(2,3);
1269 case '7': return MATRIX(3,0);
1270 case '8': return MATRIX(3,3);
1271 case '9': return MATRIX(4,0);
1272
1273 case VK_bracketleft: return MATRIX(5,6);
1274 case VK_bracketright: return MATRIX(6,1);
1275 case VK_slash: return MATRIX(6,7);
1276 case VK_semicolon: return MATRIX(5,5);
1277 case VK_grave: return MATRIX(7,1);
1278 case VK_minus: return MATRIX(5,0);
1279 case VK_equal: return MATRIX(5,3);
1280 case VK_comma: return MATRIX(5,7);
1281 case VK_period: return MATRIX(5,4);
1282 case VK_quote: return MATRIX(6,2);
1283 case VK_backslash: return MATRIX(6,6);
1284
1285 case 'A': result = MATRIX(1,2); break;
1286 case 'B': result = MATRIX(3,4); break;
1287 case 'C': result = MATRIX(2,4); break;
1288 case 'D': result = MATRIX(2,2); break;
1289 case 'E': result = MATRIX(1,6); break;
1290 case 'F': result = MATRIX(2,5); break;
1291 case 'G': result = MATRIX(3,2); break;
1292 case 'H': result = MATRIX(3,5); break;
1293 case 'I': result = MATRIX(4,1); break;
1294 case 'J': result = MATRIX(4,2); break;
1295 case 'K': result = MATRIX(4,5); break;
1296 case 'L': result = MATRIX(5,2); break;
1297 case 'M': result = MATRIX(4,4); break;
1298 case 'N': result = MATRIX(4,7); break;
1299 case 'O': result = MATRIX(4,6); break;
1300 case 'P': result = MATRIX(5,1); break;
1301 case 'Q': result = MATRIX(7,6); break;
1302 case 'R': result = MATRIX(2,1); break;
1303 case 'S': result = MATRIX(1,5); break;
1304 case 'T': result = MATRIX(2,6); break;
1305 case 'U': result = MATRIX(3,6); break;
1306 case 'V': result = MATRIX(3,7); break;
1307 case 'W': result = MATRIX(1,1); break;
1308 case 'X': result = MATRIX(2,7); break;
1309 case 'Y': result = MATRIX(3,1); break;
1310 case 'Z': result = MATRIX(1,4); break;
1311
1312 }
1313
1314 if (result != -1 && GetKeyState(VK_CAPITAL))
1315 result |= 0x80;
1316
1317 return result;
1318 }
1319
1320 BOOL C64Display::SetupWindow()
1321 {
1322 // Setup the window.
1323 SetupWindowMode(full_screen);
1324
1325 UpdateWindow(hwnd);
1326
1327 if (full_screen)
1328 ShowCursor(FALSE);
1329
1330 return TRUE;
1331 }
1332
1333 BOOL C64Display::SetupWindowMode(BOOL full_screen_mode)
1334 {
1335 DWORD style;
1336 int x0, y0, x, y;
1337 if (full_screen_mode) {
1338 style = fullscreen_style;
1339 x0 = 0;
1340 y0 = 0;
1341 x = GetSystemMetrics(SM_CXSCREEN);
1342 y = GetSystemMetrics(SM_CYSCREEN);
1343 }
1344 else {
1345 style = windowed_style;
1346 x0 = rcLast.left;
1347 y0 = rcLast.top;
1348 x = rcLast.right - rcLast.left;
1349 y = rcLast.bottom - rcLast.top;
1350 }
1351 SetWindowLong(hwnd, GWL_STYLE, style);
1352 SetWindowPos(hwnd, NULL, x0, y0, x, y, SWP_NOZORDER | SWP_NOACTIVATE);
1353 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
1354 GetClientRect(hwnd, &rcWindow);
1355 if (!full_screen_mode && ThePrefs.ShowLEDs)
1356 rcWindow.bottom -= led_rows;
1357 ClientToScreen(hwnd, (LPPOINT) &rcWindow);
1358 ClientToScreen(hwnd, (LPPOINT) &rcWindow + 1);
1359
1360 // Windowed mode has a menu, full screen mode doesn't.
1361 HMENU old_menu = GetMenu(hwnd);
1362 if (old_menu) {
1363 SetMenu(hwnd, NULL);
1364 DestroyMenu(old_menu);
1365 }
1366 if (!full_screen_mode) {
1367 HMENU new_menu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MAIN_MENU));
1368 SetMenu(hwnd, new_menu);
1369 }
1370
1371 return TRUE;
1372 }
1373
1374 BOOL C64Display::RestoreWindow()
1375 {
1376 if (full_screen)
1377 ShowCursor(TRUE);
1378
1379 if (!full_screen)
1380 GetWindowRect(hwnd, &rcLast);
1381
1382 SetupWindowMode(FALSE);
1383
1384 return TRUE;
1385 }
1386
1387 HRESULT CALLBACK C64Display::EnumModesCallback(LPDDSURFACEDESC pDDSD, LPVOID lpContext)
1388 {
1389 C64Display *pDisplay = (C64Display *) lpContext;
1390 return pDisplay->EnumModesCallback(pDDSD);
1391 }
1392
1393 HRESULT C64Display::EnumModesCallback(LPDDSURFACEDESC pDDSD)
1394 {
1395 DisplayMode mode;
1396 mode.x = pDDSD->dwWidth;
1397 mode.y = pDDSD->dwHeight;
1398 mode.depth = pDDSD->ddpfPixelFormat.dwRGBBitCount;
1399 mode.modex = (pDDSD->ddsCaps.dwCaps & DDSCAPS_MODEX) != 0;
1400 Debug("EnumModesCallback: %dx%dx%d (modex: %d)\n",
1401 mode.x, mode.y, mode.depth, mode.modex);
1402 if (display_modes == NULL)
1403 display_modes = new DisplayMode[max_display_modes];
1404 if (num_display_modes == max_display_modes) {
1405 int old_max = max_display_modes;
1406 max_display_modes *= 2;
1407 DisplayMode *new_modes = new DisplayMode[max_display_modes];
1408 memcpy(new_modes, display_modes, sizeof(DisplayMode)*old_max);
1409 delete[] display_modes;
1410 display_modes = new_modes;
1411 }
1412 display_modes[num_display_modes++] = mode;
1413 return DDENUMRET_OK;
1414 }
1415
1416 int C64Display::CompareModes(const void *e1, const void *e2)
1417 {
1418 DisplayMode *m1 = (DisplayMode *) e1;
1419 DisplayMode *m2 = (DisplayMode *) e2;
1420 if (m1->depth != m2->depth)
1421 return m1->depth - m2->depth;
1422 if (m1->x != m2->x)
1423 return m1->x - m2->x;
1424 if (m1->y != m2->y)
1425 return m1->y - m2->y;
1426 if (m1->modex != m2->modex)
1427 return int(m1->modex) - int(m2->modex);
1428 return 0;
1429 }
1430
1431 BOOL C64Display::StartDirectDraw()
1432 {
1433 // Setup our window size, position, style, etc.
1434 SetupWindow();
1435
1436 // Create the main DirectDraw object.
1437 HRESULT ddrval = DirectDrawCreate(NULL, &pDD, NULL);
1438 if (ddrval != DD_OK) {
1439 DebugResult("DirectDrawCreate failed", ddrval);
1440 return Fail("Failed to initialize direct draw.");
1441 }
1442
1443 if (full_screen) {
1444
1445 // Set exclusive mode.
1446 ddrval = pDD->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX);
1447 if (ddrval != DD_OK) {
1448 DebugResult("SetCooperativeLevel failed", ddrval);
1449 return Fail("Failed to set exclusive cooperative level.");
1450 }
1451
1452 if (!display_modes) {
1453
1454 // Get all available video modes and sort them.
1455 num_display_modes = 0;
1456 pDD->EnumDisplayModes(0, NULL, this, EnumModesCallback);
1457 qsort(display_modes, num_display_modes, sizeof(DisplayMode), CompareModes);
1458 }
1459
1460 // Set the video mode.
1461 const char *display_mode = ThePrefs.DisplayMode;
1462 if (display_mode[0] == '\0' ||
1463 stricmp(display_mode, "Default") == 0)
1464 display_mode = NULL;
1465 if (display_mode) {
1466 int x, y, depth = 8;
1467 if (sscanf(display_mode, "%dx%dx%d", &x, &y, &depth) < 2)
1468 return Fail("Invalid command line mode format.");
1469 ddrval = pDD->SetDisplayMode(x, y, depth);
1470 if (ddrval != DD_OK) {
1471 DebugResult("SetDisplayMode failed", ddrval);
1472 return Fail("Failed to set the video mode.");
1473 }
1474 }
1475 else {
1476 for (int i = 0; i < num_display_modes; i++) {
1477 DisplayMode *mode = &display_modes[i];
1478 if (mode->x < view_x || mode->y < view_y)
1479 continue;
1480 ddrval = pDD->SetDisplayMode(mode->x, mode->y, mode->depth);
1481 if (ddrval == DD_OK)
1482 break;
1483 }
1484 if (i == num_display_modes)
1485 return Fail("Failed to find a suitable video mode.");
1486 }
1487 }
1488 else {
1489
1490 // Set normal mode.
1491 ddrval = pDD->SetCooperativeLevel(hwnd, DDSCL_NORMAL);
1492 if (ddrval != DD_OK)
1493 return Fail("Failed to set normal cooperative level.");
1494 }
1495
1496 // Create the primary surface with one back buffer.
1497 DDSURFACEDESC ddsd;
1498 memset(&ddsd, 0, sizeof(ddsd));
1499 ddsd.dwSize = sizeof(ddsd);
1500 ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
1501 ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
1502 ddsd.dwBackBufferCount = 1;
1503 ddrval = pDD->CreateSurface(&ddsd, &pPrimary, NULL);
1504 if (ddrval != DD_OK) {
1505 memset(&ddsd, 0, sizeof(ddsd));
1506 ddsd.dwSize = sizeof(ddsd);
1507 ddsd.dwFlags = DDSD_CAPS;
1508 ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
1509 ddrval = pDD->CreateSurface(&ddsd, &pPrimary, NULL);
1510 if (ddrval != DD_OK)
1511 return Fail("Failed to create primary surface.");
1512 }
1513
1514 if (ddsd.dwBackBufferCount == 1) {
1515 DDSCAPS ddscaps;
1516 ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
1517 ddrval = pPrimary->GetAttachedSurface(&ddscaps, &pBack);
1518 if (ddrval != DD_OK)
1519 return Fail("Failed to get attached surface.");
1520 }
1521
1522 // Create work surface. It displays correctly without
1523 // this but doesn't handle clipping. We would have to
1524 // do that ourselves.
1525 memset(&ddsd, 0, sizeof(ddsd));
1526 ddsd.dwSize = sizeof(ddsd);
1527 ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
1528 ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
1529 if (ThePrefs.SystemMemory)
1530 ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
1531 ddsd.dwHeight = DISPLAY_Y;
1532 ddsd.dwWidth = DISPLAY_X;
1533 ddrval = pDD->CreateSurface(&ddsd, &pWork, NULL);
1534 if (ddrval != DD_OK) {
1535 //return Fail("Failed to create work surface.");
1536 Debug("cannot create work surface: %d\n", ddrval);
1537 }
1538 if (pWork) {
1539 pWork->GetCaps(&ddsd.ddsCaps);
1540 if (ddsd.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY)
1541 Debug("Work surface is in video memory.\n");
1542 else if (ddsd.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY)
1543 Debug("Work surface is in system memory.\n");
1544 else
1545 Debug("Work surface is in unknown memory.\n");
1546 }
1547
1548 if (!full_screen) {
1549
1550 // Create clipper object.
1551 ddrval = pDD->CreateClipper(0, &pClipper, NULL);
1552 if (ddrval != DD_OK)
1553 return Fail("Failed to create direct draw clipper.");
1554 ddrval = pClipper->SetHWnd(0, hwnd);
1555 if (ddrval != DD_OK)
1556 return Fail("Failed setting clipper window handle.");
1557 ddrval = pPrimary->SetClipper(pClipper);
1558 if (ddrval != DD_OK)
1559 return Fail("Failed setting primary surface clipper.");
1560 }
1561
1562 // We need to use a 256 color palette otherwise we get an
1563 // invalid pixel format error when trying to set the palette
1564 // on a windowed surface.
1565 PALETTEENTRY ape[256];
1566 HDC hdc = GetDC(NULL);
1567 int entries = GetSystemPaletteEntries(hdc, 0, 256, ape);
1568 ReleaseDC(NULL, hdc);
1569 if (entries != 256) {
1570 Debug("failed to get 256 system palette entries: %d (%d)\n",
1571 entries, GetLastError());
1572
1573 // Build a 332 palette as the default. This makes it easy for
1574 // other apps to find colors when they aren't the foreground.
1575 for (int i = 0; i < 256; i++) {
1576 ape[i].peRed = (BYTE)(((i >> 5) & 0x07) * 255 / 7);
1577 ape[i].peGreen = (BYTE)(((i >> 2) & 0x07) * 255 / 7);
1578 ape[i].peBlue = (BYTE)(((i >> 0) & 0x03) * 255 / 3);
1579 ape[i].peFlags = 0;
1580 }
1581 }
1582
1583 // Now override the first 16 entries with the C64 colors.
1584 // If we were really obsessive we could try to find the
1585 // nearest matches and replace them instead.
1586 for (int i = 0; i < 16; i++) {
1587 ape[i].peRed = palette_red[i];
1588 ape[i].peGreen = palette_green[i];
1589 ape[i].peBlue = palette_blue[i];
1590 ape[i].peFlags = 0;
1591 }
1592
1593 // Create the palette and set it on all surfaces.
1594 ddrval = pDD->CreatePalette(DDPCAPS_8BIT, ape, &pPalette, NULL);
1595 if (ddrval != DD_OK)
1596 return Fail("Failed to create palette.");
1597 if (!SetPalettes())
1598 return Fail("Failed to set palettes.");
1599 if (!BuildColorTable())
1600 return Fail("Failed to build color table.");
1601
1602 // Start with a clean slate.
1603 if (!EraseSurfaces()) {
1604 // Some display drivers have bugs, I guess.
1605 // What's a little problem erasing gonna hurt.
1606 #if 0
1607 return Fail("Failed to erase surfaces.");
1608 #endif
1609 }
1610
1611
1612 return TRUE;
1613 }
1614
1615 BOOL C64Display::ResumeDirectDraw()
1616 {
1617 if (!RestoreSurfaces())
1618 ResetDirectDraw();
1619
1620 return TRUE;
1621 }
1622
1623 BOOL C64Display::ResetDirectDraw()
1624 {
1625 Pause();
1626 StopDirectDraw();
1627 StartDirectDraw();
1628 Resume();
1629 if (paused)
1630 Update();
1631
1632 return TRUE;
1633 }
1634
1635 BOOL C64Display::StopDirectDraw()
1636 {
1637 if (pDD != NULL) {
1638 if (pClipper != NULL) {
1639 pClipper->Release();
1640 pClipper = NULL;
1641 }
1642 if (pWork != NULL) {
1643 pWork->Release();
1644 pWork = NULL;
1645 }
1646 if (pBack != NULL) {
1647 pBack->Release();
1648 pBack = NULL;
1649 }
1650 if (pPrimary != NULL) {
1651 pPrimary->Release();
1652 pPrimary = NULL;
1653 }
1654 if (pPalette != NULL) {
1655 pPalette->Release();
1656 pPalette = NULL;
1657 }
1658 pDD->RestoreDisplayMode();
1659 pDD->Release();
1660 pDD = NULL;
1661 }
1662
1663 // Restore windowing state, window position, etc.
1664 RestoreWindow();
1665
1666 return TRUE;
1667 }
1668
1669
1670 /*
1671 * This function is called if the initialization function fails
1672 */
1673 BOOL C64Display::Fail(const char *error)
1674 {
1675 Debug(error);
1676 Debug("\n");
1677 strcpy(failure_message, error);
1678 return FALSE;
1679 }
1680
1681
1682 BOOL C64Display::SetPalettes()
1683 {
1684 // Only try to set palettes when in 256 color mode.
1685 HDC hdc = GetDC(NULL);
1686 int depth = GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc, BITSPIXEL);
1687 ReleaseDC(NULL, hdc);
1688 if (depth != 8)
1689 return TRUE;
1690
1691 // Set palette on primary surface.
1692 HRESULT ddrval = pPrimary->SetPalette(pPalette);
1693 if (ddrval == DDERR_SURFACELOST) {
1694 pPrimary->Restore();
1695 ddrval = pPrimary->SetPalette(pPalette);
1696 }
1697 if (ddrval == DDERR_NOT8BITCOLOR)
1698 return TRUE;
1699 if (ddrval != DD_OK) {
1700 DebugResult("failed to set palette on primary", ddrval);
1701 return FALSE;
1702 }
1703
1704 // Set palette on back surface.
1705 if (pBack) {
1706 FlipSurfaces();
1707 pPrimary->SetPalette(pPalette);
1708 if (ddrval == DDERR_SURFACELOST) {
1709 pPrimary->Restore();
1710 ddrval = pPrimary->SetPalette(pPalette);
1711 }
1712 if (ddrval != DD_OK) {
1713 DebugResult("failed to set palette on back", ddrval);
1714 return FALSE;
1715 }
1716 }
1717
1718 // Set palette on work surface.
1719 if (pWork) {
1720 ddrval = pWork->SetPalette(pPalette);
1721 if (ddrval == DDERR_SURFACELOST) {
1722 pWork->Restore();
1723 ddrval = pWork->SetPalette(pPalette);
1724 }
1725 if (ddrval != DD_OK) {
1726 DebugResult("failed to set palette on work", ddrval);
1727 return FALSE;
1728 }
1729 }
1730
1731 return TRUE;
1732 }
1733
1734 BOOL C64Display::BuildColorTable()
1735 {
1736 if (!pPrimary)
1737 return FALSE;
1738
1739 // Determine the physical colors corresponding to the 16 C64 colors.
1740 for (int j = 0; j < 16; j++) {
1741
1742 // Compute the true color in RGB format.
1743 int red = palette_red[j];
1744 int green = palette_green[j];
1745 int blue = palette_blue[j];
1746 COLORREF rgb = RGB(red, green, blue);
1747
1748 // Set pixel(0, 0) to that value.
1749 LPDIRECTDRAWSURFACE pSurface = pBack ? pBack : pPrimary;
1750 HDC hdc;
1751 if (pSurface->GetDC(&hdc) != DD_OK)
1752 return Fail("Failed getting direct draw device context.");
1753 COLORREF new_rgb = SetPixel(hdc, 0, 0, PALETTERGB(red, green, blue));
1754 Debug("new: %.8x, old %.8x\n", new_rgb, rgb);
1755 pSurface->ReleaseDC(hdc);
1756
1757 // Read the physical color from linear memory.
1758 DDSURFACEDESC ddsd;
1759 ddsd.dwSize = sizeof(ddsd);
1760 HRESULT ddrval;
1761 for (;;) {
1762 ddrval = pSurface->Lock(NULL, &ddsd, 0, NULL);
1763 if (ddrval != DDERR_WASSTILLDRAWING)
1764 break;
1765 }
1766 if (ddrval != DD_OK)
1767 return Fail("Failed to lock surface.");
1768 colors_depth = ddsd.ddpfPixelFormat.dwRGBBitCount;
1769 DWORD dw = *(DWORD *) ddsd.lpSurface;
1770 Debug("DWORD = %.8x, depth = %d\n", dw, colors_depth);
1771 if (colors_depth != 32)
1772 dw &= (1 << colors_depth) - 1;
1773 pSurface->Unlock(NULL);
1774
1775 // Store the physical color in the colors array.
1776 colors[j] = dw;
1777 Debug("colors[%d] = %d\n", j, dw);
1778 }
1779
1780 // Replicate the physical colors into the rest of the color array.
1781 for (int k = 16; k < 256; k++)
1782 colors[k] = colors[k & 0x0f];
1783
1784 // Tell the VIC all about it;
1785 if (!in_constructor)
1786 TheC64->TheVIC->ReInitColors();
1787
1788 return TRUE;
1789 }
1790
1791 /*
1792 * Redraw bitmap using double buffering when possible.
1793 */
1794
1795 void C64Display::Update()
1796 {
1797 TIMESCOPE(ts0, "Update");
1798
1799 //Debug("Display::Update\n");
1800
1801 if (full_screen && !active)
1802 return;
1803
1804 if (!pPrimary)
1805 return;
1806
1807 #ifdef WORKBUFFER_BITMAP
1808 // Special case for using the workbuffer as a bitmap.
1809 if (workbuffer_bitmap) {
1810 if (workbuffer_locked) {
1811 pWork->Unlock(NULL);
1812 workbuffer_locked = FALSE;
1813 }
1814 RECT rc;
1815 rc.left = (DISPLAY_X - view_x)/2;
1816 rc.top = (DISPLAY_Y - view_y)/2 - 1;
1817 if (rc.top < 0)
1818 rc.top = 0;
1819 rc.right = rc.left + view_x;
1820 rc.bottom = rc.top + view_y;
1821 CopySurface(rc);
1822 draw_leds();
1823 return;
1824
1825 }
1826 #endif
1827
1828 // Work on the backing surface unless there isn't one.
1829 // We'll flip to it when we're done.
1830 LPDIRECTDRAWSURFACE pSurface = pBack ? pBack : pPrimary;
1831
1832 // Use a work surface when we have to:
1833 // * when always copy is on
1834 // * when possibly clipped
1835 // * when streching
1836 // * when partially offscreen
1837
1838 if (!full_screen && pWork) {
1839 if (ThePrefs.AlwaysCopy || !active || paused ||
1840 #if 0
1841 GetForegroundWindow() != hwnd ||
1842 #endif
1843 rcWindow.right - rcWindow.left != view_x ||
1844 rcWindow.bottom - rcWindow.top != view_y ||
1845 rcWindow.left < rcScreen.left ||
1846 rcWindow.top < rcScreen.top ||
1847 rcWindow.right > rcScreen.right ||
1848 rcWindow.bottom > rcScreen.bottom) {
1849 pSurface = pWork;
1850 //Debug("using work surface\n");
1851 }
1852 }
1853
1854 // Lock the surface.
1855 DDSURFACEDESC ddsd;
1856 ddsd.dwSize = sizeof(ddsd);
1857
1858 for (;;) {
1859 HRESULT ddrval = pSurface->Lock(NULL, &ddsd, 0, NULL);
1860 if (ddrval == DD_OK)
1861 break;
1862 if (ddrval == DDERR_SURFACELOST) {
1863 Debug("surface lost\n");
1864 if (pSurface == pWork)
1865 ddrval = pWork->Restore();
1866 else
1867 ddrval = pPrimary->Restore();
1868 if (ddrval != DD_OK) {
1869 DebugResult("surface Restore failed", ddrval);
1870 return;
1871 }
1872 EraseSurfaces();
1873 BuildColorTable();
1874 }
1875 else if (ddrval != DDERR_WASSTILLDRAWING) {
1876 if (pWork && pSurface != pWork)
1877 pSurface = pWork;
1878 else {
1879 DebugResult("surface Lock failed", ddrval);
1880 return;
1881 }
1882 }
1883 Debug("was still drawing\n");
1884 }
1885
1886 // Compute the optimal placement of our window depending on
1887 // the screen dimensions.
1888 int x_off, y_off;
1889 int x_beg, y_beg;
1890 int x_siz, y_siz;
1891
1892 // XXX: Do these calculations only when the parameters change.
1893 if (full_screen) {
1894 if (rcWindow.right >= view_x) {
1895 x_off = (rcWindow.right - view_x)/2;
1896 x_beg = (DISPLAY_X - view_x)/2;
1897 x_siz = view_x;
1898 }
1899 else {
1900 x_off = 0;
1901 x_beg = (DISPLAY_X - rcWindow.right)/2;
1902 x_siz = rcWindow.right;
1903 }
1904 if (rcWindow.bottom >= view_y) {
1905 y_off = (rcWindow.bottom - view_y)/2;
1906 y_beg = (DISPLAY_Y - view_y)/2 - 1;
1907 y_siz = view_y;
1908 }
1909 else {
1910 y_off = 0;
1911 y_beg = (DISPLAY_Y - rcWindow.bottom)/2 - 1;
1912 y_siz = rcWindow.bottom;
1913 }
1914 }
1915 else {
1916 if (pSurface == pWork) {
1917 x_off = 0;
1918 y_off = 0;
1919 }
1920 else {
1921 x_off = rcWindow.left;
1922 y_off = rcWindow.top;
1923 }
1924 x_beg = (DISPLAY_X - view_x)/2;
1925 y_beg = (DISPLAY_Y - view_y)/2 - 1;
1926 x_siz = view_x;
1927 y_siz = view_y;
1928 }
1929 if (y_beg < 0)
1930 y_beg = 0;
1931
1932 // Translate chunky colors into the surface's linear memory.
1933 int pitch = ddsd.lPitch;
1934 int depth = ddsd.ddpfPixelFormat.dwRGBBitCount;
1935 BYTE *surface = (BYTE *) ddsd.lpSurface + pitch*y_off + x_off*(depth/8);
1936 BYTE *chunky = chunky_buf + DISPLAY_X*y_beg + x_beg;
1937
1938 // These tight loops are where the display speed action is at.
1939 // Note that MSVC optimizes out the mulitiplications and
1940 // reverses the direction of the loop counters automatically.
1941 if (depth == 8) {
1942
1943 // Since the VIC is using our palette entries we just copy.
1944 //TIMESCOPE(ts1, "hand blt 8");
1945 BYTE *scanline = surface;
1946 BYTE *scanbuf = chunky;
1947 //Debug("scanline = %8p, scanbuf = %8p\n", scanline, scanbuf);
1948 for (int j = 0; j < y_siz; j++) {
1949 memcpy(scanline, scanbuf, x_siz);
1950 scanline += pitch;
1951 scanbuf += DISPLAY_X;
1952 }
1953 }
1954 else if (depth == 16) {
1955 //TIMESCOPE(ts1, "hand blt 16");
1956 for (int j = 0; j < y_siz; j++) {
1957 WORD *scanline = (WORD *) (surface + pitch*j);
1958 BYTE *scanbuf = chunky + +DISPLAY_X*j;
1959 for (int i = 0; i < x_siz; i++)
1960 *scanline++ = (WORD) colors[*scanbuf++];
1961 }
1962 }
1963 else if (depth == 24) {
1964
1965 // XXX: Works for little-endian only.
1966 //TIMESCOPE(ts1, "hand blt 24");
1967 for (int j = 0; j < y_siz; j++) {
1968 BYTE *scanline = surface + pitch*j;
1969 BYTE *scanbuf = chunky + +DISPLAY_X*j;
1970 for (int i = 0; i < x_siz; i++) {
1971 *((DWORD *) scanline) = colors[*scanbuf++];
1972 scanline += 3;
1973 }
1974 }
1975 }
1976 else if (depth == 32) {
1977 //TIMESCOPE(ts1, "hand blt 32");
1978 for (int j = 0; j < y_siz; j++) {
1979 DWORD *scanline = (DWORD *) (surface + pitch*j);
1980 BYTE *scanbuf = chunky + +DISPLAY_X*j;
1981 for (int i = 0; i < x_siz; i++)
1982 *scanline++ = colors[*scanbuf++];
1983 }
1984 }
1985 else
1986 Debug("PixelCount not 8, 16, 24, or 32\n");
1987
1988 // Unlock the surface.
1989 HRESULT ddrval = pSurface->Unlock(NULL);
1990 if (ddrval != DD_OK)
1991 Debug("DirectDrawSurface::Unlock failed\n");
1992
1993 // Now flip from the primary surface to the backing surface.
1994 if (pSurface == pWork)
1995 CopySurface(rcWork);
1996 else if (full_screen && pBack)
1997 FlipSurfaces();
1998
1999 // Update drive LEDs
2000 draw_leds();
2001 }
2002
2003
2004 BOOL C64Display::CopySurface(RECT &rcWork)
2005 {
2006 // Copy work surface to primary.
2007 for (;;) {
2008 HRESULT ddrval = pPrimary->Blt(&rcWindow, pWork, &rcWork, DDBLT_WAIT, NULL);
2009 if (ddrval == DD_OK)
2010 break;
2011 if (ddrval == DDERR_SURFACELOST) {
2012 ddrval = pPrimary->Restore();
2013 if (ddrval != DD_OK) {
2014 DebugResult("CopySurface Restore failed", ddrval);
2015 return FALSE;
2016 }
2017 }
2018 else if (ddrval != DDERR_WASSTILLDRAWING) {
2019 DebugResult("CopySurface Blt failed", ddrval);
2020 return FALSE;
2021 }
2022 }
2023 return TRUE;
2024 }
2025
2026 BOOL C64Display::FlipSurfaces()
2027 {
2028 // Flip buffers.
2029 for (;;) {
2030 HRESULT ddrval = pPrimary->Flip(NULL, 0);
2031 if (ddrval == DD_OK)
2032 break;
2033 if (ddrval == DDERR_SURFACELOST) {
2034 ddrval = pPrimary->Restore();
2035 if (ddrval != DD_OK) {
2036 Debug("Restore failed\n");
2037 return FALSE;
2038 }
2039 }
2040 else if (ddrval != DDERR_WASSTILLDRAWING)
2041 return FALSE;
2042 }
2043 return TRUE;
2044 }
2045
2046 BOOL C64Display::EraseSurfaces()
2047 {
2048 DDBLTFX ddbltfx;
2049 ddbltfx.dwSize = sizeof(ddbltfx);
2050 ddbltfx.dwFillColor = 0;
2051
2052 // Erase the backing surface.
2053 for (;;) {
2054 if (!pBack)
2055 break;
2056 HRESULT ddrval = pBack->Blt(&rcWindow, NULL, NULL, DDBLT_COLORFILL, &ddbltfx);
2057
2058 if (ddrval == DD_OK)
2059 break;
2060
2061 if (ddrval == DDERR_SURFACELOST) {
2062 ddrval = pPrimary->Restore();
2063 if (ddrval != DD_OK) {
2064 DebugResult("Restore primary failed", ddrval);
2065 return FALSE;
2066 }
2067 }
2068 else if (ddrval != DDERR_WASSTILLDRAWING) {
2069 DebugResult("Blt erase back failed", ddrval);
2070 return FALSE;
2071 }
2072 }
2073
2074 // Erase the primary surface.
2075 for (;;) {
2076 HRESULT ddrval = pPrimary->Blt(&rcWindow, NULL, NULL, DDBLT_COLORFILL, &ddbltfx);
2077
2078 if (ddrval == DD_OK)
2079 break;
2080
2081 if (ddrval == DDERR_SURFACELOST) {
2082 ddrval = pPrimary->Restore();
2083 if (ddrval != DD_OK) {
2084 DebugResult("Restore primary failed", ddrval);
2085 return FALSE;
2086 }
2087 }
2088 else if (ddrval != DDERR_WASSTILLDRAWING) {
2089 DebugResult("Blt erase primary failed", ddrval);
2090 return FALSE;
2091 }
2092 }
2093
2094 return TRUE;
2095 }
2096
2097 BOOL C64Display::RestoreSurfaces()
2098 {
2099 if (pPrimary) {
2100 HRESULT ddrval = pPrimary->Restore();
2101 if (ddrval != DD_OK)
2102 return FALSE;
2103 }
2104
2105 if (pWork) {
2106 HRESULT ddrval = pWork->Restore();
2107 if (ddrval != DD_OK)
2108 return FALSE;
2109 }
2110
2111 return TRUE;
2112 }
2113
2114 /*
2115 * Draw LED bar at the bottom of the window
2116 */
2117
2118 void C64Display::draw_led_bar()
2119 {
2120 if (full_screen || !ThePrefs.ShowLEDs)
2121 return;
2122
2123 HDC hdc = GetDC(hwnd);
2124 RECT rc;
2125 GetClientRect(hwnd, &rc);
2126 rc.top = rc.bottom - led_rows;
2127 FillRect(hdc, &rc, led_brush);
2128 if (rc.right - rc.left > view_x)
2129 rc.left = rc.right - view_x;
2130 SelectObject(hdc, led_font);
2131 SetTextAlign(hdc, TA_TOP | TA_RIGHT);
2132 SetBkMode(hdc, TRANSPARENT);
2133 SetTextColor(hdc, (COLORREF) GetSysColor(COLOR_MENUTEXT));
2134 for (int i = 0; i < 4; i++) {
2135 char str[128];
2136 if (rc.right - rc.left < view_x)
2137 sprintf(str, "%d", i + 8);
2138 else
2139 sprintf(str, "Drive %d", i + 8);
2140 RECT led;
2141 led_rect(i, rc, led);
2142 SelectObject(hdc, led_shadow);
2143 MoveToEx(hdc, led.left - 1, led.bottom - 1, NULL);
2144 LineTo(hdc, led.left - 1, led.top - 1);
2145 LineTo(hdc, led.right, led.top - 1);
2146 SelectObject(hdc, led_highlight);
2147 LineTo(hdc, led.right, led.bottom);
2148 LineTo(hdc, led.left - 2, led.bottom);
2149 TextOut(hdc, led.left - 4, rc.top + 2, str, strlen(str));
2150 }
2151 ReleaseDC(hwnd, hdc);
2152 draw_leds(TRUE);
2153 }
2154
2155 /*
2156 * Draw one LED
2157 */
2158
2159 void C64Display::draw_leds(BOOL force)
2160 {
2161 if (full_screen || !ThePrefs.ShowLEDs)
2162 return;
2163
2164 if (!force) {
2165 int i;
2166 for (i = 0; i < 4; i++) {
2167 if (led_state[i] != old_led_state[i])
2168 break;
2169 }
2170 if (i == 4)
2171 return;
2172 }
2173
2174 HDC hdc = GetDC(hwnd);
2175 RECT rc;
2176 GetClientRect(hwnd, &rc);
2177 rc.top = rc.bottom - led_rows;
2178 if (rc.right - rc.left > view_x)
2179 rc.left = rc.right - view_x;
2180 for (int i = 0; i < 4; i++) {
2181 old_led_state[i] = led_state[i];
2182 HBRUSH brush;
2183 switch (led_state[i]) {
2184 case LED_OFF: brush = off_brush; break;
2185 case LED_ERROR_OFF: brush = error_off_brush; break;
2186 case LED_ON: brush = on_brush; break;
2187 case LED_ERROR_ON: brush = error_on_brush; break;
2188 }
2189 RECT led;
2190 led_rect(i, rc, led);
2191 FillRect(hdc, &led, brush);
2192 }
2193 ReleaseDC(hwnd, hdc);
2194 }
2195
2196 void C64Display::led_rect(int n, RECT &rc, RECT &led)
2197 {
2198 int x = rc.left + (rc.right - rc.left)*(n + 2)/5 - 20;
2199 int y = rc.top + 2 + led_rows/3;
2200 SetRect(&led, x, y, x + 13, y + led_rows/3);
2201 }
2202
2203 void C64Display::InsertNextDisk()
2204 {
2205 if (strlen(ThePrefs.DrivePath[0]) > 4) {
2206 char str[256];
2207 strcpy(str, ThePrefs.DrivePath[0]);
2208 char *p = str + strlen(str) - 5;
2209
2210 // If path matches "*.?64", increment character before the '.'
2211 if (p[1] == '.' && p[3] == '6' && p[4] == '4') {
2212 p[0]++;
2213
2214 // If no such file exists, set character before the '.' to '1', 'a' or 'A'
2215 FILE *file;
2216 if ((file = fopen(str, "rb")) == NULL) {
2217 if (isdigit(p[0]))
2218 p[0] = '1';
2219 else if (isupper(p[0]))
2220 p[0] = 'A';
2221 else
2222 p[0] = 'a';
2223 } else
2224 fclose(file);
2225
2226 // Set new prefs
2227 Pause();
2228 Prefs *prefs = new Prefs(ThePrefs);
2229 strcpy(prefs->DrivePath[0], str);
2230 TheC64->NewPrefs(prefs);
2231 ThePrefs = *prefs;
2232 delete prefs;
2233 Resume();
2234 }
2235 }
2236 }
2237
2238 BOOL C64Display::FileNameDialog(char *prefs_path, BOOL save)
2239 {
2240 char filename[256];
2241 strcpy(filename, prefs_path);
2242 OPENFILENAME ofn;
2243 memset(&ofn, 0, sizeof(ofn));
2244 ofn.lStructSize = sizeof(ofn);
2245 ofn.hwndOwner = hwnd;
2246 ofn.hInstance = hInstance;
2247 ofn.lpstrFilter =
2248 "Preferences Files (*.fpr)\0*.fpr\0"
2249 "All Files (*.*)\0*.*\0"
2250 ;
2251 ofn.lpstrCustomFilter = NULL;
2252 ofn.nMaxCustFilter = 0;
2253 ofn.nFilterIndex = 1;
2254 ofn.lpstrFile = filename;
2255 ofn.nMaxFile = sizeof(filename);
2256 ofn.lpstrFileTitle = NULL;
2257 ofn.nMaxFileTitle = 0;
2258 ofn.lpstrInitialDir = NULL;
2259 ofn.lpstrTitle = NULL;
2260 ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_NOTESTFILECREATE |
2261 OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR | OFN_SHAREAWARE;
2262 ofn.nFileOffset = 0;
2263 ofn.nFileExtension = 0;
2264 ofn.lpstrDefExt = "fpr";
2265 ofn.lpfnHook = NULL;
2266 ofn.lpTemplateName = NULL;
2267 BOOL result = save ? GetSaveFileName(&ofn) : GetOpenFileName(&ofn);
2268 if (result) {
2269 char cwd[256];
2270 GetCurrentDirectory(sizeof(cwd), cwd);
2271 int cwd_len = strlen(cwd);
2272 if (cwd_len > 0 && cwd[cwd_len - 1] != '\\') {
2273 strcat(cwd, "\\");
2274 cwd_len++;
2275 }
2276 if (strnicmp(filename, cwd, cwd_len) == 0)
2277 strcpy(prefs_path, filename + cwd_len);
2278 else
2279 strcpy(prefs_path, filename);
2280 }
2281 return result;
2282 }
2283
2284 void C64Display::WindowTitle()
2285 {
2286 // Show the program name, the current preferences file,
2287 // and the paused state or the speedometer.
2288 const char *prefs_path = TheApp->prefs_path;
2289 int prefs_path_length = strlen(prefs_path);
2290 if (prefs_path_length > 4 &&
2291 stricmp(prefs_path + prefs_path_length - 4, ".fpr") == 0)
2292 prefs_path_length -= 4;
2293 const char *info = NULL;
2294 char tmp[128];
2295 if (waiting)
2296 info = "PAUSED";
2297 else if (!ThePrefs.ShowLEDs && speed_index != 0) {
2298 if (IsFrodoSC)
2299 sprintf(tmp, "%.1f%%", speed_index);
2300 else
2301 sprintf(tmp, "%.0f%%", speed_index);
2302 info = tmp;
2303 }
2304 const char *sep1 = info ? " (" : "";
2305 const char *sep2 = info ? ")" : "";
2306 char title[256];
2307 sprintf(title, "%s - %.*s%s%s%s", TITLE,
2308 prefs_path_length, prefs_path, sep1, info ? info : "", sep2);
2309 SetWindowText(hwnd, title);
2310 }
2311
2312 void C64Display::NewPrefs()
2313 {
2314 // Resize the window to the new viewport while preserving
2315 // as closely as possible the previous scaling factors.
2316 RECT rc;
2317 GetWindowRect(hwnd, &rc);
2318 int x_nc = rc.right - rc.left - (rcWindow.right - rcWindow.left);
2319 int y_nc = rc.bottom - rc.top - (rcWindow.bottom - rcWindow.top);
2320 if (show_leds)
2321 y_nc -= led_rows;
2322 double x_scale = double(rcWindow.right - rcWindow.left)/view_x;
2323 double y_scale = double(rcWindow.bottom - rcWindow.top)/view_y;
2324 if (CalcViewPort() || show_leds != ThePrefs.ShowLEDs) {
2325 show_leds = ThePrefs.ShowLEDs;
2326 rc.right = int(rc.left + x_scale*view_x + x_nc);
2327 rc.bottom = int(rc.top + y_scale*view_y + y_nc);
2328 if (show_leds)
2329 rc.bottom += led_rows;
2330 ResizeWindow(WMSZ_BOTTOMRIGHT, &rc);
2331 MoveWindow(hwnd, rc.left, rc.top,
2332 rc.right - rc.left,
2333 rc.bottom - rc.top, TRUE);
2334 }
2335
2336 // The prefs filename might have changed.
2337 WindowTitle();
2338 }
2339
2340 void C64Display::OfferSave()
2341 {
2342 if (ThePrefs == ThePrefsOnDisk)
2343 return;
2344 const char *str = "Preferences have changed.\nSave preferences now?";
2345 int result = MessageBox(hwnd, str, "Frodo", MB_YESNO | MB_ICONQUESTION);
2346 if (result == IDYES)
2347 ThePrefs.Save(TheApp->prefs_path);
2348 }
2349
2350 void C64Display::Pause()
2351 {
2352 // It's not safe to call this from the contructor or destructor.
2353 if (in_constructor || in_destructor)
2354 return;
2355
2356 if (paused == 0)
2357 TheC64->Pause();
2358 paused++;
2359 }
2360
2361 void C64Display::Resume()
2362 {
2363 // It's not safe to call this from the contructor or destructor.
2364 if (in_constructor || in_destructor)
2365 return;
2366
2367 if (paused > 0) {
2368 paused--;
2369 if (!paused)
2370 TheC64->Resume();
2371 }
2372 else
2373 _ASSERTE(paused > 0);
2374 }
2375
2376 void C64Display::Quit()
2377 {
2378 quit = 1;
2379 TheC64->Quit();
2380 }