Merge pull request #4288 from hardening/disp_channel
Disp channel changes and improvements
This commit is contained in:
commit
ed9a4b7d45
@ -176,8 +176,11 @@ UINT disp_send_display_control_monitor_layout_pdu(DISP_CHANNEL_CALLBACK* callbac
|
|||||||
UINT disp_recv_display_control_caps_pdu(DISP_CHANNEL_CALLBACK* callback, wStream* s)
|
UINT disp_recv_display_control_caps_pdu(DISP_CHANNEL_CALLBACK* callback, wStream* s)
|
||||||
{
|
{
|
||||||
DISP_PLUGIN* disp;
|
DISP_PLUGIN* disp;
|
||||||
|
DispClientContext *context;
|
||||||
|
UINT ret = CHANNEL_RC_OK;
|
||||||
|
|
||||||
disp = (DISP_PLUGIN*) callback->plugin;
|
disp = (DISP_PLUGIN*) callback->plugin;
|
||||||
|
context = (DispClientContext *)disp->iface.pInterface;
|
||||||
|
|
||||||
if (Stream_GetRemainingLength(s) < 12)
|
if (Stream_GetRemainingLength(s) < 12)
|
||||||
{
|
{
|
||||||
@ -188,10 +191,11 @@ UINT disp_recv_display_control_caps_pdu(DISP_CHANNEL_CALLBACK* callback, wStream
|
|||||||
Stream_Read_UINT32(s, disp->MaxNumMonitors); /* MaxNumMonitors (4 bytes) */
|
Stream_Read_UINT32(s, disp->MaxNumMonitors); /* MaxNumMonitors (4 bytes) */
|
||||||
Stream_Read_UINT32(s, disp->MaxMonitorAreaFactorA); /* MaxMonitorAreaFactorA (4 bytes) */
|
Stream_Read_UINT32(s, disp->MaxMonitorAreaFactorA); /* MaxMonitorAreaFactorA (4 bytes) */
|
||||||
Stream_Read_UINT32(s, disp->MaxMonitorAreaFactorB); /* MaxMonitorAreaFactorB (4 bytes) */
|
Stream_Read_UINT32(s, disp->MaxMonitorAreaFactorB); /* MaxMonitorAreaFactorB (4 bytes) */
|
||||||
//WLog_ERR(TAG, "DisplayControlCapsPdu: MaxNumMonitors: %"PRIu32" MaxMonitorAreaFactorA: %"PRIu32" MaxMonitorAreaFactorB: %"PRIu32"",
|
|
||||||
// disp->MaxNumMonitors, disp->MaxMonitorAreaFactorA, disp->MaxMonitorAreaFactorB);
|
|
||||||
|
|
||||||
return CHANNEL_RC_OK;
|
if (context->DisplayControlCaps)
|
||||||
|
ret = context->DisplayControlCaps(context, disp->MaxNumMonitors, disp->MaxMonitorAreaFactorA, disp->MaxMonitorAreaFactorB);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -486,7 +486,7 @@ static UINT dvcman_create_channel(IWTSVirtualChannelManager* pChannelMgr,
|
|||||||
listener->iface.pInterface);
|
listener->iface.pInterface);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
WLog_ERR(TAG, "context.ReceiveSamples failed with error %"PRIu32"", error);
|
WLog_ERR(TAG, "context.OnChannelConnected failed with error %"PRIu32"", error);
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
@ -520,8 +520,8 @@ static UINT dvcman_open_channel(IWTSVirtualChannelManager* pChannelMgr,
|
|||||||
DVCMAN_CHANNEL* channel;
|
DVCMAN_CHANNEL* channel;
|
||||||
IWTSVirtualChannelCallback* pCallback;
|
IWTSVirtualChannelCallback* pCallback;
|
||||||
UINT error;
|
UINT error;
|
||||||
channel = (DVCMAN_CHANNEL*) dvcman_find_channel_by_id(pChannelMgr, ChannelId);
|
|
||||||
|
|
||||||
|
channel = (DVCMAN_CHANNEL*) dvcman_find_channel_by_id(pChannelMgr, ChannelId);
|
||||||
if (!channel)
|
if (!channel)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "ChannelId %"PRIu32" not found!", ChannelId);
|
WLog_ERR(TAG, "ChannelId %"PRIu32" not found!", ChannelId);
|
||||||
@ -948,11 +948,9 @@ static UINT drdynvc_process_create_request(drdynvcPlugin* drdynvc, int Sp,
|
|||||||
WLog_Print(drdynvc->log, WLOG_DEBUG, "process_create_request: ChannelId=%"PRIu32" ChannelName=%s",
|
WLog_Print(drdynvc->log, WLOG_DEBUG, "process_create_request: ChannelId=%"PRIu32" ChannelName=%s",
|
||||||
ChannelId,
|
ChannelId,
|
||||||
Stream_Pointer(s));
|
Stream_Pointer(s));
|
||||||
channel_status = dvcman_create_channel(drdynvc->channel_mgr, ChannelId,
|
channel_status = dvcman_create_channel(drdynvc->channel_mgr, ChannelId, (char*) Stream_Pointer(s));
|
||||||
(char*) Stream_Pointer(s));
|
|
||||||
data_out = Stream_New(NULL, pos + 4);
|
data_out = Stream_New(NULL, pos + 4);
|
||||||
|
if (!data_out)
|
||||||
if (!s)
|
|
||||||
{
|
{
|
||||||
WLog_Print(drdynvc->log, WLOG_ERROR, "Stream_New failed!");
|
WLog_Print(drdynvc->log, WLOG_ERROR, "Stream_New failed!");
|
||||||
return CHANNEL_RC_NO_MEMORY;
|
return CHANNEL_RC_NO_MEMORY;
|
||||||
@ -970,12 +968,10 @@ static UINT drdynvc_process_create_request(drdynvcPlugin* drdynvc, int Sp,
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
WLog_Print(drdynvc->log, WLOG_DEBUG, "no listener");
|
WLog_Print(drdynvc->log, WLOG_DEBUG, "no listener");
|
||||||
Stream_Write_UINT32(data_out,
|
Stream_Write_UINT32(data_out, (UINT32)0xC0000001); /* same code used by mstsc */
|
||||||
(UINT32) 0xC0000001); /* same code used by mstsc */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
status = drdynvc_send(drdynvc, data_out);
|
status = drdynvc_send(drdynvc, data_out);
|
||||||
|
|
||||||
if (status != CHANNEL_RC_OK)
|
if (status != CHANNEL_RC_OK)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "VirtualChannelWriteEx failed with %s [%08"PRIX32"]",
|
WLog_ERR(TAG, "VirtualChannelWriteEx failed with %s [%08"PRIX32"]",
|
||||||
@ -1063,7 +1059,6 @@ static UINT drdynvc_process_close_request(drdynvcPlugin* drdynvc, int Sp,
|
|||||||
}
|
}
|
||||||
|
|
||||||
data_out = Stream_New(NULL, 4);
|
data_out = Stream_New(NULL, 4);
|
||||||
|
|
||||||
if (!data_out)
|
if (!data_out)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "Stream_New failed!");
|
WLog_ERR(TAG, "Stream_New failed!");
|
||||||
|
@ -145,6 +145,10 @@ set(XRENDER_FEATURE_TYPE "RECOMMENDED")
|
|||||||
set(XRENDER_FEATURE_PURPOSE "rendering")
|
set(XRENDER_FEATURE_PURPOSE "rendering")
|
||||||
set(XRENDER_FEATURE_DESCRIPTION "X11 render extension")
|
set(XRENDER_FEATURE_DESCRIPTION "X11 render extension")
|
||||||
|
|
||||||
|
set(XRANDR_FEATURE_TYPE "RECOMMENDED")
|
||||||
|
set(XRANDR_FEATURE_PURPOSE "tracking output configuration")
|
||||||
|
set(XRANDR_FEATURE_DESCRIPTION "X11 randr extension")
|
||||||
|
|
||||||
set(XFIXES_FEATURE_TYPE "RECOMMENDED")
|
set(XFIXES_FEATURE_TYPE "RECOMMENDED")
|
||||||
set(XFIXES_FEATURE_PURPOSE "X11 xfixes extension")
|
set(XFIXES_FEATURE_PURPOSE "X11 xfixes extension")
|
||||||
set(XFIXES_FEATURE_DESCRIPTION "Useful additions to the X11 core protocol")
|
set(XFIXES_FEATURE_DESCRIPTION "Useful additions to the X11 core protocol")
|
||||||
@ -156,6 +160,7 @@ find_feature(Xcursor ${XCURSOR_FEATURE_TYPE} ${XCURSOR_FEATURE_PURPOSE} ${XCURSO
|
|||||||
find_feature(Xv ${XV_FEATURE_TYPE} ${XV_FEATURE_PURPOSE} ${XV_FEATURE_DESCRIPTION})
|
find_feature(Xv ${XV_FEATURE_TYPE} ${XV_FEATURE_PURPOSE} ${XV_FEATURE_DESCRIPTION})
|
||||||
find_feature(Xi ${XI_FEATURE_TYPE} ${XI_FEATURE_PURPOSE} ${XI_FEATURE_DESCRIPTION})
|
find_feature(Xi ${XI_FEATURE_TYPE} ${XI_FEATURE_PURPOSE} ${XI_FEATURE_DESCRIPTION})
|
||||||
find_feature(Xrender ${XRENDER_FEATURE_TYPE} ${XRENDER_FEATURE_PURPOSE} ${XRENDER_FEATURE_DESCRIPTION})
|
find_feature(Xrender ${XRENDER_FEATURE_TYPE} ${XRENDER_FEATURE_PURPOSE} ${XRENDER_FEATURE_DESCRIPTION})
|
||||||
|
find_feature(XRandR ${XRANDR_FEATURE_TYPE} ${XRANDR_FEATURE_PURPOSE} ${XRANDR_FEATURE_DESCRIPTION})
|
||||||
find_feature(Xfixes ${XFIXES_FEATURE_TYPE} ${XFIXES_FEATURE_PURPOSE} ${XFIXES_FEATURE_DESCRIPTION})
|
find_feature(Xfixes ${XFIXES_FEATURE_TYPE} ${XFIXES_FEATURE_PURPOSE} ${XFIXES_FEATURE_DESCRIPTION})
|
||||||
|
|
||||||
if(WITH_XINERAMA)
|
if(WITH_XINERAMA)
|
||||||
@ -194,6 +199,12 @@ if(WITH_XRENDER)
|
|||||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${XRENDER_LIBRARIES})
|
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${XRENDER_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(NOT APPLE AND WITH_XRANDR)
|
||||||
|
add_definitions(-DWITH_XRANDR)
|
||||||
|
include_directories(${XRANDR_INCLUDE_DIRS})
|
||||||
|
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${XRANDR_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
|
||||||
if(WITH_XFIXES)
|
if(WITH_XFIXES)
|
||||||
add_definitions(-DWITH_XFIXES)
|
add_definitions(-DWITH_XFIXES)
|
||||||
include_directories(${XFIXES_INCLUDE_DIRS})
|
include_directories(${XFIXES_INCLUDE_DIRS})
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
* X11 Monitor Handling
|
* X11 Monitor Handling
|
||||||
*
|
*
|
||||||
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||||
|
* Copyright 2017 David Fort <contact@hardening-consulting.com>
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -37,30 +38,50 @@
|
|||||||
#include <X11/extensions/Xinerama.h>
|
#include <X11/extensions/Xinerama.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_XRANDR
|
||||||
|
#include <X11/extensions/Xrandr.h>
|
||||||
|
#include <X11/extensions/randr.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "xf_monitor.h"
|
#include "xf_monitor.h"
|
||||||
|
|
||||||
/* See MSDN Section on Multiple Display Monitors: http://msdn.microsoft.com/en-us/library/dd145071 */
|
/* See MSDN Section on Multiple Display Monitors: http://msdn.microsoft.com/en-us/library/dd145071 */
|
||||||
|
|
||||||
int xf_list_monitors(xfContext* xfc)
|
int xf_list_monitors(xfContext* xfc)
|
||||||
{
|
{
|
||||||
#ifdef WITH_XINERAMA
|
|
||||||
Display* display;
|
Display* display;
|
||||||
int major, minor;
|
int major, minor;
|
||||||
int i, nmonitors = 0;
|
int i, nmonitors = 0;
|
||||||
XineramaScreenInfo* screen = NULL;
|
|
||||||
display = XOpenDisplay(NULL);
|
|
||||||
|
|
||||||
|
display = XOpenDisplay(NULL);
|
||||||
if (!display)
|
if (!display)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "failed to open X display");
|
WLog_ERR(TAG, "failed to open X display");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_XRANDR
|
||||||
|
if (XRRQueryExtension(xfc->display, &major, &minor))
|
||||||
|
{
|
||||||
|
XRRMonitorInfo *monitors = XRRGetMonitors(xfc->display, DefaultRootWindow(xfc->display), 1, &nmonitors);
|
||||||
|
|
||||||
|
for (i = 0; i < nmonitors; i++)
|
||||||
|
{
|
||||||
|
printf(" %s [%d] %hdx%hd\t+%hd+%hd\n",
|
||||||
|
monitors[i].primary ? "*" : " ", i,
|
||||||
|
monitors[i].width, monitors[i].height,
|
||||||
|
monitors[i].x, monitors[i].y);
|
||||||
|
}
|
||||||
|
XRRFreeMonitors(monitors);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_XINERAMA
|
||||||
if (XineramaQueryExtension(display, &major, &minor))
|
if (XineramaQueryExtension(display, &major, &minor))
|
||||||
{
|
{
|
||||||
if (XineramaIsActive(display))
|
if (XineramaIsActive(display))
|
||||||
{
|
{
|
||||||
screen = XineramaQueryScreens(display, &nmonitors);
|
XineramaScreenInfo* screen = XineramaQueryScreens(display, &nmonitors);
|
||||||
|
|
||||||
for (i = 0; i < nmonitors; i++)
|
for (i = 0; i < nmonitors; i++)
|
||||||
{
|
{
|
||||||
@ -72,25 +93,16 @@ int xf_list_monitors(xfContext* xfc)
|
|||||||
|
|
||||||
XFree(screen);
|
XFree(screen);
|
||||||
}
|
}
|
||||||
}
|
} else
|
||||||
|
|
||||||
XCloseDisplay(display);
|
|
||||||
#else
|
#else
|
||||||
Screen* screen;
|
|
||||||
Display* display;
|
|
||||||
display = XOpenDisplay(NULL);
|
|
||||||
|
|
||||||
if (!display)
|
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "failed to open X display");
|
Screen* screen = ScreenOfDisplay(display, DefaultScreen(display));
|
||||||
return -1;
|
printf(" * [0] %dx%d\t+0+0\n", WidthOfScreen(screen), HeightOfScreen(screen));
|
||||||
}
|
}
|
||||||
|
|
||||||
screen = ScreenOfDisplay(display, DefaultScreen(display));
|
|
||||||
printf(" * [0] %dx%d\t+0+0\n", WidthOfScreen(screen),
|
|
||||||
HeightOfScreen(screen));
|
|
||||||
XCloseDisplay(display);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
XCloseDisplay(display);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,9 +135,8 @@ BOOL xf_detect_monitors(xfContext* xfc, UINT32* pMaxWidth, UINT32* pMaxHeight)
|
|||||||
Window _dummy_w;
|
Window _dummy_w;
|
||||||
int current_monitor = 0;
|
int current_monitor = 0;
|
||||||
Screen* screen;
|
Screen* screen;
|
||||||
#ifdef WITH_XINERAMA
|
#if defined WITH_XINERAMA || defined WITH_XRANDR
|
||||||
int major, minor;
|
int major, minor;
|
||||||
XineramaScreenInfo* screenInfo = NULL;
|
|
||||||
#endif
|
#endif
|
||||||
vscreen = &xfc->vscreen;
|
vscreen = &xfc->vscreen;
|
||||||
*pMaxWidth = settings->DesktopWidth;
|
*pMaxWidth = settings->DesktopWidth;
|
||||||
@ -137,41 +148,59 @@ BOOL xf_detect_monitors(xfContext* xfc, UINT32* pMaxWidth, UINT32* pMaxHeight)
|
|||||||
&_dummy_i, &_dummy_i, (void*) &_dummy_i))
|
&_dummy_i, &_dummy_i, (void*) &_dummy_i))
|
||||||
mouse_x = mouse_y = 0;
|
mouse_x = mouse_y = 0;
|
||||||
|
|
||||||
#ifdef WITH_XINERAMA
|
#ifdef WITH_XRANDR
|
||||||
|
if (XRRQueryExtension(xfc->display, &major, &minor))
|
||||||
if (XineramaQueryExtension(xfc->display, &major, &minor))
|
|
||||||
{
|
{
|
||||||
if (XineramaIsActive(xfc->display))
|
XRRMonitorInfo *monitors = XRRGetMonitors(xfc->display, DefaultRootWindow(xfc->display), 1, &vscreen->nmonitors);
|
||||||
|
|
||||||
|
if (vscreen->nmonitors > 16)
|
||||||
|
vscreen->nmonitors = 0;
|
||||||
|
|
||||||
|
if (vscreen->nmonitors)
|
||||||
{
|
{
|
||||||
screenInfo = XineramaQueryScreens(xfc->display, &vscreen->nmonitors);
|
for (i = 0; i < vscreen->nmonitors; i++)
|
||||||
|
|
||||||
if (vscreen->nmonitors > 16)
|
|
||||||
vscreen->nmonitors = 0;
|
|
||||||
|
|
||||||
if (vscreen->nmonitors)
|
|
||||||
{
|
{
|
||||||
for (i = 0; i < vscreen->nmonitors; i++)
|
vscreen->monitors[i].area.left = monitors[i].x;
|
||||||
{
|
vscreen->monitors[i].area.top = monitors[i].y;
|
||||||
vscreen->monitors[i].area.left = screenInfo[i].x_org;
|
vscreen->monitors[i].area.right = monitors[i].x + monitors[i].width - 1;
|
||||||
vscreen->monitors[i].area.top = screenInfo[i].y_org;
|
vscreen->monitors[i].area.bottom = monitors[i].y + monitors[i].height - 1;
|
||||||
vscreen->monitors[i].area.right = screenInfo[i].x_org + screenInfo[i].width - 1;
|
vscreen->monitors[i].primary = monitors[i].primary > 0;
|
||||||
vscreen->monitors[i].area.bottom = screenInfo[i].y_org + screenInfo[i].height -
|
|
||||||
1;
|
|
||||||
|
|
||||||
/* Determine which monitor that the mouse cursor is on */
|
|
||||||
if ((mouse_x >= vscreen->monitors[i].area.left) &&
|
|
||||||
(mouse_x <= vscreen->monitors[i].area.right) &&
|
|
||||||
(mouse_y >= vscreen->monitors[i].area.top) &&
|
|
||||||
(mouse_y <= vscreen->monitors[i].area.bottom))
|
|
||||||
current_monitor = i;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
XFree(screenInfo);
|
|
||||||
}
|
}
|
||||||
}
|
XRRFreeMonitors(monitors);
|
||||||
|
} else
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_XINERAMA
|
||||||
|
if (XineramaQueryExtension(xfc->display, &major, &minor) && XineramaIsActive(xfc->display))
|
||||||
|
{
|
||||||
|
XineramaScreenInfo* screenInfo = XineramaQueryScreens(xfc->display, &vscreen->nmonitors);
|
||||||
|
|
||||||
|
if (vscreen->nmonitors > 16)
|
||||||
|
vscreen->nmonitors = 0;
|
||||||
|
|
||||||
|
if (vscreen->nmonitors)
|
||||||
|
{
|
||||||
|
for (i = 0; i < vscreen->nmonitors; i++)
|
||||||
|
{
|
||||||
|
vscreen->monitors[i].area.left = screenInfo[i].x_org;
|
||||||
|
vscreen->monitors[i].area.top = screenInfo[i].y_org;
|
||||||
|
vscreen->monitors[i].area.right = screenInfo[i].x_org + screenInfo[i].width - 1;
|
||||||
|
vscreen->monitors[i].area.bottom = screenInfo[i].y_org + screenInfo[i].height - 1;
|
||||||
|
|
||||||
|
/* Determine which monitor that the mouse cursor is on */
|
||||||
|
if ((mouse_x >= vscreen->monitors[i].area.left) &&
|
||||||
|
(mouse_x <= vscreen->monitors[i].area.right) &&
|
||||||
|
(mouse_y >= vscreen->monitors[i].area.top) &&
|
||||||
|
(mouse_y <= vscreen->monitors[i].area.bottom))
|
||||||
|
current_monitor = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XFree(screenInfo);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
xfc->fullscreenMonitors.top = xfc->fullscreenMonitors.bottom =
|
xfc->fullscreenMonitors.top = xfc->fullscreenMonitors.bottom =
|
||||||
xfc->fullscreenMonitors.left = xfc->fullscreenMonitors.right = 0;
|
xfc->fullscreenMonitors.left = xfc->fullscreenMonitors.right = 0;
|
||||||
|
|
||||||
|
@ -52,6 +52,8 @@ typedef struct _DISPLAY_CONTROL_MONITOR_LAYOUT DISPLAY_CONTROL_MONITOR_LAYOUT;
|
|||||||
|
|
||||||
typedef struct _disp_client_context DispClientContext;
|
typedef struct _disp_client_context DispClientContext;
|
||||||
|
|
||||||
|
typedef UINT (*pcDispCaps)(DispClientContext* context, UINT32 MaxNumMonitors, UINT32 MaxMonitorAreaFactorA,
|
||||||
|
UINT32 MaxMonitorAreaFactorB);
|
||||||
typedef UINT (*pcDispSendMonitorLayout)(DispClientContext* context, UINT32 NumMonitors, DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors);
|
typedef UINT (*pcDispSendMonitorLayout)(DispClientContext* context, UINT32 NumMonitors, DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors);
|
||||||
|
|
||||||
struct _disp_client_context
|
struct _disp_client_context
|
||||||
@ -59,6 +61,7 @@ struct _disp_client_context
|
|||||||
void* handle;
|
void* handle;
|
||||||
void* custom;
|
void* custom;
|
||||||
|
|
||||||
|
pcDispCaps DisplayControlCaps;
|
||||||
pcDispSendMonitorLayout SendMonitorLayout;
|
pcDispSendMonitorLayout SendMonitorLayout;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user