summaryrefslogtreecommitdiffstats
path: root/widget/gonk/nsScreenManagerGonk.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'widget/gonk/nsScreenManagerGonk.cpp')
-rw-r--r--widget/gonk/nsScreenManagerGonk.cpp1081
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