def change_layer(self, up=True, top_bottom=False):
        """Raise a window above its peers or move it below them, depending on 'up'.
        Raising a normal window will not lift it above pinned windows etc.

        There are a few important things to take note of when relaying windows:
        1. If a window has a defined parent, it should not be moved underneath it.
           In case children are blocking, this could leave an application in an unusable state.
        2. If a window has children, they should be moved along with it.
        3. If a window has a defined parent, either move the parent or do nothing at all.
        4. EMWH-flags follow strict layering rules:
           https://specifications.freedesktop.org/wm-spec/1.3/ar01s07.html#STACKINGORDER
        """
        if len(self.qtile.windows_map) < 2:
            return

        if self.group is None and not isinstance(self, Static):
            return

        # Use the window's group or current group if this isn't set (e.g. Static windows)
        group = self.group or self.qtile.current_group

        parent = self.window.get_wm_transient_for()
        if parent is not None and not up:
            return

        layering = self.get_layering_information()

        # Comparison of layer states: -1 if window is now in a lower state group,
        # 0 if it's in the same group and 1 if it's in a higher group
        moved = (self.previous_layer > layering) - (layering > self.previous_layer)
        self.previous_layer = layering

        stack = list(self.qtile.core._root.query_tree())
        if self.wid not in stack or len(stack) < 2:
            return

        # Get all windows for the group and add Static windows to ensure these are included
        # in the stacking
        group_windows = group.windows.copy()
        statics = [win for win in self.qtile.windows_map.values() if isinstance(win, Static)]
        group_windows.extend(statics)

        if group.screen is not None:
            group_bars = [gap for gap in group.screen.gaps if isinstance(gap, bar.Bar)]
        else:
            group_bars = []

        # Get list of windows that are in the stack and managed by qtile
        # List of tuples (XWindow object, transient_for, layering_information)
        windows = list(
            map(
                lambda w: (
                    w.window,
                    w.window.get_wm_transient_for(),
                    w.get_layering_information(),
                ),
                group_windows,
            )
        )

        # Remove any windows that aren't in the server's stack
        windows = list(filter(lambda w: w[0].wid in stack, windows))

        # Sort this list to match stacking order reported by server
        windows.sort(key=lambda w: stack.index(w[0].wid))

        # Get lists of windows on lower, higher or same "layer" as window
        lower = [w[0].wid for w in windows if w[2] > layering]
        higher = [w[0].wid for w in windows if w[2] < layering]
        same = [w[0].wid for w in windows if w[2] == layering]

        # We now need to identify the new position in the stack

        # If the window has a parent, the window should just be put above it
        # If the parent isn't being managed by qtile then it may not be stacked correctly
        if parent and parent in self.qtile.windows_map:
            # If the window is modal then it should be placed above every other window that is in that window group
            # e.g. the parent of the dialog and any other window that is also transient for the same parent.
            if "_NET_WM_STATE_MODAL" in self.window.get_net_wm_state():
                window_group = [parent]
                window_group.extend(
                    k
                    for k, v in self.qtile.windows_map.items()
                    if v.window.get_wm_transient_for() == parent
                )
                window_group.sort(key=stack.index)

                # Make sure we're above the last window in that group
                sibling = window_group[-1]

            else:
                sibling = parent

            above = True

        # Now we just check whether the window has changed layer.

        # If we're forcing to top or bottom of current layer...
        elif top_bottom:
            # If there are no other windows in the same layer then there's nothing to do
            if not same:
                return

            if up:
                sibling = same[-1]
                above = True
            else:
                sibling = same[0]
                above = False

        # There are no windows in the desired layer (should never happen) or
        # we've moved to a new layer and are the only window in that layer
        elif not same or (len(same) == 1 and moved != 0):
            # Try to put it above the last window in the lower layers
            if lower:
                sibling = lower[-1]
                above = True

            # Or below the first window in the higher layers
            elif higher:
                sibling = higher[0]
                above = False

            # Don't think we should end up here but, if we do...
            else:
                # Put the window above the highest window if we're raising it
                if up:
                    sibling = stack[-1]
                    above = True

                # or below the lowest window if we're lowering the window
                else:
                    sibling = stack[0]
                    above = False

        else:
            # Window has moved to a lower layer state
            if moved < 0:
                if self.kept_below:
                    sibling = same[0]
                    above = False
                else:
                    sibling = same[-1]
                    above = True

            # Window is in same layer state
            elif moved == 0:
                try:
                    pos = same.index(self.wid)
                except ValueError:
                    pos = len(same) if up else 0
                if not up:
                    pos = max(0, pos - 1)
                else:
                    pos = min(pos + 1, len(same) - 1)
                sibling = same[pos]
                above = up

            # Window is in a higher layer
            else:
                if self.kept_above:
                    sibling = same[-1]
                    above = True
                else:
                    sibling = same[0]
                    above = False

        # If the sibling is the current window then we just check if any windows in lower/higher layers are
        # stacked incorrectly and, if so, restack them. However, we don't need to configure stacking for this
        # window
        if sibling == self.wid:
            index = stack.index(self.wid)

            # We need to make sure the bars are included so add them now
            if group_bars:
                for group_bar in group_bars:
                    bar_layer = group_bar.window.get_layering_information()
                    if bar_layer > layering:
                        lower.append(group_bar.window.wid)
                    elif bar_layer < layering:
                        higher.append(group_bar.window.wid)

                # Sort the list to match the server's stacking order
                lower.sort(key=lambda wid: stack.index(wid))
                higher.sort(key=lambda wid: stack.index(wid))

            for wid in [w for w in lower if stack.index(w) > index]:
                self.qtile.windows_map[wid].window.configure(
                    stackmode=xcffib.xproto.StackMode.Below, sibling=same[0]
                )

            # We reverse higher as each window will be placed above the last item in the current layer
            # this means the last item we stack will be just above the current layer.
            for wid in [w for w in higher[::-1] if stack.index(w) < index]:
                self.qtile.windows_map[wid].window.configure(
                    stackmode=xcffib.xproto.StackMode.Above, sibling=same[-1]
                )

            return

        # Window needs new stacking info. We tell the server to stack the window
        # above or below a given "sibling"
        self.window.configure(
            stackmode=xcffib.xproto.StackMode.Above if above else xcffib.xproto.StackMode.Below,
            sibling=sibling,
        )

        # Move window's children if we were moved upwards
        if above:
            self.raise_children(stack=stack)

        self.qtile.core.update_client_lists()
Raise a window above its peers or move it below them, depending on 'up'.
        Raising a normal window will not lift it above pinned windows etc.

        There are a few important things to take note of when relaying windows:
        1. If a window has a defined parent, it should not be moved underneath it.
           In case children are blocking, this could leave an application in an unusable state.
        2. If a window has children, they should be moved along with it.
        3. If a window has a defined parent, either move the parent or do nothing at all.
        4. EMWH-flags follow strict layering rules:
           https://specifications.freedesktop.org/wm-spec/1.3/ar01s07.html#STACKINGORDER
        
libqtile/backend/x11/window.py