Fancy Compositor
Fancy Compositor는 순수 QML로 세련된 Wayland 컴포지터를 작성하는 방법을 보여주는 예제입니다.
소개
Fancy Compositor는 QML API의 강력함과 사용 편의성을 보여주는, 데스크톱 스타일의 소규모 Wayland 컴포지터 예제입니다. Qt Wayland Compositor QML API의 강력함과 사용 편의성을 보여주는 작은 데스크톱 스타일의 Wayland 컴포지터 예제입니다.
Fancy Compositor 예제는 Minimal QML 예제와 유사하게, QML 코드만을 사용하여 구현된 본격적인 Wayland 컴포지터입니다.
컴포지터 초기화
Minimal Qml 예제와 마찬가지로, Fancy Compositor는 Qt에서 지원하는 주요 셸 확장을 지원합니다.
// Shell surface extension. Needed to provide a window concept for Wayland clients.
// I.e. requests and events for maximization, minimization, resizing, closing etc.
XdgShell {
onToplevelCreated: (toplevel, xdgSurface) => screen.handleShellSurface(xdgSurface)
}
// Minimalistic shell extension. Mainly used for embedded applications.
IviApplication {
onIviSurfaceCreated: (iviSurface) => screen.handleShellSurface(iviSurface)
}
// Deprecated shell extension, still used by some clients
WlShell {
onWlShellSurfaceCreated: (shellSurface) => screen.handleShellSurface(shellSurface)
}이들은 ` WaylandCompositor `의 자식 요소로 인스턴스화되며, ` `는 이를 지원되는 인터페이스 목록에 자동으로 추가하고, 이 목록은 서버에서 클라이언트로 브로드캐스팅됩니다.
연결된 클라이언트가 서피스를 생성하여 셸 확장 기능 중 하나에 바인딩하면, 해당 신호가 발산됩니다. 그러면 사용자 정의 WaylandOutput 클래스 내부의 메서드가 호출되어, ShellSurface 을 ListModel 에 추가합니다.
function handleShellSurface(shellSurface) {
shellSurfaces.append({shellSurface: shellSurface});
}이 모델은 컴포지터(compositor)의 WaylandOutput 내부에서 ShellSurfaceItems 를 생성하는 Repeater 의 소스로 사용됩니다. 이를 통해 Qt Quick 장면 내에 서피스의 뷰가 추가됩니다. 이는 ShellSurfaceItem 이므로, 사용 중인 셸 확장 프로그램에 따라 컴포지터 사용자에게 특정 상호작용 옵션도 제공합니다.
Repeater {
model: output.shellSurfaces
// Chrome displays a shell surface on the screen (See Chrome.qml)
Chrome {
shellSurface: modelData
onDestroyAnimationFinished: output.shellSurfaces.remove(index)
}
}키보드
기본적인 윈도우 시스템 기능 외에도, Fancy Compositor는 프로세스 내에서 실행되는 선택적 온스크린 키보드를 지원합니다. 이는 Qt Virtual Keyboard 모듈을 사용하며, 해당 모듈이 사용 가능한 경우 활성화됩니다.
import QtQuick
import QtQuick.VirtualKeyboard
InputPanel {
visible: active
y: active ? parent.height - height : parent.height
anchors.left: parent.left
anchors.right: parent.right
}코드는 간단합니다. 출력 영역 하단에 ` InputPanel `를 인스턴스화하고, 현재 활성화된 경우에만 표시되도록 합니다.
Loader {
anchors.fill: parent
source: "Keyboard.qml"
}그런 다음 ` Loader ` 요소를 사용하여 키보드를 ` WaylandOutput `에 추가합니다. 여기서 ` Loader `을 사용하는 이유는 Qt Virtual Keyboard 모듈에 대한 하드 의존성을 피하기 위해 xml-ph-0002@deepl.internal가 사용됩니다. 로딩에 실패하더라도 컴포지터는 정상적으로 작동을 계속하지만, 화면 키보드 기능은 지원되지 않습니다.
마지막으로, 컴포지터가 텍스트 입력을 클라이언트에 전달할 수 있는 방법이 필요합니다. 이는 ` text-input ` 확장을 통해 이루어집니다. Fancy Compositor 예제는 ` text_input_unstable_v2 ` 프로토콜과 Qt의 ` qt_text_input_method_unstable_v1 ` 프로토콜을 모두 지원합니다.
qt_text_input_method_unstable_v1 확장은 QtTextInputMethodManager 을 WaylandCompositor 의 자식으로 인스턴스화하여 컴포지터에 추가되며, ` TextInputManager `는 ` text_input_unstable_v2`를 추가합니다.
최신 Qt 애플리케이션은 qt_text_input_method_unstable_v1 가 사용 가능한 경우 이를 선택하며, 그 외의 클라이언트는 text_input_unstable_v2 를 사용할 수 있습니다.
전환 효과
기본 기능 외에도 Fancy Compositor 예제는 상태 간의 애니메이션 전환을 보여줍니다.
그 중 첫 번째는 활성화 전환입니다. 이는 XdgShell 에서만 지원되는데, activated 상태를 가진 셸 확장은 이것뿐이기 때문입니다.
Connections {
target: shellSurface.toplevel !== undefined ? shellSurface.toplevel : null
// some signals are not available on wl_shell, so let's ignore them
ignoreUnknownSignals: true
function onActivatedChanged() { // xdg_shell only
if (shellSurface.toplevel.activated) {
receivedFocusAnimation.start();
}
}
}
SequentialAnimation {
id: receivedFocusAnimation
ParallelAnimation {
NumberAnimation { target: scaleTransform; property: "yScale"; to: 1.02; duration: 100; easing.type: Easing.OutQuad }
NumberAnimation { target: scaleTransform; property: "xScale"; to: 1.02; duration: 100; easing.type: Easing.OutQuad }
}
ParallelAnimation {
NumberAnimation { target: scaleTransform; property: "yScale"; to: 1; duration: 100; easing.type: Easing.InOutQuad }
NumberAnimation { target: scaleTransform; property: "xScale"; to: 1; duration: 100; easing.type: Easing.InOutQuad }
}
}XdgShell 프로토콜에서 클라이언트 창이 활성화되면, 200ms 동안 창이 "튀어나오는" 애니메이션을 트리거합니다.
Fancy Compositor는 소멸 애니메이션도 지원합니다. 이 애니메이션은 클라이언트가 정상적으로 창을 닫았을 때든, 심지어 충돌이 발생했을 때든 상관없이 창이 닫히고 표면이 소멸될 때마다 트리거됩니다.
onSurfaceDestroyed: {
bufferLocked = true;
destroyAnimation.start();
}
SequentialAnimation {
id: destroyAnimation
ParallelAnimation {
NumberAnimation { target: scaleTransform; property: "yScale"; to: 2/height; duration: 150 }
NumberAnimation { target: scaleTransform; property: "xScale"; to: 0.4; duration: 150 }
NumberAnimation { target: chrome; property: "opacity"; to: chrome.isChild ? 0 : 1; duration: 150 }
}
NumberAnimation { target: scaleTransform; property: "xScale"; to: 0; duration: 150 }
ScriptAction { script: destroyAnimationFinished() }
}애니메이션이 진행되는 동안 콘텐츠가 유지되도록 하기 위해, 먼저 버퍼를 잠급니다. 즉, 클라이언트가 렌더링한 마지막 프레임은 작업이 완료될 때까지 메모리에 남아 있게 됩니다.
다시 한 번, 항목의 크기를 기준으로 애니메이션을 트리거합니다. 이 애니메이션은 CRT 화면의 전원을 끄는 효과를 모방하여, 창이 단순히 허공으로 사라진 것이 아니라 닫히고 있음을 사용자에게 시각적으로 알려줍니다.
Qt Quick 의 모든 기능을 활용하여 이와 같은 상태 변화에 어떤 종류의 애니메이션 효과든 적용할 수 있습니다.
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.