mirror of
https://github.com/gnif/LookingGlass.git
synced 2025-04-25 08:06:30 +00:00
Compare commits
63 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
54d811e098 | ||
![]() |
9593301511 | ||
![]() |
c2a19f5e76 | ||
![]() |
d8baa62c6a | ||
![]() |
81162b460e | ||
![]() |
852eb6bf69 | ||
![]() |
4b11743f18 | ||
![]() |
656d01a694 | ||
![]() |
a4406ac867 | ||
![]() |
57a2f68931 | ||
![]() |
f4df3f0ec7 | ||
![]() |
4e951184f1 | ||
![]() |
be4782b062 | ||
![]() |
b17c66d6bb | ||
![]() |
94fbbad21c | ||
![]() |
63a9365377 | ||
![]() |
cadcfe4b39 | ||
![]() |
3b883bf9fe | ||
![]() |
b58171c3e1 | ||
![]() |
7afb9b93eb | ||
![]() |
6396ff1e9c | ||
![]() |
db1dda00c8 | ||
![]() |
5b07286c65 | ||
![]() |
6dad0de8b8 | ||
![]() |
35c975d334 | ||
![]() |
530e83e7bf | ||
![]() |
40a4debfda | ||
![]() |
75f07cb28c | ||
![]() |
f26fa17bc1 | ||
![]() |
648fca7caa | ||
![]() |
868504d22d | ||
![]() |
cb423730e4 | ||
![]() |
86de1c9ac6 | ||
![]() |
d839a45d0b | ||
![]() |
9ffb800e93 | ||
![]() |
91e8440c9d | ||
![]() |
15eff234ec | ||
![]() |
6a4edfc6b6 | ||
![]() |
bf59e45118 | ||
![]() |
9cc99e7db9 | ||
![]() |
af5a48deca | ||
![]() |
b1b6da32ce | ||
![]() |
17b24cc8ff | ||
![]() |
7248b666ea | ||
![]() |
2084a9fee3 | ||
![]() |
a2a771f94e | ||
![]() |
b526eb3da0 | ||
![]() |
7a88a49f1c | ||
![]() |
ce23cff12e | ||
![]() |
09df8c41aa | ||
![]() |
0db9d3a27b | ||
![]() |
4f2eb984d3 | ||
![]() |
8b198091ce | ||
![]() |
62c075cfb5 | ||
![]() |
bea198735a | ||
![]() |
a421329d9a | ||
![]() |
5382a94945 | ||
![]() |
4278a10fe1 | ||
![]() |
35e6a6e81a | ||
![]() |
afbd844be8 | ||
![]() |
7c285a45fb | ||
![]() |
968fd42d46 | ||
![]() |
66ac453c98 |
7
.gitignore
vendored
7
.gitignore
vendored
@ -11,7 +11,10 @@ module/modules.order
|
||||
__pycache__
|
||||
*.py[co]
|
||||
*/.vs
|
||||
*.user
|
||||
idd/Debug
|
||||
idd/x64
|
||||
idd/LGIdd/x64
|
||||
idd/LGIdd/Debug
|
||||
idd/*/x64
|
||||
idd/*/Debug
|
||||
idd/*/VersionInfo.h
|
||||
idd/packages
|
||||
|
32
.woodpecker/idd.yaml
Normal file
32
.woodpecker/idd.yaml
Normal file
@ -0,0 +1,32 @@
|
||||
---
|
||||
labels:
|
||||
platform: windows/amd64
|
||||
|
||||
matrix:
|
||||
BUILD_TYPE:
|
||||
- Debug
|
||||
- Release
|
||||
|
||||
clone:
|
||||
- name: clone
|
||||
image: woodpeckerci/plugin-git
|
||||
pull: false
|
||||
settings:
|
||||
tags: true
|
||||
|
||||
steps:
|
||||
- name: idd
|
||||
when:
|
||||
- branch: master
|
||||
event: [push, manual]
|
||||
image: lg-vs2022:latest
|
||||
pull: true
|
||||
environment:
|
||||
VS_PATH: "\"C:\\\\Program Files (x86)\\\\Microsoft Visual Studio\\\\2022\\\\\""
|
||||
entrypoint:
|
||||
- cmd
|
||||
- /C
|
||||
- >
|
||||
%VS_PATH%\BuildTools\Common7\Tools\VsDevCmd.bat -arch=amd64 &&
|
||||
msbuild /restore idd\LGIdd.sln /p:Configuration=${BUILD_TYPE} /p:RestorePackagesConfig=true /p:Platform=x64 /p:SignMode=Off /m &&
|
||||
IF EXIST C:\artifacts\build.cmd (cmd /C C:\artifacts\build.cmd)
|
@ -113,6 +113,7 @@ set(SOURCES
|
||||
src/main.c
|
||||
src/core.c
|
||||
src/app.c
|
||||
src/message.c
|
||||
src/audio.c
|
||||
src/config.c
|
||||
src/keybind.c
|
||||
@ -124,6 +125,7 @@ set(SOURCES
|
||||
src/eglutil.c
|
||||
src/overlay_utils.c
|
||||
src/render_queue.c
|
||||
src/evdev.c
|
||||
|
||||
src/overlay/splash.c
|
||||
src/overlay/alert.c
|
||||
|
@ -209,7 +209,7 @@ done:
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static int getCharcode(uint32_t key)
|
||||
int waylandGetCharCode(int key)
|
||||
{
|
||||
key += 8; // xkb scancode is evdev scancode + 8
|
||||
xkb_keysym_t sym = xkb_state_key_get_one_sym(wlWm.xkbState, key);
|
||||
@ -232,7 +232,7 @@ static void keyboardEnterHandler(void * data, struct wl_keyboard * keyboard,
|
||||
|
||||
uint32_t * key;
|
||||
wl_array_for_each(key, keys)
|
||||
app_handleKeyPress(*key, getCharcode(*key));
|
||||
app_handleKeyPress(*key);
|
||||
}
|
||||
|
||||
static void keyboardLeaveHandler(void * data, struct wl_keyboard * keyboard,
|
||||
@ -253,9 +253,9 @@ static void keyboardKeyHandler(void * data, struct wl_keyboard * keyboard,
|
||||
return;
|
||||
|
||||
if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
|
||||
app_handleKeyPress(key, getCharcode(key));
|
||||
app_handleKeyPress(key);
|
||||
else
|
||||
app_handleKeyRelease(key, getCharcode(key));
|
||||
app_handleKeyRelease(key);
|
||||
|
||||
if (!wlWm.xkbState || !app_isOverlayMode() || state != WL_KEYBOARD_KEY_STATE_PRESSED)
|
||||
return;
|
||||
|
@ -263,6 +263,7 @@ struct LG_DisplayServerOps LGDS_Wayland =
|
||||
.uncapturePointer = waylandUncapturePointer,
|
||||
.grabKeyboard = waylandGrabKeyboard,
|
||||
.ungrabKeyboard = waylandUngrabKeyboard,
|
||||
.getCharCode = waylandGetCharCode,
|
||||
.warpPointer = waylandWarpPointer,
|
||||
.realignPointer = waylandRealignPointer,
|
||||
.isValidPointerPos = waylandIsValidPointerPos,
|
||||
|
@ -277,6 +277,7 @@ void waylandUncapturePointer(void);
|
||||
void waylandRealignPointer(void);
|
||||
void waylandWarpPointer(int x, int y, bool exiting);
|
||||
void waylandGuestPointerUpdated(double x, double y, double localX, double localY);
|
||||
int waylandGetCharCode(int key);
|
||||
|
||||
// output module
|
||||
bool waylandOutputInit(void);
|
||||
|
@ -1087,8 +1087,9 @@ static void setFocus(bool focused, double x, double y)
|
||||
app_handleFocusEvent(focused);
|
||||
}
|
||||
|
||||
static int getCharcode(int detail)
|
||||
static int x11GetCharCode(int detail)
|
||||
{
|
||||
detail += x11.minKeycode;
|
||||
if (detail < x11.minKeycode || detail > x11.maxKeycode)
|
||||
return 0;
|
||||
|
||||
@ -1229,8 +1230,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
||||
return;
|
||||
|
||||
XIDeviceEvent *device = cookie->data;
|
||||
app_handleKeyPress(device->detail - x11.minKeycode,
|
||||
getCharcode(device->detail));
|
||||
app_handleKeyPress(device->detail - x11.minKeycode);
|
||||
|
||||
if (!x11.xic || !app_isOverlayMode())
|
||||
return;
|
||||
@ -1280,8 +1280,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
||||
return;
|
||||
|
||||
XIDeviceEvent *device = cookie->data;
|
||||
app_handleKeyRelease(device->detail - x11.minKeycode,
|
||||
getCharcode(device->detail));
|
||||
app_handleKeyRelease(device->detail - x11.minKeycode);
|
||||
|
||||
if (!x11.xic || !app_isOverlayMode())
|
||||
return;
|
||||
@ -1310,8 +1309,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
||||
return;
|
||||
|
||||
XIRawEvent *raw = cookie->data;
|
||||
app_handleKeyPress(raw->detail - x11.minKeycode,
|
||||
getCharcode(raw->detail));
|
||||
app_handleKeyPress(raw->detail - x11.minKeycode);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1321,8 +1319,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
||||
return;
|
||||
|
||||
XIRawEvent *raw = cookie->data;
|
||||
app_handleKeyRelease(raw->detail - x11.minKeycode,
|
||||
getCharcode(raw->detail));
|
||||
app_handleKeyRelease(raw->detail - x11.minKeycode);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2017,6 +2014,7 @@ struct LG_DisplayServerOps LGDS_X11 =
|
||||
.ungrabPointer = x11UngrabPointer,
|
||||
.capturePointer = x11CapturePointer,
|
||||
.uncapturePointer = x11UncapturePointer,
|
||||
.getCharCode = x11GetCharCode,
|
||||
.grabKeyboard = x11GrabKeyboard,
|
||||
.ungrabKeyboard = x11UngrabKeyboard,
|
||||
.warpPointer = x11WarpPointer,
|
||||
|
@ -59,8 +59,8 @@ void app_handleButtonPress(int button);
|
||||
void app_handleButtonRelease(int button);
|
||||
void app_handleWheelMotion(double motion);
|
||||
void app_handleKeyboardTyped(const char * typed);
|
||||
void app_handleKeyPress(int scancode, int charcode);
|
||||
void app_handleKeyRelease(int scancode, int charcode);
|
||||
void app_handleKeyPress(int scancode);
|
||||
void app_handleKeyRelease(int scancode);
|
||||
void app_handleKeyboardModifiers(bool ctrl, bool shift, bool alt, bool super);
|
||||
void app_handleKeyboardLEDs(bool numLock, bool capsLock, bool scrollLock);
|
||||
void app_handleEnterEvent(bool entered);
|
||||
|
51
client/include/evdev.h
Normal file
51
client/include/evdev.h
Normal file
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2025 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* initialize configuration options
|
||||
*/
|
||||
void evdev_earlyInit(void);
|
||||
|
||||
/**
|
||||
* start the evdev layer
|
||||
*/
|
||||
bool evdev_start(void);
|
||||
|
||||
/**
|
||||
* stop the evdev layer
|
||||
*/
|
||||
void evdev_stop(void);
|
||||
|
||||
/**
|
||||
* grab the keyboard for exclusive access
|
||||
*/
|
||||
void evdev_grabKeyboard(void);
|
||||
|
||||
/**
|
||||
* ungrab the keyboard
|
||||
*/
|
||||
void evdev_ungrabKeyboard(void);
|
||||
|
||||
/**
|
||||
* returns true if input should only be processed by evdev
|
||||
*/
|
||||
bool evdev_isExclusive(void);
|
@ -183,6 +183,9 @@ struct LG_DisplayServerOps
|
||||
void (*capturePointer)(void);
|
||||
void (*uncapturePointer)(void);
|
||||
|
||||
/* get the character code for the provided scancode */
|
||||
int (*getCharCode)(int sc);
|
||||
|
||||
/* exiting = true if the warp is to leave the window */
|
||||
void (*warpPointer)(int x, int y, bool exiting);
|
||||
|
||||
@ -253,6 +256,7 @@ struct LG_DisplayServerOps
|
||||
DEBUG_ASSERT((x)->ungrabPointer ); \
|
||||
DEBUG_ASSERT((x)->capturePointer ); \
|
||||
DEBUG_ASSERT((x)->uncapturePointer ); \
|
||||
DEBUG_ASSERT((x)->getCharCode ); \
|
||||
DEBUG_ASSERT((x)->warpPointer ); \
|
||||
DEBUG_ASSERT((x)->realignPointer ); \
|
||||
DEBUG_ASSERT((x)->isValidPointerPos ); \
|
||||
|
@ -18,13 +18,14 @@
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "app.h"
|
||||
#include "app_internal.h"
|
||||
|
||||
#include "main.h"
|
||||
#include "core.h"
|
||||
#include "util.h"
|
||||
#include "clipboard.h"
|
||||
#include "render_queue.h"
|
||||
#include "evdev.h"
|
||||
|
||||
#include "kb.h"
|
||||
|
||||
@ -119,7 +120,7 @@ void app_handleFocusEvent(bool focused)
|
||||
if (g_params.releaseKeysOnFocusLoss)
|
||||
for (int key = 0; key < KEY_MAX; key++)
|
||||
if (g_state.keyDown[key])
|
||||
app_handleKeyRelease(key, 0);
|
||||
app_handleKeyReleaseInternal(key);
|
||||
|
||||
g_state.escapeActive = false;
|
||||
|
||||
@ -311,7 +312,7 @@ void app_handleWheelMotion(double motion)
|
||||
g_state.io->MouseWheel -= motion;
|
||||
}
|
||||
|
||||
void app_handleKeyPress(int sc, int charcode)
|
||||
void app_handleKeyPressInternal(int sc)
|
||||
{
|
||||
if (!app_isOverlayMode() || !g_state.io->WantCaptureKeyboard)
|
||||
{
|
||||
@ -327,6 +328,7 @@ void app_handleKeyPress(int sc, int charcode)
|
||||
{
|
||||
g_state.escapeAction = sc;
|
||||
KeybindHandle handle;
|
||||
int charcode = g_state.ds->getCharCode(sc);
|
||||
ll_forEachNL(g_state.bindings, item, handle)
|
||||
{
|
||||
if ((handle->sc && handle->sc == sc ) ||
|
||||
@ -374,7 +376,7 @@ void app_handleKeyPress(int sc, int charcode)
|
||||
}
|
||||
}
|
||||
|
||||
void app_handleKeyRelease(int sc, int charcode)
|
||||
void app_handleKeyReleaseInternal(int sc)
|
||||
{
|
||||
if (g_state.escapeActive)
|
||||
{
|
||||
@ -419,6 +421,18 @@ void app_handleKeyRelease(int sc, int charcode)
|
||||
}
|
||||
}
|
||||
|
||||
void app_handleKeyPress(int sc)
|
||||
{
|
||||
if (!evdev_isExclusive())
|
||||
app_handleKeyPressInternal(sc);
|
||||
}
|
||||
|
||||
void app_handleKeyRelease(int sc)
|
||||
{
|
||||
if (!evdev_isExclusive())
|
||||
app_handleKeyReleaseInternal(sc);
|
||||
}
|
||||
|
||||
void app_handleKeyboardTyped(const char * typed)
|
||||
{
|
||||
ImGuiIO_AddInputCharactersUTF8(g_state.io, typed);
|
||||
|
@ -18,11 +18,12 @@
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef _H_LG_APP_INTERNAL_
|
||||
#define _H_LG_APP_INTERNAL_
|
||||
|
||||
#include <Windows.h>
|
||||
#include <tchar.h>
|
||||
#include "app.h"
|
||||
|
||||
VOID _DBGPRINT(PCSTR kszFunction, INT iLineNumber, LPCSTR kszDebugFormatString, ...);
|
||||
#define DBGPRINT(kszDebugFormatString, ...) \
|
||||
_DBGPRINT(__FUNCTION__, __LINE__, kszDebugFormatString, __VA_ARGS__)
|
||||
void app_handleKeyPressInternal(int scancode);
|
||||
void app_handleKeyReleaseInternal(int scancode);
|
||||
|
||||
#endif
|
@ -220,6 +220,14 @@ static struct Option options[] =
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = false,
|
||||
},
|
||||
{
|
||||
.module = "win",
|
||||
.name = "setGuestRes",
|
||||
.description = "On window size change, request the guest to match"
|
||||
" resoution (if supported by the guest, currently LG IDD only)",
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true,
|
||||
},
|
||||
{
|
||||
.module = "win",
|
||||
.name = "fpsMin",
|
||||
@ -717,6 +725,7 @@ bool config_load(int argc, char * argv[])
|
||||
g_params.helpMenuDelayUs = option_get_int("input", "helpMenuDelay") * (uint64_t) 1000;
|
||||
|
||||
g_params.minimizeOnFocusLoss = option_get_bool("win", "minimizeOnFocusLoss");
|
||||
g_params.setGuestRes = option_get_bool("win", "setGuestRes" );
|
||||
|
||||
if ((g_params.useSpice = option_get_bool("spice", "enable")))
|
||||
{
|
||||
|
@ -23,6 +23,8 @@
|
||||
#include "app.h"
|
||||
#include "util.h"
|
||||
#include "kb.h"
|
||||
#include "message.h"
|
||||
#include "message.h"
|
||||
|
||||
#include "common/time.h"
|
||||
#include "common/debug.h"
|
||||
@ -141,6 +143,13 @@ void core_setGrabQuiet(bool enable)
|
||||
core_setCursorInView(true);
|
||||
g_state.ignoreInput = false;
|
||||
|
||||
/* ensure the local mouse is inside the window before we capture, this fixes
|
||||
* odd UI behaviour if the user is using focus follows mouse and the window
|
||||
* was focused without the cursor being in window already */
|
||||
struct DoublePoint local;
|
||||
util_guestCurToLocal(&local);
|
||||
core_warpPointer(local.x, local.y, true);
|
||||
|
||||
if (g_params.grabKeyboard)
|
||||
g_state.ds->grabKeyboard();
|
||||
|
||||
@ -181,8 +190,44 @@ bool core_warpPointer(int x, int y, bool exiting)
|
||||
return true;
|
||||
}
|
||||
|
||||
void core_onWindowSizeChanged(unsigned width, unsigned height)
|
||||
{
|
||||
if (!g_state.pointerQueue)
|
||||
return;
|
||||
|
||||
if (g_state.srcSize.x == width && g_state.srcSize.y == height)
|
||||
return;
|
||||
|
||||
const KVMFRWindowSize msg =
|
||||
{
|
||||
.msg.type = KVMFR_MESSAGE_WINDOWSIZE,
|
||||
.w = width,
|
||||
.h = height
|
||||
};
|
||||
|
||||
uint32_t serial;
|
||||
LGMP_STATUS status;
|
||||
if ((status = lgmpClientSendData(g_state.pointerQueue,
|
||||
&msg, sizeof(msg), &serial)) != LGMP_OK)
|
||||
DEBUG_WARN("Message send failed: %s", lgmpStatusString(status));
|
||||
}
|
||||
|
||||
void core_updatePositionInfo(void)
|
||||
{
|
||||
if (g_params.setGuestRes && g_state.kvmfrFeatures & KVMFR_FEATURE_WINDOWSIZE)
|
||||
{
|
||||
LGMsg msg =
|
||||
{
|
||||
.type = LG_MSG_WINDOWSIZE,
|
||||
.windowSize =
|
||||
{
|
||||
.width = g_state.windowW,
|
||||
.height = g_state.windowH
|
||||
}
|
||||
};
|
||||
lgMessage_post(&msg);
|
||||
}
|
||||
|
||||
if (!g_state.haveSrcSize)
|
||||
goto done;
|
||||
|
||||
|
@ -29,6 +29,7 @@ void core_setCursorInView(bool enable);
|
||||
void core_setGrab(bool enable);
|
||||
void core_setGrabQuiet(bool enable);
|
||||
bool core_warpPointer(int x, int y, bool exiting);
|
||||
void core_onWindowSizeChanged(unsigned width, unsigned height);
|
||||
void core_updatePositionInfo(void);
|
||||
void core_alignToGuest(void);
|
||||
bool core_isValidPointerPos(int x, int y);
|
||||
|
441
client/src/evdev.c
Normal file
441
client/src/evdev.c
Normal file
@ -0,0 +1,441 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2025 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "evdev.h"
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <linux/input.h>
|
||||
#include <string.h>
|
||||
#include <sys/epoll.h>
|
||||
|
||||
#include "app_internal.h"
|
||||
#include "core.h"
|
||||
#include "main.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/option.h"
|
||||
#include "common/stringlist.h"
|
||||
#include "common/thread.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char * path;
|
||||
int fd;
|
||||
bool grabbed;
|
||||
}
|
||||
EvdevDevice;
|
||||
|
||||
struct EvdevState
|
||||
{
|
||||
char * deviceList;
|
||||
EvdevDevice * devices;
|
||||
int deviceCount;
|
||||
bool exclusive;
|
||||
int keys[KEY_MAX];
|
||||
|
||||
void (*dsGrabKeyboard)(void);
|
||||
void (*dsUngrabKeyboard)(void);
|
||||
|
||||
int epoll;
|
||||
LGThread * thread;
|
||||
bool grabbed;
|
||||
|
||||
enum
|
||||
{
|
||||
PENDING_NONE,
|
||||
PENDING_GRAB,
|
||||
PENDING_UNGRAB
|
||||
}
|
||||
pending;
|
||||
};
|
||||
|
||||
static struct EvdevState state = {};
|
||||
|
||||
static struct Option options[] =
|
||||
{
|
||||
{
|
||||
.module = "input",
|
||||
.name = "evdev",
|
||||
.description = "csv list of evdev input devices to use "
|
||||
"for capture mode (ie: /dev/input/by-id/usb-some_device-event-kbd)",
|
||||
.type = OPTION_TYPE_STRING,
|
||||
.value.x_string = NULL,
|
||||
},
|
||||
{
|
||||
.module = "input",
|
||||
.name = "evdevExclusive",
|
||||
.description = "Only use evdev devices for input when in capture mode",
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true
|
||||
},
|
||||
{0}
|
||||
};
|
||||
|
||||
void evdev_earlyInit(void)
|
||||
{
|
||||
option_register(options);
|
||||
}
|
||||
|
||||
static bool evdev_grabDevice(EvdevDevice * device)
|
||||
{
|
||||
if (device->grabbed)
|
||||
return true;
|
||||
|
||||
if (ioctl(device->fd, EVIOCGRAB, (void *)1) < 0)
|
||||
{
|
||||
DEBUG_ERROR("EVIOCGRAB=1 failed: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_INFO("Grabbed %s", device->path);
|
||||
device->grabbed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool evdev_openDevice(EvdevDevice * device, bool quiet)
|
||||
{
|
||||
device->fd = open(device->path, O_RDWR);
|
||||
if (device->fd < 0)
|
||||
{
|
||||
if (errno != ENOENT || !quiet)
|
||||
DEBUG_ERROR("Unable to open %s (%s)", device->path, strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
|
||||
struct epoll_event event =
|
||||
{
|
||||
.events = EPOLLIN,
|
||||
.data.ptr = device
|
||||
};
|
||||
|
||||
if (epoll_ctl(state.epoll, EPOLL_CTL_ADD, device->fd, &event) != 0)
|
||||
{
|
||||
DEBUG_ERROR("Failed to add fd to epoll");
|
||||
goto err;
|
||||
}
|
||||
|
||||
DEBUG_INFO("Opened: %s", device->path);
|
||||
|
||||
if (state.grabbed)
|
||||
evdev_grabDevice(device);
|
||||
|
||||
return true;
|
||||
|
||||
err:
|
||||
close(device->fd);
|
||||
device->fd = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int evdev_thread(void * opaque)
|
||||
{
|
||||
struct epoll_event * events = alloca(sizeof(*events) * state.deviceCount);
|
||||
struct input_event msgs[256];
|
||||
|
||||
DEBUG_INFO("evdev_thread Started");
|
||||
while(app_isRunning())
|
||||
{
|
||||
int openDevices = 0;
|
||||
for(int i = 0; i < state.deviceCount; ++i)
|
||||
{
|
||||
EvdevDevice * dev = &state.devices[i];
|
||||
if (dev->fd <= 0)
|
||||
{
|
||||
if (evdev_openDevice(dev, true))
|
||||
++openDevices;
|
||||
}
|
||||
else
|
||||
++openDevices;
|
||||
}
|
||||
|
||||
if (openDevices == 0)
|
||||
{
|
||||
usleep(1000);
|
||||
continue;
|
||||
}
|
||||
|
||||
int waiting = epoll_wait(state.epoll, events, state.deviceCount, 100);
|
||||
for(int i = 0; i < waiting; ++i)
|
||||
{
|
||||
EvdevDevice * device = (EvdevDevice *)events[i].data.ptr;
|
||||
int n = read(device->fd, msgs, sizeof(msgs));
|
||||
if (n < 0)
|
||||
{
|
||||
if (errno == ENODEV)
|
||||
{
|
||||
DEBUG_WARN("Device was removed: %s", device->path);
|
||||
epoll_ctl(state.epoll, EPOLL_CTL_DEL, device->fd, NULL);
|
||||
close(device->fd);
|
||||
device->fd = 0;
|
||||
device->grabbed = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
DEBUG_WARN("Failed to read evdev event: %s (%s)",
|
||||
device->path, strerror(errno));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (n % sizeof(*msgs) != 0)
|
||||
DEBUG_WARN("Incomplete evdev read: %s", device->path);
|
||||
|
||||
bool grabbed = state.grabbed;
|
||||
|
||||
int count = n / sizeof(*msgs);
|
||||
struct input_event *ev = msgs;
|
||||
int mouseX = 0, mouseY = 0;
|
||||
|
||||
for(int i = 0; i < count; ++i, ++ev)
|
||||
{
|
||||
switch(ev->type)
|
||||
{
|
||||
case EV_KEY:
|
||||
{
|
||||
bool isMouseBtn = ev->code >= BTN_MOUSE && ev->code <= BTN_BACK;
|
||||
static const int mouseBtnMap[] = {1, 3, 2, 6, 7, 0, 0};
|
||||
|
||||
if (ev->value == 1)
|
||||
{
|
||||
++state.keys[ev->code];
|
||||
|
||||
if (grabbed && state.keys[ev->code] == 1)
|
||||
{
|
||||
if (isMouseBtn)
|
||||
app_handleButtonPress(mouseBtnMap[ev->code - BTN_MOUSE]);
|
||||
else
|
||||
app_handleKeyPressInternal(ev->code);
|
||||
}
|
||||
}
|
||||
else if (ev->value == 0 && --state.keys[ev->code] <= 0)
|
||||
{
|
||||
state.keys[ev->code] = 0;
|
||||
|
||||
if (state.pending == PENDING_GRAB)
|
||||
{
|
||||
state.pending = PENDING_NONE;
|
||||
evdev_grabKeyboard();
|
||||
}
|
||||
else if (state.pending == PENDING_UNGRAB)
|
||||
{
|
||||
state.pending = PENDING_NONE;
|
||||
evdev_ungrabKeyboard();
|
||||
}
|
||||
|
||||
if (grabbed)
|
||||
{
|
||||
if (isMouseBtn)
|
||||
app_handleButtonRelease(mouseBtnMap[ev->code - BTN_MOUSE]);
|
||||
else
|
||||
app_handleKeyReleaseInternal(ev->code);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case EV_REL:
|
||||
if (!grabbed)
|
||||
continue;
|
||||
|
||||
switch(ev->code)
|
||||
{
|
||||
case REL_X:
|
||||
mouseX += ev->value;
|
||||
break;
|
||||
|
||||
case REL_Y:
|
||||
mouseY += ev->value;
|
||||
break;
|
||||
|
||||
case REL_WHEEL:
|
||||
{
|
||||
int btn;
|
||||
if (ev->value > 0)
|
||||
btn = 4;
|
||||
else
|
||||
btn = 5;
|
||||
|
||||
app_handleButtonPress (btn);
|
||||
app_handleButtonRelease(btn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mouseX != 0 || mouseY != 0)
|
||||
core_handleMouseGrabbed(mouseX, mouseY);
|
||||
}
|
||||
}
|
||||
DEBUG_INFO("evdev_thread Stopped");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool evdev_start(void)
|
||||
{
|
||||
const char * deviceList = option_get_string("input", "evdev");
|
||||
if (!deviceList)
|
||||
return false;
|
||||
|
||||
state.deviceList = strdup(deviceList);
|
||||
StringList sl = stringlist_new(false);
|
||||
|
||||
char * token = strtok(state.deviceList, ",");
|
||||
while(token != NULL)
|
||||
{
|
||||
stringlist_push(sl, token);
|
||||
token = strtok(NULL, ",");
|
||||
}
|
||||
|
||||
state.deviceCount = stringlist_count(sl);
|
||||
state.devices = calloc(state.deviceCount, sizeof(*state.devices));
|
||||
for(int i = 0; i < state.deviceCount; ++i)
|
||||
state.devices[i].path = stringlist_at(sl, i);
|
||||
stringlist_free(&sl);
|
||||
|
||||
// nothing to do if there are no configured devices
|
||||
if (state.deviceCount == 0)
|
||||
return false;
|
||||
|
||||
state.exclusive = option_get("input", "evdevExclusive");
|
||||
|
||||
state.epoll = epoll_create1(0);
|
||||
if (state.epoll < 0)
|
||||
{
|
||||
DEBUG_ERROR("Failed to create epoll (%s)", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
for(int i = 0; i < state.deviceCount; ++i)
|
||||
{
|
||||
EvdevDevice * device = &state.devices[i];
|
||||
if (!evdev_openDevice(device, false))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!lgCreateThread("Evdev", evdev_thread, NULL, &state.thread))
|
||||
{
|
||||
DEBUG_ERROR("Failed to create the evdev thread");
|
||||
return false;
|
||||
}
|
||||
|
||||
//hook the display server's grab methods
|
||||
state.dsGrabKeyboard = g_state.ds->grabKeyboard;
|
||||
state.dsUngrabKeyboard = g_state.ds->ungrabKeyboard;
|
||||
g_state.ds->grabKeyboard = &evdev_grabKeyboard;
|
||||
g_state.ds->ungrabKeyboard = &evdev_ungrabKeyboard;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void evdev_stop(void)
|
||||
{
|
||||
if (state.deviceList)
|
||||
{
|
||||
free(state.deviceList);
|
||||
state.deviceList = NULL;
|
||||
}
|
||||
|
||||
if (state.thread)
|
||||
{
|
||||
lgJoinThread(state.thread, NULL);
|
||||
state.thread = NULL;
|
||||
}
|
||||
|
||||
if (state.epoll >= 0)
|
||||
{
|
||||
close(state.epoll);
|
||||
state.epoll = 0;
|
||||
}
|
||||
|
||||
for(EvdevDevice * device = state.devices; device->path; ++device)
|
||||
{
|
||||
if (device->fd <= 0)
|
||||
continue;
|
||||
|
||||
close(device->fd);
|
||||
device->fd = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void evdev_grabKeyboard(void)
|
||||
{
|
||||
if (state.grabbed)
|
||||
return;
|
||||
|
||||
// we must be in a neutral state
|
||||
for(int i = 0; i < KEY_MAX; ++i)
|
||||
if (state.keys[i] > 0)
|
||||
{
|
||||
state.pending = PENDING_GRAB;
|
||||
return;
|
||||
}
|
||||
|
||||
// state.dsGrabKeyboard();
|
||||
|
||||
for(EvdevDevice * device = state.devices; device->path; ++device)
|
||||
{
|
||||
if (device->fd > 0)
|
||||
evdev_grabDevice(device);
|
||||
}
|
||||
|
||||
state.grabbed = true;
|
||||
}
|
||||
|
||||
void evdev_ungrabKeyboard(void)
|
||||
{
|
||||
if (!state.grabbed)
|
||||
return;
|
||||
|
||||
// we must be in a neutral state
|
||||
for(int i = 0; i < KEY_MAX; ++i)
|
||||
if (state.keys[i] > 0)
|
||||
{
|
||||
state.pending = PENDING_UNGRAB;
|
||||
return;
|
||||
}
|
||||
|
||||
for(EvdevDevice * device = state.devices; device->path; ++device)
|
||||
{
|
||||
if (device->fd <= 0 || !device->grabbed)
|
||||
continue;
|
||||
|
||||
if (ioctl(device->fd, EVIOCGRAB, (void *)0) < 0)
|
||||
{
|
||||
DEBUG_ERROR("EVIOCGRAB=0 failed: %s", strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
DEBUG_INFO("Ungrabbed %s", device->path);
|
||||
device->grabbed = false;
|
||||
}
|
||||
|
||||
// state.dsUngrabKeyboard();
|
||||
|
||||
state.grabbed = false;
|
||||
}
|
||||
|
||||
bool evdev_isExclusive(void)
|
||||
{
|
||||
return state.exclusive && state.grabbed && !app_isOverlayMode();
|
||||
}
|
@ -25,6 +25,7 @@
|
||||
#include "audio.h"
|
||||
#include "core.h"
|
||||
#include "kb.h"
|
||||
#include "message.h"
|
||||
|
||||
#include <purespice.h>
|
||||
#include <stdio.h>
|
||||
@ -125,6 +126,26 @@ static void bind_toggleKey(int sc, void * opaque)
|
||||
purespice_keyUp((uintptr_t) opaque);
|
||||
}
|
||||
|
||||
static void bind_setGuestRes(int sc, void * opaque)
|
||||
{
|
||||
if (!(g_state.kvmfrFeatures & KVMFR_FEATURE_WINDOWSIZE))
|
||||
{
|
||||
app_alert(LG_ALERT_INFO, "The guest doesn't support this feature");
|
||||
return;
|
||||
}
|
||||
|
||||
LGMsg msg =
|
||||
{
|
||||
.type = LG_MSG_WINDOWSIZE,
|
||||
.windowSize =
|
||||
{
|
||||
.width = g_state.windowW,
|
||||
.height = g_state.windowH
|
||||
}
|
||||
};
|
||||
lgMessage_post(&msg);
|
||||
}
|
||||
|
||||
void keybind_commonRegister(void)
|
||||
{
|
||||
app_registerKeybind(0, 'F', bind_fullscreen , NULL,
|
||||
@ -137,6 +158,8 @@ void keybind_commonRegister(void)
|
||||
"Quit");
|
||||
app_registerKeybind(0, 'O', bind_toggleOverlay, NULL,
|
||||
"Toggle overlay");
|
||||
app_registerKeybind(0, '=', bind_setGuestRes, NULL,
|
||||
"Set guest resolution to match window size");
|
||||
}
|
||||
|
||||
#if ENABLE_AUDIO
|
||||
|
@ -52,6 +52,7 @@
|
||||
#include "common/cpuinfo.h"
|
||||
#include "common/ll.h"
|
||||
|
||||
#include "message.h"
|
||||
#include "core.h"
|
||||
#include "app.h"
|
||||
#include "audio.h"
|
||||
@ -64,6 +65,7 @@
|
||||
#include "overlay_utils.h"
|
||||
#include "util.h"
|
||||
#include "render_queue.h"
|
||||
#include "evdev.h"
|
||||
|
||||
// forwards
|
||||
static int renderThread(void * unused);
|
||||
@ -1240,6 +1242,9 @@ static int lg_run(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (evdev_start())
|
||||
DEBUG_INFO("Using evdev for keyboard capture");
|
||||
|
||||
// override the SIGINIT handler so that we can tell the difference between
|
||||
// SIGINT and the user sending a close event, such as ALT+F4
|
||||
signal(SIGINT , intHandler);
|
||||
@ -1752,6 +1757,7 @@ restart:
|
||||
g_state.state = APP_STATE_RESTART;
|
||||
break;
|
||||
}
|
||||
lgMessage_process();
|
||||
g_state.ds->wait(100);
|
||||
}
|
||||
|
||||
@ -1868,6 +1874,9 @@ int main(int argc, char * argv[])
|
||||
egl_dynProcsInit();
|
||||
gl_dynProcsInit();
|
||||
|
||||
if (!lgMessage_init())
|
||||
return -1;
|
||||
|
||||
g_state.bindings = ll_new();
|
||||
|
||||
g_state.overlays = ll_new();
|
||||
@ -1891,11 +1900,14 @@ int main(int argc, char * argv[])
|
||||
if (LG_AudioDevs[i]->earlyInit)
|
||||
LG_AudioDevs[i]->earlyInit();
|
||||
|
||||
evdev_earlyInit();
|
||||
|
||||
if (!config_load(argc, argv))
|
||||
return -1;
|
||||
|
||||
const int ret = lg_run();
|
||||
lg_shutdown();
|
||||
lgMessage_deinit();
|
||||
|
||||
config_free();
|
||||
|
||||
|
@ -72,6 +72,7 @@ struct AppState
|
||||
bool modSuper;
|
||||
uint64_t lastImGuiFrame;
|
||||
bool renderImGuiTwice;
|
||||
bool exclusiveEvdev;
|
||||
|
||||
struct LG_DisplayServerOps * ds;
|
||||
bool dsInitialized;
|
||||
@ -171,6 +172,7 @@ struct AppParams
|
||||
bool center;
|
||||
int x, y;
|
||||
unsigned int w, h;
|
||||
bool setGuestRes;
|
||||
int fpsMin;
|
||||
LG_RendererRotate winRotate;
|
||||
bool useSpice;
|
||||
|
146
client/src/message.c
Normal file
146
client/src/message.c
Normal file
@ -0,0 +1,146 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2025 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "message.h"
|
||||
#include "core.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/ll.h"
|
||||
#include "common/time.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct MsgEvent
|
||||
{
|
||||
uint64_t timestamp;
|
||||
LGMsg msg;
|
||||
}
|
||||
MsgEvent;
|
||||
|
||||
struct MsgState
|
||||
{
|
||||
struct ll * list;
|
||||
struct
|
||||
{
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
}
|
||||
lastWindowSize;
|
||||
};
|
||||
|
||||
static struct MsgState this = {0};
|
||||
|
||||
bool lgMessage_init(void)
|
||||
{
|
||||
this.list = ll_new();
|
||||
if (!this.list)
|
||||
{
|
||||
DEBUG_ERROR("Failed to create the message list");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void lgMessage_deinit(void)
|
||||
{
|
||||
if (this.list)
|
||||
{
|
||||
void * tmp;
|
||||
while(ll_shift(this.list, &tmp))
|
||||
free(tmp);
|
||||
ll_free(this.list);
|
||||
this.list = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void lgMessage_post(const LGMsg * msg)
|
||||
{
|
||||
MsgEvent * event = (MsgEvent *)malloc(sizeof(*event));
|
||||
if (!event)
|
||||
{
|
||||
DEBUG_ERROR("Out of memory");
|
||||
return;
|
||||
}
|
||||
|
||||
event->timestamp = microtime();
|
||||
memcpy(&event->msg, msg, sizeof(event->msg));
|
||||
if (!ll_push(this.list, event))
|
||||
{
|
||||
DEBUG_ERROR("Failed to post a message to the list");
|
||||
free(event);
|
||||
}
|
||||
}
|
||||
|
||||
void lgMessage_process(void)
|
||||
{
|
||||
MsgEvent * event;
|
||||
MsgEvent * windowSize = NULL;
|
||||
|
||||
while(ll_shift(this.list, (void **)&event))
|
||||
{
|
||||
switch(event->msg.type)
|
||||
{
|
||||
case LG_MSG_WINDOWSIZE:
|
||||
{
|
||||
// retain the last/latest windowsize event
|
||||
if (windowSize)
|
||||
free(windowSize);
|
||||
windowSize = event;
|
||||
continue;
|
||||
}
|
||||
|
||||
default:
|
||||
DEBUG_ERROR("Unhandled %d", event->msg.type);
|
||||
break;
|
||||
}
|
||||
|
||||
free(event);
|
||||
}
|
||||
|
||||
// if there was a windowSize event, then process it
|
||||
if (windowSize)
|
||||
{
|
||||
const uint64_t time = microtime();
|
||||
if (time - windowSize->timestamp < 500000)
|
||||
{
|
||||
// requeue the event for later
|
||||
if (!ll_push(this.list, event))
|
||||
{
|
||||
DEBUG_ERROR("Failed to re-queue the windowSize event");
|
||||
free(windowSize);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (event->msg.windowSize.width != this.lastWindowSize.width ||
|
||||
event->msg.windowSize.height != this.lastWindowSize.height)
|
||||
{
|
||||
this.lastWindowSize.width = event->msg.windowSize.width;
|
||||
this.lastWindowSize.height = event->msg.windowSize.height;
|
||||
core_onWindowSizeChanged(
|
||||
event->msg.windowSize.width,
|
||||
event->msg.windowSize.height);
|
||||
}
|
||||
free(windowSize);
|
||||
}
|
||||
}
|
||||
}
|
66
client/src/message.h
Normal file
66
client/src/message.h
Normal file
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2025 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _H_LG_MSG_
|
||||
#define _H_LG_MSG_
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef enum LGMsgType
|
||||
{
|
||||
/* The LG client window size changed
|
||||
* Note:
|
||||
* This message is debounced to avoid flooding the guest with resize events
|
||||
*/
|
||||
LG_MSG_WINDOWSIZE
|
||||
}
|
||||
LGMsgType;
|
||||
|
||||
typedef struct LGMsg
|
||||
{
|
||||
LGMsgType type;
|
||||
union
|
||||
{
|
||||
//LG_MSG_WINDOWSIZE
|
||||
struct
|
||||
{
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
}
|
||||
windowSize;
|
||||
|
||||
//LG_MSG_VIDEO
|
||||
struct
|
||||
{
|
||||
bool enabled;
|
||||
}
|
||||
video;
|
||||
};
|
||||
}
|
||||
LGMsg;
|
||||
|
||||
bool lgMessage_init(void);
|
||||
void lgMessage_deinit(void);
|
||||
|
||||
void lgMessage_process(void);
|
||||
|
||||
void lgMessage_post(const LGMsg * msg);
|
||||
|
||||
#endif
|
@ -9,10 +9,6 @@ include_directories(
|
||||
|
||||
add_definitions(-D_GNU_SOURCE)
|
||||
|
||||
if(ENABLE_BACKTRACE)
|
||||
add_definitions(-DENABLE_BACKTRACE)
|
||||
endif()
|
||||
|
||||
if (CMAKE_C_COMPILER_ID STREQUAL "Clang")
|
||||
add_compile_options(
|
||||
"-Wno-unknown-warning-option"
|
||||
@ -41,6 +37,10 @@ set(COMMON_SOURCES
|
||||
add_library(lg_common STATIC ${COMMON_SOURCES})
|
||||
target_link_libraries(lg_common lg_common_platform)
|
||||
|
||||
if(ENABLE_BACKTRACE)
|
||||
target_compile_definitions(lg_common PUBLIC -DENABLE_BACKTRACE)
|
||||
endif()
|
||||
|
||||
target_include_directories(lg_common
|
||||
INTERFACE
|
||||
include
|
||||
|
@ -56,14 +56,16 @@ typedef uint32_t KVMFRCursorFlags;
|
||||
|
||||
enum
|
||||
{
|
||||
KVMFR_FEATURE_SETCURSORPOS = 0x1
|
||||
KVMFR_FEATURE_SETCURSORPOS = 0x1,
|
||||
KVMFR_FEATURE_WINDOWSIZE = 0x2
|
||||
};
|
||||
|
||||
typedef uint32_t KVMFRFeatureFlags;
|
||||
|
||||
enum
|
||||
{
|
||||
KVMFR_MESSAGE_SETCURSORPOS
|
||||
KVMFR_MESSAGE_SETCURSORPOS,
|
||||
KVMFR_MESSAGE_WINDOWSIZE
|
||||
};
|
||||
|
||||
typedef uint32_t KVMFRMessageType;
|
||||
@ -176,6 +178,13 @@ typedef struct KVMFRSetCursorPos
|
||||
}
|
||||
KVMFRSetCursorPos;
|
||||
|
||||
typedef struct KVMFRWindowSize
|
||||
{
|
||||
KVMFRMessage msg;
|
||||
uint32_t w, h;
|
||||
}
|
||||
KVMFRWindowSize;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
@ -42,7 +42,7 @@ struct ll
|
||||
|
||||
struct ll * ll_new(void);
|
||||
void ll_free (struct ll * list);
|
||||
void ll_push (struct ll * list, void * data);
|
||||
bool ll_push (struct ll * list, void * data);
|
||||
bool ll_shift (struct ll * list, void ** data);
|
||||
bool ll_shift_nl (struct ll * list, void ** data);
|
||||
bool ll_peek_head (struct ll * list, void ** data);
|
||||
|
@ -48,13 +48,13 @@ void ll_free(struct ll * list)
|
||||
free(list);
|
||||
}
|
||||
|
||||
void ll_push(struct ll * list, void * data)
|
||||
bool ll_push(struct ll * list, void * data)
|
||||
{
|
||||
struct ll_item * item = malloc(sizeof(*item));
|
||||
if (!item)
|
||||
{
|
||||
DEBUG_ERROR("out of memory");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
item->data = data;
|
||||
@ -69,7 +69,7 @@ void ll_push(struct ll * list, void * data)
|
||||
list->head = item;
|
||||
list->tail = item;
|
||||
LG_UNLOCK(list->lock);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
item->prev = list->tail;
|
||||
@ -78,6 +78,7 @@ void ll_push(struct ll * list, void * data)
|
||||
list->tail = item;
|
||||
|
||||
LG_UNLOCK(list->lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ll_shift(struct ll * list, void ** data)
|
||||
|
@ -131,16 +131,16 @@ bool option_register(struct Option options[])
|
||||
for(int i = 0; options[i].type != OPTION_TYPE_NONE; ++i)
|
||||
++new;
|
||||
|
||||
state.options = realloc(
|
||||
void * tmp = realloc(
|
||||
state.options,
|
||||
sizeof(*state.options) * (state.oCount + new)
|
||||
);
|
||||
|
||||
if (!state.options)
|
||||
if (!tmp)
|
||||
{
|
||||
DEBUG_ERROR("out of memory");
|
||||
return false;
|
||||
}
|
||||
state.options = tmp;
|
||||
|
||||
for(int i = 0; options[i].type != OPTION_TYPE_NONE; ++i)
|
||||
{
|
||||
@ -229,15 +229,16 @@ bool option_register(struct Option options[])
|
||||
continue;
|
||||
|
||||
found = true;
|
||||
group->options = realloc(
|
||||
void * tmp = realloc(
|
||||
group->options,
|
||||
sizeof(*group->options) * (group->count + 1)
|
||||
);
|
||||
if (!group->options)
|
||||
if (!tmp)
|
||||
{
|
||||
DEBUG_ERROR("out of memory");
|
||||
return false;
|
||||
}
|
||||
group->options = tmp;
|
||||
group->options[group->count] = o;
|
||||
|
||||
int len = strlen(o->name);
|
||||
@ -250,15 +251,16 @@ bool option_register(struct Option options[])
|
||||
|
||||
if (!found)
|
||||
{
|
||||
state.groups = realloc(
|
||||
void * new = realloc(
|
||||
state.groups,
|
||||
sizeof(*state.groups) * (state.gCount + 1)
|
||||
);
|
||||
if (!state.groups)
|
||||
if (!new)
|
||||
{
|
||||
DEBUG_ERROR("out of memory");
|
||||
return false;
|
||||
}
|
||||
state.groups = new;
|
||||
|
||||
struct OptionGroup * group = &state.groups[state.gCount];
|
||||
++state.gCount;
|
||||
|
@ -164,7 +164,14 @@ static int dl_iterate_phdr_callback(struct dl_phdr_info * info, size_t size, voi
|
||||
ttl += hdr.p_memsz;
|
||||
}
|
||||
|
||||
crash.ranges = realloc(crash.ranges, sizeof(*crash.ranges) * (crash.rangeCount + 1));
|
||||
void * tmp = realloc(crash.ranges,
|
||||
sizeof(*crash.ranges) * (crash.rangeCount + 1));
|
||||
if (!tmp)
|
||||
{
|
||||
DEBUG_ERROR("out of memory");
|
||||
return 1;
|
||||
}
|
||||
crash.ranges = tmp;
|
||||
crash.ranges[crash.rangeCount].start = info->dlpi_addr;
|
||||
crash.ranges[crash.rangeCount].end = info->dlpi_addr + ttl;
|
||||
++crash.rangeCount;
|
||||
|
@ -626,12 +626,13 @@ static bool appendData(KVMFRUserData * dst, const void * src, const size_t size)
|
||||
if (size > dst->size - dst->used)
|
||||
{
|
||||
size_t newSize = dst->size + max(1024, size);
|
||||
dst->data = realloc(dst->data, newSize);
|
||||
if (!dst->data)
|
||||
void * tmp = realloc(dst->data, newSize);
|
||||
if (!tmp)
|
||||
{
|
||||
DEBUG_ERROR("Out of memory");
|
||||
return false;
|
||||
}
|
||||
dst->data = tmp;
|
||||
|
||||
memset(dst->data + dst->size, 0, newSize - dst->size);
|
||||
dst->size = newSize;
|
||||
|
204
idd/LGCommon/CDebug.cpp
Normal file
204
idd/LGCommon/CDebug.cpp
Normal file
@ -0,0 +1,204 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2025 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <Windows.h>
|
||||
#include <string>
|
||||
|
||||
#include <malloc.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
#include "CDebug.h"
|
||||
|
||||
CDebug g_debug;
|
||||
|
||||
void CDebug::Init(const char * name)
|
||||
{
|
||||
// don't redirect the debug output if running under a debugger
|
||||
if (IsDebuggerPresent())
|
||||
return;
|
||||
|
||||
// get the system temp directory
|
||||
char tempPath[MAX_PATH];
|
||||
DWORD pathLen = GetTempPathA(sizeof(tempPath), tempPath);
|
||||
if (pathLen == 0)
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "Failed to get the temp path");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string folder = tempPath;
|
||||
std::string baseName = name;
|
||||
std::string ext = ".txt";
|
||||
std::string logFile = folder + baseName + ext;
|
||||
|
||||
//rotate out old logs
|
||||
DeleteFileA((folder + baseName + ".4" + ext).c_str());
|
||||
for (int i = 3; i >= 0; --i)
|
||||
{
|
||||
std::string oldPath;
|
||||
std::string newPath;
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
oldPath = logFile;
|
||||
newPath = folder + baseName + ".1" + ext;
|
||||
}
|
||||
else
|
||||
{
|
||||
oldPath = folder + baseName + "." + std::to_string(i) + ext;
|
||||
newPath = folder + baseName + "." + std::to_string(i + 1) + ext;
|
||||
}
|
||||
|
||||
MoveFileA(oldPath.c_str(), newPath.c_str());
|
||||
}
|
||||
|
||||
/// open the new log file
|
||||
std::ofstream stream(logFile, std::ios::out | std::ios::trunc);
|
||||
if (!stream.is_open())
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "Failed to open the log file %s", logFile.c_str());
|
||||
return;
|
||||
}
|
||||
else
|
||||
DEBUG_INFO("Logging to: %s", logFile.c_str());
|
||||
|
||||
m_stream = std::move(stream);
|
||||
}
|
||||
|
||||
void CDebug::Log_va(CDebug::Level level, const char* function, int line, const char* fmt, va_list args)
|
||||
{
|
||||
if (level < 0 || level >= LEVEL_MAX)
|
||||
level = LEVEL_NONE;
|
||||
|
||||
static const char* fmtTemplate = "[%s] %40s:%-4d | ";
|
||||
const char* levelStr = m_levelStr[level];
|
||||
|
||||
int length = 0;
|
||||
length = _scprintf(fmtTemplate, levelStr, function, line);
|
||||
length += _vscprintf(fmt, args);
|
||||
length += 2;
|
||||
|
||||
/* Depending on the size of the format string, allocate space on the stack or the heap. */
|
||||
PCHAR buffer;
|
||||
buffer = (PCHAR)_malloca(length);
|
||||
if (!buffer)
|
||||
return;
|
||||
|
||||
/* Populate the buffer with the contents of the format string. */
|
||||
StringCbPrintfA(buffer, length, fmtTemplate, levelStr, function, line);
|
||||
|
||||
size_t offset = 0;
|
||||
StringCbLengthA(buffer, length, &offset);
|
||||
StringCbVPrintfA(&buffer[offset], length - offset, fmt, args);
|
||||
|
||||
buffer[length - 2] = '\n';
|
||||
buffer[length - 1] = '\0';
|
||||
|
||||
Write(buffer);
|
||||
|
||||
_freea(buffer);
|
||||
}
|
||||
|
||||
void CDebug::Log(CDebug::Level level, const char * function, int line, const char * fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Log_va(level, function, line, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void CDebug::LogHR(CDebug::Level level, HRESULT hr, const char * function, int line, const char * fmt, ...)
|
||||
{
|
||||
if (level < 0 || level >= LEVEL_MAX)
|
||||
level = LEVEL_NONE;
|
||||
|
||||
char * hrBuffer;
|
||||
if (!FormatMessageA(
|
||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
hr,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(char*)&hrBuffer,
|
||||
1024,
|
||||
NULL
|
||||
))
|
||||
{
|
||||
DEBUG_INFO("FormatMessage failed with code 0x%08x", GetLastError());
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Log_va(level, function, line, fmt, args);
|
||||
va_end(args);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove trailing CRLF in hrBuffer
|
||||
size_t len = strlen(hrBuffer);
|
||||
while (len && (hrBuffer[len - 1] == '\n' || hrBuffer[len - 1] == '\r'))
|
||||
hrBuffer[--len] = '\0';
|
||||
|
||||
static const char* fmtTemplate = "[%s] %40s:%-4d | ";
|
||||
const char* levelStr = m_levelStr[level];
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
int length = 0;
|
||||
length = _scprintf(fmtTemplate, levelStr, function, line);
|
||||
length += _vscprintf(fmt, args);
|
||||
length += 2 + 4 + (int)strlen(hrBuffer) + 1;
|
||||
|
||||
/* Depending on the size of the format string, allocate space on the stack or the heap. */
|
||||
PCHAR buffer;
|
||||
buffer = (PCHAR)_malloca(length);
|
||||
if (!buffer)
|
||||
{
|
||||
va_end(args);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Populate the buffer with the contents of the format string. */
|
||||
StringCbPrintfA(buffer, length, fmtTemplate, levelStr, function, line);
|
||||
|
||||
size_t offset = 0;
|
||||
StringCbLengthA(buffer, length, &offset);
|
||||
StringCbVPrintfA(&buffer[offset], length - offset, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
/* append the formatted error */
|
||||
StringCbLengthA(buffer, length, &offset);
|
||||
StringCbPrintfA(&buffer[offset], length - offset, " (%s)\n", hrBuffer);
|
||||
|
||||
Write(buffer);
|
||||
|
||||
_freea(buffer);
|
||||
LocalFree(hrBuffer);
|
||||
}
|
||||
|
||||
void CDebug::Write(const char * line)
|
||||
{
|
||||
if (m_stream.is_open())
|
||||
{
|
||||
m_stream << line;
|
||||
m_stream.flush();
|
||||
}
|
||||
else
|
||||
OutputDebugStringA(line);
|
||||
}
|
80
idd/LGCommon/CDebug.h
Normal file
80
idd/LGCommon/CDebug.h
Normal file
@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2025 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <fstream>
|
||||
|
||||
class CDebug
|
||||
{
|
||||
private:
|
||||
std::ofstream m_stream;
|
||||
void Write(const char * line);
|
||||
|
||||
public:
|
||||
|
||||
enum Level
|
||||
{
|
||||
LEVEL_NONE = 0,
|
||||
LEVEL_INFO,
|
||||
LEVEL_WARN,
|
||||
LEVEL_ERROR,
|
||||
LEVEL_TRACE,
|
||||
LEVEL_FIXME,
|
||||
LEVEL_FATAL,
|
||||
|
||||
LEVEL_MAX
|
||||
};
|
||||
|
||||
void Init(const char * name);
|
||||
void Log_va(CDebug::Level level, const char* function, int line, const char* fmt, va_list args);
|
||||
void Log(CDebug::Level level, const char * function, int line, const char * fmt, ...);
|
||||
void LogHR(CDebug::Level level, HRESULT hr, const char* function, int line, const char* fmt, ...);
|
||||
|
||||
private:
|
||||
const char* m_levelStr[LEVEL_MAX] =
|
||||
{
|
||||
" ",
|
||||
"I",
|
||||
"W",
|
||||
"E",
|
||||
"T",
|
||||
"!",
|
||||
"F"
|
||||
};
|
||||
};
|
||||
|
||||
extern CDebug g_debug;
|
||||
|
||||
#define DEBUG_INFO(fmt, ...) g_debug.Log(CDebug::LEVEL_INFO, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define DEBUG_WARN(fmt, ...) g_debug.Log(CDebug::LEVEL_WARN, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define DEBUG_ERROR(fmt, ...) g_debug.Log(CDebug::LEVEL_ERROR, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define DEBUG_TRACE(fmt, ...) g_debug.Log(CDebug::LEVEL_TRACE, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define DEBUG_FIXME(fmt, ...) g_debug.Log(CDebug::LEVEL_FIXME, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define DEBUG_FATAL(fmt, ...) g_debug.Log(CDebug::LEVEL_FATAL, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
|
||||
|
||||
#define DEBUG_INFO_HR(hr, fmt, ...) g_debug.LogHR(CDebug::LEVEL_INFO, hr, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define DEBUG_WARN_HR(hr, fmt, ...) g_debug.LogHR(CDebug::LEVEL_WARN, hr, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define DEBUG_ERROR_HR(hr, fmt, ...) g_debug.LogHR(CDebug::LEVEL_ERROR, hr, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define DEBUG_TRACE_HR(hr, fmt, ...) g_debug.LogHR(CDebug::LEVEL_TRACE, hr, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define DEBUG_FIXME_HR(hr, fmt, ...) g_debug.LogHR(CDebug::LEVEL_FIXME, hr, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
#define DEBUG_FATAL_HR(hr, fmt, ...) g_debug.LogHR(CDebug::LEVEL_FATAL, hr, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
|
51
idd/LGCommon/PipeMsg.h
Normal file
51
idd/LGCommon/PipeMsg.h
Normal file
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2025 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define LG_PIPE_NAME "\\\\.\\pipe\\LookingGlassIDD"
|
||||
|
||||
struct LGPipeMsg
|
||||
{
|
||||
unsigned size;
|
||||
enum
|
||||
{
|
||||
SETCURSORPOS,
|
||||
SETDISPLAYMODE
|
||||
}
|
||||
type;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint32_t x;
|
||||
uint32_t y;
|
||||
}
|
||||
curorPos;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
}
|
||||
displayMode;
|
||||
};
|
||||
};
|
@ -1,15 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.33423.256
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.5.33530.505
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LGIdd", "LGIdd\LGIdd.vcxproj", "{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{0045D7AD-3F26-4B87-81CB-78D18839596D} = {0045D7AD-3F26-4B87-81CB-78D18839596D}
|
||||
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65} = {FFCC376C-4D98-4B50-B431-E1BBC9C67E65}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LGMP", "..\repos\LGMP\LGMP.vcxproj", "{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LGIddHelper", "LGIddHelper\LGIddHelper.vcxproj", "{0045D7AD-3F26-4B87-81CB-78D18839596D}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LGCommon", "LGCommon", "{ACB90E34-01CA-4B86-813B-3D20904994C6}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
LGCommon\CDebug.cpp = LGCommon\CDebug.cpp
|
||||
LGCommon\CDebug.h = LGCommon\CDebug.h
|
||||
LGCommon\PipeMsg.h = LGCommon\PipeMsg.h
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|ARM = Debug|ARM
|
||||
@ -50,7 +60,6 @@ Global
|
||||
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Debug|ARM64.ActiveCfg = Debug|Win32
|
||||
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Debug|x64.Build.0 = Debug|x64
|
||||
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Debug|x64.Deploy.0 = Debug|x64
|
||||
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Debug|x86.Build.0 = Debug|Win32
|
||||
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Debug|x86.Deploy.0 = Debug|Win32
|
||||
@ -58,10 +67,25 @@ Global
|
||||
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release|ARM64.ActiveCfg = Release|Win32
|
||||
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release|x64.ActiveCfg = Release|x64
|
||||
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release|x64.Build.0 = Release|x64
|
||||
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release|x64.Deploy.0 = Release|x64
|
||||
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release|x86.ActiveCfg = Release|Win32
|
||||
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release|x86.Build.0 = Release|Win32
|
||||
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release|x86.Deploy.0 = Release|Win32
|
||||
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|ARM.ActiveCfg = Debug|x64
|
||||
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|ARM.Build.0 = Debug|x64
|
||||
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|ARM64.ActiveCfg = Debug|x64
|
||||
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|ARM64.Build.0 = Debug|x64
|
||||
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|x64.Build.0 = Debug|x64
|
||||
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|x86.Build.0 = Debug|Win32
|
||||
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|ARM.ActiveCfg = Release|x64
|
||||
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|ARM.Build.0 = Release|x64
|
||||
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|ARM64.ActiveCfg = Release|x64
|
||||
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|ARM64.Build.0 = Release|x64
|
||||
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|x64.ActiveCfg = Release|x64
|
||||
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|x64.Build.0 = Release|x64
|
||||
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|x86.ActiveCfg = Release|Win32
|
||||
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -18,9 +18,10 @@
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "Direct3DDevice.h"
|
||||
#include "CD3D11Device.h"
|
||||
#include "CDebug.h"
|
||||
|
||||
HRESULT Direct3DDevice::Init()
|
||||
HRESULT CD3D11Device::Init()
|
||||
{
|
||||
HRESULT hr = CreateDXGIFactory2(0, IID_PPV_ARGS(&m_factory));
|
||||
if (FAILED(hr))
|
||||
@ -29,18 +30,37 @@ HRESULT Direct3DDevice::Init()
|
||||
hr = m_factory->EnumAdapterByLuid(m_adapterLuid, IID_PPV_ARGS(&m_adapter));
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
// only 11.1 supports DX12 interoperabillity
|
||||
static const D3D_FEATURE_LEVEL featureLevels[] =
|
||||
{
|
||||
D3D_FEATURE_LEVEL_11_1
|
||||
};
|
||||
D3D_FEATURE_LEVEL featureLevel;
|
||||
|
||||
ComPtr<ID3D11Device> device;
|
||||
ComPtr<ID3D11DeviceContext> context;
|
||||
hr = D3D11CreateDevice(
|
||||
m_adapter.Get(),
|
||||
D3D_DRIVER_TYPE_UNKNOWN,
|
||||
nullptr,
|
||||
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
|
||||
nullptr,
|
||||
0,
|
||||
featureLevels,
|
||||
ARRAYSIZE(featureLevels),
|
||||
D3D11_SDK_VERSION,
|
||||
&m_device,
|
||||
nullptr,
|
||||
&m_context);
|
||||
&device,
|
||||
&featureLevel,
|
||||
&context);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
DEBUG_INFO("Feature Level: 0x%x", featureLevel);
|
||||
|
||||
hr = device.As(&m_device);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
hr = context.As(&m_context);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
@ -26,21 +26,29 @@
|
||||
#include <dxgi1_5.h>
|
||||
#include <d3d11_4.h>
|
||||
|
||||
struct Direct3DDevice
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
struct CD3D11Device
|
||||
{
|
||||
Direct3DDevice(LUID adapterLuid) :
|
||||
private:
|
||||
LUID m_adapterLuid;
|
||||
ComPtr<IDXGIFactory5 > m_factory;
|
||||
ComPtr<IDXGIAdapter1 > m_adapter;
|
||||
ComPtr<ID3D11Device5 > m_device;
|
||||
ComPtr<ID3D11DeviceContext4> m_context;
|
||||
|
||||
public:
|
||||
CD3D11Device(LUID adapterLuid) :
|
||||
m_adapterLuid(adapterLuid) {};
|
||||
|
||||
Direct3DDevice()
|
||||
CD3D11Device()
|
||||
{
|
||||
m_adapterLuid = LUID{};
|
||||
}
|
||||
|
||||
HRESULT Init();
|
||||
|
||||
LUID m_adapterLuid;
|
||||
Microsoft::WRL::ComPtr<IDXGIFactory5 > m_factory;
|
||||
Microsoft::WRL::ComPtr<IDXGIAdapter1 > m_adapter;
|
||||
Microsoft::WRL::ComPtr<ID3D11Device > m_device;
|
||||
Microsoft::WRL::ComPtr<ID3D11DeviceContext> m_context;
|
||||
ComPtr<ID3D11Device5> GetDevice() { return m_device; }
|
||||
|
||||
ComPtr<ID3D11DeviceContext4> GetContext() { return m_context; }
|
||||
};
|
163
idd/LGIdd/CD3D12CommandQueue.cpp
Normal file
163
idd/LGIdd/CD3D12CommandQueue.cpp
Normal file
@ -0,0 +1,163 @@
|
||||
#include "CD3D12CommandQueue.h"
|
||||
#include "CDebug.h"
|
||||
|
||||
bool CD3D12CommandQueue::Init(ID3D12Device3 * device, D3D12_COMMAND_LIST_TYPE type, const WCHAR* name, CallbackMode callbackMode)
|
||||
{
|
||||
HRESULT hr;
|
||||
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
|
||||
|
||||
queueDesc.Type = type;
|
||||
queueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_HIGH;
|
||||
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
||||
|
||||
hr = device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_queue));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to create the CommandQueue (%ls)", name);
|
||||
return false;
|
||||
}
|
||||
m_queue->SetName(name);
|
||||
|
||||
hr = device->CreateCommandAllocator(type, IID_PPV_ARGS(&m_allocator));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to create the CommandAllocator (%ls)", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = device->CreateCommandList(0, type, m_allocator.Get(), NULL, IID_PPV_ARGS(&m_gfxList));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to create the Graphics CommandList (%ls)", name);
|
||||
return false;
|
||||
}
|
||||
m_gfxList->SetName(name);
|
||||
|
||||
m_cmdList = m_gfxList;
|
||||
if (!m_cmdList)
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to get the CommandList (%ls)", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to create the Fence (%ls)", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_event.Attach(CreateEvent(NULL, FALSE, FALSE, NULL));
|
||||
if (m_event.Get() == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "Failed to create the completion event (%ls)", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (callbackMode != DISABLED)
|
||||
{
|
||||
ULONG flags = (callbackMode == FAST) ?
|
||||
WT_EXECUTEINWAITTHREAD : WT_EXECUTEINPERSISTENTTHREAD;
|
||||
|
||||
RegisterWaitForSingleObject(
|
||||
&m_waitHandle,
|
||||
m_event.Get(),
|
||||
[](PVOID param, BOOLEAN timeout){
|
||||
CD3D12CommandQueue * queue = (CD3D12CommandQueue*)param;
|
||||
if (timeout)
|
||||
queue->m_completionResult = false;
|
||||
queue->OnCompletion();
|
||||
},
|
||||
this,
|
||||
INFINITE,
|
||||
flags);
|
||||
}
|
||||
|
||||
m_name = name;
|
||||
m_fenceValue = 0;
|
||||
DEBUG_INFO("Created CD3D12CommandQueue(%ls)", name);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CD3D12CommandQueue::DeInit()
|
||||
{
|
||||
if (m_waitHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
UnregisterWait(m_waitHandle);
|
||||
m_waitHandle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
bool CD3D12CommandQueue::Execute()
|
||||
{
|
||||
m_needsReset = true;
|
||||
m_completionResult = true;
|
||||
|
||||
HRESULT hr = m_gfxList->Close();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
m_completionResult = false;
|
||||
SetEvent(m_event.Get());
|
||||
|
||||
DEBUG_ERROR_HR(hr, "Failed to close the command list (%ls)", m_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
ID3D12CommandList * lists[] = { m_cmdList.Get() };
|
||||
m_queue->ExecuteCommandLists(1, lists);
|
||||
++m_fenceValue;
|
||||
|
||||
hr = m_fence->SetEventOnCompletion(m_fenceValue, m_event.Get());
|
||||
if (FAILED(hr))
|
||||
{
|
||||
m_completionResult = false;
|
||||
SetEvent(m_event.Get());
|
||||
|
||||
DEBUG_ERROR_HR(hr, "Failed to set the fence signal (%ls)", m_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_pending = true;
|
||||
m_queue->Signal(m_fence.Get(), m_fenceValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
void CD3D12CommandQueue::Wait()
|
||||
{
|
||||
if (m_fence->GetCompletedValue() >= m_fenceValue)
|
||||
{
|
||||
m_pending = false;
|
||||
return;
|
||||
}
|
||||
|
||||
m_fence->SetEventOnCompletion(m_fenceValue, m_event.Get());
|
||||
WaitForSingleObject(m_event.Get(), INFINITE);
|
||||
m_pending = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool CD3D12CommandQueue::Reset()
|
||||
{
|
||||
if (!m_needsReset)
|
||||
return true;
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
hr = m_allocator->Reset();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to reset the command allocator (%ls)", m_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = m_gfxList->Reset(m_allocator.Get(), NULL);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to reset the graphics command list (%ls)", m_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_needsReset = false;
|
||||
return true;
|
||||
}
|
78
idd/LGIdd/CD3D12CommandQueue.h
Normal file
78
idd/LGIdd/CD3D12CommandQueue.h
Normal file
@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <wdf.h>
|
||||
#include <wrl.h>
|
||||
#include <d3d12.h>
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace Microsoft::WRL::Wrappers;
|
||||
using namespace Microsoft::WRL::Wrappers::HandleTraits;
|
||||
|
||||
class CD3D12CommandQueue
|
||||
{
|
||||
private:
|
||||
const WCHAR * m_name = nullptr;
|
||||
|
||||
ComPtr<ID3D12CommandQueue > m_queue;
|
||||
ComPtr<ID3D12CommandAllocator > m_allocator;
|
||||
ComPtr<ID3D12GraphicsCommandList> m_gfxList;
|
||||
ComPtr<ID3D12CommandList > m_cmdList;
|
||||
ComPtr<ID3D12Fence > m_fence;
|
||||
|
||||
bool m_pending = false;
|
||||
HandleT<HANDLENullTraits> m_event;
|
||||
HANDLE m_waitHandle = INVALID_HANDLE_VALUE;
|
||||
UINT64 m_fenceValue = 0;
|
||||
bool m_needsReset = false;
|
||||
|
||||
typedef void (*CompletionFunction)(CD3D12CommandQueue * queue,
|
||||
bool result, void * param1, void * param2);
|
||||
|
||||
CompletionFunction m_completionCallback = nullptr;
|
||||
void * m_completionParams[2];
|
||||
bool m_completionResult = true;
|
||||
|
||||
void OnCompletion()
|
||||
{
|
||||
if (m_completionCallback)
|
||||
m_completionCallback(
|
||||
this,
|
||||
m_completionResult,
|
||||
m_completionParams[0],
|
||||
m_completionParams[1]);
|
||||
m_pending = false;
|
||||
}
|
||||
|
||||
public:
|
||||
~CD3D12CommandQueue() { DeInit(); }
|
||||
|
||||
enum CallbackMode
|
||||
{
|
||||
DISABLED, // no callbacks
|
||||
FAST, // callback is expected to return almost immediately
|
||||
NORMAL // normal callback
|
||||
};
|
||||
|
||||
bool Init(ID3D12Device3 * device, D3D12_COMMAND_LIST_TYPE type, const WCHAR * name,
|
||||
CallbackMode callbackMode = DISABLED);
|
||||
|
||||
void DeInit();
|
||||
|
||||
void SetCompletionCallback(CompletionFunction fn, void * param1, void * param2)
|
||||
{
|
||||
m_completionCallback = fn;
|
||||
m_completionParams[0] = param1;
|
||||
m_completionParams[1] = param2;
|
||||
}
|
||||
|
||||
bool Reset();
|
||||
bool Execute();
|
||||
|
||||
//void Wait();
|
||||
bool IsReady () { return !m_pending; }
|
||||
HANDLE GetEvent() { return m_event.Get(); }
|
||||
|
||||
ComPtr<ID3D12CommandQueue > GetCmdQueue() { return m_queue; }
|
||||
ComPtr<ID3D12GraphicsCommandList> GetGfxList() { return m_gfxList; }
|
||||
};
|
195
idd/LGIdd/CD3D12Device.cpp
Normal file
195
idd/LGIdd/CD3D12Device.cpp
Normal file
@ -0,0 +1,195 @@
|
||||
#include "CD3D12Device.h"
|
||||
#include "CDebug.h"
|
||||
|
||||
bool CD3D12Device::m_indirectCopy = false;
|
||||
|
||||
CD3D12Device::CD3D12Device(LUID adapterLuid) :
|
||||
m_adapterLuid(adapterLuid),
|
||||
m_debug(false)
|
||||
{
|
||||
if (m_debug)
|
||||
{
|
||||
HRESULT hr = D3D12GetDebugInterface(IID_PPV_ARGS(&m_dxDebug));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to get the debug interface");
|
||||
return;
|
||||
}
|
||||
|
||||
m_dxDebug->EnableDebugLayer();
|
||||
m_dxDebug->SetEnableGPUBasedValidation(TRUE);
|
||||
m_dxDebug->SetEnableSynchronizedCommandQueueValidation(TRUE);
|
||||
m_dxDebug->SetForceLegacyBarrierValidation(TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
static void CALLBACK _D3D12DebugCallback(
|
||||
D3D12_MESSAGE_CATEGORY category,
|
||||
D3D12_MESSAGE_SEVERITY severity,
|
||||
D3D12_MESSAGE_ID id,
|
||||
LPCSTR description,
|
||||
void *context
|
||||
)
|
||||
{
|
||||
(void)context;
|
||||
|
||||
DEBUG_INFO("category:%d severity:%d id:%d desc:%s",
|
||||
category,
|
||||
severity,
|
||||
id,
|
||||
description);
|
||||
}
|
||||
|
||||
CD3D12Device::InitResult CD3D12Device::Init(CIVSHMEM &ivshmem, UINT64 &alignSize)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
hr = CreateDXGIFactory2(m_debug ? DXGI_CREATE_FACTORY_DEBUG : 0, IID_PPV_ARGS(&m_factory));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to create the DXGI factory");
|
||||
return InitResult::FAILURE;
|
||||
}
|
||||
|
||||
hr = m_factory->EnumAdapterByLuid(m_adapterLuid, IID_PPV_ARGS(&m_adapter));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to enumerate the adapter");
|
||||
return InitResult::FAILURE;
|
||||
}
|
||||
|
||||
hr = D3D12CreateDevice(m_adapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&m_device));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to create the DirectX12 device");
|
||||
return InitResult::FAILURE;
|
||||
}
|
||||
|
||||
if (m_debug)
|
||||
{
|
||||
hr = m_device.As(&m_infoQueue);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_WARN_HR(hr, "Failed to get the ID3D12InfoQueue1 interface");
|
||||
//non-fatal do not exit
|
||||
}
|
||||
else
|
||||
{
|
||||
m_infoQueue->RegisterMessageCallback(
|
||||
_D3D12DebugCallback, D3D12_MESSAGE_CALLBACK_FLAG_NONE, NULL, &m_callbackCookie);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_indirectCopy)
|
||||
{
|
||||
hr = m_device->OpenExistingHeapFromAddress(ivshmem.GetMem(), IID_PPV_ARGS(&m_ivshmemHeap));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to open IVSHMEM as a D3D12Heap");
|
||||
m_indirectCopy = true;
|
||||
return InitResult::RETRY;
|
||||
}
|
||||
m_ivshmemHeap->SetName(L"IVSHMEM");
|
||||
|
||||
D3D12_HEAP_DESC heapDesc = m_ivshmemHeap->GetDesc();
|
||||
alignSize = heapDesc.Alignment;
|
||||
|
||||
// test that the heap is usable
|
||||
if (!HeapTest())
|
||||
{
|
||||
DEBUG_WARN("Unable to create resources in the IVSHMEM heap, falling back to indirect copy");
|
||||
|
||||
// failure often results in the device being removed and we need to completely reinit when this occurs
|
||||
m_indirectCopy = true;
|
||||
return InitResult::RETRY;
|
||||
}
|
||||
|
||||
DEBUG_INFO("Using IVSHMEM as a D3D12Heap");
|
||||
}
|
||||
|
||||
for(int i = 0; i < ARRAYSIZE(m_copyQueue); ++i)
|
||||
if (!m_copyQueue[i].Init(m_device.Get(), D3D12_COMMAND_LIST_TYPE_COPY, L"Copy",
|
||||
m_indirectCopy ? CD3D12CommandQueue::NORMAL : CD3D12CommandQueue::FAST))
|
||||
return InitResult::FAILURE;
|
||||
|
||||
//if (!m_computeQueue.Init(m_device.Get(), D3D12_COMMAND_LIST_TYPE_COMPUTE, L"Compute"))
|
||||
//return InitResult::FAILURE;
|
||||
|
||||
DEBUG_INFO("Created CD3D12Device");
|
||||
return InitResult::SUCCESS;
|
||||
}
|
||||
|
||||
void CD3D12Device::DeInit()
|
||||
{
|
||||
if (m_debug && m_infoQueue)
|
||||
m_infoQueue->UnregisterMessageCallback(m_callbackCookie);
|
||||
m_infoQueue.Reset();
|
||||
}
|
||||
|
||||
bool CD3D12Device::HeapTest()
|
||||
{
|
||||
D3D12_RESOURCE_DESC desc = {};
|
||||
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
|
||||
desc.Width = 1048576;
|
||||
desc.Height = 1;
|
||||
desc.DepthOrArraySize = 1;
|
||||
desc.MipLevels = 1;
|
||||
desc.Format = DXGI_FORMAT_UNKNOWN;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.SampleDesc.Quality = 0;
|
||||
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER;
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
ComPtr<ID3D12Resource> resource;
|
||||
hr = m_device->CreatePlacedResource(
|
||||
m_ivshmemHeap.Get(),
|
||||
0,
|
||||
&desc,
|
||||
D3D12_RESOURCE_STATE_COPY_DEST,
|
||||
NULL,
|
||||
IID_PPV_ARGS(&resource));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to create the ivshmem ID3D12Resource");
|
||||
return false;
|
||||
}
|
||||
resource->SetName(L"HeapTest");
|
||||
|
||||
/* the above may succeed even if there was a fault, as such we also need to check
|
||||
* check if the device was removed */
|
||||
hr = m_device->GetDeviceRemovedReason();
|
||||
if (hr != S_OK)
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Device Removed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CD3D12CommandQueue * CD3D12Device::GetCopyQueue()
|
||||
{
|
||||
// try for up to a maximum of 100ms to find a free copy queue
|
||||
for (int c = 0; c < 100; ++c)
|
||||
{
|
||||
for (int i = 0; i < ARRAYSIZE(m_copyQueue); ++i)
|
||||
{
|
||||
auto& queue = m_copyQueue[m_copyQueueIndex++];
|
||||
if (m_copyQueueIndex == ARRAYSIZE(m_copyQueue))
|
||||
m_copyQueueIndex = 0;
|
||||
|
||||
if (queue.IsReady())
|
||||
{
|
||||
queue.Reset();
|
||||
return &queue;
|
||||
}
|
||||
}
|
||||
Sleep(1);
|
||||
}
|
||||
|
||||
DEBUG_ERROR("Failed to get a copy queue");
|
||||
return nullptr;
|
||||
}
|
58
idd/LGIdd/CD3D12Device.h
Normal file
58
idd/LGIdd/CD3D12Device.h
Normal file
@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <wdf.h>
|
||||
#include <wrl.h>
|
||||
#include <dxgi1_5.h>
|
||||
#include <d3d12.h>
|
||||
|
||||
#include "CIVSHMEM.h"
|
||||
#include "CD3D12CommandQueue.h"
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
struct CD3D12Device
|
||||
{
|
||||
private:
|
||||
LUID m_adapterLuid;
|
||||
bool m_debug;
|
||||
|
||||
// static as this needs to persist if set
|
||||
static bool m_indirectCopy;
|
||||
|
||||
ComPtr<ID3D12Debug6 > m_dxDebug;
|
||||
ComPtr<ID3D12InfoQueue1> m_infoQueue;
|
||||
DWORD m_callbackCookie;
|
||||
|
||||
ComPtr<IDXGIFactory5> m_factory;
|
||||
ComPtr<IDXGIAdapter1> m_adapter;
|
||||
ComPtr<ID3D12Device3> m_device;
|
||||
ComPtr<ID3D12Heap > m_ivshmemHeap;
|
||||
|
||||
CD3D12CommandQueue m_copyQueue[4];
|
||||
unsigned m_copyQueueIndex = 0;
|
||||
CD3D12CommandQueue m_computeQueue;
|
||||
|
||||
bool HeapTest();
|
||||
|
||||
public:
|
||||
CD3D12Device(LUID adapterLUID);
|
||||
~CD3D12Device() { DeInit(); }
|
||||
|
||||
enum InitResult
|
||||
{
|
||||
RETRY,
|
||||
FAILURE,
|
||||
SUCCESS
|
||||
};
|
||||
|
||||
InitResult Init(CIVSHMEM &ivshmem, UINT64 &alignSize);
|
||||
void DeInit();
|
||||
|
||||
ComPtr<ID3D12Device3> GetDevice() { return m_device; }
|
||||
ComPtr<ID3D12Heap > GetHeap() { return m_ivshmemHeap; }
|
||||
bool IsIndirectCopy() { return m_indirectCopy; }
|
||||
|
||||
CD3D12CommandQueue * GetCopyQueue();
|
||||
CD3D12CommandQueue & GetComputeQueue() { return m_computeQueue; }
|
||||
};
|
33
idd/LGIdd/CFrameBufferPool.cpp
Normal file
33
idd/LGIdd/CFrameBufferPool.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
#include "CFrameBufferPool.h"
|
||||
#include "CSwapChainProcessor.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void CFrameBufferPool::Init(CSwapChainProcessor * swapChain)
|
||||
{
|
||||
m_swapChain = swapChain;
|
||||
}
|
||||
|
||||
void CFrameBufferPool::Reset()
|
||||
{
|
||||
for (int i = 0; i < ARRAYSIZE(m_buffers); ++i)
|
||||
m_buffers[i].Reset();
|
||||
}
|
||||
|
||||
CFrameBufferResource * CFrameBufferPool::Get(
|
||||
const CIndirectDeviceContext::PreparedFrameBuffer& buffer,
|
||||
size_t minSize)
|
||||
{
|
||||
if (buffer.frameIndex > ARRAYSIZE(m_buffers) - 1)
|
||||
return nullptr;
|
||||
|
||||
CFrameBufferResource* fbr = &m_buffers[buffer.frameIndex];
|
||||
if (!fbr->IsValid() || fbr->GetBase() != buffer.mem || fbr->GetSize() < minSize)
|
||||
{
|
||||
fbr->Reset();
|
||||
if (!fbr->Init(m_swapChain, buffer.frameIndex, buffer.mem, minSize))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return fbr;
|
||||
}
|
22
idd/LGIdd/CFrameBufferPool.h
Normal file
22
idd/LGIdd/CFrameBufferPool.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "CFrameBufferResource.h"
|
||||
#include "CIndirectDeviceContext.h"
|
||||
#include "common/KVMFR.h"
|
||||
|
||||
//class CSwapChainProcessor;
|
||||
|
||||
class CFrameBufferPool
|
||||
{
|
||||
CSwapChainProcessor * m_swapChain;
|
||||
|
||||
CFrameBufferResource m_buffers[LGMP_Q_FRAME_LEN];
|
||||
|
||||
public:
|
||||
void Init(CSwapChainProcessor * swapChain);
|
||||
void Reset();
|
||||
|
||||
CFrameBufferResource* CFrameBufferPool::Get(
|
||||
const CIndirectDeviceContext::PreparedFrameBuffer& buffer,
|
||||
size_t minSize);
|
||||
};
|
114
idd/LGIdd/CFrameBufferResource.cpp
Normal file
114
idd/LGIdd/CFrameBufferResource.cpp
Normal file
@ -0,0 +1,114 @@
|
||||
#include "CFrameBufferResource.h"
|
||||
#include "CSwapChainProcessor.h"
|
||||
#include "CDebug.h"
|
||||
|
||||
bool CFrameBufferResource::Init(CSwapChainProcessor * swapChain, unsigned frameIndex, uint8_t * base, size_t size)
|
||||
{
|
||||
m_frameIndex = frameIndex;
|
||||
|
||||
if (size > swapChain->GetDevice()->GetMaxFrameSize())
|
||||
{
|
||||
DEBUG_ERROR("Frame size of %lu is too large to fit in available shared ram");
|
||||
return false;
|
||||
}
|
||||
|
||||
// nothing to do if the resource already exists and is large enough
|
||||
if (m_base == base && m_size >= size)
|
||||
{
|
||||
m_frameSize = size;
|
||||
return true;
|
||||
}
|
||||
|
||||
Reset();
|
||||
|
||||
D3D12_RESOURCE_DESC desc = {};
|
||||
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
desc.Width = size;
|
||||
desc.Height = 1;
|
||||
desc.DepthOrArraySize = 1;
|
||||
desc.MipLevels = 1;
|
||||
desc.Format = DXGI_FORMAT_UNKNOWN;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.SampleDesc.Quality = 0;
|
||||
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
||||
|
||||
HRESULT hr;
|
||||
const WCHAR * resName;
|
||||
|
||||
if (swapChain->GetD3D12Device()->IsIndirectCopy())
|
||||
{
|
||||
DEBUG_TRACE("Creating standard resource for %p", base);
|
||||
D3D12_HEAP_PROPERTIES heapProps = {};
|
||||
heapProps.Type = D3D12_HEAP_TYPE_READBACK;
|
||||
heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
heapProps.CreationNodeMask = 1;
|
||||
heapProps.VisibleNodeMask = 1;
|
||||
|
||||
hr = swapChain->GetD3D12Device()->GetDevice()->CreateCommittedResource(
|
||||
&heapProps,
|
||||
D3D12_HEAP_FLAG_NONE,
|
||||
&desc,
|
||||
D3D12_RESOURCE_STATE_COPY_DEST,
|
||||
NULL,
|
||||
IID_PPV_ARGS(&m_res)
|
||||
);
|
||||
resName = L"STAGING";
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
D3D12_RANGE range = {0, 0};
|
||||
hr = m_res->Map(0, &range, &m_map);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to map the resource");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_TRACE("Creating ivshmem resource for %p", base);
|
||||
desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
|
||||
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER;
|
||||
|
||||
hr = swapChain->GetD3D12Device()->GetDevice()->CreatePlacedResource(
|
||||
swapChain->GetD3D12Device()->GetHeap().Get(),
|
||||
(uintptr_t)base - (uintptr_t)swapChain->GetDevice()->GetIVSHMEM().GetMem(),
|
||||
&desc,
|
||||
D3D12_RESOURCE_STATE_COPY_DEST,
|
||||
NULL,
|
||||
IID_PPV_ARGS(&m_res)
|
||||
);
|
||||
resName = L"IVSHMEM";
|
||||
}
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to create the FrameBuffer ID3D12Resource");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_res->SetName(resName);
|
||||
|
||||
m_base = base;
|
||||
m_size = size;
|
||||
m_frameSize = size;
|
||||
m_valid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CFrameBufferResource::Reset()
|
||||
{
|
||||
if (m_map)
|
||||
{
|
||||
m_res->Unmap(0, NULL);
|
||||
m_map = NULL;
|
||||
}
|
||||
|
||||
m_base = nullptr;
|
||||
m_size = 0;
|
||||
m_res.Reset();
|
||||
m_valid = false;
|
||||
}
|
36
idd/LGIdd/CFrameBufferResource.h
Normal file
36
idd/LGIdd/CFrameBufferResource.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <wdf.h>
|
||||
#include <wrl.h>
|
||||
#include <d3d12.h>
|
||||
#include <stdint.h>
|
||||
|
||||
class CSwapChainProcessor;
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
class CFrameBufferResource
|
||||
{
|
||||
private:
|
||||
bool m_valid;
|
||||
unsigned m_frameIndex;
|
||||
uint8_t * m_base;
|
||||
size_t m_size;
|
||||
size_t m_frameSize;
|
||||
ComPtr<ID3D12Resource> m_res;
|
||||
void * m_map;
|
||||
|
||||
public:
|
||||
bool Init(CSwapChainProcessor * swapChain, unsigned frameIndex, uint8_t * base, size_t size);
|
||||
void Reset();
|
||||
|
||||
bool IsValid() { return m_valid; }
|
||||
unsigned GetFrameIndex() { return m_frameIndex; }
|
||||
uint8_t * GetBase() { return m_base; }
|
||||
size_t GetSize() { return m_size; }
|
||||
size_t GetFrameSize() { return m_frameSize; }
|
||||
void * GetMap() { return m_map; }
|
||||
|
||||
ComPtr<ID3D12Resource> Get() { return m_res; }
|
||||
};
|
@ -25,7 +25,7 @@
|
||||
#include <algorithm>
|
||||
#include <winioctl.h>
|
||||
|
||||
#include "Debug.h"
|
||||
#include "CDebug.h"
|
||||
#include "ivshmem/ivshmem.h"
|
||||
|
||||
CIVSHMEM::CIVSHMEM()
|
||||
@ -72,10 +72,12 @@ bool CIVSHMEM::Init()
|
||||
m_devices.push_back(data);
|
||||
}
|
||||
|
||||
if (GetLastError() != ERROR_NO_MORE_ITEMS)
|
||||
HRESULT hr = GetLastError();
|
||||
if (hr != ERROR_NO_MORE_ITEMS)
|
||||
{
|
||||
m_devices.clear();
|
||||
SetupDiDestroyDeviceInfoList(devInfoSet);
|
||||
DEBUG_ERROR_HR(hr, "Enumeration Failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -102,7 +104,7 @@ bool CIVSHMEM::Init()
|
||||
{
|
||||
DWORD bus = it->busAddr >> 32;
|
||||
DWORD addr = it->busAddr & 0xFFFFFFFF;
|
||||
DBGPRINT("IVSHMEM %u%c on bus 0x%lx, device 0x%lx, function 0x%lx\n",
|
||||
DEBUG_INFO("IVSHMEM %u%c on bus 0x%lx, device 0x%lx, function 0x%lx",
|
||||
i, i == shmDevice ? '*' : ' ', bus, addr >> 16, addr & 0xFFFF);
|
||||
|
||||
if (i == shmDevice)
|
||||
@ -111,12 +113,14 @@ bool CIVSHMEM::Init()
|
||||
|
||||
if (!device)
|
||||
{
|
||||
DEBUG_ERROR("Failed to match a shmDevice");
|
||||
SetupDiDestroyDeviceInfoList(devInfoSet);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SetupDiEnumDeviceInterfaces(devInfoSet, &devInfoData, &GUID_DEVINTERFACE_IVSHMEM, 0, &devInterfaceData) == FALSE)
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "SetupDiEnumDeviceInterfaces");
|
||||
SetupDiDestroyDeviceInfoList(devInfoSet);
|
||||
return false;
|
||||
}
|
||||
@ -125,6 +129,7 @@ bool CIVSHMEM::Init()
|
||||
SetupDiGetDeviceInterfaceDetail(devInfoSet, &devInterfaceData, nullptr, 0, &reqSize, nullptr);
|
||||
if (!reqSize)
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "SetupDiGetDeviceInterfaceDetail");
|
||||
SetupDiDestroyDeviceInfoList(devInfoSet);
|
||||
return false;
|
||||
}
|
||||
@ -133,6 +138,7 @@ bool CIVSHMEM::Init()
|
||||
infData->cbSize = sizeof(PSP_DEVICE_INTERFACE_DETAIL_DATA);
|
||||
if (!SetupDiGetDeviceInterfaceDetail(devInfoSet, &devInterfaceData, infData, reqSize, nullptr, nullptr))
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "SetupDiGetDeviceInterfaceDetail");
|
||||
SetupDiDestroyDeviceInfoList(devInfoSet);
|
||||
return false;
|
||||
}
|
||||
@ -140,11 +146,13 @@ bool CIVSHMEM::Init()
|
||||
m_handle = CreateFile(infData->DevicePath, 0, 0, nullptr, OPEN_EXISTING, 0, 0);
|
||||
if (m_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "CreateFile");
|
||||
SetupDiDestroyDeviceInfoList(devInfoSet);
|
||||
return false;
|
||||
}
|
||||
|
||||
SetupDiDestroyDeviceInfoList(devInfoSet);
|
||||
DEBUG_TRACE("IVSHMEM Initialized");
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -153,7 +161,7 @@ bool CIVSHMEM::Open()
|
||||
IVSHMEM_SIZE size;
|
||||
if (!DeviceIoControl(m_handle, IOCTL_IVSHMEM_REQUEST_SIZE, nullptr, 0, &size, sizeof(size), nullptr, nullptr))
|
||||
{
|
||||
DBGPRINT("Failed to request ivshmem size");
|
||||
DEBUG_ERROR_HR(GetLastError(), "Failed to request ivshmem size");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -163,7 +171,7 @@ bool CIVSHMEM::Open()
|
||||
config.cacheMode = IVSHMEM_CACHE_WRITECOMBINED;
|
||||
if (!DeviceIoControl(m_handle, IOCTL_IVSHMEM_REQUEST_MMAP, &config, sizeof(config), &map, sizeof(map), nullptr, nullptr))
|
||||
{
|
||||
DBGPRINT("Failed to request ivshmem mmap");
|
||||
DEBUG_ERROR_HR(GetLastError(), "Failed to request ivshmem mmap");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -180,7 +188,7 @@ void CIVSHMEM::Close()
|
||||
|
||||
if (!DeviceIoControl(m_handle, IOCTL_IVSHMEM_RELEASE_MMAP, nullptr, 0, nullptr, 0, nullptr, nullptr))
|
||||
{
|
||||
DBGPRINT("Failed to release ivshmem mmap");
|
||||
DEBUG_ERROR("Failed to release ivshmem mmap");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,9 @@
|
||||
#include "CIndirectMonitorContext.h"
|
||||
|
||||
#include "CPlatformInfo.h"
|
||||
#include "Debug.h"
|
||||
#include "CPipeServer.h"
|
||||
#include "CDebug.h"
|
||||
#include "VersionInfo.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
@ -40,31 +42,58 @@ static const struct LGMPQueueConfig POINTER_QUEUE_CONFIG =
|
||||
1000 //subTimeout
|
||||
};
|
||||
|
||||
CIndirectDeviceContext::~CIndirectDeviceContext()
|
||||
const DWORD DefaultDisplayModes[][3] =
|
||||
{
|
||||
if (m_lgmp == nullptr)
|
||||
return;
|
||||
{7680, 4800, 120}, {7680, 4320, 120}, {6016, 3384, 120}, {5760, 3600, 120},
|
||||
{5760, 3240, 120}, {5120, 2800, 120}, {4096, 2560, 120}, {4096, 2304, 120},
|
||||
{3840, 2400, 120}, {3840, 2160, 120}, {3200, 2400, 120}, {3200, 1800, 120},
|
||||
{3008, 1692, 120}, {2880, 1800, 120}, {2880, 1620, 120}, {2560, 1600, 120},
|
||||
{2560, 1440, 120}, {1920, 1440, 120}, {1920, 1200, 120}, {1920, 1080, 120},
|
||||
{1600, 1200, 120}, {1600, 1024, 120}, {1600, 1050, 120}, {1600, 900 , 120},
|
||||
{1440, 900 , 120}, {1400, 1050, 120}, {1366, 768 , 120}, {1360, 768 , 120},
|
||||
{1280, 1024, 120}, {1280, 960 , 120}, {1280, 800 , 120}, {1280, 768 , 120},
|
||||
{1280, 720 , 120}, {1280, 600 , 120}, {1152, 864 , 120}, {1024, 768 , 120},
|
||||
{800 , 600 , 120}, {640 , 480 , 120}
|
||||
};
|
||||
|
||||
if (m_lgmpTimer)
|
||||
static const BYTE EDID[] =
|
||||
{
|
||||
0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x30,0xE8,0x34,0x12,0xC9,0x07,0xCC,0x00,
|
||||
0x01,0x21,0x01,0x04,0xA5,0x3C,0x22,0x78,0xFB,0x6C,0xE5,0xA5,0x55,0x50,0xA0,0x23,
|
||||
0x0B,0x50,0x54,0x00,0x02,0x00,0xD1,0xC0,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
|
||||
0x01,0x01,0x01,0x01,0x01,0x01,0x58,0xE3,0x00,0xA0,0xA0,0xA0,0x29,0x50,0x30,0x20,
|
||||
0x35,0x00,0x55,0x50,0x21,0x00,0x00,0x1A,0x00,0x00,0x00,0xFF,0x00,0x4C,0x6F,0x6F,
|
||||
0x6B,0x69,0x6E,0x67,0x47,0x6C,0x61,0x73,0x73,0x0A,0x00,0x00,0x00,0xFC,0x00,0x4C,
|
||||
0x6F,0x6F,0x6B,0x69,0x6E,0x67,0x20,0x47,0x6C,0x61,0x73,0x73,0x00,0x00,0x00,0xFD,
|
||||
0x00,0x28,0x9B,0xFA,0xFA,0x40,0x01,0x0A,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x4A
|
||||
};
|
||||
|
||||
const DWORD DefaultPreferredDisplayMode = 19;
|
||||
|
||||
void CIndirectDeviceContext::PopulateDefaultModes(bool setDefaultMode)
|
||||
{
|
||||
m_displayModes.reserve(m_displayModes.size() +
|
||||
ARRAYSIZE(DefaultDisplayModes));
|
||||
|
||||
for (int i = 0; i < ARRAYSIZE(DefaultDisplayModes); ++i)
|
||||
{
|
||||
WdfTimerStop(m_lgmpTimer, TRUE);
|
||||
m_lgmpTimer = nullptr;
|
||||
DisplayMode m;
|
||||
m.width = DefaultDisplayModes[i][0];
|
||||
m.height = DefaultDisplayModes[i][1];
|
||||
m.refresh = DefaultDisplayModes[i][2];
|
||||
m.preferred = setDefaultMode && (i == DefaultPreferredDisplayMode);
|
||||
m_displayModes.push_back(m);
|
||||
}
|
||||
|
||||
for(int i = 0; i < LGMP_Q_FRAME_LEN; ++i)
|
||||
lgmpHostMemFree(&m_frameMemory[i]);
|
||||
for (int i = 0; i < LGMP_Q_POINTER_LEN; ++i)
|
||||
lgmpHostMemFree(&m_pointerMemory[i]);
|
||||
for (int i = 0; i < POINTER_SHAPE_BUFFERS; ++i)
|
||||
lgmpHostMemFree(&m_pointerShapeMemory[i]);
|
||||
lgmpHostFree(&m_lgmp);
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::InitAdapter()
|
||||
{
|
||||
if (!SetupLGMP())
|
||||
if (!m_ivshmem.Init() || !m_ivshmem.Open())
|
||||
return;
|
||||
|
||||
m_displayModes.clear();
|
||||
PopulateDefaultModes(true);
|
||||
|
||||
IDDCX_ADAPTER_CAPS caps = {};
|
||||
caps.Size = sizeof(caps);
|
||||
|
||||
@ -82,7 +111,7 @@ void CIndirectDeviceContext::InitAdapter()
|
||||
caps.EndPointDiagnostics.GammaSupport = IDDCX_FEATURE_IMPLEMENTATION_NONE;
|
||||
caps.EndPointDiagnostics.TransmissionType = IDDCX_TRANSMISSION_TYPE_OTHER;
|
||||
|
||||
caps.EndPointDiagnostics.pEndPointFriendlyName = L"Looking Glass IDD Device";
|
||||
caps.EndPointDiagnostics.pEndPointFriendlyName = L"Looking Glass IDD Driver";
|
||||
caps.EndPointDiagnostics.pEndPointManufacturerName = L"Looking Glass";
|
||||
caps.EndPointDiagnostics.pEndPointModelName = L"Looking Glass";
|
||||
|
||||
@ -104,7 +133,7 @@ void CIndirectDeviceContext::InitAdapter()
|
||||
NTSTATUS status = IddCxAdapterInitAsync(&init, &initOut);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DBGPRINT("IddCxAdapterInitAsync Failed");
|
||||
DEBUG_ERROR_HR(status, "IddCxAdapterInitAsync Failed");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -133,21 +162,9 @@ void CIndirectDeviceContext::InitAdapter()
|
||||
factory->Release();
|
||||
|
||||
auto * wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(m_adapter);
|
||||
wrapper->context = this;
|
||||
wrapper->context = this;
|
||||
}
|
||||
|
||||
static const BYTE EDID[] =
|
||||
{
|
||||
0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x30,0xE8,0x34,0x12,0xC9,0x07,0xCC,0x00,
|
||||
0x01,0x21,0x01,0x04,0xA5,0x3C,0x22,0x78,0xFB,0x6C,0xE5,0xA5,0x55,0x50,0xA0,0x23,
|
||||
0x0B,0x50,0x54,0x00,0x02,0x00,0xD1,0xC0,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
|
||||
0x01,0x01,0x01,0x01,0x01,0x01,0x58,0xE3,0x00,0xA0,0xA0,0xA0,0x29,0x50,0x30,0x20,
|
||||
0x35,0x00,0x55,0x50,0x21,0x00,0x00,0x1A,0x00,0x00,0x00,0xFF,0x00,0x4C,0x6F,0x6F,
|
||||
0x6B,0x69,0x6E,0x67,0x47,0x6C,0x61,0x73,0x73,0x0A,0x00,0x00,0x00,0xFC,0x00,0x4C,
|
||||
0x6F,0x6F,0x6B,0x69,0x6E,0x67,0x20,0x47,0x6C,0x61,0x73,0x73,0x00,0x00,0x00,0xFD,
|
||||
0x00,0x28,0x9B,0xFA,0xFA,0x40,0x01,0x0A,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x4A
|
||||
};
|
||||
|
||||
void CIndirectDeviceContext::FinishInit(UINT connectorIndex)
|
||||
{
|
||||
WDF_OBJECT_ATTRIBUTES attr;
|
||||
@ -181,7 +198,7 @@ void CIndirectDeviceContext::FinishInit(UINT connectorIndex)
|
||||
NTSTATUS status = IddCxMonitorCreate(m_adapter, &create, &createOut);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DBGPRINT("IddCxMonitorCreate Failed");
|
||||
DEBUG_ERROR_HR(status, "IddCxMonitorCreate Failed");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -191,20 +208,235 @@ void CIndirectDeviceContext::FinishInit(UINT connectorIndex)
|
||||
|
||||
IDARG_OUT_MONITORARRIVAL out;
|
||||
status = IddCxMonitorArrival(m_monitor, &out);
|
||||
if (FAILED(status))
|
||||
{
|
||||
DEBUG_ERROR_HR(status, "IddCxMonitorArrival Failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool CIndirectDeviceContext::SetupLGMP()
|
||||
void CIndirectDeviceContext::ReplugMonitor()
|
||||
{
|
||||
if (!m_ivshmem.Init() || !m_ivshmem.Open())
|
||||
return false;
|
||||
if (m_monitor == WDF_NO_HANDLE)
|
||||
{
|
||||
FinishInit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_replugMonitor)
|
||||
return;
|
||||
|
||||
DEBUG_TRACE("ReplugMonitor");
|
||||
m_replugMonitor = true;
|
||||
NTSTATUS status = IddCxMonitorDeparture(m_monitor);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
m_replugMonitor = false;
|
||||
DEBUG_ERROR("IddCxMonitorDeparture Failed (0x%08x)", status);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::OnAssignSwapChain()
|
||||
{
|
||||
if (m_setCustomMode)
|
||||
{
|
||||
m_setCustomMode = false;
|
||||
g_pipe.SetDisplayMode(m_customMode.width, m_customMode.height);
|
||||
}
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::OnUnassignedSwapChain()
|
||||
{
|
||||
if (m_replugMonitor)
|
||||
{
|
||||
m_replugMonitor = false;
|
||||
FinishInit(0);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void FillSignalInfo(DISPLAYCONFIG_VIDEO_SIGNAL_INFO & mode, DWORD width, DWORD height, DWORD vsync, bool monitorMode)
|
||||
{
|
||||
mode.totalSize.cx = mode.activeSize.cx = width;
|
||||
mode.totalSize.cy = mode.activeSize.cy = height;
|
||||
|
||||
mode.AdditionalSignalInfo.vSyncFreqDivider = monitorMode ? 0 : 1;
|
||||
mode.AdditionalSignalInfo.videoStandard = 255;
|
||||
|
||||
mode.vSyncFreq.Numerator = vsync;
|
||||
mode.vSyncFreq.Denominator = 1;
|
||||
mode.hSyncFreq.Numerator = vsync * height;
|
||||
mode.hSyncFreq.Denominator = 1;
|
||||
|
||||
mode.scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE;
|
||||
mode.pixelRate = ((UINT64)vsync) * ((UINT64)width) * ((UINT64)height);
|
||||
}
|
||||
|
||||
NTSTATUS CIndirectDeviceContext::ParseMonitorDescription(
|
||||
const IDARG_IN_PARSEMONITORDESCRIPTION* inArgs,
|
||||
IDARG_OUT_PARSEMONITORDESCRIPTION* outArgs)
|
||||
{
|
||||
outArgs->MonitorModeBufferOutputCount = (UINT)m_displayModes.size();
|
||||
if (inArgs->MonitorModeBufferInputCount < (UINT)m_displayModes.size())
|
||||
return (inArgs->MonitorModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS;
|
||||
|
||||
auto * mode = inArgs->pMonitorModes;
|
||||
for (auto it = m_displayModes.cbegin(); it != m_displayModes.cend(); ++it, ++mode)
|
||||
{
|
||||
mode->Size = sizeof(IDDCX_MONITOR_MODE);
|
||||
mode->Origin = IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR;
|
||||
FillSignalInfo(mode->MonitorVideoSignalInfo, it->width, it->height, it->refresh, true);
|
||||
|
||||
if (it->preferred)
|
||||
outArgs->PreferredMonitorModeIdx =
|
||||
(UINT)std::distance(m_displayModes.cbegin(), it);
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS CIndirectDeviceContext::MonitorGetDefaultModes(
|
||||
const IDARG_IN_GETDEFAULTDESCRIPTIONMODES* inArgs,
|
||||
IDARG_OUT_GETDEFAULTDESCRIPTIONMODES* outArgs)
|
||||
{
|
||||
outArgs->DefaultMonitorModeBufferOutputCount = (UINT)m_displayModes.size();
|
||||
if (inArgs->DefaultMonitorModeBufferInputCount < (UINT)m_displayModes.size())
|
||||
return (inArgs->DefaultMonitorModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS;
|
||||
|
||||
auto* mode = inArgs->pDefaultMonitorModes;
|
||||
for (auto it = m_displayModes.cbegin(); it != m_displayModes.cend(); ++it, ++mode)
|
||||
{
|
||||
mode->Size = sizeof(IDDCX_MONITOR_MODE);
|
||||
mode->Origin = IDDCX_MONITOR_MODE_ORIGIN_DRIVER;
|
||||
FillSignalInfo(mode->MonitorVideoSignalInfo, it->width, it->height, it->refresh, true);
|
||||
|
||||
if (it->preferred)
|
||||
outArgs->PreferredMonitorModeIdx =
|
||||
(UINT)std::distance(m_displayModes.cbegin(), it);
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS CIndirectDeviceContext::MonitorQueryTargetModes(
|
||||
const IDARG_IN_QUERYTARGETMODES* inArgs,
|
||||
IDARG_OUT_QUERYTARGETMODES* outArgs)
|
||||
{
|
||||
outArgs->TargetModeBufferOutputCount = (UINT)m_displayModes.size();
|
||||
if (inArgs->TargetModeBufferInputCount < (UINT)m_displayModes.size())
|
||||
return (inArgs->TargetModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS;
|
||||
|
||||
auto* mode = inArgs->pTargetModes;
|
||||
for (auto it = m_displayModes.cbegin(); it != m_displayModes.cend(); ++it, ++mode)
|
||||
{
|
||||
mode->Size = sizeof(IDDCX_TARGET_MODE);
|
||||
FillSignalInfo(mode->TargetVideoSignalInfo.targetVideoSignalInfo, it->width, it->height, it->refresh, false);
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::SetResolution(int width, int height)
|
||||
{
|
||||
m_displayModes.clear();
|
||||
m_customMode.width = width;
|
||||
m_customMode.height = height;
|
||||
m_customMode.refresh = 120;
|
||||
m_customMode.preferred = true;
|
||||
m_displayModes.push_back(m_customMode);
|
||||
PopulateDefaultModes(false);
|
||||
|
||||
m_setCustomMode = true;
|
||||
|
||||
#if 1
|
||||
ReplugMonitor();
|
||||
#else
|
||||
|
||||
if (IDD_IS_FUNCTION_AVAILABLE(IddCxMonitorUpdateModes2))
|
||||
{
|
||||
IDDCX_TARGET_MODE2* modes = (IDDCX_TARGET_MODE2*)_malloca(
|
||||
m_displayModes.size() * sizeof(IDDCX_TARGET_MODE2));
|
||||
|
||||
if (!modes)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate memory for the mode list");
|
||||
return;
|
||||
}
|
||||
|
||||
ZeroMemory(modes, m_displayModes.size() * sizeof(IDDCX_TARGET_MODE2));
|
||||
|
||||
IDARG_IN_UPDATEMODES2 um = {};
|
||||
um.Reason = IDDCX_UPDATE_REASON_OTHER;
|
||||
um.TargetModeCount = (UINT)m_displayModes.size();
|
||||
um.pTargetModes = modes;
|
||||
auto* mode = modes;
|
||||
for (auto it = m_displayModes.cbegin(); it != m_displayModes.cend(); ++it, ++mode)
|
||||
{
|
||||
mode->Size = sizeof(IDDCX_TARGET_MODE2);
|
||||
mode->RequiredBandwidth = (UINT64)(it->width * it->height * it->refresh * 32);
|
||||
mode->BitsPerComponent.Rgb = IDDCX_BITS_PER_COMPONENT_8;
|
||||
|
||||
FillSignalInfo(mode->TargetVideoSignalInfo.targetVideoSignalInfo, it->width, it->height, it->refresh, true);
|
||||
}
|
||||
|
||||
NTSTATUS status = IddCxMonitorUpdateModes2(m_monitor, &um);
|
||||
if (!NT_SUCCESS(status))
|
||||
DEBUG_ERROR("IddCxMonitorUpdateModes2 Failed (0x%08x)", status);
|
||||
|
||||
_freea(modes);
|
||||
}
|
||||
else
|
||||
{
|
||||
IDDCX_TARGET_MODE* modes = (IDDCX_TARGET_MODE*)_malloca(
|
||||
m_displayModes.size() * sizeof(IDDCX_TARGET_MODE));
|
||||
|
||||
if (!modes)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate memory for the mode list");
|
||||
return;
|
||||
}
|
||||
|
||||
IDARG_IN_UPDATEMODES um = {};
|
||||
um.Reason = IDDCX_UPDATE_REASON_OTHER;
|
||||
um.TargetModeCount = (UINT)m_displayModes.size();
|
||||
um.pTargetModes = modes;
|
||||
|
||||
auto* mode = modes;
|
||||
for (auto it = m_displayModes.cbegin(); it != m_displayModes.cend(); ++it, ++mode)
|
||||
{
|
||||
mode->Size = sizeof(IDDCX_TARGET_MODE);
|
||||
mode->RequiredBandwidth = (UINT64)(it->width * it->height * it->refresh * 32);
|
||||
|
||||
FillSignalInfo(mode->TargetVideoSignalInfo.targetVideoSignalInfo, it->width, it->height, it->refresh, true);
|
||||
}
|
||||
|
||||
NTSTATUS status = IddCxMonitorUpdateModes(m_monitor, &um);
|
||||
if (!NT_SUCCESS(status))
|
||||
DEBUG_ERROR("IddCxMonitorUpdateModes Failed (0x%08x)", status);
|
||||
|
||||
_freea(modes);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CIndirectDeviceContext::SetupLGMP(size_t alignSize)
|
||||
{
|
||||
// this may get called multiple times as we need to delay calling it until
|
||||
// we can determine the required alignment from the GPU in use
|
||||
if (m_lgmp)
|
||||
return true;
|
||||
|
||||
m_alignSize = alignSize;
|
||||
|
||||
std::stringstream ss;
|
||||
{
|
||||
KVMFR kvmfr = {};
|
||||
memcpy_s(kvmfr.magic, sizeof(kvmfr.magic), KVMFR_MAGIC, sizeof(KVMFR_MAGIC) - 1);
|
||||
kvmfr.version = KVMFR_VERSION;
|
||||
kvmfr.features = KVMFR_FEATURE_SETCURSORPOS;
|
||||
strncpy_s(kvmfr.hostver, "FIXME-IDD", sizeof(kvmfr.hostver) - 1);
|
||||
kvmfr.features =
|
||||
KVMFR_FEATURE_SETCURSORPOS |
|
||||
KVMFR_FEATURE_WINDOWSIZE;
|
||||
strncpy_s(kvmfr.hostver, LG_VERSION_STR, sizeof(kvmfr.hostver) - 1);
|
||||
ss.write(reinterpret_cast<const char *>(&kvmfr), sizeof(kvmfr));
|
||||
}
|
||||
|
||||
@ -214,7 +446,7 @@ bool CIndirectDeviceContext::SetupLGMP()
|
||||
KVMFRRecord_VMInfo * vmInfo = static_cast<KVMFRRecord_VMInfo *>(calloc(1, sizeof(*vmInfo)));
|
||||
if (!vmInfo)
|
||||
{
|
||||
DBGPRINT("Failed to allocate KVMFRRecord_VMInfo");
|
||||
DEBUG_ERROR("Failed to allocate KVMFRRecord_VMInfo");
|
||||
return false;
|
||||
}
|
||||
vmInfo->cpus = static_cast<uint8_t>(CPlatformInfo::GetProcCount ());
|
||||
@ -223,12 +455,12 @@ bool CIndirectDeviceContext::SetupLGMP()
|
||||
|
||||
const uint8_t * uuid = CPlatformInfo::GetUUID();
|
||||
memcpy_s (vmInfo->uuid, sizeof(vmInfo->uuid), uuid, 16);
|
||||
strncpy_s(vmInfo->capture, "Idd Driver", sizeof(vmInfo->capture));
|
||||
strncpy_s(vmInfo->capture, "Looking Glass IDD Driver", sizeof(vmInfo->capture));
|
||||
|
||||
KVMFRRecord * record = static_cast<KVMFRRecord *>(calloc(1, sizeof(*record)));
|
||||
if (!record)
|
||||
{
|
||||
DBGPRINT("Failed to allocate KVMFRRecord");
|
||||
DEBUG_ERROR("Failed to allocate KVMFRRecord");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -244,7 +476,7 @@ bool CIndirectDeviceContext::SetupLGMP()
|
||||
KVMFRRecord_OSInfo * osInfo = static_cast<KVMFRRecord_OSInfo *>(calloc(1, sizeof(*osInfo)));
|
||||
if (!osInfo)
|
||||
{
|
||||
DBGPRINT("Failed to allocate KVMFRRecord_OSInfo");
|
||||
DEBUG_ERROR("Failed to allocate KVMFRRecord_OSInfo");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -255,7 +487,7 @@ bool CIndirectDeviceContext::SetupLGMP()
|
||||
KVMFRRecord* record = static_cast<KVMFRRecord*>(calloc(1, sizeof(*record)));
|
||||
if (!record)
|
||||
{
|
||||
DBGPRINT("Failed to allocate KVMFRRecord");
|
||||
DEBUG_ERROR("Failed to allocate KVMFRRecord");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -273,19 +505,19 @@ bool CIndirectDeviceContext::SetupLGMP()
|
||||
if ((status = lgmpHostInit(m_ivshmem.GetMem(), (uint32_t)m_ivshmem.GetSize(),
|
||||
&m_lgmp, (uint32_t)udata.size(), (uint8_t*)&udata[0])) != LGMP_OK)
|
||||
{
|
||||
DBGPRINT("lgmpHostInit Failed: %s", lgmpStatusString(status));
|
||||
DEBUG_ERROR("lgmpHostInit Failed: %s", lgmpStatusString(status));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((status = lgmpHostQueueNew(m_lgmp, FRAME_QUEUE_CONFIG, &m_frameQueue)) != LGMP_OK)
|
||||
{
|
||||
DBGPRINT("lgmpHostQueueCreate Failed (Frame): %s", lgmpStatusString(status));
|
||||
DEBUG_ERROR("lgmpHostQueueCreate Failed (Frame): %s", lgmpStatusString(status));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((status = lgmpHostQueueNew(m_lgmp, POINTER_QUEUE_CONFIG, &m_pointerQueue)) != LGMP_OK)
|
||||
{
|
||||
DBGPRINT("lgmpHostQueueCreate Failed (Pointer): %s", lgmpStatusString(status));
|
||||
DEBUG_ERROR("lgmpHostQueueCreate Failed (Pointer): %s", lgmpStatusString(status));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -293,7 +525,7 @@ bool CIndirectDeviceContext::SetupLGMP()
|
||||
{
|
||||
if ((status = lgmpHostMemAlloc(m_lgmp, MAX_POINTER_SIZE, &m_pointerMemory[i])) != LGMP_OK)
|
||||
{
|
||||
DBGPRINT("lgmpHostMemAlloc Failed (Pointer): %s", lgmpStatusString(status));
|
||||
DEBUG_ERROR("lgmpHostMemAlloc Failed (Pointer): %s", lgmpStatusString(status));
|
||||
return false;
|
||||
}
|
||||
memset(lgmpHostMemPtr(m_pointerMemory[i]), 0, MAX_POINTER_SIZE);
|
||||
@ -303,25 +535,36 @@ bool CIndirectDeviceContext::SetupLGMP()
|
||||
{
|
||||
if ((status = lgmpHostMemAlloc(m_lgmp, MAX_POINTER_SIZE, &m_pointerShapeMemory[i])) != LGMP_OK)
|
||||
{
|
||||
DBGPRINT("lgmpHostMemAlloc Failed (Pointer Shapes): %s", lgmpStatusString(status));
|
||||
DEBUG_ERROR("lgmpHostMemAlloc Failed (Pointer Shapes): %s", lgmpStatusString(status));
|
||||
return false;
|
||||
}
|
||||
memset(lgmpHostMemPtr(m_pointerShapeMemory[i]), 0, MAX_POINTER_SIZE);
|
||||
}
|
||||
|
||||
m_maxFrameSize = lgmpHostMemAvail(m_lgmp);
|
||||
m_maxFrameSize = (m_maxFrameSize -(CPlatformInfo::GetPageSize() - 1)) & ~(CPlatformInfo::GetPageSize() - 1);
|
||||
m_maxFrameSize = (m_maxFrameSize -(m_alignSize - 1)) & ~(m_alignSize - 1);
|
||||
m_maxFrameSize /= LGMP_Q_FRAME_LEN;
|
||||
DBGPRINT("Max Frame Size: %u MiB\n", (unsigned int)(m_maxFrameSize / 1048576LL));
|
||||
DEBUG_INFO("Max Frame Size: %u MiB", (unsigned int)(m_maxFrameSize / 1048576LL));
|
||||
|
||||
for (int i = 0; i < LGMP_Q_FRAME_LEN; ++i)
|
||||
{
|
||||
if ((status = lgmpHostMemAllocAligned(m_lgmp, (uint32_t)m_maxFrameSize,
|
||||
(uint32_t)CPlatformInfo::GetPageSize(), &m_frameMemory[i])) != LGMP_OK)
|
||||
(uint32_t)m_alignSize, &m_frameMemory[i])) != LGMP_OK)
|
||||
{
|
||||
DBGPRINT("lgmpHostMemAllocAligned Failed (Frame): %s", lgmpStatusString(status));
|
||||
DEBUG_ERROR("lgmpHostMemAllocAligned Failed (Frame): %s", lgmpStatusString(status));
|
||||
return false;
|
||||
}
|
||||
|
||||
m_frame[i] = (KVMFRFrame *)lgmpHostMemPtr(m_frameMemory[i]);
|
||||
|
||||
/**
|
||||
* put the framebuffer on the border of the next page, this is to allow for
|
||||
* aligned DMA tranfers by the reciever */
|
||||
const size_t alignOffset = alignSize - sizeof(FrameBuffer);
|
||||
m_frame[i]->offset = (uint32_t)alignOffset;
|
||||
m_frameBuffer[i] = (FrameBuffer*)(((uint8_t*)m_frame[i]) + alignOffset);
|
||||
}
|
||||
|
||||
WDF_TIMER_CONFIG config;
|
||||
WDF_TIMER_CONFIG_INIT_PERIODIC(&config,
|
||||
[](WDFTIMER timer) -> void
|
||||
@ -345,7 +588,7 @@ bool CIndirectDeviceContext::SetupLGMP()
|
||||
NTSTATUS s = WdfTimerCreate(&config, &attribs, &m_lgmpTimer);
|
||||
if (!NT_SUCCESS(s))
|
||||
{
|
||||
DBGPRINT("Timer creation failed: 0x%08x", s);
|
||||
DEBUG_ERROR_HR(s, "Timer creation failed");
|
||||
return false;
|
||||
}
|
||||
WdfTimerStart(m_lgmpTimer, WDF_REL_TIMEOUT_IN_MS(10));
|
||||
@ -353,6 +596,28 @@ bool CIndirectDeviceContext::SetupLGMP()
|
||||
return true;
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::DeInitLGMP()
|
||||
{
|
||||
m_hasFrame = false;
|
||||
|
||||
if (m_lgmp == nullptr)
|
||||
return;
|
||||
|
||||
if (m_lgmpTimer)
|
||||
{
|
||||
WdfTimerStop(m_lgmpTimer, TRUE);
|
||||
m_lgmpTimer = nullptr;
|
||||
}
|
||||
|
||||
for (int i = 0; i < LGMP_Q_FRAME_LEN; ++i)
|
||||
lgmpHostMemFree(&m_frameMemory[i]);
|
||||
for (int i = 0; i < LGMP_Q_POINTER_LEN; ++i)
|
||||
lgmpHostMemFree(&m_pointerMemory[i]);
|
||||
for (int i = 0; i < POINTER_SHAPE_BUFFERS; ++i)
|
||||
lgmpHostMemFree(&m_pointerShapeMemory[i]);
|
||||
lgmpHostFree(&m_lgmp);
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::LGMPTimer()
|
||||
{
|
||||
LGMP_STATUS status;
|
||||
@ -360,12 +625,12 @@ void CIndirectDeviceContext::LGMPTimer()
|
||||
{
|
||||
if (status == LGMP_ERR_CORRUPTED)
|
||||
{
|
||||
DBGPRINT("LGMP reported the shared memory has been corrupted, attempting to recover\n");
|
||||
DEBUG_WARN("LGMP reported the shared memory has been corrupted, attempting to recover\n");
|
||||
//TODO: fixme - reinit
|
||||
return;
|
||||
}
|
||||
|
||||
DBGPRINT("lgmpHostProcess Failed: %s", lgmpStatusString(status));
|
||||
DEBUG_ERROR("lgmpHostProcess Failed: %s", lgmpStatusString(status));
|
||||
//TODO: fixme - shutdown
|
||||
return;
|
||||
}
|
||||
@ -379,10 +644,16 @@ void CIndirectDeviceContext::LGMPTimer()
|
||||
{
|
||||
case KVMFR_MESSAGE_SETCURSORPOS:
|
||||
{
|
||||
KVMFRSetCursorPos *sp = (KVMFRSetCursorPos *)msg;
|
||||
SetCursorPos(sp->x, sp->y);
|
||||
KVMFRSetCursorPos* sp = (KVMFRSetCursorPos*)msg;
|
||||
g_pipe.SetCursorPos(sp->x, sp->y);
|
||||
break;
|
||||
}
|
||||
|
||||
case KVMFR_MESSAGE_WINDOWSIZE:
|
||||
{
|
||||
KVMFRWindowSize* ws = (KVMFRWindowSize*)msg;
|
||||
SetResolution(ws->w, ws->h);
|
||||
}
|
||||
}
|
||||
|
||||
lgmpHostAckData(m_pointerQueue);
|
||||
@ -390,19 +661,20 @@ void CIndirectDeviceContext::LGMPTimer()
|
||||
|
||||
if (lgmpHostQueueNewSubs(m_frameQueue) && m_monitor)
|
||||
{
|
||||
auto* wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(m_monitor);
|
||||
if (wrapper)
|
||||
wrapper->context->ResendLastFrame();
|
||||
if (m_hasFrame)
|
||||
lgmpHostQueuePost(m_frameQueue, 0, m_frameMemory[m_frameIndex]);
|
||||
}
|
||||
|
||||
if (lgmpHostQueueNewSubs(m_pointerQueue))
|
||||
ResendCursor();
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::SendFrame(int width, int height, int pitch, DXGI_FORMAT format, void* data)
|
||||
CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrameBuffer(int width, int height, int pitch, DXGI_FORMAT format)
|
||||
{
|
||||
PreparedFrameBuffer result = {};
|
||||
|
||||
if (!m_lgmp || !m_frameQueue)
|
||||
return;
|
||||
return result;
|
||||
|
||||
if (m_width != width || m_height != height || m_pitch != pitch || m_format != format)
|
||||
{
|
||||
@ -413,13 +685,15 @@ void CIndirectDeviceContext::SendFrame(int width, int height, int pitch, DXGI_FO
|
||||
++m_formatVer;
|
||||
}
|
||||
|
||||
while (lgmpHostQueuePending(m_frameQueue) == LGMP_Q_FRAME_LEN)
|
||||
Sleep(0);
|
||||
|
||||
if (++m_frameIndex == LGMP_Q_FRAME_LEN)
|
||||
m_frameIndex = 0;
|
||||
|
||||
KVMFRFrame * fi = (KVMFRFrame *)lgmpHostMemPtr(m_frameMemory[m_frameIndex]);
|
||||
KVMFRFrame * fi = m_frame[m_frameIndex];
|
||||
|
||||
// wait until there is room in the queue
|
||||
while (lgmpHostQueuePending(m_frameQueue) == LGMP_Q_FRAME_LEN)
|
||||
Sleep(0);
|
||||
|
||||
int bpp = 4;
|
||||
switch (format)
|
||||
{
|
||||
@ -433,40 +707,54 @@ void CIndirectDeviceContext::SendFrame(int width, int height, int pitch, DXGI_FO
|
||||
break;
|
||||
|
||||
default:
|
||||
DBGPRINT("Unsuppoted DXGI format");
|
||||
return;
|
||||
DEBUG_ERROR("Unsuppoted DXGI format 0x%08x", format);
|
||||
return result;
|
||||
}
|
||||
|
||||
//FIXME: this should not really be done here, this is a hack
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4200)
|
||||
struct FrameBuffer
|
||||
{
|
||||
volatile uint32_t wp;
|
||||
uint8_t data[0];
|
||||
};
|
||||
#pragma warning(pop)
|
||||
|
||||
fi->formatVer = m_formatVer;
|
||||
fi->frameSerial = m_frameSerial++;
|
||||
fi->screenWidth = width;
|
||||
fi->screenHeight = height;
|
||||
fi->frameWidth = width;
|
||||
fi->frameHeight = height;
|
||||
fi->stride = width * bpp;
|
||||
fi->pitch = pitch;
|
||||
fi->offset = (uint32_t)(CPlatformInfo::GetPageSize() - sizeof(FrameBuffer));
|
||||
fi->flags = 0;
|
||||
fi->rotation = FRAME_ROT_0;
|
||||
|
||||
fi->formatVer = m_formatVer;
|
||||
fi->frameSerial = m_frameSerial++;
|
||||
fi->screenWidth = width;
|
||||
fi->screenHeight = height;
|
||||
fi->dataWidth = width;
|
||||
fi->dataHeight = height;
|
||||
fi->frameWidth = width;
|
||||
fi->frameHeight = height;
|
||||
fi->stride = width * bpp;
|
||||
fi->pitch = pitch;
|
||||
// fi->offset is initialized at startup
|
||||
fi->flags = 0;
|
||||
fi->rotation = FRAME_ROT_0;
|
||||
fi->damageRectsCount = 0;
|
||||
|
||||
FrameBuffer * fb = (FrameBuffer *)(((uint8_t*)fi) + fi->offset);
|
||||
FrameBuffer* fb = m_frameBuffer[m_frameIndex];
|
||||
fb->wp = 0;
|
||||
|
||||
lgmpHostQueuePost(m_frameQueue, 0, m_frameMemory[m_frameIndex]);
|
||||
memcpy(fb->data, data, (size_t)height * (size_t)pitch);
|
||||
fb->wp = height * pitch;
|
||||
|
||||
result.frameIndex = m_frameIndex;
|
||||
result.mem = fb->data;
|
||||
|
||||
m_hasFrame = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::WriteFrameBuffer(unsigned frameIndex, void* src, size_t offset, size_t len, bool setWritePos)
|
||||
{
|
||||
FrameBuffer * fb = m_frameBuffer[frameIndex];
|
||||
|
||||
memcpy(
|
||||
(void *)((uintptr_t)fb->data + offset),
|
||||
(void *)((uintptr_t)src + offset),
|
||||
len);
|
||||
|
||||
if (setWritePos)
|
||||
fb->wp = (uint32_t)(offset + len);
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::FinalizeFrameBuffer(unsigned frameIndex)
|
||||
{
|
||||
FrameBuffer * fb = m_frameBuffer[frameIndex];
|
||||
fb->wp = m_height * m_pitch;
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info, const BYTE * data)
|
||||
@ -502,7 +790,7 @@ void CIndirectDeviceContext::SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info, co
|
||||
if (info.CursorShapeInfo.CursorType != IDDCX_CURSOR_SHAPE_TYPE_UNINITIALIZED)
|
||||
{
|
||||
memcpy(cursor + 1, data,
|
||||
(size_t)(info.CursorShapeInfo.Height * info.CursorShapeInfo.Pitch));
|
||||
(size_t)info.CursorShapeInfo.Height * info.CursorShapeInfo.Pitch);
|
||||
|
||||
cursor->hx = (int8_t )info.CursorShapeInfo.XHot;
|
||||
cursor->hy = (int8_t )info.CursorShapeInfo.YHot;
|
||||
@ -534,7 +822,7 @@ void CIndirectDeviceContext::SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info, co
|
||||
continue;
|
||||
}
|
||||
|
||||
DBGPRINT("lgmpHostQueuePost Failed (Pointer): %s", lgmpStatusString(status));
|
||||
DEBUG_ERROR("lgmpHostQueuePost Failed (Pointer): %s", lgmpStatusString(status));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -562,7 +850,7 @@ void CIndirectDeviceContext::ResendCursor()
|
||||
continue;
|
||||
}
|
||||
|
||||
DBGPRINT("lgmpHostQueuePost Failed (Pointer): %s", lgmpStatusString(status));
|
||||
DEBUG_ERROR("lgmpHostQueuePost Failed (Pointer): %s", lgmpStatusString(status));
|
||||
break;
|
||||
}
|
||||
}
|
@ -23,6 +23,7 @@
|
||||
#include <Windows.h>
|
||||
#include <wdf.h>
|
||||
#include <IddCx.h>
|
||||
#include <vector>
|
||||
|
||||
#include "CIVSHMEM.h"
|
||||
|
||||
@ -34,12 +35,23 @@ extern "C" {
|
||||
#define MAX_POINTER_SIZE (sizeof(KVMFRCursor) + (512 * 512 * 4))
|
||||
#define POINTER_SHAPE_BUFFERS 3
|
||||
|
||||
//FIXME: this should not really be done here, this is a hack
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4200)
|
||||
struct FrameBuffer
|
||||
{
|
||||
volatile uint32_t wp;
|
||||
uint8_t data[0];
|
||||
};
|
||||
#pragma warning(pop)
|
||||
|
||||
class CIndirectDeviceContext
|
||||
{
|
||||
private:
|
||||
WDFDEVICE m_wdfDevice;
|
||||
IDDCX_ADAPTER m_adapter = nullptr;
|
||||
IDDCX_MONITOR m_monitor = nullptr;
|
||||
IDDCX_ADAPTER m_adapter = nullptr;
|
||||
IDDCX_MONITOR m_monitor = nullptr;
|
||||
bool m_replugMonitor = false;
|
||||
|
||||
CIVSHMEM m_ivshmem;
|
||||
|
||||
@ -56,33 +68,77 @@ private:
|
||||
bool m_cursorVisible = false;
|
||||
int m_cursorX = 0, m_cursorY = 0;
|
||||
|
||||
size_t m_alignSize = 0;
|
||||
size_t m_maxFrameSize = 0;
|
||||
int m_frameIndex = 0;
|
||||
uint32_t m_formatVer = 0;
|
||||
uint32_t m_frameSerial = 0;
|
||||
PLGMPMemory m_frameMemory[LGMP_Q_FRAME_LEN] = {};
|
||||
KVMFRFrame * m_frame [LGMP_Q_FRAME_LEN] = {};
|
||||
FrameBuffer * m_frameBuffer[LGMP_Q_FRAME_LEN] = {};
|
||||
|
||||
int m_width = 0;
|
||||
int m_height = 0;
|
||||
int m_pitch = 0;
|
||||
DXGI_FORMAT m_format = DXGI_FORMAT_UNKNOWN;
|
||||
int m_width = 0;
|
||||
int m_height = 0;
|
||||
int m_pitch = 0;
|
||||
DXGI_FORMAT m_format = DXGI_FORMAT_UNKNOWN;
|
||||
bool m_hasFrame = false;
|
||||
|
||||
bool SetupLGMP();
|
||||
void DeInitLGMP();
|
||||
void LGMPTimer();
|
||||
void ResendCursor();
|
||||
void ResendCursor();
|
||||
|
||||
struct DisplayMode
|
||||
{
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned refresh;
|
||||
bool preferred;
|
||||
};
|
||||
std::vector<DisplayMode> m_displayModes;
|
||||
DisplayMode m_customMode = {};
|
||||
bool m_setCustomMode = false;
|
||||
|
||||
public:
|
||||
CIndirectDeviceContext(_In_ WDFDEVICE wdfDevice) :
|
||||
m_wdfDevice(wdfDevice) {};
|
||||
|
||||
virtual ~CIndirectDeviceContext();
|
||||
virtual ~CIndirectDeviceContext() { DeInitLGMP(); }
|
||||
|
||||
bool SetupLGMP(size_t alignSize);
|
||||
|
||||
void PopulateDefaultModes(bool setDefaultMode);
|
||||
void InitAdapter();
|
||||
|
||||
void FinishInit(UINT connectorIndex);
|
||||
void ReplugMonitor();
|
||||
|
||||
void OnAssignSwapChain();
|
||||
void OnUnassignedSwapChain();
|
||||
|
||||
NTSTATUS ParseMonitorDescription(
|
||||
const IDARG_IN_PARSEMONITORDESCRIPTION* inArgs, IDARG_OUT_PARSEMONITORDESCRIPTION* outArgs);
|
||||
NTSTATUS MonitorGetDefaultModes(
|
||||
const IDARG_IN_GETDEFAULTDESCRIPTIONMODES* inArgs, IDARG_OUT_GETDEFAULTDESCRIPTIONMODES* outArgs);
|
||||
NTSTATUS MonitorQueryTargetModes(
|
||||
const IDARG_IN_QUERYTARGETMODES* inArgs, IDARG_OUT_QUERYTARGETMODES* outArgs);
|
||||
|
||||
void SetResolution(int width, int height);
|
||||
|
||||
size_t GetAlignSize() { return m_alignSize; }
|
||||
size_t GetMaxFrameSize() { return m_maxFrameSize; }
|
||||
|
||||
struct PreparedFrameBuffer
|
||||
{
|
||||
unsigned frameIndex;
|
||||
uint8_t* mem;
|
||||
};
|
||||
|
||||
PreparedFrameBuffer PrepareFrameBuffer(int width, int height, int pitch, DXGI_FORMAT format);
|
||||
void WriteFrameBuffer(unsigned frameIndex, void* src, size_t offset, size_t len, bool setWritePos);
|
||||
void FinalizeFrameBuffer(unsigned frameIndex);
|
||||
|
||||
void SendFrame(int width, int height, int pitch, DXGI_FORMAT format, void* data);
|
||||
void SendCursor(const IDARG_OUT_QUERY_HWCURSOR & info, const BYTE * data);
|
||||
|
||||
CIVSHMEM &GetIVSHMEM() { return m_ivshmem; }
|
||||
};
|
||||
|
||||
struct CIndirectDeviceContextWrapper
|
||||
|
@ -19,8 +19,8 @@
|
||||
*/
|
||||
|
||||
#include "CIndirectMonitorContext.h"
|
||||
#include "Direct3DDevice.h"
|
||||
#include "Debug.h"
|
||||
#include "CPlatformInfo.h"
|
||||
#include "CDebug.h"
|
||||
|
||||
CIndirectMonitorContext::CIndirectMonitorContext(_In_ IDDCX_MONITOR monitor, CIndirectDeviceContext * device) :
|
||||
m_monitor(monitor),
|
||||
@ -28,28 +28,50 @@ CIndirectMonitorContext::CIndirectMonitorContext(_In_ IDDCX_MONITOR monitor, CIn
|
||||
{
|
||||
m_terminateEvent .Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr));
|
||||
m_cursorDataEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr));
|
||||
m_thread.Attach(CreateThread(nullptr, 0, _CursorThread, this, 0, nullptr));
|
||||
m_shapeBuffer = new BYTE[512 * 512 * 4];
|
||||
}
|
||||
|
||||
CIndirectMonitorContext::~CIndirectMonitorContext()
|
||||
{
|
||||
m_swapChain.reset();
|
||||
SetEvent(m_terminateEvent.Get());
|
||||
UnassignSwapChain();
|
||||
delete[] m_shapeBuffer;
|
||||
}
|
||||
|
||||
void CIndirectMonitorContext::AssignSwapChain(IDDCX_SWAPCHAIN swapChain, LUID renderAdapter, HANDLE newFrameEvent)
|
||||
{
|
||||
m_swapChain.reset();
|
||||
auto device = std::make_shared<Direct3DDevice>(renderAdapter);
|
||||
if (FAILED(device->Init()))
|
||||
reInit:
|
||||
UnassignSwapChain();
|
||||
|
||||
m_dx11Device = std::make_shared<CD3D11Device>(renderAdapter);
|
||||
if (FAILED(m_dx11Device->Init()))
|
||||
{
|
||||
WdfObjectDelete(swapChain);
|
||||
return;
|
||||
}
|
||||
|
||||
m_swapChain.reset(new CSwapChainProcessor(m_devContext, swapChain, device, newFrameEvent));
|
||||
UINT64 alignSize = CPlatformInfo::GetPageSize();
|
||||
m_dx12Device = std::make_shared<CD3D12Device>(renderAdapter);
|
||||
switch (m_dx12Device->Init(m_devContext->GetIVSHMEM(), alignSize))
|
||||
{
|
||||
case CD3D12Device::SUCCESS:
|
||||
break;
|
||||
|
||||
case CD3D12Device::FAILURE:
|
||||
WdfObjectDelete(swapChain);
|
||||
return;
|
||||
|
||||
case CD3D12Device::RETRY:
|
||||
m_dx12Device.reset();
|
||||
m_dx11Device.reset();
|
||||
goto reInit;
|
||||
}
|
||||
|
||||
if (!m_devContext->SetupLGMP(alignSize))
|
||||
{
|
||||
WdfObjectDelete(swapChain);
|
||||
DEBUG_ERROR("SetupLGMP failed");
|
||||
return;
|
||||
}
|
||||
|
||||
IDARG_IN_SETUP_HWCURSOR c = {};
|
||||
c.CursorInfo.Size = sizeof(c.CursorInfo);
|
||||
@ -60,12 +82,30 @@ void CIndirectMonitorContext::AssignSwapChain(IDDCX_SWAPCHAIN swapChain, LUID re
|
||||
c.hNewCursorDataAvailable = m_cursorDataEvent.Get();
|
||||
NTSTATUS status = IddCxMonitorSetupHardwareCursor(m_monitor, &c);
|
||||
if (!NT_SUCCESS(status))
|
||||
DBGPRINT("IddCxMonitorSetupHardwareCursor Failed: %08x", status);
|
||||
{
|
||||
WdfObjectDelete(swapChain);
|
||||
DEBUG_ERROR("IddCxMonitorSetupHardwareCursor Failed (0x%08x)", status);
|
||||
return;
|
||||
}
|
||||
|
||||
m_swapChain.reset(new CSwapChainProcessor(m_devContext, swapChain, m_dx11Device, m_dx12Device, newFrameEvent));
|
||||
|
||||
m_lastShapeId = 0;
|
||||
m_thread.Attach(CreateThread(nullptr, 0, _CursorThread, this, 0, nullptr));
|
||||
}
|
||||
|
||||
void CIndirectMonitorContext::UnassignSwapChain()
|
||||
{
|
||||
m_swapChain.reset();
|
||||
SetEvent(m_terminateEvent.Get());
|
||||
if (m_thread.IsValid())
|
||||
WaitForSingleObject(m_thread.Get(), INFINITE);
|
||||
|
||||
m_swapChain.reset();
|
||||
m_dx11Device.reset();
|
||||
m_dx12Device.reset();
|
||||
|
||||
ResetEvent(m_terminateEvent .Get());
|
||||
ResetEvent(m_cursorDataEvent.Get());
|
||||
}
|
||||
|
||||
DWORD CALLBACK CIndirectMonitorContext::_CursorThread(LPVOID arg)
|
||||
@ -77,24 +117,37 @@ DWORD CALLBACK CIndirectMonitorContext::_CursorThread(LPVOID arg)
|
||||
void CIndirectMonitorContext::CursorThread()
|
||||
{
|
||||
HRESULT hr = 0;
|
||||
bool running = true;
|
||||
|
||||
for (;;)
|
||||
while(running)
|
||||
{
|
||||
HANDLE waitHandles[] =
|
||||
{
|
||||
m_cursorDataEvent.Get(),
|
||||
m_terminateEvent.Get()
|
||||
};
|
||||
DWORD waitResult = WaitForMultipleObjects(ARRAYSIZE(waitHandles), waitHandles, FALSE, 100);
|
||||
if (waitResult == WAIT_TIMEOUT)
|
||||
continue;
|
||||
else if (waitResult == WAIT_OBJECT_0 + 1)
|
||||
break;
|
||||
else if (waitResult != WAIT_OBJECT_0)
|
||||
|
||||
DWORD waitResult = WaitForMultipleObjects(
|
||||
ARRAYSIZE(waitHandles), waitHandles, FALSE, 100);
|
||||
|
||||
switch (waitResult)
|
||||
{
|
||||
hr = HRESULT_FROM_WIN32(waitResult);
|
||||
DBGPRINT("WaitForMultipleObjects: %08", hr);
|
||||
return;
|
||||
case WAIT_TIMEOUT:
|
||||
continue;
|
||||
|
||||
// cursorDataEvent
|
||||
case WAIT_OBJECT_0:
|
||||
break;
|
||||
|
||||
// terminateEvent
|
||||
case WAIT_OBJECT_0 + 1:
|
||||
running = false;
|
||||
continue;
|
||||
|
||||
default:
|
||||
hr = HRESULT_FROM_WIN32(waitResult);
|
||||
DEBUG_ERROR_HR(hr, "WaitForMultipleObjects");
|
||||
return;
|
||||
}
|
||||
|
||||
IDARG_IN_QUERY_HWCURSOR in = {};
|
||||
@ -106,7 +159,7 @@ void CIndirectMonitorContext::CursorThread()
|
||||
NTSTATUS status = IddCxMonitorQueryHardwareCursor(m_monitor, &in, &out);
|
||||
if (FAILED(status))
|
||||
{
|
||||
DBGPRINT("IddCxMonitorQueryHardwareCursor failed: %08x", status);
|
||||
DEBUG_ERROR("IddCxMonitorQueryHardwareCursor failed (0x%08x)", status);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,10 @@ class CIndirectMonitorContext
|
||||
{
|
||||
private:
|
||||
IDDCX_MONITOR m_monitor;
|
||||
|
||||
std::shared_ptr<CD3D11Device> m_dx11Device;
|
||||
std::shared_ptr<CD3D12Device> m_dx12Device;
|
||||
|
||||
CIndirectDeviceContext * m_devContext;
|
||||
std::unique_ptr<CSwapChainProcessor> m_swapChain;
|
||||
|
||||
@ -55,11 +59,7 @@ public:
|
||||
void AssignSwapChain(IDDCX_SWAPCHAIN swapChain, LUID renderAdapter, HANDLE newFrameEvent);
|
||||
void UnassignSwapChain();
|
||||
|
||||
inline void ResendLastFrame()
|
||||
{
|
||||
if (m_swapChain)
|
||||
m_swapChain->ResendLastFrame();
|
||||
}
|
||||
CIndirectDeviceContext * GetDeviceContext() { return m_devContext; }
|
||||
};
|
||||
|
||||
struct CIndirectMonitorContextWrapper
|
||||
|
126
idd/LGIdd/CInteropResource.cpp
Normal file
126
idd/LGIdd/CInteropResource.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
#include "CInteropResource.h"
|
||||
#include "CDebug.h"
|
||||
|
||||
bool CInteropResource::Init(std::shared_ptr<CD3D11Device> dx11Device, std::shared_ptr<CD3D12Device> dx12Device, ComPtr<ID3D11Texture2D> srcTex)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
D3D11_TEXTURE2D_DESC srcDesc;
|
||||
srcTex->GetDesc(&srcDesc);
|
||||
|
||||
ComPtr<IDXGIResource1> rSrcTex;
|
||||
hr = srcTex.As(&rSrcTex);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to obtain the IDXGIResource1 interface");
|
||||
return false;
|
||||
}
|
||||
|
||||
HANDLE h;
|
||||
Wrappers::HandleT<Wrappers::HandleTraits::HANDLENullTraits> sharedHandle;
|
||||
hr = rSrcTex->CreateSharedHandle(NULL, DXGI_SHARED_RESOURCE_READ, NULL, &h);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to create the shared handle");
|
||||
return false;
|
||||
}
|
||||
sharedHandle.Attach(h);
|
||||
|
||||
ComPtr<ID3D12Resource> dst;
|
||||
hr = dx12Device->GetDevice()->OpenSharedHandle(sharedHandle.Get(), IID_PPV_ARGS(&dst));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to open the D3D12Resource from the handle");
|
||||
return false;
|
||||
}
|
||||
|
||||
sharedHandle.Close();
|
||||
|
||||
ComPtr<ID3D11Fence> d11Fence;
|
||||
hr = dx11Device->GetDevice()->CreateFence(0, D3D11_FENCE_FLAG_SHARED, IID_PPV_ARGS(&d11Fence));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to create the d3d11 fence");
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = d11Fence->CreateSharedHandle(NULL, GENERIC_ALL, NULL, &h);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to create the d3d11 fence shared handle");
|
||||
return false;
|
||||
}
|
||||
sharedHandle.Attach(h);
|
||||
|
||||
ComPtr<ID3D12Fence> d12Fence;
|
||||
hr = dx12Device->GetDevice()->OpenSharedHandle(sharedHandle.Get(), IID_PPV_ARGS(&d12Fence));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "Failed to open the D3D12Fence from the handle");
|
||||
return false;
|
||||
}
|
||||
|
||||
sharedHandle.Close();
|
||||
|
||||
m_dx11Device = dx11Device;
|
||||
m_dx12Device = dx12Device;
|
||||
|
||||
memcpy(&m_format, &srcDesc, sizeof(m_format));
|
||||
m_srcTex = srcTex.Get();
|
||||
m_d12Res = dst;
|
||||
m_d11Fence = d11Fence;
|
||||
m_d12Fence = d12Fence;
|
||||
m_fenceValue = 0;
|
||||
m_ready = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CInteropResource::Reset()
|
||||
{
|
||||
m_ready = false;
|
||||
m_fenceValue = 0;
|
||||
m_d12Fence.Reset();
|
||||
m_d11Fence.Reset();
|
||||
m_d12Res .Reset();
|
||||
m_srcTex = NULL;
|
||||
memset(&m_format, 0, sizeof(m_format));
|
||||
|
||||
m_dx12Device.reset();
|
||||
m_dx11Device.reset();
|
||||
}
|
||||
|
||||
bool CInteropResource::Compare(const ComPtr<ID3D11Texture2D>& srcTex)
|
||||
{
|
||||
if (srcTex.Get() != m_srcTex)
|
||||
return false;
|
||||
|
||||
D3D11_TEXTURE2D_DESC format;
|
||||
srcTex->GetDesc(&format);
|
||||
|
||||
return
|
||||
m_format.Width == format.Width &&
|
||||
m_format.Height == format.Height &&
|
||||
m_format.Format == format.Format;
|
||||
}
|
||||
|
||||
void CInteropResource::Signal()
|
||||
{
|
||||
++m_fenceValue;
|
||||
m_dx11Device->GetContext()->Signal(m_d11Fence.Get(), m_fenceValue);
|
||||
}
|
||||
|
||||
void CInteropResource::Sync(CD3D12CommandQueue& queue)
|
||||
{
|
||||
if (m_d11Fence->GetCompletedValue() < m_fenceValue)
|
||||
queue.GetCmdQueue()->Wait(m_d12Fence.Get(), m_fenceValue);
|
||||
}
|
||||
|
||||
void CInteropResource::SetFullDamage()
|
||||
{
|
||||
m_dirtyRects[0].left = 0;
|
||||
m_dirtyRects[0].top = 0;
|
||||
m_dirtyRects[0].right = m_format.Width;
|
||||
m_dirtyRects[0].bottom = m_format.Height;
|
||||
m_nbDirtyRects = 1;
|
||||
}
|
48
idd/LGIdd/CInteropResource.h
Normal file
48
idd/LGIdd/CInteropResource.h
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <wdf.h>
|
||||
#include <wrl.h>
|
||||
#include <memory>
|
||||
|
||||
#include "CD3D11Device.h"
|
||||
#include "CD3D12Device.h"
|
||||
#include "CD3D12CommandQueue.h"
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
#define LG_MAX_DIRTY_RECTS 256
|
||||
|
||||
class CInteropResource
|
||||
{
|
||||
private:
|
||||
std::shared_ptr<CD3D11Device> m_dx11Device;
|
||||
std::shared_ptr<CD3D12Device> m_dx12Device;
|
||||
|
||||
/* this value is likely released, it is only used to check if the texture supplied
|
||||
is different, do not rely on it pointing to valid memory */
|
||||
void * m_srcTex;
|
||||
|
||||
ComPtr<ID3D12Resource > m_d12Res;
|
||||
D3D11_TEXTURE2D_DESC m_format;
|
||||
ComPtr<ID3D11Fence > m_d11Fence;
|
||||
ComPtr<ID3D12Fence > m_d12Fence;
|
||||
UINT64 m_fenceValue;
|
||||
bool m_ready;
|
||||
|
||||
RECT m_dirtyRects[LG_MAX_DIRTY_RECTS];
|
||||
unsigned m_nbDirtyRects;
|
||||
|
||||
public:
|
||||
bool Init(std::shared_ptr<CD3D11Device> dx11Device, std::shared_ptr<CD3D12Device> dx12Device, ComPtr<ID3D11Texture2D> srcTex);
|
||||
void Reset();
|
||||
|
||||
bool IsReady() { return m_ready; }
|
||||
bool Compare(const ComPtr<ID3D11Texture2D>& srcTex);
|
||||
void Signal();
|
||||
void Sync(CD3D12CommandQueue& queue);
|
||||
void SetFullDamage();
|
||||
|
||||
const ComPtr<ID3D12Resource>& GetRes() { return m_d12Res; }
|
||||
const D3D11_TEXTURE2D_DESC& GetFormat() { return m_format; }
|
||||
};
|
47
idd/LGIdd/CInteropResourcePool.cpp
Normal file
47
idd/LGIdd/CInteropResourcePool.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
#include "CInteropResourcePool.h"
|
||||
#include "CDebug.h"
|
||||
|
||||
void CInteropResourcePool::Init(std::shared_ptr<CD3D11Device> dx11Device, std::shared_ptr<CD3D12Device> dx12Device)
|
||||
{
|
||||
Reset();
|
||||
m_dx11Device = dx11Device;
|
||||
m_dx12Device = dx12Device;
|
||||
}
|
||||
|
||||
void CInteropResourcePool::Reset()
|
||||
{
|
||||
for (unsigned i = 0; i < POOL_SIZE; ++i)
|
||||
m_pool[i].Reset();
|
||||
m_dx11Device.reset();
|
||||
m_dx12Device.reset();
|
||||
}
|
||||
|
||||
CInteropResource* CInteropResourcePool::Get(ComPtr<ID3D11Texture2D> srcTex)
|
||||
{
|
||||
CInteropResource * res;
|
||||
unsigned freeSlot = POOL_SIZE;
|
||||
for (unsigned i = 0; i < POOL_SIZE; ++i)
|
||||
{
|
||||
res = &m_pool[i];
|
||||
if (!res->IsReady())
|
||||
{
|
||||
freeSlot = min(freeSlot, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (res->Compare(srcTex))
|
||||
return res;
|
||||
}
|
||||
|
||||
if (freeSlot == POOL_SIZE)
|
||||
{
|
||||
DEBUG_ERROR("Interop Resouce Pool Full");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
res = &m_pool[freeSlot];
|
||||
if (!res->Init(m_dx11Device, m_dx12Device, srcTex))
|
||||
return nullptr;
|
||||
|
||||
return res;
|
||||
}
|
26
idd/LGIdd/CInteropResourcePool.h
Normal file
26
idd/LGIdd/CInteropResourcePool.h
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <wdf.h>
|
||||
#include <wrl.h>
|
||||
#include <d3d11_4.h>
|
||||
#include "CInteropResource.h"
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
#define POOL_SIZE 10
|
||||
|
||||
class CInteropResourcePool
|
||||
{
|
||||
private:
|
||||
CInteropResource m_pool[POOL_SIZE];
|
||||
|
||||
std::shared_ptr<CD3D11Device> m_dx11Device;
|
||||
std::shared_ptr<CD3D12Device> m_dx12Device;
|
||||
|
||||
public:
|
||||
void Init(std::shared_ptr<CD3D11Device> dx11Device, std::shared_ptr<CD3D12Device> dx12Device);
|
||||
void Reset();
|
||||
|
||||
CInteropResource* Get(ComPtr<ID3D11Texture2D> srcTex);
|
||||
};
|
176
idd/LGIdd/CPipeServer.cpp
Normal file
176
idd/LGIdd/CPipeServer.cpp
Normal file
@ -0,0 +1,176 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2025 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "CPipeServer.h"
|
||||
#include "CDebug.h"
|
||||
|
||||
CPipeServer g_pipe;
|
||||
|
||||
bool CPipeServer::Init()
|
||||
{
|
||||
_DeInit();
|
||||
|
||||
m_pipe.Attach(CreateNamedPipeA(
|
||||
LG_PIPE_NAME,
|
||||
PIPE_ACCESS_DUPLEX,
|
||||
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
|
||||
1,
|
||||
1024,
|
||||
1024,
|
||||
0,
|
||||
NULL));
|
||||
|
||||
if (!m_pipe.IsValid())
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "Failed to create the named pipe");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_running = true;
|
||||
m_thread.Attach(CreateThread(
|
||||
NULL,
|
||||
0,
|
||||
_pipeThread,
|
||||
(LPVOID)this,
|
||||
0,
|
||||
NULL));
|
||||
|
||||
if (!m_thread.IsValid())
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "Failed to create the pipe thread");
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_TRACE("Pipe Initialized");
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPipeServer::_DeInit()
|
||||
{
|
||||
m_running = false;
|
||||
m_connected = false;
|
||||
|
||||
if (m_thread.IsValid())
|
||||
{
|
||||
CancelSynchronousIo(m_thread.Get());
|
||||
WaitForSingleObject(m_thread.Get(), INFINITE);
|
||||
m_thread.Close();
|
||||
}
|
||||
|
||||
if (m_pipe.IsValid())
|
||||
{
|
||||
FlushFileBuffers(m_pipe.Get());
|
||||
m_pipe.Close();
|
||||
}
|
||||
}
|
||||
|
||||
void CPipeServer::DeInit()
|
||||
{
|
||||
DEBUG_TRACE("Pipe Stopping");
|
||||
_DeInit();
|
||||
DEBUG_TRACE("Pipe Stopped");
|
||||
}
|
||||
|
||||
void CPipeServer::Thread()
|
||||
{
|
||||
DEBUG_TRACE("Pipe thread started");
|
||||
while(m_running)
|
||||
{
|
||||
m_connected = false;
|
||||
bool result = ConnectNamedPipe(m_pipe.Get(), NULL);
|
||||
DWORD err = GetLastError();
|
||||
if (!result && err != ERROR_PIPE_CONNECTED)
|
||||
{
|
||||
// if graceful shutdown
|
||||
if ((err == ERROR_OPERATION_ABORTED && !m_running) ||
|
||||
err == ERROR_NO_DATA)
|
||||
break;
|
||||
|
||||
// if timeout
|
||||
if (err == ERROR_SEM_TIMEOUT)
|
||||
continue;
|
||||
|
||||
DEBUG_FATAL_HR(err, "Error connecting to the named pipe");
|
||||
break;
|
||||
}
|
||||
|
||||
DEBUG_TRACE("Client connected");
|
||||
|
||||
m_connected = true;
|
||||
while (m_running && m_connected)
|
||||
{
|
||||
//TODO: Read messages from the client
|
||||
Sleep(1000);
|
||||
}
|
||||
|
||||
DEBUG_TRACE("Client disconnected");
|
||||
DisconnectNamedPipe(m_pipe.Get());
|
||||
}
|
||||
|
||||
m_running = false;
|
||||
m_connected = false;
|
||||
DEBUG_TRACE("Pipe thread shutdown");
|
||||
}
|
||||
|
||||
void CPipeServer::WriteMsg(LGPipeMsg & msg)
|
||||
{
|
||||
DWORD written;
|
||||
if (!WriteFile(m_pipe.Get(), &msg, sizeof(msg), &written, NULL))
|
||||
{
|
||||
DWORD err = GetLastError();
|
||||
if (err == ERROR_BROKEN_PIPE || err == ERROR_NO_DATA)
|
||||
{
|
||||
DEBUG_WARN_HR(err, "Client disconnected, failed to write");
|
||||
m_connected = false;
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_WARN_HR(err, "WriteFile failed on the pipe");
|
||||
return;
|
||||
}
|
||||
|
||||
FlushFileBuffers(m_pipe.Get());
|
||||
}
|
||||
|
||||
void CPipeServer::SetCursorPos(uint32_t x, uint32_t y)
|
||||
{
|
||||
if (!m_connected)
|
||||
return;
|
||||
|
||||
LGPipeMsg msg;
|
||||
msg.size = sizeof(msg);
|
||||
msg.type = LGPipeMsg::SETCURSORPOS;
|
||||
msg.curorPos.x = x;
|
||||
msg.curorPos.y = y;
|
||||
WriteMsg(msg);
|
||||
}
|
||||
|
||||
void CPipeServer::SetDisplayMode(uint32_t width, uint32_t height)
|
||||
{
|
||||
if (!m_connected)
|
||||
return;
|
||||
|
||||
LGPipeMsg msg;
|
||||
msg.size = sizeof(msg);
|
||||
msg.type = LGPipeMsg::SETDISPLAYMODE;
|
||||
msg.displayMode.width = width;
|
||||
msg.displayMode.height = height;
|
||||
WriteMsg(msg);
|
||||
}
|
60
idd/LGIdd/CPipeServer.h
Normal file
60
idd/LGIdd/CPipeServer.h
Normal file
@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2025 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <wdf.h>
|
||||
#include <stdint.h>
|
||||
#include <wrl.h>
|
||||
|
||||
#include "PipeMsg.h"
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace Microsoft::WRL::Wrappers;
|
||||
using namespace Microsoft::WRL::Wrappers::HandleTraits;
|
||||
|
||||
class CPipeServer
|
||||
{
|
||||
private:
|
||||
HandleT<HANDLENullTraits> m_pipe;
|
||||
HandleT<HANDLENullTraits> m_thread;
|
||||
|
||||
bool m_running = false;
|
||||
bool m_connected = false;
|
||||
|
||||
void _DeInit();
|
||||
|
||||
static DWORD WINAPI _pipeThread(LPVOID lpParam) { ((CPipeServer*)lpParam)->Thread(); return 0; }
|
||||
void Thread();
|
||||
|
||||
void WriteMsg(LGPipeMsg & msg);
|
||||
|
||||
public:
|
||||
~CPipeServer() { DeInit(); }
|
||||
|
||||
bool Init();
|
||||
void DeInit();
|
||||
|
||||
void SetCursorPos(uint32_t x, uint32_t y);
|
||||
void SetDisplayMode(uint32_t width, uint32_t height);
|
||||
};
|
||||
|
||||
extern CPipeServer g_pipe;
|
@ -20,7 +20,7 @@
|
||||
|
||||
#include "CPlatformInfo.h"
|
||||
|
||||
#include "Debug.h"
|
||||
#include "CDebug.h"
|
||||
#include <Windows.h>
|
||||
|
||||
size_t CPlatformInfo::m_pageSize = 0;
|
||||
@ -60,7 +60,7 @@ void CPlatformInfo::Init()
|
||||
if (status != ERROR_SUCCESS)
|
||||
{
|
||||
m_productName = "Unknown";
|
||||
DBGPRINT("Failed to read the ProductName");
|
||||
DEBUG_ERROR("Failed to read the ProductName");
|
||||
}
|
||||
else
|
||||
m_productName.resize(bufferSize - 1); // remove the double null termination
|
||||
@ -167,14 +167,14 @@ void CPlatformInfo::InitUUID()
|
||||
smbData = (RawSMBIOSData*)new BYTE[smbDataSize];
|
||||
if (!smbData)
|
||||
{
|
||||
DBGPRINT("out of memory");
|
||||
DEBUG_ERROR("out of memory");
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetSystemFirmwareTable(TABLE_SIG("RSMB"), 0, smbData, smbDataSize)
|
||||
!= smbDataSize)
|
||||
{
|
||||
DBGPRINT("Failed to read the RSMB table");
|
||||
DEBUG_ERROR("Failed to read the RSMB table");
|
||||
delete[] smbData;
|
||||
return;
|
||||
}
|
||||
@ -220,7 +220,7 @@ void CPlatformInfo::InitCPUInfo()
|
||||
if (status != ERROR_SUCCESS)
|
||||
{
|
||||
m_model = "Unknown";
|
||||
DBGPRINT("Failed to read the CPU Model");
|
||||
DEBUG_ERROR("Failed to read the CPU Model");
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -233,20 +233,20 @@ void CPlatformInfo::InitCPUInfo()
|
||||
GetLogicalProcessorInformationEx(RelationAll, nullptr, &cb);
|
||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||
{
|
||||
DBGPRINT("Failed to call GetLogicalProcessorInformationEx");
|
||||
DEBUG_ERROR("Failed to call GetLogicalProcessorInformationEx");
|
||||
return;
|
||||
}
|
||||
|
||||
BYTE * buffer = static_cast<BYTE *>(_malloca(cb));
|
||||
if (!buffer)
|
||||
{
|
||||
DBGPRINT("Failed to allocate buffer for processor information");
|
||||
DEBUG_ERROR("Failed to allocate buffer for processor information");
|
||||
return;
|
||||
}
|
||||
if (!GetLogicalProcessorInformationEx(RelationAll,
|
||||
(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)buffer, &cb))
|
||||
{
|
||||
DBGPRINT("Failed to call GetLogicalProcessorInformationEx");
|
||||
DEBUG_ERROR("Failed to call GetLogicalProcessorInformationEx");
|
||||
_freea(buffer);
|
||||
return;
|
||||
}
|
||||
|
@ -21,29 +21,21 @@
|
||||
#include "CSwapChainProcessor.h"
|
||||
|
||||
#include <avrt.h>
|
||||
#include "Debug.h"
|
||||
|
||||
#define LOCK(lock) \
|
||||
while (InterlockedCompareExchange((volatile LONG*)&(lock), 1, 0) != 0) {};
|
||||
|
||||
#define UNLOCK(lock) \
|
||||
InterlockedExchange((volatile LONG*)&(lock), 0);
|
||||
|
||||
#define LOCK_CONTEXT() LOCK(m_contextLock);
|
||||
#define UNLOCK_CONTEXT() UNLOCK(m_contextLock);
|
||||
#define LOCK_ST(st) LOCK((st).lock);
|
||||
#define UNLOCK_ST(st) UNLOCK((st).lock);
|
||||
#include "CDebug.h"
|
||||
|
||||
CSwapChainProcessor::CSwapChainProcessor(CIndirectDeviceContext* devContext, IDDCX_SWAPCHAIN hSwapChain,
|
||||
std::shared_ptr<Direct3DDevice> device, HANDLE newFrameEvent) :
|
||||
std::shared_ptr<CD3D11Device> dx11Device, std::shared_ptr<CD3D12Device> dx12Device, HANDLE newFrameEvent) :
|
||||
m_devContext(devContext),
|
||||
m_hSwapChain(hSwapChain),
|
||||
m_device(device),
|
||||
m_dx11Device(dx11Device),
|
||||
m_dx12Device(dx12Device),
|
||||
m_newFrameEvent(newFrameEvent)
|
||||
{
|
||||
{
|
||||
m_resPool.Init(dx11Device, dx12Device);
|
||||
m_fbPool.Init(this);
|
||||
|
||||
m_terminateEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr));
|
||||
m_thread[0].Attach(CreateThread(nullptr, 0, _SwapChainThread, this, 0, nullptr));
|
||||
m_thread[1].Attach(CreateThread(nullptr, 0, _FrameThread , this, 0, nullptr));
|
||||
}
|
||||
|
||||
CSwapChainProcessor::~CSwapChainProcessor()
|
||||
@ -54,12 +46,8 @@ CSwapChainProcessor::~CSwapChainProcessor()
|
||||
if (m_thread[1].Get())
|
||||
WaitForSingleObject(m_thread[1].Get(), INFINITE);
|
||||
|
||||
for(int i = 0; i < STAGING_TEXTURES; ++i)
|
||||
if (m_cpuTex[i].map.pData)
|
||||
{
|
||||
m_device->m_context->Unmap(m_cpuTex[i].tex.Get(), 0);
|
||||
m_cpuTex[i].map.pData = nullptr;
|
||||
}
|
||||
m_resPool.Reset();
|
||||
m_fbPool.Reset();
|
||||
}
|
||||
|
||||
DWORD CALLBACK CSwapChainProcessor::_SwapChainThread(LPVOID arg)
|
||||
@ -73,7 +61,7 @@ void CSwapChainProcessor::SwapChainThread()
|
||||
DWORD avTask = 0;
|
||||
HANDLE avTaskHandle = AvSetMmThreadCharacteristicsW(L"Distribution", &avTask);
|
||||
|
||||
DBGPRINT("Start");
|
||||
DEBUG_INFO("Start Thread");
|
||||
SwapChainThreadCore();
|
||||
|
||||
WdfObjectDelete((WDFOBJECT)m_hSwapChain);
|
||||
@ -83,54 +71,48 @@ void CSwapChainProcessor::SwapChainThread()
|
||||
}
|
||||
|
||||
void CSwapChainProcessor::SwapChainThreadCore()
|
||||
{
|
||||
Microsoft::WRL::ComPtr<IDXGIDevice> dxgiDevice;
|
||||
HRESULT hr = m_device->m_device.As(&dxgiDevice);
|
||||
{
|
||||
ComPtr<IDXGIDevice> dxgiDevice;
|
||||
HRESULT hr = m_dx11Device->GetDevice().As(&dxgiDevice);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DBGPRINT("Failed to get the dxgiDevice");
|
||||
DEBUG_ERROR_HR(hr, "Failed to get the dxgiDevice");
|
||||
return;
|
||||
}
|
||||
|
||||
if (IDD_IS_FUNCTION_AVAILABLE(IddCxSetRealtimeGPUPriority))
|
||||
{
|
||||
DBGPRINT("Using IddCxSetRealtimeGPUPriority");
|
||||
DEBUG_INFO("Using IddCxSetRealtimeGPUPriority");
|
||||
IDARG_IN_SETREALTIMEGPUPRIORITY arg;
|
||||
arg.pDevice = dxgiDevice.Get();
|
||||
if (FAILED(IddCxSetRealtimeGPUPriority(m_hSwapChain, &arg)))
|
||||
DBGPRINT("Failed to set realtime GPU thread priority");
|
||||
hr = IddCxSetRealtimeGPUPriority(m_hSwapChain, &arg);
|
||||
if (FAILED(hr))
|
||||
DEBUG_ERROR_HR(hr, "Failed to set realtime GPU thread priority");
|
||||
}
|
||||
else
|
||||
{
|
||||
DBGPRINT("Using SetGPUThreadPriority");
|
||||
DEBUG_INFO("Using SetGPUThreadPriority");
|
||||
dxgiDevice->SetGPUThreadPriority(7);
|
||||
}
|
||||
|
||||
IDARG_IN_SWAPCHAINSETDEVICE setDevice = {};
|
||||
setDevice.pDevice = dxgiDevice.Get();
|
||||
|
||||
LOCK_CONTEXT();
|
||||
hr = IddCxSwapChainSetDevice(m_hSwapChain, &setDevice);
|
||||
UNLOCK_CONTEXT();
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DBGPRINT("IddCxSwapChainSetDevice Failed (%08x)", hr);
|
||||
DEBUG_ERROR_HR(hr, "IddCxSwapChainSetDevice Failed");
|
||||
return;
|
||||
}
|
||||
|
||||
UINT lastFrameNumber = 0;
|
||||
for (;;)
|
||||
{
|
||||
ComPtr<IDXGIResource> acquiredBuffer;
|
||||
IDARG_OUT_RELEASEANDACQUIREBUFFER buffer = {};
|
||||
|
||||
LOCK_CONTEXT();
|
||||
hr = IddCxSwapChainReleaseAndAcquireBuffer(m_hSwapChain, &buffer);
|
||||
|
||||
if (hr == E_PENDING)
|
||||
{
|
||||
UNLOCK_CONTEXT();
|
||||
HANDLE waitHandles[] =
|
||||
{
|
||||
m_newFrameEvent,
|
||||
@ -152,153 +134,128 @@ void CSwapChainProcessor::SwapChainThreadCore()
|
||||
if (buffer.MetaData.PresentationFrameNumber != lastFrameNumber)
|
||||
{
|
||||
lastFrameNumber = buffer.MetaData.PresentationFrameNumber;
|
||||
if (m_copyCount < STAGING_TEXTURES)
|
||||
{
|
||||
acquiredBuffer.Attach(buffer.MetaData.pSurface);
|
||||
SwapChainNewFrame(acquiredBuffer);
|
||||
acquiredBuffer.Reset();
|
||||
}
|
||||
}
|
||||
SwapChainNewFrame(buffer.MetaData.pSurface);
|
||||
|
||||
hr = IddCxSwapChainFinishedProcessingFrame(m_hSwapChain);
|
||||
UNLOCK_CONTEXT();
|
||||
if (FAILED(hr))
|
||||
break;
|
||||
// report that all GPU processing for this frame has been queued
|
||||
hr = IddCxSwapChainFinishedProcessingFrame(m_hSwapChain);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_ERROR_HR(hr, "IddCxSwapChainFinishedProcessingFrame Failed");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UNLOCK_CONTEXT();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer)
|
||||
void CSwapChainProcessor::CompletionFunction(
|
||||
CD3D12CommandQueue * queue, bool result, void * param1, void * param2)
|
||||
{
|
||||
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture;
|
||||
if (FAILED(acquiredBuffer->QueryInterface(__uuidof(ID3D11Texture2D), (void**)&texture)))
|
||||
UNREFERENCED_PARAMETER(queue);
|
||||
|
||||
auto sc = (CSwapChainProcessor *)param1;
|
||||
auto fbRes = (CFrameBufferResource*)param2;
|
||||
|
||||
// fail gracefully
|
||||
if (!result)
|
||||
{
|
||||
DBGPRINT("Failed to obtain the ID3D11Texture2D from the acquiredBuffer");
|
||||
sc->m_devContext->FinalizeFrameBuffer(fbRes->GetFrameIndex());
|
||||
return;
|
||||
}
|
||||
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
texture->GetDesc(&desc);
|
||||
|
||||
StagingTexture &st = m_cpuTex[m_texWIndex];
|
||||
if (st.map.pData)
|
||||
{
|
||||
m_device->m_context->Unmap(st.tex.Get(), 0);
|
||||
st.map.pData = nullptr;
|
||||
}
|
||||
|
||||
if (!SetupStagingTexture(st, desc.Width, desc.Height, desc.Format))
|
||||
return;
|
||||
|
||||
m_device->m_context->CopyResource(st.tex.Get(), texture.Get());
|
||||
|
||||
InterlockedAdd(&m_copyCount, 1);
|
||||
if (++m_texWIndex == STAGING_TEXTURES)
|
||||
m_texWIndex = 0;
|
||||
if (sc->m_dx12Device->IsIndirectCopy())
|
||||
sc->m_devContext->WriteFrameBuffer(
|
||||
fbRes->GetFrameIndex(),
|
||||
fbRes->GetMap(), 0, fbRes->GetFrameSize(), true);
|
||||
else
|
||||
sc->m_devContext->FinalizeFrameBuffer(fbRes->GetFrameIndex());
|
||||
}
|
||||
|
||||
DWORD CALLBACK CSwapChainProcessor::_FrameThread(LPVOID arg)
|
||||
bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer)
|
||||
{
|
||||
reinterpret_cast<CSwapChainProcessor*>(arg)->FrameThread();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CSwapChainProcessor::FrameThread()
|
||||
{
|
||||
for(;;)
|
||||
ComPtr<ID3D11Texture2D> texture;
|
||||
HRESULT hr = acquiredBuffer.As(&texture);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
if (WaitForSingleObject(m_terminateEvent.Get(), 0) == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
if (!m_copyCount)
|
||||
{
|
||||
Sleep(0);
|
||||
continue;
|
||||
}
|
||||
|
||||
StagingTexture & st = m_cpuTex[m_texRIndex];
|
||||
LOCK(st);
|
||||
|
||||
LOCK_CONTEXT();
|
||||
HRESULT status = m_device->m_context->Map(st.tex.Get(), 0, D3D11_MAP_READ,
|
||||
D3D11_MAP_FLAG_DO_NOT_WAIT, &st.map);
|
||||
UNLOCK_CONTEXT();
|
||||
|
||||
if (FAILED(status))
|
||||
{
|
||||
UNLOCK(st);
|
||||
if (status == DXGI_ERROR_WAS_STILL_DRAWING)
|
||||
continue;
|
||||
|
||||
DBGPRINT("Failed to map staging texture");
|
||||
|
||||
InterlockedAdd(&m_copyCount, -1);
|
||||
if (++m_texRIndex == STAGING_TEXTURES)
|
||||
m_texRIndex = 0;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
m_devContext->SendFrame(st.width, st.height, st.map.RowPitch, st.format, st.map.pData);
|
||||
InterlockedAdd(&m_copyCount, -1);
|
||||
m_lastIndex = m_texRIndex;
|
||||
if (++m_texRIndex == STAGING_TEXTURES)
|
||||
m_texRIndex = 0;
|
||||
|
||||
UNLOCK(st);
|
||||
}
|
||||
}
|
||||
|
||||
bool CSwapChainProcessor::SetupStagingTexture(StagingTexture & st, int width, int height, DXGI_FORMAT format)
|
||||
{
|
||||
if (st.width == width && st.height == height && st.format == format)
|
||||
return true;
|
||||
|
||||
st.tex.Reset();
|
||||
st.width = width;
|
||||
st.height = height;
|
||||
st.format = format;
|
||||
|
||||
D3D11_TEXTURE2D_DESC desc = {};
|
||||
desc.Width = width;
|
||||
desc.Height = height;
|
||||
desc.MipLevels = 1;
|
||||
desc.ArraySize = 1;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.SampleDesc.Quality = 0;
|
||||
desc.Usage = D3D11_USAGE_STAGING;
|
||||
desc.Format = format;
|
||||
desc.BindFlags = 0;
|
||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
desc.MiscFlags = 0;
|
||||
|
||||
if (FAILED(m_device->m_device->CreateTexture2D(&desc, nullptr, &st.tex)))
|
||||
{
|
||||
DBGPRINT("Failed to create staging texture");
|
||||
DEBUG_ERROR_HR(hr, "Failed to obtain the ID3D11Texture2D from the acquiredBuffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CSwapChainProcessor::ResendLastFrame()
|
||||
{
|
||||
LOCK_CONTEXT()
|
||||
StagingTexture & st = m_cpuTex[m_lastIndex];
|
||||
LOCK_ST(st);
|
||||
UNLOCK_CONTEXT();
|
||||
|
||||
if (!st.map.pData)
|
||||
CInteropResource * srcRes = m_resPool.Get(texture);
|
||||
if (!srcRes)
|
||||
{
|
||||
UNLOCK_ST(st);
|
||||
return;
|
||||
DEBUG_ERROR("Failed to get a CInteropResource from the pool");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_devContext->SendFrame(st.width, st.height, st.map.RowPitch, st.format, st.map.pData);
|
||||
UNLOCK_ST(st);
|
||||
}
|
||||
/**
|
||||
* Even though we have not performed any copy/draw operations we still need to
|
||||
* use a fence. Because we share this texture with DirectX12 it is able to
|
||||
* read from it before the desktop duplication API has finished updating it.
|
||||
*/
|
||||
srcRes->Signal();
|
||||
|
||||
//FIXME: handle dirty rects
|
||||
srcRes->SetFullDamage();
|
||||
|
||||
D3D12_RESOURCE_DESC desc = srcRes->GetRes()->GetDesc();
|
||||
D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout;
|
||||
m_dx12Device->GetDevice()->GetCopyableFootprints(
|
||||
&desc,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
&layout,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
auto buffer = m_devContext->PrepareFrameBuffer(
|
||||
(int)desc.Width,
|
||||
(int)desc.Height,
|
||||
(int)layout.Footprint.RowPitch,
|
||||
desc.Format);
|
||||
|
||||
if (!buffer.mem)
|
||||
return false;
|
||||
|
||||
CFrameBufferResource * fbRes = m_fbPool.Get(buffer,
|
||||
(size_t)layout.Footprint.RowPitch * desc.Height);
|
||||
|
||||
if (!fbRes)
|
||||
{
|
||||
DEBUG_ERROR("Failed to get a CFrameBufferResource from the pool");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto copyQueue = m_dx12Device->GetCopyQueue();
|
||||
if (!copyQueue)
|
||||
{
|
||||
DEBUG_ERROR("Failed to get a CopyQueue");
|
||||
return false;
|
||||
}
|
||||
|
||||
copyQueue->SetCompletionCallback(&CompletionFunction, this, fbRes);
|
||||
|
||||
D3D12_TEXTURE_COPY_LOCATION srcLoc = {};
|
||||
srcLoc.pResource = srcRes->GetRes().Get();
|
||||
srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
||||
srcLoc.SubresourceIndex = 0;
|
||||
|
||||
D3D12_TEXTURE_COPY_LOCATION dstLoc = {};
|
||||
dstLoc.pResource = fbRes->Get().Get();
|
||||
dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
||||
dstLoc.PlacedFootprint = layout;
|
||||
|
||||
srcRes->Sync(*copyQueue);
|
||||
copyQueue->GetGfxList()->CopyTextureRegion(
|
||||
&dstLoc, 0, 0, 0, &srcLoc, NULL);
|
||||
copyQueue->Execute();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -20,8 +20,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Direct3DDevice.h"
|
||||
#include "CD3D11Device.h"
|
||||
#include "CD3D12Device.h"
|
||||
#include "CIndirectDeviceContext.h"
|
||||
#include "CInteropResourcePool.h"
|
||||
#include "CFrameBufferPool.h"
|
||||
|
||||
#include <Windows.h>
|
||||
#include <wrl.h>
|
||||
@ -37,45 +40,30 @@ class CSwapChainProcessor
|
||||
private:
|
||||
CIndirectDeviceContext * m_devContext;
|
||||
IDDCX_SWAPCHAIN m_hSwapChain;
|
||||
std::shared_ptr<Direct3DDevice> m_device;
|
||||
std::shared_ptr<CD3D11Device> m_dx11Device;
|
||||
std::shared_ptr<CD3D12Device> m_dx12Device;
|
||||
HANDLE m_newFrameEvent;
|
||||
|
||||
CInteropResourcePool m_resPool;
|
||||
CFrameBufferPool m_fbPool;
|
||||
|
||||
Wrappers::HandleT<Wrappers::HandleTraits::HANDLENullTraits> m_thread[2];
|
||||
Wrappers::Event m_terminateEvent;
|
||||
|
||||
static DWORD CALLBACK _SwapChainThread(LPVOID arg);
|
||||
static DWORD CALLBACK _FrameThread(LPVOID arg);
|
||||
|
||||
void SwapChainThread();
|
||||
void SwapChainThreadCore();
|
||||
void SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer);
|
||||
|
||||
void FrameThread();
|
||||
|
||||
struct StagingTexture
|
||||
{
|
||||
volatile LONG lock = 0;
|
||||
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN;
|
||||
Microsoft::WRL::ComPtr<ID3D11Texture2D> tex;
|
||||
D3D11_MAPPED_SUBRESOURCE map = {};
|
||||
};
|
||||
|
||||
StagingTexture m_cpuTex[STAGING_TEXTURES] = {};
|
||||
volatile LONG m_copyCount = 0;
|
||||
volatile LONG m_contextLock = 0;
|
||||
int m_texRIndex = 0;
|
||||
int m_texWIndex = 0;
|
||||
int m_lastIndex = 0;
|
||||
|
||||
bool SetupStagingTexture(StagingTexture & st, int width, int height, DXGI_FORMAT format);
|
||||
static void CompletionFunction(
|
||||
CD3D12CommandQueue * queue, bool result, void * param1, void * param2);
|
||||
bool SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer);
|
||||
|
||||
public:
|
||||
CSwapChainProcessor(CIndirectDeviceContext * devContext, IDDCX_SWAPCHAIN hSwapChain,
|
||||
std::shared_ptr<Direct3DDevice> device, HANDLE newFrameEvent);
|
||||
std::shared_ptr<CD3D11Device> dx11Device, std::shared_ptr<CD3D12Device> dx12Device, HANDLE newFrameEvent);
|
||||
~CSwapChainProcessor();
|
||||
|
||||
void ResendLastFrame();
|
||||
};
|
||||
CIndirectDeviceContext * GetDevice() { return m_devContext; }
|
||||
std::shared_ptr<CD3D12Device> GetD3D12Device() { return m_dx12Device; }
|
||||
};
|
||||
|
@ -1,52 +0,0 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2025 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <Windows.h>
|
||||
#include <malloc.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
/* credit: https://stackoverflow.com/questions/29049686/is-there-a-better-way-to-pass-formatted-output-to-outputdebugstring */
|
||||
VOID _DBGPRINT(PCSTR kwszFunction, INT iLineNumber, LPCSTR kszDebugFormatString, ...) \
|
||||
{
|
||||
INT cbFormatString = 0;
|
||||
va_list args;
|
||||
PCHAR szDebugString = NULL;
|
||||
size_t st_Offset = 0;
|
||||
|
||||
va_start(args, kszDebugFormatString);
|
||||
|
||||
cbFormatString = _scprintf("[%s:%d] ", kwszFunction, iLineNumber) * sizeof(CHAR);
|
||||
cbFormatString += _vscprintf(kszDebugFormatString, args) * sizeof(CHAR) + 2;
|
||||
|
||||
/* Depending on the size of the format string, allocate space on the stack or the heap. */
|
||||
szDebugString = (PCHAR)_malloca(cbFormatString);
|
||||
if (!szDebugString)
|
||||
return;
|
||||
|
||||
/* Populate the buffer with the contents of the format string. */
|
||||
StringCbPrintfA(szDebugString, cbFormatString, "[%s:%d] ", kwszFunction, iLineNumber);
|
||||
StringCbLengthA(szDebugString, cbFormatString, &st_Offset);
|
||||
StringCbVPrintfA(&szDebugString[st_Offset / sizeof(CHAR)], cbFormatString - st_Offset, kszDebugFormatString, args);
|
||||
|
||||
OutputDebugStringA(szDebugString);
|
||||
|
||||
_freea(szDebugString);
|
||||
va_end(args);
|
||||
}
|
@ -29,14 +29,15 @@
|
||||
#include <avrt.h>
|
||||
#include <wrl.h>
|
||||
|
||||
#include "Debug.h"
|
||||
#include "CDebug.h"
|
||||
#include "CIndirectDeviceContext.h"
|
||||
#include "CIndirectMonitorContext.h"
|
||||
|
||||
WDFDEVICE l_wdfDevice = nullptr;
|
||||
|
||||
NTSTATUS LGIddDeviceD0Entry(WDFDEVICE device, WDF_POWER_DEVICE_STATE previousState)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(previousState);
|
||||
UNREFERENCED_PARAMETER(device);
|
||||
|
||||
auto * wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(device);
|
||||
wrapper->context->InitAdapter();
|
||||
@ -81,66 +82,33 @@ static inline void FillSignalInfo(DISPLAYCONFIG_VIDEO_SIGNAL_INFO & mode, DWORD
|
||||
NTSTATUS LGIddParseMonitorDescription(const IDARG_IN_PARSEMONITORDESCRIPTION* inArgs,
|
||||
IDARG_OUT_PARSEMONITORDESCRIPTION* outArgs)
|
||||
{
|
||||
outArgs->MonitorModeBufferOutputCount = ARRAYSIZE(DisplayModes);
|
||||
if (inArgs->MonitorModeBufferInputCount < ARRAYSIZE(DisplayModes))
|
||||
return (inArgs->MonitorModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS;
|
||||
|
||||
for (UINT i = 0; i < ARRAYSIZE(DisplayModes); ++i)
|
||||
{
|
||||
inArgs->pMonitorModes[i].Size = sizeof(IDDCX_MONITOR_MODE);
|
||||
inArgs->pMonitorModes[i].Origin = IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR;
|
||||
FillSignalInfo(inArgs->pMonitorModes[i].MonitorVideoSignalInfo,
|
||||
DisplayModes[i][0], DisplayModes[i][1], DisplayModes[i][2], true);
|
||||
}
|
||||
if (!l_wdfDevice)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
outArgs->PreferredMonitorModeIdx = PreferredDisplayMode;
|
||||
return STATUS_SUCCESS;
|
||||
auto* wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(l_wdfDevice);
|
||||
return wrapper->context->ParseMonitorDescription(inArgs, outArgs);
|
||||
}
|
||||
|
||||
NTSTATUS LGIddMonitorGetDefaultModes(IDDCX_MONITOR monitor, const IDARG_IN_GETDEFAULTDESCRIPTIONMODES * inArgs,
|
||||
IDARG_OUT_GETDEFAULTDESCRIPTIONMODES * outArgs)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(monitor);
|
||||
|
||||
outArgs->DefaultMonitorModeBufferOutputCount = ARRAYSIZE(DisplayModes);
|
||||
if (inArgs->DefaultMonitorModeBufferInputCount < ARRAYSIZE(DisplayModes))
|
||||
return (inArgs->DefaultMonitorModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS;
|
||||
|
||||
for (UINT i = 0; i < ARRAYSIZE(DisplayModes); ++i)
|
||||
{
|
||||
inArgs->pDefaultMonitorModes[i].Size = sizeof(IDDCX_MONITOR_MODE);
|
||||
inArgs->pDefaultMonitorModes[i].Origin = IDDCX_MONITOR_MODE_ORIGIN_DRIVER;
|
||||
FillSignalInfo(inArgs->pDefaultMonitorModes[i].MonitorVideoSignalInfo,
|
||||
DisplayModes[i][0], DisplayModes[i][1], DisplayModes[i][2], true);
|
||||
}
|
||||
|
||||
outArgs->PreferredMonitorModeIdx = PreferredDisplayMode;
|
||||
return STATUS_SUCCESS;
|
||||
auto* wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor);
|
||||
return wrapper->context->GetDeviceContext()->MonitorGetDefaultModes(inArgs, outArgs);
|
||||
}
|
||||
|
||||
NTSTATUS LGIddMonitorQueryTargetModes(IDDCX_MONITOR monitor, const IDARG_IN_QUERYTARGETMODES * inArgs,
|
||||
IDARG_OUT_QUERYTARGETMODES * outArgs)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(monitor);
|
||||
|
||||
outArgs->TargetModeBufferOutputCount = ARRAYSIZE(DisplayModes);
|
||||
if (inArgs->TargetModeBufferInputCount < ARRAYSIZE(DisplayModes))
|
||||
return (inArgs->TargetModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS;
|
||||
|
||||
for (UINT i = 0; i < ARRAYSIZE(DisplayModes); ++i)
|
||||
{
|
||||
inArgs->pTargetModes[i].Size = sizeof(IDDCX_TARGET_MODE);
|
||||
FillSignalInfo(inArgs->pTargetModes[i].TargetVideoSignalInfo.targetVideoSignalInfo,
|
||||
DisplayModes[i][0], DisplayModes[i][1], DisplayModes[i][2], false);
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
auto* wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor);
|
||||
return wrapper->context->GetDeviceContext()->MonitorQueryTargetModes(inArgs, outArgs);
|
||||
}
|
||||
|
||||
NTSTATUS LGIddMonitorAssignSwapChain(IDDCX_MONITOR monitor, const IDARG_IN_SETSWAPCHAIN* inArgs)
|
||||
{
|
||||
auto * wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor);
|
||||
wrapper->context->AssignSwapChain(inArgs->hSwapChain, inArgs->RenderAdapterLuid, inArgs->hNextSurfaceAvailable);
|
||||
wrapper->context->AssignSwapChain(
|
||||
inArgs->hSwapChain, inArgs->RenderAdapterLuid, inArgs->hNextSurfaceAvailable);
|
||||
wrapper->context->GetDeviceContext()->OnAssignSwapChain();
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@ -148,6 +116,7 @@ NTSTATUS LGIddMonitorUnassignSwapChain(IDDCX_MONITOR monitor)
|
||||
{
|
||||
auto* wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor);
|
||||
wrapper->context->UnassignSwapChain();
|
||||
wrapper->context->GetDeviceContext()->OnUnassignedSwapChain();
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@ -158,10 +127,10 @@ NTSTATUS LGIddCreateDevice(_Inout_ PWDFDEVICE_INIT deviceInit)
|
||||
status = IddCxGetVersion(&ver);
|
||||
if (FAILED(status))
|
||||
{
|
||||
DBGPRINT("IddCxGetVersion Failed");
|
||||
DEBUG_ERROR("IddCxGetVersion Failed");
|
||||
return status;
|
||||
}
|
||||
DBGPRINT("Version: 0x%04x", ver.IddCxVersion);
|
||||
DEBUG_INFO("Version: 0x%04x", ver.IddCxVersion);
|
||||
|
||||
WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
|
||||
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
|
||||
@ -189,6 +158,7 @@ NTSTATUS LGIddCreateDevice(_Inout_ PWDFDEVICE_INIT deviceInit)
|
||||
auto * wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(object);
|
||||
if (wrapper)
|
||||
wrapper->Cleanup();
|
||||
l_wdfDevice = nullptr;
|
||||
};
|
||||
|
||||
WDFDEVICE device = nullptr;
|
||||
@ -196,6 +166,13 @@ NTSTATUS LGIddCreateDevice(_Inout_ PWDFDEVICE_INIT deviceInit)
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
/*
|
||||
* Because we are limited to IddCx 1.5 to retain Windows 10 support we have
|
||||
* no way of getting the device context in `LGIdddapterCommitModes`, as such
|
||||
* we need to store this for later.
|
||||
*/
|
||||
l_wdfDevice = device;
|
||||
|
||||
status = IddCxDeviceInitialize(device);
|
||||
|
||||
auto wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(device);
|
||||
|
@ -24,33 +24,6 @@
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#if 1
|
||||
const DWORD DisplayModes[][3] =
|
||||
{
|
||||
{7680, 4800, 120}, {7680, 4320, 120}, {6016, 3384, 120}, {5760, 3600, 120},
|
||||
{5760, 3240, 120}, {5120, 2800, 120}, {4096, 2560, 120}, {4096, 2304, 120},
|
||||
{3840, 2400, 120}, {3840, 2160, 120}, {3200, 2400, 120}, {3200, 1800, 120},
|
||||
{3008, 1692, 120}, {2880, 1800, 120}, {2880, 1620, 120}, {2560, 1600, 120},
|
||||
{2560, 1440, 120}, {1920, 1440, 120}, {1920, 1200, 120}, {1920, 1080, 120},
|
||||
{1600, 1200, 120}, {1600, 1024, 120}, {1600, 1050, 120}, {1600, 900 , 120},
|
||||
{1440, 900 , 120}, {1400, 1050, 120}, {1366, 768 , 120}, {1360, 768 , 120},
|
||||
{1280, 1024, 120}, {1280, 960 , 120}, {1280, 800 , 120}, {1280, 768 , 120},
|
||||
{1280, 720 , 120}, {1280, 600 , 120}, {1152, 864 , 120}, {1024, 768 , 120},
|
||||
{800 , 600 , 120}, {640 , 480 , 120}
|
||||
};
|
||||
|
||||
const DWORD PreferredDisplayMode = 19;
|
||||
#else
|
||||
const DWORD DisplayModes[][3] =
|
||||
{
|
||||
{ 2560, 1440, 144 },
|
||||
{ 1920, 1080, 60 },
|
||||
{ 1024, 768, 60 },
|
||||
};
|
||||
|
||||
const DWORD PreferredDisplayMode = 0;
|
||||
#endif
|
||||
|
||||
typedef struct _DEVICE_CONTEXT
|
||||
{
|
||||
ULONG PrivateDeviceData; // just a placeholder
|
||||
|
@ -21,20 +21,33 @@
|
||||
#include "driver.h"
|
||||
#include "driver.tmh"
|
||||
|
||||
#include "CDebug.h"
|
||||
#include "CPlatformInfo.h"
|
||||
#include "VersionInfo.h"
|
||||
#include "CPipeServer.h"
|
||||
|
||||
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
|
||||
{
|
||||
WDF_DRIVER_CONFIG config;
|
||||
NTSTATUS status;
|
||||
WDF_OBJECT_ATTRIBUTES attributes;
|
||||
g_debug.Init("looking-glass-idd");
|
||||
DEBUG_INFO("Looking Glass IDD Driver (" LG_VERSION_STR ")");
|
||||
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
#if UMDF_VERSION_MAJOR == 2 && UMDF_VERSION_MINOR == 0
|
||||
WPP_INIT_TRACING(MYDRIVER_TRACING_ID);
|
||||
#else
|
||||
WPP_INIT_TRACING(DriverObject, RegistryPath);
|
||||
#endif
|
||||
|
||||
if (!g_pipe.Init())
|
||||
{
|
||||
status = STATUS_UNSUCCESSFUL;
|
||||
DEBUG_ERROR("Failed to setup IPC pipe");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
WDF_DRIVER_CONFIG config;
|
||||
WDF_OBJECT_ATTRIBUTES attributes;
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry");
|
||||
|
||||
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
|
||||
@ -46,16 +59,19 @@ NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING Reg
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "WdfDriverCreate failed %!STATUS!", status);
|
||||
#if UMDF_VERSION_MAJOR == 2 && UMDF_VERSION_MINOR == 0
|
||||
WPP_CLEANUP();
|
||||
#else
|
||||
WPP_CLEANUP(DriverObject);
|
||||
#endif
|
||||
return status;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Exit");
|
||||
return status;
|
||||
|
||||
fail:
|
||||
#if UMDF_VERSION_MAJOR == 2 && UMDF_VERSION_MINOR == 0
|
||||
WPP_CLEANUP();
|
||||
#else
|
||||
WPP_CLEANUP(DriverObject);
|
||||
#endif
|
||||
return status;
|
||||
}
|
||||
|
||||
NTSTATUS LGIddEvtDeviceAdd(_In_ WDFDRIVER Driver, _Inout_ PWDFDEVICE_INIT DeviceInit)
|
||||
@ -75,6 +91,8 @@ VOID LGIddEvtDriverContextCleanup(_In_ WDFOBJECT DriverObject)
|
||||
|
||||
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry");
|
||||
|
||||
g_pipe.DeInit();
|
||||
|
||||
#if UMDF_VERSION_MAJOR == 2 && UMDF_VERSION_MINOR == 0
|
||||
WPP_CLEANUP();
|
||||
#else
|
||||
|
Binary file not shown.
@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\packages\MSBuilder.Git.0.3.0\build\MSBuilder.Git.props" Condition="Exists('..\packages\MSBuilder.Git.0.3.0\build\MSBuilder.Git.props')" />
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
@ -36,26 +37,41 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="cpp.hint" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="$(SolutionDir)LGCommon/*.cpp" />
|
||||
<ClCompile Include="CD3D12CommandQueue.cpp" />
|
||||
<ClCompile Include="CFrameBufferPool.cpp" />
|
||||
<ClCompile Include="CFrameBufferResource.cpp" />
|
||||
<ClCompile Include="CIndirectDeviceContext.cpp" />
|
||||
<ClCompile Include="CIndirectMonitorContext.cpp" />
|
||||
<ClCompile Include="CInteropResourcePool.cpp" />
|
||||
<ClCompile Include="CInteropResource.cpp" />
|
||||
<ClCompile Include="CIVSHMEM.cpp" />
|
||||
<ClCompile Include="CPipeServer.cpp" />
|
||||
<ClCompile Include="CPlatformInfo.cpp" />
|
||||
<ClCompile Include="CSwapChainProcessor.cpp" />
|
||||
<ClCompile Include="Debug.cpp" />
|
||||
<ClCompile Include="CD3D12Device.cpp" />
|
||||
<ClCompile Include="Device.cpp" />
|
||||
<ClCompile Include="Direct3DDevice.cpp" />
|
||||
<ClCompile Include="CD3D11Device.cpp" />
|
||||
<ClCompile Include="Driver.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="$(SolutionDir)/LGCommon/*.h" />
|
||||
<ClInclude Include="CD3D12CommandQueue.h" />
|
||||
<ClInclude Include="CFrameBufferPool.h" />
|
||||
<ClInclude Include="CFrameBufferResource.h" />
|
||||
<ClInclude Include="CIndirectMonitorContext.h" />
|
||||
<ClInclude Include="CInteropResourcePool.h" />
|
||||
<ClInclude Include="CInteropResource.h" />
|
||||
<ClInclude Include="CIVSHMEM.h" />
|
||||
<ClInclude Include="CPipeServer.h" />
|
||||
<ClInclude Include="CPlatformInfo.h" />
|
||||
<ClInclude Include="CSwapChainProcessor.h" />
|
||||
<ClInclude Include="Debug.h" />
|
||||
<ClInclude Include="CD3D12Device.h" />
|
||||
<ClInclude Include="Device.h" />
|
||||
<ClInclude Include="Direct3DDevice.h" />
|
||||
<ClInclude Include="CD3D11Device.h" />
|
||||
<ClInclude Include="Driver.h" />
|
||||
<ClInclude Include="CIndirectDeviceContext.h" />
|
||||
<ClInclude Include="Public.h" />
|
||||
@ -72,7 +88,7 @@
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform Condition="'$(Platform)' == ''">Win32</Platform>
|
||||
<RootNamespace>LGIdd</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.22621.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformVersion>10.0.26100.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset>
|
||||
@ -85,12 +101,10 @@
|
||||
<DriverTargetPlatform>Universal</DriverTargetPlatform>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset>
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<DriverTargetPlatform>Universal</DriverTargetPlatform>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset>
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<DriverTargetPlatform>Universal</DriverTargetPlatform>
|
||||
</PropertyGroup>
|
||||
@ -145,11 +159,12 @@
|
||||
<UMDF_VERSION_MAJOR>2</UMDF_VERSION_MAJOR>
|
||||
<IndirectDisplayDriver>true</IndirectDisplayDriver>
|
||||
<IDDCX_VERSION_MAJOR>1</IDDCX_VERSION_MAJOR>
|
||||
<IDDCX_VERSION_MINOR>9</IDDCX_VERSION_MINOR>
|
||||
<IDDCX_VERSION_MINOR>10</IDDCX_VERSION_MINOR>
|
||||
<Driver_SpectreMitigation>Spectre</Driver_SpectreMitigation>
|
||||
<UMDF_VERSION_MINOR>25</UMDF_VERSION_MINOR>
|
||||
<UMDF_MINIMUM_VERSION_REQUIRED>25</UMDF_MINIMUM_VERSION_REQUIRED>
|
||||
<_NT_TARGET_VERSION>0xA000005</_NT_TARGET_VERSION>
|
||||
<PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<TargetVersion>Windows10</TargetVersion>
|
||||
@ -157,11 +172,12 @@
|
||||
<UMDF_VERSION_MAJOR>2</UMDF_VERSION_MAJOR>
|
||||
<IndirectDisplayDriver>true</IndirectDisplayDriver>
|
||||
<IDDCX_VERSION_MAJOR>1</IDDCX_VERSION_MAJOR>
|
||||
<IDDCX_VERSION_MINOR>9</IDDCX_VERSION_MINOR>
|
||||
<IDDCX_VERSION_MINOR>10</IDDCX_VERSION_MINOR>
|
||||
<Driver_SpectreMitigation>Spectre</Driver_SpectreMitigation>
|
||||
<UMDF_VERSION_MINOR>25</UMDF_VERSION_MINOR>
|
||||
<UMDF_MINIMUM_VERSION_REQUIRED>25</UMDF_MINIMUM_VERSION_REQUIRED>
|
||||
<_NT_TARGET_VERSION>0xA000005</_NT_TARGET_VERSION>
|
||||
<PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
|
||||
<TargetVersion>Windows10</TargetVersion>
|
||||
@ -254,10 +270,10 @@
|
||||
<WppRecorderEnabled>true</WppRecorderEnabled>
|
||||
<WppScanConfigurationData Condition="'%(ClCompile.ScanConfigurationData)' == ''">trace.h</WppScanConfigurationData>
|
||||
<AdditionalOptions>/EHsc /D_ATL_NO_WIN_SUPPORT /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=9 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)..\..\repos\LGMP\lgmp\include;$(ProjectDir)..\..\vendor;$(ProjectDir)..\..\common\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)LGCommon;$(SolutionDir)..\repos\LGMP\lgmp\include;$(SolutionDir)..\vendor;$(SolutionDir)..\common\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);OneCoreUAP.lib;avrt.lib</AdditionalDependencies>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);OneCoreUAP.lib;avrt.lib;d3d12.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
<DriverSign>
|
||||
<FileDigestAlgorithm>SHA1</FileDigestAlgorithm>
|
||||
@ -269,10 +285,10 @@
|
||||
<WppRecorderEnabled>true</WppRecorderEnabled>
|
||||
<WppScanConfigurationData Condition="'%(ClCompile.ScanConfigurationData)' == ''">trace.h</WppScanConfigurationData>
|
||||
<AdditionalOptions>/EHsc /D_ATL_NO_WIN_SUPPORT /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=9 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)..\..\repos\LGMP\lgmp\include;$(ProjectDir)..\..\vendor;$(ProjectDir)..\..\common\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)LGCommon;$(SolutionDir)..\repos\LGMP\lgmp\include;$(SolutionDir)..\vendor;$(SolutionDir)..\common\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);OneCoreUAP.lib;avrt.lib</AdditionalDependencies>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);OneCoreUAP.lib;avrt.lib;d3d12.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
<DriverSign>
|
||||
<FileDigestAlgorithm>SHA1</FileDigestAlgorithm>
|
||||
@ -315,6 +331,29 @@
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="ExtensionTargets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\MSBuilder.Git.0.3.0\build\MSBuilder.Git.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSBuilder.Git.0.3.0\build\MSBuilder.Git.props'))" />
|
||||
</Target>
|
||||
<Target Name="GenerateVersionInfo" BeforeTargets="ClCompile">
|
||||
<Exec Command=""$(Git)" describe --always --abbrev=10 --dirty=+ --tags" ConsoleToMSBuild="true">
|
||||
<Output TaskParameter="ConsoleOutput" PropertyName="GitVersion" />
|
||||
</Exec>
|
||||
<ItemGroup>
|
||||
<VersionInfoLines Include="#define LG_VERSION_STR "$(GitVersion)"" />
|
||||
<VersionInfoLines Include="#define LG_CURRENT_YEAR $(CurrentYear)" />
|
||||
</ItemGroup>
|
||||
<WriteLinesToFile File="VersionInfo.h" Lines="@(VersionInfoLines)" Overwrite="true" />
|
||||
</Target>
|
||||
<Target Name="Build" DependsOnTargets="$(BuildDependsOn)" />
|
||||
<PropertyGroup>
|
||||
<CurrentYear>$([System.DateTime]::Now.ToString("yyyy"))</CurrentYear>
|
||||
<BuildDependsOn>
|
||||
GenerateVersionInfo;
|
||||
$(BuildDependsOn);
|
||||
</BuildDependsOn>
|
||||
</PropertyGroup>
|
||||
</Project>
|
@ -20,6 +20,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="cpp.hint" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Inf Include="LGIdd.inf">
|
||||
@ -48,16 +49,35 @@
|
||||
<ClInclude Include="CSwapChainProcessor.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Direct3DDevice.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CIVSHMEM.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Debug.h">
|
||||
<ClInclude Include="CPlatformInfo.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CPlatformInfo.h">
|
||||
<ClInclude Include="CD3D12CommandQueue.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CInteropResourcePool.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CInteropResource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CD3D12Device.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CD3D11Device.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CFrameBufferResource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CFrameBufferPool.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SolutionDir)/LGCommon/*.h" />
|
||||
<ClInclude Include="CPipeServer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
@ -77,17 +97,36 @@
|
||||
<ClCompile Include="CSwapChainProcessor.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Direct3DDevice.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CIVSHMEM.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Debug.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CPlatformInfo.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CD3D12CommandQueue.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CInteropResourcePool.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CInteropResource.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CD3D12Device.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CD3D11Device.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CFrameBufferResource.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CFrameBufferPool.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(SolutionDir)LGCommon/*.cpp" />
|
||||
<ClCompile Include="CPipeServer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<DebuggerFlavor>DbgengLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<DebuggerFlavor>DbgengLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<DebuggerFlavor>DbgengLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<DebuggerFlavor>DbgengLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
</Project>
|
4
idd/LGIdd/packages.config
Normal file
4
idd/LGIdd/packages.config
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="MSBuilder.Git" version="0.3.0" targetFramework="native" />
|
||||
</packages>
|
248
idd/LGIddHelper/CPipeClient.cpp
Normal file
248
idd/LGIddHelper/CPipeClient.cpp
Normal file
@ -0,0 +1,248 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2025 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "CPipeClient.h"
|
||||
#include "CDebug.h"
|
||||
|
||||
#include <setupapi.h>
|
||||
#include <tchar.h>
|
||||
|
||||
CPipeClient g_pipe;
|
||||
|
||||
bool CPipeClient::Init()
|
||||
{
|
||||
DeInit();
|
||||
if (!IsLGIddDeviceAttached())
|
||||
{
|
||||
DEBUG_ERROR("Looking Glass Indirect Display Device not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_running = true;
|
||||
m_thread.Attach(CreateThread(
|
||||
NULL,
|
||||
0,
|
||||
_pipeThread,
|
||||
(LPVOID)this,
|
||||
0,
|
||||
NULL));
|
||||
|
||||
if (!m_thread.IsValid())
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "Failed to create the pipe thread");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPipeClient::DeInit()
|
||||
{
|
||||
m_connected = false;
|
||||
if (m_thread.IsValid())
|
||||
{
|
||||
m_running = false;
|
||||
WaitForSingleObject(m_thread.Get(), INFINITE);
|
||||
m_thread.Close();
|
||||
}
|
||||
|
||||
if (m_pipe.IsValid())
|
||||
{
|
||||
FlushFileBuffers(m_pipe.Get());
|
||||
m_pipe.Close();
|
||||
}
|
||||
}
|
||||
|
||||
bool CPipeClient::IsLGIddDeviceAttached()
|
||||
{
|
||||
HDEVINFO hDevInfo = SetupDiGetClassDevs(
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
DIGCF_ALLCLASSES | DIGCF_PRESENT
|
||||
);
|
||||
|
||||
if (hDevInfo == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
SP_DEVINFO_DATA DeviceInfoData;
|
||||
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
||||
bool found = false;
|
||||
|
||||
for (DWORD i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData); i++)
|
||||
{
|
||||
DWORD DataT;
|
||||
TCHAR buffer[1024];
|
||||
DWORD buffersize = 0;
|
||||
|
||||
if (!SetupDiGetDeviceRegistryProperty(
|
||||
hDevInfo,
|
||||
&DeviceInfoData,
|
||||
SPDRP_HARDWAREID,
|
||||
&DataT,
|
||||
(PBYTE)buffer,
|
||||
sizeof(buffer),
|
||||
&buffersize))
|
||||
continue;
|
||||
|
||||
for (LPCTSTR p = buffer; *p; p += _tcslen(p) + 1)
|
||||
if (_tcsicmp(p, _T("Root\\LGIdd")) == 0)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (found)
|
||||
break;
|
||||
}
|
||||
|
||||
SetupDiDestroyDeviceInfoList(hDevInfo);
|
||||
return found;
|
||||
}
|
||||
|
||||
/* APIs like SetCursorPos are applied to the desktop our thread is
|
||||
* attached to. If the user switches to the secure desktop (UAC, etc)
|
||||
* then these functions will not work, so call this first to ensure
|
||||
* the call is effective */
|
||||
void CPipeClient::SetActiveDesktop()
|
||||
{
|
||||
HDESK desktop = NULL;
|
||||
desktop = OpenInputDesktop(0, FALSE, GENERIC_READ);
|
||||
if (!desktop)
|
||||
DEBUG_ERROR_HR(GetLastError(), "OpenInputDesktop Failed");
|
||||
else
|
||||
{
|
||||
if (!SetThreadDesktop(desktop))
|
||||
DEBUG_ERROR_HR(GetLastError(), "SetThreadDesktop Failed");
|
||||
CloseDesktop(desktop);
|
||||
}
|
||||
}
|
||||
|
||||
void CPipeClient::WriteMsg(const LGPipeMsg& msg)
|
||||
{
|
||||
DWORD written;
|
||||
if (!WriteFile(m_pipe.Get(), &msg, sizeof(msg), &written, NULL))
|
||||
{
|
||||
DWORD err = GetLastError();
|
||||
if (err == ERROR_BROKEN_PIPE)
|
||||
{
|
||||
DEBUG_WARN_HR(err, "Client disconnected, failed to write");
|
||||
m_connected = false;
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_WARN_HR(err, "WriteFile failed on the pipe");
|
||||
return;
|
||||
}
|
||||
|
||||
FlushFileBuffers(m_pipe.Get());
|
||||
}
|
||||
|
||||
void CPipeClient::Thread()
|
||||
{
|
||||
DEBUG_INFO("Pipe thread started");
|
||||
while (m_running)
|
||||
{
|
||||
if (!WaitNamedPipeA(LG_PIPE_NAME, 5000))
|
||||
{
|
||||
if (!IsLGIddDeviceAttached())
|
||||
{
|
||||
m_running = false;
|
||||
DEBUG_ERROR("Device is no longer available, shutting down");
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
m_pipe.Attach(CreateFileA(
|
||||
LG_PIPE_NAME,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
0,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
NULL
|
||||
));
|
||||
|
||||
if (!m_pipe.IsValid())
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "Failed to open the named pipe");
|
||||
continue;
|
||||
}
|
||||
|
||||
m_connected = true;
|
||||
DEBUG_INFO("Pipe connected");
|
||||
while (m_running && m_connected)
|
||||
{
|
||||
LGPipeMsg msg;
|
||||
DWORD bytesRead;
|
||||
if (!ReadFile(m_pipe.Get(), &msg, sizeof(msg), &bytesRead, NULL))
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "ReadFile Failed");
|
||||
break;
|
||||
}
|
||||
|
||||
if (bytesRead != sizeof(msg) || msg.size != sizeof(msg))
|
||||
{
|
||||
DEBUG_ERROR("Corrupted data");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (msg.type)
|
||||
{
|
||||
case LGPipeMsg::SETCURSORPOS:
|
||||
HandleSetCursorPos(msg);
|
||||
break;
|
||||
|
||||
case LGPipeMsg::SETDISPLAYMODE:
|
||||
HandleSetDisplayMode(msg);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_ERROR("Unknown message type %d", msg.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_pipe.Close();
|
||||
m_connected = false;
|
||||
DEBUG_INFO("Pipe closed");
|
||||
}
|
||||
DEBUG_INFO("Pipe thread shutdown");
|
||||
}
|
||||
|
||||
void CPipeClient::HandleSetCursorPos(const LGPipeMsg& msg)
|
||||
{
|
||||
SetActiveDesktop();
|
||||
SetCursorPos(msg.curorPos.x, msg.curorPos.y);
|
||||
}
|
||||
|
||||
void CPipeClient::HandleSetDisplayMode(const LGPipeMsg& msg)
|
||||
{
|
||||
DEVMODE dm = {};
|
||||
dm.dmSize = sizeof(dm);
|
||||
dm.dmPelsWidth = msg.displayMode.width;
|
||||
dm.dmPelsHeight = msg.displayMode.height;
|
||||
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
|
||||
|
||||
LONG result = ChangeDisplaySettingsEx(NULL, &dm, NULL, CDS_UPDATEREGISTRY, NULL);
|
||||
if (result != DISP_CHANGE_SUCCESSFUL)
|
||||
DEBUG_ERROR("ChangeDisplaySettingsEx Failed (0x%08x)", result);
|
||||
}
|
61
idd/LGIddHelper/CPipeClient.h
Normal file
61
idd/LGIddHelper/CPipeClient.h
Normal file
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2025 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdint.h>
|
||||
#include <wrl.h>
|
||||
|
||||
#include "PipeMsg.h"
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace Microsoft::WRL::Wrappers;
|
||||
using namespace Microsoft::WRL::Wrappers::HandleTraits;
|
||||
|
||||
class CPipeClient
|
||||
{
|
||||
private:
|
||||
HandleT<HANDLENullTraits> m_pipe;
|
||||
HandleT<HANDLENullTraits> m_thread;
|
||||
bool m_running = false;
|
||||
bool m_connected = false;
|
||||
|
||||
static DWORD WINAPI _pipeThread(LPVOID lpParam) { ((CPipeClient*)lpParam)->Thread(); return 0; }
|
||||
void Thread();
|
||||
|
||||
void WriteMsg(const LGPipeMsg& msg);
|
||||
|
||||
void SetActiveDesktop();
|
||||
|
||||
void HandleSetCursorPos(const LGPipeMsg& msg);
|
||||
void HandleSetDisplayMode(const LGPipeMsg& msg);
|
||||
|
||||
public:
|
||||
~CPipeClient() { DeInit(); }
|
||||
|
||||
static bool IsLGIddDeviceAttached();
|
||||
|
||||
bool Init();
|
||||
void DeInit();
|
||||
bool IsRunning() { return m_running; }
|
||||
};
|
||||
|
||||
extern CPipeClient g_pipe;
|
188
idd/LGIddHelper/LGIddHelper.vcxproj
Normal file
188
idd/LGIddHelper/LGIddHelper.vcxproj
Normal file
@ -0,0 +1,188 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\packages\MSBuilder.Git.0.3.0\build\MSBuilder.Git.props" Condition="Exists('..\packages\MSBuilder.Git.0.3.0\build\MSBuilder.Git.props')" />
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{0045d7ad-3f26-4b87-81cb-78d18839596d}</ProjectGuid>
|
||||
<RootNamespace>LGIddHelper</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)LGCommon;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)LGCommon;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>Default</LanguageStandard>
|
||||
<LanguageStandard_C>Default</LanguageStandard_C>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)LGCommon;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);Rpcrt4.lib;Userenv.lib;setupapi.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>if not exist "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd" mkdir "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd"
|
||||
copy /Y "$(TargetPath)" "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd\"</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>Default</LanguageStandard>
|
||||
<LanguageStandard_C>Default</LanguageStandard_C>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)LGCommon;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);Rpcrt4.lib;Userenv.lib;setupapi.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>if not exist "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd" mkdir "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd"
|
||||
copy /Y "$(TargetPath)" "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd\"</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="$(SolutionDir)LGCommon\*.cpp" />
|
||||
<ClCompile Include="CPipeClient.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CLInclude Include="$(SolutionDir)LGCommon\*.h" />
|
||||
<ClInclude Include="CPipeClient.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets" />
|
||||
<Target Name="GenerateVersionInfo" BeforeTargets="ClCompile">
|
||||
<Exec Command=""$(Git)" describe --always --abbrev=10 --dirty=+ --tags" ConsoleToMSBuild="true">
|
||||
<Output TaskParameter="ConsoleOutput" PropertyName="GitVersion" />
|
||||
</Exec>
|
||||
<ItemGroup>
|
||||
<VersionInfoLines Include="#define LG_VERSION_STR "$(GitVersion)"" />
|
||||
<VersionInfoLines Include="#define LG_CURRENT_YEAR $(CurrentYear)" />
|
||||
</ItemGroup>
|
||||
<WriteLinesToFile File="VersionInfo.h" Lines="@(VersionInfoLines)" Overwrite="true" />
|
||||
</Target>
|
||||
<Target Name="Build" DependsOnTargets="$(BuildDependsOn)" />
|
||||
<PropertyGroup>
|
||||
<CurrentYear>$([System.DateTime]::Now.ToString("yyyy"))</CurrentYear>
|
||||
<BuildDependsOn>
|
||||
GenerateVersionInfo;
|
||||
$(BuildDependsOn);
|
||||
</BuildDependsOn>
|
||||
</PropertyGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\MSBuilder.Git.0.3.0\build\MSBuilder.Git.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSBuilder.Git.0.3.0\build\MSBuilder.Git.props'))" />
|
||||
</Target>
|
||||
</Project>
|
35
idd/LGIddHelper/LGIddHelper.vcxproj.filters
Normal file
35
idd/LGIddHelper/LGIddHelper.vcxproj.filters
Normal file
@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(SolutionDir)LGCommon\*.cpp" />
|
||||
<ClCompile Include="CPipeClient.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CLInclude Include="$(SolutionDir)LGCommon\*.h" />
|
||||
<ClInclude Include="CPipeClient.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
</Project>
|
421
idd/LGIddHelper/main.cpp
Normal file
421
idd/LGIddHelper/main.cpp
Normal file
@ -0,0 +1,421 @@
|
||||
#include <Windows.h>
|
||||
#include <wrl.h>
|
||||
#include <UserEnv.h>
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
using namespace Microsoft::WRL::Wrappers;
|
||||
using namespace Microsoft::WRL::Wrappers::HandleTraits;
|
||||
|
||||
#include "CDebug.h"
|
||||
#include "VersionInfo.h"
|
||||
#include "CPipeClient.h"
|
||||
|
||||
#define SVCNAME "Looking Glass (IDD Helper)"
|
||||
|
||||
static SERVICE_STATUS_HANDLE l_svcStatusHandle;
|
||||
static SERVICE_STATUS l_svcStatus;
|
||||
static HandleT<EventTraits> l_svcStopEvent;
|
||||
|
||||
bool HandleService();
|
||||
static void WINAPI SvcMain(DWORD dwArgc, LPTSTR* lpszArgv);
|
||||
static void WINAPI SvcCtrlHandler(DWORD dwControl);
|
||||
static void ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint);
|
||||
|
||||
static std::string l_executable;
|
||||
static HandleT<HANDLENullTraits> l_process;
|
||||
static HandleT<EventTraits> l_exitEvent;
|
||||
static std::string l_exitEventName;
|
||||
|
||||
static void Launch();
|
||||
|
||||
LRESULT CALLBACK DummyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd)
|
||||
{
|
||||
g_debug.Init("looking-glass-iddhelper");
|
||||
DEBUG_INFO("Looking Glass IDD Helper (" LG_VERSION_STR ")");
|
||||
|
||||
char buffer[MAX_PATH];
|
||||
DWORD result = GetModuleFileNameA(NULL, buffer, MAX_PATH);
|
||||
if (result == 0)
|
||||
{
|
||||
DEBUG_ERROR("Failed to get the executable path");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
l_executable = buffer;
|
||||
|
||||
int argc = 0;
|
||||
LPWSTR * wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
||||
std::vector<std::string> args;
|
||||
args.reserve(argc);
|
||||
for (int i = 0; i < argc; ++i)
|
||||
{
|
||||
size_t len = wcslen(wargv[i]);
|
||||
size_t bufSize = (len + 1) * 2;
|
||||
std::vector<char> buffer(bufSize);
|
||||
|
||||
size_t converted = 0;
|
||||
errno_t err = wcstombs_s(&converted, buffer.data(), bufSize, wargv[i], bufSize - 1);
|
||||
if (err != 0)
|
||||
{
|
||||
DEBUG_ERROR("Conversion failed");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
args.emplace_back(buffer.data());
|
||||
}
|
||||
LocalFree(wargv);
|
||||
|
||||
if (argc == 1)
|
||||
{
|
||||
if (!HandleService())
|
||||
return EXIT_FAILURE;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// child process
|
||||
if (argc != 2)
|
||||
{
|
||||
// the one and only value we should see is the exit event name
|
||||
DEBUG_ERROR("Invalid invocation");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
l_exitEvent.Attach(OpenEvent(SYNCHRONIZE, FALSE, args[1].c_str()));
|
||||
if (!l_exitEvent.IsValid())
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "Failed to open the exit event: %s", args[1].c_str());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
WNDCLASSEX wx = {};
|
||||
wx.cbSize = sizeof(WNDCLASSEX);
|
||||
wx.lpfnWndProc = DummyWndProc;
|
||||
wx.hInstance = hInstance;
|
||||
wx.lpszClassName = "DUMMY_CLASS";
|
||||
wx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
|
||||
wx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
|
||||
wx.hCursor = LoadCursor(NULL, IDC_ARROW);
|
||||
wx.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE;
|
||||
ATOM aclass;
|
||||
if (!(aclass = RegisterClassEx(&wx)))
|
||||
{
|
||||
DEBUG_ERROR("Failed to register message window class");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
HWND msgWnd = CreateWindowExA(0, MAKEINTATOM(aclass), NULL,
|
||||
0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);
|
||||
|
||||
bool running = g_pipe.Init();
|
||||
while (running)
|
||||
{
|
||||
switch (MsgWaitForMultipleObjects(1, l_exitEvent.GetAddressOf(),
|
||||
FALSE, INFINITE, QS_ALLINPUT))
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
running = false;
|
||||
break;
|
||||
|
||||
case WAIT_OBJECT_0 + 1:
|
||||
{
|
||||
MSG msg;
|
||||
if (GetMessage(&msg, NULL, 0, 0))
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WAIT_FAILED:
|
||||
DEBUG_ERROR_HR(GetLastError(), "MsgWaitForMultipleObjects Failed");
|
||||
running = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_pipe.DeInit();
|
||||
|
||||
DestroyWindow(msgWnd);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
bool HandleService()
|
||||
{
|
||||
SERVICE_TABLE_ENTRY DispatchTable[] =
|
||||
{
|
||||
{ (char *)SVCNAME, SvcMain },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
if (StartServiceCtrlDispatcher(DispatchTable) == FALSE)
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "StartServiceCtrlDispatcher Failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void WINAPI SvcCtrlHandler(DWORD dwControl)
|
||||
{
|
||||
switch (dwControl)
|
||||
{
|
||||
case SERVICE_CONTROL_STOP:
|
||||
ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
|
||||
SetEvent(l_svcStopEvent.Get());
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ReportSvcStatus(l_svcStatus.dwCurrentState, NO_ERROR, 0);
|
||||
}
|
||||
|
||||
static void WINAPI SvcMain(DWORD dwArgc, LPTSTR* lpszArgv)
|
||||
{
|
||||
l_svcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
||||
l_svcStatus.dwWin32ExitCode = 0;
|
||||
|
||||
l_svcStatusHandle = RegisterServiceCtrlHandler(SVCNAME, SvcCtrlHandler);
|
||||
if (!l_svcStatusHandle)
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "RegisterServiceCtrlHandler Failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CPipeClient::IsLGIddDeviceAttached())
|
||||
{
|
||||
DEBUG_INFO("Looking Glass Indirect Display Device not found, not starting.");
|
||||
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 0);
|
||||
l_svcStopEvent.Attach(CreateEvent(NULL, TRUE, FALSE, NULL));
|
||||
if (!l_svcStopEvent.IsValid())
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "CreateEvent Failed");
|
||||
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
UUID uuid;
|
||||
RPC_CSTR uuidStr;
|
||||
RPC_STATUS status = UuidCreate(&uuid);
|
||||
if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY && status != RPC_S_UUID_NO_ADDRESS)
|
||||
{
|
||||
DEBUG_ERROR("UuidCreate Failed: 0x%x", status);
|
||||
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (UuidToString(&uuid, &uuidStr) == RPC_S_OK)
|
||||
{
|
||||
l_exitEventName = "Global\\";
|
||||
l_exitEventName += (const char *)uuidStr;
|
||||
RpcStringFree(&uuidStr);
|
||||
|
||||
l_exitEvent.Attach(CreateEvent(NULL, FALSE, FALSE, l_exitEventName.c_str()));
|
||||
if (!l_exitEvent.IsValid())
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "CreateEvent Failed");
|
||||
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0);
|
||||
bool running = true;
|
||||
while (running)
|
||||
{
|
||||
ULONGLONG launchTime = 0ULL;
|
||||
|
||||
DWORD interactiveSession = WTSGetActiveConsoleSessionId();
|
||||
if (interactiveSession != 0 && interactiveSession != 0xFFFFFFFF)
|
||||
{
|
||||
if (!CPipeClient::IsLGIddDeviceAttached())
|
||||
{
|
||||
DEBUG_INFO("Looking Glass Indirect Display Device has gone away");
|
||||
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
Launch();
|
||||
launchTime = GetTickCount64();
|
||||
}
|
||||
|
||||
HANDLE waitOn[] = { l_svcStopEvent.Get(), l_process.Get()};
|
||||
DWORD count = 2;
|
||||
DWORD duration = INFINITE;
|
||||
|
||||
if (!l_process.IsValid())
|
||||
{
|
||||
count = 1;
|
||||
duration = 1000;
|
||||
}
|
||||
|
||||
switch (WaitForMultipleObjects(count, waitOn, FALSE, duration))
|
||||
{
|
||||
// stop requested by the service manager
|
||||
case WAIT_OBJECT_0:
|
||||
running = false;
|
||||
break;
|
||||
|
||||
// child application exited
|
||||
case WAIT_OBJECT_0 + 1:
|
||||
{
|
||||
DWORD code;
|
||||
if (!GetExitCodeProcess(l_process.Get(), &code))
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "GetExitCodeProcess Failed");
|
||||
break;
|
||||
}
|
||||
|
||||
DEBUG_INFO("Child process exited with code 0x%lx", code);
|
||||
l_process.Close();
|
||||
break;
|
||||
}
|
||||
|
||||
case WAIT_FAILED:
|
||||
DEBUG_ERROR_HR(GetLastError(), "Failed to WaitForMultipleObjects");
|
||||
running = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!running)
|
||||
break;
|
||||
|
||||
Sleep(1000);
|
||||
}
|
||||
|
||||
SetEvent(l_exitEvent.Get());
|
||||
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
|
||||
}
|
||||
|
||||
static void ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint)
|
||||
{
|
||||
static DWORD dwCheckPoint = 0;
|
||||
l_svcStatus.dwCurrentState = dwCurrentState;
|
||||
l_svcStatus.dwWin32ExitCode = dwWin32ExitCode;
|
||||
l_svcStatus.dwWaitHint = dwWaitHint;
|
||||
|
||||
if (dwCurrentState == SERVICE_START_PENDING)
|
||||
l_svcStatus.dwControlsAccepted = 0;
|
||||
else
|
||||
l_svcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
|
||||
|
||||
if ((dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED))
|
||||
l_svcStatus.dwCheckPoint = 0;
|
||||
else
|
||||
l_svcStatus.dwCheckPoint = ++dwCheckPoint;
|
||||
|
||||
SetServiceStatus(l_svcStatusHandle, &l_svcStatus);
|
||||
}
|
||||
|
||||
//static void
|
||||
|
||||
static bool EnablePriv(const char * name)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void DisablePriv(const char * name)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void Launch()
|
||||
{
|
||||
l_process.Close();
|
||||
|
||||
HandleT<HANDLENullTraits> sysToken;
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE |
|
||||
TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_ADJUST_DEFAULT,
|
||||
sysToken.GetAddressOf()))
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "OpenProcessToken failed");
|
||||
return;
|
||||
}
|
||||
|
||||
HandleT<HANDLENullTraits> token;
|
||||
if (!DuplicateTokenEx(sysToken.Get(), 0, NULL, SecurityAnonymous,
|
||||
TokenPrimary, token.GetAddressOf()))
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "DuplicateTokenEx failed");
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD origSessionID, targetSessionID, returnedLen;
|
||||
GetTokenInformation(token.Get(), TokenSessionId, &origSessionID,
|
||||
sizeof(origSessionID), &returnedLen);
|
||||
|
||||
targetSessionID = WTSGetActiveConsoleSessionId();
|
||||
if (origSessionID != targetSessionID)
|
||||
{
|
||||
if (!SetTokenInformation(token.Get(), TokenSessionId,
|
||||
&targetSessionID, sizeof(targetSessionID)))
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "SetTokenInformation failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LPVOID env = NULL;
|
||||
if (!CreateEnvironmentBlock(&env, token.Get(), TRUE))
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "CreateEnvironmentBlock failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EnablePriv(SE_INCREASE_QUOTA_NAME))
|
||||
{
|
||||
DEBUG_ERROR("Failed to enable %s", SE_INCREASE_QUOTA_NAME);
|
||||
return;
|
||||
}
|
||||
|
||||
PROCESS_INFORMATION pi = {0};
|
||||
STARTUPINFO si = {0};
|
||||
si.cb = sizeof(si);
|
||||
si.dwFlags = STARTF_USESHOWWINDOW;
|
||||
si.wShowWindow = SW_SHOW;
|
||||
si.lpDesktop = (LPSTR)"WinSta0\\Default";
|
||||
|
||||
char * cmdLine = NULL;
|
||||
char cmdBuf[128];
|
||||
if (l_exitEvent.IsValid())
|
||||
{
|
||||
snprintf(cmdBuf, sizeof(cmdBuf), "LGIddHelper.exe %s",
|
||||
l_exitEventName.c_str());
|
||||
cmdLine = cmdBuf;
|
||||
}
|
||||
|
||||
if (!CreateProcessAsUserA(
|
||||
token.Get(),
|
||||
l_executable.c_str(),
|
||||
cmdLine,
|
||||
NULL,
|
||||
NULL,
|
||||
FALSE,
|
||||
DETACHED_PROCESS | HIGH_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT,
|
||||
env,
|
||||
NULL,
|
||||
&si,
|
||||
&pi
|
||||
))
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "CreateProcessAsUser failed");
|
||||
return;
|
||||
}
|
||||
|
||||
DisablePriv(SE_INCREASE_QUOTA_NAME);
|
||||
|
||||
l_process.Attach(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
}
|
4
idd/LGIddHelper/packages.config
Normal file
4
idd/LGIddHelper/packages.config
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="MSBuilder.Git" version="0.3.0" targetFramework="native" />
|
||||
</packages>
|
@ -1 +1 @@
|
||||
Subproject commit de89c63bfe5e31f4ff83ea999c17022e235d96e4
|
||||
Subproject commit daf0a360b73ee94c945f9be1001106218944b7ab
|
Loading…
x
Reference in New Issue
Block a user