[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-core-devel
Subject:    Thoughts on the systray II.
From:       Lubos Lunak <l.lunak () suse ! cz>
Date:       2005-04-15 16:46:32
Message-ID: 200504151846.32493.l.lunak () suse ! cz
[Download RAW message or body]

Hello,

 this is more or less a followup to 
http://lists.kde.org/?l=kde-core-devel&m=110841124008784&w=2 , which died out 
(because of a certain person being a bit busy, I guess :-/ ). I'd like to 
revive and continue with it, and present also some "show me the code".

 To quickly summarize (see the link above for full details), the things I 
suggested were:
1) not using systray for apps minimizing, using another taskbar instead, which 
may in the end even look a lot like a systray from the user's point of view. 
As you'll see below this in practice mainly means replacing KSystemTray and 
the underlying protocol. People in general seemed(?) to like this part, so 
see below about the code.
2) converting applet-like systray apps to real applets. Aaron had some issues 
with this because of having bad nightmares about XEmbed, so this needs more 
discussion, the remaining complains can be basically solved by saying "so 
that needs fixing/improving" if my memory serves me well.
3) improving KNotify so that apps that now use systray just to notify about 
things will stop using systray, will stop rolling their own notification 
implementations, and will simply use KNotify. This seemed to be generally 
like as well.
4) not using the systray for daemons, quick access and such stuff, instead 
simply not having any GUI for those, or using applets/whatever. I'm not 
actually sure what was the opinion on this.

 Ok, now the more interesting part, i.e. the one with patches. See attachments 
with patches for kdelibs/kdecore, kdebase/kicker/applets/systemtray and 
kdebase/kwin . They kind of implement 1), it still needs quite some work and 
they're a bit hackish in some places, but it's good enough for seeing the 
idea in practice. Note that you need current CVS of kdecore for them to work 
properly.

 With the patches now KWin has a new titlebar button which controls whether a 
window should be trayed or not. In order to save me some work the patch 
actually now uses the button for keeping a window below others, you need to 
enable it in the decoration config if you don't see it. Systemtray is patched 
to also provide an icon for such windows. Window have two new flags, 
NET::Trayed and NET::TrayedHidden, first one meaning the window should show 
in the systray, the second meaning it's in the systray and hidden. I.e. 
nothing is set, it's a normal window, only Trayed is set, the window is 
visible, it has an icon in the systray and it has also a taskbar entry, both 
flags are set, it's hidden in the systray and has no taskbar entry. Works for 
any application, blah blah (see the URL above for all the benefits).

 It still needs doing something with the KSystemTray class. First of all 
there's something like this class needed to show the RMB menu, probably some 
KNewTray class that will provide the tray the menu and all the other things 
it might need from the app. And secondly, KSystemTray and the attached 
patches clash in quite ugly ways, so having this in 3.5 would be ... 
interesting from the user's point of view. Basically, the apps again roll too 
many things on their own, so they e.g. block quiting when one closes the main 
window, which means that if KSystemTray functionality is mapped to this new 
functionality, the app may end up running in the background, if KSystemTray 
is left as it is, such apps may have two systray icons. Not that the 
remapping of the KSystemTray functionality would be that trivial without 
dumping it completely and starting anew.

 Anyway, if people will like this, the plan would be something like
- finishing the patches, so that there really is a separate titlebar button, 
that there would be the RMB menu, and who knows what
- deciding on the exact GUI behaviour (which can be enforced to be consistent 
between the apps), e.g. I think the titlebar button should perhaps just put 
the icon into the tray for LMB and additionally also "minimize" the window 
for RMB. Currently one has to toggle the KeepBelow button and then minimize. 
This also includes deciding whether the icons should be in a separate systray 
applet, or should be perhaps in a separate part of the taskbar, or so.
- proving a KSystemTray-like class that'd provide the RMB menu, and perhaps 
icon, tooltip or whatever.
- (the "I'm bored case") making it work somewhat reasonably even for 3.5 
without having two systray icons and so on

 Ok. Comments, flames, threats, cookies, and similar stuff related to this?

 Now going to reply to Aaron's mail[*]. (The "XEMBED" case, AKA "beat Aaron 
until he likes my idea" ;)  ).

[*] http://lists.kde.org/?l=kde-core-devel&m=110841412418641&w=2

-- 
Lubos Lunak
KDE developer
---------------------------------------------------------------------
SuSE CR, s.r.o.  e-mail: l.lunak@suse.cz , l.lunak@kde.org
Drahobejlova 27  tel: +420 2 9654 2373
190 00 Praha 9   fax: +420 2 9654 2374
Czech Republic   http://www.suse.cz/

["kdecore.patch" (text/x-diff)]

--- kdecore/netwm.cpp.sav	2005-04-14 11:36:21.000000000 +0200
+++ kdecore/netwm.cpp	2005-04-14 22:16:52.316133760 +0200
@@ -82,12 +82,15 @@ static Atom net_wm_allowed_actions   = 0
 static Atom wm_window_role           = 0;
 
 // KDE extensions
-static Atom kde_net_system_tray_windows       = 0;
-static Atom kde_net_wm_system_tray_window_for = 0;
 static Atom kde_net_wm_frame_strut            = 0;
 static Atom kde_net_wm_window_type_override   = 0;
 static Atom kde_net_wm_window_type_topmenu    = 0;
 static Atom kde_net_wm_temporary_rules        = 0;
+static Atom kde_net_wm_trayed_windows         = 0;
+static Atom kde_net_wm_state_trayed           = 0;
+static Atom kde_net_wm_state_trayed_hidden    = 0;
+static Atom kde_net_system_tray_windows       = 0;
+static Atom kde_net_wm_system_tray_window_for = 0;
 
 // application protocols
 static Atom wm_protocols = 0;
@@ -227,7 +230,7 @@ static int wcmp(const void *a, const voi
 }
 
 
-static const int netAtomCount = 75;
+static const int netAtomCount = 78;
 static void create_atoms(Display *d) {
     static const char * const names[netAtomCount] =
     {
@@ -304,12 +307,15 @@ static void create_atoms(Display *d) {
 
 	    "_NET_WM_STATE_STAYS_ON_TOP",
 
-	    "_KDE_NET_SYSTEM_TRAY_WINDOWS",
-	    "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR",
 	    "_KDE_NET_WM_FRAME_STRUT",
 	    "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE",
 	    "_KDE_NET_WM_WINDOW_TYPE_TOPMENU",
             "_KDE_NET_WM_TEMPORARY_RULES",
+            "_KDE_NET_WM_TRAYED_WINDOWS",
+            "_KDE_NET_WM_TRAYED",
+            "_KDE_NET_WM_TRAYED_HIDDEN",
+	    "_KDE_NET_SYSTEM_TRAY_WINDOWS",
+	    "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR",
 
 	    "WM_STATE",
 	    "WM_PROTOCOLS"
@@ -390,12 +396,15 @@ static void create_atoms(Display *d) {
 
 	    &net_wm_state_stays_on_top,
 
-	    &kde_net_system_tray_windows,
-	    &kde_net_wm_system_tray_window_for,
 	    &kde_net_wm_frame_strut,
 	    &kde_net_wm_window_type_override,
 	    &kde_net_wm_window_type_topmenu,
             &kde_net_wm_temporary_rules,
+            &kde_net_wm_trayed_windows,
+            &kde_net_wm_state_trayed,
+            &kde_net_wm_state_trayed_hidden,
+	    &kde_net_system_tray_windows,
+	    &kde_net_wm_system_tray_window_for,
 
 	    &xa_wm_state,
 	    &wm_protocols
@@ -586,8 +595,8 @@ NETRootInfo::NETRootInfo(Display *displa
     p->active = None;
     p->clients = p->stacking = p->virtual_roots = (Window *) 0;
     p->clients_count = p->stacking_count = p->virtual_roots_count = 0;
-    p->kde_system_tray_windows = 0;
-    p->kde_system_tray_windows_count = 0;
+    p->trayed_windows = p->kde_system_tray_windows = 0;
+    p->trayed_windows_count = p->kde_system_tray_windows_count = 0;
     setDefaultProperties();
     if( properties_size > PROPERTIES_SIZE ) {
         fprintf( stderr, "NETRootInfo::NETRootInfo(): properties array too large\n");
@@ -634,8 +643,8 @@ NETRootInfo::NETRootInfo(Display *displa
     p->active = None;
     p->clients = p->stacking = p->virtual_roots = (Window *) 0;
     p->clients_count = p->stacking_count = p->virtual_roots_count = 0;
-    p->kde_system_tray_windows = 0;
-    p->kde_system_tray_windows_count = 0;
+    p->trayed_windows = p->kde_system_tray_windows = 0;
+    p->trayed_windows_count = p->kde_system_tray_windows_count = 0;
     setDefaultProperties();
     p->properties[ PROTOCOLS ] = properties;
     // force support for Supported and SupportingWMCheck for window managers
@@ -682,8 +691,8 @@ NETRootInfo::NETRootInfo(Display *displa
     p->active = None;
     p->clients = p->stacking = p->virtual_roots = (Window *) 0;
     p->clients_count = p->stacking_count = p->virtual_roots_count = 0;
-    p->kde_system_tray_windows = 0;
-    p->kde_system_tray_windows_count = 0;
+    p->trayed_windows = p->kde_system_tray_windows = 0;
+    p->trayed_windows_count = p->kde_system_tray_windows_count = 0;
     setDefaultProperties();
     if( properties_size > 2 ) {
         fprintf( stderr, "NETWinInfo::NETWinInfo(): properties array too large\n");
@@ -739,8 +748,8 @@ NETRootInfo::NETRootInfo(Display *displa
     p->active = None;
     p->clients = p->stacking = p->virtual_roots = (Window *) 0;
     p->clients_count = p->stacking_count = p->virtual_roots_count = 0;
-    p->kde_system_tray_windows = 0;
-    p->kde_system_tray_windows_count = 0;
+    p->trayed_windows = p->kde_system_tray_windows = 0;
+    p->trayed_windows_count = p->kde_system_tray_windows_count = 0;
     setDefaultProperties();
     p->client_properties[ PROTOCOLS ] = properties;
     for( int i = 0; i < PROPERTIES_SIZE; ++i )
@@ -762,6 +771,12 @@ NETRootInfo2::NETRootInfo2(Display *disp
 {
 }
 
+NETRootInfo2::NETRootInfo2(Display *display, const unsigned long properties[], int properties_size,
+                int screen, bool doActivate)
+    : NETRootInfo( display, properties, properties_size, screen, doActivate )
+{
+}
+
 NETRootInfo3::NETRootInfo3(Display *display, Window supportWindow, const char *wmName,
 			 unsigned long properties[], int properties_size,
                          int screen, bool doActivate)
@@ -770,6 +785,26 @@ NETRootInfo3::NETRootInfo3(Display *disp
 {
 }
 
+NETRootInfo3::NETRootInfo3(Display *display, const unsigned long properties[], int properties_size,
+                int screen, bool doActivate)
+    : NETRootInfo2( display, properties, properties_size, screen, doActivate )
+{
+}
+
+NETRootInfo4::NETRootInfo4(Display *display, Window supportWindow, const char *wmName,
+			 unsigned long properties[], int properties_size,
+                         int screen, bool doActivate)
+    : NETRootInfo3( display, supportWindow, wmName, properties, properties_size,
+	screen, doActivate )
+{
+}
+
+NETRootInfo4::NETRootInfo4(Display *display, const unsigned long properties[], int properties_size,
+                int screen, bool doActivate)
+    : NETRootInfo3( display, properties, properties_size, screen, doActivate )
+{
+}
+
 // Copy an existing NETRootInfo object.
 
 NETRootInfo::NETRootInfo(const NETRootInfo &rootinfo) {
@@ -868,6 +903,25 @@ void NETRootInfo::setClientListStacking(
 }
 
 
+void NETRootInfo::setTrayedWindows(Window *windows, unsigned int count) {
+    if (role != WindowManager) return;
+
+    p->trayed_windows_count = count;
+    delete [] p->trayed_windows;
+    p->trayed_windows = nwindup(windows, count);
+
+#ifdef    NETWMDEBUG
+    fprintf(stderr,
+	    "NETRootInfo::setTrayedWindows: setting list with %ld windows\n",
+	    p->trayed_windows_count);
+#endif
+
+    XChangeProperty(p->display, p->root, kde_net_wm_trayed_windows, XA_WINDOW, 32,
+		    PropModeReplace,
+		    (unsigned char *) p->trayed_windows,
+		    p->trayed_windows_count);
+}
+
 void NETRootInfo::setKDESystemTrayWindows(Window *windows, unsigned int count) {
     if (role != WindowManager) return;
 
@@ -1198,6 +1252,10 @@ void NETRootInfo::setSupported() {
 
         if (p->properties[ STATES ] & StaysOnTop)
 	    atoms[pnum++] = net_wm_state_stays_on_top;
+        if (p->properties[ STATES ] & Trayed)
+	    atoms[pnum++] = kde_net_wm_state_trayed;
+        if (p->properties[ STATES ] & TrayedHidden)
+	    atoms[pnum++] = kde_net_wm_state_trayed_hidden;
     }
 
     if (p->properties[ PROTOCOLS ] & WMStrut)
@@ -1257,6 +1315,9 @@ void NETRootInfo::setSupported() {
     }
 
     // KDE specific extensions
+    if (p->properties[ PROTOCOLS2 ] & WM2TrayedWindows)
+	atoms[pnum++] = kde_net_wm_trayed_windows;
+
     if (p->properties[ PROTOCOLS ] & KDESystemTrayWindows)
 	atoms[pnum++] = kde_net_system_tray_windows;
 
@@ -1413,6 +1474,10 @@ void NETRootInfo::updateSupportedPropert
 
     else if( atom == net_wm_state_stays_on_top )
         p->properties[ STATES ] |= StaysOnTop;
+    else if( atom == kde_net_wm_state_trayed )
+        p->properties[ STATES ] |= Trayed;
+    else if( atom == kde_net_wm_state_trayed_hidden )
+        p->properties[ STATES ] |= TrayedHidden;
 
     else if( atom == net_wm_strut )
         p->properties[ PROTOCOLS ] |= WMStrut;
@@ -1470,6 +1535,9 @@ void NETRootInfo::updateSupportedPropert
         p->properties[ ACTIONS ] |= ActionClose;
 
     // KDE specific extensions
+    else if( atom == kde_net_wm_trayed_windows )
+        p->properties[ PROTOCOLS2 ] |= WM2TrayedWindows;
+
     else if( atom == kde_net_system_tray_windows )
         p->properties[ PROTOCOLS ] |= KDESystemTrayWindows;
 
@@ -1751,8 +1819,6 @@ void NETRootInfo::event(XEvent *event, u
     unsigned long& dirty2 = props[ PROTOCOLS2 ];
     bool do_update = false;
 
-    Q_UNUSED( dirty2 ); // for now
-
     // the window manager will be interested in client messages... no other
     // client should get these messages
     if (role == WindowManager && event->type == ClientMessage &&
@@ -1940,6 +2006,8 @@ void NETRootInfo::event(XEvent *event, u
 		dirty |= ClientList;
 	    else if (pe.xproperty.atom == net_client_list_stacking)
 		dirty |= ClientListStacking;
+	    else if (pe.xproperty.atom == kde_net_wm_trayed_windows)
+		dirty2 |= WM2TrayedWindows;
 	    else if (pe.xproperty.atom == kde_net_system_tray_windows)
 		dirty |= KDESystemTrayWindows;
 	    else if (pe.xproperty.atom == net_desktop_names)
@@ -2009,8 +2077,6 @@ void NETRootInfo::update( const unsigned
     const unsigned long& dirty = props[ PROTOCOLS ];
     const unsigned long& dirty2 = props[ PROTOCOLS2 ];
 
-    Q_UNUSED( dirty2 ); // for now
-
     if (dirty & Supported ) {
         // only in Client mode
         for( int i = 0; i < PROPERTIES_SIZE; ++i )
@@ -2102,6 +2168,73 @@ void NETRootInfo::update( const unsigned
 #endif
     }
 
+    if (dirty2 & WM2TrayedWindows) {
+        bool read_ok = false;
+	if (XGetWindowProperty(p->display, p->root, kde_net_wm_trayed_windows,
+			       0l, MAX_PROP_SIZE, False, XA_WINDOW, &type_ret,
+			       &format_ret, &nitems_ret, &unused, &data_ret)
+	    == Success) {
+	    if (type_ret == XA_WINDOW && format_ret == 32) {
+		Window *wins = (Window *) data_ret;
+
+		qsort(wins, nitems_ret, sizeof(Window), wcmp);
+
+                if( NETRootInfo4* this4 = dynamic_cast< NETRootInfo4* >( this )) {
+		if (p->trayed_windows) {
+		        if (role == Client) {
+			    unsigned long new_index = 0, new_count = nitems_ret;
+			    unsigned long old_index = 0,
+				          old_count = p->trayed_windows_count;
+
+			    while(old_index < old_count || new_index < new_count) {
+			        if (old_index == old_count) {
+				    this4->addTrayedWin(wins[new_index++]);
+			        } else if (new_index == new_count) {
+				    this4->removeTrayedWin(p->trayed_windows[old_index++]);
+			        } else {
+				    if (p->trayed_windows[old_index] <
+				        wins[new_index]) {
+				        this4->removeTrayedWin(p->trayed_windows[old_index++]);
+				    } else if (wins[new_index] <
+					       p->trayed_windows[old_index]) {
+				        this4->addTrayedWin(wins[new_index++]);
+				    } else {
+				        new_index++;
+				        old_index++;
+				    }
+			        }
+			    }
+		        }
+
+		    } else {
+		        unsigned long n;
+		        for (n = 0; n < nitems_ret; n++) {
+			    this4->addTrayedWin(wins[n]);
+		        }
+		    }
+                }
+
+		p->trayed_windows_count = nitems_ret;
+		delete [] p->trayed_windows;
+		p->trayed_windows =
+		    nwindup(wins, p->trayed_windows_count);
+                read_ok = true;
+	    }
+
+	    if ( data_ret )
+		XFree(data_ret);
+	}
+        if( !read_ok ) {
+            if( NETRootInfo4* this4 = dynamic_cast< NETRootInfo4* >( this )) {
+                for( unsigned int i = 0; i < p->trayed_windows_count; ++i )
+                    this4->removeTrayedWin(p->trayed_windows[i]);
+            }
+            p->trayed_windows_count = 0;
+	    delete [] p->trayed_windows;
+            p->trayed_windows = NULL;
+        }
+    }
+
     if (dirty & KDESystemTrayWindows) {
         bool read_ok = false;
 	if (XGetWindowProperty(p->display, p->root, kde_net_system_tray_windows,
@@ -2505,6 +2638,16 @@ int NETRootInfo::clientListStackingCount
 }
 
 
+const Window *NETRootInfo::trayedWindows() const {
+    return p->trayed_windows;
+}
+
+
+int NETRootInfo::trayedWindowsCount() const {
+    return p->trayed_windows_count;
+}
+
+
 const Window *NETRootInfo::kdeSystemTrayWindows() const {
     return p->kde_system_tray_windows;
 }
@@ -3005,6 +3148,24 @@ void NETWinInfo::setState(unsigned long 
             XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
         }
 
+        if ((mask & Trayed) &&
+	    ((p->state & Trayed) != (state & Trayed))) {
+            e.xclient.data.l[0] = (state & Trayed) ? 1 : 0;
+            e.xclient.data.l[1] = kde_net_wm_state_trayed;
+            e.xclient.data.l[2] = 0l;
+
+            XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
+        }
+
+        if ((mask & TrayedHidden) && // must be after the handling of Trayed, because it relies on it
+	    ((p->state & TrayedHidden) != (state & TrayedHidden))) {
+            e.xclient.data.l[0] = (state & TrayedHidden) ? 1 : 0;
+            e.xclient.data.l[1] = kde_net_wm_state_trayed_hidden;
+            e.xclient.data.l[2] = 0l;
+
+            XSendEvent(p->display, p->root, False, netwm_sendevent_mask, &e);
+        }
+
     } else {
 	p->state &= ~mask;
 	p->state |= state;
@@ -3028,6 +3189,8 @@ void NETWinInfo::setState(unsigned long 
 	if (p->state & Sticky) data[count++] = net_wm_state_sticky;
 	if (p->state & SkipTaskbar) data[count++] = net_wm_state_skip_taskbar;
 	if (p->state & SkipPager) data[count++] = net_wm_state_skip_pager;
+	if (p->state & Trayed) data[count++] = kde_net_wm_state_trayed;
+	if (p->state & TrayedHidden) data[count++] = kde_net_wm_state_trayed_hidden;
 
 #ifdef NETWMDEBUG
 	fprintf(stderr, "NETWinInfo::setState: setting state property (%d)\n", count);
@@ -3445,6 +3608,10 @@ void NETWinInfo::event(XEvent *event, un
 		    mask |= DemandsAttention;
 		else if ((Atom) event->xclient.data.l[i] == net_wm_state_stays_on_top)
 		    mask |= StaysOnTop;
+		else if ((Atom) event->xclient.data.l[i] == kde_net_wm_state_trayed)
+		    mask |= Trayed;
+		else if ((Atom) event->xclient.data.l[i] == kde_net_wm_state_trayed_hidden)
+		    mask |= TrayedHidden;
 	    }
 
 	    // when removing, we just leave newstate == 0
@@ -3693,6 +3860,10 @@ void NETWinInfo::update(const unsigned l
 			p->state |= DemandsAttention;
 		    else if ((Atom) states[count] == net_wm_state_stays_on_top)
 			p->state |= StaysOnTop;
+		    else if ((Atom) states[count] == kde_net_wm_state_trayed)
+			p->state |= Trayed;
+		    else if ((Atom) states[count] == kde_net_wm_state_trayed_hidden)
+			p->state |= TrayedHidden;
 		}
 	    }
 	    if ( data_ret )
--- kdecore/netwm.h.sav	2005-01-11 11:46:04.000000000 +0100
+++ kdecore/netwm.h	2005-04-14 11:43:59.000000000 +0200
@@ -297,7 +297,27 @@ public:
     int clientListStackingCount() const;
 
     /**
-       Returns an array of Window id's, which contain all KDE system tray windows.
+       Returns an array of Window id's, which contains all windows marked to be included in tray.
+
+       @return the array of Window id's of system tray windows
+
+       @see trayedWindowsCount()
+       @since 3.5
+    **/
+    const Window *trayedWindows() const;
+
+    /**
+       Returns the number of windows in the kdeSystemTrayWindows array.
+
+       @return the number of Window id's in the system tray list
+
+       @see trayedWindows()
+       @since 3.5
+    **/
+    int trayedWindowsCount() const;
+
+    /**
+       Returns an array of Window id's, which contains all (old) KDE system tray windows.
 
        @return the array of Window id's of system tray windows
 
@@ -423,6 +443,16 @@ public:
     void setClientListStacking(Window *windows, unsigned int count);
 
     /**
+       Sets the list of windows marked for tray on the root window.
+
+       @param windows The array of window id's
+
+       @param count The number of windows in the array.
+       @since 3.5
+    **/
+    void setTrayedWindows(Window *windows, unsigned int count);
+
+    /**
        Sets the list of KDE system tray windows on the root window.
 
        @param windows The array of window id's
@@ -748,6 +778,11 @@ public:
 		unsigned long properties[], int properties_size,
                 int screen = -1, bool doActivate = true);
     /**
+     * @since 3.5
+     */
+    NETRootInfo2(Display *display, const unsigned long properties[], int properties_size,
+                int screen = -1, bool doActivate = true);
+    /**
       Sends a ping with the given timestamp to the window, using
       the _NET_WM_PING protocol.
     */
@@ -815,6 +850,11 @@ public:
 		unsigned long properties[], int properties_size,
                 int screen = -1, bool doActivate = true);
     /**
+     * @since 3.5
+     */
+    NETRootInfo3(Display *display, const unsigned long properties[], int properties_size,
+                int screen = -1, bool doActivate = true);
+    /**
        Sends a take activity message with the given timestamp to the window, using
        the _NET_WM_TAKE_ACTIVITY protocol (see the WM spec for details).
        @param window the window to which the message should be sent
@@ -849,6 +889,43 @@ protected:
 };
 
 /**
+ This class is an extension of the NETRootInfo class, and exists solely
+ for binary compatibility reasons (adds new virtual methods). Simply
+ use it instead of NETRootInfo and override also the added virtual methods.
+ @since 3.5
+*/
+class KDECORE_EXPORT NETRootInfo4
+    : public NETRootInfo3
+{
+public:
+    NETRootInfo4(Display *display, Window supportWindow, const char *wmName,
+		unsigned long properties[], int properties_size,
+                int screen = -1, bool doActivate = true);
+    NETRootInfo4(Display *display, const unsigned long properties[], int properties_size,
+                int screen = -1, bool doActivate = true);
+
+protected:
+    friend class NETRootInfo;
+    /**
+       A Client should subclass NETRootInfo and reimplement this function when
+       it wants to know when a window has been marked to be included in the tray.
+
+       @param window the id of the window to add
+    **/
+    virtual void addTrayedWin(Window window) { Q_UNUSED(window); }
+
+    /**
+       A Client should subclass NETRootInfo and reimplement this function when
+       it wants to know when a window is no longer marked to be included in the tray.
+       
+       @param window the id of the window to remove
+    **/
+    virtual void removeTrayedWin(Window window) { Q_UNUSED(window); }
+// no private data, use NETRootInfoPrivate
+};
+
+
+/**
    Common API for application window properties/protocols.
 
    The NETWinInfo class provides a common API for clients and window managers to
--- kdecore/netwm_def.h.sav	2004-11-23 16:08:29.000000000 +0100
+++ kdecore/netwm_def.h	2005-04-14 09:55:10.000000000 +0200
@@ -376,7 +376,9 @@ public:
 	Hidden       = 1<<8,	///< @since 3.2
 	FullScreen   = 1<<9,	///< @since 3.2
 	KeepBelow    = 1<<10,	///< @since 3.2
-        DemandsAttention = 1<<11  ///< @since 3.2
+        DemandsAttention = 1<<11,  ///< @since 3.2
+        Trayed       = 1<<12,   ///< @since 3.5
+        TrayedHidden = 1<<13    ///< @since 3.5
     };
 
     /**
@@ -578,7 +580,8 @@ public:
         WM2KDETemporaryRules   = 1<<9,  // NOT STANDARD
         WM2WindowClass         = 1<<10, ///< @since 3.3
         WM2WindowRole          = 1<<11, ///< @since 3.3
-        WM2ClientMachine       = 1<<12  ///< @since 3.3
+        WM2ClientMachine       = 1<<12, ///< @since 3.3
+        WM2TrayedWindows       = 1<<13  ///< @since 3.5
     };
 
     /**
--- kdecore/netwm_p.h.sav	2004-08-03 17:10:06.000000000 +0200
+++ kdecore/netwm_p.h	2005-04-14 09:55:10.000000000 +0200
@@ -95,13 +95,13 @@ struct NETRootInfoPrivate {
     NETRArray<NETRect> workarea;
     NETSize geometry;
     Window active;
-    Window *clients, *stacking, *virtual_roots, *kde_system_tray_windows;
+    Window *clients, *stacking, *virtual_roots, *trayed_windows, *kde_system_tray_windows;
     NETRArray<const char *> desktop_names;
     int number_of_desktops;
     int current_desktop;
 
     unsigned long clients_count, stacking_count, virtual_roots_count,
-	kde_system_tray_windows_count;
+	trayed_windows_count, kde_system_tray_windows_count;
 
     unsigned long properties[ 5 ];
     unsigned long client_properties[ 5 ]; // properties the client is interested in
--- kdecore/kwinmodule.cpp.sav	2004-11-29 11:39:33.000000000 +0100
+++ kdecore/kwinmodule.cpp	2005-04-14 11:11:50.000000000 +0200
@@ -36,21 +36,34 @@
 
 static KWinModulePrivate* static_d = 0;
 
-class KWinModulePrivate : public QWidget, public NETRootInfo
+static unsigned long windows_properties[ 2 ] = { NET::ClientList | NET::ClientListStacking |
+				     NET::NumberOfDesktops |
+				     NET::DesktopGeometry |
+				     NET::CurrentDesktop |
+				     NET::DesktopNames |
+				     NET::ActiveWindow |
+				     NET::WorkArea |
+				     NET::KDESystemTrayWindows,
+                                     NET::WM2TrayedWindows };
+
+static unsigned long desktop_properties[ 2 ] = { 
+				     NET::NumberOfDesktops |
+				     NET::DesktopGeometry |
+				     NET::CurrentDesktop |
+				     NET::DesktopNames |
+				     NET::ActiveWindow |
+				     NET::WorkArea |
+				     NET::KDESystemTrayWindows,
+                                     0 };
+
+class KWinModulePrivate : public QWidget, public NETRootInfo4
 {
 public:
     KWinModulePrivate(int _what)
-	: QWidget(0,0), NETRootInfo( qt_xdisplay(),
-				     ( _what >= KWinModule::INFO_WINDOWS ? 
-				       (ClientList | ClientListStacking) : 0
-				     ) |
-				     NumberOfDesktops |
-				     DesktopGeometry |
-				     CurrentDesktop |
-				     DesktopNames |
-				     ActiveWindow |
-				     WorkArea |
-				     KDESystemTrayWindows,
+	: QWidget(0,0), NETRootInfo4( qt_xdisplay(),
+                                     _what >= KWinModule::INFO_WINDOWS ?
+                                     windows_properties : desktop_properties,
+                                     2,
 				     -1, false
 				     ),
           strutSignalConnected( false ),
@@ -68,6 +81,7 @@ public:
 
     QValueList<WId> windows;
     QValueList<WId> stackingOrder;
+    QValueList<WId> trayedWindows;
     QValueList<WId> systemTrayWindows;
 
     struct StrutData
@@ -86,6 +100,8 @@ public:
 
     void addClient(Window);
     void removeClient(Window);
+    void addTrayedWin(Window);
+    void removeTrayedWin(Window);
     void addSystemTrayWin(Window);
     void removeSystemTrayWin(Window);
 
@@ -158,6 +174,11 @@ bool KWinModule::hasWId(WId w) const
     return d->windows.findIndex( w ) != -1;
 }
 
+const QValueList<WId>& KWinModule::trayedWindows() const
+{
+    return d->trayedWindows;
+}
+
 const QValueList<WId>& KWinModule::systemTrayWindows() const
 {
     return d->systemTrayWindows;
@@ -282,6 +303,20 @@ void KWinModulePrivate::removeClient(Win
     }
 }
 
+void KWinModulePrivate::addTrayedWin(Window w)
+{
+    trayedWindows.append( w );
+    for ( QPtrListIterator<KWinModule> mit( modules ); mit.current(); ++mit )
+	emit (*mit)->trayedWindowAdded( w );
+}
+
+void KWinModulePrivate::removeTrayedWin(Window w)
+{
+    trayedWindows.remove( w );
+    for ( QPtrListIterator<KWinModule> mit( modules ); mit.current(); ++mit )
+	emit (*mit)->trayedWindowRemoved( w );
+}
+
 void KWinModulePrivate::addSystemTrayWin(Window w)
 {
     systemTrayWindows.append( w );
--- kdecore/kwinmodule.h.sav	2005-02-22 17:55:23.000000000 +0100
+++ kdecore/kwinmodule.h	2005-04-14 10:55:34.000000000 +0200
@@ -81,6 +81,9 @@ public:
      *                  systemTrayWindows,
      *                  systemTrayWindowAdded,
      *                  systemTrayWindowRemoved,
+     *                  trayedWindows,
+     *                  trayedWindowAdded,
+     *                  trayedWindowRemoved,
      *                  windowChanged,
      *                  strutChanged,
      *                  workArea(const QValueList<WId> &excludes, int desktop)
@@ -134,6 +137,13 @@ public:
     bool hasWId(WId id) const;
 
     /**
+     * Returns a list of the trayed windows.
+     * @return a list of all trayed windows
+     * @since 3.5
+     **/
+    const QValueList<WId>& trayedWindows() const;
+
+    /**
      * Returns a list of the system tray windows.
      * @return a list of all system tray windows
      **/
@@ -245,6 +255,20 @@ signals:
 
     /**
      * Emitted when a dock window has been added.
+     * @param id the id of the new trayed window
+     * @since 3.5
+     */
+    void trayedWindowAdded(WId id);
+
+    /**
+     * Emitted when a dock window has been removed.
+     * @param id the id of the former trayed window
+     * @since 3.5
+     */
+    void trayedWindowRemoved(WId id);
+
+    /**
+     * Emitted when a dock window has been added.
      * @param id the id of the new system tray window
      */
     void systemTrayWindowAdded(WId id);

["kwin.patch" (text/x-diff)]

--- kwin/workspace.h.sav	2005-02-02 20:06:01.000000000 +0100
+++ kwin/workspace.h	2001-01-01 01:01:00.000000000 +0100
@@ -148,6 +148,7 @@ class Workspace : public QObject, public
 
         void clientHidden( Client*  );
         void clientAttentionChanged( Client* c, bool set );
+        void updateTrayed();
 
         void clientMoved(const QPoint &pos, Time time);
 
@@ -622,7 +623,7 @@ class StackingUpdatesBlocker
     };
 
 // NET WM Protocol handler class
-class RootInfo : public NETRootInfo3
+class RootInfo : public NETRootInfo4
     {
     private:
         typedef KWinInternal::Client Client;  // because of NET::Client
--- kwin/sm.h.sav	2005-01-17 17:12:17.000000000 +0100
+++ kwin/sm.h	2005-04-14 09:55:54.000000000 +0200
@@ -44,6 +44,8 @@ struct SessionInfo
     bool skipTaskbar;
     bool skipPager;
     bool userNoBorder;
+    bool trayed;
+    bool trayedHidden;
     NET::WindowType windowType;
     QString shortcut;
     bool active; // means 'was active in the saved session'
--- kwin/bridge.h.sav	2004-12-15 12:04:44.000000000 +0100
+++ kwin/bridge.h	2005-04-14 09:55:54.000000000 +0200
@@ -60,6 +60,7 @@ class Bridge : public KDecorationBridge
         virtual void setShade( bool set );
         virtual void setKeepAbove( bool );
         virtual void setKeepBelow( bool );
+        // TODOTRAY
         virtual int currentDesktop() const;
         virtual QWidget* initialParentWidget() const;
         virtual Qt::WFlags initialWFlags() const;
--- kwin/manage.cpp.sav	2005-04-12 19:44:18.000000000 +0200
+++ kwin/manage.cpp	2005-04-14 09:55:54.000000000 +0200
@@ -357,6 +357,8 @@ bool Client::manage( Window w, bool isMa
             setFullScreen( true, false );
             geom_fs_restore = session->fsrestore;
             }
+        setTrayed( session->trayed );
+        setTrayedHidden( session->trayedHidden );
         }
     else 
         {
@@ -402,6 +404,7 @@ bool Client::manage( Window w, bool isMa
         // read other initial states
         setShade( rules()->checkShade( info->state() & NET::Shaded ? ShadeNormal : \
                ShadeNone, !isMapped ));
         setKeepAbove( rules()->checkKeepAbove( info->state() & NET::KeepAbove, \
!isMapped )); +        // TODOTRAY
         setKeepBelow( rules()->checkKeepBelow( info->state() & NET::KeepBelow, \
                !isMapped ));
         setSkipTaskbar( rules()->checkSkipTaskbar( info->state() & NET::SkipTaskbar, \
                !isMapped ), true );
         setSkipPager( rules()->checkSkipPager( info->state() & NET::SkipPager, \
!isMapped )); @@ -411,6 +414,7 @@ bool Client::manage( Window w, bool isMa
             setModal( true );
         if( fullscreen_mode != FullScreenHack && isFullScreenable())
             setFullScreen( rules()->checkFullScreen( info->state() & \
NET::FullScreen, !isMapped ), false ); +        // TODOTRAY
         }
 
     updateAllowedActions( true );
--- kwin/client.h.sav	2005-04-12 19:23:46.000000000 +0200
+++ kwin/client.h	2005-04-14 10:28:45.000000000 +0200
@@ -143,6 +143,10 @@ class Client : public QObject, public KD
 
         bool skipTaskbar( bool from_outside = false ) const;
         void setSkipTaskbar( bool set, bool from_outside );
+        bool trayed() const;
+        void setTrayed( bool set );
+        bool trayedHidden() const;
+        void setTrayedHidden( bool set );
 
         bool skipPager() const;
         void setSkipPager( bool );
@@ -480,6 +484,8 @@ class Client : public QObject, public KD
         uint not_obscured : 1;
         uint urgency : 1; // XWMHints, UrgencyHint
         uint ignore_focus_stealing : 1; // don't apply focus stealing prevention to \
this client +        uint is_trayed : 1;
+        uint trayed_hidden : 1;
         WindowRules client_rules;
         void getWMHints();
         void readIcons();
@@ -670,7 +676,7 @@ inline bool Client::isOnDesktop( int d )
 inline
 bool Client::isShown( bool shaded_is_shown ) const
     {
-    return !isMinimized() && ( !isShade() || shaded_is_shown ) && !hidden;
+    return !isMinimized() && ( !isShade() || shaded_is_shown ) && !hidden && \
!trayed_hidden;  }
 
 inline
@@ -710,6 +716,16 @@ inline bool Client::skipTaskbar( bool fr
     return from_outside ? original_skip_taskbar : skip_taskbar;
     }
 
+inline bool Client::trayed() const
+    {
+    return is_trayed;
+    }
+
+inline bool Client::trayedHidden() const
+    {
+    return trayed_hidden;
+    }
+
 inline bool Client::skipPager() const
     {
     return skip_pager;
--- kwin/client.cpp.sav	2005-04-12 19:23:46.000000000 +0200
+++ kwin/client.cpp	2005-04-14 18:56:56.000000000 +0200
@@ -127,6 +127,8 @@ Client::Client( Workspace *ws )
     not_obscured = false;
     urgency = false;
     ignore_focus_stealing = false;
+    is_trayed = false;
+    trayed_hidden = false;
     check_active_modal = false;
 
     Pdeletewindow = 0;
@@ -527,6 +529,12 @@ bool Client::isMinimizable() const
  */
 void Client::minimize( bool avoid_animation )
     {
+    if( trayed())
+        {
+        setTrayedHidden( true );
+        return;
+        }
+
     if ( !isMinimizable() || isMinimized())
         return;
 
@@ -546,6 +554,11 @@ void Client::minimize( bool avoid_animat
 
 void Client::unminimize( bool avoid_animation )
     {
+    if( trayed())
+        {
+        setTrayedHidden( false );
+        return;
+        }
     if( !isMinimized())
         return;
 
@@ -835,6 +848,19 @@ void Client::updateVisibility()
         {
         setSkipTaskbar( original_skip_taskbar, false );
         }
+    if( trayed_hidden )
+        {
+        setMappingState( IconicState );
+        info->setState( NET::Hidden, NET::Hidden );
+        setSkipTaskbar( true, false ); // also hide from taskbar
+        rawHide();
+        show = false;
+        }
+    else
+        {
+        if( !hidden )
+            setSkipTaskbar( original_skip_taskbar, false );
+        }
     if( minimized )
         {
         setMappingState( IconicState );
@@ -1109,6 +1135,47 @@ void Client::setSkipTaskbar( bool b, boo
     updateWindowRules();
     }
 
+void Client::setTrayed( bool set )
+    {
+// TODOTRAY    set = rules()->checkTrayed( set );
+    if( set == trayed() )
+        return;
+    is_trayed = set;
+    info->setState( set ? NET::Trayed : 0, NET::Trayed );
+    if( isManaged())
+        workspace()->updateTrayed();
+    if( decoration != NULL )
+        decoration->emitKeepBelowChanged( trayed());
+    if( minimized )
+        {
+        if( set )
+            {
+            setTrayedHidden( true );
+            unminimize( true );
+            }
+        else
+            {
+            setTrayedHidden( false );
+            minimize( true );
+            }
+        }
+    if( !set )
+        setTrayedHidden( false );
+    updateWindowRules();
+    }
+
+void Client::setTrayedHidden( bool set )
+    {
+    if( set == trayedHidden() )
+        return;
+    if( !trayed())
+        set = false;
+    trayed_hidden = set;
+    info->setState( set ? NET::TrayedHidden : 0, NET::TrayedHidden );
+    updateVisibility();
+    updateWindowRules();
+    }
+
 void Client::setSkipPager( bool b )
     {
     b = rules()->checkSkipPager( b );
--- kwin/sm.cpp.sav	2005-01-17 17:12:17.000000000 +0100
+++ kwin/sm.cpp	2005-04-14 09:55:54.000000000 +0200
@@ -112,6 +112,8 @@ void Workspace::storeSession( KConfig* c
             config->writeEntry( QString("skipTaskbar")+n, c->skipTaskbar( true ) );
             config->writeEntry( QString("skipPager")+n, c->skipPager() );
             config->writeEntry( QString("userNoBorder")+n, c->isUserNoBorder() );
+            config->writeEntry( QString("trayed")+n, c->trayed() );
+            config->writeEntry( QString("trayedHidden")+n, c->trayedHidden() );
             config->writeEntry( QString("windowType")+n, windowTypeToTxt( \
                c->windowType()));
             config->writeEntry( QString("shortcut")+n, \
c->shortcut().toStringInternal());  }
@@ -177,6 +179,8 @@ void Workspace::loadSessionInfo()
         info->skipTaskbar = config->readBoolEntry( QString("skipTaskbar")+n, FALSE  \
                );
         info->skipPager = config->readBoolEntry( QString("skipPager")+n, FALSE  );
         info->userNoBorder = config->readBoolEntry( QString("userNoBorder")+n, FALSE \
); +        info->trayed = config->readBoolEntry( QString("trayed")+n, FALSE  );
+        info->trayedHidden = config->readBoolEntry( QString("trayedHidden")+n, FALSE \
                );
         info->windowType = txtToWindowType( config->readEntry( \
QString("windowType")+n ).latin1());  info->shortcut = config->readEntry( \
QString("shortcut")+n );  info->active = ( active_client == i );
--- kwin/workspace.cpp.sav	2005-04-12 19:23:46.000000000 +0200
+++ kwin/workspace.cpp	2005-04-14 11:07:22.000000000 +0200
@@ -272,6 +272,8 @@ void Workspace::init()
         NET::FullScreen |
         NET::KeepBelow |
         NET::DemandsAttention |
+        NET::Trayed |
+        NET::TrayedHidden |
         0
         ,
         NET::WM2UserTime |
@@ -281,6 +283,7 @@ void Workspace::init()
         NET::WM2MoveResizeWindow |
         NET::WM2ExtendedStrut |
         NET::WM2KDETemporaryRules |
+        NET::WM2TrayedWindows |
         0
         ,
         NET::ActionMove |
@@ -525,6 +528,8 @@ void Workspace::addClient( Client* c, al
     c->checkActiveModal();
     checkTransients( c->window()); // SELI does this really belong here?
     updateStackingOrder( true ); // propagate new client
+    if( c->trayed())
+        updateTrayed();
     if( c->isUtility() || c->isMenu() || c->isToolbar())
         updateToolWindows( true );
     }
@@ -556,6 +561,8 @@ void Workspace::removeClient( Client* c,
     attention_chain.remove( c );
     if( c->isTopMenu())
         removeTopMenu( c );
+    if( c->trayed())
+        updateTrayed();
     Group* group = findGroup( c->window());
     if( group != NULL )
         group->lostLeader();
@@ -771,6 +778,21 @@ void Workspace::slotUpdateToolWindows()
     updateToolWindows( true );
     }
 
+void Workspace::updateTrayed()
+    {
+    Window* prop = new Window[ clients.count() ];
+    int cnt = 0;
+    for( ClientList::ConstIterator it = clients.begin();
+         it != clients.end();
+         ++it )
+        {
+        if( (*it)->trayed())
+            prop[ cnt++ ] = (*it)->window();
+        }
+    kdDebug() << "TRAYED:" << cnt << endl;
+    rootInfo->setTrayedWindows( prop, cnt );
+    }
+
 /*!
   Updates the current colormap according to the currently active client
  */
--- kwin/events.cpp.sav	2005-01-17 17:12:17.000000000 +0100
+++ kwin/events.cpp	2005-04-14 10:06:05.000000000 +0200
@@ -82,6 +82,10 @@ void WinInfo::changeState( unsigned long
     // unsetting fullscreen first, setting it last (because e.g. maximize works only \
                for !isFullScreen() )
     if(( mask & NET::FullScreen ) != 0 && ( state & NET::FullScreen ) != 0 )
         m_client->setFullScreen( true, false );
+    if( mask & NET::Trayed )
+        m_client->setTrayed(( state & NET::Trayed ) != 0 );
+    if( mask & NET::TrayedHidden )
+        m_client->setTrayedHidden(( state & NET::TrayedHidden ) != 0 );
     }
 
 
@@ -90,7 +94,7 @@ void WinInfo::changeState( unsigned long
 // ****************************************
 
 RootInfo::RootInfo( Workspace* ws, Display *dpy, Window w, const char *name, \
                unsigned long pr[], int pr_num, int scr )
-    : NETRootInfo3( dpy, w, name, pr, pr_num, scr )
+    : NETRootInfo4( dpy, w, name, pr, pr_num, scr )
     {
     workspace = ws;
     }
--- kwin/bridge.cpp.sav	2004-12-15 12:04:44.000000000 +0100
+++ kwin/bridge.cpp	2005-04-14 18:48:15.000000000 +0200
@@ -38,7 +38,12 @@ BRIDGE_HELPER( bool, isModal,,, const )
 BRIDGE_HELPER( bool, isShadeable,,, const )
 BRIDGE_HELPER( bool, isShade,,, const )
 BRIDGE_HELPER( bool, keepAbove,,, const )
-BRIDGE_HELPER( bool, keepBelow,,, const )
+//BRIDGE_HELPER( bool, keepBelow,,, const )
+bool Bridge::keepBelow() const
+    {
+    return c->trayed();
+    }
+
 BRIDGE_HELPER( bool, isMovable,,, const )
 BRIDGE_HELPER( bool, isResizable,,, const )
 BRIDGE_HELPER( QString, caption,,, const )
@@ -50,7 +55,11 @@ BRIDGE_HELPER( void, minimize,,, )
 BRIDGE_HELPER( void, showContextHelp,,, )
 BRIDGE_HELPER( void, setDesktop, int desktop, desktop, )
 BRIDGE_HELPER( void, setKeepAbove, bool set, set, )
-BRIDGE_HELPER( void, setKeepBelow, bool set, set, )
+//BRIDGE_HELPER( void, setKeepBelow, bool set, set, )
+void Bridge::setKeepBelow( bool set )
+    {
+    c->setTrayed( set );
+    }
 
 NET::WindowType Bridge::windowType( unsigned long supported_types ) const
     {


["systemtray.patch" (text/x-diff)]

--- systemtray/systemtrayapplet.cpp.sav	2005-03-15 14:00:39.000000000 +0100
+++ systemtray/systemtrayapplet.cpp	2005-04-14 18:36:38.000000000 +0200
@@ -95,6 +95,13 @@ void SystemTrayApplet::initialize()
         embedWindow(*it, true);
         existing = true;
     }
+    const QValueList<WId> trayedWindows = kwin_module->trayedWindows();
+    for (QValueList<WId>::ConstIterator it = trayedWindows.begin();
+         it != trayedWindows.end(); ++it )
+    {
+        trayWindow(*it);
+        existing = true;
+    }
 
     showExpandButton(!m_hiddenWins.isEmpty());
 
@@ -109,6 +116,10 @@ void SystemTrayApplet::initialize()
              this, SLOT( systemTrayWindowAdded(WId) ) );
     connect( kwin_module, SIGNAL( systemTrayWindowRemoved(WId) ),
              this, SLOT( updateTrayWindows() ) );
+    connect( kwin_module, SIGNAL( trayedWindowAdded(WId) ),
+             this, SLOT( trayedWindowAdded(WId) ) );
+    connect( kwin_module, SIGNAL( trayedWindowRemoved(WId) ),
+             this, SLOT( trayedWindowRemoved(WId) ) );
 
     QCString screenstr;
     screenstr.setNum(qt_xscreen());
@@ -163,7 +174,7 @@ bool SystemTrayApplet::x11Event( XEvent 
              e->xclient.data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) {
             if( isWinManaged( (WId)e->xclient.data.l[2] ) ) // we already manage it
                 return true;
-            embedWindow( e->xclient.data.l[2], false );
+            embedWindow( e->xclient.data.l[2], false);
             layoutTray();
             emit updateLayout();
             return true;
@@ -225,14 +236,14 @@ void SystemTrayApplet::preferences()
     QListBox *shownListBox = m_iconSelector->availableListBox();
     QListBox *hiddenListBox = m_iconSelector->selectedListBox();
 
-    TrayEmbedList::const_iterator it = m_shownWins.begin();
-    TrayEmbedList::const_iterator itEnd = m_shownWins.end();
+    TrayWidgetList::const_iterator it = m_shownWins.begin();
+    TrayWidgetList::const_iterator itEnd = m_shownWins.end();
     for (; it != itEnd; ++it)
     {
-        QString name = KWin::windowInfo((*it)->embeddedWinId()).name();
+        QString name = KWin::windowInfo((*it)->windowId()).name();
         if(!shownListBox->findItem(name, Qt::ExactMatch | Qt::CaseSensitive))
         {
-            shownListBox->insertItem(KWin::icon((*it)->embeddedWinId(), 22, 22, \
true), name); +            shownListBox->insertItem(KWin::icon((*it)->windowId(), 22, \
22, true), name);  }
     }
 
@@ -240,10 +251,10 @@ void SystemTrayApplet::preferences()
     itEnd = m_hiddenWins.end();
     for (; it != itEnd; ++it)
     {
-        QString name = KWin::windowInfo((*it)->embeddedWinId()).name();
+        QString name = KWin::windowInfo((*it)->windowId()).name();
         if(!hiddenListBox->findItem(name, Qt::ExactMatch | Qt::CaseSensitive))
         {
-            hiddenListBox->insertItem(KWin::icon((*it)->embeddedWinId(), 22, 22, \
true), name); +            hiddenListBox->insertItem(KWin::icon((*it)->windowId(), \
22, 22, true), name);  }
     }
 
@@ -289,9 +300,9 @@ void SystemTrayApplet::applySettings()
     conf->writeEntry("Hidden", m_hiddenIconList);
     conf->sync();
 
-    for (TrayEmbed* emb = m_shownWins.first(); emb != 0; )
+    for (TrayWidget* emb = m_shownWins.first(); emb != 0; )
     {
-        if (shouldHide(emb->embeddedWinId()))
+        if (shouldHide(emb->windowId()))
         {
             emb = m_shownWins.take(); // Next item becomes current
             m_hiddenWins.append(emb);
@@ -303,9 +314,9 @@ void SystemTrayApplet::applySettings()
         }
     }
 
-    for (TrayEmbed* emb = m_hiddenWins.first(); emb != 0; )
+    for (TrayWidget* emb = m_hiddenWins.first(); emb != 0; )
     {
-        if (!shouldHide(emb->embeddedWinId()))
+        if (!shouldHide(emb->windowId()))
         {
             emb = m_hiddenWins.take(); // Next item becomes current
             m_shownWins.append(emb);
@@ -409,7 +420,7 @@ void SystemTrayApplet::loadSettings()
     m_hiddenIconList = conf->readListEntry("Hidden");
 }
 
-void SystemTrayApplet::systemTrayWindowAdded( WId w )
+void SystemTrayApplet::windowAdded( WId w, bool kde_tray, bool xdg )
 {
     if (isWinManaged(w))
     {
@@ -417,7 +428,10 @@ void SystemTrayApplet::systemTrayWindowA
         return;
     }
 
-    embedWindow(w, true);
+    if( kde_tray || xdg )
+        embedWindow(w, kde_tray);
+    else
+        trayWindow( w );
     updateVisibleWins();
     layoutTray();
     emit updateLayout();
@@ -429,7 +443,17 @@ void SystemTrayApplet::systemTrayWindowA
     }
 }
 
-void SystemTrayApplet::embedWindow( WId w, bool kde_tray )
+void SystemTrayApplet::systemTrayWindowAdded( WId w )
+{
+    windowAdded( w, true, false );
+}
+
+void SystemTrayApplet::trayedWindowAdded( WId w )
+{
+    windowAdded( w, false, false );
+}
+
+void SystemTrayApplet::embedWindow( WId w, bool kde_tray)
 {
     TrayEmbed* emb = new TrayEmbed(kde_tray, this);
     emb->setAutoDelete(false);
@@ -448,7 +472,7 @@ void SystemTrayApplet::embedWindow( WId 
         emb->embed(w);
     }
 
-    if (emb->embeddedWinId() == 0)  // error embedding
+    if (emb->windowId() == 0)  // error embedding
     {
         delete emb;
         return;
@@ -470,21 +494,81 @@ void SystemTrayApplet::embedWindow( WId 
     }
 }
 
+void SystemTrayApplet::trayWindow( WId w )
+{
+    TrayIcon* emb = new TrayIcon(w, this);
+    emb->setBackgroundOrigin(AncestorOrigin);
+    emb->setBackgroundMode(X11ParentRelative);
+
+    QPixmap p = KWin::icon( w, 24, 24, true );
+    if (p.isNull())
+    {
+        delete emb;
+        return;
+    }
+
+    emb->resize(24, 24);
+    emb->setPixmap( p );
+    if (shouldHide(w))
+    {
+        emb->hide();
+        m_hiddenWins.append(emb);
+        showExpandButton(true);
+    }
+    else
+    {
+        emb->hide();
+        emb->show();
+        m_shownWins.append(emb);
+    }
+}
+
+void SystemTrayApplet::trayedWindowRemoved( WId w )
+{
+    TrayWidget* emb = m_shownWins.first();
+    while ((emb = m_shownWins.current()) != 0)
+    {
+        WId wid = emb->windowId();
+        if( wid == w )
+            m_shownWins.remove(emb);
+        else
+        {
+            m_shownWins.next();
+        }
+    }
+
+    emb = m_hiddenWins.first();
+    while ((emb = m_hiddenWins.current()) != 0L)
+    {
+        WId wid = emb->windowId();
+        if( wid == w )
+            m_hiddenWins.remove(emb);
+        else
+            m_hiddenWins.next();
+    }
+
+    showExpandButton(!m_hiddenWins.isEmpty());
+
+    updateVisibleWins();
+    layoutTray();
+    emit updateLayout();
+}
+
 bool SystemTrayApplet::isWinManaged(WId w)
 {
-    TrayEmbedList::const_iterator lastEmb = m_shownWins.end();
-    for (TrayEmbedList::const_iterator emb = m_shownWins.begin(); emb != lastEmb; \
++emb) +    TrayWidgetList::const_iterator lastEmb = m_shownWins.end();
+    for (TrayWidgetList::const_iterator emb = m_shownWins.begin(); emb != lastEmb; \
++emb)  {
-        if ((*emb)->embeddedWinId() == w) // we already manage it
+        if ((*emb)->windowId() == w) // we already manage it
         {
             return true;
         }
     }
 
     lastEmb = m_hiddenWins.end();
-    for (TrayEmbedList::const_iterator emb = m_hiddenWins.begin(); emb != lastEmb; \
++emb) +    for (TrayWidgetList::const_iterator emb = m_hiddenWins.begin(); emb != \
lastEmb; ++emb)  {
-        if ((*emb)->embeddedWinId() == w) // we already manage it
+        if ((*emb)->windowId() == w) // we already manage it
         {
             return true;
         }
@@ -500,8 +584,8 @@ bool SystemTrayApplet::shouldHide(WId w)
 
 void SystemTrayApplet::updateVisibleWins()
 {
-    TrayEmbedList::const_iterator lastEmb = m_hiddenWins.end();
-    TrayEmbedList::const_iterator emb = m_hiddenWins.begin();
+    TrayWidgetList::const_iterator lastEmb = m_hiddenWins.end();
+    TrayWidgetList::const_iterator emb = m_hiddenWins.begin();
 
     if (m_showHidden)
     {
@@ -574,10 +658,10 @@ void SystemTrayApplet::retract()
 
 void SystemTrayApplet::updateTrayWindows()
 {
-    TrayEmbed* emb = m_shownWins.first();
+    TrayWidget* emb = m_shownWins.first();
     while ((emb = m_shownWins.current()) != 0)
     {
-        WId wid = emb->embeddedWinId();
+        WId wid = emb->windowId();
         if ((wid == 0) ||
             (emb->kdeTray() && !kwin_module->systemTrayWindows().contains(wid)))
         {
@@ -593,7 +677,7 @@ void SystemTrayApplet::updateTrayWindows
     emb = m_hiddenWins.first();
     while ((emb = m_hiddenWins.current()) != 0L)
     {
-        WId wid = emb->embeddedWinId();
+        WId wid = emb->windowId();
         if ((wid == 0) ||
             (emb->kdeTray() && !kwin_module->systemTrayWindows().contains(wid)))
             m_hiddenWins.remove(emb);
@@ -612,8 +696,8 @@ int SystemTrayApplet::maxIconWidth() con
 {
     int largest = 24;
 
-    TrayEmbedList::const_iterator lastEmb = m_shownWins.end();
-    for (TrayEmbedList::const_iterator emb = m_shownWins.begin(); emb != lastEmb; \
++emb) +    TrayWidgetList::const_iterator lastEmb = m_shownWins.end();
+    for (TrayWidgetList::const_iterator emb = m_shownWins.begin(); emb != lastEmb; \
++emb)  {
         if (*emb == 0)
         {
@@ -630,7 +714,7 @@ int SystemTrayApplet::maxIconWidth() con
     if (m_showHidden)
     {
         lastEmb = m_hiddenWins.end();
-        for (TrayEmbedList::const_iterator emb = m_hiddenWins.begin(); emb != \
lastEmb; ++emb) +        for (TrayWidgetList::const_iterator emb = \
m_hiddenWins.begin(); emb != lastEmb; ++emb)  {
             if (*emb == 0)
             {
@@ -652,8 +736,8 @@ int SystemTrayApplet::maxIconHeight() co
 {
     int largest = 24;
 
-    TrayEmbedList::const_iterator lastEmb = m_shownWins.end();
-    for (TrayEmbedList::const_iterator emb = m_shownWins.begin(); emb != \
m_shownWins.end(); ++emb) +    TrayWidgetList::const_iterator lastEmb = \
m_shownWins.end(); +    for (TrayWidgetList::const_iterator emb = \
m_shownWins.begin(); emb != m_shownWins.end(); ++emb)  {
         if (*emb == 0)
         {
@@ -670,7 +754,7 @@ int SystemTrayApplet::maxIconHeight() co
     if (m_showHidden)
     {
         lastEmb = m_hiddenWins.end();
-        for (TrayEmbedList::const_iterator emb = m_hiddenWins.begin(); emb != \
m_hiddenWins.end(); ++emb) +        for (TrayWidgetList::const_iterator emb = \
m_hiddenWins.begin(); emb != m_hiddenWins.end(); ++emb)  {
             if (*emb == 0)
             {
@@ -817,8 +901,8 @@ void SystemTrayApplet::layoutTray()
 
         if (m_showHidden)
         {
-            TrayEmbedList::const_iterator lastEmb = m_hiddenWins.end();
-            for (TrayEmbedList::const_iterator emb = m_hiddenWins.begin(); emb != \
lastEmb; ++emb) +            TrayWidgetList::const_iterator lastEmb = \
m_hiddenWins.end(); +            for (TrayWidgetList::const_iterator emb = \
m_hiddenWins.begin(); emb != lastEmb; ++emb)  {
                 line = i % nbrOfLines;
                 (*emb)->hide();
@@ -832,8 +916,8 @@ void SystemTrayApplet::layoutTray()
             }
         }
 
-        TrayEmbedList::const_iterator lastEmb = m_shownWins.end();
-        for (TrayEmbedList::const_iterator emb = m_shownWins.begin(); emb != \
lastEmb; ++emb) +        TrayWidgetList::const_iterator lastEmb = m_shownWins.end();
+        for (TrayWidgetList::const_iterator emb = m_shownWins.begin(); emb != \
lastEmb; ++emb)  {
             line = i % nbrOfLines;
             (*emb)->hide();
@@ -855,8 +939,8 @@ void SystemTrayApplet::layoutTray()
 
         if (m_showHidden)
         {
-            TrayEmbedList::const_iterator lastEmb = m_hiddenWins.end();
-            for (TrayEmbedList::const_iterator emb = m_hiddenWins.begin(); emb != \
lastEmb; ++emb) +            TrayWidgetList::const_iterator lastEmb = \
m_hiddenWins.end(); +            for (TrayWidgetList::const_iterator emb = \
m_hiddenWins.begin(); emb != lastEmb; ++emb)  {
                 line = i % nbrOfLines;
                 (*emb)->hide();
@@ -870,8 +954,8 @@ void SystemTrayApplet::layoutTray()
             }
         }
 
-        TrayEmbedList::const_iterator lastEmb = m_shownWins.end();
-        for (TrayEmbedList::const_iterator emb = m_shownWins.begin(); emb != \
lastEmb; ++emb) +        TrayWidgetList::const_iterator lastEmb = m_shownWins.end();
+        for (TrayWidgetList::const_iterator emb = m_shownWins.begin(); emb != \
lastEmb; ++emb)  {
             line = i % nbrOfLines;
             (*emb)->hide();
@@ -898,8 +982,8 @@ void SystemTrayApplet::layoutTray()
 
 void SystemTrayApplet::paletteChange(const QPalette & /* oldPalette */)
 {
-    TrayEmbedList::const_iterator lastEmb = m_shownWins.end();
-    for (TrayEmbedList::const_iterator emb = m_shownWins.begin(); emb != lastEmb; \
++emb) +    TrayWidgetList::const_iterator lastEmb = m_shownWins.end();
+    for (TrayWidgetList::const_iterator emb = m_shownWins.begin(); emb != lastEmb; \
++emb)  {
         (*emb)->hide();
         (*emb)->show();
@@ -912,3 +996,24 @@ TrayEmbed::TrayEmbed( bool kdeTray, QWid
 //    if( kde_tray ) // after QXEmbed reparents windows to the root window as \
unmapped.  //        setMapAfterRelease( true ); // systray one will have to be made \
visible somehow  }
+
+TrayIcon::TrayIcon( WId w, QWidget* parent )
+    : QLabel( parent ), window( w )
+{
+}
+
+void TrayIcon::mousePressEvent( QMouseEvent* e )
+{
+    if( e->button() == LeftButton )
+    {
+        if( KWin::windowInfo( window, NET::WMState ).hasState( NET::TrayedHidden ))
+        {
+            KWin::clearState( window, NET::TrayedHidden );
+            KWin::forceActiveWindow( window );
+            return;
+        }
+        else
+            KWin::setState( window, NET::TrayedHidden );
+    }
+    return QLabel::mousePressEvent( e );
+}
--- systemtray/systemtrayapplet.h.sav	2005-02-21 15:38:58.000000000 +0100
+++ systemtray/systemtrayapplet.h	2005-04-14 18:41:42.000000000 +0200
@@ -27,6 +27,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE 
 #include <qptrlist.h>
 #include <qstringlist.h>
 #include <qxembed.h>
+#include <qlabel.h>
 
 #include <dcopobject.h>
 #include <kapplication.h>
@@ -34,7 +35,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE 
 
 class QTimer;
 class KWinModule;
-class TrayEmbed;
+class TrayWidget;
 class KDialogBase;
 class KActionSelector;
 class HideButton;
@@ -43,7 +44,7 @@ class SystemTrayApplet : public KPanelAp
 {
     Q_OBJECT
     K_DCOP
-    typedef QPtrList<TrayEmbed> TrayEmbedList;
+    typedef QPtrList<TrayWidget> TrayWidgetList;
 
 public:
 
@@ -70,6 +71,8 @@ protected:
 protected slots:
     void initialize();
     void systemTrayWindowAdded( WId );
+    void trayedWindowAdded( WId );
+    void trayedWindowRemoved( WId );
     void updateTrayWindows();
     void layoutTray();
     void paletteChange(const QPalette & /* oldPalette */);
@@ -79,7 +82,9 @@ protected slots:
     void applySettings();
 
 private:
-    void embedWindow( WId w, bool kde_tray );
+    void windowAdded( WId w, bool kde_tray, bool xdg );
+    void embedWindow( WId w, bool kde_tray);
+    void trayWindow( WId w );
     bool isWinManaged( WId w);
     bool shouldHide( WId w);
     void updateVisibleWins();
@@ -88,8 +93,8 @@ private:
     void showExpandButton(bool show);
     void refreshExpandButton();
 
-    TrayEmbedList m_shownWins;
-    TrayEmbedList m_hiddenWins;
+    TrayWidgetList m_shownWins;
+    TrayWidgetList m_hiddenWins;
     QStringList m_hiddenIconList;
     KWinModule *kwin_module;
     Atom net_system_tray_selection;
@@ -102,14 +107,48 @@ private:
     KActionSelector* m_iconSelector;
 };
 
-class TrayEmbed : public QXEmbed
+class TrayWidget
+{
+public:
+    virtual ~TrayWidget() {};
+    virtual WId windowId() const = 0;
+    virtual bool kdeTray() const = 0;
+    virtual void show() = 0;
+    virtual void hide() = 0;
+    virtual QSize sizeHint() const = 0;
+    virtual void move( int x, int y ) = 0;
+};
+
+class TrayEmbed : public QXEmbed, public TrayWidget
 {
     Q_OBJECT
 public:
     TrayEmbed( bool kdeTray, QWidget* parent = NULL );
-    bool kdeTray() const { return kde_tray; }
+    virtual WId windowId() const { return embeddedWinId(); }
+    virtual bool kdeTray() const { return kde_tray; }
+    virtual void show() { QXEmbed::show(); }
+    virtual void hide() { QXEmbed::hide(); }
+    virtual QSize sizeHint() const { return QXEmbed::sizeHint(); }
+    virtual void move( int x, int y ) { return QXEmbed::move( x, y ); }
 private:
     bool kde_tray;
 };
 
+class TrayIcon : public QLabel, public TrayWidget
+{
+    Q_OBJECT
+public:
+    TrayIcon( WId window, QWidget* parent = NULL );
+    virtual WId windowId() const { return window; }
+    virtual bool kdeTray() const { return false; }
+    virtual void show() { QLabel::show(); }
+    virtual void hide() { QLabel::hide(); }
+    virtual QSize sizeHint() const { return QLabel::sizeHint(); }
+    virtual void move( int x, int y ) { return QLabel::move( x, y ); }
+protected:
+    virtual void mousePressEvent( QMouseEvent* e );
+private:
+    WId window;
+};
+
 #endif



[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic