ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/Display_WIN32.h
Revision: 1.3
Committed: 2004-01-12T15:13:20Z (20 years, 9 months ago) by cebix
Content type: text/plain
Branch: MAIN
Changes since 1.2: +1 -1 lines
Log Message:
Happy New Year!

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * Display_WIN32.h - C64 graphics display, emulator window handling,
3     * WIN32 specific stuff
4     *
5 cebix 1.3 * Frodo (C) 1994-1997,2002-2004 Christian Bauer
6 cebix 1.1 *
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(char *str, char *button1, 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     }