00001 /*
00002 * Copyright (C) 2004 Lubos Lunak <l.lunak@kde.org>
00003 *
00004 * This program is free software; you can redistribute it and/or modify
00005 * it under the terms of the GNU General Public License as published by
00006 * the Free Software Foundation; either version 2 of the License, or
00007 * (at your option) any later version.
00008 *
00009 * This program is distributed in the hope that it will be useful,
00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00012 * GNU General Public License for more details.
00013 *
00014 * You should have received a copy of the GNU General Public License
00015 * along with this program; if not, write to the Free Software
00016 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017 *
00018 */
00019
00020 #include "kdetrayproxy.h"
00021
00022 #include <kapplication.h>
00023 #include <kdebug.h>
00024 #include <netwm.h>
00025 #include <X11/Xlib.h>
00026 #include <sys/select.h>
00027 #include <sys/time.h>
00028 #include <sys/types.h>
00029 #include <unistd.h>
00030 #include <assert.h>
00031
00032 KDETrayProxy::KDETrayProxy()
00033 : selection( makeSelectionAtom())
00034 {
00035 connect( &selection, SIGNAL( newOwner( Window )), SLOT( newOwner( Window )));
00036 connect( &module, SIGNAL( windowAdded( WId )), SLOT( windowAdded( WId )));
00037 selection.owner();
00038 for( QValueList< WId >::ConstIterator it = module.windows().begin();
00039 it != module.windows().end();
00040 ++it )
00041 windowAdded( *it );
00042 kapp->installX11EventFilter( this ); // XSelectInput( StructureNotifyMask ) on windows is done by KWinModule
00043 // kdDebug() << "Init done" << endl;
00044 }
00045
00046 Atom KDETrayProxy::makeSelectionAtom()
00047 {
00048 return XInternAtom( qt_xdisplay(), "_NET_SYSTEM_TRAY_S" + QCString().setNum( qt_xscreen()), False );
00049 }
00050
00051 extern Time qt_x_time;
00052
00053 void KDETrayProxy::windowAdded( WId w )
00054 {
00055 NETWinInfo ni( qt_xdisplay(), w, qt_xrootwin(), NET::WMKDESystemTrayWinFor );
00056 WId trayWinFor = ni.kdeSystemTrayWinFor();
00057 if ( !trayWinFor ) // not a KDE tray window
00058 return;
00059 // kdDebug() << "New tray window:" << w << endl;
00060 if( !tray_windows.contains( w ))
00061 tray_windows.append( w );
00062 withdrawWindow( w );
00063 // window will be removed from pending_windows when after docked
00064 if( !pending_windows.contains( w ))
00065 pending_windows.append( w );
00066 docked_windows.remove( w );
00067 Window owner = selection.owner();
00068 if( owner == None ) // no tray owner, sorry
00069 {
00070 // kdDebug() << "No owner, left in pending" << endl;
00071 return;
00072 }
00073 dockWindow( w, owner );
00074 }
00075
00076 void KDETrayProxy::newOwner( Window owner )
00077 {
00078 // kdDebug() << "New owner:" << owner << endl;
00079 for( QValueList< Window >::ConstIterator it = pending_windows.begin();
00080 it != pending_windows.end();
00081 ++it )
00082 dockWindow( *it, owner );
00083 // remove from pending_windows only in windowRemoved(), after it's really docked
00084 }
00085
00086 bool KDETrayProxy::x11Event( XEvent* e )
00087 {
00088 if( tray_windows.isEmpty())
00089 return false;
00090 if( e->type == DestroyNotify && tray_windows.contains( e->xdestroywindow.window ))
00091 {
00092 tray_windows.remove( e->xdestroywindow.window );
00093 pending_windows.remove( e->xdestroywindow.window );
00094 docked_windows.remove( e->xdestroywindow.window );
00095 }
00096 if( e->type == ReparentNotify && tray_windows.contains( e->xreparent.window ))
00097 {
00098 if( e->xreparent.parent == qt_xrootwin())
00099 {
00100 if( !docked_windows.contains( e->xreparent.window ) || e->xreparent.serial >= docked_windows[ e->xreparent.window ] )
00101 {
00102 // kdDebug() << "Window released:" << e->xreparent.window << endl;
00103 docked_windows.remove( e->xreparent.window );
00104 if( !pending_windows.contains( e->xreparent.window ))
00105 pending_windows.append( e->xreparent.window );
00106 }
00107 }
00108 else
00109 {
00110 // kdDebug() << "Window away:" << e->xreparent.window << ":" << e->xreparent.parent << endl;
00111 pending_windows.remove( e->xreparent.window );
00112 }
00113 }
00114 if( e->type == UnmapNotify && tray_windows.contains( e->xunmap.window ))
00115 {
00116 if( docked_windows.contains( e->xunmap.window ) && e->xunmap.serial >= docked_windows[ e->xunmap.window ] )
00117 {
00118 // kdDebug() << "Window unmapped:" << e->xunmap.window << endl;
00119 XReparentWindow( qt_xdisplay(), e->xunmap.window, qt_xrootwin(), 0, 0 );
00120 // ReparentNotify will take care of the rest
00121 }
00122 }
00123 return false;
00124 }
00125
00126 void KDETrayProxy::dockWindow( Window w, Window owner )
00127 {
00128 // kdDebug() << "Docking " << w << " into " << owner << endl;
00129 docked_windows[ w ] = XNextRequest( qt_xdisplay());
00130 static Atom prop = XInternAtom( qt_xdisplay(), "_XEMBED_INFO", False );
00131 long data[ 2 ] = { 0, 1 };
00132 XChangeProperty( qt_xdisplay(), w, prop, prop, 32, PropModeReplace, (unsigned char*)data, 2 );
00133 XSizeHints hints;
00134 hints.flags = PMinSize | PMaxSize;
00135 hints.min_width = 24;
00136 hints.max_width = 24;
00137 hints.min_height = 24;
00138 hints.max_height = 24;
00139 XSetWMNormalHints( qt_xdisplay(), w, &hints );
00140 // kxerrorhandler ?
00141 XEvent ev;
00142 memset(&ev, 0, sizeof( ev ));
00143 static Atom atom = XInternAtom( qt_xdisplay(), "_NET_SYSTEM_TRAY_OPCODE", False );
00144 ev.xclient.type = ClientMessage;
00145 ev.xclient.window = owner;
00146 ev.xclient.message_type = atom;
00147 ev.xclient.format = 32;
00148 ev.xclient.data.l[ 0 ] = qt_x_time;
00149 ev.xclient.data.l[ 1 ] = 0; // SYSTEM_TRAY_REQUEST_DOCK
00150 ev.xclient.data.l[ 2 ] = w;
00151 ev.xclient.data.l[ 3 ] = 0; // unused
00152 ev.xclient.data.l[ 4 ] = 0; // unused
00153 XSendEvent( qt_xdisplay(), owner, False, NoEventMask, &ev );
00154 }
00155
00156 void KDETrayProxy::withdrawWindow( Window w )
00157 {
00158 XWithdrawWindow( qt_xdisplay(), w, qt_xscreen());
00159 static Atom wm_state = XInternAtom( qt_xdisplay(), "WM_STATE", False );
00160 for(;;)
00161 {
00162 Atom type;
00163 int format;
00164 unsigned long length, after;
00165 unsigned char *data;
00166 int r = XGetWindowProperty( qt_xdisplay(), w, wm_state, 0, 2,
00167 False, AnyPropertyType, &type, &format,
00168 &length, &after, &data );
00169 bool withdrawn = true;
00170 if ( r == Success && data && format == 32 )
00171 {
00172 withdrawn = ( *( long* )data == WithdrawnState );
00173 XFree( (char *)data );
00174 }
00175 if( withdrawn )
00176 return; // --->
00177 struct timeval tm;
00178 tm.tv_sec = 0;
00179 tm.tv_usec = 10 * 1000; // 10ms
00180 select(0, NULL, NULL, NULL, &tm);
00181 }
00182 }
00183
00184 #include "kdetrayproxy.moc"
00185
00186 #if 0
00187 #include <kcmdlineargs.h>
00188 int main( int argc, char* argv[] )
00189 {
00190 KCmdLineArgs::init( argc, argv, "a", "b", "c", "d" );
00191 KApplication app( false ); // no styles
00192 app.disableSessionManagement();
00193 KDETrayProxy proxy;
00194 return app.exec();
00195 }
00196 #endif