mirror of
https://github.com/Zaarrg/stremio-community-v5.git
synced 2026-01-11 20:10:31 +00:00
- Added Allow debug logs to cmakelist instead of debug runs - Added Discord rpc SDK supporting buttons and type - Added Discord RPC handling - Added Discord RPC Events for each stremio site - Added DiscordRPC settings toggle to disable RPC - Changed App image to a cleaner one
584 lines
19 KiB
C++
584 lines
19 KiB
C++
#include "mainwindow.h"
|
|
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <windowsx.h>
|
|
#include <ShlObj.h>
|
|
|
|
#include "../core/globals.h"
|
|
#include "../resource.h"
|
|
#include "../utils/crashlog.h"
|
|
#include "../utils/helpers.h"
|
|
#include "../utils/config.h"
|
|
#include "../mpv/player.h"
|
|
#include "../tray/tray.h"
|
|
#include "../ui/splash.h"
|
|
#include "../webview/webview.h"
|
|
#include "../updater/updater.h"
|
|
#include "../utils/discord.h"
|
|
|
|
// Single-instance
|
|
bool FocusExistingInstance(const std::wstring &protocolArg)
|
|
{
|
|
HWND hExistingWnd = FindWindowW(APP_CLASS, nullptr);
|
|
if(hExistingWnd) {
|
|
if(IsIconic(hExistingWnd)) {
|
|
ShowWindow(hExistingWnd, SW_RESTORE);
|
|
} else if(!IsWindowVisible(hExistingWnd)) {
|
|
ShowWindow(hExistingWnd, SW_SHOW);
|
|
}
|
|
SetForegroundWindow(hExistingWnd);
|
|
SetFocus(hExistingWnd);
|
|
if(!protocolArg.empty()) {
|
|
COPYDATASTRUCT cds;
|
|
cds.dwData = 1;
|
|
cds.cbData = static_cast<DWORD>(protocolArg.size() * sizeof(wchar_t));
|
|
cds.lpData = (PVOID)protocolArg.c_str();
|
|
SendMessage(hExistingWnd, WM_COPYDATA, 0, (LPARAM)&cds);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CheckSingleInstance(int argc, char* argv[], std::wstring &outProtocolArg)
|
|
{
|
|
g_hMutex = CreateMutexW(nullptr, FALSE, L"SingleInstanceMtx_StremioWebShell");
|
|
if(!g_hMutex){
|
|
std::wcerr << L"CreateMutex failed => fallback to multi.\n";
|
|
AppendToCrashLog("CreateMutex failed => fallback to multi.");
|
|
return true;
|
|
}
|
|
std::wstring protocolArg;
|
|
for(int i=1; i<argc; ++i){
|
|
int size_needed = MultiByteToWideChar(CP_UTF8, 0, argv[i], -1, NULL, 0);
|
|
if(size_needed > 0){
|
|
std::wstring argW(size_needed - 1, 0);
|
|
MultiByteToWideChar(CP_UTF8, 0, argv[i], -1, &argW[0], size_needed);
|
|
if(argW.rfind(L"stremio://",0)==0 || argW.rfind(L"magnet:",0)==0 || FileExists(argW)) {
|
|
protocolArg = argW;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(GetLastError()==ERROR_ALREADY_EXISTS){
|
|
FocusExistingInstance(protocolArg);
|
|
return false;
|
|
}
|
|
outProtocolArg = protocolArg;
|
|
return true;
|
|
}
|
|
|
|
void ToggleFullScreen(HWND hWnd, bool enable)
|
|
{
|
|
static WINDOWPLACEMENT prevPlc={sizeof(prevPlc)};
|
|
if(enable==g_isFullscreen) return;
|
|
g_isFullscreen = enable;
|
|
|
|
if(enable){
|
|
GetWindowPlacement(hWnd, &prevPlc);
|
|
MONITORINFO mi={sizeof(mi)};
|
|
if(GetMonitorInfoW(MonitorFromWindow(hWnd,MONITOR_DEFAULTTOPRIMARY), &mi)){
|
|
SetWindowLongW(hWnd,GWL_STYLE,WS_POPUP|WS_VISIBLE);
|
|
SetWindowPos(hWnd,HWND_TOP,
|
|
mi.rcMonitor.left, mi.rcMonitor.top,
|
|
mi.rcMonitor.right - mi.rcMonitor.left,
|
|
mi.rcMonitor.bottom - mi.rcMonitor.top,
|
|
SWP_FRAMECHANGED|SWP_SHOWWINDOW);
|
|
}
|
|
} else {
|
|
SetWindowLongW(hWnd,GWL_STYLE,WS_OVERLAPPEDWINDOW|WS_VISIBLE);
|
|
SetWindowPlacement(hWnd,&prevPlc);
|
|
SetWindowPos(hWnd,nullptr,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_FRAMECHANGED|SWP_SHOWWINDOW);
|
|
}
|
|
}
|
|
|
|
// Dark/Light theme
|
|
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
|
|
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
|
|
#endif
|
|
|
|
static void UpdateTheme(HWND hWnd)
|
|
{
|
|
if(g_useDarkTheme){
|
|
BOOL dark = TRUE;
|
|
DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &dark, sizeof(dark));
|
|
} else {
|
|
BOOL dark = FALSE;
|
|
DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &dark, sizeof(dark));
|
|
}
|
|
}
|
|
// Handling inbound/outbound Messages
|
|
void SendToJS(const std::string &eventName, const nlohmann::json &eventData)
|
|
{
|
|
static int nextId = 1;
|
|
nlohmann::json msg;
|
|
msg["type"] = 1;
|
|
msg["object"] = "transport";
|
|
msg["id"] = nextId++;
|
|
|
|
msg["args"] = { eventName, eventData };
|
|
|
|
// Serialize to wstring + Post
|
|
std::string payload = msg.dump();
|
|
std::wstring wpayload(payload.begin(), payload.end());
|
|
g_webview->PostWebMessageAsString(wpayload.c_str());
|
|
|
|
#ifdef DEBUG_LOG
|
|
std::cout << "[Native->JS] " << payload << "\n";
|
|
#endif
|
|
}
|
|
|
|
void HandleEvent(const std::string &ev, std::vector<std::string> &args)
|
|
{
|
|
if(ev=="mpv-command"){
|
|
if(!args.empty() && args[0] == "loadfile" && args.size() > 1) {
|
|
if (args[1].rfind("http://", 0) != 0 && args[1].rfind("https://", 0) != 0) {
|
|
args[1] = decodeURIComponent(args[1]);
|
|
}
|
|
std::vector<std::string> voArgs = {"vo",g_initialVO};
|
|
HandleMpvSetProp(voArgs);
|
|
std::vector<std::string> volumeArgs = {"volume", std::to_string(g_currentVolume)};
|
|
HandleMpvSetProp(volumeArgs);
|
|
g_initialSet = true;
|
|
}
|
|
HandleMpvCommand(args);
|
|
} else if(ev=="mpv-set-prop"){
|
|
HandleMpvSetProp(args);
|
|
} else if(ev=="mpv-observe-prop"){
|
|
HandleMpvObserveProp(args);
|
|
} else if(ev=="app-ready"){
|
|
g_isAppReady=true;
|
|
HideSplash();
|
|
PostMessage(g_hWnd, WM_NOTIFY_FLUSH, 0, 0);
|
|
} else if(ev=="update-requested"){
|
|
RunInstallerAndExit();
|
|
} else if(ev == "seek-hover") {
|
|
if (g_thumbFastHeight == 0) return;
|
|
if(g_ignoreHover) {
|
|
auto now = std::chrono::steady_clock::now();
|
|
if(now < g_ignoreUntil) {
|
|
return;
|
|
}
|
|
g_ignoreHover = false;
|
|
}
|
|
|
|
// Expecting arguments: hovered_seconds, x, y
|
|
if(args.size() < 3) {
|
|
std::cerr << "seek-hover requires at least 3 arguments.\n";
|
|
return;
|
|
}
|
|
|
|
// Convert the y-coordinate from string to an integer
|
|
int yCoord = 0;
|
|
try {
|
|
yCoord = std::stoi(args[2]);
|
|
} catch(const std::exception &e) {
|
|
std::cerr << "Error converting y coordinate: " << e.what() << "\n";
|
|
return;
|
|
}
|
|
|
|
// Subtract the thumb fast height from y
|
|
int adjustedY = yCoord - g_thumbFastHeight;
|
|
|
|
// Prepare command for thumbfast with adjusted y-coordinate
|
|
std::vector<std::string> cmdArgs = {
|
|
"script-message-to",
|
|
"thumbfast",
|
|
"thumb",
|
|
args[0], // hovered_seconds
|
|
args[1], // x
|
|
std::to_string(adjustedY) // y with offset
|
|
};
|
|
|
|
HandleMpvCommand(cmdArgs);
|
|
}
|
|
else if(ev == "seek-leave") {
|
|
if (g_thumbFastHeight == 0) return;
|
|
// Set ignore flag and calculate ignore-until timestamp
|
|
g_ignoreHover = true;
|
|
g_ignoreUntil = std::chrono::steady_clock::now() + IGNORE_DURATION;
|
|
|
|
std::vector<std::string> cmdArgs = {
|
|
"script-message-to",
|
|
"thumbfast",
|
|
"clear"
|
|
};
|
|
HandleMpvCommand(cmdArgs);
|
|
} else if(ev=="start-drag"){
|
|
ReleaseCapture();
|
|
SendMessageW(g_hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
|
|
} else if(ev=="refresh"){
|
|
refreshWeb(args.size()>0 && args[0]=="all");
|
|
} else if(ev=="app-error"){
|
|
if(!args.empty() && args.size()>0 && args[0] == "shellComm"){
|
|
if(!g_isAppReady && !g_waitStarted.exchange(true)){
|
|
WaitAndRefreshIfNeeded();
|
|
}
|
|
}
|
|
} else if (ev=="open-external") {
|
|
std::wstring uri(args[0].begin(), args[0].end());
|
|
ShellExecuteW(nullptr, L"open", uri.c_str(), nullptr, nullptr, SW_SHOWNORMAL);
|
|
} else if (ev=="navigate") {
|
|
std::wstring uri(args[0].begin(), args[0].end());
|
|
if (args[0] == "home") {
|
|
g_webview->Navigate(g_webuiUrl.c_str());
|
|
} else {
|
|
g_webview->Navigate(uri.c_str());
|
|
}
|
|
} else if (ev == "activity") {
|
|
SetDiscordPresenceFromArgs(args);
|
|
} else {
|
|
std::cout<<"Unknown event="<<ev<<"\n";
|
|
}
|
|
}
|
|
|
|
|
|
void HandleInboundJSON(const std::string &msg)
|
|
{
|
|
try {
|
|
#ifdef DEBUG_LOG
|
|
std::cout << "[JS -> NATIVE]: " << msg << std::endl;
|
|
#endif
|
|
|
|
auto j = nlohmann::json::parse(msg);
|
|
int type = 0;
|
|
if (j.contains("type") && j["type"].is_number()) {
|
|
type = j["type"].get<int>();
|
|
}
|
|
|
|
if (type == 3) {
|
|
// 3 = Init event
|
|
nlohmann::json root;
|
|
root["id"] = 0;
|
|
nlohmann::json transportObj;
|
|
|
|
json extData = {};
|
|
if (!g_extensionMap.empty()) {
|
|
for (auto& [name, id] : g_extensionMap) {
|
|
extData[WStringToUtf8(name)] = WStringToUtf8(id);
|
|
}
|
|
}
|
|
|
|
transportObj["properties"] = {
|
|
1,
|
|
nlohmann::json::array({0, "shellVersion", 0, APP_VERSION}),
|
|
nlohmann::json::array({0, "BrowserExtensions", 0, extData}),
|
|
};
|
|
transportObj["signals"] = {
|
|
nlohmann::json::array({0, "handleInboundJSONSignal"}),
|
|
};
|
|
nlohmann::json methods = nlohmann::json::array();
|
|
methods.push_back(nlohmann::json::array({"onEvent", "handleInboundJSON"}));
|
|
transportObj["methods"] = methods;
|
|
root["data"]["transport"] = transportObj;
|
|
|
|
std::string payload = root.dump();
|
|
std::wstring wpayload(payload.begin(), payload.end());
|
|
g_webview->PostWebMessageAsString(wpayload.c_str());
|
|
return;
|
|
}
|
|
|
|
if (type == 6 && j.contains("method"))
|
|
{
|
|
std::string methodName = j["method"].get<std::string>();
|
|
if (methodName == "handleInboundJSON" || methodName == "onEvent")
|
|
{
|
|
if (j["args"].is_array() && !j["args"].empty())
|
|
{
|
|
std::string ev;
|
|
if (j["args"][0].is_string()) {
|
|
ev = j["args"][0].get<std::string>();
|
|
} else {
|
|
ev = "Unknown";
|
|
}
|
|
|
|
std::vector<std::string> argVec;
|
|
if (j["args"].size() > 1)
|
|
{
|
|
auto &second = j["args"][1];
|
|
if (second.is_array())
|
|
{
|
|
for (auto &x: second)
|
|
{
|
|
if (x.is_string()) argVec.push_back(x.get<std::string>());
|
|
else argVec.push_back(x.dump());
|
|
}
|
|
}
|
|
else if (second.is_string()) {
|
|
argVec.push_back(second.get<std::string>());
|
|
}
|
|
else {
|
|
argVec.push_back(second.dump());
|
|
}
|
|
}
|
|
|
|
HandleEvent(ev, argVec);
|
|
}
|
|
else {
|
|
std::cout << "[WARN] invokeMethod=handleInboundJSON => no args array?\n";
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
std::cout<<"Unknown Inbound event="<<msg<<"\n";
|
|
} catch(std::exception &ex) {
|
|
std::cerr<<"JSON parse error:"<<ex.what()<<"\n";
|
|
}
|
|
}
|
|
|
|
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch(message)
|
|
{
|
|
case WM_CREATE:
|
|
{
|
|
HICON hIconBig = LoadIcon(g_hInst, MAKEINTRESOURCE(IDR_MAINFRAME));
|
|
HICON hIconSmall = LoadIcon(g_hInst, MAKEINTRESOURCE(IDR_MAINFRAME));
|
|
SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)hIconBig);
|
|
SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSmall);
|
|
|
|
CreateTrayIcon(hWnd);
|
|
UpdateTheme(hWnd);
|
|
break;
|
|
}
|
|
case WM_DPICHANGED:
|
|
{
|
|
RECT* const newRect = reinterpret_cast<RECT*>(lParam);
|
|
SetWindowPos(hWnd, NULL, newRect->left, newRect->top,
|
|
newRect->right - newRect->left,
|
|
newRect->bottom - newRect->top,
|
|
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
break;
|
|
}
|
|
case WM_NOTIFY_FLUSH: {
|
|
if (g_isAppReady) {
|
|
for(const auto& pendingMsg : g_outboundMessages) {
|
|
SendToJS(pendingMsg["type"], pendingMsg);
|
|
}
|
|
g_outboundMessages.clear();
|
|
|
|
if(!g_launchProtocol.empty()) {
|
|
COPYDATASTRUCT cds;
|
|
cds.dwData = 1;
|
|
cds.cbData = static_cast<DWORD>((g_launchProtocol.size()+1) * sizeof(wchar_t));
|
|
cds.lpData = (PVOID)g_launchProtocol.c_str();
|
|
SendMessage(g_hWnd, WM_COPYDATA, (WPARAM)g_hWnd, (LPARAM)&cds);
|
|
g_launchProtocol.clear();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case WM_REACHABILITY_DONE: {
|
|
// wParam is a pointer to a std::wstring we allocated in the thread
|
|
std::wstring* pUrl = reinterpret_cast<std::wstring*>(wParam);
|
|
if(pUrl)
|
|
{
|
|
if (!pUrl->empty() && g_webview)
|
|
{
|
|
std::wcout << L"[WEBVIEW]: Navigating to " << *pUrl << std::endl;
|
|
g_webview->Navigate(pUrl->c_str());
|
|
}
|
|
else
|
|
{
|
|
MessageBoxW(nullptr,
|
|
L"All endpoints are unreachable",
|
|
L"WebView2 Initialization Error",
|
|
MB_ICONERROR | MB_OK);
|
|
}
|
|
delete pUrl;
|
|
}
|
|
break;
|
|
}
|
|
case WM_SETTINGCHANGE:
|
|
{
|
|
UpdateTheme(hWnd);
|
|
break;
|
|
}
|
|
case WM_TRAYICON:
|
|
{
|
|
if(LOWORD(lParam)==WM_RBUTTONUP) {
|
|
ShowTrayMenu(hWnd);
|
|
}
|
|
if(lParam==WM_LBUTTONDBLCLK){
|
|
ShowWindow(hWnd, SW_RESTORE);
|
|
SetForegroundWindow(hWnd);
|
|
}
|
|
break;
|
|
}
|
|
case WM_COMMAND:
|
|
{
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case ID_TRAY_SHOWWINDOW:
|
|
g_showWindow = !g_showWindow;
|
|
ShowWindow(hWnd, g_showWindow?SW_SHOW:SW_HIDE);
|
|
break;
|
|
case ID_TRAY_ALWAYSONTOP:
|
|
g_alwaysOnTop=!g_alwaysOnTop;
|
|
SetWindowPos(hWnd,
|
|
g_alwaysOnTop?HWND_TOPMOST:HWND_NOTOPMOST,
|
|
0,0,0,0,
|
|
SWP_NOMOVE|SWP_NOSIZE);
|
|
break;
|
|
case ID_TRAY_CLOSE_ON_EXIT:
|
|
g_closeOnExit=!g_closeOnExit;
|
|
SaveSettings();
|
|
break;
|
|
case ID_TRAY_USE_DARK_THEME:
|
|
g_useDarkTheme=!g_useDarkTheme;
|
|
SaveSettings();
|
|
UpdateTheme(hWnd);
|
|
break;
|
|
case ID_TRAY_PICTURE_IN_PICTURE:
|
|
TogglePictureInPicture(hWnd, !g_isPipMode);
|
|
break;
|
|
case ID_TRAY_PAUSE_FOCUS_LOST:
|
|
g_pauseOnLostFocus=!g_pauseOnLostFocus;
|
|
SaveSettings();
|
|
break;
|
|
case ID_TRAY_PAUSE_MINIMIZED:
|
|
g_pauseOnMinimize=!g_pauseOnMinimize;
|
|
SaveSettings();
|
|
break;
|
|
case ID_TRAY_QUIT:
|
|
if(g_mpv) mpv_command_string(g_mpv,"quit");
|
|
WINDOWPLACEMENT wp;
|
|
wp.length = sizeof(wp);
|
|
if (GetWindowPlacement(hWnd, &wp)) {
|
|
SaveWindowPlacement(wp);
|
|
}
|
|
DestroyWindow(hWnd);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case WM_COPYDATA:
|
|
{
|
|
PCOPYDATASTRUCT pcds = (PCOPYDATASTRUCT)lParam;
|
|
if (pcds && pcds->dwData == 1 && pcds->lpData) {
|
|
// Assuming data is a wide string containing the URL or file path
|
|
std::wstring receivedUrl((wchar_t*)pcds->lpData, pcds->cbData / sizeof(wchar_t));
|
|
std::wcout << L"Received URL in main instance: " << receivedUrl << std::endl;
|
|
|
|
// Check if received URL is a file and exists
|
|
if (FileExists(receivedUrl)) {
|
|
// Extract file extension
|
|
size_t dotPos = receivedUrl.find_last_of(L".");
|
|
std::wstring extension = (dotPos != std::wstring::npos) ? receivedUrl.substr(dotPos) : L"";
|
|
|
|
if (extension == L".torrent") {
|
|
// Handle .torrent files
|
|
std::string utf8FilePath = WStringToUtf8(receivedUrl);
|
|
|
|
std::ifstream ifs(utf8FilePath, std::ios::binary);
|
|
if (!ifs) {
|
|
std::cerr << "Error: Could not open torrent file.\n";
|
|
break;
|
|
}
|
|
std::vector<unsigned char> fileBuffer(
|
|
(std::istreambuf_iterator<char>(ifs)),
|
|
(std::istreambuf_iterator<char>())
|
|
);
|
|
|
|
json j;
|
|
j["type"] = "OpenTorrent";
|
|
j["data"] = fileBuffer;
|
|
SendToJS("OpenTorrent", j);
|
|
} else {
|
|
// Handle other media files
|
|
std::string utf8FilePath = WStringToUtf8(receivedUrl);
|
|
json j;
|
|
j["type"] = "OpenFile";
|
|
j["path"] = utf8FilePath;
|
|
SendToJS("OpenFile", j);
|
|
}
|
|
} else if (receivedUrl.rfind(L"stremio://detail", 0) == 0) {
|
|
std::string utf8Url = WStringToUtf8(receivedUrl);
|
|
json j;
|
|
j["type"] = "ReplaceLocation";
|
|
j["path"] = utf8Url;
|
|
SendToJS("ReplaceLocation", j);
|
|
} else if (receivedUrl.rfind(L"stremio://", 0) == 0) {
|
|
// Handle stremio:// protocol
|
|
std::string utf8Url = WStringToUtf8(receivedUrl);
|
|
json j;
|
|
j["type"] = "AddonInstall";
|
|
j["path"] = utf8Url;
|
|
SendToJS("AddonInstall", j);
|
|
} else if (receivedUrl.rfind(L"magnet:", 0) == 0) {
|
|
std::string utf8Url = WStringToUtf8(receivedUrl);
|
|
json j;
|
|
j["type"] = "OpenTorrent";
|
|
j["magnet"] = utf8Url;
|
|
SendToJS("OpenTorrent", j);
|
|
} else {
|
|
std::wcout << L"Received URL is neither a valid file nor a stremio:// protocol." << std::endl;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
case WM_CLOSE:
|
|
{
|
|
WINDOWPLACEMENT wp;
|
|
wp.length = sizeof(wp);
|
|
if (GetWindowPlacement(hWnd, &wp)) {
|
|
// Save to ini
|
|
SaveWindowPlacement(wp);
|
|
}
|
|
|
|
if(g_closeOnExit) {
|
|
DestroyWindow(hWnd);
|
|
} else {
|
|
ShowWindow(hWnd, SW_HIDE);
|
|
pauseMPV(g_pauseOnMinimize);
|
|
g_showWindow=false;
|
|
}
|
|
return 0;
|
|
}
|
|
case WM_ACTIVATE:
|
|
{
|
|
if(LOWORD(wParam)==WA_INACTIVE){
|
|
pauseMPV(g_pauseOnLostFocus);
|
|
}
|
|
if (LOWORD(wParam) != WA_INACTIVE)
|
|
{
|
|
SetFocus(hWnd);
|
|
if (g_webview && g_webviewController) {
|
|
g_webviewController->MoveFocus(COREWEBVIEW2_MOVE_FOCUS_REASON_PROGRAMMATIC);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case WM_SIZE:
|
|
{
|
|
if(wParam==SIZE_MINIMIZED){
|
|
pauseMPV(g_pauseOnMinimize);
|
|
}
|
|
if(g_webviewController){
|
|
RECT rc; GetClientRect(hWnd,&rc);
|
|
g_webviewController->put_Bounds(rc);
|
|
}
|
|
if(g_hSplash){
|
|
int w = LOWORD(lParam);
|
|
int h = HIWORD(lParam);
|
|
SetWindowPos(g_hSplash,nullptr,0,0,w,h,SWP_NOZORDER);
|
|
}
|
|
break;
|
|
}
|
|
case WM_MPV_WAKEUP:
|
|
HandleMpvEvents();
|
|
break;
|
|
case WM_DESTROY:
|
|
{
|
|
// release mutex
|
|
if(g_hMutex) { CloseHandle(g_hMutex); g_hMutex=nullptr; }
|
|
PostQuitMessage(0);
|
|
break;
|
|
}
|
|
default:
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
}
|
|
return 0;
|
|
}
|