GNOME Bugzilla – Bug 96772
Startup notification patch
Last modified: 2011-02-04 16:11:52 UTC
Patch to implement startup notification. This uses the currently-proposed spec, but the API is appropriate for any of the specs that have been proposed so far. I've searched for the win32 API for this to check portability, but haven't had any luck. Probably I'm not searching for the right thing as I don't know what they call the feature.
Created attachment 11821 [details] [review] initial patch
From: Francois Gouget <fgouget@codeweavers.com> Subject: Re: startup notification To: Havoc Pennington <hp@redhat.com> CC: xdg-list@freedesktop.org Date: Fri, 25 Oct 2002 02:44:34 -0700 Havoc Pennington wrote: [...] > cvs.freedesktop.org has this document under > startup-notification/doc/startup-notification.txt; the module also > contains "libsn" which is a reference implementation. You asked how Windows does this in another email. I don't really know, however there is the following API: DWORD WINAPI WaitForInputIdle(HANDLE hProcess, DWORD dwTimeOut); From the MSDN: (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/waitforinputidle.asp) > The WaitForInputIdle function waits until the specified process is > waiting for user input with no input pending, or until the time-out > interval has elapsed. Here 'no input pending' is when the message queue of the process's main thread is empty. It does not take into account data that may be pending on file descriptor or other stuff like that. So here is how I think 'startup notification' is implemented: when you start an application using Explorer, it calls CreateProcess to start it. Then it setups up a busy cursor and calls the above API with a timeout of 30 seconds or so. Finally it restores the normal cursor as soon as the API returns.
The interaction of this with --screen is really a mess. Sending the notification to the screen given by --screen would require the launching application to understand the --screen option, but sending it to the default screen from $DISPLAY would require parsing $DISPLAY to figure out what screen that was. (Yes, --screen isn't meant for interactive use, but it actually is quite useful for interactive use ... imagine creating a launcher to launch your 8-bit only app on the right screen, so I suspect that people will use it occasionally.)
Brain dump on interaction with multihead: Each startup notification sequence needs to be screen-specific. That is, I launch it on a given screen, the tasklist on a given screen notices the launch (tasklists are per-screen), the window manager sets the root window cursor for a given screen, the app then comes up on a given screen. I think it's broken if the "given screen" in any of those cases is different, a sequence is for a specific screen, that's how all our code (and I think KDE's) has been written, and is the only real way to write the code without breaking the EWMH in major ways and confusing the hell out of ourselves. Also, it's what people want; e.g. workspaces are per-screen because people use multihead for things like keeping a trading graph on one head and their office apps on the other, or keeping a video display on one head and their apps on the other. Now the concrete problem is, how do we be sure the "given screen" is known to all the processes involved. To keep it simple let's say the panel (doing the launching), the application, and the task list. From a protocol standpoint, the "initiate startup notification sequence" message can (must?) contain a SCREEN attribute giving the number of the screen the notification is on. This is because we don't know which root window the message was sent to. However the protocol also requires the messages for a sequence to be sent to the root window corresponding to SCREEN. SCREEN is thus provided by the application doing the launching (say the panel) and is not known to the application being launched, as the application-being-launched didn't see the initial message for obvious reasons. Right now we assume the screen in DISPLAY (default screen) is the "screen we were launched on." As currently written the panel is attempting to pass in the screen when it does a launch. cf. Mark McLoughlin's exec_on_screen() set of functions posted a while back. i.e. the panel knows which screen it intends to launch something on. The app picks this up from DISPLAY. If the user adds --screen to a .desktop file, this is going to mean one of two things: 1) the panel sets DISPLAY but --screen overrides it, so the panel has the wrong idea about which screen the app is launching on 2) the panel understands --screen somehow and knows which screen the app is launching on (s/--screen/--display/ and the same things are true) According to the current protocol, 2) will work and 1) will break. Imagine a solution to 1) such as sending all messages to screen 0. The problem is, the panel still needs to know which root window the app is starting on. There's at least two features we have already planned that require this: a) the panel sends the DESKTOP property for the startup sequence, to start the app on the current DESKTOP; but DESKTOP is per-screen, so the panel needs to know which screen the app is on and b) the panel sends the geometry of the launcher in root window coordinates, so an "opening" animation that expands the icon to the new window can be drawn. So sending everything to root 0 keeps us from totally hosing the startup notification, but still we screw up details of it, because fundamentally everything has to know "which screen the app is being launched on." I'm not sure we can get out of having to know that. However this patch makes one assumption beyond "we know which screen we're launched on" which is "at least one window will open on the screen we're launched on", that is it sends the "finished" message only when opening a window on that particular screen. Arguably we should instead send "finished" as soon as we open a window on *any* screen. Or we could say that's a case where the app is supposed to do its own startup notification handling. At least in principle, we should be able to display three independent startup notification sequences for opening three windows from the same app on three different screens. A per-screen GDK API can be mapped to a per-display startup notification protocol by simply ignoring the passed-in screen, the reverse is less true. As a bad hack, we could require setting DESKTOP_STARTUP_SCREEN in addition to DESKTOP_STARTUP_ID when launching an app, so that GDK could detect and handle the case where --screen is out of sync with what the launcher application thought.
There are basically three things here: A) The screen where the user triggered the launch B) The screen on whose root window the client message is exchanged C) The screen(s) where the application opens its windows. Right now, the protocol links A, B, and C together. An application showing application feedback can only determine A) via B). The launchee can only determine B) as a function of C). What you seem to be arguing here is that A) needs to be exposed in the protocol. Which doesn't imply to me that B) and C) need to be tied to it in this way. Practically speaking, multihead is such a fringe case that we probably can get away with getting it sort of wrong, but don't see any particular reason why it buys us much to tie these different screens together.
What I'm saying is: - there's a single screen which is the "screen where the launch occurs" typically identical to A), the screen where the user triggered the launch. - unless everyone knows this "screen where launch occurs" stuff just breaks - thus there's no advantage to using screen 0 for B), vs. the screen where the launch occurs. the argument for screen 0 is that screen 0 is more reliably known than the screen where the launch occured. Well, we have to reliably know the screen where the launch occurred *anyway* - there are disadvantages to using screen 0 for B), namely it makes the window manager and pager code more annoying and less modular, because I have to write extra code to always XSelectInput() on screen 0, and to filter out non-startup-notification events on screen 0. You are hardcoding B) instead of making it track A), but this adds no useful flexibility whatsoever, unless we didn't know A), but we *have* to know A). C) is totally orthogonal to this whole issue on the protocol level. The proposed GTK auto-startup-notification-handling currently only stops notification when opening a window on A), but that could trivially be changed if we want. As we *have* to know A), we may as well avoid hardcoding B) and make B) conveniently sync up to the screen the pager and window manager are already dealing with.
Why I'm proposing hardcoding B) is to keep things simple for the launchee. Currently, there is no way for the launchee to know A), so it can't reliably use it for B). Since I dont' see any reason why the launchee needs to know A), hardcoding it seems simpler than passing it, but you could in fact pass it in an environment variable as well; it doesn't make much of a difference for me within GTK+; what I'd like to avoid is having to make guesses at A) to derive B). I think it's more important to keep things simple for launchees than for launchers; ISV's may have to write their own launchee handling, they are unlikely to write launchers.
I have some knowledge about how Windows handles this. I can't find the reference now, it has passed a lot of time since I did heavy duty Windows development, but I understood it as this: * Explorer launches the app through the Shell API. Reference: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/programmersguide/shell_basics/shell_basics_programming/launch.asp?frame=true&hidetoc=true * A compound busy pointer is shown on the window and only on the window that launched the app. This means that the cursor is shown either on the folder window, desktop, quick launch bar or other. I am not so sure about this one; some testing on a Windows machine would help. * Last, Explorer peeks at the message queue of the new thread. When the first event (or input event, can't remember well) is processed by the thread the pointer is restored to normal. The pointer is also restored if a timeout is exceeded. The main points for the Windows implementation are the ShellExecuteEx() part and its associated SHELLEXECUTEINFO struct, and the peeking of the message queue associated with the "launchee" application by the "launcher". The SHELLEXECUTEINFO struct has a member hMonitor to indicate the screen on which to show the window on multi-head configurations.
> Currently, there is no way for the launchee > to know A), so it can't reliably use it for B). The launchee *has* to know A). That's the point. Perhaps a better way to phrase it, A) really must be the DefaultScreen() for the launchee, so that DESKTOP property, animations, etc. work. If you want to improve the reliability of the launchee knowing A) then OK, but we can't get out of the launchee needing to know A) if we want stuff to work right. All the panel/tasklist/WM interactions are done per-screen right now. I think DefaultScreen() is the most obvious/intuitive thing for launchees to implement, anyhow.
All that said, the GDK API may as well take no arguments: gdk_notify_startup_complete() because it's known that it has to go to default screen on default display in any case. To make it useful for any situation other than initial app startup (say opening a new window), it needs to take a "startup ID" arg in addition to the screen. So we could just do the no-args thing for now.
I think it's perfectly fine in the unusual case of cross-screen launching (A != C) if the user sees a watch cursor and/or task list item on screen A until the app pops up on C. The window manager should have no problem realizing that a cross-screen launch occurred and not doing the animation from the launch location. I also came to the idea that the GDK function probably should be a global function ... startup notification is "per application" and there is no reason to assume a one-to-one correspondence between applications and screens, or even displays. Just making a per-screen function doesn't give you any ability to do multiple apps. The only advantage of making it per-screen or (probably better) per-display would be to handle the weird case where the app wants the startup notification to end when it opens something on the default display, but first opens a window on some other display that the user can't see. I don't see that as worth adding extra complexity that we'd need to deal with public per-screen fnuctions if we decide to later add some real facility for multiple startup notifications.
> I think it's perfectly fine in the unusual case of cross-screen > launching (A != C) if the user sees a watch cursor and/or > task list item on screen A until the app pops up on C. It's not a disaster, but it is broken. The tasklist icon should be on screen C, and the desktop the app is placed on should be the current desktop for C, not the current desktop for screen A. Anyhow, let's just do the global function, that's the right thing. We can sort this out later. I'll do a new patch on Monday.
I'm almost done reworking your patch for global function; I want the functionality in for 1.1.2, which is imminent.
Created attachment 11980 [details] [review] New version of patch
OK, new version of patch attached. Differences is mostly making the notification global not per-screen, and concentrating the code in gdkdisplay-x11.c. I haven't tried to handle add any magic handling of --screen; doing so would require some grotty but straightforward display name parsing code and a couple of gdk <=> gdk/x11 hooks. I'm going to go ahead and commit it, but I don't really have any means of testing (I checked that a client message is sent to the root window, and it doesn't crash.)
Sat Nov 2 00:22:33 2002 Owen Taylor <otaylor@redhat.com> Add startup notification hooks - mostly based on patch by Havoc Pennington in #96772. * gdk/gdk.h gdk/x11/gdkdisplay-x11.c gdk/{win32,linux-fb}/gdkmain-*.c: (gdk_notify_startup_complete): new function that indicates an application has finished starting up. * gdk/x11/gdkmain-x11.c gdk/x11/gdkdisplay-x11.c (_gdk_windowing_set_default_display): store value of DESKTOP_STARTUP_ID on the default screen, and clear it from the environment. * gdk/x11/gdkdisplay-x11.c: Set _NET_STARTUP_ID hint on display's group leader window. * gtk/gtkwindow.c (gtk_window_set_auto_startup_notification): function to toggle whether we automatically broadcast that we've started up, after mapping the first toplevel window. (gtk_window_map): call gdk_screen_notify_startup_complete() by default, unless enabled by above.
OK, Havoc promises to put language in the spec that either: - Makes it clear that GTK+'s current handling of --screen and startup notification is correct Or: - Forbids passing --screen when using startup notification Given that, I think this can be closed now.