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 <tqpopupmenu.h> 00022 #include <klocale.h> 00023 #include <tqregexp.h> 00024 #include <tqpainter.h> 00025 #include <tqbitmap.h> 00026 #include <tqclipboard.h> 00027 #include <kmenubar.h> 00028 #include <kprocess.h> 00029 #include <kglobalaccel.h> 00030 #include <dcopclient.h> 00031 #include <kipc.h> 00032 00033 #include "plugins.h" 00034 #include "client.h" 00035 #include "popupinfo.h" 00036 #include "tabbox.h" 00037 #include "atoms.h" 00038 #include "placement.h" 00039 #include "notifications.h" 00040 #include "group.h" 00041 #include "rules.h" 00042 00043 #include <X11/extensions/shape.h> 00044 #include <X11/keysym.h> 00045 #include <X11/keysymdef.h> 00046 #include <X11/cursorfont.h> 00047 00048 #include <pwd.h> 00049 00050 namespace KWinInternal 00051 { 00052 00053 extern int screen_number; 00054 00055 Workspace *Workspace::_self = 0; 00056 00057 KProcess* kompmgr = 0; 00058 KSelectionOwner* kompmgr_selection; 00059 00060 bool allowKompmgrRestart = TRUE; 00061 extern bool disable_kwin_composition_manager; 00062 00063 bool supportsCompMgr() 00064 { 00065 if (disable_kwin_composition_manager) { 00066 return false; 00067 } 00068 00069 int i; 00070 00071 bool damageExt = XQueryExtension(qt_xdisplay(), "DAMAGE", &i, &i, &i); 00072 bool compositeExt = XQueryExtension(qt_xdisplay(), "Composite", &i, &i, &i); 00073 bool xfixesExt = XQueryExtension(qt_xdisplay(), "XFIXES", &i, &i, &i); 00074 00075 return damageExt && compositeExt && xfixesExt; 00076 } 00077 00078 // Rikkus: This class is too complex. It needs splitting further. 00079 // It's a nightmare to understand, especially with so few comments :( 00080 00081 // Matthias: Feel free to ask me questions about it. Feel free to add 00082 // comments. I disagree that further splittings makes it easier. 2500 00083 // lines are not too much. It's the task that is complex, not the 00084 // code. 00085 Workspace::Workspace( bool restore ) 00086 : DCOPObject ("KWinInterface"), 00087 TQObject (0, "workspace"), 00088 current_desktop (0), 00089 number_of_desktops(0), 00090 active_screen (0), 00091 active_popup( NULL ), 00092 active_popup_client( NULL ), 00093 desktop_widget (0), 00094 temporaryRulesMessages( "_KDE_NET_WM_TEMPORARY_RULES", NULL, false ), 00095 rules_updates_disabled( false ), 00096 active_client (0), 00097 last_active_client (0), 00098 next_active_client (0), 00099 most_recently_raised (0), 00100 movingClient(0), 00101 pending_take_activity ( NULL ), 00102 delayfocus_client (0), 00103 showing_desktop( false ), 00104 block_showing_desktop( 0 ), 00105 was_user_interaction (false), 00106 session_saving (false), 00107 control_grab (false), 00108 tab_grab (false), 00109 mouse_emulation (false), 00110 block_focus (0), 00111 tab_box (0), 00112 popupinfo (0), 00113 popup (0), 00114 advanced_popup (0), 00115 desk_popup (0), 00116 desk_popup_index (0), 00117 keys (0), 00118 client_keys ( NULL ), 00119 client_keys_dialog ( NULL ), 00120 client_keys_client ( NULL ), 00121 disable_shortcuts_keys ( NULL ), 00122 global_shortcuts_disabled( false ), 00123 global_shortcuts_disabled_for_client( false ), 00124 root (0), 00125 workspaceInit (true), 00126 startup(0), electric_have_borders(false), 00127 electric_current_border(0), 00128 electric_top_border(None), 00129 electric_bottom_border(None), 00130 electric_left_border(None), 00131 electric_right_border(None), 00132 layoutOrientation(Qt::Vertical), 00133 layoutX(-1), 00134 layoutY(2), 00135 workarea(NULL), 00136 screenarea(NULL), 00137 managing_topmenus( false ), 00138 topmenu_selection( NULL ), 00139 topmenu_watcher( NULL ), 00140 topmenu_height( 0 ), 00141 topmenu_space( NULL ), 00142 set_active_client_recursion( 0 ), 00143 block_stacking_updates( 0 ), 00144 forced_global_mouse_grab( false ) 00145 { 00146 _self = this; 00147 mgr = new PluginMgr; 00148 root = qt_xrootwin(); 00149 default_colormap = DefaultColormap(qt_xdisplay(), qt_xscreen() ); 00150 installed_colormap = default_colormap; 00151 session.setAutoDelete( TRUE ); 00152 00153 connect( &temporaryRulesMessages, TQT_SIGNAL( gotMessage( const TQString& )), 00154 this, TQT_SLOT( gotTemporaryRulesMessage( const TQString& ))); 00155 connect( &rulesUpdatedTimer, TQT_SIGNAL( timeout()), this, TQT_SLOT( writeWindowRules())); 00156 00157 updateXTime(); // needed for proper initialization of user_time in Client ctor 00158 00159 delayFocusTimer = 0; 00160 00161 electric_time_first = GET_QT_X_TIME(); 00162 electric_time_last = GET_QT_X_TIME(); 00163 00164 if ( restore ) 00165 loadSessionInfo(); 00166 00167 loadWindowRules(); 00168 00169 (void) TQApplication::desktop(); // trigger creation of desktop widget 00170 00171 desktop_widget = 00172 new TQWidget( 00173 0, 00174 "desktop_widget", 00175 (WFlags)(TQt::WType_Desktop | TQt::WPaintUnclipped) 00176 ); 00177 00178 kapp->setGlobalMouseTracking( true ); // so that this doesn't mess eventmask on root window later 00179 // call this before XSelectInput() on the root window 00180 startup = new KStartupInfo( 00181 KStartupInfo::DisableKWinModule | KStartupInfo::AnnounceSilenceChanges, this ); 00182 00183 // select windowmanager privileges 00184 XSelectInput(qt_xdisplay(), root, 00185 KeyPressMask | 00186 PropertyChangeMask | 00187 ColormapChangeMask | 00188 SubstructureRedirectMask | 00189 SubstructureNotifyMask | 00190 FocusChangeMask // for NotifyDetailNone 00191 ); 00192 00193 Shape::init(); 00194 00195 // compatibility 00196 long data = 1; 00197 00198 XChangeProperty( 00199 qt_xdisplay(), 00200 qt_xrootwin(), 00201 atoms->kwin_running, 00202 atoms->kwin_running, 00203 32, 00204 PropModeAppend, 00205 (unsigned char*) &data, 00206 1 00207 ); 00208 00209 client_keys = new KGlobalAccel( this ); 00210 initShortcuts(); 00211 tab_box = new TabBox( this ); 00212 popupinfo = new PopupInfo( this ); 00213 00214 init(); 00215 00216 #if (QT_VERSION-0 >= 0x030200) // XRANDR support 00217 connect( kapp->desktop(), TQT_SIGNAL( resized( int )), TQT_SLOT( desktopResized())); 00218 #endif 00219 00220 if (!supportsCompMgr()) { 00221 options->useTranslucency = false; 00222 } 00223 00224 // start kompmgr - i wanted to put this into main.cpp, but that would prevent dcop support, as long as Application was no dcop_object 00225 if (options->useTranslucency) 00226 { 00227 kompmgr = new KProcess; 00228 connect(kompmgr, TQT_SIGNAL(receivedStderr(KProcess*, char*, int)), TQT_SLOT(handleKompmgrOutput(KProcess*, char*, int))); 00229 *kompmgr << "kompmgr"; 00230 startKompmgr(); 00231 } 00232 else 00233 { 00234 // If kompmgr is already running, send it SIGTERM 00235 // Attempt to load the kompmgr pid file 00236 const char *home; 00237 struct passwd *p; 00238 p = getpwuid(getuid()); 00239 if (p) 00240 home = p->pw_dir; 00241 else 00242 home = getenv("HOME"); 00243 char *filename; 00244 const char *configfile = "/.kompmgr.pid"; 00245 int n = strlen(home)+strlen(configfile)+1; 00246 filename = (char*)malloc(n*sizeof(char)); 00247 memset(filename,0,n); 00248 strcat(filename, home); 00249 strcat(filename, configfile); 00250 00251 // Now that we did all that by way of introduction...read the file! 00252 FILE *pFile; 00253 char buffer[255]; 00254 pFile = fopen(filename, "r"); 00255 int kompmgrpid = 0; 00256 if (pFile) 00257 { 00258 printf("[kwin-workspace] Using '%s' as kompmgr pidfile\n\n", filename); 00259 // obtain file size 00260 fseek (pFile , 0 , SEEK_END); 00261 unsigned long lSize = ftell (pFile); 00262 if (lSize > 254) 00263 lSize = 254; 00264 rewind (pFile); 00265 size_t result = fread (buffer, 1, lSize, pFile); 00266 fclose(pFile); 00267 kompmgrpid = atoi(buffer); 00268 } 00269 00270 free(filename); 00271 filename = NULL; 00272 00273 if (kompmgrpid) 00274 { 00275 kill(kompmgrpid, SIGTERM); 00276 } 00277 else 00278 { 00279 stopKompmgr(); 00280 } 00281 } 00282 } 00283 00284 00285 void Workspace::init() 00286 { 00287 checkElectricBorders(); 00288 00289 // not used yet 00290 // topDock = 0L; 00291 // maximizedWindowCounter = 0; 00292 00293 supportWindow = new TQWidget; 00294 XLowerWindow( qt_xdisplay(), supportWindow->winId()); // see usage in layers.cpp 00295 00296 XSetWindowAttributes attr; 00297 attr.override_redirect = 1; 00298 null_focus_window = XCreateWindow( qt_xdisplay(), qt_xrootwin(), -1,-1, 1, 1, 0, CopyFromParent, 00299 InputOnly, CopyFromParent, CWOverrideRedirect, &attr ); 00300 XMapWindow(qt_xdisplay(), null_focus_window); 00301 00302 unsigned long protocols[ 5 ] = 00303 { 00304 NET::Supported | 00305 NET::SupportingWMCheck | 00306 NET::ClientList | 00307 NET::ClientListStacking | 00308 NET::DesktopGeometry | 00309 NET::NumberOfDesktops | 00310 NET::CurrentDesktop | 00311 NET::ActiveWindow | 00312 NET::WorkArea | 00313 NET::CloseWindow | 00314 NET::DesktopNames | 00315 NET::KDESystemTrayWindows | 00316 NET::WMName | 00317 NET::WMVisibleName | 00318 NET::WMDesktop | 00319 NET::WMWindowType | 00320 NET::WMState | 00321 NET::WMStrut | 00322 NET::WMIconGeometry | 00323 NET::WMIcon | 00324 NET::WMPid | 00325 NET::WMMoveResize | 00326 NET::WMKDESystemTrayWinFor | 00327 NET::WMFrameExtents | 00328 NET::WMPing 00329 , 00330 NET::NormalMask | 00331 NET::DesktopMask | 00332 NET::DockMask | 00333 NET::ToolbarMask | 00334 NET::MenuMask | 00335 NET::DialogMask | 00336 NET::OverrideMask | 00337 NET::TopMenuMask | 00338 NET::UtilityMask | 00339 NET::SplashMask | 00340 0 00341 , 00342 NET::Modal | 00343 // NET::Sticky | // large desktops not supported (and probably never will be) 00344 NET::MaxVert | 00345 NET::MaxHoriz | 00346 NET::Shaded | 00347 NET::SkipTaskbar | 00348 NET::KeepAbove | 00349 // NET::StaysOnTop | the same like KeepAbove 00350 NET::SkipPager | 00351 NET::Hidden | 00352 NET::FullScreen | 00353 NET::KeepBelow | 00354 NET::DemandsAttention | 00355 0 00356 , 00357 NET::WM2UserTime | 00358 NET::WM2StartupId | 00359 NET::WM2AllowedActions | 00360 NET::WM2RestackWindow | 00361 NET::WM2MoveResizeWindow | 00362 NET::WM2ExtendedStrut | 00363 NET::WM2KDETemporaryRules | 00364 NET::WM2ShowingDesktop | 00365 NET::WM2FullPlacement | 00366 NET::WM2DesktopLayout | 00367 0 00368 , 00369 NET::ActionMove | 00370 NET::ActionResize | 00371 NET::ActionMinimize | 00372 NET::ActionShade | 00373 // NET::ActionStick | // Sticky state is not supported 00374 NET::ActionMaxVert | 00375 NET::ActionMaxHoriz | 00376 NET::ActionFullScreen | 00377 NET::ActionChangeDesktop | 00378 NET::ActionClose | 00379 0 00380 , 00381 }; 00382 00383 rootInfo = new RootInfo( this, qt_xdisplay(), supportWindow->winId(), "KWin", 00384 protocols, 5, qt_xscreen() ); 00385 00386 loadDesktopSettings(); 00387 updateDesktopLayout(); 00388 // extra NETRootInfo instance in Client mode is needed to get the values of the properties 00389 NETRootInfo client_info( qt_xdisplay(), NET::ActiveWindow | NET::CurrentDesktop ); 00390 int initial_desktop; 00391 if( !kapp->isSessionRestored()) 00392 initial_desktop = client_info.currentDesktop(); 00393 else 00394 { 00395 KConfigGroupSaver saver( kapp->sessionConfig(), "Session" ); 00396 initial_desktop = kapp->sessionConfig()->readNumEntry( "desktop", 1 ); 00397 } 00398 if( !setCurrentDesktop( initial_desktop )) 00399 setCurrentDesktop( 1 ); 00400 00401 // now we know how many desktops we'll, thus, we initialise the positioning object 00402 initPositioning = new Placement(this); 00403 00404 connect(&reconfigureTimer, TQT_SIGNAL(timeout()), this, 00405 TQT_SLOT(slotReconfigure())); 00406 connect( &updateToolWindowsTimer, TQT_SIGNAL( timeout()), this, TQT_SLOT( slotUpdateToolWindows())); 00407 00408 connect(kapp, TQT_SIGNAL(appearanceChanged()), this, 00409 TQT_SLOT(slotReconfigure())); 00410 connect(kapp, TQT_SIGNAL(settingsChanged(int)), this, 00411 TQT_SLOT(slotSettingsChanged(int))); 00412 connect(kapp, TQT_SIGNAL( kipcMessage( int, int )), this, TQT_SLOT( kipcMessage( int, int ))); 00413 00414 active_client = NULL; 00415 rootInfo->setActiveWindow( None ); 00416 focusToNull(); 00417 if( !kapp->isSessionRestored()) 00418 ++block_focus; // because it will be set below 00419 00420 char nm[ 100 ]; 00421 sprintf( nm, "_KDE_TOPMENU_OWNER_S%d", DefaultScreen( qt_xdisplay())); 00422 Atom topmenu_atom = XInternAtom( qt_xdisplay(), nm, False ); 00423 topmenu_selection = new KSelectionOwner( topmenu_atom ); 00424 topmenu_watcher = new KSelectionWatcher( topmenu_atom ); 00425 // TODO grabXServer(); - where exactly put this? topmenu selection claiming down belong must be before 00426 00427 { // begin updates blocker block 00428 StackingUpdatesBlocker blocker( this ); 00429 00430 if( options->topMenuEnabled() && topmenu_selection->claim( false )) 00431 setupTopMenuHandling(); // this can call updateStackingOrder() 00432 else 00433 lostTopMenuSelection(); 00434 00435 unsigned int i, nwins; 00436 Window root_return, parent_return, *wins; 00437 XQueryTree(qt_xdisplay(), root, &root_return, &parent_return, &wins, &nwins); 00438 for (i = 0; i < nwins; i++) 00439 { 00440 XWindowAttributes attr; 00441 XGetWindowAttributes(qt_xdisplay(), wins[i], &attr); 00442 if (attr.override_redirect ) 00443 continue; 00444 if( topmenu_space && topmenu_space->winId() == wins[ i ] ) 00445 continue; 00446 if (attr.map_state != IsUnmapped) 00447 { 00448 if ( addSystemTrayWin( wins[i] ) ) 00449 continue; 00450 Client* c = createClient( wins[i], true ); 00451 if ( c != NULL && root != qt_xrootwin() ) 00452 { // TODO what is this? 00453 // TODO may use TQWidget:.create 00454 XReparentWindow( qt_xdisplay(), c->frameId(), root, 0, 0 ); 00455 c->move(0,0); 00456 } 00457 } 00458 } 00459 if ( wins ) 00460 XFree((void *) wins); 00461 // propagate clients, will really happen at the end of the updates blocker block 00462 updateStackingOrder( true ); 00463 00464 updateClientArea(); 00465 raiseElectricBorders(); 00466 00467 // NETWM spec says we have to set it to (0,0) if we don't support it 00468 NETPoint* viewports = new NETPoint[ number_of_desktops ]; 00469 rootInfo->setDesktopViewport( number_of_desktops, *viewports ); 00470 delete[] viewports; 00471 TQRect geom = TQApplication::desktop()->geometry(); 00472 NETSize desktop_geometry; 00473 desktop_geometry.width = geom.width(); 00474 desktop_geometry.height = geom.height(); 00475 rootInfo->setDesktopGeometry( -1, desktop_geometry ); 00476 setShowingDesktop( false ); 00477 00478 } // end updates blocker block 00479 00480 Client* new_active_client = NULL; 00481 if( !kapp->isSessionRestored()) 00482 { 00483 --block_focus; 00484 new_active_client = findClient( WindowMatchPredicate( client_info.activeWindow())); 00485 } 00486 if( new_active_client == NULL 00487 && activeClient() == NULL && should_get_focus.count() == 0 ) // no client activated in manage() 00488 { 00489 if( new_active_client == NULL ) 00490 new_active_client = topClientOnDesktop( currentDesktop()); 00491 if( new_active_client == NULL && !desktops.isEmpty() ) 00492 new_active_client = findDesktop( true, currentDesktop()); 00493 } 00494 if( new_active_client != NULL ) 00495 activateClient( new_active_client ); 00496 // SELI TODO this won't work with unreasonable focus policies, 00497 // and maybe in rare cases also if the selected client doesn't 00498 // want focus 00499 workspaceInit = false; 00500 // TODO ungrabXServer() 00501 } 00502 00503 Workspace::~Workspace() 00504 { 00505 if (kompmgr) 00506 delete kompmgr; 00507 blockStackingUpdates( true ); 00508 // TODO grabXServer(); 00509 // use stacking_order, so that kwin --replace keeps stacking order 00510 for( ClientList::ConstIterator it = stacking_order.begin(); 00511 it != stacking_order.end(); 00512 ++it ) 00513 { 00514 // only release the window 00515 (*it)->releaseWindow( true ); 00516 // No removeClient() is called, it does more than just removing. 00517 // However, remove from some lists to e.g. prevent performTransiencyCheck() 00518 // from crashing. 00519 clients.remove( *it ); 00520 desktops.remove( *it ); 00521 } 00522 delete desktop_widget; 00523 delete tab_box; 00524 delete popupinfo; 00525 delete popup; 00526 if ( root == qt_xrootwin() ) 00527 XDeleteProperty(qt_xdisplay(), qt_xrootwin(), atoms->kwin_running); 00528 00529 writeWindowRules(); 00530 KGlobal::config()->sync(); 00531 00532 delete rootInfo; 00533 delete supportWindow; 00534 delete mgr; 00535 delete[] workarea; 00536 delete[] screenarea; 00537 delete startup; 00538 delete initPositioning; 00539 delete topmenu_watcher; 00540 delete topmenu_selection; 00541 delete topmenu_space; 00542 delete client_keys_dialog; 00543 while( !rules.isEmpty()) 00544 { 00545 delete rules.front(); 00546 rules.pop_front(); 00547 } 00548 XDestroyWindow( qt_xdisplay(), null_focus_window ); 00549 // TODO ungrabXServer(); 00550 _self = 0; 00551 } 00552 00553 Client* Workspace::createClient( Window w, bool is_mapped ) 00554 { 00555 StackingUpdatesBlocker blocker( this ); 00556 Client* c = new Client( this ); 00557 if( !c->manage( w, is_mapped )) 00558 { 00559 Client::deleteClient( c, Allowed ); 00560 return NULL; 00561 } 00562 addClient( c, Allowed ); 00563 return c; 00564 } 00565 00566 void Workspace::addClient( Client* c, allowed_t ) 00567 { 00568 // waited with trans settings until window figured out if active or not ;) 00569 // qWarning("%s", (const char*)(c->resourceClass())); 00570 c->setBMP(c->resourceName() == "beep-media-player" || c->decorationId() == None); 00571 // first check if the window has it's own opinion of it's translucency ;) 00572 c->getWindowOpacity(); 00573 if (c->isDock()) 00574 { 00575 // if (c->x() == 0 && c->y() == 0 && c->width() > c->height()) topDock = c; 00576 if (!c->hasCustomOpacity()) // this xould be done slightly more efficient, but we want to support the topDock in future 00577 { 00578 c->setShadowSize(options->dockShadowSize); 00579 c->setOpacity(options->translucentDocks, options->dockOpacity); 00580 } 00581 } 00582 00583 if (c->isMenu() || c->isTopMenu()) 00584 { 00585 c->setShadowSize(options->menuShadowSize); 00586 } 00587 //------------------------------------------------ 00588 Group* grp = findGroup( c->window()); 00589 if( grp != NULL ) 00590 grp->gotLeader( c ); 00591 00592 if ( c->isDesktop() ) 00593 { 00594 desktops.append( c ); 00595 if( active_client == NULL && should_get_focus.isEmpty() && c->isOnCurrentDesktop()) 00596 requestFocus( c ); // CHECKME? make sure desktop is active after startup if there's no other window active 00597 } 00598 else 00599 { 00600 updateFocusChains( c, FocusChainUpdate ); // add to focus chain if not already there 00601 clients.append( c ); 00602 } 00603 if( !unconstrained_stacking_order.contains( c )) 00604 unconstrained_stacking_order.append( c ); 00605 if( !stacking_order.contains( c )) // it'll be updated later, and updateToolWindows() requires 00606 stacking_order.append( c ); // c to be in stacking_order 00607 if( c->isTopMenu()) 00608 addTopMenu( c ); 00609 updateClientArea(); // this cannot be in manage(), because the client got added only now 00610 updateClientLayer( c ); 00611 if( c->isDesktop()) 00612 { 00613 raiseClient( c ); 00614 // if there's no active client, make this desktop the active one 00615 if( activeClient() == NULL && should_get_focus.count() == 0 ) 00616 activateClient( findDesktop( true, currentDesktop())); 00617 } 00618 c->checkActiveModal(); 00619 checkTransients( c->window()); // SELI does this really belong here? 00620 updateStackingOrder( true ); // propagate new client 00621 if( c->isUtility() || c->isMenu() || c->isToolbar()) 00622 updateToolWindows( true ); 00623 checkNonExistentClients(); 00624 } 00625 00626 /* 00627 Destroys the client \a c 00628 */ 00629 void Workspace::removeClient( Client* c, allowed_t ) 00630 { 00631 if (c == active_popup_client) 00632 closeActivePopup(); 00633 00634 if( client_keys_client == c ) 00635 setupWindowShortcutDone( false ); 00636 if( !c->shortcut().isNull()) 00637 c->setShortcut( TQString::null ); // remove from client_keys 00638 00639 if( c->isDialog()) 00640 Notify::raise( Notify::TransDelete ); 00641 if( c->isNormalWindow()) 00642 Notify::raise( Notify::Delete ); 00643 00644 Q_ASSERT( clients.contains( c ) || desktops.contains( c )); 00645 clients.remove( c ); 00646 desktops.remove( c ); 00647 unconstrained_stacking_order.remove( c ); 00648 stacking_order.remove( c ); 00649 for( int i = 1; 00650 i <= numberOfDesktops(); 00651 ++i ) 00652 focus_chain[ i ].remove( c ); 00653 global_focus_chain.remove( c ); 00654 attention_chain.remove( c ); 00655 showing_desktop_clients.remove( c ); 00656 if( c->isTopMenu()) 00657 removeTopMenu( c ); 00658 Group* group = findGroup( c->window()); 00659 if( group != NULL ) 00660 group->lostLeader(); 00661 00662 if ( c == most_recently_raised ) 00663 most_recently_raised = 0; 00664 should_get_focus.remove( c ); 00665 Q_ASSERT( c != active_client ); 00666 if ( c == last_active_client ) 00667 last_active_client = 0; 00668 if( c == pending_take_activity ) 00669 pending_take_activity = NULL; 00670 if( c == delayfocus_client ) 00671 cancelDelayFocus(); 00672 00673 updateStackingOrder( true ); 00674 00675 if (tab_grab) 00676 tab_box->repaint(); 00677 00678 updateClientArea(); 00679 } 00680 00681 void Workspace::updateFocusChains( Client* c, FocusChainChange change ) 00682 { 00683 if( !c->wantsTabFocus()) // doesn't want tab focus, remove 00684 { 00685 for( int i=1; 00686 i<= numberOfDesktops(); 00687 ++i ) 00688 focus_chain[i].remove(c); 00689 global_focus_chain.remove( c ); 00690 return; 00691 } 00692 if(c->desktop() == NET::OnAllDesktops) 00693 { //now on all desktops, add it to focus_chains it is not already in 00694 for( int i=1; i<= numberOfDesktops(); i++) 00695 { // making first/last works only on current desktop, don't affect all desktops 00696 if( i == currentDesktop() 00697 && ( change == FocusChainMakeFirst || change == FocusChainMakeLast )) 00698 { 00699 focus_chain[ i ].remove( c ); 00700 if( change == FocusChainMakeFirst ) 00701 focus_chain[ i ].append( c ); 00702 else 00703 focus_chain[ i ].prepend( c ); 00704 } 00705 else if( !focus_chain[ i ].contains( c )) 00706 { // add it after the active one 00707 if( active_client != NULL && active_client != c 00708 && !focus_chain[ i ].isEmpty() && focus_chain[ i ].last() == active_client ) 00709 focus_chain[ i ].insert( focus_chain[ i ].fromLast(), c ); 00710 else 00711 focus_chain[ i ].append( c ); // otherwise add as the first one 00712 } 00713 } 00714 } 00715 else //now only on desktop, remove it anywhere else 00716 { 00717 for( int i=1; i<= numberOfDesktops(); i++) 00718 { 00719 if( i == c->desktop()) 00720 { 00721 if( change == FocusChainMakeFirst ) 00722 { 00723 focus_chain[ i ].remove( c ); 00724 focus_chain[ i ].append( c ); 00725 } 00726 else if( change == FocusChainMakeLast ) 00727 { 00728 focus_chain[ i ].remove( c ); 00729 focus_chain[ i ].prepend( c ); 00730 } 00731 else if( !focus_chain[ i ].contains( c )) 00732 { 00733 if( active_client != NULL && active_client != c 00734 && !focus_chain[ i ].isEmpty() && focus_chain[ i ].last() == active_client ) 00735 focus_chain[ i ].insert( focus_chain[ i ].fromLast(), c ); 00736 else 00737 focus_chain[ i ].append( c ); // otherwise add as the first one 00738 } 00739 } 00740 else 00741 focus_chain[ i ].remove( c ); 00742 } 00743 } 00744 if( change == FocusChainMakeFirst ) 00745 { 00746 global_focus_chain.remove( c ); 00747 global_focus_chain.append( c ); 00748 } 00749 else if( change == FocusChainMakeLast ) 00750 { 00751 global_focus_chain.remove( c ); 00752 global_focus_chain.prepend( c ); 00753 } 00754 else if( !global_focus_chain.contains( c )) 00755 { 00756 if( active_client != NULL && active_client != c 00757 && !global_focus_chain.isEmpty() && global_focus_chain.last() == active_client ) 00758 global_focus_chain.insert( global_focus_chain.fromLast(), c ); 00759 else 00760 global_focus_chain.append( c ); // otherwise add as the first one 00761 } 00762 } 00763 00764 void Workspace::updateOverlappingShadows(unsigned long window) 00765 { 00766 Client *client; 00767 00768 if ((client = findClient(WindowMatchPredicate((WId)window)))) 00769 // Redraw overlapping shadows without waiting for the specified window 00770 // to redraw its own shadow 00771 client->drawOverlappingShadows(false); 00772 } 00773 00774 void Workspace::setShadowed(unsigned long window, bool shadowed) 00775 { 00776 Client *client; 00777 00778 if ((client = findClient(WindowMatchPredicate((WId)window)))) 00779 client->setShadowed(shadowed); 00780 } 00781 00782 void Workspace::updateCurrentTopMenu() 00783 { 00784 if( !managingTopMenus()) 00785 return; 00786 // toplevel menubar handling 00787 Client* menubar = 0; 00788 bool block_desktop_menubar = false; 00789 if( active_client ) 00790 { 00791 // show the new menu bar first... 00792 Client* menu_client = active_client; 00793 for(;;) 00794 { 00795 if( menu_client->isFullScreen()) 00796 block_desktop_menubar = true; 00797 for( ClientList::ConstIterator it = menu_client->transients().begin(); 00798 it != menu_client->transients().end(); 00799 ++it ) 00800 if( (*it)->isTopMenu()) 00801 { 00802 menubar = *it; 00803 break; 00804 } 00805 if( menubar != NULL || !menu_client->isTransient()) 00806 break; 00807 if( menu_client->isModal() || menu_client->transientFor() == NULL ) 00808 break; // don't use mainwindow's menu if this is modal or group transient 00809 menu_client = menu_client->transientFor(); 00810 } 00811 if( !menubar ) 00812 { // try to find any topmenu from the application (#72113) 00813 for( ClientList::ConstIterator it = active_client->group()->members().begin(); 00814 it != active_client->group()->members().end(); 00815 ++it ) 00816 if( (*it)->isTopMenu()) 00817 { 00818 menubar = *it; 00819 break; 00820 } 00821 } 00822 } 00823 if( !menubar && !block_desktop_menubar && options->desktopTopMenu()) 00824 { 00825 // Find the menubar of the desktop 00826 Client* desktop = findDesktop( true, currentDesktop()); 00827 if( desktop != NULL ) 00828 { 00829 for( ClientList::ConstIterator it = desktop->transients().begin(); 00830 it != desktop->transients().end(); 00831 ++it ) 00832 if( (*it)->isTopMenu()) 00833 { 00834 menubar = *it; 00835 break; 00836 } 00837 } 00838 // TODO to be cleaned app with window grouping 00839 // Without qt-copy patch #0009, the topmenu and desktop are not in the same group, 00840 // thus the topmenu is not transient for it :-/. 00841 if( menubar == NULL ) 00842 { 00843 for( ClientList::ConstIterator it = topmenus.begin(); 00844 it != topmenus.end(); 00845 ++it ) 00846 if( (*it)->wasOriginallyGroupTransient()) // kdesktop's topmenu has WM_TRANSIENT_FOR 00847 { // set pointing to the root window 00848 menubar = *it; // to recognize it here 00849 break; // Also, with the xroot hack in kdesktop, 00850 } // there's no NET::Desktop window to be transient for 00851 } 00852 } 00853 00854 // kdDebug() << "CURRENT TOPMENU:" << menubar << ":" << active_client << endl; 00855 if ( menubar ) 00856 { 00857 if( active_client && !menubar->isOnDesktop( active_client->desktop())) 00858 menubar->setDesktop( active_client->desktop()); 00859 menubar->hideClient( false ); 00860 topmenu_space->hide(); 00861 // make it appear like it's been raised manually - it's in the Dock layer anyway, 00862 // and not raising it could mess up stacking order of topmenus within one application, 00863 // and thus break raising of mainclients in raiseClient() 00864 unconstrained_stacking_order.remove( menubar ); 00865 unconstrained_stacking_order.append( menubar ); 00866 } 00867 else if( !block_desktop_menubar ) 00868 { // no topmenu active - show the space window, so that there's not empty space 00869 topmenu_space->show(); 00870 } 00871 00872 // ... then hide the other ones. Avoids flickers. 00873 for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) 00874 { 00875 if( (*it)->isTopMenu() && (*it) != menubar ) 00876 (*it)->hideClient( true ); 00877 } 00878 } 00879 00880 00881 void Workspace::updateToolWindows( bool also_hide ) 00882 { 00883 // TODO what if Client's transiency/group changes? should this be called too? (I'm paranoid, am I not?) 00884 if( !options->hideUtilityWindowsForInactive ) 00885 { 00886 for( ClientList::ConstIterator it = clients.begin(); 00887 it != clients.end(); 00888 ++it ) 00889 (*it)->hideClient( false ); 00890 return; 00891 } 00892 const Group* group = NULL; 00893 const Client* client = active_client; 00894 // Go up in transiency hiearchy, if the top is found, only tool transients for the top mainwindow 00895 // will be shown; if a group transient is group, all tools in the group will be shown 00896 while( client != NULL ) 00897 { 00898 if( !client->isTransient()) 00899 break; 00900 if( client->groupTransient()) 00901 { 00902 group = client->group(); 00903 break; 00904 } 00905 client = client->transientFor(); 00906 } 00907 // use stacking order only to reduce flicker, it doesn't matter if block_stacking_updates == 0, 00908 // i.e. if it's not up to date 00909 00910 // SELI but maybe it should - what if a new client has been added that's not in stacking order yet? 00911 ClientList to_show, to_hide; 00912 for( ClientList::ConstIterator it = stacking_order.begin(); 00913 it != stacking_order.end(); 00914 ++it ) 00915 { 00916 if( (*it)->isUtility() || (*it)->isMenu() || (*it)->isToolbar()) 00917 { 00918 bool show = true; 00919 if( !(*it)->isTransient()) 00920 { 00921 if( (*it)->group()->members().count() == 1 ) // has its own group, keep always visible 00922 show = true; 00923 else if( client != NULL && (*it)->group() == client->group()) 00924 show = true; 00925 else 00926 show = false; 00927 } 00928 else 00929 { 00930 if( group != NULL && (*it)->group() == group ) 00931 show = true; 00932 else if( client != NULL && client->hasTransient( (*it), true )) 00933 show = true; 00934 else 00935 show = false; 00936 } 00937 if( !show && also_hide ) 00938 { 00939 const ClientList mainclients = (*it)->mainClients(); 00940 // don't hide utility windows which are standalone(?) or 00941 // have e.g. kicker as mainwindow 00942 if( mainclients.isEmpty()) 00943 show = true; 00944 for( ClientList::ConstIterator it2 = mainclients.begin(); 00945 it2 != mainclients.end(); 00946 ++it2 ) 00947 { 00948 if( (*it2)->isSpecialWindow()) 00949 show = true; 00950 } 00951 if( !show ) 00952 to_hide.append( *it ); 00953 } 00954 if( show ) 00955 to_show.append( *it ); 00956 } 00957 } // first show new ones, then hide 00958 for( ClientList::ConstIterator it = to_show.fromLast(); 00959 it != to_show.end(); 00960 --it ) // from topmost 00961 // TODO since this is in stacking order, the order of taskbar entries changes :( 00962 (*it)->hideClient( false ); 00963 if( also_hide ) 00964 { 00965 for( ClientList::ConstIterator it = to_hide.begin(); 00966 it != to_hide.end(); 00967 ++it ) // from bottommost 00968 (*it)->hideClient( true ); 00969 updateToolWindowsTimer.stop(); 00970 } 00971 else // setActiveClient() is after called with NULL client, quickly followed 00972 { // by setting a new client, which would result in flickering 00973 updateToolWindowsTimer.start( 50, true ); 00974 } 00975 } 00976 00977 void Workspace::slotUpdateToolWindows() 00978 { 00979 updateToolWindows( true ); 00980 } 00981 00985 void Workspace::updateColormap() 00986 { 00987 Colormap cmap = default_colormap; 00988 if ( activeClient() && activeClient()->colormap() != None ) 00989 cmap = activeClient()->colormap(); 00990 if ( cmap != installed_colormap ) 00991 { 00992 XInstallColormap(qt_xdisplay(), cmap ); 00993 installed_colormap = cmap; 00994 } 00995 } 00996 00997 void Workspace::reconfigure() 00998 { 00999 reconfigureTimer.start(200, true); 01000 } 01001 01002 01003 void Workspace::slotSettingsChanged(int category) 01004 { 01005 kdDebug(1212) << "Workspace::slotSettingsChanged()" << endl; 01006 if( category == (int) KApplication::SETTINGS_SHORTCUTS ) 01007 readShortcuts(); 01008 } 01009 01013 KWIN_PROCEDURE( CheckBorderSizesProcedure, cl->checkBorderSizes() ); 01014 01015 void Workspace::slotReconfigure() 01016 { 01017 kdDebug(1212) << "Workspace::slotReconfigure()" << endl; 01018 reconfigureTimer.stop(); 01019 01020 KGlobal::config()->reparseConfiguration(); 01021 unsigned long changed = options->updateSettings(); 01022 tab_box->reconfigure(); 01023 popupinfo->reconfigure(); 01024 initPositioning->reinitCascading( 0 ); 01025 readShortcuts(); 01026 forEachClient( CheckIgnoreFocusStealingProcedure()); 01027 updateToolWindows( true ); 01028 01029 if( mgr->reset( changed )) 01030 { // decorations need to be recreated 01031 #if 0 // This actually seems to make things worse now 01032 TQWidget curtain; 01033 curtain.setBackgroundMode( NoBackground ); 01034 curtain.setGeometry( TQApplication::desktop()->geometry() ); 01035 curtain.show(); 01036 #endif 01037 for( ClientList::ConstIterator it = clients.begin(); 01038 it != clients.end(); 01039 ++it ) 01040 { 01041 (*it)->updateDecoration( true, true ); 01042 } 01043 mgr->destroyPreviousPlugin(); 01044 } 01045 else 01046 { 01047 forEachClient( CheckBorderSizesProcedure()); 01048 } 01049 01050 checkElectricBorders(); 01051 01052 if( options->topMenuEnabled() && !managingTopMenus()) 01053 { 01054 if( topmenu_selection->claim( false )) 01055 setupTopMenuHandling(); 01056 else 01057 lostTopMenuSelection(); 01058 } 01059 else if( !options->topMenuEnabled() && managingTopMenus()) 01060 { 01061 topmenu_selection->release(); 01062 lostTopMenuSelection(); 01063 } 01064 topmenu_height = 0; // invalidate used menu height 01065 if( managingTopMenus()) 01066 { 01067 updateTopMenuGeometry(); 01068 updateCurrentTopMenu(); 01069 } 01070 01071 loadWindowRules(); 01072 for( ClientList::Iterator it = clients.begin(); 01073 it != clients.end(); 01074 ++it ) 01075 { 01076 (*it)->setupWindowRules( true ); 01077 (*it)->applyWindowRules(); 01078 discardUsedWindowRules( *it, false ); 01079 } 01080 01081 if (options->resetKompmgr) // need restart 01082 { 01083 bool tmp = options->useTranslucency; 01084 01085 // If kompmgr is already running, sending SIGUSR2 will force a reload of its settings 01086 // Attempt to load the kompmgr pid file 01087 const char *home; 01088 struct passwd *p; 01089 p = getpwuid(getuid()); 01090 if (p) 01091 home = p->pw_dir; 01092 else 01093 home = getenv("HOME"); 01094 char *filename; 01095 const char *configfile = "/.kompmgr.pid"; 01096 int n = strlen(home)+strlen(configfile)+1; 01097 filename = (char*)malloc(n*sizeof(char)); 01098 memset(filename,0,n); 01099 strcat(filename, home); 01100 strcat(filename, configfile); 01101 01102 // Now that we did all that by way of introduction...read the file! 01103 FILE *pFile; 01104 char buffer[255]; 01105 pFile = fopen(filename, "r"); 01106 int kompmgrpid = 0; 01107 if (pFile) 01108 { 01109 printf("[kwin-workspace] Using '%s' as kompmgr pidfile\n\n", filename); 01110 // obtain file size 01111 fseek (pFile , 0 , SEEK_END); 01112 unsigned long lSize = ftell (pFile); 01113 if (lSize > 254) 01114 lSize = 254; 01115 rewind (pFile); 01116 size_t result = fread (buffer, 1, lSize, pFile); 01117 fclose(pFile); 01118 kompmgrpid = atoi(buffer); 01119 } 01120 01121 free(filename); 01122 filename = NULL; 01123 01124 if (tmp) 01125 { 01126 if (kompmgrpid) 01127 { 01128 kill(kompmgrpid, SIGUSR2); 01129 } 01130 else 01131 { 01132 stopKompmgr(); 01133 TQTimer::singleShot( 200, this, TQT_SLOT(startKompmgr()) ); // wait some time to ensure system's ready for restart 01134 } 01135 } 01136 else 01137 { 01138 if (kompmgrpid) 01139 { 01140 kill(kompmgrpid, SIGTERM); 01141 } 01142 else 01143 { 01144 stopKompmgr(); 01145 } 01146 } 01147 } 01148 } 01149 01150 void Workspace::loadDesktopSettings() 01151 { 01152 KConfig* c = KGlobal::config(); 01153 TQCString groupname; 01154 if (screen_number == 0) 01155 groupname = "Desktops"; 01156 else 01157 groupname.sprintf("Desktops-screen-%d", screen_number); 01158 KConfigGroupSaver saver(c,groupname); 01159 01160 int n = c->readNumEntry("Number", 4); 01161 number_of_desktops = n; 01162 delete workarea; 01163 workarea = new TQRect[ n + 1 ]; 01164 delete screenarea; 01165 screenarea = NULL; 01166 rootInfo->setNumberOfDesktops( number_of_desktops ); 01167 desktop_focus_chain.resize( n ); 01168 // make it +1, so that it can be accessed as [1..numberofdesktops] 01169 focus_chain.resize( n + 1 ); 01170 for(int i = 1; i <= n; i++) 01171 { 01172 TQString s = c->readEntry(TQString("Name_%1").arg(i), 01173 i18n("Desktop %1").arg(i)); 01174 rootInfo->setDesktopName( i, s.utf8().data() ); 01175 desktop_focus_chain[i-1] = i; 01176 } 01177 } 01178 01179 void Workspace::saveDesktopSettings() 01180 { 01181 KConfig* c = KGlobal::config(); 01182 TQCString groupname; 01183 if (screen_number == 0) 01184 groupname = "Desktops"; 01185 else 01186 groupname.sprintf("Desktops-screen-%d", screen_number); 01187 KConfigGroupSaver saver(c,groupname); 01188 01189 c->writeEntry("Number", number_of_desktops ); 01190 for(int i = 1; i <= number_of_desktops; i++) 01191 { 01192 TQString s = desktopName( i ); 01193 TQString defaultvalue = i18n("Desktop %1").arg(i); 01194 if ( s.isEmpty() ) 01195 { 01196 s = defaultvalue; 01197 rootInfo->setDesktopName( i, s.utf8().data() ); 01198 } 01199 01200 if (s != defaultvalue) 01201 { 01202 c->writeEntry( TQString("Name_%1").arg(i), s ); 01203 } 01204 else 01205 { 01206 TQString currentvalue = c->readEntry(TQString("Name_%1").arg(i)); 01207 if (currentvalue != defaultvalue) 01208 c->writeEntry( TQString("Name_%1").arg(i), "" ); 01209 } 01210 } 01211 } 01212 01213 TQStringList Workspace::configModules(bool controlCenter) 01214 { 01215 TQStringList args; 01216 args << "kde-kwindecoration.desktop"; 01217 if (controlCenter) 01218 args << "kde-kwinoptions.desktop"; 01219 else if (kapp->authorizeControlModule("kde-kwinoptions.desktop")) 01220 args << "kwinactions" << "kwinfocus" << "kwinmoving" << "kwinadvanced" << "kwinrules" << "kwintranslucency"; 01221 return args; 01222 } 01223 01224 void Workspace::configureWM() 01225 { 01226 KApplication::kdeinitExec( "kcmshell", configModules(false) ); 01227 } 01228 01232 void Workspace::doNotManage( TQString title ) 01233 { 01234 doNotManageList.append( title ); 01235 } 01236 01240 bool Workspace::isNotManaged( const TQString& title ) 01241 { 01242 for ( TQStringList::Iterator it = doNotManageList.begin(); it != doNotManageList.end(); ++it ) 01243 { 01244 TQRegExp r( (*it) ); 01245 if (r.search(title) != -1) 01246 { 01247 doNotManageList.remove( it ); 01248 return TRUE; 01249 } 01250 } 01251 return FALSE; 01252 } 01253 01257 void Workspace::refresh() 01258 { 01259 TQWidget w; 01260 w.setGeometry( TQApplication::desktop()->geometry() ); 01261 w.show(); 01262 w.hide(); 01263 TQApplication::flushX(); 01264 } 01265 01273 class ObscuringWindows 01274 { 01275 public: 01276 ~ObscuringWindows(); 01277 void create( Client* c ); 01278 private: 01279 TQValueList<Window> obscuring_windows; 01280 static TQValueList<Window>* cached; 01281 static unsigned int max_cache_size; 01282 }; 01283 01284 TQValueList<Window>* ObscuringWindows::cached = 0; 01285 unsigned int ObscuringWindows::max_cache_size = 0; 01286 01287 void ObscuringWindows::create( Client* c ) 01288 { 01289 if( cached == 0 ) 01290 cached = new TQValueList<Window>; 01291 Window obs_win; 01292 XWindowChanges chngs; 01293 int mask = CWSibling | CWStackMode; 01294 if( cached->count() > 0 ) 01295 { 01296 cached->remove( obs_win = cached->first()); 01297 chngs.x = c->x(); 01298 chngs.y = c->y(); 01299 chngs.width = c->width(); 01300 chngs.height = c->height(); 01301 mask |= CWX | CWY | CWWidth | CWHeight; 01302 } 01303 else 01304 { 01305 XSetWindowAttributes a; 01306 a.background_pixmap = None; 01307 a.override_redirect = True; 01308 obs_win = XCreateWindow( qt_xdisplay(), qt_xrootwin(), c->x(), c->y(), 01309 c->width(), c->height(), 0, CopyFromParent, InputOutput, 01310 CopyFromParent, CWBackPixmap | CWOverrideRedirect, &a ); 01311 } 01312 chngs.sibling = c->frameId(); 01313 chngs.stack_mode = Below; 01314 XConfigureWindow( qt_xdisplay(), obs_win, mask, &chngs ); 01315 XMapWindow( qt_xdisplay(), obs_win ); 01316 obscuring_windows.append( obs_win ); 01317 } 01318 01319 ObscuringWindows::~ObscuringWindows() 01320 { 01321 max_cache_size = TQMAX( max_cache_size, obscuring_windows.count() + 4 ) - 1; 01322 for( TQValueList<Window>::ConstIterator it = obscuring_windows.begin(); 01323 it != obscuring_windows.end(); 01324 ++it ) 01325 { 01326 XUnmapWindow( qt_xdisplay(), *it ); 01327 if( cached->count() < max_cache_size ) 01328 cached->prepend( *it ); 01329 else 01330 XDestroyWindow( qt_xdisplay(), *it ); 01331 } 01332 } 01333 01334 01341 bool Workspace::setCurrentDesktop( int new_desktop ) 01342 { 01343 if (new_desktop < 1 || new_desktop > number_of_desktops ) 01344 return false; 01345 01346 closeActivePopup(); 01347 ++block_focus; 01348 // TODO Q_ASSERT( block_stacking_updates == 0 ); // make sure stacking_order is up to date 01349 StackingUpdatesBlocker blocker( this ); 01350 01351 int old_desktop = current_desktop; 01352 if (new_desktop != current_desktop) 01353 { 01354 ++block_showing_desktop; 01355 /* 01356 optimized Desktop switching: unmapping done from back to front 01357 mapping done from front to back => less exposure events 01358 */ 01359 Notify::raise((Notify::Event) (Notify::DesktopChange+new_desktop)); 01360 01361 ObscuringWindows obs_wins; 01362 01363 current_desktop = new_desktop; // change the desktop (so that Client::updateVisibility() works) 01364 01365 bool desktopHasCompositing = kapp->isCompositionManagerAvailable(); // Technically I should call isX11CompositionAvailable(), but it isn't initialized via my kapp constructir, and in this case it doesn't really matter anyway.... 01366 if (!desktopHasCompositing) { 01367 // If composition is not in use then we can hide the old windows before showing the new ones 01368 for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it) { 01369 if ( !(*it)->isOnDesktop( new_desktop ) && (*it) != movingClient ) 01370 { 01371 if( (*it)->isShown( true ) && (*it)->isOnDesktop( old_desktop )) { 01372 obs_wins.create( *it ); 01373 } 01374 (*it)->updateVisibility(); 01375 } 01376 } 01377 } 01378 01379 rootInfo->setCurrentDesktop( current_desktop ); // now propagate the change, after hiding, before showing 01380 01381 if( movingClient && !movingClient->isOnDesktop( new_desktop )) 01382 movingClient->setDesktop( new_desktop ); 01383 01384 for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) { 01385 if ( (*it)->isOnDesktop( new_desktop ) ) { 01386 (*it)->updateVisibility(); 01387 } 01388 } 01389 01390 if (desktopHasCompositing) { 01391 // If composition is in use then we cannot hide the old windows before showing the new ones, 01392 // unless you happen to like the "flicker annoyingly to desktop" effect... :-P 01393 XSync( qt_xdisplay(), false); // Make absolutely certain all new windows are shown before hiding the old ones 01394 for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it) { 01395 if ( !(*it)->isOnDesktop( new_desktop ) && (*it) != movingClient ) 01396 { 01397 if( (*it)->isShown( true ) && (*it)->isOnDesktop( old_desktop )) { 01398 obs_wins.create( *it ); 01399 } 01400 (*it)->updateVisibility(); 01401 } 01402 } 01403 } 01404 01405 --block_showing_desktop; 01406 if( showingDesktop()) // do this only after desktop change to avoid flicker 01407 resetShowingDesktop( false ); 01408 } 01409 01410 // restore the focus on this desktop 01411 --block_focus; 01412 Client* c = 0; 01413 01414 if ( options->focusPolicyIsReasonable()) 01415 { 01416 // Search in focus chain 01417 if ( movingClient != NULL && active_client == movingClient 01418 && focus_chain[currentDesktop()].contains( active_client ) 01419 && active_client->isShown( true ) && active_client->isOnCurrentDesktop()) 01420 { 01421 c = active_client; // the requestFocus below will fail, as the client is already active 01422 } 01423 if ( !c ) 01424 { 01425 for( ClientList::ConstIterator it = focus_chain[currentDesktop()].fromLast(); 01426 it != focus_chain[currentDesktop()].end(); 01427 --it ) 01428 { 01429 if ( (*it)->isShown( false ) && (*it)->isOnCurrentDesktop()) 01430 { 01431 c = *it; 01432 break; 01433 } 01434 } 01435 } 01436 } 01437 01438 //if "unreasonable focus policy" 01439 // and active_client is on_all_desktops and under mouse (hence == old_active_client), 01440 // conserve focus (thanks to Volker Schatz <V.Schatz at thphys.uni-heidelberg.de>) 01441 else if( active_client && active_client->isShown( true ) && active_client->isOnCurrentDesktop()) 01442 c = active_client; 01443 01444 if( c == NULL && !desktops.isEmpty()) 01445 c = findDesktop( true, currentDesktop()); 01446 01447 if( c != active_client ) 01448 setActiveClient( NULL, Allowed ); 01449 01450 if ( c ) 01451 requestFocus( c ); 01452 else 01453 focusToNull(); 01454 01455 updateCurrentTopMenu(); 01456 01457 // Update focus chain: 01458 // If input: chain = { 1, 2, 3, 4 } and current_desktop = 3, 01459 // Output: chain = { 3, 1, 2, 4 }. 01460 // kdDebug(1212) << TQString("Switching to desktop #%1, at focus_chain[currentDesktop()] index %2\n") 01461 // .arg(currentDesktop()).arg(desktop_focus_chain.find( currentDesktop() )); 01462 for( int i = desktop_focus_chain.find( currentDesktop() ); i > 0; i-- ) 01463 desktop_focus_chain[i] = desktop_focus_chain[i-1]; 01464 desktop_focus_chain[0] = currentDesktop(); 01465 01466 // TQString s = "desktop_focus_chain[] = { "; 01467 // for( uint i = 0; i < desktop_focus_chain.size(); i++ ) 01468 // s += TQString::number(desktop_focus_chain[i]) + ", "; 01469 // kdDebug(1212) << s << "}\n"; 01470 01471 if( old_desktop != 0 ) // not for the very first time 01472 popupinfo->showInfo( desktopName(currentDesktop()) ); 01473 return true; 01474 } 01475 01476 // called only from DCOP 01477 void Workspace::nextDesktop() 01478 { 01479 int desktop = currentDesktop() + 1; 01480 setCurrentDesktop(desktop > numberOfDesktops() ? 1 : desktop); 01481 } 01482 01483 // called only from DCOP 01484 void Workspace::previousDesktop() 01485 { 01486 int desktop = currentDesktop() - 1; 01487 setCurrentDesktop(desktop > 0 ? desktop : numberOfDesktops()); 01488 } 01489 01490 int Workspace::desktopToRight( int desktop ) const 01491 { 01492 int x,y; 01493 calcDesktopLayout(x,y); 01494 int dt = desktop-1; 01495 if (layoutOrientation == Qt::Vertical) 01496 { 01497 dt += y; 01498 if ( dt >= numberOfDesktops() ) 01499 { 01500 if ( options->rollOverDesktops ) 01501 dt -= numberOfDesktops(); 01502 else 01503 return desktop; 01504 } 01505 } 01506 else 01507 { 01508 int d = (dt % x) + 1; 01509 if ( d >= x ) 01510 { 01511 if ( options->rollOverDesktops ) 01512 d -= x; 01513 else 01514 return desktop; 01515 } 01516 dt = dt - (dt % x) + d; 01517 } 01518 return dt+1; 01519 } 01520 01521 int Workspace::desktopToLeft( int desktop ) const 01522 { 01523 int x,y; 01524 calcDesktopLayout(x,y); 01525 int dt = desktop-1; 01526 if (layoutOrientation == Qt::Vertical) 01527 { 01528 dt -= y; 01529 if ( dt < 0 ) 01530 { 01531 if ( options->rollOverDesktops ) 01532 dt += numberOfDesktops(); 01533 else 01534 return desktop; 01535 } 01536 } 01537 else 01538 { 01539 int d = (dt % x) - 1; 01540 if ( d < 0 ) 01541 { 01542 if ( options->rollOverDesktops ) 01543 d += x; 01544 else 01545 return desktop; 01546 } 01547 dt = dt - (dt % x) + d; 01548 } 01549 return dt+1; 01550 } 01551 01552 int Workspace::desktopUp( int desktop ) const 01553 { 01554 int x,y; 01555 calcDesktopLayout(x,y); 01556 int dt = desktop-1; 01557 if (layoutOrientation == Qt::Horizontal) 01558 { 01559 dt -= x; 01560 if ( dt < 0 ) 01561 { 01562 if ( options->rollOverDesktops ) 01563 dt += numberOfDesktops(); 01564 else 01565 return desktop; 01566 } 01567 } 01568 else 01569 { 01570 int d = (dt % y) - 1; 01571 if ( d < 0 ) 01572 { 01573 if ( options->rollOverDesktops ) 01574 d += y; 01575 else 01576 return desktop; 01577 } 01578 dt = dt - (dt % y) + d; 01579 } 01580 return dt+1; 01581 } 01582 01583 int Workspace::desktopDown( int desktop ) const 01584 { 01585 int x,y; 01586 calcDesktopLayout(x,y); 01587 int dt = desktop-1; 01588 if (layoutOrientation == Qt::Horizontal) 01589 { 01590 dt += x; 01591 if ( dt >= numberOfDesktops() ) 01592 { 01593 if ( options->rollOverDesktops ) 01594 dt -= numberOfDesktops(); 01595 else 01596 return desktop; 01597 } 01598 } 01599 else 01600 { 01601 int d = (dt % y) + 1; 01602 if ( d >= y ) 01603 { 01604 if ( options->rollOverDesktops ) 01605 d -= y; 01606 else 01607 return desktop; 01608 } 01609 dt = dt - (dt % y) + d; 01610 } 01611 return dt+1; 01612 } 01613 01614 01618 void Workspace::setNumberOfDesktops( int n ) 01619 { 01620 if ( n == number_of_desktops ) 01621 return; 01622 int old_number_of_desktops = number_of_desktops; 01623 number_of_desktops = n; 01624 01625 if( currentDesktop() > numberOfDesktops()) 01626 setCurrentDesktop( numberOfDesktops()); 01627 01628 // if increasing the number, do the resizing now, 01629 // otherwise after the moving of windows to still existing desktops 01630 if( old_number_of_desktops < number_of_desktops ) 01631 { 01632 rootInfo->setNumberOfDesktops( number_of_desktops ); 01633 NETPoint* viewports = new NETPoint[ number_of_desktops ]; 01634 rootInfo->setDesktopViewport( number_of_desktops, *viewports ); 01635 delete[] viewports; 01636 updateClientArea( true ); 01637 focus_chain.resize( number_of_desktops + 1 ); 01638 } 01639 01640 // if the number of desktops decreased, move all 01641 // windows that would be hidden to the last visible desktop 01642 if( old_number_of_desktops > number_of_desktops ) 01643 { 01644 for( ClientList::ConstIterator it = clients.begin(); 01645 it != clients.end(); 01646 ++it) 01647 { 01648 if( !(*it)->isOnAllDesktops() && (*it)->desktop() > numberOfDesktops()) 01649 sendClientToDesktop( *it, numberOfDesktops(), true ); 01650 } 01651 } 01652 if( old_number_of_desktops > number_of_desktops ) 01653 { 01654 rootInfo->setNumberOfDesktops( number_of_desktops ); 01655 NETPoint* viewports = new NETPoint[ number_of_desktops ]; 01656 rootInfo->setDesktopViewport( number_of_desktops, *viewports ); 01657 delete[] viewports; 01658 updateClientArea( true ); 01659 focus_chain.resize( number_of_desktops + 1 ); 01660 } 01661 01662 saveDesktopSettings(); 01663 01664 // Resize and reset the desktop focus chain. 01665 desktop_focus_chain.resize( n ); 01666 for( int i = 0; i < (int)desktop_focus_chain.size(); i++ ) 01667 desktop_focus_chain[i] = i+1; 01668 } 01669 01675 void Workspace::sendClientToDesktop( Client* c, int desk, bool dont_activate ) 01676 { 01677 bool was_on_desktop = c->isOnDesktop( desk ) || c->isOnAllDesktops(); 01678 c->setDesktop( desk ); 01679 if ( c->desktop() != desk ) // no change or desktop forced 01680 return; 01681 desk = c->desktop(); // Client did range checking 01682 01683 if ( c->isOnDesktop( currentDesktop() ) ) 01684 { 01685 if ( c->wantsTabFocus() && options->focusPolicyIsReasonable() 01686 && !was_on_desktop // for stickyness changes 01687 && !dont_activate ) 01688 requestFocus( c ); 01689 else 01690 restackClientUnderActive( c ); 01691 } 01692 else 01693 { 01694 raiseClient( c ); 01695 } 01696 01697 ClientList transients_stacking_order = ensureStackingOrder( c->transients()); 01698 for( ClientList::ConstIterator it = transients_stacking_order.begin(); 01699 it != transients_stacking_order.end(); 01700 ++it ) 01701 sendClientToDesktop( *it, desk, dont_activate ); 01702 updateClientArea(); 01703 } 01704 01705 int Workspace::numScreens() const 01706 { 01707 if( !options->xineramaEnabled ) 01708 return 0; 01709 return tqApp->desktop()->numScreens(); 01710 } 01711 01712 int Workspace::activeScreen() const 01713 { 01714 if( !options->xineramaEnabled ) 01715 return 0; 01716 if( !options->activeMouseScreen ) 01717 { 01718 if( activeClient() != NULL && !activeClient()->isOnScreen( active_screen )) 01719 return tqApp->desktop()->screenNumber( activeClient()->geometry().center()); 01720 return active_screen; 01721 } 01722 return tqApp->desktop()->screenNumber( TQCursor::pos()); 01723 } 01724 01725 // check whether a client moved completely out of what's considered the active screen, 01726 // if yes, set a new active screen 01727 void Workspace::checkActiveScreen( const Client* c ) 01728 { 01729 if( !options->xineramaEnabled ) 01730 return; 01731 if( !c->isActive()) 01732 return; 01733 if( !c->isOnScreen( active_screen )) 01734 active_screen = c->screen(); 01735 } 01736 01737 // called e.g. when a user clicks on a window, set active screen to be the screen 01738 // where the click occured 01739 void Workspace::setActiveScreenMouse( TQPoint mousepos ) 01740 { 01741 if( !options->xineramaEnabled ) 01742 return; 01743 active_screen = tqApp->desktop()->screenNumber( mousepos ); 01744 } 01745 01746 TQRect Workspace::screenGeometry( int screen ) const 01747 { 01748 if (( !options->xineramaEnabled ) || (kapp->desktop()->numScreens() < 2)) 01749 return tqApp->desktop()->geometry(); 01750 return tqApp->desktop()->screenGeometry( screen ); 01751 } 01752 01753 int Workspace::screenNumber( TQPoint pos ) const 01754 { 01755 if( !options->xineramaEnabled ) 01756 return 0; 01757 return tqApp->desktop()->screenNumber( pos ); 01758 } 01759 01760 void Workspace::sendClientToScreen( Client* c, int screen ) 01761 { 01762 if( c->screen() == screen ) // don't use isOnScreen(), that's true even when only partially 01763 return; 01764 GeometryUpdatesPostponer blocker( c ); 01765 TQRect old_sarea = clientArea( MaximizeArea, c ); 01766 TQRect sarea = clientArea( MaximizeArea, screen, c->desktop()); 01767 c->setGeometry( sarea.x() - old_sarea.x() + c->x(), sarea.y() - old_sarea.y() + c->y(), 01768 c->size().width(), c->size().height()); 01769 c->checkWorkspacePosition(); 01770 ClientList transients_stacking_order = ensureStackingOrder( c->transients()); 01771 for( ClientList::ConstIterator it = transients_stacking_order.begin(); 01772 it != transients_stacking_order.end(); 01773 ++it ) 01774 sendClientToScreen( *it, screen ); 01775 if( c->isActive()) 01776 active_screen = screen; 01777 } 01778 01779 01780 void Workspace::setDesktopLayout( int, int, int ) 01781 { // DCOP-only, unused 01782 } 01783 01784 void Workspace::updateDesktopLayout() 01785 { 01786 // rootInfo->desktopLayoutCorner(); // I don't find this worth bothering, feel free to 01787 layoutOrientation = ( rootInfo->desktopLayoutOrientation() == NET::OrientationHorizontal 01788 ? Qt::Horizontal : Qt::Vertical ); 01789 layoutX = rootInfo->desktopLayoutColumnsRows().width(); 01790 layoutY = rootInfo->desktopLayoutColumnsRows().height(); 01791 if( layoutX == 0 && layoutY == 0 ) // not given, set default layout 01792 layoutY = 2; 01793 } 01794 01795 void Workspace::calcDesktopLayout(int &x, int &y) const 01796 { 01797 x = layoutX; // <= 0 means compute it from the other and total number of desktops 01798 y = layoutY; 01799 if((x <= 0) && (y > 0)) 01800 x = (numberOfDesktops()+y-1) / y; 01801 else if((y <=0) && (x > 0)) 01802 y = (numberOfDesktops()+x-1) / x; 01803 01804 if(x <=0) 01805 x = 1; 01806 if (y <= 0) 01807 y = 1; 01808 } 01809 01814 bool Workspace::addSystemTrayWin( WId w ) 01815 { 01816 if ( systemTrayWins.contains( w ) ) 01817 return TRUE; 01818 01819 NETWinInfo ni( qt_xdisplay(), w, root, NET::WMKDESystemTrayWinFor ); 01820 WId trayWinFor = ni.kdeSystemTrayWinFor(); 01821 if ( !trayWinFor ) 01822 return FALSE; 01823 systemTrayWins.append( SystemTrayWindow( w, trayWinFor ) ); 01824 XSelectInput( qt_xdisplay(), w, 01825 StructureNotifyMask 01826 ); 01827 XAddToSaveSet( qt_xdisplay(), w ); 01828 propagateSystemTrayWins(); 01829 return TRUE; 01830 } 01831 01836 bool Workspace::removeSystemTrayWin( WId w, bool check ) 01837 { 01838 if ( !systemTrayWins.contains( w ) ) 01839 return FALSE; 01840 if( check ) 01841 { 01842 // When getting UnmapNotify, it's not clear if it's the systray 01843 // reparenting the window into itself, or if it's the window 01844 // going away. This is obviously a flaw in the design, and we were 01845 // just lucky it worked for so long. Kicker's systray temporarily 01846 // sets _KDE_SYSTEM_TRAY_EMBEDDING property on the window while 01847 // embedding it, allowing KWin to figure out. Kicker just mustn't 01848 // crash before removing it again ... *shrug* . 01849 int num_props; 01850 Atom* props = XListProperties( qt_xdisplay(), w, &num_props ); 01851 if( props != NULL ) 01852 { 01853 for( int i = 0; 01854 i < num_props; 01855 ++i ) 01856 if( props[ i ] == atoms->kde_system_tray_embedding ) 01857 { 01858 XFree( props ); 01859 return false; 01860 } 01861 XFree( props ); 01862 } 01863 } 01864 systemTrayWins.remove( w ); 01865 XRemoveFromSaveSet (qt_xdisplay (), w); 01866 propagateSystemTrayWins(); 01867 return TRUE; 01868 } 01869 01870 01874 void Workspace::propagateSystemTrayWins() 01875 { 01876 Window *cl = new Window[ systemTrayWins.count()]; 01877 01878 int i = 0; 01879 for ( SystemTrayWindowList::ConstIterator it = systemTrayWins.begin(); it != systemTrayWins.end(); ++it ) 01880 { 01881 cl[i++] = (*it).win; 01882 } 01883 01884 rootInfo->setKDESystemTrayWindows( cl, i ); 01885 delete [] cl; 01886 } 01887 01888 01889 void Workspace::killWindowId( Window window_to_kill ) 01890 { 01891 if( window_to_kill == None ) 01892 return; 01893 Window window = window_to_kill; 01894 Client* client = NULL; 01895 for(;;) 01896 { 01897 client = findClient( FrameIdMatchPredicate( window )); 01898 if( client != NULL ) // found the client 01899 break; 01900 Window parent, root; 01901 Window* children; 01902 unsigned int children_count; 01903 XQueryTree( qt_xdisplay(), window, &root, &parent, &children, &children_count ); 01904 if( children != NULL ) 01905 XFree( children ); 01906 if( window == root ) // we didn't find the client, probably an override-redirect window 01907 break; 01908 window = parent; // go up 01909 } 01910 if( client != NULL ) 01911 client->killWindow(); 01912 else 01913 XKillClient( qt_xdisplay(), window_to_kill ); 01914 } 01915 01916 01917 void Workspace::sendPingToWindow( Window window, Time timestamp ) 01918 { 01919 rootInfo->sendPing( window, timestamp ); 01920 } 01921 01922 void Workspace::sendTakeActivity( Client* c, Time timestamp, long flags ) 01923 { 01924 rootInfo->takeActivity( c->window(), timestamp, flags ); 01925 pending_take_activity = c; 01926 } 01927 01928 01932 void Workspace::slotGrabWindow() 01933 { 01934 if ( active_client ) 01935 { 01936 TQPixmap snapshot = TQPixmap::grabWindow( active_client->frameId() ); 01937 01938 //No XShape - no work. 01939 if( Shape::available()) 01940 { 01941 //As the first step, get the mask from XShape. 01942 int count, order; 01943 XRectangle* rects = XShapeGetRectangles( qt_xdisplay(), active_client->frameId(), 01944 ShapeBounding, &count, &order); 01945 //The ShapeBounding region is the outermost shape of the window; 01946 //ShapeBounding - ShapeClipping is defined to be the border. 01947 //Since the border area is part of the window, we use bounding 01948 // to limit our work region 01949 if (rects) 01950 { 01951 //Create a TQRegion from the rectangles describing the bounding mask. 01952 TQRegion contents; 01953 for (int pos = 0; pos < count; pos++) 01954 contents += TQRegion(rects[pos].x, rects[pos].y, 01955 rects[pos].width, rects[pos].height); 01956 XFree(rects); 01957 01958 //Create the bounding box. 01959 TQRegion bbox(0, 0, snapshot.width(), snapshot.height()); 01960 01961 //Get the masked away area. 01962 TQRegion maskedAway = bbox - contents; 01963 TQMemArray<TQRect> maskedAwayRects = maskedAway.rects(); 01964 01965 //Construct a bitmap mask from the rectangles 01966 TQBitmap mask( snapshot.width(), snapshot.height()); 01967 TQPainter p(&mask); 01968 p.fillRect(0, 0, mask.width(), mask.height(), Qt::color1); 01969 for (uint pos = 0; pos < maskedAwayRects.count(); pos++) 01970 p.fillRect(maskedAwayRects[pos], Qt::color0); 01971 p.end(); 01972 snapshot.setMask(mask); 01973 } 01974 } 01975 01976 TQClipboard *cb = TQApplication::clipboard(); 01977 cb->setPixmap( snapshot ); 01978 } 01979 else 01980 slotGrabDesktop(); 01981 } 01982 01986 void Workspace::slotGrabDesktop() 01987 { 01988 TQPixmap p = TQPixmap::grabWindow( qt_xrootwin() ); 01989 TQClipboard *cb = TQApplication::clipboard(); 01990 cb->setPixmap( p ); 01991 } 01992 01993 01997 void Workspace::slotMouseEmulation() 01998 { 01999 02000 if ( mouse_emulation ) 02001 { 02002 XUngrabKeyboard(qt_xdisplay(), GET_QT_X_TIME()); 02003 mouse_emulation = FALSE; 02004 return; 02005 } 02006 02007 if ( XGrabKeyboard(qt_xdisplay(), 02008 root, FALSE, 02009 GrabModeAsync, GrabModeAsync, 02010 GET_QT_X_TIME()) == GrabSuccess ) 02011 { 02012 mouse_emulation = TRUE; 02013 mouse_emulation_state = 0; 02014 mouse_emulation_window = 0; 02015 } 02016 } 02017 02024 WId Workspace::getMouseEmulationWindow() 02025 { 02026 Window root; 02027 Window child = qt_xrootwin(); 02028 int root_x, root_y, lx, ly; 02029 uint state; 02030 Window w; 02031 Client * c = 0; 02032 do 02033 { 02034 w = child; 02035 if (!c) 02036 c = findClient( FrameIdMatchPredicate( w )); 02037 XQueryPointer( qt_xdisplay(), w, &root, &child, 02038 &root_x, &root_y, &lx, &ly, &state ); 02039 } while ( child != None && child != w ); 02040 02041 if ( c && !c->isActive() ) 02042 activateClient( c ); 02043 return (WId) w; 02044 } 02045 02049 unsigned int Workspace::sendFakedMouseEvent( TQPoint pos, WId w, MouseEmulation type, int button, unsigned int state ) 02050 { 02051 if ( !w ) 02052 return state; 02053 TQWidget* widget = TQWidget::find( w ); 02054 if ( (!widget || widget->inherits(TQTOOLBUTTON_OBJECT_NAME_STRING) ) && !findClient( WindowMatchPredicate( w )) ) 02055 { 02056 int x, y; 02057 Window xw; 02058 XTranslateCoordinates( qt_xdisplay(), qt_xrootwin(), w, pos.x(), pos.y(), &x, &y, &xw ); 02059 if ( type == EmuMove ) 02060 { // motion notify events 02061 XEvent e; 02062 e.type = MotionNotify; 02063 e.xmotion.window = w; 02064 e.xmotion.root = qt_xrootwin(); 02065 e.xmotion.subwindow = w; 02066 e.xmotion.time = GET_QT_X_TIME(); 02067 e.xmotion.x = x; 02068 e.xmotion.y = y; 02069 e.xmotion.x_root = pos.x(); 02070 e.xmotion.y_root = pos.y(); 02071 e.xmotion.state = state; 02072 e.xmotion.is_hint = NotifyNormal; 02073 XSendEvent( qt_xdisplay(), w, TRUE, ButtonMotionMask, &e ); 02074 } 02075 else 02076 { 02077 XEvent e; 02078 e.type = type == EmuRelease ? ButtonRelease : ButtonPress; 02079 e.xbutton.window = w; 02080 e.xbutton.root = qt_xrootwin(); 02081 e.xbutton.subwindow = w; 02082 e.xbutton.time = GET_QT_X_TIME(); 02083 e.xbutton.x = x; 02084 e.xbutton.y = y; 02085 e.xbutton.x_root = pos.x(); 02086 e.xbutton.y_root = pos.y(); 02087 e.xbutton.state = state; 02088 e.xbutton.button = button; 02089 XSendEvent( qt_xdisplay(), w, TRUE, ButtonPressMask, &e ); 02090 02091 if ( type == EmuPress ) 02092 { 02093 switch ( button ) 02094 { 02095 case 2: 02096 state |= Button2Mask; 02097 break; 02098 case 3: 02099 state |= Button3Mask; 02100 break; 02101 default: // 1 02102 state |= Button1Mask; 02103 break; 02104 } 02105 } 02106 else 02107 { 02108 switch ( button ) 02109 { 02110 case 2: 02111 state &= ~Button2Mask; 02112 break; 02113 case 3: 02114 state &= ~Button3Mask; 02115 break; 02116 default: // 1 02117 state &= ~Button1Mask; 02118 break; 02119 } 02120 } 02121 } 02122 } 02123 return state; 02124 } 02125 02129 bool Workspace::keyPressMouseEmulation( XKeyEvent& ev ) 02130 { 02131 if ( root != qt_xrootwin() ) 02132 return FALSE; 02133 int kc = XKeycodeToKeysym(qt_xdisplay(), ev.keycode, 0); 02134 int km = ev.state & (ControlMask | Mod1Mask | ShiftMask); 02135 02136 bool is_control = km & ControlMask; 02137 bool is_alt = km & Mod1Mask; 02138 bool is_shift = km & ShiftMask; 02139 int delta = is_control?1:is_alt?32:8; 02140 TQPoint pos = TQCursor::pos(); 02141 02142 switch ( kc ) 02143 { 02144 case XK_Left: 02145 case XK_KP_Left: 02146 pos.rx() -= delta; 02147 break; 02148 case XK_Right: 02149 case XK_KP_Right: 02150 pos.rx() += delta; 02151 break; 02152 case XK_Up: 02153 case XK_KP_Up: 02154 pos.ry() -= delta; 02155 break; 02156 case XK_Down: 02157 case XK_KP_Down: 02158 pos.ry() += delta; 02159 break; 02160 case XK_F1: 02161 if ( !mouse_emulation_state ) 02162 mouse_emulation_window = getMouseEmulationWindow(); 02163 if ( (mouse_emulation_state & Button1Mask) == 0 ) 02164 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button1, mouse_emulation_state ); 02165 if ( !is_shift ) 02166 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state ); 02167 break; 02168 case XK_F2: 02169 if ( !mouse_emulation_state ) 02170 mouse_emulation_window = getMouseEmulationWindow(); 02171 if ( (mouse_emulation_state & Button2Mask) == 0 ) 02172 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button2, mouse_emulation_state ); 02173 if ( !is_shift ) 02174 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button2, mouse_emulation_state ); 02175 break; 02176 case XK_F3: 02177 if ( !mouse_emulation_state ) 02178 mouse_emulation_window = getMouseEmulationWindow(); 02179 if ( (mouse_emulation_state & Button3Mask) == 0 ) 02180 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button3, mouse_emulation_state ); 02181 if ( !is_shift ) 02182 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button3, mouse_emulation_state ); 02183 break; 02184 case XK_Return: 02185 case XK_space: 02186 case XK_KP_Enter: 02187 case XK_KP_Space: 02188 { 02189 if ( !mouse_emulation_state ) 02190 { 02191 // nothing was pressed, fake a LMB click 02192 mouse_emulation_window = getMouseEmulationWindow(); 02193 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button1, mouse_emulation_state ); 02194 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state ); 02195 } 02196 else 02197 { // release all 02198 if ( mouse_emulation_state & Button1Mask ) 02199 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state ); 02200 if ( mouse_emulation_state & Button2Mask ) 02201 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button2, mouse_emulation_state ); 02202 if ( mouse_emulation_state & Button3Mask ) 02203 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button3, mouse_emulation_state ); 02204 } 02205 } 02206 // fall through 02207 case XK_Escape: 02208 XUngrabKeyboard(qt_xdisplay(), GET_QT_X_TIME()); 02209 mouse_emulation = FALSE; 02210 return TRUE; 02211 default: 02212 return FALSE; 02213 } 02214 02215 TQCursor::setPos( pos ); 02216 if ( mouse_emulation_state ) 02217 mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuMove, 0, mouse_emulation_state ); 02218 return TRUE; 02219 02220 } 02221 02227 TQWidget* Workspace::desktopWidget() 02228 { 02229 return desktop_widget; 02230 } 02231 02232 //Delayed focus functions 02233 void Workspace::delayFocus() 02234 { 02235 requestFocus( delayfocus_client ); 02236 cancelDelayFocus(); 02237 } 02238 02239 void Workspace::requestDelayFocus( Client* c ) 02240 { 02241 delayfocus_client = c; 02242 delete delayFocusTimer; 02243 delayFocusTimer = new TQTimer( this ); 02244 connect( delayFocusTimer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( delayFocus() ) ); 02245 delayFocusTimer->start( options->delayFocusInterval, TRUE ); 02246 } 02247 02248 void Workspace::cancelDelayFocus() 02249 { 02250 delete delayFocusTimer; 02251 delayFocusTimer = 0; 02252 } 02253 02254 // Electric Borders 02255 //========================================================================// 02256 // Electric Border Window management. Electric borders allow a user 02257 // to change the virtual desktop by moving the mouse pointer to the 02258 // borders. Technically this is done with input only windows. Since 02259 // electric borders can be switched on and off, we have these two 02260 // functions to create and destroy them. 02261 void Workspace::checkElectricBorders( bool force ) 02262 { 02263 if( force ) 02264 destroyBorderWindows(); 02265 02266 electric_current_border = 0; 02267 02268 TQRect r = TQApplication::desktop()->geometry(); 02269 electricTop = r.top(); 02270 electricBottom = r.bottom(); 02271 electricLeft = r.left(); 02272 electricRight = r.right(); 02273 02274 if (options->electricBorders() == Options::ElectricAlways) 02275 createBorderWindows(); 02276 else 02277 destroyBorderWindows(); 02278 } 02279 02280 void Workspace::createBorderWindows() 02281 { 02282 if ( electric_have_borders ) 02283 return; 02284 02285 electric_have_borders = true; 02286 02287 TQRect r = TQApplication::desktop()->geometry(); 02288 XSetWindowAttributes attributes; 02289 unsigned long valuemask; 02290 attributes.override_redirect = True; 02291 attributes.event_mask = ( EnterWindowMask | LeaveWindowMask ); 02292 valuemask= (CWOverrideRedirect | CWEventMask | CWCursor ); 02293 attributes.cursor = XCreateFontCursor(qt_xdisplay(), 02294 XC_sb_up_arrow); 02295 electric_top_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(), 02296 0,0, 02297 r.width(),1, 02298 0, 02299 CopyFromParent, InputOnly, 02300 CopyFromParent, 02301 valuemask, &attributes); 02302 XMapWindow(qt_xdisplay(), electric_top_border); 02303 02304 attributes.cursor = XCreateFontCursor(qt_xdisplay(), 02305 XC_sb_down_arrow); 02306 electric_bottom_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(), 02307 0,r.height()-1, 02308 r.width(),1, 02309 0, 02310 CopyFromParent, InputOnly, 02311 CopyFromParent, 02312 valuemask, &attributes); 02313 XMapWindow(qt_xdisplay(), electric_bottom_border); 02314 02315 attributes.cursor = XCreateFontCursor(qt_xdisplay(), 02316 XC_sb_left_arrow); 02317 electric_left_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(), 02318 0,0, 02319 1,r.height(), 02320 0, 02321 CopyFromParent, InputOnly, 02322 CopyFromParent, 02323 valuemask, &attributes); 02324 XMapWindow(qt_xdisplay(), electric_left_border); 02325 02326 attributes.cursor = XCreateFontCursor(qt_xdisplay(), 02327 XC_sb_right_arrow); 02328 electric_right_border = XCreateWindow (qt_xdisplay(), qt_xrootwin(), 02329 r.width()-1,0, 02330 1,r.height(), 02331 0, 02332 CopyFromParent, InputOnly, 02333 CopyFromParent, 02334 valuemask, &attributes); 02335 XMapWindow(qt_xdisplay(), electric_right_border); 02336 // Set XdndAware on the windows, so that DND enter events are received (#86998) 02337 Atom version = 4; // XDND version 02338 XChangeProperty( qt_xdisplay(), electric_top_border, atoms->xdnd_aware, XA_ATOM, 02339 32, PropModeReplace, ( unsigned char* )&version, 1 ); 02340 XChangeProperty( qt_xdisplay(), electric_bottom_border, atoms->xdnd_aware, XA_ATOM, 02341 32, PropModeReplace, ( unsigned char* )&version, 1 ); 02342 XChangeProperty( qt_xdisplay(), electric_left_border, atoms->xdnd_aware, XA_ATOM, 02343 32, PropModeReplace, ( unsigned char* )&version, 1 ); 02344 XChangeProperty( qt_xdisplay(), electric_right_border, atoms->xdnd_aware, XA_ATOM, 02345 32, PropModeReplace, ( unsigned char* )&version, 1 ); 02346 } 02347 02348 02349 // Electric Border Window management. Electric borders allow a user 02350 // to change the virtual desktop by moving the mouse pointer to the 02351 // borders. Technically this is done with input only windows. Since 02352 // electric borders can be switched on and off, we have these two 02353 // functions to create and destroy them. 02354 void Workspace::destroyBorderWindows() 02355 { 02356 if( !electric_have_borders) 02357 return; 02358 02359 electric_have_borders = false; 02360 02361 if(electric_top_border) 02362 XDestroyWindow(qt_xdisplay(),electric_top_border); 02363 if(electric_bottom_border) 02364 XDestroyWindow(qt_xdisplay(),electric_bottom_border); 02365 if(electric_left_border) 02366 XDestroyWindow(qt_xdisplay(),electric_left_border); 02367 if(electric_right_border) 02368 XDestroyWindow(qt_xdisplay(),electric_right_border); 02369 02370 electric_top_border = None; 02371 electric_bottom_border = None; 02372 electric_left_border = None; 02373 electric_right_border = None; 02374 } 02375 02376 void Workspace::clientMoved(const TQPoint &pos, Time now) 02377 { 02378 if (options->electricBorders() == Options::ElectricDisabled) 02379 return; 02380 02381 if ((pos.x() != electricLeft) && 02382 (pos.x() != electricRight) && 02383 (pos.y() != electricTop) && 02384 (pos.y() != electricBottom)) 02385 return; 02386 02387 Time treshold_set = options->electricBorderDelay(); // set timeout 02388 Time treshold_reset = 250; // reset timeout 02389 int distance_reset = 30; // Mouse should not move more than this many pixels 02390 02391 int border = 0; 02392 if (pos.x() == electricLeft) 02393 border = 1; 02394 else if (pos.x() == electricRight) 02395 border = 2; 02396 else if (pos.y() == electricTop) 02397 border = 3; 02398 else if (pos.y() == electricBottom) 02399 border = 4; 02400 02401 if ((electric_current_border == border) && 02402 (timestampDiff(electric_time_last, now) < treshold_reset) && 02403 ((pos-electric_push_point).manhattanLength() < distance_reset)) 02404 { 02405 electric_time_last = now; 02406 02407 if (timestampDiff(electric_time_first, now) > treshold_set) 02408 { 02409 electric_current_border = 0; 02410 02411 TQRect r = TQApplication::desktop()->geometry(); 02412 int offset; 02413 02414 int desk_before = currentDesktop(); 02415 switch(border) 02416 { 02417 case 1: 02418 slotSwitchDesktopLeft(); 02419 if (currentDesktop() != desk_before) 02420 { 02421 offset = r.width() / 5; 02422 TQCursor::setPos(r.width() - offset, pos.y()); 02423 } 02424 break; 02425 02426 case 2: 02427 slotSwitchDesktopRight(); 02428 if (currentDesktop() != desk_before) 02429 { 02430 offset = r.width() / 5; 02431 TQCursor::setPos(offset, pos.y()); 02432 } 02433 break; 02434 02435 case 3: 02436 slotSwitchDesktopUp(); 02437 if (currentDesktop() != desk_before) 02438 { 02439 offset = r.height() / 5; 02440 TQCursor::setPos(pos.x(), r.height() - offset); 02441 } 02442 break; 02443 02444 case 4: 02445 slotSwitchDesktopDown(); 02446 if (currentDesktop() != desk_before) 02447 { 02448 offset = r.height() / 5; 02449 TQCursor::setPos(pos.x(), offset); 02450 } 02451 break; 02452 } 02453 return; 02454 } 02455 } 02456 else 02457 { 02458 electric_current_border = border; 02459 electric_time_first = now; 02460 electric_time_last = now; 02461 electric_push_point = pos; 02462 } 02463 02464 int mouse_warp = 1; 02465 02466 // reset the pointer to find out wether the user is really pushing 02467 switch( border) 02468 { 02469 case 1: TQCursor::setPos(pos.x()+mouse_warp, pos.y()); break; 02470 case 2: TQCursor::setPos(pos.x()-mouse_warp, pos.y()); break; 02471 case 3: TQCursor::setPos(pos.x(), pos.y()+mouse_warp); break; 02472 case 4: TQCursor::setPos(pos.x(), pos.y()-mouse_warp); break; 02473 } 02474 } 02475 02476 // this function is called when the user entered an electric border 02477 // with the mouse. It may switch to another virtual desktop 02478 bool Workspace::electricBorder(XEvent *e) 02479 { 02480 if( !electric_have_borders ) 02481 return false; 02482 if( e->type == EnterNotify ) 02483 { 02484 if( e->xcrossing.window == electric_top_border || 02485 e->xcrossing.window == electric_left_border || 02486 e->xcrossing.window == electric_bottom_border || 02487 e->xcrossing.window == electric_right_border) 02488 // the user entered an electric border 02489 { 02490 clientMoved( TQPoint( e->xcrossing.x_root, e->xcrossing.y_root ), e->xcrossing.time ); 02491 return true; 02492 } 02493 } 02494 if( e->type == ClientMessage ) 02495 { 02496 if( e->xclient.message_type == atoms->xdnd_position 02497 && ( e->xclient.window == electric_top_border 02498 || e->xclient.window == electric_bottom_border 02499 || e->xclient.window == electric_left_border 02500 || e->xclient.window == electric_right_border )) 02501 { 02502 updateXTime(); 02503 clientMoved( TQPoint( e->xclient.data.l[2]>>16, e->xclient.data.l[2]&0xffff), GET_QT_X_TIME() ); 02504 return true; 02505 } 02506 } 02507 return false; 02508 } 02509 02510 // electric borders (input only windows) have to be always on the 02511 // top. For that reason kwm calls this function always after some 02512 // windows have been raised. 02513 void Workspace::raiseElectricBorders() 02514 { 02515 02516 if(electric_have_borders) 02517 { 02518 XRaiseWindow(qt_xdisplay(), electric_top_border); 02519 XRaiseWindow(qt_xdisplay(), electric_left_border); 02520 XRaiseWindow(qt_xdisplay(), electric_bottom_border); 02521 XRaiseWindow(qt_xdisplay(), electric_right_border); 02522 } 02523 } 02524 02525 void Workspace::addTopMenu( Client* c ) 02526 { 02527 assert( c->isTopMenu()); 02528 assert( !topmenus.contains( c )); 02529 topmenus.append( c ); 02530 if( managingTopMenus()) 02531 { 02532 int minsize = c->minSize().height(); 02533 if( minsize > topMenuHeight()) 02534 { 02535 topmenu_height = minsize; 02536 updateTopMenuGeometry(); 02537 } 02538 updateTopMenuGeometry( c ); 02539 updateCurrentTopMenu(); 02540 } 02541 // kdDebug() << "NEW TOPMENU:" << c << endl; 02542 } 02543 02544 void Workspace::removeTopMenu( Client* c ) 02545 { 02546 // if( c->isTopMenu()) 02547 // kdDebug() << "REMOVE TOPMENU:" << c << endl; 02548 assert( c->isTopMenu()); 02549 assert( topmenus.contains( c )); 02550 topmenus.remove( c ); 02551 updateCurrentTopMenu(); 02552 // TODO reduce topMenuHeight() if possible? 02553 } 02554 02555 void Workspace::lostTopMenuSelection() 02556 { 02557 // kdDebug() << "lost TopMenu selection" << endl; 02558 // make sure this signal is always set when not owning the selection 02559 disconnect( topmenu_watcher, TQT_SIGNAL( lostOwner()), this, TQT_SLOT( lostTopMenuOwner())); 02560 connect( topmenu_watcher, TQT_SIGNAL( lostOwner()), this, TQT_SLOT( lostTopMenuOwner())); 02561 if( !managing_topmenus ) 02562 return; 02563 connect( topmenu_watcher, TQT_SIGNAL( lostOwner()), this, TQT_SLOT( lostTopMenuOwner())); 02564 disconnect( topmenu_selection, TQT_SIGNAL( lostOwnership()), this, TQT_SLOT( lostTopMenuSelection())); 02565 managing_topmenus = false; 02566 delete topmenu_space; 02567 topmenu_space = NULL; 02568 updateClientArea(); 02569 for( ClientList::ConstIterator it = topmenus.begin(); 02570 it != topmenus.end(); 02571 ++it ) 02572 (*it)->checkWorkspacePosition(); 02573 } 02574 02575 void Workspace::lostTopMenuOwner() 02576 { 02577 if( !options->topMenuEnabled()) 02578 return; 02579 // kdDebug() << "TopMenu selection lost owner" << endl; 02580 if( !topmenu_selection->claim( false )) 02581 { 02582 // kdDebug() << "Failed to claim TopMenu selection" << endl; 02583 return; 02584 } 02585 // kdDebug() << "claimed TopMenu selection" << endl; 02586 setupTopMenuHandling(); 02587 } 02588 02589 void Workspace::setupTopMenuHandling() 02590 { 02591 if( managing_topmenus ) 02592 return; 02593 connect( topmenu_selection, TQT_SIGNAL( lostOwnership()), this, TQT_SLOT( lostTopMenuSelection())); 02594 disconnect( topmenu_watcher, TQT_SIGNAL( lostOwner()), this, TQT_SLOT( lostTopMenuOwner())); 02595 managing_topmenus = true; 02596 topmenu_space = new TQWidget; 02597 Window stack[ 2 ]; 02598 stack[ 0 ] = supportWindow->winId(); 02599 stack[ 1 ] = topmenu_space->winId(); 02600 XRestackWindows(qt_xdisplay(), stack, 2); 02601 updateTopMenuGeometry(); 02602 topmenu_space->show(); 02603 updateClientArea(); 02604 updateCurrentTopMenu(); 02605 } 02606 02607 int Workspace::topMenuHeight() const 02608 { 02609 if( topmenu_height == 0 ) 02610 { // simply create a dummy menubar and use its preffered height as the menu height 02611 KMenuBar tmpmenu; 02612 tmpmenu.insertItem( "dummy" ); 02613 topmenu_height = tmpmenu.sizeHint().height(); 02614 } 02615 return topmenu_height; 02616 } 02617 02618 KDecoration* Workspace::createDecoration( KDecorationBridge* bridge ) 02619 { 02620 return mgr->createDecoration( bridge ); 02621 } 02622 02623 TQString Workspace::desktopName( int desk ) const 02624 { 02625 return TQString::fromUtf8( rootInfo->desktopName( desk ) ); 02626 } 02627 02628 bool Workspace::checkStartupNotification( Window w, KStartupInfoId& id, KStartupInfoData& data ) 02629 { 02630 return startup->checkStartup( w, id, data ) == KStartupInfo::Match; 02631 } 02632 02637 void Workspace::focusToNull() 02638 { 02639 XSetInputFocus(qt_xdisplay(), null_focus_window, RevertToPointerRoot, GET_QT_X_TIME() ); 02640 } 02641 02642 void Workspace::helperDialog( const TQString& message, const Client* c ) 02643 { 02644 TQStringList args; 02645 TQString type; 02646 if( message == "noborderaltf3" ) 02647 { 02648 TQString shortcut = TQString( "%1 (%2)" ).arg( keys->label( "Window Operations Menu" )) 02649 .arg( keys->shortcut( "Window Operations Menu" ).seq( 0 ).toString()); 02650 args << "--msgbox" << 02651 i18n( "You have selected to show a window without its border.\n" 02652 "Without the border, you will not be able to enable the border " 02653 "again using the mouse: use the window operations menu instead, " 02654 "activated using the %1 keyboard shortcut." ) 02655 .arg( shortcut ); 02656 type = "altf3warning"; 02657 } 02658 else if( message == "fullscreenaltf3" ) 02659 { 02660 TQString shortcut = TQString( "%1 (%2)" ).arg( keys->label( "Window Operations Menu" )) 02661 .arg( keys->shortcut( "Window Operations Menu" ).seq( 0 ).toString()); 02662 args << "--msgbox" << 02663 i18n( "You have selected to show a window in fullscreen mode.\n" 02664 "If the application itself does not have an option to turn the fullscreen " 02665 "mode off you will not be able to disable it " 02666 "again using the mouse: use the window operations menu instead, " 02667 "activated using the %1 keyboard shortcut." ) 02668 .arg( shortcut ); 02669 type = "altf3warning"; 02670 } 02671 else 02672 assert( false ); 02673 KProcess proc; 02674 proc << "kdialog" << args; 02675 if( !type.isEmpty()) 02676 { 02677 KConfig cfg( "kwin_dialogsrc" ); 02678 cfg.setGroup( "Notification Messages" ); // this depends on KMessageBox 02679 if( !cfg.readBoolEntry( type, true )) // has don't show again checked 02680 return; // save launching kdialog 02681 proc << "--dontagain" << "kwin_dialogsrc:" + type; 02682 } 02683 if( c != NULL ) 02684 proc << "--embed" << TQString::number( c->window()); 02685 proc.start( KProcess::DontCare ); 02686 } 02687 02688 02689 // kompmgr stuff 02690 02691 void Workspace::startKompmgr() 02692 { 02693 // See if the desktop is loaded yet 02694 Atom type; 02695 int format; 02696 unsigned long length, after; 02697 unsigned char* data_root; 02698 Atom prop_root; 02699 prop_root = XInternAtom(qt_xdisplay(), "_XROOTPMAP_ID", False); 02700 if( XGetWindowProperty( qt_xdisplay(), qt_xrootwin(), prop_root, 0L, 1L, False, AnyPropertyType, &type, &format, &length, &after, &data_root) == Success && data_root != NULL ) { 02701 // Root pixmap is available; OK to load... 02702 } 02703 else { 02704 // Try again a bit later! 02705 TQTimer::singleShot( 200, this, TQT_SLOT(startKompmgr()) ); 02706 return; 02707 } 02708 if (!kompmgr || kompmgr->isRunning()) 02709 return; 02710 if (!kompmgr->start(KProcess::OwnGroup, KProcess::Stderr)) 02711 { 02712 options->useTranslucency = FALSE; 02713 KProcess proc; 02714 proc << "kdialog" << "--error" 02715 << i18n("The Composite Manager could not be started.\\nMake sure you have \"kompmgr\" in a $PATH directory.") 02716 << "--title" << "Composite Manager Failure"; 02717 proc.start(KProcess::DontCare); 02718 } 02719 else 02720 { 02721 delete kompmgr_selection; 02722 char selection_name[ 100 ]; 02723 sprintf( selection_name, "_NET_WM_CM_S%d", DefaultScreen( qt_xdisplay())); 02724 kompmgr_selection = new KSelectionOwner( selection_name ); 02725 connect( kompmgr_selection, TQT_SIGNAL( lostOwnership()), TQT_SLOT( stopKompmgr())); 02726 kompmgr_selection->claim( true ); 02727 connect(kompmgr, TQT_SIGNAL(processExited(KProcess*)), TQT_SLOT(restartKompmgr(KProcess*))); 02728 options->useTranslucency = TRUE; 02729 //allowKompmgrRestart = FALSE; 02730 //TQTimer::singleShot( 60000, this, TQT_SLOT(unblockKompmgrRestart()) ); 02731 TQByteArray ba; 02732 TQDataStream arg(ba, IO_WriteOnly); 02733 arg << ""; 02734 kapp->dcopClient()->emitDCOPSignal("default", "kompmgrStarted()", ba); 02735 } 02736 if (popup){ delete popup; popup = 0L; } // to add/remove opacity slider 02737 } 02738 02739 void Workspace::stopKompmgr() 02740 { 02741 if (!kompmgr || !kompmgr->isRunning()) { 02742 return; 02743 } 02744 delete kompmgr_selection; 02745 kompmgr_selection = NULL; 02746 kompmgr->disconnect(this, TQT_SLOT(restartKompmgr(KProcess*))); 02747 options->useTranslucency = FALSE; 02748 if (popup){ delete popup; popup = 0L; } // to add/remove opacity slider 02749 kompmgr->kill(); 02750 TQByteArray ba; 02751 TQDataStream arg(ba, IO_WriteOnly); 02752 arg << ""; 02753 kapp->dcopClient()->emitDCOPSignal("default", "kompmgrStopped()", ba); 02754 } 02755 02756 bool Workspace::kompmgrIsRunning() 02757 { 02758 return kompmgr && kompmgr->isRunning(); 02759 } 02760 02761 void Workspace::unblockKompmgrRestart() 02762 { 02763 allowKompmgrRestart = TRUE; 02764 } 02765 02766 void Workspace::restartKompmgr( KProcess *proc ) 02767 // this is for inernal purpose (crashhandling) only, usually you want to use workspace->stopKompmgr(); TQTimer::singleShot(200, workspace, TQT_SLOT(startKompmgr())); 02768 { 02769 bool crashed; 02770 if (proc->signalled()) { // looks like kompmgr may have crashed 02771 int exit_signal_number = proc->exitSignal(); 02772 if ( (exit_signal_number == SIGILL) || (exit_signal_number == SIGTRAP) || (exit_signal_number == SIGABRT) || (exit_signal_number == SIGSYS) || (exit_signal_number == SIGFPE) || (exit_signal_number == SIGBUS) || (exit_signal_number == SIGSEGV) ) { 02773 crashed = true; 02774 } 02775 else { 02776 crashed = false; 02777 } 02778 if (!allowKompmgrRestart) // uh oh, it exited recently already 02779 { 02780 delete kompmgr_selection; 02781 kompmgr_selection = NULL; 02782 options->useTranslucency = FALSE; 02783 if (crashed) { 02784 KProcess proc; 02785 proc << "kdialog" << "--error" 02786 << i18n( "The Composite Manager crashed twice within a minute and is therefore disabled for this session.") 02787 << "--title" << i18n("Composite Manager Failure"); 02788 proc.start(KProcess::DontCare); 02789 } 02790 return; 02791 } 02792 if (!kompmgr) 02793 return; 02794 // this should be useless, i keep it for maybe future need 02795 // if (!kcompmgr) 02796 // { 02797 // kompmgr = new KProcess; 02798 // kompmgr->clearArguments(); 02799 // *kompmgr << "kompmgr"; 02800 // } 02801 // ------------------- 02802 if (!kompmgr->start(KProcess::NotifyOnExit, KProcess::Stderr)) 02803 { 02804 delete kompmgr_selection; 02805 kompmgr_selection = NULL; 02806 options->useTranslucency = FALSE; 02807 KProcess proc; 02808 proc << "kdialog" << "--error" 02809 << i18n("The Composite Manager could not be started.\\nMake sure you have \"kompmgr\" in a $PATH directory.") 02810 << "--title" << i18n("Composite Manager Failure"); 02811 proc.start(KProcess::DontCare); 02812 } 02813 else 02814 { 02815 allowKompmgrRestart = FALSE; 02816 TQTimer::singleShot( 60000, this, TQT_SLOT(unblockKompmgrRestart()) ); 02817 } 02818 } 02819 } 02820 02821 void Workspace::handleKompmgrOutput( KProcess* , char *buffer, int buflen) 02822 { 02823 TQString message; 02824 TQString output = TQString::fromLocal8Bit( buffer, buflen ); 02825 if (output.contains("Started",false)) 02826 ; // don't do anything, just pass to the connection release 02827 else if (output.contains("Can't open display",false)) 02828 message = i18n("<qt><b>kompmgr failed to open the display</b><br>There is probably an invalid display entry in your ~/.xcompmgrrc.</qt>"); 02829 else if (output.contains("No render extension",false)) 02830 message = i18n("<qt><b>kompmgr cannot find the Xrender extension</b><br>You are using either an outdated or a crippled version of XOrg.<br>Get XOrg ≥ 6.8 from www.freedesktop.org.<br></qt>"); 02831 else if (output.contains("No composite extension",false)) 02832 message = i18n("<qt><b>Composite extension not found</b><br>You <i>must</i> use XOrg ≥ 6.8 for translucency and shadows to work.<br>Additionally, you need to add a new section to your X config file:<br>" 02833 "<i>Section \"Extensions\"<br>" 02834 "Option \"Composite\" \"Enable\"<br>" 02835 "EndSection</i></qt>"); 02836 else if (output.contains("No damage extension",false)) 02837 message = i18n("<qt><b>Damage extension not found</b><br>You <i>must</i> use XOrg ≥ 6.8 for translucency and shadows to work.</qt>"); 02838 else if (output.contains("No XFixes extension",false)) 02839 message = i18n("<qt><b>XFixes extension not found</b><br>You <i>must</i> use XOrg ≥ 6.8 for translucency and shadows to work.</qt>"); 02840 else return; //skip others 02841 // kompmgr startup failed or succeeded, release connection 02842 kompmgr->closeStderr(); 02843 disconnect(kompmgr, TQT_SIGNAL(receivedStderr(KProcess*, char*, int)), this, TQT_SLOT(handleKompmgrOutput(KProcess*, char*, int))); 02844 if( !message.isEmpty()) 02845 { 02846 KProcess proc; 02847 proc << "kdialog" << "--error" 02848 << message 02849 << "--title" << i18n("Composite Manager Failure"); 02850 proc.start(KProcess::DontCare); 02851 } 02852 } 02853 02854 02855 void Workspace::setOpacity(unsigned long winId, unsigned int opacityPercent) 02856 { 02857 if (opacityPercent > 100) opacityPercent = 100; 02858 for( ClientList::ConstIterator it = stackingOrder().begin(); it != stackingOrder().end(); it++ ) 02859 if (winId == (*it)->window()) 02860 { 02861 (*it)->setOpacity(opacityPercent < 100, (unsigned int)((opacityPercent/100.0)*0xFFFFFFFF)); 02862 return; 02863 } 02864 } 02865 02866 void Workspace::setShadowSize(unsigned long winId, unsigned int shadowSizePercent) 02867 { 02868 //this is open to the user by dcop - to avoid stupid trials, we limit the max shadow size to 400% 02869 if (shadowSizePercent > 400) shadowSizePercent = 400; 02870 for( ClientList::ConstIterator it = stackingOrder().begin(); it != stackingOrder().end(); it++ ) 02871 if (winId == (*it)->window()) 02872 { 02873 (*it)->setShadowSize(shadowSizePercent); 02874 return; 02875 } 02876 } 02877 02878 void Workspace::setUnshadowed(unsigned long winId) 02879 { 02880 for( ClientList::ConstIterator it = stackingOrder().begin(); it != stackingOrder().end(); it++ ) 02881 if (winId == (*it)->window()) 02882 { 02883 (*it)->setShadowSize(0); 02884 return; 02885 } 02886 } 02887 02888 void Workspace::setShowingDesktop( bool showing ) 02889 { 02890 rootInfo->setShowingDesktop( showing ); 02891 showing_desktop = showing; 02892 ++block_showing_desktop; 02893 if( showing_desktop ) 02894 { 02895 showing_desktop_clients.clear(); 02896 ++block_focus; 02897 ClientList cls = stackingOrder(); 02898 // find them first, then minimize, otherwise transients may get minimized with the window 02899 // they're transient for 02900 for( ClientList::ConstIterator it = cls.begin(); 02901 it != cls.end(); 02902 ++it ) 02903 { 02904 if( (*it)->isOnCurrentDesktop() && (*it)->isShown( true ) && !(*it)->isSpecialWindow()) 02905 showing_desktop_clients.prepend( *it ); // topmost first to reduce flicker 02906 } 02907 for( ClientList::ConstIterator it = showing_desktop_clients.begin(); 02908 it != showing_desktop_clients.end(); 02909 ++it ) 02910 (*it)->minimize(true); 02911 --block_focus; 02912 if( Client* desk = findDesktop( true, currentDesktop())) 02913 requestFocus( desk ); 02914 } 02915 else 02916 { 02917 for( ClientList::ConstIterator it = showing_desktop_clients.begin(); 02918 it != showing_desktop_clients.end(); 02919 ++it ) 02920 (*it)->unminimize(true); 02921 if( showing_desktop_clients.count() > 0 ) 02922 requestFocus( showing_desktop_clients.first()); 02923 showing_desktop_clients.clear(); 02924 } 02925 --block_showing_desktop; 02926 } 02927 02928 // Following Kicker's behavior: 02929 // Changing a virtual desktop resets the state and shows the windows again. 02930 // Unminimizing a window resets the state but keeps the windows hidden (except 02931 // the one that was unminimized). 02932 // A new window resets the state and shows the windows again, with the new window 02933 // being active. Due to popular demand (#67406) by people who apparently 02934 // don't see a difference between "show desktop" and "minimize all", this is not 02935 // true if "showDesktopIsMinimizeAll" is set in kwinrc. In such case showing 02936 // a new window resets the state but doesn't show windows. 02937 void Workspace::resetShowingDesktop( bool keep_hidden ) 02938 { 02939 if( block_showing_desktop > 0 ) 02940 return; 02941 rootInfo->setShowingDesktop( false ); 02942 showing_desktop = false; 02943 ++block_showing_desktop; 02944 if( !keep_hidden ) 02945 { 02946 for( ClientList::ConstIterator it = showing_desktop_clients.begin(); 02947 it != showing_desktop_clients.end(); 02948 ++it ) 02949 (*it)->unminimize(true); 02950 } 02951 showing_desktop_clients.clear(); 02952 --block_showing_desktop; 02953 } 02954 02955 // Activating/deactivating this feature works like this: 02956 // When nothing is active, and the shortcut is pressed, global shortcuts are disabled 02957 // (using global_shortcuts_disabled) 02958 // When a window that has disabling forced is activated, global shortcuts are disabled. 02959 // (using global_shortcuts_disabled_for_client) 02960 // When a shortcut is pressed and global shortcuts are disabled (either by a shortcut 02961 // or for a client), they are enabled again. 02962 void Workspace::slotDisableGlobalShortcuts() 02963 { 02964 if( global_shortcuts_disabled || global_shortcuts_disabled_for_client ) 02965 disableGlobalShortcuts( false ); 02966 else 02967 disableGlobalShortcuts( true ); 02968 } 02969 02970 static bool pending_dfc = false; 02971 02972 void Workspace::disableGlobalShortcutsForClient( bool disable ) 02973 { 02974 if( global_shortcuts_disabled_for_client == disable ) 02975 return; 02976 if( !global_shortcuts_disabled ) 02977 { 02978 if( disable ) 02979 pending_dfc = true; 02980 KIPC::sendMessageAll( KIPC::BlockShortcuts, disable ); 02981 // kwin will get the kipc message too 02982 } 02983 } 02984 02985 void Workspace::disableGlobalShortcuts( bool disable ) 02986 { 02987 KIPC::sendMessageAll( KIPC::BlockShortcuts, disable ); 02988 // kwin will get the kipc message too 02989 } 02990 02991 void Workspace::kipcMessage( int id, int data ) 02992 { 02993 if( id != KIPC::BlockShortcuts ) 02994 return; 02995 if( pending_dfc && data ) 02996 { 02997 global_shortcuts_disabled_for_client = true; 02998 pending_dfc = false; 02999 } 03000 else 03001 { 03002 global_shortcuts_disabled = data; 03003 global_shortcuts_disabled_for_client = false; 03004 } 03005 // update also Alt+LMB actions etc. 03006 for( ClientList::ConstIterator it = clients.begin(); 03007 it != clients.end(); 03008 ++it ) 03009 (*it)->updateMouseGrab(); 03010 } 03011 03012 } // namespace 03013 03014 #include "workspace.moc"