Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add i3/Sway support #8

Merged
merged 2 commits into from
Nov 24, 2024
Merged

Add i3/Sway support #8

merged 2 commits into from
Nov 24, 2024

Conversation

nydragon
Copy link
Contributor

@nydragon nydragon commented Nov 2, 2024

This pr adds support for retrieving data from Sway's IPC.

Features:

  • Get last IPC object
  • Dispatch Sway commands
  • Workspaces
    • Retrieve all
    • Listen to data changes via signals
    • Force full refresh of workspaces
    • Get active
    • Live update
  • Monitors
    • Retrieve all
    • Listen to data changes via signals
    • Force full refresh of monitors
    • Get active
    • Live update

Additional Todo:

  • documentation

@outfoxxed
Copy link
Member

Looks largely copied from hyprland ipc at this point so I figured I'd mention a few things:

  • Make sure you only copy what makes sense to copy, don't keep what doesn't fit.
  • If you can leave the dispatch socket open you should. You can't in hyprland, idk about i3/sway
  • If you aren't implementing any sway specific features, this should work with i3 and be named as such.

@nydragon
Copy link
Contributor Author

nydragon commented Nov 3, 2024

Thanks for the feedback, I wanted to stay mainly in line with the existing code, but I do have some questions:

  1. Sway calls monitors "outputs", should that be reflected in Quickshell or is it better to be have uniform naming across different compositors ? Or "dispatch" is called "run commands"
  2. Should I move the x11 and wayland folders into a compositor folder and create a shared folder in there? Or where would I put the sway-i3 code ?
  3. We could abstract a part of the logic, namely the workspace/monitors refresh functions, across Hyprland & Sway/i3, does that make sense ?

@outfoxxed
Copy link
Member

  1. Monitor. Dispatch under hyprland is only for commands with no response as we don't currently have a promise type.
  2. For now src/x11/i3/ipc is fine. Hyprland is under the wayland folder because it has to use cmake functions defined in the wayland folder.
  3. Yes. This should happen, but not in this PR.

Also make sure you have a way to turn a sway monitor into a qs monitor.

@nydragon nydragon force-pushed the master branch 3 times, most recently from c061ef6 to e899eb4 Compare November 5, 2024 13:38
@nydragon nydragon changed the title Add Sway support Add i3/Sway support Nov 5, 2024
@nydragon nydragon force-pushed the master branch 5 times, most recently from 4a93776 to 30aa969 Compare November 5, 2024 23:33
@outfoxxed
Copy link
Member

You've force pushed a lot, this ready for review?

@nydragon
Copy link
Contributor Author

nydragon commented Nov 6, 2024

Yep, you could review it,

I would still want to implement more i3/Sway IPC features, but I think that will be for a later PR

@nydragon nydragon marked this pull request as ready for review November 8, 2024 15:36
Copy link
Member

@outfoxxed outfoxxed left a comment

Choose a reason for hiding this comment

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

Bunch of nitpicks. I assume you've gone through and tested everything manually as well, run the lints, etc. If you have a tester file send that along as well and I'll give it a try before merging.

CMakeLists.txt Show resolved Hide resolved
src/x11/i3/ipc/CMakeLists.txt Outdated Show resolved Hide resolved
src/x11/i3/ipc/connection.cpp Outdated Show resolved Hide resolved
src/x11/i3/ipc/connection.hpp Outdated Show resolved Hide resolved
src/x11/i3/ipc/connection.cpp Outdated Show resolved Hide resolved
src/x11/i3/ipc/connection.cpp Outdated Show resolved Hide resolved
src/x11/i3/ipc/connection.cpp Outdated Show resolved Hide resolved
src/x11/i3/ipc/connection.cpp Outdated Show resolved Hide resolved
src/x11/i3/ipc/connection.hpp Outdated Show resolved Hide resolved
src/x11/i3/ipc/qml.hpp Outdated Show resolved Hide resolved
@nydragon
Copy link
Contributor Author

nydragon commented Nov 10, 2024

I have just noticed a crash related to those changes, don't know how to replicate it yet but I would need to fix that before this PR can progress

edit: fixed, was a lot more straightforward than I thought

@outfoxxed
Copy link
Member

outfoxxed commented Nov 10, 2024

Consider using QDataStream transactions instead. As is, if you break halfway through an event then the following event will be invalid and so on. check ipc.cpp for an example

@nydragon
Copy link
Contributor Author

Consider using QDataStream transactions instead. As is, if you break halfway through an event then the following event will be invalid and so on. check ipc.cpp for an example

I ended up doing that with QByteArrays instead, if I encounter a broken message i try to find the next occurrence of the magic sequence and skip to there. However this is under the assumption that the Sway IPC does not write partial messages to the socket, and it does not seem to do so.

@outfoxxed
Copy link
Member

outfoxxed commented Nov 16, 2024

Do we ever encounter broken messages? i3 shouldn't be sending those at all. I think the best solution to read the header would be a QDataStream transaction, which lets you do a partial read pretty easily.

Basically:

stream.startTransaction()
stream.startTransaction()
stream >> magic >> header bits >> size;
if (!stream.commitTransaction()) return;
stream >> data; // assuming you need size to find data. nested commitTransaction works as a barrier for the integrity of values read previosuly
if (!stream.commitTransaction()) return;

You can nest transactions and it works pretty well. This should be more reliable than skipping events. Failing commits roll back the stream, and if there's enough data on the next readyread it should work.

@nydragon nydragon force-pushed the master branch 2 times, most recently from de9da8d to 35191b1 Compare November 17, 2024 02:30
@nydragon
Copy link
Contributor Author

Alrighty, that should be implemented now, here is also a file to test the IPC impl:

Details

import QtQuick
import Quickshell
import Quickshell.I3

ShellRoot {
    PanelWindow {
        id: panel
        Timer {
            id: timer
        }
        Component.onCompleted: {
            const program = "foot"; // change to an installed program
            const longLog = false; // toggle logging of workspace & monitor objects
            const stringify = v => JSON.stringify(v, null, 2);
            console.log("Connecting to", I3.socketPath);
            I3.rawEvent.connect(e => {
                console.log("Event:", e.type);
            });
            I3.focusedWorkspaceChanged.connect(() => {
                console.log("Changed to workspace", I3.focusedWorkspace.name, I3.focusedMonitor.focusedWorkspace.name);
                console.log("Focused monitor's focused workspace and actual focused workspace should be the same:");
                console.log(`${I3.focusedMonitor.focusedWorkspace.name} and ${I3.focusedWorkspace.name}`);
            });
            I3.focusedMonitorChanged.connect(() => {
                console.log("Changed to monitor", I3.focusedMonitor.name);
            });
            for (const monitor of I3.monitors.values) {
                console.log("Monitor:", monitor.name);
                longLog && console.log(stringify(monitor));
            }
            for (const workspace of I3.workspaces.values) {
                console.log("Workspace:", workspace.name);
                longLog && console.log(stringify(workspace));
                I3.dispatch(`workspace ${workspace.num}`);
            }
            I3.dispatch(`workspace 13`);
            console.log(`Changing workspace response: ${stringify(I3.dispatch(`exec ${program}`))}`);
            console.log(`Bad request response: ${stringify(I3.dispatch(`quickshell`))}`);
            I3.dispatch(`workspace 1`);
            I3.refreshWorkspaces();
            I3.refreshMonitors();
            console.log(stringify(I3.monitorFor(panel.screen)));
        }
    }
}

@outfoxxed
Copy link
Member

outfoxxed commented Nov 17, 2024

Until you commit the transaction you shouldn't access data that is part of it, as it might not have been read. Also, rolling back the transaction to read again when you hit something invalid will just hang. On my phone rn, will do a proper code review again later.

@outfoxxed
Copy link
Member

While reviewing, I was going to test it and it crashed instantly under hyprland. It shouldn't work but it shouldn't crash either.
image

Copy link
Member

@outfoxxed outfoxxed left a comment

Choose a reason for hiding this comment

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

Might've missed things again, but this is what I saw this time. Apologies for the repeated re-reviews but I want to be sure quickshell's integrations are stable.

src/x11/i3/ipc/connection.cpp Outdated Show resolved Hide resolved
src/x11/i3/ipc/connection.cpp Outdated Show resolved Hide resolved
src/x11/i3/ipc/connection.cpp Show resolved Hide resolved
src/x11/i3/ipc/connection.cpp Outdated Show resolved Hide resolved
src/x11/i3/ipc/connection.hpp Outdated Show resolved Hide resolved
src/x11/i3/ipc/connection.cpp Outdated Show resolved Hide resolved
src/x11/i3/ipc/connection.cpp Outdated Show resolved Hide resolved
src/x11/i3/ipc/connection.cpp Outdated Show resolved Hide resolved
src/x11/i3/ipc/connection.cpp Outdated Show resolved Hide resolved
src/x11/i3/ipc/monitor.hpp Show resolved Hide resolved
@nydragon
Copy link
Contributor Author

Should be all fixed now

Copy link
Member

@outfoxxed outfoxxed left a comment

Choose a reason for hiding this comment

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

Didn't see anything else at a glance, will give it a more thorough lookover again shortly.

src/x11/i3/ipc/connection.cpp Outdated Show resolved Hide resolved
src/x11/i3/ipc/connection.cpp Outdated Show resolved Hide resolved
@outfoxxed
Copy link
Member

We shouldn't be trying to write to the socket unless we're actually connected. I think hyprland IPC also does this though, now that I think about it. Will fix that later.

build/src/quickshell -p testi3.qml
  INFO: Launching config: "/home/admin/programming/outfoxxed/quickshell/i3/testi3.qml"
  INFO: Shell ID: "c4c77744f223921c7870881fbb46b0a8" Path ID "c4c77744f223921c7870881fbb46b0a8"
  INFO: Saving logs to "/run/user/1000/quickshell/by-id/jij3guvdns/log.log"
  WARN quickshell.I3.ipc: $I3SOCK is unset. Trying $SWAYSOCK.
  WARN quickshell.I3.ipc: $SWAYSOCK and I3SOCK are unset. Cannot connect to socket.
 DEBUG qml: Connecting to
  WARN: QIODevice::write (QLocalSocket): device not open
  WARN: QIODevice::write (QLocalSocket): device not open
 DEBUG qml: Changing workspace response: undefined
  WARN: QIODevice::write (QLocalSocket): device not open
 DEBUG qml: Bad request response: undefined
  WARN: QIODevice::write (QLocalSocket): device not open
  WARN: QIODevice::write (QLocalSocket): device not open
  WARN: QIODevice::write (QLocalSocket): device not open
 DEBUG qml: null
  INFO: Configuration Loaded

@outfoxxed
Copy link
Member

Failing lints

/home/admin/programming/outfoxxed/quickshell/i3/src/x11/i3/ipc/qml.cpp:3:1: error: included header qjsonarray.h is not used directly [misc-include-cleaner,-warnings-as-errors]
    3 | #include <qjsonarray.h>
      | ^~~~~~~~~~~~~~~~~~~~~~~
    4 | #include <qobject.h>
/home/admin/programming/outfoxxed/quickshell/i3/src/x11/i3/ipc/connection.cpp:96:34: error: class member accesses must explicitly use thisptr [tidyfox-explicit-thisptr,-warnings-as-errors]
   96 |         for (auto& [type, data]: I3Ipc::parseResponse()) {
      |                                         ^
      |                                         this->
/home/admin/programming/outfoxxed/quickshell/i3/src/x11/i3/ipc/connection.cpp:135:7: error: no header providing "strncmp" is directly included [misc-include-cleaner,-warnings-as-errors]
    3 |                 if (strncmp(buffer.data(), MAGIC.data(), 6) != 0) {
      |                     ^
/home/admin/programming/outfoxxed/quickshell/i3/src/x11/i3/ipc/connection.cpp:377:13: error: method 'handleRunCommand' can be made static [readability-convert-member-functions-to-static,-warnings-as-errors]
  377 | void I3Ipc::handleRunCommand(I3IpcEvent* event) {
      |             ^
/home/admin/programming/outfoxxed/quickshell/i3/src/x11/i3/ipc/connection.cpp:380:3: error: variable 'success' of type 'bool' can be declared 'const' [misc-const-correctness,-warnings-as-errors]
  380 |                 bool success = obj["success"].toBool();
      |                 ^
      |                      const
/home/admin/programming/outfoxxed/quickshell/i3/src/x11/i3/ipc/connection.cpp:383:4: error: variable 'error' of type 'QString' can be declared 'const' [misc-const-correctness,-warnings-as-errors]
  383 |                         QString error = obj["error"].toString();
      |                         ^
      |                                 const

@nydragon
Copy link
Contributor Author

Fixed

sway: add urgent and focused dispatchers to workspaces

flake: add sway toggle

WIP sway: add monitor status

sway: handle multiple ipc events in one line

sway: reuse socket connection for dispatches & better command type handling

WIP sway: add associated monitor to a workspace

i3/sway: update to allow for i3 compatibility

i3/sway: manage setting the focused monitors

i3/sway: fix multi monitor crash

i3/sway: fix linting errors

i3/sway: update nix package flag naming to i3

i3/sway: add documentation, fix module.md and impl monitorFor

i3/sway: handle more workspace ipc events

i3/sway: fix review

i3/sway: fix crash due to newline breaking up an IPC message

i3/sway: handle broken messages by forwarding to the next magic sequence

i3/sway: break loop when buffer is empty

i3/sway: fix monitor focus & focused monitor signal not being emitted

i3/sway: use datastreams instead of qbytearrays for socket reading

i3/sway: fix lint issues

i3/sway: drop second socket connection, remove dispatch return value, recreate IPC connection on fatal error

i3/sway: handle run_command responses

i3/sway: remove reconnection on unknown event

i3/sway: fix formatting, lint & avoid writing to socket if connection is not open
Copy link
Member

@outfoxxed outfoxxed left a comment

Choose a reason for hiding this comment

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

Thanks!

@outfoxxed outfoxxed merged commit 31adcaa into quickshell-mirror:master Nov 24, 2024
20 checks passed
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.

2 participants