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