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

twin

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

twin

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

twin

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