Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4350ac3953 | ||
|
|
6ba381f5d4 | ||
|
|
9287ef2f2d | ||
|
|
dc6b8bc204 | ||
|
|
14cd67e0c6 | ||
|
|
73018f671a | ||
|
|
7a27d86eff | ||
|
|
99e1774388 | ||
|
|
e0408d4749 | ||
|
|
23ceef6502 | ||
|
|
4317fc06e3 | ||
|
|
f8cb71b472 | ||
|
|
f1a7c302bd | ||
|
|
ec82c49d7f | ||
|
|
f8146c15bd | ||
|
|
d5a9827b4d | ||
|
|
4b499a96de | ||
|
|
583326c333 | ||
|
|
c3ec17dd7b | ||
|
|
6f375e2e5b | ||
|
|
d9f83cf89e | ||
|
|
ba5ebe8094 | ||
|
|
1e2ac40507 | ||
|
|
de0c157fb5 | ||
|
|
c93bd58ec4 | ||
|
|
c4db1ab14a | ||
|
|
e72eff0ee7 | ||
|
|
50fd042e35 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -4,6 +4,7 @@
|
||||
/[Oo]bj/
|
||||
/[Bb]uild/
|
||||
/[Ww]iki/
|
||||
/[Mm]isc/
|
||||
|
||||
# Autogenerated VS/MD solution and project files
|
||||
*.csproj
|
||||
@@ -24,3 +25,5 @@ sysinfo.txt
|
||||
|
||||
# Others
|
||||
.DS_Store
|
||||
/Assets/AssetStoreTools*
|
||||
/Assets/Extensions*
|
||||
|
||||
9
Assets/uDesktopDuplication/Examples/Prefabs.meta
Normal file
9
Assets/uDesktopDuplication/Examples/Prefabs.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 678e1de5e6de7914baed516a78940503
|
||||
folderAsset: yes
|
||||
timeCreated: 1477643728
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/uDesktopDuplication/Examples/Prefabs/Monitor.prefab
Normal file
BIN
Assets/uDesktopDuplication/Examples/Prefabs/Monitor.prefab
Normal file
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: de6f36e446e5e8a48bf61fe198985bb4
|
||||
timeCreated: 1477643732
|
||||
licenseType: Pro
|
||||
NativeFormatImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
Assets/uDesktopDuplication/Examples/Scenes.meta
Normal file
9
Assets/uDesktopDuplication/Examples/Scenes.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a3456d4b2f59e84f969e31deca4016d
|
||||
folderAsset: yes
|
||||
timeCreated: 1477643596
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 445860b05342be845967392bfd70224e
|
||||
timeCreated: 1477643648
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 281e2991d4d33984fa180845e56cd3f3
|
||||
timeCreated: 1477648643
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/uDesktopDuplication/Examples/Scenes/Shaders.unity
Normal file
BIN
Assets/uDesktopDuplication/Examples/Scenes/Shaders.unity
Normal file
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f66b67cb0e751dc4ca0251b38bc8638d
|
||||
timeCreated: 1477648784
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/uDesktopDuplication/Examples/Scenes/Toggle Monitors.unity
Normal file
BIN
Assets/uDesktopDuplication/Examples/Scenes/Toggle Monitors.unity
Normal file
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 78e54899a518eb749a589b921a1b263f
|
||||
timeCreated: 1477650252
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
Assets/uDesktopDuplication/Examples/Scripts.meta
Normal file
9
Assets/uDesktopDuplication/Examples/Scripts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 71921882396f0f345aa8a66e2fcb0b6c
|
||||
folderAsset: yes
|
||||
timeCreated: 1477643608
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,38 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class MultipleMonitorCreator : MonoBehaviour
|
||||
{
|
||||
[SerializeField] GameObject monitorPrefab;
|
||||
[SerializeField] float scale = 10f;
|
||||
[SerializeField] float margin = 1f;
|
||||
[SerializeField] float ratio = 0.001f;
|
||||
|
||||
void Start()
|
||||
{
|
||||
var monitors = uDesktopDuplication.Manager.monitors;
|
||||
var n = monitors.Count;
|
||||
|
||||
var totalWidth = 0f;
|
||||
for (int i = 0 ; i < n; ++i) {
|
||||
var go = Instantiate(monitorPrefab);
|
||||
go.name = "Monitor " + i;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
x += halfWidth;
|
||||
go.transform.position = new Vector3(x, 0f, 0f);
|
||||
x += halfWidth + margin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb54a34570e4747429b1c5b66c69d356
|
||||
timeCreated: 1452445649
|
||||
guid: fb22986017201f74aa864dcbf0cab751
|
||||
timeCreated: 1477643778
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
@@ -0,0 +1,17 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class ToggleMonitors : MonoBehaviour
|
||||
{
|
||||
void Update()
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.Tab)) {
|
||||
var texture = GetComponent<uDesktopDuplication.Texture>();
|
||||
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) {
|
||||
texture.monitorId--;
|
||||
} else {
|
||||
texture.monitorId++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 602d3a9bed585d14eaf21e0528d24d54
|
||||
timeCreated: 1477650268
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/uDesktopDuplication/Materials/uDD_Cursor.mat
Normal file
BIN
Assets/uDesktopDuplication/Materials/uDD_Cursor.mat
Normal file
Binary file not shown.
8
Assets/uDesktopDuplication/Materials/uDD_Cursor.mat.meta
Normal file
8
Assets/uDesktopDuplication/Materials/uDD_Cursor.mat.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 033e0db89e0ca1d46a399c953629f67d
|
||||
timeCreated: 1477555367
|
||||
licenseType: Pro
|
||||
NativeFormatImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/uDesktopDuplication/Materials/uDD_Screen_Standard.mat
Normal file
BIN
Assets/uDesktopDuplication/Materials/uDD_Screen_Standard.mat
Normal file
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 06ef13d14bbcf9242a8eba19a94803f3
|
||||
timeCreated: 1477648806
|
||||
licenseType: Pro
|
||||
NativeFormatImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3abaadab838fb3c4ba342eda7431d4f7
|
||||
timeCreated: 1477648806
|
||||
licenseType: Pro
|
||||
NativeFormatImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 647550562f402264fb69871ec732b74c
|
||||
timeCreated: 1477648806
|
||||
licenseType: Pro
|
||||
NativeFormatImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
9
Assets/uDesktopDuplication/Resources.meta
Normal file
9
Assets/uDesktopDuplication/Resources.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e15bc51aada81042a909c808e25c176
|
||||
folderAsset: yes
|
||||
timeCreated: 1477555207
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48b92326b146fa848beec6f35365b1f4
|
||||
folderAsset: yes
|
||||
timeCreated: 1477555207
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a615a0cde495b914d95b9d2d53182305
|
||||
folderAsset: yes
|
||||
timeCreated: 1477555207
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 967 B |
@@ -0,0 +1,59 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 89616e59e1cccb346bd4e959257990ca
|
||||
timeCreated: 1477555207
|
||||
licenseType: Pro
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
serializedVersion: 2
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
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: 0
|
||||
aniso: 1
|
||||
mipBias: -1
|
||||
wrapMode: 1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
rGBM: 0
|
||||
compressionQuality: 50
|
||||
allowsAlphaSplitting: 0
|
||||
spriteMode: 1
|
||||
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: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 8
|
||||
buildTargetSettings: []
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
spritePackingTag:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
107
Assets/uDesktopDuplication/Scripts/Cursor.cs
Normal file
107
Assets/uDesktopDuplication/Scripts/Cursor.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace uDesktopDuplication
|
||||
{
|
||||
|
||||
[AddComponentMenu("uDesktopDuplication/Cursor"),
|
||||
RequireComponent(typeof(Texture))]
|
||||
public class Cursor : MonoBehaviour
|
||||
{
|
||||
[SerializeField] Vector2 modelScale = Vector2.one;
|
||||
|
||||
Vector3 worldPosition { get; set; }
|
||||
|
||||
private Texture uddTexture_;
|
||||
private Monitor monitor { get { return uddTexture_.monitor; } }
|
||||
|
||||
private Texture2D pointerTexture_;
|
||||
private Material cursorMaterial_;
|
||||
|
||||
private Dictionary<Vector2, Texture2D> pointerTextures_ = new Dictionary<Vector2, Texture2D>();
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
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) {
|
||||
UpdatePosition();
|
||||
UpdateTexture();
|
||||
UpdateMaterial();
|
||||
}
|
||||
}
|
||||
|
||||
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 iy = uddTexture_.invertY ? -1 : +1;
|
||||
var localPos = transform.right * x + iy * transform.up * y;
|
||||
worldPosition = transform.TransformPoint(localPos);
|
||||
}
|
||||
|
||||
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);
|
||||
texture.wrapMode = TextureWrapMode.Clamp;
|
||||
pointerTextures_.Add(scale, texture);
|
||||
}
|
||||
var pointerTexture = pointerTextures_[scale];
|
||||
monitor.UpdatePointerTexture(pointerTexture.GetNativeTexturePtr());
|
||||
cursorMaterial_.SetTexture("_MainTex", pointerTexture);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
12
Assets/uDesktopDuplication/Scripts/Cursor.cs.meta
Normal file
12
Assets/uDesktopDuplication/Scripts/Cursor.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2d9e4ca459ecb44da8815bafe9655c5
|
||||
timeCreated: 1477635630
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 89616e59e1cccb346bd4e959257990ca, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
69
Assets/uDesktopDuplication/Scripts/Lib.cs
Normal file
69
Assets/uDesktopDuplication/Scripts/Lib.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace uDesktopDuplication
|
||||
{
|
||||
|
||||
public enum PointerShapeType
|
||||
{
|
||||
MonoChrome = 1,
|
||||
Color = 2,
|
||||
MaskedColor = 4,
|
||||
}
|
||||
|
||||
public static class Lib
|
||||
{
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetMonitorCount();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void SetTimeout(int timeout);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern IntPtr GetRenderEventFunc();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void GetName(int id, StringBuilder buf, int len);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetWidth(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetHeight(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern bool IsPrimary(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern bool IsPointerVisible(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetPointerX(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetPointerY(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetPointerShapeWidth(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetPointerShapeHeight(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern int GetPointerShapePitch(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern PointerShapeType GetPointerShapeType(int id);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
public static extern void UpdatePointerTexture(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)
|
||||
{
|
||||
var buf = new StringBuilder(32);
|
||||
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
Assets/uDesktopDuplication/Scripts/Lib.cs.meta
Normal file
12
Assets/uDesktopDuplication/Scripts/Lib.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cb02196c3e766c14ba398dc130e443e8
|
||||
timeCreated: 1477643164
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 89616e59e1cccb346bd4e959257990ca, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
92
Assets/uDesktopDuplication/Scripts/Manager.cs
Normal file
92
Assets/uDesktopDuplication/Scripts/Manager.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace uDesktopDuplication
|
||||
{
|
||||
|
||||
public class Manager : MonoBehaviour
|
||||
{
|
||||
private static Manager instance_;
|
||||
public static Manager instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance_) return instance_;
|
||||
var go = new GameObject("uDesktopDuplicationManager");
|
||||
return go.AddComponent<Manager>();
|
||||
}
|
||||
}
|
||||
|
||||
private List<Monitor> monitors_ = new List<Monitor>();
|
||||
static public List<Monitor> monitors
|
||||
{
|
||||
get { return instance.monitors_; }
|
||||
}
|
||||
|
||||
static public int monitorCount
|
||||
{
|
||||
get { return instance.monitors_.Count; }
|
||||
}
|
||||
|
||||
static public Monitor primary
|
||||
{
|
||||
get
|
||||
{
|
||||
return instance.monitors_.Find(monitor => monitor.isPrimary);
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField, Tooltip("Set Desktop Duplication API timeout (milliseconds).")]
|
||||
int timeout = 0;
|
||||
|
||||
[SerializeField, Tooltip("Output logs given by the plugin.")]
|
||||
bool outputDebugLog = false;
|
||||
|
||||
private Coroutine renderCoroutine_ = null;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (instance_ != null) return;
|
||||
instance_ = this;
|
||||
|
||||
for (int i = 0; i < Lib.GetMonitorCount(); ++i) {
|
||||
monitors.Add(new Monitor(i));
|
||||
}
|
||||
|
||||
Lib.SetTimeout(timeout);
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
renderCoroutine_ = StartCoroutine(OnRender());
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
if (renderCoroutine_ != null) {
|
||||
StopCoroutine(renderCoroutine_);
|
||||
renderCoroutine_ = null;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator OnRender()
|
||||
{
|
||||
for (;;) {
|
||||
yield return new WaitForEndOfFrame();
|
||||
for (int i = 0; i < monitors.Count; ++i) {
|
||||
var monitor = monitors[i];
|
||||
if (monitor.shouldBeUpdated) {
|
||||
monitor.Render();
|
||||
}
|
||||
monitor.shouldBeUpdated = false;
|
||||
}
|
||||
if (outputDebugLog && Lib.GetErrorCode() != 0)
|
||||
{
|
||||
Debug.Log(Lib.GetErrorMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
12
Assets/uDesktopDuplication/Scripts/Manager.cs.meta
Normal file
12
Assets/uDesktopDuplication/Scripts/Manager.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4dccb434f55ebee4f91bfb9935092bbf
|
||||
timeCreated: 1477643174
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 89616e59e1cccb346bd4e959257990ca, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
116
Assets/uDesktopDuplication/Scripts/Monitor.cs
Normal file
116
Assets/uDesktopDuplication/Scripts/Monitor.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace uDesktopDuplication
|
||||
{
|
||||
|
||||
public class Monitor
|
||||
{
|
||||
public Monitor(int id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int id
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public string name
|
||||
{
|
||||
get { return Lib.GetName(id); }
|
||||
}
|
||||
|
||||
public bool isPrimary
|
||||
{
|
||||
get { return Lib.IsPrimary(id); }
|
||||
}
|
||||
|
||||
public int width
|
||||
{
|
||||
get { return Lib.GetWidth(id); }
|
||||
}
|
||||
|
||||
public int height
|
||||
{
|
||||
get { return Lib.GetHeight(id); }
|
||||
}
|
||||
|
||||
public float aspect
|
||||
{
|
||||
get { return (float)width / height; }
|
||||
}
|
||||
|
||||
public bool isHorizontal
|
||||
{
|
||||
get { return width > height; }
|
||||
}
|
||||
|
||||
public bool isVertical
|
||||
{
|
||||
get { return height > width; }
|
||||
}
|
||||
|
||||
public bool isPointerVisible
|
||||
{
|
||||
get { return Lib.IsPointerVisible(id); }
|
||||
}
|
||||
|
||||
public int pointerX
|
||||
{
|
||||
get { return Lib.GetPointerX(id); }
|
||||
}
|
||||
|
||||
public int pointerY
|
||||
{
|
||||
get { return Lib.GetPointerY(id); }
|
||||
}
|
||||
|
||||
public int pointerShapeWidth
|
||||
{
|
||||
get { return Lib.GetPointerShapeWidth(id); }
|
||||
}
|
||||
|
||||
public int pointerShapeHeight
|
||||
{
|
||||
get { return Lib.GetPointerShapeHeight(id); }
|
||||
}
|
||||
|
||||
public PointerShapeType pointerShapeType
|
||||
{
|
||||
get { return Lib.GetPointerShapeType(id); }
|
||||
}
|
||||
|
||||
public bool shouldBeUpdated
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private Texture2D texture_;
|
||||
public Texture2D texture
|
||||
{
|
||||
get
|
||||
{
|
||||
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());
|
||||
}
|
||||
return texture_;
|
||||
}
|
||||
}
|
||||
|
||||
public void Render()
|
||||
{
|
||||
GL.IssuePluginEvent(Lib.GetRenderEventFunc(), id);
|
||||
}
|
||||
|
||||
public void UpdatePointerTexture(System.IntPtr ptr)
|
||||
{
|
||||
Lib.UpdatePointerTexture(id, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
12
Assets/uDesktopDuplication/Scripts/Monitor.cs.meta
Normal file
12
Assets/uDesktopDuplication/Scripts/Monitor.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 310590b113d909b43b4ea19702904ac3
|
||||
timeCreated: 1477643181
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 89616e59e1cccb346bd4e959257990ca, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
67
Assets/uDesktopDuplication/Scripts/Texture.cs
Normal file
67
Assets/uDesktopDuplication/Scripts/Texture.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace uDesktopDuplication
|
||||
{
|
||||
|
||||
[AddComponentMenu("uDesktopDuplication/Texture")]
|
||||
public class Texture : MonoBehaviour
|
||||
{
|
||||
private Monitor monitor_;
|
||||
public Monitor monitor
|
||||
{
|
||||
get { return monitor_; }
|
||||
set
|
||||
{
|
||||
monitor_ = value;
|
||||
material_ = GetComponent<Renderer>().material;
|
||||
material_.mainTexture = monitor_.texture;
|
||||
}
|
||||
}
|
||||
|
||||
public int monitorId
|
||||
{
|
||||
get { return monitor.id; }
|
||||
set { monitor = Manager.monitors[Mathf.Clamp(value, 0, Manager.monitorCount - 1)]; }
|
||||
}
|
||||
|
||||
public bool invertX = false;
|
||||
public bool invertY = false;
|
||||
|
||||
private Material material_;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
if (monitor == null) {
|
||||
monitor = Manager.primary;
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
monitor.shouldBeUpdated = true;
|
||||
UpdateMaterial();
|
||||
}
|
||||
|
||||
void UpdateMaterial()
|
||||
{
|
||||
if (invertX) {
|
||||
material_.EnableKeyword("INVERT_X");
|
||||
} else {
|
||||
material_.DisableKeyword("INVERT_X");
|
||||
}
|
||||
|
||||
if (invertY) {
|
||||
material_.EnableKeyword("INVERT_Y");
|
||||
} else {
|
||||
material_.DisableKeyword("INVERT_Y");
|
||||
}
|
||||
|
||||
if (monitor.isVertical) {
|
||||
material_.EnableKeyword("VERTICAL");
|
||||
} else {
|
||||
material_.DisableKeyword("VERTICAL");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
12
Assets/uDesktopDuplication/Scripts/Texture.cs.meta
Normal file
12
Assets/uDesktopDuplication/Scripts/Texture.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb54a34570e4747429b1c5b66c69d356
|
||||
timeCreated: 1477635636
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 89616e59e1cccb346bd4e959257990ca, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,62 +0,0 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
public class uDesktopDuplication : MonoBehaviour
|
||||
{
|
||||
[DllImport("uDesktopDuplication")]
|
||||
private static extern void Initialize();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
private static extern int GetWidth();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
private static extern int GetHeight();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
private static extern bool IsPointerVisible();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
private static extern int GetPointerX();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
private static extern int GetPointerY();
|
||||
[DllImport("uDesktopDuplication")]
|
||||
private static extern int SetTexturePtr(IntPtr ptr);
|
||||
[DllImport("uDesktopDuplication")]
|
||||
private static extern IntPtr GetRenderEventFunc();
|
||||
|
||||
public bool isPointerVisible = false;
|
||||
public int pointerX = 0;
|
||||
public int pointerY = 0;
|
||||
|
||||
Coroutine renderCoroutine_ = null;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
var tex = new Texture2D(GetWidth(), GetHeight(), TextureFormat.BGRA32, false);
|
||||
GetComponent<Renderer>().sharedMaterial.mainTexture = tex;
|
||||
|
||||
SetTexturePtr(tex.GetNativeTexturePtr());
|
||||
renderCoroutine_ = StartCoroutine(OnRender());
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
if (renderCoroutine_ != null) {
|
||||
StopCoroutine(renderCoroutine_);
|
||||
renderCoroutine_ = null;
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
isPointerVisible = IsPointerVisible();
|
||||
pointerX = GetPointerX();
|
||||
pointerY = GetPointerY();
|
||||
}
|
||||
|
||||
IEnumerator OnRender()
|
||||
{
|
||||
for (;;) {
|
||||
yield return new WaitForEndOfFrame();
|
||||
GL.IssuePluginEvent(GetRenderEventFunc(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
9
Assets/uDesktopDuplication/Shaders.meta
Normal file
9
Assets/uDesktopDuplication/Shaders.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9844af21eb203794b852f0bcaa5f2be2
|
||||
folderAsset: yes
|
||||
timeCreated: 1477556975
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
36
Assets/uDesktopDuplication/Shaders/uDD_Common.cginc
Normal file
36
Assets/uDesktopDuplication/Shaders/uDD_Common.cginc
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef UDD_COMMON_CGINC
|
||||
#define UDD_COMMON_CGINC
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
|
||||
inline void uddInvertUV(inout float2 uv)
|
||||
{
|
||||
#ifdef INVERT_X
|
||||
uv.x = 1.0 - uv.x;
|
||||
#endif
|
||||
#ifdef INVERT_Y
|
||||
uv.y = 1.0 - uv.y;
|
||||
#endif
|
||||
#ifdef VERTICAL
|
||||
float2 tmp = uv;
|
||||
uv.x = tmp.y;
|
||||
uv.y = 1.0 - tmp.x;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void uddToLinearIfNeeded(inout float3 rgb)
|
||||
{
|
||||
if (!IsGammaSpace()) {
|
||||
rgb = GammaToLinearSpace(rgb);
|
||||
}
|
||||
}
|
||||
|
||||
inline fixed4 uddGetTexture(sampler2D tex, float2 uv)
|
||||
{
|
||||
uddInvertUV(uv);
|
||||
fixed4 c = tex2D(tex, uv);
|
||||
uddToLinearIfNeeded(c.rgb);
|
||||
return c;
|
||||
}
|
||||
|
||||
#endif
|
||||
9
Assets/uDesktopDuplication/Shaders/uDD_Common.cginc.meta
Normal file
9
Assets/uDesktopDuplication/Shaders/uDD_Common.cginc.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1090e048de83c254d99a242f407890c6
|
||||
timeCreated: 1477561310
|
||||
licenseType: Pro
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
79
Assets/uDesktopDuplication/Shaders/uDD_Cursor.shader
Normal file
79
Assets/uDesktopDuplication/Shaders/uDD_Cursor.shader
Normal file
@@ -0,0 +1,79 @@
|
||||
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"
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d224cadaa1bfa2d46a6224ce2a1443c7
|
||||
timeCreated: 1477847636
|
||||
licenseType: Pro
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
51
Assets/uDesktopDuplication/Shaders/uDD_Standard.shader
Normal file
51
Assets/uDesktopDuplication/Shaders/uDD_Standard.shader
Normal file
@@ -0,0 +1,51 @@
|
||||
Shader "uDesktopDuplication/Standard"
|
||||
{
|
||||
|
||||
Properties
|
||||
{
|
||||
_Color ("Color", Color) = (1, 1, 1, 1)
|
||||
_MainTex ("Albedo (RGB)", 2D) = "white" {}
|
||||
_Glossiness ("Smoothness", Range(0, 1)) = 0.5
|
||||
_Metallic ("Metallic", Range(0, 1)) = 0.0
|
||||
}
|
||||
|
||||
SubShader
|
||||
{
|
||||
Tags { "RenderType"="Opaque" }
|
||||
|
||||
CGPROGRAM
|
||||
|
||||
#pragma target 3.0
|
||||
#pragma surface surf Standard fullforwardshadows
|
||||
#pragma multi_compile ___ INVERT_X
|
||||
#pragma multi_compile ___ INVERT_Y
|
||||
#pragma multi_compile ___ VERTICAL
|
||||
|
||||
#include "./uDD_Common.cginc"
|
||||
|
||||
sampler2D _MainTex;
|
||||
|
||||
struct Input
|
||||
{
|
||||
float2 uv_MainTex;
|
||||
};
|
||||
|
||||
half _Glossiness;
|
||||
half _Metallic;
|
||||
fixed4 _Color;
|
||||
|
||||
void surf(Input IN, inout SurfaceOutputStandard o)
|
||||
{
|
||||
fixed4 c = uddGetTexture(_MainTex, IN.uv_MainTex) * _Color;
|
||||
o.Albedo = c.rgb;
|
||||
o.Metallic = _Metallic;
|
||||
o.Smoothness = _Glossiness;
|
||||
o.Alpha = c.a;
|
||||
}
|
||||
|
||||
ENDCG
|
||||
}
|
||||
|
||||
FallBack "Diffuse"
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1428bebbde173ab4ab509941809d6aec
|
||||
timeCreated: 1477559925
|
||||
licenseType: Pro
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
66
Assets/uDesktopDuplication/Shaders/uDD_Unlit.shader
Normal file
66
Assets/uDesktopDuplication/Shaders/uDD_Unlit.shader
Normal file
@@ -0,0 +1,66 @@
|
||||
Shader "uDesktopDuplication/Unlit"
|
||||
{
|
||||
|
||||
Properties
|
||||
{
|
||||
_Color ("Color", Color) = (1, 1, 1, 1)
|
||||
_MainTex ("Texture", 2D) = "white" {}
|
||||
}
|
||||
|
||||
SubShader
|
||||
{
|
||||
|
||||
Tags { "RenderType"="Opaque" }
|
||||
|
||||
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;
|
||||
|
||||
v2f vert(appdata v)
|
||||
{
|
||||
v2f o;
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
|
||||
return o;
|
||||
}
|
||||
|
||||
fixed4 frag(v2f i) : SV_Target
|
||||
{
|
||||
return uddGetTexture(_MainTex, i.uv) * _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"
|
||||
|
||||
}
|
||||
9
Assets/uDesktopDuplication/Shaders/uDD_Unlit.shader.meta
Normal file
9
Assets/uDesktopDuplication/Shaders/uDD_Unlit.shader.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2ca7b38c676bddd439a77a646a279a54
|
||||
timeCreated: 1477556963
|
||||
licenseType: Pro
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,73 @@
|
||||
Shader "uDesktopDuplication/Unlit BlackMask"
|
||||
{
|
||||
|
||||
Properties
|
||||
{
|
||||
_Color ("Color", Color) = (1, 1, 1, 1)
|
||||
_MainTex ("Texture", 2D) = "white" {}
|
||||
_Mask ("Mask", Range(0, 1)) = 0.1
|
||||
}
|
||||
|
||||
SubShader
|
||||
{
|
||||
|
||||
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
|
||||
|
||||
ZWrite Off
|
||||
Blend SrcAlpha OneMinusSrcAlpha
|
||||
|
||||
CGINCLUDE
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
#include "./uDD_Common.cginc"
|
||||
|
||||
struct appdata
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 vertex : SV_POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
sampler2D _MainTex;
|
||||
float4 _MainTex_ST;
|
||||
fixed4 _Color;
|
||||
fixed _Mask;
|
||||
|
||||
v2f vert(appdata v)
|
||||
{
|
||||
v2f o;
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
|
||||
return o;
|
||||
}
|
||||
|
||||
fixed4 frag(v2f i) : SV_Target
|
||||
{
|
||||
fixed4 tex = uddGetTexture(_MainTex, i.uv);
|
||||
fixed alpha = pow((tex.r + tex.g + tex.b) / 3.0, _Mask);
|
||||
return fixed4(tex.rgb * _Color.rgb, alpha);
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8d8bed8b85c7bb844a44481b2feb53ca
|
||||
timeCreated: 1477561145
|
||||
licenseType: Pro
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,69 @@
|
||||
Shader "uDesktopDuplication/Unlit Transparent"
|
||||
{
|
||||
|
||||
Properties
|
||||
{
|
||||
_Color ("Color", Color) = (1, 1, 1, 1)
|
||||
_MainTex ("Texture", 2D) = "white" {}
|
||||
}
|
||||
|
||||
SubShader
|
||||
{
|
||||
|
||||
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
|
||||
|
||||
ZWrite Off
|
||||
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;
|
||||
|
||||
v2f vert(appdata v)
|
||||
{
|
||||
v2f o;
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
|
||||
return o;
|
||||
}
|
||||
|
||||
fixed4 frag(v2f i) : SV_Target
|
||||
{
|
||||
return fixed4(uddGetTexture(_MainTex, i.uv).rgb, 1.0) * _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/Transparent"
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 32b14c1fb31640f43b002138823eb1e1
|
||||
timeCreated: 1477560210
|
||||
licenseType: Pro
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
405
Plugins/uDesktopDuplication/'
Normal file
405
Plugins/uDesktopDuplication/'
Normal file
@@ -0,0 +1,405 @@
|
||||
#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);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
#include <d3d11.h>
|
||||
#include <dxgi1_2.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "IUnityInterface.h"
|
||||
#include "IUnityGraphics.h"
|
||||
@@ -10,57 +12,77 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
IUnityInterfaces* g_unity = nullptr;
|
||||
IDXGIOutputDuplication* g_deskDupl = nullptr;
|
||||
ID3D11Texture2D* g_texture = nullptr;
|
||||
bool g_isPointerVisible = -1;
|
||||
int g_pointerX = -1;
|
||||
int g_pointerY = -1;
|
||||
int g_width = -1;
|
||||
int g_height = -1;
|
||||
struct Monitor
|
||||
{
|
||||
int id = -1;
|
||||
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_mouseMonitor = 0;
|
||||
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.
|
||||
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++)
|
||||
for (int j = 0; (adapter->EnumOutputs(j, &output) != DXGI_ERROR_NOT_FOUND); ++j)
|
||||
{
|
||||
DXGI_OUTPUT_DESC outputDesc;
|
||||
output->GetDesc(&outputDesc);
|
||||
Monitor monitor;
|
||||
monitor.id = id++;
|
||||
|
||||
MONITORINFOEX monitorInfo;
|
||||
monitorInfo.cbSize = sizeof(MONITORINFOEX);
|
||||
GetMonitorInfo(outputDesc.Monitor, &monitorInfo);
|
||||
|
||||
if (monitorInfo.dwFlags == MONITORINFOF_PRIMARY)
|
||||
{
|
||||
g_width = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left;
|
||||
g_height = monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top;
|
||||
|
||||
auto device = g_unity->Get<IUnityGraphicsD3D11>()->GetDevice();
|
||||
IDXGIOutput1* output1;
|
||||
output1 = reinterpret_cast<IDXGIOutput1*>(output);
|
||||
output1->DuplicateOutput(device, &g_deskDupl);
|
||||
|
||||
output->Release();
|
||||
adapter->Release();
|
||||
factory->Release();
|
||||
|
||||
return;
|
||||
}
|
||||
output->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();
|
||||
@@ -69,12 +91,6 @@ extern "C"
|
||||
factory->Release();
|
||||
}
|
||||
|
||||
void FinalizeDuplication()
|
||||
{
|
||||
g_deskDupl->Release();
|
||||
g_deskDupl = nullptr;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces)
|
||||
{
|
||||
g_unity = unityInterfaces;
|
||||
@@ -84,62 +100,233 @@ extern "C"
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UnityPluginUnload()
|
||||
{
|
||||
g_unity = nullptr;
|
||||
g_texture = nullptr;
|
||||
g_width = -1;
|
||||
g_height = -1;
|
||||
g_pointerX = -1;
|
||||
g_pointerY = -1;
|
||||
FinalizeDuplication();
|
||||
}
|
||||
|
||||
void UNITY_INTERFACE_API OnRenderEvent(int eventId)
|
||||
bool IsValidMonitorId(int id)
|
||||
{
|
||||
if (g_deskDupl == nullptr || g_texture == nullptr) return;
|
||||
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;
|
||||
}
|
||||
|
||||
void UNITY_INTERFACE_API OnRenderEvent(int id)
|
||||
{
|
||||
if (!IsValidMonitorId(id)) return;
|
||||
auto& monitor = g_monitors[id];
|
||||
|
||||
if (monitor.deskDupl == nullptr || monitor.texture == nullptr) return;
|
||||
|
||||
HRESULT hr;
|
||||
IDXGIResource* resource = nullptr;
|
||||
DXGI_OUTDUPL_FRAME_INFO frameInfo;
|
||||
|
||||
const UINT timeout = 100; // ms
|
||||
hr = g_deskDupl->AcquireNextFrame(timeout, &frameInfo, &resource);
|
||||
switch (hr)
|
||||
g_errorCode = monitor.deskDupl->AcquireNextFrame(g_timeout, &frameInfo, &resource);
|
||||
if (FAILED(g_errorCode))
|
||||
{
|
||||
case S_OK:
|
||||
if (g_errorCode == DXGI_ERROR_ACCESS_LOST)
|
||||
{
|
||||
break;
|
||||
}
|
||||
case DXGI_ERROR_ACCESS_LOST:
|
||||
{
|
||||
FinalizeDuplication();
|
||||
InitializeDuplication();
|
||||
return;
|
||||
g_errorMessage = "[IDXGIOutputDuplication::AcquireNextFrame()] Access lost.";
|
||||
}
|
||||
default:
|
||||
else
|
||||
{
|
||||
return;
|
||||
g_errorMessage = "[IDXGIOutputDuplication::AcquireNextFrame()] Maybe timeout.";
|
||||
}
|
||||
}
|
||||
|
||||
g_isPointerVisible = frameInfo.PointerPosition.Visible;
|
||||
g_pointerX = frameInfo.PointerPosition.Position.x;
|
||||
g_pointerY = frameInfo.PointerPosition.Position.y;
|
||||
|
||||
ID3D11Texture2D* texture;
|
||||
hr = resource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&texture));
|
||||
if (hr != S_OK)
|
||||
{
|
||||
resource->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
resource->Release();
|
||||
ID3D11Texture2D* texture;
|
||||
resource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&texture));
|
||||
|
||||
ID3D11DeviceContext* context;
|
||||
auto device = g_unity->Get<IUnityGraphicsD3D11>()->GetDevice();
|
||||
device->GetImmediateContext(&context);
|
||||
context->CopyResource(g_texture, texture);
|
||||
context->CopyResource(monitor.texture, texture);
|
||||
|
||||
g_deskDupl->ReleaseFrame();
|
||||
if (!UpdateMouse(frameInfo, monitor))
|
||||
{
|
||||
g_errorCode = -999;
|
||||
g_errorMessage = "[UpdateMouse()] failed.";
|
||||
}
|
||||
|
||||
resource->Release();
|
||||
monitor.deskDupl->ReleaseFrame();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT UnityRenderingEvent UNITY_INTERFACE_API GetRenderEventFunc()
|
||||
@@ -147,33 +334,114 @@ extern "C"
|
||||
return OnRenderEvent;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetWidth()
|
||||
UNITY_INTERFACE_EXPORT size_t UNITY_INTERFACE_API GetMonitorCount()
|
||||
{
|
||||
return g_width;
|
||||
return g_monitors.size();
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetHeight()
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetTimeout(int timeout)
|
||||
{
|
||||
return g_height;
|
||||
g_timeout = timeout;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API IsPointerVisible()
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API GetName(int id, char* buf, int len)
|
||||
{
|
||||
return g_isPointerVisible;
|
||||
if (!IsValidMonitorId(id)) return;
|
||||
strcpy_s(buf, len, g_monitors[id].monitorInfo.szDevice);
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetPointerX()
|
||||
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API IsPrimary(int id)
|
||||
{
|
||||
return g_pointerX;
|
||||
if (!IsValidMonitorId(id)) return false;
|
||||
return g_monitors[id].monitorInfo.dwFlags == MONITORINFOF_PRIMARY;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetPointerY()
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetWidth(int id)
|
||||
{
|
||||
return g_pointerY;
|
||||
if (!IsValidMonitorId(id)) return -1;
|
||||
const auto rect = g_monitors[id].monitorInfo.rcMonitor;
|
||||
return rect.right - rect.left;
|
||||
}
|
||||
|
||||
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetTexturePtr(void* texture)
|
||||
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetHeight(int id)
|
||||
{
|
||||
g_texture = reinterpret_cast<ID3D11Texture2D*>(texture);
|
||||
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;
|
||||
const auto& info = g_monitors[id].pointer.shapeInfo;
|
||||
return (info.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME) ? info.Height / 2 : info.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;
|
||||
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);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
48
README.md
48
README.md
@@ -1,12 +1,12 @@
|
||||
uDesktopDuplication
|
||||
===================
|
||||
|
||||
**uDesktopDuplication** is Desktop Duplication API implementation for Unity.
|
||||
**uDesktopDuplication** is an Unity asset to use the realtime screen capture as `Texture2D` using Desktop Duplication API.
|
||||
|
||||
|
||||
ScreenShot
|
||||
----------
|
||||

|
||||

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