Minecraft screenshot with moon setting in y=-59
개발
기타 개발
MacOS 라이브 배경화면 개발 염탐 후기 - with Spine Skeleton
2023-08-24, 21:22 | HoonKun
이 페이지의 내용
MacOS 라이브 배경화면 개발 염탐 후기 - with Spine Skeleton

선배님의 개쩌는 캐리를 받아 새로 장만한 맥북에 기존에 사용하던 라이브 배경화면을 이식한 후기.

서론

맥북을 장만했다!! M2 Max 32GB!!
개쩌는 배터리와 엄청난 수명의 배터리, 엄청난 시간동안 사용할 수 있게 해주는 배터리와 써도써도 닳지 않는 배터리가 정말 인상적이었다.
물론 엄청난 속도는 말할 것도 없고(나는 포토샵이 이렇게 빨리 켜질 수 있는지 처음 알았다...).

아무튼, 맥북을 장만한 김에 이전 리눅스와 윈도우에서 가능했던 라이브 배경화면을 쓰고싶었다.
리눅스에서는 KDE가 원만한 방법으로(qml을 사용해 wallpaper 플러그인을 만들면 설정할 수 있었다) 지원했고, 윈도우에서는 월페이퍼 엔진을 가져다가 아무 프로그램이나 배경화면에 깔 수 있었다.

그러다가 드디어 MacOS의 차례가 온 것이다!
당연히 Wallpaper Engine 처럼 편리한 것이 있나 찾아보았지만... 기본적으로 유료이면서 원하는 기능이 지원되는지는 명확히게 서술되어있지 않았다. 사놓고 아 이거 안되네... 할 지도 모른다는 뜻이었다.

사실 이게 없어서 거의 포기하려고 했는데, 주변에 계시던 개쩌는 능력을 가지신 선배님께서 아주 좋은 얘기를 해주셨다. 정리하자면 다음과 같았다.

  • Swift 나 C같은 애들은, 특히 애플쪽은 Private API 라고 해서 숨겨진 함수들이 있는데, 그걸 헤더파일만 추가해서 호출하면 구현이 없어도 그 숨겨진 API에 접근할 수 있어.
  • 그 중에는 특정 윈도우를 배경화면 레이어에 깔 수 있게 하는 것도 포함되어있을 걸!

그리하여, 이번에는 직접 배경화면 레이어에 깔리는 배경화면 프로그램을 제작해보기로 한 것이다!

개발

그러나... 거의 모든 것은 선배님이 해줬다. 실제로 내가 만진 부분은... UI쪽 부분과 코드를 최종 패키징하여 로그인 시 실행할 수 있도록 한 것 정도였다.

최종 목표는 이러했다:

  • 배경화면 레이어에 만든 프로그램을 띄울 것
  • 로그인 시 자동으로 실행되어 딱히 손댈 부분이 없을 것
  • Spine 툴로 제작된 스켈레톤과 텍스쳐 파일을 넣어서 실제로 표시되게 할 것

단순한 목표다. 단순했어야했다. 그러나... 너무 많은 삽질을 해주셨다.

  • 윈도우를 배경화면 레이어에 띄우는 것도, 보기에 생각만큼 쉽지는 않았다. 아무래도 Private API 이므로 정보가 분산되어있었다.
    물론 선배님께는 그렇게 어렵지 않은 모양이었다. 이런 API를 사용해본 적이 많으신지... 문서 몇 번 뒤적이시다가 어떤 Git에 있는 일부 헤더를 싹 긁어다가 프로젝트에 넣으시더니 빌드 스크립트와 확장명(cpp를 mm으로 바꾸셨다)같은걸 만지시곤 다됐다고 하셨다. 이름은 CGSInternal 이었는데... 주로는 CGSWindow 를 사용해 CGSSetWindowLevel 를 호출하여 데스크탑 레이어로 이동시켰다.
  • 우선 Swift 로 제작된 Spine 렌더링 라이브러리는 분명 문서가 4.1을 지원한다고 되어있었으나 Essential 버전 기준이라 그런지 Pro 에서 쓰이는 기능을 포함한 skeleton 파일은 열지 못했다.
    즉, MacOS에서 사용하는 SwiftUI를 통해서는 제작하기 어렵다는 것을 의미했다. 게다가 얘가 4.1만 지원한다고 해서 Spine툴을 사서($340) 3.8짜리 .skel 을 4.1짜리 .skel로 변경했는데... 의미가 없었다
    이거 변환하려고 .skel 을 파싱하여 변환하는 코드를 짜보려고도 했는데, 공식 문서가 너무나도 불친절해서 하다가 중간에 포기했다
  • 그리하여 원래 리눅스에서 썼던 Qml에서 사용가능한 라이브러리인 qspine 을 사용하려 했다. 즉, C++의 Qt를 통해 Qml을 표시해야한다는 의미였다. 다행히 여기까지는 어렵지 않았(던걸로 보였)다.
  • 그런데 이번엔 Qt의 버전에서 꼬이기 시작한다. qspine은 Qt 5에서 작성된 라이브러리인데, Qt 6으로 넘어오면서 CGSTexture에서 bind 함수를 비롯한 여러 OpenGL에 의존하는 코드가 삭제되고 다시 디자인되었다는 것이었다.
    혹시나 해서 Qt를 5로 내리고 다시 해보았으나 이번엔 빌드 단계에서 터졌다... 아마 CMake 지원 관련 문제일 것으로 예상된다.

이 문제는 선배님이 해결해주셨다. 근본적인 원인은 이 소스가 OpenGL을 사용한다는 것을 전제로 하고 있었고, QImage 를 CGSTexture 로 바꾸어, 그것을 렌더링하도록 되어있었는데 그것을 사용하지 못한다는 것이었다.
그래서 원래 Qt 5에서 CGSTexture.bind() 에 있던 코드를 참고해, OpenGL 에서 사용할 수 있게 GlFunction 을 호출하는 별도의 텍스쳐 클래스를 만드셨다.

그러고 나니 일단 '로그인 시 자동으로 실행될 것'을 제외한 목표가 달성되었다.

UI는 시시한 이야기이므로 몇 가지만 얘기하고 제끼고, 패키징한 이야기로 건너뛰어보자.

  • 프로그램이 시작되어 첫 윈도우가 표시되기 전에 CGSSetWindowLevel을 호출하면 무시되는데, 한 번 창이 뜬 이후에 호출해야하므로 그 사이에는 일반 윈도우로 취급되었다.
    그 때는 창이 포커스도 잡고 맨 위에 표시되므로, 영 보기 좋지 않았다. 그래서 모든 Qml UI의 opacity를 초기 실행 시 0으로 설정하고, 그 0.5초 뒤에 CSGSetWindowLevel을 호출, 다시 그 0.5초 뒤에 Qml UI의 opacity 를 transition과 함께 1로 설정했다. 나름 만족스럽게 허점이 숨겨진 것 같다.
  • 아무튼 이것도 프로그램이므로, 작업표시줄에 표시되고 Alt-Tab 목록에 포함된다. 당연히 이러면 안되므로, 아래 코드를 통해 그렇게 되지 않도록 바꾸었다:
    void setCurrentAppAsAgent() {
        ProcessSerialNumber psn = { 0, kCurrentProcess };
        TransformProcessType(&psn, kProcessTransformToUIElementApplication);
    }
    

이제 이 모든 것이 하나의 프로그램으로 패키징되어야했다. 아래와 같은 과정을 거쳤다.

  • 위에서 언급한 qspine은 하나의 Qml 라이브러리로서 .dylibqmldir 파일로 출력되는데, 그것을 내 Cpp 프로그램의 Qml Loader 가 인식할 수 있게 해야했다.
    그것은 QML2_IMPORT_PATH 환경변수에 최종 출력 파일들의 경로를 지정해줌으로써 해결했다. 당연히 수동으로 설정한 것이 아닌 프로그램 실행 시 C 표준의 putenv를 사용하여 진행했다.
  • qspine의 Qml 컴포넌트인 SpineItem에, 프로그램 바깥에 있는 .skel.atlas, .png 를 전달해주기 어려운 문제가 있었다.
    실행 환경에 따라 Working Directory 가 변해서, 상대경로를 사용할 수 없어 절대 경로를 사용해야하는데 그럴 경우 환경 자체를 옮겼을 때 또 문제가 되기 때문에 그러고싶지는 않았다.
    그리하여 이 리소스들도 하나로 번들링하기로 한다. 다행히도 SpineItem은 qrc: 접미어를 통해 프로그램 리소스에 있는 파일을 불러올 수 있었다.
  • 이번에 알게된건데 .app 으로 완성되는 패키징 결과물이 사실은 디렉터리였다...
    그냥 맘대로 안에 들어가서 내용을 수정할 수 있었다. 첫 번째 문제도 이 사실을 알게 되고 나서 좀 더 깔끔하게 수정했다. 그리하여 Contents/Resources/.icn 파일을 추가하여 앱 아이콘도 만들고... Contents/Info.plist 를 수정해서 앱 이름이나 번들이름같은것도 바꾸고 그랬다.

마지막으로, 패키징한 프로그램을 MacOS 설정에서 로그인 항목에 추가했다.
이제 배터리 사용량 변화 추이를 보고 실사용을 할지 말지를 결정하면 될 것 같다.

결론

쓰고 보니 진짜 내가 한 건 별로 없는 것 같다...
선배님은 4시간동안 삽질하시면서도 하나하나 해결해나가시던데 대단하시다 싶었다.
특히 OpenGL Function 문서 뒤지시면서 '나 이거 안해봐서 모르는데...'라고 하시다가도 금방 다음 단계로 넘어가시는게...
나는 진짜 거의 UI 부분만 했다고 봐도 무방한데... 어차피 UI는 시시한 이야기 이므로.

뭐 아무튼 이전에는 항상 좌측에 시계와 우측에 Spine 으로 만든 오퍼레이터를 표시했는데, 이번에는 시계를 생략하고 간단한 메시지만 표시하도록 했다. 물론 매번 같으면 재미가 없으므로 로그인마다 랜덤하게 바뀌도록 했다.

제발 다음에 내가 이 장난감을 메인터넌스할 일이 생기면 그 때는 이 선배님이 짜주신 코드들을 이해할 수 있게 되길 바랄 뿐이다. 나는 아직까지는 CPP 문법도 제대로 모르는 뉴비일 뿐이므로.

키위새의 아무말 저장소
  • 개발
  • 마인크래프트
  • 생명과학II
  • 아무말
Blog Logo