Main Page | Class Hierarchy | Compound List | File List | Compound Members | File Members | Related Pages

Display_WIN32.i

Go to the documentation of this file.
00001 /*
00002  *  Display_WIN32.i - C64 graphics display, emulator window handling,
00003  *                    WIN32 specific stuff
00004  *
00005  *  Frodo (C) 1994-1997 Christian Bauer
00006  *  WIN32 code by J. Richard Sladkey <jrs@world.std.com>
00007  */
00008 
00009 #include <shellapi.h>
00010 #include <commctrl.h>
00011 
00012 #include "C64.h"
00013 #include "SAM.h"
00014 #include "Version.h"
00015 #include "VIC.h"
00016 #include "resource.h"
00017 
00018 #define NAME "Frodo"
00019 #define TITLE (IsFrodoSC ? "FrodoSC" : "Frodo")
00020 
00021 #ifdef DEBUG
00022 
00023 class TimeScope
00024 {
00025 
00026 public:
00027         TimeScope(const char *s)
00028         {
00029                 tag = s;
00030                 QueryPerformanceCounter(&liStart);
00031         }
00032         ~TimeScope()
00033         {
00034                 QueryPerformanceCounter(&liFinish);
00035                 OutputTime();
00036         }
00037 
00038 private:
00039         void
00040         OutputTime()
00041         {
00042                 LARGE_INTEGER liFreq;
00043 
00044                 QueryPerformanceFrequency(&liFreq);
00045                 Debug("%s: %.0f usec\n", tag,
00046                         double(liFinish.LowPart - liStart.LowPart)
00047                         /liFreq.LowPart*1000000);
00048         }
00049         const char *tag;
00050         LARGE_INTEGER liStart, liFinish;
00051 };
00052 
00053 #define TIMESCOPE(var, tag) TimeScope var(tag)
00054 
00055 #else
00056 
00057 #define TIMESCOPE(var, tag)
00058 
00059 #endif
00060 
00061 /*
00062   C64 keyboard matrix:
00063 
00064     Bit 7   6   5   4   3   2   1   0
00065   0    CUD  F5  F3  F1  F7 CLR RET DEL
00066   1    SHL  E   S   Z   4   A   W   3
00067   2     X   T   F   C   6   D   R   5
00068   3     V   U   H   B   8   G   Y   7
00069   4     N   O   K   M   0   J   I   9
00070   5     ,   @   :   .   -   L   P   +
00071   6     /   ^   =  SHR HOM  ;   *   
00072   7    R/S  Q   C= SPC  2  CTL  <-  1
00073 */
00074 
00075 #define MATRIX(a,b) (((a) << 3) | (b))
00076 
00077 #define KEY_F9          256
00078 #define KEY_F10         257
00079 #define KEY_F11         258
00080 #define KEY_F12         259
00081 
00082 #define KEY_FIRE        260
00083 #define KEY_JUP         261
00084 #define KEY_JDN         262
00085 #define KEY_JLF         263
00086 #define KEY_JRT         264
00087 #define KEY_JUPLF       265
00088 #define KEY_JUPRT       266
00089 #define KEY_JDNLF       267
00090 #define KEY_JDNRT       268
00091 #define KEY_CENTER      269
00092 
00093 #define KEY_NUMLOCK     270
00094 
00095 #define KEY_KPPLUS      271
00096 #define KEY_KPMINUS     272
00097 #define KEY_KPMULT      273
00098 #define KEY_KPDIV       274
00099 #define KEY_KPENTER     275
00100 #define KEY_KPPERIOD    276
00101 
00102 #define KEY_PAUSE       277
00103 #define KEY_ALTENTER    278
00104 #define KEY_CTRLENTER   279
00105 
00106 #define VK_bracketleft  0xdb
00107 #define VK_bracketright 0xdd
00108 #define VK_comma        0xbc
00109 #define VK_period       0xbe
00110 #define VK_slash        0xbf
00111 #define VK_semicolon    0xba
00112 #define VK_grave        0xc0
00113 #define VK_minus        0xbd
00114 #define VK_equal        0xbb
00115 #define VK_quote        0xde
00116 #define VK_backslash    0xdc
00117 
00118 static C64Display *TheDisplay;
00119 static int keystate[256];
00120 static UBYTE rev_matrix[8], key_matrix[8];
00121 static int quit = 0;
00122 static int numlock = 0;
00123 static int joystate = 0xff;
00124 
00125 static RECT rcScreen;
00126 static RECT rcLast;
00127 static RECT rcWindow;
00128 static RECT rcWork;
00129 static BOOL need_new_color_table = FALSE;
00130 static int view_x, view_y;
00131 
00132 static int led_rows = 16;
00133 
00134 static HCURSOR invisible_cursor;
00135 static HCURSOR arrow_cursor;
00136 
00137 static HFONT led_font;
00138 
00139 static HPEN led_highlight;
00140 static HPEN led_shadow;
00141 
00142 static HBRUSH led_brush;
00143 static HBRUSH off_brush;
00144 static HBRUSH error_off_brush;
00145 static HBRUSH on_brush;
00146 static HBRUSH error_on_brush;
00147 
00148 // Not fully working yet.
00149 #ifdef WORKBUFFER_BITMAP
00150 static BOOL workbuffer_bitmap = FALSE;
00151 static BOOL workbuffer_locked = FALSE;
00152 static DDSURFACEDESC bitmap_ddsd;
00153 #endif
00154 
00155 C64Display::DisplayMode default_modes[] = {
00156         { 320, 200, 8 },
00157         { 320, 240, 8 },
00158         { 512, 384, 8 },
00159         { 640, 400, 8 },
00160         { 640, 480, 8 },
00161         { 320, 200, 16 },
00162         { 320, 240, 16 },
00163         { 512, 384, 16 },
00164         { 640, 400, 16 },
00165         { 640, 480, 16 },
00166 };
00167 static int num_default_modes =
00168         sizeof(default_modes)/sizeof(C64Display::DisplayMode);
00169 
00170 static C64Display::DisplayMode *display_modes = NULL;
00171 static int num_display_modes = 0;
00172 static int max_display_modes = 16;
00173 
00174 int C64Display::GetNumDisplayModes() const
00175 {
00176         if (num_display_modes == 0)
00177                 return num_default_modes;
00178         return num_display_modes;
00179 }
00180 
00181 const C64Display::DisplayMode *C64Display::GetDisplayModes() const
00182 {
00183         if (num_display_modes == 0)
00184                 return default_modes;
00185         return display_modes;
00186 }
00187 
00188 long ShowRequester(char *str, char *button1, char *button2)
00189 {
00190         if (!TheDisplay) {
00191                 MessageBox(hwnd, str, "Frodo", MB_OK | MB_ICONSTOP);
00192                 return FALSE;
00193         }
00194         return TheDisplay->ShowRequester(str, button1, button2);
00195 }
00196 
00197 /*
00198  *  Display constructor: Create window/screen
00199  */
00200 
00201 C64Display::C64Display(C64 *the_c64) : TheC64(the_c64)
00202 {
00203         in_constructor = TRUE;
00204         in_destructor = FALSE;
00205 
00206         TheDisplay = this;
00207         speed_index = 0;
00208 
00209         pDD = NULL;
00210         pPrimary = NULL;
00211         pBack = NULL;
00212         pWork = NULL;
00213         pClipper = NULL;
00214         pPalette = NULL;
00215         active = FALSE;
00216         paused = FALSE;
00217         waiting = FALSE;
00218         show_leds = ThePrefs.ShowLEDs;
00219         full_screen = ThePrefs.DisplayType == DISPTYPE_SCREEN;
00220 
00221         // Turn LEDs off.
00222         for (int i = 0; i < 4; i++)
00223                 led_state[i] = old_led_state[i] = LED_OFF;
00224 
00225         // Allocate chunky buffer to draw into.
00226         chunky_buf = new UBYTE[DISPLAY_X * DISPLAY_Y];
00227 
00228         CalcViewPort();
00229 
00230         ResetKeyboardState();
00231 
00232         if (!MakeWindow()) {
00233                 ShowRequester("Failed to create window.", "Quit");
00234                 Quit();
00235         }
00236         else {
00237                 WindowTitle();
00238 
00239                 if (!StartDirectDraw()) {
00240                         ShowRequester(failure_message, "Quit");
00241                         Quit();
00242                 }
00243                 else
00244                         draw_led_bar();
00245         }
00246 
00247         in_constructor = FALSE;
00248 }
00249 
00250 /*
00251  *  Display destructor
00252  */
00253 
00254 C64Display::~C64Display()
00255 {
00256         in_destructor = TRUE;
00257 
00258         Debug("~C64Display\n");
00259 
00260         StopDirectDraw();
00261 
00262         // Offer to save now that we are not in full screen mode.
00263         OfferSave();
00264 
00265         // Free the display modes table.
00266         delete[] display_modes;
00267 
00268         // Free chunky buffer
00269         delete chunky_buf;
00270 
00271         // Destroy the main window.
00272         DestroyWindow(hwnd);
00273 
00274         // Drain the window message queue.
00275         for (;;)
00276         {
00277                 MSG msg;
00278                 if (!GetMessage(&msg, NULL, 0, 0))
00279                         break;
00280                 if (ThePrefs.SystemKeys)
00281                         TranslateMessage(&msg);
00282                 DispatchMessage(&msg);
00283         }
00284 
00285         DeleteObjects();
00286 
00287         in_destructor = FALSE;
00288 }
00289 
00290 /*
00291  *  Prefs may have changed
00292  */
00293 
00294 void C64Display::NewPrefs(Prefs *prefs)
00295 {
00296 }
00297 
00298 void C64Display::DeleteObjects()
00299 {
00300         // Delete objects we created.
00301         DeleteObject(led_highlight);
00302         DeleteObject(led_shadow);
00303         DeleteObject(led_font);
00304         DeleteObject(led_brush);
00305         DeleteObject(off_brush);
00306         DeleteObject(error_off_brush);
00307         DeleteObject(on_brush);
00308         DeleteObject(error_on_brush);
00309 }
00310 
00311 BOOL C64Display::CalcViewPort()
00312 {
00313         int old_view_x = view_x, old_view_y = view_y;
00314         const char *view_port = ThePrefs.ViewPort;
00315         if (view_port[0] == '\0' ||
00316             stricmp(view_port, "Default") == 0)
00317                 view_port = NULL;
00318         if (!view_port || sscanf(view_port, "%dx%d", &view_x, &view_y) != 2) {
00319                 view_x = DISPLAY_X;
00320                 view_y = DISPLAY_Y;
00321         }
00322         SetRect(&rcWork, 0, 0, view_x, view_y);
00323         if (view_x != old_view_x || view_y != old_view_y)
00324                 return TRUE;
00325         return FALSE;
00326 }
00327 
00328 
00329 BOOL C64Display::ResizeWindow(int side, RECT *pRect)
00330 {
00331         // Compute size of non-client borders.
00332         DWORD style = GetWindowLong(hwnd, GWL_STYLE);
00333         RECT rc;
00334         SetRect(&rc, 0, 0, view_x, view_y);
00335         BOOL has_menu = GetMenu(hwnd) != NULL;
00336         AdjustWindowRect(&rc, style, has_menu);
00337         if (ThePrefs.ShowLEDs)
00338                 rc.bottom += led_rows;
00339         int nc_x = rc.right - rc.left - view_x;
00340         int nc_y = rc.bottom - rc.top - view_y;
00341 
00342         // Compute client area corresponding to resizing.
00343         int old_x = pRect->right - pRect->left - nc_x;
00344         int old_y = pRect->bottom - pRect->top - nc_y;
00345 
00346         // Compute nearest integral scaling numerators.
00347         int d = ThePrefs.ScalingDenominator;
00348         int x = (old_x + view_x/d/2)/(view_x/d);
00349         if (x == 0)
00350                 x = 1;
00351         int y = (old_y + view_y/4)/(view_y/d);
00352         if (y == 0)
00353                 y = 1;
00354 
00355         // When resizing corners make the scale factors agree.
00356         switch (side) {
00357         case WMSZ_BOTTOMRIGHT:
00358         case WMSZ_BOTTOMLEFT:
00359         case WMSZ_TOPRIGHT:
00360         case WMSZ_TOPLEFT:
00361                 if (x < y)
00362                         y = x;
00363                 else
00364                         x = y;
00365         }
00366 
00367         // Compute the quantized size of the window area.
00368         int new_x = x*(view_x/d) + nc_x;
00369         int new_y = y*(view_y/d) + nc_y;
00370 
00371         // Adjust the resizing rectangle.
00372         switch (side) {
00373 
00374         case WMSZ_BOTTOMRIGHT:
00375         case WMSZ_BOTTOMLEFT:
00376         case WMSZ_BOTTOM:
00377                 pRect->bottom = pRect->top + new_y;
00378                 break;
00379 
00380         case WMSZ_TOPRIGHT:
00381         case WMSZ_TOPLEFT:
00382         case WMSZ_TOP:
00383                 pRect->top = pRect->bottom - new_y;
00384                 break;
00385         }
00386         switch (side) {
00387 
00388         case WMSZ_TOPRIGHT:
00389         case WMSZ_BOTTOMRIGHT:
00390         case WMSZ_RIGHT:
00391                 pRect->right = pRect->left + new_x;
00392                 break;
00393 
00394         case WMSZ_TOPLEFT:
00395         case WMSZ_BOTTOMLEFT:
00396         case WMSZ_LEFT:
00397                 pRect->left = pRect->right - new_x;
00398                 break;
00399         }
00400 
00401         return TRUE;
00402 }
00403 
00404 /*
00405  *  Update speedometer
00406  */
00407 
00408 void C64Display::Speedometer(int speed)
00409 {
00410         Debug("speed = %d %%\n", speed);
00411         speed_index = speed;
00412 
00413         if (full_screen)
00414                 return;
00415 
00416         if (!ThePrefs.ShowLEDs) {
00417                 WindowTitle();
00418                 return;
00419         }
00420 
00421         if (speed_index == 0)
00422                 return;
00423 
00424         HDC hdc = GetDC(hwnd);
00425         RECT rc;
00426         GetClientRect(hwnd, &rc);
00427         rc.top = rc.bottom - led_rows;
00428         rc.right = rc.left + (rc.right - rc.left)/5;
00429         FillRect(hdc, &rc, led_brush);
00430         SelectObject(hdc, led_font);
00431         SetTextAlign(hdc, TA_TOP | TA_LEFT);
00432         SetBkMode(hdc, TRANSPARENT);
00433         SetTextColor(hdc, (COLORREF) GetSysColor(COLOR_MENUTEXT));
00434         char str[128];
00435         if (IsFrodoSC)
00436                 sprintf(str, "%d%%", speed_index);
00437         else
00438                 sprintf(str, "%d%%", speed_index);
00439         int x = rc.left + 4;
00440         int y = rc.top + 2;
00441         TextOut(hdc, x, y, str, strlen(str));
00442         ReleaseDC(hwnd, hdc);
00443 }
00444 
00445 
00446 /*
00447  *  Return pointer to bitmap data
00448  */
00449 
00450 UBYTE *C64Display::BitmapBase()
00451 {
00452 #ifdef WORKBUFFER_BITMAP
00453         if (colors_depth == 8 && pWork) {
00454                 if (workbuffer_locked) {
00455                         pWork->Unlock(NULL);
00456                         workbuffer_locked = FALSE;
00457                 }
00458                 HRESULT ddrval;
00459                 for (;;) {
00460                         bitmap_ddsd.dwSize = sizeof(bitmap_ddsd);
00461                         ddrval = pWork->Lock(NULL, &bitmap_ddsd, 0, NULL);
00462                         if (ddrval != DDERR_WASSTILLDRAWING)
00463                                 break;
00464                 }
00465                 if (ddrval == DD_OK) {
00466                         workbuffer_locked = TRUE;
00467                         workbuffer_bitmap = TRUE;
00468                         return (UBYTE *) bitmap_ddsd.lpSurface;
00469                 }
00470         }
00471         workbuffer_bitmap = FALSE;
00472 #endif
00473         return chunky_buf;
00474 }
00475 
00476 
00477 /*
00478  *  Return number of bytes per row
00479  */
00480 
00481 int C64Display::BitmapXMod()
00482 {
00483 #ifdef WORKBUFFER_BITMAP
00484         if (workbuffer_locked)
00485                 return bitmap_ddsd.lPitch;
00486 #endif
00487         return DISPLAY_X;
00488 }
00489 
00490 
00491 /*
00492  *  Freshen keyboard state
00493  */
00494 
00495 void C64Display::PollKeyboard(UBYTE *CIA_key_matrix, UBYTE *CIA_rev_matrix, UBYTE *joystick)
00496 {
00497         //Debug("Display::PollKeyboard\n");
00498 
00499 #ifdef WORKBUFFER_BITMAP
00500         if (workbuffer_locked) {
00501                 pWork->Unlock(NULL);
00502                 workbuffer_locked = FALSE;
00503         }
00504 #endif
00505 
00506         for (;;)
00507         {
00508                 MSG msg;
00509                 if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
00510                         break;
00511                 if (ThePrefs.SystemKeys)
00512                         TranslateMessage(&msg);
00513                 DispatchMessage(&msg);
00514         }
00515 
00516         *joystick = joystate;
00517         memcpy(CIA_key_matrix, key_matrix, sizeof(key_matrix));
00518         memcpy(CIA_rev_matrix, rev_matrix, sizeof(rev_matrix));
00519 }
00520 
00521 /*
00522  *  Check if NumLock is down (for switching the joystick keyboard emulation)
00523  */
00524 
00525 bool C64Display::NumLock()
00526 {
00527         return numlock;
00528 }
00529 
00530 
00531 /*
00532  *  Allocate C64 colors
00533  */
00534 
00535 void C64Display::InitColors(UBYTE *array)
00536 {
00537         if (colors_depth == 8) {
00538                 for (int i = 0; i < 256; i++)
00539                         array[i] = colors[i & 0x0f];
00540         }
00541         else {
00542                 for (int i = 0; i < 256; i++)
00543                         array[i] = i & 0x0f;
00544         }
00545 }
00546 
00547 
00548 long C64Display::ShowRequester(const char *str, const char *button1, const char *button2)
00549 {
00550         // This could be a lot nicer but quick and dirty is fine with me.
00551         char message[1024];
00552         strcpy(message, str);
00553         strcat(message, "\nPress OK to ");
00554         strcat(message, button1);
00555         if (button2) {
00556                 strcat(message, ", Cancel to ");
00557                 strcat(message, button2);
00558         }
00559         strcat(message, ".");
00560         UINT type;
00561         if (button2) 
00562                 type = MB_OKCANCEL | MB_ICONQUESTION;
00563         else
00564                 type = MB_OK | MB_ICONSTOP;
00565         Pause();
00566         if (full_screen)
00567                 StopDirectDraw();
00568         int result = MessageBox(hwnd, message, NAME " Error", type);
00569         if (full_screen)
00570                 StartDirectDraw();
00571         Resume();
00572         if (result == IDCANCEL)
00573                 return TRUE;
00574         return FALSE;
00575 }
00576 
00577 void C64Display::WaitUntilActive()
00578 {
00579         Debug("waiting until not paused...\n");
00580         waiting = TRUE;
00581         WindowTitle();
00582         for (;;) {
00583 
00584                 // Check for termination condition.
00585                 if (!paused || quit)
00586                         break;
00587 
00588                 // Process message queue.
00589                 MSG msg;
00590                 if (GetMessage(&msg, NULL, 0, 0) != TRUE)
00591                         break;
00592 
00593                 // Always translate system keys while paused.
00594                 TranslateMessage(&msg);
00595                 DispatchMessage(&msg);
00596         }
00597         waiting = FALSE;
00598         Debug("...done waiting\n");
00599         WindowTitle();
00600         ResetKeyboardState();
00601 }
00602 
00603 void C64Display::ResetKeyboardState()
00604 {
00605         memset(keystate, 0, sizeof(keystate));
00606         memset(key_matrix, 0xff, sizeof(key_matrix));
00607         memset(rev_matrix, 0xff, sizeof(rev_matrix));
00608         joystate = 0xff;
00609 }
00610 
00611 BOOL C64Display::MakeWindow()
00612 {
00613         // Set up and register window class.
00614         WNDCLASS wc;
00615         wc.style = CS_HREDRAW | CS_VREDRAW;
00616         wc.lpfnWndProc = StaticWindowProc;
00617         wc.cbClsExtra = 0;
00618         wc.cbWndExtra = 0;
00619         wc.hInstance = hInstance;
00620         wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(FRODO_ICON));
00621         wc.hCursor = NULL;
00622         wc.hbrBackground = NULL;
00623         wc.lpszMenuName = MAKEINTRESOURCE(IDR_MAIN_MENU);
00624         wc.lpszClassName = NAME;
00625         RegisterClass(&wc);
00626 
00627         // Set up our preferred styles for our window depending on the mode.
00628         windowed_style = WS_VISIBLE | WS_SYSMENU | WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_THICKFRAME;
00629         fullscreen_style = WS_POPUP | WS_VISIBLE;
00630 
00631         // Compute the initial window size.
00632         DWORD style = windowed_style;
00633         RECT rc;
00634         int n = ThePrefs.ScalingNumerator;
00635         int d = ThePrefs.ScalingDenominator;
00636         SetRect(&rc, 0, 0, n*view_x/d, n*view_y/d);
00637         BOOL has_menu = wc.lpszMenuName != NULL;
00638         AdjustWindowRect(&rc, style, has_menu);
00639         if (ThePrefs.ShowLEDs)
00640                 rc.bottom += led_rows;
00641         int x_size = rc.right - rc.left;
00642         int y_size = rc.bottom - rc.top;
00643 
00644         // Create the window and save the initial position.
00645         hwnd = CreateWindowEx(0, NAME, TITLE, style, CW_USEDEFAULT, 0, x_size, y_size, NULL, NULL, hInstance, NULL);
00646         GetWindowRect(hwnd, &rcLast);
00647         SetRect(&rcScreen, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
00648 
00649         // Load cursors.
00650         invisible_cursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_INVISIBLE));
00651         arrow_cursor = LoadCursor(0, MAKEINTRESOURCE(IDC_ARROW));
00652 
00653         // Create fonts, pens, brushes, etc.
00654         CreateObjects();
00655 
00656         if (!hwnd)
00657                 return FALSE;
00658 
00659         ShowWindow(hwnd, nCmdShow);
00660         UpdateWindow(hwnd);
00661 
00662         return TRUE;
00663 }
00664 
00665 void C64Display::CreateObjects()
00666 {
00667         // Create fonts.
00668         led_font = CreateFont(14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
00669                 VARIABLE_PITCH | FF_SWISS, "");
00670 
00671         // Create pens.
00672         led_highlight = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_3DHIGHLIGHT));
00673         led_shadow = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_3DSHADOW));
00674 
00675         // Create brushes.
00676         LOGBRUSH logbrush;
00677         logbrush.lbStyle = BS_SOLID;
00678         logbrush.lbHatch = 0;
00679         logbrush.lbColor = GetSysColor(COLOR_MENU);
00680         led_brush = CreateBrushIndirect(&logbrush);
00681         logbrush.lbColor = RGB(0x00, 0x00, 0x00); // black
00682         off_brush = CreateBrushIndirect(&logbrush);
00683         logbrush.lbColor = RGB(0x00, 0x00, 0x00); // black
00684         error_off_brush = CreateBrushIndirect(&logbrush);
00685         logbrush.lbColor = RGB(0x00, 0xff, 0x00); // green
00686         on_brush = CreateBrushIndirect(&logbrush);
00687         logbrush.lbColor = RGB(0xff, 0x00, 0x00); // red
00688         error_on_brush = CreateBrushIndirect(&logbrush);
00689 }
00690 
00691 HRESULT CALLBACK C64Display::StaticWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
00692 {
00693         return TheDisplay->WindowProc(hWnd, message, wParam, lParam);
00694 }
00695 
00696 long C64Display::WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
00697 {
00698         Debug("window message: 0x%x\n", message);
00699 
00700         switch (message) {
00701 
00702         case WM_SYSCOLORCHANGE:
00703                 DeleteObjects();
00704                 CreateObjects();
00705                 InvalidateRect(hwnd, NULL, FALSE);
00706                 break;
00707 
00708         case WM_MOUSEMOVE:
00709                 SetCursor(ThePrefs.HideCursor ? invisible_cursor : arrow_cursor);
00710                 break;
00711                 
00712         case WM_MENUCHAR:
00713                 // Eat Alt-foo characters so that it doesn't beep.
00714                 if (HIWORD(wParam) == 0)
00715                         return MAKELONG(0, 1);
00716                 break;
00717                 
00718         case WM_ENTERSIZEMOVE:
00719                 Pause();
00720                 break;
00721 
00722         case WM_EXITSIZEMOVE:
00723                 Resume();
00724                 break;
00725 
00726         case WM_SIZING:
00727                 ResizeWindow(wParam, (RECT *) lParam);
00728                 return TRUE;
00729 
00730         case WM_SIZE:
00731         case WM_MOVE:
00732                 if (full_screen)
00733                         SetRect(&rcWindow, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
00734                 else {
00735                         GetClientRect(hWnd, &rcWindow);
00736                         if (ThePrefs.ShowLEDs)
00737                                 rcWindow.bottom -= led_rows;
00738                         ClientToScreen(hWnd, (LPPOINT) &rcWindow);
00739                         ClientToScreen(hWnd, (LPPOINT) &rcWindow + 1);
00740 
00741                         // Align the client rect to a four-byte
00742                         // boundary because this can triple the
00743                         // speed of a memcpy to the display.
00744                         int align_to = 4;
00745                         int misalignment = rcWindow.left % align_to;
00746                         if (misalignment == 0)
00747                                 Update();
00748                         else {
00749                                 if (misalignment > align_to/2)
00750                                         misalignment -= align_to;
00751                                 RECT rc;
00752                                 GetWindowRect(hwnd, &rc);
00753                                 MoveWindow(hwnd, rc.left - misalignment,
00754                                         rc.top, rc.right - rc.left,
00755                                         rc.bottom - rc.top, TRUE);
00756                         }
00757                 }
00758                 break;
00759 
00760         case WM_DISPLAYCHANGE:
00761                 if (!full_screen)
00762                         ResumeDirectDraw();
00763                 SetRect(&rcScreen, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
00764                 break;
00765 
00766         case WM_ACTIVATE:
00767                 Debug("WM_ACTIVATE\n");
00768                 {
00769                         int old_active = active;
00770                         active = LOWORD(wParam) != WA_INACTIVE;
00771                         if (ThePrefs.AutoPause && active != old_active) {
00772                                 if (!active)
00773                                         Pause();
00774                                 else
00775                                         Resume();
00776                         }
00777                 }
00778                 if (active) {
00779                         ResumeDirectDraw();
00780                         ResetKeyboardState();
00781                 }
00782 
00783                 // Kick the message loop since this was sent to us, not posted.
00784                 PostMessage(hWnd, WM_USER, 0, 0);
00785                 break;
00786 
00787         case WM_COMMAND:
00788                 {
00789                         int id = LOWORD(wParam);
00790                         switch (id) {
00791 
00792                         case ID_FILE_NEW:
00793                                 {
00794                                         OfferSave();
00795                                         Prefs *prefs = new Prefs;
00796                                         TheC64->NewPrefs(prefs);
00797                                         ThePrefs = *prefs;
00798                                         ThePrefsOnDisk = ThePrefs;
00799                                         delete prefs;
00800                                         strcpy(TheApp->prefs_path, "Untitled.fpr");
00801                                         NewPrefs();
00802                                 }
00803                                 break;
00804 
00805                         case ID_FILE_OPEN:
00806                                 Pause();
00807                                 OfferSave();
00808                                 if (FileNameDialog(TheApp->prefs_path)) {
00809                                         Prefs *prefs = new Prefs;
00810                                         prefs->Load(TheApp->prefs_path);
00811                                         TheC64->NewPrefs(prefs);
00812                                         ThePrefs = *prefs;
00813                                         delete prefs;
00814                                         NewPrefs();
00815                                 }
00816                                 Resume();
00817                                 break;
00818 
00819                         case ID_FILE_SAVE:
00820                                 ThePrefs.Save(TheApp->prefs_path);
00821                                 break;
00822 
00823                         case ID_FILE_SAVEAS:
00824                                 Pause();
00825                                 if (FileNameDialog(TheApp->prefs_path, TRUE)) {
00826                                         ThePrefs.Save(TheApp->prefs_path);
00827                                         WindowTitle();
00828                                 }
00829                                 Resume();
00830                                 break;
00831 
00832                         case ID_FILE_EX:
00833                                 PostMessage(hWnd, WM_CLOSE, 0, 0);
00834                                 break;
00835 
00836                         case ID_TOOLS_PREFERENCES:
00837                                 Pause();
00838                                 TheApp->RunPrefsEditor();
00839                                 NewPrefs();
00840                                 Resume();
00841                                 break;
00842 
00843                         case ID_TOOLS_FULLSCREEN:
00844                                 Pause();
00845                                 StopDirectDraw();
00846                                 full_screen = !full_screen;
00847                                 if (!StartDirectDraw()) {
00848                                         StopDirectDraw();
00849                                         full_screen = !full_screen;
00850                                         StartDirectDraw();
00851                                         if (!full_screen)
00852                                                 ShowRequester(failure_message, "Continue");
00853                                 }
00854                                 Resume();
00855                                 CheckMenuItem(GetMenu(hWnd), ID_TOOLS_FULLSCREEN, full_screen ? MF_CHECKED : MF_UNCHECKED);
00856                                 if (paused)
00857                                         Update();
00858                                 break;
00859 
00860                         case ID_TOOLS_RESETDIRECTDRAW:
00861                                 ResetDirectDraw();
00862                                 break;
00863 
00864                         case ID_TOOLS_PAUSE:
00865                                 if (!paused)
00866                                         Pause();
00867                                 else {
00868                                         // XXX: Shouldn't happen but be safe.
00869                                         while (paused)
00870                                                 Resume();
00871                                         ResetKeyboardState();
00872                                 }
00873                                 CheckMenuItem(GetMenu(hWnd), ID_TOOLS_PAUSE, paused ? MF_CHECKED : MF_UNCHECKED);
00874                                 break;
00875 
00876                         case ID_TOOLS_RESETC64:
00877                                 TheC64->Reset();
00878                                 break;
00879 
00880                         case ID_TOOLS_INSERTNEXTDISK:
00881                                 InsertNextDisk();
00882                                 break;
00883 
00884                         case ID_TOOLS_SAM:
00885                                 Pause();
00886                                 MessageBox(hWnd, "SAM not yet implemented.", NAME, MB_OK);
00887                                 Resume();
00888                                 break;
00889 
00890                         case ID_HELP_CONTENTS:
00891                         case ID_HELP_KEYBOARD:
00892                         case ID_HELP_SETTINGS:
00893                                 {
00894                                         const char *html;
00895                                         switch (id) {
00896                                         case ID_HELP_CONTENTS: html = "Main"; break;
00897                                         case ID_HELP_KEYBOARD: html = "keyboard"; break;
00898                                         case ID_HELP_SETTINGS: html = "settings"; break;
00899                                         }
00900                                         char helpfile[256];
00901                                         sprintf(helpfile, "%s\\Docs\\%s.html", AppDirPath, html);
00902                                         ShellExecute(0, 0, helpfile, 0, 0, SW_NORMAL);
00903                                 }
00904                                 break;
00905 
00906                         case ID_HELP_ABOUT:
00907                                 {
00908                                         Pause();
00909                                         char message[256];
00910                                         sprintf(message, "%s by %s\n%s by %s",
00911                                                 VERSION_STRING,
00912                                                 "Christian Bauer",
00913                                                 "WIN32 port",
00914                                                 "J. Richard Sladkey");
00915                                         MessageBox(hWnd, message, NAME, MB_OK);
00916                                         Resume();
00917                                 }
00918                                 break;
00919                         }
00920                         ResetKeyboardState();
00921                 }
00922                 break;
00923 
00924         case WM_CLOSE:
00925                 Quit();
00926                 return 0;
00927 
00928         case WM_DESTROY:
00929                 PostQuitMessage(0);
00930                 break;
00931 
00932         case WM_QUERYNEWPALETTE:
00933                 if (!full_screen && pPalette && pPrimary) {
00934                         SetPalettes();
00935                         BuildColorTable();
00936                         if (!active)
00937                                 Update();
00938                 }
00939                 break;
00940 
00941         case WM_PALETTECHANGED:
00942                 if (!full_screen) {
00943                         if ((HWND) wParam != hWnd) {
00944                                 need_new_color_table = TRUE;
00945                                 InvalidateRect(hwnd, NULL, FALSE);
00946                         }
00947                 }
00948                 break;
00949 
00950         case WM_PAINT:
00951                 if (!full_screen)
00952                 {
00953                         PAINTSTRUCT ps;
00954                         HDC hdc = BeginPaint(hWnd, &ps);
00955                         EndPaint(hWnd, &ps);
00956                         if (need_new_color_table) {
00957                                 BuildColorTable();
00958                                 need_new_color_table = FALSE;
00959                         }
00960                         if (paused)
00961                                 Update();
00962                         draw_led_bar();
00963                         Speedometer(speed_index);
00964                         return 0;
00965                 }
00966                 break;
00967 
00968         case WM_ENTERMENULOOP:
00969                 Pause();
00970                 break;
00971 
00972         case WM_EXITMENULOOP:
00973                 Resume();
00974                 ResetKeyboardState();
00975                 break;
00976 
00977         case WM_SYSKEYDOWN:
00978         case WM_KEYDOWN:
00979                 Debug("Display::WindowProc: KEYDOWN: 0x%x\n", wParam);
00980                 {
00981                         int kc = VirtKey2C64(wParam, lParam);
00982                         switch (kc) {
00983 
00984                         case KEY_PAUSE:
00985                                 PostMessage(hWnd, WM_COMMAND, ID_TOOLS_PAUSE, 0);
00986                                 break;
00987 
00988                         case KEY_KPPLUS:
00989                                 if (ThePrefs.SkipFrames < 10)
00990                                         ThePrefs.SkipFrames++;
00991                                 break;
00992 
00993                         case KEY_KPMINUS:
00994                                 if (ThePrefs.SkipFrames > 1)
00995                                         ThePrefs.SkipFrames--;
00996                                 break;
00997 
00998                         case KEY_KPMULT:
00999                                 ThePrefs.LimitSpeed = !ThePrefs.LimitSpeed;
01000                                 break;
01001 
01002                         case KEY_KPDIV:
01003                                 {
01004                                         Prefs *prefs = new Prefs(ThePrefs);
01005                                         prefs->Emul1541Proc = !prefs->Emul1541Proc;
01006                                         TheC64->NewPrefs(prefs);
01007                                         ThePrefs = *prefs;
01008                                         delete prefs;
01009                                 }
01010                                 break;
01011 
01012                         case KEY_KPPERIOD:
01013                                 ThePrefs.JoystickSwap = !ThePrefs.JoystickSwap;
01014                                 break;
01015 
01016                         case KEY_F9:
01017                                 PostMessage(hWnd, WM_COMMAND, ID_TOOLS_INSERTNEXTDISK, 0);
01018                                 break;
01019 
01020                         case KEY_ALTENTER:
01021                                 PostMessage(hWnd, WM_COMMAND, ID_TOOLS_FULLSCREEN, 0);
01022                                 break;
01023 
01024                         case KEY_CTRLENTER:
01025                                 PostMessage(hWnd, WM_COMMAND, ID_TOOLS_RESETDIRECTDRAW, 0);
01026                                 break;
01027 
01028                         case KEY_F10:
01029                                 PostMessage(hWnd, WM_CLOSE, 0, 0);
01030                                 break;
01031 
01032                         case KEY_F11:
01033                                 if (!paused)
01034                                         TheC64->NMI();
01035                                 break;
01036 
01037                         case KEY_F12:
01038                                 if (!paused)
01039                                         TheC64->Reset();
01040                                 break;
01041 
01042                         case KEY_FIRE:
01043                                 joystate &= ~0x10;
01044                                 break;
01045 
01046                         case KEY_JUP:
01047                                 joystate |=  0x02;
01048                                 joystate &= ~0x01;
01049                                 break;
01050 
01051                         case KEY_JDN:
01052                                 joystate |=  0x01;
01053                                 joystate &= ~0x02;
01054                                 break;
01055 
01056                         case KEY_JLF:
01057                                 joystate |=  0x08;
01058                                 joystate &= ~0x04;
01059                                 break;
01060 
01061                         case KEY_JRT:
01062                                 joystate |=  0x04;
01063                                 joystate &= ~0x08;
01064                                 break;
01065 
01066                         case KEY_JUPLF:
01067                                 joystate |=  0x0a;
01068                                 joystate &= ~0x05;
01069                                 break;
01070 
01071                         case KEY_JUPRT:
01072                                 joystate |=  0x06;
01073                                 joystate &= ~0x09;
01074                                 break;
01075 
01076                         case KEY_JDNLF:
01077                                 joystate |=  0x09;
01078                                 joystate &= ~0x06;
01079                                 break;
01080 
01081                         case KEY_JDNRT:
01082                                 joystate |=  0x05;
01083                                 joystate &= ~0x0a;
01084                                 break;
01085 
01086                         case KEY_CENTER:
01087                                 joystate |=  0x0f;
01088                                 break;
01089 
01090                         default:
01091                                 if (kc < 0 || kc >= 256)
01092                                         break;
01093                                 if (keystate[kc])
01094                                         break;
01095                                 keystate[kc] = 1;
01096                                 int c64_byte = kc >> 3;
01097                                 int c64_bit = kc & 7;
01098                                 int shifted = kc & 128;
01099                                 c64_byte &= 7;
01100                                 if (shifted) {
01101                                         key_matrix[6] &= 0xef;
01102                                         rev_matrix[4] &= 0xbf;
01103                                 }
01104                                 key_matrix[c64_byte] &= ~(1 << c64_bit);
01105                                 rev_matrix[c64_bit] &= ~(1 << c64_byte);
01106                                 break;
01107                         }
01108                         return 0;
01109                 }
01110                 break;
01111 
01112         case WM_SYSKEYUP:
01113         case WM_KEYUP:
01114                 Debug("Display::WindowProc: KEYUP: 0x%x\n", wParam);
01115                 {
01116                         int kc = VirtKey2C64(wParam, lParam);
01117                         switch (kc) {
01118 
01119                         case KEY_FIRE:
01120                                 joystate |= 0x10;
01121                                 break;
01122 
01123                         case KEY_JUP:
01124                                 joystate |= 0x01;
01125                                 break;
01126 
01127                         case KEY_JDN:
01128                                 joystate |= 0x02;
01129                                 break;
01130 
01131                         case KEY_JLF:
01132                                 joystate |= 0x04;
01133                                 break;
01134 
01135                         case KEY_JRT:
01136                                 joystate |= 0x08;
01137                                 break;
01138 
01139                         case KEY_JUPLF:
01140                                 joystate |= 0x05;
01141                                 break;
01142 
01143                         case KEY_JUPRT:
01144                                 joystate |= 0x09;
01145                                 break;
01146 
01147                         case KEY_JDNLF:
01148                                 joystate |= 0x06;
01149                                 break;
01150 
01151                         case KEY_JDNRT:
01152                                 joystate |= 0x0a;
01153                                 break;
01154 
01155                         default:
01156                                 if (kc < 0 || kc >= 256)
01157                                         break;
01158                                 if (!keystate[kc])
01159                                         break;
01160                                 keystate[kc] = 0;
01161                                 int c64_byte = kc >> 3;
01162                                 int c64_bit = kc & 7;
01163                                 int shifted = kc & 128;
01164                                 c64_byte &= 7;
01165                                 if (shifted) {
01166                                         key_matrix[6] |= 0x10;
01167                                         rev_matrix[4] |= 0x40;
01168                                 }
01169                                 key_matrix[c64_byte] |= (1 << c64_bit);
01170                                 rev_matrix[c64_bit] |= (1 << c64_byte);
01171                                 break;
01172                         }
01173                         return 0;
01174                 }
01175                 break;
01176         }
01177 
01178         return DefWindowProc(hWnd, message, wParam, lParam);
01179 }
01180 
01181 int C64Display::VirtKey2C64(int virtkey, DWORD keydata)
01182 {
01183         int ext = keydata & 0x01000000;
01184         int sc = (keydata & 0x00ff0000) >> 16;
01185         int result = -1;
01186 
01187         switch (virtkey) {
01188 
01189         case VK_NUMPAD0: numlock = 1; return KEY_FIRE;
01190         case VK_NUMPAD1: numlock = 1; return KEY_JDNLF;
01191         case VK_NUMPAD2: numlock = 1; return KEY_JDN;
01192         case VK_NUMPAD3: numlock = 1; return KEY_JDNRT;
01193         case VK_NUMPAD4: numlock = 1; return KEY_JLF;
01194         case VK_NUMPAD5: numlock = 1; return KEY_CENTER;
01195         case VK_NUMPAD6: numlock = 1; return KEY_JRT;
01196         case VK_NUMPAD7: numlock = 1; return KEY_JUPLF;
01197         case VK_NUMPAD8: numlock = 1; return KEY_JUP;
01198         case VK_NUMPAD9: numlock = 1; return KEY_JUPRT;
01199 
01200         case VK_NUMLOCK: return KEY_NUMLOCK;
01201         case VK_MULTIPLY: return KEY_KPMULT;
01202         case VK_DIVIDE: return KEY_KPDIV;
01203         case VK_SUBTRACT: return KEY_KPMINUS;
01204         case VK_ADD: return KEY_KPPLUS;
01205         case VK_DECIMAL: return KEY_KPPERIOD;
01206 
01207         case VK_F9: return KEY_F9;
01208         case VK_F10: return KEY_F10;
01209         case VK_F11: return KEY_F11;
01210         case VK_F12: return KEY_F12;
01211         case VK_PAUSE: return KEY_PAUSE;
01212 
01213         case VK_BACK: return MATRIX(0,0);
01214         case VK_DELETE: return ext ? MATRIX(0,0) : /*KP*/ KEY_KPPERIOD;
01215         case VK_TAB: return -1;
01216         case VK_RETURN:
01217                 if ((GetKeyState(VK_MENU) & 0x8000))
01218                         return KEY_ALTENTER;
01219                 if ((GetKeyState(VK_CONTROL) & 0x8000))
01220                         return KEY_CTRLENTER;
01221                 return ext ? /*KP*/ MATRIX(0,1) : MATRIX(0,1);
01222         case VK_SPACE: return MATRIX(7,4);
01223         case VK_ESCAPE: return MATRIX(7,7);
01224         case VK_INSERT: if (!ext) numlock = 0; return ext ? MATRIX(0,0) | 0x80 : /*KP*/ KEY_FIRE;
01225         case VK_HOME: if (!ext) numlock = 0; return ext ? MATRIX(6,3) : /*KP*/ KEY_JUPLF;
01226         case VK_END: if (!ext) numlock = 0; return ext ? MATRIX(6,0) : /*KP*/ KEY_JDNLF;
01227         case VK_PRIOR: if (!ext) numlock = 0; return ext ? MATRIX(6,6) : /*KP*/ KEY_JUPRT;
01228         case VK_NEXT: if (!ext) numlock = 0; return ext ? MATRIX(6,5) : /*KP*/ KEY_JDNRT;
01229         case VK_CLEAR: return KEY_CENTER;
01230 
01231         case VK_SHIFT: return sc == 0x36 ? /*R*/ MATRIX(6,4) : MATRIX(1,7);
01232         case VK_CONTROL: return ext ? /*R*/ MATRIX(7,5) : MATRIX(7,2);
01233         case VK_MENU: return ext ? /*R*/ MATRIX(7,5) : MATRIX(7,5);
01234 
01235         case VK_UP: if (!ext) numlock = 0; return ext ? MATRIX(0,7) | 0x80 : /*KP*/ KEY_JUP;
01236         case VK_DOWN: if (!ext) numlock = 0; return ext ? MATRIX(0,7) : /*KP*/ KEY_JDN;
01237         case VK_LEFT: if (!ext) numlock = 0; return ext ? MATRIX(0,2) | 0x80 : /*KP*/ KEY_JLF;
01238         case VK_RIGHT: if (!ext) numlock = 0; return ext ? MATRIX(0,2) : /*KP*/ KEY_JRT;
01239 
01240         case VK_F1: return MATRIX(0,4);
01241         case VK_F2: return MATRIX(0,4) | 0x80;
01242         case VK_F3: return MATRIX(0,5);
01243         case VK_F4: return MATRIX(0,5) | 0x80;
01244         case VK_F5: return MATRIX(0,6);
01245         case VK_F6: return MATRIX(0,6) | 0x80;
01246         case VK_F7: return MATRIX(0,3);
01247         case VK_F8: return MATRIX(0,3) | 0x80;
01248 
01249         case '0': return MATRIX(4,3);
01250         case '1': return MATRIX(7,0);
01251         case '2': return MATRIX(7,3);
01252         case '3': return MATRIX(1,0);
01253         case '4': return MATRIX(1,3);
01254         case '5': return MATRIX(2,0);
01255         case '6': return MATRIX(2,3);
01256         case '7': return MATRIX(3,0);
01257         case '8': return MATRIX(3,3);
01258         case '9': return MATRIX(4,0);
01259 
01260         case VK_bracketleft: return MATRIX(5,6);
01261         case VK_bracketright: return MATRIX(6,1);
01262         case VK_slash: return MATRIX(6,7);
01263         case VK_semicolon: return MATRIX(5,5);
01264         case VK_grave: return MATRIX(7,1);
01265         case VK_minus: return MATRIX(5,0);
01266         case VK_equal: return MATRIX(5,3);
01267         case VK_comma: return MATRIX(5,7);
01268         case VK_period: return MATRIX(5,4);
01269         case VK_quote: return MATRIX(6,2);
01270         case VK_backslash: return MATRIX(6,6);
01271 
01272         case 'A': result = MATRIX(1,2); break;
01273         case 'B': result = MATRIX(3,4); break;
01274         case 'C': result = MATRIX(2,4); break;
01275         case 'D': result = MATRIX(2,2); break;
01276         case 'E': result = MATRIX(1,6); break;
01277         case 'F': result = MATRIX(2,5); break;
01278         case 'G': result = MATRIX(3,2); break;
01279         case 'H': result = MATRIX(3,5); break;
01280         case 'I': result = MATRIX(4,1); break;
01281         case 'J': result = MATRIX(4,2); break;
01282         case 'K': result = MATRIX(4,5); break;
01283         case 'L': result = MATRIX(5,2); break;
01284         case 'M': result = MATRIX(4,4); break;
01285         case 'N': result = MATRIX(4,7); break;
01286         case 'O': result = MATRIX(4,6); break;
01287         case 'P': result = MATRIX(5,1); break;
01288         case 'Q': result = MATRIX(7,6); break;
01289         case 'R': result = MATRIX(2,1); break;
01290         case 'S': result = MATRIX(1,5); break;
01291         case 'T': result = MATRIX(2,6); break;
01292         case 'U': result = MATRIX(3,6); break;
01293         case 'V': result = MATRIX(3,7); break;
01294         case 'W': result = MATRIX(1,1); break;
01295         case 'X': result = MATRIX(2,7); break;
01296         case 'Y': result = MATRIX(3,1); break;
01297         case 'Z': result = MATRIX(1,4); break;
01298 
01299         }
01300 
01301         if (result != -1 && GetKeyState(VK_CAPITAL))
01302                 result |= 0x80;
01303 
01304         return result;
01305 }
01306 
01307 BOOL C64Display::SetupWindow()
01308 {
01309         // Setup the window.
01310         SetupWindowMode(full_screen);
01311 
01312         UpdateWindow(hwnd);
01313 
01314         if (full_screen)
01315                 ShowCursor(FALSE);
01316 
01317         return TRUE;
01318 }
01319 
01320 BOOL C64Display::SetupWindowMode(BOOL full_screen_mode)
01321 {
01322         DWORD style;
01323         int x0, y0, x, y;
01324         if (full_screen_mode) {
01325                 style = fullscreen_style;
01326                 x0 = 0;
01327                 y0 = 0;
01328                 x = GetSystemMetrics(SM_CXSCREEN);
01329                 y = GetSystemMetrics(SM_CYSCREEN);
01330         }
01331         else {
01332                 style = windowed_style;
01333                 x0 = rcLast.left;
01334                 y0 = rcLast.top;
01335                 x = rcLast.right - rcLast.left;
01336                 y = rcLast.bottom - rcLast.top;
01337         }
01338         SetWindowLong(hwnd, GWL_STYLE, style);
01339         SetWindowPos(hwnd, NULL, x0, y0, x, y, SWP_NOZORDER | SWP_NOACTIVATE);
01340         SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
01341         GetClientRect(hwnd, &rcWindow);
01342         if (!full_screen_mode && ThePrefs.ShowLEDs)
01343                 rcWindow.bottom -= led_rows;
01344         ClientToScreen(hwnd, (LPPOINT) &rcWindow);
01345         ClientToScreen(hwnd, (LPPOINT) &rcWindow + 1);
01346 
01347         // Windowed mode has a menu, full screen mode doesn't.
01348         HMENU old_menu = GetMenu(hwnd);
01349         if (old_menu) {
01350                 SetMenu(hwnd, NULL);
01351                 DestroyMenu(old_menu);
01352         }
01353         if (!full_screen_mode) {
01354                 HMENU new_menu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MAIN_MENU));
01355                 SetMenu(hwnd, new_menu);
01356         }
01357 
01358         return TRUE;
01359 }
01360 
01361 BOOL C64Display::RestoreWindow()
01362 {
01363         if (full_screen)
01364                 ShowCursor(TRUE);
01365 
01366         if (!full_screen)
01367                 GetWindowRect(hwnd, &rcLast);
01368 
01369         SetupWindowMode(FALSE);
01370 
01371         return TRUE;
01372 }
01373 
01374 HRESULT CALLBACK C64Display::EnumModesCallback(LPDDSURFACEDESC pDDSD, LPVOID lpContext)
01375 {
01376         C64Display *pDisplay = (C64Display *) lpContext;
01377         return pDisplay->EnumModesCallback(pDDSD);
01378 }
01379 
01380 HRESULT C64Display::EnumModesCallback(LPDDSURFACEDESC pDDSD)
01381 {
01382         DisplayMode mode;
01383         mode.x = pDDSD->dwWidth;
01384         mode.y = pDDSD->dwHeight;
01385         mode.depth = pDDSD->ddpfPixelFormat.dwRGBBitCount;
01386         mode.modex = (pDDSD->ddsCaps.dwCaps & DDSCAPS_MODEX) != 0;
01387         Debug("EnumModesCallback: %dx%dx%d (modex: %d)\n",
01388                 mode.x, mode.y, mode.depth, mode.modex);
01389         if (display_modes == NULL)
01390                 display_modes = new DisplayMode[max_display_modes];
01391         if (num_display_modes == max_display_modes) {
01392                 int old_max = max_display_modes;
01393                 max_display_modes *= 2;
01394                 DisplayMode *new_modes = new DisplayMode[max_display_modes];
01395                 memcpy(new_modes, display_modes, sizeof(DisplayMode)*old_max);
01396                 delete[] display_modes;
01397                 display_modes = new_modes;
01398         }
01399         display_modes[num_display_modes++] = mode;
01400         return DDENUMRET_OK;
01401 }
01402 
01403 int C64Display::CompareModes(const void *e1, const void *e2)
01404 {
01405         DisplayMode *m1 = (DisplayMode *) e1;
01406         DisplayMode *m2 = (DisplayMode *) e2;
01407         if (m1->depth != m2->depth)
01408                 return m1->depth - m2->depth;
01409         if (m1->x != m2->x)
01410                 return m1->x - m2->x;
01411         if (m1->y != m2->y)
01412                 return m1->y - m2->y;
01413         if (m1->modex != m2->modex)
01414                 return int(m1->modex) - int(m2->modex);
01415         return 0;
01416 }
01417 
01418 BOOL C64Display::StartDirectDraw()
01419 {
01420         // Setup our window size, position, style, etc.
01421         SetupWindow();
01422 
01423         // Create the main DirectDraw object.
01424         HRESULT ddrval = DirectDrawCreate(NULL, &pDD, NULL);
01425         if (ddrval != DD_OK) {
01426                 DebugResult("DirectDrawCreate failed", ddrval);
01427                 return Fail("Failed to initialize direct draw.");
01428         }
01429 
01430         if (full_screen) {
01431 
01432                 // Set exclusive mode.
01433                 ddrval = pDD->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX);
01434                 if (ddrval != DD_OK) {
01435                         DebugResult("SetCooperativeLevel failed", ddrval);
01436                         return Fail("Failed to set exclusive cooperative level.");
01437                 }
01438 
01439                 if (!display_modes) {
01440 
01441                         // Get all available video modes and sort them.
01442                         num_display_modes = 0;
01443                         pDD->EnumDisplayModes(0, NULL, this, EnumModesCallback);
01444                         qsort(display_modes, num_display_modes, sizeof(DisplayMode), CompareModes);
01445                 }
01446 
01447                 // Set the video mode.
01448                 const char *display_mode = ThePrefs.DisplayMode;
01449                 if (display_mode[0] == '\0' ||
01450                     stricmp(display_mode, "Default") == 0)
01451                         display_mode = NULL;
01452                 if (display_mode) {
01453                         int x, y, depth = 8;
01454                         if (sscanf(display_mode, "%dx%dx%d", &x, &y, &depth) < 2)
01455                                 return Fail("Invalid command line mode format.");
01456                         ddrval = pDD->SetDisplayMode(x, y, depth);
01457                         if (ddrval != DD_OK) {
01458                                 DebugResult("SetDisplayMode failed", ddrval);
01459                                 return Fail("Failed to set the video mode.");
01460                         }
01461                 }
01462                 else {
01463                         for (int i = 0; i < num_display_modes; i++) {
01464                                 DisplayMode *mode = &display_modes[i];
01465                                 if (mode->x < view_x || mode->y < view_y)
01466                                         continue;
01467                                 ddrval = pDD->SetDisplayMode(mode->x, mode->y, mode->depth);
01468                                 if (ddrval == DD_OK)
01469                                         break;
01470                         }
01471                         if (i == num_display_modes)
01472                                 return Fail("Failed to find a suitable video mode.");
01473                 }
01474         }
01475         else {
01476 
01477                 // Set normal mode.
01478                 ddrval = pDD->SetCooperativeLevel(hwnd, DDSCL_NORMAL);
01479                 if (ddrval != DD_OK)
01480                         return Fail("Failed to set normal cooperative level.");
01481         }
01482 
01483         // Create the primary surface with one back buffer.
01484         DDSURFACEDESC ddsd;
01485         memset(&ddsd, 0, sizeof(ddsd));
01486         ddsd.dwSize = sizeof(ddsd);
01487         ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
01488         ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
01489         ddsd.dwBackBufferCount = 1;
01490         ddrval = pDD->CreateSurface(&ddsd, &pPrimary, NULL);
01491         if (ddrval != DD_OK) {
01492                 memset(&ddsd, 0, sizeof(ddsd));
01493                 ddsd.dwSize = sizeof(ddsd);
01494                 ddsd.dwFlags = DDSD_CAPS;
01495                 ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
01496                 ddrval = pDD->CreateSurface(&ddsd, &pPrimary, NULL);
01497                 if (ddrval != DD_OK)
01498                         return Fail("Failed to create primary surface.");
01499         }
01500 
01501         if (ddsd.dwBackBufferCount == 1) {
01502                 DDSCAPS ddscaps;
01503                 ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
01504                 ddrval = pPrimary->GetAttachedSurface(&ddscaps, &pBack);
01505                 if (ddrval != DD_OK)
01506                         return Fail("Failed to get attached surface.");
01507         }
01508 
01509         // Create work surface.  It displays correctly without
01510         // this but doesn't handle clipping.  We would have to
01511         // do that ourselves.
01512         memset(&ddsd, 0, sizeof(ddsd));
01513         ddsd.dwSize = sizeof(ddsd);
01514         ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
01515         ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
01516         if (ThePrefs.SystemMemory)
01517                 ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
01518         ddsd.dwHeight = DISPLAY_Y;
01519         ddsd.dwWidth = DISPLAY_X;
01520         ddrval = pDD->CreateSurface(&ddsd, &pWork, NULL);
01521         if (ddrval != DD_OK) {
01522                 //return Fail("Failed to create work surface.");
01523                 Debug("cannot create work surface: %d\n", ddrval);
01524         }
01525         if (pWork) {
01526                 pWork->GetCaps(&ddsd.ddsCaps);
01527                 if (ddsd.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY)
01528                         Debug("Work surface is in video memory.\n");
01529                 else if (ddsd.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY)
01530                         Debug("Work surface is in system memory.\n");
01531                 else
01532                         Debug("Work surface is in unknown memory.\n");
01533         }
01534 
01535         if (!full_screen) {
01536 
01537                 // Create clipper object.
01538                 ddrval = pDD->CreateClipper(0, &pClipper, NULL);
01539                 if (ddrval != DD_OK)
01540                         return Fail("Failed to create direct draw clipper.");
01541                 ddrval = pClipper->SetHWnd(0, hwnd);
01542                 if (ddrval != DD_OK)
01543                         return Fail("Failed setting clipper window handle.");
01544                 ddrval = pPrimary->SetClipper(pClipper);
01545                 if (ddrval != DD_OK)
01546                         return Fail("Failed setting primary surface clipper.");
01547         }
01548 
01549         // We need to use a 256 color palette otherwise we get an
01550         // invalid pixel format error when trying to set the palette
01551         // on a windowed surface.
01552         PALETTEENTRY ape[256];
01553         HDC hdc = GetDC(NULL);
01554         int entries = GetSystemPaletteEntries(hdc, 0, 256, ape);
01555         ReleaseDC(NULL, hdc);
01556         if (entries != 256) {
01557                 Debug("failed to get 256 system palette entries: %d (%d)\n",
01558                         entries, GetLastError());
01559 
01560                 // Build a 332 palette as the default.  This makes it easy for
01561                 // other apps to find colors when they aren't the foreground.
01562                 for (int i = 0; i < 256; i++) {
01563                         ape[i].peRed   = (BYTE)(((i >> 5) & 0x07) * 255 / 7);
01564                         ape[i].peGreen = (BYTE)(((i >> 2) & 0x07) * 255 / 7);
01565                         ape[i].peBlue  = (BYTE)(((i >> 0) & 0x03) * 255 / 3);
01566                         ape[i].peFlags = 0;
01567                 }
01568         }
01569 
01570         // Now override the first 16 entries with the C64 colors.
01571         // If we were really obsessive we could try to find the
01572         // nearest matches and replace them instead.
01573         for (int i = 0; i < 16; i++) {
01574                 ape[i].peRed = palette_red[i];
01575                 ape[i].peGreen = palette_green[i];
01576                 ape[i].peBlue = palette_blue[i];
01577                 ape[i].peFlags = 0;
01578         }
01579 
01580         // Create the palette and set it on all surfaces.
01581         ddrval = pDD->CreatePalette(DDPCAPS_8BIT, ape, &pPalette, NULL);
01582         if (ddrval != DD_OK)
01583                 return Fail("Failed to create palette.");
01584         if (!SetPalettes())
01585                 return Fail("Failed to set palettes.");
01586         if (!BuildColorTable())
01587                 return Fail("Failed to build color table.");
01588 
01589         // Start with a clean slate.
01590         if (!EraseSurfaces()) {
01591                 // Some display drivers have bugs, I guess.
01592                 // What's a little problem erasing gonna hurt.
01593 #if 0
01594                 return Fail("Failed to erase surfaces.");
01595 #endif
01596         }
01597 
01598 
01599         return TRUE;
01600 }
01601 
01602 BOOL C64Display::ResumeDirectDraw()
01603 {
01604         if (!RestoreSurfaces())
01605                 ResetDirectDraw();
01606 
01607         return TRUE;
01608 }
01609 
01610 BOOL C64Display::ResetDirectDraw()
01611 {
01612         Pause();
01613         StopDirectDraw();
01614         StartDirectDraw();
01615         Resume();
01616         if (paused)
01617                 Update();
01618 
01619         return TRUE;
01620 }
01621 
01622 BOOL C64Display::StopDirectDraw()
01623 {
01624         if (pDD != NULL) {
01625                 if (pClipper != NULL) {
01626                         pClipper->Release();
01627                         pClipper = NULL;
01628                 }
01629                 if (pWork != NULL) {
01630                         pWork->Release();
01631                         pWork = NULL;
01632                 }
01633                 if (pBack != NULL) {
01634                         pBack->Release();
01635                         pBack = NULL;
01636                 }
01637                 if (pPrimary != NULL) {
01638                         pPrimary->Release();
01639                         pPrimary = NULL;
01640                 }
01641                 if (pPalette != NULL) {
01642                         pPalette->Release();
01643                         pPalette = NULL;
01644                 }
01645                 pDD->RestoreDisplayMode();
01646                 pDD->Release();
01647                 pDD = NULL;
01648         }
01649 
01650         // Restore windowing state, window position, etc.
01651         RestoreWindow();
01652 
01653         return TRUE;
01654 }
01655 
01656 
01657 /*
01658  * This function is called if the initialization function fails
01659  */
01660 BOOL C64Display::Fail(const char *error)
01661 {
01662         Debug(error);
01663         Debug("\n");
01664         strcpy(failure_message, error);
01665         return FALSE;
01666 }
01667 
01668 
01669 BOOL C64Display::SetPalettes()
01670 {
01671         // Only try to set palettes when in 256 color mode.
01672         HDC hdc = GetDC(NULL);
01673         int depth = GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc, BITSPIXEL);
01674         ReleaseDC(NULL, hdc);
01675         if (depth != 8)
01676                 return TRUE;
01677 
01678         // Set palette on primary surface.
01679         HRESULT ddrval = pPrimary->SetPalette(pPalette);
01680         if (ddrval == DDERR_SURFACELOST) {
01681                 pPrimary->Restore();
01682                 ddrval = pPrimary->SetPalette(pPalette);
01683         }
01684         if (ddrval == DDERR_NOT8BITCOLOR)
01685                 return TRUE;
01686         if (ddrval != DD_OK) {
01687                 DebugResult("failed to set palette on primary", ddrval);
01688                 return FALSE;
01689         }
01690 
01691         // Set palette on back surface.
01692         if (pBack) {
01693                 FlipSurfaces();
01694                 pPrimary->SetPalette(pPalette);
01695                 if (ddrval == DDERR_SURFACELOST) {
01696                         pPrimary->Restore();
01697                         ddrval = pPrimary->SetPalette(pPalette);
01698                 }
01699                 if (ddrval != DD_OK) {
01700                         DebugResult("failed to set palette on back", ddrval);
01701                         return FALSE;
01702                 }
01703         }
01704 
01705         // Set palette on work surface.
01706         if (pWork) {
01707                 ddrval = pWork->SetPalette(pPalette);
01708                 if (ddrval == DDERR_SURFACELOST) {
01709                         pWork->Restore();
01710                         ddrval = pWork->SetPalette(pPalette);
01711                 }
01712                 if (ddrval != DD_OK) {
01713                         DebugResult("failed to set palette on work", ddrval);
01714                         return FALSE;
01715                 }
01716         }
01717 
01718         return TRUE;
01719 }
01720 
01721 BOOL C64Display::BuildColorTable()
01722 {
01723         if (!pPrimary)
01724                 return FALSE;
01725 
01726         // Determine the physical colors corresponding to the 16 C64 colors.
01727         for (int j = 0; j < 16; j++) {
01728 
01729                 // Compute the true color in RGB format.
01730                 int red = palette_red[j];
01731                 int green = palette_green[j];
01732                 int blue = palette_blue[j];
01733                 COLORREF rgb = RGB(red, green, blue);
01734 
01735                 // Set pixel(0, 0) to that value.
01736                 LPDIRECTDRAWSURFACE pSurface = pBack ? pBack : pPrimary;
01737                 HDC hdc;
01738                 if (pSurface->GetDC(&hdc) != DD_OK)
01739                         return Fail("Failed getting direct draw device context.");
01740                 COLORREF new_rgb = SetPixel(hdc, 0, 0, PALETTERGB(red, green, blue));
01741                 Debug("new: %.8x, old %.8x\n", new_rgb, rgb);
01742                 pSurface->ReleaseDC(hdc);
01743 
01744                 // Read the physical color from linear memory.
01745                 DDSURFACEDESC ddsd;
01746                 ddsd.dwSize = sizeof(ddsd);
01747                 HRESULT ddrval;
01748                 for (;;) {
01749                         ddrval = pSurface->Lock(NULL, &ddsd, 0, NULL);
01750                         if (ddrval != DDERR_WASSTILLDRAWING)
01751                                 break;
01752                 }
01753                 if (ddrval != DD_OK)
01754                         return Fail("Failed to lock surface.");
01755                 colors_depth = ddsd.ddpfPixelFormat.dwRGBBitCount;
01756                 DWORD dw = *(DWORD *) ddsd.lpSurface;
01757                 Debug("DWORD = %.8x, depth = %d\n", dw, colors_depth);
01758                 if (colors_depth != 32)
01759                         dw &= (1 << colors_depth) - 1;
01760                 pSurface->Unlock(NULL);
01761 
01762                 // Store the physical color in the colors array.
01763                 colors[j] = dw;
01764                 Debug("colors[%d] = %d\n", j, dw);
01765         }
01766 
01767         // Replicate the physical colors into the rest of the color array.
01768         for (int k = 16; k < 256; k++)
01769                 colors[k] = colors[k & 0x0f];
01770 
01771         // Tell the VIC all about it;
01772         if (!in_constructor)
01773                 TheC64->TheVIC->ReInitColors();
01774 
01775         return TRUE;
01776 }
01777 
01778 /*
01779  *  Redraw bitmap using double buffering when possible.
01780  */
01781 
01782 void C64Display::Update()
01783 {
01784         TIMESCOPE(ts0, "Update");
01785 
01786         //Debug("Display::Update\n");
01787 
01788         if (full_screen && !active)
01789                 return;
01790 
01791         if (!pPrimary)
01792                 return;
01793 
01794 #ifdef WORKBUFFER_BITMAP
01795         // Special case for using the workbuffer as a bitmap.
01796         if (workbuffer_bitmap) {
01797                 if (workbuffer_locked) {
01798                         pWork->Unlock(NULL);
01799                         workbuffer_locked = FALSE;
01800                 }
01801                 RECT rc;
01802                 rc.left = (DISPLAY_X - view_x)/2;
01803                 rc.top = (DISPLAY_Y - view_y)/2 - 1;
01804                 if (rc.top < 0)
01805                         rc.top = 0;
01806                 rc.right = rc.left + view_x;
01807                 rc.bottom = rc.top + view_y;
01808                 CopySurface(rc);
01809                 draw_leds();
01810                 return;
01811 
01812         }
01813 #endif
01814 
01815         // Work on the backing surface unless there isn't one.
01816         // We'll flip to it when we're done.
01817         LPDIRECTDRAWSURFACE pSurface = pBack ? pBack : pPrimary;
01818 
01819         // Use a work surface when we have to:
01820         // * when always copy is on
01821         // * when possibly clipped
01822         // * when streching
01823         // * when partially offscreen
01824 
01825         if (!full_screen && pWork) {
01826                 if (ThePrefs.AlwaysCopy || !active || paused ||
01827 #if 0
01828                     GetForegroundWindow() != hwnd ||
01829 #endif
01830                     rcWindow.right - rcWindow.left != view_x ||
01831                     rcWindow.bottom - rcWindow.top != view_y ||
01832                     rcWindow.left < rcScreen.left ||
01833                     rcWindow.top < rcScreen.top ||
01834                     rcWindow.right > rcScreen.right ||
01835                     rcWindow.bottom > rcScreen.bottom) {
01836                         pSurface = pWork;
01837                         //Debug("using work surface\n");
01838                 }
01839         }
01840 
01841         // Lock the surface.
01842         DDSURFACEDESC ddsd;
01843         ddsd.dwSize = sizeof(ddsd);
01844 
01845         for (;;) {
01846                 HRESULT ddrval = pSurface->Lock(NULL, &ddsd, 0, NULL);
01847                 if (ddrval == DD_OK)
01848                         break;
01849                 if (ddrval == DDERR_SURFACELOST) {
01850                         Debug("surface lost\n");
01851                         if (pSurface == pWork)
01852                                 ddrval = pWork->Restore();
01853                         else
01854                                 ddrval = pPrimary->Restore();
01855                         if (ddrval != DD_OK) {
01856                                 DebugResult("surface Restore failed", ddrval);
01857                                 return;
01858                         }
01859                         EraseSurfaces();
01860                         BuildColorTable();
01861                 }
01862                 else if (ddrval != DDERR_WASSTILLDRAWING) {
01863                         if (pWork && pSurface != pWork)
01864                                 pSurface = pWork;
01865                         else {
01866                                 DebugResult("surface Lock failed", ddrval);
01867                                 return;
01868                         }
01869                 }
01870                 Debug("was still drawing\n");
01871         }
01872 
01873         // Compute the optimal placement of our window depending on
01874         // the screen dimensions.
01875         int x_off, y_off;
01876         int x_beg, y_beg;
01877         int x_siz, y_siz;
01878 
01879         // XXX: Do these calculations only when the parameters change.
01880         if (full_screen) {
01881                 if (rcWindow.right >= view_x) {
01882                         x_off = (rcWindow.right - view_x)/2;
01883                         x_beg = (DISPLAY_X - view_x)/2;
01884                         x_siz = view_x;
01885                 }
01886                 else {
01887                         x_off = 0;
01888                         x_beg = (DISPLAY_X - rcWindow.right)/2;
01889                         x_siz = rcWindow.right;
01890                 }
01891                 if (rcWindow.bottom >= view_y) {
01892                         y_off = (rcWindow.bottom - view_y)/2;
01893                         y_beg = (DISPLAY_Y - view_y)/2 - 1;
01894                         y_siz = view_y;
01895                 }
01896                 else {
01897                         y_off = 0;
01898                         y_beg = (DISPLAY_Y - rcWindow.bottom)/2 - 1;
01899                         y_siz = rcWindow.bottom;
01900                 }
01901         }
01902         else {
01903                 if (pSurface == pWork) {
01904                         x_off = 0;
01905                         y_off = 0;
01906                 }
01907                 else {
01908                         x_off = rcWindow.left;
01909                         y_off = rcWindow.top;
01910                 }
01911                 x_beg = (DISPLAY_X - view_x)/2;
01912                 y_beg = (DISPLAY_Y - view_y)/2 - 1;
01913                 x_siz = view_x;
01914                 y_siz = view_y;
01915         }
01916         if (y_beg < 0)
01917                 y_beg = 0;
01918 
01919         // Translate chunky colors into the surface's linear memory.
01920         int pitch = ddsd.lPitch;
01921         int depth = ddsd.ddpfPixelFormat.dwRGBBitCount;
01922         BYTE *surface = (BYTE *) ddsd.lpSurface + pitch*y_off + x_off*(depth/8);
01923         BYTE *chunky = chunky_buf + DISPLAY_X*y_beg + x_beg;
01924 
01925         // These tight loops are where the display speed action is at.
01926         // Note that MSVC optimizes out the mulitiplications and
01927         // reverses the direction of the loop counters automatically.
01928         if (depth == 8) {
01929 
01930                 // Since the VIC is using our palette entries we just copy.
01931                 //TIMESCOPE(ts1, "hand blt 8");
01932                 BYTE *scanline = surface;
01933                 BYTE *scanbuf = chunky;
01934                 //Debug("scanline = %8p, scanbuf = %8p\n", scanline, scanbuf);
01935                 for (int j = 0; j < y_siz; j++) {
01936                         memcpy(scanline, scanbuf, x_siz);
01937                         scanline += pitch;
01938                         scanbuf += DISPLAY_X;
01939                 }
01940         }
01941         else if (depth == 16) {
01942                 //TIMESCOPE(ts1, "hand blt 16");
01943                 for (int j = 0; j < y_siz; j++) {
01944                         WORD *scanline = (WORD *) (surface + pitch*j);
01945                         BYTE *scanbuf = chunky + +DISPLAY_X*j;
01946                         for (int i = 0; i < x_siz; i++)
01947                                 *scanline++ = (WORD) colors[*scanbuf++];
01948                 }
01949         }
01950         else if (depth == 24) {
01951 
01952                 // XXX: Works for little-endian only.
01953                 //TIMESCOPE(ts1, "hand blt 24");
01954                 for (int j = 0; j < y_siz; j++) {
01955                         BYTE *scanline = surface + pitch*j;
01956                         BYTE *scanbuf = chunky + +DISPLAY_X*j;
01957                         for (int i = 0; i < x_siz; i++) {
01958                                 *((DWORD *) scanline) = colors[*scanbuf++];
01959                                 scanline += 3;
01960                         }
01961                 }
01962         }
01963         else if (depth == 32) {
01964                 //TIMESCOPE(ts1, "hand blt 32");
01965                 for (int j = 0; j < y_siz; j++) {
01966                         DWORD *scanline = (DWORD *) (surface + pitch*j);
01967                         BYTE *scanbuf = chunky + +DISPLAY_X*j;
01968                         for (int i = 0; i < x_siz; i++)
01969                                 *scanline++ = colors[*scanbuf++];
01970                 }
01971         }
01972         else
01973                 Debug("PixelCount not 8, 16, 24, or 32\n");
01974 
01975         // Unlock the surface.
01976         HRESULT ddrval = pSurface->Unlock(NULL);
01977         if (ddrval != DD_OK)
01978                 Debug("DirectDrawSurface::Unlock failed\n");
01979 
01980         // Now flip from the primary surface to the backing surface.
01981         if (pSurface == pWork)
01982                 CopySurface(rcWork);
01983         else if (full_screen && pBack)
01984                 FlipSurfaces();
01985 
01986         // Update drive LEDs
01987         draw_leds();
01988 }
01989 
01990 
01991 BOOL C64Display::CopySurface(RECT &rcWork)
01992 {
01993         // Copy work surface to primary.
01994         for (;;) {
01995                 HRESULT ddrval = pPrimary->Blt(&rcWindow, pWork, &rcWork, DDBLT_WAIT, NULL);
01996                 if (ddrval == DD_OK)
01997                         break;
01998                 if (ddrval == DDERR_SURFACELOST) {
01999                         ddrval = pPrimary->Restore();
02000                         if (ddrval != DD_OK) {
02001                                 DebugResult("CopySurface Restore failed", ddrval);
02002                                 return FALSE;
02003                         }
02004                 }
02005                 else if (ddrval != DDERR_WASSTILLDRAWING) {
02006                         DebugResult("CopySurface Blt failed", ddrval);
02007                         return FALSE;
02008                 }
02009         }
02010         return TRUE;
02011 }
02012 
02013 BOOL C64Display::FlipSurfaces()
02014 {
02015         // Flip buffers.
02016         for (;;) {
02017                 HRESULT ddrval = pPrimary->Flip(NULL, 0);
02018                 if (ddrval == DD_OK)
02019                         break;
02020                 if (ddrval == DDERR_SURFACELOST) {
02021                         ddrval = pPrimary->Restore();
02022                         if (ddrval != DD_OK) {
02023                                 Debug("Restore failed\n");
02024                                 return FALSE;
02025                         }
02026                 }
02027                 else if (ddrval != DDERR_WASSTILLDRAWING)
02028                         return FALSE;
02029         }
02030         return TRUE;
02031 }
02032 
02033 BOOL C64Display::EraseSurfaces()
02034 {
02035         DDBLTFX ddbltfx;
02036         ddbltfx.dwSize = sizeof(ddbltfx);
02037         ddbltfx.dwFillColor = 0;
02038 
02039         // Erase the backing surface.
02040         for (;;) {
02041                 if (!pBack)
02042                         break;
02043                 HRESULT ddrval = pBack->Blt(&rcWindow, NULL, NULL, DDBLT_COLORFILL, &ddbltfx);
02044 
02045                 if (ddrval == DD_OK)
02046                         break;
02047 
02048                 if (ddrval == DDERR_SURFACELOST) {
02049                         ddrval = pPrimary->Restore();
02050                         if (ddrval != DD_OK) {
02051                                 DebugResult("Restore primary failed", ddrval);
02052                                 return FALSE;
02053                         }
02054                 }
02055                 else if (ddrval != DDERR_WASSTILLDRAWING) {
02056                         DebugResult("Blt erase back failed", ddrval);
02057                         return FALSE;
02058                 }
02059         }
02060 
02061         // Erase the primary surface.
02062         for (;;) {
02063                 HRESULT ddrval = pPrimary->Blt(&rcWindow, NULL, NULL, DDBLT_COLORFILL, &ddbltfx);
02064 
02065                 if (ddrval == DD_OK)
02066                         break;
02067 
02068                 if (ddrval == DDERR_SURFACELOST) {
02069                         ddrval = pPrimary->Restore();
02070                         if (ddrval != DD_OK) {
02071                                 DebugResult("Restore primary failed", ddrval);
02072                                 return FALSE;
02073                         }
02074                 }
02075                 else if (ddrval != DDERR_WASSTILLDRAWING) {
02076                         DebugResult("Blt erase primary failed", ddrval);
02077                         return FALSE;
02078                 }
02079         }
02080 
02081         return TRUE;
02082 }
02083 
02084 BOOL C64Display::RestoreSurfaces()
02085 {
02086         if (pPrimary) {
02087                 HRESULT ddrval = pPrimary->Restore();
02088                 if (ddrval != DD_OK)
02089                         return FALSE;
02090         }
02091 
02092         if (pWork) {
02093                 HRESULT ddrval = pWork->Restore();
02094                 if (ddrval != DD_OK)
02095                         return FALSE;
02096         }
02097 
02098         return TRUE;
02099 }
02100 
02101 /*
02102  *  Draw LED bar at the bottom of the window
02103  */
02104 
02105 void C64Display::draw_led_bar()
02106 {
02107         if (full_screen || !ThePrefs.ShowLEDs)
02108                 return;
02109 
02110         HDC hdc = GetDC(hwnd);
02111         RECT rc;
02112         GetClientRect(hwnd, &rc);
02113         rc.top = rc.bottom - led_rows;
02114         FillRect(hdc, &rc, led_brush);
02115         if (rc.right - rc.left > view_x)
02116                 rc.left = rc.right - view_x;
02117         SelectObject(hdc, led_font);
02118         SetTextAlign(hdc, TA_TOP | TA_RIGHT);
02119         SetBkMode(hdc, TRANSPARENT);
02120         SetTextColor(hdc, (COLORREF) GetSysColor(COLOR_MENUTEXT));
02121         for (int i = 0; i < 4; i++) {
02122                 char str[128];
02123                 if (rc.right - rc.left < view_x)
02124                         sprintf(str, "%d", i + 8);
02125                 else
02126                         sprintf(str, "Drive %d", i + 8);
02127                 RECT led;
02128                 led_rect(i, rc, led);
02129                 SelectObject(hdc, led_shadow);
02130                 MoveToEx(hdc, led.left - 1, led.bottom - 1, NULL);
02131                 LineTo(hdc, led.left - 1, led.top - 1);
02132                 LineTo(hdc, led.right, led.top - 1);
02133                 SelectObject(hdc, led_highlight);
02134                 LineTo(hdc, led.right, led.bottom);
02135                 LineTo(hdc, led.left - 2, led.bottom);
02136                 TextOut(hdc, led.left - 4, rc.top + 2, str, strlen(str));
02137         }
02138         ReleaseDC(hwnd, hdc);
02139         draw_leds(TRUE);
02140 }
02141 
02142 /*
02143  *  Draw one LED
02144  */
02145 
02146 void C64Display::draw_leds(BOOL force)
02147 {
02148         if (full_screen || !ThePrefs.ShowLEDs)
02149                 return;
02150 
02151         if (!force) {
02152                 int i;
02153                 for (i = 0; i < 4; i++) {
02154                         if (led_state[i] != old_led_state[i])
02155                                 break;
02156                 }
02157                 if (i == 4)
02158                         return;
02159         }
02160 
02161         HDC hdc = GetDC(hwnd);
02162         RECT rc;
02163         GetClientRect(hwnd, &rc);
02164         rc.top = rc.bottom - led_rows;
02165         if (rc.right - rc.left > view_x)
02166                 rc.left = rc.right - view_x;
02167         for (int i = 0; i < 4; i++) {
02168                 old_led_state[i] = led_state[i];
02169                 HBRUSH brush;
02170                 switch (led_state[i]) {
02171                 case LED_OFF: brush = off_brush; break;
02172                 case LED_ERROR_OFF: brush = error_off_brush; break;
02173                 case LED_ON: brush = on_brush; break;
02174                 case LED_ERROR_ON: brush = error_on_brush; break;
02175                 }
02176                 RECT led;
02177                 led_rect(i, rc, led);
02178                 FillRect(hdc, &led, brush);
02179         }
02180         ReleaseDC(hwnd, hdc);
02181 }
02182 
02183 void C64Display::led_rect(int n, RECT &rc, RECT &led)
02184 {
02185         int x = rc.left + (rc.right - rc.left)*(n + 2)/5 - 20;
02186         int y = rc.top + 2 + led_rows/3;
02187         SetRect(&led, x, y, x + 13, y + led_rows/3);
02188 }
02189 
02190 void C64Display::InsertNextDisk()
02191 {
02192         if (strlen(ThePrefs.DrivePath[0]) > 4) {
02193                 char str[256];
02194                 strcpy(str, ThePrefs.DrivePath[0]);
02195                 char *p = str + strlen(str) - 5;
02196 
02197                 // If path matches "*.?64", increment character before the '.'
02198                 if (p[1] == '.' && p[3] == '6' && p[4] == '4') {
02199                         p[0]++;
02200 
02201                         // If no such file exists, set character before the '.' to '1', 'a' or 'A'
02202                         FILE *file;
02203                         if ((file = fopen(str, "rb")) == NULL) {
02204                                 if (isdigit(p[0]))
02205                                         p[0] = '1';
02206                                 else if (isupper(p[0]))
02207                                         p[0] = 'A';
02208                                 else
02209                                         p[0] = 'a';
02210                         } else
02211                                 fclose(file);
02212 
02213                         // Set new prefs
02214                         Pause();
02215                         Prefs *prefs = new Prefs(ThePrefs);
02216                         strcpy(prefs->DrivePath[0], str);
02217                         TheC64->NewPrefs(prefs);
02218                         ThePrefs = *prefs;
02219                         delete prefs;
02220                         Resume();
02221                 }
02222         }
02223 }
02224 
02225 BOOL C64Display::FileNameDialog(char *prefs_path, BOOL save)
02226 {
02227         char filename[256];
02228         strcpy(filename, prefs_path);
02229         OPENFILENAME ofn;
02230         memset(&ofn, 0, sizeof(ofn));
02231         ofn.lStructSize = sizeof(ofn);
02232         ofn.hwndOwner = hwnd;
02233         ofn.hInstance = hInstance;
02234         ofn.lpstrFilter =
02235                 "Preferences Files (*.fpr)\0*.fpr\0"
02236                 "All Files (*.*)\0*.*\0"
02237                 ;
02238         ofn.lpstrCustomFilter = NULL;
02239         ofn.nMaxCustFilter = 0;
02240         ofn.nFilterIndex = 1;
02241         ofn.lpstrFile = filename;
02242         ofn.nMaxFile = sizeof(filename);
02243         ofn.lpstrFileTitle = NULL;
02244         ofn.nMaxFileTitle = 0;
02245         ofn.lpstrInitialDir = NULL;
02246         ofn.lpstrTitle = NULL;
02247         ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_NOTESTFILECREATE |
02248                 OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR | OFN_SHAREAWARE;
02249         ofn.nFileOffset = 0;
02250         ofn.nFileExtension = 0;
02251         ofn.lpstrDefExt = "fpr";
02252         ofn.lpfnHook = NULL;
02253         ofn.lpTemplateName = NULL;
02254         BOOL result = save ? GetSaveFileName(&ofn) : GetOpenFileName(&ofn);
02255         if (result) {
02256                 char cwd[256];
02257                 GetCurrentDirectory(sizeof(cwd), cwd);
02258                 int cwd_len = strlen(cwd);
02259                 if (cwd_len > 0 && cwd[cwd_len - 1] != '\\') {
02260                         strcat(cwd, "\\");
02261                         cwd_len++;
02262                 }
02263                 if (strnicmp(filename, cwd, cwd_len) == 0)
02264                         strcpy(prefs_path, filename + cwd_len);
02265                 else
02266                         strcpy(prefs_path, filename);
02267         }
02268         return result;
02269 }
02270 
02271 void C64Display::WindowTitle()
02272 {
02273         // Show the program name, the current preferences file,
02274         // and the paused state or the speedometer.
02275         const char *prefs_path = TheApp->prefs_path;
02276         int prefs_path_length = strlen(prefs_path);
02277         if (prefs_path_length > 4 &&
02278             stricmp(prefs_path + prefs_path_length - 4, ".fpr") == 0)
02279                 prefs_path_length -= 4;
02280         const char *info = NULL;
02281         char tmp[128];
02282         if (waiting)
02283                 info = "PAUSED";
02284         else if (!ThePrefs.ShowLEDs && speed_index != 0) {
02285                 if (IsFrodoSC)
02286                         sprintf(tmp, "%.1f%%", speed_index);
02287                 else
02288                         sprintf(tmp, "%.0f%%", speed_index);
02289                 info = tmp;
02290         }
02291         const char *sep1 = info ? " (" : "";
02292         const char *sep2 = info ? ")" : "";
02293         char title[256];
02294         sprintf(title, "%s - %.*s%s%s%s", TITLE,
02295                 prefs_path_length, prefs_path, sep1, info ? info : "", sep2);
02296         SetWindowText(hwnd, title);
02297 }
02298 
02299 void C64Display::NewPrefs()
02300 {
02301         // Resize the window to the new viewport while preserving
02302         // as closely as possible the previous scaling factors.
02303         RECT rc;
02304         GetWindowRect(hwnd, &rc);
02305         int x_nc = rc.right - rc.left - (rcWindow.right - rcWindow.left);
02306         int y_nc = rc.bottom - rc.top - (rcWindow.bottom - rcWindow.top);
02307         if (show_leds)
02308                 y_nc -= led_rows;
02309         double x_scale = double(rcWindow.right - rcWindow.left)/view_x;
02310         double y_scale = double(rcWindow.bottom - rcWindow.top)/view_y;
02311         if (CalcViewPort() || show_leds != ThePrefs.ShowLEDs) {
02312                 show_leds = ThePrefs.ShowLEDs;
02313                 rc.right = int(rc.left + x_scale*view_x + x_nc);
02314                 rc.bottom = int(rc.top + y_scale*view_y + y_nc);
02315                 if (show_leds)
02316                         rc.bottom += led_rows;
02317                 ResizeWindow(WMSZ_BOTTOMRIGHT, &rc);
02318                 MoveWindow(hwnd, rc.left, rc.top,
02319                         rc.right - rc.left,
02320                         rc.bottom - rc.top, TRUE);
02321         }
02322 
02323         // The prefs filename might have changed.
02324         WindowTitle();
02325 }
02326 
02327 void C64Display::OfferSave()
02328 {
02329         if (ThePrefs == ThePrefsOnDisk)
02330                 return;
02331         const char *str = "Preferences have changed.\nSave preferences now?";
02332         int result = MessageBox(hwnd, str, "Frodo", MB_YESNO | MB_ICONQUESTION);
02333         if (result == IDYES)
02334                 ThePrefs.Save(TheApp->prefs_path);
02335 }
02336 
02337 void C64Display::Pause()
02338 {
02339         // It's not safe to call this from the contructor or destructor.
02340         if (in_constructor || in_destructor)
02341                 return;
02342 
02343         if (paused == 0)
02344                 TheC64->Pause();
02345         paused++;
02346 }
02347 
02348 void C64Display::Resume()
02349 {
02350         // It's not safe to call this from the contructor or destructor.
02351         if (in_constructor || in_destructor)
02352                 return;
02353 
02354         if (paused > 0) {
02355                 paused--;
02356                 if (!paused)
02357                         TheC64->Resume();
02358         }
02359         else
02360                 _ASSERTE(paused > 0);
02361 }
02362 
02363 void C64Display::Quit()
02364 {
02365         quit = 1;
02366         TheC64->Quit();
02367 }

Generated on Tue Feb 8 04:07:50 2005 for E32frodo by doxygen 1.3.3