/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set sw=4 ts=4 expandtab: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #define MOZ_FATAL_ASSERTIONS_FOR_THREAD_SAFETY #include "mozilla/SyncRunnable.h" #include "nsScreenManagerAndroid.h" #include "nsServiceManagerUtils.h" #include "AndroidRect.h" #include "FennecJNINatives.h" #include "nsAppShell.h" #include "nsThreadUtils.h" #include <android/log.h> #include <mozilla/jni/Refs.h> #define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "nsScreenManagerAndroid", ## args) using namespace mozilla; using namespace mozilla::java; static uint32_t sScreenId = 0; const uint32_t PRIMARY_SCREEN_ID = 0; nsScreenAndroid::nsScreenAndroid(DisplayType aDisplayType, nsIntRect aRect) : mId(sScreenId++) , mDisplayType(aDisplayType) , mRect(aRect) , mDensity(0.0) { // ensure that the ID of the primary screen would be PRIMARY_SCREEN_ID. if (mDisplayType == DisplayType::DISPLAY_PRIMARY) { mId = PRIMARY_SCREEN_ID; } } nsScreenAndroid::~nsScreenAndroid() { } float nsScreenAndroid::GetDensity() { if (mDensity != 0.0) { return mDensity; } if (mDisplayType == DisplayType::DISPLAY_PRIMARY) { mDensity = mozilla::jni::IsAvailable() ? GeckoAppShell::GetDensity() : 1.0; // xpcshell most likely return mDensity; } return 1.0; } NS_IMETHODIMP nsScreenAndroid::GetId(uint32_t *outId) { *outId = mId; return NS_OK; } NS_IMETHODIMP nsScreenAndroid::GetRect(int32_t *outLeft, int32_t *outTop, int32_t *outWidth, int32_t *outHeight) { if (mDisplayType != DisplayType::DISPLAY_PRIMARY) { *outLeft = mRect.x; *outTop = mRect.y; *outWidth = mRect.width; *outHeight = mRect.height; return NS_OK; } if (!mozilla::jni::IsAvailable()) { // xpcshell most likely *outLeft = *outTop = *outWidth = *outHeight = 0; return NS_ERROR_FAILURE; } java::sdk::Rect::LocalRef rect = java::GeckoAppShell::GetScreenSize(); rect->Left(outLeft); rect->Top(outTop); rect->Width(outWidth); rect->Height(outHeight); return NS_OK; } NS_IMETHODIMP nsScreenAndroid::GetAvailRect(int32_t *outLeft, int32_t *outTop, int32_t *outWidth, int32_t *outHeight) { return GetRect(outLeft, outTop, outWidth, outHeight); } NS_IMETHODIMP nsScreenAndroid::GetPixelDepth(int32_t *aPixelDepth) { if (!mozilla::jni::IsAvailable()) { // xpcshell most likely *aPixelDepth = 16; return NS_ERROR_FAILURE; } *aPixelDepth = java::GeckoAppShell::GetScreenDepth(); return NS_OK; } NS_IMETHODIMP nsScreenAndroid::GetColorDepth(int32_t *aColorDepth) { return GetPixelDepth(aColorDepth); } void nsScreenAndroid::ApplyMinimumBrightness(uint32_t aBrightness) { if (mDisplayType == DisplayType::DISPLAY_PRIMARY && mozilla::jni::IsAvailable()) { java::GeckoAppShell::SetKeepScreenOn(aBrightness == BRIGHTNESS_FULL); } } class nsScreenManagerAndroid::ScreenManagerHelperSupport final : public ScreenManagerHelper::Natives<ScreenManagerHelperSupport> { public: typedef ScreenManagerHelper::Natives<ScreenManagerHelperSupport> Base; static int32_t AddDisplay(int32_t aDisplayType, int32_t aWidth, int32_t aHeight, float aDensity) { int32_t screenId = -1; // return value nsCOMPtr<nsIThread> mainThread = do_GetMainThread(); SyncRunnable::DispatchToThread(mainThread, NS_NewRunnableFunction( [&aDisplayType, &aWidth, &aHeight, &aDensity, &screenId] { MOZ_ASSERT(NS_IsMainThread()); nsCOMPtr<nsIScreenManager> screenMgr = do_GetService("@mozilla.org/gfx/screenmanager;1"); MOZ_ASSERT(screenMgr, "Failed to get nsIScreenManager"); RefPtr<nsScreenManagerAndroid> screenMgrAndroid = (nsScreenManagerAndroid*) screenMgr.get(); RefPtr<nsScreenAndroid> screen = screenMgrAndroid->AddScreen(static_cast<DisplayType>(aDisplayType), nsIntRect(0, 0, aWidth, aHeight)); MOZ_ASSERT(screen); screen->SetDensity(aDensity); screenId = static_cast<int32_t>(screen->GetId()); }).take()); return screenId; } static void RemoveDisplay(int32_t aScreenId) { nsCOMPtr<nsIThread> mainThread = do_GetMainThread(); SyncRunnable::DispatchToThread(mainThread, NS_NewRunnableFunction( [&aScreenId] { MOZ_ASSERT(NS_IsMainThread()); nsCOMPtr<nsIScreenManager> screenMgr = do_GetService("@mozilla.org/gfx/screenmanager;1"); MOZ_ASSERT(screenMgr, "Failed to get nsIScreenManager"); RefPtr<nsScreenManagerAndroid> screenMgrAndroid = (nsScreenManagerAndroid*) screenMgr.get(); screenMgrAndroid->RemoveScreen(aScreenId); }).take()); } }; NS_IMPL_ISUPPORTS(nsScreenManagerAndroid, nsIScreenManager) nsScreenManagerAndroid::nsScreenManagerAndroid() { if (mozilla::jni::IsAvailable()) { ScreenManagerHelperSupport::Base::Init(); } nsCOMPtr<nsIScreen> screen = AddScreen(DisplayType::DISPLAY_PRIMARY); MOZ_ASSERT(screen); } nsScreenManagerAndroid::~nsScreenManagerAndroid() { } NS_IMETHODIMP nsScreenManagerAndroid::GetPrimaryScreen(nsIScreen **outScreen) { ScreenForId(PRIMARY_SCREEN_ID, outScreen); return NS_OK; } NS_IMETHODIMP nsScreenManagerAndroid::ScreenForId(uint32_t aId, nsIScreen **outScreen) { for (size_t i = 0; i < mScreens.Length(); ++i) { if (aId == mScreens[i]->GetId()) { nsCOMPtr<nsIScreen> screen = (nsIScreen*) mScreens[i]; screen.forget(outScreen); return NS_OK; } } *outScreen = nullptr; return NS_OK; } NS_IMETHODIMP nsScreenManagerAndroid::ScreenForRect(int32_t inLeft, int32_t inTop, int32_t inWidth, int32_t inHeight, nsIScreen **outScreen) { // Not support to query non-primary screen with rect. return GetPrimaryScreen(outScreen); } NS_IMETHODIMP nsScreenManagerAndroid::ScreenForNativeWidget(void *aWidget, nsIScreen **outScreen) { // Not support to query non-primary screen with native widget. return GetPrimaryScreen(outScreen); } NS_IMETHODIMP nsScreenManagerAndroid::GetNumberOfScreens(uint32_t *aNumberOfScreens) { *aNumberOfScreens = mScreens.Length(); return NS_OK; } NS_IMETHODIMP nsScreenManagerAndroid::GetSystemDefaultScale(float *aDefaultScale) { *aDefaultScale = 1.0f; return NS_OK; } already_AddRefed<nsScreenAndroid> nsScreenManagerAndroid::AddScreen(DisplayType aDisplayType, nsIntRect aRect) { ALOG("nsScreenManagerAndroid: add %s screen", (aDisplayType == DisplayType::DISPLAY_PRIMARY ? "PRIMARY" : (aDisplayType == DisplayType::DISPLAY_EXTERNAL ? "EXTERNAL" : "VIRTUAL"))); RefPtr<nsScreenAndroid> screen = new nsScreenAndroid(aDisplayType, aRect); mScreens.AppendElement(screen); return screen.forget(); } void nsScreenManagerAndroid::RemoveScreen(uint32_t aScreenId) { for (size_t i = 0; i < mScreens.Length(); i++) { if (aScreenId == mScreens[i]->GetId()) { mScreens.RemoveElementAt(i); } } }