diff --git a/widget/src/gonk/Framebuffer.cpp b/widget/src/gonk/Framebuffer.cpp new file mode 100644 index 00000000000..c65944be068 --- /dev/null +++ b/widget/src/gonk/Framebuffer.cpp @@ -0,0 +1,185 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: sw=2 ts=8 et ft=cpp : */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Code. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Jones + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "android/log.h" + +#include "Framebuffer.h" +#include "gfxImageSurface.h" +#include "mozilla/FileUtils.h" +#include "nsTArray.h" + +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args) + +using namespace std; + +namespace mozilla { + +namespace Framebuffer { + +static int sFd = -1; +static size_t sMappedSize; +static struct fb_var_screeninfo sVi; +static size_t sActiveBuffer; +typedef vector > BufferVector; +BufferVector* sBuffers; + +BufferVector& Buffers() { return *sBuffers; } + +bool +SetGraphicsMode() +{ + ScopedClose fd(open("/dev/tty0", O_RDWR | O_SYNC)); + if (0 > fd.mFd) { + // This is non-fatal; post-Cupcake kernels don't have tty0. + LOG("No /dev/tty0?"); + } else if (ioctl(fd.mFd, KDSETMODE, (void*) KD_GRAPHICS)) { + LOG("Error setting graphics mode on /dev/tty0"); + return false; + } + return true; +} + +bool +Open(nsIntSize* aScreenSize) +{ + if (0 <= sFd) + return true; + + if (!SetGraphicsMode()) + return false; + + ScopedClose fd(open("/dev/graphics/fb0", O_RDWR)); + if (0 > fd.mFd) { + LOG("Error opening framebuffer device"); + return false; + } + + struct fb_fix_screeninfo fi; + if (0 > ioctl(fd.mFd, FBIOGET_FSCREENINFO, &fi)) { + LOG("Error getting fixed screeninfo"); + return false; + } + + if (0 > ioctl(fd.mFd, FBIOGET_VSCREENINFO, &sVi)) { + LOG("Error getting variable screeninfo"); + return false; + } + + sMappedSize = fi.smem_len; + void* mem = mmap(0, sMappedSize, PROT_READ | PROT_WRITE, MAP_SHARED, + fd.mFd, 0); + if (MAP_FAILED == mem) { + LOG("Error mmap'ing framebuffer"); + return false; + } + + sFd = fd.mFd; + fd.mFd = -1; + + // The android porting doc requires a /dev/graphics/fb0 device + // that's double buffered with r5g6b5 format. Hence the + // hard-coded numbers here. + gfxASurface::gfxImageFormat format = gfxASurface::ImageFormatRGB16_565; + int bytesPerPixel = gfxASurface::BytePerPixelFromFormat(format); + gfxIntSize size(sVi.xres, sVi.yres); + long stride = size.width * bytesPerPixel; + size_t numFrameBytes = stride * size.height; + + sBuffers = new BufferVector(2); + unsigned char* data = static_cast(mem); + for (size_t i = 0; i < 2; ++i, data += numFrameBytes) { + memset(data, 0, numFrameBytes); + Buffers()[i] = new gfxImageSurface(data, size, stride, format); + } + + // Clear the framebuffer to a known state. + Present(); + + *aScreenSize = size; + return true; +} + +void +Close() +{ + if (0 > sFd) + return; + + munmap(Buffers()[0]->Data(), sMappedSize); + delete sBuffers; + sBuffers = NULL; + + close(sFd); + sFd = -1; +} + +gfxASurface* +BackBuffer() +{ + return Buffers()[!sActiveBuffer]; +} + +void +Present() +{ + sActiveBuffer = !sActiveBuffer; + + sVi.yres_virtual = sVi.yres * 2; + sVi.yoffset = sActiveBuffer * sVi.yres; + sVi.bits_per_pixel = 16; + if (ioctl(sFd, FBIOPUT_VSCREENINFO, &sVi) < 0) { + LOG("Error presenting front buffer"); + } +} + +} // namespace Framebuffer + +} // namespace mozilla diff --git a/widget/src/gonk/Framebuffer.h b/widget/src/gonk/Framebuffer.h new file mode 100644 index 00000000000..4325e2c5cbb --- /dev/null +++ b/widget/src/gonk/Framebuffer.h @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: sw=2 ts=8 et ft=cpp : */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Code. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Jones + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +class gfxASurface; +class nsIntSize; + +namespace mozilla { + +namespace Framebuffer { + +// +// The general usage of Framebuffer is +// +// -- in initialization code -- +// Open(); +// +// -- ready to paint next frame -- +// nsRefPtr backBuffer = BackBuffer(); +// // ... +// Paint(backBuffer); +// // ... +// Present(); +// + +// Return true if the fbdev was successfully opened, along with the +// dimensions of the screen. If this fails, the result of all further +// calls is undefined. Open() is idempotent. +bool Open(nsIntSize* aScreenSize); + +// After Close(), the result of all further calls is undefined. +// Close() is idempotent, and Open() can be called again after +// Close(). +void Close(); + +// Return the buffer to be drawn into, that will be the next frame. +gfxASurface* BackBuffer(); + +// Swap the front buffer for the back buffer. +void Present(); + +} // namespace Framebuffer + +} // namespace mozilla diff --git a/widget/src/gonk/Makefile.in b/widget/src/gonk/Makefile.in index f449b866369..54487951aa5 100644 --- a/widget/src/gonk/Makefile.in +++ b/widget/src/gonk/Makefile.in @@ -52,6 +52,7 @@ LIBXUL_LIBRARY = 1 CPPSRCS = \ + Framebuffer.cpp \ nsAppShell.cpp \ nsWidgetFactory.cpp \ nsWindow.cpp \ diff --git a/widget/src/gonk/nsAppShell.cpp b/widget/src/gonk/nsAppShell.cpp index 943fa65119b..0e57c7ebdb3 100644 --- a/widget/src/gonk/nsAppShell.cpp +++ b/widget/src/gonk/nsAppShell.cpp @@ -62,6 +62,9 @@ #ifndef ABS_MT_TOUCH_MAJOR // Taken from include/linux/input.h // XXX update the bionic input.h so we don't have to do this! +#define ABS_X 0x00 +#define ABS_Y 0x01 +// ... #define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */ #define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */ #define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */ @@ -136,6 +139,87 @@ sendMouseEvent(PRUint32 msg, struct timeval *time, int x, int y) //LOG("Dispatched type %d at %dx%d", msg, x, y); } +static nsEventStatus +sendKeyEventWithMsg(PRUint32 keyCode, + PRUint32 msg, + const timeval &time, + PRUint32 flags) +{ + nsKeyEvent event(true, msg, NULL); + event.keyCode = keyCode; + event.time = timevalToMS(time); + event.flags |= flags; + return nsWindow::DispatchInputEvent(event); +} + +static void +sendKeyEvent(PRUint32 keyCode, bool down, const timeval &time) +{ + nsEventStatus status = + sendKeyEventWithMsg(keyCode, down ? NS_KEY_DOWN : NS_KEY_UP, time, 0); + if (down) { + sendKeyEventWithMsg(keyCode, NS_KEY_PRESS, time, + status == nsEventStatus_eConsumeNoDefault ? + NS_EVENT_FLAG_NO_DEFAULT : 0); + } +} + +static void +sendSpecialKeyEvent(nsIAtom *command, const timeval &time) +{ + nsCommandEvent event(true, nsGkAtoms::onAppCommand, command, NULL); + event.time = timevalToMS(time); + nsWindow::DispatchInputEvent(event); +} + +static void +maybeSendKeyEvent(const input_event& e) +{ + if (e.type != EV_KEY) { + LOG("Got unknown key event type. type 0x%04x code 0x%04x value %d", + e.type, e.code, e.value); + return; + } + + if (e.value != 0 && e.value != 1) { + LOG("Got unknown key event value. type 0x%04x code 0x%04x value %d", + e.type, e.code, e.value); + return; + } + + bool pressed = e.value == 1; + switch (e.code) { + case KEY_BACK: + sendKeyEvent(NS_VK_ESCAPE, pressed, e.time); + break; + case KEY_MENU: + if (!pressed) + sendSpecialKeyEvent(nsGkAtoms::Menu, e.time); + break; + case KEY_SEARCH: + if (pressed) + sendSpecialKeyEvent(nsGkAtoms::Search, e.time); + break; + case KEY_HOME: + sendKeyEvent(NS_VK_HOME, pressed, e.time); + break; + case KEY_POWER: + sendKeyEvent(NS_VK_SLEEP, pressed, e.time); + break; + case KEY_VOLUMEUP: + if (pressed) + sendSpecialKeyEvent(nsGkAtoms::VolumeUp, e.time); + break; + case KEY_VOLUMEDOWN: + if (pressed) + sendSpecialKeyEvent(nsGkAtoms::VolumeDown, e.time); + break; + default: + LOG("Got unknown key event code. type 0x%04x code 0x%04x value %d", + e.type, e.code, e.value); + } +} + static void multitouchHandler(int fd, FdHandler *data) { @@ -217,37 +301,64 @@ multitouchHandler(int fd, FdHandler *data) } } -static nsEventStatus -sendKeyEventWithMsg(PRUint32 keyCode, - PRUint32 msg, - const timeval &time, - PRUint32 flags) -{ - nsKeyEvent event(true, msg, NULL); - event.keyCode = keyCode; - event.time = timevalToMS(time); - event.flags |= flags; - return nsWindow::DispatchInputEvent(event); -} - static void -sendKeyEvent(PRUint32 keyCode, bool down, const timeval &time) +singleTouchHandler(int fd, FdHandler *data) { - nsEventStatus status = - sendKeyEventWithMsg(keyCode, down ? NS_KEY_DOWN : NS_KEY_UP, time, 0); - if (down) { - sendKeyEventWithMsg(keyCode, NS_KEY_PRESS, time, - status == nsEventStatus_eConsumeNoDefault ? - NS_EVENT_FLAG_NO_DEFAULT : 0); + // The Linux's input documentation (Documentation/input/input.txt) + // says that we'll always read a multiple of sizeof(input_event) bytes here. + input_event events[16]; + int event_count = read(fd, events, sizeof(events)); + if (event_count < 0) { + LOG("Error reading in singleTouchHandler"); + return; } -} + MOZ_ASSERT(event_count % sizeof(input_event) == 0); -static void -sendSpecialKeyEvent(nsIAtom *command, const timeval &time) -{ - nsCommandEvent event(true, nsGkAtoms::onAppCommand, command, NULL); - event.time = timevalToMS(time); - nsWindow::DispatchInputEvent(event); + event_count /= sizeof(struct input_event); + + for (int i = 0; i < event_count; i++) { + input_event *event = &events[i]; + + if (event->type == EV_KEY) { + switch (event->code) { + case BTN_TOUCH: + data->mtDown = event->value; + break; + default: + maybeSendKeyEvent(*event); + } + } + else if (event->type == EV_ABS) { + switch (event->code) { + case ABS_X: + data->mtX = event->value; + break; + case ABS_Y: + data->mtY = event->value; + break; + default: + LOG("Got unknown st abs event type 0x%04x with code 0x%04x and value %d", + event->type, event->code, event->value); + } + } else if (event->type == EV_SYN) { + if (data->mtState == FdHandler::MT_START) { + MOZ_ASSERT(data->mtDown); + sendMouseEvent(NS_MOUSE_BUTTON_DOWN, &event->time, + data->mtX, data->mtY); + data->mtState = FdHandler::MT_COLLECT; + } else if (data->mtDown) { + MOZ_ASSERT(data->mtDown); + sendMouseEvent(NS_MOUSE_MOVE, &event->time, + data->mtX, data->mtY); + } else { + MOZ_ASSERT(!data->mtDown); + sendMouseEvent(NS_MOUSE_BUTTON_UP, &event->time, + data->mtX, data->mtY); + data->mtDown = false; + data->mtState = FdHandler::MT_START; + } + } + } } static void @@ -269,50 +380,7 @@ keyHandler(int fd, FdHandler *data) continue; } - if (e.type != EV_KEY) { - LOG("Got unknown key event type. type 0x%04x code 0x%04x value %d", - e.type, e.code, e.value); - continue; - } - - if (e.value != 0 && e.value != 1) { - LOG("Got unknown key event value. type 0x%04x code 0x%04x value %d", - e.type, e.code, e.value); - continue; - } - - bool pressed = e.value == 1; - const char* upOrDown = pressed ? "pressed" : "released"; - switch (e.code) { - case KEY_BACK: - sendKeyEvent(NS_VK_ESCAPE, pressed, e.time); - break; - case KEY_MENU: - if (!pressed) - sendSpecialKeyEvent(nsGkAtoms::Menu, e.time); - break; - case KEY_SEARCH: - if (pressed) - sendSpecialKeyEvent(nsGkAtoms::Search, e.time); - break; - case KEY_HOME: - sendKeyEvent(NS_VK_HOME, pressed, e.time); - break; - case KEY_POWER: - sendKeyEvent(NS_VK_SLEEP, pressed, e.time); - break; - case KEY_VOLUMEUP: - if (pressed) - sendSpecialKeyEvent(nsGkAtoms::VolumeUp, e.time); - break; - case KEY_VOLUMEDOWN: - if (pressed) - sendSpecialKeyEvent(nsGkAtoms::VolumeDown, e.time); - break; - default: - LOG("Got unknown key event code. type 0x%04x code 0x%04x value %d", - e.type, e.code, e.value); - } + maybeSendKeyEvent(e); } } @@ -378,8 +446,12 @@ nsAppShell::Init() if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(flags)), flags) >= 0 && BITSET(ABS_MT_POSITION_X, flags)) { - LOG("Found absolute input device"); + LOG("Found multitouch input device"); handlerFunc = multitouchHandler; + } else if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(flags)), flags) >= 0 && + BITSET(ABS_X, flags)) { + LOG("Found single touch input device"); + handlerFunc = singleTouchHandler; } else if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(flags)), flags) >= 0) { LOG("Found key input device"); handlerFunc = keyHandler; diff --git a/widget/src/gonk/nsWindow.cpp b/widget/src/gonk/nsWindow.cpp index 266a7951ed7..48d149d4567 100644 --- a/widget/src/gonk/nsWindow.cpp +++ b/widget/src/gonk/nsWindow.cpp @@ -35,24 +35,27 @@ * * ***** END LICENSE BLOCK ***** */ +#include +#include + +#include "android/log.h" +#include "ui/FramebufferNativeWindow.h" + +#include "Framebuffer.h" +#include "gfxContext.h" +#include "gfxUtils.h" +#include "GLContextProvider.h" +#include "LayerManagerOGL.h" #include "nsAutoPtr.h" #include "nsAppShell.h" #include "nsTArray.h" #include "nsWindow.h" -#include -#include -#include "ui/FramebufferNativeWindow.h" - -#include "LayerManagerOGL.h" -#include "GLContextProvider.h" - -#include "android/log.h" - #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args) #define IS_TOPLEVEL() (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog) +using namespace mozilla; using namespace mozilla::gl; using namespace mozilla::layers; using namespace mozilla::widget; @@ -64,13 +67,29 @@ static nsTArray sTopWindows; static nsWindow *gWindowToRedraw = nsnull; static nsWindow *gFocusedWindow = nsnull; static android::FramebufferNativeWindow *gNativeWindow = nsnull; +static bool sFramebufferOpen; nsWindow::nsWindow() { - if (!sGLContext) { + if (!sGLContext && !sFramebufferOpen) { + // We (apparently) don't have a way to tell if allocating the + // fbs succeeded or failed. gNativeWindow = new android::FramebufferNativeWindow(); sGLContext = GLContextProvider::CreateForWindow(this); // CreateForWindow sets up gScreenBounds + if (!sGLContext) { + LOG("Failed to create GL context for fb, trying /dev/fb0"); + + // We can't delete gNativeWindow. + + nsIntSize screenSize; + sFramebufferOpen = Framebuffer::Open(&screenSize); + gScreenBounds = nsIntRect(nsIntPoint(0, 0), screenSize); + if (!sFramebufferOpen) { + LOG("Failed to mmap fb(?!?), aborting ..."); + NS_RUNTIMEABORT("Can't open GL context and can't fall back on /dev/fb0 ..."); + } + } } } @@ -81,14 +100,40 @@ nsWindow::~nsWindow() void nsWindow::DoDraw(void) { - if (!gWindowToRedraw) + if (!gWindowToRedraw) { + LOG(" no window to draw, bailing"); return; + } nsPaintEvent event(true, NS_PAINT, gWindowToRedraw); event.region = gScreenBounds; - static_cast(gWindowToRedraw->GetLayerManager(nsnull))-> - SetClippingRegion(nsIntRegion(gScreenBounds)); - gWindowToRedraw->mEventCallback(&event); + + LayerManager* lm = gWindowToRedraw->GetLayerManager(); + if (LayerManager::LAYERS_OPENGL == lm->GetBackendType()) { + static_cast(lm)-> + SetClippingRegion(nsIntRegion(gScreenBounds)); + gWindowToRedraw->mEventCallback(&event); + } else if (LayerManager::LAYERS_BASIC == lm->GetBackendType()) { + MOZ_ASSERT(sFramebufferOpen); + + nsRefPtr backBuffer = Framebuffer::BackBuffer(); + { + nsRefPtr ctx = new gfxContext(backBuffer); + ctx->NewPath(); + gfxUtils::PathFromRegion(ctx, event.region); + ctx->Clip(); + + // No double-buffering needed. + AutoLayerManagerSetup setupLayerManager( + gWindowToRedraw, ctx, BasicLayerManager::BUFFER_NONE); + gWindowToRedraw->mEventCallback(&event); + } + backBuffer->Flush(); + + Framebuffer::Present(); + } else { + NS_RUNTIMEABORT("Unexpected layer manager type"); + } } nsEventStatus @@ -243,8 +288,10 @@ nsWindow::Invalidate(const nsIntRect &aRect, nsWindow *parent = mParent; while (parent && parent != sTopWindows[0]) parent = parent->mParent; - if (parent != sTopWindows[0]) + if (parent != sTopWindows[0]) { + LOG(" parent isn't top window, bailing"); return NS_OK; + } gWindowToRedraw = this; gDrawRequest = true; @@ -337,19 +384,18 @@ nsWindow::GetLayerManager(PLayersChild* aShadowManager, return nsnull; } - if (!sGLContext) { - LOG(" -- no GLContext\n"); - return nsnull; + if (sGLContext) { + nsRefPtr layerManager = new LayerManagerOGL(this); + + if (layerManager->Initialize(sGLContext)) + mLayerManager = layerManager; + else + LOG("Could not create OGL LayerManager"); + } else { + MOZ_ASSERT(sFramebufferOpen); + mLayerManager = new BasicShadowLayerManager(this); } - nsRefPtr layerManager = - new LayerManagerOGL(this); - - if (layerManager->Initialize(sGLContext)) - mLayerManager = layerManager; - else - LOG("Could not create LayerManager"); - return mLayerManager; }