"Open with" in Qt5 application

I am trying to get the “Open with” feature working for an application written in Qt5 (MusicBrainz Picard). When I right click a supported file and choose “Open with… > Picard” the application launches, but it does not receive the file path. Apparently in Haiku the path is not passed as an argument but by some other means.

I know basically nothing about the Haiku / BeOS API, but looking at BApplication there are two candidates I think could be used for getting the file from “Open with”: ArgvReceived and RefsReceived. Is this right? Which one? My guess goes to ArgvReceived since RefsReceived is handled by qthaikuplugin already and should do the correct thing.

Can I get the file passed to “Open with” using Qt5 somehow? Or would this need to be implemented in qthaikuplugins first by implementing ArgvReceived (if that’s actually the responsible handler)?

Does your application handle QFileOpenEvent? (Many don’t.)

I would be very surprised if it’s sent to Argv and not Refs, that would sound like a bug in Tracker or the Roster to me.

1 Like

Also note that you can use the “spybmessage” utility to watch all messages sent to an application, this should allow you to see exactly what is going on.

2 Likes

OpenWith (and double click in Tracker) works fine for Qt5 application. It works for many text editors (Kate, KWrite, Notepadqq, etc), media players (qmmp, audacious, qmplay2) and browsers (qupzilla, otter). But it doesn’t work for pyqt5 apps, because there is no handler for the refs recieved event in python BApplication. In my opinion, the only way to implement this is to write a native launcher. Launcher will receive REFS and run python apps with command line parameters.

2 Likes

Thanks both of you for the clarifications. Yes, the app handles QFileOpenEvent. But I naively assumed having this even handling in qthaikuplugin would make this also available to PyQt based apps.

But implementing a small warapper BApplication sounds like a good plan to handle that. I think it also resolves some other issues (e.g. better setting of resources for the file), having a Python script as the main launcher for a Haiku app does not seem to be ideal. In the end this would be similar to how launching the app already works on Windows or macOS.

I will look into this, it also gives me some possibility to learn a bit abut the native Haiku API :wink:

1 Like

Just for me to understand this correctly: In a C++ Qt app the qthaikuplugin does create a BApplication, but in PyQt5 the creation of the BApplication is already handled by Python itself, and not by qthaikuplugins or even PyQt5, is this right? And qthaikuplugins detects this and reuses the BApplication instance already present?

Where exactly is this BApplication instance created then? And doesn’t this also cause other issues? E.g. in qhaikuintegration_haiku.cpp the haiku integration connects to the applicationQuit signal, but that’s only provided by the custom HQApplication implementation.

I started looking more deeply into this issue. In general there is actually not much special about PyQt app and I probably misunderstood your previous comment. The BApplication is just created as expected by the qthaikuplugins and in general the event handling also works.

But it looks like we have a collection of issues at play :slight_smile:

  1. My PyQt5 application was doing some stupid things with argv, breaking the detection of the proper app signature. That meant the B_REF_RECEIVED never reached the proper BApplication. This was easy to fix.

  2. Even with the fix in place, current qthaikuplugins always ignores the first B_REF_RECEIVED the application received. A proposed fix is at QPA: Fix first RefsReceived call being ignored by phw · Pull Request #14 · threedeyes/qthaikuplugins · GitHub

With the first two issues fixed dropping supported files on the deskbar icon works as expected and the application receives the QFileOpenEvent. But there is

  1. “Open” or “Open with” from tracker still has no effect. It does not even call BApplication::RefsReceived. So far all my debugging was unsuccessful. Once the app is open I can again drop on the deskbar icon, proofing that the general setup of the B_REF_RECEIVED is ok, but it does not receive any B_REF_RECEIVED from the initial open action. I wonder if this is related to Need to implement applicationFilePath() · Issue #9 · threedeyes/qthaikuplugins · GitHub

I tested with audacious and clementine, it doesn’t work there at all :frowning: Not even with my patched qthaikuplugins. Even the drop on deskbar icon action doesn’t work (even though the icon indicates a drop target).

Did spybmessage help at all?

It’s initialization app timeline for now:
%D0%91%D0%B5%D0%B7%D1%8B%D0%BC%D1%8F%D0%BD%D0%BD%D1%8B%D0%B9

The problem block is highlighted in red.
Many applications do not use QFileOpenEvent for open files, but use command line argv[] parameters.

1 Like

Partially :slight_smile: It helped me understand the messages on other applications. But for debugging my PyQt app I ended up patching qthaikuplugins to write debug output to a file.

Thanks. Turns out Audacious is actually working fine now after a fresh boot, not sure what was happening. Same is true for my PyQt5 app, open with works, but only with the patch from QPA: Fix first RefsReceived call being ignored by phw · Pull Request #14 · threedeyes/qthaikuplugins · GitHub applied.

Can you take a look at the patch? I’m probably missing something here, but I see no reason why it should block the very first B_REF_RECEIVED message or do the obscure dance at https://github.com/threedeyes/qthaikuplugins/blob/master/platforms/qhaikuplatform/haiku/qhaikuintegration_haiku.cpp#L177 . Bu it’s your code :smiley:

I test it and i have a problem. Without prevention first B_REF_RECEIVED i have twice opening files. KWrite for example. Open any text file with KWrite and this file will be opened twice.

Ok, I now see how this is working out. So the existing code tries to turn the first ref send to the application into a command line argument by waiting for the first ref to arrive, suppressing the event for it and copying it over to argv.

I think we should remove this workaround, IMHO it causes more trouble then it solves:

  1. It is not reliable. The application might have already parsed the argv before, thus not receiving the file even if the application does otherwise the right thing (listening to FileOpen events). Cases where argv is parsed before Qt gets initialized include Picard and Audacious¹

  2. It works only for a single file. As soon as one selects multiple files for “Open with” it breaks unless the application implements FileOpen. And if it implements FileOpen this hack is not needed anyway.

  3. It does not work for the case where the user drops a supported file type on the deskbar, this will always require FileOpen support.

I think the proper way would be to remove this workaround and instead see to patch affected applications for FileOpen support. I have updated my pull request to remove the workaround completely.

If you think we should still keep it I would suggest making it opt-in instead. E.g. we could set an attribute (e.g. QT:REF_TO_ARGV) on the executable and only apply the workaround if this attribute is set. Or if you really want to have it default, at least have a way to opt-out of this behavior. What do you think?


¹ I previously reported Audacious would be working, but that was not true. I fooled myself because Audacious as a player remembers the files already opened. Actually Audacious does not work with the current approach, as it parses argv to early, and also not with the patched version, as it does not handle FileOpen. Proper thing would be to patch Audacious for FileOpen support.

3 Likes

Patching/requesting other Qt5 apps support that event sounds good to me, yes. We like deep, systemic solutions around here instead of work-arounds :slight_smile:

6 Likes

For starters I will see if I can look into providing a patch for Audacious next week. This is a kind of change projects might also accept upstream, especially if they have some ambition to also support macOS.

I test many Qt5 (and KF5) apps without workaround (with your PR).
OpenWith from Tracker worked for: Kate, Kwrite, QtCreator
Not worked: Calligra, LibreOffice, Audacious, QMMP, QMPlay2, Avidemux, Gwenview, Otter, Qupzilla, Notepadqq, Kwave, etc.

I think will be problematic to add QFileOpenEvent support to all these applications. We need a simple solution imho.

I would like to see a simple solution, but the current hack unfortunately isn’t one :frowning: It has numerous issues:

  1. It doesn’t work for applications which interpret argv before initializing Qt app (Picard, Audacious)
  2. It breaks handling of FileOpen for the first ref passed for “well behaved” apps
  3. It works only with a single ref in B_MULTIPLE_LAUNCH mode
  4. The code is not save and has side effects. E.g. I actually get an exception in a Python module imported after the argv hackery which tries to access sys.argv if the app is launched from tracker and the workaround is active. I haven’t debugged this fully, but it is clear that something is broken by the argv manipulation.

To emphasize the last point, what this code basically does is something like this:

void updateArgs(int& argc, char** argv) {
  if (argc = 1) {
    argc = 2;
    argv[1] = strdup("somepath");
    argv[2] = 0; // This actually overwrites the first environment variable provided to the app
  }
}

void main(int argc, char** argv) {
    updateArgs(argc, argv);
    // ....
}

This is not a safe thing to do, it writes outside of the space allocated for argv. In practice this will actually overwrite the first environment variable, which are placed in memory behind argv. But this is an implementation detail. And it is even unpredictable. When I ran this it overwrote LC_MONETARY variable on my system. Could be anything else, could be something different next time something changes. Could crash the app or cause any kind of unwanted side effect. Who knows.

I know that my workaround is bad solution, but I don’t see how it can be implemented.
The only thing that comes to my mind is a separate launcher for applications. But this is also a bad solution.

Perhaps as a temporary solution (until we come up with a better one) I will implement a solution with an extended attribute in a binary file. The workaround will be disabled by default. If you add an attribute (QT: REF_TO_ARGV as you suggested) it will intercept all refs, form a new ARGV array, and not replace elements in an existing array)

1 Like

Sounds good I think.

How will it be passed on reliable to the application?

Another thought I had, but this goes a bit outside of the scope here, is whether Haiku itself could implement some launch flag for this. If that flag is set Tracker would pass the file paths as arguments instead of sending ref messages. This would allow applications without the messaging support to work with the Tracker open feature.

I originally expected B_ARGV_ONLY to trigger something like this, but this only suppresses the messages? Not sure what it does really :smiley:

@waddlesplash Do you think this is this something that could be considered?

From BeBook: B_ARGV_ONLY: The application can’t receive messages. Information can be passed to it at launch only, in an array of argument strings (as on the command line).