00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #if !defined(_POSIX_C_SOURCE)
00021 #define _POSIX_C_SOURCE 199309
00022 #elif _POSIX_C_SOURCE < 199309
00023 #undef _POSIX_C_SOURCE
00024 #define _POSIX_C_SOURCE 199309
00025 #endif
00026
00027 #include "kdetrayproxy.h"
00028
00029 #include <kapplication.h>
00030 #include <kdebug.h>
00031 #include <netwm.h>
00032 #include <X11/Xlib.h>
00033 #include <time.h>
00034 #include <assert.h>
00035
00036
00037 #if defined(__FreeBSD__)
00038 #include <osreldate.h>
00039 #if __FreeBSD_version < 500042
00040 #warning FreeBSD 4.x compatibility shims in effect
00041 #include <sys/time.h>
00042
00043 int nanosleep(const struct timespec *, struct timespec *);
00044 #endif
00045 #endif
00046
00047 KDETrayProxy::KDETrayProxy()
00048 : selection( makeSelectionAtom())
00049 {
00050 connect( &selection, SIGNAL( newOwner( Window )), SLOT( newOwner( Window )));
00051 connect( &module, SIGNAL( windowAdded( WId )), SLOT( windowAdded( WId )));
00052 selection.owner();
00053 for( QValueList< WId >::ConstIterator it = module.windows().begin();
00054 it != module.windows().end();
00055 ++it )
00056 windowAdded( *it );
00057 kapp->installX11EventFilter( this );
00058
00059 }
00060
00061 Atom KDETrayProxy::makeSelectionAtom()
00062 {
00063 return XInternAtom( qt_xdisplay(), "_NET_SYSTEM_TRAY_S" + QCString().setNum( qt_xscreen()), False );
00064 }
00065
00066 extern Time qt_x_time;
00067
00068 void KDETrayProxy::windowAdded( WId w )
00069 {
00070 NETWinInfo ni( qt_xdisplay(), w, qt_xrootwin(), NET::WMKDESystemTrayWinFor );
00071 WId trayWinFor = ni.kdeSystemTrayWinFor();
00072 if ( !trayWinFor )
00073 return;
00074
00075 if( !tray_windows.contains( w ))
00076 tray_windows.append( w );
00077 withdrawWindow( w );
00078
00079 if( !pending_windows.contains( w ))
00080 pending_windows.append( w );
00081 docked_windows.remove( w );
00082 Window owner = selection.owner();
00083 if( owner == None )
00084 {
00085
00086 return;
00087 }
00088 dockWindow( w, owner );
00089 }
00090
00091 void KDETrayProxy::newOwner( Window owner )
00092 {
00093
00094 for( QValueList< Window >::ConstIterator it = pending_windows.begin();
00095 it != pending_windows.end();
00096 ++it )
00097 dockWindow( *it, owner );
00098
00099 }
00100
00101 bool KDETrayProxy::x11Event( XEvent* e )
00102 {
00103 if( tray_windows.isEmpty())
00104 return false;
00105 if( e->type == DestroyNotify && tray_windows.contains( e->xdestroywindow.window ))
00106 {
00107 tray_windows.remove( e->xdestroywindow.window );
00108 pending_windows.remove( e->xdestroywindow.window );
00109 docked_windows.remove( e->xdestroywindow.window );
00110 }
00111 if( e->type == ReparentNotify && tray_windows.contains( e->xreparent.window ))
00112 {
00113 if( e->xreparent.parent == qt_xrootwin())
00114 {
00115 if( !docked_windows.contains( e->xreparent.window ) || e->xreparent.serial >= docked_windows[ e->xreparent.window ] )
00116 {
00117
00118 docked_windows.remove( e->xreparent.window );
00119 if( !pending_windows.contains( e->xreparent.window ))
00120 pending_windows.append( e->xreparent.window );
00121 }
00122 }
00123 else
00124 {
00125
00126 pending_windows.remove( e->xreparent.window );
00127 }
00128 }
00129 if( e->type == UnmapNotify && tray_windows.contains( e->xunmap.window ))
00130 {
00131 if( docked_windows.contains( e->xunmap.window ) && e->xunmap.serial >= docked_windows[ e->xunmap.window ] )
00132 {
00133
00134 XReparentWindow( qt_xdisplay(), e->xunmap.window, qt_xrootwin(), 0, 0 );
00135
00136 }
00137 }
00138 return false;
00139 }
00140
00141 void KDETrayProxy::dockWindow( Window w, Window owner )
00142 {
00143
00144 docked_windows[ w ] = XNextRequest( qt_xdisplay());
00145 static Atom prop = XInternAtom( qt_xdisplay(), "_XEMBED_INFO", False );
00146 long data[ 2 ] = { 0, 1 };
00147 XChangeProperty( qt_xdisplay(), w, prop, prop, 32, PropModeReplace, (unsigned char*)data, 2 );
00148 XSizeHints hints;
00149 hints.flags = PMinSize | PMaxSize;
00150 hints.min_width = 24;
00151 hints.max_width = 24;
00152 hints.min_height = 24;
00153 hints.max_height = 24;
00154 XSetWMNormalHints( qt_xdisplay(), w, &hints );
00155
00156 XEvent ev;
00157 memset(&ev, 0, sizeof( ev ));
00158 static Atom atom = XInternAtom( qt_xdisplay(), "_NET_SYSTEM_TRAY_OPCODE", False );
00159 ev.xclient.type = ClientMessage;
00160 ev.xclient.window = owner;
00161 ev.xclient.message_type = atom;
00162 ev.xclient.format = 32;
00163 ev.xclient.data.l[ 0 ] = qt_x_time;
00164 ev.xclient.data.l[ 1 ] = 0;
00165 ev.xclient.data.l[ 2 ] = w;
00166 ev.xclient.data.l[ 3 ] = 0;
00167 ev.xclient.data.l[ 4 ] = 0;
00168 XSendEvent( qt_xdisplay(), owner, False, NoEventMask, &ev );
00169 }
00170
00171 void KDETrayProxy::withdrawWindow( Window w )
00172 {
00173 XWithdrawWindow( qt_xdisplay(), w, qt_xscreen());
00174 static Atom wm_state = XInternAtom( qt_xdisplay(), "WM_STATE", False );
00175 for(;;)
00176 {
00177 Atom type;
00178 int format;
00179 unsigned long length, after;
00180 unsigned char *data;
00181 int r = XGetWindowProperty( qt_xdisplay(), w, wm_state, 0, 2,
00182 False, AnyPropertyType, &type, &format,
00183 &length, &after, &data );
00184 bool withdrawn = true;
00185 if ( r == Success && data && format == 32 )
00186 {
00187 withdrawn = ( *( long* )data == WithdrawnState );
00188 XFree( (char *)data );
00189 }
00190 if( withdrawn )
00191 return;
00192 struct timespec tm;
00193 tm.tv_sec = 0;
00194 tm.tv_nsec = 10 * 1000 * 1000;
00195 nanosleep( &tm, NULL );
00196 }
00197 }
00198
00199 #include "kdetrayproxy.moc"
00200
00201 #if 0
00202 #include <kcmdlineargs.h>
00203 int main( int argc, char* argv[] )
00204 {
00205 KCmdLineArgs::init( argc, argv, "a", "b", "c", "d" );
00206 KApplication app( false );
00207 app.disableSessionManagement();
00208 KDETrayProxy proxy;
00209 return app.exec();
00210 }
00211 #endif