版本比较

密钥

  • 该行被添加。
  • 该行被删除。
  • 格式已经改变。
目录
minLevel1
maxLevel3
outlinefalse
stylenone
typelist
printablefalse

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以属性结构管理窗口,每个屏幕有一个根窗口,其它创建的窗口都为根窗口子孙窗口。其中有几个约束:

  1. 子窗口不能超过父窗口可显示区域显示,所有子窗口显示在父窗口上面

  2. 只能兄弟窗口间切换窗口,兄弟窗口有堆叠层次。若切换的窗口所在的顶层窗口不在顶层显示,则涉及顶层窗口间的切换

  3. 父窗口使用列表管理子窗口及其堆叠层次,排在列表前的子窗口显示在上层,也即是排在前的子窗口可能会遮挡排在后的子窗口。

以堆叠层次切换为例。X11 server收到ConfigureWindow中CWStackMode请求,X11 server对窗口树进行重排。

  • 更新窗口树结构:确定pWin位置并更新子窗口列表(MoveWindowInStack)

  • 识别重绘区域:父窗口原可见区域clipList,包含边框区域boardClip,窗口移动时,影响的可绘制区域,这些区域限制在父窗口的winSize区域内。(MoveWindowInStack)

  • 计算子窗口的重绘区域:按照深度优先顺序计算子窗口的重绘区域,生成exposed和borderExposed区域 (miMarkOverlappedWindows / miValidateTree)

  • 重绘各子窗口的exposed和borderExposed区域:在server端绘制窗口边框和背景,发送Expose事件由client绘制窗口内容(miHandleValidateExposures)

代码块
languagejava
// pWin为移动的窗口,pSib为pWin移动在该窗口之上。若pSib为空,则pWin窗口在最上层
static void ReflectStackChange(WindowPtr pWin, WindowPtr pSib, VTKind kind){
    Bool WasViewable = (Bool) pWin->viewable;
    Bool anyMarked;
    WindowPtr pFirstChange;
    WindowPtr pLayerWin;
    ScreenPtr pScreen = pWin->drawable.pScreen;
    if (!pWin->parent)
        return; // if this is a root window, can't be restacked
    // pWin为移动窗口,pSib为空,pWin置底;若pWin下一个相邻窗口为pSib,栈层次未变,则什么都不做
    // pFirstChange:表示第一个被暴露的窗口
    // pWin向下移,则pFirstChange为pWin相邻的下一个窗口。pWin与pSib中间有兄弟窗口,当pWin在pSib上方时,需下移。
    // pWin向上移,则pFirstChange为pWin本身
    pFirstChange = MoveWindowInStack(pWin, pSib);
    if (WasViewable) {
        // miMarkOverlappedWindows,标记与pWin窗口重叠的兄弟窗口及其子窗口所在位置,这些窗口可能需要重绘
        // 若pFirstChange为pWin(窗口上移),则标记pWin及其子窗口,同时标记与pWin窗口(包括边框)重叠的兄弟窗口以及其子窗口
        // 若pFirstChange不为pWin(窗口下移),标记与pWin窗口重叠的兄弟窗口以及其子窗口
        anyMarked = (*pScreen->MarkOverlappedWindows) (pWin, pFirstChange, &pLayerWin);
        // pLayerWin初始化为pWin,不清楚这里为什么要判断
        if (pLayerWin != pWin)
            pFirstChange = pLayerWin;
        if (anyMarked) {
            // 有窗口重叠。若无窗口则不需重绘,修改窗口树结构即可。此场景必有重叠,pWin窗口与其子窗口重叠。
            // miValidateTree,计算父窗口以及所有子窗口的可剪接区域(boarderClip和clipList),以及exposed和borderExposed区域
            (*pScreen->ValidateTree) (pLayerWin->parent, pFirstChange, kind);
            // miHandleValidateExposures,深度优先,先绘制边框,再绘制exposed区域的背景,然后发送Expose事件给客户端绘制
            // 边框和背景的绘制在server端完成,exposed区域的内容通过发送Expose事件由client发起绘制完成
            (*pScreen->HandleExposures) (pLayerWin->parent);
            // 一般为空,只有当视频窗口时,xv扩展才需处理
            if (pWin->drawable.pScreen->PostValidateTree)
                (*pScreen->PostValidateTree) (pLayerWin->parent, pFirstChange, kind);
        }
    }
    if (pWin->realized)
        // 处理焦点和光标
        WindowsRestructured();
}

窗口栈管理

wayland对窗口栈管理分为组(group),层(layer),栈(stack),由MetaStack和MetaWindow管理实现。MetaStack处理3个信号事件:"changed", "window-added", "window-removed",这3个事件都涉及窗口栈的更新。

一般来说,顶层窗口作为组长,与子孙窗口分为一组。client可通过EWMH指定窗口为特定的组(WindowGroupHint)。一般只有X11窗口有该属性。MetaWindow::group保存组关系

代码块
languagejava
struct _MetaGroup {
  int refcount;
  MetaX11Display *x11_display;
  GSList *windows;       // 窗口列表
  Window group_leader;  // 组长
  char *startup_id;
  char *wm_client_machine;
};

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中的位置,越大越显示在上层,后续操作均按该栈序处理。窗口的层次切换后,也需保持相对位置。

使用如下两个函数来增加和删除窗口:

  1. meta_stack_add增加窗口:初始化窗口的stack_position值,为列表大小,发送"window-added"同步信号。新增窗口都显示在最上面。只能增加可堆叠窗口,处理完成后再发送"changed"同步信号

    1. meta_window_x11_is_stackable:非override redirect窗口

    2. meta_window_wayland_is_stackable:可显示窗口,MetaWaylandBuffer不为空

  2. 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窗口栈

代码块
languagejava
struct _MetaStack{
  GObject parent;
  MetaDisplay *display;  // The MetaDisplay containing this stack.
  GList *sorted;         // The MetaWindows of the windows we manage, sorted in order.
 
  // If this is zero, the local stack oughtn't to be brought up to date with the X server's stack, because it is in the middle of being updated.
  // If it is positive, the local stack is said to be "frozen", and will need to be thawed that many times before the stack can be brought up to date again.
  // You may freeze the stack with meta_stack_freeze() and thaw it with meta_stack_thaw().
  int freeze_count;    // 计数可实现嵌套freeze 
 
  // The last-known stack of all windows, bottom to top.  We cache it here so that subsequent times we'll be able to do incremental moves.
  GArray *last_all_root_children_stacked;
  gint n_positions; // Number of stack positions; same as the length of added, but kept for quick reference.
 
  unsigned int need_resort : 1;    // Is the stack in need of re-sorting?
  unsigned int need_relayer : 1;   // Are the windows in the stack in need of having their layers recalculated?
  unsigned int need_constrain : 1; // Are the windows in the stack in need of having their positions recalculated with respect to transiency (parent and child windows)?
};
 
// 窗口排序需考虑窗口的层次,窗口显示的约束等,先按层次排序,再按位置排序。
static void stack_ensure_sorted (MetaStack *stack) {
  // 对stack中的窗口分层,若层次有变化,则需重新计算窗口约束和重新排序。窗口的层次为窗口组中的最高层次
  stack_do_relayer (stack);
  // 显示约束,对于瞬态窗口,未脱离父子关系,则显示在窗口组的上层,对于脱离父子关系的瞬态窗口(MetaWindow::transient_for非空),则显示在原父窗口的上层
  stack_do_constrain (stack);
  // 重新排序,先按层,再按位置。重排完成后,延后调用check_fullscreen_func处理全屏窗口
  stack_do_resort (stack);
}
 
// 临时窗口类型(瞬态窗口)
gboolean meta_window_has_transient_type (MetaWindow *window)
{
  return (window->type == META_WINDOW_DIALOG ||
          window->type == META_WINDOW_MODAL_DIALOG ||
          window->type == META_WINDOW_TOOLBAR ||
          window->type == META_WINDOW_MENU ||
          window->type == META_WINDOW_UTILITY);
}

堆跟踪器

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.

代码块
languagejava
struct _MetaStackTracker {
  MetaDisplay *display;
  gulong xserver_serial;   // This is the serial of the last request we made that was reflected in xserver_stack
  GArray *verified_stack; // A combined stack containing X and Wayland windows but without any unverified operations applied.   
  // This is a queue of requests we've made to change the stacking order, where we haven't yet gotten a reply back from the server.
  GQueue *unverified_predictions; // Wayland server修改了X11窗口的配置,发送XConfigureWindow请求该X11 server,但还未收到X11 server的回复
 
  // This is how we think the stack is, based on verified_stack, and on the unverified_predictions we've made subsequent to verified_stack.
  GArray *predicted_stack;  // 操作和更新栈时,MetaWindow窗口栈的一个临时列表
 
  // Idle function used to sync the compositor's view of the window stack up with our best guess before a frame is drawn.
  guint sync_stack_later;
};
  • 调用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"等正向切换事件,都对应有反向切换的事件。本次只描述窗口正向切换流程。

代码块
languagejava
// src/core/keybindings.c
static void handle_switch (MetaDisplay *display, MetaWindow *event_window, ClutterKeyEvent *event, MetaKeyBinding  *binding, gpointer dummy) {
  gboolean backwards = meta_key_binding_is_reversed (binding);
  // backwards = false;
  do_choose_window (display, event_window, event, binding, backwards);
}
 
static void do_choose_window (MetaDisplay *display, MetaWindow *event_window, ClutterKeyEvent *event, MetaKeyBinding  *binding, gboolean backward){
  MetaWorkspaceManager *workspace_manager = display->workspace_manager;
  // type = META_TAB_LIST_NORMAL
  MetaTabList type = binding->handler->data;
  // 获取当前焦点窗口的下一个窗口
  MetaWindow *window = meta_display_get_tab_next (display, type, workspace_manager->active_workspace, NULL, backward);
 
  // 激活该窗口
  if (window)
    meta_window_activate (window, event->time);
}
  1. meta_display_get_tab_next:从工作台中获取切换窗口

  2. meta_window_activate:激活切换窗口

    1. meta_window_raise:将切换窗口放置在顶层,更新MetaStack窗口栈顺序,同步至MetaStackTracker窗口顺序

      1. meta_window_set_stack_position_no_sync:移动切换窗口,更新MetaStack窗口栈顺序

      2. meta_stack_changed:同步tracker、compositor和X11 server栈信息

        1. 调整MetaStackTracer窗口栈顺序

        2. 发送XConfigureWindow给X11 Server修改server上栈信息,根据信息调整栈位置,更新窗口显示

        3. 同步compositor的窗口栈,即MetaWindowActor栈

      3. meta_stack_update_window_tile_matches:调整窗口平铺位置

    2. meta_window_focus:窗口获取焦点,包括键盘输入焦点和光标

      1. 显示窗口,包括隐藏窗口,第一次显示的窗口,未放置窗口和图标窗口

      2. 发送SetInputFocus请求给X11 server,同步X11 server的焦点信息,由X11 Server处理焦点

      3. 关联输入设备与切换窗口

获取窗口

有几个地方维护了窗口列表,

  • MetaWindow:维护两类窗口列表,一类为原生Wayland窗口列表(wayland_windows);另一类为X11对应的Wayland窗口列表(x11_display->xids)

  • MetaWorkspace:维护mru_list窗口列表,属于该工作台中的窗口列表,按照MRU(most recently used)排序,最近访问的窗口排在前面

从X11和wayland server中找到所有在workspace中的MetaWindow窗口。

  1. 获取所有窗口列表:从MetaDisplay中获取所有的X11和Wayland窗口(不包括override redirect窗口)

  2. 初始化候选窗口列表:获取工作台(workspace)中类型为META_TAB_LIST_NORMAL的窗口列表,非最小化窗口在前,最小化窗口在后,每组列表保序(MRU序),拼接成一个链表

  3. 向前追加状态为demands_attention的窗口值候选窗口列表中:该状态窗口不在工作台中,类型为META_TAB_LIST_NORMAL,需要额外追加

  4. 候选窗口列表中第一个非焦点窗口,即为候选窗口

注:获取所有窗口列表时,列表并未按照MRU排序,在追加demands_attention窗口时,窗口创建越迟越优先。

代码块
languagejava
// 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

代码块
languagejava
void meta_window_activate (MetaWindow *window, guint32 timestamp){
  // We're not really a pager, but the behavior we want is the same as if we were such.
  // If we change the pager behavior later, we could revisit this and just add extra flags to window_activate.
  meta_window_activate_full (window, timestamp, META_CLIENT_TYPE_PAGER, NULL);
}
 
// 激活窗口以及与该窗口相关的transient_for属性的窗口
void meta_window_activate_full (MetaWindow *window, guint32 timestamp, MetaClientType  source_indication, MetaWorkspace  *workspace){
  MetaWorkspaceManager *workspace_manager = window->display->workspace_manager;
  // timestamp为0表示最新事件,默认取当前时间戳,比任何窗口的user_time时间戳新
  gboolean allow_workspace_switch = (timestamp != 0);
  if (timestamp != 0 && XSERVER_TIME_IS_BEFORE (timestamp, window->display->last_user_time)){
    // client通过类型为_NET_ACTIVE_WINDOW的atom请求,更新了display的交互时间,比该事件要新,则只设置window的wm_state_demands_attention为真,下次再处理
    meta_window_set_demands_attention(window);
    return;
  }
 
  // 获取最新时间戳
  if (timestamp == 0)
    timestamp = meta_display_get_current_time_roundtrip (window->display);
  // 更新窗口的net_wm_user_time,以及display->last_user_time
  meta_window_set_user_time (window, timestamp);
  /* disable show desktop mode unless we're a desktop component */
  maybe_leave_show_desktop_mode (window);
 
  // For non-transient windows, we just set up a pulsing indicator, rather than move windows or workspaces. See http://bugzilla.gnome.org/show_bug.cgi?id=482354
  if (window->transient_for == NULL && !allow_workspace_switch && !meta_window_located_on_workspace (window, workspace)){
    // 不在工作台中,且事件时间戳为0的普通窗口
    meta_window_set_demands_attention (window);
    return;
  }
  else if (window->transient_for != NULL) {
      /* Move transients to current workspace - preference dialogs should appear over the source window.  */
      // 3类窗口需切换至该工作台,有可能切换不成功,其中两类是由于transient_for属性脱离的关系的窗口,祖先和子孙窗口
      // 1. 自身窗口
      // 2. 将transient_for递归指向该窗口的所有窗口,即该窗口的子孙窗口
      // 3. 该窗口的transient_for指向的窗口,以及transient_for递归指向的祖先窗口。也就是该窗口的祖先窗口
      meta_window_change_workspace (window, workspace);
    }
  // 取消窗口的阴影,client设置该窗口为阴影,当激活时,需取消其阴影窗口
  if (window->shaded)
    meta_window_unshade (window, timestamp);
  // 恢复窗口显示,以及脱离关系的祖先窗口,该窗口的transient_for指向的窗口
  unminimize_window_and_all_transient_parents (window);
 
  // 将窗口显示在最上面,在此上下文下,条件为真,更新compositor,actor的窗口栈顺序
  if (meta_prefs_get_raise_on_click () || source_indication == META_CLIENT_TYPE_PAGER)
    meta_window_raise (window);
  // 窗口在此工作台中,则获取焦点,不在该工作台中,则切换至窗口所在工作台,并使窗口获得焦点
  if (meta_window_located_on_workspace (window, workspace))
    meta_window_focus (window, timestamp);
  else
    meta_workspace_activate_with_focus (window->workspace, window, timestamp);
  // 发送一个ping请求,检测client是否有响应,则弹出关闭对话框
  meta_window_check_alive (window, timestamp);
}

meta_window_raise

显示窗口,对于普通窗口只显示自身窗口,对于具有transient_for属性的窗口,先显示祖先窗口,再显示自身窗口

代码块
languagejava
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

代码块
languagejava
// 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

代码块
languagejava
// managed为顶层窗口有序列表,队尾窗口显示在最顶层。
// 更新MetaStackTracker中窗口列表序列,并将更新同步至X11 server,并启动META_LATER_SYNC_STACK延时调用stack_tracker_sync_stack_later,该函数用于同步compositor的窗口栈
void meta_stack_tracker_restack_managed (MetaStackTracker *tracker, const guint64 *managed, int n_managed){
  guint64 *windows;
  int n_windows;
  int old_pos, new_pos;
  if (n_managed == 0)
    return;
  // 获取tracker中的窗口列表,为有序列表,队尾的窗口显示在最顶层
  meta_stack_tracker_get_stack (tracker, &windows, &n_windows);
  // If the top window has to be restacked, we don't want to move it to the very top of the stack,
  // since apps expect override-redirect windows to stay near the top of the X stack;
  // we instead move it above all managed windows (or above the guard window if there are no non-hidden managed windows.)
  old_pos = n_windows - 1;
  // 跳过tracker中不存在的窗口,找到第一个顶级窗口或者guard窗口,顶级窗口为可显示的非override redirect窗口;guard窗口为可显示窗口与非显示窗口的分界线
  for (old_pos = n_windows - 1; old_pos >= 0; old_pos--) {
    MetaWindow *old_window = meta_display_lookup_stack_id (tracker->display, windows[old_pos]);
    if ((old_window && !old_window->override_redirect && !old_window->unmanaging) || meta_stack_tracker_is_guard_window (tracker, windows[old_pos]))
      break;
  }
 
  new_pos = n_managed - 1;
  if (managed[new_pos] != windows[old_pos]){
    // Move the first managed window in the new stack above all managed windows
    // 更新MetaStackTracer,将new窗口移至old窗口位置,其它窗口对应移一位。对于X11窗口,发送XConfigureWindow至X11 server同步窗口叠层。
   meta_stack_tracker_raise_above (tracker, managed[new_pos], windows[old_pos]);
    // 重新获取windows,更新了window在tracer中的位置
    meta_stack_tracker_get_stack (tracker, &windows, &n_windows);
  }
  old_pos--;
  new_pos--;
 
  while (old_pos >= 0 && new_pos >= 0) {
    if (meta_stack_tracker_is_guard_window (tracker, windows[old_pos]))
      break;
    // 窗口的相对位置未变,则跳过
    if (windows[old_pos] == managed[new_pos]){
      old_pos--;
      new_pos--;
      continue;
    }
    // window不在tracker中,或者为override_redirect和unmanaging窗口,则跳过。managed中不存在这些窗口
    MetaWindow *old_window = meta_display_lookup_stack_id (tracker->display, windows[old_pos]);
    if (!old_window || old_window->override_redirect || old_window->unmanaging){
       old_pos--;
       continue;
    }
    // 更新MetaStackTracer,将new_pos窗口移至new_pos+1窗口后面,其它窗口对应移一位。对于X11窗口,发送XConfigureWindow至X11 server同步窗口叠层。
   meta_stack_tracker_lower_below (tracker, managed[new_pos], managed[new_pos + 1]);
    meta_stack_tracker_get_stack (tracker, &windows, &n_windows);
    // Moving managed[new_pos] above windows[old_pos] moves the window at old_pos down by one, we'll examine it again to see if it matches the next new window
    old_pos--;
    new_pos--;
  }
  while (new_pos > 0) {
      meta_stack_tracker_lower_below (tracker, managed[new_pos], managed[new_pos - 1]);
      new_pos--;
    }
}
 
// 将window移至sibling位置,其它窗口相应移一位,类似于ALT+TAB键的窗口切换,下层窗口移至最前面
static void meta_stack_tracker_raise_above (MetaStackTracker *tracker, guint64 window, guint64 sibling) {
  gulong serial = 0;
  MetaX11Display *x11_display = tracker->display->x11_display;
  if (META_STACK_ID_IS_X11 (window)) {
    // 向X11 server同步窗口堆叠层次请求,由X11 server处理该请求
    XWindowChanges changes;
    changes.sibling = sibling ? find_x11_sibling_downwards (tracker, sibling) : None;
    if (changes.sibling != find_x11_sibling_downwards (tracker, window)){
      serial = XNextRequest (x11_display->xdisplay);
      meta_x11_error_trap_push (x11_display);
      changes.stack_mode = changes.sibling ? Above : Below;
      XConfigureWindow (x11_display->xdisplay, (Window)window, (changes.sibling ? CWSibling : 0) | CWStackMode, &changes);
      meta_x11_error_trap_pop (x11_display);
    }
  }
  // 更新MetaStackTracer的窗口列表
  meta_stack_tracker_record_raise_above (tracker, window, sibling, serial);
}
 
static void meta_stack_tracker_record_raise_above (MetaStackTracker *tracker, guint64 window, guint64 sibling, gulong serial){
  MetaStackOp *op = g_new0 (MetaStackOp, 1);
  op->any.type = STACK_OP_RAISE_ABOVE;
  op->any.serial = serial;
  op->any.window = window;
  op->raise_above.sibling = sibling;
  stack_tracker_apply_prediction (tracker, op);
}
 
static void stack_tracker_apply_prediction (MetaStackTracker *tracker, MetaStackOp *op) {
  gboolean free_at_end = FALSE;
  // If this operation doesn't involve restacking X windows then it's implicitly verified.
  // We can apply it immediately unless there are outstanding X restacks that haven't yet been confirmed.
  if (op->any.serial == 0 && tracker->unverified_predictions->length == 0) {
    if (meta_stack_op_apply (tracker, op, tracker->verified_stack, APPLY_DEFAULT))
      meta_stack_tracker_queue_sync_stack (tracker);
    free_at_end = TRUE;
  }
  else {
    g_queue_push_tail (tracker->unverified_predictions, op);
  }
  // 启动MetaLater,用于延后处理compositor的窗口栈同步(stack_tracker_sync_stack_later)
  if (!tracker->predicted_stack || meta_stack_op_apply (tracker, op, tracker->predicted_stack, APPLY_DEFAULT))
    meta_stack_tracker_queue_sync_stack (tracker);
  if (free_at_end)
    meta_stack_op_free (op);
}
 
void meta_stack_tracker_queue_sync_stack (MetaStackTracker *tracker){
  if (tracker->sync_stack_later == 0) {
    tracker->sync_stack_later = meta_later_add (META_LATER_SYNC_STACK, stack_tracker_sync_stack_later, tracker, NULL);
  }
}

stack_tracker_sync_stack_later

代码块
languagejava
static gboolean stack_tracker_sync_stack_later (gpointer data){
  meta_stack_tracker_sync_stack (data);
  return FALSE;
}
 
void meta_stack_tracker_sync_stack (MetaStackTracker *tracker){
  guint64 *windows;
  GList *meta_windows;
  int n_windows;
  int i;
 
  if (tracker->sync_stack_later) {
    meta_later_remove (tracker->sync_stack_later);
    tracker->sync_stack_later = 0;
  }
  // 将override redirect层的窗口提到最上层,有可能更新MetaStackTracker的窗口栈,形成循环调用
  meta_stack_tracker_keep_override_redirect_on_top (tracker);
  meta_stack_tracker_get_stack (tracker, &windows, &n_windows);
  meta_windows = NULL;
  for (i = 0; i < n_windows; i++){
    guint64 window = windows[i];
    if (META_STACK_ID_IS_X11 (window)){
      MetaX11Display *x11_display = tracker->display->x11_display;
      MetaWindow *meta_window = NULL;
 
      if (x11_display)
         meta_window = meta_x11_display_lookup_x_window (x11_display, (Window) window);
       // When mapping back from xwindow to MetaWindow we have to be a bit careful;
       // children of the root could include unmapped windows created by toolkits for internal purposes, including ones that we have registered in our XID => window table.
       // (Wine uses a toplevel for _NET_WM_USER_TIME_WINDOW; see window-prop.c:reload_net_wm_user_time_window() for registration.)
       if (meta_window && ((Window)window == meta_window->xwindow || (meta_window->frame && (Window)window == meta_window->frame->xwindow)))
         meta_windows = g_list_prepend (meta_windows, meta_window);
    }
    else
      meta_windows = g_list_prepend (meta_windows, meta_display_lookup_stamp (tracker->display, window));
  }
  // 同步compositor的窗口栈,即MetaWindowActor栈
  meta_compositor_sync_stack (tracker->display->compositor, meta_windows);
  g_list_free (meta_windows);
  meta_display_restacked (tracker->display);
}
 
void meta_compositor_sync_stack (MetaCompositor  *compositor, GList *stack){
  MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor);
  GList *old_stack;
  // This is painful because hidden windows that we are in the process of animating out of existence.
  // They'll be at the bottom of the stack of X windows, but we want to leave them in their old position until the animation effect finishes.
  /* Sources: first window is the highest */
  stack = g_list_copy (stack); /* The new stack of MetaWindow */
  old_stack = g_list_reverse (priv->windows); /* The old stack of MetaWindowActor */
  priv->windows = NULL;
  while (TRUE){
    MetaWindowActor *old_actor = NULL, *stack_actor = NULL, *actor;
    MetaWindow *old_window = NULL, *stack_window = NULL, *window;
 
    // Find the remaining top actor in our existing stack (ignoring windows that have been hidden and are no longer animating)
    while (old_stack) {
      old_actor = old_stack->data;
      old_window = meta_window_actor_get_meta_window (old_actor);
      if ((old_window->hidden || old_window->unmanaging) && !meta_window_actor_effect_in_progress (old_actor)){
        old_stack = g_list_delete_link (old_stack, old_stack);
        old_actor = NULL;
      }
      else
        break;
    }
 
    /* And the remaining top actor in the new stack */
    while (stack) {
      stack_window = stack->data;
      stack_actor = meta_window_actor_from_window (stack_window);
      if (!stack_actor) {
        stack = g_list_delete_link (stack, stack);
      }
      else
        break;
    }
    if (!old_actor && !stack_actor) /* Nothing more to stack */
      break;
 
    // We usually prefer the window in the new stack, but if we found a hidden window in the process of being animated out of existence in the old stack we use that instead.
    // We've filtered out non-animating hidden windows above.
    if (old_actor && (!stack_actor || old_window->hidden || old_window->unmanaging)){
      actor = old_actor;
      window = old_window;
    }
    else{
      actor = stack_actor;
      window = stack_window;
    }
 
    // OK, we know what actor we want next. Add it to our window list, and remove it from both source lists.
    // (It will be at the front of at least one, hopefully it will be near the front of the other.)
    priv->windows = g_list_prepend (priv->windows, actor);
    stack = g_list_remove (stack, window);
    old_stack = g_list_remove (old_stack, actor);
  }
  sync_actor_stacking (compositor);
  update_top_window_actor (compositor);
}
 
// 根据compositor的MetaWindowActor序更新ClutterActor类型中的窗口栈
static void sync_actor_stacking (MetaCompositor *compositor){
  MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor);
  GList *children;
  GList *expected_window_node;
  GList *tmp;
  GList *old;
  GList *backgrounds;
  gboolean has_windows;
  gboolean reordered;
 
  // NB: The first entries in the lists are stacked the lowest
  // Restacking will trigger full screen redraws, so it's worth a little effort to make sure we actually need to restack before we go ahead and do it
  children = clutter_actor_get_children (priv->window_group);
  has_windows = FALSE;
  reordered = FALSE;
  // We allow for actors in the window group other than the actors we know about, but it's up to a plugin to try and keep them stacked correctly
  // First we collect a list of all backgrounds, and check if they're at the bottom. Then we check if the window actors are in the correct sequence
  backgrounds = NULL;
  expected_window_node = priv->windows;
  for (old = children; old != NULL; old = old->next){
    ClutterActor *actor = old->data;
    if (META_IS_BACKGROUND_GROUP (actor) || META_IS_BACKGROUND_ACTOR (actor)){
      backgrounds = g_list_prepend (backgrounds, actor);
      // background actor在window actor的上面,则需要重排
      if (has_windows)
        reordered = TRUE;
    }
    else if (META_IS_WINDOW_ACTOR (actor) && !reordered){
      has_windows = TRUE;
      // 跟composite的窗口序一致,则不需要重排,否则需要重排
      if (expected_window_node != NULL && actor == expected_window_node->data)
        expected_window_node = expected_window_node->next;
      else
        reordered = TRUE;
    }
  }
  g_list_free (children);
  if (!reordered){
    g_list_free (backgrounds);
    return;
  }
 
  // reorder the actors by lowering them in turn to the bottom of the stack. windows first, then background.
  // We reorder the actors even if they're not parented to the window group, to allow stacking to work with intermediate actors (eg during effects)
  for (tmp = g_list_last (priv->windows); tmp != NULL; tmp = tmp->prev){
    ClutterActor *actor = tmp->data, *parent;
    parent = clutter_actor_get_parent (actor);
    clutter_actor_set_child_below_sibling (parent, actor, NULL);
  }
 
  // we prepended the backgrounds above so the last actor in the list should get lowered to the bottom last.
  for (tmp = backgrounds; tmp != NULL; tmp = tmp->next){
    ClutterActor *actor = tmp->data, *parent;
    parent = clutter_actor_get_parent (actor);
    clutter_actor_set_child_below_sibling (parent, actor, NULL);
  }
  g_list_free (backgrounds);
}

meta_window_focus

代码块
languagejava
// 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

显示或隐藏窗口

代码块
languagejava
// 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,但未显示过

  • 未放置窗口:从未计算过窗口的放置位置,也就是从未显示过的窗口

  • 图标窗口:窗口已最小化,使用图标显示的窗口

代码块
languagejava
static void meta_window_show (MetaWindow *window){
  gboolean takes_focus_on_map;
  gboolean place_on_top_on_map;
  gboolean notify_demands_attention = FALSE;
  MetaDisplay *display = window->display;
  MetaWindow *focus_window = window->display->focus_window;  /* May be NULL! */
  gboolean did_show = FALSE;
  gboolean needs_stacking_adjustment = FALSE;
  // window是否需要获取光标,是否需要顶层显示
  window_state_on_map (window, &takes_focus_on_map, &place_on_top_on_map);
 
  // Now, in some rare cases we should *not* put a new window on top.
  // These cases include certain types of windows showing for the first time,
  // and any window which would be covered because of another window being set "above" ("always on top").
 
  // FIXME: Although "place_on_top_on_map" and "takes_focus_on_map" are generally based on the window type,
  // there is a special case when the focus window is a terminal for them both to be false; this should probably rather be a term in the "if" condition below.
  if (focus_window != NULL && window->showing_for_first_time && ((!place_on_top_on_map && !takes_focus_on_map) || window_would_be_covered (window))) {
    if (!meta_window_is_ancestor_of_transient (focus_window, window)){
      needs_stacking_adjustment = TRUE;
      if (!window->placed)
        window->denied_focus_and_not_transient = TRUE;
    }
  }
  if (!window->placed){
    if (window->monitor && meta_prefs_get_auto_maximize() && window->showing_for_first_time && window->has_maximize_func){
      MetaRectangle work_area;
      meta_window_get_work_area_for_monitor (window, window->monitor->number, &work_area);
      /* Automaximize windows that map with a size > MAX_UNMAXIMIZED_WINDOW_AREA of the work area */
      if (window->rect.width * window->rect.height > work_area.width * work_area.height * MAX_UNMAXIMIZED_WINDOW_AREA){
        window->maximize_horizontally_after_placement = TRUE;
        window->maximize_vertically_after_placement = TRUE;
      }
    }
    meta_window_force_placement (window, FALSE);
  }
  if (needs_stacking_adjustment){
    // This window isn't getting focus on map.  We may need to do some special handing with it in regards to
    // the stacking of the window, the MRU position of the window and the demands attention setting of the window.
 
    // Firstly, set the flag so we don't give the window focus anyway and confuse people.
   gboolean overlap = windows_overlap (window, focus_window);
    takes_focus_on_map = FALSE;
    /* We want alt tab to go to the denied-focus window */
    ensure_mru_position_after (window, focus_window);
 
    // We don't want the denied-focus window to obscure the focus window,
    // and if we're in both click-to-focus mode and raise-on-click mode then we want to maintain the invariant that MRU order == stacking order.
    // The need for this if comes from the fact that in sloppy/mouse focus the focus window may not overlap other windows and also can be considered "below" them;
    // this combination means that placing the denied-focus window "below" the focus window in the stack when it doesn't overlap it confusingly places
    // that new window below a lot of other windows.
    if (overlap || (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK && meta_prefs_get_raise_on_click ()))
      meta_window_stack_just_below (window, focus_window);
 
    // If the window will be obscured by the focus window, then the user might not notice the window appearing so set the demands attention hint.
    // We set the hint ourselves rather than calling meta_window_set_demands_attention() because that would cause a recalculation of overlap,
    // and a call to set_net_wm_state() which we are going to call ourselves here a few lines down.
    if (overlap && !window->wm_state_demands_attention){
      window->wm_state_demands_attention = TRUE;
      notify_demands_attention = TRUE;
    }
  }
  if (window->hidden){
    meta_stack_freeze (window->display->stack);
    window->hidden = FALSE;
    meta_stack_thaw (window->display->stack);
    did_show = TRUE;
  }
  if (window->iconic){
    window->iconic = FALSE;
    set_wm_state (window);
  }
  if (!window->visible_to_compositor){
    MetaCompEffect effect = META_COMP_EFFECT_NONE;
    window->visible_to_compositor = TRUE;
    switch (window->pending_compositor_effect){
      case META_COMP_EFFECT_CREATE:
      case META_COMP_EFFECT_UNMINIMIZE:
        effect = window->pending_compositor_effect;
        break;
      case META_COMP_EFFECT_NONE:
      case META_COMP_EFFECT_DESTROY:
      case META_COMP_EFFECT_MINIMIZE:
        break;
    }
    meta_compositor_show_window (window->display->compositor, window, effect);
    window->pending_compositor_effect = META_COMP_EFFECT_NONE;
  }
 
  // We don't want to worry about all cases from inside implement_showing(); we only want to worry about focus if this window has not been shown before.
  if (window->showing_for_first_time){
    window->showing_for_first_time = FALSE;
    if (takes_focus_on_map){
      guint32     timestamp;
      timestamp = meta_display_get_current_time_roundtrip (window->display);
      meta_window_focus (window, timestamp);
    }
    else if (display->x11_display){
      // Prevent EnterNotify events in sloppy/mouse focus from erroneously focusing the window that had been denied focus.
      // FIXME: This introduces a race; I have a couple ideas for a better way to accomplish the same thing, but they're more involved so do it this way for now.
      meta_x11_display_increment_focus_sentinel (display->x11_display);
    }
  }
  set_net_wm_state (window);
  if (did_show && window->struts){
    invalidate_work_areas (window);
  }
  // 注册MetaLater(META_LATER_CHECK_FULLSCREEN),延后调用check_fullscreen_func处理全屏
  if (did_show)
    meta_display_queue_check_fullscreen (window->display);
 
  // we have shown the window, we no longer want to consider the initial timestamp in any subsequent deliberations whether to focus this window or not, so clear the flag.
  window->initial_timestamp_set = FALSE;
 
  if (notify_demands_attention){
    g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_DEMANDS_ATTENTION]);
    g_signal_emit_by_name (window->display, "window-demands-attention", window);
  }
 
  // on_window_shown (src/wayland/meta-window-wayland.c) : 更新状态
  if (did_show)
    g_signal_emit (window, window_signals[SHOWN], 0);
}

meta_window_x11_focus

代码块
languagejava
static void meta_window_x11_focus (MetaWindow *window, guint32 timestamp){
  MetaWindowX11 *window_x11 = META_WINDOW_X11 (window);
  MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11);
  // For output-only or shaded windows, focus the frame.
  // This seems to result in the client window getting key events though, so I don't know if it's icccm-compliant.
 
  // Still, we have to do this or keynav breaks for these windows.
  // meta_window_x11_is_focusable : input为真,或者使用"WM_TAKE_FOCUS" atom
  // meta_window_wayland_is_focusable : input为真,可输入的窗口
  if (window->frame && (window->shaded || !meta_window_is_focusable (window))) {
    meta_display_set_input_focus (window->display, window, TRUE, timestamp);
  }
  else {
    if (window->input) {
      meta_display_set_input_focus (window->display,  window, FALSE, timestamp);
    }
    if (priv->wm_take_focus) {
      if (!window->input){
        // The "Globally Active Input" window case, where the window doesn't want us to call XSetInputFocus on it, but does want us to send a WM_TAKE_FOCUS.
        // Normally, we want to just leave the focus undisturbed until the window responds to WM_TAKE_FOCUS,
        // but if we're unmanaging the current focus window we *need* to move the focus away, so we focus the no focus window before sending WM_TAKE_FOCUS,
        // and eventually the default focus window excluding this one, if meanwhile we don't get any focus request.
        if (window->display->focus_window != NULL && window->display->focus_window->unmanaging){
          meta_display_unset_input_focus (window->display, timestamp);
          maybe_focus_default_window (window->display, window, timestamp);
        }
      }
      request_take_focus (window, timestamp);
    }
  }
}
 
void meta_display_set_input_focus (MetaDisplay *display, MetaWindow *window, gboolean focus_frame, guint32 timestamp){
  if (meta_display_timestamp_too_old (display, &timestamp))
    return;
  if (display->x11_display){
    meta_x11_display_set_input_focus (display->x11_display, window, focus_frame, timestamp);
  }
  meta_display_update_focus_window (display, window);
  display->last_focus_time = timestamp;
  if (window == NULL || window != display->autoraise_window)
    meta_display_remove_autoraise_callback (display);
}
meta_x11_display_set_input_focus
代码块
languagejava
// 发送SetInputFocus请求给X11 server,由X11 server处理input focus事件;发送ChangeProperty请求,修改根窗口"_NET_ACTIVE_WINDOW"属性
void meta_x11_display_set_input_focus (MetaX11Display *x11_display, MetaWindow *window, gboolean focus_frame, uint32_t timestamp){
  Window xwindow;
  gulong serial;
  if (window)
    xwindow = focus_frame ? window->frame->xwindow : window->xwindow;
  else
    xwindow = x11_display->no_focus_window;
 
  meta_x11_error_trap_push (x11_display);
  // 发送SetInputFocus请求给X11 server
  meta_x11_display_set_input_focus_internal (x11_display, xwindow, timestamp);
  serial = XNextRequest (x11_display->xdisplay);
  // 修改根窗口"_NET_ACTIVE_WINDOW"属性,发送ChangeProperty请求
  meta_x11_display_update_focus_window (x11_display, xwindow, serial, TRUE);
  meta_x11_error_trap_pop (x11_display);
}
 
static void meta_x11_display_set_input_focus_internal (MetaX11Display *x11_display, Window xwindow, uint32_t timestamp){
  meta_x11_error_trap_push (x11_display);
  // In order for mutter to know that the focus request succeeded, we track the serial of the "focus request" we made, but if we take the serial of the XSetInputFocus request,
  // then there's no way to determine the difference between focus events as a result of the SetInputFocus and focus events that other clients send around the same time.
  // Ensure that we know which is which by making two requests that the server will process at the same time.
  XGrabServer (x11_display->xdisplay);
  // 发送SetInputFocus请求给X11 server,X11处理SetInputFocus实际工作
  XSetInputFocus (x11_display->xdisplay, xwindow, RevertToPointerRoot,timestamp);
  // 更改timestamp_pinging_window窗口属性"_MUTTER_FOCUS_SET"类型,发送ChangeProperty请求该X11 server。
  XChangeProperty (x11_display->xdisplay, x11_display->timestamp_pinging_window, x11_display->atom__MUTTER_FOCUS_SET, XA_STRING, 8, PropModeAppend, NULL, 0);
 
  XUngrabServer (x11_display->xdisplay);
  XFlush (x11_display->xdisplay);
  meta_x11_error_trap_pop (x11_display);
}
 
void meta_x11_display_update_focus_window (MetaX11Display *x11_display, Window xwindow, gulong serial, gboolean focused_by_us){
  x11_display->focus_serial = serial;
  x11_display->focused_by_us = !!focused_by_us;
  if (x11_display->focus_xwindow == xwindow)
    return;
  x11_display->focus_xwindow = xwindow;
  meta_x11_display_update_active_window_hint (x11_display);
}
 
static void meta_x11_display_update_active_window_hint (MetaX11Display *x11_display){
  MetaWindow *focus_window;
  gulong data[1];
  if (x11_display->display->closing)
    return; /* Leave old value for a replacement */
  focus_window = meta_x11_display_lookup_x_window (x11_display, x11_display->focus_xwindow);
 
  if (focus_window)
    data[0] = focus_window->xwindow;
  else
    data[0] = None;
 
  meta_x11_error_trap_push (x11_display);
  XChangeProperty (x11_display->xdisplay, x11_display->xroot, x11_display->atom__NET_ACTIVE_WINDOW, XA_WINDOW, 32, PropModeReplace, (guchar*) data, 1);
  meta_x11_error_trap_pop (x11_display);
}
meta_display_update_focus_window
代码块
languagejava
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;
}