Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
306a656c4e | ||
|
|
faf65b4200 | ||
|
|
ab25af516e | ||
|
|
45b0ab2cd8 | ||
|
|
68227e9af6 | ||
|
|
be9871ba92 | ||
|
|
110d5d7bda | ||
|
|
533a4c9f28 | ||
|
|
88e8342d7a | ||
|
|
72376b9e91 | ||
|
|
a10d5aa5c1 | ||
|
|
306185a1e9 | ||
|
|
60889d53f4 | ||
|
|
f207e65952 | ||
|
|
aea8d112c6 | ||
|
|
9f6971dd45 | ||
|
|
1be698af63 | ||
|
|
064db9f137 | ||
|
|
166d172db2 | ||
|
|
95f8099392 | ||
|
|
9c62157c0c | ||
|
|
be79e52135 | ||
|
|
e9c8b4e571 | ||
|
|
77c95ab1bb | ||
|
|
7b3990107d | ||
|
|
5c21b65afe | ||
|
|
e83f43d7fd | ||
|
|
58e14d76d8 | ||
|
|
f8d8e64e46 | ||
|
|
0a39e37659 | ||
|
|
931ff6b2db | ||
|
|
75819f0a7d | ||
|
|
d6ce41f9b6 | ||
|
|
9f8a026c24 | ||
|
|
dc702c6e1d | ||
|
|
5a3af11d08 | ||
|
|
0646621f73 | ||
|
|
c9d6d3ad6a | ||
|
|
78ee75a875 | ||
|
|
c83ce50468 | ||
|
|
3567182a4d | ||
|
|
e466aa65db |
Binary file not shown.
BIN
Assets/uDesktopDuplication/Examples/Scenes/Meta Data.unity
Normal file
BIN
Assets/uDesktopDuplication/Examples/Scenes/Meta Data.unity
Normal file
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aac40d1b58f3f8940be815779d89b5a4
|
||||
timeCreated: 1479544409
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
Binary file not shown.
181
Assets/uDesktopDuplication/Examples/Scripts/GazePointAnalyzer.cs
Normal file
181
Assets/uDesktopDuplication/Examples/Scripts/GazePointAnalyzer.cs
Normal file
@@ -0,0 +1,181 @@
|
||||
using UnityEngine;
|
||||
using MeshForwardDirection = uDesktopDuplication.Texture.MeshForwardDirection;
|
||||
|
||||
[RequireComponent(typeof(uDesktopDuplication.Texture))]
|
||||
public class GazePointAnalyzer : MonoBehaviour
|
||||
{
|
||||
uDesktopDuplication.Texture uddTexture_;
|
||||
|
||||
[Tooltip("This needs a small calculation cost.")]
|
||||
public bool calcAveragePos = true;
|
||||
|
||||
private Vector2 averageCoord_ = Vector2.zero;
|
||||
private Vector2 averageCoordVelocity_ = Vector2.zero;
|
||||
public Vector3 averagePos
|
||||
{
|
||||
get { return GetWorldPositionFromCoord((int)averageCoord_.x, (int)averageCoord_.y); }
|
||||
}
|
||||
private Vector2 preCursorCoord_ = Vector2.zero;
|
||||
|
||||
[Header("Filters")]
|
||||
[Range(0f, 1f)] public float moveRectFilter = 0.05f;
|
||||
[Range(0f, 1f)] public float mouseFilter = 0.05f;
|
||||
[Range(0f, 1f)] public float dirtyRectFilter = 0.01f;
|
||||
[Range(0f, 1f)] public float noEventFilter = 0.01f;
|
||||
[Range(0f, 1f)] public float velocityFilter = 0.1f;
|
||||
|
||||
[Header("Debug")]
|
||||
public bool drawAveragePos;
|
||||
public bool drawMoveRects;
|
||||
public bool drawDirtyRects;
|
||||
|
||||
void Start()
|
||||
{
|
||||
uddTexture_ = GetComponent<uDesktopDuplication.Texture>();
|
||||
averageCoord_ = new Vector2(uddTexture_.monitor.width / 2, uddTexture_.monitor.height / 2);
|
||||
}
|
||||
|
||||
public Vector3 GetWorldPositionFromCoord(int u, int v)
|
||||
{
|
||||
var monitor = uddTexture_.monitor;
|
||||
|
||||
// Mesh & Scale information
|
||||
var mesh = GetComponent<MeshFilter>().sharedMesh;
|
||||
var width = transform.localScale.x * (mesh.bounds.extents.x * 2f);
|
||||
var height = transform.localScale.y * (mesh.bounds.extents.y * 2f);
|
||||
|
||||
// Local position (scale included).
|
||||
var x = (float)(u - monitor.width / 2) / monitor.width;
|
||||
var y = -(float)(v - monitor.height / 2) / monitor.height;
|
||||
var localPos = new Vector3(width * x, height * y, 0f);
|
||||
|
||||
// Bending
|
||||
if (uddTexture_.bend) {
|
||||
var radius = uddTexture_.radius;
|
||||
var angle = localPos.x / radius;
|
||||
if (uddTexture_.meshForwardDirection == MeshForwardDirection.Y) {
|
||||
localPos.y -= radius * (1f - Mathf.Cos(angle));
|
||||
} else {
|
||||
localPos.z -= radius * (1f - Mathf.Cos(angle));
|
||||
}
|
||||
localPos.x = radius * Mathf.Sin(angle);
|
||||
}
|
||||
|
||||
// To world position
|
||||
return transform.position + (transform.rotation * localPos);
|
||||
}
|
||||
|
||||
void CalcAveragePos()
|
||||
{
|
||||
if (!calcAveragePos) return;
|
||||
|
||||
var coord = Vector2.zero;
|
||||
var monitor = uddTexture_.monitor;
|
||||
var cursorCoord = new Vector2(monitor.cursorX, monitor.cursorY);
|
||||
var filter = 0f;
|
||||
|
||||
// move rect
|
||||
if (monitor.moveRectCount > 0) {
|
||||
foreach (var rects in monitor.moveRects) {
|
||||
var rect = rects.destination;
|
||||
var center = new Vector2(
|
||||
(rect.right + rect.left) / 2,
|
||||
(rect.bottom + rect.top) / 2);
|
||||
coord += center;
|
||||
}
|
||||
coord /= monitor.moveRectCount;
|
||||
filter = moveRectFilter;
|
||||
}
|
||||
// mouse
|
||||
else if (
|
||||
monitor.isCursorVisible &&
|
||||
cursorCoord != preCursorCoord_ &&
|
||||
(cursorCoord - preCursorCoord_).magnitude > 5 &&
|
||||
monitor.cursorX >= 0 &&
|
||||
monitor.cursorY >= 0)
|
||||
{
|
||||
coord = cursorCoord;
|
||||
filter = mouseFilter;
|
||||
}
|
||||
// dirty rect
|
||||
else if (monitor.dirtyRectCount > 0) {
|
||||
var totalWeights = 0f;
|
||||
foreach (var rect in monitor.dirtyRects) {
|
||||
var center = new Vector2(
|
||||
(rect.right + rect.left) / 2,
|
||||
(rect.bottom + rect.top) / 2);
|
||||
var weight = 1f / Mathf.Sqrt((rect.right - rect.left) * (rect.bottom - rect.top));
|
||||
coord += center * weight;
|
||||
totalWeights += weight;
|
||||
}
|
||||
coord /= totalWeights;
|
||||
filter = dirtyRectFilter;
|
||||
}
|
||||
// no event
|
||||
else {
|
||||
coord = new Vector2(monitor.width / 2, monitor.height / 2);
|
||||
filter = noEventFilter;
|
||||
}
|
||||
|
||||
var cf = (filter / ((1f / 60) / Time.deltaTime));
|
||||
var vf = (velocityFilter / ((1f / 60) / Time.deltaTime));
|
||||
var targetCoord = averageCoord_ + (coord - averageCoord_) * cf;
|
||||
var targetVelocity = targetCoord - averageCoord_;
|
||||
|
||||
if (float.IsNaN(targetCoord.x) || float.IsNaN(targetCoord.y)) return;
|
||||
if (float.IsNaN(targetVelocity.x) || float.IsNaN(targetVelocity.y)) return;
|
||||
|
||||
averageCoordVelocity_ += (targetVelocity - averageCoordVelocity_) * vf;
|
||||
averageCoord_ += averageCoordVelocity_;
|
||||
averageCoord_.x = Mathf.Clamp(averageCoord_.x, 0, monitor.width);
|
||||
averageCoord_.y = Mathf.Clamp(averageCoord_.y, 0, monitor.height);
|
||||
|
||||
preCursorCoord_ = cursorCoord;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
CalcAveragePos();
|
||||
DebugDraw();
|
||||
}
|
||||
|
||||
void DebugDraw()
|
||||
{
|
||||
if (drawAveragePos) DrawAveragePos();
|
||||
if (drawDirtyRects) DrawDirtyRects();
|
||||
if (drawMoveRects) DrawMoveRects();
|
||||
}
|
||||
|
||||
void DrawRect(uDesktopDuplication.RECT rect, Color color)
|
||||
{
|
||||
var p0 = GetWorldPositionFromCoord(rect.left, rect.top);
|
||||
var p1 = GetWorldPositionFromCoord(rect.right, rect.top);
|
||||
var p2 = GetWorldPositionFromCoord(rect.right, rect.bottom);
|
||||
var p3 = GetWorldPositionFromCoord(rect.left, rect.bottom);
|
||||
|
||||
Debug.DrawLine(p0, p1, color);
|
||||
Debug.DrawLine(p1, p2, color);
|
||||
Debug.DrawLine(p2, p3, color);
|
||||
Debug.DrawLine(p3, p0, color);
|
||||
}
|
||||
|
||||
void DrawAveragePos()
|
||||
{
|
||||
Debug.DrawLine(transform.position, averagePos, Color.yellow);
|
||||
}
|
||||
|
||||
void DrawMoveRects()
|
||||
{
|
||||
foreach (var rect in uddTexture_.monitor.moveRects) {
|
||||
DrawRect(rect.source, Color.blue);
|
||||
DrawRect(rect.destination, Color.green);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawDirtyRects()
|
||||
{
|
||||
foreach (var rect in uddTexture_.monitor.dirtyRects) {
|
||||
DrawRect(rect, Color.red);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 52d402bdca823cc42925c9d65993ee59
|
||||
timeCreated: 1479546123
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,18 +1,22 @@
|
||||
using UnityEngine;
|
||||
|
||||
[RequireComponent(typeof(uDesktopDuplication.Texture)),
|
||||
RequireComponent(typeof(uDesktopDuplication.Cursor))]
|
||||
public class Loupe : MonoBehaviour
|
||||
{
|
||||
private uDesktopDuplication.Texture uddTexture_;
|
||||
private uDesktopDuplication.Cursor uddCursor_;
|
||||
public float zoom = 3f;
|
||||
public float aspect = 1f;
|
||||
|
||||
void Start()
|
||||
{
|
||||
uddTexture_ = GetComponent<uDesktopDuplication.Texture>();
|
||||
uddCursor_ = GetComponent<uDesktopDuplication.Cursor>();
|
||||
uddTexture_.useClip = true;
|
||||
}
|
||||
|
||||
void Update()
|
||||
void LateUpdate()
|
||||
{
|
||||
CheckVariables();
|
||||
|
||||
@@ -21,16 +25,17 @@ 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;
|
||||
}
|
||||
|
||||
var monitor = uddTexture_.monitor;
|
||||
var cursorX = monitor.isCursorVisible ? monitor.cursorX : monitor.systemCursorX;
|
||||
var cursorY = monitor.isCursorVisible ? monitor.cursorY : monitor.systemCursorY;
|
||||
|
||||
var x = (float)cursorX / monitor.width;
|
||||
var y = (float)cursorY / monitor.height;
|
||||
var x = monitor.isCursorVisible ?
|
||||
uddCursor_.coord.x :
|
||||
(float)monitor.systemCursorX / monitor.width;
|
||||
var y = monitor.isCursorVisible ?
|
||||
uddCursor_.coord.y :
|
||||
(float)monitor.systemCursorY / monitor.height;
|
||||
var w = 1f / zoom;
|
||||
var h = w / aspect * monitor.aspect;
|
||||
x = Mathf.Clamp(x - w / 2, 0f, 1f - w);
|
||||
@@ -40,7 +45,7 @@ public class Loupe : MonoBehaviour
|
||||
}
|
||||
|
||||
void CheckVariables()
|
||||
{
|
||||
{
|
||||
if (zoom < 1f) zoom = 1f;
|
||||
if (aspect < 0.01f) aspect = 0.01f;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
using UnityEngine;
|
||||
|
||||
[RequireComponent(typeof(MultipleMonitorCreator))]
|
||||
public class MultipleMonitorAnalyzer : MonoBehaviour
|
||||
{
|
||||
MultipleMonitorCreator creator_;
|
||||
Vector3 gazePoint_ = Vector3.zero;
|
||||
public Vector3 gazePoint
|
||||
{
|
||||
get { return gazePoint_; }
|
||||
private set
|
||||
{
|
||||
gazePoint_ += (value - gazePoint) * (gazePointFilter / (1f / 60 / Time.deltaTime));
|
||||
}
|
||||
}
|
||||
|
||||
[Header("Filters")]
|
||||
[Range(0f, 1f)] public float gazePointFilter = 0.1f;
|
||||
[Range(0f, 1f)] public float moveRectFilter = 0.05f;
|
||||
[Range(0f, 1f)] public float mouseFilter = 0.05f;
|
||||
[Range(0f, 1f)] public float dirtyRectFilter = 0.01f;
|
||||
[Range(0f, 1f)] public float noEventFilter = 0.01f;
|
||||
[Range(0f, 1f)] public float velocityFilter = 0.1f;
|
||||
|
||||
[Header("Debug")]
|
||||
[SerializeField] bool drawGazePoint;
|
||||
[SerializeField] bool drawAveragePos;
|
||||
[SerializeField] bool drawMoveRects;
|
||||
[SerializeField] bool drawDirtyRects;
|
||||
|
||||
void Start()
|
||||
{
|
||||
creator_ = GetComponent<MultipleMonitorCreator>();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
var cursorMonitorId = uDesktopDuplication.Manager.cursorMonitorId;
|
||||
for (int i = 0; i < creator_.monitors.Count; ++i) {
|
||||
var info = creator_.monitors[i];
|
||||
var analyzer =
|
||||
info.gameObject.GetComponent<GazePointAnalyzer>() ??
|
||||
info.gameObject.AddComponent<GazePointAnalyzer>();
|
||||
UpdateAnalyzer(analyzer);
|
||||
if (info.uddTexture.monitorId == cursorMonitorId) {
|
||||
gazePoint = analyzer.averagePos;
|
||||
}
|
||||
}
|
||||
|
||||
if (drawGazePoint) DrawGazePoint();
|
||||
}
|
||||
|
||||
void DrawGazePoint()
|
||||
{
|
||||
Debug.DrawLine(transform.position, gazePoint, Color.magenta);
|
||||
}
|
||||
|
||||
void UpdateAnalyzer(GazePointAnalyzer analyzer)
|
||||
{
|
||||
analyzer.moveRectFilter = moveRectFilter;
|
||||
analyzer.mouseFilter = mouseFilter;
|
||||
analyzer.dirtyRectFilter = dirtyRectFilter;
|
||||
analyzer.noEventFilter = noEventFilter;
|
||||
analyzer.velocityFilter = velocityFilter;
|
||||
analyzer.drawAveragePos = drawAveragePos;
|
||||
analyzer.drawMoveRects = drawMoveRects;
|
||||
analyzer.drawDirtyRects = drawDirtyRects;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dbdfccef47068644b81b671bb4e6e166
|
||||
timeCreated: 1479620455
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,22 +1,47 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
using System.Collections.Generic;
|
||||
using MeshForwardDirection = uDesktopDuplication.Texture.MeshForwardDirection;
|
||||
using MonitorState = uDesktopDuplication.MonitorState;
|
||||
|
||||
public class MultipleMonitorCreator : MonoBehaviour
|
||||
{
|
||||
[SerializeField] GameObject monitorPrefab;
|
||||
[SerializeField] bool removeIfUnsupported = true;
|
||||
[SerializeField] float removeWaitDuration = 5f;
|
||||
bool hasMonitorUnsupportStateChecked = false;
|
||||
float removeWaitTimer_ = 0f;
|
||||
[Tooltip("Create monitors using this prefab.")]
|
||||
public GameObject monitorPrefab;
|
||||
|
||||
public enum MeshForwardDirection { Y, Z }
|
||||
[Tooltip("Please specify the upper vector direction of the mesh (e.g. Plane's upper direction is Y.)")]
|
||||
public enum ScaleMode
|
||||
{
|
||||
Real,
|
||||
Fixed,
|
||||
Pixel,
|
||||
}
|
||||
|
||||
[Tooltip("Real: DPI-based real scale \nFixed: Same width \nPixel: bigger if screen resolution is high.")]
|
||||
public ScaleMode scaleMode = ScaleMode.Fixed;
|
||||
|
||||
[Tooltip("Use this scale as width if scaleMode is Fixed.")]
|
||||
public 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.")]
|
||||
public bool removeIfUnsupported = true;
|
||||
|
||||
[Tooltip("Remove unsupported monitors automatically after removeWaitDuration.")]
|
||||
public float removeWaitDuration = 5f;
|
||||
|
||||
[Tooltip("Remove all childrens (for debug).")]
|
||||
public bool removeChildrenWhenClear = true;
|
||||
|
||||
bool hasMonitorUnsupportStateChecked_ = false;
|
||||
float removeWaitTimer_ = 0f;
|
||||
|
||||
public class MonitorInfo
|
||||
{
|
||||
public GameObject gameObject { get; set; }
|
||||
public Quaternion originalRotation { get; set; }
|
||||
public Vector3 originalLocalScale { get; set; }
|
||||
public uDesktopDuplication.Texture uddTexture { get; set; }
|
||||
public Mesh mesh { get; set; }
|
||||
}
|
||||
@@ -24,6 +49,15 @@ public class MultipleMonitorCreator : MonoBehaviour
|
||||
private List<MonitorInfo> monitors_ = new List<MonitorInfo>();
|
||||
public List<MonitorInfo> monitors { get { return monitors_; } }
|
||||
|
||||
public class SavedMonitorInfo
|
||||
{
|
||||
public float widthScale = 1f;
|
||||
public float heightScale = 1f;
|
||||
}
|
||||
|
||||
private List<SavedMonitorInfo> savedInfoList_ = new List<SavedMonitorInfo>();
|
||||
public List<SavedMonitorInfo> savedInfoList { get { return savedInfoList_; } }
|
||||
|
||||
void Start()
|
||||
{
|
||||
uDesktopDuplication.Manager.CreateInstance();
|
||||
@@ -35,6 +69,10 @@ public class MultipleMonitorCreator : MonoBehaviour
|
||||
if (removeIfUnsupported) {
|
||||
RemoveUnsupportedDisplayAfterRemoveTimer();
|
||||
}
|
||||
|
||||
if (uDesktopDuplication.Manager.monitorCount != monitors.Count) {
|
||||
Recreate();
|
||||
}
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
@@ -49,23 +87,23 @@ public class MultipleMonitorCreator : MonoBehaviour
|
||||
|
||||
void RemoveUnsupportedDisplayAfterRemoveTimer()
|
||||
{
|
||||
if (!hasMonitorUnsupportStateChecked) {
|
||||
if (!hasMonitorUnsupportStateChecked_) {
|
||||
removeWaitTimer_ += Time.deltaTime;
|
||||
if (removeWaitTimer_ > removeWaitDuration) {
|
||||
hasMonitorUnsupportStateChecked = true;
|
||||
foreach (var info in monitors_) {
|
||||
if (info.uddTexture.monitor.state == uDesktopDuplication.MonitorState.Unsupported) {
|
||||
hasMonitorUnsupportStateChecked_ = true;
|
||||
foreach (var info in monitors) {
|
||||
if (info.uddTexture.monitor.state == MonitorState.Unsupported) {
|
||||
Destroy(info.gameObject);
|
||||
}
|
||||
}
|
||||
monitors_.RemoveAll(info => info.uddTexture.monitor.state == uDesktopDuplication.MonitorState.Unsupported);
|
||||
monitors.RemoveAll(info => info.uddTexture.monitor.state == MonitorState.Unsupported);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ResetRemoveTimer()
|
||||
{
|
||||
hasMonitorUnsupportStateChecked = false;
|
||||
hasMonitorUnsupportStateChecked_ = false;
|
||||
removeWaitTimer_ = 0f;
|
||||
}
|
||||
|
||||
@@ -74,11 +112,19 @@ public class MultipleMonitorCreator : MonoBehaviour
|
||||
ResetRemoveTimer();
|
||||
|
||||
// Create monitors
|
||||
for (int i = 0; i < uDesktopDuplication.Manager.monitorCount; ++i) {
|
||||
var n = uDesktopDuplication.Manager.monitors.Count;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
// Create monitor obeject
|
||||
var go = Instantiate(monitorPrefab);
|
||||
go.name = "Monitor " + i;
|
||||
|
||||
// Saved infomation
|
||||
if (savedInfoList.Count == i) {
|
||||
savedInfoList.Add(new SavedMonitorInfo());
|
||||
Assert.AreEqual(i, savedInfoList.Count - 1);
|
||||
}
|
||||
var savedInfo = savedInfoList[i];
|
||||
|
||||
// Expand AABB
|
||||
var mesh = go.GetComponent<MeshFilter>().mesh; // clone
|
||||
var aabbScale = mesh.bounds.size;
|
||||
@@ -92,10 +138,29 @@ public class MultipleMonitorCreator : MonoBehaviour
|
||||
var monitor = texture.monitor;
|
||||
|
||||
// Set width / height
|
||||
float width = 1f, height = 1f;
|
||||
switch (scaleMode) {
|
||||
case ScaleMode.Real:
|
||||
width = monitor.widthMeter;
|
||||
height = monitor.heightMeter;
|
||||
break;
|
||||
case ScaleMode.Fixed:
|
||||
width = scale * (monitor.isHorizontal ? monitor.aspect : 1f);
|
||||
height = scale * (monitor.isHorizontal ? 1f : 1f / monitor.aspect);
|
||||
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;
|
||||
}
|
||||
|
||||
width *= savedInfo.widthScale;
|
||||
height *= savedInfo.heightScale;
|
||||
|
||||
if (meshForwardDirection == MeshForwardDirection.Y) {
|
||||
go.transform.localScale = new Vector3(monitor.widthMeter, go.transform.localScale.y, monitor.heightMeter);
|
||||
go.transform.localScale = new Vector3(width, go.transform.localScale.y, height);
|
||||
} else {
|
||||
go.transform.localScale = new Vector3(monitor.widthMeter, monitor.heightMeter, go.transform.localScale.z);
|
||||
go.transform.localScale = new Vector3(width, height, go.transform.localScale.z);
|
||||
}
|
||||
|
||||
// Set parent as this object
|
||||
@@ -105,24 +170,31 @@ public class MultipleMonitorCreator : MonoBehaviour
|
||||
var info = new MonitorInfo();
|
||||
info.gameObject = go;
|
||||
info.originalRotation = go.transform.rotation;
|
||||
info.originalLocalScale = go.transform.localScale;
|
||||
info.uddTexture = texture;
|
||||
info.mesh = mesh;
|
||||
monitors_.Add(info);
|
||||
monitors.Add(info);
|
||||
}
|
||||
|
||||
// Sort monitors in coordinate order
|
||||
monitors_.Sort((a, b) => a.uddTexture.monitor.left - b.uddTexture.monitor.left);
|
||||
monitors.Sort((a, b) => a.uddTexture.monitor.left - b.uddTexture.monitor.left);
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
foreach (var info in monitors_) {
|
||||
foreach (var info in monitors) {
|
||||
Destroy(info.gameObject);
|
||||
}
|
||||
monitors_.Clear();
|
||||
if (removeChildrenWhenClear) {
|
||||
for (int i = 0; i < transform.childCount; ++i) {
|
||||
Destroy(transform.GetChild(i).gameObject);
|
||||
}
|
||||
}
|
||||
monitors.Clear();
|
||||
}
|
||||
|
||||
void Recreate()
|
||||
[ContextMenu("Recreate")]
|
||||
public void Recreate()
|
||||
{
|
||||
Clear();
|
||||
Create();
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
public class MultipleMonitorLayouter : MonoBehaviour
|
||||
{
|
||||
protected MultipleMonitorCreator creator_;
|
||||
[SerializeField] bool updateEveryFrame = true;
|
||||
[SerializeField] protected float margin = 0.1f;
|
||||
public bool updateEveryFrame = true;
|
||||
public float margin = 0.1f;
|
||||
[Range(0f, 10f)] public float thickness = 1f;
|
||||
|
||||
void Start()
|
||||
{
|
||||
@@ -15,16 +16,13 @@ public class MultipleMonitorLayouter : MonoBehaviour
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
margin = Mathf.Max(margin, 0f);
|
||||
|
||||
if (updateEveryFrame) {
|
||||
Layout();
|
||||
}
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
margin = Mathf.Max(margin, 0f);
|
||||
}
|
||||
|
||||
protected virtual void Layout()
|
||||
{
|
||||
var monitors = creator_.monitors;
|
||||
@@ -32,19 +30,23 @@ public class MultipleMonitorLayouter : MonoBehaviour
|
||||
|
||||
var totalWidth = 0f;
|
||||
foreach (var info in monitors) {
|
||||
var width = info.uddTexture.monitor.widthMeter * (info.mesh.bounds.extents.x * 2f);
|
||||
var width = info.gameObject.transform.localScale.x * (info.mesh.bounds.extents.x * 2f);
|
||||
totalWidth += width;
|
||||
}
|
||||
totalWidth += margin * (n - 1);
|
||||
|
||||
var x = -totalWidth / 2;
|
||||
|
||||
foreach (var info in creator_.monitors) {
|
||||
var monitor = info.uddTexture.monitor;
|
||||
x += (monitor.widthMeter * info.mesh.bounds.extents.x);
|
||||
foreach (var info in monitors) {
|
||||
var width = info.gameObject.transform.localScale.x;
|
||||
x += (width * info.mesh.bounds.extents.x);
|
||||
info.gameObject.transform.localPosition = new Vector3(x, 0f, 0f);
|
||||
info.gameObject.transform.localRotation = info.originalRotation;
|
||||
x += (monitor.widthMeter * info.mesh.bounds.extents.x) + margin;
|
||||
x += (width * info.mesh.bounds.extents.x) + margin;
|
||||
}
|
||||
|
||||
foreach (var info in monitors) {
|
||||
info.uddTexture.thickness = thickness;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
using UnityEngine;
|
||||
using MeshForwardDirection = uDesktopDuplication.Texture.MeshForwardDirection;
|
||||
|
||||
public class MultipleMonitorRoundLayouter : MultipleMonitorLayouter
|
||||
{
|
||||
[SerializeField] float radius = 10f;
|
||||
public float radius = 10f;
|
||||
public Vector3 offsetAngle = Vector3.zero;
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
foreach (var info in creator_.monitors) {
|
||||
info.uddTexture.bend = uDesktopDuplication.Texture.Bend.Off;
|
||||
info.uddTexture.bend = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,32 +18,61 @@ public class MultipleMonitorRoundLayouter : MultipleMonitorLayouter
|
||||
var monitors = creator_.monitors;
|
||||
var n = monitors.Count;
|
||||
|
||||
// Keep the local scale z of monitors as 1 to bend them correctly.
|
||||
// And save width / height to use same values after reinitialization.
|
||||
for (int i = 0; i < n; ++i) {
|
||||
var info = monitors[i];
|
||||
var savedInfo = creator_.savedInfoList[i];
|
||||
var scale = info.gameObject.transform.localScale;
|
||||
if (creator_.meshForwardDirection == MeshForwardDirection.Y) {
|
||||
scale.y = 1f;
|
||||
savedInfo.widthScale = scale.x / info.originalLocalScale.x;
|
||||
savedInfo.heightScale = scale.z / info.originalLocalScale.z;
|
||||
} else {
|
||||
scale.z = 1f;
|
||||
savedInfo.widthScale = scale.x / info.originalLocalScale.x;
|
||||
savedInfo.heightScale = scale.y / info.originalLocalScale.y;
|
||||
}
|
||||
info.gameObject.transform.localScale = scale;
|
||||
}
|
||||
|
||||
// keep thicness plus value.
|
||||
thickness = Mathf.Max(thickness, 0f);
|
||||
|
||||
// calculate total width
|
||||
var totalWidth = 0f;
|
||||
foreach (var info in monitors) {
|
||||
var width = info.uddTexture.monitor.widthMeter * (info.mesh.bounds.extents.x * 2f);
|
||||
var width = info.gameObject.transform.localScale.x * (info.mesh.bounds.extents.x * 2f);
|
||||
totalWidth += width;
|
||||
}
|
||||
totalWidth += margin * (n - 1);
|
||||
|
||||
// expand radius if total width is larger than the circumference.
|
||||
radius = Mathf.Max(radius, (totalWidth + margin) / (2 * Mathf.PI));
|
||||
|
||||
// total angle of monitors
|
||||
var totalAngle = totalWidth / radius;
|
||||
|
||||
// layout
|
||||
float angle = -totalAngle / 2;
|
||||
var offsetRot = Quaternion.Euler(offsetAngle);
|
||||
foreach (var info in monitors) {
|
||||
var uddTex = info.uddTexture;
|
||||
var width = uddTex.monitor.widthMeter * (info.mesh.bounds.extents.x * 2f);
|
||||
var width = info.gameObject.transform.localScale.x * (info.mesh.bounds.extents.x * 2f);
|
||||
|
||||
angle += (width / radius) * 0.5f;
|
||||
uddTex.transform.localPosition = radius * new Vector3(Mathf.Sin(angle), 0f, Mathf.Cos(angle) - 1f);
|
||||
uddTex.transform.localRotation = Quaternion.AngleAxis(angle * Mathf.Rad2Deg, Vector3.up) * info.originalRotation;
|
||||
var pos = radius * new Vector3(Mathf.Sin(angle), 0f, Mathf.Cos(angle) - 1f);
|
||||
pos += radius * Vector3.forward;
|
||||
pos = offsetRot * pos;
|
||||
pos -= radius * Vector3.forward;
|
||||
uddTex.transform.localPosition = pos;
|
||||
uddTex.transform.localRotation = offsetRot * Quaternion.AngleAxis(angle * Mathf.Rad2Deg, Vector3.up) * info.originalRotation;
|
||||
angle += (width * 0.5f + margin) / radius;
|
||||
|
||||
if (creator_.meshForwardDirection == MultipleMonitorCreator.MeshForwardDirection.Y) {
|
||||
uddTex.bend = uDesktopDuplication.Texture.Bend.Y;
|
||||
} else {
|
||||
uddTex.bend = uDesktopDuplication.Texture.Bend.Z;
|
||||
}
|
||||
uddTex.bend = true;
|
||||
uddTex.meshForwardDirection = creator_.meshForwardDirection;
|
||||
uddTex.radius = radius;
|
||||
uddTex.thickness = thickness;
|
||||
uddTex.width = uddTex.transform.localScale.x;
|
||||
}
|
||||
}
|
||||
@@ -49,7 +80,12 @@ public class MultipleMonitorRoundLayouter : MultipleMonitorLayouter
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
DebugDraw();
|
||||
}
|
||||
|
||||
void DebugDraw()
|
||||
{
|
||||
// draw the circumference in the scene view.
|
||||
var scale = transform.localScale.x;
|
||||
var center = transform.position - Vector3.forward * radius * scale;
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
|
||||
@@ -6,10 +6,12 @@ public class ToggleMonitors : MonoBehaviour
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.Tab)) {
|
||||
var texture = GetComponent<uDesktopDuplication.Texture>();
|
||||
var id = texture.monitorId;
|
||||
var n = uDesktopDuplication.Manager.monitorCount;
|
||||
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) {
|
||||
texture.monitorId--;
|
||||
texture.monitorId = (id - 1 < 0) ? 0 : (id - 1);
|
||||
} else {
|
||||
texture.monitorId++;
|
||||
texture.monitorId = (id + 1 >= n) ? (n - 1) : (id + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
Binary file not shown.
@@ -5,13 +5,13 @@ 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;
|
||||
|
||||
Vector3 worldPosition { get; set; }
|
||||
public Vector3 worldPosition { get; set; }
|
||||
public Vector2 coord { get; set; }
|
||||
|
||||
private Texture uddTexture_;
|
||||
private Monitor monitor { get { return uddTexture_.monitor; } }
|
||||
@@ -66,6 +66,8 @@ public class Cursor : MonoBehaviour
|
||||
var w = (float)monitor.cursorShapeWidth / monitor.width;
|
||||
var h = (float)monitor.cursorShapeHeight / monitor.height;
|
||||
uddTexture_.material.SetVector("_CursorPositionScale", new Vector4(x, y, w, h));
|
||||
|
||||
coord = new Vector2(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,12 @@ public enum Message
|
||||
{
|
||||
None = -1,
|
||||
Reinitialized = 0,
|
||||
TextureSizeChanged = 1,
|
||||
}
|
||||
|
||||
public enum CursorShapeType
|
||||
{
|
||||
Unspecified = 0,
|
||||
MonoChrome = 1,
|
||||
Color = 2,
|
||||
MaskedColor = 4,
|
||||
@@ -29,14 +31,16 @@ 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,
|
||||
}
|
||||
|
||||
public enum DebugMode
|
||||
@@ -46,6 +50,26 @@ public enum DebugMode
|
||||
UnityLog = 2, /* currently has bug when app exits. */
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RECT
|
||||
{
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int left;
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int top;
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int right;
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int bottom;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct DXGI_OUTDUPL_MOVE_RECT
|
||||
{
|
||||
public RECT source;
|
||||
public RECT destination;
|
||||
}
|
||||
|
||||
public static class Lib
|
||||
{
|
||||
public delegate void MessageHandler(Message message);
|
||||
@@ -75,6 +99,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();
|
||||
@@ -128,6 +154,14 @@ public static class Lib
|
||||
public static extern void GetCursorTexture(int id, System.IntPtr ptr);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int SetTexturePtr(int id, IntPtr ptr);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetMoveRectCount(int id);
|
||||
[DllImport("uDesktopDuplication", EntryPoint = "GetMoveRects")]
|
||||
private static extern IntPtr GetMoveRects_Internal(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetDirtyRectCount(int id);
|
||||
[DllImport("uDesktopDuplication", EntryPoint = "GetDirtyRects")]
|
||||
private static extern IntPtr GetDirtyRects_Internal(int id);
|
||||
|
||||
public static string GetName(int id)
|
||||
{
|
||||
@@ -135,6 +169,32 @@ public static class Lib
|
||||
GetName(id, buf, buf.Capacity);
|
||||
return buf.ToString();
|
||||
}
|
||||
|
||||
public static DXGI_OUTDUPL_MOVE_RECT[] GetMoveRects(int id)
|
||||
{
|
||||
var count = GetMoveRectCount(id);
|
||||
var rects = new DXGI_OUTDUPL_MOVE_RECT[count];
|
||||
var ptr = GetMoveRects_Internal(id);
|
||||
var size = Marshal.SizeOf(typeof(DXGI_OUTDUPL_MOVE_RECT));
|
||||
for (int i = 0; i < count; ++i) {
|
||||
var data = new IntPtr(ptr.ToInt64() + size * i);
|
||||
rects[i] = (DXGI_OUTDUPL_MOVE_RECT)Marshal.PtrToStructure(data, typeof(DXGI_OUTDUPL_MOVE_RECT));
|
||||
}
|
||||
return rects;
|
||||
}
|
||||
|
||||
public static RECT[] GetDirtyRects(int id)
|
||||
{
|
||||
var count = GetDirtyRectCount(id);
|
||||
var rects = new RECT[count];
|
||||
var ptr = GetDirtyRects_Internal(id);
|
||||
var size = Marshal.SizeOf(typeof(RECT));
|
||||
for (int i = 0; i < count; ++i) {
|
||||
var data = new IntPtr(ptr.ToInt64() + size * i);
|
||||
rects[i] = (RECT)Marshal.PtrToStructure(data, typeof(RECT));
|
||||
}
|
||||
return rects;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
@@ -45,26 +57,11 @@ public class Manager : MonoBehaviour
|
||||
private Coroutine renderCoroutine_ = null;
|
||||
private bool shouldReinitialize_ = false;
|
||||
private float reinitializationTimer_ = 0f;
|
||||
private bool isFirstFrame_ = true;
|
||||
|
||||
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) {
|
||||
@@ -76,25 +73,32 @@ public class Manager : MonoBehaviour
|
||||
|
||||
void Awake()
|
||||
{
|
||||
Lib.SetDebugMode(debugMode);
|
||||
Lib.InitializeUDD();
|
||||
|
||||
if (instance_ != null) return;
|
||||
// for simple singleton
|
||||
if (instance_ != null) {
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
instance_ = this;
|
||||
|
||||
CreateMonitors();
|
||||
|
||||
Lib.SetDebugMode(debugMode);
|
||||
Lib.SetTimeout(desktopDuplicationApiTimeout);
|
||||
Lib.InitializeUDD();
|
||||
|
||||
CreateMonitors();
|
||||
}
|
||||
|
||||
void OnApplicationQuit()
|
||||
{
|
||||
Lib.FinalizeUDD();
|
||||
DestroyMonitors();
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
renderCoroutine_ = StartCoroutine(OnRender());
|
||||
if (!isFirstFrame_) {
|
||||
Reinitialize();
|
||||
}
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
@@ -110,12 +114,13 @@ public class Manager : MonoBehaviour
|
||||
Lib.Update();
|
||||
ReinitializeIfNeeded();
|
||||
UpdateMessage();
|
||||
isFirstFrame_ = false;
|
||||
}
|
||||
|
||||
[ContextMenu("Reinitialize")]
|
||||
void Reinitialize()
|
||||
public void Reinitialize()
|
||||
{
|
||||
Debug.Log("[uDesktopDuplication] Reinitialize");
|
||||
Debug.Log("[uDD] Reinitialize");
|
||||
Lib.Reinitialize();
|
||||
CreateMonitors();
|
||||
if (onReinitialized != null) {
|
||||
@@ -125,20 +130,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 +167,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 +198,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
|
||||
{
|
||||
@@ -12,24 +11,36 @@ public class Monitor
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case MonitorState.Available:
|
||||
break;
|
||||
case MonitorState.InvalidArg:
|
||||
Debug.LogErrorFormat("[{0}:{1}] Invalid.", id, name);
|
||||
Debug.LogErrorFormat("[uDD] {0}:{1} => Invalid.", id, name);
|
||||
break;
|
||||
case MonitorState.AccessDenied:
|
||||
Debug.LogErrorFormat("[{0}:{1}] Access Denied.", id, name);
|
||||
Debug.LogWarningFormat("[uDD] {0}:{1} => Access Denied.", id, name);
|
||||
break;
|
||||
case MonitorState.Unsupported:
|
||||
Debug.LogErrorFormat("[{0}:{1}] Unsupported.", id, name);
|
||||
Debug.LogWarningFormat("[uDD] {0}:{1} => Unsupported.", id, name);
|
||||
break;
|
||||
case MonitorState.SessionDisconnected:
|
||||
Debug.LogErrorFormat("[{0}:{1}] Disconnected.", id, name);
|
||||
Debug.LogWarningFormat("[uDD] {0}:{1} => Disconnected.", id, name);
|
||||
break;
|
||||
case MonitorState.NotSet:
|
||||
Debug.LogErrorFormat("[{0}:{1}] Something wrong.", id, name);
|
||||
Debug.LogErrorFormat("[uDD] {0}:{1} => Something wrong.", id, name);
|
||||
break;
|
||||
default:
|
||||
Debug.LogErrorFormat("[uDD] {0}:{1} => Unknown error.", id, name);
|
||||
break;
|
||||
}
|
||||
|
||||
if (dpiX == 0 || dpiY == 0) {
|
||||
Debug.LogWarningFormat("[uDD] {0}:{1} => Could not get DPI", id, name);
|
||||
}
|
||||
}
|
||||
|
||||
~Monitor()
|
||||
{
|
||||
DestroyTexture();
|
||||
}
|
||||
|
||||
public int id
|
||||
@@ -191,23 +202,50 @@ public class Monitor
|
||||
get { return Lib.GetCursorShapeType(id); }
|
||||
}
|
||||
|
||||
public int moveRectCount
|
||||
{
|
||||
get { return Lib.GetMoveRectCount(id); }
|
||||
}
|
||||
|
||||
public DXGI_OUTDUPL_MOVE_RECT[] moveRects
|
||||
{
|
||||
get { return Lib.GetMoveRects(id); }
|
||||
}
|
||||
|
||||
public int dirtyRectCount
|
||||
{
|
||||
get { return Lib.GetDirtyRectCount(id); }
|
||||
}
|
||||
|
||||
public RECT[] dirtyRects
|
||||
{
|
||||
get { return Lib.GetDirtyRects(id); }
|
||||
}
|
||||
|
||||
public bool shouldBeUpdated
|
||||
{
|
||||
get;
|
||||
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_;
|
||||
}
|
||||
}
|
||||
@@ -225,7 +263,7 @@ public class Monitor
|
||||
Lib.GetCursorTexture(id, ptr);
|
||||
}
|
||||
|
||||
public void CreateTexture()
|
||||
public void CreateTextureIfNeeded()
|
||||
{
|
||||
if (!available) return;
|
||||
|
||||
@@ -233,13 +271,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) {
|
||||
@@ -247,13 +280,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_;
|
||||
@@ -13,12 +13,15 @@ public class Texture : MonoBehaviour
|
||||
set
|
||||
{
|
||||
monitor_ = value;
|
||||
material = GetComponent<Renderer>().material; // clone
|
||||
material.mainTexture = monitor_.texture;
|
||||
material.SetFloat("_Width", transform.localScale.x);
|
||||
if (monitor_ != null) {
|
||||
material = GetComponent<Renderer>().material; // clone
|
||||
material.mainTexture = monitor_.texture;
|
||||
material.SetFloat("_Width", transform.localScale.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int lastMonitorId_ = 0;
|
||||
public int monitorId
|
||||
{
|
||||
get { return monitor.id; }
|
||||
@@ -34,39 +37,48 @@ public class Texture : MonoBehaviour
|
||||
public Vector2 clipPos = Vector2.zero;
|
||||
public Vector2 clipScale = new Vector2(0.2f, 0.2f);
|
||||
|
||||
public enum Bend
|
||||
public enum MeshForwardDirection
|
||||
{
|
||||
Off = 0,
|
||||
Y = 1,
|
||||
Z = 2,
|
||||
Y = 0,
|
||||
Z = 1,
|
||||
}
|
||||
|
||||
public Bend bend
|
||||
public bool bend
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Bend)material.GetInt("_Bend");
|
||||
return material.GetInt("_Bend") != 0;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value) {
|
||||
material.EnableKeyword("BEND_ON");
|
||||
material.SetInt("_Bend", 1);
|
||||
} else {
|
||||
material.DisableKeyword("BEND_ON");
|
||||
material.SetInt("_Bend", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MeshForwardDirection meshForwardDirection
|
||||
{
|
||||
get
|
||||
{
|
||||
return (MeshForwardDirection)material.GetInt("_Forward");
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (value) {
|
||||
case Bend.Off:
|
||||
material.SetInt("_Bend", 0);
|
||||
material.DisableKeyword("_BEND_OFF");
|
||||
material.DisableKeyword("_BEND_Y");
|
||||
material.DisableKeyword("_BEND_Z");
|
||||
case MeshForwardDirection.Y:
|
||||
material.SetInt("_Forward", 0);
|
||||
material.EnableKeyword("_FORWARD_Y");
|
||||
material.DisableKeyword("_FORWARD_Z");
|
||||
break;
|
||||
case Bend.Y:
|
||||
material.SetInt("_Bend", 1);
|
||||
material.DisableKeyword("_BEND_OFF");
|
||||
material.EnableKeyword("_BEND_Y");
|
||||
material.DisableKeyword("_BEND_Z");
|
||||
break;
|
||||
case Bend.Z:
|
||||
material.SetInt("_Bend", 2);
|
||||
material.DisableKeyword("_BEND_OFF");
|
||||
material.DisableKeyword("_BEND_Y");
|
||||
material.EnableKeyword("_BEND_Z");
|
||||
case MeshForwardDirection.Z:
|
||||
material.SetInt("_Forward", 1);
|
||||
material.DisableKeyword("_FORWARD_Y");
|
||||
material.EnableKeyword("_FORWARD_Z");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -84,6 +96,12 @@ public class Texture : MonoBehaviour
|
||||
set { material.SetFloat("_Width", value); }
|
||||
}
|
||||
|
||||
public float thickness
|
||||
{
|
||||
get { return material.GetFloat("_Thickness"); }
|
||||
set { material.SetFloat("_Thickness", value); }
|
||||
}
|
||||
|
||||
public Material material
|
||||
{
|
||||
get;
|
||||
@@ -92,10 +110,7 @@ public class Texture : MonoBehaviour
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (!GetComponent<Cursor>())
|
||||
{
|
||||
gameObject.AddComponent<Cursor>();
|
||||
}
|
||||
AddCursorIfNotAttached();
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
@@ -103,14 +118,44 @@ public class Texture : MonoBehaviour
|
||||
if (monitor == null) {
|
||||
monitor = Manager.primary;
|
||||
}
|
||||
Manager.onReinitialized += Reinitialize;
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
Manager.onReinitialized -= Reinitialize;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
KeepMonitor();
|
||||
monitor.shouldBeUpdated = true;
|
||||
UpdateMaterial();
|
||||
}
|
||||
|
||||
void KeepMonitor()
|
||||
{
|
||||
if (monitor == null) {
|
||||
Reinitialize();
|
||||
} else {
|
||||
lastMonitorId_ = monitorId;
|
||||
}
|
||||
}
|
||||
|
||||
void AddCursorIfNotAttached()
|
||||
{
|
||||
if (!GetComponent<Cursor>())
|
||||
{
|
||||
gameObject.AddComponent<Cursor>();
|
||||
}
|
||||
}
|
||||
|
||||
void Reinitialize()
|
||||
{
|
||||
// Monitor instance is released here when initialized.
|
||||
monitor = Manager.GetMonitor(lastMonitorId_);
|
||||
}
|
||||
|
||||
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,12 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6c7e8da181b03ce4fb343ffa33cbb929
|
||||
timeCreated: 1478934399
|
||||
timeCreated: 1479043365
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
icon: {fileID: 2800000, guid: 89616e59e1cccb346bd4e959257990ca, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@@ -92,18 +92,26 @@ inline fixed4 uddGetScreenTextureWithCursor(float2 uv)
|
||||
return color;
|
||||
}
|
||||
|
||||
inline void uddBendVertex(inout float4 v, half radius, half width)
|
||||
inline void uddBendVertex(inout float4 v, half radius, half width, half thickness)
|
||||
{
|
||||
#if !defined(_BEND_OFF)
|
||||
#ifdef BEND_ON
|
||||
half angle = width * v.x / radius;
|
||||
#ifdef _BEND_Y
|
||||
radius -= v.y;
|
||||
#ifdef _FORWARD_Z
|
||||
v.z *= thickness;
|
||||
radius += v.z;
|
||||
v.z -= radius * (1 - cos(angle));
|
||||
#elif _FORWARD_Y
|
||||
v.y *= thickness;
|
||||
radius += v.y;
|
||||
v.y += radius * (1 - cos(angle));
|
||||
#elif _BEND_Z
|
||||
radius -= v.z;
|
||||
v.z += radius * (1 - cos(angle));
|
||||
#endif
|
||||
v.x = radius * sin(angle) / width;
|
||||
#else
|
||||
#ifdef _FORWARD_Z
|
||||
v.y *= thickness;
|
||||
#elif _FORWARD_Y
|
||||
v.z *= thickness;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,10 @@ Properties
|
||||
_Color ("Color", Color) = (1, 1, 1, 1)
|
||||
_MainTex ("Texture", 2D) = "white" {}
|
||||
_CursorTex ("Cursor Texture", 2D) = "white" {}
|
||||
[KeywordEnum(Off, Y, Z)] _Bend("Bending", Int) = 0
|
||||
[PowerSlider(10.0)] _Radius("Radius", Range(1, 100)) = 30
|
||||
[KeywordEnum(Y, Z)] _Forward("Mesh Forward Direction", Int) = 0
|
||||
[Toggle(BEND_ON)] _Bend("Use Bend", Int) = 0
|
||||
[PowerSlider(10.0)] _Radius("Bend Radius", Range(1, 100)) = 30
|
||||
[PowerSlider(10.0)] _Thickness("Thickness", Range(0.01, 10)) = 1
|
||||
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
|
||||
}
|
||||
|
||||
@@ -24,11 +26,12 @@ CGINCLUDE
|
||||
|
||||
half _Radius;
|
||||
half _Width;
|
||||
half _Thickness;
|
||||
|
||||
v2f vert(appdata v)
|
||||
{
|
||||
v2f o;
|
||||
uddBendVertex(v.vertex, _Radius, _Width);
|
||||
uddBendVertex(v.vertex, _Radius, _Width, _Thickness);
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
|
||||
return o;
|
||||
@@ -50,7 +53,8 @@ Pass
|
||||
#pragma multi_compile ___ INVERT_Y
|
||||
#pragma multi_compile ___ ROTATE90 ROTATE180 ROTATE270
|
||||
#pragma multi_compile ___ USE_CLIP
|
||||
#pragma multi_compile _BEND_OFF _BEND_Y _BEND_Z
|
||||
#pragma multi_compile ___ BEND_ON
|
||||
#pragma multi_compile _FORWARD_Y _FORWARD_Z
|
||||
ENDCG
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,10 @@ Properties
|
||||
_MainTex ("Texture", 2D) = "white" {}
|
||||
_Mask ("Mask", Range(0, 1)) = 0.1
|
||||
_CursorTex ("Cursor Texture", 2D) = "white" {}
|
||||
[KeywordEnum(Off, Y, Z)] _Bend("Bending", Int) = 0
|
||||
[PowerSlider(10.0)] _Radius("Radius", Range(1, 100)) = 30
|
||||
[KeywordEnum(Y, Z)] _Forward("Mesh Forward Direction", Int) = 0
|
||||
[Toggle(BEND_ON)] _Bend("Use Bend", Int) = 0
|
||||
[PowerSlider(10.0)] _Radius("Bend Radius", Range(1, 100)) = 30
|
||||
[PowerSlider(10.0)] _Thickness("Thickness", Range(0.01, 10)) = 1
|
||||
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
|
||||
}
|
||||
|
||||
@@ -28,11 +30,12 @@ CGINCLUDE
|
||||
fixed _Mask;
|
||||
half _Radius;
|
||||
half _Width;
|
||||
half _Thickness;
|
||||
|
||||
v2f vert(appdata v)
|
||||
{
|
||||
v2f o;
|
||||
uddBendVertex(v.vertex, _Radius, _Width);
|
||||
uddBendVertex(v.vertex, _Radius, _Width, _Thickness);
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
|
||||
return o;
|
||||
|
||||
@@ -6,8 +6,10 @@ Properties
|
||||
_Color ("Color", Color) = (1, 1, 1, 1)
|
||||
_MainTex ("Texture", 2D) = "white" {}
|
||||
_CursorTex ("Cursor Texture", 2D) = "white" {}
|
||||
[KeywordEnum(Off, Y, Z)] _Bend("Bending", Int) = 0
|
||||
[PowerSlider(10.0)] _Radius("Radius", Range(1, 100)) = 30
|
||||
[KeywordEnum(Y, Z)] _Forward("Mesh Forward Direction", Int) = 0
|
||||
[Toggle(BEND_ON)] _Bend("Use Bend", Int) = 0
|
||||
[PowerSlider(10.0)] _Radius("Bend Radius", Range(1, 100)) = 30
|
||||
[PowerSlider(10.0)] _Thickness("Thickness", Range(0.01, 10)) = 1
|
||||
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
|
||||
}
|
||||
|
||||
@@ -26,11 +28,12 @@ CGINCLUDE
|
||||
|
||||
half _Radius;
|
||||
half _Width;
|
||||
half _Thickness;
|
||||
|
||||
v2f vert(appdata v)
|
||||
{
|
||||
v2f o;
|
||||
uddBendVertex(v.vertex, _Radius, _Width);
|
||||
uddBendVertex(v.vertex, _Radius, _Width, _Thickness);
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
|
||||
return o;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -36,4 +36,4 @@ const std::unique_ptr<MonitorManager>& GetMonitorManager()
|
||||
void SendMessageToUnity(Message message)
|
||||
{
|
||||
g_messages.push(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -23,6 +22,78 @@ enum class Message
|
||||
{
|
||||
None = -1,
|
||||
Reinitialized = 0,
|
||||
TextureSizeChanged = 1,
|
||||
};
|
||||
|
||||
void SendMessageToUnity(Message message);
|
||||
void SendMessageToUnity(Message message);
|
||||
|
||||
|
||||
// Buffer
|
||||
template <class T>
|
||||
class Buffer
|
||||
{
|
||||
public:
|
||||
Buffer() {}
|
||||
~Buffer() {}
|
||||
|
||||
void ExpandIfNeeded(UINT size)
|
||||
{
|
||||
if (size > size_)
|
||||
{
|
||||
size_ = size;
|
||||
value_ = std::make_unique<T[]>(size);
|
||||
}
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
value_.reset();
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
UINT Size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
T* Get() const
|
||||
{
|
||||
return value_.get();
|
||||
}
|
||||
|
||||
T* Get(UINT offset) const
|
||||
{
|
||||
return (value_.get() + offset);
|
||||
}
|
||||
|
||||
template <class U>
|
||||
U* As() const
|
||||
{
|
||||
return reinterpret_cast<U*>(Get());
|
||||
}
|
||||
|
||||
template <class U>
|
||||
U* As(UINT offset) const
|
||||
{
|
||||
return reinterpret_cast<U*>(Get(offset));
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return value_ != nullptr;
|
||||
}
|
||||
|
||||
T operator [](UINT index) const
|
||||
{
|
||||
if (index >= size_)
|
||||
{
|
||||
Debug::Error("Array index out of range: ", index, size_);
|
||||
return T(0);
|
||||
}
|
||||
return value_[index];
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<T[]> value_;
|
||||
UINT size_ = 0;
|
||||
};
|
||||
@@ -1,6 +1,5 @@
|
||||
#include <d3d11.h>
|
||||
#include <wrl/client.h>
|
||||
#include "Common.h"
|
||||
#include "Debug.h"
|
||||
#include "MonitorManager.h"
|
||||
#include "Monitor.h"
|
||||
@@ -47,28 +46,26 @@ void Cursor::UpdateBuffer(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
|
||||
return;
|
||||
}
|
||||
|
||||
// Increase the buffer size if needed
|
||||
if (frameInfo.PointerShapeBufferSize > apiBufferSize_)
|
||||
apiBuffer_.ExpandIfNeeded(frameInfo.PointerShapeBufferSize);
|
||||
if (!apiBuffer_)
|
||||
{
|
||||
apiBufferSize_ = frameInfo.PointerShapeBufferSize;
|
||||
apiBuffer_ = std::make_unique<BYTE[]>(apiBufferSize_);
|
||||
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_.Size(),
|
||||
apiBuffer_.Get(),
|
||||
&bufferSize,
|
||||
&shapeInfo);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Debug::Error("Cursor::UpdateBuffer() => GetFramePointerShape() failed.");
|
||||
apiBuffer_.reset();
|
||||
apiBufferSize_ = 0;
|
||||
apiBuffer_.Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
shapeInfo_ = shapeInfo;
|
||||
@@ -82,66 +79,133 @@ void Cursor::UpdateTexture()
|
||||
return;
|
||||
}
|
||||
|
||||
// cursor type
|
||||
// Cursor information
|
||||
const bool isMono = GetType() == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME;
|
||||
const bool isColorMask = GetType() == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR;
|
||||
const auto cursorImageWidth = GetWidth();
|
||||
const auto cursorImageHeight = GetHeight();
|
||||
const auto cursorImagePitch = GetPitch();
|
||||
|
||||
// Size
|
||||
const auto w0 = GetWidth();
|
||||
const auto h0 = GetHeight();
|
||||
const auto p = GetPitch();
|
||||
auto w = w0;
|
||||
auto h = h0;
|
||||
// Captured area
|
||||
auto capturedImageWidth = cursorImageWidth;
|
||||
auto capturedImageHeight = cursorImageHeight;
|
||||
|
||||
// Convert the buffer given by API into BGRA32
|
||||
const UINT bgraBufferSize = w0 * h0 * 4;
|
||||
if (bgraBufferSize > bgra32BufferSize_)
|
||||
const UINT bgraBufferSize = cursorImageWidth * cursorImageHeight * 4;
|
||||
bgra32Buffer_.ExpandIfNeeded(bgraBufferSize);
|
||||
|
||||
// Check buffers
|
||||
if (!bgra32Buffer_ || !apiBuffer_)
|
||||
{
|
||||
bgra32BufferSize_ = bgraBufferSize;
|
||||
bgra32Buffer_ = std::make_unique<BYTE[]>(bgra32BufferSize_);
|
||||
return;
|
||||
}
|
||||
if (!bgra32Buffer_) return;
|
||||
|
||||
// If masked, copy the desktop image and merge it with masked image.
|
||||
if (isMono || isColorMask)
|
||||
{
|
||||
HRESULT hr;
|
||||
const auto monitorWidth = monitor_->GetWidth();
|
||||
const auto monitorHeight = monitor_->GetHeight();
|
||||
const auto monitorRot = static_cast<DXGI_MODE_ROTATION>(monitor_->GetRotation());
|
||||
const auto isVertical =
|
||||
monitorRot == DXGI_MODE_ROTATION_ROTATE90 ||
|
||||
monitorRot == DXGI_MODE_ROTATION_ROTATE270;
|
||||
const auto desktopImageWidth = !isVertical ? monitorWidth : monitorHeight;
|
||||
const auto desktopImageHeight = !isVertical ? monitorHeight : monitorWidth;
|
||||
|
||||
const auto mw = monitor_->GetWidth();
|
||||
const auto mh = monitor_->GetHeight();
|
||||
auto x = x_;
|
||||
auto y = y_;
|
||||
auto colMin = 0;
|
||||
auto colMax = w0;
|
||||
auto colMax = cursorImageWidth;
|
||||
auto rowMin = 0;
|
||||
auto rowMax = h0;
|
||||
auto rowMax = cursorImageHeight;
|
||||
|
||||
if (x < 0)
|
||||
{
|
||||
x = 0;
|
||||
w = w0 + x_;
|
||||
colMin = w0 - w;
|
||||
capturedImageWidth = cursorImageWidth + x_;
|
||||
colMin = cursorImageWidth - capturedImageWidth;
|
||||
}
|
||||
if (x + w >= mw)
|
||||
if (x + capturedImageWidth >= monitorWidth)
|
||||
{
|
||||
w = mw - x_;
|
||||
colMax = w;
|
||||
capturedImageWidth = monitorWidth - x_;
|
||||
colMax = capturedImageWidth;
|
||||
}
|
||||
if (y < 0)
|
||||
{
|
||||
y = 0;
|
||||
h = h0 + y_;
|
||||
rowMin = h0 - h;
|
||||
capturedImageHeight = cursorImageHeight + y_;
|
||||
rowMin = cursorImageHeight - capturedImageHeight;
|
||||
}
|
||||
if (y + h >= mh)
|
||||
if (y + capturedImageHeight >= monitorHeight)
|
||||
{
|
||||
h = mh - y_;
|
||||
rowMax = h;
|
||||
capturedImageHeight = monitorHeight - y_;
|
||||
rowMax = capturedImageHeight;
|
||||
}
|
||||
|
||||
D3D11_BOX box;
|
||||
box.front = 0;
|
||||
box.back = 1;
|
||||
|
||||
switch (monitorRot)
|
||||
{
|
||||
case DXGI_MODE_ROTATION_ROTATE90:
|
||||
{
|
||||
box.left = y;
|
||||
box.top = monitorWidth - x - capturedImageWidth;
|
||||
box.right = y + capturedImageWidth;
|
||||
box.bottom = monitorWidth - x;
|
||||
break;
|
||||
}
|
||||
case DXGI_MODE_ROTATION_ROTATE180:
|
||||
{
|
||||
box.left = monitorWidth - x - capturedImageWidth;
|
||||
box.top = monitorHeight - y - capturedImageHeight;
|
||||
box.right = monitorWidth - x;
|
||||
box.bottom = monitorHeight - y;
|
||||
break;
|
||||
}
|
||||
case DXGI_MODE_ROTATION_ROTATE270:
|
||||
{
|
||||
box.left = monitorHeight - y - capturedImageHeight;
|
||||
box.top = x;
|
||||
box.right = monitorHeight - y;
|
||||
box.bottom = x + capturedImageWidth;
|
||||
break;
|
||||
}
|
||||
case DXGI_MODE_ROTATION_IDENTITY:
|
||||
case DXGI_MODE_ROTATION_UNSPECIFIED:
|
||||
{
|
||||
box.left = x;
|
||||
box.top = y;
|
||||
box.right = x + capturedImageWidth;
|
||||
box.bottom = y + capturedImageHeight;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (box.left < 0 ||
|
||||
box.top < 0 ||
|
||||
box.right > static_cast<UINT>(desktopImageWidth) ||
|
||||
box.bottom > static_cast<UINT>(desktopImageHeight))
|
||||
{
|
||||
Debug::Error("Cursor::UpdateTexture() => box is out of area.");
|
||||
Debug::Error(
|
||||
" ",
|
||||
"(", box.left, ", ", box.top, ")",
|
||||
" ~ (", box.right, ", ", box.bottom, ") > ",
|
||||
"(", desktopImageWidth, ", ", desktopImageHeight, ")");
|
||||
return;
|
||||
}
|
||||
|
||||
if (monitor_->GetUnityTexture() == nullptr)
|
||||
{
|
||||
Debug::Error("Cursor::UpdateTexture() => Monitor::GetUnityTexture() is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
desc.Width = w;
|
||||
desc.Height = h;
|
||||
desc.Width = capturedImageWidth;
|
||||
desc.Height = capturedImageHeight;
|
||||
desc.MipLevels = 1;
|
||||
desc.ArraySize = 1;
|
||||
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
@@ -153,27 +217,12 @@ 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;
|
||||
}
|
||||
|
||||
D3D11_BOX box;
|
||||
box.front = 0;
|
||||
box.back = 1;
|
||||
box.left = x;
|
||||
box.top = y;
|
||||
box.right = x + w;
|
||||
box.bottom = y + h;
|
||||
|
||||
if (monitor_->GetUnityTexture() == nullptr)
|
||||
{
|
||||
Debug::Error("Cursor::UpdateTexture() => Monitor::GetUnityTexture() is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
ComPtr<ID3D11DeviceContext> context;
|
||||
GetDevice()->GetImmediateContext(&context);
|
||||
@@ -181,16 +230,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;
|
||||
@@ -200,8 +247,29 @@ void Cursor::UpdateTexture()
|
||||
const auto desktop32 = reinterpret_cast<UINT*>(mappedSurface.pBits);
|
||||
const UINT desktopPitch = mappedSurface.Pitch / sizeof(UINT);
|
||||
|
||||
// Take the monitor orientation into consideration.
|
||||
const auto getDesktop32 = [&](int col, int row)
|
||||
{
|
||||
switch (monitorRot)
|
||||
{
|
||||
case DXGI_MODE_ROTATION_ROTATE90:
|
||||
return desktop32[(capturedImageWidth - 1 - col) * desktopPitch + row];
|
||||
break;
|
||||
case DXGI_MODE_ROTATION_ROTATE180:
|
||||
return desktop32[(capturedImageHeight - 1 - row) * desktopPitch + (capturedImageWidth - 1 - col)];
|
||||
break;
|
||||
case DXGI_MODE_ROTATION_ROTATE270:
|
||||
return desktop32[col * desktopPitch + (capturedImageHeight - 1 - row)];
|
||||
break;
|
||||
case DXGI_MODE_ROTATION_IDENTITY:
|
||||
case DXGI_MODE_ROTATION_UNSPECIFIED:
|
||||
return desktop32[row * desktopPitch + col];
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// Access RGBA values at the same time
|
||||
auto output32 = reinterpret_cast<UINT*>(bgra32Buffer_.get());
|
||||
auto output32 = reinterpret_cast<UINT*>(bgra32Buffer_.Get());
|
||||
|
||||
if (isMono)
|
||||
{
|
||||
@@ -210,30 +278,29 @@ void Cursor::UpdateTexture()
|
||||
for (int col = colMin, x = 0; col < colMax; ++col, ++x)
|
||||
{
|
||||
BYTE mask = 0b10000000 >> (col % 8);
|
||||
const int i = row * w0 + col;
|
||||
const BYTE andMask = apiBuffer_[col / 8 + row * p] & mask;
|
||||
const BYTE xorMask = apiBuffer_[col / 8 + (row + h) * p] & mask;
|
||||
const BYTE andMask = apiBuffer_[col / 8 + row * cursorImagePitch] & mask;
|
||||
const BYTE xorMask = apiBuffer_[col / 8 + (row + capturedImageHeight) * cursorImagePitch] & mask;
|
||||
const UINT andMask32 = andMask ? 0xFFFFFFFF : 0x00000000;
|
||||
const UINT xorMask32 = xorMask ? 0xFFFFFFFF : 0x00000000;
|
||||
output32[i] = (desktop32[y * desktopPitch + x] & andMask32) ^ xorMask32;
|
||||
output32[row * cursorImageWidth + col] = (getDesktop32(x, y) & andMask32) ^ xorMask32;
|
||||
}
|
||||
}
|
||||
}
|
||||
else // DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR
|
||||
{
|
||||
const auto buffer32 = reinterpret_cast<UINT*>(apiBuffer_.get());
|
||||
const auto buffer32 = reinterpret_cast<UINT*>(apiBuffer_.Get());
|
||||
|
||||
for (int row = rowMin, y = 0; row < rowMax; ++row, ++y)
|
||||
{
|
||||
for (int col = colMin, x = 0; col < colMax; ++col, ++x)
|
||||
{
|
||||
const int i = col + row * w0;
|
||||
const int j = col + row * p / sizeof(UINT);
|
||||
const int i = col + row * cursorImageWidth;
|
||||
const int j = col + row * cursorImagePitch / sizeof(UINT);
|
||||
|
||||
UINT mask = 0xFF000000 & buffer32[j];
|
||||
if (mask)
|
||||
{
|
||||
output32[i] = (desktop32[y * desktopPitch + x] ^ buffer32[j]) | 0xFF000000;
|
||||
output32[i] = (getDesktop32(x, y) ^ buffer32[j]) | 0xFF000000;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -243,17 +310,16 @@ void Cursor::UpdateTexture()
|
||||
}
|
||||
}
|
||||
|
||||
hr = surface->Unmap();
|
||||
if (FAILED(hr))
|
||||
if (FAILED(surface->Unmap()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else // DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR
|
||||
{
|
||||
auto output32 = reinterpret_cast<UINT*>(bgra32Buffer_.get());
|
||||
const auto buffer32 = reinterpret_cast<UINT*>(apiBuffer_.get());
|
||||
for (int i = 0; i < w * h; ++i)
|
||||
auto output32 = reinterpret_cast<UINT*>(bgra32Buffer_.Get());
|
||||
const auto buffer32 = reinterpret_cast<UINT*>(apiBuffer_.Get());
|
||||
for (int i = 0; i < cursorImageWidth * cursorImageHeight; ++i)
|
||||
{
|
||||
output32[i] = buffer32[i];
|
||||
}
|
||||
@@ -263,7 +329,7 @@ void Cursor::UpdateTexture()
|
||||
|
||||
void Cursor::GetTexture(ID3D11Texture2D* texture)
|
||||
{
|
||||
if (bgra32Buffer_ == nullptr)
|
||||
if (!bgra32Buffer_)
|
||||
{
|
||||
Debug::Error("Cursor::GetTexture() => bgra32Buffer is null.");
|
||||
return;
|
||||
@@ -290,7 +356,7 @@ void Cursor::GetTexture(ID3D11Texture2D* texture)
|
||||
|
||||
ComPtr<ID3D11DeviceContext> context;
|
||||
GetDevice()->GetImmediateContext(&context);
|
||||
context->UpdateSubresource(texture, 0, nullptr, bgra32Buffer_.get(), GetWidth() * 4, 0);
|
||||
context->UpdateSubresource(texture, 0, nullptr, bgra32Buffer_.Get(), GetWidth() * 4, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <d3d11.h>
|
||||
#include <dxgi1_2.h>
|
||||
#include <memory>
|
||||
#include "Common.h"
|
||||
|
||||
class Monitor;
|
||||
|
||||
@@ -30,10 +31,8 @@ private:
|
||||
bool isVisible_ = false;
|
||||
int x_ = -1;
|
||||
int y_ = -1;
|
||||
std::unique_ptr<BYTE[]> apiBuffer_ = nullptr;
|
||||
UINT apiBufferSize_ = 0;
|
||||
std::unique_ptr<BYTE[]> bgra32Buffer_ = nullptr;
|
||||
UINT bgra32BufferSize_ = 0;
|
||||
Buffer<BYTE> apiBuffer_;
|
||||
Buffer<BYTE> bgra32Buffer_;
|
||||
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo_;
|
||||
LARGE_INTEGER timestamp_;
|
||||
};
|
||||
@@ -4,81 +4,30 @@
|
||||
#include "Debug.h"
|
||||
|
||||
|
||||
decltype(Debug::mode_) Debug::mode_ = Debug::Mode::kFile;
|
||||
decltype(Debug::mode_) Debug::mode_ = Debug::Mode::File;
|
||||
decltype(Debug::logFunc_) Debug::logFunc_ = nullptr;
|
||||
decltype(Debug::errFunc_) Debug::errFunc_ = nullptr;
|
||||
decltype(Debug::fs_) Debug::fs_;
|
||||
decltype(Debug::ss_) Debug::ss_;
|
||||
|
||||
|
||||
void Debug::Initialize()
|
||||
{
|
||||
if (mode_ == Mode::kFile)
|
||||
if (mode_ == Mode::File)
|
||||
{
|
||||
fs_.open("uDesktopDuplication.log");
|
||||
Debug::Log("Start");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Debug::Finalize()
|
||||
{
|
||||
fs_.close();
|
||||
}
|
||||
|
||||
|
||||
void Debug::Log(const char* msg)
|
||||
{
|
||||
switch (mode_)
|
||||
if (mode_ == Mode::File)
|
||||
{
|
||||
case Mode::kNone:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case Mode::kFile:
|
||||
{
|
||||
if (fs_.good())
|
||||
{
|
||||
fs_ << "[uDD::Log] " << msg << std::endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Mode::kUnityLog:
|
||||
{
|
||||
if (logFunc_ == nullptr)
|
||||
{
|
||||
char buf[256];
|
||||
sprintf_s(buf, 256, "[uDD::Log] %s", msg);
|
||||
logFunc_(buf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Debug::Error(const char* msg)
|
||||
{
|
||||
switch (mode_)
|
||||
{
|
||||
case Mode::kNone:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case Mode::kFile:
|
||||
{
|
||||
if (fs_.good())
|
||||
{
|
||||
fs_ << "[uDD::Err] " << msg << std::endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Mode::kUnityLog:
|
||||
{
|
||||
if (logFunc_ == nullptr)
|
||||
{
|
||||
char buf[256];
|
||||
sprintf_s(buf, 256, "[uDD::Err] %s", msg);
|
||||
errFunc_(buf);
|
||||
}
|
||||
}
|
||||
Debug::Log("Stop");
|
||||
fs_.close();
|
||||
}
|
||||
Debug::SetLogFunc(nullptr);
|
||||
Debug::SetErrorFunc(nullptr);
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
#include <time.h>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include "IUnityInterface.h"
|
||||
|
||||
// Logging
|
||||
@@ -8,9 +10,9 @@ class Debug
|
||||
public:
|
||||
enum class Mode
|
||||
{
|
||||
kNone = 0,
|
||||
kFile = 1,
|
||||
kUnityLog = 2,
|
||||
None = 0,
|
||||
File = 1,
|
||||
UnityLog = 2,
|
||||
};
|
||||
|
||||
using DebugLogFuncPtr = void(UNITY_INTERFACE_API *)(const char*);
|
||||
@@ -21,13 +23,105 @@ public:
|
||||
static void SetLogFunc(DebugLogFuncPtr func) { logFunc_ = func; }
|
||||
static void SetErrorFunc(DebugLogFuncPtr func) { errFunc_ = func; }
|
||||
|
||||
private:
|
||||
enum class Level
|
||||
{
|
||||
Log,
|
||||
Error
|
||||
};
|
||||
|
||||
template <class T>
|
||||
static void Output(T&& arg)
|
||||
{
|
||||
if (mode_ == Mode::None) return;
|
||||
if (ss_.good())
|
||||
{
|
||||
ss_ << std::forward<T>(arg);
|
||||
}
|
||||
}
|
||||
|
||||
static void Flush(Level level)
|
||||
{
|
||||
switch (mode_)
|
||||
{
|
||||
case Mode::None:
|
||||
{
|
||||
return;
|
||||
}
|
||||
case Mode::File:
|
||||
{
|
||||
if (fs_.good() && ss_.good())
|
||||
{
|
||||
const auto str = ss_.str();
|
||||
fs_ << str << std::endl;
|
||||
fs_.flush();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Mode::UnityLog:
|
||||
{
|
||||
if (ss_.good())
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case Level::Log : logFunc_(ss_.str().c_str()); break;
|
||||
case Level::Error : errFunc_(ss_.str().c_str()); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
ss_.str("");
|
||||
ss_.clear(std::stringstream::goodbit);
|
||||
}
|
||||
|
||||
template <class Arg, class... RestArgs>
|
||||
static void _Log(Level level, Arg&& arg, RestArgs&&... restArgs)
|
||||
{
|
||||
Output(std::forward<Arg>(arg));
|
||||
_Log(level, std::forward<RestArgs>(restArgs)...);
|
||||
}
|
||||
|
||||
static void _Log(Level level)
|
||||
{
|
||||
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:
|
||||
static void Log(const char* msg);
|
||||
static void Error(const char* msg);
|
||||
template <class Arg, class... RestArgs>
|
||||
static void Log(Arg&& arg, RestArgs&&... restArgs)
|
||||
{
|
||||
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]");
|
||||
OutputTime();
|
||||
Output(" ");
|
||||
_Log(Level::Error, std::forward<Arg>(arg), std::forward<RestArgs>(restArgs)...);
|
||||
}
|
||||
|
||||
private:
|
||||
static Mode mode_;
|
||||
static std::ofstream fs_;
|
||||
static std::ostringstream ss_;
|
||||
static DebugLogFuncPtr logFunc_;
|
||||
static DebugLogFuncPtr errFunc_;
|
||||
};
|
||||
@@ -1,6 +1,5 @@
|
||||
#include <d3d11.h>
|
||||
#include <ShellScalingAPI.h>
|
||||
#include "Common.h"
|
||||
#include "Debug.h"
|
||||
#include "Cursor.h"
|
||||
#include "MonitorManager.h"
|
||||
@@ -18,56 +17,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.");
|
||||
// DPI is set as -1, so the application has to use the appropriate value.
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,58 +115,192 @@ 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;
|
||||
}
|
||||
|
||||
// Get texture
|
||||
if (unityTexture_)
|
||||
{
|
||||
ComPtr<ID3D11Texture2D> texture;
|
||||
resource.As<ID3D11Texture2D>(&texture);
|
||||
ID3D11Texture2D* texture;
|
||||
if (FAILED(resource.CopyTo(&texture)))
|
||||
{
|
||||
Debug::Error("Monitor::Render() => resource.As() failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
ComPtr<ID3D11DeviceContext> context;
|
||||
GetDevice()->GetImmediateContext(&context);
|
||||
context->CopyResource(unityTexture_, texture.Get());
|
||||
D3D11_TEXTURE2D_DESC srcDesc, dstDesc;
|
||||
texture->GetDesc(&srcDesc);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
cursor_->UpdateBuffer(frameInfo);
|
||||
cursor_->UpdateTexture();
|
||||
UpdateMetadata(frameInfo);
|
||||
UpdateCursor(frameInfo);
|
||||
|
||||
hr = deskDupl_->ReleaseFrame();
|
||||
if (FAILED(hr))
|
||||
if (FAILED(deskDupl_->ReleaseFrame()))
|
||||
{
|
||||
Debug::Error("Monitor::Render() => ReleaseFrame() failed.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Monitor::UpdateCursor(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
|
||||
{
|
||||
cursor_->UpdateBuffer(frameInfo);
|
||||
cursor_->UpdateTexture();
|
||||
}
|
||||
|
||||
|
||||
void Monitor::UpdateMetadata(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
|
||||
{
|
||||
metaData_.ExpandIfNeeded(frameInfo.TotalMetadataBufferSize);
|
||||
UpdateMoveRects(frameInfo);
|
||||
UpdateDirtyRects(frameInfo);
|
||||
}
|
||||
|
||||
|
||||
void Monitor::UpdateMoveRects(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
|
||||
{
|
||||
moveRectSize_ = metaData_.Size();
|
||||
|
||||
const auto hr = deskDupl_->GetFrameMoveRects(
|
||||
moveRectSize_,
|
||||
metaData_.As<DXGI_OUTDUPL_MOVE_RECT>(),
|
||||
&moveRectSize_);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
switch (hr)
|
||||
{
|
||||
case DXGI_ERROR_ACCESS_LOST:
|
||||
{
|
||||
Debug::Log("Monitor::Render() => DXGI_ERROR_ACCESS_LOST (GetFrameMoveRects()).");
|
||||
break;
|
||||
}
|
||||
case DXGI_ERROR_MORE_DATA:
|
||||
{
|
||||
Debug::Error("Monitor::Render() => DXGI_ERROR_MORE_DATA (GetFrameMoveRects()).");
|
||||
break;
|
||||
}
|
||||
case DXGI_ERROR_INVALID_CALL:
|
||||
{
|
||||
Debug::Error("Monitor::Render() => DXGI_ERROR_INVALID_CALL (GetFrameMoveRects()).");
|
||||
break;
|
||||
}
|
||||
case E_INVALIDARG:
|
||||
{
|
||||
Debug::Error("Monitor::Render() => E_INVALIDARG (GetFrameMoveRects()).");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Debug::Error("Monitor::Render() => Unknown Error (GetFrameMoveRects()).");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Monitor::UpdateDirtyRects(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
|
||||
{
|
||||
dirtyRectSize_ = metaData_.Size() - moveRectSize_;
|
||||
|
||||
const auto hr = deskDupl_->GetFrameDirtyRects(
|
||||
dirtyRectSize_,
|
||||
metaData_.As<RECT>(moveRectSize_ /* offset */),
|
||||
&dirtyRectSize_);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
switch (hr)
|
||||
{
|
||||
case DXGI_ERROR_ACCESS_LOST:
|
||||
{
|
||||
Debug::Log("Monitor::Render() => DXGI_ERROR_ACCESS_LOST (GetFrameDirtyRects()).");
|
||||
break;
|
||||
}
|
||||
case DXGI_ERROR_MORE_DATA:
|
||||
{
|
||||
Debug::Error("Monitor::Render() => DXGI_ERROR_MORE_DATA (GetFrameDirtyRects()).");
|
||||
break;
|
||||
}
|
||||
case DXGI_ERROR_INVALID_CALL:
|
||||
{
|
||||
Debug::Error("Monitor::Render() => DXGI_ERROR_INVALID_CALL (GetFrameDirtyRects()).");
|
||||
break;
|
||||
}
|
||||
case E_INVALIDARG:
|
||||
{
|
||||
Debug::Error("Monitor::Render() => E_INVALIDARG (GetFrameDirtyRects()).");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Debug::Error("Monitor::Render() => Unknown Error (GetFrameDirtyRects()).");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetId() const
|
||||
{
|
||||
return id_;
|
||||
@@ -154,7 +325,7 @@ ID3D11Texture2D* Monitor::GetUnityTexture() const
|
||||
}
|
||||
|
||||
|
||||
const ComPtr<IDXGIOutputDuplication>& Monitor::GetDeskDupl()
|
||||
IDXGIOutputDuplication* Monitor::GetDeskDupl()
|
||||
{
|
||||
return deskDupl_;
|
||||
}
|
||||
@@ -186,25 +357,25 @@ bool Monitor::IsPrimary() const
|
||||
|
||||
int Monitor::GetLeft() const
|
||||
{
|
||||
return outputDesc_.DesktopCoordinates.left;
|
||||
return static_cast<int>(outputDesc_.DesktopCoordinates.left);
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetRight() const
|
||||
{
|
||||
return outputDesc_.DesktopCoordinates.right;
|
||||
return static_cast<int>(outputDesc_.DesktopCoordinates.right);
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetTop() const
|
||||
{
|
||||
return outputDesc_.DesktopCoordinates.top;
|
||||
return static_cast<int>(outputDesc_.DesktopCoordinates.top);
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetBottom() const
|
||||
{
|
||||
return outputDesc_.DesktopCoordinates.bottom;
|
||||
return static_cast<int>(outputDesc_.DesktopCoordinates.bottom);
|
||||
}
|
||||
|
||||
|
||||
@@ -228,13 +399,35 @@ 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_;
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetMoveRectCount() const
|
||||
{
|
||||
return moveRectSize_ / sizeof(DXGI_OUTDUPL_MOVE_RECT);
|
||||
}
|
||||
|
||||
|
||||
DXGI_OUTDUPL_MOVE_RECT* Monitor::GetMoveRects() const
|
||||
{
|
||||
return metaData_.As<DXGI_OUTDUPL_MOVE_RECT>();
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetDirtyRectCount() const
|
||||
{
|
||||
return dirtyRectSize_ / sizeof(RECT);
|
||||
}
|
||||
|
||||
|
||||
RECT* Monitor::GetDirtyRects() const
|
||||
{
|
||||
return metaData_.As<RECT>(moveRectSize_);
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <dxgi1_2.h>
|
||||
#include <wrl/client.h>
|
||||
#include <memory>
|
||||
#include "Common.h"
|
||||
|
||||
class Cursor;
|
||||
|
||||
@@ -17,6 +18,7 @@ enum class MonitorState
|
||||
CurrentlyNotAvailable = 4,
|
||||
SessionDisconnected = 5,
|
||||
AccessLost = 6,
|
||||
TextureSizeInconsistent = 7,
|
||||
Unknown = 999,
|
||||
};
|
||||
|
||||
@@ -47,16 +49,29 @@ public:
|
||||
int GetRotation() const;
|
||||
int GetDpiX() const;
|
||||
int GetDpiY() const;
|
||||
const Microsoft::WRL::ComPtr<IDXGIOutputDuplication>& GetDeskDupl();
|
||||
IDXGIOutputDuplication* GetDeskDupl();
|
||||
const std::unique_ptr<Cursor>& GetCursor();
|
||||
int GetMoveRectCount() const;
|
||||
DXGI_OUTDUPL_MOVE_RECT* GetMoveRects() const;
|
||||
int GetDirtyRectCount() const;
|
||||
RECT* GetDirtyRects() const;
|
||||
|
||||
private:
|
||||
void UpdateCursor(const DXGI_OUTDUPL_FRAME_INFO& frameInfo);
|
||||
void UpdateMetadata(const DXGI_OUTDUPL_FRAME_INFO& frameInfo);
|
||||
void UpdateMoveRects(const DXGI_OUTDUPL_FRAME_INFO& frameInfo);
|
||||
void UpdateDirtyRects(const DXGI_OUTDUPL_FRAME_INFO& frameInfo);
|
||||
|
||||
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_;
|
||||
Buffer<BYTE> metaData_;
|
||||
UINT moveRectSize_ = 0;
|
||||
UINT dirtyRectSize_ = 0;;
|
||||
};
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "IUnityGraphicsD3D11.h"
|
||||
|
||||
#include "Common.h"
|
||||
#include "Debug.h"
|
||||
#include "Monitor.h"
|
||||
#include "Cursor.h"
|
||||
#include "MonitorManager.h"
|
||||
@@ -34,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;
|
||||
@@ -67,11 +72,36 @@ void MonitorManager::RequireReinitilization()
|
||||
|
||||
void MonitorManager::Reinitialize()
|
||||
{
|
||||
Debug::Log("MonitorManager::Reinitialize()");
|
||||
Initialize();
|
||||
SendMessageToUnity(Message::Reinitialized);
|
||||
}
|
||||
|
||||
|
||||
bool MonitorManager::HasMonitorCountChanged() const
|
||||
{
|
||||
ComPtr<IDXGIFactory1> factory;
|
||||
if (FAILED(CreateDXGIFactory1(IID_PPV_ARGS(&factory))))
|
||||
{
|
||||
Debug::Error("MonitorManager::CheckMonitorConnection() => CreateDXGIFactory1() failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
int id = 0;
|
||||
ComPtr<IDXGIAdapter1> adapter;
|
||||
for (int i = 0; (factory->EnumAdapters1(i, &adapter) != DXGI_ERROR_NOT_FOUND); ++i)
|
||||
{
|
||||
ComPtr<IDXGIOutput> output;
|
||||
for (int j = 0; (adapter->EnumOutputs(j, &output) != DXGI_ERROR_NOT_FOUND); ++j)
|
||||
{
|
||||
id++;
|
||||
}
|
||||
}
|
||||
|
||||
return monitors_.size() != id;
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<Monitor> MonitorManager::GetMonitor(int id) const
|
||||
{
|
||||
if (id >= 0 && id < monitors_.size())
|
||||
@@ -109,6 +139,7 @@ int MonitorManager::GetMonitorCount() const
|
||||
return static_cast<int>(monitors_.size());
|
||||
}
|
||||
|
||||
|
||||
int MonitorManager::GetTotalWidth() const
|
||||
{
|
||||
std::vector<int> lefts, rights;
|
||||
@@ -122,6 +153,7 @@ int MonitorManager::GetTotalWidth() const
|
||||
return maxRight - minLeft;
|
||||
}
|
||||
|
||||
|
||||
int MonitorManager::GetTotalHeight() const
|
||||
{
|
||||
std::vector<int> tops, bottoms;
|
||||
|
||||
@@ -16,6 +16,7 @@ public:
|
||||
explicit MonitorManager();
|
||||
~MonitorManager();
|
||||
void Reinitialize();
|
||||
bool HasMonitorCountChanged() const;
|
||||
void RequireReinitilization();
|
||||
void SetCursorMonitorId(int id) { cursorMonitorId_ = id; }
|
||||
int GetCursorMonitorId() const { return cursorMonitorId_; }
|
||||
|
||||
@@ -29,31 +29,54 @@ extern "C"
|
||||
{
|
||||
if (g_unity && !g_manager)
|
||||
{
|
||||
g_manager = std::make_unique<MonitorManager>();
|
||||
Debug::Initialize();
|
||||
g_manager = std::make_unique<MonitorManager>();
|
||||
}
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API FinalizeUDD()
|
||||
{
|
||||
if (!g_manager) return;
|
||||
|
||||
g_manager.reset();
|
||||
|
||||
std::queue<Message> empty;
|
||||
g_messages.swap(empty);
|
||||
|
||||
Debug::SetLogFunc(nullptr);
|
||||
Debug::SetErrorFunc(nullptr);
|
||||
Debug::Finalize();
|
||||
}
|
||||
|
||||
void UNITY_INTERFACE_API OnGraphicsDeviceEvent(UnityGfxDeviceEventType event)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case kUnityGfxDeviceEventInitialize:
|
||||
{
|
||||
InitializeUDD();
|
||||
break;
|
||||
}
|
||||
case kUnityGfxDeviceEventShutdown:
|
||||
{
|
||||
FinalizeUDD();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces)
|
||||
{
|
||||
g_unity = unityInterfaces;
|
||||
InitializeUDD();
|
||||
|
||||
auto unityGraphics = g_unity->Get<IUnityGraphics>();
|
||||
unityGraphics->RegisterDeviceEventCallback(OnGraphicsDeviceEvent);
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UnityPluginUnload()
|
||||
{
|
||||
auto unityGraphics = g_unity->Get<IUnityGraphics>();
|
||||
unityGraphics->UnregisterDeviceEventCallback(OnGraphicsDeviceEvent);
|
||||
|
||||
FinalizeUDD();
|
||||
g_unity = nullptr;
|
||||
}
|
||||
@@ -114,6 +137,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;
|
||||
@@ -354,4 +383,40 @@ extern "C"
|
||||
monitor->SetUnityTexture(d3d11Texture);
|
||||
}
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetMoveRectCount(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetMoveRectCount();
|
||||
}
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void* UNITY_INTERFACE_API GetMoveRects(int id)
|
||||
{
|
||||
if (!g_manager) return nullptr;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetMoveRects();
|
||||
}
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetDirtyRectCount(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetDirtyRectCount();
|
||||
}
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void* UNITY_INTERFACE_API GetDirtyRects(int id)
|
||||
{
|
||||
if (!g_manager) return nullptr;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetDirtyRects();
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
13
README.md
13
README.md
@@ -1,6 +1,9 @@
|
||||
uDesktopDuplication
|
||||
===================
|
||||
|
||||

|
||||

|
||||
|
||||
**uDesktopDuplication** is an Unity asset to use the realtime screen capture as `Texture2D` using Desktop Duplication API.
|
||||
|
||||
|
||||
@@ -25,16 +28,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