/* LICENSE ------- Copyright (C) 1999-2002 Nullsoft, Inc. This source code is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this source code or the software it produces. Permission is granted to anyone to use this source code for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this source code must not be misrepresented; you must not claim that you wrote the original source code. If you use this source code in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original source code. 3. This notice may not be removed or altered from any source distribution. */ #include "DXContext.h" #include "utility.h" #include "shell_defines.h" #include "resource.h" #include #define COMPILE_MULTIMON_STUBS 1 #include #define MY_EXT_WINDOW_STYLE (m_current_mode.m_skin ? 0 : ((m_current_mode.screenmode==DESKTOP) ? WS_EX_APPWINDOW : 0)) // note: changed from TOOLWINDOW to APPWINDOW b/c we wanted the plugin to appear in the taskbar. #define MY_WINDOW_STYLE (m_current_mode.m_skin ? (WS_VISIBLE|WS_CHILDWINDOW|WS_OVERLAPPED|WS_CLIPCHILDREN|WS_CLIPSIBLINGS) : ((m_current_mode.screenmode==FAKE_FULLSCREEN || m_current_mode.screenmode==DESKTOP) ? WS_POPUP : WS_OVERLAPPEDWINDOW)) // note: WS_POPUP (by itself) removes all borders, captions, etc. #include "vis.h" extern winampVisModule mod1; // note: the use of delayimp.lib, and the '/delayload' options below, // will all ensure that the plugin DLL can still be loaded, even if // DX8 is not installed. Without this delayed loading of the DX8 // DLL's, **the plugin would not show up in the plug-ins list** on // machines that do not have DX8 installed. Using delayed loading, // we can at least let them try to configure or run the plugin, and // then tell them that DX8 is missing. #pragma comment(lib,"delayimp.lib") #pragma comment(lib,"d3d8.lib") #pragma comment(linker,"/delayload:d3d8.dll") #ifdef _DEBUG #pragma comment(lib, "d3dx8d.lib") //#pragma comment(linker,"/delayload:d3dx8d.dll") #else #pragma comment(lib, "d3dx8.lib") //#pragma comment(linker,"/delayload:d3dx8.dll") #endif DXContext::DXContext(HWND hWndWinamp,HINSTANCE hInstance,LPCSTR szClassName,LPCSTR szWindowCaption,WNDPROC pProc,LONG uWindowLong, int minimize_winamp, char* szIniFile) { m_szClassName[0] = 0; m_szWindowCaption[0] = 0; m_hwnd = NULL; m_lpD3D = NULL; m_lpDevice = NULL; m_hmod_d3d8 = NULL; m_zFormat = D3DFMT_UNKNOWN; for (int i=0; iCheckDeviceType(ordinal_adapter,D3DDEVTYPE_HAL,fmt,fmt,FALSE)) return TRUE; return FALSE; } BOOL DXContext::TestDepth(int ordinal_adapter, D3DFORMAT fmt) { if (D3D_OK!=m_lpD3D->CheckDeviceFormat(ordinal_adapter,D3DDEVTYPE_HAL,m_current_mode.display_mode.Format, D3DUSAGE_DEPTHSTENCIL,D3DRTYPE_SURFACE,fmt)) return FALSE; if (D3D_OK!=m_lpD3D->CheckDepthStencilMatch(ordinal_adapter,D3DDEVTYPE_HAL, m_current_mode.display_mode.Format,m_current_mode.display_mode.Format,fmt)) return FALSE; return TRUE; } int DXContext::CheckAndCorrectFullscreenDispMode(int ordinal_adapter, D3DDISPLAYMODE *pdm) { // given the user's choice of fullscreen display mode, // go through all the display modes available to the currently-selected adapter // and find the best match. // returns 1 if it altered pdm to the best match, // or 0 if it was able to find a perfect match. // if it returns 1, you might want to notify the user. #define MAX_DISPLAY_MODES 4096 D3DDISPLAYMODE list[MAX_DISPLAY_MODES]; int nCount = min(m_lpD3D->GetAdapterModeCount(ordinal_adapter), MAX_DISPLAY_MODES); int nValid = 0; for (int i=0; iEnumAdapterModes(ordinal_adapter, i, &list[nValid]) == D3D_OK) nValid++; // do many passes through the set until we find a match, // each time relaxing more constraints. // outline of the passes: int bpp_desired = 0; switch(pdm->Format) { case D3DFMT_R8G8B8 : bpp_desired = 32; break; case D3DFMT_A8R8G8B8: bpp_desired = 32; break; case D3DFMT_X8R8G8B8: bpp_desired = 32; break; case D3DFMT_R5G6B5 : bpp_desired = 16; break; case D3DFMT_X1R5G5B5: bpp_desired = 16; break; case D3DFMT_A1R5G5B5: bpp_desired = 16; break; case D3DFMT_A4R4G4B4: bpp_desired = 16; break; case D3DFMT_R3G3B2 : bpp_desired = 8; break; case D3DFMT_A8R3G3B2: bpp_desired = 16; break; case D3DFMT_X4R4G4B4: bpp_desired = 16; break; } // rep MATCH: // 0. w,h,r,f // 1. w,h,-,f // 2. w,h,r,- pass: // 3. w,h,-,- -on pass 0, for 'f', match exact format // 4. 8,6,r,f -on pass 1, for 'f', just match # of bits per pixel // 5. 8,6,-,f (more relaxed match) // 6. 8,6,r,- // 7. 8,6,-,- // 8. -,-,r,f // 9. -,-,-,f // 10. -,-,r,- // 11. -,-,-,- int found = 0; for (int rep=0; rep<12 && !found; rep++) { for (int pass=0; pass<2 && !found; pass++) { for (i=0; iWidth != list[i].Width) bMatch = false; if (pdm->Height != list[i].Height) bMatch = false; } else if (rep < 8) { if (DEFAULT_FULLSCREEN_WIDTH != list[i].Width) bMatch = false; if (DEFAULT_FULLSCREEN_HEIGHT != list[i].Height) bMatch = false; } if (((rep/2)%2)==0) { if (pass==0 && pdm->Format != list[i].Format) bMatch = false; else if (pass==1 && bpp_desired != bpp_this_mode) bMatch = false; } if (((rep%2)==0) && pdm->RefreshRate != list[i].RefreshRate) { bMatch = false; } if (bMatch) { memcpy(pdm, &list[i], sizeof(D3DDISPLAYMODE)); found = 1; if (rep != 0 || pass != 0) { return 1; /* MessageBox(m_hwnd, "The fullscreen display mode selected from the config panel\r" "was invalid, for some reason. For now, the closest match\r" "(to the old selection) will be used.\r" "\r" "To fix this, please return to the config panel and select a new\r" "fullscreen display mode.\r" "\r" "The plugin will now run using the best match...\r" ,"WARNING", MB_OK|MB_SETFOREGROUND|MB_TOPMOST); */ } } } } } return 0; } BOOL CALLBACK MyMonitorEnumProc( HMONITOR hMonitor, // handle to display monitor HDC hdcMonitor, // handle to monitor DC LPRECT lprcMonitor, // monitor intersection rectangle LPARAM dwData // data ) { RECT* p = (RECT*)dwData; if (hMonitor) { MONITORINFO mi; mi.cbSize = sizeof(mi); if (GetMonitorInfo(hMonitor, &mi)) { p->top = min(p->top , mi.rcMonitor.top ); p->left = min(p->left , mi.rcMonitor.left ); p->right = max(p->right , mi.rcMonitor.right ); p->bottom = max(p->bottom, mi.rcMonitor.bottom); } } return TRUE; } int DXContext::GetWindowedModeAutoSize(int iteration) { // note: requires 'm_monitor_rect' has been set! // generically determine size of window, for windowed mode: int x = m_monitor_rect.right-m_monitor_rect.left; int y = m_monitor_rect.bottom-m_monitor_rect.top; // if running in horz/vert-span multi-display mode, base the window size on // an actual display size, not the giant double-sized monitor. Also, position // the window on the same monitor that Winamp is on. if (x >= y*2) { x /= 2; // move window to same display that Winamp is on: WINDOWPLACEMENT wp; wp.length = sizeof(wp); if (GetWindowPlacement(m_hwnd_winamp, &wp)) { int winamp_center_x = (wp.rcNormalPosition.right + wp.rcNormalPosition.left)/2; if (winamp_center_x > x) { m_monitor_rect.left += x; m_monitor_rect.right += x; } } } else if (y > x*4/3) { y /= 2; // move window to same display that Winamp is on: WINDOWPLACEMENT wp; wp.length = sizeof(wp); if (GetWindowPlacement(m_hwnd_winamp, &wp)) { int winamp_center_y = (wp.rcNormalPosition.top + wp.rcNormalPosition.bottom)/2; if (winamp_center_y > y) { m_monitor_rect.top += y; m_monitor_rect.bottom += y; } } } int size = min(x, y); size = (int)(size*DEFAULT_WINDOW_SIZE); size = (size/64 - iteration)*64; if (size < 64) size = 64; return size; } void DXContext::WriteSafeWindowPos() { if (m_current_mode.screenmode == WINDOWED) { WritePrivateProfileInt(64, "nMainWndTop", m_szIniFile, "settings"); WritePrivateProfileInt(64, "nMainWndLeft", m_szIniFile, "settings"); WritePrivateProfileInt(64+256, "nMainWndRight", m_szIniFile, "settings"); WritePrivateProfileInt(64+256, "nMainWndBottom", m_szIniFile, "settings"); WritePrivateProfileInt(64, "avs_wx",m_szIniFile,"settings"); WritePrivateProfileInt(64, "avs_wy",m_szIniFile,"settings"); WritePrivateProfileInt( 256, "avs_ww",m_szIniFile,"settings"); WritePrivateProfileInt( 256, "avs_wh",m_szIniFile,"settings"); } } BOOL DXContext::Internal_Init(DXCONTEXT_PARAMS *pParams, BOOL bFirstInit) { memcpy(&m_current_mode, pParams, sizeof(DXCONTEXT_PARAMS)); memset(&myWindowState,0,sizeof(myWindowState)); // various checks if (m_current_mode.screenmode != WINDOWED) m_current_mode.m_skin = 0; // 1. destroy old window if (m_hwnd) { m_ignore_wm_destroy = 1; DestroyWindow(m_hwnd); m_ignore_wm_destroy = 0; m_hwnd = NULL; } // 2. CHECK TO MAKE SURE DIRECTX/DDRAW IS INSTALLED if (bFirstInit) { // Test for DirectX 8 + start it // note: if you don't call LoadLibrary here, and you're on a system // where DX8 is missing, Direct3DCreate8() might crash; so call it. int d3d8_already_loaded = (GetModuleHandle("d3d8.dll") != NULL) ? 1 : 0; if (!d3d8_already_loaded) m_hmod_d3d8 = LoadLibrary("d3d8.dll"); if ( (!d3d8_already_loaded && !m_hmod_d3d8) || !(m_lpD3D = Direct3DCreate8(D3D_SDK_VERSION)) ) { MissingDirectX(NULL); m_lastErr = DXC_ERR_CREATE3D; return FALSE; } } // 3. get the smallest single rectangle that encloses ALL the monitors on the desktop: SetRect(&m_all_monitors_rect, 0, 0, 0, 0); EnumDisplayMonitors(NULL, NULL, MyMonitorEnumProc, (LPARAM)&m_all_monitors_rect); // 4. some DirectX- / DDraw-specific stuff. Also determine hPluginMonitor. HMONITOR hPluginMonitor = NULL; { D3DADAPTER_IDENTIFIER8 temp; // find the ordinal # of the adapter whose GUID matches what the user picked from the config panel. // if no match found, use D3DADAPTER_DEFAULT. m_ordinal_adapter = D3DADAPTER_DEFAULT; int nAdapters = m_lpD3D->GetAdapterCount(); { for (int i=0; iGetAdapterIdentifier(i, D3DENUM_NO_WHQL_LEVEL, &temp) == D3D_OK) && (memcmp(&temp.DeviceIdentifier, &m_current_mode.adapter_guid, sizeof(GUID))==0)) { m_ordinal_adapter = i; break; } } } if (m_lpD3D->GetAdapterIdentifier(m_ordinal_adapter, D3DENUM_NO_WHQL_LEVEL, &temp) == D3D_OK) { strcpy(m_szDriver, temp.Driver); strcpy(m_szDesc, temp.Description); } int caps_ok = 0; int caps_tries = 0; int changed_fs_disp_mode; // try to get the device caps for the adapter selected from the config panel. // if GetDeviceCaps() fails, it's probably because the adapter has been // removed from the system (or disabled), so we try again with other adapter(s). do { changed_fs_disp_mode = 0; SetRect(&m_monitor_rect, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)); // get bounding rect of the monitor attached to the adapter (to assist w/window positioning) // note: in vert/horz span setups (psuedo-multimon), // this will be 2048x768 or 1024x1536 or something like that. hPluginMonitor = m_lpD3D->GetAdapterMonitor(m_ordinal_adapter); /*if (hPluginMonitor) { MONITORINFO mi; mi.cbSize = sizeof(mi); if (GetMonitorInfo(hPluginMonitor, &mi)) { memcpy(&m_monitor_rect, &mi.rcMonitor, sizeof(RECT)); memcpy(&m_monitor_work_rect, &mi.rcWork, sizeof(RECT)); } }*/ if (bFirstInit) { for (int i=0; iGetAdapterDisplayMode( i, &d3ddm ) ) ) { d3ddm.Format = D3DFMT_UNKNOWN; //m_lastErr = DXC_ERR_GETFORMAT; //MessageBox(m_hwnd, "DirectX initialization failed (GetAdapterDisplayMode)", "ERROR", MB_OK|MB_SETFOREGROUND|MB_TOPMOST); //return FALSE; } m_orig_windowed_mode_format[i] = d3ddm.Format; } } // figure out pixel (color) format for back buffer: (m_current_mode.display_mode.Format) if (m_current_mode.screenmode!=FULLSCREEN && m_ordinal_adapter < MAX_DXC_ADAPTERS) m_current_mode.display_mode.Format = m_orig_windowed_mode_format[m_ordinal_adapter]; // else // for fullscreen, use what they gave us if (m_current_mode.display_mode.Format == D3DFMT_UNKNOWN || !TestFormat(m_ordinal_adapter, m_current_mode.display_mode.Format)) { // if they try to run the plugin without ever running the config panel // first (& pressing OK), then the fullscreen pixelformat hasn't been // chosen... so we try all the possilibities until one works: if (TestFormat(m_ordinal_adapter,D3DFMT_A8R8G8B8)) m_current_mode.display_mode.Format = D3DFMT_A8R8G8B8; else if (TestFormat(m_ordinal_adapter,D3DFMT_X8R8G8B8)) m_current_mode.display_mode.Format = D3DFMT_X8R8G8B8; else if (TestFormat(m_ordinal_adapter,D3DFMT_R8G8B8 )) m_current_mode.display_mode.Format = D3DFMT_R8G8B8 ; else if (TestFormat(m_ordinal_adapter,D3DFMT_R5G6B5 )) m_current_mode.display_mode.Format = D3DFMT_R5G6B5 ; else if (TestFormat(m_ordinal_adapter,D3DFMT_X1R5G5B5)) m_current_mode.display_mode.Format = D3DFMT_X1R5G5B5; else if (TestFormat(m_ordinal_adapter,D3DFMT_A1R5G5B5)) m_current_mode.display_mode.Format = D3DFMT_A1R5G5B5; else if (TestFormat(m_ordinal_adapter,D3DFMT_A4R4G4B4)) m_current_mode.display_mode.Format = D3DFMT_A4R4G4B4; else if (TestFormat(m_ordinal_adapter,D3DFMT_X4R4G4B4)) m_current_mode.display_mode.Format = D3DFMT_X4R4G4B4; } if (m_current_mode.display_mode.Format==D3DFMT_UNKNOWN) { m_lastErr = DXC_ERR_FORMAT; MessageBox(m_hwnd, "DirectX initialization failed; unknown color format", "ERROR", MB_OK|MB_SETFOREGROUND|MB_TOPMOST); return FALSE; } if (m_current_mode.screenmode == FULLSCREEN) changed_fs_disp_mode = CheckAndCorrectFullscreenDispMode(m_ordinal_adapter, &m_current_mode.display_mode); // figure out pixel format of the z-buffer: (m_zFormat) m_zFormat = D3DFMT_UNKNOWN; if (TestDepth(m_ordinal_adapter,D3DFMT_D32 )) m_zFormat=D3DFMT_D32; else if (TestDepth(m_ordinal_adapter,D3DFMT_D24S8 )) m_zFormat=D3DFMT_D24S8; else if (TestDepth(m_ordinal_adapter,D3DFMT_D24X4S4 )) m_zFormat=D3DFMT_D24X4S4; else if (TestDepth(m_ordinal_adapter,D3DFMT_D24X8 )) m_zFormat=D3DFMT_D24X8; else if (TestDepth(m_ordinal_adapter,D3DFMT_D16 )) m_zFormat=D3DFMT_D16; else if (TestDepth(m_ordinal_adapter,D3DFMT_D15S1 )) m_zFormat=D3DFMT_D15S1; else if (TestDepth(m_ordinal_adapter,D3DFMT_D16_LOCKABLE)) m_zFormat=D3DFMT_D16_LOCKABLE; // get device caps: memset(&m_caps, 0, sizeof(m_caps)); if (FAILED(m_lpD3D->GetDeviceCaps(m_ordinal_adapter, D3DDEVTYPE_HAL, &m_caps))) { // that adapter was found in the system, but it might be disabled // (i.e. 'extend my Windows desktop onto this monitor') is unchecked) // so, try other adapters (try all sequentially). if (caps_tries < nAdapters) { // try again, this time using the default adapter: m_ordinal_adapter = caps_tries; caps_tries++; } else { m_lastErr = DXC_ERR_CAPSFAIL; MessageBox(m_hwnd, "DirectX initialization failed (GetDeviceCaps).\r" "\r" "This means that no valid 3D-accelerated display adapter could be found\r" "on your computer." "\r" "If you know this is not the case, it is possible that your graphics\r" "subsystem is temporarily unstable; please try rebooting your computer,\r" "and then try to run the plugin again. Otherwise, please install a\r" "3D-accelerated display adapter." ,"ERROR",MB_OK|MB_SETFOREGROUND|MB_TOPMOST); return FALSE; } } else { caps_ok = 1; } } while (!caps_ok); if (changed_fs_disp_mode) { MessageBox(m_hwnd, "The fullscreen display mode selected from the config panel\r" "was invalid, for some reason. For now, the closest match\r" "(to the old selection) will be used.\r" "\r" "To fix this, please return to the config panel and select a new\r" "fullscreen display mode.\r" "\r" "The plugin will now run using the best match...\r" ,"WARNING", MB_OK|MB_SETFOREGROUND|MB_TOPMOST); } switch(m_current_mode.display_mode.Format) { case D3DFMT_R8G8B8 : m_bpp = 32; break; case D3DFMT_A8R8G8B8: m_bpp = 32; break; case D3DFMT_X8R8G8B8: m_bpp = 32; break; case D3DFMT_R5G6B5 : m_bpp = 16; break; case D3DFMT_X1R5G5B5: m_bpp = 16; break; case D3DFMT_A1R5G5B5: m_bpp = 16; break; case D3DFMT_A8R3G3B2: m_bpp = 16; break; case D3DFMT_A4R4G4B4: m_bpp = 16; break; case D3DFMT_X4R4G4B4: m_bpp = 16; break; case D3DFMT_R3G3B2 : m_bpp = 8; break; // misleading? implies a palette... } } // 5. set m_monitor_rect and m_monitor_work_rect. if (hPluginMonitor) { MONITORINFO mi; mi.cbSize = sizeof(mi); if (GetMonitorInfo(hPluginMonitor, &mi)) { m_monitor_rect = mi.rcMonitor; m_monitor_rect_orig = mi.rcMonitor; m_monitor_work_rect = mi.rcWork; m_monitor_work_rect_orig = mi.rcWork; } } // 6. embedded window stuff [where the plugin window is integrated w/winamp] if (m_current_mode.m_skin) { // set up the window's position on screen // note that we'd prefer to set the CLIENT size we want, but we can't, so we'll just do // this here, and later, adjust the client rect size to what's left... int size = GetWindowedModeAutoSize(0); // note: requires 'm_monitor_rect' has been set! myWindowState.r.left = GetPrivateProfileInt("settings","avs_wx",64,m_szIniFile); myWindowState.r.top = GetPrivateProfileInt("settings","avs_wy",64,m_szIniFile); myWindowState.r.right = myWindowState.r.left + GetPrivateProfileInt("settings","avs_ww",size+24,m_szIniFile); myWindowState.r.bottom = myWindowState.r.top + GetPrivateProfileInt("settings","avs_wh",size+40,m_szIniFile); // only works on winamp 2.90+! int success = 0; if (SendMessage(mod1.hwndParent,WM_WA_IPC,0,0) >= 0x2900) { HWND (*e)(embedWindowState *v); *(void**)&e = (void *)SendMessage(mod1.hwndParent,WM_WA_IPC,(LPARAM)0,IPC_GET_EMBEDIF); if (e) { m_current_mode.parent_window = e(&myWindowState); if (m_current_mode.parent_window) { SetWindowText(m_current_mode.parent_window,m_szWindowCaption); success = 1; } } } if (!success) m_current_mode.m_skin = 0; } // remember the client rect that was originally desired... RECT windowed_mode_desired_client_rect; windowed_mode_desired_client_rect.top = GetPrivateProfileInt("settings","nMainWndTop",-1,m_szIniFile); windowed_mode_desired_client_rect.left = GetPrivateProfileInt("settings","nMainWndLeft",-1,m_szIniFile); windowed_mode_desired_client_rect.right = GetPrivateProfileInt("settings","nMainWndRight",-1,m_szIniFile); windowed_mode_desired_client_rect.bottom = GetPrivateProfileInt("settings","nMainWndBottom",-1,m_szIniFile); // ...and in case windowed mode init fails severely, // set it up to try next time for a simple 256x256 window. WriteSafeWindowPos(); // 7. create the window, if not already created if (!m_hwnd) { m_hwnd = CreateWindowEx( MY_EXT_WINDOW_STYLE, // extended style m_szClassName, // class m_szWindowCaption, // caption MY_WINDOW_STYLE, // style 0, // left 0, // top 256, // temporary width 256, // temporary height m_current_mode.parent_window, // parent window NULL, // menu m_hInstance, // instance NULL ); // parms if (!m_hwnd) { m_lastErr = DXC_ERR_CREATEWIN; MessageBox(m_hwnd, "CreateWindow failed", "ERROR", MB_OK|MB_SETFOREGROUND|MB_TOPMOST); return FALSE; } SetWindowLong(m_hwnd, GWL_USERDATA, m_uWindowLong); if (m_current_mode.m_skin) ShowWindow(m_current_mode.parent_window,SW_SHOWNA); // showing the parent wnd will make it size the child, too } // 8. minimize winamp before creating devices & such, so there aren't // any confusing window-focus issues MinimizeWinamp(hPluginMonitor); // 9. loop to try and create the window. // if in windowed mode and not enough vidmem, it will try again w/smaller window // (repeatedly, until window client size would be < 64) int iteration = 0; int device_ok = 0; do { // set the window position if (m_current_mode.screenmode==DESKTOP || m_current_mode.screenmode==FAKE_FULLSCREEN) { int x = m_monitor_rect.right - m_monitor_rect.left; int y = m_monitor_rect.bottom - m_monitor_rect.top; if (x >= y*2) { // (pseudo-multimon modes like 2048x768) int mid = (m_monitor_rect.left + m_monitor_rect.right)/2; if (m_current_mode.m_dualhead_horz==1) // show on left side m_monitor_rect.right = mid; else if (m_current_mode.m_dualhead_horz==2) // show on right side m_monitor_rect.left = mid; } else if (y > x*4/3) { // (pseudo-multimon modes like 1024x1536) int mid = (m_monitor_rect.top + m_monitor_rect.bottom)/2; if (m_current_mode.m_dualhead_vert==1) // show on top half m_monitor_rect.bottom = mid; else if (m_current_mode.m_dualhead_vert==2) // show on bottom half m_monitor_rect.top = mid; } // recompute width & height (into x,y): x = m_monitor_rect.right - m_monitor_rect.left; y = m_monitor_rect.bottom - m_monitor_rect.top; m_client_width = x; m_client_height = y; m_window_width = x; m_window_height = y; if (m_current_mode.screenmode == DESKTOP) { // note: we initially hide the window, and then // only display it once the desktop is all nice & ready. // see CPluginShell::DrawAndDisplay(). SetWindowPos(m_hwnd,HWND_BOTTOM,m_monitor_rect.left,m_monitor_rect.top,m_window_width,m_window_height,SWP_HIDEWINDOW); } else // FAKE_FULLSCREEN { if (memcmp(&m_all_monitors_rect, &m_monitor_rect, sizeof(RECT))==0) { // there's only one display, and it's entirely covered // by the plugin -> PUT THE PLUGIN ABOVE THE TASKBAR // -> normally, if the user clicked another window, // it would pop the taskbar to the top; but we don't // have to worry about that here, since we're taking // up the whole screen. // -> don't worry about making the text, etc. avoid // the taskbar in this case (see DrawAndDisplay()) // -> DO worry about hiding the mouse cursor in this case // (see WM_SETCURSOR handler) m_fake_fs_covers_all = 1; //SetWindowPos(m_hwnd,HWND_TOPMOST,m_monitor_rect.left,m_monitor_rect.top,m_window_width,m_window_height,SWP_SHOWWINDOW); } else { // there is space to work outside of the plugin window. // -> here we pretty much have to let the taskbar stay on // top, because it really likes to be there; i.e., // if you click any other window, it automatically // pops up again. // -> therefore, TRY TO KEEP THE WINDOW ON BOTTOM // (below the taskbar). (see PushWindowToBack) // -> don't worry about hiding the mouse cursor in this case // (see WM_SETCURSOR handler) // -> DO worry about making the text, etc. avoid // the taskbar in this case (see DrawAndDisplay()) // (note that if taskbar is in the way, they can move it, // since there are other monitors available) m_fake_fs_covers_all = 0; //SetWindowPos(m_hwnd,HWND_TOP,m_monitor_rect.left,m_monitor_rect.top,m_window_width,m_window_height,SWP_SHOWWINDOW); } SetWindowPos(m_hwnd,HWND_TOPMOST,m_monitor_rect.left,m_monitor_rect.top,m_window_width,m_window_height,SWP_SHOWWINDOW); } } else if (m_current_mode.screenmode == FULLSCREEN) { int x = m_current_mode.display_mode.Width ; int y = m_current_mode.display_mode.Height; int cx = m_monitor_rect.right - m_monitor_rect.left; int cy = m_monitor_rect.bottom - m_monitor_rect.top; // test #1 if (x >= y*2 || y > x*4/3) // tackle problem of vert/horz spans { int ret = MessageBox(m_hwnd, "You are trying to enter fullscreen mode while running\r" "multiple displays in a vertical or horizontal span,\r" "without using 'Fake Fullscreen Mode'. As a result,\r" "--the image will be stretched over both displays.--\r" "\r" "If you would prefer the plugin to appear on only one display\r" "(and still be free to operate on the other display),\r" "please return to the config panel, enable 'Fake Fullscreen\r" "Mode', click the 'DualHead' button to configure your DualHead\r" "setup, and then try again.\r" "\r" "Hit OK to proceed, or Cancel to exit now." ,"Tip", MB_OKCANCEL|MB_SETFOREGROUND|MB_TOPMOST); if (ret==IDCANCEL) { m_lastErr = DXC_ERR_USER_CANCELED; return FALSE; } } // test #2 if ((cx >= cy*2 && x < y*2) || (cy > cx*4/3 && y <= x*4/3)) { int ret = MessageBox(m_hwnd, "You are trying to enter fullscreen mode while running\r" "multiple displays in a vertical or horizontal span,\r" "but the display mode you are entering does not stretch\r" "over both displays. As a result, --the image will only\r" "appear on one display, and the other display will be disabled.--\r" "\r" "There are two alternatives:\r" "\r" "1. To make the fullscreen image appear on only ONE display\r" "AND still be free to operate on the other display,\r" "please return to the config panel and enable 'Fake Fullscreen Mode',\r" "then click 'DualHead' to select which screen you'd like the\r" "plugin to occupy. (--RECOMMENDED--)\r" "\r" "2. To make the fullscreen image stretch across BOTH displays,\r" "return to the config panel and select a display mode that\r" "spans both displays (such as 2048 x 768, or 1024 x 1536).\r" "\r" "Hit OK to continue, or Cancel to exit now." ,"Tip", MB_OKCANCEL|MB_SETFOREGROUND|MB_TOPMOST); if (ret==IDCANCEL) { m_lastErr = DXC_ERR_USER_CANCELED; return FALSE; } } m_client_width = x; m_client_height = y; m_window_width = x; m_window_height = y; SetWindowPos(m_hwnd,HWND_TOPMOST,m_monitor_rect.left,m_monitor_rect.top,m_window_width,m_window_height,SWP_SHOWWINDOW); } else // WINDOWED { RECT margin; if (m_current_mode.m_skin) { RECT r1, r2; GetWindowRect(GetParent(m_hwnd), &r1); GetWindowRect( m_hwnd , &r2); margin.left = r2.left - r1.left; margin.right = r1.right - r2.right; margin.top = r2.top - r1.top; margin.bottom= r1.bottom - r2.bottom; } else { RECT r1; SetRect(&r1, 0, 0, 256, 256); AdjustWindowRect(&r1, MY_WINDOW_STYLE, 0); margin.left = 0 - r1.left; margin.right = r1.right - 256; margin.top = 0 - r1.top; margin.bottom= r1.bottom - 256; } int autosize = 1; RECT r = windowed_mode_desired_client_rect; if (iteration==0 && r.top != -1 && r.left != -1 && r.bottom != -1 && r.right != -1) { // use prev. window coordinates: m_client_width = r.right - r.left; m_client_height = r.bottom - r.top; if (m_current_mode.m_skin) // check this here in case they got a non-aligned size by resizing when "integrated with winamp" was unchecked, then checked it & ran the plugin... { // STRANGE ALIGNMENTS FOR THE WINDOW FRAME: (required by winamp 2): // the window frame's width must be divisible by 25, and height by 29. m_client_width = ((m_client_width + margin.left + margin.right)/25)*25 - margin.left - margin.right; m_client_height = ((m_client_height + margin.top + margin.bottom)/29)*29 - margin.top - margin.bottom; } // transform screen-space CLIENT rect into screen-space WINDOW rect r.top = windowed_mode_desired_client_rect.top - margin.top; r.left = windowed_mode_desired_client_rect.left - margin.left; r.right = r.left + margin.left + m_client_width + margin.right; r.bottom = r.top + margin.top + m_client_height + margin.bottom; // make sure the window is entirely visible on the selected monitor; // otherwise, autosize/place it. // (note that this test is only appled 1) at startup, and 2) after a resize/max/restore. // this test is not applied when merely moving the window.) if (r.top >= m_monitor_work_rect.top && r.left >= m_monitor_work_rect.left && r.right <= m_monitor_work_rect.right && r.bottom <= m_monitor_work_rect.bottom) { if (m_current_mode.m_skin) { m_window_width = m_client_width ; // m_window_width/height are for OUR borderless window, not the embedwnd parent frame. m_window_height = m_client_height; SetWindowPos(GetParent(m_hwnd),HWND_NOTOPMOST, r.left, r.top, r.right-r.left, r.bottom-r.top, SWP_SHOWWINDOW); SetWindowPos( m_hwnd ,HWND_NOTOPMOST, windowed_mode_desired_client_rect.left, windowed_mode_desired_client_rect.top, m_client_width, m_client_height, SWP_SHOWWINDOW); } else { m_window_width = r.right - r.left; m_window_height = r.bottom - r.top; SetWindowPos(m_hwnd,HWND_NOTOPMOST,r.left,r.top,m_window_width,m_window_height,SWP_SHOWWINDOW); } autosize = 0; } } if (autosize) { int size = GetWindowedModeAutoSize(iteration); // note: requires 'm_monitor_rect' has been set! m_client_width = size; m_client_height = size; if (m_current_mode.m_skin) { // STRANGE ALIGNMENTS FOR THE WINDOW FRAME: (required by winamp 2): // the window frame's width must be divisible by 25, and height by 29. m_client_width = ((m_client_width + margin.left + margin.right)/25)*25 - margin.left - margin.right; m_client_height = ((m_client_height + margin.top + margin.bottom)/29)*29 - margin.top - margin.bottom; m_window_width = m_client_width ; // m_window_width/height are for OUR [borderless] window, not the parent window (which is the embedwnd frame). m_window_height = m_client_height; SetWindowPos(GetParent(m_hwnd),HWND_NOTOPMOST, m_monitor_work_rect.left+32, m_monitor_work_rect.top+32, m_client_width + margin.left + margin.right, m_client_height + margin.top + margin.bottom, SWP_SHOWWINDOW); SetWindowPos( m_hwnd ,HWND_NOTOPMOST, m_monitor_work_rect.left+32 + margin.left, m_monitor_work_rect.top+32 + margin.top, m_client_width, m_client_height, SWP_SHOWWINDOW); } else { SetRect(&r, 0, 0, size, size); AdjustWindowRect(&r, MY_WINDOW_STYLE, 0); m_window_width = r.right - r.left; m_window_height = r.bottom - r.top; SetWindowPos(m_hwnd,HWND_NOTOPMOST, m_monitor_work_rect.left+32, m_monitor_work_rect.top+32, m_window_width, m_window_height, SWP_SHOWWINDOW); } } } m_frame_delay = 1; // set this to 2 if you use triple buffering! { m_current_mode.display_mode.Width = m_client_width; m_current_mode.display_mode.Height = m_client_height; // set up m_d3dpp (presentation parameters): ZeroMemory(&m_d3dpp,sizeof(m_d3dpp)); m_d3dpp.Windowed = (m_current_mode.screenmode==FULLSCREEN) ? 0 : 1; m_d3dpp.BackBufferFormat = m_current_mode.display_mode.Format; m_d3dpp.BackBufferWidth = m_client_width; m_d3dpp.BackBufferHeight = m_client_height; m_d3dpp.BackBufferCount = m_current_mode.nbackbuf; if (m_current_mode.screenmode==FULLSCREEN) m_d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;//D3DSWAPEFFECT_FLIP; else // windowed or fake FS m_d3dpp.SwapEffect = (m_current_mode.allow_page_tearing) ? D3DSWAPEFFECT_DISCARD : D3DSWAPEFFECT_COPY_VSYNC;//D3DSWAPEFFECT_DISCARD;//D3DSWAPEFFECT_FLIP; // note: multisampling is only allowed if swapeffect is DISCARD! m_d3dpp.MultiSampleType = (m_d3dpp.SwapEffect==D3DSWAPEFFECT_DISCARD) ? m_current_mode.multisamp : D3DMULTISAMPLE_NONE; //m_d3dpp.hDeviceWindow = m_hwnd; if (m_current_mode.screenmode==FULLSCREEN) { m_d3dpp.FullScreen_RefreshRateInHz = m_current_mode.display_mode.RefreshRate;//D3DPRESENT_RATE_DEFAULT; m_d3dpp.FullScreen_PresentationInterval = m_current_mode.allow_page_tearing ? D3DPRESENT_INTERVAL_IMMEDIATE : D3DPRESENT_INTERVAL_ONE;//D3DPRESENT_INTERVAL_IMMEDIATE;//D3DPRESENT_INTERVAL_ONE; } if (m_zFormat != D3DFMT_UNKNOWN) { m_d3dpp.EnableAutoDepthStencil=TRUE; m_d3dpp.AutoDepthStencilFormat=m_zFormat; } // finally, create the device: HRESULT hRes; if(FAILED(hRes = m_lpD3D->CreateDevice( m_ordinal_adapter, D3DDEVTYPE_HAL, m_hwnd, (m_caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) ? D3DCREATE_MIXED_VERTEXPROCESSING : D3DCREATE_SOFTWARE_VERTEXPROCESSING, &m_d3dpp, &m_lpDevice ) ) ) { int code = LOWORD(hRes); char str[1024]; if (code==2156) //D3DERR_NOTAVAILABLE { m_lastErr = DXC_ERR_CREATEDEV_NOT_AVAIL; char str[2048]; sprintf(str, "Unable to create a DirectX device. (D3DERR_NOTAVAILABLE)\r" "\r" "This could mean that you've chosen a combination of settings that is\r" "not available on your video card. Try resetting the plugin to its\r" "default settings (via the config panel's 'Default' button), and then\r" "try running the plugin again.\r" "\r" "You might also want to close all other applications, to make sure they're\r" "not interfering.\r" "\r" "If you have made any changes to your graphics subsystem since your\r" "last reboot (such as updating video drivers, installing new software,\r" "etc.), or have witnessed any strange behaviors, TRY REBOOTING first.\r" "\r" ); if (m_current_mode.screenmode == FULLSCREEN) strcat(str, "NOTE: If you are trying to run the plugin on an older display adapter\r" "(such as a Voodoo3 card), try going to the config panel (ALT+K)\r" "and selecting a fullscreen display mode of a different color depth;\r" "some of these older cards can only do 3D in particular color depths\r" "(such as 16-bit color, for the Voodoo 3).\r" ); else strcat(str, "NOTE: If you are trying to run the plugin on an older display adapter\r" "(such as a Voodoo3 card), try changing the color depth that Windows\r" "is running in; some of these cards can only do 3D in particular color depths\r" "(such as 16-bit color, for the Voodoo3).\r" ); MessageBox(m_hwnd,str,"ERROR", MB_OK|MB_SETFOREGROUND|MB_TOPMOST); return FALSE; } else if (m_current_mode.screenmode==WINDOWED && m_client_width>64) { // DO NOTHING; try again w/smaller window } else if (m_current_mode.screenmode != WINDOWED || m_client_width <= 64) { // usually, code==2154 here, which is D3DERR_OUTOFVIDEOMEMORY m_lastErr = DXC_ERR_CREATEDEV_PROBABLY_OUTOFVIDEOMEMORY; sprintf(str, "DirectX initialization failed (CreateDevice; code %d)\r" "\r" "Often this means you don't have enough free video memory.\r" ,LOWORD(hRes)); // NOTE: *A 'SUGGESTION' SCREEN SHOULD APPEAR NEXT, PROVIDED BY THE CALLER* MessageBox(m_hwnd, str, "ERROR", MB_OK|MB_SETFOREGROUND|MB_TOPMOST); return FALSE; } } else { device_ok = 1; } } iteration++; } while (!device_ok); // set initial viewport SetViewport(); // for desktop mode, push window to back again: if (m_current_mode.screenmode==DESKTOP) SetWindowPos(m_hwnd,HWND_BOTTOM,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE); if (m_current_mode.m_skin) { SetFocus(m_current_mode.parent_window); //SetActiveWindow(m_current_mode.parent_window); //SetForegroundWindow(m_current_mode.parent_window); } if (m_current_mode.screenmode == WINDOWED) SaveWindow(); // return success m_ready = TRUE; return TRUE; } BOOL DXContext::StartOrRestartDevice(DXCONTEXT_PARAMS *pParams) { // call this to [re]initialize the DirectX environment with new parameters. // examples: startup; toggle windowed/fullscreen mode; change fullscreen resolution; // and so on. // be sure to clean up all your DirectX stuff first (textures, vertex buffers, // D3DX allocations, etc.) and reallocate it afterwards! // note: for windowed mode, 'pParams->disp_mode' (w/h/r/f) is ignored. // destroy old window if (m_hwnd) { m_ignore_wm_destroy = 1; DestroyWindow(m_hwnd); m_ignore_wm_destroy = 0; m_hwnd = NULL; } if (myWindowState.me) { SetForegroundWindow(mod1.hwndParent); DestroyWindow(myWindowState.me); myWindowState.me = NULL; } if (!m_ready) { // first-time init: create a fresh new device return Internal_Init(pParams, TRUE); } else { // re-init: preserve the DX8 object (m_lpD3D), // but destroy and re-create the DX8 device (m_lpDevice). m_ready = FALSE; SafeRelease(m_lpDevice); // but leave the D3D object! RestoreWinamp(); return Internal_Init(pParams, FALSE); } } BOOL DXContext::OnUserResizeWindow(RECT *new_window_rect, RECT *new_client_rect) { // call this function on WM_EXITSIZEMOVE when running windowed. // don't bother calling this when fullscreen. // be sure to clean up all your DirectX stuff first (textures, vertex buffers, // D3DX allocations, etc.) and reallocate it afterwards! if (!m_ready || (m_current_mode.screenmode != WINDOWED)) return FALSE; if ((m_client_width == new_client_rect->right - new_client_rect->left) && (m_client_height == new_client_rect->bottom - new_client_rect->top) && (m_window_width == new_window_rect->right - new_window_rect->left) && (m_window_height == new_window_rect->bottom - new_window_rect->top)) { return TRUE; } m_ready = FALSE; m_window_width = new_window_rect->right - new_window_rect->left; m_window_height = new_window_rect->bottom - new_window_rect->top; m_client_width = new_client_rect->right - new_client_rect->left; m_client_height = new_client_rect->bottom - new_client_rect->top; m_d3dpp.BackBufferWidth = m_client_width; m_d3dpp.BackBufferHeight = m_client_height; if (m_lpDevice->Reset( &m_d3dpp ) != D3D_OK) { WriteSafeWindowPos(); char str[1024]; sprintf(str, "Window resize failed.\r" "\r" "Often this means the application ran out of video memory;\r" " perhaps you tried to make the window too large.\r" ); MessageBox(m_hwnd, str, "OUT OF VIDEO MEMORY", MB_OK|MB_SETFOREGROUND|MB_TOPMOST); m_lastErr = DXC_ERR_RESIZEFAILED; return FALSE; } SetViewport(); m_ready = TRUE; return TRUE; } void DXContext::SetViewport() { D3DVIEWPORT8 v; v.X = 0; v.Y = 0; v.Width = m_client_width; v.Height = m_client_height; v.MinZ = 0.0f; v.MaxZ = 1.0f; m_lpDevice->SetViewport(&v); } void DXContext::MinimizeWinamp(HMONITOR hPluginMonitor) { // minimize Winamp window HMONITOR hWinampMon = MonitorFromWindow(m_hwnd_winamp, MONITOR_DEFAULTTONEAREST); HMONITOR hPluginMon = hPluginMonitor;//MonitorFromWindow(m_hwnd, MONITOR_DEFAULTTONEAREST);//m_lpD3D->GetAdapterMonitor(ordinal_adapter); if ((m_current_mode.screenmode == FULLSCREEN || m_current_mode.screenmode == FAKE_FULLSCREEN) && (m_minimize_winamp) && (hWinampMon && hPluginMon && hPluginMon==hWinampMon) && (!m_winamp_minimized) ) { // nitpicky check: if we're in fake fullscreen mode // and are only going to display on half the screen, // don't minimize Winamp. if (m_current_mode.screenmode == FAKE_FULLSCREEN) { int x = m_monitor_rect.right - m_monitor_rect.left; int y = m_monitor_rect.bottom - m_monitor_rect.top; if ((x >= y*2 && m_current_mode.m_dualhead_horz != 0) || (y > x*4/3 && m_current_mode.m_dualhead_vert != 0)) { return; } } ShowWindow(m_hwnd_winamp, SW_MINIMIZE); // also restore the focus to the plugin window, since this will steal it: SetFocus(m_hwnd); SetActiveWindow(m_hwnd); SetForegroundWindow(m_hwnd); m_winamp_minimized = 1; } } void DXContext::RestoreWinamp() { if (m_winamp_minimized) { ShowWindow(m_hwnd_winamp, SW_RESTORE); m_winamp_minimized = 0; } } void DXContext::UpdateMonitorWorkRect() { // get active monitor's bounding rectangle (to assist w/window positioning) // note: in vert/horz span setups (psuedo-multimon), // this will be 2048x768 or 1024x1536 or something like that. // calling this each frame allows you to detect when the taskbar // moves around on the screen (from edge to edge), and rearrange // the visual elements accordingly, so nothing is obscured. HMONITOR hMon = MonitorFromWindow(m_hwnd, MONITOR_DEFAULTTONEAREST);//m_lpD3D->GetAdapterMonitor(m_ordinal_adapter); if (hMon) { MONITORINFO mi; mi.cbSize = sizeof(mi); if (GetMonitorInfo(hMon, &mi)) { m_monitor_work_rect = mi.rcWork; m_monitor_work_rect_orig = mi.rcWork; // if the monitor rect we're using is the same as the // whole area of the monitor, there's no need to update it... //if (memcmp(&mi.rcMonitor, &m_monitor_rect, sizeof(RECT))==0) // return; // otherwise, we're doing a half-screen special case // and are running in some pseudo-multimon res like // 2048x768 or 1024x1536, but only using half of it // (i.e. fake fullscreen or desktop mode) // therefore... we need to update the work-area rectangle // to reflect which half of the screen it's on. if (m_monitor_rect.left == mi.rcMonitor.left) m_monitor_work_rect.left = mi.rcWork.left; else m_monitor_work_rect.left = m_monitor_rect.left + (mi.rcWork.left - mi.rcMonitor.left); if (m_monitor_rect.top == mi.rcMonitor.top) m_monitor_work_rect.top = mi.rcWork.top; else m_monitor_work_rect.top = m_monitor_rect.top + (mi.rcWork.top - mi.rcMonitor.top); if (m_monitor_rect.right == mi.rcMonitor.right) m_monitor_work_rect.right = mi.rcWork.right; else m_monitor_work_rect.right = m_monitor_rect.right; if (m_monitor_rect.bottom == mi.rcMonitor.bottom) m_monitor_work_rect.bottom = mi.rcWork.bottom; else m_monitor_work_rect.bottom = m_monitor_rect.bottom; } } } void DXContext::SaveWindow() { if (m_current_mode.screenmode == WINDOWED) { RECT c; GetClientRect( m_hwnd, &c ); // convert client rect from client coords to screen coords: // (window rect is already in screen coords...) POINT p; p.x = c.left; p.y = c.top; if (ClientToScreen(m_hwnd, &p)) { c.left += p.x; c.right += p.x; c.top += p.y; c.bottom += p.y; } // save bounds for window CLIENT area, but in screen coords WritePrivateProfileInt(c.top, "nMainWndTop", m_szIniFile, "settings"); WritePrivateProfileInt(c.left, "nMainWndLeft", m_szIniFile, "settings"); WritePrivateProfileInt(c.right, "nMainWndRight", m_szIniFile, "settings"); WritePrivateProfileInt(c.bottom,"nMainWndBottom", m_szIniFile, "settings"); // also save bounds for embedwnd if (m_current_mode.m_skin && myWindowState.me) { WritePrivateProfileInt(myWindowState.r.left,"avs_wx",m_szIniFile,"settings"); WritePrivateProfileInt(myWindowState.r.top ,"avs_wy",m_szIniFile,"settings"); WritePrivateProfileInt(myWindowState.r.right-myWindowState.r.left,"avs_ww",m_szIniFile,"settings"); WritePrivateProfileInt(myWindowState.r.bottom-myWindowState.r.top,"avs_wh",m_szIniFile,"settings"); } else if (!m_current_mode.m_skin && m_hwnd) { RECT r; GetWindowRect(m_hwnd, &r); WritePrivateProfileInt(r.left,"avs_wx",m_szIniFile,"settings"); WritePrivateProfileInt(r.top ,"avs_wy",m_szIniFile,"settings"); WritePrivateProfileInt(r.right-r.left,"avs_ww",m_szIniFile,"settings"); WritePrivateProfileInt(r.bottom-r.top,"avs_wh",m_szIniFile,"settings"); } } }