Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ac1a5ee60 | ||
|
|
15a98cd774 | ||
|
|
06bb971a42 | ||
|
|
217e63d40c | ||
|
|
9e42fb0abf | ||
|
|
9de5064fbe |
@@ -1,12 +1,37 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class MultipleMonitorCreator : MonoBehaviour
|
||||
{
|
||||
[SerializeField] GameObject monitorPrefab;
|
||||
[SerializeField] float width = 0.3f;
|
||||
[SerializeField] float scale = 1f;
|
||||
[SerializeField] float margin = 1f;
|
||||
|
||||
private List<GameObject> monitors_ = new List<GameObject>();
|
||||
private float totalWidth_ = 0f;
|
||||
|
||||
void Start()
|
||||
{
|
||||
Create();
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
uDesktopDuplication.Manager.onReinitialized += Recreate;
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
uDesktopDuplication.Manager.onReinitialized -= Recreate;
|
||||
}
|
||||
|
||||
void Create()
|
||||
{
|
||||
CreateMonitors();
|
||||
LayoutMonitors();
|
||||
}
|
||||
|
||||
void CreateMonitors()
|
||||
{
|
||||
// Sort monitors in coordinate order
|
||||
var monitors = uDesktopDuplication.Manager.monitors;
|
||||
@@ -14,44 +39,58 @@ public class MultipleMonitorCreator : MonoBehaviour
|
||||
|
||||
// Create monitors
|
||||
var n = monitors.Count;
|
||||
var totalWidth = 0f;
|
||||
totalWidth_ = 0f;
|
||||
for (int i = 0 ; i < n; ++i) {
|
||||
// Create monitor obeject
|
||||
var go = Instantiate(monitorPrefab);
|
||||
go.name = "Monitor " + i;
|
||||
monitors_.Add(go);
|
||||
|
||||
// Assign monitor
|
||||
var texture = go.GetComponent<uDesktopDuplication.Texture>();
|
||||
texture.monitorId = i;
|
||||
var monitor = texture.monitor;
|
||||
|
||||
// Set width / height
|
||||
var isHorizontal = texture.monitor.isHorizontal;
|
||||
var w = isHorizontal ? width : (width * texture.monitor.aspect);
|
||||
var h = isHorizontal ? width / texture.monitor.aspect : width;
|
||||
go.transform.localScale = new Vector3(w, go.transform.localScale.y, h);
|
||||
go.transform.localScale = new Vector3(monitor.widthMeter, go.transform.localScale.y, monitor.heightMeter);
|
||||
|
||||
// Set parent as this object
|
||||
go.transform.SetParent(transform);
|
||||
|
||||
// Calc actual size considering mesh size
|
||||
var scaleX = go.GetComponent<MeshFilter>().sharedMesh.bounds.extents.x * 2f;
|
||||
totalWidth += w * scaleX;
|
||||
totalWidth_ += monitor.widthMeter * scaleX;
|
||||
}
|
||||
}
|
||||
|
||||
void LayoutMonitors()
|
||||
{
|
||||
// Set positions with margin
|
||||
totalWidth += margin * (n - 1);
|
||||
var x = -totalWidth / 2;
|
||||
for (int i = 0 ; i < n; ++i) {
|
||||
//
|
||||
var go = transform.FindChild("Monitor " + i);
|
||||
var texture = go.GetComponent<uDesktopDuplication.Texture>();
|
||||
totalWidth_ += margin * (monitors_.Count - 1);
|
||||
var x = -totalWidth_ / 2;
|
||||
foreach (var go in monitors_) {
|
||||
var monitor = go.GetComponent<uDesktopDuplication.Texture>().monitor;
|
||||
var halfScaleX = go.GetComponent<MeshFilter>().sharedMesh.bounds.extents.x;
|
||||
var w = texture.monitor.isHorizontal ? width : (width * texture.monitor.aspect);
|
||||
var halfWidth = w * halfScaleX;
|
||||
var width = monitor.widthMeter;
|
||||
var halfWidth = width * halfScaleX;
|
||||
x += halfWidth;
|
||||
go.transform.localPosition = new Vector3(x, 0f, 0f);
|
||||
x += halfWidth + margin;
|
||||
}
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
foreach (var go in monitors_) {
|
||||
Destroy(go);
|
||||
}
|
||||
monitors_.Clear();
|
||||
}
|
||||
|
||||
void Recreate()
|
||||
{
|
||||
Clear();
|
||||
Create();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
@@ -54,14 +54,14 @@ public class Cursor : MonoBehaviour
|
||||
}
|
||||
|
||||
var cursorTexture = textures_[scale];
|
||||
monitor.UpdateCursorTexture(cursorTexture.GetNativeTexturePtr());
|
||||
monitor.GetCursorTexture(cursorTexture.GetNativeTexturePtr());
|
||||
uddTexture_.material.SetTexture("_CursorTex", cursorTexture);
|
||||
}
|
||||
|
||||
void UpdateMaterial()
|
||||
{
|
||||
var x = (float)monitor.cursorX / monitor.width;
|
||||
var y = (float)monitor.cursorY / monitor.height;
|
||||
var x = monitor.isCursorVisible ? (float)monitor.cursorX / monitor.width : -9999f;
|
||||
var y = monitor.isCursorVisible ? (float)monitor.cursorY / monitor.height : -9999f;
|
||||
var w = (float)monitor.cursorShapeWidth / monitor.width;
|
||||
var h = (float)monitor.cursorShapeHeight / monitor.height;
|
||||
uddTexture_.material.SetVector("_CursorPositionScale", new Vector4(x, y, w, h));
|
||||
|
||||
@@ -27,6 +27,18 @@ public enum MonitorRotation
|
||||
Rotate270 = 4
|
||||
}
|
||||
|
||||
public enum MonitorState
|
||||
{
|
||||
NotSet = -1,
|
||||
Available = 0,
|
||||
InvalidArg = 1,
|
||||
AccessDenied = 2,
|
||||
Unsupported = 3,
|
||||
CurrentlyNotAvailable = 4,
|
||||
SessionDisconnected = 5,
|
||||
AccessLost = 6,
|
||||
}
|
||||
|
||||
public static class Lib
|
||||
{
|
||||
public delegate void MessageHandler(Message message);
|
||||
@@ -36,6 +48,8 @@ public static class Lib
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void FinalizeUDD();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void Reinitialize();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void Update();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern Message PopMessage();
|
||||
@@ -50,7 +64,7 @@ public static class Lib
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern IntPtr GetRenderEventFunc();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern bool IsAvailable(int id);
|
||||
public static extern MonitorState GetState(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void GetName(int id, StringBuilder buf, int len);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
@@ -66,6 +80,10 @@ public static class Lib
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetHeight(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetDpiX(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetDpiY(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern MonitorRotation GetRotation(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern bool IsPrimary(int id);
|
||||
@@ -84,7 +102,7 @@ public static class Lib
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern CursorShapeType GetCursorShapeType(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void UpdateCursorTexture(int id, System.IntPtr ptr);
|
||||
public static extern void GetCursorTexture(int id, System.IntPtr ptr);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int SetTexturePtr(int id, IntPtr ptr);
|
||||
|
||||
|
||||
@@ -12,7 +12,16 @@ public class Manager : MonoBehaviour
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance_) return instance_;
|
||||
if (instance_) {
|
||||
return instance_;
|
||||
}
|
||||
|
||||
var manager = FindObjectOfType<Manager>();
|
||||
if (manager) {
|
||||
manager.Awake();
|
||||
return manager;
|
||||
}
|
||||
|
||||
var go = new GameObject("uDesktopDuplicationManager");
|
||||
return go.AddComponent<Manager>();
|
||||
}
|
||||
@@ -37,10 +46,15 @@ public class Manager : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField, Tooltip("Set Desktop Duplication API timeout (milliseconds).")]
|
||||
int timeout = 0;
|
||||
[SerializeField] int desktopDuplicationApiTimeout = 0;
|
||||
[SerializeField] float retryReinitializationDuration = 1f;
|
||||
|
||||
private Coroutine renderCoroutine_ = null;
|
||||
private bool shouldReinitialize = false;
|
||||
private float reinitializationTimer = 0f;
|
||||
|
||||
public delegate void ReinitializeHandler();
|
||||
public static event ReinitializeHandler onReinitialized;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
@@ -51,7 +65,7 @@ public class Manager : MonoBehaviour
|
||||
|
||||
CreateMonitors();
|
||||
|
||||
Lib.SetTimeout(timeout);
|
||||
Lib.SetTimeout(desktopDuplicationApiTimeout);
|
||||
}
|
||||
|
||||
void OnApplicationQuit()
|
||||
@@ -75,13 +89,56 @@ public class Manager : MonoBehaviour
|
||||
void Update()
|
||||
{
|
||||
Lib.Update();
|
||||
ReinitializeIfNeeded();
|
||||
UpdateMessage();
|
||||
|
||||
/*
|
||||
foreach (var monitor in monitors_) {
|
||||
Debug.LogFormat("[{0}] {1}", monitor.id, monitor.state);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
[ContextMenu("Reinitialize")]
|
||||
void Reinitialize()
|
||||
{
|
||||
Debug.Log("[uDesktopDuplication] Reinitialize");
|
||||
Lib.Reinitialize();
|
||||
if (onReinitialized != null) onReinitialized();
|
||||
}
|
||||
|
||||
void ReinitializeIfNeeded()
|
||||
{
|
||||
for (int i = 0; i < monitors.Count; ++i) {
|
||||
var monitor = monitors[i];
|
||||
if (monitor.state == MonitorState.NotSet ||
|
||||
monitor.state == MonitorState.AccessLost ||
|
||||
monitor.state == MonitorState.AccessDenied ||
|
||||
monitor.state == MonitorState.SessionDisconnected) {
|
||||
if (!shouldReinitialize) {
|
||||
shouldReinitialize = true;
|
||||
reinitializationTimer = 0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldReinitialize) {
|
||||
if (reinitializationTimer > retryReinitializationDuration) {
|
||||
Reinitialize();
|
||||
shouldReinitialize = false;
|
||||
}
|
||||
reinitializationTimer += Time.deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateMessage()
|
||||
{
|
||||
var message = Lib.PopMessage();
|
||||
while (message != Message.None) {
|
||||
switch (message) {
|
||||
case Message.Reinitialized:
|
||||
Debug.Log("Reinitialize");
|
||||
Reinitialize();
|
||||
ReinitializeMonitors();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -111,7 +168,7 @@ public class Manager : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
void Reinitialize()
|
||||
void ReinitializeMonitors()
|
||||
{
|
||||
for (int i = 0; i < monitorCount; ++i) {
|
||||
if (i == monitors.Count) {
|
||||
|
||||
@@ -8,6 +8,27 @@ public class Monitor
|
||||
public Monitor(int id)
|
||||
{
|
||||
this.id = id;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case MonitorState.InvalidArg:
|
||||
Debug.LogErrorFormat("[{0}:{1}] Invalid.", id, name);
|
||||
break;
|
||||
case MonitorState.AccessDenied:
|
||||
Debug.LogErrorFormat("[{0}:{1}] Access Denied.", id, name);
|
||||
break;
|
||||
case MonitorState.Unsupported:
|
||||
Debug.LogErrorFormat("[{0}:{1}] Unsupported.", id, name);
|
||||
break;
|
||||
case MonitorState.SessionDisconnected:
|
||||
Debug.LogErrorFormat("[{0}:{1}] Disconnected.", id, name);
|
||||
break;
|
||||
case MonitorState.NotSet:
|
||||
Debug.LogErrorFormat("[{0}:{1}] Something wrong.", id, name);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public int id
|
||||
@@ -21,9 +42,14 @@ public class Monitor
|
||||
get { return id < Manager.monitorCount; }
|
||||
}
|
||||
|
||||
public MonitorState state
|
||||
{
|
||||
get { return Lib.GetState(id); }
|
||||
}
|
||||
|
||||
public bool available
|
||||
{
|
||||
get { return Lib.IsAvailable(id); }
|
||||
get { return state == MonitorState.Available; }
|
||||
}
|
||||
|
||||
public string name
|
||||
@@ -66,6 +92,26 @@ public class Monitor
|
||||
get { return Lib.GetHeight(id); }
|
||||
}
|
||||
|
||||
public int dpiX
|
||||
{
|
||||
get { return Lib.GetDpiX(id); }
|
||||
}
|
||||
|
||||
public int dpiY
|
||||
{
|
||||
get { return Lib.GetDpiY(id); }
|
||||
}
|
||||
|
||||
public float widthMeter
|
||||
{
|
||||
get { return width / dpiX * 0.0254f; }
|
||||
}
|
||||
|
||||
public float heightMeter
|
||||
{
|
||||
get { return height / dpiY * 0.0254f; }
|
||||
}
|
||||
|
||||
public MonitorRotation rotation
|
||||
{
|
||||
get { return Lib.GetRotation(id); }
|
||||
@@ -145,9 +191,9 @@ public class Monitor
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateCursorTexture(System.IntPtr ptr)
|
||||
public void GetCursorTexture(System.IntPtr ptr)
|
||||
{
|
||||
Lib.UpdateCursorTexture(id, ptr);
|
||||
Lib.GetCursorTexture(id, ptr);
|
||||
}
|
||||
|
||||
void CreateTexture()
|
||||
|
||||
@@ -50,7 +50,6 @@ public class Texture : MonoBehaviour
|
||||
|
||||
void Update()
|
||||
{
|
||||
Debug.Log(monitor.available);
|
||||
monitor.shouldBeUpdated = true;
|
||||
UpdateMaterial();
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ Properties
|
||||
_Color ("Color", Color) = (1, 1, 1, 1)
|
||||
_MainTex ("Texture", 2D) = "white" {}
|
||||
[Toggle(USE_BEND)] _UseBend("UseBend", Float) = 0
|
||||
[PowerSlider(10.0)]_Radius ("Radius", Range(3, 100)) = 10
|
||||
[PowerSlider(10.0)]_Radius ("Radius", Range(10, 100)) = 30
|
||||
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ const std::unique_ptr<MonitorManager>& GetMonitorManager();
|
||||
|
||||
enum class Message
|
||||
{
|
||||
None = -1,
|
||||
Reinitialized = 0,
|
||||
None = -1,
|
||||
Reinitialized = 0,
|
||||
};
|
||||
void SendMessageToUnity(Message message);
|
||||
@@ -26,19 +26,23 @@ Cursor::~Cursor()
|
||||
}
|
||||
|
||||
|
||||
void Cursor::Update(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
|
||||
void Cursor::UpdateBuffer(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
|
||||
{
|
||||
isVisible_ = frameInfo.PointerPosition.Visible != 0;
|
||||
x_ = frameInfo.PointerPosition.Position.x;
|
||||
y_ = frameInfo.PointerPosition.Position.y;
|
||||
|
||||
// TODO: see more information to check where the cursor is.
|
||||
if (isVisible_)
|
||||
if (frameInfo.LastMouseUpdateTime.QuadPart == 0)
|
||||
{
|
||||
GetMonitorManager()->SetCursorMonitorId(monitor_->GetId());
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetMonitorManager()->GetCursorMonitorId() != monitor_->GetId()) {
|
||||
x_ = frameInfo.PointerPosition.Position.x;
|
||||
y_ = frameInfo.PointerPosition.Position.y;
|
||||
if (frameInfo.PointerPosition.Visible != 0) {
|
||||
GetMonitorManager()->SetCursorMonitorId(monitor_->GetId());
|
||||
timestamp_ = frameInfo.LastMouseUpdateTime;
|
||||
isVisible_ = frameInfo.PointerPosition.Visible != 0;
|
||||
}
|
||||
|
||||
if (frameInfo.PointerShapeBufferSize == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -51,29 +55,35 @@ void Cursor::Update(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
|
||||
}
|
||||
if (apiBuffer_ == nullptr) return;
|
||||
|
||||
// Get information about the mouse pointer if needed
|
||||
if (frameInfo.PointerShapeBufferSize != 0)
|
||||
// Get mouse pointer information
|
||||
UINT bufferSize;
|
||||
const auto hr = monitor_->GetDeskDupl()->GetFramePointerShape(
|
||||
frameInfo.PointerShapeBufferSize,
|
||||
reinterpret_cast<void*>(apiBuffer_),
|
||||
&bufferSize,
|
||||
&shapeInfo_);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
UINT bufferSize;
|
||||
monitor_->GetDeskDupl()->GetFramePointerShape(
|
||||
frameInfo.PointerShapeBufferSize,
|
||||
reinterpret_cast<void*>(apiBuffer_),
|
||||
&bufferSize,
|
||||
&shapeInfo_);
|
||||
delete[] apiBuffer_;
|
||||
apiBuffer_ = nullptr;
|
||||
apiBufferSize_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Cursor::UpdateTexture()
|
||||
{
|
||||
// cursor type
|
||||
const auto cursorType = shapeInfo_.Type;
|
||||
const bool isMono = cursorType == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME;
|
||||
const bool isColorMask = cursorType == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR;
|
||||
const bool isMono = GetType() == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME;
|
||||
const bool isColorMask = GetType() == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR;
|
||||
|
||||
// Size
|
||||
const auto w = shapeInfo_.Width;
|
||||
const auto h = shapeInfo_.Height / (isMono ? 2 : 1);
|
||||
const auto p = shapeInfo_.Pitch;
|
||||
const auto w = GetWidth();
|
||||
const auto h = GetHeight();
|
||||
const auto p = GetPitch();
|
||||
|
||||
// Convert the buffer given by API into BGRA32
|
||||
const auto bgraBufferSize = w * h * 4;
|
||||
const UINT bgraBufferSize = w * h * 4;
|
||||
if (bgraBufferSize > bgra32BufferSize_)
|
||||
{
|
||||
if (bgra32Buffer_) delete[] bgra32Buffer_;
|
||||
@@ -145,10 +155,10 @@ void Cursor::Update(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
|
||||
|
||||
if (isMono)
|
||||
{
|
||||
for (UINT row = 0; row < h; ++row)
|
||||
for (int row = 0; row < h; ++row)
|
||||
{
|
||||
BYTE mask = 0x80;
|
||||
for (UINT col = 0; col < w; ++col)
|
||||
for (int col = 0; col < w; ++col)
|
||||
{
|
||||
const int i = row * w + col;
|
||||
const BYTE andMask = apiBuffer_[col / 8 + row * p] & mask;
|
||||
@@ -164,9 +174,9 @@ void Cursor::Update(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
|
||||
{
|
||||
const auto buffer32 = reinterpret_cast<UINT*>(apiBuffer_);
|
||||
|
||||
for (UINT row = 0; row < h; ++row)
|
||||
for (int row = 0; row < h; ++row)
|
||||
{
|
||||
for (UINT col = 0; col < w; ++col)
|
||||
for (int col = 0; col < w; ++col)
|
||||
{
|
||||
const int i = row * w + col;
|
||||
const int j = row * p / sizeof(UINT) + col;
|
||||
@@ -195,7 +205,7 @@ void Cursor::Update(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
|
||||
{
|
||||
auto output32 = reinterpret_cast<UINT*>(bgra32Buffer_);
|
||||
const auto buffer32 = reinterpret_cast<UINT*>(apiBuffer_);
|
||||
for (UINT i = 0; i < w * h; ++i)
|
||||
for (int i = 0; i < w * h; ++i)
|
||||
{
|
||||
output32[i] = buffer32[i];
|
||||
}
|
||||
@@ -205,7 +215,7 @@ void Cursor::Update(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
|
||||
}
|
||||
|
||||
|
||||
void Cursor::UpdateTexture(ID3D11Texture2D* texture)
|
||||
void Cursor::GetTexture(ID3D11Texture2D* texture)
|
||||
{
|
||||
if (bgra32Buffer_ == nullptr) return;
|
||||
ID3D11DeviceContext* context;
|
||||
|
||||
@@ -9,8 +9,9 @@ class Cursor
|
||||
public:
|
||||
explicit Cursor(Monitor* monitor);
|
||||
~Cursor();
|
||||
void Update(const DXGI_OUTDUPL_FRAME_INFO& frameInfo);
|
||||
void UpdateTexture(ID3D11Texture2D* texture);
|
||||
void UpdateBuffer(const DXGI_OUTDUPL_FRAME_INFO& frameInfo);
|
||||
void UpdateTexture();
|
||||
void GetTexture(ID3D11Texture2D* texture);
|
||||
|
||||
bool IsVisible() const;
|
||||
int GetX() const;
|
||||
@@ -30,4 +31,5 @@ private:
|
||||
BYTE* bgra32Buffer_ = nullptr;
|
||||
UINT bgra32BufferSize_ = 0;
|
||||
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo_;
|
||||
LARGE_INTEGER timestamp_;
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <d3d11.h>
|
||||
#include <ShellScalingAPI.h>
|
||||
#include "Common.h"
|
||||
#include "Cursor.h"
|
||||
#include "MonitorManager.h"
|
||||
@@ -11,37 +12,44 @@ Monitor::Monitor(int id)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
HRESULT Monitor::Initialize(IDXGIOutput* output)
|
||||
{
|
||||
output->GetDesc(&outputDesc_);
|
||||
monitorInfo_.cbSize = sizeof(MONITORINFOEX);
|
||||
GetMonitorInfo(outputDesc_.Monitor, &monitorInfo_);
|
||||
|
||||
GetDpiForMonitor(outputDesc_.Monitor, MDT_RAW_DPI, &dpiX_, &dpiY_);
|
||||
|
||||
auto output1 = reinterpret_cast<IDXGIOutput1*>(output);
|
||||
const auto hr = output1->DuplicateOutput(GetDevice(), &deskDupl_);
|
||||
|
||||
// TODO: error check
|
||||
// TODO: error check
|
||||
switch (hr)
|
||||
{
|
||||
case S_OK:
|
||||
available_ = true;
|
||||
state_ = State::Available;
|
||||
break;
|
||||
case E_INVALIDARG:
|
||||
state_ = State::InvalidArg;
|
||||
break;
|
||||
case E_ACCESSDENIED:
|
||||
// For example, when the user presses Ctrl + Alt + Delete and the screen
|
||||
// switches to admin screen, this error occurs.
|
||||
// GetMonitorManager()->RequireReinitilization();
|
||||
// For example, when the user presses Ctrl + Alt + Delete and the screen
|
||||
// switches to admin screen, this error occurs.
|
||||
state_ = State::AccessDenied;
|
||||
break;
|
||||
case DXGI_ERROR_UNSUPPORTED:
|
||||
// If the display adapter on the computer is running under the Microsoft Hybrid system,
|
||||
// this error occurs.
|
||||
state_ = State::Unsupported;
|
||||
break;
|
||||
case DXGI_ERROR_NOT_CURRENTLY_AVAILABLE:
|
||||
// When other application use Desktop Duplication API, this error occurs.
|
||||
case DXGI_ERROR_NOT_CURRENTLY_AVAILABLE:
|
||||
// When other application use Desktop Duplication API, this error occurs.
|
||||
state_ = State::CurrentlyNotAvailable;
|
||||
break;
|
||||
case DXGI_ERROR_SESSION_DISCONNECTED:
|
||||
break;
|
||||
state_ = State::SessionDisconnected;
|
||||
break;
|
||||
}
|
||||
|
||||
return hr;
|
||||
@@ -50,21 +58,21 @@ HRESULT Monitor::Initialize(IDXGIOutput* output)
|
||||
|
||||
Monitor::~Monitor()
|
||||
{
|
||||
if (deskDupl_ != nullptr)
|
||||
{
|
||||
deskDupl_->Release();
|
||||
}
|
||||
if (deskDupl_ != nullptr)
|
||||
{
|
||||
deskDupl_->Release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HRESULT Monitor::Render(UINT timeout)
|
||||
{
|
||||
if (deskDupl_ == nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (deskDupl_ == nullptr)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
if (unityTexture_ == nullptr) return 0;
|
||||
if (unityTexture_ == nullptr) return 0;
|
||||
|
||||
IDXGIResource* resource = nullptr;
|
||||
DXGI_OUTDUPL_FRAME_INFO frameInfo;
|
||||
@@ -72,6 +80,20 @@ HRESULT Monitor::Render(UINT timeout)
|
||||
const auto hr = deskDupl_->AcquireNextFrame(timeout, &frameInfo, &resource);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
switch (hr)
|
||||
{
|
||||
case DXGI_ERROR_ACCESS_LOST:
|
||||
// If any monitor setting has changed (e.g. monitor size has changed),
|
||||
// it is necessary to re-initialize monitors.
|
||||
state_ = State::AccessLost;
|
||||
break;
|
||||
case DXGI_ERROR_WAIT_TIMEOUT:
|
||||
break;
|
||||
case DXGI_ERROR_INVALID_CALL:
|
||||
break;
|
||||
case E_INVALIDARG:
|
||||
break;
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
@@ -83,7 +105,8 @@ HRESULT Monitor::Render(UINT timeout)
|
||||
context->CopyResource(unityTexture_, texture);
|
||||
context->Release();
|
||||
|
||||
cursor_->Update(frameInfo);
|
||||
cursor_->UpdateBuffer(frameInfo);
|
||||
cursor_->UpdateTexture();
|
||||
|
||||
resource->Release();
|
||||
deskDupl_->ReleaseFrame();
|
||||
@@ -98,9 +121,9 @@ int Monitor::GetId() const
|
||||
}
|
||||
|
||||
|
||||
bool Monitor::IsAvailable() const
|
||||
MonitorState Monitor::GetState() const
|
||||
{
|
||||
return available_;
|
||||
return state_;
|
||||
}
|
||||
|
||||
|
||||
@@ -128,9 +151,9 @@ const std::unique_ptr<Cursor>& Monitor::GetCursor()
|
||||
}
|
||||
|
||||
|
||||
void Monitor::UpdateCursorTexture(ID3D11Texture2D* texture)
|
||||
void Monitor::GetCursorTexture(ID3D11Texture2D* texture)
|
||||
{
|
||||
cursor_->UpdateTexture(texture);
|
||||
cursor_->GetTexture(texture);
|
||||
}
|
||||
|
||||
|
||||
@@ -148,31 +171,43 @@ bool Monitor::IsPrimary() const
|
||||
|
||||
int Monitor::GetLeft() const
|
||||
{
|
||||
return outputDesc_.DesktopCoordinates.left;
|
||||
return outputDesc_.DesktopCoordinates.left;
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetRight() const
|
||||
{
|
||||
return outputDesc_.DesktopCoordinates.right;
|
||||
return outputDesc_.DesktopCoordinates.right;
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetTop() const
|
||||
{
|
||||
return outputDesc_.DesktopCoordinates.top;
|
||||
return outputDesc_.DesktopCoordinates.top;
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetBottom() const
|
||||
{
|
||||
return outputDesc_.DesktopCoordinates.bottom;
|
||||
return outputDesc_.DesktopCoordinates.bottom;
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetRotation() const
|
||||
{
|
||||
return static_cast<int>(outputDesc_.Rotation);
|
||||
return static_cast<int>(outputDesc_.Rotation);
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetDpiX() const
|
||||
{
|
||||
return static_cast<int>(dpiX_);
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetDpiY() const
|
||||
{
|
||||
return static_cast<int>(dpiY_);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,35 +4,52 @@
|
||||
|
||||
class Cursor;
|
||||
|
||||
enum class MonitorState
|
||||
{
|
||||
NotSet = -1,
|
||||
Available = 0,
|
||||
InvalidArg = 1,
|
||||
AccessDenied = 2,
|
||||
Unsupported = 3,
|
||||
CurrentlyNotAvailable = 4,
|
||||
SessionDisconnected = 5,
|
||||
AccessLost = 6,
|
||||
};
|
||||
|
||||
class Monitor
|
||||
{
|
||||
public:
|
||||
using State = MonitorState;
|
||||
|
||||
explicit Monitor(int id);
|
||||
~Monitor();
|
||||
HRESULT Initialize(IDXGIOutput* output);
|
||||
HRESULT Render(UINT timeout = 0);
|
||||
void UpdateCursorTexture(ID3D11Texture2D* texture);
|
||||
void GetCursorTexture(ID3D11Texture2D* texture);
|
||||
|
||||
public:
|
||||
int GetId() const;
|
||||
bool IsAvailable() const;
|
||||
State GetState() const;
|
||||
void SetUnityTexture(ID3D11Texture2D* texture);
|
||||
ID3D11Texture2D* GetUnityTexture() const;
|
||||
void GetName(char* buf, int len) const;
|
||||
bool IsPrimary() const;
|
||||
int GetLeft() const;
|
||||
int GetRight() const;
|
||||
int GetTop() const;
|
||||
int GetBottom() const;
|
||||
int GetLeft() const;
|
||||
int GetRight() const;
|
||||
int GetTop() const;
|
||||
int GetBottom() const;
|
||||
int GetWidth() const;
|
||||
int GetHeight() const;
|
||||
int GetRotation() const;
|
||||
int GetRotation() const;
|
||||
int GetDpiX() const;
|
||||
int GetDpiY() const;
|
||||
IDXGIOutputDuplication* GetDeskDupl();
|
||||
const std::unique_ptr<Cursor>& GetCursor();
|
||||
|
||||
private:
|
||||
int id_ = -1;
|
||||
bool available_ = false;
|
||||
UINT dpiX_ = -1, dpiY_ = -1;
|
||||
State state_ = State::NotSet;
|
||||
std::unique_ptr<Cursor> cursor_;
|
||||
IDXGIOutputDuplication* deskDupl_ = nullptr;
|
||||
ID3D11Texture2D* unityTexture_ = nullptr;
|
||||
|
||||
@@ -59,10 +59,16 @@ void MonitorManager::Finalize()
|
||||
}
|
||||
|
||||
|
||||
void MonitorManager::RequireReinitilization()
|
||||
{
|
||||
isReinitializationRequired_ = true;
|
||||
}
|
||||
|
||||
|
||||
void MonitorManager::Reinitialize()
|
||||
{
|
||||
Initialize();
|
||||
SendMessageToUnity(Message::Reinitialized);
|
||||
Initialize();
|
||||
SendMessageToUnity(Message::Reinitialized);
|
||||
}
|
||||
|
||||
|
||||
@@ -78,28 +84,10 @@ std::shared_ptr<Monitor> MonitorManager::GetMonitor(int id) const
|
||||
|
||||
void MonitorManager::Update()
|
||||
{
|
||||
if (isReinitializationRequired_)
|
||||
{
|
||||
Reinitialize();
|
||||
isReinitializationRequired_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MonitorManager::OnRender(int id)
|
||||
{
|
||||
if (auto monitor = GetMonitor(id))
|
||||
if (isReinitializationRequired_)
|
||||
{
|
||||
if (!monitor->IsAvailable()) return;
|
||||
|
||||
const auto hr = monitor->Render(timeout_);
|
||||
|
||||
// If any monitor setting has changed (e.g. monitor size has changed),
|
||||
// it is necessary to re-initialize monitors.
|
||||
if (hr == DXGI_ERROR_ACCESS_LOST)
|
||||
{
|
||||
isReinitializationRequired_ = true;
|
||||
}
|
||||
Reinitialize();
|
||||
isReinitializationRequired_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,26 +111,26 @@ int MonitorManager::GetMonitorCount() const
|
||||
|
||||
int MonitorManager::GetTotalWidth() const
|
||||
{
|
||||
std::vector<int> lefts, rights;
|
||||
for (const auto& monitor : monitors_)
|
||||
{
|
||||
lefts.push_back(monitor->GetLeft());
|
||||
rights.push_back(monitor->GetRight());
|
||||
}
|
||||
const auto minLeft = *std::min_element(lefts.begin(), lefts.end());
|
||||
const auto maxRight = *std::max_element(rights.begin(), rights.end());
|
||||
return maxRight - minLeft;
|
||||
std::vector<int> lefts, rights;
|
||||
for (const auto& monitor : monitors_)
|
||||
{
|
||||
lefts.push_back(monitor->GetLeft());
|
||||
rights.push_back(monitor->GetRight());
|
||||
}
|
||||
const auto minLeft = *std::min_element(lefts.begin(), lefts.end());
|
||||
const auto maxRight = *std::max_element(rights.begin(), rights.end());
|
||||
return maxRight - minLeft;
|
||||
}
|
||||
|
||||
int MonitorManager::GetTotalHeight() const
|
||||
{
|
||||
std::vector<int> tops, bottoms;
|
||||
for (const auto& monitor : monitors_)
|
||||
{
|
||||
tops.push_back(monitor->GetTop());
|
||||
bottoms.push_back(monitor->GetBottom());
|
||||
}
|
||||
const auto minTop = *std::min_element(tops.begin(), tops.end());
|
||||
const auto maxBottom = *std::max_element(bottoms.begin(), bottoms.end());
|
||||
return maxBottom - minTop;
|
||||
std::vector<int> tops, bottoms;
|
||||
for (const auto& monitor : monitors_)
|
||||
{
|
||||
tops.push_back(monitor->GetTop());
|
||||
bottoms.push_back(monitor->GetBottom());
|
||||
}
|
||||
const auto minTop = *std::min_element(tops.begin(), tops.end());
|
||||
const auto maxBottom = *std::max_element(bottoms.begin(), bottoms.end());
|
||||
return maxBottom - minTop;
|
||||
}
|
||||
@@ -13,9 +13,8 @@ class MonitorManager
|
||||
public:
|
||||
explicit MonitorManager();
|
||||
~MonitorManager();
|
||||
|
||||
void RequireReinitilization() { isReinitializationRequired_ = true; }
|
||||
|
||||
void Reinitialize();
|
||||
void RequireReinitilization();
|
||||
void SetCursorMonitorId(int id) { cursorMonitorId_ = id; }
|
||||
int GetCursorMonitorId() const { return cursorMonitorId_; }
|
||||
std::shared_ptr<Monitor> GetMonitor(int id) const;
|
||||
@@ -23,12 +22,10 @@ public:
|
||||
private:
|
||||
void Initialize();
|
||||
void Finalize();
|
||||
void Reinitialize();
|
||||
|
||||
// Setters from Unity
|
||||
public:
|
||||
void OnRender(int id);
|
||||
void Update();
|
||||
void Update();
|
||||
void SetTimeout(int timeout);
|
||||
int GetTimeout() const;
|
||||
|
||||
@@ -43,5 +40,5 @@ private:
|
||||
std::vector<std::shared_ptr<Monitor>> monitors_;
|
||||
std::shared_ptr<Cursor> cursor_;
|
||||
int cursorMonitorId_ = -1;
|
||||
bool isReinitializationRequired_ = false;
|
||||
bool isReinitializationRequired_ = false;
|
||||
};
|
||||
@@ -15,13 +15,14 @@
|
||||
#include "MonitorManager.h"
|
||||
|
||||
#pragma comment(lib, "dxgi.lib")
|
||||
#pragma comment(lib, "Shcore.lib")
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
IUnityInterfaces* g_unity = nullptr;
|
||||
std::unique_ptr<MonitorManager> g_manager;
|
||||
std::queue<Message> g_messages;
|
||||
std::queue<Message> g_messages;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +46,7 @@ const std::unique_ptr<MonitorManager>& GetMonitorManager()
|
||||
|
||||
void SendMessageToUnity(Message message)
|
||||
{
|
||||
g_messages.push(message);
|
||||
g_messages.push(message);
|
||||
}
|
||||
|
||||
|
||||
@@ -53,33 +54,36 @@ extern "C"
|
||||
{
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API InitializeUDD()
|
||||
{
|
||||
if (g_unity && !g_manager)
|
||||
{
|
||||
g_manager = std::make_unique<MonitorManager>();
|
||||
}
|
||||
if (g_unity && !g_manager)
|
||||
{
|
||||
g_manager = std::make_unique<MonitorManager>();
|
||||
}
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API FinalizeUDD()
|
||||
{
|
||||
g_manager.reset();
|
||||
g_manager.reset();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces)
|
||||
{
|
||||
g_unity = unityInterfaces;
|
||||
InitializeUDD();
|
||||
InitializeUDD();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UnityPluginUnload()
|
||||
{
|
||||
g_unity = nullptr;
|
||||
FinalizeUDD();
|
||||
g_unity = nullptr;
|
||||
FinalizeUDD();
|
||||
}
|
||||
|
||||
void UNITY_INTERFACE_API OnRenderEvent(int id)
|
||||
{
|
||||
if (!g_manager) return;
|
||||
g_manager->OnRender(id);
|
||||
if (!g_manager) return;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
monitor->Render(g_manager->GetTimeout());
|
||||
}
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT UnityRenderingEvent UNITY_INTERFACE_API GetRenderEventFunc()
|
||||
@@ -87,58 +91,64 @@ extern "C"
|
||||
return OnRenderEvent;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API Update()
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API Reinitialize()
|
||||
{
|
||||
if (!g_manager) return;
|
||||
g_manager->Update();
|
||||
if (!g_manager) return;
|
||||
return g_manager->Reinitialize();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT Message PopMessage()
|
||||
{
|
||||
if (g_messages.empty()) return Message::None;
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API Update()
|
||||
{
|
||||
if (!g_manager) return;
|
||||
g_manager->Update();
|
||||
}
|
||||
|
||||
const auto message = g_messages.front();
|
||||
g_messages.pop();
|
||||
return message;
|
||||
}
|
||||
UNITY_INTERFACE_EXPORT Message PopMessage()
|
||||
{
|
||||
if (g_messages.empty()) return Message::None;
|
||||
|
||||
const auto message = g_messages.front();
|
||||
g_messages.pop();
|
||||
return message;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT size_t UNITY_INTERFACE_API GetMonitorCount()
|
||||
{
|
||||
if (!g_manager) return 0;
|
||||
if (!g_manager) return 0;
|
||||
return g_manager->GetMonitorCount();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetTotalWidth()
|
||||
{
|
||||
if (!g_manager) return 0;
|
||||
if (!g_manager) return 0;
|
||||
return g_manager->GetTotalWidth();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetTotalHeight()
|
||||
{
|
||||
if (!g_manager) return 0;
|
||||
if (!g_manager) return 0;
|
||||
return g_manager->GetTotalHeight();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetTimeout(int timeout)
|
||||
{
|
||||
if (!g_manager) return;
|
||||
if (!g_manager) return;
|
||||
g_manager->SetTimeout(timeout);
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API IsAvailable(int id)
|
||||
UNITY_INTERFACE_EXPORT MonitorState UNITY_INTERFACE_API GetState(int id)
|
||||
{
|
||||
if (!g_manager) return false;
|
||||
if (!g_manager) return MonitorState::NotSet;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->IsAvailable();
|
||||
return monitor->GetState();
|
||||
}
|
||||
return false;
|
||||
return MonitorState::NotSet;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API GetName(int id, char* buf, int len)
|
||||
{
|
||||
if (!g_manager) return;
|
||||
if (!g_manager) return;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
monitor->GetName(buf, len);
|
||||
@@ -147,7 +157,7 @@ extern "C"
|
||||
|
||||
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API IsPrimary(int id)
|
||||
{
|
||||
if (!g_manager) return false;
|
||||
if (!g_manager) return false;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->IsPrimary();
|
||||
@@ -225,6 +235,26 @@ extern "C"
|
||||
return DXGI_MODE_ROTATION_UNSPECIFIED;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetDpiX(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetDpiX();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetDpiY(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetDpiY();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API IsCursorVisible(int id)
|
||||
{
|
||||
if (!g_manager) return false;
|
||||
@@ -295,12 +325,12 @@ extern "C"
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UpdateCursorTexture(int id, ID3D11Texture2D* texture)
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API GetCursorTexture(int id, ID3D11Texture2D* texture)
|
||||
{
|
||||
if (!g_manager) return;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
monitor->UpdateCursorTexture(texture);
|
||||
monitor->GetCursorTexture(texture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user