Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
a0de8bb
Fix macOS StyleAgent notification lifetime safety (AUDIT-001)
wangwenx190 Sep 22, 2026
8d264a8
Fix macOS system button area lifetime handling (AUDIT-002)
wangwenx190 Sep 22, 2026
cc368f3
Fix Quick Windows 10 border ownership and rendering (AUDIT-003)
wangwenx190 Sep 22, 2026
76e19f3
fix(examples): initialize the QML window agent before registering con…
wangwenx190 Sep 22, 2026
182118b
fix(quick): generate and install QML tooling metadata for Qt 6
wangwenx190 Sep 22, 2026
2f9d166
fix(core): guard the Wayland context by native API availability
wangwenx190 Sep 22, 2026
77bd218
refactor(core): name X11 and Wayland backends by protocol
wangwenx190 Sep 22, 2026
255ca87
fix(windows): preserve caption hits for single-axis fixed windows
wangwenx190 Sep 22, 2026
3b67afc
fix(quick): hit test transformed items in local coordinates
wangwenx190 Sep 22, 2026
0645c9f
fix(wayland): convert system menu positions to surface coordinates
wangwenx190 Sep 22, 2026
39b8393
fix(msbuild): pass static import macros to the C++ compiler
wangwenx190 Sep 22, 2026
e20e615
test: add a bounded offline Qt unit test runner
wangwenx190 Sep 22, 2026
e90b5ce
test(core): cover event dispatcher mutation and reentrancy
wangwenx190 Sep 22, 2026
5fb2673
test(core): cover window context attributes and hit-test state
wangwenx190 Sep 22, 2026
06a38fc
test(core): isolate and verify StyleAgent notifications
wangwenx190 Sep 22, 2026
23ab77a
test: cover Widgets and Quick window agent contracts
wangwenx190 Sep 22, 2026
8abd403
ci: require fast unit suites and document offline execution
wangwenx190 Sep 22, 2026
041a862
test: bound build consumers and tighten integration timeouts
wangwenx190 Sep 22, 2026
f0aa236
test: enforce timeout policy and cap CI test steps
wangwenx190 Sep 22, 2026
dbe35f4
test: cover window actions and context lifecycle notifications
wangwenx190 Sep 22, 2026
5064325
test: exercise Qt fallback mouse handling and frame flags offline
wangwenx190 Sep 22, 2026
f879c4a
test: verify QObject event filter forwarding contracts
wangwenx190 Sep 22, 2026
ef99b66
ci: require expanded offline suites and document validation results
wangwenx190 Sep 22, 2026
d0799be
Fix event forwarding after window and dispatcher destruction
wangwenx190 Sep 22, 2026
7a67d7f
Preserve each native window's predecessor procedure chain
wangwenx190 Sep 22, 2026
e1e0aa4
Restore guarded native message contexts across nested dispatch
wangwenx190 Sep 22, 2026
8022687
Pin installed CMake dependencies to the build-time Qt major
wangwenx190 Sep 22, 2026
f7738be
Enforce required and optional components in installed CMake packages
wangwenx190 Sep 22, 2026
4375999
Export Win32 system libraries to static qmake consumers
wangwenx190 Sep 22, 2026
5e4f7b0
Fix per-axis resize constraints in the Qt window context
wangwenx190 Sep 22, 2026
f47cde7
Preserve reachable negative coordinates after manual window dragging
wangwenx190 Sep 22, 2026
96105f5
Track transformed Quick system button areas across scene changes
wangwenx190 Sep 23, 2026
81df71f
Observe Linux system appearance through the XDG Settings portal
wangwenx190 Sep 23, 2026
c4a9d14
Fix native menu and WinId callback lifetime handling
wangwenx190 Sep 23, 2026
de8824c
Make window attribute updates and replay safe across reentrant callbacks
wangwenx190 Sep 23, 2026
eab8175
Validate SystemButton roles before indexing context registrations
wangwenx190 Sep 23, 2026
eedca4e
Preserve explicit Windows 10 border attributes across handle recreation
wangwenx190 Sep 23, 2026
f97915e
Restore complete application cursors after Qt fallback resize ownership
wangwenx190 Sep 23, 2026
b3a1ef7
Preserve legacy Acrylic while reporting and rolling back platform fai…
wangwenx190 Sep 23, 2026
9456aec
Propagate Windows effect failures and preserve newer material updates
wangwenx190 Sep 23, 2026
ace9d29
Clear stale registrations when replacing a destroyed title bar
wangwenx190 Sep 23, 2026
dce4831
Install only qmake files generated for currently enabled modules
wangwenx190 Sep 23, 2026
6ec4dfa
Honor the maximize button hint in Cocoa title bar double-clicks
wangwenx190 Sep 23, 2026
ffb09ba
Remove forwarding Cocoa event swizzling and inactive private helpers
wangwenx190 Sep 23, 2026
41acf2d
Unify shared and native event dispatcher state and lifecycle management
wangwenx190 Sep 23, 2026
6d54cd7
Share synchronous StyleAgent subscriber snapshots across Windows and …
wangwenx190 Sep 23, 2026
07a50d4
Remove empty agent initialization and redundant Qt hook override
wangwenx190 Sep 23, 2026
b79e2a3
Remove unused Windows border helpers and emulated paint hook
wangwenx190 Sep 23, 2026
71f0906
Simplify Windows registry access and remove unused helpers
wangwenx190 Sep 23, 2026
21cec21
Simplify agent setup state and remove unused delegate extension points
wangwenx190 Sep 23, 2026
9be9063
Simplify attribute callbacks and share legacy Acrylic margins rollback
wangwenx190 Sep 23, 2026
905498f
Consolidate StyleAgent initialization and appearance publication
wangwenx190 Sep 23, 2026
c016b10
Resolve the X11 default root window without copying Display internals
wangwenx190 Sep 23, 2026
1dadc4d
Preserve cached margins when recreating borderless Windows handles
wangwenx190 Sep 23, 2026
c2e8eec
Replace Context hook dispatch with typed operations and simplify cons…
wangwenx190 Sep 23, 2026
6aa5be9
fix quick demo
wangwenx190 Sep 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 100 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,18 +97,25 @@ jobs:
# The build system tests register themselves only where the configure found the tool they
# drive, which is the one way this job could go green while covering less than it says. A
# missing qmake or nmake is meant to be a skip on a developer's machine and a failure here.
- name: Check that the consumer tests registered
# The fast unit suites are also required, so accidental conditional exclusions fail CI.
- name: Check that the required tests registered
shell: bash
run: |
expected="buildsystems.cmake buildsystems.qmake"
expected="testpolicy.timeouts buildsystems.cmake buildsystems.qmake buildsystems.qtmajor core.eventdispatch.unit core.dispatchlifetime.unit core.objecteventfilters.unit core.windowcontext.unit core.qtwindowcontext.unit core.windowmovegeometry.unit core.windowmove.component core.styleagent.unit core.portalstyle.component agents.unit"
for scenario in present default core missing-required unknown-required optional quiet-missing repeat optional-qt-missing; do
expected="$expected buildsystems.components.$scenario"
done

if [ "$RUNNER_OS" = "Windows" ]; then
expected="$expected buildsystems.msbuild"
expected="$expected buildsystems.msbuild quickgeometry.unit quicksystembuttonarea.component systembuttons.qml widgetborder.lifetime windows.windowproc windows.windowlifetime windows.borderattributes windows.acrylic windows.effects windows.titlebar windows.styleagent core.windowsregistry.unit fallbackcursor.offscreen fallbackcursor.windows"
fi

for stage in prepare build_core build_widgets build_quick all core widgets quick restored; do
expected="$expected buildsystems.qmake.reconfigure.$stage"
done
for name in $expected; do
if ! ctest --test-dir build -N -R "$name" | grep -q "$name"; then
echo "$name did not register, so the tool it drives was not found here"
if ! ctest --test-dir build -N -R "^${name}$" | grep -Fq "$name"; then
echo "Required test $name did not register"
exit 1
fi
done
Expand All @@ -119,7 +126,76 @@ jobs:
# --no-tests=error is not optional. ctest exits 0 when it finds nothing to run, and every
# test in this suite registers itself conditionally.
- name: Test
run: ctest --test-dir build --output-on-failure --no-tests=error
timeout-minutes: 3
run: ctest --test-dir build -LE reconfigure --output-on-failure --no-tests=error

- name: Configure static package consumer regressions
if: matrix.platform == 'windows-msvc'
shell: bash
env:
CC: ${{ matrix.cc }}
CXX: ${{ matrix.cxx }}
run: |
cmake -G Ninja -S . -B build-static \
-DCMAKE_BUILD_TYPE=Release \
-DQWINDOWKIT_BUILD_STATIC=ON \
-DQWINDOWKIT_BUILD_WIDGETS=ON \
-DQWINDOWKIT_BUILD_QUICK=ON \
-DQWINDOWKIT_BUILD_TESTS=ON
expected="buildsystems.msbuild buildsystems.qmake core.qtwindowcontext.unit core.windowmovegeometry.unit core.windowmove.component core.portalstyle.component quicksystembuttonarea.component systembuttons.qml windows.borderattributes windows.acrylic windows.effects windows.titlebar windows.styleagent core.windowsregistry.unit fallbackcursor.offscreen fallbackcursor.windows"
for stage in prepare build_core build_widgets build_quick all core widgets quick restored; do
expected="$expected buildsystems.qmake.reconfigure.$stage"
done
for name in $expected; do
if ! ctest --test-dir build-static -N -R "^${name}$" | grep -Fq "$name"; then
echo "Required static test $name did not register"
exit 1
fi
done

- name: Build static configuration
if: matrix.platform == 'windows-msvc'
run: cmake --build build-static

- name: Test static units and package consumers
if: matrix.platform == 'windows-msvc'
timeout-minutes: 2
run: ctest --test-dir build-static -R "(\.unit$|^core.windowmove.component$|^core.portalstyle.component$|^quicksystembuttonarea.component$|^systembuttons.qml$|^windows.borderattributes$|^windows.acrylic$|^windows.effects$|^windows.titlebar$|^windows.styleagent$|^fallbackcursor\.|^testpolicy.timeouts$|^buildsystems.msbuild$|^buildsystems.qmake$|^buildsystems.qtmajor$|^buildsystems.components\.)" --output-on-failure --no-tests=error

- name: Test shared qmake reconfiguration
timeout-minutes: 4
run: ctest --test-dir build -L reconfigure --output-on-failure --no-tests=error

- name: Test static qmake reconfiguration
if: matrix.platform == 'windows-msvc'
timeout-minutes: 4
run: ctest --test-dir build-static -L reconfigure --output-on-failure --no-tests=error

# Single-axis caption hit testing has a separate compile-time path without
# system borders. Exercise it with real Widgets and Quick HWNDs as well.
- name: Configure without system borders
if: matrix.platform == 'windows-msvc'
shell: bash
env:
CC: ${{ matrix.cc }}
CXX: ${{ matrix.cxx }}
run: |
cmake -G Ninja -S . -B build-no-system-borders \
-DCMAKE_BUILD_TYPE=Debug \
-DQWINDOWKIT_BUILD_WIDGETS=ON \
-DQWINDOWKIT_BUILD_QUICK=ON \
-DQWINDOWKIT_BUILD_TESTS=ON \
-DQWINDOWKIT_INSTALL=OFF \
-DQWINDOWKIT_ENABLE_WINDOWS_SYSTEM_BORDERS=OFF

- name: Build native hit-test regression without system borders
if: matrix.platform == 'windows-msvc'
run: cmake --build build-no-system-borders --target tst_windowhittest

- name: Test native hit testing without system borders
if: matrix.platform == 'windows-msvc'
timeout-minutes: 1
run: ctest --test-dir build-no-system-borders -R "^windows.hittest$" --output-on-failure --no-tests=error

# MinGW is a job of its own because it wants msys2 rather than the toolchain the matrix above
# sets up with an environment variable, and because Qt comes from the same place as the compiler
Expand Down Expand Up @@ -162,12 +238,19 @@ jobs:

# The same question the matrix jobs ask. MSBuild is not part of this toolchain, so only the
# qmake half is expected here.
- name: Check that the consumer tests registered
- name: Check that the required tests registered
shell: msys2 {0}
run: |
for name in buildsystems.cmake buildsystems.qmake; do
if ! ctest --test-dir build -N -R "$name" | grep -q "$name"; then
echo "$name did not register, so the tool it drives was not found here"
expected="testpolicy.timeouts buildsystems.cmake buildsystems.qmake buildsystems.qtmajor core.eventdispatch.unit core.dispatchlifetime.unit core.objecteventfilters.unit core.windowcontext.unit core.qtwindowcontext.unit core.windowmovegeometry.unit core.windowmove.component core.styleagent.unit core.portalstyle.component agents.unit quickgeometry.unit quicksystembuttonarea.component systembuttons.qml widgetborder.lifetime windows.windowproc windows.windowlifetime windows.borderattributes windows.acrylic windows.effects windows.titlebar windows.styleagent core.windowsregistry.unit fallbackcursor.offscreen fallbackcursor.windows"
for scenario in present default core missing-required unknown-required optional quiet-missing repeat optional-qt-missing; do
expected="$expected buildsystems.components.$scenario"
done
for stage in prepare build_core build_widgets build_quick all core widgets quick restored; do
expected="$expected buildsystems.qmake.reconfigure.$stage"
done
for name in $expected; do
if ! ctest --test-dir build -N -R "^${name}$" | grep -Fq "$name"; then
echo "Required test $name did not register"
exit 1
fi
done
Expand All @@ -177,5 +260,11 @@ jobs:
run: cmake --build build

- name: Test
timeout-minutes: 3
shell: msys2 {0}
run: ctest --test-dir build -LE reconfigure --output-on-failure --no-tests=error

- name: Test qmake reconfiguration
timeout-minutes: 4
shell: msys2 {0}
run: ctest --test-dir build --output-on-failure --no-tests=error
run: ctest --test-dir build -L reconfigure --output-on-failure --no-tests=error
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,25 @@ target_link_libraries(my_quick_app PRIVATE

QWindowKit can also be added as a CMake subdirectory when it is vendored into your project.

With Qt 6.3 or later, building Quick generates `qml/QWindowKit/qmldir` and
`QWKQuick.qmltypes` in the build tree and installs them under `<INSTALL_DIR>/qml/QWindowKit`.
`QWindowKit_QML_IMPORT_PATH` points to the import root for both CMake integration methods.
Add it to Qt Creator's code model and to the application's QML module:

```cmake
list(APPEND QML_IMPORT_PATH "${QWindowKit_QML_IMPORT_PATH}")
list(REMOVE_DUPLICATES QML_IMPORT_PATH)
set(QML_IMPORT_PATH "${QML_IMPORT_PATH}" CACHE STRING "QML import paths" FORCE)
# When using qt_add_qml_module(my_quick_app ...), also pass:
# IMPORT_PATH "${QWindowKit_QML_IMPORT_PATH}"
```

For other editors, add this same directory to the QML language server's import paths.
For a direct check, run `qmllint -I <INSTALL_DIR>/qml <your-file.qml>`.
Continue linking `QWindowKit::Quick` and calling `QWK::registerTypes(&engine)` before loading
QML, including in static builds; this module does not install a runtime plugin.
Qt 5 and Qt 6.0–6.2 retain procedural registration without generated tooling metadata.

### qmake

After installing QWindowKit with CMake, include the generated `.pri` file:
Expand Down
19 changes: 19 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,25 @@ target_link_libraries(my_quick_app PRIVATE

如果你将 QWindowKit 作为源码依赖放入项目,也可以通过 CMake `add_subdirectory()` 集成。

使用 Qt 6.3 或更高版本构建 Quick 时,会在构建目录的 `qml/QWindowKit` 中生成
`qmldir` 和 `QWKQuick.qmltypes`,并安装到 `<INSTALL_DIR>/qml/QWindowKit`。
上述两种 CMake 集成方式均提供 `QWindowKit_QML_IMPORT_PATH`,指向 QML 导入根目录。
将其加入 Qt Creator 的代码模型以及应用的 QML 模块:

```cmake
list(APPEND QML_IMPORT_PATH "${QWindowKit_QML_IMPORT_PATH}")
list(REMOVE_DUPLICATES QML_IMPORT_PATH)
set(QML_IMPORT_PATH "${QML_IMPORT_PATH}" CACHE STRING "QML import paths" FORCE)
# 使用 qt_add_qml_module(my_quick_app ...) 时,还需传入:
# IMPORT_PATH "${QWindowKit_QML_IMPORT_PATH}"
```

其他编辑器需要将同一目录加入 QML 语言服务器的导入路径。
也可运行 `qmllint -I <INSTALL_DIR>/qml <your-file.qml>` 检查。
仍须链接 `QWindowKit::Quick`,并在加载 QML 前调用 `QWK::registerTypes(&engine)`,
静态构建也采用这一方式;本模块不安装运行时插件。
Qt 5 和 Qt 6.0–6.2 保留原来的手动注册方式,暂不生成工具元数据。

### qmake

先使用 CMake 安装 QWindowKit,然后包含生成的 `.pri` 文件:
Expand Down
65 changes: 21 additions & 44 deletions docs/TODO.md
Original file line number Diff line number Diff line change
@@ -1,53 +1,30 @@
# TODO

## Wayland: `showSystemMenu()` may be passing the wrong coordinate space
## Wayland: validate system menu placement on a compositor

**File:** `src/core/contexts/linuxwaylandcontext.cpp`
**Introduced by:** the Wayland/X11 backend contribution (Wing-summer), needs the original author's eyes.
**Status:** unverified — reported from code reading only, no Wayland machine was available to test.
**File:** `src/core/contexts/waylandcontext.cpp`
**Status:** coordinate conversion fixed and statically reviewed; Wayland runtime validation pending.

`LinuxWaylandContext::virtual_hook()` forwards the incoming point straight to the
xdg-shell request:
`showSystemMenu()` accepts global Qt device-independent coordinates. The Wayland backend maps
them to window-local coordinates and uses Qt's native local conversion before issuing
`xdg_toplevel.show_window_menu`, which requires surface-local coordinates. QWindowKit sets
`Qt::FramelessWindowHint`, so there is no Qt client-side decoration offset to add. Wayland's
buffer scale must not be applied to the request coordinates.

```cpp
auto pos = static_cast<const QPoint *>(data);
xdg_toplevel_show_window_menu(toplevel, seat, serial, pos->x(), pos->y());
```
References:

The point that arrives here is a **global** position. `AbstractWindowContext::showSystemMenu()`
is fed from `QtWindowEventFilter::sharedEventFilter()`, which passes
`getMouseEventGlobalPos(me)`, and the public `WindowAgentBase::showSystemMenu()` is documented
in terms of global coordinates too.
- [xdg-shell protocol](https://raw.githubusercontent.com/wayland-mirror/wayland-protocols/main/stable/xdg-shell/xdg-shell.xml), `show_window_menu`.
- [Qt Wayland window implementation](https://github.com/qt/qtwayland/blob/6.8/src/client/qwaylandwindow.cpp), `mapFromWlSurface()` and `createDecoration()`.
- [Qt window coordinate mapping](https://github.com/qt/qtbase/blob/6.8/src/gui/kernel/qwindow.cpp), `mapFromGlobal()`.

`xdg_toplevel.show_window_menu` is believed to expect **surface-local** coordinates (relative to
the top-left of the window geometry). If that is right, the menu pops up displaced by the
window's own position on screen, and the error disappears only when the window happens to sit at
the origin.
On a Wayland compositor that supports this menu, check both Widgets and Quick windows:

Circumstantial evidence that the two Linux backends disagree about what they are handed: the X11
path in `linuxx11context.cpp` treats the same argument as global and converts it to root
coordinates,
1. Right-click different title-bar positions, then move the window and repeat. The menu anchor
should follow the click, subject to compositor placement constraints.
2. Repeat with compositor scaling at 100%, 150%, and 200%, and with `QT_SCALE_FACTOR=2` to
exercise Qt's extra coordinate scaling separately from the Wayland buffer scale.
3. Trigger the public `showSystemMenu()` API during a valid input event using a point obtained
from `QWindow::mapToGlobal()`, and confirm the corresponding local anchor is used.

```cpp
qreal dpr = m_windowHandle->devicePixelRatio();
int root_x = qRound(pos->x() * dpr);
int root_y = qRound(pos->y() * dpr);
```

which is the correct thing to do for `_GTK_SHOW_WINDOW_MENU`. Both backends implement the same
hook and receive the same input, so at most one of them can be right.

### To check

1. Confirm against the xdg-shell protocol XML which coordinate space `show_window_menu` wants.
2. If it is surface-local, subtract the window position before sending, e.g.
`*pos - m_windowHandle->position()`, and decide whether a device pixel ratio conversion is
needed at all (Wayland surface-local coordinates are logical, so probably not).
3. Test with a window that is **not** at the top-left of the screen — the bug is invisible at the
origin.

### Related, same file

`m_windowHandle` is passed to `nativeResourceForWindow()` without a null check. It happens to be
safe today because a null window yields a null `xdg_toplevel` and the function returns early, but
the X11 path had the same shape and did crash; it has since been given an explicit guard.
Record the compositor and Qt versions. Windows builds and tests do not execute this backend;
no Wayland compile or runtime result is claimed for the fix.
41 changes: 41 additions & 0 deletions docs/tutorial.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,18 @@ Disable maximization:
setWindowFlag(Qt::WindowMaximizeButtonHint, false);
```

For QWindowKit's custom title bar on macOS and in the Qt fallback context, a left
double-click toggles maximized/normal state only when `Qt::WindowMaximizeButtonHint`
is set, the window is not fixed-size or fullscreen, and the pointer is in a
draggable title bar area. Clearing the flag also disables double-click restoration
of an already maximized window. The same rule applies to Quick window `flags`.
It does not prevent application code or a custom button handler from changing
the window state directly.

The macOS custom title bar uses this QWindowKit policy; it does not read the
Desktop & Dock preference for native title bar double-click actions. Native
traffic-light buttons remain separate from this custom title bar behavior.

Create a fixed-size window:

```cpp
Expand Down Expand Up @@ -523,6 +535,35 @@ windowAgent->setWindowAttribute(QStringLiteral("glass-tint-color"), QColor(255,

If the platform does not support an attribute, `setWindowAttribute()` returns `false`.

## System appearance with StyleAgent

Enable `QWINDOWKIT_ENABLE_STYLE_AGENT` to use `QWK::StyleAgent`. Create and use it
on the GUI thread with a running Qt event loop. Both getters are updated before
change signals are emitted; repeated equal settings do not emit signals.

On Linux, both X11 and Wayland use only the [XDG Desktop Portal Settings interface](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Settings.html)
through Qt DBus. This adds a Qt DBus build/runtime dependency when StyleAgent is
enabled; static CMake/qmake consumers receive it automatically. Disabling
StyleAgent removes this dependency from the library. No direct XCB/Wayland calls
or desktop-specific fallbacks are used. The desktop's portal backend must provide
the corresponding appearance settings.

The initial read is asynchronous: immediately after construction, `systemTheme()`
is `Unknown` and `systemAccentColor()` is invalid. Connect the change signals and
read the getters to apply subsequent snapshots. The portal's `color-scheme` selects
Dark/Light; high `contrast` selects HighContrast. No preference or a missing scheme
means Unknown; missing/invalid `accent-color` means an invalid QColor. Application
palette overrides never replace unavailable system settings.

StyleAgent subscribes before reading and rereads the full snapshot on changes.
Each asynchronous read has a 1-second timeout. A change during a read discards that
snapshot and triggers another read; after changes settle, allow the outstanding
read plus one new read (up to 2 seconds of read time), plus event-loop delivery.
Service/connection loss or failed reads invalidate old values. Service-owner changes
trigger immediate recovery; otherwise failures retry every 5 seconds (then up to
1 second for the read). These timings require a responsive event loop and working
session bus. There is no periodic polling while the portal is healthy.

## Linux Notes

On Linux, QWindowKit can use the Qt fallback window context and provides partial system menu support on Qt 6 Wayland/X11. Behavior varies by desktop environment and window manager.
Expand Down
Loading