IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> C++实现显示(监视)器复制显示 -> 正文阅读

[游戏开发]C++实现显示(监视)器复制显示

1.背景:

设备上的应用启动后正常运行,当有显示器接入到设备的视频输出口(HDMI/DP)时,要求将主屏的内容复制到新接入的显示器上进行显示,且分辨率和显示方向保持主屏一致。

2.调研和思路:

经过查找资料(有效的博客)及调试验证:

C++编程实现多显示器控制(复制、横屏、纵屏,显示器个数)等_荆楚闲人的博客-CSDN博客_c++ 多显示器需求的提出: 最近做了个三维的程序,部署到客户机器上,客户看了后,现场提出这样的一个需求:程序能智能探测接入的显示器个数,当有新的显示器接入时,现有的只在一个显示器上显示的三维场景能投递到新插入的显示器上显示。类似在桌面上点击鼠标右键,选择“显示设置”菜单,弹出的如下界面:然后在此界面,对显示器进行复制、横向、纵向等处理。可以在程序中开启一个线程,进行探测,探测到显示器个数增加后,调用SetDisplayConfig(0, NULL, 0, NULL, SDC_TOPOLOGY_CLONE.https://blog.csdn.net/danshiming/article/details/112554980

Windows 下编程检测显示器信息及插拔_shallen320的博客-CSDN博客该文章讨论了windows下编程获得显示器信息,并检测显示器硬件插入拔出的方法。https://blog.csdn.net/shallen320/article/details/45070477

确定思路:

1>获取当前设备主屏的设备以及显示模式;

2>监控显示器的拔插事件,根据事件对显示设备和监视器的信息进行读取,然后做相应的处理:

设置克隆模式,并将显示设置模式和主屏的模式一致。

3.解决方案:

1>获取当前设备主屏的设备以及显示模式;

关键函数:EnumDisplayDevices:查找到主显示设备;

? ? ? ? ? ? ? ? ? EnumDisplaySettings:获取显示设备的显示模式:分辨率,方向等等。

? ??? ? ? ? ? ? ? SetDisplayConfig:设置显示设备
? ? ? ? ? ? ? ? ?ChangeDisplaySettingsEx修改显示设置,方向等。

????????????????EnumDisplayMonitors:查找监视器的信息,主要获取了数量和多显示器的显示模式。该函数采用回调实现,我们需要实现自己的回调函数,回调函数如下:

BOOL CALLBACK EnumDisplayDeviceProc(
	HMONITOR hMonitor,
	HDC hdcMonitor,
	LPRECT lprcMonitor,
	LPARAM dwData
)
{
	MONITORINFOEX mi;
	ZeroMemory(&mi, sizeof(mi));
	mi.cbSize = sizeof(mi);
	GetMonitorInfo(hMonitor, &mi);
	g_monitorRlt= GetMonitorInfoRlt(hMonitor);
	return g_monitorRlt.isCloneSettting;
}

其中GetMonitorInfoRlt为我们的具体功能函数,可根据不同的需求做对应的更改。该功能函数中主要使用GetMonitorInfoW,GetDisplayConfigBufferSizes,QueryDisplayConfig来获取监视器数量和显示配置。

tMonitorRlt GetMonitorInfoRlt(HMONITOR monitor)
{
	tMonitorRlt rlt;
	rlt.isCloneSettting = false;
	rlt.numbers = 0;

	DISPLAYCONFIG_TOPOLOGY_ID currentTopologyId;
	MONITORINFOEXW info;
	info.cbSize = sizeof(info);
	GetMonitorInfoW(monitor, &info);

	UINT32 requiredPaths, requiredModes;
	GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &requiredPaths, &requiredModes);
	std::vector<DISPLAYCONFIG_PATH_INFO> paths(requiredPaths);
	std::vector<DISPLAYCONFIG_MODE_INFO> modes(requiredModes);
	QueryDisplayConfig(QDC_DATABASE_CURRENT, &requiredPaths, paths.data(), &requiredModes, modes.data(), &currentTopologyId);

	rlt.isCloneSettting = currentTopologyId == DISPLAYCONFIG_TOPOLOGY_CLONE ? true : false;
	rlt.numbers = paths.size();
	return rlt;
}

2>监控显示器的拔插事件

当显示器拔插后,Windows系统的设备和打印机页面就会有显示器的图标显示和消失,同时还显示了显示器的名字。所以是可以通过监听Windows的消息来获取拔插事件的。

通过查找资料发现,Windows对外部设备都有统一的GUID限制。通过注册GUID设备的通知消息,就可以在代码中实现拔插消息的相应。

有效资料连接:

GUID获取

GUID_DEVINTERFACE_MONITOR - Windows drivers | Microsoft Docshttps://msdn.microsoft.com/en-us/library/windows/hardware/ff545901(v=vs.85).aspx事件监控示例:

csdn - 安全中心https://link.csdn.net/?target=https%3A%2F%2Fmsdn.microsoft.com%2Fen-us%2Flibrary%2Fwindows%2Fdesktop%2Faa363432%2528v%3Dvs.85%2529.aspx具体实现如下:

1.注册监视器的GUID,用于监控其操作信息,函数如下

GUID WceusbshGUID = { 0xE6F07B5F,0xEE97,0x4a90,0xB0,0x76,0x33,0xF5,0x7B,0xF4,0xEA,0xA7 };
?? ?if (DoRegisterDeviceInterfaceToHwnd(
?? ??? ?WceusbshGUID,
?? ??? ?(HWND)this->winId(),
?? ??? ?&m_hDeviceNotify))
?? ?{
?? ??? ?m_pMonitorSet = new CMonitorSet;
?? ?
?? ?}

BOOL DoRegisterDeviceInterfaceToHwnd(
	IN GUID InterfaceClassGuid,
	IN HWND hWnd,
	OUT HDEVNOTIFY* hDeviceNotify
)
{
	DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;

	ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
	NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
	NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
	NotificationFilter.dbcc_classguid = InterfaceClassGuid;

	*hDeviceNotify = RegisterDeviceNotification(
		hWnd,                       // events recipient
		&NotificationFilter,        // type of device
		DEVICE_NOTIFY_WINDOW_HANDLE // type of recipient handle
	);

	if (NULL == *hDeviceNotify)
	{

		return FALSE;
	}

	return TRUE;
}

2.Qt的窗口类继承QAbstractNativeEventFilter,并重载bool nativeEventFilter(const QByteArray &eventType, void *message, long *result);使用installNativeEventFilter安装事件过滤。

3.在bool nativeEventFilter(const QByteArray &eventType, void *message, long *result)实现拔插后的函数处理:

bool rlt = EnumDisplayMonitors(NULL, NULL, EnumDisplayDeviceProc, 0);
	if (!rlt)
	{
		if (m_primaryDeviceMode.dmDisplayOrientation == DMDO_DEFAULT)
		{
			m_primaryDeviceMode.dmDisplayOrientation = DMDO_90;
			DWORD dwTemp = m_primaryDeviceMode.dmPelsHeight;
			m_primaryDeviceMode.dmPelsHeight = m_primaryDeviceMode.dmPelsWidth;
			m_primaryDeviceMode.dmPelsWidth = dwTemp;
		}

		SetDisplayConfig(0, NULL, 0, NULL, SDC_TOPOLOGY_CLONE | SDC_APPLY);
		
			ChangeDisplaySettingsEx(m_primaryDisplayDevice.DeviceName,
				&m_primaryDeviceMode,
				NULL,
				CDS_UPDATEREGISTRY,
				NULL);
		
	}

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-04-22 19:10:32  更:2022-04-22 19:13:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:34:54-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码