blob: d0ef39a5391b0f17202004035239e0b82a90fe6a [file] [log] [blame]
/*
* Copyright 2016 The Fuchsia Authors. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cmakeconfig.h"
#include <algorithm>
#include <chrono>
#include <iostream>
#include <map>
#include <cairo/cairo.h>
#include <fcntl.h>
#include <math.h>
#include <assert.h>
#include <dirent.h>
#include <hid/hid.h>
#include <hid/usages.h>
#include <zircon/device/console.h>
#include <zircon/device/display.h>
#include <zircon/pixelformat.h>
#include <zircon/process.h>
#include <zircon/syscalls.h>
#include <zircon/types.h>
#include <lib/fdio/io.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <WebKit/fuchsia/WebView.h>
#include "FuchsiaCursor.h"
#include "FuchsiaInputHandler.h"
using namespace WebCore;
using std::endl;
using std::cout;
using std::cerr;
using Fuchsia::Event;
using Fuchsia::MouseEvent;
using Fuchsia::KeyEvent;
#define USE_INPUT 1
constexpr float kMinimumZoomMultiplier = 0.5f;
constexpr float kMaximumZoomMultiplier = 3.0f;
constexpr float kZoomMultiplierRatio = 1.2f;
// TODO: replace the hard coded device 000 with some method of getting
// the first device.
constexpr char kVirtualConsole[] = "/dev/class/framebuffer/000";
int main(int argc, char** argv)
{
std::string urlToOpen = "file:///system/docs/input.html";
if (argc > 1) {
urlToOpen = argv[1];
if (urlToOpen[0] == '/') {
urlToOpen = "file://" + urlToOpen;
}
}
cout << "Welcome to MiniBrowser" << endl;
int fd = open(kVirtualConsole, O_RDWR);
if (fd < 0) {
cout << "Failed to open frame buffer:" << errno << endl;
return -1;
}
ssize_t result = fdio_ioctl(fd, IOCTL_CONSOLE_SET_ACTIVE_VC, NULL, 0, NULL, 0);
if (result < 0) {
cout << "could not set active console: " << result << endl;
cout << "press f1/f2 to switch consoles" << endl;
}
ioctl_display_get_fb_t frameBuffer;
result = fdio_ioctl(fd, IOCTL_DISPLAY_GET_FB, nullptr, 0, &frameBuffer,
sizeof(frameBuffer));
if (result < 0) {
cout << "DISPLAY_OP_GET_FB failed.";
close(fd);
return -1;
}
#if 1
uint32_t beFull = 1;
ssize_t resultFs = fdio_ioctl(fd, IOCTL_DISPLAY_SET_FULLSCREEN, &beFull, sizeof(beFull), NULL, 0);
if (resultFs < 0) {
cout << "could not set full screen: " << resultFs << endl;
}
#endif
size_t rowbytes = frameBuffer.info.stride * frameBuffer.info.pixelsize;
size_t size = rowbytes * frameBuffer.info.height;
uintptr_t buffer = 0;
zx_status_t status = zx_vmar_map_old(zx_vmar_root_self(), 0, frameBuffer.vmo, 0, size,
ZX_VM_FLAG_PERM_READ | ZX_VM_FLAG_PERM_WRITE, &buffer);
if (status < 0) {
cout << "Cannot map frame buffer " << status << endl;
return -1;
}
auto cursorSurface = cairo_image_surface_create_from_png("/system/docs/cursor32.png");
int w = cairo_image_surface_get_width(cursorSurface);
int h = cairo_image_surface_get_height(cursorSurface);
cout << "cursor size is " << w << ", " << h << endl;
cairo_format_t format = CAIRO_FORMAT_INVALID;
switch (frameBuffer.info.format) {
case ZX_PIXEL_FORMAT_RGB_565:
format = CAIRO_FORMAT_RGB16_565;
break;
case ZX_PIXEL_FORMAT_RGB_x888:
format = CAIRO_FORMAT_RGB24;
break;
case ZX_PIXEL_FORMAT_ARGB_8888:
format = CAIRO_FORMAT_ARGB32;
break;
case ZX_PIXEL_FORMAT_MONO_8:
format = CAIRO_FORMAT_A8;
break;
default:
cout << "Unsupported pixel format " << frameBuffer.info.format << endl;
return -1;
}
#if USE_INPUT
Fuchsia::InputHandler inputHandler;
inputHandler.openDevices();
std::map<int, Fuchsia::Cursor> cursors;
#endif
mkdir("/data/web_view", 0777);
WebView webView;
webView.setup(reinterpret_cast<unsigned char*>(buffer), frameBuffer.info.format, frameBuffer.info.width, frameBuffer.info.height, rowbytes);
webView.setURL(urlToOpen);
webView.setFocused(true);
webView.setVisible(true);
bool shiftDown = false;
bool controlDown = false;
float webScale = 1.0;
while (true) {
#if USE_INPUT
if (inputHandler.hasEvents()) {
auto events = inputHandler.getPendingEvents();
for (const auto& oneEvent : events) {
if (oneEvent->type() == Event::kMouse) {
MouseEvent* mouseEvent = static_cast<MouseEvent*>(oneEvent.get());
auto cursorP = cursors.find(mouseEvent->eventSource());
if (cursorP == cursors.end()) {
auto insertResult = cursors.insert(std::make_pair(mouseEvent->eventSource(),
Fuchsia::Cursor(frameBuffer.info.width, frameBuffer.info.height)));
cursorP = insertResult.first;
}
auto actions = cursorP->second.handleEvent(*mouseEvent);
for (const auto& actionPair : actions) {
if (actionPair.first == 0) {
if (actionPair.second == Fuchsia::Cursor::ButtonAction::kDown) {
webView.handleMouseEvent(cursorP->second.getX(), cursorP->second.getY(), WebView::kMouseDown);
} else if (actionPair.second == Fuchsia::Cursor::ButtonAction::kUp) {
webView.handleMouseEvent(cursorP->second.getX(), cursorP->second.getY(), WebView::kMouseUp);
} else {
webView.handleMouseEvent(cursorP->second.getX(), cursorP->second.getY(), WebView::kMouseMoved);
}
}
}
} else if (oneEvent->type() == Event::kKey) {
KeyEvent* keyEvent = static_cast<KeyEvent*>(oneEvent.get());
uint8_t keycode = keyEvent->keycode();
bool pressed = keyEvent->pressed();
if (keycode == HID_USAGE_KEY_LEFT_SHIFT || keycode == HID_USAGE_KEY_RIGHT_SHIFT) {
shiftDown = pressed;
} else if (keycode == HID_USAGE_KEY_LEFT_CTRL || keycode == HID_USAGE_KEY_RIGHT_CTRL) {
controlDown = pressed;
}
if (controlDown && keycode == HID_USAGE_KEY_C) {
cout << "Exit requested." << endl;
exit(0);
}
uint8_t ch = hid_map_key(keycode, shiftDown, qwerty_map);
if (pressed && controlDown && ch == '+') {
float newScale = webScale * kZoomMultiplierRatio;
if (newScale < kMaximumZoomMultiplier) {
webScale = newScale;
webView.setPageAndTextZoomFactors(webScale, 1);
}
} else if (pressed && controlDown && ch == '-') {
float newScale = webScale / kZoomMultiplierRatio;
if (newScale > kMinimumZoomMultiplier) {
webScale = newScale;
webView.setPageAndTextZoomFactors(webScale, 1);
}
} else if (pressed && controlDown && keycode == HID_USAGE_KEY_LEFTBRACE) {
webView.goBack();
} else if (pressed && controlDown && keycode == HID_USAGE_KEY_RIGHTBRACE) {
webView.goForward();
} else if (pressed && controlDown && keycode == HID_USAGE_KEY_R) {
webView.reload();
} else if (pressed && controlDown && keycode == HID_USAGE_KEY_D) {
webView.deleteAllCookies();
} else {
bool handled = webView.handleKeyEvent(keycode, ch, pressed, false);
if (!handled) {
if (keycode == HID_USAGE_KEY_DOWN) {
webView.scrollDownOneLine();
} else if (keycode == HID_USAGE_KEY_UP) {
webView.scrollUpOneLine();
}
}
}
}
}
}
#endif
webView.iterateEventLoop();
webView.layoutAndPaint();
#if USE_INPUT
for (const auto& cursor : cursors) {
cairo_set_source_surface(webView.cairoContext(), cursorSurface, cursor.second.getX(), cursor.second.getY());
cairo_paint(webView.cairoContext());
}
#endif
result = fdio_ioctl(fd, IOCTL_DISPLAY_FLUSH_FB, nullptr, 0, nullptr, 0);
if (result < 0) {
cout << "IOCTL_DISPLAY_FLUSH_FB failed.";
close(fd);
return -1;
}
}
return 0;
}