- Notifications
You must be signed in to change notification settings - Fork 53
Add i3/Sway support #8
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
Conversation
Looks largely copied from hyprland ipc at this point so I figured I'd mention a few things:
|
Thanks for the feedback, I wanted to stay mainly in line with the existing code, but I do have some questions:
|
Also make sure you have a way to turn a sway monitor into a qs monitor. |
c061ef6
to e899eb4
Compare 4a93776
to 30aa969
Compare You've force pushed a lot, this ready for review? |
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 |
There was a problem hiding this 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.
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 |
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. |
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. |
de9da8d
to 35191b1
Compare 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))); } } } |
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. |
There was a problem hiding this 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.
Should be all fixed now |
There was a problem hiding this 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.
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.
|
Failing lints
|
Fixed |
449327b
to 2571766
Compare 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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
This is because QTextStream is not thread safe, as witnessed by this ASAN catch: | #0 0x7ffff78fb648 in realloc.part.0 (/lib64/libasan.so.8+0xfb648) (BuildId: 62576d6cef8744b024a915995656ecd4afeb4351) | quickshell-mirror#1 0x7ffff44fa2c7 in QArrayData::reallocateUnaligned(QArrayData*, void*, long long, long long, QArrayData::AllocationOption) (/lib64/libQt6Core.so.6+0x2fa2c7) (BuildId: c21ff2de250ed0aba68ae0250f9a0d1c455f9fd3) | quickshell-mirror#2 0x7ffff44d025c in QArrayDataPointer<char16_t>::reallocateAndGrow(QArrayData::GrowthPosition, long long, QArrayDataPointer<char16_t>*) (/lib64/libQt6Core.so.6+0x2d025c) (BuildId: c21ff2de250ed0aba68ae0250f9a0d1c455f9fd3) | quickshell-mirror#3 0x7ffff44ca21e in QString::append(QLatin1String) (/lib64/libQt6Core.so.6+0x2ca21e) (BuildId: c21ff2de250ed0aba68ae0250f9a0d1c455f9fd3) | quickshell-mirror#4 0x7ffff4484bec in QTextStream::operator<<(QLatin1String) (/lib64/libQt6Core.so.6+0x284bec) (BuildId: c21ff2de250ed0aba68ae0250f9a0d1c455f9fd3) | quickshell-mirror#5 0x6dec0e in qs::log::LogMessage::formatMessage(QTextStream&, qs::log::LogMessage const&, bool, bool, QString const&) /home/dan/dev/gh/da-x/quickshell/src/core/logging.cpp:101 | quickshell-mirror#6 0x6dfaa3 in qs::log::LogManager::messageHandler(QtMsgType, QMessageLogContext const&, QString const&) /home/dan/dev/gh/da-x/quickshell/src/core/logging.cpp:166 | quickshell-mirror#7 0x7ffff4326319 in qt_message_print(QtMsgType, QMessageLogContext const&, QString const&) (/lib64/libQt6Core.so.6+0x126319) (BuildId: c21ff2de250ed0aba68ae0250f9a0d1c455f9fd3) | quickshell-mirror#8 0x7ffff432c2dd in qt_message_output(QtMsgType, QMessageLogContext const&, QString const&) (/lib64/libQt6Core.so.6+0x12c2dd) (BuildId: c21ff2de250ed0aba68ae0250f9a0d1c455f9fd3) | quickshell-mirror#9 0x7ffff433e384 in QDebug::~QDebug() (/lib64/libQt6Core.so.6+0x13e384) (BuildId: c21ff2de250ed0aba68ae0250f9a0d1c455f9fd3) | quickshell-mirror#10 0x7ffff44dfd55 in QScopedScopeLevelCounter::QScopedScopeLevelCounter(QThreadData*) (/lib64/libQt6Core.so.6+0x2dfd55) (BuildId: c21ff2de250ed0aba68ae0250f9a0d1c455f9fd3) | quickshell-mirror#11 0x7ffff43af631 in QCoreApplication::notifyInternal2(QObject*, QEvent*) (/lib64/libQt6Core.so.6+0x1af631) (BuildId: c21ff2de250ed0aba68ae0250f9a0d1c455f9fd3) | quickshell-mirror#12 0x7ffff43b344d in QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) (/lib64/libQt6Core.so.6+0x1b344d) (BuildId: c21ff2de250ed0aba68ae0250f9a0d1c455f9fd3) | quickshell-mirror#13 0x7ffff46e2b86 in postEventSourceDispatch(_GSource*, int (*)(void*), void*) (/lib64/libQt6Core.so.6+0x4e2b86) (BuildId: c21ff2de250ed0aba68ae0250f9a0d1c455f9fd3) | quickshell-mirror#14 0x7ffff130cf80 in g_main_context_dispatch_unlocked.lto_priv.0 (/lib64/libglib-2.0.so.0+0x5bf80) (BuildId: 8eb8a9a2c6a8fd0b4f3a00d75e20274c6c52e805) | quickshell-mirror#15 0x7ffff136cc67 in g_main_context_iterate_unlocked.isra.0 (/lib64/libglib-2.0.so.0+0xbbc67) (BuildId: 8eb8a9a2c6a8fd0b4f3a00d75e20274c6c52e805) | quickshell-mirror#16 0x7ffff130e35f in g_main_context_iteration (/lib64/libglib-2.0.so.0+0x5d35f) (BuildId: 8eb8a9a2c6a8fd0b4f3a00d75e20274c6c52e805) | quickshell-mirror#17 0x7ffff46e22f2 in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (/lib64/libQt6Core.so.6+0x4e22f2) (BuildId: c21ff2de250ed0aba68ae0250f9a0d1c455f9fd3) | quickshell-mirror#18 0x7ffff43bdcc9 in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) (/lib64/libQt6Core.so.6+0x1bdcc9) (BuildId: c21ff2de250ed0aba68ae0250f9a0d1c455f9fd3) | quickshell-mirror#19 0x7ffff44e0c54 in QThread::exec() (/lib64/libQt6Core.so.6+0x2e0c54) (BuildId: c21ff2de250ed0aba68ae0250f9a0d1c455f9fd3) | quickshell-mirror#20 0x7ffff458725e in QThreadPrivate::start(void*) (/lib64/libQt6Core.so.6+0x38725e) (BuildId: c21ff2de250ed0aba68ae0250f9a0d1c455f9fd3) | quickshell-mirror#21 0x7ffff785e259 in asan_thread_start(void*) (/lib64/libasan.so.8+0x5e259) (BuildId: 62576d6cef8744b024a915995656ecd4afeb4351) | quickshell-mirror#22 0x7ffff4cbbb67 in start_thread (/lib64/libc.so.6+0x94b67) (BuildId: 9488b7bc91c299f62d05325020fbee9cfaf229bc) | quickshell-mirror#23 0x7ffff4d2c6bb in __clone3 (/lib64/libc.so.6+0x1056bb) (BuildId: 9488b7bc91c299f62d05325020fbee9cfaf229bc) | |0x50c00008fe00 is located 0 bytes inside of 128-byte region [0x50c00008fe00,0x50c00008fe80) |freed by thread T2 (QDBusConnection) here: | #0 0x7ffff78fb648 in realloc.part.0 (/lib64/libasan.so.8+0xfb648) (BuildId: 62576d6cef8744b024a915995656ecd4afeb4351) | quickshell-mirror#1 0x7ffff44fa2c7 in QArrayData::reallocateUnaligned(QArrayData*, void*, long long, long long, QArrayData::AllocationOption) (/lib64/libQt6Core.so.6+0x2fa2c7) (BuildId: c21ff2de250ed0aba68ae0250f9a0d1c455f9fd3) | |previously allocated by thread T1 (QThread) here: | #0 0x7ffff78fb648 in realloc.part.0 (/lib64/libasan.so.8+0xfb648) (BuildId: 62576d6cef8744b024a915995656ecd4afeb4351) | quickshell-mirror#1 0x7ffff44fa2c7 in QArrayData::reallocateUnaligned(QArrayData*, void*, long long, long long, QArrayData::AllocationOption) (/lib64/libQt6Core.so.6+0x2fa2c7) (BuildId: c21ff2de250ed0aba68ae0250f9a0d1c455f9fd3) | |Thread T1 (QThread) created by T0 here: | #0 0x7ffff78f40a3 in pthread_create (/lib64/libasan.so.8+0xf40a3) (BuildId: 62576d6cef8744b024a915995656ecd4afeb4351) | quickshell-mirror#1 0x7ffff45868e8 in QThread::start(QThread::Priority) (/lib64/libQt6Core.so.6+0x3868e8) (BuildId: c21ff2de250ed0aba68ae0250f9a0d1c455f9fd3) | quickshell-mirror#2 0x6e1049 in qs::log::LogManager::init(bool, bool, bool, QtMsgType, QString const&, QString const&) /home/dan/dev/gh/da-x/quickshell/src/core/logging.cpp:261 | quickshell-mirror#3 0x46a94f in qs::launch::runCommand(int, char**, QCoreApplication*) /home/dan/dev/gh/da-x/quickshell/src/launch/command.cpp:500 | quickshell-mirror#4 0x445373 in qs::launch::main(int, char**) /home/dan/dev/gh/da-x/quickshell/src/launch/main.cpp:112 | quickshell-mirror#5 0x4374b5 in main /home/dan/dev/gh/da-x/quickshell/src/main.cpp:3 | quickshell-mirror#6 0x7ffff4c5130d in __libc_start_call_main (/lib64/libc.so.6+0x2a30d) (BuildId: 9488b7bc91c299f62d05325020fbee9cfaf229bc) | |Thread T2 (QDBusConnection) created by T0 here: | #0 0x7ffff78f40a3 in pthread_create (/lib64/libasan.so.8+0xf40a3) (BuildId: 62576d6cef8744b024a915995656ecd4afeb4351) | quickshell-mirror#1 0x7ffff45868e8 in QThread::start(QThread::Priority) (/lib64/libQt6Core.so.6+0x3868e8) (BuildId: c21ff2de250ed0aba68ae0250f9a0d1c455f9fd3) | quickshell-mirror#2 0x7ffff613a166 in QDBusConnectionManager::instance() (/lib64/libQt6DBus.so.6+0x3d166) (BuildId: 35a23e05853374dafc6df118df20e91852bc6344) | |SUMMARY: AddressSanitizer: double-free (/lib64/libasan.so.8+0xfb648) (BuildId: 62576d6cef8744b024a915995656ecd4afeb4351) in realloc.part.0 |==3529858==ABORTING |[Thread 0x7fffd40f56c0 (LWP 3529873) exited] |[Thread 0x7fffd53ff6c0 (LWP 3529872) exited] |[Inferior 1 (process 3529858) exited with code 01]
This pr adds support for retrieving data from Sway's IPC.
Features:
Additional Todo: