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