kwin Library API Documentation

workspace.cpp

00001 /***************************************************************** 00002 KWin - the KDE window manager 00003 This file is part of the KDE project. 00004 00005 Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> 00006 Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> 00007 00008 You can Freely distribute this program under the GNU General Public 00009 License. See the file "COPYING" for the exact licensing terms. 00010 ******************************************************************/ 00011 00012 //#define QT_CLEAN_NAMESPACE 00013 00014 #include "workspace.h" 00015 00016 #include <kapplication.h> 00017 #include <kstartupinfo.h> 00018 #include <fixx11h.h> 00019 #include <kconfig.h> 00020 #include <kglobal.h> 00021 #include <qpopupmenu.h> 00022 #include <klocale.h> 00023 #include <qregexp.h> 00024 #include <qpainter.h> 00025 #include <qbitmap.h> 00026 #include <qclipboard.h> 00027 #include <kmenubar.h> 00028 #include <kprocess.h> 00029 #include <kglobalaccel.h> 00030 00031 #include "plugins.h" 00032 #include "client.h" 00033 #include "popupinfo.h" 00034 #include "tabbox.h" 00035 #include "atoms.h" 00036 #include "placement.h" 00037 #include "notifications.h" 00038 #include "group.h" 00039 #include "rules.h" 00040 00041 #include <X11/extensions/shape.h> 00042 #include <X11/keysym.h> 00043 #include <X11/keysymdef.h> 00044 #include <X11/cursorfont.h> 00045 00046 extern Time qt_x_time; 00047 00048 namespace KWinInternal 00049 { 00050 00051 extern int screen_number; 00052 00053 Workspace *Workspace::_self = 0; 00054 00055 // Rikkus: This class is too complex. It needs splitting further. 00056 // It's a nightmare to understand, especially with so few comments :( 00057 00058 // Matthias: Feel free to ask me questions about it. Feel free to add 00059 // comments. I dissagree that further splittings makes it easier. 2500 00060 // lines are not too much. It's the task that is complex, not the 00061 // code. 00062 Workspace::Workspace( bool restore ) 00063 : DCOPObject ("KWinInterface"), 00064 QObject (0, "workspace"), 00065 current_desktop (0), 00066 number_of_desktops(0), 00067 popup_client (0), 00068 desktop_widget (0), 00069 temporaryRulesMessages( "_KDE_NET_WM_TEMPORARY_RULES", NULL, false ), 00070 active_client (0), 00071 last_active_client (0), 00072 most_recently_raised (0), 00073 movingClient(0), 00074 pending_take_activity ( NULL ), 00075 delayfocus_client (0), 00076 was_user_interaction (false), 00077 session_saving (false), 00078 control_grab (false), 00079 tab_grab (false), 00080 mouse_emulation (false), 00081 block_focus (0), 00082 tab_box (0), 00083 popupinfo (0), 00084 popup (0), 00085 advanced_popup (0), 00086 desk_popup (0), 00087 desk_popup_index (0), 00088 keys (0), 00089 root (0), 00090 workspaceInit (true), 00091 startup(0), electric_have_borders(false), 00092 electric_current_border(0), 00093 electric_top_border(None), 00094 electric_bottom_border(None), 00095 electric_left_border(None), 00096 electric_right_border(None), 00097 layoutOrientation(Qt::Vertical), 00098 layoutX(-1), 00099 layoutY(2), 00100 workarea(NULL), 00101 screenarea(NULL), 00102 set_active_client_recursion( 0 ), 00103 block_stacking_updates( 0 ), 00104 forced_global_mouse_grab( false ) 00105 { 00106 _self = this; 00107 mgr = new PluginMgr; 00108 root = qt_xrootwin(); 00109 default_colormap = DefaultColormap(qt_xdisplay(), qt_xscreen() ); 00110 installed_colormap = default_colormap; 00111 session.setAutoDelete( TRUE ); 00112 00113 connect( &temporaryRulesMessages, SIGNAL( gotMessage( const QString& )), 00114 this, SLOT( gotTemporaryRulesMessage( const QString& ))); 00115 connect( &rulesUpdatedTimer, SIGNAL( timeout()), this, SLOT( writeWindowRules())); 00116 00117 updateXTime(); // needed for proper initialization of user_time in Client ctor 00118 00119 delayFocusTimer = 0; 00120 00121 electric_time_first = qt_x_time; 00122 electric_time_last = qt_x_time; 00123 00124 if ( restore ) 00125 loadSessionInfo(); 00126 00127 loadWindowRules(); 00128 00129 (void) QApplication::desktop(); // trigger creation of desktop widget 00130 00131 desktop_widget = 00132 new QWidget( 00133 0, 00134 "desktop_widget", 00135 Qt::WType_Desktop | Qt::WPaintUnclipped 00136 ); 00137 00138 kapp->setGlobalMouseTracking( true ); // so that this doesn't mess eventmask on root window later 00139 // call this before XSelectInput() on the root window 00140 startup = new KStartupInfo( 00141 KStartupInfo::DisableKWinModule | KStartupInfo::AnnounceSilenceChanges, this ); 00142 00143 // select windowmanager privileges 00144 XSelectInput(qt_xdisplay(), root, 00145 KeyPressMask | 00146 PropertyChangeMask | 00147 ColormapChangeMask | 00148 SubstructureRedirectMask | 00149 SubstructureNotifyMask | 00150 FocusChangeMask // for NotifyDetailNone 00151 ); 00152 00153 Shape::init(); 00154 00155 // compatibility 00156 long data = 1; 00157 00158 XChangeProperty( 00159 qt_xdisplay(), 00160 qt_xrootwin(), 00161 atoms->kwin_running, 00162 atoms->kwin_running, 00163 32, 00164 PropModeAppend, 00165 (unsigned char*) &data, 00166 1 00167 ); 00168 00169 initShortcuts(); 00170 tab_box = new TabBox( this ); 00171 popupinfo = new PopupInfo( ); 00172 00173 init(); 00174 00175 #if (QT_VERSION-0 >= 0x030200) // XRANDR support 00176 connect( kapp->desktop(), SIGNAL( resized( int )), SLOT( desktopResized())); 00177 #endif 00178 } 00179 00180 00181 void Workspace::init() 00182 { 00183 checkElectricBorders(); 00184 00185 supportWindow = new QWidget; 00186 XLowerWindow( qt_xdisplay(), supportWindow->winId()); // see usage in layers.cpp 00187 00188 XSetWindowAttributes attr; 00189 attr.override_redirect = 1; 00190 null_focus_window = XCreateWindow( qt_xdisplay(), qt_xrootwin(), -1,-1, 1, 1, 0, CopyFromParent, 00191 InputOnly, CopyFromParent, CWOverrideRedirect, &attr ); 00192 XMapWindow(qt_xdisplay(), null_focus_window); 00193 00194 unsigned long protocols[ 5 ] = 00195 { 00196 NET::Supported | 00197 NET::SupportingWMCheck | 00198 NET::ClientList | 00199 NET::ClientListStacking | 00200 NET::DesktopGeometry | 00201 NET::NumberOfDesktops | 00202 NET::CurrentDesktop | 00203 NET::ActiveWindow | 00204 NET::WorkArea | 00205 NET::CloseWindow | 00206 NET::DesktopNames | 00207 NET::KDESystemTrayWindows | 00208 NET::WMName | 00209 NET::WMVisibleName | 00210 NET::WMDesktop | 00211 NET::WMWindowType | 00212 NET::WMState | 00213 NET::WMStrut | 00214 NET::WMIconGeometry | 00215 NET::WMIcon | 00216 NET::WMPid | 00217 NET::WMMoveResize | 00218 NET::WMKDESystemTrayWinFor | 00219 NET::WMKDEFrameStrut | 00220 NET::WMPing 00221 , 00222 NET::NormalMask | 00223 NET::DesktopMask | 00224 NET::DockMask | 00225 NET::ToolbarMask | 00226 NET::MenuMask | 00227 NET::DialogMask | 00228 NET::OverrideMask | 00229 NET::TopMenuMask | 00230 NET::UtilityMask | 00231 NET::SplashMask | 00232 0 00233 , 00234 NET::Modal | 00235 // NET::Sticky | // large desktops not supported (and probably never will be) 00236 NET::MaxVert | 00237 NET::MaxHoriz | 00238 NET::Shaded | 00239 NET::SkipTaskbar | 00240 NET::KeepAbove | 00241 // NET::StaysOnTop | the same like KeepAbove 00242 NET::SkipPager | 00243 NET::Hidden | 00244 NET::FullScreen | 00245 NET::KeepBelow | 00246 NET::DemandsAttention | 00247 0 00248 , 00249 NET::WM2UserTime | 00250 NET::WM2StartupId | 00251 NET::WM2AllowedActions | 00252 NET::WM2RestackWindow | 00253 NET::WM2MoveResizeWindow | 00254 NET::WM2ExtendedStrut | 00255 NET::WM2KDETemporaryRules | 00256 0 00257 , 00258 NET::ActionMove | 00259 NET::ActionResize | 00260 NET::ActionMinimize | 00261 NET::ActionShade | 00262 // NET::ActionStick | // Sticky state is not supported 00263 NET::ActionMaxVert | 00264 NET::ActionMaxHoriz | 00265 NET::ActionFullScreen | 00266 NET::ActionChangeDesktop | 00267 NET::ActionClose | 00268 0 00269 , 00270 }; 00271 00272 rootInfo = new RootInfo( this, qt_xdisplay(), supportWindow->winId(), "KWin", 00273 protocols, 5, qt_xscreen() ); 00274 00275 loadDesktopSettings(); 00276 // extra NETRootInfo instance in Client mode is needed to get the values of the properties 00277 NETRootInfo client_info( qt_xdisplay(), NET::ActiveWindow | NET::CurrentDesktop ); 00278 int initial_desktop; 00279 if( !kapp->isSessionRestored()) 00280 initial_desktop = client_info.currentDesktop(); 00281 else 00282 { 00283 KConfigGroupSaver saver( kapp->sessionConfig(), "Session" ); 00284 initial_desktop = kapp->sessionConfig()->readNumEntry( "desktop", 1 ); 00285 } 00286 if( !setCurrentDesktop( initial_desktop )) 00287 setCurrentDesktop( 1 ); 00288 00289 // now we know how many desktops we'll, thus, we initialise the positioning object 00290 initPositioning = new Placement(this); 00291 00292 connect(&reconfigureTimer, SIGNAL(timeout()), this, 00293 SLOT(slotReconfigure())); 00294 connect( &updateToolWindowsTimer, SIGNAL( timeout()), this, SLOT( slotUpdateToolWindows())); 00295 00296 connect(kapp, SIGNAL(appearanceChanged()), this, 00297 SLOT(slotReconfigure())); 00298 connect(kapp, SIGNAL(settingsChanged(int)), this, 00299 SLOT(slotSettingsChanged(int))); 00300 00301 active_client = NULL; 00302 rootInfo->setActiveWindow( None ); 00303 focusToNull(); 00304 if( !kapp->isSessionRestored()) 00305 ++block_focus; // because it will be set below 00306 00307 char nm[ 100 ]; 00308 sprintf( nm, "_KDE_TOPMENU_OWNER_S%d", DefaultScreen( qt_xdisplay())); 00309 Atom topmenu_atom = XInternAtom( qt_xdisplay(), nm, False ); 00310 topmenu_selection = new KSelectionOwner( topmenu_atom ); 00311 topmenu_watcher = new KSelectionWatcher( topmenu_atom ); 00312 topmenu_height = 0; 00313 managing_topmenus = false; 00314 topmenu_space = NULL; 00315 // TODO grabXServer(); - where exactly put this? topmenu selection claiming down belong must be before 00316 00317 { // begin updates blocker block 00318 StackingUpdatesBlocker blocker( this ); 00319 00320 if( options->topMenuEnabled() && topmenu_selection->claim( false )) 00321 setupTopMenuHandling(); // this can call updateStackingOrder() 00322 else 00323 lostTopMenuSelection(); 00324 00325 unsigned int i, nwins; 00326 Window root_return, parent_return, *wins; 00327 XQueryTree(qt_xdisplay(), root, &root_return, &parent_return, &wins, &nwins); 00328 for (i = 0; i < nwins; i++) 00329 { 00330 XWindowAttributes attr; 00331 XGetWindowAttributes(qt_xdisplay(), wins[i], &attr); 00332 if (attr.override_redirect ) 00333 continue; 00334 if( topmenu_space && topmenu_space->winId() == wins[ i ] ) 00335 continue; 00336 if (attr.map_state != IsUnmapped) 00337 { 00338 if ( addSystemTrayWin( wins[i] ) ) 00339 continue; 00340 Client* c = createClient( wins[i], true ); 00341 if ( c != NULL && root != qt_xrootwin() ) 00342 { // TODO what is this? 00343 // TODO may use QWidget:.create 00344 XReparentWindow( qt_xdisplay(), c->frameId(), root, 0, 0 ); 00345 c->move(0,0); 00346 } 00347 } 00348 } 00349 if ( wins ) 00350 XFree((void *) wins); 00351 // propagate clients, will really happen at the end of the updates blocker block 00352 updateStackingOrder( true ); 00353 00354 updateClientArea(); 00355 raiseElectricBorders(); 00356 00357 // NETWM spec says we have to set it to (0,0) if we don't support it 00358 NETPoint* viewports = new NETPoint[ number_of_desktops ]; 00359 rootInfo->setDesktopViewport( number_of_desktops, *viewports ); 00360 delete[] viewports; 00361 QRect geom = QApplication::desktop()->geometry(); 00362 NETSize desktop_geometry; 00363 desktop_geometry.width = geom.width(); 00364 desktop_geometry.height = geom.height(); 00365 // TODO update also after gaining XRANDR support 00366 rootInfo->setDesktopGeometry( -1, desktop_geometry ); 00367 00368 } // end updates blocker block 00369 00370 Client* new_active_client = NULL; 00371 if( !kapp->isSessionRestored()) 00372 { 00373 --block_focus; 00374 new_active_client = findClient( WindowMatchPredicate( client_info.activeWindow())); 00375 } 00376 if( new_active_client == NULL 00377 && activeClient() == NULL && should_get_focus.count() == 0 ) // no client activated in manage() 00378 { 00379 if( new_active_client == NULL ) 00380 new_active_client = topClientOnDesktop( currentDesktop()); 00381 if( new_active_client == NULL && !desktops.isEmpty() ) 00382 new_active_client = findDesktop( true, currentDesktop()); 00383 } 00384 if( new_active_client != NULL ) 00385 activateClient( new_active_client ); 00386 // SELI TODO this won't work with unreasonable focus policies, 00387 // and maybe in rare cases also if the selected client doesn't 00388 // want focus 00389 workspaceInit = false; 00390 // TODO ungrabXServer() 00391 } 00392 00393 Workspace::~Workspace() 00394 { 00395 blockStackingUpdates( true ); 00396 // TODO grabXServer(); 00397 // use stacking_order, so that kwin --replace keeps stacking order 00398 for( ClientList::ConstIterator it = stacking_order.begin(); 00399 it != stacking_order.end(); 00400 ++it ) 00401 { 00402 // only release the window 00403 (*it)->releaseWindow( true ); 00404 // no removeClient() is called ! 00405 } 00406 delete desktop_widget; 00407 delete tab_box; 00408 delete popupinfo; 00409 delete popup; 00410 if ( root == qt_xrootwin() ) 00411 XDeleteProperty(qt_xdisplay(), qt_xrootwin(), atoms->kwin_running); 00412 00413 writeWindowRules(); 00414 KGlobal::config()->sync(); 00415 00416 delete rootInfo; 00417 delete supportWindow; 00418 delete mgr; 00419 delete[] workarea; 00420 delete[] screenarea; 00421 delete startup; 00422 delete initPositioning; 00423 delete topmenu_watcher; 00424 delete topmenu_selection; 00425 delete topmenu_space; 00426 while( !rules.isEmpty()) 00427 { 00428 delete rules.front(); 00429 rules.pop_front(); 00430 } 00431 XDestroyWindow( qt_xdisplay(), null_focus_window ); 00432 // TODO ungrabXServer(); 00433 _self = 0; 00434 } 00435 00436 Client* Workspace::createClient( Window w, bool is_mapped ) 00437 { 00438 StackingUpdatesBlocker blocker( this ); 00439 Client* c = new Client( this ); 00440 if( !c->manage( w, is_mapped )) 00441 { 00442 Client::deleteClient( c, Allowed ); 00443 return NULL; 00444 } 00445 addClient( c, Allowed ); 00446 return c; 00447 } 00448 00449 void Workspace::addClient( Client* c, allowed_t ) 00450 { 00451 Group* grp = findGroup( c->window()); 00452 if( grp != NULL ) 00453 grp->gotLeader( c ); 00454 00455 if ( c->isDesktop() ) 00456 { 00457 desktops.append( c ); 00458 if( active_client == NULL && should_get_focus.isEmpty() && c->isOnCurrentDesktop()) 00459 requestFocus( c ); // CHECKME? make sure desktop is active after startup if there's no other window active 00460 } 00461 else 00462 { 00463 if ( c->wantsTabFocus() && !focus_chain.contains( c )) 00464 focus_chain.append( c ); 00465 clients.append( c ); 00466 } 00467 if( !unconstrained_stacking_order.contains( c )) 00468 unconstrained_stacking_order.append( c ); 00469 if( !stacking_order.contains( c )) // it'll be updated later, and updateToolWindows() requires 00470 stacking_order.append( c ); // c to be in stacking_order 00471 if( c->isTopMenu()) 00472 addTopMenu( c ); 00473 updateClientArea(); // this cannot be in manage(), because the client got added only now 00474 updateClientLayer( c ); 00475 if( c->isDesktop()) 00476 { 00477 raiseClient( c ); 00478 // if there's no active client, make this desktop the active one 00479 if( activeClient() == NULL && should_get_focus.count() == 0 ) 00480 activateClient( findDesktop( true, currentDesktop())); 00481 } 00482 checkTransients( c->window()); // SELI does this really belong here? 00483 updateStackingOrder( true ); // propagate new client 00484 if( c->isUtility() || c->isMenu() || c->isToolbar()) 00485 updateToolWindows( true ); 00486 } 00487 00488 /* 00489 Destroys the client \a c 00490 */ 00491 void Workspace::removeClient( Client* c, allowed_t ) 00492 { 00493 if (c == active_client && popup) 00494 popup->close(); 00495 if( c == popup_client ) 00496 popup_client = 0; 00497 00498 if( c->isDialog()) 00499 Notify::raise( Notify::TransDelete ); 00500 if( c->isNormalWindow()) 00501 Notify::raise( Notify::Delete ); 00502 00503 Q_ASSERT( clients.contains( c ) || desktops.contains( c )); 00504 clients.remove( c ); 00505 desktops.remove( c ); 00506 unconstrained_stacking_order.remove( c ); 00507 stacking_order.remove( c ); 00508 focus_chain.remove( c ); 00509 attention_chain.remove( c ); 00510 if( c->isTopMenu()) 00511 removeTopMenu( c ); 00512 Group* group = findGroup( c->window()); 00513 if( group != NULL ) 00514 group->lostLeader(); 00515 00516 if ( c == most_recently_raised ) 00517 most_recently_raised = 0; 00518 should_get_focus.remove( c ); 00519 Q_ASSERT( c != active_client ); 00520 if ( c == last_active_client ) 00521 last_active_client = 0; 00522 if( c == pending_take_activity ) 00523 pending_take_activity = NULL; 00524 if( c == delayfocus_client ) 00525 cancelDelayFocus(); 00526 00527 updateStackingOrder( true ); 00528 00529 if (tab_grab) 00530 tab_box->repaint(); 00531 00532 updateClientArea(); 00533 } 00534 00535 void Workspace::updateCurrentTopMenu() 00536 { 00537 if( !managingTopMenus()) 00538 return; 00539 // toplevel menubar handling 00540 Client* menubar = 0; 00541 bool block_desktop_menubar = false; 00542 if( active_client ) 00543 { 00544 // show the new menu bar first... 00545 Client* menu_client = active_client; 00546 for(;;) 00547 { 00548 if( menu_client->isFullScreen()) 00549 block_desktop_menubar = true; 00550 for( ClientList::ConstIterator it = menu_client->transients().begin(); 00551 it != menu_client->transients().end(); 00552 ++it ) 00553 if( (*it)->isTopMenu()) 00554 { 00555 menubar = *it; 00556 break; 00557 } 00558 if( menubar != NULL || !menu_client->isTransient()) 00559 break; 00560 if( menu_client->isModal() || menu_client->transientFor() == NULL ) 00561 break; // don't use mainwindow's menu if this is modal or group transient 00562 menu_client = menu_client->transientFor(); 00563 } 00564 if( !menubar ) 00565 { // try to find any topmenu from the application (#72113) 00566 for( ClientList::ConstIterator it = active_client->group()->members().begin(); 00567 it != active_client->group()->members().end(); 00568 ++it ) 00569 if( (*it)->isTopMenu()) 00570 { 00571 menubar = *it; 00572 break; 00573 } 00574 } 00575 } 00576 if( !menubar && !block_desktop_menubar && options->desktopTopMenu()) 00577 { 00578 // Find the menubar of the desktop 00579 Client* desktop = findDesktop( true, currentDesktop()); 00580 if( desktop != NULL ) 00581 { 00582 for( ClientList::ConstIterator it = desktop->transients().begin(); 00583 it != desktop->transients().end(); 00584 ++it ) 00585 if( (*it)->isTopMenu()) 00586 { 00587 menubar = *it; 00588 break; 00589 } 00590 } 00591 // TODO to be cleaned app with window grouping 00592 // Without qt-copy patch #0009, the topmenu and desktop are not in the same group, 00593 // thus the topmenu is not transient for it :-/. 00594 if( menubar == NULL ) 00595 { 00596 for( ClientList::ConstIterator it = topmenus.begin(); 00597 it != topmenus.end(); 00598 ++it ) 00599 if( (*it)->wasOriginallyGroupTransient()) // kdesktop's topmenu has WM_TRANSIENT_FOR 00600 { // set pointing to the root window 00601 menubar = *it; // to recognize it here 00602 break; // Also, with the xroot hack in kdesktop, 00603 } // there's no NET::Desktop window to be transient for 00604 } 00605 } 00606 00607 // kdDebug() << "CURRENT TOPMENU:" << menubar << ":" << active_client << endl; 00608 if ( menubar ) 00609 { 00610 if( active_client && !menubar->isOnDesktop( active_client->desktop())) 00611 menubar->setDesktop( active_client->desktop()); 00612 menubar->hideClient( false ); 00613 topmenu_space->hide(); 00614 // make it appear like it's been raised manually - it's in the Dock layer anyway, 00615 // and not raising it could mess up stacking order of topmenus within one application, 00616 // and thus break raising of mainclients in raiseClient() 00617 unconstrained_stacking_order.remove( menubar ); 00618 unconstrained_stacking_order.append( menubar ); 00619 } 00620 else if( !block_desktop_menubar ) 00621 { // no topmenu active - show the space window, so that there's not empty space 00622 topmenu_space->show(); 00623 } 00624 00625 // ... then hide the other ones. Avoids flickers. 00626 for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) 00627 { 00628 if( (*it)->isTopMenu() && (*it) != menubar ) 00629 (*it)->hideClient( true ); 00630 } 00631 } 00632 00633 00634 void Workspace::updateToolWindows( bool also_hide ) 00635 { 00636 // TODO what if Client's transiency/group changes? should this be called too? (I'm paranoid, am I not?) 00637 const Group* group = NULL; 00638 const Client* client = active_client; 00639 // Go up in transiency hiearchy, if the top is found, only tool transients for the top mainwindow 00640 // will be shown; if a group transient is group, all tools in the group will be shown 00641 while( client != NULL ) 00642 { 00643 if( !client->isTransient()) 00644 break; 00645 if( client->groupTransient()) 00646 { 00647 group = client->group(); 00648 break; 00649 } 00650 client = client->transientFor(); 00651 } 00652 // use stacking order only to reduce flicker, it doesn't matter if block_stacking_updates == 0, 00653 // i.e. if it's not up to date 00654 00655 // SELI but maybe it should - what if a new client has been added that's not in stacking order yet? 00656 ClientList to_show, to_hide; 00657 for( ClientList::ConstIterator it = stacking_order.begin(); 00658 it != stacking_order.end(); 00659 ++it ) 00660 { 00661 if( (*it)->isUtility() || (*it)->isMenu() || (*it)->isToolbar()) 00662 { 00663 bool show = true; 00664 if( !(*it)->isTransient()) 00665 { 00666 if( (*it)->group()->members().count() == 1 ) // has its own group, keep always visible 00667 show = true; 00668 else if( client != NULL && (*it)->group() == client->group()) 00669 show = true; 00670 else 00671 show = false; 00672 } 00673 else 00674 { 00675 if( group != NULL && (*it)->group() == group ) 00676 show = true; 00677 else if( client != NULL && client->hasTransient( (*it), true )) 00678 show = true; 00679 else 00680 show = false; 00681 } 00682 if( !show && also_hide ) 00683 { 00684 const ClientList mainclients = (*it)->mainClients(); 00685 // don't hide utility windows which are standalone(?) or 00686 // have e.g. kicker as mainwindow 00687 if( mainclients.isEmpty()) 00688 show = true; 00689 for( ClientList::ConstIterator it2 = mainclients.begin(); 00690 it2 != mainclients.end(); 00691 ++it2 ) 00692 { 00693 if( (*it2)->isSpecialWindow()) 00694 show = true; 00695 } 00696 if( !show ) 00697 to_hide.append( *it ); 00698 } 00699 if( show ) 00700 to_show.append( *it ); 00701 } 00702 } // first show new ones, then hide 00703 for( ClientList::ConstIterator it = to_show.fromLast(); 00704 it != to_show.end(); 00705 --it ) // from topmost 00706 // TODO since this is in stacking order, the order of taskbar entries changes :( 00707 (*it)->hideClient( false ); 00708 if( also_hide ) 00709 { 00710 for( ClientList::ConstIterator it = to_hide.begin(); 00711 it != to_hide.end(); 00712 ++it ) // from bottommost 00713 (*it)->hideClient( true ); 00714 updateToolWindowsTimer.stop(); 00715 } 00716 else // setActiveClient() is after called with NULL client, quickly followed 00717 { // by setting a new client, which would result in flickering 00718 updateToolWindowsTimer.start( 50, true ); 00719 } 00720 } 00721 00722 void Workspace::slotUpdateToolWindows() 00723 { 00724 updateToolWindows( true ); 00725 } 00726 00730 void Workspace::updateColormap() 00731 { 00732 Colormap cmap = default_colormap; 00733 if ( activeClient() && activeClient()->colormap() != None ) 00734 cmap = activeClient()->colormap(); 00735 if ( cmap != installed_colormap ) 00736 { 00737 XInstallColormap(qt_xdisplay(), cmap ); 00738 installed_colormap = cmap; 00739 } 00740 } 00741 00742 void Workspace::reconfigure() 00743 { 00744 reconfigureTimer.start(200, true); 00745 } 00746 00747 00748 void Workspace::slotSettingsChanged(int category) 00749 { 00750 kdDebug(1212) << "Workspace::slotSettingsChanged()" << endl; 00751 if( category == (int) KApplication::SETTINGS_SHORTCUTS ) 00752 readShortcuts(); 00753 } 00754 00758 KWIN_PROCEDURE( CheckBorderSizesProcedure, cl->checkBorderSizes() ); 00759 KWIN_PROCEDURE( ResetupRulesProcedure, cl->setupWindowRules( true ) ); 00760 00761 void Workspace::slotReconfigure() 00762 { 00763 kdDebug(1212) << "Workspace::slotReconfigure()" << endl; 00764 reconfigureTimer.stop(); 00765 00766 KGlobal::config()->reparseConfiguration(); 00767 unsigned long changed = options->updateSettings(); 00768 tab_box->reconfigure(); 00769 popupinfo->reconfigure(); 00770 readShortcuts(); 00771 forEachClient( CheckIgnoreFocusStealingProcedure()); 00772 00773 if( mgr->reset( changed )) 00774 { // decorations need to be recreated 00775 #if 0 // This actually seems to make things worse now 00776 QWidget curtain; 00777 curtain.setBackgroundMode( NoBackground ); 00778 curtain.setGeometry( QApplication::desktop()->geometry() ); 00779 curtain.show(); 00780 #endif 00781 for( ClientList::ConstIterator it = clients.begin(); 00782 it != clients.end(); 00783 ++it ) 00784 { 00785 (*it)->updateDecoration( true, true ); 00786 } 00787 mgr->destroyPreviousPlugin(); 00788 } 00789 else 00790 { 00791 forEachClient( CheckBorderSizesProcedure()); 00792 } 00793 00794 checkElectricBorders(); 00795 00796 if( options->topMenuEnabled() && !managingTopMenus()) 00797 { 00798 if( topmenu_selection->claim( false )) 00799 setupTopMenuHandling(); 00800 else 00801 lostTopMenuSelection(); 00802 } 00803 else if( !options->topMenuEnabled() && managingTopMenus()) 00804 { 00805 topmenu_selection->release(); 00806 lostTopMenuSelection(); 00807 } 00808 topmenu_height = 0; // invalidate used menu height 00809 if( managingTopMenus()) 00810 { 00811 updateTopMenuGeometry(); 00812 updateCurrentTopMenu(); 00813 } 00814 00815 loadWindowRules(); 00816 forEachClient( ResetupRulesProcedure()); 00817 } 00818 00819 void Workspace::loadDesktopSettings() 00820 { 00821 KConfig c("kwinrc"); 00822 00823 QCString groupname; 00824 if (screen_number == 0) 00825 groupname = "Desktops"; 00826 else 00827 groupname.sprintf("Desktops-screen-%d", screen_number); 00828 c.setGroup(groupname); 00829 00830 int n = c.readNumEntry("Number", 4); 00831 number_of_desktops = n; 00832 delete workarea; 00833 workarea = new QRect[ n + 1 ]; 00834 delete screenarea; 00835 screenarea = NULL; 00836 rootInfo->setNumberOfDesktops( number_of_desktops ); 00837 desktop_focus_chain.resize( n ); 00838 for(int i = 1; i <= n; i++) 00839 { 00840 QString s = c.readEntry(QString("Name_%1").arg(i), 00841 i18n("Desktop %1").arg(i)); 00842 rootInfo->setDesktopName( i, s.utf8().data() ); 00843 desktop_focus_chain[i-1] = i; 00844 } 00845 } 00846 00847 void Workspace::saveDesktopSettings() 00848 { 00849 KConfig c("kwinrc"); 00850 00851 QCString groupname; 00852 if (screen_number == 0) 00853 groupname = "Desktops"; 00854 else 00855 groupname.sprintf("Desktops-screen-%d", screen_number); 00856 c.setGroup(groupname); 00857 00858 c.writeEntry("Number", number_of_desktops ); 00859 for(int i = 1; i <= number_of_desktops; i++) 00860 { 00861 QString s = desktopName( i ); 00862 QString defaultvalue = i18n("Desktop %1").arg(i); 00863 if ( s.isEmpty() ) 00864 { 00865 s = defaultvalue; 00866 rootInfo->setDesktopName( i, s.utf8().data() ); 00867 } 00868 00869 if (s != defaultvalue) 00870 { 00871 c.writeEntry( QString("Name_%1").arg(i), s ); 00872 } 00873 else 00874 { 00875 QString currentvalue = c.readEntry(QString("Name_%1").arg(i)); 00876 if (currentvalue != defaultvalue) 00877 c.writeEntry( QString("Name_%1").arg(i), "" ); 00878 } 00879 } 00880 } 00881 00882 QStringList Workspace::configModules(bool controlCenter) 00883 { 00884 QStringList args; 00885 args << "kde-kwindecoration.desktop"; 00886 if (controlCenter) 00887 args << "kde-kwinoptions.desktop"; 00888 else if (kapp->authorizeControlModule("kde-kwinoptions.desktop")) 00889 args << "kwinactions" << "kwinfocus" << "kwinmoving" << "kwinadvanced" << "kwinrules"; 00890 return args; 00891 } 00892 00893 void Workspace::configureWM() 00894 { 00895 KApplication::kdeinitExec( "kcmshell", configModules(false) ); 00896 } 00897 00901 void Workspace::doNotManage( QString title ) 00902 { 00903 doNotManageList.append( title ); 00904 } 00905 00909 bool Workspace::isNotManaged( const QString& title ) 00910 { 00911 for ( QStringList::Iterator it = doNotManageList.begin(); it != doNotManageList.end(); ++it ) 00912 { 00913 QRegExp r( (*it) ); 00914 if (r.search(title) != -1) 00915 { 00916 doNotManageList.remove( it ); 00917 return TRUE; 00918 } 00919 } 00920 return FALSE; 00921 } 00922 00926 void Workspace::refresh() 00927 { 00928 QWidget w; 00929 w.setGeometry( QApplication::desktop()->geometry() ); 00930 w.show(); 00931 w.hide(); 00932 QApplication::flushX(); 00933 } 00934 00942 class ObscuringWindows 00943 { 00944 public: 00945 ~ObscuringWindows(); 00946 void create( Client* c ); 00947 private: 00948 QValueList<Window> obscuring_windows; 00949 static QValueList<Window>* cached; 00950 static unsigned int max_cache_size; 00951 }; 00952 00953 QValueList<Window>* ObscuringWindows::cached = 0; 00954 unsigned int ObscuringWindows::max_cache_size = 0; 00955 00956 void ObscuringWindows::create( Client* c ) 00957 { 00958 if( cached == 0 ) 00959 cached = new QValueList<Window>; 00960 Window obs_win; 00961 XWindowChanges chngs; 00962 int mask = CWSibling | CWStackMode; 00963 if( cached->count() > 0 ) 00964 { 00965 cached->remove( obs_win = cached->first()); 00966 chngs.x = c->x(); 00967 chngs.y = c->y(); 00968 chngs.width = c->width(); 00969 chngs.height = c->height(); 00970 mask |= CWX | CWY | CWWidth | CWHeight; 00971 } 00972 else 00973 { 00974 XSetWindowAttributes a; 00975 a.background_pixmap = None; 00976 a.override_redirect = True; 00977 obs_win = XCreateWindow( qt_xdisplay(), qt_xrootwin(), c->x(), c->y(), 00978 c->width(), c->height(), 0, CopyFromParent, InputOutput, 00979 CopyFromParent, CWBackPixmap | CWOverrideRedirect, &a ); 00980 } 00981 chngs.sibling = c->frameId(); 00982 chngs.stack_mode = Below; 00983 XConfigureWindow( qt_xdisplay(), obs_win, mask, &chngs ); 00984 XMapWindow( qt_xdisplay(), obs_win ); 00985 obscuring_windows.append( obs_win ); 00986 } 00987 00988 ObscuringWindows::~ObscuringWindows() 00989 { 00990 max_cache_size = QMAX( max_cache_size, obscuring_windows.count() + 4 ) - 1; 00991 for( QValueList<Window>::ConstIterator it = obscuring_windows.begin(); 00992 it != obscuring_windows.end(); 00993 ++it ) 00994 { 00995 XUnmapWindow( qt_xdisplay(), *it ); 00996 if( cached->count() < max_cache_size ) 00997 cached->prepend( *it ); 00998 else 00999 XDestroyWindow( qt_xdisplay(), *it ); 01000 } 01001 } 01002 01003 01010 bool Workspace::setCurrentDesktop( int new_desktop ) 01011 { 01012 if (new_desktop < 1 || new_desktop > number_of_desktops ) 01013 return false; 01014 01015 if( popup ) 01016 popup->close(); 01017 ++block_focus; 01018 // TODO Q_ASSERT( block_stacking_updates == 0 ); // make sure stacking_order is up to date 01019 StackingUpdatesBlocker blocker( this ); 01020 01021 if (new_desktop != current_desktop) 01022 { 01023 /* 01024 optimized Desktop switching: unmapping done from back to front 01025 mapping done from front to back => less exposure events 01026 */ 01027 Notify::raise((Notify::Event) (Notify::DesktopChange+new_desktop)); 01028 01029 ObscuringWindows obs_wins; 01030 01031 int old_desktop = current_desktop; 01032 current_desktop = new_desktop; // change the desktop (so that Client::virtualDesktopChange() works) 01033 01034 for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it) 01035 if ( !(*it)->isOnDesktop( new_desktop ) && (*it) != movingClient ) 01036 { 01037 if( (*it)->isShown( true ) && (*it)->isOnDesktop( old_desktop )) 01038 obs_wins.create( *it ); 01039 (*it)->virtualDesktopChange(); 01040 } 01041 01042 rootInfo->setCurrentDesktop( current_desktop ); // now propagate the change, after hiding, before showing 01043 01044 if( movingClient && !movingClient->isOnDesktop( new_desktop )) 01045 movingClient->setDesktop( new_desktop ); 01046 01047 for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) 01048 if ( (*it)->isOnDesktop( new_desktop ) ) 01049 (*it)->virtualDesktopChange(); 01050 } 01051 01052 // restore the focus on this desktop 01053 --block_focus; 01054 Client* c = 0; 01055 01056 if ( options->focusPolicyIsReasonable()) 01057 { 01058 // Search in focus chain 01059 01060 if ( focus_chain.contains( active_client ) && active_client->isShown( true ) 01061 && active_client->isOnCurrentDesktop()) 01062 { 01063 c = active_client; // the requestFocus below will fail, as the client is already active 01064 } 01065 01066 if ( !c ) 01067 { 01068 for( ClientList::ConstIterator it = focus_chain.fromLast(); it != focus_chain.end(); --it) 01069 { 01070 if ( (*it)->isShown( false ) && !(*it)->isOnAllDesktops() && (*it)->isOnCurrentDesktop()) 01071 { 01072 c = *it; 01073 break; 01074 } 01075 } 01076 } 01077 01078 if ( !c ) 01079 { 01080 for( ClientList::ConstIterator it = focus_chain.fromLast(); it != focus_chain.end(); --it) 01081 { 01082 if ( (*it)->isShown( false ) && (*it)->isOnCurrentDesktop()) 01083 { 01084 c = *it; 01085 break; 01086 } 01087 } 01088 } 01089 } 01090 01091 //if "unreasonable focus policy" 01092 // and active_client is on_all_desktops and under mouse (hence == old_active_client), 01093 // conserve focus (thanks to Volker Schatz <V.Schatz at thphys.uni-heidelberg.de>) 01094 else if( active_client && active_client->isShown( true ) && active_client->isOnCurrentDesktop()) 01095 c= active_client; 01096 01097 if( c != active_client ) 01098 setActiveClient( NULL, Allowed ); 01099 01100 if ( c ) 01101 requestFocus( c ); 01102 else 01103 focusToNull(); 01104 01105 if( !desktops.isEmpty() ) 01106 { 01107 Window w_tmp; 01108 int i_tmp; 01109 XGetInputFocus( qt_xdisplay(), &w_tmp, &i_tmp ); 01110 if( w_tmp == null_focus_window ) // CHECKME? 01111 requestFocus( findDesktop( true, currentDesktop())); 01112 } 01113 01114 // Update focus chain: 01115 // If input: chain = { 1, 2, 3, 4 } and current_desktop = 3, 01116 // Output: chain = { 3, 1, 2, 4 }. 01117 // kdDebug(1212) << QString("Switching to desktop #%1, at focus_chain index %2\n") 01118 // .arg(current_desktop).arg(desktop_focus_chain.find( current_desktop )); 01119 for( int i = desktop_focus_chain.find( current_desktop ); i > 0; i-- ) 01120 desktop_focus_chain[i] = desktop_focus_chain[i-1]; 01121 desktop_focus_chain[0] = current_desktop; 01122 01123 // QString s = "desktop_focus_chain[] = { "; 01124 // for( uint i = 0; i < desktop_focus_chain.size(); i++ ) 01125 // s += QString::number(desktop_focus_chain[i]) + ", "; 01126 // kdDebug(1212) << s << "}\n"; 01127 return true; 01128 } 01129 01130 void Workspace::nextDesktop() 01131 { 01132 int desktop = currentDesktop() + 1; 01133 setCurrentDesktop(desktop > numberOfDesktops() ? 1 : desktop); 01134 popupinfo->showInfo( desktopName(currentDesktop()) ); 01135 } 01136 01137 void Workspace::previousDesktop() 01138 { 01139 int desktop = currentDesktop() - 1; 01140 setCurrentDesktop(desktop > 0 ? desktop : numberOfDesktops()); 01141 popupinfo->showInfo( desktopName(currentDesktop()) ); 01142 } 01143 01147 void Workspace::setNumberOfDesktops( int n ) 01148 { 01149 if ( n == number_of_desktops ) 01150 return; 01151 int old_number_of_desktops = number_of_desktops; 01152 number_of_desktops = n; 01153 01154 if( currentDesktop() > numberOfDesktops()) 01155 setCurrentDesktop( numberOfDesktops()); 01156 01157 // if increasing the number, do the resizing now, 01158 // otherwise after the moving of windows to still existing desktops 01159 if( old_number_of_desktops < number_of_desktops ) 01160 { 01161 rootInfo->setNumberOfDesktops( number_of_desktops ); 01162 NETPoint* viewports = new NETPoint[ number_of_desktops ]; 01163 rootInfo->setDesktopViewport( number_of_desktops, *viewports ); 01164 delete[] viewports; 01165 updateClientArea( true ); 01166 } 01167 01168 // if the number of desktops decreased, move all 01169 // windows that would be hidden to the last visible desktop 01170 if( old_number_of_desktops > number_of_desktops ) 01171 { 01172 for( ClientList::ConstIterator it = clients.begin(); 01173 it != clients.end(); 01174 ++it) 01175 { 01176 if( !(*it)->isOnAllDesktops() && (*it)->desktop() > numberOfDesktops()) 01177 sendClientToDesktop( *it, numberOfDesktops(), true ); 01178 } 01179 } 01180 if( old_number_of_desktops > number_of_desktops ) 01181 { 01182 rootInfo->setNumberOfDesktops( number_of_desktops ); 01183 NETPoint* viewports = new NETPoint[ number_of_desktops ]; 01184 rootInfo->setDesktopViewport( number_of_desktops, *viewports ); 01185 delete[] viewports; 01186 updateClientArea( true ); 01187 } 01188 01189 saveDesktopSettings(); 01190 01191 // Resize and reset the desktop focus chain. 01192 desktop_focus_chain.resize( n ); 01193 for( int i = 0; i < (int)desktop_focus_chain.size(); i++ ) 01194 desktop_focus_chain[i] = i+1; 01195 } 01196 01202 void Workspace::sendClientToDesktop( Client* c, int desk, bool dont_activate ) 01203 { 01204 bool was_on_desktop = c->isOnDesktop( desk ) || c->isOnAllDesktops(); 01205 c->setDesktop( desk ); 01206 if ( c->desktop() == desk ) // no change or desktop forced 01207 return; 01208 desk = c->desktop(); // Client did range checking 01209 01210 if ( c->isOnDesktop( currentDesktop() ) ) 01211 { 01212 if ( c->wantsTabFocus() && options->focusPolicyIsReasonable() 01213 && !was_on_desktop // for stickyness changes 01214 && !dont_activate ) 01215 requestFocus( c ); 01216 else 01217 restackClientUnderActive( c ); 01218 } 01219 else 01220 { 01221 raiseClient( c ); 01222 focus_chain.remove( c ); 01223 if ( c->wantsTabFocus() ) 01224 focus_chain.append( c ); 01225 } 01226 01227 ClientList transients_stacking_order = ensureStackingOrder( c->transients()); 01228 for( ClientList::ConstIterator it = transients_stacking_order.begin(); 01229 it != transients_stacking_order.end(); 01230 ++it ) 01231 sendClientToDesktop( *it, desk, dont_activate ); 01232 updateClientArea(); 01233 } 01234 01235 void Workspace::setDesktopLayout(int o, int x, int y) 01236 { 01237 layoutOrientation = (Qt::Orientation) o; 01238 layoutX = x; 01239 layoutY = y; 01240 } 01241 01242 void Workspace::calcDesktopLayout(int &x, int &y) 01243 { 01244 x = layoutX; 01245 y = layoutY; 01246 if ((x == -1) && (y > 0)) 01247 x = (numberOfDesktops()+y-1) / y; 01248 else if ((y == -1) && (x > 0)) 01249 y = (numberOfDesktops()+x-1) / x; 01250 01251 if (x == -1) 01252 x = 1; 01253 if (y == -1) 01254 y = 1; 01255 } 01256 01261 bool Workspace::addSystemTrayWin( WId w ) 01262 { 01263 if ( systemTrayWins.contains( w ) ) 01264 return TRUE; 01265 01266 NETWinInfo ni( qt_xdisplay(), w, root, NET::WMKDESystemTrayWinFor ); 01267 WId trayWinFor = ni.kdeSystemTrayWinFor(); 01268 if ( !trayWinFor ) 01269 return FALSE; 01270 systemTrayWins.append( SystemTrayWindow( w, trayWinFor ) ); 01271 XSelectInput( qt_xdisplay(), w, 01272 StructureNotifyMask 01273 ); 01274 XAddToSaveSet( qt_xdisplay(), w ); 01275 propagateSystemTrayWins(); 01276 return TRUE; 01277 } 01278 01283 bool Workspace::removeSystemTrayWin( WId w, bool check ) 01284 { 01285 if ( !systemTrayWins.contains( w ) ) 01286 return FALSE; 01287 if( check ) 01288 { 01289 // When getting UnmapNotify, it's not clear if it's the systray 01290 // reparenting the window into itself, or if it's the window 01291 // going away. This is obviously a flaw in the design, and we were 01292 // just lucky it worked for so long. Kicker's systray temporarily 01293 // sets _KDE_SYSTEM_TRAY_EMBEDDING property on the window while 01294 // embedding it, allowing KWin to figure out. Kicker just mustn't 01295 // crash before removing it again ... *shrug* . 01296 int num_props; 01297 Atom* props = XListProperties( qt_xdisplay(), w, &num_props ); 01298 if( props != NULL ) 01299 { 01300 for( int i = 0; 01301 i < num_props; 01302 ++i ) 01303 if( props[ i ] == atoms->kde_system_tray_embedding ) 01304 { 01305 XFree( props ); 01306 return false; 01307 } 01308 XFree( props ); 01309 } 01310 } 01311 systemTrayWins.remove( w ); 01312 propagateSystemTrayWins(); 01313 return TRUE; 01314 } 01315 01316 01320 void Workspace::propagateSystemTrayWins() 01321 { 01322 Window *cl = new Window[ systemTrayWins.count()]; 01323 01324 int i = 0; 01325 for ( SystemTrayWindowList::ConstIterator it = systemTrayWins.begin(); it != systemTrayWins.end(); ++it ) 01326 { 01327 cl[i++] = (*it).win; 01328 } 01329 01330 rootInfo->setKDESystemTrayWindows( cl, i ); 01331 delete [] cl; 01332 } 01333 01334 01335 void Workspace::killWindowId( Window window_to_kill ) 01336 { 01337 if( window_to_kill == None ) 01338 return; 01339 Window window = window_to_kill; 01340 Client* client = NULL; 01341 for(;;) 01342 { 01343 client = findClient( FrameIdMatchPredicate( window )); 01344 if( client != NULL ) // found the client 01345 break; 01346 Window parent, root; 01347 Window* children; 01348 unsigned int children_count; 01349 XQueryTree( qt_xdisplay(), window, &root, &parent, &children, &children_count ); 01350 if( children != NULL ) 01351 XFree( children ); 01352 if( window == root ) // we didn't find the client, probably an override-redirect window 01353 break; 01354 window = parent; // go up 01355 } 01356 if( client != NULL ) 01357 client->killWindow(); 01358 else 01359 XKillClient( qt_xdisplay(), window_to_kill ); 01360 } 01361 01362 01363 void Workspace::sendPingToWindow( Window window, Time timestamp ) 01364 { 01365 rootInfo->sendPing( window, timestamp ); 01366 } 01367 01368 void Workspace::sendTakeActivity( Client* c, Time timestamp, long flags ) 01369 { 01370 rootInfo->takeActivity( c->window(), timestamp, flags ); 01371 pending_take_activity = c; 01372 } 01373 01374 01378 void Workspace::slotGrabWindow() 01379 { 01380 if ( active_client ) 01381 { 01382 QPixmap snapshot = QPixmap::grabWindow( active_client->frameId() ); 01383 01384 //No XShape - no work. 01385 if( Shape::available()) 01386 { 01387 //As the first step, get the mask from XShape. 01388 int count, order; 01389 XRectangle* rects = XShapeGetRectangles( qt_xdisplay(), active_client->frameId(), 01390 ShapeBounding, &count, &order); 01391 //The ShapeBounding region is the outermost shape of the window; 01392 //ShapeBounding - ShapeClipping is defined to be the border. 01393 //Since the border area is part of the window, we use bounding 01394 // to limit our work region 01395 if (rects) 01396 { 01397 //Create a QRegion from the rectangles describing the bounding mask. 01398 QRegion contents; 01399 for (int pos = 0; pos < count; pos++) 01400 contents += QRegion(rects[pos].x, rects[pos].y, 01401 rects[pos].width, rects[pos].height); 01402 XFree(rects); 01403 01404 //Create the bounding box. 01405 QRegion bbox(0, 0, snapshot.width(), snapshot.height()); 01406 01407 //Get the masked away area. 01408 QRegion maskedAway = bbox - contents; 01409 QMemArray<QRect> maskedAwayRects = maskedAway.rects(); 01410 01411 //Construct a bitmap mask from the rectangles 01412 QBitmap mask( snapshot.width(), snapshot.height()); 01413 QPainter p(&mask); 01414 p.fillRect(0, 0, mask.width(), mask.height(), Qt::color1); 01415 for (uint pos = 0; pos < maskedAwayRects.count(); pos++) 01416 p.fillRect(maskedAwayRects[pos], Qt::color0); 01417 p.end(); 01418 snapshot.setMask(mask); 01419 } 01420 } 01421 01422 QClipboard *cb = QApplication::clipboard(); 01423 cb->setPixmap( snapshot ); 01424 } 01425 else 01426 slotGrabDesktop(); 01427 } 01428 01432 void Workspace::slotGrabDesktop() 01433 { 01434 QPixmap p = QPixmap::grabWindow( qt_xrootwin() ); 01435 QClipboard *cb = QApplication::clipboard(); 01436 cb->setPixmap( p ); 01437 } 01438 01439 01443 void Workspace::slotMouseEmulation() 01444 { 01445 01446 if ( mouse_emulation ) 01447 { 01448 XUngrabKeyboard(qt_xdisplay(), qt_x_time); 01449 mouse_emulation = FALSE; 01450 return; 01451 } 01452 01453 if ( XGrabKeyboard(qt_xdisplay(), 01454 root, FALSE, 01455 GrabModeAsync, GrabModeAsync, 01456 qt_x_time) == GrabSuccess ) 01457 { 01458 mouse_emulation = TRUE; 01459 mouse_emulation_state = 0; 01460 mouse_emulation_window = 0; 01461 } 01462 } 01463 01470 WId Workspace::getMouseEmulationWindow() 01471 { 01472 Window root; 01473 Window child = qt_xrootwin(); 01474 int root_x, root_y, lx, ly; 01475 uint state; 01476 Window w; 01477 Client * c = 0; 01478 do 01479 { 01480 w = child; 01481 if (!c) 01482 c = findClient( FrameIdMatchPredicate( w )); 01483 XQueryPointer( qt_xdisplay(), w, &root, &child, 01484 &root_x, &root_y, &lx, &ly, &state ); 01485 } while ( child != None && child != w ); 01486 01487 if ( c && !c->isActive() ) 01488 activateClient( c ); 01489 return (WId) w; 01490 } 01491 01495 unsigned int Workspace::sendFakedMouseEvent( QPoint pos, WId w, MouseEmulation type, int button, unsigned int state ) 01496 { 01497 if ( !w ) 01498 return state; 01499 QWidget* widget = QWidget::find( w ); 01500 if ( (!widget || widget->inherits("QToolButton") ) && !findClient( WindowMatchPredicate( w )) ) 01501 { 01502 int x, y; 01503 Window xw; 01504 XTranslateCoordinates( qt_xdisplay(), qt_xrootwin(), w, pos.x(), pos.y(), &x, &y, &xw ); 01505 if ( type == EmuMove ) 01506 { // motion notify events 01507 XMotionEvent e; 01508 e.type = MotionNotify; 01509 e.window = w; 01510 e.root = qt_xrootwin(); 01511 e.subwindow = w; 01512 e.time = qt_x_time; 01513 e.x = x; 01514 e.y = y; 01515 e.x_root = pos.x(); 01516 e.y_root = pos.y(); 01517 e.state = state; 01518 e.is_hint = NotifyNormal; 01519 XSendEvent( qt_xdisplay(), w, TRUE, ButtonMotionMask, (XEvent*)&e ); 01520 } 01521 else 01522 { 01523 XButtonEvent e; 01524 e.type = type == EmuRelease ? ButtonRelease : ButtonPress; 01525 e.window = w; 01526 e.root = qt_xrootwin(); 01527 e.subwindow = w; 01528 e.time = qt_x_time; 01529 e.x = x; 01530 e.y = y; 01531 e.x_root = pos.x(); 01532 e.y_root = pos.y(); 01533 e.state = state; 01534 e.button = button; 01535 XSendEvent( qt_xdisplay(), w, TRUE, ButtonPressMask, (XEvent*)&e ); 01536 01537 if ( type == EmuPress ) 01538 { 01539 switch ( button ) 01540 { 01541 case 2: 01542 state |= Button2Mask; 01543 break; 01544 case 3: 01545 state |= Button3Mask; 01546 break; 01547 default: // 1 01548 state |= Button1Mask; 01549 break; 01550 } 01551 } 01552 else 01553 { 01554 switch ( button ) 01555 { 01556 case 2: 01557 state &= ~Button2Mask; 01558 break; 01559 case 3: 01560 state &= ~Button3Mask; 01561 break; 01562 default: // 1 01563 state &= ~Button1Mask; 01564 break; 01565 } 01566 } 01567 } 01568 } 01569 return state; 01570 } 01571 01575 bool Workspace::keyPressMouseEmulation( XKeyEvent& ev ) 01576 { 01577 if ( root != qt_xrootwin() ) 01578 return FALSE; 01579 int kc = XKeycodeToKeysym(qt_xdisplay(), ev.keycode, 0); 01580 int km = ev.state & (ControlMask | Mod1Mask | ShiftMask); 01581 01582 bool is_control = km & ControlMask; 01583 bool is_alt = km & Mod1Mask; 01584 bool is_shift = km & ShiftMask; 01585 int delta = is_control?1:is_alt?32:8; 01586 QPoint pos = QCursor::pos(); 01587 01588 switch ( kc ) 01589 { 01590 case XK_Left: 01591 case XK_KP_Left: 01592 pos.rx() -= delta; 01593 break; 01594 case XK_Right: 01595 case XK_KP_Right: 01596 pos.rx() += delta; 01597 break; 01598 case XK_Up: 01599 case XK_KP_Up: 01600 pos.ry() -= delta; 01601 break; 01602 case XK_Down: 01603 case XK_KP_Down: 01604 pos.ry() += delta; 01605 break; 01606 case XK_F1: 01607 if ( !mouse_emulation_state ) 01608 mouse_emulation_window = getMouseEmulationWindow(); 01609 if ( (mouse_emulation_state & Button1Mask) == 0 ) 01610 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button1, mouse_emulation_state ); 01611 if ( !is_shift ) 01612 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state ); 01613 break; 01614 case XK_F2: 01615 if ( !mouse_emulation_state ) 01616 mouse_emulation_window = getMouseEmulationWindow(); 01617 if ( (mouse_emulation_state & Button2Mask) == 0 ) 01618 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button2, mouse_emulation_state ); 01619 if ( !is_shift ) 01620 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button2, mouse_emulation_state ); 01621 break; 01622 case XK_F3: 01623 if ( !mouse_emulation_state ) 01624 mouse_emulation_window = getMouseEmulationWindow(); 01625 if ( (mouse_emulation_state & Button3Mask) == 0 ) 01626 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button3, mouse_emulation_state ); 01627 if ( !is_shift ) 01628 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button3, mouse_emulation_state ); 01629 break; 01630 case XK_Return: 01631 case XK_space: 01632 case XK_KP_Enter: 01633 case XK_KP_Space: 01634 { 01635 if ( !mouse_emulation_state ) 01636 { 01637 // nothing was pressed, fake a LMB click 01638 mouse_emulation_window = getMouseEmulationWindow(); 01639 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button1, mouse_emulation_state ); 01640 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state ); 01641 } 01642 else 01643 { // release all 01644 if ( mouse_emulation_state & Button1Mask ) 01645 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state ); 01646 if ( mouse_emulation_state & Button2Mask ) 01647 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button2, mouse_emulation_state ); 01648 if ( mouse_emulation_state & Button3Mask ) 01649 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button3, mouse_emulation_state ); 01650 } 01651 } 01652 // fall through 01653 case XK_Escape: 01654 XUngrabKeyboard(qt_xdisplay(), qt_x_time); 01655 mouse_emulation = FALSE; 01656 return TRUE; 01657 default: 01658 return FALSE; 01659 } 01660 01661 QCursor::setPos( pos ); 01662 if ( mouse_emulation_state ) 01663 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuMove, 0, mouse_emulation_state ); 01664 return TRUE; 01665 01666 } 01667 01673 QWidget* Workspace::desktopWidget() 01674 { 01675 return desktop_widget; 01676 } 01677 01678 //Delayed focus functions 01679 void Workspace::delayFocus() 01680 { 01681 requestFocus( delayfocus_client ); 01682 cancelDelayFocus(); 01683 } 01684 01685 void Workspace::requestDelayFocus( Client* c ) 01686 { 01687 delayfocus_client = c; 01688 delete delayFocusTimer; 01689 delayFocusTimer = new QTimer( this ); 01690 connect( delayFocusTimer, SIGNAL( timeout() ), this, SLOT( delayFocus() ) ); 01691 delayFocusTimer->start( options->delayFocusInterval, TRUE ); 01692 } 01693 01694 void Workspace::cancelDelayFocus() 01695 { 01696 delete delayFocusTimer; 01697 delayFocusTimer = 0; 01698 } 01699 01700 // Electric Borders 01701 //========================================================================// 01702 // Electric Border Window management. Electric borders allow a user 01703 // to change the virtual desktop by moving the mouse pointer to the 01704 // borders. Technically this is done with input only windows. Since 01705 // electric borders can be switched on and off, we have these two 01706 // functions to create and destroy them. 01707 void Workspace::checkElectricBorders() 01708 { 01709 electric_current_border = 0; 01710 01711 QRect r = QApplication::desktop()->geometry(); 01712 electricTop = r.top(); 01713 electricBottom = r.bottom(); 01714 electricLeft = r.left(); 01715 electricRight = r.right(); 01716 01717 if (options->electricBorders() == Options::ElectricAlways) 01718 createBorderWindows(); 01719 else 01720 destroyBorderWindows(); 01721 } 01722 01723 void Workspace::createBorderWindows() 01724 { 01725 if ( electric_have_borders ) 01726 return; 01727 01728 electric_have_borders = true; 01729 01730 QRect r = QApplication::desktop()->geometry(); 01731 XSetWindowAttributes attributes; 01732 unsigned long valuemask; 01733 attributes.override_redirect = True; 01734 attributes.event_mask = (EnterWindowMask | LeaveWindowMask | 01735 VisibilityChangeMask); 01736 valuemask= (CWOverrideRedirect | CWEventMask | CWCursor ); 01737 attributes.cursor = XCreateFontCursor(qt_xdisplay(), 01738 XC_sb_up_arrow); 01739 electric_top_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(), 01740 0,0, 01741 r.width(),1, 01742 0, 01743 CopyFromParent, InputOnly, 01744 CopyFromParent, 01745 valuemask, &attributes); 01746 XMapWindow(qt_xdisplay(), electric_top_border); 01747 01748 attributes.cursor = XCreateFontCursor(qt_xdisplay(), 01749 XC_sb_down_arrow); 01750 electric_bottom_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(), 01751 0,r.height()-1, 01752 r.width(),1, 01753 0, 01754 CopyFromParent, InputOnly, 01755 CopyFromParent, 01756 valuemask, &attributes); 01757 XMapWindow(qt_xdisplay(), electric_bottom_border); 01758 01759 attributes.cursor = XCreateFontCursor(qt_xdisplay(), 01760 XC_sb_left_arrow); 01761 electric_left_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(), 01762 0,0, 01763 1,r.height(), 01764 0, 01765 CopyFromParent, InputOnly, 01766 CopyFromParent, 01767 valuemask, &attributes); 01768 XMapWindow(qt_xdisplay(), electric_left_border); 01769 01770 attributes.cursor = XCreateFontCursor(qt_xdisplay(), 01771 XC_sb_right_arrow); 01772 electric_right_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(), 01773 r.width()-1,0, 01774 1,r.height(), 01775 0, 01776 CopyFromParent, InputOnly, 01777 CopyFromParent, 01778 valuemask, &attributes); 01779 XMapWindow(qt_xdisplay(), electric_right_border); 01780 } 01781 01782 01783 // Electric Border Window management. Electric borders allow a user 01784 // to change the virtual desktop by moving the mouse pointer to the 01785 // borders. Technically this is done with input only windows. Since 01786 // electric borders can be switched on and off, we have these two 01787 // functions to create and destroy them. 01788 void Workspace::destroyBorderWindows() 01789 { 01790 if( !electric_have_borders) 01791 return; 01792 01793 electric_have_borders = false; 01794 01795 if(electric_top_border) 01796 XDestroyWindow(qt_xdisplay(),electric_top_border); 01797 if(electric_bottom_border) 01798 XDestroyWindow(qt_xdisplay(),electric_bottom_border); 01799 if(electric_left_border) 01800 XDestroyWindow(qt_xdisplay(),electric_left_border); 01801 if(electric_right_border) 01802 XDestroyWindow(qt_xdisplay(),electric_right_border); 01803 01804 electric_top_border = None; 01805 electric_bottom_border = None; 01806 electric_left_border = None; 01807 electric_right_border = None; 01808 } 01809 01810 void Workspace::clientMoved(const QPoint &pos, Time now) 01811 { 01812 if (options->electricBorders() == Options::ElectricDisabled) 01813 return; 01814 01815 if ((pos.x() != electricLeft) && 01816 (pos.x() != electricRight) && 01817 (pos.y() != electricTop) && 01818 (pos.y() != electricBottom)) 01819 return; 01820 01821 Time treshold_set = options->electricBorderDelay(); // set timeout 01822 Time treshold_reset = 250; // reset timeout 01823 int distance_reset = 10; // Mouse should not move more than this many pixels 01824 01825 int border = 0; 01826 if (pos.x() == electricLeft) 01827 border = 1; 01828 else if (pos.x() == electricRight) 01829 border = 2; 01830 else if (pos.y() == electricTop) 01831 border = 3; 01832 else if (pos.y() == electricBottom) 01833 border = 4; 01834 01835 if ((electric_current_border == border) && 01836 (timestampDiff(electric_time_last, now) < treshold_reset) && 01837 ((pos-electric_push_point).manhattanLength() < distance_reset)) 01838 { 01839 electric_time_last = now; 01840 01841 if (timestampDiff(electric_time_first, now) > treshold_set) 01842 { 01843 electric_current_border = 0; 01844 01845 QRect r = QApplication::desktop()->geometry(); 01846 int offset; 01847 01848 int desk_before = currentDesktop(); 01849 switch(border) 01850 { 01851 case 1: 01852 slotSwitchDesktopLeft(); 01853 if (currentDesktop() != desk_before) 01854 { 01855 offset = r.width() / 5; 01856 QCursor::setPos(r.width() - offset, pos.y()); 01857 } 01858 break; 01859 01860 case 2: 01861 slotSwitchDesktopRight(); 01862 if (currentDesktop() != desk_before) 01863 { 01864 offset = r.width() / 5; 01865 QCursor::setPos(offset, pos.y()); 01866 } 01867 break; 01868 01869 case 3: 01870 slotSwitchDesktopUp(); 01871 if (currentDesktop() != desk_before) 01872 { 01873 offset = r.height() / 5; 01874 QCursor::setPos(pos.x(), r.height() - offset); 01875 } 01876 break; 01877 01878 case 4: 01879 slotSwitchDesktopDown(); 01880 if (currentDesktop() != desk_before) 01881 { 01882 offset = r.height() / 5; 01883 QCursor::setPos(pos.x(), offset); 01884 } 01885 break; 01886 } 01887 return; 01888 } 01889 } 01890 else 01891 { 01892 electric_current_border = border; 01893 electric_time_first = now; 01894 electric_time_last = now; 01895 electric_push_point = pos; 01896 } 01897 01898 int mouse_warp = 1; 01899 01900 // reset the pointer to find out wether the user is really pushing 01901 switch( border) 01902 { 01903 case 1: QCursor::setPos(pos.x()+mouse_warp, pos.y()); break; 01904 case 2: QCursor::setPos(pos.x()-mouse_warp, pos.y()); break; 01905 case 3: QCursor::setPos(pos.x(), pos.y()+mouse_warp); break; 01906 case 4: QCursor::setPos(pos.x(), pos.y()-mouse_warp); break; 01907 } 01908 } 01909 01910 // this function is called when the user entered an electric border 01911 // with the mouse. It may switch to another virtual desktop 01912 void Workspace::electricBorder(XEvent *e) 01913 { 01914 Time now = e->xcrossing.time; 01915 QPoint p(e->xcrossing.x_root, e->xcrossing.y_root); 01916 01917 clientMoved(p, now); 01918 } 01919 01920 // electric borders (input only windows) have to be always on the 01921 // top. For that reason kwm calls this function always after some 01922 // windows have been raised. 01923 void Workspace::raiseElectricBorders() 01924 { 01925 01926 if(electric_have_borders) 01927 { 01928 XRaiseWindow(qt_xdisplay(), electric_top_border); 01929 XRaiseWindow(qt_xdisplay(), electric_left_border); 01930 XRaiseWindow(qt_xdisplay(), electric_bottom_border); 01931 XRaiseWindow(qt_xdisplay(), electric_right_border); 01932 } 01933 } 01934 01935 void Workspace::addTopMenu( Client* c ) 01936 { 01937 assert( c->isTopMenu()); 01938 assert( !topmenus.contains( c )); 01939 topmenus.append( c ); 01940 if( managingTopMenus()) 01941 { 01942 int minsize = c->minSize().height(); 01943 if( minsize > topMenuHeight()) 01944 { 01945 topmenu_height = minsize; 01946 updateTopMenuGeometry(); 01947 } 01948 updateTopMenuGeometry( c ); 01949 updateCurrentTopMenu(); 01950 } 01951 // kdDebug() << "NEW TOPMENU:" << c << endl; 01952 } 01953 01954 void Workspace::removeTopMenu( Client* c ) 01955 { 01956 // if( c->isTopMenu()) 01957 // kdDebug() << "REMOVE TOPMENU:" << c << endl; 01958 assert( c->isTopMenu()); 01959 assert( topmenus.contains( c )); 01960 topmenus.remove( c ); 01961 updateCurrentTopMenu(); 01962 // TODO reduce topMenuHeight() if possible? 01963 } 01964 01965 void Workspace::lostTopMenuSelection() 01966 { 01967 // kdDebug() << "lost TopMenu selection" << endl; 01968 // make sure this signal is always set when not owning the selection 01969 disconnect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner())); 01970 connect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner())); 01971 if( !managing_topmenus ) 01972 return; 01973 connect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner())); 01974 disconnect( topmenu_selection, SIGNAL( lostOwnership()), this, SLOT( lostTopMenuSelection())); 01975 managing_topmenus = false; 01976 delete topmenu_space; 01977 topmenu_space = NULL; 01978 updateClientArea(); 01979 for( ClientList::ConstIterator it = topmenus.begin(); 01980 it != topmenus.end(); 01981 ++it ) 01982 (*it)->checkWorkspacePosition(); 01983 } 01984 01985 void Workspace::lostTopMenuOwner() 01986 { 01987 if( !options->topMenuEnabled()) 01988 return; 01989 // kdDebug() << "TopMenu selection lost owner" << endl; 01990 if( !topmenu_selection->claim( false )) 01991 { 01992 // kdDebug() << "Failed to claim TopMenu selection" << endl; 01993 return; 01994 } 01995 // kdDebug() << "claimed TopMenu selection" << endl; 01996 setupTopMenuHandling(); 01997 } 01998 01999 void Workspace::setupTopMenuHandling() 02000 { 02001 if( managing_topmenus ) 02002 return; 02003 connect( topmenu_selection, SIGNAL( lostOwnership()), this, SLOT( lostTopMenuSelection())); 02004 disconnect( topmenu_watcher, SIGNAL( lostOwner()), this, SLOT( lostTopMenuOwner())); 02005 managing_topmenus = true; 02006 topmenu_space = new QWidget; 02007 Window stack[ 2 ]; 02008 stack[ 0 ] = supportWindow->winId(); 02009 stack[ 1 ] = topmenu_space->winId(); 02010 XRestackWindows(qt_xdisplay(), stack, 2); 02011 updateTopMenuGeometry(); 02012 topmenu_space->show(); 02013 updateClientArea(); 02014 updateCurrentTopMenu(); 02015 } 02016 02017 int Workspace::topMenuHeight() const 02018 { 02019 if( topmenu_height == 0 ) 02020 { // simply create a dummy menubar and use its preffered height as the menu height 02021 KMenuBar tmpmenu; 02022 tmpmenu.insertItem( "dummy" ); 02023 topmenu_height = tmpmenu.sizeHint().height(); 02024 } 02025 return topmenu_height; 02026 } 02027 02028 KDecoration* Workspace::createDecoration( KDecorationBridge* bridge ) 02029 { 02030 return mgr->createDecoration( bridge ); 02031 } 02032 02033 QString Workspace::desktopName( int desk ) const 02034 { 02035 return QString::fromUtf8( rootInfo->desktopName( desk ) ); 02036 } 02037 02038 bool Workspace::checkStartupNotification( Window w, KStartupInfoId& id, KStartupInfoData& data ) 02039 { 02040 return startup->checkStartup( w, id, data ) == KStartupInfo::Match; 02041 } 02042 02047 void Workspace::focusToNull() 02048 { 02049 XSetInputFocus(qt_xdisplay(), null_focus_window, RevertToPointerRoot, qt_x_time ); 02050 } 02051 02052 void Workspace::helperDialog( const QString& message, const Client* c ) 02053 { 02054 QStringList args; 02055 QString type; 02056 if( message == "noborderaltf3" ) 02057 { 02058 QString shortcut = QString( "%1 (%2)" ).arg( keys->label( "Window Operations Menu" )) 02059 .arg( keys->shortcut( "Window Operations Menu" ).seq( 0 ).toString()); 02060 args << "--msgbox" << 02061 i18n( "You have selected to show a window without its border.\n" 02062 "Without the border, you will not be able to enable the border " 02063 "again using the mouse: use the window operations menu instead, " 02064 "activated using the %1 keyboard shortcut." ) 02065 .arg( shortcut ); 02066 type = "altf3warning"; 02067 } 02068 else if( message == "fullscreenaltf3" ) 02069 { 02070 QString shortcut = QString( "%1 (%2)" ).arg( keys->label( "Window Operations Menu" )) 02071 .arg( keys->shortcut( "Window Operations Menu" ).seq( 0 ).toString()); 02072 args << "--msgbox" << 02073 i18n( "You have selected to show a window in fullscreen mode.\n" 02074 "If the application itself does not have an option to turn the fullscreen " 02075 "mode off you will not be able to disable it " 02076 "again using the mouse: use the window operations menu instead, " 02077 "activated using the %1 keyboard shortcut." ) 02078 .arg( shortcut ); 02079 type = "altf3warning"; 02080 } 02081 else 02082 assert( false ); 02083 KProcess proc; 02084 proc << "kdialog" << args; 02085 if( !type.isEmpty()) 02086 { 02087 KConfig cfg( "kwin_dialogsrc" ); 02088 cfg.setGroup( "Notification Messages" ); // this depends on KMessageBox 02089 if( !cfg.readBoolEntry( type, true )) // has don't show again checked 02090 return; // save launching kdialog 02091 proc << "--dontagain" << "kwin_dialogsrc:" + type; 02092 } 02093 if( c != NULL ) 02094 proc << "--embed" << QString::number( c->window()); 02095 proc.start( KProcess::DontCare ); 02096 } 02097 02098 } // namespace 02099 02100 #include "workspace.moc"
KDE Logo
This file is part of the documentation for kwin Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Aug 13 21:47:06 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003