Repository navigation
Expand file tree
/
Copy pathCMakeLists.txt
More file actions
3736 lines (3501 loc) · 187 KB
/
Copy pathCMakeLists.txt
File metadata and controls
3736 lines (3501 loc) · 187 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Copyright (c) 2026 Mark Liversedge (liversedge@gmail.com)
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc., 51
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
cmake_minimum_required(VERSION 3.16)
# ── Version: single source of truth is src/Core/version.h ─────────────────────
# Derive the CMake/CPack/Inno project version from version.h so the installer
# version and the in-app updaters never drift from the compiled version (Windows
# design §7). MAJOR.MINOR come from the macros; PATCH is the monotonic build number
# (PINPOINT_VERSION_BUILD) so the Inno AppVersion strictly increases every release
# and WinSparkle's compare key matches. Bump version.h only.
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/src/Core/version.h" _pp_version_h)
string(REGEX MATCH "#define[ \t]+PINPOINT_VERSION_MAJOR[ \t]+([0-9]+)" _ "${_pp_version_h}")
set(_pp_ver_major "${CMAKE_MATCH_1}")
string(REGEX MATCH "#define[ \t]+PINPOINT_VERSION_MINOR[ \t]+([0-9]+)" _ "${_pp_version_h}")
set(_pp_ver_minor "${CMAKE_MATCH_1}")
string(REGEX MATCH "#define[ \t]+PINPOINT_VERSION_BUILD[ \t]+([0-9]+)" _ "${_pp_version_h}")
set(_pp_ver_build "${CMAKE_MATCH_1}")
# POSTFIX is the prerelease suffix ("-alpha3", "" for a clean release). Used for the
# human display version (macOS CFBundleShortVersionString / Sparkle shortVersionString).
string(REGEX MATCH "#define[ \t]+PINPOINT_VERSION_POSTFIX[ \t]+\"([^\"]*)\"" _ "${_pp_version_h}")
set(_pp_ver_postfix "${CMAKE_MATCH_1}")
# Compare against "" (not NOT), since a valid MAJOR of 0 is falsey to CMake's if().
if("${_pp_ver_major}" STREQUAL "" OR "${_pp_ver_minor}" STREQUAL "" OR "${_pp_ver_build}" STREQUAL "")
message(FATAL_ERROR "Could not parse version from src/Core/version.h")
endif()
project(PinPointStudio
VERSION ${_pp_ver_major}.${_pp_ver_minor}.${_pp_ver_build}
LANGUAGES CXX)
# Standard install dirs (bin/, lib/, share/). Without this CMAKE_INSTALL_BINDIR is
# empty and install(TARGETS ... RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) would
# place the executable at the install-prefix root while the DLLs (DESTINATION bin)
# land in bin/ — splitting the runtime tree. Including this pins BINDIR=bin.
include(GNUInstallDirs)
# ── Packaged-build marker ─────────────────────────────────────────────────────
# OFF for plain dev builds (QtCreator, run-from-build/); the packaging scripts
# configure with -DPINPOINT_INSTALLED=ON so the produced bundle/installer is
# marked as a real distributable. The in-app update engines key off this (plus a
# runtime check) to stay inert in a dev tree — the cross-platform analogue of the
# Linux "$APPIMAGE unset" guard. macOS design §3; Windows uses its installer's
# unins000.exe probe today, this is the portable knob.
option(PINPOINT_INSTALLED "Mark this as a packaged/installed build" OFF)
# ── Shipping-build assertion ─────────────────────────────────────────────────
# Separate from PINPOINT_INSTALLED on purpose. That one is a RUNTIME marker —
# it compiles a define in and the update engines key off it — and only the macOS
# script sets it, because flipping it on Linux/Windows would change how those
# builds decide whether they may update themselves. This one has no runtime
# effect whatsoever: it exists so a configure can be told "the output of this
# build is going to be handed to someone", and be checked.
#
# What it guards is a dependency we do not compile ourselves. libwrist is
# embedded (§ the block before qt_add_executable), so it is built with THIS
# project's CMAKE_BUILD_TYPE — there is no separate library build to get right,
# and its own `release` preset cannot apply to an add_subdirectory. Release is
# therefore what strips it, and the two ways that silently stops being true are
# a packaging path edited to RelWithDebInfo (-> 15 DWARF compile units and a
# 348 KB archive instead of 112 KB) and a configure with no build type at all,
# which is worse: no -O, no NDEBUG, and nothing warns. Both now fail loudly.
option(PP_SHIPPING_BUILD "This configure produces a distributable artefact — assert it is Release" OFF)
# The escape hatch for the PPCP assertion at the bottom of this file. A shipping
# build with no capture-device link is a decision someone has to make ON PURPOSE
# and write in the release notes, not something a missing -D does to them quietly.
# Declared here rather than beside the assertion so `cmake -LAH` answers "could a
# shipped PinPointStudio have no PPCP?" from the same place as the plaintext question.
option(PP_ALLOW_NO_PPCP
"Permit a shipping build with NO PPCP transport (no phone pairing). Deliberate only."
OFF)
# ══════════════════════════════════════════════════════════════════════════
# ⚠⚠⚠ PLAINTEXT PPCP. OFF, AND A SHIPPING BUILD REFUSES TO TURN IT ON. ⚠⚠⚠
#
# `PPCP-CONF` §2c drives the peer under test from outside, with `ppcp-sim` as
# the counterpart — and `ppcp-sim` has no TLS transport, so the conformance
# harness of work package H8 reaches this host over a PLAINTEXT loopback socket.
# `PPCP-RV` erratum E4 (RV 2c1) permits exactly that for a test harness, and
# `PPCP-CORE` §3.2's `direct` transport is conformant plaintext.
#
# It is declared HERE, in the application's configure, so that the answer to
# "could a shipped PinPointStudio listen without TLS?" is visible in this file
# and in `cmake -LAH`, rather than buried in a test directory. The only thing
# that sets it ON is `src/Ppcp/tests/CMakeLists.txt`. With it OFF, every
# plaintext branch in `src/Ppcp/ppcp_transport.cpp` is `#if`-ed out of
# existence: there is no code path to reach, which is the same way RV 5.2f's
# "never fall back to an unencrypted connection" is kept.
option(PP_PPCP_PLAINTEXT_HARNESS
"Compile the PLAINTEXT conformance-harness listener (CONF 2c). NEVER in a release build."
OFF)
if(PP_PPCP_PLAINTEXT_HARNESS AND PP_SHIPPING_BUILD)
message(FATAL_ERROR
"PP_PPCP_PLAINTEXT_HARNESS is ON in a shipping build. That would ship a listener "
"that accepts unauthenticated, unencrypted PPCP connections. Refusing to configure.")
endif()
if(PP_PPCP_PLAINTEXT_HARNESS)
add_compile_definitions(PP_PPCP_PLAINTEXT_HARNESS)
endif()
# ── RV-6's harness tap (H10) — the SAME shape and the SAME refusal ──────────
#
# `PPCP-RV` §11 is authenticated by a person comparing six digits on two
# screens. RT-20c drives BOTH applications either side of the relay and has to
# be able to press the confirm button without a person in the room, so this
# compiles an entry that supplies THE TAP.
#
# ⛔⛔ IT SUPPLIES THE TAP AND NEVER THE COMPARISON, AND THE DIFFERENCE IS THE
# WHOLE SECURITY OF THE PATH (11.1d, trap 8). Nothing behind this flag reads
# the six digits, receives the counterpart's, or decides whether they match; it
# operates the control a person would operate, and the decision it carries is a
# constant the harness was told to send. A build that compared them in software
# would pass every static test in the document and authenticate nothing.
#
# ⛔ AND IT MUST NEVER SHIP. A shipping build refuses to configure with it ON,
# exactly as the plaintext harness above does, and for a sharper reason: this
# one presses "yes, they match" on the user's behalf.
option(PP_PPCP_RV6_HARNESS
"Compile the RV-6 guided-pairing harness tap (RT-20c). NEVER in a release build."
OFF)
if(PP_PPCP_RV6_HARNESS AND PP_SHIPPING_BUILD)
message(FATAL_ERROR
"PP_PPCP_RV6_HARNESS is ON in a shipping build. That would ship an entry that "
"affirms a guided pairing without a person having compared anything — RV 11.1d "
"and 11.7c. Refusing to configure.")
endif()
if(PP_PPCP_RV6_HARNESS)
add_compile_definitions(PP_PPCP_RV6_HARNESS)
endif()
if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE)
message(FATAL_ERROR
"No CMAKE_BUILD_TYPE set.\n"
"A single-config generator with an empty build type compiles everything — this "
"project AND the embedded libwrist — with no optimisation and no NDEBUG, "
"and nothing downstream reports it. Pass one explicitly:\n"
" -DCMAKE_BUILD_TYPE=Debug for development\n"
" -DCMAKE_BUILD_TYPE=Release for anything you intend to ship")
endif()
# The option must reach the SOURCES, not just this file: AppInfo::devBuild gates the
# dev-only QML probe hook on it, and an option that is only a configure-time assertion
# leaves every #ifdef PP_SHIPPING_BUILD in the tree silently dead-false.
if(PP_SHIPPING_BUILD)
add_compile_definitions(PP_SHIPPING_BUILD)
endif()
if(PP_SHIPPING_BUILD AND NOT CMAKE_BUILD_TYPE STREQUAL "Release")
message(FATAL_ERROR
"PP_SHIPPING_BUILD=ON but CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}, not Release.\n"
"This build is packaged and handed to someone, so it must be Release: that is "
"what builds the embedded libwrist the way its `release` preset does "
"(-O3 -DNDEBUG, no -g). RelWithDebInfo ships the library's full DWARF and the "
"source layout of this machine, and does so silently.\n"
"To debug an optimised libwrist, build the LIBRARY on its own with its "
"`rel` preset — do not change the build type of a shipping configure.")
endif()
# ── Windows runtime bundling helper ───────────────────────────────────────────
# Copy a list of DLLs/files next to the dev executable (POST_BUILD) AND register
# them for install/packaging under bin/, tagged with a CPack component. Windows
# only; assumes a flat layout next to the executable. The function body is
# evaluated lazily at call time, so it may be defined before the target exists.
function(pp_win_bundle_dll _component)
if(NOT WIN32 OR NOT ARGN)
return()
endif()
foreach(_f ${ARGN})
add_custom_command(TARGET PinPointStudio POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${_f}" "$<TARGET_FILE_DIR:PinPointStudio>"
VERBATIM)
endforeach()
install(FILES ${ARGN} DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT ${_component})
endfunction()
# Build provenance recorded into saved shots (swing.json "capture.host"). The
# short git sha is captured at configure time — good enough for corpus
# provenance; reconfigure refreshes it.
execute_process(
COMMAND git rev-parse --short HEAD
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
OUTPUT_VARIABLE PP_GIT_SHA
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
if(NOT PP_GIT_SHA)
set(PP_GIT_SHA "unknown")
endif()
configure_file(cmake/pp_version.h.in "${CMAKE_BINARY_DIR}/generated/pp_version.h" @ONLY)
# Qt prefix. Shared with tests/cmake/PinPointTests.cmake so configuring the app
# and configuring a test suite land on the same Qt. Must precede find_package(Qt6):
# without it this project names no prefix at all, and CMake's ordinary search finds
# whatever Qt is on the box — on macOS the Homebrew one, which has no Quick3D, and
# the failure below then reads as a missing module rather than the wrong Qt.
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/PinPointQtPrefix.cmake)
# Qt GuiPrivate is used for QRhi (public API since Qt 6.6, accessed via GuiPrivate includes).
set(QT_NO_PRIVATE_MODULE_WARNING ON)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt6 REQUIRED COMPONENTS Quick QuickControls2 Quick3D SerialPort Bluetooth Multimedia Network WebSockets Concurrent ShaderTools GuiPrivate CorePrivate)
qt_standard_project_setup(REQUIRES 6.10)
include(FetchContent)
FetchContent_Declare(
whisper
GIT_REPOSITORY https://github.com/ggerganov/whisper.cpp.git
GIT_TAG v1.7.2
GIT_SHALLOW TRUE
)
set(WHISPER_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(WHISPER_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
# ── GPU acceleration for whisper.cpp (Linux / Windows) ─────────────────────
# Vulkan works on NVIDIA, AMD, and Intel without proprietary drivers.
# CUDA is tried as a fallback when Vulkan headers are not present.
if(NOT APPLE)
find_package(Vulkan QUIET)
if(Vulkan_FOUND)
set(GGML_VULKAN ON CACHE BOOL "" FORCE)
set(_whisper_gpu_backend "Vulkan")
message(STATUS "Whisper: Vulkan GPU acceleration enabled")
else()
# check_language(CUDA) is verbose even on failure; guard with a silent
# find_program first so machines without nvcc don't get noisy output.
find_program(_nvcc_path nvcc QUIET)
if(_nvcc_path)
include(CheckLanguage)
check_language(CUDA)
endif()
if(CMAKE_CUDA_COMPILER)
enable_language(CUDA)
set(GGML_CUDA ON CACHE BOOL "" FORCE)
# Target only the GPU(s) present; avoids compiling for old architectures
# (e.g. compute_52) that CUDA 12.x dropped support for.
set(CMAKE_CUDA_ARCHITECTURES native CACHE STRING "" FORCE)
if(MSVC)
# CUDA 13+ CCCL headers require the MSVC standard-conforming
# preprocessor; the traditional one triggers a hard #error.
string(APPEND CMAKE_CUDA_FLAGS " -Xcompiler /Zc:preprocessor")
endif()
# CUDA 13+ CUB/CCCL headers require C++17.
set(CMAKE_CUDA_STANDARD 17 CACHE STRING "" FORCE)
set(CMAKE_CUDA_STANDARD_REQUIRED ON CACHE BOOL "" FORCE)
set(_whisper_gpu_backend "CUDA")
message(STATUS "Whisper: CUDA GPU acceleration enabled")
else()
message(STATUS "Whisper: CPU only (install Vulkan SDK or CUDA Toolkit for GPU acceleration)")
endif()
endif()
endif()
FetchContent_MakeAvailable(whisper)
FetchContent_Declare(
libsamplerate
GIT_REPOSITORY https://github.com/libsndfile/libsamplerate.git
GIT_TAG 0.2.2
GIT_SHALLOW TRUE
PATCH_COMMAND
${CMAKE_COMMAND}
"-DFILE=${CMAKE_BINARY_DIR}/_deps/libsamplerate-src/CMakeLists.txt"
-P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/PatchLibsamplerate.cmake"
)
set(LIBSAMPLERATE_EXAMPLES OFF CACHE BOOL "" FORCE)
set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(libsamplerate)
# Eigen 3 (header-only) — required by the vendored ESKF orientation filter
# (third_party/imu_ekf). We only need the headers; EXCLUDE_FROM_ALL plus the
# disabled build options below keep Eigen's own tests/BLAS/doc targets out of
# the build. Fallback for offline builds: vendor the Eigen/ header tree under
# third_party/eigen/ and point the include path there instead.
set(EIGEN_BUILD_DOC OFF CACHE BOOL "" FORCE)
set(EIGEN_BUILD_TESTING OFF CACHE BOOL "" FORCE)
set(EIGEN_BUILD_PKGCONFIG OFF CACHE BOOL "" FORCE)
set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
# ⚠ AND THE SPARSE BENCHMARKS, WHICH NO EIGEN OPTION TURNS OFF. Eigen's own CMakeLists adds
# bench/spbench under a bare `if(NOT WIN32)` (eigen CMakeLists.txt:548), and that directory
# creates spbenchsolver / spsolver / test_sparseLU unconditionally — EXCLUDE_FROM_ALL, so
# they never build, but they are configured targets all the same. They then hunt for
# CHOLMOD/UMFPACK/KLU/SuperLU and link whatever they find, which on a Homebrew machine is
# /opt/homebrew/lib — the same directory that carries Homebrew's Qt and breaks the app's
# plugin resolution under Qt Creator (see the long note at the OpenCV block). Refusing the
# find_packages leaves the three targets present but linking nothing out of there.
#
# They still appear in Qt Creator's run-configuration list. That is Eigen's doing, there is
# no switch for it, and patching a fetched dependency to tidy a menu is the worse trade.
foreach(_pp_no_find CHOLMOD UMFPACK KLU SuperLU PASTIX METIS)
set(CMAKE_DISABLE_FIND_PACKAGE_${_pp_no_find} ON)
endforeach()
FetchContent_Declare(
eigen
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
GIT_TAG 3.4.0
GIT_SHALLOW TRUE
EXCLUDE_FROM_ALL
)
FetchContent_MakeAvailable(eigen)
# ── libwrist — C11 sans-I/O driver for the HackMotion wG3 wrist sensor ───
# github.com/PinPoint-Golf/libwrist, MIT. A sister project:
# it owns no radio, thread, timer or clock — PinPoint supplies the BLE transport
# (BleImuTransport) and the host clock, and drains the library. See
# docs/implementation/hackmotion_integration_brief.md for what consumes it.
#
# Static, like whisper/samplerate/espeak-ng, courtesy of the global
# BUILD_SHARED_LIBS OFF above — so nothing has to be bundled by any of the three
# packaging paths. Placed here (before qt_add_executable) so wrist_SOURCE_DIR
# is resolved for the provenance block and the offline-target function below.
#
# ⚠ BUILD TYPE: the library's own CMakePresets.json — including its `release`
# preset — is INERT here. Presets apply to a top-level configure only, and this
# is an add_subdirectory, so the library compiles at whatever CMAKE_BUILD_TYPE
# this project was configured with. Release therefore already yields exactly what
# that preset yields: measured on this source, -O3 -DNDEBUG and no -g, 112 KB
# with zero DWARF compile units. Every path that produces a distributable passes
# -DCMAKE_BUILD_TYPE=Release — tools/package_macos.sh, tools/package_appimage.sh
# and packaging/build_installer.ps1, plus the direct configure in the Windows leg
# of .github/workflows/release.yml (its macOS and Linux legs run those scripts) —
# and PP_SHIPPING_BUILD now makes each of them assert it, so shipping the debug
# info takes a deliberate change. Do NOT switch a release
# path to RelWithDebInfo to get a symbolised stack: it lifts the archive to 348 KB
# / 15 compile units and the failure is silent. Build the LIBRARY standalone with
# its `rel` preset instead — that is the question that preset exists to answer.
#
# ⚠ SOURCE RESOLUTION: a sibling ../libwrist checkout WINS over GitHub.
# The library is under active co-development and its fixes are made and pushed
# from that clone, so the build follows it directly — CMake performs no download
# and no checkout when FETCHCONTENT_SOURCE_DIR_WRIST is set, which is the
# mechanism its own documentation points developers at. NEVER edit the fetched
# copy under build/*/_deps/wrist-src: it is a shallow clone, the update step
# overwrites local changes on reconfigure, and `rm -rf build` destroys them.
option(PP_LIBWRIST_LOCAL "Prefer a sibling ../libwrist checkout over GitHub" ON)
set(_wrist_local "${CMAKE_CURRENT_SOURCE_DIR}/../libwrist")
if(PP_LIBWRIST_LOCAL AND EXISTS "${_wrist_local}/CMakeLists.txt")
get_filename_component(_wrist_local "${_wrist_local}" ABSOLUTE)
set(FETCHCONTENT_SOURCE_DIR_WRIST "${_wrist_local}" CACHE PATH
"Local libwrist checkout to build against instead of fetching" FORCE)
set(_wrist_origin "local ${_wrist_local}")
else()
# Clear the cache entry rather than merely not setting it. It was written
# FORCE above, so a build dir that once resolved the sibling checkout still
# carries the path — and FetchContent would keep honouring it, making
# -DPP_LIBWRIST_LOCAL=OFF a silent no-op on an existing build dir. That
# switch exists to check a local fix against upstream main, so it has to
# actually reach upstream main.
unset(FETCHCONTENT_SOURCE_DIR_WRIST CACHE)
unset(_wrist_local)
set(_wrist_origin "github main")
endif()
# Nothing to switch off: the library's tests, tools, FFI object, -Werror and
# install rules all default to its own top-level check, so embedding it yields
# the library and nothing else. Asking for the record module is the one positive
# statement we make — the offline targets below link it, so say so rather than
# inherit it.
set(WR_BUILD_RECORD ON CACHE BOOL "" FORCE)
FetchContent_Declare(
wrist
# ⚠ Deliberately tracking a BRANCH, not a tag: bugs are expected while the
# integration lands, and FETCHCONTENT_UPDATES_DISCONNECTED is OFF by default so
# every reconfigure picks up the latest main. Revert to a tag pin (GIT_TAG
# v0.x.0) once the library stabilises — and note GIT_SHALLOW works with
# branches and tags but NOT with a raw commit hash, so it must go if a SHA is
# ever pinned. Ignored entirely when a sibling checkout was found above.
GIT_REPOSITORY https://github.com/PinPoint-Golf/libwrist.git
GIT_TAG main
GIT_SHALLOW TRUE
EXCLUDE_FROM_ALL
)
FetchContent_MakeAvailable(wrist)
# Provenance. With a branch tracked upstream and a local checkout able to win,
# "which libwrist built this?" is a question the About box has to answer —
# it is how a capture gets traced back to a library build while chasing a bug.
file(STRINGS "${wrist_SOURCE_DIR}/include/wrist/version.h" _wrist_ver_line
REGEX "^#define[ \t]+WR_VERSION_STRING")
string(REGEX REPLACE ".*\"([^\"]+)\".*" "\\1" PP_LIBWRIST_VERSION "${_wrist_ver_line}")
execute_process(
COMMAND git rev-parse --short HEAD
WORKING_DIRECTORY "${wrist_SOURCE_DIR}"
OUTPUT_VARIABLE _wrist_sha OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
execute_process(
COMMAND git status --porcelain
WORKING_DIRECTORY "${wrist_SOURCE_DIR}"
OUTPUT_VARIABLE _wrist_dirty OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
if(_wrist_sha)
if(_wrist_dirty)
string(APPEND _wrist_sha "-dirty")
endif()
if(_wrist_local)
string(APPEND PP_LIBWRIST_VERSION " (${_wrist_sha}, local)")
else()
string(APPEND PP_LIBWRIST_VERSION " (${_wrist_sha})")
endif()
endif()
message(STATUS "libwrist: ${PP_LIBWRIST_VERSION} — ${_wrist_origin}")
unset(_wrist_ver_line)
unset(_wrist_sha)
unset(_wrist_dirty)
unset(_wrist_local)
# ── libppcp — C11 sans-I/O implementation of the PinPoint Capture Protocol ───
# github.com/PinPoint-Golf/libppcp, MIT. Work package H0 of
# libppcp/docs/implementation/implementation-plan.md, and decision A4 there:
# PinPointStudio embeds libppcp EXACTLY as it embeds libwrist, so the block
# above is the pattern and this one deliberately does not invent a second.
#
# The same sister-project shape: it owns no socket, thread, timer, clock or file
# (plan ground rule 7). PinPointStudio supplies the transport — src/Ppcp, the
# block near the end of this file — the host clock and the storage, and drains
# the library. That split is what lets one MIT implementation serve both ends of
# the protocol without dragging a transport dependency into it (CORE A.3).
#
# ⚠ SOURCE RESOLUTION: a sibling ../libppcp checkout WINS over GitHub, for the
# same reason libwrist's does — the library is under active co-development and
# its fixes are made in that clone. NEVER edit the fetched copy under
# build/*/_deps/ppcp-src: the update step overwrites it on reconfigure.
#
# ⚠ PP_LIBPPCP_FETCH DEFAULTS OFF, AND THAT IS TEMPORARY. A4 says FetchContent
# from GitHub with the sibling overriding it, and that is what this block does —
# but the repository is not published yet, and a failed clone is a failed
# configure for everyone including people who do not care about PPCP. Flip this
# default to ON the day PinPoint-Golf/libppcp is pushed; nothing else changes.
option(PP_LIBPPCP_LOCAL "Prefer a sibling ../libppcp checkout over GitHub" ON)
option(PP_LIBPPCP_FETCH "Fetch libppcp from GitHub when no sibling checkout exists" OFF)
set(_ppcp_local "${CMAKE_CURRENT_SOURCE_DIR}/../libppcp")
set(_ppcp_origin "")
if(PP_LIBPPCP_LOCAL AND EXISTS "${_ppcp_local}/CMakeLists.txt")
get_filename_component(_ppcp_local "${_ppcp_local}" ABSOLUTE)
set(FETCHCONTENT_SOURCE_DIR_PPCP "${_ppcp_local}" CACHE PATH
"Local libppcp checkout to build against instead of fetching" FORCE)
set(_ppcp_origin "local ${_ppcp_local}")
elseif(PP_LIBPPCP_FETCH)
# Written FORCE above, so clear it rather than merely not setting it — else
# a build dir that once resolved the sibling keeps honouring the stale path
# and -DPP_LIBPPCP_LOCAL=OFF becomes a silent no-op. Same trap as libwrist.
unset(FETCHCONTENT_SOURCE_DIR_PPCP CACHE)
unset(_ppcp_local)
set(_ppcp_origin "github main")
else()
unset(FETCHCONTENT_SOURCE_DIR_PPCP CACHE)
unset(_ppcp_local)
endif()
if(_ppcp_origin)
# Defensive, and no-ops if the library names its options differently: it is
# expected to gate its own tests, tools and install rules on its top-level
# check the way libwrist does, so embedding yields the library and nothing else.
set(PPCP_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(PPCP_BUILD_TOOLS OFF CACHE BOOL "" FORCE)
FetchContent_Declare(
ppcp
# ⚠ Tracking a BRANCH, not a tag, while the protocol lands — the same
# deliberate choice as libwrist above, and to be pinned once ppcp/1.0
# freezes. GIT_SHALLOW works with branches and tags but NOT a raw SHA.
GIT_REPOSITORY https://github.com/PinPoint-Golf/libppcp.git
GIT_TAG main
GIT_SHALLOW TRUE
EXCLUDE_FROM_ALL
)
FetchContent_MakeAvailable(ppcp)
# Provenance, for the same reason libwrist has it: a branch upstream and a
# local checkout able to win means "which libppcp built this?" is a question
# the About box has to answer, and a conformance claim is worthless without
# the commit it was measured against.
file(STRINGS "${ppcp_SOURCE_DIR}/include/ppcp/version.h" _ppcp_ver_line
REGEX "^#define[ \t]+PPCP_LIB_VERSION_STRING")
string(REGEX REPLACE ".*\"([^\"]+)\".*" "\\1" PP_LIBPPCP_VERSION "${_ppcp_ver_line}")
execute_process(
COMMAND git rev-parse --short HEAD
WORKING_DIRECTORY "${ppcp_SOURCE_DIR}"
OUTPUT_VARIABLE _ppcp_sha OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
execute_process(
COMMAND git status --porcelain
WORKING_DIRECTORY "${ppcp_SOURCE_DIR}"
OUTPUT_VARIABLE _ppcp_dirty OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
if(_ppcp_sha)
if(_ppcp_dirty)
string(APPEND _ppcp_sha "-dirty")
endif()
if(_ppcp_local)
string(APPEND PP_LIBPPCP_VERSION " (${_ppcp_sha}, local)")
else()
string(APPEND PP_LIBPPCP_VERSION " (${_ppcp_sha})")
endif()
endif()
message(STATUS "libppcp: ${PP_LIBPPCP_VERSION} — ${_ppcp_origin}")
else()
message(STATUS "libppcp: not embedded (no sibling ../libppcp and PP_LIBPPCP_FETCH is OFF)")
endif()
unset(_ppcp_ver_line)
unset(_ppcp_sha)
unset(_ppcp_dirty)
unset(_ppcp_local)
unset(_ppcp_origin)
unset(_wrist_origin)
# ── libgspro — C11 sans-I/O server for the GSPro Open Connect v1 protocol ────
# github.com/PinPoint-Golf/libgspro, MIT. The third sister project, embedded
# exactly as libwrist and libppcp are — the two blocks above are the pattern and
# this one deliberately does not invent a third.
#
# What it is FOR. Many launch monitors are reachable through a GSPro Open Connect
# client somebody else has already written — the library's survey found seventeen,
# five of them for the Garmin R10 alone, plus the Rapsodo MLM2PRO, a SkyTrak+ via
# OpenSkyPlus, a Foresight GC2's serial feed, an SLX proxy, and PiTrac and
# OpenFlight which are native clients. Speaking the SERVER side of that one
# protocol reaches all of them with no per-device work, which is why it is worth a
# dependency rather than a parser per device. It is the second Kind of launch
# monitor connector beside GcQuad — see the library's docs/design.md §6, which is
# written against THIS repository's LaunchMonitorBase / LaunchMonitorReading /
# ShotPairing.
#
# ⚠ IT DOES NOT REPLACE THE GCQUAD CONNECTOR. Uneekor, Bushnell and Foresight ship
# no Open Connect client at all — their connectors are closed and speak their own
# vendors' simulators — so a GCQuad is still reached by watching FSX2020's
# LastShot.CSV, and a Uneekor only by a third-party folder-watching bridge.
#
# ⚠ THE SAME SANS-I/O SPLIT, AND HERE IT IS THE POINT OF THE LIBRARY. It owns no
# socket, thread, timer, clock or file: PinPoint supplies the QTcpServer, the
# host clock and the storage, and drains the library. So the reference transport
# (GS_BUILD_NET) and the .gswire recorder (GS_BUILD_RECORD) are NOT asked for
# here — a Qt host has its own event loop and its own storage, and linking a
# select() loop into an application that already has one is the mistake the
# split exists to make impossible. Both default OFF when embedded; this comment
# is why they are not turned on rather than an oversight.
#
# ⚠ SOURCE RESOLUTION: a sibling ../libgspro checkout WINS over GitHub, for the
# same reason libwrist's and libppcp's do — the library is under active
# co-development and its fixes are made and pushed from that clone. NEVER edit
# the fetched copy under build/*/_deps/gspro-src: it is a shallow clone whose
# update step overwrites local changes on reconfigure.
#
# ⚠ Unlike libppcp there is no PP_LIBGSPRO_FETCH switch: the repository IS
# published, so a machine with no sibling checkout fetches it and the build works
# for someone who has never heard of the library. That is the libwrist shape.
option(PP_LIBGSPRO_LOCAL "Prefer a sibling ../libgspro checkout over GitHub" ON)
set(_gspro_local "${CMAKE_CURRENT_SOURCE_DIR}/../libgspro")
if(PP_LIBGSPRO_LOCAL AND EXISTS "${_gspro_local}/CMakeLists.txt")
get_filename_component(_gspro_local "${_gspro_local}" ABSOLUTE)
set(FETCHCONTENT_SOURCE_DIR_GSPRO "${_gspro_local}" CACHE PATH
"Local libgspro checkout to build against instead of fetching" FORCE)
set(_gspro_origin "local ${_gspro_local}")
else()
# Cleared rather than merely not set: the branch above writes FORCE, so a
# build dir that once resolved the sibling still carries the path and
# -DPP_LIBGSPRO_LOCAL=OFF would be a silent no-op. Same trap as the two above.
unset(FETCHCONTENT_SOURCE_DIR_GSPRO CACHE)
unset(_gspro_local)
set(_gspro_origin "github main")
endif()
FetchContent_Declare(
gspro
# ⚠ Tracking a BRANCH while the integration lands, as libwrist does; to be
# pinned to a tag once the library stabilises. GIT_SHALLOW works with branches
# and tags but NOT a raw SHA. Ignored entirely when the sibling was found.
GIT_REPOSITORY https://github.com/PinPoint-Golf/libgspro.git
GIT_TAG main
GIT_SHALLOW TRUE
EXCLUDE_FROM_ALL
)
FetchContent_MakeAvailable(gspro)
# Provenance, for the reason the other two have it: a branch upstream and a local
# checkout able to win means "which libgspro built this?" is a question the About
# box has to answer — and a launch monitor session traced back to a library build
# is the difference between a bug report and a guess.
file(STRINGS "${gspro_SOURCE_DIR}/include/gspro/version.h" _gspro_ver_line
REGEX "^#define[ \t]+GSP_VERSION_STRING")
string(REGEX REPLACE ".*\"([^\"]+)\".*" "\\1" PP_LIBGSPRO_VERSION "${_gspro_ver_line}")
execute_process(
COMMAND git rev-parse --short HEAD
WORKING_DIRECTORY "${gspro_SOURCE_DIR}"
OUTPUT_VARIABLE _gspro_sha OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
execute_process(
COMMAND git status --porcelain
WORKING_DIRECTORY "${gspro_SOURCE_DIR}"
OUTPUT_VARIABLE _gspro_dirty OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
if(_gspro_sha)
if(_gspro_dirty)
string(APPEND _gspro_sha "-dirty")
endif()
if(_gspro_local)
string(APPEND PP_LIBGSPRO_VERSION " (${_gspro_sha}, local)")
else()
string(APPEND PP_LIBGSPRO_VERSION " (${_gspro_sha})")
endif()
endif()
message(STATUS "libgspro: ${PP_LIBGSPRO_VERSION} — ${_gspro_origin}")
unset(_gspro_ver_line)
unset(_gspro_sha)
unset(_gspro_dirty)
unset(_gspro_local)
unset(_gspro_origin)
# ── libppswing — the PinPoint swing file format (.ppsw) ──────────────────────
# github.com/PinPoint-Golf/libppswing, MIT. The fourth sister project, embedded
# the way libgspro is (the block above is the pattern). It reads and writes the
# per-swing document: swing.ppsw replaced swing.json as the written format in
# Phase 2 of docs/implementation/swing_storage_impl.md, and swing.json is still
# read, indefinitely. C++17, one vendored dependency (zstd), links nothing else.
#
# ⚠ THE APP NEVER INCLUDES A ppsw HEADER OUTSIDE src/Export/swing_store.cpp and
# ppsw_qt.cpp. Those two files are the pinpoint_swingstore library
# (cmake/PinPointSwingStore.cmake), and every target that touches a swing
# document links that one name. That is what keeps dozens of test link lines
# from each having to learn about this library.
#
# ⚠ SOURCE RESOLUTION: a sibling ../libppswing checkout WINS over GitHub, as for
# the other three. NEVER edit the fetched copy under build/*/_deps/ppswing-src.
# The repository is public, so there is no FETCH switch: a machine with no
# sibling fetches it, and the build works for someone who has never heard of it.
option(PP_LIBPPSWING_LOCAL "Prefer a sibling ../libppswing checkout over GitHub" ON)
set(_ppsw_local "${CMAKE_CURRENT_SOURCE_DIR}/../libppswing")
if(PP_LIBPPSWING_LOCAL AND EXISTS "${_ppsw_local}/CMakeLists.txt")
get_filename_component(_ppsw_local "${_ppsw_local}" ABSOLUTE)
set(FETCHCONTENT_SOURCE_DIR_PPSWING "${_ppsw_local}" CACHE PATH
"Local libppswing checkout to build against instead of fetching" FORCE)
set(_ppsw_origin "local ${_ppsw_local}")
else()
# Cleared, not merely unset: same stale-cache trap as the three above.
unset(FETCHCONTENT_SOURCE_DIR_PPSWING CACHE)
unset(_ppsw_local)
set(_ppsw_origin "github main")
endif()
# Embedded, the library already defaults its tests, tools, -Werror and install
# rules OFF (its top-level check); set them anyway so an embed never inherits a
# gate that is the library's own.
set(PPSW_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(PPSW_BUILD_TOOLS OFF CACHE BOOL "" FORCE)
FetchContent_Declare(
ppswing
# ⚠ Tracking a BRANCH while the format is at 1.0 draft, as libgspro does; to
# be pinned to a tag once the spec is final.
GIT_REPOSITORY https://github.com/PinPoint-Golf/libppswing.git
GIT_TAG main
GIT_SHALLOW TRUE
EXCLUDE_FROM_ALL
)
FetchContent_MakeAvailable(ppswing)
# Provenance, for the About box: the library has no version header, so the
# version is its project() line, plus the commit, as for the others.
file(STRINGS "${ppswing_SOURCE_DIR}/CMakeLists.txt" _ppsw_ver_line REGEX "^[ \t]*VERSION[ \t]+[0-9]")
string(REGEX REPLACE ".*VERSION[ \t]+([0-9.]+).*" "\\1" PP_LIBPPSWING_VERSION "${_ppsw_ver_line}")
execute_process(
COMMAND git rev-parse --short HEAD
WORKING_DIRECTORY "${ppswing_SOURCE_DIR}"
OUTPUT_VARIABLE _ppsw_sha OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
execute_process(
COMMAND git status --porcelain
WORKING_DIRECTORY "${ppswing_SOURCE_DIR}"
OUTPUT_VARIABLE _ppsw_dirty OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
if(_ppsw_sha)
if(_ppsw_dirty)
string(APPEND _ppsw_sha "-dirty")
endif()
if(_ppsw_local)
string(APPEND PP_LIBPPSWING_VERSION " (${_ppsw_sha}, local)")
else()
string(APPEND PP_LIBPPSWING_VERSION " (${_ppsw_sha})")
endif()
endif()
message(STATUS "libppswing: ${PP_LIBPPSWING_VERSION} — ${_ppsw_origin}")
unset(_ppsw_ver_line)
unset(_ppsw_sha)
unset(_ppsw_dirty)
unset(_ppsw_local)
unset(_ppsw_origin)
include(cmake/PinPointSwingStore.cmake)
pp_define_swingstore(${CMAKE_CURRENT_SOURCE_DIR})
# EventBuffer library — lock-free ring-buffer capture store
add_subdirectory(src/Buffer)
qt_add_executable(PinPointStudio
src/Gui/main.cpp
$<$<BOOL:${APPLE}>:${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/icons/PinPointStudio.icns>
$<$<BOOL:${WIN32}>:${CMAKE_CURRENT_SOURCE_DIR}/PinPointStudio.rc>
src/Gui/app/app_settings.h src/Gui/app/app_settings.cpp
src/Gui/settings/library_converter.h src/Gui/settings/library_converter.cpp
src/Gui/settings/archive_controller.h src/Gui/settings/archive_controller.cpp
src/Gui/app/notification_center.h src/Gui/app/notification_center.cpp
src/Gui/shot/ppcp_clip_filer.h src/Gui/shot/ppcp_clip_filer.cpp
src/Gui/imu/imu_manager.h src/Gui/imu/imu_manager.cpp
src/Gui/imu/imu_role_map.h src/Gui/imu/imu_role_map.cpp
src/Gui/imu/paced_connect_queue.h src/Gui/imu/paced_connect_queue.cpp
src/Analysis/segment_role.h
src/Gui/imu/imu_instance.h src/Gui/imu/imu_instance.cpp
# imu_device.h carries Q_OBJECT and has no .cpp of its own, so AUTOMOC only
# sees it if it is listed here — without the line, HmInstance links against
# an ImuDeviceBase with no metaobject.
src/Gui/imu/imu_device.h
src/Gui/imu/hm_instance.h src/Gui/imu/hm_instance.cpp
src/Gui/media/transcription_controller.h src/Gui/media/transcription_controller.cpp
src/Gui/media/tts_controller.h src/Gui/media/tts_controller.cpp
src/Gui/media/film_controller.h src/Gui/media/film_controller.cpp
src/Gui/athlete/athlete_controller.h src/Gui/athlete/athlete_controller.cpp
src/Gui/shell/navigation_controller.h src/Gui/shell/navigation_controller.cpp
src/Gui/monitor/resource_monitor_controller.h src/Gui/monitor/resource_monitor_controller.cpp
src/Gui/monitor/profiler_controller.h src/Gui/monitor/profiler_controller.cpp
src/Gui/app/clipboard_helper.h
src/Gui/app/app_log.h src/Gui/app/app_log.cpp
src/Gui/session/session_controller.h src/Gui/session/session_controller.cpp
src/Gui/review/session_review_controller.h src/Gui/review/session_review_controller.cpp
src/Gui/review/session_list_model.h src/Gui/review/session_list_model.cpp
src/Gui/review/session_summary.h
src/Gui/shot/shot_arbiter.h
src/Gui/shot/shot_controller.h src/Gui/shot/shot_controller.cpp
src/Gui/shot/shot_list_model.h src/Gui/shot/shot_list_model.cpp
src/Gui/shot/shot_processor.h src/Gui/shot/shot_processor.cpp
src/Gui/shot/shot_replay_controller.h src/Gui/shot/shot_replay_controller.cpp
src/Gui/shot/current_swing.h
src/Gui/shot/reanalysis_controller.h src/Gui/shot/reanalysis_controller.cpp
src/Gui/markup/markup_truth.h src/Gui/markup/markup_truth.cpp
src/Gui/markup/markup_controller.h src/Gui/markup/markup_controller.cpp
src/Gui/markup/markup_image_provider.h
src/Gui/shot/replay_source.h
src/Gui/shot/disk_replay_source.h src/Gui/shot/disk_replay_source.cpp
src/Gui/shot/dtl_overlay_payload.h src/Gui/shot/dtl_overlay_payload.cpp
src/Gui/viz/live_wrist_angles.h src/Gui/viz/live_wrist_angles.cpp
src/IMU/imu_base.h src/IMU/imu_base.cpp
src/IMU/imu_capabilities.h
src/IMU/iorientation_filter.h
src/IMU/orientation_filter.h
src/IMU/eskf_orientation_filter.h src/IMU/eskf_orientation_filter.cpp
src/IMU/imu_calibration.h
src/IMU/impact_detector.h
src/IMU/imu_factory.h src/IMU/imu_factory.cpp
src/IMU/wt9011dcl_base.h src/IMU/wt9011dcl_base.cpp
src/IMU/wt9011dcl.h src/IMU/wt9011dcl.cpp
src/IMU/ble_adapter_pool.h src/IMU/ble_adapter_pool.cpp
src/IMU/ble_imu_transport.h src/IMU/ble_imu_transport.cpp
src/IMU/wt9011dcl_ble.h src/IMU/wt9011dcl_ble.cpp
src/IMU/imu_io_worker.h src/IMU/imu_io_worker.cpp
src/IMU/hm_session_types.h
src/IMU/hm_session_worker.h src/IMU/hm_session_worker.cpp
src/LaunchMonitor/launch_monitor_reading.h src/LaunchMonitor/launch_monitor_reading.cpp
src/LaunchMonitor/launch_monitor_base.h src/LaunchMonitor/launch_monitor_base.cpp
src/LaunchMonitor/launch_monitor_factory.h src/LaunchMonitor/launch_monitor_factory.cpp
src/LaunchMonitor/shot_pairing.h
src/LaunchMonitor/standalone_gate.h src/LaunchMonitor/standalone_gate.cpp
src/LaunchMonitor/gcquad_csv_parser.h src/LaunchMonitor/gcquad_csv_parser.cpp
src/LaunchMonitor/gcquad_monitor.h src/LaunchMonitor/gcquad_monitor.cpp
src/LaunchMonitor/gspro_reading.h src/LaunchMonitor/gspro_reading.cpp
src/LaunchMonitor/gspro_monitor.h src/LaunchMonitor/gspro_monitor.cpp
src/Gui/launchmonitor/launch_monitor_controller.h src/Gui/launchmonitor/launch_monitor_controller.cpp
src/Audio/onset_detector.h
src/Audio/acoustic_shot_detector.h src/Audio/acoustic_shot_detector.cpp
src/Audio/audio_processor_base.h src/Audio/audio_processor_base.cpp
src/Audio/audio_input_base.h src/Audio/audio_input_base.cpp
src/Audio/audio_input.h src/Audio/audio_input.cpp
src/Audio/audio_output_base.h src/Audio/audio_output_base.cpp
src/Audio/audio_output.h src/Audio/audio_output.cpp
src/Audio/audio_stream_saver.h src/Audio/audio_stream_saver.cpp
src/Video/camera_capabilities.h
src/Video/video_input_base.h src/Video/video_input_base.cpp
src/Video/video_input.h src/Video/video_input.cpp
src/Video/video_input_factory.h src/Video/video_input_factory.cpp
src/Video/video_preprocessor_base.h src/Video/video_preprocessor_base.cpp
src/Video/video_overlay_base.h src/Video/video_overlay_base.cpp
src/Video/raw_video_frame.h src/Video/raw_video_frame.cpp
src/Video/frame_crop.h src/Video/frame_crop.cpp
src/Pose/pose_estimator_base.h src/Pose/pose_estimator_base.cpp
src/Core/device_enumerator.h src/Core/device_enumerator.cpp
src/Video/VideoInputAravis.h src/Video/VideoInputAravis.cpp
src/Gui/cameras/camera_instance.h src/Gui/cameras/camera_instance.cpp
src/Gui/cameras/camera_manager.h src/Gui/cameras/camera_manager.cpp
src/Gui/cameras/camera_role_probe.h src/Gui/cameras/camera_role_probe.cpp
src/Gui/cameras/buffer_controller.h src/Gui/cameras/buffer_controller.cpp
src/STT/stt_processor.h src/STT/stt_processor.cpp
src/TTS/TTSEngine.h src/TTS/TTSEngine.cpp
src/TTS/TTSEngineFactory.h src/TTS/TTSEngineFactory.cpp
src/TTS/AzureTTSEngine.h src/TTS/AzureTTSEngine.cpp
src/TTS/KokoroTTSEngine.h src/TTS/KokoroTTSEngine.cpp
src/TTS/PhonemeTokenizer.h src/TTS/PhonemeTokenizer.cpp
src/TTS/TtsWorker.h src/TTS/TtsWorker.cpp
src/TTS/ModelDownloader.h src/TTS/ModelDownloader.cpp
src/LLM/LlmEngine.h src/LLM/LlmEngine.cpp
src/LLM/LocalLlmEngine.h src/LLM/LocalLlmEngine.cpp
src/LLM/GeminiLlmEngine.h src/LLM/GeminiLlmEngine.cpp
src/LLM/LlmWorker.h src/LLM/LlmWorker.cpp
src/Gui/media/llm_controller.h src/Gui/media/llm_controller.cpp
)
target_include_directories(PinPointStudio PRIVATE
src/Gui
src/Gui/app src/Gui/shell src/Gui/athlete src/Gui/monitor src/Gui/cameras
src/Gui/imu src/Gui/viz src/Gui/swing3d src/Gui/session src/Gui/shot src/Gui/review
src/Gui/media src/Gui/calibration src/Gui/diagnostics src/Gui/markup src/Gui/charts src/Gui/trust
src/Gui/characteristics src/Gui/diagnosticmodel src/Gui/launchmonitor src/Gui/print
src/IMU src/LaunchMonitor src/Audio src/Video src/Pose src/STT src/TTS src/LLM src/Secrets src/Core src/Buffer
src/Analysis src/Club src/Wrist src/Body src/Timeline src/Ball src/Kinematics src/Skeleton3d
src/Coaching src/Metrics
src/Diagnostics/engine src/Diagnostics/packs src/Diagnostics/norms src/Diagnostics/measures
src/Update
"${CMAKE_BINARY_DIR}/generated")
# The module's three lists — .qml files, which of them are singletons, and the C++ types —
# live in cmake/PinPointQmlModule.cmake because qml_ui_test declares the SAME module to press
# real components without building the app. One list, two consumers; see that file for why the
# singleton list in particular must not get separated from the other two.
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/PinPointQmlModule.cmake)
foreach(_pp_singleton IN LISTS PP_QML_SINGLETONS)
set_source_files_properties(${_pp_singleton} PROPERTIES QT_QML_SINGLETON_TYPE TRUE)
endforeach()
qt_add_qml_module(PinPointStudio
URI PinPointStudio
OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/qml/PinPointStudio
QML_FILES ${PP_QML_FILES}
SOURCES ${PP_QML_SOURCES}
)
qt6_add_shaders(PinPointStudio "debayer_shaders"
BATCHABLE
PRECOMPILE
PREFIX "/shaders"
FILES
"src/Shaders/debayer.vert"
"src/Shaders/debayer.frag"
)
qt6_add_shaders(PinPointStudio "bayer_demosaic_shaders"
PRECOMPILE
OPTIMIZED
PREFIX "/shaders"
FILES
"src/Shaders/bayer_demosaic.vert"
"src/Shaders/bayer_demosaic.frag"
)
qt6_add_shaders(PinPointStudio "topo_shaders"
BATCHABLE
PRECOMPILE
OPTIMIZED
PREFIX "/shaders"
FILES
"src/Shaders/topo.vert"
"src/Shaders/topo.frag"
)
qt6_add_shaders(PinPointStudio "viewgain_shaders"
BATCHABLE
PRECOMPILE
OPTIMIZED
PREFIX "/shaders"
FILES
"src/Shaders/viewgain.vert"
"src/Shaders/viewgain.frag"
)
target_compile_definitions(PinPointStudio PRIVATE
QT_APPLICATION_NAME="PinPointStudio"
)
# ── PinPointStudio debug level ──────────────────────────────────────────────────────
# Controls pp_debug.h verbosity at compile time:
# 0 = silent 1 = warnings+errors only (default for all builds)
# 2 = startup info + warnings/errors (pass -DPINPOINT_DEBUG_LEVEL=2)
# 3 = verbose debug + all of the above
if(NOT DEFINED PINPOINT_DEBUG_LEVEL)
set(PINPOINT_DEBUG_LEVEL 1)
endif()
message(STATUS "PinPointStudio debug level: ${PINPOINT_DEBUG_LEVEL}")
target_compile_definitions(PinPointStudio PRIVATE PINPOINT_DEBUG_LEVEL=${PINPOINT_DEBUG_LEVEL})
# ── Resource profiler ───────────────────────────────────────────────────────
# Baseline tier ships in ALL builds (incl. release) through alpha/beta; flip OFF
# for GA. Deep tier is opt-in at compile time AND gated by a runtime toggle.
option(PINPOINT_PROFILE_BASELINE "Always-on baseline resource profiling" ON)
option(PINPOINT_PROFILE "Deep, toggle-gated resource profiling" OFF)
message(STATUS "PinPointStudio profiler: baseline=${PINPOINT_PROFILE_BASELINE} deep=${PINPOINT_PROFILE}")
target_compile_definitions(PinPointStudio PRIVATE
PINPOINT_PROFILE_BASELINE=$<BOOL:${PINPOINT_PROFILE_BASELINE}>
PINPOINT_PROFILE=$<BOOL:${PINPOINT_PROFILE}>)
target_sources(PinPointStudio PRIVATE
src/Core/pp_debug.h
src/Core/pp_debug.cpp
src/Core/pp_log_stream.cpp
src/Core/pp_colorspace_pin.h
src/Core/pp_colorspace_pin.cpp
src/Core/app_info.h
src/Core/app_info.cpp
src/Core/pp_settings.h
src/Core/PpMessageLog.h
src/Core/PpMessageLog.cpp
src/Core/PpStatsLog.h
src/Core/PpStatsLog.cpp
src/Core/AnalysisProfileLog.h
src/Core/AnalysisProfileLog.cpp
src/Core/pp_profiler.h
src/Core/pp_profiler.cpp
src/Core/pp_os_metrics.h
src/Core/pp_os_metrics.cpp
src/Core/pp_gpu_metrics.h
src/Core/pp_gpu_metrics.cpp
)
# ── Application icon ─────────────────────────────────────────────────────────
if(APPLE)
set(MACOSX_BUNDLE_ICON_FILE PinPointStudio)
set_source_files_properties(
"${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/icons/PinPointStudio.icns"
PROPERTIES MACOSX_PACKAGE_LOCATION "Resources"
)
endif()
set_target_properties(PinPointStudio PROPERTIES
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_ICON_FILE PinPointStudio
WIN32_EXECUTABLE TRUE
)
# Packaged-build marker (see the option() above) — compiled in only when the
# packaging scripts pass -DPINPOINT_INSTALLED=ON.
if(PINPOINT_INSTALLED)
target_compile_definitions(PinPointStudio PRIVATE PINPOINT_INSTALLED)
endif()
# ── Info.plist substitution variables (macOS) ────────────────────────────────
# Single source of truth is src/Core/version.h (parsed above). These feed the
# @VAR@ placeholders in Info.plist.in so the bundle version, the Sparkle compare
# key, and the appcast can never drift (macOS design §7).
# • CFBundleShortVersionString = display version (e.g. 0.1-alpha3)
# • CFBundleVersion = monotonic build int (Sparkle's compare key)
set(PP_DISPLAY_VERSION "${_pp_ver_major}.${_pp_ver_minor}${_pp_ver_postfix}")
set(PP_VERSION_BUILD "${_pp_ver_build}")
# Sparkle feed + pinned EdDSA public key (macOS design §4.1, §6). The feed URL is
# static (GitHub latest-release redirect); the key is read from the repo's pinned
# .pub so the generated Info.plist carries it as SUPublicEDKey. PLACEHOLDER until
# the offline key is generated — the macOS updater (Stage 2) refuses to arm while
# it is the placeholder, exactly as the Windows/Linux gates do.
#
# ONE FEED PER ARCHITECTURE. x86_64 and arm64 ship different ONNX Runtime major
# versions and different engines (arm64: ORT 1.26 + CoreML + ORT-GenAI; x86_64: ORT
# 1.20.1, CPU, no local LLM), so a single feed would offer each arch the other's DMG.
# Sparkle installs IN PLACE, so that is not a failed download — it is a working install
# replaced by one that cannot launch. Design §4.1: each architecture's binary only ever
# sees its own feed.
#
# Branch on CMAKE_SYSTEM_PROCESSOR — the SAME predicate the ORT package and the CoreML
# define already use above, so there is exactly one arch test in this file with one
# meaning. Note this deliberately does NOT set CMAKE_OSX_ARCHITECTURES: that would not
# move CMAKE_SYSTEM_PROCESSOR, so a cross-build would silently desync the ORT package,
# the CoreML define and this feed URL from the slice actually emitted. Host-native
# builds only.
#
# !! THE x86_64 URL IS FROZEN !! It is baked into the Info.plist of every Intel
# install already in the field, and Sparkle re-reads it from the INSTALLED bundle.
# Renaming it — even for symmetry with the arm64 name — permanently orphans every one
# of them: they would poll a URL that 404s and silently never update again. Add feeds,
# never rename this one.
if(APPLE AND CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
set(PP_SU_FEED_URL "https://github.com/PinPoint-Golf/PinPointStudio/releases/latest/download/appcast-mac-arm64.xml")
else()
set(PP_SU_FEED_URL "https://github.com/PinPoint-Golf/PinPointStudio/releases/latest/download/appcast-mac.xml")
endif()
if(APPLE)
message(STATUS "Sparkle feed (${CMAKE_SYSTEM_PROCESSOR}): ${PP_SU_FEED_URL}")
endif()