| /* |
| * 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_1: |
| format = CAIRO_FORMAT_A1; |
| 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; |
| } |