summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Smith <brian@dbsoft.org>2022-04-26 09:38:50 -0500
committerBrian Smith <brian@dbsoft.org>2022-04-26 10:19:02 -0500
commitadf51bc0f5eb9b924e52c11bb184f6a4d2c5534a (patch)
tree797a081cc0bb717a8a83a7c508b9265dec244217
parent378738aaa9924d0b95e2c57f27cbad2b2e644282 (diff)
downloaduxp-adf51bc0f5eb9b924e52c11bb184f6a4d2c5534a.tar.gz
Issue #1829 - Revert "Issue #1751 -- Remove cocoa and uikit gfx and hal support code"
This reverts commit 1ee35eafa043142293f3d42317c1eee490d00375.
-rw-r--r--gfx/gl/ForceDiscreteGPUHelperCGL.h36
-rw-r--r--gfx/gl/GLContextCGL.h67
-rw-r--r--gfx/gl/GLContextEAGL.h80
-rw-r--r--gfx/gl/GLContextProviderCGL.mm397
-rw-r--r--gfx/gl/GLContextProviderEAGL.mm275
-rw-r--r--gfx/gl/SharedSurfaceIO.cpp248
-rw-r--r--gfx/gl/SharedSurfaceIO.h100
-rw-r--r--gfx/gl/moz.build32
-rw-r--r--hal/cocoa/CocoaSensor.mm149
-rw-r--r--hal/cocoa/smslib.h159
-rw-r--r--hal/cocoa/smslib.mm938
-rw-r--r--hal/moz.build13
12 files changed, 2493 insertions, 1 deletions
diff --git a/gfx/gl/ForceDiscreteGPUHelperCGL.h b/gfx/gl/ForceDiscreteGPUHelperCGL.h
new file mode 100644
index 0000000000..3b4cb07efa
--- /dev/null
+++ b/gfx/gl/ForceDiscreteGPUHelperCGL.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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/. */
+
+#ifndef ForceDiscreteGPUHelperCGL_h_
+#define ForceDiscreteGPUHelperCGL_h_
+
+#include <OpenGL/OpenGL.h>
+
+/** This RAII helper guarantees that we're on the discrete GPU during its lifetime.
+ *
+ * As long as any ForceDiscreteGPUHelperCGL object is alive, we're on the discrete GPU.
+ */
+class ForceDiscreteGPUHelperCGL
+{
+ CGLPixelFormatObj mPixelFormatObj;
+
+public:
+ ForceDiscreteGPUHelperCGL()
+ {
+ // the code in this function is taken from Chromium, src/ui/gfx/gl/gl_context_cgl.cc, r122013
+ // BSD-style license, (c) The Chromium Authors
+ CGLPixelFormatAttribute attribs[1];
+ attribs[0] = static_cast<CGLPixelFormatAttribute>(0);
+ GLint num_pixel_formats = 0;
+ CGLChoosePixelFormat(attribs, &mPixelFormatObj, &num_pixel_formats);
+ }
+
+ ~ForceDiscreteGPUHelperCGL()
+ {
+ CGLReleasePixelFormat(mPixelFormatObj);
+ }
+};
+
+#endif // ForceDiscreteGPUHelperCGL_h_
diff --git a/gfx/gl/GLContextCGL.h b/gfx/gl/GLContextCGL.h
new file mode 100644
index 0000000000..29d5d982d7
--- /dev/null
+++ b/gfx/gl/GLContextCGL.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#ifndef GLCONTEXTCGL_H_
+#define GLCONTEXTCGL_H_
+
+#include "GLContext.h"
+
+#include "OpenGL/OpenGL.h"
+
+#ifdef __OBJC__
+#include <AppKit/NSOpenGL.h>
+#else
+typedef void NSOpenGLContext;
+#endif
+
+namespace mozilla {
+namespace gl {
+
+class GLContextCGL : public GLContext
+{
+ friend class GLContextProviderCGL;
+
+ NSOpenGLContext* const mContext;
+
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GLContextCGL, override)
+ GLContextCGL(CreateContextFlags flags, const SurfaceCaps& caps,
+ NSOpenGLContext* context, bool isOffscreen, ContextProfile profile);
+
+ ~GLContextCGL();
+
+ virtual GLContextType GetContextType() const override { return GLContextType::CGL; }
+
+ static GLContextCGL* Cast(GLContext* gl) {
+ MOZ_ASSERT(gl->GetContextType() == GLContextType::CGL);
+ return static_cast<GLContextCGL*>(gl);
+ }
+
+ bool Init() override;
+
+ NSOpenGLContext* GetNSOpenGLContext() const { return mContext; }
+ CGLContextObj GetCGLContext() const;
+
+ virtual bool MakeCurrentImpl(bool aForce) override;
+
+ virtual bool IsCurrent() override;
+
+ virtual GLenum GetPreferredARGB32Format() const override;
+
+ virtual bool SetupLookupFunction() override;
+
+ virtual bool IsDoubleBuffered() const override;
+
+ virtual bool SupportsRobustness() const override { return false; }
+
+ virtual bool SwapBuffers() override;
+
+ virtual void GetWSIInfo(nsCString* const out) const override;
+};
+
+} // namespace gl
+} // namespace mozilla
+
+#endif // GLCONTEXTCGL_H_
diff --git a/gfx/gl/GLContextEAGL.h b/gfx/gl/GLContextEAGL.h
new file mode 100644
index 0000000000..f677d23d57
--- /dev/null
+++ b/gfx/gl/GLContextEAGL.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#ifndef GLCONTEXTEAGL_H_
+#define GLCONTEXTEAGL_H_
+
+#include "GLContext.h"
+
+#include <CoreGraphics/CoreGraphics.h>
+#include <OpenGLES/EAGL.h>
+
+namespace mozilla {
+namespace gl {
+
+class GLContextEAGL : public GLContext
+{
+ friend class GLContextProviderEAGL;
+
+ EAGLContext* const mContext;
+
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GLContextEAGL, override)
+ GLContextEAGL(CreateContextFlags flags, const SurfaceCaps& caps, EAGLContext* context,
+ GLContext* sharedContext, bool isOffscreen, ContextProfile profile);
+
+ ~GLContextEAGL();
+
+ virtual GLContextType GetContextType() const override {
+ return GLContextType::EAGL;
+ }
+
+ static GLContextEAGL* Cast(GLContext* gl) {
+ MOZ_ASSERT(gl->GetContextType() == GLContextType::EAGL);
+ return static_cast<GLContextEAGL*>(gl);
+ }
+
+ bool Init() override;
+
+ bool AttachToWindow(nsIWidget* aWidget);
+
+ EAGLContext* GetEAGLContext() const { return mContext; }
+
+ virtual bool MakeCurrentImpl(bool aForce) override;
+
+ virtual bool IsCurrent() override;
+
+ virtual bool SetupLookupFunction() override;
+
+ virtual bool IsDoubleBuffered() const override;
+
+ virtual bool SupportsRobustness() const override { return false; }
+
+ virtual bool SwapBuffers() override;
+
+ virtual void GetWSIInfo(nsCString* const out) const override;
+
+ virtual GLuint GetDefaultFramebuffer() override {
+ return mBackbufferFB;
+ }
+
+ virtual bool RenewSurface(nsIWidget* aWidget) override {
+ // FIXME: should use the passed widget instead of the existing one.
+ return RecreateRB();
+ }
+
+private:
+ GLuint mBackbufferRB;
+ GLuint mBackbufferFB;
+
+ void* mLayer;
+
+ bool RecreateRB();
+};
+
+} // namespace gl
+} // namespace mozilla
+
+#endif // GLCONTEXTEAGL_H_
diff --git a/gfx/gl/GLContextProviderCGL.mm b/gfx/gl/GLContextProviderCGL.mm
new file mode 100644
index 0000000000..7cc89eac9f
--- /dev/null
+++ b/gfx/gl/GLContextProviderCGL.mm
@@ -0,0 +1,397 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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/. */
+
+#include "GLContextProvider.h"
+#include "GLContextCGL.h"
+#include "nsDebug.h"
+#include "nsIWidget.h"
+#include <OpenGL/gl.h>
+#include "gfxFailure.h"
+#include "gfxPrefs.h"
+#include "prenv.h"
+#include "GeckoProfiler.h"
+#include "mozilla/gfx/MacIOSurface.h"
+#include "mozilla/widget/CompositorWidget.h"
+
+#include <OpenGL/OpenGL.h>
+
+// When running inside a VM, creating an accelerated OpenGL context usually
+// fails. Uncomment this line to emulate that behavior.
+// #define EMULATE_VM
+
+namespace mozilla {
+namespace gl {
+
+using namespace mozilla::gfx;
+using namespace mozilla::widget;
+
+class CGLLibrary
+{
+public:
+ CGLLibrary()
+ : mInitialized(false)
+ , mUseDoubleBufferedWindows(true)
+ , mOGLLibrary(nullptr)
+ {}
+
+ bool EnsureInitialized()
+ {
+ if (mInitialized) {
+ return true;
+ }
+ if (!mOGLLibrary) {
+ mOGLLibrary = PR_LoadLibrary("/System/Library/Frameworks/OpenGL.framework/OpenGL");
+ if (!mOGLLibrary) {
+ NS_WARNING("Couldn't load OpenGL Framework.");
+ return false;
+ }
+ }
+
+ const char* db = PR_GetEnv("MOZ_CGL_DB");
+ if (db) {
+ mUseDoubleBufferedWindows = *db != '0';
+ }
+
+ mInitialized = true;
+ return true;
+ }
+
+ bool UseDoubleBufferedWindows() const {
+ MOZ_ASSERT(mInitialized);
+ return mUseDoubleBufferedWindows;
+ }
+
+private:
+ bool mInitialized;
+ bool mUseDoubleBufferedWindows;
+ PRLibrary* mOGLLibrary;
+};
+
+CGLLibrary sCGLLibrary;
+
+GLContextCGL::GLContextCGL(CreateContextFlags flags, const SurfaceCaps& caps,
+ NSOpenGLContext* context, bool isOffscreen,
+ ContextProfile profile)
+ : GLContext(flags, caps, nullptr, isOffscreen)
+ , mContext(context)
+{
+ SetProfileVersion(profile, 210);
+}
+
+GLContextCGL::~GLContextCGL()
+{
+ MarkDestroyed();
+ [mContext release];
+}
+
+bool
+GLContextCGL::Init()
+{
+ return InitWithPrefix("gl", true);
+}
+
+CGLContextObj
+GLContextCGL::GetCGLContext() const
+{
+ return static_cast<CGLContextObj>([mContext CGLContextObj]);
+}
+
+bool
+GLContextCGL::MakeCurrentImpl(bool aForce)
+{
+ if (IsDestroyed()) {
+ [NSOpenGLContext clearCurrentContext];
+ return false;
+ }
+
+ if (!aForce && [NSOpenGLContext currentContext] == mContext) {
+ return true;
+ }
+
+ if (mContext) {
+ [mContext makeCurrentContext];
+ MOZ_ASSERT(IsCurrent());
+ // Use non-blocking swap in "ASAP mode".
+ // ASAP mode means that rendering is iterated as fast as possible.
+ // ASAP mode is entered when layout.frame_rate=0 (requires restart).
+ // If swapInt is 1, then glSwapBuffers will block and wait for a vblank signal.
+ // When we're iterating as fast as possible, however, we want a non-blocking
+ // glSwapBuffers, which will happen when swapInt==0.
+ GLint swapInt = gfxPrefs::LayoutFrameRate() == 0 ? 0 : 1;
+ [mContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
+ }
+ return true;
+}
+
+bool
+GLContextCGL::IsCurrent() {
+ return [NSOpenGLContext currentContext] == mContext;
+}
+
+GLenum
+GLContextCGL::GetPreferredARGB32Format() const
+{
+ return LOCAL_GL_BGRA;
+}
+
+bool
+GLContextCGL::SetupLookupFunction()
+{
+ return false;
+}
+
+bool
+GLContextCGL::IsDoubleBuffered() const
+{
+ return sCGLLibrary.UseDoubleBufferedWindows();
+}
+
+bool
+GLContextCGL::SwapBuffers()
+{
+ PROFILER_LABEL("GLContextCGL", "SwapBuffers",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ [mContext flushBuffer];
+ return true;
+}
+
+void
+GLContextCGL::GetWSIInfo(nsCString* const out) const
+{
+ out->AppendLiteral("CGL");
+}
+
+already_AddRefed<GLContext>
+GLContextProviderCGL::CreateWrappingExisting(void*, void*)
+{
+ return nullptr;
+}
+
+static const NSOpenGLPixelFormatAttribute kAttribs_singleBuffered[] = {
+ NSOpenGLPFAAllowOfflineRenderers,
+ 0
+};
+
+static const NSOpenGLPixelFormatAttribute kAttribs_singleBuffered_accel[] = {
+ NSOpenGLPFAAccelerated,
+ NSOpenGLPFAAllowOfflineRenderers,
+ 0
+};
+
+static const NSOpenGLPixelFormatAttribute kAttribs_doubleBuffered[] = {
+ NSOpenGLPFAAllowOfflineRenderers,
+ NSOpenGLPFADoubleBuffer,
+ 0
+};
+
+static const NSOpenGLPixelFormatAttribute kAttribs_doubleBuffered_accel[] = {
+ NSOpenGLPFAAccelerated,
+ NSOpenGLPFAAllowOfflineRenderers,
+ NSOpenGLPFADoubleBuffer,
+ 0
+};
+
+static const NSOpenGLPixelFormatAttribute kAttribs_offscreen[] = {
+ 0
+};
+
+static const NSOpenGLPixelFormatAttribute kAttribs_offscreen_allow_offline[] = {
+ NSOpenGLPFAAllowOfflineRenderers,
+ 0
+};
+
+static const NSOpenGLPixelFormatAttribute kAttribs_offscreen_accel[] = {
+ NSOpenGLPFAAccelerated,
+ 0
+};
+
+static const NSOpenGLPixelFormatAttribute kAttribs_offscreen_coreProfile[] = {
+ NSOpenGLPFAAccelerated,
+ NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
+ 0
+};
+
+static NSOpenGLContext*
+CreateWithFormat(const NSOpenGLPixelFormatAttribute* attribs)
+{
+ NSOpenGLPixelFormat* format = [[NSOpenGLPixelFormat alloc]
+ initWithAttributes:attribs];
+ if (!format) {
+ NS_WARNING("Failed to create NSOpenGLPixelFormat.");
+ return nullptr;
+ }
+
+ NSOpenGLContext* context = [[NSOpenGLContext alloc] initWithFormat:format
+ shareContext:nullptr];
+
+ [format release];
+
+ return context;
+}
+
+already_AddRefed<GLContext>
+GLContextProviderCGL::CreateForCompositorWidget(CompositorWidget* aCompositorWidget, bool aForceAccelerated)
+{
+ return CreateForWindow(aCompositorWidget->RealWidget(), aForceAccelerated);
+}
+
+already_AddRefed<GLContext>
+GLContextProviderCGL::CreateForWindow(nsIWidget* aWidget, bool aForceAccelerated)
+{
+ if (!sCGLLibrary.EnsureInitialized()) {
+ return nullptr;
+ }
+
+#ifdef EMULATE_VM
+ if (aForceAccelerated) {
+ return nullptr;
+ }
+#endif
+
+ const NSOpenGLPixelFormatAttribute* attribs;
+ if (sCGLLibrary.UseDoubleBufferedWindows()) {
+ attribs = aForceAccelerated ? kAttribs_doubleBuffered_accel : kAttribs_doubleBuffered;
+ } else {
+ attribs = aForceAccelerated ? kAttribs_singleBuffered_accel : kAttribs_singleBuffered;
+ }
+ NSOpenGLContext* context = CreateWithFormat(attribs);
+ if (!context) {
+ return nullptr;
+ }
+
+ // make the context transparent
+ GLint opaque = 0;
+ [context setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity];
+
+ SurfaceCaps caps = SurfaceCaps::ForRGBA();
+ ContextProfile profile = ContextProfile::OpenGLCompatibility;
+ RefPtr<GLContextCGL> glContext = new GLContextCGL(CreateContextFlags::NONE, caps,
+ context, false, profile);
+
+ if (!glContext->Init()) {
+ glContext = nullptr;
+ [context release];
+ return nullptr;
+ }
+
+ return glContext.forget();
+}
+
+static already_AddRefed<GLContextCGL>
+CreateOffscreenFBOContext(CreateContextFlags flags)
+{
+ if (!sCGLLibrary.EnsureInitialized()) {
+ return nullptr;
+ }
+
+ ContextProfile profile;
+ NSOpenGLContext* context = nullptr;
+
+ if (!(flags & CreateContextFlags::REQUIRE_COMPAT_PROFILE)) {
+ profile = ContextProfile::OpenGLCore;
+ context = CreateWithFormat(kAttribs_offscreen_coreProfile);
+ }
+ if (!context) {
+ profile = ContextProfile::OpenGLCompatibility;
+
+ if (flags & CreateContextFlags::ALLOW_OFFLINE_RENDERER) {
+ if (gfxPrefs::RequireHardwareGL())
+ context = CreateWithFormat(kAttribs_singleBuffered);
+ else
+ context = CreateWithFormat(kAttribs_offscreen_allow_offline);
+
+ } else {
+ if (gfxPrefs::RequireHardwareGL())
+ context = CreateWithFormat(kAttribs_offscreen_accel);
+ else
+ context = CreateWithFormat(kAttribs_offscreen);
+ }
+ }
+ if (!context) {
+ NS_WARNING("Failed to create NSOpenGLContext.");
+ return nullptr;
+ }
+
+ SurfaceCaps dummyCaps = SurfaceCaps::Any();
+ RefPtr<GLContextCGL> glContext = new GLContextCGL(flags, dummyCaps, context, true,
+ profile);
+
+ if (gfxPrefs::GLMultithreaded()) {
+ CGLEnable(glContext->GetCGLContext(), kCGLCEMPEngine);
+ }
+ return glContext.forget();
+}
+
+already_AddRefed<GLContext>
+GLContextProviderCGL::CreateHeadless(CreateContextFlags flags,
+ nsACString* const out_failureId)
+{
+ RefPtr<GLContextCGL> gl;
+ gl = CreateOffscreenFBOContext(flags);
+ if (!gl) {
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_CGL_FBO");
+ return nullptr;
+ }
+
+ if (!gl->Init()) {
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_CGL_INIT");
+ NS_WARNING("Failed during Init.");
+ return nullptr;
+ }
+
+ return gl.forget();
+}
+
+already_AddRefed<GLContext>
+GLContextProviderCGL::CreateOffscreen(const IntSize& size,
+ const SurfaceCaps& minCaps,
+ CreateContextFlags flags,
+ nsACString* const out_failureId)
+{
+ RefPtr<GLContext> gl = CreateHeadless(flags, out_failureId);
+ if (!gl) {
+ return nullptr;
+ }
+
+ if (!gl->InitOffscreen(size, minCaps)) {
+ *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_CGL_INIT");
+ return nullptr;
+ }
+
+ return gl.forget();
+}
+
+static RefPtr<GLContext> gGlobalContext;
+
+GLContext*
+GLContextProviderCGL::GetGlobalContext()
+{
+ static bool triedToCreateContext = false;
+ if (!triedToCreateContext) {
+ triedToCreateContext = true;
+
+ MOZ_RELEASE_ASSERT(!gGlobalContext);
+ nsCString discardFailureId;
+ RefPtr<GLContext> temp = CreateHeadless(CreateContextFlags::NONE,
+ &discardFailureId);
+ gGlobalContext = temp;
+
+ if (!gGlobalContext) {
+ NS_WARNING("Couldn't init gGlobalContext.");
+ }
+ }
+
+ return gGlobalContext;
+}
+
+void
+GLContextProviderCGL::Shutdown()
+{
+ gGlobalContext = nullptr;
+}
+
+} /* namespace gl */
+} /* namespace mozilla */
diff --git a/gfx/gl/GLContextProviderEAGL.mm b/gfx/gl/GLContextProviderEAGL.mm
new file mode 100644
index 0000000000..11c7cce776
--- /dev/null
+++ b/gfx/gl/GLContextProviderEAGL.mm
@@ -0,0 +1,275 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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/. */
+
+#include "GLContextProvider.h"
+#include "GLContextEAGL.h"
+#include "nsDebug.h"
+#include "nsIWidget.h"
+#include "gfxPrefs.h"
+#include "gfxFailure.h"
+#include "prenv.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/widget/CompositorWidget.h"
+#include "GeckoProfiler.h"
+
+#import <UIKit/UIKit.h>
+
+namespace mozilla {
+namespace gl {
+
+using namespace mozilla::widget;
+
+GLContextEAGL::GLContextEAGL(CreateContextFlags flags, const SurfaceCaps& caps,
+ EAGLContext* context, GLContext* sharedContext,
+ bool isOffscreen, ContextProfile profile)
+ : GLContext(flags, caps, sharedContext, isOffscreen)
+ , mContext(context)
+ , mBackbufferRB(0)
+ , mBackbufferFB(0)
+ , mLayer(nil)
+{
+ SetProfileVersion(ContextProfile::OpenGLES,
+ [context API] == kEAGLRenderingAPIOpenGLES3 ? 300 : 200);
+}
+
+GLContextEAGL::~GLContextEAGL()
+{
+ if (MakeCurrent()) {
+ if (mBackbufferFB) {
+ fDeleteFramebuffers(1, &mBackbufferFB);
+ }
+ if (mBackbufferRB) {
+ fDeleteRenderbuffers(1, &mBackbufferRB);
+ }
+ }
+
+ mLayer = nil;
+ MarkDestroyed();
+ [mContext release];
+}
+
+bool
+GLContextEAGL::Init()
+{
+ return InitWithPrefix("gl", true);
+}
+
+bool
+GLContextEAGL::AttachToWindow(nsIWidget* aWidget)
+{
+ // This should only be called once
+ MOZ_ASSERT(!mBackbufferFB && !mBackbufferRB);
+
+ UIView* view =
+ reinterpret_cast<UIView*>(aWidget->GetNativeData(NS_NATIVE_WIDGET));
+
+ if (!view) {
+ MOZ_CRASH("no view!");
+ }
+
+ mLayer = [view layer];
+
+ fGenFramebuffers(1, &mBackbufferFB);
+ return RecreateRB();
+}
+
+bool
+GLContextEAGL::RecreateRB()
+{
+ MakeCurrent();
+
+ CAEAGLLayer* layer = (CAEAGLLayer*)mLayer;
+
+ if (mBackbufferRB) {
+ // It doesn't seem to be enough to just call renderbufferStorage: below,
+ // we apparently have to recreate the RB.
+ fDeleteRenderbuffers(1, &mBackbufferRB);
+ mBackbufferRB = 0;
+ }
+
+ fGenRenderbuffers(1, &mBackbufferRB);
+ fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mBackbufferRB);
+
+ [mContext renderbufferStorage:LOCAL_GL_RENDERBUFFER
+ fromDrawable:layer];
+
+ fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mBackbufferFB);
+ fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
+ LOCAL_GL_RENDERBUFFER, mBackbufferRB);
+
+ return LOCAL_GL_FRAMEBUFFER_COMPLETE == fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+}
+
+bool
+GLContextEAGL::MakeCurrentImpl(bool aForce)
+{
+ if (!aForce && [EAGLContext currentContext] == mContext) {
+ return true;
+ }
+
+ if (IsDestroyed()) {
+ [EAGLContext setCurrentContext:nil];
+ return false;
+ }
+
+ return [EAGLContext setCurrentContext:mContext];
+}
+
+bool
+GLContextEAGL::IsCurrent() {
+ return [EAGLContext currentContext] == mContext;
+}
+
+bool
+GLContextEAGL::SetupLookupFunction()
+{
+ return false;
+}
+
+bool
+GLContextEAGL::IsDoubleBuffered() const
+{
+ return true;
+}
+
+bool
+GLContextEAGL::SwapBuffers()
+{
+ PROFILER_LABEL("GLContextEAGL", "SwapBuffers",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ [mContext presentRenderbuffer:LOCAL_GL_RENDERBUFFER];
+ return true;
+}
+
+void
+GLContextEAGL::GetWSIInfo(nsCString* const out) const
+{
+ out->AppendLiteral("EAGL");
+}
+
+already_AddRefed<GLContext>
+GLContextProviderEAGL::CreateWrappingExisting(void*, void*)
+{
+ return nullptr;
+}
+
+static GLContextEAGL*
+GetGlobalContextEAGL()
+{
+ return static_cast<GLContextEAGL*>(GLContextProviderEAGL::GetGlobalContext());
+}
+
+static already_AddRefed<GLContext>
+CreateEAGLContext(CreateContextFlags flags, bool aOffscreen, GLContextEAGL* sharedContext)
+{
+ EAGLRenderingAPI apis[] = { kEAGLRenderingAPIOpenGLES3, kEAGLRenderingAPIOpenGLES2 };
+
+ // Try to create a GLES3 context if we can, otherwise fall back to GLES2
+ EAGLContext* context = nullptr;
+ for (EAGLRenderingAPI api : apis) {
+ if (sharedContext) {
+ context = [[EAGLContext alloc] initWithAPI:api
+ sharegroup:sharedContext->GetEAGLContext().sharegroup];
+ } else {
+ context = [[EAGLContext alloc] initWithAPI:api];
+ }
+
+ if (context) {
+ break;
+ }
+ }
+
+ if (!context) {
+ return nullptr;
+ }
+
+ SurfaceCaps caps = SurfaceCaps::ForRGBA();
+ ContextProfile profile = ContextProfile::OpenGLES;
+ RefPtr<GLContextEAGL> glContext = new GLContextEAGL(flags, caps, context,
+ sharedContext,
+ aOffscreen,
+ profile);
+
+ if (!glContext->Init()) {
+ glContext = nullptr;
+ return nullptr;
+ }
+
+ return glContext.forget();
+}
+
+already_AddRefed<GLContext>
+GLContextProviderEAGL::CreateForCompositorWidget(CompositorWidget* aCompositorWidget, bool aForceAccelerated)
+{
+ return CreateForWindow(aCompositorWidget->RealWidget(), aForceAccelerated);
+}
+
+already_AddRefed<GLContext>
+GLContextProviderEAGL::CreateForWindow(nsIWidget* aWidget, bool aForceAccelerated)
+{
+ RefPtr<GLContext> glContext = CreateEAGLContext(CreateContextFlags::NONE, false,
+ GetGlobalContextEAGL());
+ if (!glContext) {
+ return nullptr;
+ }
+
+ if (!GLContextEAGL::Cast(glContext)->AttachToWindow(aWidget)) {
+ return nullptr;
+ }
+
+ return glContext.forget();
+}
+
+already_AddRefed<GLContext>
+GLContextProviderEAGL::CreateHeadless(CreateContextFlags flags,
+ nsACString* const out_failureId)
+{
+ return CreateEAGLContext(flags, true, GetGlobalContextEAGL());
+}
+
+already_AddRefed<GLContext>
+GLContextProviderEAGL::CreateOffscreen(const mozilla::gfx::IntSize& size,
+ const SurfaceCaps& caps,
+ CreateContextFlags flags,
+ nsACString* const out_failureId)
+{
+ RefPtr<GLContext> glContext = CreateHeadless(flags, out_failureId);
+ if (!glContext->InitOffscreen(size, caps)) {
+ return nullptr;
+ }
+
+ return glContext.forget();
+}
+
+static RefPtr<GLContext> gGlobalContext;
+
+GLContext*
+GLContextProviderEAGL::GetGlobalContext()
+{
+ static bool triedToCreateContext = false;
+ if (!triedToCreateContext) {
+ triedToCreateContext = true;
+
+ MOZ_RELEASE_ASSERT(!gGlobalContext, "GFX: Global GL context already initialized.");
+ RefPtr<GLContext> temp = CreateHeadless(CreateContextFlags::NONE);
+ gGlobalContext = temp;
+
+ if (!gGlobalContext) {
+ MOZ_CRASH("Failed to create global context");
+ }
+ }
+
+ return gGlobalContext;
+}
+
+void
+GLContextProviderEAGL::Shutdown()
+{
+ gGlobalContext = nullptr;
+}
+
+} /* namespace gl */
+} /* namespace mozilla */
diff --git a/gfx/gl/SharedSurfaceIO.cpp b/gfx/gl/SharedSurfaceIO.cpp
new file mode 100644
index 0000000000..50262d6cce
--- /dev/null
+++ b/gfx/gl/SharedSurfaceIO.cpp
@@ -0,0 +1,248 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* 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/. */
+
+#include "SharedSurfaceIO.h"
+
+#include "GLContextCGL.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/gfx/MacIOSurface.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
+#include "ScopedGLHelpers.h"
+
+namespace mozilla {
+namespace gl {
+
+/*static*/ UniquePtr<SharedSurface_IOSurface>
+SharedSurface_IOSurface::Create(const RefPtr<MacIOSurface>& ioSurf,
+ GLContext* gl,
+ bool hasAlpha)
+{
+ MOZ_ASSERT(ioSurf);
+ MOZ_ASSERT(gl);
+
+ auto size = gfx::IntSize::Truncate(ioSurf->GetWidth(), ioSurf->GetHeight());
+
+ typedef SharedSurface_IOSurface ptrT;
+ UniquePtr<ptrT> ret( new ptrT(ioSurf, gl, size, hasAlpha) );
+ return Move(ret);
+}
+
+void
+SharedSurface_IOSurface::ProducerReleaseImpl()
+{
+ mGL->MakeCurrent();
+ mGL->fFlush();
+}
+
+bool
+SharedSurface_IOSurface::CopyTexImage2D(GLenum target, GLint level, GLenum internalformat,
+ GLint x, GLint y, GLsizei width, GLsizei height,
+ GLint border)
+{
+ /* Bug 896693 - OpenGL framebuffers that are backed by IOSurface on OSX expose a bug
+ * in glCopyTexImage2D --- internalformats GL_ALPHA, GL_LUMINANCE, GL_LUMINANCE_ALPHA
+ * return the wrong results. To work around, copy framebuffer to a temporary texture
+ * using GL_RGBA (which works), attach as read framebuffer and glCopyTexImage2D
+ * instead.
+ */
+
+ // https://www.opengl.org/sdk/docs/man3/xhtml/glCopyTexImage2D.xml says that width or
+ // height set to 0 results in a NULL texture. Lets not do any work and punt to
+ // original glCopyTexImage2D, since the FBO below will fail when trying to attach a
+ // texture of 0 width or height.
+ if (width == 0 || height == 0)
+ return false;
+
+ switch (internalformat) {
+ case LOCAL_GL_ALPHA:
+ case LOCAL_GL_LUMINANCE:
+ case LOCAL_GL_LUMINANCE_ALPHA:
+ break;
+
+ default:
+ return false;
+ }
+
+ MOZ_ASSERT(mGL->IsCurrent());
+
+ ScopedTexture destTex(mGL);
+ {
+ ScopedBindTexture bindTex(mGL, destTex.Texture());
+ mGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER,
+ LOCAL_GL_NEAREST);
+ mGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER,
+ LOCAL_GL_NEAREST);
+ mGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S,
+ LOCAL_GL_CLAMP_TO_EDGE);
+ mGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T,
+ LOCAL_GL_CLAMP_TO_EDGE);
+ mGL->raw_fCopyTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, x, y, width,
+ height, 0);
+ }
+
+ ScopedFramebufferForTexture tmpFB(mGL, destTex.Texture(), LOCAL_GL_TEXTURE_2D);
+ ScopedBindFramebuffer bindFB(mGL, tmpFB.FB());
+ mGL->raw_fCopyTexImage2D(target, level, internalformat, x, y, width, height, border);
+
+ return true;
+}
+
+bool
+SharedSurface_IOSurface::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
+ GLenum format, GLenum type, GLvoid* pixels)
+{
+ // Calling glReadPixels when an IOSurface is bound to the current framebuffer
+ // can cause corruption in following glReadPixel calls (even if they aren't
+ // reading from an IOSurface).
+ // We workaround this by copying to a temporary texture, and doing the readback
+ // from that.
+ MOZ_ASSERT(mGL->IsCurrent());
+
+ ScopedTexture destTex(mGL);
+ {
+ ScopedFramebufferForTexture srcFB(mGL, ProdTexture(), ProdTextureTarget());
+
+ ScopedBindFramebuffer bindFB(mGL, srcFB.FB());
+ ScopedBindTexture bindTex(mGL, destTex.Texture());
+ mGL->raw_fCopyTexImage2D(LOCAL_GL_TEXTURE_2D, 0,
+ mHasAlpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB,
+ x, y,
+ width, height, 0);
+ }
+
+ ScopedFramebufferForTexture destFB(mGL, destTex.Texture());
+
+ ScopedBindFramebuffer bindFB(mGL, destFB.FB());
+ mGL->raw_fReadPixels(0, 0, width, height, format, type, pixels);
+ return true;
+}
+
+static void
+BackTextureWithIOSurf(GLContext* gl, GLuint tex, MacIOSurface* ioSurf)
+{
+ MOZ_ASSERT(gl->IsCurrent());
+
+ ScopedBindTexture texture(gl, tex, LOCAL_GL_TEXTURE_RECTANGLE_ARB);
+
+ gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB,
+ LOCAL_GL_TEXTURE_MIN_FILTER,
+ LOCAL_GL_LINEAR);
+ gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB,
+ LOCAL_GL_TEXTURE_MAG_FILTER,
+ LOCAL_GL_LINEAR);
+ gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB,
+ LOCAL_GL_TEXTURE_WRAP_S,
+ LOCAL_GL_CLAMP_TO_EDGE);
+ gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB,
+ LOCAL_GL_TEXTURE_WRAP_T,
+ LOCAL_GL_CLAMP_TO_EDGE);
+
+ CGLContextObj cgl = GLContextCGL::Cast(gl)->GetCGLContext();
+ MOZ_ASSERT(cgl);
+
+ ioSurf->CGLTexImageIOSurface2D(cgl);
+}
+
+SharedSurface_IOSurface::SharedSurface_IOSurface(const RefPtr<MacIOSurface>& ioSurf,
+ GLContext* gl,
+ const gfx::IntSize& size,
+ bool hasAlpha)
+ : SharedSurface(SharedSurfaceType::IOSurface,
+ AttachmentType::GLTexture,
+ gl,
+ size,
+ hasAlpha,
+ true)
+ , mIOSurf(ioSurf)
+{
+ gl->MakeCurrent();
+ mProdTex = 0;
+ gl->fGenTextures(1, &mProdTex);
+ BackTextureWithIOSurf(gl, mProdTex, mIOSurf);
+}
+
+SharedSurface_IOSurface::~SharedSurface_IOSurface()
+{
+ if (!mGL || !mGL->MakeCurrent())
+ return;
+
+ mGL->fDeleteTextures(1, &mProdTex);
+}
+
+bool
+SharedSurface_IOSurface::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor)
+{
+ bool isOpaque = !mHasAlpha;
+ *out_descriptor = layers::SurfaceDescriptorMacIOSurface(mIOSurf->GetIOSurfaceID(),
+ mIOSurf->GetContentsScaleFactor(),
+ isOpaque);
+ return true;
+}
+
+bool
+SharedSurface_IOSurface::ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface)
+{
+ MOZ_ASSERT(out_surface);
+ mIOSurf->Lock();
+ size_t bytesPerRow = mIOSurf->GetBytesPerRow();
+ size_t ioWidth = mIOSurf->GetDevicePixelWidth();
+ size_t ioHeight = mIOSurf->GetDevicePixelHeight();
+
+ const unsigned char* ioData = (unsigned char*)mIOSurf->GetBaseAddress();
+ gfx::DataSourceSurface::ScopedMap map(out_surface, gfx::DataSourceSurface::WRITE);
+ if (!map.IsMapped()) {
+ mIOSurf->Unlock();
+ return false;
+ }
+
+ for (size_t i = 0; i < ioHeight; i++) {
+ memcpy(map.GetData() + i * map.GetStride(),
+ ioData + i * bytesPerRow, ioWidth * 4);
+ }
+
+ mIOSurf->Unlock();
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////
+// SurfaceFactory_IOSurface
+
+/*static*/ UniquePtr<SurfaceFactory_IOSurface>
+SurfaceFactory_IOSurface::Create(GLContext* gl, const SurfaceCaps& caps,
+ const RefPtr<layers::LayersIPCChannel>& allocator,
+ const layers::TextureFlags& flags)
+{
+ auto maxDims = gfx::IntSize::Truncate(MacIOSurface::GetMaxWidth(),
+ MacIOSurface::GetMaxHeight());
+
+ typedef SurfaceFactory_IOSurface ptrT;
+ UniquePtr<ptrT> ret( new ptrT(gl, caps, allocator, flags, maxDims) );
+ return Move(ret);
+}
+
+UniquePtr<SharedSurface>
+SurfaceFactory_IOSurface::CreateShared(const gfx::IntSize& size)
+{
+ if (size.width > mMaxDims.width ||
+ size.height > mMaxDims.height)
+ {
+ return nullptr;
+ }
+
+ bool hasAlpha = mReadCaps.alpha;
+ RefPtr<MacIOSurface> ioSurf;
+ ioSurf = MacIOSurface::CreateIOSurface(size.width, size.height, 1.0,
+ hasAlpha);
+
+ if (!ioSurf) {
+ NS_WARNING("Failed to create MacIOSurface.");
+ return nullptr;
+ }
+
+ return SharedSurface_IOSurface::Create(ioSurf, mGL, hasAlpha);
+}
+
+} // namespace gl
+} // namespace mozilla
diff --git a/gfx/gl/SharedSurfaceIO.h b/gfx/gl/SharedSurfaceIO.h
new file mode 100644
index 0000000000..16e0cbbb7f
--- /dev/null
+++ b/gfx/gl/SharedSurfaceIO.h
@@ -0,0 +1,100 @@
+/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
+/* 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/. */
+
+#ifndef SHARED_SURFACEIO_H_
+#define SHARED_SURFACEIO_H_
+
+#include "mozilla/RefPtr.h"
+#include "SharedSurface.h"
+
+class MacIOSurface;
+
+namespace mozilla {
+namespace gl {
+
+class SharedSurface_IOSurface : public SharedSurface
+{
+private:
+ const RefPtr<MacIOSurface> mIOSurf;
+ GLuint mProdTex;
+
+public:
+ static UniquePtr<SharedSurface_IOSurface> Create(const RefPtr<MacIOSurface>& ioSurf,
+ GLContext* gl,
+ bool hasAlpha);
+
+private:
+ SharedSurface_IOSurface(const RefPtr<MacIOSurface>& ioSurf,
+ GLContext* gl, const gfx::IntSize& size,
+ bool hasAlpha);
+
+public:
+ ~SharedSurface_IOSurface();
+
+ virtual void LockProdImpl() override { }
+ virtual void UnlockProdImpl() override { }
+
+ virtual void ProducerAcquireImpl() override {}
+ virtual void ProducerReleaseImpl() override;
+
+ virtual bool CopyTexImage2D(GLenum target, GLint level, GLenum internalformat,
+ GLint x, GLint y, GLsizei width, GLsizei height,
+ GLint border) override;
+ virtual bool ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
+ GLenum format, GLenum type, GLvoid* pixels) override;
+
+ virtual GLuint ProdTexture() override {
+ return mProdTex;
+ }
+
+ virtual GLenum ProdTextureTarget() const override {
+ return LOCAL_GL_TEXTURE_RECTANGLE_ARB;
+ }
+
+ static SharedSurface_IOSurface* Cast(SharedSurface* surf) {
+ MOZ_ASSERT(surf->mType == SharedSurfaceType::IOSurface);
+ return static_cast<SharedSurface_IOSurface*>(surf);
+ }
+
+ MacIOSurface* GetIOSurface() const {
+ return mIOSurf;
+ }
+
+ virtual bool NeedsIndirectReads() const override {
+ return true;
+ }
+
+ virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
+
+ virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) override;
+};
+
+class SurfaceFactory_IOSurface : public SurfaceFactory
+{
+public:
+ // Infallible.
+ static UniquePtr<SurfaceFactory_IOSurface> Create(GLContext* gl,
+ const SurfaceCaps& caps,
+ const RefPtr<layers::LayersIPCChannel>& allocator,
+ const layers::TextureFlags& flags);
+protected:
+ const gfx::IntSize mMaxDims;
+
+ SurfaceFactory_IOSurface(GLContext* gl, const SurfaceCaps& caps,
+ const RefPtr<layers::LayersIPCChannel>& allocator,
+ const layers::TextureFlags& flags,
+ const gfx::IntSize& maxDims)
+ : SurfaceFactory(SharedSurfaceType::IOSurface, gl, caps, allocator, flags)
+ , mMaxDims(maxDims)
+ { }
+
+ virtual UniquePtr<SharedSurface> CreateShared(const gfx::IntSize& size) override;
+};
+
+} // namespace gl
+
+} /* namespace mozilla */
+
+#endif /* SHARED_SURFACEIO_H_ */
diff --git a/gfx/gl/moz.build b/gfx/gl/moz.build
index 7bf5a4ab04..c490400cbf 100644
--- a/gfx/gl/moz.build
+++ b/gfx/gl/moz.build
@@ -7,6 +7,10 @@ gl_provider = 'Null'
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
gl_provider = 'WGL'
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ gl_provider = 'CGL'
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'uikit':
+ gl_provider = 'EAGL'
elif 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
if CONFIG['MOZ_EGL_XRENDER_COMPOSITE']:
gl_provider = 'EGL'
@@ -19,6 +23,7 @@ if CONFIG['MOZ_GL_PROVIDER']:
EXPORTS += [
'DecomposeIntoNoRepeatTriangles.h',
'EGLUtils.h',
+ 'ForceDiscreteGPUHelperCGL.h',
'GfxTexturesReporter.h',
'GLBlitHelper.h',
'GLConsts.h',
@@ -73,7 +78,32 @@ if CONFIG['MOZ_ENABLE_SKIA_GPU']:
# Suppress warnings from Skia header files.
SOURCES['SkiaGLGlue.cpp'].flags += ['-Wno-implicit-fallthrough']
-if gl_provider == 'GLX':
+if gl_provider == 'CGL':
+ # These files include Mac headers that are unfriendly to unified builds
+ SOURCES += [
+ "GLContextProviderCGL.mm",
+ ]
+ EXPORTS += [
+ 'GLContextCGL.h',
+ 'SharedSurfaceIO.h',
+ ]
+ # SharedSurfaceIO.cpp includes MacIOSurface.h which include Mac headers
+ # which define Size and Point types in root namespace with often conflict with
+ # our own types. While I haven't actually hit this issue in the present case,
+ # it's been an issue in gfx/layers so let's not risk it.
+ SOURCES += [
+ 'SharedSurfaceIO.cpp',
+ ]
+elif gl_provider == 'EAGL':
+ # These files include ObjC headers that are unfriendly to unified builds
+ SOURCES += [
+ 'GLContextProviderEAGL.mm',
+ ]
+ EXPORTS += [
+ 'GLContextEAGL.h',
+ ]
+
+elif gl_provider == 'GLX':
# GLContextProviderGLX.cpp needs to be kept out of UNIFIED_SOURCES
# as it includes X11 headers which cause conflicts.
SOURCES += [
diff --git a/hal/cocoa/CocoaSensor.mm b/hal/cocoa/CocoaSensor.mm
new file mode 100644
index 0000000000..ad203073c3
--- /dev/null
+++ b/hal/cocoa/CocoaSensor.mm
@@ -0,0 +1,149 @@
+/* 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/. */
+#include "Hal.h"
+#include "nsITimer.h"
+#include "smslib.h"
+#include "nsComponentManagerUtils.h"
+
+#include <mach/mach.h>
+#include <cmath>
+#import <IOKit/IOKitLib.h>
+
+#define MEAN_GRAVITY 9.80665
+#define DEFAULT_SENSOR_POLL 100
+using namespace mozilla::hal;
+namespace mozilla {
+namespace hal_impl {
+static nsITimer* sUpdateTimer = nullptr;
+static bool sActiveSensors[NUM_SENSOR_TYPE];
+static io_connect_t sDataPort = IO_OBJECT_NULL;
+static uint64_t sLastMean = -1;
+static float
+LMUvalueToLux(uint64_t aValue)
+{
+ //Conversion formula from regression. See Bug 793728.
+ // -3*(10^-27)*x^4 + 2.6*(10^-19)*x^3 + -3.4*(10^-12)*x^2 + 3.9*(10^-5)*x - 0.19
+ long double powerC4 = 1/pow((long double)10,27);
+ long double powerC3 = 1/pow((long double)10,19);
+ long double powerC2 = 1/pow((long double)10,12);
+ long double powerC1 = 1/pow((long double)10,5);
+
+ long double term4 = -3.0 * powerC4 * pow(aValue,4);
+ long double term3 = 2.6 * powerC3 * pow(aValue,3);
+ long double term2 = -3.4 * powerC2 * pow(aValue,2);
+ long double term1 = 3.9 * powerC1 * aValue;
+
+ float lux = ceil(static_cast<float>(term4 + term3 + term2 + term1 - 0.19));
+ return lux > 0 ? lux : 0;
+}
+void
+UpdateHandler(nsITimer *aTimer, void *aClosure)
+{
+ for (int i = 0; i < NUM_SENSOR_TYPE; i++) {
+ if (!sActiveSensors[i]) {
+ continue;
+ }
+ SensorType sensor = static_cast<SensorType>(i);
+ InfallibleTArray<float> values;
+ if (sensor == SENSOR_ACCELERATION) {
+ sms_acceleration accel;
+ smsGetData(&accel);
+
+ values.AppendElement(accel.x * MEAN_GRAVITY);
+ values.AppendElement(accel.y * MEAN_GRAVITY);
+ values.AppendElement(accel.z * MEAN_GRAVITY);
+ } else if (sensor == SENSOR_LIGHT && sDataPort != IO_OBJECT_NULL) {
+ kern_return_t kr;
+ uint32_t outputs = 2;
+ uint64_t lightLMU[outputs];
+
+ kr = IOConnectCallMethod(sDataPort, 0, nil, 0, nil, 0, lightLMU, &outputs, nil, 0);
+ if (kr == KERN_SUCCESS) {
+ uint64_t mean = (lightLMU[0] + lightLMU[1]) / 2;
+ if (mean == sLastMean) {
+ continue;
+ }
+ sLastMean = mean;
+ values.AppendElement(LMUvalueToLux(mean));
+ } else if (kr == kIOReturnBusy) {
+ continue;
+ }
+ }
+
+ hal::SensorData sdata(sensor,
+ PR_Now(),
+ values,
+ hal::SENSOR_ACCURACY_UNKNOWN);
+ hal::NotifySensorChange(sdata);
+ }
+}
+void
+EnableSensorNotifications(SensorType aSensor)
+{
+ if (aSensor == SENSOR_ACCELERATION) {
+ int result = smsStartup(nil, nil);
+
+ if (result != SMS_SUCCESS) {
+ return;
+ }
+
+ if (!smsLoadCalibration()) {
+ return;
+ }
+ } else if (aSensor == SENSOR_LIGHT) {
+ io_service_t serviceObject;
+ serviceObject = IOServiceGetMatchingService(kIOMasterPortDefault,
+ IOServiceMatching("AppleLMUController"));
+ if (!serviceObject) {
+ return;
+ }
+ kern_return_t kr;
+ kr = IOServiceOpen(serviceObject, mach_task_self(), 0, &sDataPort);
+ IOObjectRelease(serviceObject);
+ if (kr != KERN_SUCCESS) {
+ return;
+ }
+ } else {
+ NS_WARNING("EnableSensorNotifications called on an unknown sensor type");
+ return;
+ }
+ sActiveSensors[aSensor] = true;
+
+ if (!sUpdateTimer) {
+ CallCreateInstance("@mozilla.org/timer;1", &sUpdateTimer);
+ if (sUpdateTimer) {
+ sUpdateTimer->InitWithFuncCallback(UpdateHandler,
+ nullptr,
+ DEFAULT_SENSOR_POLL,
+ nsITimer::TYPE_REPEATING_SLACK);
+ }
+ }
+}
+void
+DisableSensorNotifications(SensorType aSensor)
+{
+ if (!sActiveSensors[aSensor] || (aSensor != SENSOR_ACCELERATION && aSensor != SENSOR_LIGHT)) {
+ return;
+ }
+
+ sActiveSensors[aSensor] = false;
+
+ if (aSensor == SENSOR_ACCELERATION) {
+ smsShutdown();
+ } else if (aSensor == SENSOR_LIGHT) {
+ IOServiceClose(sDataPort);
+ }
+ // If all sensors are disabled, cancel the update timer.
+ if (sUpdateTimer) {
+ for (int i = 0; i < NUM_SENSOR_TYPE; i++) {
+ if (sActiveSensors[i]) {
+ return;
+ }
+ }
+ sUpdateTimer->Cancel();
+ NS_RELEASE(sUpdateTimer);
+ }
+}
+} // namespace hal_impl
+} // namespace mozilla
diff --git a/hal/cocoa/smslib.h b/hal/cocoa/smslib.h
new file mode 100644
index 0000000000..2f0b2664e4
--- /dev/null
+++ b/hal/cocoa/smslib.h
@@ -0,0 +1,159 @@
+/*
+ * smslib.h
+ *
+ * SMSLib Sudden Motion Sensor Access Library
+ * Copyright (c) 2010 Suitable Systems
+ * All rights reserved.
+ *
+ * Developed by: Daniel Griscom
+ * Suitable Systems
+ * http://www.suitable.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal with the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimers.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimers in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the names of Suitable Systems nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this Software without specific prior written permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+ *
+ * For more information about SMSLib, see
+ * <http://www.suitable.com/tools/smslib.html>
+ * or contact
+ * Daniel Griscom
+ * Suitable Systems
+ * 1 Centre Street, Suite 204
+ * Wakefield, MA 01880
+ * (781) 665-0053
+ *
+ */
+
+#import <Foundation/Foundation.h>
+
+#define SMSLIB_VERSION "1.8"
+
+#pragma mark Structure definitions
+
+// Structure for specifying a 3-axis acceleration. 0.0 means "zero gravities",
+// 1.0 means "one gravity".
+typedef struct sms_acceleration {
+ float x; // Right-left acceleration (positive is rightwards)
+ float y; // Front-rear acceleration (positive is rearwards)
+ float z; // Up-down acceleration (positive is upwards)
+} sms_acceleration;
+
+// Structure for specifying a calibration.
+typedef struct sms_calibration {
+ float zeros[3]; // Zero points for three axes (X, Y, Z)
+ float onegs[3]; // One gravity values for three axes
+} sms_calibration;
+
+#pragma mark Return value definitions
+
+// These are the return values for accelStartup(), giving the
+// various stages where the most successful attempt at accessing
+// the accelerometer failed. The higher the value, the further along the
+// software progressed before failing. The options are:
+// - Didn't match model name
+#define SMS_FAIL_MODEL (-7)
+// - Failure getting dictionary matching desired services
+#define SMS_FAIL_DICTIONARY (-6)
+// - Failure getting list of services
+#define SMS_FAIL_LIST_SERVICES (-5)
+// - Failure if list of services is empty. The process generally fails
+// here if run on a machine without a Sudden Motion Sensor.
+#define SMS_FAIL_NO_SERVICES (-4)
+// - Failure if error opening device.
+#define SMS_FAIL_OPENING (-3)
+// - Failure if opened, but didn't get a connection
+#define SMS_FAIL_CONNECTION (-2)
+// - Failure if couldn't access connction using given function and size. This
+// is where the process would probably fail with a change in Apple's API.
+// Driver problems often also cause failures here.
+#define SMS_FAIL_ACCESS (-1)
+// - Success!
+#define SMS_SUCCESS (0)
+
+#pragma mark Function declarations
+
+// This starts up the accelerometer code, trying each possible sensor
+// specification. Note that for logging purposes it
+// takes an object and a selector; the object's selector is then invoked
+// with a single NSString as argument giving progress messages. Example
+// logging method:
+// - (void)logMessage: (NSString *)theString
+// which would be used in accelStartup's invocation thusly:
+// result = accelStartup(self, @selector(logMessage:));
+// If the object is nil, then no logging is done. Sets calibation from built-in
+// value table. Returns ACCEL_SUCCESS for success, and other (negative)
+// values for various failures (returns value indicating result of
+// most successful trial).
+int smsStartup(id logObject, SEL logSelector);
+
+// This starts up the library in debug mode, ignoring the actual hardware.
+// Returned data is in the form of 1Hz sine waves, with the X, Y and Z
+// axes 120 degrees out of phase; "calibrated" data has range +/- (1.0/5);
+// "uncalibrated" data has range +/- (256/5). X and Y axes centered on 0.0,
+// Z axes centered on 1 (calibrated) or 256 (uncalibrated).
+// Don't use smsGetBufferLength or smsGetBufferData. Always returns SMS_SUCCESS.
+int smsDebugStartup(id logObject, SEL logSelector);
+
+// Returns the current calibration values.
+void smsGetCalibration(sms_calibration *calibrationRecord);
+
+// Sets the calibration, but does NOT store it as a preference. If the argument
+// is nil then the current calibration is set from the built-in value table.
+void smsSetCalibration(sms_calibration *calibrationRecord);
+
+// Stores the current calibration values as a stored preference.
+void smsStoreCalibration(void);
+
+// Loads the stored preference values into the current calibration.
+// Returns YES if successful.
+BOOL smsLoadCalibration(void);
+
+// Deletes any stored calibration, and then takes the current calibration values
+// from the built-in value table.
+void smsDeleteCalibration(void);
+
+// Fills in the accel record with calibrated acceleration data. Takes
+// 1-2ms to return a value. Returns 0 if success, error number if failure.
+int smsGetData(sms_acceleration *accel);
+
+// Fills in the accel record with uncalibrated acceleration data.
+// Returns 0 if success, error number if failure.
+int smsGetUncalibratedData(sms_acceleration *accel);
+
+// Returns the length of a raw block of data for the current type of sensor.
+int smsGetBufferLength(void);
+
+// Takes a pointer to accelGetRawLength() bytes; sets those bytes
+// to return value from sensor. Make darn sure the buffer length is right!
+void smsGetBufferData(char *buffer);
+
+// This returns an NSString describing the current calibration in
+// human-readable form. Also include a description of the machine.
+NSString *smsGetCalibrationDescription(void);
+
+// Shuts down the accelerometer.
+void smsShutdown(void);
+
diff --git a/hal/cocoa/smslib.mm b/hal/cocoa/smslib.mm
new file mode 100644
index 0000000000..c11c1e4d60
--- /dev/null
+++ b/hal/cocoa/smslib.mm
@@ -0,0 +1,938 @@
+/*
+ * smslib.m
+ *
+ * SMSLib Sudden Motion Sensor Access Library
+ * Copyright (c) 2010 Suitable Systems
+ * All rights reserved.
+ *
+ * Developed by: Daniel Griscom
+ * Suitable Systems
+ * http://www.suitable.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal with the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimers.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimers in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the names of Suitable Systems nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this Software without specific prior written permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+ *
+ * For more information about SMSLib, see
+ * <http://www.suitable.com/tools/smslib.html>
+ * or contact
+ * Daniel Griscom
+ * Suitable Systems
+ * 1 Centre Street, Suite 204
+ * Wakefield, MA 01880
+ * (781) 665-0053
+ *
+ */
+
+#import <IOKit/IOKitLib.h>
+#import <sys/sysctl.h>
+#import <math.h>
+#import "smslib.h"
+
+#pragma mark Internal structures
+
+// Represents a single axis of a type of sensor.
+typedef struct axisStruct {
+ int enabled; // Non-zero if axis is valid in this sensor
+ int index; // Location in struct of first byte
+ int size; // Number of bytes
+ float zerog; // Value meaning "zero g"
+ float oneg; // Change in value meaning "increase of one g"
+ // (can be negative if axis sensor reversed)
+} axisStruct;
+
+// Represents the configuration of a type of sensor.
+typedef struct sensorSpec {
+ const char *model; // Prefix of model to be tested
+ const char *name; // Name of device to be read
+ unsigned int function; // Kernel function index
+ int recordSize; // Size of record to be sent/received
+ axisStruct axes[3]; // Description of three axes (X, Y, Z)
+} sensorSpec;
+
+// Configuration of all known types of sensors. The configurations are
+// tried in order until one succeeds in returning data.
+// All default values are set here, but each axis' zerog and oneg values
+// may be changed to saved (calibrated) values.
+//
+// These values came from SeisMaCalibrate calibration reports. In general I've
+// found the following:
+// - All Intel-based SMSs have 250 counts per g, centered on 0, but the signs
+// are different (and in one case two axes are swapped)
+// - PowerBooks and iBooks all have sensors centered on 0, and reading
+// 50-53 steps per gravity (but with differing polarities!)
+// - PowerBooks and iBooks of the same model all have the same axis polarities
+// - PowerBook and iBook access methods are model- and OS version-specific
+//
+// So, the sequence of tests is:
+// - Try model-specific access methods. Note that the test is for a match to the
+// beginning of the model name, e.g. the record with model name "MacBook"
+// matches computer models "MacBookPro1,2" and "MacBook1,1" (and ""
+// matches any model).
+// - If no model-specific record's access fails, then try each model-independent
+// access method in order, stopping when one works.
+static const sensorSpec sensors[] = {
+ // ****** Model-dependent methods ******
+ // The PowerBook5,6 is one of the G4 models that seems to lose
+ // SMS access until the next reboot.
+ {"PowerBook5,6", "IOI2CMotionSensor", 21, 60, {
+ {1, 0, 1, 0, 51.5},
+ {1, 1, 1, 0, -51.5},
+ {1, 2, 1, 0, -51.5}
+ }
+ },
+ // The PowerBook5,7 is one of the G4 models that seems to lose
+ // SMS access until the next reboot.
+ {"PowerBook5,7", "IOI2CMotionSensor", 21, 60, {
+ {1, 0, 1, 0, 51.5},
+ {1, 1, 1, 0, 51.5},
+ {1, 2, 1, 0, 51.5}
+ }
+ },
+ // Access seems to be reliable on the PowerBook5,8
+ {"PowerBook5,8", "PMUMotionSensor", 21, 60, {
+ {1, 0, 1, 0, -51.5},
+ {1, 1, 1, 0, 51.5},
+ {1, 2, 1, 0, -51.5}
+ }
+ },
+ // Access seems to be reliable on the PowerBook5,9
+ {"PowerBook5,9", "PMUMotionSensor", 21, 60, {
+ {1, 0, 1, 0, 51.5},
+ {1, 1, 1, 0, -51.5},
+ {1, 2, 1, 0, -51.5}
+ }
+ },
+ // The PowerBook6,7 is one of the G4 models that seems to lose
+ // SMS access until the next reboot.
+ {"PowerBook6,7", "IOI2CMotionSensor", 21, 60, {
+ {1, 0, 1, 0, 51.5},
+ {1, 1, 1, 0, 51.5},
+ {1, 2, 1, 0, 51.5}
+ }
+ },
+ // The PowerBook6,8 is one of the G4 models that seems to lose
+ // SMS access until the next reboot.
+ {"PowerBook6,8", "IOI2CMotionSensor", 21, 60, {
+ {1, 0, 1, 0, 51.5},
+ {1, 1, 1, 0, 51.5},
+ {1, 2, 1, 0, 51.5}
+ }
+ },
+ // MacBook Pro Core 2 Duo 17". Note the reversed Y and Z axes.
+ {"MacBookPro2,1", "SMCMotionSensor", 5, 40, {
+ {1, 0, 2, 0, 251},
+ {1, 2, 2, 0, -251},
+ {1, 4, 2, 0, -251}
+ }
+ },
+ // MacBook Pro Core 2 Duo 15" AND 17" with LED backlight, introduced June '07.
+ // NOTE! The 17" machines have the signs of their X and Y axes reversed
+ // from this calibration, but there's no clear way to discriminate between
+ // the two machines.
+ {"MacBookPro3,1", "SMCMotionSensor", 5, 40, {
+ {1, 0, 2, 0, -251},
+ {1, 2, 2, 0, 251},
+ {1, 4, 2, 0, -251}
+ }
+ },
+ // ... specs?
+ {"MacBook5,2", "SMCMotionSensor", 5, 40, {
+ {1, 0, 2, 0, -251},
+ {1, 2, 2, 0, 251},
+ {1, 4, 2, 0, -251}
+ }
+ },
+ // ... specs?
+ {"MacBookPro5,1", "SMCMotionSensor", 5, 40, {
+ {1, 0, 2, 0, -251},
+ {1, 2, 2, 0, -251},
+ {1, 4, 2, 0, 251}
+ }
+ },
+ // ... specs?
+ {"MacBookPro5,2", "SMCMotionSensor", 5, 40, {
+ {1, 0, 2, 0, -251},
+ {1, 2, 2, 0, -251},
+ {1, 4, 2, 0, 251}
+ }
+ },
+ // This is speculative, based on a single user's report. Looks like the X and Y axes
+ // are swapped. This is true for no other known Appple laptop.
+ {"MacBookPro5,3", "SMCMotionSensor", 5, 40, {
+ {1, 2, 2, 0, -251},
+ {1, 0, 2, 0, -251},
+ {1, 4, 2, 0, -251}
+ }
+ },
+ // ... specs?
+ {"MacBookPro5,4", "SMCMotionSensor", 5, 40, {
+ {1, 0, 2, 0, -251},
+ {1, 2, 2, 0, -251},
+ {1, 4, 2, 0, 251}
+ }
+ },
+ // ****** Model-independent methods ******
+ // Seen once with PowerBook6,8 under system 10.3.9; I suspect
+ // other G4-based 10.3.* systems might use this
+ {"", "IOI2CMotionSensor", 24, 60, {
+ {1, 0, 1, 0, 51.5},
+ {1, 1, 1, 0, 51.5},
+ {1, 2, 1, 0, 51.5}
+ }
+ },
+ // PowerBook5,6 , PowerBook5,7 , PowerBook6,7 , PowerBook6,8
+ // under OS X 10.4.*
+ {"", "IOI2CMotionSensor", 21, 60, {
+ {1, 0, 1, 0, 51.5},
+ {1, 1, 1, 0, 51.5},
+ {1, 2, 1, 0, 51.5}
+ }
+ },
+ // PowerBook5,8 , PowerBook5,9 under OS X 10.4.*
+ {"", "PMUMotionSensor", 21, 60, {
+ // Each has two out of three gains negative, but it's different
+ // for the different models. So, this will be right in two out
+ // of three axis for either model.
+ {1, 0, 1, 0, -51.5},
+ {1, 1, 1, -6, -51.5},
+ {1, 2, 1, 0, -51.5}
+ }
+ },
+ // All MacBook, MacBookPro models. Hardware (at least on early MacBookPro 15")
+ // is Kionix KXM52-1050 three-axis accelerometer chip. Data is at
+ // http://kionix.com/Product-Index/product-index.htm. Specific MB and MBP models
+ // that use this are:
+ // MacBook1,1
+ // MacBook2,1
+ // MacBook3,1
+ // MacBook4,1
+ // MacBook5,1
+ // MacBook6,1
+ // MacBookAir1,1
+ // MacBookPro1,1
+ // MacBookPro1,2
+ // MacBookPro4,1
+ // MacBookPro5,5
+ {"", "SMCMotionSensor", 5, 40, {
+ {1, 0, 2, 0, 251},
+ {1, 2, 2, 0, 251},
+ {1, 4, 2, 0, 251}
+ }
+ }
+};
+
+#define SENSOR_COUNT (sizeof(sensors)/sizeof(sensorSpec))
+
+#pragma mark Internal prototypes
+
+static int getData(sms_acceleration *accel, int calibrated, id logObject, SEL logSelector);
+static float getAxis(int which, int calibrated);
+static int signExtend(int value, int size);
+static NSString *getModelName(void);
+static NSString *getOSVersion(void);
+static BOOL loadCalibration(void);
+static void storeCalibration(void);
+static void defaultCalibration(void);
+static void deleteCalibration(void);
+static int prefIntRead(NSString *prefName, BOOL *success);
+static void prefIntWrite(NSString *prefName, int prefValue);
+static float prefFloatRead(NSString *prefName, BOOL *success);
+static void prefFloatWrite(NSString *prefName, float prefValue);
+static void prefDelete(NSString *prefName);
+static void prefSynchronize(void);
+// static long getMicroseconds(void);
+float fakeData(NSTimeInterval time);
+
+#pragma mark Static variables
+
+static int debugging = NO; // True if debugging (synthetic data)
+static io_connect_t connection; // Connection for reading accel values
+static int running = NO; // True if we successfully started
+static unsigned int sensorNum = 0; // The current index into sensors[]
+static const char *serviceName; // The name of the current service
+static char *iRecord, *oRecord; // Pointers to read/write records for sensor
+static int recordSize; // Size of read/write records
+static unsigned int function; // Which kernel function should be used
+static float zeros[3]; // X, Y and Z zero calibration values
+static float onegs[3]; // X, Y and Z one-g calibration values
+
+#pragma mark Defines
+
+// Pattern for building axis letter from axis number
+#define INT_TO_AXIS(a) (a == 0 ? @"X" : a == 1 ? @"Y" : @"Z")
+// Name of configuration for given axis' zero (axis specified by integer)
+#define ZERO_NAME(a) [NSString stringWithFormat:@"%@-Axis-Zero", INT_TO_AXIS(a)]
+// Name of configuration for given axis' oneg (axis specified by integer)
+#define ONEG_NAME(a) [NSString stringWithFormat:@"%@-Axis-One-g", INT_TO_AXIS(a)]
+// Name of "Is calibrated" preference
+#define CALIBRATED_NAME (@"Calibrated")
+// Application domain for SeisMac library
+#define APP_ID ((CFStringRef)@"com.suitable.SeisMacLib")
+
+// These #defines make the accelStartup code a LOT easier to read.
+#undef LOG
+#define LOG(message) \
+ if (logObject) { \
+ [logObject performSelector:logSelector withObject:message]; \
+ }
+#define LOG_ARG(format, var1) \
+ if (logObject) { \
+ [logObject performSelector:logSelector \
+ withObject:[NSString stringWithFormat:format, var1]]; \
+ }
+#define LOG_2ARG(format, var1, var2) \
+ if (logObject) { \
+ [logObject performSelector:logSelector \
+ withObject:[NSString stringWithFormat:format, var1, var2]]; \
+ }
+#define LOG_3ARG(format, var1, var2, var3) \
+ if (logObject) { \
+ [logObject performSelector:logSelector \
+ withObject:[NSString stringWithFormat:format, var1, var2, var3]]; \
+ }
+
+#pragma mark Function definitions
+
+// This starts up the accelerometer code, trying each possible sensor
+// specification. Note that for logging purposes it
+// takes an object and a selector; the object's selector is then invoked
+// with a single NSString as argument giving progress messages. Example
+// logging method:
+// - (void)logMessage: (NSString *)theString
+// which would be used in accelStartup's invocation thusly:
+// result = accelStartup(self, @selector(logMessage:));
+// If the object is nil, then no logging is done. Sets calibation from built-in
+// value table. Returns ACCEL_SUCCESS for success, and other (negative)
+// values for various failures (returns value indicating result of
+// most successful trial).
+int smsStartup(id logObject, SEL logSelector) {
+ io_iterator_t iterator;
+ io_object_t device;
+ kern_return_t result;
+ sms_acceleration accel;
+ int failure_result = SMS_FAIL_MODEL;
+
+ running = NO;
+ debugging = NO;
+
+ NSString *modelName = getModelName();
+
+ LOG_ARG(@"Machine model: %@\n", modelName);
+ LOG_ARG(@"OS X version: %@\n", getOSVersion());
+ LOG_ARG(@"Accelerometer library version: %s\n", SMSLIB_VERSION);
+
+ for (sensorNum = 0; sensorNum < SENSOR_COUNT; sensorNum++) {
+
+ // Set up all specs for this type of sensor
+ serviceName = sensors[sensorNum].name;
+ recordSize = sensors[sensorNum].recordSize;
+ function = sensors[sensorNum].function;
+
+ LOG_3ARG(@"Trying service \"%s\" with selector %d and %d byte record:\n",
+ serviceName, function, recordSize);
+
+ NSString *targetName = [NSString stringWithCString:sensors[sensorNum].model
+ encoding:NSMacOSRomanStringEncoding];
+ LOG_ARG(@" Comparing model name to target \"%@\": ", targetName);
+ if ([targetName length] == 0 || [modelName hasPrefix:targetName]) {
+ LOG(@"success.\n");
+ } else {
+ LOG(@"failure.\n");
+ // Don't need to increment failure_result.
+ continue;
+ }
+
+ LOG(@" Fetching dictionary for service: ");
+ CFMutableDictionaryRef dict = IOServiceMatching(serviceName);
+
+ if (dict) {
+ LOG(@"success.\n");
+ } else {
+ LOG(@"failure.\n");
+ if (failure_result < SMS_FAIL_DICTIONARY) {
+ failure_result = SMS_FAIL_DICTIONARY;
+ }
+ continue;
+ }
+
+ LOG(@" Getting list of matching services: ");
+ result = IOServiceGetMatchingServices(kIOMasterPortDefault,
+ dict,
+ &iterator);
+
+ if (result == KERN_SUCCESS) {
+ LOG(@"success.\n");
+ } else {
+ LOG_ARG(@"failure, with return value 0x%x.\n", result);
+ if (failure_result < SMS_FAIL_LIST_SERVICES) {
+ failure_result = SMS_FAIL_LIST_SERVICES;
+ }
+ continue;
+ }
+
+ LOG(@" Getting first device in list: ");
+ device = IOIteratorNext(iterator);
+
+ if (device == 0) {
+ LOG(@"failure.\n");
+ if (failure_result < SMS_FAIL_NO_SERVICES) {
+ failure_result = SMS_FAIL_NO_SERVICES;
+ }
+ continue;
+ } else {
+ LOG(@"success.\n");
+ LOG(@" Opening device: ");
+ }
+
+ result = IOServiceOpen(device, mach_task_self(), 0, &connection);
+
+ if (result != KERN_SUCCESS) {
+ LOG_ARG(@"failure, with return value 0x%x.\n", result);
+ IOObjectRelease(device);
+ if (failure_result < SMS_FAIL_OPENING) {
+ failure_result = SMS_FAIL_OPENING;
+ }
+ continue;
+ } else if (connection == 0) {
+ LOG_ARG(@"'success', but didn't get a connection (return value was: 0x%x).\n", result);
+ IOObjectRelease(device);
+ if (failure_result < SMS_FAIL_CONNECTION) {
+ failure_result = SMS_FAIL_CONNECTION;
+ }
+ continue;
+ } else {
+ IOObjectRelease(device);
+ LOG(@"success.\n");
+ }
+ LOG(@" Testing device.\n");
+
+ defaultCalibration();
+
+ iRecord = (char*) malloc(recordSize);
+ oRecord = (char*) malloc(recordSize);
+
+ running = YES;
+ result = getData(&accel, true, logObject, logSelector);
+ running = NO;
+
+ if (result) {
+ LOG_ARG(@" Failure testing device, with result 0x%x.\n", result);
+ free(iRecord);
+ iRecord = 0;
+ free(oRecord);
+ oRecord = 0;
+ if (failure_result < SMS_FAIL_ACCESS) {
+ failure_result = SMS_FAIL_ACCESS;
+ }
+ continue;
+ } else {
+ LOG(@" Success testing device!\n");
+ running = YES;
+ return SMS_SUCCESS;
+ }
+ }
+ return failure_result;
+}
+
+// This starts up the library in debug mode, ignoring the actual hardware.
+// Returned data is in the form of 1Hz sine waves, with the X, Y and Z
+// axes 120 degrees out of phase; "calibrated" data has range +/- (1.0/5);
+// "uncalibrated" data has range +/- (256/5). X and Y axes centered on 0.0,
+// Z axes centered on 1 (calibrated) or 256 (uncalibrated).
+// Don't use smsGetBufferLength or smsGetBufferData. Always returns SMS_SUCCESS.
+int smsDebugStartup(id logObject, SEL logSelector) {
+ LOG(@"Starting up in debug mode\n");
+ debugging = YES;
+ return SMS_SUCCESS;
+}
+
+// Returns the current calibration values.
+void smsGetCalibration(sms_calibration *calibrationRecord) {
+ int x;
+
+ for (x = 0; x < 3; x++) {
+ calibrationRecord->zeros[x] = (debugging ? 0 : zeros[x]);
+ calibrationRecord->onegs[x] = (debugging ? 256 : onegs[x]);
+ }
+}
+
+// Sets the calibration, but does NOT store it as a preference. If the argument
+// is nil then the current calibration is set from the built-in value table.
+void smsSetCalibration(sms_calibration *calibrationRecord) {
+ int x;
+
+ if (!debugging) {
+ if (calibrationRecord) {
+ for (x = 0; x < 3; x++) {
+ zeros[x] = calibrationRecord->zeros[x];
+ onegs[x] = calibrationRecord->onegs[x];
+ }
+ } else {
+ defaultCalibration();
+ }
+ }
+}
+
+// Stores the current calibration values as a stored preference.
+void smsStoreCalibration(void) {
+ if (!debugging)
+ storeCalibration();
+}
+
+// Loads the stored preference values into the current calibration.
+// Returns YES if successful.
+BOOL smsLoadCalibration(void) {
+ if (debugging) {
+ return YES;
+ } else if (loadCalibration()) {
+ return YES;
+ } else {
+ defaultCalibration();
+ return NO;
+ }
+}
+
+// Deletes any stored calibration, and then takes the current calibration values
+// from the built-in value table.
+void smsDeleteCalibration(void) {
+ if (!debugging) {
+ deleteCalibration();
+ defaultCalibration();
+ }
+}
+
+// Fills in the accel record with calibrated acceleration data. Takes
+// 1-2ms to return a value. Returns 0 if success, error number if failure.
+int smsGetData(sms_acceleration *accel) {
+ NSTimeInterval time;
+ if (debugging) {
+ usleep(1500); // Usually takes 1-2 milliseconds
+ time = [NSDate timeIntervalSinceReferenceDate];
+ accel->x = fakeData(time)/5;
+ accel->y = fakeData(time - 1)/5;
+ accel->z = fakeData(time - 2)/5 + 1.0;
+ return true;
+ } else {
+ return getData(accel, true, nil, nil);
+ }
+}
+
+// Fills in the accel record with uncalibrated acceleration data.
+// Returns 0 if success, error number if failure.
+int smsGetUncalibratedData(sms_acceleration *accel) {
+ NSTimeInterval time;
+ if (debugging) {
+ usleep(1500); // Usually takes 1-2 milliseconds
+ time = [NSDate timeIntervalSinceReferenceDate];
+ accel->x = fakeData(time) * 256 / 5;
+ accel->y = fakeData(time - 1) * 256 / 5;
+ accel->z = fakeData(time - 2) * 256 / 5 + 256;
+ return true;
+ } else {
+ return getData(accel, false, nil, nil);
+ }
+}
+
+// Returns the length of a raw block of data for the current type of sensor.
+int smsGetBufferLength(void) {
+ if (debugging) {
+ return 0;
+ } else if (running) {
+ return sensors[sensorNum].recordSize;
+ } else {
+ return 0;
+ }
+}
+
+// Takes a pointer to accelGetRawLength() bytes; sets those bytes
+// to return value from sensor. Make darn sure the buffer length is right!
+void smsGetBufferData(char *buffer) {
+ IOItemCount iSize = recordSize;
+ IOByteCount oSize = recordSize;
+ kern_return_t result;
+
+ if (debugging || running == NO) {
+ return;
+ }
+
+ memset(iRecord, 1, iSize);
+ memset(buffer, 0, oSize);
+#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
+ const size_t InStructSize = recordSize;
+ size_t OutStructSize = recordSize;
+ result = IOConnectCallStructMethod(connection,
+ function, // magic kernel function number
+ (const void *)iRecord,
+ InStructSize,
+ (void *)buffer,
+ &OutStructSize
+ );
+#else // __MAC_OS_X_VERSION_MIN_REQUIRED 1050
+ result = IOConnectMethodStructureIStructureO(connection,
+ function, // magic kernel function number
+ iSize,
+ &oSize,
+ iRecord,
+ buffer
+ );
+#endif // __MAC_OS_X_VERSION_MIN_REQUIRED 1050
+
+ if (result != KERN_SUCCESS) {
+ running = NO;
+ }
+}
+
+// This returns an NSString describing the current calibration in
+// human-readable form. Also include a description of the machine.
+NSString *smsGetCalibrationDescription(void) {
+ BOOL success;
+ NSMutableString *s = [[NSMutableString alloc] init];
+
+ if (debugging) {
+ [s release];
+ return @"Debugging!";
+ }
+
+ [s appendString:@"---- SeisMac Calibration Record ----\n \n"];
+ [s appendFormat:@"Machine model: %@\n",
+ getModelName()];
+ [s appendFormat:@"OS X build: %@\n",
+ getOSVersion()];
+ [s appendFormat:@"SeisMacLib version %s, record %d\n \n",
+ SMSLIB_VERSION, sensorNum];
+ [s appendFormat:@"Using service \"%s\", function index %d, size %d\n \n",
+ serviceName, function, recordSize];
+ if (prefIntRead(CALIBRATED_NAME, &success) && success) {
+ [s appendString:@"Calibration values (from calibration):\n"];
+ } else {
+ [s appendString:@"Calibration values (from defaults):\n"];
+ }
+ [s appendFormat:@" X-Axis-Zero = %.2f\n", zeros[0]];
+ [s appendFormat:@" X-Axis-One-g = %.2f\n", onegs[0]];
+ [s appendFormat:@" Y-Axis-Zero = %.2f\n", zeros[1]];
+ [s appendFormat:@" Y-Axis-One-g = %.2f\n", onegs[1]];
+ [s appendFormat:@" Z-Axis-Zero = %.2f\n", zeros[2]];
+ [s appendFormat:@" Z-Axis-One-g = %.2f\n \n", onegs[2]];
+ [s appendString:@"---- End Record ----\n"];
+ return s;
+}
+
+// Shuts down the accelerometer.
+void smsShutdown(void) {
+ if (!debugging) {
+ running = NO;
+ if (iRecord) free(iRecord);
+ if (oRecord) free(oRecord);
+ IOServiceClose(connection);
+ }
+}
+
+#pragma mark Internal functions
+
+// Loads the current calibration from the stored preferences.
+// Returns true iff successful.
+BOOL loadCalibration(void) {
+ BOOL thisSuccess, allSuccess;
+ int x;
+
+ prefSynchronize();
+
+ if (prefIntRead(CALIBRATED_NAME, &thisSuccess) && thisSuccess) {
+ // Calibrated. Set all values from saved values.
+ allSuccess = YES;
+ for (x = 0; x < 3; x++) {
+ zeros[x] = prefFloatRead(ZERO_NAME(x), &thisSuccess);
+ allSuccess &= thisSuccess;
+ onegs[x] = prefFloatRead(ONEG_NAME(x), &thisSuccess);
+ allSuccess &= thisSuccess;
+ }
+ return allSuccess;
+ }
+
+ return NO;
+}
+
+// Stores the current calibration into the stored preferences.
+static void storeCalibration(void) {
+ int x;
+ prefIntWrite(CALIBRATED_NAME, 1);
+ for (x = 0; x < 3; x++) {
+ prefFloatWrite(ZERO_NAME(x), zeros[x]);
+ prefFloatWrite(ONEG_NAME(x), onegs[x]);
+ }
+ prefSynchronize();
+}
+
+
+// Sets the calibration to its default values.
+void defaultCalibration(void) {
+ int x;
+ for (x = 0; x < 3; x++) {
+ zeros[x] = sensors[sensorNum].axes[x].zerog;
+ onegs[x] = sensors[sensorNum].axes[x].oneg;
+ }
+}
+
+// Deletes the stored preferences.
+static void deleteCalibration(void) {
+ int x;
+
+ prefDelete(CALIBRATED_NAME);
+ for (x = 0; x < 3; x++) {
+ prefDelete(ZERO_NAME(x));
+ prefDelete(ONEG_NAME(x));
+ }
+ prefSynchronize();
+}
+
+// Read a named floating point value from the stored preferences. Sets
+// the success boolean based on, you guessed it, whether it succeeds.
+static float prefFloatRead(NSString *prefName, BOOL *success) {
+ float result = 0.0f;
+
+ CFPropertyListRef ref = CFPreferencesCopyAppValue((CFStringRef)prefName,
+ APP_ID);
+ // If there isn't such a preference, fail
+ if (ref == NULL) {
+ *success = NO;
+ return result;
+ }
+ CFTypeID typeID = CFGetTypeID(ref);
+ // Is it a number?
+ if (typeID == CFNumberGetTypeID()) {
+ // Is it a floating point number?
+ if (CFNumberIsFloatType((CFNumberRef)ref)) {
+ // Yup: grab it.
+ *success = CFNumberGetValue((__CFNumber*)ref, kCFNumberFloat32Type, &result);
+ } else {
+ // Nope: grab as an integer, and convert to a float.
+ long num;
+ if (CFNumberGetValue((CFNumberRef)ref, kCFNumberLongType, &num)) {
+ result = num;
+ *success = YES;
+ } else {
+ *success = NO;
+ }
+ }
+ // Or is it a string (e.g. set by the command line "defaults" command)?
+ } else if (typeID == CFStringGetTypeID()) {
+ result = (float)CFStringGetDoubleValue((CFStringRef)ref);
+ *success = YES;
+ } else {
+ // Can't convert to a number: fail.
+ *success = NO;
+ }
+ CFRelease(ref);
+ return result;
+}
+
+// Writes a named floating point value to the stored preferences.
+static void prefFloatWrite(NSString *prefName, float prefValue) {
+ CFNumberRef cfFloat = CFNumberCreate(kCFAllocatorDefault,
+ kCFNumberFloatType,
+ &prefValue);
+ CFPreferencesSetAppValue((CFStringRef)prefName,
+ cfFloat,
+ APP_ID);
+ CFRelease(cfFloat);
+}
+
+// Reads a named integer value from the stored preferences.
+static int prefIntRead(NSString *prefName, BOOL *success) {
+ Boolean internalSuccess;
+ CFIndex result = CFPreferencesGetAppIntegerValue((CFStringRef)prefName,
+ APP_ID,
+ &internalSuccess);
+ *success = internalSuccess;
+
+ return result;
+}
+
+// Writes a named integer value to the stored preferences.
+static void prefIntWrite(NSString *prefName, int prefValue) {
+ CFPreferencesSetAppValue((CFStringRef)prefName,
+ (CFNumberRef)[NSNumber numberWithInt:prefValue],
+ APP_ID);
+}
+
+// Deletes the named preference values.
+static void prefDelete(NSString *prefName) {
+ CFPreferencesSetAppValue((CFStringRef)prefName,
+ NULL,
+ APP_ID);
+}
+
+// Synchronizes the local preferences with the stored preferences.
+static void prefSynchronize(void) {
+ CFPreferencesAppSynchronize(APP_ID);
+}
+
+// Internal version of accelGetData, with logging
+int getData(sms_acceleration *accel, int calibrated, id logObject, SEL logSelector) {
+ IOItemCount iSize = recordSize;
+ IOByteCount oSize = recordSize;
+ kern_return_t result;
+
+ if (running == NO) {
+ return -1;
+ }
+
+ memset(iRecord, 1, iSize);
+ memset(oRecord, 0, oSize);
+
+ LOG_2ARG(@" Querying device (%u, %d): ",
+ sensors[sensorNum].function, sensors[sensorNum].recordSize);
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
+ const size_t InStructSize = recordSize;
+ size_t OutStructSize = recordSize;
+ result = IOConnectCallStructMethod(connection,
+ function, // magic kernel function number
+ (const void *)iRecord,
+ InStructSize,
+ (void *)oRecord,
+ &OutStructSize
+ );
+#else // __MAC_OS_X_VERSION_MIN_REQUIRED 1050
+ result = IOConnectMethodStructureIStructureO(connection,
+ function, // magic kernel function number
+ iSize,
+ &oSize,
+ iRecord,
+ oRecord
+ );
+#endif // __MAC_OS_X_VERSION_MIN_REQUIRED 1050
+
+ if (result != KERN_SUCCESS) {
+ LOG(@"failed.\n");
+ running = NO;
+ return result;
+ } else {
+ LOG(@"succeeded.\n");
+
+ accel->x = getAxis(0, calibrated);
+ accel->y = getAxis(1, calibrated);
+ accel->z = getAxis(2, calibrated);
+ return 0;
+ }
+}
+
+// Given the returned record, extracts the value of the given axis. If
+// calibrated, then zero G is 0.0, and one G is 1.0.
+float getAxis(int which, int calibrated) {
+ // Get various values (to make code cleaner)
+ int indx = sensors[sensorNum].axes[which].index;
+ int size = sensors[sensorNum].axes[which].size;
+ float zerog = zeros[which];
+ float oneg = onegs[which];
+ // Storage for value to be returned
+ int value = 0;
+
+ // Although the values in the returned record should have the proper
+ // endianness, we still have to get it into the proper end of value.
+#if (BYTE_ORDER == BIG_ENDIAN)
+ // On PowerPC processors
+ memcpy(((char *)&value) + (sizeof(int) - size), &oRecord[indx], size);
+#endif
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+ // On Intel processors
+ memcpy(&value, &oRecord[indx], size);
+#endif
+
+ value = signExtend(value, size);
+
+ if (calibrated) {
+ // Scale and shift for zero.
+ return ((float)(value - zerog)) / oneg;
+ } else {
+ return value;
+ }
+}
+
+// Extends the sign, given the length of the value.
+int signExtend(int value, int size) {
+ // Extend sign
+ switch (size) {
+ case 1:
+ if (value & 0x00000080)
+ value |= 0xffffff00;
+ break;
+ case 2:
+ if (value & 0x00008000)
+ value |= 0xffff0000;
+ break;
+ case 3:
+ if (value & 0x00800000)
+ value |= 0xff000000;
+ break;
+ }
+ return value;
+}
+
+// Returns the model name of the computer (e.g. "MacBookPro1,1")
+NSString *getModelName(void) {
+ char model[32];
+ size_t len = sizeof(model);
+ int name[2] = {CTL_HW, HW_MODEL};
+ NSString *result;
+
+ if (sysctl(name, 2, &model, &len, NULL, 0) == 0) {
+ result = [NSString stringWithFormat:@"%s", model];
+ } else {
+ result = @"";
+ }
+
+ return result;
+}
+
+// Returns the current OS X version and build (e.g. "10.4.7 (build 8J2135a)")
+NSString *getOSVersion(void) {
+ NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:
+ @"/System/Library/CoreServices/SystemVersion.plist"];
+ NSString *versionString = [dict objectForKey:@"ProductVersion"];
+ NSString *buildString = [dict objectForKey:@"ProductBuildVersion"];
+ NSString *wholeString = [NSString stringWithFormat:@"%@ (build %@)",
+ versionString, buildString];
+ return wholeString;
+}
+
+// Returns time within the current second in microseconds.
+// long getMicroseconds() {
+// struct timeval t;
+// gettimeofday(&t, 0);
+// return t.tv_usec;
+//}
+
+// Returns fake data given the time. Range is +/-1.
+float fakeData(NSTimeInterval time) {
+ long secs = lround(floor(time));
+ int secsMod3 = secs % 3;
+ double angle = time * 10 * M_PI * 2;
+ double mag = exp(-(time - (secs - secsMod3)) * 2);
+ return sin(angle) * mag;
+}
+
diff --git a/hal/moz.build b/hal/moz.build
index 1aecdb750e..fefd56fcf1 100644
--- a/hal/moz.build
+++ b/hal/moz.build
@@ -40,6 +40,13 @@ elif CONFIG['OS_TARGET'] == 'WINNT':
'fallback/FallbackScreenConfiguration.cpp',
'windows/WindowsSensor.cpp',
]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ UNIFIED_SOURCES += [
+ 'fallback/FallbackAlarm.cpp',
+ 'fallback/FallbackMemory.cpp',
+ 'fallback/FallbackPower.cpp',
+ 'fallback/FallbackScreenConfiguration.cpp',
+ ]
elif CONFIG['OS_TARGET'] in ('OpenBSD', 'NetBSD', 'FreeBSD', 'DragonFly'):
UNIFIED_SOURCES += [
'fallback/FallbackAlarm.cpp',
@@ -72,6 +79,12 @@ UNIFIED_SOURCES += [
'fallback/FallbackNetwork.cpp',
]
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ UNIFIED_SOURCES += [
+ 'cocoa/CocoaSensor.mm',
+ 'cocoa/smslib.mm',
+ ]
+
IPDL_SOURCES = [
'sandbox/PHal.ipdl',
]