1 /*
2 * Copyright 2001-2020, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Adrian Oanca, adioanca@cotty.iren.ro
7 * Stephan Aßmus, superstippi@gmx.de
8 * Axel Dörfler, axeld@pinc-software.de
9 * Andrej Spielmann, andrej.spielmann@seh.ox.ac.uk
10 * Brecht Machiels, brecht@mos6581.org
11 * Clemens Zeidler, haiku@clemens-zeidler.de
12 * Ingo Weinhold, ingo_weinhold@gmx.de
13 * Joseph Groover, looncraz@looncraz.net
14 * Tri-Edge AI
15 * Jacob Secunda, secundja@gmail.com
16 */
17
18
19 /*! Class used to encapsulate desktop management */
20
21
22 #include "Desktop.h"
23
24 #include <stdio.h>
25 #include <string.h>
26 #include <syslog.h>
27
28 #include <Debug.h>
29 #include <debugger.h>
30 #include <DirectWindow.h>
31 #include <Entry.h>
32 #include <FindDirectory.h>
33 #include <Message.h>
34 #include <MessageFilter.h>
35 #include <Path.h>
36 #include <Region.h>
37 #include <Roster.h>
38
39 #include <PrivateScreen.h>
40 #include <ServerProtocol.h>
41 #include <ViewPrivate.h>
42 #include <WindowInfo.h>
43
44 #include "AppServer.h"
45 #include "ClickTarget.h"
46 #include "DecorManager.h"
47 #include "DesktopSettingsPrivate.h"
48 #include "DrawingEngine.h"
49 #include "GlobalFontManager.h"
50 #include "HWInterface.h"
51 #include "InputManager.h"
52 #include "Screen.h"
53 #include "ScreenManager.h"
54 #include "ServerApp.h"
55 #include "ServerConfig.h"
56 #include "ServerCursor.h"
57 #include "ServerWindow.h"
58 #include "SystemPalette.h"
59 #include "WindowPrivate.h"
60 #include "Window.h"
61 #include "Workspace.h"
62 #include "WorkspacesView.h"
63
64 #if TEST_MODE
65 # include "EventStream.h"
66 #endif
67
68
69 //#define DEBUG_DESKTOP
70 #ifdef DEBUG_DESKTOP
71 # define STRACE(a) printf a
72 #else
73 # define STRACE(a) ;
74 #endif
75
76
77 static inline float
square_vector_length(float x,float y)78 square_vector_length(float x, float y)
79 {
80 return x * x + y * y;
81 }
82
83
84 static inline float
square_distance(const BPoint & a,const BPoint & b)85 square_distance(const BPoint& a, const BPoint& b)
86 {
87 return square_vector_length(a.x - b.x, a.y - b.y);
88 }
89
90
91 class KeyboardFilter : public EventFilter {
92 public:
93 KeyboardFilter(Desktop* desktop);
94
95 virtual filter_result Filter(BMessage* message, EventTarget** _target,
96 int32* _viewToken, BMessage* latestMouseMoved);
97 virtual void RemoveTarget(EventTarget* target);
98
99 private:
100 void _UpdateFocus(int32 key, uint32 modifiers, EventTarget** _target);
101
102 Desktop* fDesktop;
103 EventTarget* fLastFocus;
104 bigtime_t fTimestamp;
105 };
106
107
108 class MouseFilter : public EventFilter {
109 public:
110 MouseFilter(Desktop* desktop);
111
112 virtual filter_result Filter(BMessage* message, EventTarget** _target,
113 int32* _viewToken, BMessage* latestMouseMoved);
114
115 private:
116 Desktop* fDesktop;
117 int32 fLastClickButtons;
118 int32 fLastClickModifiers;
119 int32 fResetClickCount;
120 BPoint fLastClickPoint;
121 ClickTarget fLastClickTarget;
122 };
123
124
125 // #pragma mark -
126
127
KeyboardFilter(Desktop * desktop)128 KeyboardFilter::KeyboardFilter(Desktop* desktop)
129 :
130 fDesktop(desktop),
131 fLastFocus(NULL),
132 fTimestamp(0)
133 {
134 }
135
136
137 void
_UpdateFocus(int32 key,uint32 modifiers,EventTarget ** _target)138 KeyboardFilter::_UpdateFocus(int32 key, uint32 modifiers, EventTarget** _target)
139 {
140 if (!fDesktop->LockSingleWindow())
141 return;
142
143 EventTarget* focus = fDesktop->KeyboardEventTarget();
144
145 #if 0
146 bigtime_t now = system_time();
147
148 // TODO: this is a try to not steal focus from the current window
149 // in case you enter some text and a window pops up you haven't
150 // triggered yourself (like a pop-up window in your browser while
151 // you're typing a password in another window) - maybe this should
152 // be done differently, though (using something like B_LOCK_WINDOW_FOCUS)
153 // (at least B_WINDOW_ACTIVATED must be postponed)
154
155 if (fLastFocus == NULL
156 || (focus != fLastFocus && now - fTimestamp > 100000)) {
157 // if the time span between the key presses is very short
158 // we keep our previous focus alive - this is safe even
159 // if the target doesn't exist anymore, as we don't reset
160 // it, and the event focus passed in is always valid (or NULL)
161 *_target = focus;
162 fLastFocus = focus;
163 }
164 #endif
165 *_target = focus;
166 fLastFocus = focus;
167
168 fDesktop->UnlockSingleWindow();
169
170 #if 0
171 // we always allow to switch focus after the enter key has pressed
172 if (key == B_ENTER || modifiers == B_COMMAND_KEY
173 || modifiers == B_CONTROL_KEY || modifiers == B_OPTION_KEY)
174 fTimestamp = 0;
175 else
176 fTimestamp = now;
177 #endif
178 }
179
180
181 filter_result
Filter(BMessage * message,EventTarget ** _target,int32 *,BMessage *)182 KeyboardFilter::Filter(BMessage* message, EventTarget** _target,
183 int32* /*_viewToken*/, BMessage* /*latestMouseMoved*/)
184 {
185 int32 key = 0;
186 int32 modifiers = 0;
187
188 message->FindInt32("key", &key);
189 message->FindInt32("modifiers", &modifiers);
190
191 if ((message->what == B_KEY_DOWN || message->what == B_UNMAPPED_KEY_DOWN)) {
192 // Check for safe video mode (shift + cmd + ctrl + escape)
193 if (key == 0x01 && (modifiers & B_COMMAND_KEY) != 0
194 && (modifiers & B_CONTROL_KEY) != 0
195 && (modifiers & B_SHIFT_KEY) != 0) {
196 system("screenmode --fall-back &");
197 return B_SKIP_MESSAGE;
198 }
199
200 bool takeWindow = (modifiers & B_SHIFT_KEY) != 0
201 || fDesktop->MouseEventWindow() != NULL;
202 if (key >= B_F1_KEY && key <= B_F12_KEY) {
203 // workspace change
204
205 #if !TEST_MODE
206 if ((modifiers & (B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY))
207 == B_COMMAND_KEY)
208 #else
209 if ((modifiers & B_CONTROL_KEY) != 0)
210 #endif
211 {
212 STRACE(("Set Workspace %" B_PRId32 "\n", key - 1));
213
214 fDesktop->SetWorkspaceAsync(key - B_F1_KEY, takeWindow);
215 return B_SKIP_MESSAGE;
216 }
217 } else if (key == 0x11
218 && (modifiers & (B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY))
219 == B_COMMAND_KEY) {
220 // switch to previous workspace (command + `)
221 fDesktop->SetWorkspaceAsync(-1, takeWindow);
222 return B_SKIP_MESSAGE;
223 }
224 }
225
226 if (message->what == B_KEY_DOWN
227 || message->what == B_MODIFIERS_CHANGED
228 || message->what == B_UNMAPPED_KEY_DOWN
229 || message->what == B_INPUT_METHOD_EVENT)
230 _UpdateFocus(key, modifiers, _target);
231
232 return fDesktop->KeyEvent(message->what, key, modifiers);
233 }
234
235
236 void
RemoveTarget(EventTarget * target)237 KeyboardFilter::RemoveTarget(EventTarget* target)
238 {
239 if (target == fLastFocus)
240 fLastFocus = NULL;
241 }
242
243
244 // #pragma mark -
245
246
MouseFilter(Desktop * desktop)247 MouseFilter::MouseFilter(Desktop* desktop)
248 :
249 fDesktop(desktop),
250 fLastClickButtons(0),
251 fLastClickModifiers(0),
252 fResetClickCount(0),
253 fLastClickPoint(),
254 fLastClickTarget()
255 {
256 }
257
258
259 filter_result
Filter(BMessage * message,EventTarget ** _target,int32 * _viewToken,BMessage * latestMouseMoved)260 MouseFilter::Filter(BMessage* message, EventTarget** _target, int32* _viewToken,
261 BMessage* latestMouseMoved)
262 {
263 BPoint where;
264 if (message->FindPoint("where", &where) != B_OK)
265 return B_DISPATCH_MESSAGE;
266
267 int32 buttons;
268 if (message->FindInt32("buttons", &buttons) != B_OK)
269 buttons = 0;
270
271 if (!fDesktop->LockAllWindows())
272 return B_DISPATCH_MESSAGE;
273
274 int32 viewToken = B_NULL_TOKEN;
275
276 Window* window = fDesktop->MouseEventWindow();
277 if (window == NULL)
278 window = fDesktop->WindowAt(where);
279
280 if (window != NULL) {
281 // dispatch event to the window
282 switch (message->what) {
283 case B_MOUSE_DOWN:
284 {
285 int32 windowToken = window->ServerWindow()->ServerToken();
286
287 // First approximation of click count validation. We reset the
288 // click count when modifiers or pressed buttons have changed
289 // or when we've got a different click target, or when the
290 // previous click location is too far from the new one. We can
291 // only check the window of the click target here; we'll recheck
292 // after asking the window.
293 int32 modifiers = message->FindInt32("modifiers");
294
295 int32 originalClickCount = message->FindInt32("clicks");
296 if (originalClickCount <= 0)
297 originalClickCount = 1;
298
299 int32 clickCount = originalClickCount;
300 if (clickCount > 1) {
301 if (modifiers != fLastClickModifiers
302 || buttons != fLastClickButtons
303 || !fLastClickTarget.IsValid()
304 || fLastClickTarget.WindowToken() != windowToken
305 || square_distance(where, fLastClickPoint) >= 16
306 || clickCount - fResetClickCount < 1) {
307 clickCount = 1;
308 } else
309 clickCount -= fResetClickCount;
310 }
311
312 // notify the window
313 ClickTarget clickTarget;
314 window->MouseDown(message, where, fLastClickTarget, clickCount,
315 clickTarget);
316
317 // If the click target changed, always reset the click count.
318 if (clickCount != 1 && clickTarget != fLastClickTarget)
319 clickCount = 1;
320
321 // update our click count management attributes
322 fResetClickCount = originalClickCount - clickCount;
323 fLastClickTarget = clickTarget;
324 fLastClickButtons = buttons;
325 fLastClickModifiers = modifiers;
326 fLastClickPoint = where;
327
328 // get the view token from the click target
329 if (clickTarget.GetType() == ClickTarget::TYPE_WINDOW_CONTENTS)
330 viewToken = clickTarget.WindowElement();
331
332 // update the message's "clicks" field, if necessary
333 if (clickCount != originalClickCount) {
334 if (message->HasInt32("clicks"))
335 message->ReplaceInt32("clicks", clickCount);
336 else
337 message->AddInt32("clicks", clickCount);
338 }
339
340 // notify desktop listeners
341 fDesktop->NotifyMouseDown(window, message, where);
342 break;
343 }
344
345 case B_MOUSE_UP:
346 window->MouseUp(message, where, &viewToken);
347 if (buttons == 0)
348 fDesktop->SetMouseEventWindow(NULL);
349 fDesktop->NotifyMouseUp(window, message, where);
350 break;
351
352 case B_MOUSE_MOVED:
353 window->MouseMoved(message, where, &viewToken,
354 latestMouseMoved == NULL || latestMouseMoved == message,
355 false);
356 fDesktop->NotifyMouseMoved(window, message, where);
357 break;
358 }
359
360 if (viewToken != B_NULL_TOKEN) {
361 fDesktop->SetViewUnderMouse(window, viewToken);
362
363 *_viewToken = viewToken;
364 *_target = &window->EventTarget();
365 }
366 } else if (message->what == B_MOUSE_DOWN) {
367 // the mouse-down didn't hit a window -- reset the click target
368 fResetClickCount = 0;
369 fLastClickTarget = ClickTarget();
370 fLastClickButtons = message->FindInt32("buttons");
371 fLastClickModifiers = message->FindInt32("modifiers");
372 fLastClickPoint = where;
373 }
374
375 if (window == NULL || viewToken == B_NULL_TOKEN) {
376 // mouse is not over a window or over a decorator
377 fDesktop->SetViewUnderMouse(window, B_NULL_TOKEN);
378 fDesktop->SetCursor(NULL);
379
380 *_target = NULL;
381 }
382
383 fDesktop->SetLastMouseState(where, buttons, window);
384
385 fDesktop->NotifyMouseEvent(message);
386
387 fDesktop->UnlockAllWindows();
388
389 return B_DISPATCH_MESSAGE;
390 }
391
392
393 // #pragma mark -
394
395
396 static inline uint32
workspace_to_workspaces(int32 index)397 workspace_to_workspaces(int32 index)
398 {
399 return 1UL << index;
400 }
401
402
403 static inline bool
workspace_in_workspaces(int32 index,uint32 workspaces)404 workspace_in_workspaces(int32 index, uint32 workspaces)
405 {
406 return (workspaces & (1UL << index)) != 0;
407 }
408
409
410 // #pragma mark -
411
412
Desktop(uid_t userID,const char * targetScreen)413 Desktop::Desktop(uid_t userID, const char* targetScreen)
414 :
415 MessageLooper("desktop"),
416
417 fUserID(userID),
418 fTargetScreen(strdup(targetScreen)),
419 fSettings(NULL),
420 fSharedReadOnlyArea(-1),
421 fApplicationsLock("application list"),
422 fShutdownSemaphore(-1),
423 fShutdownCount(0),
424 fScreenLock("screen lock"),
425 fDirectScreenLock("direct screen lock"),
426 fDirectScreenTeam(-1),
427 fCurrentWorkspace(0),
428 fPreviousWorkspace(0),
429 fAllWindows(kAllWindowList),
430 fSubsetWindows(kSubsetList),
431 fFocusList(kFocusList),
432 fWorkspacesViews(false),
433
434 fWorkspacesLock("workspaces list"),
435 fWindowLock("window lock"),
436
437 fMouseEventWindow(NULL),
438 fWindowUnderMouse(NULL),
439 fLockedFocusWindow(NULL),
440 fViewUnderMouse(B_NULL_TOKEN),
441 fLastMousePosition(B_ORIGIN),
442 fLastMouseButtons(0),
443
444 fFocus(NULL),
445 fFront(NULL),
446 fBack(NULL)
447 {
448 memset(fLastWorkspaceFocus, 0, sizeof(fLastWorkspaceFocus));
449
450 char name[B_OS_NAME_LENGTH];
451 Desktop::_GetLooperName(name, sizeof(name));
452
453 fMessagePort = create_port(DEFAULT_MONITOR_PORT_SIZE, name);
454 if (fMessagePort < B_OK)
455 return;
456
457 fLink.SetReceiverPort(fMessagePort);
458
459 // register listeners
460 RegisterListener(&fStackAndTile);
461
462 const DesktopListenerList& newListeners
463 = gDecorManager.GetDesktopListeners();
464 for (int i = 0; i < newListeners.CountItems(); i++)
465 RegisterListener(newListeners.ItemAt(i));
466 }
467
468
~Desktop()469 Desktop::~Desktop()
470 {
471 delete_area(fSharedReadOnlyArea);
472 delete_port(fMessagePort);
473
474 free(fTargetScreen);
475 }
476
477
478 void
RegisterListener(DesktopListener * listener)479 Desktop::RegisterListener(DesktopListener* listener)
480 {
481 DesktopObservable::RegisterListener(listener, this);
482 }
483
484
485 /*! This method is allowed to throw exceptions.
486 */
487 status_t
Init()488 Desktop::Init()
489 {
490 if (fMessagePort < B_OK)
491 return fMessagePort;
492
493 // the system palette needs to be initialized before the
494 // desktop settings, since it is used there already
495 InitializeColorMap();
496
497 const size_t areaSize = B_PAGE_SIZE;
498 char name[B_OS_NAME_LENGTH];
499 snprintf(name, sizeof(name), "d:%d:shared read only", fUserID);
500 fSharedReadOnlyArea = create_area(name, (void **)&fServerReadOnlyMemory,
501 B_ANY_ADDRESS, areaSize, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA | B_CLONEABLE_AREA);
502 if (fSharedReadOnlyArea < B_OK)
503 return fSharedReadOnlyArea;
504
505 fSettings.SetTo(new DesktopSettingsPrivate(fServerReadOnlyMemory));
506
507 for (int32 i = 0; i < kMaxWorkspaces; i++) {
508 _Windows(i).SetIndex(i);
509 fWorkspaces[i].RestoreConfiguration(*fSettings->WorkspacesMessage(i));
510 }
511
512 status_t status = fVirtualScreen.SetConfiguration(*this,
513 fWorkspaces[0].CurrentScreenConfiguration());
514 if (status != B_OK) {
515 debug_printf("app_server: Failed to initialize virtual screen configuration: %s\n",
516 strerror(status));
517 return status;
518 }
519
520 if (fVirtualScreen.HWInterface() == NULL) {
521 debug_printf("Could not initialize graphics output. Exiting.\n");
522 return B_ERROR;
523 }
524
525 // now that the mode is set, see if we should increase the default font size
526 if (fSettings->DefaultPlainFont() == *gFontManager->DefaultPlainFont()
527 && !fSettings->DidLoadFontSettings()) {
528 float fontSize = fSettings->DefaultPlainFont().Size();
529 gScreenManager->Lock();
530 Screen* screen = gScreenManager->ScreenAt(0);
531 if (screen != NULL) {
532 display_mode mode;
533 screen->GetMode(mode);
534
535 if (mode.virtual_width > 3840 && mode.virtual_height > 2160)
536 fontSize *= 2.0f;
537 else if (mode.virtual_width > 1920 && mode.virtual_height > 1080)
538 fontSize *= 1.5f;
539 }
540 gScreenManager->Unlock();
541
542 // modify settings without saving them
543 const_cast<ServerFont&>(fSettings->DefaultPlainFont()).SetSize(fontSize);
544 const_cast<ServerFont&>(fSettings->DefaultBoldFont()).SetSize(fontSize);
545 const_cast<ServerFont&>(fSettings->DefaultFixedFont()).SetSize(fontSize);
546 const_cast<menu_info&>(fSettings->MenuInfo()).font_size = fontSize;
547 }
548
549 HWInterface()->SetDPMSMode(B_DPMS_ON);
550
551 float brightness = fWorkspaces[0].StoredScreenConfiguration().Brightness(0);
552 if (brightness > 0)
553 HWInterface()->SetBrightness(brightness);
554
555 fVirtualScreen.HWInterface()->MoveCursorTo(
556 fVirtualScreen.Frame().Width() / 2,
557 fVirtualScreen.Frame().Height() / 2);
558
559 #if TEST_MODE
560 gInputManager->AddStream(new InputServerStream);
561 #endif
562
563 EventStream* stream = fVirtualScreen.HWInterface()->CreateEventStream();
564 if (stream == NULL)
565 stream = gInputManager->GetStream();
566
567 fEventDispatcher.SetDesktop(this);
568 fEventDispatcher.SetTo(stream);
569 if (fEventDispatcher.InitCheck() != B_OK)
570 _LaunchInputServer();
571
572 fEventDispatcher.SetHWInterface(fVirtualScreen.HWInterface());
573
574 fEventDispatcher.SetMouseFilter(new MouseFilter(this));
575 fEventDispatcher.SetKeyboardFilter(new KeyboardFilter(this));
576
577 // draw the background
578
579 fScreenRegion = fVirtualScreen.Frame();
580
581 BRegion stillAvailableOnScreen;
582 _RebuildClippingForAllWindows(stillAvailableOnScreen);
583 _SetBackground(stillAvailableOnScreen);
584
585 SetCursor(NULL);
586 // this will set the default cursor
587
588 fVirtualScreen.HWInterface()->SetCursorVisible(true);
589
590 return B_OK;
591 }
592
593
594 /*! \brief Send a quick (no attachments) message to all applications.
595
596 Quite useful for notification for things like server shutdown, system
597 color changes, etc.
598 */
599 void
BroadcastToAllApps(int32 code)600 Desktop::BroadcastToAllApps(int32 code)
601 {
602 BAutolock locker(fApplicationsLock);
603
604 for (int32 i = fApplications.CountItems(); i-- > 0;) {
605 fApplications.ItemAt(i)->PostMessage(code);
606 }
607 }
608
609
610 /*! \brief Send a quick (no attachments) message to all windows.
611 */
612 void
BroadcastToAllWindows(int32 code)613 Desktop::BroadcastToAllWindows(int32 code)
614 {
615 AutoReadLocker _(fWindowLock);
616
617 for (Window* window = fAllWindows.FirstWindow(); window != NULL;
618 window = window->NextWindow(kAllWindowList)) {
619 window->ServerWindow()->PostMessage(code);
620 }
621 }
622
623
624 int32
GetAllWindowTargets(DelayedMessage & message)625 Desktop::GetAllWindowTargets(DelayedMessage& message)
626 {
627 AutoReadLocker _(fWindowLock);
628 int32 count = 0;
629
630 for (Window* window = fAllWindows.FirstWindow(); window != NULL;
631 window = window->NextWindow(kAllWindowList)) {
632 message.AddTarget(window->ServerWindow()->MessagePort());
633 ++count;
634 }
635
636 return count;
637 }
638
639
640 int32
GetAllAppTargets(DelayedMessage & message)641 Desktop::GetAllAppTargets(DelayedMessage& message)
642 {
643 BAutolock _(fApplicationsLock);
644
645 for (int32 index = 0; index < fApplications.CountItems(); ++index)
646 message.AddTarget(fApplications.ItemAt(index)->MessagePort());
647
648 return fApplications.CountItems();
649 }
650
651
652 filter_result
KeyEvent(uint32 what,int32 key,int32 modifiers)653 Desktop::KeyEvent(uint32 what, int32 key, int32 modifiers)
654 {
655 filter_result result = B_DISPATCH_MESSAGE;
656 if (LockAllWindows()) {
657 Window* window = MouseEventWindow();
658 if (window == NULL)
659 window = WindowAt(fLastMousePosition);
660
661 if (window != NULL) {
662 if (what == B_MODIFIERS_CHANGED)
663 window->ModifiersChanged(modifiers);
664 }
665
666 if (NotifyKeyPressed(what, key, modifiers))
667 result = B_SKIP_MESSAGE;
668
669 UnlockAllWindows();
670 }
671
672 return result;
673 }
674
675
676 // #pragma mark - Mouse and cursor methods
677
678
679 void
SetCursor(ServerCursor * newCursor)680 Desktop::SetCursor(ServerCursor* newCursor)
681 {
682 if (newCursor == NULL)
683 newCursor = fCursorManager.GetCursor(B_CURSOR_ID_SYSTEM_DEFAULT);
684
685 if (newCursor == fCursor)
686 return;
687
688 fCursor.SetTo(newCursor, false);
689
690 if (!fManagementCursor.IsSet())
691 HWInterface()->SetCursor(newCursor);
692 }
693
694
695 ServerCursorReference
Cursor() const696 Desktop::Cursor() const
697 {
698 return fCursor;
699 }
700
701
702 void
SetManagementCursor(ServerCursor * newCursor)703 Desktop::SetManagementCursor(ServerCursor* newCursor)
704 {
705 if (newCursor == fManagementCursor)
706 return;
707
708 fManagementCursor.SetTo(newCursor, false);
709
710 HWInterface()->SetCursor(newCursor != NULL ? newCursor : fCursor.Get());
711 }
712
713
714 void
SetLastMouseState(const BPoint & position,int32 buttons,Window * windowUnderMouse)715 Desktop::SetLastMouseState(const BPoint& position, int32 buttons,
716 Window* windowUnderMouse)
717 {
718 // The all-window-lock is write-locked.
719 fLastMousePosition = position;
720 fLastMouseButtons = buttons;
721
722 if (fLastMouseButtons == 0 && fLockedFocusWindow) {
723 fLockedFocusWindow = NULL;
724 if (fSettings->FocusFollowsMouse())
725 SetFocusWindow(windowUnderMouse);
726 }
727 }
728
729
730 void
GetLastMouseState(BPoint * position,int32 * buttons) const731 Desktop::GetLastMouseState(BPoint* position, int32* buttons) const
732 {
733 *position = fLastMousePosition;
734 *buttons = fLastMouseButtons;
735 }
736
737
738 // #pragma mark - Screen methods
739
740
741 status_t
SetScreenMode(int32 workspace,int32 id,const display_mode & mode,bool makeDefault)742 Desktop::SetScreenMode(int32 workspace, int32 id, const display_mode& mode,
743 bool makeDefault)
744 {
745 AutoWriteLocker _(fWindowLock);
746
747 if (workspace == B_CURRENT_WORKSPACE_INDEX)
748 workspace = fCurrentWorkspace;
749
750 if (workspace < 0 || workspace >= kMaxWorkspaces)
751 return B_BAD_VALUE;
752
753 Screen* screen = fVirtualScreen.ScreenByID(id);
754 if (screen == NULL)
755 return B_NAME_NOT_FOUND;
756
757 // Check if the mode has actually changed
758
759 if (workspace == fCurrentWorkspace) {
760 // retrieve from current screen
761 display_mode oldMode;
762 screen->GetMode(oldMode);
763
764 if (!memcmp(&oldMode, &mode, sizeof(display_mode)))
765 return B_OK;
766
767 // Set the new one
768
769 _SuspendDirectFrameBufferAccess();
770
771 AutoWriteLocker locker(fScreenLock);
772
773 status_t status = screen->SetMode(mode);
774 if (status != B_OK) {
775 locker.Unlock();
776
777 _ResumeDirectFrameBufferAccess();
778 return status;
779 }
780 } else {
781 // retrieve from settings
782 screen_configuration* configuration
783 = fWorkspaces[workspace].CurrentScreenConfiguration().CurrentByID(
784 screen->ID());
785 if (configuration != NULL
786 && !memcmp(&configuration->mode, &mode, sizeof(display_mode)))
787 return B_OK;
788 }
789
790 // Update our configurations
791
792 monitor_info info;
793 bool hasInfo = screen->GetMonitorInfo(info) == B_OK;
794
795 fWorkspaces[workspace].CurrentScreenConfiguration().Set(id,
796 hasInfo ? &info : NULL, screen->Frame(), mode);
797 if (makeDefault) {
798 fWorkspaces[workspace].StoredScreenConfiguration().Set(id,
799 hasInfo ? &info : NULL, screen->Frame(), mode);
800 StoreWorkspaceConfiguration(workspace);
801 }
802
803 _ScreenChanged(screen);
804 if (workspace == fCurrentWorkspace)
805 _ResumeDirectFrameBufferAccess();
806
807 return B_OK;
808 }
809
810
811 status_t
GetScreenMode(int32 workspace,int32 id,display_mode & mode)812 Desktop::GetScreenMode(int32 workspace, int32 id, display_mode& mode)
813 {
814 AutoReadLocker _(fScreenLock);
815
816 if (workspace == B_CURRENT_WORKSPACE_INDEX)
817 workspace = fCurrentWorkspace;
818
819 if (workspace < 0 || workspace >= kMaxWorkspaces)
820 return B_BAD_VALUE;
821
822 if (workspace == fCurrentWorkspace) {
823 // retrieve from current screen
824 Screen* screen = fVirtualScreen.ScreenByID(id);
825 if (screen == NULL)
826 return B_NAME_NOT_FOUND;
827
828 screen->GetMode(mode);
829 return B_OK;
830 }
831
832 // retrieve from settings
833 screen_configuration* configuration
834 = fWorkspaces[workspace].CurrentScreenConfiguration().CurrentByID(id);
835 if (configuration == NULL)
836 return B_NAME_NOT_FOUND;
837
838 mode = configuration->mode;
839 return B_OK;
840 }
841
842
843 status_t
GetScreenFrame(int32 workspace,int32 id,BRect & frame)844 Desktop::GetScreenFrame(int32 workspace, int32 id, BRect& frame)
845 {
846 AutoReadLocker _(fScreenLock);
847
848 if (workspace == B_CURRENT_WORKSPACE_INDEX)
849 workspace = fCurrentWorkspace;
850
851 if (workspace < 0 || workspace >= kMaxWorkspaces)
852 return B_BAD_VALUE;
853
854 if (workspace == fCurrentWorkspace) {
855 // retrieve from current screen
856 Screen* screen = fVirtualScreen.ScreenByID(id);
857 if (screen == NULL)
858 return B_NAME_NOT_FOUND;
859
860 frame = screen->Frame();
861 return B_OK;
862 }
863
864 // retrieve from settings
865 screen_configuration* configuration
866 = fWorkspaces[workspace].CurrentScreenConfiguration().CurrentByID(id);
867 if (configuration == NULL)
868 return B_NAME_NOT_FOUND;
869
870 frame = configuration->frame;
871 return B_OK;
872 }
873
874
875 void
RevertScreenModes(uint32 workspaces)876 Desktop::RevertScreenModes(uint32 workspaces)
877 {
878 if (workspaces == 0)
879 return;
880
881 AutoWriteLocker _(fWindowLock);
882
883 for (int32 workspace = 0; workspace < kMaxWorkspaces; workspace++) {
884 if ((workspaces & (1U << workspace)) == 0)
885 continue;
886
887 // Revert all screens on this workspace
888
889 // TODO: ideally, we would know which screens to revert - this way, too
890 // many of them could be reverted
891
892 for (int32 index = 0; index < fVirtualScreen.CountScreens(); index++) {
893 Screen* screen = fVirtualScreen.ScreenAt(index);
894
895 // retrieve configurations
896 screen_configuration* stored = fWorkspaces[workspace]
897 .StoredScreenConfiguration().CurrentByID(screen->ID());
898 screen_configuration* current = fWorkspaces[workspace]
899 .CurrentScreenConfiguration().CurrentByID(screen->ID());
900
901 if ((stored != NULL && current != NULL
902 && !memcmp(&stored->mode, ¤t->mode,
903 sizeof(display_mode)))
904 || (stored == NULL && current == NULL))
905 continue;
906
907 if (stored == NULL) {
908 fWorkspaces[workspace].CurrentScreenConfiguration()
909 .Remove(current);
910
911 if (workspace == fCurrentWorkspace) {
912 _SuspendDirectFrameBufferAccess();
913 _SetCurrentWorkspaceConfiguration();
914 _ResumeDirectFrameBufferAccess();
915 }
916 } else
917 SetScreenMode(workspace, screen->ID(), stored->mode, false);
918 }
919 }
920 }
921
922
923 status_t
SetBrightness(int32 id,float brightness)924 Desktop::SetBrightness(int32 id, float brightness)
925 {
926 status_t result = HWInterface()->SetBrightness(brightness);
927
928 if (result == B_OK) {
929 if (fWorkspaces[0].StoredScreenConfiguration().CurrentByID(id) == NULL) {
930 // store the current configuration if empty
931 screen_configuration* current
932 = fWorkspaces[0].CurrentScreenConfiguration().CurrentByID(id);
933 fWorkspaces[0].StoredScreenConfiguration().Set(id,
934 current->has_info ? ¤t->info : NULL, current->frame, current->mode);
935 }
936 fWorkspaces[0].StoredScreenConfiguration().SetBrightness(id,
937 brightness);
938 // Save brightness for next boot
939 StoreWorkspaceConfiguration(0);
940 }
941
942 return result;
943 }
944
945
946 status_t
LockDirectScreen(team_id team)947 Desktop::LockDirectScreen(team_id team)
948 {
949 // TODO: BWindowScreens should use the same mechanism as BDirectWindow,
950 // which would make this method superfluous.
951
952 status_t status = fDirectScreenLock.LockWithTimeout(1000000L);
953 if (status == B_OK)
954 fDirectScreenTeam = team;
955
956 return status;
957 }
958
959
960 status_t
UnlockDirectScreen(team_id team)961 Desktop::UnlockDirectScreen(team_id team)
962 {
963 if (fDirectScreenTeam == team) {
964 fDirectScreenLock.Unlock();
965 fDirectScreenTeam = -1;
966 return B_OK;
967 }
968
969 return B_PERMISSION_DENIED;
970 }
971
972
973 // #pragma mark - Workspaces methods
974
975
976 /*! Changes the current workspace to the one specified by \a index.
977 */
978 void
SetWorkspaceAsync(int32 index,bool moveFocusWindow)979 Desktop::SetWorkspaceAsync(int32 index, bool moveFocusWindow)
980 {
981 BPrivate::LinkSender link(MessagePort());
982 link.StartMessage(AS_ACTIVATE_WORKSPACE);
983 link.Attach<int32>(index);
984 link.Attach<bool>(moveFocusWindow);
985 link.Flush();
986 }
987
988
989 /*! Changes the current workspace to the one specified by \a index.
990 You must not hold any window lock when calling this method.
991 */
992 void
SetWorkspace(int32 index,bool moveFocusWindow)993 Desktop::SetWorkspace(int32 index, bool moveFocusWindow)
994 {
995 LockAllWindows();
996 DesktopSettings settings(this);
997
998 if (index < 0 || index >= settings.WorkspacesCount()
999 || index == fCurrentWorkspace) {
1000 UnlockAllWindows();
1001 return;
1002 }
1003
1004 _SetWorkspace(index, moveFocusWindow);
1005 UnlockAllWindows();
1006
1007 _SendFakeMouseMoved();
1008 }
1009
1010
1011 status_t
SetWorkspacesLayout(int32 newColumns,int32 newRows)1012 Desktop::SetWorkspacesLayout(int32 newColumns, int32 newRows)
1013 {
1014 int32 newCount = newColumns * newRows;
1015 if (newCount < 1 || newCount > kMaxWorkspaces)
1016 return B_BAD_VALUE;
1017
1018 if (!LockAllWindows())
1019 return B_ERROR;
1020
1021 fSettings->SetWorkspacesLayout(newColumns, newRows);
1022
1023 // either update the workspaces window, or switch to
1024 // the last available workspace - which will update
1025 // the workspaces window automatically
1026 bool workspaceChanged = CurrentWorkspace() >= newCount;
1027 if (workspaceChanged)
1028 _SetWorkspace(newCount - 1);
1029 else
1030 _WindowChanged(NULL);
1031
1032 UnlockAllWindows();
1033
1034 if (workspaceChanged)
1035 _SendFakeMouseMoved();
1036
1037 return B_OK;
1038 }
1039
1040
1041 /*! Returns the virtual screen frame of the workspace specified by \a index.
1042 */
1043 BRect
WorkspaceFrame(int32 index) const1044 Desktop::WorkspaceFrame(int32 index) const
1045 {
1046 BRect frame;
1047 if (index == fCurrentWorkspace)
1048 frame = fVirtualScreen.Frame();
1049 else if (index >= 0 && index < fSettings->WorkspacesCount()) {
1050 BMessage screenData;
1051 if (fSettings->WorkspacesMessage(index)->FindMessage("screen",
1052 &screenData) != B_OK
1053 || screenData.FindRect("frame", &frame) != B_OK) {
1054 frame = fVirtualScreen.Frame();
1055 }
1056 }
1057
1058 return frame;
1059 }
1060
1061
1062 /*! \brief Stores the workspace configuration.
1063 You must hold the window lock when calling this method.
1064 */
1065 void
StoreWorkspaceConfiguration(int32 index)1066 Desktop::StoreWorkspaceConfiguration(int32 index)
1067 {
1068 // Retrieve settings
1069
1070 BMessage settings;
1071 fWorkspaces[index].StoreConfiguration(settings);
1072
1073 // and store them
1074
1075 fSettings->SetWorkspacesMessage(index, settings);
1076 fSettings->Save(kWorkspacesSettings);
1077 }
1078
1079
1080 void
AddWorkspacesView(WorkspacesView * view)1081 Desktop::AddWorkspacesView(WorkspacesView* view)
1082 {
1083 if (view->Window() == NULL || view->Window()->IsHidden())
1084 return;
1085
1086 BAutolock _(fWorkspacesLock);
1087
1088 if (!fWorkspacesViews.HasItem(view))
1089 fWorkspacesViews.AddItem(view);
1090 }
1091
1092
1093 void
RemoveWorkspacesView(WorkspacesView * view)1094 Desktop::RemoveWorkspacesView(WorkspacesView* view)
1095 {
1096 BAutolock _(fWorkspacesLock);
1097 fWorkspacesViews.RemoveItem(view);
1098 }
1099
1100
1101 // #pragma mark - Methods for Window manipulation
1102
1103
1104 /*! \brief Activates or focusses the window based on the pointer position.
1105 */
1106 void
SelectWindow(Window * window)1107 Desktop::SelectWindow(Window* window)
1108 {
1109 if (fSettings->ClickToFocusMouse()) {
1110 // Only bring the window to front when it is not the window under the
1111 // mouse pointer. This should result in sensible behaviour.
1112 if (window != fWindowUnderMouse
1113 || (window == fWindowUnderMouse && window != FocusWindow()))
1114 ActivateWindow(window);
1115 else
1116 SetFocusWindow(window);
1117 } else
1118 ActivateWindow(window);
1119 }
1120
1121
1122 /*! \brief Tries to move the specified window to the front of the screen,
1123 and make it the focus window.
1124
1125 If there are any modal windows on this screen, it might not actually
1126 become the frontmost window, though, as modal windows stay in front
1127 of their subset.
1128 */
1129 void
ActivateWindow(Window * window)1130 Desktop::ActivateWindow(Window* window)
1131 {
1132 STRACE(("ActivateWindow(%p, %s)\n", window, window
1133 ? window->Title() : "<none>"));
1134
1135 if (window == NULL) {
1136 fBack = NULL;
1137 fFront = NULL;
1138 return;
1139 }
1140 if (window->Workspaces() == 0 && window->IsNormal())
1141 return;
1142
1143 AutoWriteLocker allWindowLocker(fWindowLock);
1144
1145 NotifyWindowActivated(window);
1146
1147 bool windowOnOtherWorkspace = !window->InWorkspace(fCurrentWorkspace);
1148 if (windowOnOtherWorkspace
1149 && (window->Flags() & B_NOT_ANCHORED_ON_ACTIVATE) == 0) {
1150 if ((window->Flags() & B_NO_WORKSPACE_ACTIVATION) == 0) {
1151 // Switch to the workspace on which this window is
1152 // (we'll take the first one that the window is on)
1153 uint32 workspaces = window->Workspaces();
1154 for (int32 i = 0; i < fSettings->WorkspacesCount(); i++) {
1155 uint32 workspace = workspace_to_workspaces(i);
1156 if (workspaces & workspace) {
1157 SetWorkspace(i);
1158 windowOnOtherWorkspace = false;
1159 break;
1160 }
1161 }
1162 } else
1163 return;
1164 }
1165
1166 if (windowOnOtherWorkspace) {
1167 if (!window->IsNormal()) {
1168 // Bring a window to front that this floating window belongs to
1169 Window* front = _LastFocusSubsetWindow(window);
1170 if (front == NULL) {
1171 // We can't do anything about those.
1172 return;
1173 }
1174
1175 ActivateWindow(front);
1176
1177 if (!window->InWorkspace(fCurrentWorkspace)) {
1178 // This window can't be made active
1179 return;
1180 }
1181 } else {
1182 // Bring the window to the current workspace
1183 // TODO: what if this window is on multiple workspaces?!?
1184 uint32 workspaces = workspace_to_workspaces(fCurrentWorkspace);
1185 SetWindowWorkspaces(window, workspaces);
1186 }
1187 }
1188
1189 if (window->IsMinimized()) {
1190 // Unlike WindowAction(), this is called from the application itself,
1191 // so we will just unminimize the window here.
1192 window->SetMinimized(false);
1193 ShowWindow(window);
1194 }
1195
1196 if (window == FrontWindow()) {
1197 // see if there is a normal B_AVOID_FRONT window still in front of us
1198 Window* avoidsFront = window->NextWindow(fCurrentWorkspace);
1199 while (avoidsFront && avoidsFront->IsNormal()
1200 && (avoidsFront->Flags() & B_AVOID_FRONT) == 0) {
1201 avoidsFront = avoidsFront->NextWindow(fCurrentWorkspace);
1202 }
1203
1204 if (avoidsFront == NULL) {
1205 // we're already the frontmost window, we might just not have focus
1206 // yet
1207 if ((window->Flags() & B_AVOID_FOCUS) == 0)
1208 SetFocusWindow(window);
1209 return;
1210 }
1211 }
1212
1213 WindowList windows(kWorkingList);
1214 Window* frontmost = window->Frontmost();
1215 const Window* lastWindowUnderMouse = fWindowUnderMouse;
1216
1217 CurrentWindows().RemoveWindow(window);
1218 windows.AddWindow(window);
1219 window->MoveToTopStackLayer();
1220
1221 if (frontmost != NULL && frontmost->IsModal()) {
1222 // all modal windows follow their subsets to the front
1223 // (ie. they are staying in front of them, but they are
1224 // not supposed to change their order because of that)
1225
1226 Window* nextModal;
1227 for (Window* modal = frontmost; modal != NULL; modal = nextModal) {
1228 // get the next modal window
1229 nextModal = modal->NextWindow(fCurrentWorkspace);
1230 while (nextModal != NULL && !nextModal->IsModal()) {
1231 nextModal = nextModal->NextWindow(fCurrentWorkspace);
1232 }
1233 if (nextModal != NULL && !nextModal->HasInSubset(window))
1234 nextModal = NULL;
1235
1236 CurrentWindows().RemoveWindow(modal);
1237 windows.AddWindow(modal);
1238 }
1239 }
1240
1241 _BringWindowsToFront(windows, kWorkingList, true);
1242
1243 if ((window->Flags() & B_AVOID_FOCUS) == 0)
1244 SetFocusWindow(window);
1245
1246 bool sendFakeMouseMoved = _CheckSendFakeMouseMoved(lastWindowUnderMouse);
1247
1248 allWindowLocker.Unlock();
1249
1250 if (sendFakeMouseMoved)
1251 _SendFakeMouseMoved();
1252 }
1253
1254
1255 void
SendWindowBehind(Window * window,Window * behindOf,bool sendStack)1256 Desktop::SendWindowBehind(Window* window, Window* behindOf, bool sendStack)
1257 {
1258 if (!LockAllWindows())
1259 return;
1260
1261 Window* orgWindow = window;
1262 WindowStack* stack = window->GetWindowStack();
1263 if (sendStack && stack != NULL)
1264 window = stack->TopLayerWindow();
1265
1266 // TODO: should the "not in current workspace" be handled anyway?
1267 // (the code below would have to be changed then, though)
1268 if (window == BackWindow()
1269 || !window->InWorkspace(fCurrentWorkspace)
1270 || (behindOf != NULL && !behindOf->InWorkspace(fCurrentWorkspace))) {
1271 UnlockAllWindows();
1272 return;
1273 }
1274
1275 // Is this a valid behindOf window?
1276 if (behindOf != NULL && window->HasInSubset(behindOf))
1277 behindOf = NULL;
1278
1279 // what is currently visible of the window
1280 // might be dirty after the window is send to back
1281 BRegion dirty(window->VisibleRegion());
1282
1283 Window* backmost = window->Backmost(behindOf);
1284 const Window* lastWindowUnderMouse = fWindowUnderMouse;
1285
1286 CurrentWindows().RemoveWindow(window);
1287 CurrentWindows().AddWindow(window, backmost
1288 ? backmost->NextWindow(fCurrentWorkspace) : BackWindow());
1289
1290 BRegion dummy;
1291 _RebuildClippingForAllWindows(dummy);
1292
1293 // only redraw the top layer window to avoid flicker
1294 if (sendStack) {
1295 // mark everything dirty that is no longer visible
1296 BRegion clean(window->VisibleRegion());
1297 dirty.Exclude(&clean);
1298 MarkDirty(dirty);
1299 }
1300
1301 _UpdateFronts();
1302 if (fSettings->FocusFollowsMouse())
1303 SetFocusWindow(WindowAt(fLastMousePosition));
1304 else if (fSettings->NormalMouse())
1305 SetFocusWindow(NULL);
1306
1307 _WindowChanged(window);
1308
1309 if (sendStack && stack != NULL) {
1310 for (int32 i = 0; i < stack->CountWindows(); i++) {
1311 Window* stackWindow = stack->LayerOrder().ItemAt(i);
1312 if (stackWindow == window)
1313 continue;
1314 SendWindowBehind(stackWindow, behindOf, false);
1315 }
1316 }
1317
1318 bool sendFakeMouseMoved = _CheckSendFakeMouseMoved(lastWindowUnderMouse);
1319 NotifyWindowSentBehind(orgWindow, behindOf);
1320
1321 UnlockAllWindows();
1322
1323 if (sendFakeMouseMoved)
1324 _SendFakeMouseMoved();
1325 }
1326
1327
1328 void
ShowWindow(Window * window)1329 Desktop::ShowWindow(Window* window)
1330 {
1331 if (!window->IsHidden())
1332 return;
1333
1334 AutoWriteLocker locker(fWindowLock);
1335
1336 window->SetHidden(false);
1337 fFocusList.AddWindow(window);
1338
1339 // If the window is on the current workspace, we'll show it. Special
1340 // handling for floating windows, as they can only be shown if their
1341 // subset is.
1342 if (window->InWorkspace(fCurrentWorkspace)
1343 || (window->IsFloating() && _LastFocusSubsetWindow(window) != NULL)) {
1344 _ShowWindow(window, true);
1345 _UpdateSubsetWorkspaces(window);
1346 ActivateWindow(window);
1347 } else {
1348 // then we don't need to send the fake mouse event either
1349 _WindowChanged(window);
1350 return;
1351 }
1352
1353 if (window->HasWorkspacesViews()) {
1354 // find workspaces views in view hierarchy
1355 BAutolock _(fWorkspacesLock);
1356 window->FindWorkspacesViews(fWorkspacesViews);
1357 }
1358
1359 // If the mouse cursor is directly over the newly visible window,
1360 // we'll send a fake mouse moved message to the window, so that
1361 // it knows the mouse is over it.
1362
1363 _SendFakeMouseMoved(window);
1364 }
1365
1366
1367 void
HideWindow(Window * window,bool fromMinimize)1368 Desktop::HideWindow(Window* window, bool fromMinimize)
1369 {
1370 if (window->IsHidden())
1371 return;
1372
1373 if (!LockAllWindows())
1374 return;
1375
1376 window->SetHidden(true);
1377 fFocusList.RemoveWindow(window);
1378
1379 if (fMouseEventWindow == window) {
1380 // Make its decorator lose the current mouse action
1381 BMessage message;
1382 int32 viewToken;
1383 window->MouseUp(&message, fLastMousePosition, &viewToken);
1384
1385 fMouseEventWindow = NULL;
1386 }
1387
1388 if (fLockedFocusWindow == window) {
1389 // Remove the focus lock so the focus can be changed below
1390 fLockedFocusWindow = NULL;
1391 }
1392
1393 if (window->InWorkspace(fCurrentWorkspace)) {
1394 _UpdateSubsetWorkspaces(window);
1395 _HideWindow(window);
1396 _UpdateFronts();
1397 } else
1398 _WindowChanged(window);
1399
1400 if (FocusWindow() == window)
1401 SetFocusWindow();
1402
1403 _WindowRemoved(window);
1404
1405 if (window->HasWorkspacesViews()) {
1406 // remove workspaces views from this window
1407 BObjectList<WorkspacesView> list(false);
1408 window->FindWorkspacesViews(list);
1409
1410 BAutolock _(fWorkspacesLock);
1411
1412 while (WorkspacesView* view = list.RemoveItemAt(0)) {
1413 fWorkspacesViews.RemoveItem(view);
1414 }
1415 }
1416
1417 NotifyWindowHidden(window, fromMinimize);
1418
1419 UnlockAllWindows();
1420
1421 if (window == fWindowUnderMouse)
1422 _SendFakeMouseMoved();
1423 }
1424
1425
1426 void
MinimizeWindow(Window * window,bool minimize)1427 Desktop::MinimizeWindow(Window* window, bool minimize)
1428 {
1429 if (!LockAllWindows())
1430 return;
1431
1432 if (minimize && !window->IsHidden()) {
1433 HideWindow(window, true);
1434 window->SetMinimized(minimize);
1435 NotifyWindowMinimized(window, minimize);
1436 } else if (!minimize && window->IsHidden()) {
1437 ActivateWindow(window);
1438 // this will unminimize the window for us
1439 NotifyWindowMinimized(window, minimize);
1440 }
1441
1442 UnlockAllWindows();
1443 }
1444
1445
1446 void
MoveWindowBy(Window * window,float x,float y,int32 workspace)1447 Desktop::MoveWindowBy(Window* window, float x, float y, int32 workspace)
1448 {
1449 if (x == 0 && y == 0)
1450 return;
1451
1452 AutoWriteLocker _(fWindowLock);
1453
1454 Window* topWindow = window->TopLayerStackWindow();
1455 if (topWindow != NULL)
1456 window = topWindow;
1457
1458 if (workspace == -1)
1459 workspace = fCurrentWorkspace;
1460 if (!window->IsVisible() || workspace != fCurrentWorkspace) {
1461 if (workspace != fCurrentWorkspace) {
1462 WindowStack* stack = window->GetWindowStack();
1463 if (stack != NULL) {
1464 for (int32 s = 0; s < stack->CountWindows(); s++) {
1465 Window* stackWindow = stack->WindowAt(s);
1466 // move the window on another workspace - this doesn't
1467 // change it's current position
1468 if (stackWindow->Anchor(workspace).position
1469 == kInvalidWindowPosition) {
1470 stackWindow->Anchor(workspace).position
1471 = stackWindow->Frame().LeftTop();
1472 }
1473
1474 stackWindow->Anchor(workspace).position += BPoint(x, y);
1475 stackWindow->SetCurrentWorkspace(workspace);
1476 _WindowChanged(stackWindow);
1477 }
1478 }
1479 } else
1480 window->MoveBy((int32)x, (int32)y);
1481
1482 NotifyWindowMoved(window);
1483 return;
1484 }
1485
1486 // the dirty region starts with the visible area of the window being moved
1487 BRegion newDirtyRegion(window->VisibleRegion());
1488
1489 // stop direct frame buffer access
1490 bool direct = false;
1491 if (window->ServerWindow()->IsDirectlyAccessing()) {
1492 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
1493 direct = true;
1494 }
1495
1496 window->MoveBy((int32)x, (int32)y);
1497
1498 BRegion background;
1499 _RebuildClippingForAllWindows(background);
1500
1501 // construct the region that is possible to be blitted
1502 // to move the contents of the window
1503 BRegion copyRegion(window->VisibleRegion());
1504 copyRegion.OffsetBy((int32)-x, (int32)-y);
1505 copyRegion.IntersectWith(&newDirtyRegion);
1506 // newDirtyRegion == the windows old visible region
1507
1508 // include the the new visible region of the window being
1509 // moved into the dirty region (for now)
1510 newDirtyRegion.Include(&window->VisibleRegion());
1511
1512 // NOTE: Having all windows locked should prevent any
1513 // problems with locking the drawing engine here.
1514 if (GetDrawingEngine()->LockParallelAccess()) {
1515 GetDrawingEngine()->CopyRegion(©Region, (int32)x, (int32)y);
1516 GetDrawingEngine()->UnlockParallelAccess();
1517 }
1518
1519 // in the dirty region, exclude the parts that we
1520 // could move by blitting
1521 copyRegion.OffsetBy((int32)x, (int32)y);
1522 newDirtyRegion.Exclude(©Region);
1523
1524 MarkDirty(newDirtyRegion);
1525 _SetBackground(background);
1526 _WindowChanged(window);
1527
1528 // resume direct frame buffer access
1529 if (direct) {
1530 // TODO: the clipping actually only changes when we move our window
1531 // off screen, or behind some other window
1532 window->ServerWindow()->HandleDirectConnection(
1533 B_DIRECT_START | B_BUFFER_MOVED | B_CLIPPING_MODIFIED);
1534 }
1535
1536 NotifyWindowMoved(window);
1537 }
1538
1539
1540 void
ResizeWindowBy(Window * window,float x,float y)1541 Desktop::ResizeWindowBy(Window* window, float x, float y)
1542 {
1543 if (x == 0 && y == 0)
1544 return;
1545
1546 AutoWriteLocker _(fWindowLock);
1547
1548 Window* topWindow = window->TopLayerStackWindow();
1549 if (topWindow)
1550 window = topWindow;
1551
1552 if (!window->IsVisible()) {
1553 window->ResizeBy((int32)x, (int32)y, NULL);
1554 NotifyWindowResized(window);
1555 return;
1556 }
1557
1558 // The dirty region for the inside of the window is constructed by the window itself in
1559 // ResizeBy()
1560 BRegion newDirtyRegion;
1561 // Track the dirty region outside the window in case it is shrunk in "previouslyOccupiedRegion"
1562 BRegion previouslyOccupiedRegion(window->VisibleRegion());
1563 // Track the region that was drawn in previous update sessions, so we can compute the newly
1564 // exposed areas by excluding this from the update region.
1565 BRegion previousVisibleContentRegion(window->VisibleContentRegion());
1566
1567 // stop direct frame buffer access
1568 bool direct = false;
1569 if (window->ServerWindow()->IsDirectlyAccessing()) {
1570 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
1571 direct = true;
1572 }
1573
1574 window->ResizeBy((int32)x, (int32)y, &newDirtyRegion);
1575
1576 BRegion background;
1577 _RebuildClippingForAllWindows(background);
1578
1579 // we just care for the region outside the window
1580 previouslyOccupiedRegion.Exclude(&window->VisibleRegion());
1581
1582 // make sure the window cannot mark stuff dirty outside
1583 // its visible region...
1584 newDirtyRegion.IntersectWith(&window->VisibleRegion());
1585 // ...because we do this ourselves
1586 newDirtyRegion.Include(&previouslyOccupiedRegion);
1587
1588 // calculate old expose region as window visible region difference
1589 BRegion exposeRegion(previouslyOccupiedRegion);
1590 exposeRegion.Exclude(&window->VisibleRegion());
1591 // ...and new expose region as window content visible region difference
1592 BRegion tmp(window->VisibleContentRegion());
1593 tmp.Exclude(&previousVisibleContentRegion);
1594 exposeRegion.Include(&tmp);
1595
1596 MarkDirty(newDirtyRegion, exposeRegion);
1597 _SetBackground(background);
1598 _WindowChanged(window);
1599
1600 // resume direct frame buffer access
1601 if (direct) {
1602 window->ServerWindow()->HandleDirectConnection(
1603 B_DIRECT_START | B_BUFFER_RESIZED | B_CLIPPING_MODIFIED);
1604 }
1605
1606 NotifyWindowResized(window);
1607 }
1608
1609
1610 void
SetWindowOutlinesDelta(Window * window,BPoint delta)1611 Desktop::SetWindowOutlinesDelta(Window* window, BPoint delta)
1612 {
1613 AutoWriteLocker _(fWindowLock);
1614
1615 if (!window->IsVisible())
1616 return;
1617
1618 BRegion newDirtyRegion;
1619 window->SetOutlinesDelta(delta, &newDirtyRegion);
1620
1621 BRegion background;
1622 _RebuildClippingForAllWindows(background);
1623
1624 MarkDirty(newDirtyRegion);
1625 _SetBackground(background);
1626 }
1627
1628
1629 bool
SetWindowTabLocation(Window * window,float location,bool isShifting)1630 Desktop::SetWindowTabLocation(Window* window, float location, bool isShifting)
1631 {
1632 AutoWriteLocker _(fWindowLock);
1633
1634 BRegion dirty;
1635 bool changed = window->SetTabLocation(location, isShifting, dirty);
1636 if (changed)
1637 RebuildAndRedrawAfterWindowChange(window, dirty);
1638
1639 NotifyWindowTabLocationChanged(window, location, isShifting);
1640
1641 return changed;
1642 }
1643
1644
1645 bool
SetWindowDecoratorSettings(Window * window,const BMessage & settings)1646 Desktop::SetWindowDecoratorSettings(Window* window, const BMessage& settings)
1647 {
1648 AutoWriteLocker _(fWindowLock);
1649
1650 BRegion dirty;
1651 bool changed = window->SetDecoratorSettings(settings, dirty);
1652 bool listenerChanged = SetDecoratorSettings(window, settings);
1653 if (changed || listenerChanged)
1654 RebuildAndRedrawAfterWindowChange(window, dirty);
1655
1656 return changed;
1657 }
1658
1659
1660 void
SetWindowWorkspaces(Window * window,uint32 workspaces)1661 Desktop::SetWindowWorkspaces(Window* window, uint32 workspaces)
1662 {
1663 LockAllWindows();
1664
1665 if (window->IsNormal() && workspaces == B_CURRENT_WORKSPACE)
1666 workspaces = workspace_to_workspaces(CurrentWorkspace());
1667
1668 WindowStack* stack = window->GetWindowStack();
1669 if (stack != NULL) {
1670 for (int32 s = 0; s < stack->CountWindows(); s++) {
1671 window = stack->LayerOrder().ItemAt(s);
1672
1673 uint32 oldWorkspaces = window->Workspaces();
1674 window->WorkspacesChanged(oldWorkspaces, workspaces);
1675 _ChangeWindowWorkspaces(window, oldWorkspaces, workspaces);
1676 }
1677 }
1678 UnlockAllWindows();
1679 }
1680
1681
1682 /*! \brief Adds the window to the desktop.
1683 At this point, the window is still hidden and must be shown explicitly
1684 via ShowWindow().
1685 */
1686 void
AddWindow(Window * window)1687 Desktop::AddWindow(Window *window)
1688 {
1689 LockAllWindows();
1690
1691 fAllWindows.AddWindow(window);
1692 if (!window->IsNormal())
1693 fSubsetWindows.AddWindow(window);
1694
1695 if (window->IsNormal()) {
1696 if (window->Workspaces() == B_CURRENT_WORKSPACE)
1697 window->SetWorkspaces(workspace_to_workspaces(CurrentWorkspace()));
1698 } else {
1699 // subset windows are visible on all workspaces their subset is on
1700 window->SetWorkspaces(window->SubsetWorkspaces());
1701 }
1702
1703 _ChangeWindowWorkspaces(window, 0, window->Workspaces());
1704
1705 NotifyWindowAdded(window);
1706
1707 UnlockAllWindows();
1708 }
1709
1710
1711 void
RemoveWindow(Window * window)1712 Desktop::RemoveWindow(Window *window)
1713 {
1714 LockAllWindows();
1715
1716 if (!window->IsHidden())
1717 HideWindow(window);
1718
1719 fAllWindows.RemoveWindow(window);
1720 if (!window->IsNormal())
1721 fSubsetWindows.RemoveWindow(window);
1722
1723 _ChangeWindowWorkspaces(window, window->Workspaces(), 0);
1724
1725 NotifyWindowRemoved(window);
1726
1727 UnlockAllWindows();
1728
1729 // make sure this window won't get any events anymore
1730
1731 EventDispatcher().RemoveTarget(window->EventTarget());
1732 }
1733
1734
1735 bool
AddWindowToSubset(Window * subset,Window * window)1736 Desktop::AddWindowToSubset(Window* subset, Window* window)
1737 {
1738 if (!subset->AddToSubset(window))
1739 return false;
1740
1741 _ChangeWindowWorkspaces(subset, subset->Workspaces(),
1742 subset->SubsetWorkspaces());
1743 return true;
1744 }
1745
1746
1747 void
RemoveWindowFromSubset(Window * subset,Window * window)1748 Desktop::RemoveWindowFromSubset(Window* subset, Window* window)
1749 {
1750 subset->RemoveFromSubset(window);
1751 _ChangeWindowWorkspaces(subset, subset->Workspaces(),
1752 subset->SubsetWorkspaces());
1753 }
1754
1755
1756 void
FontsChanged(Window * window)1757 Desktop::FontsChanged(Window* window)
1758 {
1759 AutoWriteLocker _(fWindowLock);
1760
1761 BRegion dirty;
1762 window->FontsChanged(&dirty);
1763
1764 RebuildAndRedrawAfterWindowChange(window, dirty);
1765 }
1766
1767
1768 void
ColorUpdated(Window * window,color_which which,rgb_color color)1769 Desktop::ColorUpdated(Window* window, color_which which, rgb_color color)
1770 {
1771 AutoWriteLocker _(fWindowLock);
1772
1773 window->TopView()->ColorUpdated(which, color);
1774
1775 switch (which) {
1776 case B_WINDOW_TAB_COLOR:
1777 case B_WINDOW_TEXT_COLOR:
1778 case B_WINDOW_INACTIVE_TAB_COLOR:
1779 case B_WINDOW_INACTIVE_TEXT_COLOR:
1780 case B_WINDOW_BORDER_COLOR:
1781 case B_WINDOW_INACTIVE_BORDER_COLOR:
1782 break;
1783 default:
1784 return;
1785 }
1786
1787 BRegion dirty;
1788 window->ColorsChanged(&dirty);
1789 RebuildAndRedrawAfterWindowChange(window, dirty);
1790 }
1791
1792
1793 void
SetWindowLook(Window * window,window_look newLook)1794 Desktop::SetWindowLook(Window* window, window_look newLook)
1795 {
1796 if (window->Look() == newLook)
1797 return;
1798
1799 AutoWriteLocker _(fWindowLock);
1800
1801 BRegion dirty;
1802 window->SetLook(newLook, &dirty);
1803 // TODO: test what happens when the window
1804 // finds out it needs to resize itself...
1805
1806 RebuildAndRedrawAfterWindowChange(window, dirty);
1807
1808 NotifyWindowLookChanged(window, newLook);
1809 }
1810
1811
1812 void
SetWindowFeel(Window * window,window_feel newFeel)1813 Desktop::SetWindowFeel(Window* window, window_feel newFeel)
1814 {
1815 if (window->Feel() == newFeel)
1816 return;
1817
1818 LockAllWindows();
1819
1820 bool wasNormal = window->IsNormal();
1821
1822 window->SetFeel(newFeel);
1823
1824 // move the window out of or into the subset window list as needed
1825 if (window->IsNormal() && !wasNormal)
1826 fSubsetWindows.RemoveWindow(window);
1827 else if (!window->IsNormal() && wasNormal)
1828 fSubsetWindows.AddWindow(window);
1829
1830 // A normal window that was once a floating or modal window will
1831 // adopt the window's current workspaces
1832
1833 if (!window->IsNormal()) {
1834 _ChangeWindowWorkspaces(window, window->Workspaces(),
1835 window->SubsetWorkspaces());
1836 }
1837
1838 // make sure the window has the correct position in the window lists
1839 // (ie. all floating windows have to be on the top, ...)
1840
1841 for (int32 i = 0; i < kMaxWorkspaces; i++) {
1842 if (!workspace_in_workspaces(i, window->Workspaces()))
1843 continue;
1844
1845 bool changed = false;
1846 BRegion visibleBefore;
1847 if (i == fCurrentWorkspace && window->IsVisible())
1848 visibleBefore = window->VisibleRegion();
1849
1850 Window* backmost = window->Backmost(_Windows(i).LastWindow(), i);
1851 if (backmost != NULL) {
1852 // check if the backmost window is really behind it
1853 Window* previous = window->PreviousWindow(i);
1854 while (previous != NULL) {
1855 if (previous == backmost)
1856 break;
1857
1858 previous = previous->PreviousWindow(i);
1859 }
1860
1861 if (previous == NULL) {
1862 // need to reinsert window before its backmost window
1863 _Windows(i).RemoveWindow(window);
1864 _Windows(i).AddWindow(window, backmost->NextWindow(i));
1865 changed = true;
1866 }
1867 }
1868
1869 Window* frontmost = window->Frontmost(_Windows(i).FirstWindow(), i);
1870 if (frontmost != NULL) {
1871 // check if the frontmost window is really in front of it
1872 Window* next = window->NextWindow(i);
1873 while (next != NULL) {
1874 if (next == frontmost)
1875 break;
1876
1877 next = next->NextWindow(i);
1878 }
1879
1880 if (next == NULL) {
1881 // need to reinsert window behind its frontmost window
1882 _Windows(i).RemoveWindow(window);
1883 _Windows(i).AddWindow(window, frontmost);
1884 changed = true;
1885 }
1886 }
1887
1888 if (i == fCurrentWorkspace && changed) {
1889 BRegion dummy;
1890 _RebuildClippingForAllWindows(dummy);
1891
1892 // mark everything dirty that is no longer visible, or
1893 // is now visible and wasn't before
1894 BRegion visibleAfter(window->VisibleRegion());
1895 BRegion dirty(visibleAfter);
1896 dirty.Exclude(&visibleBefore);
1897 visibleBefore.Exclude(&visibleAfter);
1898 dirty.Include(&visibleBefore);
1899
1900 MarkDirty(dirty);
1901 }
1902 }
1903
1904 _UpdateFronts();
1905
1906 if (window == FocusWindow() && !window->IsVisible())
1907 SetFocusWindow();
1908
1909 NotifyWindowFeelChanged(window, newFeel);
1910
1911 UnlockAllWindows();
1912 }
1913
1914
1915 void
SetWindowFlags(Window * window,uint32 newFlags)1916 Desktop::SetWindowFlags(Window *window, uint32 newFlags)
1917 {
1918 if (window->Flags() == newFlags)
1919 return;
1920
1921 AutoWriteLocker _(fWindowLock);
1922
1923 BRegion dirty;
1924 window->SetFlags(newFlags, &dirty);
1925 // TODO: test what happens when the window
1926 // finds out it needs to resize itself...
1927
1928 RebuildAndRedrawAfterWindowChange(window, dirty);
1929 }
1930
1931
1932 void
SetWindowTitle(Window * window,const char * title)1933 Desktop::SetWindowTitle(Window *window, const char* title)
1934 {
1935 AutoWriteLocker _(fWindowLock);
1936
1937 BRegion dirty;
1938 window->SetTitle(title, dirty);
1939
1940 RebuildAndRedrawAfterWindowChange(window, dirty);
1941 }
1942
1943
1944 /*! Returns the window under the mouse cursor.
1945 You need to have acquired the All Windows lock when calling this method.
1946 */
1947 Window*
WindowAt(BPoint where)1948 Desktop::WindowAt(BPoint where)
1949 {
1950 for (Window* window = CurrentWindows().LastWindow(); window;
1951 window = window->PreviousWindow(fCurrentWorkspace)) {
1952 if (window->IsVisible() && window->VisibleRegion().Contains(where))
1953 return window->StackedWindowAt(where);
1954 }
1955
1956 return NULL;
1957 }
1958
1959
1960 void
SetMouseEventWindow(Window * window)1961 Desktop::SetMouseEventWindow(Window* window)
1962 {
1963 fMouseEventWindow = window;
1964 }
1965
1966
1967 void
SetViewUnderMouse(const Window * window,int32 viewToken)1968 Desktop::SetViewUnderMouse(const Window* window, int32 viewToken)
1969 {
1970 fWindowUnderMouse = window;
1971 fViewUnderMouse = viewToken;
1972 }
1973
1974
1975 int32
ViewUnderMouse(const Window * window)1976 Desktop::ViewUnderMouse(const Window* window)
1977 {
1978 if (window != NULL && fWindowUnderMouse == window)
1979 return fViewUnderMouse;
1980
1981 return B_NULL_TOKEN;
1982 }
1983
1984
1985 /*! Returns the current keyboard event target candidate - which is either the
1986 top-most window (in case it has the kAcceptKeyboardFocusFlag flag set), or
1987 the one having focus.
1988 The window lock must be held when calling this function.
1989 */
1990 EventTarget*
KeyboardEventTarget()1991 Desktop::KeyboardEventTarget()
1992 {
1993 // Get the top most non-hidden window
1994 Window* window = CurrentWindows().LastWindow();
1995 while (window != NULL && window->IsHidden()) {
1996 window = window->PreviousWindow(fCurrentWorkspace);
1997 }
1998
1999 if (window != NULL && (window->Flags() & kAcceptKeyboardFocusFlag) != 0)
2000 return &window->EventTarget();
2001
2002 if (FocusWindow() != NULL)
2003 return &FocusWindow()->EventTarget();
2004
2005 return NULL;
2006 }
2007
2008
2009 /*! Tries to set the focus to the specified \a focus window. It will make sure,
2010 however, that the window actually can have focus. You are allowed to pass
2011 in a NULL pointer for \a focus.
2012
2013 Besides the B_AVOID_FOCUS flag, a modal window, or a BWindowScreen can both
2014 prevent it from getting focus.
2015
2016 In any case, this method makes sure that there is a focus window, if there
2017 is any window at all, that is.
2018 */
2019 void
SetFocusWindow(Window * nextFocus)2020 Desktop::SetFocusWindow(Window* nextFocus)
2021 {
2022 if (!LockAllWindows())
2023 return;
2024
2025 // test for B_LOCK_WINDOW_FOCUS
2026 if (fLockedFocusWindow && nextFocus != fLockedFocusWindow) {
2027 UnlockAllWindows();
2028 return;
2029 }
2030
2031 bool hasModal = _WindowHasModal(nextFocus);
2032 bool hasWindowScreen = false;
2033
2034 if (!hasModal && nextFocus != NULL) {
2035 // Check whether or not a window screen is in front of the window
2036 // (if it has a modal, the right thing is done, anyway)
2037 Window* window = nextFocus;
2038 while (true) {
2039 window = window->NextWindow(fCurrentWorkspace);
2040 if (window == NULL || window->Feel() == kWindowScreenFeel)
2041 break;
2042 }
2043 if (window != NULL)
2044 hasWindowScreen = true;
2045 }
2046
2047 if (nextFocus == fFocus && nextFocus != NULL && !nextFocus->IsHidden()
2048 && (nextFocus->Flags() & B_AVOID_FOCUS) == 0
2049 && !hasModal && !hasWindowScreen) {
2050 // the window that is supposed to get focus already has focus
2051 UnlockAllWindows();
2052 return;
2053 }
2054
2055 uint32 listIndex = fCurrentWorkspace;
2056 WindowList* list = &_Windows(fCurrentWorkspace);
2057 if (!fSettings->NormalMouse()) {
2058 listIndex = kFocusList;
2059 list = &fFocusList;
2060 }
2061
2062 if (nextFocus == NULL || hasModal || hasWindowScreen) {
2063 nextFocus = list->LastWindow();
2064
2065 if (fSettings->NormalMouse()) {
2066 // If the last window having focus is a window that cannot make it
2067 // to the front, we use that as the next focus
2068 Window* lastFocus = fFocusList.LastWindow();
2069 if (lastFocus != NULL && !lastFocus->SupportsFront()
2070 && _WindowCanHaveFocus(lastFocus)) {
2071 nextFocus = lastFocus;
2072 }
2073 }
2074 }
2075
2076 // make sure no window is chosen that doesn't want focus or cannot have it
2077 while (nextFocus != NULL && !_WindowCanHaveFocus(nextFocus)) {
2078 nextFocus = nextFocus->PreviousWindow(listIndex);
2079 }
2080
2081 if (fFocus == nextFocus) {
2082 // turns out the window that is supposed to get focus now already has it
2083 UnlockAllWindows();
2084 return;
2085 }
2086
2087 team_id oldActiveApp = -1;
2088 team_id newActiveApp = -1;
2089
2090 if (fFocus != NULL) {
2091 fFocus->SetFocus(false);
2092 oldActiveApp = fFocus->ServerWindow()->App()->ClientTeam();
2093 }
2094
2095 fFocus = nextFocus;
2096
2097 if (fFocus != NULL) {
2098 fFocus->SetFocus(true);
2099 newActiveApp = fFocus->ServerWindow()->App()->ClientTeam();
2100
2101 // move current focus to the end of the focus list
2102 fFocusList.RemoveWindow(fFocus);
2103 fFocusList.AddWindow(fFocus);
2104 }
2105
2106 if (newActiveApp == -1) {
2107 // make sure the cursor is visible
2108 HWInterface()->SetCursorVisible(true);
2109 }
2110
2111 UnlockAllWindows();
2112
2113 // change the "active" app if appropriate
2114 if (oldActiveApp == newActiveApp)
2115 return;
2116
2117 BAutolock locker(fApplicationsLock);
2118
2119 for (int32 i = 0; i < fApplications.CountItems(); i++) {
2120 ServerApp* app = fApplications.ItemAt(i);
2121
2122 if (oldActiveApp != -1 && app->ClientTeam() == oldActiveApp)
2123 app->Activate(false);
2124 else if (newActiveApp != -1 && app->ClientTeam() == newActiveApp)
2125 app->Activate(true);
2126 }
2127 }
2128
2129
2130 void
SetFocusLocked(const Window * window)2131 Desktop::SetFocusLocked(const Window* window)
2132 {
2133 AutoWriteLocker _(fWindowLock);
2134
2135 if (window != NULL) {
2136 // Don't allow this to be set when no mouse buttons
2137 // are pressed. (BView::SetMouseEventMask() should only be called
2138 // from mouse hooks.)
2139 if (fLastMouseButtons == 0)
2140 return;
2141 }
2142
2143 fLockedFocusWindow = window;
2144 }
2145
2146
2147 Window*
FindWindowByClientToken(int32 token,team_id teamID)2148 Desktop::FindWindowByClientToken(int32 token, team_id teamID)
2149 {
2150 for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2151 window = window->NextWindow(kAllWindowList)) {
2152 if (window->ServerWindow()->ClientToken() == token
2153 && window->ServerWindow()->ClientTeam() == teamID) {
2154 return window;
2155 }
2156 }
2157
2158 return NULL;
2159 }
2160
2161
2162 ::EventTarget*
FindTarget(BMessenger & messenger)2163 Desktop::FindTarget(BMessenger& messenger)
2164 {
2165 for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2166 window = window->NextWindow(kAllWindowList)) {
2167 if (window->EventTarget().Messenger() == messenger)
2168 return &window->EventTarget();
2169 }
2170
2171 return NULL;
2172 }
2173
2174
2175 void
MarkDirty(BRegion & dirtyRegion,BRegion & exposeRegion)2176 Desktop::MarkDirty(BRegion& dirtyRegion, BRegion& exposeRegion)
2177 {
2178 if (dirtyRegion.CountRects() == 0)
2179 return;
2180
2181 if (LockAllWindows()) {
2182 // send redraw messages to all windows intersecting the dirty region
2183 _TriggerWindowRedrawing(dirtyRegion, exposeRegion);
2184
2185 UnlockAllWindows();
2186 }
2187 }
2188
2189
2190 void
Redraw()2191 Desktop::Redraw()
2192 {
2193 BRegion dirty(fVirtualScreen.Frame());
2194 MarkDirty(dirty);
2195 }
2196
2197
2198 /*! \brief Redraws the background (ie. the desktop window, if any).
2199 */
2200 void
RedrawBackground()2201 Desktop::RedrawBackground()
2202 {
2203 LockAllWindows();
2204
2205 BRegion redraw;
2206
2207 Window* window = CurrentWindows().FirstWindow();
2208 if (window != NULL && window->Feel() == kDesktopWindowFeel) {
2209 redraw = window->VisibleContentRegion();
2210
2211 // look for desktop background view, and update its background color
2212 // TODO: is there a better way to do this?
2213 View* view = window->TopView();
2214 if (view != NULL)
2215 view = view->FirstChild();
2216
2217 while (view != NULL) {
2218 if (view->IsDesktopBackground()) {
2219 view->SetViewColor(fWorkspaces[fCurrentWorkspace].Color());
2220 break;
2221 }
2222 view = view->NextSibling();
2223 }
2224
2225 window->ProcessDirtyRegion(redraw);
2226 } else {
2227 redraw = BackgroundRegion();
2228 fBackgroundRegion.MakeEmpty();
2229 _SetBackground(redraw);
2230 }
2231
2232 _WindowChanged(NULL);
2233 // update workspaces view as well
2234
2235 UnlockAllWindows();
2236 }
2237
2238
2239 bool
ReloadDecor(DecorAddOn * oldDecor)2240 Desktop::ReloadDecor(DecorAddOn* oldDecor)
2241 {
2242 AutoWriteLocker _(fWindowLock);
2243
2244 bool returnValue = true;
2245
2246 if (oldDecor != NULL) {
2247 const DesktopListenerList* oldListeners
2248 = &oldDecor->GetDesktopListeners();
2249 for (int i = 0; i < oldListeners->CountItems(); i++)
2250 UnregisterListener(oldListeners->ItemAt(i));
2251 }
2252
2253 for (Window* window = fAllWindows.FirstWindow(); window != NULL;
2254 window = window->NextWindow(kAllWindowList)) {
2255 BRegion oldBorder;
2256 window->GetBorderRegion(&oldBorder);
2257
2258 if (!window->ReloadDecor()) {
2259 // prevent unloading previous add-on
2260 returnValue = false;
2261 }
2262
2263 BRegion border;
2264 window->GetBorderRegion(&border);
2265
2266 border.Include(&oldBorder);
2267 RebuildAndRedrawAfterWindowChange(window, border);
2268 }
2269
2270 // register new listeners
2271 const DesktopListenerList& newListeners
2272 = gDecorManager.GetDesktopListeners();
2273 for (int i = 0; i < newListeners.CountItems(); i++)
2274 RegisterListener(newListeners.ItemAt(i));
2275
2276 return returnValue;
2277 }
2278
2279
2280 void
MinimizeApplication(team_id team)2281 Desktop::MinimizeApplication(team_id team)
2282 {
2283 AutoWriteLocker locker(fWindowLock);
2284
2285 // Just minimize all windows of that application
2286
2287 for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2288 window = window->NextWindow(kAllWindowList)) {
2289 if (window->ServerWindow()->ClientTeam() != team)
2290 continue;
2291
2292 window->ServerWindow()->NotifyMinimize(true);
2293 }
2294 }
2295
2296
2297 void
BringApplicationToFront(team_id team)2298 Desktop::BringApplicationToFront(team_id team)
2299 {
2300 AutoWriteLocker locker(fWindowLock);
2301
2302 // TODO: for now, just maximize all windows of that application
2303 // TODO: have the ability to lock the current workspace
2304
2305 for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2306 window = window->NextWindow(kAllWindowList)) {
2307 if (window->ServerWindow()->ClientTeam() != team)
2308 continue;
2309
2310 window->ServerWindow()->NotifyMinimize(false);
2311 }
2312 }
2313
2314
2315 void
WindowAction(int32 windowToken,int32 action)2316 Desktop::WindowAction(int32 windowToken, int32 action)
2317 {
2318 if (action != B_MINIMIZE_WINDOW && action != B_BRING_TO_FRONT)
2319 return;
2320
2321 LockAllWindows();
2322
2323 ::ServerWindow* serverWindow;
2324 Window* window;
2325 if (BPrivate::gDefaultTokens.GetToken(windowToken,
2326 B_SERVER_TOKEN, (void**)&serverWindow) != B_OK
2327 || (window = serverWindow->Window()) == NULL) {
2328 UnlockAllWindows();
2329 return;
2330 }
2331
2332 if (action == B_BRING_TO_FRONT && !window->IsMinimized()) {
2333 // the window is visible, we just need to make it the front window
2334 ActivateWindow(window);
2335 } else {
2336 // if not, ask the window if it wants to be unminimized
2337 serverWindow->NotifyMinimize(action == B_MINIMIZE_WINDOW);
2338 }
2339
2340 UnlockAllWindows();
2341 }
2342
2343
2344 void
WriteWindowList(team_id team,BPrivate::LinkSender & sender)2345 Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender)
2346 {
2347 AutoWriteLocker locker(fWindowLock);
2348
2349 // compute the number of windows
2350
2351 int32 count = 0;
2352
2353 for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2354 window = window->NextWindow(kAllWindowList)) {
2355 if (team < B_OK || window->ServerWindow()->ClientTeam() == team)
2356 count++;
2357 }
2358
2359 // write list
2360
2361 sender.StartMessage(B_OK);
2362 sender.Attach<int32>(count);
2363
2364 // first write the windows of the current workspace correctly ordered
2365 for (Window *window = CurrentWindows().LastWindow(); window != NULL;
2366 window = window->PreviousWindow(fCurrentWorkspace)) {
2367 if (team >= B_OK && window->ServerWindow()->ClientTeam() != team)
2368 continue;
2369
2370 sender.Attach<int32>(window->ServerWindow()->ServerToken());
2371 }
2372
2373 // then write all the other windows
2374 for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2375 window = window->NextWindow(kAllWindowList)) {
2376 if ((team >= B_OK && window->ServerWindow()->ClientTeam() != team)
2377 || window->InWorkspace(fCurrentWorkspace))
2378 continue;
2379
2380 sender.Attach<int32>(window->ServerWindow()->ServerToken());
2381 }
2382
2383 sender.Flush();
2384 }
2385
2386
2387 void
WriteWindowInfo(int32 serverToken,BPrivate::LinkSender & sender)2388 Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender)
2389 {
2390 AutoWriteLocker locker(fWindowLock);
2391 BAutolock tokenLocker(BPrivate::gDefaultTokens);
2392
2393 ::ServerWindow* window;
2394 if (BPrivate::gDefaultTokens.GetToken(serverToken,
2395 B_SERVER_TOKEN, (void**)&window) != B_OK) {
2396 sender.StartMessage(B_ENTRY_NOT_FOUND);
2397 sender.Flush();
2398 return;
2399 }
2400
2401 window_info info;
2402 window->GetInfo(info);
2403
2404 float tabSize = 0.0;
2405 float borderSize = 0.0;
2406 ::Window* tmp = window->Window();
2407 if (tmp) {
2408 BMessage message;
2409 if (tmp->GetDecoratorSettings(&message)) {
2410 BRect tabFrame;
2411 message.FindRect("tab frame", &tabFrame);
2412 tabSize = tabFrame.bottom - tabFrame.top;
2413 message.FindFloat("border width", &borderSize);
2414 }
2415 }
2416
2417 int32 length = window->Title() ? strlen(window->Title()) : 0;
2418
2419 sender.StartMessage(B_OK);
2420 sender.Attach<int32>(sizeof(client_window_info) + length + 1);
2421 sender.Attach(&info, sizeof(window_info));
2422 sender.Attach<float>(tabSize);
2423 sender.Attach<float>(borderSize);
2424
2425 if (length > 0)
2426 sender.Attach(window->Title(), length + 1);
2427 else
2428 sender.Attach<char>('\0');
2429
2430 sender.Flush();
2431 }
2432
2433
2434 void
WriteWindowOrder(int32 workspace,BPrivate::LinkSender & sender)2435 Desktop::WriteWindowOrder(int32 workspace, BPrivate::LinkSender& sender)
2436 {
2437 LockSingleWindow();
2438
2439 if (workspace < 0)
2440 workspace = fCurrentWorkspace;
2441 else if (workspace >= kMaxWorkspaces) {
2442 sender.StartMessage(B_BAD_VALUE);
2443 sender.Flush();
2444 UnlockSingleWindow();
2445 return;
2446 }
2447
2448 int32 count = _Windows(workspace).Count();
2449
2450 // write list
2451
2452 sender.StartMessage(B_OK);
2453 sender.Attach<int32>(count);
2454
2455 for (Window *window = _Windows(workspace).LastWindow(); window != NULL;
2456 window = window->PreviousWindow(workspace)) {
2457 sender.Attach<int32>(window->ServerWindow()->ServerToken());
2458 }
2459
2460 sender.Flush();
2461
2462 UnlockSingleWindow();
2463 }
2464
2465
2466 void
WriteApplicationOrder(int32 workspace,BPrivate::LinkSender & sender)2467 Desktop::WriteApplicationOrder(int32 workspace, BPrivate::LinkSender& sender)
2468 {
2469 fApplicationsLock.Lock();
2470 LockSingleWindow();
2471
2472 int32 maxCount = fApplications.CountItems();
2473
2474 fApplicationsLock.Unlock();
2475 // as long as we hold the window lock, no new window can appear
2476
2477 if (workspace < 0)
2478 workspace = fCurrentWorkspace;
2479 else if (workspace >= kMaxWorkspaces) {
2480 sender.StartMessage(B_BAD_VALUE);
2481 sender.Flush();
2482 UnlockSingleWindow();
2483 return;
2484 }
2485
2486 // compute the list of applications on this workspace
2487
2488 team_id* teams = (team_id*)malloc(maxCount * sizeof(team_id));
2489 if (teams == NULL) {
2490 sender.StartMessage(B_NO_MEMORY);
2491 sender.Flush();
2492 UnlockSingleWindow();
2493 return;
2494 }
2495
2496 int32 count = 0;
2497
2498 for (Window *window = _Windows(workspace).LastWindow(); window != NULL;
2499 window = window->PreviousWindow(workspace)) {
2500 team_id team = window->ServerWindow()->ClientTeam();
2501 if (count > 1) {
2502 // see if we already have this team
2503 bool found = false;
2504 for (int32 i = 0; i < count; i++) {
2505 if (teams[i] == team) {
2506 found = true;
2507 break;
2508 }
2509 }
2510 if (found)
2511 continue;
2512 }
2513
2514 ASSERT(count < maxCount);
2515 teams[count++] = team;
2516 }
2517
2518 UnlockSingleWindow();
2519
2520 // write list
2521
2522 sender.StartMessage(B_OK);
2523 sender.Attach<int32>(count);
2524
2525 for (int32 i = 0; i < count; i++) {
2526 sender.Attach<int32>(teams[i]);
2527 }
2528
2529 sender.Flush();
2530 free(teams);
2531 }
2532
2533
2534 void
_LaunchInputServer()2535 Desktop::_LaunchInputServer()
2536 {
2537 BRoster roster;
2538 status_t status = roster.Launch("application/x-vnd.Be-input_server");
2539 if (status == B_OK || status == B_ALREADY_RUNNING)
2540 return;
2541
2542 // Could not load input_server by signature, try well-known location
2543
2544 BEntry entry;
2545 BPath inputServerPath;
2546 if (find_directory(B_SYSTEM_SERVERS_DIRECTORY, &inputServerPath) == B_OK
2547 && inputServerPath.Append("input_server") == B_OK) {
2548 entry.SetTo(inputServerPath.Path());
2549 } else
2550 entry.SetTo("/system/servers/input_server");
2551 entry_ref ref;
2552 status_t entryStatus = entry.GetRef(&ref);
2553 if (entryStatus == B_OK)
2554 entryStatus = roster.Launch(&ref);
2555 if (entryStatus == B_OK || entryStatus == B_ALREADY_RUNNING) {
2556 syslog(LOG_ERR, "Failed to launch the input server by signature: %s!\n",
2557 strerror(status));
2558 return;
2559 }
2560
2561 syslog(LOG_ERR, "Failed to launch the input server: %s!\n",
2562 strerror(entryStatus));
2563 }
2564
2565
2566 void
_GetLooperName(char * name,size_t length)2567 Desktop::_GetLooperName(char* name, size_t length)
2568 {
2569 snprintf(name, length, "d:%d:%s", fUserID,
2570 fTargetScreen == NULL ? "baron" : fTargetScreen);
2571 }
2572
2573
2574 void
_PrepareQuit()2575 Desktop::_PrepareQuit()
2576 {
2577 // let's kill all remaining applications
2578
2579 fApplicationsLock.Lock();
2580
2581 int32 count = fApplications.CountItems();
2582 for (int32 i = 0; i < count; i++) {
2583 ServerApp *app = fApplications.ItemAt(i);
2584 team_id clientTeam = app->ClientTeam();
2585
2586 app->Quit();
2587 kill_team(clientTeam);
2588 }
2589
2590 // wait for the last app to die
2591 if (count > 0) {
2592 acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT,
2593 250000);
2594 }
2595
2596 fApplicationsLock.Unlock();
2597 }
2598
2599
2600 void
_DispatchMessage(int32 code,BPrivate::LinkReceiver & link)2601 Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link)
2602 {
2603 switch (code) {
2604 case AS_CREATE_APP:
2605 {
2606 // Create the ServerApp to node monitor a new BApplication
2607
2608 // Attached data:
2609 // 1) port_id - receiver port of a regular app
2610 // 2) port_id - client looper port - for sending messages to the
2611 // client
2612 // 2) team_id - app's team ID
2613 // 3) int32 - handler token of the regular app
2614 // 4) char * - signature of the regular app
2615
2616 // Find the necessary data
2617 team_id clientTeamID = -1;
2618 port_id clientLooperPort = -1;
2619 port_id clientReplyPort = -1;
2620 int32 htoken = B_NULL_TOKEN;
2621 char* appSignature = NULL;
2622
2623 link.Read<port_id>(&clientReplyPort);
2624 link.Read<port_id>(&clientLooperPort);
2625 link.Read<team_id>(&clientTeamID);
2626 link.Read<int32>(&htoken);
2627 if (link.ReadString(&appSignature) != B_OK)
2628 break;
2629
2630 ObjectDeleter<ServerApp> app(new (std::nothrow) ServerApp(this, clientReplyPort,
2631 clientLooperPort, clientTeamID, htoken, appSignature));
2632 status_t status = B_OK;
2633 if (!app.IsSet())
2634 status = B_NO_MEMORY;
2635 if (status == B_OK)
2636 status = app->InitCheck();
2637 if (status == B_OK)
2638 status = app->Run();
2639 if (status == B_OK) {
2640 // add the new ServerApp to the known list of ServerApps
2641 fApplicationsLock.Lock();
2642 fApplications.AddItem(app.Detach());
2643 fApplicationsLock.Unlock();
2644 } else {
2645 // if everything went well, ServerApp::Run() will notify
2646 // the client - but since it didn't, we do it here
2647 BPrivate::LinkSender reply(clientReplyPort);
2648 reply.StartMessage(status);
2649 reply.Flush();
2650 }
2651
2652 // This is necessary because BPortLink::ReadString allocates memory
2653 free(appSignature);
2654 break;
2655 }
2656
2657 case AS_DELETE_APP:
2658 {
2659 // Delete a ServerApp. Received only from the respective ServerApp
2660 // when a BApplication asks it to quit.
2661
2662 // Attached Data:
2663 // 1) thread_id - thread ID of the ServerApp to be deleted
2664
2665 thread_id thread = -1;
2666 if (link.Read<thread_id>(&thread) < B_OK)
2667 break;
2668
2669 fApplicationsLock.Lock();
2670
2671 // Run through the list of apps and nuke the proper one
2672
2673 int32 count = fApplications.CountItems();
2674 ServerApp* removeApp = NULL;
2675
2676 for (int32 i = 0; i < count; i++) {
2677 ServerApp* app = fApplications.ItemAt(i);
2678
2679 if (app->Thread() == thread) {
2680 fApplications.RemoveItemAt(i);
2681 removeApp = app;
2682 break;
2683 }
2684 }
2685
2686 fApplicationsLock.Unlock();
2687
2688 if (removeApp != NULL)
2689 removeApp->Quit(fShutdownSemaphore);
2690
2691 if (fQuitting && count <= 1) {
2692 // wait for the last app to die
2693 acquire_sem_etc(fShutdownSemaphore, fShutdownCount,
2694 B_RELATIVE_TIMEOUT, 500000);
2695 PostMessage(kMsgQuitLooper);
2696 }
2697 break;
2698 }
2699
2700 case AS_ACTIVATE_APP:
2701 {
2702 // Someone is requesting to activation of a certain app.
2703
2704 // Attached data:
2705 // 1) port_id reply port
2706 // 2) team_id team
2707
2708 status_t status;
2709
2710 // get the parameters
2711 port_id replyPort;
2712 team_id team;
2713 if (link.Read(&replyPort) == B_OK
2714 && link.Read(&team) == B_OK)
2715 status = _ActivateApp(team);
2716 else
2717 status = B_ERROR;
2718
2719 // send the reply
2720 BPrivate::PortLink replyLink(replyPort);
2721 replyLink.StartMessage(status);
2722 replyLink.Flush();
2723 break;
2724 }
2725
2726 case AS_APP_CRASHED:
2727 case AS_DUMP_ALLOCATOR:
2728 case AS_DUMP_BITMAPS:
2729 {
2730 BAutolock locker(fApplicationsLock);
2731
2732 team_id team;
2733 if (link.Read(&team) != B_OK)
2734 break;
2735
2736 for (int32 i = 0; i < fApplications.CountItems(); i++) {
2737 ServerApp* app = fApplications.ItemAt(i);
2738
2739 if (app->ClientTeam() == team)
2740 app->PostMessage(code);
2741 }
2742 break;
2743 }
2744
2745 case AS_EVENT_STREAM_CLOSED:
2746 _LaunchInputServer();
2747 break;
2748
2749 case B_QUIT_REQUESTED:
2750 // We've been asked to quit, so (for now) broadcast to all
2751 // test apps to quit. This situation will occur only when the
2752 // server is compiled as a regular Be application.
2753
2754 fApplicationsLock.Lock();
2755 fShutdownSemaphore = create_sem(0, "desktop shutdown");
2756 fShutdownCount = fApplications.CountItems();
2757 fApplicationsLock.Unlock();
2758
2759 fQuitting = true;
2760 BroadcastToAllApps(AS_QUIT_APP);
2761
2762 // We now need to process the remaining AS_DELETE_APP messages.
2763 // We quit the looper when the last app is deleted.
2764
2765 // if there are no apps to quit, shutdown directly
2766 if (fShutdownCount == 0)
2767 PostMessage(kMsgQuitLooper);
2768
2769 break;
2770
2771 case AS_ACTIVATE_WORKSPACE:
2772 {
2773 int32 index;
2774 link.Read<int32>(&index);
2775 if (index == -1)
2776 index = fPreviousWorkspace;
2777
2778 bool moveFocusWindow;
2779 link.Read<bool>(&moveFocusWindow);
2780
2781 SetWorkspace(index, moveFocusWindow);
2782 break;
2783 }
2784
2785 case AS_TALK_TO_DESKTOP_LISTENER:
2786 {
2787 port_id clientReplyPort;
2788 if (link.Read<port_id>(&clientReplyPort) != B_OK)
2789 break;
2790
2791 BPrivate::LinkSender reply(clientReplyPort);
2792 AutoWriteLocker locker(fWindowLock);
2793 if (MessageForListener(NULL, link, reply) != true) {
2794 // unhandled message, at least send an error if needed
2795 if (link.NeedsReply()) {
2796 reply.StartMessage(B_ERROR);
2797 reply.Flush();
2798 }
2799 }
2800 break;
2801 }
2802
2803 case AS_SET_UI_COLOR:
2804 {
2805 color_which which;
2806 rgb_color color;
2807
2808 if (link.Read<color_which>(&which) == B_OK
2809 && link.Read<rgb_color>(&color) == B_OK) {
2810
2811 const char* colorName = ui_color_name(which);
2812 fPendingColors.SetColor(colorName, color);
2813
2814 DelayedMessage delayed(AS_SET_UI_COLORS, DM_60HZ_DELAY);
2815 delayed.AddTarget(MessagePort());
2816 delayed.SetMerge(DM_MERGE_CANCEL);
2817
2818 delayed.Attach<bool>(true);
2819 delayed.Flush();
2820 }
2821
2822 break;
2823 }
2824
2825 case AS_SET_UI_COLORS:
2826 {
2827 bool flushPendingOnly = false;
2828
2829 if (link.Read<bool>(&flushPendingOnly) != B_OK
2830 || (flushPendingOnly &&
2831 fPendingColors.CountNames(B_RGB_32_BIT_TYPE) == 0)) {
2832 break;
2833 }
2834
2835 if (!flushPendingOnly) {
2836 // Client wants to set a color map
2837 color_which which = B_NO_COLOR;
2838 rgb_color color;
2839
2840 do {
2841 if (link.Read<color_which>(&which) != B_OK
2842 || link.Read<rgb_color>(&color) != B_OK)
2843 break;
2844
2845 fPendingColors.SetColor(ui_color_name(which), color);
2846 } while (which != B_NO_COLOR);
2847 }
2848
2849 _FlushPendingColors();
2850 break;
2851 }
2852
2853 // ToDo: Remove this again. It is a message sent by the
2854 // invalidate_on_exit kernel debugger add-on to trigger a redraw
2855 // after exiting a kernel debugger session.
2856 case 'KDLE':
2857 {
2858 BRegion dirty;
2859 dirty.Include(fVirtualScreen.Frame());
2860 MarkDirty(dirty);
2861 break;
2862 }
2863
2864 default:
2865 printf("Desktop %d:%s received unexpected code %" B_PRId32 "\n", 0,
2866 "baron", code);
2867
2868 if (link.NeedsReply()) {
2869 // the client is now blocking and waiting for a reply!
2870 fLink.StartMessage(B_ERROR);
2871 fLink.Flush();
2872 }
2873 break;
2874 }
2875 }
2876
2877
2878 WindowList&
CurrentWindows()2879 Desktop::CurrentWindows()
2880 {
2881 return fWorkspaces[fCurrentWorkspace].Windows();
2882 }
2883
2884
2885 WindowList&
AllWindows()2886 Desktop::AllWindows()
2887 {
2888 return fAllWindows;
2889 }
2890
2891
2892 Window*
WindowForClientLooperPort(port_id port)2893 Desktop::WindowForClientLooperPort(port_id port)
2894 {
2895 ASSERT_MULTI_LOCKED(fWindowLock);
2896
2897 for (Window* window = fAllWindows.FirstWindow(); window != NULL;
2898 window = window->NextWindow(kAllWindowList)) {
2899 if (window->ServerWindow()->ClientLooperPort() == port)
2900 return window;
2901 }
2902 return NULL;
2903 }
2904
2905
2906 WindowList&
_Windows(int32 index)2907 Desktop::_Windows(int32 index)
2908 {
2909 ASSERT(index >= 0 && index < kMaxWorkspaces);
2910 return fWorkspaces[index].Windows();
2911 }
2912
2913
2914 void
_FlushPendingColors()2915 Desktop::_FlushPendingColors()
2916 {
2917 // Update all windows while we are holding the write lock.
2918
2919 int32 count = fPendingColors.CountNames(B_RGB_32_BIT_TYPE);
2920 if (count == 0)
2921 return;
2922
2923 bool changed[count];
2924 LockedDesktopSettings settings(this);
2925 settings.SetUIColors(fPendingColors, &changed[0]);
2926
2927 int32 index = 0;
2928 char* name = NULL;
2929 type_code type = B_RGB_32_BIT_TYPE;
2930 rgb_color color;
2931 color_which which = B_NO_COLOR;
2932 BMessage clientMessage(B_COLORS_UPDATED);
2933
2934 while (fPendingColors.GetInfo(type, index, &name, &type) == B_OK) {
2935 which = which_ui_color(name);
2936 if (which == B_NO_COLOR || fPendingColors.FindColor(name,
2937 &color) != B_OK || !changed[index]) {
2938 ++index;
2939 continue;
2940 }
2941
2942 for (Window* window = fAllWindows.FirstWindow(); window != NULL;
2943 window = window->NextWindow(kAllWindowList)) {
2944 ColorUpdated(window, which, color);
2945 }
2946
2947 // Ensure client only gets list of changed colors
2948 clientMessage.AddColor(name, color);
2949 ++index;
2950 }
2951
2952 // Notify client applications
2953 BAutolock appListLock(fApplicationsLock);
2954 for (int32 index = 0; index < fApplications.CountItems(); ++index) {
2955 fApplications.ItemAt(index)->SendMessageToClient(&clientMessage);
2956 }
2957
2958 fPendingColors.MakeEmpty();
2959 }
2960
2961
2962 void
_UpdateFloating(int32 previousWorkspace,int32 nextWorkspace,Window * mouseEventWindow)2963 Desktop::_UpdateFloating(int32 previousWorkspace, int32 nextWorkspace,
2964 Window* mouseEventWindow)
2965 {
2966 if (previousWorkspace == -1)
2967 previousWorkspace = fCurrentWorkspace;
2968 if (nextWorkspace == -1)
2969 nextWorkspace = previousWorkspace;
2970
2971 for (Window* floating = fSubsetWindows.FirstWindow(); floating != NULL;
2972 floating = floating->NextWindow(kSubsetList)) {
2973 // we only care about app/subset floating windows
2974 if (floating->Feel() != B_FLOATING_SUBSET_WINDOW_FEEL
2975 && floating->Feel() != B_FLOATING_APP_WINDOW_FEEL)
2976 continue;
2977
2978 if (fFront != NULL && fFront->IsNormal()
2979 && floating->HasInSubset(fFront)) {
2980 // is now visible
2981 if (_Windows(previousWorkspace).HasWindow(floating)
2982 && previousWorkspace != nextWorkspace
2983 && !floating->InSubsetWorkspace(previousWorkspace)) {
2984 // but no longer on the previous workspace
2985 _Windows(previousWorkspace).RemoveWindow(floating);
2986 floating->SetCurrentWorkspace(-1);
2987 }
2988
2989 if (!_Windows(nextWorkspace).HasWindow(floating)) {
2990 // but wasn't before
2991 _Windows(nextWorkspace).AddWindow(floating,
2992 floating->Frontmost(_Windows(nextWorkspace).FirstWindow(),
2993 nextWorkspace));
2994 floating->SetCurrentWorkspace(nextWorkspace);
2995 if (mouseEventWindow != fFront)
2996 _ShowWindow(floating);
2997
2998 // TODO: put the floating last in the floating window list to
2999 // preserve the on screen window order
3000 }
3001 } else if (_Windows(previousWorkspace).HasWindow(floating)
3002 && !floating->InSubsetWorkspace(previousWorkspace)) {
3003 // was visible, but is no longer
3004
3005 _Windows(previousWorkspace).RemoveWindow(floating);
3006 floating->SetCurrentWorkspace(-1);
3007 _HideWindow(floating);
3008
3009 if (FocusWindow() == floating)
3010 SetFocusWindow();
3011 }
3012 }
3013 }
3014
3015
3016 /*! Search the visible windows for a valid back window
3017 (only desktop windows can't be back windows)
3018 */
3019 void
_UpdateBack()3020 Desktop::_UpdateBack()
3021 {
3022 fBack = NULL;
3023
3024 for (Window* window = CurrentWindows().FirstWindow(); window != NULL;
3025 window = window->NextWindow(fCurrentWorkspace)) {
3026 if (window->IsHidden() || window->Feel() == kDesktopWindowFeel)
3027 continue;
3028
3029 fBack = window;
3030 break;
3031 }
3032 }
3033
3034
3035 /*! Search the visible windows for a valid front window
3036 (only normal and modal windows can be front windows)
3037
3038 The only place where you don't want to update floating windows is
3039 during a workspace change - because then you'll call _UpdateFloating()
3040 yourself.
3041 */
3042 void
_UpdateFront(bool updateFloating)3043 Desktop::_UpdateFront(bool updateFloating)
3044 {
3045 fFront = NULL;
3046
3047 for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3048 window = window->PreviousWindow(fCurrentWorkspace)) {
3049 if (window->IsHidden() || window->IsFloating()
3050 || !window->SupportsFront())
3051 continue;
3052
3053 fFront = window;
3054 break;
3055 }
3056
3057 if (updateFloating)
3058 _UpdateFloating();
3059 }
3060
3061
3062 void
_UpdateFronts(bool updateFloating)3063 Desktop::_UpdateFronts(bool updateFloating)
3064 {
3065 _UpdateBack();
3066 _UpdateFront(updateFloating);
3067 }
3068
3069
3070 bool
_WindowHasModal(Window * window) const3071 Desktop::_WindowHasModal(Window* window) const
3072 {
3073 if (window == NULL)
3074 return false;
3075
3076 for (Window* modal = fSubsetWindows.FirstWindow(); modal != NULL;
3077 modal = modal->NextWindow(kSubsetList)) {
3078 // only visible modal windows count
3079 if (!modal->IsModal() || modal->IsHidden())
3080 continue;
3081
3082 if (modal->HasInSubset(window))
3083 return true;
3084 }
3085
3086 return false;
3087 }
3088
3089
3090 /*! Determines whether or not the specified \a window can have focus at all.
3091 */
3092 bool
_WindowCanHaveFocus(Window * window) const3093 Desktop::_WindowCanHaveFocus(Window* window) const
3094 {
3095 return window != NULL
3096 && window->InWorkspace(fCurrentWorkspace)
3097 && (window->Flags() & B_AVOID_FOCUS) == 0
3098 && !_WindowHasModal(window)
3099 && !window->IsHidden();
3100 }
3101
3102
3103 /*! You must at least hold a single window lock when calling this method.
3104 */
3105 void
_WindowChanged(Window * window)3106 Desktop::_WindowChanged(Window* window)
3107 {
3108 ASSERT_MULTI_LOCKED(fWindowLock);
3109
3110 BAutolock _(fWorkspacesLock);
3111
3112 for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) {
3113 WorkspacesView* view = fWorkspacesViews.ItemAt(i);
3114 view->WindowChanged(window);
3115 }
3116 }
3117
3118
3119 /*! You must at least hold a single window lock when calling this method.
3120 */
3121 void
_WindowRemoved(Window * window)3122 Desktop::_WindowRemoved(Window* window)
3123 {
3124 ASSERT_MULTI_LOCKED(fWindowLock);
3125
3126 BAutolock _(fWorkspacesLock);
3127
3128 for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) {
3129 WorkspacesView* view = fWorkspacesViews.ItemAt(i);
3130 view->WindowRemoved(window);
3131 }
3132 }
3133
3134
3135 /*! Shows the window on the screen - it does this independently of the
3136 Window::IsHidden() state.
3137 */
3138 void
_ShowWindow(Window * window,bool affectsOtherWindows)3139 Desktop::_ShowWindow(Window* window, bool affectsOtherWindows)
3140 {
3141 BRegion background;
3142 _RebuildClippingForAllWindows(background);
3143 _SetBackground(background);
3144 _WindowChanged(window);
3145
3146 BRegion dirty(window->VisibleRegion());
3147
3148 if (!affectsOtherWindows) {
3149 // everything that is now visible in the
3150 // window needs a redraw, but other windows
3151 // are not affected, we can call ProcessDirtyRegion()
3152 // of the window, and don't have to use MarkDirty()
3153 window->ProcessDirtyRegion(dirty);
3154 } else
3155 MarkDirty(dirty);
3156
3157 if (window->ServerWindow()->HasDirectFrameBufferAccess()) {
3158 window->ServerWindow()->HandleDirectConnection(
3159 B_DIRECT_START | B_BUFFER_RESET);
3160 }
3161 }
3162
3163
3164 /*! Hides the window from the screen - it does this independently of the
3165 Window::IsHidden() state.
3166 */
3167 void
_HideWindow(Window * window)3168 Desktop::_HideWindow(Window* window)
3169 {
3170 if (window->ServerWindow()->IsDirectlyAccessing())
3171 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
3172
3173 // after rebuilding the clipping,
3174 // this window will not have a visible
3175 // region anymore, so we need to remember
3176 // it now
3177 // (actually that's not true, since
3178 // hidden windows are excluded from the
3179 // clipping calculation, but anyways)
3180 BRegion dirty(window->VisibleRegion());
3181
3182 BRegion background;
3183 _RebuildClippingForAllWindows(background);
3184 _SetBackground(background);
3185 _WindowChanged(window);
3186
3187 MarkDirty(dirty);
3188 }
3189
3190
3191 /*! Updates the workspaces of all subset windows with regard to the
3192 specifed window.
3193 If newIndex is not -1, it will move all subset windows that belong to
3194 the specifed window to the new workspace; this form is only called by
3195 SetWorkspace().
3196 */
3197 void
_UpdateSubsetWorkspaces(Window * window,int32 previousIndex,int32 newIndex)3198 Desktop::_UpdateSubsetWorkspaces(Window* window, int32 previousIndex,
3199 int32 newIndex)
3200 {
3201 STRACE(("_UpdateSubsetWorkspaces(window %p, %s)\n", window,
3202 window->Title()));
3203
3204 // if the window is hidden, the subset windows are up-to-date already
3205 if (!window->IsNormal() || window->IsHidden())
3206 return;
3207
3208 for (Window* subset = fSubsetWindows.FirstWindow(); subset != NULL;
3209 subset = subset->NextWindow(kSubsetList)) {
3210 if (subset->Feel() == B_MODAL_ALL_WINDOW_FEEL
3211 || subset->Feel() == B_FLOATING_ALL_WINDOW_FEEL) {
3212 // These windows are always visible on all workspaces,
3213 // no need to update them.
3214 continue;
3215 }
3216
3217 if (subset->IsFloating()) {
3218 // Floating windows are inserted and removed to the current
3219 // workspace as the need arises - they are not handled here
3220 // but in _UpdateFront()
3221 continue;
3222 }
3223
3224 if (subset->HasInSubset(window)) {
3225 // adopt the workspace change
3226 SetWindowWorkspaces(subset, subset->SubsetWorkspaces());
3227 }
3228 }
3229 }
3230
3231
3232 /*! \brief Adds or removes the window to or from the workspaces it's on.
3233 */
3234 void
_ChangeWindowWorkspaces(Window * window,uint32 oldWorkspaces,uint32 newWorkspaces)3235 Desktop::_ChangeWindowWorkspaces(Window* window, uint32 oldWorkspaces,
3236 uint32 newWorkspaces)
3237 {
3238 if (oldWorkspaces == newWorkspaces)
3239 return;
3240
3241 // apply changes to the workspaces' window lists
3242
3243 LockAllWindows();
3244
3245 // NOTE: we bypass the anchor-mechanism by intention when switching
3246 // the workspace programmatically.
3247
3248 for (int32 i = 0; i < kMaxWorkspaces; i++) {
3249 if (workspace_in_workspaces(i, oldWorkspaces)) {
3250 // window is on this workspace, is it anymore?
3251 if (!workspace_in_workspaces(i, newWorkspaces)) {
3252 _Windows(i).RemoveWindow(window);
3253 if (fLastWorkspaceFocus[i] == window)
3254 fLastWorkspaceFocus[i] = NULL;
3255
3256 if (i == CurrentWorkspace()) {
3257 // remove its appearance from the current workspace
3258 window->SetCurrentWorkspace(-1);
3259
3260 if (!window->IsHidden())
3261 _HideWindow(window);
3262 }
3263 }
3264 } else {
3265 // window was not on this workspace, is it now?
3266 if (workspace_in_workspaces(i, newWorkspaces)) {
3267 _Windows(i).AddWindow(window,
3268 window->Frontmost(_Windows(i).FirstWindow(), i));
3269
3270 if (i == CurrentWorkspace()) {
3271 // make the window visible in current workspace
3272 window->SetCurrentWorkspace(fCurrentWorkspace);
3273
3274 if (!window->IsHidden()) {
3275 // This only affects other windows if this window has
3276 // floating or modal windows that need to be shown as
3277 // well
3278 // TODO: take care of this
3279 _ShowWindow(window, FrontWindow() == window);
3280 }
3281 }
3282 }
3283 }
3284 }
3285
3286 // If the window is visible only on one workspace, we set it's current
3287 // position in that workspace (so that WorkspacesView will find us).
3288 int32 firstWorkspace = -1;
3289 for (int32 i = 0; i < kMaxWorkspaces; i++) {
3290 if ((newWorkspaces & (1L << i)) != 0) {
3291 if (firstWorkspace != -1) {
3292 firstWorkspace = -1;
3293 break;
3294 }
3295 firstWorkspace = i;
3296 }
3297 }
3298 if (firstWorkspace >= 0)
3299 window->Anchor(firstWorkspace).position = window->Frame().LeftTop();
3300
3301 // take care about modals and floating windows
3302 _UpdateSubsetWorkspaces(window);
3303
3304 NotifyWindowWorkspacesChanged(window, newWorkspaces);
3305
3306 UnlockAllWindows();
3307 }
3308
3309
3310 void
_BringWindowsToFront(WindowList & windows,int32 list,bool wereVisible)3311 Desktop::_BringWindowsToFront(WindowList& windows, int32 list, bool wereVisible)
3312 {
3313 // we don't need to redraw what is currently
3314 // visible of the window
3315 BRegion clean;
3316
3317 for (Window* window = windows.FirstWindow(); window != NULL;
3318 window = window->NextWindow(list)) {
3319 if (wereVisible)
3320 clean.Include(&window->VisibleRegion());
3321
3322 CurrentWindows().AddWindow(window,
3323 window->Frontmost(CurrentWindows().FirstWindow(),
3324 fCurrentWorkspace));
3325
3326 _WindowChanged(window);
3327 }
3328
3329 BRegion dummy;
3330 _RebuildClippingForAllWindows(dummy);
3331
3332 // redraw what became visible of the window(s)
3333
3334 BRegion dirty;
3335 for (Window* window = windows.FirstWindow(); window != NULL;
3336 window = window->NextWindow(list)) {
3337 dirty.Include(&window->VisibleRegion());
3338 }
3339
3340 dirty.Exclude(&clean);
3341 MarkDirty(dirty);
3342
3343 _UpdateFront();
3344
3345 if (windows.FirstWindow() == fBack || fBack == NULL)
3346 _UpdateBack();
3347 }
3348
3349
3350 /*! Returns the last focussed non-hidden subset window belonging to the
3351 specified \a window.
3352 */
3353 Window*
_LastFocusSubsetWindow(Window * window)3354 Desktop::_LastFocusSubsetWindow(Window* window)
3355 {
3356 if (window == NULL)
3357 return NULL;
3358
3359 for (Window* front = fFocusList.LastWindow(); front != NULL;
3360 front = front->PreviousWindow(kFocusList)) {
3361 if (front != window && !front->IsHidden()
3362 && window->HasInSubset(front))
3363 return front;
3364 }
3365
3366 return NULL;
3367 }
3368
3369
3370 /*! \brief Checks whether or not a fake mouse moved message needs to be sent
3371 to the previous mouse window.
3372
3373 You need to have the all window lock held when calling this method.
3374 */
3375 bool
_CheckSendFakeMouseMoved(const Window * lastWindowUnderMouse)3376 Desktop::_CheckSendFakeMouseMoved(const Window* lastWindowUnderMouse)
3377 {
3378 Window* window = WindowAt(fLastMousePosition);
3379 return window != lastWindowUnderMouse;
3380 }
3381
3382
3383 /*! \brief Sends a fake B_MOUSE_MOVED event to the window under the mouse,
3384 and also updates the current view under the mouse.
3385
3386 This has only to be done in case the view changed without mouse movement,
3387 ie. because of a workspace change, a closing window, or programmatic window
3388 movement.
3389
3390 You must not have locked any windows when calling this method.
3391 */
3392 void
_SendFakeMouseMoved(Window * window)3393 Desktop::_SendFakeMouseMoved(Window* window)
3394 {
3395 int32 viewToken = B_NULL_TOKEN;
3396 EventTarget* target = NULL;
3397
3398 LockAllWindows();
3399
3400 if (window == NULL)
3401 window = WindowAt(fLastMousePosition);
3402
3403 if (window != NULL) {
3404 BMessage message;
3405 window->MouseMoved(&message, fLastMousePosition, &viewToken, true,
3406 true);
3407
3408 if (viewToken != B_NULL_TOKEN)
3409 target = &window->EventTarget();
3410 }
3411
3412 if (viewToken != B_NULL_TOKEN)
3413 SetViewUnderMouse(window, viewToken);
3414 else {
3415 SetViewUnderMouse(NULL, B_NULL_TOKEN);
3416 SetCursor(NULL);
3417 }
3418
3419 UnlockAllWindows();
3420
3421 if (target != NULL)
3422 EventDispatcher().SendFakeMouseMoved(*target, viewToken);
3423 }
3424
3425
3426 Screen*
_DetermineScreenFor(BRect frame)3427 Desktop::_DetermineScreenFor(BRect frame)
3428 {
3429 AutoReadLocker _(fScreenLock);
3430
3431 // TODO: choose the screen depending on where most of the area is
3432 return fVirtualScreen.ScreenAt(0);
3433 }
3434
3435
3436 void
_RebuildClippingForAllWindows(BRegion & stillAvailableOnScreen)3437 Desktop::_RebuildClippingForAllWindows(BRegion& stillAvailableOnScreen)
3438 {
3439 // the available region on screen starts with the entire screen area
3440 // each window on the screen will take a portion from that area
3441
3442 // figure out what the entire screen area is
3443 stillAvailableOnScreen = fScreenRegion;
3444
3445 // set clipping of each window
3446 for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3447 window = window->PreviousWindow(fCurrentWorkspace)) {
3448 if (!window->IsHidden()) {
3449 window->SetClipping(&stillAvailableOnScreen);
3450 window->SetScreen(_DetermineScreenFor(window->Frame()));
3451
3452 if (window->ServerWindow()->IsDirectlyAccessing()) {
3453 window->ServerWindow()->HandleDirectConnection(
3454 B_DIRECT_MODIFY | B_CLIPPING_MODIFIED);
3455 }
3456
3457 // that windows region is not available on screen anymore
3458 stillAvailableOnScreen.Exclude(&window->VisibleRegion());
3459 }
3460 }
3461 }
3462
3463
3464 void
_TriggerWindowRedrawing(BRegion & dirtyRegion,BRegion & exposeRegion)3465 Desktop::_TriggerWindowRedrawing(BRegion& dirtyRegion, BRegion& exposeRegion)
3466 {
3467 // send redraw messages to all windows intersecting the dirty region
3468 for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3469 window = window->PreviousWindow(fCurrentWorkspace)) {
3470 if (!window->IsHidden()
3471 && dirtyRegion.Intersects(window->VisibleRegion().Frame()))
3472 window->ProcessDirtyRegion(dirtyRegion, exposeRegion);
3473 }
3474 }
3475
3476
3477 void
_SetBackground(BRegion & background)3478 Desktop::_SetBackground(BRegion& background)
3479 {
3480 // NOTE: the drawing operation is caried out
3481 // in the clipping region rebuild, but it is
3482 // ok actually, because it also avoids trails on
3483 // moving windows
3484
3485 // remember the region not covered by any windows
3486 // and redraw the dirty background
3487 BRegion dirtyBackground(background);
3488 dirtyBackground.Exclude(&fBackgroundRegion);
3489 dirtyBackground.IntersectWith(&background);
3490 fBackgroundRegion = background;
3491 if (dirtyBackground.Frame().IsValid()) {
3492 if (GetDrawingEngine()->LockParallelAccess()) {
3493 GetDrawingEngine()->FillRegion(dirtyBackground,
3494 fWorkspaces[fCurrentWorkspace].Color());
3495
3496 GetDrawingEngine()->UnlockParallelAccess();
3497 }
3498 }
3499 }
3500
3501
3502 //! The all window lock must be held when calling this function.
3503 void
RebuildAndRedrawAfterWindowChange(Window * changedWindow,BRegion & dirty)3504 Desktop::RebuildAndRedrawAfterWindowChange(Window* changedWindow,
3505 BRegion& dirty)
3506 {
3507 ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3508 if (!changedWindow->IsVisible() || dirty.CountRects() == 0)
3509 return;
3510
3511 // The following loop is pretty much a copy of
3512 // _RebuildClippingForAllWindows(), but will also
3513 // take care about restricting our dirty region.
3514
3515 // figure out what the entire screen area is
3516 BRegion stillAvailableOnScreen(fScreenRegion);
3517
3518 // set clipping of each window
3519 for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3520 window = window->PreviousWindow(fCurrentWorkspace)) {
3521 if (!window->IsHidden()) {
3522 if (window == changedWindow)
3523 dirty.IntersectWith(&stillAvailableOnScreen);
3524
3525 window->SetClipping(&stillAvailableOnScreen);
3526 window->SetScreen(_DetermineScreenFor(window->Frame()));
3527
3528 if (window->ServerWindow()->IsDirectlyAccessing()) {
3529 window->ServerWindow()->HandleDirectConnection(
3530 B_DIRECT_MODIFY | B_CLIPPING_MODIFIED);
3531 }
3532
3533 // that windows region is not available on screen anymore
3534 stillAvailableOnScreen.Exclude(&window->VisibleRegion());
3535 }
3536 }
3537
3538 _SetBackground(stillAvailableOnScreen);
3539 _WindowChanged(changedWindow);
3540
3541 _TriggerWindowRedrawing(dirty, dirty);
3542 }
3543
3544
3545 //! Suspend all windows with direct access to the frame buffer
3546 void
_SuspendDirectFrameBufferAccess()3547 Desktop::_SuspendDirectFrameBufferAccess()
3548 {
3549 ASSERT_MULTI_LOCKED(fWindowLock);
3550
3551 for (Window* window = fAllWindows.FirstWindow(); window != NULL;
3552 window = window->NextWindow(kAllWindowList)) {
3553 if (window->ServerWindow()->IsDirectlyAccessing())
3554 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
3555 }
3556 }
3557
3558
3559 //! Resume all windows with direct access to the frame buffer
3560 void
_ResumeDirectFrameBufferAccess()3561 Desktop::_ResumeDirectFrameBufferAccess()
3562 {
3563 ASSERT_MULTI_LOCKED(fWindowLock);
3564
3565 for (Window* window = fAllWindows.FirstWindow(); window != NULL;
3566 window = window->NextWindow(kAllWindowList)) {
3567 if (window->IsHidden() || !window->InWorkspace(fCurrentWorkspace))
3568 continue;
3569
3570 if (window->ServerWindow()->HasDirectFrameBufferAccess()) {
3571 window->ServerWindow()->HandleDirectConnection(
3572 B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED);
3573 }
3574 }
3575 }
3576
3577
3578 void
ScreenChanged(Screen * screen)3579 Desktop::ScreenChanged(Screen* screen)
3580 {
3581 AutoWriteLocker windowLocker(fWindowLock);
3582
3583 AutoWriteLocker screenLocker(fScreenLock);
3584 screen->SetPreferredMode();
3585 screenLocker.Unlock();
3586
3587 _ScreenChanged(screen);
3588 }
3589
3590
3591 void
_ScreenChanged(Screen * screen)3592 Desktop::_ScreenChanged(Screen* screen)
3593 {
3594 ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3595
3596 // the entire screen is dirty, because we're actually
3597 // operating on an all new buffer in memory
3598 BRegion dirty(screen->Frame());
3599
3600 // update our cached screen region
3601 fScreenRegion.Set(screen->Frame());
3602 gInputManager->UpdateScreenBounds(screen->Frame());
3603
3604 BRegion background;
3605 _RebuildClippingForAllWindows(background);
3606
3607 fBackgroundRegion.MakeEmpty();
3608 // makes sure that the complete background is redrawn
3609 _SetBackground(background);
3610
3611 // figure out dirty region
3612 dirty.Exclude(&background);
3613 _TriggerWindowRedrawing(dirty, dirty);
3614
3615 // send B_SCREEN_CHANGED to windows on that screen
3616 BMessage update(B_SCREEN_CHANGED);
3617 update.AddInt64("when", real_time_clock_usecs());
3618 update.AddRect("frame", screen->Frame());
3619 update.AddInt32("mode", screen->ColorSpace());
3620
3621 fVirtualScreen.UpdateFrame();
3622
3623 for (Window* window = fAllWindows.FirstWindow(); window != NULL;
3624 window = window->NextWindow(kAllWindowList)) {
3625 if (window->Screen() == screen)
3626 window->ServerWindow()->ScreenChanged(&update);
3627 }
3628 }
3629
3630
3631 /*! \brief activate one of the app's windows.
3632 */
3633 status_t
_ActivateApp(team_id team)3634 Desktop::_ActivateApp(team_id team)
3635 {
3636 // search for an unhidden window in the current workspace
3637
3638 AutoWriteLocker locker(fWindowLock);
3639
3640 for (Window* window = CurrentWindows().LastWindow(); window != NULL;
3641 window = window->PreviousWindow(fCurrentWorkspace)) {
3642 if (!window->IsHidden() && window->IsNormal()
3643 && window->ServerWindow()->ClientTeam() == team) {
3644 ActivateWindow(window);
3645 return B_OK;
3646 }
3647 }
3648
3649 // search for an unhidden window to give focus to
3650
3651 for (Window* window = fAllWindows.FirstWindow(); window != NULL;
3652 window = window->NextWindow(kAllWindowList)) {
3653 // if window is a normal window of the team, and not hidden,
3654 // we've found our target
3655 if (!window->IsHidden() && window->IsNormal()
3656 && window->ServerWindow()->ClientTeam() == team) {
3657 ActivateWindow(window);
3658 return B_OK;
3659 }
3660 }
3661
3662 // TODO: we cannot maximize minimized windows here (with the window lock
3663 // write locked). To work-around this, we could forward the request to
3664 // the ServerApp of this team - it maintains its own window list, and can
3665 // therefore call ActivateWindow() without holding the window lock.
3666 return B_BAD_VALUE;
3667 }
3668
3669
3670 void
_SetCurrentWorkspaceConfiguration()3671 Desktop::_SetCurrentWorkspaceConfiguration()
3672 {
3673 ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3674
3675 status_t status = fDirectScreenLock.LockWithTimeout(1000000L);
3676 if (status != B_OK) {
3677 // The application having the direct screen lock didn't give it up in
3678 // time, make it crash
3679 syslog(LOG_ERR, "Team %" B_PRId32 " did not give up its direct screen "
3680 "lock.\n", fDirectScreenTeam);
3681
3682 debug_thread(fDirectScreenTeam);
3683 fDirectScreenTeam = -1;
3684 } else
3685 fDirectScreenLock.Unlock();
3686
3687 AutoWriteLocker _(fScreenLock);
3688
3689 uint32 changedScreens;
3690 fVirtualScreen.SetConfiguration(*this,
3691 fWorkspaces[fCurrentWorkspace].CurrentScreenConfiguration(),
3692 &changedScreens);
3693
3694 for (int32 i = 0; changedScreens != 0; i++, changedScreens /= 2) {
3695 if ((changedScreens & (1 << i)) != 0)
3696 _ScreenChanged(fVirtualScreen.ScreenAt(i));
3697 }
3698 }
3699
3700
3701 /*! Changes the current workspace to the one specified by \a index.
3702 You must hold the all window lock when calling this method.
3703 */
3704 void
_SetWorkspace(int32 index,bool moveFocusWindow)3705 Desktop::_SetWorkspace(int32 index, bool moveFocusWindow)
3706 {
3707 ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3708
3709 int32 previousIndex = fCurrentWorkspace;
3710 rgb_color previousColor = fWorkspaces[fCurrentWorkspace].Color();
3711 bool movedMouseEventWindow = false;
3712 Window* movedWindow = NULL;
3713 if (moveFocusWindow) {
3714 if (fMouseEventWindow != NULL)
3715 movedWindow = fMouseEventWindow;
3716 else
3717 movedWindow = FocusWindow();
3718 }
3719
3720 if (movedWindow != NULL) {
3721 if (movedWindow->IsNormal()) {
3722 if (!movedWindow->InWorkspace(index)) {
3723 // The window currently being dragged will follow us to this
3724 // workspace if it's not already on it.
3725 // But only normal windows are following
3726 uint32 oldWorkspaces = movedWindow->Workspaces();
3727
3728 WindowStack* stack = movedWindow->GetWindowStack();
3729 if (stack != NULL) {
3730 for (int32 s = 0; s < stack->CountWindows(); s++) {
3731 Window* stackWindow = stack->LayerOrder().ItemAt(s);
3732
3733 _Windows(previousIndex).RemoveWindow(stackWindow);
3734 _Windows(index).AddWindow(stackWindow,
3735 stackWindow->Frontmost(
3736 _Windows(index).FirstWindow(), index));
3737
3738 // send B_WORKSPACES_CHANGED message
3739 stackWindow->WorkspacesChanged(oldWorkspaces,
3740 stackWindow->Workspaces());
3741 }
3742 }
3743 // TODO: subset windows will always flicker this way
3744
3745 movedMouseEventWindow = true;
3746
3747 NotifyWindowWorkspacesChanged(movedWindow,
3748 movedWindow->Workspaces());
3749 } else {
3750 // make sure it's frontmost
3751 _Windows(index).RemoveWindow(movedWindow);
3752 _Windows(index).AddWindow(movedWindow,
3753 movedWindow->Frontmost(_Windows(index).FirstWindow(),
3754 index));
3755 }
3756 }
3757
3758 movedWindow->Anchor(index).position = movedWindow->Frame().LeftTop();
3759 }
3760
3761 if (movedWindow == NULL || movedWindow->InWorkspace(previousIndex))
3762 fLastWorkspaceFocus[previousIndex] = FocusWindow();
3763 else
3764 fLastWorkspaceFocus[previousIndex] = NULL;
3765
3766 // build region of windows that are no longer visible in the new workspace
3767
3768 BRegion dirty;
3769
3770 for (Window* window = CurrentWindows().FirstWindow();
3771 window != NULL; window = window->NextWindow(previousIndex)) {
3772 // store current position in Workspace anchor
3773 window->Anchor(previousIndex).position = window->Frame().LeftTop();
3774
3775 if (!window->IsHidden()
3776 && window->ServerWindow()->IsDirectlyAccessing())
3777 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
3778
3779 window->WorkspaceActivated(previousIndex, false);
3780
3781 if (window->InWorkspace(index))
3782 continue;
3783
3784 if (!window->IsHidden()) {
3785 // this window will no longer be visible
3786 dirty.Include(&window->VisibleRegion());
3787 }
3788
3789 window->SetCurrentWorkspace(-1);
3790 }
3791
3792 fPreviousWorkspace = fCurrentWorkspace;
3793 fCurrentWorkspace = index;
3794
3795 // Change the display modes, if needed
3796 _SetCurrentWorkspaceConfiguration();
3797
3798 // Show windows, and include them in the changed region - but only
3799 // those that were not visible before (or whose position changed)
3800
3801 WindowList windows(kWorkingList);
3802 BList previousRegions;
3803
3804 for (Window* window = _Windows(index).FirstWindow();
3805 window != NULL; window = window->NextWindow(index)) {
3806 BPoint position = window->Anchor(index).position;
3807
3808 window->SetCurrentWorkspace(index);
3809
3810 if (window->IsHidden())
3811 continue;
3812
3813 if (position == kInvalidWindowPosition) {
3814 // if you enter a workspace for the first time, the position
3815 // of the window in the previous workspace is adopted
3816 position = window->Frame().LeftTop();
3817 // TODO: make sure the window is still on-screen if it
3818 // was before!
3819 }
3820
3821 if (!window->InWorkspace(previousIndex)) {
3822 // This window was not visible before, make sure its frame
3823 // is up-to-date
3824 if (window->Frame().LeftTop() != position) {
3825 BPoint offset = position - window->Frame().LeftTop();
3826 window->MoveBy((int32)offset.x, (int32)offset.y);
3827 }
3828 continue;
3829 }
3830
3831 if (window->Frame().LeftTop() != position) {
3832 // the window was visible before, but its on-screen location changed
3833 BPoint offset = position - window->Frame().LeftTop();
3834 MoveWindowBy(window, offset.x, offset.y);
3835 // TODO: be a bit smarter than this...
3836 } else {
3837 // We need to remember the previous visible region of the
3838 // window if they changed their order
3839 ObjectDeleter<BRegion> region(new (std::nothrow)
3840 BRegion(window->VisibleRegion()));
3841 if (region.IsSet()) {
3842 if (previousRegions.AddItem(region.Detach()))
3843 windows.AddWindow(window);
3844 }
3845 }
3846 }
3847
3848 _UpdateFronts(false);
3849 _UpdateFloating(previousIndex, index,
3850 movedMouseEventWindow ? movedWindow : NULL);
3851
3852 BRegion stillAvailableOnScreen;
3853 _RebuildClippingForAllWindows(stillAvailableOnScreen);
3854 _SetBackground(stillAvailableOnScreen);
3855
3856 for (Window* window = _Windows(index).FirstWindow(); window != NULL;
3857 window = window->NextWindow(index)) {
3858 // send B_WORKSPACE_ACTIVATED message
3859 window->WorkspaceActivated(index, true);
3860
3861 if (!window->IsHidden()
3862 && window->ServerWindow()->HasDirectFrameBufferAccess()) {
3863 window->ServerWindow()->HandleDirectConnection(
3864 B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED);
3865 }
3866
3867 if (window->InWorkspace(previousIndex) || window->IsHidden()
3868 || (window == movedWindow && movedWindow->IsNormal())
3869 || (!window->IsNormal()
3870 && window->HasInSubset(movedWindow))) {
3871 // This window was visible before, and is already handled in the
3872 // above loop
3873 continue;
3874 }
3875
3876 dirty.Include(&window->VisibleRegion());
3877 }
3878
3879 // Catch order changes in the new workspaces window list
3880 int32 i = 0;
3881 for (Window* window = windows.FirstWindow(); window != NULL;
3882 window = window->NextWindow(kWorkingList), i++) {
3883 BRegion* region = (BRegion*)previousRegions.ItemAt(i);
3884 region->ExclusiveInclude(&window->VisibleRegion());
3885 dirty.Include(region);
3886 delete region;
3887 }
3888
3889 // Set new focus, but keep focus to a floating window if still visible
3890 if (movedWindow != NULL)
3891 SetFocusWindow(movedWindow);
3892 else if (!_Windows(index).HasWindow(FocusWindow())
3893 || (FocusWindow() != NULL && !FocusWindow()->IsFloating()))
3894 SetFocusWindow(fLastWorkspaceFocus[index]);
3895
3896 _WindowChanged(NULL);
3897 MarkDirty(dirty);
3898
3899 #if 0
3900 // Show the dirty regions of this workspace switch
3901 if (GetDrawingEngine()->LockParallelAccess()) {
3902 GetDrawingEngine()->FillRegion(dirty, (rgb_color){255, 0, 0});
3903 GetDrawingEngine()->UnlockParallelAccess();
3904 snooze(100000);
3905 }
3906 #endif
3907
3908 if (previousColor != fWorkspaces[fCurrentWorkspace].Color())
3909 RedrawBackground();
3910 }
3911