/* * Copyright 2001-2019, Haiku. * Distributed under the terms of the MIT License. * * Authors: * DarkWyrm * Adrian Oanca * Stephan Aßmus * Stefano Ceccherini * Axel Dörfler * Artur Wyszynski * Philippe Saint-Pierre * Brecht Machiels * Julian Harnath * Joseph Groover */ /*! \class ServerWindow The ServerWindow class handles all BWindow messaging; it forwards all BWindow requests to the corresponding app_server classes, that is Desktop, Window, and View. Furthermore, it also sends app_server requests/notices to its BWindow. There is one ServerWindow per BWindow. */ #include "ServerWindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "clipping.h" #include "utf8_functions.h" #include "AlphaMask.h" #include "AppServer.h" #include "AutoDeleter.h" #include "BBitmapBuffer.h" #include "BitmapManager.h" #include "Desktop.h" #include "DirectWindowInfo.h" #include "DrawingEngine.h" #include "DrawState.h" #include "HWInterface.h" #include "Layer.h" #include "Overlay.h" #include "ProfileMessageSupport.h" #include "RenderingBuffer.h" #include "ServerApp.h" #include "ServerBitmap.h" #include "ServerPicture.h" #include "ServerProtocol.h" #include "Window.h" #include "WorkspacesView.h" using std::nothrow; //#define TRACE_SERVER_WINDOW #ifdef TRACE_SERVER_WINDOW # include # define STRACE(x) debug_printf x #else # define STRACE(x) ; #endif //#define TRACE_SERVER_WINDOW_MESSAGES #ifdef TRACE_SERVER_WINDOW_MESSAGES # include static const char* kDrawingModeMap[] = { "B_OP_COPY", "B_OP_OVER", "B_OP_ERASE", "B_OP_INVERT", "B_OP_ADD", "B_OP_SUBTRACT", "B_OP_BLEND", "B_OP_MIN", "B_OP_MAX", "B_OP_SELECT", "B_OP_ALPHA", "fix kDrawingModeMap", "fix kDrawingModeMap", "fix kDrawingModeMap", "fix kDrawingModeMap", "fix kDrawingModeMap", }; # define DTRACE(x) debug_printf x #else # define DTRACE(x) ; #endif //#define TRACE_SERVER_GRADIENTS #ifdef TRACE_SERVER_GRADIENTS # include # define GTRACE(x) debug_printf x #else # define GTRACE(x) ; #endif //#define PROFILE_MESSAGE_LOOP #ifdef PROFILE_MESSAGE_LOOP struct profile { int32 code; int32 count; bigtime_t time; }; static profile sMessageProfile[AS_LAST_CODE]; static profile sRedrawProcessingTime; //static profile sNextMessageTime; #endif // #pragma mark - #ifdef PROFILE_MESSAGE_LOOP static int compare_message_profiles(const void* _a, const void* _b) { profile* a = (profile*)*(void**)_a; profile* b = (profile*)*(void**)_b; if (a->time < b->time) return 1; if (a->time > b->time) return -1; return 0; } #endif // #pragma mark - /*! Sets up the basic BWindow counterpart - you have to call Init() before you can actually use it, though. */ ServerWindow::ServerWindow(const char* title, ServerApp* app, port_id clientPort, port_id looperPort, int32 clientToken) : MessageLooper(title && *title ? title : "Unnamed Window"), fTitle(NULL), fDesktop(app->GetDesktop()), fServerApp(app), fWindowAddedToDesktop(false), fClientTeam(app->ClientTeam()), fMessagePort(-1), fClientReplyPort(clientPort), fClientLooperPort(looperPort), fClientToken(clientToken), fCurrentView(NULL), fCurrentDrawingRegion(), fCurrentDrawingRegionValid(false), fIsDirectlyAccessing(false) { STRACE(("ServerWindow(%s)::ServerWindow()\n", title)); SetTitle(title); fServerToken = BPrivate::gDefaultTokens.NewToken(B_SERVER_TOKEN, this); BMessenger::Private(fFocusMessenger).SetTo(fClientTeam, looperPort, B_PREFERRED_TOKEN); BMessenger::Private(fHandlerMessenger).SetTo(fClientTeam, looperPort, clientToken); fEventTarget.SetTo(fFocusMessenger); fDeathSemaphore = create_sem(0, "window death"); } /*! Tears down all connections the main app_server objects, and deletes some internals. */ ServerWindow::~ServerWindow() { STRACE(("ServerWindow(%s@%p):~ServerWindow()\n", fTitle, this)); if (!fWindow->IsOffscreenWindow()) { fWindowAddedToDesktop = false; fDesktop->RemoveWindow(fWindow.Get()); } if (App() != NULL) { App()->RemoveWindow(this); fServerApp = NULL; } fWindow.Unset(); // TODO: is it really needed? free(fTitle); delete_port(fMessagePort); BPrivate::gDefaultTokens.RemoveToken(fServerToken); fDirectWindowInfo.Unset(); // TODO: is it really needed? STRACE(("ServerWindow(%p) will exit NOW\n", this)); delete_sem(fDeathSemaphore); #ifdef PROFILE_MESSAGE_LOOP BList profiles; for (int32 i = 0; i < AS_LAST_CODE; i++) { if (sMessageProfile[i].count == 0) continue; sMessageProfile[i].code = i; profiles.AddItem(&sMessageProfile[i]); } profiles.SortItems(compare_message_profiles); int32 count = profiles.CountItems(); for (int32 i = 0; i < count; i++) { profile* p = (profile*)profiles.ItemAtFast(i); printf("[%s] called %" B_PRId32 " times, %g secs (%" B_PRId64 " usecs " "per call)\n", string_for_message_code(p->code), p->count, p->time / 1000000.0, p->time / p->count); } if (sRedrawProcessingTime.count > 0) { printf("average redraw processing time: %g secs, count: %" B_PRId32 " " "(%" B_PRId64 " usecs per call)\n", sRedrawProcessingTime.time / 1000000.0, sRedrawProcessingTime.count, sRedrawProcessingTime.time / sRedrawProcessingTime.count); } // if (sNextMessageTime.count > 0) { // printf("average NextMessage() time: %g secs, count: %ld (%lld usecs per call)\n", // sNextMessageTime.time / 1000000.0, sNextMessageTime.count, // sNextMessageTime.time / sNextMessageTime.count); // } #endif } status_t ServerWindow::Init(BRect frame, window_look look, window_feel feel, uint32 flags, uint32 workspace) { if (!App()->AddWindow(this)) { fServerApp = NULL; return B_NO_MEMORY; } if (fTitle == NULL) return B_NO_MEMORY; // fMessagePort is the port to which the app sends messages for the server fMessagePort = create_port(100, fTitle); if (fMessagePort < B_OK) return fMessagePort; fLink.SetSenderPort(fClientReplyPort); fLink.SetReceiverPort(fMessagePort); // We cannot call MakeWindow in the constructor, since it // is a virtual function! fWindow.SetTo(MakeWindow(frame, fTitle, look, feel, flags, workspace)); if (!fWindow.IsSet() || fWindow->InitCheck() != B_OK) { fWindow.Unset(); return B_NO_MEMORY; } if (!fWindow->IsOffscreenWindow()) { fDesktop->AddWindow(fWindow.Get()); fWindowAddedToDesktop = true; } return B_OK; } /*! Returns the ServerWindow's Window, if it exists and has been added to the Desktop already. In other words, you cannot assume this method will always give you a valid pointer. */ Window* ServerWindow::Window() const { if (!fWindowAddedToDesktop) return NULL; return fWindow.Get(); } void ServerWindow::_PrepareQuit() { if (fThread == find_thread(NULL)) { // make sure we're hidden fDesktop->LockSingleWindow(); _Hide(); fDesktop->UnlockSingleWindow(); } else if (fThread >= B_OK) PostMessage(AS_INTERNAL_HIDE_WINDOW); } void ServerWindow::_GetLooperName(char* name, size_t length) { const char *title = Title(); if (title == NULL || !title[0]) title = "Unnamed Window"; snprintf(name, length, "w:%" B_PRId32 ":%s", ClientTeam(), title); } /*! Shows the window's Window. */ void ServerWindow::_Show() { // NOTE: if you do something else, other than sending a port message, PLEASE lock STRACE(("ServerWindow %s: _Show\n", Title())); if (fQuitting || fWindow->IsMinimized() || !fWindow->IsHidden() || fWindow->IsOffscreenWindow() || fWindow->TopView() == NULL) return; // TODO: Maybe we need to dispatch a message to the desktop to show/hide us // instead of doing it from this thread. fDesktop->UnlockSingleWindow(); fDesktop->ShowWindow(fWindow.Get()); if (fDirectWindowInfo.IsSet() && fDirectWindowInfo->IsFullScreen()) _ResizeToFullScreen(); fDesktop->LockSingleWindow(); } /*! Hides the window's Window. You need to have all windows locked when calling this function. */ void ServerWindow::_Hide() { STRACE(("ServerWindow %s: _Hide\n", Title())); if (fWindow->IsHidden() || fWindow->IsOffscreenWindow()) return; fDesktop->UnlockSingleWindow(); fDesktop->HideWindow(fWindow.Get()); fDesktop->LockSingleWindow(); } void ServerWindow::RequestRedraw() { PostMessage(AS_REDRAW, 0); // we don't care if this fails - it's only a notification, and if // it fails, there are obviously enough messages in the queue // already atomic_add(&fRedrawRequested, 1); } void ServerWindow::SetTitle(const char* newTitle) { char* oldTitle = fTitle; if (newTitle == NULL) newTitle = ""; fTitle = strdup(newTitle); if (fTitle == NULL) { // out of memory condition fTitle = oldTitle; return; } free(oldTitle); if (Thread() >= B_OK) { char name[B_OS_NAME_LENGTH]; _GetLooperName(name, sizeof(name)); rename_thread(Thread(), name); } if (fWindow.IsSet()) fDesktop->SetWindowTitle(fWindow.Get(), newTitle); } //! Requests that the ServerWindow's BWindow quit void ServerWindow::NotifyQuitRequested() { // NOTE: if you do something else, other than sending a port message, // PLEASE lock STRACE(("ServerWindow %s: Quit\n", fTitle)); BMessage msg(B_QUIT_REQUESTED); SendMessageToClient(&msg); } void ServerWindow::NotifyMinimize(bool minimize) { if (fWindow->Feel() != B_NORMAL_WINDOW_FEEL) return; // The client is responsible for the actual minimization BMessage msg(B_MINIMIZE); msg.AddInt64("when", real_time_clock_usecs()); msg.AddBool("minimize", minimize); SendMessageToClient(&msg); } //! Sends a message to the client to perform a Zoom void ServerWindow::NotifyZoom() { // NOTE: if you do something else, other than sending a port message, // PLEASE lock BMessage msg(B_ZOOM); SendMessageToClient(&msg); } void ServerWindow::GetInfo(window_info& info) { info.team = ClientTeam(); info.server_token = ServerToken(); info.thread = Thread(); info.client_token = ClientToken(); info.client_port = fClientLooperPort; info.workspaces = fWindow->Workspaces(); // logic taken from Switcher comments and experiments if (fWindow->IsHidden()) info.layer = 0; else if (fWindow->IsVisible()) { if (fWindow->Feel() == kDesktopWindowFeel) info.layer = 2; else if (fWindow->IsFloating() || fWindow->IsModal()) info.layer = 4; else info.layer = 3; } else info.layer = 1; info.feel = fWindow->Feel(); info.flags = fWindow->Flags(); info.window_left = (int)floor(fWindow->Frame().left); info.window_top = (int)floor(fWindow->Frame().top); info.window_right = (int)floor(fWindow->Frame().right); info.window_bottom = (int)floor(fWindow->Frame().bottom); info.show_hide_level = fWindow->ShowLevel(); info.is_mini = fWindow->IsMinimized(); } void ServerWindow::ResyncDrawState() { _UpdateDrawState(fCurrentView); } View* ServerWindow::_CreateView(BPrivate::LinkReceiver& link, View** _parent) { // NOTE: no need to check for a lock. This is a private method. int32 token; BRect frame; uint32 resizeMask; uint32 eventMask; uint32 eventOptions; uint32 flags; bool hidden; int32 parentToken; char* name = NULL; rgb_color viewColor; BPoint scrollingOffset; link.Read(&token); link.ReadString(&name); link.Read(&frame); link.Read(&scrollingOffset); link.Read(&resizeMask); link.Read(&eventMask); link.Read(&eventOptions); link.Read(&flags); link.Read(&hidden); link.Read(&viewColor); link.Read(&parentToken); STRACE(("ServerWindow(%s)::_CreateView()-> view %s, token %" B_PRId32 "\n", fTitle, name, token)); View* newView; if ((flags & kWorkspacesViewFlag) != 0) { newView = new (nothrow) WorkspacesView(frame, scrollingOffset, name, token, resizeMask, flags); } else { newView = new (nothrow) View(frame, scrollingOffset, name, token, resizeMask, flags); } free(name); if (newView == NULL) return NULL; if (newView->InitCheck() != B_OK) { delete newView; return NULL; } // there is no way of setting this, other than manually :-) newView->SetViewColor(viewColor); newView->SetHidden(hidden); newView->SetEventMask(eventMask, eventOptions); if (eventMask != 0 || eventOptions != 0) { // fDesktop->UnlockSingleWindow(); // fDesktop->LockAllWindows(); fDesktop->UnlockAllWindows(); // TODO: possible deadlock fDesktop->EventDispatcher().AddListener(EventTarget(), newView->Token(), eventMask, eventOptions); fDesktop->LockAllWindows(); // fDesktop->UnlockAllWindows(); // fDesktop->LockSingleWindow(); } // Initialize the view with the current application plain font. // NOTE: This might be out of sync with the global app_server plain // font, but that is so on purpose! The client needs to resync itself // with the app_server fonts upon notification, but if we just use // the current font here, the be_plain_font on the client may still // hold old values. So this needs to be an update initiated by the // client application. newView->CurrentState()->SetFont(App()->PlainFont()); if (_parent) { View *parent; if (App()->ViewTokens().GetToken(parentToken, B_HANDLER_TOKEN, (void**)&parent) != B_OK || parent->Window()->ServerWindow() != this) { debug_printf("View token not found!\n"); parent = NULL; } *_parent = parent; } return newView; } /*! Dispatches all window messages, and those view messages that don't need a valid fCurrentView (ie. view creation). */ void ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) { switch (code) { case AS_SHOW_OR_HIDE_WINDOW: { int32 showLevel; if (link.Read(&showLevel) == B_OK) { DTRACE(("ServerWindow %s: Message AS_SHOW_OR_HIDE_WINDOW, " "show level: %" B_PRId32 "\n", Title(), showLevel)); fWindow->SetShowLevel(showLevel); if (showLevel <= 0) _Show(); else _Hide(); } break; } // Only for internal use within this class case AS_INTERNAL_HIDE_WINDOW: _Hide(); break; case AS_MINIMIZE_WINDOW: { bool minimize; if (link.Read(&minimize) == B_OK) { DTRACE(("ServerWindow %s: Message AS_MINIMIZE_WINDOW, " "minimize: %d\n", Title(), minimize)); fDesktop->UnlockSingleWindow(); fDesktop->MinimizeWindow(fWindow.Get(), minimize); fDesktop->LockSingleWindow(); } break; } case AS_ACTIVATE_WINDOW: { bool activate = true; if (link.Read(&activate) != B_OK) break; DTRACE(("ServerWindow %s: Message AS_ACTIVATE_WINDOW: activate: " "%d\n", Title(), activate)); fDesktop->UnlockSingleWindow(); if (activate) fDesktop->SelectWindow(fWindow.Get()); else fDesktop->SendWindowBehind(fWindow.Get(), NULL); fDesktop->LockSingleWindow(); break; } case AS_SEND_BEHIND: { // Has the all-window lock int32 token; team_id teamID; status_t status = B_ERROR; link.Read(&token); if (link.Read(&teamID) == B_OK) { ::Window* behindOf = fDesktop->FindWindowByClientToken(token, teamID); DTRACE(("ServerWindow %s: Message AS_SEND_BEHIND %s\n", Title(), behindOf != NULL ? behindOf->Title() : "NULL")); if (behindOf != NULL || token == -1) { fDesktop->SendWindowBehind(fWindow.Get(), behindOf); status = B_OK; } else status = B_NAME_NOT_FOUND; } fLink.StartMessage(status); fLink.Flush(); break; } case B_QUIT_REQUESTED: DTRACE(("ServerWindow %s received quit request\n", Title())); NotifyQuitRequested(); break; case AS_ENABLE_UPDATES: DTRACE(("ServerWindow %s: Message AS_ENABLE_UPDATES\n", Title())); fWindow->EnableUpdateRequests(); break; case AS_DISABLE_UPDATES: DTRACE(("ServerWindow %s: Message AS_DISABLE_UPDATES\n", Title())); fWindow->DisableUpdateRequests(); break; case AS_NEEDS_UPDATE: DTRACE(("ServerWindow %s: Message AS_NEEDS_UPDATE: %d\n", Title(), fWindow->NeedsUpdate())); if (fWindow->NeedsUpdate()) fLink.StartMessage(B_OK); else fLink.StartMessage(B_ERROR); fLink.Flush(); break; case AS_SET_WINDOW_TITLE: { char* newTitle; if (link.ReadString(&newTitle) == B_OK) { DTRACE(("ServerWindow %s: Message AS_SET_WINDOW_TITLE: %s\n", Title(), newTitle)); SetTitle(newTitle); free(newTitle); } break; } case AS_ADD_TO_SUBSET: { // Has the all-window lock DTRACE(("ServerWindow %s: Message AS_ADD_TO_SUBSET\n", Title())); status_t status = B_ERROR; int32 token; if (link.Read(&token) == B_OK) { ::Window* window = fDesktop->FindWindowByClientToken(token, App()->ClientTeam()); if (window == NULL || window->Feel() != B_NORMAL_WINDOW_FEEL) { status = B_BAD_VALUE; } else { status = fDesktop->AddWindowToSubset(fWindow.Get(), window) ? B_OK : B_NO_MEMORY; } } fLink.StartMessage(status); fLink.Flush(); break; } case AS_REMOVE_FROM_SUBSET: { // Has the all-window lock DTRACE(("ServerWindow %s: Message AS_REM_FROM_SUBSET\n", Title())); status_t status = B_ERROR; int32 token; if (link.Read(&token) == B_OK) { ::Window* window = fDesktop->FindWindowByClientToken(token, App()->ClientTeam()); if (window != NULL) { fDesktop->RemoveWindowFromSubset(fWindow.Get(), window); status = B_OK; } else status = B_BAD_VALUE; } fLink.StartMessage(status); fLink.Flush(); break; } case AS_SET_LOOK: { // Has the all-window look DTRACE(("ServerWindow %s: Message AS_SET_LOOK\n", Title())); status_t status = B_ERROR; int32 look; if (link.Read(&look) == B_OK) { // test if look is valid status = Window::IsValidLook((window_look)look) ? B_OK : B_BAD_VALUE; } if (status == B_OK && !fWindow->IsOffscreenWindow()) fDesktop->SetWindowLook(fWindow.Get(), (window_look)look); fLink.StartMessage(status); fLink.Flush(); break; } case AS_SET_FEEL: { // Has the all-window look DTRACE(("ServerWindow %s: Message AS_SET_FEEL\n", Title())); status_t status = B_ERROR; int32 feel; if (link.Read(&feel) == B_OK) { // test if feel is valid status = Window::IsValidFeel((window_feel)feel) ? B_OK : B_BAD_VALUE; } if (status == B_OK && !fWindow->IsOffscreenWindow()) fDesktop->SetWindowFeel(fWindow.Get(), (window_feel)feel); fLink.StartMessage(status); fLink.Flush(); break; } case AS_SET_FLAGS: { // Has the all-window look DTRACE(("ServerWindow %s: Message AS_SET_FLAGS\n", Title())); status_t status = B_ERROR; uint32 flags; if (link.Read(&flags) == B_OK) { // test if flags are valid status = (flags & ~Window::ValidWindowFlags()) == 0 ? B_OK : B_BAD_VALUE; } if (status == B_OK && !fWindow->IsOffscreenWindow()) fDesktop->SetWindowFlags(fWindow.Get(), flags); fLink.StartMessage(status); fLink.Flush(); break; } #if 0 case AS_SET_ALIGNMENT: { // TODO: Implement AS_SET_ALIGNMENT DTRACE(("ServerWindow %s: Message Set_Alignment unimplemented\n", Title())); break; } case AS_GET_ALIGNMENT: { // TODO: Implement AS_GET_ALIGNMENT DTRACE(("ServerWindow %s: Message Get_Alignment unimplemented\n", Title())); break; } #endif case AS_IS_FRONT_WINDOW: { bool isFront = fDesktop->FrontWindow() == fWindow.Get(); DTRACE(("ServerWindow %s: Message AS_IS_FRONT_WINDOW: %d\n", Title(), isFront)); fLink.StartMessage(isFront ? B_OK : B_ERROR); fLink.Flush(); break; } case AS_GET_WORKSPACES: { DTRACE(("ServerWindow %s: Message AS_GET_WORKSPACES\n", Title())); fLink.StartMessage(B_OK); fLink.Attach(fWindow->Workspaces()); fLink.Flush(); break; } case AS_SET_WORKSPACES: { // Has the all-window lock (but would actually not need to lock at // all) uint32 newWorkspaces; if (link.Read(&newWorkspaces) != B_OK) break; DTRACE(("ServerWindow %s: Message AS_SET_WORKSPACES %" B_PRIx32 "\n", Title(), newWorkspaces)); fDesktop->SetWindowWorkspaces(fWindow.Get(), newWorkspaces); break; } case AS_WINDOW_RESIZE: { // Has the all-window look float xResizeTo; float yResizeTo; link.Read(&xResizeTo); if (link.Read(&yResizeTo) != B_OK) break; DTRACE(("ServerWindow %s: Message AS_WINDOW_RESIZE %.1f, %.1f\n", Title(), xResizeTo, yResizeTo)); // comment this code for the time being, as some apps rely // on the programmatically resize behavior during user resize // if (fWindow->IsResizing()) { // While the user resizes the window, we ignore // pragmatically set window bounds // fLink.StartMessage(B_BUSY); // } else { fDesktop->ResizeWindowBy(fWindow.Get(), xResizeTo - fWindow->Frame().Width(), yResizeTo - fWindow->Frame().Height()); fLink.StartMessage(B_OK); // } fLink.Flush(); break; } case AS_WINDOW_MOVE: { // Has the all-window look float xMoveTo; float yMoveTo; link.Read(&xMoveTo); if (link.Read(&yMoveTo) != B_OK) break; DTRACE(("ServerWindow %s: Message AS_WINDOW_MOVE: %.1f, %.1f\n", Title(), xMoveTo, yMoveTo)); if (fWindow->IsDragging()) { // While the user moves the window, we ignore // pragmatically set window positions fLink.StartMessage(B_BUSY); } else { fDesktop->MoveWindowBy(fWindow.Get(), xMoveTo - fWindow->Frame().left, yMoveTo - fWindow->Frame().top); fLink.StartMessage(B_OK); } fLink.Flush(); break; } case AS_SET_SIZE_LIMITS: { // Has the all-window look // Attached Data: // 1) float minimum width // 2) float maximum width // 3) float minimum height // 4) float maximum height // TODO: for now, move the client to int32 as well! int32 minWidth, maxWidth, minHeight, maxHeight; float value; link.Read(&value); minWidth = (int32)value; link.Read(&value); maxWidth = (int32)value; link.Read(&value); minHeight = (int32)value; link.Read(&value); maxHeight = (int32)value; /* link.Read(&minWidth); link.Read(&maxWidth); link.Read(&minHeight); link.Read(&maxHeight); */ DTRACE(("ServerWindow %s: Message AS_SET_SIZE_LIMITS: " "x: %" B_PRId32 "-%" B_PRId32 ", y: %" B_PRId32 "-%" B_PRId32 "\n", Title(), minWidth, maxWidth, minHeight, maxHeight)); fWindow->SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight); // and now, sync the client to the limits that we were able to enforce fWindow->GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight); fLink.StartMessage(B_OK); fLink.Attach(fWindow->Frame()); fLink.Attach((float)minWidth); fLink.Attach((float)maxWidth); fLink.Attach((float)minHeight); fLink.Attach((float)maxHeight); fLink.Flush(); fDesktop->NotifySizeLimitsChanged(fWindow.Get(), minWidth, maxWidth, minHeight, maxHeight); break; } case AS_SET_DECORATOR_SETTINGS: { // Has the all-window look DTRACE(("ServerWindow %s: Message AS_SET_DECORATOR_SETTINGS\n", Title())); int32 size; if (fWindow.IsSet() && link.Read(&size) == B_OK) { char buffer[size]; if (link.Read(buffer, size) == B_OK) { BMessage settings; if (settings.Unflatten(buffer) == B_OK) fDesktop->SetWindowDecoratorSettings( fWindow.Get(), settings); } } break; } case AS_GET_DECORATOR_SETTINGS: { DTRACE(("ServerWindow %s: Message AS_GET_DECORATOR_SETTINGS\n", Title())); bool success = false; BMessage settings; if (fWindow->GetDecoratorSettings(&settings)) { int32 size = settings.FlattenedSize(); char buffer[size]; if (settings.Flatten(buffer, size) == B_OK) { success = true; fLink.StartMessage(B_OK); fLink.Attach(size); fLink.Attach(buffer, size); } } if (!success) fLink.StartMessage(B_ERROR); fLink.Flush(); break; } case AS_SYSTEM_FONT_CHANGED: { // Has the all-window look fDesktop->FontsChanged(fWindow.Get()); break; } // Forward to client case B_FONTS_UPDATED: { // TODO: would knowing which font was changed be useful? BMessage message(code); SendMessageToClient(&message); break; } case AS_REDRAW: // Nothing to do here - the redraws are actually handled by looking // at the fRedrawRequested member variable in _MessageLooper(). break; case AS_SYNC: DTRACE(("ServerWindow %s: Message AS_SYNC\n", Title())); // the synchronisation works by the fact that the client // window is waiting for this reply, after having received it, // client and server queues are in sync (earlier, the client // may have pushed drawing commands at the server and now it // knows they have all been carried out) fLink.StartMessage(B_OK); fLink.Flush(); break; case AS_BEGIN_UPDATE: DTRACE(("ServerWindow %s: Message AS_BEGIN_UPDATE\n", Title())); fWindow->BeginUpdate(fLink); break; case AS_END_UPDATE: DTRACE(("ServerWindow %s: Message AS_END_UPDATE\n", Title())); fWindow->EndUpdate(); break; case AS_GET_MOUSE: { // Has the all-window look DTRACE(("ServerWindow %s: Message AS_GET_MOUSE\n", fTitle)); // Returns // 1) BPoint mouse location // 2) int32 button state BPoint where; int32 buttons; fDesktop->GetLastMouseState(&where, &buttons); fLink.StartMessage(B_OK); fLink.Attach(where); fLink.Attach(buttons); fLink.Flush(); break; } // BDirectWindow communication case AS_DIRECT_WINDOW_GET_SYNC_DATA: { status_t status = _EnableDirectWindowMode(); fLink.StartMessage(status); if (status == B_OK) { struct direct_window_sync_data syncData; fDirectWindowInfo->GetSyncData(syncData); fLink.Attach(&syncData, sizeof(syncData)); } fLink.Flush(); break; } case AS_DIRECT_WINDOW_SET_FULLSCREEN: { // Has the all-window look bool enable; link.Read(&enable); status_t status = B_OK; if (fDirectWindowInfo.IsSet()) _DirectWindowSetFullScreen(enable); else status = B_BAD_TYPE; fLink.StartMessage(status); fLink.Flush(); break; } // View creation and destruction (don't need a valid fCurrentView) case AS_SET_CURRENT_VIEW: { int32 token; if (link.Read(&token) != B_OK) break; View *current; if (App()->ViewTokens().GetToken(token, B_HANDLER_TOKEN, (void**)¤t) != B_OK || current->Window()->ServerWindow() != this) { // TODO: if this happens, we probably want to kill the app and // clean up debug_printf("ServerWindow %s: Message " "\n\n\nAS_SET_CURRENT_VIEW: view not found, token %" B_PRId32 "\n", fTitle, token); current = NULL; } else { DTRACE(("\n\n\nServerWindow %s: Message AS_SET_CURRENT_VIEW: %s, " "token %" B_PRId32 "\n", fTitle, current->Name(), token)); _SetCurrentView(current); } break; } case AS_VIEW_CREATE_ROOT: { DTRACE(("ServerWindow %s: Message AS_VIEW_CREATE_ROOT\n", fTitle)); // Start receiving top_view data -- pass NULL as the parent view. // This should be the *only* place where this happens. if (fCurrentView != NULL) { debug_printf("ServerWindow %s: Message " "AS_VIEW_CREATE_ROOT: fCurrentView already set!!\n", fTitle); break; } _SetCurrentView(_CreateView(link, NULL)); fWindow->SetTopView(fCurrentView); break; } case AS_VIEW_CREATE: { DTRACE(("ServerWindow %s: Message AS_VIEW_CREATE: View name: " "%s\n", fTitle, fCurrentView->Name())); View* parent = NULL; View* newView = _CreateView(link, &parent); if (parent != NULL && newView != NULL) parent->AddChild(newView); else { delete newView; debug_printf("ServerWindow %s: Message AS_VIEW_CREATE: " "parent or newView NULL!!\n", fTitle); } break; } case AS_TALK_TO_DESKTOP_LISTENER: { if (fDesktop->MessageForListener(fWindow.Get(), fLink.Receiver(), fLink.Sender())) break; // unhandled message at least send an error if needed if (link.NeedsReply()) { fLink.StartMessage(B_ERROR); fLink.Flush(); } break; } default: if (fCurrentView == NULL) { debug_printf("ServerWindow %s received unexpected code - " "message '%s' before top_view attached.\n", Title(), string_for_message_code(code)); if (link.NeedsReply()) { fLink.StartMessage(B_ERROR); fLink.Flush(); } return; } _DispatchViewMessage(code, link); break; } } /*! Dispatches all view messages that need a valid fCurrentView. */ void ServerWindow::_DispatchViewMessage(int32 code, BPrivate::LinkReceiver &link) { if (_DispatchPictureMessage(code, link)) return; switch (code) { case AS_VIEW_SCROLL: { float dh; float dv; link.Read(&dh); if (link.Read(&dv) != B_OK) break; DTRACE(("ServerWindow %s: Message AS_VIEW_SCROLL: View name: " "%s, %.1f x %.1f\n", fTitle, fCurrentView->Name(), dh, dv)); fWindow->ScrollViewBy(fCurrentView, dh, dv); break; } case AS_VIEW_COPY_BITS: { BRect src; BRect dst; link.Read(&src); if (link.Read(&dst) != B_OK) break; DTRACE(("ServerWindow %s: Message AS_VIEW_COPY_BITS: View name: " "%s, BRect(%.1f, %.1f, %.1f, %.1f) -> " "BRect(%.1f, %.1f, %.1f, %.1f)\n", fTitle, fCurrentView->Name(), src.left, src.top, src.right, src.bottom, dst.left, dst.top, dst.right, dst.bottom)); BRegion contentRegion; // TODO: avoid copy operation maybe? fWindow->GetContentRegion(&contentRegion); fCurrentView->CopyBits(src, dst, contentRegion); break; } case AS_VIEW_DELETE: { // Received when a view is detached from a window int32 token; if (link.Read(&token) != B_OK) break; View *view; if (App()->ViewTokens().GetToken(token, B_HANDLER_TOKEN, (void**)&view) == B_OK && view->Window()->ServerWindow() == this) { View* parent = view->Parent(); DTRACE(("ServerWindow %s: AS_VIEW_DELETE view: %p, " "parent: %p\n", fTitle, view, parent)); if (parent != NULL) { parent->RemoveChild(view); if (view->EventMask() != 0) { // TODO: possible deadlock (event dispatcher already // locked itself, waits for Desktop write lock, but // we have it, now we are trying to lock the event // dispatcher -> deadlock) fDesktop->UnlockSingleWindow(); fDesktop->EventDispatcher().RemoveListener( EventTarget(), token); fDesktop->LockSingleWindow(); } if (fCurrentView == view || fCurrentView->HasParent(view)) _SetCurrentView(parent); delete view; } // else we don't delete the root view } break; } case AS_VIEW_SET_STATE: { DTRACE(("ServerWindow %s: Message AS_VIEW_SET_STATE: " "View name: %s\n", fTitle, fCurrentView->Name())); fCurrentView->CurrentState()->ReadFromLink(link); // TODO: When is this used?!? fCurrentView->RebuildClipping(true); _UpdateDrawState(fCurrentView); break; } case AS_VIEW_SET_FONT_STATE: { DTRACE(("ServerWindow %s: Message AS_VIEW_SET_FONT_STATE: " "View name: %s\n", fTitle, fCurrentView->Name())); fCurrentView->CurrentState()->ReadFontFromLink(link, fServerApp->FontManager()); fWindow->GetDrawingEngine()->SetFont( fCurrentView->CurrentState()); break; } case AS_VIEW_GET_STATE: { DTRACE(("ServerWindow %s: Message AS_VIEW_GET_STATE: " "View name: %s\n", fTitle, fCurrentView->Name())); fLink.StartMessage(B_OK); // attach state data fCurrentView->CurrentState()->WriteToLink(fLink.Sender()); fLink.Flush(); break; } case AS_VIEW_SET_EVENT_MASK: { DTRACE(("ServerWindow %s: Message AS_VIEW_SET_EVENT_MASK: " "View name: %s\n", fTitle, fCurrentView->Name())); uint32 eventMask, options; link.Read(&eventMask); if (link.Read(&options) == B_OK) { fCurrentView->SetEventMask(eventMask, options); fDesktop->UnlockSingleWindow(); // TODO: possible deadlock! if (eventMask != 0 || options != 0) { fDesktop->EventDispatcher().AddListener(EventTarget(), fCurrentView->Token(), eventMask, options); } else { fDesktop->EventDispatcher().RemoveListener(EventTarget(), fCurrentView->Token()); } fDesktop->LockSingleWindow(); } break; } case AS_VIEW_SET_MOUSE_EVENT_MASK: { DTRACE(("ServerWindow %s: Message AS_VIEW_SET_MOUSE_EVENT_MASK: " "View name: %s\n", fTitle, fCurrentView->Name())); uint32 eventMask, options; link.Read(&eventMask); if (link.Read(&options) == B_OK) { fDesktop->UnlockSingleWindow(); // TODO: possible deadlock if (eventMask != 0 || options != 0) { if (options & B_LOCK_WINDOW_FOCUS) fDesktop->SetFocusLocked(fWindow.Get()); fDesktop->EventDispatcher().AddTemporaryListener(EventTarget(), fCurrentView->Token(), eventMask, options); } else { fDesktop->EventDispatcher().RemoveTemporaryListener(EventTarget(), fCurrentView->Token()); } fDesktop->LockSingleWindow(); } // TODO: support B_LOCK_WINDOW_FOCUS option in Desktop break; } case AS_VIEW_MOVE_TO: { float x, y; link.Read(&x); if (link.Read(&y) != B_OK) break; DTRACE(("ServerWindow %s: Message AS_VIEW_MOVE_TO: View name: " "%s, x: %.1f, y: %.1f\n", fTitle, fCurrentView->Name(), x, y)); float offsetX = x - fCurrentView->Frame().left; float offsetY = y - fCurrentView->Frame().top; BRegion dirty, expose; fCurrentView->MoveBy(offsetX, offsetY, &dirty); // TODO: think about how to avoid this hack: // the parent clipping needs to be updated, it is not // done in MoveBy() since it would cause // too much computations when children are resized because // follow modes if (View* parent = fCurrentView->Parent()) parent->RebuildClipping(false); fWindow->MarkContentDirty(dirty, expose); break; } case AS_VIEW_RESIZE_TO: { float newWidth, newHeight; link.Read(&newWidth); if (link.Read(&newHeight) != B_OK) break; DTRACE(("ServerWindow %s: Message AS_VIEW_RESIZE_TO: View name: " "%s, width: %.1f, height: %.1f\n", fTitle, fCurrentView->Name(), newWidth, newHeight)); float deltaWidth = newWidth - fCurrentView->Frame().Width(); float deltaHeight = newHeight - fCurrentView->Frame().Height(); BRegion dirty, expose; fCurrentView->ResizeBy(deltaWidth, deltaHeight, &dirty); // TODO: see above if (View* parent = fCurrentView->Parent()) parent->RebuildClipping(false); fWindow->MarkContentDirty(dirty, expose); break; } case AS_VIEW_GET_COORD: { // our offset in the parent -> will be originX and originY // in BView BPoint parentOffset = fCurrentView->Frame().LeftTop(); DTRACE(("ServerWindow %s: Message AS_VIEW_GET_COORD: " "View: %s -> x: %.1f, y: %.1f\n", Title(), fCurrentView->Name(), parentOffset.x, parentOffset.y)); fLink.StartMessage(B_OK); fLink.Attach(parentOffset); fLink.Attach(fCurrentView->Bounds()); fLink.Flush(); break; } case AS_VIEW_SET_ORIGIN: { float x, y; link.Read(&x); if (link.Read(&y) != B_OK) break; DTRACE(("ServerWindow %s: Message AS_VIEW_SET_ORIGIN: " "View: %s -> x: %.1f, y: %.1f\n", Title(), fCurrentView->Name(), x, y)); fCurrentView->SetDrawingOrigin(BPoint(x, y)); _UpdateDrawState(fCurrentView); break; } case AS_VIEW_GET_ORIGIN: { BPoint drawingOrigin = fCurrentView->DrawingOrigin(); DTRACE(("ServerWindow %s: Message AS_VIEW_GET_ORIGIN: " "View: %s -> x: %.1f, y: %.1f\n", Title(), fCurrentView->Name(), drawingOrigin.x, drawingOrigin.y)); fLink.StartMessage(B_OK); fLink.Attach(drawingOrigin); fLink.Flush(); break; } case AS_VIEW_RESIZE_MODE: { uint32 resizeMode; if (link.Read(&resizeMode) != B_OK) break; DTRACE(("ServerWindow %s: Message AS_VIEW_RESIZE_MODE: " "View: %s -> %" B_PRId32 "\n", Title(), fCurrentView->Name(), resizeMode)); fCurrentView->SetResizeMode(resizeMode); break; } case AS_VIEW_SET_FLAGS: { uint32 flags; link.Read(&flags); // The views clipping changes when the B_DRAW_ON_CHILDREN flag is // toggled. bool updateClipping = (flags & B_DRAW_ON_CHILDREN) ^ (fCurrentView->Flags() & B_DRAW_ON_CHILDREN); fCurrentView->SetFlags(flags); _UpdateDrawState(fCurrentView); if (updateClipping) { fCurrentView->RebuildClipping(false); fCurrentDrawingRegionValid = false; } DTRACE(("ServerWindow %s: Message AS_VIEW_SET_FLAGS: " "View: %s -> flags: %" B_PRIu32 "\n", Title(), fCurrentView->Name(), flags)); break; } case AS_VIEW_HIDE: DTRACE(("ServerWindow %s: Message AS_VIEW_HIDE: View: %s\n", Title(), fCurrentView->Name())); fCurrentView->SetHidden(true); break; case AS_VIEW_SHOW: DTRACE(("ServerWindow %s: Message AS_VIEW_SHOW: View: %s\n", Title(), fCurrentView->Name())); fCurrentView->SetHidden(false); break; case AS_VIEW_SET_LINE_MODE: { DTRACE(("ServerWindow %s: Message AS_VIEW_SET_LINE_MODE: " "View: %s\n", Title(), fCurrentView->Name())); ViewSetLineModeInfo info; if (link.Read(&info) != B_OK) break; fCurrentView->CurrentState()->SetLineCapMode(info.lineCap); fCurrentView->CurrentState()->SetLineJoinMode(info.lineJoin); fCurrentView->CurrentState()->SetMiterLimit(info.miterLimit); fWindow->GetDrawingEngine()->SetStrokeMode(info.lineCap, info.lineJoin, info.miterLimit); break; } case AS_VIEW_GET_LINE_MODE: { DTRACE(("ServerWindow %s: Message AS_VIEW_GET_LINE_MODE: " "View: %s\n", Title(), fCurrentView->Name())); ViewSetLineModeInfo info; info.lineJoin = fCurrentView->CurrentState()->LineJoinMode(); info.lineCap = fCurrentView->CurrentState()->LineCapMode(); info.miterLimit = fCurrentView->CurrentState()->MiterLimit(); fLink.StartMessage(B_OK); fLink.Attach(info); fLink.Flush(); break; } case AS_VIEW_SET_FILL_RULE: { DTRACE(("ServerWindow %s: Message AS_VIEW_SET_FILL_RULE: " "View: %s\n", Title(), fCurrentView->Name())); int32 fillRule; if (link.Read(&fillRule) != B_OK) break; fCurrentView->CurrentState()->SetFillRule(fillRule); fWindow->GetDrawingEngine()->SetFillRule(fillRule); break; } case AS_VIEW_GET_FILL_RULE: { DTRACE(("ServerWindow %s: Message AS_VIEW_GET_FILL_RULE: " "View: %s\n", Title(), fCurrentView->Name())); int32 fillRule = fCurrentView->CurrentState()->FillRule(); fLink.StartMessage(B_OK); fLink.Attach(fillRule); fLink.Flush(); break; } case AS_VIEW_PUSH_STATE: { DTRACE(("ServerWindow %s: Message AS_VIEW_PUSH_STATE: View: " "%s\n", Title(), fCurrentView->Name())); fCurrentView->PushState(); // TODO: is this necessary? // _UpdateDrawState(fCurrentView); break; } case AS_VIEW_POP_STATE: { DTRACE(("ServerWindow %s: Message AS_VIEW_POP_STATE: View: %s\n", Title(), fCurrentView->Name())); fCurrentView->PopState(); _UpdateDrawState(fCurrentView); break; } case AS_VIEW_SET_SCALE: { float scale; if (link.Read(&scale) != B_OK) break; DTRACE(("ServerWindow %s: Message AS_VIEW_SET_SCALE: " "View: %s -> scale: %.2f\n", Title(), fCurrentView->Name(), scale)); fCurrentView->SetScale(scale); _UpdateDrawState(fCurrentView); break; } case AS_VIEW_GET_SCALE: { float scale = fCurrentView->CurrentState()->Scale(); DTRACE(("ServerWindow %s: Message AS_VIEW_GET_SCALE: " "View: %s -> scale: %.2f\n", Title(), fCurrentView->Name(), scale)); fLink.StartMessage(B_OK); fLink.Attach(scale); fLink.Flush(); break; } case AS_VIEW_SET_TRANSFORM: { BAffineTransform transform; if (link.Read(&transform) != B_OK) break; DTRACE(("ServerWindow %s: Message AS_VIEW_SET_TRANSFORM: " "View: %s -> transform: %.2f, %.2f, %.2f, %.2f, %.2f, %.2f\n", Title(), fCurrentView->Name(), transform.sx, transform.shy, transform.shx, transform.sy, transform.tx, transform.ty)); fCurrentView->CurrentState()->SetTransform(transform); _UpdateDrawState(fCurrentView); break; } case AS_VIEW_GET_TRANSFORM: { BAffineTransform transform = fCurrentView->CurrentState()->Transform(); DTRACE(("ServerWindow %s: Message AS_VIEW_GET_TRANSFORM: " "View: %s -> transform: %.2f, %.2f, %.2f, %.2f, %.2f, %.2f\n", Title(), fCurrentView->Name(), transform.sx, transform.shy, transform.shx, transform.sy, transform.tx, transform.ty)); fLink.StartMessage(B_OK); fLink.Attach(transform); fLink.Flush(); break; } case AS_VIEW_GET_PARENT_COMPOSITE: { DrawState* state = fCurrentView->CurrentState()->PreviousState(); fLink.StartMessage(B_OK); if (state != NULL) { fLink.Attach(state->CombinedTransform()); fLink.Attach(state->CombinedScale()); fLink.Attach(state->CombinedOrigin()); } else { fLink.Attach(BAffineTransform()); fLink.Attach(1.0f); fLink.Attach(B_ORIGIN); } fLink.Flush(); break; } case AS_VIEW_AFFINE_TRANSLATE: { double x, y; link.Read(&x); link.Read(&y); BAffineTransform current = fCurrentView->CurrentState()->Transform(); current.PreTranslateBy(x, y); fCurrentView->CurrentState()->SetTransform(current); _UpdateDrawState(fCurrentView); break; } case AS_VIEW_AFFINE_SCALE: { double x, y; link.Read(&x); link.Read(&y); BAffineTransform current = fCurrentView->CurrentState()->Transform(); current.PreScaleBy(x, y); fCurrentView->CurrentState()->SetTransform(current); _UpdateDrawState(fCurrentView); break; } case AS_VIEW_AFFINE_ROTATE: { double angleRadians; link.Read(&angleRadians); BAffineTransform current = fCurrentView->CurrentState()->Transform(); current.PreRotateBy(angleRadians); fCurrentView->CurrentState()->SetTransform(current); _UpdateDrawState(fCurrentView); break; } case AS_VIEW_SET_PEN_LOC: { BPoint location; if (link.Read(&location) != B_OK) break; DTRACE(("ServerWindow %s: Message AS_VIEW_SET_PEN_LOC: " "View: %s -> BPoint(%.1f, %.1f)\n", Title(), fCurrentView->Name(), location.x, location.y)); fCurrentView->CurrentState()->SetPenLocation(location); break; } case AS_VIEW_GET_PEN_LOC: { BPoint location = fCurrentView->CurrentState()->PenLocation(); DTRACE(("ServerWindow %s: Message AS_VIEW_GET_PEN_LOC: " "View: %s -> BPoint(%.1f, %.1f)\n", Title(), fCurrentView->Name(), location.x, location.y)); fLink.StartMessage(B_OK); fLink.Attach(location); fLink.Flush(); break; } case AS_VIEW_SET_PEN_SIZE: { float penSize; if (link.Read(&penSize) != B_OK) break; DTRACE(("ServerWindow %s: Message AS_VIEW_SET_PEN_SIZE: " "View: %s -> %.1f\n", Title(), fCurrentView->Name(), penSize)); fCurrentView->CurrentState()->SetPenSize(penSize); fWindow->GetDrawingEngine()->SetPenSize( fCurrentView->CurrentState()->PenSize()); break; } case AS_VIEW_GET_PEN_SIZE: { float penSize = fCurrentView->CurrentState()->UnscaledPenSize(); DTRACE(("ServerWindow %s: Message AS_VIEW_GET_PEN_SIZE: " "View: %s -> %.1f\n", Title(), fCurrentView->Name(), penSize)); fLink.StartMessage(B_OK); fLink.Attach(penSize); fLink.Flush(); break; } case AS_VIEW_SET_VIEW_COLOR: { rgb_color color; if (link.Read(&color, sizeof(rgb_color)) != B_OK) break; DTRACE(("ServerWindow %s: Message AS_VIEW_SET_VIEW_COLOR: " "View: %s -> rgb_color(%d, %d, %d, %d)\n", Title(), fCurrentView->Name(), color.red, color.green, color.blue, color.alpha)); fCurrentView->SetViewColor(color); break; } case AS_VIEW_GET_VIEW_COLOR: { rgb_color color = fCurrentView->ViewColor(); DTRACE(("ServerWindow %s: Message AS_VIEW_GET_VIEW_COLOR: " "View: %s -> rgb_color(%d, %d, %d, %d)\n", Title(), fCurrentView->Name(), color.red, color.green, color.blue, color.alpha)); fLink.StartMessage(B_OK); fLink.Attach(color); fLink.Flush(); break; } case AS_VIEW_SET_HIGH_COLOR: { rgb_color color; if (link.Read(&color, sizeof(rgb_color)) != B_OK) break; DTRACE(("ServerWindow %s: Message AS_VIEW_SET_HIGH_COLOR: " "View: %s -> rgb_color(%d, %d, %d, %d)\n", Title(), fCurrentView->Name(), color.red, color.green, color.blue, color.alpha)); fCurrentView->CurrentState()->SetHighColor(color); fWindow->GetDrawingEngine()->SetHighColor(color); break; } case AS_VIEW_SET_HIGH_UI_COLOR: { color_which which = B_NO_COLOR; float tint = B_NO_TINT; if (link.Read(&which) != B_OK || link.Read(&tint) != B_OK ) break; fCurrentView->CurrentState()->SetHighUIColor(which, tint); // TODO: should we do more color_which validity checking? if (which != B_NO_COLOR) { DesktopSettings settings(fDesktop); rgb_color color = tint_color(settings.UIColor(which), tint); fCurrentView->CurrentState()->SetHighColor(color); fWindow->GetDrawingEngine()->SetHighColor(color); } break; } case AS_VIEW_SET_LOW_UI_COLOR: { color_which which = B_NO_COLOR; float tint = B_NO_TINT; if (link.Read(&which) != B_OK || link.Read(&tint) != B_OK ) break; fCurrentView->CurrentState()->SetLowUIColor(which, tint); // TODO: should we do more color_which validity checking? if (which != B_NO_COLOR) { DesktopSettings settings(fDesktop); rgb_color color = tint_color(settings.UIColor(which), tint); fCurrentView->CurrentState()->SetLowColor(color); fWindow->GetDrawingEngine()->SetLowColor(color); } break; } case AS_VIEW_SET_VIEW_UI_COLOR: { color_which which = B_NO_COLOR; float tint = B_NO_TINT; if (link.Read(&which) != B_OK || link.Read(&tint) != B_OK ) break; // TODO: should we do more color_which validity checking? fCurrentView->SetViewUIColor(which, tint); break; } case AS_VIEW_GET_HIGH_UI_COLOR: { float tint; color_which which = fCurrentView->CurrentState()->HighUIColor(&tint); rgb_color color = fCurrentView->CurrentState()->HighColor(); DTRACE(("ServerWindow %s: Message AS_VIEW_GET_HIGH_UI_COLOR: " "View: %s -> color_which(%i) tint(%.3f) - rgb_color(%i, %i," " %i, %i)\n", Title(), fCurrentView->Name(), which, tint, color.red, color.green, color.blue, color.alpha)); fLink.StartMessage(B_OK); fLink.Attach(which); fLink.Attach(tint); fLink.Attach(color); fLink.Flush(); break; } case AS_VIEW_GET_LOW_UI_COLOR: { float tint; color_which which = fCurrentView->CurrentState()->LowUIColor(&tint); rgb_color color = fCurrentView->CurrentState()->LowColor(); DTRACE(("ServerWindow %s: Message AS_VIEW_GET_LOW_UI_COLOR: " "View: %s -> color_which(%i) tint(%.3f) - rgb_color(%i, %i," " %i, %i)\n", Title(), fCurrentView->Name(), which, tint, color.red, color.green, color.blue, color.alpha)); fLink.StartMessage(B_OK); fLink.Attach(which); fLink.Attach(tint); fLink.Attach(color); fLink.Flush(); break; } case AS_VIEW_GET_VIEW_UI_COLOR: { float tint; color_which which = fCurrentView->ViewUIColor(&tint); rgb_color color = fCurrentView->ViewColor(); DTRACE(("ServerWindow %s: Message AS_VIEW_GET_VIEW_UI_COLOR: " "View: %s -> color_which(%i) tint(%.3f) - rgb_color(%i, %i," " %i, %i)\n", Title(), fCurrentView->Name(), which, tint, color.red, color.green, color.blue, color.alpha)); fLink.StartMessage(B_OK); fLink.Attach(which); fLink.Attach(tint); fLink.Attach(color); fLink.Flush(); break; } case AS_VIEW_GET_HIGH_COLOR: { rgb_color color = fCurrentView->CurrentState()->HighColor(); DTRACE(("ServerWindow %s: Message AS_VIEW_GET_HIGH_COLOR: " "View: %s -> rgb_color(%d, %d, %d, %d)\n", Title(), fCurrentView->Name(), color.red, color.green, color.blue, color.alpha)); fLink.StartMessage(B_OK); fLink.Attach(color); fLink.Flush(); break; } case AS_VIEW_SET_LOW_COLOR: { rgb_color color; if (link.Read(&color, sizeof(rgb_color)) != B_OK) break; DTRACE(("ServerWindow %s: Message AS_VIEW_SET_LOW_COLOR: " "View: %s -> rgb_color(%d, %d, %d, %d)\n", Title(), fCurrentView->Name(), color.red, color.green, color.blue, color.alpha)); fCurrentView->CurrentState()->SetLowColor(color); fWindow->GetDrawingEngine()->SetLowColor(color); break; } case AS_VIEW_GET_LOW_COLOR: { rgb_color color = fCurrentView->CurrentState()->LowColor(); DTRACE(("ServerWindow %s: Message AS_VIEW_GET_LOW_COLOR: " "View: %s -> rgb_color(%d, %d, %d, %d)\n", Title(), fCurrentView->Name(), color.red, color.green, color.blue, color.alpha)); fLink.StartMessage(B_OK); fLink.Attach(color); fLink.Flush(); break; } case AS_VIEW_SET_PATTERN: { DTRACE(("ServerWindow %s: Message AS_VIEW_SET_PATTERN: " "View: %s\n", fTitle, fCurrentView->Name())); pattern pat; if (link.Read(&pat, sizeof(pattern)) != B_OK) break; fCurrentView->CurrentState()->SetPattern(Pattern(pat)); fWindow->GetDrawingEngine()->SetPattern(pat); break; } case AS_VIEW_SET_BLENDING_MODE: { DTRACE(("ServerWindow %s: Message AS_VIEW_SET_BLEND_MODE: " "View: %s\n", Title(), fCurrentView->Name())); ViewBlendingModeInfo info; if (link.Read(&info) != B_OK) break; fCurrentView->CurrentState()->SetBlendingMode( info.sourceAlpha, info.alphaFunction); fWindow->GetDrawingEngine()->SetBlendingMode( info.sourceAlpha, info.alphaFunction); break; } case AS_VIEW_GET_BLENDING_MODE: { DTRACE(("ServerWindow %s: Message AS_VIEW_GET_BLEND_MODE: " "View: %s\n", Title(), fCurrentView->Name())); ViewBlendingModeInfo info; info.sourceAlpha = fCurrentView->CurrentState()->AlphaSrcMode(); info.alphaFunction = fCurrentView->CurrentState()->AlphaFncMode(); fLink.StartMessage(B_OK); fLink.Attach(info); fLink.Flush(); break; } case AS_VIEW_SET_DRAWING_MODE: { int8 drawingMode; if (link.Read(&drawingMode) != B_OK) break; DTRACE(("ServerWindow %s: Message AS_VIEW_SET_DRAW_MODE: " "View: %s -> %s\n", Title(), fCurrentView->Name(), kDrawingModeMap[drawingMode])); fCurrentView->CurrentState()->SetDrawingMode( (drawing_mode)drawingMode); fWindow->GetDrawingEngine()->SetDrawingMode( (drawing_mode)drawingMode); break; } case AS_VIEW_GET_DRAWING_MODE: { int8 drawingMode = (int8)(fCurrentView->CurrentState()->GetDrawingMode()); DTRACE(("ServerWindow %s: Message AS_VIEW_GET_DRAW_MODE: " "View: %s -> %s\n", Title(), fCurrentView->Name(), kDrawingModeMap[drawingMode])); fLink.StartMessage(B_OK); fLink.Attach(drawingMode); fLink.Flush(); break; } case AS_VIEW_SET_VIEW_BITMAP: { DTRACE(("ServerWindow %s: Message AS_VIEW_SET_VIEW_BITMAP: " "View: %s\n", Title(), fCurrentView->Name())); int32 bitmapToken, resizingMode, options; BRect srcRect, dstRect; link.Read(&bitmapToken); link.Read(&srcRect); link.Read(&dstRect); link.Read(&resizingMode); status_t status = link.Read(&options); rgb_color colorKey = {0}; if (status == B_OK) { BReference bitmap(fServerApp->GetBitmap(bitmapToken), true); if (bitmapToken == -1 || bitmap != NULL) { bool wasOverlay = fCurrentView->ViewBitmap() != NULL && fCurrentView->ViewBitmap()->Overlay() != NULL; fCurrentView->SetViewBitmap(bitmap, srcRect, dstRect, resizingMode, options); // TODO: if we revert the view color overlay handling // in View::Draw() to the BeOS version, we never // need to invalidate the view for overlays. // Invalidate view - but only if this is a non-overlay // switch if (bitmap == NULL || bitmap->Overlay() == NULL || !wasOverlay) { BRegion dirty((BRect)fCurrentView->Bounds()); fWindow->InvalidateView(fCurrentView, dirty); } if (bitmap != NULL && bitmap->Overlay() != NULL) { bitmap->Overlay()->SetFlags(options); colorKey = bitmap->Overlay()->Color(); } } else status = B_BAD_VALUE; } fLink.StartMessage(status); if (status == B_OK && (options & AS_REQUEST_COLOR_KEY) != 0) { // Attach color key for the overlay bitmap fLink.Attach(colorKey); } fLink.Flush(); break; } case AS_VIEW_PRINT_ALIASING: { DTRACE(("ServerWindow %s: Message AS_VIEW_PRINT_ALIASING: " "View: %s\n", Title(), fCurrentView->Name())); bool fontAliasing; if (link.Read(&fontAliasing) == B_OK) { fCurrentView->CurrentState()->SetForceFontAliasing(fontAliasing); _UpdateDrawState(fCurrentView); } break; } case AS_VIEW_CLIP_TO_PICTURE: { DTRACE(("ServerWindow %s: Message AS_VIEW_CLIP_TO_PICTURE: " "View: %s\n", Title(), fCurrentView->Name())); int32 pictureToken; BPoint where; bool inverse = false; link.Read(&pictureToken); if (pictureToken < 0) { fCurrentView->SetAlphaMask(NULL); _UpdateDrawState(fCurrentView); break; } link.Read(&where); if (link.Read(&inverse) != B_OK) break; BReference picture(fServerApp->GetPicture(pictureToken), true); if (picture == NULL) break; BReference const mask(new(std::nothrow) PictureAlphaMask( fCurrentView->GetAlphaMask(), picture, *fCurrentView->CurrentState(), where, inverse), true); fCurrentView->SetAlphaMask(mask); _UpdateDrawState(fCurrentView); break; } case AS_VIEW_GET_CLIP_REGION: { DTRACE(("ServerWindow %s: Message AS_VIEW_GET_CLIP_REGION: " "View: %s\n", Title(), fCurrentView->Name())); // if this view is hidden, it has no visible region fLink.StartMessage(B_OK); if (!fWindow->IsVisible() || !fCurrentView->IsVisible()) { BRegion empty; fLink.AttachRegion(empty); } else { _UpdateCurrentDrawingRegion(); BRegion region(fCurrentDrawingRegion); fCurrentView->ScreenToLocalTransform().Apply(®ion); fLink.AttachRegion(region); } fLink.Flush(); break; } case AS_VIEW_SET_CLIP_REGION: { int32 rectCount; status_t status = link.Read(&rectCount); // a negative count means no // region for the current draw state, // but an *empty* region is actually valid! // even if it means no drawing is allowed if (status < B_OK) break; if (rectCount >= 0) { // we are supposed to set the clipping region BRegion region; if (rectCount > 0 && link.ReadRegion(®ion) < B_OK) break; DTRACE(("ServerWindow %s: Message AS_VIEW_SET_CLIP_REGION: " "View: %s -> rect count: %" B_PRId32 ", frame = " "BRect(%.1f, %.1f, %.1f, %.1f)\n", Title(), fCurrentView->Name(), rectCount, region.Frame().left, region.Frame().top, region.Frame().right, region.Frame().bottom)); fCurrentView->SetUserClipping(®ion); } else { // we are supposed to unset the clipping region // passing NULL sets this states region to that // of the previous state DTRACE(("ServerWindow %s: Message AS_VIEW_SET_CLIP_REGION: " "View: %s -> unset\n", Title(), fCurrentView->Name())); fCurrentView->SetUserClipping(NULL); } fCurrentDrawingRegionValid = false; break; } case AS_VIEW_CLIP_TO_RECT: { bool inverse; BRect rect; link.Read(&inverse); link.Read(&rect); bool needDrawStateUpdate = fCurrentView->ClipToRect( rect, inverse); fCurrentDrawingRegionValid = false; if (needDrawStateUpdate) _UpdateDrawState(fCurrentView); _UpdateCurrentDrawingRegion(); BRegion region(fCurrentDrawingRegion); fCurrentView->ScreenToLocalTransform().Apply(®ion); break; } case AS_VIEW_CLIP_TO_SHAPE: { bool inverse; link.Read(&inverse); shape_data shape; link.Read(&shape.opCount); link.Read(&shape.ptCount); shape.opSize = shape.opCount * sizeof(uint32); shape.ptSize = shape.ptCount * sizeof(BPoint); shape.opList = new(nothrow) uint32[shape.opCount]; shape.ptList = new(nothrow) BPoint[shape.ptCount]; if (link.Read(shape.opList, shape.opSize) >= B_OK && link.Read(shape.ptList, shape.ptSize) >= B_OK) { fCurrentView->ClipToShape(&shape, inverse); _UpdateDrawState(fCurrentView); } delete[] shape.opList; delete[] shape.ptList; break; } case AS_VIEW_INVALIDATE_RECT: { // NOTE: looks like this call is NOT affected by origin and scale // on R5 so this implementation is "correct" BRect invalidRect; if (link.Read(&invalidRect) == B_OK) { DTRACE(("ServerWindow %s: Message AS_VIEW_INVALIDATE_RECT: " "View: %s -> BRect(%.1f, %.1f, %.1f, %.1f)\n", Title(), fCurrentView->Name(), invalidRect.left, invalidRect.top, invalidRect.right, invalidRect.bottom)); View* view = NULL; if (link.Read(&view) != B_OK) view = fCurrentView; // make sure the view is still available! if (view != fCurrentView && !fWindow->TopView()->HasView(view)) break; BRegion dirty(invalidRect); fWindow->InvalidateView(view, dirty); } break; } case AS_VIEW_DELAYED_INVALIDATE_RECT: { bigtime_t time = 0; BRect invalidRect; if (link.Read(&time) == B_OK && link.Read(&invalidRect) == B_OK) { DTRACE(("ServerWindow %s: Message " "AS_VIEW_DELAYED_INVALIDATE_RECT: " "View: %s -> BRect(%.1f, %.1f, %.1f, %.1f) at time %llu\n", Title(), fCurrentView->Name(), invalidRect.left, invalidRect.top, invalidRect.right, invalidRect.bottom, time)); DelayedMessage delayed(AS_VIEW_INVALIDATE_RECT, time, true); delayed.AddTarget(MessagePort()); delayed.SetMerge(DM_MERGE_DUPLICATES); if (delayed.Attach(invalidRect) == B_OK && delayed.Attach(fCurrentView) == B_OK) delayed.Flush(); } break; } case AS_VIEW_INVALIDATE_REGION: { // NOTE: looks like this call is NOT affected by origin and scale // on R5 so this implementation is "correct" BRegion region; if (link.ReadRegion(®ion) < B_OK) break; DTRACE(("ServerWindow %s: Message AS_VIEW_INVALIDATE_REGION: " "View: %s -> rect count: %" B_PRId32 ", frame: BRect(%.1f, " "%.1f, %.1f, %.1f)\n", Title(), fCurrentView->Name(), region.CountRects(), region.Frame().left, region.Frame().top, region.Frame().right, region.Frame().bottom)); fWindow->InvalidateView(fCurrentView, region); break; } case AS_VIEW_DRAG_IMAGE: { // TODO: flesh out AS_VIEW_DRAG_IMAGE DTRACE(("ServerWindow %s: Message AS_DRAG_IMAGE\n", Title())); int32 bitmapToken; drawing_mode dragMode; BPoint offset; int32 bufferSize; link.Read(&bitmapToken); link.Read((int32*)&dragMode); link.Read(&offset); link.Read(&bufferSize); if (bufferSize > 0) { char* buffer = new (nothrow) char[bufferSize]; BMessage dragMessage; if (link.Read(buffer, bufferSize) == B_OK && dragMessage.Unflatten(buffer) == B_OK) { BReference bitmap( fServerApp->GetBitmap(bitmapToken), true); // TODO: possible deadlock fDesktop->UnlockSingleWindow(); fDesktop->EventDispatcher().SetDragMessage(dragMessage, bitmap, offset); fDesktop->LockSingleWindow(); } delete[] buffer; } // sync the client (it can now delete the bitmap) fLink.StartMessage(B_OK); fLink.Flush(); break; } case AS_VIEW_DRAG_RECT: { // TODO: flesh out AS_VIEW_DRAG_RECT DTRACE(("ServerWindow %s: Message AS_DRAG_RECT\n", Title())); BRect dragRect; BPoint offset; int32 bufferSize; link.Read(&dragRect); link.Read(&offset); link.Read(&bufferSize); if (bufferSize > 0) { char* buffer = new (nothrow) char[bufferSize]; BMessage dragMessage; if (link.Read(buffer, bufferSize) == B_OK && dragMessage.Unflatten(buffer) == B_OK) { // TODO: possible deadlock fDesktop->UnlockSingleWindow(); fDesktop->EventDispatcher().SetDragMessage(dragMessage, NULL /* should be dragRect */, offset); fDesktop->LockSingleWindow(); } delete[] buffer; } break; } case AS_VIEW_BEGIN_RECT_TRACK: { DTRACE(("ServerWindow %s: Message AS_VIEW_BEGIN_RECT_TRACK\n", Title())); BRect dragRect; uint32 style; link.Read(&dragRect); link.Read(&style); // TODO: implement rect tracking (used sometimes for selecting // a group of things, also sometimes used to appear to drag // something, but without real drag message) break; } case AS_VIEW_END_RECT_TRACK: { DTRACE(("ServerWindow %s: Message AS_VIEW_END_RECT_TRACK\n", Title())); // TODO: implement rect tracking break; } case AS_VIEW_BEGIN_PICTURE: { DTRACE(("ServerWindow %s: Message AS_VIEW_BEGIN_PICTURE\n", Title())); BReference picture(App()->CreatePicture(), true); if (picture != NULL) { picture->SyncState(fCurrentView); fCurrentView->SetPicture(picture); } break; } case AS_VIEW_APPEND_TO_PICTURE: { DTRACE(("ServerWindow %s: Message AS_VIEW_APPEND_TO_PICTURE\n", Title())); int32 token; link.Read(&token); BReference picture(App()->GetPicture(token), true); if (picture != NULL) picture->SyncState(fCurrentView); fCurrentView->SetPicture(picture); break; } case AS_VIEW_END_PICTURE: { DTRACE(("ServerWindow %s: Message AS_VIEW_END_PICTURE\n", Title())); ServerPicture* picture = fCurrentView->Picture(); if (picture != NULL) { fCurrentView->SetPicture(NULL); fLink.StartMessage(B_OK); fLink.Attach(picture->Token()); } else fLink.StartMessage(B_ERROR); fLink.Flush(); break; } case AS_VIEW_BEGIN_LAYER: { DTRACE(("ServerWindow %s: Message AS_VIEW_BEGIN_LAYER\n", Title())); uint8 opacity; link.Read(&opacity); Layer* layer = new(std::nothrow) Layer(opacity); if (layer == NULL) break; if (opacity != 255) { fCurrentView->CurrentState()->SetDrawingMode(B_OP_ALPHA); fCurrentView->CurrentState()->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); fCurrentView->CurrentState()->SetDrawingModeLocked(true); } fCurrentView->SetPicture(layer); break; } default: // The drawing code handles allocation failures using exceptions; // so we need to account for that here. try { _DispatchViewDrawingMessage(code, link); } catch (std::bad_alloc&) { // Cancel any message we were in the middle of sending. fLink.CancelMessage(); if (link.NeedsReply()) { // As done in _DispatchViewDrawingMessage, send just a // single status_t as the reply. fLink.StartMessage(B_NO_MEMORY); fLink.Flush(); } } break; } } /*! Dispatches all view drawing messages. The desktop clipping must be read locked when entering this method. Requires a valid fCurrentView. */ void ServerWindow::_DispatchViewDrawingMessage(int32 code, BPrivate::LinkReceiver &link) { if (!fCurrentView->IsVisible() || !fWindow->IsVisible()) { if (link.NeedsReply()) { debug_printf("ServerWindow::DispatchViewDrawingMessage() got " "message %" B_PRId32 " that needs a reply!\n", code); // the client is now blocking and waiting for a reply! fLink.StartMessage(B_ERROR); fLink.Flush(); } return; } DrawingEngine* drawingEngine = fWindow->GetDrawingEngine(); if (!drawingEngine) { // ?!? debug_printf("ServerWindow %s: no drawing engine!!\n", Title()); if (link.NeedsReply()) { // the client is now blocking and waiting for a reply! fLink.StartMessage(B_ERROR); fLink.Flush(); } return; } _UpdateCurrentDrawingRegion(); if (fCurrentDrawingRegion.CountRects() <= 0 && code != AS_VIEW_END_LAYER) { // If the command is AS_VIEW_END_LAYER, then we continue even if // the clipping region is empty. The layer itself might set a valid // clipping while its contents are drawn, and even if it doesn't, // we must still play back its picture so that we don't leak // nested layer instances. DTRACE(("ServerWindow %s: _DispatchViewDrawingMessage(): View: %s, " "INVALID CLIPPING!\n", Title(), fCurrentView->Name())); if (link.NeedsReply()) { // the client is now blocking and waiting for a reply! fLink.StartMessage(B_ERROR); fLink.Flush(); } return; } drawingEngine->LockParallelAccess(); // NOTE: the region is not copied, Painter keeps a pointer, // that's why you need to use the clipping only for as long // as you have it locked drawingEngine->ConstrainClippingRegion(&fCurrentDrawingRegion); switch (code) { case AS_STROKE_LINE: { ViewStrokeLineInfo info; if (link.Read(&info) != B_OK) break; DTRACE(("ServerWindow %s: Message AS_STROKE_LINE: View: %s -> " "BPoint(%.1f, %.1f) - BPoint(%.1f, %.1f)\n", Title(), fCurrentView->Name(), info.startPoint.x, info.startPoint.y, info.endPoint.x, info.endPoint.y)); BPoint penPos = info.endPoint; const SimpleTransform transform = fCurrentView->PenToScreenTransform(); transform.Apply(&info.startPoint); transform.Apply(&info.endPoint); drawingEngine->StrokeLine(info.startPoint, info.endPoint); // We update the pen here because many DrawingEngine calls which // do not update the pen position actually call StrokeLine // TODO: Decide where to put this, for example, it cannot be done // for DrawString(), also there needs to be a decision, if the pen // location is in View coordinates (I think it should be) or in // screen coordinates. fCurrentView->CurrentState()->SetPenLocation(penPos); break; } case AS_VIEW_INVERT_RECT: { BRect rect; if (link.Read(&rect) != B_OK) break; DTRACE(("ServerWindow %s: Message AS_INVERT_RECT: View: %s -> " "BRect(%.1f, %.1f, %.1f, %.1f)\n", Title(), fCurrentView->Name(), rect.left, rect.top, rect.right, rect.bottom)); fCurrentView->PenToScreenTransform().Apply(&rect); drawingEngine->InvertRect(rect); break; } case AS_STROKE_RECT: { BRect rect; if (link.Read(&rect) != B_OK) break; DTRACE(("ServerWindow %s: Message AS_STROKE_RECT: View: %s -> " "BRect(%.1f, %.1f, %.1f, %.1f)\n", Title(), fCurrentView->Name(), rect.left, rect.top, rect.right, rect.bottom)); fCurrentView->PenToScreenTransform().Apply(&rect); drawingEngine->StrokeRect(rect); break; } case AS_FILL_RECT: { BRect rect; if (link.Read(&rect) != B_OK) break; DTRACE(("ServerWindow %s: Message AS_FILL_RECT: View: %s -> " "BRect(%.1f, %.1f, %.1f, %.1f)\n", Title(), fCurrentView->Name(), rect.left, rect.top, rect.right, rect.bottom)); fCurrentView->PenToScreenTransform().Apply(&rect); drawingEngine->FillRect(rect); break; } case AS_FILL_RECT_GRADIENT: { BRect rect; link.Read(&rect); BGradient* gradient; if (link.ReadGradient(&gradient) != B_OK) break; GTRACE(("ServerWindow %s: Message AS_FILL_RECT_GRADIENT: View: %s " "-> BRect(%.1f, %.1f, %.1f, %.1f)\n", Title(), fCurrentView->Name(), rect.left, rect.top, rect.right, rect.bottom)); const SimpleTransform transform = fCurrentView->PenToScreenTransform(); transform.Apply(&rect); transform.Apply(gradient); drawingEngine->FillRect(rect, *gradient); delete gradient; break; } case AS_VIEW_DRAW_BITMAP: { ViewDrawBitmapInfo info; if (link.Read(&info) != B_OK) break; #if 0 if (strcmp(fServerApp->SignatureLeaf(), "x-vnd.videolan-vlc") == 0) info.options |= B_FILTER_BITMAP_BILINEAR; #endif BReference bitmap(fServerApp->GetBitmap(info.bitmapToken), true); if (bitmap != NULL) { DTRACE(("ServerWindow %s: Message AS_VIEW_DRAW_BITMAP: " "View: %s, bitmap: %" B_PRId32 " (size %" B_PRId32 " x " "%" B_PRId32 "), BRect(%.1f, %.1f, %.1f, %.1f) -> " "BRect(%.1f, %.1f, %.1f, %.1f)\n", fTitle, fCurrentView->Name(), info.bitmapToken, bitmap->Width(), bitmap->Height(), info.bitmapRect.left, info.bitmapRect.top, info.bitmapRect.right, info.bitmapRect.bottom, info.viewRect.left, info.viewRect.top, info.viewRect.right, info.viewRect.bottom)); fCurrentView->PenToScreenTransform().Apply(&info.viewRect); // TODO: Unbreak... // if ((info.options & B_WAIT_FOR_RETRACE) != 0) // fDesktop->HWInterface()->WaitForRetrace(20000); drawingEngine->DrawBitmap(bitmap, info.bitmapRect, info.viewRect, info.options); } break; } case AS_STROKE_ARC: case AS_FILL_ARC: { DTRACE(("ServerWindow %s: Message AS_STROKE/FILL_ARC\n", Title())); float angle, span; BRect r; link.Read(&r); link.Read(&angle); if (link.Read(&span) != B_OK) break; fCurrentView->PenToScreenTransform().Apply(&r); drawingEngine->DrawArc(r, angle, span, code == AS_FILL_ARC); break; } case AS_FILL_ARC_GRADIENT: { GTRACE(("ServerWindow %s: Message AS_FILL_ARC_GRADIENT\n", Title())); float angle, span; BRect r; link.Read(&r); link.Read(&angle); link.Read(&span); BGradient* gradient; if (link.ReadGradient(&gradient) != B_OK) break; const SimpleTransform transform = fCurrentView->PenToScreenTransform(); transform.Apply(&r); transform.Apply(gradient); drawingEngine->FillArc(r, angle, span, *gradient); delete gradient; break; } case AS_STROKE_BEZIER: case AS_FILL_BEZIER: { DTRACE(("ServerWindow %s: Message AS_STROKE/FILL_BEZIER\n", Title())); const SimpleTransform transform = fCurrentView->PenToScreenTransform(); BPoint pts[4]; status_t status; for (int32 i = 0; i < 4; i++) { status = link.Read(&(pts[i])); transform.Apply(&pts[i]); } if (status != B_OK) break; drawingEngine->DrawBezier(pts, code == AS_FILL_BEZIER); break; } case AS_FILL_BEZIER_GRADIENT: { GTRACE(("ServerWindow %s: Message AS_FILL_BEZIER_GRADIENT\n", Title())); const SimpleTransform transform = fCurrentView->PenToScreenTransform(); BPoint pts[4]; for (int32 i = 0; i < 4; i++) { link.Read(&(pts[i])); transform.Apply(&pts[i]); } BGradient* gradient; if (link.ReadGradient(&gradient) != B_OK) break; transform.Apply(gradient); drawingEngine->FillBezier(pts, *gradient); delete gradient; break; } case AS_STROKE_ELLIPSE: case AS_FILL_ELLIPSE: { DTRACE(("ServerWindow %s: Message AS_STROKE/FILL_ELLIPSE\n", Title())); BRect rect; if (link.Read(&rect) != B_OK) break; fCurrentView->PenToScreenTransform().Apply(&rect); drawingEngine->DrawEllipse(rect, code == AS_FILL_ELLIPSE); break; } case AS_FILL_ELLIPSE_GRADIENT: { GTRACE(("ServerWindow %s: Message AS_FILL_ELLIPSE_GRADIENT\n", Title())); BRect rect; link.Read(&rect); BGradient* gradient; if (link.ReadGradient(&gradient) != B_OK) break; const SimpleTransform transform = fCurrentView->PenToScreenTransform(); transform.Apply(&rect); transform.Apply(gradient); drawingEngine->FillEllipse(rect, *gradient); delete gradient; break; } case AS_STROKE_ROUNDRECT: case AS_FILL_ROUNDRECT: { DTRACE(("ServerWindow %s: Message AS_STROKE/FILL_ROUNDRECT\n", Title())); BRect rect; float xRadius; float yRadius; link.Read(&rect); link.Read(&xRadius); if (link.Read(&yRadius) != B_OK) break; fCurrentView->PenToScreenTransform().Apply(&rect); float scale = fCurrentView->CurrentState()->CombinedScale(); drawingEngine->DrawRoundRect(rect, xRadius * scale, yRadius * scale, code == AS_FILL_ROUNDRECT); break; } case AS_FILL_ROUNDRECT_GRADIENT: { GTRACE(("ServerWindow %s: Message AS_FILL_ROUNDRECT_GRADIENT\n", Title())); BRect rect; float xrad,yrad; link.Read(&rect); link.Read(&xrad); link.Read(&yrad); BGradient* gradient; if (link.ReadGradient(&gradient) != B_OK) break; const SimpleTransform transform = fCurrentView->PenToScreenTransform(); transform.Apply(&rect); transform.Apply(gradient); drawingEngine->FillRoundRect(rect, xrad, yrad, *gradient); delete gradient; break; } case AS_STROKE_TRIANGLE: case AS_FILL_TRIANGLE: { DTRACE(("ServerWindow %s: Message AS_STROKE/FILL_TRIANGLE\n", Title())); const SimpleTransform transform = fCurrentView->PenToScreenTransform(); BPoint pts[3]; BRect rect; for (int32 i = 0; i < 3; i++) { link.Read(&(pts[i])); transform.Apply(&pts[i]); } if (link.Read(&rect) != B_OK) break; transform.Apply(&rect); drawingEngine->DrawTriangle(pts, rect, code == AS_FILL_TRIANGLE); break; } case AS_FILL_TRIANGLE_GRADIENT: { DTRACE(("ServerWindow %s: Message AS_FILL_TRIANGLE_GRADIENT\n", Title())); const SimpleTransform transform = fCurrentView->PenToScreenTransform(); BPoint pts[3]; BRect rect; for (int32 i = 0; i < 3; i++) { link.Read(&(pts[i])); transform.Apply(&pts[i]); } link.Read(&rect); BGradient* gradient; if (link.ReadGradient(&gradient) != B_OK) break; transform.Apply(&rect); transform.Apply(gradient); drawingEngine->FillTriangle(pts, rect, *gradient); delete gradient; break; } case AS_STROKE_POLYGON: case AS_FILL_POLYGON: { DTRACE(("ServerWindow %s: Message AS_STROKE/FILL_POLYGON\n", Title())); BRect polyFrame; bool isClosed = true; int32 pointCount; link.Read(&polyFrame); if (code == AS_STROKE_POLYGON) link.Read(&isClosed); link.Read(&pointCount); const SimpleTransform transform = fCurrentView->PenToScreenTransform(); BPoint* pointList = new(nothrow) BPoint[pointCount]; if (link.Read(pointList, pointCount * sizeof(BPoint)) >= B_OK) { for (int32 i = 0; i < pointCount; i++) transform.Apply(&pointList[i]); transform.Apply(&polyFrame); drawingEngine->DrawPolygon(pointList, pointCount, polyFrame, code == AS_FILL_POLYGON, isClosed && pointCount > 2); } delete[] pointList; break; } case AS_FILL_POLYGON_GRADIENT: { DTRACE(("ServerWindow %s: Message AS_FILL_POLYGON_GRADIENT\n", Title())); BRect polyFrame; bool isClosed = true; int32 pointCount; link.Read(&polyFrame); link.Read(&pointCount); const SimpleTransform transform = fCurrentView->PenToScreenTransform(); BPoint* pointList = new(nothrow) BPoint[pointCount]; BGradient* gradient; if (link.Read(pointList, pointCount * sizeof(BPoint)) == B_OK && link.ReadGradient(&gradient) == B_OK) { for (int32 i = 0; i < pointCount; i++) transform.Apply(&pointList[i]); transform.Apply(&polyFrame); transform.Apply(gradient); drawingEngine->FillPolygon(pointList, pointCount, polyFrame, *gradient, isClosed && pointCount > 2); delete gradient; } delete[] pointList; break; } case AS_STROKE_SHAPE: case AS_FILL_SHAPE: { DTRACE(("ServerWindow %s: Message AS_STROKE/FILL_SHAPE\n", Title())); BRect shapeFrame; int32 opCount; int32 ptCount; link.Read(&shapeFrame); link.Read(&opCount); link.Read(&ptCount); uint32* opList = new(nothrow) uint32[opCount]; BPoint* ptList = new(nothrow) BPoint[ptCount]; if (link.Read(opList, opCount * sizeof(uint32)) >= B_OK && link.Read(ptList, ptCount * sizeof(BPoint)) >= B_OK) { // this might seem a bit weird, but under R5, the shapes // are always offset by the current pen location BPoint screenOffset = fCurrentView->CurrentState()->PenLocation(); shapeFrame.OffsetBy(screenOffset); const SimpleTransform transform = fCurrentView->PenToScreenTransform(); transform.Apply(&screenOffset); transform.Apply(&shapeFrame); drawingEngine->DrawShape(shapeFrame, opCount, opList, ptCount, ptList, code == AS_FILL_SHAPE, screenOffset, fCurrentView->Scale()); } delete[] opList; delete[] ptList; break; } case AS_FILL_SHAPE_GRADIENT: { DTRACE(("ServerWindow %s: Message AS_FILL_SHAPE_GRADIENT\n", Title())); BRect shapeFrame; int32 opCount; int32 ptCount; link.Read(&shapeFrame); link.Read(&opCount); link.Read(&ptCount); uint32* opList = new(nothrow) uint32[opCount]; BPoint* ptList = new(nothrow) BPoint[ptCount]; BGradient* gradient; if (link.Read(opList, opCount * sizeof(uint32)) == B_OK && link.Read(ptList, ptCount * sizeof(BPoint)) == B_OK && link.ReadGradient(&gradient) == B_OK) { // this might seem a bit weird, but under R5, the shapes // are always offset by the current pen location BPoint screenOffset = fCurrentView->CurrentState()->PenLocation(); shapeFrame.OffsetBy(screenOffset); const SimpleTransform transform = fCurrentView->PenToScreenTransform(); transform.Apply(&screenOffset); transform.Apply(&shapeFrame); transform.Apply(gradient); drawingEngine->FillShape(shapeFrame, opCount, opList, ptCount, ptList, *gradient, screenOffset, fCurrentView->Scale()); delete gradient; } delete[] opList; delete[] ptList; break; } case AS_FILL_REGION: { DTRACE(("ServerWindow %s: Message AS_FILL_REGION\n", Title())); BRegion region; if (link.ReadRegion(®ion) < B_OK) break; fCurrentView->PenToScreenTransform().Apply(®ion); drawingEngine->FillRegion(region); break; } case AS_FILL_REGION_GRADIENT: { DTRACE(("ServerWindow %s: Message AS_FILL_REGION_GRADIENT\n", Title())); BRegion region; link.ReadRegion(®ion); BGradient* gradient; if (link.ReadGradient(&gradient) != B_OK) break; const SimpleTransform transform = fCurrentView->PenToScreenTransform(); transform.Apply(®ion); transform.Apply(gradient); drawingEngine->FillRegion(region, *gradient); delete gradient; break; } case AS_STROKE_LINEARRAY: { DTRACE(("ServerWindow %s: Message AS_STROKE_LINEARRAY\n", Title())); // Attached Data: // 1) int32 Number of lines in the array // 2) LineArrayData int32 lineCount; if (link.Read(&lineCount) != B_OK || lineCount <= 0) break; // To speed things up, try to use a stack allocation and only // fall back to the heap if there are enough lines... ViewLineArrayInfo* lineData; const int32 kStackBufferLineDataCount = 64; ViewLineArrayInfo lineDataStackBuffer[kStackBufferLineDataCount]; if (lineCount > kStackBufferLineDataCount) { lineData = new(std::nothrow) ViewLineArrayInfo[lineCount]; if (lineData == NULL) break; } else lineData = lineDataStackBuffer; // Read them all in one go size_t dataSize = lineCount * sizeof(ViewLineArrayInfo); if (link.Read(lineData, dataSize) != B_OK) { if (lineData != lineDataStackBuffer) delete[] lineData; break; } // Convert to screen coords and draw const SimpleTransform transform = fCurrentView->PenToScreenTransform(); for (int32 i = 0; i < lineCount; i++) { transform.Apply(&lineData[i].startPoint); transform.Apply(&lineData[i].endPoint); } drawingEngine->StrokeLineArray(lineCount, lineData); if (lineData != lineDataStackBuffer) delete[] lineData; break; } case AS_DRAW_STRING: case AS_DRAW_STRING_WITH_DELTA: { ViewDrawStringInfo info; if (link.Read(&info) != B_OK || info.stringLength <= 0) { break; } // NOTE: Careful, the + 1 is for termination! BStackOrHeapArray string( (info.stringLength + 1 + 63) / 64 * 64); if (!string.IsValid()) break; escapement_delta* delta = NULL; if (code == AS_DRAW_STRING_WITH_DELTA) { // In this case, info.delta will contain valid values. delta = &info.delta; } if (link.Read(string, info.stringLength) != B_OK) break; // Terminate the string, if nothing else, it's important // for the DTRACE call below... string[info.stringLength] = '\0'; DTRACE(("ServerWindow %s: Message AS_DRAW_STRING, View: %s " "-> %s\n", Title(), fCurrentView->Name(), string)); fCurrentView->PenToScreenTransform().Apply(&info.location); BPoint penLocation = drawingEngine->DrawString(string, info.stringLength, info.location, delta); fCurrentView->ScreenToPenTransform().Apply(&penLocation); fCurrentView->CurrentState()->SetPenLocation(penLocation); break; } case AS_DRAW_STRING_WITH_OFFSETS: { int32 stringLength; if (link.Read(&stringLength) != B_OK || stringLength <= 0) break; int32 glyphCount; if (link.Read(&glyphCount) != B_OK || glyphCount <= 0) break; // NOTE: Careful, the + 1 is for termination! BStackOrHeapArray string( (stringLength + 1 + 63) / 64 * 64); BStackOrHeapArray locations(glyphCount); if (!string.IsValid() || !locations.IsValid()) break; if (link.Read(string, stringLength) != B_OK) break; // Count UTF8 glyphs and make sure we have enough locations if ((int32)UTF8CountChars(string, stringLength) > glyphCount) break; if (link.Read(locations, glyphCount * sizeof(BPoint)) != B_OK) break; // Terminate the string, if nothing else, it's important // for the DTRACE call below... string[stringLength] = '\0'; DTRACE(("ServerWindow %s: Message AS_DRAW_STRING_WITH_OFFSETS, View: %s " "-> %s\n", Title(), fCurrentView->Name(), string)); const SimpleTransform transform = fCurrentView->PenToScreenTransform(); for (int32 i = 0; i < glyphCount; i++) transform.Apply(&locations[i]); BPoint penLocation = drawingEngine->DrawString(string, stringLength, locations); fCurrentView->ScreenToPenTransform().Apply(&penLocation); fCurrentView->CurrentState()->SetPenLocation(penLocation); break; } case AS_VIEW_DRAW_PICTURE: { int32 token; link.Read(&token); BPoint where; if (link.Read(&where) == B_OK) { BReference picture(App()->GetPicture(token), true); if (picture != NULL) { // Setting the drawing origin outside of the // state makes sure that everything the picture // does is relative to the global picture offset. fCurrentView->PushState(); fCurrentView->SetDrawingOrigin(where); fCurrentView->PushState(); picture->Play(fCurrentView); fCurrentView->PopState(); fCurrentView->PopState(); } } break; } case AS_VIEW_END_LAYER: { DTRACE(("ServerWindow %s: Message AS_VIEW_END_LAYER\n", Title())); fCurrentView->BlendAllLayers(); fCurrentView->SetPicture(NULL); fCurrentView->CurrentState()->SetDrawingModeLocked(false); break; } default: debug_printf("ServerWindow %s received unexpected code: %s\n", Title(), string_for_message_code(code)); if (link.NeedsReply()) { // the client is now blocking and waiting for a reply! fLink.StartMessage(B_ERROR); fLink.Flush(); } break; } drawingEngine->UnlockParallelAccess(); } bool ServerWindow::_DispatchPictureMessage(int32 code, BPrivate::LinkReceiver& link) { ServerPicture* picture = fCurrentView->Picture(); if (picture == NULL) return false; switch (code) { case AS_VIEW_SET_ORIGIN: { float x, y; link.Read(&x); link.Read(&y); fCurrentView->SetDrawingOrigin(BPoint(x, y)); picture->WriteSetOrigin(BPoint(x, y)); break; } case AS_VIEW_INVERT_RECT: { BRect rect; link.Read(&rect); picture->WriteInvertRect(rect); break; } case AS_VIEW_PUSH_STATE: { fCurrentView->PushState(); picture->WritePushState(); break; } case AS_VIEW_POP_STATE: { fCurrentView->PopState(); picture->WritePopState(); break; } case AS_VIEW_SET_DRAWING_MODE: { int8 drawingMode; link.Read(&drawingMode); picture->WriteSetDrawingMode((drawing_mode)drawingMode); fCurrentView->CurrentState()->SetDrawingMode( (drawing_mode)drawingMode); fWindow->GetDrawingEngine()->SetDrawingMode( (drawing_mode)drawingMode); break; } case AS_VIEW_SET_PEN_LOC: { BPoint location; link.Read(&location); picture->WriteSetPenLocation(location); fCurrentView->CurrentState()->SetPenLocation(location); break; } case AS_VIEW_SET_PEN_SIZE: { float penSize; link.Read(&penSize); picture->WriteSetPenSize(penSize); fCurrentView->CurrentState()->SetPenSize(penSize); fWindow->GetDrawingEngine()->SetPenSize( fCurrentView->CurrentState()->PenSize()); break; } case AS_VIEW_SET_LINE_MODE: { ViewSetLineModeInfo info; link.Read(&info); picture->WriteSetLineMode(info.lineCap, info.lineJoin, info.miterLimit); fCurrentView->CurrentState()->SetLineCapMode(info.lineCap); fCurrentView->CurrentState()->SetLineJoinMode(info.lineJoin); fCurrentView->CurrentState()->SetMiterLimit(info.miterLimit); fWindow->GetDrawingEngine()->SetStrokeMode(info.lineCap, info.lineJoin, info.miterLimit); break; } case AS_VIEW_SET_FILL_RULE: { int32 fillRule; if (link.Read(&fillRule) != B_OK) break; picture->WriteSetFillRule(fillRule); fCurrentView->CurrentState()->SetFillRule(fillRule); fWindow->GetDrawingEngine()->SetFillRule(fillRule); break; } case AS_VIEW_SET_SCALE: { float scale; if (link.Read(&scale) != B_OK) break; picture->WriteSetScale(scale); fCurrentView->SetScale(scale); _UpdateDrawState(fCurrentView); break; } case AS_VIEW_SET_TRANSFORM: { BAffineTransform transform; if (link.Read(&transform) != B_OK) break; picture->WriteSetTransform(transform); fCurrentView->CurrentState()->SetTransform(transform); _UpdateDrawState(fCurrentView); break; } case AS_VIEW_AFFINE_TRANSLATE: { double x, y; link.Read(&x); link.Read(&y); picture->WriteTranslateBy(x, y); BAffineTransform current = fCurrentView->CurrentState()->Transform(); current.PreTranslateBy(x, y); fCurrentView->CurrentState()->SetTransform(current); _UpdateDrawState(fCurrentView); break; } case AS_VIEW_AFFINE_SCALE: { double x, y; link.Read(&x); link.Read(&y); picture->WriteScaleBy(x, y); BAffineTransform current = fCurrentView->CurrentState()->Transform(); current.PreScaleBy(x, y); fCurrentView->CurrentState()->SetTransform(current); _UpdateDrawState(fCurrentView); break; } case AS_VIEW_AFFINE_ROTATE: { double angleRadians; link.Read(&angleRadians); picture->WriteRotateBy(angleRadians); BAffineTransform current = fCurrentView->CurrentState()->Transform(); current.PreRotateBy(angleRadians); fCurrentView->CurrentState()->SetTransform(current); _UpdateDrawState(fCurrentView); break; } case AS_VIEW_SET_PATTERN: { pattern pat; link.Read(&pat, sizeof(pattern)); picture->WriteSetPattern(pat); break; } case AS_VIEW_SET_FONT_STATE: { uint16 mask = fCurrentView->CurrentState()->ReadFontFromLink(link); fWindow->GetDrawingEngine()->SetFont( fCurrentView->CurrentState()); picture->WriteFontState(fCurrentView->CurrentState()->Font(), mask); break; } case AS_FILL_RECT: case AS_STROKE_RECT: { BRect rect; link.Read(&rect); picture->WriteDrawRect(rect, code == AS_FILL_RECT); break; } case AS_FILL_REGION: { // There is no B_PIC_FILL_REGION op, we have to // implement it using B_PIC_FILL_RECT BRegion region; if (link.ReadRegion(®ion) < B_OK) break; for (int32 i = 0; i < region.CountRects(); i++) picture->WriteDrawRect(region.RectAt(i), true); break; } case AS_STROKE_ROUNDRECT: case AS_FILL_ROUNDRECT: { BRect rect; link.Read(&rect); BPoint radii; link.Read(&radii.x); link.Read(&radii.y); picture->WriteDrawRoundRect(rect, radii, code == AS_FILL_ROUNDRECT); break; } case AS_STROKE_ELLIPSE: case AS_FILL_ELLIPSE: { BRect rect; link.Read(&rect); picture->WriteDrawEllipse(rect, code == AS_FILL_ELLIPSE); break; } case AS_STROKE_ARC: case AS_FILL_ARC: { BRect rect; link.Read(&rect); float startTheta, arcTheta; link.Read(&startTheta); link.Read(&arcTheta); BPoint radii((rect.Width() + 1) / 2, (rect.Height() + 1) / 2); BPoint center = rect.LeftTop() + radii; picture->WriteDrawArc(center, radii, startTheta, arcTheta, code == AS_FILL_ARC); break; } case AS_STROKE_TRIANGLE: case AS_FILL_TRIANGLE: { // There is no B_PIC_FILL/STROKE_TRIANGLE op, // we implement it using B_PIC_FILL/STROKE_POLYGON BPoint points[3]; for (int32 i = 0; i < 3; i++) { link.Read(&(points[i])); } BRect rect; link.Read(&rect); picture->WriteDrawPolygon(3, points, true, code == AS_FILL_TRIANGLE); break; } case AS_STROKE_POLYGON: case AS_FILL_POLYGON: { BRect polyFrame; bool isClosed = true; int32 pointCount; const bool fill = (code == AS_FILL_POLYGON); link.Read(&polyFrame); if (code == AS_STROKE_POLYGON) link.Read(&isClosed); link.Read(&pointCount); BPoint* pointList = new(nothrow) BPoint[pointCount]; if (link.Read(pointList, pointCount * sizeof(BPoint)) >= B_OK) { picture->WriteDrawPolygon(pointCount, pointList, isClosed && pointCount > 2, fill); } delete[] pointList; break; } case AS_STROKE_BEZIER: case AS_FILL_BEZIER: { BPoint points[4]; for (int32 i = 0; i < 4; i++) { link.Read(&(points[i])); } picture->WriteDrawBezier(points, code == AS_FILL_BEZIER); break; } //case AS_STROKE_RECT_GRADIENT: case AS_FILL_RECT_GRADIENT: { BRect rect; link.Read(&rect); BGradient* gradient; if (link.ReadGradient(&gradient) != B_OK) break; ObjectDeleter gradientDeleter(gradient); picture->WriteDrawRectGradient(rect, *gradient, code == AS_FILL_RECT_GRADIENT); break; } //case AS_STROKE_ARC_GRADIENT: case AS_FILL_ARC_GRADIENT: { BRect rect; link.Read(&rect); float startTheta, arcTheta; link.Read(&startTheta); link.Read(&arcTheta); BGradient* gradient; if (link.ReadGradient(&gradient) != B_OK) break; ObjectDeleter gradientDeleter(gradient); BPoint radii((rect.Width() + 1) / 2, (rect.Height() + 1) / 2); BPoint center = rect.LeftTop() + radii; picture->WriteDrawArcGradient(center, radii, startTheta, arcTheta, *gradient, code == AS_FILL_ARC_GRADIENT); break; } //case AS_STROKE_BEZIER_GRADIENT: case AS_FILL_BEZIER_GRADIENT: { BPoint points[4]; for (int32 i = 0; i < 4; i++) { link.Read(&(points[i])); } BGradient* gradient; if (link.ReadGradient(&gradient) != B_OK) break; ObjectDeleter gradientDeleter(gradient); picture->WriteDrawBezierGradient(points, *gradient, code == AS_FILL_BEZIER_GRADIENT); break; } //case AS_STROKE_ELLIPSE_GRADIENT: case AS_FILL_ELLIPSE_GRADIENT: { BRect rect; link.Read(&rect); BGradient* gradient; if (link.ReadGradient(&gradient) != B_OK) break; ObjectDeleter gradientDeleter(gradient); picture->WriteDrawEllipseGradient(rect, *gradient, code == AS_FILL_ELLIPSE_GRADIENT); break; } //case AS_STROKE_ROUNDRECT_GRADIENT: case AS_FILL_ROUNDRECT_GRADIENT: { BRect rect; link.Read(&rect); BPoint radii; link.Read(&radii.x); link.Read(&radii.y); BGradient* gradient; if (link.ReadGradient(&gradient) != B_OK) break; ObjectDeleter gradientDeleter(gradient); picture->WriteDrawRoundRectGradient(rect, radii, *gradient, code == AS_FILL_ROUNDRECT_GRADIENT); break; } //case AS_STROKE_TRIANGLE_GRADIENT: case AS_FILL_TRIANGLE_GRADIENT: { // There is no B_PIC_FILL/STROKE_TRIANGLE op, // we implement it using B_PIC_FILL/STROKE_POLYGON BPoint points[3]; for (int32 i = 0; i < 3; i++) { link.Read(&(points[i])); } BRect rect; link.Read(&rect); BGradient* gradient; if (link.ReadGradient(&gradient) != B_OK) break; ObjectDeleter gradientDeleter(gradient); picture->WriteDrawPolygonGradient(3, points, true, *gradient, code == AS_FILL_TRIANGLE_GRADIENT); break; } //case AS_STROKE_POLYGON_GRADIENT: case AS_FILL_POLYGON_GRADIENT: { BRect polyFrame; bool isClosed = true; int32 pointCount; const bool fill = (code == AS_FILL_POLYGON_GRADIENT); link.Read(&polyFrame); if (code == AS_STROKE_POLYGON) link.Read(&isClosed); link.Read(&pointCount); ArrayDeleter pointList(new(nothrow) BPoint[pointCount]); if (link.Read(pointList.Get(), pointCount * sizeof(BPoint)) != B_OK) break; BGradient* gradient; if (link.ReadGradient(&gradient) != B_OK) break; ObjectDeleter gradientDeleter(gradient); picture->WriteDrawPolygonGradient(pointCount, pointList.Get(), isClosed && pointCount > 2, *gradient, fill); break; } //case AS_STROKE_SHAPE_GRADIENT: case AS_FILL_SHAPE_GRADIENT: { BRect shapeFrame; int32 opCount; int32 ptCount; link.Read(&shapeFrame); link.Read(&opCount); link.Read(&ptCount); ArrayDeleter opList(new(std::nothrow) uint32[opCount]); ArrayDeleter ptList(new(std::nothrow) BPoint[ptCount]); if (!opList.IsSet() || !ptList.IsSet() || link.Read(opList.Get(), opCount * sizeof(uint32)) != B_OK || link.Read(ptList.Get(), ptCount * sizeof(BPoint)) != B_OK) break; BGradient* gradient; if (link.ReadGradient(&gradient) != B_OK) break; ObjectDeleter gradientDeleter(gradient); // This might seem a bit weird, but under BeOS, the shapes // are always offset by the current pen location BPoint penLocation = fCurrentView->CurrentState()->PenLocation(); for (int32 i = 0; i < ptCount; i++) { ptList.Get()[i] += penLocation; } const bool fill = (code == AS_FILL_SHAPE_GRADIENT); picture->WriteDrawShapeGradient(opCount, opList.Get(), ptCount, ptList.Get(), *gradient, fill); break; } case AS_FILL_REGION_GRADIENT: { // There is no B_PIC_FILL_REGION op, we have to // implement it using B_PIC_FILL_RECT BRegion region; if (link.ReadRegion(®ion) < B_OK) break; BGradient* gradient; if (link.ReadGradient(&gradient) != B_OK) break; ObjectDeleter gradientDeleter(gradient); for (int32 i = 0; i < region.CountRects(); i++) picture->WriteDrawRectGradient(region.RectAt(i), *gradient, true); break; } case AS_STROKE_LINE: { ViewStrokeLineInfo info; link.Read(&info); picture->WriteStrokeLine(info.startPoint, info.endPoint); BPoint penPos = info.endPoint; const SimpleTransform transform = fCurrentView->PenToScreenTransform(); transform.Apply(&info.endPoint); fCurrentView->CurrentState()->SetPenLocation(penPos); break; } case AS_STROKE_LINEARRAY: { int32 lineCount; if (link.Read(&lineCount) != B_OK || lineCount <= 0) break; // To speed things up, try to use a stack allocation and only // fall back to the heap if there are enough lines... ViewLineArrayInfo* lineData; const int32 kStackBufferLineDataCount = 64; ViewLineArrayInfo lineDataStackBuffer[kStackBufferLineDataCount]; if (lineCount > kStackBufferLineDataCount) { lineData = new(std::nothrow) ViewLineArrayInfo[lineCount]; if (lineData == NULL) break; } else lineData = lineDataStackBuffer; // Read them all in one go size_t dataSize = lineCount * sizeof(ViewLineArrayInfo); if (link.Read(lineData, dataSize) != B_OK) { if (lineData != lineDataStackBuffer) delete[] lineData; break; } picture->WritePushState(); for (int32 i = 0; i < lineCount; i++) { picture->WriteSetHighColor(lineData[i].color); picture->WriteStrokeLine(lineData[i].startPoint, lineData[i].endPoint); } picture->WritePopState(); if (lineData != lineDataStackBuffer) delete[] lineData; break; } case AS_VIEW_SET_LOW_COLOR: case AS_VIEW_SET_HIGH_COLOR: { rgb_color color; link.Read(&color, sizeof(rgb_color)); if (code == AS_VIEW_SET_HIGH_COLOR) { picture->WriteSetHighColor(color); fCurrentView->CurrentState()->SetHighColor(color); fWindow->GetDrawingEngine()->SetHighColor(color); } else { picture->WriteSetLowColor(color); fCurrentView->CurrentState()->SetLowColor(color); fWindow->GetDrawingEngine()->SetLowColor(color); } } break; case AS_DRAW_STRING: case AS_DRAW_STRING_WITH_DELTA: { ViewDrawStringInfo info; if (link.Read(&info) != B_OK) break; char* string = (char*)malloc(info.stringLength + 1); if (string == NULL) break; if (code != AS_DRAW_STRING_WITH_DELTA) { // In this case, info.delta will NOT contain valid values. info.delta = (escapement_delta){ 0, 0 }; } if (link.Read(string, info.stringLength) != B_OK) { free(string); break; } // Terminate the string string[info.stringLength] = '\0'; picture->WriteDrawString(info.location, string, info.stringLength, info.delta); // We need to update the pen location fCurrentView->PenToScreenTransform().Apply(&info.location); DrawingEngine* drawingEngine = fWindow->GetDrawingEngine(); if (drawingEngine->LockParallelAccess()) { BPoint penLocation = drawingEngine->DrawStringDry( string, info.stringLength, info.location, &info.delta); fCurrentView->ScreenToPenTransform().Apply(&penLocation); fCurrentView->CurrentState()->SetPenLocation(penLocation); drawingEngine->UnlockParallelAccess(); } free(string); break; } case AS_DRAW_STRING_WITH_OFFSETS: { int32 stringLength; if (link.Read(&stringLength) != B_OK || stringLength <= 0) break; int32 glyphCount; if (link.Read(&glyphCount) != B_OK || glyphCount <= 0) break; // NOTE: Careful, the + 1 is for termination! BStackOrHeapArray string( (stringLength + 1 + 63) / 64 * 64); BStackOrHeapArray locations(glyphCount); if (!string.IsValid() || !locations.IsValid()) break; if (link.Read(string, stringLength) != B_OK) break; // Count UTF8 glyphs and make sure we have enough locations if ((int32)UTF8CountChars(string, stringLength) > glyphCount) break; if (link.Read(locations, glyphCount * sizeof(BPoint)) != B_OK) break; // Terminate the string string[stringLength] = '\0'; const SimpleTransform transform = fCurrentView->PenToScreenTransform(); for (int32 i = 0; i < glyphCount; i++) transform.Apply(&locations[i]); picture->WriteDrawString(string, stringLength, locations, glyphCount); DrawingEngine* drawingEngine = fWindow->GetDrawingEngine(); if (drawingEngine->LockParallelAccess()) { // Update pen location BPoint penLocation = drawingEngine->DrawStringDry( string, stringLength, locations); fCurrentView->ScreenToPenTransform().Apply(&penLocation); fCurrentView->CurrentState()->SetPenLocation(penLocation); drawingEngine->UnlockParallelAccess(); } break; } case AS_STROKE_SHAPE: case AS_FILL_SHAPE: { BRect shapeFrame; int32 opCount; int32 ptCount; link.Read(&shapeFrame); link.Read(&opCount); link.Read(&ptCount); BStackOrHeapArray opList(opCount); BStackOrHeapArray ptList(ptCount); if (!opList.IsValid() || !ptList.IsValid() || link.Read(opList, opCount * sizeof(uint32)) < B_OK || link.Read(ptList, ptCount * sizeof(BPoint)) < B_OK) { break; } picture->WriteDrawShape(opCount, opList, ptCount, ptList, code == AS_FILL_SHAPE); break; } case AS_VIEW_DRAW_BITMAP: { ViewDrawBitmapInfo info; link.Read(&info); BReference bitmap(App()->GetBitmap(info.bitmapToken), true); if (bitmap == NULL) break; picture->WriteDrawBitmap(info.bitmapRect, info.viewRect, bitmap->Width(), bitmap->Height(), bitmap->BytesPerRow(), bitmap->ColorSpace(), info.options, bitmap->Bits(), bitmap->BitsLength()); break; } case AS_VIEW_DRAW_PICTURE: { int32 token; link.Read(&token); BPoint where; if (link.Read(&where) == B_OK) { BReference pictureToDraw(App()->GetPicture(token), true); if (pictureToDraw != NULL) { // We need to make a copy of the picture, since it can // change after it has been drawn BReference copy(App()->CreatePicture(pictureToDraw), true); picture->NestPicture(copy); picture->WriteDrawPicture(where, copy->Token()); } } break; } case AS_VIEW_SET_CLIP_REGION: { int32 rectCount; status_t status = link.Read(&rectCount); // a negative count means no // region for the current draw state, // but an *empty* region is actually valid! // even if it means no drawing is allowed if (status < B_OK) break; if (rectCount >= 0) { // we are supposed to set the clipping region BRegion region; if (rectCount > 0 && link.ReadRegion(®ion) < B_OK) break; picture->WriteSetClipping(region); } else { // we are supposed to clear the clipping region picture->WriteClearClipping(); } break; } case AS_VIEW_CLIP_TO_PICTURE: { int32 pictureToken; BPoint where; bool inverse = false; link.Read(&pictureToken); if (pictureToken < 0) break; link.Read(&where); if (link.Read(&inverse) != B_OK) break; BReference pictureToClip(fServerApp->GetPicture(pictureToken), true); if (pictureToClip != NULL) { // We need to make a copy of the picture, since it can // change after it has been drawn BReference copy(App()->CreatePicture(pictureToClip), true); picture->NestPicture(copy); picture->WriteClipToPicture(copy->Token(), where, inverse); } break; } case AS_VIEW_CLIP_TO_RECT: { bool inverse; BRect rect; link.Read(&inverse); link.Read(&rect); picture->WriteClipToRect(rect, inverse); break; } case AS_VIEW_CLIP_TO_SHAPE: { bool inverse; link.Read(&inverse); shape_data shape; link.Read(&shape.opCount); link.Read(&shape.ptCount); shape.opSize = shape.opCount * sizeof(uint32); shape.ptSize = shape.ptCount * sizeof(BPoint); shape.opList = new(nothrow) uint32[shape.opCount]; shape.ptList = new(nothrow) BPoint[shape.ptCount]; if (link.Read(shape.opList, shape.opSize) >= B_OK && link.Read(shape.ptList, shape.ptSize) >= B_OK) { picture->WriteClipToShape(shape.opCount, shape.opList, shape.ptCount, shape.ptList, inverse); } delete[] shape.opList; delete[] shape.ptList; break; } case AS_VIEW_BEGIN_PICTURE: { BReference newPicture(App()->CreatePicture(), true); if (newPicture != NULL) { newPicture->PushPicture(picture); newPicture->SyncState(fCurrentView); fCurrentView->SetPicture(newPicture); } break; } case AS_VIEW_APPEND_TO_PICTURE: { int32 token; link.Read(&token); BReference appendPicture(App()->GetPicture(token), true); if (appendPicture != NULL) { //picture->SyncState(fCurrentView); appendPicture->AppendPicture(picture); } fCurrentView->SetPicture(appendPicture); break; } case AS_VIEW_END_PICTURE: { BReference poppedPicture(picture->PopPicture(), true); fCurrentView->SetPicture(poppedPicture); fLink.StartMessage(B_OK); fLink.Attach(picture->Token()); fLink.Flush(); return true; } case AS_VIEW_BEGIN_LAYER: { uint8 opacity; link.Read(&opacity); Layer* layer = dynamic_cast(picture); if (layer == NULL) break; Layer* nextLayer = new(std::nothrow) Layer(opacity); if (nextLayer == NULL) break; if (opacity != 255) { fCurrentView->CurrentState()->SetDrawingMode(B_OP_ALPHA); fCurrentView->CurrentState()->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); fCurrentView->CurrentState()->SetDrawingModeLocked(true); } nextLayer->PushLayer(layer); fCurrentView->SetPicture(nextLayer); break; } case AS_VIEW_END_LAYER: { Layer* layer = dynamic_cast(picture); if (layer == NULL) break; BReference previousLayer(layer->PopLayer(), true); if (previousLayer == NULL) { // End last layer return false; } fCurrentView->SetPicture(previousLayer); previousLayer->WriteBlendLayer(layer); break; } /* case AS_VIEW_SET_BLENDING_MODE: { ViewBlendingModeInfo info; link.Read(&info); picture->BeginOp(B_PIC_SET_BLENDING_MODE); picture->AddInt16((int16)info.sourceAlpha); picture->AddInt16((int16)info.alphaFunction); picture->EndOp(); fCurrentView->CurrentState()->SetBlendingMode(info.sourceAlpha, info.alphaFunction); fWindow->GetDrawingEngine()->SetBlendingMode(info.sourceAlpha, info.alphaFunction); break; }*/ default: return false; } if (link.NeedsReply()) { fLink.StartMessage(B_ERROR); fLink.Flush(); } return true; } /*! \brief Message-dispatching loop for the ServerWindow Watches the ServerWindow's message port and dispatches as necessary */ void ServerWindow::_MessageLooper() { // Send a reply to our window - it is expecting fMessagePort // port and some other info. fLink.StartMessage(B_OK); fLink.Attach(fMessagePort); int32 minWidth, maxWidth, minHeight, maxHeight; fWindow->GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight); fLink.Attach(fWindow->Frame()); fLink.Attach((float)minWidth); fLink.Attach((float)maxWidth); fLink.Attach((float)minHeight); fLink.Attach((float)maxHeight); fLink.Flush(); BPrivate::LinkReceiver& receiver = fLink.Receiver(); bool quitLoop = false; while (!quitLoop) { //STRACE(("info: ServerWindow::MonitorWin listening on port %ld.\n", // fMessagePort)); int32 code; status_t status = receiver.GetNextMessage(code); if (status != B_OK) { // that shouldn't happen, it's our port printf("Someone deleted our message port!\n"); // try to let our client die happily NotifyQuitRequested(); break; } #ifdef PROFILE_MESSAGE_LOOP bigtime_t start = system_time(); #endif Lock(); #ifdef PROFILE_MESSAGE_LOOP bigtime_t diff = system_time() - start; if (diff > 10000) { printf("ServerWindow %s: lock acquisition took %" B_PRId64 " usecs\n", Title(), diff); } #endif int32 messagesProcessed = 0; bigtime_t processingStart = system_time(); bool lockedDesktopSingleWindow = false; while (true) { if (code == AS_DELETE_WINDOW || code == kMsgQuitLooper) { // this means the client has been killed DTRACE(("ServerWindow %s received 'AS_DELETE_WINDOW' message " "code\n", Title())); if (code == AS_DELETE_WINDOW) { fLink.StartMessage(B_OK); fLink.Flush(); } if (lockedDesktopSingleWindow) fDesktop->UnlockSingleWindow(); quitLoop = true; // ServerWindow's destructor takes care of pulling this object // off the desktop. ASSERT(fWindow->IsHidden()); break; } // Acquire the appropriate lock bool needsAllWindowsLocked = _MessageNeedsAllWindowsLocked(code); if (needsAllWindowsLocked) { // We may already still hold the read-lock from the previous // inner-loop iteration. if (lockedDesktopSingleWindow) { fDesktop->UnlockSingleWindow(); lockedDesktopSingleWindow = false; } fDesktop->LockAllWindows(); } else { // We never keep the write-lock across inner-loop iterations, // so there is nothing else to do besides read-locking unless // we already have the read-lock from the previous iteration. if (!lockedDesktopSingleWindow) { fDesktop->LockSingleWindow(); lockedDesktopSingleWindow = true; } } if (atomic_and(&fRedrawRequested, 0) != 0) { #ifdef PROFILE_MESSAGE_LOOP bigtime_t redrawStart = system_time(); #endif fWindow->RedrawDirtyRegion(); #ifdef PROFILE_MESSAGE_LOOP diff = system_time() - redrawStart; atomic_add(&sRedrawProcessingTime.count, 1); # ifndef HAIKU_TARGET_PLATFORM_LIBBE_TEST atomic_add64(&sRedrawProcessingTime.time, diff); # else sRedrawProcessingTime.time += diff; # endif #endif } #ifdef PROFILE_MESSAGE_LOOP bigtime_t dispatchStart = system_time(); #endif _DispatchMessage(code, receiver); #ifdef PROFILE_MESSAGE_LOOP if (code >= 0 && code < AS_LAST_CODE) { diff = system_time() - dispatchStart; atomic_add(&sMessageProfile[code].count, 1); #ifndef HAIKU_TARGET_PLATFORM_LIBBE_TEST atomic_add64(&sMessageProfile[code].time, diff); #else sMessageProfile[code].time += diff; #endif if (diff > 10000) { printf("ServerWindow %s: message %" B_PRId32 " took %" B_PRId64 " usecs\n", Title(), code, diff); } } #endif if (needsAllWindowsLocked) fDesktop->UnlockAllWindows(); // Only process up to 70 waiting messages at once (we have the // Desktop locked), but don't hold the lock longer than 10 ms if (!receiver.HasMessages() || ++messagesProcessed > 70 || system_time() - processingStart > 10000) { if (lockedDesktopSingleWindow) fDesktop->UnlockSingleWindow(); break; } // next message status_t status = receiver.GetNextMessage(code); if (status != B_OK) { // that shouldn't happen, it's our port printf("Someone deleted our message port!\n"); if (lockedDesktopSingleWindow) fDesktop->UnlockSingleWindow(); // try to let our client die happily NotifyQuitRequested(); break; } } Unlock(); } // We were asked to quit the message loop - either on request or because of // an error. Quit(); // does not return } void ServerWindow::ScreenChanged(const BMessage* message) { SendMessageToClient(message); if (fDirectWindowInfo.IsSet() && fDirectWindowInfo->IsFullScreen()) _ResizeToFullScreen(); } status_t ServerWindow::SendMessageToClient(const BMessage* msg, int32 target) const { if (target == B_NULL_TOKEN) target = fClientToken; BMessenger reply; BMessage::Private messagePrivate((BMessage*)msg); return messagePrivate.SendMessage(fClientLooperPort, fClientTeam, target, 0, false, reply); } Window* ServerWindow::MakeWindow(BRect frame, const char* name, window_look look, window_feel feel, uint32 flags, uint32 workspace) { // The non-offscreen ServerWindow uses the DrawingEngine instance from // the desktop. return new(std::nothrow) ::Window(frame, name, look, feel, flags, workspace, this, fDesktop->HWInterface()->CreateDrawingEngine()); } void ServerWindow::HandleDirectConnection(int32 bufferState, int32 driverState) { ASSERT_MULTI_LOCKED(fDesktop->WindowLocker()); if (!fDirectWindowInfo.IsSet()) return; STRACE(("HandleDirectConnection(bufferState = %" B_PRId32 ", driverState = " "%" B_PRId32 ")\n", bufferState, driverState)); status_t status = fDirectWindowInfo->SetState( (direct_buffer_state)bufferState, (direct_driver_state)driverState, fDesktop->HWInterface()->FrontBuffer(), fWindow->Frame(), fWindow->VisibleContentRegion()); if (status != B_OK) { char errorString[256]; snprintf(errorString, sizeof(errorString), "%s killed for a problem in DirectConnected(): %s", App()->Signature(), strerror(status)); syslog(LOG_ERR, errorString); // The client application didn't release the semaphore // within the given timeout. Or something else went wrong. // Deleting this member should make it crash. fDirectWindowInfo.Unset(); } else if ((bufferState & B_DIRECT_MODE_MASK) == B_DIRECT_START) fIsDirectlyAccessing = true; else if ((bufferState & B_DIRECT_MODE_MASK) == B_DIRECT_STOP) fIsDirectlyAccessing = false; } void ServerWindow::_SetCurrentView(View* view) { if (fCurrentView == view) return; fCurrentView = view; fCurrentDrawingRegionValid = false; _UpdateDrawState(fCurrentView); #if 0 #if DELAYED_BACKGROUND_CLEARING if (fCurrentView && fCurrentView->IsBackgroundDirty() && fWindow->InUpdate()) { DrawingEngine* drawingEngine = fWindow->GetDrawingEngine(); if (drawingEngine->LockParallelAccess()) { fWindow->GetEffectiveDrawingRegion(fCurrentView, fCurrentDrawingRegion); fCurrentDrawingRegionValid = true; BRegion dirty(fCurrentDrawingRegion); BRegion content; fWindow->GetContentRegion(&content); fCurrentView->Draw(drawingEngine, &dirty, &content, false); drawingEngine->UnlockParallelAccess(); } } #endif #endif // 0 } void ServerWindow::_UpdateDrawState(View* view) { // switch the drawing state // TODO: is it possible to scroll a view while it // is being drawn? probably not... otherwise the // "offsets" passed below would need to be updated again DrawingEngine* drawingEngine = fWindow->GetDrawingEngine(); if (view != NULL && drawingEngine != NULL) { BPoint leftTop(0, 0); if (view->GetAlphaMask() != NULL) { view->LocalToScreenTransform().Apply(&leftTop); view->GetAlphaMask()->SetCanvasGeometry(leftTop, view->Bounds()); leftTop = BPoint(0, 0); } view->PenToScreenTransform().Apply(&leftTop); drawingEngine->SetDrawState(view->CurrentState(), leftTop.x, leftTop.y); } } void ServerWindow::_UpdateCurrentDrawingRegion() { if (!fCurrentDrawingRegionValid || fWindow->DrawingRegionChanged(fCurrentView)) { fWindow->GetEffectiveDrawingRegion(fCurrentView, fCurrentDrawingRegion); fCurrentDrawingRegionValid = true; } } bool ServerWindow::_MessageNeedsAllWindowsLocked(uint32 code) const { switch (code) { case AS_SET_WINDOW_TITLE: case AS_ADD_TO_SUBSET: case AS_REMOVE_FROM_SUBSET: case AS_VIEW_CREATE_ROOT: case AS_VIEW_CREATE: case AS_SEND_BEHIND: case AS_SET_LOOK: case AS_SET_FEEL: case AS_SET_FLAGS: case AS_SET_WORKSPACES: case AS_WINDOW_MOVE: case AS_WINDOW_RESIZE: case AS_SET_SIZE_LIMITS: case AS_SYSTEM_FONT_CHANGED: case AS_SET_DECORATOR_SETTINGS: case AS_GET_MOUSE: case AS_DIRECT_WINDOW_SET_FULLSCREEN: // case AS_VIEW_SET_EVENT_MASK: // case AS_VIEW_SET_MOUSE_EVENT_MASK: case AS_TALK_TO_DESKTOP_LISTENER: return true; default: return false; } } void ServerWindow::_ResizeToFullScreen() { BRect screenFrame; { AutoReadLocker _(fDesktop->ScreenLocker()); const Screen* screen = fWindow->Screen(); if (screen == NULL) return; screenFrame = fWindow->Screen()->Frame(); } fDesktop->MoveWindowBy(fWindow.Get(), screenFrame.left - fWindow->Frame().left, screenFrame.top - fWindow->Frame().top); fDesktop->ResizeWindowBy(fWindow.Get(), screenFrame.Width() - fWindow->Frame().Width(), screenFrame.Height() - fWindow->Frame().Height()); } status_t ServerWindow::_EnableDirectWindowMode() { if (fDirectWindowInfo.IsSet()) { // already in direct window mode return B_ERROR; } if (fDesktop->HWInterface()->FrontBuffer() == NULL) { // direct window mode not supported return B_UNSUPPORTED; } fDirectWindowInfo.SetTo(new(std::nothrow) DirectWindowInfo); if (!fDirectWindowInfo.IsSet()) return B_NO_MEMORY; status_t status = fDirectWindowInfo->InitCheck(); if (status != B_OK) { fDirectWindowInfo.Unset(); return status; } return B_OK; } void ServerWindow::_DirectWindowSetFullScreen(bool enable) { window_feel feel = kWindowScreenFeel; if (enable) { fDesktop->HWInterface()->SetCursorVisible(false); fDirectWindowInfo->EnableFullScreen(fWindow->Frame(), fWindow->Feel()); _ResizeToFullScreen(); } else { const BRect& originalFrame = fDirectWindowInfo->OriginalFrame(); fDirectWindowInfo->DisableFullScreen(); // Resize window back to its original size fDesktop->MoveWindowBy(fWindow.Get(), originalFrame.left - fWindow->Frame().left, originalFrame.top - fWindow->Frame().top); fDesktop->ResizeWindowBy(fWindow.Get(), originalFrame.Width() - fWindow->Frame().Width(), originalFrame.Height() - fWindow->Frame().Height()); fDesktop->HWInterface()->SetCursorVisible(true); } fDesktop->SetWindowFeel(fWindow.Get(), feel); }