Skip to content

revive Windows port#541

Merged
scottdraves merged 200 commits into
masterfrom
windows-vs2022-new
Apr 15, 2026
Merged

revive Windows port#541
scottdraves merged 200 commits into
masterfrom
windows-vs2022-new

Conversation

@scottdraves

Copy link
Copy Markdown
Contributor

No description provided.

glouel added 30 commits October 31, 2024 14:26
Early update of MSVC project :
- Using Visual Studio 2022 Community
- Using Clang-cl (clang + MSVC runtime)
- Fix some mac only stuff
- OSD.hpp duplicated to client (will need to be fixed in Xcode)
Remove outdated files from project (should be complete)
Add new files to project (should be complete)
Fix some platform issues

Next: fix intrinsics issues
Maybe take the opportunity to remove outdated manual smart pointers and replace them with modern C++ smart pointers?
Getting to DirectX
Notes :
- Event handling seems to have changed a lot in new client, will probably need to be redone for windows
- Update requires extra parameters now
- Still some intrinsic thing going on
Boost library 1.83 from https://sourceforge.net/projects/boost/files/boost-binaries/1.83.0/

We need the msvc 14.3 (aka vc143) static variants in both mt-gd (releases) and mt-sgd (debug)
Fix virtual functions added for Metal in DX renderer
Add virtual functions exclusively used by DX renderer
Switch to msvc's _aligned_buffer to allocate buffers directly aligned to page sizes. Most of this code seems to have been here only to deal with the lack of valloc on windows, and can probably be simplified further.
Additional fixes to correctly free the aligned buffers on windows
Using boost::log to get rid of the custom posix only implementation
- Fix somehow expected decoder frames that aren't quite there yet for a few ms. Could be it's not an issue with videotoolbox hw acceleration on Mac but is an issue with the software path. FFmpeg decoding threads seems to work and decode thread now
- Fix FFMPEG error that were disabled
- Implement thread naming on windows
- TODO : Find an equivalent for Mac's GCD dispatch_after / dispatch on main queue
- Demote some repetitive useless logging from curl to trace level
Move content to ` C:\ProgramData\e-dream` by default (safe location on all modern  windows systems).
New build target `DebugMD` should now be used to build and debug.

Libpng does require MD (multi threaded DLL compilation), this will likely also fix socket.io which also seemed to require this.
Add DX12 Agility SDK via NuGET
Base Display init
Move to DX12 headers from github as Agility SDK requires dll building
Remove old DirectX renderer
Fix various includes/imports, remove old SDK so that only DX12 remains and no conflict arises
Start moving client_win32 to D3D12
Move stuff to DeviceResources + DirectXTK (both from Microsoft, they supposedly simplify D3D12 handling and handle HDR)
Add texture loading (nearly done)
Anytime a GPU resource is needed, piggyback to DisplayOutputD3D12 that contains a unique_ptr to DirectX12 resources
Basic run loop with DX12 is now updating frames
Aligning on new changes on Mac
@scottdraves scottdraves merged commit 64b3521 into master Apr 15, 2026
scottdraves added a commit that referenced this pull request Apr 15, 2026
PR #541 accidentally re-added the removed lua distribution as
lua5.1-old/ — ~200 files including IDE state and build artifacts.
Nothing in the build references it. Also extend .gitignore to prevent
.vs/, *.vsidx, *.tlog, *.obj, *.pdb, *.idb from slipping in again.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
scottdraves added a commit that referenced this pull request Apr 15, 2026
PR #541 accidentally re-added the removed lua distribution as
lua5.1-old/ — ~200 files including IDE state and build artifacts.
Nothing in the build references it. Also extend .gitignore to prevent
.vs/, *.vsidx, *.tlog, *.obj, *.pdb, *.idb from slipping in again.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
scottdraves added a commit that referenced this pull request Apr 15, 2026
The 4.6 MB debug build of e-dreamd.exe leaked into RuntimeMSVC/ from
PR #541. .gitignore already ignores client_generic/RuntimeMSVC/*.exe,
but the file predated that rule.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
scottdraves added a commit that referenced this pull request Apr 17, 2026
Upgrades OpenSSL 1.0.2k (EOL 2019) to 3.6.1 via the existing vcpkg
manifest, and removes the vendored Windows binary blobs for ffmpeg,
boost, and curl that PR #541 introduced. Progress toward #549 — the
socket.io-client-cpp lib dir (219 MB) is intentionally left for a
follow-up once the WebSocket reconnect regression under vcpkg asio is
root-caused.

Cleaned electricsheep.vcxproj across all 8 configs: removed hardcoded
absolute paths (C:\boost_1_*, C:\local\boost_1_85_0, C:\Users\Jack\...
openssl build tree), vendored dep paths (..\ffmpeg\..., ..\openssl-1.0.2k,
..\curl\lib\...), CURL_STATICLIB define, and OpenSSL 1.0-era library
names (libeay32, ssleay32). Vcpkg manifest integration auto-resolves
include/lib paths via VCPKG_ROOT.

Deleted (216 MB, 346 files):
  client_generic/ffmpeg/win32/ffmpeg-6.1.1-93/   117 MB
  client_generic/openssl-1.0.2k/windows/          62 MB
  client_generic/boost/win32/lib/                 23 MB
  client_generic/curl/lib/debug-ssl-zlib/         14 MB

Verified Release|x64 build (0 errors) and runtime: HTTPS auth, video
playback, and WebSocket remote control all work against api-alpha
with vcpkg's OpenSSL 3.6.1, libcurl, ffmpeg, and boost DLLs.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
scottdraves added a commit that referenced this pull request Apr 22, 2026
PR #541 bumped the vcpkg submodule past upstream commit b94ab47c99
("[ffmpeg] Remove deprecated securetransport"), which silently dropped
the implicit --enable-securetransport on macOS/iOS. The Apr 16 rebuild
produced libavformat.a with no TLS backend (no ff_openssl,
ff_securetransport, or ff_gnutls symbols), breaking the
Player::PlayDreamNow uncached-streaming path with:

  FFmpeg warning: https or dtls protocol not found
  Failed to open URL https://...

Enable the openssl feature for ffmpeg on !windows (Mac reads
/etc/ssl/cert.pem, Linux reads the distro bundle; OpenSSL 3.6.1 is
already linked for libcurl since 823d072). Leave Windows on the
portfile's default --enable-schannel, which reads the Windows cert
store natively; vcpkg's curl[ssl] port already defaults to sspi on
Windows for the same reason.

Also document the runtime log path (/Users/Shared/infinidream.ai/Logs/)
in AGENTS.md.

Related: #556 will eventually supersede this by removing ffmpeg's
TLS dependency entirely (use libcurl for all network).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
scottdraves added a commit that referenced this pull request Apr 22, 2026
PR #541 bumped the vcpkg submodule past upstream commit b94ab47c99
("[ffmpeg] Remove deprecated securetransport"), which silently dropped
the implicit --enable-securetransport on macOS/iOS. The Apr 16 rebuild
produced libavformat.a with no TLS backend (no ff_openssl,
ff_securetransport, or ff_gnutls symbols), breaking the
Player::PlayDreamNow uncached-streaming path with:

  FFmpeg warning: https or dtls protocol not found
  Failed to open URL https://...

Enable the openssl feature for ffmpeg on !windows (Mac reads
/etc/ssl/cert.pem, Linux reads the distro bundle; OpenSSL 3.6.1 is
already linked for libcurl since 823d072). Leave Windows on the
portfile's default --enable-schannel, which reads the Windows cert
store natively; vcpkg's curl[ssl] port already defaults to sspi on
Windows for the same reason.

Also document the runtime log path (/Users/Shared/infinidream.ai/Logs/)
in AGENTS.md.

Related: #556 will eventually supersede this by removing ffmpeg's
TLS dependency entirely (use libcurl for all network).

Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
scottdraves added a commit that referenced this pull request May 11, 2026
* Fix cross-platform compilation bugs (Mac-safe)

Ten standalone bug fixes that are correct on all platforms. Each was a
latent bug on macOS masked by libc++/GLUT/Metal; they surface as hard
errors on Linux/libstdc++/Vulkan.

- Common/SmartPtr.h: guard _LIBCPP_INLINE_VISIBILITY — libstdc++ does
  not define _LIBCPP_HIDE_FROM_ABI, so the macro expansion failed to
  compile on GCC.

- Common/BlockingQueue.h: condition_variable::wait() requires a lock
  object, not a raw mutex. Three call sites passed m_mutex directly
  (undefined behaviour). Also mark condition var members mutable so
  const methods can legally call wait().

- Common/XTimer.h: remove unused <X11/Xlib.h> / <X11/Xutil.h> —
  these headers define the None macro (0L) which clashes with
  enum class TransitionType { None }. Also make Time() const to
  satisfy the ITimer pure-virtual signature.

- Client/FrameDisplay.h: guard <CoreVideo/CVPixelBuffer.h> inside
  #ifdef MAC — CoreVideo does not exist on Linux.

- Client/StringFormat.cpp: add missing #include <cstdarg> for
  va_list / va_start / va_end.

- Client/client.h: wrap SHAREDIR in std::string() when calling
  Set("settings.app.InstallDir", ...). SHAREDIR is const char*; without
  the cast, overload resolution picks Set(string_view, bool) (non-null
  pointer → true) instead of Set(string_view, string_view), storing a
  boolean in JSON and crashing later at as_string().

- ContentDecoder/ContentDecoder.cpp: avcodec_close() was removed in
  FFmpeg 6.x. Replace with avcodec_free_context() which also nulls the
  pointer.

- ContentDownloader/PlaylistManager.h: add missing <condition_variable>
  and <mutex> for the std::condition_variable / std::mutex members.

- DisplayOutput/Renderer/TextureFlat.h: wrap FFmpeg headers in
  extern "C". Without it, sws_getContext gets C++ name mangling and
  fails to link even with -lswscale present.

- Networking/EDreamClient.h: add missing SetQuota() declaration.
  The method is defined in EDreamClient.cpp and called from a free
  function, but was absent from the header, causing a compile error
  on strict-conformance builds.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Wire Linux platform path into existing client code

Modify existing files to route Linux through Vulkan instead of falling
through to Metal (which only exists on macOS). No Mac code paths are
changed — all additions are inside new #elif defined(LINUX_GNU) or
#else branches that were previously absent or broken.

- Client/Player.cpp: change bare #else after WIN32 guard to
  #elif defined(MAC) for Metal includes, add new #else block that
  includes DisplayVulkan.h / RendererVulkan.h. AddDisplay() gains a
  matching Linux branch that constructs CDisplayVulkan + CRendererVulkan.
  Remove dead EndFrameUpdate() FPS-capping code (spFD / m_PlayerFps
  undefined on Linux; the block was never reachable on Mac either due
  to the surrounding #ifndef USE_METAL guard).

- Client/main.cpp: remove <GL/gl.h> / <GL/glut.h> includes from the
  Linux branch (Vulkan does not use GLUT). Guard glutInit() inside
  #if defined(MAC) so it is not called on Linux.

- Client/client_linux.h: remove #include "lua_playlist.h" (file does
  not exist). Add #include "PlatformUtils.h". Fix m_AppData path to
  ~/.config/infinidream/. Derive m_WorkingDir from
  PlatformUtils::GetWorkingDir() rather than the autotools SHAREDIR
  macro. Fix g_Player().AddDisplay() missing nullptr second argument.
  Remove g_Player().Framerate() call (method no longer exists). Fix
  CElectricSheep::Update() call to pass required int argument. Replace
  invalid static_cast<spCKeyEvent> with std::static_pointer_cast.
  Remove entire stale voting block (m_pVoter, GetCurrentPlayingID,
  RepeatSheep, m_spSplashPos/Neg — all removed from current codebase).

- Client/cpu_usage_linux.h: replace glibtop (GNOME library, requires
  libgtop2 which is not a standard dependency) with direct /proc/stat
  parsing. ReadProcStat() uses fscanf with a format string that matches
  the literal "cpu" prefix — no throwaway variables, no string
  allocation, returns matched == 4 on success. GetNumCores() uses
  sysconf(_SC_NPROCESSORS_ONLN) instead of parsing /proc/cpuinfo.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Add Linux build system and platform utilities

Everything needed to configure and compile the project on Linux.
No existing Mac build files are touched.

LinuxBuild/CMakeLists.txt
  Standalone CMake build for Linux (mirrors MacBuild structure).
  Targets: Vulkan, X11, Boost (filesystem/thread/log/log_setup/json),
  FFmpeg (via pkg-config), OpenSSL, libcurl, libpng, pthreads.
  Compiles GLSL shaders to SPIR-V via glslc (from Vulkan SDK or PATH)
  and copies them next to the binary at POST_BUILD. Defines LINUX_GNU,
  BOOST_FILESYSTEM_VERSION=3, SHAREDIR=".".

LinuxBuild/PlatformUtils_Linux.cpp
  Implements the PlatformUtils static interface for Linux:
  IsInternetReachable (connect to 8.8.8.8:53), GetWorkingDir / GetAppPath
  (/proc/self/exe), OpenURLExternally (xdg-open), SetThreadName
  (pthread_setname_np), SetCursorHidden (X11 blank cursor),
  SetOnMouseMovedCallback, DispatchOnMainThread (thread-safe queue
  drained on the main thread), NotifyError (stderr), CalculateFileMD5
  (OpenSSL EVP — same logic as the Mac version).

LinuxBuild/PlatformUtils_Internal.h
  Internal helpers shared between DisplayVulkan and PlatformUtils_Linux:
  DrainMainThreadQueue(), GetMouseCallback(), GetCursorHidden().

socket.io-client-cpp/lib/linux/libsioclient{,_tls}.a
  Linux ELF builds of socket.io-client-cpp. The vendored .a files in
  lib/ are Mach-O universal binaries used by the Xcode project and are
  left untouched. Linux links against the lib/linux/ variants via CMake.
  To rebuild: clone socket.io-client-cpp, cmake -DUSE_TLS=1, make.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Add Vulkan rendering backend for Linux

New DisplayOutput/Vulkan/ directory implements the CDisplayOutput and
CRenderer interfaces for Linux using Vulkan + X11. The Metal backend
(DisplayOutput/Metal/) is untouched.

DisplayVulkan (.h/.cpp)
  Creates an X11 Window and a VkSurfaceKHR via VK_KHR_xlib_surface.
  Manages VkInstance, VkDevice (picks first discrete GPU, falls back to
  integrated), VkSwapchainKHR, render pass, framebuffers, and per-frame
  sync primitives (semaphores + fence). Fullscreen via EWMH
  _NET_WM_STATE_FULLSCREEN. Handles ConfigureNotify resize by
  recreating the swapchain. XScreensaver mode: if XSCREENSAVER_WINDOW
  env var is set the window is reparented into it rather than created
  standalone. Implements CDisplayOutput: Initialize, Resize, SetTitle,
  SetFullScreen, Present, SwapBuffers, ProcessEvents.

RendererVulkan (.h/.cpp)
  Implements CRenderer. Allocates a VkDescriptorPool, descriptor set
  layout, and a graphics pipeline backed by the compiled quad shaders.
  Per-frame: acquires swapchain image, begins render pass, binds
  pipeline + descriptor set, draws the fullscreen quad, ends pass,
  submits, presents. BeginFrame/EndFrame bracket each rendered frame.
  NewTextureFlat() returns a CTextureFlatVulkan.

TextureFlatVulkan (.h/.cpp)
  Implements CTextureFlat. Allocates a VkImage + VkImageView + VkSampler
  and a host-visible VkDeviceMemory staging buffer. Upload(spCImage)
  copies RGBA pixel data via vkMapMemory then transitions the image to
  SHADER_READ_ONLY_OPTIMAL. BindFrame(spCVideoFrame) maps decoded AVFrame
  data directly into the staging buffer each frame for zero-copy video
  upload. The descriptor set is updated with vkUpdateDescriptorSets so
  the fragment shader samples the current frame.

ShaderVulkan (.h/.cpp)
  Loads SPIR-V bytecode (compiled from shaders/quad.vert / quad.frag at
  build time by glslc). Creates VkShaderModule.

FontVulkan (.h/.cpp)
  Stub implementing CFont. Text rendering is not yet wired up on Linux;
  the stub satisfies the interface so the binary links and runs.

shaders/quad.vert
  Fullscreen triangle-pair. Passes UV coordinates to the fragment stage.

shaders/quad.frag
  Samples a combined image sampler (the current video frame texture)
  and writes to the swapchain image. YUV→RGB conversion is handled
  upstream by FFmpeg swscale (frames arrive as RGBA); the shader is a
  straight passthrough blit.

Verified on Arch Linux, RTX 4090, Vulkan 1.4.341, driver 575.57.08:
  cmake -B build -DCMAKE_BUILD_TYPE=Debug && cmake --build build
  ./build/infinidream   →  window opens, content decoder and downloader
                            initialise, video playback begins.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Add native Wayland surface support (VK_KHR_wayland_surface)

At runtime, if WAYLAND_DISPLAY is set and XSCREENSAVER_WINDOW is not,
DisplayVulkan connects natively to the Wayland compositor using
VK_KHR_wayland_surface instead of VK_KHR_xlib_surface. XScreensaver
embed mode always uses X11 (that protocol is inherently X11). On
X11-only systems or if wayland-client is absent at build time, behaviour
is identical to before.

CMakeLists.txt:
  Optionally find wayland-client, wayland-protocols, and wayland-scanner
  via pkg-config. When all three are present, generates xdg-shell
  protocol bindings (xdg-shell-protocol.c / xdg-shell-client-protocol.h)
  at configure time via wayland-scanner, adds HAVE_WAYLAND compile
  definition, and links wayland-client. Graceful fallback to X11-only
  if any piece is missing.

DisplayVulkan.h / .cpp:
  createVulkanInstance() picks VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME or
  VK_KHR_XLIB_SURFACE_EXTENSION_NAME based on m_bWayland.

  initWayland() — connects to wl_display, binds wl_compositor and
  xdg_wm_base from the registry, creates wl_surface, xdg_surface, and
  xdg_toplevel, handles ping/configure/close via static listener
  callbacks (onXdgWmBasePing, onXdgSurfaceConfigure,
  onXdgToplevelConfigure, onXdgToplevelClose), sets fullscreen if
  requested, and does two wl_display_roundtrips to complete the initial
  configure handshake before returning.

  checkEvents() dispatches Wayland events with wl_display_flush() +
  wl_display_dispatch_pending() instead of XPending/XNextEvent.

  setFullScreen() calls xdg_toplevel_set_fullscreen / _unset_fullscreen
  on Wayland; EWMH ClientMessage on X11.

  Title() calls xdg_toplevel_set_title on Wayland; XStoreName on X11.

  destroyWayland() tears down xdg_toplevel → xdg_surface → wl_surface
  → xdg_wm_base → wl_compositor → wl_display in dependency order,
  after Vulkan surface/instance are already destroyed.

client_linux.h:
  Replace CElectricSheep::Update(0) (now requires two boost::barrier
  refs) with the equivalent direct calls: BeginFrameUpdate(),
  DoRealFrameUpdate(0), EndFrameUpdate().

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Fix string_view passed to printf %s in ParsePlaylist (Linux crash)

EDreamClient::ParsePlaylist line 1631 passed a std::string_view to a
%s variadic argument. On Apple Clang/libc++ this accidentally works
because the ABI passes the pointer field first and it happens to be
null-terminated; on GCC/libstdc++ vsnprintf reads the struct bytes
differently and segfaults.

Fix: uuid.empty() ? "default playlist" : uuid.data()

uuid.data() is safe here — callers always pass a std::string-backed
view so the data pointer is null-terminated.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Fix Linux video playback: frame format, texture binding, shader coords

- Frame.h: set m_pFrame->format/width/height in first constructor (was
  left as AV_PIX_FMT_NONE=-1, causing sws_getContext to crash)
- Clip.cpp: use AV_PIX_FMT_RGBA on Linux so ContentDecoder pre-converts
  decoded frames; call BindFrame via USE_HW_ACCELERATION||LINUX_GNU guard
- TextureFlatVulkan: add Bind() override to set descriptor set on renderer;
  add dirty flag so Apply() re-binds after each BindFrame upload; fast-path
  RGBA upload without redundant swscale pass
- RendererVulkan: Apply() was a no-op stub — delegate to base class so
  texture Bind()/Dirty() logic actually runs
- quad.vert: input coords are normalized [0,1], not pixels — remove
  division by screenWidth/Height that squashed quad to a 1px strip
- websocketpp/constants.hpp: bump max_header_size 16KB→64KB for Heroku
  large set-cookie response headers
- libsioclient_tls.a: rebuild with patched websocketpp header
- client.h: fix Run() loop and add virtual Update() base
- EDreamClient.cpp: fix string_view passed to %s (UB on GCC)

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Remove no-op stub comments and dead tmplog markers

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Remove legacy autotools build system and dead screensaver wrapper scripts

The Mac build (primary platform) uses Xcode/CMake and is unaffected.
These files were remnants of the original Electric Sheep Linux autotools era:

- electricsheep-saver, electricsheep-saver-gnome: wrapper scripts pointing
  at an 'electricsheep' binary that no longer exists. Linux xscreensaver
  integration already works via the XSCREENSAVER_WINDOW env var in
  DisplayVulkan — no wrapper script needed.
- Makefile.am (root, Client/, MSVC/SettingsGUI/), configure.ac, Makefile.in:
  dead autotools scaffolding superseded by LinuxBuild/CMakeLists.txt.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Reduce Linux CPU usage: vsync via FIFO present mode + FpsCap

Two targeted fixes for the high CPU usage observed on Linux (~57% CPU
for simple video playback).

RendererVulkan: switch swapchain from MAILBOX to FIFO present mode.
MAILBOX never blocks — vkQueuePresentKHR returns immediately and the
render loop spins at uncapped speed consuming a full CPU core. FIFO
is guaranteed supported by the Vulkan spec and blocks on each present
until the next vblank, giving vsync-throttled output for free with
zero extra code. Mac is unaffected (Vulkan backend is Linux-only).

client.h / Player.h: call FpsCap(m_PerceptualFPS) in the Run() loop
after each frame. FpsCap() already existed in Player.cpp (complete
with its own wall-clock timer) but was unreachable — it was buried
inside a WIN32-only private section. Move it to public and call it
from Run(), which caps frame delivery to the configured perceptual
FPS (default 20 fps) and sleeps away any remaining frame budget.
Run() is only entered from main.cpp on Linux; the Mac Cocoa/display-
link loop never calls it, so Mac behaviour is unchanged.

Together these changes eliminate the busy-spin that was burning ~30-40%
of a CPU core between frames and the uncapped GPU submission loop on
top.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Add API key authentication for Linux / headless environments

The existing auth flow only knew how to refresh a WorkOS sealed session
(wos-session cookie). On Linux there is no UI to obtain one, so a fresh
install could never authenticate.

The backend middleware already accepts an Api-Key <key> header as an
alternative to the session cookie on all protected endpoints, so no
server changes are needed.

Changes:

EDreamClient: AppendAuthHeader()
  Centralises all request auth in one place. Previously every call site
  duplicated three lines of get-setting / build-header / append-header.
  Now: sealed session present → Cookie: wos-session=<session> (existing
  behaviour); no session but api_key present → Api-Key <key>; neither →
  no auth header (request will 401, existing error handling applies).
  All ~15 call sites replaced with AppendAuthHeader(spDownload).

EDreamClient: SignInWithApiKey()
  Persists the API key to settings.content.api_key and returns true.
  No round-trip to the server needed — the key is used directly on each
  subsequent request.

EDreamClient: Authenticate() fallback
  After sealed-session refresh fails, checks the INFINIDREAM_API_KEY
  environment variable. If set, calls SignInWithApiKey() and persists
  the key for future runs. Falls back to a key already stored in
  settings. This means Linux users can authenticate with:
    export INFINIDREAM_API_KEY=<key>  ./infinidream
  or by adding settings.content.api_key to ~/.electricsheep/settings.json.

WebSocket connection
  ConnectRemoteControlSocket() now passes Api-Key in the query map when
  no sealed session is available, matching the HTTP auth fallback.

Mac is unaffected: EDreamClient is shared but the Mac flow always
obtains a sealed session via the first-time setup UI before reaching
any of these paths.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Fix control-reaches-end-of-non-void-function warning in Player.cpp

The streaming-load lambda (detached thread that fetches a download link
and starts a clip) declared a bool return type via an early 'return false'
on the no-display-units error path, but fell off the end without returning
on the success path. Add 'return true' after the clip-start block.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Fix black screen and white square on Linux startup

Three related issues with the startup/loading screen on Linux:

client.h: startup screen invisible during buffering
  The render block was gated on (drawNoSheepIntro || drawn). When a clip
  is in buffering mode g_Player().Update() returns false (drawn=false),
  and if the user is logged in HasStarted() is true so drawNoSheepIntro
  is also false. Result: nothing rendered at all — pure black screen while
  waiting for the first frames to decode. Fix: hoist the startup-screen
  visibility check into a 'showStartup' bool and include it in the outer
  gate condition so the startup screen renders independently of whether
  the player has a frame ready to draw.

StartupScreen.h: white square when logo.png not found
  CImage::Create() initialises the 256×256 buffer to white. If Load()
  fails (file not found) the blank white image was still uploaded as a
  texture and rendered as a white square in the centre of the screen.
  Fix: check the return value of Load() and set m_spImageRef = nullptr on
  failure. The existing if (m_spImageRef) guard around the DrawQuad then
  cleanly skips the logo without affecting the text rendering below it.

CMakeLists.txt: deploy logo.png next to the binary at build time
  The startup screen looks for logo.png relative to the working directory
  (settings.app.InstallDir). On Linux this is the build output directory,
  but the asset was never copied there. Add a POST_BUILD custom command
  that copies Runtime/logo.png next to the infinidream binary so it is
  found on first launch without manual installation steps.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Fix startup screen hidden when player has no frames to draw yet

HasStarted() returns true as soon as the playlist is set in the startup
thread, which happens before the first render frame. This meant
drawNoSheepIntro=false and m_StartupScreen was never created, so
showStartup=false and drawn=false → pure black screen.

Fix: include !drawn in the showStartup condition so the startup screen
is shown whenever there is no video frame available yet (buffering,
quota exhausted, no content downloaded, etc.) regardless of whether
HasStarted() returned true.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Fix Linux video playback: handle Vulkan swapchain invalidation

On Wayland, vkAcquireNextImageKHR returns VK_ERROR_OUT_OF_DATE_KHR
after the first few frames during initial surface configuration.
Previously BeginFrame() returned false without recreating the swapchain,
causing BeginDisplayFrame() to return false every subsequent frame.
Since Player::Update() is gated on BeginDisplayFrame(), it was never
called, so Clip::Update() never ran, the buffering threshold was never
checked, the frame queue filled to max (25 frames), and the decoder
thread blocked on push() — meaning video never played.

Fix: store the VkSurfaceKHR in Initialize(), implement recreateSwapchain()
(wait idle → destroy framebuffers/imageviews/swapchain → recreate), and
call it from both BeginFrame() and EndFrame() on OUT_OF_DATE or SUBOPTIMAL.

Also:
- Return bool from RenderFrame() so Update() can report whether it drew
- Fix SHAREDIR path suffix (\"./\" instead of \".\") in CMakeLists.txt

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Linux Vulkan: fullscreen toggle, VAAPI PTS fix, font rendering, and misc fixes

Fullscreen / windowed toggle (Wayland):
- Add F key binding to toggle fullscreen (replaces Ctrl+F) in client_linux.h
- Add key-release guard (m_bPressed check) to prevent double-fire on toggle actions
- Add ToggleFullscreen() virtual to DisplayOutput base; override in CDisplayVulkan
- Set m_bFullScreen = bFullscreen in initWayland before the initial roundtrip so
  the configure-event filter correctly accepts the startup fullscreen configure
- Parse XDG_TOPLEVEL_STATE_FULLSCREEN from the wl_array in onXdgToplevelConfigure
  and ignore stale fullscreen configures when we have already requested windowed mode
- Set m_Width/m_Height = 1280x720 in setFullScreen(false) before unset_fullscreen
  so RendererVulkan recreates the swapchain at the correct windowed size
- Re-assert ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE on windowed transition
- Add proactive swapchain recreation in BeginFrame when display dimensions change
  (Wayland compositors do not always send VK_ERROR_OUT_OF_DATE_KHR for client-driven
  resizes)
- Add xdg-decoration-unstable-v1 protocol support (server-side window borders);
  falls back gracefully when the compositor does not advertise the interface (GNOME)
- Log all Wayland registry globals at startup for diagnostics
- Update CMakeLists.txt to generate xdg-decoration protocol bindings via
  wayland-scanner alongside the existing xdg-shell bindings

VAAPI / video decoder fixes:
- Fix VAAPI PTS loss: av_hwframe_transfer_data copies only pixel data; manually
  copy pts, pkt_dts, and best_effort_timestamp to sw_frame before av_frame_move_ref
- Guard against corrupted/wrapped last_played_frame resume values (e.g. uint32_t
  wrap from a previous VAAPI PTS bug) by clamping seekFrame to a sane range

Font / HUD rendering (FontVulkan):
- Implement FreeType-based glyph atlas for Vulkan (CFontVulkan)
- Wire up TextureFlatVulkan for font atlas texture upload

Startup / display:
- Fix startup screen visibility: show logo until the first video frame is ready
- Silence spurious PNG warnings for Mac-only OSD assets missing on Linux by
  removing the Warning log from the file-not-found path in LoadPNG.cpp

HUD / help text (client.h):
- Capitalise all F1 help text entries ("A: Slower playback" not "a: slower…")
- Show "F: Toggle full screen" on Linux instead of "Ctrl+F: Toggle full screen"

README: update Linux build and feature documentation

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Fix Mac build regressions introduced in previous commit

- client.h: make BK() macro a no-op on non-Linux builds; the \x01/\x02
  inline-bold markers are only handled by the Linux HUD renderer — on Mac
  they would appear as raw control characters in the F1 help overlay
- ContentDecoder.cpp: change #ifndef WIN32 guards to #ifdef LINUX_GNU so
  the VAAPI code (vaapi_get_format, hw device init, hw frame transfer)
  is only compiled on Linux; Mac retains its VideoToolbox path via the
  existing USE_HW_ACCELERATION / USE_METAL logic in base.h

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Linux/Vulkan: replace FreeType text rendering with ImGui (Mac untouched)

Remove the FreeType-based CFontVulkan/CTextVulkan implementation and replace
it with ImGui-backed CFontImGui/CTextImGui, using ImGui's embedded ProggyForever
font (MIT licensed, no external font files required).

New build dependency: Dear ImGui added as a git submodule at
client_generic/imgui (pinned to v1.92.6, the latest stable release).
Its source files are compiled directly into the Linux build — no system
package or vcpkg entry needed.

- Drop FreeType dependency from LinuxBuild/CMakeLists.txt
- CFontImGui: registers font with ImGui atlas via AddFontDefaultVector()
- CTextImGui: GetExtent() measures via CalcTextSizeA()
- RendererVulkan: integrate ImGui frame lifecycle (NewFrame/Render/RenderDrawData)
- DrawText: segment-based renderer handles \n (newlines) and \t (tab stops at
  4×space width); \x01/\x02 bold markers are stripped — ProggyForever has no
  bold variant so bold text support is removed for now pending a future decision
  on font strategy
- Remove Lato-Regular.ttf from the build (was missing its license)
- Update README: remove freetype2 from prereqs, note ImGui submodule init

No Mac files were modified. The Metal renderer, Mac CMakeLists, OSD,
and CI workflow are all unchanged.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Linux/Vulkan: fix fullscreen, HUD layout, help text, and auth robustness

X11 fullscreen / window management:
- Create window at requested size and apply _NET_WM_STATE_FULLSCREEN as a
  window *property* before mapping (not a client message), so the WM
  resizes it to the correct monitor dimensions on startup.
- MapNotify wait loop now captures ConfigureNotify events so the initial
  fullscreen size is known before creating the Vulkan surface/swapchain.
- Add 500 ms fallback poll if ConfigureNotify arrives after MapNotify.
- Fix EWMH setFullScreen message: data.l[0] must be 0/1, not an Atom
  value; set data.l[2]=0, data.l[3]=1 (source: normal application).
- checkEvents(): update m_Width/m_Height on ConfigureNotify so HUD
  coordinates stay in sync with WM-driven window resizes at runtime.
- Add IsWayland() accessor to CDisplayVulkan.

Swapchain / HUD aspect ratio:
- createSwapchain(): use std::clamp(requested, min, max) instead of
  caps.currentExtent, which can lag behind ConfigureNotify on X11.
- BeginFrame(): trigger swapchain recreation when Display()->Width/Height()
  differs from m_swapExtent, replacing the surface-caps query that had
  the same lag problem.
- Set io.DisplaySize from Display()->Width/Height() so ImGui always
  reflects the true window dimensions.
- Remove max(1.0f, ...) floor from font scaling in DrawText, GetExtent,
  StatsConsole, and StartupScreen; use proportional screenH/1080 scaling.
- Compute startup logo aspect ratio dynamically each frame rather than
  baking it in the constructor, fixing logo squash after fullscreen toggle.

F1 help overlay:
- Remove inline bold markers (\x01/\x02) from BK macro — ProggyForever
  does not support bold; simplify DrawText and GetExtent accordingly.
- Fix tab stop counts so all right-column key bindings align correctly.

Auth:
- On InvalidSession refresh failure, probe the QUOTA endpoint before
  wiping the session; HTTP 200 means the token is still live and the
  client proceeds logged in rather than forcing an unnecessary re-login.
- Companion backend change: e-dream-ai/backend#407

Misc: remove unused boost::bind include from Player.cpp.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Linux/Vulkan: fix ghost texture artefact on F1/F2 HUD background

CRendererVulkan::Reset() was a complete no-op, so calling it with
eTexture never cleared m_aspSelectedTextures[]. Apply() then re-bound
the stale video/startup texture, causing DrawSoftQuad to sample it
instead of the white descriptor set — manifesting as a ghost ellipse
behind the F1 help and F2 status overlays.

Fix: delegate to CRenderer::Reset(_flags) so the base class clears
m_aspSelectedTextures, then explicitly reset m_currentDescSet to
m_whiteDescSet when eTexture is requested.

Inspired by upstream commit 29a5da8 (e-dream-ai/client), which fixed
the same class of stale-texture bug on Metal by clearing stale bindings
between draw calls that share a single render encoder.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Linux: headless auth via magic link, --cached offline mode, XDG config path

- Add LoginWithMagicLinkCode(): on first run prompts for email (saved to
  settings.generator.nickname), sends a one-time code, reads it from stdin,
  and exchanges it for a sealed session before the window opens. Subsequent
  runs auto-refresh the session; re-prompts interactively if it expires.
- Add --cached flag: skips auth entirely and plays locally cached videos in
  offline mode, reporting count and size ("cycling through N (X.X GB) of
  cached videos"). Exits with a clear message if the cache is empty.
- Pre-check auth state before the display window opens: if no sealed session
  and no API key, attempt magic link login or abort with actionable guidance
  rather than opening a window that cannot download any content.
- Store api_key and user email in settings.json (settings.content.api_key /
  settings.generator.nickname) rather than a separate credentials file.
- Fix Linux settings path: remove hardcoded ~/.electricsheep/ in InitStorage()
  so settings, logs, and content all live under ~/.config/infinidream/ as
  client_linux.h always intended.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Print user email less often during TTY login flow

* Reduce client idle CPU by caching frame-loop diagnostics

The Linux client was doing too much synchronous work on the main/render loop for what should be a mostly idle video screensaver workload. In particular, the shared client frame path was repeatedly probing internet reachability, rebuilding cache statistics, polling download/network status, and updating large HUD/stat blocks every frame. On Linux that showed up as excessive CPU usage even when hardware video decode was active, and it also created extra event-pump churn because the Linux wrapper called Display()->Update() a second time each frame.

This change introduces a cached RuntimeDiagnostics layer inside CElectricSheep so the expensive shared-loop diagnostics are sampled on coarse timers instead of per frame. Fast-changing values such as CPU, GPU, and transfer status are refreshed at a short interval, while slower values such as internet reachability, disk/update status, and cache size are refreshed at longer intervals. The dream stats and credits HUD updates are also gated on HUD visibility so hidden overlays no longer pay the full per-frame formatting and data collection cost. An optional INFINIDREAM_PERF_LOG path remains available for coarse runtime measurements.

On Linux specifically, the extra Display()->Update() call in CElectricSheep_Linux::Update() is removed so window events are pumped once per frame through the shared render path instead of twice. That reduces unnecessary X11/Wayland work without changing presentation semantics.

This is intentionally safe for macOS because it does not change the Metal renderer, macOS display update path, swap/present behavior, or decode pipeline. The shared-code changes only reduce how often diagnostic state is recomputed and how often hidden HUDs are updated. Visible HUD behavior remains intact, user-triggered network checks still force a fresh connectivity probe, and the Linux-only event-pump change is isolated to client_linux.h.

* Add libdecor fallback for Wayland window decorations

Wayland windowed mode previously relied entirely on xdg-decoration server-side decorations. On compositors that do not provide that protocol, the Linux client came up undecorated, which left the window difficult to move and resize compared with the existing X11 path.

Add an optional libdecor-0 fallback in the Vulkan display backend and Linux build. When xdg-decoration is unavailable, the Wayland path now creates a libdecor frame, routes title/fullscreen updates through it, dispatches libdecor events during the frame loop, and tears it down safely without affecting the X11 path.

Also add Wayland cursor-theme handling for the content surface so the app restores a normal arrow when re-entered from another application, reports a distro-correct warning when libdecor decorations cannot be provided, and exposes a content-side resize hotzone near the window edges and corners to make resizing practical even when the decoration backend's own hit targets are narrow.

This change is Linux-only. macOS is unaffected because the code paths are guarded behind the Wayland/Linux build configuration and do not alter the shared Metal or Cocoa client behavior.

* Fix post-merge regressions: cross-platform safety and startup screen text

After merging upstream/master, resolve several issues introduced during
conflict resolution:

- Guard POSIX-only code in EDreamClient.cpp (#include <unistd.h>,
  LoginWithMagicLinkCode body) so Windows builds are not broken
- Guard the Linux headless auth block in CElectricSheep::Startup() with
  #ifdef LINUX_GNU so Mac/Windows first-run startup is not short-circuited
  before the GUI auth flow can run
- Restore null guards on g_Player().Renderer() and g_Player().Display()
  in StartupScreen constructor and CElectricSheep::Run()
- Remove DrawSoftQuad override keyword from RendererVulkan (upstream
  removed the base class virtual in commit 1b4735d)
- Fix Frame.h constructor assigning _width/_height to AVFrame fields
- Fix duplicate ToggleFullscreen in DisplayOutput.h (bool vs void return)
- Restore startup screen text rendering: draw "Connecting to
  infinidream.ai..." centered at top of screen via GetExtent() + DrawText

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Fix Mac crash: restore VideoToolbox hardware decoding in ContentDecoder

Our earlier work replaced the upstream `if (USE_HW_ACCELERATION)` block
with a `#ifdef LINUX_GNU` VAAPI block, which inadvertently deleted the
Mac VideoToolbox initialisation.  Without it, FFmpeg outputs software
YUV frames.  TextureFlatMetal::GetPixelBuffer() casts data[3] directly
to CVPixelBufferRef; for software frames data[3] is null, causing an
immediate crash the moment the first video starts playing.  Once the
app crashes the main loop stops running and ProcessCommandQueue() is
never called, which is why remote-control commands also appeared broken.

Fix:
- Add a vt_get_format() callback (MAC-only, mirrors the existing
  vaapi_get_format for Linux) so the codec negotiates
  AV_PIX_FMT_VIDEOTOOLBOX rather than falling back to software YUV.
- Restore VideoToolbox hw-device creation in ContentDecoder::Open()
  using av_hwdevice_ctx_create() (the correct one-step API, replacing
  the upstream's fragile alloc+init pattern) guarded with
  #elif defined(MAC).

The Linux VAAPI path is untouched; Windows gets neither block and
continues with software decoding as before.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Fix ffmpeg HTTPS streaming on Mac/Linux; keep Windows on Schannel

PR #541 bumped the vcpkg submodule past upstream commit b94ab47c99
("[ffmpeg] Remove deprecated securetransport"), which silently dropped
the implicit --enable-securetransport on macOS/iOS. The Apr 16 rebuild
produced libavformat.a with no TLS backend (no ff_openssl,
ff_securetransport, or ff_gnutls symbols), breaking the
Player::PlayDreamNow uncached-streaming path with:

  FFmpeg warning: https or dtls protocol not found
  Failed to open URL https://...

Enable the openssl feature for ffmpeg on !windows (Mac reads
/etc/ssl/cert.pem, Linux reads the distro bundle; OpenSSL 3.6.1 is
already linked for libcurl since 823d072). Leave Windows on the
portfile's default --enable-schannel, which reads the Windows cert
store natively; vcpkg's curl[ssl] port already defaults to sspi on
Windows for the same reason.

Also document the runtime log path (/Users/Shared/infinidream.ai/Logs/)
in AGENTS.md.

Related: #556 will eventually supersede this by removing ffmpeg's
TLS dependency entirely (use libcurl for all network).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* F2 overlay: update server/download status every frame

Timecode already refreshed per-frame but server/download status was on the
0.5s RefreshFastDiagnostics cache. Moved both into the dreamStatsVisible
block so they tick at frame rate when the overlay is visible and not at
all when it isn't.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* Refresh Mac vcpkg cache to enable ffmpeg HTTPS streaming

The cache/univ-osx artifacts committed in a5ac6b3 predate the
ffmpeg openssl feature added in 1d998a4, so the universal libs
shipped without TLS-dependent protocols (tls, dtls, https, rtmps,
gophers, rtmp[e|s|te|ts], ffrtmpcrypt). Streaming uncached dreams
on Mac failed with "https or dtls protocol not found".

Re-run lipo_dir_merge.py against the per-triplet vcpkg trees to
pick up the openssl-enabled ffmpeg.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* Fix Api-Key header format and add Ctrl+R/B keyboard shortcuts on Linux

- EDreamClient: fix malformed Api-Key HTTP header (missing colon).
  curl_slist_append expects "Name: Value" — the missing colon meant the
  header was never delivered, causing all API-key-only logins to silently
  fail and return a black screen.
- client_linux.h: honour Ctrl modifier on KEY_R and KEY_B. Ctrl+R opens
  the remote control URL and Ctrl+B opens the playlists page in the
  browser (mirroring the Mac NSMenu handlers in ESWindow.mm). Without
  this check both keys triggered their unmodified actions (Repeat /
  Report) regardless of Ctrl state.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Fix Windows build: namespace, ToggleFullscreen void return, AddDisplay signature

- Add DisplayOutput:: namespace to CDisplayDX11 construction/cast sites (Player.cpp, RendererDX11.cpp)
- Change ToggleFullscreen() from bool to void; callers now check IsFullscreen() before/after
- Update AddDisplay() call sites in client_win32.h to pass nullptr for new middle parameter
- Add cachedOnlyMode=false stub in WinMain (--cached flag is Linux-only for now)
- Bump generated build version header to 0.1.0 / 6fe1bb1

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* Fix Windows render rate: scope FpsCap call to Linux

Commit e0a87d5 added FpsCap(m_PerceptualFPS) to the Run() loop to fix
Linux CPU usage, claiming "Run() is only entered from main.cpp on
Linux." That is incorrect: WinMain in main.cpp also calls client.Run(),
so the cap was throttling the Windows render loop to the perceptual FPS,
slowing it down and producing visibly choppy motion.

Guard the FpsCap call with #ifdef LINUX_GNU. Windows is paced by DXGI
Present and does not need an explicit cap; Mac never enters Run() (uses
the Cocoa/display-link loop) and is unaffected either way.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* Remove startup splash text and simplify StartupScreen

Drop the "Connecting to infinidream.ai..." message and all the font/text
plumbing it required. The text was invisible on macOS anyway (CATextLayer
starts hidden and nothing unhid it), and it adds nothing useful on the
platforms where it did render. Also clean up dead members
(m_LogoSize, m_TextureUploaded, m_MoveMessageCounter) and the orphaned
m_bServerMessageStartTimer block, plus the dead drawNoSheepIntro branch
in client.h.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* Fix Remote indicator flash at startup

Force live connectivity probes until the first websocket connection, and
tighten the cache interval from 3s to 0.5s. The diagnostics-caching commit
widened the auth-done -> websocket-up gap from sub-frame to up to 3
seconds, triggering the "initial connection not yet established" branch
before the cache caught up.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

* Fix Linux link order: move OpenSSL after libsioclient_tls.a

ld --as-needed dropped libssl before the socket.io static archive could
reference it, failing with "DSO missing from command line".

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

---------

Co-authored-by: amillionbouncyballs <[email protected]>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Co-authored-by: alansley <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants