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

kwin

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

kwin

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

kwin

Skip menu "kwin"
  • kate
  • kwin
  •   lib
  • libkonq
Generated for kwin by doxygen 1.8.1.2
This website is maintained by Timothy Pearson.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. |