зеркало из https://github.com/mozilla/gecko-dev.git
1014 строки
28 KiB
C++
1014 строки
28 KiB
C++
/* 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)) {
|
|
MOZ_CRASH("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;
|
|
}
|
|
|
|
float
|
|
nsScreenGonk::GetDpi()
|
|
{
|
|
return mDpi;
|
|
}
|
|
|
|
int32_t
|
|
nsScreenGonk::GetSurfaceFormat()
|
|
{
|
|
return mSurfaceFormat;
|
|
}
|
|
|
|
ANativeWindow*
|
|
nsScreenGonk::GetNativeWindow()
|
|
{
|
|
return mNativeWindow.get();
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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
|