Weston 컴포지터는 Weston의 동작을 준비/실행하는 핵심 컴포넌트이다. Weston 컴포넌트가 하는 일은 크게 두 가지다. 하나는 Wayland 객체, Backend 컴포넌트, Shell 컴포넌트를 생성 / 로딩하고, 실행하는 것이고, 다른 하나는 주요 Wayland Interface의 동작(= 컴포지팅)을 정의하는 일이다. Weston 컴포지터의 코드는 compositor 폴더에서 찾을 수 있으며, libweston 폴더를 참조한다. 워크플로우와 실제 소스코드를 보며, Weson compositor가 어떤식으로 동작하는지 알아보자.
(자세한 설명은 코드 주석 참조)
// compositor/main.c
WL_EXPORT int
wet_main(int argc, char *argv[])
{
struct wl_display *display;
struct wl_client *primary_client;
struct wet_compositor wet = { 0 };
// (생략)
// 인자 파싱
// 변수 초기화
// 로그 컨택스트 생성/설정
// 1. Wayland display 생성
// wl_display는 wayland에서 가장 핵심이 되는 객체이다.
// (참조: https://www.kernelpanic.kr/7)
// Weston은 wl_display 객체를 이용하여, wayland protocol에 따른 동작을 정의하고,
// Wayland 메인 이벤트 루프를 실행한다.
// (wl_display 객체 내에는 이벤트 큐가 있어서, 이벤트 루프 내에서 큐에 등록된 이벤트를 실행한다.)
display = wl_display_create();
if (display == NULL) {
weston_log("fatal: failed to create display\n");
goto out_display;
}
// (생략)
// 시그널 처리
// 2. weston 컴포지터 생성
// weston_compositor_create를 통해 weston_compositor 객체를 생성한다.
// weston_compositor 객체를 통해 Backend 컴포넌트, Shell 컴포넌트는 Weston의
// 동작을 제어, 인터페이스 등록, 자원 접근 등을 수행할 수 있다.
// weston_compositor 생성에 대한 자세한 설명은 아래 libweston/compositor.c를 참조하자.
wet.compositor = weston_compositor_create(display, log_ctx, &wet);
if (wet.compositor == NULL) {
weston_log("fatal: failed to create compositor\n");
goto out;
}
// (생략)
// config 파일 파싱
// 4. Backend 로딩 (drm, fb, wayland, x11, rdp)
// Backend는 Weston의 출력을 관리하는 컴포넌트이다. Backend를 통해 Weston은
// 컴포지팅이 완료된 버퍼를 화면에 보여줄 수 있다.
// Weston은 다양한 출력 환경을 지원하기 위해 Backend 컴포넌트를 공유객체(.so)로 관리하고 있다.
// 만약 Weston이 linux kernel의 drm을 사용한다면, drm_backend를 로딩하여,
// weston_compositor backend에 등록하면 된다. 만약 X11을 사용하는 Gnome에서
// Weston을 실행한다면, x11_backend를 로딩하면 된다.
// Weston은 이를 통해 다양한 백앤드 환경에서도 동일한 UI/UX를 제공한다.
// Backend, 특히 drm은 그 자체로 하나의 쳅터가 되는 주제기 때문에
// 여기서는 Backend에 대해 자세히 다루지는 않는다.
if (load_backend(wet.compositor, backend, &argc, argv, config) < 0) {
weston_log("fatal: failed to create compositor backend\n");
goto out;
}
// (생략)
// 소켓 생성
// 5. Shell 로딩
// Shell은 우리가 보는 UI/UX를 그리는 컴포넌트이다. Shell을 통해 어플리케이션의 창 관리,
// 인풋 관리, 단축키 등록, 배경화면 설정 등 우리가 "데스크탑"이라고 느끼는 요소들을 제공해 준다.
// Shell도 Backend와 같이 공유객체(.so)로 관리되고 있다. 자세한 내용은 뒤에서 다루도록 하겠다.
if (wet_load_shell(wet.compositor, shell, &argc, argv) < 0)
goto out;
// (생략)
// 모듈 로딩
// 6. Wayland 메인 이벤트 루프
// 무한 루프를 wl_display에 등록되는 이벤트를 처리한다. 여기에서 화면 갱신, idle 처리 등
// 앞에서 준비한 작업들이 이뤄진다.
wl_display_run(display);
// (생략)
// 정리작업
}
위는 Weston compositor의 메인함수이다. 메인함수는 워크플로우 그림과 거의 흡사하게 동작한다. 자잘한 동작들이 많긴 하지만, 큰 틀에서 보면 크게 복잡하지는 않다.
// libweston/compositor.c
WL_EXPORT struct weston_compositor *
weston_compositor_create(struct wl_display *display,
struct weston_log_context *log_ctx,
void *user_data)
{
struct weston_compositor *ec;
struct wl_event_loop *loop;
// (생략)
// Weston 컴포지터 생성 / 초기화
// 3. 주요 Wayland Interface 등록
// wl_global_create api를 통해 Weston은 Wayland 프로토콜 인터페이스에 대한 동작을 정의할 수 있다.
// 예를 들어, 데스크톱 앱은 create_surface 프로토콜을 보내서, 새로운 surface를 요구할 수 있으며,
// weston 서버는 새로운 surface를 생성/할당하거나, (메모리 부족 등의 이유로) 거부할 수 있다.
if (!wl_global_create(ec->wl_display, &wl_compositor_interface, 4,
ec, compositor_bind))
goto fail;
if (!wl_global_create(ec->wl_display, &wl_subcompositor_interface, 1,
ec, bind_subcompositor))
goto fail;
if (!wl_global_create(ec->wl_display, &zxdg_output_manager_v1_interface, 2,
ec, bind_xdg_output_manager))
goto fail;
// (생략)
// weston 의존적인 Interface 등록
// weston 잔여 작업 초기화
return ec;
fail:
free(ec);
return NULL;
}
weston_compositor_create 함수를 통해 weston_compositor 객체가 생성된다. 이 때 주요 Weston 인터페이스들의 동작이 등록된다. Weston 컴포지터에서 등록되는 인터페이스들은 데스크톱 앱에 대해 다음과 같은 동작을 수행한다.
(참고자료: www.kernelpanic.kr/7)
프로토콜 | 내용 |
wl_compositor | 새로운 화면 생성 예를 들어 앱에서 새 창을 띄우면 create_surface 호출된다. |
wl_surface | 앱 화면 변화를 처리 예를 들어, 화면에 마우스가 올라가면 다음과 같은 프로토콜 호출이 발생한다. - surface에 새로운 버퍼가 생성되었기 때문에 (surface)attach가 우선 호출된다. - 다음으로 surface에 변경사항이 생겼기 때문에 (surface)damage가 호출이 된다. - 마지막으로 해당 변경을 반영하기 위해 (surface)commit이 호출된다. |
이 외에도 subcompositor, zxdg_output_manager_v1 등 여러 프로토콜이 등록된다. 이 프로토콜들에 대한 자세한 설명은 아래 링크에서 볼 수 있다.
Note if (!wl_global_create(ec->wl_display, &wl_compositor_interface, 4, ec, compositor_bind)) goto fail; 위 코드에서 "wl_compositor_interface" 때문에 한동안 코드 독해에 고생하였다. 왜냐하면 libweston/compositor.c에 다음 선언이 있기 때문이다. static const struct wl_compositor_interface compositor_interface = { compositor_create_surface, compositor_create_region }; 마치 struct의 이름을 '&' 참조를 통해 함수에 인자로 전달한 것 처럼 보이기 때문이다. C 언어에 익숙하다면, 해당 문법이 성립되지 않음을 알 수 있다. 위 구문이 당황스러웠던 것은 나 뿐만이 아니었던 것 같다. Stackoverflow에 보면 비슷한 질문이 올라와 있다. (출처: stackoverflow.com/questions/44101813/can-struct-type-itself-be-passed-to-function-as-parameter-in-c) 어떻게 된 일일까?사실 wl_compositor_interface은 wayland_protocol.h 헤더에서 struct wl_interface 형으로 선언되어 있다. 즉 wl_global_create에 전달된 wl_compositor_interface는 어떤 구조체의 이름이 아니라, 단순히 wayland_protocol.h에 정의된 struct wl_interface형 변수였던 것이다 |
'오픈소스 읽기 (OLD) > 데스크탑환경 - weston (wayland)' 카테고리의 다른 글
4. Weston 컴포지터 살펴보기 - 컴포지팅 (3/3) (0) | 2020.08.21 |
---|---|
3. Weston 컴포지터 살펴보기 - 화면 갱신 (2/3) (0) | 2020.08.12 |
번외. Wayland 프로토콜 (0) | 2020.08.04 |
1. Weston 훑어보기 (0) | 2020.08.03 |
0. Wayland란? (+ Weston) (0) | 2020.08.03 |