main :: () { using platform; using ImGui; // #Startup: Program Startup app.args = get_command_line_arguments(); print_version_and_exit := array_find(app.args, "-version"); if print_version_and_exit { print("Version: %-%-%, built on %", VERSION, GIT_BRANCH, GIT_HASH, BUILD_DATE); return; } load_hardware_information(); load_platform_information(); args_combined := join(..app.args, " "); app.run_tests = array_find(app.args, "run_tests"); app.headless = array_find(app.args, "-headless") || NO_CRASH_DIALOG; runtime_support_headless = app.headless; app.allow_multiple_instances = array_find(app.args, "-allow-multiple-instances"); defer report_memory_leaks(); channel_key := app_shared_memory_channel_key(); defer free(channel_key); // 1. Check file lock on POSIX machines: app_instance_running : bool; #if USING_SDL { lock_path := app_instance_lock_path(); lock_fd: s32 = -1; if lock_path { // 600 in base 8 is 0x180. O_NOFOLLOW, so a planted symlink is not truncated below. lock_fd = POSIX.open(temp_c_string(lock_path), POSIX.O_CREAT|POSIX.O_RDWR|POSIX.O_CLOEXEC|POSIX.O_NOFOLLOW, 0x180); if lock_fd < 0 log_error("Failed to open the instance lock %: errno %", lock_path, POSIX.errno()); } else { log_error("No per-user directory is available for the instance lock."); } defer if lock_fd >= 0 POSIX.close(lock_fd); lock_taken := false; if lock_fd >= 0 { // Set up the flock structure for an exclusive write lock: fl: POSIX.flock; fl.l_type = POSIX.F_WRLCK; // exclusive write lock fl.l_whence = POSIX.SEEK_SET; fl.l_start = 0; // lock from start of file fl.l_len = 0; // lock until EOF // Try to lock the file. F_SETLK returns -1 immediately if locked. if POSIX.fcntl(lock_fd, POSIX.F_SETLK, *fl) == -1 { if (POSIX.errno() == POSIX.EACCES || POSIX.errno() == POSIX.EAGAIN) { log("app_instance_running already!"); app_instance_running = true; } else { log_error("fcntl on the instance lock failed: errno %", POSIX.errno()); } } else { lock_taken = true; } } if !lock_taken && !app_instance_running { log_error("Could not take the instance lock; running without single-instance support."); app.allow_multiple_instances = true; } if lock_taken { // remove stale reference if needed: POSIX.shm_unlink(temp_c_string(channel_key)); // Truncate the file and write PID: if POSIX.ftruncate(lock_fd, 0) == 0 { pid := POSIX.getpid(); pid_str := tprint("%\n", pid); POSIX.write(lock_fd, pid_str.data, cast(u64)pid_str.count); } } } if !app.allow_multiple_instances { // #Startup:#shared_memory_channel:bringToFront // One primitive decides which instance is master: the lock on POSIX, and the creation of the // mapping on Windows. Whoever loses only ever connects, retrying while the winner may still // be setting its channel up, and then exits. another_instance_owns_channel := false; #if USING_SDL another_instance_owns_channel = app_instance_running; if !another_instance_owns_channel && app_bring_running_instance_to_front(channel_key, attempts=1) return; if !another_instance_owns_channel { read_channel_success, read_channel, already_exists := Shared_Channel.reader_create_channel(channel_key, APP_SHARED_MEMORY_CHANNEL_BYTES); if read_channel_success { app.read_channel = read_channel; app.is_master = true; } else if already_exists { another_instance_owns_channel = true; } else { // Not fatal: this instance runs without the channel, so later launches will not bring it forward. log_error("Failed to create the shared memory channel; continuing without single-instance support."); } } if another_instance_owns_channel { if !app_bring_running_instance_to_front(channel_key, attempts=20) { log_error("Another instance is running, but its shared memory channel could not be reached (it may be running as administrator); exiting."); } return; } } defer if app.is_master Shared_Channel.reader_release_channel(*app.read_channel, channel_key); crash_report_folder_name = APP_FOLDER_NAME; init_logger(); defer close_logger(); log(" --- /STARTUP INFO\\ --- "); log(" - APP_NAME: %", APP_NAME); log(" - VERSION: %", VERSION); log(" - DEBUG: %", DEBUG); log(" - BUILD_DATE: %", BUILD_DATE); log(" - GIT_BRANCH: %", GIT_BRANCH); log(" - GIT_HASH: %", GIT_HASH); log(" - COMPILE_ARGS: %", COMPILE_ARGS); log(" - COMMAND_LINE_ARGUMENTS: %", args_combined); log(" --- \\STARTUP INFO/ --- "); app.startup_time = current_time_monotonic(); resize(*perf.frame_times, PERF_FRAME_TIME_HISTORY,, context.default_allocator); timing_set_tick_frequency(); if app.run_tests { #if COMPILE_TESTS { // Headless, a failed suite is counted and reported in the exit code rather than asserted on. testing_init(assert_on_fail = DEBUG && !app.headless, log_successful_tests=DEBUG); run_all_tests(); } else { log_error("run_tests was passed, but this build was compiled without tests. Rebuild with the compile_tests argument to include them."); } } #if USING_WINDOWS { windows_check_if_we_are_running_as_administrator(); if app.should_exit_early then return; windows_enable_large_pages_if_possible(); } #if OS == .MACOS { init_mac_app(); } app.machine_name = copy_string(get_machinename()); app.username = copy_string(get_username()); log("Current PC name: %, username: %", app.machine_name, app.username); settings_write_back := app_load_user_settings_file(); logger_set_event_log_capacity(app_user_settings.event_log_capacity); // #Startup:CreateWindow #if USING_SDL { // Keep freetype from being removed by the compiler FT_Error_String(0); // Setup window and gpu using the SDL3_GPU3 backend. // This is based on the 'example_sdl3_sdlgpu3' Dear ImGui example, which you can find on their repo. success := SDL_Init(SDL_INIT_VIDEO); #if OS == .MACOS { // By default Cmd+W closes the application if the last window exits, so we need to tell SDL to NOT do that. SDL_SetHint(SDL_HINT_QUIT_ON_LAST_WINDOW_CLOSE, "0"); } ok_or_exit(success, "failed to init sdl3 with error = %", consume_sdl_error()); display := sdl_largest_display(); app.win_scale = SDL_GetDisplayContentScale(display); if app.win_scale <= 0 app.win_scale = 1.0; // 0 when SDL cannot tell, as on some Linux setups mode := SDL_GetCurrentDisplayMode(display); w, h := sdl_default_window_size(mode); window_flags : SDL_WindowFlags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN | SDL_WINDOW_HIGH_PIXEL_DENSITY; platform.window = SDL_CreateWindow(APP_WINDOW_TITLE, w, h, window_flags); ok_or_exit(platform.window != null, "failed to create window with error = %", consume_sdl_error()); centered := SDL_WINDOWPOS_CENTERED_DISPLAY(display); SDL_SetWindowPosition(platform.window, xx centered, xx centered); sdl_set_app_icon(); #if OS == .MACOS { SDL_AddEventWatch(sdl_live_resize_watch, null); } SDL_ShowWindow(platform.window); } // #if USING_SDL #if USING_WINDOWS { ImGui.ImplWin32_EnableDpiAwareness(); app.win_scale = ImGui.ImplWin32_GetDpiScaleForMonitor(MonitorFromPoint({0,0}, MONITOR_DEFAULTTOPRIMARY)); lpszClassName :: #run tprint("%WindowClass", APP_NAME); wc: WNDCLASSEXW; wc.cbSize = size_of(WNDCLASSEXW); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = xx WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = null; wc.hIcon = LoadImageW(null, utf8_to_wide(get_app_tray_icon_path(),,temp), IMAGE_ICON, 0, 0, LR_LOADFROMFILE); wc.hCursor = LoadCursorW(null, IDC_ARROW); wc.hbrBackground = null; wc.lpszMenuName = null; wc.lpszClassName = utf8_to_wide(lpszClassName,, context.default_allocator); if RegisterClassExW(*wc) == 0 { log("RegisterClassExW failed\n"); return; } defer { UnregisterClassW(wc.lpszClassName, wc.hInstance); free(wc.lpszClassName,, context.default_allocator); } // Get primary display height and width window_x, window_y, win_width, win_height := platform_get_centered_window_dimensions(open_on_biggest=true); log("Window Location: x: %, y: %, size: %x% px", window_x, window_y, win_width, win_height); hwnd = CreateWindowExW(WS_EX_APPWINDOW, utf8_to_wide(lpszClassName,,temp), utf8_to_wide(APP_WINDOW_TITLE,,temp), WS_OVERLAPPEDWINDOW, window_x, window_y, win_width, win_height, null, null, null, null); if !hwnd { print("CreateWindowExW failed\n"); return; } defer DestroyWindow(hwnd); } // #if USING_WINDOWS // #Startup:Tray #Startup:GlobalHotkeys #if USING_SDL { posix_install_exit_signal_handlers(); #if OS == .LINUX { linux_platform_setup(); } } #if USING_WINDOWS { // :Win32_Initialize_Extended Win32_Set_Tray_Icon(); Win32_Hide_Tray_Icon(); defer Win32_Hide_Tray_Icon(); // exiting from the tray would otherwise leave the icon behind } // [global_hotkeys] was read with the settings, before there was a window to register them for. global_hotkeys_apply(); defer platform_unregister_global_hotkeys(); // #Startup:Graphics #if USING_SDL { new_device := SDL_CreateGPUDevice(SDL_GPU_SHADERFORMAT_SPIRV | SDL_GPU_SHADERFORMAT_DXIL | SDL_GPU_SHADERFORMAT_METALLIB, DEBUG, null); ok_or_exit(new_device != null, "failed to create gpu device with error = %", consume_sdl_error()); platform.gpu_device = new_device; success = SDL_ClaimWindowForGPUDevice(gpu_device, platform.window); ok_or_exit(success, "failed to claim window with title '%' for gpu device with error = %", to_string(SDL_GetWindowTitle(platform.window)), consume_sdl_error()); success = SDL_SetGPUSwapchainParameters(gpu_device, platform.window, .SDR, .VSYNC); ok_or_exit(success, "failed to set swapchain parameters with error = %", consume_sdl_error()); perf.renderer_name = copy_string(to_string(SDL_GetGPUDeviceDriver(gpu_device))); log("SDL renderer_name: %", perf.renderer_name); } #if USING_WINDOWS { // #Startup:Graphics See #WarpFirstStartup in graphics/DX11.jai. if !D3D11_InitDevice(hwnd, .WARP) { log_error("Could not create a D3D11 device of any kind. Exiting."); return; } perf.renderer_name = "direct3d11"; perf.started_on_warp = (g_driver_type == .WARP); log("[D3D11] Renderer up on '%' (%): device % ms, swap chain % ms.", perf.adapter_name, g_driver_type, FF(perf.startup_device_ms, 1), FF(perf.startup_swap_chain_ms, 1)); // Started hardware device thread after WARP initialied, as in the reference implementation: https://gist.github.com/thomasklemenc/2d4d43001a977a3dafe4de73efc348da // Running both simultaneously causes contention with the loader lock and DXGI's internal locks, and the resulting init time is 100ms+ if perf.started_on_warp then d3d11_start_hardware_device_thread(); defer d3d11_finish_hardware_device_thread(); ShowWindow(hwnd, SW_SHOWDEFAULT); UpdateWindow(hwnd); // Set dark titlebar: dark_mode: s64 = 1; DwmSetWindowAttribute(hwnd, .USE_IMMERSIVE_DARK_MODE, *dark_mode, size_of(s64)); // A hacky workaround to display the dark title bar properly. Nothing else we've tried worked (credit: focus-editor) ShowWindow(hwnd, SW_HIDE); ShowWindow(hwnd, SW_SHOW); } perf.backend = graphics_backend_from_string(perf.renderer_name); // #Startup:ImGui Graphics Backend app_imgui_init(); #if USING_SDL { io := GetIO(); // Enable freetype rendering io.Fonts.SetFontLoader(io.Fonts, FreeType.GetFontLoader()); // Setup imgui sdl3_gpu3 backend success = ImplSDL3_InitForSDLGPU(platform.window); ok_or_exit(success, "failed to InitForSDLGPU with error = %", consume_sdl_error()); init_info := ImplSDLGPU3_InitInfo.{ Device = gpu_device, ColorTargetFormat = SDL_GetGPUSwapchainTextureFormat(gpu_device, platform.window), MSAASamples = .SDL_GPU_SAMPLECOUNT_1, SwapchainComposition = .SDL_GPU_SWAPCHAINCOMPOSITION_SDR, PresentMode = .SDL_GPU_PRESENTMODE_VSYNC, }; success = ImplSDLGPU3_Init(*init_info); ok_or_exit(success, "failed to init SDLGPU3 with error = %", consume_sdl_error()); } #if USING_WINDOWS { ImGui.ImplWin32_Init(hwnd); ImGui.ImplDX11_Init( cast,no_check(*ImGui.ID3D11Device) g_pd3dDevice, cast,no_check(*ImGui.ID3D11DeviceContext) g_pd3dDeviceContext); } // #Startup:Font,Config,etc. app_imgui_load_fonts(); app_imgui_config_setup(); app_apply_font_settings(); { // #Startup:App app_theme_startup(); // registry + settings.config + apply if settings_write_back app_save_user_settings_file(); app_settings_watch_start(); app_ui_startup(); } #if USING_WINDOWS || OS == .MACOS platform.main_context = *context; // #MainLoop while !app.should_exit_early { defer reset_temporary_storage(); if !app_frame() break; } // ImGui otherwise writes the layout only on its periodic autosave, so recent changes were lost. ini_filename := ImGui.GetIO().IniFilename; if ini_filename ImGui.SaveIniSettingsToDisk(ini_filename); // By the time the loop above exits, app_exit_shutdown_poll (called from app_frame) has already // confirmed the background shutdown work finished. if app.exit_code exit(app.exit_code); // e.g. a headless test run with failures } // Returns false when the main loop should exit. app_frame :: (in_live_resize := false) -> bool { using platform; using ImGui; app.frame_time = current_time_monotonic(); #if COMPILE_TESTS testing_cleanup(); platform_update_memory_usage_metrics(); app.frame_count += 1; // #shared_memory_channel:bringToFront if app.is_master && app.read_channel_fatal_errors < 2 { message_success, fatal_error, message := Shared_Channel.reader_poll_for_message(*app.read_channel); if fatal_error { app.read_channel_fatal_errors += 1; if app.read_channel_fatal_errors == 1 { log_error("[IPC] The shared memory channel is corrupted; discarding its queue."); Shared_Channel.reader_reset_queue(*app.read_channel); } else { log_error("[IPC] The shared memory channel is corrupted again; no longer polling it. Later launches will not bring this window forward."); } } if message_success { assert(message.user_read_bytes > 0); log("[IPC] message received: message[%] %", message.user_read_bytes, []u8.{message.user_read_bytes, message.user_read_pointer}); command := cast(App_Shared_Memory_Tag)message.user_read_pointer[0]; if command == .Bring_To_Front { app.restore_window_this_frame = true; } Shared_Channel.reader_done_with_message(*message); } } // #frameStart #if USING_SDL posix_exit_signal_check(); // Once the background shutdown thread owns the settings watch (exit_shutdown.jai), the main // thread must leave it alone to avoid racing it. shutting_down := app_exit_shutdown_started(); if !shutting_down { app_settings_watch_frame(); } app_check_key_combinations(); app_ui_frame_start(); // On macOS a live-resize frame runs from an event watch inside SDL_PollEvent, so it must not poll. #if USING_SDL if !in_live_resize { #if OS == .MACOS { platform.polling_events = true; platform.live_resize_frame_drawn = false; } ev: SDL_Event; while(SDL_PollEvent(*ev)) { // Send events to imgui through the backend, then do our processing event_consumed := ImplSDL3_ProcessEvent(*ev); if ev.type.(SDL_EventType) == { case SDL_EventType.SDL_EVENT_WINDOW_MINIMIZED; app.minimize_to_tray_this_frame = true; case SDL_EventType.QUIT; app.should_exit = true; case SDL_EventType.WINDOW_CLOSE_REQUESTED; app.minimize_to_tray_this_frame = true; case; } } SDL_PumpEvents(); SDL_UpdateTrays(); #if OS == .MACOS { platform.polling_events = false; // A live-resize frame drew during the poll and released the temporary storage it rebuilt the // per-frame state (anything app_ui_frame_start builds) in, so this frame must not draw with it. if platform.live_resize_frame_drawn return true; } } // #if USING_SDL // A live-resize frame runs from WM_PAINT inside the pump, so it must neither pump nor swap devices. #if USING_WINDOWS { if !in_live_resize { // Also checks registered global hotkeys! The flags it sets wait for the next frame if this one ends here. if !Windows_Read_Messages() return true; // Between the message pump and NewFrame is the only safe place for these; see #WarpFirstStartup. d3d11_swap_to_hardware_if_ready(); d3d11_recover_lost_device_if_needed(); } win32_apply_dpi_change(); // also in a live-resize frame: dragging across monitors happens there } #if USING_SDL sdl_apply_pending_app_icon(); // a frame after the restore below asked for it if app.minimize_to_tray_this_frame { app.minimize_to_tray_this_frame = false; platform_minimize_to_tray(); } if app.restore_window_this_frame { app.restore_window_this_frame = false; app.last_window_restore_time = current_time_monotonic(); platform_restore_window(); } if app.should_exit { app_exit_shutdown_start(); if app_exit_shutdown_poll() return false; } // Whichever way the window was hidden or brought back (tray click, hotkey, a second launch). imgui_show_floating_windows(!app.minimized_to_tray); if app.minimized_to_tray != app.was_minimized_to_tray { app.was_minimized_to_tray = app.minimized_to_tray; imgui_drop_stale_input(); // The minimize hotkey's 0.15 s guard, for every way back. if !app.minimized_to_tray app.last_window_restore_time = current_time_monotonic(); } #if USING_WINDOWS { // While hidden in the tray, a lost device waits as any hidden frame does, not every 16 ms. if g_device_lost || !g_pSwapChain { if app.minimized_to_tray platform_wait_while_hidden(HIDDEN_WAIT_MS); else sleep_milliseconds(16); return true; } if g_SwapChainOccluded && IDXGISwapChain_Present(g_pSwapChain, 0, DXGI_PRESENT_TEST) == DXGI_STATUS_OCCLUDED { platform_wait_while_hidden(HIDDEN_WAIT_MS); return true; } g_SwapChainOccluded = false; D3D11_HandleWindowResize(); if d3d11_device_was_removed() || !g_mainRenderTargetView { if app.minimized_to_tray platform_wait_while_hidden(HIDDEN_WAIT_MS); else sleep_milliseconds(16); return true; } } // :InputDisabledOnMinimize: no #UI block, so no input handling, runs while hidden. if app.minimized_to_tray { platform_wait_while_hidden(HIDDEN_WAIT_MS); return true; } imgui_reset_layout_if_requested(); // #ImGui:NewFrame #if USING_SDL { ImplSDLGPU3_NewFrame(); ImplSDL3_NewFrame(); } #if USING_WINDOWS { ImGui.ImplDX11_NewFrame(); ImGui.ImplWin32_NewFrame(); } ImGui.NewFrame(); { // #UI Start ImGui.DockSpaceOverViewport(); app_debug_panel_ui(); app_frame_times_ui(); app_style_editor_ui(); app_logger_panel_ui(); app_main_window_ui(); // app/app_ui.jai: start here app_toast_ui(); app_exit_shutdown_overlay_ui(); // last, so it draws over everything else } // #UI End // #ImGui:Rendering ImGui.Render(); swapchain_wait: Apollo_Time; // left out of the recorded work time #if USING_SDL { draw_data := GetDrawData(); is_minimized := (draw_data.DisplaySize.x <= 0 || draw_data.DisplaySize.y <= 0); // Acquire a GPU command buffer and swapchain texture. With vsync these can block until the // display frees an image (Metal's nextDrawable), which is the vsync wait, not work. acquire_start := current_time_monotonic(); cmd_buffer := SDL_AcquireGPUCommandBuffer(gpu_device); swapchain_texture: *SDL_GPUTexture; success := SDL_AcquireGPUSwapchainTexture(cmd_buffer, platform.window, *swapchain_texture, null, null); swapchain_wait = current_time_monotonic() - acquire_start; if swapchain_texture && !is_minimized { ImplSDLGPU3_PrepareDrawData(draw_data, cmd_buffer); target_info := SDL_GPUColorTargetInfo.{ texture = swapchain_texture, clear_color = app.ui_global_clear_color, load_op = .CLEAR, store_op = .STORE, mip_level = 0, layer_or_depth_plane = 0, cycle = false, }; render_pass := SDL_BeginGPURenderPass(cmd_buffer, *target_info, 1, null); ImplSDLGPU3_RenderDrawData(draw_data, cmd_buffer, render_pass); SDL_EndGPURenderPass(render_pass); } else { // Minimized to the Dock (no tray), there is no swapchain texture to wait on for vsync, so // without this the loop would spin. platform_wait_while_hidden(HIDDEN_WAIT_MS); } } // #if USING_SDL // The frame is still ended, with UpdatePlatformWindows, but not drawn or presented. draw_frame := true; #if USING_WINDOWS { draw_frame = !d3d11_device_was_removed(); if draw_frame { ID3D11DeviceContext_OMSetRenderTargets(g_pd3dDeviceContext, 1, *g_mainRenderTargetView, null); ID3D11DeviceContext_ClearRenderTargetView(g_pd3dDeviceContext, g_mainRenderTargetView, *app.ui_global_clear_color); ImGui.ImplDX11_RenderDrawData(ImGui.GetDrawData()); } } io := ImGui.GetIO(); // #ImGui: Update and Render additional Platform Windows if io.ConfigFlags_ & .ViewportsEnable { // ImGui requires this every frame, but on a removed device the DX11 backend asserts creating a // swap chain for a new floating window. None is made then: recovery recreates every window. #if USING_WINDOWS { platform_io := ImGui.GetPlatformIO(); renderer_create_window := platform_io.Renderer_CreateWindow; if !draw_frame platform_io.Renderer_CreateWindow = null; ImGui.UpdatePlatformWindows(); platform_io.Renderer_CreateWindow = renderer_create_window; } else { ImGui.UpdatePlatformWindows(); } if draw_frame ImGui.RenderPlatformWindowsDefault(); } work_seconds := to_float64_seconds(current_time_monotonic() - app.frame_time - swapchain_wait); insert(*perf.frame_times, Frame_Timing.{work = xx work_seconds, delta = ImGui.GetIO().DeltaTime}); // #ImGui:Present and VSYNC #if USING_SDL { SDL_SubmitGPUCommandBuffer(cmd_buffer); } #if USING_WINDOWS { if draw_frame DXGI_Handle_Vsync(); } if perf.time_to_first_frame_ms == 0 { perf.time_to_first_frame_ms = to_float64_seconds(app.frame_time - app.startup_time) * 1000.0; } if (perf.backend == .Vulkan || app.sleep_on_fast_frame) && (ImGui.GetIO().DeltaTime < QUICK_FRAME_DT) { // Sleep for 1 ms after a quick frame to avoid pegging the core sleep_milliseconds(1); } return true; } QUICK_FRAME_DT : float64 : 1.0 / 300.0; // The IPC channel is only polled, so a hidden window still wakes this often to notice a // Bring_To_Front from a second launch; window messages and SDL events end the wait sooner. HIDDEN_WAIT_MS :: 50; // POSIX shared memory names are machine-wide, so the name carries the user id; otherwise a // second user's launch would find the first user's channel and exit. app_shared_memory_channel_key :: () -> string { #if OS == .MACOS { // macOS caps the name at 31 characters (PSHMNAMLEN), which a long folder name plus a user id // of six or more digits, as directory and mobile accounts have, would pass. MAX_NAME_PREFIX :: 16; prefix := APP_FOLDER_NAME; if prefix.count > MAX_NAME_PREFIX prefix.count = MAX_NAME_PREFIX; return sprint("%_%", prefix, POSIX.getuid()); } else #if USING_SDL { return sprint("%_ipc_%", APP_FOLDER_NAME, POSIX.getuid()); } else { return sprint("%_shared_memory_channel", APP_FOLDER_NAME); } } #if USING_SDL { // Per-user, so another user can neither plant a symlink at it nor hold it against this one. app_instance_lock_path :: () -> string { #if OS == .MACOS { directory := to_string(SDL_getenv("TMPDIR")); // a per-user directory on macOS } else { directory := to_string(SDL_getenv("XDG_RUNTIME_DIR")); if !directory { // Unset under sudo, some ssh sessions, and non-systemd sessions. home := to_string(SDL_getenv("HOME")); if !home return ""; directory = tprint("%/.cache", home); make_directory_if_it_does_not_exist(directory); } } if !directory return ""; return tprint("%/%.lock", trim_right(directory, "/"), APP_FOLDER_NAME); } } // Returns true once connected to a running instance, whether or not the message got through. app_bring_running_instance_to_front :: (channel_key: string, attempts: int) -> bool { connected := false; for 1..attempts { connected, app.write_channel = Shared_Channel.writer_connect(channel_key, name_not_found_is_quiet=true); if connected break; if it < attempts sleep_milliseconds(50); } if !connected return false; defer Shared_Channel.writer_disconnect(*app.write_channel); // Reserving fails when the queue is full, e.g. the running instance has stopped polling. message_success, message := Shared_Channel.writer_reserve_message(*app.write_channel, APP_SHARED_MEMORY_CHANNEL_HEADER_SIZE); if !message_success { log_error("Another instance is running, but no space could be reserved in its message queue; it was not brought to the front."); return true; } message.user_write_pointer[0] = cast(u8)App_Shared_Memory_Tag.Bring_To_Front; message.user_write_pointer[1] = 0; #if USING_WINDOWS AllowSetForegroundWindow(ASFW_ANY); if !Shared_Channel.writer_commit_message(*app.write_channel, message) { log_error("Another instance is running, but the bring-to-front message could not be committed."); } return true; } panic :: (format_string: string, args: .. Any, exit_code : s32 = 1) { log_error(format_string, ..args); exit(exit_code); } @PrintLike