Compare commits
123 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
a0edc1a439 | ||
|
|
1eb821b34d | ||
|
|
212d8ccb9b | ||
|
|
fac8fe1ea7 | ||
|
|
f90178f1db | ||
|
|
74e25f2bd7 | ||
|
|
70c126be6c | ||
|
|
f74221bf75 | ||
|
|
3a705732d1 | ||
|
|
804bfb96bd | ||
|
|
63d8f6936d | ||
|
|
e4e4917bcb | ||
|
|
0e205ecef7 | ||
|
|
cda8024b17 | ||
|
|
05085f7d24 | ||
|
|
52eafbe919 | ||
|
|
be1e0c25bb | ||
|
|
372e44d8c1 | ||
|
|
d79eb91333 | ||
|
|
f654df7f5b | ||
|
|
620762dba7 | ||
|
|
43ba445623 | ||
|
|
c7c12de730 | ||
|
|
1086595d6b | ||
|
|
f69fb6a7bb | ||
|
|
b661b4a93f | ||
|
|
8d9ef71829 | ||
|
|
fb7bcbfa5b | ||
|
|
9ed8bb8d5b | ||
|
|
fab67bbc61 | ||
|
|
5fa9f03a0e | ||
|
|
941bb2da91 | ||
|
|
d423e0d1db | ||
|
|
33a55d95bd | ||
|
|
8e2d8d3218 | ||
|
|
6ac1a5ee60 | ||
|
|
15a98cd774 | ||
|
|
06bb971a42 | ||
|
|
217e63d40c | ||
|
|
9e42fb0abf | ||
|
|
9de5064fbe | ||
|
|
2b108bd4b1 | ||
|
|
f9f8b14f46 | ||
|
|
7774290e71 | ||
|
|
a3a7754fb0 | ||
|
|
0accebb657 | ||
|
|
0a38eda14f | ||
|
|
720f74e6f6 | ||
|
|
80fa39f4eb | ||
|
|
22b21ae3f4 | ||
|
|
43153266a0 | ||
|
|
dc4fe44342 | ||
|
|
6bb9846510 | ||
|
|
90b811cd31 | ||
|
|
1d7b7e8650 | ||
|
|
6f784860e5 | ||
|
|
690dba23cc | ||
|
|
908988dcaa | ||
|
|
0f72396540 | ||
|
|
8f7659d7c2 | ||
|
|
2e763d994d | ||
|
|
2c61fcd470 | ||
|
|
f2cad8bf73 | ||
|
|
ad4b49c177 | ||
|
|
92b926bcde | ||
|
|
3e90889266 | ||
|
|
6531fe1dd6 | ||
|
|
0b62376ac5 | ||
|
|
9f611d5d34 | ||
|
|
dd4e27030d | ||
|
|
4350ac3953 | ||
|
|
6ba381f5d4 | ||
|
|
9287ef2f2d | ||
|
|
dc6b8bc204 | ||
|
|
14cd67e0c6 | ||
|
|
73018f671a | ||
|
|
7a27d86eff | ||
|
|
99e1774388 | ||
|
|
e0408d4749 | ||
|
|
23ceef6502 | ||
|
|
4317fc06e3 | ||
|
|
f8cb71b472 | ||
|
|
f1a7c302bd | ||
|
|
ec82c49d7f | ||
|
|
f8146c15bd | ||
|
|
d5a9827b4d | ||
|
|
4b499a96de | ||
|
|
583326c333 | ||
|
|
c3ec17dd7b | ||
|
|
6f375e2e5b | ||
|
|
d9f83cf89e | ||
|
|
ba5ebe8094 | ||
|
|
1e2ac40507 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -4,6 +4,7 @@
|
||||
/[Oo]bj/
|
||||
/[Bb]uild/
|
||||
/[Ww]iki/
|
||||
/[Mm]isc/
|
||||
|
||||
# Autogenerated VS/MD solution and project files
|
||||
*.csproj
|
||||
@@ -24,3 +25,6 @@ sysinfo.txt
|
||||
|
||||
# Others
|
||||
.DS_Store
|
||||
/Assets/AssetStoreTools*
|
||||
/Assets/Extensions*
|
||||
/uDesktopDuplication.log
|
||||
|
||||
9
Assets/uDesktopDuplication/Editor.meta
Normal file
9
Assets/uDesktopDuplication/Editor.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 022a80d652ef0304e84e9d6ac689e21f
|
||||
folderAsset: yes
|
||||
timeCreated: 1478353382
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
Assets/uDesktopDuplication/Editor/Resources.meta
Normal file
9
Assets/uDesktopDuplication/Editor/Resources.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e15bc51aada81042a909c808e25c176
|
||||
folderAsset: yes
|
||||
timeCreated: 1477555207
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
Before Width: | Height: | Size: 967 B After Width: | Height: | Size: 967 B |
9
Assets/uDesktopDuplication/Examples/Prefabs.meta
Normal file
9
Assets/uDesktopDuplication/Examples/Prefabs.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 678e1de5e6de7914baed516a78940503
|
||||
folderAsset: yes
|
||||
timeCreated: 1477643728
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/uDesktopDuplication/Examples/Prefabs/Monitor Board.prefab
Normal file
BIN
Assets/uDesktopDuplication/Examples/Prefabs/Monitor Board.prefab
Normal file
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 033e0db89e0ca1d46a399c953629f67d
|
||||
timeCreated: 1477555367
|
||||
guid: de6f36e446e5e8a48bf61fe198985bb4
|
||||
timeCreated: 1477643732
|
||||
licenseType: Pro
|
||||
NativeFormatImporter:
|
||||
userData:
|
||||
BIN
Assets/uDesktopDuplication/Examples/Prefabs/Monitor Plane.prefab
Normal file
BIN
Assets/uDesktopDuplication/Examples/Prefabs/Monitor Plane.prefab
Normal file
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 42d6b6dbae38a384e882c2c907915139
|
||||
timeCreated: 1478889784
|
||||
licenseType: Pro
|
||||
NativeFormatImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
Assets/uDesktopDuplication/Examples/Scenes.meta
Normal file
9
Assets/uDesktopDuplication/Examples/Scenes.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a3456d4b2f59e84f969e31deca4016d
|
||||
folderAsset: yes
|
||||
timeCreated: 1477643596
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4bae91f55031b98488c9e668031f7387
|
||||
timeCreated: 1478610618
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 445860b05342be845967392bfd70224e
|
||||
timeCreated: 1477643648
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/uDesktopDuplication/Examples/Scenes/Primary Monitor.unity
Normal file
BIN
Assets/uDesktopDuplication/Examples/Scenes/Primary Monitor.unity
Normal file
Binary file not shown.
BIN
Assets/uDesktopDuplication/Examples/Scenes/Shaders.unity
Normal file
BIN
Assets/uDesktopDuplication/Examples/Scenes/Shaders.unity
Normal file
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f66b67cb0e751dc4ca0251b38bc8638d
|
||||
timeCreated: 1477648784
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/uDesktopDuplication/Examples/Scenes/Switch Scenes.unity
Normal file
BIN
Assets/uDesktopDuplication/Examples/Scenes/Switch Scenes.unity
Normal file
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9ce9467b01f36d647ace156faef9cb9f
|
||||
timeCreated: 1478927635
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 78e54899a518eb749a589b921a1b263f
|
||||
timeCreated: 1477650252
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/uDesktopDuplication/Examples/Scenes/Zoom.unity
Normal file
BIN
Assets/uDesktopDuplication/Examples/Scenes/Zoom.unity
Normal file
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f3d41026babadc241b9d0852b0831584
|
||||
timeCreated: 1478436298
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
Assets/uDesktopDuplication/Examples/Scripts.meta
Normal file
9
Assets/uDesktopDuplication/Examples/Scripts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 71921882396f0f345aa8a66e2fcb0b6c
|
||||
folderAsset: yes
|
||||
timeCreated: 1477643608
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
48
Assets/uDesktopDuplication/Examples/Scripts/Loupe.cs
Normal file
48
Assets/uDesktopDuplication/Examples/Scripts/Loupe.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class Loupe : MonoBehaviour
|
||||
{
|
||||
private uDesktopDuplication.Texture uddTexture_;
|
||||
public float zoom = 3f;
|
||||
public float aspect = 1f;
|
||||
|
||||
void Start()
|
||||
{
|
||||
uddTexture_ = GetComponent<uDesktopDuplication.Texture>();
|
||||
uddTexture_.useClip = true;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
CheckVariables();
|
||||
|
||||
if (uDesktopDuplication.Manager.cursorMonitorId < 0) return;
|
||||
uddTexture_.monitorId = uDesktopDuplication.Manager.cursorMonitorId;
|
||||
|
||||
// To get other monitor textures, set dirty flag.
|
||||
foreach (var target in uDesktopDuplication.Manager.monitors) {
|
||||
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 w = 1f / zoom;
|
||||
var h = w / aspect * monitor.aspect;
|
||||
x = Mathf.Clamp(x - w / 2, 0f, 1f - w);
|
||||
y = Mathf.Clamp(y - h / 2, 0f, 1f - h);
|
||||
uddTexture_.clipPos = new Vector2(x, y);
|
||||
uddTexture_.clipScale = new Vector2(w, h);
|
||||
}
|
||||
|
||||
void CheckVariables()
|
||||
{
|
||||
if (zoom < 1f) zoom = 1f;
|
||||
if (aspect < 0.01f) aspect = 0.01f;
|
||||
}
|
||||
}
|
||||
|
||||
12
Assets/uDesktopDuplication/Examples/Scripts/Loupe.cs.meta
Normal file
12
Assets/uDesktopDuplication/Examples/Scripts/Loupe.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: efd9499cdb931b945947a2cd47909676
|
||||
timeCreated: 1478438457
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,178 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using MeshForwardDirection = uDesktopDuplication.Texture.MeshForwardDirection;
|
||||
|
||||
public class MultipleMonitorCreator : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Create monitors using this prefab.")]
|
||||
public GameObject monitorPrefab;
|
||||
|
||||
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 uDesktopDuplication.Texture uddTexture { get; set; }
|
||||
public Mesh mesh { get; set; }
|
||||
}
|
||||
|
||||
private List<MonitorInfo> monitors_ = new List<MonitorInfo>();
|
||||
public List<MonitorInfo> monitors { get { return monitors_; } }
|
||||
|
||||
void Start()
|
||||
{
|
||||
uDesktopDuplication.Manager.CreateInstance();
|
||||
Create();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (removeIfUnsupported) {
|
||||
RemoveUnsupportedDisplayAfterRemoveTimer();
|
||||
}
|
||||
|
||||
if (uDesktopDuplication.Manager.monitorCount != monitors.Count) {
|
||||
Recreate();
|
||||
}
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
uDesktopDuplication.Manager.onReinitialized += Recreate;
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
uDesktopDuplication.Manager.onReinitialized -= Recreate;
|
||||
}
|
||||
|
||||
void RemoveUnsupportedDisplayAfterRemoveTimer()
|
||||
{
|
||||
if (!hasMonitorUnsupportStateChecked_) {
|
||||
removeWaitTimer_ += Time.deltaTime;
|
||||
if (removeWaitTimer_ > removeWaitDuration) {
|
||||
hasMonitorUnsupportStateChecked_ = true;
|
||||
foreach (var info in monitors_) {
|
||||
if (info.uddTexture.monitor.state == uDesktopDuplication.MonitorState.Unsupported) {
|
||||
Destroy(info.gameObject);
|
||||
}
|
||||
}
|
||||
monitors_.RemoveAll(info => info.uddTexture.monitor.state == uDesktopDuplication.MonitorState.Unsupported);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ResetRemoveTimer()
|
||||
{
|
||||
hasMonitorUnsupportStateChecked_ = false;
|
||||
removeWaitTimer_ = 0f;
|
||||
}
|
||||
|
||||
void Create()
|
||||
{
|
||||
ResetRemoveTimer();
|
||||
|
||||
// Create monitors
|
||||
for (int i = 0; i < uDesktopDuplication.Manager.monitorCount; ++i) {
|
||||
// Create monitor obeject
|
||||
var go = Instantiate(monitorPrefab);
|
||||
go.name = "Monitor " + i;
|
||||
|
||||
// Expand AABB
|
||||
var mesh = go.GetComponent<MeshFilter>().sharedMesh;
|
||||
var aabbScale = mesh.bounds.size;
|
||||
aabbScale.y = Mathf.Max(aabbScale.y, aabbScale.x);
|
||||
aabbScale.z = Mathf.Max(aabbScale.z, aabbScale.x);
|
||||
mesh.bounds = new Bounds(mesh.bounds.center, aabbScale);
|
||||
|
||||
// Assign monitor
|
||||
var texture = go.GetComponent<uDesktopDuplication.Texture>();
|
||||
texture.monitorId = i;
|
||||
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;
|
||||
}
|
||||
if (meshForwardDirection == MeshForwardDirection.Y) {
|
||||
go.transform.localScale = new Vector3(width, go.transform.localScale.y, height);
|
||||
} else {
|
||||
go.transform.localScale = new Vector3(width, height, go.transform.localScale.z);
|
||||
}
|
||||
|
||||
// Set parent as this object
|
||||
go.transform.SetParent(transform);
|
||||
|
||||
// Save
|
||||
var info = new MonitorInfo();
|
||||
info.gameObject = go;
|
||||
info.originalRotation = go.transform.rotation;
|
||||
info.uddTexture = texture;
|
||||
info.mesh = mesh;
|
||||
monitors_.Add(info);
|
||||
}
|
||||
|
||||
// Sort monitors in coordinate order
|
||||
monitors_.Sort((a, b) => a.uddTexture.monitor.left - b.uddTexture.monitor.left);
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
foreach (var info in monitors_) {
|
||||
Destroy(info.gameObject);
|
||||
}
|
||||
if (removeChildrenWhenClear) {
|
||||
for (int i = 0; i < transform.childCount; ++i) {
|
||||
Destroy(transform.GetChild(i).gameObject);
|
||||
}
|
||||
}
|
||||
monitors_.Clear();
|
||||
}
|
||||
|
||||
[ContextMenu("Recreate")]
|
||||
public void Recreate()
|
||||
{
|
||||
Clear();
|
||||
Create();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb22986017201f74aa864dcbf0cab751
|
||||
timeCreated: 1477643778
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,54 @@
|
||||
using UnityEngine;
|
||||
|
||||
[RequireComponent(typeof(MultipleMonitorCreator))]
|
||||
public class MultipleMonitorLayouter : MonoBehaviour
|
||||
{
|
||||
protected MultipleMonitorCreator creator_;
|
||||
public bool updateEveryFrame = true;
|
||||
public float margin = 0.1f;
|
||||
[Range(0f, 10f)] public float thickness = 1f;
|
||||
|
||||
void Start()
|
||||
{
|
||||
creator_ = GetComponent<MultipleMonitorCreator>();
|
||||
Layout();
|
||||
}
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
margin = Mathf.Max(margin, 0f);
|
||||
|
||||
if (updateEveryFrame) {
|
||||
Layout();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Layout()
|
||||
{
|
||||
var monitors = creator_.monitors;
|
||||
var n = monitors.Count;
|
||||
|
||||
var totalWidth = 0f;
|
||||
foreach (var info in monitors) {
|
||||
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 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 += (width * info.mesh.bounds.extents.x) + margin;
|
||||
}
|
||||
|
||||
foreach (var info in monitors) {
|
||||
info.uddTexture.thickness = thickness;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0db1dcac2dedc724194c423ee75b3bf4
|
||||
timeCreated: 1478608632
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,88 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class MultipleMonitorRoundLayouter : MultipleMonitorLayouter
|
||||
{
|
||||
public float radius = 10f;
|
||||
public Vector3 offsetAngle = Vector3.zero;
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
foreach (var info in creator_.monitors) {
|
||||
info.uddTexture.bend = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Layout()
|
||||
{
|
||||
var monitors = creator_.monitors;
|
||||
var n = monitors.Count;
|
||||
|
||||
// keep the local scale z of monitors as 1 to bend them correctly.
|
||||
foreach (var info in monitors) {
|
||||
var scale = info.gameObject.transform.localScale;
|
||||
scale.z = 1f;
|
||||
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.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 = info.gameObject.transform.localScale.x * (info.mesh.bounds.extents.x * 2f);
|
||||
|
||||
angle += (width / radius) * 0.5f;
|
||||
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;
|
||||
|
||||
uddTex.bend = true;
|
||||
uddTex.meshForwardDirection = creator_.meshForwardDirection;
|
||||
uddTex.radius = radius;
|
||||
uddTex.thickness = thickness;
|
||||
uddTex.width = uddTex.transform.localScale.x;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
var a0 = 2 * Mathf.PI * i / 100;
|
||||
var a1 = 2 * Mathf.PI * (i + 1) / 100;
|
||||
var p0 = center + radius * scale * new Vector3(Mathf.Cos(a0), 0f, Mathf.Sin(a0));
|
||||
var p1 = center + radius * scale * new Vector3(Mathf.Cos(a1), 0f, Mathf.Sin(a1));
|
||||
Debug.DrawLine(p0, p1, Color.red);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a31b05fdfa301f14e88365e64622b43a
|
||||
timeCreated: 1478524060
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,17 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class ToggleMonitors : MonoBehaviour
|
||||
{
|
||||
void Update()
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.Tab)) {
|
||||
var texture = GetComponent<uDesktopDuplication.Texture>();
|
||||
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) {
|
||||
texture.monitorId--;
|
||||
} else {
|
||||
texture.monitorId++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 602d3a9bed585d14eaf21e0528d24d54
|
||||
timeCreated: 1477650268
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,58 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
public class UddSceneManager : MonoBehaviour
|
||||
{
|
||||
public static UddSceneManager instance { get; set; }
|
||||
[SerializeField] string[] scenes;
|
||||
[SerializeField] int sceneNo = 0;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (!instance) {
|
||||
instance = this;
|
||||
} else {
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
DontDestroyOnLoad(gameObject);
|
||||
Load();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
void Prev()
|
||||
{
|
||||
sceneNo = (sceneNo - 1) % scenes.Length;
|
||||
Load();
|
||||
}
|
||||
|
||||
void Load()
|
||||
{
|
||||
SceneManager.LoadScene(scenes[Mathf.Clamp(sceneNo, 0, scenes.Length - 1)]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 426ff762e9c02b0499e1453018ecdc94
|
||||
timeCreated: 1478927673
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 06ef13d14bbcf9242a8eba19a94803f3
|
||||
timeCreated: 1477648806
|
||||
licenseType: Pro
|
||||
NativeFormatImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3abaadab838fb3c4ba342eda7431d4f7
|
||||
timeCreated: 1477648806
|
||||
licenseType: Pro
|
||||
NativeFormatImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 647550562f402264fb69871ec732b74c
|
||||
timeCreated: 1477648806
|
||||
licenseType: Pro
|
||||
NativeFormatImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
Assets/uDesktopDuplication/Models.meta
Normal file
9
Assets/uDesktopDuplication/Models.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 75494e992a22ea44d9b614d7fd3dc235
|
||||
folderAsset: yes
|
||||
timeCreated: 1478887431
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/uDesktopDuplication/Models/uDD_Board.fbx
Normal file
BIN
Assets/uDesktopDuplication/Models/uDD_Board.fbx
Normal file
Binary file not shown.
78
Assets/uDesktopDuplication/Models/uDD_Board.fbx.meta
Normal file
78
Assets/uDesktopDuplication/Models/uDD_Board.fbx.meta
Normal file
@@ -0,0 +1,78 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d6b30b913257fee4d8246bf7a2620fcd
|
||||
timeCreated: 1478887431
|
||||
licenseType: Pro
|
||||
ModelImporter:
|
||||
serializedVersion: 19
|
||||
fileIDToRecycleName:
|
||||
100000: //RootNode
|
||||
400000: //RootNode
|
||||
2300000: //RootNode
|
||||
3300000: //RootNode
|
||||
4300000: pCube1
|
||||
4300002: uDD_Board
|
||||
9500000: //RootNode
|
||||
materials:
|
||||
importMaterials: 0
|
||||
materialName: 0
|
||||
materialSearch: 1
|
||||
animations:
|
||||
legacyGenerateAnimations: 4
|
||||
bakeSimulation: 0
|
||||
resampleCurves: 1
|
||||
optimizeGameObjects: 0
|
||||
motionNodeName:
|
||||
animationImportErrors:
|
||||
animationImportWarnings:
|
||||
animationRetargetingWarnings:
|
||||
animationDoRetargetingWarnings: 0
|
||||
animationCompression: 1
|
||||
animationRotationError: 0.5
|
||||
animationPositionError: 0.5
|
||||
animationScaleError: 0.5
|
||||
animationWrapMode: 0
|
||||
extraExposedTransformPaths: []
|
||||
clipAnimations: []
|
||||
isReadable: 0
|
||||
meshes:
|
||||
lODScreenPercentages: []
|
||||
globalScale: 0.1
|
||||
meshCompression: 0
|
||||
addColliders: 0
|
||||
importBlendShapes: 1
|
||||
swapUVChannels: 0
|
||||
generateSecondaryUV: 0
|
||||
useFileUnits: 1
|
||||
optimizeMeshForGPU: 1
|
||||
keepQuads: 0
|
||||
weldVertices: 1
|
||||
secondaryUVAngleDistortion: 8
|
||||
secondaryUVAreaDistortion: 15.000001
|
||||
secondaryUVHardAngle: 88
|
||||
secondaryUVPackMargin: 4
|
||||
useFileScale: 1
|
||||
tangentSpace:
|
||||
normalSmoothAngle: 60
|
||||
normalImportMode: 0
|
||||
tangentImportMode: 2
|
||||
importAnimation: 0
|
||||
copyAvatar: 0
|
||||
humanDescription:
|
||||
human: []
|
||||
skeleton: []
|
||||
armTwist: 0.5
|
||||
foreArmTwist: 0.5
|
||||
upperLegTwist: 0.5
|
||||
legTwist: 0.5
|
||||
armStretch: 0.05
|
||||
legStretch: 0.05
|
||||
feetSpacing: 0
|
||||
rootMotionBoneName:
|
||||
hasTranslationDoF: 0
|
||||
lastHumanDescriptionAvatarSource: {instanceID: 0}
|
||||
animationType: 0
|
||||
humanoidOversampling: 1
|
||||
additionalBone: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
9
Assets/uDesktopDuplication/Plugins/x86_64.meta
Normal file
9
Assets/uDesktopDuplication/Plugins/x86_64.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 99257566ccd0582439a44abad90e65d4
|
||||
folderAsset: yes
|
||||
timeCreated: 1478626361
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -1,8 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e15bc51aada81042a909c808e25c176
|
||||
guid: b281589e6b8e27b4fbf0509d620b7871
|
||||
folderAsset: yes
|
||||
timeCreated: 1477555207
|
||||
licenseType: Pro
|
||||
timeCreated: 1478354860
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48b92326b146fa848beec6f35365b1f4
|
||||
guid: e36cd526b654d024e875d5110a1b0442
|
||||
folderAsset: yes
|
||||
timeCreated: 1477555207
|
||||
licenseType: Pro
|
||||
timeCreated: 1478354877
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e2bcc27e10c0d141a0c11cd88cdcf98
|
||||
folderAsset: yes
|
||||
timeCreated: 1478353263
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
@@ -0,0 +1,59 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9855ecfc2b823c94d8a1354715d343a1
|
||||
timeCreated: 1478353264
|
||||
licenseType: Pro
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
serializedVersion: 2
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
linearTexture: 0
|
||||
correctGamma: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 0
|
||||
cubemapConvolution: 0
|
||||
cubemapConvolutionSteps: 7
|
||||
cubemapConvolutionExponent: 1.5
|
||||
seamlessCubemap: 0
|
||||
textureFormat: -1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
filterMode: -1
|
||||
aniso: -1
|
||||
mipBias: -1
|
||||
wrapMode: -1
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
rGBM: 0
|
||||
compressionQuality: 50
|
||||
allowsAlphaSplitting: 0
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spritePixelsToUnits: 100
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: -1
|
||||
buildTargetSettings: []
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
spritePackingTag:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
71
Assets/uDesktopDuplication/Scripts/Cursor.cs
Normal file
71
Assets/uDesktopDuplication/Scripts/Cursor.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace uDesktopDuplication
|
||||
{
|
||||
|
||||
[AddComponentMenu("uDesktopDuplication/Cursor"), RequireComponent(typeof(Texture))]
|
||||
public class Cursor : MonoBehaviour
|
||||
{
|
||||
[SerializeField] Vector2 modelScale = Vector2.one;
|
||||
|
||||
Vector3 worldPosition { get; set; }
|
||||
|
||||
private Texture uddTexture_;
|
||||
private Monitor monitor { get { return uddTexture_.monitor; } }
|
||||
private Dictionary<int, Texture2D> textures_ = new Dictionary<int, Texture2D>();
|
||||
|
||||
void Start()
|
||||
{
|
||||
uddTexture_ = GetComponent<Texture>();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (monitor.isCursorVisible) {
|
||||
UpdatePosition();
|
||||
UpdateTexture();
|
||||
}
|
||||
UpdateMaterial();
|
||||
}
|
||||
|
||||
void UpdatePosition()
|
||||
{
|
||||
var x = (1f * monitor.cursorX / monitor.width - 0.5f) * modelScale.x;
|
||||
var y = (1f * monitor.cursorY / monitor.height - 0.5f) * modelScale.y;
|
||||
var iy = uddTexture_.invertY ? -1 : +1;
|
||||
var localPos = transform.right * x + iy * transform.up * y;
|
||||
worldPosition = transform.TransformPoint(localPos);
|
||||
}
|
||||
|
||||
void UpdateTexture()
|
||||
{
|
||||
var w = monitor.cursorShapeWidth;
|
||||
var h = monitor.cursorShapeHeight;
|
||||
if (w == 0 || h == 0) return;
|
||||
|
||||
var key = w + h * 100;
|
||||
if (!textures_.ContainsKey(key)) {
|
||||
var texture = new Texture2D(w, h, TextureFormat.BGRA32, false);
|
||||
texture.wrapMode = TextureWrapMode.Clamp;
|
||||
textures_.Add(key, texture);
|
||||
}
|
||||
|
||||
var cursorTexture = textures_[key];
|
||||
Assert.IsNotNull(cursorTexture);
|
||||
monitor.GetCursorTexture(cursorTexture.GetNativeTexturePtr());
|
||||
uddTexture_.material.SetTexture("_CursorTex", cursorTexture);
|
||||
}
|
||||
|
||||
void UpdateMaterial()
|
||||
{
|
||||
var x = monitor.isCursorVisible ? (float)monitor.cursorX / monitor.width : -9999f;
|
||||
var y = monitor.isCursorVisible ? (float)monitor.cursorY / monitor.height : -9999f;
|
||||
var w = (float)monitor.cursorShapeWidth / monitor.width;
|
||||
var h = (float)monitor.cursorShapeHeight / monitor.height;
|
||||
uddTexture_.material.SetVector("_CursorPositionScale", new Vector4(x, y, w, h));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2d9e4ca459ecb44da8815bafe9655c5
|
||||
timeCreated: 1477559806
|
||||
timeCreated: 1477635630
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
146
Assets/uDesktopDuplication/Scripts/Lib.cs
Normal file
146
Assets/uDesktopDuplication/Scripts/Lib.cs
Normal file
@@ -0,0 +1,146 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace uDesktopDuplication
|
||||
{
|
||||
|
||||
public enum Message
|
||||
{
|
||||
None = -1,
|
||||
Reinitialized = 0,
|
||||
TextureSizeChanged = 1,
|
||||
}
|
||||
|
||||
public enum CursorShapeType
|
||||
{
|
||||
Unspecified = 0,
|
||||
MonoChrome = 1,
|
||||
Color = 2,
|
||||
MaskedColor = 4,
|
||||
}
|
||||
|
||||
public enum MonitorRotation
|
||||
{
|
||||
Unspecified = 0,
|
||||
Identity = 1,
|
||||
Rotate90 = 2,
|
||||
Rotate180 = 3,
|
||||
Rotate270 = 4
|
||||
}
|
||||
|
||||
public enum MonitorState
|
||||
{
|
||||
NotSet = -1,
|
||||
Available = 0,
|
||||
InvalidArg = 1,
|
||||
AccessDenied = 2,
|
||||
Unsupported = 3,
|
||||
CurrentlyNotAvailable = 4,
|
||||
SessionDisconnected = 5,
|
||||
AccessLost = 6,
|
||||
TextureSizeInconsistent = 7,
|
||||
Unknown = 999,
|
||||
}
|
||||
|
||||
public enum DebugMode
|
||||
{
|
||||
None = 0,
|
||||
File = 1,
|
||||
UnityLog = 2, /* currently has bug when app exits. */
|
||||
}
|
||||
|
||||
public static class Lib
|
||||
{
|
||||
public delegate void MessageHandler(Message message);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void DebugLogDelegate(string str);
|
||||
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void InitializeUDD();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void FinalizeUDD();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void Reinitialize();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void Update();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern Message PopMessage();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void EnableDebug();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void DisableDebug();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void SetDebugMode(DebugMode mode);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void SetLogFunc(IntPtr func);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void SetErrorFunc(IntPtr func);
|
||||
[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();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetTotalHeight();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void SetTimeout(int timeout);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern IntPtr GetRenderEventFunc();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetId(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern MonitorState GetState(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void GetName(int id, StringBuilder buf, int len);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetLeft(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetRight(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetTop(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetBottom(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetWidth(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetHeight(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetDpiX(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetDpiY(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern MonitorRotation GetRotation(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern bool IsPrimary(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern bool IsCursorVisible(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetCursorX(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetCursorY(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetCursorShapeWidth(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetCursorShapeHeight(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetCursorShapePitch(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern CursorShapeType GetCursorShapeType(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void GetCursorTexture(int id, System.IntPtr ptr);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int SetTexturePtr(int id, IntPtr ptr);
|
||||
|
||||
public static string GetName(int id)
|
||||
{
|
||||
var buf = new StringBuilder(32);
|
||||
GetName(id, buf, buf.Capacity);
|
||||
return buf.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
12
Assets/uDesktopDuplication/Scripts/Lib.cs.meta
Normal file
12
Assets/uDesktopDuplication/Scripts/Lib.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cb02196c3e766c14ba398dc130e443e8
|
||||
timeCreated: 1477643164
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 89616e59e1cccb346bd4e959257990ca, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
228
Assets/uDesktopDuplication/Scripts/Manager.cs
Normal file
228
Assets/uDesktopDuplication/Scripts/Manager.cs
Normal file
@@ -0,0 +1,228 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace uDesktopDuplication
|
||||
{
|
||||
|
||||
public class Manager : MonoBehaviour
|
||||
{
|
||||
private static Manager instance_;
|
||||
public static Manager instance
|
||||
{
|
||||
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
|
||||
{
|
||||
get { return instance.monitors_; }
|
||||
}
|
||||
|
||||
static public int monitorCount
|
||||
{
|
||||
get { return Lib.GetMonitorCount(); }
|
||||
}
|
||||
|
||||
static public int cursorMonitorId
|
||||
{
|
||||
get { return Lib.GetCursorMonitorId(); }
|
||||
}
|
||||
|
||||
static public Monitor primary
|
||||
{
|
||||
get
|
||||
{
|
||||
return instance.monitors_.Find(monitor => monitor.isPrimary);
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField] DebugMode debugMode = DebugMode.File;
|
||||
[SerializeField] int desktopDuplicationApiTimeout = 0;
|
||||
[SerializeField] float retryReinitializationDuration = 1f;
|
||||
|
||||
private Coroutine renderCoroutine_ = null;
|
||||
private bool shouldReinitialize_ = false;
|
||||
private float reinitializationTimer_ = 0f;
|
||||
|
||||
public delegate void ReinitializeHandler();
|
||||
public static event ReinitializeHandler onReinitialized;
|
||||
|
||||
public static Monitor GetMonitor(int id)
|
||||
{
|
||||
if (id < 0 || id >= Manager.monitors.Count) {
|
||||
Debug.LogErrorFormat("[uDD::Error] there is no monitor whose id is {0}.", id);
|
||||
return Manager.primary;
|
||||
}
|
||||
return monitors[Mathf.Clamp(id, 0, Manager.monitorCount - 1)];
|
||||
}
|
||||
|
||||
void Awake()
|
||||
{
|
||||
Lib.SetDebugMode(debugMode);
|
||||
Lib.SetTimeout(desktopDuplicationApiTimeout);
|
||||
Lib.InitializeUDD();
|
||||
|
||||
if (instance_ != null) {
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
instance_ = this;
|
||||
|
||||
CreateMonitors();
|
||||
}
|
||||
|
||||
void OnApplicationQuit()
|
||||
{
|
||||
Lib.FinalizeUDD();
|
||||
DestroyMonitors();
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
renderCoroutine_ = StartCoroutine(OnRender());
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
if (renderCoroutine_ != null) {
|
||||
StopCoroutine(renderCoroutine_);
|
||||
renderCoroutine_ = null;
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
Lib.Update();
|
||||
ReinitializeIfNeeded();
|
||||
UpdateMessage();
|
||||
}
|
||||
|
||||
[ContextMenu("Reinitialize")]
|
||||
public void Reinitialize()
|
||||
{
|
||||
Debug.Log("[uDD] Reinitialize");
|
||||
Lib.Reinitialize();
|
||||
CreateMonitors();
|
||||
if (onReinitialized != null) {
|
||||
onReinitialized();
|
||||
}
|
||||
}
|
||||
|
||||
void ReinitializeIfNeeded()
|
||||
{
|
||||
bool reinitializeNeeded = false;
|
||||
|
||||
for (int i = 0; i < monitors.Count; ++i) {
|
||||
var monitor = monitors[i];
|
||||
if (
|
||||
monitor.state == MonitorState.NotSet ||
|
||||
monitor.state == MonitorState.AccessLost ||
|
||||
monitor.state == MonitorState.AccessDenied ||
|
||||
monitor.state == MonitorState.SessionDisconnected
|
||||
) {
|
||||
reinitializeNeeded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Lib.HasMonitorCountChanged()) {
|
||||
reinitializeNeeded = true;
|
||||
}
|
||||
|
||||
if (!shouldReinitialize_ && reinitializeNeeded) {
|
||||
shouldReinitialize_ = true;
|
||||
reinitializationTimer_ = 0f;
|
||||
}
|
||||
|
||||
if (shouldReinitialize_) {
|
||||
if (reinitializationTimer_ > retryReinitializationDuration) {
|
||||
Reinitialize();
|
||||
shouldReinitialize_ = false;
|
||||
}
|
||||
reinitializationTimer_ += Time.deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateMessage()
|
||||
{
|
||||
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;
|
||||
}
|
||||
message = Lib.PopMessage();
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator OnRender()
|
||||
{
|
||||
for (;;) {
|
||||
yield return new WaitForEndOfFrame();
|
||||
for (int i = 0; i < monitors.Count; ++i) {
|
||||
var monitor = monitors[i];
|
||||
if (monitor.shouldBeUpdated) {
|
||||
monitor.Render();
|
||||
}
|
||||
monitor.shouldBeUpdated = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CreateMonitors()
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RecreateTextures()
|
||||
{
|
||||
for (int i = 0; i < monitorCount; ++i) {
|
||||
monitors[i].CreateTextureIfNeeded();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
12
Assets/uDesktopDuplication/Scripts/Manager.cs.meta
Normal file
12
Assets/uDesktopDuplication/Scripts/Manager.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4dccb434f55ebee4f91bfb9935092bbf
|
||||
timeCreated: 1477643174
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 89616e59e1cccb346bd4e959257990ca, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
289
Assets/uDesktopDuplication/Scripts/Monitor.cs
Normal file
289
Assets/uDesktopDuplication/Scripts/Monitor.cs
Normal file
@@ -0,0 +1,289 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace uDesktopDuplication
|
||||
{
|
||||
|
||||
public class Monitor
|
||||
{
|
||||
public Monitor(int id)
|
||||
{
|
||||
this.id = id;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case MonitorState.Available:
|
||||
break;
|
||||
case MonitorState.InvalidArg:
|
||||
Debug.LogErrorFormat("[uDD] {0}:{1} => Invalid.", id, name);
|
||||
break;
|
||||
case MonitorState.AccessDenied:
|
||||
Debug.LogWarningFormat("[uDD] {0}:{1} => Access Denied.", id, name);
|
||||
break;
|
||||
case MonitorState.Unsupported:
|
||||
Debug.LogWarningFormat("[uDD] {0}:{1} => Unsupported.", id, name);
|
||||
break;
|
||||
case MonitorState.SessionDisconnected:
|
||||
Debug.LogWarningFormat("[uDD] {0}:{1} => Disconnected.", id, name);
|
||||
break;
|
||||
case MonitorState.NotSet:
|
||||
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
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public bool exists
|
||||
{
|
||||
get { return id < Manager.monitorCount; }
|
||||
}
|
||||
|
||||
public MonitorState state
|
||||
{
|
||||
get { return Lib.GetState(id); }
|
||||
}
|
||||
|
||||
public bool available
|
||||
{
|
||||
get { return state == MonitorState.Available; }
|
||||
}
|
||||
|
||||
public string name
|
||||
{
|
||||
get { return Lib.GetName(id); }
|
||||
}
|
||||
|
||||
public bool isPrimary
|
||||
{
|
||||
get { return Lib.IsPrimary(id); }
|
||||
}
|
||||
|
||||
public int left
|
||||
{
|
||||
get { return Lib.GetLeft(id); }
|
||||
}
|
||||
|
||||
public int right
|
||||
{
|
||||
get { return Lib.GetRight(id); }
|
||||
}
|
||||
|
||||
public int top
|
||||
{
|
||||
get { return Lib.GetTop(id); }
|
||||
}
|
||||
|
||||
public int bottom
|
||||
{
|
||||
get { return Lib.GetBottom(id); }
|
||||
}
|
||||
|
||||
public int width
|
||||
{
|
||||
get { return Lib.GetWidth(id); }
|
||||
}
|
||||
|
||||
public int height
|
||||
{
|
||||
get { return Lib.GetHeight(id); }
|
||||
}
|
||||
|
||||
public int dpiX
|
||||
{
|
||||
get
|
||||
{
|
||||
var dpi = Lib.GetDpiX(id);
|
||||
if (dpi == 0) dpi = 100; // when monitors are duplicated
|
||||
return dpi;
|
||||
}
|
||||
}
|
||||
|
||||
public int dpiY
|
||||
{
|
||||
get
|
||||
{
|
||||
var dpi = Lib.GetDpiY(id);
|
||||
if (dpi == 0) dpi = 100; // when monitors are duplicated
|
||||
return dpi;
|
||||
}
|
||||
}
|
||||
|
||||
public float widthMeter
|
||||
{
|
||||
get { return width / dpiX * 0.0254f; }
|
||||
}
|
||||
|
||||
public float heightMeter
|
||||
{
|
||||
get { return height / dpiY * 0.0254f; }
|
||||
}
|
||||
|
||||
public MonitorRotation rotation
|
||||
{
|
||||
get { return Lib.GetRotation(id); }
|
||||
}
|
||||
|
||||
public float aspect
|
||||
{
|
||||
get { return (float)width / height; }
|
||||
}
|
||||
|
||||
public bool isHorizontal
|
||||
{
|
||||
get { return width > height; }
|
||||
}
|
||||
|
||||
public bool isVertical
|
||||
{
|
||||
get { return height > width; }
|
||||
}
|
||||
|
||||
public bool isCursorVisible
|
||||
{
|
||||
get { return Lib.IsCursorVisible(id); }
|
||||
}
|
||||
|
||||
public int cursorX
|
||||
{
|
||||
get { return Lib.GetCursorX(id); }
|
||||
}
|
||||
|
||||
public int cursorY
|
||||
{
|
||||
get { return Lib.GetCursorY(id); }
|
||||
}
|
||||
|
||||
public int systemCursorX
|
||||
{
|
||||
get
|
||||
{
|
||||
var p = Utility.GetCursorPos();
|
||||
return p.x - left;
|
||||
}
|
||||
}
|
||||
|
||||
public int systemCursorY
|
||||
{
|
||||
get
|
||||
{
|
||||
var p = Utility.GetCursorPos();
|
||||
return p.y - top;
|
||||
}
|
||||
}
|
||||
|
||||
public int cursorShapeWidth
|
||||
{
|
||||
get { return Lib.GetCursorShapeWidth(id); }
|
||||
}
|
||||
|
||||
public int cursorShapeHeight
|
||||
{
|
||||
get { return Lib.GetCursorShapeHeight(id); }
|
||||
}
|
||||
|
||||
public CursorShapeType cursorShapeType
|
||||
{
|
||||
get { return Lib.GetCursorShapeType(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 errorTexture;
|
||||
if (texture_ == null) CreateTextureIfNeeded();
|
||||
return texture_;
|
||||
}
|
||||
}
|
||||
|
||||
public void Render()
|
||||
{
|
||||
if (texture_ && available) {
|
||||
Lib.SetTexturePtr(id, texture_.GetNativeTexturePtr());
|
||||
GL.IssuePluginEvent(Lib.GetRenderEventFunc(), id);
|
||||
}
|
||||
}
|
||||
|
||||
public void GetCursorTexture(System.IntPtr ptr)
|
||||
{
|
||||
Lib.GetCursorTexture(id, ptr);
|
||||
}
|
||||
|
||||
public void CreateTextureIfNeeded()
|
||||
{
|
||||
if (!available) return;
|
||||
|
||||
var w = isHorizontal ? width : height;
|
||||
var h = isHorizontal ? height : width;
|
||||
bool shouldCreate = true;
|
||||
|
||||
if (texture_ && texture_.width == w && texture_.height == h) {
|
||||
shouldCreate = false;
|
||||
}
|
||||
|
||||
if (w <= 0 || h <= 0) {
|
||||
shouldCreate = false;
|
||||
}
|
||||
|
||||
if (shouldCreate) {
|
||||
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()
|
||||
{
|
||||
CreateTextureIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
12
Assets/uDesktopDuplication/Scripts/Monitor.cs.meta
Normal file
12
Assets/uDesktopDuplication/Scripts/Monitor.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 310590b113d909b43b4ea19702904ac3
|
||||
timeCreated: 1477643181
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 89616e59e1cccb346bd4e959257990ca, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,44 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace uDesktopDuplication
|
||||
{
|
||||
|
||||
[RequireComponent(typeof(uDesktopDuplication))]
|
||||
public class MouseDrawer : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
Transform cursor;
|
||||
[SerializeField]
|
||||
Vector2 modelScale = Vector2.one;
|
||||
[SerializeField]
|
||||
Vector2 offset = new Vector2(0.5f, 0.5f);
|
||||
|
||||
uDesktopDuplication udd_;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
udd_ = GetComponent<uDesktopDuplication>();
|
||||
udd_.onMouseMove += OnMouseMove;
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
udd_.onMouseMove -= OnMouseMove;
|
||||
}
|
||||
|
||||
void OnMouseMove(Vector2 pos)
|
||||
{
|
||||
var x = pos.x * modelScale.x * 0.5f;
|
||||
var y = pos.y * modelScale.y * 0.5f;
|
||||
var iy = udd_.invertY ? +1 : -1;
|
||||
var localPos = transform.right * x + iy * transform.up * y;
|
||||
|
||||
var worldPos = transform.TransformPoint(localPos);
|
||||
worldPos += cursor.right * offset.x * cursor.localScale.x;
|
||||
worldPos += -cursor.up * offset.y * cursor.localScale.y;
|
||||
|
||||
cursor.position = worldPos;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
208
Assets/uDesktopDuplication/Scripts/Texture.cs
Normal file
208
Assets/uDesktopDuplication/Scripts/Texture.cs
Normal file
@@ -0,0 +1,208 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace uDesktopDuplication
|
||||
{
|
||||
|
||||
[AddComponentMenu("uDesktopDuplication/Texture"), RequireComponent(typeof(Cursor))]
|
||||
public class Texture : MonoBehaviour
|
||||
{
|
||||
private Monitor monitor_;
|
||||
public Monitor monitor
|
||||
{
|
||||
get { return monitor_; }
|
||||
set
|
||||
{
|
||||
monitor_ = value;
|
||||
if (monitor_ != null) {
|
||||
material = GetComponent<Renderer>().material; // clone
|
||||
material.mainTexture = monitor_.texture;
|
||||
material.SetFloat("_Width", transform.localScale.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int monitorId
|
||||
{
|
||||
get { return monitor.id; }
|
||||
set { monitor = Manager.GetMonitor(value); }
|
||||
}
|
||||
|
||||
[Header("Invert UVs")]
|
||||
public bool invertX = false;
|
||||
public bool invertY = false;
|
||||
|
||||
[Header("Clip")]
|
||||
public bool useClip = false;
|
||||
public Vector2 clipPos = Vector2.zero;
|
||||
public Vector2 clipScale = new Vector2(0.2f, 0.2f);
|
||||
|
||||
public enum MeshForwardDirection
|
||||
{
|
||||
Y = 0,
|
||||
Z = 1,
|
||||
}
|
||||
|
||||
public bool bend
|
||||
{
|
||||
get
|
||||
{
|
||||
return material.GetInt("_Bend") != 0;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value) {
|
||||
material.EnableKeyword("BEND_ON");
|
||||
} else {
|
||||
material.DisableKeyword("BEND_ON");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MeshForwardDirection meshForwardDirection
|
||||
{
|
||||
get
|
||||
{
|
||||
return (MeshForwardDirection)material.GetInt("_Forward");
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (value) {
|
||||
case MeshForwardDirection.Y:
|
||||
material.SetInt("_Forward", 0);
|
||||
material.EnableKeyword("_BEND_Y");
|
||||
material.DisableKeyword("_BEND_Z");
|
||||
break;
|
||||
case MeshForwardDirection.Z:
|
||||
material.SetInt("_Forward", 1);
|
||||
material.DisableKeyword("_BEND_Y");
|
||||
material.EnableKeyword("_BEND_Z");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float radius
|
||||
{
|
||||
get { return material.GetFloat("_Radius"); }
|
||||
set { material.SetFloat("_Radius", value); }
|
||||
}
|
||||
|
||||
public float width
|
||||
{
|
||||
get { return material.GetFloat("_Width"); }
|
||||
set { material.SetFloat("_Width", value); }
|
||||
}
|
||||
|
||||
public float thickness
|
||||
{
|
||||
get { return material.GetFloat("_Thickness"); }
|
||||
set { material.SetFloat("_Thickness", value); }
|
||||
}
|
||||
|
||||
public Material material
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
void Awake()
|
||||
{
|
||||
AddCursorIfNotAttached();
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
if (monitor == null) {
|
||||
monitor = Manager.primary;
|
||||
}
|
||||
Manager.onReinitialized += Reinitialize;
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
Manager.onReinitialized -= Reinitialize;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
monitor.shouldBeUpdated = true;
|
||||
UpdateMaterial();
|
||||
}
|
||||
|
||||
void AddCursorIfNotAttached()
|
||||
{
|
||||
if (!GetComponent<Cursor>())
|
||||
{
|
||||
gameObject.AddComponent<Cursor>();
|
||||
}
|
||||
}
|
||||
|
||||
void Reinitialize()
|
||||
{
|
||||
// Monitor instance is released here when initialized.
|
||||
monitor = Manager.GetMonitor(monitor.id);
|
||||
}
|
||||
|
||||
void UpdateMaterial()
|
||||
{
|
||||
Invert();
|
||||
Rotate();
|
||||
Clip();
|
||||
}
|
||||
|
||||
void Invert()
|
||||
{
|
||||
if (invertX) {
|
||||
material.EnableKeyword("INVERT_X");
|
||||
} else {
|
||||
material.DisableKeyword("INVERT_X");
|
||||
}
|
||||
|
||||
if (invertY) {
|
||||
material.EnableKeyword("INVERT_Y");
|
||||
} else {
|
||||
material.DisableKeyword("INVERT_Y");
|
||||
}
|
||||
}
|
||||
|
||||
void Rotate()
|
||||
{
|
||||
switch (monitor.rotation)
|
||||
{
|
||||
case MonitorRotation.Identity:
|
||||
material.DisableKeyword("ROTATE90");
|
||||
material.DisableKeyword("ROTATE180");
|
||||
material.DisableKeyword("ROTATE270");
|
||||
break;
|
||||
case MonitorRotation.Rotate90:
|
||||
material.EnableKeyword("ROTATE90");
|
||||
material.DisableKeyword("ROTATE180");
|
||||
material.DisableKeyword("ROTATE270");
|
||||
break;
|
||||
case MonitorRotation.Rotate180:
|
||||
material.DisableKeyword("ROTATE90");
|
||||
material.EnableKeyword("ROTATE180");
|
||||
material.DisableKeyword("ROTATE270");
|
||||
break;
|
||||
case MonitorRotation.Rotate270:
|
||||
material.DisableKeyword("ROTATE90");
|
||||
material.DisableKeyword("ROTATE180");
|
||||
material.EnableKeyword("ROTATE270");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Clip()
|
||||
{
|
||||
if (useClip) {
|
||||
material.EnableKeyword("USE_CLIP");
|
||||
material.SetVector("_ClipPositionScale", new Vector4(clipPos.x, clipPos.y, clipScale.x, clipScale.y));
|
||||
} else {
|
||||
material.DisableKeyword("USE_CLIP");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb54a34570e4747429b1c5b66c69d356
|
||||
timeCreated: 1477559813
|
||||
timeCreated: 1477635636
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
42
Assets/uDesktopDuplication/Scripts/Utility.cs
Normal file
42
Assets/uDesktopDuplication/Scripts/Utility.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace uDesktopDuplication
|
||||
{
|
||||
|
||||
public struct MousePoint
|
||||
{
|
||||
public int x;
|
||||
public int y;
|
||||
}
|
||||
|
||||
public static class Utility
|
||||
{
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool GetCursorPos(out MousePoint point);
|
||||
|
||||
public static MousePoint GetCursorPos()
|
||||
{
|
||||
MousePoint p;
|
||||
if (!GetCursorPos(out p)) {
|
||||
p.x = -1;
|
||||
p.y = -1;
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
12
Assets/uDesktopDuplication/Scripts/Utility.cs.meta
Normal file
12
Assets/uDesktopDuplication/Scripts/Utility.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6c7e8da181b03ce4fb343ffa33cbb929
|
||||
timeCreated: 1479043365
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 89616e59e1cccb346bd4e959257990ca, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,97 +0,0 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace uDesktopDuplication
|
||||
{
|
||||
|
||||
public class uDesktopDuplication : MonoBehaviour
|
||||
{
|
||||
[DllImport("uDesktopDuplication")]
|
||||
private static extern void Initialize();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
private static extern int GetWidth();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
private static extern int GetHeight();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
private static extern bool IsPointerVisible();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
private static extern int GetPointerX();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
private static extern int GetPointerY();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
private static extern int SetTexturePtr(IntPtr ptr);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
private static extern IntPtr GetRenderEventFunc();
|
||||
|
||||
private Material material_;
|
||||
public bool invertX = false;
|
||||
public bool invertY = false;
|
||||
|
||||
public delegate void MouseMoveEventHandler(Vector2 pos);
|
||||
public MouseMoveEventHandler onMouseMove { get; set; }
|
||||
|
||||
private Coroutine renderCoroutine_ = null;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
var tex = new Texture2D(GetWidth(), GetHeight(), TextureFormat.BGRA32, false);
|
||||
material_ = GetComponent<Renderer>().material;
|
||||
material_.mainTexture = tex;
|
||||
|
||||
SetTexturePtr(tex.GetNativeTexturePtr());
|
||||
renderCoroutine_ = StartCoroutine(OnRender());
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
if (renderCoroutine_ != null) {
|
||||
StopCoroutine(renderCoroutine_);
|
||||
renderCoroutine_ = null;
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
UpdateMouseEvent();
|
||||
UpdateMaterial();
|
||||
}
|
||||
|
||||
void UpdateMouseEvent()
|
||||
{
|
||||
var isVisible = IsPointerVisible();
|
||||
if (isVisible && onMouseMove != null) {
|
||||
var x = GetPointerX();
|
||||
var y = GetPointerY();
|
||||
var w = GetWidth();
|
||||
var h = GetHeight();
|
||||
onMouseMove(new Vector2(2f * x / w - 1f, 1f - 2f * y / h));
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateMaterial()
|
||||
{
|
||||
if (invertX) {
|
||||
material_.EnableKeyword("INVERT_X");
|
||||
} else {
|
||||
material_.DisableKeyword("INVERT_X");
|
||||
}
|
||||
|
||||
if (invertY) {
|
||||
material_.EnableKeyword("INVERT_Y");
|
||||
} else {
|
||||
material_.DisableKeyword("INVERT_Y");
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator OnRender()
|
||||
{
|
||||
for (;;) {
|
||||
yield return new WaitForEndOfFrame();
|
||||
GL.IssuePluginEvent(GetRenderEventFunc(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,13 +1,117 @@
|
||||
#ifndef UDD_COMMON_CGINC
|
||||
#define UDD_COMMON_CGINC
|
||||
|
||||
inline void uddInvertUV(inout float2 uv)
|
||||
#include "UnityCG.cginc"
|
||||
#include "./uDD_Params.cginc"
|
||||
|
||||
struct appdata
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 vertex : SV_POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct Input
|
||||
{
|
||||
float2 uv_MainTex;
|
||||
};
|
||||
|
||||
inline float2 uddInvertUV(float2 uv)
|
||||
{
|
||||
#ifdef INVERT_X
|
||||
uv.x *= -1.0;
|
||||
uv.x = 1.0 - uv.x;
|
||||
#endif
|
||||
#ifdef INVERT_Y
|
||||
uv.y *= -1.0;
|
||||
uv.y = 1.0 - uv.y;
|
||||
#endif
|
||||
return uv;
|
||||
}
|
||||
|
||||
inline float2 uddRotateUV(float2 uv)
|
||||
{
|
||||
#ifdef ROTATE90
|
||||
float2 tmp = uv;
|
||||
uv.x = tmp.y;
|
||||
uv.y = 1.0 - tmp.x;
|
||||
#elif ROTATE180
|
||||
uv.x = 1.0 - uv.x;
|
||||
uv.y = 1.0 - uv.y;
|
||||
#elif ROTATE270
|
||||
float2 tmp = uv;
|
||||
uv.x = 1.0 - tmp.y;
|
||||
uv.y = tmp.x;
|
||||
#endif
|
||||
return uv;
|
||||
}
|
||||
|
||||
inline float2 uddClipUV(float2 uv)
|
||||
{
|
||||
uv.x = _ClipX + uv.x * _ClipWidth;
|
||||
uv.y = _ClipY + uv.y * _ClipHeight;
|
||||
return uv;
|
||||
}
|
||||
|
||||
inline void uddConvertToLinearIfNeeded(inout fixed3 rgb)
|
||||
{
|
||||
if (!IsGammaSpace()) {
|
||||
rgb = GammaToLinearSpace(rgb);
|
||||
}
|
||||
}
|
||||
|
||||
inline fixed4 uddGetScreenTexture(float2 uv)
|
||||
{
|
||||
fixed4 c = tex2D(_MainTex, uv);
|
||||
return c;
|
||||
}
|
||||
|
||||
inline fixed4 uddGetCursorTexture(float2 uv)
|
||||
{
|
||||
uv.x = (uv.x - _CursorX) / _CursorWidth;
|
||||
uv.y = (uv.y - _CursorY) / _CursorHeight;
|
||||
fixed4 c = tex2D(_CursorTex, uv);
|
||||
fixed a = step(0, uv.x) * step(0, uv.y) * step(uv.x, 1) * step(uv.y, 1);
|
||||
c.a *= step(0.01, a);
|
||||
return c;
|
||||
}
|
||||
|
||||
inline fixed4 uddGetScreenTextureWithCursor(float2 uv)
|
||||
{
|
||||
uv = uddInvertUV(uv);
|
||||
#ifdef USE_CLIP
|
||||
uv = uddClipUV(uv);
|
||||
#endif
|
||||
fixed4 screen = uddGetScreenTexture(uddRotateUV(uv));
|
||||
fixed4 cursor = uddGetCursorTexture(uv);
|
||||
fixed4 color = lerp(screen, cursor, cursor.a);
|
||||
uddConvertToLinearIfNeeded(color.rgb);
|
||||
return color;
|
||||
}
|
||||
|
||||
inline void uddBendVertex(inout float4 v, half radius, half width, half thickness)
|
||||
{
|
||||
#ifdef BEND_ON
|
||||
half angle = width * v.x / radius;
|
||||
#ifdef _FORWARD_Z
|
||||
v.y *= thickness;
|
||||
radius -= v.y;
|
||||
v.y += radius * (1 - cos(angle));
|
||||
#elif _FORWARD_Y
|
||||
v.z *= thickness;
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
24
Assets/uDesktopDuplication/Shaders/uDD_Params.cginc
Normal file
24
Assets/uDesktopDuplication/Shaders/uDD_Params.cginc
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef UDD_PARAMS_CGINC
|
||||
#define UDD_PARAMS_CGINC
|
||||
|
||||
sampler2D _MainTex;
|
||||
fixed4 _Color;
|
||||
sampler2D _CursorTex;
|
||||
|
||||
half4 _CursorPositionScale;
|
||||
#define _CursorX _CursorPositionScale.x
|
||||
#define _CursorY _CursorPositionScale.y
|
||||
#define _CursorWidth _CursorPositionScale.z
|
||||
#define _CursorHeight _CursorPositionScale.w
|
||||
|
||||
half4 _ClipPositionScale;
|
||||
#define _ClipX _ClipPositionScale.x
|
||||
#define _ClipY _ClipPositionScale.y
|
||||
#define _ClipWidth _ClipPositionScale.z
|
||||
#define _ClipHeight _ClipPositionScale.w
|
||||
|
||||
#ifndef SURFACE_SHADER
|
||||
float4 _MainTex_ST;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
9
Assets/uDesktopDuplication/Shaders/uDD_Params.cginc.meta
Normal file
9
Assets/uDesktopDuplication/Shaders/uDD_Params.cginc.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 54044418ab4785c4a84dd4be507db6c2
|
||||
timeCreated: 1478266678
|
||||
licenseType: Pro
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -7,11 +7,14 @@ Properties
|
||||
_MainTex ("Albedo (RGB)", 2D) = "white" {}
|
||||
_Glossiness ("Smoothness", Range(0, 1)) = 0.5
|
||||
_Metallic ("Metallic", Range(0, 1)) = 0.0
|
||||
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
|
||||
}
|
||||
|
||||
SubShader
|
||||
{
|
||||
Tags { "RenderType"="Opaque" }
|
||||
|
||||
Cull [_Cull]
|
||||
|
||||
CGPROGRAM
|
||||
|
||||
@@ -19,24 +22,19 @@ SubShader
|
||||
#pragma surface surf Standard fullforwardshadows
|
||||
#pragma multi_compile ___ INVERT_X
|
||||
#pragma multi_compile ___ INVERT_Y
|
||||
#pragma multi_compile ___ ROTATE90 ROTATE180 ROTATE270
|
||||
#pragma multi_compile ___ USE_BEND
|
||||
#pragma multi_compile ___ USE_CLIP
|
||||
|
||||
#define SURFACE_SHADER
|
||||
#include "./uDD_Common.cginc"
|
||||
|
||||
sampler2D _MainTex;
|
||||
|
||||
struct Input
|
||||
{
|
||||
float2 uv_MainTex;
|
||||
};
|
||||
|
||||
half _Glossiness;
|
||||
half _Metallic;
|
||||
fixed4 _Color;
|
||||
|
||||
void surf(Input IN, inout SurfaceOutputStandard o)
|
||||
{
|
||||
uddInvertUV(IN.uv_MainTex);
|
||||
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
|
||||
fixed4 c = uddGetScreenTextureWithCursor(IN.uv_MainTex) * _Color;
|
||||
o.Albedo = c.rgb;
|
||||
o.Metallic = _Metallic;
|
||||
o.Smoothness = _Glossiness;
|
||||
|
||||
@@ -5,6 +5,12 @@ Properties
|
||||
{
|
||||
_Color ("Color", Color) = (1, 1, 1, 1)
|
||||
_MainTex ("Texture", 2D) = "white" {}
|
||||
_CursorTex ("Cursor Texture", 2D) = "white" {}
|
||||
[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
|
||||
}
|
||||
|
||||
SubShader
|
||||
@@ -12,30 +18,20 @@ SubShader
|
||||
|
||||
Tags { "RenderType"="Opaque" }
|
||||
|
||||
Cull [_Cull]
|
||||
|
||||
CGINCLUDE
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
#include "./uDD_Common.cginc"
|
||||
|
||||
struct appdata
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 vertex : SV_POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
sampler2D _MainTex;
|
||||
float4 _MainTex_ST;
|
||||
fixed4 _Color;
|
||||
half _Radius;
|
||||
half _Width;
|
||||
half _Thickness;
|
||||
|
||||
v2f vert(appdata v)
|
||||
{
|
||||
v2f o;
|
||||
uddBendVertex(v.vertex, _Radius, _Width, _Thickness);
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
|
||||
return o;
|
||||
@@ -43,8 +39,7 @@ v2f vert(appdata v)
|
||||
|
||||
fixed4 frag(v2f i) : SV_Target
|
||||
{
|
||||
uddInvertUV(i.uv);
|
||||
return tex2D(_MainTex, i.uv) * _Color;
|
||||
return uddGetScreenTextureWithCursor(i.uv);
|
||||
}
|
||||
|
||||
ENDCG
|
||||
@@ -56,6 +51,10 @@ Pass
|
||||
#pragma fragment frag
|
||||
#pragma multi_compile ___ INVERT_X
|
||||
#pragma multi_compile ___ INVERT_Y
|
||||
#pragma multi_compile ___ ROTATE90 ROTATE180 ROTATE270
|
||||
#pragma multi_compile ___ USE_CLIP
|
||||
#pragma multi_compile ___ BEND_ON
|
||||
#pragma multi_compile _FORWARD_Y _FORWARD_Z
|
||||
ENDCG
|
||||
}
|
||||
|
||||
@@ -63,4 +62,4 @@ Pass
|
||||
|
||||
Fallback "Unlit/Texture"
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,12 @@ Properties
|
||||
_Color ("Color", Color) = (1, 1, 1, 1)
|
||||
_MainTex ("Texture", 2D) = "white" {}
|
||||
_Mask ("Mask", Range(0, 1)) = 0.1
|
||||
_CursorTex ("Cursor Texture", 2D) = "white" {}
|
||||
[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
|
||||
}
|
||||
|
||||
SubShader
|
||||
@@ -13,34 +19,23 @@ SubShader
|
||||
|
||||
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
|
||||
|
||||
ZWrite Off
|
||||
Cull [_Cull]
|
||||
ZWrite On
|
||||
Blend SrcAlpha OneMinusSrcAlpha
|
||||
|
||||
CGINCLUDE
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
#include "./uDD_Common.cginc"
|
||||
|
||||
struct appdata
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 vertex : SV_POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
sampler2D _MainTex;
|
||||
float4 _MainTex_ST;
|
||||
fixed4 _Color;
|
||||
fixed _Mask;
|
||||
half _Radius;
|
||||
half _Width;
|
||||
half _Thickness;
|
||||
|
||||
v2f vert(appdata v)
|
||||
{
|
||||
v2f o;
|
||||
uddBendVertex(v.vertex, _Radius, _Width, _Thickness);
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
|
||||
return o;
|
||||
@@ -48,10 +43,9 @@ v2f vert(appdata v)
|
||||
|
||||
fixed4 frag(v2f i) : SV_Target
|
||||
{
|
||||
uddInvertUV(i.uv);
|
||||
fixed4 tex = tex2D(_MainTex, i.uv);
|
||||
fixed4 tex = uddGetScreenTextureWithCursor(i.uv);
|
||||
fixed alpha = pow((tex.r + tex.g + tex.b) / 3.0, _Mask);
|
||||
return fixed4(tex.rgb * _Color.rgb, alpha);
|
||||
return fixed4(tex.rgb * _Color.rgb, alpha * _Color.a);
|
||||
}
|
||||
|
||||
ENDCG
|
||||
@@ -63,6 +57,10 @@ Pass
|
||||
#pragma fragment frag
|
||||
#pragma multi_compile ___ INVERT_X
|
||||
#pragma multi_compile ___ INVERT_Y
|
||||
#pragma multi_compile ___ ROTATE90 ROTATE180 ROTATE270
|
||||
#pragma multi_compile ___ USE_BEND
|
||||
#pragma multi_compile ___ USE_CLIP
|
||||
#pragma multi_compile _BEND_OFF _BEND_Y _BEND_Z
|
||||
ENDCG
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,12 @@ Properties
|
||||
{
|
||||
_Color ("Color", Color) = (1, 1, 1, 1)
|
||||
_MainTex ("Texture", 2D) = "white" {}
|
||||
_CursorTex ("Cursor Texture", 2D) = "white" {}
|
||||
[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
|
||||
}
|
||||
|
||||
SubShader
|
||||
@@ -12,33 +18,22 @@ SubShader
|
||||
|
||||
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
|
||||
|
||||
ZWrite Off
|
||||
Cull [_Cull]
|
||||
ZWrite On
|
||||
Blend SrcAlpha OneMinusSrcAlpha
|
||||
|
||||
CGINCLUDE
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
#include "./uDD_Common.cginc"
|
||||
|
||||
struct appdata
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 vertex : SV_POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
sampler2D _MainTex;
|
||||
float4 _MainTex_ST;
|
||||
fixed4 _Color;
|
||||
half _Radius;
|
||||
half _Width;
|
||||
half _Thickness;
|
||||
|
||||
v2f vert(appdata v)
|
||||
{
|
||||
v2f o;
|
||||
uddBendVertex(v.vertex, _Radius, _Width, _Thickness);
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
|
||||
return o;
|
||||
@@ -46,8 +41,7 @@ v2f vert(appdata v)
|
||||
|
||||
fixed4 frag(v2f i) : SV_Target
|
||||
{
|
||||
uddInvertUV(i.uv);
|
||||
return fixed4(tex2D(_MainTex, i.uv).rgb, 1.0) * _Color;
|
||||
return fixed4(uddGetScreenTextureWithCursor(i.uv).rgb, 1.0) * _Color;
|
||||
}
|
||||
|
||||
ENDCG
|
||||
@@ -59,6 +53,10 @@ Pass
|
||||
#pragma fragment frag
|
||||
#pragma multi_compile ___ INVERT_X
|
||||
#pragma multi_compile ___ INVERT_Y
|
||||
#pragma multi_compile ___ ROTATE90 ROTATE180 ROTATE270
|
||||
#pragma multi_compile ___ USE_BEND
|
||||
#pragma multi_compile ___ USE_CLIP
|
||||
#pragma multi_compile _BEND_OFF _BEND_Y _BEND_Z
|
||||
ENDCG
|
||||
}
|
||||
|
||||
|
||||
39
Plugins/uDesktopDuplication/uDesktopDuplication/Common.cpp
Normal file
39
Plugins/uDesktopDuplication/uDesktopDuplication/Common.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
#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;
|
||||
extern std::queue<Message> g_messages;
|
||||
|
||||
|
||||
IUnityInterfaces* GetUnity()
|
||||
{
|
||||
return g_unity;
|
||||
}
|
||||
|
||||
|
||||
ComPtr<ID3D11Device> GetDevice()
|
||||
{
|
||||
return GetUnity()->Get<IUnityGraphicsD3D11>()->GetDevice();
|
||||
}
|
||||
|
||||
|
||||
const std::unique_ptr<MonitorManager>& GetMonitorManager()
|
||||
{
|
||||
return g_manager;
|
||||
}
|
||||
|
||||
|
||||
void SendMessageToUnity(Message message)
|
||||
{
|
||||
g_messages.push(message);
|
||||
}
|
||||
39
Plugins/uDesktopDuplication/uDesktopDuplication/Common.h
Normal file
39
Plugins/uDesktopDuplication/uDesktopDuplication/Common.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <wrl/client.h>
|
||||
|
||||
|
||||
// Unity interface and ID3D11Device getters
|
||||
struct IUnityInterfaces;
|
||||
IUnityInterfaces* GetUnity();
|
||||
|
||||
struct ID3D11Device;
|
||||
Microsoft::WRL::ComPtr<ID3D11Device> GetDevice();
|
||||
|
||||
|
||||
// Manager getter
|
||||
class MonitorManager;
|
||||
const std::unique_ptr<MonitorManager>& GetMonitorManager();
|
||||
|
||||
|
||||
template <class T>
|
||||
auto MakeUniqueWithReleaser(T* ptr)
|
||||
{
|
||||
const auto deleter = [](T* ptr)
|
||||
{
|
||||
if (ptr != nullptr) ptr->Release();
|
||||
};
|
||||
return std::unique_ptr<T, decltype(deleter)>(ptr, deleter);
|
||||
}
|
||||
|
||||
|
||||
// Message is pooled and fetch from Unity.
|
||||
enum class Message
|
||||
{
|
||||
None = -1,
|
||||
Reinitialized = 0,
|
||||
TextureSizeChanged = 1,
|
||||
};
|
||||
|
||||
void SendMessageToUnity(Message message);
|
||||
422
Plugins/uDesktopDuplication/uDesktopDuplication/Cursor.cpp
Normal file
422
Plugins/uDesktopDuplication/uDesktopDuplication/Cursor.cpp
Normal file
@@ -0,0 +1,422 @@
|
||||
#include <d3d11.h>
|
||||
#include <wrl/client.h>
|
||||
#include "Common.h"
|
||||
#include "Debug.h"
|
||||
#include "MonitorManager.h"
|
||||
#include "Monitor.h"
|
||||
#include "Cursor.h"
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
|
||||
Cursor::Cursor(Monitor* monitor)
|
||||
: monitor_(monitor)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Cursor::~Cursor()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void Cursor::UpdateBuffer(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
|
||||
{
|
||||
if (frameInfo.LastMouseUpdateTime.QuadPart == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
isVisible_ = frameInfo.PointerPosition.Visible != 0;
|
||||
if (isVisible_)
|
||||
{
|
||||
GetMonitorManager()->SetCursorMonitorId(monitor_->GetId());
|
||||
}
|
||||
|
||||
x_ = frameInfo.PointerPosition.Position.x;
|
||||
y_ = frameInfo.PointerPosition.Position.y;
|
||||
timestamp_ = frameInfo.LastMouseUpdateTime;
|
||||
|
||||
if (!IsCursorOnParentMonitor())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (frameInfo.PointerShapeBufferSize == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Increase the buffer size if needed
|
||||
if (frameInfo.PointerShapeBufferSize > apiBufferSize_)
|
||||
{
|
||||
apiBufferSize_ = frameInfo.PointerShapeBufferSize;
|
||||
apiBuffer_ = std::make_unique<BYTE[]>(apiBufferSize_);
|
||||
}
|
||||
|
||||
if (!apiBuffer_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get mouse pointer information
|
||||
UINT bufferSize;
|
||||
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo;
|
||||
const auto hr = monitor_->GetDeskDupl()->GetFramePointerShape(
|
||||
apiBufferSize_,
|
||||
apiBuffer_.get(),
|
||||
&bufferSize,
|
||||
&shapeInfo);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Debug::Error("Cursor::UpdateBuffer() => GetFramePointerShape() failed.");
|
||||
apiBuffer_.reset();
|
||||
apiBufferSize_ = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
shapeInfo_ = shapeInfo;
|
||||
}
|
||||
|
||||
|
||||
void Cursor::UpdateTexture()
|
||||
{
|
||||
if (!IsCursorOnParentMonitor())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
// Captured area
|
||||
auto capturedImageWidth = cursorImageWidth;
|
||||
auto capturedImageHeight = cursorImageHeight;
|
||||
|
||||
// Convert the buffer given by API into BGRA32
|
||||
const UINT bgraBufferSize = cursorImageWidth * cursorImageHeight * 4;
|
||||
if (bgraBufferSize > bgra32BufferSize_)
|
||||
{
|
||||
bgra32BufferSize_ = bgraBufferSize;
|
||||
bgra32Buffer_ = std::make_unique<BYTE[]>(bgra32BufferSize_);
|
||||
}
|
||||
|
||||
// Check buffers
|
||||
if (!bgra32Buffer_ || !apiBuffer_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If masked, copy the desktop image and merge it with masked image.
|
||||
if (isMono || isColorMask)
|
||||
{
|
||||
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;
|
||||
|
||||
auto x = x_;
|
||||
auto y = y_;
|
||||
auto colMin = 0;
|
||||
auto colMax = cursorImageWidth;
|
||||
auto rowMin = 0;
|
||||
auto rowMax = cursorImageHeight;
|
||||
|
||||
if (x < 0)
|
||||
{
|
||||
x = 0;
|
||||
capturedImageWidth = cursorImageWidth + x_;
|
||||
colMin = cursorImageWidth - capturedImageWidth;
|
||||
}
|
||||
if (x + capturedImageWidth >= monitorWidth)
|
||||
{
|
||||
capturedImageWidth = monitorWidth - x_;
|
||||
colMax = capturedImageWidth;
|
||||
}
|
||||
if (y < 0)
|
||||
{
|
||||
y = 0;
|
||||
capturedImageHeight = cursorImageHeight + y_;
|
||||
rowMin = cursorImageHeight - capturedImageHeight;
|
||||
}
|
||||
if (y + capturedImageHeight >= monitorHeight)
|
||||
{
|
||||
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 = capturedImageWidth;
|
||||
desc.Height = capturedImageHeight;
|
||||
desc.MipLevels = 1;
|
||||
desc.ArraySize = 1;
|
||||
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.SampleDesc.Quality = 0;
|
||||
desc.Usage = D3D11_USAGE_STAGING;
|
||||
desc.BindFlags = 0;
|
||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
desc.MiscFlags = 0;
|
||||
|
||||
ComPtr<ID3D11Texture2D> texture;
|
||||
if (FAILED(GetDevice()->CreateTexture2D(&desc, nullptr, &texture)))
|
||||
{
|
||||
Debug::Error("Cursor::UpdateTexture() => GetDevice()->CreateTexture2D() failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
ComPtr<ID3D11DeviceContext> context;
|
||||
GetDevice()->GetImmediateContext(&context);
|
||||
context->CopySubresourceRegion(texture.Get(), 0, 0, 0, 0, monitor_->GetUnityTexture(), 0, &box);
|
||||
}
|
||||
|
||||
ComPtr<IDXGISurface> surface;
|
||||
if (FAILED(texture.As(&surface)))
|
||||
{
|
||||
Debug::Error("Cursor::UpdateTexture() => texture->QueryInterface() failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
DXGI_MAPPED_RECT mappedSurface;
|
||||
if (FAILED(surface->Map(&mappedSurface, DXGI_MAP_READ)))
|
||||
{
|
||||
Debug::Error("Cursor::UpdateTexture() => surface->Map() failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Finally, get the texture behind the mouse cursor.
|
||||
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());
|
||||
|
||||
if (isMono)
|
||||
{
|
||||
for (int row = rowMin, y = 0; row < rowMax; ++row, ++y)
|
||||
{
|
||||
for (int col = colMin, x = 0; col < colMax; ++col, ++x)
|
||||
{
|
||||
BYTE mask = 0b10000000 >> (col % 8);
|
||||
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[row * cursorImageWidth + col] = (getDesktop32(x, y) & andMask32) ^ xorMask32;
|
||||
}
|
||||
}
|
||||
}
|
||||
else // DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR
|
||||
{
|
||||
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 * cursorImageWidth;
|
||||
const int j = col + row * cursorImagePitch / sizeof(UINT);
|
||||
|
||||
UINT mask = 0xFF000000 & buffer32[j];
|
||||
if (mask)
|
||||
{
|
||||
output32[i] = (getDesktop32(x, y) ^ buffer32[j]) | 0xFF000000;
|
||||
}
|
||||
else
|
||||
{
|
||||
output32[i] = buffer32[j] | 0xFF000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 < cursorImageWidth * cursorImageHeight; ++i)
|
||||
{
|
||||
output32[i] = buffer32[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Cursor::GetTexture(ID3D11Texture2D* texture)
|
||||
{
|
||||
if (bgra32Buffer_ == nullptr)
|
||||
{
|
||||
Debug::Error("Cursor::GetTexture() => bgra32Buffer is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (texture == nullptr)
|
||||
{
|
||||
Debug::Error("Cursor::GetTexture() => The given texture is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
texture->GetDesc(&desc);
|
||||
if ((int)desc.Width < GetWidth() || (int)desc.Height < GetHeight())
|
||||
{
|
||||
char buf[256];
|
||||
sprintf_s(buf, 256,
|
||||
"Cursor::GetTexture() => The given texture has smaller width / height.\n"
|
||||
"Given => (%d, %d) Buffer => (%d, %d)",
|
||||
desc.Width, desc.Height, GetWidth(), GetHeight());
|
||||
Debug::Error(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
ComPtr<ID3D11DeviceContext> context;
|
||||
GetDevice()->GetImmediateContext(&context);
|
||||
context->UpdateSubresource(texture, 0, nullptr, bgra32Buffer_.get(), GetWidth() * 4, 0);
|
||||
}
|
||||
|
||||
|
||||
bool Cursor::IsVisible() const
|
||||
{
|
||||
return isVisible_;
|
||||
}
|
||||
|
||||
|
||||
int Cursor::GetX() const
|
||||
{
|
||||
return x_;
|
||||
}
|
||||
|
||||
|
||||
int Cursor::GetY() const
|
||||
{
|
||||
return y_;
|
||||
}
|
||||
|
||||
|
||||
int Cursor::GetWidth() const
|
||||
{
|
||||
return shapeInfo_.Width;
|
||||
}
|
||||
|
||||
|
||||
int Cursor::GetHeight() const
|
||||
{
|
||||
return (shapeInfo_.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME) ?
|
||||
shapeInfo_.Height / 2 :
|
||||
shapeInfo_.Height;
|
||||
}
|
||||
|
||||
|
||||
int Cursor::GetPitch() const
|
||||
{
|
||||
return shapeInfo_.Pitch;
|
||||
}
|
||||
|
||||
|
||||
int Cursor::GetType() const
|
||||
{
|
||||
return shapeInfo_.Type;
|
||||
}
|
||||
|
||||
|
||||
bool Cursor::IsCursorOnParentMonitor() const
|
||||
{
|
||||
return GetMonitorManager()->GetCursorMonitorId() == monitor_->GetId();
|
||||
}
|
||||
39
Plugins/uDesktopDuplication/uDesktopDuplication/Cursor.h
Normal file
39
Plugins/uDesktopDuplication/uDesktopDuplication/Cursor.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <dxgi1_2.h>
|
||||
#include <memory>
|
||||
|
||||
class Monitor;
|
||||
|
||||
class Cursor
|
||||
{
|
||||
public:
|
||||
explicit Cursor(Monitor* monitor);
|
||||
~Cursor();
|
||||
void UpdateBuffer(const DXGI_OUTDUPL_FRAME_INFO& frameInfo);
|
||||
void UpdateTexture();
|
||||
void GetTexture(ID3D11Texture2D* texture);
|
||||
|
||||
bool IsVisible() const;
|
||||
int GetX() const;
|
||||
int GetY() const;
|
||||
int GetWidth() const;
|
||||
int GetHeight() const;
|
||||
int GetPitch() const;
|
||||
int GetType() const;
|
||||
|
||||
private:
|
||||
bool IsCursorOnParentMonitor() const;
|
||||
|
||||
Monitor* monitor_;
|
||||
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;
|
||||
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo_;
|
||||
LARGE_INTEGER timestamp_;
|
||||
};
|
||||
33
Plugins/uDesktopDuplication/uDesktopDuplication/Debug.cpp
Normal file
33
Plugins/uDesktopDuplication/uDesktopDuplication/Debug.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
#include "Debug.h"
|
||||
|
||||
|
||||
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::File)
|
||||
{
|
||||
fs_.open("uDesktopDuplication.log");
|
||||
Debug::Log("Start");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Debug::Finalize()
|
||||
{
|
||||
if (mode_ == Mode::File)
|
||||
{
|
||||
Debug::Log("Stop");
|
||||
fs_.close();
|
||||
}
|
||||
Debug::SetLogFunc(nullptr);
|
||||
Debug::SetErrorFunc(nullptr);
|
||||
}
|
||||
127
Plugins/uDesktopDuplication/uDesktopDuplication/Debug.h
Normal file
127
Plugins/uDesktopDuplication/uDesktopDuplication/Debug.h
Normal file
@@ -0,0 +1,127 @@
|
||||
#pragma once
|
||||
#include <time.h>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include "IUnityInterface.h"
|
||||
|
||||
// Logging
|
||||
class Debug
|
||||
{
|
||||
public:
|
||||
enum class Mode
|
||||
{
|
||||
None = 0,
|
||||
File = 1,
|
||||
UnityLog = 2,
|
||||
};
|
||||
|
||||
using DebugLogFuncPtr = void(UNITY_INTERFACE_API *)(const char*);
|
||||
|
||||
static void SetMode(Mode mode) { mode_ = mode; }
|
||||
static void Initialize();
|
||||
static void Finalize();
|
||||
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:
|
||||
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_;
|
||||
};
|
||||
306
Plugins/uDesktopDuplication/uDesktopDuplication/Monitor.cpp
Normal file
306
Plugins/uDesktopDuplication/uDesktopDuplication/Monitor.cpp
Normal file
@@ -0,0 +1,306 @@
|
||||
#include <d3d11.h>
|
||||
#include <ShellScalingAPI.h>
|
||||
#include "Common.h"
|
||||
#include "Debug.h"
|
||||
#include "Cursor.h"
|
||||
#include "MonitorManager.h"
|
||||
#include "Monitor.h"
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
|
||||
Monitor::Monitor(int id)
|
||||
: id_(id)
|
||||
, cursor_(std::make_unique<Cursor>(this))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Monitor::~Monitor()
|
||||
{
|
||||
if (deskDupl_)
|
||||
{
|
||||
deskDupl_->Release();
|
||||
deskDupl_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Monitor::Initialize(IDXGIOutput* output)
|
||||
{
|
||||
if (FAILED(output->GetDesc(&outputDesc_)))
|
||||
{
|
||||
Debug::Error("Monitor::Initialize() => IDXGIOutput::GetDesc() failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Monitor::Render(UINT timeout)
|
||||
{
|
||||
if (!deskDupl_) return;
|
||||
|
||||
ComPtr<IDXGIResource> resource;
|
||||
DXGI_OUTDUPL_FRAME_INFO frameInfo;
|
||||
|
||||
const auto hr = deskDupl_->AcquireNextFrame(timeout, &frameInfo, &resource);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
switch (hr)
|
||||
{
|
||||
case DXGI_ERROR_ACCESS_LOST:
|
||||
{
|
||||
// If any monitor setting has changed (e.g. monitor size has changed),
|
||||
// it is necessary to re-initialize monitors.
|
||||
Debug::Log("Monitor::Render() => DXGI_ERROR_ACCESS_LOST.");
|
||||
state_ = State::AccessLost;
|
||||
break;
|
||||
}
|
||||
case DXGI_ERROR_WAIT_TIMEOUT:
|
||||
{
|
||||
// This often occurs when timeout value is small and it is not problem.
|
||||
// Debug::Log("Monitor::Render() => DXGI_ERROR_WAIT_TIMEOUT.");
|
||||
break;
|
||||
}
|
||||
case DXGI_ERROR_INVALID_CALL:
|
||||
{
|
||||
Debug::Error("Monitor::Render() => DXGI_ERROR_INVALID_CALL.");
|
||||
break;
|
||||
}
|
||||
case E_INVALIDARG:
|
||||
{
|
||||
Debug::Error("Monitor::Render() => E_INVALIDARG.");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
state_ = State::Unknown;
|
||||
Debug::Error("Monitor::Render() => Unknown Error.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (unityTexture_)
|
||||
{
|
||||
ID3D11Texture2D* texture;
|
||||
if (FAILED(resource.CopyTo(&texture)))
|
||||
{
|
||||
Debug::Error("Monitor::Render() => resource.As() failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
if (FAILED(deskDupl_->ReleaseFrame()))
|
||||
{
|
||||
Debug::Error("Monitor::Render() => ReleaseFrame() failed.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetId() const
|
||||
{
|
||||
return id_;
|
||||
}
|
||||
|
||||
|
||||
MonitorState Monitor::GetState() const
|
||||
{
|
||||
return state_;
|
||||
}
|
||||
|
||||
|
||||
void Monitor::SetUnityTexture(ID3D11Texture2D* texture)
|
||||
{
|
||||
unityTexture_ = texture;
|
||||
}
|
||||
|
||||
|
||||
ID3D11Texture2D* Monitor::GetUnityTexture() const
|
||||
{
|
||||
return unityTexture_;
|
||||
}
|
||||
|
||||
|
||||
IDXGIOutputDuplication* Monitor::GetDeskDupl()
|
||||
{
|
||||
return deskDupl_;
|
||||
}
|
||||
|
||||
|
||||
const std::unique_ptr<Cursor>& Monitor::GetCursor()
|
||||
{
|
||||
return cursor_;
|
||||
}
|
||||
|
||||
|
||||
void Monitor::GetCursorTexture(ID3D11Texture2D* texture)
|
||||
{
|
||||
cursor_->GetTexture(texture);
|
||||
}
|
||||
|
||||
|
||||
void Monitor::GetName(char* buf, int len) const
|
||||
{
|
||||
strcpy_s(buf, len, monitorInfo_.szDevice);
|
||||
}
|
||||
|
||||
|
||||
bool Monitor::IsPrimary() const
|
||||
{
|
||||
return monitorInfo_.dwFlags == MONITORINFOF_PRIMARY;
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetLeft() const
|
||||
{
|
||||
return static_cast<int>(outputDesc_.DesktopCoordinates.left);
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetRight() const
|
||||
{
|
||||
return static_cast<int>(outputDesc_.DesktopCoordinates.right);
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetTop() const
|
||||
{
|
||||
return static_cast<int>(outputDesc_.DesktopCoordinates.top);
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetBottom() const
|
||||
{
|
||||
return static_cast<int>(outputDesc_.DesktopCoordinates.bottom);
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetRotation() const
|
||||
{
|
||||
return static_cast<int>(outputDesc_.Rotation);
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetDpiX() const
|
||||
{
|
||||
return static_cast<int>(dpiX_);
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetDpiY() const
|
||||
{
|
||||
return static_cast<int>(dpiY_);
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetWidth() const
|
||||
{
|
||||
return width_;
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetHeight() const
|
||||
{
|
||||
return height_;
|
||||
}
|
||||
64
Plugins/uDesktopDuplication/uDesktopDuplication/Monitor.h
Normal file
64
Plugins/uDesktopDuplication/uDesktopDuplication/Monitor.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <dxgi1_2.h>
|
||||
#include <wrl/client.h>
|
||||
#include <memory>
|
||||
|
||||
class Cursor;
|
||||
|
||||
enum class MonitorState
|
||||
{
|
||||
NotSet = -1,
|
||||
Available = 0,
|
||||
InvalidArg = 1,
|
||||
AccessDenied = 2,
|
||||
Unsupported = 3,
|
||||
CurrentlyNotAvailable = 4,
|
||||
SessionDisconnected = 5,
|
||||
AccessLost = 6,
|
||||
TextureSizeInconsistent = 7,
|
||||
Unknown = 999,
|
||||
};
|
||||
|
||||
class Monitor
|
||||
{
|
||||
public:
|
||||
using State = MonitorState;
|
||||
|
||||
explicit Monitor(int id);
|
||||
~Monitor();
|
||||
void Initialize(IDXGIOutput* output);
|
||||
void Render(UINT timeout = 0);
|
||||
void GetCursorTexture(ID3D11Texture2D* texture);
|
||||
|
||||
public:
|
||||
int GetId() const;
|
||||
State GetState() const;
|
||||
void SetUnityTexture(ID3D11Texture2D* texture);
|
||||
ID3D11Texture2D* GetUnityTexture() const;
|
||||
void GetName(char* buf, int len) const;
|
||||
bool IsPrimary() const;
|
||||
int GetLeft() const;
|
||||
int GetRight() const;
|
||||
int GetTop() const;
|
||||
int GetBottom() const;
|
||||
int GetWidth() const;
|
||||
int GetHeight() const;
|
||||
int GetRotation() const;
|
||||
int GetDpiX() const;
|
||||
int GetDpiY() const;
|
||||
IDXGIOutputDuplication* GetDeskDupl();
|
||||
const std::unique_ptr<Cursor>& GetCursor();
|
||||
|
||||
private:
|
||||
int id_ = -1;
|
||||
UINT dpiX_ = -1, dpiY_ = -1;
|
||||
int width_ = -1, height_ = -1;
|
||||
State state_ = State::NotSet;
|
||||
std::unique_ptr<Cursor> cursor_;
|
||||
IDXGIOutputDuplication* deskDupl_ = nullptr;
|
||||
ID3D11Texture2D* unityTexture_ = nullptr;
|
||||
DXGI_OUTPUT_DESC outputDesc_;
|
||||
MONITORINFOEX monitorInfo_;
|
||||
};
|
||||
@@ -0,0 +1,168 @@
|
||||
#include <d3d11.h>
|
||||
#include <dxgi1_2.h>
|
||||
#include <wrl/client.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
#include "IUnityInterface.h"
|
||||
#include "IUnityGraphicsD3D11.h"
|
||||
|
||||
#include "Common.h"
|
||||
#include "Debug.h"
|
||||
#include "Monitor.h"
|
||||
#include "Cursor.h"
|
||||
#include "MonitorManager.h"
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
|
||||
MonitorManager::MonitorManager()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
|
||||
MonitorManager::~MonitorManager()
|
||||
{
|
||||
Finalize();
|
||||
}
|
||||
|
||||
|
||||
void MonitorManager::Initialize()
|
||||
{
|
||||
Finalize();
|
||||
|
||||
// Get factory
|
||||
ComPtr<IDXGIFactory1> factory;
|
||||
if (FAILED(CreateDXGIFactory1(IID_PPV_ARGS(&factory))))
|
||||
{
|
||||
Debug::Error("MonitorManager::Initialize() => CreateDXGIFactory1() failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check all display adapters
|
||||
int id = 0;
|
||||
ComPtr<IDXGIAdapter1> adapter;
|
||||
for (int i = 0; (factory->EnumAdapters1(i, &adapter) != DXGI_ERROR_NOT_FOUND); ++i)
|
||||
{
|
||||
// Search the main monitor from all outputs
|
||||
ComPtr<IDXGIOutput> output;
|
||||
for (int j = 0; (adapter->EnumOutputs(j, &output) != DXGI_ERROR_NOT_FOUND); ++j)
|
||||
{
|
||||
auto monitor = std::make_shared<Monitor>(id++);
|
||||
monitor->Initialize(output.Get());
|
||||
monitors_.push_back(monitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MonitorManager::Finalize()
|
||||
{
|
||||
monitors_.clear();
|
||||
}
|
||||
|
||||
|
||||
void MonitorManager::RequireReinitilization()
|
||||
{
|
||||
isReinitializationRequired_ = true;
|
||||
}
|
||||
|
||||
|
||||
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())
|
||||
{
|
||||
return monitors_[id];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void MonitorManager::Update()
|
||||
{
|
||||
if (isReinitializationRequired_)
|
||||
{
|
||||
Reinitialize();
|
||||
isReinitializationRequired_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MonitorManager::SetTimeout(int timeout)
|
||||
{
|
||||
timeout_ = timeout;
|
||||
}
|
||||
|
||||
|
||||
int MonitorManager::GetTimeout() const
|
||||
{
|
||||
return timeout_;
|
||||
}
|
||||
|
||||
|
||||
int MonitorManager::GetMonitorCount() const
|
||||
{
|
||||
return static_cast<int>(monitors_.size());
|
||||
}
|
||||
|
||||
|
||||
int MonitorManager::GetTotalWidth() const
|
||||
{
|
||||
std::vector<int> lefts, rights;
|
||||
for (const auto& monitor : monitors_)
|
||||
{
|
||||
lefts.push_back(monitor->GetLeft());
|
||||
rights.push_back(monitor->GetRight());
|
||||
}
|
||||
const auto minLeft = *std::min_element(lefts.begin(), lefts.end());
|
||||
const auto maxRight = *std::max_element(rights.begin(), rights.end());
|
||||
return maxRight - minLeft;
|
||||
}
|
||||
|
||||
|
||||
int MonitorManager::GetTotalHeight() const
|
||||
{
|
||||
std::vector<int> tops, bottoms;
|
||||
for (const auto& monitor : monitors_)
|
||||
{
|
||||
tops.push_back(monitor->GetTop());
|
||||
bottoms.push_back(monitor->GetBottom());
|
||||
}
|
||||
const auto minTop = *std::min_element(tops.begin(), tops.end());
|
||||
const auto maxBottom = *std::max_element(bottoms.begin(), bottoms.end());
|
||||
return maxBottom - minTop;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <dxgi1_2.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
struct IUnityInterfaces;
|
||||
class Monitor;
|
||||
class Cursor;
|
||||
|
||||
class MonitorManager
|
||||
{
|
||||
public:
|
||||
explicit MonitorManager();
|
||||
~MonitorManager();
|
||||
void Reinitialize();
|
||||
bool HasMonitorCountChanged() const;
|
||||
void RequireReinitilization();
|
||||
void SetCursorMonitorId(int id) { cursorMonitorId_ = id; }
|
||||
int GetCursorMonitorId() const { return cursorMonitorId_; }
|
||||
std::shared_ptr<Monitor> GetMonitor(int id) const;
|
||||
|
||||
private:
|
||||
void Initialize();
|
||||
void Finalize();
|
||||
|
||||
// Setters from Unity
|
||||
public:
|
||||
void Update();
|
||||
void SetTimeout(int timeout);
|
||||
int GetTimeout() const;
|
||||
|
||||
// Getters from Unity
|
||||
public:
|
||||
int GetMonitorCount() const;
|
||||
int GetTotalWidth() const;
|
||||
int GetTotalHeight() const;
|
||||
|
||||
private:
|
||||
int timeout_ = 10;
|
||||
std::vector<std::shared_ptr<Monitor>> monitors_;
|
||||
std::shared_ptr<Cursor> cursor_;
|
||||
int cursorMonitorId_ = -1;
|
||||
bool isReinitializationRequired_ = false;
|
||||
};
|
||||
@@ -1,145 +1,93 @@
|
||||
#include <d3d11.h>
|
||||
#include <dxgi1_2.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
|
||||
#include "IUnityInterface.h"
|
||||
#include "IUnityGraphics.h"
|
||||
#include "IUnityGraphicsD3D11.h"
|
||||
|
||||
#include "Common.h"
|
||||
#include "Debug.h"
|
||||
#include "Monitor.h"
|
||||
#include "Cursor.h"
|
||||
#include "MonitorManager.h"
|
||||
|
||||
#pragma comment(lib, "dxgi.lib")
|
||||
#pragma comment(lib, "Shcore.lib")
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
IUnityInterfaces* g_unity = nullptr;
|
||||
IDXGIOutputDuplication* g_deskDupl = nullptr;
|
||||
ID3D11Texture2D* g_texture = nullptr;
|
||||
bool g_isPointerVisible = -1;
|
||||
int g_pointerX = -1;
|
||||
int g_pointerY = -1;
|
||||
int g_width = -1;
|
||||
int g_height = -1;
|
||||
}
|
||||
IUnityInterfaces* g_unity = nullptr;
|
||||
std::unique_ptr<MonitorManager> g_manager;
|
||||
std::queue<Message> g_messages;
|
||||
|
||||
|
||||
extern "C"
|
||||
{
|
||||
void InitializeDuplication()
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API InitializeUDD()
|
||||
{
|
||||
IDXGIFactory1* factory;
|
||||
CreateDXGIFactory1(__uuidof(IDXGIFactory1), reinterpret_cast<void**>(&factory));
|
||||
|
||||
// Check all display adapters.
|
||||
IDXGIAdapter1* adapter;
|
||||
for (int i = 0; (factory->EnumAdapters1(i, &adapter) != DXGI_ERROR_NOT_FOUND); ++i)
|
||||
if (g_unity && !g_manager)
|
||||
{
|
||||
// Search the main monitor from all outputs.
|
||||
IDXGIOutput* output;
|
||||
for (int j = 0; (adapter->EnumOutputs(j, &output) != DXGI_ERROR_NOT_FOUND); ++j)
|
||||
{
|
||||
DXGI_OUTPUT_DESC outputDesc;
|
||||
output->GetDesc(&outputDesc);
|
||||
|
||||
MONITORINFOEX monitorInfo;
|
||||
monitorInfo.cbSize = sizeof(MONITORINFOEX);
|
||||
GetMonitorInfo(outputDesc.Monitor, &monitorInfo);
|
||||
|
||||
if (monitorInfo.dwFlags == MONITORINFOF_PRIMARY)
|
||||
{
|
||||
g_width = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left;
|
||||
g_height = monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top;
|
||||
|
||||
auto device = g_unity->Get<IUnityGraphicsD3D11>()->GetDevice();
|
||||
IDXGIOutput1* output1;
|
||||
output1 = reinterpret_cast<IDXGIOutput1*>(output);
|
||||
output1->DuplicateOutput(device, &g_deskDupl);
|
||||
|
||||
output->Release();
|
||||
adapter->Release();
|
||||
factory->Release();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
output->Release();
|
||||
}
|
||||
|
||||
adapter->Release();
|
||||
Debug::Initialize();
|
||||
g_manager = std::make_unique<MonitorManager>();
|
||||
}
|
||||
|
||||
factory->Release();
|
||||
}
|
||||
|
||||
void FinalizeDuplication()
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API FinalizeUDD()
|
||||
{
|
||||
g_deskDupl->Release();
|
||||
g_deskDupl = nullptr;
|
||||
if (!g_manager) return;
|
||||
|
||||
g_manager.reset();
|
||||
|
||||
std::queue<Message> empty;
|
||||
g_messages.swap(empty);
|
||||
|
||||
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;
|
||||
InitializeDuplication();
|
||||
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;
|
||||
g_texture = nullptr;
|
||||
g_width = -1;
|
||||
g_height = -1;
|
||||
g_pointerX = -1;
|
||||
g_pointerY = -1;
|
||||
FinalizeDuplication();
|
||||
}
|
||||
|
||||
void UNITY_INTERFACE_API OnRenderEvent(int eventId)
|
||||
void UNITY_INTERFACE_API OnRenderEvent(int id)
|
||||
{
|
||||
if (g_deskDupl == nullptr || g_texture == nullptr) return;
|
||||
|
||||
HRESULT hr;
|
||||
IDXGIResource* resource = nullptr;
|
||||
DXGI_OUTDUPL_FRAME_INFO frameInfo;
|
||||
|
||||
const UINT timeout = 100; // ms
|
||||
hr = g_deskDupl->AcquireNextFrame(timeout, &frameInfo, &resource);
|
||||
switch (hr)
|
||||
if (!g_manager) return;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
case S_OK:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case DXGI_ERROR_ACCESS_LOST:
|
||||
{
|
||||
FinalizeDuplication();
|
||||
InitializeDuplication();
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return;
|
||||
}
|
||||
monitor->Render(g_manager->GetTimeout());
|
||||
}
|
||||
|
||||
g_isPointerVisible = frameInfo.PointerPosition.Visible;
|
||||
g_pointerX = frameInfo.PointerPosition.Position.x;
|
||||
g_pointerY = frameInfo.PointerPosition.Position.y;
|
||||
|
||||
ID3D11Texture2D* texture;
|
||||
hr = resource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&texture));
|
||||
if (hr != S_OK)
|
||||
{
|
||||
resource->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
resource->Release();
|
||||
|
||||
ID3D11DeviceContext* context;
|
||||
auto device = g_unity->Get<IUnityGraphicsD3D11>()->GetDevice();
|
||||
device->GetImmediateContext(&context);
|
||||
context->CopyResource(g_texture, texture);
|
||||
|
||||
g_deskDupl->ReleaseFrame();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT UnityRenderingEvent UNITY_INTERFACE_API GetRenderEventFunc()
|
||||
@@ -147,33 +95,292 @@ extern "C"
|
||||
return OnRenderEvent;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetWidth()
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API Reinitialize()
|
||||
{
|
||||
return g_width;
|
||||
if (!g_manager) return;
|
||||
return g_manager->Reinitialize();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetHeight()
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API Update()
|
||||
{
|
||||
return g_height;
|
||||
if (!g_manager) return;
|
||||
g_manager->Update();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API IsPointerVisible()
|
||||
UNITY_INTERFACE_EXPORT Message UNITY_INTERFACE_API PopMessage()
|
||||
{
|
||||
return g_isPointerVisible;
|
||||
if (g_messages.empty()) return Message::None;
|
||||
|
||||
const auto message = g_messages.front();
|
||||
g_messages.pop();
|
||||
return message;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetPointerX()
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetDebugMode(Debug::Mode mode)
|
||||
{
|
||||
return g_pointerX;
|
||||
Debug::SetMode(mode);
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetPointerY()
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetLogFunc(Debug::DebugLogFuncPtr func)
|
||||
{
|
||||
return g_pointerY;
|
||||
Debug::SetLogFunc(func);
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetTexturePtr(void* texture)
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetErrorFunc(Debug::DebugLogFuncPtr func)
|
||||
{
|
||||
g_texture = reinterpret_cast<ID3D11Texture2D*>(texture);
|
||||
Debug::SetErrorFunc(func);
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT size_t UNITY_INTERFACE_API GetMonitorCount()
|
||||
{
|
||||
if (!g_manager) return 0;
|
||||
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;
|
||||
return g_manager->GetCursorMonitorId();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetTotalWidth()
|
||||
{
|
||||
if (!g_manager) return 0;
|
||||
return g_manager->GetTotalWidth();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetTotalHeight()
|
||||
{
|
||||
if (!g_manager) return 0;
|
||||
return g_manager->GetTotalHeight();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetTimeout(int timeout)
|
||||
{
|
||||
if (!g_manager) return;
|
||||
g_manager->SetTimeout(timeout);
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API GetId(int id)
|
||||
{
|
||||
if (!g_manager) return;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
monitor->GetId();
|
||||
}
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT MonitorState UNITY_INTERFACE_API GetState(int id)
|
||||
{
|
||||
if (!g_manager) return MonitorState::NotSet;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetState();
|
||||
}
|
||||
return MonitorState::NotSet;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API GetName(int id, char* buf, int len)
|
||||
{
|
||||
if (!g_manager) return;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
monitor->GetName(buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API IsPrimary(int id)
|
||||
{
|
||||
if (!g_manager) return false;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->IsPrimary();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetLeft(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetLeft();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetRight(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetRight();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetTop(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetTop();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetBottom(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetBottom();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetWidth(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetWidth();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetHeight(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetHeight();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetRotation(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetRotation();
|
||||
}
|
||||
return DXGI_MODE_ROTATION_UNSPECIFIED;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetDpiX(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetDpiX();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetDpiY(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetDpiY();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API IsCursorVisible(int id)
|
||||
{
|
||||
if (!g_manager) return false;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetCursor()->IsVisible();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorX(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetCursor()->GetX();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorY(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetCursor()->GetY();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorShapeWidth(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetCursor()->GetWidth();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorShapeHeight(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetCursor()->GetHeight();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorShapePitch(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetCursor()->GetPitch();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorShapeType(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetCursor()->GetType();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API GetCursorTexture(int id, ID3D11Texture2D* texture)
|
||||
{
|
||||
if (!g_manager) return;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
monitor->GetCursorTexture(texture);
|
||||
}
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetTexturePtr(int id, void* texture)
|
||||
{
|
||||
if (!g_manager) return;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
auto d3d11Texture = reinterpret_cast<ID3D11Texture2D*>(texture);
|
||||
monitor->SetUnityTexture(d3d11Texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,8 +69,12 @@
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);include</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);include</IncludePath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
@@ -85,7 +89,7 @@
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<PostBuildEvent>
|
||||
<Command>copy /Y "$(SolutionDir)$(Platform)\$(Configuration)\$(TargetFileName)" "$(SolutionDir)..\..\Assets\$(ProjectName)\Plugins"</Command>
|
||||
<Command>copy /Y "$(SolutionDir)$(Platform)\$(Configuration)\$(TargetFileName)" "$(SolutionDir)..\..\Assets\$(ProjectName)\Plugins\x86_64"</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
@@ -118,16 +122,26 @@
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>copy /Y "$(SolutionDir)$(Platform)\$(Configuration)\$(TargetFileName)" "$(SolutionDir)..\..\Assets\$(ProjectName)\Plugins"</Command>
|
||||
<Command>copy /Y "$(SolutionDir)$(Platform)\$(Configuration)\$(TargetFileName)" "$(SolutionDir)..\..\Assets\$(ProjectName)\Plugins\x86_64"</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="Common.cpp" />
|
||||
<ClCompile Include="Debug.cpp" />
|
||||
<ClCompile Include="MonitorManager.cpp" />
|
||||
<ClCompile Include="Main.cpp" />
|
||||
<ClCompile Include="Monitor.cpp" />
|
||||
<ClCompile Include="Cursor.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="IUnityGraphics.h" />
|
||||
<ClInclude Include="IUnityGraphicsD3D11.h" />
|
||||
<ClInclude Include="IUnityInterface.h" />
|
||||
<ClInclude Include="Common.h" />
|
||||
<ClInclude Include="Debug.h" />
|
||||
<ClInclude Include="MonitorManager.h" />
|
||||
<ClInclude Include="include\IUnityGraphics.h" />
|
||||
<ClInclude Include="include\IUnityGraphicsD3D11.h" />
|
||||
<ClInclude Include="include\IUnityInterface.h" />
|
||||
<ClInclude Include="Monitor.h" />
|
||||
<ClInclude Include="Cursor.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Include">
|
||||
<UniqueIdentifier>{a820eab3-2c2a-4d56-9670-064198e6a1cf}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="include\IUnityInterface.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\IUnityGraphics.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\IUnityGraphicsD3D11.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Monitor.h" />
|
||||
<ClInclude Include="Cursor.h" />
|
||||
<ClInclude Include="Common.h" />
|
||||
<ClInclude Include="MonitorManager.h" />
|
||||
<ClInclude Include="Debug.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Monitor.cpp" />
|
||||
<ClCompile Include="Cursor.cpp" />
|
||||
<ClCompile Include="Main.cpp" />
|
||||
<ClCompile Include="MonitorManager.cpp" />
|
||||
<ClCompile Include="Common.cpp" />
|
||||
<ClCompile Include="Debug.cpp" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user