Compare commits

...

17 Commits

Author SHA1 Message Date
hecomi
63d8f6936d use ComPtr for resources instead of standard smart pointers. 2016-11-12 02:44:20 +09:00
hecomi
e4e4917bcb fix manager non-initialization bug. 2016-11-12 02:05:09 +09:00
hecomi
0e205ecef7 fix zoom bug. 2016-11-12 02:00:04 +09:00
hecomi
cda8024b17 fix break in error image. 2016-11-12 00:40:11 +09:00
hecomi
05085f7d24 fix bug caused when releasing duplication output. 2016-11-10 23:33:19 +09:00
hecomi
52eafbe919 change unsupported texture. 2016-11-10 23:21:04 +09:00
hecomi
be1e0c25bb expand aabb to avoid culling. 2016-11-10 23:06:24 +09:00
hecomi
372e44d8c1 output logs. 2016-11-10 22:35:46 +09:00
hecomi
d79eb91333 fix freeze bug caused by masked cursor boundary conditions. 2016-11-10 02:50:59 +09:00
hecomi
f654df7f5b add debug logs and use safer code. 2016-11-09 21:58:00 +09:00
hecomi
620762dba7 add logging and use smart pointers for buffers. 2016-11-09 19:40:05 +09:00
hecomi
43ba445623 Merge branch 'master' of github.com:hecomi/uDesktopDuplication 2016-11-09 02:55:32 +09:00
hecomi
c7c12de730 move dll location. 2016-11-09 02:55:26 +09:00
hecomi
1086595d6b fix bug that did not remove unsupported monitor element in list. 2016-11-09 02:54:49 +09:00
hecomi
f69fb6a7bb remove monitor if unsupported. 2016-11-09 00:50:57 +09:00
hecomi
b661b4a93f modify zoom scene. 2016-11-09 00:23:25 +09:00
hecomi
8d9ef71829 update monitor id of loupe. 2016-11-09 00:18:52 +09:00
30 changed files with 542 additions and 203 deletions

1
.gitignore vendored
View File

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

View File

@@ -7,21 +7,19 @@ public class Loupe : MonoBehaviour
public float aspect = 1f;
void Start()
{
uddTexture_ = GetComponent<uDesktopDuplication.Texture>();
{
uddTexture_ = GetComponent<uDesktopDuplication.Texture>();
uddTexture_.useClip = true;
}
void Update()
{
// To get other monitor textures, set dirty flag.
foreach (var monitor in uDesktopDuplication.Manager.monitors) {
monitor.CreateTexture();
monitor.shouldBeUpdated = true;
}
uddTexture_.monitorId = uDesktopDuplication.Manager.cursorMonitorId;
if (!uddTexture_.monitor.isCursorVisible) {
uddTexture_.monitorId = uDesktopDuplication.Manager.cursorMonitorId;
// To get other monitor textures, set dirty flag.
foreach (var monitor in uDesktopDuplication.Manager.monitors) {
monitor.CreateTexture();
monitor.shouldBeUpdated = true;
}
var x = (float)uddTexture_.monitor.cursorX / uddTexture_.monitor.width;

View File

@@ -4,23 +4,35 @@ using System.Collections.Generic;
public class MultipleMonitorCreator : MonoBehaviour
{
[SerializeField] GameObject monitorPrefab;
[SerializeField] bool removeIfUnsupported = true;
[SerializeField] float removeWaitDuration = 5f;
bool hasMonitorUnsupportStateChecked = false;
float removeWaitTimer_ = 0f;
public class MonitorInfo
{
public GameObject gameObject { get; set; }
public Quaternion originalRotation { get; set; }
public uDesktopDuplication.Texture uddTexture { get; set; }
public Vector3 meshBounds;
public Mesh mesh { get; set; }
}
private List<MonitorInfo> monitors_ = new List<MonitorInfo>();
public List<MonitorInfo> monitors { get { return monitors_; } }
private List<MonitorInfo> monitors_ = new List<MonitorInfo>();
public List<MonitorInfo> monitors { get { return monitors_; } }
void Start()
{
uDesktopDuplication.Manager.CreateInstance();
Create();
}
void Update()
{
if (removeIfUnsupported) {
RemoveUnsupportedDisplayAfterRemoveTimer();
}
}
void OnEnable()
{
uDesktopDuplication.Manager.onReinitialized += Recreate;
@@ -31,18 +43,45 @@ public class MultipleMonitorCreator : MonoBehaviour
uDesktopDuplication.Manager.onReinitialized -= Recreate;
}
void RemoveUnsupportedDisplayAfterRemoveTimer()
{
if (!hasMonitorUnsupportStateChecked) {
removeWaitTimer_ += Time.deltaTime;
if (removeWaitTimer_ > removeWaitDuration) {
hasMonitorUnsupportStateChecked = true;
foreach (var info in monitors_) {
if (info.uddTexture.monitor.state == uDesktopDuplication.MonitorState.Unsupported) {
Destroy(info.gameObject);
}
}
monitors_.RemoveAll(info => info.uddTexture.monitor.state == uDesktopDuplication.MonitorState.Unsupported);
}
}
}
void ResetRemoveTimer()
{
hasMonitorUnsupportStateChecked = false;
removeWaitTimer_ = 0f;
}
void Create()
{
// Sort monitors in coordinate order
var monitors = uDesktopDuplication.Manager.monitors;
monitors.Sort((a, b) => a.left - b.left);
ResetRemoveTimer();
// Create monitors
for (int i = 0 ; i < uDesktopDuplication.Manager.monitorCount; ++i) {
for (int i = 0; i < uDesktopDuplication.Manager.monitorCount; ++i) {
// Create monitor obeject
var go = Instantiate(monitorPrefab);
go.name = "Monitor " + i;
// Expand AABB
var mesh = go.GetComponent<MeshFilter>().mesh; // clone
var aabbScale = mesh.bounds.size;
aabbScale.y = Mathf.Max(aabbScale.y, aabbScale.x);
aabbScale.z = Mathf.Max(aabbScale.z, aabbScale.x);
mesh.bounds = new Bounds(mesh.bounds.center, aabbScale);
// Assign monitor
var texture = go.GetComponent<uDesktopDuplication.Texture>();
texture.monitorId = i;
@@ -54,17 +93,17 @@ public class MultipleMonitorCreator : MonoBehaviour
// Set parent as this object
go.transform.SetParent(transform);
// Calc actual size considering mesh size
var bounds = go.GetComponent<MeshFilter>().sharedMesh.bounds.extents * 2f;
// Save
var info = new MonitorInfo();
info.gameObject = go;
info.originalRotation = go.transform.rotation;
info.uddTexture = texture;
info.meshBounds = bounds;
info.mesh = mesh;
monitors_.Add(info);
}
// Sort monitors in coordinate order
monitors_.Sort((a, b) => a.uddTexture.monitor.left - b.uddTexture.monitor.left);
}
void Clear()

View File

@@ -15,13 +15,13 @@ public class MultipleMonitorLayouter : MonoBehaviour
protected virtual void Update()
{
if (updateEveryFrame) {
Layout();
if (updateEveryFrame) {
Layout();
}
}
void LateUpdate()
{
{
margin = Mathf.Max(margin, 0f);
}
@@ -32,7 +32,7 @@ public class MultipleMonitorLayouter : MonoBehaviour
var totalWidth = 0f;
foreach (var info in monitors) {
var width = info.uddTexture.monitor.widthMeter * info.meshBounds.x;
var width = info.uddTexture.monitor.widthMeter * (info.mesh.bounds.extents.x * 2f);
totalWidth += width;
}
totalWidth += margin * (n - 1);
@@ -41,10 +41,10 @@ public class MultipleMonitorLayouter : MonoBehaviour
foreach (var info in creator_.monitors) {
var monitor = info.uddTexture.monitor;
x += (monitor.widthMeter * info.meshBounds.x) / 2;
x += (monitor.widthMeter * info.mesh.bounds.extents.x);
info.gameObject.transform.localPosition = new Vector3(x, 0f, 0f);
info.gameObject.transform.localRotation = info.originalRotation;
x += (monitor.widthMeter * info.meshBounds.x) / 2 + margin;
x += (monitor.widthMeter * info.mesh.bounds.extents.x) + margin;
}
}

View File

@@ -18,7 +18,7 @@ public class MultipleMonitorRoundLayouter : MultipleMonitorLayouter
var totalWidth = 0f;
foreach (var info in monitors) {
var width = info.uddTexture.monitor.widthMeter * info.meshBounds.x;
var width = info.uddTexture.monitor.widthMeter * (info.mesh.bounds.extents.x * 2f);
totalWidth += width;
}
totalWidth += margin * (n - 1);
@@ -29,7 +29,7 @@ public class MultipleMonitorRoundLayouter : MultipleMonitorLayouter
float angle = -totalAngle / 2;
foreach (var info in monitors) {
var uddTex = info.uddTexture;
var width = uddTex.monitor.widthMeter * info.meshBounds.x;
var width = uddTex.monitor.widthMeter * (info.mesh.bounds.extents.x * 2f);
angle += (width / radius) * 0.5f;
uddTex.transform.localPosition = radius * new Vector3(Mathf.Sin(angle), 0f, Mathf.Cos(angle) - 1f);

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -1,4 +1,5 @@
using UnityEngine;
using UnityEngine.Assertions;
using System.Collections.Generic;
namespace uDesktopDuplication
@@ -53,6 +54,7 @@ public class Cursor : MonoBehaviour
}
var cursorTexture = textures_[key];
Assert.IsNotNull(cursorTexture);
monitor.GetCursorTexture(cursorTexture.GetNativeTexturePtr());
uddTexture_.material.SetTexture("_CursorTex", cursorTexture);
}

View File

@@ -39,9 +39,18 @@ public enum MonitorState
AccessLost = 6,
}
public enum DebugMode
{
None = 0,
File = 1,
UnityLog = 2, /* currently has bug when app exits. */
}
public static class Lib
{
public delegate void MessageHandler(Message message);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DebugLogDelegate(string str);
[DllImport("uDesktopDuplication")]
public static extern void InitializeUDD();
@@ -54,6 +63,16 @@ public static class Lib
[DllImport("uDesktopDuplication")]
public static extern Message PopMessage();
[DllImport("uDesktopDuplication")]
public static extern void EnableDebug();
[DllImport("uDesktopDuplication")]
public static extern void DisableDebug();
[DllImport("uDesktopDuplication")]
public static extern void SetDebugMode(DebugMode mode);
[DllImport("uDesktopDuplication")]
public static extern void SetLogFunc(IntPtr func);
[DllImport("uDesktopDuplication")]
public static extern void SetErrorFunc(IntPtr func);
[DllImport("uDesktopDuplication")]
public static extern int GetMonitorCount();
[DllImport("uDesktopDuplication")]
public static extern int GetCursorMonitorId();
@@ -66,6 +85,8 @@ public static class Lib
[DllImport("uDesktopDuplication")]
public static extern IntPtr GetRenderEventFunc();
[DllImport("uDesktopDuplication")]
public static extern int GetId(int id);
[DllImport("uDesktopDuplication")]
public static extern MonitorState GetState(int id);
[DllImport("uDesktopDuplication")]
public static extern void GetName(int id, StringBuilder buf, int len);

View File

@@ -1,6 +1,7 @@
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace uDesktopDuplication
{
@@ -10,21 +11,7 @@ public class Manager : MonoBehaviour
private static Manager instance_;
public static Manager instance
{
get
{
if (instance_) {
return instance_;
}
var manager = FindObjectOfType<Manager>();
if (manager) {
manager.Awake();
return manager;
}
var go = new GameObject("uDesktopDuplicationManager");
return go.AddComponent<Manager>();
}
get { return CreateInstance(); }
}
private List<Monitor> monitors_ = new List<Monitor>();
@@ -51,18 +38,36 @@ public class Manager : MonoBehaviour
}
}
[SerializeField] DebugMode debugMode = DebugMode.File;
[SerializeField] int desktopDuplicationApiTimeout = 0;
[SerializeField] float retryReinitializationDuration = 1f;
private Coroutine renderCoroutine_ = null;
private bool shouldReinitialize = false;
private float reinitializationTimer = 0f;
private bool shouldReinitialize_ = false;
private float reinitializationTimer_ = 0f;
public delegate void ReinitializeHandler();
public static event ReinitializeHandler onReinitialized;
public static Manager CreateInstance()
{
if (instance_) {
return instance_;
}
var manager = FindObjectOfType<Manager>();
if (manager) {
manager.Awake();
return manager;
}
var go = new GameObject("uDesktopDuplicationManager");
return go.AddComponent<Manager>();
}
void Awake()
{
Lib.SetDebugMode(debugMode);
Lib.InitializeUDD();
if (instance_ != null) return;
@@ -96,12 +101,6 @@ public class Manager : MonoBehaviour
Lib.Update();
ReinitializeIfNeeded();
UpdateMessage();
/*
foreach (var monitor in monitors_) {
Debug.LogFormat("[{0}] {1}", monitor.id, monitor.state);
}
*/
}
[ContextMenu("Reinitialize")]
@@ -120,20 +119,20 @@ public class Manager : MonoBehaviour
monitor.state == MonitorState.AccessLost ||
monitor.state == MonitorState.AccessDenied ||
monitor.state == MonitorState.SessionDisconnected) {
if (!shouldReinitialize) {
shouldReinitialize = true;
reinitializationTimer = 0f;
if (!shouldReinitialize_) {
shouldReinitialize_ = true;
reinitializationTimer_ = 0f;
break;
}
}
}
if (shouldReinitialize) {
if (reinitializationTimer > retryReinitializationDuration) {
if (shouldReinitialize_) {
if (reinitializationTimer_ > retryReinitializationDuration) {
Reinitialize();
shouldReinitialize = false;
shouldReinitialize_ = false;
}
reinitializationTimer += Time.deltaTime;
reinitializationTimer_ += Time.deltaTime;
}
}
@@ -168,6 +167,7 @@ public class Manager : MonoBehaviour
void CreateMonitors()
{
monitors.Clear();
for (int i = 0; i < monitorCount; ++i) {
monitors.Add(new Monitor(i));
}

View File

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

View File

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

View File

@@ -1,9 +1,13 @@
#include <d3d11.h>
#include <wrl/client.h>
#include "Common.h"
#include "Debug.h"
#include "MonitorManager.h"
#include "Monitor.h"
#include "Cursor.h"
using namespace Microsoft::WRL;
Cursor::Cursor(Monitor* monitor)
: monitor_(monitor)
@@ -13,16 +17,6 @@ Cursor::Cursor(Monitor* monitor)
Cursor::~Cursor()
{
if (apiBuffer_ != nullptr)
{
delete[] apiBuffer_;
apiBuffer_ = nullptr;
}
if (bgra32Buffer_ != nullptr)
{
delete[] bgra32Buffer_;
bgra32Buffer_ = nullptr;
}
}
@@ -33,16 +27,21 @@ void Cursor::UpdateBuffer(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
return;
}
x_ = frameInfo.PointerPosition.Position.x;
y_ = frameInfo.PointerPosition.Position.y;
isVisible_ = frameInfo.PointerPosition.Visible != 0;
timestamp_ = frameInfo.LastMouseUpdateTime;
if (isVisible_)
{
GetMonitorManager()->SetCursorMonitorId(monitor_->GetId());
}
x_ = frameInfo.PointerPosition.Position.x;
y_ = frameInfo.PointerPosition.Position.y;
timestamp_ = frameInfo.LastMouseUpdateTime;
if (!IsCursorOnParentMonitor())
{
return;
}
if (frameInfo.PointerShapeBufferSize == 0)
{
return;
@@ -51,54 +50,95 @@ void Cursor::UpdateBuffer(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
// Increase the buffer size if needed
if (frameInfo.PointerShapeBufferSize > apiBufferSize_)
{
if (apiBuffer_) delete[] apiBuffer_;
apiBuffer_ = new BYTE[frameInfo.PointerShapeBufferSize];
apiBufferSize_ = frameInfo.PointerShapeBufferSize;
apiBuffer_ = std::make_unique<BYTE[]>(apiBufferSize_);
}
if (apiBuffer_ == nullptr) return;
if (!apiBuffer_) return;
// Get mouse pointer information
UINT bufferSize;
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo;
const auto hr = monitor_->GetDeskDupl()->GetFramePointerShape(
frameInfo.PointerShapeBufferSize,
reinterpret_cast<void*>(apiBuffer_),
apiBufferSize_,
reinterpret_cast<void*>(apiBuffer_.get()),
&bufferSize,
&shapeInfo_);
&shapeInfo);
if (FAILED(hr))
{
delete[] apiBuffer_;
apiBuffer_ = nullptr;
Debug::Error("Cursor::UpdateBuffer() => GetFramePointerShape() failed.");
apiBuffer_.reset();
apiBufferSize_ = 0;
}
shapeInfo_ = shapeInfo;
}
void Cursor::UpdateTexture()
{
if (!IsCursorOnParentMonitor())
{
return;
}
// cursor type
const bool isMono = GetType() == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME;
const bool isColorMask = GetType() == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR;
// Size
const auto w = GetWidth();
const auto h = GetHeight();
const auto w0 = GetWidth();
const auto h0 = GetHeight();
const auto p = GetPitch();
auto w = w0;
auto h = h0;
// Convert the buffer given by API into BGRA32
const UINT bgraBufferSize = w * h * 4;
const UINT bgraBufferSize = w0 * h0 * 4;
if (bgraBufferSize > bgra32BufferSize_)
{
if (bgra32Buffer_) delete[] bgra32Buffer_;
bgra32Buffer_ = new BYTE[bgraBufferSize];
bgra32BufferSize_ = bgraBufferSize;
bgra32Buffer_ = std::make_unique<BYTE[]>(bgra32BufferSize_);
}
if (bgra32Buffer_ == nullptr) return;
if (!bgra32Buffer_) return;
// If masked, copy the desktop image and merge it with masked image.
if (isMono || isColorMask)
{
HRESULT hr;
const auto mw = monitor_->GetWidth();
const auto mh = monitor_->GetHeight();
auto x = x_;
auto y = y_;
auto colMin = 0;
auto colMax = w0;
auto rowMin = 0;
auto rowMax = h0;
if (x < 0)
{
x = 0;
w = w0 + x_;
colMin = w0 - w;
}
if (x + w >= mw)
{
w = mw - x_;
colMax = w;
}
if (y < 0)
{
y = 0;
h = h0 + y_;
rowMin = h0 - h;
}
if (y + h >= mh)
{
h = mh - y_;
rowMax = h;
}
D3D11_TEXTURE2D_DESC desc;
desc.Width = w;
desc.Height = h;
@@ -112,31 +152,39 @@ void Cursor::UpdateTexture()
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.MiscFlags = 0;
ID3D11Texture2D* texture = nullptr;
ComPtr<ID3D11Texture2D> texture;
hr = GetDevice()->CreateTexture2D(&desc, nullptr, &texture);
if (FAILED(hr))
{
Debug::Error("Cursor::UpdateTexture() => GetDevice()->CreateTexture2D() failed.");
return;
}
D3D11_BOX box;
box.front = 0;
box.back = 1;
box.left = x_;
box.top = y_;
box.right = x_ + w;
box.bottom = y_ + h;
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();
if (monitor_->GetUnityTexture() == nullptr)
{
Debug::Error("Cursor::UpdateTexture() => Monitor::GetUnityTexture() is null.");
return;
}
IDXGISurface* surface = nullptr;
hr = texture->QueryInterface(__uuidof(IDXGISurface), (void**)&surface);
texture->Release();
{
ComPtr<ID3D11DeviceContext> context;
GetDevice()->GetImmediateContext(&context);
context->CopySubresourceRegion(texture.Get(), 0, 0, 0, 0, monitor_->GetUnityTexture(), 0, &box);
}
ComPtr<IDXGISurface> surface;
hr = texture.As<IDXGISurface>(&surface);
if (FAILED(hr))
{
Debug::Error("Cursor::UpdateTexture() => texture->QueryInterface() failed.");
return;
}
@@ -144,7 +192,7 @@ void Cursor::UpdateTexture()
hr = surface->Map(&mappedSurface, DXGI_MAP_READ);
if (FAILED(hr))
{
surface->Release();
Debug::Error("Cursor::UpdateTexture() => surface->Map() failed.");
return;
}
@@ -153,40 +201,39 @@ void Cursor::UpdateTexture()
const UINT desktopPitch = mappedSurface.Pitch / sizeof(UINT);
// Access RGBA values at the same time
auto output32 = reinterpret_cast<UINT*>(bgra32Buffer_);
auto output32 = reinterpret_cast<UINT*>(bgra32Buffer_.get());
if (isMono)
{
for (int row = 0; row < h; ++row)
for (int row = rowMin, y = 0; row < rowMax; ++row, ++y)
{
BYTE mask = 0x80;
for (int col = 0; col < w; ++col)
for (int col = colMin, x = 0; col < colMax; ++col, ++x)
{
const int i = row * w + col;
BYTE mask = 0b10000000 >> (col % 8);
const int i = row * w0 + col;
const BYTE andMask = apiBuffer_[col / 8 + row * p] & mask;
const BYTE xorMask = apiBuffer_[col / 8 + (row + h) * p] & mask;
const UINT andMask32 = andMask ? 0xFFFFFFFF : 0x00000000;
const UINT xorMask32 = xorMask ? 0xFFFFFFFF : 0x00000000;
output32[i] = (desktop32[row * desktopPitch + col] & andMask32) ^ xorMask32;
mask = (mask == 0x01) ? 0x80 : (mask >> 1);
output32[i] = (desktop32[y * desktopPitch + x] & andMask32) ^ xorMask32;
}
}
}
else // DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR
{
const auto buffer32 = reinterpret_cast<UINT*>(apiBuffer_);
const auto buffer32 = reinterpret_cast<UINT*>(apiBuffer_.get());
for (int row = 0; row < h; ++row)
for (int row = rowMin, y = 0; row < rowMax; ++row, ++y)
{
for (int col = 0; col < w; ++col)
for (int col = colMin, x = 0; col < colMax; ++col, ++x)
{
const int i = row * w + col;
const int j = row * p / sizeof(UINT) + col;
const int i = col + row * w0;
const int j = col + row * p / sizeof(UINT);
UINT mask = 0xFF000000 & buffer32[j];
if (mask)
{
output32[i] = (desktop32[row * desktopPitch + col] ^ buffer32[j]) | 0xFF000000;
output32[i] = (desktop32[y * desktopPitch + x] ^ buffer32[j]) | 0xFF000000;
}
else
{
@@ -197,7 +244,6 @@ void Cursor::UpdateTexture()
}
hr = surface->Unmap();
surface->Release();
if (FAILED(hr))
{
return;
@@ -205,25 +251,46 @@ void Cursor::UpdateTexture()
}
else // DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR
{
auto output32 = reinterpret_cast<UINT*>(bgra32Buffer_);
const auto buffer32 = reinterpret_cast<UINT*>(apiBuffer_);
auto output32 = reinterpret_cast<UINT*>(bgra32Buffer_.get());
const auto buffer32 = reinterpret_cast<UINT*>(apiBuffer_.get());
for (int i = 0; i < w * h; ++i)
{
output32[i] = buffer32[i];
}
}
return;
}
void Cursor::GetTexture(ID3D11Texture2D* texture)
{
if (bgra32Buffer_ == nullptr || texture == nullptr) return;
ID3D11DeviceContext* context;
if (bgra32Buffer_ == nullptr)
{
Debug::Error("Cursor::GetTexture() => bgra32Buffer is null.");
return;
}
if (texture == nullptr)
{
Debug::Error("Cursor::GetTexture() => The given texture is null.");
return;
}
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
if ((int)desc.Width < GetWidth() || (int)desc.Height < GetHeight())
{
char buf[256];
sprintf_s(buf, 256,
"Cursor::GetTexture() => The given texture has smaller width / height.\n"
"Given => (%d, %d) Buffer => (%d, %d)",
desc.Width, desc.Height, GetWidth(), GetHeight());
Debug::Error(buf);
return;
}
ComPtr<ID3D11DeviceContext> context;
GetDevice()->GetImmediateContext(&context);
context->UpdateSubresource(texture, 0, nullptr, bgra32Buffer_, shapeInfo_.Width * 4, 0);
context->Release();
context->UpdateSubresource(texture, 0, nullptr, bgra32Buffer_.get(), GetWidth() * 4, 0);
}
@@ -269,3 +336,9 @@ int Cursor::GetType() const
{
return shapeInfo_.Type;
}
bool Cursor::IsCursorOnParentMonitor() const
{
return GetMonitorManager()->GetCursorMonitorId() == monitor_->GetId();
}

View File

@@ -1,3 +1,5 @@
#pragma once
#include <d3d11.h>
#include <dxgi1_2.h>
#include <memory>
@@ -22,13 +24,15 @@ public:
int GetType() const;
private:
bool IsCursorOnParentMonitor() const;
Monitor* monitor_;
bool isVisible_ = false;
int x_ = -1;
int y_ = -1;
BYTE* apiBuffer_ = nullptr;
std::unique_ptr<BYTE[]> apiBuffer_ = nullptr;
UINT apiBufferSize_ = 0;
BYTE* bgra32Buffer_ = nullptr;
std::unique_ptr<BYTE[]> bgra32Buffer_ = nullptr;
UINT bgra32BufferSize_ = 0;
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo_;
LARGE_INTEGER timestamp_;

View File

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

View File

@@ -0,0 +1,33 @@
#pragma once
#include <fstream>
#include "IUnityInterface.h"
// Logging
class Debug
{
public:
enum class Mode
{
kNone = 0,
kFile = 1,
kUnityLog = 2,
};
using DebugLogFuncPtr = void(UNITY_INTERFACE_API *)(const char*);
static void SetMode(Mode mode) { mode_ = mode; }
static void Initialize();
static void Finalize();
static void SetLogFunc(DebugLogFuncPtr func) { logFunc_ = func; }
static void SetErrorFunc(DebugLogFuncPtr func) { errFunc_ = func; }
public:
static void Log(const char* msg);
static void Error(const char* msg);
private:
static Mode mode_;
static std::ofstream fs_;
static DebugLogFuncPtr logFunc_;
static DebugLogFuncPtr errFunc_;
};

View File

@@ -1,10 +1,13 @@
#include <d3d11.h>
#include <ShellScalingAPI.h>
#include "Common.h"
#include "Debug.h"
#include "Cursor.h"
#include "MonitorManager.h"
#include "Monitor.h"
using namespace Microsoft::WRL;
Monitor::Monitor(int id)
: id_(id)
@@ -13,7 +16,12 @@ Monitor::Monitor(int id)
}
HRESULT Monitor::Initialize(IDXGIOutput* output)
Monitor::~Monitor()
{
}
void Monitor::Initialize(IDXGIOutput* output)
{
output->GetDesc(&outputDesc_);
monitorInfo_.cbSize = sizeof(MONITORINFOEX);
@@ -29,53 +37,46 @@ HRESULT Monitor::Initialize(IDXGIOutput* output)
{
case S_OK:
state_ = State::Available;
Debug::Log("Monitor::Initialize() => OK.");
break;
case E_INVALIDARG:
state_ = State::InvalidArg;
Debug::Error("Monitor::Initialize() => Invalid arguments.");
break;
case E_ACCESSDENIED:
// For example, when the user presses Ctrl + Alt + Delete and the screen
// switches to admin screen, this error occurs.
state_ = State::AccessDenied;
Debug::Error("Monitor::Initialize() => Access denied.");
break;
case DXGI_ERROR_UNSUPPORTED:
// If the display adapter on the computer is running under the Microsoft Hybrid system,
// this error occurs.
state_ = State::Unsupported;
Debug::Error("Monitor::Initialize() => Unsupported display.");
break;
case DXGI_ERROR_NOT_CURRENTLY_AVAILABLE:
// When other application use Desktop Duplication API, this error occurs.
state_ = State::CurrentlyNotAvailable;
Debug::Error("Monitor::Initialize() => Currently not available.");
break;
case DXGI_ERROR_SESSION_DISCONNECTED:
state_ = State::SessionDisconnected;
Debug::Error("Monitor::Initialize() => Session disconnected.");
break;
}
return hr;
}
Monitor::~Monitor()
void Monitor::Render(UINT timeout)
{
if (deskDupl_ != nullptr)
{
deskDupl_->Release();
}
}
if (!deskDupl_) return;
HRESULT Monitor::Render(UINT timeout)
{
if (deskDupl_ == nullptr)
{
return S_OK;
}
IDXGIResource* resource = nullptr;
ComPtr<IDXGIResource> resource;
DXGI_OUTDUPL_FRAME_INFO frameInfo;
HRESULT hr;
const auto hr = deskDupl_->AcquireNextFrame(timeout, &frameInfo, &resource);
hr = deskDupl_->AcquireNextFrame(timeout, &frameInfo, &resource);
if (FAILED(hr))
{
switch (hr)
@@ -83,37 +84,43 @@ HRESULT Monitor::Render(UINT timeout)
case DXGI_ERROR_ACCESS_LOST:
// If any monitor setting has changed (e.g. monitor size has changed),
// it is necessary to re-initialize monitors.
Debug::Log("Monitor::Render() => DXGI_ERROR_ACCESS_LOST.");
state_ = State::AccessLost;
break;
case DXGI_ERROR_WAIT_TIMEOUT:
// This often occurs when timeout value is small and it is not problem.
// Debug::Log("Monitor::Render() => DXGI_ERROR_WAIT_TIMEOUT.");
break;
case DXGI_ERROR_INVALID_CALL:
Debug::Error("Monitor::Render() => DXGI_ERROR_INVALID_CALL.");
break;
case E_INVALIDARG:
Debug::Error("Monitor::Render() => E_INVALIDARG.");
break;
default:
break;
}
return hr;
return;
}
if (unityTexture_)
{
ID3D11Texture2D* texture;
resource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&texture));
ComPtr<ID3D11Texture2D> texture;
resource.As<ID3D11Texture2D>(&texture);
ID3D11DeviceContext* context;
ComPtr<ID3D11DeviceContext> context;
GetDevice()->GetImmediateContext(&context);
context->CopyResource(unityTexture_, texture);
context->Release();
resource->Release();
context->CopyResource(unityTexture_, texture.Get());
}
cursor_->UpdateBuffer(frameInfo);
cursor_->UpdateTexture();
deskDupl_->ReleaseFrame();
return S_OK;
hr = deskDupl_->ReleaseFrame();
if (FAILED(hr))
{
Debug::Error("Monitor::Render() => ReleaseFrame() failed.");
}
}
@@ -141,7 +148,7 @@ ID3D11Texture2D* Monitor::GetUnityTexture() const
}
IDXGIOutputDuplication* Monitor::GetDeskDupl()
const ComPtr<IDXGIOutputDuplication>& Monitor::GetDeskDupl()
{
return deskDupl_;
}

View File

@@ -1,5 +1,8 @@
#pragma once
#include <d3d11.h>
#include <dxgi1_2.h>
#include <wrl/client.h>
#include <memory>
class Cursor;
@@ -23,8 +26,8 @@ public:
explicit Monitor(int id);
~Monitor();
HRESULT Initialize(IDXGIOutput* output);
HRESULT Render(UINT timeout = 0);
void Initialize(IDXGIOutput* output);
void Render(UINT timeout = 0);
void GetCursorTexture(ID3D11Texture2D* texture);
public:
@@ -43,7 +46,7 @@ public:
int GetRotation() const;
int GetDpiX() const;
int GetDpiY() const;
IDXGIOutputDuplication* GetDeskDupl();
const Microsoft::WRL::ComPtr<IDXGIOutputDuplication>& GetDeskDupl();
const std::unique_ptr<Cursor>& GetCursor();
private:
@@ -51,7 +54,7 @@ private:
UINT dpiX_ = -1, dpiY_ = -1;
State state_ = State::NotSet;
std::unique_ptr<Cursor> cursor_;
IDXGIOutputDuplication* deskDupl_ = nullptr;
Microsoft::WRL::ComPtr<IDXGIOutputDuplication> deskDupl_;
ID3D11Texture2D* unityTexture_ = nullptr;
DXGI_OUTPUT_DESC outputDesc_;
MONITORINFOEX monitorInfo_;

View File

@@ -1,5 +1,6 @@
#include <d3d11.h>
#include <dxgi1_2.h>
#include <wrl/client.h>
#include <vector>
#include <string>
#include <algorithm>
@@ -12,6 +13,8 @@
#include "Cursor.h"
#include "MonitorManager.h"
using namespace Microsoft::WRL;
MonitorManager::MonitorManager()
{
@@ -30,26 +33,23 @@ void MonitorManager::Initialize()
Finalize();
// Get factory
IDXGIFactory1* factory;
CreateDXGIFactory1(__uuidof(IDXGIFactory1), reinterpret_cast<void**>(&factory));
ComPtr<IDXGIFactory1> factory;
CreateDXGIFactory1(IID_PPV_ARGS(&factory));
// Check all display adapters
int id = 0;
IDXGIAdapter1* adapter;
ComPtr<IDXGIAdapter1> adapter;
for (int i = 0; (factory->EnumAdapters1(i, &adapter) != DXGI_ERROR_NOT_FOUND); ++i)
{
// Search the main monitor from all outputs
IDXGIOutput* output;
ComPtr<IDXGIOutput> output;
for (int j = 0; (adapter->EnumOutputs(j, &output) != DXGI_ERROR_NOT_FOUND); ++j)
{
auto monitor = std::make_shared<Monitor>(id++);
monitor->Initialize(output);
monitor->Initialize(output.Get());
monitors_.push_back(monitor);
output->Release();
}
adapter->Release();
}
factory->Release();
}

View File

@@ -1,3 +1,5 @@
#pragma once
#include <d3d11.h>
#include <dxgi1_2.h>
#include <vector>

View File

@@ -7,9 +7,9 @@
#include "IUnityInterface.h"
#include "IUnityGraphics.h"
#include "IUnityGraphicsD3D11.h"
#include "Common.h"
#include "Debug.h"
#include "Monitor.h"
#include "Cursor.h"
#include "MonitorManager.h"
@@ -18,36 +18,9 @@
#pragma comment(lib, "Shcore.lib")
namespace
{
IUnityInterfaces* g_unity = nullptr;
std::unique_ptr<MonitorManager> g_manager;
std::queue<Message> g_messages;
}
IUnityInterfaces* GetUnity()
{
return g_unity;
}
ID3D11Device* GetDevice()
{
return GetUnity()->Get<IUnityGraphicsD3D11>()->GetDevice();
}
const std::unique_ptr<MonitorManager>& GetMonitorManager()
{
return g_manager;
}
void SendMessageToUnity(Message message)
{
g_messages.push(message);
}
IUnityInterfaces* g_unity = nullptr;
std::unique_ptr<MonitorManager> g_manager;
std::queue<Message> g_messages;
extern "C"
@@ -57,12 +30,20 @@ extern "C"
if (g_unity && !g_manager)
{
g_manager = std::make_unique<MonitorManager>();
Debug::Initialize();
}
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API FinalizeUDD()
{
g_manager.reset();
std::queue<Message> empty;
g_messages.swap(empty);
Debug::SetLogFunc(nullptr);
Debug::SetErrorFunc(nullptr);
Debug::Finalize();
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces)
@@ -73,8 +54,8 @@ extern "C"
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UnityPluginUnload()
{
g_unity = nullptr;
FinalizeUDD();
g_unity = nullptr;
}
void UNITY_INTERFACE_API OnRenderEvent(int id)
@@ -103,7 +84,7 @@ extern "C"
g_manager->Update();
}
UNITY_INTERFACE_EXPORT Message PopMessage()
UNITY_INTERFACE_EXPORT Message UNITY_INTERFACE_API PopMessage()
{
if (g_messages.empty()) return Message::None;
@@ -112,6 +93,21 @@ extern "C"
return message;
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetDebugMode(Debug::Mode mode)
{
Debug::SetMode(mode);
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetLogFunc(Debug::DebugLogFuncPtr func)
{
Debug::SetLogFunc(func);
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetErrorFunc(Debug::DebugLogFuncPtr func)
{
Debug::SetErrorFunc(func);
}
UNITY_INTERFACE_EXPORT size_t UNITY_INTERFACE_API GetMonitorCount()
{
if (!g_manager) return 0;
@@ -142,6 +138,15 @@ extern "C"
g_manager->SetTimeout(timeout);
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API GetId(int id)
{
if (!g_manager) return;
if (auto monitor = g_manager->GetMonitor(id))
{
monitor->GetId();
}
}
UNITY_INTERFACE_EXPORT MonitorState UNITY_INTERFACE_API GetState(int id)
{
if (!g_manager) return MonitorState::NotSet;

View File

@@ -89,7 +89,7 @@
<SDLCheck>true</SDLCheck>
</ClCompile>
<PostBuildEvent>
<Command>copy /Y "$(SolutionDir)$(Platform)\$(Configuration)\$(TargetFileName)" "$(SolutionDir)..\..\Assets\$(ProjectName)\Plugins"</Command>
<Command>copy /Y "$(SolutionDir)$(Platform)\$(Configuration)\$(TargetFileName)" "$(SolutionDir)..\..\Assets\$(ProjectName)\Plugins\x86_64"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@@ -122,10 +122,12 @@
<OptimizeReferences>true</OptimizeReferences>
</Link>
<PostBuildEvent>
<Command>copy /Y "$(SolutionDir)$(Platform)\$(Configuration)\$(TargetFileName)" "$(SolutionDir)..\..\Assets\$(ProjectName)\Plugins"</Command>
<Command>copy /Y "$(SolutionDir)$(Platform)\$(Configuration)\$(TargetFileName)" "$(SolutionDir)..\..\Assets\$(ProjectName)\Plugins\x86_64"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="Common.cpp" />
<ClCompile Include="Debug.cpp" />
<ClCompile Include="MonitorManager.cpp" />
<ClCompile Include="Main.cpp" />
<ClCompile Include="Monitor.cpp" />
@@ -133,6 +135,7 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="Common.h" />
<ClInclude Include="Debug.h" />
<ClInclude Include="MonitorManager.h" />
<ClInclude Include="include\IUnityGraphics.h" />
<ClInclude Include="include\IUnityGraphicsD3D11.h" />

View File

@@ -19,11 +19,14 @@
<ClInclude Include="Cursor.h" />
<ClInclude Include="Common.h" />
<ClInclude Include="MonitorManager.h" />
<ClInclude Include="Debug.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Monitor.cpp" />
<ClCompile Include="Cursor.cpp" />
<ClCompile Include="Main.cpp" />
<ClCompile Include="MonitorManager.cpp" />
<ClCompile Include="Common.cpp" />
<ClCompile Include="Debug.cpp" />
</ItemGroup>
</Project>