Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ac1a5ee60 | ||
|
|
15a98cd774 | ||
|
|
06bb971a42 | ||
|
|
217e63d40c | ||
|
|
9e42fb0abf | ||
|
|
9de5064fbe | ||
|
|
2b108bd4b1 | ||
|
|
f9f8b14f46 | ||
|
|
7774290e71 | ||
|
|
a3a7754fb0 | ||
|
|
0accebb657 | ||
|
|
0a38eda14f | ||
|
|
720f74e6f6 | ||
|
|
80fa39f4eb | ||
|
|
22b21ae3f4 | ||
|
|
43153266a0 | ||
|
|
dc4fe44342 | ||
|
|
6bb9846510 | ||
|
|
90b811cd31 | ||
|
|
1d7b7e8650 | ||
|
|
6f784860e5 | ||
|
|
690dba23cc | ||
|
|
908988dcaa | ||
|
|
0f72396540 | ||
|
|
8f7659d7c2 | ||
|
|
2e763d994d | ||
|
|
2c61fcd470 | ||
|
|
f2cad8bf73 | ||
|
|
ad4b49c177 | ||
|
|
92b926bcde | ||
|
|
3e90889266 | ||
|
|
6531fe1dd6 | ||
|
|
0b62376ac5 | ||
|
|
9f611d5d34 | ||
|
|
dd4e27030d |
@@ -1,6 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 281e2991d4d33984fa180845e56cd3f3
|
||||
timeCreated: 1477648643
|
||||
guid: 022a80d652ef0304e84e9d6ac689e21f
|
||||
folderAsset: yes
|
||||
timeCreated: 1478353382
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
9
Assets/uDesktopDuplication/Editor/Resources.meta
Normal file
9
Assets/uDesktopDuplication/Editor/Resources.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e15bc51aada81042a909c808e25c176
|
||||
folderAsset: yes
|
||||
timeCreated: 1477555207
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
Before Width: | Height: | Size: 967 B After Width: | Height: | Size: 967 B |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,38 +1,96 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class MultipleMonitorCreator : MonoBehaviour
|
||||
{
|
||||
[SerializeField] GameObject monitorPrefab;
|
||||
[SerializeField] float scale = 10f;
|
||||
[SerializeField] float scale = 1f;
|
||||
[SerializeField] float margin = 1f;
|
||||
[SerializeField] float ratio = 0.001f;
|
||||
|
||||
private List<GameObject> monitors_ = new List<GameObject>();
|
||||
private float totalWidth_ = 0f;
|
||||
|
||||
void Start()
|
||||
{
|
||||
var monitors = uDesktopDuplication.Manager.monitors;
|
||||
var n = monitors.Count;
|
||||
Create();
|
||||
}
|
||||
|
||||
var totalWidth = 0f;
|
||||
void OnEnable()
|
||||
{
|
||||
uDesktopDuplication.Manager.onReinitialized += Recreate;
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
uDesktopDuplication.Manager.onReinitialized -= Recreate;
|
||||
}
|
||||
|
||||
void Create()
|
||||
{
|
||||
CreateMonitors();
|
||||
LayoutMonitors();
|
||||
}
|
||||
|
||||
void CreateMonitors()
|
||||
{
|
||||
// Sort monitors in coordinate order
|
||||
var monitors = uDesktopDuplication.Manager.monitors;
|
||||
monitors.Sort((a, b) => a.left - b.left);
|
||||
|
||||
// Create monitors
|
||||
var n = monitors.Count;
|
||||
totalWidth_ = 0f;
|
||||
for (int i = 0 ; i < n; ++i) {
|
||||
// Create monitor obeject
|
||||
var go = Instantiate(monitorPrefab);
|
||||
go.name = "Monitor " + i;
|
||||
monitors_.Add(go);
|
||||
|
||||
// Assign monitor
|
||||
var texture = go.GetComponent<uDesktopDuplication.Texture>();
|
||||
texture.monitorId = i;
|
||||
go.transform.localScale = new Vector3(texture.monitor.width * ratio, 1f, texture.monitor.height * ratio);
|
||||
go.transform.SetParent(transform);
|
||||
totalWidth += texture.monitor.width * ratio * scale;
|
||||
}
|
||||
var monitor = texture.monitor;
|
||||
|
||||
totalWidth += margin * (n - 1);
|
||||
var x = -totalWidth / 2;
|
||||
for (int i = 0 ; i < n; ++i) {
|
||||
var go = transform.FindChild("Monitor " + i);
|
||||
var texture = go.GetComponent<uDesktopDuplication.Texture>();
|
||||
var halfWidth = texture.monitor.width * ratio * scale / 2;
|
||||
// Set width / height
|
||||
go.transform.localScale = new Vector3(monitor.widthMeter, go.transform.localScale.y, monitor.heightMeter);
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
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.position = new Vector3(x, 0f, 0f);
|
||||
go.transform.localPosition = new Vector3(x, 0f, 0f);
|
||||
x += halfWidth + margin;
|
||||
}
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
foreach (var go in monitors_) {
|
||||
Destroy(go);
|
||||
}
|
||||
monitors_.Clear();
|
||||
}
|
||||
|
||||
void Recreate()
|
||||
{
|
||||
Clear();
|
||||
Create();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 033e0db89e0ca1d46a399c953629f67d
|
||||
timeCreated: 1477555367
|
||||
licenseType: Pro
|
||||
NativeFormatImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,8 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e15bc51aada81042a909c808e25c176
|
||||
guid: b281589e6b8e27b4fbf0509d620b7871
|
||||
folderAsset: yes
|
||||
timeCreated: 1477555207
|
||||
licenseType: Pro
|
||||
timeCreated: 1478354860
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48b92326b146fa848beec6f35365b1f4
|
||||
guid: e36cd526b654d024e875d5110a1b0442
|
||||
folderAsset: yes
|
||||
timeCreated: 1477555207
|
||||
licenseType: Pro
|
||||
timeCreated: 1478354877
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e2bcc27e10c0d141a0c11cd88cdcf98
|
||||
folderAsset: yes
|
||||
timeCreated: 1478353263
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
@@ -0,0 +1,59 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9855ecfc2b823c94d8a1354715d343a1
|
||||
timeCreated: 1478353264
|
||||
licenseType: Pro
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
serializedVersion: 2
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
linearTexture: 0
|
||||
correctGamma: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 0
|
||||
cubemapConvolution: 0
|
||||
cubemapConvolutionSteps: 7
|
||||
cubemapConvolutionExponent: 1.5
|
||||
seamlessCubemap: 0
|
||||
textureFormat: -1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
filterMode: -1
|
||||
aniso: -1
|
||||
mipBias: -1
|
||||
wrapMode: -1
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
rGBM: 0
|
||||
compressionQuality: 50
|
||||
allowsAlphaSplitting: 0
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spritePixelsToUnits: 100
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: -1
|
||||
buildTargetSettings: []
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
spritePackingTag:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -8,46 +8,23 @@ namespace uDesktopDuplication
|
||||
RequireComponent(typeof(Texture))]
|
||||
public class Cursor : MonoBehaviour
|
||||
{
|
||||
[SerializeField] Vector2 modelScale = Vector2.one;
|
||||
|
||||
Vector3 worldPosition { get; set; }
|
||||
|
||||
private Texture uddTexture_;
|
||||
private Monitor monitor { get { return uddTexture_.monitor; } }
|
||||
|
||||
private Texture2D pointerTexture_;
|
||||
private Material cursorMaterial_;
|
||||
private Texture2D currentTexture_;
|
||||
private Dictionary<Vector2, Texture2D> textures_ = new Dictionary<Vector2, Texture2D>();
|
||||
[SerializeField] Vector2 modelScale = Vector2.one;
|
||||
|
||||
private Dictionary<Vector2, Texture2D> pointerTextures_ = new Dictionary<Vector2, Texture2D>();
|
||||
|
||||
void OnEnable()
|
||||
void Start()
|
||||
{
|
||||
uddTexture_ = GetComponent<Texture>();
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
var clone = new GameObject(name + " Cursor");
|
||||
clone.transform.SetParent(transform);
|
||||
clone.transform.localPosition = Vector3.zero;
|
||||
clone.transform.localRotation = Quaternion.identity;
|
||||
clone.transform.localScale = Vector3.one;
|
||||
|
||||
var filter = clone.AddComponent<MeshFilter>();
|
||||
filter.mesh = GetComponent<MeshFilter>().sharedMesh;
|
||||
|
||||
var renderer = clone.AddComponent<MeshRenderer>();
|
||||
var shader = Shader.Find("uDesktopDuplication/Cursor");
|
||||
cursorMaterial_ = new Material(shader);
|
||||
renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
|
||||
renderer.receiveShadows = false;
|
||||
renderer.motionVectors = false;
|
||||
renderer.material = cursorMaterial_;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (monitor.isPointerVisible) {
|
||||
if (monitor.isCursorVisible) {
|
||||
UpdatePosition();
|
||||
UpdateTexture();
|
||||
UpdateMaterial();
|
||||
@@ -56,8 +33,8 @@ public class Cursor : MonoBehaviour
|
||||
|
||||
void UpdatePosition()
|
||||
{
|
||||
var x = (1f * monitor.pointerX / monitor.width - 0.5f) * modelScale.x;
|
||||
var y = (1f * monitor.pointerY / monitor.height - 0.5f) * modelScale.y;
|
||||
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);
|
||||
@@ -65,42 +42,29 @@ public class Cursor : MonoBehaviour
|
||||
|
||||
void UpdateTexture()
|
||||
{
|
||||
var scale = new Vector2(monitor.pointerShapeWidth, monitor.pointerShapeHeight);
|
||||
if (!pointerTextures_.ContainsKey(scale)) {
|
||||
var texture = new Texture2D(monitor.pointerShapeWidth, monitor.pointerShapeHeight, TextureFormat.BGRA32, false);
|
||||
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;
|
||||
pointerTextures_.Add(scale, texture);
|
||||
textures_.Add(scale, texture);
|
||||
}
|
||||
var pointerTexture = pointerTextures_[scale];
|
||||
monitor.UpdatePointerTexture(pointerTexture.GetNativeTexturePtr());
|
||||
cursorMaterial_.SetTexture("_MainTex", pointerTexture);
|
||||
|
||||
var cursorTexture = textures_[scale];
|
||||
monitor.GetCursorTexture(cursorTexture.GetNativeTexturePtr());
|
||||
uddTexture_.material.SetTexture("_CursorTex", cursorTexture);
|
||||
}
|
||||
|
||||
void UpdateMaterial()
|
||||
{
|
||||
if (uddTexture_.invertX) {
|
||||
cursorMaterial_.EnableKeyword("INVERT_X");
|
||||
} else {
|
||||
cursorMaterial_.DisableKeyword("INVERT_X");
|
||||
}
|
||||
|
||||
if (uddTexture_.invertY) {
|
||||
cursorMaterial_.EnableKeyword("INVERT_Y");
|
||||
} else {
|
||||
cursorMaterial_.DisableKeyword("INVERT_Y");
|
||||
}
|
||||
|
||||
if (monitor.isVertical) {
|
||||
cursorMaterial_.EnableKeyword("VERTICAL");
|
||||
} else {
|
||||
cursorMaterial_.DisableKeyword("VERTICAL");
|
||||
}
|
||||
|
||||
var x = (float)monitor.pointerX / monitor.width;
|
||||
var y = (float)monitor.pointerY / monitor.height;
|
||||
var w = (float)monitor.pointerShapeWidth / monitor.width;
|
||||
var h = (float)monitor.pointerShapeHeight / monitor.height;
|
||||
cursorMaterial_.SetVector("_PositionScale", new Vector4(x, y, w, h));
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,51 +5,106 @@ using System.Runtime.InteropServices;
|
||||
namespace uDesktopDuplication
|
||||
{
|
||||
|
||||
public enum PointerShapeType
|
||||
public enum Message
|
||||
{
|
||||
None = -1,
|
||||
Reinitialized = 0,
|
||||
}
|
||||
|
||||
public enum CursorShapeType
|
||||
{
|
||||
MonoChrome = 1,
|
||||
Color = 2,
|
||||
MaskedColor = 4,
|
||||
}
|
||||
|
||||
public enum MonitorRotation
|
||||
{
|
||||
Unspecified = 0,
|
||||
Identity = 1,
|
||||
Rotate90 = 2,
|
||||
Rotate180 = 3,
|
||||
Rotate270 = 4
|
||||
}
|
||||
|
||||
public enum MonitorState
|
||||
{
|
||||
NotSet = -1,
|
||||
Available = 0,
|
||||
InvalidArg = 1,
|
||||
AccessDenied = 2,
|
||||
Unsupported = 3,
|
||||
CurrentlyNotAvailable = 4,
|
||||
SessionDisconnected = 5,
|
||||
AccessLost = 6,
|
||||
}
|
||||
|
||||
public static class Lib
|
||||
{
|
||||
public delegate void MessageHandler(Message message);
|
||||
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void InitializeUDD();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void FinalizeUDD();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void Reinitialize();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void Update();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern Message PopMessage();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetMonitorCount();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetTotalWidth();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetTotalHeight();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void SetTimeout(int timeout);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern IntPtr GetRenderEventFunc();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern MonitorState GetState(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void GetName(int id, StringBuilder buf, int len);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetLeft(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetRight(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetTop(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetBottom(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetWidth(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetHeight(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetDpiX(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetDpiY(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern MonitorRotation GetRotation(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern bool IsPrimary(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern bool IsPointerVisible(int id);
|
||||
public static extern bool IsCursorVisible(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetPointerX(int id);
|
||||
public static extern int GetCursorX(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetPointerY(int id);
|
||||
public static extern int GetCursorY(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetPointerShapeWidth(int id);
|
||||
public static extern int GetCursorShapeWidth(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetPointerShapeHeight(int id);
|
||||
public static extern int GetCursorShapeHeight(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetPointerShapePitch(int id);
|
||||
public static extern int GetCursorShapePitch(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern PointerShapeType GetPointerShapeType(int id);
|
||||
public static extern CursorShapeType GetCursorShapeType(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void UpdatePointerTexture(int id, System.IntPtr ptr);
|
||||
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 GetErrorCode();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void GetErrorMessage(StringBuilder buf, int len);
|
||||
|
||||
public static string GetName(int id)
|
||||
{
|
||||
@@ -57,13 +112,6 @@ public static class Lib
|
||||
GetName(id, buf, buf.Capacity);
|
||||
return buf.ToString();
|
||||
}
|
||||
|
||||
public static string GetErrorMessage()
|
||||
{
|
||||
var buf = new StringBuilder(64);
|
||||
GetErrorMessage(buf, buf.Capacity);
|
||||
return buf.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,7 +12,16 @@ public class Manager : MonoBehaviour
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance_) return instance_;
|
||||
if (instance_) {
|
||||
return instance_;
|
||||
}
|
||||
|
||||
var manager = FindObjectOfType<Manager>();
|
||||
if (manager) {
|
||||
manager.Awake();
|
||||
return manager;
|
||||
}
|
||||
|
||||
var go = new GameObject("uDesktopDuplicationManager");
|
||||
return go.AddComponent<Manager>();
|
||||
}
|
||||
@@ -26,7 +35,7 @@ public class Manager : MonoBehaviour
|
||||
|
||||
static public int monitorCount
|
||||
{
|
||||
get { return instance.monitors_.Count; }
|
||||
get { return Lib.GetMonitorCount(); }
|
||||
}
|
||||
|
||||
static public Monitor primary
|
||||
@@ -37,24 +46,31 @@ public class Manager : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField, Tooltip("Set Desktop Duplication API timeout (milliseconds).")]
|
||||
int timeout = 0;
|
||||
|
||||
[SerializeField, Tooltip("Output logs given by the plugin.")]
|
||||
bool outputDebugLog = false;
|
||||
[SerializeField] int desktopDuplicationApiTimeout = 0;
|
||||
[SerializeField] float retryReinitializationDuration = 1f;
|
||||
|
||||
private Coroutine renderCoroutine_ = null;
|
||||
private bool shouldReinitialize = false;
|
||||
private float reinitializationTimer = 0f;
|
||||
|
||||
public delegate void ReinitializeHandler();
|
||||
public static event ReinitializeHandler onReinitialized;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
Lib.InitializeUDD();
|
||||
|
||||
if (instance_ != null) return;
|
||||
instance_ = this;
|
||||
|
||||
for (int i = 0; i < Lib.GetMonitorCount(); ++i) {
|
||||
monitors.Add(new Monitor(i));
|
||||
}
|
||||
CreateMonitors();
|
||||
|
||||
Lib.SetTimeout(timeout);
|
||||
Lib.SetTimeout(desktopDuplicationApiTimeout);
|
||||
}
|
||||
|
||||
void OnApplicationQuit()
|
||||
{
|
||||
Lib.FinalizeUDD();
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
@@ -70,6 +86,67 @@ public class Manager : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
Lib.Update();
|
||||
ReinitializeIfNeeded();
|
||||
UpdateMessage();
|
||||
|
||||
/*
|
||||
foreach (var monitor in monitors_) {
|
||||
Debug.LogFormat("[{0}] {1}", monitor.id, monitor.state);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
[ContextMenu("Reinitialize")]
|
||||
void Reinitialize()
|
||||
{
|
||||
Debug.Log("[uDesktopDuplication] Reinitialize");
|
||||
Lib.Reinitialize();
|
||||
if (onReinitialized != null) onReinitialized();
|
||||
}
|
||||
|
||||
void ReinitializeIfNeeded()
|
||||
{
|
||||
for (int i = 0; i < monitors.Count; ++i) {
|
||||
var monitor = monitors[i];
|
||||
if (monitor.state == MonitorState.NotSet ||
|
||||
monitor.state == MonitorState.AccessLost ||
|
||||
monitor.state == MonitorState.AccessDenied ||
|
||||
monitor.state == MonitorState.SessionDisconnected) {
|
||||
if (!shouldReinitialize) {
|
||||
shouldReinitialize = true;
|
||||
reinitializationTimer = 0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldReinitialize) {
|
||||
if (reinitializationTimer > retryReinitializationDuration) {
|
||||
Reinitialize();
|
||||
shouldReinitialize = false;
|
||||
}
|
||||
reinitializationTimer += Time.deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateMessage()
|
||||
{
|
||||
var message = Lib.PopMessage();
|
||||
while (message != Message.None) {
|
||||
switch (message) {
|
||||
case Message.Reinitialized:
|
||||
ReinitializeMonitors();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
message = Lib.PopMessage();
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator OnRender()
|
||||
{
|
||||
for (;;) {
|
||||
@@ -81,10 +158,23 @@ public class Manager : MonoBehaviour
|
||||
}
|
||||
monitor.shouldBeUpdated = false;
|
||||
}
|
||||
if (outputDebugLog && Lib.GetErrorCode() != 0)
|
||||
{
|
||||
Debug.Log(Lib.GetErrorMessage());
|
||||
}
|
||||
}
|
||||
|
||||
void CreateMonitors()
|
||||
{
|
||||
for (int i = 0; i < monitorCount; ++i) {
|
||||
monitors.Add(new Monitor(i));
|
||||
}
|
||||
}
|
||||
|
||||
void ReinitializeMonitors()
|
||||
{
|
||||
for (int i = 0; i < monitorCount; ++i) {
|
||||
if (i == monitors.Count) {
|
||||
monitors.Add(new Monitor(i));
|
||||
}
|
||||
monitors[i].Reinitialize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,27 @@ public class Monitor
|
||||
public Monitor(int id)
|
||||
{
|
||||
this.id = id;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case MonitorState.InvalidArg:
|
||||
Debug.LogErrorFormat("[{0}:{1}] Invalid.", id, name);
|
||||
break;
|
||||
case MonitorState.AccessDenied:
|
||||
Debug.LogErrorFormat("[{0}:{1}] Access Denied.", id, name);
|
||||
break;
|
||||
case MonitorState.Unsupported:
|
||||
Debug.LogErrorFormat("[{0}:{1}] Unsupported.", id, name);
|
||||
break;
|
||||
case MonitorState.SessionDisconnected:
|
||||
Debug.LogErrorFormat("[{0}:{1}] Disconnected.", id, name);
|
||||
break;
|
||||
case MonitorState.NotSet:
|
||||
Debug.LogErrorFormat("[{0}:{1}] Something wrong.", id, name);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public int id
|
||||
@@ -16,6 +37,21 @@ public class Monitor
|
||||
private set;
|
||||
}
|
||||
|
||||
public bool exists
|
||||
{
|
||||
get { return id < Manager.monitorCount; }
|
||||
}
|
||||
|
||||
public MonitorState state
|
||||
{
|
||||
get { return Lib.GetState(id); }
|
||||
}
|
||||
|
||||
public bool available
|
||||
{
|
||||
get { return state == MonitorState.Available; }
|
||||
}
|
||||
|
||||
public string name
|
||||
{
|
||||
get { return Lib.GetName(id); }
|
||||
@@ -26,6 +62,26 @@ public class Monitor
|
||||
get { return Lib.IsPrimary(id); }
|
||||
}
|
||||
|
||||
public int left
|
||||
{
|
||||
get { return Lib.GetLeft(id); }
|
||||
}
|
||||
|
||||
public int right
|
||||
{
|
||||
get { return Lib.GetRight(id); }
|
||||
}
|
||||
|
||||
public int top
|
||||
{
|
||||
get { return Lib.GetTop(id); }
|
||||
}
|
||||
|
||||
public int bottom
|
||||
{
|
||||
get { return Lib.GetBottom(id); }
|
||||
}
|
||||
|
||||
public int width
|
||||
{
|
||||
get { return Lib.GetWidth(id); }
|
||||
@@ -36,6 +92,31 @@ public class Monitor
|
||||
get { return Lib.GetHeight(id); }
|
||||
}
|
||||
|
||||
public int dpiX
|
||||
{
|
||||
get { return Lib.GetDpiX(id); }
|
||||
}
|
||||
|
||||
public int dpiY
|
||||
{
|
||||
get { return Lib.GetDpiY(id); }
|
||||
}
|
||||
|
||||
public float widthMeter
|
||||
{
|
||||
get { return width / dpiX * 0.0254f; }
|
||||
}
|
||||
|
||||
public float heightMeter
|
||||
{
|
||||
get { return height / dpiY * 0.0254f; }
|
||||
}
|
||||
|
||||
public MonitorRotation rotation
|
||||
{
|
||||
get { return Lib.GetRotation(id); }
|
||||
}
|
||||
|
||||
public float aspect
|
||||
{
|
||||
get { return (float)width / height; }
|
||||
@@ -51,34 +132,34 @@ public class Monitor
|
||||
get { return height > width; }
|
||||
}
|
||||
|
||||
public bool isPointerVisible
|
||||
public bool isCursorVisible
|
||||
{
|
||||
get { return Lib.IsPointerVisible(id); }
|
||||
get { return Lib.IsCursorVisible(id); }
|
||||
}
|
||||
|
||||
public int pointerX
|
||||
public int cursorX
|
||||
{
|
||||
get { return Lib.GetPointerX(id); }
|
||||
get { return Lib.GetCursorX(id); }
|
||||
}
|
||||
|
||||
public int pointerY
|
||||
public int cursorY
|
||||
{
|
||||
get { return Lib.GetPointerY(id); }
|
||||
get { return Lib.GetCursorY(id); }
|
||||
}
|
||||
|
||||
public int pointerShapeWidth
|
||||
public int cursorShapeWidth
|
||||
{
|
||||
get { return Lib.GetPointerShapeWidth(id); }
|
||||
get { return Lib.GetCursorShapeWidth(id); }
|
||||
}
|
||||
|
||||
public int pointerShapeHeight
|
||||
public int cursorShapeHeight
|
||||
{
|
||||
get { return Lib.GetPointerShapeHeight(id); }
|
||||
get { return Lib.GetCursorShapeHeight(id); }
|
||||
}
|
||||
|
||||
public PointerShapeType pointerShapeType
|
||||
public CursorShapeType cursorShapeType
|
||||
{
|
||||
get { return Lib.GetPointerShapeType(id); }
|
||||
get { return Lib.GetCursorShapeType(id); }
|
||||
}
|
||||
|
||||
public bool shouldBeUpdated
|
||||
@@ -92,11 +173,11 @@ public class Monitor
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!available) {
|
||||
return Resources.Load<Texture2D>("uDesktopDuplication/Textures/NotAvailable");
|
||||
}
|
||||
if (texture_ == null) {
|
||||
var w = isHorizontal ? width : height;
|
||||
var h = isHorizontal ? height : width;
|
||||
texture_ = new Texture2D(w, h, TextureFormat.BGRA32, false);
|
||||
Lib.SetTexturePtr(id, texture_.GetNativeTexturePtr());
|
||||
CreateTexture();
|
||||
}
|
||||
return texture_;
|
||||
}
|
||||
@@ -104,12 +185,46 @@ public class Monitor
|
||||
|
||||
public void Render()
|
||||
{
|
||||
GL.IssuePluginEvent(Lib.GetRenderEventFunc(), id);
|
||||
if (texture_ && available) {
|
||||
Lib.SetTexturePtr(id, texture_.GetNativeTexturePtr());
|
||||
GL.IssuePluginEvent(Lib.GetRenderEventFunc(), id);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdatePointerTexture(System.IntPtr ptr)
|
||||
public void GetCursorTexture(System.IntPtr ptr)
|
||||
{
|
||||
Lib.UpdatePointerTexture(id, ptr);
|
||||
Lib.GetCursorTexture(id, ptr);
|
||||
}
|
||||
|
||||
void CreateTexture()
|
||||
{
|
||||
if (!available) return;
|
||||
|
||||
var w = isHorizontal ? width : height;
|
||||
var h = isHorizontal ? height : width;
|
||||
bool shouldCreate = true;
|
||||
|
||||
if (texture_) {
|
||||
if (texture_.width != w || texture_.height != h) {
|
||||
if (texture_) Object.DestroyImmediate(texture_);
|
||||
shouldCreate = true;
|
||||
} else {
|
||||
shouldCreate = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (w <= 0 || h <= 0) {
|
||||
shouldCreate = false;
|
||||
}
|
||||
|
||||
if (shouldCreate) {
|
||||
texture_ = new Texture2D(w, h, TextureFormat.BGRA32, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void Reinitialize()
|
||||
{
|
||||
CreateTexture();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ public class Texture : MonoBehaviour
|
||||
set
|
||||
{
|
||||
monitor_ = value;
|
||||
material_ = GetComponent<Renderer>().material;
|
||||
material_.mainTexture = monitor_.texture;
|
||||
material = GetComponent<Renderer>().material;
|
||||
material.mainTexture = monitor_.texture;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,19 @@ public class Texture : MonoBehaviour
|
||||
public bool invertX = false;
|
||||
public bool invertY = false;
|
||||
|
||||
private Material material_;
|
||||
public Material material
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (!GetComponent<Cursor>())
|
||||
{
|
||||
gameObject.AddComponent<Cursor>();
|
||||
}
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
@@ -45,21 +57,41 @@ public class Texture : MonoBehaviour
|
||||
void UpdateMaterial()
|
||||
{
|
||||
if (invertX) {
|
||||
material_.EnableKeyword("INVERT_X");
|
||||
material.EnableKeyword("INVERT_X");
|
||||
} else {
|
||||
material_.DisableKeyword("INVERT_X");
|
||||
material.DisableKeyword("INVERT_X");
|
||||
}
|
||||
|
||||
if (invertY) {
|
||||
material_.EnableKeyword("INVERT_Y");
|
||||
material.EnableKeyword("INVERT_Y");
|
||||
} else {
|
||||
material_.DisableKeyword("INVERT_Y");
|
||||
material.DisableKeyword("INVERT_Y");
|
||||
}
|
||||
|
||||
if (monitor.isVertical) {
|
||||
material_.EnableKeyword("VERTICAL");
|
||||
} else {
|
||||
material_.DisableKeyword("VERTICAL");
|
||||
switch (monitor.rotation)
|
||||
{
|
||||
case MonitorRotation.Identity:
|
||||
material.DisableKeyword("ROTATE90");
|
||||
material.DisableKeyword("ROTATE180");
|
||||
material.DisableKeyword("ROTATE270");
|
||||
break;
|
||||
case MonitorRotation.Rotate90:
|
||||
material.EnableKeyword("ROTATE90");
|
||||
material.DisableKeyword("ROTATE180");
|
||||
material.DisableKeyword("ROTATE270");
|
||||
break;
|
||||
case MonitorRotation.Rotate180:
|
||||
material.DisableKeyword("ROTATE90");
|
||||
material.EnableKeyword("ROTATE180");
|
||||
material.DisableKeyword("ROTATE270");
|
||||
break;
|
||||
case MonitorRotation.Rotate270:
|
||||
material.DisableKeyword("ROTATE90");
|
||||
material.DisableKeyword("ROTATE180");
|
||||
material.EnableKeyword("ROTATE270");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,24 @@
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
|
||||
inline void uddInvertUV(inout float2 uv)
|
||||
struct appdata
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 vertex : SV_POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct Input
|
||||
{
|
||||
float2 uv_MainTex;
|
||||
};
|
||||
|
||||
float2 uddInvertUV(float2 uv)
|
||||
{
|
||||
#ifdef INVERT_X
|
||||
uv.x = 1.0 - uv.x;
|
||||
@@ -11,26 +28,57 @@ inline void uddInvertUV(inout float2 uv)
|
||||
#ifdef INVERT_Y
|
||||
uv.y = 1.0 - uv.y;
|
||||
#endif
|
||||
#ifdef VERTICAL
|
||||
return uv;
|
||||
}
|
||||
|
||||
float2 uddRotateUV(float2 uv)
|
||||
{
|
||||
#ifdef ROTATE90
|
||||
float2 tmp = uv;
|
||||
uv.x = tmp.y;
|
||||
uv.y = 1.0 - tmp.x;
|
||||
#elif ROTATE180
|
||||
uv.x = 1.0 - uv.x;
|
||||
uv.y = 1.0 - uv.y;
|
||||
#elif ROTATE270
|
||||
float2 tmp = uv;
|
||||
uv.x = 1.0 - tmp.y;
|
||||
uv.y = tmp.x;
|
||||
#endif
|
||||
return uv;
|
||||
}
|
||||
|
||||
inline void uddToLinearIfNeeded(inout float3 rgb)
|
||||
inline void uddConvertToLinearIfNeeded(inout fixed3 rgb)
|
||||
{
|
||||
if (!IsGammaSpace()) {
|
||||
rgb = GammaToLinearSpace(rgb);
|
||||
}
|
||||
}
|
||||
|
||||
inline fixed4 uddGetTexture(sampler2D tex, float2 uv)
|
||||
inline fixed4 uddGetScreenTexture(sampler2D tex, float2 uv)
|
||||
{
|
||||
uddInvertUV(uv);
|
||||
fixed4 c = tex2D(tex, uv);
|
||||
uddToLinearIfNeeded(c.rgb);
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,79 +0,0 @@
|
||||
Shader "uDesktopDuplication/Cursor"
|
||||
{
|
||||
|
||||
Properties
|
||||
{
|
||||
_MainTex ("Texture", 2D) = "white" {}
|
||||
}
|
||||
|
||||
SubShader
|
||||
{
|
||||
|
||||
Tags { "RenderType"="Opaque" }
|
||||
|
||||
ZWrite Off
|
||||
Offset -1, -1
|
||||
|
||||
CGINCLUDE
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
#include "./uDD_Common.cginc"
|
||||
|
||||
half4 _PositionScale;
|
||||
#define _PointerX _PositionScale.x
|
||||
#define _PointerY _PositionScale.y
|
||||
#define _PointerWidth _PositionScale.z
|
||||
#define _PointerHeight _PositionScale.w
|
||||
|
||||
struct appdata
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 vertex : SV_POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
sampler2D _MainTex;
|
||||
|
||||
v2f vert(appdata v)
|
||||
{
|
||||
v2f o;
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.uv = v.uv;
|
||||
return o;
|
||||
}
|
||||
|
||||
fixed4 frag(v2f i) : SV_Target
|
||||
{
|
||||
uddInvertUV(i.uv);
|
||||
i.uv.x = (i.uv.x - _PointerX) / _PointerWidth;
|
||||
i.uv.y = (i.uv.y - _PointerY) / _PointerHeight;
|
||||
fixed4 color = tex2D(_MainTex, i.uv);
|
||||
color.a *= step(0, i.uv.x) * step(0, i.uv.y) * step(i.uv.x, 1) * step(i.uv.y, 1);
|
||||
clip(color.a - 0.01);
|
||||
uddToLinearIfNeeded(color.rgb);
|
||||
return color;
|
||||
}
|
||||
|
||||
ENDCG
|
||||
|
||||
Pass
|
||||
{
|
||||
CGPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
#pragma multi_compile ___ INVERT_X
|
||||
#pragma multi_compile ___ INVERT_Y
|
||||
#pragma multi_compile ___ VERTICAL
|
||||
ENDCG
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Fallback "Unlit/Texture"
|
||||
|
||||
}
|
||||
13
Assets/uDesktopDuplication/Shaders/uDD_Params.cginc
Normal file
13
Assets/uDesktopDuplication/Shaders/uDD_Params.cginc
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef UDD_PARAMS_CGINC
|
||||
#define UDD_PARAMS_CGINC
|
||||
|
||||
sampler2D _MainTex;
|
||||
fixed4 _Color;
|
||||
sampler2D _CursorTex;
|
||||
half4 _CursorPositionScale;
|
||||
|
||||
#ifndef SURFACE_SHADER
|
||||
float4 _MainTex_ST;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,6 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d224cadaa1bfa2d46a6224ce2a1443c7
|
||||
timeCreated: 1477847636
|
||||
guid: 54044418ab4785c4a84dd4be507db6c2
|
||||
timeCreated: 1478266678
|
||||
licenseType: Pro
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
@@ -7,11 +7,14 @@ Properties
|
||||
_MainTex ("Albedo (RGB)", 2D) = "white" {}
|
||||
_Glossiness ("Smoothness", Range(0, 1)) = 0.5
|
||||
_Metallic ("Metallic", Range(0, 1)) = 0.0
|
||||
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
|
||||
}
|
||||
|
||||
SubShader
|
||||
{
|
||||
Tags { "RenderType"="Opaque" }
|
||||
|
||||
Cull [_Cull]
|
||||
|
||||
CGPROGRAM
|
||||
|
||||
@@ -20,23 +23,18 @@ SubShader
|
||||
#pragma multi_compile ___ INVERT_X
|
||||
#pragma multi_compile ___ INVERT_Y
|
||||
#pragma multi_compile ___ VERTICAL
|
||||
#pragma multi_compile ___ ROTATE90 ROTATE180 ROTATE270
|
||||
|
||||
#define SURFACE_SHADER
|
||||
#include "./uDD_Common.cginc"
|
||||
|
||||
sampler2D _MainTex;
|
||||
|
||||
struct Input
|
||||
{
|
||||
float2 uv_MainTex;
|
||||
};
|
||||
#include "./uDD_Params.cginc"
|
||||
|
||||
half _Glossiness;
|
||||
half _Metallic;
|
||||
fixed4 _Color;
|
||||
|
||||
void surf(Input IN, inout SurfaceOutputStandard o)
|
||||
{
|
||||
fixed4 c = uddGetTexture(_MainTex, IN.uv_MainTex) * _Color;
|
||||
fixed4 c = uddGetScreenTextureWithCursor(_MainTex, _CursorTex, IN.uv_MainTex, _CursorPositionScale) * _Color;
|
||||
o.Albedo = c.rgb;
|
||||
o.Metallic = _Metallic;
|
||||
o.Smoothness = _Glossiness;
|
||||
|
||||
@@ -5,6 +5,9 @@ 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(Off, Front, Back)] _Cull("Culling", Int) = 2
|
||||
}
|
||||
|
||||
SubShader
|
||||
@@ -12,30 +15,23 @@ SubShader
|
||||
|
||||
Tags { "RenderType"="Opaque" }
|
||||
|
||||
Cull [_Cull]
|
||||
|
||||
CGINCLUDE
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
#include "./uDD_Common.cginc"
|
||||
#include "./uDD_Params.cginc"
|
||||
|
||||
struct appdata
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 vertex : SV_POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
sampler2D _MainTex;
|
||||
float4 _MainTex_ST;
|
||||
fixed4 _Color;
|
||||
fixed _Radius;
|
||||
|
||||
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
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
|
||||
return o;
|
||||
@@ -43,7 +39,7 @@ v2f vert(appdata v)
|
||||
|
||||
fixed4 frag(v2f i) : SV_Target
|
||||
{
|
||||
return uddGetTexture(_MainTex, i.uv) * _Color;
|
||||
return uddGetScreenTextureWithCursor(_MainTex, _CursorTex, i.uv, _CursorPositionScale);
|
||||
}
|
||||
|
||||
ENDCG
|
||||
@@ -56,6 +52,8 @@ Pass
|
||||
#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
|
||||
ENDCG
|
||||
}
|
||||
|
||||
@@ -63,4 +61,4 @@ Pass
|
||||
|
||||
Fallback "Unlit/Texture"
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ Properties
|
||||
_Color ("Color", Color) = (1, 1, 1, 1)
|
||||
_MainTex ("Texture", 2D) = "white" {}
|
||||
_Mask ("Mask", Range(0, 1)) = 0.1
|
||||
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
|
||||
}
|
||||
|
||||
SubShader
|
||||
@@ -13,29 +14,15 @@ SubShader
|
||||
|
||||
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
|
||||
|
||||
ZWrite Off
|
||||
Cull [_Cull]
|
||||
ZWrite On
|
||||
Blend SrcAlpha OneMinusSrcAlpha
|
||||
|
||||
CGINCLUDE
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
#include "./uDD_Common.cginc"
|
||||
#include "./uDD_Params.cginc"
|
||||
|
||||
struct appdata
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 vertex : SV_POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
sampler2D _MainTex;
|
||||
float4 _MainTex_ST;
|
||||
fixed4 _Color;
|
||||
fixed _Mask;
|
||||
|
||||
v2f vert(appdata v)
|
||||
@@ -48,9 +35,9 @@ v2f vert(appdata v)
|
||||
|
||||
fixed4 frag(v2f i) : SV_Target
|
||||
{
|
||||
fixed4 tex = uddGetTexture(_MainTex, i.uv);
|
||||
fixed4 tex = uddGetScreenTextureWithCursor(_MainTex, _CursorTex, i.uv, _CursorPositionScale);
|
||||
fixed alpha = pow((tex.r + tex.g + tex.b) / 3.0, _Mask);
|
||||
return fixed4(tex.rgb * _Color.rgb, alpha);
|
||||
return fixed4(tex.rgb * _Color.rgb, alpha * _Color.a);
|
||||
}
|
||||
|
||||
ENDCG
|
||||
@@ -63,6 +50,7 @@ Pass
|
||||
#pragma multi_compile ___ INVERT_X
|
||||
#pragma multi_compile ___ INVERT_Y
|
||||
#pragma multi_compile ___ VERTICAL
|
||||
#pragma multi_compile ___ ROTATE90 ROTATE180 ROTATE270
|
||||
ENDCG
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ Properties
|
||||
{
|
||||
_Color ("Color", Color) = (1, 1, 1, 1)
|
||||
_MainTex ("Texture", 2D) = "white" {}
|
||||
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
|
||||
}
|
||||
|
||||
SubShader
|
||||
@@ -12,29 +13,14 @@ SubShader
|
||||
|
||||
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
|
||||
|
||||
ZWrite Off
|
||||
Cull [_Cull]
|
||||
ZWrite On
|
||||
Blend SrcAlpha OneMinusSrcAlpha
|
||||
|
||||
CGINCLUDE
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
#include "./uDD_Common.cginc"
|
||||
|
||||
struct appdata
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 vertex : SV_POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
sampler2D _MainTex;
|
||||
float4 _MainTex_ST;
|
||||
fixed4 _Color;
|
||||
#include "./uDD_Params.cginc"
|
||||
|
||||
v2f vert(appdata v)
|
||||
{
|
||||
@@ -46,7 +32,7 @@ v2f vert(appdata v)
|
||||
|
||||
fixed4 frag(v2f i) : SV_Target
|
||||
{
|
||||
return fixed4(uddGetTexture(_MainTex, i.uv).rgb, 1.0) * _Color;
|
||||
return fixed4(uddGetScreenTextureWithCursor(_MainTex, _CursorTex, i.uv, _CursorPositionScale).rgb, 1.0) * _Color;
|
||||
}
|
||||
|
||||
ENDCG
|
||||
@@ -59,6 +45,7 @@ Pass
|
||||
#pragma multi_compile ___ INVERT_X
|
||||
#pragma multi_compile ___ INVERT_Y
|
||||
#pragma multi_compile ___ VERTICAL
|
||||
#pragma multi_compile ___ ROTATE90 ROTATE180 ROTATE270
|
||||
ENDCG
|
||||
}
|
||||
|
||||
|
||||
@@ -1,405 +0,0 @@
|
||||
#include <d3d11.h>
|
||||
#include <dxgi1_2.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "IUnityInterface.h"
|
||||
#include "IUnityGraphics.h"
|
||||
#include "IUnityGraphicsD3D11.h"
|
||||
|
||||
#pragma comment(lib, "dxgi.lib")
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
struct Monitor
|
||||
{
|
||||
IDXGIOutputDuplication* deskDupl = nullptr;
|
||||
ID3D11Texture2D* texture = nullptr;
|
||||
DXGI_OUTPUT_DESC outputDesc;
|
||||
MONITORINFOEX monitorInfo;
|
||||
|
||||
struct Pointer
|
||||
{
|
||||
bool isVisible = false;
|
||||
int x = -1;
|
||||
int y = -1;
|
||||
BYTE* apiBuffer = nullptr;
|
||||
UINT apiBufferSize = 0;
|
||||
BYTE* bgra32Buffer = nullptr;
|
||||
UINT bgra32BufferSize = 0;
|
||||
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo;
|
||||
};
|
||||
Pointer pointer;
|
||||
};
|
||||
|
||||
IUnityInterfaces* g_unity = nullptr;
|
||||
int g_timeout = 10;
|
||||
HRESULT g_errorCode = 0;
|
||||
std::string g_errorMessage = "";
|
||||
std::vector<Monitor> g_monitors;
|
||||
}
|
||||
|
||||
|
||||
extern "C"
|
||||
{
|
||||
void FinalizeDuplication()
|
||||
{
|
||||
for (auto& monitor : g_monitors)
|
||||
{
|
||||
monitor.deskDupl->Release();
|
||||
}
|
||||
g_monitors.clear();
|
||||
}
|
||||
|
||||
void InitializeDuplication()
|
||||
{
|
||||
FinalizeDuplication();
|
||||
|
||||
IDXGIFactory1* factory;
|
||||
CreateDXGIFactory1(__uuidof(IDXGIFactory1), reinterpret_cast<void**>(&factory));
|
||||
|
||||
// Check all display adapters.
|
||||
IDXGIAdapter1* adapter;
|
||||
for (int i = 0; (factory->EnumAdapters1(i, &adapter) != DXGI_ERROR_NOT_FOUND); ++i)
|
||||
{
|
||||
// Search the main monitor from all outputs.
|
||||
IDXGIOutput* output;
|
||||
for (int j = 0; (adapter->EnumOutputs(j, &output) != DXGI_ERROR_NOT_FOUND); ++j)
|
||||
{
|
||||
Monitor monitor;
|
||||
|
||||
output->GetDesc(&monitor.outputDesc);
|
||||
monitor.monitorInfo.cbSize = sizeof(MONITORINFOEX);
|
||||
GetMonitorInfo(monitor.outputDesc.Monitor, &monitor.monitorInfo);
|
||||
|
||||
auto device = g_unity->Get<IUnityGraphicsD3D11>()->GetDevice();
|
||||
auto output1 = reinterpret_cast<IDXGIOutput1*>(output);
|
||||
output1->DuplicateOutput(device, &monitor.deskDupl);
|
||||
output->Release();
|
||||
|
||||
g_monitors.push_back(monitor);
|
||||
}
|
||||
|
||||
adapter->Release();
|
||||
}
|
||||
|
||||
factory->Release();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces)
|
||||
{
|
||||
g_unity = unityInterfaces;
|
||||
InitializeDuplication();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UnityPluginUnload()
|
||||
{
|
||||
g_unity = nullptr;
|
||||
FinalizeDuplication();
|
||||
}
|
||||
|
||||
bool IsValidMonitorId(int id)
|
||||
{
|
||||
return id >= 0 && id < g_monitors.size();
|
||||
}
|
||||
|
||||
bool UpdateMouse(const DXGI_OUTDUPL_FRAME_INFO& frameInfo, Monitor& monitor)
|
||||
{
|
||||
auto& pointer = monitor.pointer;
|
||||
pointer.isVisible = frameInfo.PointerPosition.Visible != 0;
|
||||
pointer.x = frameInfo.PointerPosition.Position.x;
|
||||
pointer.y = frameInfo.PointerPosition.Position.y;
|
||||
if (frameInfo.PointerShapeBufferSize != 0)
|
||||
{
|
||||
if (frameInfo.PointerShapeBufferSize > pointer.apiBufferSize)
|
||||
{
|
||||
if (pointer.apiBuffer) delete[] pointer.apiBuffer;
|
||||
pointer.apiBuffer = new BYTE[frameInfo.PointerShapeBufferSize];
|
||||
pointer.apiBufferSize = frameInfo.PointerShapeBufferSize;
|
||||
}
|
||||
if (pointer.apiBuffer)
|
||||
{
|
||||
UINT bufferSize;
|
||||
monitor.deskDupl->GetFramePointerShape(
|
||||
frameInfo.PointerShapeBufferSize,
|
||||
reinterpret_cast<void*>(pointer.apiBuffer),
|
||||
&bufferSize,
|
||||
&pointer.shapeInfo);
|
||||
}
|
||||
|
||||
// Convert buffer give by API into BGRA32
|
||||
const auto bgraBufferSize = pointer.shapeInfo.Width * pointer.shapeInfo.Height * 4;
|
||||
if (bgraBufferSize > pointer.bgra32BufferSize)
|
||||
{
|
||||
if (pointer.bgra32Buffer) delete[] pointer.bgra32Buffer;
|
||||
pointer.bgra32Buffer = new BYTE[bgraBufferSize];
|
||||
pointer.bgra32BufferSize = bgraBufferSize;
|
||||
}
|
||||
if (pointer.bgra32Buffer)
|
||||
{
|
||||
const auto w = pointer.shapeInfo.Width;
|
||||
const auto h = pointer.shapeInfo.Height;
|
||||
const auto p = pointer.shapeInfo.Pitch;
|
||||
|
||||
// If masked, copy the desktop image and merge it with masked image.
|
||||
const bool isMasked =
|
||||
pointer.shapeInfo.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME ||
|
||||
pointer.shapeInfo.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR;
|
||||
if (isMasked)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
desc.Width = w;
|
||||
desc.Height = h;
|
||||
desc.MipLevels = 1;
|
||||
desc.ArraySize = 1;
|
||||
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.SampleDesc.Quality = 0;
|
||||
desc.Usage = D3D11_USAGE_STAGING;
|
||||
desc.BindFlags = 0;
|
||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
desc.MiscFlags = 0;
|
||||
|
||||
ID3D11DeviceContext* context;
|
||||
auto device = g_unity->Get<IUnityGraphicsD3D11>()->GetDevice();
|
||||
device->GetImmediateContext(&context);
|
||||
|
||||
ID3D11Texture2D* texture = nullptr;
|
||||
hr = device->CreateTexture2D(&desc, nullptr, &texture);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D11_BOX box;
|
||||
box.left = pointer.x;
|
||||
box.top = pointer.y;
|
||||
box.right = pointer.x + w;
|
||||
box.bottom = pointer.y + h;
|
||||
context->CopySubresourceRegion(texture, 0, 0, 0, 0, monitor.texture, 0, &box);
|
||||
|
||||
IDXGISurface* surface = nullptr;
|
||||
hr = texture->QueryInterface(__uuidof(IDXGISurface), (void**)&surface);
|
||||
texture->Release();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DXGI_MAPPED_RECT mappedSurface;
|
||||
hr = surface->Map(&mappedSurface, DXGI_MAP_READ);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
surface->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finally, get the texture behind the mouse pointer.
|
||||
const auto desktop32 = reinterpret_cast<UINT*>(mappedSurface.pBits);
|
||||
const auto desktopPitch = mappedSurface.Pitch / sizeof(UINT);
|
||||
|
||||
// Access RGBA values at the same time
|
||||
auto output32 = reinterpret_cast<UINT*>(pointer.bgra32Buffer);
|
||||
|
||||
if (pointer.shapeInfo.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME)
|
||||
{
|
||||
for (UINT row = 0; row < h; ++row)
|
||||
{
|
||||
for (UINT col = 0; col < w; ++col)
|
||||
{
|
||||
const int i = row * w + col;
|
||||
const UINT mask = 0b10000000 >> (i % 8);
|
||||
const BYTE andMask = pointer.apiBuffer[col / 8 + row * p] & mask;
|
||||
const BYTE xorMask = pointer.apiBuffer[col / 8 + (row + h / 2) * p] & mask;
|
||||
const UINT andMask32 = andMask ? 0xFFFFFFFF : 0xFF000000;
|
||||
const UINT xorMask32 = xorMask ? 0x00FFFFFF : 0x00000000;
|
||||
output32[i] = (desktop32[row * desktopPitch + col] & andMask32) ^ xorMask32;
|
||||
}
|
||||
}
|
||||
}
|
||||
else // DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR
|
||||
{
|
||||
const auto buffer32 = reinterpret_cast<UINT*>(pointer.apiBuffer);
|
||||
|
||||
for (UINT row = 0; row < h; ++row)
|
||||
{
|
||||
for (UINT col = 0; col < w; ++col)
|
||||
{
|
||||
const int i = row * w + col;
|
||||
const int j = row * p / sizeof(UINT) + col;
|
||||
|
||||
UINT MaskVal = 0xFF000000 & buffer32[j];
|
||||
|
||||
const UINT mask = 0xFF000000 & buffer[j];
|
||||
if (mask) {
|
||||
output[i] = 0xFF000000 ^ buffer[j];
|
||||
} else {
|
||||
output[i] = 0x00000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hr = surface->Unmap();
|
||||
surface->Release();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UNITY_INTERFACE_API OnRenderEvent(int id)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return;
|
||||
auto& monitor = g_monitors[id];
|
||||
|
||||
if (monitor.deskDupl == nullptr || monitor.texture == nullptr) return;
|
||||
|
||||
IDXGIResource* resource = nullptr;
|
||||
DXGI_OUTDUPL_FRAME_INFO frameInfo;
|
||||
|
||||
g_errorCode = monitor.deskDupl->AcquireNextFrame(g_timeout, &frameInfo, &resource);
|
||||
if (FAILED(g_errorCode))
|
||||
{
|
||||
if (g_errorCode == DXGI_ERROR_ACCESS_LOST)
|
||||
{
|
||||
InitializeDuplication();
|
||||
g_errorMessage = "[IDXGIOutputDuplication::AcquireNextFrame()] Access lost.";
|
||||
}
|
||||
else
|
||||
{
|
||||
g_errorMessage = "[IDXGIOutputDuplication::AcquireNextFrame()] Unknown error.";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ID3D11Texture2D* texture;
|
||||
resource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&texture));
|
||||
|
||||
ID3D11DeviceContext* context;
|
||||
auto device = g_unity->Get<IUnityGraphicsD3D11>()->GetDevice();
|
||||
device->GetImmediateContext(&context);
|
||||
context->CopyResource(monitor.texture, texture);
|
||||
|
||||
if (!UpdateMouse(frameInfo, monitor))
|
||||
{
|
||||
g_errorCode = -999;
|
||||
g_errorMessage = "[UpdateMouse()] failed.";
|
||||
}
|
||||
|
||||
resource->Release();
|
||||
monitor.deskDupl->ReleaseFrame();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT UnityRenderingEvent UNITY_INTERFACE_API GetRenderEventFunc()
|
||||
{
|
||||
return OnRenderEvent;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT size_t UNITY_INTERFACE_API GetMonitorCount()
|
||||
{
|
||||
return g_monitors.size();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetTimeout(int timeout)
|
||||
{
|
||||
g_timeout = timeout;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API GetName(int id, char* buf, int len)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return;
|
||||
strcpy_s(buf, len, g_monitors[id].monitorInfo.szDevice);
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API IsPrimary(int id)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return false;
|
||||
return g_monitors[id].monitorInfo.dwFlags == MONITORINFOF_PRIMARY;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetWidth(int id)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return -1;
|
||||
const auto rect = g_monitors[id].monitorInfo.rcMonitor;
|
||||
return rect.right - rect.left;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetHeight(int id)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return -1;
|
||||
const auto rect = g_monitors[id].monitorInfo.rcMonitor;
|
||||
return rect.bottom - rect.top;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API IsPointerVisible(int id)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return false;
|
||||
return g_monitors[id].pointer.isVisible;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetPointerX(int id)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return -1;
|
||||
return g_monitors[id].pointer.x;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetPointerY(int id)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return -1;
|
||||
return g_monitors[id].pointer.y;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetPointerShapeWidth(int id)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return -1;
|
||||
return g_monitors[id].pointer.shapeInfo.Width;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetPointerShapeHeight(int id)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return -1;
|
||||
return g_monitors[id].pointer.shapeInfo.Height;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetPointerShapePitch(int id)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return -1;
|
||||
return g_monitors[id].pointer.shapeInfo.Pitch;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetPointerShapeType(int id)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return -1;
|
||||
return g_monitors[id].pointer.shapeInfo.Type;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UpdatePointerTexture(int id, ID3D11Texture2D* ptr)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return;
|
||||
const auto& pointer = g_monitors[id].pointer;
|
||||
auto device = g_unity->Get<IUnityGraphicsD3D11>()->GetDevice();
|
||||
ID3D11DeviceContext* context;
|
||||
device->GetImmediateContext(&context);
|
||||
context->UpdateSubresource(ptr, 0, nullptr, pointer.bgra32Buffer, pointer.shapeInfo.Pitch, 0);
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetTexturePtr(int id, void* texture)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return;
|
||||
g_monitors[id].texture = reinterpret_cast<ID3D11Texture2D*>(texture);
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetErrorCode()
|
||||
{
|
||||
return static_cast<int>(g_errorCode);
|
||||
}
|
||||
}
|
||||
15
Plugins/uDesktopDuplication/uDesktopDuplication/Common.h
Normal file
15
Plugins/uDesktopDuplication/uDesktopDuplication/Common.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#include <memory>
|
||||
#include "IUnityInterface.h"
|
||||
|
||||
class MonitorManager;
|
||||
|
||||
IUnityInterfaces* GetUnity();
|
||||
ID3D11Device* GetDevice();
|
||||
const std::unique_ptr<MonitorManager>& GetMonitorManager();
|
||||
|
||||
enum class Message
|
||||
{
|
||||
None = -1,
|
||||
Reinitialized = 0,
|
||||
};
|
||||
void SendMessageToUnity(Message message);
|
||||
269
Plugins/uDesktopDuplication/uDesktopDuplication/Cursor.cpp
Normal file
269
Plugins/uDesktopDuplication/uDesktopDuplication/Cursor.cpp
Normal file
@@ -0,0 +1,269 @@
|
||||
#include <d3d11.h>
|
||||
#include "Common.h"
|
||||
#include "MonitorManager.h"
|
||||
#include "Monitor.h"
|
||||
#include "Cursor.h"
|
||||
|
||||
|
||||
Cursor::Cursor(Monitor* monitor)
|
||||
: monitor_(monitor)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
if (frameInfo.LastMouseUpdateTime.QuadPart == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (frameInfo.PointerShapeBufferSize == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Increase the buffer size if needed
|
||||
if (frameInfo.PointerShapeBufferSize > apiBufferSize_)
|
||||
{
|
||||
if (apiBuffer_) delete[] apiBuffer_;
|
||||
apiBuffer_ = new BYTE[frameInfo.PointerShapeBufferSize];
|
||||
apiBufferSize_ = frameInfo.PointerShapeBufferSize;
|
||||
}
|
||||
if (apiBuffer_ == nullptr) return;
|
||||
|
||||
// Get mouse pointer information
|
||||
UINT bufferSize;
|
||||
const auto hr = monitor_->GetDeskDupl()->GetFramePointerShape(
|
||||
frameInfo.PointerShapeBufferSize,
|
||||
reinterpret_cast<void*>(apiBuffer_),
|
||||
&bufferSize,
|
||||
&shapeInfo_);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
delete[] apiBuffer_;
|
||||
apiBuffer_ = nullptr;
|
||||
apiBufferSize_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Cursor::UpdateTexture()
|
||||
{
|
||||
// cursor type
|
||||
const bool isMono = GetType() == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME;
|
||||
const bool isColorMask = GetType() == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR;
|
||||
|
||||
// Size
|
||||
const auto w = GetWidth();
|
||||
const auto h = GetHeight();
|
||||
const auto p = GetPitch();
|
||||
|
||||
// Convert the buffer given by API into BGRA32
|
||||
const UINT bgraBufferSize = w * h * 4;
|
||||
if (bgraBufferSize > bgra32BufferSize_)
|
||||
{
|
||||
if (bgra32Buffer_) delete[] bgra32Buffer_;
|
||||
bgra32Buffer_ = new BYTE[bgraBufferSize];
|
||||
bgra32BufferSize_ = bgraBufferSize;
|
||||
}
|
||||
if (bgra32Buffer_ == nullptr) return;
|
||||
|
||||
// If masked, copy the desktop image and merge it with masked image.
|
||||
if (isMono || isColorMask)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
desc.Width = w;
|
||||
desc.Height = h;
|
||||
desc.MipLevels = 1;
|
||||
desc.ArraySize = 1;
|
||||
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.SampleDesc.Quality = 0;
|
||||
desc.Usage = D3D11_USAGE_STAGING;
|
||||
desc.BindFlags = 0;
|
||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
desc.MiscFlags = 0;
|
||||
|
||||
ID3D11Texture2D* texture = nullptr;
|
||||
hr = GetDevice()->CreateTexture2D(&desc, nullptr, &texture);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
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;
|
||||
GetDevice()->GetImmediateContext(&context);
|
||||
context->CopySubresourceRegion(texture, 0, 0, 0, 0, monitor_->GetUnityTexture(), 0, &box);
|
||||
context->Release();
|
||||
|
||||
IDXGISurface* surface = nullptr;
|
||||
hr = texture->QueryInterface(__uuidof(IDXGISurface), (void**)&surface);
|
||||
texture->Release();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
BYTE mask = 0x80;
|
||||
for (int col = 0; col < w; ++col)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
else // DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR
|
||||
{
|
||||
const auto buffer32 = reinterpret_cast<UINT*>(apiBuffer_);
|
||||
|
||||
for (int row = 0; row < h; ++row)
|
||||
{
|
||||
for (int col = 0; col < w; ++col)
|
||||
{
|
||||
const int i = row * w + col;
|
||||
const int j = row * p / sizeof(UINT) + col;
|
||||
|
||||
UINT mask = 0xFF000000 & buffer32[j];
|
||||
if (mask)
|
||||
{
|
||||
output32[i] = (desktop32[row * desktopPitch + col] ^ buffer32[j]) | 0xFF000000;
|
||||
}
|
||||
else
|
||||
{
|
||||
output32[i] = buffer32[j] | 0xFF000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void Cursor::GetTexture(ID3D11Texture2D* texture)
|
||||
{
|
||||
if (bgra32Buffer_ == nullptr) return;
|
||||
ID3D11DeviceContext* context;
|
||||
GetDevice()->GetImmediateContext(&context);
|
||||
context->UpdateSubresource(texture, 0, nullptr, bgra32Buffer_, shapeInfo_.Width * 4, 0);
|
||||
context->Release();
|
||||
}
|
||||
|
||||
|
||||
bool Cursor::IsVisible() const
|
||||
{
|
||||
return isVisible_;
|
||||
}
|
||||
|
||||
|
||||
int Cursor::GetX() const
|
||||
{
|
||||
return x_;
|
||||
}
|
||||
|
||||
|
||||
int Cursor::GetY() const
|
||||
{
|
||||
return y_;
|
||||
}
|
||||
|
||||
|
||||
int Cursor::GetWidth() const
|
||||
{
|
||||
return shapeInfo_.Width;
|
||||
}
|
||||
|
||||
|
||||
int Cursor::GetHeight() const
|
||||
{
|
||||
return (shapeInfo_.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME) ?
|
||||
shapeInfo_.Height / 2 :
|
||||
shapeInfo_.Height;
|
||||
}
|
||||
|
||||
|
||||
int Cursor::GetPitch() const
|
||||
{
|
||||
return shapeInfo_.Pitch;
|
||||
}
|
||||
|
||||
|
||||
int Cursor::GetType() const
|
||||
{
|
||||
return shapeInfo_.Type;
|
||||
}
|
||||
35
Plugins/uDesktopDuplication/uDesktopDuplication/Cursor.h
Normal file
35
Plugins/uDesktopDuplication/uDesktopDuplication/Cursor.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#include <d3d11.h>
|
||||
#include <dxgi1_2.h>
|
||||
#include <memory>
|
||||
|
||||
class Monitor;
|
||||
|
||||
class Cursor
|
||||
{
|
||||
public:
|
||||
explicit Cursor(Monitor* monitor);
|
||||
~Cursor();
|
||||
void UpdateBuffer(const DXGI_OUTDUPL_FRAME_INFO& frameInfo);
|
||||
void UpdateTexture();
|
||||
void GetTexture(ID3D11Texture2D* texture);
|
||||
|
||||
bool IsVisible() const;
|
||||
int GetX() const;
|
||||
int GetY() const;
|
||||
int GetWidth() const;
|
||||
int GetHeight() const;
|
||||
int GetPitch() const;
|
||||
int GetType() const;
|
||||
|
||||
private:
|
||||
Monitor* monitor_;
|
||||
bool isVisible_ = false;
|
||||
int x_ = -1;
|
||||
int y_ = -1;
|
||||
BYTE* apiBuffer_ = nullptr;
|
||||
UINT apiBufferSize_ = 0;
|
||||
BYTE* bgra32Buffer_ = nullptr;
|
||||
UINT bgra32BufferSize_ = 0;
|
||||
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo_;
|
||||
LARGE_INTEGER timestamp_;
|
||||
};
|
||||
225
Plugins/uDesktopDuplication/uDesktopDuplication/Monitor.cpp
Normal file
225
Plugins/uDesktopDuplication/uDesktopDuplication/Monitor.cpp
Normal file
@@ -0,0 +1,225 @@
|
||||
#include <d3d11.h>
|
||||
#include <ShellScalingAPI.h>
|
||||
#include "Common.h"
|
||||
#include "Cursor.h"
|
||||
#include "MonitorManager.h"
|
||||
#include "Monitor.h"
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
deskDupl_->Release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HRESULT Monitor::Render(UINT timeout)
|
||||
{
|
||||
if (deskDupl_ == nullptr)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
if (unityTexture_ == nullptr) return 0;
|
||||
|
||||
IDXGIResource* resource = nullptr;
|
||||
DXGI_OUTDUPL_FRAME_INFO frameInfo;
|
||||
|
||||
const auto hr = deskDupl_->AcquireNextFrame(timeout, &frameInfo, &resource);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
switch (hr)
|
||||
{
|
||||
case DXGI_ERROR_ACCESS_LOST:
|
||||
// If any monitor setting has changed (e.g. monitor size has changed),
|
||||
// it is necessary to re-initialize monitors.
|
||||
state_ = State::AccessLost;
|
||||
break;
|
||||
case DXGI_ERROR_WAIT_TIMEOUT:
|
||||
break;
|
||||
case DXGI_ERROR_INVALID_CALL:
|
||||
break;
|
||||
case E_INVALIDARG:
|
||||
break;
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
ID3D11Texture2D* texture;
|
||||
resource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&texture));
|
||||
|
||||
ID3D11DeviceContext* context;
|
||||
GetDevice()->GetImmediateContext(&context);
|
||||
context->CopyResource(unityTexture_, texture);
|
||||
context->Release();
|
||||
|
||||
cursor_->UpdateBuffer(frameInfo);
|
||||
cursor_->UpdateTexture();
|
||||
|
||||
resource->Release();
|
||||
deskDupl_->ReleaseFrame();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetId() const
|
||||
{
|
||||
return id_;
|
||||
}
|
||||
|
||||
|
||||
MonitorState Monitor::GetState() const
|
||||
{
|
||||
return state_;
|
||||
}
|
||||
|
||||
|
||||
void Monitor::SetUnityTexture(ID3D11Texture2D* texture)
|
||||
{
|
||||
unityTexture_ = texture;
|
||||
}
|
||||
|
||||
|
||||
ID3D11Texture2D* Monitor::GetUnityTexture() const
|
||||
{
|
||||
return unityTexture_;
|
||||
}
|
||||
|
||||
|
||||
IDXGIOutputDuplication* Monitor::GetDeskDupl()
|
||||
{
|
||||
return deskDupl_;
|
||||
}
|
||||
|
||||
|
||||
const std::unique_ptr<Cursor>& Monitor::GetCursor()
|
||||
{
|
||||
return cursor_;
|
||||
}
|
||||
|
||||
|
||||
void Monitor::GetCursorTexture(ID3D11Texture2D* texture)
|
||||
{
|
||||
cursor_->GetTexture(texture);
|
||||
}
|
||||
|
||||
|
||||
void Monitor::GetName(char* buf, int len) const
|
||||
{
|
||||
strcpy_s(buf, len, monitorInfo_.szDevice);
|
||||
}
|
||||
|
||||
|
||||
bool Monitor::IsPrimary() const
|
||||
{
|
||||
return monitorInfo_.dwFlags == MONITORINFOF_PRIMARY;
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetLeft() const
|
||||
{
|
||||
return outputDesc_.DesktopCoordinates.left;
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetRight() const
|
||||
{
|
||||
return outputDesc_.DesktopCoordinates.right;
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetTop() const
|
||||
{
|
||||
return outputDesc_.DesktopCoordinates.top;
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetBottom() const
|
||||
{
|
||||
return outputDesc_.DesktopCoordinates.bottom;
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetRotation() const
|
||||
{
|
||||
return static_cast<int>(outputDesc_.Rotation);
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetDpiX() const
|
||||
{
|
||||
return static_cast<int>(dpiX_);
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetDpiY() const
|
||||
{
|
||||
return static_cast<int>(dpiY_);
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetWidth() const
|
||||
{
|
||||
const auto rect = monitorInfo_.rcMonitor;
|
||||
return rect.right - rect.left;
|
||||
}
|
||||
|
||||
|
||||
int Monitor::GetHeight() const
|
||||
{
|
||||
const auto rect = monitorInfo_.rcMonitor;
|
||||
return rect.bottom - rect.top;
|
||||
}
|
||||
58
Plugins/uDesktopDuplication/uDesktopDuplication/Monitor.h
Normal file
58
Plugins/uDesktopDuplication/uDesktopDuplication/Monitor.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#include <d3d11.h>
|
||||
#include <dxgi1_2.h>
|
||||
#include <memory>
|
||||
|
||||
class Cursor;
|
||||
|
||||
enum class MonitorState
|
||||
{
|
||||
NotSet = -1,
|
||||
Available = 0,
|
||||
InvalidArg = 1,
|
||||
AccessDenied = 2,
|
||||
Unsupported = 3,
|
||||
CurrentlyNotAvailable = 4,
|
||||
SessionDisconnected = 5,
|
||||
AccessLost = 6,
|
||||
};
|
||||
|
||||
class Monitor
|
||||
{
|
||||
public:
|
||||
using State = MonitorState;
|
||||
|
||||
explicit Monitor(int id);
|
||||
~Monitor();
|
||||
HRESULT Initialize(IDXGIOutput* output);
|
||||
HRESULT Render(UINT timeout = 0);
|
||||
void GetCursorTexture(ID3D11Texture2D* texture);
|
||||
|
||||
public:
|
||||
int GetId() const;
|
||||
State GetState() const;
|
||||
void SetUnityTexture(ID3D11Texture2D* texture);
|
||||
ID3D11Texture2D* GetUnityTexture() const;
|
||||
void GetName(char* buf, int len) const;
|
||||
bool IsPrimary() const;
|
||||
int GetLeft() const;
|
||||
int GetRight() const;
|
||||
int GetTop() const;
|
||||
int GetBottom() const;
|
||||
int GetWidth() const;
|
||||
int GetHeight() const;
|
||||
int GetRotation() const;
|
||||
int GetDpiX() const;
|
||||
int GetDpiY() const;
|
||||
IDXGIOutputDuplication* GetDeskDupl();
|
||||
const std::unique_ptr<Cursor>& GetCursor();
|
||||
|
||||
private:
|
||||
int id_ = -1;
|
||||
UINT dpiX_ = -1, dpiY_ = -1;
|
||||
State state_ = State::NotSet;
|
||||
std::unique_ptr<Cursor> cursor_;
|
||||
IDXGIOutputDuplication* deskDupl_ = nullptr;
|
||||
ID3D11Texture2D* unityTexture_ = nullptr;
|
||||
DXGI_OUTPUT_DESC outputDesc_;
|
||||
MONITORINFOEX monitorInfo_;
|
||||
};
|
||||
@@ -0,0 +1,136 @@
|
||||
#include <d3d11.h>
|
||||
#include <dxgi1_2.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
#include "IUnityInterface.h"
|
||||
#include "IUnityGraphicsD3D11.h"
|
||||
|
||||
#include "Common.h"
|
||||
#include "Monitor.h"
|
||||
#include "Cursor.h"
|
||||
#include "MonitorManager.h"
|
||||
|
||||
|
||||
MonitorManager::MonitorManager()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
|
||||
MonitorManager::~MonitorManager()
|
||||
{
|
||||
Finalize();
|
||||
}
|
||||
|
||||
|
||||
void MonitorManager::Initialize()
|
||||
{
|
||||
Finalize();
|
||||
|
||||
// Get factory
|
||||
IDXGIFactory1* factory;
|
||||
CreateDXGIFactory1(__uuidof(IDXGIFactory1), reinterpret_cast<void**>(&factory));
|
||||
|
||||
// Check all display adapters
|
||||
int id = 0;
|
||||
IDXGIAdapter1* adapter;
|
||||
for (int i = 0; (factory->EnumAdapters1(i, &adapter) != DXGI_ERROR_NOT_FOUND); ++i)
|
||||
{
|
||||
// Search the main monitor from all outputs
|
||||
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);
|
||||
monitors_.push_back(monitor);
|
||||
output->Release();
|
||||
}
|
||||
adapter->Release();
|
||||
}
|
||||
factory->Release();
|
||||
}
|
||||
|
||||
|
||||
void MonitorManager::Finalize()
|
||||
{
|
||||
monitors_.clear();
|
||||
}
|
||||
|
||||
|
||||
void MonitorManager::RequireReinitilization()
|
||||
{
|
||||
isReinitializationRequired_ = true;
|
||||
}
|
||||
|
||||
|
||||
void MonitorManager::Reinitialize()
|
||||
{
|
||||
Initialize();
|
||||
SendMessageToUnity(Message::Reinitialized);
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<Monitor> MonitorManager::GetMonitor(int id) const
|
||||
{
|
||||
if (id >= 0 && id < monitors_.size())
|
||||
{
|
||||
return monitors_[id];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void MonitorManager::Update()
|
||||
{
|
||||
if (isReinitializationRequired_)
|
||||
{
|
||||
Reinitialize();
|
||||
isReinitializationRequired_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MonitorManager::SetTimeout(int timeout)
|
||||
{
|
||||
timeout_ = timeout;
|
||||
}
|
||||
|
||||
|
||||
int MonitorManager::GetTimeout() const
|
||||
{
|
||||
return timeout_;
|
||||
}
|
||||
|
||||
|
||||
int MonitorManager::GetMonitorCount() const
|
||||
{
|
||||
return static_cast<int>(monitors_.size());
|
||||
}
|
||||
|
||||
int MonitorManager::GetTotalWidth() const
|
||||
{
|
||||
std::vector<int> lefts, rights;
|
||||
for (const auto& monitor : monitors_)
|
||||
{
|
||||
lefts.push_back(monitor->GetLeft());
|
||||
rights.push_back(monitor->GetRight());
|
||||
}
|
||||
const auto minLeft = *std::min_element(lefts.begin(), lefts.end());
|
||||
const auto maxRight = *std::max_element(rights.begin(), rights.end());
|
||||
return maxRight - minLeft;
|
||||
}
|
||||
|
||||
int MonitorManager::GetTotalHeight() const
|
||||
{
|
||||
std::vector<int> tops, bottoms;
|
||||
for (const auto& monitor : monitors_)
|
||||
{
|
||||
tops.push_back(monitor->GetTop());
|
||||
bottoms.push_back(monitor->GetBottom());
|
||||
}
|
||||
const auto minTop = *std::min_element(tops.begin(), tops.end());
|
||||
const auto maxBottom = *std::max_element(bottoms.begin(), bottoms.end());
|
||||
return maxBottom - minTop;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#include <d3d11.h>
|
||||
#include <dxgi1_2.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
struct IUnityInterfaces;
|
||||
class Monitor;
|
||||
class Cursor;
|
||||
|
||||
class MonitorManager
|
||||
{
|
||||
public:
|
||||
explicit MonitorManager();
|
||||
~MonitorManager();
|
||||
void Reinitialize();
|
||||
void RequireReinitilization();
|
||||
void SetCursorMonitorId(int id) { cursorMonitorId_ = id; }
|
||||
int GetCursorMonitorId() const { return cursorMonitorId_; }
|
||||
std::shared_ptr<Monitor> GetMonitor(int id) const;
|
||||
|
||||
private:
|
||||
void Initialize();
|
||||
void Finalize();
|
||||
|
||||
// Setters from Unity
|
||||
public:
|
||||
void Update();
|
||||
void SetTimeout(int timeout);
|
||||
int GetTimeout() const;
|
||||
|
||||
// Getters from Unity
|
||||
public:
|
||||
int GetMonitorCount() const;
|
||||
int GetTotalWidth() const;
|
||||
int GetTotalHeight() const;
|
||||
|
||||
private:
|
||||
int timeout_ = 10;
|
||||
std::vector<std::shared_ptr<Monitor>> monitors_;
|
||||
std::shared_ptr<Cursor> cursor_;
|
||||
int cursorMonitorId_ = -1;
|
||||
bool isReinitializationRequired_ = false;
|
||||
};
|
||||
@@ -2,331 +2,88 @@
|
||||
#include <dxgi1_2.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
|
||||
#include "IUnityInterface.h"
|
||||
#include "IUnityGraphics.h"
|
||||
#include "IUnityGraphicsD3D11.h"
|
||||
|
||||
#include "Common.h"
|
||||
#include "Monitor.h"
|
||||
#include "Cursor.h"
|
||||
#include "MonitorManager.h"
|
||||
|
||||
#pragma comment(lib, "dxgi.lib")
|
||||
#pragma comment(lib, "Shcore.lib")
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
struct Monitor
|
||||
{
|
||||
int id = -1;
|
||||
IDXGIOutputDuplication* deskDupl = nullptr;
|
||||
ID3D11Texture2D* texture = nullptr;
|
||||
DXGI_OUTPUT_DESC outputDesc;
|
||||
MONITORINFOEX monitorInfo;
|
||||
IUnityInterfaces* g_unity = nullptr;
|
||||
std::unique_ptr<MonitorManager> g_manager;
|
||||
std::queue<Message> g_messages;
|
||||
}
|
||||
|
||||
struct Pointer
|
||||
{
|
||||
bool isVisible = false;
|
||||
int x = -1;
|
||||
int y = -1;
|
||||
BYTE* apiBuffer = nullptr;
|
||||
UINT apiBufferSize = 0;
|
||||
BYTE* bgra32Buffer = nullptr;
|
||||
UINT bgra32BufferSize = 0;
|
||||
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo;
|
||||
};
|
||||
Pointer pointer;
|
||||
};
|
||||
|
||||
IUnityInterfaces* g_unity = nullptr;
|
||||
int g_mouseMonitor = 0;
|
||||
int g_timeout = 10;
|
||||
HRESULT g_errorCode = 0;
|
||||
std::string g_errorMessage = "";
|
||||
std::vector<Monitor> g_monitors;
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
extern "C"
|
||||
{
|
||||
void FinalizeDuplication()
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API InitializeUDD()
|
||||
{
|
||||
for (auto& monitor : g_monitors)
|
||||
if (g_unity && !g_manager)
|
||||
{
|
||||
monitor.deskDupl->Release();
|
||||
g_manager = std::make_unique<MonitorManager>();
|
||||
}
|
||||
g_monitors.clear();
|
||||
}
|
||||
|
||||
void InitializeDuplication()
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API FinalizeUDD()
|
||||
{
|
||||
FinalizeDuplication();
|
||||
|
||||
IDXGIFactory1* factory;
|
||||
CreateDXGIFactory1(__uuidof(IDXGIFactory1), reinterpret_cast<void**>(&factory));
|
||||
|
||||
// Check all display adapters.
|
||||
int id = 0;
|
||||
IDXGIAdapter1* adapter;
|
||||
for (int i = 0; (factory->EnumAdapters1(i, &adapter) != DXGI_ERROR_NOT_FOUND); ++i)
|
||||
{
|
||||
// Search the main monitor from all outputs.
|
||||
IDXGIOutput* output;
|
||||
for (int j = 0; (adapter->EnumOutputs(j, &output) != DXGI_ERROR_NOT_FOUND); ++j)
|
||||
{
|
||||
Monitor monitor;
|
||||
monitor.id = id++;
|
||||
|
||||
output->GetDesc(&monitor.outputDesc);
|
||||
monitor.monitorInfo.cbSize = sizeof(MONITORINFOEX);
|
||||
GetMonitorInfo(monitor.outputDesc.Monitor, &monitor.monitorInfo);
|
||||
|
||||
auto device = g_unity->Get<IUnityGraphicsD3D11>()->GetDevice();
|
||||
auto output1 = reinterpret_cast<IDXGIOutput1*>(output);
|
||||
output1->DuplicateOutput(device, &monitor.deskDupl);
|
||||
output->Release();
|
||||
|
||||
g_monitors.push_back(monitor);
|
||||
}
|
||||
|
||||
adapter->Release();
|
||||
}
|
||||
|
||||
factory->Release();
|
||||
g_manager.reset();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces)
|
||||
{
|
||||
g_unity = unityInterfaces;
|
||||
InitializeDuplication();
|
||||
InitializeUDD();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UnityPluginUnload()
|
||||
{
|
||||
g_unity = nullptr;
|
||||
FinalizeDuplication();
|
||||
}
|
||||
|
||||
bool IsValidMonitorId(int id)
|
||||
{
|
||||
return id >= 0 && id < g_monitors.size();
|
||||
}
|
||||
|
||||
bool UpdateMouse(const DXGI_OUTDUPL_FRAME_INFO& frameInfo, Monitor& monitor)
|
||||
{
|
||||
auto& pointer = monitor.pointer;
|
||||
pointer.isVisible = frameInfo.PointerPosition.Visible != 0;
|
||||
pointer.x = frameInfo.PointerPosition.Position.x;
|
||||
pointer.y = frameInfo.PointerPosition.Position.y;
|
||||
|
||||
// Pointer type
|
||||
const auto pointerType = pointer.shapeInfo.Type;
|
||||
const bool isMono = pointerType == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME;
|
||||
const bool isColorMask = pointerType == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR;
|
||||
|
||||
if (pointer.isVisible)
|
||||
{
|
||||
g_mouseMonitor = monitor.id;
|
||||
}
|
||||
|
||||
if (g_mouseMonitor != monitor.id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Increase the buffer size if needed
|
||||
if (frameInfo.PointerShapeBufferSize > pointer.apiBufferSize)
|
||||
{
|
||||
if (pointer.apiBuffer) delete[] pointer.apiBuffer;
|
||||
pointer.apiBuffer = new BYTE[frameInfo.PointerShapeBufferSize];
|
||||
pointer.apiBufferSize = frameInfo.PointerShapeBufferSize;
|
||||
}
|
||||
if (!pointer.apiBuffer) return true;
|
||||
|
||||
// Get information about the mouse pointer if needed
|
||||
if (frameInfo.PointerShapeBufferSize != 0)
|
||||
{
|
||||
UINT bufferSize;
|
||||
monitor.deskDupl->GetFramePointerShape(
|
||||
frameInfo.PointerShapeBufferSize,
|
||||
reinterpret_cast<void*>(pointer.apiBuffer),
|
||||
&bufferSize,
|
||||
&pointer.shapeInfo);
|
||||
}
|
||||
|
||||
// Size
|
||||
const auto w = pointer.shapeInfo.Width;
|
||||
const auto h = pointer.shapeInfo.Height / (isMono ? 2 : 1);
|
||||
const auto p = pointer.shapeInfo.Pitch;
|
||||
|
||||
// Convert the buffer given by API into BGRA32
|
||||
const auto bgraBufferSize = w * h * 4;
|
||||
if (bgraBufferSize > pointer.bgra32BufferSize)
|
||||
{
|
||||
if (pointer.bgra32Buffer) delete[] pointer.bgra32Buffer;
|
||||
pointer.bgra32Buffer = new BYTE[bgraBufferSize];
|
||||
pointer.bgra32BufferSize = bgraBufferSize;
|
||||
}
|
||||
if (!pointer.bgra32Buffer) return true;
|
||||
|
||||
// If masked, copy the desktop image and merge it with masked image.
|
||||
if (isMono || isColorMask)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
desc.Width = w;
|
||||
desc.Height = h;
|
||||
desc.MipLevels = 1;
|
||||
desc.ArraySize = 1;
|
||||
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.SampleDesc.Quality = 0;
|
||||
desc.Usage = D3D11_USAGE_STAGING;
|
||||
desc.BindFlags = 0;
|
||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
desc.MiscFlags = 0;
|
||||
|
||||
ID3D11DeviceContext* context;
|
||||
auto device = g_unity->Get<IUnityGraphicsD3D11>()->GetDevice();
|
||||
device->GetImmediateContext(&context);
|
||||
|
||||
ID3D11Texture2D* texture = nullptr;
|
||||
hr = device->CreateTexture2D(&desc, nullptr, &texture);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D11_BOX box;
|
||||
box.front = 0;
|
||||
box.back = 1;
|
||||
box.left = pointer.x;
|
||||
box.top = pointer.y;
|
||||
box.right = pointer.x + w;
|
||||
box.bottom = pointer.y + h;
|
||||
context->CopySubresourceRegion(texture, 0, 0, 0, 0, monitor.texture, 0, &box);
|
||||
|
||||
IDXGISurface* surface = nullptr;
|
||||
hr = texture->QueryInterface(__uuidof(IDXGISurface), (void**)&surface);
|
||||
texture->Release();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DXGI_MAPPED_RECT mappedSurface;
|
||||
hr = surface->Map(&mappedSurface, DXGI_MAP_READ);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
surface->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finally, get the texture behind the mouse pointer.
|
||||
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*>(pointer.bgra32Buffer);
|
||||
|
||||
if (isMono)
|
||||
{
|
||||
for (UINT row = 0; row < h; ++row)
|
||||
{
|
||||
BYTE mask = 0x80;
|
||||
for (UINT col = 0; col < w; ++col)
|
||||
{
|
||||
const int i = row * w + col;
|
||||
const BYTE andMask = pointer.apiBuffer[col / 8 + row * p] & mask;
|
||||
const BYTE xorMask = pointer.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
else // DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR
|
||||
{
|
||||
const auto buffer32 = reinterpret_cast<UINT*>(pointer.apiBuffer);
|
||||
|
||||
for (UINT row = 0; row < h; ++row)
|
||||
{
|
||||
for (UINT col = 0; col < w; ++col)
|
||||
{
|
||||
const int i = row * w + col;
|
||||
const int j = row * p / sizeof(UINT) + col;
|
||||
|
||||
UINT mask = 0xFF000000 & buffer32[j];
|
||||
if (mask)
|
||||
{
|
||||
output32[i] = (desktop32[row * desktopPitch + col] ^ buffer32[j]) | 0xFF000000;
|
||||
}
|
||||
else
|
||||
{
|
||||
output32[i] = buffer32[j] | 0xFF000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hr = surface->Unmap();
|
||||
surface->Release();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else // DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR
|
||||
{
|
||||
auto output32 = reinterpret_cast<UINT*>(pointer.bgra32Buffer);
|
||||
const auto buffer32 = reinterpret_cast<UINT*>(pointer.apiBuffer);
|
||||
for (UINT i = 0; i < w * h; ++i)
|
||||
{
|
||||
output32[i] = buffer32[i];
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
FinalizeUDD();
|
||||
}
|
||||
|
||||
void UNITY_INTERFACE_API OnRenderEvent(int id)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return;
|
||||
auto& monitor = g_monitors[id];
|
||||
|
||||
if (monitor.deskDupl == nullptr || monitor.texture == nullptr) return;
|
||||
|
||||
IDXGIResource* resource = nullptr;
|
||||
DXGI_OUTDUPL_FRAME_INFO frameInfo;
|
||||
|
||||
g_errorCode = monitor.deskDupl->AcquireNextFrame(g_timeout, &frameInfo, &resource);
|
||||
if (FAILED(g_errorCode))
|
||||
if (!g_manager) return;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
if (g_errorCode == DXGI_ERROR_ACCESS_LOST)
|
||||
{
|
||||
InitializeDuplication();
|
||||
g_errorMessage = "[IDXGIOutputDuplication::AcquireNextFrame()] Access lost.";
|
||||
}
|
||||
else
|
||||
{
|
||||
g_errorMessage = "[IDXGIOutputDuplication::AcquireNextFrame()] Maybe timeout.";
|
||||
}
|
||||
return;
|
||||
monitor->Render(g_manager->GetTimeout());
|
||||
}
|
||||
|
||||
ID3D11Texture2D* texture;
|
||||
resource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&texture));
|
||||
|
||||
ID3D11DeviceContext* context;
|
||||
auto device = g_unity->Get<IUnityGraphicsD3D11>()->GetDevice();
|
||||
device->GetImmediateContext(&context);
|
||||
context->CopyResource(monitor.texture, texture);
|
||||
|
||||
if (!UpdateMouse(frameInfo, monitor))
|
||||
{
|
||||
g_errorCode = -999;
|
||||
g_errorMessage = "[UpdateMouse()] failed.";
|
||||
}
|
||||
|
||||
resource->Release();
|
||||
monitor.deskDupl->ReleaseFrame();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT UnityRenderingEvent UNITY_INTERFACE_API GetRenderEventFunc()
|
||||
@@ -334,114 +91,256 @@ extern "C"
|
||||
return OnRenderEvent;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API Reinitialize()
|
||||
{
|
||||
if (!g_manager) return;
|
||||
return g_manager->Reinitialize();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API Update()
|
||||
{
|
||||
if (!g_manager) return;
|
||||
g_manager->Update();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT Message PopMessage()
|
||||
{
|
||||
if (g_messages.empty()) return Message::None;
|
||||
|
||||
const auto message = g_messages.front();
|
||||
g_messages.pop();
|
||||
return message;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT size_t UNITY_INTERFACE_API GetMonitorCount()
|
||||
{
|
||||
return g_monitors.size();
|
||||
if (!g_manager) return 0;
|
||||
return g_manager->GetMonitorCount();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetTotalWidth()
|
||||
{
|
||||
if (!g_manager) return 0;
|
||||
return g_manager->GetTotalWidth();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetTotalHeight()
|
||||
{
|
||||
if (!g_manager) return 0;
|
||||
return g_manager->GetTotalHeight();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetTimeout(int timeout)
|
||||
{
|
||||
g_timeout = timeout;
|
||||
if (!g_manager) return;
|
||||
g_manager->SetTimeout(timeout);
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT MonitorState UNITY_INTERFACE_API GetState(int id)
|
||||
{
|
||||
if (!g_manager) return MonitorState::NotSet;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetState();
|
||||
}
|
||||
return MonitorState::NotSet;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API GetName(int id, char* buf, int len)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return;
|
||||
strcpy_s(buf, len, g_monitors[id].monitorInfo.szDevice);
|
||||
if (!g_manager) return;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
monitor->GetName(buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API IsPrimary(int id)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return false;
|
||||
return g_monitors[id].monitorInfo.dwFlags == MONITORINFOF_PRIMARY;
|
||||
if (!g_manager) return false;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->IsPrimary();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetLeft(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetLeft();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetRight(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetRight();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetTop(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetTop();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetBottom(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetBottom();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetWidth(int id)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return -1;
|
||||
const auto rect = g_monitors[id].monitorInfo.rcMonitor;
|
||||
return rect.right - rect.left;
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetWidth();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetHeight(int id)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return -1;
|
||||
const auto rect = g_monitors[id].monitorInfo.rcMonitor;
|
||||
return rect.bottom - rect.top;
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetHeight();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API IsPointerVisible(int id)
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetRotation(int id)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return false;
|
||||
return g_monitors[id].pointer.isVisible;
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetRotation();
|
||||
}
|
||||
return DXGI_MODE_ROTATION_UNSPECIFIED;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetPointerX(int id)
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetDpiX(int id)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return -1;
|
||||
return g_monitors[id].pointer.x;
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetDpiX();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetPointerY(int id)
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetDpiY(int id)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return -1;
|
||||
return g_monitors[id].pointer.y;
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetDpiY();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetPointerShapeWidth(int id)
|
||||
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API IsCursorVisible(int id)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return -1;
|
||||
return g_monitors[id].pointer.shapeInfo.Width;
|
||||
if (!g_manager) return false;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetCursor()->IsVisible();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetPointerShapeHeight(int id)
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorX(int id)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return -1;
|
||||
const auto& info = g_monitors[id].pointer.shapeInfo;
|
||||
return (info.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME) ? info.Height / 2 : info.Height;
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetCursor()->GetX();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetPointerShapePitch(int id)
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorY(int id)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return -1;
|
||||
return g_monitors[id].pointer.shapeInfo.Pitch;
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetCursor()->GetY();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetPointerShapeType(int id)
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorShapeWidth(int id)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return -1;
|
||||
return g_monitors[id].pointer.shapeInfo.Type;
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetCursor()->GetWidth();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UpdatePointerTexture(int id, ID3D11Texture2D* ptr)
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorShapeHeight(int id)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return;
|
||||
const auto& pointer = g_monitors[id].pointer;
|
||||
if (!pointer.bgra32Buffer) return;
|
||||
auto device = g_unity->Get<IUnityGraphicsD3D11>()->GetDevice();
|
||||
ID3D11DeviceContext* context;
|
||||
device->GetImmediateContext(&context);
|
||||
context->UpdateSubresource(ptr, 0, nullptr, pointer.bgra32Buffer, pointer.shapeInfo.Width * 4, 0);
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetCursor()->GetHeight();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorShapePitch(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetCursor()->GetPitch();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorShapeType(int id)
|
||||
{
|
||||
if (!g_manager) return -1;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
return monitor->GetCursor()->GetType();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API GetCursorTexture(int id, ID3D11Texture2D* texture)
|
||||
{
|
||||
if (!g_manager) return;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
monitor->GetCursorTexture(texture);
|
||||
}
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetTexturePtr(int id, void* texture)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return;
|
||||
g_monitors[id].texture = reinterpret_cast<ID3D11Texture2D*>(texture);
|
||||
if (!g_manager) return;
|
||||
if (auto monitor = g_manager->GetMonitor(id))
|
||||
{
|
||||
auto d3d11Texture = reinterpret_cast<ID3D11Texture2D*>(texture);
|
||||
monitor->SetUnityTexture(d3d11Texture);
|
||||
}
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetErrorCode()
|
||||
{
|
||||
const auto code = g_errorCode;
|
||||
g_errorCode = 0;
|
||||
return static_cast<int>(code);
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API GetErrorMessage(char* buf, int len)
|
||||
{
|
||||
const auto message = g_errorMessage;
|
||||
g_errorMessage = "";
|
||||
strcpy_s(buf, len, message.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -69,8 +69,12 @@
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);include</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);include</IncludePath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
@@ -122,12 +126,19 @@
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="MonitorManager.cpp" />
|
||||
<ClCompile Include="Main.cpp" />
|
||||
<ClCompile Include="Monitor.cpp" />
|
||||
<ClCompile Include="Cursor.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="IUnityGraphics.h" />
|
||||
<ClInclude Include="IUnityGraphicsD3D11.h" />
|
||||
<ClInclude Include="IUnityInterface.h" />
|
||||
<ClInclude Include="Common.h" />
|
||||
<ClInclude Include="MonitorManager.h" />
|
||||
<ClInclude Include="include\IUnityGraphics.h" />
|
||||
<ClInclude Include="include\IUnityGraphicsD3D11.h" />
|
||||
<ClInclude Include="include\IUnityInterface.h" />
|
||||
<ClInclude Include="Monitor.h" />
|
||||
<ClInclude Include="Cursor.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Include">
|
||||
<UniqueIdentifier>{a820eab3-2c2a-4d56-9670-064198e6a1cf}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="include\IUnityInterface.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\IUnityGraphics.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\IUnityGraphicsD3D11.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Monitor.h" />
|
||||
<ClInclude Include="Cursor.h" />
|
||||
<ClInclude Include="Common.h" />
|
||||
<ClInclude Include="MonitorManager.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Monitor.cpp" />
|
||||
<ClCompile Include="Cursor.cpp" />
|
||||
<ClCompile Include="Main.cpp" />
|
||||
<ClCompile Include="MonitorManager.cpp" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Binary file not shown.
Reference in New Issue
Block a user