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