• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • twin
 

twin

  • twin
workspace.cpp
1 /*****************************************************************
2  KWin - the KDE window manager
3  This file is part of the KDE project.
4 
5 Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6 Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
7 
8 You can Freely distribute this program under the GNU General Public
9 License. See the file "COPYING" for the exact licensing terms.
10 ******************************************************************/
11 
12 #include "workspace.h"
13 
14 #include <tdeapplication.h>
15 #include <tdestartupinfo.h>
16 #include <fixx11h.h>
17 #include <tdeconfig.h>
18 #include <tdeglobal.h>
19 #include <tqpopupmenu.h>
20 #include <tdelocale.h>
21 #include <tqregexp.h>
22 #include <tqpainter.h>
23 #include <tqbitmap.h>
24 #include <tqclipboard.h>
25 #include <tdemenubar.h>
26 #include <kprocess.h>
27 #include <kglobalaccel.h>
28 #include <dcopclient.h>
29 #include <kipc.h>
30 
31 #include "plugins.h"
32 #include "client.h"
33 #include "popupinfo.h"
34 #include "tabbox.h"
35 #include "atoms.h"
36 #include "placement.h"
37 #include "notifications.h"
38 #include "group.h"
39 #include "rules.h"
40 
41 #include <X11/XKBlib.h>
42 #include <X11/extensions/shape.h>
43 #include <X11/keysym.h>
44 #include <X11/keysymdef.h>
45 #include <X11/cursorfont.h>
46 
47 #include <pwd.h>
48 
49 #include "config.h"
50 
51 namespace KWinInternal
52 {
53 
54 extern int screen_number;
55 
56 Workspace *Workspace::_self = 0;
57 
58 TDEProcess* kompmgr = 0;
59 TDESelectionOwner* kompmgr_selection;
60 
61 bool allowKompmgrRestart = TRUE;
62 extern bool disable_twin_composition_manager;
63 
64 bool supportsCompMgr()
65 {
66  if (disable_twin_composition_manager) {
67  return false;
68  }
69 
70  int i;
71 
72  bool damageExt = XQueryExtension(tqt_xdisplay(), "DAMAGE", &i, &i, &i);
73  bool compositeExt = XQueryExtension(tqt_xdisplay(), "Composite", &i, &i, &i);
74  bool xfixesExt = XQueryExtension(tqt_xdisplay(), "XFIXES", &i, &i, &i);
75 
76  return damageExt && compositeExt && xfixesExt;
77 }
78 
79 pid_t getCompositorPID() {
80  // Attempt to load the compton-tde pid file
81  char *filename;
82  const char *pidfile = "compton-tde.pid";
83  char uidstr[sizeof(uid_t)*8+1];
84  sprintf(uidstr, "%d", getuid());
85  int n = strlen(P_tmpdir)+strlen(uidstr)+strlen(pidfile)+3;
86  filename = (char*)malloc(n*sizeof(char)+1);
87  memset(filename,0,n);
88  strcat(filename, P_tmpdir);
89  strcat(filename, "/.");
90  strcat(filename, uidstr);
91  strcat(filename, "-");
92  strcat(filename, pidfile);
93 
94  // Now that we did all that by way of introduction...read the file!
95  FILE *pFile;
96  char buffer[255];
97  pFile = fopen(filename, "r");
98  pid_t kompmgrpid = 0;
99  if (pFile)
100  {
101  printf("[twin-workspace] Using '%s' as compton-tde pidfile\n\n", filename);
102  // obtain file size
103  fseek (pFile , 0 , SEEK_END);
104  unsigned long lSize = ftell (pFile);
105  if (lSize > 254)
106  lSize = 254;
107  rewind (pFile);
108  size_t result = fread (buffer, 1, lSize, pFile);
109  fclose(pFile);
110  if (result > 0)
111  {
112  kompmgrpid = atoi(buffer);
113  }
114  }
115 
116  free(filename);
117  filename = NULL;
118 
119  return kompmgrpid;
120 }
121 
122 // Rikkus: This class is too complex. It needs splitting further.
123 // It's a nightmare to understand, especially with so few comments :(
124 
125 // Matthias: Feel free to ask me questions about it. Feel free to add
126 // comments. I disagree that further splittings makes it easier. 2500
127 // lines are not too much. It's the task that is complex, not the
128 // code.
129 Workspace::Workspace( bool restore )
130  : DCOPObject ("KWinInterface"),
131  TQObject (0, "workspace"),
132  current_desktop (0),
133  number_of_desktops(0),
134  active_screen (0),
135  active_popup( NULL ),
136  active_popup_client( NULL ),
137  desktop_widget (0),
138  temporaryRulesMessages( "_KDE_NET_WM_TEMPORARY_RULES", NULL, false ),
139  rules_updates_disabled( false ),
140  active_client (0),
141  last_active_client (0),
142  next_active_client (0),
143  most_recently_raised (0),
144  movingClient(0),
145  pending_take_activity ( NULL ),
146  delayfocus_client (0),
147  showing_desktop( false ),
148  block_showing_desktop( 0 ),
149  was_user_interaction (false),
150  session_saving (false),
151  control_grab (false),
152  tab_grab (false),
153  mouse_emulation (false),
154  block_focus (0),
155  tab_box (0),
156  popupinfo (0),
157  popup (0),
158  advanced_popup (0),
159  desk_popup (0),
160  desk_popup_index (0),
161  keys (0),
162  client_keys ( NULL ),
163  client_keys_dialog ( NULL ),
164  client_keys_client ( NULL ),
165  disable_shortcuts_keys ( NULL ),
166  global_shortcuts_disabled( false ),
167  global_shortcuts_disabled_for_client( false ),
168  root (0),
169  workspaceInit (true),
170  startup(0),
171  layoutOrientation(TQt::Vertical),
172  layoutX(-1),
173  layoutY(2),
174  workarea(NULL),
175  screenarea(NULL),
176  managing_topmenus( false ),
177  topmenu_selection( NULL ),
178  topmenu_watcher( NULL ),
179  topmenu_height( 0 ),
180  topmenu_space( NULL ),
181  set_active_client_recursion( 0 ),
182  block_stacking_updates( 0 ),
183  forced_global_mouse_grab( false )
184  {
185  _self = this;
186  mgr = new PluginMgr;
187  root = tqt_xrootwin();
188  default_colormap = DefaultColormap(tqt_xdisplay(), tqt_xscreen() );
189  installed_colormap = default_colormap;
190  session.setAutoDelete( TRUE );
191 
192  for (int i = 0; i < ACTIVE_BORDER_COUNT; ++i)
193  {
194  active_reserved[i] = 0;
195  active_windows[i] = None;
196  }
197 
198  connect( &temporaryRulesMessages, TQ_SIGNAL( gotMessage( const TQString& )),
199  this, TQ_SLOT( gotTemporaryRulesMessage( const TQString& )));
200  connect( &rulesUpdatedTimer, TQ_SIGNAL( timeout()), this, TQ_SLOT( writeWindowRules()));
201 
202  updateXTime(); // needed for proper initialization of user_time in Client ctor
203 
204  delayFocusTimer = 0;
205 
206  active_time_first = get_tqt_x_time();
207  active_time_last = get_tqt_x_time();
208 
209  if ( restore )
210  loadSessionInfo();
211 
212  loadWindowRules();
213 
214  (void) TQApplication::desktop(); // trigger creation of desktop widget
215 
216  desktop_widget =
217  new TQWidget(
218  0,
219  "desktop_widget",
220  (WFlags)(TQt::WType_Desktop | TQt::WPaintUnclipped)
221  );
222 
223  kapp->setGlobalMouseTracking( true ); // so that this doesn't mess eventmask on root window later
224  // call this before XSelectInput() on the root window
225  startup = new TDEStartupInfo(
226  TDEStartupInfo::DisableKWinModule | TDEStartupInfo::AnnounceSilenceChanges, this );
227 
228  // select windowmanager privileges
229  XSelectInput(tqt_xdisplay(), root,
230  KeyPressMask |
231  PropertyChangeMask |
232  ColormapChangeMask |
233  SubstructureRedirectMask |
234  SubstructureNotifyMask |
235  FocusChangeMask // for NotifyDetailNone
236  );
237 
238  Shape::init();
239 
240  // compatibility
241  long data = 1;
242 
243  XChangeProperty(
244  tqt_xdisplay(),
245  tqt_xrootwin(),
246  atoms->twin_running,
247  atoms->twin_running,
248  32,
249  PropModeAppend,
250  (unsigned char*) &data,
251  1
252  );
253 
254  client_keys = new TDEGlobalAccel( this );
255  initShortcuts();
256  tab_box = new TabBox( this );
257  popupinfo = new PopupInfo( this );
258 
259  init();
260 
261 #if (TQT_VERSION-0 >= 0x030200) // XRANDR support
262  connect( kapp->desktop(), TQ_SIGNAL( resized( int )), TQ_SLOT( desktopResized()));
263 #endif
264 
265  if (!supportsCompMgr()) {
266  options->useTranslucency = false;
267  }
268 
269  // start kompmgr - i wanted to put this into main.cpp, but that would prevent dcop support, as long as Application was no dcop_object
270 
271  // If compton-tde is already running, send it SIGTERM
272  pid_t kompmgrpid = getCompositorPID();
273 
274  if (options->useTranslucency)
275  {
276  kompmgr = new TDEProcess;
277  connect(kompmgr, TQ_SIGNAL(receivedStderr(TDEProcess*, char*, int)), TQ_SLOT(handleKompmgrOutput(TDEProcess*, char*, int)));
278  *kompmgr << TDE_COMPOSITOR_BINARY;
279  if (kompmgrpid)
280  {
281  if (kill(kompmgrpid, 0) < 0)
282  {
283  // Stale PID file detected; (re)start compositor!
284  startKompmgr();
285  }
286  }
287  else
288  {
289  startKompmgr();
290  }
291  }
292  else if (!disable_twin_composition_manager)
293  {
294 
295  if (kompmgrpid)
296  {
297  kill(kompmgrpid, SIGTERM);
298  }
299  else
300  {
301  stopKompmgr();
302  }
303  }
304  }
305 
306 
307 void Workspace::init()
308 {
309  if (options->activeBorders() == Options::ActiveSwitchAlways)
310  {
311  reserveActiveBorderSwitching(true);
312  }
313  updateActiveBorders();
314 
315 // not used yet
316 // topDock = 0L;
317 // maximizedWindowCounter = 0;
318 
319  supportWindow = new TQWidget;
320  XLowerWindow( tqt_xdisplay(), supportWindow->winId()); // see usage in layers.cpp
321 
322  XSetWindowAttributes attr;
323  attr.override_redirect = 1;
324  null_focus_window = XCreateWindow( tqt_xdisplay(), tqt_xrootwin(), -1,-1, 1, 1, 0, CopyFromParent,
325  InputOnly, CopyFromParent, CWOverrideRedirect, &attr );
326  XMapWindow(tqt_xdisplay(), null_focus_window);
327 
328  unsigned long protocols[ 5 ] =
329  {
330  NET::Supported |
331  NET::SupportingWMCheck |
332  NET::ClientList |
333  NET::ClientListStacking |
334  NET::DesktopGeometry |
335  NET::NumberOfDesktops |
336  NET::CurrentDesktop |
337  NET::ActiveWindow |
338  NET::WorkArea |
339  NET::CloseWindow |
340  NET::DesktopNames |
341  NET::KDESystemTrayWindows |
342  NET::WMName |
343  NET::WMVisibleName |
344  NET::WMDesktop |
345  NET::WMWindowType |
346  NET::WMState |
347  NET::WMStrut |
348  NET::WMIconGeometry |
349  NET::WMIcon |
350  NET::WMPid |
351  NET::WMMoveResize |
352  NET::WMKDESystemTrayWinFor |
353  NET::WMFrameExtents |
354  NET::WMPing
355  ,
356  NET::NormalMask |
357  NET::DesktopMask |
358  NET::DockMask |
359  NET::ToolbarMask |
360  NET::MenuMask |
361  NET::DialogMask |
362  NET::OverrideMask |
363  NET::TopMenuMask |
364  NET::UtilityMask |
365  NET::SplashMask |
366  0
367  ,
368  NET::Modal |
369 // NET::Sticky | // large desktops not supported (and probably never will be)
370  NET::MaxVert |
371  NET::MaxHoriz |
372  NET::Shaded |
373  NET::SkipTaskbar |
374  NET::KeepAbove |
375 // NET::StaysOnTop | the same like KeepAbove
376  NET::SkipPager |
377  NET::Hidden |
378  NET::FullScreen |
379  NET::KeepBelow |
380  NET::DemandsAttention |
381  0
382  ,
383  NET::WM2UserTime |
384  NET::WM2StartupId |
385  NET::WM2AllowedActions |
386  NET::WM2RestackWindow |
387  NET::WM2MoveResizeWindow |
388  NET::WM2ExtendedStrut |
389  NET::WM2KDETemporaryRules |
390  NET::WM2ShowingDesktop |
391  NET::WM2FullPlacement |
392  NET::WM2DesktopLayout |
393  0
394  ,
395  NET::ActionMove |
396  NET::ActionResize |
397  NET::ActionMinimize |
398  NET::ActionShade |
399 // NET::ActionStick | // Sticky state is not supported
400  NET::ActionMaxVert |
401  NET::ActionMaxHoriz |
402  NET::ActionFullScreen |
403  NET::ActionChangeDesktop |
404  NET::ActionClose |
405  0
406  ,
407  };
408 
409  rootInfo = new RootInfo( this, tqt_xdisplay(), supportWindow->winId(), "KWin",
410  protocols, 5, tqt_xscreen() );
411 
412  loadDesktopSettings();
413  updateDesktopLayout();
414  // extra NETRootInfo instance in Client mode is needed to get the values of the properties
415  NETRootInfo client_info( tqt_xdisplay(), NET::ActiveWindow | NET::CurrentDesktop );
416  int initial_desktop;
417  if( !kapp->isSessionRestored())
418  initial_desktop = client_info.currentDesktop();
419  else
420  {
421  TDEConfigGroupSaver saver( kapp->sessionConfig(), "Session" );
422  initial_desktop = kapp->sessionConfig()->readNumEntry( "desktop", 1 );
423  }
424  if( !setCurrentDesktop( initial_desktop ))
425  setCurrentDesktop( 1 );
426 
427  // now we know how many desktops we'll, thus, we initialise the positioning object
428  initPositioning = new Placement(this);
429 
430  connect(&reconfigureTimer, TQ_SIGNAL(timeout()), this,
431  TQ_SLOT(slotReconfigure()));
432  connect( &updateToolWindowsTimer, TQ_SIGNAL( timeout()), this, TQ_SLOT( slotUpdateToolWindows()));
433 
434  connect(kapp, TQ_SIGNAL(appearanceChanged()), this,
435  TQ_SLOT(slotReconfigure()));
436  connect(kapp, TQ_SIGNAL(settingsChanged(int)), this,
437  TQ_SLOT(slotSettingsChanged(int)));
438  connect(kapp, TQ_SIGNAL( kipcMessage( int, int )), this, TQ_SLOT( kipcMessage( int, int )));
439 
440  active_client = NULL;
441  rootInfo->setActiveWindow( None );
442  focusToNull();
443  if( !kapp->isSessionRestored())
444  ++block_focus; // because it will be set below
445 
446  char nm[ 100 ];
447  sprintf( nm, "_KDE_TOPMENU_OWNER_S%d", DefaultScreen( tqt_xdisplay()));
448  Atom topmenu_atom = XInternAtom( tqt_xdisplay(), nm, False );
449  topmenu_selection = new TDESelectionOwner( topmenu_atom );
450  topmenu_watcher = new TDESelectionWatcher( topmenu_atom );
451 // TODO grabXServer(); - where exactly put this? topmenu selection claiming down belong must be before
452 
453  { // begin updates blocker block
454  StackingUpdatesBlocker blocker( this );
455 
456  if( options->topMenuEnabled() && topmenu_selection->claim( false ))
457  setupTopMenuHandling(); // this can call updateStackingOrder()
458  else
459  lostTopMenuSelection();
460 
461  unsigned int i, nwins;
462  Window root_return, parent_return, *wins;
463  XQueryTree(tqt_xdisplay(), root, &root_return, &parent_return, &wins, &nwins);
464  for (i = 0; i < nwins; i++)
465  {
466  XWindowAttributes attr;
467  XGetWindowAttributes(tqt_xdisplay(), wins[i], &attr);
468  if (attr.override_redirect )
469  continue;
470  if( topmenu_space && topmenu_space->winId() == wins[ i ] )
471  continue;
472  if (attr.map_state != IsUnmapped)
473  {
474  if ( addSystemTrayWin( wins[i] ) )
475  continue;
476  Client* c = createClient( wins[i], true );
477  if ( c != NULL && root != tqt_xrootwin() )
478  { // TODO what is this?
479  // TODO may use TQWidget:.create
480  XReparentWindow( tqt_xdisplay(), c->frameId(), root, 0, 0 );
481  c->move(0,0);
482  }
483  }
484  }
485  if ( wins )
486  XFree((void *) wins);
487  // propagate clients, will really happen at the end of the updates blocker block
488  updateStackingOrder( true );
489 
490  updateClientArea();
491 
492  // NETWM spec says we have to set it to (0,0) if we don't support it
493  NETPoint* viewports = new NETPoint[ number_of_desktops ];
494  rootInfo->setDesktopViewport( number_of_desktops, *viewports );
495  delete[] viewports;
496  TQRect geom = TQApplication::desktop()->geometry();
497  NETSize desktop_geometry;
498  desktop_geometry.width = geom.width();
499  desktop_geometry.height = geom.height();
500  rootInfo->setDesktopGeometry( -1, desktop_geometry );
501  setShowingDesktop( false );
502 
503  } // end updates blocker block
504 
505  Client* new_active_client = NULL;
506  if( !kapp->isSessionRestored())
507  {
508  --block_focus;
509  new_active_client = findClient( WindowMatchPredicate( client_info.activeWindow()));
510  }
511  if( new_active_client == NULL
512  && activeClient() == NULL && should_get_focus.count() == 0 ) // no client activated in manage()
513  {
514  if( new_active_client == NULL )
515  new_active_client = topClientOnDesktop( currentDesktop());
516  if( new_active_client == NULL && !desktops.isEmpty() )
517  new_active_client = findDesktop( true, currentDesktop());
518  }
519  if( new_active_client != NULL )
520  activateClient( new_active_client );
521 
522  // outline windows for active border maximize window mode
523  outline_left = XCreateWindow(tqt_xdisplay(), rootWin(), 0, 0, 1, 1, 0,
524  CopyFromParent, CopyFromParent, CopyFromParent,
525  CWOverrideRedirect, &attr);
526  outline_right = XCreateWindow(tqt_xdisplay(), rootWin(), 0, 0, 1, 1, 0,
527  CopyFromParent, CopyFromParent, CopyFromParent,
528  CWOverrideRedirect, &attr);
529  outline_top = XCreateWindow(tqt_xdisplay(), rootWin(), 0, 0, 1, 1, 0,
530  CopyFromParent, CopyFromParent, CopyFromParent,
531  CWOverrideRedirect, &attr);
532  outline_bottom = XCreateWindow(tqt_xdisplay(), rootWin(), 0, 0, 1, 1, 0,
533  CopyFromParent, CopyFromParent, CopyFromParent,
534  CWOverrideRedirect, &attr);
535 
536  // SELI TODO this won't work with unreasonable focus policies,
537  // and maybe in rare cases also if the selected client doesn't
538  // want focus
539  workspaceInit = false;
540 // TODO ungrabXServer()
541 }
542 
543 Workspace::~Workspace()
544  {
545  if (kompmgr)
546  delete kompmgr;
547  blockStackingUpdates( true );
548 // TODO grabXServer();
549  // use stacking_order, so that twin --replace keeps stacking order
550  for( ClientList::ConstIterator it = stacking_order.begin();
551  it != stacking_order.end();
552  ++it )
553  {
554  // only release the window
555  (*it)->releaseWindow( true );
556  // No removeClient() is called, it does more than just removing.
557  // However, remove from some lists to e.g. prevent performTransiencyCheck()
558  // from crashing.
559  clients.remove( *it );
560  desktops.remove( *it );
561  }
562  delete desktop_widget;
563  delete tab_box;
564  delete popupinfo;
565  delete popup;
566  if ( root == tqt_xrootwin() )
567  XDeleteProperty(tqt_xdisplay(), tqt_xrootwin(), atoms->twin_running);
568 
569  writeWindowRules();
570  TDEGlobal::config()->sync();
571 
572  // destroy outline windows for active border maximize window mode
573  XDestroyWindow(tqt_xdisplay(), outline_left);
574  XDestroyWindow(tqt_xdisplay(), outline_right);
575  XDestroyWindow(tqt_xdisplay(), outline_top);
576  XDestroyWindow(tqt_xdisplay(), outline_bottom);
577 
578  delete rootInfo;
579  delete supportWindow;
580  delete mgr;
581  delete[] workarea;
582  delete[] screenarea;
583  delete startup;
584  delete initPositioning;
585  delete topmenu_watcher;
586  delete topmenu_selection;
587  delete topmenu_space;
588  delete client_keys_dialog;
589  while( !rules.isEmpty())
590  {
591  delete rules.front();
592  rules.pop_front();
593  }
594  XDestroyWindow( tqt_xdisplay(), null_focus_window );
595 // TODO ungrabXServer();
596  _self = 0;
597  }
598 
599 Client* Workspace::createClient( Window w, bool is_mapped )
600  {
601  StackingUpdatesBlocker blocker( this );
602  Client* c = new Client( this );
603  if( !c->manage( w, is_mapped ))
604  {
605  Client::deleteClient( c, Allowed );
606  return NULL;
607  }
608  addClient( c, Allowed );
609  return c;
610  }
611 
612 void Workspace::addClient( Client* c, allowed_t )
613  {
614  // waited with trans settings until window figured out if active or not ;)
615 // tqWarning("%s", (const char*)(c->resourceClass()));
616  c->setBMP(c->resourceName() == "beep-media-player" || c->decorationId() == None);
617  // first check if the window has it's own opinion of it's translucency ;)
618  c->getWindowOpacity();
619  if (c->isDock())
620  {
621 // if (c->x() == 0 && c->y() == 0 && c->width() > c->height()) topDock = c;
622  if (!c->hasCustomOpacity()) // this xould be done slightly more efficient, but we want to support the topDock in future
623  {
624  c->setShadowSize(options->dockShadowSize);
625  c->setOpacity(options->translucentDocks, options->dockOpacity);
626  }
627  }
628 
629  if (c->isMenu() || c->isTopMenu())
630  {
631  c->setShadowSize(options->menuShadowSize);
632  }
633 //------------------------------------------------
634  Group* grp = findGroup( c->window());
635  if( grp != NULL )
636  grp->gotLeader( c );
637 
638  if ( c->isDesktop() )
639  {
640  desktops.append( c );
641  if( active_client == NULL && should_get_focus.isEmpty() && c->isOnCurrentDesktop())
642  requestFocus( c ); // CHECKME? make sure desktop is active after startup if there's no other window active
643  }
644  else
645  {
646  updateFocusChains( c, FocusChainUpdate ); // add to focus chain if not already there
647  clients.append( c );
648  }
649  if( !unconstrained_stacking_order.contains( c ))
650  unconstrained_stacking_order.append( c );
651  if( !stacking_order.contains( c )) // it'll be updated later, and updateToolWindows() requires
652  stacking_order.append( c ); // c to be in stacking_order
653  if( c->isTopMenu())
654  addTopMenu( c );
655  updateClientArea(); // this cannot be in manage(), because the client got added only now
656  updateClientLayer( c );
657  if( c->isDesktop())
658  {
659  raiseClient( c );
660  // if there's no active client, make this desktop the active one
661  if( activeClient() == NULL && should_get_focus.count() == 0 )
662  activateClient( findDesktop( true, currentDesktop()));
663  }
664  c->checkActiveModal();
665  checkTransients( c->window()); // SELI does this really belong here?
666  updateStackingOrder( true ); // propagate new client
667  if( c->isUtility() || c->isMenu() || c->isToolbar())
668  updateToolWindows( true );
669  checkNonExistentClients();
670  }
671 
672 /*
673  Destroys the client \a c
674  */
675 void Workspace::removeClient( Client* c, allowed_t )
676  {
677  if (c == active_popup_client)
678  closeActivePopup();
679 
680  if( client_keys_client == c )
681  setupWindowShortcutDone( false );
682  if( !c->shortcut().isNull())
683  c->setShortcut( TQString::null ); // remove from client_keys
684 
685  if( c->isDialog())
686  Notify::raise( Notify::TransDelete );
687  if( c->isNormalWindow())
688  Notify::raise( Notify::Delete );
689 
690  Q_ASSERT( clients.contains( c ) || desktops.contains( c ));
691  clients.remove( c );
692  desktops.remove( c );
693  unconstrained_stacking_order.remove( c );
694  stacking_order.remove( c );
695  for( int i = 1;
696  i <= numberOfDesktops();
697  ++i )
698  focus_chain[ i ].remove( c );
699  global_focus_chain.remove( c );
700  attention_chain.remove( c );
701  showing_desktop_clients.remove( c );
702  if( c->isTopMenu())
703  removeTopMenu( c );
704  Group* group = findGroup( c->window());
705  if( group != NULL )
706  group->lostLeader();
707 
708  if ( c == most_recently_raised )
709  most_recently_raised = 0;
710  should_get_focus.remove( c );
711  Q_ASSERT( c != active_client );
712  if ( c == last_active_client )
713  last_active_client = 0;
714  if( c == pending_take_activity )
715  pending_take_activity = NULL;
716  if( c == delayfocus_client )
717  cancelDelayFocus();
718 
719  updateStackingOrder( true );
720 
721  if (tab_grab)
722  tab_box->repaint();
723 
724  updateClientArea();
725  }
726 
727 void Workspace::updateFocusChains( Client* c, FocusChainChange change )
728  {
729  if( !c->wantsTabFocus()) // doesn't want tab focus, remove
730  {
731  for( int i=1;
732  i<= numberOfDesktops();
733  ++i )
734  focus_chain[i].remove(c);
735  global_focus_chain.remove( c );
736  return;
737  }
738  if(c->desktop() == NET::OnAllDesktops)
739  { //now on all desktops, add it to focus_chains it is not already in
740  for( int i=1; i<= numberOfDesktops(); i++)
741  { // making first/last works only on current desktop, don't affect all desktops
742  if( i == currentDesktop()
743  && ( change == FocusChainMakeFirst || change == FocusChainMakeLast ))
744  {
745  focus_chain[ i ].remove( c );
746  if( change == FocusChainMakeFirst )
747  focus_chain[ i ].append( c );
748  else
749  focus_chain[ i ].prepend( c );
750  }
751  else if( !focus_chain[ i ].contains( c ))
752  { // add it after the active one
753  if( active_client != NULL && active_client != c
754  && !focus_chain[ i ].isEmpty() && focus_chain[ i ].last() == active_client )
755  focus_chain[ i ].insert( focus_chain[ i ].fromLast(), c );
756  else
757  focus_chain[ i ].append( c ); // otherwise add as the first one
758  }
759  }
760  }
761  else //now only on desktop, remove it anywhere else
762  {
763  for( int i=1; i<= numberOfDesktops(); i++)
764  {
765  if( i == c->desktop())
766  {
767  if( change == FocusChainMakeFirst )
768  {
769  focus_chain[ i ].remove( c );
770  focus_chain[ i ].append( c );
771  }
772  else if( change == FocusChainMakeLast )
773  {
774  focus_chain[ i ].remove( c );
775  focus_chain[ i ].prepend( c );
776  }
777  else if( !focus_chain[ i ].contains( c ))
778  {
779  if( active_client != NULL && active_client != c
780  && !focus_chain[ i ].isEmpty() && focus_chain[ i ].last() == active_client )
781  focus_chain[ i ].insert( focus_chain[ i ].fromLast(), c );
782  else
783  focus_chain[ i ].append( c ); // otherwise add as the first one
784  }
785  }
786  else
787  focus_chain[ i ].remove( c );
788  }
789  }
790  if( change == FocusChainMakeFirst )
791  {
792  global_focus_chain.remove( c );
793  global_focus_chain.append( c );
794  }
795  else if( change == FocusChainMakeLast )
796  {
797  global_focus_chain.remove( c );
798  global_focus_chain.prepend( c );
799  }
800  else if( !global_focus_chain.contains( c ))
801  {
802  if( active_client != NULL && active_client != c
803  && !global_focus_chain.isEmpty() && global_focus_chain.last() == active_client )
804  global_focus_chain.insert( global_focus_chain.fromLast(), c );
805  else
806  global_focus_chain.append( c ); // otherwise add as the first one
807  }
808  }
809 
810 void Workspace::updateOverlappingShadows(unsigned long window)
811  {
812  Client *client;
813 
814  if ((client = findClient(WindowMatchPredicate((WId)window))))
815  // Redraw overlapping shadows without waiting for the specified window
816  // to redraw its own shadow
817  client->drawOverlappingShadows(false);
818  }
819 
820 void Workspace::setShadowed(unsigned long window, bool shadowed)
821  {
822  Client *client;
823 
824  if ((client = findClient(WindowMatchPredicate((WId)window))))
825  client->setShadowed(shadowed);
826  }
827 
828 void Workspace::updateCurrentTopMenu()
829  {
830  if( !managingTopMenus())
831  return;
832  // toplevel menubar handling
833  Client* menubar = 0;
834  bool block_desktop_menubar = false;
835  if( active_client )
836  {
837  // show the new menu bar first...
838  Client* menu_client = active_client;
839  for(;;)
840  {
841  if( menu_client->isFullScreen())
842  block_desktop_menubar = true;
843  for( ClientList::ConstIterator it = menu_client->transients().begin();
844  it != menu_client->transients().end();
845  ++it )
846  if( (*it)->isTopMenu())
847  {
848  menubar = *it;
849  break;
850  }
851  if( menubar != NULL || !menu_client->isTransient())
852  break;
853  if( menu_client->isModal() || menu_client->transientFor() == NULL )
854  break; // don't use mainwindow's menu if this is modal or group transient
855  menu_client = menu_client->transientFor();
856  }
857  if( !menubar )
858  { // try to find any topmenu from the application (#72113)
859  for( ClientList::ConstIterator it = active_client->group()->members().begin();
860  it != active_client->group()->members().end();
861  ++it )
862  if( (*it)->isTopMenu())
863  {
864  menubar = *it;
865  break;
866  }
867  }
868  }
869  if( !menubar && !block_desktop_menubar && options->desktopTopMenu())
870  {
871  // Find the menubar of the desktop
872  Client* desktop = findDesktop( true, currentDesktop());
873  if( desktop != NULL )
874  {
875  for( ClientList::ConstIterator it = desktop->transients().begin();
876  it != desktop->transients().end();
877  ++it )
878  if( (*it)->isTopMenu())
879  {
880  menubar = *it;
881  break;
882  }
883  }
884  // TODO to be cleaned app with window grouping
885  // Without qt-copy patch #0009, the topmenu and desktop are not in the same group,
886  // thus the topmenu is not transient for it :-/.
887  if( menubar == NULL )
888  {
889  for( ClientList::ConstIterator it = topmenus.begin();
890  it != topmenus.end();
891  ++it )
892  if( (*it)->wasOriginallyGroupTransient()) // kdesktop's topmenu has WM_TRANSIENT_FOR
893  { // set pointing to the root window
894  menubar = *it; // to recognize it here
895  break; // Also, with the xroot hack in kdesktop,
896  } // there's no NET::Desktop window to be transient for
897  }
898  }
899 
900 // kdDebug() << "CURRENT TOPMENU:" << menubar << ":" << active_client << endl;
901  if ( menubar )
902  {
903  if( active_client && !menubar->isOnDesktop( active_client->desktop()))
904  menubar->setDesktop( active_client->desktop());
905  menubar->hideClient( false );
906  topmenu_space->hide();
907  // make it appear like it's been raised manually - it's in the Dock layer anyway,
908  // and not raising it could mess up stacking order of topmenus within one application,
909  // and thus break raising of mainclients in raiseClient()
910  unconstrained_stacking_order.remove( menubar );
911  unconstrained_stacking_order.append( menubar );
912  }
913  else if( !block_desktop_menubar )
914  { // no topmenu active - show the space window, so that there's not empty space
915  topmenu_space->show();
916  }
917 
918  // ... then hide the other ones. Avoids flickers.
919  for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it)
920  {
921  if( (*it)->isTopMenu() && (*it) != menubar )
922  (*it)->hideClient( true );
923  }
924  }
925 
926 
927 void Workspace::updateToolWindows( bool also_hide )
928  {
929  // TODO what if Client's transiency/group changes? should this be called too? (I'm paranoid, am I not?)
930  if( !options->hideUtilityWindowsForInactive )
931  {
932  for( ClientList::ConstIterator it = clients.begin();
933  it != clients.end();
934  ++it )
935  (*it)->hideClient( false );
936  return;
937  }
938  const Group* group = NULL;
939  const Client* client = active_client;
940 // Go up in transiency hiearchy, if the top is found, only tool transients for the top mainwindow
941 // will be shown; if a group transient is group, all tools in the group will be shown
942  while( client != NULL )
943  {
944  if( !client->isTransient())
945  break;
946  if( client->groupTransient())
947  {
948  group = client->group();
949  break;
950  }
951  client = client->transientFor();
952  }
953  // use stacking order only to reduce flicker, it doesn't matter if block_stacking_updates == 0,
954  // i.e. if it's not up to date
955 
956  // SELI but maybe it should - what if a new client has been added that's not in stacking order yet?
957  ClientList to_show, to_hide;
958  for( ClientList::ConstIterator it = stacking_order.begin();
959  it != stacking_order.end();
960  ++it )
961  {
962  if( (*it)->isUtility() || (*it)->isMenu() || (*it)->isToolbar())
963  {
964  bool show = true;
965  if( !(*it)->isTransient())
966  {
967  if( (*it)->group()->members().count() == 1 ) // has its own group, keep always visible
968  show = true;
969  else if( client != NULL && (*it)->group() == client->group())
970  show = true;
971  else
972  show = false;
973  }
974  else
975  {
976  if( group != NULL && (*it)->group() == group )
977  show = true;
978  else if( client != NULL && client->hasTransient( (*it), true ))
979  show = true;
980  else
981  show = false;
982  }
983  if( !show && also_hide )
984  {
985  const ClientList mainclients = (*it)->mainClients();
986  // don't hide utility windows which are standalone(?) or
987  // have e.g. kicker as mainwindow
988  if( mainclients.isEmpty())
989  show = true;
990  for( ClientList::ConstIterator it2 = mainclients.begin();
991  it2 != mainclients.end();
992  ++it2 )
993  {
994  if( (*it2)->isSpecialWindow())
995  show = true;
996  }
997  if( !show )
998  to_hide.append( *it );
999  }
1000  if( show )
1001  to_show.append( *it );
1002  }
1003  } // first show new ones, then hide
1004  for( ClientList::ConstIterator it = to_show.fromLast();
1005  it != to_show.end();
1006  --it ) // from topmost
1007  // TODO since this is in stacking order, the order of taskbar entries changes :(
1008  (*it)->hideClient( false );
1009  if( also_hide )
1010  {
1011  for( ClientList::ConstIterator it = to_hide.begin();
1012  it != to_hide.end();
1013  ++it ) // from bottommost
1014  (*it)->hideClient( true );
1015  updateToolWindowsTimer.stop();
1016  }
1017  else // setActiveClient() is after called with NULL client, quickly followed
1018  { // by setting a new client, which would result in flickering
1019  updateToolWindowsTimer.start( 50, true );
1020  }
1021  }
1022 
1023 void Workspace::slotUpdateToolWindows()
1024  {
1025  updateToolWindows( true );
1026  }
1027 
1031 void Workspace::updateColormap()
1032  {
1033  Colormap cmap = default_colormap;
1034  if ( activeClient() && activeClient()->colormap() != None )
1035  cmap = activeClient()->colormap();
1036  if ( cmap != installed_colormap )
1037  {
1038  XInstallColormap(tqt_xdisplay(), cmap );
1039  installed_colormap = cmap;
1040  }
1041  }
1042 
1043 void Workspace::reconfigure()
1044  {
1045  reconfigureTimer.start(200, true);
1046  }
1047 
1048 
1049 void Workspace::slotSettingsChanged(int category)
1050  {
1051  kdDebug(1212) << "Workspace::slotSettingsChanged()" << endl;
1052  if( category == (int) TDEApplication::SETTINGS_SHORTCUTS )
1053  readShortcuts();
1054  }
1055 
1059 KWIN_PROCEDURE( CheckBorderSizesProcedure, cl->checkBorderSizes() );
1060 
1061 void Workspace::slotReconfigure()
1062  {
1063  kdDebug(1212) << "Workspace::slotReconfigure()" << endl;
1064  reconfigureTimer.stop();
1065 
1066  if (options->activeBorders() == Options::ActiveSwitchAlways)
1067  {
1068  reserveActiveBorderSwitching(false);
1069  }
1070 
1071  TDEGlobal::config()->reparseConfiguration();
1072  unsigned long changed = options->updateSettings();
1073  tab_box->reconfigure();
1074  popupinfo->reconfigure();
1075  initPositioning->reinitCascading( 0 );
1076  readShortcuts();
1077  forEachClient( CheckIgnoreFocusStealingProcedure());
1078  updateToolWindows( true );
1079 
1080  if( mgr->reset( changed ))
1081  { // decorations need to be recreated
1082 #if 0 // This actually seems to make things worse now
1083  TQWidget curtain;
1084  curtain.setBackgroundMode( NoBackground );
1085  curtain.setGeometry( TQApplication::desktop()->geometry() );
1086  curtain.show();
1087 #endif
1088  for( ClientList::ConstIterator it = clients.begin();
1089  it != clients.end();
1090  ++it )
1091  {
1092  (*it)->updateDecoration( true, true );
1093  }
1094  mgr->destroyPreviousPlugin();
1095  }
1096  else
1097  {
1098  forEachClient( CheckBorderSizesProcedure());
1099  }
1100 
1101  if (options->activeBorders() == Options::ActiveSwitchAlways)
1102  {
1103  reserveActiveBorderSwitching(true);
1104  }
1105 
1106  if( options->topMenuEnabled() && !managingTopMenus())
1107  {
1108  if( topmenu_selection->claim( false ))
1109  setupTopMenuHandling();
1110  else
1111  lostTopMenuSelection();
1112  }
1113  else if( !options->topMenuEnabled() && managingTopMenus())
1114  {
1115  topmenu_selection->release();
1116  lostTopMenuSelection();
1117  }
1118  topmenu_height = 0; // invalidate used menu height
1119  if( managingTopMenus())
1120  {
1121  updateTopMenuGeometry();
1122  updateCurrentTopMenu();
1123  }
1124 
1125  loadWindowRules();
1126  for( ClientList::Iterator it = clients.begin();
1127  it != clients.end();
1128  ++it )
1129  {
1130  (*it)->setupWindowRules( true );
1131  (*it)->applyWindowRules();
1132  discardUsedWindowRules( *it, false );
1133  }
1134 
1135  if (options->resetKompmgr) // need restart
1136  {
1137  bool tmp = options->useTranslucency;
1138 
1139  // If compton-tde is already running, sending SIGUSR2 will force a reload of its settings
1140  // Attempt to load the compton-tde pid file
1141  char *filename;
1142  const char *pidfile = "compton-tde.pid";
1143  char uidstr[sizeof(uid_t)*8+1];
1144  sprintf(uidstr, "%d", getuid());
1145  int n = strlen(P_tmpdir)+strlen(uidstr)+strlen(pidfile)+3;
1146  filename = (char*)malloc(n*sizeof(char)+1);
1147  memset(filename,0,n);
1148  strcat(filename, P_tmpdir);
1149  strcat(filename, "/.");
1150  strcat(filename, uidstr);
1151  strcat(filename, "-");
1152  strcat(filename, pidfile);
1153 
1154  // Now that we did all that by way of introduction...read the file!
1155  FILE *pFile;
1156  char buffer[255];
1157  pFile = fopen(filename, "r");
1158  int kompmgrpid = 0;
1159  if (pFile)
1160  {
1161  printf("[twin-workspace] Using '%s' as compton-tde pidfile\n\n", filename);
1162  // obtain file size
1163  fseek (pFile , 0 , SEEK_END);
1164  unsigned long lSize = ftell (pFile);
1165  if (lSize > 254)
1166  lSize = 254;
1167  rewind (pFile);
1168  size_t result = fread (buffer, 1, lSize, pFile);
1169  fclose(pFile);
1170  if (result > 0)
1171  {
1172  kompmgrpid = atoi(buffer);
1173  }
1174  }
1175 
1176  free(filename);
1177  filename = NULL;
1178 
1179  if (tmp)
1180  {
1181  if (kompmgrpid)
1182  {
1183  kill(kompmgrpid, SIGUSR2);
1184  }
1185  else
1186  {
1187  stopKompmgr();
1188  if (!kompmgr)
1189  {
1190  kompmgr = new TDEProcess;
1191  connect(kompmgr, TQ_SIGNAL(receivedStderr(TDEProcess*, char*, int)), TQ_SLOT(handleKompmgrOutput(TDEProcess*, char*, int)));
1192  *kompmgr << TDE_COMPOSITOR_BINARY;
1193  }
1194  TQTimer::singleShot( 200, this, TQ_SLOT(startKompmgr()) ); // wait some time to ensure system's ready for restart
1195  }
1196  }
1197  else
1198  {
1199  if (kompmgrpid)
1200  {
1201  kill(kompmgrpid, SIGTERM);
1202  }
1203  else
1204  {
1205  stopKompmgr();
1206  }
1207  }
1208  }
1209  }
1210 
1211 void Workspace::loadDesktopSettings()
1212  {
1213  TDEConfig* c = TDEGlobal::config();
1214  TQCString groupname;
1215  if (screen_number == 0)
1216  groupname = "Desktops";
1217  else
1218  groupname.sprintf("Desktops-screen-%d", screen_number);
1219  TDEConfigGroupSaver saver(c,groupname);
1220 
1221  int n = c->readNumEntry("Number", 4);
1222  number_of_desktops = n;
1223  delete workarea;
1224  workarea = new TQRect[ n + 1 ];
1225  delete screenarea;
1226  screenarea = NULL;
1227  rootInfo->setNumberOfDesktops( number_of_desktops );
1228  desktop_focus_chain.resize( n );
1229  // make it +1, so that it can be accessed as [1..numberofdesktops]
1230  focus_chain.resize( n + 1 );
1231  for(int i = 1; i <= n; i++)
1232  {
1233  TQString s = c->readEntry(TQString("Name_%1").arg(i),
1234  i18n("Desktop %1").arg(i));
1235  rootInfo->setDesktopName( i, s.utf8().data() );
1236  desktop_focus_chain[i-1] = i;
1237  }
1238  }
1239 
1240 void Workspace::saveDesktopSettings()
1241  {
1242  TDEConfig* c = TDEGlobal::config();
1243  TQCString groupname;
1244  if (screen_number == 0)
1245  groupname = "Desktops";
1246  else
1247  groupname.sprintf("Desktops-screen-%d", screen_number);
1248  TDEConfigGroupSaver saver(c,groupname);
1249 
1250  c->writeEntry("Number", number_of_desktops );
1251  for(int i = 1; i <= number_of_desktops; i++)
1252  {
1253  TQString s = desktopName( i );
1254  TQString defaultvalue = i18n("Desktop %1").arg(i);
1255  if ( s.isEmpty() )
1256  {
1257  s = defaultvalue;
1258  rootInfo->setDesktopName( i, s.utf8().data() );
1259  }
1260 
1261  if (s != defaultvalue)
1262  {
1263  c->writeEntry( TQString("Name_%1").arg(i), s );
1264  }
1265  else
1266  {
1267  TQString currentvalue = c->readEntry(TQString("Name_%1").arg(i));
1268  if (currentvalue != defaultvalue)
1269  c->writeEntry( TQString("Name_%1").arg(i), "" );
1270  }
1271  }
1272  }
1273 
1274 TQStringList Workspace::configModules(bool controlCenter)
1275  {
1276  TQStringList args;
1277  args << "tde-twindecoration.desktop";
1278  if (controlCenter)
1279  args << "tde-twinoptions.desktop";
1280  else if (kapp->authorizeControlModule("tde-twinoptions.desktop"))
1281  args << "twinactions" << "twinfocus" << "twinmoving" << "twinadvanced" << "twinrules" << "twintranslucency";
1282  return args;
1283  }
1284 
1285 void Workspace::configureWM()
1286  {
1287  TDEApplication::tdeinitExec( "tdecmshell", configModules(false) );
1288  }
1289 
1293 void Workspace::doNotManage( TQString title )
1294  {
1295  doNotManageList.append( title );
1296  }
1297 
1301 bool Workspace::isNotManaged( const TQString& title )
1302  {
1303  for ( TQStringList::Iterator it = doNotManageList.begin(); it != doNotManageList.end(); ++it )
1304  {
1305  TQRegExp r( (*it) );
1306  if (r.search(title) != -1)
1307  {
1308  doNotManageList.remove( it );
1309  return TRUE;
1310  }
1311  }
1312  return FALSE;
1313  }
1314 
1318 void Workspace::refresh()
1319  {
1320  TQWidget w;
1321  w.setGeometry( TQApplication::desktop()->geometry() );
1322  w.show();
1323  w.hide();
1324  TQApplication::flushX();
1325  }
1326 
1334 class ObscuringWindows
1335  {
1336  public:
1337  ~ObscuringWindows();
1338  void create( Client* c );
1339  private:
1340  TQValueList<Window> obscuring_windows;
1341  static TQValueList<Window>* cached;
1342  static unsigned int max_cache_size;
1343  };
1344 
1345 TQValueList<Window>* ObscuringWindows::cached = 0;
1346 unsigned int ObscuringWindows::max_cache_size = 0;
1347 
1348 void ObscuringWindows::create( Client* c )
1349  {
1350  if( cached == 0 )
1351  cached = new TQValueList<Window>;
1352  Window obs_win;
1353  XWindowChanges chngs;
1354  int mask = CWSibling | CWStackMode;
1355  if( cached->count() > 0 )
1356  {
1357  cached->remove( obs_win = cached->first());
1358  chngs.x = c->x();
1359  chngs.y = c->y();
1360  chngs.width = c->width();
1361  chngs.height = c->height();
1362  mask |= CWX | CWY | CWWidth | CWHeight;
1363  }
1364  else
1365  {
1366  XSetWindowAttributes a;
1367  a.background_pixmap = None;
1368  a.override_redirect = True;
1369  obs_win = XCreateWindow( tqt_xdisplay(), tqt_xrootwin(), c->x(), c->y(),
1370  c->width(), c->height(), 0, CopyFromParent, InputOutput,
1371  CopyFromParent, CWBackPixmap | CWOverrideRedirect, &a );
1372  }
1373  chngs.sibling = c->frameId();
1374  chngs.stack_mode = Below;
1375  XConfigureWindow( tqt_xdisplay(), obs_win, mask, &chngs );
1376  XMapWindow( tqt_xdisplay(), obs_win );
1377  obscuring_windows.append( obs_win );
1378  }
1379 
1380 ObscuringWindows::~ObscuringWindows()
1381  {
1382  max_cache_size = TQMAX( max_cache_size, obscuring_windows.count() + 4 ) - 1;
1383  for( TQValueList<Window>::ConstIterator it = obscuring_windows.begin();
1384  it != obscuring_windows.end();
1385  ++it )
1386  {
1387  XUnmapWindow( tqt_xdisplay(), *it );
1388  if( cached->count() < max_cache_size )
1389  cached->prepend( *it );
1390  else
1391  XDestroyWindow( tqt_xdisplay(), *it );
1392  }
1393  }
1394 
1395 
1402 bool Workspace::setCurrentDesktop( int new_desktop )
1403  {
1404  if (new_desktop < 1 || new_desktop > number_of_desktops )
1405  return false;
1406 
1407  closeActivePopup();
1408  ++block_focus;
1409 // TODO Q_ASSERT( block_stacking_updates == 0 ); // make sure stacking_order is up to date
1410  StackingUpdatesBlocker blocker( this );
1411 
1412  int old_desktop = current_desktop;
1413  if (new_desktop != current_desktop)
1414  {
1415  ++block_showing_desktop;
1416  /*
1417  optimized Desktop switching: unmapping done from back to front
1418  mapping done from front to back => less exposure events
1419  */
1420  Notify::raise((Notify::Event) (Notify::DesktopChange+new_desktop));
1421 
1422  ObscuringWindows obs_wins;
1423 
1424  current_desktop = new_desktop; // change the desktop (so that Client::updateVisibility() works)
1425 
1426  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....
1427  if (!desktopHasCompositing) {
1428  // If composition is not in use then we can hide the old windows before showing the new ones
1429  for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it) {
1430  if ( !(*it)->isOnDesktop( new_desktop ) && (*it) != movingClient )
1431  {
1432  if( (*it)->isShown( true ) && (*it)->isOnDesktop( old_desktop )) {
1433  obs_wins.create( *it );
1434  }
1435  (*it)->updateVisibility();
1436  }
1437  }
1438  }
1439 
1440  rootInfo->setCurrentDesktop( current_desktop ); // now propagate the change, after hiding, before showing
1441 
1442  if( movingClient && !movingClient->isOnDesktop( new_desktop ))
1443  movingClient->setDesktop( new_desktop );
1444 
1445  for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) {
1446  if ( (*it)->isOnDesktop( new_desktop ) ) {
1447  (*it)->updateVisibility();
1448  }
1449  }
1450 
1451  if (desktopHasCompositing) {
1452  // If composition is in use then we cannot hide the old windows before showing the new ones,
1453  // unless you happen to like the "flicker annoyingly to desktop" effect... :-P
1454  XSync( tqt_xdisplay(), false); // Make absolutely certain all new windows are shown before hiding the old ones
1455  for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it) {
1456  if ( !(*it)->isOnDesktop( new_desktop ) && (*it) != movingClient )
1457  {
1458  if( (*it)->isShown( true ) && (*it)->isOnDesktop( old_desktop )) {
1459  obs_wins.create( *it );
1460  }
1461  (*it)->updateVisibility();
1462  }
1463  }
1464  }
1465 
1466  --block_showing_desktop;
1467  if( showingDesktop()) // do this only after desktop change to avoid flicker
1468  resetShowingDesktop( false );
1469  }
1470 
1471  // restore the focus on this desktop
1472  --block_focus;
1473  Client* c = 0;
1474 
1475  if ( options->focusPolicyIsReasonable())
1476  {
1477  // Search in focus chain
1478  if ( movingClient != NULL && active_client == movingClient
1479  && focus_chain[currentDesktop()].contains( active_client )
1480  && active_client->isShown( true ) && active_client->isOnCurrentDesktop())
1481  {
1482  c = active_client; // the requestFocus below will fail, as the client is already active
1483  }
1484  if ( !c )
1485  {
1486  for( ClientList::ConstIterator it = focus_chain[currentDesktop()].fromLast();
1487  it != focus_chain[currentDesktop()].end();
1488  --it )
1489  {
1490  if ( (*it)->isShown( false ) && (*it)->isOnCurrentDesktop())
1491  {
1492  c = *it;
1493  break;
1494  }
1495  }
1496  }
1497  }
1498 
1499  //if "unreasonable focus policy"
1500  // and active_client is on_all_desktops and under mouse (hence == old_active_client),
1501  // conserve focus (thanks to Volker Schatz <V.Schatz at thphys.uni-heidelberg.de>)
1502  else if( active_client && active_client->isShown( true ) && active_client->isOnCurrentDesktop())
1503  c = active_client;
1504 
1505  if( c == NULL && !desktops.isEmpty())
1506  c = findDesktop( true, currentDesktop());
1507 
1508  if( c != active_client )
1509  setActiveClient( NULL, Allowed );
1510 
1511  if ( c )
1512  requestFocus( c );
1513  else
1514  focusToNull();
1515 
1516  updateCurrentTopMenu();
1517 
1518  // Update focus chain:
1519  // If input: chain = { 1, 2, 3, 4 } and current_desktop = 3,
1520  // Output: chain = { 3, 1, 2, 4 }.
1521 // kdDebug(1212) << TQString("Switching to desktop #%1, at focus_chain[currentDesktop()] index %2\n")
1522 // .arg(currentDesktop()).arg(desktop_focus_chain.find( currentDesktop() ));
1523  for( int i = desktop_focus_chain.find( currentDesktop() ); i > 0; i-- )
1524  desktop_focus_chain[i] = desktop_focus_chain[i-1];
1525  desktop_focus_chain[0] = currentDesktop();
1526 
1527 // TQString s = "desktop_focus_chain[] = { ";
1528 // for( uint i = 0; i < desktop_focus_chain.size(); i++ )
1529 // s += TQString::number(desktop_focus_chain[i]) + ", ";
1530 // kdDebug(1212) << s << "}\n";
1531 
1532  if( old_desktop != 0 ) // not for the very first time
1533  popupinfo->showInfo( desktopName(currentDesktop()) );
1534  return true;
1535  }
1536 
1537 // called only from DCOP
1538 void Workspace::nextDesktop()
1539  {
1540  int desktop = currentDesktop() + 1;
1541  setCurrentDesktop(desktop > numberOfDesktops() ? 1 : desktop);
1542  }
1543 
1544 // called only from DCOP
1545 void Workspace::previousDesktop()
1546  {
1547  int desktop = currentDesktop() - 1;
1548  setCurrentDesktop(desktop > 0 ? desktop : numberOfDesktops());
1549  }
1550 
1551 int Workspace::desktopToRight( int desktop ) const
1552  {
1553  int x,y;
1554  calcDesktopLayout(x,y);
1555  int dt = desktop-1;
1556  if (layoutOrientation == TQt::Vertical)
1557  {
1558  dt += y;
1559  if ( dt >= numberOfDesktops() )
1560  {
1561  if ( options->rollOverDesktops )
1562  dt -= numberOfDesktops();
1563  else
1564  return desktop;
1565  }
1566  }
1567  else
1568  {
1569  int d = (dt % x) + 1;
1570  if ( d >= x )
1571  {
1572  if ( options->rollOverDesktops )
1573  d -= x;
1574  else
1575  return desktop;
1576  }
1577  dt = dt - (dt % x) + d;
1578  }
1579  return dt+1;
1580  }
1581 
1582 int Workspace::desktopToLeft( int desktop ) const
1583  {
1584  int x,y;
1585  calcDesktopLayout(x,y);
1586  int dt = desktop-1;
1587  if (layoutOrientation == TQt::Vertical)
1588  {
1589  dt -= y;
1590  if ( dt < 0 )
1591  {
1592  if ( options->rollOverDesktops )
1593  dt += numberOfDesktops();
1594  else
1595  return desktop;
1596  }
1597  }
1598  else
1599  {
1600  int d = (dt % x) - 1;
1601  if ( d < 0 )
1602  {
1603  if ( options->rollOverDesktops )
1604  d += x;
1605  else
1606  return desktop;
1607  }
1608  dt = dt - (dt % x) + d;
1609  }
1610  return dt+1;
1611  }
1612 
1613 int Workspace::desktopUp( int desktop ) const
1614  {
1615  int x,y;
1616  calcDesktopLayout(x,y);
1617  int dt = desktop-1;
1618  if (layoutOrientation == TQt::Horizontal)
1619  {
1620  dt -= x;
1621  if ( dt < 0 )
1622  {
1623  if ( options->rollOverDesktops )
1624  dt += numberOfDesktops();
1625  else
1626  return desktop;
1627  }
1628  }
1629  else
1630  {
1631  int d = (dt % y) - 1;
1632  if ( d < 0 )
1633  {
1634  if ( options->rollOverDesktops )
1635  d += y;
1636  else
1637  return desktop;
1638  }
1639  dt = dt - (dt % y) + d;
1640  }
1641  return dt+1;
1642  }
1643 
1644 int Workspace::desktopDown( int desktop ) const
1645  {
1646  int x,y;
1647  calcDesktopLayout(x,y);
1648  int dt = desktop-1;
1649  if (layoutOrientation == TQt::Horizontal)
1650  {
1651  dt += x;
1652  if ( dt >= numberOfDesktops() )
1653  {
1654  if ( options->rollOverDesktops )
1655  dt -= numberOfDesktops();
1656  else
1657  return desktop;
1658  }
1659  }
1660  else
1661  {
1662  int d = (dt % y) + 1;
1663  if ( d >= y )
1664  {
1665  if ( options->rollOverDesktops )
1666  d -= y;
1667  else
1668  return desktop;
1669  }
1670  dt = dt - (dt % y) + d;
1671  }
1672  return dt+1;
1673  }
1674 
1675 
1679 void Workspace::setNumberOfDesktops( int n )
1680  {
1681  if ( n == number_of_desktops )
1682  return;
1683  int old_number_of_desktops = number_of_desktops;
1684  number_of_desktops = n;
1685 
1686  if( currentDesktop() > numberOfDesktops())
1687  setCurrentDesktop( numberOfDesktops());
1688 
1689  // if increasing the number, do the resizing now,
1690  // otherwise after the moving of windows to still existing desktops
1691  if( old_number_of_desktops < number_of_desktops )
1692  {
1693  rootInfo->setNumberOfDesktops( number_of_desktops );
1694  NETPoint* viewports = new NETPoint[ number_of_desktops ];
1695  rootInfo->setDesktopViewport( number_of_desktops, *viewports );
1696  delete[] viewports;
1697  updateClientArea( true );
1698  focus_chain.resize( number_of_desktops + 1 );
1699  }
1700 
1701  // if the number of desktops decreased, move all
1702  // windows that would be hidden to the last visible desktop
1703  if( old_number_of_desktops > number_of_desktops )
1704  {
1705  for( ClientList::ConstIterator it = clients.begin();
1706  it != clients.end();
1707  ++it)
1708  {
1709  if( !(*it)->isOnAllDesktops() && (*it)->desktop() > numberOfDesktops())
1710  sendClientToDesktop( *it, numberOfDesktops(), true );
1711  }
1712  }
1713  if( old_number_of_desktops > number_of_desktops )
1714  {
1715  rootInfo->setNumberOfDesktops( number_of_desktops );
1716  NETPoint* viewports = new NETPoint[ number_of_desktops ];
1717  rootInfo->setDesktopViewport( number_of_desktops, *viewports );
1718  delete[] viewports;
1719  updateClientArea( true );
1720  focus_chain.resize( number_of_desktops + 1 );
1721  }
1722 
1723  saveDesktopSettings();
1724 
1725  // Resize and reset the desktop focus chain.
1726  desktop_focus_chain.resize( n );
1727  for( int i = 0; i < (int)desktop_focus_chain.size(); i++ )
1728  desktop_focus_chain[i] = i+1;
1729  }
1730 
1736 void Workspace::sendClientToDesktop( Client* c, int desk, bool dont_activate )
1737  {
1738  bool was_on_desktop = c->isOnDesktop( desk ) || c->isOnAllDesktops();
1739  c->setDesktop( desk );
1740  if ( c->desktop() != desk ) // no change or desktop forced
1741  return;
1742  desk = c->desktop(); // Client did range checking
1743 
1744  if ( c->isOnDesktop( currentDesktop() ) )
1745  {
1746  if ( c->wantsTabFocus() && options->focusPolicyIsReasonable()
1747  && !was_on_desktop // for stickyness changes
1748  && !dont_activate )
1749  requestFocus( c );
1750  else
1751  restackClientUnderActive( c );
1752  }
1753  else
1754  {
1755  raiseClient( c );
1756  }
1757 
1758  ClientList transients_stacking_order = ensureStackingOrder( c->transients());
1759  for( ClientList::ConstIterator it = transients_stacking_order.begin();
1760  it != transients_stacking_order.end();
1761  ++it )
1762  sendClientToDesktop( *it, desk, dont_activate );
1763  updateClientArea();
1764  }
1765 
1766 int Workspace::numScreens() const
1767  {
1768  if( !options->xineramaEnabled )
1769  return 0;
1770  return tqApp->desktop()->numScreens();
1771  }
1772 
1773 int Workspace::activeScreen() const
1774  {
1775  if( !options->xineramaEnabled )
1776  return 0;
1777  if( !options->activeMouseScreen )
1778  {
1779  if( activeClient() != NULL && !activeClient()->isOnScreen( active_screen ))
1780  return tqApp->desktop()->screenNumber( activeClient()->geometry().center());
1781  return active_screen;
1782  }
1783  return tqApp->desktop()->screenNumber( TQCursor::pos());
1784  }
1785 
1786 // check whether a client moved completely out of what's considered the active screen,
1787 // if yes, set a new active screen
1788 void Workspace::checkActiveScreen( const Client* c )
1789  {
1790  if( !options->xineramaEnabled )
1791  return;
1792  if( !c->isActive())
1793  return;
1794  if( !c->isOnScreen( active_screen ))
1795  active_screen = c->screen();
1796  }
1797 
1798 // called e.g. when a user clicks on a window, set active screen to be the screen
1799 // where the click occured
1800 void Workspace::setActiveScreenMouse( TQPoint mousepos )
1801  {
1802  if( !options->xineramaEnabled )
1803  return;
1804  active_screen = tqApp->desktop()->screenNumber( mousepos );
1805  }
1806 
1807 TQRect Workspace::screenGeometry( int screen ) const
1808  {
1809  if (( !options->xineramaEnabled ) || (kapp->desktop()->numScreens() < 2))
1810  return tqApp->desktop()->geometry();
1811  return tqApp->desktop()->screenGeometry( screen );
1812  }
1813 
1814 int Workspace::screenNumber( TQPoint pos ) const
1815  {
1816  if( !options->xineramaEnabled )
1817  return 0;
1818  return tqApp->desktop()->screenNumber( pos );
1819  }
1820 
1821 void Workspace::sendClientToScreen( Client* c, int screen )
1822  {
1823  if( c->screen() == screen ) // don't use isOnScreen(), that's true even when only partially
1824  return;
1825  GeometryUpdatesPostponer blocker( c );
1826  TQRect old_sarea = clientArea( MaximizeArea, c );
1827  TQRect sarea = clientArea( MaximizeArea, screen, c->desktop());
1828  c->setGeometry( sarea.x() - old_sarea.x() + c->x(), sarea.y() - old_sarea.y() + c->y(),
1829  c->size().width(), c->size().height());
1830  c->checkWorkspacePosition();
1831  ClientList transients_stacking_order = ensureStackingOrder( c->transients());
1832  for( ClientList::ConstIterator it = transients_stacking_order.begin();
1833  it != transients_stacking_order.end();
1834  ++it )
1835  sendClientToScreen( *it, screen );
1836  if( c->isActive())
1837  active_screen = screen;
1838  }
1839 
1840 
1841 void Workspace::setDesktopLayout( int, int, int )
1842  { // DCOP-only, unused
1843  }
1844 
1845 void Workspace::updateDesktopLayout()
1846  {
1847  // rootInfo->desktopLayoutCorner(); // I don't find this worth bothering, feel free to
1848  layoutOrientation = ( rootInfo->desktopLayoutOrientation() == NET::OrientationHorizontal
1849  ? TQt::Horizontal : TQt::Vertical );
1850  layoutX = rootInfo->desktopLayoutColumnsRows().width();
1851  layoutY = rootInfo->desktopLayoutColumnsRows().height();
1852  if( layoutX == 0 && layoutY == 0 ) // not given, set default layout
1853  layoutY = 2;
1854  }
1855 
1856 void Workspace::calcDesktopLayout(int &x, int &y) const
1857  {
1858  x = layoutX; // <= 0 means compute it from the other and total number of desktops
1859  y = layoutY;
1860  if((x <= 0) && (y > 0))
1861  x = (numberOfDesktops()+y-1) / y;
1862  else if((y <=0) && (x > 0))
1863  y = (numberOfDesktops()+x-1) / x;
1864 
1865  if(x <=0)
1866  x = 1;
1867  if (y <= 0)
1868  y = 1;
1869  }
1870 
1875 bool Workspace::addSystemTrayWin( WId w )
1876  {
1877  if ( systemTrayWins.contains( w ) )
1878  return TRUE;
1879 
1880  NETWinInfo ni( tqt_xdisplay(), w, root, NET::WMKDESystemTrayWinFor );
1881  WId trayWinFor = ni.kdeSystemTrayWinFor();
1882  if ( !trayWinFor )
1883  return FALSE;
1884  systemTrayWins.append( SystemTrayWindow( w, trayWinFor ) );
1885  XSelectInput( tqt_xdisplay(), w,
1886  StructureNotifyMask
1887  );
1888  XAddToSaveSet( tqt_xdisplay(), w );
1889  propagateSystemTrayWins();
1890  return TRUE;
1891  }
1892 
1897 bool Workspace::removeSystemTrayWin( WId w, bool check )
1898  {
1899  if ( !systemTrayWins.contains( w ) )
1900  return FALSE;
1901  if( check )
1902  {
1903  // When getting UnmapNotify, it's not clear if it's the systray
1904  // reparenting the window into itself, or if it's the window
1905  // going away. This is obviously a flaw in the design, and we were
1906  // just lucky it worked for so long. Kicker's systray temporarily
1907  // sets _TDE_SYSTEM_TRAY_EMBEDDING property on the window while
1908  // embedding it, allowing KWin to figure out. Kicker just mustn't
1909  // crash before removing it again ... *shrug* .
1910  int num_props;
1911  Atom* props = XListProperties( tqt_xdisplay(), w, &num_props );
1912  if( props != NULL )
1913  {
1914  for( int i = 0;
1915  i < num_props;
1916  ++i )
1917  if( props[ i ] == atoms->kde_system_tray_embedding )
1918  {
1919  XFree( props );
1920  return false;
1921  }
1922  XFree( props );
1923  }
1924  }
1925  systemTrayWins.remove( w );
1926  XRemoveFromSaveSet (tqt_xdisplay (), w);
1927  propagateSystemTrayWins();
1928  return TRUE;
1929  }
1930 
1931 
1935 void Workspace::propagateSystemTrayWins()
1936  {
1937  Window *cl = new Window[ systemTrayWins.count()];
1938 
1939  int i = 0;
1940  for ( SystemTrayWindowList::ConstIterator it = systemTrayWins.begin(); it != systemTrayWins.end(); ++it )
1941  {
1942  cl[i++] = (*it).win;
1943  }
1944 
1945  rootInfo->setKDESystemTrayWindows( cl, i );
1946  delete [] cl;
1947  }
1948 
1949 
1950 void Workspace::killWindowId( Window window_to_kill )
1951  {
1952  if( window_to_kill == None )
1953  return;
1954  Window window = window_to_kill;
1955  Client* client = NULL;
1956  for(;;)
1957  {
1958  client = findClient( FrameIdMatchPredicate( window ));
1959  if( client != NULL ) // found the client
1960  break;
1961  Window parent = 0L;
1962  Window root = 0L;
1963  Window* children = 0L;
1964  unsigned int children_count;
1965  XQueryTree( tqt_xdisplay(), window, &root, &parent, &children, &children_count );
1966  if( children != NULL )
1967  XFree( children );
1968  if( window == root ) // we didn't find the client, probably an override-redirect window
1969  break;
1970  window = parent; // go up
1971  if( window == 0L )
1972  break;
1973  }
1974  if( client != NULL )
1975  client->killWindow();
1976  else
1977  XKillClient( tqt_xdisplay(), window_to_kill );
1978  }
1979 
1980 void Workspace::suspendWindowId( Window window_to_suspend )
1981  {
1982  if( window_to_suspend == None )
1983  return;
1984  Window window = window_to_suspend;
1985  Client* client = NULL;
1986  for(;;)
1987  {
1988  client = findClient( FrameIdMatchPredicate( window ));
1989  if( client != NULL ) // found the client
1990  break;
1991  Window parent = 0L;
1992  Window root = 0L;
1993  Window* children = 0L;
1994  unsigned int children_count;
1995  XQueryTree( tqt_xdisplay(), window, &root, &parent, &children, &children_count );
1996  if( children != NULL )
1997  XFree( children );
1998  if( window == root ) // we didn't find the client, probably an override-redirect window
1999  break;
2000  window = parent; // go up
2001  if( window == 0L )
2002  break;
2003  }
2004  if( client != NULL )
2005  client->suspendWindow();
2006  else
2007  return;
2008  }
2009 
2010 void Workspace::resumeWindowId( Window window_to_resume )
2011  {
2012  if( window_to_resume == None )
2013  return;
2014  Window window = window_to_resume;
2015  Client* client = NULL;
2016  for(;;)
2017  {
2018  client = findClient( FrameIdMatchPredicate( window ));
2019  if( client != NULL ) // found the client
2020  break;
2021  Window parent = 0L;
2022  Window root = 0L;
2023  Window* children = 0L;
2024  unsigned int children_count;
2025  XQueryTree( tqt_xdisplay(), window, &root, &parent, &children, &children_count );
2026  if( children != NULL )
2027  XFree( children );
2028  if( window == root ) // we didn't find the client, probably an override-redirect window
2029  break;
2030  window = parent; // go up
2031  if( window == 0L )
2032  break;
2033  }
2034  if( client != NULL )
2035  client->resumeWindow();
2036  else
2037  return;
2038  }
2039 
2040 
2041 bool Workspace::isResumeableWindowID( Window window_to_check )
2042  {
2043  if( window_to_check == None )
2044  return false;
2045  Window window = window_to_check;
2046  Client* client = NULL;
2047  for(;;)
2048  {
2049  client = findClient( FrameIdMatchPredicate( window ));
2050  if( client != NULL ) // found the client
2051  break;
2052  Window parent = 0L;
2053  Window root = 0L;
2054  Window* children = 0L;
2055  unsigned int children_count;
2056  XQueryTree( tqt_xdisplay(), window, &root, &parent, &children, &children_count );
2057  if( children != NULL )
2058  XFree( children );
2059  if( window == root ) // we didn't find the client, probably an override-redirect window
2060  break;
2061  window = parent; // go up
2062  if( window == 0L )
2063  break;
2064  }
2065  if( client != NULL )
2066  return client->isResumeable();
2067  else
2068  return false;
2069  }
2070 
2071 
2072 void Workspace::sendPingToWindow( Window window, Time timestamp )
2073  {
2074  rootInfo->sendPing( window, timestamp );
2075  }
2076 
2077 void Workspace::sendTakeActivity( Client* c, Time timestamp, long flags )
2078  {
2079  rootInfo->takeActivity( c->window(), timestamp, flags );
2080  pending_take_activity = c;
2081  }
2082 
2083 
2087 void Workspace::slotGrabWindow()
2088  {
2089  if ( active_client )
2090  {
2091  TQPixmap snapshot = TQPixmap::grabWindow( active_client->frameId() );
2092 
2093  //No XShape - no work.
2094  if( Shape::available())
2095  {
2096  //As the first step, get the mask from XShape.
2097  int count, order;
2098  XRectangle* rects = XShapeGetRectangles( tqt_xdisplay(), active_client->frameId(),
2099  ShapeBounding, &count, &order);
2100  //The ShapeBounding region is the outermost shape of the window;
2101  //ShapeBounding - ShapeClipping is defined to be the border.
2102  //Since the border area is part of the window, we use bounding
2103  // to limit our work region
2104  if (rects)
2105  {
2106  //Create a TQRegion from the rectangles describing the bounding mask.
2107  TQRegion contents;
2108  for (int pos = 0; pos < count; pos++)
2109  contents += TQRegion(rects[pos].x, rects[pos].y,
2110  rects[pos].width, rects[pos].height);
2111  XFree(rects);
2112 
2113  //Create the bounding box.
2114  TQRegion bbox(0, 0, snapshot.width(), snapshot.height());
2115 
2116  //Get the masked away area.
2117  TQRegion maskedAway = bbox - contents;
2118  TQMemArray<TQRect> maskedAwayRects = maskedAway.rects();
2119 
2120  //Construct a bitmap mask from the rectangles
2121  TQBitmap mask( snapshot.width(), snapshot.height());
2122  TQPainter p(&mask);
2123  p.fillRect(0, 0, mask.width(), mask.height(), TQt::color1);
2124  for (uint pos = 0; pos < maskedAwayRects.count(); pos++)
2125  p.fillRect(maskedAwayRects[pos], TQt::color0);
2126  p.end();
2127  snapshot.setMask(mask);
2128  }
2129  }
2130 
2131  TQClipboard *cb = TQApplication::clipboard();
2132  cb->setPixmap( snapshot );
2133  }
2134  else
2135  slotGrabDesktop();
2136  }
2137 
2141 void Workspace::slotGrabDesktop()
2142  {
2143  TQPixmap p = TQPixmap::grabWindow( tqt_xrootwin() );
2144  TQClipboard *cb = TQApplication::clipboard();
2145  cb->setPixmap( p );
2146  }
2147 
2148 
2152 void Workspace::slotMouseEmulation()
2153  {
2154 
2155  if ( mouse_emulation )
2156  {
2157  XUngrabKeyboard(tqt_xdisplay(), get_tqt_x_time());
2158  mouse_emulation = FALSE;
2159  return;
2160  }
2161 
2162  if ( XGrabKeyboard(tqt_xdisplay(),
2163  root, FALSE,
2164  GrabModeAsync, GrabModeAsync,
2165  get_tqt_x_time()) == GrabSuccess )
2166  {
2167  mouse_emulation = TRUE;
2168  mouse_emulation_state = 0;
2169  mouse_emulation_window = 0;
2170  }
2171  }
2172 
2179 WId Workspace::getMouseEmulationWindow()
2180  {
2181  Window root;
2182  Window child = tqt_xrootwin();
2183  int root_x, root_y, lx, ly;
2184  uint state;
2185  Window w;
2186  Client * c = 0;
2187  do
2188  {
2189  w = child;
2190  if (!c)
2191  c = findClient( FrameIdMatchPredicate( w ));
2192  XQueryPointer( tqt_xdisplay(), w, &root, &child,
2193  &root_x, &root_y, &lx, &ly, &state );
2194  } while ( child != None && child != w );
2195 
2196  if ( c && !c->isActive() )
2197  activateClient( c );
2198  return (WId) w;
2199  }
2200 
2204 unsigned int Workspace::sendFakedMouseEvent( TQPoint pos, WId w, MouseEmulation type, int button, unsigned int state )
2205  {
2206  if ( !w )
2207  return state;
2208  TQWidget* widget = TQWidget::find( w );
2209  if ( (!widget || widget->inherits("TQToolButton") ) && !findClient( WindowMatchPredicate( w )) )
2210  {
2211  int x, y;
2212  Window xw;
2213  XTranslateCoordinates( tqt_xdisplay(), tqt_xrootwin(), w, pos.x(), pos.y(), &x, &y, &xw );
2214  if ( type == EmuMove )
2215  { // motion notify events
2216  XEvent e;
2217  e.type = MotionNotify;
2218  e.xmotion.window = w;
2219  e.xmotion.root = tqt_xrootwin();
2220  e.xmotion.subwindow = w;
2221  e.xmotion.time = get_tqt_x_time();
2222  e.xmotion.x = x;
2223  e.xmotion.y = y;
2224  e.xmotion.x_root = pos.x();
2225  e.xmotion.y_root = pos.y();
2226  e.xmotion.state = state;
2227  e.xmotion.is_hint = NotifyNormal;
2228  XSendEvent( tqt_xdisplay(), w, TRUE, ButtonMotionMask, &e );
2229  }
2230  else
2231  {
2232  XEvent e;
2233  e.type = type == EmuRelease ? ButtonRelease : ButtonPress;
2234  e.xbutton.window = w;
2235  e.xbutton.root = tqt_xrootwin();
2236  e.xbutton.subwindow = w;
2237  e.xbutton.time = get_tqt_x_time();
2238  e.xbutton.x = x;
2239  e.xbutton.y = y;
2240  e.xbutton.x_root = pos.x();
2241  e.xbutton.y_root = pos.y();
2242  e.xbutton.state = state;
2243  e.xbutton.button = button;
2244  XSendEvent( tqt_xdisplay(), w, TRUE, ButtonPressMask, &e );
2245 
2246  if ( type == EmuPress )
2247  {
2248  switch ( button )
2249  {
2250  case 2:
2251  state |= Button2Mask;
2252  break;
2253  case 3:
2254  state |= Button3Mask;
2255  break;
2256  default: // 1
2257  state |= Button1Mask;
2258  break;
2259  }
2260  }
2261  else
2262  {
2263  switch ( button )
2264  {
2265  case 2:
2266  state &= ~Button2Mask;
2267  break;
2268  case 3:
2269  state &= ~Button3Mask;
2270  break;
2271  default: // 1
2272  state &= ~Button1Mask;
2273  break;
2274  }
2275  }
2276  }
2277  }
2278  return state;
2279  }
2280 
2284 bool Workspace::keyPressMouseEmulation( XKeyEvent& ev )
2285  {
2286  if ( root != tqt_xrootwin() )
2287  return FALSE;
2288  int kc = XkbKeycodeToKeysym(tqt_xdisplay(), ev.keycode, 0, 0);
2289  int km = ev.state & (ControlMask | Mod1Mask | ShiftMask);
2290 
2291  bool is_control = km & ControlMask;
2292  bool is_alt = km & Mod1Mask;
2293  bool is_shift = km & ShiftMask;
2294  int delta = is_control?1:is_alt?32:8;
2295  TQPoint pos = TQCursor::pos();
2296 
2297  switch ( kc )
2298  {
2299  case XK_Left:
2300  case XK_KP_Left:
2301  pos.rx() -= delta;
2302  break;
2303  case XK_Right:
2304  case XK_KP_Right:
2305  pos.rx() += delta;
2306  break;
2307  case XK_Up:
2308  case XK_KP_Up:
2309  pos.ry() -= delta;
2310  break;
2311  case XK_Down:
2312  case XK_KP_Down:
2313  pos.ry() += delta;
2314  break;
2315  case XK_F1:
2316  if ( !mouse_emulation_state )
2317  mouse_emulation_window = getMouseEmulationWindow();
2318  if ( (mouse_emulation_state & Button1Mask) == 0 )
2319  mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button1, mouse_emulation_state );
2320  if ( !is_shift )
2321  mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state );
2322  break;
2323  case XK_F2:
2324  if ( !mouse_emulation_state )
2325  mouse_emulation_window = getMouseEmulationWindow();
2326  if ( (mouse_emulation_state & Button2Mask) == 0 )
2327  mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button2, mouse_emulation_state );
2328  if ( !is_shift )
2329  mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button2, mouse_emulation_state );
2330  break;
2331  case XK_F3:
2332  if ( !mouse_emulation_state )
2333  mouse_emulation_window = getMouseEmulationWindow();
2334  if ( (mouse_emulation_state & Button3Mask) == 0 )
2335  mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button3, mouse_emulation_state );
2336  if ( !is_shift )
2337  mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button3, mouse_emulation_state );
2338  break;
2339  case XK_Return:
2340  case XK_space:
2341  case XK_KP_Enter:
2342  case XK_KP_Space:
2343  {
2344  if ( !mouse_emulation_state )
2345  {
2346  // nothing was pressed, fake a LMB click
2347  mouse_emulation_window = getMouseEmulationWindow();
2348  mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuPress, Button1, mouse_emulation_state );
2349  mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state );
2350  }
2351  else
2352  { // release all
2353  if ( mouse_emulation_state & Button1Mask )
2354  mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button1, mouse_emulation_state );
2355  if ( mouse_emulation_state & Button2Mask )
2356  mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button2, mouse_emulation_state );
2357  if ( mouse_emulation_state & Button3Mask )
2358  mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuRelease, Button3, mouse_emulation_state );
2359  }
2360  }
2361  // fall through
2362  case XK_Escape:
2363  XUngrabKeyboard(tqt_xdisplay(), get_tqt_x_time());
2364  mouse_emulation = FALSE;
2365  return TRUE;
2366  default:
2367  return FALSE;
2368  }
2369 
2370  TQCursor::setPos( pos );
2371  if ( mouse_emulation_state )
2372  mouse_emulation_state = sendFakedMouseEvent( pos, mouse_emulation_window, EmuMove, 0, mouse_emulation_state );
2373  return TRUE;
2374 
2375  }
2376 
2382 TQWidget* Workspace::desktopWidget()
2383  {
2384  return desktop_widget;
2385  }
2386 
2387 //Delayed focus functions
2388 void Workspace::delayFocus()
2389  {
2390  requestFocus( delayfocus_client );
2391  cancelDelayFocus();
2392  }
2393 
2394 void Workspace::requestDelayFocus( Client* c )
2395  {
2396  delayfocus_client = c;
2397  delete delayFocusTimer;
2398  delayFocusTimer = new TQTimer( this );
2399  connect( delayFocusTimer, TQ_SIGNAL( timeout() ), this, TQ_SLOT( delayFocus() ) );
2400  delayFocusTimer->start( options->delayFocusInterval, TRUE );
2401  }
2402 
2403 void Workspace::cancelDelayFocus()
2404  {
2405  delete delayFocusTimer;
2406  delayFocusTimer = 0;
2407  }
2408 
2409 /* Active (Electric) Borders
2410  * ========================================================================
2411  * Active Border Window management. Active borders allow a user to switch
2412  * to another virtual desktop or activate other features by moving
2413  * the mouse pointer to the borders or corners of the workspace.
2414  * Technically this is done with input only windows.
2415  */
2416 void Workspace::updateActiveBorders()
2417  {
2418  active_time_first = get_tqt_x_time();
2419  active_time_last = get_tqt_x_time();
2420  active_time_last_trigger = get_tqt_x_time();
2421  active_current_border = ActiveNone;
2422  TQRect r = TQApplication::desktop()->geometry();
2423  activeTop = r.top();
2424  activeBottom = r.bottom();
2425  activeLeft = r.left();
2426  activeRight = r.right();
2427 
2428  for (int pos = 0; pos < ACTIVE_BORDER_COUNT; ++pos)
2429  {
2430  if (active_reserved[pos] == 0)
2431  {
2432  if (active_windows[pos] != None)
2433  {
2434  XDestroyWindow( tqt_xdisplay(), active_windows[pos] );
2435  }
2436  active_windows[pos] = None;
2437  continue;
2438  }
2439 
2440  if (active_windows[pos] != None)
2441  {
2442  continue;
2443  }
2444 
2445  XSetWindowAttributes attributes;
2446  attributes.override_redirect = True;
2447  attributes.event_mask = EnterWindowMask;
2448  unsigned long valuemask = CWOverrideRedirect | CWEventMask;
2449  int xywh[ ACTIVE_BORDER_COUNT ][ 4 ] =
2450  {
2451  { r.left() + 1, r.top(), r.width() - 2, 1 }, // top
2452  { r.right(), r.top(), 1, 1 }, // topright
2453  { r.right(), r.top() + 1, 1, r.height() - 2 }, // etc.
2454  { r.right(), r.bottom(), 1, 1 },
2455  { r.left() + 1, r.bottom(), r.width() - 2, 1 },
2456  { r.left(), r.bottom(), 1, 1 },
2457  { r.left(), r.top() + 1, 1, r.height() - 2 },
2458  { r.left(), r.top(), 1, 1 }
2459  };
2460  active_windows[pos] = XCreateWindow(tqt_xdisplay(), tqt_xrootwin(),
2461  xywh[pos][0], xywh[pos][1],
2462  xywh[pos][2], xywh[pos][3],
2463  0, CopyFromParent, InputOnly,
2464  CopyFromParent, valuemask,
2465  &attributes);
2466  XMapWindow(tqt_xdisplay(), active_windows[pos]);
2467 
2468  // Set XdndAware on the windows, so that DND enter events are received (#86998)
2469  Atom version = 4; // XDND version
2470  XChangeProperty(tqt_xdisplay(), active_windows[pos],
2471  atoms->xdnd_aware, XA_ATOM, 32, PropModeReplace,
2472  (unsigned char*)&version, 1);
2473  }
2474 }
2475 
2476 void Workspace::destroyActiveBorders()
2477 {
2478  for (int pos = 0; pos < ACTIVE_BORDER_COUNT; ++pos)
2479  {
2480  if (active_windows[ pos ] != None)
2481  {
2482  XDestroyWindow( tqt_xdisplay(), active_windows[ pos ] );
2483  }
2484  active_windows[ pos ] = None;
2485  }
2486 }
2487 
2488 void Workspace::reserveActiveBorderSwitching( bool reserve )
2489 {
2490  for (int pos = 0; pos < ACTIVE_BORDER_COUNT; ++pos)
2491  {
2492  if (reserve)
2493  {
2494  reserveActiveBorder(static_cast<ActiveBorder>(pos));
2495  }
2496  else
2497  {
2498  unreserveActiveBorder(static_cast<ActiveBorder>(pos));
2499  }
2500  }
2501 }
2502 
2503 void Workspace::reserveActiveBorder( ActiveBorder border )
2504 {
2505  if (border == ActiveNone)
2506  return;
2507 
2508  if (active_reserved[border]++ == 0)
2509  TQTimer::singleShot(0, this, TQ_SLOT(updateActiveBorders()));
2510 }
2511 
2512 void Workspace::unreserveActiveBorder( ActiveBorder border )
2513 {
2514  if (border == ActiveNone)
2515  return;
2516 
2517  assert(active_reserved[ border ] > 0);
2518  if (--active_reserved[ border ] == 0)
2519  TQTimer::singleShot(0, this, TQ_SLOT(updateActiveBorders()));
2520 }
2521 
2522 void Workspace::checkActiveBorder(const TQPoint &pos, Time now)
2523 {
2524  Time treshold_set = options->activeBorderDelay(); // set timeout
2525  Time treshold_trigger = 250; // Minimum time between triggers
2526  Time treshold_reset = 250; // reset timeout
2527  int activation_distance = options->borderActivationDistance();
2528 
2529  bool have_borders = false;
2530  for (int i = 0; i < ACTIVE_BORDER_COUNT; ++i)
2531  {
2532  if (active_windows[ i ] != None)
2533  {
2534  have_borders = true;
2535  }
2536  }
2537  if (!have_borders) {
2538  return;
2539  }
2540 
2541  // Mouse should not move more than this many pixels
2542  int distance_reset = activation_distance + 10;
2543 
2544  // Leave active maximizing mode when window moved away
2545  if (active_current_border != ActiveNone &&
2546  (pos.x() > activeLeft + distance_reset) &&
2547  (pos.x() < activeRight - distance_reset) &&
2548  (pos.y() > activeTop + distance_reset) &&
2549  (pos.y() < activeBottom - distance_reset))
2550  {
2551  if (movingClient &&
2552  (options->activeBorders() == Options::ActiveTileMaximize ||
2553  options->activeBorders() == Options::ActiveTileOnly))
2554  {
2555  movingClient->cancelActiveBorderMaximizing();
2556  return;
2557  }
2558  }
2559 
2560  // These checks take activation distance into account, creating a
2561  // virtual "activation band" for easier border/corner activation.
2562  bool active_left = pos.x() < activeLeft + activation_distance;
2563  bool active_right = pos.x() > activeRight - activation_distance;
2564  bool active_top = pos.y() < activeTop + activation_distance;
2565  bool active_bottom = pos.y() > activeBottom - activation_distance;
2566 
2567  if (!active_left && !active_right && !active_top && !active_bottom)
2568  return;
2569 
2570  // These checks are used to make corner activation easier: we assume
2571  // a 25% zone on the edge of each border where instead of half size
2572  // tiling we perform quarter size tiling. The rest 50% is left for
2573  // normal half size tiling.
2574  // These options make sense only for the tiling mode.
2575  int active_width_quart = (activeRight - activeLeft) / 4;
2576  int active_height_quart = (activeBottom - activeTop) / 4;
2577 
2578  bool active_qleft = false;
2579  bool active_qright = false;
2580  bool active_qtop = false;
2581  bool active_qbottom = false;
2582  if (options->activeBorders() == Options::ActiveTileMaximize ||
2583  options->activeBorders() == Options::ActiveTileOnly)
2584  {
2585  active_qleft = pos.x() < activeLeft + active_width_quart;
2586  active_qright = pos.x() > activeRight - active_width_quart;
2587  active_qtop = pos.y() < activeTop + active_height_quart;
2588  active_qbottom = pos.y() > activeBottom - active_height_quart;
2589  }
2590 
2591  ActiveBorder border = ActiveNone;
2592  if ((active_left && active_qtop) || (active_top && active_qleft))
2593  {
2594  border = ActiveTopLeft;
2595  }
2596  else if ((active_right && active_qtop) || (active_top && active_qright))
2597  {
2598  border = ActiveTopRight;
2599  }
2600  else if ((active_left && active_qbottom) || (active_bottom && active_qleft))
2601  {
2602  border = ActiveBottomLeft;
2603  }
2604  else if ((active_right && active_qbottom) || (active_bottom && active_qright))
2605  {
2606  border = ActiveBottomRight;
2607  }
2608  else if (active_left)
2609  {
2610  border = ActiveLeft;
2611  }
2612  else if (active_right)
2613  {
2614  border = ActiveRight;
2615  }
2616  else if (active_top)
2617  {
2618  border = ActiveTop;
2619  }
2620  else if (active_bottom)
2621  {
2622  border = ActiveBottom;
2623  }
2624  else
2625  {
2626  // Should never happen
2627  abort();
2628  }
2629 
2630  if( active_windows[border] == None )
2631  {
2632  return;
2633  }
2634 
2635  if ((active_current_border == border) &&
2636  (timestampDiff(active_time_last, now) < treshold_reset) &&
2637  (timestampDiff(active_time_last_trigger, now) > treshold_trigger) &&
2638  ((pos-active_push_point).manhattanLength() < distance_reset))
2639  {
2640  active_time_last = now;
2641  if (timestampDiff(active_time_first, now) > treshold_set)
2642  {
2643  active_time_last_trigger = now;
2644  active_current_border = ActiveNone;
2645  bool isSide = (border == ActiveTop || border == ActiveRight ||
2646  border == ActiveBottom || border == ActiveLeft);
2647 
2648  if (movingClient)
2649  {
2650  // Desktop switching
2651  if (options->activeBorders() == Options::ActiveSwitchAlways ||
2652  options->activeBorders() == Options::ActiveSwitchOnMove)
2653  {
2654  activeBorderSwitchDesktop(border, pos);
2655  return; // Don't reset cursor position
2656  }
2657 
2658  // Tiling maximize
2659  else if (options->activeBorders() == Options::ActiveTileMaximize &&
2660  border == ActiveTop && movingClient->isMaximizable())
2661  {
2662  if (!movingClient->isResizable()) return;
2663  movingClient->setActiveBorderMode(ActiveMaximizeMode);
2664  movingClient->setActiveBorder(ActiveNone);
2665  movingClient->setActiveBorderMaximizing(true);
2666  }
2667 
2668  // Tiling
2669  else if ((options->activeBorders() == Options::ActiveTileMaximize ||
2670  options->activeBorders() == Options::ActiveTileOnly))
2671  {
2672  if (!movingClient->isResizable()) return;
2673  movingClient->setActiveBorderMode(ActiveTilingMode);
2674  movingClient->setActiveBorder(border);
2675  movingClient->setActiveBorderMaximizing(true);
2676  }
2677 
2678  else
2679  {
2680  return; // Don't reset cursor position
2681  }
2682  }
2683  else
2684  {
2685  // Desktop switching
2686  if (options->activeBorders() == Options::ActiveSwitchAlways && isSide)
2687  {
2688  activeBorderSwitchDesktop(border, pos);
2689  return; // Don't reset cursor position
2690  }
2691  }
2692  }
2693  }
2694  else
2695  {
2696  active_current_border = border;
2697  active_time_first = now;
2698  active_time_last = now;
2699  active_push_point = pos;
2700  }
2701 
2702  if ((options->activeBorders() == Options::ActiveSwitchAlways && !movingClient) ||
2703  activation_distance < 2)
2704  {
2705  // Reset the pointer to find out whether the user is really pushing
2706  // (ordered according to enum ActiveBorder minus ActiveNone)
2707  const int xdiff[ ACTIVE_BORDER_COUNT ] = { 0, -1, -1, -1, 0, 1, 1, 1 };
2708  const int ydiff[ ACTIVE_BORDER_COUNT ] = { 1, 1, 0, -1, -1, -1, 0, 1 };
2709  TQCursor::setPos(pos.x() + xdiff[border], pos.y() + ydiff[border]);
2710  }
2711 }
2712 
2713 void Workspace::activeBorderSwitchDesktop(ActiveBorder border, const TQPoint& _pos)
2714 {
2715  TQPoint pos = _pos;
2716  TQRect r = TQApplication::desktop()->geometry();
2717  const int offset = 5;
2718 
2719  int desk_before = currentDesktop();
2720  if (border == ActiveLeft || border == ActiveTopLeft || border == ActiveBottomLeft)
2721  {
2722  slotSwitchDesktopLeft();
2723  pos.setX(r.width() - offset);
2724  }
2725  if (border == ActiveRight || border == ActiveTopRight || border == ActiveBottomRight)
2726  {
2727  slotSwitchDesktopRight();
2728  pos.setX(offset);
2729  }
2730 
2731  if (border == ActiveTop || border == ActiveTopLeft || border == ActiveTopRight)
2732  {
2733  slotSwitchDesktopUp();
2734  pos.setY(r.height() - offset);
2735  }
2736  if (border == ActiveBottom || border == ActiveBottomLeft || border == ActiveBottomRight)
2737  {
2738  slotSwitchDesktopDown();
2739  pos.setY(offset);
2740  }
2741 
2742  if (currentDesktop() != desk_before)
2743  {
2744  TQCursor::setPos(pos);
2745  }
2746 }
2747 
2748 // this function is called when the user entered an active border
2749 // with the mouse. It may switch to another virtual desktop
2750 bool Workspace::activeBorderEvent(XEvent *e)
2751 {
2752  if (e->type == EnterNotify)
2753  {
2754  for (int i = 0; i < ACTIVE_BORDER_COUNT; ++i)
2755  {
2756  if (active_windows[i] != None && e->xcrossing.window == active_windows[i])
2757  { // the user entered an active border
2758  checkActiveBorder(TQPoint(e->xcrossing.x_root, e->xcrossing.y_root), e->xcrossing.time);
2759  return true;
2760  }
2761  }
2762  }
2763  if (e->type == ClientMessage)
2764  {
2765  if (e->xclient.message_type == atoms->xdnd_position)
2766  {
2767  for (int i = 0; i < ACTIVE_BORDER_COUNT; ++i)
2768  {
2769  if (active_windows[i] != None && e->xclient.window == active_windows[i])
2770  {
2771  updateXTime();
2772  checkActiveBorder(TQPoint(e->xclient.data.l[2]>>16, e->xclient.data.l[2]&0xffff), get_tqt_x_time());
2773  return true;
2774  }
2775  }
2776  }
2777  }
2778  return false;
2779 }
2780 
2781 void Workspace::addTopMenu( Client* c )
2782  {
2783  assert( c->isTopMenu());
2784  assert( !topmenus.contains( c ));
2785  topmenus.append( c );
2786  if( managingTopMenus())
2787  {
2788  int minsize = c->minSize().height();
2789  if( minsize > topMenuHeight())
2790  {
2791  topmenu_height = minsize;
2792  updateTopMenuGeometry();
2793  }
2794  updateTopMenuGeometry( c );
2795  updateCurrentTopMenu();
2796  }
2797 // kdDebug() << "NEW TOPMENU:" << c << endl;
2798  }
2799 
2800 void Workspace::removeTopMenu( Client* c )
2801  {
2802 // if( c->isTopMenu())
2803 // kdDebug() << "REMOVE TOPMENU:" << c << endl;
2804  assert( c->isTopMenu());
2805  assert( topmenus.contains( c ));
2806  topmenus.remove( c );
2807  updateCurrentTopMenu();
2808  // TODO reduce topMenuHeight() if possible?
2809  }
2810 
2811 void Workspace::lostTopMenuSelection()
2812  {
2813 // kdDebug() << "lost TopMenu selection" << endl;
2814  // make sure this signal is always set when not owning the selection
2815  disconnect( topmenu_watcher, TQ_SIGNAL( lostOwner()), this, TQ_SLOT( lostTopMenuOwner()));
2816  connect( topmenu_watcher, TQ_SIGNAL( lostOwner()), this, TQ_SLOT( lostTopMenuOwner()));
2817  if( !managing_topmenus )
2818  return;
2819  connect( topmenu_watcher, TQ_SIGNAL( lostOwner()), this, TQ_SLOT( lostTopMenuOwner()));
2820  disconnect( topmenu_selection, TQ_SIGNAL( lostOwnership()), this, TQ_SLOT( lostTopMenuSelection()));
2821  managing_topmenus = false;
2822  delete topmenu_space;
2823  topmenu_space = NULL;
2824  updateClientArea();
2825  for( ClientList::ConstIterator it = topmenus.begin();
2826  it != topmenus.end();
2827  ++it )
2828  (*it)->checkWorkspacePosition();
2829  }
2830 
2831 void Workspace::lostTopMenuOwner()
2832  {
2833  if( !options->topMenuEnabled())
2834  return;
2835 // kdDebug() << "TopMenu selection lost owner" << endl;
2836  if( !topmenu_selection->claim( false ))
2837  {
2838 // kdDebug() << "Failed to claim TopMenu selection" << endl;
2839  return;
2840  }
2841 // kdDebug() << "claimed TopMenu selection" << endl;
2842  setupTopMenuHandling();
2843  }
2844 
2845 void Workspace::setupTopMenuHandling()
2846  {
2847  if( managing_topmenus )
2848  return;
2849  connect( topmenu_selection, TQ_SIGNAL( lostOwnership()), this, TQ_SLOT( lostTopMenuSelection()));
2850  disconnect( topmenu_watcher, TQ_SIGNAL( lostOwner()), this, TQ_SLOT( lostTopMenuOwner()));
2851  managing_topmenus = true;
2852  topmenu_space = new TQWidget;
2853  Window stack[ 2 ];
2854  stack[ 0 ] = supportWindow->winId();
2855  stack[ 1 ] = topmenu_space->winId();
2856  XRestackWindows(tqt_xdisplay(), stack, 2);
2857  updateTopMenuGeometry();
2858  topmenu_space->show();
2859  updateClientArea();
2860  updateCurrentTopMenu();
2861  }
2862 
2863 int Workspace::topMenuHeight() const
2864  {
2865  if( topmenu_height == 0 )
2866  { // simply create a dummy menubar and use its preffered height as the menu height
2867  KMenuBar tmpmenu;
2868  tmpmenu.insertItem( "dummy" );
2869  topmenu_height = tmpmenu.sizeHint().height();
2870  }
2871  return topmenu_height;
2872  }
2873 
2874 KDecoration* Workspace::createDecoration( KDecorationBridge* bridge )
2875  {
2876  return mgr->createDecoration( bridge );
2877  }
2878 
2879 TQString Workspace::desktopName( int desk ) const
2880  {
2881  return TQString::fromUtf8( rootInfo->desktopName( desk ) );
2882  }
2883 
2884 bool Workspace::checkStartupNotification( Window w, TDEStartupInfoId& id, TDEStartupInfoData& data )
2885  {
2886  return startup->checkStartup( w, id, data ) == TDEStartupInfo::Match;
2887  }
2888 
2893 void Workspace::focusToNull()
2894  {
2895  XSetInputFocus(tqt_xdisplay(), null_focus_window, RevertToPointerRoot, get_tqt_x_time() );
2896  }
2897 
2898 void Workspace::helperDialog( const TQString& message, const Client* c )
2899  {
2900  TQStringList args;
2901  TQString type;
2902  if( message == "noborderaltf3" )
2903  {
2904  TQString shortcut = TQString( "%1 (%2)" ).arg( keys->label( "Window Operations Menu" ))
2905  .arg( keys->shortcut( "Window Operations Menu" ).seq( 0 ).toString());
2906  args << "--msgbox" <<
2907  i18n( "You have selected to show a window without its border.\n"
2908  "Without the border, you will not be able to enable the border "
2909  "again using the mouse: use the window operations menu instead, "
2910  "activated using the %1 keyboard shortcut." )
2911  .arg( shortcut );
2912  type = "altf3warning";
2913  }
2914  else if( message == "fullscreenaltf3" )
2915  {
2916  TQString shortcut = TQString( "%1 (%2)" ).arg( keys->label( "Window Operations Menu" ))
2917  .arg( keys->shortcut( "Window Operations Menu" ).seq( 0 ).toString());
2918  args << "--msgbox" <<
2919  i18n( "You have selected to show a window in fullscreen mode.\n"
2920  "If the application itself does not have an option to turn the fullscreen "
2921  "mode off you will not be able to disable it "
2922  "again using the mouse: use the window operations menu instead, "
2923  "activated using the %1 keyboard shortcut." )
2924  .arg( shortcut );
2925  type = "altf3warning";
2926  }
2927  else
2928  assert( false );
2929  TDEProcess proc;
2930  proc << "kdialog" << args;
2931  if( !type.isEmpty())
2932  {
2933  TDEConfig cfg( "twin_dialogsrc" );
2934  cfg.setGroup( "Notification Messages" ); // this depends on KMessageBox
2935  if( !cfg.readBoolEntry( type, true )) // has don't show again checked
2936  return; // save launching kdialog
2937  proc << "--dontagain" << "twin_dialogsrc:" + type;
2938  }
2939  if( c != NULL )
2940  proc << "--embed" << TQString::number( c->window());
2941  proc.start( TDEProcess::DontCare );
2942  }
2943 
2944 
2945 // kompmgr stuff
2946 
2947 void Workspace::startKompmgr()
2948 {
2949  // See if the desktop is loaded yet
2950  Atom type;
2951  int format;
2952  unsigned long length, after;
2953  unsigned char* data_root;
2954  Atom prop_root;
2955  prop_root = XInternAtom(tqt_xdisplay(), "_XROOTPMAP_ID", False);
2956  if( XGetWindowProperty( tqt_xdisplay(), tqt_xrootwin(), prop_root, 0L, 1L, False, AnyPropertyType, &type, &format, &length, &after, &data_root) == Success && data_root != NULL ) {
2957  // Root pixmap is available; OK to load...
2958  }
2959  else {
2960  // Try again a bit later!
2961  TQTimer::singleShot( 200, this, TQ_SLOT(startKompmgr()) );
2962  return;
2963  }
2964  pid_t kompmgrpid = getCompositorPID();
2965  if (kompmgrpid && kill(kompmgrpid, 0) >= 0)
2966  {
2967  // Active PID file detected; do not attempt to restart
2968  return;
2969  }
2970  if (!kompmgr || kompmgr->isRunning()) {
2971  kompmgrReloadSettings();
2972  return;
2973  }
2974  if (!kompmgr->start(TDEProcess::OwnGroup, TDEProcess::Stderr))
2975  {
2976  options->useTranslucency = FALSE;
2977  TDEProcess proc;
2978  proc << "kdialog" << "--error"
2979  << i18n("The Composite Manager could not be started.\\nMake sure you have \"" TDE_COMPOSITOR_BINARY "\" in a $PATH directory.")
2980  << "--title" << "Composite Manager Failure";
2981  proc.start(TDEProcess::DontCare);
2982  }
2983  else
2984  {
2985  delete kompmgr_selection;
2986  char selection_name[ 100 ];
2987  sprintf( selection_name, "_NET_WM_CM_S%d", DefaultScreen( tqt_xdisplay()));
2988  kompmgr_selection = new TDESelectionOwner( selection_name );
2989  connect( kompmgr_selection, TQ_SIGNAL( lostOwnership()), TQ_SLOT( stopKompmgr()));
2990  kompmgr_selection->claim( true );
2991  connect(kompmgr, TQ_SIGNAL(processExited(TDEProcess*)), TQ_SLOT(restartKompmgr(TDEProcess*)));
2992  options->useTranslucency = TRUE;
2993  //allowKompmgrRestart = FALSE;
2994  //TQTimer::singleShot( 60000, this, TQ_SLOT(unblockKompmgrRestart()) );
2995  TQByteArray ba;
2996  TQDataStream arg(ba, IO_WriteOnly);
2997  arg << "";
2998  kapp->dcopClient()->emitDCOPSignal("default", "kompmgrStarted()", ba);
2999  }
3000  if (popup){ delete popup; popup = 0L; } // to add/remove opacity slider
3001 }
3002 
3003 void Workspace::stopKompmgr()
3004 {
3005  if (!kompmgr || !kompmgr->isRunning()) {
3006  return;
3007  }
3008  delete kompmgr_selection;
3009  kompmgr_selection = NULL;
3010  kompmgr->disconnect(this, TQ_SLOT(restartKompmgr(TDEProcess*)));
3011  options->useTranslucency = FALSE;
3012  if (popup){ delete popup; popup = 0L; } // to add/remove opacity slider
3013  kompmgr->kill(SIGKILL);
3014  TQByteArray ba;
3015  TQDataStream arg(ba, IO_WriteOnly);
3016  arg << "";
3017  kapp->dcopClient()->emitDCOPSignal("default", "kompmgrStopped()", ba);
3018 }
3019 
3020 void Workspace::kompmgrReloadSettings()
3021 {
3022  if (!kompmgr || !kompmgr->isRunning()) {
3023  return;
3024  }
3025  kompmgr->kill(SIGUSR2);
3026 }
3027 
3028 bool Workspace::kompmgrIsRunning()
3029 {
3030  return kompmgr && kompmgr->isRunning();
3031 }
3032 
3033 void Workspace::unblockKompmgrRestart()
3034 {
3035  allowKompmgrRestart = TRUE;
3036 }
3037 
3038 void Workspace::restartKompmgr( TDEProcess *proc )
3039 // this is for inernal purpose (crashhandling) only, usually you want to use workspace->stopKompmgr(); TQTimer::singleShot(200, workspace, TQ_SLOT(startKompmgr()));
3040 {
3041  bool crashed;
3042  if (proc->signalled()) { // looks like kompmgr may have crashed
3043  int exit_signal_number = proc->exitSignal();
3044  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) ) {
3045  crashed = true;
3046  }
3047  else {
3048  crashed = false;
3049  }
3050  if (!allowKompmgrRestart) // uh oh, it exited recently already
3051  {
3052  delete kompmgr_selection;
3053  kompmgr_selection = NULL;
3054  options->useTranslucency = FALSE;
3055  if (crashed) {
3056  TDEProcess proc;
3057  proc << "kdialog" << "--error"
3058  << i18n( "The Composite Manager crashed twice within a minute and is therefore disabled for this session.")
3059  << "--title" << i18n("Composite Manager Failure");
3060  proc.start(TDEProcess::DontCare);
3061  }
3062  return;
3063  }
3064  if (!kompmgr)
3065  return;
3066 // this should be useless, i keep it for maybe future need
3067 // if (!kcompmgr)
3068 // {
3069 // kompmgr = new TDEProcess;
3070 // kompmgr->clearArguments();
3071 // *kompmgr << TDE_COMPOSITOR_BINARY;
3072 // }
3073 // -------------------
3074  if (!kompmgr->start(TDEProcess::NotifyOnExit, TDEProcess::Stderr))
3075  {
3076  delete kompmgr_selection;
3077  kompmgr_selection = NULL;
3078  options->useTranslucency = FALSE;
3079  TDEProcess proc;
3080  proc << "kdialog" << "--error"
3081  << i18n("The Composite Manager could not be started.\\nMake sure you have \"" TDE_COMPOSITOR_BINARY "\" in a $PATH directory.")
3082  << "--title" << i18n("Composite Manager Failure");
3083  proc.start(TDEProcess::DontCare);
3084  }
3085  else
3086  {
3087  allowKompmgrRestart = FALSE;
3088  TQTimer::singleShot( 60000, this, TQ_SLOT(unblockKompmgrRestart()) );
3089  }
3090  }
3091 }
3092 
3093 void Workspace::handleKompmgrOutput( TDEProcess* , char *buffer, int buflen)
3094 {
3095  TQString message;
3096  TQString output = TQString::fromLocal8Bit( buffer, buflen );
3097  if (output.contains("Started",false))
3098  ; // don't do anything, just pass to the connection release
3099  else if (output.contains("Can't open display",false))
3100  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>");
3101  else if (output.contains("No render extension",false))
3102  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 &ge; 6.8 from www.freedesktop.org.<br></qt>");
3103  else if (output.contains("No composite extension",false))
3104  message = i18n("<qt><b>Composite extension not found</b><br>You <i>must</i> use XOrg &ge; 6.8 for translucency and shadows to work.<br>Additionally, you need to add a new section to your X config file:<br>"
3105  "<i>Section \"Extensions\"<br>"
3106  "Option \"Composite\" \"Enable\"<br>"
3107  "EndSection</i></qt>");
3108  else if (output.contains("No damage extension",false))
3109  message = i18n("<qt><b>Damage extension not found</b><br>You <i>must</i> use XOrg &ge; 6.8 for translucency and shadows to work.</qt>");
3110  else if (output.contains("No XFixes extension",false))
3111  message = i18n("<qt><b>XFixes extension not found</b><br>You <i>must</i> use XOrg &ge; 6.8 for translucency and shadows to work.</qt>");
3112  else return; //skip others
3113  // kompmgr startup failed or succeeded, release connection
3114  kompmgr->closeStderr();
3115  disconnect(kompmgr, TQ_SIGNAL(receivedStderr(TDEProcess*, char*, int)), this, TQ_SLOT(handleKompmgrOutput(TDEProcess*, char*, int)));
3116  if( !message.isEmpty())
3117  {
3118  TDEProcess proc;
3119  proc << "kdialog" << "--error"
3120  << message
3121  << "--title" << i18n("Composite Manager Failure");
3122  proc.start(TDEProcess::DontCare);
3123  }
3124 }
3125 
3126 void Workspace::setOpacity(unsigned long winId, unsigned int opacityPercent)
3127 {
3128  if (opacityPercent > 100) opacityPercent = 100;
3129  for( ClientList::ConstIterator it = stackingOrder().begin(); it != stackingOrder().end(); it++ )
3130  if (winId == (*it)->window())
3131  {
3132  (*it)->setOpacity(opacityPercent < 100, (unsigned int)((opacityPercent/100.0)*0xFFFFFFFF));
3133  return;
3134  }
3135 }
3136 
3137 void Workspace::setShadowSize(unsigned long winId, unsigned int shadowSizePercent)
3138 {
3139  //this is open to the user by dcop - to avoid stupid trials, we limit the max shadow size to 400%
3140  if (shadowSizePercent > 400) shadowSizePercent = 400;
3141  for( ClientList::ConstIterator it = stackingOrder().begin(); it != stackingOrder().end(); it++ )
3142  if (winId == (*it)->window())
3143  {
3144  (*it)->setShadowSize(shadowSizePercent);
3145  return;
3146  }
3147 }
3148 
3149 void Workspace::setUnshadowed(unsigned long winId)
3150 {
3151  for( ClientList::ConstIterator it = stackingOrder().begin(); it != stackingOrder().end(); it++ )
3152  if (winId == (*it)->window())
3153  {
3154  (*it)->setShadowSize(0);
3155  return;
3156  }
3157 }
3158 
3159 void Workspace::setShowingDesktop( bool showing )
3160  {
3161  rootInfo->setShowingDesktop( showing );
3162  showing_desktop = showing;
3163  ++block_showing_desktop;
3164  if( showing_desktop )
3165  {
3166  showing_desktop_clients.clear();
3167  ++block_focus;
3168  ClientList cls = stackingOrder();
3169  // find them first, then minimize, otherwise transients may get minimized with the window
3170  // they're transient for
3171  for( ClientList::ConstIterator it = cls.begin();
3172  it != cls.end();
3173  ++it )
3174  {
3175  if( (*it)->isOnCurrentDesktop() && (*it)->isShown( true ) && !(*it)->isSpecialWindow())
3176  showing_desktop_clients.prepend( *it ); // topmost first to reduce flicker
3177  }
3178  for( ClientList::ConstIterator it = showing_desktop_clients.begin();
3179  it != showing_desktop_clients.end();
3180  ++it )
3181  (*it)->minimize(true);
3182  --block_focus;
3183  if( Client* desk = findDesktop( true, currentDesktop()))
3184  requestFocus( desk );
3185  }
3186  else
3187  {
3188  for( ClientList::ConstIterator it = showing_desktop_clients.begin();
3189  it != showing_desktop_clients.end();
3190  ++it )
3191  (*it)->unminimize(true);
3192  if( showing_desktop_clients.count() > 0 )
3193  requestFocus( showing_desktop_clients.first());
3194  showing_desktop_clients.clear();
3195  }
3196  --block_showing_desktop;
3197  }
3198 
3199 // Following Kicker's behavior:
3200 // Changing a virtual desktop resets the state and shows the windows again.
3201 // Unminimizing a window resets the state but keeps the windows hidden (except
3202 // the one that was unminimized).
3203 // A new window resets the state and shows the windows again, with the new window
3204 // being active. Due to popular demand (#67406) by people who apparently
3205 // don't see a difference between "show desktop" and "minimize all", this is not
3206 // true if "showDesktopIsMinimizeAll" is set in twinrc. In such case showing
3207 // a new window resets the state but doesn't show windows.
3208 void Workspace::resetShowingDesktop( bool keep_hidden )
3209  {
3210  if( block_showing_desktop > 0 )
3211  return;
3212  rootInfo->setShowingDesktop( false );
3213  showing_desktop = false;
3214  ++block_showing_desktop;
3215  if( !keep_hidden )
3216  {
3217  for( ClientList::ConstIterator it = showing_desktop_clients.begin();
3218  it != showing_desktop_clients.end();
3219  ++it )
3220  (*it)->unminimize(true);
3221  }
3222  showing_desktop_clients.clear();
3223  --block_showing_desktop;
3224  }
3225 
3226 // Activating/deactivating this feature works like this:
3227 // When nothing is active, and the shortcut is pressed, global shortcuts are disabled
3228 // (using global_shortcuts_disabled)
3229 // When a window that has disabling forced is activated, global shortcuts are disabled.
3230 // (using global_shortcuts_disabled_for_client)
3231 // When a shortcut is pressed and global shortcuts are disabled (either by a shortcut
3232 // or for a client), they are enabled again.
3233 void Workspace::slotDisableGlobalShortcuts()
3234  {
3235  if( global_shortcuts_disabled || global_shortcuts_disabled_for_client )
3236  disableGlobalShortcuts( false );
3237  else
3238  disableGlobalShortcuts( true );
3239  }
3240 
3241 static bool pending_dfc = false;
3242 
3243 void Workspace::disableGlobalShortcutsForClient( bool disable )
3244  {
3245  if( global_shortcuts_disabled_for_client == disable )
3246  return;
3247  if( !global_shortcuts_disabled )
3248  {
3249  if( disable )
3250  pending_dfc = true;
3251  KIPC::sendMessageAll( KIPC::BlockShortcuts, disable );
3252  // twin will get the kipc message too
3253  }
3254  }
3255 
3256 void Workspace::disableGlobalShortcuts( bool disable )
3257  {
3258  KIPC::sendMessageAll( KIPC::BlockShortcuts, disable );
3259  // twin will get the kipc message too
3260  }
3261 
3262 void Workspace::kipcMessage( int id, int data )
3263  {
3264  if( id != KIPC::BlockShortcuts )
3265  return;
3266  if( pending_dfc && data )
3267  {
3268  global_shortcuts_disabled_for_client = true;
3269  pending_dfc = false;
3270  }
3271  else
3272  {
3273  global_shortcuts_disabled = data;
3274  global_shortcuts_disabled_for_client = false;
3275  }
3276  // update also Alt+LMB actions etc.
3277  for( ClientList::ConstIterator it = clients.begin();
3278  it != clients.end();
3279  ++it )
3280  (*it)->updateMouseGrab();
3281  }
3282 
3283 } // namespace
3284 
3285 #include "workspace.moc"

twin

Skip menu "twin"
  • Main Page
  • Alphabetical List
  • Class List
  • File List
  • Class Members

twin

Skip menu "twin"
  • kate
  • libkonq
  • twin
  •   lib
Generated for twin by doxygen 1.9.1
This website is maintained by Timothy Pearson.