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