Compare commits

...

8 Commits

Author SHA1 Message Date
hecomi
60889d53f4 modify layouter to fix height. 2016-11-19 13:49:38 +09:00
hecomi
f207e65952 do not stop initialization even when getting dpi has error. 2016-11-19 13:09:50 +09:00
hecomi
aea8d112c6 fix indent. 2016-11-18 02:33:24 +09:00
hecomi
9f6971dd45 register callbacks. 2016-11-18 02:28:27 +09:00
hecomi
1be698af63 keep local scale of monitors as 1. 2016-11-18 00:19:22 +09:00
hecomi
064db9f137 modify access rights of layouter. 2016-11-17 23:31:39 +09:00
hecomi
166d172db2 modify access rights. 2016-11-17 23:23:22 +09:00
hecomi
95f8099392 capture cursor mask taking monitor rotation into consideration. 2016-11-17 23:19:46 +09:00
11 changed files with 203 additions and 94 deletions

View File

@@ -5,8 +5,7 @@ using MeshForwardDirection = uDesktopDuplication.Texture.MeshForwardDirection;
public class MultipleMonitorCreator : MonoBehaviour
{
[Tooltip("Create monitors using this prefab.")]
[SerializeField]
GameObject monitorPrefab;
public GameObject monitorPrefab;
public enum ScaleMode
{
@@ -16,29 +15,24 @@ public class MultipleMonitorCreator : MonoBehaviour
}
[Tooltip("Real: DPI-based real scale \nFixed: Same width \nPixel: bigger if screen resolution is high.")]
[SerializeField]
ScaleMode scaleMode = ScaleMode.Fixed;
public ScaleMode scaleMode = ScaleMode.Fixed;
[Tooltip("Use this scale as width if scaleMode is Fixed.")]
[SerializeField]
float scale = 0.5f;
public float scale = 0.5f;
[Tooltip("Please specify the surface direction of the mesh (e.g. Plane => Y.)")]
public MeshForwardDirection meshForwardDirection = MeshForwardDirection.Z;
[Tooltip("Remove unsupported monitors automatically after removeWaitDuration.")]
[SerializeField]
bool removeIfUnsupported = true;
public bool removeIfUnsupported = true;
[Tooltip("Remove unsupported monitors automatically after removeWaitDuration.")]
[SerializeField]
float removeWaitDuration = 5f;
public float removeWaitDuration = 5f;
[Tooltip("Remove all childrens (for debug).")]
[SerializeField]
bool removeChildrenWhenClear = true;
public bool removeChildrenWhenClear = true;
bool hasMonitorUnsupportStateChecked = false;
bool hasMonitorUnsupportStateChecked_ = false;
float removeWaitTimer_ = 0f;
public class MonitorInfo
@@ -81,10 +75,10 @@ public class MultipleMonitorCreator : MonoBehaviour
void RemoveUnsupportedDisplayAfterRemoveTimer()
{
if (!hasMonitorUnsupportStateChecked) {
if (!hasMonitorUnsupportStateChecked_) {
removeWaitTimer_ += Time.deltaTime;
if (removeWaitTimer_ > removeWaitDuration) {
hasMonitorUnsupportStateChecked = true;
hasMonitorUnsupportStateChecked_ = true;
foreach (var info in monitors_) {
if (info.uddTexture.monitor.state == uDesktopDuplication.MonitorState.Unsupported) {
Destroy(info.gameObject);
@@ -97,7 +91,7 @@ public class MultipleMonitorCreator : MonoBehaviour
void ResetRemoveTimer()
{
hasMonitorUnsupportStateChecked = false;
hasMonitorUnsupportStateChecked_ = false;
removeWaitTimer_ = 0f;
}
@@ -131,8 +125,8 @@ public class MultipleMonitorCreator : MonoBehaviour
height = monitor.heightMeter;
break;
case ScaleMode.Fixed:
width = scale * (monitor.isHorizontal ? 1f : monitor.aspect);
height = scale * (monitor.isHorizontal ? 1f / monitor.aspect : 1f);
width = scale * (monitor.isHorizontal ? monitor.aspect : 1f);
height = scale * (monitor.isHorizontal ? 1f : 1f / monitor.aspect);
break;
case ScaleMode.Pixel:
width = scale * (monitor.isHorizontal ? 1f : monitor.aspect) * ((float)monitor.width / 1920);

View File

@@ -4,9 +4,9 @@
public class MultipleMonitorLayouter : MonoBehaviour
{
protected MultipleMonitorCreator creator_;
[SerializeField] bool updateEveryFrame = true;
[SerializeField] protected float margin = 0.1f;
[SerializeField][Range(0f, 10f)] protected float thickness = 1f;
public bool updateEveryFrame = true;
public float margin = 0.1f;
[Range(0f, 10f)] public float thickness = 1f;
void Start()
{
@@ -30,7 +30,7 @@ public class MultipleMonitorLayouter : MonoBehaviour
var totalWidth = 0f;
foreach (var info in monitors) {
var width = info.gameObject.transform.localEulerAngles.x * (info.mesh.bounds.extents.x * 2f);
var width = info.gameObject.transform.localScale.x * (info.mesh.bounds.extents.x * 2f);
totalWidth += width;
}
totalWidth += margin * (n - 1);
@@ -38,7 +38,7 @@ public class MultipleMonitorLayouter : MonoBehaviour
var x = -totalWidth / 2;
foreach (var info in monitors) {
var width = info.gameObject.transform.localEulerAngles.x;
var width = info.gameObject.transform.localScale.x;
x += (width * info.mesh.bounds.extents.x);
info.gameObject.transform.localPosition = new Vector3(x, 0f, 0f);
info.gameObject.transform.localRotation = info.originalRotation;

View File

@@ -2,8 +2,8 @@
public class MultipleMonitorRoundLayouter : MultipleMonitorLayouter
{
[SerializeField] float radius = 10f;
[SerializeField] Vector3 offsetAngle = Vector3.zero;
public float radius = 10f;
public Vector3 offsetAngle = Vector3.zero;
void OnDisable()
{
@@ -17,6 +17,17 @@ public class MultipleMonitorRoundLayouter : MultipleMonitorLayouter
var monitors = creator_.monitors;
var n = monitors.Count;
// keep the local scale z of monitors as 1 to bend them correctly.
foreach (var info in monitors) {
var scale = info.gameObject.transform.localScale;
scale.z = 1f;
info.gameObject.transform.localScale = scale;
}
// keep thicness plus value.
thickness = Mathf.Max(thickness, 0f);
// calculate total width
var totalWidth = 0f;
foreach (var info in monitors) {
var width = info.gameObject.transform.localScale.x * (info.mesh.bounds.extents.x * 2f);
@@ -24,9 +35,13 @@ public class MultipleMonitorRoundLayouter : MultipleMonitorLayouter
}
totalWidth += margin * (n - 1);
// expand radius if total width is larger than the circumference.
radius = Mathf.Max(radius, (totalWidth + margin) / (2 * Mathf.PI));
// total angle of monitors
var totalAngle = totalWidth / radius;
// layout
float angle = -totalAngle / 2;
var offsetRot = Quaternion.Euler(offsetAngle);
foreach (var info in monitors) {
@@ -53,7 +68,12 @@ public class MultipleMonitorRoundLayouter : MultipleMonitorLayouter
protected override void Update()
{
base.Update();
DebugDraw();
}
void DebugDraw()
{
// draw the circumference in the scene view.
var scale = transform.localScale.x;
var center = transform.position - Vector3.forward * radius * scale;
for (int i = 0; i < 100; ++i) {

View File

@@ -14,6 +14,7 @@ public enum Message
public enum CursorShapeType
{
Unspecified = 0,
MonoChrome = 1,
Color = 2,
MaskedColor = 4,

View File

@@ -73,8 +73,8 @@ public class Manager : MonoBehaviour
void Awake()
{
Lib.SetDebugMode(debugMode);
Lib.InitializeUDD();
Lib.SetTimeout(desktopDuplicationApiTimeout);
Lib.InitializeUDD();
if (instance_ != null) {
Destroy(gameObject);

View File

@@ -13,9 +13,11 @@ public class Texture : MonoBehaviour
set
{
monitor_ = value;
material = GetComponent<Renderer>().material; // clone
material.mainTexture = monitor_.texture;
material.SetFloat("_Width", transform.localScale.x);
if (monitor_ != null) {
material = GetComponent<Renderer>().material; // clone
material.mainTexture = monitor_.texture;
material.SetFloat("_Width", transform.localScale.x);
}
}
}

View File

@@ -73,7 +73,7 @@ void Cursor::UpdateBuffer(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
Debug::Error("Cursor::UpdateBuffer() => GetFramePointerShape() failed.");
apiBuffer_.reset();
apiBufferSize_ = 0;
return;
return;
}
shapeInfo_ = shapeInfo;
@@ -87,73 +87,137 @@ void Cursor::UpdateTexture()
return;
}
// cursor type
// Cursor information
const bool isMono = GetType() == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME;
const bool isColorMask = GetType() == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR;
const auto cursorImageWidth = GetWidth();
const auto cursorImageHeight = GetHeight();
const auto cursorImagePitch = GetPitch();
// Size
const auto w0 = GetWidth();
const auto h0 = GetHeight();
const auto p = GetPitch();
auto w = w0;
auto h = h0;
// Captured area
auto capturedImageWidth = cursorImageWidth;
auto capturedImageHeight = cursorImageHeight;
// Convert the buffer given by API into BGRA32
const UINT bgraBufferSize = w0 * h0 * 4;
const UINT bgraBufferSize = cursorImageWidth * cursorImageHeight * 4;
if (bgraBufferSize > bgra32BufferSize_)
{
bgra32BufferSize_ = bgraBufferSize;
bgra32Buffer_ = std::make_unique<BYTE[]>(bgra32BufferSize_);
}
if (!bgra32Buffer_)
{
return;
}
if (!apiBuffer_)
{
return;
}
// Check buffers
if (!bgra32Buffer_ || !apiBuffer_)
{
return;
}
// If masked, copy the desktop image and merge it with masked image.
if (isMono || isColorMask)
{
const auto mw = monitor_->GetWidth();
const auto mh = monitor_->GetHeight();
const auto monitorWidth = monitor_->GetWidth();
const auto monitorHeight = monitor_->GetHeight();
const auto monitorRot = static_cast<DXGI_MODE_ROTATION>(monitor_->GetRotation());
const auto isVertical =
monitorRot == DXGI_MODE_ROTATION_ROTATE90 ||
monitorRot == DXGI_MODE_ROTATION_ROTATE270;
const auto desktopImageWidth = !isVertical ? monitorWidth : monitorHeight;
const auto desktopImageHeight = !isVertical ? monitorHeight : monitorWidth;
auto x = x_;
auto y = y_;
auto colMin = 0;
auto colMax = w0;
auto colMax = cursorImageWidth;
auto rowMin = 0;
auto rowMax = h0;
auto rowMax = cursorImageHeight;
if (x < 0)
{
x = 0;
w = w0 + x_;
colMin = w0 - w;
capturedImageWidth = cursorImageWidth + x_;
colMin = cursorImageWidth - capturedImageWidth;
}
if (x + w >= mw)
if (x + capturedImageWidth >= monitorWidth)
{
w = mw - x_;
colMax = w;
capturedImageWidth = monitorWidth - x_;
colMax = capturedImageWidth;
}
if (y < 0)
{
y = 0;
h = h0 + y_;
rowMin = h0 - h;
capturedImageHeight = cursorImageHeight + y_;
rowMin = cursorImageHeight - capturedImageHeight;
}
if (y + h >= mh)
if (y + capturedImageHeight >= monitorHeight)
{
h = mh - y_;
rowMax = h;
capturedImageHeight = monitorHeight - y_;
rowMax = capturedImageHeight;
}
D3D11_BOX box;
box.front = 0;
box.back = 1;
switch (monitorRot)
{
case DXGI_MODE_ROTATION_ROTATE90:
{
box.left = y;
box.top = monitorWidth - x - capturedImageWidth;
box.right = y + capturedImageWidth;
box.bottom = monitorWidth - x;
break;
}
case DXGI_MODE_ROTATION_ROTATE180:
{
box.left = monitorWidth - x - capturedImageWidth;
box.top = monitorHeight - y - capturedImageHeight;
box.right = monitorWidth - x;
box.bottom = monitorHeight - y;
break;
}
case DXGI_MODE_ROTATION_ROTATE270:
{
box.left = monitorHeight - y - capturedImageHeight;
box.top = x;
box.right = monitorHeight - y;
box.bottom = x + capturedImageWidth;
break;
}
case DXGI_MODE_ROTATION_IDENTITY:
case DXGI_MODE_ROTATION_UNSPECIFIED:
{
box.left = x;
box.top = y;
box.right = x + capturedImageWidth;
box.bottom = y + capturedImageHeight;
break;
}
}
if (box.left < 0 ||
box.top < 0 ||
box.right >= static_cast<UINT>(desktopImageWidth) ||
box.bottom >= static_cast<UINT>(desktopImageHeight))
{
Debug::Error("Cursor::UpdateTexture() => box is out of area.");
Debug::Error(
" ",
"(", box.left, ", ", box.top, ")",
" ~ (", box.right, ", ", box.bottom, ") > ",
"(", desktopImageWidth, ", ", desktopImageHeight, ")");
return;
}
if (monitor_->GetUnityTexture() == nullptr)
{
Debug::Error("Cursor::UpdateTexture() => Monitor::GetUnityTexture() is null.");
return;
}
D3D11_TEXTURE2D_DESC desc;
desc.Width = w;
desc.Height = h;
desc.Width = capturedImageWidth;
desc.Height = capturedImageHeight;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
@@ -171,20 +235,6 @@ void Cursor::UpdateTexture()
return;
}
D3D11_BOX box;
box.front = 0;
box.back = 1;
box.left = x;
box.top = y;
box.right = x + w;
box.bottom = y + h;
if (monitor_->GetUnityTexture() == nullptr)
{
Debug::Error("Cursor::UpdateTexture() => Monitor::GetUnityTexture() is null.");
return;
}
{
ComPtr<ID3D11DeviceContext> context;
GetDevice()->GetImmediateContext(&context);
@@ -209,6 +259,27 @@ void Cursor::UpdateTexture()
const auto desktop32 = reinterpret_cast<UINT*>(mappedSurface.pBits);
const UINT desktopPitch = mappedSurface.Pitch / sizeof(UINT);
// Take the monitor orientation into consideration.
const auto getDesktop32 = [&](int col, int row)
{
switch (monitorRot)
{
case DXGI_MODE_ROTATION_ROTATE90:
return desktop32[(capturedImageWidth - 1 - col) * desktopPitch + row];
break;
case DXGI_MODE_ROTATION_ROTATE180:
return desktop32[(capturedImageHeight - 1 - row) * desktopPitch + (capturedImageWidth - 1 - col)];
break;
case DXGI_MODE_ROTATION_ROTATE270:
return desktop32[col * desktopPitch + (capturedImageHeight - 1 - row)];
break;
case DXGI_MODE_ROTATION_IDENTITY:
case DXGI_MODE_ROTATION_UNSPECIFIED:
return desktop32[row * desktopPitch + col];
break;
}
};
// Access RGBA values at the same time
auto output32 = reinterpret_cast<UINT*>(bgra32Buffer_.get());
@@ -219,12 +290,11 @@ void Cursor::UpdateTexture()
for (int col = colMin, x = 0; col < colMax; ++col, ++x)
{
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 BYTE andMask = apiBuffer_[col / 8 + row * cursorImagePitch] & mask;
const BYTE xorMask = apiBuffer_[col / 8 + (row + capturedImageHeight) * cursorImagePitch] & mask;
const UINT andMask32 = andMask ? 0xFFFFFFFF : 0x00000000;
const UINT xorMask32 = xorMask ? 0xFFFFFFFF : 0x00000000;
output32[i] = (desktop32[y * desktopPitch + x] & andMask32) ^ xorMask32;
output32[row * cursorImageWidth + col] = (getDesktop32(x, y) & andMask32) ^ xorMask32;
}
}
}
@@ -236,13 +306,13 @@ void Cursor::UpdateTexture()
{
for (int col = colMin, x = 0; col < colMax; ++col, ++x)
{
const int i = col + row * w0;
const int j = col + row * p / sizeof(UINT);
const int i = col + row * cursorImageWidth;
const int j = col + row * cursorImagePitch / sizeof(UINT);
UINT mask = 0xFF000000 & buffer32[j];
if (mask)
{
output32[i] = (desktop32[y * desktopPitch + x] ^ buffer32[j]) | 0xFF000000;
output32[i] = (getDesktop32(x, y) ^ buffer32[j]) | 0xFF000000;
}
else
{
@@ -261,7 +331,7 @@ void Cursor::UpdateTexture()
{
auto output32 = reinterpret_cast<UINT*>(bgra32Buffer_.get());
const auto buffer32 = reinterpret_cast<UINT*>(apiBuffer_.get());
for (int i = 0; i < w * h; ++i)
for (int i = 0; i < cursorImageWidth * cursorImageHeight; ++i)
{
output32[i] = buffer32[i];
}

View File

@@ -50,7 +50,7 @@ void Monitor::Initialize(IDXGIOutput* output)
if (FAILED(GetDpiForMonitor(outputDesc_.Monitor, MDT_RAW_DPI, &dpiX_, &dpiY_)))
{
Debug::Error("Monitor::Initialize() => GetDpiForMonitor() failed.");
return;
// DPI is set as -1, so the application has to use the appropriate value.
}
auto output1 = reinterpret_cast<IDXGIOutput1*>(output);

View File

@@ -36,14 +36,30 @@ extern "C"
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API FinalizeUDD()
{
if (g_manager)
if (!g_manager) return;
g_manager.reset();
std::queue<Message> empty;
g_messages.swap(empty);
Debug::Finalize();
}
void UNITY_INTERFACE_API OnGraphicsDeviceEvent(UnityGfxDeviceEventType event)
{
switch (event)
{
g_manager.reset();
std::queue<Message> empty;
g_messages.swap(empty);
Debug::Finalize();
case kUnityGfxDeviceEventInitialize:
{
InitializeUDD();
break;
}
case kUnityGfxDeviceEventShutdown:
{
FinalizeUDD();
break;
}
}
}
@@ -51,10 +67,16 @@ extern "C"
{
g_unity = unityInterfaces;
InitializeUDD();
auto unityGraphics = g_unity->Get<IUnityGraphics>();
unityGraphics->RegisterDeviceEventCallback(OnGraphicsDeviceEvent);
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UnityPluginUnload()
{
auto unityGraphics = g_unity->Get<IUnityGraphics>();
unityGraphics->UnregisterDeviceEventCallback(OnGraphicsDeviceEvent);
FinalizeUDD();
g_unity = nullptr;
}