xwayland是X11协议服务端的一种实现,其运行在Wayland server上。rootless window是由wayland server提供X11窗口管理和合成器,统一Wayland窗口和X11窗口。
1. 框架
wayland compositor提供了Wayland display、window manager和compositor功能,同时提供了xwayland manager功能(XWM),提供X11窗口管理,作为xwayland窗口状态和WWM之间的桥梁。
xwayland在框架中既作为X11应用和XWM的显示服务器,也作为Wayland compositor的wayland客户端。使用Wayland compositor作为输入输出,不再需要DDX驱动。
在xwayland与Wayland compositor有两个异步通信通道,一个是XWM与xwayland通过X11协议通信,另一个是xwayland与wayland compositor通过Wayland协议通信。
在xwayland中,每个window在Wayland中存在一个对应的wl_surface对象,该surface对象用作输入输入出。
输入事件引用surface对象,该对象向Wayland compositor提供X11 window中的内容;X11 window和wl_surface位于不同协议服务端中,需要建立关联关系。
xwayland在realize_window过程中,调用wl_compositor_create_surface创建wl_surface对象,通过发送"WL_SURFACE_ID" atom的X11 ClientMessage事件发送给XWM,XWM将X11 window和wl_surface关联在一起。
2. xwanland实现
相比于原生xserver,增加了窗口和缩放处理的回调函数
2.1 初始化回调函数
2.1.1 窗口相关
窗口相关操作需要与wayland compositor交互
callback | implement | description |
---|---|---|
CreateWindow | xwl_glamor_create_window | 增加识别合成器隐形重定向的窗口,即视觉配置与祖先不同的窗口 |
ReparentWindow | xwl_glamor_reparent_window | 同上 |
RealizeWindow | xwl_realize_window | 创建damage,wl_surface,并使wl_surface关联X11 window |
UnrealizeWindow | xwl_unrealize_window | 回收xwl相关显示资源 |
DestroyWindow | xwl_destroy_window | 回收xwl相关资源 |
ResizeWindow | xwl_resize_window | 调整分辨率引起的视口(viewport)坐标调整 |
MoveWindow | xwl_move_window | 同上 |
XYToWindow | xwl_xy_to_window | 处理鼠标移动,跨X11和wayland窗口的场景,若是则返回X11根窗口 |
CloseScreen | xwl_close_screen | 回收资源 |
ChangeWindowAttributes | xwl_change_window_attributes | 跟踪窗口管理器的注册,记录窗口管理器client ID |
SetWindowPixmap | xwl_window_set_window_pixmap | 画布尺寸变化时,释放wl_surface的xwl_window_buffer资源 |
CursorWarpedTo | xwl_cursor_warped_to | |
CursorConfinedTo | xwl_cursor_confined_to | |
CreateScreenResources | xwl_glamor_create_screen_resources |
// 检查光标精灵是否丢失了光标焦点,及精灵跟踪的窗口栈是否失效,window为获取光标焦点的窗口
static Bool sprite_check_lost_focus(SpritePtr sprite, WindowPtr window){
DeviceIntPtr device, master;
struct xwl_seat *xwl_seat;
// 找到sprite对应的wayland输入设备
for (device = inputInfo.devices; device; device = device->next) {
if (device->deviceProc == xwl_pointer_proc &&
device->spriteInfo->sprite == sprite)
break;
}
if (!device)
return FALSE;
xwl_seat = device->public.devicePrivate;
if (!xwl_seat)
return FALSE;
master = GetMaster(device, POINTER_OR_FLOAT);
if (!master || !master->lastSlave)
return FALSE;
// xwl_seat需为与主光标设备关联的从属设备
if (master->lastSlave != get_pointer_device(xwl_seat))
return FALSE;
// 光标在X11窗口中,但光标锁定窗口与焦点窗口不同,涉及窗口切换
if (xwl_seat->focus_window != NULL &&
xwl_seat->cursor_confinement_window != NULL &&
xwl_seat->focus_window != xwl_seat->cursor_confinement_window)
return TRUE;
// 光标不在X11窗口系统中,最后一次离开时的光标焦点窗口是当前获取焦点窗口的自身或祖先窗口
if (xwl_seat->focus_window == NULL &&
xwl_seat->last_xwindow != NullWindow &&
(IsParent(xwl_seat->last_xwindow, window) || xwl_seat->last_xwindow == window))
return TRUE;
return FALSE;
}
// 根据(x,y)坐标,找到对应的窗口,光标将聚焦在该窗口上
static WindowPtr xwl_xy_to_window(ScreenPtr screen, SpritePtr sprite, int x, int y){
struct xwl_screen *xwl_screen = xwl_screen_get(screen);
// 返回(x,y)坐标所在的窗口
WindowPtr ret = miXYToWindow(screen, sprite, x, y);
// 场景:光标从Wayland窗口系统进入X11窗口系统,但X11窗口系统跟踪了上次离开X11窗口系统时的光标信息
// 采用返回X11系统根窗口的方式,使X11系统能发送一个LeaveNotify事件通知client进行光标处理
if (sprite_check_lost_focus(sprite, ret)) {
sprite->spriteTraceGood = 1;
return sprite->spriteTrace[0];
}
return ret;
}2.1.2 缩放相关
与CRTC相关操作,非本地硬件,其操作需通过Wayland compositor实现。使用drm-lease扩展协议实现对输出设备的租赁功能
callback | implement | description |
---|---|---|
rrGetInfo | xwl_randr_get_info | 获取旋转方向掩码,都支持 |
rrSetConfig | xwl_randr_set_config | 设置配置,不支持 |
rrScreenSetSize | xwl_randr_screen_set_size | 设置屏幕大小,不生效 |
rrCrtcSet | xwl_randr_crtc_set | 设置CRTC模式 |
rrCrtcGet | xwl_randr_crtc_get | 获取CRTC模式 |
rrCrtcSetGamma | xwl_randr_crtc_set_gamma | 设置CRTC灰度映射表,不生效 |
rrCrtcGetGamma | xwl_randr_crtc_get_gamma | 获取CRTC灰度映射表,不生效 |
rrOutputSetProperty | xwl_randr_output_set_property | 设置输出设备属性,不生效 |
rrOutputValidateMode | xwl_output_validate_mode | 检查randr模式是否有效,不生效 |
rrModeDestroy | xwl_randr_mode_destroy | 销毁randr模式 |
rrRequestLease | xwl_randr_request_lease | 请求租赁CRTC等,使用drm-lease扩展协议实现 |
rrGetLease | xwl_randr_get_lease | 获取租赁设备的句柄 |
rrTerminateLease | xwl_randr_terminate_lease | 终止租赁,是否资源 |
2.1.3 显存相关
callback | implement | description |
---|---|---|
init_wl_registry | xwl_glamor_gbm_init_wl_registry | |
has_wl_interfaces | xwl_glamor_gbm_has_wl_interfaces | |
init_egl | xwl_glamor_gbm_init_egl | |
init_screen | xwl_glamor_gbm_init_screen | |
get_wl_buffer_for_pixmap | xwl_glamor_gbm_get_wl_buffer_for_pixmap | |
check_flip | ||
get_main_device | xwl_gbm_get_main_device | |
create_pixmap_for_window | xwl_glamor_gbm_create_pixmap_for_window |
2.2 创建根窗口
创建X11根窗口,主要是三大步,假设Wayland server已经启动
CreateRootWindow:创建根窗口对象,初始化窗口尺寸,属性等,几个关键属性设置为:
cursorIsNone = TRUE
backingStore = NotUseful
visibility = VisibilityNotViewable
redirectDraw = RedirectDrawNone
forcedBG = FALSE
backingStoreSupport =WhenMapped
clipList = winSize = borderSize = borderClip = {0,0,0,0} :窗口区域,剪切区域,边框区域的坐标和宽高均为0
CreateRootCursor:创建光标对象,分配资源
InitRootWindow:初始化和显示根窗口,主要由几个回调函数完成
CreateWindow:实际未做任何事情
PositionWindow:实际未做任何事情
ChangeWindowAttributes:获取窗口管理器的client ID
RealizeWindow:初始化根窗口的可绘制区间
ClipNotify:根窗口未使用扩展功能,实际未做任何事情
PostValidateTree:实际未做任何事情
WindowExposures:根窗口给定的区间为空(clipList为空),实际未做任何事情
创建根窗口后,未通知wayland server,根窗口与Wayland未建立联系,wayland不管理X11根窗口,其画布为空,不会被渲染。
注:
xserver的回调一般为链式回调,由X11 核心协议和扩展协议实现具体功能,大部分执行过程可能是逆向的,具体要根据回调函数的实现
创建根窗口的过程不会向外发送事件
代码示例都经简化,只关注根窗口的执行流程,回调函数用实际函数代替
非根模式下,Screen的宽高均为0. width=height=0,默认值,未初始化宽高
2.2.1 CreateWindow
xwl_glamor_create_window → compCreateWindow → fbCreateWindow
在该环境下,只有fbCreateWindow初始化了窗口的画布,其它函数不做任何处理。
Bool fbCreateWindow(WindowPtr pWin) {
// 初始化窗口画布PixMapPtr,非根模式下由fbCreatePixmap,而根模式由screen->CreatePixmap链式回调创建
dixSetPrivate(&pWin->devPrivates, fbGetWinPrivateKey(pWin), fbGetScreenPixmap(pWin->drawable.pScreen));
return TRUE;
}
Bool compCreateWindow(WindowPtr pWin) {
ScreenPtr pScreen = pWin->drawable.pScreen;
Bool ret = fbCreateWindow (pWin);
// 条件不满足,根窗口没有父窗口
if (pWin->parent && ret) {
CompSubwindowsPtr csw = GetCompSubwindows(pWin->parent);
CompClientWindowPtr ccw;
PixmapPtr parent_pixmap = (*pScreen->GetWindowPixmap)(pWin->parent);
PixmapPtr window_pixmap = (*pScreen->GetWindowPixmap)(pWin);
if (window_pixmap != parent_pixmap)
(*pScreen->SetWindowPixmap) (pWin, parent_pixmap);
if (csw)
for (ccw = csw->clients; ccw; ccw = ccw->next)
compRedirectWindow(clients[CLIENT_ID(ccw->id)],
pWin, ccw->update);
if (compImplicitRedirect(pWin, pWin->parent))
compRedirectWindow(serverClient, pWin, CompositeRedirectAutomatic);
}
return ret;
}
static Bool xwl_glamor_create_window(WindowPtr window) {
// 条件不满足,根窗口没有父窗口
if (window->parent)
xwl_avoid_implicit_redirect(window);
return compCreateWindow (window);
} |
注:
在初始化Screen时,xwl_screen_init将非根模式的Screen的clip_root_mode设置为ROOT_CLIP_INPUT_ONLY,所有渲染操作都会重定向。
在创建Screen资源时,xwl_glamor_create_screen_resources在非根模式采用fbCreatePixmap创建画布(且宽高均为0),而根模式由screen->CreatePixmap链式回调创建
2.2.2 PositionWindow
miDbePositionWindow → compPositionWindow → fbPositionWindow
非根模式的根窗口未使用双缓冲,miDbePositionWindow中双缓冲部分不需处理,其相当于直接调用compPositionWindow,而根窗口的宽高和画布未修改,PositionWindow未做任何事。
// 桩函数,啥都不做
Bool fbPositionWindow(WindowPtr pWin, int x, int y){ return TRUE; }
Bool
compPositionWindow(WindowPtr pWin, int x, int y)
{
ScreenPtr pScreen = pWin->drawable.pScreen;
Bool ret = TRUE;
// 根窗口不满足该条件
if (pWin->redirectDraw != RedirectDrawNone) {
PixmapPtr pPixmap = (*pScreen->GetWindowPixmap) (pWin);
int bw = wBorderWidth(pWin);
int nx = pWin->drawable.x - bw;
int ny = pWin->drawable.y - bw;
if (pPixmap->screen_x != nx || pPixmap->screen_y != ny) {
pPixmap->screen_x = nx;
pPixmap->screen_y = ny;
pPixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER;
}
}
if (!fbPositionWindow(pWin, x, y))
ret = FALSE;
// 更新重叠窗口,此时根窗口此时无重叠窗口
if (updateOverlayWindow(pScreen) != Success)
ret = FALSE;
return ret;
} |
2.2.3 ChangeWindowAttributes
xwl_change_window_attributes → compChangeWindowAttributes → fbChangeWindowAttributes
xwl_change_window_attributes主要功能是找到窗口管理器的client ID。其它函数因mask等条件不满足,未做任何事情
// mask = (CWBorderPixel | CWCursor || CWBackingStore | CWBackPixel)
// 处理pixmap,而根窗口为pixel。
Bool fbChangeWindowAttributes(WindowPtr pWin, unsigned long mask) {
if (mask & CWBackPixmap) {
if (pWin->backgroundState == BackgroundPixmap)
fbFixupWindowPixmap(&pWin->drawable, &pWin->background.pixmap);
}
if (mask & CWBorderPixmap) {
if (pWin->borderIsPixel == FALSE)
fbFixupWindowPixmap(&pWin->drawable, &pWin->border.pixmap);
}
return TRUE;
}
// 处理后端存储,而根窗口不支持后端存储
static Bool compChangeWindowAttributes(WindowPtr pWin, unsigned long mask){
ScreenPtr pScreen = pWin->drawable.pScreen;
Bool ret = fbChangeWindowAttributes(pWin, mask);
// 根窗口的backingStoreSupport为WhenMapped,条件满足,但backingStore = NotUseful,compCheckBackingStore啥事也不做
if (ret && (mask & CWBackingStore) && pScreen->backingStoreSupport != NotUseful)
compCheckBackingStore(pWin);
return ret;
}
Bool xwl_change_window_attributes(WindowPtr window, unsigned long mask)
{
ScreenPtr screen = window->drawable.pScreen;
struct xwl_screen *xwl_screen = xwl_screen_get(screen);
OtherClients *others;
Bool ret = compChangeWindowAttributes(window, mask);
if (window != screen->root || !(mask & CWEventMask))
return ret;
// 查找窗口管理器客户端,窗口管理器将其mask设置为 SubstructureRedirectMask
for (others = wOtherClients(window); others; others = others->next) {
if (others->mask & (SubstructureRedirectMask | ResizeRedirectMask))
xwl_screen->wm_client_id = CLIENT_ID(others->resource);
}
return ret;
} |
2.2.4 RealizeWindow
xwl_realize_window → compRealizeWindow → fbRealizeWindow
初始化根窗口的可绘制区间。其它因条件不满足,未做任何事情
Bool fbRealizeWindow(WindowPtr pWindow) { return TRUE; }
Bool compRealizeWindow(WindowPtr pWin) {
compCheckRedirect(pWin);
fbRealizeWindow(pWin);
return TRUE;
}
Bool xwl_realize_window(WindowPtr window){
ScreenPtr screen = window->drawable.pScreen;
CompScreenPtr comp_screen = GetCompScreen(screen);
struct xwl_screen *xwl_screen = xwl_screen_get(screen);
ret = compRealizeWindow(window);
if (xwl_screen->rootless) {
/* We do not want the COW to be mapped when rootless in Xwayland */
if (window == comp_screen->pOverlayWin) {
window->mapped = FALSE;
return TRUE;
}
// 初始化根窗口空间,其它空间清空
if (!window->parent) {
BoxRec box = { 0, 0, xwl_screen->width, xwl_screen->height };
RegionReset(&window->winSize, &box);
RegionNull(&window->clipList);
RegionNull(&window->borderClip);
}
}
// 条件不满足,根窗口的父窗口为空,只有顶层窗口满足该条件
if (xwl_screen->rootless ? (window->drawable.class == InputOutput && window->parent == window->drawable.pScreen->root) : !window->parent) {
if (!register_damage(window)) return FALSE;
}
// 该函数对根窗口不做任何处理,其redirectDraw = RedirectDrawNone
return ensure_surface_for_window(window);
} |
2.2.5 ClipNotify
present_clip_notify → compClipNotify → xf86XVClipNotify
根窗口未使用扩展功能,这3个函数什么都不做。
2.2.6 WindowExposures
xf86XVWindowExposures → miWindowExposures
根窗口给定的区间为空,这两个函数什么都不做。