Compare commits

...

86 Commits

Author SHA1 Message Date
hecomi
f74221bf75 update board. 2016-11-12 04:27:28 +09:00
hecomi
3a705732d1 improve bending. 2016-11-12 04:25:12 +09:00
hecomi
804bfb96bd add board-style screen. 2016-11-12 03:56:46 +09:00
hecomi
63d8f6936d use ComPtr for resources instead of standard smart pointers. 2016-11-12 02:44:20 +09:00
hecomi
e4e4917bcb fix manager non-initialization bug. 2016-11-12 02:05:09 +09:00
hecomi
0e205ecef7 fix zoom bug. 2016-11-12 02:00:04 +09:00
hecomi
cda8024b17 fix break in error image. 2016-11-12 00:40:11 +09:00
hecomi
05085f7d24 fix bug caused when releasing duplication output. 2016-11-10 23:33:19 +09:00
hecomi
52eafbe919 change unsupported texture. 2016-11-10 23:21:04 +09:00
hecomi
be1e0c25bb expand aabb to avoid culling. 2016-11-10 23:06:24 +09:00
hecomi
372e44d8c1 output logs. 2016-11-10 22:35:46 +09:00
hecomi
d79eb91333 fix freeze bug caused by masked cursor boundary conditions. 2016-11-10 02:50:59 +09:00
hecomi
f654df7f5b add debug logs and use safer code. 2016-11-09 21:58:00 +09:00
hecomi
620762dba7 add logging and use smart pointers for buffers. 2016-11-09 19:40:05 +09:00
hecomi
43ba445623 Merge branch 'master' of github.com:hecomi/uDesktopDuplication 2016-11-09 02:55:32 +09:00
hecomi
c7c12de730 move dll location. 2016-11-09 02:55:26 +09:00
hecomi
1086595d6b fix bug that did not remove unsupported monitor element in list. 2016-11-09 02:54:49 +09:00
hecomi
f69fb6a7bb remove monitor if unsupported. 2016-11-09 00:50:57 +09:00
hecomi
b661b4a93f modify zoom scene. 2016-11-09 00:23:25 +09:00
hecomi
8d9ef71829 update monitor id of loupe. 2016-11-09 00:18:52 +09:00
hecomi
fb7bcbfa5b change texture cache key. 2016-11-09 00:13:55 +09:00
hecomi
9ed8bb8d5b fix mouse cursor mono mask bug. 2016-11-09 00:02:43 +09:00
hecomi
fab67bbc61 add null check. 2016-11-08 23:28:37 +09:00
hecomi
5fa9f03a0e separate multiple monitors examples. 2016-11-08 22:10:47 +09:00
hecomi
941bb2da91 add round layout example. 2016-11-08 22:09:55 +09:00
hecomi
d423e0d1db remove collider from prefab. 2016-11-06 23:29:19 +09:00
hecomi
33a55d95bd add clipping function and the example. 2016-11-06 23:28:33 +09:00
hecomi
8e2d8d3218 fix cursor visibility bug. 2016-11-06 21:16:35 +09:00
hecomi
6ac1a5ee60 get dpi of monitor and calculate actual size of it. 2016-11-06 12:17:29 +09:00
hecomi
15a98cd774 fix singleton bug. 2016-11-06 11:55:01 +09:00
hecomi
06bb971a42 support display setting change in realtime. 2016-11-06 04:04:23 +09:00
hecomi
217e63d40c fix indent. 2016-11-06 03:33:21 +09:00
hecomi
9e42fb0abf there is only one cursor in screens. 2016-11-06 03:30:21 +09:00
hecomi
9de5064fbe do reinitialization from unity. 2016-11-06 00:39:07 +09:00
hecomi
2b108bd4b1 fix available getter bug. 2016-11-05 23:25:22 +09:00
hecomi
f9f8b14f46 show not-available texture when display is not available. 2016-11-05 23:15:57 +09:00
hecomi
7774290e71 fix text orientation. 2016-11-05 22:56:22 +09:00
hecomi
a3a7754fb0 remove unused cursor material and move icon to editor dir. 2016-11-05 22:44:17 +09:00
hecomi
0accebb657 add not-available texture. 2016-11-05 22:42:45 +09:00
hecomi
0a38eda14f update files. 2016-11-05 22:36:08 +09:00
hecomi
720f74e6f6 check unsupported display. 2016-11-05 22:34:11 +09:00
hecomi
80fa39f4eb remove unused debug.log. 2016-11-05 02:14:19 +09:00
hecomi
22b21ae3f4 check texture size before recreating it. 2016-11-05 01:44:26 +09:00
hecomi
43153266a0 rename members named pointer to cursor. 2016-11-05 01:40:20 +09:00
hecomi
dc4fe44342 support rotation 90/180/270 degs. 2016-11-05 01:33:29 +09:00
hecomi
6bb9846510 remove unnecessary debug.log. 2016-11-05 00:11:54 +09:00
hecomi
90b811cd31 add function to fetch left/right/top/bottom/rotation. 2016-11-05 00:09:04 +09:00
hecomi
1d7b7e8650 add bending test. 2016-11-04 23:29:40 +09:00
hecomi
6f784860e5 make shaders simpler. 2016-11-04 22:58:19 +09:00
hecomi
690dba23cc make shader one pass. 2016-11-04 22:45:57 +09:00
hecomi
908988dcaa tweak multiple monitors example. 2016-11-04 21:37:08 +09:00
hecomi
0f72396540 rename initialize/finalize to suppress warning. 2016-11-04 21:35:34 +09:00
hecomi
8f7659d7c2 build with release mode. 2016-11-04 19:28:47 +09:00
hecomi
2e763d994d prevent screen lost after admin screen. 2016-11-04 19:11:17 +09:00
hecomi
2c61fcd470 refactoring. 2016-11-04 15:45:35 +09:00
hecomi
f2cad8bf73 check cursor texture size. 2016-11-02 14:59:15 +09:00
hecomi
ad4b49c177 add scenes in build. 2016-11-02 14:13:04 +09:00
hecomi
92b926bcde remove cursor GameObject from Monitor.prefab. 2016-11-02 14:12:56 +09:00
hecomi
3e90889266 add culling option and fix cursor problem on transparent material. 2016-11-02 11:25:43 +09:00
hecomi
6531fe1dd6 create class for duplication. 2016-11-02 02:57:27 +09:00
hecomi
0b62376ac5 fix shader position. 2016-11-02 02:57:08 +09:00
hecomi
9f611d5d34 use release build. 2016-11-01 09:26:45 +09:00
hecomi
dd4e27030d remove unused file. 2016-11-01 02:37:05 +09:00
hecomi
4350ac3953 cache pointer texture. 2016-11-01 02:20:46 +09:00
hecomi
6ba381f5d4 fetch cursor texture. 2016-11-01 02:15:25 +09:00
hecomi
9287ef2f2d simplify duplication code. 2016-10-30 21:20:31 +09:00
hecomi
dc6b8bc204 fix vertical screen bug. 2016-10-30 19:56:22 +09:00
hecomi
14cd67e0c6 check if monitor is vertical or not. 2016-10-30 19:35:23 +09:00
hecomi
73018f671a add error code getter. 2016-10-30 19:35:00 +09:00
hecomi
7a27d86eff consider aspect ratio in Multiple Monitor scene. 2016-10-30 19:03:25 +09:00
hecomi
99e1774388 add Monitor.aspect. 2016-10-30 19:03:06 +09:00
hecomi
e0408d4749 fix line endings. 2016-10-30 18:42:16 +09:00
hecomi
23ceef6502 modify invert method. 2016-10-30 18:41:43 +09:00
hecomi
4317fc06e3 update .gitignore. 2016-10-30 18:41:30 +09:00
hecomi
f8cb71b472 fix typo. 2016-10-28 20:15:10 +09:00
hecomi
f1a7c302bd update .gitignore. 2016-10-28 20:15:03 +09:00
hecomi
ec82c49d7f fix toggle scene. 2016-10-28 19:41:44 +09:00
hecomi
f8146c15bd update readme. 2016-10-28 19:33:45 +09:00
hecomi
d5a9827b4d add toggle example. 2016-10-28 19:33:37 +09:00
hecomi
4b499a96de update readme. 2016-10-28 19:23:10 +09:00
hecomi
583326c333 add license. 2016-10-28 19:09:42 +09:00
hecomi
c3ec17dd7b add shader example. 2016-10-28 19:08:08 +09:00
hecomi
6f375e2e5b change class names. / add multiple screen example. / performance tuning. 2016-10-28 18:48:53 +09:00
hecomi
d9f83cf89e implement multiple screen. 2016-10-28 17:28:22 +09:00
hecomi
ba5ebe8094 update readme. 2016-10-27 19:19:45 +09:00
hecomi
1e2ac40507 support linear color. 2016-10-27 19:18:44 +09:00
94 changed files with 3105 additions and 363 deletions

4
.gitignore vendored
View File

@@ -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

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 022a80d652ef0304e84e9d6ac689e21f
folderAsset: yes
timeCreated: 1478353382
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 6e15bc51aada81042a909c808e25c176
folderAsset: yes
timeCreated: 1477555207
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 678e1de5e6de7914baed516a78940503
folderAsset: yes
timeCreated: 1477643728
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,6 +1,6 @@
fileFormatVersion: 2
guid: 033e0db89e0ca1d46a399c953629f67d
timeCreated: 1477555367
guid: de6f36e446e5e8a48bf61fe198985bb4
timeCreated: 1477643732
licenseType: Pro
NativeFormatImporter:
userData:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 42d6b6dbae38a384e882c2c907915139
timeCreated: 1478889784
licenseType: Pro
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 0a3456d4b2f59e84f969e31deca4016d
folderAsset: yes
timeCreated: 1477643596
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4bae91f55031b98488c9e668031f7387
timeCreated: 1478610618
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 445860b05342be845967392bfd70224e
timeCreated: 1477643648
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f66b67cb0e751dc4ca0251b38bc8638d
timeCreated: 1477648784
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 78e54899a518eb749a589b921a1b263f
timeCreated: 1477650252
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f3d41026babadc241b9d0852b0831584
timeCreated: 1478436298
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 71921882396f0f345aa8a66e2fcb0b6c
folderAsset: yes
timeCreated: 1477643608
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,35 @@
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()
{
uddTexture_.monitorId = uDesktopDuplication.Manager.cursorMonitorId;
// To get other monitor textures, set dirty flag.
foreach (var monitor in uDesktopDuplication.Manager.monitors) {
monitor.CreateTexture();
monitor.shouldBeUpdated = true;
}
var x = (float)uddTexture_.monitor.cursorX / uddTexture_.monitor.width;
var y = (float)uddTexture_.monitor.cursorY / uddTexture_.monitor.height;
var w = 1f / zoom;
var h = w / aspect * uddTexture_.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);
}
}

View 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:

View File

@@ -0,0 +1,131 @@
using UnityEngine;
using System.Collections.Generic;
public class MultipleMonitorCreator : MonoBehaviour
{
[SerializeField] GameObject monitorPrefab;
[SerializeField] bool removeIfUnsupported = true;
[SerializeField] float removeWaitDuration = 5f;
bool hasMonitorUnsupportStateChecked = false;
float removeWaitTimer_ = 0f;
public enum MeshForwardDirection { Y, Z }
[Tooltip("Please specify the upper vector direction of the mesh (e.g. Plane's upper direction is Y.)")]
public MeshForwardDirection meshForwardDirection = MeshForwardDirection.Z;
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();
}
}
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>().mesh; // clone
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
if (meshForwardDirection == MeshForwardDirection.Y) {
go.transform.localScale = new Vector3(monitor.widthMeter, go.transform.localScale.y, monitor.heightMeter);
} else {
go.transform.localScale = new Vector3(monitor.widthMeter, monitor.heightMeter, 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);
}
monitors_.Clear();
}
void Recreate()
{
Clear();
Create();
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: fb22986017201f74aa864dcbf0cab751
timeCreated: 1477643778
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,52 @@
using UnityEngine;
[RequireComponent(typeof(MultipleMonitorCreator))]
public class MultipleMonitorLayouter : MonoBehaviour
{
protected MultipleMonitorCreator creator_;
[SerializeField] bool updateEveryFrame = true;
[SerializeField] protected float margin = 0.1f;
void Start()
{
creator_ = GetComponent<MultipleMonitorCreator>();
Layout();
}
protected virtual void Update()
{
if (updateEveryFrame) {
Layout();
}
}
void LateUpdate()
{
margin = Mathf.Max(margin, 0f);
}
protected virtual void Layout()
{
var monitors = creator_.monitors;
var n = monitors.Count;
var totalWidth = 0f;
foreach (var info in monitors) {
var width = info.uddTexture.monitor.widthMeter * (info.mesh.bounds.extents.x * 2f);
totalWidth += width;
}
totalWidth += margin * (n - 1);
var x = -totalWidth / 2;
foreach (var info in creator_.monitors) {
var monitor = info.uddTexture.monitor;
x += (monitor.widthMeter * info.mesh.bounds.extents.x);
info.gameObject.transform.localPosition = new Vector3(x, 0f, 0f);
info.gameObject.transform.localRotation = info.originalRotation;
x += (monitor.widthMeter * info.mesh.bounds.extents.x) + margin;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 0db1dcac2dedc724194c423ee75b3bf4
timeCreated: 1478608632
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,64 @@
using UnityEngine;
public class MultipleMonitorRoundLayouter : MultipleMonitorLayouter
{
[SerializeField] float radius = 10f;
void OnDisable()
{
foreach (var info in creator_.monitors) {
info.uddTexture.bend = uDesktopDuplication.Texture.Bend.Off;
}
}
protected override void Layout()
{
var monitors = creator_.monitors;
var n = monitors.Count;
var totalWidth = 0f;
foreach (var info in monitors) {
var width = info.uddTexture.monitor.widthMeter * (info.mesh.bounds.extents.x * 2f);
totalWidth += width;
}
totalWidth += margin * (n - 1);
radius = Mathf.Max(radius, (totalWidth + margin) / (2 * Mathf.PI));
var totalAngle = totalWidth / radius;
float angle = -totalAngle / 2;
foreach (var info in monitors) {
var uddTex = info.uddTexture;
var width = uddTex.monitor.widthMeter * (info.mesh.bounds.extents.x * 2f);
angle += (width / radius) * 0.5f;
uddTex.transform.localPosition = radius * new Vector3(Mathf.Sin(angle), 0f, Mathf.Cos(angle) - 1f);
uddTex.transform.localRotation = Quaternion.AngleAxis(angle * Mathf.Rad2Deg, Vector3.up) * info.originalRotation;
angle += (width * 0.5f + margin) / radius;
if (creator_.meshForwardDirection == MultipleMonitorCreator.MeshForwardDirection.Y) {
uddTex.bend = uDesktopDuplication.Texture.Bend.Y;
} else {
uddTex.bend = uDesktopDuplication.Texture.Bend.Z;
}
uddTex.radius = radius;
uddTex.width = uddTex.transform.localScale.x;
}
}
protected override void Update()
{
base.Update();
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);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a31b05fdfa301f14e88365e64622b43a
timeCreated: 1478524060
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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++;
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 602d3a9bed585d14eaf21e0528d24d54
timeCreated: 1477650268
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 06ef13d14bbcf9242a8eba19a94803f3
timeCreated: 1477648806
licenseType: Pro
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3abaadab838fb3c4ba342eda7431d4f7
timeCreated: 1477648806
licenseType: Pro
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 647550562f402264fb69871ec732b74c
timeCreated: 1477648806
licenseType: Pro
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 75494e992a22ea44d9b614d7fd3dc235
folderAsset: yes
timeCreated: 1478887431
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View 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:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 99257566ccd0582439a44abad90e65d4
folderAsset: yes
timeCreated: 1478626361
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,8 +1,8 @@
fileFormatVersion: 2
guid: 6e15bc51aada81042a909c808e25c176
guid: b281589e6b8e27b4fbf0509d620b7871
folderAsset: yes
timeCreated: 1477555207
licenseType: Pro
timeCreated: 1478354860
licenseType: Free
DefaultImporter:
userData:
assetBundleName:

View File

@@ -1,8 +1,8 @@
fileFormatVersion: 2
guid: 48b92326b146fa848beec6f35365b1f4
guid: e36cd526b654d024e875d5110a1b0442
folderAsset: yes
timeCreated: 1477555207
licenseType: Pro
timeCreated: 1478354877
licenseType: Free
DefaultImporter:
userData:
assetBundleName:

View File

@@ -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

View File

@@ -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:

View File

@@ -0,0 +1,72 @@
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));
}
}
}

View File

@@ -1,6 +1,6 @@
fileFormatVersion: 2
guid: d2d9e4ca459ecb44da8815bafe9655c5
timeCreated: 1477559806
timeCreated: 1477635630
licenseType: Pro
MonoImporter:
serializedVersion: 2

View File

@@ -0,0 +1,140 @@
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace uDesktopDuplication
{
public enum Message
{
None = -1,
Reinitialized = 0,
}
public enum CursorShapeType
{
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,
}
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 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();
}
}
}

View 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:

View File

@@ -0,0 +1,187 @@
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace uDesktopDuplication
{
public class Manager : MonoBehaviour
{
private static Manager instance_;
public static Manager instance
{
get { return CreateInstance(); }
}
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 Manager CreateInstance()
{
if (instance_) {
return instance_;
}
var manager = FindObjectOfType<Manager>();
if (manager) {
manager.Awake();
return manager;
}
var go = new GameObject("uDesktopDuplicationManager");
return go.AddComponent<Manager>();
}
void Awake()
{
Lib.SetDebugMode(debugMode);
Lib.InitializeUDD();
if (instance_ != null) return;
instance_ = this;
CreateMonitors();
Lib.SetTimeout(desktopDuplicationApiTimeout);
}
void OnApplicationQuit()
{
Lib.FinalizeUDD();
}
void OnEnable()
{
renderCoroutine_ = StartCoroutine(OnRender());
}
void OnDisable()
{
if (renderCoroutine_ != null) {
StopCoroutine(renderCoroutine_);
renderCoroutine_ = null;
}
}
void Update()
{
Lib.Update();
ReinitializeIfNeeded();
UpdateMessage();
}
[ContextMenu("Reinitialize")]
void Reinitialize()
{
Debug.Log("[uDesktopDuplication] Reinitialize");
Lib.Reinitialize();
if (onReinitialized != null) onReinitialized();
}
void ReinitializeIfNeeded()
{
for (int i = 0; i < monitors.Count; ++i) {
var monitor = monitors[i];
if (monitor.state == MonitorState.NotSet ||
monitor.state == MonitorState.AccessLost ||
monitor.state == MonitorState.AccessDenied ||
monitor.state == MonitorState.SessionDisconnected) {
if (!shouldReinitialize_) {
shouldReinitialize_ = true;
reinitializationTimer_ = 0f;
break;
}
}
}
if (shouldReinitialize_) {
if (reinitializationTimer_ > retryReinitializationDuration) {
Reinitialize();
shouldReinitialize_ = false;
}
reinitializationTimer_ += Time.deltaTime;
}
}
void UpdateMessage()
{
var message = Lib.PopMessage();
while (message != Message.None) {
switch (message) {
case Message.Reinitialized:
ReinitializeMonitors();
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()
{
monitors.Clear();
for (int i = 0; i < monitorCount; ++i) {
monitors.Add(new Monitor(i));
}
}
void ReinitializeMonitors()
{
for (int i = 0; i < monitorCount; ++i) {
if (i == monitors.Count) {
monitors.Add(new Monitor(i));
}
monitors[i].Reinitialize();
}
}
}
}

View 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:

View File

@@ -0,0 +1,231 @@
using UnityEngine;
namespace uDesktopDuplication
{
public class Monitor
{
public Monitor(int id)
{
this.id = id;
switch (state)
{
case MonitorState.InvalidArg:
Debug.LogErrorFormat("[{0}:{1}] Invalid.", id, name);
break;
case MonitorState.AccessDenied:
Debug.LogErrorFormat("[{0}:{1}] Access Denied.", id, name);
break;
case MonitorState.Unsupported:
Debug.LogErrorFormat("[{0}:{1}] Unsupported.", id, name);
break;
case MonitorState.SessionDisconnected:
Debug.LogErrorFormat("[{0}:{1}] Disconnected.", id, name);
break;
case MonitorState.NotSet:
Debug.LogErrorFormat("[{0}:{1}] Something wrong.", id, name);
break;
default:
break;
}
}
public int id
{
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 { return Lib.GetDpiX(id); }
}
public int dpiY
{
get { return Lib.GetDpiY(id); }
}
public float widthMeter
{
get { return width / dpiX * 0.0254f; }
}
public float heightMeter
{
get { return height / dpiY * 0.0254f; }
}
public MonitorRotation rotation
{
get { return Lib.GetRotation(id); }
}
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 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 Texture2D texture_;
public Texture2D texture
{
get
{
if (!available) {
return Resources.Load<Texture2D>("uDesktopDuplication/Textures/NotAvailable");
}
if (texture_ == null) {
CreateTexture();
}
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 CreateTexture()
{
if (!available) return;
var w = isHorizontal ? width : height;
var h = isHorizontal ? height : width;
bool shouldCreate = true;
if (texture_) {
if (texture_.width != w || texture_.height != h) {
if (texture_) Object.DestroyImmediate(texture_);
shouldCreate = true;
} else {
shouldCreate = false;
}
}
if (w <= 0 || h <= 0) {
shouldCreate = false;
}
if (shouldCreate) {
texture_ = new Texture2D(w, h, TextureFormat.BGRA32, false);
}
}
public void Reinitialize()
{
CreateTexture();
}
}
}

View 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:

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,176 @@
using UnityEngine;
namespace uDesktopDuplication
{
[AddComponentMenu("uDesktopDuplication/Texture")]
public class Texture : MonoBehaviour
{
private Monitor monitor_;
public Monitor monitor
{
get { return monitor_; }
set
{
monitor_ = value;
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.monitors[Mathf.Clamp(value, 0, Manager.monitorCount - 1)]; }
}
[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 Bend
{
Off = 0,
Y = 1,
Z = 2,
}
public Bend bend
{
get
{
return (Bend)material.GetInt("_Bend");
}
set
{
switch (value) {
case Bend.Off:
material.SetInt("_Bend", 0);
material.DisableKeyword("_BEND_OFF");
material.DisableKeyword("_BEND_Y");
material.DisableKeyword("_BEND_Z");
break;
case Bend.Y:
material.SetInt("_Bend", 1);
material.DisableKeyword("_BEND_OFF");
material.EnableKeyword("_BEND_Y");
material.DisableKeyword("_BEND_Z");
break;
case Bend.Z:
material.SetInt("_Bend", 2);
material.DisableKeyword("_BEND_OFF");
material.DisableKeyword("_BEND_Y");
material.EnableKeyword("_BEND_Z");
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 Material material
{
get;
private set;
}
void Awake()
{
if (!GetComponent<Cursor>())
{
gameObject.AddComponent<Cursor>();
}
}
void OnEnable()
{
if (monitor == null) {
monitor = Manager.primary;
}
}
void Update()
{
monitor.shouldBeUpdated = true;
UpdateMaterial();
}
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");
}
}
}
}

View File

@@ -1,6 +1,6 @@
fileFormatVersion: 2
guid: bb54a34570e4747429b1c5b66c69d356
timeCreated: 1477559813
timeCreated: 1477635636
licenseType: Pro
MonoImporter:
serializedVersion: 2

View File

@@ -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);
}
}
}
}

View File

@@ -1,13 +1,109 @@
#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;
};
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;
}
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;
}
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)
{
#if !defined(_BEND_OFF)
half angle = width * v.x / radius;
#ifdef _BEND_Y
radius -= v.y;
v.y += radius * (1 - cos(angle));
#elif _BEND_Z
radius -= v.z;
v.z += radius * (1 - cos(angle));
#endif
v.x = radius * sin(angle) / width;
#endif
}

View 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

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 54044418ab4785c4a84dd4be507db6c2
timeCreated: 1478266678
licenseType: Pro
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;

View File

@@ -5,6 +5,10 @@ Properties
{
_Color ("Color", Color) = (1, 1, 1, 1)
_MainTex ("Texture", 2D) = "white" {}
_CursorTex ("Cursor Texture", 2D) = "white" {}
[KeywordEnum(Off, Y, Z)] _Bend("Bending", Int) = 0
[PowerSlider(10.0)] _Radius("Radius", Range(1, 100)) = 30
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
}
SubShader
@@ -12,30 +16,19 @@ 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;
v2f vert(appdata v)
{
v2f o;
uddBendVertex(v.vertex, _Radius, _Width);
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
@@ -43,8 +36,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 +48,9 @@ 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_OFF _BEND_Y _BEND_Z
ENDCG
}
@@ -63,4 +58,4 @@ Pass
Fallback "Unlit/Texture"
}
}

View File

@@ -6,6 +6,10 @@ 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(Off, Y, Z)] _Bend("Bending", Int) = 0
[PowerSlider(10.0)] _Radius("Radius", Range(1, 100)) = 30
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
}
SubShader
@@ -13,34 +17,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;
fixed _Mask;
half _Radius;
half _Width;
v2f vert(appdata v)
{
v2f o;
uddBendVertex(v.vertex, _Radius, _Width);
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
@@ -48,10 +40,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 +54,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
}

View File

@@ -5,6 +5,10 @@ Properties
{
_Color ("Color", Color) = (1, 1, 1, 1)
_MainTex ("Texture", 2D) = "white" {}
_CursorTex ("Cursor Texture", 2D) = "white" {}
[KeywordEnum(Off, Y, Z)] _Bend("Bending", Int) = 0
[PowerSlider(10.0)] _Radius("Radius", Range(1, 100)) = 30
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
}
SubShader
@@ -12,33 +16,21 @@ 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;
v2f vert(appdata v)
{
v2f o;
uddBendVertex(v.vertex, _Radius, _Width);
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
@@ -46,8 +38,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 +50,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
}

View File

@@ -0,0 +1,39 @@
#pragma once
#include <queue>
#include <unordered_map>
#include <iostream>
#include <d3d11.h>
#include "IUnityInterface.h"
#include "IUnityGraphicsD3D11.h"
#include "Common.h"
extern IUnityInterfaces* g_unity;
extern std::unique_ptr<MonitorManager> g_manager;
extern std::queue<Message> g_messages;
IUnityInterfaces* GetUnity()
{
return g_unity;
}
ID3D11Device* GetDevice()
{
return GetUnity()->Get<IUnityGraphicsD3D11>()->GetDevice();
}
const std::unique_ptr<MonitorManager>& GetMonitorManager()
{
return g_manager;
}
void SendMessageToUnity(Message message)
{
g_messages.push(message);
}

View File

@@ -0,0 +1,28 @@
#pragma once
#include <memory>
#include <string>
#include <functional>
// Unity interface and ID3D11Device getters
struct IUnityInterfaces;
IUnityInterfaces* GetUnity();
struct ID3D11Device;
ID3D11Device* GetDevice();
// Manager getter
class MonitorManager;
const std::unique_ptr<MonitorManager>& GetMonitorManager();
// Message is pooled and fetch from Unity.
enum class Message
{
None = -1,
Reinitialized = 0,
};
void SendMessageToUnity(Message message);

View File

@@ -0,0 +1,344 @@
#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_,
reinterpret_cast<void*>(apiBuffer_.get()),
&bufferSize,
&shapeInfo);
if (FAILED(hr))
{
Debug::Error("Cursor::UpdateBuffer() => GetFramePointerShape() failed.");
apiBuffer_.reset();
apiBufferSize_ = 0;
}
shapeInfo_ = shapeInfo;
}
void Cursor::UpdateTexture()
{
if (!IsCursorOnParentMonitor())
{
return;
}
// cursor type
const bool isMono = GetType() == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME;
const bool isColorMask = GetType() == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR;
// Size
const auto w0 = GetWidth();
const auto h0 = GetHeight();
const auto p = GetPitch();
auto w = w0;
auto h = h0;
// Convert the buffer given by API into BGRA32
const UINT bgraBufferSize = w0 * h0 * 4;
if (bgraBufferSize > bgra32BufferSize_)
{
bgra32BufferSize_ = bgraBufferSize;
bgra32Buffer_ = std::make_unique<BYTE[]>(bgra32BufferSize_);
}
if (!bgra32Buffer_) return;
// If masked, copy the desktop image and merge it with masked image.
if (isMono || isColorMask)
{
HRESULT hr;
const auto mw = monitor_->GetWidth();
const auto mh = monitor_->GetHeight();
auto x = x_;
auto y = y_;
auto colMin = 0;
auto colMax = w0;
auto rowMin = 0;
auto rowMax = h0;
if (x < 0)
{
x = 0;
w = w0 + x_;
colMin = w0 - w;
}
if (x + w >= mw)
{
w = mw - x_;
colMax = w;
}
if (y < 0)
{
y = 0;
h = h0 + y_;
rowMin = h0 - h;
}
if (y + h >= mh)
{
h = mh - y_;
rowMax = h;
}
D3D11_TEXTURE2D_DESC desc;
desc.Width = w;
desc.Height = h;
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;
hr = GetDevice()->CreateTexture2D(&desc, nullptr, &texture);
if (FAILED(hr))
{
Debug::Error("Cursor::UpdateTexture() => GetDevice()->CreateTexture2D() failed.");
return;
}
D3D11_BOX box;
box.front = 0;
box.back = 1;
box.left = x;
box.top = y;
box.right = x + w;
box.bottom = y + h;
if (monitor_->GetUnityTexture() == nullptr)
{
Debug::Error("Cursor::UpdateTexture() => Monitor::GetUnityTexture() is null.");
return;
}
{
ComPtr<ID3D11DeviceContext> context;
GetDevice()->GetImmediateContext(&context);
context->CopySubresourceRegion(texture.Get(), 0, 0, 0, 0, monitor_->GetUnityTexture(), 0, &box);
}
ComPtr<IDXGISurface> surface;
hr = texture.As<IDXGISurface>(&surface);
if (FAILED(hr))
{
Debug::Error("Cursor::UpdateTexture() => texture->QueryInterface() failed.");
return;
}
DXGI_MAPPED_RECT mappedSurface;
hr = surface->Map(&mappedSurface, DXGI_MAP_READ);
if (FAILED(hr))
{
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);
// 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 int i = row * w0 + col;
const BYTE andMask = apiBuffer_[col / 8 + row * p] & mask;
const BYTE xorMask = apiBuffer_[col / 8 + (row + h) * p] & mask;
const UINT andMask32 = andMask ? 0xFFFFFFFF : 0x00000000;
const UINT xorMask32 = xorMask ? 0xFFFFFFFF : 0x00000000;
output32[i] = (desktop32[y * desktopPitch + x] & 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 * w0;
const int j = col + row * p / sizeof(UINT);
UINT mask = 0xFF000000 & buffer32[j];
if (mask)
{
output32[i] = (desktop32[y * desktopPitch + x] ^ buffer32[j]) | 0xFF000000;
}
else
{
output32[i] = buffer32[j] | 0xFF000000;
}
}
}
}
hr = surface->Unmap();
if (FAILED(hr))
{
return;
}
}
else // DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR
{
auto output32 = reinterpret_cast<UINT*>(bgra32Buffer_.get());
const auto buffer32 = reinterpret_cast<UINT*>(apiBuffer_.get());
for (int i = 0; i < w * h; ++i)
{
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();
}

View 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_;
};

View File

@@ -0,0 +1,84 @@
#pragma once
#include <cstdio>
#include "Debug.h"
decltype(Debug::mode_) Debug::mode_ = Debug::Mode::kFile;
decltype(Debug::logFunc_) Debug::logFunc_ = nullptr;
decltype(Debug::errFunc_) Debug::errFunc_ = nullptr;
decltype(Debug::fs_) Debug::fs_;
void Debug::Initialize()
{
if (mode_ == Mode::kFile)
{
fs_.open("uDesktopDuplication.log");
}
}
void Debug::Finalize()
{
fs_.close();
}
void Debug::Log(const char* msg)
{
switch (mode_)
{
case Mode::kNone:
{
break;
}
case Mode::kFile:
{
if (fs_.good())
{
fs_ << "[uDD::Log] " << msg << std::endl;
}
break;
}
case Mode::kUnityLog:
{
if (logFunc_ == nullptr)
{
char buf[256];
sprintf_s(buf, 256, "[uDD::Log] %s", msg);
logFunc_(buf);
break;
}
}
}
}
void Debug::Error(const char* msg)
{
switch (mode_)
{
case Mode::kNone:
{
break;
}
case Mode::kFile:
{
if (fs_.good())
{
fs_ << "[uDD::Err] " << msg << std::endl;
}
break;
}
case Mode::kUnityLog:
{
if (logFunc_ == nullptr)
{
char buf[256];
sprintf_s(buf, 256, "[uDD::Err] %s", msg);
errFunc_(buf);
}
}
}
}

View File

@@ -0,0 +1,33 @@
#pragma once
#include <fstream>
#include "IUnityInterface.h"
// Logging
class Debug
{
public:
enum class Mode
{
kNone = 0,
kFile = 1,
kUnityLog = 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; }
public:
static void Log(const char* msg);
static void Error(const char* msg);
private:
static Mode mode_;
static std::ofstream fs_;
static DebugLogFuncPtr logFunc_;
static DebugLogFuncPtr errFunc_;
};

View File

@@ -0,0 +1,234 @@
#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()
{
}
void Monitor::Initialize(IDXGIOutput* output)
{
output->GetDesc(&outputDesc_);
monitorInfo_.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(outputDesc_.Monitor, &monitorInfo_);
GetDpiForMonitor(outputDesc_.Monitor, MDT_RAW_DPI, &dpiX_, &dpiY_);
auto output1 = reinterpret_cast<IDXGIOutput1*>(output);
const auto hr = output1->DuplicateOutput(GetDevice(), &deskDupl_);
// TODO: error check
switch (hr)
{
case S_OK:
state_ = State::Available;
Debug::Log("Monitor::Initialize() => OK.");
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;
}
}
void Monitor::Render(UINT timeout)
{
if (!deskDupl_) return;
ComPtr<IDXGIResource> resource;
DXGI_OUTDUPL_FRAME_INFO frameInfo;
HRESULT hr;
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:
break;
}
return;
}
if (unityTexture_)
{
ComPtr<ID3D11Texture2D> texture;
resource.As<ID3D11Texture2D>(&texture);
ComPtr<ID3D11DeviceContext> context;
GetDevice()->GetImmediateContext(&context);
context->CopyResource(unityTexture_, texture.Get());
}
cursor_->UpdateBuffer(frameInfo);
cursor_->UpdateTexture();
hr = deskDupl_->ReleaseFrame();
if (FAILED(hr))
{
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_;
}
const ComPtr<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 outputDesc_.DesktopCoordinates.left;
}
int Monitor::GetRight() const
{
return outputDesc_.DesktopCoordinates.right;
}
int Monitor::GetTop() const
{
return outputDesc_.DesktopCoordinates.top;
}
int Monitor::GetBottom() const
{
return 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
{
const auto rect = monitorInfo_.rcMonitor;
return rect.right - rect.left;
}
int Monitor::GetHeight() const
{
const auto rect = monitorInfo_.rcMonitor;
return rect.bottom - rect.top;
}

View File

@@ -0,0 +1,61 @@
#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,
};
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;
const Microsoft::WRL::ComPtr<IDXGIOutputDuplication>& GetDeskDupl();
const std::unique_ptr<Cursor>& GetCursor();
private:
int id_ = -1;
UINT dpiX_ = -1, dpiY_ = -1;
State state_ = State::NotSet;
std::unique_ptr<Cursor> cursor_;
Microsoft::WRL::ComPtr<IDXGIOutputDuplication> deskDupl_;
ID3D11Texture2D* unityTexture_ = nullptr;
DXGI_OUTPUT_DESC outputDesc_;
MONITORINFOEX monitorInfo_;
};

View File

@@ -0,0 +1,136 @@
#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 "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;
CreateDXGIFactory1(IID_PPV_ARGS(&factory));
// 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()
{
Initialize();
SendMessageToUnity(Message::Reinitialized);
}
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;
}

View File

@@ -0,0 +1,46 @@
#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();
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;
};

View File

@@ -1,145 +1,70 @@
#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();
g_manager = std::make_unique<MonitorManager>();
Debug::Initialize();
}
factory->Release();
}
void FinalizeDuplication()
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API FinalizeUDD()
{
g_deskDupl->Release();
g_deskDupl = nullptr;
g_manager.reset();
std::queue<Message> empty;
g_messages.swap(empty);
Debug::SetLogFunc(nullptr);
Debug::SetErrorFunc(nullptr);
Debug::Finalize();
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces)
{
g_unity = unityInterfaces;
InitializeDuplication();
InitializeUDD();
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UnityPluginUnload()
{
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 +72,286 @@ 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 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);
}
}
}

View File

@@ -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">

View File

@@ -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>

View File

@@ -1,12 +1,12 @@
uDesktopDuplication
===================
**uDesktopDuplication** is a simple Desktop Duplication API implementation for Unity.
**uDesktopDuplication** is an Unity asset to use the realtime screen capture as `Texture2D` using Desktop Duplication API.
ScreenShot
----------
![uDesktopDuplication](https://raw.githubusercontent.com/wiki/hecomi/uDesktopDuplication/screenshot.png)
![uDesktopDuplication](https://raw.githubusercontent.com/wiki/hecomi/uDesktopDuplication/animation.gif)
Environment
@@ -22,21 +22,38 @@ Please download the latest *uDesktopDuplication.unitypackage* from the [release
Usage
-----
Attach `uDesktopDuplication.cs` component to the target object, then its main texture will be replaced with the captured screen.
TODOs
-----
- [x] Mouse cursor
- [ ] Monitor selector
- [ ] Support linear color
Please request new features you want to issues.
Attach `uDesktopDuplication/Texture` component to the target object, then its main texture will be replaced with the captured screen. Please see example scenes for more details.
Version
-------
| Data | Version | Description |
| ---------- | ------- | --------------------------------- |
| 2016/10/27 | 0.0.2 | Add mouse cursor / shader family. |
| 2016/10/28 | 1.0.0 | Support multiple screens. |
| 2016/10/27 | 0.0.3 | Support lienar color. |
| 2016/10/27 | 0.0.2 | Add mouse cursor / shaders. |
| 2016/10/27 | 0.0.1 | Initial commit. |
Lisence
-------
The MIT License (MIT)
Copyright (c) 2016 hecomi
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.