diff --git a/ProjectFish/Binaries/Win64/ProjectFishEditor.target b/ProjectFish/Binaries/Win64/ProjectFishEditor.target index 42b7486..1116c58 100644 --- a/ProjectFish/Binaries/Win64/ProjectFishEditor.target +++ b/ProjectFish/Binaries/Win64/ProjectFishEditor.target @@ -8028,6 +8028,18 @@ { "Path": "$(ProjectDir)/Binaries/Win64/UnrealEditor.modules", "Type": "RequiredResource" + }, + { + "Path": "$(ProjectDir)/Plugins/DeskMode/Binaries/Win64/UnrealEditor-DeskMode.dll", + "Type": "DynamicLibrary" + }, + { + "Path": "$(ProjectDir)/Plugins/DeskMode/Binaries/Win64/UnrealEditor-DeskMode.pdb", + "Type": "SymbolFile" + }, + { + "Path": "$(ProjectDir)/Plugins/DeskMode/Binaries/Win64/UnrealEditor.modules", + "Type": "RequiredResource" } ], "RuntimeDependencies": [ @@ -33759,6 +33771,10 @@ "Path": "$(EngineDir)/Plugins/XGEController/XGEController.uplugin", "Type": "UFS" }, + { + "Path": "$(ProjectDir)/Plugins/DeskMode/DeskMode.uplugin", + "Type": "UFS" + }, { "Path": "$(ProjectDir)/ProjectFish.uproject", "Type": "UFS" @@ -33827,6 +33843,7 @@ "Dataflow", "DatasmithContent", "DeformerGraph", + "DeskMode", "DumpGPUServices", "EOSShared", "EditorDataStorage", diff --git a/ProjectFish/Binaries/Win64/UnrealEditor-ProjectFish.dll b/ProjectFish/Binaries/Win64/UnrealEditor-ProjectFish.dll index 4046e19..feb522f 100644 Binary files a/ProjectFish/Binaries/Win64/UnrealEditor-ProjectFish.dll and b/ProjectFish/Binaries/Win64/UnrealEditor-ProjectFish.dll differ diff --git a/ProjectFish/Config/DefaultEngine.ini b/ProjectFish/Config/DefaultEngine.ini index 9c21609..013910b 100644 --- a/ProjectFish/Config/DefaultEngine.ini +++ b/ProjectFish/Config/DefaultEngine.ini @@ -29,10 +29,12 @@ r.Lumen.TraceMeshSDFs=0 r.Lumen.Reflections.HardwareRayTracing.Translucent.Refraction.EnableForProject=True r.Lumen.TranslucencyReflections.FrontLayer.EnableForProject=False r.Shadow.TranslucentPerObject.ProjectEnabled=False +r.PostProcessing.PropagateAlpha=True +r.D3D11.UseAllowTearing=0 [/Script/WindowsTargetPlatform.WindowsTargetSettings] -DefaultGraphicsRHI=DefaultGraphicsRHI_DX12 -DefaultGraphicsRHI=DefaultGraphicsRHI_DX12 +DefaultGraphicsRHI=DefaultGraphicsRHI_DX11 +DefaultGraphicsRHI=DefaultGraphicsRHI_DX11 -D3D12TargetedShaderFormats=PCD3D_SM5 +D3D12TargetedShaderFormats=PCD3D_SM6 -D3D11TargetedShaderFormats=PCD3D_SM5 diff --git a/ProjectFish/Content/Maps/DeskModeTest.umap b/ProjectFish/Content/Maps/DeskModeTest.umap new file mode 100644 index 0000000..38b39c9 Binary files /dev/null and b/ProjectFish/Content/Maps/DeskModeTest.umap differ diff --git a/ProjectFish/Content/Materials/M_DeskModeMat.uasset b/ProjectFish/Content/Materials/M_DeskModeMat.uasset new file mode 100644 index 0000000..a38e63e Binary files /dev/null and b/ProjectFish/Content/Materials/M_DeskModeMat.uasset differ diff --git a/ProjectFish/Plugins/DeskMode/Binaries/Win64/UnrealEditor-DeskMode.dll b/ProjectFish/Plugins/DeskMode/Binaries/Win64/UnrealEditor-DeskMode.dll new file mode 100644 index 0000000..f9ae273 Binary files /dev/null and b/ProjectFish/Plugins/DeskMode/Binaries/Win64/UnrealEditor-DeskMode.dll differ diff --git a/ProjectFish/Plugins/DeskMode/Binaries/Win64/UnrealEditor.modules b/ProjectFish/Plugins/DeskMode/Binaries/Win64/UnrealEditor.modules new file mode 100644 index 0000000..437a3ab --- /dev/null +++ b/ProjectFish/Plugins/DeskMode/Binaries/Win64/UnrealEditor.modules @@ -0,0 +1,7 @@ +{ + "BuildId": "37670630", + "Modules": + { + "DeskMode": "UnrealEditor-DeskMode.dll" + } +} \ No newline at end of file diff --git a/ProjectFish/Plugins/DeskMode/DeskMode.uplugin b/ProjectFish/Plugins/DeskMode/DeskMode.uplugin new file mode 100644 index 0000000..233e2f0 --- /dev/null +++ b/ProjectFish/Plugins/DeskMode/DeskMode.uplugin @@ -0,0 +1,24 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.0", + "FriendlyName": "DeskMode", + "Description": "", + "Category": "Other", + "CreatedBy": "", + "CreatedByURL": "", + "DocsURL": "", + "MarketplaceURL": "", + "SupportURL": "", + "CanContainContent": true, + "IsBetaVersion": false, + "IsExperimentalVersion": false, + "Installed": false, + "Modules": [ + { + "Name": "DeskMode", + "Type": "Runtime", + "LoadingPhase": "Default" + } + ] +} \ No newline at end of file diff --git a/ProjectFish/Plugins/DeskMode/Resources/Icon128.png b/ProjectFish/Plugins/DeskMode/Resources/Icon128.png new file mode 100644 index 0000000..1231d4a Binary files /dev/null and b/ProjectFish/Plugins/DeskMode/Resources/Icon128.png differ diff --git a/ProjectFish/Plugins/DeskMode/Source/DeskMode/DeskMode.Build.cs b/ProjectFish/Plugins/DeskMode/Source/DeskMode/DeskMode.Build.cs new file mode 100644 index 0000000..d26eea5 --- /dev/null +++ b/ProjectFish/Plugins/DeskMode/Source/DeskMode/DeskMode.Build.cs @@ -0,0 +1,54 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class DeskMode : ModuleRules +{ + public DeskMode(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicIncludePaths.AddRange( + new string[] { + // ... add public include paths required here ... + } + ); + + + PrivateIncludePaths.AddRange( + new string[] { + // ... add other private include paths required here ... + } + ); + + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + // ... add other public dependencies that you statically link with here ... + } + ); + + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "CoreUObject", + "Engine", + "Slate", + "SlateCore", + // ... add private dependencies that you statically link with here ... + "UnrealEd" + } + ); + + + DynamicallyLoadedModuleNames.AddRange( + new string[] + { + // ... add any modules that your module loads dynamically here ... + } + ); + } +} diff --git a/ProjectFish/Plugins/DeskMode/Source/DeskMode/Private/DeskMode.cpp b/ProjectFish/Plugins/DeskMode/Source/DeskMode/Private/DeskMode.cpp new file mode 100644 index 0000000..7af02bd --- /dev/null +++ b/ProjectFish/Plugins/DeskMode/Source/DeskMode/Private/DeskMode.cpp @@ -0,0 +1,73 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "DeskMode.h" + +#include "WindowTransparency.h" +#include "Features/EditorFeatures.h" +#include "Features/IPluginsEditorFeature.h" +DEFINE_LOG_CATEGORY_STATIC(LogDeskMode, Log, All); + +#define LOCTEXT_NAMESPACE "FDeskModeModule" + +void FDeskModeModule::StartupModule() +{ + // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module + //绑定module注册回调 + IModularFeatures& ModularFeatures = IModularFeatures::Get(); + ModularFeatures.OnModularFeatureRegistered().AddRaw(this, &FDeskModeModule::OnModularFeatureRegistered); + //防止plugin模块先加载 + if (ModularFeatures.IsModularFeatureAvailable(EditorFeatures::PluginsEditor)) + { + OnModularFeatureRegistered(EditorFeatures::PluginsEditor, &ModularFeatures.GetModularFeature(EditorFeatures::PluginsEditor)); + } +} + +void FDeskModeModule::ShutdownModule() +{ + // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, + // we call this function before unloading the module. + if (IsValid(WindowTransparency)) + { +#if PLATFORM_WINDOWS + UE_LOG(LogDeskMode, Log, TEXT("WindowTransparency is valid, attempting to restore settings.")); + WindowTransparency->RestoreDefaultWindowSettings(); +#endif + } +} + +void FDeskModeModule::OnModularFeatureRegistered(const FName& Name, IModularFeature* ModularFeature) +{ + +} + + + +TObjectPtr FDeskModeModule::GetWindowTransparency() +{ + FDeskModeModule* Module = FModuleManager::GetModulePtr("DeskMode"); + if (Module) + { + Module->WindowTransparency = nullptr; +#if PLATFORM_WINDOWS + if (!Module->WindowTransparency && !IsRunningCommandlet() && !IsRunningDedicatedServer() && GEngine) + { + Module->WindowTransparency = NewObject(); + if (Module->WindowTransparency->Initialize()) + { + UE_LOG(LogDeskMode, Log, TEXT("WindowTransparency instance created and initialized.")); + } + else + { + UE_LOG(LogDeskMode, Error, TEXT("WindowTransparency instance created but failed to initialize HWND.")); + } + } +#endif + return Module->WindowTransparency; + } + UE_LOG(LogDeskMode, Log, TEXT("WindowTransparency is null, GetWindowTransparency() error.")); + return nullptr; +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FDeskModeModule, DeskMode) \ No newline at end of file diff --git a/ProjectFish/Plugins/DeskMode/Source/DeskMode/Private/DeskModeFunctionLibrary.cpp b/ProjectFish/Plugins/DeskMode/Source/DeskMode/Private/DeskModeFunctionLibrary.cpp new file mode 100644 index 0000000..397cc85 --- /dev/null +++ b/ProjectFish/Plugins/DeskMode/Source/DeskMode/Private/DeskModeFunctionLibrary.cpp @@ -0,0 +1,22 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "DeskModeFunctionLibrary.h" + +#include "DeskMode.h" +#include "WindowTransparency.h" + +void UDeskModeFunctionLibrary::SetWindowFeatures(bool bEnableWindowTransparency, bool bEnableBorderless, + bool bEnableClickThrough, bool bSetWindowTopmost) +{ +#if PLATFORM_WINDOWS + TObjectPtr WindowTransparency = FDeskModeModule::GetWindowTransparency(); + if (IsValid(WindowTransparency)) + { + WindowTransparency->EnableBorderless(bEnableWindowTransparency); + WindowTransparency->EnableWindowTransparency(bEnableWindowTransparency); + WindowTransparency->EnableClickThrough(bEnableClickThrough); + WindowTransparency->SetWIndowsTopmost(bSetWindowTopmost); + } +#endif +} diff --git a/ProjectFish/Plugins/DeskMode/Source/DeskMode/Private/WindowTransparency.cpp b/ProjectFish/Plugins/DeskMode/Source/DeskMode/Private/WindowTransparency.cpp new file mode 100644 index 0000000..8003cdf --- /dev/null +++ b/ProjectFish/Plugins/DeskMode/Source/DeskMode/Private/WindowTransparency.cpp @@ -0,0 +1,372 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "WindowTransparency.h" + +#include "Widgets/SViewport.h" +DEFINE_LOG_CATEGORY_STATIC(LogWindowTransparency, Log, All); + +HWND UWindowTransparency::GetGameHWnd() +{ + if (GEngine->GameViewport->GetWindow()) + { + TSharedPtr StrongSWindow = GEngine->GameViewport->GetWindow(); + if (StrongSWindow.IsValid() && StrongSWindow->GetNativeWindow().IsValid()) { + void* Handle = StrongSWindow->GetNativeWindow()->GetOSWindowHandle(); + UE_LOG(LogWindowTransparency, Verbose, TEXT("GetGameHWnd: Returning HWND from cached GameSWindowPtr: %p"), Handle); + return static_cast(Handle); + } + } + // if (GEngine && GEngine->GameViewport && GEngine->GameViewport->GetWindow().IsValid()) + // { + // TSharedPtr GameSWindow = GEngine->GameViewport->GetWindow(); + // const_cast(this)->GameSWindowPtr = GameSWindow; + // if (GameSWindow.IsValid() && GameSWindow->GetNativeWindow().IsValid()) + // { + // void* Handle = GameSWindow->GetNativeWindow()->GetOSWindowHandle(); + // UE_LOG(LogWindowTransparency, Verbose, TEXT("GetGameHWnd: Returning HWND from GEngine->GameViewport and caching SWindow: %p"), Handle); + // return static_cast(Handle); + // } + // } + // if (FSlateApplication::IsInitialized()) + // { + // TSharedPtr ActiveWindow = FSlateApplication::Get().GetActiveTopLevelWindow(); + // if (ActiveWindow.IsValid() && ActiveWindow->GetNativeWindow().IsValid()) + // { + // void* Handle = ActiveWindow->GetNativeWindow()->GetOSWindowHandle(); + // UE_LOG(LogWindowTransparency, Verbose, TEXT("GetGameHWnd: Returning HWND from GetActiveTopLevelWindow (fallback): %p"), Handle); + // return static_cast(Handle); + // } + // } + // UE_LOG(LogWindowTransparency, Warning, TEXT("GetGameHWnd: Could not retrieve HWND.")); + return nullptr; +} + +bool UWindowTransparency::Initialize() +{ +#if PLATFORM_WINDOWS + if (GameHWnd) + { + return true; + } + + GameHWnd = GetGameHWnd(); + if (GameHWnd) + { + OriginalWindowStyle = GetWindowLongPtr(GameHWnd, GWL_STYLE); + OriginalExWindowStyle = GetWindowLongPtr(GameHWnd, GWL_EXSTYLE); + + LONG_PTR CurrentExStyle = GetWindowLongPtr(GameHWnd, GWL_EXSTYLE); + bIsClickThroughStateOS = (CurrentExStyle & WS_EX_TRANSPARENT) != 0; + + return true; + } + else + { + + UE_LOG(LogWindowTransparency, Warning, TEXT("WindowTransparencyHelper: Could not get game HWND during Initialize.")); + return false; + } +#endif +} + +void UWindowTransparency::RestoreDefaultWindowSettings() +{ +#if PLATFORM_WINDOWS + UE_LOG(LogWindowTransparency, Log, TEXT("Attempting to restore default window settings...")); + if (!GameHWnd || !IsWindow(GameHWnd)) + { + UE_LOG(LogWindowTransparency, Warning, TEXT("Cannot restore default settings: HWND is null or invalid.")); + + bIsClickThroughStateOS = false; + return; + } + + if (GetWindowLongPtr(GameHWnd, GWL_EXSTYLE) != OriginalExWindowStyle) + { + SetWindowLongPtr(GameHWnd, GWL_EXSTYLE, OriginalExWindowStyle); + UE_LOG(LogWindowTransparency, Log, TEXT("Restored OriginalExWindowStyle.")); + + } + + SetWindowPos(GameHWnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE); + InvalidateRect(GameHWnd, NULL, true); + UpdateWindow(GameHWnd); + UE_LOG(LogWindowTransparency, Log, TEXT("Window settings restoration commands issued.")); +#endif +} + +void UWindowTransparency::EnableBorderless(bool bEnableWindowTransparency) +{ +#if PLATFORM_WINDOWS + if (!GameHWnd) + { + UE_LOG(LogWindowTransparency, Error, TEXT("EnableBorderless failed, GameHWnd is null")); + return; + } + LONG_PTR CurrentStyle = GetWindowLongPtr(GameHWnd, GWL_STYLE); + bool bIsCurrentlyBorderless = !(CurrentStyle & WS_CAPTION) && !(CurrentStyle & WS_THICKFRAME); + + LONG_PTR NewStyle = bEnableWindowTransparency ? ((OriginalWindowStyle & ~WS_OVERLAPPEDWINDOW) | WS_POPUP) : OriginalWindowStyle; + SetWindowLongPtr(GameHWnd, GWL_STYLE, NewStyle); + SetWindowPos(GameHWnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE); + InvalidateRect(GameHWnd, NULL, true); + UpdateWindow(GameHWnd); + UE_LOG(LogWindowTransparency, Log, TEXT("Borderless mode set to: %s"), bEnableWindowTransparency ? TEXT("true") : TEXT("false")); +#endif + +} + +void UWindowTransparency::EnableWindowTransparency(bool bEnableWindowTransparency) +{ +#if PLATFORM_WINDOWS + if (!GameHWnd) + { + UE_LOG(LogWindowTransparency, Error, TEXT("EnableWindowTransparency failed, GameHWnd is null")); + return; + } + + //设置窗口透明 + MARGINS margins = bEnableWindowTransparency ? MARGINS{ -1 } : MARGINS{ 0, 0, 0, 0 }; + HRESULT hr = DwmExtendFrameIntoClientArea(GameHWnd, &margins); + if (!SUCCEEDED(hr)) + { + UE_LOG(LogWindowTransparency, Error, TEXT("DwmExtendFrameIntoClientArea %s failed with HRESULT: 0x%08lX"), bEnableWindowTransparency ? TEXT("enable") : TEXT("disable"), hr); + } + + InvalidateRect(GameHWnd, NULL, true); + UpdateWindow(GameHWnd); + UE_LOG(LogWindowTransparency, Log, TEXT("DWM Transparency set to: %s"), bEnableWindowTransparency ? TEXT("true") : TEXT("false")); +#endif +} + +void UWindowTransparency::EnableClickThrough(bool bEnableClickThrough) +{ + #if PLATFORM_WINDOWS + if (!GameHWnd) + { + UE_LOG(LogWindowTransparency, Warning, TEXT("EnableClickThrough: Not initialized or HWND is null. Cannot set OS click-through state.")); + return; + } + + LONG_PTR CurrentExStyle = GetWindowLongPtr(GameHWnd, GWL_EXSTYLE); + bool bIsCurrentlyClickThroughOSLevel = (CurrentExStyle & WS_EX_TRANSPARENT) != 0; + + LONG_PTR NewExStyle; + if (bEnableClickThrough) + { + NewExStyle = CurrentExStyle | WS_EX_LAYERED | WS_EX_TRANSPARENT; + } + else + { + NewExStyle = CurrentExStyle & ~WS_EX_TRANSPARENT; + + } + bIsClickThroughStateOS = bEnableClickThrough; + SetWindowLongPtr(GameHWnd, GWL_EXSTYLE, NewExStyle); + LONG_PTR StyleAfterSet = GetWindowLongPtr(GameHWnd, GWL_EXSTYLE); + bool bSetSuccessfully = (bEnableClickThrough && (StyleAfterSet & WS_EX_TRANSPARENT)) || (!bEnableClickThrough && !(StyleAfterSet & WS_EX_TRANSPARENT)); + + UE_LOG(LogWindowTransparency, Log, TEXT("EnableClickThrough: OS Click-Through set to %s. OldExStyle: 0x%p, Attempted NewExStyle: 0x%p, Actual StyleAfterSet: 0x%p. Success: %s"), + bEnableClickThrough ? TEXT("true") : TEXT("false"), + (void*)CurrentExStyle, + (void*)NewExStyle, + (void*)StyleAfterSet, + bSetSuccessfully ? TEXT("Yes") : TEXT("No")); + + if (bSetSuccessfully) { + SetWindowPos(GameHWnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE); + } + else { + UE_LOG(LogWindowTransparency, Error, TEXT("EnableClickThrough: Failed to apply desired ExStyle change!")); + } + + + #endif +} + +void UWindowTransparency::SetWIndowsTopmost(bool bSetWindowTopmost) +{ +#if PLATFORM_WINDOWS + if (!GameHWnd) + { + UE_LOG(LogWindowTransparency, Warning, TEXT("SetWIndowsTopmost: Not initialized or HWND is null.")); + return; + } + LONG_PTR CurrentExStyle = GetWindowLongPtr(GameHWnd, GWL_EXSTYLE); + + HWND HwndInsertAfter = bSetWindowTopmost ? HWND_TOPMOST : HWND_NOTOPMOST; + SetWindowPos(GameHWnd, HwndInsertAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + UE_LOG(LogWindowTransparency, Log, TEXT("Window topmost set to: %s"), bSetWindowTopmost ? TEXT("true") : TEXT("false")); + +#endif +} + + + + +void UWindowTransparency::Tick(float DeltaTime) +{ + + if (!bIsClickThroughStateOS) + { + return; + } + + UpdateHitDetectionLogic(DeltaTime); + + bool bShouldBeClickThroughLogically = !bIsMouseOverOpaqueAreaLogic; + + if (bIsClickThroughStateOS != bShouldBeClickThroughLogically) + { + UE_LOG(LogWindowTransparency, Verbose, TEXT("Tick: Logic dictates click-through: %s. OS state is: %s. Updating OS state."), + bShouldBeClickThroughLogically ? TEXT("true") : TEXT("false"), + bIsClickThroughStateOS ? TEXT("true") : TEXT("false")); + EnableClickThrough(bShouldBeClickThroughLogically); + } + +} + +TStatId UWindowTransparency::GetStatId() const +{ + RETURN_QUICK_DECLARE_CYCLE_STAT(UWindowTransparency, STATGROUP_Tickables); +} + +void UWindowTransparency::UpdateHitDetectionLogic(float DeltaTime) +{ +#if PLATFORM_WINDOWS + bool bMousePosSuccess; + FVector2D MousePosInWindow = GetMousePositionInWindow(bMousePosSuccess); + + if (!bMousePosSuccess) + { + bIsMouseOverOpaqueAreaLogic = false; + UE_LOG(LogWindowTransparency, Verbose, TEXT("UpdateHitDetectionLogic: Mouse position not retrieved. Assuming transparent area.")); + return; + } + + bIsMouseOverOpaqueAreaLogic = SimulateGameRaycastUnderMouse(MousePosInWindow); + // const UEnum* EnumPtr = StaticEnum(); + // FString ChannelName = EnumPtr ? EnumPtr->GetNameStringByValue(static_cast(GameRaycastTraceChannelLogic)) : FString::FromInt(static_cast(GameRaycastTraceChannelLogic)); + // UE_LOG(LogWindowTransparency, Log, TEXT("GameRaycastTest Result: bIsMouseOverOpaqueAreaLogic = %s at Pos: %s (Channel: %s)"), + // bIsMouseOverOpaqueAreaLogic ? TEXT("true (Opaque)") : TEXT("false (Transparent)"), *MousePosInWindow.ToString(), *ChannelName); +#endif +} + +FVector2D UWindowTransparency::GetMousePositionInWindow(bool& bMousePosSuccess) +{ + bMousePosSuccess = false; +#if PLATFORM_WINDOWS + + POINT CursorPosScreen; + if (::GetCursorPos(&CursorPosScreen)) + { + RECT WindowRect; + if (::GetWindowRect(GameHWnd, &WindowRect)) + { + bMousePosSuccess = true; + return FVector2D(static_cast(CursorPosScreen.x - WindowRect.left), static_cast(CursorPosScreen.y - WindowRect.top)); + } + else + { + DWORD ErrorCode = GetLastError(); + UE_LOG(LogWindowTransparency, Error, TEXT("GetMousePositionInWindow: GetWindowRect failed for HWND %p. Error code: %u"), GameHWnd, ErrorCode); + + } + } +#endif + return FVector2D::ZeroVector; +} + +bool UWindowTransparency::SimulateGameRaycastUnderMouse(FVector2D MousePos) +{ + + UWorld* World = GetWorld(); + APlayerController* PC = nullptr; + if (World) + { + PC = Cast(GetWorld()->GetFirstLocalPlayerFromController()); + } + if (!PC) + { + return false; + } + //检测3D世界是否有碰撞 + FHitResult HitResult3D; + FCollisionQueryParams CollisionParams3D(SCENE_QUERY_STAT(WindowTransparencyRaycast3D), true); + + bool bHit3D = PC->GetHitResultAtScreenPosition( + MousePos, + ECC_Visibility, + CollisionParams3D, + HitResult3D + ); + + if (bHit3D && HitResult3D.GetActor()) + { + // AActor* HitActor = HitResult3D.GetActor(); + // const UEnum* EnumPtr = StaticEnum(); + // FString ChannelName = EnumPtr ? EnumPtr->GetNameStringByValue(static_cast(this->GameRaycastTraceChannelLogic)) : FString::FromInt(static_cast(this->GameRaycastTraceChannelLogic)); + // UE_LOG(LogWindowHelper, Verbose, TEXT("GameRaycastTest: Hit 3D Actor: %s (Component: %s) using Channel %s"), + // *HitActor->GetName(), + // HitResult3D.GetComponent() ? *HitResult3D.GetComponent()->GetName() : TEXT("None"), + // *ChannelName); + return true; + } + //2D检测是否有可点击UI + if (FSlateApplication::IsInitialized() && GEngine && GEngine->GameViewport) + { + TSharedPtr GameSWindow = GEngine->GameViewport->GetWindow(); + if (GameSWindow.IsValid()) + { + TArray> WindowsToSearch; + WindowsToSearch.Add(GameSWindow.ToSharedRef()); + + FVector2D MousePosScreen = MousePos + GEngine->GameViewport->GetGameViewportWidget()->GetCachedGeometry().GetAbsolutePosition(); + FWidgetPath WidgetPath = FSlateApplication::Get().LocateWindowUnderMouse( + MousePosScreen, + WindowsToSearch, + false, /*bAllowDisabledWidgets*/ + PC->GetLocalPlayer() ? PC->GetLocalPlayer()->GetControllerId() : 0 + ); + + if (WidgetPath.IsValid() && WidgetPath.Widgets.Num() > 0) + { + const FArrangedWidget& LastWidgetArranged = WidgetPath.Widgets.Last(); + TSharedRef HitWidget = LastWidgetArranged.Widget; + FString WidgetType = HitWidget->GetTypeAsString(); + FString WidgetDesc = HitWidget->ToString(); + + UE_LOG(LogWindowTransparency, Verbose, TEXT("GameRaycastTest: UI Hit Candidate: Type: %s, Desc: %s, Visible: %d, Enabled: %d, PathLen: %d"), + *WidgetType, *WidgetDesc, HitWidget->GetVisibility().IsVisible(), HitWidget->IsEnabled(), WidgetPath.Widgets.Num()); + + if (HitWidget->GetVisibility().IsVisible() && HitWidget->IsEnabled()) + { + if (WidgetType == TEXT("SWindow") || + WidgetType == TEXT("SGameLayerManager") || + WidgetType == TEXT("SViewport") || + (WidgetType == TEXT("SBorder") && WidgetPath.Widgets.Num() <= 2) || + (WidgetType == TEXT("SOverlay") && WidgetPath.Widgets.Num() <= 2) || + (WidgetType == TEXT("SScaleBox") && WidgetPath.Widgets.Num() <= 2) || + (WidgetType == TEXT("SCanvasPanel") && WidgetPath.Widgets.Num() <= 2) || + (WidgetType == TEXT("SObjectWidget") && WidgetPath.Widgets.Num() <= 1) + ) + { + UE_LOG(LogWindowTransparency, Verbose, TEXT("GameRaycastTest: UI Hit on %s (%s), but considering it non-blocking/transparent due to type/context."), *WidgetType, *WidgetDesc); + } + else + { + UE_LOG(LogWindowTransparency, Verbose, TEXT("GameRaycastTest: Hit blocking UI Widget: %s (%s)"), *WidgetType, *WidgetDesc); + return true; + } + } + } + } + } + // const UEnum* EnumPtr = StaticEnum(); + // FString ChannelName = EnumPtr ? EnumPtr->GetNameStringByValue(static_cast(this->GameRaycastTraceChannelLogic)) : FString::FromInt(static_cast(this->GameRaycastTraceChannelLogic)); + // UE_LOG(LogWindowTransparency, Verbose, TEXT("GameRaycastTest: No blocking hit found on 3D (using Channel %s) or UI. Assuming transparent."), *ChannelName); + return false; +} + +#undef LOCTEXT_NAMESPACE diff --git a/ProjectFish/Plugins/DeskMode/Source/DeskMode/Public/DeskMode.h b/ProjectFish/Plugins/DeskMode/Source/DeskMode/Public/DeskMode.h new file mode 100644 index 0000000..fbcd789 --- /dev/null +++ b/ProjectFish/Plugins/DeskMode/Source/DeskMode/Public/DeskMode.h @@ -0,0 +1,19 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "Modules/ModuleManager.h" + +class FDeskModeModule : public IModuleInterface +{ +public: + + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; + void OnModularFeatureRegistered(const FName& Name, IModularFeature* ModularFeature); + + static TObjectPtr GetWindowTransparency(); +private: + TObjectPtr WindowTransparency; +}; diff --git a/ProjectFish/Plugins/DeskMode/Source/DeskMode/Public/DeskModeFunctionLibrary.h b/ProjectFish/Plugins/DeskMode/Source/DeskMode/Public/DeskModeFunctionLibrary.h new file mode 100644 index 0000000..ae214bf --- /dev/null +++ b/ProjectFish/Plugins/DeskMode/Source/DeskMode/Public/DeskModeFunctionLibrary.h @@ -0,0 +1,20 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "DeskModeFunctionLibrary.generated.h" + +/** + * + */ +UCLASS() +class DESKMODE_API UDeskModeFunctionLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintCallable, Category = "DeskMode") + static void SetWindowFeatures(bool bEnableWindowTransparency, bool bEnableBorderless, bool bEnableClickThrough, bool bSetWindowTopmost); +}; diff --git a/ProjectFish/Plugins/DeskMode/Source/DeskMode/Public/WindowTransparency.h b/ProjectFish/Plugins/DeskMode/Source/DeskMode/Public/WindowTransparency.h new file mode 100644 index 0000000..2a4196c --- /dev/null +++ b/ProjectFish/Plugins/DeskMode/Source/DeskMode/Public/WindowTransparency.h @@ -0,0 +1,54 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#if PLATFORM_WINDOWS +#include "Windows/AllowWindowsPlatformTypes.h" +#include +#include +#include "Windows/HideWindowsPlatformTypes.h" +#endif + +#include "CoreMinimal.h" +#include "UObject/Object.h" +#include "WindowTransparency.generated.h" + +/** + * + */ +UCLASS() +class DESKMODE_API UWindowTransparency : public UObject, public FTickableGameObject +{ + GENERATED_BODY() +public: + HWND GetGameHWnd(); + bool Initialize(); + void RestoreDefaultWindowSettings(); + //设置窗口无边框 + void EnableBorderless(bool bEnableWindowTransparency); + //设置窗口透明 + void EnableWindowTransparency(bool bEnableWindowTransparency); + //设置窗口是否点击穿透 + void EnableClickThrough(bool bEnableClickThrough); + //设置窗口顶层 + void SetWIndowsTopmost(bool bSetWindowTopmost); + FVector2D GetMousePositionInWindow(bool& bMousePosSuccess); + bool SimulateGameRaycastUnderMouse(FVector2D MousePos); + //更新鼠标位置逻辑判断 + void UpdateHitDetectionLogic(float DeltaTime); + //////////Tick//////////////// + virtual void Tick(float DeltaTime) override; + virtual ETickableTickType GetTickableTickType() const override { return ETickableTickType::Always; } + virtual TStatId GetStatId() const override; + virtual bool IsTickableWhenPaused() const override { return true; } + virtual bool IsTickableInEditor() const override { return false; } +private: +#if PLATFORM_WINDOWS + HWND GameHWnd; + LONG_PTR OriginalWindowStyle; + LONG_PTR OriginalExWindowStyle; +#endif + bool bIsMouseOverOpaqueAreaLogic;; //鼠标是否经过程序的可点击区域 + bool bIsClickThroughStateOS; //当前的点击穿透状态 +}; + diff --git a/ProjectFish/ProjectFish.sln.DotSettings.user b/ProjectFish/ProjectFish.sln.DotSettings.user index a5e292d..8b0af20 100644 --- a/ProjectFish/ProjectFish.sln.DotSettings.user +++ b/ProjectFish/ProjectFish.sln.DotSettings.user @@ -24,11 +24,13 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded @@ -38,6 +40,7 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded