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