Skip to content

New libqsgepaper and initial rMPP support#395

Draft
asivery wants to merge 56 commits into
Eeems-Org:qt6from
asivery:new-libqgsepaper
Draft

New libqsgepaper and initial rMPP support#395
asivery wants to merge 56 commits into
Eeems-Org:qt6from
asivery:new-libqgsepaper

Conversation

@asivery
Copy link
Copy Markdown

@asivery asivery commented Feb 26, 2025

The pen input device is flipped, and refreshes are slow, but other than that, it works :)

Summary by CodeRabbit

  • New Features

    • Support for the reMarkable Paper Pro (RMPP) device and its screen/touch resolutions.
    • Parameterized exclusive-mode repaints (partial and full) for refined display updates.
  • Improvements

    • Rendering now uses an offscreen auxiliary framebuffer for smoother updates.
    • Display geometry and scaling adapt to real device dimensions at runtime.
    • Stricter validation and clearer error handling for display connections and surfaces.
  • Bug Fixes

    • More robust connection, surface and lifecycle handling to reduce visual/focus glitches.
  • Build / CI

    • Updated CI workflows and expanded platform/target build steps.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 26, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d6648a42-b691-4f00-8a53-415a617059ea

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
📝 Walkthrough

Walkthrough

This pull request updates several components related to display and device settings. In the display server, the rectangle data used for GUI updates now comes from an auxiliary buffer rather than the main framebuffer, and framebuffer update methods have been simplified. The EPAPER feature has been removed from the main configuration. Additionally, dynamic screen dimensions are now used across multiple modules, and support for a new device type ("reMarkable Paper Pro") has been added. The ePaper library’s build configuration and EPFramebuffer API have been restructured to accommodate these changes.

Changes

File(s) Change Summary
applications/display-server/dbusinterface.cpp
applications/display-server/guithread.cpp
applications/display-server/main.cpp
Moved rectangle retrieval from main framebuffer to auxiliary buffer in GUI update methods; updated GUIThread’s framebuffer file descriptor initialization and simplified sendUpdate logic; removed EPAPER preprocessor directives from main.
applications/settings-manager/main.cpp Fixed meta type resolution by replacing the undefined variable type with the correct typeName when calling QMetaType::fromName().
applications/system-service/apibase.cpp
applications/system-service/screenapi.h
Replaced hardcoded framebuffer dimensions with dynamic values from deviceSettings.getScreenWidth() and getScreenHeight(), ensuring the image and screen definitions adapt to runtime device settings.
shared/epaper/epaper.pro
shared/epaper/epframebuffer.h
Restructured the ePaper library build configuration by changing the TEMPLATE type and modifying header installation steps; updated EPFramebuffer API by adding new buffer constants, methods (initialize(), sync()), and a new class (EPFramebufferFusion) to manage buffer operations across architectures.
shared/liboxide/devicesettings.cpp
shared/liboxide/devicesettings.h
Extended the DeviceSettings class with support for the new device type RMPP ("reMarkable Paper Pro") and added methods getScreenWidth() and getScreenHeight() to return dynamic screen dimensions.
shared/liboxide/eventfilter.cpp
shared/qpa/oxideeventfilter.cpp
shared/qpa/oxideintegration.cpp
Updated definitions of DISPLAYWIDTH and DISPLAYHEIGHT to dynamically use deviceSettings.getScreenWidth() and getScreenHeight(); adjusted the screen geometry in OxideIntegration to reflect runtime device dimensions.

Sequence Diagram(s)

sequenceDiagram
    participant DBusInterface
    participant GUIThread
    participant EPFramebuffer
    participant Driver

    %% Display update flow initiated from a DBus event
    DBusInterface->>GUIThread: Trigger exitExclusiveMode/exclusiveModeRepaint
    GUIThread->>EPFramebuffer: Retrieve auxBuffer rectangle data
    EPFramebuffer-->>GUIThread: Return rectangle dimensions
    GUIThread->>Driver: Call swapBuffers(Color, QualityMode, CompleteRefresh)
Loading

Poem

Oh, I hop with joy so light,
Auxiliary buffers make the update bright.
From main to aux, the pixels leap,
Dynamic screens no longer sleep.
In settings and code, the magic's spun—
A rabbit’s cheer: “Tech hops, fun’s begun!”
🐇✨

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@asivery asivery changed the title New libqgsepaper and initial rMPP support New libqsgepaper and initial rMPP support Feb 26, 2025
@Eeems

This comment was marked as resolved.

@coderabbitai

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (7)
shared/liboxide/devicesettings.cpp (1)

167-172: Fix inconsistent indentation.

The indentation in the getTouchHeight() method is inconsistent after adding the RMPP case. The default: case and subsequent lines should be aligned with the other cases.

            case DeviceType::RMPP:
                return 2832;
-            default:
-                return 0;
-        }
-    }
+           default:
+               return 0;
+       }
+   }
shared/liboxide/eventfilter.cpp (1)

14-15: Consider replacing macros with inline functions.
Using macros for DISPLAYWIDTH and DISPLAYHEIGHT can reduce type safety and hinder debugging. Inline functions or static constants can serve the same purpose while offering clearer semantics.

-#define DISPLAYWIDTH (deviceSettings.getScreenWidth())
-#define DISPLAYHEIGHT (deviceSettings.getScreenHeight())
+static inline int displayWidth() {
+    return deviceSettings.getScreenWidth();
+}
+static inline int displayHeight() {
+    return deviceSettings.getScreenHeight();
+}
applications/display-server/guithread.cpp (1)

76-78: Offer to help with memfd_create implementation.
The TODO suggests a plan to switch from /dev/null to a dynamically created file descriptor. Let me know if you'd like assistance implementing this feature.

shared/epaper/epframebuffer.h (4)

42-42: Factory-method pointer return.
Returning a raw pointer is typical in singletons/factory methods, but consider returning by reference or using a smart pointer if you need stronger lifetime guarantees.


47-48: Add documentation for newly introduced methods.
These initialize() and sync() methods are self-explanatory, but additional doc comments clarifying their usage and preconditions might help maintainability.


52-68: Check layout consistency in EPFramebufferAcep2.
This class relies on carefully placed buffers and the size of QImage. Changes in Qt versions or compilers may cause alignment or size-differences, leading to potential memory corruption.

If you’d like, I can provide a shell script to search for any references that might incorrectly cast or offset these buffer members.


70-85: Ensure robust handling of EPFramebufferFusion memory layout.
Similar to EPFramebufferAcep2, this class uses partially opaque data and members relying on specific offset sizes. Confirm that sizeof(QImage) is stable on ARM builds to avoid out-of-bounds access.

Would you like me to generate a web search query to investigate QImage size consistency across Qt builds?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d1f7f7f and 3de9444.

⛔ Files ignored due to path filters (1)
  • shared/epaper/libqsgepaper.so is excluded by !**/*.so
📒 Files selected for processing (13)
  • applications/display-server/dbusinterface.cpp (2 hunks)
  • applications/display-server/guithread.cpp (5 hunks)
  • applications/display-server/main.cpp (0 hunks)
  • applications/settings-manager/main.cpp (1 hunks)
  • applications/system-service/apibase.cpp (1 hunks)
  • applications/system-service/screenapi.h (1 hunks)
  • shared/epaper/epaper.pro (1 hunks)
  • shared/epaper/epframebuffer.h (3 hunks)
  • shared/liboxide/devicesettings.cpp (4 hunks)
  • shared/liboxide/devicesettings.h (2 hunks)
  • shared/liboxide/eventfilter.cpp (1 hunks)
  • shared/qpa/oxideeventfilter.cpp (2 hunks)
  • shared/qpa/oxideintegration.cpp (1 hunks)
💤 Files with no reviewable changes (1)
  • applications/display-server/main.cpp
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (22)
shared/liboxide/devicesettings.h (2)

29-30: LGTM: Device type enum updated correctly.

The addition of the RMPP device type to the enum with proper documentation looks good.


78-87: LGTM: New screen dimension methods well documented.

The new methods for retrieving screen dimensions are properly documented and follow the existing code style pattern.

applications/system-service/screenapi.h (1)

20-23: Good improvement: Dynamic screen dimensions.

Replacing hardcoded screen dimensions with dynamic values from deviceSettings improves flexibility and allows for better support of different device types.

shared/qpa/oxideeventfilter.cpp (2)

9-9: LGTM: Required header added.

Adding the deviceSettings header is appropriate for accessing the screen dimension methods.


88-89: Good improvement: Dynamic screen dimensions.

Replacing hardcoded dimensions with dynamic values from deviceSettings enhances device compatibility and maintainability.

shared/liboxide/devicesettings.cpp (5)

128-129: LGTM: Device name added appropriately.

Adding the proper display name for the new device type is good.


154-155: LGTM: Touch dimensions added for new device.

Touch input dimensions added correctly for the RMPP device.


174-196: LGTM: Screen dimension methods implemented correctly.

The implementations for getScreenWidth() and getScreenHeight() look good, providing appropriate values for each device type.


106-110:

Details

❓ Verification inconclusive

Verify device identification method.

Using "reMarkable Ferrari" to identify the RMPP device seems like a development codename rather than a production identifier. Consider if this is the final identification string that will be used in production.


🏁 Script executed:

#!/bin/bash
# Check if there are any references to the final device name in the codebase
echo "Searching for references to reMarkable Ferrari and other device identifiers..."
rg -A 2 -B 2 "reMarkable Ferrari" --glob "!*.md"

Length of output: 563


Action: Confirm the Device Identifier

In shared/liboxide/devicesettings.cpp (lines 106–110), the code checks if the model name contains "reMarkable Ferrari". This string appears to be a development codename rather than a finalized production identifier for the RMPP device. Please verify whether "reMarkable Ferrari" is indeed the intended identifier for production or if it should be updated to reflect the correct final device name.


141-142:

Details

❓ Verification inconclusive

Verify touch environment setting for RMPP.

The RMPP device has an empty touch environment setting. Confirm if this is intentional or if specific settings are needed for proper touch input orientation.


🏁 Script executed:

#!/bin/bash
# Look for any rotation or inversion settings for RMPP elsewhere in the codebase
echo "Searching for touch orientation settings for RMPP..."
rg -A 3 -B 3 "RMPP.*rotate|rotate.*RMPP" --glob "!*.md"
rg -A 3 -B 3 "touchscreen.*orientation|orientation.*touchscreen" --glob "!*.md"

Length of output: 254


I'll need to gather a bit more context before finalizing the review comment. Could you please run an additional verification script to further search for any RMPP-specific touch or orientation configurations in the codebase? For example, a script that searches for general mentions of "RMPP" and entries that might involve "touch" or "orientation" might shed more light on whether the empty setting for RMPP is intentional.

Below is a proposed script for re-verification:

Once you have the output from this script, we can better determine if the empty touch environment setting for the RMPP device is deliberate or if further adjustments are needed.


🏁 Script executed:

[run_scripts]
#!/bin/bash
echo "Searching for any mention of RMPP in the codebase..."
rg "RMPP" --glob "!*.md"

echo "Searching for RMPP touch related settings..."
rg -A 5 -B 5 "RMPP.*touch" --glob "!*.md"

Length of output: 1060


Action Required: Verify RMPP Touch Configuration

After searching the codebase, we found several references to RMPP in shared/liboxide/devicesettings.cpp and shared/liboxide/devicesettings.h, but no evidence of any dedicated touch orientation or inversion settings for this device type. This suggests that returning an empty string for the RMPP touch environment might be intentional.

  • The code snippet in question:
                case DeviceType::RMPP:
                    return "";
  • Our searches for keywords like rotate, touch, and orientation in relation to RMPP did not reveal any additional configurations.
  • Next Step: Please verify against the product specifications or with the relevant stakeholders whether the empty touch environment setting for RMPP is by design or if specific parameters (e.g., rotation or inversion settings) should be applied.
shared/liboxide/eventfilter.cpp (1)

12-12: No issues with new include.
This header inclusion is necessary for accessing deviceSettings.

applications/display-server/guithread.cpp (4)

64-64: Validate auxiliary buffer geometry.
Before creating GUIThread with auxBuffer.rect(), consider handling edge cases where the auxiliary buffer might not be initialized or has a zero-size rectangle. This helps prevent potential runtime errors.


79-79: Ensure correct usage of /dev/null.
Opening /dev/null is likely a temporary placeholder and won't provide meaningful read/write operations for a framebuffer. Confirm that this usage is intentional and doesn't break any downstream logic.


179-185: Clear function logic looks consistent.
Clearing the auxiliary buffer with white and issuing a complete refresh appears correct. Verify whether additional error handling is needed for swapBuffers() failures.


230-230: Aux buffer usage confirmed.
Switching to auxBuffer is aligned with the new approach. No issues here.

shared/epaper/epaper.pro (4)

4-4: TEMPLATE changed to aux.
Switching from lib to aux is unorthodox. Ensure this aligns with the intended build artifacts and doesn't break downstream usage.


6-7: Copying .so and header together could be fragile.
Copying the library file in the same command as the header may cause unexpected issues if either path changes or if the shared object isn't built yet. Confirm build order or consider separate targets.


11-11: Installing multiple files.
Including both the header and the .so in epframebuffer_h_install.files broadens deployment scope. Double-check that this matches your packaging requirements and doesn't omit versioned library handling.


16-17: Review new dependencies and clean rules.
PRE_TARGETDEPS and QMAKE_CLEAN now include the raw_copy target. Ensure these additions align with your build steps and that cleaning removes all generated artifacts.

shared/epaper/epframebuffer.h (3)

11-13: Validate newly introduced macros for reMarkable 2.
These macros are derived from disassembly and may change in future library updates. Consider maintaining them in a single source of truth or verifying them dynamically to avoid misalignment.

If you’d like, I can provide a shell script or a web search query to confirm these offsets match the actual structure size.


15-19: Confirm architecture detection logic.
The definition of DEFAULT_EPFR_RETURN differs across __aarch64__ and the fallback. Ensure that reMarkable 2 builds consistently match the intended macro.


40-40: Signature looks good.
The revised parameter list and default argument appear consistent with usage patterns. No issues spotted.

Comment thread applications/settings-manager/main.cpp
Comment thread applications/system-service/apibase.cpp Outdated
Comment thread shared/qpa/oxideintegration.cpp Outdated
Comment thread applications/display-server/dbusinterface.cpp Outdated
Comment thread applications/display-server/dbusinterface.cpp Outdated
Comment thread applications/display-server/guithread.cpp Outdated
Comment thread applications/system-service/apibase.cpp Outdated
Comment thread shared/epaper/epaper.pro

epframebuffer_h.target = epframebuffer.h
epframebuffer_h.commands = cp $$PWD/epframebuffer.h $$OUT_PWD
epframebuffer_h.target = raw_copy
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see raw_copy defined here. Shouldn't it be what was here before?

Comment thread shared/epaper/epaper.pro Outdated
Comment thread shared/epaper/libqsgepaper.so Outdated
Comment thread shared/liboxide/devicesettings.cpp Outdated
@Eeems
Copy link
Copy Markdown
Collaborator

Eeems commented Apr 19, 2025

@asivery I just merged the c abi branch, luckily there are no conflicts!

@Eeems Eeems linked an issue Apr 20, 2025 that may be closed by this pull request
Eeems

This comment was marked as resolved.

@Eeems Eeems force-pushed the new-libqgsepaper branch from 3de9444 to 0c2f130 Compare May 22, 2026 21:32
@codacy-production

This comment was marked as resolved.

@Eeems Eeems force-pushed the new-libqgsepaper branch from 0c2f130 to 7da3ba9 Compare May 22, 2026 22:01
@Eeems Eeems force-pushed the new-libqgsepaper branch from 149ff40 to 7935966 Compare May 23, 2026 16:01
@Eeems Eeems force-pushed the new-libqgsepaper branch from e5df6fa to 90de2c5 Compare May 23, 2026 17:01
coderabbitai[bot]

This comment was marked as resolved.

@Eeems Eeems force-pushed the new-libqgsepaper branch from 545cb6b to 364a3ff Compare May 24, 2026 19:32
coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 18

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
applications/system-service/appsapi.cpp (1)

1090-1103: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Snapshot applications before stopping them during shutdown.

stopNoSecurityCheck() can synchronously unregister transient apps, so iterating the live applications map here can mutate the container mid-loop. That risks skipping apps or walking entries that were just removed.

Suggested fix
-        for (auto app : applications) {
+        const auto apps = applications.values();
+        for (auto app : apps) {
             if (app->stateNoSecurityCheck() != Application::Inactive) {
                 auto text = "Stopping " + app->displayName() + "...";
                 O_DEBUG(text.toStdString().c_str());
                 notification->setProperty("text", text);
             }
             app->stopNoSecurityCheck();
         }
         O_INFO("Ensuring all applications have stopped...");
-        for (auto app : applications) {
+        for (auto app : apps) {
             app->waitForFinished();
             app->deleteLater();
         }
         applications.clear();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@applications/system-service/appsapi.cpp` around lines 1090 - 1103, Create a
snapshot of the current applications container before calling
stopNoSecurityCheck to avoid mutating the live map during iteration: copy the
entries from applications into a local vector (e.g., appsToStop), iterate
appsToStop to call stateNoSecurityCheck(), set the notification text and call
stopNoSecurityCheck() on each item, and then iterate the original applications
collection to call waitForFinished() and deleteLater() and finally clear()
applications; reference: applications, appsToStop (new), stopNoSecurityCheck(),
stateNoSecurityCheck(), waitForFinished(), deleteLater().
♻️ Duplicate comments (2)
.github/workflows/publish.yml (1)

19-21: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add persist-credentials: false to prevent credential leakage.

The checkout step does not set persist-credentials: false, allowing GITHUB_TOKEN to persist in local git config. This could leak credentials through uploaded artifacts (line 25).

🔒 Suggested fix
       - name: Checkout the Git repository
         uses: actions/checkout@v6
         with:
+          persist-credentials: false
           submodules: true
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/publish.yml around lines 19 - 21, The checkout action
currently uses actions/checkout@v6 with submodules enabled but doesn’t disable
credential persistence; update the checkout step (the actions/checkout@v6 usage)
to add persist-credentials: false so the GITHUB_TOKEN is not written to the
local git config and cannot be leaked via artifacts or subsequent git
operations.
applications/launcher/main.cpp (1)

31-34: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Keep signal-handler path async-signal-safe only.

signalHandler2 still routes into code that calls Qt APIs (QTimer::singleShot, signal emission), which is undefined in a POSIX signal context.

#!/bin/bash
# Verify non-async-signal-safe Qt work is reachable from the signal handler path.
rg -n -C4 'signalHandler2|shutdown_handler|QTimer::singleShot|emit controller->reload|signal\s*\(\s*SIGCONT' applications/launcher/main.cpp

Also applies to: 98-101

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@applications/launcher/main.cpp` around lines 31 - 34, signalHandler2
currently calls shutdown_handler which invokes Qt APIs (e.g., QTimer::singleShot
and controller->reload) from a POSIX signal context; replace the direct Qt calls
from the signal path with an async-signal-safe notification mechanism: set a
sig_atomic_t flag or write a byte to a self-pipe in signalHandler2, and change
shutdown_handler (or a new poll/slot in main/Qt event loop) to perform the
actual Qt work (QTimer::singleShot, emit controller->reload) when the main loop
notices the flag or reads the pipe; ensure signalHandler2, any SIGCONT handler,
and related handlers touch only async-signal-safe globals/functions.
🧹 Nitpick comments (1)
applications/system-service/dbusservice.cpp (1)

278-283: ⚡ Quick win

Keep dependants unique per DBus service.

releaseAPI() and serviceOwnerChanged() both clear all entries for a service, so this list is effectively treated as a set. Appending unconditionally here lets one client accumulate duplicates until teardown.

Suggested fix
-    api.dependants->append(message.service());
+    if (!api.dependants->contains(message.service())) {
+        api.dependants->append(message.service());
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@applications/system-service/dbusservice.cpp` around lines 278 - 283, The
dependants list for each API (api.dependants) must act like a set, so don’t
append the same DBus service multiple times; update the logic in the block that
currently does api.dependants->append(message.service()) to first check whether
message.service() is already present (e.g., using contains() or std::find) and
only append if it isn’t, ensuring api.dependants remains unique per DBus service
(this affects the code paths around serviceOwnerChanged() and releaseAPI() which
clear entries).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/publish.yml:
- Line 19: The workflow uses floating action tags (actions/checkout@v6,
actions/upload-pages-artifact@v3, actions/deploy-pages@v4) which should be
pinned to immutable commit SHAs; update each `uses:` entry to the corresponding
action's full commit SHA (replace `@vX` with @<commit-sha>) so the workflow is
deterministic and supply-chain secure, ensuring you fetch the canonical SHA from
the action's GitHub repo releases/tags and substitute it for
actions/checkout@v6, actions/upload-pages-artifact@v3, and
actions/deploy-pages@v4 respectively.

In `@applications/desktop-file-install/main.cpp`:
- Around line 113-120: Check the result of toFile.write(...) and toFile.flush()
after opening and before closing the file: call toFile.write() and verify the
returned qint64 equals the JSON byte length (json.toUtf8().size()), then call
toFile.flush() and ensure it returns true; on any mismatch or failure, log an
error including path, set success = false and continue, then close the file.
Locate the block around the variables json, toFile, path and the calls to
toFile.open(), toFile.write(), and toFile.close() and add these checks and error
handling for short-writes/flush failures.

In `@applications/display-server/connection.cpp`:
- Around line 63-80: Connection constructor must abort when either socketpair()
fails: detect failure of the first or second ::socketpair call and do not
continue initializing with -1 fds; close any fds that were successfully created
(m_clientFd/m_serverFd or m_clientInputFd/m_serverInputFd) and clean up
m_notifier if already allocated, then throw an exception (e.g.,
std::runtime_error) to abort construction. Specifically, after the first
::socketpair call, if it fails, ensure you close any non -1 entries in fds and
throw; after creating m_notifier, if the second ::socketpair fails, close the
previously created m_clientFd/m_serverFd and any fds from the failed call before
throwing; alternatively introduce a bool valid flag set only after both
socketpairs succeed and only construct m_notifier after both succeed—use
function names/fields socketpair, m_clientFd, m_serverFd, m_clientInputFd,
m_serverInputFd, m_notifier, and Connection::readSocket to locate and implement
the fix.

In `@applications/display-server/dbusinterface.cpp`:
- Around line 677-680: Read m_focused into a local variable before calling
Oxide::dispatchToThread and capture that local in the lambda so the dispatched
work uses the stable pointer; specifically, in the branch that checks m_focused,
assign e.g. auto focused = m_focused, then call
Oxide::dispatchToThread(focused->thread(), [this, focused, device, events] { if
(focused) focused->inputEvents(device, events); }); to avoid TOCTOU where
m_focused could become nullptr or change target between scheduling and
execution.
- Around line 780-791: The range-loop holds ptr as a reference into connections
and then unlocks connectionsLock before calling connections.removeAll(ptr),
which makes ptr invalid if the list changes; to fix, copy the value out before
unlocking (e.g., auto localPtr = ptr) and use localPtr with
connections.removeAll(localPtr) after acquiring the write lock, or rewrite the
loop to use an index/iterator that is only used while the lock is held; update
the code around connectionsLock, the loop over connections, ptr, and the call to
guiThread->notify() accordingly.

In `@applications/display-server/evdevdevice.cpp`:
- Around line 157-159: In EvDevDevice::emitSomeEvents() the reverse-iterator
arithmetic using (i - 1) is invalid when i == events.rbegin(); replace the
current computation of index that uses std::next(i - 1).base() with one that
uses i.base() (or adjust to std::next(i).base() as needed) so you obtain the
correct forward iterator for slicing events; update the code that builds the
vector (some) to use events.begin() up to that i.base()-derived iterator and
remove the i-1 usage to avoid undefined behavior when checking
EV_SYN/SYN_REPORT.

In `@applications/display-server/guithread.cpp`:
- Around line 312-340: GUIThread::sendUpdate currently drops the client-supplied
marker so Connection::Wait (which issues MXCFB_WAIT_FOR_UPDATE_COMPLETE with
mxcfb_update_marker_data{marker,0}) can block on the wrong value; fix by
propagating the marker through the framebuffer submission path: add a marker
parameter to EPFramebuffer::swapBuffers (or an overload) and have
GUIThread::sendUpdate pass its marker through, ensuring EPFramebuffer
stores/returns the same marker value associated with the submitted update (or
maps the client marker to the internal update id and returns that mapping) so
Connection::Wait uses the identical marker produced/recognized by EPFramebuffer;
update all callers of swapBuffers (and relevant types/signatures) to
accept/forward the marker so the MXCFB_WAIT_FOR_UPDATE_COMPLETE path waits on
the correct update.

In `@applications/launcher/controller.h`:
- Line 882: The WiFi state string is misspelled causing QML state logic to fail;
locate the call ui->setProperty("state", "unkown") and change the property value
to the correct spelling "unknown" so state comparisons in QML match expected
values (update any other occurrences of "unkown" in controller.h or related code
paths).
- Around line 775-776: The charger-state branch is erroneously comparing the
charger state variable "state" against BatteryNotPresent; update the check to
use the correct enum ChargerNotPresent (i.e., replace comparisons of
BatteryNotPresent with ChargerNotPresent) where the UI property is set (the
block that calls ui->setProperty("present", true)) so charger-present logic uses
the ChargerNotPresent enum consistently.
- Around line 634-645: The branch that handles notifications->length() > 1
returns early after calling setNotification(...) which prevents emitting
notificationsChanged and causes observers to miss updates; remove the early
return in the >1 branch (the block that calls setNotification(QStringLiteral("%1
notifications").arg(notifications->length()))) and ensure emit
notificationsChanged(notifications) runs for all branches (i.e., after the
if/else ladder), keeping the existing setNotification, clearNotification, and
notificationsChanged calls intact for consistency.

In `@applications/launcher/wifinetworklist.h`:
- Around line 345-356: The removal loop in WifiNetworkList::remove(const
QDBusObjectPath& path) deletes WifiNetwork instances from the networks list
without emitting model change notifications; wrap each removal with
QAbstractListModel notifications by calling beginRemoveRows(parent, row, row)
immediately before removing the element (use the current index of the
iterator/network in networks) and endRemoveRows() immediately after
i.remove()/delete network, or detect and remove consecutive ranges and call
beginRemoveRows/endRemoveRows once per range so the model contract is preserved.
- Around line 216-238: The append(QList<Network*> networks) currently removes
elements from the incoming QList while range-iterating
(networks.removeAll(network)), which can invalidate iteration and later produces
invalid beginInsertRows ranges when nothing remains; update
append(QList<Network*> networks) to first build a separate QList<Network*>
toInsert: iterate the incoming networks without mutating it, for each network
compare ssid() against existing entries in this->networks (calling
item->setNetwork(network) when matched) and otherwise append to toInsert; if
toInsert is empty skip beginInsertRows/endInsertRows and sort(), otherwise call
beginInsertRows with this->networks.length() and this->networks.length() +
toInsert.length() - 1, append new WifiNetwork(...) objects for each entry in
toInsert, then endInsertRows and sort(); apply the identical
collect-and-skip-empty-insert pattern to append(QList<BSS*> bsss) (the BSS path
at ~lines 257-269), avoiding in-loop removals and guarding beginInsertRows
against empty ranges.
- Around line 313-334: The comparator used for sorting WifiNetwork objects
violates strict weak ordering because it unconditionally returns true when
a->connected() is true without checking b->connected(); update the lambda
comparator in wifinetworklist.h to compare the boolean flags explicitly (e.g.,
compare a->connected() vs b->connected() first using a != b -> return
a->connected(); then do the same for available and known) and only use ssid() as
a final tie-breaker so comparisons are deterministic and antisymmetric for pairs
like two connected networks.
- Around line 271-280: The clear() implementation must avoid calling
beginRemoveRows/endRemoveRows when networks is empty and must pass an inclusive
end index of networks.length() - 1 (not networks.length()); modify clear() to
first check if networks.isEmpty() (or networks.length() == 0) and return early,
otherwise call beginRemoveRows(QModelIndex(), 0, networks.length() - 1), then
delete entries in networks, clear the container, and finally call
endRemoveRows(); reference the clear() method and the networks container as the
targets to change, and keep beginRemoveRows/endRemoveRows paired correctly.
- Around line 378-379: WifiNetworkList's QML-exposed methods dereference the
possibly-null member api (methods scanning() and scan(bool)), so add null
guards: in bool WifiNetworkList::scanning() return false if api is nullptr, and
in Q_INVOKABLE void WifiNetworkList::scan(bool active) early-return (no-op) if
api is nullptr instead of calling api->scan(active).waitForFinished();;
reference the WifiNetworkList class and its api member and update scanning() and
scan(bool) to check api before use (optionally log or warn when api is null).

In `@applications/system-service/apibase.cpp`:
- Around line 55-91: APIBase::getFrameBuffer() currently allocates file = new
QFile() before DBus/dup() and uses QFile::open(fd, QFile::ReadWrite) which by
default keeps the duplicated fd open on QFile deletion, causing leaked QFile
objects on early returns and leaked fds after open failures; fix by deferring
construction of the QFile until after you have a valid duplicated fd (after
dup(qfd.fileDescriptor()) succeeds) or ensure every early-return path deletes
the previously-allocated QFile, and when opening with file->open(fd, ...) set
the QFile handle ownership flag so the QFile will close the underlying fd on
close/delete (i.e. use the QFile open variant/flag that makes QFile take
ownership), and also ensure that if file->map() or subsequent QImage
construction fails you explicitly close the fd (or rely on QFile ownership)
before deleting file to avoid descriptor leaks.

In `@applications/system-service/application.cpp`:
- Around line 91-93: The conditional incorrectly negates only setUser and not
setGroup causing successful setGroup to trigger the warning; change the check so
it fails when either m_process->setUser(user()) or m_process->setGroup(group())
returns false (i.e., if (!m_process->setUser(user()) ||
!m_process->setGroup(group())) ), so the O_WARNING(("Failed to set user/group
for the process")) is executed on any failure and the child is prevented from
continuing; update the condition around m_process->setUser, m_process->setGroup,
user(), group() and ensure the failure branch still handles cleanup/abort as
intended.

In `@applications/system-service/main.cpp`:
- Around line 138-142: Before calling stopProcess on PIDs from
Oxide::lsof(lockPath), verify each PID is actually the tarnish process by
checking identifying attributes (e.g., process name/argv or executable path)
rather than blindly trusting Oxide::lsof output; update the loop that iterates
over Oxide::lsof(lockPath) to, for each lockingPid, call
Oxide::processExists(lockingPid) and then confirm the process identity (match
process name "tarnish" or compare its executable path/argv to the current
tarnish binary) and only then call stopProcess(lockingPid).

---

Outside diff comments:
In `@applications/system-service/appsapi.cpp`:
- Around line 1090-1103: Create a snapshot of the current applications container
before calling stopNoSecurityCheck to avoid mutating the live map during
iteration: copy the entries from applications into a local vector (e.g.,
appsToStop), iterate appsToStop to call stateNoSecurityCheck(), set the
notification text and call stopNoSecurityCheck() on each item, and then iterate
the original applications collection to call waitForFinished() and deleteLater()
and finally clear() applications; reference: applications, appsToStop (new),
stopNoSecurityCheck(), stateNoSecurityCheck(), waitForFinished(), deleteLater().

---

Duplicate comments:
In @.github/workflows/publish.yml:
- Around line 19-21: The checkout action currently uses actions/checkout@v6 with
submodules enabled but doesn’t disable credential persistence; update the
checkout step (the actions/checkout@v6 usage) to add persist-credentials: false
so the GITHUB_TOKEN is not written to the local git config and cannot be leaked
via artifacts or subsequent git operations.

In `@applications/launcher/main.cpp`:
- Around line 31-34: signalHandler2 currently calls shutdown_handler which
invokes Qt APIs (e.g., QTimer::singleShot and controller->reload) from a POSIX
signal context; replace the direct Qt calls from the signal path with an
async-signal-safe notification mechanism: set a sig_atomic_t flag or write a
byte to a self-pipe in signalHandler2, and change shutdown_handler (or a new
poll/slot in main/Qt event loop) to perform the actual Qt work
(QTimer::singleShot, emit controller->reload) when the main loop notices the
flag or reads the pipe; ensure signalHandler2, any SIGCONT handler, and related
handlers touch only async-signal-safe globals/functions.

---

Nitpick comments:
In `@applications/system-service/dbusservice.cpp`:
- Around line 278-283: The dependants list for each API (api.dependants) must
act like a set, so don’t append the same DBus service multiple times; update the
logic in the block that currently does api.dependants->append(message.service())
to first check whether message.service() is already present (e.g., using
contains() or std::find) and only append if it isn’t, ensuring api.dependants
remains unique per DBus service (this affects the code paths around
serviceOwnerChanged() and releaseAPI() which clear entries).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f7de97e3-c2e9-4e39-ade2-88150a4612c6

📥 Commits

Reviewing files that changed from the base of the PR and between 3de9444 and a7ba321.

📒 Files selected for processing (125)
  • .clang-format
  • .github/actions/sync-repository/action.yml
  • .github/actions/web/action.yaml
  • .github/workflows/build.yml
  • .github/workflows/publish.yml
  • .github/workflows/web.yaml
  • .gitignore
  • Makefile
  • applications/desktop-file-edit/edit.cpp
  • applications/desktop-file-edit/edit.h
  • applications/desktop-file-edit/main.cpp
  • applications/desktop-file-install/main.cpp
  • applications/desktop-file-validate/main.cpp
  • applications/display-server/connection.cpp
  • applications/display-server/connection.h
  • applications/display-server/dbusinterface.cpp
  • applications/display-server/dbusinterface.h
  • applications/display-server/display-server.pro
  • applications/display-server/evdevdevice.cpp
  • applications/display-server/evdevdevice.h
  • applications/display-server/evdevhandler.cpp
  • applications/display-server/evdevhandler.h
  • applications/display-server/guithread.cpp
  • applications/display-server/guithread.h
  • applications/display-server/main.cpp
  • applications/display-server/surface.cpp
  • applications/display-server/surface.h
  • applications/display-server/surfacewidget.cpp
  • applications/display-server/surfacewidget.h
  • applications/fbinfo/main.cpp
  • applications/gio/cat.h
  • applications/gio/common.cpp
  • applications/gio/common.h
  • applications/gio/copy.h
  • applications/gio/help.h
  • applications/gio/launch.h
  • applications/gio/main.cpp
  • applications/gio/mkdir.h
  • applications/gio/open.h
  • applications/gio/remove.h
  • applications/gio/rename.h
  • applications/gio/version.h
  • applications/inject_evdev/main.cpp
  • applications/launcher/appitem.cpp
  • applications/launcher/appitem.h
  • applications/launcher/controller.cpp
  • applications/launcher/controller.h
  • applications/launcher/launcher.pro
  • applications/launcher/main.cpp
  • applications/launcher/mxcfb.h
  • applications/launcher/notificationlist.h
  • applications/launcher/oxide_stable.h
  • applications/launcher/widgets/CalendarMenu.qml
  • applications/launcher/widgets/NotificationsPopup.qml
  • applications/launcher/wifinetworklist.h
  • applications/lockscreen/controller.h
  • applications/lockscreen/lockscreen.pro
  • applications/lockscreen/main.cpp
  • applications/notify-send/main.cpp
  • applications/process-manager/controller.h
  • applications/process-manager/erode_stable.h
  • applications/process-manager/main.cpp
  • applications/process-manager/process-manager.pro
  • applications/process-manager/taskitem.h
  • applications/process-manager/tasklist.h
  • applications/screenshot-viewer/anxiety_stable.h
  • applications/screenshot-viewer/controller.h
  • applications/screenshot-viewer/main.cpp
  • applications/screenshot-viewer/screenshot-viewer.pro
  • applications/screenshot-viewer/screenshotlist.h
  • applications/settings-manager/main.cpp
  • applications/system-service/apibase.cpp
  • applications/system-service/apibase.h
  • applications/system-service/application.cpp
  • applications/system-service/application.h
  • applications/system-service/appsapi.cpp
  • applications/system-service/appsapi.h
  • applications/system-service/bss.cpp
  • applications/system-service/bss.h
  • applications/system-service/controller.h
  • applications/system-service/dbusservice.cpp
  • applications/system-service/dbusservice.h
  • applications/system-service/eventlistener.cpp
  • applications/system-service/eventlistener.h
  • applications/system-service/generate_xml.sh
  • applications/system-service/main.cpp
  • applications/system-service/network.cpp
  • applications/system-service/network.h
  • applications/system-service/notification.cpp
  • applications/system-service/notification.h
  • applications/system-service/notificationapi.cpp
  • applications/system-service/notificationapi.h
  • applications/system-service/powerapi.cpp
  • applications/system-service/powerapi.h
  • applications/system-service/screenapi.cpp
  • applications/system-service/screenapi.h
  • applications/system-service/screenshot.cpp
  • applications/system-service/screenshot.h
  • applications/system-service/system-service.pro
  • applications/system-service/systemapi.cpp
  • applications/system-service/systemapi.h
  • applications/system-service/tarnish_stable.h
  • applications/system-service/wifiapi.cpp
  • applications/system-service/wifiapi.h
  • applications/system-service/wlan.cpp
  • applications/system-service/wlan.h
  • applications/system-service/wpa_supplicant.cpp
  • applications/system-service/wpa_supplicant.h
  • applications/task-switcher/appitem.cpp
  • applications/task-switcher/appitem.h
  • applications/task-switcher/controller.h
  • applications/task-switcher/corrupt_stable.h
  • applications/task-switcher/main.cpp
  • applications/task-switcher/task-switcher.pro
  • applications/update-desktop-database/main.cpp
  • applications/xclip/main.cpp
  • applications/xdg-desktop-icon/main.cpp
  • applications/xdg-desktop-menu/main.cpp
  • applications/xdg-icon-resource/main.cpp
  • applications/xdg-open/main.cpp
  • applications/xdg-settings/main.cpp
  • interfaces/blight.xml
  • package
  • qmake/common.pri
  • qmake/epaper.pri
💤 Files with no reviewable changes (6)
  • applications/system-service/notificationapi.h
  • applications/system-service/notification.h
  • applications/system-service/network.cpp
  • applications/system-service/network.h
  • applications/system-service/notification.cpp
  • applications/system-service/notificationapi.cpp
✅ Files skipped from review due to trivial changes (40)
  • .clang-format
  • .gitignore
  • applications/desktop-file-edit/edit.h
  • applications/process-manager/process-manager.pro
  • applications/system-service/eventlistener.h
  • applications/process-manager/erode_stable.h
  • applications/launcher/mxcfb.h
  • applications/screenshot-viewer/anxiety_stable.h
  • applications/system-service/apibase.h
  • applications/display-server/surfacewidget.h
  • applications/process-manager/controller.h
  • applications/display-server/evdevhandler.h
  • applications/gio/main.cpp
  • applications/launcher/widgets/NotificationsPopup.qml
  • applications/system-service/eventlistener.cpp
  • applications/system-service/application.h
  • applications/desktop-file-edit/main.cpp
  • applications/gio/open.h
  • applications/launcher/oxide_stable.h
  • applications/gio/version.h
  • applications/screenshot-viewer/controller.h
  • applications/lockscreen/main.cpp
  • applications/gio/mkdir.h
  • applications/gio/launch.h
  • applications/gio/help.h
  • applications/desktop-file-validate/main.cpp
  • applications/screenshot-viewer/screenshotlist.h
  • applications/system-service/controller.h
  • applications/launcher/notificationlist.h
  • applications/gio/common.cpp
  • applications/process-manager/tasklist.h
  • applications/gio/rename.h
  • applications/gio/copy.h
  • applications/fbinfo/main.cpp
  • applications/process-manager/taskitem.h
  • applications/inject_evdev/main.cpp
  • applications/gio/remove.h
  • applications/display-server/main.cpp
  • applications/desktop-file-edit/edit.cpp
  • applications/launcher/appitem.cpp

Comment thread .github/workflows/publish.yml
Comment on lines 113 to 120
auto json = toJson(reg, QJsonDocument::Indented);
if (!toFile.open(QFile::WriteOnly | QFile::Truncate)) {
qDebug() << "Error on file" << path << ": Cannot write to file";
success = false;
continue;
}
toFile.write(json.toUtf8());
toFile.close();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the relevant section around the reported lines
sed -n '80,160p' applications/desktop-file-install/main.cpp | nl -ba

Repository: Eeems-Org/oxide

Length of output: 102


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find all occurrences of toFile.write / flush / close in that file
rg -n "toFile\.(write|flush|close|remove)" applications/desktop-file-install/main.cpp || true

Repository: Eeems-Org/oxide

Length of output: 128


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show declarations/usages of QFile toFile
rg -n "QFile\s+toFile|toFile\s*\(" applications/desktop-file-install/main.cpp || true

Repository: Eeems-Org/oxide

Length of output: 84


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Print a larger window around the reported lines with line numbers (cat -n)
sed -n '60,150p' applications/desktop-file-install/main.cpp | cat -n

Repository: Eeems-Org/oxide

Length of output: 2652


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check for any flush handling / error handling related to toFile
rg -n "toFile\.(write|flush|close|open|error)|QFile::errorString" applications/desktop-file-install/main.cpp || true

Repository: Eeems-Org/oxide

Length of output: 196


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the declaration block for toFile (around the match)
rg -n "QFile toFile\(" -n applications/desktop-file-install/main.cpp || true
sed -n '70,100p' applications/desktop-file-install/main.cpp | cat -n

Repository: Eeems-Org/oxide

Length of output: 1294


Check QFile::write()/flush() results to avoid partial .oxide output

After toFile.open(... | QFile::Truncate) the code ignores possible short-write/flush failures (toFile.write(...) result and any flush() result), which can leave a truncated/partial destination file while the iteration still looks successful.

Suggested fix
-        auto json = toJson(reg, QJsonDocument::Indented);
+        auto json = toJson(reg, QJsonDocument::Indented).toUtf8();
         if (!toFile.open(QFile::WriteOnly | QFile::Truncate)) {
             qDebug() << "Error on file" << path << ": Cannot write to file";
             success = false;
             continue;
         }
-        toFile.write(json.toUtf8());
+        if (toFile.write(json) != json.size() || !toFile.flush()) {
+            qDebug() << "Error on file" << path << ": Failed to write file";
+            toFile.close();
+            toFile.remove();
+            success = false;
+            continue;
+        }
         toFile.close();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
auto json = toJson(reg, QJsonDocument::Indented);
if (!toFile.open(QFile::WriteOnly | QFile::Truncate)) {
qDebug() << "Error on file" << path << ": Cannot write to file";
success = false;
continue;
}
toFile.write(json.toUtf8());
toFile.close();
auto json = toJson(reg, QJsonDocument::Indented).toUtf8();
if (!toFile.open(QFile::WriteOnly | QFile::Truncate)) {
qDebug() << "Error on file" << path << ": Cannot write to file";
success = false;
continue;
}
if (toFile.write(json) != json.size() || !toFile.flush()) {
qDebug() << "Error on file" << path << ": Failed to write file";
toFile.close();
toFile.remove();
success = false;
continue;
}
toFile.close();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@applications/desktop-file-install/main.cpp` around lines 113 - 120, Check the
result of toFile.write(...) and toFile.flush() after opening and before closing
the file: call toFile.write() and verify the returned qint64 equals the JSON
byte length (json.toUtf8().size()), then call toFile.flush() and ensure it
returns true; on any mismatch or failure, log an error including path, set
success = false and continue, then close the file. Locate the block around the
variables json, toFile, path and the calls to toFile.open(), toFile.write(), and
toFile.close() and add these checks and error handling for short-writes/flush
failures.

Comment on lines 63 to 80
int fds[2];
if(::socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, fds) == -1){
if (::socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, fds) == -1) {
C_WARNING("Unable to open socket pair:" << strerror(errno));
fds[0] = fds[1] = -1;
}
m_clientFd = fds[0];
m_serverFd = fds[1];
m_notifier = new QSocketNotifier(m_serverFd, QSocketNotifier::Read, this);
connect(m_notifier, &QSocketNotifier::activated, this, &Connection::readSocket);
connect(
m_notifier, &QSocketNotifier::activated, this, &Connection::readSocket
);

if(::socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, fds) == -1){
if (::socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, fds) == -1) {
C_WARNING("Unable to open input socket pair:" << strerror(errno));
fds[0] = fds[1] = -1;
}
m_clientInputFd = fds[0];
m_serverInputFd = fds[1];
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Abort connection setup when either socket pair cannot be created.

After either socketpair() failure, the constructor still stores -1 descriptors and keeps going. The rest of the object then reads from or writes to invalid fds instead of failing cleanly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@applications/display-server/connection.cpp` around lines 63 - 80, Connection
constructor must abort when either socketpair() fails: detect failure of the
first or second ::socketpair call and do not continue initializing with -1 fds;
close any fds that were successfully created (m_clientFd/m_serverFd or
m_clientInputFd/m_serverInputFd) and clean up m_notifier if already allocated,
then throw an exception (e.g., std::runtime_error) to abort construction.
Specifically, after the first ::socketpair call, if it fails, ensure you close
any non -1 entries in fds and throw; after creating m_notifier, if the second
::socketpair fails, close the previously created m_clientFd/m_serverFd and any
fds from the failed call before throwing; alternatively introduce a bool valid
flag set only after both socketpairs succeed and only construct m_notifier after
both succeed—use function names/fields socketpair, m_clientFd, m_serverFd,
m_clientInputFd, m_serverInputFd, m_notifier, and Connection::readSocket to
locate and implement the fix.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see the fds being used later in the constructor.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Eeems, you're right that the local fds[] array itself isn't used again after the assignments. The concern I should have been clearer about is what happens to the values assigned from fds immediately afterwards.

Specifically, after the first socketpair() failure sets m_serverFd = -1, the very next statement passes it directly to QSocketNotifier:

m_clientFd = fds[0];   // -1 on failure
m_serverFd = fds[1];   // -1 on failure
m_notifier = new QSocketNotifier(m_serverFd, QSocketNotifier::Read, this);  // QSocketNotifier(-1, ...)

QSocketNotifier created with fd -1 won't work correctly — Qt will emit a warning and the notifier will never fire, leaving the connection silently broken with no way for the caller to detect it at construction time (unless they explicitly call isValid() afterwards).

The rest is handled gracefully: isValid() checks m_clientFd > 0 && m_serverFd > 0, and ::close(-1) in the destructor just returns EBADF harmlessly.

Would you like to open a follow-up issue to track guarding the QSocketNotifier construction (or returning early before it) when socketpair() fails?

Comment thread applications/display-server/dbusinterface.cpp
Comment thread applications/display-server/dbusinterface.cpp Outdated
Comment on lines +345 to 356
void remove(const QDBusObjectPath& path)
{
QMutableListIterator<WifiNetwork*> i(networks);
while(i.hasNext()){
while (i.hasNext()) {
auto network = i.next();
network->remove(path);
if(!network->paths().length()){
if (!network->paths().length()) {
qDebug() << "Network removed" << network->ssid();
i.remove();
delete network;
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and inspect the target code region
sed -n '300,420p' applications/launcher/wifinetworklist.h

# Find the class definition to understand what kind of model it is
rg -n "class\s+|QAbstractListModel|beginRemoveRows|endRemoveRows|remove\s*\(\s*const\s+QDBusObjectPath&\s*path\s*\)" applications/launcher/wifinetworklist.h

# Search the repository for beginRemoveRows/endRemoveRows usage in related files
rg -n "beginRemoveRows|endRemoveRows" applications/launcher | head -n 200

# Find other remove/row removal methods in this file (or neighbors)
rg -n "void\s+remove\s*\(|remove\s*\(" applications/launcher/wifinetworklist.h

Repository: Eeems-Org/oxide

Length of output: 5137


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the full WifiNetworkList model methods around remove and rowCount/data
sed -n '120,420p' applications/launcher/wifinetworklist.h

Repository: Eeems-Org/oxide

Length of output: 8324


Wrap row removals with model notifications in WifiNetworkList::remove(const QDBusObjectPath& path).

remove(path) removes from networks (and deletes WifiNetwork) without beginRemoveRows/endRemoveRows, breaking QAbstractListModel update contracts; other removals in this model (remove(QString), removeUnknown(), clear()) already do the proper notifications. Add beginRemoveRows/endRemoveRows around each removed row (using the index before i.remove()), or remove consecutive ranges.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@applications/launcher/wifinetworklist.h` around lines 345 - 356, The removal
loop in WifiNetworkList::remove(const QDBusObjectPath& path) deletes WifiNetwork
instances from the networks list without emitting model change notifications;
wrap each removal with QAbstractListModel notifications by calling
beginRemoveRows(parent, row, row) immediately before removing the element (use
the current index of the iterator/network in networks) and endRemoveRows()
immediately after i.remove()/delete network, or detect and remove consecutive
ranges and call beginRemoveRows/endRemoveRows once per range so the model
contract is preserved.

Comment on lines +378 to +379
bool scanning() { return api->scanning(); }
Q_INVOKABLE void scan(bool active) { api->scan(active).waitForFinished(); }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the relevant section around the reported lines
sed -n '330,430p' applications/launcher/wifinetworklist.h | cat -n

# Find where `api` is declared and `setAPI()` is defined/used
rg -n "setAPI\\b|\\bapi\\b|scanning\\(\\)|scan\\(bool" applications/launcher/wifinetworklist.h

# Search for other definitions/usages of scanning()/scan() and where QML calls might happen
rg -n "scanning\\(\\)|scan\\(bool" applications/launcher -S

# If present, find the type of `api` and how it's set (constructor defaults, nullptr usage, etc.)
rg -n "class|struct|Q_PROPERTY|Q_INVOKABLE|signals?:|slots?:|private:" applications/launcher/wifinetworklist.h

Repository: Eeems-Org/oxide

Length of output: 4776


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the WifiNetworkList class definition including constructor/initialization and scanning property
sed -n '120,420p' applications/launcher/wifinetworklist.h | cat -n

# Find all call sites of setAPI() for WifiNetworkList
rg -n "setAPI\\(" applications/launcher -S

# Find where WifiNetworkList is created/owned (constructors/usages)
rg -n "new\\s+WifiNetworkList\\b|WifiNetworkList\\s*\\*|WifiNetworkList\\b" applications/launcher -S

Repository: Eeems-Org/oxide

Length of output: 11304


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '120,220p' applications/launcher/controller.h | cat -n
sed -n '220,310p' applications/launcher/controller.h | cat -n
sed -n '480,560p' applications/launcher/controller.h | cat -n
sed -n '880,980p' applications/launcher/controller.h | cat -n

Repository: Eeems-Org/oxide

Length of output: 13710


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "networks\\.?scanning\\b|scanningChanged\\b|\\bscan\\s*\\(" -S . --glob='*.qml' --glob='*.js' --glob='*.ts'

# Also check for direct invocations by exposing the model under some name
rg -n "\\bscanning\\b" applications -S --glob='*.qml' --glob='*.ts' --glob='*.h' --glob='*.cpp'

# Find where WifiNetworkList is exposed to QML (property name)
rg -n "getNetworks\\b|Q_PROPERTY\\(.*networks|NOTIFY.*networksChanged" applications/launcher/controller.h -n

Repository: Eeems-Org/oxide

Length of output: 1683


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '1,140p' applications/launcher/widgets/WifiMenu.qml | cat -n

Repository: Eeems-Org/oxide

Length of output: 7228


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find where WifiMenu is instantiated
rg -n "WifiMenu\\b" applications/launcher -S --glob='*.qml'

# Inspect the top-level QML files around where the popup visibility is toggled
# (narrow to likely candidates by searching for "wifi" menus / opening/visible bindings)
rg -n "open|visible\\s*:\\s*|onVisibleChanged\\s*:\\s*|wifiMenu|WifiMenu" applications/launcher -S --glob='*.qml'

Repository: Eeems-Org/oxide

Length of output: 2052


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '340,460p' applications/launcher/main.qml | cat -n

Repository: Eeems-Org/oxide

Length of output: 5943


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find launcher entrypoint / where Controller is instantiated
rg -n "new\\s+Controller\\b|Controller\\s*\\*" applications/launcher -S --glob='*.cpp' --glob='*.h'

# Find where Controller is set as a context property / registered to QML
rg -n "setContextProperty\\(|rootContext\\b|QQmlContext|engine\\b|QQmlApplicationEngine|load\\(" applications/launcher -S --glob='*.cpp'

# Show the relevant launcher main file(s)
fd -e cpp applications/launcher | head -n 50

Repository: Eeems-Org/oxide

Length of output: 1442


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,120p' applications/launcher/main.cpp | cat -n

Repository: Eeems-Org/oxide

Length of output: 4014


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "new\\s+WifiNetworkList\\b|WifiNetworkList\\s*\\(" applications/launcher -S
rg -n "new\\s+WifiNetworkList\\b|WifiNetworkList\\s*\\(" -S .

Repository: Eeems-Org/oxide

Length of output: 465


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "networks->setAPI\\b|\\.setAPI\\(wifiApi\\b|setAPI\\(" applications/launcher -S
rg -n "\\bapi\\s*=\\s*nullptr\\b" applications/launcher/wifinetworklist.h applications/launcher -S

Repository: Eeems-Org/oxide

Length of output: 512


Guard null api in WifiNetworkList QML-exposed scanning methods.

applications/launcher/wifinetworklist.h has WifiNetworkList::api defaulting to nullptr, but scanning() and scan() dereference it unconditionally. Current launcher code sets networks->setAPI(wifiApi) before loading QML, so this is likely not hit on initial UI—but it’s still a fragile footgun if these methods are called before setAPI().

Proposed fix
-    bool scanning() { return api->scanning(); }
-    Q_INVOKABLE void scan(bool active) { api->scan(active).waitForFinished(); }
+    bool scanning() { return api != nullptr && api->scanning(); }
+    Q_INVOKABLE void scan(bool active)
+    {
+        if (api != nullptr) {
+            api->scan(active).waitForFinished();
+        }
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
bool scanning() { return api->scanning(); }
Q_INVOKABLE void scan(bool active) { api->scan(active).waitForFinished(); }
bool scanning() { return api != nullptr && api->scanning(); }
Q_INVOKABLE void scan(bool active)
{
if (api != nullptr) {
api->scan(active).waitForFinished();
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@applications/launcher/wifinetworklist.h` around lines 378 - 379,
WifiNetworkList's QML-exposed methods dereference the possibly-null member api
(methods scanning() and scan(bool)), so add null guards: in bool
WifiNetworkList::scanning() return false if api is nullptr, and in Q_INVOKABLE
void WifiNetworkList::scan(bool active) early-return (no-op) if api is nullptr
instead of calling api->scan(active).waitForFinished();; reference the
WifiNetworkList class and its api member and update scanning() and scan(bool) to
check api before use (optionally log or warn when api is null).

Comment on lines +55 to 91
if (image == nullptr) {
file = new QFile();
auto compositor = getCompositorDBus();
auto reply = compositor->frameBuffer();
reply.waitForFinished();
if(reply.isError()){
O_WARNING("Failed to get framebuffer fd" << reply.error().message());
if (reply.isError()) {
O_WARNING(
"Failed to get framebuffer fd" << reply.error().message()
);
return nullptr;
}
QDBusUnixFileDescriptor qfd = reply.value();
if(!qfd.isValid()){
if (!qfd.isValid()) {
O_WARNING("Framebuffer fd is not valid");
return nullptr;
}
int fd = dup(qfd.fileDescriptor());
if(fd < 0){
if (fd < 0) {
O_WARNING("Failed to get framebuffer fd" << std::strerror(errno));
return nullptr;
}
if(!file->open(fd, QFile::ReadWrite)){
O_WARNING("Failed to open framebuffer" << file->errorString());
O_DEBUG("Framebuffer fd:" << std::to_string(fd).c_str())
if (!file->open(fd, QFile::ReadWrite)) {
O_WARNING("Failed to open framebuffer " << file->errorString());
::close(fd);
delete file;
file = nullptr;
return nullptr;
}
auto stride = deviceSettings.getDeviceType() == Oxide::DeviceSettings::RM1 ? 2816 : 2808;
uchar* data = file->map(0, stride * 1872);
if(data == nullptr){
int stride = deviceSettings.getScreenStride();
uchar* data = file->map(0, stride * deviceSettings.getScreenHeight());
if (data == nullptr) {
O_WARNING("Failed to map framebuffer" << file->errorString());
file->close();
delete file;
file = nullptr;
return nullptr;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
# locate file
ls -la applications/system-service/apibase.cpp
# show surrounding code with line numbers
sed -n '1,140p' applications/system-service/apibase.cpp | nl -ba | sed -n '1,140p'

Repository: Eeems-Org/oxide

Length of output: 209


🏁 Script executed:

#!/bin/bash
set -euo pipefail
# extract around likely function boundaries for getFrameBuffer
rg -n "getFrameBuffer|getFramebuffer|frameBuffer" -S applications/system-service/apibase.cpp

Repository: Eeems-Org/oxide

Length of output: 337


🏁 Script executed:

#!/bin/bash
set -euo pipefail
# show more lines from snippet start to end (lines 40-140)
sed -n '35,140p' applications/system-service/apibase.cpp | nl -ba

Repository: Eeems-Org/oxide

Length of output: 102


🏁 Script executed:

#!/bin/bash
set -euo pipefail
# show further lines beyond 140 to see success path cleanup
sed -n '140,260p' applications/system-service/apibase.cpp | nl -ba

Repository: Eeems-Org/oxide

Length of output: 102


🏁 Script executed:

#!/bin/bash
set -euo pipefail
# Search other usages of QFile::open(fd, QFile::ReadWrite) with file handle flags in repo
rg -n "QFile::AutoCloseHandle|DontCloseHandle|open\(fd, QFile::ReadWrite" -S --glob='*.cpp' --glob='*.h' . | head -n 50

Repository: Eeems-Org/oxide

Length of output: 153


🏁 Script executed:

#!/bin/bash
set -euo pipefail
set -x
file="applications/system-service/apibase.cpp"
if [ ! -f "$file" ]; then echo "missing $file"; exit 1; fi
echo "=== file info ==="
wc -l "$file"
echo "=== rg matches for framebuffer ==="
rg -n "getFrameBuffer|getFramebuffer|frameBuffer" -S "$file" || true

echo "=== lines 35-170 ==="
sed -n '35,170p' "$file" | nl -ba

echo "=== lines 170-320 ==="
sed -n '170,320p' "$file" | nl -ba

echo "=== repo-wide QFile::open(fd, ...) patterns (limited) ==="
rg -n "QFile::AutoCloseHandle|DontCloseHandle|open\(\s*fd\s*,\s*QFile::ReadWrite" -S --glob='*.cpp' --glob='*.h' . | head -n 80 || true

Repository: Eeems-Org/oxide

Length of output: 931


🏁 Script executed:

#!/bin/bash
set -euo pipefail
set -x
file="applications/system-service/apibase.cpp"
wc -l "$file"
rg -n "getFrameBuffer|getFramebuffer|frameBuffer" -S "$file" || true
sed -n '35,170p' "$file" | nl -ba
sed -n '170,320p' "$file" | nl -ba
rg -n "QFile::AutoCloseHandle|DontCloseHandle|open\(\s*fd\s*,\s*QFile::ReadWrite" -S --glob='*.cpp' --glob='*.h' . | head -n 80 || true

Repository: Eeems-Org/oxide

Length of output: 698


🏁 Script executed:

#!/bin/bash
set -euo pipefail
set -x
file="applications/system-service/apibase.cpp"
wc -l "$file"
rg -n "getFrameBuffer|getFramebuffer|frameBuffer" -S "$file" || true
sed -n '35,170p' "$file" | nl -ba
sed -n '170,320p' "$file" | nl -ba
rg -n "QFile::AutoCloseHandle|DontCloseHandle|open\(\s*fd\s*,\s*QFile::ReadWrite" -S --glob='*.cpp' --glob='*.h' . | head -n 80 || true

Repository: Eeems-Org/oxide

Length of output: 698


🏁 Script executed:

#!/bin/bash
set -euo pipefail
file="applications/system-service/apibase.cpp"
echo "=== wc ==="
wc -l "$file"
echo "=== rg framebuffer ==="
rg -n "getFrameBuffer|getFramebuffer|frameBuffer" -S "$file" || true
echo "=== sed 35-170 ==="
sed -n '35,170p' "$file" | nl -ba
echo "=== sed 170-320 ==="
sed -n '170,320p' "$file" | nl -ba
echo "=== repo-wide patterns ==="
rg -n "QFile::AutoCloseHandle|DontCloseHandle|open\(\s*fd\s*,\s*QFile::ReadWrite" -S --glob='*.cpp' --glob='*.h' . | head -n 80 || true

Repository: Eeems-Org/oxide

Length of output: 495


🏁 Script executed:

#!/bin/bash
set -euo pipefail
file="applications/system-service/apibase.cpp"

echo "=== getFrameBuffer() implementation lines 40-130 ==="
sed -n '40,130p' "$file" | cat -n

echo "=== lines 130-190 (cleanup / use sites) ==="
sed -n '130,190p' "$file" | cat -n

echo "=== exact QFile::open call line ==="
rg -n "file->open\\(fd, QFile::ReadWrite\\)" "$file" || true

Repository: Eeems-Org/oxide

Length of output: 4869


🌐 Web query:

Qt QFile::open(int fd, OpenMode mode, FileHandleFlags flags=DontCloseHandle) AutoCloseHandle closes fd on failure

💡 Result:

No, passing the AutoCloseHandle flag to QFile::open(int fd,...) does not cause the file descriptor to be closed if the open operation fails [1][2][3]. According to the official Qt documentation, the AutoCloseHandle flag specifically controls the behavior of the QFile::close method after the file has been successfully opened [1][4]. If AutoCloseHandle is specified and the open call succeeds, the QFile object takes ownership of the file descriptor, and subsequent calls to close will close the descriptor [1][5]. If the open call fails, the QFile object does not successfully adopt the handle, and therefore the logic associated with AutoCloseHandle is not triggered [1][2]. In such cases, the caller remains responsible for managing the file descriptor [1].

Citations:


Fix APIBase::getFrameBuffer() fd/QFile ownership on error paths

  • file = new QFile() is allocated before DBus/dup() failures and is not deleted on early returns (reply.isError(), !qfd.isValid(), fd < 0), leaking the heap object (and re-allocating on each retry).
  • After dup(), file->open(fd, QFile::ReadWrite) uses Qt’s default DontCloseHandle, so on later failures after a successful open() (map() failure / null/empty QImage), file->close() + delete file won’t close the duplicated fd, leaking descriptors across retries.
Suggested fix
-        file = new QFile();
         auto compositor = getCompositorDBus();
         auto reply = compositor->frameBuffer();
         reply.waitForFinished();
         if (reply.isError()) {
             O_WARNING(
@@
         int fd = dup(qfd.fileDescriptor());
         if (fd < 0) {
             O_WARNING("Failed to get framebuffer fd" << std::strerror(errno));
             return nullptr;
         }
+        file = new QFile();
         O_DEBUG("Framebuffer fd:" << std::to_string(fd).c_str())
-        if (!file->open(fd, QFile::ReadWrite)) {
+        if (!file->open(
+                fd, QFile::ReadWrite, QFileDevice::AutoCloseHandle
+            )) {
             O_WARNING("Failed to open framebuffer " << file->errorString());
             ::close(fd);
             delete file;
             file = nullptr;
             return nullptr;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (image == nullptr) {
file = new QFile();
auto compositor = getCompositorDBus();
auto reply = compositor->frameBuffer();
reply.waitForFinished();
if(reply.isError()){
O_WARNING("Failed to get framebuffer fd" << reply.error().message());
if (reply.isError()) {
O_WARNING(
"Failed to get framebuffer fd" << reply.error().message()
);
return nullptr;
}
QDBusUnixFileDescriptor qfd = reply.value();
if(!qfd.isValid()){
if (!qfd.isValid()) {
O_WARNING("Framebuffer fd is not valid");
return nullptr;
}
int fd = dup(qfd.fileDescriptor());
if(fd < 0){
if (fd < 0) {
O_WARNING("Failed to get framebuffer fd" << std::strerror(errno));
return nullptr;
}
if(!file->open(fd, QFile::ReadWrite)){
O_WARNING("Failed to open framebuffer" << file->errorString());
O_DEBUG("Framebuffer fd:" << std::to_string(fd).c_str())
if (!file->open(fd, QFile::ReadWrite)) {
O_WARNING("Failed to open framebuffer " << file->errorString());
::close(fd);
delete file;
file = nullptr;
return nullptr;
}
auto stride = deviceSettings.getDeviceType() == Oxide::DeviceSettings::RM1 ? 2816 : 2808;
uchar* data = file->map(0, stride * 1872);
if(data == nullptr){
int stride = deviceSettings.getScreenStride();
uchar* data = file->map(0, stride * deviceSettings.getScreenHeight());
if (data == nullptr) {
O_WARNING("Failed to map framebuffer" << file->errorString());
file->close();
delete file;
file = nullptr;
return nullptr;
if (image == nullptr) {
auto compositor = getCompositorDBus();
auto reply = compositor->frameBuffer();
reply.waitForFinished();
if (reply.isError()) {
O_WARNING(
"Failed to get framebuffer fd" << reply.error().message()
);
return nullptr;
}
QDBusUnixFileDescriptor qfd = reply.value();
if (!qfd.isValid()) {
O_WARNING("Framebuffer fd is not valid");
return nullptr;
}
int fd = dup(qfd.fileDescriptor());
if (fd < 0) {
O_WARNING("Failed to get framebuffer fd" << std::strerror(errno));
return nullptr;
}
file = new QFile();
O_DEBUG("Framebuffer fd:" << std::to_string(fd).c_str())
if (!file->open(
fd, QFile::ReadWrite, QFileDevice::AutoCloseHandle
)) {
O_WARNING("Failed to open framebuffer " << file->errorString());
::close(fd);
delete file;
file = nullptr;
return nullptr;
}
int stride = deviceSettings.getScreenStride();
uchar* data = file->map(0, stride * deviceSettings.getScreenHeight());
if (data == nullptr) {
O_WARNING("Failed to map framebuffer" << file->errorString());
file->close();
delete file;
file = nullptr;
return nullptr;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@applications/system-service/apibase.cpp` around lines 55 - 91,
APIBase::getFrameBuffer() currently allocates file = new QFile() before
DBus/dup() and uses QFile::open(fd, QFile::ReadWrite) which by default keeps the
duplicated fd open on QFile deletion, causing leaked QFile objects on early
returns and leaked fds after open failures; fix by deferring construction of the
QFile until after you have a valid duplicated fd (after
dup(qfd.fileDescriptor()) succeeds) or ensure every early-return path deletes
the previously-allocated QFile, and when opening with file->open(fd, ...) set
the QFile handle ownership flag so the QFile will close the underlying fd on
close/delete (i.e. use the QFile open variant/flag that makes QFile take
ownership), and also ensure that if file->map() or subsequent QImage
construction fails you explicitly close the fd (or rely on QFile ownership)
before deleting file to avoid descriptor leaks.

Comment on lines +91 to +93
if (!m_process->setUser(user()) ||
m_process->setGroup(group())) {
O_WARNING(("Failed to set user/group for the process"));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Invert the setGroup() check and fail closed.

m_process->setGroup(group()) is currently un-negated, so success logs a warning while a failed group lookup is silently ignored. In the failure case the child can still start with the default gid.

Suggested fix
-                if (!m_process->setUser(user()) ||
-                    m_process->setGroup(group())) {
+                if (!m_process->setUser(user()) ||
+                    !m_process->setGroup(group())) {
                     O_WARNING(("Failed to set user/group for the process"));
+                    return;
                 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@applications/system-service/application.cpp` around lines 91 - 93, The
conditional incorrectly negates only setUser and not setGroup causing successful
setGroup to trigger the warning; change the check so it fails when either
m_process->setUser(user()) or m_process->setGroup(group()) returns false (i.e.,
if (!m_process->setUser(user()) || !m_process->setGroup(group())) ), so the
O_WARNING(("Failed to set user/group for the process")) is executed on any
failure and the child is prevented from continuing; update the condition around
m_process->setUser, m_process->setGroup, user(), group() and ensure the failure
branch still handles cleanup/abort as intended.

Comment thread applications/system-service/main.cpp
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.

Implement new qgsepaper for all devices

2 participants