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