Bug 708154: Add a fallback non-GL rendering patch to the gonk widget backend and add support for the qemu touch device. r=kmachulis,gal pending-r=mwu

This commit is contained in:
Chris Jones 2011-12-23 23:13:33 -08:00
Родитель 5c2ad9ace8
Коммит ee006217e9
5 изменённых файлов: 481 добавлений и 98 удалений

Просмотреть файл

@ -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 <jones.chris.g@gmail.com>
*
* 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 <fcntl.h>
#include <linux/fb.h>
#include <linux/kd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <vector>
#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<nsRefPtr<gfxImageSurface> > 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<unsigned char*>(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

Просмотреть файл

@ -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 <jones.chris.g@gmail.com>
*
* 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<gfxASurface> 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

Просмотреть файл

@ -52,6 +52,7 @@ LIBXUL_LIBRARY = 1
CPPSRCS = \
Framebuffer.cpp \
nsAppShell.cpp \
nsWidgetFactory.cpp \
nsWindow.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;

Просмотреть файл

@ -35,24 +35,27 @@
*
* ***** END LICENSE BLOCK ***** */
#include <EGL/egl.h>
#include <EGL/eglext.h>
#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 <EGL/egl.h>
#include <EGL/eglext.h>
#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<nsWindow *> 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<LayerManagerOGL*>(gWindowToRedraw->GetLayerManager(nsnull))->
SetClippingRegion(nsIntRegion(gScreenBounds));
gWindowToRedraw->mEventCallback(&event);
LayerManager* lm = gWindowToRedraw->GetLayerManager();
if (LayerManager::LAYERS_OPENGL == lm->GetBackendType()) {
static_cast<LayerManagerOGL*>(lm)->
SetClippingRegion(nsIntRegion(gScreenBounds));
gWindowToRedraw->mEventCallback(&event);
} else if (LayerManager::LAYERS_BASIC == lm->GetBackendType()) {
MOZ_ASSERT(sFramebufferOpen);
nsRefPtr<gfxASurface> backBuffer = Framebuffer::BackBuffer();
{
nsRefPtr<gfxContext> 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<LayerManagerOGL> 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<LayerManagerOGL> layerManager =
new LayerManagerOGL(this);
if (layerManager->Initialize(sGLContext))
mLayerManager = layerManager;
else
LOG("Could not create LayerManager");
return mLayerManager;
}