diff options
author | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
---|---|---|
committer | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
commit | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Visualization/vis_milk2/pluginshell.cpp | |
parent | 537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff) | |
download | winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz |
Initial community commit
Diffstat (limited to 'Src/Plugins/Visualization/vis_milk2/pluginshell.cpp')
-rw-r--r-- | Src/Plugins/Visualization/vis_milk2/pluginshell.cpp | 3693 |
1 files changed, 3693 insertions, 0 deletions
diff --git a/Src/Plugins/Visualization/vis_milk2/pluginshell.cpp b/Src/Plugins/Visualization/vis_milk2/pluginshell.cpp new file mode 100644 index 00000000..cd79a410 --- /dev/null +++ b/Src/Plugins/Visualization/vis_milk2/pluginshell.cpp @@ -0,0 +1,3693 @@ +/* + LICENSE + ------- +Copyright 2005-2013 Nullsoft, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of Nullsoft nor the names of its contributors may be used to + endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + TO DO + ----- + -done/v1.06: + -(nothing yet) + - + - + -to do/v1.06: + -FFT: high freq. data kinda sucks because of the 8-bit samples we get in; + look for justin to put 16-bit vis data into wa5. + -make an 'advanced view' button on config panel; hide complicated stuff + til they click that. + -put an asterisk(*) next to the 'max framerate' values that + are ideal (given the current windows display mode or selected FS dispmode). + -or add checkbox: "smart sync" + -> matches FPS limit to nearest integer divisor of refresh rate. + -debug.txt/logging support! + -audio: make it a DSP plugin? then we could get the complete, continuous waveform + and overlap our waveform windows, so we'd never miss a brief high note. + -bugs: + -vms plugins sometimes freeze after a several-minute pause; I've seen it + with most of them. hard to repro, though. + -running FS on monitor 2, hit ALT-TAB -> minimizes!!! + -but only if you let go of TAB first. Let go of ALT first and it's fine! + -> means it's related to the keyup... + -fix delayloadhelper leak; one for each launch to config panel/plugin. + -also, delayload(d3d9.dll) still leaks, if plugin has error initializing and + quits by returning false from PluginInitialize(). + -add config panel option to ignore fake-fullscreen tips + -"tip" boxes in dxcontext.cpp + -"notice" box on WM_ACTIVATEAPP? + -desktop mode: + -icon context menus: 'send to', 'cut', and 'copy' links do nothing. + -http://netez.com/2xExplorer/shellFAQ/bas_context.html + -create a 2nd texture to render all icon text labels into + (they're the sole reason that desktop mode is slow) + -in UpdateIconBitmaps, don't read the whole bitmap and THEN + realize it's a dupe; try to compare icon filename+index or somethign? + -DRAG AND DROP. COMPLICATED; MANY DETAILS. + -http://netez.com/2xExplorer/shellFAQ/adv_drag.html + -http://www.codeproject.com/shell/explorerdragdrop.asp + -hmm... you can't drag icons between the 2 desktops (ugh) + -multiple delete/open/props/etc + -delete + enter + arrow keys. + -try to solve mysteries w/ShellExecuteEx() and desktop *shortcuts* (*.lnk). + -(notice that when icons are selected, they get modulated by the + highlight color, when they should be blended 50% with that color.) + + --------------------------- + final touches: + -Tests: + -make sure desktop still functions/responds properly when winamp paused + -desktop mode + multimon: + -try desktop mode on all monitors + -try moving taskbar around; make sure icons are in the + right place, that context menus (general & for + specific icons) pop up in the right place, and that + text-off-left-edge is ok. + -try setting the 2 monitors to different/same resolutions + -check tab order of config panel controls! + -Clean All + -build in release mode to include in the ZIP + -leave only one file open in workspace: README.TXT. + -TEMPORARILY "ATTRIB -R" ALL FILES BEFORE ZIPPING THEM! + + --------------------------- + KEEP IN VIEW: + -EMBEDWND: + -kiv: on resize of embedwnd, it's out of our control; winamp + resizes the child every time the mouse position changes, + and we have to cleanup & reallocate everything, b/c we + can't tell when the resize begins & ends. + [justin said he'd fix in wa5, though] + -kiv: with embedded windows of any type (plugin, playlist, etc.) + you can't place the winamp main wnd over them. + -kiv: embedded windows are child windows and don't get the + WM_SETFOCUS or WM_KILLFOCUS messages when they get or lose + the focus. (For a workaround, see milkdrop & scroll lock key.) + -kiv: tiny bug (IGNORE): when switching between embedwnd & + no-embedding, the window gets scooted a tiny tiny bit. + -kiv: fake fullscreen mode w/multiple monitors: there is no way + to keep the taskbar from popping up [potentially overtop of + the plugin] when you click on something besides the plugin. + To get around this, use true fullscreen mode. + -kiv: max_fps implementation assumptions: + -that most computers support high-precision timer + -that no computers [regularly] sleep for more than 1-2 ms + when you call Sleep(1) after timeBeginPeriod(1). + -reminder: if vms_desktop.dll's interface needs changed, + it will have to be renamed! (version # upgrades are ok + as long as it won't break on an old version; if the + new functionality is essential, rename the DLL.) + + --------------------------- + REMEMBER: + -GF2MX + GF4 have icon scooting probs in desktop mode + (when taskbar is on upper or left edge of screen) + -Radeon is the one w/super slow text probs @ 1280x1024. + (it goes unstable after you show playlist AND helpscr; -> ~1 fps) + -Mark's win98 machine has hidden cursor (in all modes), + but no one else seems to have this problem. + -links: + -win2k-only-style desktop mode: (uses VirtualAllocEx, vs. DLL Injection) + http://www.digiwar.com/scripts/renderpage.php?section=2&subsection=2 + -http://www.experts-exchange.com/Programming/Programming_Platforms/Win_Prog/Q_20096218.html +*/ + +#include "api__vis_milk2.h" +#include "pluginshell.h" +#include "utility.h" +#include "defines.h" +#include "shell_defines.h" +#include "resource.h" +#include "vis.h" +#include <multimon.h> +#include "../Winamp/wa_ipc.h" +#include "../nu/AutoCharFn.h" +#include <mmsystem.h> +#pragma comment(lib,"winmm.lib") // for timeGetTime + +// STATE VALUES & VERTEX FORMATS FOR HELP SCREEN TEXTURE: +#define TEXT_SURFACE_NOT_READY 0 +#define TEXT_SURFACE_REQUESTED 1 +#define TEXT_SURFACE_READY 2 +#define TEXT_SURFACE_ERROR 3 +typedef struct _HELPVERTEX +{ + float x, y; // screen position + float z; // Z-buffer depth + DWORD Diffuse; // diffuse color. also acts as filler; aligns struct to 16 bytes (good for random access/indexed prims) + float tu, tv; // texture coordinates for texture #0 +} HELPVERTEX, *LPHELPVERTEX; +#define HELP_VERTEX_FORMAT (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1) +typedef struct _SIMPLEVERTEX +{ + float x, y; // screen position + float z; // Z-buffer depth + DWORD Diffuse; // diffuse color. also acts as filler; aligns struct to 16 bytes (good for random access/indexed prims) +} SIMPLEVERTEX, *LPSIMPLEVERTEX; +#define SIMPLE_VERTEX_FORMAT (D3DFVF_XYZ | D3DFVF_DIFFUSE) + +extern wchar_t* g_szHelp; +extern int g_szHelp_W; +extern winampVisModule mod1; + +// resides in vms_desktop.dll/lib: +void getItemData(int x); + + +CPluginShell::CPluginShell() +{ + // this should remain empty! +} + +CPluginShell::~CPluginShell() +{ + // this should remain empty! +} + +eScrMode CPluginShell::GetScreenMode() +{ + return m_screenmode; +} +int CPluginShell::GetFrame() +{ + return m_frame; +} +float CPluginShell::GetTime() +{ + return m_time; +} +float CPluginShell::GetFps() +{ + return m_fps; +} +HWND CPluginShell::GetPluginWindow() +{ + if (m_lpDX) return m_lpDX->GetHwnd(); else return NULL; +} +int CPluginShell::GetWidth() +{ + if (m_lpDX) return m_lpDX->m_client_width; else return 0; +} +int CPluginShell::GetHeight() +{ + if (m_lpDX) return m_lpDX->m_client_height; else return 0; +} +int CPluginShell::GetCanvasMarginX() +{ + if (m_lpDX && m_screenmode==WINDOWED) return (m_lpDX->m_client_width - m_lpDX->m_REAL_client_width)/2; else return 0; +} +int CPluginShell::GetCanvasMarginY() +{ + if (m_lpDX && m_screenmode==WINDOWED) return (m_lpDX->m_client_height - m_lpDX->m_REAL_client_height)/2; else return 0; +} +HWND CPluginShell::GetWinampWindow() +{ + return m_hWndWinamp; +} +HINSTANCE CPluginShell::GetInstance() +{ + return m_hInstance; +} +wchar_t* CPluginShell::GetPluginsDirPath() +{ + return m_szPluginsDirPath; +} +wchar_t* CPluginShell::GetConfigIniFile() +{ + return m_szConfigIniFile; +} +char* CPluginShell::GetConfigIniFileA() +{ + return m_szConfigIniFileA; +} +int CPluginShell::GetFontHeight(eFontIndex idx) +{ + if (idx >= 0 && idx < NUM_BASIC_FONTS + NUM_EXTRA_FONTS) return m_fontinfo[idx].nSize; else return 0; +} +int CPluginShell::GetBitDepth() +{ + return m_lpDX->GetBitDepth(); +} +LPDIRECT3DDEVICE9 CPluginShell::GetDevice() +{ + if (m_lpDX) return m_lpDX->m_lpDevice; else return NULL; +} +D3DCAPS9* CPluginShell::GetCaps() +{ + if (m_lpDX) return &(m_lpDX->m_caps); else return NULL; +} +D3DFORMAT CPluginShell::GetBackBufFormat() +{ + if (m_lpDX) return m_lpDX->m_current_mode.display_mode.Format; else return D3DFMT_UNKNOWN; +} +D3DFORMAT CPluginShell::GetBackBufZFormat() +{ + if (m_lpDX) return m_lpDX->GetZFormat(); else return D3DFMT_UNKNOWN; +} +LPD3DXFONT CPluginShell::GetFont(eFontIndex idx) +{ + if (idx >= 0 && idx < NUM_BASIC_FONTS + NUM_EXTRA_FONTS) return m_d3dx_font[idx]; else return NULL; +} +char* CPluginShell::GetDriverFilename() +{ + if (m_lpDX) return m_lpDX->GetDriver(); else return NULL; +} +char* CPluginShell::GetDriverDescription() +{ + if (m_lpDX) return m_lpDX->GetDesc(); else return NULL; +} + +int CPluginShell::InitNondx9Stuff() +{ + timeBeginPeriod(1); + m_fftobj.Init(576, NUM_FREQUENCIES); + if (!InitGDIStuff()) return false; + return AllocateMyNonDx9Stuff(); +} + +void CPluginShell::CleanUpNondx9Stuff() +{ + timeEndPeriod(1); + CleanUpMyNonDx9Stuff(); + CleanUpGDIStuff(); + m_fftobj.CleanUp(); +} + +int CPluginShell::InitGDIStuff() +{ + wchar_t title[64]; + // note: messagebox parent window should be NULL here, because lpDX is still NULL! + for (int i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++) + { + if (!(m_font[i] = CreateFontW(m_fontinfo[i].nSize, 0, 0, 0, m_fontinfo[i].bBold ? 900 : 400, m_fontinfo[i].bItalic, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, m_fontinfo[i].bAntiAliased ? ANTIALIASED_QUALITY : DEFAULT_QUALITY, DEFAULT_PITCH, m_fontinfo[i].szFace))) + { + MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_ERROR_CREATING_GDI_FONTS), + WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64), + MB_OK|MB_SETFOREGROUND|MB_TOPMOST); + return false; + } + } + + if (!(m_main_menu = WASABI_API_LOADMENU(IDR_WINDOWED_CONTEXT_MENU))) + { + MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_ERROR_LOADING_MAIN_MENU), + WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64), + MB_OK|MB_SETFOREGROUND|MB_TOPMOST); + return false; + } + + if (!(m_context_menu = GetSubMenu(m_main_menu, 0))) + { + MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_ERROR_LOADING_CONTEXT_MENU), + WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64), + MB_OK|MB_SETFOREGROUND|MB_TOPMOST); + return false; + } + + return true; +} + +void CPluginShell::CleanUpGDIStuff() +{ + for (int i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++) + { + if (m_font[i]) + { + DeleteObject(m_font[i]); + m_font[i] = NULL; + } + } + + /*if (m_context_menu) + { + DestroyMenu(m_context_menu); + m_context_menu = NULL; + }*/ + + if (m_main_menu) + { + DestroyMenu(m_main_menu); + m_main_menu = NULL; + } + + //CleanUpMyGDIStuff(); +} + +int CPluginShell::InitVJStuff(RECT* pClientRect) +{ + wchar_t title[64]; + // Init VJ mode (second window for text): + if (m_vj_mode) + { + DWORD dwStyle = WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_SYSMENU; + POINT upper_left_corner; + upper_left_corner.x = 0; + upper_left_corner.y = 0; + + // Create direct 3d & get some infos + if (!(m_vjd3d9 = Direct3DCreate9(D3D_SDK_VERSION))) + { + MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_ERROR_CREATING_DIRECT3D_DEVICE_FOR_VJ_MODE), + WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64), MB_OK|MB_SETFOREGROUND|MB_TOPMOST); + return false; + } + + // Get ordinal adapter # for the currently-selected Windowed Mode display adapter + int ordinal_adapter = D3DADAPTER_DEFAULT; + int nAdapters = m_vjd3d9->GetAdapterCount(); + for (int i=0; i<nAdapters; i++) + { + D3DADAPTER_IDENTIFIER9 temp; + if ((m_vjd3d9->GetAdapterIdentifier(i, /*D3DENUM_NO_WHQL_LEVEL*/ 0, &temp) == D3D_OK) && + (memcmp(&temp.DeviceIdentifier, &m_adapter_guid_windowed, sizeof(GUID))==0)) + { + ordinal_adapter = i; + break; + } + } + + // Get current display mode for windowed-mode adapter: + D3DDISPLAYMODE dm; + if (D3D_OK != m_vjd3d9->GetAdapterDisplayMode(ordinal_adapter, &dm)) + { + MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_VJ_MODE_INIT_ERROR), + WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64), + MB_OK|MB_SETFOREGROUND|MB_TOPMOST); + return false; + } + + // And get the upper-left corner of the monitor for it: + HMONITOR hMon = m_vjd3d9->GetAdapterMonitor(ordinal_adapter); + if (hMon) + { + MONITORINFO mi; + mi.cbSize = sizeof(mi); + if (GetMonitorInfo(hMon, &mi)) + { + upper_left_corner.x = mi.rcWork.left; + upper_left_corner.y = mi.rcWork.top; + } + } + + // CREATE THE WINDOW + + RECT rect; + if (pClientRect) + { + rect = *pClientRect; + AdjustWindowRect(&rect, dwStyle, 0); // convert client->wnd + } + else + { + SetRect(&rect, 0, 0, 384, 384); + AdjustWindowRect(&rect, dwStyle, 0); // convert client->wnd + + rect.right -= rect.left; + rect.left = 0; + rect.bottom -= rect.top; + rect.top = 0; + + rect.top += upper_left_corner.y+32; + rect.left += upper_left_corner.x+32; + rect.right += upper_left_corner.x+32; + rect.bottom += upper_left_corner.y+32; + } + + WNDCLASS wc = {0}; + wc.lpfnWndProc = VJModeWndProc; // our window procedure + wc.hInstance = GetInstance(); // hInstance of DLL + wc.hIcon = LoadIcon(GetInstance(), MAKEINTRESOURCE(IDI_PLUGIN_ICON)); + wc.lpszClassName = TEXT_WINDOW_CLASSNAME; // our window class name + wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; // CS_DBLCLKS lets the window receive WM_LBUTTONDBLCLK, for toggling fullscreen mode... + wc.cbWndExtra = sizeof(DWORD); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH); + + if (!RegisterClass(&wc)) + { + MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_ERROR_REGISTERING_WINDOW_CLASS_FOR_TEXT_WINDOW), + WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64), + MB_OK|MB_SETFOREGROUND|MB_TOPMOST); + return false; + } + m_bTextWindowClassRegistered = true; + + //DWORD nThreadID; + //CreateThread(NULL, 0, TextWindowThread, &rect, 0, &nThreadID); + + // Create the text window + m_hTextWnd = CreateWindowEx( + 0, + TEXT_WINDOW_CLASSNAME, // our window class name + TEXT_WINDOW_CLASSNAME, // use description for a window title + dwStyle, + rect.left, rect.top, // screen position (read from config) + rect.right - rect.left, rect.bottom - rect.top, // width & height of window (need to adjust client area later) + NULL, // parent window (winamp main window) + NULL, // no menu + GetInstance(), // hInstance of DLL + NULL + ); // no window creation data + + if (!m_hTextWnd) + { + MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_ERROR_CREATING_VJ_WINDOW), + WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64), + MB_OK|MB_SETFOREGROUND|MB_TOPMOST); + return false; + } + + SetWindowLongPtr(m_hTextWnd, GWLP_USERDATA, (LONG_PTR)this); + + GetClientRect(m_hTextWnd, &rect); + m_nTextWndWidth = rect.right-rect.left; + m_nTextWndHeight = rect.bottom-rect.top; + + + // Create the device + D3DPRESENT_PARAMETERS pres_param; + ZeroMemory(&pres_param,sizeof(pres_param)); + pres_param.BackBufferCount = 0; + pres_param.BackBufferFormat = dm.Format; + pres_param.BackBufferWidth = rect.right - rect.left; + pres_param.BackBufferHeight = rect.bottom - rect.top; + pres_param.hDeviceWindow = m_hTextWnd; + pres_param.AutoDepthStencilFormat = D3DFMT_D16; + pres_param.EnableAutoDepthStencil = FALSE; + pres_param.SwapEffect = D3DSWAPEFFECT_DISCARD; + pres_param.MultiSampleType = D3DMULTISAMPLE_NONE; + pres_param.Flags = 0; + pres_param.FullScreen_RefreshRateInHz = 0; + pres_param.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;//D3DPRESENT_INTERVAL_ONE;//D3DPRESENT_INTERVAL_IMMEDIATE;//m_current_mode.allow_page_tearing ? D3DPRESENT_INTERVAL_IMMEDIATE : D3DPRESENT_INTERVAL_ONE;//D3DPRESENT_INTERVAL_IMMEDIATE;//D3DPRESENT_INTERVAL_ONE; + //pres_param.FullScreen_PresentationInterval = 0; + pres_param.Windowed = TRUE; + + HRESULT hr; + if (D3D_OK != (hr = m_vjd3d9->CreateDevice(ordinal_adapter,//D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + m_hTextWnd, + D3DCREATE_SOFTWARE_VERTEXPROCESSING, + &pres_param, + &m_vjd3d9_device))) + { + m_vjd3d9_device = NULL; + MessageBoxW(m_lpDX->GetHwnd(), WASABI_API_LNGSTRINGW(IDS_ERROR_CREATING_D3D_DEVICE_FOR_VJ_MODE), + WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64), + MB_OK|MB_SETFOREGROUND|MB_TOPMOST); + return false; + } + + if (!AllocateFonts(m_vjd3d9_device)) + return false; + + if (m_fix_slow_text) // note that when not doing vj mode, m_lpDDSText is allocated in AllocateDX9Stuff + AllocateTextSurface(); + + m_text.Finish(); + m_text.Init(m_vjd3d9_device, m_lpDDSText, 0); + + m_bClearVJWindow = true; + } + + return true; +} + +void CPluginShell::CleanUpVJStuff() +{ + // ALWAYS set the textures to NULL before releasing textures, + // otherwise they might still have a hanging reference! + if (m_lpDX && m_lpDX->m_lpDevice) + { + for (int i=0; i<16; i++) + m_lpDX->m_lpDevice->SetTexture(i, NULL); + } + + if (m_vjd3d9_device) + { + for (int i=0; i<16; i++) + m_vjd3d9_device->SetTexture(i, NULL); + } + + if (!m_vj_mode) + return; + + // clean up VJ mode + { + CleanUpFonts(); + SafeRelease(m_lpDDSText); + + SafeRelease(m_vjd3d9_device); + SafeRelease(m_vjd3d9); + + if (m_hTextWnd) + { + //dumpmsg("Finish: destroying text window"); + DestroyWindow(m_hTextWnd); + m_hTextWnd = NULL; + //dumpmsg("Finish: text window destroyed"); + } + + if (m_bTextWindowClassRegistered) + { + //dumpmsg("Finish: unregistering text window class"); + UnregisterClass(TEXT_WINDOW_CLASSNAME,GetInstance()); // unregister window class + m_bTextWindowClassRegistered = false; + //dumpmsg("Finish: text window class unregistered"); + } + } +} + +int CPluginShell::AllocateFonts(IDirect3DDevice9* pDevice) +{ + // Create D3DX system font: + for (int i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++) + if (pCreateFontW(pDevice, //m_font[i], + m_fontinfo[i].nSize, + m_fontinfo[i].nSize*4/10, + m_fontinfo[i].bBold ? 900 : 400, + 1, // mip levels + m_fontinfo[i].bItalic, + DEFAULT_CHARSET, + OUT_DEFAULT_PRECIS, + m_fontinfo[i].bAntiAliased ? ANTIALIASED_QUALITY : DEFAULT_QUALITY, + DEFAULT_PITCH, + m_fontinfo[i].szFace, + &m_d3dx_font[i] + ) != D3D_OK) + { + wchar_t title[64]; + MessageBoxW(m_lpDX ? m_lpDX->GetHwnd() : NULL, WASABI_API_LNGSTRINGW(IDS_ERROR_CREATING_D3DX_FONTS), + WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64), + MB_OK|MB_SETFOREGROUND|MB_TOPMOST); + return false; + } + + // get actual font heights + for (int i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++) + { + RECT r; + SetRect(&r, 0, 0, 1024, 1024); + int h = m_d3dx_font[i]->DrawText(NULL, "M", -1, &r, DT_CALCRECT, 0xFFFFFFFF); + if (h>0) m_fontinfo[i].nSize = h; + } + + return true; +} + +void CPluginShell::CleanUpFonts() +{ + for (int i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++) + SafeRelease(m_d3dx_font[i]); +} + +void CPluginShell::AllocateTextSurface() +{ + IDirect3DDevice9 *pDevice = m_vjd3d9_device ? m_vjd3d9_device : GetDevice(); + int w = m_vjd3d9_device ? m_nTextWndWidth : GetWidth() ; + int h = m_vjd3d9_device ? m_nTextWndHeight : GetHeight(); + + if (D3D_OK != pCreateTexture(pDevice, w, h, 1, D3DUSAGE_RENDERTARGET, GetBackBufFormat(), D3DPOOL_DEFAULT, &m_lpDDSText)) + m_lpDDSText = NULL; // OK if there's not enough mem for it! + else + { + // if m_lpDDSText doesn't cover enough of screen, cancel it. + D3DSURFACE_DESC desc; + if (D3D_OK == m_lpDDSText->GetLevelDesc(0, &desc)) + { + if ((desc.Width < 256 && w >= 256) || + (desc.Height < 256 && h >= 256) || + (desc.Width /(float)w < 0.74f) || + (desc.Height/(float)h < 0.74f) + ) + { + m_lpDDSText->Release(); + m_lpDDSText = NULL; + } + } + } +} + +int CPluginShell::AllocateDX9Stuff() +{ + if (!m_vj_mode) + { + AllocateFonts(m_lpDX->m_lpDevice); + if (m_fix_slow_text) // note that when not doing vj mode, m_lpDDSText is allocated in AllocateDX9Stuff + AllocateTextSurface(); + } + + /* + // Create D3DX system font: + for (int i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++) + if (pCreateFontW(m_lpDX->m_lpDevice, + m_fontinfo[i].nSize, + m_fontinfo[i].nSize*4/10, + m_fontinfo[i].bBold ? 900 : 400, + 0, // mip levels + m_fontinfo[i].bItalic, + DEFAULT_CHARSET, + OUT_DEFAULT_PRECIS, + m_fontinfo[i].bAntiAliased ? ANTIALIASED_QUALITY : DEFAULT_QUALITY, + DEFAULT_PITCH, + m_fontinfo[i].szFace, + &m_d3dx_font[i] + ) != D3D_OK) + { + MessageBox(m_lpDX->GetHwnd(), "Error creating D3DX fonts", "ERROR", MB_OK|MB_SETFOREGROUND|MB_TOPMOST); + return false; + } + + // get actual font heights + for (i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++) + { + RECT r; + SetRect(&r, 0, 0, 1024, 1024); + int h = m_d3dx_font[i]->DrawText(NULL, "M", -1, &r, DT_CALCRECT, 0xFFFFFFFF); + if (h>0) m_fontinfo[i].nSize = h; + } + */ + + if (m_screenmode == DESKTOP) + if (!InitDesktopMode()) + return false; + + int ret = AllocateMyDX9Stuff(); + + // invalidate various 'caches' here: + m_playlist_top_idx = -1; // invalidating playlist cache forces recompute of playlist width + //m_icon_list.clear(); // clear desktop mode icon list, so it has to read the bitmaps back in + + if (!m_vj_mode) + { + m_text.Finish(); + m_text.Init(GetDevice(), m_lpDDSText, 1); + } + + return ret; +} + +void CPluginShell::CleanUpDX9Stuff(int final_cleanup) +{ + // ALWAYS unbind the textures before releasing textures, + // otherwise they might still have a hanging reference! + if (m_lpDX && m_lpDX->m_lpDevice) + { + for (int i=0; i<16; i++) + m_lpDX->m_lpDevice->SetTexture(i, NULL); + } + + if (m_screenmode == DESKTOP) + CleanUpDesktopMode(); + + if (!m_vj_mode) + { + for (int i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++) + SafeRelease(m_d3dx_font[i]); + SafeRelease(m_lpDDSText); + } + + CleanUpMyDX9Stuff(final_cleanup); +} + +void CPluginShell::OnUserResizeTextWindow() +{ + // Update window properties + RECT w, c; + GetWindowRect(m_hTextWnd, &w); + GetClientRect(m_hTextWnd, &c); + + WINDOWPLACEMENT wp; + ZeroMemory(&wp, sizeof(wp)); + wp.length = sizeof(wp); + GetWindowPlacement(m_hTextWnd, &wp); + + // 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_hTextWnd, &p)) + { + c.left += p.x; + c.right += p.x; + c.top += p.y; + c.bottom += p.y; + } + + if (wp.showCmd != SW_SHOWMINIMIZED) + { + if (m_nTextWndWidth != c.right-c.left || + m_nTextWndHeight != c.bottom-c.top) + { + CleanUpVJStuff(); + if (!InitVJStuff(&c)) + { + SuggestHowToFreeSomeMem(); + m_lpDX->m_ready = false; // flag to exit + return; + } + } + + // save the new window position: + //if (wp.showCmd==SW_SHOWNORMAL) + // SaveTextWindowPos(); + } +} + +void CPluginShell::OnUserResizeWindow() +{ + // Update window properties + RECT w, c; + GetWindowRect(m_lpDX->GetHwnd(), &w); + GetClientRect(m_lpDX->GetHwnd(), &c); + + WINDOWPLACEMENT wp; + ZeroMemory(&wp, sizeof(wp)); + wp.length = sizeof(wp); + GetWindowPlacement(m_lpDX->GetHwnd(), &wp); + + // 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_lpDX->GetHwnd(), &p)) + { + c.left += p.x; + c.right += p.x; + c.top += p.y; + c.bottom += p.y; + } + + if (wp.showCmd != SW_SHOWMINIMIZED) + { + int new_REAL_client_w = c.right-c.left; + int new_REAL_client_h = c.bottom-c.top; + + // kiv: could we just resize when the *snapped* w/h changes? slightly more ideal... + if (m_lpDX->m_REAL_client_width != new_REAL_client_w || + m_lpDX->m_REAL_client_height != new_REAL_client_h) + { + //CleanUpVJStuff(); + CleanUpDX9Stuff(0); + if (!m_lpDX->OnUserResizeWindow(&w, &c)) + { + // note: a basic warning messagebox will have already been given. + // now suggest specific advice on how to regain more video memory: + SuggestHowToFreeSomeMem(); + return; + } + if (!AllocateDX9Stuff()) + { + m_lpDX->m_ready = false; // flag to exit + return; + } + /*if (!InitVJStuff()) + { + m_lpDX->m_ready = false; // flag to exit + return; + }*/ + } + + // save the new window position: + if (wp.showCmd==SW_SHOWNORMAL) + m_lpDX->SaveWindow(); + } +} + +void CPluginShell::StuffParams(DXCONTEXT_PARAMS *pParams) +{ + pParams->screenmode = m_screenmode; + pParams->display_mode = m_disp_mode_fs; + pParams->nbackbuf = 1; + pParams->m_dualhead_horz = m_dualhead_horz; + pParams->m_dualhead_vert = m_dualhead_vert; + pParams->m_skin = (m_screenmode==WINDOWED) ? m_skin : 0; + switch (m_screenmode) + { + case WINDOWED: + pParams->allow_page_tearing = m_allow_page_tearing_w; + pParams->adapter_guid = m_adapter_guid_windowed; + pParams->multisamp = m_multisample_windowed; + strcpy(pParams->adapter_devicename, m_adapter_devicename_windowed); + break; + case FULLSCREEN: + case FAKE_FULLSCREEN: + pParams->allow_page_tearing = m_allow_page_tearing_fs; + pParams->adapter_guid = m_adapter_guid_fullscreen; + pParams->multisamp = m_multisample_fullscreen; + strcpy(pParams->adapter_devicename, m_adapter_devicename_fullscreen); + break; + case DESKTOP: + pParams->allow_page_tearing = m_allow_page_tearing_dm; + pParams->adapter_guid = m_adapter_guid_desktop; + pParams->multisamp = m_multisample_desktop; + strcpy(pParams->adapter_devicename, m_adapter_devicename_desktop); + break; + } + pParams->parent_window = (m_screenmode==DESKTOP) ? m_hWndDesktopListView : NULL; +} + +void CPluginShell::ToggleDesktop() +{ + CleanUpDX9Stuff(0); + + switch (m_screenmode) + { + case WINDOWED: + case FULLSCREEN: + case FAKE_FULLSCREEN: + m_screenmode = DESKTOP; + break; + case DESKTOP: + m_screenmode = WINDOWED; + break; + } + + DXCONTEXT_PARAMS params; + StuffParams(¶ms); + + if (!m_lpDX->StartOrRestartDevice(¶ms)) + { + // note: a basic warning messagebox will have already been given. + if (m_lpDX->m_lastErr == DXC_ERR_CREATEDEV_PROBABLY_OUTOFVIDEOMEMORY) + SuggestHowToFreeSomeMem(); + return; + } + + if (!AllocateDX9Stuff()) + { + m_lpDX->m_ready = false; // flag to exit + return; + } + + SetForegroundWindow(m_lpDX->GetHwnd()); + SetActiveWindow(m_lpDX->GetHwnd()); + SetFocus(m_lpDX->GetHwnd()); +} + +#define IPC_IS_PLAYING_VIDEO 501 // from wa_ipc.h +#define IPC_SET_VIS_FS_FLAG 631 // a vis should send this message with 1/as param to notify winamp that it has gone to or has come back from fullscreen mode + +void CPluginShell::ToggleFullScreen() +{ + CleanUpDX9Stuff(0); + + switch (m_screenmode) + { + case DESKTOP: + case WINDOWED: + m_screenmode = m_fake_fullscreen_mode ? FAKE_FULLSCREEN : FULLSCREEN; + if (m_screenmode == FULLSCREEN && SendMessage(GetWinampWindow(), WM_WA_IPC, 0, IPC_IS_PLAYING_VIDEO) > 1) + { + m_screenmode = FAKE_FULLSCREEN; + } + SendMessage(GetWinampWindow(), WM_WA_IPC, 1, IPC_SET_VIS_FS_FLAG); + break; + case FULLSCREEN: + case FAKE_FULLSCREEN: + m_screenmode = WINDOWED; + SendMessage(GetWinampWindow(), WM_WA_IPC, 0, IPC_SET_VIS_FS_FLAG); + break; + } + + DXCONTEXT_PARAMS params; + StuffParams(¶ms); + + if (!m_lpDX->StartOrRestartDevice(¶ms)) + { + // note: a basic warning messagebox will have already been given. + if (m_lpDX->m_lastErr == DXC_ERR_CREATEDEV_PROBABLY_OUTOFVIDEOMEMORY) + SuggestHowToFreeSomeMem(); + return; + } + + if (!AllocateDX9Stuff()) + { + m_lpDX->m_ready = false; // flag to exit + return; + } + + SetForegroundWindow(m_lpDX->GetHwnd()); + SetActiveWindow(m_lpDX->GetHwnd()); + SetFocus(m_lpDX->GetHwnd()); +} + +void CPluginShell::ToggleHelp() +{ + m_show_help = 1-m_show_help; + int ret = CheckMenuItem(m_context_menu, ID_SHOWHELP, MF_BYCOMMAND | (m_show_help ? MF_CHECKED : MF_UNCHECKED)); +} + +void CPluginShell::TogglePlaylist() +{ + m_show_playlist = 1-m_show_playlist; + m_playlist_top_idx = -1; // <- invalidates playlist cache + int ret = CheckMenuItem(m_context_menu, ID_SHOWPLAYLIST, MF_BYCOMMAND | (m_show_playlist ? MF_CHECKED : MF_UNCHECKED)); +} + +int CPluginShell::InitDirectX() +{ + m_lpDX = new DXContext(m_hWndWinamp,m_hInstance,CLASSNAME,WINDOWCAPTION,CPluginShell::WindowProc,(LONG_PTR)this, m_minimize_winamp, m_szConfigIniFile); + + if (!m_lpDX) + { + wchar_t title[64]; + MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_UNABLE_TO_INIT_DXCONTEXT), + WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64), + MB_OK|MB_SETFOREGROUND|MB_TOPMOST); + return FALSE; + } + + if (m_lpDX->m_lastErr != S_OK) + { + // warning messagebox will have already been given + delete m_lpDX; + return FALSE; + } + + // initialize graphics + DXCONTEXT_PARAMS params; + StuffParams(¶ms); + + if (!m_lpDX->StartOrRestartDevice(¶ms)) + { + // note: a basic warning messagebox will have already been given. + + if (m_lpDX->m_lastErr == DXC_ERR_CREATEDEV_PROBABLY_OUTOFVIDEOMEMORY) + { + // suggest specific advice on how to regain more video memory: + SuggestHowToFreeSomeMem(); + } + + delete m_lpDX; + m_lpDX = NULL; + return FALSE; + } + + return TRUE; +} + +void CPluginShell::CleanUpDirectX() +{ + SafeDelete(m_lpDX); +} + +int CPluginShell::PluginPreInitialize(HWND hWinampWnd, HINSTANCE hWinampInstance) +{ + // PROTECTED CONFIG PANEL SETTINGS (also see 'private' settings, below) + m_start_fullscreen = 0; + m_start_desktop = 0; + m_fake_fullscreen_mode = 0; + m_max_fps_fs = 30; + m_max_fps_dm = 30; + m_max_fps_w = 30; + m_show_press_f1_msg = 1; + m_allow_page_tearing_w = 1; + m_allow_page_tearing_fs = 0; + m_allow_page_tearing_dm = 0; + m_minimize_winamp = 1; + m_desktop_show_icons = 1; + m_desktop_textlabel_boxes = 1; + m_desktop_manual_icon_scoot = 0; + m_desktop_555_fix = 2; + m_dualhead_horz = 2; + m_dualhead_vert = 1; + m_save_cpu = 1; + m_skin = 1; + m_fix_slow_text = 0; + + // initialize font settings: + wcscpy(m_fontinfo[SIMPLE_FONT ].szFace, SIMPLE_FONT_DEFAULT_FACE); + m_fontinfo[SIMPLE_FONT ].nSize = SIMPLE_FONT_DEFAULT_SIZE ; + m_fontinfo[SIMPLE_FONT ].bBold = SIMPLE_FONT_DEFAULT_BOLD ; + m_fontinfo[SIMPLE_FONT ].bItalic = SIMPLE_FONT_DEFAULT_ITAL ; + m_fontinfo[SIMPLE_FONT ].bAntiAliased = SIMPLE_FONT_DEFAULT_AA ; + wcscpy(m_fontinfo[DECORATIVE_FONT].szFace, DECORATIVE_FONT_DEFAULT_FACE); + m_fontinfo[DECORATIVE_FONT].nSize = DECORATIVE_FONT_DEFAULT_SIZE; + m_fontinfo[DECORATIVE_FONT].bBold = DECORATIVE_FONT_DEFAULT_BOLD; + m_fontinfo[DECORATIVE_FONT].bItalic = DECORATIVE_FONT_DEFAULT_ITAL; + m_fontinfo[DECORATIVE_FONT].bAntiAliased = DECORATIVE_FONT_DEFAULT_AA ; + wcscpy(m_fontinfo[HELPSCREEN_FONT].szFace, HELPSCREEN_FONT_DEFAULT_FACE); + m_fontinfo[HELPSCREEN_FONT].nSize = HELPSCREEN_FONT_DEFAULT_SIZE; + m_fontinfo[HELPSCREEN_FONT].bBold = HELPSCREEN_FONT_DEFAULT_BOLD; + m_fontinfo[HELPSCREEN_FONT].bItalic = HELPSCREEN_FONT_DEFAULT_ITAL; + m_fontinfo[HELPSCREEN_FONT].bAntiAliased = HELPSCREEN_FONT_DEFAULT_AA ; + wcscpy(m_fontinfo[PLAYLIST_FONT ].szFace, PLAYLIST_FONT_DEFAULT_FACE); + m_fontinfo[PLAYLIST_FONT ].nSize = PLAYLIST_FONT_DEFAULT_SIZE; + m_fontinfo[PLAYLIST_FONT ].bBold = PLAYLIST_FONT_DEFAULT_BOLD; + m_fontinfo[PLAYLIST_FONT ].bItalic = PLAYLIST_FONT_DEFAULT_ITAL; + m_fontinfo[PLAYLIST_FONT ].bAntiAliased = PLAYLIST_FONT_DEFAULT_AA ; + +#if (NUM_EXTRA_FONTS >= 1) + wcscpy(m_fontinfo[NUM_BASIC_FONTS + 0].szFace, EXTRA_FONT_1_DEFAULT_FACE); + m_fontinfo[NUM_BASIC_FONTS + 0].nSize = EXTRA_FONT_1_DEFAULT_SIZE; + m_fontinfo[NUM_BASIC_FONTS + 0].bBold = EXTRA_FONT_1_DEFAULT_BOLD; + m_fontinfo[NUM_BASIC_FONTS + 0].bItalic = EXTRA_FONT_1_DEFAULT_ITAL; + m_fontinfo[NUM_BASIC_FONTS + 0].bAntiAliased = EXTRA_FONT_1_DEFAULT_AA; +#endif +#if (NUM_EXTRA_FONTS >= 2) + wcscpy(m_fontinfo[NUM_BASIC_FONTS + 1].szFace, EXTRA_FONT_2_DEFAULT_FACE); + m_fontinfo[NUM_BASIC_FONTS + 1].nSize = EXTRA_FONT_2_DEFAULT_SIZE; + m_fontinfo[NUM_BASIC_FONTS + 1].bBold = EXTRA_FONT_2_DEFAULT_BOLD; + m_fontinfo[NUM_BASIC_FONTS + 1].bItalic = EXTRA_FONT_2_DEFAULT_ITAL; + m_fontinfo[NUM_BASIC_FONTS + 1].bAntiAliased = EXTRA_FONT_2_DEFAULT_AA; +#endif +#if (NUM_EXTRA_FONTS >= 3) + strcpy(m_fontinfo[NUM_BASIC_FONTS + 2].szFace, EXTRA_FONT_3_DEFAULT_FACE); + m_fontinfo[NUM_BASIC_FONTS + 2].nSize = EXTRA_FONT_3_DEFAULT_SIZE; + m_fontinfo[NUM_BASIC_FONTS + 2].bBold = EXTRA_FONT_3_DEFAULT_BOLD; + m_fontinfo[NUM_BASIC_FONTS + 2].bItalic = EXTRA_FONT_3_DEFAULT_ITAL; + m_fontinfo[NUM_BASIC_FONTS + 2].bAntiAliased = EXTRA_FONT_3_DEFAULT_AA; +#endif +#if (NUM_EXTRA_FONTS >= 4) + strcpy(m_fontinfo[NUM_BASIC_FONTS + 3].szFace, EXTRA_FONT_4_DEFAULT_FACE); + m_fontinfo[NUM_BASIC_FONTS + 3].nSize = EXTRA_FONT_4_DEFAULT_SIZE; + m_fontinfo[NUM_BASIC_FONTS + 3].bBold = EXTRA_FONT_4_DEFAULT_BOLD; + m_fontinfo[NUM_BASIC_FONTS + 3].bItalic = EXTRA_FONT_4_DEFAULT_ITAL; + m_fontinfo[NUM_BASIC_FONTS + 3].bAntiAliased = EXTRA_FONT_4_DEFAULT_AA; +#endif +#if (NUM_EXTRA_FONTS >= 5) + strcpy(m_fontinfo[NUM_BASIC_FONTS + 4].szFace, EXTRA_FONT_5_DEFAULT_FACE); + m_fontinfo[NUM_BASIC_FONTS + 4].nSize = EXTRA_FONT_5_DEFAULT_SIZE; + m_fontinfo[NUM_BASIC_FONTS + 4].bBold = EXTRA_FONT_5_DEFAULT_BOLD; + m_fontinfo[NUM_BASIC_FONTS + 4].bItalic = EXTRA_FONT_5_DEFAULT_ITAL; + m_fontinfo[NUM_BASIC_FONTS + 4].bAntiAliased = EXTRA_FONT_5_DEFAULT_AA; +#endif + + m_disp_mode_fs.Width = DEFAULT_FULLSCREEN_WIDTH; + m_disp_mode_fs.Height = DEFAULT_FULLSCREEN_HEIGHT; + m_disp_mode_fs.Format = D3DFMT_UNKNOWN; + m_disp_mode_fs.RefreshRate = 60; + // better yet - in case there is no config INI file saved yet, use the current display mode (if detectable) as the default fullscreen res: + DEVMODE dm; + dm.dmSize = sizeof(dm); + dm.dmDriverExtra = 0; + if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm)) + { + m_disp_mode_fs.Width = dm.dmPelsWidth; + m_disp_mode_fs.Height = dm.dmPelsHeight; + m_disp_mode_fs.RefreshRate = dm.dmDisplayFrequency; + m_disp_mode_fs.Format = (dm.dmBitsPerPel==16) ? D3DFMT_R5G6B5 : D3DFMT_X8R8G8B8; + } + + // PROTECTED STRUCTURES/POINTERS + int i = 0; + for (int i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++) + m_d3dx_font[i] = NULL; + m_d3dx_desktop_font = NULL; + m_lpDDSText = NULL; + ZeroMemory(&m_sound, sizeof(td_soundinfo)); + for (int ch=0; ch<2; ch++) + for (i=0; i<3; i++) + { + m_sound.infinite_avg[ch][i] = m_sound.avg[ch][i] = m_sound.med_avg[ch][i] = m_sound.long_avg[ch][i] = 1.0f; + } + + // GENERAL PRIVATE STUFF + //m_screenmode: set at end (derived setting) + m_frame = 0; + m_time = 0; + m_fps = 30; + m_hWndWinamp = hWinampWnd; + m_hInstance = hWinampInstance; + m_lpDX = NULL; + m_szPluginsDirPath[0] = 0; // will be set further down + m_szConfigIniFile[0] = 0; // will be set further down + // m_szPluginsDirPath: + + wchar_t *p; + + if (hWinampWnd + && (p = (wchar_t *)SendMessage(hWinampWnd, WM_WA_IPC, 0, IPC_GETPLUGINDIRECTORYW)) + && p != (wchar_t *)1) + { + swprintf(m_szPluginsDirPath, L"%s\\", p); + } + else + { + // get path to INI file & read in prefs/settings right away, so DumpMsg works! + GetModuleFileNameW(m_hInstance, m_szPluginsDirPath, MAX_PATH); + wchar_t *p = m_szPluginsDirPath + wcslen(m_szPluginsDirPath); + while (p >= m_szPluginsDirPath && *p != L'\\') p--; + if (++p >= m_szPluginsDirPath) *p = 0; + } + + if (hWinampWnd + && (p = (wchar_t *)SendMessage(hWinampWnd, WM_WA_IPC, 0, IPC_GETINIDIRECTORYW)) + && p != (wchar_t *)1) + { + // load settings as well as coping with moving old settings to a contained folder + wchar_t m_szOldConfigIniFile[MAX_PATH] = {0}, temp[MAX_PATH] = {0}, temp2[MAX_PATH] = {0}; + swprintf(m_szOldConfigIniFile, L"%s\\Plugins\\%s", p, INIFILE); + swprintf(m_szConfigIniFile, L"%s\\Plugins\\%s%s", p, SUBDIR, INIFILE); + swprintf(temp, L"%s\\Plugins\\%s", p, SUBDIR); + swprintf(temp2, L"%s\\Plugins\\", p); + CreateDirectoryW(temp, NULL); + + if (PathFileExistsW(m_szOldConfigIniFile) && !PathFileExistsW(m_szConfigIniFile)) + { + MoveFileW(m_szOldConfigIniFile, m_szConfigIniFile); + + wchar_t m_szMsgIniFile[MAX_PATH] = {0}, m_szNewMsgIniFile[MAX_PATH] = {0}, + m_szImgIniFile[MAX_PATH] = {0}, m_szNewImgIniFile[MAX_PATH] = {0}, + m_szAdaptersFile[MAX_PATH] = {0}, m_szNewAdaptersFile[MAX_PATH] = {0}; + swprintf(m_szMsgIniFile, L"%s%s", temp2, MSG_INIFILE); + swprintf(m_szNewMsgIniFile, L"%s%s", temp, MSG_INIFILE); + swprintf(m_szImgIniFile, L"%s%s", temp2, IMG_INIFILE); + swprintf(m_szNewImgIniFile, L"%s%s", temp, IMG_INIFILE); + swprintf(m_szAdaptersFile, L"%s%s", temp2, ADAPTERSFILE); + swprintf(m_szNewAdaptersFile, L"%s%s", temp, ADAPTERSFILE); + + MoveFileW(m_szImgIniFile, m_szNewImgIniFile); + MoveFileW(m_szMsgIniFile, m_szNewMsgIniFile); + MoveFileW(m_szAdaptersFile, m_szNewAdaptersFile); + } + } + else + { + swprintf(m_szConfigIniFile, L"%s%s", m_szPluginsDirPath, INIFILE); + } + lstrcpyn(m_szConfigIniFileA,AutoCharFn(m_szConfigIniFile),MAX_PATH); + + // PRIVATE CONFIG PANEL SETTINGS + m_multisample_fullscreen = D3DMULTISAMPLE_NONE; + m_multisample_desktop = D3DMULTISAMPLE_NONE; + m_multisample_windowed = D3DMULTISAMPLE_NONE; + ZeroMemory(&m_adapter_guid_fullscreen, sizeof(GUID)); + ZeroMemory(&m_adapter_guid_desktop , sizeof(GUID)); + ZeroMemory(&m_adapter_guid_windowed , sizeof(GUID)); + m_adapter_devicename_windowed[0] = 0; + m_adapter_devicename_fullscreen[0] = 0; + m_adapter_devicename_desktop[0] = 0; + + + // PRIVATE RUNTIME SETTINGS + m_lost_focus = 0; + m_hidden = 0; + m_resizing = 0; + m_show_help = 0; + m_show_playlist = 0; + m_playlist_pos = 0; + m_playlist_pageups = 0; + m_playlist_top_idx = -1; + m_playlist_btm_idx = -1; + // m_playlist_width_pixels will be considered invalid whenever 'm_playlist_top_idx' is -1. + // m_playlist[256][256] will be considered invalid whenever 'm_playlist_top_idx' is -1. + m_exiting = 0; + m_upper_left_corner_y = 0; + m_lower_left_corner_y = 0; + m_upper_right_corner_y = 0; + m_lower_right_corner_y = 0; + m_left_edge = 0; + m_right_edge = 0; + m_force_accept_WM_WINDOWPOSCHANGING = 0; + + // PRIVATE - GDI STUFF + m_main_menu = NULL; + m_context_menu = NULL; + for (i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++) + m_font[i] = NULL; + m_font_desktop = NULL; + + // PRIVATE - DESKTOP MODE STUFF + m_icon_list.clear(); + for (i=0; i<MAX_ICON_TEXTURES; i++) + m_desktop_icons_texture[i] = NULL; + FindDesktopWindows(&m_hWndProgMan, &m_hWndDesktop, &m_hWndDesktopListView); + GetDesktopFolder(m_szDesktopFolder); + m_desktop_icon_size = 32; + m_desktop_dragging = 0; // '1' when user is dragging icons around + m_desktop_box = 0; // '1' when user is drawing a box + m_desktop_wc_registered = 0; + m_desktop_bk_color = 0xFF000000 | BGR2RGB(::GetSysColor(COLOR_BACKGROUND)); + m_desktop_text_color = 0xFF000000 | BGR2RGB(SendMessage(m_hWndDesktopListView, LVM_GETTEXTCOLOR, 0, 0)); + m_desktop_sel_color = 0xFF000000 | BGR2RGB(::GetSysColor(COLOR_HIGHLIGHT)); + m_desktop_sel_text_color = 0xFF000000 | BGR2RGB(::GetSysColor(COLOR_HIGHLIGHTTEXT)); + m_desktop_icon_state = 0; + m_desktop_icon_count = 0; + m_desktop_icon_update_frame = 0; + m_desktop_icons_disabled = 0; + m_vms_desktop_loaded = 0; + m_desktop_hook_set = 0; + + // PRIVATE - MORE TIMEKEEPING + m_last_raw_time = 0; + memset(m_time_hist, 0, sizeof(m_time_hist)); + m_time_hist_pos = 0; + if (!QueryPerformanceFrequency(&m_high_perf_timer_freq)) + m_high_perf_timer_freq.QuadPart = 0; + m_prev_end_of_frame.QuadPart = 0; + + // PRIVATE AUDIO PROCESSING DATA + //(m_fftobj needs no init) + memset(m_oldwave[0], 0, sizeof(float)*576); + memset(m_oldwave[1], 0, sizeof(float)*576); + m_prev_align_offset[0] = 0; + m_prev_align_offset[1] = 0; + m_align_weights_ready = 0; + + // SEPARATE TEXT WINDOW (FOR VJ MODE) + m_vj_mode = 0; + m_hidden_textwnd = 0; + m_resizing_textwnd = 0; + m_hTextWnd = NULL; + m_nTextWndWidth = 0; + m_nTextWndHeight = 0; + m_bTextWindowClassRegistered = false; + m_vjd3d9 = NULL; + m_vjd3d9_device = NULL; + + //----- + + m_screenmode = NOT_YET_KNOWN; + + OverrideDefaults(); + ReadConfig(); + + if (m_start_fullscreen) + { + m_screenmode = m_fake_fullscreen_mode ? FAKE_FULLSCREEN : FULLSCREEN; + if (m_screenmode == FULLSCREEN && SendMessage(GetWinampWindow(), WM_WA_IPC, 0, IPC_IS_PLAYING_VIDEO) > 1) + { + m_screenmode = FAKE_FULLSCREEN; + } + } + else if (m_start_desktop) + m_screenmode = DESKTOP; + else + m_screenmode = WINDOWED; + + MyPreInitialize(); + MyReadConfig(); + + //----- + + return TRUE; +} + +int CPluginShell::PluginInitialize() +{ + // note: initialize GDI before DirectX. Also separate them because + // when we change windowed<->fullscreen, or lose the device and restore it, + // we don't want to mess with any (persistent) GDI stuff. + + if (!InitDirectX()) return FALSE; // gives its own error messages + if (!InitNondx9Stuff()) return FALSE; // gives its own error messages + if (!AllocateDX9Stuff()) return FALSE; // gives its own error messages + if (!InitVJStuff()) return FALSE; + + return TRUE; +} + +void CPluginShell::PluginQuit() +{ + CleanUpVJStuff(); + CleanUpDX9Stuff(1); + CleanUpNondx9Stuff(); + CleanUpDirectX(); + + SetFocus(m_hWndWinamp); + SetActiveWindow(m_hWndWinamp); + SetForegroundWindow(m_hWndWinamp); +} + +wchar_t* BuildSettingName(wchar_t* name, int number){ +static wchar_t temp[64]; + swprintf(temp, L"%s%d", name, number); + return temp; +} + +void CPluginShell::READ_FONT(int n){ + GetPrivateProfileStringW(L"settings",BuildSettingName(L"szFontFace",n),m_fontinfo[n].szFace,m_fontinfo[n].szFace,sizeof(m_fontinfo[n].szFace), m_szConfigIniFile); + m_fontinfo[n].nSize = GetPrivateProfileIntW(L"settings",BuildSettingName(L"nFontSize",n),m_fontinfo[n].nSize ,m_szConfigIniFile); + m_fontinfo[n].bBold = GetPrivateProfileIntW(L"settings",BuildSettingName(L"bFontBold",n),m_fontinfo[n].bBold ,m_szConfigIniFile); + m_fontinfo[n].bItalic = GetPrivateProfileIntW(L"settings",BuildSettingName(L"bFontItalic",n),m_fontinfo[n].bItalic,m_szConfigIniFile); + m_fontinfo[n].bAntiAliased = GetPrivateProfileIntW(L"settings",BuildSettingName(L"bFontAA",n),m_fontinfo[n].bItalic,m_szConfigIniFile); +} + +void CPluginShell::ReadConfig() +{ + int old_ver = GetPrivateProfileIntW(L"settings",L"version" ,-1,m_szConfigIniFile); + int old_subver = GetPrivateProfileIntW(L"settings",L"subversion",-1,m_szConfigIniFile); + + // nuke old settings from prev. version: + if (old_ver < INT_VERSION) + return; + else if (old_subver < INT_SUBVERSION) + return; + + //D3DMULTISAMPLE_TYPE m_multisample_fullscreen; + //D3DMULTISAMPLE_TYPE m_multisample_desktop; + //D3DMULTISAMPLE_TYPE m_multisample_windowed; + m_multisample_fullscreen = (D3DMULTISAMPLE_TYPE)GetPrivateProfileIntW(L"settings",L"multisample_fullscreen",m_multisample_fullscreen,m_szConfigIniFile); + m_multisample_desktop = (D3DMULTISAMPLE_TYPE)GetPrivateProfileIntW(L"settings",L"multisample_desktop",m_multisample_desktop,m_szConfigIniFile); + m_multisample_windowed = (D3DMULTISAMPLE_TYPE)GetPrivateProfileIntW(L"settings",L"multisample_windowed" ,m_multisample_windowed ,m_szConfigIniFile); + + //GUID m_adapter_guid_fullscreen + //GUID m_adapter_guid_desktop + //GUID m_adapter_guid_windowed + char str[256]; + GetPrivateProfileString("settings","adapter_guid_fullscreen","",str,sizeof(str)-1,m_szConfigIniFileA); + TextToGuid(str, &m_adapter_guid_fullscreen); + GetPrivateProfileString("settings","adapter_guid_desktop","",str,sizeof(str)-1,m_szConfigIniFileA); + TextToGuid(str, &m_adapter_guid_desktop); + GetPrivateProfileString("settings","adapter_guid_windowed","",str,sizeof(str)-1,m_szConfigIniFileA); + TextToGuid(str, &m_adapter_guid_windowed); + GetPrivateProfileString("settings","adapter_devicename_fullscreen","",m_adapter_devicename_fullscreen,sizeof(m_adapter_devicename_fullscreen)-1,m_szConfigIniFileA); + GetPrivateProfileString("settings","adapter_devicename_desktop", "",m_adapter_devicename_desktop ,sizeof(m_adapter_devicename_desktop)-1,m_szConfigIniFileA); + GetPrivateProfileString("settings","adapter_devicename_windowed", "",m_adapter_devicename_windowed ,sizeof(m_adapter_devicename_windowed)-1,m_szConfigIniFileA); + + // FONTS + READ_FONT(0); + READ_FONT(1); + READ_FONT(2); + READ_FONT(3); +#if (NUM_EXTRA_FONTS >= 1) + READ_FONT(4); +#endif +#if (NUM_EXTRA_FONTS >= 2) + READ_FONT(5); +#endif +#if (NUM_EXTRA_FONTS >= 3) + READ_FONT(6); +#endif +#if (NUM_EXTRA_FONTS >= 4) + READ_FONT(7); +#endif +#if (NUM_EXTRA_FONTS >= 5) + READ_FONT(8); +#endif + + m_start_fullscreen = GetPrivateProfileIntW(L"settings",L"start_fullscreen",m_start_fullscreen,m_szConfigIniFile); + m_start_desktop = GetPrivateProfileIntW(L"settings",L"start_desktop" ,m_start_desktop ,m_szConfigIniFile); + m_fake_fullscreen_mode = GetPrivateProfileIntW(L"settings",L"fake_fullscreen_mode",m_fake_fullscreen_mode,m_szConfigIniFile); + m_max_fps_fs = GetPrivateProfileIntW(L"settings",L"max_fps_fs",m_max_fps_fs,m_szConfigIniFile); + m_max_fps_dm = GetPrivateProfileIntW(L"settings",L"max_fps_dm",m_max_fps_dm,m_szConfigIniFile); + m_max_fps_w = GetPrivateProfileIntW(L"settings",L"max_fps_w" ,m_max_fps_w ,m_szConfigIniFile); + m_show_press_f1_msg = GetPrivateProfileIntW(L"settings",L"show_press_f1_msg",m_show_press_f1_msg,m_szConfigIniFile); + m_allow_page_tearing_w = GetPrivateProfileIntW(L"settings",L"allow_page_tearing_w",m_allow_page_tearing_w,m_szConfigIniFile); + m_allow_page_tearing_fs= GetPrivateProfileIntW(L"settings",L"allow_page_tearing_fs",m_allow_page_tearing_fs,m_szConfigIniFile); + m_allow_page_tearing_dm= GetPrivateProfileIntW(L"settings",L"allow_page_tearing_dm",m_allow_page_tearing_dm,m_szConfigIniFile); + m_minimize_winamp = GetPrivateProfileIntW(L"settings",L"minimize_winamp",m_minimize_winamp,m_szConfigIniFile); + m_desktop_show_icons = GetPrivateProfileIntW(L"settings",L"desktop_show_icons",m_desktop_show_icons,m_szConfigIniFile); + m_desktop_textlabel_boxes = GetPrivateProfileIntW(L"settings",L"desktop_textlabel_boxes",m_desktop_textlabel_boxes,m_szConfigIniFile); + m_desktop_manual_icon_scoot = GetPrivateProfileIntW(L"settings",L"desktop_manual_icon_scoot",m_desktop_manual_icon_scoot,m_szConfigIniFile); + m_desktop_555_fix = GetPrivateProfileIntW(L"settings",L"desktop_555_fix",m_desktop_555_fix,m_szConfigIniFile); + m_dualhead_horz = GetPrivateProfileIntW(L"settings",L"dualhead_horz",m_dualhead_horz,m_szConfigIniFile); + m_dualhead_vert = GetPrivateProfileIntW(L"settings",L"dualhead_vert",m_dualhead_vert,m_szConfigIniFile); + m_save_cpu = GetPrivateProfileIntW(L"settings",L"save_cpu",m_save_cpu,m_szConfigIniFile); + m_skin = GetPrivateProfileIntW(L"settings",L"skin",m_skin,m_szConfigIniFile); + m_fix_slow_text = GetPrivateProfileIntW(L"settings",L"fix_slow_text",m_fix_slow_text,m_szConfigIniFile); + m_vj_mode = GetPrivateProfileBoolW(L"settings",L"vj_mode",m_vj_mode,m_szConfigIniFile); + + //D3DDISPLAYMODE m_fs_disp_mode + m_disp_mode_fs.Width = GetPrivateProfileIntW(L"settings",L"disp_mode_fs_w", m_disp_mode_fs.Width ,m_szConfigIniFile); + m_disp_mode_fs.Height = GetPrivateProfileIntW(L"settings",L"disp_mode_fs_h",m_disp_mode_fs.Height ,m_szConfigIniFile); + m_disp_mode_fs.RefreshRate = GetPrivateProfileIntW(L"settings",L"disp_mode_fs_r",m_disp_mode_fs.RefreshRate,m_szConfigIniFile); + m_disp_mode_fs.Format = (D3DFORMAT)GetPrivateProfileIntW(L"settings",L"disp_mode_fs_f",m_disp_mode_fs.Format ,m_szConfigIniFile); + + // note: we don't call MyReadConfig() yet, because we + // want to completely finish CPluginShell's preinit (and ReadConfig) + // before calling CPlugin's preinit and ReadConfig. +} + +void CPluginShell::WRITE_FONT(int n){ + WritePrivateProfileStringW(L"settings",BuildSettingName(L"szFontFace",n),m_fontinfo[n].szFace,m_szConfigIniFile); + WritePrivateProfileIntW(m_fontinfo[n].bBold, BuildSettingName(L"bFontBold",n), m_szConfigIniFile, L"settings"); + WritePrivateProfileIntW(m_fontinfo[n].bItalic,BuildSettingName(L"bFontItalic",n), m_szConfigIniFile, L"settings"); + WritePrivateProfileIntW(m_fontinfo[n].nSize, BuildSettingName(L"nFontSize",n), m_szConfigIniFile, L"settings"); + WritePrivateProfileIntW(m_fontinfo[n].bAntiAliased, BuildSettingName(L"bFontAA",n),m_szConfigIniFile, L"settings"); +} + +void CPluginShell::WriteConfig() +{ + //D3DMULTISAMPLE_TYPE m_multisample_fullscreen; + //D3DMULTISAMPLE_TYPE m_multisample_desktop; + //D3DMULTISAMPLE_TYPE m_multisample_windowed; + WritePrivateProfileIntW((int)m_multisample_fullscreen,L"multisample_fullscreen",m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW((int)m_multisample_desktop ,L"multisample_desktop" ,m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW((int)m_multisample_windowed ,L"multisample_windowed" ,m_szConfigIniFile,L"settings"); + + //GUID m_adapter_guid_fullscreen + //GUID m_adapter_guid_desktop + //GUID m_adapter_guid_windowed + char str[256]; + GuidToText(&m_adapter_guid_fullscreen, str, sizeof(str)); + WritePrivateProfileString("settings","adapter_guid_fullscreen",str,m_szConfigIniFileA); + GuidToText(&m_adapter_guid_desktop, str, sizeof(str)); + WritePrivateProfileString("settings","adapter_guid_desktop",str,m_szConfigIniFileA); + GuidToText(&m_adapter_guid_windowed, str, sizeof(str)); + WritePrivateProfileString("settings","adapter_guid_windowed" ,str,m_szConfigIniFileA); + WritePrivateProfileString("settings","adapter_devicename_fullscreen",m_adapter_devicename_fullscreen,m_szConfigIniFileA); + WritePrivateProfileString("settings","adapter_devicename_desktop" ,m_adapter_devicename_desktop ,m_szConfigIniFileA); + WritePrivateProfileString("settings","adapter_devicename_windowed" ,m_adapter_devicename_windowed ,m_szConfigIniFileA); + + // FONTS + WRITE_FONT(0); + WRITE_FONT(1); + WRITE_FONT(2); + WRITE_FONT(3); +#if (NUM_EXTRA_FONTS >= 1) + WRITE_FONT(4); +#endif +#if (NUM_EXTRA_FONTS >= 2) + WRITE_FONT(5); +#endif +#if (NUM_EXTRA_FONTS >= 3) + WRITE_FONT(6); +#endif +#if (NUM_EXTRA_FONTS >= 4) + WRITE_FONT(7); +#endif +#if (NUM_EXTRA_FONTS >= 5) + WRITE_FONT(8); +#endif + + WritePrivateProfileIntW(m_start_fullscreen,L"start_fullscreen",m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW(m_start_desktop ,L"start_desktop" ,m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW(m_fake_fullscreen_mode,L"fake_fullscreen_mode",m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW(m_max_fps_fs,L"max_fps_fs",m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW(m_max_fps_dm,L"max_fps_dm",m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW(m_max_fps_w ,L"max_fps_w" ,m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW(m_show_press_f1_msg,L"show_press_f1_msg",m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW(m_allow_page_tearing_w,L"allow_page_tearing_w",m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW(m_allow_page_tearing_fs,L"allow_page_tearing_fs",m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW(m_allow_page_tearing_dm,L"allow_page_tearing_dm",m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW(m_minimize_winamp,L"minimize_winamp",m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW(m_desktop_show_icons,L"desktop_show_icons",m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW(m_desktop_textlabel_boxes,L"desktop_textlabel_boxes",m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW(m_desktop_manual_icon_scoot,L"desktop_manual_icon_scoot",m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW(m_desktop_555_fix,L"desktop_555_fix",m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW(m_dualhead_horz,L"dualhead_horz",m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW(m_dualhead_vert,L"dualhead_vert",m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW(m_save_cpu,L"save_cpu",m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW(m_skin,L"skin",m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW(m_fix_slow_text,L"fix_slow_text",m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW(m_vj_mode,L"vj_mode",m_szConfigIniFile,L"settings"); + + //D3DDISPLAYMODE m_fs_disp_mode + WritePrivateProfileIntW(m_disp_mode_fs.Width ,L"disp_mode_fs_w",m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW(m_disp_mode_fs.Height ,L"disp_mode_fs_h",m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW(m_disp_mode_fs.RefreshRate,L"disp_mode_fs_r",m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW(m_disp_mode_fs.Format ,L"disp_mode_fs_f",m_szConfigIniFile,L"settings"); + + WritePrivateProfileIntW(INT_VERSION ,L"version" ,m_szConfigIniFile,L"settings"); + WritePrivateProfileIntW(INT_SUBVERSION ,L"subversion" ,m_szConfigIniFile,L"settings"); + + // finally, save the plugin's unique settings: + MyWriteConfig(); +} + +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- + +int CPluginShell::PluginRender(unsigned char *pWaveL, unsigned char *pWaveR)//, unsigned char *pSpecL, unsigned char *pSpecR) +{ + // return FALSE here to tell Winamp to terminate the plugin + + if (!m_lpDX || !m_lpDX->m_ready) + { + // note: 'm_ready' will go false when a device reset fatally fails + // (for example, when user resizes window, or toggles fullscreen.) + m_exiting = 1; + return false; // EXIT THE PLUGIN + } + + if (m_hTextWnd) + m_lost_focus = ((GetFocus() != GetPluginWindow()) && (GetFocus() != m_hTextWnd)); + else + m_lost_focus = (GetFocus() != GetPluginWindow()); + + if ((m_screenmode==WINDOWED && m_hidden) || + (m_screenmode==FULLSCREEN && m_lost_focus) || + (m_screenmode==WINDOWED && m_resizing) + ) + { + Sleep(30); + return true; + } + + // test for lost device + // (this happens when device is fullscreen & user alt-tabs away, + // or when monitor power-saving kicks in) + HRESULT hr = m_lpDX->m_lpDevice->TestCooperativeLevel(); + if (hr == D3DERR_DEVICENOTRESET) + { + // device WAS lost, and is now ready to be reset (and come back online): + CleanUpDX9Stuff(0); + if (m_lpDX->m_lpDevice->Reset(&m_lpDX->m_d3dpp) != D3D_OK) + { + // note: a basic warning messagebox will have already been given. + // now suggest specific advice on how to regain more video memory: + if (m_lpDX->m_lastErr == DXC_ERR_CREATEDEV_PROBABLY_OUTOFVIDEOMEMORY) + SuggestHowToFreeSomeMem(); + return false; // EXIT THE PLUGIN + } + if (!AllocateDX9Stuff()) + return false; // EXIT THE PLUGIN + } + else if (hr != D3D_OK) + { + // device is lost, and not yet ready to come back; sleep. + Sleep(30); + return true; + } + + if (m_vjd3d9_device) + { + HRESULT hr = m_vjd3d9_device->TestCooperativeLevel(); + if (hr == D3DERR_DEVICENOTRESET) + { + RECT c; + GetClientRect(m_hTextWnd, &c); + + POINT p; + p.x = c.left; + p.y = c.top; + if (ClientToScreen(m_hTextWnd, &p)) + { + c.left += p.x; + c.right += p.x; + c.top += p.y; + c.bottom += p.y; + } + + CleanUpVJStuff(); + if (!InitVJStuff(&c)) + return false; // EXIT THE PLUGIN + } + } + + if (m_screenmode==DESKTOP) + { + PushWindowToJustBeforeDesktop(GetPluginWindow()); + } + + DoTime(); + AnalyzeNewSound(pWaveL, pWaveR); + AlignWaves(); + + DrawAndDisplay(0); + + EnforceMaxFPS(); + + m_frame++; + + return true; +} + +void CPluginShell::PushWindowToJustBeforeDesktop(HWND h) +{ + // if our window isn't already at the bottom of the Z order, + // freshly send it to HWND_BOTTOM. + + // this usually gives us the Program Manager window: + HWND hWndBottom = GetWindow(h, GW_HWNDLAST); + + // then, bottommost 'normal' window is usually the one just in front of it: + if (hWndBottom == m_hWndProgMan) + hWndBottom = GetWindow(hWndBottom, GW_HWNDPREV); + + if (hWndBottom != h) + { + m_force_accept_WM_WINDOWPOSCHANGING = 1; + SetWindowPos(h, HWND_BOTTOM, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE); + m_force_accept_WM_WINDOWPOSCHANGING = 0; + } + + /* + HWND hDesktopBkgWnd = FindWindow("SHELLDLL_DefView", ""); + if (hDesktopBkgWnd) + { + HWND hWndInFrontOfIcons = GetWindow(h, GW_HWNDPREV); + if (hWndInFrontOfIcons != h) + { + m_force_accept_WM_WINDOWPOSCHANGING = 1; + SetWindowPos(hDesktopBkgWnd, h, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE); + m_force_accept_WM_WINDOWPOSCHANGING = 0; + } + } + */ + + +} + +void CPluginShell::DrawAndDisplay(int redraw) +{ + int cx = m_vjd3d9_device ? m_nTextWndWidth : m_lpDX->m_client_width; + int cy = m_vjd3d9_device ? m_nTextWndHeight : m_lpDX->m_client_height; + if (m_lpDDSText) + { + D3DSURFACE_DESC desc; + if (D3D_OK == m_lpDDSText->GetLevelDesc(0, &desc)) + { + cx = min(cx, (int)desc.Width); + cy = min(cy, (int)desc.Height); + } + } + m_upper_left_corner_y = TEXT_MARGIN + GetCanvasMarginY(); + m_upper_right_corner_y = TEXT_MARGIN + GetCanvasMarginY(); + m_lower_left_corner_y = cy - TEXT_MARGIN - GetCanvasMarginY(); + m_lower_right_corner_y = cy - TEXT_MARGIN - GetCanvasMarginY(); + m_left_edge = TEXT_MARGIN + GetCanvasMarginX(); + m_right_edge = cx - TEXT_MARGIN - GetCanvasMarginX(); + + /*if (m_screenmode == DESKTOP || m_screenmode == FAKE_FULLSCREEN) + { + // check if taskbar is above plugin window; + // if so, scoot text & icons out of the way. + // [...should always be true for Desktop Mode, + // but it's like this for code simplicity.] + int taskbar_is_above_plugin_window = 1; + HWND h = FindWindow("Shell_TrayWnd", NULL); + while (h) //(..shouldn't be very many windows to iterate through here) + { + h = GetWindow(h, GW_HWNDPREV); + if (h == GetPluginWindow()) + { + taskbar_is_above_plugin_window = 0; + break; + } + } + + if (taskbar_is_above_plugin_window) + { + // respect the taskbar area; make sure the text, desktop icons, etc. + // don't appear underneath it. + //m_upper_left_corner_y += m_lpDX->m_monitor_work_rect.top - m_lpDX->m_monitor_rect.top; + //m_upper_right_corner_y += m_lpDX->m_monitor_work_rect.top - m_lpDX->m_monitor_rect.top; + //m_lower_left_corner_y -= m_lpDX->m_monitor_rect.bottom - m_lpDX->m_monitor_work_rect.bottom; + //m_lower_right_corner_y -= m_lpDX->m_monitor_rect.bottom - m_lpDX->m_monitor_work_rect.bottom; + //m_left_edge += m_lpDX->m_monitor_work_rect.left - m_lpDX->m_monitor_rect.left; + //m_right_edge -= m_lpDX->m_monitor_rect.right - m_lpDX->m_monitor_work_rect.right; + m_lpDX->UpdateMonitorWorkRect(); + m_upper_left_corner_y = max(m_upper_left_corner_y , m_lpDX->m_monitor_work_rect.top - m_lpDX->m_monitor_rect.top + TEXT_MARGIN + GetCanvasMarginY()); + m_upper_right_corner_y = max(m_upper_right_corner_y, m_lpDX->m_monitor_work_rect.top - m_lpDX->m_monitor_rect.top + TEXT_MARGIN + GetCanvasMarginY()); + m_lower_left_corner_y = min(m_lower_left_corner_y , m_lpDX->m_client_height - (m_lpDX->m_monitor_rect.bottom - m_lpDX->m_monitor_work_rect.bottom) - TEXT_MARGIN - GetCanvasMarginY()); + m_lower_right_corner_y = min(m_lower_right_corner_y, m_lpDX->m_client_height - (m_lpDX->m_monitor_rect.bottom - m_lpDX->m_monitor_work_rect.bottom) - TEXT_MARGIN - GetCanvasMarginY()); + m_left_edge = max(m_left_edge , m_lpDX->m_monitor_work_rect.left - m_lpDX->m_monitor_rect.left + TEXT_MARGIN + GetCanvasMarginX() ); + m_right_edge = min(m_right_edge, m_lpDX->m_client_width - (m_lpDX->m_monitor_rect.right - m_lpDX->m_monitor_work_rect.right) - TEXT_MARGIN + GetCanvasMarginX()); + } + }*/ + + if (D3D_OK==m_lpDX->m_lpDevice->BeginScene()) + { + MyRenderFn(redraw); + + PrepareFor2DDrawing_B(GetDevice(), GetWidth(), GetHeight()); + + RenderDesktop(); + if (!m_vjd3d9_device) // in VJ mode, this renders to different context, so do it after BeginScene() on 2nd device. + RenderBuiltInTextMsgs(); // to m_lpDDSText? + MyRenderUI(&m_upper_left_corner_y, &m_upper_right_corner_y, &m_lower_left_corner_y, &m_lower_right_corner_y, m_left_edge, m_right_edge); + RenderPlaylist(); + + if (!m_vjd3d9_device) + m_text.DrawNow(); + + m_lpDX->m_lpDevice->EndScene(); + } + + // VJ Mode: + if (m_vj_mode && m_vjd3d9_device && !m_hidden_textwnd && D3D_OK==m_vjd3d9_device->BeginScene()) + { + if (!m_lpDDSText || m_bClearVJWindow) + m_vjd3d9_device->Clear(0, 0, D3DCLEAR_TARGET, 0xFF000000, 1.0f, 0); + m_bClearVJWindow = false; + // note: when using debug DX runtime, textwnd will flash red/green after frame 4, if no text is drawn on a frame! + + RenderBuiltInTextMsgs(); + + PrepareFor2DDrawing_B(m_vjd3d9_device, m_nTextWndWidth, m_nTextWndHeight); + + m_text.DrawNow(); + + m_vjd3d9_device->EndScene(); + } + + if (m_screenmode == DESKTOP) + { + // window is hidden after creation, until 1st frame is ready to go; + // now that it's ready, we show it. + // see dxcontext::Internal_Init()'s call to SetWindowPos() for the DESKTOP case. + if (!IsWindowVisible(GetPluginWindow())) + ShowWindow(GetPluginWindow(), SW_SHOWNORMAL); + } + + if (m_screenmode == WINDOWED && (m_lpDX->m_client_width != m_lpDX->m_REAL_client_width || m_lpDX->m_client_height != m_lpDX->m_REAL_client_height)) + { + int real_w = m_lpDX->m_REAL_client_width; // real client size, in pixels + int real_h = m_lpDX->m_REAL_client_height; + int fat_w = m_lpDX->m_client_width; // oversized VS canvas size, in pixels + int fat_h = m_lpDX->m_client_height; + int extra_w = fat_w - real_w; + int extra_h = fat_h - real_h; + RECT src, dst; + SetRect(&src, extra_w/2, extra_h/2, extra_w/2 + real_w, extra_h/2 + real_h); + SetRect(&dst, 0, 0, real_w, real_h); + m_lpDX->m_lpDevice->Present(&src, &dst,NULL,NULL); + } + else + m_lpDX->m_lpDevice->Present(NULL,NULL,NULL,NULL); + + if (m_vjd3d9_device && !m_hidden_textwnd) + m_vjd3d9_device->Present(NULL,NULL,NULL,NULL); +} + +void CPluginShell::EnforceMaxFPS() +{ + int max_fps; + switch (m_screenmode) + { + case WINDOWED: max_fps = m_max_fps_w; break; + case FULLSCREEN: max_fps = m_max_fps_fs; break; + case FAKE_FULLSCREEN: max_fps = m_max_fps_fs; break; + case DESKTOP: max_fps = m_max_fps_dm; break; + } + + if (max_fps <= 0) + return; + + float fps_lo = (float)max_fps; + float fps_hi = (float)max_fps; + + if (m_save_cpu) + { + // Find the optimal lo/hi bounds for the fps + // that will result in a maximum difference, + // in the time for a single frame, of 0.003 seconds - + // the assumed granularity for Sleep(1) - + + // Using this range of acceptable fps + // will allow us to do (sloppy) fps limiting + // using only Sleep(1), and never the + // second half of it: Sleep(0) in a tight loop, + // which sucks up the CPU (whereas Sleep(1) + // leaves it idle). + + // The original equation: + // 1/(max_fps*t1) = 1/(max*fps/t1) - 0.003 + // where: + // t1 > 0 + // max_fps*t1 is the upper range for fps + // max_fps/t1 is the lower range for fps + + float a = 1; + float b = -0.003f * max_fps; + float c = -1.0f; + float det = b*b - 4*a*c; + if (det>0) + { + float t1 = (-b + sqrtf(det)) / (2*a); + //float t2 = (-b - sqrtf(det)) / (2*a); + + if (t1 > 1.0f) + { + fps_lo = max_fps / t1; + fps_hi = max_fps * t1; + // verify: now [1.0f/fps_lo - 1.0f/fps_hi] should equal 0.003 seconds. + // note: allowing tolerance to go beyond these values for + // fps_lo and fps_hi would gain nothing. + } + } + } + + if (m_high_perf_timer_freq.QuadPart > 0) + { + LARGE_INTEGER t; + QueryPerformanceCounter(&t); + + if (m_prev_end_of_frame.QuadPart != 0) + { + int ticks_to_wait_lo = (int)((float)m_high_perf_timer_freq.QuadPart / (float)fps_hi); + int ticks_to_wait_hi = (int)((float)m_high_perf_timer_freq.QuadPart / (float)fps_lo); + int done = 0; + int loops = 0; + do + { + QueryPerformanceCounter(&t); + + __int64 t2 = t.QuadPart - m_prev_end_of_frame.QuadPart; + if (t2 > 2147483000) + done = 1; + if (t.QuadPart < m_prev_end_of_frame.QuadPart) // time wrap + done = 1; + + // this is sloppy - if your freq. is high, this can overflow (to a (-) int) in just a few minutes + // but it's ok, we have protection for that above. + int ticks_passed = (int)(t.QuadPart - m_prev_end_of_frame.QuadPart); + if (ticks_passed >= ticks_to_wait_lo) + done = 1; + + if (!done) + { + // if > 0.01s left, do Sleep(1), which will actually sleep some + // steady amount of up to 3 ms (depending on the OS), + // and do so in a nice way (cpu meter drops; laptop battery spared). + // otherwise, do a few Sleep(0)'s, which just give up the timeslice, + // but don't really save cpu or battery, but do pass a tiny + // amount of time. + + //if (ticks_left > (int)m_high_perf_timer_freq.QuadPart/500) + if (ticks_to_wait_hi - ticks_passed > (int)m_high_perf_timer_freq.QuadPart/100) + Sleep(5); + else if (ticks_to_wait_hi - ticks_passed > (int)m_high_perf_timer_freq.QuadPart/1000) + Sleep(1); + else + for (int i=0; i<10; i++) + Sleep(0); // causes thread to give up its timeslice + } + } + while (!done); + } + + m_prev_end_of_frame = t; + } + else + { + Sleep(1000/max_fps); + } +} + +void CPluginShell::DoTime() +{ + if (m_frame==0) + { + m_fps = 30; + m_time = 0; + m_time_hist_pos = 0; + } + + double new_raw_time; + float elapsed; + + if (m_high_perf_timer_freq.QuadPart != 0) + { + // get high-precision time + // precision: usually from 1..6 us (MICROseconds), depending on the cpu speed. + // (higher cpu speeds tend to have better precision here) + LARGE_INTEGER t; + if (!QueryPerformanceCounter(&t)) + { + m_high_perf_timer_freq.QuadPart = 0; // something went wrong (exception thrown) -> revert to crappy timer + } + else + { + new_raw_time = (double)t.QuadPart; + elapsed = (float)((new_raw_time - m_last_raw_time)/(double)m_high_perf_timer_freq.QuadPart); + } + } + + if (m_high_perf_timer_freq.QuadPart == 0) + { + // get low-precision time + // precision: usually 1 ms (MILLIsecond) for win98, and 10 ms for win2k. + new_raw_time = (double)(timeGetTime()*0.001); + elapsed = (float)(new_raw_time - m_last_raw_time); + } + + m_last_raw_time = new_raw_time; + int slots_to_look_back = (m_high_perf_timer_freq.QuadPart==0) ? TIME_HIST_SLOTS : TIME_HIST_SLOTS/2; + + m_time += 1.0f/m_fps; + + // timekeeping goals: + // 1. keep 'm_time' increasing SMOOTHLY: (smooth animation depends on it) + // m_time += 1.0f/m_fps; // where m_fps is a bit damped + // 2. keep m_time_hist[] 100% accurate (except for filtering out pauses), + // so that when we look take the difference between two entries, + // we get the real amount of time that passed between those 2 frames. + // m_time_hist[i] = m_last_raw_time + elapsed_corrected; + + if (m_frame > TIME_HIST_SLOTS) + { + if (m_fps < 60.0f) + slots_to_look_back = (int)(slots_to_look_back*(0.1f + 0.9f*(m_fps/60.0f))); + + if (elapsed > 5.0f/m_fps || elapsed > 1.0f || elapsed < 0) + elapsed = 1.0f / 30.0f; + + float old_hist_time = m_time_hist[(m_time_hist_pos - slots_to_look_back + TIME_HIST_SLOTS) % TIME_HIST_SLOTS]; + float new_hist_time = m_time_hist[(m_time_hist_pos - 1 + TIME_HIST_SLOTS) % TIME_HIST_SLOTS] + + elapsed; + + m_time_hist[m_time_hist_pos] = new_hist_time; + m_time_hist_pos = (m_time_hist_pos+1) % TIME_HIST_SLOTS; + + float new_fps = slots_to_look_back / (float)(new_hist_time - old_hist_time); + float damping = (m_high_perf_timer_freq.QuadPart==0) ? 0.93f : 0.87f; + + // damp heavily, so that crappy timer precision doesn't make animation jerky + if (fabsf(m_fps - new_fps) > 3.0f) + m_fps = new_fps; + else + m_fps = damping*m_fps + (1-damping)*new_fps; + } + else + { + float damping = (m_high_perf_timer_freq.QuadPart==0) ? 0.8f : 0.6f; + + if (m_frame < 2) + elapsed = 1.0f / 30.0f; + else if (elapsed > 1.0f || elapsed < 0) + elapsed = 1.0f / m_fps; + + float old_hist_time = m_time_hist[0]; + float new_hist_time = m_time_hist[(m_time_hist_pos - 1 + TIME_HIST_SLOTS) % TIME_HIST_SLOTS] + + elapsed; + + m_time_hist[m_time_hist_pos] = new_hist_time; + m_time_hist_pos = (m_time_hist_pos+1) % TIME_HIST_SLOTS; + + if (m_frame > 0) + { + float new_fps = (m_frame) / (new_hist_time - old_hist_time); + m_fps = damping*m_fps + (1-damping)*new_fps; + } + } + + // Synchronize the audio and video by telling Winamp how many milliseconds we want the audio data, + // before it's actually audible. If we set this to the amount of time it takes to display 1 frame + // (1/fps), the video and audio should be perfectly synchronized. + if (m_fps < 2.0f) + mod1.latencyMs = 500; + else if (m_fps > 125.0f) + mod1.latencyMs = 8; + else + mod1.latencyMs = (int)(1000.0f/m_fps*m_lpDX->m_frame_delay + 0.5f); +} + +void CPluginShell::AnalyzeNewSound(unsigned char *pWaveL, unsigned char *pWaveR) +{ + // we get 576 samples in from winamp. + // the output of the fft has 'num_frequencies' samples, + // and represents the frequency range 0 hz - 22,050 hz. + // usually, plugins only use half of this output (the range 0 hz - 11,025 hz), + // since >10 khz doesn't usually contribute much. + + int i; + + float temp_wave[2][576]; + + int old_i = 0; + for (i=0; i<576; i++) + { + m_sound.fWaveform[0][i] = (float)((pWaveL[i] ^ 128) - 128); + m_sound.fWaveform[1][i] = (float)((pWaveR[i] ^ 128) - 128); + + // simulating single frequencies from 200 to 11,025 Hz: + //float freq = 1.0f + 11050*(GetFrame() % 100)*0.01f; + //m_sound.fWaveform[0][i] = 10*sinf(i*freq*6.28f/44100.0f); + + // damp the input into the FFT a bit, to reduce high-frequency noise: + temp_wave[0][i] = 0.5f*(m_sound.fWaveform[0][i] + m_sound.fWaveform[0][old_i]); + temp_wave[1][i] = 0.5f*(m_sound.fWaveform[1][i] + m_sound.fWaveform[1][old_i]); + old_i = i; + } + + m_fftobj.time_to_frequency_domain(temp_wave[0], m_sound.fSpectrum[0]); + m_fftobj.time_to_frequency_domain(temp_wave[1], m_sound.fSpectrum[1]); + + // sum (left channel) spectrum up into 3 bands + // [note: the new ranges do it so that the 3 bands are equally spaced, pitch-wise] + float min_freq = 200.0f; + float max_freq = 11025.0f; + float net_octaves = (logf(max_freq/min_freq) / logf(2.0f)); // 5.7846348455575205777914165223593 + float octaves_per_band = net_octaves / 3.0f; // 1.9282116151858401925971388407864 + float mult = powf(2.0f, octaves_per_band); // each band's highest freq. divided by its lowest freq.; 3.805831305510122517035102576162 + // [to verify: min_freq * mult * mult * mult should equal max_freq.] + for (int ch=0; ch<2; ch++) + { + for (i=0; i<3; i++) + { + // old guesswork code for this: + // float exp = 2.1f; + // int start = (int)(NUM_FREQUENCIES*0.5f*powf(i/3.0f, exp)); + // int end = (int)(NUM_FREQUENCIES*0.5f*powf((i+1)/3.0f, exp)); + // results: + // old range: new range (ideal): + // bass: 0-1097 200-761 + // mids: 1097-4705 761-2897 + // treb: 4705-11025 2897-11025 + int start = (int)(NUM_FREQUENCIES * min_freq*powf(mult, (float)i)/11025.0f); + int end = (int)(NUM_FREQUENCIES * min_freq*powf(mult, (float)(i+1))/11025.0f); + if (start < 0) start = 0; + if (end > NUM_FREQUENCIES) end = NUM_FREQUENCIES; + + m_sound.imm[ch][i] = 0; + for (int j=start; j<end; j++) + m_sound.imm[ch][i] += m_sound.fSpectrum[ch][j]; + m_sound.imm[ch][i] /= (float)(end-start); + } + } + + // some code to find empirical long-term averages for imm[0..2]: + /*{ + static float sum[3]; + static int count = 0; + + #define FRAMES_PER_SONG 300 // should be at least 200! + + if (m_frame < FRAMES_PER_SONG) + { + sum[0] = sum[1] = sum[2] = 0; + count = 0; + } + else + { + if (m_frame%FRAMES_PER_SONG == 0) + { + char buf[256]; + sprintf(buf, "%.4f, %.4f, %.4f (%d samples / ~%d songs)\n", + sum[0]/(float)(count), + sum[1]/(float)(count), + sum[2]/(float)(count), + count, + count/(FRAMES_PER_SONG-10) + ); + OutputDebugString(buf); + + // skip to next song + PostMessage(m_hWndWinamp,WM_COMMAND,40048,0); + } + else if (m_frame%FRAMES_PER_SONG == 5) + { + // then advance to 0-2 minutes into the song: + PostMessage(m_hWndWinamp,WM_USER,(20 + (warand()%65) + (rand()%65))*1000,106); + } + else if (m_frame%FRAMES_PER_SONG >= 10) + { + sum[0] += m_sound.imm[0]; + sum[1] += m_sound.imm[1]; + sum[2] += m_sound.imm[2]; + count++; + } + } + }*/ + + // multiply by long-term, empirically-determined inverse averages: + // (for a trial of 244 songs, 10 seconds each, somewhere in the 2nd or 3rd minute, + // the average levels were: 0.326781557 0.38087377 0.199888934 + for (int ch=0; ch<2; ch++) + { + m_sound.imm[ch][0] /= 0.326781557f;//0.270f; + m_sound.imm[ch][1] /= 0.380873770f;//0.343f; + m_sound.imm[ch][2] /= 0.199888934f;//0.295f; + } + + // do temporal blending to create attenuated and super-attenuated versions + for (int ch=0; ch<2; ch++) + { + for (i=0; i<3; i++) + { + // m_sound.avg[i] + { + float avg_mix; + if (m_sound.imm[ch][i] > m_sound.avg[ch][i]) + avg_mix = AdjustRateToFPS(0.2f, 14.0f, m_fps); + else + avg_mix = AdjustRateToFPS(0.5f, 14.0f, m_fps); + m_sound.avg[ch][i] = m_sound.avg[ch][i]*avg_mix + m_sound.imm[ch][i]*(1-avg_mix); + } + + // m_sound.med_avg[i] + // m_sound.long_avg[i] + { + float med_mix = 0.91f;//0.800f + 0.11f*powf(t, 0.4f); // primarily used for velocity_damping + float long_mix = 0.96f;//0.800f + 0.16f*powf(t, 0.2f); // primarily used for smoke plumes + med_mix = AdjustRateToFPS(med_mix, 14.0f, m_fps); + long_mix = AdjustRateToFPS(long_mix, 14.0f, m_fps); + m_sound.med_avg[ch][i] = m_sound.med_avg[ch][i]*(med_mix) + m_sound.imm[ch][i]*(1-med_mix); + m_sound.long_avg[ch][i] = m_sound.long_avg[ch][i]*(long_mix) + m_sound.imm[ch][i]*(1-long_mix); + } + } + } +} + +void CPluginShell::PrepareFor2DDrawing_B(IDirect3DDevice9 *pDevice, int w, int h) +{ + // New 2D drawing area will have x,y coords in the range <-1,-1> .. <1,1> + // +--------+ Y=-1 + // | | + // | screen | Z=0: front of scene + // | | Z=1: back of scene + // +--------+ Y=1 + // X=-1 X=1 + // NOTE: After calling this, be sure to then call (at least): + // 1. SetVertexShader() + // 2. SetTexture(), if you need it + // before rendering primitives! + // Also, be sure your sprites have a z coordinate of 0. + + pDevice->SetRenderState(D3DRS_ZENABLE, TRUE); + pDevice->SetRenderState(D3DRS_ZWRITEENABLE, TRUE); + pDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL); + pDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD); + pDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); + pDevice->SetRenderState(D3DRS_FOGENABLE, FALSE); + pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + pDevice->SetRenderState(D3DRS_CLIPPING, TRUE); + pDevice->SetRenderState(D3DRS_LIGHTING, FALSE); + pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + pDevice->SetRenderState(D3DRS_LOCALVIEWER, FALSE); + pDevice->SetRenderState(D3DRS_COLORVERTEX, TRUE); + + pDevice->SetTexture(0, NULL); + pDevice->SetTexture(1, NULL); + pDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);//D3DTEXF_LINEAR); + pDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_POINT);//D3DTEXF_LINEAR); + pDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE); + pDevice->SetTextureStageState(1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE); + pDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + pDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + pDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT); + pDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + + pDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + pDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE); + pDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + + pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + + // set up for 2D drawing: + { + D3DXMATRIX Ortho2D; + D3DXMATRIX Identity; + + pMatrixOrthoLH(&Ortho2D, (float)w, (float)h, 0.0f, 1.0f); + D3DXMatrixIdentity(&Identity); + + pDevice->SetTransform(D3DTS_PROJECTION, &Ortho2D); + pDevice->SetTransform(D3DTS_WORLD, &Identity); + pDevice->SetTransform(D3DTS_VIEW, &Identity); + } +} + +void CPluginShell::DrawDarkTranslucentBox(RECT* pr) +{ + // 'pr' is the rectangle that some text will occupy; + // a black box will be drawn around it, plus a bit of extra margin space. + + if (m_vjd3d9_device) + return; + + m_lpDX->m_lpDevice->SetVertexShader(NULL); + m_lpDX->m_lpDevice->SetPixelShader(NULL); + m_lpDX->m_lpDevice->SetFVF(SIMPLE_VERTEX_FORMAT); + m_lpDX->m_lpDevice->SetTexture(0, NULL); + + m_lpDX->m_lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + m_lpDX->m_lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + m_lpDX->m_lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + + m_lpDX->m_lpDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); + m_lpDX->m_lpDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE); + m_lpDX->m_lpDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + m_lpDX->m_lpDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + m_lpDX->m_lpDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE); + + // set up a quad + SIMPLEVERTEX verts[4]; + for (int i=0; i<4; i++) + { + verts[i].x = (i%2==0) ? (float)(-m_lpDX->m_client_width /2 + pr->left) : + (float)(-m_lpDX->m_client_width /2 + pr->right); + verts[i].y = (i/2==0) ? (float)-(-m_lpDX->m_client_height/2 + pr->bottom) : + (float)-(-m_lpDX->m_client_height/2 + pr->top); + verts[i].z = 0; + verts[i].Diffuse = (m_screenmode==DESKTOP) ? 0xE0000000 : 0xD0000000; + } + + m_lpDX->m_lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, verts, sizeof(SIMPLEVERTEX)); + + // undo unusual state changes: + m_lpDX->m_lpDevice->SetRenderState(D3DRS_ZENABLE, TRUE); + m_lpDX->m_lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); +} + +void CPluginShell::RenderBuiltInTextMsgs() +{ + int _show_press_f1_NOW = (m_show_press_f1_msg && m_time < PRESS_F1_DUR); + + { + RECT r; + + if (m_show_help) + { + int y = m_upper_left_corner_y; + + SetRect(&r, 0, 0, GetWidth(), GetHeight()); + if(!g_szHelp_W) + m_d3dx_font[HELPSCREEN_FONT]->DrawTextA(NULL, (char*)g_szHelp, -1, &r, DT_CALCRECT, 0xFFFFFFFF); + else + m_d3dx_font[HELPSCREEN_FONT]->DrawTextW(NULL, g_szHelp, -1, &r, DT_CALCRECT, 0xFFFFFFFF); + + r.top += m_upper_left_corner_y; + r.left += m_left_edge; + r.right += m_left_edge + PLAYLIST_INNER_MARGIN*2; + r.bottom += m_upper_left_corner_y + PLAYLIST_INNER_MARGIN*2; + DrawDarkTranslucentBox(&r); + + r.top += PLAYLIST_INNER_MARGIN; + r.left += PLAYLIST_INNER_MARGIN; + r.right -= PLAYLIST_INNER_MARGIN; + r.bottom -= PLAYLIST_INNER_MARGIN; + if(!g_szHelp_W) + m_d3dx_font[HELPSCREEN_FONT]->DrawTextA(NULL, (char*)g_szHelp, -1, &r, 0, 0xFFFFFFFF); + else + m_d3dx_font[HELPSCREEN_FONT]->DrawTextW(NULL, g_szHelp, -1, &r, 0, 0xFFFFFFFF); + + m_upper_left_corner_y += r.bottom-r.top + PLAYLIST_INNER_MARGIN*3; + } + + // render 'Press F1 for Help' message in lower-right corner: + if (_show_press_f1_NOW) + { + int dx = (int)(160.0f * powf(m_time/(float)(PRESS_F1_DUR), (float)(PRESS_F1_EXP))); + SetRect(&r, m_left_edge, m_lower_right_corner_y - GetFontHeight(DECORATIVE_FONT), m_right_edge + dx, m_lower_right_corner_y); + m_lower_right_corner_y -= m_d3dx_font[DECORATIVE_FONT]->DrawTextW(NULL, WASABI_API_LNGSTRINGW(IDS_PRESS_F1_MSG), -1, &r, DT_RIGHT, 0xFFFFFFFF); + } + } +} + +void CPluginShell::RenderPlaylist() +{ + // draw playlist: + if (m_show_playlist) + { + RECT r; + int nSongs = SendMessage(m_hWndWinamp,WM_USER, 0, 124); + int now_playing = SendMessage(m_hWndWinamp,WM_USER, 0, 125); + + if (nSongs <= 0) + { + m_show_playlist = 0; + } + else + { + int playlist_vert_pixels = m_lower_left_corner_y - m_upper_left_corner_y; + int disp_lines = min(MAX_SONGS_PER_PAGE, (playlist_vert_pixels - PLAYLIST_INNER_MARGIN*2) / GetFontHeight(PLAYLIST_FONT)); + int total_pages = (nSongs) / disp_lines; + + if (disp_lines<=0) + return; + + // apply PgUp/PgDn keypresses since last time + m_playlist_pos -= m_playlist_pageups * disp_lines; + m_playlist_pageups = 0; + + if (m_playlist_pos < 0) + m_playlist_pos = 0; + if (m_playlist_pos >= nSongs) + m_playlist_pos = nSongs-1; + + // NOTE: 'dwFlags' is used for both DDRAW and DX9 + DWORD dwFlags = DT_SINGLELINE;// | DT_NOPREFIX | DT_WORD_ELLIPSIS; + DWORD color; + + int cur_page = (m_playlist_pos) / disp_lines; + int cur_line = (m_playlist_pos + disp_lines - 1) % disp_lines; + int new_top_idx = cur_page * disp_lines; + int new_btm_idx = new_top_idx + disp_lines; + wchar_t buf[1024] = {0}; + + // ask winamp for the song names, but DO IT BEFORE getting the DC, + // otherwise vaio will crash (~DDRAW port). + if (m_playlist_top_idx != new_top_idx || + m_playlist_btm_idx != new_btm_idx) + { + for (int i=0; i<disp_lines; i++) + { + int j = new_top_idx + i; + if (j < nSongs) + { + // clip max len. of song name to 240 chars, to prevent overflows + lstrcpynW(buf, (wchar_t*)SendMessage(m_hWndWinamp, WM_USER, j, IPC_GETPLAYLISTTITLEW), 240); + wsprintfW(m_playlist[i], L"%d. %s ", j+1, buf); // leave an extra space @ end, so italicized fonts don't get clipped + } + } + } + + // update playlist cache, if necessary: + if (m_playlist_top_idx != new_top_idx || + m_playlist_btm_idx != new_btm_idx) + { + m_playlist_top_idx = new_top_idx; + m_playlist_btm_idx = new_btm_idx; + m_playlist_width_pixels = 0; + + int max_w = min(m_right_edge - m_left_edge, m_lpDX->m_client_width - TEXT_MARGIN*2 - PLAYLIST_INNER_MARGIN*2); + + for (int i=0; i<disp_lines; i++) + { + int j = new_top_idx + i; + if (j < nSongs) + { + // clip max len. of song name to 240 chars, to prevent overflows + //strcpy(buf, (char*)SendMessage(m_hWndWinamp, WM_USER, j, 212)); + //buf[240] = 0; + //sprintf(m_playlist[i], "%d. %s ", j+1, buf); // leave an extra space @ end, so italicized fonts don't get clipped + + SetRect(&r, 0, 0, max_w, 1024); + m_d3dx_font[PLAYLIST_FONT]->DrawTextW(NULL, m_playlist[i], -1, &r, dwFlags | DT_CALCRECT, 0xFFFFFFFF); + int w = r.right-r.left; + if (w>0) + m_playlist_width_pixels = max(m_playlist_width_pixels, w); + } + else + { + m_playlist[i][0] = 0; + } + } + + if (m_playlist_width_pixels == 0 || + m_playlist_width_pixels > max_w) + m_playlist_width_pixels = max_w; + } + + int start = max(0, (cur_page)*disp_lines); + int end = min(nSongs, (cur_page+1)*disp_lines); + + // draw dark box around where the playlist will go: + + RECT r; + r.top = m_upper_left_corner_y; + r.left = m_left_edge; + r.right = m_left_edge + m_playlist_width_pixels + PLAYLIST_INNER_MARGIN*2; + r.bottom = m_upper_left_corner_y + (end-start)*GetFontHeight(PLAYLIST_FONT) + PLAYLIST_INNER_MARGIN*2; + DrawDarkTranslucentBox(&r); + + //m_d3dx_font[PLAYLIST_FONT]->Begin(); + + // draw playlist text + int y = m_upper_left_corner_y + PLAYLIST_INNER_MARGIN; + for (int i=start; i<end; i++) + { + SetRect(&r, m_left_edge + PLAYLIST_INNER_MARGIN, y, m_left_edge + PLAYLIST_INNER_MARGIN + m_playlist_width_pixels, y + GetFontHeight(PLAYLIST_FONT)); + + if (m_lpDX->GetBitDepth() == 8) + color = (i==m_playlist_pos) ? + (i==now_playing ? 0xFFFFFFFF : 0xFFFFFFFF) : + (i==now_playing ? 0xFFFFFFFF : 0xFF707070); + else + color = (i==m_playlist_pos) ? + (i==now_playing ? PLAYLIST_COLOR_BOTH : PLAYLIST_COLOR_HILITE_TRACK) : + (i==now_playing ? PLAYLIST_COLOR_PLAYING_TRACK : PLAYLIST_COLOR_NORMAL); + + y += m_d3dx_font[PLAYLIST_FONT]->DrawTextW(NULL, m_playlist[i-start], -1, &r, dwFlags, color); + } + + //m_d3dx_font[PLAYLIST_FONT]->End(); + } + } +} + +void CPluginShell::SuggestHowToFreeSomeMem() +{ + // This function is called when the plugin runs out of video memory; + // it lets you show a messagebox to the user so you can (intelligently) + // suggest how to free up some video memory, based on what settings + // they've chosen. + + wchar_t str[1024]; + + if (m_lpDX->m_current_mode.multisamp != D3DMULTISAMPLE_NONE) + { + if (m_lpDX->m_current_mode.screenmode == WINDOWED) + WASABI_API_LNGSTRINGW_BUF(IDS_TO_FREE_UP_SOME_MEMORY_RESTART_WINAMP_THEN_GO_TO_CONFIG, str, 2048); + else if (m_lpDX->m_current_mode.screenmode == FAKE_FULLSCREEN) + WASABI_API_LNGSTRINGW_BUF(IDS_TO_FREE_UP_SOME_MEMORY_RESTART_WINAMP_THEN_GO_TO_CONFIG_2, str, 2048); + else + WASABI_API_LNGSTRINGW_BUF(IDS_TO_FREE_UP_SOME_MEMORY_RESTART_WINAMP_THEN_GO_TO_CONFIG_3, str, 2048); + } + else + if (m_lpDX->m_current_mode.screenmode == FULLSCREEN) // true fullscreen + WASABI_API_LNGSTRINGW_BUF(IDS_TO_FREE_UP_VIDEO_MEMORY, str, 2048); + else // windowed, desktop mode, or fake fullscreen + WASABI_API_LNGSTRINGW_BUF(IDS_TO_FREE_UP_VIDEO_MEMORY, str, 2048); + + MessageBoxW(m_lpDX->GetHwnd(), str, WASABI_API_LNGSTRINGW(IDS_MILKDROP_SUGGESTION), MB_OK|MB_SETFOREGROUND|MB_TOPMOST); +} + +LRESULT CALLBACK CPluginShell::WindowProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam) +{ + //if (uMsg==WM_GETDLGCODE) + // return DLGC_WANTALLKEYS|DLGC_WANTCHARS|DLGC_WANTMESSAGE; // this tells the embedwnd that we want keypresses to flow through to our client wnd. + + if (uMsg == WM_CREATE) + { + CREATESTRUCT *create = (CREATESTRUCT *)lParam; + SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)create->lpCreateParams); + } + + CPluginShell* p = (CPluginShell*)GetWindowLongPtr(hWnd,GWLP_USERDATA); + if (p) + return p->PluginShellWindowProc(hWnd, uMsg, wParam, lParam); + else + return DefWindowProcW(hWnd, uMsg, wParam, lParam); +} + +LRESULT CPluginShell::PluginShellWindowProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam) +{ + USHORT mask = 1 << (sizeof(SHORT)*8 - 1); + //bool bShiftHeldDown = (GetKeyState(VK_SHIFT) & mask) != 0; + bool bCtrlHeldDown = (GetKeyState(VK_CONTROL) & mask) != 0; + //bool bAltHeldDown: most keys come in under WM_SYSKEYDOWN when ALT is depressed. + + int i; +#ifdef _DEBUG + char caption[256] = "WndProc: frame 0, "; + if (m_frame > 0) + { + float time = m_time; + int hours = (int)(time/3600); + time -= hours*3600; + int minutes = (int)(time/60); + time -= minutes*60; + int seconds = (int)time; + time -= seconds; + int dsec = (int)(time*100); + sprintf(caption, "WndProc: frame %d, t=%dh:%02dm:%02d.%02ds, ", m_frame, hours, minutes, seconds, dsec); + } + + if (uMsg != WM_MOUSEMOVE && + uMsg != WM_NCHITTEST && + uMsg != WM_SETCURSOR && + uMsg != WM_COPYDATA && + uMsg != WM_USER) + OutputDebugMessage(caption, hWnd, uMsg, wParam, lParam); +#endif + + switch (uMsg) + { + case WM_USER: + if (m_screenmode == DESKTOP) + { + // this function resides in vms_desktop.dll; + // its response will come later, via the WM_COPYDATA + // message (See below). + //KIV: **THIS CALL CRASHES EXPLORER IN VISTA** + getItemData(wParam); + return 0; + } + break; + + case WM_COPYDATA: + if (m_screenmode == DESKTOP) + { + // this message is vms_desktop.dll's response to + // our call to getItemData(). + PCOPYDATASTRUCT c = (PCOPYDATASTRUCT)lParam; + if (c && (c->cbData % sizeof(icon_t) == 0)) + { + icon_t *pNewIcons = (icon_t*)c->lpData; + + EnterCriticalSection(&m_desktop_cs); + + if (m_desktop_icon_state == 1 && (c->dwData & 0x80000000)) // if doing a total refresh... + { + // ...we build the list from zero + int len = c->dwData & 0xFFFF; + for (int i=0; i<len; i++) + m_icon_list.push_back(pNewIcons[i]); + } + else if (m_desktop_icon_state == 3 && !(c->dwData & 0x80000000)) + { + // otherwise, we alter existing things in the list: + IconList::iterator p; + int start = c->dwData & 0xFFFF; + int len = c->dwData >> 16; + + int i = 0; + for (p = m_icon_list.begin(); p != m_icon_list.end() && i<start; p++) + i++; + for (; p != m_icon_list.end() && i<start+len; p++) + { + p->x = pNewIcons[i-start].x; + p->y = pNewIcons[i-start].y; + memcpy(p->name, pNewIcons[i-start].name, sizeof(p->name)); + memcpy(p->pidl, pNewIcons[i-start].pidl, sizeof(p->pidl)); + i++; + } + + m_desktop_icon_state = 2; + m_desktop_icon_update_frame = GetFrame(); + } + + LeaveCriticalSection(&m_desktop_cs); + } + + return 0; + } + break; + + case WM_ERASEBKGND: + // Repaint window when song is paused and image needs to be repainted: + if (SendMessage(m_hWndWinamp,WM_USER,0,104)!=1 && m_lpDX && m_lpDX->m_lpDevice && GetFrame() > 0) // WM_USER/104 return codes: 1=playing, 3=paused, other=stopped + { + m_lpDX->m_lpDevice->Present(NULL,NULL,NULL,NULL); + return 0; + } + break; + + case WM_WINDOWPOSCHANGING: + if ( + m_screenmode == DESKTOP + && (!m_force_accept_WM_WINDOWPOSCHANGING) + && m_lpDX && m_lpDX->m_ready + ) + { + // unless we requested it ourselves or it's init time, + // prevent the fake desktop window from moving around + // in the Z order! (i.e., keep it on the bottom) + + // without this code, when you click on the 'real' desktop + // in a multimon setup, any windows that are overtop of the + // 'fake' desktop will flash, since they'll be covered + // up by the fake desktop window (but then shown again on + // the next frame, when we detect that the fake desktop + // window isn't on bottom & send it back to the bottom). + + LPWINDOWPOS pwp = (LPWINDOWPOS)lParam; + if (pwp) + pwp->flags |= SWP_NOOWNERZORDER | SWP_NOZORDER; + } + if (m_screenmode==WINDOWED && m_lpDX && m_lpDX->m_ready && m_lpDX->m_current_mode.m_skin) + m_lpDX->SaveWindow(); + break; + case WM_NCACTIVATE: + // *Very Important Handler!* + // -Without this code, the app would not work properly when running in true + // fullscreen mode on multiple monitors; it would auto-minimize whenever the + // user clicked on a window in another display. + if (wParam == 0 && + m_screenmode == FULLSCREEN && + m_frame > 0 && + !m_exiting && + m_lpDX && + m_lpDX->m_ready + && m_lpDX->m_lpD3D && + m_lpDX->m_lpD3D->GetAdapterCount() > 1 + ) + { + return 0; + } + break; + + case WM_DESTROY: + // note: don't post quit message here if the window is being destroyed + // and re-created on a switch between windowed & FAKE fullscreen modes. + if (!m_lpDX->TempIgnoreDestroyMessages()) + { + // this is a final exit, and not just destroy-then-recreate-the-window. + // so, flag DXContext so it knows that someone else + // will take care of destroying the window! + m_lpDX->OnTrulyExiting(); + PostQuitMessage(0); + } + return FALSE; + break; + // benski> a little hack to get the window size correct. it seems to work + case WM_USER+555: + if (m_lpDX && m_lpDX->m_ready && m_screenmode==WINDOWED && !m_resizing) + { + OnUserResizeWindow(); + m_lpDX->SaveWindow(); + } + break; + case WM_MOVE: + m_lpDX->SaveWindow(); + break; + case WM_SIZE: + // clear or set activity flag to reflect focus + if (m_lpDX && m_lpDX->m_ready && m_screenmode==WINDOWED && !m_resizing) + { + m_hidden = (SIZE_MAXHIDE==wParam || SIZE_MINIMIZED==wParam) ? TRUE : FALSE; + + if (SIZE_MAXIMIZED==wParam || SIZE_RESTORED==wParam) // the window has been maximized or restored + OnUserResizeWindow(); + } + break; + + case WM_ENTERSIZEMOVE: + m_resizing = 1; + break; + + case WM_EXITSIZEMOVE: + if (m_lpDX && m_lpDX->m_ready && m_screenmode==WINDOWED) + OnUserResizeWindow(); + m_lpDX->SaveWindow(); + m_resizing = 0; + break; + + case WM_GETMINMAXINFO: + { + // don't let the window get too small + MINMAXINFO* p = (MINMAXINFO*)lParam; + if (p->ptMinTrackSize.x < 64) + p->ptMinTrackSize.x = 64; + p->ptMinTrackSize.y = p->ptMinTrackSize.x*3/4; + } + return 0; + + case WM_MOUSEMOVE: + if (m_screenmode==DESKTOP && (m_desktop_dragging==1 || m_desktop_box==1)) + { + m_desktop_drag_curpos.x = LOWORD(lParam); + m_desktop_drag_curpos.y = HIWORD(lParam); + if (m_desktop_box==1) + { + // update selection based on box coords + RECT box, temp; + box.left = min(m_desktop_drag_curpos.x, m_desktop_drag_startpos.x); + box.right = max(m_desktop_drag_curpos.x, m_desktop_drag_startpos.x); + box.top = min(m_desktop_drag_curpos.y, m_desktop_drag_startpos.y); + box.bottom = max(m_desktop_drag_curpos.y, m_desktop_drag_startpos.y); + + IconList::iterator p; + for (p = m_icon_list.begin(); p != m_icon_list.end(); p++) + { + p->selected = 0; + + if (IntersectRect(&temp, &box, &p->label_rect)) + p->selected = 1; + else if (IntersectRect(&temp, &box, &p->icon_rect)) + p->selected = 1; + } + } + + // repaint window manually, if winamp is paused + if (SendMessage(m_hWndWinamp,WM_USER,0,104) != 1) + { + PushWindowToJustBeforeDesktop(GetPluginWindow()); + DrawAndDisplay(1); + } + + //return 0; + } + m_lpDX->SaveWindow(); + break; + + case WM_LBUTTONUP: + if (m_screenmode==DESKTOP) + { + if (m_desktop_dragging) + { + m_desktop_dragging = 0; + + // move selected item(s) to new cursor position + int dx = LOWORD(lParam) - m_desktop_drag_startpos.x; + int dy = HIWORD(lParam) - m_desktop_drag_startpos.y; + + if (dx!=0 || dy!=0) + { + int idx=0; + IconList::iterator p; + for (p = m_icon_list.begin(); p != m_icon_list.end(); p++) + { + if (p->selected) + { + SendMessage(m_hWndDesktopListView, LVM_SETITEMPOSITION, idx, MAKELPARAM(p->x + dx, p->y + dy)); + p->x += dx; + p->y += dy; + } + idx++; + } + } + + // repaint window manually, if winamp is paused + if (SendMessage(m_hWndWinamp,WM_USER,0,104) != 1) + { + PushWindowToJustBeforeDesktop(GetPluginWindow()); + DrawAndDisplay(1); + } + } + + if (m_desktop_box) + { + m_desktop_box = 0; + + // repaint window manually, if winamp is paused + if (SendMessage(m_hWndWinamp,WM_USER,0,104) != 1) + { + PushWindowToJustBeforeDesktop(GetPluginWindow()); + DrawAndDisplay(1); + } + } + + //return 0; + } + break; + + case WM_USER + 1666: + if (wParam == 1 && lParam == 15) + { + if (m_screenmode == FULLSCREEN || m_screenmode == FAKE_FULLSCREEN) + ToggleFullScreen(); + } + return 0; + case WM_LBUTTONDOWN: + case WM_LBUTTONDBLCLK: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + // Toggle between Fullscreen and Windowed modes on double-click + // note: this requires the 'CS_DBLCLKS' windowclass style! + if (m_screenmode != DESKTOP) + { + SetFocus(hWnd); + if (uMsg==WM_LBUTTONDBLCLK && m_frame>0) + { + ToggleFullScreen(); + return 0; + } + } + else + { + POINT pt; + pt.x = LOWORD(lParam); + pt.y = HIWORD(lParam); + + int done = 0; + + for (int pass=0; pass<2 && !done; pass++) + { + IconList::iterator p; + for (p = m_icon_list.begin(); p != m_icon_list.end(); p++) + { + RECT *pr = (pass==0) ? &p->icon_rect : &p->label_rect; + int bottom_extend = (pass==0) ? 3 : 0; // accepts clicks in the 3-pixel gap between the icon and the text label. + if (pt.x >= pr->left && + pt.x <= pr->right && + pt.y >= pr->top && + pt.y <= pr->bottom + bottom_extend) + { + switch (uMsg) + { + case WM_RBUTTONUP: + //pt.x += m_lpDX->m_monitor_rect.left; + //pt.y += m_lpDX->m_monitor_rect.top; + DoExplorerMenu(GetPluginWindow(), (LPITEMIDLIST)p->pidl, pt); + break; + case WM_LBUTTONDBLCLK: + { + char buf[MAX_PATH]; + sprintf(buf, "%s\\%s", m_szDesktopFolder, p->name); + ExecutePidl((LPITEMIDLIST)p->pidl, buf, m_szDesktopFolder, GetPluginWindow()); + } + break; + case WM_LBUTTONDOWN: + m_desktop_dragging = 1; + memcpy(m_desktop_drag_pidl, p->pidl, sizeof(m_desktop_drag_pidl)); + m_desktop_drag_startpos.x = LOWORD(lParam); + m_desktop_drag_startpos.y = HIWORD(lParam); + m_desktop_drag_curpos.x = LOWORD(lParam); + m_desktop_drag_curpos.y = HIWORD(lParam); + if (!(wParam & MK_CONTROL)) // if CTRL not held down + { + if (!p->selected) + { + DeselectDesktop(); + p->selected = 1; + } + } + else + { + p->selected = 1-p->selected; + } + break; + case WM_RBUTTONDOWN: + DeselectDesktop(); + p->selected = 1; + break; + } + + done = 1; + break; + } + } + } + + if (!done) + { + // deselect all, unless they're CTRL+clicking and missed an icon. + if (uMsg!=WM_LBUTTONDOWN || !(wParam & MK_CONTROL)) + DeselectDesktop(); + + if (uMsg==WM_RBUTTONUP)// || uMsg==WM_RBUTTONDOWN) + { + // note: can't use GetMenu and TrackPopupMenu here because the hwnd param to TrackPopupMenu must belong to current application. + + // (before sending coords to desktop window, xform them into its client coords:) + POINT pt; + pt.x = LOWORD(lParam); + pt.y = HIWORD(lParam); + ScreenToClient(m_hWndDesktopListView, &pt); + lParam = MAKELPARAM(pt.x + m_lpDX->m_monitor_rect.left, pt.y + m_lpDX->m_monitor_rect.top); + + PostMessage(m_hWndDesktopListView, uMsg, wParam, lParam); + //PostMessage(m_hWndDesktopListView, WM_CONTEXTMENU, (WPARAM)m_hWndDesktopListView, lParam); + } + else if (uMsg==WM_LBUTTONDOWN) + { + m_desktop_box = 1; + m_desktop_drag_startpos.x = LOWORD(lParam); + m_desktop_drag_startpos.y = HIWORD(lParam); + m_desktop_drag_curpos.x = LOWORD(lParam); + m_desktop_drag_curpos.y = HIWORD(lParam); + } + } + + // repaint window manually, if winamp is paused + if (SendMessage(m_hWndWinamp,WM_USER,0,104) != 1) + { + PushWindowToJustBeforeDesktop(GetPluginWindow()); + DrawAndDisplay(1); + } + + //return 0; + } + break; + + case WM_SETFOCUS: + // note: this msg never comes in when embedwnd is used, but that's ok, because that's only + // in Windowed mode, and m_lost_focus only makes us sleep when fullscreen. + m_lost_focus = 0; + break; + + case WM_KILLFOCUS: + // note: this msg never comes in when embedwnd is used, but that's ok, because that's only + // in Windowed mode, and m_lost_focus only makes us sleep when fullscreen. + m_lost_focus = 1; + break; + + case WM_SETCURSOR: + if ( + (m_screenmode == FULLSCREEN) || + (m_screenmode == FAKE_FULLSCREEN && m_lpDX->m_fake_fs_covers_all) + ) + { + // hide the cursor + SetCursor(NULL); + return TRUE; // prevent Windows from setting cursor to window class cursor + } + break; + + case WM_NCHITTEST: + // Prevent the user from selecting the menu in fullscreen mode + if (m_screenmode != WINDOWED) + return HTCLIENT; + break; + + case WM_SYSCOMMAND: + // Prevent *moving/sizing* and *entering standby mode* when in fullscreen mode + switch (wParam) + { + case SC_MOVE: + case SC_SIZE: + case SC_MAXIMIZE: + case SC_KEYMENU: + if (m_screenmode != WINDOWED) + return 1; + break; + case SC_MONITORPOWER: + if (m_screenmode == FULLSCREEN || m_screenmode == FAKE_FULLSCREEN) + return 1; + break; + } + break; + + case WM_CONTEXTMENU: + // launch popup context menu. see handler for WM_COMMAND also. + if (m_screenmode == DESKTOP) + { + // note: execution should never reach this point, + // because we don't pass WM_RBUTTONUP to DefWindowProc + // when in desktop mode! + return 0; + } + else if (m_screenmode == WINDOWED) // context menus only allowed in ~windowed modes + { + TrackPopupMenuEx(m_context_menu, TPM_VERTICAL, LOWORD(lParam), HIWORD(lParam), hWnd, NULL); + return 0; + } + break; + + case WM_COMMAND: + // handle clicks on items on context menu. + if (m_screenmode == WINDOWED) + { + switch (LOWORD(wParam)) + { + case ID_QUIT: + m_exiting = 1; + PostMessage(hWnd, WM_CLOSE, 0, 0); + return 0; + case ID_GO_FS: + if (m_frame > 0) + ToggleFullScreen(); + return 0; + case ID_DESKTOP_MODE: + if (m_frame > 0) + ToggleDesktop(); + return 0; + case ID_SHOWHELP: + ToggleHelp(); + return 0; + case ID_SHOWPLAYLIST: + TogglePlaylist(); + return 0; + } + // then allow the plugin to override any command: + if (MyWindowProc(hWnd, uMsg, wParam, lParam) == 0) + return 0; + } + break; + + /* + KEY HANDLING: the basic idea: + -in all cases, handle or capture: + -ZXCVBRS, zxcvbrs + -also make sure it's case-insensitive! (lowercase come through only as WM_CHAR; uppercase come in as both) + -(ALT+ENTER) + -(F1, ESC, UP, DN, Left, Right, SHIFT+l/r) + -(P for playlist) + -when playlist showing: steal J, HOME, END, PGUP, PGDN, UP, DOWN, ESC + -(BLOCK J, L) + -when integrated with winamp (using embedwnd), also handle these keys: + -j, l, L, CTRL+L [windowed mode only!] + -CTRL+P, CTRL+D + -CTRL+TAB + -ALT-E + -ALT+F (main menu) + -ALT+3 (id3) + */ + + case WM_SYSKEYDOWN: + if (wParam==VK_RETURN && m_frame > 0) + { + ToggleFullScreen(); + return 0; + } + // if in embedded mode (using winamp skin), pass ALT+ keys on to winamp + // ex: ALT+E, ALT+F, ALT+3... + if (m_screenmode==WINDOWED && m_lpDX->m_current_mode.m_skin) + return PostMessage(m_hWndWinamp, uMsg, wParam, lParam); // force-pass to winamp; required for embedwnd + break; + + case WM_SYSKEYUP: + if (m_screenmode==WINDOWED && m_lpDX->m_current_mode.m_skin) + return PostMessage(m_hWndWinamp, uMsg, wParam, lParam); // force-pass to winamp; required for embedwnd + break; + + case WM_SYSCHAR: + if ((wParam=='k' || wParam=='K')) + { + OnAltK(); + return 0; + } + if ((wParam=='d' || wParam=='D') && m_frame > 0) + { + ToggleDesktop(); + return 0; + } + break; + + case WM_CHAR: + // if playlist is showing, steal p/j keys from the plugin: + if (m_show_playlist) + { + switch (wParam) + { + case 'j': + case 'J': + m_playlist_pos = SendMessage(m_hWndWinamp,WM_USER, 0, 125); + return 0; + default: + { + int nSongs = SendMessage(m_hWndWinamp,WM_USER, 0, 124); + int found = 0; + int orig_pos = m_playlist_pos; + int inc = (wParam>='A' && wParam<='Z') ? -1 : 1; + while (1) + { + if (inc==1 && m_playlist_pos >= nSongs-1) + break; + if (inc==-1 && m_playlist_pos <= 0) + break; + + m_playlist_pos += inc; + + char buf[32]; + strncpy(buf, (char*)SendMessage(m_hWndWinamp, WM_USER, m_playlist_pos, 212), 31); + buf[31] = 0; + + // remove song # and period from beginning + char *p = buf; + while (*p >= '0' && *p <= '9') p++; + if (*p == '.' && *(p+1) == ' ') + { + p += 2; + int pos = 0; + while (*p != 0) + { + buf[pos++] = *p; + p++; + } + buf[pos++] = 0; + } + + int wParam2 = (wParam>='A' && wParam<='Z') ? (wParam + 'a'-'A') : (wParam + 'A'-'a'); + if (buf[0]==wParam || buf[0]==wParam2) + { + found = 1; + break; + } + } + + if (!found) + m_playlist_pos = orig_pos; + } + return 0; + } + } + + // then allow the plugin to override any keys: + if (MyWindowProc(hWnd, uMsg, wParam, lParam) == 0) + return 0; + + // finally, default key actions: + if (wParam == keyMappings[5] || wParam == keyMappings[6]) // 'z' or 'Z' + { + PostMessage(m_hWndWinamp,WM_COMMAND,40044,0); + return 0; + } + else + { + switch (wParam) + { + // WINAMP PLAYBACK CONTROL KEYS: + case 'x': + case 'X': + PostMessage(m_hWndWinamp,WM_COMMAND,40045,0); + return 0; + case 'c': + case 'C': + PostMessage(m_hWndWinamp,WM_COMMAND,40046,0); + return 0; + case 'v': + case 'V': + PostMessage(m_hWndWinamp,WM_COMMAND,40047,0); + return 0; + case 'b': + case 'B': + PostMessage(m_hWndWinamp,WM_COMMAND,40048,0); + return 0; + case 's': + case 'S': + //if (SendMessage(m_hWndWinamp,WM_USER,0,250)) + // sprintf(m_szUserMessage, "shuffle is now OFF"); // shuffle was on + //else + // sprintf(m_szUserMessage, "shuffle is now ON"); // shuffle was off + + // toggle shuffle + PostMessage(m_hWndWinamp,WM_COMMAND,40023,0); + return 0; + case 'r': + case 'R': + // toggle repeat + PostMessage(m_hWndWinamp,WM_COMMAND,40022,0); + return 0; + case 'p': + case 'P': + TogglePlaylist(); + return 0; + case 'l': + // note that this is actually correct; when you hit 'l' from the + // MAIN winamp window, you get an "open files" dialog; when you hit + // 'l' from the playlist editor, you get an "add files to playlist" dialog. + // (that sends IDC_PLAYLIST_ADDMP3==1032 to the playlist, which we can't + // do from here.) + PostMessage(m_hWndWinamp,WM_COMMAND,40029,0); + return 0; + case 'L': + PostMessage(m_hWndWinamp,WM_COMMAND,40187,0); + return 0; + case 'j': + PostMessage(m_hWndWinamp,WM_COMMAND,40194,0); + return 0; + } + + return 0;//DefWindowProc(hWnd,uMsg,wParam,lParam); + } + break; // end case WM_CHAR + + case WM_KEYUP: + + // allow the plugin to override any keys: + if (MyWindowProc(hWnd, uMsg, wParam, lParam) == 0) + return 0; + + /* + switch(wParam) + { + case VK_SOMETHING: + ... + break; + } + */ + + return 0; + break; + + case WM_KEYDOWN: + if (m_show_playlist) + { + switch (wParam) + { + case VK_ESCAPE: + if(m_show_playlist) + TogglePlaylist(); + //m_show_playlist = 0; + return 0; + + case VK_UP: + { + int nRepeat = lParam & 0xFFFF; + if (GetKeyState(VK_SHIFT) & mask) + m_playlist_pos -= 10*nRepeat; + else + m_playlist_pos -= nRepeat; + } + return 0; + + case VK_DOWN: + { + int nRepeat = lParam & 0xFFFF; + if (GetKeyState(VK_SHIFT) & mask) + m_playlist_pos += 10*nRepeat; + else + m_playlist_pos += nRepeat; + } + return 0; + + case VK_HOME: + m_playlist_pos = 0; + return 0; + + case VK_END: + m_playlist_pos = SendMessage(m_hWndWinamp,WM_USER, 0, 124) - 1; + return 0; + + case VK_PRIOR: + if (GetKeyState(VK_SHIFT) & mask) + m_playlist_pageups += 10; + else + m_playlist_pageups++; + return 0; + + case VK_NEXT: + if (GetKeyState(VK_SHIFT) & mask) + m_playlist_pageups -= 10; + else + m_playlist_pageups--; + return 0; + + case VK_RETURN: + SendMessage(m_hWndWinamp,WM_USER, m_playlist_pos, 121); // set sel + SendMessage(m_hWndWinamp,WM_COMMAND, 40045, 0); // play it + return 0; + } + } + + // allow the plugin to override any keys: + if (MyWindowProc(hWnd, uMsg, wParam, lParam) == 0) + return 0; + + switch (wParam) + { + case VK_F1: + m_show_press_f1_msg = 0; + ToggleHelp(); + return 0; + + case VK_ESCAPE: + if (m_show_help) + ToggleHelp(); + else + { + if (m_screenmode == FAKE_FULLSCREEN || m_screenmode == FULLSCREEN) + { + ToggleFullScreen(); + } + else if (m_screenmode == DESKTOP) + { + ToggleDesktop(); + } + // exit the program on escape + //m_exiting = 1; + //PostMessage(hWnd, WM_CLOSE, 0, 0); + } + return 0; + + case VK_UP: + // increase volume + { + int nRepeat = lParam & 0xFFFF; + for (i=0; i<nRepeat*2; i++) PostMessage(m_hWndWinamp,WM_COMMAND,40058,0); + } + return 0; + + case VK_DOWN: + // decrease volume + { + int nRepeat = lParam & 0xFFFF; + for (i=0; i<nRepeat*2; i++) PostMessage(m_hWndWinamp,WM_COMMAND,40059,0); + } + return 0; + + case VK_LEFT: + case VK_RIGHT: + { + bool bShiftHeldDown = (GetKeyState(VK_SHIFT) & mask) != 0; + int cmd = (wParam == VK_LEFT) ? 40144 : 40148; + int nRepeat = lParam & 0xFFFF; + int reps = (bShiftHeldDown) ? 6*nRepeat : 1*nRepeat; + + for (int i=0; i<reps; i++) + PostMessage(m_hWndWinamp,WM_COMMAND,cmd,0); + } + return 0; + default: + // pass CTRL+A thru CTRL+Z, and also CTRL+TAB, to winamp, *if we're in windowed mode* and using an embedded window. + // be careful though; uppercase chars come both here AND to WM_CHAR handler, + // so we have to eat some of them here, to avoid them from acting twice. + if (m_screenmode==WINDOWED && m_lpDX && m_lpDX->m_current_mode.m_skin) + { + if (bCtrlHeldDown && ((wParam >= 'A' && wParam <= 'Z') || wParam==VK_TAB)) + { + PostMessage(m_hWndWinamp, uMsg, wParam, lParam); + return 0; + } + } + return 0; + } + + return 0; + break; + } + + return MyWindowProc(hWnd, uMsg, wParam, lParam);//DefWindowProc(hWnd, uMsg, wParam, lParam); + //return 0L; +} + +LRESULT CALLBACK CPluginShell::DesktopWndProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam) +{ + CPluginShell* p = (CPluginShell*)GetWindowLongPtr(hWnd,GWLP_USERDATA); + if (p) + return p->PluginShellDesktopWndProc(hWnd, uMsg, wParam, lParam); + else + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +LRESULT CPluginShell::PluginShellDesktopWndProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam) +{ + //#ifdef _DEBUG + // OutputDebugMessage("kbfocus", hWnd, uMsg, wParam, lParam); + //#endif + + switch (uMsg) + { + case WM_KEYDOWN: + case WM_KEYUP: + case WM_CHAR: + case WM_SYSCHAR: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + //PostMessage(GetPluginWindow(), uMsg, wParam, lParam); + PluginShellWindowProc(GetPluginWindow(), uMsg, wParam, lParam); + return 0; + break; + } + + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +void CPluginShell::AlignWaves() +{ + // align waves, using recursive (mipmap-style) least-error matching + // note: NUM_WAVEFORM_SAMPLES must be between 32 and 576. + + int align_offset[2] = { 0, 0 }; + +#if (NUM_WAVEFORM_SAMPLES < 576) // [don't let this code bloat our DLL size if it's not going to be used] + + int nSamples = NUM_WAVEFORM_SAMPLES; + +#define MAX_OCTAVES 10 + + int octaves = (int)floorf(logf((float)(576-nSamples))/logf(2.0f)); + if (octaves < 4) + return; + if (octaves > MAX_OCTAVES) + octaves = MAX_OCTAVES; + + for (int ch=0; ch<2; ch++) + { + // only worry about matching the lower 'nSamples' samples + float temp_new[MAX_OCTAVES][576]; + float temp_old[MAX_OCTAVES][576]; + static float temp_weight[MAX_OCTAVES][576]; + static int first_nonzero_weight[MAX_OCTAVES]; + static int last_nonzero_weight[MAX_OCTAVES]; + int spls[MAX_OCTAVES]; + int space[MAX_OCTAVES]; + + memcpy(temp_new[0], m_sound.fWaveform[ch], sizeof(float)*576); + memcpy(temp_old[0], &m_oldwave[ch][m_prev_align_offset[ch]], sizeof(float)*nSamples); + spls[0] = 576; + space[0] = 576 - nSamples; + + // potential optimization: could reuse (instead of recompute) mip levels for m_oldwave[2][]? + int octave = 0; + for (octave=1; octave<octaves; octave++) + { + spls[octave] = spls[octave-1]/2; + space[octave] = space[octave-1]/2; + for (int n=0; n<spls[octave]; n++) + { + temp_new[octave][n] = 0.5f*(temp_new[octave-1][n*2] + temp_new[octave-1][n*2+1]); + temp_old[octave][n] = 0.5f*(temp_old[octave-1][n*2] + temp_old[octave-1][n*2+1]); + } + } + + if (!m_align_weights_ready) + { + m_align_weights_ready = 1; + for (octave=0; octave<octaves; octave++) + { + int compare_samples = spls[octave] - space[octave]; + int n = 0; + for (n=0; n<compare_samples; n++) + { + // start with pyramid-shaped pdf, from 0..1..0 + if (n < compare_samples/2) + temp_weight[octave][n] = n*2/(float)compare_samples; + else + temp_weight[octave][n] = (compare_samples-1 - n)*2/(float)compare_samples; + + // TWEAK how much the center matters, vs. the edges: + temp_weight[octave][n] = (temp_weight[octave][n] - 0.8f)*5.0f + 0.8f; + + // clip: + if (temp_weight[octave][n]>1) temp_weight[octave][n] = 1; + if (temp_weight[octave][n]<0) temp_weight[octave][n] = 0; + } + + n = 0; + while (temp_weight[octave][n] == 0 && n < compare_samples) + n++; + first_nonzero_weight[octave] = n; + + n = compare_samples-1; + while (temp_weight[octave][n] == 0 && n >= 0) + n--; + last_nonzero_weight[octave] = n; + } + } + + int n1 = 0; + int n2 = space[octaves-1]; + for (octave = octaves-1; octave>=0; octave--) + { + // for example: + // space[octave] == 4 + // spls[octave] == 36 + // (so we test 32 samples, w/4 offsets) + int compare_samples = spls[octave]-space[octave]; + + int lowest_err_offset = -1; + float lowest_err_amount = 0; + for (int n=n1; n<n2; n++) + { + float err_sum = 0; + //for (int i=0; i<compare_samples; i++) + for (int i=first_nonzero_weight[octave]; i<=last_nonzero_weight[octave]; i++) + { + float x = (temp_new[octave][i+n] - temp_old[octave][i]) * temp_weight[octave][i]; + if (x>0) + err_sum += x; + else + err_sum -= x; + } + + if (lowest_err_offset == -1 || err_sum < lowest_err_amount) + { + lowest_err_offset = n; + lowest_err_amount = err_sum; + } + } + + // now use 'lowest_err_offset' to guide bounds of search in next octave: + // space[octave] == 8 + // spls[octave] == 72 + // -say 'lowest_err_offset' was 2 + // -that corresponds to samples 4 & 5 of the next octave + // -also, expand about this by 2 samples? YES. + // (so we'd test 64 samples, w/8->4 offsets) + if (octave > 0) + { + n1 = lowest_err_offset*2 -1; + n2 = lowest_err_offset*2+2+1; + if (n1 < 0) n1=0; + if (n2 > space[octave-1]) n2 = space[octave-1]; + } + else + align_offset[ch] = lowest_err_offset; + } + } +#endif + memcpy(m_oldwave[0], m_sound.fWaveform[0], sizeof(float)*576); + memcpy(m_oldwave[1], m_sound.fWaveform[1], sizeof(float)*576); + m_prev_align_offset[0] = align_offset[0]; + m_prev_align_offset[1] = align_offset[1]; + + // finally, apply the results: modify m_sound.fWaveform[2][0..576] + // by scooting the aligned samples so that they start at m_sound.fWaveform[2][0]. + for (int ch=0; ch<2; ch++) + if (align_offset[ch]>0) + { + for (int i=0; i<nSamples; i++) + m_sound.fWaveform[ch][i] = m_sound.fWaveform[ch][i+align_offset[ch]]; + // zero the rest out, so it's visually evident that these samples are now bogus: + memset(&m_sound.fWaveform[ch][nSamples], 0, (576-nSamples)*sizeof(float)); + } +} + +LRESULT CALLBACK CPluginShell::VJModeWndProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam) +{ + CPluginShell* p = (CPluginShell*)GetWindowLongPtr(hWnd,GWLP_USERDATA); + if (p) + return p->PluginShellVJModeWndProc(hWnd, uMsg, wParam, lParam); + else + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +LRESULT CPluginShell::PluginShellVJModeWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ +#ifdef _DEBUG + if (message != WM_MOUSEMOVE && + message != WM_NCHITTEST && + message != WM_SETCURSOR && + message != WM_COPYDATA && + message != WM_USER) + { + char caption[256] = "VJWndProc: frame 0, "; + if (m_frame > 0) + { + float time = m_time; + int hours = (int)(time/3600); + time -= hours*3600; + int minutes = (int)(time/60); + time -= minutes*60; + int seconds = (int)time; + time -= seconds; + int dsec = (int)(time*100); + sprintf(caption, "VJWndProc: frame %d, t=%dh:%02dm:%02d.%02ds, ", m_frame, hours, minutes, seconds, dsec); + } + OutputDebugMessage(caption, hwnd, message, wParam, lParam); + } +#endif + + switch (message) + { + case WM_KEYDOWN: + case WM_KEYUP: + case WM_CHAR: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_SYSCHAR: + // pass keystrokes on to plugin! + return PluginShellWindowProc(GetPluginWindow(),message,wParam,lParam); + + case WM_ERASEBKGND: + // Repaint window when song is paused and image needs to be repainted: + if (SendMessage(m_hWndWinamp,WM_USER,0,104)!=1 && m_vjd3d9_device && GetFrame() > 0) // WM_USER/104 return codes: 1=playing, 3=paused, other=stopped + { + m_vjd3d9_device->Present(NULL,NULL,NULL,NULL); + return 0; + } + break; + + /* + case WM_WINDOWPOSCHANGING: + if (m_screenmode == DESKTOP) + { + LPWINDOWPOS pwp = (LPWINDOWPOS)lParam; + if (pwp) + pwp->flags |= SWP_NOOWNERZORDER | SWP_NOZORDER; + } + break; + + case WM_ACTIVATEAPP: + // *Very Important Handler!* + // -Without this code, the app would not work properly when running in true + // fullscreen mode on multiple monitors; it would auto-minimize whenever the + // user clicked on a window in another display. + if (wParam == 1 && + m_screenmode == DESKTOP && + m_frame > 0 && + !m_exiting + ) + { + return 0; + } + break; + + /* + case WM_NCACTIVATE: + // *Very Important Handler!* + // -Without this code, the app would not work properly when running in true + // fullscreen mode on multiple monitors; it would auto-minimize whenever the + // user clicked on a window in another display. + // (NOTE: main window also handles this message this way) + if (wParam == 0 && + m_screenmode == FULLSCREEN && + m_frame > 0 && + !m_exiting && + m_lpDX && + m_lpDX->m_ready + && m_lpDX->m_lpD3D && + m_lpDX->m_lpD3D->GetAdapterCount() > 1 + ) + { + return 0; + } + break; + */ + + /* + case WM_ACTIVATEAPP: + if (wParam == 1 && + m_screenmode == DESKTOP && + m_frame > 0 && + !m_exiting && + m_vjd3d9_device + ) + { + return 0; + } + break; + */ + + /* + case WM_WINDOWPOSCHANGING: + if ( + m_screenmode == DESKTOP + && (!m_force_accept_WM_WINDOWPOSCHANGING) + && m_lpDX && m_lpDX->m_ready + ) + { + // unless we requested it ourselves or it's init time, + // prevent the fake desktop window from moving around + // in the Z order! (i.e., keep it on the bottom) + + // without this code, when you click on the 'real' desktop + // in a multimon setup, any windows that are overtop of the + // 'fake' desktop will flash, since they'll be covered + // up by the fake desktop window (but then shown again on + // the next frame, when we detect that the fake desktop + // window isn't on bottom & send it back to the bottom). + + LPWINDOWPOS pwp = (LPWINDOWPOS)lParam; + if (pwp) + pwp->flags |= SWP_NOOWNERZORDER | SWP_NOZORDER; + } + break; + */ + + case WM_CLOSE: + // if they close the VJ window (by some means other than ESC key), + // this will make the graphics window close, too. + m_exiting = 1; + if (GetPluginWindow()) + PostMessage(GetPluginWindow(), WM_CLOSE, 0, 0); + break; + + case WM_GETMINMAXINFO: + { + // don't let the window get too small + MINMAXINFO* p = (MINMAXINFO*)lParam; + if (p->ptMinTrackSize.x < 64) + p->ptMinTrackSize.x = 64; + p->ptMinTrackSize.y = p->ptMinTrackSize.x*3/4; + } + return 0; + + case WM_SIZE: + // clear or set activity flag to reflect focus + if (m_vjd3d9_device && !m_resizing_textwnd) + { + m_hidden_textwnd = (SIZE_MAXHIDE==wParam || SIZE_MINIMIZED==wParam) ? TRUE : FALSE; + + if (SIZE_MAXIMIZED==wParam || SIZE_RESTORED==wParam) // the window has been maximized or restored + OnUserResizeTextWindow(); + } + break; + + case WM_ENTERSIZEMOVE: + m_resizing_textwnd = 1; + break; + + case WM_EXITSIZEMOVE: + if (m_vjd3d9_device) + OnUserResizeTextWindow(); + m_resizing_textwnd = 0; + break; + } + + return DefWindowProc(hwnd, message, wParam, lParam); +}
\ No newline at end of file |