PEBL 2.2
Psychology Experiment Building Language - Cross-platform psychological experiment development system
PEBLLauncher.cpp
Go to the documentation of this file.
1// PEBLLauncher.cpp - Main entry point for PEBL Launcher
2// Copyright (c) 2026 Shane T. Mueller
3// Licensed under GPL
4//
5// =============================================================================
6// PORTABLE/STANDALONE MODE DETECTION
7// =============================================================================
8//
9// The launcher supports two operating modes:
10//
11// 1. INSTALLED MODE (default)
12// - Workspace is created in Documents/pebl-exp.2.4/
13// - First run triggers resource copying dialog (demos, tutorials, docs)
14// - Standard installation via installer or package manager
15//
16// 2. PORTABLE/STANDALONE MODE
17// - Workspace is the current working directory
18// - No first-run dialog (resources already in place)
19// - Designed for USB drives or self-contained distributions
20//
21// PORTABLE MODE DETECTION (in priority order):
22//
23// 1. STANDALONE.txt marker file
24// - Check for ./STANDALONE.txt, ../STANDALONE.txt, or ../../STANDALONE.txt
25// - Most explicit and reliable trigger
26// - Batch file can create this before launching
27//
28// 2. PORTABLE.txt marker file
29// - Check for ./PORTABLE.txt, ../PORTABLE.txt, or ../../PORTABLE.txt
30// - Legacy support
31//
32// 3. PEBL_PORTABLE environment variable
33// - Set PEBL_PORTABLE=1 before launching
34// - Useful for batch files
35//
36// NOTE: No automatic detection based on directory structure. Only explicit
37// marker files or environment variable will trigger portable mode.
38//
39// RECOMMENDED STANDALONE DIRECTORY STRUCTURE:
40//
41// /MyPEBLDistribution/
42// runme.bat <- Batch file to launch (sets CWD, runs launcher)
43// STANDALONE.txt <- Marker file (REQUIRED for portable mode)
44// PEBL/
45// bin/
46// pebl-launcher.exe
47// pebl2.exe
48// *.dll
49// battery/
50// pebl-lib/
51// media/
52// my_studies/ <- User's studies (in CWD, not inside PEBL/)
53// snapshots/
54//
55// EXAMPLE runme.bat:
56//
57// @echo off
58// cd /d "%~dp0"
59// REM Optional: create marker file for explicit detection
60// echo. > STANDALONE.txt
61// PEBL\bin\pebl-launcher.exe
62// REM Optional: clean up marker file
63// del STANDALONE.txt
64//
65// This ensures:
66// - Running via batch file -> portable mode (marker file created)
67// - Double-clicking exe directly -> installed mode (no marker file present)
68//
69// See WorkspaceManager::IsPortableMode() for implementation details.
70// =============================================================================
71
72#include "imgui.h"
73#include "backends/imgui_impl_sdl2.h"
74#include "backends/imgui_impl_sdlrenderer2.h"
75#include <SDL2/SDL.h>
76#include <stdio.h>
77#include <string>
78#include <filesystem>
79
80namespace fs = std::filesystem;
81
82#include "LauncherUI.h"
83#include "LauncherConfig.h"
84
85int main(int, char**)
86{
87 // Initialize SDL
88 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0)
89 {
90 printf("Error: %s\n", SDL_GetError());
91 return -1;
92 }
93
94 // Load configuration first (need window size)
95 LauncherConfig config;
96 config.LoadConfig();
97
98 // Setup window with HiDPI support
99 SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
100 SDL_Window* window = SDL_CreateWindow(
101 "PEBL Experiment Launcher",
102 SDL_WINDOWPOS_CENTERED,
103 SDL_WINDOWPOS_CENTERED,
104 config.GetWindowWidth(),
105 config.GetWindowHeight(),
106 window_flags
107 );
108
109 if (window == nullptr)
110 {
111 printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError());
112 return -1;
113 }
114
115 // Create renderer with VSync
116 SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
117 if (renderer == nullptr)
118 {
119 printf("Error creating SDL_Renderer: %s\n", SDL_GetError());
120 return -1;
121 }
122
123 // Setup Dear ImGui context
124 IMGUI_CHECKVERSION();
125 ImGui::CreateContext();
126 ImGuiIO& io = ImGui::GetIO(); (void)io;
127 io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
128
129 // Set imgui.ini path to settings directory (must be static to persist)
130 static std::string imguiIniPath;
131 {
132 fs::path settingsDir = fs::path(config.GetWorkspacePath()) / "settings";
133 try {
134 fs::create_directories(settingsDir);
135 } catch (...) {}
136 imguiIniPath = (settingsDir / "imgui.ini").string();
137 io.IniFilename = imguiIniPath.c_str();
138 }
139
140 // Set font size from configuration
141 ImFontConfig fontConfig;
142 fontConfig.SizePixels = config.GetFontSize();
143 io.Fonts->AddFontDefault(&fontConfig);
144
145 // Setup Dear ImGui style
146 ImGui::StyleColorsDark();
147 // ImGui::StyleColorsLight();
148
149 // Setup Platform/Renderer backends
150 ImGui_ImplSDL2_InitForSDLRenderer(window, renderer);
151 ImGui_ImplSDLRenderer2_Init(renderer);
152
153 // Initialize launcher UI
154 LauncherUI launcherUI(&config, renderer);
155
156 // Main loop
157 bool done = false;
158 while (!done)
159 {
160 // Poll and handle events
161 SDL_Event event;
162 while (SDL_PollEvent(&event))
163 {
164 ImGui_ImplSDL2_ProcessEvent(&event);
165 if (event.type == SDL_QUIT)
166 done = true;
167 if (event.type == SDL_WINDOWEVENT &&
168 event.window.event == SDL_WINDOWEVENT_CLOSE &&
169 event.window.windowID == SDL_GetWindowID(window))
170 done = true;
171 }
172
173 // Start the Dear ImGui frame
174 ImGui_ImplSDLRenderer2_NewFrame();
175 ImGui_ImplSDL2_NewFrame();
176 ImGui::NewFrame();
177
178 // Render launcher UI
179 launcherUI.Render(&done);
180
181 // Rendering
182 ImGui::Render();
183 SDL_RenderSetScale(renderer, io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
184 SDL_SetRenderDrawColor(renderer, 45, 45, 48, 255);
185 SDL_RenderClear(renderer);
186 ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), renderer);
187 SDL_RenderPresent(renderer);
188 }
189
190 // Save window size to configuration before exit
191 int windowWidth, windowHeight;
192 SDL_GetWindowSize(window, &windowWidth, &windowHeight);
193 config.SetWindowWidth(windowWidth);
194 config.SetWindowHeight(windowHeight);
195 config.SaveConfig();
196
197 // Cleanup
198 ImGui_ImplSDLRenderer2_Shutdown();
199 ImGui_ImplSDL2_Shutdown();
200 ImGui::DestroyContext();
201
202 SDL_DestroyRenderer(renderer);
203 SDL_DestroyWindow(window);
204 SDL_Quit();
205
206 return 0;
207}
int main(int, char **)
void SetWindowWidth(int width)
int GetFontSize() const
int GetWindowWidth() const
void SetWindowHeight(int height)
std::string GetWorkspacePath() const
int GetWindowHeight() const
void Render(bool *p_open)