diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a9b0c96ce..7d9d2e2f28 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,9 +48,23 @@ endif() set(VSG_WARNING_FLAGS ${VSG_WARNING_FLAGS} CACHE STRING "Compiler flags to use." FORCE) add_compile_options(${VSG_WARNING_FLAGS}) -find_package(glfw3) find_package(Vulkan) +if (ANDROID) + # TODO +elseif (WIN32) + # just use native windowing +elseif (APPLE) + find_package(glfw3) +else() + find_package(glfw3) + + find_package(PkgConfig) + pkg_check_modules(xcb REQUIRED IMPORTED_TARGET xcb) + option(USE_GLFW "Use GLFW for Windowing" OFF) +endif() + + # create doxygen build target find_package(Doxygen QUIET) diff --git a/include/vsg/core/ConstVisitor.h b/include/vsg/core/ConstVisitor.h index a994479a7e..c68cd8343a 100644 --- a/include/vsg/core/ConstVisitor.h +++ b/include/vsg/core/ConstVisitor.h @@ -34,12 +34,16 @@ namespace vsg // forward declare ui events classes class UIEvent; + class WindowEvent; + class ExposeWindowEvent; + class DeleteWindowEvent; class KeyEvent; class KeyPressEvent; class KeyReleaseEvent; class PointerEvent; class ButtonPressEvent; class ButtonReleaseEvent; + class MoveEvent; class VSG_DECLSPEC ConstVisitor : public Object { @@ -120,12 +124,16 @@ namespace vsg // ui events virtual void apply(const UIEvent&); + virtual void apply(const WindowEvent&); + virtual void apply(const ExposeWindowEvent&); + virtual void apply(const DeleteWindowEvent&); virtual void apply(const KeyEvent&); virtual void apply(const KeyPressEvent&); virtual void apply(const KeyReleaseEvent&); virtual void apply(const PointerEvent&); virtual void apply(const ButtonPressEvent&); virtual void apply(const ButtonReleaseEvent&); + virtual void apply(const MoveEvent&); }; // provide Value<>::accept() implementation diff --git a/include/vsg/core/Visitor.h b/include/vsg/core/Visitor.h index 95cf197aee..8eaf122bee 100644 --- a/include/vsg/core/Visitor.h +++ b/include/vsg/core/Visitor.h @@ -34,12 +34,16 @@ namespace vsg // forward declare ui events classes class UIEvent; + class WindowEvent; + class ExposeWindowEvent; + class DeleteWindowEvent; class KeyEvent; class KeyPressEvent; class KeyReleaseEvent; class PointerEvent; class ButtonPressEvent; class ButtonReleaseEvent; + class MoveEvent; class VSG_DECLSPEC Visitor : public Object { @@ -120,12 +124,16 @@ namespace vsg // ui events virtual void apply(UIEvent&); + virtual void apply(WindowEvent&); + virtual void apply(ExposeWindowEvent&); + virtual void apply(DeleteWindowEvent&); virtual void apply(KeyEvent&); virtual void apply(KeyPressEvent&); virtual void apply(KeyReleaseEvent&); virtual void apply(PointerEvent&); virtual void apply(ButtonPressEvent&); virtual void apply(ButtonReleaseEvent&); + virtual void apply(MoveEvent&); }; // provide Value<>::accept() implementation diff --git a/include/vsg/ui/KeyEvent.h b/include/vsg/ui/KeyEvent.h index 97514080ab..1edf4cfb3f 100644 --- a/include/vsg/ui/KeyEvent.h +++ b/include/vsg/ui/KeyEvent.h @@ -13,7 +13,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI */ #include -#include +#include #include namespace vsg @@ -264,18 +264,16 @@ namespace vsg MODKEY_Meta = 128 }; - class KeyEvent : public Inherit + class KeyEvent : public Inherit { public: KeyEvent(Window* in_window, time_point in_time, KeySymbol in_keyBase, KeySymbol in_keyModified, KeyModifier in_modifier, uint32_t in_repeatCount=0): - Inherit(in_time), - window(in_window), + Inherit(in_window, in_time), keyBase(in_keyBase), keyModified(in_keyModified), keyModifier(in_modifier), repeatCount(in_repeatCount) {} - observer_ptr window; KeySymbol keyBase; KeySymbol keyModified; KeyModifier keyModifier; diff --git a/include/vsg/ui/PointerEvent.h b/include/vsg/ui/PointerEvent.h index bbd8f9e1a5..a6da8fa2c4 100644 --- a/include/vsg/ui/PointerEvent.h +++ b/include/vsg/ui/PointerEvent.h @@ -13,7 +13,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI */ #include -#include +#include #include namespace vsg @@ -30,33 +30,44 @@ namespace vsg // VSG_type_name(vsg::PointerEvent); - class PointerEvent : public Inherit + class PointerEvent : public Inherit { public: PointerEvent(Window* in_window, time_point in_time, uint32_t in_x, uint32_t in_y, ButtonMask in_buttonMask): - Inherit(in_time), - window(in_window), + Inherit(in_window, in_time), x(in_x), y(in_y), - buttonMask(in_buttonMask) {} + mask(in_buttonMask) {} - observer_ptr window; uint32_t x; uint32_t y; - ButtonMask buttonMask; + ButtonMask mask; }; class ButtonPressEvent : public Inherit { public: - ButtonPressEvent(Window* in_window, time_point in_time, uint32_t in_x, uint32_t in_y, ButtonMask in_buttonMask): - Inherit(in_window, in_time, in_x, in_y, in_buttonMask) {} + ButtonPressEvent(Window* in_window, time_point in_time, uint32_t in_x, uint32_t in_y, ButtonMask in_buttonMask, uint32_t in_button): + Inherit(in_window, in_time, in_x, in_y, in_buttonMask), + button(in_button) {} + + uint32_t button; }; class ButtonReleaseEvent : public Inherit { public: - ButtonReleaseEvent(Window* in_window, time_point in_time, uint32_t in_x, uint32_t in_y, ButtonMask in_buttonMask): + ButtonReleaseEvent(Window* in_window, time_point in_time, uint32_t in_x, uint32_t in_y, ButtonMask in_buttonMask, uint32_t in_button): + Inherit(in_window, in_time, in_x, in_y, in_buttonMask), + button(in_button) {} + + uint32_t button; + }; + + class MoveEvent : public Inherit + { + public: + MoveEvent(Window* in_window, time_point in_time, uint32_t in_x, uint32_t in_y, ButtonMask in_buttonMask): Inherit(in_window, in_time, in_x, in_y, in_buttonMask) {} }; diff --git a/include/vsg/ui/UIEvent.h b/include/vsg/ui/UIEvent.h index d58312e0cf..f8fd1d375f 100644 --- a/include/vsg/ui/UIEvent.h +++ b/include/vsg/ui/UIEvent.h @@ -15,6 +15,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include +#include namespace vsg { @@ -31,5 +32,5 @@ namespace vsg time_point time; }; - + using Events = std::list>; } diff --git a/include/vsg/ui/WindowEvent.h b/include/vsg/ui/WindowEvent.h new file mode 100644 index 0000000000..cfea5cb32f --- /dev/null +++ b/include/vsg/ui/WindowEvent.h @@ -0,0 +1,56 @@ +#pragma once + +/* + +Copyright(c) 2018 Robert Osfield + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + */ + +#include +#include +#include + +namespace vsg +{ + + class WindowEvent : public Inherit + { + public: + WindowEvent(Window* in_window, time_point in_time): + Inherit(in_time), + window(in_window) {} + + observer_ptr window; + }; + + class ExposeWindowEvent : public Inherit + { + public: + ExposeWindowEvent(Window* in_window, time_point in_time, int32_t in_x, int32_t in_y, uint32_t in_width, uint32_t in_height): + Inherit(in_window, in_time), + x(in_x), + y(in_y), + width(in_width), + height(in_height) {} + + int x = 0; + int y = 0; + int width = 0; + int height = 0; + }; + + class DeleteWindowEvent : public Inherit + { + public: + DeleteWindowEvent(Window* in_window, time_point in_time): + Inherit(in_window, in_time) {} + }; + + +} diff --git a/include/vsg/viewer/Window.h b/include/vsg/viewer/Window.h index 055d8718ad..f78b0e0fd8 100644 --- a/include/vsg/viewer/Window.h +++ b/include/vsg/viewer/Window.h @@ -14,6 +14,8 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include +#include + #include #include #include @@ -21,6 +23,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include + namespace vsg { @@ -32,13 +35,15 @@ namespace vsg struct Traits { - uint32_t x = 0; - uint32_t y = 0; + uint32_t x = 100; + uint32_t y = 100; uint32_t width = 800; uint32_t height = 600; uint32_t screenNum = 0; - std::string title = "vsg window"; + std::string windowClass = "vsg::Window"; + std::string windowTitle = "vsg window"; + bool decoration = true; Window* shareWindow = nullptr; @@ -51,7 +56,7 @@ namespace vsg virtual bool valid() const { return false; } - virtual bool pollEvents() { return false; } + virtual bool pollEvents(Events& /*events*/) { return false; } virtual bool resized() const { return false; } virtual void resize() {} diff --git a/src/vsg/CMakeLists.txt b/src/vsg/CMakeLists.txt index 0a4f7f2d5b..a2da75c25e 100644 --- a/src/vsg/CMakeLists.txt +++ b/src/vsg/CMakeLists.txt @@ -79,10 +79,24 @@ set(SOURCES ) # add platform specific Window implementation -if(WIN32) +# set up library dependancies +set(LIBRARIES PUBLIC Vulkan::Vulkan) + +if (ANDROID) + # TODO +elseif (WIN32) set(SOURCES ${SOURCES} viewer/Win32_Window.cpp) -else() +elseif (APPLE) set(SOURCES ${SOURCES} viewer/GLFW_Window.cpp) + set(LIBRARIES ${LIBRARIES} PRIVATE glfw) +else() + if (USE_GLFW) + set(SOURCES ${SOURCES} viewer/GLFW_Window.cpp) + set(LIBRARIES ${LIBRARIES} PRIVATE glfw) + else() + set(SOURCES ${SOURCES} viewer/Xcb_Window.cpp) + set(LIBRARIES ${LIBRARIES} PRIVATE PkgConfig::xcb) + endif() endif() @@ -142,14 +156,6 @@ set_property(TARGET vsg PROPERTY CXX_STANDARD 17) target_include_directories(vsg PUBLIC $ $) -# set up library dependancies -set(LIBRARIES PUBLIC Vulkan::Vulkan) - -if(WIN32) - set(LIBRARIES ${LIBRARIES}) -else() - set(LIBRARIES ${LIBRARIES} PRIVATE glfw) -endif() target_link_libraries(vsg ${LIBRARIES}) diff --git a/src/vsg/core/ConstVisitor.cpp b/src/vsg/core/ConstVisitor.cpp index 31bcd3307b..2dcf37b9b2 100644 --- a/src/vsg/core/ConstVisitor.cpp +++ b/src/vsg/core/ConstVisitor.cpp @@ -305,10 +305,22 @@ void ConstVisitor::apply(const UIEvent& event) { apply(static_cast(event)); } -void ConstVisitor::apply(const KeyEvent& event) +void ConstVisitor::apply(const WindowEvent& event) { apply(static_cast(event)); } +void ConstVisitor::apply(const ExposeWindowEvent& event) +{ + apply(static_cast(event)); +} +void ConstVisitor::apply(const DeleteWindowEvent& event) +{ + apply(static_cast(event)); +} +void ConstVisitor::apply(const KeyEvent& event) +{ + apply(static_cast(event)); +} void ConstVisitor::apply(const KeyPressEvent& event) { apply(static_cast(event)); @@ -319,7 +331,7 @@ void ConstVisitor::apply(const KeyReleaseEvent& event) } void ConstVisitor::apply(const PointerEvent& event) { - apply(static_cast(event)); + apply(static_cast(event)); } void ConstVisitor::apply(const ButtonPressEvent& event) { @@ -329,3 +341,7 @@ void ConstVisitor::apply(const ButtonReleaseEvent& event) { apply(static_cast(event)); } +void ConstVisitor::apply(const MoveEvent& event) +{ + apply(static_cast(event)); +} diff --git a/src/vsg/core/Visitor.cpp b/src/vsg/core/Visitor.cpp index 302cde840a..b7dbe0723c 100644 --- a/src/vsg/core/Visitor.cpp +++ b/src/vsg/core/Visitor.cpp @@ -305,10 +305,22 @@ void Visitor::apply(UIEvent& event) { apply(static_cast(event)); } -void Visitor::apply(KeyEvent& event) +void Visitor::apply(WindowEvent& event) { apply(static_cast(event)); } +void Visitor::apply(ExposeWindowEvent& event) +{ + apply(static_cast(event)); +} +void Visitor::apply(DeleteWindowEvent& event) +{ + apply(static_cast(event)); +} +void Visitor::apply(KeyEvent& event) +{ + apply(static_cast(event)); +} void Visitor::apply(KeyPressEvent& event) { apply(static_cast(event)); @@ -319,7 +331,7 @@ void Visitor::apply(KeyReleaseEvent& event) } void Visitor::apply(PointerEvent& event) { - apply(static_cast(event)); + apply(static_cast(event)); } void Visitor::apply(ButtonPressEvent& event) { @@ -329,4 +341,8 @@ void Visitor::apply(ButtonReleaseEvent& event) { apply(static_cast(event)); } +void Visitor::apply(MoveEvent& event) +{ + apply(static_cast(event)); +} diff --git a/src/vsg/viewer/GLFW_Window.cpp b/src/vsg/viewer/GLFW_Window.cpp index bb3c147f8a..1d20aad733 100644 --- a/src/vsg/viewer/GLFW_Window.cpp +++ b/src/vsg/viewer/GLFW_Window.cpp @@ -18,6 +18,16 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI using namespace glfw; +namespace vsg +{ + // Provide the Window::create(...) implementation that automatically maps to a GLFW_Window + Window::Result Window::create(const Window::Traits& traits, bool debugLayer, bool apiDumpLayer, vsg::AllocationCallbacks* allocator) + { + ref_ptr window = glfw::GLFW_Window::create(traits.width, traits.height, debugLayer, apiDumpLayer, traits.shareWindow, allocator); + return Result(window); + } +} + vsg::Names glfw::getInstanceExtensions() { uint32_t glfw_count; @@ -163,7 +173,7 @@ GLFW_Window::~GLFW_Window() } } -bool GLFW_Window::pollEvents() +bool GLFW_Window::pollEvents(vsg::Events& /*events*/) { glfwPollEvents(); return false; diff --git a/src/vsg/viewer/GLFW_Window.h b/src/vsg/viewer/GLFW_Window.h index 3010b1fac4..26e34b35f1 100644 --- a/src/vsg/viewer/GLFW_Window.h +++ b/src/vsg/viewer/GLFW_Window.h @@ -34,13 +34,13 @@ class GLFW_Window : public vsg::Window static Result create(uint32_t width, uint32_t height, bool debugLayer=false, bool apiDumpLayer=false, vsg::Window* shareWindow=nullptr, vsg::AllocationCallbacks* allocator=nullptr); - virtual bool valid() const { return _window && !glfwWindowShouldClose(_window); } + bool valid() const override { return _window && !glfwWindowShouldClose(_window); } - virtual bool pollEvents(); + bool pollEvents(vsg::Events& events) override; - virtual bool resized() const; + bool resized() const override; - virtual void resize(); + void resize() override; operator GLFWwindow* () { return _window; } operator const GLFWwindow* () const { return _window; } diff --git a/src/vsg/viewer/Viewer.cpp b/src/vsg/viewer/Viewer.cpp index 37109cd700..775023daa2 100644 --- a/src/vsg/viewer/Viewer.cpp +++ b/src/vsg/viewer/Viewer.cpp @@ -73,13 +73,79 @@ bool Viewer::done() const return false; } +#include +#include + bool Viewer::pollEvents() { bool result = false; + Events events; for (auto& window : _windows) { - if (window->pollEvents()) result = true; + if (window->pollEvents(events)) result = true; } + + if (result) + { + struct PrintEvents : public vsg::Visitor + { + vsg::clock::time_point start_point; + bool& close; + + PrintEvents(vsg::clock::time_point in_start_point, bool& in_close) : start_point(in_start_point), close(in_close) {} + + void apply(vsg::UIEvent& event) + { + std::cout<<"event : "<(event.time - start_point).count()<(event.time - start_point).count()<<" "<(event.time - start_point).count()<(keyRelease.time - start_point).count()<<", "<(keyPress.time - start_point).count()<<", "<(buttonPress.time - start_point).count()<<", "<(buttonRelease.time - start_point).count()<<", "<(move.time - start_point).count()<<", "<accept(print); + } + } + return result; } diff --git a/src/vsg/viewer/Win32_Window.cpp b/src/vsg/viewer/Win32_Window.cpp index e0930e7c89..a4448aca71 100644 --- a/src/vsg/viewer/Win32_Window.cpp +++ b/src/vsg/viewer/Win32_Window.cpp @@ -20,14 +20,21 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI using namespace vsg; using namespace vsgWin32; -namespace vsgWin32 +namespace vsg { - const std::string kWindowClassName = "vsg_Win32_Window_Class"; + // Provide the Window::create(...) implementation that automatically maps to a GLFW_Window + Window::Result Window::create(const Window::Traits& traits, bool debugLayer, bool apiDumpLayer, vsg::AllocationCallbacks* allocator) + { + ref_ptr window = vsgWin32::Win32_Window::create(traits, debugLayer, apiDumpLayer, allocator); + } +} +namespace vsgWin32 +{ vsg::Names vsgWin32::getInstanceExtensions() { // check the extensions are avaliable first - Names requiredExtensions = {"VK_KHR_surface", "VK_KHR_win32_surface"}; + Names requiredExtensions = {VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_WIN32_SURFACE_EXTENSION_NAME}; if (!vsg::isExtensionListSupported(requiredExtensions)) { @@ -95,7 +102,7 @@ Win32_Window::Result Win32_Window::create(const Traits& traits, bool debugLayer, wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = 0; wc.lpszMenuName = 0; - wc.lpszClassName = kWindowClassName.c_str(); + wc.lpszClassName = traits.windowClass.c_str(); wc.hIconSm = 0; if (::RegisterClassEx(&wc) == 0) @@ -142,7 +149,7 @@ Win32_Window::Result Win32_Window::create(const Traits& traits, bool debugLayer, if (!::AdjustWindowRectEx(&windowRect, windowStyle, FALSE, extendedStyle)) return Result("Error: vsg::Win32_Window::create(...) failed to create Window, AdjustWindowRectEx failed.", VK_ERROR_INVALID_EXTERNAL_HANDLE); // create the window - hwnd = ::CreateWindowEx(extendedStyle, kWindowClassName.c_str(), traits.title.c_str(), windowStyle, + hwnd = ::CreateWindowEx(extendedStyle, traits.windowClass.c_str(), traits.windowTitle.c_str(), windowStyle, windowRect.left, windowRect.top, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, NULL, NULL, ::GetModuleHandle(NULL), NULL); @@ -237,11 +244,11 @@ Win32_Window::~Win32_Window() _window = nullptr; // when should we unregister?? - ::UnregisterClass(kWindowClassName.c_str(), ::GetModuleHandle(NULL)); + ::UnregisterClass(traits.windowClass.c_str(), ::GetModuleHandle(NULL)); } } -bool Win32_Window::pollEvents() +bool Win32_Window::pollEvents(vsg::Events& events) { MSG msg; diff --git a/src/vsg/viewer/Win32_Window.h b/src/vsg/viewer/Win32_Window.h index da6506a79f..2a3e0c8515 100644 --- a/src/vsg/viewer/Win32_Window.h +++ b/src/vsg/viewer/Win32_Window.h @@ -35,13 +35,13 @@ namespace vsgWin32 static Result create(const Traits& traits, bool debugLayer = false, bool apiDumpLayer = false, vsg::AllocationCallbacks* allocator = nullptr); - virtual bool valid() const { return _window && !_shouldClose; } + bool valid() const override { return _window && !_shouldClose; } - virtual bool pollEvents(); + bool pollEvents(vsg::Events& events) override; - virtual bool resized() const; + bool resized() const override; - virtual void resize(); + void resize() override; operator HWND () { return _window; } operator const HWND () const { return _window; } diff --git a/src/vsg/viewer/Window.cpp b/src/vsg/viewer/Window.cpp index 252e4dbd33..0627f1880d 100644 --- a/src/vsg/viewer/Window.cpp +++ b/src/vsg/viewer/Window.cpp @@ -16,12 +16,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include -#ifdef _WIN32 -# include "Win32_Window.h" -#else -# include "GLFW_Window.h" -#endif - namespace vsg { @@ -173,14 +167,4 @@ namespace vsg return create(traits, debugLayer, apiDumpLayer, allocator); } - Window::Result Window::create(const Window::Traits& traits, bool debugLayer, bool apiDumpLayer, vsg::AllocationCallbacks* allocator) - { -#ifdef _WIN32 - ref_ptr window = vsgWin32::Win32_Window::create(traits, debugLayer, apiDumpLayer, allocator); -#else - ref_ptr window = glfw::GLFW_Window::create(traits.width, traits.height, debugLayer, apiDumpLayer, traits.shareWindow, allocator); -#endif - return Result(window); - } - } // namespace vsg diff --git a/src/vsg/viewer/Xcb_Window.cpp b/src/vsg/viewer/Xcb_Window.cpp new file mode 100644 index 0000000000..3e55cd64a5 --- /dev/null +++ b/src/vsg/viewer/Xcb_Window.cpp @@ -0,0 +1,495 @@ +/* + +Copyright(c) 2018 Robert Osfield + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + */ + +#include "Xcb_Window.h" + +#include +#include + +#include + +#include + +#include +#include +#include + +namespace vsg +{ + // Provide the Window::create(...) implementation that automatically maps to a Xcb_Window + Window::Result Window::create(const Window::Traits& traits, bool debugLayer, bool apiDumpLayer, vsg::AllocationCallbacks* allocator) + { + return xcb::Xcb_Window::create(traits, debugLayer, apiDumpLayer, allocator); + } +} + +using namespace vsg; +using namespace xcb; + +KeyboardMap::KeyboardMap() +{ + _modifierMask=0xff; +} + +void KeyboardMap::add(uint16_t keycode, uint16_t modifier, KeySymbol key) +{ + //std::cout<<"Added ["< > combinations) +{ + for(auto [modifier, key] : combinations) + { + add(keycode, modifier, key); + } +} + +Xcb_Surface::Xcb_Surface(vsg::Instance* instance, xcb_connection_t* connection, xcb_window_t window, vsg::AllocationCallbacks* allocator) : + vsg::Surface(VK_NULL_HANDLE, instance, allocator) +{ + VkXcbSurfaceCreateInfoKHR surfaceCreateInfo{}; + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; + surfaceCreateInfo.connection = connection; + surfaceCreateInfo.window = window; + + /*VkResult result =*/ vkCreateXcbSurfaceKHR(*instance, &surfaceCreateInfo, nullptr, &_surface); +} + +// reference +// https://harrylovescode.gitbooks.io/vulkan-api/content/chap04/chap04-linux.html + +vsg::Window::Result Xcb_Window::create(const Traits& traits, bool debugLayer, bool apiDumpLayer, vsg::AllocationCallbacks* allocator) +{ + try + { + ref_ptr window(new Xcb_Window(traits, debugLayer, apiDumpLayer, allocator)); + return Result(window); + } + catch(vsg::Window::Result result) + { + return result; + } +} + + +Xcb_Window::Xcb_Window(const Traits& traits, bool debugLayer, bool apiDumpLayer, vsg::AllocationCallbacks* allocator) +{ + std::cout<<"Xcb_Window() "<=32 && keysym[m]<256) std::cout<<" ["<add(keycode, m, static_cast(keysym[m])); + } + } + + free(keyboard_mapping_reply); + } + + + + // select the appropriate screen for the window + xcb_screen_iterator_t screen_iterator = xcb_setup_roots_iterator(setup); + for (;screenNum>0; --screenNum) xcb_screen_next(&screen_iterator); + _screen = screen_iterator.data; + std::cout<<" selected screen "<<_screen< stateAtoms{ wmFullScreen }; + + xcb_change_property(_connection, XCB_PROP_MODE_REPLACE, _window, wmState, XCB_ATOM_ATOM, 32, stateAtoms.size(), stateAtoms.data()); + + std::cout<<"Set up full screen"<response_type & ~0x80; + if (response_type==XCB_PROPERTY_NOTIFY) + { + auto propety_notify = reinterpret_cast(event); + _first_xcb_timestamp = propety_notify->time; + _first_xcb_time_point = vsg::clock::now(); + } + free(event); + } + } + xcb_map_window(_connection, _window); + +#if 0 + // reconfigure the window position and size. + const uint32_t values[] = { 100, 200, 300, 400 }; + xcb_configure_window (_connection, _window, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values); + xcb_flush(_connection); +#endif + + + //xcb_flush(_connection); + + if (traits.shareWindow) + { + throw Result("Error: vsg::Xcb_Window::create(...) Sharing of Windows not Not supported yet.", VK_ERROR_INVALID_EXTERNAL_HANDLE); + } + else + { + Names instanceExtensions = {VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_XCB_SURFACE_EXTENSION_NAME}; + + vsg::Names requestedLayers; + if (debugLayer) + { + instanceExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); + requestedLayers.push_back("VK_LAYER_LUNARG_standard_validation"); + if (apiDumpLayer) requestedLayers.push_back("VK_LAYER_LUNARG_api_dump"); + } + + vsg::Names validatedNames = vsg::validateInstancelayerNames(requestedLayers); + + vsg::Names deviceExtensions; + deviceExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + + vsg::ref_ptr instance = vsg::Instance::create(instanceExtensions, validatedNames, allocator); + if (!instance) throw Result("Error: vsg::Xcb_Window::create(...) failed to create Window, unable to create Vulkan instance.", VK_ERROR_INVALID_EXTERNAL_HANDLE); + + std::cout<<"Instance created"< surface(new Xcb_Surface(instance, _connection, _window, allocator)); + if (!surface) throw Result("Error: vsg::Xcb_Window::create(...) failed to create Window, unable to create GLFWSurface.", VK_ERROR_INVALID_EXTERNAL_HANDLE); + + std::cout<<"Surface created"< physicalDevice = vsg::PhysicalDevice::create(instance, VK_QUEUE_GRAPHICS_BIT, surface); + if (!physicalDevice) throw Result("Error: vsg::Xcb_Window::create(...) failed to create Window, no Vulkan PhysicalDevice supported.", VK_ERROR_INVALID_EXTERNAL_HANDLE); + + std::cout<<"Physical Device created"< device = vsg::Device::create(physicalDevice, validatedNames, deviceExtensions, allocator); + if (!device) throw Result("Error: vsg::Xcb_Window::create(...) failed to create Window, unable to create Vulkan logical Device.", VK_ERROR_INVALID_EXTERNAL_HANDLE); + + std::cout<<"Device created"< renderPass = vsg::RenderPass::create(device, imageFormat.format, depthFormat, allocator); + if (!renderPass) throw Result("Error: vsg::Xcb_Window::create(...) failed to create Window, unable to create Vulkan RenderPass.", VK_ERROR_INVALID_EXTERNAL_HANDLE); + + _instance = instance; + _surface = surface; + _physicalDevice = physicalDevice; + _device = device; + _renderPass = renderPass; + _debugLayersEnabled = debugLayer; + + std::cout<<"Renderpass created"<response_type & ~0x80; + switch(response_type) + { +#if 0 + case(XCB_CONFIGURE_NOTIFY): + { + auto configure = reinterpret_cast(event); + + //vsg::clock::time_point event_time = vsg::clock::now(); + //events.emplace_back(new vsg::ExposeWindowEvent(this, event_time, expose->x, expose->y, expose->width, expose->height)); + + std::cout<<"XCB_CONFIGURE_NOTIFY x = "<x<<", y = "<y<<", "<width<<", "<height<width != _extent2D.width || configure->height != _extent2D.height); + break; + } +#endif + case(XCB_EXPOSE): + { + auto expose = reinterpret_cast(event); + + vsg::clock::time_point event_time = vsg::clock::now(); + events.emplace_back(new vsg::ExposeWindowEvent(this, event_time, expose->x, expose->y, expose->width, expose->height)); + + _windowResized = (expose->width != _extent2D.width || expose->height != _extent2D.height); + + break; + } + case XCB_CLIENT_MESSAGE: + { + auto client_message = reinterpret_cast(event); + + if (client_message->data.data32[0]==_wmDeleteWindow) + { + vsg::clock::time_point event_time = vsg::clock::now(); + events.emplace_back(new vsg::DeleteWindowEvent(this, event_time)); + + _closeEventRecieved = true; + } + break; + } + case XCB_KEY_PRESS: + { + auto key_press = reinterpret_cast(event); + + vsg::clock::time_point event_time = _first_xcb_time_point + std::chrono::milliseconds(key_press->time-_first_xcb_timestamp); + vsg::KeySymbol keySymbol = _keyboard->getKeySymbol(key_press->detail, 0); + vsg::KeySymbol keySymbolModified = _keyboard->getKeySymbol(key_press->detail, key_press->state); + events.emplace_back(new vsg::KeyPressEvent(this, event_time, keySymbol, keySymbolModified, KeyModifier(key_press->state), 0)); + + break; + } + case XCB_KEY_RELEASE: + { + auto key_release = reinterpret_cast(event); + + vsg::clock::time_point event_time = _first_xcb_time_point + std::chrono::milliseconds(key_release->time-_first_xcb_timestamp); + vsg::KeySymbol keySymbol = _keyboard->getKeySymbol(key_release->detail, 0); + vsg::KeySymbol keySymbolModified = _keyboard->getKeySymbol(key_release->detail, key_release->state); + events.emplace_back(new vsg::KeyReleaseEvent(this, event_time, keySymbol, keySymbolModified, KeyModifier(key_release->state), 0)); + + break; + } + case(XCB_BUTTON_PRESS): + { + auto button_press = reinterpret_cast(event); + + vsg::clock::time_point event_time = _first_xcb_time_point + std::chrono::milliseconds(button_press->time-_first_xcb_timestamp); + events.emplace_back(new vsg::ButtonPressEvent(this, event_time, button_press->event_x, button_press->event_y, vsg::ButtonMask(button_press->state), button_press->detail)); + + break; + } + case(XCB_BUTTON_RELEASE): + { + auto button_release = reinterpret_cast(event); + + vsg::clock::time_point event_time = _first_xcb_time_point + std::chrono::milliseconds(button_release->time-_first_xcb_timestamp); + events.emplace_back(new vsg::ButtonPressEvent(this, event_time, button_release->event_x, button_release->event_y, vsg::ButtonMask(button_release->state), button_release->detail)); + + break; + } + case(XCB_MOTION_NOTIFY): + { + auto motion_notify = reinterpret_cast(event); + + vsg::clock::time_point event_time = _first_xcb_time_point + std::chrono::milliseconds(motion_notify->time-_first_xcb_timestamp); + events.emplace_back(new vsg::MoveEvent(this, event_time, motion_notify->event_x, motion_notify->event_y, vsg::ButtonMask(motion_notify->state))); + + break; + } + default: + { + std::cout<<"event not handled, response_type = "<<(int)response_type<width != _extent2D.width || geometry_reply->height != _extent2D.height); + } + + return false; +#endif +} + +void Xcb_Window::resize() +{ + xcb_get_geometry_reply_t* geometry_reply = xcb_get_geometry_reply(_connection, xcb_get_geometry(_connection, _window), nullptr); + if (geometry_reply) + { + buildSwapchain(geometry_reply->width, geometry_reply->height); + } + _windowResized = false; +} diff --git a/src/vsg/viewer/Xcb_Window.h b/src/vsg/viewer/Xcb_Window.h new file mode 100644 index 0000000000..ace4f5cafc --- /dev/null +++ b/src/vsg/viewer/Xcb_Window.h @@ -0,0 +1,247 @@ +/* + +Copyright(c) 2018 Robert Osfield + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + */ + +#pragma once + +#include +#include + +#include + +#include + +namespace xcb +{ + + class KeyboardMap : public vsg::Object + { + public: + KeyboardMap(); + + using KeycodeModifier = std::pair; + using KeycodeMap = std::map; + + void add(uint16_t keycode, uint16_t modifier, vsg::KeySymbol key); + + void add(uint16_t keycode, std::initializer_list > combinations); + + vsg::KeySymbol getKeySymbol(uint16_t keycode, uint16_t modifier) + { + auto itr = _keycodeMap.find(KeycodeModifier(keycode, 0)); + if (itr==_keycodeMap.end()) return vsg::KEY_Undefined; + + vsg::KeySymbol baseKeySymbol = itr->second; + if (modifier==0) return baseKeySymbol; + + bool shift = (modifier & vsg::MODKEY_Shift)!=0; + uint16_t index = 0; + + if (baseKeySymbol>=vsg::KEY_KP_Space && baseKeySymbol<=vsg::KEY_KP_Divide) + { + // numeric keypad values + bool numLock = ((modifier & vsg::MODKEY_NumLock)!=0); + index = (numLock && !shift) ? 1 : 0; + } + else + { + bool capsLock = (modifier & vsg::MODKEY_CapsLock)!=0; + index = (capsLock ? !shift : shift) ? 1 : 0; + } + if (index==0) return baseKeySymbol; + + if (itr = _keycodeMap.find(KeycodeModifier(keycode, index)); itr!=_keycodeMap.end()) return itr->second; + return vsg::KEY_Undefined; + } + + protected: + KeycodeMap _keycodeMap; + uint16_t _modifierMask; + }; + + + // window decoation + struct MotifHints + { + enum Flags : uint32_t + { + FLAGS_FUNCTIONS = 1<<0, + FLAGS_DECORATIONS = 1<<1, + FLAGS_INPUT_MODE = 1<<2, + FLAGS_STATUS = 1<<3, + }; + + enum Functions : uint32_t + { + FUNC_ALL = 1<<0, + FUNC_RESIZE = 1<<1, + FUNC_MOVE = 1<<2, + FUNC_MINIMUMSIZE = 1<<3, + FUNC_MAXIMUMSIZE = 1<<4, + FUNC_CLOSE = 1<<5 + }; + + enum Decorations : uint32_t + { + DECOR_ALL = 1<<0, + DECOR_BORDER = 1<<1, + DECOR_RESIZE = 1<<2, + DECOR_TITLE = 1<<3, + DECOR_MENU = 1<<4, + DECOR_MINIMUMSIZE = 1<<5, + DECOR_MAXIMUMSIZE = 1<<6 + }; + + static MotifHints borderless() + { + MotifHints hints; + hints.flags = FLAGS_DECORATIONS; + return hints; + } + + static MotifHints window(bool resize=true, bool move=true, bool close=true) + { + MotifHints hints; + hints.flags = FLAGS_DECORATIONS | FLAGS_FUNCTIONS; + hints.functions = 0; + if (resize) hints.functions |= FUNC_RESIZE; + if (move) hints.functions |= FUNC_MOVE; + if (close) hints.functions |= FUNC_CLOSE; + hints.decorations = DECOR_ALL; + return hints; + } + + + uint32_t flags{}; + uint32_t functions{}; + uint32_t decorations{}; + int32_t input_mode{}; + uint32_t status{}; + }; + + class AtomRequest + { + public: + AtomRequest(xcb_connection_t* connection, const char* atom_name): + _connection(connection) + { + _cookie = xcb_intern_atom(connection, false, strlen(atom_name), atom_name); + } + + operator xcb_atom_t () + { + if (_connection) + { + xcb_intern_atom_reply_t* reply = xcb_intern_atom_reply(_connection, _cookie, nullptr); + if (reply) + { + _atom = reply->atom; + free(reply); + } + _connection = nullptr; + } + return _atom; + } + + xcb_connection_t* _connection{}; + xcb_intern_atom_cookie_t _cookie{}; + xcb_atom_t _atom{}; + }; + + bool getWindowGeometry(xcb_connection_t* connection, xcb_drawable_t window, int32_t& x, int32_t& y, uint32_t& width, uint32_t& height) + { + xcb_get_geometry_cookie_t geometry_cookie = xcb_get_geometry(connection, window); + xcb_query_tree_cookie_t tree_cookie = xcb_query_tree(connection, window); + + xcb_get_geometry_reply_t* geometry_reply = xcb_get_geometry_reply(connection, geometry_cookie, nullptr); + xcb_query_tree_reply_t* tree_reply = xcb_query_tree_reply(connection, tree_cookie, nullptr); + + if (geometry_reply) + { + x = geometry_reply->x; + y = geometry_reply->y; + width = geometry_reply->width; + height = geometry_reply->height; + + if (tree_reply) + { + xcb_translate_coordinates_cookie_t trans_cookie = xcb_translate_coordinates(connection, window, tree_reply->parent, x, y); + xcb_translate_coordinates_reply_t* trans_reply = xcb_translate_coordinates_reply(connection, trans_cookie, nullptr); + if (trans_reply) + { + std::cout<<"trans->dst_x = "<dst_x<<", trans->dst_y = "<dst_y<root, x, y); + trans_reply = xcb_translate_coordinates_reply(connection, trans_cookie, nullptr); + if (trans_reply) + { + std::cout<<"2nd trans->dst_x = "<dst_x<<", trans->dst_y = "<dst_y< _keyboard; + }; + + +} + diff --git a/src/vsg/vsgConfig.cmake b/src/vsg/vsgConfig.cmake index 4211d59203..9eb1150619 100644 --- a/src/vsg/vsgConfig.cmake +++ b/src/vsg/vsgConfig.cmake @@ -2,8 +2,15 @@ include(CMakeFindDependencyMacro) find_dependency(Vulkan) -if(NOT WIN32) +if (ANDROID) + # TODO +elseif (WIN32) + # just use native windowing +elseif (APPLE) find_dependency(glfw3) +else() + find_package(PkgConfig) + pkg_check_modules(xcb REQUIRED IMPORTED_TARGET xcb) endif() include("${CMAKE_CURRENT_LIST_DIR}/vsgTargets.cmake")