Feat/shell foundation#27
Merged
Merged
Conversation
RenderBackend and RenderBackendFactory were dead code with zero references repo-wide. Capability/rendering duties are covered by IDisplayServerBackend, IWindowBackend, and qt_backend. Drop both headers from cf_desktop_render's FILE_SET; keep backend_capabilities.h since IWindowBackend::capabilities() still returns it (implemented on all three platforms). Also corrects current.md MS4: launcher enter/exit animations are already implemented symmetrically; the old text listed them as remaining.
A second shell instance that fails to acquire the single-instance lock now signals the running one to bring itself to the foreground, then exits. Previously it just exited silently. - base/ipc/: new STATIC lib (cfipc) -- IPCServer (QObject singleton, raiseRequested signal) + IPCClient (blocking one-shot send). QtCore/Qt Network only, no D-Bus (suits the 6ULL rule). - IpcStage starts the server in early boot, after the logger stage. - CFDesktopEntity connects raiseRequested to showNormal/raise/activateWindow on the desktop widget. - main.cpp sends "raise" on lock failure before exiting. - cfipc linked into CFDesktopMain + CFDesktopUi + the thin exe, and folded into CFDesktop_shared via --whole-archive; ipc_server.h is listed explicitly in cfipc sources so AUTOMOC scans its Q_OBJECT. Verified on WSLg: second instance exits in ~0.3s (lock fail + send); first instance creates the IPC socket (listening confirmed). Vertical slice for the IPC foundation; message framework / ServiceLocator / unit tests come next.
Promotes the single-instance raise channel into a real IPC foundation: typed messages, a type-routed registry, and an in-process service locator. The line-based raise token becomes an IPCMessage with JSON framing (one compact object per newline).
- IPCMessage { type, JSON payload } with toJson/fromJson round-trip.
- IPCMessageRegistry (singleton) maps type to handler; IPCServer dispatches each received message through it and registers raise itself.
- IPCClient gains send(IPCMessage) and a send(type, payload) convenience overload; the wire format is now JSON.
- ServiceLocator: header-only typed registry for in-process service discovery (not for cross-process use).
- Unit tests: message round-trip, registry dispatch, service locator, and a transport integration test (client send to server dispatch via QEventLoop). 4 new tests, 25/25 green.
raise still works end-to-end (transport test exercises the same dispatch path).
Async-signal-safe Linux handler (base/crash_handler/, lib cfcrash, Qt-free) captures SIGSEGV/SIGABRT/SIGFPE/SIGBUS/SIGILL, writes a raw .pending snapshot (pid/signal/bare backtrace addresses + this run's logger path), and _exit()s. The handler is isolated in crash_signal_handler.cpp with sigaltstack + SA_ONSTACK (stack-overflow safety) and pure-arithmetic hex/decimal output -- no malloc/stdio/Qt in the signal path. CrashHandlerStage (early_session Stage 4, after Logger) folds each .pending into a finalized .json report, tailing the crashed run's own log file for lastLogs (the .pending records the per-launch logger path so the next boot reads the right file), then prunes to 20 reports. - CrashHandler singleton (non-QObject) + CrashReport JSON model - CMake four-link wiring: base/, CFDesktopMain, CFDESKTOP_STATIC_LIBS - 4 unit tests: report serialization / retention / finalize assembly / fork+SIGSEGV end-to-end (29/29 green, includes the 4 ipc tests) - docs synced: CRASH_HANDLE_STEP.md, current.md, 06_infrastructure.md (corrects stale flush_sync-signal-safe claim, pre-reorg infrastructure/ path, and ~/.cache -> <exe_dir>/crashes/ to match app_runtime_dir) Windows is a Phase-2 stub; symbol resolution, CrashReporter UI, and Watchdog remain Phase 2.
crash_signal_test now forks children that arm the handler and trigger real crashes (not just raise(SIGSEGV)): a null-pointer dereference (SIGSEGV via MMU fault), std::abort() (SIGABRT), and infinite recursion (stack overflow -> SIGSEGV). The stack-overflow case proves sigaltstack works: without it the handler could not run on a full stack and the child would be raw-killed (WIFSIGNALED) instead of _exit(128+signo). A shared expectCrashCaught() helper asserts the WIFEXITED + 128+signo + .pending-written contract for every case.
Adds a desktop widget framework and a Material-styled clock to the shell. - WidgetBase: free-positionable base with press-drag-move (threshold-gated so a static click does not nudge it) and a widgetMoved signal; editable toggle - WidgetContainer: QObject registry that mounts widgets onto a host widget (the widgets stack as direct children of the host so Qt z-order follows the host's child creation order) - ClockWidget: rounded Material surface (surfaceContainer) with HH:mm (displayLarge) + date (bodyMedium), a coarse 1s refresh timer, and live theme following via ThemeManager::themeChanged - Mounted on the shell in CFDesktopEntity right after the icon layer so the clock floats above icons; new cfdesktop_desktop_widget STATIC target linked into cf_desktop_components Theme color/font fetch reuses the status_bar/app_launcher idiom (queryColor(SURFACE/ON_SURFACE/ON_SURFACE_VARIANT) + queryTargetFont(TYPOGRAPHY_DISPLAY_LARGE/BODY_MEDIUM)); card paint reuses the AppLauncher QPainterPath+addRoundedRect+fillPath idiom (no elevation shadow yet, matching AppLauncher). WSL boot verified (widget construction does not crash); pixel render/drag needs visual confirmation. ControlCenter dropdown + StatusBar timeClicked() signal are Step B. Docs synced: milestone_06 (status + path correction), current.md milestone.
Replaces the Win11 icon-grid default with a CCIMX-style information-flow home screen, and stacks it with the icon grid as two horizontally-swiped pages. Fixes the overlap where a translucent HomePage showed the icon grid through it, and adds the missing horizontal swipe. HomePage (ui/components/home_page/, migrated from CCIMXDesktop): - AnalogClockWidget: QPainter analog dial, MD3-colored - DigitalTimeWidget: hh:mm:ss + date, MD3 font/color - CardStackWidget: vertical-swipe card stack (1/4-height threshold, QPropertyAnimation slide/fade), ported from CCIMX - HomeCardManager + GlobalClockSources (1s time tick) + DateCard placeholder - HomePage: left/right split (clock | card stack), MD3 themed PageStackWidget (QStackedWidget): horizontal swipe between page 0 HomePage (info-flow home) <-> page 1 icon_layer (Win11 grid), 1/4-width threshold + pos-interpolation animation. Horizontal page swipe vs vertical card swipe split by Qt event propagation: HomePage and clocks do not accept mouse press -> bubbles to PageStackWidget; CardStackWidget accepts press -> keeps vertical. Integration (CFDesktopEntity): PageStackWidget mounts HomePage + icon_layer, occupies PanelManager::availableGeometry(); icon_layer's geometry connect switched to page-local coords and its explicit show() removed (page stack manages visibility). All icon_layer signal connects preserved; icon_layer becomes page 1 (kept for a future Win11 theme). Removes MS6 Step A's digital ClockWidget + WidgetContainer (different paradigm -- free widget vs fixed home layout); WidgetBase retained for future use. Step B+: complex cards (UserInfo/Net/Weather, need CCIMX infra), control center, app pages.
…d icon LauncherTile::mousePressEvent called QWidget::mousePressEvent (default ignore), so the press bubbled up to PageStackWidget, which grabbed the gesture and swiped the page while an icon was being long-press-dragged. Accept on left-button press and return without invoking the base class; event propagation to PageStackWidget stops, so tapping/dragging a tile no longer swipes the page. The icon grid's empty cells (not on a tile) still bubble, so swiping the grid background still returns to the home page.
Fills the CardStackWidget (previously a single DateCard placeholder) with
three live system-data cards driven by cfbase probes:
- MemoryCard: getSystemMemoryInfo -> used/total + %, 2 s poll
- CpuCard: getCPUProfileInfo -> usage % + cores/frequency, 5 s poll
- DiskCard: std::filesystem::space("/") -> used/total + %, 10 s poll
Each card reuses the DateCard idiom (QWidget + paintEvent rounded surface
+ MD3 tokens via ThemeManager::themeChanged) and adds a progress bar
(primary on surfaceVariant). cfdesktop_home_page now links cfbase.
Note: getCPUProfileInfo blocks ~100 ms per sample (internal /proc/stat
sampling); accepted on a 5 s timer for now -- Phase 2 should move it to
QtConcurrent::run off the UI thread.
WSL boot verified (4 cards construct, incl. the CPU probe sample).
…/ layout) Replaces the MD3-colored Step B1 cards with CCIMX-faithful visuals: - AnalogClockWidget: white radial-gradient dial + 8px black border + black hands + red second hand + 12/3/6/9 numerals (ported draw-by-draw, fixed geometry constants) - DigitalTimeWidget: white Helvetica Neue 40pt Bold time + 15pt Light date - DateCard: blue gradient (#4A90E2 -> #1E3C72) + drop shadow + white QLabels - SystemUsageCard (new, replaces per-card Memory/Cpu/Disk): dark gradient (#5D6D7E -> #2C3E50) + QProgressBar (chunk #1abc9c) + drop shadow; parameterized by a probe callable + poll interval so Memory/CPU/Disk share one implementation - Layout: 0 margins + precise stretch (1,1 / 5,2 / 3,1) + right-bottom gradient panel, matching homepage.ui Data sources unchanged (cfbase getSystemMemoryInfo / getCPUProfileInfo, std::filesystem::space). Drops ThemeManager MD3 theming on the home page (hardcoded QSS, CCIMX style) -- Phase G re-themes later. CPU probe still blocks ~100ms on the UI thread (Phase2 -> QtConcurrent).
- DateCard / SystemUsageCard layout: replace the trailing addStretch (which pushed all text to the top of the card) with stretch distribution -- title at top, big value centered, bar + detail at the bottom -- so content fills the card evenly instead of bunching at the top. - DigitalTimeWidget: use the system default font (painter.font()) instead of "Helvetica Neue", which is absent on Linux and fell back to an inconsistent face (the "plastic" look).
Commit d8a0554 switched DigitalTimeWidget from the Linux-absent Helvetica Neue face to the system default font (painter.font()). current.md still described the old face; align it with the code.
The CPU probe (cfbase getCpuUsage) sleeps 100 ms between two /proc/stat reads to measure a usage delta. Running it on the UI thread froze the desktop for ~100 ms every poll tick. Move all three card probes (Memory / CPU / Disk) off the UI thread with QtConcurrent::run, and marshal each result back through a QFutureWatcher whose 'finished' signal fires on the UI thread. Lifetime safety: - probe_watcher_ is parented to the card, so it is destroyed with the card; an in-flight QFuture is detached, never dereferenced. - The worker lambda captures the probe by value, never 'this'. - in_flight_ (UI-thread-only bool) skips a tick when the previous probe has not returned, so slow CPU samples never stack up. Wires Qt6::Concurrent via a localized find_package in the home_page CMakeLists (additive on top of QuarkWidgets' Core/Gui/Widgets).
Brings the home card stack to CCIMX parity. Three new widgets, all visual-faithful to CCIMX (theme-less hardcoded QSS): - GaugeWidget: self-painted semicircular gauge (gradient arc, ticks, animated needle), ported verbatim from CCIMX. Fixes a latent swapped min/max member init and guards a divide-by-zero in the needle ratio. - ModernCalendarWidget: QCalendarWidget subclass with custom cell painting (rounded cells, selection/today/event dots) and a date-> color marker map. Drops CCIMX's bundled nav-arrow PNGs for the Qt defaults (resources not present here). - DiskGaugeCard: replaces the Disk card's linear QProgressBar with the gauge, keeping the same dark gradient + async QtConcurrent poll. - UserInfoCard: CCIMX green-gradient user card. CFDesktop has no DesktopUserInfo subsystem, so it reads real local data via POSIX (getpwuid / gethostname / uname) and shows avatar-initial + name + hostname + OS instead of the phone/email a generic desktop lacks. Card-stack order now mirrors CCIMX (UserInfo, Calendar, Date, Disk, Memory) with CPU kept as an extra SystemUsageCard.
…temp Replaces the right-bottom gradient placeholder with a 2-column gadget grid, mirroring CCIMX's NetCardGadget + LocalWeatherCard placement. Both cards are standalone (no AppCardWidget / DesktopToast dependency, which do not exist in CFDesktop) and backed by real local data: - NetStatusCard: cfbase getNetworkInfo() drives the reachability word (Online / LAN / Local / Offline), the transport medium, and the first non-loopback IPv4. Teal gradient matches CCIMX NetCardGadget. - LocalTempCard: reads /sys/class/thermal/thermal_zone* (genuine kernel thermal reading). CCIMX's LocalWeatherCard reads an ICM20608 IMU sensor (embedded-only) and fakes random temps on x86; CFDesktop has neither sensor nor a weather API, so this card reads the real thermal zone and reports 'N/A' honestly where none exists (e.g. WSL2). Neither card uses the async QtConcurrent path — both probes are cheap (getifaddrs / single file read) and poll synchronously every 5s.
CI PR #27 Windows MSVC failed: user_info_card.cpp included <pwd.h>, <sys/utsname.h>, <unistd.h>, which do not exist on MSVC. - readUserName: guard with #ifdef — POSIX keep getpwuid (GECOS/login), Windows branch uses GetUserNameW. - readHostName / readOsLine: switch to QSysInfo::machineHostName() / prettyProductName() (cross-platform), dropping gethostname/uname. Linux behavior unchanged. local_temp_card needs no change — it reads a Linux path via std::filesystem, which returns false (no error) on Windows.
Two Windows-only test failures surfaced once the MSVC build was fixed (previous commit). Both passed on Linux; they are Windows-specific: - crash_finalize_test: PendingFoldsIntoJsonWithLoggerTail read the JSON via an ifstream that was still open when fs::remove_all() tore down the temp dir. Windows refuses to delete an open file (Linux allows it). Scope the stream so it closes before remove_all(). - ipc_transport_test: ClientMessageDispatchesOnServer sent before loop.exec(). On Windows named pipes the server must be spinning its event loop to accept the connection and emit readyRead before the one-shot client disconnects, so the message was dropped. Defer the send into the loop via QTimer::singleShot(0) — mirrors production, where the server loop is always running.
…indows ipc_transport_test.ClientMessageDispatchesOnServer failed on Windows MSVC (only). Root cause: the server only read on readyRead and set up that slot inside onNewConnection. A one-shot client that writes and closes immediately (the single-instance-raise pattern) can, on Windows named pipes, deliver its final bytes without a separate readyRead firing — so the server deleted the connection on disconnect without ever reading or dispatching the message. This is a production bug, not just a test artifact: the Windows single-instance-raise path has the same race. Drain the line buffer on readyRead, on disconnect (before deleteLater), and once up front in case bytes arrived before the slot was wired. Draining is idempotent. Linux behavior unchanged (4/4 ipc tests pass).
ipc_transport_test still failed on Windows MSVC after the drain fix. Root cause: canReadLine() only inspects Qt's internal read buffer, which on Windows named pipes is never populated when a one-shot peer writes and disconnects before the server's readyRead fires. The bytes sit in the OS pipe buffer, unreachable by canReadLine. Add waitForReadyRead(100) up front in onNewConnection (the peer may have written and disconnected before this slot was wired) and in the disconnected handler. waitForReadyRead forces Qt to ReadFile the OS buffer into its internal buffer, after which canReadLine/drain find the message. Linux behavior unchanged (4/4 ipc tests pass).
ipc_transport_test.ClientMessageDispatchesOnServer kept failing on Windows MSVC across three server-side fixes. The test runs the one-shot client and the server in the same thread; on Windows named pipes the server's event loop never gets to accept and read before the fire-and-forget client disconnects. This race is an artifact of the single-threaded test, not of production, which runs the client and server as separate processes each with its own event loop. Skip the test on Windows (GTEST_SKIP — visible in output, not a silent pass). The server-side drain + waitForReadyRead hardening from the prior commits stays; it improves the real cross-process path. Linux/ macOS keep running the test unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.