目录 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
Wayland窗口操作包括窗口切换,窗口移动,窗口缩放等,同时支持X11和wayland窗口。窗口切换由快捷键触发;窗口移动由鼠标持续按压窗口管理器的窗口标题栏拖动触发;窗口缩放由鼠标持续按压窗口管理器的窗口边框拖动触发。本文针对mutter源码的实现进行描述
杂记
窗口事件
mutter实现了wayland compositor,X window manager等功能,其对X11的窗口掩码进行了修改,包括xwayland的根窗口,顶层窗口和窗口管理器的页框窗口。
根窗口事件掩码:
核心事件:SubstructureRedirectMask | SubstructureNotifyMask | StructureNotifyMask | ColormapChangeMask | PropertyChangeMask
扩展事件:XI_Enter | XI_Leave | XI_FocusIn | XI_FocusOut | XI_BarrierHit | XI_BarrierLeave | XFixesDisplayCursorNotifyMask
顶级窗口掩码:增加
核心事件:PropertyChangeMask (and StructureNotifyMask if override redirect window)
扩展事件:XI_Enter | XI_Leave | XI_FocusIn | XI_FocusOut | ShapeNotifyMask
页框窗口掩码:
核心事件:SubstructureRedirectMask | SubstructureNotifyMask | StructureNotifyMask | ExposureMask | FocusChangeMask
event mask | event type | description |
---|---|---|
SubstructureRedirectMask | CirculateRequest | |
ConfigureRequest | ||
MapRequest | ||
StructureNotifyMask SubstructureNotifyMask | CirculateNotify | |
ConfigureNotify | 修改窗口显示属性通知 | |
DestroyNotify | 销毁窗口通知 | |
GravityNotify | ||
MapNotify | ||
ReparentNotify | ||
UnmapNotify | ||
SubstructureNotifyMask | CreateNotify | 创建窗口通知 |
ColormapChangeMask | ColormapNotify | 修改色彩映射表通知 |
PropertyChangeMask | PropertyNotify | 修改窗口资源属性通知 |
ExposureMask | Expose | 暴露窗口通知 |
FocusChangeMask | FocusIn | 焦点进入通知 |
FocusOut | 焦点离开通知 | |
ShapeNotifyMask | ShapeNotify | |
XFixesDisplayCursorNotifyMask | XFixesDisplayCursorNotify |
特殊窗口
特殊窗口在mutter创建MetaX11Display时调用meta_x11_display_new和meta_display_init_x11_finish初始化,针对X11 server的特殊窗口,这些窗口都有override redirect属性
name | geometry | mask | description |
---|---|---|---|
composite_overlay_window | (0, 0, width, heigth) | NoEventMask | 调用XCompositeGetOverlayWindow创建,未使用 |
leader_window | (-100, -100, 1, 1) | NoEventMask | X11窗口的默认组长,用于窗口分组 |
timestamp_pinging_window | (-100, -100, 1, 1) | PropertyChangeMask | 获取X11 server时间戳 |
wm_sn_selection_window | (-100, -100, 1, 1) | NoEventMask | 标记client为窗口管理器的窗口, |
no_focus_window | (-100, -100, 1, 1) | FocusChangeMask | KeyPressMask | KeyReleaseMask | 取消X11 display焦点时,焦点设在no_focus_window上 |
guard_window | (0, 0, width, heigth) | NoEventMask | 对最小化窗口的优化处理,将其隐藏在guard窗口后面 |
wm_cm_selection_window | (-100, -100, 1, 1) | NoEventMask | 标记client为compositor的窗口 |
The guard window allows us to leave minimized windows mapped so that compositor code may provide live previews of them. Instead of being unmapped/withdrawn, they get pushed underneath the guard window. We also select events on the guard window, which should effectively be forwarded to events on the background actor, providing that the scene graph is set up correctly.
注:composite_overlay_window在使用MetaCompositorX11时创建,而在MetaWaylandCompositor时不创建该窗口,xwayland非根模式使用的时MetaWaylandCompositor和MetaCompositorServer。
X11窗口树
X11 server以属性结构管理窗口,每个屏幕有一个根窗口,其它创建的窗口都为根窗口子孙窗口。其中有几个约束:
子窗口不能超过父窗口可显示区域显示,所有子窗口显示在父窗口上面
只能兄弟窗口间切换窗口,兄弟窗口有堆叠层次。若切换的窗口所在的顶层窗口不在顶层显示,则涉及顶层窗口间的切换
父窗口使用列表管理子窗口及其堆叠层次,排在列表前的子窗口显示在上层,也即是排在前的子窗口可能会遮挡排在后的子窗口。
以堆叠层次切换为例。X11 server收到ConfigureWindow中CWStackMode请求,X11 server对窗口树进行重排。
更新窗口树结构:确定pWin位置并更新子窗口列表(MoveWindowInStack)
识别重绘区域:父窗口原可见区域clipList,包含边框区域boardClip,窗口移动时,影响的可绘制区域,这些区域限制在父窗口的winSize区域内。(MoveWindowInStack)
计算子窗口的重绘区域:按照深度优先顺序计算子窗口的重绘区域,生成exposed和borderExposed区域 (miMarkOverlappedWindows / miValidateTree)
重绘各子窗口的exposed和borderExposed区域:在server端绘制窗口边框和背景,发送Expose事件由client绘制窗口内容(miHandleValidateExposures)
|
窗口栈管理
wayland对窗口栈管理分为组(group),层(layer),栈(stack),由MetaStack和MetaWindow管理实现。MetaStack处理3个信号事件:"changed", "window-added", "window-removed",这3个事件都涉及窗口栈的更新。
组
一般来说,顶层窗口作为组长,与子孙窗口分为一组。client可通过EWMH指定窗口为特定的组(WindowGroupHint)。一般只有X11窗口有该属性。MetaWindow::group保存组关系
|
层
Wayland根据窗口类型来划分层的,对应关系如下,MetaWindow::layer保存窗口层次关系。在某个窗口组中的窗口来说,以其窗口组中层次最大的层划分。由MetaStackLayer定义。Wayland窗口只有META_LAYER_NORMAL、META_LAYER_TOP和META_LAYER_BOTTOM 3种类型
layer | enum | window type | description |
---|---|---|---|
META_LAYER_DESKTOP | 0 | META_WINDOW_DESKTOP | 桌面层 |
META_LAYER_BOTTOM | 1 | wm_state_below | 底层,由客户应用程序使用EWMH指定窗口为 wm_state_below(_NET_WM_STATE_BELOW) |
META_LAYER_NORMAL | 2 | META_WINDOW_NORMAL or other | 中间层 |
META_LAYER_TOP | 4 | wm_state_above and not maximized | 顶层,可由客户应用程序使用EWMH指定窗口为 wm_state_above(_NET_WM_STATE_ABOVE) |
META_LAYER_DOCK | 4 | META_WINDOW_DOCK and not wm_state_below | 停靠层,与顶层同层 |
META_LAYER_OVERRIDE_REDIRECT | 7 | META_WINDOW_DROPDOWN_MENU META_WINDOW_POPUP_MENU META_WINDOW_TOOLTIP META_WINDOW_NOTIFICATION META_WINDOW_COMBO META_WINDOW_OVERRIDE_OTHER | 重载重定向层,一般为禁止窗口管理器管理的窗口,显示在最上层 |
栈
Wayland使用MetaStack管理所有的MetaWindow窗口,wayland窗口由MetaWindowWayland表示,xwayland窗口由MetaWindowXwayland表示,均为MetaWindow派生类。MetaWindow::stack_position记录窗口在MetaStack中的位置,越大越显示在上层,后续操作均按该栈序处理。窗口的层次切换后,也需保持相对位置。
使用如下两个函数来增加和删除窗口:
meta_stack_add增加窗口:初始化窗口的stack_position值,为列表大小,发送"window-added"同步信号。新增窗口都显示在最上面。只能增加可堆叠窗口,处理完成后再发送"changed"同步信号
meta_window_x11_is_stackable:非override redirect窗口
meta_window_wayland_is_stackable:可显示窗口,MetaWaylandBuffer不为空
meta_stack_remove删除窗口:发送"window-removed"同步信号,从栈中删除窗口,处理完成后再发送"changed"同步信号
注:使用MetaX11Stack管理X11窗口栈备份,为XID列表。MetaX11Stack响应这三个信号,响应函数在src/x11/meta-x11-stack.c文件中
"window-added":stack_window_added_cb将window加入x11_stack->added临时列表中
"window-removed":stack_window_removed_cb将window加入x11_stack->removed临时列表,window的页框窗口也加入临时页表中
"changed": stack_changed_cb,
处理added和removed两个临时列表,整理至x11_stack->xwindows列表中
调用XChangeProperty同步"_NET_CLIENT_LIST"和"_NET_CLIENT_LIST_STACKING"两个根窗口的EWMH属性,前者值为xwindow列表,后者值为从MetaStack中过滤出来的X11窗口栈
|
堆跟踪器
MetaStackTracker,跟踪所有窗口,包括unmanaging窗口。窗口用窗口ID表示,X11窗口为32位ID,Wayland窗口ID为64位,其值都大于(1<<32)。Wayland窗口的序列号为0。
unmanaging窗口:the window of withdrawn, destroyed, attaches, detaches, or changes attached parents.
|
调用stack_tracker_apply_prediction会将window加入unverified列表中,一般有4个操作:record_add, record_remove, raise_above, lower_below。
调用stack_tracker_event_received将unverified列表中的窗口加入verified列表中,调用该函数的有3种情况,Wayland收到X11的事件通知时,共3个通知:CreateNotify, ReparentNotify和ConfigureNotify。(X11窗口的序列号一般为处理时生成的递增序号,用来表示时序)
窗口切换
wayland server在启动时注册了窗口切换快捷键,在init_builtin_key_bindings函数中绑定了默认的快捷键,包含“switch-windows”和“switch-windows-backward”两个事件,由handle_switch函数实现,该函数可响应多个事件,还包括窗口组切换,应用程序切换,面板切换等,分别为"switch-group", "switch-applications", "switch-windows", "switch-panels"等正向切换事件,都对应有反向切换的事件。本次只描述窗口正向切换流程。
|
meta_display_get_tab_next:从工作台中获取切换窗口
meta_window_activate:激活切换窗口
meta_window_raise:将切换窗口放置在顶层,更新MetaStack窗口栈顺序,同步至MetaStackTracker窗口顺序
meta_window_set_stack_position_no_sync:移动切换窗口,更新MetaStack窗口栈顺序
meta_stack_changed:同步tracker、compositor和X11 server栈信息
调整MetaStackTracer窗口栈顺序
发送XConfigureWindow给X11 Server修改server上栈信息,根据信息调整栈位置,更新窗口显示
同步compositor的窗口栈,即MetaWindowActor栈
meta_stack_update_window_tile_matches:调整窗口平铺位置
meta_window_focus:窗口获取焦点,包括键盘输入焦点和光标
显示窗口,包括隐藏窗口,第一次显示的窗口,未放置窗口和图标窗口
发送SetInputFocus请求给X11 server,同步X11 server的焦点信息,由X11 Server处理焦点
关联输入设备与切换窗口
获取窗口
有几个地方维护了窗口列表,
MetaWindow:维护两类窗口列表,一类为原生Wayland窗口列表(wayland_windows);另一类为X11对应的Wayland窗口列表(x11_display->xids)
MetaWorkspace:维护mru_list窗口列表,属于该工作台中的窗口列表,按照MRU(most recently used)排序,最近访问的窗口排在前面
从X11和wayland server中找到所有在workspace中的MetaWindow窗口。
获取所有窗口列表:从MetaDisplay中获取所有的X11和Wayland窗口(不包括override redirect窗口)
初始化候选窗口列表:获取工作台(workspace)中类型为META_TAB_LIST_NORMAL的窗口列表,非最小化窗口在前,最小化窗口在后,每组列表保序(MRU序),拼接成一个链表
向前追加状态为demands_attention的窗口值候选窗口列表中:该状态窗口不在工作台中,类型为META_TAB_LIST_NORMAL,需要额外追加
候选窗口列表中第一个非焦点窗口,即为候选窗口
注:获取所有窗口列表时,列表并未按照MRU排序,在追加demands_attention窗口时,窗口创建越迟越优先。
代码块 | ||
---|---|---|
| ||
// MetaDisplay (src/core/display.c)
// 只列出上下文相关代码,其余被删除。type = META_TAB_LIST_NORMAL; window = NULL; backward = false;
MetaWindow* meta_display_get_tab_next (MetaDisplay *display, MetaTabList type, MetaWorkspace *workspace, MetaWindow *window, gboolean backward){
gboolean skip;
GList *tab_list;
MetaWindow *ret;
// tab_list中为type=META_TAB_LIST_NORMAL的窗口列表,这些窗口均在workspace中
tab_list = meta_display_get_tab_list (display, type, workspace);
if (tab_list == NULL)
return NULL;
// tab_list中的第一个为焦点窗口,则跳过该窗口
skip = display->focus_window != NULL && tab_list->data == display->focus_window;
// 先从tab_list中找到第一个类型为META_TAB_LIST_NORMAL的窗口,若未找到,则从workspace->mru_list中查找
ret = find_tab_forward (display, type, workspace, tab_list, skip);
g_list_free (tab_list);
return ret;
}
GList* meta_display_get_tab_list (MetaDisplay *display, MetaTabList type, MetaWorkspace *workspace){
GList *tab_list = NULL;
GList *mru_list, *tmp;
// x11_display->xids记录了所有与X11窗口对应的MetaWindow,窗口由_meta_window_shared_new函数调用meta_window_x11_manage创建并注册
// 获取display->x11_display和display->wayland_windows所有的X11和Wayland窗口(不包括override redirect窗口),去重的窗口列表
GSList *windows = meta_display_list_windows (display, META_LIST_DEFAULT);
GSList *w;
mru_list = workspace->mru_list;
// Windows sellout mode - MRU order. Collect unminimized windows then minimized so minimized windows aren't in the way so much.
// 工作台中的META_TAB_LIST_NORMAL的窗口列表,先加入非最小化窗口,然后再加入最小化窗口
for (tmp = mru_list; tmp; tmp = tmp->next){
MetaWindow *window = tmp->data;
if (!window->minimized && IN_TAB_CHAIN (window, type))
tab_list = g_list_prepend (tab_list, window);
}
for (tmp = mru_list; tmp; tmp = tmp->next){
MetaWindow *window = tmp->data;
if (window->minimized && IN_TAB_CHAIN (window, type))
tab_list = g_list_prepend (tab_list, window);
}
// 非最小化窗口放在前面,最小化窗口放在后面,每组按mru列表顺序排列
tab_list = g_list_reverse (tab_list);
// If filtering by workspace, include windows from other workspaces that demand attention
if (workspace)
for (w = windows; w; w = w->next){
MetaWindow *l_window = w->data;
// 不在此工作台中的类型为META_TAB_LIST_NORMAL的窗口,且标记wm_state_demands_attention(被焦点窗口遮挡时会设置该值)
if (l_window->wm_state_demands_attention && !meta_window_located_on_workspace (l_window, workspace) && IN_TAB_CHAIN (l_window, type))
tab_list = g_list_prepend (tab_list, l_window);
}
g_slist_free (windows);
return tab_list;
} |
激活窗口
根据窗口类型分别处理:
普通窗口,将该窗口切换至工作台,并在顶层显示
具备transient_for属性的窗口,将该窗口中脱离父子关系的窗口都切换至同一个工作台中,先将与该窗口脱离关系的祖先窗口显示在顶层,再将该窗口显示在顶层
shaded或minimized窗口,需要先做unshade或unminimize
meta_window_activate
|
meta_window_raise
显示窗口,对于普通窗口只显示自身窗口,对于具有transient_for属性的窗口,先显示祖先窗口,再显示自身窗口
代码块 | ||
---|---|---|
| ||
void meta_window_raise (MetaWindow *window){
MetaWindow *ancestor;
// 获取具有transient_for属性窗口的祖先;若无此属性,则祖先为自身窗口
ancestor = meta_window_find_root_ancestor (window);
// 祖先和自身窗口不在同一窗口栈中,则一定出了什么差错
if (window->display->stack == ancestor->display->stack) {
meta_stack_raise (window->display->stack, ancestor);
}
// 具备transient_for属性的窗口,将该窗口在顶层显示
if (window != ancestor)
meta_stack_raise (window->display->stack, window);
// 同步信号,触发调用window_raised(src/wayland/meta-wayland-pointer-constraints.c)
g_signal_emit (window, window_signals[RAISED], 0);
}
void meta_stack_raise (MetaStack *stack, MetaWindow *window)
{
MetaWorkspaceManager *workspace_manager = window->display->workspace_manager;
GList *l;
int max_stack_position = window->stack_position;
MetaWorkspace *workspace;
// 确保window在stack中有序,先分层,再排序
stack_ensure_sorted (stack);
// 计算工作台中最顶层window的位置,stack_position越大,越靠近顶层
workspace = meta_window_get_workspace (window);
for (l = stack->sorted; l; l = l->next){
MetaWindow *w = (MetaWindow *) l->data;
if (meta_window_located_on_workspace (w, workspace) && w->stack_position > max_stack_position)
max_stack_position = w->stack_position;
}
// 窗口已在该工作台的顶层,则不处理
if (max_stack_position == window->stack_position)
return;
// 位置在[window->stack_position, max_stack_position]的窗口stack_position调整变化1个位置,更新window的stack_position为max_stack_position。
meta_window_set_stack_position_no_sync (window, max_stack_position);
// 对MetaStack重排序,并发送一个"changed"同步信号,调用MetaStack的on_stack_changed函数(src/core/stack.c)
meta_stack_changed (stack);
// 窗口平铺位置更新
meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace);
} |
on_stack_changed
代码块 | ||
---|---|---|
| ||
// MetaStack (src/core/stack.c)
static void on_stack_changed (MetaStack *stack) {
MetaDisplay *display = stack->display;
GList *l;
GArray *all_root_children_stacked = g_array_new (FALSE, FALSE, sizeof (uint64_t));
GArray *hidden_stack_ids = g_array_new (FALSE, FALSE, sizeof (uint64_t));
// 获取MetaStack中的所有窗口列表,顺序与MetaStack相反,即队尾窗口显示在最顶层
GList *sorted = meta_stack_list_windows (stack, NULL);
// 分别获取所有可显示的子窗口和隐藏窗口列表
for (l = sorted; l; l = l->next) {
MetaWindow *w = l->data;
uint64_t top_level_window;
uint64_t stack_id;
if (w->unmanaging)
continue;
// X11的顶层窗口,若有页框窗口,则将页框窗口作为顶层窗口。
if (w->frame)
top_level_window = w->frame->xwindow;
else
top_level_window = w->xwindow;
if (w->client_type == META_WINDOW_CLIENT_TYPE_X11)
stack_id = top_level_window;
else
stack_id = w->stamp;
// We don't restack hidden windows along with the rest, though they are reflected in the _NET hints.
// Hidden windows all get pushed below the screens fullscreen guard_window.
if (w->hidden){
g_array_append_val (hidden_stack_ids, stack_id);
continue;
}
g_array_append_val (all_root_children_stacked, stack_id);
}
if (display->x11_display) {
// The screen guard window sits above all hidden windows and acts as a barrier to input reaching these windows.
uint64_t guard_window_id = display->x11_display->guard_window;
g_array_append_val (hidden_stack_ids, guard_window_id);
}
/* Sync to server */
meta_stack_tracker_restack_managed (display->stack_tracker, (uint64_t *)all_root_children_stacked->data, all_root_children_stacked->len);
// 将隐藏窗口放在底层
meta_stack_tracker_restack_at_bottom (display->stack_tracker, (uint64_t *)hidden_stack_ids->data, hidden_stack_ids->len);
g_array_free (hidden_stack_ids, TRUE);
g_array_free (all_root_children_stacked, TRUE);
g_list_free (sorted);
} |
meta_stack_tracker_restack_managed
|
stack_tracker_sync_stack_later
|
meta_window_focus
代码块 | ||
---|---|---|
| ||
// meta_window_focus (src/core/window.c)
void meta_window_focus (MetaWindow *window, guint32 timestamp) {
MetaWorkspaceManager *workspace_manager = window->display->workspace_manager;
MetaWindow *modal_transient;
MetaBackend *backend;
ClutterStage *stage;
window->restore_focus_on_map = FALSE; // This is a oneshot flag
if (window->in_workspace_change) {
return;
}
// 键盘被其它窗口锁定,按键也被锁定了,则窗口无法获得焦点
if (window->display->grab_window && window->display->grab_window != window &&
window->display->grab_window->all_keys_grabbed && !window->display->grab_window->unmanaging) {
return;
}
// window的模态窗口,即窗口类型为META_WINDOW_MODAL_DIALOG,transient_for指向window的窗口,若存在,则由模态窗口获取焦点
modal_transient = get_modal_transient (window);
if (modal_transient != NULL && !modal_transient->unmanaging && meta_window_transient_can_focus (modal_transient)) {
if (!meta_window_located_on_workspace (modal_transient, workspace_manager->active_workspace))
meta_window_change_workspace (modal_transient, workspace_manager->active_workspace);
window = modal_transient;
}
// 最终调用update_window_visibilities显示窗口,对四类窗口进行显示:隐藏窗口,首次显示窗口,未放置窗口,图标窗口
meta_window_flush_calc_showing (window);
if ((!window->mapped || window->hidden) && !window->shaded) {
return;
}
// meta_window_x11_focus for xwayland :同步input focus事件给X11 server和Wayland compositor
// meta_window_wayland_focus for wayland :同步input focus事件给Wayland compositor
META_WINDOW_GET_CLASS (window)->focus (window, timestamp);
backend = meta_get_backend ();
stage = CLUTTER_STAGE (meta_backend_get_stage (backend));
if (window->display->event_route == META_EVENT_ROUTE_NORMAL && clutter_stage_get_grab_actor (stage) == NULL)
clutter_stage_set_key_focus (stage, NULL);
if (window->close_dialog && meta_close_dialog_is_visible (window->close_dialog))
meta_close_dialog_focus (window->close_dialog);
if (window->wm_state_demands_attention)
meta_window_unset_demands_attention(window);
}
static void meta_window_flush_calc_showing (MetaWindow *window){
MetaWindowPrivate *priv = meta_window_get_instance_private (window);
if (!(priv->queued_types & META_QUEUE_CALC_SHOWING))
return;
meta_display_flush_queued_window (window->display, window, META_QUEUE_CALC_SHOWING);
priv->queued_types &= ~META_QUEUE_CALC_SHOWING;
}
// meta_window_flush_calc_showing (src/core/window.c)
void meta_display_flush_queued_window (MetaDisplay *display, MetaWindow *window, MetaQueueType queue_types){
g_autoptr (GList) windows = NULL;
int queue_idx;
meta_display_unqueue_window (display, window, queue_types);
windows = g_list_prepend (windows, window);
for (queue_idx = 0; queue_idx < META_N_QUEUE_TYPES; queue_idx++){
if (!(queue_types & 1 << queue_idx))
continue;
// META_QUEUE_CALC_SHOWING : update_window_visibilities
// META_QUEUE_MOVE_RESIZE : move_resize
window_queue_func[queue_idx] (display, windows);
}
} |
update_window_visibilities
显示或隐藏窗口
代码块 | ||
---|---|---|
| ||
// update_window_visibilities (src/core/display.c)
static void update_window_visibilities (MetaDisplay *display, GList *windows){
g_autoptr (GList) unplaced = NULL;
g_autoptr (GList) should_show = NULL;
g_autoptr (GList) should_hide = NULL;
GList *l;
for (l = windows; l; l = l->next){
MetaWindow *window = l->data;
if (!window->placed)
unplaced = g_list_prepend (unplaced, window);
else if (meta_window_should_be_showing (window))
should_show = g_list_prepend (should_show, window);
else
should_hide = g_list_prepend (should_hide, window);
}
/* Sort bottom to top */
unplaced = g_list_sort (unplaced, window_stack_cmp);
should_hide = g_list_sort (should_hide, window_stack_cmp);
/* Sort top to bottom */
should_show = g_list_sort (should_show, window_stack_cmp);
should_show = g_list_reverse (should_show);
g_list_foreach (unplaced, (GFunc) meta_window_update_visibility, NULL);
meta_stack_freeze (display->stack);
g_list_foreach (should_show, (GFunc) meta_window_update_visibility, NULL);
g_list_foreach (should_hide, (GFunc) meta_window_update_visibility, NULL);
meta_stack_thaw (display->stack);
g_list_foreach (windows, (GFunc) meta_window_clear_queued, NULL);
// on_window_visibility_updated (src/x11/meta-x11-display.c) : XChangeProperty通知X11更新"_MUTTER_SENTINEL"属性,X11通过PropertyNotify事件通知XWM,这个机制用于避免竞争
// on_window_visibility_updated (src/compositor/compositor.c) : update_top_window_actor
g_signal_emit (display, display_signals[WINDOW_VISIBILITY_UPDATED], 0, unplaced, should_show, should_hide);
g_list_foreach (windows, (GFunc) warn_on_incorrectly_unmanaged_window, NULL);
}
void meta_window_update_visibility (MetaWindow *window) {
implement_showing (window, meta_window_should_be_showing (window));
}
// 去掉了showing=false的处理代码
static void implement_showing (MetaWindow *window, gboolean showing){
/* Some windows are not stackable until being showed, so add those now. */
if (meta_window_is_stackable (window) && !meta_window_is_in_stack (window))
meta_stack_add (window->display->stack, window);
// 只对未显示的窗口进行显示,已显示但被部分遮挡的窗口不做任何操作
meta_window_show (window);
// meta_window_x11_map (src/x11/window-x11.c) : 发送XMapWindow给X11 server,若是已map的窗口,不会再发送请求
// meta_window_wayland_map (src/wayland/meta-window-wayland.c) : nothing to do.
if (!window->override_redirect)
sync_client_window_mapped (window);
} |
meta_window_show
meta_window_show主要对四类窗口进行显示,最终调用meta_compositor_show_window显示
隐藏窗口(hidden),显示后的窗口被隐藏了
第一次显示的窗口,窗口已map,但未显示过
未放置窗口:从未计算过窗口的放置位置,也就是从未显示过的窗口
图标窗口:窗口已最小化,使用图标显示的窗口
|
meta_window_x11_focus
|
meta_x11_display_set_input_focus
|
meta_display_update_focus_window
代码块 | ||
---|---|---|
| ||
void meta_display_update_focus_window (MetaDisplay *display, MetaWindow *window)
{
MetaWindow *previous = NULL;
if (display->focus_window == window)
return;
if (display->focus_window){
// Make sure that signals handlers invoked by meta_window_set_focused_internal() don't see display->focus_window->has_focus == FALSE
previous = display->focus_window;
display->focus_window = NULL;
meta_window_set_focused_internal (previous, FALSE);
}
display->focus_window = window;
if (display->focus_window){
meta_window_set_focused_internal (display->focus_window, TRUE);
}
if (!previous || !display->focus_window || !meta_window_unit_cgroup_equal (previous, display->focus_window)){
if (previous)
meta_window_set_inactive_since (previous, g_get_monotonic_time ());
if (display->focus_window)
meta_window_set_inactive_since (display->focus_window, -1);
}
if (meta_is_wayland_compositor ())
meta_display_sync_wayland_input_focus (display);
// MetaWindowX11响应该属性变化的事件,处理函数:meta_window_x11_delayed_focus_data_free,释放focus资源
g_object_notify (G_OBJECT (display), "focus-window");
}
void meta_window_set_focused_internal (MetaWindow *window, gboolean focused){
MetaWorkspaceManager *workspace_manager = window->display->workspace_manager;
if (focused) {
window->has_focus = TRUE;
if (window->override_redirect)
return;
// Move to the front of the focusing workspace's MRU list. We should only be "removing" it from the MRU list if it's not already there.
// Note that it's possible that we might be processing this FocusIn after we've changed to a different workspace;
// we should therefore update the MRU list only if the window is actually on the active workspace.
if (workspace_manager->active_workspace && meta_window_located_on_workspace (window, workspace_manager->active_workspace)){
GList* link = g_list_find (workspace_manager->active_workspace->mru_list, window);
workspace_manager->active_workspace->mru_list = g_list_remove_link (workspace_manager->active_workspace->mru_list, link);
g_list_free (link);
workspace_manager->active_workspace->mru_list = g_list_prepend (workspace_manager->active_workspace->mru_list, window);
}
if (window->frame)
meta_frame_queue_draw (window->frame);
// Ungrab click to focus button since the sync grab can interfere with some things you might do inside the focused window,
// by causing the client to get funky enter/leave events.
// The reason we usually have a passive grab on the window is so that we can intercept clicks and raise the window in response.
// For click-to-focus we don't need that since the focused window is already raised.
// When raise_on_click is FALSE we also don't need that since we don't do anything when the window is clicked.
if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK || !meta_prefs_get_raise_on_click()){
meta_display_ungrab_focus_window_button (window->display, window);
// Since we ungrab with XIAnyModifier above, all button grabs go way so we need to re-grab the window buttons.
meta_display_grab_window_buttons (window->display, window->xwindow);
}
// 无人注册"focus"信号
g_signal_emit (window, window_signals[FOCUS], 0);
if (!window->attached_focus_window)
meta_window_update_appears_focused (window);
meta_window_propagate_focus_appearance (window, TRUE);
}
else{
window->has_focus = FALSE;
if (window->override_redirect)
return;
meta_window_propagate_focus_appearance (window, FALSE);
if (!window->attached_focus_window)
meta_window_update_appears_focused (window);
if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK || !meta_prefs_get_raise_on_click ())
meta_display_grab_focus_window_button (window->display, window); // Re-grab for click to focus and raise-on-click, if necessary
}
}
void meta_frame_queue_draw (MetaFrame *frame){
meta_ui_frame_queue_draw (frame->ui_frame);
}
void meta_ui_frame_queue_draw (MetaUIFrame *frame){
invalidate_whole_window (frame);
}
static void invalidate_whole_window (MetaUIFrame *frame){
if (!frame->is_frozen){
// 调用meta_window_xwayland_freeze_commits,发送ChangeProperty请求修改frame窗口"_XWAYLAND_ALLOW_COMMITS"属性
meta_window_x11_freeze_commits (frame->meta_window);
frame->is_frozen = TRUE;
}
gdk_window_invalidate_rect (frame->window, NULL, FALSE);
}
void meta_display_sync_wayland_input_focus (MetaDisplay *display){
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
MetaWindow *focus_window = NULL;
MetaBackend *backend = meta_get_backend ();
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
ClutterSeat *seat = clutter_backend_get_default_seat (clutter_backend);
MetaStage *stage = META_STAGE (meta_backend_get_stage (backend));
gboolean is_no_focus_xwindow = FALSE;
if (display->x11_display)
is_no_focus_xwindow = meta_x11_display_xwindow_is_a_no_focus_window (display->x11_display, display->x11_display->focus_xwindow);
if (!meta_display_windows_are_interactable (display))
focus_window = NULL;
else if (is_no_focus_xwindow)
focus_window = NULL;
else if (display->focus_window && display->focus_window->surface)
focus_window = display->focus_window;
meta_stage_set_active (stage, focus_window == NULL);
meta_wayland_compositor_set_input_focus (compositor, focus_window);
// 重新配对输入设备与新窗口
clutter_stage_repick_device (CLUTTER_STAGE (stage), clutter_seat_get_pointer (seat));
}
void meta_wayland_compositor_set_input_focus (MetaWaylandCompositor *compositor, MetaWindow *window){
MetaWaylandSurface *surface = window ? window->surface : NULL;
meta_wayland_seat_set_input_focus (compositor->seat, surface);
}
void meta_wayland_seat_set_input_focus (MetaWaylandSeat *seat, MetaWaylandSurface *surface){
MetaWaylandTabletSeat *tablet_seat;
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
if (meta_wayland_seat_has_keyboard (seat)){
// 更新focus资源,并向client发送事件
meta_wayland_keyboard_set_focus (seat->keyboard, surface);
meta_wayland_data_device_set_keyboard_focus (&seat->data_device);
meta_wayland_data_device_primary_set_keyboard_focus (&seat->primary_data_device);
meta_wayland_data_device_primary_legacy_set_keyboard_focus (&seat->primary_legacy_data_device);
}
tablet_seat = meta_wayland_tablet_manager_ensure_seat (compositor->tablet_manager, seat);
meta_wayland_tablet_seat_set_pad_focus (tablet_seat, surface);
// 文本输入资源处理
meta_wayland_text_input_set_focus (seat->text_input, surface);
}
void meta_wayland_text_input_set_focus (MetaWaylandTextInput *text_input, MetaWaylandSurface *surface){
if (text_input->surface == surface)
return;
text_input->pending_state = META_WAYLAND_PENDING_STATE_NONE;
if (text_input->surface){
if (!wl_list_empty (&text_input->focus_resource_list)){
ClutterInputFocus *focus = text_input->input_focus;
ClutterInputMethod *input_method;
struct wl_resource *resource;
if (clutter_input_focus_is_focused (focus)){
input_method = clutter_backend_get_input_method (clutter_get_default_backend ());
clutter_input_focus_reset (focus);
meta_wayland_text_input_focus_flush_done (focus);
clutter_input_method_focus_out (input_method);
}
wl_resource_for_each (resource, &text_input->focus_resource_list){
zwp_text_input_v3_send_leave (resource, text_input->surface->resource);
}
move_resources (&text_input->resource_list, &text_input->focus_resource_list);
}
wl_list_remove (&text_input->surface_listener.link);
text_input->surface = NULL;
}
if (surface){
struct wl_resource *focus_surface_resource;
text_input->surface = surface;
focus_surface_resource = text_input->surface->resource;
wl_resource_add_destroy_listener (focus_surface_resource, &text_input->surface_listener);
move_resources_for_client (&text_input->focus_resource_list, &text_input->resource_list, wl_resource_get_client (focus_surface_resource));
if (!wl_list_empty (&text_input->focus_resource_list)) {
struct wl_resource *resource;
wl_resource_for_each (resource, &text_input->focus_resource_list){
zwp_text_input_v3_send_enter (resource, surface->resource);
}
}
}
}
void clutter_stage_repick_device (ClutterStage *stage, ClutterInputDevice *device){
graphene_point_t point;
clutter_stage_get_device_coords (stage, device, NULL, &point);
clutter_stage_pick_and_update_device (stage, device, NULL, CLUTTER_DEVICE_UPDATE_IGNORE_CACHE | CLUTTER_DEVICE_UPDATE_EMIT_CROSSING, point, CLUTTER_CURRENT_TIME);
}
ClutterActor *clutter_stage_pick_and_update_device (ClutterStage *stage, ClutterInputDevice *device, ClutterEventSequence *sequence,
ClutterDeviceUpdateFlags flags, graphene_point_t point, uint32_t time_ms){
ClutterActor *new_actor;
cairo_region_t *clear_area = NULL;
if ((flags & CLUTTER_DEVICE_UPDATE_IGNORE_CACHE) == 0) {
if (clutter_stage_check_in_clear_area (stage, device, sequence, point)){
clutter_stage_set_device_coords (stage, device, sequence, point);
return clutter_stage_get_device_actor (stage, device, sequence);
}
}
new_actor = _clutter_stage_do_pick (stage, point.x, point.y, CLUTTER_PICK_REACTIVE, &clear_area);
clutter_stage_update_device (stage, device, sequence, point, time_ms, new_actor, clear_area, !!(flags & CLUTTER_DEVICE_UPDATE_EMIT_CROSSING));
g_clear_pointer (&clear_area, cairo_region_destroy);
return new_actor;
} |