diff --git a/gyp/viewer.gyp b/gyp/viewer.gyp index 84daa8d4c5..1013e070ae 100644 --- a/gyp/viewer.gyp +++ b/gyp/viewer.gyp @@ -25,11 +25,13 @@ '../src/gpu', '../src/images', '../src/image', + '../src/views/unix', '../tools/timer', ], 'sources': [ '../gm/gm.cpp', '../src/views/SkTouchGesture.cpp', + '../src/views/unix/keysym2ucs.c', ' - -// Xlib.h may define these macros with common names (Grrr) -#ifdef Bool -# undef Bool -#endif -#ifdef Status -# undef Status -#endif -#ifdef True -# undef True -#endif -#ifdef False -# undef False -#endif - #endif diff --git a/src/gpu/vk/GrVkBackendContext.cpp b/src/gpu/vk/GrVkBackendContext.cpp index ae61aa5337..8b6c1dadc6 100644 --- a/src/gpu/vk/GrVkBackendContext.cpp +++ b/src/gpu/vk/GrVkBackendContext.cpp @@ -40,7 +40,9 @@ const uint32_t kGrVkMinimumVersion = VK_MAKE_VERSION(1, 0, 8); // Create the base Vulkan objects needed by the GrVkGpu object const GrVkBackendContext* GrVkBackendContext::Create(uint32_t* presentQueueIndexPtr, - bool(*canPresent)(VkInstance, VkPhysicalDevice, uint32_t queueIndex)) { + bool(*canPresent)(VkInstance, VkPhysicalDevice, uint32_t queueIndex, + void* platformData), + void* platformData) { VkPhysicalDevice physDev; VkDevice device; VkInstance inst; @@ -91,11 +93,11 @@ const GrVkBackendContext* GrVkBackendContext::Create(uint32_t* presentQueueIndex if (extensions.hasInstanceExtension(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME)) { instanceExtensionNames.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME); extensionFlags |= kKHR_android_surface_GrVkExtensionFlag; -} + } #elif SK_BUILD_FOR_UNIX - if (extensions.hasInstanceExtension(VK_KHR_XLIB_SURFACE_EXTENSION_NAME)) { - instanceExtensionNames.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); - extensionFlags |= kKHR_xlib_surface_GrVkExtensionFlag; + if (extensions.hasInstanceExtension(VK_KHR_XCB_SURFACE_EXTENSION_NAME)) { + instanceExtensionNames.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME); + extensionFlags |= kKHR_xcb_surface_GrVkExtensionFlag; } #endif @@ -159,7 +161,7 @@ const GrVkBackendContext* GrVkBackendContext::Create(uint32_t* presentQueueIndex uint32_t presentQueueIndex = graphicsQueueIndex; if (presentQueueIndexPtr && canPresent) { for (uint32_t i = 0; i < queueCount; i++) { - if (canPresent(inst, physDev, i)) { + if (canPresent(inst, physDev, i, platformData)) { presentQueueIndex = i; break; } diff --git a/tools/viewer/sk_app/VulkanWindowContext.cpp b/tools/viewer/sk_app/VulkanWindowContext.cpp index bc200de427..2e2b8a988c 100644 --- a/tools/viewer/sk_app/VulkanWindowContext.cpp +++ b/tools/viewer/sk_app/VulkanWindowContext.cpp @@ -41,8 +41,9 @@ VulkanWindowContext::VulkanWindowContext(void* platformData, const DisplayParams } void VulkanWindowContext::initializeContext(void* platformData, const DisplayParams& params) { + fBackendContext.reset(GrVkBackendContext::Create(&fPresentQueueIndex, canPresent, + platformData)); - fBackendContext.reset(GrVkBackendContext::Create(&fPresentQueueIndex, canPresent)); if (!(fBackendContext->fExtensions & kKHR_surface_GrVkExtensionFlag) || !(fBackendContext->fExtensions & kKHR_swapchain_GrVkExtensionFlag)) { fBackendContext.reset(nullptr); @@ -147,6 +148,7 @@ bool VulkanWindowContext::createSwapchain(uint32_t width, uint32_t height, } else if (extent.height > caps.maxImageExtent.height) { extent.height = caps.maxImageExtent.height; } + fWidth = (int)extent.width; fHeight = (int)extent.height; diff --git a/tools/viewer/sk_app/VulkanWindowContext.h b/tools/viewer/sk_app/VulkanWindowContext.h index 8480a9efa8..5071401175 100644 --- a/tools/viewer/sk_app/VulkanWindowContext.h +++ b/tools/viewer/sk_app/VulkanWindowContext.h @@ -23,7 +23,8 @@ public: // each platform will have to implement these in its CPP file static VkSurfaceKHR createVkSurface(VkInstance, void* platformData); - static bool canPresent(VkInstance, VkPhysicalDevice, uint32_t queueFamilyIndex); + static bool canPresent(VkInstance, VkPhysicalDevice, uint32_t queueFamilyIndex, + void* platformData); static VulkanWindowContext* Create(void* platformData, const DisplayParams& params) { VulkanWindowContext* ctx = new VulkanWindowContext(platformData, params); @@ -99,8 +100,6 @@ private: VkSwapchainKHR fSwapchain; uint32_t fPresentQueueIndex; VkQueue fPresentQueue; - int fWidth; - int fHeight; uint32_t fImageCount; VkImage* fImages; // images in the swapchain diff --git a/tools/viewer/sk_app/android/VulkanWindowContext_android.cpp b/tools/viewer/sk_app/android/VulkanWindowContext_android.cpp index 1ef994aa3f..555f8a692b 100644 --- a/tools/viewer/sk_app/android/VulkanWindowContext_android.cpp +++ b/tools/viewer/sk_app/android/VulkanWindowContext_android.cpp @@ -40,7 +40,7 @@ VkSurfaceKHR VulkanWindowContext::createVkSurface(VkInstance instance, void* pla } bool VulkanWindowContext::canPresent(VkInstance instance, VkPhysicalDevice physDev, - uint32_t queueFamilyIndex) { + uint32_t queueFamilyIndex, void*) { return true; } diff --git a/tools/viewer/sk_app/unix/VulkanWindowContext_unix.cpp b/tools/viewer/sk_app/unix/VulkanWindowContext_unix.cpp new file mode 100644 index 0000000000..ddfc8e3068 --- /dev/null +++ b/tools/viewer/sk_app/unix/VulkanWindowContext_unix.cpp @@ -0,0 +1,74 @@ + +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "../VulkanWindowContext.h" +#include "Window_unix.h" + +#include "vk/GrVkInterface.h" +#include "vk/GrVkUtil.h" + +#include + +namespace sk_app { + +// Platform dependant call +VkSurfaceKHR VulkanWindowContext::createVkSurface(VkInstance instance, void* platformData) { + static PFN_vkCreateXcbSurfaceKHR createXcbSurfaceKHR = nullptr; + if (!createXcbSurfaceKHR) { + createXcbSurfaceKHR = (PFN_vkCreateXcbSurfaceKHR) vkGetInstanceProcAddr(instance, + "vkCreateXcbSurfaceKHR"); + } + + if (!platformData) { + return VK_NULL_HANDLE; + } + ContextPlatformData_unix* unixPlatformData = + reinterpret_cast(platformData); + + + VkSurfaceKHR surface; + + VkXcbSurfaceCreateInfoKHR surfaceCreateInfo; + memset(&surfaceCreateInfo, 0, sizeof(VkXcbSurfaceCreateInfoKHR)); + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; + surfaceCreateInfo.pNext = nullptr; + surfaceCreateInfo.flags = 0; + surfaceCreateInfo.connection = XGetXCBConnection(unixPlatformData->fDisplay); + surfaceCreateInfo.window = unixPlatformData->fHWnd; + + VkResult res = createXcbSurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface); + if (VK_SUCCESS != res) { + return VK_NULL_HANDLE; + } + + return surface; +} + +// Platform dependant call +bool VulkanWindowContext::canPresent(VkInstance instance, VkPhysicalDevice physDev, + uint32_t queueFamilyIndex, void* platformData) { + static PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR + getPhysicalDeviceXcbPresentationSupportKHR = nullptr; + if (!getPhysicalDeviceXcbPresentationSupportKHR) { + getPhysicalDeviceXcbPresentationSupportKHR = + (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR) vkGetInstanceProcAddr(instance, + "vkGetPhysicalDeviceXcbPresentationSupportKHR"); + } + + ContextPlatformData_unix* unixPlatformData = + reinterpret_cast(platformData); + + Display* display = unixPlatformData->fDisplay; + VkBool32 check = getPhysicalDeviceXcbPresentationSupportKHR(physDev, + queueFamilyIndex, + XGetXCBConnection(display), + unixPlatformData->fVisualID); + return (VK_FALSE != check); +} + +} // namespace sk_app diff --git a/tools/viewer/sk_app/unix/Window_unix.cpp b/tools/viewer/sk_app/unix/Window_unix.cpp new file mode 100644 index 0000000000..953eb36d74 --- /dev/null +++ b/tools/viewer/sk_app/unix/Window_unix.cpp @@ -0,0 +1,221 @@ +/* +* Copyright 2016 Google Inc. +* +* Use of this source code is governed by a BSD-style license that can be +* found in the LICENSE file. +*/ + +//#include + +#include "SkUtils.h" +#include "Timer.h" +#include "../VulkanWindowContext.h" +#include "Window_unix.h" + +extern "C" { + #include "keysym2ucs.h" +} +#include +#include + +namespace sk_app { + +SkTDynamicHash Window_unix::gWindowMap; + +Window* Window::CreateNativeWindow(void* platformData) { + Display* display = (Display*)platformData; + + Window_unix* window = new Window_unix(); + if (!window->init(display)) { + delete window; + return nullptr; + } + + return window; +} + +bool Window_unix::init(Display* display) { + fDisplay = display; + + fWidth = 1280; + fHeight = 960; + fHWnd = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0, fWidth, fHeight, + 0, BlackPixel(display, DefaultScreen(display)), + BlackPixel(display, DefaultScreen(display))); + + if (!fHWnd) { + return false; + } + + // choose the events we care about + XSelectInput(display, fHWnd, + ExposureMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask | + PointerMotionMask | ButtonPressMask | ButtonReleaseMask); + + // set up to catch window delete message + fWmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False); + XSetWMProtocols(display, fHWnd, &fWmDeleteMessage, 1); + + // add to hashtable of windows + gWindowMap.add(this); + + // init event variables + fPendingPaint = false; + fPendingResize = false; + + return true; +} + +static Window::Key get_key(KeySym keysym) { + static const struct { + KeySym fXK; + Window::Key fKey; + } gPair[] = { + { XK_BackSpace, Window::Key::kBack }, + { XK_Clear, Window::Key::kBack }, + { XK_Return, Window::Key::kOK }, + { XK_Up, Window::Key::kUp }, + { XK_Down, Window::Key::kDown }, + { XK_Left, Window::Key::kLeft }, + { XK_Right, Window::Key::kRight } + }; + for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) { + if (gPair[i].fXK == keysym) { + return gPair[i].fKey; + } + } + return Window::Key::kNONE; +} + +static uint32_t get_modifiers(const XEvent& event) { + static const struct { + unsigned fXMask; + unsigned fSkMask; + } gModifiers[] = { + { ShiftMask, Window::kShift_ModifierKey }, + { ControlMask, Window::kControl_ModifierKey }, + { Mod1Mask, Window::kOption_ModifierKey }, + }; + + auto modifiers = 0; + for (size_t i = 0; i < SK_ARRAY_COUNT(gModifiers); ++i) { + if (event.xkey.state & gModifiers[i].fXMask) { + modifiers |= gModifiers[i].fSkMask; + } + } + return modifiers; +} + +bool Window_unix::handleEvent(const XEvent& event) { + switch (event.type) { + case ClientMessage: + if ((Atom)event.xclient.data.l[0] == fWmDeleteMessage && + gWindowMap.count() == 1) { + return true; + } + break; + + case ButtonPress: + if (event.xbutton.button == Button1) { + this->onMouse(event.xbutton.x, event.xbutton.y, + Window::kDown_InputState, get_modifiers(event)); + } + break; + + case ButtonRelease: + if (event.xbutton.button == Button1) { + this->onMouse(event.xbutton.x, event.xbutton.y, + Window::kUp_InputState, get_modifiers(event)); + } + break; + + case MotionNotify: + // only track if left button is down + if (event.xmotion.state & Button1Mask) { + this->onMouse(event.xmotion.x, event.xmotion.y, + Window::kMove_InputState, get_modifiers(event)); + } + break; + + case KeyPress: { + int shiftLevel = (event.xkey.state & ShiftMask) ? 1 : 0; + KeySym keysym = XkbKeycodeToKeysym(fDisplay, event.xkey.keycode, + 0, shiftLevel); + if (keysym == XK_Escape) { + return true; + } + Window::Key key = get_key(keysym); + if (key != Window::Key::kNONE) { + (void) this->onKey(key, Window::kDown_InputState, + get_modifiers(event)); + } else { + long uni = keysym2ucs(keysym); + if (uni != -1) { + (void) this->onChar((SkUnichar) uni, + get_modifiers(event)); + } + } + } break; + + case KeyRelease: { + int shiftLevel = (event.xkey.state & ShiftMask) ? 1 : 0; + KeySym keysym = XkbKeycodeToKeysym(fDisplay, event.xkey.keycode, + 0, shiftLevel); + Window::Key key = get_key(keysym); + (void) this->onKey(key, Window::kUp_InputState, + get_modifiers(event)); + } break; + + + default: + // these events should be handled in the main event loop + SkASSERT(event.type != Expose && event.type != ConfigureNotify); + break; + } + + return false; +} + +void Window_unix::setTitle(const char* title) { + XTextProperty textproperty; + XStringListToTextProperty(const_cast(&title), 1, &textproperty); + XSetWMName(fDisplay, fHWnd, &textproperty); +} + +void Window_unix::show() { + XMapWindow(fDisplay, fHWnd); +} + +bool Window_unix::attach(BackendType attachType, const DisplayParams& params) { + ContextPlatformData_unix platformData; + platformData.fDisplay = fDisplay; + platformData.fHWnd = fHWnd; + XWindowAttributes attribs; + XGetWindowAttributes(fDisplay, fHWnd, &attribs); + platformData.fVisualID = XVisualIDFromVisual(attribs.visual); + switch (attachType) { + case kVulkan_BackendType: + default: + fWindowContext = VulkanWindowContext::Create((void*)&platformData, params); + break; + } + + return (SkToBool(fWindowContext)); +} + +void Window_unix::onInval() { + XEvent event; + event.type = Expose; + event.xexpose.send_event = True; + event.xexpose.display = fDisplay; + event.xexpose.window = fHWnd; + event.xexpose.x = 0; + event.xexpose.y = 0; + event.xexpose.width = fWidth; + event.xexpose.height = fHeight; + event.xexpose.count = 0; + + XSendEvent(fDisplay, fHWnd, False, 0, &event); +} + +} // namespace sk_app diff --git a/tools/viewer/sk_app/unix/Window_unix.h b/tools/viewer/sk_app/unix/Window_unix.h new file mode 100644 index 0000000000..c2156fc6ae --- /dev/null +++ b/tools/viewer/sk_app/unix/Window_unix.h @@ -0,0 +1,86 @@ +/* +* Copyright 2016 Google Inc. +* +* Use of this source code is governed by a BSD-style license that can be +* found in the LICENSE file. +*/ + +#ifndef Window_unix_DEFINED +#define Window_unix_DEFINED + +#include +#include "../Window.h" +#include "SkChecksum.h" +#include "SkTDynamicHash.h" + +typedef Window XWindow; + +namespace sk_app { + +struct ContextPlatformData_unix { + Display* fDisplay; + XWindow fHWnd; + VisualID fVisualID; +}; + +class Window_unix : public Window { +public: + Window_unix() : Window() {} + ~Window_unix() override {} + + bool init(Display* display); + + void setTitle(const char*) override; + void show() override; + + bool attach(BackendType attachType, const DisplayParams& params) override; + + void onInval() override; + + bool handleEvent(const XEvent& event); + + static const XWindow& GetKey(const Window_unix& w) { + return w.fHWnd; + } + + static uint32_t Hash(const XWindow& w) { + return SkChecksum::Mix(w); + } + + static SkTDynamicHash gWindowMap; + + void markPendingPaint() { fPendingPaint = true; } + void finishPaint() { + if (fPendingPaint) { + this->onPaint(); + fPendingPaint = false; + } + } + + void markPendingResize(int width, int height) { + fPendingWidth = width; + fPendingHeight = height; + fPendingResize = true; + } + void finishResize() { + if (fPendingResize) { + this->onResize(fPendingWidth, fPendingHeight); + fPendingResize = false; + } + } + +private: + Display* fDisplay; + XWindow fHWnd; + + Atom fWmDeleteMessage; + + bool fPendingPaint; + int fPendingWidth; + int fPendingHeight; + bool fPendingResize; +}; + +} // namespace sk_app + +#endif diff --git a/tools/viewer/sk_app/unix/main_unix.cpp b/tools/viewer/sk_app/unix/main_unix.cpp new file mode 100644 index 0000000000..d595379174 --- /dev/null +++ b/tools/viewer/sk_app/unix/main_unix.cpp @@ -0,0 +1,90 @@ +/* +* Copyright 2016 Google Inc. +* +* Use of this source code is governed by a BSD-style license that can be +* found in the LICENSE file. +*/ + +#include "SkTypes.h" +#include "SkTHash.h" +#include "Timer.h" +#include "Window_unix.h" +#include "../Application.h" + +using sk_app::Application; + +static double now_ms() { return SkTime::GetNSecs() * 1e-6; } + +void finishWindow(sk_app::Window_unix* win) { + win->finishResize(); + win->finishPaint(); +} + +int main(int argc, char**argv) { + + Display* display = XOpenDisplay(nullptr); + + Application* app = Application::Create(argc, argv, (void*)display); + + double currentTime = 0.0; + double previousTime = 0.0; + + // Get the file descriptor for the X display + int x11_fd = ConnectionNumber(display); + fd_set in_fds; + + SkTHashSet pendingWindows; + bool done = false; + while (!done) { + // Create a file description set containing x11_fd + FD_ZERO(&in_fds); + FD_SET(x11_fd, &in_fds); + + // Set a sleep timer + struct timeval tv; + tv.tv_usec = 100; + tv.tv_sec = 0; + + // Wait for an event on the file descriptor or for timer expiration + (void) select(1, &in_fds, NULL, NULL, &tv); + + // Handle XEvents (if any) and flush the input + XEvent event; + while (XPending(display) && !done) { + XNextEvent(display, &event); + + sk_app::Window_unix* win = sk_app::Window_unix::gWindowMap.find(event.xany.window); + // paint and resize events get collapsed + switch (event.type) { + case Expose: + win->markPendingPaint(); + pendingWindows.add(win); + break; + case ConfigureNotify: + win->markPendingResize(event.xconfigurerequest.width, + event.xconfigurerequest.height); + pendingWindows.add(win); + break; + default: + if (win->handleEvent(event)) { + done = true; + } + break; + } + } + + pendingWindows.foreach(finishWindow); + if (pendingWindows.count() > 0) { + previousTime = currentTime; + currentTime = now_ms(); + app->onIdle(currentTime - previousTime); + } + pendingWindows.reset(); + } + + delete app; + + XCloseDisplay(display); + + return 0; +} diff --git a/tools/viewer/sk_app/win/VulkanWindowContext_win.cpp b/tools/viewer/sk_app/win/VulkanWindowContext_win.cpp index 460dba1da3..e4251b88cf 100644 --- a/tools/viewer/sk_app/win/VulkanWindowContext_win.cpp +++ b/tools/viewer/sk_app/win/VulkanWindowContext_win.cpp @@ -47,7 +47,7 @@ VkSurfaceKHR VulkanWindowContext::createVkSurface(VkInstance instance, void* pla // Platform dependant call bool VulkanWindowContext::canPresent(VkInstance instance, VkPhysicalDevice physDev, - uint32_t queueFamilyIndex) { + uint32_t queueFamilyIndex, void*) { static PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR getPhysicalDeviceWin32PresentationSupportKHR = nullptr; if (!getPhysicalDeviceWin32PresentationSupportKHR) {