Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c62157c0c | ||
|
|
be79e52135 | ||
|
|
e9c8b4e571 | ||
|
|
77c95ab1bb | ||
|
|
7b3990107d | ||
|
|
5c21b65afe | ||
|
|
e83f43d7fd | ||
|
|
58e14d76d8 | ||
|
|
f8d8e64e46 | ||
|
|
0a39e37659 | ||
|
|
931ff6b2db | ||
|
|
75819f0a7d | ||
|
|
d6ce41f9b6 |
Binary file not shown.
@@ -21,7 +21,7 @@ public class Loupe : MonoBehaviour
|
||||
|
||||
// To get other monitor textures, set dirty flag.
|
||||
foreach (var target in uDesktopDuplication.Manager.monitors) {
|
||||
target.CreateTexture();
|
||||
target.CreateTextureIfNeeded();
|
||||
target.shouldBeUpdated = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,14 +8,24 @@ public class MultipleMonitorCreator : MonoBehaviour
|
||||
[SerializeField]
|
||||
GameObject monitorPrefab;
|
||||
|
||||
[Tooltip("Use same scale as real using DPI.")]
|
||||
[SerializeField]
|
||||
bool useRealScale = true;
|
||||
public enum ScaleMode
|
||||
{
|
||||
Real,
|
||||
Fixed,
|
||||
Pixel,
|
||||
}
|
||||
|
||||
[Tooltip("Use this sacle as width if useRealScale is false.")]
|
||||
[Tooltip("Real: DPI-based real scale \nFixed: Same width \nPixel: bigger if screen resolution is high.")]
|
||||
[SerializeField]
|
||||
ScaleMode scaleMode = ScaleMode.Fixed;
|
||||
|
||||
[Tooltip("Use this scale as width if scaleMode is Fixed.")]
|
||||
[SerializeField]
|
||||
float scale = 0.5f;
|
||||
|
||||
[Tooltip("Please specify the surface direction of the mesh (e.g. Plane => Y.)")]
|
||||
public MeshForwardDirection meshForwardDirection = MeshForwardDirection.Z;
|
||||
|
||||
[Tooltip("Remove unsupported monitors automatically after removeWaitDuration.")]
|
||||
[SerializeField]
|
||||
bool removeIfUnsupported = true;
|
||||
@@ -24,12 +34,13 @@ public class MultipleMonitorCreator : MonoBehaviour
|
||||
[SerializeField]
|
||||
float removeWaitDuration = 5f;
|
||||
|
||||
[Tooltip("Remove all childrens (for debug).")]
|
||||
[SerializeField]
|
||||
bool removeChildrenWhenClear = true;
|
||||
|
||||
bool hasMonitorUnsupportStateChecked = false;
|
||||
float removeWaitTimer_ = 0f;
|
||||
|
||||
[Tooltip("Please specify the surface direction of the mesh (e.g. Plane => Y.)")]
|
||||
public MeshForwardDirection meshForwardDirection = MeshForwardDirection.Z;
|
||||
|
||||
public class MonitorInfo
|
||||
{
|
||||
public GameObject gameObject { get; set; }
|
||||
@@ -101,7 +112,7 @@ public class MultipleMonitorCreator : MonoBehaviour
|
||||
go.name = "Monitor " + i;
|
||||
|
||||
// Expand AABB
|
||||
var mesh = go.GetComponent<MeshFilter>().mesh; // clone
|
||||
var mesh = go.GetComponent<MeshFilter>().sharedMesh;
|
||||
var aabbScale = mesh.bounds.size;
|
||||
aabbScale.y = Mathf.Max(aabbScale.y, aabbScale.x);
|
||||
aabbScale.z = Mathf.Max(aabbScale.z, aabbScale.x);
|
||||
@@ -113,13 +124,20 @@ public class MultipleMonitorCreator : MonoBehaviour
|
||||
var monitor = texture.monitor;
|
||||
|
||||
// Set width / height
|
||||
float width, height;
|
||||
if (useRealScale) {
|
||||
width = monitor.widthMeter;
|
||||
height = monitor.heightMeter;
|
||||
} else {
|
||||
width = scale * (monitor.isHorizontal ? 1f : monitor.aspect);
|
||||
height = scale * (monitor.isHorizontal ? 1f / monitor.aspect : 1f);
|
||||
float width = 1f, height = 1f;
|
||||
switch (scaleMode) {
|
||||
case ScaleMode.Real:
|
||||
width = monitor.widthMeter;
|
||||
height = monitor.heightMeter;
|
||||
break;
|
||||
case ScaleMode.Fixed:
|
||||
width = scale * (monitor.isHorizontal ? 1f : monitor.aspect);
|
||||
height = scale * (monitor.isHorizontal ? 1f / monitor.aspect : 1f);
|
||||
break;
|
||||
case ScaleMode.Pixel:
|
||||
width = scale * (monitor.isHorizontal ? 1f : monitor.aspect) * ((float)monitor.width / 1920);
|
||||
height = scale * (monitor.isHorizontal ? 1f / monitor.aspect : 1f) * ((float)monitor.width / 1920);
|
||||
break;
|
||||
}
|
||||
if (meshForwardDirection == MeshForwardDirection.Y) {
|
||||
go.transform.localScale = new Vector3(width, go.transform.localScale.y, height);
|
||||
@@ -148,6 +166,11 @@ public class MultipleMonitorCreator : MonoBehaviour
|
||||
foreach (var info in monitors_) {
|
||||
Destroy(info.gameObject);
|
||||
}
|
||||
if (removeChildrenWhenClear) {
|
||||
for (int i = 0; i < transform.childCount; ++i) {
|
||||
Destroy(transform.GetChild(i).gameObject);
|
||||
}
|
||||
}
|
||||
monitors_.Clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,12 +8,12 @@ public class UddSceneManager : MonoBehaviour
|
||||
[SerializeField] int sceneNo = 0;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (!instance) {
|
||||
instance = this;
|
||||
} else {
|
||||
Destroy(gameObject);
|
||||
}
|
||||
{
|
||||
if (!instance) {
|
||||
instance = this;
|
||||
} else {
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
void Start()
|
||||
@@ -24,17 +24,25 @@ public class UddSceneManager : MonoBehaviour
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.RightArrow)) {
|
||||
Next();
|
||||
if (Input.GetKeyDown(KeyCode.RightArrow)) {
|
||||
Next();
|
||||
} else if (Input.GetKeyDown(KeyCode.LeftArrow)) {
|
||||
Prev();
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.R) && (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl))) {
|
||||
uDesktopDuplication.Manager.instance.Reinitialize();
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.Escape)) {
|
||||
Application.Quit();
|
||||
}
|
||||
}
|
||||
|
||||
void Next()
|
||||
{
|
||||
sceneNo = (sceneNo + 1) % scenes.Length;
|
||||
Load();
|
||||
{
|
||||
sceneNo = (sceneNo + 1) % scenes.Length;
|
||||
Load();
|
||||
}
|
||||
|
||||
void Prev()
|
||||
@@ -44,7 +52,7 @@ public class UddSceneManager : MonoBehaviour
|
||||
}
|
||||
|
||||
void Load()
|
||||
{
|
||||
SceneManager.LoadScene(scenes[sceneNo]);
|
||||
{
|
||||
SceneManager.LoadScene(scenes[Mathf.Clamp(sceneNo, 0, scenes.Length - 1)]);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -5,8 +5,7 @@ using System.Collections.Generic;
|
||||
namespace uDesktopDuplication
|
||||
{
|
||||
|
||||
[AddComponentMenu("uDesktopDuplication/Cursor"),
|
||||
RequireComponent(typeof(Texture))]
|
||||
[AddComponentMenu("uDesktopDuplication/Cursor"), RequireComponent(typeof(Texture))]
|
||||
public class Cursor : MonoBehaviour
|
||||
{
|
||||
[SerializeField] Vector2 modelScale = Vector2.one;
|
||||
|
||||
@@ -9,6 +9,7 @@ public enum Message
|
||||
{
|
||||
None = -1,
|
||||
Reinitialized = 0,
|
||||
TextureSizeChanged = 1,
|
||||
}
|
||||
|
||||
public enum CursorShapeType
|
||||
@@ -29,14 +30,15 @@ public enum MonitorRotation
|
||||
|
||||
public enum MonitorState
|
||||
{
|
||||
NotSet = -1,
|
||||
Available = 0,
|
||||
InvalidArg = 1,
|
||||
AccessDenied = 2,
|
||||
Unsupported = 3,
|
||||
CurrentlyNotAvailable = 4,
|
||||
SessionDisconnected = 5,
|
||||
AccessLost = 6,
|
||||
NotSet = -1,
|
||||
Available = 0,
|
||||
InvalidArg = 1,
|
||||
AccessDenied = 2,
|
||||
Unsupported = 3,
|
||||
CurrentlyNotAvailable = 4,
|
||||
SessionDisconnected = 5,
|
||||
AccessLost = 6,
|
||||
TextureSizeInconsistent = 7,
|
||||
Unknown = 999,
|
||||
}
|
||||
|
||||
@@ -76,6 +78,8 @@ public static class Lib
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetMonitorCount();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern bool HasMonitorCountChanged();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetCursorMonitorId();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetTotalWidth();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace uDesktopDuplication
|
||||
{
|
||||
@@ -14,6 +13,19 @@ public class Manager : MonoBehaviour
|
||||
get { return CreateInstance(); }
|
||||
}
|
||||
|
||||
public static Manager CreateInstance()
|
||||
{
|
||||
if (instance_) {
|
||||
return instance_;
|
||||
}
|
||||
|
||||
var manager = FindObjectOfType<Manager>();
|
||||
if (manager) return manager;
|
||||
|
||||
var go = new GameObject("uDesktopDuplicationManager");
|
||||
return go.AddComponent<Manager>();
|
||||
}
|
||||
|
||||
private List<Monitor> monitors_ = new List<Monitor>();
|
||||
static public List<Monitor> monitors
|
||||
{
|
||||
@@ -49,22 +61,6 @@ public class Manager : MonoBehaviour
|
||||
public delegate void ReinitializeHandler();
|
||||
public static event ReinitializeHandler onReinitialized;
|
||||
|
||||
public static Manager CreateInstance()
|
||||
{
|
||||
if (instance_) {
|
||||
return instance_;
|
||||
}
|
||||
|
||||
var manager = FindObjectOfType<Manager>();
|
||||
if (manager) {
|
||||
manager.Awake();
|
||||
return manager;
|
||||
}
|
||||
|
||||
var go = new GameObject("uDesktopDuplicationManager");
|
||||
return go.AddComponent<Manager>();
|
||||
}
|
||||
|
||||
public static Monitor GetMonitor(int id)
|
||||
{
|
||||
if (id < 0 || id >= Manager.monitors.Count) {
|
||||
@@ -78,18 +74,21 @@ public class Manager : MonoBehaviour
|
||||
{
|
||||
Lib.SetDebugMode(debugMode);
|
||||
Lib.InitializeUDD();
|
||||
Lib.SetTimeout(desktopDuplicationApiTimeout);
|
||||
|
||||
if (instance_ != null) return;
|
||||
if (instance_ != null) {
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
instance_ = this;
|
||||
|
||||
CreateMonitors();
|
||||
|
||||
Lib.SetTimeout(desktopDuplicationApiTimeout);
|
||||
}
|
||||
|
||||
void OnApplicationQuit()
|
||||
{
|
||||
Lib.FinalizeUDD();
|
||||
DestroyMonitors();
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
@@ -113,7 +112,7 @@ public class Manager : MonoBehaviour
|
||||
}
|
||||
|
||||
[ContextMenu("Reinitialize")]
|
||||
void Reinitialize()
|
||||
public void Reinitialize()
|
||||
{
|
||||
Debug.Log("[uDD] Reinitialize");
|
||||
Lib.Reinitialize();
|
||||
@@ -125,20 +124,30 @@ public class Manager : MonoBehaviour
|
||||
|
||||
void ReinitializeIfNeeded()
|
||||
{
|
||||
bool reinitializeNeeded = false;
|
||||
|
||||
for (int i = 0; i < monitors.Count; ++i) {
|
||||
var monitor = monitors[i];
|
||||
if (monitor.state == MonitorState.NotSet ||
|
||||
if (
|
||||
monitor.state == MonitorState.NotSet ||
|
||||
monitor.state == MonitorState.AccessLost ||
|
||||
monitor.state == MonitorState.AccessDenied ||
|
||||
monitor.state == MonitorState.SessionDisconnected) {
|
||||
if (!shouldReinitialize_) {
|
||||
shouldReinitialize_ = true;
|
||||
reinitializationTimer_ = 0f;
|
||||
break;
|
||||
}
|
||||
monitor.state == MonitorState.SessionDisconnected
|
||||
) {
|
||||
reinitializeNeeded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Lib.HasMonitorCountChanged()) {
|
||||
reinitializeNeeded = true;
|
||||
}
|
||||
|
||||
if (!shouldReinitialize_ && reinitializeNeeded) {
|
||||
shouldReinitialize_ = true;
|
||||
reinitializationTimer_ = 0f;
|
||||
}
|
||||
|
||||
if (shouldReinitialize_) {
|
||||
if (reinitializationTimer_ > retryReinitializationDuration) {
|
||||
Reinitialize();
|
||||
@@ -152,10 +161,14 @@ public class Manager : MonoBehaviour
|
||||
{
|
||||
var message = Lib.PopMessage();
|
||||
while (message != Message.None) {
|
||||
Debug.Log("[uDD] " + message);
|
||||
switch (message) {
|
||||
case Message.Reinitialized:
|
||||
ReinitializeMonitors();
|
||||
break;
|
||||
case Message.TextureSizeChanged:
|
||||
RecreateTextures();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -179,31 +192,36 @@ public class Manager : MonoBehaviour
|
||||
|
||||
void CreateMonitors()
|
||||
{
|
||||
monitors.Clear();
|
||||
DestroyMonitors();
|
||||
for (int i = 0; i < monitorCount; ++i) {
|
||||
monitors.Add(new Monitor(i));
|
||||
}
|
||||
}
|
||||
|
||||
void DestroyMonitors()
|
||||
{
|
||||
for (int i = 0; i < monitors.Count; ++i) {
|
||||
monitors[i].DestroyTexture();
|
||||
}
|
||||
monitors.Clear();
|
||||
}
|
||||
|
||||
void ReinitializeMonitors()
|
||||
{
|
||||
for (int i = 0; i < monitorCount; ++i) {
|
||||
if (i == monitors.Count) {
|
||||
monitors.Add(new Monitor(i));
|
||||
} else {
|
||||
monitors[i].Reinitialize();
|
||||
}
|
||||
monitors[i].Reinitialize();
|
||||
}
|
||||
}
|
||||
|
||||
void WaitThenDo(System.Action func, float sec)
|
||||
void RecreateTextures()
|
||||
{
|
||||
StartCoroutine(_WaitThenDo(func, sec));
|
||||
}
|
||||
|
||||
IEnumerator _WaitThenDo(System.Action func, float sec)
|
||||
{
|
||||
yield return new WaitForSeconds(sec);
|
||||
func();
|
||||
for (int i = 0; i < monitorCount; ++i) {
|
||||
monitors[i].CreateTextureIfNeeded();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using UnityEngine;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace uDesktopDuplication
|
||||
{
|
||||
@@ -39,6 +38,11 @@ public class Monitor
|
||||
}
|
||||
}
|
||||
|
||||
~Monitor()
|
||||
{
|
||||
DestroyTexture();
|
||||
}
|
||||
|
||||
public int id
|
||||
{
|
||||
get;
|
||||
@@ -204,17 +208,24 @@ public class Monitor
|
||||
set;
|
||||
}
|
||||
|
||||
private static Texture2D errorTexture_;
|
||||
private static readonly string errorTexturePath = "uDesktopDuplication/Textures/NotAvailable";
|
||||
private Texture2D errorTexture
|
||||
{
|
||||
get
|
||||
{
|
||||
return errorTexture_ ??
|
||||
(errorTexture_ = Resources.Load<Texture2D>(errorTexturePath));
|
||||
}
|
||||
}
|
||||
|
||||
private Texture2D texture_;
|
||||
public Texture2D texture
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!available) {
|
||||
return Resources.Load<Texture2D>("uDesktopDuplication/Textures/NotAvailable");
|
||||
}
|
||||
if (texture_ == null) {
|
||||
CreateTexture();
|
||||
}
|
||||
if (!available) return errorTexture;
|
||||
if (texture_ == null) CreateTextureIfNeeded();
|
||||
return texture_;
|
||||
}
|
||||
}
|
||||
@@ -232,7 +243,7 @@ public class Monitor
|
||||
Lib.GetCursorTexture(id, ptr);
|
||||
}
|
||||
|
||||
public void CreateTexture()
|
||||
public void CreateTextureIfNeeded()
|
||||
{
|
||||
if (!available) return;
|
||||
|
||||
@@ -240,13 +251,8 @@ public class Monitor
|
||||
var h = isHorizontal ? height : width;
|
||||
bool shouldCreate = true;
|
||||
|
||||
if (texture_) {
|
||||
if (texture_.width != w || texture_.height != h) {
|
||||
if (texture_) Object.DestroyImmediate(texture_);
|
||||
shouldCreate = true;
|
||||
} else {
|
||||
shouldCreate = false;
|
||||
}
|
||||
if (texture_ && texture_.width == w && texture_.height == h) {
|
||||
shouldCreate = false;
|
||||
}
|
||||
|
||||
if (w <= 0 || h <= 0) {
|
||||
@@ -254,13 +260,29 @@ public class Monitor
|
||||
}
|
||||
|
||||
if (shouldCreate) {
|
||||
texture_ = new Texture2D(w, h, TextureFormat.BGRA32, false);
|
||||
CreateTexture();
|
||||
}
|
||||
}
|
||||
|
||||
void CreateTexture()
|
||||
{
|
||||
DestroyTexture();
|
||||
var w = isHorizontal ? width : height;
|
||||
var h = isHorizontal ? height : width;
|
||||
texture_ = new Texture2D(w, h, TextureFormat.BGRA32, false);
|
||||
}
|
||||
|
||||
public void DestroyTexture()
|
||||
{
|
||||
if (texture_) {
|
||||
Object.Destroy(texture_);
|
||||
texture_ = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Reinitialize()
|
||||
{
|
||||
CreateTexture();
|
||||
CreateTextureIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace uDesktopDuplication
|
||||
{
|
||||
|
||||
[AddComponentMenu("uDesktopDuplication/Texture")]
|
||||
[AddComponentMenu("uDesktopDuplication/Texture"), RequireComponent(typeof(Cursor))]
|
||||
public class Texture : MonoBehaviour
|
||||
{
|
||||
private Monitor monitor_;
|
||||
@@ -105,10 +105,7 @@ public class Texture : MonoBehaviour
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (!GetComponent<Cursor>())
|
||||
{
|
||||
gameObject.AddComponent<Cursor>();
|
||||
}
|
||||
AddCursorIfNotAttached();
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
@@ -116,6 +113,12 @@ public class Texture : MonoBehaviour
|
||||
if (monitor == null) {
|
||||
monitor = Manager.primary;
|
||||
}
|
||||
Manager.onReinitialized += Reinitialize;
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
Manager.onReinitialized -= Reinitialize;
|
||||
}
|
||||
|
||||
void Update()
|
||||
@@ -124,6 +127,20 @@ public class Texture : MonoBehaviour
|
||||
UpdateMaterial();
|
||||
}
|
||||
|
||||
void AddCursorIfNotAttached()
|
||||
{
|
||||
if (!GetComponent<Cursor>())
|
||||
{
|
||||
gameObject.AddComponent<Cursor>();
|
||||
}
|
||||
}
|
||||
|
||||
void Reinitialize()
|
||||
{
|
||||
// Monitor instance is released here when initialized.
|
||||
monitor = Manager.GetMonitor(monitor.id);
|
||||
}
|
||||
|
||||
void UpdateMaterial()
|
||||
{
|
||||
Invert();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace uDesktopDuplication
|
||||
@@ -25,6 +26,17 @@ public static class Utility
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
public static void WaitThenDo(System.Action func, float sec)
|
||||
{
|
||||
Manager.instance.StartCoroutine(_WaitThenDo(func, sec));
|
||||
}
|
||||
|
||||
public static IEnumerator _WaitThenDo(System.Action func, float sec)
|
||||
{
|
||||
yield return new WaitForSeconds(sec);
|
||||
func();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
#include <unordered_map>
|
||||
#include <iostream>
|
||||
#include <d3d11.h>
|
||||
|
||||
#include "IUnityInterface.h"
|
||||
#include "IUnityGraphicsD3D11.h"
|
||||
#include "Common.h"
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
|
||||
extern IUnityInterfaces* g_unity;
|
||||
extern std::unique_ptr<MonitorManager> g_manager;
|
||||
@@ -21,7 +21,7 @@ IUnityInterfaces* GetUnity()
|
||||
}
|
||||
|
||||
|
||||
ID3D11Device* GetDevice()
|
||||
ComPtr<ID3D11Device> GetDevice()
|
||||
{
|
||||
return GetUnity()->Get<IUnityGraphicsD3D11>()->GetDevice();
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <wrl/client.h>
|
||||
|
||||
|
||||
// Unity interface and ID3D11Device getters
|
||||
@@ -10,7 +9,7 @@ struct IUnityInterfaces;
|
||||
IUnityInterfaces* GetUnity();
|
||||
|
||||
struct ID3D11Device;
|
||||
ID3D11Device* GetDevice();
|
||||
Microsoft::WRL::ComPtr<ID3D11Device> GetDevice();
|
||||
|
||||
|
||||
// Manager getter
|
||||
@@ -18,11 +17,23 @@ class MonitorManager;
|
||||
const std::unique_ptr<MonitorManager>& GetMonitorManager();
|
||||
|
||||
|
||||
template <class T>
|
||||
auto MakeUniqueWithReleaser(T* ptr)
|
||||
{
|
||||
const auto deleter = [](T* ptr)
|
||||
{
|
||||
if (ptr != nullptr) ptr->Release();
|
||||
};
|
||||
return std::unique_ptr<T, decltype(deleter)>(ptr, deleter);
|
||||
}
|
||||
|
||||
|
||||
// Message is pooled and fetch from Unity.
|
||||
enum class Message
|
||||
{
|
||||
None = -1,
|
||||
Reinitialized = 0,
|
||||
TextureSizeChanged = 1,
|
||||
};
|
||||
|
||||
void SendMessageToUnity(Message message);
|
||||
@@ -53,14 +53,18 @@ void Cursor::UpdateBuffer(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
|
||||
apiBufferSize_ = frameInfo.PointerShapeBufferSize;
|
||||
apiBuffer_ = std::make_unique<BYTE[]>(apiBufferSize_);
|
||||
}
|
||||
if (!apiBuffer_) return;
|
||||
|
||||
if (!apiBuffer_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get mouse pointer information
|
||||
UINT bufferSize;
|
||||
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo;
|
||||
const auto hr = monitor_->GetDeskDupl()->GetFramePointerShape(
|
||||
apiBufferSize_,
|
||||
reinterpret_cast<void*>(apiBuffer_.get()),
|
||||
apiBuffer_.get(),
|
||||
&bufferSize,
|
||||
&shapeInfo);
|
||||
|
||||
@@ -69,6 +73,7 @@ void Cursor::UpdateBuffer(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
|
||||
Debug::Error("Cursor::UpdateBuffer() => GetFramePointerShape() failed.");
|
||||
apiBuffer_.reset();
|
||||
apiBufferSize_ = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
shapeInfo_ = shapeInfo;
|
||||
@@ -100,13 +105,20 @@ void Cursor::UpdateTexture()
|
||||
bgra32BufferSize_ = bgraBufferSize;
|
||||
bgra32Buffer_ = std::make_unique<BYTE[]>(bgra32BufferSize_);
|
||||
}
|
||||
if (!bgra32Buffer_) return;
|
||||
|
||||
if (!bgra32Buffer_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!apiBuffer_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If masked, copy the desktop image and merge it with masked image.
|
||||
if (isMono || isColorMask)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
const auto mw = monitor_->GetWidth();
|
||||
const auto mh = monitor_->GetHeight();
|
||||
auto x = x_;
|
||||
@@ -153,8 +165,7 @@ void Cursor::UpdateTexture()
|
||||
desc.MiscFlags = 0;
|
||||
|
||||
ComPtr<ID3D11Texture2D> texture;
|
||||
hr = GetDevice()->CreateTexture2D(&desc, nullptr, &texture);
|
||||
if (FAILED(hr))
|
||||
if (FAILED(GetDevice()->CreateTexture2D(&desc, nullptr, &texture)))
|
||||
{
|
||||
Debug::Error("Cursor::UpdateTexture() => GetDevice()->CreateTexture2D() failed.");
|
||||
return;
|
||||
@@ -181,16 +192,14 @@ void Cursor::UpdateTexture()
|
||||
}
|
||||
|
||||
ComPtr<IDXGISurface> surface;
|
||||
hr = texture.As<IDXGISurface>(&surface);
|
||||
if (FAILED(hr))
|
||||
if (FAILED(texture.As(&surface)))
|
||||
{
|
||||
Debug::Error("Cursor::UpdateTexture() => texture->QueryInterface() failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
DXGI_MAPPED_RECT mappedSurface;
|
||||
hr = surface->Map(&mappedSurface, DXGI_MAP_READ);
|
||||
if (FAILED(hr))
|
||||
if (FAILED(surface->Map(&mappedSurface, DXGI_MAP_READ)))
|
||||
{
|
||||
Debug::Error("Cursor::UpdateTexture() => surface->Map() failed.");
|
||||
return;
|
||||
@@ -243,8 +252,7 @@ void Cursor::UpdateTexture()
|
||||
}
|
||||
}
|
||||
|
||||
hr = surface->Unmap();
|
||||
if (FAILED(hr))
|
||||
if (FAILED(surface->Unmap()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -28,4 +28,6 @@ void Debug::Finalize()
|
||||
Debug::Log("Stop");
|
||||
fs_.close();
|
||||
}
|
||||
Debug::SetLogFunc(nullptr);
|
||||
Debug::SetErrorFunc(nullptr);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include <time.h>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include "IUnityInterface.h"
|
||||
@@ -86,18 +87,34 @@ private:
|
||||
Flush(level);
|
||||
}
|
||||
|
||||
static void OutputTime()
|
||||
{
|
||||
auto t = time(nullptr);
|
||||
tm tm;
|
||||
localtime_s(&tm, &t);
|
||||
char buf[64];
|
||||
strftime(buf, 64, "%F %T", &tm);
|
||||
Output("[");;
|
||||
Output(buf);
|
||||
Output("]");
|
||||
}
|
||||
|
||||
public:
|
||||
template <class Arg, class... RestArgs>
|
||||
static void Log(Arg&& arg, RestArgs&&... restArgs)
|
||||
{
|
||||
Output("[uDD::Log] ");
|
||||
Output("[uDD::Log]");
|
||||
OutputTime();
|
||||
Output(" ");
|
||||
_Log(Level::Log, std::forward<Arg>(arg), std::forward<RestArgs>(restArgs)...);
|
||||
}
|
||||
|
||||
template <class Arg, class... RestArgs>
|
||||
static void Error(Arg&& arg, RestArgs&&... restArgs)
|
||||
{
|
||||
Output("[uDD::Err] ");
|
||||
Output("[uDD::Err]");
|
||||
OutputTime();
|
||||
Output(" ");
|
||||
_Log(Level::Error, std::forward<Arg>(arg), std::forward<RestArgs>(restArgs)...);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,59 +18,94 @@ Monitor::Monitor(int id)
|
||||
|
||||
Monitor::~Monitor()
|
||||
{
|
||||
if (deskDupl_)
|
||||
{
|
||||
deskDupl_->Release();
|
||||
deskDupl_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Monitor::Initialize(IDXGIOutput* output)
|
||||
{
|
||||
output->GetDesc(&outputDesc_);
|
||||
monitorInfo_.cbSize = sizeof(MONITORINFOEX);
|
||||
GetMonitorInfo(outputDesc_.Monitor, &monitorInfo_);
|
||||
if (FAILED(output->GetDesc(&outputDesc_)))
|
||||
{
|
||||
Debug::Error("Monitor::Initialize() => IDXGIOutput::GetDesc() failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
GetDpiForMonitor(outputDesc_.Monitor, MDT_RAW_DPI, &dpiX_, &dpiY_);
|
||||
monitorInfo_.cbSize = sizeof(MONITORINFOEX);
|
||||
if (!GetMonitorInfo(outputDesc_.Monitor, &monitorInfo_))
|
||||
{
|
||||
Debug::Error("Monitor::Initialize() => GetMonitorInfo() failed.");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto rect = monitorInfo_.rcMonitor;
|
||||
width_ = rect.right - rect.left;
|
||||
height_ = rect.bottom - rect.top;
|
||||
}
|
||||
|
||||
if (FAILED(GetDpiForMonitor(outputDesc_.Monitor, MDT_RAW_DPI, &dpiX_, &dpiY_)))
|
||||
{
|
||||
Debug::Error("Monitor::Initialize() => GetDpiForMonitor() failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
auto output1 = reinterpret_cast<IDXGIOutput1*>(output);
|
||||
const auto hr = output1->DuplicateOutput(GetDevice(), &deskDupl_);
|
||||
|
||||
// TODO: error check
|
||||
switch (hr)
|
||||
switch (output1->DuplicateOutput(GetDevice().Get(), &deskDupl_))
|
||||
{
|
||||
case S_OK:
|
||||
{
|
||||
state_ = State::Available;
|
||||
Debug::Log("Monitor::Initialize() => OK.");
|
||||
Debug::Log(" ID : ", GetId());
|
||||
Debug::Log(" Size : (", GetWidth(), ", ", GetHeight(), ")");
|
||||
Debug::Log(" DPI : (", GetDpiX(), ", ", GetDpiY(), ")");
|
||||
break;
|
||||
}
|
||||
case E_INVALIDARG:
|
||||
{
|
||||
state_ = State::InvalidArg;
|
||||
Debug::Error("Monitor::Initialize() => Invalid arguments.");
|
||||
break;
|
||||
}
|
||||
case E_ACCESSDENIED:
|
||||
{
|
||||
// For example, when the user presses Ctrl + Alt + Delete and the screen
|
||||
// switches to admin screen, this error occurs.
|
||||
state_ = State::AccessDenied;
|
||||
Debug::Error("Monitor::Initialize() => Access denied.");
|
||||
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;
|
||||
Debug::Error("Monitor::Initialize() => Unsupported display.");
|
||||
break;
|
||||
}
|
||||
case DXGI_ERROR_NOT_CURRENTLY_AVAILABLE:
|
||||
{
|
||||
// When other application use Desktop Duplication API, this error occurs.
|
||||
state_ = State::CurrentlyNotAvailable;
|
||||
Debug::Error("Monitor::Initialize() => Currently not available.");
|
||||
break;
|
||||
}
|
||||
case DXGI_ERROR_SESSION_DISCONNECTED:
|
||||
{
|
||||
state_ = State::SessionDisconnected;
|
||||
Debug::Error("Monitor::Initialize() => Session disconnected.");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
state_ = State::Unknown;
|
||||
Debug::Error("Monitor::Render() => Unknown Error.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,65 +116,82 @@ void Monitor::Render(UINT timeout)
|
||||
|
||||
ComPtr<IDXGIResource> resource;
|
||||
DXGI_OUTDUPL_FRAME_INFO frameInfo;
|
||||
HRESULT hr;
|
||||
|
||||
hr = deskDupl_->AcquireNextFrame(timeout, &frameInfo, &resource);
|
||||
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.
|
||||
Debug::Log("Monitor::Render() => DXGI_ERROR_ACCESS_LOST.");
|
||||
state_ = State::AccessLost;
|
||||
break;
|
||||
}
|
||||
case DXGI_ERROR_WAIT_TIMEOUT:
|
||||
{
|
||||
// This often occurs when timeout value is small and it is not problem.
|
||||
// Debug::Log("Monitor::Render() => DXGI_ERROR_WAIT_TIMEOUT.");
|
||||
break;
|
||||
}
|
||||
case DXGI_ERROR_INVALID_CALL:
|
||||
{
|
||||
Debug::Error("Monitor::Render() => DXGI_ERROR_INVALID_CALL.");
|
||||
break;
|
||||
}
|
||||
case E_INVALIDARG:
|
||||
{
|
||||
Debug::Error("Monitor::Render() => E_INVALIDARG.");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
state_ = State::Unknown;
|
||||
Debug::Error("Monitor::Render() => Unknown Error.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (unityTexture_)
|
||||
{
|
||||
ComPtr<ID3D11Texture2D> texture;
|
||||
resource.As<ID3D11Texture2D>(&texture);
|
||||
ID3D11Texture2D* texture;
|
||||
if (FAILED(resource.CopyTo(&texture)))
|
||||
{
|
||||
Debug::Error("Monitor::Render() => resource.As() failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
D3D11_TEXTURE2D_DESC srcDesc, dstDesc;
|
||||
texture->GetDesc(&srcDesc);
|
||||
texture->GetDesc(&dstDesc);
|
||||
unityTexture_->GetDesc(&dstDesc);
|
||||
if (srcDesc.Width != dstDesc.Width ||
|
||||
srcDesc.Height != dstDesc.Height)
|
||||
{
|
||||
Debug::Error("Monitor::Render() => Texture sizes are defferent.");
|
||||
Debug::Error(" Source : (", srcDesc.Width, ", ", srcDesc.Height, ")");
|
||||
Debug::Error(" Dest : (", dstDesc.Width, ", ", dstDesc.Height, ")");
|
||||
//Debug::Log(" => Try modifying width/height using reported value from DDA.");
|
||||
//width_ = srcDesc.Width;
|
||||
//height_ = srcDesc.Height;
|
||||
state_ = MonitorState::TextureSizeInconsistent;
|
||||
//SendMessageToUnity(Message::TextureSizeChanged);
|
||||
}
|
||||
else
|
||||
{
|
||||
ComPtr<ID3D11DeviceContext> context;
|
||||
GetDevice()->GetImmediateContext(&context);
|
||||
context->CopyResource(unityTexture_, texture.Get());
|
||||
context->CopyResource(unityTexture_, texture);
|
||||
}
|
||||
}
|
||||
|
||||
cursor_->UpdateBuffer(frameInfo);
|
||||
cursor_->UpdateTexture();
|
||||
|
||||
hr = deskDupl_->ReleaseFrame();
|
||||
if (FAILED(hr))
|
||||
if (FAILED(deskDupl_->ReleaseFrame()))
|
||||
{
|
||||
Debug::Error("Monitor::Render() => ReleaseFrame() failed.");
|
||||
}
|
||||
@@ -170,7 +222,7 @@ ID3D11Texture2D* Monitor::GetUnityTexture() const
|
||||
}
|
||||
|
||||
|
||||
const ComPtr<IDXGIOutputDuplication>& Monitor::GetDeskDupl()
|
||||
IDXGIOutputDuplication* Monitor::GetDeskDupl()
|
||||
{
|
||||
return deskDupl_;
|
||||
}
|
||||
@@ -244,13 +296,11 @@ int Monitor::GetDpiY() const
|
||||
|
||||
int Monitor::GetWidth() const
|
||||
{
|
||||
const auto rect = monitorInfo_.rcMonitor;
|
||||
return rect.right - rect.left;
|
||||
return width_;
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetHeight() const
|
||||
{
|
||||
const auto rect = monitorInfo_.rcMonitor;
|
||||
return rect.bottom - rect.top;
|
||||
return height_;
|
||||
}
|
||||
@@ -17,6 +17,7 @@ enum class MonitorState
|
||||
CurrentlyNotAvailable = 4,
|
||||
SessionDisconnected = 5,
|
||||
AccessLost = 6,
|
||||
TextureSizeInconsistent = 7,
|
||||
Unknown = 999,
|
||||
};
|
||||
|
||||
@@ -47,15 +48,16 @@ public:
|
||||
int GetRotation() const;
|
||||
int GetDpiX() const;
|
||||
int GetDpiY() const;
|
||||
const Microsoft::WRL::ComPtr<IDXGIOutputDuplication>& GetDeskDupl();
|
||||
IDXGIOutputDuplication* GetDeskDupl();
|
||||
const std::unique_ptr<Cursor>& GetCursor();
|
||||
|
||||
private:
|
||||
int id_ = -1;
|
||||
UINT dpiX_ = -1, dpiY_ = -1;
|
||||
int width_ = -1, height_ = -1;
|
||||
State state_ = State::NotSet;
|
||||
std::unique_ptr<Cursor> cursor_;
|
||||
Microsoft::WRL::ComPtr<IDXGIOutputDuplication> deskDupl_;
|
||||
IDXGIOutputDuplication* deskDupl_ = nullptr;
|
||||
ID3D11Texture2D* unityTexture_ = nullptr;
|
||||
DXGI_OUTPUT_DESC outputDesc_;
|
||||
MONITORINFOEX monitorInfo_;
|
||||
|
||||
@@ -35,7 +35,11 @@ void MonitorManager::Initialize()
|
||||
|
||||
// Get factory
|
||||
ComPtr<IDXGIFactory1> factory;
|
||||
CreateDXGIFactory1(IID_PPV_ARGS(&factory));
|
||||
if (FAILED(CreateDXGIFactory1(IID_PPV_ARGS(&factory))))
|
||||
{
|
||||
Debug::Error("MonitorManager::Initialize() => CreateDXGIFactory1() failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check all display adapters
|
||||
int id = 0;
|
||||
@@ -74,10 +78,14 @@ void MonitorManager::Reinitialize()
|
||||
}
|
||||
|
||||
|
||||
void MonitorManager::CheckMonitorNumbers()
|
||||
bool MonitorManager::HasMonitorCountChanged() const
|
||||
{
|
||||
ComPtr<IDXGIFactory1> factory;
|
||||
CreateDXGIFactory1(IID_PPV_ARGS(&factory));
|
||||
if (FAILED(CreateDXGIFactory1(IID_PPV_ARGS(&factory))))
|
||||
{
|
||||
Debug::Error("MonitorManager::CheckMonitorConnection() => CreateDXGIFactory1() failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
int id = 0;
|
||||
ComPtr<IDXGIAdapter1> adapter;
|
||||
@@ -90,11 +98,7 @@ void MonitorManager::CheckMonitorNumbers()
|
||||
}
|
||||
}
|
||||
|
||||
if (GetMonitorCount() != id)
|
||||
{
|
||||
Debug::Log("Monitor number changed: ", GetMonitorCount(), " => ", id);
|
||||
RequireReinitilization();
|
||||
}
|
||||
return monitors_.size() != id;
|
||||
}
|
||||
|
||||
|
||||
@@ -110,8 +114,6 @@ std::shared_ptr<Monitor> MonitorManager::GetMonitor(int id) const
|
||||
|
||||
void MonitorManager::Update()
|
||||
{
|
||||
CheckMonitorNumbers();
|
||||
|
||||
if (isReinitializationRequired_)
|
||||
{
|
||||
Reinitialize();
|
||||
@@ -137,6 +139,7 @@ int MonitorManager::GetMonitorCount() const
|
||||
return static_cast<int>(monitors_.size());
|
||||
}
|
||||
|
||||
|
||||
int MonitorManager::GetTotalWidth() const
|
||||
{
|
||||
std::vector<int> lefts, rights;
|
||||
@@ -150,6 +153,7 @@ int MonitorManager::GetTotalWidth() const
|
||||
return maxRight - minLeft;
|
||||
}
|
||||
|
||||
|
||||
int MonitorManager::GetTotalHeight() const
|
||||
{
|
||||
std::vector<int> tops, bottoms;
|
||||
|
||||
@@ -16,7 +16,7 @@ public:
|
||||
explicit MonitorManager();
|
||||
~MonitorManager();
|
||||
void Reinitialize();
|
||||
void CheckMonitorNumbers();
|
||||
bool HasMonitorCountChanged() const;
|
||||
void RequireReinitilization();
|
||||
void SetCursorMonitorId(int id) { cursorMonitorId_ = id; }
|
||||
int GetCursorMonitorId() const { return cursorMonitorId_; }
|
||||
|
||||
@@ -43,8 +43,6 @@ extern "C"
|
||||
std::queue<Message> empty;
|
||||
g_messages.swap(empty);
|
||||
|
||||
Debug::SetLogFunc(nullptr);
|
||||
Debug::SetErrorFunc(nullptr);
|
||||
Debug::Finalize();
|
||||
}
|
||||
}
|
||||
@@ -117,6 +115,12 @@ extern "C"
|
||||
return g_manager->GetMonitorCount();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API HasMonitorCountChanged()
|
||||
{
|
||||
if (!g_manager) return false;
|
||||
return g_manager->HasMonitorCountChanged();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorMonitorId()
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,2 +1,2 @@
|
||||
m_EditorVersion: 5.4.1f1
|
||||
m_EditorVersion: 5.4.2f2
|
||||
m_StandardAssetsVersion: 0
|
||||
|
||||
10
README.md
10
README.md
@@ -25,16 +25,6 @@ Usage
|
||||
Attach `uDesktopDuplication/Texture` component to the target object, then its main texture will be replaced with the captured screen. Please see example scenes for more details.
|
||||
|
||||
|
||||
Version
|
||||
-------
|
||||
| Data | Version | Description |
|
||||
| ---------- | ------- | --------------------------------- |
|
||||
| 2016/10/28 | 1.0.0 | Support multiple screens. |
|
||||
| 2016/10/27 | 0.0.3 | Support lienar color. |
|
||||
| 2016/10/27 | 0.0.2 | Add mouse cursor / shaders. |
|
||||
| 2016/10/27 | 0.0.1 | Initial commit. |
|
||||
|
||||
|
||||
Lisence
|
||||
-------
|
||||
The MIT License (MIT)
|
||||
|
||||
Reference in New Issue
Block a user