Skip to content

Commit be55964

Browse files
authored
Add in-app log console; stop launching a separate terminal window (#4)
* Add in-app log console; stop launching a separate terminal window Launching the app from a GUI no longer opens a separate console window (CMD on Windows, Terminal on macOS). All logging now appears in a docked "Log" console along the bottom of the app window, and still prints to the terminal when the app is launched from one. Logging - New LogConsole (include/Log.h, src/Log.cpp): tees std::cout/std::cerr so every line goes to both the original stream (terminal) and a bounded, thread-safe ring buffer rendered as the bottom panel. No changes needed at the ~54 existing log call sites. The render path snapshots under the lock and draws unlocked, so a worker thread logging never blocks the GUI and there is no re-entrancy path back into the lock. No separate console on launch - Windows: built as a GUI-subsystem executable (WIN32_EXECUTABLE + /ENTRY:mainCRTStartup to keep int main). AttachConsole(ATTACH_PARENT_PROCESS) reattaches stdio when started from a terminal so logs still print there. - macOS: CMake now builds an ir-tracking-app.app bundle (Info.plist.in + icon), so double-clicking in Finder does not spawn Terminal. release.yml's macOS packaging consumes the CMake bundle (dylibbundler + PlistBuddy version stamp) instead of hand-assembling it. macOS data directory - A Finder-launched .app has working directory "/", which broke the relative "Tools" folder and CSV paths. On macOS these now live under ~/Library/Application Support/IR Tracking App/ regardless of launch method; Windows and Linux keep their current working-directory behavior. The data directory is logged at startup. Layout - Default window grown to 1060x900; IR/Depth monitors lifted above the log console and clamped so they never render off the top on a short window. Tracker fixes (from code review) - UdpThreadFunction: GetToolTransform() returns 8 floats, so copying begin()+3..end() wrote 5 floats into data.quaternion[4] (out-of-bounds). Copy exactly the 4 quaternion components. - MIN_SPHERES 4 -> 3 to match the tracker, which supports 3-sphere tools in both AddTool and CalibrateTool. The old value rejected valid 3-sphere manual/ROM definitions and 3-sphere calibration results. * Update ViewerWindow.cpp
1 parent 1f517dc commit be55964

9 files changed

Lines changed: 551 additions & 47 deletions

File tree

.github/workflows/release.yml

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -215,14 +215,16 @@ jobs:
215215
run: |
216216
set -euxo pipefail
217217
APP_NAME="IR Tracking App"
218+
219+
# CMake now emits the .app bundle directly (Info.plist + icon already
220+
# baked in). Self-contain it with dylibbundler and stamp the version.
221+
BUILT_APP="build/ir-tracking-app.app"
222+
test -d "$BUILT_APP" || { echo "Expected $BUILT_APP from the CMake build"; exit 1; }
223+
218224
APP_DIR="$RUNNER_TEMP/$APP_NAME.app"
219225
rm -rf "$APP_DIR"
220-
mkdir -p "$APP_DIR/Contents/MacOS" \
221-
"$APP_DIR/Contents/Resources" \
222-
"$APP_DIR/Contents/Frameworks"
223-
224-
cp build/ir-tracking-app "$APP_DIR/Contents/MacOS/"
225-
cp resources/app_icon.icns "$APP_DIR/Contents/Resources/app_icon.icns"
226+
cp -R "$BUILT_APP" "$APP_DIR"
227+
mkdir -p "$APP_DIR/Contents/Frameworks"
226228
227229
# Bundle dylibs into Contents/Frameworks/, with install names rewritten
228230
# to @executable_path/../Frameworks/ so the .app is self-contained.
@@ -232,25 +234,10 @@ jobs:
232234
-p "@executable_path/../Frameworks/" \
233235
|| true
234236
235-
cat > "$APP_DIR/Contents/Info.plist" <<EOF
236-
<?xml version="1.0" encoding="UTF-8"?>
237-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
238-
<plist version="1.0">
239-
<dict>
240-
<key>CFBundleExecutable</key><string>ir-tracking-app</string>
241-
<key>CFBundleIdentifier</key><string>com.medivis.ir-tracking-app</string>
242-
<key>CFBundleName</key><string>$APP_NAME</string>
243-
<key>CFBundleDisplayName</key><string>$APP_NAME</string>
244-
<key>CFBundleVersion</key><string>$VERSION</string>
245-
<key>CFBundleShortVersionString</key><string>$VERSION</string>
246-
<key>CFBundlePackageType</key><string>APPL</string>
247-
<key>CFBundleIconFile</key><string>app_icon.icns</string>
248-
<key>LSMinimumSystemVersion</key><string>11.0</string>
249-
<key>NSHighResolutionCapable</key><true/>
250-
<key>NSCameraUsageDescription</key><string>Used to display the IR camera feed for tool tracking.</string>
251-
</dict>
252-
</plist>
253-
EOF
237+
# Stamp the release version into the CMake-generated Info.plist.
238+
PLIST="$APP_DIR/Contents/Info.plist"
239+
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $VERSION" "$PLIST"
240+
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $VERSION" "$PLIST"
254241
255242
# Compose the DMG. UDZO = zlib-compressed read-only.
256243
DMG_NAME="ir-tracking-app-${VERSION}-${{ matrix.label }}.dmg"

CMakeLists.txt

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ target_link_libraries(IRToolTracking PUBLIC Eigen3 ${realsense2_LIBRARY} opencv_
8585

8686

8787

88-
#set(MACOS_ICON "resources/app_icon.icns")
88+
set(MACOS_ICON_NAME "app_icon.icns")
89+
set(MACOS_ICON "${CMAKE_CURRENT_SOURCE_DIR}/resources/${MACOS_ICON_NAME}")
8990

9091

9192
find_package(OpenGL REQUIRED)
@@ -95,25 +96,56 @@ include_directories(
9596
)
9697

9798
set(VIEWER_SOURCE_FILES
98-
#${MACOS_ICON}
9999
win.rc
100100
include/ROMParser.h
101101
include/ViewerWindow.h
102+
include/Log.h
102103
src/ViewerWindow.cpp
103104
src/viewer_main.cpp
105+
src/Log.cpp
104106
)
105107

108+
# On macOS the app icon is compiled into the .app bundle's Resources folder.
109+
if(APPLE)
110+
list(APPEND VIEWER_SOURCE_FILES ${MACOS_ICON})
111+
endif()
112+
106113
# Add the main application
107114
add_executable(ir-tracking-app ${VIEWER_SOURCE_FILES})
108115

109-
# if(APPLE)
110-
# set_target_properties(ir-tracking-app PROPERTIES
111-
# MACOSX_BUNDLE TRUE
112-
# MACOSX_BUNDLE_ICON_FILE app_icon.icns
113-
# RESOURCE "${MACOS_ICON}"
114-
# )
115-
# set_source_files_properties(${MACOS_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
116-
# endif()
116+
# -----------------------------------------------------------------------------
117+
# Windowing / launch behavior: don't pop a separate console (CMD / Terminal)
118+
# window when the app is launched from a GUI.
119+
# -----------------------------------------------------------------------------
120+
if(WIN32)
121+
# Build as a GUI-subsystem (/SUBSYSTEM:WINDOWS) executable so launching from
122+
# Explorer or a Start-menu shortcut no longer spawns a CMD window. Keep the
123+
# standard int main(...) entry point instead of WinMain by pointing the
124+
# linker at the console CRT startup, which calls main(). The app still
125+
# AttachConsole()s to a parent terminal at runtime so `ir-tracking-app.exe`
126+
# launched from cmd/PowerShell keeps printing logs.
127+
set_target_properties(ir-tracking-app PROPERTIES WIN32_EXECUTABLE TRUE)
128+
if(MSVC)
129+
target_link_options(ir-tracking-app PRIVATE /ENTRY:mainCRTStartup)
130+
endif()
131+
endif()
132+
133+
if(APPLE)
134+
# Build a .app bundle so double-clicking it in Finder does not open a
135+
# Terminal window (a bare Unix executable would). Logs still go to the
136+
# terminal when the inner binary is run from a shell, and always to the
137+
# in-app log console.
138+
set_source_files_properties(${MACOS_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
139+
set_target_properties(ir-tracking-app PROPERTIES
140+
MACOSX_BUNDLE TRUE
141+
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/resources/Info.plist.in"
142+
MACOSX_BUNDLE_BUNDLE_NAME "IR Tracking App"
143+
MACOSX_BUNDLE_GUI_IDENTIFIER "com.medivis.ir-tracking-app"
144+
MACOSX_BUNDLE_ICON_FILE "${MACOS_ICON_NAME}"
145+
MACOSX_BUNDLE_BUNDLE_VERSION "0.0.0"
146+
MACOSX_BUNDLE_SHORT_VERSION_STRING "0.0.0"
147+
)
148+
endif()
117149

118150
include_directories( ${IMGUI_DIR} )
119151
include_directories( ${IMGUI_DIR}/backends)

README.md

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,36 @@ Note: I have only tested this build process with Visual Studio 2022.
4545

4646
## Running
4747

48+
The application no longer opens a separate console window when launched from a
49+
GUI (a CMD window on Windows or a Terminal window on macOS). Instead, all log
50+
output is shown in a **Log console docked along the bottom of the app window**.
51+
When you launch the app from a terminal, the same logs are also printed to that
52+
terminal.
53+
4854
### For Linux and Windows:
4955
```bash
5056
./ir-tracking-app
5157
```
58+
On Windows the app is now a GUI-subsystem executable, so double-clicking it (or
59+
its Start-menu shortcut) does not spawn a CMD window. Run it from `cmd` /
60+
PowerShell if you want the logs in your terminal as well.
61+
5262
### For MacOS
53-
Due to certain permissions and security features in MacOS, you might need to run the application with elevated privileges.
63+
The build now produces an `ir-tracking-app.app` bundle, so you can launch it
64+
from Finder without a Terminal window appearing:
65+
```bash
66+
open build/ir-tracking-app.app
67+
```
68+
To see the logs in your terminal as well (and to run with elevated privileges
69+
for camera/USB access), run the binary inside the bundle directly:
5470
```bash
55-
sudo ./ir-tracking-app
71+
sudo ./build/ir-tracking-app.app/Contents/MacOS/ir-tracking-app
5672
```
73+
> Note: on macOS, tool definitions (`Tools/`) and recorded CSV files are stored
74+
> under `~/Library/Application Support/IR Tracking App/` regardless of how the
75+
> app is launched (the data directory is also printed to the log console at
76+
> startup). On Windows and Linux these are read/written relative to the current
77+
> working directory, as before.
5778
## RealSense Camera Modification: Adding a Light Diffuser
5879

5980
The laser projector of the RealSense camera emits a sharp, focused IR dot pattern. While this is generally beneficial for depth sensing, it is not ideal for doing thresholding on IR stream to find retroreflective surfaces.

include/Log.h

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#pragma once
2+
3+
#ifndef LOG_CONSOLE_H
4+
#define LOG_CONSOLE_H
5+
6+
#include <deque>
7+
#include <memory>
8+
#include <mutex>
9+
#include <streambuf>
10+
#include <string>
11+
12+
// In-app log console.
13+
//
14+
// Install() tees std::cout / std::cerr so every line written through them is
15+
// BOTH forwarded to the original stream (so a launch from a terminal keeps
16+
// printing logs as before) AND captured into a bounded ring buffer that the GUI
17+
// renders as a docked panel at the bottom of the window. This means the 50-odd
18+
// existing std::cout/std::cerr call sites need no changes — they light up the
19+
// in-app console automatically.
20+
//
21+
// Thread-safe: AddLine() may be called concurrently from worker threads (the
22+
// UDP / CSV / tracking threads all log), while DrawContents() reads on the GUI
23+
// thread.
24+
class LogConsole
25+
{
26+
public:
27+
enum class Level
28+
{
29+
Info, // captured from std::cout
30+
Error // captured from std::cerr
31+
};
32+
33+
struct Entry
34+
{
35+
Level level;
36+
std::string text; // includes a leading "HH:MM:SS " timestamp
37+
};
38+
39+
static LogConsole& Get();
40+
41+
// Redirect std::cout / std::cerr through the tee. Idempotent.
42+
void Install();
43+
// Restore the original stream buffers. Idempotent; safe to call at shutdown.
44+
void Restore();
45+
46+
// Append a fully-formed log line (no trailing newline). Thread-safe.
47+
void AddLine(Level level, const std::string& text);
48+
49+
void Clear();
50+
51+
// Render the console body (toolbar + scrolling region) into the *current*
52+
// ImGui window. The caller owns the window (position / size / Begin/End).
53+
void DrawContents();
54+
55+
LogConsole(const LogConsole&) = delete;
56+
LogConsole& operator=(const LogConsole&) = delete;
57+
58+
private:
59+
LogConsole() = default;
60+
~LogConsole();
61+
62+
std::mutex mutex_;
63+
std::deque<Entry> entries_;
64+
std::size_t maxEntries_ = 2000;
65+
66+
bool installed_ = false;
67+
bool autoScroll_ = true;
68+
69+
// Saved originals so Restore() can put them back.
70+
std::streambuf* originalCout_ = nullptr;
71+
std::streambuf* originalCerr_ = nullptr;
72+
// Tee buffers that wrap the originals; owned here.
73+
std::unique_ptr<std::streambuf> coutTee_;
74+
std::unique_ptr<std::streambuf> cerrTee_;
75+
};
76+
77+
#endif // LOG_CONSOLE_H

include/ViewerWindow.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class ViewerWindow {
6161

6262
// Validation bounds.
6363
static constexpr int MAX_TOOLS = 32; // upper bound for "Number of Tools"
64-
static constexpr int MIN_SPHERES = 4; // lower bound for spheres per tool
64+
static constexpr int MIN_SPHERES = 3; // lower bound for spheres per tool (matches the tracker's 3-sphere minimum)
6565
static constexpr int MAX_SPHERES = 20; // safety cap for manual entry / ROM
6666
static constexpr int MAX_CALIB_SPHERES = 6; // a calibration must not exceed this
6767

resources/Info.plist.in

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>CFBundleExecutable</key>
6+
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
7+
<key>CFBundleIdentifier</key>
8+
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
9+
<key>CFBundleName</key>
10+
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
11+
<key>CFBundleDisplayName</key>
12+
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
13+
<key>CFBundleIconFile</key>
14+
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
15+
<key>CFBundleVersion</key>
16+
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
17+
<key>CFBundleShortVersionString</key>
18+
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
19+
<key>CFBundlePackageType</key>
20+
<string>APPL</string>
21+
<key>LSMinimumSystemVersion</key>
22+
<string>11.0</string>
23+
<key>NSHighResolutionCapable</key>
24+
<true/>
25+
<key>NSCameraUsageDescription</key>
26+
<string>Used to display the IR camera feed for tool tracking.</string>
27+
</dict>
28+
</plist>

0 commit comments

Comments
 (0)