diff options
Diffstat (limited to 'widget/gonk/nsScreenManagerGonk.cpp')
-rw-r--r-- | widget/gonk/nsScreenManagerGonk.cpp | 1081 |
1 files changed, 1081 insertions, 0 deletions
diff --git a/widget/gonk/nsScreenManagerGonk.cpp b/widget/gonk/nsScreenManagerGonk.cpp new file mode 100644 index 000000000..e359fd195 --- /dev/null +++ b/widget/gonk/nsScreenManagerGonk.cpp @@ -0,0 +1,1081 @@ +/* Copyright 2012 Mozilla Foundation and Mozilla contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "android/log.h" +#include "GLContext.h" +#include "gfxPrefs.h" +#include "gfxUtils.h" +#include "mozilla/MouseEvents.h" +#include "mozilla/TouchEvents.h" +#include "mozilla/Hal.h" +#include "libdisplay/BootAnimation.h" +#include "libdisplay/GonkDisplay.h" +#include "nsScreenManagerGonk.h" +#include "nsThreadUtils.h" +#include "HwcComposer2D.h" +#include "VsyncSource.h" +#include "nsWindow.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/Services.h" +#include "mozilla/ProcessPriorityManager.h" +#include "nsIdleService.h" +#include "nsIObserverService.h" +#include "nsAppShell.h" +#include "nsProxyRelease.h" +#include "nsTArray.h" +#include "pixelflinger/format.h" +#include "nsIDisplayInfo.h" +#include "base/task.h" + +#if ANDROID_VERSION >= 17 +#include "libdisplay/DisplaySurface.h" +#endif + +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "nsScreenGonk" , ## args) +#define LOGW(args...) __android_log_print(ANDROID_LOG_WARN, "nsScreenGonk", ## args) +#define LOGE(args...) __android_log_print(ANDROID_LOG_ERROR, "nsScreenGonk", ## args) + +using namespace mozilla; +using namespace mozilla::hal; +using namespace mozilla::gfx; +using namespace mozilla::gl; +using namespace mozilla::layers; +using namespace mozilla::dom; + +namespace { + +class ScreenOnOffEvent : public mozilla::Runnable { +public: + ScreenOnOffEvent(bool on) + : mIsOn(on) + {} + + NS_IMETHOD Run() override { + // Notify observers that the screen state has just changed. + nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService(); + if (observerService) { + observerService->NotifyObservers( + nullptr, "screen-state-changed", + mIsOn ? u"on" : u"off" + ); + } + + RefPtr<nsScreenGonk> screen = nsScreenManagerGonk::GetPrimaryScreen(); + const nsTArray<nsWindow*>& windows = screen->GetTopWindows(); + + for (uint32_t i = 0; i < windows.Length(); i++) { + nsWindow *win = windows[i]; + + if (nsIWidgetListener* listener = win->GetWidgetListener()) { + listener->SizeModeChanged(mIsOn ? nsSizeMode_Fullscreen : nsSizeMode_Minimized); + } + } + + return NS_OK; + } + +private: + bool mIsOn; +}; + +static void +displayEnabledCallback(bool enabled) +{ + RefPtr<nsScreenManagerGonk> screenManager = nsScreenManagerGonk::GetInstance(); + screenManager->DisplayEnabled(enabled); +} + +} // namespace + +static uint32_t +SurfaceFormatToColorDepth(int32_t aSurfaceFormat) +{ + switch (aSurfaceFormat) { + case GGL_PIXEL_FORMAT_RGB_565: + return 16; + case GGL_PIXEL_FORMAT_RGBA_8888: + return 32; + } + return 24; // GGL_PIXEL_FORMAT_RGBX_8888 +} + +// nsScreenGonk.cpp + +nsScreenGonk::nsScreenGonk(uint32_t aId, + GonkDisplay::DisplayType aDisplayType, + const GonkDisplay::NativeData& aNativeData, + NotifyDisplayChangedEvent aEventVisibility) + : mId(aId) + , mEventVisibility(aEventVisibility) + , mNativeWindow(aNativeData.mNativeWindow) + , mDpi(aNativeData.mXdpi) + , mScreenRotation(nsIScreen::ROTATION_0_DEG) + , mPhysicalScreenRotation(nsIScreen::ROTATION_0_DEG) +#if ANDROID_VERSION >= 17 + , mDisplaySurface(aNativeData.mDisplaySurface) +#endif + , mIsMirroring(false) + , mDisplayType(aDisplayType) + , mEGLDisplay(EGL_NO_DISPLAY) + , mEGLSurface(EGL_NO_SURFACE) + , mGLContext(nullptr) + , mFramebuffer(nullptr) + , mMappedBuffer(nullptr) +{ + if (mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_WIDTH, &mVirtualBounds.width) || + mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_HEIGHT, &mVirtualBounds.height) || + mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_FORMAT, &mSurfaceFormat)) { + NS_RUNTIMEABORT("Failed to get native window size, aborting..."); + } + + mNaturalBounds = mVirtualBounds; + + if (IsPrimaryScreen()) { + char propValue[PROPERTY_VALUE_MAX]; + property_get("ro.sf.hwrotation", propValue, "0"); + mPhysicalScreenRotation = atoi(propValue) / 90; + } + + mColorDepth = SurfaceFormatToColorDepth(mSurfaceFormat); +} + +static void +ReleaseGLContextSync(mozilla::gl::GLContext* aGLContext) +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + aGLContext->Release(); +} + +nsScreenGonk::~nsScreenGonk() +{ + // Release GLContext on compositor thread + if (mGLContext) { + CompositorThreadHolder::Loop()->PostTask( + NewRunnableFunction(&ReleaseGLContextSync, + mGLContext.forget().take())); + mGLContext = nullptr; + } +} + +bool +nsScreenGonk::IsPrimaryScreen() +{ + return mDisplayType == GonkDisplay::DISPLAY_PRIMARY; +} + +NS_IMETHODIMP +nsScreenGonk::GetId(uint32_t *outId) +{ + *outId = mId; + return NS_OK; +} + +uint32_t +nsScreenGonk::GetId() +{ + return mId; +} + +NotifyDisplayChangedEvent +nsScreenGonk::GetEventVisibility() +{ + return mEventVisibility; +} + +NS_IMETHODIMP +nsScreenGonk::GetRect(int32_t *outLeft, int32_t *outTop, + int32_t *outWidth, int32_t *outHeight) +{ + *outLeft = mVirtualBounds.x; + *outTop = mVirtualBounds.y; + + *outWidth = mVirtualBounds.width; + *outHeight = mVirtualBounds.height; + + return NS_OK; +} + +LayoutDeviceIntRect +nsScreenGonk::GetRect() +{ + return mVirtualBounds; +} + +NS_IMETHODIMP +nsScreenGonk::GetAvailRect(int32_t *outLeft, int32_t *outTop, + int32_t *outWidth, int32_t *outHeight) +{ + return GetRect(outLeft, outTop, outWidth, outHeight); +} + +NS_IMETHODIMP +nsScreenGonk::GetPixelDepth(int32_t *aPixelDepth) +{ + // XXX: this should actually return 32 when we're using 24-bit + // color, because we use RGBX. + *aPixelDepth = mColorDepth; + return NS_OK; +} + +NS_IMETHODIMP +nsScreenGonk::GetColorDepth(int32_t *aColorDepth) +{ + *aColorDepth = mColorDepth; + return NS_OK; +} + +NS_IMETHODIMP +nsScreenGonk::GetRotation(uint32_t* aRotation) +{ + *aRotation = mScreenRotation; + return NS_OK; +} + +float +nsScreenGonk::GetDpi() +{ + return mDpi; +} + +int32_t +nsScreenGonk::GetSurfaceFormat() +{ + return mSurfaceFormat; +} + +ANativeWindow* +nsScreenGonk::GetNativeWindow() +{ + return mNativeWindow.get(); +} + +NS_IMETHODIMP +nsScreenGonk::SetRotation(uint32_t aRotation) +{ + if (!(aRotation <= ROTATION_270_DEG)) { + return NS_ERROR_ILLEGAL_VALUE; + } + + if (mScreenRotation == aRotation) { + return NS_OK; + } + + mScreenRotation = aRotation; + uint32_t rotation = EffectiveScreenRotation(); + if (rotation == nsIScreen::ROTATION_90_DEG || + rotation == nsIScreen::ROTATION_270_DEG) { + mVirtualBounds = LayoutDeviceIntRect(0, 0, + mNaturalBounds.height, + mNaturalBounds.width); + } else { + mVirtualBounds = mNaturalBounds; + } + + nsAppShell::NotifyScreenRotation(); + + for (unsigned int i = 0; i < mTopWindows.Length(); i++) { + mTopWindows[i]->Resize(mVirtualBounds.width, + mVirtualBounds.height, + true); + } + + return NS_OK; +} + +LayoutDeviceIntRect +nsScreenGonk::GetNaturalBounds() +{ + return mNaturalBounds; +} + +uint32_t +nsScreenGonk::EffectiveScreenRotation() +{ + return (mScreenRotation + mPhysicalScreenRotation) % (360 / 90); +} + +// NB: This isn't gonk-specific, but gonk is the only widget backend +// that does this calculation itself, currently. +static ScreenOrientationInternal +ComputeOrientation(uint32_t aRotation, const LayoutDeviceIntSize& aScreenSize) +{ + bool naturallyPortrait = (aScreenSize.height > aScreenSize.width); + switch (aRotation) { + case nsIScreen::ROTATION_0_DEG: + return (naturallyPortrait ? eScreenOrientation_PortraitPrimary : + eScreenOrientation_LandscapePrimary); + case nsIScreen::ROTATION_90_DEG: + // Arbitrarily choosing 90deg to be primary "unnatural" + // rotation. + return (naturallyPortrait ? eScreenOrientation_LandscapePrimary : + eScreenOrientation_PortraitPrimary); + case nsIScreen::ROTATION_180_DEG: + return (naturallyPortrait ? eScreenOrientation_PortraitSecondary : + eScreenOrientation_LandscapeSecondary); + case nsIScreen::ROTATION_270_DEG: + return (naturallyPortrait ? eScreenOrientation_LandscapeSecondary : + eScreenOrientation_PortraitSecondary); + default: + MOZ_CRASH("Gonk screen must always have a known rotation"); + } +} + +static uint16_t +RotationToAngle(uint32_t aRotation) +{ + uint16_t angle = 90 * aRotation; + MOZ_ASSERT(angle == 0 || angle == 90 || angle == 180 || angle == 270); + return angle; +} + +ScreenConfiguration +nsScreenGonk::GetConfiguration() +{ + ScreenOrientationInternal orientation = + ComputeOrientation(mScreenRotation, mNaturalBounds.Size()); + + // NB: perpetuating colorDepth == pixelDepth illusion here, for + // consistency. + return ScreenConfiguration(mVirtualBounds.ToUnknownRect(), orientation, + RotationToAngle(mScreenRotation), + mColorDepth, mColorDepth); +} + +void +nsScreenGonk::RegisterWindow(nsWindow* aWindow) +{ + mTopWindows.AppendElement(aWindow); +} + +void +nsScreenGonk::UnregisterWindow(nsWindow* aWindow) +{ + mTopWindows.RemoveElement(aWindow); +} + +void +nsScreenGonk::BringToTop(nsWindow* aWindow) +{ + mTopWindows.RemoveElement(aWindow); + mTopWindows.InsertElementAt(0, aWindow); +} + +static gralloc_module_t const* +gralloc_module() +{ + hw_module_t const *module; + if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module)) { + return nullptr; + } + return reinterpret_cast<gralloc_module_t const*>(module); +} + +static SurfaceFormat +HalFormatToSurfaceFormat(int aHalFormat) +{ + switch (aHalFormat) { + case HAL_PIXEL_FORMAT_RGBA_8888: + // Needs RB swap + return SurfaceFormat::B8G8R8A8; + case HAL_PIXEL_FORMAT_RGBX_8888: + // Needs RB swap + return SurfaceFormat::B8G8R8X8; + case HAL_PIXEL_FORMAT_BGRA_8888: + return SurfaceFormat::B8G8R8A8; + case HAL_PIXEL_FORMAT_RGB_565: + return SurfaceFormat::R5G6B5_UINT16; + default: + MOZ_CRASH("Unhandled HAL pixel format"); + return SurfaceFormat::UNKNOWN; // not reached + } +} + +static bool +NeedsRBSwap(int aHalFormat) +{ + switch (aHalFormat) { + case HAL_PIXEL_FORMAT_RGBA_8888: + return true; + case HAL_PIXEL_FORMAT_RGBX_8888: + return true; + case HAL_PIXEL_FORMAT_BGRA_8888: + return false; + case HAL_PIXEL_FORMAT_RGB_565: + return false; + default: + MOZ_CRASH("Unhandled HAL pixel format"); + return false; // not reached + } +} + +already_AddRefed<DrawTarget> +nsScreenGonk::StartRemoteDrawing() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MOZ_ASSERT(!mFramebuffer); + MOZ_ASSERT(!mMappedBuffer); + + mFramebuffer = DequeueBuffer(); + int width = mFramebuffer->width, height = mFramebuffer->height; + if (gralloc_module()->lock(gralloc_module(), mFramebuffer->handle, + GRALLOC_USAGE_SW_READ_NEVER | + GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_FB, + 0, 0, width, height, + reinterpret_cast<void**>(&mMappedBuffer))) { + EndRemoteDrawing(); + return nullptr; + } + SurfaceFormat format = HalFormatToSurfaceFormat(GetSurfaceFormat()); + mFramebufferTarget = Factory::CreateDrawTargetForData( + BackendType::CAIRO, + mMappedBuffer, + IntSize(width, height), + mFramebuffer->stride * gfx::BytesPerPixel(format), + format); + if (!mFramebufferTarget) { + MOZ_CRASH("nsWindow::StartRemoteDrawing failed in CreateDrawTargetForData"); + } + if (!mBackBuffer || + mBackBuffer->GetSize() != mFramebufferTarget->GetSize() || + mBackBuffer->GetFormat() != mFramebufferTarget->GetFormat()) { + mBackBuffer = mFramebufferTarget->CreateSimilarDrawTarget( + mFramebufferTarget->GetSize(), mFramebufferTarget->GetFormat()); + } + RefPtr<DrawTarget> buffer(mBackBuffer); + return buffer.forget(); +} + +void +nsScreenGonk::EndRemoteDrawing() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + + if (mFramebufferTarget && mFramebuffer) { + IntSize size = mFramebufferTarget->GetSize(); + Rect rect(0, 0, size.width, size.height); + RefPtr<SourceSurface> source = mBackBuffer->Snapshot(); + mFramebufferTarget->DrawSurface(source, rect, rect); + + // Convert from BGR to RGB + // XXX this is a temporary solution. It consumes extra cpu cycles, + // it should not be used on product device. + if (NeedsRBSwap(GetSurfaceFormat())) { + LOGE("Very slow composition path, it should not be used on product!!!"); + SurfaceFormat format = HalFormatToSurfaceFormat(GetSurfaceFormat()); + gfxUtils::ConvertBGRAtoRGBA( + mMappedBuffer, + mFramebuffer->stride * mFramebuffer->height * gfx::BytesPerPixel(format)); + } + } + if (mMappedBuffer) { + MOZ_ASSERT(mFramebuffer); + gralloc_module()->unlock(gralloc_module(), mFramebuffer->handle); + mMappedBuffer = nullptr; + } + if (mFramebuffer) { + QueueBuffer(mFramebuffer); + } + mFramebuffer = nullptr; + mFramebufferTarget = nullptr; +} + +ANativeWindowBuffer* +nsScreenGonk::DequeueBuffer() +{ + ANativeWindowBuffer* buf = nullptr; +#if ANDROID_VERSION >= 17 + int fenceFd = -1; + mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf, &fenceFd); + android::sp<android::Fence> fence(new android::Fence(fenceFd)); +#if ANDROID_VERSION == 17 + fence->waitForever(1000, "nsScreenGonk_DequeueBuffer"); + // 1000 is what Android uses. It is a warning timeout in ms. + // This timeout was removed in ANDROID_VERSION 18. +#else + fence->waitForever("nsScreenGonk_DequeueBuffer"); +#endif +#else + mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf); +#endif + return buf; +} + +bool +nsScreenGonk::QueueBuffer(ANativeWindowBuffer* buf) +{ +#if ANDROID_VERSION >= 17 + int ret = mNativeWindow->queueBuffer(mNativeWindow.get(), buf, -1); + return ret == 0; +#else + int ret = mNativeWindow->queueBuffer(mNativeWindow.get(), buf); + return ret == 0; +#endif +} + +nsresult +nsScreenGonk::MakeSnapshot(ANativeWindowBuffer* aBuffer) +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MOZ_ASSERT(aBuffer); + + layers::CompositorBridgeParent* compositorParent = mCompositorBridgeParent; + if (!compositorParent) { + return NS_ERROR_FAILURE; + } + + int width = aBuffer->width, height = aBuffer->height; + uint8_t* mappedBuffer = nullptr; + if (gralloc_module()->lock(gralloc_module(), aBuffer->handle, + GRALLOC_USAGE_SW_READ_OFTEN | + GRALLOC_USAGE_SW_WRITE_OFTEN, + 0, 0, width, height, + reinterpret_cast<void**>(&mappedBuffer))) { + return NS_ERROR_FAILURE; + } + + SurfaceFormat format = HalFormatToSurfaceFormat(GetSurfaceFormat()); + RefPtr<DrawTarget> mTarget = + Factory::CreateDrawTargetForData( + BackendType::CAIRO, + mappedBuffer, + IntSize(width, height), + aBuffer->stride * gfx::BytesPerPixel(format), + format); + if (!mTarget) { + return NS_ERROR_FAILURE; + } + + gfx::IntRect rect = GetRect().ToUnknownRect(); + compositorParent->ForceComposeToTarget(mTarget, &rect); + + // Convert from BGR to RGB + // XXX this is a temporary solution. It consumes extra cpu cycles, + if (NeedsRBSwap(GetSurfaceFormat())) { + LOGE("Slow path of making Snapshot!!!"); + SurfaceFormat format = HalFormatToSurfaceFormat(GetSurfaceFormat()); + gfxUtils::ConvertBGRAtoRGBA( + mappedBuffer, + aBuffer->stride * aBuffer->height * gfx::BytesPerPixel(format)); + mappedBuffer = nullptr; + } + gralloc_module()->unlock(gralloc_module(), aBuffer->handle); + return NS_OK; +} + +void +nsScreenGonk::SetCompositorBridgeParent(layers::CompositorBridgeParent* aCompositorBridgeParent) +{ + MOZ_ASSERT(NS_IsMainThread()); + mCompositorBridgeParent = aCompositorBridgeParent; +} + +#if ANDROID_VERSION >= 17 +android::DisplaySurface* +nsScreenGonk::GetDisplaySurface() +{ + return mDisplaySurface.get(); +} + +int +nsScreenGonk::GetPrevDispAcquireFd() +{ + if (!mDisplaySurface.get()) { + return -1; + } + return mDisplaySurface->GetPrevDispAcquireFd(); +} +#endif + +GonkDisplay::DisplayType +nsScreenGonk::GetDisplayType() +{ + return mDisplayType; +} + +void +nsScreenGonk::SetEGLInfo(hwc_display_t aDisplay, + hwc_surface_t aSurface, + gl::GLContext* aGLContext) +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + mEGLDisplay = aDisplay; + mEGLSurface = aSurface; + mGLContext = aGLContext; +} + +hwc_display_t +nsScreenGonk::GetEGLDisplay() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + return mEGLDisplay; +} + +hwc_surface_t +nsScreenGonk::GetEGLSurface() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + return mEGLSurface; +} + +already_AddRefed<mozilla::gl::GLContext> +nsScreenGonk::GetGLContext() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + RefPtr<mozilla::gl::GLContext>glContext = mGLContext; + return glContext.forget(); +} + +static void +UpdateMirroringWidgetSync(nsMainThreadPtrHandle<nsScreenGonk>&& aScreen, nsWindow* aWindow) +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + already_AddRefed<nsWindow> window(aWindow); + aScreen->UpdateMirroringWidget(window); +} + +bool +nsScreenGonk::EnableMirroring() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!IsPrimaryScreen()); + + RefPtr<nsScreenGonk> primaryScreen = nsScreenManagerGonk::GetPrimaryScreen(); + NS_ENSURE_TRUE(primaryScreen, false); + + bool ret = primaryScreen->SetMirroringScreen(this); + NS_ENSURE_TRUE(ret, false); + + // Create a widget for mirroring + nsWidgetInitData initData; + initData.mScreenId = mId; + RefPtr<nsWindow> window = new nsWindow(); + nsresult rv = window->Create(nullptr, nullptr, mNaturalBounds, &initData); + NS_ENSURE_SUCCESS(rv, false); + MOZ_ASSERT(static_cast<nsWindow*>(window)->GetScreen() == this); + + // Update mMirroringWidget on compositor thread + nsMainThreadPtrHandle<nsScreenGonk> primary = + nsMainThreadPtrHandle<nsScreenGonk>(new nsMainThreadPtrHolder<nsScreenGonk>(primaryScreen, false)); + CompositorThreadHolder::Loop()->PostTask( + NewRunnableFunction(&UpdateMirroringWidgetSync, + primary, + window.forget().take())); + + mIsMirroring = true; + return true; +} + +bool +nsScreenGonk::DisableMirroring() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!IsPrimaryScreen()); + + mIsMirroring = false; + RefPtr<nsScreenGonk> primaryScreen = nsScreenManagerGonk::GetPrimaryScreen(); + NS_ENSURE_TRUE(primaryScreen, false); + + bool ret = primaryScreen->ClearMirroringScreen(this); + NS_ENSURE_TRUE(ret, false); + + // Update mMirroringWidget on compositor thread + nsMainThreadPtrHandle<nsScreenGonk> primary = + nsMainThreadPtrHandle<nsScreenGonk>(new nsMainThreadPtrHolder<nsScreenGonk>(primaryScreen, false)); + CompositorThreadHolder::Loop()->PostTask( + NewRunnableFunction(&UpdateMirroringWidgetSync, + primary, + nullptr)); + return true; +} + +bool +nsScreenGonk::SetMirroringScreen(nsScreenGonk* aScreen) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(IsPrimaryScreen()); + + if (mMirroringScreen) { + return false; + } + mMirroringScreen = aScreen; + return true; +} + +bool +nsScreenGonk::ClearMirroringScreen(nsScreenGonk* aScreen) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(IsPrimaryScreen()); + + if (mMirroringScreen != aScreen) { + return false; + } + mMirroringScreen = nullptr; + return true; +} + +void +nsScreenGonk::UpdateMirroringWidget(already_AddRefed<nsWindow>& aWindow) +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MOZ_ASSERT(IsPrimaryScreen()); + + if (mMirroringWidget) { + nsCOMPtr<nsIWidget> widget = mMirroringWidget.forget(); + NS_ReleaseOnMainThread(widget.forget()); + } + mMirroringWidget = aWindow; +} + +nsWindow* +nsScreenGonk::GetMirroringWidget() +{ + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + MOZ_ASSERT(IsPrimaryScreen()); + + return mMirroringWidget; +} + +NS_IMPL_ISUPPORTS(nsScreenManagerGonk, nsIScreenManager) + +nsScreenManagerGonk::nsScreenManagerGonk() + : mInitialized(false) +#if ANDROID_VERSION >= 19 + , mDisplayEnabled(false) +#endif +{ +} + +nsScreenManagerGonk::~nsScreenManagerGonk() +{ +} + +static StaticRefPtr<nsScreenManagerGonk> sScreenManagerGonk; + +/* static */ already_AddRefed<nsScreenManagerGonk> +nsScreenManagerGonk::GetInstance() +{ + MOZ_ASSERT(NS_IsMainThread()); + + // Avoid creating nsScreenManagerGonk from content process. + if (!XRE_IsParentProcess()) { + MOZ_CRASH("Non-chrome processes should not get here."); + } + + // Avoid creating multiple nsScreenManagerGonk instance inside main process. + if (!sScreenManagerGonk) { + sScreenManagerGonk = new nsScreenManagerGonk(); + ClearOnShutdown(&sScreenManagerGonk); + } + + RefPtr<nsScreenManagerGonk> screenMgr = sScreenManagerGonk.get(); + return screenMgr.forget(); +} + +/* static */ already_AddRefed< nsScreenGonk> +nsScreenManagerGonk::GetPrimaryScreen() +{ + MOZ_ASSERT(NS_IsMainThread()); + + RefPtr<nsScreenManagerGonk> manager = nsScreenManagerGonk::GetInstance(); + nsCOMPtr<nsIScreen> screen; + manager->GetPrimaryScreen(getter_AddRefs(screen)); + MOZ_ASSERT(screen); + return already_AddRefed<nsScreenGonk>( + static_cast<nsScreenGonk*>(screen.forget().take())); +} + +void +nsScreenManagerGonk::Initialize() +{ + if (mInitialized) { + return; + } + + mScreenOnEvent = new ScreenOnOffEvent(true); + mScreenOffEvent = new ScreenOnOffEvent(false); + GetGonkDisplay()->OnEnabled(displayEnabledCallback); + + AddScreen(GonkDisplay::DISPLAY_PRIMARY); + + nsAppShell::NotifyScreenInitialized(); + mInitialized = true; +} + +void +nsScreenManagerGonk::DisplayEnabled(bool aEnabled) +{ + MOZ_ASSERT(NS_IsMainThread()); + +#if ANDROID_VERSION >= 19 + /* Bug 1244044 + * This function could be called before |mCompositorVsyncScheduler| is set. + * To avoid this issue, keep the value stored in |mDisplayEnabled|. + */ + mDisplayEnabled = aEnabled; + if (mCompositorVsyncScheduler) { + mCompositorVsyncScheduler->SetDisplay(mDisplayEnabled); + } +#endif + + VsyncControl(aEnabled); + NS_DispatchToMainThread(aEnabled ? mScreenOnEvent : mScreenOffEvent); +} + +NS_IMETHODIMP +nsScreenManagerGonk::GetPrimaryScreen(nsIScreen **outScreen) +{ + NS_IF_ADDREF(*outScreen = mScreens[0].get()); + return NS_OK; +} + +NS_IMETHODIMP +nsScreenManagerGonk::ScreenForId(uint32_t aId, + nsIScreen **outScreen) +{ + for (size_t i = 0; i < mScreens.Length(); i++) { + if (mScreens[i]->GetId() == aId) { + NS_IF_ADDREF(*outScreen = mScreens[i].get()); + return NS_OK; + } + } + + *outScreen = nullptr; + return NS_OK; +} + +NS_IMETHODIMP +nsScreenManagerGonk::ScreenForRect(int32_t inLeft, + int32_t inTop, + int32_t inWidth, + int32_t inHeight, + nsIScreen **outScreen) +{ + // Since all screens have independent coordinate system, we could + // only return the primary screen no matter what rect is given. + return GetPrimaryScreen(outScreen); +} + +NS_IMETHODIMP +nsScreenManagerGonk::ScreenForNativeWidget(void *aWidget, nsIScreen **outScreen) +{ + for (size_t i = 0; i < mScreens.Length(); i++) { + if (aWidget == mScreens[i]->GetNativeWindow()) { + NS_IF_ADDREF(*outScreen = mScreens[i].get()); + return NS_OK; + } + } + + *outScreen = nullptr; + return NS_OK; +} + +NS_IMETHODIMP +nsScreenManagerGonk::GetNumberOfScreens(uint32_t *aNumberOfScreens) +{ + *aNumberOfScreens = mScreens.Length(); + return NS_OK; +} + +NS_IMETHODIMP +nsScreenManagerGonk::GetSystemDefaultScale(float *aDefaultScale) +{ + *aDefaultScale = 1.0f; + return NS_OK; +} + +void +nsScreenManagerGonk::VsyncControl(bool aEnabled) +{ + if (!NS_IsMainThread()) { + NS_DispatchToMainThread( + NewRunnableMethod<bool>(this, + &nsScreenManagerGonk::VsyncControl, + aEnabled)); + return; + } + + MOZ_ASSERT(NS_IsMainThread()); + VsyncSource::Display &display = gfxPlatform::GetPlatform()->GetHardwareVsync()->GetGlobalDisplay(); + if (aEnabled) { + display.EnableVsync(); + } else { + display.DisableVsync(); + } +} + +uint32_t +nsScreenManagerGonk::GetIdFromType(GonkDisplay::DisplayType aDisplayType) +{ + // This is the only place where we make the assumption that + // display type is equivalent to screen id. + + // Bug 1138287 will address the conversion from type to id. + return aDisplayType; +} + +bool +nsScreenManagerGonk::IsScreenConnected(uint32_t aId) +{ + for (size_t i = 0; i < mScreens.Length(); ++i) { + if (mScreens[i]->GetId() == aId) { + return true; + } + } + + return false; +} + +namespace { + +// A concrete class as a subject for 'display-changed' observer event. +class DisplayInfo : public nsIDisplayInfo { +public: + NS_DECL_ISUPPORTS + + DisplayInfo(uint32_t aId, bool aConnected) + : mId(aId) + , mConnected(aConnected) + { + } + + NS_IMETHODIMP GetId(int32_t *aId) + { + *aId = mId; + return NS_OK; + } + + NS_IMETHODIMP GetConnected(bool *aConnected) + { + *aConnected = mConnected; + return NS_OK; + } + +private: + virtual ~DisplayInfo() {} + + uint32_t mId; + bool mConnected; +}; + +NS_IMPL_ISUPPORTS(DisplayInfo, nsIDisplayInfo, nsISupports) + +class NotifyTask : public mozilla::Runnable { +public: + NotifyTask(uint32_t aId, bool aConnected) + : mDisplayInfo(new DisplayInfo(aId, aConnected)) + { + } + + NS_IMETHOD Run() override + { + nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); + if (os) { + os->NotifyObservers(mDisplayInfo, "display-changed", nullptr); + } + + return NS_OK; + } +private: + RefPtr<DisplayInfo> mDisplayInfo; +}; + +void +NotifyDisplayChange(uint32_t aId, bool aConnected) +{ + NS_DispatchToMainThread(new NotifyTask(aId, aConnected)); +} + +} // end of unnamed namespace. + +nsresult +nsScreenManagerGonk::AddScreen(GonkDisplay::DisplayType aDisplayType, + android::IGraphicBufferProducer* aSink, + NotifyDisplayChangedEvent aEventVisibility) +{ + MOZ_ASSERT(NS_IsMainThread()); + + NS_ENSURE_TRUE(aDisplayType < GonkDisplay::DisplayType::NUM_DISPLAY_TYPES, + NS_ERROR_FAILURE); + + uint32_t id = GetIdFromType(aDisplayType); + NS_ENSURE_TRUE(!IsScreenConnected(id), NS_ERROR_FAILURE); + + GonkDisplay::NativeData nativeData = + GetGonkDisplay()->GetNativeData(aDisplayType, aSink); + nsScreenGonk* screen = new nsScreenGonk(id, + aDisplayType, + nativeData, + aEventVisibility); + mScreens.AppendElement(screen); + + if (aEventVisibility == NotifyDisplayChangedEvent::Observable) { + NotifyDisplayChange(id, true); + } + + // By default, non primary screen does mirroring. + if (aDisplayType != GonkDisplay::DISPLAY_PRIMARY && + gfxPrefs::ScreenMirroringEnabled()) { + screen->EnableMirroring(); + } + + return NS_OK; +} + +nsresult +nsScreenManagerGonk::RemoveScreen(GonkDisplay::DisplayType aDisplayType) +{ + MOZ_ASSERT(NS_IsMainThread()); + + NS_ENSURE_TRUE(aDisplayType < GonkDisplay::DisplayType::NUM_DISPLAY_TYPES, + NS_ERROR_FAILURE); + + NotifyDisplayChangedEvent eventVisibility = NotifyDisplayChangedEvent::Observable; + uint32_t screenId = GetIdFromType(aDisplayType); + NS_ENSURE_TRUE(IsScreenConnected(screenId), NS_ERROR_FAILURE); + + for (size_t i = 0; i < mScreens.Length(); i++) { + if (mScreens[i]->GetId() == screenId) { + if (mScreens[i]->IsMirroring()) { + mScreens[i]->DisableMirroring(); + } + eventVisibility = mScreens[i]->GetEventVisibility(); + mScreens.RemoveElementAt(i); + break; + } + } + + if (eventVisibility == NotifyDisplayChangedEvent::Observable) { + NotifyDisplayChange(screenId, false); + } + return NS_OK; +} + +#if ANDROID_VERSION >= 19 +void +nsScreenManagerGonk::SetCompositorVsyncScheduler(mozilla::layers::CompositorVsyncScheduler *aObserver) +{ + MOZ_ASSERT(NS_IsMainThread()); + + // We assume on b2g that there is only 1 CompositorBridgeParent + MOZ_ASSERT(mCompositorVsyncScheduler == nullptr); + MOZ_ASSERT(aObserver); + mCompositorVsyncScheduler = aObserver; + mCompositorVsyncScheduler->SetDisplay(mDisplayEnabled); +} +#endif |