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