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

twin

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

twin

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

twin

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