Compare commits

...

97 Commits

Author SHA1 Message Date
hecomi
81bc23d0bc fix bug that destroyed texture in non-main thread. 2016-12-01 22:34:35 +09:00
hecomi
583af4ff46 Change the way to get pixels and finish the implementation of the APIs 🎉 2016-12-01 22:29:52 +09:00
hecomi
7e3df8ec2e add GetPixels() and GetPixel() APIs (wip). 2016-11-30 02:40:16 +09:00
hecomi
32adf6c2d0 fix out-of-area bug. 2016-11-29 20:10:05 +09:00
hecomi
a24472bd92 fix cursor capture area bug. 2016-11-29 20:02:02 +09:00
hecomi
cfee22832a fix indent. 2016-11-28 02:31:55 +09:00
hecomi
9c467a0e6f fix indent. 2016-11-28 02:31:17 +09:00
hecomi
87557ed5ff refactoring. 2016-11-28 02:30:09 +09:00
hecomi
67c90073d9 fix unity log call from native. 2016-11-27 16:22:03 +09:00
hecomi
ce9c148282 remove unused code and fix minor bug. 2016-11-27 15:48:09 +09:00
hecomi
8201a22523 move cursor owner from monitor to manager. 2016-11-27 14:44:23 +09:00
hecomi
5cfbd70e0b output monitor orientation in log. 2016-11-27 13:54:42 +09:00
hecomi
f2b6a99af3 modify the mothod to check monitor orientation. 2016-11-26 18:11:02 +09:00
hecomi
c48bf7fecf add displacement mapping example. 2016-11-26 17:50:03 +09:00
hecomi
6673734b9c remove Cursor-related codes because they were handled directly in the native code. 2016-11-26 16:31:34 +09:00
hecomi
3364c19adf add displacement shader. 2016-11-26 16:14:00 +09:00
hecomi
f7eba2434f fix bug #2 that causes crash after returning from UAC. 2016-11-24 23:38:07 +09:00
hecomi
ddbfad6e95 upload cursor texture in native plugin. 2016-11-20 22:35:52 +09:00
hecomi
49aef432e7 improve performance. 2016-11-20 15:59:06 +09:00
hecomi
fc81892917 cache cursor texture pointer to improve performance. 2016-11-20 15:41:47 +09:00
hecomi
306a656c4e add gaze point analyzer for multiple monitors. 2016-11-20 15:15:19 +09:00
hecomi
faf65b4200 support hot-reload. 2016-11-20 14:32:31 +09:00
hecomi
ab25af516e do not loop generation of monitors in creator. 2016-11-20 12:38:24 +09:00
hecomi
45b0ab2cd8 save scale even after reinitialization. 2016-11-20 03:27:16 +09:00
hecomi
68227e9af6 remove unused debug log. 2016-11-20 03:03:40 +09:00
hecomi
be9871ba92 fix thickness bug. 2016-11-20 02:57:31 +09:00
hecomi
110d5d7bda fix boudary condition of cursor texture. 2016-11-20 02:54:38 +09:00
hecomi
533a4c9f28 add gaze point analyzer. 2016-11-20 02:49:58 +09:00
hecomi
88e8342d7a tweak scenes. 2016-11-19 21:55:25 +09:00
hecomi
72376b9e91 implement dirty rects and move rects. 2016-11-19 21:45:32 +09:00
hecomi
a10d5aa5c1 add buffer class. 2016-11-19 15:37:35 +09:00
hecomi
306185a1e9 add badges. 2016-11-19 13:56:16 +09:00
hecomi
60889d53f4 modify layouter to fix height. 2016-11-19 13:49:38 +09:00
hecomi
f207e65952 do not stop initialization even when getting dpi has error. 2016-11-19 13:09:50 +09:00
hecomi
aea8d112c6 fix indent. 2016-11-18 02:33:24 +09:00
hecomi
9f6971dd45 register callbacks. 2016-11-18 02:28:27 +09:00
hecomi
1be698af63 keep local scale of monitors as 1. 2016-11-18 00:19:22 +09:00
hecomi
064db9f137 modify access rights of layouter. 2016-11-17 23:31:39 +09:00
hecomi
166d172db2 modify access rights. 2016-11-17 23:23:22 +09:00
hecomi
95f8099392 capture cursor mask taking monitor rotation into consideration. 2016-11-17 23:19:46 +09:00
hecomi
9c62157c0c fix typo. 2016-11-16 02:27:57 +09:00
hecomi
be79e52135 fix cursor buffer bug. 2016-11-16 02:24:49 +09:00
hecomi
e9c8b4e571 modify component requirements. 2016-11-16 02:07:57 +09:00
hecomi
77c95ab1bb fix indent 2016-11-16 02:04:13 +09:00
hecomi
7b3990107d fix bugs. 2016-11-15 22:29:36 +09:00
hecomi
5c21b65afe reinitialize texture. 2016-11-15 22:02:00 +09:00
hecomi
e83f43d7fd fix crash bugs and memory leaks. 2016-11-15 21:50:35 +09:00
hecomi
58e14d76d8 make code more stable (still has crash bug). 2016-11-15 03:06:55 +09:00
hecomi
f8d8e64e46 modify codes. 2016-11-15 02:29:38 +09:00
hecomi
0a39e37659 add logs. 2016-11-15 01:00:26 +09:00
hecomi
931ff6b2db modify field order. 2016-11-14 00:04:43 +09:00
hecomi
75819f0a7d fix uv. 2016-11-13 23:43:05 +09:00
hecomi
d6ce41f9b6 remove version information from readme. 2016-11-13 23:28:03 +09:00
hecomi
9f8a026c24 add offset angle xyz. 2016-11-13 23:25:53 +09:00
hecomi
dc702c6e1d support option to choise real / fixed scale. 2016-11-13 23:18:00 +09:00
hecomi
5a3af11d08 add thickness control. 2016-11-13 22:58:14 +09:00
hecomi
0646621f73 modify utility icon. 2016-11-13 22:22:57 +09:00
hecomi
c9d6d3ad6a check texture sizes. 2016-11-13 22:22:33 +09:00
hecomi
78ee75a875 improve detection for monitor state change. 2016-11-13 22:15:22 +09:00
hecomi
c83ce50468 improve log. 2016-11-13 21:59:22 +09:00
hecomi
3567182a4d add dpi warning. 2016-11-13 11:12:45 +09:00
hecomi
e466aa65db add offset angle of roundly layouter. 2016-11-13 11:09:39 +09:00
hecomi
a0edc1a439 use system cursor position for zoom when cursor is invisible (e.g. dragging). 2016-11-12 16:28:33 +09:00
hecomi
1eb821b34d add unknown error. 2016-11-12 16:04:37 +09:00
hecomi
212d8ccb9b check zoom values. 2016-11-12 15:22:53 +09:00
hecomi
fac8fe1ea7 fix dpi zero bug. 2016-11-12 15:22:39 +09:00
hecomi
f90178f1db use inline. 2016-11-12 14:46:22 +09:00
hecomi
74e25f2bd7 add scene manager. 2016-11-12 14:38:13 +09:00
hecomi
70c126be6c fix monitor reinitialization bug. 2016-11-12 13:16:00 +09:00
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
96 changed files with 3449 additions and 607 deletions

1
.gitignore vendored
View File

@@ -27,3 +27,4 @@ sysinfo.txt
.DS_Store
/Assets/AssetStoreTools*
/Assets/Extensions*
/uDesktopDuplication.log

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 57d4a6e606848364c9880f2adec16d90
timeCreated: 1480074774
licenseType: Pro
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

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

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 38a5db360d5147c46ae925104162b881
timeCreated: 1480420198
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: aac40d1b58f3f8940be815779d89b5a4
timeCreated: 1479544409
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: 9ce9467b01f36d647ace156faef9cb9f
timeCreated: 1478927635
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,58 @@
using UnityEngine;
using UnityEngine.Assertions;
[RequireComponent(typeof(uDesktopDuplication.Texture))]
public class DisplacementMapping : MonoBehaviour
{
public enum TargetMonitor
{
Self,
Next,
Prev,
}
public TargetMonitor target = TargetMonitor.Self;
uDesktopDuplication.Texture uddTexture_;
int dispTexId_;
int dispFactorId_;
int tessMinDistId_;
int tessMaxDistId_;
int tessFactorId_;
[Range(0.0f, 10f)] public float displacementFactor = 0.1f;
[Range(0.1f, 10f)] public float tessellationMinDist = 0.5f;
[Range(1.0f, 50f)] public float tessellationMaxDist = 25f;
[Range(1.0f, 50f)] public float tessellationFactor = 25f;
void Start()
{
uddTexture_ = GetComponent<uDesktopDuplication.Texture>();
Assert.IsNotNull(uddTexture_);
dispTexId_ = Shader.PropertyToID("_DispTex");
dispFactorId_ = Shader.PropertyToID("_DispFactor");
tessMinDistId_ = Shader.PropertyToID("_TessMinDist");
tessMaxDistId_ = Shader.PropertyToID("_TessMaxDist");
tessFactorId_ = Shader.PropertyToID("_TessFactor");
}
void Update()
{
var id = uddTexture_.monitor.id;
switch (target) {
case TargetMonitor.Self: break;
case TargetMonitor.Next: ++id; break;
case TargetMonitor.Prev: --id; break;
}
id = Mathf.Clamp(id, 0, uDesktopDuplication.Manager.monitorCount - 1);
var monitor = uDesktopDuplication.Manager.GetMonitor(id);
monitor.shouldBeUpdated = true;
uddTexture_.material.SetTexture(dispTexId_, monitor.texture);
uddTexture_.material.SetFloat(dispFactorId_, displacementFactor);
uddTexture_.material.SetFloat(tessMinDistId_, tessellationMinDist);
uddTexture_.material.SetFloat(tessMaxDistId_, tessellationMaxDist);
uddTexture_.material.SetFloat(tessFactorId_, tessellationFactor);
}
}

View File

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

View File

@@ -0,0 +1,168 @@
using UnityEngine;
using MeshForwardDirection = uDesktopDuplication.Texture.MeshForwardDirection;
[RequireComponent(typeof(uDesktopDuplication.Texture))]
public class GazePointAnalyzer : MonoBehaviour
{
uDesktopDuplication.Texture uddTexture_;
[Tooltip("This needs a small calculation cost.")]
public bool calcAveragePos = true;
private Vector2 averageCoord_ = Vector2.zero;
private Vector2 averageCoordVelocity_ = Vector2.zero;
public Vector3 averagePos
{
get { return GetWorldPositionFromCoord((int)averageCoord_.x, (int)averageCoord_.y); }
}
public Vector3 cursorPos
{
get { return GetWorldPositionFromCoord(uddTexture_.monitor.cursorX, uddTexture_.monitor.cursorY); }
}
private Vector2 preCursorCoord_ = Vector2.zero;
[Header("Filters")]
[Range(0f, 1f)] public float moveRectFilter = 0.05f;
[Range(0f, 1f)] public float mouseFilter = 0.05f;
[Range(0f, 1f)] public float dirtyRectFilter = 0.01f;
[Range(0f, 1f)] public float noEventFilter = 0.01f;
[Range(0f, 1f)] public float velocityFilter = 0.1f;
[Header("Debug")]
public bool drawAveragePos;
public bool drawCursorPos;
public bool drawMoveRects;
public bool drawDirtyRects;
void Start()
{
uddTexture_ = GetComponent<uDesktopDuplication.Texture>();
averageCoord_ = new Vector2(uddTexture_.monitor.width / 2, uddTexture_.monitor.height / 2);
}
public Vector3 GetWorldPositionFromCoord(int u, int v)
{
return uddTexture_.GetWorldPositionFromCoord(u, v);
}
void CalcAveragePos()
{
if (!calcAveragePos) return;
var coord = Vector2.zero;
var monitor = uddTexture_.monitor;
var cursorCoord = new Vector2(monitor.cursorX, monitor.cursorY);
var filter = 0f;
// move rect
if (monitor.moveRectCount > 0) {
foreach (var rects in monitor.moveRects) {
var rect = rects.destination;
var center = new Vector2(
(rect.right + rect.left) / 2,
(rect.bottom + rect.top) / 2);
coord += center;
}
coord /= monitor.moveRectCount;
filter = moveRectFilter;
}
// mouse
else if (
monitor.isCursorVisible &&
cursorCoord != preCursorCoord_ &&
(cursorCoord - preCursorCoord_).magnitude > 5 &&
monitor.cursorX >= 0 &&
monitor.cursorY >= 0)
{
coord = cursorCoord;
filter = mouseFilter;
}
// dirty rect
else if (monitor.dirtyRectCount > 0) {
var totalWeights = 0f;
foreach (var rect in monitor.dirtyRects) {
var center = new Vector2(
(rect.right + rect.left) / 2,
(rect.bottom + rect.top) / 2);
var weight = 1f / Mathf.Sqrt((rect.right - rect.left) * (rect.bottom - rect.top));
coord += center * weight;
totalWeights += weight;
}
coord /= totalWeights;
filter = dirtyRectFilter;
}
// no event
else {
coord = new Vector2(monitor.width / 2, monitor.height / 2);
filter = noEventFilter;
}
var cf = (filter / ((1f / 60) / Time.deltaTime));
var vf = (velocityFilter / ((1f / 60) / Time.deltaTime));
var targetCoord = averageCoord_ + (coord - averageCoord_) * cf;
var targetVelocity = targetCoord - averageCoord_;
if (float.IsNaN(targetCoord.x) || float.IsNaN(targetCoord.y)) return;
if (float.IsNaN(targetVelocity.x) || float.IsNaN(targetVelocity.y)) return;
averageCoordVelocity_ += (targetVelocity - averageCoordVelocity_) * vf;
averageCoord_ += averageCoordVelocity_;
averageCoord_.x = Mathf.Clamp(averageCoord_.x, 0, monitor.width);
averageCoord_.y = Mathf.Clamp(averageCoord_.y, 0, monitor.height);
preCursorCoord_ = cursorCoord;
}
void Update()
{
CalcAveragePos();
DebugDraw();
}
void DebugDraw()
{
if (drawAveragePos) DrawAveragePos();
if (drawCursorPos) DrawCursorPos();
if (drawDirtyRects) DrawDirtyRects();
if (drawMoveRects) DrawMoveRects();
}
void DrawRect(uDesktopDuplication.RECT rect, Color color)
{
var p0 = GetWorldPositionFromCoord(rect.left, rect.top);
var p1 = GetWorldPositionFromCoord(rect.right, rect.top);
var p2 = GetWorldPositionFromCoord(rect.right, rect.bottom);
var p3 = GetWorldPositionFromCoord(rect.left, rect.bottom);
Debug.DrawLine(p0, p1, color);
Debug.DrawLine(p1, p2, color);
Debug.DrawLine(p2, p3, color);
Debug.DrawLine(p3, p0, color);
}
void DrawAveragePos()
{
Debug.DrawLine(transform.position, averagePos, Color.yellow);
}
void DrawCursorPos()
{
Debug.DrawLine(transform.position, cursorPos, Color.grey);
}
void DrawMoveRects()
{
foreach (var rect in uddTexture_.monitor.moveRects) {
DrawRect(rect.source, Color.blue);
DrawRect(rect.destination, Color.green);
}
}
void DrawDirtyRects()
{
foreach (var rect in uddTexture_.monitor.dirtyRects) {
DrawRect(rect, Color.red);
}
}
}

View File

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

View File

@@ -0,0 +1,36 @@
using UnityEngine;
public class GetPixelsExample : MonoBehaviour
{
[SerializeField] uDesktopDuplication.Texture uddTexture;
[SerializeField] int x = 100;
[SerializeField] int y = 100;
const int width = 64;
const int height = 32;
public Texture2D texture;
Color32[] colors = new Color32[width * height];
void Start()
{
texture = new Texture2D(width, height, TextureFormat.ARGB32, false);
GetComponent<Renderer>().material.mainTexture = texture;
}
void Update()
{
// must be called (performance will be slightly down).
uDesktopDuplication.Manager.primary.useGetPixels = true;
var monitor = uddTexture.monitor;
if (!monitor.hasBeenUpdated) return;
if (monitor.GetPixels(colors, x, y, width, height)) {
texture.SetPixels32(colors);
texture.Apply();
}
Debug.Log(monitor.GetPixel(monitor.cursorX, monitor.cursorY));
}
}

View File

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

View File

@@ -0,0 +1,50 @@
using UnityEngine;
[RequireComponent(typeof(uDesktopDuplication.Texture))]
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 LateUpdate()
{
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 x = monitor.isCursorVisible ?
(float)monitor.cursorX / monitor.width :
(float)monitor.systemCursorX / monitor.width;
var y = monitor.isCursorVisible ?
(float)monitor.cursorY / monitor.height :
(float)monitor.systemCursorY / 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;
}
}

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,69 @@
using UnityEngine;
[RequireComponent(typeof(MultipleMonitorCreator))]
public class MultipleMonitorAnalyzer : MonoBehaviour
{
MultipleMonitorCreator creator_;
Vector3 gazePoint_ = Vector3.zero;
public Vector3 gazePoint
{
get { return gazePoint_; }
private set
{
gazePoint_ += (value - gazePoint) * (gazePointFilter / (1f / 60 / Time.deltaTime));
}
}
[Header("Filters")]
[Range(0f, 1f)] public float gazePointFilter = 0.1f;
[Range(0f, 1f)] public float moveRectFilter = 0.05f;
[Range(0f, 1f)] public float mouseFilter = 0.05f;
[Range(0f, 1f)] public float dirtyRectFilter = 0.01f;
[Range(0f, 1f)] public float noEventFilter = 0.01f;
[Range(0f, 1f)] public float velocityFilter = 0.1f;
[Header("Debug")]
[SerializeField] bool drawGazePoint;
[SerializeField] bool drawAveragePos;
[SerializeField] bool drawMoveRects;
[SerializeField] bool drawDirtyRects;
void Start()
{
creator_ = GetComponent<MultipleMonitorCreator>();
}
void Update()
{
var cursorMonitorId = uDesktopDuplication.Manager.cursorMonitorId;
for (int i = 0; i < creator_.monitors.Count; ++i) {
var info = creator_.monitors[i];
var analyzer =
info.gameObject.GetComponent<GazePointAnalyzer>() ??
info.gameObject.AddComponent<GazePointAnalyzer>();
UpdateAnalyzer(analyzer);
if (info.uddTexture.monitorId == cursorMonitorId) {
gazePoint = analyzer.averagePos;
}
}
if (drawGazePoint) DrawGazePoint();
}
void DrawGazePoint()
{
Debug.DrawLine(transform.position, gazePoint, Color.magenta);
}
void UpdateAnalyzer(GazePointAnalyzer analyzer)
{
analyzer.moveRectFilter = moveRectFilter;
analyzer.mouseFilter = mouseFilter;
analyzer.dirtyRectFilter = dirtyRectFilter;
analyzer.noEventFilter = noEventFilter;
analyzer.velocityFilter = velocityFilter;
analyzer.drawAveragePos = drawAveragePos;
analyzer.drawMoveRects = drawMoveRects;
analyzer.drawDirtyRects = drawDirtyRects;
}
}

View File

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

View File

@@ -1,20 +1,80 @@
using UnityEngine;
using UnityEngine.Assertions;
using System.Collections.Generic;
using MeshForwardDirection = uDesktopDuplication.Texture.MeshForwardDirection;
using MonitorState = uDesktopDuplication.MonitorState;
public class MultipleMonitorCreator : MonoBehaviour
{
[SerializeField] GameObject monitorPrefab;
[SerializeField] float scale = 1f;
[SerializeField] float margin = 1f;
[Tooltip("Create monitors using this prefab.")]
public GameObject monitorPrefab;
private List<GameObject> monitors_ = new List<GameObject>();
private float totalWidth_ = 0f;
public enum ScaleMode
{
Real,
Fixed,
Pixel,
}
[Tooltip("Real: DPI-based real scale \nFixed: Same width \nPixel: bigger if screen resolution is high.")]
public ScaleMode scaleMode = ScaleMode.Fixed;
[Tooltip("Use this scale as width if scaleMode is Fixed.")]
public float scale = 0.5f;
[Tooltip("Please specify the surface direction of the mesh (e.g. Plane => Y.)")]
public MeshForwardDirection meshForwardDirection = MeshForwardDirection.Z;
[Tooltip("Remove unsupported monitors automatically after removeWaitDuration.")]
public bool removeIfUnsupported = true;
[Tooltip("Remove unsupported monitors automatically after removeWaitDuration.")]
public float removeWaitDuration = 5f;
[Tooltip("Remove all childrens (for debug).")]
public bool removeChildrenWhenClear = true;
bool hasMonitorUnsupportStateChecked_ = false;
float removeWaitTimer_ = 0f;
public class MonitorInfo
{
public GameObject gameObject { get; set; }
public Quaternion originalRotation { get; set; }
public Vector3 originalLocalScale { get; set; }
public uDesktopDuplication.Texture uddTexture { get; set; }
public Mesh mesh { get; set; }
}
private List<MonitorInfo> monitors_ = new List<MonitorInfo>();
public List<MonitorInfo> monitors { get { return monitors_; } }
public class SavedMonitorInfo
{
public float widthScale = 1f;
public float heightScale = 1f;
}
private List<SavedMonitorInfo> savedInfoList_ = new List<SavedMonitorInfo>();
public List<SavedMonitorInfo> savedInfoList { get { return savedInfoList_; } }
void Start()
{
uDesktopDuplication.Manager.CreateInstance();
Create();
}
void Update()
{
if (removeIfUnsupported) {
RemoveUnsupportedDisplayAfterRemoveTimer();
}
if (uDesktopDuplication.Manager.monitorCount != monitors.Count) {
Recreate();
}
}
void OnEnable()
{
uDesktopDuplication.Manager.onReinitialized += Recreate;
@@ -25,26 +85,52 @@ public class MultipleMonitorCreator : MonoBehaviour
uDesktopDuplication.Manager.onReinitialized -= Recreate;
}
void Create()
void RemoveUnsupportedDisplayAfterRemoveTimer()
{
CreateMonitors();
LayoutMonitors();
if (!hasMonitorUnsupportStateChecked_) {
removeWaitTimer_ += Time.deltaTime;
if (removeWaitTimer_ > removeWaitDuration) {
hasMonitorUnsupportStateChecked_ = true;
foreach (var info in monitors) {
if (info.uddTexture.monitor.state == MonitorState.Unsupported) {
Destroy(info.gameObject);
}
}
monitors.RemoveAll(info => info.uddTexture.monitor.state == MonitorState.Unsupported);
}
}
}
void CreateMonitors()
void ResetRemoveTimer()
{
// Sort monitors in coordinate order
var monitors = uDesktopDuplication.Manager.monitors;
monitors.Sort((a, b) => a.left - b.left);
hasMonitorUnsupportStateChecked_ = false;
removeWaitTimer_ = 0f;
}
void Create()
{
ResetRemoveTimer();
// Create monitors
var n = monitors.Count;
totalWidth_ = 0f;
for (int i = 0 ; i < n; ++i) {
var n = uDesktopDuplication.Manager.monitors.Count;
for (int i = 0; i < n; ++i) {
// Create monitor obeject
var go = Instantiate(monitorPrefab);
go.name = "Monitor " + i;
monitors_.Add(go);
// Saved infomation
if (savedInfoList.Count == i) {
savedInfoList.Add(new SavedMonitorInfo());
Assert.AreEqual(i, savedInfoList.Count - 1);
}
var savedInfo = savedInfoList[i];
// Expand AABB
var mesh = go.GetComponent<MeshFilter>().mesh; // clone
var aabbScale = mesh.bounds.size;
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>();
@@ -52,42 +138,63 @@ public class MultipleMonitorCreator : MonoBehaviour
var monitor = texture.monitor;
// Set width / height
go.transform.localScale = new Vector3(monitor.widthMeter, go.transform.localScale.y, monitor.heightMeter);
float width = 1f, height = 1f;
switch (scaleMode) {
case ScaleMode.Real:
width = monitor.widthMeter;
height = monitor.heightMeter;
break;
case ScaleMode.Fixed:
width = scale * (monitor.isHorizontal ? monitor.aspect : 1f);
height = scale * (monitor.isHorizontal ? 1f : 1f / monitor.aspect);
break;
case ScaleMode.Pixel:
width = scale * (monitor.isHorizontal ? 1f : monitor.aspect) * ((float)monitor.width / 1920);
height = scale * (monitor.isHorizontal ? 1f / monitor.aspect : 1f) * ((float)monitor.width / 1920);
break;
}
width *= savedInfo.widthScale;
height *= savedInfo.heightScale;
if (meshForwardDirection == MeshForwardDirection.Y) {
go.transform.localScale = new Vector3(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);
// Calc actual size considering mesh size
var scaleX = go.GetComponent<MeshFilter>().sharedMesh.bounds.extents.x * 2f;
totalWidth_ += monitor.widthMeter * scaleX;
// Save
var info = new MonitorInfo();
info.gameObject = go;
info.originalRotation = go.transform.rotation;
info.originalLocalScale = go.transform.localScale;
info.uddTexture = texture;
info.mesh = mesh;
monitors.Add(info);
}
}
void LayoutMonitors()
{
// Set positions with margin
totalWidth_ += margin * (monitors_.Count - 1);
var x = -totalWidth_ / 2;
foreach (var go in monitors_) {
var monitor = go.GetComponent<uDesktopDuplication.Texture>().monitor;
var halfScaleX = go.GetComponent<MeshFilter>().sharedMesh.bounds.extents.x;
var width = monitor.widthMeter;
var halfWidth = width * halfScaleX;
x += halfWidth;
go.transform.localPosition = new Vector3(x, 0f, 0f);
x += halfWidth + margin;
}
// Sort monitors in coordinate order
monitors.Sort((a, b) => a.uddTexture.monitor.left - b.uddTexture.monitor.left);
}
void Clear()
{
foreach (var go in monitors_) {
Destroy(go);
foreach (var info in monitors) {
Destroy(info.gameObject);
}
monitors_.Clear();
if (removeChildrenWhenClear) {
for (int i = 0; i < transform.childCount; ++i) {
Destroy(transform.GetChild(i).gameObject);
}
}
monitors.Clear();
}
void Recreate()
[ContextMenu("Recreate")]
public void Recreate()
{
Clear();
Create();

View File

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

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,102 @@
using UnityEngine;
using MeshForwardDirection = uDesktopDuplication.Texture.MeshForwardDirection;
public class MultipleMonitorRoundLayouter : MultipleMonitorLayouter
{
[SerializeField] bool debugDraw = true;
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.
// And save width / height to use same values after reinitialization.
for (int i = 0; i < n; ++i) {
var info = monitors[i];
var savedInfo = creator_.savedInfoList[i];
var scale = info.gameObject.transform.localScale;
if (creator_.meshForwardDirection == MeshForwardDirection.Y) {
scale.y = 1f;
savedInfo.widthScale = scale.x / info.originalLocalScale.x;
savedInfo.heightScale = scale.z / info.originalLocalScale.z;
} else {
scale.z = 1f;
savedInfo.widthScale = scale.x / info.originalLocalScale.x;
savedInfo.heightScale = scale.y / info.originalLocalScale.y;
}
info.gameObject.transform.localScale = scale;
}
// keep thicness plus value.
thickness = Mathf.Max(thickness, 0f);
// calculate total width
var totalWidth = 0f;
foreach (var info in monitors) {
var width = info.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();
if (debugDraw) 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);
}
}
}

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

@@ -6,10 +6,12 @@ public class ToggleMonitors : MonoBehaviour
{
if (Input.GetKeyDown(KeyCode.Tab)) {
var texture = GetComponent<uDesktopDuplication.Texture>();
var id = texture.monitorId;
var n = uDesktopDuplication.Manager.monitorCount;
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) {
texture.monitorId--;
texture.monitorId = (id - 1 < 0) ? 0 : (id - 1);
} else {
texture.monitorId++;
texture.monitorId = (id + 1 >= n) ? (n - 1) : (id + 1);
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 596fd740aeeee0d4cb4f962d4072954e
timeCreated: 1480074277
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.

After

Width:  |  Height:  |  Size: 37 KiB

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: 0
swapUVChannels: 0
generateSecondaryUV: 0
useFileUnits: 1
optimizeMeshForGPU: 0
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.

View File

@@ -0,0 +1,87 @@
fileFormatVersion: 2
guid: f30f6cfd6aa37554ca886424cada7cbb
timeCreated: 1480072865
licenseType: Pro
ModelImporter:
serializedVersion: 19
fileIDToRecycleName:
100000: //RootNode
100002: uDD_Plane_MeshPart0
100004: uDD_Plane_MeshPart1
400000: //RootNode
400002: uDD_Plane_MeshPart0
400004: uDD_Plane_MeshPart1
2300000: //RootNode
2300002: uDD_Plane_MeshPart0
2300004: uDD_Plane_MeshPart1
3300000: //RootNode
3300002: uDD_Plane_MeshPart0
3300004: uDD_Plane_MeshPart1
4300000: uDD_Plane
4300002: uDD_Plane_MeshPart0
4300004: uDD_Plane_MeshPart1
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: 0
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:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -1,71 +0,0 @@
using UnityEngine;
using System.Collections.Generic;
namespace uDesktopDuplication
{
[AddComponentMenu("uDesktopDuplication/Cursor"),
RequireComponent(typeof(Texture))]
public class Cursor : MonoBehaviour
{
Vector3 worldPosition { get; set; }
private Texture uddTexture_;
private Monitor monitor { get { return uddTexture_.monitor; } }
private Texture2D currentTexture_;
private Dictionary<Vector2, Texture2D> textures_ = new Dictionary<Vector2, Texture2D>();
[SerializeField] Vector2 modelScale = Vector2.one;
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 scale = new Vector2(w, h);
if (!textures_.ContainsKey(scale)) {
var texture = new Texture2D(w, h, TextureFormat.BGRA32, false);
texture.wrapMode = TextureWrapMode.Clamp;
textures_.Add(scale, texture);
}
var cursorTexture = textures_[scale];
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,7 +1,10 @@
using System;
using UnityEngine;
using System;
using System.Text;
using System.Runtime.InteropServices;
#pragma warning disable 114, 465
namespace uDesktopDuplication
{
@@ -9,10 +12,12 @@ public enum Message
{
None = -1,
Reinitialized = 0,
TextureSizeChanged = 1,
}
public enum CursorShapeType
{
Unspecified = 0,
MonoChrome = 1,
Color = 2,
MaskedColor = 4,
@@ -29,24 +34,57 @@ public enum MonitorRotation
public enum MonitorState
{
NotSet = -1,
Available = 0,
InvalidArg = 1,
AccessDenied = 2,
Unsupported = 3,
CurrentlyNotAvailable = 4,
SessionDisconnected = 5,
AccessLost = 6,
NotSet = -1,
Available = 0,
InvalidArg = 1,
AccessDenied = 2,
Unsupported = 3,
CurrentlyNotAvailable = 4,
SessionDisconnected = 5,
AccessLost = 6,
TextureSizeInconsistent = 7,
Unknown = 999,
}
public enum DebugMode
{
None = 0,
File = 1,
UnityLog = 2, /* currently has bug when app exits. */
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
[MarshalAs(UnmanagedType.I4)]
public int left;
[MarshalAs(UnmanagedType.I4)]
public int top;
[MarshalAs(UnmanagedType.I4)]
public int right;
[MarshalAs(UnmanagedType.I4)]
public int bottom;
}
[StructLayout(LayoutKind.Sequential)]
public struct DXGI_OUTDUPL_MOVE_RECT
{
public RECT source;
public RECT destination;
}
public static class Lib
{
public delegate void MessageHandler(Message message);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DebugLogDelegate(string str);
[DllImport("uDesktopDuplication")]
public static extern void InitializeUDD();
public static extern bool IsInitialized();
[DllImport("uDesktopDuplication")]
public static extern void FinalizeUDD();
public static extern void Initialize();
[DllImport("uDesktopDuplication")]
public static extern void Finalize();
[DllImport("uDesktopDuplication")]
public static extern void Reinitialize();
[DllImport("uDesktopDuplication")]
@@ -54,8 +92,22 @@ public static class Lib
[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(DebugLogDelegate func);
[DllImport("uDesktopDuplication")]
public static extern void SetErrorFunc(DebugLogDelegate 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();
@@ -64,6 +116,8 @@ public static class Lib
[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);
@@ -90,21 +144,35 @@ public static class Lib
[DllImport("uDesktopDuplication")]
public static extern bool IsCursorVisible(int id);
[DllImport("uDesktopDuplication")]
public static extern int GetCursorX(int id);
public static extern int GetCursorX();
[DllImport("uDesktopDuplication")]
public static extern int GetCursorY(int id);
public static extern int GetCursorY();
[DllImport("uDesktopDuplication")]
public static extern int GetCursorShapeWidth(int id);
public static extern int GetCursorShapeWidth();
[DllImport("uDesktopDuplication")]
public static extern int GetCursorShapeHeight(int id);
public static extern int GetCursorShapeHeight();
[DllImport("uDesktopDuplication")]
public static extern int GetCursorShapePitch(int id);
public static extern int GetCursorShapePitch();
[DllImport("uDesktopDuplication")]
public static extern CursorShapeType GetCursorShapeType(int id);
public static extern CursorShapeType GetCursorShapeType();
[DllImport("uDesktopDuplication")]
public static extern void GetCursorTexture(int id, System.IntPtr ptr);
[DllImport("uDesktopDuplication")]
public static extern int SetTexturePtr(int id, IntPtr ptr);
[DllImport("uDesktopDuplication")]
public static extern int GetMoveRectCount(int id);
[DllImport("uDesktopDuplication", EntryPoint = "GetMoveRects")]
private static extern IntPtr GetMoveRects_Internal(int id);
[DllImport("uDesktopDuplication")]
public static extern int GetDirtyRectCount(int id);
[DllImport("uDesktopDuplication", EntryPoint = "GetDirtyRects")]
private static extern IntPtr GetDirtyRects_Internal(int id);
[DllImport("uDesktopDuplication", EntryPoint = "GetPixels")]
private static extern bool GetPixels_Internal(int id, System.IntPtr ptr, int x, int y, int width, int height);
[DllImport("uDesktopDuplication")]
public static extern bool HasBeenUpdated(int id);
[DllImport("uDesktopDuplication")]
public static extern bool UseGetPixels(int id, bool use);
public static string GetName(int id)
{
@@ -112,6 +180,60 @@ public static class Lib
GetName(id, buf, buf.Capacity);
return buf.ToString();
}
public static DXGI_OUTDUPL_MOVE_RECT[] GetMoveRects(int id)
{
var count = GetMoveRectCount(id);
var rects = new DXGI_OUTDUPL_MOVE_RECT[count];
var ptr = GetMoveRects_Internal(id);
var size = Marshal.SizeOf(typeof(DXGI_OUTDUPL_MOVE_RECT));
for (int i = 0; i < count; ++i) {
var data = new IntPtr(ptr.ToInt64() + size * i);
rects[i] = (DXGI_OUTDUPL_MOVE_RECT)Marshal.PtrToStructure(data, typeof(DXGI_OUTDUPL_MOVE_RECT));
}
return rects;
}
public static RECT[] GetDirtyRects(int id)
{
var count = GetDirtyRectCount(id);
var rects = new RECT[count];
var ptr = GetDirtyRects_Internal(id);
var size = Marshal.SizeOf(typeof(RECT));
for (int i = 0; i < count; ++i) {
var data = new IntPtr(ptr.ToInt64() + size * i);
rects[i] = (RECT)Marshal.PtrToStructure(data, typeof(RECT));
}
return rects;
}
public static Color32[] GetPixels(int id, int x, int y, int width, int height)
{
var color = new Color32[width * height];
GetPixels(id, color, x, y, width, height);
return color;
}
public static bool GetPixels(int id, Color32[] colors, int x, int y, int width, int height)
{
if (colors.Length < width * height) {
Debug.LogErrorFormat("colors is small.", id, x, y, width, height);
return false;
}
var handle = GCHandle.Alloc(colors, GCHandleType.Pinned);
var ptr = handle.AddrOfPinnedObject();
if (!GetPixels_Internal(id, ptr, x, y, width, height)) {
Debug.LogErrorFormat("GetPixels({0}, {1}, {2}, {3}, {4}) failed.", id, x, y, width, height);
return false;
}
handle.Free();
return true;
}
public static Color32 GetPixel(int id, int x, int y)
{
return GetPixels(id, x, y, 1, 1)[0];
}
}
}

View File

@@ -10,21 +10,22 @@ public class Manager : MonoBehaviour
private static Manager instance_;
public static Manager instance
{
get
{
if (instance_) {
return instance_;
}
get { return CreateInstance(); }
}
var manager = FindObjectOfType<Manager>();
if (manager) {
manager.Awake();
return manager;
}
public static Manager CreateInstance()
{
if (instance_ != null) return instance_;
var go = new GameObject("uDesktopDuplicationManager");
return go.AddComponent<Manager>();
var manager = FindObjectOfType<Manager>();
if (manager) {
instance_ = manager;
return manager;
}
var go = new GameObject("uDesktopDuplicationManager");
instance_ = go.AddComponent<Manager>();
return instance_;
}
private List<Monitor> monitors_ = new List<Monitor>();
@@ -38,6 +39,11 @@ public class Manager : MonoBehaviour
get { return Lib.GetMonitorCount(); }
}
static public int cursorMonitorId
{
get { return Lib.GetCursorMonitorId(); }
}
static public Monitor primary
{
get
@@ -46,36 +52,72 @@ public class Manager : MonoBehaviour
}
}
[Tooltip("Debug mode is not applied while running.")]
[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;
private bool shouldReinitialize_ = false;
private float reinitializationTimer_ = 0f;
private bool isFirstFrame_ = true;
public static event Lib.DebugLogDelegate onDebugLog = msg => Debug.Log(msg);
public static event Lib.DebugLogDelegate onDebugErr = msg => Debug.LogError(msg);
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.InitializeUDD();
// for simple singleton
if (instance_ == this) {
return;
}
if (instance_ != null && instance_ != this) {
Destroy(gameObject);
return;
}
if (instance_ != null) return;
instance_ = this;
CreateMonitors();
Lib.SetDebugMode(debugMode);
Lib.SetLogFunc(onDebugLog);
Lib.SetErrorFunc(onDebugErr);
Lib.SetTimeout(desktopDuplicationApiTimeout);
Lib.Initialize();
CreateMonitors();
}
void OnApplicationQuit()
{
Lib.FinalizeUDD();
Lib.Finalize();
DestroyMonitors();
}
void OnEnable()
{
renderCoroutine_ = StartCoroutine(OnRender());
if (!isFirstFrame_) {
Reinitialize();
}
Lib.SetDebugMode(debugMode);
Lib.SetLogFunc(onDebugLog);
}
void OnDisable()
@@ -84,6 +126,9 @@ public class Manager : MonoBehaviour
StopCoroutine(renderCoroutine_);
renderCoroutine_ = null;
}
Lib.SetLogFunc(null);
Lib.SetErrorFunc(null);
}
void Update()
@@ -91,44 +136,54 @@ public class Manager : MonoBehaviour
Lib.Update();
ReinitializeIfNeeded();
UpdateMessage();
/*
foreach (var monitor in monitors_) {
Debug.LogFormat("[{0}] {1}", monitor.id, monitor.state);
}
*/
isFirstFrame_ = false;
}
[ContextMenu("Reinitialize")]
void Reinitialize()
public void Reinitialize()
{
Debug.Log("[uDesktopDuplication] Reinitialize");
Debug.Log("[uDD] Reinitialize");
Lib.Reinitialize();
if (onReinitialized != null) onReinitialized();
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) {
if (!shouldReinitialize) {
shouldReinitialize = true;
reinitializationTimer = 0f;
break;
}
var state = monitor.state;
if (
state == MonitorState.NotSet ||
state == MonitorState.AccessLost ||
state == MonitorState.AccessDenied ||
state == MonitorState.SessionDisconnected ||
state == MonitorState.Unknown
) {
reinitializeNeeded = true;
break;
}
}
if (shouldReinitialize) {
if (reinitializationTimer > retryReinitializationDuration) {
if (Lib.HasMonitorCountChanged()) {
reinitializeNeeded = true;
}
if (!shouldReinitialize_ && reinitializeNeeded) {
shouldReinitialize_ = true;
reinitializationTimer_ = 0f;
}
if (shouldReinitialize_) {
if (reinitializationTimer_ > retryReinitializationDuration) {
Reinitialize();
shouldReinitialize = false;
shouldReinitialize_ = false;
}
reinitializationTimer += Time.deltaTime;
reinitializationTimer_ += Time.deltaTime;
}
}
@@ -136,10 +191,14 @@ public class Manager : MonoBehaviour
{
var message = Lib.PopMessage();
while (message != Message.None) {
Debug.Log("[uDD] " + message);
switch (message) {
case Message.Reinitialized:
ReinitializeMonitors();
break;
case Message.TextureSizeChanged:
RecreateTextures();
break;
default:
break;
}
@@ -163,18 +222,35 @@ public class Manager : MonoBehaviour
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();
}
monitors[i].Reinitialize();
}
}
void RecreateTextures()
{
for (int i = 0; i < monitorCount; ++i) {
monitors[i].CreateTextureIfNeeded();
}
}
}

View File

@@ -11,24 +11,35 @@ public class Monitor
switch (state)
{
case MonitorState.Available:
break;
case MonitorState.InvalidArg:
Debug.LogErrorFormat("[{0}:{1}] Invalid.", id, name);
Debug.LogErrorFormat("[uDD] {0}:{1} => Invalid.", id, name);
break;
case MonitorState.AccessDenied:
Debug.LogErrorFormat("[{0}:{1}] Access Denied.", id, name);
Debug.LogWarningFormat("[uDD] {0}:{1} => Access Denied.", id, name);
break;
case MonitorState.Unsupported:
Debug.LogErrorFormat("[{0}:{1}] Unsupported.", id, name);
Debug.LogWarningFormat("[uDD] {0}:{1} => Unsupported.", id, name);
break;
case MonitorState.SessionDisconnected:
Debug.LogErrorFormat("[{0}:{1}] Disconnected.", id, name);
Debug.LogWarningFormat("[uDD] {0}:{1} => Disconnected.", id, name);
break;
case MonitorState.NotSet:
Debug.LogErrorFormat("[{0}:{1}] Something wrong.", id, name);
Debug.LogErrorFormat("[uDD] {0}:{1} => Something wrong.", id, name);
break;
default:
Debug.LogErrorFormat("[uDD] {0}:{1} => Unknown error.", id, name);
break;
}
if (dpiX == 0 || dpiY == 0) {
Debug.LogWarningFormat("[uDD] {0}:{1} => Could not get DPI", id, name);
}
}
~Monitor()
{
}
public int id
@@ -94,12 +105,22 @@ public class Monitor
public int dpiX
{
get { return Lib.GetDpiX(id); }
get
{
var dpi = Lib.GetDpiX(id);
if (dpi == 0) dpi = 100; // when monitors are duplicated
return dpi;
}
}
public int dpiY
{
get { return Lib.GetDpiY(id); }
get
{
var dpi = Lib.GetDpiY(id);
if (dpi == 0) dpi = 100; // when monitors are duplicated
return dpi;
}
}
public float widthMeter
@@ -124,12 +145,22 @@ public class Monitor
public bool isHorizontal
{
get { return width > height; }
get
{
return
(rotation == MonitorRotation.Identity) ||
(rotation == MonitorRotation.Rotate180);
}
}
public bool isVertical
{
get { return height > width; }
get
{
return
(rotation == MonitorRotation.Rotate90) ||
(rotation == MonitorRotation.Rotate270);
}
}
public bool isCursorVisible
@@ -139,27 +170,84 @@ public class Monitor
public int cursorX
{
get { return Lib.GetCursorX(id); }
get { return Lib.GetCursorMonitorId() == id ? Lib.GetCursorX() : -1; }
}
public int cursorY
{
get { return Lib.GetCursorY(id); }
get { return Lib.GetCursorMonitorId() == id ? Lib.GetCursorY() : -1; }
}
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); }
get { return Lib.GetCursorShapeWidth(); }
}
public int cursorShapeHeight
{
get { return Lib.GetCursorShapeHeight(id); }
get { return Lib.GetCursorShapeHeight(); }
}
public CursorShapeType cursorShapeType
{
get { return Lib.GetCursorShapeType(id); }
get { return Lib.GetCursorShapeType(); }
}
public int moveRectCount
{
get { return Lib.GetMoveRectCount(id); }
}
public DXGI_OUTDUPL_MOVE_RECT[] moveRects
{
get { return Lib.GetMoveRects(id); }
}
public int dirtyRectCount
{
get { return Lib.GetDirtyRectCount(id); }
}
public RECT[] dirtyRects
{
get { return Lib.GetDirtyRects(id); }
}
public bool hasBeenUpdated
{
get { return Lib.HasBeenUpdated(id); }
}
bool useGetPixels_ = false;
public bool useGetPixels
{
get
{
return useGetPixels_;
}
set
{
useGetPixels_ = value;
Lib.UseGetPixels(id, value);
}
}
public bool shouldBeUpdated
@@ -168,25 +256,33 @@ public class Monitor
set;
}
private static Texture2D errorTexture_;
private static readonly string errorTexturePath = "uDesktopDuplication/Textures/NotAvailable";
private Texture2D errorTexture
{
get
{
return errorTexture_ ??
(errorTexture_ = Resources.Load<Texture2D>(errorTexturePath));
}
}
private Texture2D texture_;
private System.IntPtr texturePtr_;
public Texture2D texture
{
get
{
if (!available) {
return Resources.Load<Texture2D>("uDesktopDuplication/Textures/NotAvailable");
}
if (texture_ == null) {
CreateTexture();
}
if (!available) return errorTexture;
if (texture_ == null) CreateTextureIfNeeded();
return texture_;
}
}
public void Render()
{
if (texture_ && available) {
Lib.SetTexturePtr(id, texture_.GetNativeTexturePtr());
if (texture_ && available && texturePtr_ != System.IntPtr.Zero) {
Lib.SetTexturePtr(id, texturePtr_);
GL.IssuePluginEvent(Lib.GetRenderEventFunc(), id);
}
}
@@ -196,7 +292,7 @@ public class Monitor
Lib.GetCursorTexture(id, ptr);
}
void CreateTexture()
public void CreateTextureIfNeeded()
{
if (!available) return;
@@ -204,13 +300,8 @@ public class Monitor
var h = isHorizontal ? height : width;
bool shouldCreate = true;
if (texture_) {
if (texture_.width != w || texture_.height != h) {
if (texture_) Object.DestroyImmediate(texture_);
shouldCreate = true;
} else {
shouldCreate = false;
}
if (texture_ && texture_.width == w && texture_.height == h) {
shouldCreate = false;
}
if (w <= 0 || h <= 0) {
@@ -218,13 +309,58 @@ public class Monitor
}
if (shouldCreate) {
texture_ = new Texture2D(w, h, TextureFormat.BGRA32, false);
CreateTexture();
}
}
void CreateTexture()
{
DestroyTexture();
var w = isHorizontal ? width : height;
var h = isHorizontal ? height : width;
texture_ = new Texture2D(w, h, TextureFormat.BGRA32, false);
texturePtr_ = texture_.GetNativeTexturePtr();
}
public void DestroyTexture()
{
if (texture_) {
Object.Destroy(texture_);
texture_ = null;
texturePtr_ = System.IntPtr.Zero;
}
}
public void Reinitialize()
{
CreateTexture();
CreateTextureIfNeeded();
}
public Color32[] GetPixels(int x, int y, int width, int height)
{
if (!useGetPixels_) {
Debug.LogErrorFormat("Please set Monitor[{0}].useGetPixels as true.", id);
return null;
}
return Lib.GetPixels(id, x, y, width, height);
}
public bool GetPixels(Color32[] colors, int x, int y, int width, int height)
{
if (!useGetPixels_) {
Debug.LogErrorFormat("Please set Monitor[{0}].useGetPixels as true.", id);
return false;
}
return Lib.GetPixels(id, colors, x, y, width, height);
}
public Color32 GetPixel(int x, int y)
{
if (!useGetPixels_) {
Debug.LogErrorFormat("Please set Monitor[{0}].useGetPixels as true.", id);
return Color.black;
}
return Lib.GetPixel(id, x, y);
}
}

View File

@@ -13,48 +13,144 @@ public class Texture : MonoBehaviour
set
{
monitor_ = value;
material = GetComponent<Renderer>().material;
material.mainTexture = monitor_.texture;
if (monitor_ != null) {
material = GetComponent<Renderer>().material; // clone
material.mainTexture = monitor_.texture;
material.SetFloat("_Width", transform.localScale.x);
}
}
}
private int lastMonitorId_ = 0;
public int monitorId
{
get { return monitor.id; }
set { monitor = Manager.monitors[Mathf.Clamp(value, 0, Manager.monitorCount - 1)]; }
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");
material.SetInt("_Bend", 1);
} else {
material.DisableKeyword("BEND_ON");
material.SetInt("_Bend", 0);
}
}
}
public MeshForwardDirection meshForwardDirection
{
get
{
return (MeshForwardDirection)material.GetInt("_Forward");
}
set
{
switch (value) {
case MeshForwardDirection.Y:
material.SetInt("_Forward", 0);
material.EnableKeyword("_FORWARD_Y");
material.DisableKeyword("_FORWARD_Z");
break;
case MeshForwardDirection.Z:
material.SetInt("_Forward", 1);
material.DisableKeyword("_FORWARD_Y");
material.EnableKeyword("_FORWARD_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()
{
if (!GetComponent<Cursor>())
{
gameObject.AddComponent<Cursor>();
}
}
void OnEnable()
{
if (monitor == null) {
monitor = Manager.primary;
}
Manager.onReinitialized += Reinitialize;
}
void OnDisable()
{
Manager.onReinitialized -= Reinitialize;
}
void Update()
{
KeepMonitor();
monitor.shouldBeUpdated = true;
UpdateMaterial();
}
void KeepMonitor()
{
if (monitor == null) {
Reinitialize();
} else {
lastMonitorId_ = monitorId;
}
}
void Reinitialize()
{
// Monitor instance is released here when initialized.
monitor = Manager.GetMonitor(lastMonitorId_);
}
void UpdateMaterial()
{
Invert();
Rotate();
Clip();
}
void Invert()
{
if (invertX) {
material.EnableKeyword("INVERT_X");
@@ -67,7 +163,10 @@ public class Texture : MonoBehaviour
} else {
material.DisableKeyword("INVERT_Y");
}
}
void Rotate()
{
switch (monitor.rotation)
{
case MonitorRotation.Identity:
@@ -94,6 +193,43 @@ public class Texture : MonoBehaviour
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");
}
}
public Vector3 GetWorldPositionFromCoord(int u, int v)
{
// Mesh & Scale information
var mesh = GetComponent<MeshFilter>().sharedMesh;
var width = transform.localScale.x * (mesh.bounds.extents.x * 2f);
var height = transform.localScale.y * (mesh.bounds.extents.y * 2f);
// Local position (scale included).
var x = (float)(u - monitor.width / 2) / monitor.width;
var y = -(float)(v - monitor.height / 2) / monitor.height;
var localPos = new Vector3(width * x, height * y, 0f);
// Bending
if (bend) {
var angle = localPos.x / radius;
if (meshForwardDirection == MeshForwardDirection.Y) {
localPos.y -= radius * (1f - Mathf.Cos(angle));
} else {
localPos.z -= radius * (1f - Mathf.Cos(angle));
}
localPos.x = radius * Mathf.Sin(angle);
}
// To world position
return transform.position + (transform.rotation * localPos);
}
}
}

View 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();
}
}
}

View File

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

View File

@@ -2,6 +2,7 @@
#define UDD_COMMON_CGINC
#include "UnityCG.cginc"
#include "./uDD_Params.cginc"
struct appdata
{
@@ -20,7 +21,7 @@ struct Input
float2 uv_MainTex;
};
float2 uddInvertUV(float2 uv)
inline float2 uddInvertUV(float2 uv)
{
#ifdef INVERT_X
uv.x = 1.0 - uv.x;
@@ -31,7 +32,7 @@ float2 uddInvertUV(float2 uv)
return uv;
}
float2 uddRotateUV(float2 uv)
inline float2 uddRotateUV(float2 uv)
{
#ifdef ROTATE90
float2 tmp = uv;
@@ -48,6 +49,13 @@ float2 uddRotateUV(float2 uv)
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()) {
@@ -55,30 +63,69 @@ inline void uddConvertToLinearIfNeeded(inout fixed3 rgb)
}
}
inline fixed4 uddGetScreenTexture(sampler2D tex, float2 uv)
{
fixed4 c = tex2D(tex, uv);
return c;
}
inline fixed4 uddGetCursorTexture(sampler2D tex, float2 uv, fixed4 cursorPosScale)
{
uv.x = (uv.x - cursorPosScale.x) / cursorPosScale.z;
uv.y = (uv.y - cursorPosScale.y) / cursorPosScale.w;
fixed4 c = tex2D(tex, uv);
fixed a = c.a * step(0, uv.x) * step(0, uv.y) * step(uv.x, 1) * step(uv.y, 1);
c *= step(0.01, a);
return c;
}
inline fixed4 uddGetScreenTextureWithCursor(sampler2D screenTex, sampler2D cursorTex, float2 uv, fixed4 cursorPosScale)
inline fixed4 uddGetTexture(sampler2D tex, float2 uv)
{
uv = uddInvertUV(uv);
fixed4 screen = uddGetScreenTexture(screenTex, uddRotateUV(uv));
fixed4 cursor = uddGetCursorTexture(cursorTex, uv, cursorPosScale);
fixed4 color = lerp(screen, cursor, cursor.a);
uddConvertToLinearIfNeeded(color.rgb);
return color;
#ifdef USE_CLIP
uv = uddClipUV(uv);
#endif
fixed4 c = tex2D(tex, uddRotateUV(uv));
uddConvertToLinearIfNeeded(c.rgb);
return c;
}
inline fixed4 uddGetScreenTexture(float2 uv)
{
return uddGetTexture(_MainTex, uv);
}
inline void uddBendVertex(inout float3 v, half radius, half width, half thickness)
{
#ifdef BEND_ON
half angle = width * v.x / radius;
#ifdef _FORWARD_Z
v.z *= thickness;
radius += v.z;
v.z -= radius * (1 - cos(angle));
#elif _FORWARD_Y
v.y *= thickness;
radius += v.y;
v.y += radius * (1 - cos(angle));
#endif
v.x = radius * sin(angle) / width;
#else
#ifdef _FORWARD_Z
v.y *= thickness;
#elif _FORWARD_Y
v.z *= thickness;
#endif
#endif
}
inline float3 uddRotateY(float3 n, float angle)
{
float c = cos(angle);
float s = sin(angle);
return float3(c * n.x - s * n.z, n.y, s * n.x + c * n.z);
}
inline float3 uddRotateX(float3 n, float angle)
{
float c = cos(angle);
float s = sin(angle);
return float3(n.x, c * n.y + s * n.z, -s * n.y + c * n.z);
}
inline void uddBendNormal(float4 x, inout float3 n, half radius, half width)
{
#ifdef BEND_ON
half angle = width * x / radius;
#ifdef _FORWARD_Z
n = uddRotateY(n, -angle);
#elif _FORWARD_Y
n = uddRotateX(n, -angle);
#endif
#endif
}
#endif

View File

@@ -4,7 +4,18 @@
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;

View File

@@ -22,19 +22,19 @@ SubShader
#pragma surface surf Standard fullforwardshadows
#pragma multi_compile ___ INVERT_X
#pragma multi_compile ___ INVERT_Y
#pragma multi_compile ___ VERTICAL
#pragma multi_compile ___ ROTATE90 ROTATE180 ROTATE270
#pragma multi_compile ___ USE_BEND
#pragma multi_compile ___ USE_CLIP
#define SURFACE_SHADER
#include "./uDD_Common.cginc"
#include "./uDD_Params.cginc"
half _Glossiness;
half _Metallic;
void surf(Input IN, inout SurfaceOutputStandard o)
{
fixed4 c = uddGetScreenTextureWithCursor(_MainTex, _CursorTex, IN.uv_MainTex, _CursorPositionScale) * _Color;
fixed4 c = uddGetScreenTexture(IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;

View File

@@ -5,8 +5,11 @@ Properties
{
_Color ("Color", Color) = (1, 1, 1, 1)
_MainTex ("Texture", 2D) = "white" {}
[Toggle(USE_BEND)] _UseBend("UseBend", Float) = 0
[PowerSlider(10.0)]_Radius ("Radius", Range(10, 100)) = 30
[KeywordEnum(Y, Z)] _Forward("Mesh Forward Direction", Int) = 0
[Toggle(BEND_ON)] _Bend("Use Bend", Int) = 0
[PowerSlider(10.0)] _Radius("Bend Radius", Range(1, 100)) = 30
[PowerSlider(10.0)] _Thickness("Thickness", Range(0.01, 10)) = 1
_Width("Width", Range(0.0, 10.0)) = 1.0
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
}
@@ -20,18 +23,15 @@ Cull [_Cull]
CGINCLUDE
#include "./uDD_Common.cginc"
#include "./uDD_Params.cginc"
fixed _Radius;
half _Radius;
half _Width;
half _Thickness;
v2f vert(appdata v)
{
v2f o;
#ifdef USE_BEND
half a = v.vertex.x / _Radius;
v.vertex.x = _Radius * sin(a);
v.vertex.y += _Radius * (1 - cos(a));
#endif
uddBendVertex(v.vertex.xyz, _Radius, _Width, _Thickness);
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
@@ -39,7 +39,7 @@ v2f vert(appdata v)
fixed4 frag(v2f i) : SV_Target
{
return uddGetScreenTextureWithCursor(_MainTex, _CursorTex, i.uv, _CursorPositionScale);
return uddGetScreenTexture(i.uv);
}
ENDCG
@@ -51,9 +51,10 @@ Pass
#pragma fragment frag
#pragma multi_compile ___ INVERT_X
#pragma multi_compile ___ INVERT_Y
#pragma multi_compile ___ VERTICAL
#pragma shader_feature ___ USE_BEND
#pragma multi_compile ___ ROTATE90 ROTATE180 ROTATE270
#pragma multi_compile ___ USE_CLIP
#pragma multi_compile ___ BEND_ON
#pragma multi_compile _FORWARD_Y _FORWARD_Z
ENDCG
}

View File

@@ -6,6 +6,11 @@ Properties
_Color ("Color", Color) = (1, 1, 1, 1)
_MainTex ("Texture", 2D) = "white" {}
_Mask ("Mask", Range(0, 1)) = 0.1
[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
_Width("Width", Range(0.0, 10.0)) = 1.0
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
}
@@ -21,13 +26,16 @@ Blend SrcAlpha OneMinusSrcAlpha
CGINCLUDE
#include "./uDD_Common.cginc"
#include "./uDD_Params.cginc"
fixed _Mask;
half _Radius;
half _Width;
half _Thickness;
v2f vert(appdata v)
{
v2f o;
uddBendVertex(v.vertex.xyz, _Radius, _Width, _Thickness);
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
@@ -35,7 +43,7 @@ v2f vert(appdata v)
fixed4 frag(v2f i) : SV_Target
{
fixed4 tex = uddGetScreenTextureWithCursor(_MainTex, _CursorTex, i.uv, _CursorPositionScale);
fixed4 tex = uddGetScreenTexture(i.uv);
fixed alpha = pow((tex.r + tex.g + tex.b) / 3.0, _Mask);
return fixed4(tex.rgb * _Color.rgb, alpha * _Color.a);
}
@@ -49,8 +57,10 @@ Pass
#pragma fragment frag
#pragma multi_compile ___ INVERT_X
#pragma multi_compile ___ INVERT_Y
#pragma multi_compile ___ VERTICAL
#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,178 @@
Shader "uDesktopDuplication/Unlit_Displacement"
{
Properties
{
_Color ("Color", Color) = (1, 1, 1, 1)
_MainTex ("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
_Width ("Width", Range(0.0, 10.0)) = 1.92
[PowerSlider(10.0)] _Thickness("Thickness", Range(0.01, 10)) = 1
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
_DispTex ("Displacement Map", 2D) = "black" {}
_DispFactor("Displacement Factor", Range(0, 5.0)) = 1
_TessMinDist("Tessellation Min Distance", Range(0.1, 100.0)) = 1.0
_TessMaxDist("Tessellation Max Distance", Range(0.1, 100.0)) = 5.0
_TessFactor("Tessellation Factor", Range(0.1, 50.0)) = 10.0
}
SubShader
{
Tags { "RenderType"="Opaque" }
Cull [_Cull]
CGINCLUDE
#include "./uDD_Common.cginc"
#include "Tessellation.cginc"
half _Radius;
half _Width;
half _Thickness;
Texture2D _DispTex;
SamplerState sampler_DispTex;
half _DispFactor;
half _TessMinDist;
half _TessMaxDist;
half _TessFactor;
struct VsInput
{
float3 vertex : POSITION;
float3 normal : NORMAL;
float2 texcoord : TEXCOORD0;
};
struct HsInput
{
float4 f4Position : POS;
float3 f3Normal : NORMAL;
float2 f2TexCoord : TEXCOORD;
};
struct HsControlPointOutput
{
float3 f3Position : POS;
float3 f3Normal : NORMAL;
float2 f2TexCoord : TEXCOORD;
};
struct HsConstantOutput
{
float fTessFactor[3] : SV_TessFactor;
float fInsideTessFactor : SV_InsideTessFactor;
};
struct DsOutput
{
float4 f4Position : SV_Position;
float2 f2TexCoord : TEXCOORD0;
};
HsInput vert(VsInput i)
{
HsInput o;
o.f4Position = float4(i.vertex, 1.0);
o.f3Normal = i.normal;
o.f2TexCoord = i.texcoord;
return o;
}
[domain("tri")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[patchconstantfunc("hullConst")]
[outputcontrolpoints(3)]
HsControlPointOutput hull(InputPatch<HsInput, 3> i, uint id : SV_OutputControlPointID)
{
HsControlPointOutput o = (HsControlPointOutput)0;
o.f3Position = i[id].f4Position.xyz;
o.f3Normal = i[id].f3Normal;
o.f2TexCoord = i[id].f2TexCoord;
return o;
}
HsConstantOutput hullConst(InputPatch<HsInput, 3> i)
{
HsConstantOutput o = (HsConstantOutput)0;
float4 p0 = i[0].f4Position;
float4 p1 = i[1].f4Position;
float4 p2 = i[2].f4Position;
float4 tessFactor = UnityDistanceBasedTess(p0, p1, p2, _TessMinDist, _TessMaxDist, _TessFactor);
o.fTessFactor[0] = tessFactor.x;
o.fTessFactor[1] = tessFactor.y;
o.fTessFactor[2] = tessFactor.z;
o.fInsideTessFactor = tessFactor.w;
return o;
}
[domain("tri")]
DsOutput domain(
HsConstantOutput hsConst,
const OutputPatch<HsControlPointOutput, 3> i,
float3 bary : SV_DomainLocation)
{
DsOutput o = (DsOutput)0;
float3 f3Position =
bary.x * i[0].f3Position +
bary.y * i[1].f3Position +
bary.z * i[2].f3Position;
float3 f3Normal = normalize(
bary.x * i[0].f3Normal +
bary.y * i[1].f3Normal +
bary.z * i[2].f3Normal);
o.f2TexCoord =
bary.x * i[0].f2TexCoord +
bary.y * i[1].f2TexCoord +
bary.z * i[2].f2TexCoord;
uddBendNormal(f3Position.x, f3Normal, _Radius, _Width);
uddBendVertex(f3Position, _Radius, _Width, _Thickness);
float disp = length(_DispTex.SampleLevel(sampler_DispTex, o.f2TexCoord, 0)) * _DispFactor;
f3Position.xyz += f3Normal * disp;
o.f4Position = mul(UNITY_MATRIX_MVP, float4(f3Position.xyz, 1.0));
return o;
}
fixed4 frag(DsOutput i) : SV_Target
{
return uddGetScreenTexture(i.f2TexCoord);
}
ENDCG
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma hull hull
#pragma domain domain
#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
}
}
Fallback "Unlit/Texture"
}

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 05df46eb51b13c84eabbdf4c53cc8db7
timeCreated: 1480072905
licenseType: Pro
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -5,6 +5,11 @@ Properties
{
_Color ("Color", Color) = (1, 1, 1, 1)
_MainTex ("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
_Width("Width", Range(0.0, 10.0)) = 1.0
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
}
@@ -20,11 +25,15 @@ Blend SrcAlpha OneMinusSrcAlpha
CGINCLUDE
#include "./uDD_Common.cginc"
#include "./uDD_Params.cginc"
half _Radius;
half _Width;
half _Thickness;
v2f vert(appdata v)
{
v2f o;
uddBendVertex(v.vertex.xyz, _Radius, _Width, _Thickness);
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
@@ -32,7 +41,7 @@ v2f vert(appdata v)
fixed4 frag(v2f i) : SV_Target
{
return fixed4(uddGetScreenTextureWithCursor(_MainTex, _CursorTex, i.uv, _CursorPositionScale).rgb, 1.0) * _Color;
return fixed4(uddGetScreenTexture(i.uv).rgb, 1.0) * _Color;
}
ENDCG
@@ -44,8 +53,10 @@ Pass
#pragma fragment frag
#pragma multi_compile ___ INVERT_X
#pragma multi_compile ___ INVERT_Y
#pragma multi_compile ___ VERTICAL
#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 <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);
}

View File

@@ -1,15 +1,109 @@
#pragma once
#include <memory>
#include "IUnityInterface.h"
#include <wrl/client.h>
class MonitorManager;
// Unity interface and ID3D11Device getters
struct IUnityInterfaces;
IUnityInterfaces* GetUnity();
ID3D11Device* GetDevice();
struct ID3D11Device;
Microsoft::WRL::ComPtr<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,
TextureSizeChanged = 1,
};
void SendMessageToUnity(Message message);
void SendMessageToUnity(Message message);
// Buffer
template <class T>
class Buffer
{
public:
Buffer() {}
~Buffer() {}
void ExpandIfNeeded(UINT size)
{
if (size > size_)
{
size_ = size;
value_ = std::make_unique<T[]>(size);
}
}
void Reset()
{
value_.reset();
size_ = 0;
}
UINT Size() const
{
return size_;
}
T* Get() const
{
return value_.get();
}
T* Get(UINT offset) const
{
return (value_.get() + offset);
}
template <class U>
U* As() const
{
return reinterpret_cast<U*>(Get());
}
template <class U>
U* As(UINT offset) const
{
return reinterpret_cast<U*>(Get(offset));
}
operator bool() const
{
return value_ != nullptr;
}
const T operator [](UINT index) const
{
if (index >= size_)
{
Debug::Error("Array index out of range: ", index, size_);
return T(0);
}
return value_[index];
}
T& operator [](UINT index)
{
if (index >= size_)
{
Debug::Error("Array index out of range: ", index, size_);
return value_[0];
}
return value_[index];
}
private:
std::unique_ptr<T[]> value_;
UINT size_ = 0;
};

View File

@@ -1,227 +1,407 @@
#include <d3d11.h>
#include "Common.h"
#include <wrl/client.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()
{
}
Cursor::~Cursor()
{
if (apiBuffer_ != nullptr)
{
delete[] apiBuffer_;
apiBuffer_ = nullptr;
}
if (bgra32Buffer_ != nullptr)
{
delete[] bgra32Buffer_;
bgra32Buffer_ = nullptr;
}
}
void Cursor::UpdateBuffer(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
void Cursor::UpdateBuffer(Monitor* monitor, 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;
if (frameInfo.PointerPosition.Visible != 0) {
GetMonitorManager()->SetCursorMonitorId(monitor_->GetId());
timestamp_ = frameInfo.LastMouseUpdateTime;
isVisible_ = frameInfo.PointerPosition.Visible != 0;
}
timestamp_ = frameInfo.LastMouseUpdateTime;
if (frameInfo.PointerShapeBufferSize == 0)
{
return;
}
// Increase the buffer size if needed
if (frameInfo.PointerShapeBufferSize > apiBufferSize_)
buffer_.ExpandIfNeeded(frameInfo.PointerShapeBufferSize);
if (!buffer_)
{
if (apiBuffer_) delete[] apiBuffer_;
apiBuffer_ = new BYTE[frameInfo.PointerShapeBufferSize];
apiBufferSize_ = frameInfo.PointerShapeBufferSize;
return;
}
if (apiBuffer_ == nullptr) return;
// Get mouse pointer information
UINT bufferSize;
const auto hr = monitor_->GetDeskDupl()->GetFramePointerShape(
frameInfo.PointerShapeBufferSize,
reinterpret_cast<void*>(apiBuffer_),
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo;
const auto hr = monitor->GetDeskDupl()->GetFramePointerShape(
buffer_.Size(),
buffer_.Get(),
&bufferSize,
&shapeInfo_);
&shapeInfo);
if (FAILED(hr))
{
delete[] apiBuffer_;
apiBuffer_ = nullptr;
apiBufferSize_ = 0;
Debug::Error("Cursor::UpdateBuffer() => GetFramePointerShape() failed.");
buffer_.Reset();
return;
}
shapeInfo_ = shapeInfo;
}
void Cursor::UpdateTexture()
void Cursor::Draw(Monitor* monitor)
{
// cursor type
const bool isMono = GetType() == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME;
const bool isColorMask = GetType() == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR;
// Check desktop texure
if (monitor->GetUnityTexture() == nullptr)
{
Debug::Error("Cursor::UpdateTexture() => Monitor::GetUnityTexture() is null.");
return;
}
// Size
const auto w = GetWidth();
const auto h = GetHeight();
const auto p = GetPitch();
// Cursor information
const auto cursorImageWidth = GetWidth();
const auto cursorImageHeight = GetHeight();
const auto cursorImagePitch = GetPitch();
// Monitor orientation
const auto monitorRot = static_cast<DXGI_MODE_ROTATION>(monitor->GetRotation());
const auto isMonitorPortrait =
monitorRot == DXGI_MODE_ROTATION_ROTATE90 ||
monitorRot == DXGI_MODE_ROTATION_ROTATE270;
// Captured size (desktop cooridinates).
auto capturedImageWidth = !isMonitorPortrait ? cursorImageWidth : cursorImageHeight;
auto capturedImageHeight = !isMonitorPortrait ? cursorImageHeight : cursorImageWidth;
// Convert the buffer given by API into BGRA32
const UINT bgraBufferSize = w * h * 4;
if (bgraBufferSize > bgra32BufferSize_)
const UINT bgraBufferSize = cursorImageWidth * cursorImageHeight * 4;
bgraBuffer_.ExpandIfNeeded(bgraBufferSize);
// Check buffers
if (!bgraBuffer_ || !buffer_)
{
if (bgra32Buffer_) delete[] bgra32Buffer_;
bgra32Buffer_ = new BYTE[bgraBufferSize];
bgra32BufferSize_ = bgraBufferSize;
return;
}
if (bgra32Buffer_ == nullptr) return;
// If masked, copy the desktop image and merge it with masked image.
if (isMono || isColorMask)
// Desktop size
const int monitorWidth = monitor->GetWidth();
const int monitorHeight = monitor->GetHeight();
const int desktopImageWidth = !isMonitorPortrait ? monitorWidth : monitorHeight;
const int desktopImageHeight = !isMonitorPortrait ? monitorHeight : monitorWidth;
// x_, y_ are cooridinates in rotated monitor.
// desktopX, desktopY are coordinates in captured desktop image (always landscape).
int desktopX, desktopY;
switch (monitorRot)
{
HRESULT hr;
case DXGI_MODE_ROTATION_ROTATE90:
{
desktopX = y_;
desktopY = (desktopImageHeight - 1) - x_ - cursorImageWidth;
break;
}
case DXGI_MODE_ROTATION_ROTATE180:
{
desktopX = (desktopImageWidth - 1) - x_ - cursorImageWidth;
desktopY = (desktopImageHeight - 1) - y_ - cursorImageHeight;
break;
}
case DXGI_MODE_ROTATION_ROTATE270:
{
desktopX = (desktopImageWidth - 1) - y_ - cursorImageHeight;
desktopY = x_;
break;
}
case DXGI_MODE_ROTATION_IDENTITY:
case DXGI_MODE_ROTATION_UNSPECIFIED:
default:
{
desktopX = x_;
desktopY = y_;
break;
}
}
// Calculate information to capture desktop image under cursor.
int cursorOffsetX = 0;
int cursorOffsetY = 0;
int capturedImageLeft = desktopX;
int capturedImageTop = desktopY;
int capturedImageRight = desktopX + capturedImageWidth;
int capturedImageBottom = desktopY + capturedImageHeight;
if (capturedImageLeft < 0)
{
capturedImageWidth -= -desktopX;
cursorOffsetX = -desktopX;
capturedImageLeft = 0;
}
if (capturedImageRight >= desktopImageWidth)
{
capturedImageWidth -= capturedImageRight - desktopImageWidth;
capturedImageRight = desktopImageWidth - 1;
}
if (capturedImageTop < 0)
{
capturedImageHeight -= -desktopY;
cursorOffsetY = -desktopY;
capturedImageTop = 0;
}
if (capturedImageBottom >= desktopImageHeight)
{
capturedImageHeight -= capturedImageBottom - desktopImageHeight;
capturedImageBottom = desktopImageHeight - 1;
}
// Check if box is inner desktop area
if (capturedImageLeft < 0 ||
capturedImageTop < 0 ||
capturedImageRight >= desktopImageWidth ||
capturedImageBottom >= desktopImageHeight)
{
Debug::Error("Cursor::UpdateTexture() => box is out of area.");
Debug::Error(
" ",
"(", capturedImageLeft, ", ", capturedImageTop, ")",
" ~ (", capturedImageRight, ", ", capturedImageBottom, ") > ",
"(", desktopImageWidth, ", ", desktopImageHeight, ")");
return;
}
if (capturedImageWidth == 0 || capturedImageHeight == 0)
{
return;
}
D3D11_BOX capturedImageArea
{
static_cast<UINT>(capturedImageLeft),
static_cast<UINT>(capturedImageTop),
0,
static_cast<UINT>(capturedImageRight),
static_cast<UINT>(capturedImageBottom),
1
};
// Create texture for capturing desktop image
ComPtr<ID3D11Texture2D> texture;
{
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.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;
desc.Usage = D3D11_USAGE_STAGING;
desc.BindFlags = 0;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.MiscFlags = 0;
ID3D11Texture2D* texture = nullptr;
hr = GetDevice()->CreateTexture2D(&desc, nullptr, &texture);
if (FAILED(hr))
if (FAILED(GetDevice()->CreateTexture2D(&desc, nullptr, &texture)))
{
Debug::Error("Cursor::UpdateTexture() => GetDevice()->CreateTexture2D() failed.");
return;
}
}
D3D11_BOX box;
box.front = 0;
box.back = 1;
box.left = x_;
box.top = y_;
box.right = x_ + w;
box.bottom = y_ + h;
ID3D11DeviceContext* context;
// Copy desktop image to the texture
{
ComPtr<ID3D11DeviceContext> context;
GetDevice()->GetImmediateContext(&context);
context->CopySubresourceRegion(texture, 0, 0, 0, 0, monitor_->GetUnityTexture(), 0, &box);
context->Release();
context->CopySubresourceRegion(texture.Get(), 0, 0, 0, 0, monitor->GetUnityTexture(), 0, &capturedImageArea);
}
IDXGISurface* surface = nullptr;
hr = texture->QueryInterface(__uuidof(IDXGISurface), (void**)&surface);
texture->Release();
if (FAILED(hr))
// Get mapped surface to access pixels in CPU
ComPtr<IDXGISurface> surface;
if (FAILED(texture.As(&surface)))
{
Debug::Error("Cursor::UpdateTexture() => texture.As() 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 desktop texture under the mouse cursor.
const auto desktop32 = reinterpret_cast<UINT*>(mappedSurface.pBits);
const UINT desktopPitch = mappedSurface.Pitch / sizeof(UINT);
// Rotate cursor image to match the monitor orientation
Buffer<BYTE> rotatedBuffer_;
rotatedBuffer_.ExpandIfNeeded(buffer_.Size());
for (int y = 0; y < capturedImageHeight; ++y)
{
for (int x = 0; x < capturedImageWidth; ++x)
{
return;
}
// Cursor coordinates
int cursorX, cursorY;
DXGI_MAPPED_RECT mappedSurface;
hr = surface->Map(&mappedSurface, DXGI_MAP_READ);
if (FAILED(hr))
{
surface->Release();
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_);
if (isMono)
{
for (int row = 0; row < h; ++row)
switch (monitorRot)
{
BYTE mask = 0x80;
for (int col = 0; col < w; ++col)
case DXGI_MODE_ROTATION_ROTATE90:
{
const int i = row * w + 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 : 0xFF000000;
const UINT xorMask32 = xorMask ? 0x00FFFFFF : 0x00000000;
output32[i] = (desktop32[row * desktopPitch + col] & andMask32) ^ xorMask32;
mask = (mask == 0x01) ? 0x80 : (mask >> 1);
cursorX = (cursorImageWidth - 1) - (y + cursorOffsetY);
cursorY = (x + cursorOffsetX);
break;
}
case DXGI_MODE_ROTATION_ROTATE180:
{
cursorX = (cursorImageWidth - 1) - (x + cursorOffsetX);
cursorY = (cursorImageHeight - 1) - (y + cursorOffsetY);
break;
}
case DXGI_MODE_ROTATION_ROTATE270:
{
cursorX = (y + cursorOffsetY);
cursorY = (cursorImageHeight - 1) - (x + cursorOffsetX);
break;
}
case DXGI_MODE_ROTATION_IDENTITY:
case DXGI_MODE_ROTATION_UNSPECIFIED:
default:
{
cursorX = (x + cursorOffsetX);
cursorY = (y + cursorOffsetY);
break;
}
}
}
else // DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR
{
const auto buffer32 = reinterpret_cast<UINT*>(apiBuffer_);
for (int row = 0; row < h; ++row)
const auto outputIndex = y * capturedImageWidth + x;
const auto desktopIndex = y * desktopPitch + x;
const auto cursorIndex = cursorY * cursorImageWidth + cursorX;
const auto buffer32 = buffer_.As<UINT>();
auto output32 = bgraBuffer_.As<UINT>();
switch (GetType())
{
for (int col = 0; col < w; ++col)
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME:
{
const int i = row * w + col;
const int j = row * p / sizeof(UINT) + col;
UINT mask = 0xFF000000 & buffer32[j];
BYTE mask = 0b10000000 >> (cursorX % 8);
const BYTE andMask = buffer_[cursorX / 8 + cursorY * cursorImagePitch] & mask;
const BYTE xorMask = buffer_[cursorX / 8 + (cursorY + cursorImageHeight) * cursorImagePitch] & mask;
const UINT andMask32 = andMask ? 0xFFFFFFFF : 0x00000000;
const UINT xorMask32 = xorMask ? 0xFFFFFFFF : 0x00000000;
output32[outputIndex] = (desktop32[desktopIndex] & andMask32) ^ xorMask32;
break;
}
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR:
{
UINT mask = 0xFF000000 & buffer32[cursorIndex];
if (mask)
{
output32[i] = (desktop32[row * desktopPitch + col] ^ buffer32[j]) | 0xFF000000;
output32[outputIndex] = (desktop32[desktopIndex] ^ buffer32[cursorIndex]) | 0xFF000000;
}
else
{
output32[i] = buffer32[j] | 0xFF000000;
output32[outputIndex] = buffer32[cursorIndex] | 0xFF000000;
}
break;
}
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR:
{
const auto desktop = reinterpret_cast<BYTE*>(&desktop32[desktopIndex]);
const auto desktopB = desktop[0];
const auto desktopG = desktop[1];
const auto desktopR = desktop[2];
const auto desktopA = desktop[3];
const auto cursor = buffer_.Get(cursorIndex * 4);
const auto cursorB = cursor[0];
const auto cursorG = cursor[1];
const auto cursorR = cursor[2];
const auto cursorA = cursor[3];
const auto a0 = cursorA / 255.f;
const auto a1 = 1.f - a0;
auto output = reinterpret_cast<BYTE*>(&output32[outputIndex]);
output[0] = static_cast<BYTE>(cursorB * a0 + desktopB * a1);
output[1] = static_cast<BYTE>(cursorG * a0 + desktopG * a1);
output[2] = static_cast<BYTE>(cursorR * a0 + desktopR * a1);
output[3] = desktopA;
break;
}
default:
{
Debug::Error("Cursor::UpdateTexture() => Unknown cursor type");
return;
}
}
}
hr = surface->Unmap();
surface->Release();
if (FAILED(hr))
{
return;
}
}
else // DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR
{
auto output32 = reinterpret_cast<UINT*>(bgra32Buffer_);
const auto buffer32 = reinterpret_cast<UINT*>(apiBuffer_);
for (int i = 0; i < w * h; ++i)
{
output32[i] = buffer32[i];
}
ComPtr<ID3D11DeviceContext> context;
GetDevice()->GetImmediateContext(&context);
context->UpdateSubresource(monitor->GetUnityTexture(), 0, &capturedImageArea, bgraBuffer_.Get(), capturedImageWidth * 4, 0);
}
return;
if (FAILED(surface->Unmap()))
{
Debug::Error("Cursor::UpdateTexture() => surface->Unmap() failed.");
return;
}
}
void Cursor::GetTexture(ID3D11Texture2D* texture)
{
if (bgra32Buffer_ == nullptr) return;
ID3D11DeviceContext* context;
if (!bgraBuffer_)
{
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_, shapeInfo_.Width * 4, 0);
context->Release();
context->UpdateSubresource(texture, 0, nullptr, bgraBuffer_.Get(), GetWidth() * 4, 0);
}
@@ -266,4 +446,4 @@ int Cursor::GetPitch() const
int Cursor::GetType() const
{
return shapeInfo_.Type;
}
}

View File

@@ -1,16 +1,19 @@
#pragma once
#include <d3d11.h>
#include <dxgi1_2.h>
#include <memory>
#include "Common.h"
class Monitor;
class Cursor
{
public:
explicit Cursor(Monitor* monitor);
explicit Cursor();
~Cursor();
void UpdateBuffer(const DXGI_OUTDUPL_FRAME_INFO& frameInfo);
void UpdateTexture();
void UpdateBuffer(Monitor* monitor, const DXGI_OUTDUPL_FRAME_INFO& frameInfo);
void Draw(Monitor* monitor);
void GetTexture(ID3D11Texture2D* texture);
bool IsVisible() const;
@@ -22,14 +25,11 @@ public:
int GetType() const;
private:
Monitor* monitor_;
bool isVisible_ = false;
int x_ = -1;
int y_ = -1;
BYTE* apiBuffer_ = nullptr;
UINT apiBufferSize_ = 0;
BYTE* bgra32Buffer_ = nullptr;
UINT bgra32BufferSize_ = 0;
Buffer<BYTE> buffer_;
Buffer<BYTE> bgraBuffer_;
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo_;
LARGE_INTEGER timestamp_;
};

View File

@@ -0,0 +1,40 @@
#pragma once
#include <cstdio>
#include "Debug.h"
decltype(Debug::isInitialized_) Debug::isInitialized_ = false;
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 (isInitialized_) return;
isInitialized_ = true;
if (mode_ == Mode::File)
{
fs_.open("uDesktopDuplication.log");
Debug::Log("Start");
}
}
void Debug::Finalize()
{
if (!isInitialized_) return;
isInitialized_ = false;
if (mode_ == Mode::File)
{
Debug::Log("Stop");
fs_.close();
}
Debug::SetLogFunc(nullptr);
Debug::SetErrorFunc(nullptr);
}

View File

@@ -0,0 +1,132 @@
#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 :
if (logFunc_) logFunc_(ss_.str().c_str());
break;
case Level::Error :
if (errFunc_) 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 bool isInitialized_;
static Mode mode_;
static std::ofstream fs_;
static std::ostringstream ss_;
static DebugLogFuncPtr logFunc_;
static DebugLogFuncPtr errFunc_;
};

View File

@@ -1,117 +1,348 @@
#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))
{
}
HRESULT Monitor::Initialize(IDXGIOutput* output)
{
output->GetDesc(&outputDesc_);
monitorInfo_.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(outputDesc_.Monitor, &monitorInfo_);
GetDpiForMonitor(outputDesc_.Monitor, MDT_RAW_DPI, &dpiX_, &dpiY_);
auto output1 = reinterpret_cast<IDXGIOutput1*>(output);
const auto hr = output1->DuplicateOutput(GetDevice(), &deskDupl_);
// TODO: error check
switch (hr)
{
case S_OK:
state_ = State::Available;
break;
case E_INVALIDARG:
state_ = State::InvalidArg;
break;
case E_ACCESSDENIED:
// For example, when the user presses Ctrl + Alt + Delete and the screen
// switches to admin screen, this error occurs.
state_ = State::AccessDenied;
break;
case DXGI_ERROR_UNSUPPORTED:
// If the display adapter on the computer is running under the Microsoft Hybrid system,
// this error occurs.
state_ = State::Unsupported;
break;
case DXGI_ERROR_NOT_CURRENTLY_AVAILABLE:
// When other application use Desktop Duplication API, this error occurs.
state_ = State::CurrentlyNotAvailable;
break;
case DXGI_ERROR_SESSION_DISCONNECTED:
state_ = State::SessionDisconnected;
break;
}
return hr;
}
Monitor::~Monitor()
{
if (deskDupl_ != nullptr)
if (deskDupl_)
{
deskDupl_->Release();
deskDupl_ = nullptr;
}
}
HRESULT Monitor::Render(UINT timeout)
void Monitor::Initialize(IDXGIOutput* output)
{
if (deskDupl_ == nullptr)
if (FAILED(output->GetDesc(&outputDesc_)))
{
return S_OK;
Debug::Error("Monitor::Initialize() => IDXGIOutput::GetDesc() failed.");
return;
}
if (unityTexture_ == nullptr) return 0;
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;
}
IDXGIResource* resource = nullptr;
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;
const auto rot = static_cast<DXGI_MODE_ROTATION>(GetRotation());
Debug::Log("Monitor::Initialize() => OK.");
Debug::Log(" ID : ", GetId());
Debug::Log(" Size : (", GetWidth(), ", ", GetHeight(), ")");
Debug::Log(" DPI : (", GetDpiX(), ", ", GetDpiY(), ")");
Debug::Log(" Rot : ",
rot == DXGI_MODE_ROTATION_IDENTITY ? "Landscape" :
rot == DXGI_MODE_ROTATION_ROTATE90 ? "Portrait" :
rot == DXGI_MODE_ROTATION_ROTATE180 ? "Landscape (flipped)" :
rot == DXGI_MODE_ROTATION_ROTATE270 ? "Portrait (flipped)" :
"Unspecified");
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;
HRESULT hr;
ComPtr<IDXGIResource> resource;
DXGI_OUTDUPL_FRAME_INFO frameInfo;
const auto hr = deskDupl_->AcquireNextFrame(timeout, &frameInfo, &resource);
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 hr;
return;
}
ID3D11Texture2D* texture;
resource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&texture));
if (FAILED(resource.CopyTo(&texture)))
{
Debug::Error("Monitor::Render() => resource.As() failed.");
return;
}
ID3D11DeviceContext* context;
GetDevice()->GetImmediateContext(&context);
context->CopyResource(unityTexture_, texture);
context->Release();
// Get texture
if (unityTexture_)
{
D3D11_TEXTURE2D_DESC srcDesc, dstDesc;
texture->GetDesc(&srcDesc);
unityTexture_->GetDesc(&dstDesc);
if (srcDesc.Width != dstDesc.Width ||
srcDesc.Height != dstDesc.Height)
{
Debug::Error("Monitor::Render() => Texture sizes are defferent.");
Debug::Error(" Source : (", srcDesc.Width, ", ", srcDesc.Height, ")");
Debug::Error(" Dest : (", dstDesc.Width, ", ", dstDesc.Height, ")");
//Debug::Log(" => Try modifying width/height using reported value from DDA.");
//width_ = srcDesc.Width;
//height_ = srcDesc.Height;
state_ = MonitorState::TextureSizeInconsistent;
//SendMessageToUnity(Message::TextureSizeChanged);
}
else
{
ComPtr<ID3D11DeviceContext> context;
GetDevice()->GetImmediateContext(&context);
context->CopyResource(unityTexture_, texture);
}
}
cursor_->UpdateBuffer(frameInfo);
cursor_->UpdateTexture();
UpdateMetadata(frameInfo);
resource->Release();
deskDupl_->ReleaseFrame();
if (frameInfo.PointerPosition.Visible)
{
GetMonitorManager()->SetCursorMonitorId(id_);
}
return S_OK;
if (GetMonitorManager()->GetCursorMonitorId() == id_)
{
UpdateCursor(frameInfo);
}
if (UseGetPixels())
{
CopyTextureFromGpuToCpu(texture);
}
hr = deskDupl_->ReleaseFrame();
if (FAILED(hr))
{
switch (hr)
{
case DXGI_ERROR_ACCESS_LOST:
{
Debug::Log("Monitor::Render() => DXGI_ERROR_ACCESS_LOST.");
state_ = State::AccessLost;
break;
}
case DXGI_ERROR_INVALID_CALL:
{
Debug::Error("Monitor::Render() => DXGI_ERROR_INVALID_CALL.");
break;
}
default:
{
state_ = State::Unknown;
Debug::Error("Monitor::Render() => Unknown Error.");
break;
}
}
return;
}
hasBeenUpdated_ = true;
}
void Monitor::UpdateCursor(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
{
auto cursor_ = GetMonitorManager()->GetCursor();
cursor_->UpdateBuffer(this, frameInfo);
cursor_->Draw(this);
}
void Monitor::UpdateMetadata(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
{
metaData_.ExpandIfNeeded(frameInfo.TotalMetadataBufferSize);
UpdateMoveRects(frameInfo);
UpdateDirtyRects(frameInfo);
}
void Monitor::UpdateMoveRects(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
{
moveRectSize_ = metaData_.Size();
const auto hr = deskDupl_->GetFrameMoveRects(
moveRectSize_,
metaData_.As<DXGI_OUTDUPL_MOVE_RECT>(),
&moveRectSize_);
if (FAILED(hr))
{
switch (hr)
{
case DXGI_ERROR_ACCESS_LOST:
{
Debug::Log("Monitor::Render() => DXGI_ERROR_ACCESS_LOST (GetFrameMoveRects()).");
break;
}
case DXGI_ERROR_MORE_DATA:
{
Debug::Error("Monitor::Render() => DXGI_ERROR_MORE_DATA (GetFrameMoveRects()).");
break;
}
case DXGI_ERROR_INVALID_CALL:
{
Debug::Error("Monitor::Render() => DXGI_ERROR_INVALID_CALL (GetFrameMoveRects()).");
break;
}
case E_INVALIDARG:
{
Debug::Error("Monitor::Render() => E_INVALIDARG (GetFrameMoveRects()).");
break;
}
default:
{
Debug::Error("Monitor::Render() => Unknown Error (GetFrameMoveRects()).");
break;
}
}
return;
}
}
void Monitor::UpdateDirtyRects(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
{
dirtyRectSize_ = metaData_.Size() - moveRectSize_;
const auto hr = deskDupl_->GetFrameDirtyRects(
dirtyRectSize_,
metaData_.As<RECT>(moveRectSize_ /* offset */),
&dirtyRectSize_);
if (FAILED(hr))
{
switch (hr)
{
case DXGI_ERROR_ACCESS_LOST:
{
Debug::Log("Monitor::Render() => DXGI_ERROR_ACCESS_LOST (GetFrameDirtyRects()).");
break;
}
case DXGI_ERROR_MORE_DATA:
{
Debug::Error("Monitor::Render() => DXGI_ERROR_MORE_DATA (GetFrameDirtyRects()).");
break;
}
case DXGI_ERROR_INVALID_CALL:
{
Debug::Error("Monitor::Render() => DXGI_ERROR_INVALID_CALL (GetFrameDirtyRects()).");
break;
}
case E_INVALIDARG:
{
Debug::Error("Monitor::Render() => E_INVALIDARG (GetFrameDirtyRects()).");
break;
}
default:
{
Debug::Error("Monitor::Render() => Unknown Error (GetFrameDirtyRects()).");
break;
}
}
return;
}
}
@@ -145,18 +376,6 @@ IDXGIOutputDuplication* Monitor::GetDeskDupl()
}
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);
@@ -169,27 +388,33 @@ bool Monitor::IsPrimary() const
}
bool Monitor::HasBeenUpdated() const
{
return hasBeenUpdated_;
}
int Monitor::GetLeft() const
{
return outputDesc_.DesktopCoordinates.left;
return static_cast<int>(outputDesc_.DesktopCoordinates.left);
}
int Monitor::GetRight() const
{
return outputDesc_.DesktopCoordinates.right;
return static_cast<int>(outputDesc_.DesktopCoordinates.right);
}
int Monitor::GetTop() const
{
return outputDesc_.DesktopCoordinates.top;
return static_cast<int>(outputDesc_.DesktopCoordinates.top);
}
int Monitor::GetBottom() const
{
return outputDesc_.DesktopCoordinates.bottom;
return static_cast<int>(outputDesc_.DesktopCoordinates.bottom);
}
@@ -213,13 +438,235 @@ int Monitor::GetDpiY() const
int Monitor::GetWidth() const
{
const auto rect = monitorInfo_.rcMonitor;
return rect.right - rect.left;
return width_;
}
int Monitor::GetHeight() const
{
const auto rect = monitorInfo_.rcMonitor;
return rect.bottom - rect.top;
}
return height_;
}
int Monitor::GetMoveRectCount() const
{
return moveRectSize_ / sizeof(DXGI_OUTDUPL_MOVE_RECT);
}
DXGI_OUTDUPL_MOVE_RECT* Monitor::GetMoveRects() const
{
return metaData_.As<DXGI_OUTDUPL_MOVE_RECT>();
}
int Monitor::GetDirtyRectCount() const
{
return dirtyRectSize_ / sizeof(RECT);
}
RECT* Monitor::GetDirtyRects() const
{
return metaData_.As<RECT>(moveRectSize_);
}
void Monitor::UseGetPixels(bool use)
{
useGetPixels_ = use;
}
bool Monitor::UseGetPixels() const
{
return useGetPixels_;
}
void Monitor::CopyTextureFromGpuToCpu(ID3D11Texture2D* texture)
{
const auto monitorRot = static_cast<DXGI_MODE_ROTATION>(GetRotation());
const auto monitorWidth = GetWidth();
const auto monitorHeight = GetHeight();
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;
if (!textureForGetPixels_)
{
D3D11_TEXTURE2D_DESC desc;
desc.Width = desktopImageWidth;
desc.Height = desktopImageHeight;
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;
if (FAILED(GetDevice()->CreateTexture2D(&desc, nullptr, &textureForGetPixels_)))
{
Debug::Error("Monitor::CopyTextureFromGpuToCpu() => GetDevice()->CreateTexture2D() failed.");
return;
}
}
{
ComPtr<ID3D11DeviceContext> context;
GetDevice()->GetImmediateContext(&context);
context->CopyResource(textureForGetPixels_.Get(), texture);
}
ComPtr<IDXGISurface> surface;
if (FAILED(textureForGetPixels_.As(&surface)))
{
Debug::Error("Monitor::CopyTextureFromGpuToCpu() => texture.As() failed.");
return;
}
DXGI_MAPPED_RECT mappedSurface;
if (FAILED(surface->Map(&mappedSurface, DXGI_MAP_READ)))
{
Debug::Error("Monitor::CopyTextureFromGpuToCpu() => surface->Map() failed.");
return;
}
const UINT size = desktopImageWidth * desktopImageHeight * sizeof(UINT);
bufferForGetPixels_.ExpandIfNeeded(size);
std::memcpy(bufferForGetPixels_.Get(), mappedSurface.pBits, size);
if (FAILED(surface->Unmap()))
{
Debug::Error("Monitor::CopyTextureFromGpuToCpu() => surface->Unmap() failed.");
return;
}
}
bool Monitor::GetPixels(BYTE* output, int x, int y, int width, int height)
{
if (!UseGetPixels())
{
Debug::Error("Monitor::GetPixels() => UseGetPixels(true) must have been called when you want to use GetPixels().");
return false;
}
if (!bufferForGetPixels_)
{
Debug::Error("Monitor::GetPixels() => CopyTextureFromGpuToCpu() has not been called yet.");
return false;
}
const auto monitorRot = static_cast<DXGI_MODE_ROTATION>(GetRotation());
const auto monitorWidth = GetWidth();
const auto monitorHeight = GetHeight();
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;
// check area in destop coorinates.
int left, top, right, bottom;
switch (monitorRot)
{
case DXGI_MODE_ROTATION_ROTATE90:
{
left = y;
top = monitorWidth - x - width;
right = y + width;
bottom = monitorWidth - x;
break;
}
case DXGI_MODE_ROTATION_ROTATE180:
{
left = monitorWidth - x - width;
top = monitorHeight - y - height;
right = monitorWidth - x;
bottom = monitorHeight - y;
break;
}
case DXGI_MODE_ROTATION_ROTATE270:
{
left = monitorHeight - y - height;
top = x;
right = monitorHeight - y;
bottom = x + width;
break;
}
case DXGI_MODE_ROTATION_IDENTITY:
case DXGI_MODE_ROTATION_UNSPECIFIED:
default:
{
left = x;
top = y;
right = x + width;
bottom = y + height;
break;
}
}
if (left < 0 ||
top < 0 ||
right >= desktopImageWidth ||
bottom >= desktopImageHeight)
{
Debug::Error("Monitor::GetPixels() => is out of area.");
Debug::Error(
" ",
"(", left, ", ", top, ")",
" ~ (", right, ", ", bottom, ") > ",
"(", desktopImageWidth, ", ", desktopImageHeight, ")");
return false;
}
for (int row = 0; row < height; ++row)
{
for (int col = 0; col < width; ++col)
{
int inRow, inCol;
switch (monitorRot)
{
case DXGI_MODE_ROTATION_ROTATE90:
inCol = left + row;
inRow = bottom - 1 - col;
break;
case DXGI_MODE_ROTATION_ROTATE180:
inCol = right - 1 - col;
inRow = bottom - 1 - row;
break;
case DXGI_MODE_ROTATION_ROTATE270:
inCol = right - 1 - row;
inRow = top + col;
break;
case DXGI_MODE_ROTATION_IDENTITY:
case DXGI_MODE_ROTATION_UNSPECIFIED:
default:
inCol = left + col;
inRow = top + row;
break;
}
const auto inIndex = 4 * (inRow * desktopImageWidth + inCol);
const auto outRow = height - 1 - row;
const auto outCol = col;
const auto outIndex = 4 * (outRow * width + outCol);
// BGRA -> RGBA
output[outIndex + 0] = bufferForGetPixels_[inIndex + 2];
output[outIndex + 1] = bufferForGetPixels_[inIndex + 1];
output[outIndex + 2] = bufferForGetPixels_[inIndex + 0];
output[outIndex + 3] = bufferForGetPixels_[inIndex + 3];
}
}
return true;
}

View File

@@ -1,8 +1,11 @@
#pragma once
#include <d3d11.h>
#include <dxgi1_2.h>
#include <wrl/client.h>
#include <memory>
class Cursor;
#include <mutex>
#include "Common.h"
enum class MonitorState
{
@@ -14,6 +17,8 @@ enum class MonitorState
CurrentlyNotAvailable = 4,
SessionDisconnected = 5,
AccessLost = 6,
TextureSizeInconsistent = 7,
Unknown = 999,
};
class Monitor
@@ -23,9 +28,8 @@ public:
explicit Monitor(int id);
~Monitor();
HRESULT Initialize(IDXGIOutput* output);
HRESULT Render(UINT timeout = 0);
void GetCursorTexture(ID3D11Texture2D* texture);
void Initialize(IDXGIOutput* output);
void Render(UINT timeout = 0);
public:
int GetId() const;
@@ -34,6 +38,7 @@ public:
ID3D11Texture2D* GetUnityTexture() const;
void GetName(char* buf, int len) const;
bool IsPrimary() const;
bool HasBeenUpdated() const;
int GetLeft() const;
int GetRight() const;
int GetTop() const;
@@ -44,15 +49,34 @@ public:
int GetDpiX() const;
int GetDpiY() const;
IDXGIOutputDuplication* GetDeskDupl();
const std::unique_ptr<Cursor>& GetCursor();
int GetMoveRectCount() const;
DXGI_OUTDUPL_MOVE_RECT* GetMoveRects() const;
int GetDirtyRectCount() const;
RECT* GetDirtyRects() const;
void UseGetPixels(bool use);
bool UseGetPixels() const;
bool GetPixels(BYTE* output, int x, int y, int width, int height);
private:
void UpdateCursor(const DXGI_OUTDUPL_FRAME_INFO& frameInfo);
void UpdateMetadata(const DXGI_OUTDUPL_FRAME_INFO& frameInfo);
void UpdateMoveRects(const DXGI_OUTDUPL_FRAME_INFO& frameInfo);
void UpdateDirtyRects(const DXGI_OUTDUPL_FRAME_INFO& frameInfo);
void CopyTextureFromGpuToCpu(ID3D11Texture2D* texture);
int id_ = -1;
UINT dpiX_ = -1, dpiY_ = -1;
int width_ = -1, height_ = -1;
bool hasBeenUpdated_ = false;
bool useGetPixels_ = false;
State state_ = State::NotSet;
std::unique_ptr<Cursor> cursor_;
IDXGIOutputDuplication* deskDupl_ = nullptr;
ID3D11Texture2D* unityTexture_ = nullptr;
DXGI_OUTPUT_DESC outputDesc_;
MONITORINFOEX monitorInfo_;
Buffer<BYTE> metaData_;
Microsoft::WRL::ComPtr<ID3D11Texture2D> textureForGetPixels_;
Buffer<BYTE> bufferForGetPixels_;
UINT moveRectSize_ = 0;
UINT dirtyRectSize_ = 0;;
};

View File

@@ -1,5 +1,6 @@
#include <d3d11.h>
#include <dxgi1_2.h>
#include <wrl/client.h>
#include <vector>
#include <string>
#include <algorithm>
@@ -8,10 +9,13 @@
#include "IUnityGraphicsD3D11.h"
#include "Common.h"
#include "Debug.h"
#include "Monitor.h"
#include "Cursor.h"
#include "MonitorManager.h"
using namespace Microsoft::WRL;
MonitorManager::MonitorManager()
{
@@ -30,26 +34,27 @@ void MonitorManager::Initialize()
Finalize();
// Get factory
IDXGIFactory1* factory;
CreateDXGIFactory1(__uuidof(IDXGIFactory1), reinterpret_cast<void**>(&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;
IDXGIAdapter1* adapter;
ComPtr<IDXGIAdapter1> adapter;
for (int i = 0; (factory->EnumAdapters1(i, &adapter) != DXGI_ERROR_NOT_FOUND); ++i)
{
// Search the main monitor from all outputs
IDXGIOutput* output;
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);
monitor->Initialize(output.Get());
monitors_.push_back(monitor);
output->Release();
}
adapter->Release();
}
factory->Release();
}
@@ -67,11 +72,36 @@ void MonitorManager::RequireReinitilization()
void MonitorManager::Reinitialize()
{
Debug::Log("MonitorManager::Reinitialize()");
Initialize();
SendMessageToUnity(Message::Reinitialized);
}
bool MonitorManager::HasMonitorCountChanged() const
{
ComPtr<IDXGIFactory1> factory;
if (FAILED(CreateDXGIFactory1(IID_PPV_ARGS(&factory))))
{
Debug::Error("MonitorManager::CheckMonitorConnection() => CreateDXGIFactory1() failed.");
return false;
}
int id = 0;
ComPtr<IDXGIAdapter1> adapter;
for (int i = 0; (factory->EnumAdapters1(i, &adapter) != DXGI_ERROR_NOT_FOUND); ++i)
{
ComPtr<IDXGIOutput> output;
for (int j = 0; (adapter->EnumOutputs(j, &output) != DXGI_ERROR_NOT_FOUND); ++j)
{
id++;
}
}
return monitors_.size() != id;
}
std::shared_ptr<Monitor> MonitorManager::GetMonitor(int id) const
{
if (id >= 0 && id < monitors_.size())
@@ -82,6 +112,12 @@ std::shared_ptr<Monitor> MonitorManager::GetMonitor(int id) const
}
std::shared_ptr<Cursor> MonitorManager::GetCursor() const
{
return cursor_;
}
void MonitorManager::Update()
{
if (isReinitializationRequired_)
@@ -109,6 +145,7 @@ int MonitorManager::GetMonitorCount() const
return static_cast<int>(monitors_.size());
}
int MonitorManager::GetTotalWidth() const
{
std::vector<int> lefts, rights;
@@ -122,6 +159,7 @@ int MonitorManager::GetTotalWidth() const
return maxRight - minLeft;
}
int MonitorManager::GetTotalHeight() const
{
std::vector<int> tops, bottoms;

View File

@@ -1,3 +1,5 @@
#pragma once
#include <d3d11.h>
#include <dxgi1_2.h>
#include <vector>
@@ -14,10 +16,12 @@ 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;
std::shared_ptr<Cursor> GetCursor() const;
private:
void Initialize();
@@ -37,8 +41,9 @@ public:
private:
int timeout_ = 10;
bool enableTextureCopyFromGpuToCpu_ = false;
std::vector<std::shared_ptr<Monitor>> monitors_;
std::shared_ptr<Cursor> cursor_;
std::shared_ptr<Cursor> cursor_ = std::make_shared<Cursor>();
int cursorMonitorId_ = -1;
bool isReinitializationRequired_ = false;
};

View File

@@ -7,9 +7,9 @@
#include "IUnityInterface.h"
#include "IUnityGraphics.h"
#include "IUnityGraphicsD3D11.h"
#include "Common.h"
#include "Debug.h"
#include "Monitor.h"
#include "Cursor.h"
#include "MonitorManager.h"
@@ -18,63 +18,68 @@
#pragma comment(lib, "Shcore.lib")
namespace
{
IUnityInterfaces* g_unity = nullptr;
std::unique_ptr<MonitorManager> g_manager;
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);
}
IUnityInterfaces* g_unity = nullptr;
std::unique_ptr<MonitorManager> g_manager;
std::queue<Message> g_messages;
ID3D11DeviceContext* g_deviceContextForMainThread = nullptr;
extern "C"
{
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API InitializeUDD()
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API IsInitialized()
{
return g_unity && g_manager;
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API Initialize()
{
if (g_unity && !g_manager)
{
Debug::Initialize();
g_manager = std::make_unique<MonitorManager>();
}
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API FinalizeUDD()
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API Finalize()
{
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:
{
Initialize();
break;
}
case kUnityGfxDeviceEventShutdown:
{
Finalize();
break;
}
}
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces)
{
g_unity = unityInterfaces;
InitializeUDD();
auto unityGraphics = g_unity->Get<IUnityGraphics>();
unityGraphics->RegisterDeviceEventCallback(OnGraphicsDeviceEvent);
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UnityPluginUnload()
{
auto unityGraphics = g_unity->Get<IUnityGraphics>();
unityGraphics->UnregisterDeviceEventCallback(OnGraphicsDeviceEvent);
g_unity = nullptr;
FinalizeUDD();
}
void UNITY_INTERFACE_API OnRenderEvent(int id)
@@ -103,7 +108,7 @@ extern "C"
g_manager->Update();
}
UNITY_INTERFACE_EXPORT Message PopMessage()
UNITY_INTERFACE_EXPORT Message UNITY_INTERFACE_API PopMessage()
{
if (g_messages.empty()) return Message::None;
@@ -112,12 +117,39 @@ extern "C"
return message;
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetDebugMode(Debug::Mode mode)
{
Debug::SetMode(mode);
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetLogFunc(Debug::DebugLogFuncPtr func)
{
Debug::SetLogFunc(func);
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetErrorFunc(Debug::DebugLogFuncPtr func)
{
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;
@@ -136,6 +168,15 @@ extern "C"
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;
@@ -255,83 +296,52 @@ extern "C"
return -1;
}
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API IsCursorVisible(int id)
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API IsCursorVisible()
{
if (!g_manager) return false;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->GetCursor()->IsVisible();
}
return false;
return g_manager->GetCursor()->IsVisible();
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorX(int id)
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorX()
{
if (!g_manager) return -1;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->GetCursor()->GetX();
}
return -1;
return g_manager->GetCursor()->GetX();
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorY(int id)
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorY()
{
if (!g_manager) return -1;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->GetCursor()->GetY();
}
return -1;
return g_manager->GetCursor()->GetY();
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorShapeWidth(int id)
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorShapeWidth()
{
if (!g_manager) return -1;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->GetCursor()->GetWidth();
}
return -1;
return g_manager->GetCursor()->GetWidth();
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorShapeHeight(int id)
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorShapeHeight()
{
if (!g_manager) return -1;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->GetCursor()->GetHeight();
}
return -1;
return g_manager->GetCursor()->GetHeight();
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorShapePitch(int id)
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorShapePitch()
{
if (!g_manager) return -1;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->GetCursor()->GetPitch();
}
return -1;
return g_manager->GetCursor()->GetPitch();
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorShapeType(int id)
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorShapeType()
{
if (!g_manager) return -1;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->GetCursor()->GetType();
}
return -1;
return g_manager->GetCursor()->GetType();
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API GetCursorTexture(int id, ID3D11Texture2D* texture)
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API GetCursorTexture(ID3D11Texture2D* texture)
{
if (!g_manager) return;
if (auto monitor = g_manager->GetMonitor(id))
{
monitor->GetCursorTexture(texture);
}
return g_manager->GetCursor()->GetTexture(texture);
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetTexturePtr(int id, void* texture)
@@ -343,4 +353,73 @@ extern "C"
monitor->SetUnityTexture(d3d11Texture);
}
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetMoveRectCount(int id)
{
if (!g_manager) return -1;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->GetMoveRectCount();
}
return 0;
}
UNITY_INTERFACE_EXPORT void* UNITY_INTERFACE_API GetMoveRects(int id)
{
if (!g_manager) return nullptr;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->GetMoveRects();
}
return nullptr;
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetDirtyRectCount(int id)
{
if (!g_manager) return -1;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->GetDirtyRectCount();
}
return 0;
}
UNITY_INTERFACE_EXPORT void* UNITY_INTERFACE_API GetDirtyRects(int id)
{
if (!g_manager) return nullptr;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->GetDirtyRects();
}
return nullptr;
}
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API GetPixels(int id, BYTE* output, int x, int y, int width, int height)
{
if (!g_manager) return nullptr;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->GetPixels(output, x, y, width, height);
}
return false;
}
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API HasBeenUpdated(int id)
{
if (!g_manager) return nullptr;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->HasBeenUpdated();
}
return false;
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UseGetPixels(int id, bool use)
{
if (!g_manager) return;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->UseGetPixels(use);
}
}
}

View File

@@ -89,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'">
@@ -112,20 +112,23 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<Optimization>Full</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<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="Common.cpp" />
<ClCompile Include="Debug.cpp" />
<ClCompile Include="MonitorManager.cpp" />
<ClCompile Include="Main.cpp" />
<ClCompile Include="Monitor.cpp" />
@@ -133,6 +136,7 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="Common.h" />
<ClInclude Include="Debug.h" />
<ClInclude Include="MonitorManager.h" />
<ClInclude Include="include\IUnityGraphics.h" />
<ClInclude Include="include\IUnityGraphicsD3D11.h" />

View File

@@ -19,11 +19,14 @@
<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,2 +1,2 @@
m_EditorVersion: 5.4.1f1
m_EditorVersion: 5.4.2f2
m_StandardAssetsVersion: 0

View File

@@ -1,6 +1,9 @@
uDesktopDuplication
===================
![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)
![Build Status](http://unitybuildbadge.azurewebsites.net/api/status/7566d616-2916-4c99-bb14-e7ab9501cf7e)
**uDesktopDuplication** is an Unity asset to use the realtime screen capture as `Texture2D` using Desktop Duplication API.
@@ -25,16 +28,6 @@ Usage
Attach `uDesktopDuplication/Texture` component to the target object, then its main texture will be replaced with the captured screen. Please see example scenes for more details.
Version
-------
| Data | Version | Description |
| ---------- | ------- | --------------------------------- |
| 2016/10/28 | 1.0.0 | Support multiple screens. |
| 2016/10/27 | 0.0.3 | Support lienar color. |
| 2016/10/27 | 0.0.2 | Add mouse cursor / shaders. |
| 2016/10/27 | 0.0.1 | Initial commit. |
Lisence
-------
The MIT License (MIT)