| /* |
| Copyright 2005-2010 Intel Corporation. All Rights Reserved. |
| |
| This file is part of Threading Building Blocks. |
| |
| Threading Building Blocks is free software; you can redistribute it |
| and/or modify it under the terms of the GNU General Public License |
| version 2 as published by the Free Software Foundation. |
| |
| Threading Building Blocks is distributed in the hope that it will be |
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty |
| of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with Threading Building Blocks; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| |
| As a special exception, you may use this file as part of a free software |
| library without restriction. Specifically, if other files instantiate |
| templates or use macros or inline functions from this file, or you compile |
| this file and link it with other files to produce an executable, this |
| file does not by itself cause the resulting executable to be covered by |
| the GNU General Public License. This exception does not however |
| invalidate any other reasons why the executable file might be covered by |
| the GNU General Public License. |
| */ |
| |
| // common Windows parts |
| #include "winvideo.h" |
| // and another headers |
| #include <cassert> |
| #include <stdio.h> |
| #include <ddraw.h> |
| |
| #pragma comment(lib, "ddraw.lib") |
| #pragma comment(lib, "dxguid.lib") |
| |
| LPDIRECTDRAW7 g_pDD = NULL; // DirectDraw object |
| LPDIRECTDRAWSURFACE7 g_pDDSPrimary = NULL;// DirectDraw primary surface |
| LPDIRECTDRAWSURFACE7 g_pDDSBack = NULL; // DirectDraw back surface |
| LPDIRECTDRAWSURFACE7 g_pDDSOverlay = NULL;// DirectDraw overlay surface |
| LPDIRECTDRAWCLIPPER g_pClipper = NULL; // DirectDraw clipping struct |
| DDOVERLAYFX g_OverlayFX; // DirectDraw overlay effects struct |
| DDCAPS g_DDCaps; // DirectDraw hardware capabilities struct |
| DWORD g_OverlayFlags = 0; // DirectDraw overlay flags variable |
| DWORD g_dwXRatio, |
| g_dwYRatio; // The ratios between the src and dst rects |
| RECT g_rcSrc = {0, 0, 0, 0}, |
| g_rcDst = {0, 0, 0, 0}; |
| HANDLE g_hVSync; |
| |
| // check for new DX SDK (8 & 9) |
| #ifdef DDSCAPS_PRIMARYSURFACELEFT |
| #include <dxerr8.h> |
| #pragma comment(lib, "dxerr8.lib") |
| #else |
| // old SDK (7) |
| #include <d3dx.h> |
| #pragma comment(lib, "d3dx.lib") |
| #endif |
| |
| //! Create a dialog box and tell the user what went wrong |
| bool DisplayError(LPSTR lpstrErr, HRESULT hres) |
| { |
| static bool InError = false; |
| int retval = 0; |
| if (!InError) |
| { |
| InError = true; |
| #ifdef DDSCAPS_PRIMARYSURFACELEFT |
| const char *message = hres?DXGetErrorString8A(hres):0; |
| #else |
| char message[256]; if(hres) D3DXGetErrorString(hres, 256, message); |
| #endif |
| retval = MessageBoxA(g_hAppWnd, lpstrErr, hres?message:"Error!", MB_OK|MB_ICONERROR); |
| InError = false; |
| } |
| return false; |
| } |
| |
| //! Releases the overlay surface |
| void DestroyOverlay() |
| { |
| if (g_pClipper) |
| g_pClipper->Release(); |
| if (g_pDDSOverlay) { |
| g_pImg = 0; LPDIRECTDRAWSURFACE7 pDDSOverlay(g_pDDSOverlay); |
| g_pDDSOverlay = NULL; |
| YIELD_TO_THREAD(); |
| pDDSOverlay->Release(); // be sure nobody uses old value |
| } |
| } |
| |
| //! Releases the primary surface |
| void DestroyPrimary() |
| { |
| if (g_pDDSPrimary) |
| { |
| g_pDDSPrimary->Release(); |
| g_pDDSPrimary = NULL; |
| } |
| } |
| |
| //! Releases core DirectDraw objects |
| void DestroyDDraw() |
| { |
| DestroyPrimary(); |
| // Release the DDraw object |
| if (g_pDD) { |
| LPDIRECTDRAW7 pDD(g_pDD); // be sure nobody uses old value |
| g_pDD = NULL; Sleep(1); pDD->Release(); |
| } |
| } |
| |
| //! Checks and corrects all boundries for alignment and stretching |
| void CheckBoundries(void) |
| { |
| // Make sure the coordinates fulfill the stretching requirements. Often |
| // the hardware will require a certain ammount of stretching to do |
| // overlays. This stretch factor is held in dwMinOverlayStretch as the |
| // stretch factor multiplied by 1000 (to keep an accuracy of 3 decimal places). |
| if ((g_DDCaps.dwCaps & DDCAPS_OVERLAYSTRETCH) && (g_DDCaps.dwMinOverlayStretch) |
| && (g_dwXRatio < g_DDCaps.dwMinOverlayStretch)) |
| { |
| g_rcDst.right = 2 * GetSystemMetrics(SM_CXSIZEFRAME) + g_rcDst.left + (g_sizex |
| * (g_DDCaps.dwMinOverlayStretch + 1)) / 1000; |
| SetWindowTextA(g_hAppWnd, "Window is too small!"); |
| } |
| else if ((g_DDCaps.dwCaps & DDCAPS_OVERLAYSTRETCH) && (g_DDCaps.dwMaxOverlayStretch) |
| && (g_dwXRatio > g_DDCaps.dwMaxOverlayStretch)) |
| { |
| g_rcDst.right = 2 * GetSystemMetrics(SM_CXSIZEFRAME) + g_rcDst.left + (g_sizey |
| * (g_DDCaps.dwMaxOverlayStretch + 999)) / 1000; |
| SetWindowTextA(g_hAppWnd, "Window is too large!"); |
| } |
| else if(!g_video->calc_fps) SetWindowText(g_hAppWnd, g_video->title); |
| |
| // Recalculate the ratio's for the upcoming calculations |
| g_dwXRatio = (g_rcDst.right - g_rcDst.left) * 1000 / (g_rcSrc.right - g_rcSrc.left); |
| g_dwYRatio = (g_rcDst.bottom - g_rcDst.top) * 1000 / (g_rcSrc.bottom - g_rcSrc.top); |
| |
| // Check to make sure we're within the screen's boundries, if not then fix |
| // the problem by adjusting the source rectangle which we draw from. |
| if (g_rcDst.left < 0) |
| { |
| g_rcSrc.left = -g_rcDst.left * 1000 / g_dwXRatio; |
| g_rcDst.left = 0; |
| } |
| if (g_rcDst.right > GetSystemMetrics(SM_CXSCREEN)) |
| { |
| g_rcSrc.right = g_sizex - ((g_rcDst.right - GetSystemMetrics(SM_CXSCREEN)) * 1000 / g_dwXRatio); |
| g_rcDst.right = GetSystemMetrics(SM_CXSCREEN); |
| } |
| if (g_rcDst.bottom > GetSystemMetrics(SM_CYSCREEN)) |
| { |
| g_rcSrc.bottom = g_sizey - ((g_rcDst.bottom - GetSystemMetrics(SM_CYSCREEN)) * 1000 / g_dwYRatio); |
| g_rcDst.bottom = GetSystemMetrics(SM_CYSCREEN); |
| } |
| // I don't know how useful this is... but just in case someone can do it - here's the check. |
| if (g_rcDst.top < 0) |
| { |
| g_rcSrc.top = -g_rcDst.top * 1000 / g_dwYRatio; |
| g_rcDst.top = 0; |
| } |
| |
| // Make sure the coordinates fulfill the alignment requirements |
| // these expressions (x & -y) just do alignment by dropping low order bits... |
| // so to round up, we add first, then truncate. |
| if ((g_DDCaps.dwCaps & DDCAPS_ALIGNBOUNDARYSRC) && g_DDCaps.dwAlignBoundarySrc) |
| g_rcSrc.left = (g_rcSrc.left + g_DDCaps.dwAlignBoundarySrc / 2) & -(signed) |
| (g_DDCaps.dwAlignBoundarySrc); |
| if ((g_DDCaps.dwCaps & DDCAPS_ALIGNSIZESRC) && g_DDCaps.dwAlignSizeSrc) |
| g_rcSrc.right = g_rcSrc.left + (g_rcSrc.right - g_rcSrc.left + g_DDCaps.dwAlignSizeSrc |
| / 2) & -(signed) (g_DDCaps.dwAlignSizeSrc); |
| if ((g_DDCaps.dwCaps & DDCAPS_ALIGNBOUNDARYDEST) && g_DDCaps.dwAlignBoundaryDest) |
| g_rcDst.left = (g_rcDst.left + g_DDCaps.dwAlignBoundaryDest / 2) & -(signed) |
| (g_DDCaps.dwAlignBoundaryDest); |
| if ((g_DDCaps.dwCaps & DDCAPS_ALIGNSIZEDEST) && g_DDCaps.dwAlignSizeDest) |
| g_rcDst.right = g_rcDst.left + (g_rcDst.right - g_rcDst.left) & -(signed) (g_DDCaps.dwAlignSizeDest); |
| } |
| |
| //! Get translated by system color value |
| DWORD DDColorMatch(IDirectDrawSurface7 * pdds, COLORREF rgb) |
| { |
| COLORREF rgbT; |
| HDC hdc; |
| DWORD dw = CLR_INVALID; |
| DDSURFACEDESC2 ddsd; |
| HRESULT hres; |
| |
| // Use GDI SetPixel to color match for us |
| if (rgb != CLR_INVALID && pdds->GetDC(&hdc) == DD_OK) { |
| rgbT = GetPixel(hdc, 0, 0); // Save current pixel value |
| SetPixel(hdc, 0, 0, rgb); // Set our value |
| pdds->ReleaseDC(hdc); |
| } |
| // Now lock the surface so we can read back the converted color |
| ddsd.dwSize = sizeof(ddsd); |
| while ((hres = pdds->Lock(NULL, &ddsd, 0, NULL)) == DDERR_WASSTILLDRAWING) |
| YIELD_TO_THREAD(); |
| if (hres == DD_OK) { |
| dw = *(DWORD *) ddsd.lpSurface; // Get DWORD |
| if (ddsd.ddpfPixelFormat.dwRGBBitCount < 32) |
| dw &= (1 << ddsd.ddpfPixelFormat.dwRGBBitCount) - 1; // Mask it to bpp |
| pdds->Unlock(NULL); |
| } |
| else return DisplayError("Can't lock primary surface", hres); |
| // Now put the color that was there back. |
| if (rgb != CLR_INVALID && pdds->GetDC(&hdc) == DD_OK) { |
| SetPixel(hdc, 0, 0, rgbT); |
| pdds->ReleaseDC(hdc); |
| } |
| return dw; |
| } |
| |
| //! Load the bitmap and copy it to the overlay surface |
| bool DrawOverlay() |
| { |
| HRESULT hRet; // This is where we put return values from DirectDraw. |
| DDSURFACEDESC2 surfDesc; |
| // Setup structure |
| memset(&surfDesc, 0, sizeof(surfDesc)); surfDesc.dwSize = sizeof(surfDesc); |
| |
| hRet = g_pDDSOverlay->Lock(NULL, &surfDesc, DDLOCK_SURFACEMEMORYPTR | DDLOCK_NOSYSLOCK | DDLOCK_WRITEONLY, NULL); |
| if (hRet != DD_OK || surfDesc.lpSurface == NULL) |
| return DisplayError("Can't lock overlay surface", hRet); |
| else { |
| g_pImg = (unsigned int *)surfDesc.lpSurface; |
| //g_pDDSOverlay->Unlock(NULL); is not needed? |
| } |
| // Setup effects structure |
| memset(&g_OverlayFX, 0, sizeof(g_OverlayFX)); g_OverlayFX.dwSize = sizeof(g_OverlayFX); |
| // Setup overlay flags. |
| g_OverlayFlags = DDOVER_SHOW; |
| // Check for destination color keying capability |
| if ((g_DDCaps.dwCKeyCaps & DDCKEYCAPS_DESTOVERLAY) && ((g_DDCaps.dwCaps & DDCAPS_OVERLAYCANTCLIP) || (g_DDCaps.dwCKeyCaps & DDCKEYCAPS_NOCOSTOVERLAY) )) |
| { |
| // If so, we'll use it to clip the bitmap when other windows go on top |
| // of us. Just for the record - this color range for color keying (the |
| // high/low values) are not heavily supported right now, so for almost |
| // all cards, just use the same color for both. |
| g_OverlayFX.dckDestColorkey.dwColorSpaceLowValue = |
| g_OverlayFX.dckDestColorkey.dwColorSpaceHighValue = DDColorMatch(g_pDDSPrimary, RGBKEY); |
| g_OverlayFlags |= DDOVER_DDFX | DDOVER_KEYDESTOVERRIDE; |
| } else { |
| // If not, we'll setup a clipper for the window. This will fix the |
| // problem on a few video cards - but the ones that don't shouldn't care. |
| hRet = g_pDD->CreateClipper(0, &g_pClipper, NULL); |
| if (hRet != DD_OK) |
| return DisplayError("Can't create clipper", hRet); |
| hRet = g_pClipper->SetHWnd(0, g_hAppWnd); |
| if (hRet != DD_OK) |
| return DisplayError("Can't attach clipper", hRet); |
| hRet = g_pDDSPrimary->SetClipper(g_pClipper); |
| if (hRet != DD_OK) |
| return DisplayError("Can't set clipper", hRet); |
| } |
| return true; |
| } |
| |
| //! Init the primary surface |
| bool DDPrimaryInit() |
| { |
| HRESULT hRet; |
| DDSURFACEDESC2 ddsd; // A surface description structure |
| |
| // Create the primary surface. The primary surface is the full screen - |
| // since we're a windowed app - we'll just write to the portion of the |
| // screen within our window. |
| memset(&ddsd, 0, sizeof(ddsd)); // Set all fields of struct to 0 and set .dwSize to |
| ddsd.dwSize = sizeof(ddsd); // Sizeof the variable - these two steps required for most DDraw structs |
| ddsd.dwFlags = DDSD_CAPS; // Set flags for variables we're using... |
| ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; // Set the variables we said we would in dwFlags |
| hRet = g_pDD->CreateSurface(&ddsd, &g_pDDSPrimary, NULL); |
| if (hRet != DD_OK) |
| return DisplayError("Can't create primary surface", hRet); |
| return true; |
| } |
| |
| //! Init DirectDraw Stuff |
| bool DDInit() |
| { |
| HRESULT hRet; |
| g_rcSrc.right = g_sizex; |
| g_rcSrc.bottom = g_sizey; |
| |
| hRet = DirectDrawCreateEx(NULL, (VOID**)&g_pDD, IID_IDirectDraw7, NULL); |
| if (hRet != DD_OK) |
| return DisplayError("Can't create DirectDraw7 instance", hRet); |
| |
| // Set cooperation level with other windows to be normal (ie. not full screen) |
| // You MUST set the cooperation level to be SOMETHING, for windowed apps use |
| // DDSCL_NORMAL, for full screen use: DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN. |
| hRet = g_pDD->SetCooperativeLevel(g_hAppWnd, DDSCL_NORMAL); |
| if (hRet != DD_OK) |
| return DisplayError("Can't set cooperative level", hRet); |
| return DDPrimaryInit(); |
| } |
| |
| //! Setup the overlay object |
| bool DDOverlayInit() |
| { |
| // Get hardware's CAPabilitieS |
| memset(&g_DDCaps, 0, sizeof(g_DDCaps)); |
| g_DDCaps.dwSize = sizeof(g_DDCaps); |
| if (g_pDD->GetCaps(&g_DDCaps, 0)) |
| return DisplayError("Can't get capabilities"); |
| |
| // Make sure it supports overlays |
| if (!(g_DDCaps.dwCaps & DDCAPS_OVERLAY)) |
| return DisplayError("Hardware doesn't support overlays"); |
| |
| //DO NOT Make sure it supports stretching (scaling) |
| //if (!(g_DDCaps.dwCaps & DDCAPS_OVERLAYSTRETCH)) return false; |
| |
| DDSURFACEDESC2 ddsd; // DirectDraw surface descriptor |
| HRESULT hRet; // I'm not even going to try... |
| // The pixel formats that we want the surface to be in |
| DDPIXELFORMAT ddpfOverlayFormats[] = { |
| {sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 32, 0xFF0000, 0x0FF00, 0x0000FF, 0}, // 32-bit RGB |
| {sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16, 0x007C00, 0x003e0, 0x00001F, 0}, // 16-bit RGB 5:5:5 |
| {sizeof(DDPIXELFORMAT), DDPF_RGB, 0, 16, 0x00F800, 0x007e0, 0x00001F, 0}, // 16-bit RGB 5:6:5 |
| {sizeof(DDPIXELFORMAT), DDPF_FOURCC, mmioFOURCC('U','Y','V','Y'), 16, 0, 0, 0, 0}, // UYVY |
| {sizeof(DDPIXELFORMAT), DDPF_FOURCC, mmioFOURCC('Y','4','2','2'), 16, 0, 0, 0, 0}, // the same as UYVY |
| {sizeof(DDPIXELFORMAT), DDPF_FOURCC, mmioFOURCC('Y','U','Y','2'), 16, 0, 0, 0, 0}, // YUY2 is unsupported color-space here |
| {0}}; |
| |
| // Setup the overlay surface's attributes in the surface descriptor |
| memset(&ddsd, 0, sizeof(ddsd)); |
| ddsd.dwSize = sizeof(ddsd); |
| ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY | g_DDCaps.ddsCaps.dwCaps&DDSCAPS_VIDEOMEMORY; |
| ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; |
| ddsd.dwBackBufferCount = 0; |
| ddsd.dwWidth = g_sizex; |
| ddsd.dwHeight = g_sizey; |
| for(int format = 0; ddpfOverlayFormats[format].dwSize; format++) { |
| ddsd.ddpfPixelFormat = ddpfOverlayFormats[format]; |
| // Attempt to create the surface with theses settings |
| hRet = g_pDD->CreateSurface(&ddsd, &g_pDDSOverlay, NULL); |
| if(hRet == DD_OK) break; |
| } |
| if (hRet != DD_OK) |
| return DisplayError("Can't create appropriate overlay surface", hRet); |
| return true; |
| } |
| |
| inline void mouse(int k, LPARAM lParam) |
| { |
| int x = (int)LOWORD(lParam), y = (int)HIWORD(lParam); |
| g_video->on_mouse( x*g_sizex/(g_rcDst.right - g_rcDst.left), |
| y*g_sizey/(g_rcDst.bottom - g_rcDst.top), k); |
| } |
| |
| LRESULT CALLBACK InternalWndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) |
| { |
| PAINTSTRUCT ps; // Structure for the paint message |
| POINT p = {0, 0}; // Translation point for the window's client region |
| HRESULT hRet; |
| |
| switch (iMsg) |
| { |
| case WM_MOVE: |
| // Make sure we're not moving to be minimized - because otherwise |
| // our ratio varialbes (g_dwXRatio and g_dwYRatio) will end up |
| // being 0, and once we hit CheckBoundries it divides by 0. |
| if (!IsIconic(hwnd)) |
| { |
| g_rcSrc.left = 0; |
| g_rcSrc.right = g_sizex; |
| g_rcSrc.top = 0; |
| g_rcSrc.bottom = g_sizey; |
| GetClientRect(hwnd, &g_rcDst); |
| g_dwXRatio = (g_rcDst.right - g_rcDst.left) * 1000 / |
| (g_rcSrc.right - g_rcSrc.left); |
| g_dwYRatio = (g_rcDst.bottom - g_rcDst.top) * 1000 / |
| (g_rcSrc.bottom - g_rcSrc.top); |
| ClientToScreen(hwnd, &p); |
| g_rcDst.left = p.x; |
| g_rcDst.top = p.y; |
| g_rcDst.bottom += p.y; |
| g_rcDst.right += p.x; |
| CheckBoundries(); |
| } |
| else |
| // Else, hide the overlay... just in case we can't do |
| // destination color keying, this will pull the overlay |
| // off of the screen for the user. |
| if (g_pDDSOverlay && g_pDDSPrimary) |
| g_pDDSOverlay->UpdateOverlay(NULL, g_pDDSPrimary, NULL, DDOVER_HIDE, NULL); |
| // Check to make sure our window exists before we tell it to |
| // repaint. This will fail the first time (while the window is being created). |
| if (hwnd) |
| { |
| InvalidateRect(hwnd, NULL, FALSE); |
| UpdateWindow(hwnd); |
| } |
| return 0L; |
| |
| case WM_SIZE: |
| // Another check for the minimization action. This check is |
| // quicker though... |
| if (wParam != SIZE_MINIMIZED) |
| { |
| GetClientRect(hwnd, &g_rcDst); |
| ClientToScreen(hwnd, &p); |
| g_rcDst.left = p.x; |
| g_rcDst.top = p.y; |
| g_rcDst.bottom += p.y; |
| g_rcDst.right += p.x; |
| g_rcSrc.left = 0; |
| g_rcSrc.right = g_sizex; |
| g_rcSrc.top = 0; |
| g_rcSrc.bottom = g_sizey; |
| // Here we multiply by 1000 to preserve 3 decimal places in the |
| // division opperation (we picked 1000 to be on the same order |
| // of magnitude as the stretch factor for easier comparisons) |
| g_dwXRatio = (g_rcDst.right - g_rcDst.left) * 1000 / |
| (g_rcSrc.right - g_rcSrc.left); |
| g_dwYRatio = (g_rcDst.bottom - g_rcDst.top) * 1000 / |
| (g_rcSrc.bottom - g_rcSrc.top); |
| CheckBoundries(); |
| } |
| return 0L; |
| |
| case WM_PAINT: |
| BeginPaint(hwnd, &ps); |
| // Check the primary surface to see if it's lost - if so you can |
| // pretty much bet that the other surfaces are also lost - thus |
| // restore EVERYTHING! If we got our surfaces stolen by a full |
| // screen app - then we'll destroy our primary - and won't be able |
| // to initialize it again. When we get our next paint message (the |
| // full screen app closed for example) we'll want to try to reinit |
| // the surfaces again - that's why there is a check for |
| // g_pDDSPrimary == NULL. The other option, is that our program |
| // went through this process, could init the primary again, but it |
| // couldn't init the overlay, that's why there's a third check for |
| // g_pDDSOverlay == NULL. Make sure that the check for |
| // !g_pDDSPrimary is BEFORE the IsLost call - that way if the |
| // pointer is NULL (ie. !g_pDDSPrimary is TRUE) - the compiler |
| // won't try to evaluate the IsLost function (which, since the |
| // g_pDDSPrimary surface is NULL, would be bad...). |
| if (!g_pDDSPrimary || (g_pDDSPrimary->IsLost() != DD_OK) || |
| (g_pDDSOverlay == NULL)) |
| { |
| DestroyOverlay(); |
| DestroyPrimary(); |
| if (DDPrimaryInit()) |
| if (DDOverlayInit()) |
| if (!DrawOverlay()) |
| DestroyOverlay(); |
| } |
| // UpdateOverlay is how we put the overlay on the screen. |
| if (g_pDDSOverlay && g_pDDSPrimary && g_video->updating) |
| { |
| hRet = g_pDDSOverlay->UpdateOverlay(&g_rcSrc, g_pDDSPrimary, |
| &g_rcDst, g_OverlayFlags, |
| &g_OverlayFX); |
| #ifdef _DEBUG |
| if(hRet != DD_OK) DisplayError("Can't update overlay", hRet); |
| #endif |
| } |
| EndPaint(hwnd, &ps); |
| return 0L; |
| |
| // process mouse and keyboard events |
| case WM_LBUTTONDOWN: mouse(1, lParam); break; |
| case WM_LBUTTONUP: mouse(-1, lParam); break; |
| case WM_RBUTTONDOWN: mouse(2, lParam); break; |
| case WM_RBUTTONUP: mouse(-2, lParam); break; |
| case WM_MBUTTONDOWN: mouse(3, lParam); break; |
| case WM_MBUTTONUP: mouse(-3, lParam); break; |
| case WM_CHAR: g_video->on_key(wParam); break; |
| |
| case WM_DISPLAYCHANGE: return 0L; |
| |
| case WM_DESTROY: |
| // Now, shut down the window... |
| PostQuitMessage(0); |
| return 0L; |
| } |
| return g_pUserProc? g_pUserProc(hwnd, iMsg, wParam, lParam) : DefWindowProc(hwnd, iMsg, wParam, lParam); |
| } |
| |
| DWORD WINAPI thread_vsync(LPVOID lpParameter) |
| { |
| BOOL vblank = false; |
| while(g_video && g_video->running) { |
| while(!vblank && g_video && g_video->running) { |
| YIELD_TO_THREAD(); |
| LPDIRECTDRAW7 pDD(g_pDD); |
| if(pDD) pDD->GetVerticalBlankStatus(&vblank); |
| } |
| LPDIRECTDRAWSURFACE7 pDDSOverlay(g_pDDSOverlay); |
| if(pDDSOverlay) pDDSOverlay->UpdateOverlay(&g_rcSrc, g_pDDSPrimary, &g_rcDst, g_OverlayFlags | DDOVER_REFRESHALL, &g_OverlayFX); |
| do { |
| Sleep(1); |
| LPDIRECTDRAW7 pDD(g_pDD); |
| if(pDD) pDD->GetVerticalBlankStatus(&vblank); |
| } while(vblank && g_video && g_video->running); |
| while(g_video && !g_video->updating && g_video->running) Sleep(10); |
| } |
| return 0; |
| } |
| |
| ///////////////////////////////////////////// public methods of video class /////////////////////// |
| |
| inline void mask2bits(unsigned int mask, color_t &save, char &shift) |
| { |
| save = mask; if(!mask) { shift = 8; return; } |
| shift = 0; while(!(mask&1)) ++shift, mask >>= 1; |
| int bits = 0; while(mask&1) ++bits, mask >>= 1; |
| shift += bits - 8; |
| } |
| |
| bool video::init_window(int sizex, int sizey) |
| { |
| assert(win_hInstance != 0); |
| g_sizex = sizex; g_sizey = sizey; |
| if( !WinInit(win_hInstance, win_iCmdShow, gWndClass, title, false) ) |
| return DisplayError("Unable to initialize the program's window."); |
| running = true; |
| if( !DDInit() ) { |
| DestroyDDraw(); |
| goto fail; |
| } |
| if( !DDOverlayInit() || !DrawOverlay() ) { |
| DestroyOverlay(); |
| DestroyDDraw(); |
| goto fail; |
| } |
| DDPIXELFORMAT PixelFormat; memset(&PixelFormat, 0, sizeof(PixelFormat)); PixelFormat.dwSize = sizeof(PixelFormat); |
| g_pDDSOverlay->GetPixelFormat(&PixelFormat); |
| mask2bits(PixelFormat.dwRBitMask, red_mask, red_shift); |
| mask2bits(PixelFormat.dwGBitMask, green_mask, green_shift); |
| mask2bits(PixelFormat.dwBBitMask, blue_mask, blue_shift); |
| if(PixelFormat.dwFlags == DDPF_RGB) |
| depth = char(PixelFormat.dwRGBBitCount); |
| else depth = -char(PixelFormat.dwFourCC); |
| for(int i = 0, e = sizex * sizey * PixelFormat.dwRGBBitCount / 32, c = get_color(0, 0, 0); i < e; i++) |
| g_pImg[i] = c; // clear surface |
| ShowWindow(g_hAppWnd, SW_SHOW); |
| g_hVSync = CreateThread ( |
| NULL, // LPSECURITY_ATTRIBUTES security_attrs |
| 0, // SIZE_T stacksize |
| (LPTHREAD_START_ROUTINE) thread_vsync, |
| this, // argument |
| 0, 0); |
| SetPriorityClass(g_hVSync, IDLE_PRIORITY_CLASS); // questionable |
| return true; |
| fail: |
| g_pImg = new unsigned int[g_sizex * g_sizey]; |
| return false; |
| } |
| |
| void video::terminate() |
| { |
| running = false; |
| DestroyOverlay(); |
| if(WaitForSingleObject(g_hVSync, 100) == WAIT_TIMEOUT) TerminateThread(g_hVSync, 0); |
| CloseHandle(g_hVSync); |
| DestroyDDraw(); |
| if(g_pImg) delete[] g_pImg; |
| g_pImg = 0; g_video = 0; |
| } |
| //////////// drawing area constructor & destructor ///////////// |
| |
| drawing_area::drawing_area(int x, int y, int sizex, int sizey) |
| : start_x(x), start_y(y), size_x(sizex), size_y(sizey), pixel_depth(g_video->depth), |
| base_index(y*g_sizex + x), max_index(g_sizex*g_sizey), index_stride(g_sizex), ptr32(g_pImg) |
| { |
| assert(ptr32); assert(x < g_sizex); assert(y < g_sizey); |
| assert(x+sizex <= g_sizex); assert(y+sizey <= g_sizey); |
| |
| index = base_index; // current index |
| } |
| |
| drawing_area::~drawing_area() |
| { |
| } |