kdetrayproxy.cpp
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, TQT_SIGNAL( newOwner( Window )), TQT_SLOT( newOwner( Window ))); 00036 connect( &module, TQT_SIGNAL( windowAdded( WId )), TQT_SLOT( windowAdded( WId ))); 00037 selection.owner(); 00038 for( TQValueList< 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" + TQCString().setNum( qt_xscreen()), False ); 00049 } 00050 00051 void KDETrayProxy::windowAdded( WId w ) 00052 { 00053 NETWinInfo ni( qt_xdisplay(), w, qt_xrootwin(), NET::WMKDESystemTrayWinFor ); 00054 WId trayWinFor = ni.kdeSystemTrayWinFor(); 00055 if ( !trayWinFor ) // not a KDE tray window 00056 return; 00057 // kdDebug() << "New tray window:" << w << endl; 00058 if( !tray_windows.contains( w )) 00059 tray_windows.append( w ); 00060 withdrawWindow( w ); 00061 // window will be removed from pending_windows when after docked 00062 if( !pending_windows.contains( w )) 00063 pending_windows.append( w ); 00064 docked_windows.remove( w ); 00065 Window owner = selection.owner(); 00066 if( owner == None ) // no tray owner, sorry 00067 { 00068 // kdDebug() << "No owner, left in pending" << endl; 00069 return; 00070 } 00071 dockWindow( w, owner ); 00072 } 00073 00074 void KDETrayProxy::newOwner( Window owner ) 00075 { 00076 // kdDebug() << "New owner:" << owner << endl; 00077 for( TQValueList< Window >::ConstIterator it = pending_windows.begin(); 00078 it != pending_windows.end(); 00079 ++it ) 00080 dockWindow( *it, owner ); 00081 // remove from pending_windows only in windowRemoved(), after it's really docked 00082 } 00083 00084 bool KDETrayProxy::x11Event( XEvent* e ) 00085 { 00086 if( tray_windows.isEmpty()) 00087 return false; 00088 if( e->type == DestroyNotify && tray_windows.contains( e->xdestroywindow.window )) 00089 { 00090 tray_windows.remove( e->xdestroywindow.window ); 00091 pending_windows.remove( e->xdestroywindow.window ); 00092 docked_windows.remove( e->xdestroywindow.window ); 00093 } 00094 if( e->type == ReparentNotify && tray_windows.contains( e->xreparent.window )) 00095 { 00096 if( e->xreparent.parent == qt_xrootwin()) 00097 { 00098 if( !docked_windows.contains( e->xreparent.window ) || e->xreparent.serial >= docked_windows[ e->xreparent.window ] ) 00099 { 00100 // kdDebug() << "Window released:" << e->xreparent.window << endl; 00101 docked_windows.remove( e->xreparent.window ); 00102 if( !pending_windows.contains( e->xreparent.window )) 00103 pending_windows.append( e->xreparent.window ); 00104 } 00105 } 00106 else 00107 { 00108 // kdDebug() << "Window away:" << e->xreparent.window << ":" << e->xreparent.parent << endl; 00109 pending_windows.remove( e->xreparent.window ); 00110 } 00111 } 00112 if( e->type == UnmapNotify && tray_windows.contains( e->xunmap.window )) 00113 { 00114 if( docked_windows.contains( e->xunmap.window ) && e->xunmap.serial >= docked_windows[ e->xunmap.window ] ) 00115 { 00116 // kdDebug() << "Window unmapped:" << e->xunmap.window << endl; 00117 XReparentWindow( qt_xdisplay(), e->xunmap.window, qt_xrootwin(), 0, 0 ); 00118 // ReparentNotify will take care of the rest 00119 } 00120 } 00121 return false; 00122 } 00123 00124 void KDETrayProxy::dockWindow( Window w, Window owner ) 00125 { 00126 // kdDebug() << "Docking " << w << " into " << owner << endl; 00127 docked_windows[ w ] = XNextRequest( qt_xdisplay()); 00128 static Atom prop = XInternAtom( qt_xdisplay(), "_XEMBED_INFO", False ); 00129 long data[ 2 ] = { 0, 1 }; 00130 XChangeProperty( qt_xdisplay(), w, prop, prop, 32, PropModeReplace, (unsigned char*)data, 2 ); 00131 XSizeHints hints; 00132 hints.flags = PMinSize | PMaxSize; 00133 hints.min_width = 24; 00134 hints.max_width = 24; 00135 hints.min_height = 24; 00136 hints.max_height = 24; 00137 XSetWMNormalHints( qt_xdisplay(), w, &hints ); 00138 // kxerrorhandler ? 00139 XEvent ev; 00140 memset(&ev, 0, sizeof( ev )); 00141 static Atom atom = XInternAtom( qt_xdisplay(), "_NET_SYSTEM_TRAY_OPCODE", False ); 00142 ev.xclient.type = ClientMessage; 00143 ev.xclient.window = owner; 00144 ev.xclient.message_type = atom; 00145 ev.xclient.format = 32; 00146 ev.xclient.data.l[ 0 ] = GET_QT_X_TIME(); 00147 ev.xclient.data.l[ 1 ] = 0; // SYSTEM_TRAY_REQUEST_DOCK 00148 ev.xclient.data.l[ 2 ] = w; 00149 ev.xclient.data.l[ 3 ] = 0; // unused 00150 ev.xclient.data.l[ 4 ] = 0; // unused 00151 XSendEvent( qt_xdisplay(), owner, False, NoEventMask, &ev ); 00152 } 00153 00154 void KDETrayProxy::withdrawWindow( Window w ) 00155 { 00156 XWithdrawWindow( qt_xdisplay(), w, qt_xscreen()); 00157 static Atom wm_state = XInternAtom( qt_xdisplay(), "WM_STATE", False ); 00158 for(;;) 00159 { 00160 Atom type; 00161 int format; 00162 unsigned long length, after; 00163 unsigned char *data; 00164 int r = XGetWindowProperty( qt_xdisplay(), w, wm_state, 0, 2, 00165 False, AnyPropertyType, &type, &format, 00166 &length, &after, &data ); 00167 bool withdrawn = true; 00168 if ( r == Success && data && format == 32 ) 00169 { 00170 withdrawn = ( *( long* )data == WithdrawnState ); 00171 XFree( (char *)data ); 00172 } 00173 if( withdrawn ) 00174 return; // ---> 00175 struct timeval tm; 00176 tm.tv_sec = 0; 00177 tm.tv_usec = 10 * 1000; // 10ms 00178 select(0, NULL, NULL, NULL, &tm); 00179 } 00180 } 00181 00182 #include "kdetrayproxy.moc" 00183 00184 #if 0 00185 #include <kcmdlineargs.h> 00186 int main( int argc, char* argv[] ) 00187 { 00188 KCmdLineArgs::init( argc, argv, "a", "b", "c", "d" ); 00189 KApplication app( false ); // no styles 00190 app.disableSessionManagement(); 00191 KDETrayProxy proxy; 00192 return app.exec(); 00193 } 00194 #endif