Compare commits

..

13 Commits

Author SHA1 Message Date
hecomi
9c62157c0c fix typo. 2016-11-16 02:27:57 +09:00
hecomi
be79e52135 fix cursor buffer bug. 2016-11-16 02:24:49 +09:00
hecomi
e9c8b4e571 modify component requirements. 2016-11-16 02:07:57 +09:00
hecomi
77c95ab1bb fix indent 2016-11-16 02:04:13 +09:00
hecomi
7b3990107d fix bugs. 2016-11-15 22:29:36 +09:00
hecomi
5c21b65afe reinitialize texture. 2016-11-15 22:02:00 +09:00
hecomi
e83f43d7fd fix crash bugs and memory leaks. 2016-11-15 21:50:35 +09:00
hecomi
58e14d76d8 make code more stable (still has crash bug). 2016-11-15 03:06:55 +09:00
hecomi
f8d8e64e46 modify codes. 2016-11-15 02:29:38 +09:00
hecomi
0a39e37659 add logs. 2016-11-15 01:00:26 +09:00
hecomi
931ff6b2db modify field order. 2016-11-14 00:04:43 +09:00
hecomi
75819f0a7d fix uv. 2016-11-13 23:43:05 +09:00
hecomi
d6ce41f9b6 remove version information from readme. 2016-11-13 23:28:03 +09:00
26 changed files with 358 additions and 167 deletions

View File

@@ -21,7 +21,7 @@ public class Loupe : MonoBehaviour
// To get other monitor textures, set dirty flag.
foreach (var target in uDesktopDuplication.Manager.monitors) {
target.CreateTexture();
target.CreateTextureIfNeeded();
target.shouldBeUpdated = true;
}

View File

@@ -8,14 +8,24 @@ public class MultipleMonitorCreator : MonoBehaviour
[SerializeField]
GameObject monitorPrefab;
[Tooltip("Use same scale as real using DPI.")]
[SerializeField]
bool useRealScale = true;
public enum ScaleMode
{
Real,
Fixed,
Pixel,
}
[Tooltip("Use this sacle as width if useRealScale is false.")]
[Tooltip("Real: DPI-based real scale \nFixed: Same width \nPixel: bigger if screen resolution is high.")]
[SerializeField]
ScaleMode scaleMode = ScaleMode.Fixed;
[Tooltip("Use this scale as width if scaleMode is Fixed.")]
[SerializeField]
float scale = 0.5f;
[Tooltip("Please specify the surface direction of the mesh (e.g. Plane => Y.)")]
public MeshForwardDirection meshForwardDirection = MeshForwardDirection.Z;
[Tooltip("Remove unsupported monitors automatically after removeWaitDuration.")]
[SerializeField]
bool removeIfUnsupported = true;
@@ -24,12 +34,13 @@ public class MultipleMonitorCreator : MonoBehaviour
[SerializeField]
float removeWaitDuration = 5f;
[Tooltip("Remove all childrens (for debug).")]
[SerializeField]
bool removeChildrenWhenClear = true;
bool hasMonitorUnsupportStateChecked = false;
float removeWaitTimer_ = 0f;
[Tooltip("Please specify the surface direction of the mesh (e.g. Plane => Y.)")]
public MeshForwardDirection meshForwardDirection = MeshForwardDirection.Z;
public class MonitorInfo
{
public GameObject gameObject { get; set; }
@@ -101,7 +112,7 @@ public class MultipleMonitorCreator : MonoBehaviour
go.name = "Monitor " + i;
// Expand AABB
var mesh = go.GetComponent<MeshFilter>().mesh; // clone
var mesh = go.GetComponent<MeshFilter>().sharedMesh;
var aabbScale = mesh.bounds.size;
aabbScale.y = Mathf.Max(aabbScale.y, aabbScale.x);
aabbScale.z = Mathf.Max(aabbScale.z, aabbScale.x);
@@ -113,13 +124,20 @@ public class MultipleMonitorCreator : MonoBehaviour
var monitor = texture.monitor;
// Set width / height
float width, height;
if (useRealScale) {
width = monitor.widthMeter;
height = monitor.heightMeter;
} else {
width = scale * (monitor.isHorizontal ? 1f : monitor.aspect);
height = scale * (monitor.isHorizontal ? 1f / monitor.aspect : 1f);
float width = 1f, height = 1f;
switch (scaleMode) {
case ScaleMode.Real:
width = monitor.widthMeter;
height = monitor.heightMeter;
break;
case ScaleMode.Fixed:
width = scale * (monitor.isHorizontal ? 1f : monitor.aspect);
height = scale * (monitor.isHorizontal ? 1f / monitor.aspect : 1f);
break;
case ScaleMode.Pixel:
width = scale * (monitor.isHorizontal ? 1f : monitor.aspect) * ((float)monitor.width / 1920);
height = scale * (monitor.isHorizontal ? 1f / monitor.aspect : 1f) * ((float)monitor.width / 1920);
break;
}
if (meshForwardDirection == MeshForwardDirection.Y) {
go.transform.localScale = new Vector3(width, go.transform.localScale.y, height);
@@ -148,6 +166,11 @@ public class MultipleMonitorCreator : MonoBehaviour
foreach (var info in monitors_) {
Destroy(info.gameObject);
}
if (removeChildrenWhenClear) {
for (int i = 0; i < transform.childCount; ++i) {
Destroy(transform.GetChild(i).gameObject);
}
}
monitors_.Clear();
}

View File

@@ -8,12 +8,12 @@ public class UddSceneManager : MonoBehaviour
[SerializeField] int sceneNo = 0;
void Awake()
{
if (!instance) {
instance = this;
} else {
Destroy(gameObject);
}
{
if (!instance) {
instance = this;
} else {
Destroy(gameObject);
}
}
void Start()
@@ -24,17 +24,25 @@ public class UddSceneManager : MonoBehaviour
void Update()
{
if (Input.GetKeyDown(KeyCode.RightArrow)) {
Next();
if (Input.GetKeyDown(KeyCode.RightArrow)) {
Next();
} else if (Input.GetKeyDown(KeyCode.LeftArrow)) {
Prev();
}
if (Input.GetKeyDown(KeyCode.R) && (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl))) {
uDesktopDuplication.Manager.instance.Reinitialize();
}
if (Input.GetKeyDown(KeyCode.Escape)) {
Application.Quit();
}
}
void Next()
{
sceneNo = (sceneNo + 1) % scenes.Length;
Load();
{
sceneNo = (sceneNo + 1) % scenes.Length;
Load();
}
void Prev()
@@ -44,7 +52,7 @@ public class UddSceneManager : MonoBehaviour
}
void Load()
{
SceneManager.LoadScene(scenes[sceneNo]);
{
SceneManager.LoadScene(scenes[Mathf.Clamp(sceneNo, 0, scenes.Length - 1)]);
}
}

View File

@@ -5,8 +5,7 @@ using System.Collections.Generic;
namespace uDesktopDuplication
{
[AddComponentMenu("uDesktopDuplication/Cursor"),
RequireComponent(typeof(Texture))]
[AddComponentMenu("uDesktopDuplication/Cursor"), RequireComponent(typeof(Texture))]
public class Cursor : MonoBehaviour
{
[SerializeField] Vector2 modelScale = Vector2.one;

View File

@@ -9,6 +9,7 @@ public enum Message
{
None = -1,
Reinitialized = 0,
TextureSizeChanged = 1,
}
public enum CursorShapeType
@@ -29,14 +30,15 @@ public enum MonitorRotation
public enum MonitorState
{
NotSet = -1,
Available = 0,
InvalidArg = 1,
AccessDenied = 2,
Unsupported = 3,
CurrentlyNotAvailable = 4,
SessionDisconnected = 5,
AccessLost = 6,
NotSet = -1,
Available = 0,
InvalidArg = 1,
AccessDenied = 2,
Unsupported = 3,
CurrentlyNotAvailable = 4,
SessionDisconnected = 5,
AccessLost = 6,
TextureSizeInconsistent = 7,
Unknown = 999,
}
@@ -76,6 +78,8 @@ public static class Lib
[DllImport("uDesktopDuplication")]
public static extern int GetMonitorCount();
[DllImport("uDesktopDuplication")]
public static extern bool HasMonitorCountChanged();
[DllImport("uDesktopDuplication")]
public static extern int GetCursorMonitorId();
[DllImport("uDesktopDuplication")]
public static extern int GetTotalWidth();

View File

@@ -1,7 +1,6 @@
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace uDesktopDuplication
{
@@ -14,6 +13,19 @@ public class Manager : MonoBehaviour
get { return CreateInstance(); }
}
public static Manager CreateInstance()
{
if (instance_) {
return instance_;
}
var manager = FindObjectOfType<Manager>();
if (manager) return manager;
var go = new GameObject("uDesktopDuplicationManager");
return go.AddComponent<Manager>();
}
private List<Monitor> monitors_ = new List<Monitor>();
static public List<Monitor> monitors
{
@@ -49,22 +61,6 @@ public class Manager : MonoBehaviour
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>();
}
public static Monitor GetMonitor(int id)
{
if (id < 0 || id >= Manager.monitors.Count) {
@@ -78,18 +74,21 @@ public class Manager : MonoBehaviour
{
Lib.SetDebugMode(debugMode);
Lib.InitializeUDD();
Lib.SetTimeout(desktopDuplicationApiTimeout);
if (instance_ != null) return;
if (instance_ != null) {
Destroy(gameObject);
return;
}
instance_ = this;
CreateMonitors();
Lib.SetTimeout(desktopDuplicationApiTimeout);
}
void OnApplicationQuit()
{
Lib.FinalizeUDD();
DestroyMonitors();
}
void OnEnable()
@@ -113,7 +112,7 @@ public class Manager : MonoBehaviour
}
[ContextMenu("Reinitialize")]
void Reinitialize()
public void Reinitialize()
{
Debug.Log("[uDD] Reinitialize");
Lib.Reinitialize();
@@ -125,20 +124,30 @@ public class Manager : MonoBehaviour
void ReinitializeIfNeeded()
{
bool reinitializeNeeded = false;
for (int i = 0; i < monitors.Count; ++i) {
var monitor = monitors[i];
if (monitor.state == MonitorState.NotSet ||
if (
monitor.state == MonitorState.NotSet ||
monitor.state == MonitorState.AccessLost ||
monitor.state == MonitorState.AccessDenied ||
monitor.state == MonitorState.SessionDisconnected) {
if (!shouldReinitialize_) {
shouldReinitialize_ = true;
reinitializationTimer_ = 0f;
break;
}
monitor.state == MonitorState.SessionDisconnected
) {
reinitializeNeeded = true;
break;
}
}
if (Lib.HasMonitorCountChanged()) {
reinitializeNeeded = true;
}
if (!shouldReinitialize_ && reinitializeNeeded) {
shouldReinitialize_ = true;
reinitializationTimer_ = 0f;
}
if (shouldReinitialize_) {
if (reinitializationTimer_ > retryReinitializationDuration) {
Reinitialize();
@@ -152,10 +161,14 @@ public class Manager : MonoBehaviour
{
var message = Lib.PopMessage();
while (message != Message.None) {
Debug.Log("[uDD] " + message);
switch (message) {
case Message.Reinitialized:
ReinitializeMonitors();
break;
case Message.TextureSizeChanged:
RecreateTextures();
break;
default:
break;
}
@@ -179,31 +192,36 @@ public class Manager : MonoBehaviour
void CreateMonitors()
{
monitors.Clear();
DestroyMonitors();
for (int i = 0; i < monitorCount; ++i) {
monitors.Add(new Monitor(i));
}
}
void DestroyMonitors()
{
for (int i = 0; i < monitors.Count; ++i) {
monitors[i].DestroyTexture();
}
monitors.Clear();
}
void ReinitializeMonitors()
{
for (int i = 0; i < monitorCount; ++i) {
if (i == monitors.Count) {
monitors.Add(new Monitor(i));
} else {
monitors[i].Reinitialize();
}
monitors[i].Reinitialize();
}
}
void WaitThenDo(System.Action func, float sec)
void RecreateTextures()
{
StartCoroutine(_WaitThenDo(func, sec));
}
IEnumerator _WaitThenDo(System.Action func, float sec)
{
yield return new WaitForSeconds(sec);
func();
for (int i = 0; i < monitorCount; ++i) {
monitors[i].CreateTextureIfNeeded();
}
}
}

View File

@@ -1,5 +1,4 @@
using UnityEngine;
using System.Runtime.InteropServices;
namespace uDesktopDuplication
{
@@ -39,6 +38,11 @@ public class Monitor
}
}
~Monitor()
{
DestroyTexture();
}
public int id
{
get;
@@ -204,17 +208,24 @@ public class Monitor
set;
}
private static Texture2D errorTexture_;
private static readonly string errorTexturePath = "uDesktopDuplication/Textures/NotAvailable";
private Texture2D errorTexture
{
get
{
return errorTexture_ ??
(errorTexture_ = Resources.Load<Texture2D>(errorTexturePath));
}
}
private Texture2D texture_;
public Texture2D texture
{
get
{
if (!available) {
return Resources.Load<Texture2D>("uDesktopDuplication/Textures/NotAvailable");
}
if (texture_ == null) {
CreateTexture();
}
if (!available) return errorTexture;
if (texture_ == null) CreateTextureIfNeeded();
return texture_;
}
}
@@ -232,7 +243,7 @@ public class Monitor
Lib.GetCursorTexture(id, ptr);
}
public void CreateTexture()
public void CreateTextureIfNeeded()
{
if (!available) return;
@@ -240,13 +251,8 @@ public class Monitor
var h = isHorizontal ? height : width;
bool shouldCreate = true;
if (texture_) {
if (texture_.width != w || texture_.height != h) {
if (texture_) Object.DestroyImmediate(texture_);
shouldCreate = true;
} else {
shouldCreate = false;
}
if (texture_ && texture_.width == w && texture_.height == h) {
shouldCreate = false;
}
if (w <= 0 || h <= 0) {
@@ -254,13 +260,29 @@ public class Monitor
}
if (shouldCreate) {
texture_ = new Texture2D(w, h, TextureFormat.BGRA32, false);
CreateTexture();
}
}
void CreateTexture()
{
DestroyTexture();
var w = isHorizontal ? width : height;
var h = isHorizontal ? height : width;
texture_ = new Texture2D(w, h, TextureFormat.BGRA32, false);
}
public void DestroyTexture()
{
if (texture_) {
Object.Destroy(texture_);
texture_ = null;
}
}
public void Reinitialize()
{
CreateTexture();
CreateTextureIfNeeded();
}
}

View File

@@ -3,7 +3,7 @@
namespace uDesktopDuplication
{
[AddComponentMenu("uDesktopDuplication/Texture")]
[AddComponentMenu("uDesktopDuplication/Texture"), RequireComponent(typeof(Cursor))]
public class Texture : MonoBehaviour
{
private Monitor monitor_;
@@ -105,10 +105,7 @@ public class Texture : MonoBehaviour
void Awake()
{
if (!GetComponent<Cursor>())
{
gameObject.AddComponent<Cursor>();
}
AddCursorIfNotAttached();
}
void OnEnable()
@@ -116,6 +113,12 @@ public class Texture : MonoBehaviour
if (monitor == null) {
monitor = Manager.primary;
}
Manager.onReinitialized += Reinitialize;
}
void OnDisable()
{
Manager.onReinitialized -= Reinitialize;
}
void Update()
@@ -124,6 +127,20 @@ public class Texture : MonoBehaviour
UpdateMaterial();
}
void AddCursorIfNotAttached()
{
if (!GetComponent<Cursor>())
{
gameObject.AddComponent<Cursor>();
}
}
void Reinitialize()
{
// Monitor instance is released here when initialized.
monitor = Manager.GetMonitor(monitor.id);
}
void UpdateMaterial()
{
Invert();

View File

@@ -1,4 +1,5 @@
using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
namespace uDesktopDuplication
@@ -25,6 +26,17 @@ public static class Utility
}
return p;
}
public static void WaitThenDo(System.Action func, float sec)
{
Manager.instance.StartCoroutine(_WaitThenDo(func, sec));
}
public static IEnumerator _WaitThenDo(System.Action func, float sec)
{
yield return new WaitForSeconds(sec);
func();
}
}
}

View File

@@ -1,14 +1,14 @@
#pragma once
#include <queue>
#include <unordered_map>
#include <iostream>
#include <d3d11.h>
#include "IUnityInterface.h"
#include "IUnityGraphicsD3D11.h"
#include "Common.h"
using namespace Microsoft::WRL;
extern IUnityInterfaces* g_unity;
extern std::unique_ptr<MonitorManager> g_manager;
@@ -21,7 +21,7 @@ IUnityInterfaces* GetUnity()
}
ID3D11Device* GetDevice()
ComPtr<ID3D11Device> GetDevice()
{
return GetUnity()->Get<IUnityGraphicsD3D11>()->GetDevice();
}

View File

@@ -1,8 +1,7 @@
#pragma once
#include <memory>
#include <string>
#include <functional>
#include <wrl/client.h>
// Unity interface and ID3D11Device getters
@@ -10,7 +9,7 @@ struct IUnityInterfaces;
IUnityInterfaces* GetUnity();
struct ID3D11Device;
ID3D11Device* GetDevice();
Microsoft::WRL::ComPtr<ID3D11Device> GetDevice();
// Manager getter
@@ -18,11 +17,23 @@ class MonitorManager;
const std::unique_ptr<MonitorManager>& GetMonitorManager();
template <class T>
auto MakeUniqueWithReleaser(T* ptr)
{
const auto deleter = [](T* ptr)
{
if (ptr != nullptr) ptr->Release();
};
return std::unique_ptr<T, decltype(deleter)>(ptr, deleter);
}
// Message is pooled and fetch from Unity.
enum class Message
{
None = -1,
Reinitialized = 0,
TextureSizeChanged = 1,
};
void SendMessageToUnity(Message message);

View File

@@ -53,14 +53,18 @@ void Cursor::UpdateBuffer(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
apiBufferSize_ = frameInfo.PointerShapeBufferSize;
apiBuffer_ = std::make_unique<BYTE[]>(apiBufferSize_);
}
if (!apiBuffer_) return;
if (!apiBuffer_)
{
return;
}
// Get mouse pointer information
UINT bufferSize;
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo;
const auto hr = monitor_->GetDeskDupl()->GetFramePointerShape(
apiBufferSize_,
reinterpret_cast<void*>(apiBuffer_.get()),
apiBuffer_.get(),
&bufferSize,
&shapeInfo);
@@ -69,6 +73,7 @@ void Cursor::UpdateBuffer(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
Debug::Error("Cursor::UpdateBuffer() => GetFramePointerShape() failed.");
apiBuffer_.reset();
apiBufferSize_ = 0;
return;
}
shapeInfo_ = shapeInfo;
@@ -100,13 +105,20 @@ void Cursor::UpdateTexture()
bgra32BufferSize_ = bgraBufferSize;
bgra32Buffer_ = std::make_unique<BYTE[]>(bgra32BufferSize_);
}
if (!bgra32Buffer_) return;
if (!bgra32Buffer_)
{
return;
}
if (!apiBuffer_)
{
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_;
@@ -153,8 +165,7 @@ void Cursor::UpdateTexture()
desc.MiscFlags = 0;
ComPtr<ID3D11Texture2D> texture;
hr = GetDevice()->CreateTexture2D(&desc, nullptr, &texture);
if (FAILED(hr))
if (FAILED(GetDevice()->CreateTexture2D(&desc, nullptr, &texture)))
{
Debug::Error("Cursor::UpdateTexture() => GetDevice()->CreateTexture2D() failed.");
return;
@@ -181,16 +192,14 @@ void Cursor::UpdateTexture()
}
ComPtr<IDXGISurface> surface;
hr = texture.As<IDXGISurface>(&surface);
if (FAILED(hr))
if (FAILED(texture.As(&surface)))
{
Debug::Error("Cursor::UpdateTexture() => texture->QueryInterface() failed.");
return;
}
DXGI_MAPPED_RECT mappedSurface;
hr = surface->Map(&mappedSurface, DXGI_MAP_READ);
if (FAILED(hr))
if (FAILED(surface->Map(&mappedSurface, DXGI_MAP_READ)))
{
Debug::Error("Cursor::UpdateTexture() => surface->Map() failed.");
return;
@@ -243,8 +252,7 @@ void Cursor::UpdateTexture()
}
}
hr = surface->Unmap();
if (FAILED(hr))
if (FAILED(surface->Unmap()))
{
return;
}

View File

@@ -28,4 +28,6 @@ void Debug::Finalize()
Debug::Log("Stop");
fs_.close();
}
Debug::SetLogFunc(nullptr);
Debug::SetErrorFunc(nullptr);
}

View File

@@ -1,4 +1,5 @@
#pragma once
#include <time.h>
#include <fstream>
#include <sstream>
#include "IUnityInterface.h"
@@ -86,18 +87,34 @@ private:
Flush(level);
}
static void OutputTime()
{
auto t = time(nullptr);
tm tm;
localtime_s(&tm, &t);
char buf[64];
strftime(buf, 64, "%F %T", &tm);
Output("[");;
Output(buf);
Output("]");
}
public:
template <class Arg, class... RestArgs>
static void Log(Arg&& arg, RestArgs&&... restArgs)
{
Output("[uDD::Log] ");
Output("[uDD::Log]");
OutputTime();
Output(" ");
_Log(Level::Log, std::forward<Arg>(arg), std::forward<RestArgs>(restArgs)...);
}
template <class Arg, class... RestArgs>
static void Error(Arg&& arg, RestArgs&&... restArgs)
{
Output("[uDD::Err] ");
Output("[uDD::Err]");
OutputTime();
Output(" ");
_Log(Level::Error, std::forward<Arg>(arg), std::forward<RestArgs>(restArgs)...);
}

View File

@@ -18,59 +18,94 @@ Monitor::Monitor(int id)
Monitor::~Monitor()
{
if (deskDupl_)
{
deskDupl_->Release();
deskDupl_ = nullptr;
}
}
void Monitor::Initialize(IDXGIOutput* output)
{
output->GetDesc(&outputDesc_);
monitorInfo_.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(outputDesc_.Monitor, &monitorInfo_);
if (FAILED(output->GetDesc(&outputDesc_)))
{
Debug::Error("Monitor::Initialize() => IDXGIOutput::GetDesc() failed.");
return;
}
GetDpiForMonitor(outputDesc_.Monitor, MDT_RAW_DPI, &dpiX_, &dpiY_);
monitorInfo_.cbSize = sizeof(MONITORINFOEX);
if (!GetMonitorInfo(outputDesc_.Monitor, &monitorInfo_))
{
Debug::Error("Monitor::Initialize() => GetMonitorInfo() failed.");
return;
}
else
{
const auto rect = monitorInfo_.rcMonitor;
width_ = rect.right - rect.left;
height_ = rect.bottom - rect.top;
}
if (FAILED(GetDpiForMonitor(outputDesc_.Monitor, MDT_RAW_DPI, &dpiX_, &dpiY_)))
{
Debug::Error("Monitor::Initialize() => GetDpiForMonitor() failed.");
return;
}
auto output1 = reinterpret_cast<IDXGIOutput1*>(output);
const auto hr = output1->DuplicateOutput(GetDevice(), &deskDupl_);
// TODO: error check
switch (hr)
switch (output1->DuplicateOutput(GetDevice().Get(), &deskDupl_))
{
case S_OK:
{
state_ = State::Available;
Debug::Log("Monitor::Initialize() => OK.");
Debug::Log(" ID : ", GetId());
Debug::Log(" Size : (", GetWidth(), ", ", GetHeight(), ")");
Debug::Log(" DPI : (", GetDpiX(), ", ", GetDpiY(), ")");
break;
}
case E_INVALIDARG:
{
state_ = State::InvalidArg;
Debug::Error("Monitor::Initialize() => Invalid arguments.");
break;
}
case E_ACCESSDENIED:
{
// For example, when the user presses Ctrl + Alt + Delete and the screen
// switches to admin screen, this error occurs.
state_ = State::AccessDenied;
Debug::Error("Monitor::Initialize() => Access denied.");
break;
}
case DXGI_ERROR_UNSUPPORTED:
{
// If the display adapter on the computer is running under the Microsoft Hybrid system,
// this error occurs.
state_ = State::Unsupported;
Debug::Error("Monitor::Initialize() => Unsupported display.");
break;
}
case DXGI_ERROR_NOT_CURRENTLY_AVAILABLE:
{
// When other application use Desktop Duplication API, this error occurs.
state_ = State::CurrentlyNotAvailable;
Debug::Error("Monitor::Initialize() => Currently not available.");
break;
}
case DXGI_ERROR_SESSION_DISCONNECTED:
{
state_ = State::SessionDisconnected;
Debug::Error("Monitor::Initialize() => Session disconnected.");
break;
}
default:
{
state_ = State::Unknown;
Debug::Error("Monitor::Render() => Unknown Error.");
break;
}
}
}
@@ -81,65 +116,82 @@ void Monitor::Render(UINT timeout)
ComPtr<IDXGIResource> resource;
DXGI_OUTDUPL_FRAME_INFO frameInfo;
HRESULT hr;
hr = deskDupl_->AcquireNextFrame(timeout, &frameInfo, &resource);
const auto hr = deskDupl_->AcquireNextFrame(timeout, &frameInfo, &resource);
if (FAILED(hr))
{
switch (hr)
{
case DXGI_ERROR_ACCESS_LOST:
{
// If any monitor setting has changed (e.g. monitor size has changed),
// it is necessary to re-initialize monitors.
Debug::Log("Monitor::Render() => DXGI_ERROR_ACCESS_LOST.");
state_ = State::AccessLost;
break;
}
case DXGI_ERROR_WAIT_TIMEOUT:
{
// This often occurs when timeout value is small and it is not problem.
// Debug::Log("Monitor::Render() => DXGI_ERROR_WAIT_TIMEOUT.");
break;
}
case DXGI_ERROR_INVALID_CALL:
{
Debug::Error("Monitor::Render() => DXGI_ERROR_INVALID_CALL.");
break;
}
case E_INVALIDARG:
{
Debug::Error("Monitor::Render() => E_INVALIDARG.");
break;
}
default:
{
state_ = State::Unknown;
Debug::Error("Monitor::Render() => Unknown Error.");
break;
}
}
return;
}
if (unityTexture_)
{
ComPtr<ID3D11Texture2D> texture;
resource.As<ID3D11Texture2D>(&texture);
ID3D11Texture2D* texture;
if (FAILED(resource.CopyTo(&texture)))
{
Debug::Error("Monitor::Render() => resource.As() failed.");
return;
}
D3D11_TEXTURE2D_DESC srcDesc, dstDesc;
texture->GetDesc(&srcDesc);
texture->GetDesc(&dstDesc);
unityTexture_->GetDesc(&dstDesc);
if (srcDesc.Width != dstDesc.Width ||
srcDesc.Height != dstDesc.Height)
{
Debug::Error("Monitor::Render() => Texture sizes are defferent.");
Debug::Error(" Source : (", srcDesc.Width, ", ", srcDesc.Height, ")");
Debug::Error(" Dest : (", dstDesc.Width, ", ", dstDesc.Height, ")");
//Debug::Log(" => Try modifying width/height using reported value from DDA.");
//width_ = srcDesc.Width;
//height_ = srcDesc.Height;
state_ = MonitorState::TextureSizeInconsistent;
//SendMessageToUnity(Message::TextureSizeChanged);
}
else
{
ComPtr<ID3D11DeviceContext> context;
GetDevice()->GetImmediateContext(&context);
context->CopyResource(unityTexture_, texture.Get());
context->CopyResource(unityTexture_, texture);
}
}
cursor_->UpdateBuffer(frameInfo);
cursor_->UpdateTexture();
hr = deskDupl_->ReleaseFrame();
if (FAILED(hr))
if (FAILED(deskDupl_->ReleaseFrame()))
{
Debug::Error("Monitor::Render() => ReleaseFrame() failed.");
}
@@ -170,7 +222,7 @@ ID3D11Texture2D* Monitor::GetUnityTexture() const
}
const ComPtr<IDXGIOutputDuplication>& Monitor::GetDeskDupl()
IDXGIOutputDuplication* Monitor::GetDeskDupl()
{
return deskDupl_;
}
@@ -244,13 +296,11 @@ int Monitor::GetDpiY() const
int Monitor::GetWidth() const
{
const auto rect = monitorInfo_.rcMonitor;
return rect.right - rect.left;
return width_;
}
int Monitor::GetHeight() const
{
const auto rect = monitorInfo_.rcMonitor;
return rect.bottom - rect.top;
return height_;
}

View File

@@ -17,6 +17,7 @@ enum class MonitorState
CurrentlyNotAvailable = 4,
SessionDisconnected = 5,
AccessLost = 6,
TextureSizeInconsistent = 7,
Unknown = 999,
};
@@ -47,15 +48,16 @@ public:
int GetRotation() const;
int GetDpiX() const;
int GetDpiY() const;
const Microsoft::WRL::ComPtr<IDXGIOutputDuplication>& GetDeskDupl();
IDXGIOutputDuplication* GetDeskDupl();
const std::unique_ptr<Cursor>& GetCursor();
private:
int id_ = -1;
UINT dpiX_ = -1, dpiY_ = -1;
int width_ = -1, height_ = -1;
State state_ = State::NotSet;
std::unique_ptr<Cursor> cursor_;
Microsoft::WRL::ComPtr<IDXGIOutputDuplication> deskDupl_;
IDXGIOutputDuplication* deskDupl_ = nullptr;
ID3D11Texture2D* unityTexture_ = nullptr;
DXGI_OUTPUT_DESC outputDesc_;
MONITORINFOEX monitorInfo_;

View File

@@ -35,7 +35,11 @@ void MonitorManager::Initialize()
// Get factory
ComPtr<IDXGIFactory1> factory;
CreateDXGIFactory1(IID_PPV_ARGS(&factory));
if (FAILED(CreateDXGIFactory1(IID_PPV_ARGS(&factory))))
{
Debug::Error("MonitorManager::Initialize() => CreateDXGIFactory1() failed.");
return;
}
// Check all display adapters
int id = 0;
@@ -74,10 +78,14 @@ void MonitorManager::Reinitialize()
}
void MonitorManager::CheckMonitorNumbers()
bool MonitorManager::HasMonitorCountChanged() const
{
ComPtr<IDXGIFactory1> factory;
CreateDXGIFactory1(IID_PPV_ARGS(&factory));
if (FAILED(CreateDXGIFactory1(IID_PPV_ARGS(&factory))))
{
Debug::Error("MonitorManager::CheckMonitorConnection() => CreateDXGIFactory1() failed.");
return false;
}
int id = 0;
ComPtr<IDXGIAdapter1> adapter;
@@ -90,11 +98,7 @@ void MonitorManager::CheckMonitorNumbers()
}
}
if (GetMonitorCount() != id)
{
Debug::Log("Monitor number changed: ", GetMonitorCount(), " => ", id);
RequireReinitilization();
}
return monitors_.size() != id;
}
@@ -110,8 +114,6 @@ std::shared_ptr<Monitor> MonitorManager::GetMonitor(int id) const
void MonitorManager::Update()
{
CheckMonitorNumbers();
if (isReinitializationRequired_)
{
Reinitialize();
@@ -137,6 +139,7 @@ int MonitorManager::GetMonitorCount() const
return static_cast<int>(monitors_.size());
}
int MonitorManager::GetTotalWidth() const
{
std::vector<int> lefts, rights;
@@ -150,6 +153,7 @@ int MonitorManager::GetTotalWidth() const
return maxRight - minLeft;
}
int MonitorManager::GetTotalHeight() const
{
std::vector<int> tops, bottoms;

View File

@@ -16,7 +16,7 @@ public:
explicit MonitorManager();
~MonitorManager();
void Reinitialize();
void CheckMonitorNumbers();
bool HasMonitorCountChanged() const;
void RequireReinitilization();
void SetCursorMonitorId(int id) { cursorMonitorId_ = id; }
int GetCursorMonitorId() const { return cursorMonitorId_; }

View File

@@ -43,8 +43,6 @@ extern "C"
std::queue<Message> empty;
g_messages.swap(empty);
Debug::SetLogFunc(nullptr);
Debug::SetErrorFunc(nullptr);
Debug::Finalize();
}
}
@@ -117,6 +115,12 @@ extern "C"
return g_manager->GetMonitorCount();
}
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API HasMonitorCountChanged()
{
if (!g_manager) return false;
return g_manager->HasMonitorCountChanged();
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorMonitorId()
{
if (!g_manager) return -1;

View File

@@ -1,2 +1,2 @@
m_EditorVersion: 5.4.1f1
m_EditorVersion: 5.4.2f2
m_StandardAssetsVersion: 0

View File

@@ -25,16 +25,6 @@ Usage
Attach `uDesktopDuplication/Texture` component to the target object, then its main texture will be replaced with the captured screen. Please see example scenes for more details.
Version
-------
| Data | Version | Description |
| ---------- | ------- | --------------------------------- |
| 2016/10/28 | 1.0.0 | Support multiple screens. |
| 2016/10/27 | 0.0.3 | Support lienar color. |
| 2016/10/27 | 0.0.2 | Add mouse cursor / shaders. |
| 2016/10/27 | 0.0.1 | Initial commit. |
Lisence
-------
The MIT License (MIT)