很多时候为了提升产品的用户友好性,我们需要针对系统其它第三方应用的窗口状态进行处理,比如当其他应用全屏显示的时候,隐藏当前应用的窗口显示。为了实现这种功能,我们需要定时检查当前激活窗口的窗口状态,判断其是不是全屏显示。这里介绍一下该功能在Windows和Linux下的实现策略。
Windows窗口状态检测
首先我们需要顶层激活窗口的句柄,对应的接口如下:
//获得系统最前端的窗口
//返回值是对应的窗口的句柄,如果一个窗口失去激活的时候,前台窗口返回NULL
HWND GetForegroundWindow(void);
拿到窗口句柄之后,我们需要获得窗口的尺寸,对应的接口如下:
//@1窗口的句柄 @2窗口的尺寸
//return 获取是否成功
BOOL GetWindowRect(HWND hWnd,LPRECT lpRect);
拿到窗口尺寸之后,我们还需要拿窗口尺寸和屏幕尺寸进行对比来判断应用窗口是否全屏。获取屏幕尺寸的接口如下:
//获取窗口所在的屏幕
//@1 窗口矩形 @2如果窗口尺寸和屏幕没有交集的时候的默认返回值
//@2 标志位列表
//MONITOR_DEFAULTTONEAREST 默认返回离窗口显示范围最近的显示屏句柄
//MONITOR_DEFAULTTONULL 默认返回NULL
//MONITOR_DEFAULTTOPRIMARY 默认返回主显示屏的句柄
//return 显示器的句柄
HMONITOR MonitorFromRect(LPCRECT lprc,DWORD dwFlags);
//获取显示屏的信息
//@1显示屏的句柄 @2显示器的信息
//return 获取是否成功
BOOL GetMonitorInfo( HMONITOR hMonitor,LPMONITORINFO lpmi);
整个窗口状态检测流程的调用实例如下所示:
//判断是否全屏状态
BOOL IsCurreenWindowFullScreen()
{
//获取顶层窗口的句柄
HWND hwnd = GetForegroundWindow();
//获取顶层窗口的矩形范围
RECT top_window_rect;
if(!GetWindowRect(hwnd, &top_window_rect))
{
return FALSE;
}
//获取窗口所在显示器
HMONITOR monitor_hwnd = MonitorFromRect(&top_window_rect, MONITOR_DEFAULTTONULL);
if(!monitor_hwnd)return FALSE;
//获取显示器的信息
LPMONITORINFO monitor_info;
monitor_info.cbSize = sizeof(monitor_info);
GetMonitorInfo(monitor_hwnd, &monitor_info);
//判断显示器的显示范围和顶层窗口是否相同,相同就是全屏显示了
//不相同就不是全屏显示
return EqualRect(&top_window_rect, &monitor_info.rcMonitor);
}
为了能及时响应窗口全屏状态,我们添加一个定时器任务,定时检查窗口状态:
//设置定时器
UINT_PTR SetTimer(
HWND hWnd, // 窗口句柄
UINT_PTR nIDEvent, // 定时器ID,多个定时器时,可以通过该ID判断是哪个定时器
UINT nElapse, // 时间间隔,单位为毫秒
TIMERPROC lpTimerFunc // 回调函数
);
//定时器的回调函数
//@1句柄 @2消息Id @3定时器ID @4时间
VOID CALLBACK TimerProc(HWND hwnd, UINT message, UINT_PTR iTimerID, DWORD dwTime)
{
if (IsCurreenWindowFullScreen())
{
//do something when full screen
}
else
{
//do something when not full screen
}
}
Linux窗口状态检测
Linux下窗口状态检可以通过X11的库来进行获取,首先我们在QT项目的pro文件中引入X11的库,配置如下:
LIBS += -lX11
获取窗口状态的调用方法如下:
#include <X11/Xlib.h>
bool IsCurreenWindowFullScreen()
{
//首先创建一个窗口显示连接
Display *display = XOpenDisplay(NULL);
//获得当前的焦点窗口
Window focus;
int revert;
XGetInputFocus(display, &focus, &revert);
//获得窗口的属性
XWindowAttributes attr;
XGetWindowAttributes(display, focus, &attr);
//获得屏幕的尺寸
int screen_width = attr.screen->width;
int screen_height = attr.screen->height;
//关闭显示连接
XCloseDisplay(display);
//尺寸判断
if((attr.height == screen_height)&&(attr.width == screen_width))
{
return true;
}
else
{
return false;
}
}
采用X11的的库接口来获取当前窗口的属性是有缺陷的,部分应用的窗口尺寸获取不到。
比如浏览器的子窗口。
为了解决这个问题,我们可以采用命令行的方式获取窗口属性。首先通过xprop命令查找当前被激活的窗口,对应的命令如下,如果还需要窗口的其它信息可以通过wmctrl命令查看窗口的其它信息。
#查找当前激活的窗口的ID
active_win_id=$(xprop -root | grep _NET_ACTIVE_WINDOW | head -1 | \
awk '{print $5}' | sed 's/,//' | sed 's/^0x/0x0/')
#查找激活窗口的信息
wmctrl -lp | grep $active_win_id
使用xwininfo命令根据窗口的ID获取对应的窗口尺寸信息。
xwininfo -id $active_win_id | awk -F'[ +]' '$3 ~ /-geometry/ {print $4}'
获取窗口的尺寸信息之后,我们就可以通过xwininfo命令获取屏幕的尺寸信息了,对应的命令如下所示:
xwininfo -root | awk -F'[ +]' '$3 ~ /-geometry/ {print $4}'
通过比较窗口的尺寸信息和屏幕的尺寸信息,就可以判断窗口是否全屏显示了。在C++中完整的调用流程如下所示:
using namespace std;
//剔除前后的空格和换行
void Trim(string & str)
{
string blanks("\f\v\r\t\n ");
str.erase(0,str.find_first_not_of(blanks));
str.erase(str.find_last_not_of(blanks) + 1);
}
//执行命令行的命令
string exec_command_line(string input_cmd_line)
{
FILE* pipe = popen(input_cmd_line.c_str(),"r");
if (!pipe)
return string("ERROR");
char buffer[256];
string result ="";
while(!feof(pipe))
{
if(fgets(buffer, 256, pipe) != NULL)
result += buffer;
}
pclose(pipe);
return result;
}
//判断当前窗口是否全屏
void IsCurreenWindowFullScreen()
{
//获取窗口的ID
string active_win_cmd = string("xprop -root | grep _NET_ACTIVE_WINDOW | head -1 | \
awk '{print $5}' | sed 's/,//' | sed 's/^0x/0x0/'");
string active_win_id = exec_command_line(active_win_cmd);
Trim(active_win_id);
//异常ID过滤
if(active_win_id == string("0x00"))
{
return;
}
//获取屏幕尺寸
string screen_geo_info_cmd = string("xwininfo -root | awk -F'[ +]' '$3 ~ /-geometry/ {print $4}'");
string screen_info = exec_command_line(screen_geo_info_cmd);
Trim(screen_info);
//根据窗口ID获取窗口信息
string search_window_cmd = string("xwininfo -id ") + active_win_id
+ string(" | awk -F'[ +]' '$3 ~ /-geometry/ {print $4}'");
string active_window_info = exec_command_line(search_window_cmd);
Trim(active_window_info);
//窗口状态判断
if(active_window_info == screen_info)
{
//do something when full screen
}
else
{
//do something when not full screen
}
}
在多屏幕下其它常用的命令:
#获得屏幕的个数
xrandr | grep ' connected ' | wc -l
#多屏幕状态下获取屏幕尺寸
xwininfo -root | awk -F'[ +x]' '$3 ~ /-geometry/ {printf "%dx%d",$4/'"$(xrandr | grep ' connected ' | wc -l)"',$5}'