How to coordinate foreground/background windows between 2 processes?
first of all, I'm totally new to QT and new to the project I'm working on as well. But I'm a fast learner.
I have an application with multiple processes, but 2 of them are of particular interest:
- P1: Utility process that starts and monitors the other processes of the app. If one of them dies, it starts it again. But it also has this particularity: It displays a splash screen in full screen. The splash screen should be displayed as the background layer, all the time.
- P2: Main process that displays the instrument panel in full screen. The instrument panel should be displayed as the foreground layer. But this process has this particularity: After it is started, it takes 30 sec before the instrument panel is displayed (yes, the initialization takes that long).
- P3, P4: The other processes do not interact with the display.
The expected behavior is this one:
- On my system (Linux-based):
- After booting up, P1 is the first process that is started once the OS has finished initializing.
- P1 displays its splash screen and starts P2, P3, P4.
- The normal user cannot start any other executable.
- The normal user cannot interact with neither a keyboard nor a mouse. Only a touch screen. (it's an embedded system)
- As a developer I have access to a keyboard and a shell though.
- When P2 finishes its initialization it displays the instrument panel, which should hide the splash screen.
- If P2 dies, the splash screen will automatically reappear during the time P1 restarts P2 and for P2 to finish its initialization again.
- Then P2 will display the instrument panel again, which should hide the splash screen. And so on.
However what happens in the reality is that the splash screen is always the only thing visible. When P2 finishes its initialization, I cannot see the instrument panel. How do I know if P2 really displays its panel? Well, if I kill P1 I can then see the instrument panel! I've tried to put as many raise() calls in P2 everywhere as possible, and as many lower() calls in P1 everywhere as possible, but nothing works.
At this point I understand that raise()/lower() probably only works within a single process and has no control over the windows of the other processes. I'd like a confirmation about this. I don't know if QT is able to negotiate the window layering between multiple processes?
I also suspect that it has something to do with the window manager's focus policy. Maybe I am fighting against the window manager?
At this point, I don't know what to do in order to get the app to work as expected. I need your help.
(Note that our code based can be built and run on a Windows platform as well. I never tried it yet but intend to do it soon enough!)
Hi! You said P1 starts P2 and also knows when it dies. So why don't you simply hide the splash screen during P2's lifetime?
@Wieland: Thank you for your help. Yes, P1 may know when P2 dies and when it is running, but P1 does not when P2 is actually displaying the instrument panel. If P1 hides the splash screen while P2 is initializing, there will be a white background. This is not acceptable (according to the reqs). Note also that the P2 initialization period is not set in stone either; it may change in the future.
I know that P2 could send a signal to P1 every time but this solution seems over-complicated wrt making sure a splash window is positioned in the back-end of the screen once after booting up.
That being said, I have a hard time believing that QT is not able to manage a top-front and a back-end windows. However I can understand that maybe it is not able to do it between 2 different processes. That is where I need someone to confirm, and also where I need to learn more about it. But knowing if it cannot be done would save me some precious time...
Hi and welcome to devnet,
Which version of Qt are you using for that project ?
@SGaist : Thank you for your help. Sorry to have omitted this essential info:
This is the QtCore library version 4.8.5 Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). Contact: http://www.qt-project.org/legal Build key: i386 linux g++-4 full-config Compat build key: | i686 Linux g++-4 full-config | Build date: 2014-03-10 Installation prefix: /opt/qt-4.8.5 Library path: /opt/qt-4.8.5/lib Include path: /opt/qt-4.8.5/include Processor features: mmx sse sse2 cmov sse3 ssse3 sse4.1 sse4.2
Are you using a X server or Qt for Embedded Linux ?
@SGaist : As far as I can see, it is a X server, running on Yocto.
Ok, because your setup looks like you could use Qt for Embedded Linux and QWS which would give you a simple window manager that would likely handle your use case in a simpler manner.
How is the splash screens shown full screen? If you use
QWidget::showFullScreenthen I think that sets a number of window flags (stay on top comes to mind so it won't release the focus to any other widget/window. I suggest trying a "pseudo" full screen by manually moving and resizing the splash to the correct rect. E.g. (untested):
QWidget * splash; // The splash screen splash->setWindowFlags(Qt::WindowStaysOnBottomHint); // So it's under stuff splash->setGeometry(QDesktopWidget::screenGeometry()); splash->show(); // Show regular window
@SGaist : I understand. But I don't own the project and it involves a very big framework that encapsulates P2's main view (the instrument panel). You see, the tests on the product have all passed, and only few minor issues still irritate the product owner. One of it is the absence of the splash screen (without it the screen is white for too long, and it is not acceptable). So replacing the X server by something else would be considered a high risk at this phase.
That being said, with all the attempts I did until now, I think I'm fighting against the window manager. Here's why I say that: When P2 wants to display the instrument panel (after its initialization), I always see a glitch on the screen for a fraction of a second. I believe this glitch is P2 that wants to show/raise its view. I believe something else prevents P2's view to be moved on top or get the focus (despite the multiple raise() calls I placed everywhere in P2's code). I believe this entity is the window manager that forces the splash screen to stay on top.
Then maybe a silly question: wouldn't it be simpler to just set a background on your X session ?
@SGaist : Exactly what I thought at the beginning of my investigation! But the splash screen is not static: there is some text animation (rolling dots). So even if the image is in background, I'll still need a "transparent" window for the text animation, which I fear will still be maintained up-front by the wm. Not an easy one!
At this point, I'll start looking at a simple and portable peer-to-peer IPC mechnanism where P2 will notify "something" and that something will be P1. And once P1 notified, it will hide or close the splash screen. As far as I can see, a boost message_queue should do it.
This is basically what @Wieland suggested at the beginning, but in the mean time I could not find an appropriate call back in the big framework and have to conclude at this point that the problem I have is most probably caused by the window manager.
I just finished implementing the hide-and-seek communications between P1 and P2 (I used a boost's message queue).
However I have another problem when P1's window is up. Normally P1's window is hidden. But it can be activated via a P2's secret menu (known by the maintenance team). This window is supposed to be always on top.
In that window, one thing that is possible to do is to stop (kill) and restart P2 via a button. But when P2 is killed, the splash screen comes up and hide everything, including P1's window. But since P1 killed P2 on purpose, it waits for the user to click the button again (that is a use-case). So it is impossible to restart P2 because the user cannot see/access P1's window... It's a nightmare.
What I can understand from QSplashScreen is that it works well when the main window is in the same process. Indeed, calling finish(mainWindow) seems to solve a bunch of problems.
Is it possible to (easily) replace QSplashScreen or at least remove some flags that makes it appear "always on top"?
Also, looking at the Qt documentation, I can read that a QWidget that has a parent can become a window by setting the Qt::WA_Window flag. Maybe that's the way to go in my case? I don't know however if I will have to invent the wheel again in order to manage such a bare object...
Or maybe as @SGaist suggested, moving the splash screen image to the screen background suddenly becomes an viable alternative.