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ïŒ
// 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äżćç»ć łçł»
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äžçäœçœźïŒè¶ć€§è¶æŸç€șćšäžć±ïŒćç»æäœćæèŻ„æ ćșć€çăçȘćŁçć±æŹĄćæąćïŒäčéäżæçžćŻčäœçœźă
äœżçšćŠäžäž€äžȘćœæ°æ„ćąć ćć é€çȘćŁïŒ
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"ćæ„俥ć·
MetaStackććșâchangedâ俥ć·ïŒäżĄć·ć€çćœæ°äžșon_stack_changedïŒéæ°ćŻčwaylandćX11ç饶ć±çȘćŁć æ æćșă
æłšïŒäœżçš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çȘćŁæ
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.
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"çæŁććæąäș件ïŒéœćŻčćșæćććæąçäș件ăæŹæŹĄćȘæèż°çȘćŁæŁććæąæ”çšă
// 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);
} |
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
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ć±æ§ççȘćŁïŒć æŸç€șç„ć çȘćŁïŒćæŸç€șèȘèș«çȘćŁ
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
// 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
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
// 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ïŒäœæȘæŸç€șèż
æȘæŸçœźçȘćŁïŒä»æȘèźĄçźèżçȘćŁçæŸçœźäœçœźïŒäčć°±æŻä»æȘæŸç€șèżççȘćŁ
ćŸæ çȘćŁïŒçȘćŁć·Čæć°ćïŒäœżçšćŸæ æŸç€șççȘćŁ
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
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, ×tamp))
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
// ćé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
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;
}