diff --git a/desmume/src/frontend/windows/DeSmuME.vcxproj b/desmume/src/frontend/windows/DeSmuME.vcxproj
index 794d7106f..d4d430baa 100644
--- a/desmume/src/frontend/windows/DeSmuME.vcxproj
+++ b/desmume/src/frontend/windows/DeSmuME.vcxproj
@@ -284,6 +284,8 @@
+
+
@@ -371,6 +373,7 @@
+
@@ -582,6 +585,8 @@
+
+
@@ -657,6 +662,7 @@
+
diff --git a/desmume/src/frontend/windows/DeSmuME.vcxproj.filters b/desmume/src/frontend/windows/DeSmuME.vcxproj.filters
index 4047bb282..20bfe9dc2 100644
--- a/desmume/src/frontend/windows/DeSmuME.vcxproj.filters
+++ b/desmume/src/frontend/windows/DeSmuME.vcxproj.filters
@@ -921,6 +921,15 @@
utils\colorspacehandler
+
+ frontend\Windows
+
+
+ frontend\Windows
+
+
+ frontend\Windows
+
@@ -1610,6 +1619,15 @@
utils\colorspacehandler
+
+ frontend\Windows
+
+
+ frontend\Windows
+
+
+ frontend\Windows
+
diff --git a/desmume/src/frontend/windows/ddraw.cpp b/desmume/src/frontend/windows/ddraw.cpp
new file mode 100644
index 000000000..afbdb58b9
--- /dev/null
+++ b/desmume/src/frontend/windows/ddraw.cpp
@@ -0,0 +1,185 @@
+/*
+Copyright (C) 2018 DeSmuME team
+
+This file is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This file 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 the this software. If not, see .
+*/
+
+#include "ddraw.h"
+
+#include "directx/ddraw.h"
+#include "GPU.h"
+#include "types.h"
+
+const char *DDerrors[] = {
+ "no errors",
+ "Unable to initialize DirectDraw",
+ "Unable to set DirectDraw Cooperative Level",
+ "Unable to create DirectDraw primary surface",
+ "Unable to set DirectDraw clipper" };
+
+DDRAW::DDRAW() :
+ handle(NULL), clip(NULL)
+{
+ surface.primary = NULL;
+ surface.back = NULL;
+ memset(&surfDesc, 0, sizeof(surfDesc));
+ memset(&surfDescBack, 0, sizeof(surfDescBack));
+}
+
+u32 DDRAW::create(HWND hwnd)
+{
+ if (handle) return 0;
+
+ if (FAILED(DirectDrawCreateEx(NULL, (LPVOID*)&handle, IID_IDirectDraw7, NULL)))
+ return 1;
+
+ if (FAILED(handle->SetCooperativeLevel(hwnd, DDSCL_NORMAL)))
+ return 2;
+
+ createSurfaces(hwnd);
+
+ return 0;
+}
+
+bool DDRAW::release()
+{
+ if (!handle) return true;
+
+ if (clip != NULL) clip->Release();
+ if (surface.back != NULL) surface.back->Release();
+ if (surface.primary != NULL) surface.primary->Release();
+
+ if (FAILED(handle->Release())) return false;
+ return true;
+}
+
+bool DDRAW::createBackSurface(int width, int height)
+{
+ if (surface.back) { surface.back->Release(); surface.back = NULL; }
+
+ memset(&surfDescBack, 0, sizeof(surfDescBack));
+ surfDescBack.dwSize = sizeof(surfDescBack);
+ surfDescBack.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
+ surfDescBack.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
+
+ if (systemMemory)
+ surfDescBack.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
+ else
+ surfDescBack.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
+
+
+ surfDescBack.dwWidth = width;
+ surfDescBack.dwHeight = height;
+
+ if (FAILED(handle->CreateSurface(&surfDescBack, &surface.back, NULL))) return false;
+
+ return true;
+}
+
+bool DDRAW::createSurfaces(HWND hwnd)
+{
+ if (!handle) return true;
+
+ if (clip) { clip->Release(); clip = NULL; }
+ if (surface.primary) { surface.primary->Release(); surface.primary = NULL; }
+
+
+ // primary
+ memset(&surfDesc, 0, sizeof(surfDesc));
+ surfDesc.dwSize = sizeof(surfDesc);
+ surfDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
+ surfDesc.dwFlags = DDSD_CAPS;
+ if (FAILED(handle->CreateSurface(&surfDesc, &surface.primary, NULL)))
+ return false;
+
+ //default doesnt matter much, itll get adjusted later
+ if (!createBackSurface(GPU_FRAMEBUFFER_NATIVE_WIDTH, GPU_FRAMEBUFFER_NATIVE_HEIGHT * 2))
+ return false;
+
+ if (FAILED(handle->CreateClipper(0, &clip, NULL))) return false;
+ if (FAILED(clip->SetHWnd(0, hwnd))) return false;
+ if (FAILED(surface.primary->SetClipper(clip))) return false;
+
+ return true;
+}
+
+bool DDRAW::lock()
+{
+ if (!handle) return true;
+ if (!surface.back) return false;
+ memset(&surfDescBack, 0, sizeof(surfDescBack));
+ surfDescBack.dwSize = sizeof(surfDescBack);
+ surfDescBack.dwFlags = DDSD_ALL;
+
+ HRESULT res = surface.back->Lock(NULL, &surfDescBack, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL);
+ if (FAILED(res))
+ {
+ //INFO("DDraw failed: Lock %i\n", res);
+ if (res == DDERR_SURFACELOST)
+ {
+ res = surface.back->Restore();
+ if (FAILED(res)) return false;
+ }
+ }
+ return true;
+}
+
+bool DDRAW::unlock()
+{
+ if (!handle) return true;
+ if (!surface.back) return false;
+ if (FAILED(surface.back->Unlock((LPRECT)surfDescBack.lpSurface))) return false;
+ return true;
+}
+
+bool DDRAW::OK()
+{
+ if (!handle) return false;
+ if (!surface.primary) return false;
+ if (!surface.back) return false;
+ return true;
+}
+
+bool DDRAW::blt(LPRECT dst, LPRECT src)
+{
+ if (!handle) return true;
+ if (!surface.primary) return false;
+ if (!surface.back) return false;
+
+ if (vSync)
+ {
+ //this seems to block the whole process. this destroys the display thread and will easily block the emulator to 30fps.
+ //IDirectDraw7_WaitForVerticalBlank(handle,DDWAITVB_BLOCKBEGIN,0);
+
+ for (;;)
+ {
+ BOOL vblank;
+ IDirectDraw7_GetVerticalBlankStatus(handle, &vblank);
+ if (vblank) break;
+ //must be a greedy loop since vblank is small relative to 1msec minimum Sleep() resolution.
+ }
+ }
+
+ HRESULT res = surface.primary->Blt(dst, surface.back, src, DDBLT_WAIT, 0);
+ if (FAILED(res))
+ {
+ //INFO("DDraw failed: Blt %i\n", res);
+ if (res == DDERR_SURFACELOST)
+ {
+ res = surface.primary->Restore();
+ if (FAILED(res)) return false;
+ }
+ }
+ return true;
+}
diff --git a/desmume/src/frontend/windows/ddraw.h b/desmume/src/frontend/windows/ddraw.h
new file mode 100644
index 000000000..ca7df272c
--- /dev/null
+++ b/desmume/src/frontend/windows/ddraw.h
@@ -0,0 +1,54 @@
+/*
+Copyright (C) 2018 DeSmuME team
+
+This file is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This file 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 the this software. If not, see .
+*/
+
+#ifndef _DDRAW_H_
+#define _DDRAW_H_
+
+#include "types.h"
+#include "directx/ddraw.h"
+
+extern const char *DDerrors[];
+
+struct DDRAW
+{
+ DDRAW();
+
+ u32 create(HWND hwnd);
+ bool release();
+ bool createSurfaces(HWND hwnd);
+ bool createBackSurface(int width, int height);
+ bool lock();
+ bool unlock();
+ bool blt(LPRECT dst, LPRECT src);
+ bool OK();
+
+ LPDIRECTDRAW7 handle;
+ struct
+ {
+ LPDIRECTDRAWSURFACE7 primary;
+ LPDIRECTDRAWSURFACE7 back;
+ } surface;
+
+ DDSURFACEDESC2 surfDesc;
+ DDSURFACEDESC2 surfDescBack;
+ LPDIRECTDRAWCLIPPER clip;
+
+ bool systemMemory = false;
+ bool vSync = false;
+};
+
+#endif
\ No newline at end of file
diff --git a/desmume/src/frontend/windows/display.cpp b/desmume/src/frontend/windows/display.cpp
new file mode 100644
index 000000000..61021e197
--- /dev/null
+++ b/desmume/src/frontend/windows/display.cpp
@@ -0,0 +1,961 @@
+/*
+Copyright (C) 2018 DeSmuME team
+
+This file is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This file 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 the this software. If not, see .
+*/
+
+#include "display.h"
+
+#include
+#include
+
+#include "main.h"
+#include "windriver.h"
+#include "winutil.h"
+
+DDRAW ddraw;
+GLDISPLAY gldisplay;
+u32 displayMethod;
+
+VideoInfo video;
+
+int gpu_bpp = 18;
+
+
+int frameskiprate = 0;
+int lastskiprate = 0;
+int emu_paused = 0;
+bool frameAdvance = false;
+
+bool SeparationBorderDrag = true;
+int ScreenGapColor = 0xFFFFFF;
+/*const u32 gapColors[16] = {
+Color::Gray,
+Color::Brown,
+Color::Red,
+Color::Pink,
+Color::Orange,
+Color::Yellow,
+Color::LightGreen,
+Color::Lime,
+Color::Green,
+Color::SeaGreen,
+Color::SkyBlue,
+Color::Blue,
+Color::DarkBlue,
+Color::DarkViolet,
+Color::Purple,
+Color::Fuchsia
+};*/
+
+RECT FullScreenRect, MainScreenRect, SubScreenRect, GapRect;
+RECT MainScreenSrcRect, SubScreenSrcRect;
+
+bool ForceRatio = true;
+bool PadToInteger = false;
+float screenSizeRatio = 1.0f;
+bool vCenterResizedScr = true;
+
+//triple buffering logic
+struct DisplayBuffer
+{
+ DisplayBuffer()
+ : buffer(NULL)
+ , size(0)
+ {
+ }
+ u32* buffer;
+ size_t size; //[256*192*4];
+} displayBuffers[3];
+
+volatile int currDisplayBuffer = -1;
+volatile int newestDisplayBuffer = -2;
+slock_t *display_mutex = NULL;
+sthread_t *display_thread = NULL;
+volatile bool display_die = false;
+HANDLE display_wakeup_event = INVALID_HANDLE_VALUE;
+HANDLE display_done_event = INVALID_HANDLE_VALUE;
+DWORD display_done_timeout = 500;
+
+int displayPostponeType = 0;
+DWORD displayPostponeUntil = ~0;
+bool displayNoPostponeNext = false;
+
+DWORD display_invoke_argument = 0;
+void(*display_invoke_function)(DWORD) = NULL;
+HANDLE display_invoke_ready_event = INVALID_HANDLE_VALUE;
+HANDLE display_invoke_done_event = INVALID_HANDLE_VALUE;
+DWORD display_invoke_timeout = 500;
+CRITICAL_SECTION display_invoke_handler_cs;
+static void InvokeOnMainThread(void(*function)(DWORD), DWORD argument);
+
+int scanline_filter_a = 0;
+int scanline_filter_b = 2;
+int scanline_filter_c = 2;
+int scanline_filter_d = 4;
+
+#pragma pack(push,1)
+struct pix24
+{
+ u8 b, g, r;
+ FORCEINLINE pix24(u32 s) : b(s & 0xFF), g((s & 0xFF00) >> 8), r((s & 0xFF0000) >> 16) {}
+};
+struct pix16
+{
+ u16 c;
+ FORCEINLINE pix16(u32 s) : c(((s & 0xF8) >> 3) | ((s & 0xFC00) >> 5) | ((s & 0xF80000) >> 8)) {}
+};
+struct pix15
+{
+ u16 c;
+ FORCEINLINE pix15(u32 s) : c(((s & 0xF8) >> 3) | ((s & 0xF800) >> 6) | ((s & 0xF80000) >> 9)) {}
+};
+#pragma pack(pop)
+
+void GetNdsScreenRect(RECT* r)
+{
+ RECT zero;
+ SetRect(&zero, 0, 0, 0, 0);
+ if (zero == MainScreenRect) *r = SubScreenRect;
+ else if (zero == SubScreenRect || video.layout == 2) *r = MainScreenRect;
+ else
+ {
+ RECT* dstRects[2] = { &MainScreenRect, &SubScreenRect };
+ int left = min(dstRects[0]->left, dstRects[1]->left);
+ int top = min(dstRects[0]->top, dstRects[1]->top);
+ int right = max(dstRects[0]->right, dstRects[1]->right);
+ int bottom = max(dstRects[0]->bottom, dstRects[1]->bottom);
+ SetRect(r, left, top, right, bottom);
+ }
+}
+
+struct DisplayLayoutInfo
+{
+ int vx, vy, vw, vh;
+ float widthScale, heightScale;
+ int bufferWidth, bufferHeight;
+};
+//performs aspect ratio letterboxing correction and integer clamping
+DisplayLayoutInfo CalculateDisplayLayout(RECT rcClient, bool maintainAspect, bool maintainInteger, int targetWidth, int targetHeight)
+{
+ DisplayLayoutInfo ret;
+
+ //do maths on the viewport and the native resolution and the user settings to get a display rectangle
+ SIZE sz = { rcClient.right - rcClient.left, rcClient.bottom - rcClient.top };
+
+ float widthScale = (float)sz.cx / targetWidth;
+ float heightScale = (float)sz.cy / targetHeight;
+ if (maintainAspect)
+ {
+ if (widthScale > heightScale) widthScale = heightScale;
+ if (heightScale > widthScale) heightScale = widthScale;
+ }
+ if (maintainInteger)
+ {
+ widthScale = floorf(widthScale);
+ heightScale = floorf(heightScale);
+ }
+ ret.vw = (int)(widthScale * targetWidth);
+ ret.vh = (int)(heightScale * targetHeight);
+ ret.vx = (sz.cx - ret.vw) / 2;
+ ret.vy = (sz.cy - ret.vh) / 2;
+ ret.widthScale = widthScale;
+ ret.heightScale = heightScale;
+ ret.bufferWidth = sz.cx;
+ ret.bufferHeight = sz.cy;
+
+ return ret;
+}
+//reformulates CalculateDisplayLayout() into a format more convenient for this purpose
+RECT CalculateDisplayLayoutWrapper(RECT rcClient, int targetWidth, int targetHeight, int tbHeight, bool maximized)
+{
+ bool maintainInteger = !!PadToInteger;
+ bool maintainAspect = !!ForceRatio;
+
+ if (maintainInteger) maintainAspect = true;
+
+ //nothing to do here if maintain aspect isnt chosen
+ if (!maintainAspect) return rcClient;
+
+ RECT rc = { rcClient.left, rcClient.top + tbHeight, rcClient.right, rcClient.bottom };
+ DisplayLayoutInfo dli = CalculateDisplayLayout(rc, maintainAspect, maintainInteger, targetWidth, targetHeight);
+ rc.left = rcClient.left + dli.vx;
+ rc.top = rcClient.top + dli.vy;
+ rc.right = rc.left + dli.vw;
+ rc.bottom = rc.top + dli.vh + tbHeight;
+ return rc;
+}
+
+template static void doRotate(void* dst)
+{
+ u8* buffer = (u8*)dst;
+ int size = video.size();
+ u32* src = (u32*)video.finalBuffer();
+ int width = video.width;
+ int height = video.height;
+ int pitch = ddraw.surfDescBack.lPitch;
+
+ switch (video.rotation)
+ {
+ case 0:
+ case 180:
+ {
+ if (pitch == 1024)
+ {
+ if (video.rotation == 0)
+ if (sizeof(T) == sizeof(u32))
+ memcpy(buffer, src, size * sizeof(u32));
+ else
+ for (int i = 0; i < size; i++)
+ ((T*)buffer)[i] = src[i];
+ else // if(video.rotation==180)
+ for (int i = 0, j = size - 1; j >= 0; i++, j--)
+ ((T*)buffer)[i] = src[j];
+ }
+ else
+ {
+ if (video.rotation == 0)
+ if (sizeof(T) != sizeof(u32))
+ for (int y = 0; y < height; y++)
+ {
+ for (int x = 0; x < width; x++)
+ ((T*)buffer)[x] = src[(y * width) + x];
+
+ buffer += pitch;
+ }
+ else
+ for (int y = 0; y < height; y++)
+ {
+ memcpy(buffer, &src[y * width], width * sizeof(u32));
+ buffer += pitch;
+ }
+ else // if(video.rotation==180)
+ for (int y = 0; y < height; y++)
+ {
+ for (int x = 0; x < width; x++)
+ ((T*)buffer)[x] = src[height*width - (y * width) - x - 1];
+
+ buffer += pitch;
+ }
+ }
+ }
+ break;
+ case 90:
+ case 270:
+ {
+ if (video.rotation == 90)
+ for (int y = 0; y < width; y++)
+ {
+ for (int x = 0; x < height; x++)
+ ((T*)buffer)[x] = src[(((height - 1) - x) * width) + y];
+
+ buffer += pitch;
+ }
+ else
+ for (int y = 0; y < width; y++)
+ {
+ for (int x = 0; x < height; x++)
+ ((T*)buffer)[x] = src[((x)* width) + (width - 1) - y];
+
+ buffer += pitch;
+ }
+ }
+ break;
+ }
+}
+static void DD_FillRect(LPDIRECTDRAWSURFACE7 surf, int left, int top, int right, int bottom, DWORD color)
+{
+ RECT r;
+ SetRect(&r, left, top, right, bottom);
+ DDBLTFX fx;
+ memset(&fx, 0, sizeof(DDBLTFX));
+ fx.dwSize = sizeof(DDBLTFX);
+ //fx.dwFillColor = color;
+ fx.dwFillColor = 0; //color is just for debug
+ surf->Blt(&r, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx);
+}
+
+static void OGL_DrawTexture(RECT* srcRects, RECT* dstRects)
+{
+ //don't change the original rects
+ RECT sRects[2];
+ sRects[0] = RECT(srcRects[0]);
+ sRects[1] = RECT(srcRects[1]);
+
+ //use clear+scissor for gap
+ if (video.screengap > 0)
+ {
+ //adjust client rect into scissor rect (0,0 at bottomleft)
+ glScissor(dstRects[2].left, dstRects[2].bottom, dstRects[2].right - dstRects[2].left, dstRects[2].top - dstRects[2].bottom);
+
+ u32 color_rev = (u32)ScreenGapColor;
+ int r = (color_rev >> 0) & 0xFF;
+ int g = (color_rev >> 8) & 0xFF;
+ int b = (color_rev >> 16) & 0xFF;
+ glClearColor(r / 255.0f, g / 255.0f, b / 255.0f, 1.0f);
+ glEnable(GL_SCISSOR_TEST);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glDisable(GL_SCISSOR_TEST);
+ }
+
+ //draw two screens
+ glBegin(GL_QUADS);
+ for (int i = 0; i<2; i++)
+ {
+ //none of this makes any goddamn sense. dont even try.
+ int idx = i;
+ int ofs = 0;
+ switch (video.rotation)
+ {
+ case 0:
+ break;
+ case 90:
+ ofs = 3;
+ idx = 1 - i;
+ std::swap(sRects[idx].right, sRects[idx].bottom);
+ std::swap(sRects[idx].left, sRects[idx].top);
+ break;
+ case 180:
+ idx = 1 - i;
+ ofs = 2;
+ break;
+ case 270:
+ std::swap(sRects[idx].right, sRects[idx].bottom);
+ std::swap(sRects[idx].left, sRects[idx].top);
+ ofs = 1;
+ break;
+ }
+ float u1 = sRects[idx].left / (float)video.width;
+ float u2 = sRects[idx].right / (float)video.width;
+ float v1 = sRects[idx].top / (float)video.height;
+ float v2 = sRects[idx].bottom / (float)video.height;
+ float u[] = { u1,u2,u2,u1 };
+ float v[] = { v1,v1,v2,v2 };
+
+ glTexCoord2f(u[(ofs + 0) % 4], v[(ofs + 0) % 4]);
+ glVertex2i(dstRects[i].left, dstRects[i].top);
+
+ glTexCoord2f(u[(ofs + 1) % 4], v[(ofs + 1) % 4]);
+ glVertex2i(dstRects[i].right, dstRects[i].top);
+
+ glTexCoord2f(u[(ofs + 2) % 4], v[(ofs + 2) % 4]);
+ glVertex2i(dstRects[i].right, dstRects[i].bottom);
+
+ glTexCoord2f(u[(ofs + 3) % 4], v[(ofs + 3) % 4]);
+ glVertex2i(dstRects[i].left, dstRects[i].bottom);
+ }
+ glEnd();
+}
+static void OGL_DoDisplay()
+{
+ if (!gldisplay.begin(MainWindow->getHWnd())) return;
+
+ //the ds screen fills the texture entirely, so we dont have garbage at edge to worry about,
+ //but we need to make sure this is clamped for when filtering is selected
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ if (gldisplay.filter)
+ {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ }
+ else
+ {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ }
+
+ RECT rc;
+ HWND hwnd = MainWindow->getHWnd();
+ GetClientRect(hwnd, &rc);
+ int width = rc.right - rc.left;
+ int height = rc.bottom - rc.top;
+
+ glViewport(0, 0, width, height);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0.0f, (float)width, (float)height, 0.0f, -100.0f, 100.0f);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ //clear entire area, for cases where the screen is maximized
+ glClearColor(0, 0, 0, 0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ // get dest and src rects
+ RECT dr[] = { MainScreenRect, SubScreenRect, GapRect };
+ for (int i = 0; i<2; i++) //dont change gap rect, for some reason
+ {
+ ScreenToClient(hwnd, (LPPOINT)&dr[i].left);
+ ScreenToClient(hwnd, (LPPOINT)&dr[i].right);
+ }
+ dr[2].bottom = height - dr[2].bottom;
+ dr[2].top = height - dr[2].top;
+
+ RECT srcRects[2];
+ const bool isMainGPUFirst = (GPU->GetDisplayInfo().engineID[NDSDisplayID_Main] == GPUEngineID_Main);
+
+ if (video.swap == 0)
+ {
+ srcRects[0] = MainScreenSrcRect;
+ srcRects[1] = SubScreenSrcRect;
+ if (osd) osd->swapScreens = false;
+ }
+ else if (video.swap == 1)
+ {
+ srcRects[0] = SubScreenSrcRect;
+ srcRects[1] = MainScreenSrcRect;
+ if (osd) osd->swapScreens = true;
+ }
+ else if (video.swap == 2)
+ {
+ srcRects[0] = (!isMainGPUFirst) ? SubScreenSrcRect : MainScreenSrcRect;
+ srcRects[1] = (!isMainGPUFirst) ? MainScreenSrcRect : SubScreenSrcRect;
+ if (osd) osd->swapScreens = !isMainGPUFirst;
+ }
+ else if (video.swap == 3)
+ {
+ srcRects[0] = (!isMainGPUFirst) ? MainScreenSrcRect : SubScreenSrcRect;
+ srcRects[1] = (!isMainGPUFirst) ? SubScreenSrcRect : MainScreenSrcRect;
+ if (osd) osd->swapScreens = isMainGPUFirst;
+ }
+
+ //printf("%d,%d %dx%d -- %d,%d %dx%d\n",
+ // srcRects[0].left,srcRects[0].top, srcRects[0].right-srcRects[0].left, srcRects[0].bottom-srcRects[0].top,
+ // srcRects[1].left,srcRects[1].top, srcRects[1].right-srcRects[1].left, srcRects[1].bottom-srcRects[1].top
+ // );
+
+ glEnable(GL_TEXTURE_2D);
+ // draw DS display
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, video.width, video.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, video.finalBuffer());
+ OGL_DrawTexture(srcRects, dr);
+
+ // draw HUD
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, GPU_FRAMEBUFFER_NATIVE_WIDTH, GPU_FRAMEBUFFER_NATIVE_HEIGHT * 2, 0, GL_BGRA, GL_UNSIGNED_BYTE, aggDraw.hud->buf().buf());
+ OGL_DrawTexture(srcRects, dr);
+ glDisable(GL_BLEND);
+
+ gldisplay.showPage();
+
+ gldisplay.end();
+}
+static void DD_DoDisplay()
+{
+ if (ddraw.surfDescBack.dwWidth != video.rotatedwidth() || ddraw.surfDescBack.dwHeight != video.rotatedheight())
+ ddraw.createBackSurface(video.rotatedwidth(), video.rotatedheight());
+
+ if (!ddraw.lock()) return;
+ char* buffer = (char*)ddraw.surfDescBack.lpSurface;
+
+ switch (ddraw.surfDescBack.ddpfPixelFormat.dwRGBBitCount)
+ {
+ case 32:
+ doRotate(ddraw.surfDescBack.lpSurface);
+ break;
+ case 24:
+ doRotate(ddraw.surfDescBack.lpSurface);
+ break;
+ case 16:
+ if (ddraw.surfDescBack.ddpfPixelFormat.dwGBitMask != 0x3E0)
+ doRotate(ddraw.surfDescBack.lpSurface);
+ else
+ doRotate(ddraw.surfDescBack.lpSurface);
+ break;
+ case 0:
+ break;
+ default:
+ INFO("Unsupported color depth: %i bpp\n", ddraw.surfDescBack.ddpfPixelFormat.dwRGBBitCount);
+ //emu_halt();
+ break;
+ }
+ if (!ddraw.unlock()) return;
+
+ RECT* dstRects[2] = { &MainScreenRect, &SubScreenRect };
+ RECT* srcRects[2];
+ const bool isMainGPUFirst = (GPU->GetDisplayInfo().engineID[NDSDisplayID_Main] == GPUEngineID_Main);
+
+ if (video.swap == 0)
+ {
+ srcRects[0] = &MainScreenSrcRect;
+ srcRects[1] = &SubScreenSrcRect;
+ if (osd) osd->swapScreens = false;
+ }
+ else if (video.swap == 1)
+ {
+ srcRects[0] = &SubScreenSrcRect;
+ srcRects[1] = &MainScreenSrcRect;
+ if (osd) osd->swapScreens = true;
+ }
+ else if (video.swap == 2)
+ {
+ srcRects[0] = (!isMainGPUFirst) ? &SubScreenSrcRect : &MainScreenSrcRect;
+ srcRects[1] = (!isMainGPUFirst) ? &MainScreenSrcRect : &SubScreenSrcRect;
+ if (osd) osd->swapScreens = !isMainGPUFirst;
+ }
+ else if (video.swap == 3)
+ {
+ srcRects[0] = (!isMainGPUFirst) ? &MainScreenSrcRect : &SubScreenSrcRect;
+ srcRects[1] = (!isMainGPUFirst) ? &SubScreenSrcRect : &MainScreenSrcRect;
+ if (osd) osd->swapScreens = isMainGPUFirst;
+ }
+
+ //this code fills in all the undrawn areas
+ //it is probably a waste of time to keep black-filling all this, but we need to do it to be safe.
+ {
+ RECT wr;
+ GetWindowRect(MainWindow->getHWnd(), &wr);
+ RECT r;
+ GetNdsScreenRect(&r);
+ int left = r.left;
+ int top = r.top;
+ int right = r.right;
+ int bottom = r.bottom;
+ //printf("%d %d %d %d / %d %d %d %d\n",fullScreen.left,fullScreen.top,fullScreen.right,fullScreen.bottom,left,top,right,bottom);
+ //printf("%d %d %d %d / %d %d %d %d\n",MainScreenRect.left,MainScreenRect.top,MainScreenRect.right,MainScreenRect.bottom,SubScreenRect.left,SubScreenRect.top,SubScreenRect.right,SubScreenRect.bottom);
+ if (ddraw.OK())
+ {
+ DD_FillRect(ddraw.surface.primary, 0, 0, left, top, RGB(255, 0, 0)); //topleft
+ DD_FillRect(ddraw.surface.primary, left, 0, right, top, RGB(128, 0, 0)); //topcenter
+ DD_FillRect(ddraw.surface.primary, right, 0, wr.right, top, RGB(0, 255, 0)); //topright
+ DD_FillRect(ddraw.surface.primary, 0, top, left, bottom, RGB(0, 128, 0)); //left
+ DD_FillRect(ddraw.surface.primary, right, top, wr.right, bottom, RGB(0, 0, 255)); //right
+ DD_FillRect(ddraw.surface.primary, 0, bottom, left, wr.bottom, RGB(0, 0, 128)); //bottomleft
+ DD_FillRect(ddraw.surface.primary, left, bottom, right, wr.bottom, RGB(255, 0, 255)); //bottomcenter
+ DD_FillRect(ddraw.surface.primary, right, bottom, wr.right, wr.bottom, RGB(0, 255, 255)); //bottomright
+ if (video.layout == 1)
+ {
+ DD_FillRect(ddraw.surface.primary, SubScreenRect.left, wr.top, wr.right, SubScreenRect.top, RGB(0, 0, 0)); //Top gap left when centering the resized screen
+ DD_FillRect(ddraw.surface.primary, SubScreenRect.left, SubScreenRect.bottom, wr.right, wr.bottom, RGB(0, 0, 0)); //Bottom gap left when centering the resized screen
+ }
+ }
+ }
+
+ for (int i = 0; i < 2; i++)
+ {
+ if (i && video.layout == 2)
+ break;
+
+ if (!ddraw.blt(dstRects[i], srcRects[i])) return;
+ }
+
+ if (video.layout == 1) return;
+ if (video.layout == 2) return;
+
+ // Gap
+ if (video.screengap > 0)
+ {
+ //u32 color = gapColors[win_fw_config.fav_colour];
+ //u32 color_rev = (((color & 0xFF) << 16) | (color & 0xFF00) | ((color & 0xFF0000) >> 16));
+ u32 color_rev = (u32)ScreenGapColor;
+
+ HDC dc;
+ HBRUSH brush;
+
+ dc = GetDC(MainWindow->getHWnd());
+ brush = CreateSolidBrush(color_rev);
+
+ FillRect(dc, &GapRect, brush);
+
+ DeleteObject((HGDIOBJ)brush);
+ ReleaseDC(MainWindow->getHWnd(), dc);
+ }
+}
+
+void displayProc()
+{
+ slock_lock(display_mutex);
+
+ //find a buffer to display
+ int todo = newestDisplayBuffer;
+ bool alreadyDisplayed = (todo == currDisplayBuffer);
+
+ slock_unlock(display_mutex);
+
+ //something new to display:
+ if (!alreadyDisplayed) {
+ //start displaying a new buffer
+ currDisplayBuffer = todo;
+ video.srcBuffer = (u8*)displayBuffers[currDisplayBuffer].buffer;
+ video.srcBufferSize = displayBuffers[currDisplayBuffer].size;
+ }
+ DoDisplay();
+}
+void displayThread(void *arg)
+{
+ do
+ {
+ if ((MainWindow == NULL) || IsMinimized(MainWindow->getHWnd()))
+ {
+ WaitForSingleObject(display_wakeup_event, INFINITE);
+ }
+ else if ((emu_paused || !execute || !romloaded) && (!HudEditorMode && !CommonSettings.hud.ShowInputDisplay && !CommonSettings.hud.ShowGraphicalInputDisplay))
+ {
+ WaitForSingleObject(display_wakeup_event, 250);
+ }
+ else
+ {
+ WaitForSingleObject(display_wakeup_event, 10);
+ }
+
+ if (display_die)
+ {
+ break;
+ }
+
+ displayProc();
+ SetEvent(display_done_event);
+ } while (!display_die);
+}
+
+static void DoDisplay_DrawHud()
+{
+ osd->update();
+ DrawHUD();
+ osd->clear();
+}
+
+void Display()
+{
+ const NDSDisplayInfo &dispInfo = GPU->GetDisplayInfo();
+
+ if (CommonSettings.single_core())
+ {
+ video.srcBuffer = (u8*)dispInfo.masterCustomBuffer;
+ video.srcBufferSize = dispInfo.customWidth * dispInfo.customHeight * dispInfo.pixelBytes * 2;
+ DoDisplay();
+ }
+ else
+ {
+ if (display_thread == NULL)
+ {
+ display_mutex = slock_new();
+ display_thread = sthread_create(&displayThread, nullptr);
+ }
+
+ slock_lock(display_mutex);
+
+ if (int diff = (currDisplayBuffer + 1) % 3 - newestDisplayBuffer)
+ newestDisplayBuffer += diff;
+ else newestDisplayBuffer = (currDisplayBuffer + 2) % 3;
+
+ DisplayBuffer& db = displayBuffers[newestDisplayBuffer];
+ size_t targetSize = dispInfo.customWidth * dispInfo.customHeight * dispInfo.pixelBytes * 2;
+ if (db.size != targetSize)
+ {
+ free_aligned(db.buffer);
+ db.buffer = (u32*)malloc_alignedPage(targetSize);
+ db.size = targetSize;
+ }
+ memcpy(db.buffer, dispInfo.masterCustomBuffer, targetSize);
+
+ slock_unlock(display_mutex);
+
+ SetEvent(display_wakeup_event);
+ }
+}
+
+//does a single display work unit. only to be used from the display thread
+void DoDisplay()
+{
+ Lock lock(win_backbuffer_sync);
+
+ if (displayPostponeType && !displayNoPostponeNext && (displayPostponeType < 0 || timeGetTime() < displayPostponeUntil))
+ return;
+ displayNoPostponeNext = false;
+
+ //we have to do a copy here because we're about to draw the OSD onto it. bummer.
+ if (gpu_bpp == 15)
+ ColorspaceConvertBuffer555To8888Opaque((u16 *)video.srcBuffer, video.buffer, video.srcBufferSize / 2);
+ else
+ ColorspaceConvertBuffer888XTo8888Opaque((u32*)video.srcBuffer, video.buffer, video.srcBufferSize / 4);
+
+ //some games use the backlight for fading effects
+ const size_t pixCount = video.prefilterWidth * video.prefilterHeight / 2;
+ const NDSDisplayInfo &displayInfo = GPU->GetDisplayInfo();
+ ColorspaceApplyIntensityToBuffer32(video.buffer, pixCount, displayInfo.backlightIntensity[NDSDisplayID_Main]);
+ ColorspaceApplyIntensityToBuffer32(video.buffer + pixCount, pixCount, displayInfo.backlightIntensity[NDSDisplayID_Touch]);
+
+ // Lua draws to the HUD buffer, so clear it here instead of right before redrawing the HUD.
+ aggDraw.hud->clear();
+ if (AnyLuaActive())
+ {
+ if (sthread_isself(display_thread))
+ {
+ InvokeOnMainThread((void(*)(DWORD))
+ CallRegisteredLuaFunctions, LUACALL_AFTEREMULATIONGUI);
+ }
+ else
+ {
+ CallRegisteredLuaFunctions(LUACALL_AFTEREMULATIONGUI);
+ }
+ }
+
+ // draw hud
+ DoDisplay_DrawHud();
+ if (displayMethod == DISPMETHOD_DDRAW_HW || displayMethod == DISPMETHOD_DDRAW_SW)
+ {
+ // DirectDraw doesn't support alpha blending, so we must scale and overlay the HUD ourselves.
+ T_AGG_RGBA target((u8*)video.buffer, video.prefilterWidth, video.prefilterHeight, video.prefilterWidth * 4);
+ target.transformImage(aggDraw.hud->image(), 0, 0, video.prefilterWidth, video.prefilterHeight);
+ }
+
+ //apply user's filter
+ video.filter();
+
+ if (displayMethod == DISPMETHOD_DDRAW_HW || displayMethod == DISPMETHOD_DDRAW_SW)
+ {
+ gldisplay.kill();
+ DD_DoDisplay();
+ }
+ else
+ {
+ OGL_DoDisplay();
+ }
+}
+
+void KillDisplay()
+{
+ display_die = true;
+ SetEvent(display_wakeup_event);
+ if (display_thread)
+ sthread_join(display_thread);
+}
+
+void UpdateWndRects(HWND hwnd, RECT* newClientRect)
+{
+ POINT ptClient;
+ RECT rc;
+
+ GapRect = {0,0,0,0};
+
+ bool maximized = IsZoomed(hwnd) != FALSE;
+
+ int wndWidth, wndHeight;
+ int defHeight = video.height;
+ if (video.layout == 0)
+ defHeight += video.scaledscreengap();
+ float ratio;
+ int oneScreenHeight, gapHeight;
+ int tbheight;
+
+ //if we're in the middle of resizing the window, GetClientRect will return the old rect
+ if (newClientRect)
+ rc = RECT(*newClientRect);
+ else
+ GetClientRect(hwnd, &rc);
+
+ if (maximized)
+ rc = FullScreenRect;
+
+ tbheight = MainWindowToolbar->GetHeight();
+
+ if (video.layout == 1) //horizontal
+ {
+ rc = CalculateDisplayLayoutWrapper(rc, (int)((float)GPU_FRAMEBUFFER_NATIVE_WIDTH * 2 / screenSizeRatio), GPU_FRAMEBUFFER_NATIVE_HEIGHT, tbheight, maximized);
+
+ wndWidth = (rc.bottom - rc.top) - tbheight;
+ wndHeight = (rc.right - rc.left);
+
+ ratio = ((float)wndHeight / (float)512);
+ oneScreenHeight = (int)((float)GPU_FRAMEBUFFER_NATIVE_WIDTH * ratio);
+ int oneScreenWidth = (int)((float)GPU_FRAMEBUFFER_NATIVE_HEIGHT * ratio);
+ int vResizedScrOffset = 0;
+
+ // Main screen
+ ptClient.x = rc.left;
+ ptClient.y = rc.top;
+ ClientToScreen(hwnd, &ptClient);
+ MainScreenRect.left = ptClient.x;
+ MainScreenRect.top = ptClient.y;
+ ptClient.x = (rc.left + oneScreenHeight * screenSizeRatio);
+ ptClient.y = (rc.top + wndWidth);
+ ClientToScreen(hwnd, &ptClient);
+ MainScreenRect.right = ptClient.x;
+ MainScreenRect.bottom = ptClient.y;
+
+ // Sub screen
+ ptClient.x = (rc.left + oneScreenHeight * screenSizeRatio);
+ if (vCenterResizedScr && ForceRatio)
+ vResizedScrOffset = (wndWidth - oneScreenWidth * (2 - screenSizeRatio)) / 2;
+ ptClient.y = rc.top + vResizedScrOffset;
+ ClientToScreen(hwnd, &ptClient);
+ SubScreenRect.left = ptClient.x;
+ SubScreenRect.top = ptClient.y;
+ ptClient.x = (rc.left + oneScreenHeight * 2);
+ if (ForceRatio)
+ ptClient.y = (rc.top + vResizedScrOffset + oneScreenWidth * (2 - screenSizeRatio));
+ else
+ ptClient.y = (rc.top + wndWidth);
+ ClientToScreen(hwnd, &ptClient);
+ SubScreenRect.right = ptClient.x;
+ SubScreenRect.bottom = ptClient.y;
+ }
+ else
+ if (video.layout == 2) //one screen
+ {
+ rc = CalculateDisplayLayoutWrapper(rc, GPU_FRAMEBUFFER_NATIVE_WIDTH, GPU_FRAMEBUFFER_NATIVE_HEIGHT, tbheight, maximized);
+
+ wndWidth = (rc.bottom - rc.top) - tbheight;
+ wndHeight = (rc.right - rc.left);
+
+ ratio = ((float)wndHeight / (float)defHeight);
+ oneScreenHeight = (int)((video.height) * ratio);
+
+ // Main screen
+ ptClient.x = rc.left;
+ ptClient.y = rc.top;
+ ClientToScreen(hwnd, &ptClient);
+ MainScreenRect.left = ptClient.x;
+ MainScreenRect.top = ptClient.y;
+ ptClient.x = (rc.left + oneScreenHeight);
+ ptClient.y = (rc.top + wndWidth);
+ ClientToScreen(hwnd, &ptClient);
+ MainScreenRect.right = ptClient.x;
+ MainScreenRect.bottom = ptClient.y;
+ SetRectEmpty(&SubScreenRect);
+ }
+ else
+ if (video.layout == 0) //vertical
+ {
+ //apply logic to correct things if forced integer is selected
+ if ((video.rotation == 90) || (video.rotation == 270))
+ {
+ rc = CalculateDisplayLayoutWrapper(rc, (GPU_FRAMEBUFFER_NATIVE_HEIGHT * 2) + video.screengap, GPU_FRAMEBUFFER_NATIVE_WIDTH, tbheight, maximized);
+ }
+ else
+ {
+ rc = CalculateDisplayLayoutWrapper(rc, GPU_FRAMEBUFFER_NATIVE_WIDTH, (GPU_FRAMEBUFFER_NATIVE_HEIGHT * 2) + video.screengap, tbheight, maximized);
+ }
+
+ if ((video.rotation == 90) || (video.rotation == 270))
+ {
+ wndWidth = (rc.bottom - rc.top) - tbheight;
+ wndHeight = (rc.right - rc.left);
+ }
+ else
+ {
+ wndWidth = (rc.right - rc.left);
+ wndHeight = (rc.bottom - rc.top) - tbheight;
+ }
+
+ ratio = ((float)wndHeight / (float)defHeight);
+
+ oneScreenHeight = (int)((video.height / 2) * ratio);
+ gapHeight = (wndHeight - (oneScreenHeight * 2));
+
+ if ((video.rotation == 90) || (video.rotation == 270))
+ {
+ // Main screen
+ ptClient.x = rc.left;
+ ptClient.y = rc.top;
+ ClientToScreen(hwnd, &ptClient);
+ MainScreenRect.left = ptClient.x;
+ MainScreenRect.top = ptClient.y;
+ ptClient.x = (rc.left + oneScreenHeight);
+ ptClient.y = (rc.top + wndWidth);
+ ClientToScreen(hwnd, &ptClient);
+ MainScreenRect.right = ptClient.x;
+ MainScreenRect.bottom = ptClient.y;
+
+ //if there was no specified screen gap, extend the top screen to cover the extra column
+ if (video.screengap == 0) MainScreenRect.right += gapHeight;
+
+ // Sub screen
+ ptClient.x = (rc.left + oneScreenHeight + gapHeight);
+ ptClient.y = rc.top;
+ ClientToScreen(hwnd, &ptClient);
+ SubScreenRect.left = ptClient.x;
+ SubScreenRect.top = ptClient.y;
+ ptClient.x = (rc.left + oneScreenHeight + gapHeight + oneScreenHeight);
+ ptClient.y = (rc.top + wndWidth);
+ ClientToScreen(hwnd, &ptClient);
+ SubScreenRect.right = ptClient.x;
+ SubScreenRect.bottom = ptClient.y;
+
+ // Gap
+ GapRect.left = (rc.left + oneScreenHeight);
+ GapRect.top = rc.top;
+ GapRect.right = (rc.left + oneScreenHeight + gapHeight);
+ GapRect.bottom = (rc.top + wndWidth);
+ }
+ else
+ {
+
+
+ // Main screen
+ ptClient.x = rc.left;
+ ptClient.y = rc.top;
+ ClientToScreen(hwnd, &ptClient);
+ MainScreenRect.left = ptClient.x;
+ MainScreenRect.top = ptClient.y;
+ ptClient.x = (rc.left + wndWidth);
+ ptClient.y = (rc.top + oneScreenHeight);
+ ClientToScreen(hwnd, &ptClient);
+ MainScreenRect.right = ptClient.x;
+ MainScreenRect.bottom = ptClient.y;
+
+ //if there was no specified screen gap, extend the top screen to cover the extra row
+ if (video.screengap == 0) MainScreenRect.bottom += gapHeight;
+
+ // Sub screen
+ ptClient.x = rc.left;
+ ptClient.y = (rc.top + oneScreenHeight + gapHeight);
+ ClientToScreen(hwnd, &ptClient);
+ SubScreenRect.left = ptClient.x;
+ SubScreenRect.top = ptClient.y;
+ ptClient.x = (rc.left + wndWidth);
+ ptClient.y = (rc.top + oneScreenHeight + gapHeight + oneScreenHeight);
+ ClientToScreen(hwnd, &ptClient);
+ SubScreenRect.right = ptClient.x;
+ SubScreenRect.bottom = ptClient.y;
+
+ // Gap
+ GapRect.left = rc.left;
+ GapRect.top = (rc.top + oneScreenHeight);
+ GapRect.right = (rc.left + wndWidth);
+ GapRect.bottom = (rc.top + oneScreenHeight + gapHeight);
+ }
+ }
+
+ MainScreenRect.top += tbheight;
+ MainScreenRect.bottom += tbheight;
+ SubScreenRect.top += tbheight;
+ SubScreenRect.bottom += tbheight;
+ GapRect.top += tbheight;
+ GapRect.bottom += tbheight;
+}
+
+static void InvokeOnMainThread(void(*function)(DWORD), DWORD argument)
+{
+ ResetEvent(display_invoke_ready_event);
+ display_invoke_argument = argument;
+ display_invoke_function = function;
+ PostMessage(MainWindow->getHWnd(), WM_CUSTINVOKE, 0, 0); // in case a modal dialog or menu is open
+ SignalObjectAndWait(display_invoke_ready_event, display_invoke_done_event, display_invoke_timeout, FALSE);
+ display_invoke_function = NULL;
+}
+void _ServiceDisplayThreadInvocation()
+{
+ Lock lock(display_invoke_handler_cs);
+ DWORD res = WaitForSingleObject(display_invoke_ready_event, display_invoke_timeout);
+ if (res != WAIT_ABANDONED && display_invoke_function)
+ display_invoke_function(display_invoke_argument);
+ display_invoke_function = NULL;
+ SetEvent(display_invoke_done_event);
+}
diff --git a/desmume/src/frontend/windows/display.h b/desmume/src/frontend/windows/display.h
new file mode 100644
index 000000000..e43c801fa
--- /dev/null
+++ b/desmume/src/frontend/windows/display.h
@@ -0,0 +1,108 @@
+/*
+Copyright (C) 2018 DeSmuME team
+
+This file is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This file 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 the this software. If not, see .
+*/
+
+#ifndef _DISPLAY_H_
+#define _DISPLAY_H_
+
+#include "main.h"
+#include "NDSSystem.h"
+#include "debug.h"
+#include "GPU.h"
+#include "resource.h"
+#include "lua-engine.h"
+#include "video.h"
+#include "frontend/modules/osd/agg/agg_osd.h"
+#include "rthreads/rthreads.h"
+#include "ddraw.h"
+#include "ogl_display.h"
+
+extern DDRAW ddraw;
+extern GLDISPLAY gldisplay;
+extern u32 displayMethod;
+const u32 DISPMETHOD_DDRAW_HW = 1;
+const u32 DISPMETHOD_DDRAW_SW = 2;
+const u32 DISPMETHOD_OPENGL = 3;
+
+extern int gpu_bpp;
+
+extern int emu_paused;
+extern bool frameAdvance;
+extern int frameskiprate;
+extern int lastskiprate;
+extern VideoInfo video;
+
+extern RECT FullScreenRect, MainScreenRect, SubScreenRect, GapRect;
+extern RECT MainScreenSrcRect, SubScreenSrcRect;
+const int kGapNone = 0;
+const int kGapBorder = 5;
+const int kGapNDS = 64; // extremely tilted (but some games seem to use this value)
+const int kGapNDS2 = 90; // more normal viewing angle
+const int kScale1point5x = 65535;
+const int kScale2point5x = 65534;
+
+extern bool ForceRatio;
+extern bool PadToInteger;
+extern float screenSizeRatio;
+extern bool vCenterResizedScr;
+
+extern bool SeparationBorderDrag;
+extern int ScreenGapColor;
+
+extern slock_t *display_mutex;
+extern HANDLE display_wakeup_event;
+extern HANDLE display_done_event;
+extern DWORD display_done_timeout;
+
+extern int displayPostponeType;
+extern DWORD displayPostponeUntil;
+extern bool displayNoPostponeNext;
+
+extern void(*display_invoke_function)(DWORD);
+extern HANDLE display_invoke_ready_event;
+extern HANDLE display_invoke_done_event;
+extern DWORD display_invoke_timeout;
+extern CRITICAL_SECTION display_invoke_handler_cs;
+
+// Scanline filter parameters. The first set is from commandline.cpp, the second
+// set is externed to scanline.cpp.
+// TODO: When videofilter.cpp becomes Windows-friendly, remove the second set of
+// variables, as they will no longer be necessary at that point.
+extern int _scanline_filter_a;
+extern int _scanline_filter_b;
+extern int _scanline_filter_c;
+extern int _scanline_filter_d;
+extern int scanline_filter_a;
+extern int scanline_filter_b;
+extern int scanline_filter_c;
+extern int scanline_filter_d;
+
+void Display();
+void DoDisplay();
+void KillDisplay();
+
+void GetNdsScreenRect(RECT* r);
+
+void _ServiceDisplayThreadInvocation();
+FORCEINLINE void ServiceDisplayThreadInvocations()
+{
+ if (display_invoke_function)
+ _ServiceDisplayThreadInvocation();
+}
+
+void UpdateWndRects(HWND hwnd, RECT* newClientRect = NULL);
+
+#endif
\ No newline at end of file
diff --git a/desmume/src/frontend/windows/main.cpp b/desmume/src/frontend/windows/main.cpp
index 241ba08f7..d140dd503 100755
--- a/desmume/src/frontend/windows/main.cpp
+++ b/desmume/src/frontend/windows/main.cpp
@@ -91,6 +91,7 @@
#include "frontend/modules/ImageOut.h"
#include "winutil.h"
#include "ogl.h"
+#include "display.h"
//tools and dialogs
#include "pathsettings.h"
@@ -296,19 +297,10 @@ msgBoxInterface msgBoxWnd = {
#include "winpcap.h"
-VideoInfo video;
-
#ifndef PUBLIC_RELEASE
#define DEVELOPER_MENU_ITEMS
#endif
-const int kGapNone = 0;
-const int kGapBorder = 5;
-const int kGapNDS = 64; // extremely tilted (but some games seem to use this value)
-const int kGapNDS2 = 90; // more normal viewing angle
-const int kScale1point5x = 65535;
-const int kScale2point5x = 65534;
-
static BOOL OpenCore(const char* filename);
BOOL Mic_DeInit_Physical();
BOOL Mic_Init_Physical();
@@ -324,8 +316,6 @@ bool start_paused;
extern bool killStylusTopScreen;
extern bool killStylusOffScreen;
-int gpu_bpp = 18;
-
extern LRESULT CALLBACK RamSearchProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
void InitRamSearch();
void FilterUpdate(HWND hwnd, bool user=true);
@@ -340,49 +330,11 @@ Lock::Lock() : m_cs(&win_execute_sync) { EnterCriticalSection(m_cs); }
Lock::Lock(CRITICAL_SECTION& cs) : m_cs(&cs) { EnterCriticalSection(m_cs); }
Lock::~Lock() { LeaveCriticalSection(m_cs); }
-//====================== DirectDraw
-const char *DDerrors[] = { "no errors",
- "Unable to initialize DirectDraw",
- "Unable to set DirectDraw Cooperative Level",
- "Unable to create DirectDraw primary surface",
- "Unable to set DirectDraw clipper"};
-struct DDRAW
-{
- DDRAW():
- handle(NULL), clip(NULL)
- {
- surface.primary = NULL;
- surface.back = NULL;
- memset(&surfDesc, 0, sizeof(surfDesc));
- memset(&surfDescBack, 0, sizeof(surfDescBack));
- }
-
- u32 create(HWND hwnd);
- bool release();
- bool createSurfaces(HWND hwnd);
- bool createBackSurface(int width, int height);
- bool lock();
- bool unlock();
- bool blt(LPRECT dst, LPRECT src);
- bool OK();
-
- LPDIRECTDRAW7 handle;
- struct
- {
- LPDIRECTDRAWSURFACE7 primary;
- LPDIRECTDRAWSURFACE7 back;
- } surface;
-
- DDSURFACEDESC2 surfDesc;
- DDSURFACEDESC2 surfDescBack;
- LPDIRECTDRAWCLIPPER clip;
-} ddraw;
-
//===================== Input vars
#define WM_CUSTKEYDOWN (WM_USER+50)
#define WM_CUSTKEYUP (WM_USER+51)
-#define WM_CUSTINVOKE (WM_USER+52)
+//#define WM_CUSTINVOKE (WM_USER+52)
#ifndef __WISPSHRD_H
#define WM_TABLET_DEFBASE 0x02C0
@@ -420,8 +372,6 @@ bool gConsoleTopmost = false;
//HDC hdc;
HACCEL hAccel;
HINSTANCE hAppInst = NULL;
-RECT FullScreenRect, MainScreenRect, SubScreenRect, GapRect;
-RECT MainScreenSrcRect, SubScreenSrcRect;
int WndX = 0;
int WndY = 0;
@@ -454,28 +404,11 @@ volatile bool paused = true;
volatile BOOL pausedByMinimize = FALSE;
u32 glock = 0;
-// Scanline filter parameters. The first set is from commandline.cpp, the second
-// set is externed to scanline.cpp.
-// TODO: When videofilter.cpp becomes Windows-friendly, remove the second set of
-// variables, as they will no longer be necessary at that point.
-extern int _scanline_filter_a;
-extern int _scanline_filter_b;
-extern int _scanline_filter_c;
-extern int _scanline_filter_d;
-int scanline_filter_a = 0;
-int scanline_filter_b = 2;
-int scanline_filter_c = 2;
-int scanline_filter_d = 4;
-
BOOL finished = FALSE;
bool romloaded = false;
void SetScreenGap(int gap);
-bool ForceRatio = true;
-bool PadToInteger = false;
-bool SeparationBorderDrag = true;
-int ScreenGapColor = 0xFFFFFF;
float DefaultWidth;
float DefaultHeight;
float widthTradeOff;
@@ -507,38 +440,12 @@ GPU3DInterface *core3DList[] = {
};
bool autoframeskipenab=1;
-int frameskiprate=0;
-int lastskiprate=0;
-int emu_paused = 0;
-bool frameAdvance = false;
bool continuousframeAdvancing = false;
unsigned short windowSize = 0;
bool fsWindow = false;
bool autoHideCursor = false;
-float screenSizeRatio = 1.0f;
-bool vCenterResizedScr = true;
-
-/*const u32 gapColors[16] = {
- Color::Gray,
- Color::Brown,
- Color::Red,
- Color::Pink,
- Color::Orange,
- Color::Yellow,
- Color::LightGreen,
- Color::Lime,
- Color::Green,
- Color::SeaGreen,
- Color::SkyBlue,
- Color::Blue,
- Color::DarkBlue,
- Color::DarkViolet,
- Color::Purple,
- Color::Fuchsia
-};*/
-
class GPUEventHandlerWindows : public GPUEventHandlerDefault
{
public:
@@ -778,23 +685,6 @@ void SetMinWindowSize()
MainWindow->setMinSize(video.rotatedwidthgap(), video.rotatedheightgap());
}
-static void GetNdsScreenRect(RECT* r)
-{
- RECT zero;
- SetRect(&zero,0,0,0,0);
- if(zero == MainScreenRect) *r = SubScreenRect;
- else if(zero == SubScreenRect || video.layout == 2) *r = MainScreenRect;
- else
- {
- RECT* dstRects [2] = {&MainScreenRect, &SubScreenRect};
- int left = min(dstRects[0]->left,dstRects[1]->left);
- int top = min(dstRects[0]->top,dstRects[1]->top);
- int right = max(dstRects[0]->right,dstRects[1]->right);
- int bottom = max(dstRects[0]->bottom,dstRects[1]->bottom);
- SetRect(r,left,top,right,bottom);
- }
-}
-
void UnscaleScreenCoords(s32& x, s32& y)
{
HWND hwnd = MainWindow->getHWnd();
@@ -966,23 +856,12 @@ void ToDSScreenRelativeCoords(s32& x, s32& y, int whichScreen)
}
}
-// END Rotation definitions
-
//-----window style handling----
-const u32 DISPMETHOD_DDRAW_HW = 1;
-const u32 DISPMETHOD_DDRAW_SW = 2;
-const u32 DISPMETHOD_OPENGL = 3;
-
const u32 DWS_NORMAL = 0;
const u32 DWS_ALWAYSONTOP = 1;
const u32 DWS_LOCKDOWN = 2;
const u32 DWS_FULLSCREEN = 4;
const u32 DWS_VSYNC = 8;
-const u32 DWS_DDRAW_SW = 16;
-const u32 DWS_DDRAW_HW = 32;
-const u32 DWS_OPENGL = 64;
-const u32 DWS_DISPMETHODS = (DWS_DDRAW_SW|DWS_DDRAW_HW|DWS_OPENGL);
-const u32 DWS_FILTER = 128;
const u32 DWS_FS_MENU = 256;
const u32 DWS_FS_WINDOW = 512;
@@ -1141,347 +1020,6 @@ static void PopulateLuaSubmenu()
}
}
-template static void doRotate(void* dst)
-{
- u8* buffer = (u8*)dst;
- int size = video.size();
- u32* src = (u32*)video.finalBuffer();
- int width = video.width;
- int height = video.height;
- int pitch = ddraw.surfDescBack.lPitch;
-
- switch(video.rotation)
- {
- case 0:
- case 180:
- {
- if(pitch == 1024)
- {
- if(video.rotation==0)
- if(sizeof(T) == sizeof(u32))
- memcpy(buffer, src, size * sizeof(u32));
- else
- for(int i = 0; i < size; i++)
- ((T*)buffer)[i] = src[i];
- else // if(video.rotation==180)
- for(int i = 0, j=size-1; j>=0; i++,j--)
- ((T*)buffer)[i] = src[j];
- }
- else
- {
- if(video.rotation==0)
- if(sizeof(T) != sizeof(u32))
- for(int y = 0; y < height; y++)
- {
- for(int x = 0; x < width; x++)
- ((T*)buffer)[x] = src[(y * width) + x];
-
- buffer += pitch;
- }
- else
- for(int y = 0; y < height; y++)
- {
- memcpy(buffer, &src[y * width], width * sizeof(u32));
- buffer += pitch;
- }
- else // if(video.rotation==180)
- for(int y = 0; y < height; y++)
- {
- for(int x = 0; x < width; x++)
- ((T*)buffer)[x] = src[height*width - (y * width) - x - 1];
-
- buffer += pitch;
- }
- }
- }
- break;
- case 90:
- case 270:
- {
- if(video.rotation == 90)
- for(int y = 0; y < width; y++)
- {
- for(int x = 0; x < height; x++)
- ((T*)buffer)[x] = src[(((height-1)-x) * width) + y];
-
- buffer += pitch;
- }
- else
- for(int y = 0; y < width; y++)
- {
- for(int x = 0; x < height; x++)
- ((T*)buffer)[x] = src[((x) * width) + (width-1) - y];
-
- buffer += pitch;
- }
- }
- break;
- }
-}
-
-struct DisplayLayoutInfo
-{
- int vx,vy,vw,vh;
- float widthScale, heightScale;
- int bufferWidth, bufferHeight;
-};
-
-//performs aspect ratio letterboxing correction and integer clamping
-DisplayLayoutInfo CalculateDisplayLayout(RECT rcClient, bool maintainAspect, bool maintainInteger, int targetWidth, int targetHeight)
-{
- DisplayLayoutInfo ret;
-
- //do maths on the viewport and the native resolution and the user settings to get a display rectangle
- SIZE sz = { rcClient.right - rcClient.left, rcClient.bottom - rcClient.top };
-
- float widthScale = (float)sz.cx / targetWidth;
- float heightScale = (float)sz.cy / targetHeight;
- if(maintainAspect)
- {
- if(widthScale > heightScale) widthScale = heightScale;
- if(heightScale > widthScale) heightScale = widthScale;
- }
- if(maintainInteger)
- {
- widthScale = floorf(widthScale);
- heightScale = floorf(heightScale);
- }
- ret.vw = (int)(widthScale * targetWidth);
- ret.vh = (int)(heightScale * targetHeight);
- ret.vx = (sz.cx - ret.vw)/2;
- ret.vy = (sz.cy - ret.vh)/2;
- ret.widthScale = widthScale;
- ret.heightScale = heightScale;
- ret.bufferWidth = sz.cx;
- ret.bufferHeight = sz.cy;
-
- return ret;
-}
-
-//reformulates CalculateDisplayLayout() into a format more convenient for this purpose
-RECT CalculateDisplayLayoutWrapper(RECT rcClient, int targetWidth, int targetHeight, int tbHeight, bool maximized)
-{
- bool maintainInteger = !!PadToInteger;
- bool maintainAspect = !!ForceRatio;
-
- if(maintainInteger) maintainAspect = true;
-
- //nothing to do here if maintain aspect isnt chosen
- if(!maintainAspect) return rcClient;
-
- RECT rc = { rcClient.left, rcClient.top + tbHeight, rcClient.right, rcClient.bottom };
- DisplayLayoutInfo dli = CalculateDisplayLayout(rc, maintainAspect, maintainInteger, targetWidth, targetHeight);
- rc.left = rcClient.left + dli.vx;
- rc.top = rcClient.top + dli.vy;
- rc.right = rc.left + dli.vw;
- rc.bottom = rc.top + dli.vh + tbHeight;
- return rc;
-}
-
-void UpdateWndRects(HWND hwnd, RECT* newClientRect = NULL)
-{
- POINT ptClient;
- RECT rc;
-
- GapRect = {0,0,0,0};
-
- bool maximized = IsZoomed(hwnd)!=FALSE;
-
- int wndWidth, wndHeight;
- int defHeight = video.height;
- if(video.layout == 0)
- defHeight += video.scaledscreengap();
- float ratio;
- int oneScreenHeight, gapHeight;
- int tbheight;
-
- //if we're in the middle of resizing the window, GetClientRect will return the old rect
- if (newClientRect)
- rc = RECT(*newClientRect);
- else
- GetClientRect(hwnd, &rc);
-
- if(maximized)
- rc = FullScreenRect;
-
- tbheight = MainWindowToolbar->GetHeight();
-
- if (video.layout == 1) //horizontal
- {
- rc = CalculateDisplayLayoutWrapper(rc, (int)((float)GPU_FRAMEBUFFER_NATIVE_WIDTH * 2 / screenSizeRatio), GPU_FRAMEBUFFER_NATIVE_HEIGHT, tbheight, maximized);
-
- wndWidth = (rc.bottom - rc.top) - tbheight;
- wndHeight = (rc.right - rc.left);
-
- ratio = ((float)wndHeight / (float)512);
- oneScreenHeight = (int)((float)GPU_FRAMEBUFFER_NATIVE_WIDTH * ratio);
- int oneScreenWidth = (int)((float)GPU_FRAMEBUFFER_NATIVE_HEIGHT * ratio);
- int vResizedScrOffset = 0;
-
- // Main screen
- ptClient.x = rc.left;
- ptClient.y = rc.top;
- ClientToScreen(hwnd, &ptClient);
- MainScreenRect.left = ptClient.x;
- MainScreenRect.top = ptClient.y;
- ptClient.x = (rc.left + oneScreenHeight * screenSizeRatio);
- ptClient.y = (rc.top + wndWidth);
- ClientToScreen(hwnd, &ptClient);
- MainScreenRect.right = ptClient.x;
- MainScreenRect.bottom = ptClient.y;
-
- // Sub screen
- ptClient.x = (rc.left + oneScreenHeight * screenSizeRatio);
- if(vCenterResizedScr && ForceRatio)
- vResizedScrOffset = (wndWidth - oneScreenWidth * (2 - screenSizeRatio)) / 2;
- ptClient.y = rc.top + vResizedScrOffset;
- ClientToScreen(hwnd, &ptClient);
- SubScreenRect.left = ptClient.x;
- SubScreenRect.top = ptClient.y;
- ptClient.x = (rc.left + oneScreenHeight * 2);
- if(ForceRatio)
- ptClient.y = (rc.top + vResizedScrOffset + oneScreenWidth * (2 - screenSizeRatio));
- else
- ptClient.y = (rc.top + wndWidth);
- ClientToScreen(hwnd, &ptClient);
- SubScreenRect.right = ptClient.x;
- SubScreenRect.bottom = ptClient.y;
- }
- else
- if (video.layout == 2) //one screen
- {
- rc = CalculateDisplayLayoutWrapper(rc, GPU_FRAMEBUFFER_NATIVE_WIDTH, GPU_FRAMEBUFFER_NATIVE_HEIGHT, tbheight, maximized);
-
- wndWidth = (rc.bottom - rc.top) - tbheight;
- wndHeight = (rc.right - rc.left);
-
- ratio = ((float)wndHeight / (float)defHeight);
- oneScreenHeight = (int)((video.height) * ratio);
-
- // Main screen
- ptClient.x = rc.left;
- ptClient.y = rc.top;
- ClientToScreen(hwnd, &ptClient);
- MainScreenRect.left = ptClient.x;
- MainScreenRect.top = ptClient.y;
- ptClient.x = (rc.left + oneScreenHeight);
- ptClient.y = (rc.top + wndWidth);
- ClientToScreen(hwnd, &ptClient);
- MainScreenRect.right = ptClient.x;
- MainScreenRect.bottom = ptClient.y;
- SetRectEmpty(&SubScreenRect);
- }
- else
- if (video.layout == 0) //vertical
- {
- //apply logic to correct things if forced integer is selected
- if((video.rotation == 90) || (video.rotation == 270))
- {
- rc = CalculateDisplayLayoutWrapper(rc, (GPU_FRAMEBUFFER_NATIVE_HEIGHT*2) + video.screengap, GPU_FRAMEBUFFER_NATIVE_WIDTH, tbheight, maximized);
- }
- else
- {
- rc = CalculateDisplayLayoutWrapper(rc, GPU_FRAMEBUFFER_NATIVE_WIDTH, (GPU_FRAMEBUFFER_NATIVE_HEIGHT*2) + video.screengap, tbheight, maximized);
- }
-
- if((video.rotation == 90) || (video.rotation == 270))
- {
- wndWidth = (rc.bottom - rc.top) - tbheight;
- wndHeight = (rc.right - rc.left);
- }
- else
- {
- wndWidth = (rc.right - rc.left);
- wndHeight = (rc.bottom - rc.top) - tbheight;
- }
-
- ratio = ((float)wndHeight / (float)defHeight);
-
- oneScreenHeight = (int)((video.height/2) * ratio);
- gapHeight = (wndHeight - (oneScreenHeight * 2));
-
- if((video.rotation == 90) || (video.rotation == 270))
- {
- // Main screen
- ptClient.x = rc.left;
- ptClient.y = rc.top;
- ClientToScreen(hwnd, &ptClient);
- MainScreenRect.left = ptClient.x;
- MainScreenRect.top = ptClient.y;
- ptClient.x = (rc.left + oneScreenHeight);
- ptClient.y = (rc.top + wndWidth);
- ClientToScreen(hwnd, &ptClient);
- MainScreenRect.right = ptClient.x;
- MainScreenRect.bottom = ptClient.y;
-
- //if there was no specified screen gap, extend the top screen to cover the extra column
- if(video.screengap == 0) MainScreenRect.right += gapHeight;
-
- // Sub screen
- ptClient.x = (rc.left + oneScreenHeight + gapHeight);
- ptClient.y = rc.top;
- ClientToScreen(hwnd, &ptClient);
- SubScreenRect.left = ptClient.x;
- SubScreenRect.top = ptClient.y;
- ptClient.x = (rc.left + oneScreenHeight + gapHeight + oneScreenHeight);
- ptClient.y = (rc.top + wndWidth);
- ClientToScreen(hwnd, &ptClient);
- SubScreenRect.right = ptClient.x;
- SubScreenRect.bottom = ptClient.y;
-
- // Gap
- GapRect.left = (rc.left + oneScreenHeight);
- GapRect.top = rc.top;
- GapRect.right = (rc.left + oneScreenHeight + gapHeight);
- GapRect.bottom = (rc.top + wndWidth);
- }
- else
- {
-
-
- // Main screen
- ptClient.x = rc.left;
- ptClient.y = rc.top;
- ClientToScreen(hwnd, &ptClient);
- MainScreenRect.left = ptClient.x;
- MainScreenRect.top = ptClient.y;
- ptClient.x = (rc.left + wndWidth);
- ptClient.y = (rc.top + oneScreenHeight);
- ClientToScreen(hwnd, &ptClient);
- MainScreenRect.right = ptClient.x;
- MainScreenRect.bottom = ptClient.y;
-
- //if there was no specified screen gap, extend the top screen to cover the extra row
- if(video.screengap == 0) MainScreenRect.bottom += gapHeight;
-
- // Sub screen
- ptClient.x = rc.left;
- ptClient.y = (rc.top + oneScreenHeight + gapHeight);
- ClientToScreen(hwnd, &ptClient);
- SubScreenRect.left = ptClient.x;
- SubScreenRect.top = ptClient.y;
- ptClient.x = (rc.left + wndWidth);
- ptClient.y = (rc.top + oneScreenHeight + gapHeight + oneScreenHeight);
- ClientToScreen(hwnd, &ptClient);
- SubScreenRect.right = ptClient.x;
- SubScreenRect.bottom = ptClient.y;
-
- // Gap
- GapRect.left = rc.left;
- GapRect.top = (rc.top + oneScreenHeight);
- GapRect.right = (rc.left + wndWidth);
- GapRect.bottom = (rc.top + oneScreenHeight + gapHeight);
- }
- }
-
- MainScreenRect.top += tbheight;
- MainScreenRect.bottom += tbheight;
- SubScreenRect.top += tbheight;
- SubScreenRect.bottom += tbheight;
- GapRect.top += tbheight;
- GapRect.bottom += tbheight;
-}
-
void FixAspectRatio();
void LCDsSwap(int swapVal)
@@ -1657,650 +1195,6 @@ void doLCDsLayout(int videoLayout)
}
}
-#pragma pack(push,1)
-struct pix24
-{
- u8 b,g,r;
- FORCEINLINE pix24(u32 s) : b(s&0xFF), g((s&0xFF00)>>8), r((s&0xFF0000)>>16) {}
-};
-struct pix16
-{
- u16 c;
- FORCEINLINE pix16(u32 s) : c(((s&0xF8)>>3) | ((s&0xFC00)>>5) | ((s&0xF80000)>>8)) {}
-};
-struct pix15
-{
- u16 c;
- FORCEINLINE pix15(u32 s) : c(((s&0xF8)>>3) | ((s&0xF800)>>6) | ((s&0xF80000)>>9)) {}
-};
-#pragma pack(pop)
-
-static void DD_FillRect(LPDIRECTDRAWSURFACE7 surf, int left, int top, int right, int bottom, DWORD color)
-{
- RECT r;
- SetRect(&r,left,top,right,bottom);
- DDBLTFX fx;
- memset(&fx,0,sizeof(DDBLTFX));
- fx.dwSize = sizeof(DDBLTFX);
- //fx.dwFillColor = color;
- fx.dwFillColor = 0; //color is just for debug
- surf->Blt(&r,NULL,NULL,DDBLT_COLORFILL | DDBLT_WAIT,&fx);
-}
-
-struct GLDISPLAY
-{
- HGLRC privateContext;
- HDC privateDC;
- bool init;
- bool active;
- bool wantVsync, haveVsync;
-
- GLDISPLAY()
- : init(false)
- , active(false)
- , wantVsync(false)
- , haveVsync(false)
- {
- }
-
- bool initialize()
- {
- //do we need to use another HDC?
- if(init) return true;
- init = initContext(MainWindow->getHWnd(),&privateContext);
- setvsync(!!(GetStyle()&DWS_VSYNC));
- return init;
- }
-
- void kill()
- {
- if(!init) return;
- wglDeleteContext(privateContext);
- privateContext = NULL;
- haveVsync = false;
- init = false;
- }
-
- bool begin()
- {
- DWORD myThread = GetCurrentThreadId();
-
- //always use another context for display logic
- //1. if this is a single threaded process (3d rendering and display in the main thread) then alternating contexts is benign
- //2. if this is a multi threaded process (3d rendernig and display in other threads) then the display needs some context
-
- if(!init)
- {
- if(!initialize()) return false;
- }
-
- privateDC = GetDC(MainWindow->getHWnd());
- wglMakeCurrent(privateDC,privateContext);
-
- //go ahead and sync the vsync setting while we have the context
- if (wantVsync != haveVsync)
- _setvsync();
-
- return active = true;
- }
-
- void end()
- {
- wglMakeCurrent(NULL,privateContext);
- ReleaseDC(MainWindow->getHWnd(),privateDC);
- privateDC = NULL;
- active = false;
- }
-
-
-
- //http://stackoverflow.com/questions/589064/how-to-enable-vertical-sync-in-opengl
- bool WGLExtensionSupported(const char *extension_name)
- {
- // this is pointer to function which returns pointer to string with list of all wgl extensions
- PFNWGLGETEXTENSIONSSTRINGEXTPROC _wglGetExtensionsStringEXT = NULL;
-
- // determine pointer to wglGetExtensionsStringEXT function
- _wglGetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)wglGetProcAddress("wglGetExtensionsStringEXT");
-
- if (strstr(_wglGetExtensionsStringEXT(), extension_name) == NULL)
- {
- // string was not found
- return false;
- }
-
- // extension is supported
- return true;
- }
-
- void setvsync(bool vsync)
- {
- wantVsync = vsync;
- }
-
- void _setvsync()
- {
- //even if it doesn't work, we'll track it
- haveVsync = wantVsync;
-
- if (!WGLExtensionSupported("WGL_EXT_swap_control")) return;
-
- //http://stackoverflow.com/questions/589064/how-to-enable-vertical-sync-in-opengl
- PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;
- PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT = NULL;
- {
- // Extension is supported, init pointers.
- wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");
-
- // this is another function from WGL_EXT_swap_control extension
- wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC)wglGetProcAddress("wglGetSwapIntervalEXT");
- }
-
- wglSwapIntervalEXT(wantVsync ? 1 : 0);
- }
-
- void showPage()
- {
- SwapBuffers(privateDC);
- }
-} gldisplay;
-
-
-static void OGL_DrawTexture(RECT* srcRects, RECT* dstRects)
-{
- //don't change the original rects
- RECT sRects[2];
- sRects[0] = RECT(srcRects[0]);
- sRects[1] = RECT(srcRects[1]);
-
- //use clear+scissor for gap
- if (video.screengap > 0)
- {
- //adjust client rect into scissor rect (0,0 at bottomleft)
- glScissor(dstRects[2].left, dstRects[2].bottom, dstRects[2].right - dstRects[2].left, dstRects[2].top - dstRects[2].bottom);
-
- u32 color_rev = (u32)ScreenGapColor;
- int r = (color_rev >> 0) & 0xFF;
- int g = (color_rev >> 8) & 0xFF;
- int b = (color_rev >> 16) & 0xFF;
- glClearColor(r / 255.0f, g / 255.0f, b / 255.0f, 1.0f);
- glEnable(GL_SCISSOR_TEST);
- glClear(GL_COLOR_BUFFER_BIT);
- glDisable(GL_SCISSOR_TEST);
- }
-
- //draw two screens
- glBegin(GL_QUADS);
- for (int i = 0; i<2; i++)
- {
- //none of this makes any goddamn sense. dont even try.
- int idx = i;
- int ofs = 0;
- switch (video.rotation)
- {
- case 0:
- break;
- case 90:
- ofs = 3;
- idx = 1 - i;
- std::swap(sRects[idx].right, sRects[idx].bottom);
- std::swap(sRects[idx].left, sRects[idx].top);
- break;
- case 180:
- idx = 1 - i;
- ofs = 2;
- break;
- case 270:
- std::swap(sRects[idx].right, sRects[idx].bottom);
- std::swap(sRects[idx].left, sRects[idx].top);
- ofs = 1;
- break;
- }
- float u1 = sRects[idx].left / (float)video.width;
- float u2 = sRects[idx].right / (float)video.width;
- float v1 = sRects[idx].top / (float)video.height;
- float v2 = sRects[idx].bottom / (float)video.height;
- float u[] = { u1,u2,u2,u1 };
- float v[] = { v1,v1,v2,v2 };
-
- glTexCoord2f(u[(ofs + 0) % 4], v[(ofs + 0) % 4]);
- glVertex2i(dstRects[i].left, dstRects[i].top);
-
- glTexCoord2f(u[(ofs + 1) % 4], v[(ofs + 1) % 4]);
- glVertex2i(dstRects[i].right, dstRects[i].top);
-
- glTexCoord2f(u[(ofs + 2) % 4], v[(ofs + 2) % 4]);
- glVertex2i(dstRects[i].right, dstRects[i].bottom);
-
- glTexCoord2f(u[(ofs + 3) % 4], v[(ofs + 3) % 4]);
- glVertex2i(dstRects[i].left, dstRects[i].bottom);
- }
- glEnd();
-}
-static void OGL_DoDisplay()
-{
- if(!gldisplay.begin()) return;
-
- //the ds screen fills the texture entirely, so we dont have garbage at edge to worry about,
- //but we need to make sure this is clamped for when filtering is selected
- glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
-
- if(GetStyle()&DWS_FILTER)
- {
- glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
- }
- else
- {
- glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
- }
-
- RECT rc;
- HWND hwnd = MainWindow->getHWnd();
- GetClientRect(hwnd,&rc);
- int width = rc.right - rc.left;
- int height = rc.bottom - rc.top;
-
- glViewport(0,0,width,height);
-
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glOrtho(0.0f, (float)width, (float)height, 0.0f, -100.0f, 100.0f);
-
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
-
- //clear entire area, for cases where the screen is maximized
- glClearColor(0,0,0,0);
- glClear(GL_COLOR_BUFFER_BIT);
-
- // get dest and src rects
- RECT dr[] = {MainScreenRect, SubScreenRect, GapRect};
- for(int i=0;i<2;i++) //dont change gap rect, for some reason
- {
- ScreenToClient(hwnd,(LPPOINT)&dr[i].left);
- ScreenToClient(hwnd,(LPPOINT)&dr[i].right);
- }
- dr[2].bottom = height - dr[2].bottom;
- dr[2].top = height - dr[2].top;
-
- RECT srcRects [2];
- const bool isMainGPUFirst = (GPU->GetDisplayInfo().engineID[NDSDisplayID_Main] == GPUEngineID_Main);
-
- if(video.swap == 0)
- {
- srcRects[0] = MainScreenSrcRect;
- srcRects[1] = SubScreenSrcRect;
- if(osd) osd->swapScreens = false;
- }
- else if(video.swap == 1)
- {
- srcRects[0] = SubScreenSrcRect;
- srcRects[1] = MainScreenSrcRect;
- if(osd) osd->swapScreens = true;
- }
- else if(video.swap == 2)
- {
- srcRects[0] = (!isMainGPUFirst) ? SubScreenSrcRect : MainScreenSrcRect;
- srcRects[1] = (!isMainGPUFirst) ? MainScreenSrcRect : SubScreenSrcRect;
- if(osd) osd->swapScreens = !isMainGPUFirst;
- }
- else if(video.swap == 3)
- {
- srcRects[0] = (!isMainGPUFirst) ? MainScreenSrcRect : SubScreenSrcRect;
- srcRects[1] = (!isMainGPUFirst) ? SubScreenSrcRect : MainScreenSrcRect;
- if(osd) osd->swapScreens = isMainGPUFirst;
- }
-
- //printf("%d,%d %dx%d -- %d,%d %dx%d\n",
- // srcRects[0].left,srcRects[0].top, srcRects[0].right-srcRects[0].left, srcRects[0].bottom-srcRects[0].top,
- // srcRects[1].left,srcRects[1].top, srcRects[1].right-srcRects[1].left, srcRects[1].bottom-srcRects[1].top
- // );
-
- glEnable(GL_TEXTURE_2D);
- // draw DS display
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, video.width, video.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, video.finalBuffer());
- OGL_DrawTexture(srcRects, dr);
-
- // draw HUD
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glEnable(GL_BLEND);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, GPU_FRAMEBUFFER_NATIVE_WIDTH, GPU_FRAMEBUFFER_NATIVE_HEIGHT * 2, 0, GL_BGRA, GL_UNSIGNED_BYTE, aggDraw.hud->buf().buf());
- OGL_DrawTexture(srcRects, dr);
- glDisable(GL_BLEND);
-
- gldisplay.showPage();
-
- gldisplay.end();
-}
-
-//the directdraw final presentation portion of display, including rotating
-static void DD_DoDisplay()
-{
- if (!ddraw.lock()) return;
- char* buffer = (char*)ddraw.surfDescBack.lpSurface;
-
- if(ddraw.surfDescBack.dwWidth != video.rotatedwidth() || ddraw.surfDescBack.dwHeight != video.rotatedheight())
- {
- ddraw.createBackSurface(video.rotatedwidth(),video.rotatedheight());
- }
-
- switch (ddraw.surfDescBack.ddpfPixelFormat.dwRGBBitCount)
- {
- case 32:
- doRotate(ddraw.surfDescBack.lpSurface);
- break;
- case 24:
- doRotate(ddraw.surfDescBack.lpSurface);
- break;
- case 16:
- if (ddraw.surfDescBack.ddpfPixelFormat.dwGBitMask != 0x3E0)
- doRotate(ddraw.surfDescBack.lpSurface);
- else
- doRotate(ddraw.surfDescBack.lpSurface);
- break;
- case 0:
- break;
- default:
- INFO("Unsupported color depth: %i bpp\n", ddraw.surfDescBack.ddpfPixelFormat.dwRGBBitCount);
- //emu_halt();
- break;
- }
- if (!ddraw.unlock()) return;
-
- RECT* dstRects [2] = {&MainScreenRect, &SubScreenRect};
- RECT* srcRects [2];
- const bool isMainGPUFirst = (GPU->GetDisplayInfo().engineID[NDSDisplayID_Main] == GPUEngineID_Main);
-
- if(video.swap == 0)
- {
- srcRects[0] = &MainScreenSrcRect;
- srcRects[1] = &SubScreenSrcRect;
- if(osd) osd->swapScreens = false;
- }
- else if(video.swap == 1)
- {
- srcRects[0] = &SubScreenSrcRect;
- srcRects[1] = &MainScreenSrcRect;
- if(osd) osd->swapScreens = true;
- }
- else if(video.swap == 2)
- {
- srcRects[0] = (!isMainGPUFirst) ? &SubScreenSrcRect : &MainScreenSrcRect;
- srcRects[1] = (!isMainGPUFirst) ? &MainScreenSrcRect : &SubScreenSrcRect;
- if(osd) osd->swapScreens = !isMainGPUFirst;
- }
- else if(video.swap == 3)
- {
- srcRects[0] = (!isMainGPUFirst) ? &MainScreenSrcRect : &SubScreenSrcRect;
- srcRects[1] = (!isMainGPUFirst) ? &SubScreenSrcRect : &MainScreenSrcRect;
- if(osd) osd->swapScreens = isMainGPUFirst;
- }
-
- //this code fills in all the undrawn areas
- //it is probably a waste of time to keep black-filling all this, but we need to do it to be safe.
- {
- RECT wr;
- GetWindowRect(MainWindow->getHWnd(),&wr);
- RECT r;
- GetNdsScreenRect(&r);
- int left = r.left;
- int top = r.top;
- int right = r.right;
- int bottom = r.bottom;
- //printf("%d %d %d %d / %d %d %d %d\n",fullScreen.left,fullScreen.top,fullScreen.right,fullScreen.bottom,left,top,right,bottom);
- //printf("%d %d %d %d / %d %d %d %d\n",MainScreenRect.left,MainScreenRect.top,MainScreenRect.right,MainScreenRect.bottom,SubScreenRect.left,SubScreenRect.top,SubScreenRect.right,SubScreenRect.bottom);
- if(ddraw.OK())
- {
- DD_FillRect(ddraw.surface.primary,0,0,left,top,RGB(255,0,0)); //topleft
- DD_FillRect(ddraw.surface.primary,left,0,right,top,RGB(128,0,0)); //topcenter
- DD_FillRect(ddraw.surface.primary,right,0,wr.right,top,RGB(0,255,0)); //topright
- DD_FillRect(ddraw.surface.primary,0,top,left,bottom,RGB(0,128,0)); //left
- DD_FillRect(ddraw.surface.primary,right,top,wr.right,bottom,RGB(0,0,255)); //right
- DD_FillRect(ddraw.surface.primary,0,bottom,left,wr.bottom,RGB(0,0,128)); //bottomleft
- DD_FillRect(ddraw.surface.primary,left,bottom,right,wr.bottom,RGB(255,0,255)); //bottomcenter
- DD_FillRect(ddraw.surface.primary,right,bottom,wr.right,wr.bottom,RGB(0,255,255)); //bottomright
- if (video.layout == 1)
- {
- DD_FillRect(ddraw.surface.primary, SubScreenRect.left, wr.top, wr.right, SubScreenRect.top, RGB(0, 128, 128)); //Top gap left when centering the resized screen
- DD_FillRect(ddraw.surface.primary, SubScreenRect.left, SubScreenRect.bottom, wr.right, wr.bottom, RGB(128, 128, 128)); //Bottom gap left when centering the resized screen
- }
- }
- }
-
- for(int i = 0; i < 2; i++)
- {
- if(i && video.layout == 2)
- break;
-
- if (!ddraw.blt(dstRects[i], srcRects[i])) return;
- }
-
- if (video.layout == 1) return;
- if (video.layout == 2) return;
-
- // Gap
- if(video.screengap > 0)
- {
- //u32 color = gapColors[win_fw_config.fav_colour];
- //u32 color_rev = (((color & 0xFF) << 16) | (color & 0xFF00) | ((color & 0xFF0000) >> 16));
- u32 color_rev = (u32)ScreenGapColor;
-
- HDC dc;
- HBRUSH brush;
-
- dc = GetDC(MainWindow->getHWnd());
- brush = CreateSolidBrush(color_rev);
-
- FillRect(dc, &GapRect, brush);
-
- DeleteObject((HGDIOBJ)brush);
- ReleaseDC(MainWindow->getHWnd(), dc);
- }
-}
-
-//triple buffering logic
-struct DisplayBuffer
-{
- DisplayBuffer()
- : buffer(NULL)
- , size(0)
- {
- }
- u32* buffer;
- size_t size; //[256*192*4];
-} displayBuffers[3];
-
-volatile int currDisplayBuffer=-1;
-volatile int newestDisplayBuffer=-2;
-slock_t *display_mutex = NULL;
-sthread_t *display_thread = NULL;
-volatile bool display_die = false;
-HANDLE display_wakeup_event = INVALID_HANDLE_VALUE;
-HANDLE display_done_event = INVALID_HANDLE_VALUE;
-DWORD display_done_timeout = 500;
-
-int displayPostponeType = 0;
-DWORD displayPostponeUntil = ~0;
-bool displayNoPostponeNext = false;
-
-DWORD display_invoke_argument = 0;
-void (*display_invoke_function)(DWORD) = NULL;
-HANDLE display_invoke_ready_event = INVALID_HANDLE_VALUE;
-HANDLE display_invoke_done_event = INVALID_HANDLE_VALUE;
-DWORD display_invoke_timeout = 500;
-CRITICAL_SECTION display_invoke_handler_cs;
-
-static void InvokeOnMainThread(void (*function)(DWORD), DWORD argument);
-
-static void DoDisplay_DrawHud()
-{
- osd->update();
- DrawHUD();
- osd->clear();
-}
-
-//does a single display work unit. only to be used from the display thread
-static void DoDisplay()
-{
- Lock lock (win_backbuffer_sync);
-
- bool ddhw = (GetStyle()&DWS_DDRAW_HW)!=0;
- bool ddsw = (GetStyle()&DWS_DDRAW_SW)!=0;
-
- if(displayPostponeType && !displayNoPostponeNext && (displayPostponeType < 0 || timeGetTime() < displayPostponeUntil))
- return;
- displayNoPostponeNext = false;
-
- //we have to do a copy here because we're about to draw the OSD onto it. bummer.
- if(gpu_bpp == 15)
- ColorspaceConvertBuffer555To8888Opaque((u16 *)video.srcBuffer, video.buffer, video.srcBufferSize / 2);
- else
- ColorspaceConvertBuffer888XTo8888Opaque((u32*)video.srcBuffer, video.buffer, video.srcBufferSize / 4);
-
- //some games use the backlight for fading effects
- const size_t pixCount = video.prefilterWidth * video.prefilterHeight / 2;
- const NDSDisplayInfo &displayInfo = GPU->GetDisplayInfo();
- ColorspaceApplyIntensityToBuffer32(video.buffer, pixCount, displayInfo.backlightIntensity[NDSDisplayID_Main]);
- ColorspaceApplyIntensityToBuffer32(video.buffer + pixCount, pixCount, displayInfo.backlightIntensity[NDSDisplayID_Touch]);
-
- if(AnyLuaActive())
- {
- if(sthread_isself(display_thread))
- {
- InvokeOnMainThread((void(*)(DWORD))
- CallRegisteredLuaFunctions, LUACALL_AFTEREMULATIONGUI);
- }
- else
- {
- CallRegisteredLuaFunctions(LUACALL_AFTEREMULATIONGUI);
- }
- }
-
- // draw hud
- aggDraw.hud->clear();
- DoDisplay_DrawHud();
- if (ddhw || ddsw)
- {
- // DirectDraw doesn't support alpha blending, so we must scale and overlay the HUD ourselves.
- T_AGG_RGBA target((u8*)video.buffer, video.prefilterWidth, video.prefilterHeight, video.prefilterWidth * 4);
- target.transformImage(aggDraw.hud->image(), 0, 0, video.prefilterWidth, video.prefilterHeight);
- }
-
- //apply user's filter
- video.filter();
-
- if(ddhw || ddsw)
- {
- gldisplay.kill();
- DD_DoDisplay();
- }
- else
- {
- OGL_DoDisplay();
- }
-}
-
-void displayProc()
-{
- slock_lock(display_mutex);
-
- //find a buffer to display
- int todo = newestDisplayBuffer;
- bool alreadyDisplayed = (todo == currDisplayBuffer);
-
- slock_unlock(display_mutex);
-
- //something new to display:
- if(!alreadyDisplayed) {
- //start displaying a new buffer
- currDisplayBuffer = todo;
- video.srcBuffer = (u8*)displayBuffers[currDisplayBuffer].buffer;
- video.srcBufferSize = displayBuffers[currDisplayBuffer].size;
- }
- DoDisplay();
-}
-
-
-void displayThread(void *arg)
-{
- do
- {
- if ( (MainWindow == NULL) || IsMinimized(MainWindow->getHWnd()) )
- {
- WaitForSingleObject(display_wakeup_event, INFINITE);
- }
- else if ( (emu_paused || !execute || !romloaded) && (!HudEditorMode && !CommonSettings.hud.ShowInputDisplay && !CommonSettings.hud.ShowGraphicalInputDisplay) )
- {
- WaitForSingleObject(display_wakeup_event, 250);
- }
- else
- {
- WaitForSingleObject(display_wakeup_event, 10);
- }
-
- if (display_die)
- {
- break;
- }
-
- displayProc();
- SetEvent(display_done_event);
- } while (!display_die);
-}
-
-void KillDisplay()
-{
- display_die = true;
- SetEvent(display_wakeup_event);
- if(display_thread)
- sthread_join(display_thread);
-}
-
-void Display()
-{
- const NDSDisplayInfo &dispInfo = GPU->GetDisplayInfo();
-
- if(CommonSettings.single_core())
- {
- video.srcBuffer = (u8*)dispInfo.masterCustomBuffer;
- video.srcBufferSize = dispInfo.customWidth * dispInfo.customHeight * dispInfo.pixelBytes * 2;
- DoDisplay();
- }
- else
- {
- if(display_thread == NULL)
- {
- display_mutex = slock_new();
- display_thread = sthread_create(&displayThread, nullptr);
- }
-
- slock_lock(display_mutex);
-
- if(int diff = (currDisplayBuffer+1)%3 - newestDisplayBuffer)
- newestDisplayBuffer += diff;
- else newestDisplayBuffer = (currDisplayBuffer+2)%3;
-
- DisplayBuffer& db = displayBuffers[newestDisplayBuffer];
- size_t targetSize = dispInfo.customWidth * dispInfo.customHeight * dispInfo.pixelBytes * 2;
- if(db.size != targetSize)
- {
- free_aligned(db.buffer);
- db.buffer = (u32*)malloc_alignedPage(targetSize);
- db.size = targetSize;
- }
- memcpy(db.buffer,dispInfo.masterCustomBuffer,targetSize);
-
- slock_unlock(display_mutex);
-
- SetEvent(display_wakeup_event);
- }
-}
-
-
-
void CheckMessages()
{
MSG msg;
@@ -2330,31 +1224,6 @@ void CheckMessages()
readConsole();
}
-static void InvokeOnMainThread(void (*function)(DWORD), DWORD argument)
-{
- ResetEvent(display_invoke_ready_event);
- display_invoke_argument = argument;
- display_invoke_function = function;
- PostMessage(MainWindow->getHWnd(), WM_CUSTINVOKE, 0, 0); // in case a modal dialog or menu is open
- SignalObjectAndWait(display_invoke_ready_event, display_invoke_done_event, display_invoke_timeout, FALSE);
- display_invoke_function = NULL;
-}
-static void _ServiceDisplayThreadInvocation()
-{
- Lock lock (display_invoke_handler_cs);
- DWORD res = WaitForSingleObject(display_invoke_ready_event, display_invoke_timeout);
- if(res != WAIT_ABANDONED && display_invoke_function)
- display_invoke_function(display_invoke_argument);
- display_invoke_function = NULL;
- SetEvent(display_invoke_done_event);
-}
-static FORCEINLINE void ServiceDisplayThreadInvocations()
-{
- if(display_invoke_function)
- _ServiceDisplayThreadInvocation();
-}
-
-
static struct MainLoopData
{
u64 freq;
@@ -2372,7 +1241,6 @@ static struct MainLoopData
int toolframecount;
} mainLoopData = {0};
-
static void StepRunLoop_Core()
{
input_acquire();
@@ -2424,9 +1292,12 @@ static void StepRunLoop_User()
Hud.fps = mainLoopData.fps;
Hud.fps3d = GPU->GetFPSRender3D();
- // wait for the HUD to update from last frame
- if(frameskiprate==0) WaitForSingleObject(display_done_event, display_done_timeout);
- Display();
+ if (mainLoopData.framesskipped == 0)
+ {
+ WaitForSingleObject(display_done_event, display_done_timeout);
+ Display();
+ }
+ ResetEvent(display_done_event);
mainLoopData.fps3d = Hud.fps3d;
@@ -3083,17 +1954,10 @@ int _main()
if(GetPrivateProfileBool("Display", "Show Menu In Fullscreen Mode", false, IniName)) style |= DWS_FS_MENU;
if (GetPrivateProfileBool("Display", "Non-exclusive Fullscreen Mode", false, IniName)) style |= DWS_FS_WINDOW;
- if(GetPrivateProfileBool("Video","Display Method Filter", false, IniName))
- style |= DWS_FILTER;
+ gldisplay.filter = GetPrivateProfileBool("Video","Display Method Filter", false, IniName);
if(GetPrivateProfileBool("Video","VSync", false, IniName))
style |= DWS_VSYNC;
- int dispMethod = GetPrivateProfileInt("Video","Display Method", DISPMETHOD_DDRAW_HW, IniName);
- if(dispMethod == DISPMETHOD_DDRAW_SW)
- style |= DWS_DDRAW_SW;
- if(dispMethod == DISPMETHOD_DDRAW_HW)
- style |= DWS_DDRAW_HW;
- if(dispMethod == DISPMETHOD_OPENGL)
- style |= DWS_OPENGL;
+ displayMethod = GetPrivateProfileInt("Video","Display Method", DISPMETHOD_DDRAW_HW, IniName);
windowSize = GetPrivateProfileInt("Video","Window Size", 0, IniName);
video.rotation = GetPrivateProfileInt("Video","Window Rotate", 0, IniName);
@@ -4860,10 +3724,10 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM
MainWindow->checkMenu(IDM_RENDER_5XBRZ, video.currentfilter == video._5XBRZ );
MainWindow->checkMenu(ID_DISPLAYMETHOD_VSYNC, (GetStyle()&DWS_VSYNC)!=0);
- MainWindow->checkMenu(ID_DISPLAYMETHOD_DIRECTDRAWHW, (GetStyle()&DWS_DDRAW_HW)!=0);
- MainWindow->checkMenu(ID_DISPLAYMETHOD_DIRECTDRAWSW, (GetStyle()&DWS_DDRAW_SW)!=0);
- MainWindow->checkMenu(ID_DISPLAYMETHOD_OPENGL, (GetStyle()&DWS_OPENGL)!=0);
- MainWindow->checkMenu(ID_DISPLAYMETHOD_FILTER, (GetStyle()&DWS_FILTER)!=0);
+ MainWindow->checkMenu(ID_DISPLAYMETHOD_DIRECTDRAWHW, displayMethod == DISPMETHOD_DDRAW_HW);
+ MainWindow->checkMenu(ID_DISPLAYMETHOD_DIRECTDRAWSW, displayMethod == DISPMETHOD_DDRAW_SW);
+ MainWindow->checkMenu(ID_DISPLAYMETHOD_OPENGL, displayMethod == DISPMETHOD_OPENGL);
+ MainWindow->checkMenu(ID_DISPLAYMETHOD_FILTER, gldisplay.filter);
MainWindow->checkMenu(IDC_BACKGROUNDPAUSE, lostFocusPause);
MainWindow->checkMenu(IDC_BACKGROUNDINPUT, allowBackgroundInput);
@@ -6233,7 +5097,8 @@ DOKEYDOWN:
case ID_DISPLAYMETHOD_DIRECTDRAWHW:
{
Lock lock (win_backbuffer_sync);
- SetStyle((GetStyle()&~DWS_DISPMETHODS) | DWS_DDRAW_HW);
+ displayMethod = DISPMETHOD_DDRAW_HW;
+ ddraw.systemMemory = false;
WritePrivateProfileInt("Video","Display Method", DISPMETHOD_DDRAW_HW, IniName);
ddraw.createSurfaces(hwnd);
}
@@ -6242,7 +5107,8 @@ DOKEYDOWN:
case ID_DISPLAYMETHOD_DIRECTDRAWSW:
{
Lock lock (win_backbuffer_sync);
- SetStyle((GetStyle()&~DWS_DISPMETHODS) | DWS_DDRAW_SW);
+ displayMethod = DISPMETHOD_DDRAW_SW;
+ ddraw.systemMemory = true;
WritePrivateProfileInt("Video","Display Method", DISPMETHOD_DDRAW_SW, IniName);
ddraw.createSurfaces(hwnd);
}
@@ -6251,7 +5117,7 @@ DOKEYDOWN:
case ID_DISPLAYMETHOD_OPENGL:
{
Lock lock (win_backbuffer_sync);
- SetStyle((GetStyle()&~DWS_DISPMETHODS) | DWS_OPENGL);
+ displayMethod = DISPMETHOD_OPENGL;
WritePrivateProfileInt("Video","Display Method", DISPMETHOD_OPENGL, IniName);
}
break;
@@ -6259,8 +5125,8 @@ DOKEYDOWN:
case ID_DISPLAYMETHOD_FILTER:
{
Lock lock (win_backbuffer_sync);
- SetStyle((GetStyle()^DWS_FILTER));
- WritePrivateProfileInt("Video","Display Method Filter", (GetStyle()&DWS_FILTER)?1:0, IniName);
+ gldisplay.filter = !gldisplay.filter;
+ WritePrivateProfileInt("Video","Display Method Filter", gldisplay.filter?1:0, IniName);
}
break;
@@ -7821,161 +6687,6 @@ void WIN_InstallGBACartridge()
GBACartridge_SRAMPath = Path::GetFileNameWithoutExt(win32_GBA_cfgRomPath) + "." + GBA_SRAM_FILE_EXT;
}
-// ================================================================= DDraw
-u32 DDRAW::create(HWND hwnd)
-{
- if (handle) return 0;
-
- if (FAILED(DirectDrawCreateEx(NULL, (LPVOID*)&handle, IID_IDirectDraw7, NULL)))
- return 1;
-
- if (FAILED(handle->SetCooperativeLevel(hwnd, DDSCL_NORMAL)))
- return 2;
-
- createSurfaces(hwnd);
-
- return 0;
-}
-
-bool DDRAW::release()
-{
- if (!handle) return true;
-
- if (clip != NULL) clip->Release();
- if (surface.back != NULL) surface.back->Release();
- if (surface.primary != NULL) surface.primary->Release();
-
- if (FAILED(handle->Release())) return false;
- return true;
-}
-
-bool DDRAW::createBackSurface(int width, int height)
-{
- if (surface.back) { surface.back->Release(); surface.back = NULL; }
-
- bool hw = (GetStyle()&DWS_DDRAW_HW)!=0;
- bool sw = (GetStyle()&DWS_DDRAW_SW)!=0;
-
- if(!hw && !sw) return true;
-
- memset(&surfDescBack, 0, sizeof(surfDescBack));
- surfDescBack.dwSize = sizeof(surfDescBack);
- surfDescBack.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
- surfDescBack.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
-
- if(sw)
- surfDescBack.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
- else
- surfDescBack.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
-
-
- surfDescBack.dwWidth = width;
- surfDescBack.dwHeight = height;
-
- if (FAILED(handle->CreateSurface(&surfDescBack, &surface.back, NULL))) return false;
-
- return true;
-}
-
-bool DDRAW::createSurfaces(HWND hwnd)
-{
- if (!handle) return true;
-
- if (clip) { clip->Release(); clip = NULL; }
- if (surface.primary) { surface.primary->Release(); surface.primary = NULL; }
-
-
- // primary
- memset(&surfDesc, 0, sizeof(surfDesc));
- surfDesc.dwSize = sizeof(surfDesc);
- surfDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
- surfDesc.dwFlags = DDSD_CAPS;
- if (FAILED(handle->CreateSurface(&surfDesc, &surface.primary, NULL)))
- return false;
-
- //default doesnt matter much, itll get adjusted later
- if(!createBackSurface(GPU_FRAMEBUFFER_NATIVE_WIDTH, GPU_FRAMEBUFFER_NATIVE_HEIGHT * 2))
- return false;
-
- if (FAILED(handle->CreateClipper(0, &clip, NULL))) return false;
- if (FAILED(clip->SetHWnd(0, hwnd))) return false;
- if (FAILED(surface.primary->SetClipper(clip))) return false;
-
- backbuffer_invalidate = false;
-
- return true;
-}
-
-bool DDRAW::lock()
-{
- if (!handle) return true;
- if (!surface.back) return false;
- memset(&surfDescBack, 0, sizeof(surfDescBack));
- surfDescBack.dwSize = sizeof(surfDescBack);
- surfDescBack.dwFlags = DDSD_ALL;
-
- HRESULT res = surface.back->Lock(NULL, &surfDescBack, DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL);
- if (FAILED(res))
- {
- //INFO("DDraw failed: Lock %i\n", res);
- if (res == DDERR_SURFACELOST)
- {
- res = surface.back->Restore();
- if (FAILED(res)) return false;
- }
- }
- return true;
-}
-
-bool DDRAW::unlock()
-{
- if (!handle) return true;
- if (!surface.back) return false;
- if (FAILED(surface.back->Unlock((LPRECT)surfDescBack.lpSurface))) return false;
- return true;
-}
-
-bool DDRAW::OK()
-{
- if (!handle) return false;
- if (!surface.primary) return false;
- if (!surface.back) return false;
- return true;
-}
-
-bool DDRAW::blt(LPRECT dst, LPRECT src)
-{
- if (!handle) return true;
- if (!surface.primary) return false;
- if (!surface.back) return false;
-
- if(GetStyle()&DWS_VSYNC)
- {
- //this seems to block the whole process. this destroys the display thread and will easily block the emulator to 30fps.
- //IDirectDraw7_WaitForVerticalBlank(handle,DDWAITVB_BLOCKBEGIN,0);
-
- for(;;)
- {
- BOOL vblank;
- IDirectDraw7_GetVerticalBlankStatus(handle,&vblank);
- if(vblank) break;
- //must be a greedy loop since vblank is small relative to 1msec minimum Sleep() resolution.
- }
- }
-
- HRESULT res = surface.primary->Blt(dst, surface.back, src, DDBLT_WAIT, 0);
- if (FAILED(res))
- {
- //INFO("DDraw failed: Blt %i\n", res);
- if (res == DDERR_SURFACELOST)
- {
- res = surface.primary->Restore();
- if (FAILED(res)) return false;
- }
- }
- return true;
-}
-
void SetStyle(u32 dws)
{
//pokefan's suggestion, there are a number of ways we could do this.
@@ -8004,4 +6715,5 @@ void SetStyle(u32 dws)
SetWindowPos(MainWindow->getHWnd(), insertAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
gldisplay.setvsync(!!(GetStyle()&DWS_VSYNC));
+ ddraw.vSync = GetStyle()&DWS_VSYNC;
}
diff --git a/desmume/src/frontend/windows/main.h b/desmume/src/frontend/windows/main.h
index bf6037032..b65231234 100644
--- a/desmume/src/frontend/windows/main.h
+++ b/desmume/src/frontend/windows/main.h
@@ -28,6 +28,7 @@ extern WINCLASS *MainWindow;
extern HINSTANCE hAppInst;
extern HMENU mainMenu; //Holds handle to the main DeSmuME menu
extern CToolBar* MainWindowToolbar;
+extern CRITICAL_SECTION win_backbuffer_sync;
extern volatile bool execute, paused;
extern bool romloaded;
@@ -98,4 +99,6 @@ void WIN_InstallGBACartridge();
#define IDT_VIEW_LIGHTS 50011
#define IDM_EXEC 50112
+#define WM_CUSTINVOKE WM_USER+52
+
#endif
diff --git a/desmume/src/frontend/windows/ogl_display.cpp b/desmume/src/frontend/windows/ogl_display.cpp
new file mode 100644
index 000000000..107f8ef38
--- /dev/null
+++ b/desmume/src/frontend/windows/ogl_display.cpp
@@ -0,0 +1,142 @@
+/*
+Copyright (C) 2018 DeSmuME team
+
+This file is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This file 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 the this software. If not, see .
+*/
+
+#include "ogl_display.h"
+
+bool GLDISPLAY::initialize(HWND hwnd)
+{
+ //do we need to use another HDC?
+ if (hwnd == this->hwnd) return true;
+ else if (this->hwnd) return false;
+
+ if (initContext(hwnd, &privateContext))
+ {
+ this->hwnd = hwnd;
+ return true;
+ }
+ else
+ return false;
+}
+//http://stackoverflow.com/questions/589064/how-to-enable-vertical-sync-in-opengl
+bool GLDISPLAY::WGLExtensionSupported(const char *extension_name)
+{
+ // this is pointer to function which returns pointer to string with list of all wgl extensions
+ PFNWGLGETEXTENSIONSSTRINGEXTPROC _wglGetExtensionsStringEXT = NULL;
+
+ // determine pointer to wglGetExtensionsStringEXT function
+ _wglGetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)wglGetProcAddress("wglGetExtensionsStringEXT");
+
+ if (strstr(_wglGetExtensionsStringEXT(), extension_name) == NULL)
+ {
+ // string was not found
+ return false;
+ }
+
+ // extension is supported
+ return true;
+}
+void GLDISPLAY::_setvsync()
+{
+ //even if it doesn't work, we'll track it
+ haveVsync = wantVsync;
+
+ if (!WGLExtensionSupported("WGL_EXT_swap_control")) return;
+
+ //http://stackoverflow.com/questions/589064/how-to-enable-vertical-sync-in-opengl
+ PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;
+ PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT = NULL;
+ {
+ // Extension is supported, init pointers.
+ wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");
+
+ // this is another function from WGL_EXT_swap_control extension
+ wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC)wglGetProcAddress("wglGetSwapIntervalEXT");
+ }
+
+ wglSwapIntervalEXT(wantVsync ? 1 : 0);
+}
+
+
+GLDISPLAY::GLDISPLAY()
+ : active(false)
+ , wantVsync(false)
+ , haveVsync(false)
+{
+}
+
+void GLDISPLAY::kill()
+{
+ if (!hwnd) return;
+ wglDeleteContext(privateContext);
+ privateContext = NULL;
+ hwnd = NULL;
+ haveVsync = false;
+}
+
+bool GLDISPLAY::begin(HWND hwnd)
+{
+ DWORD myThread = GetCurrentThreadId();
+
+ //always use another context for display logic
+ //1. if this is a single threaded process (3d rendering and display in the main thread) then alternating contexts is benign
+ //2. if this is a multi threaded process (3d rendernig and display in other threads) then the display needs some context
+
+ if (!this->hwnd)
+ {
+ if (!initialize(hwnd)) return false;
+ }
+
+ privateDC = GetDC(hwnd);
+ wglMakeCurrent(privateDC, privateContext);
+
+ //go ahead and sync the vsync setting while we have the context
+ if (wantVsync != haveVsync)
+ _setvsync();
+
+ if (filter)
+ {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ }
+ else
+ {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ }
+
+
+ return active = true;
+}
+
+void GLDISPLAY::end()
+{
+ wglMakeCurrent(NULL, privateContext);
+ ReleaseDC(hwnd, privateDC);
+ privateDC = NULL;
+ active = false;
+}
+
+void GLDISPLAY::setvsync(bool vsync)
+{
+ wantVsync = vsync;
+}
+
+void GLDISPLAY::showPage()
+{
+ SwapBuffers(privateDC);
+}
+
diff --git a/desmume/src/frontend/windows/ogl_display.h b/desmume/src/frontend/windows/ogl_display.h
new file mode 100644
index 000000000..0b84c26c9
--- /dev/null
+++ b/desmume/src/frontend/windows/ogl_display.h
@@ -0,0 +1,52 @@
+/*
+Copyright (C) 2018 DeSmuME team
+
+This file is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This file 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 the this software. If not, see .
+*/
+
+#ifndef _OGL_DISPLAY_H_
+#define _OGL_DISPLAY_H_
+
+#include "types.h"
+#include "ogl.h"
+
+#include
+#include
+#include
+
+
+struct GLDISPLAY
+{
+private:
+ HGLRC privateContext;
+ HDC privateDC;
+ HWND hwnd;
+ bool initialize(HWND hwnd);
+ bool WGLExtensionSupported(const char *extension_name);
+ void _setvsync();
+
+public:
+ bool active;
+ bool wantVsync, haveVsync;
+ bool filter;
+
+ GLDISPLAY();
+ void kill();
+ bool begin(HWND hwnd);
+ void end();
+ void setvsync(bool vsync);
+ void showPage();
+};
+
+#endif
\ No newline at end of file
diff --git a/desmume/src/frontend/windows/video.h b/desmume/src/frontend/windows/video.h
index 66938f33b..ce40c9917 100644
--- a/desmume/src/frontend/windows/video.h
+++ b/desmume/src/frontend/windows/video.h
@@ -15,6 +15,9 @@ You should have received a copy of the GNU General Public License
along with the this software. If not, see .
*/
+#ifndef _VIDEO_H_
+#define _VIDEO_H_
+
#include "filter/filter.h"
#include "common.h"
@@ -349,3 +352,5 @@ public:
return screengap * height / 384;
}
};
+
+#endif