From 6d591970ab4263210e70884aedd2608222a6b0b5 Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Fri, 18 Jan 2019 09:01:11 -0500 Subject: [PATCH 1/9] [vulkan linux] Fix native_platform_handle with gtk Use gtk connection as handle and use it when creating surface. --- src/xenia/ui/vulkan/vulkan_context.cc | 4 ---- src/xenia/ui/window_gtk.cc | 6 ++++++ src/xenia/ui/window_gtk.h | 4 +++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/xenia/ui/vulkan/vulkan_context.cc b/src/xenia/ui/vulkan/vulkan_context.cc index 50f51ad74..bc6600389 100644 --- a/src/xenia/ui/vulkan/vulkan_context.cc +++ b/src/xenia/ui/vulkan/vulkan_context.cc @@ -73,10 +73,6 @@ bool VulkanContext::Initialize() { #ifdef GDK_WINDOWING_X11 GtkWidget* window_handle = static_cast(target_window_->native_handle()); - GdkDisplay* gdk_display = gtk_widget_get_display(window_handle); - assert(GDK_IS_X11_DISPLAY(gdk_display)); - xcb_connection_t* connection = - XGetXCBConnection(gdk_x11_display_get_xdisplay(gdk_display)); xcb_window_t window = gdk_x11_window_get_xid(gtk_widget_get_window(window_handle)); VkXcbSurfaceCreateInfoKHR create_info; diff --git a/src/xenia/ui/window_gtk.cc b/src/xenia/ui/window_gtk.cc index 56248bd2d..7d11d316c 100644 --- a/src/xenia/ui/window_gtk.cc +++ b/src/xenia/ui/window_gtk.cc @@ -9,6 +9,8 @@ #include +#include + #include "xenia/base/assert.h" #include "xenia/base/logging.h" #include "xenia/base/platform_linux.h" @@ -81,6 +83,10 @@ void GTKWindow::Create() { NULL); g_signal_connect(G_OBJECT(window_), "event", G_CALLBACK(gtk_event_handler_), reinterpret_cast(this)); + + GdkDisplay* gdk_display = gtk_widget_get_display(window_); + assert(GDK_IS_X11_DISPLAY(gdk_display)); + connection_ = XGetXCBConnection(gdk_x11_display_get_xdisplay(gdk_display)); } bool GTKWindow::OnCreate() { diff --git a/src/xenia/ui/window_gtk.h b/src/xenia/ui/window_gtk.h index c20c0d5d6..ce28cfcd0 100644 --- a/src/xenia/ui/window_gtk.h +++ b/src/xenia/ui/window_gtk.h @@ -15,6 +15,7 @@ #include #include +#include #include "xenia/base/platform_linux.h" #include "xenia/ui/menu_item.h" @@ -31,7 +32,7 @@ class GTKWindow : public Window { ~GTKWindow() override; NativePlatformHandle native_platform_handle() const override { - return nullptr; + return connection_; } NativeWindowHandle native_handle() const override { return window_; } @@ -74,6 +75,7 @@ class GTKWindow : public Window { private: void Create(); GtkWidget* window_; + xcb_connection_t* connection_; friend void gtk_event_handler_(GtkWidget*, GdkEvent*, gpointer); bool HandleMouse(GdkEventAny* event); From 5ed4f8809168e145ab3f81cbbecdb3bc2e965597 Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Wed, 16 Jan 2019 07:46:35 -0500 Subject: [PATCH 2/9] [vulkan linux] Add extension requirement for XCB --- src/xenia/ui/vulkan/vulkan_instance.cc | 15 +++++++++++++++ src/xenia/ui/vulkan/vulkan_provider.cc | 9 ++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/xenia/ui/vulkan/vulkan_instance.cc b/src/xenia/ui/vulkan/vulkan_instance.cc index b324f86f2..ea6f59ca0 100644 --- a/src/xenia/ui/vulkan/vulkan_instance.cc +++ b/src/xenia/ui/vulkan/vulkan_instance.cc @@ -64,6 +64,21 @@ VulkanInstance::VulkanInstance() { DeclareRequiredExtension(VK_EXT_DEBUG_MARKER_EXTENSION_NAME, Version::Make(0, 0, 0), true); + DeclareRequiredExtension(VK_KHR_SURFACE_EXTENSION_NAME, + Version::Make(0, 0, 0), true); +#if XE_PLATFORM_WIN32 + DeclareRequiredExtension(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, + Version::Make(0, 0, 0), true); +#elif XE_PLATFORM_LINUX +#ifdef GDK_WINDOWING_X11 + DeclareRequiredExtension(VK_KHR_XCB_SURFACE_EXTENSION_NAME, + Version::Make(0, 0, 0), true); +#else +#error No Vulkan surface extension for the GDK backend defined yet. +#endif +#else +#error No Vulkan surface extension for the platform defined yet. +#endif } VulkanInstance::~VulkanInstance() { DestroyInstance(); } diff --git a/src/xenia/ui/vulkan/vulkan_provider.cc b/src/xenia/ui/vulkan/vulkan_provider.cc index 119f03992..ab566565f 100644 --- a/src/xenia/ui/vulkan/vulkan_provider.cc +++ b/src/xenia/ui/vulkan/vulkan_provider.cc @@ -52,10 +52,13 @@ bool VulkanProvider::Initialize() { instance_ = std::make_unique(); // Always enable the swapchain. + instance_->DeclareRequiredExtension(VK_KHR_SURFACE_EXTENSION_NAME, + Version::Make(0, 0, 0), false); #if XE_PLATFORM_WIN32 - instance_->DeclareRequiredExtension("VK_KHR_surface", Version::Make(0, 0, 0), - false); - instance_->DeclareRequiredExtension("VK_KHR_win32_surface", + instance_->DeclareRequiredExtension(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, + Version::Make(0, 0, 0), false); +#elif XE_PLATFORM_LINUX + instance_->DeclareRequiredExtension(VK_KHR_XCB_SURFACE_EXTENSION_NAME, Version::Make(0, 0, 0), false); #endif From c4a2dff09936daddbfbc3d4526ab2770de43613f Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Thu, 17 Jan 2019 18:49:23 -0800 Subject: [PATCH 3/9] [vulkan] Add Report Callback to instance create --- src/xenia/ui/vulkan/vulkan_instance.cc | 87 +++++++++++++++----------- 1 file changed, 49 insertions(+), 38 deletions(-) diff --git a/src/xenia/ui/vulkan/vulkan_instance.cc b/src/xenia/ui/vulkan/vulkan_instance.cc index ea6f59ca0..6f258d6ea 100644 --- a/src/xenia/ui/vulkan/vulkan_instance.cc +++ b/src/xenia/ui/vulkan/vulkan_instance.cc @@ -218,6 +218,43 @@ bool VulkanInstance::QueryGlobals() { return true; } +VkBool32 VKAPI_PTR DebugMessageCallback(VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT objectType, + uint64_t object, size_t location, + int32_t messageCode, + const char* pLayerPrefix, + const char* pMessage, void* pUserData) { + if (strcmp(pLayerPrefix, "Validation") == 0) { + const char* blacklist[] = { + "bound but it was never updated. You may want to either update it or " + "not bind it.", + "is being used in draw but has not been updated.", + }; + for (uint32_t i = 0; i < xe::countof(blacklist); ++i) { + if (strstr(pMessage, blacklist[i]) != nullptr) { + return false; + } + } + } + + auto instance = reinterpret_cast(pUserData); + const char* message_type = "UNKNOWN"; + if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) { + message_type = "ERROR"; + } else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) { + message_type = "WARN"; + } else if (flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) { + message_type = "PERF WARN"; + } else if (flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) { + message_type = "INFO"; + } else if (flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) { + message_type = "DEBUG"; + } + + XELOGVK("[{}/{}:{}] {}", pLayerPrefix, message_type, messageCode, pMessage); + return false; +} + bool VulkanInstance::CreateInstance() { XELOGVK("Verifying layers and extensions..."); @@ -239,10 +276,21 @@ bool VulkanInstance::CreateInstance() { XELOGVK("Initializing application instance..."); + VkDebugReportCallbackCreateInfoEXT debug_info; + debug_info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; + debug_info.pNext = nullptr; + // TODO(benvanik): flags to set these. + debug_info.flags = + VK_DEBUG_REPORT_INFORMATION_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | + VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | + VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_DEBUG_BIT_EXT; + debug_info.pfnCallback = &DebugMessageCallback; + debug_info.pUserData = this; + // TODO(benvanik): use GetEntryInfo? VkApplicationInfo application_info; application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - application_info.pNext = nullptr; + application_info.pNext = &debug_info; application_info.pApplicationName = "xenia"; application_info.applicationVersion = 1; application_info.pEngineName = "xenia"; @@ -306,43 +354,6 @@ void VulkanInstance::DestroyInstance() { handle = nullptr; } -VkBool32 VKAPI_PTR DebugMessageCallback(VkDebugReportFlagsEXT flags, - VkDebugReportObjectTypeEXT objectType, - uint64_t object, size_t location, - int32_t messageCode, - const char* pLayerPrefix, - const char* pMessage, void* pUserData) { - if (strcmp(pLayerPrefix, "Validation") == 0) { - const char* blacklist[] = { - "bound but it was never updated. You may want to either update it or " - "not bind it.", - "is being used in draw but has not been updated.", - }; - for (uint32_t i = 0; i < xe::countof(blacklist); ++i) { - if (strstr(pMessage, blacklist[i]) != nullptr) { - return false; - } - } - } - - auto instance = reinterpret_cast(pUserData); - const char* message_type = "UNKNOWN"; - if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) { - message_type = "ERROR"; - } else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) { - message_type = "WARN"; - } else if (flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) { - message_type = "PERF WARN"; - } else if (flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) { - message_type = "INFO"; - } else if (flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) { - message_type = "DEBUG"; - } - - XELOGVK("[{}/{}:{}] {}", pLayerPrefix, message_type, messageCode, pMessage); - return false; -} - void VulkanInstance::EnableDebugValidation() { if (dbg_report_callback_) { DisableDebugValidation(); From 31a8d189f00e839e39b0633ff757e09536c0de4b Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Fri, 18 Jan 2019 09:05:42 -0500 Subject: [PATCH 4/9] [vulkan] Fix typos --- src/xenia/ui/vulkan/vulkan_context.cc | 1 - src/xenia/ui/vulkan/vulkan_instance.cc | 2 +- src/xenia/ui/window_demo.cc | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/xenia/ui/vulkan/vulkan_context.cc b/src/xenia/ui/vulkan/vulkan_context.cc index bc6600389..e39eada39 100644 --- a/src/xenia/ui/vulkan/vulkan_context.cc +++ b/src/xenia/ui/vulkan/vulkan_context.cc @@ -52,7 +52,6 @@ VulkanContext::~VulkanContext() { bool VulkanContext::Initialize() { auto provider = static_cast(provider_); - auto device = provider->device(); if (target_window_) { // Create swap chain used to present to the window. diff --git a/src/xenia/ui/vulkan/vulkan_instance.cc b/src/xenia/ui/vulkan/vulkan_instance.cc index 6f258d6ea..47ddfcf90 100644 --- a/src/xenia/ui/vulkan/vulkan_instance.cc +++ b/src/xenia/ui/vulkan/vulkan_instance.cc @@ -336,7 +336,7 @@ bool VulkanInstance::CreateInstance() { return false; } - // Load Vulkan entrypoints and extensions. + // Load Vulkan entry points and extensions. volkLoadInstance(handle); // Enable debug validation, if needed. diff --git a/src/xenia/ui/window_demo.cc b/src/xenia/ui/window_demo.cc index a6a05140a..cda8a6b83 100644 --- a/src/xenia/ui/window_demo.cc +++ b/src/xenia/ui/window_demo.cc @@ -70,7 +70,7 @@ int window_demo_main(const std::vector& args) { std::unique_ptr graphics_provider; loop->PostSynchronous([&window, &graphics_provider]() { // Create graphics provider and an initial context for the window. - // The window will finish initialization wtih the context (loading + // The window will finish initialization with the context (loading // resources, etc). graphics_provider = CreateDemoGraphicsProvider(window.get()); window->set_context(graphics_provider->CreateContext(window.get())); From cdf77d21bc2f9acaec112e5a4519ba4a2d300042 Mon Sep 17 00:00:00 2001 From: Doug Johnson Date: Mon, 8 Jan 2018 02:57:55 -0700 Subject: [PATCH 5/9] UI: Improve GTK GUI Abstractions for Linux --- src/xenia/ui/loop_gtk.cc | 30 +++---- src/xenia/ui/window.h | 5 +- src/xenia/ui/window_gtk.cc | 165 +++++++++++++++++++++++++------------ src/xenia/ui/window_gtk.h | 12 ++- 4 files changed, 141 insertions(+), 71 deletions(-) diff --git a/src/xenia/ui/loop_gtk.cc b/src/xenia/ui/loop_gtk.cc index 4a753c853..a2b704aeb 100644 --- a/src/xenia/ui/loop_gtk.cc +++ b/src/xenia/ui/loop_gtk.cc @@ -17,15 +17,6 @@ namespace xe { namespace ui { -class PostedFn { - public: - explicit PostedFn(std::function fn) : fn_(std::move(fn)) {} - void Call() { fn_(); } - - private: - std::function fn_; -}; - std::unique_ptr Loop::Create() { return std::make_unique(); } GTKLoop::GTKLoop() : thread_id_() { @@ -56,24 +47,33 @@ bool GTKLoop::is_on_loop_thread() { } gboolean _posted_fn_thunk(gpointer posted_fn) { - PostedFn* Fn = reinterpret_cast(posted_fn); - Fn->Call(); + // convert gpointer back to std::function, call it, then free std::function + std::function* f = + reinterpret_cast*>(posted_fn); + std::function& func = *f; + func(); + delete f; + // Tells GDK we don't want to run this again return G_SOURCE_REMOVE; } void GTKLoop::Post(std::function fn) { assert_true(thread_id_ != std::thread::id()); - gdk_threads_add_idle(_posted_fn_thunk, - reinterpret_cast(new PostedFn(std::move(fn)))); + // converting std::function to a generic pointer for gdk + gdk_threads_add_idle(_posted_fn_thunk, reinterpret_cast( + new std::function(fn))); } void GTKLoop::PostDelayed(std::function fn, uint64_t delay_millis) { gdk_threads_add_timeout( delay_millis, _posted_fn_thunk, - reinterpret_cast(new PostedFn(std::move(fn)))); + reinterpret_cast(new std::function(fn))); } -void GTKLoop::Quit() { assert_true(thread_id_ != std::thread::id()); } +void GTKLoop::Quit() { + assert_true(thread_id_ != std::thread::id()); + Post([]() { gtk_main_quit(); }); +} void GTKLoop::AwaitQuit() { quit_fence_.Wait(); } diff --git a/src/xenia/ui/window.h b/src/xenia/ui/window.h index accdc65b9..d8d3ac00b 100644 --- a/src/xenia/ui/window.h +++ b/src/xenia/ui/window.h @@ -172,8 +172,9 @@ class Window { Loop* loop_ = nullptr; std::unique_ptr main_menu_; std::string title_; - int32_t width_ = 0; - int32_t height_ = 0; + // GTK must have a default value here that isn't 0 + int32_t width_ = 1280; + int32_t height_ = 720; bool has_focus_ = true; bool is_cursor_visible_ = true; bool is_imgui_input_enabled_ = false; diff --git a/src/xenia/ui/window_gtk.cc b/src/xenia/ui/window_gtk.cc index 7d11d316c..a785af2df 100644 --- a/src/xenia/ui/window_gtk.cc +++ b/src/xenia/ui/window_gtk.cc @@ -7,11 +7,13 @@ ****************************************************************************** */ +#include #include #include #include "xenia/base/assert.h" +#include "xenia/base/clock.h" #include "xenia/base/logging.h" #include "xenia/base/platform_linux.h" #include "xenia/ui/window_gtk.h" @@ -19,15 +21,6 @@ namespace xe { namespace ui { -class FnWrapper { - public: - explicit FnWrapper(std::function fn) : fn_(std::move(fn)) {} - void Call() { fn_(); } - - private: - std::function fn_; -}; - std::unique_ptr Window::Create(Loop* loop, const std::string& title) { return std::make_unique(loop, title); } @@ -38,14 +31,14 @@ GTKWindow::GTKWindow(Loop* loop, const std::string& title) GTKWindow::~GTKWindow() { OnDestroy(); if (window_) { - gtk_widget_destroy(window_); + if (GTK_IS_WIDGET(window_)) gtk_widget_destroy(window_); window_ = nullptr; } } bool GTKWindow::Initialize() { return OnCreate(); } -void gtk_event_handler_(GtkWidget* widget, GdkEvent* event, gpointer data) { +gboolean gtk_event_handler(GtkWidget* widget, GdkEvent* event, gpointer data) { GTKWindow* window = reinterpret_cast(data); switch (event->type) { case GDK_OWNER_CHANGE: @@ -59,34 +52,80 @@ void gtk_event_handler_(GtkWidget* widget, GdkEvent* event, gpointer data) { window->HandleKeyboard(&(event->key)); break; case GDK_SCROLL: - case GDK_BUTTON_PRESS: case GDK_MOTION_NOTIFY: + case GDK_BUTTON_PRESS: + case GDK_BUTTON_RELEASE: window->HandleMouse(&(event->any)); break; case GDK_FOCUS_CHANGE: window->HandleWindowFocus(&(event->focus_change)); break; case GDK_CONFIGURE: - window->HandleWindowResize(&(event->configure)); + // Only handle the event for the drawing area so we don't save + // a width and height that includes the menu bar on the full window + if (event->configure.window == + gtk_widget_get_window(window->drawing_area_)) { + window->HandleWindowResize(&(event->configure)); + } + break; default: // Do nothing - return; + break; } + // Propagate the event to other handlers + return GDK_EVENT_PROPAGATE; +} + +gboolean draw_callback(GtkWidget* widget, GdkFrameClock* frame_clock, + gpointer data) { + GTKWindow* window = reinterpret_cast(data); + window->HandleWindowPaint(); + return G_SOURCE_CONTINUE; +} + +gboolean close_callback(GtkWidget* widget, gpointer data) { + GTKWindow* window = reinterpret_cast(data); + window->Close(); + return G_SOURCE_CONTINUE; } void GTKWindow::Create() { + // GTK optionally allows passing argv and argc here for parsing gtk specific + // options. We won't bother window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_title(GTK_WINDOW(window_), (gchar*)title_.c_str()); - gtk_window_set_default_size(GTK_WINDOW(window_), 1280, 720); - gtk_widget_show_all(window_); - g_signal_connect(G_OBJECT(window_), "destroy", G_CALLBACK(gtk_main_quit), - NULL); - g_signal_connect(G_OBJECT(window_), "event", G_CALLBACK(gtk_event_handler_), + gtk_window_set_resizable(GTK_WINDOW(window_), true); + gtk_window_set_title(GTK_WINDOW(window_), title_.c_str()); + gtk_window_set_default_size(GTK_WINDOW(window_), width_, height_); + // Drawing area is where we will attach our vulkan/gl context + drawing_area_ = gtk_drawing_area_new(); + // tick callback is for the refresh rate of the window + gtk_widget_add_tick_callback(drawing_area_, draw_callback, + reinterpret_cast(this), nullptr); + // Attach our event handler to both the main window (for keystrokes) and the + // drawing area (for mouse input, resize event, etc) + g_signal_connect(G_OBJECT(drawing_area_), "event", + G_CALLBACK(gtk_event_handler), reinterpret_cast(this)); GdkDisplay* gdk_display = gtk_widget_get_display(window_); assert(GDK_IS_X11_DISPLAY(gdk_display)); connection_ = XGetXCBConnection(gdk_x11_display_get_xdisplay(gdk_display)); + + g_signal_connect(G_OBJECT(window_), "event", G_CALLBACK(gtk_event_handler), + reinterpret_cast(this)); + // When the window manager kills the window (ie, the user hits X) + g_signal_connect(G_OBJECT(window_), "destroy", G_CALLBACK(close_callback), + reinterpret_cast(this)); + // Enable only keyboard events (so no mouse) for the top window + gtk_widget_set_events(window_, GDK_KEY_PRESS | GDK_KEY_RELEASE); + // Enable all events for the drawing area + gtk_widget_add_events(drawing_area_, GDK_ALL_EVENTS_MASK); + // Place the drawing area in a container (which later will hold the menu) + // then let it fill the whole area + box_ = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + gtk_box_pack_end(GTK_BOX(box_), drawing_area_, TRUE, TRUE, 0); + gtk_container_add(GTK_CONTAINER(window_), box_); + gtk_widget_show_all(window_); } bool GTKWindow::OnCreate() { @@ -128,7 +167,6 @@ void GTKWindow::ToggleFullscreen(bool fullscreen) { } fullscreen_ = fullscreen; - if (fullscreen) { gtk_window_fullscreen(GTK_WINDOW(window_)); } else { @@ -145,7 +183,6 @@ void GTKWindow::set_bordered(bool enabled) { // Don't screw with the borders if we're fullscreen. return; } - gtk_window_set_decorated(GTK_WINDOW(window_), enabled); } @@ -176,31 +213,26 @@ void GTKWindow::set_focus(bool value) { } void GTKWindow::Resize(int32_t width, int32_t height) { + width_ = width; + height_ = height; gtk_window_resize(GTK_WINDOW(window_), width, height); } void GTKWindow::Resize(int32_t left, int32_t top, int32_t right, int32_t bottom) { - // TODO(dougvj) Verify that this is the desired behavior from this call + int32_t width = right - left; + int32_t height = bottom - top; + width_ = width; + height_ = height; gtk_window_move(GTK_WINDOW(window_), left, top); - gtk_window_resize(GTK_WINDOW(window_), left - right, top - bottom); + gtk_window_resize(GTK_WINDOW(window_), width, height); } -void GTKWindow::OnResize(UIEvent* e) { - int32_t width; - int32_t height; - gtk_window_get_size(GTK_WINDOW(window_), &width, &height); - if (width != width_ || height != height_) { - width_ = width; - height_ = height; - Layout(); - } - super::OnResize(e); -} +void GTKWindow::OnResize(UIEvent* e) { super::OnResize(e); } void GTKWindow::Invalidate() { + // gtk_widget_queue_draw(drawing_area_); super::Invalidate(); - // TODO(dougvj) I am not sure what this is supposed to do } void GTKWindow::Close() { @@ -214,14 +246,15 @@ void GTKWindow::Close() { void GTKWindow::OnMainMenuChange() { // We need to store the old handle for detachment - static GtkWidget* box = nullptr; + static int count = 0; auto main_menu = reinterpret_cast(main_menu_.get()); - if (main_menu && !is_fullscreen()) { - if (box) gtk_widget_destroy(box); - GtkWidget* menu = main_menu->handle(); - box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); - gtk_box_pack_start(GTK_BOX(box), menu, FALSE, FALSE, 3); - gtk_container_add(GTK_CONTAINER(window_), box); + if (main_menu && main_menu->handle()) { + if (!is_fullscreen()) { + gtk_box_pack_start(GTK_BOX(box_), main_menu->handle(), FALSE, FALSE, 0); + gtk_widget_show_all(window_); + } else { + gtk_container_remove(GTK_CONTAINER(box_), main_menu->handle()); + } } } @@ -239,9 +272,22 @@ bool GTKWindow::HandleWindowOwnerChange(GdkEventOwnerChange* event) { return false; } +bool GTKWindow::HandleWindowPaint() { + auto e = UIEvent(this); + OnPaint(&e); + return true; +} + bool GTKWindow::HandleWindowResize(GdkEventConfigure* event) { if (event->type == GDK_CONFIGURE) { + int32_t width = event->width; + int32_t height = event->height; auto e = UIEvent(this); + if (width != width_ || height != height_) { + width_ = width; + height_ = height; + Layout(); + } OnResize(&e); return true; } @@ -356,18 +402,20 @@ bool GTKWindow::HandleKeyboard(GdkEventKey* event) { bool ctrl_pressed = modifiers & GDK_CONTROL_MASK; bool alt_pressed = modifiers & GDK_META_MASK; bool super_pressed = modifiers & GDK_SUPER_MASK; + uint32_t key_char = gdk_keyval_to_unicode(event->keyval); auto e = KeyEvent(this, event->hardware_keycode, 1, event->type == GDK_KEY_RELEASE, shift_pressed, ctrl_pressed, alt_pressed, super_pressed); switch (event->type) { case GDK_KEY_PRESS: OnKeyDown(&e); + if (key_char > 0) { + OnKeyChar(&e); + } break; case GDK_KEY_RELEASE: OnKeyUp(&e); break; - // TODO(dougvj) GDK doesn't have a KEY CHAR event, so we will have to - // figure out its equivalent here to call OnKeyChar(&e); default: return false; } @@ -381,23 +429,35 @@ std::unique_ptr MenuItem::Create(Type type, return std::make_unique(type, text, hotkey, callback); } -static void _menu_activate_callback(GtkWidget* menu, gpointer data) { - auto fn = reinterpret_cast(data); - fn->Call(); - delete fn; +static void _menu_activate_callback(GtkWidget* gtk_menu, gpointer data) { + GTKMenuItem* menu = reinterpret_cast(data); + menu->Activate(); +} + +void GTKMenuItem::Activate() { + try { + callback_(); + } catch (const std::bad_function_call& e) { + // Ignore + } } GTKMenuItem::GTKMenuItem(Type type, const std::string& text, const std::string& hotkey, std::function callback) : MenuItem(type, text, hotkey, std::move(callback)) { + std::string label = text; + // TODO(dougvj) Would we ever need to escape underscores? + // Replace & with _ for gtk to see the memonic + std::replace(label.begin(), label.end(), '&', '_'); + const gchar* gtk_label = reinterpret_cast(label.c_str()); switch (type) { case MenuItem::Type::kNormal: default: menu_ = gtk_menu_bar_new(); break; case MenuItem::Type::kPopup: - menu_ = gtk_menu_item_new_with_label((gchar*)text.c_str()); + menu_ = gtk_menu_item_new_with_mnemonic(gtk_label); break; case MenuItem::Type::kSeparator: menu_ = gtk_separator_menu_item_new(); @@ -407,12 +467,12 @@ GTKMenuItem::GTKMenuItem(Type type, const std::string& text, if (!hotkey.empty()) { full_name += "\t" + hotkey; } - menu_ = gtk_menu_item_new_with_label((gchar*)full_name.c_str()); + menu_ = gtk_menu_item_new_with_mnemonic(gtk_label); break; } if (GTK_IS_MENU_ITEM(menu_)) g_signal_connect(menu_, "activate", G_CALLBACK(_menu_activate_callback), - (gpointer) new FnWrapper(callback)); + (gpointer)this); } GTKMenuItem::~GTKMenuItem() { @@ -460,4 +520,5 @@ void GTKMenuItem::OnChildRemoved(MenuItem* generic_child_item) { } } // namespace ui + } // namespace xe diff --git a/src/xenia/ui/window_gtk.h b/src/xenia/ui/window_gtk.h index ce28cfcd0..fba1886a3 100644 --- a/src/xenia/ui/window_gtk.h +++ b/src/xenia/ui/window_gtk.h @@ -34,7 +34,7 @@ class GTKWindow : public Window { NativePlatformHandle native_platform_handle() const override { return connection_; } - NativeWindowHandle native_handle() const override { return window_; } + NativeWindowHandle native_handle() const override { return drawing_area_; } void EnableMainMenu() override {} void DisableMainMenu() override {} @@ -75,15 +75,22 @@ class GTKWindow : public Window { private: void Create(); GtkWidget* window_; + GtkWidget* box_; + GtkWidget* drawing_area_; xcb_connection_t* connection_; - friend void gtk_event_handler_(GtkWidget*, GdkEvent*, gpointer); + // C Callback shims for GTK + friend gboolean gtk_event_handler(GtkWidget*, GdkEvent*, gpointer); + friend gboolean close_callback(GtkWidget*, gpointer); + friend gboolean draw_callback(GtkWidget*, GdkFrameClock*, gpointer); + bool HandleMouse(GdkEventAny* event); bool HandleKeyboard(GdkEventKey* event); bool HandleWindowResize(GdkEventConfigure* event); bool HandleWindowFocus(GdkEventFocus* event); bool HandleWindowVisibility(GdkEventVisibility* event); bool HandleWindowOwnerChange(GdkEventOwnerChange* event); + bool HandleWindowPaint(); bool closing_ = false; bool fullscreen_ = false; @@ -97,6 +104,7 @@ class GTKMenuItem : public MenuItem { GtkWidget* handle() { return menu_; } using MenuItem::OnSelected; + void Activate(); void EnableMenuItem(Window& window) override {} void DisableMenuItem(Window& window) override {} From 0b5ff8332e9248406d08e8fc24e68f0a4a9f6f43 Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Mon, 21 Jan 2019 15:15:15 -0500 Subject: [PATCH 6/9] [ui gtk] Fix file dialog Use drawing area for vulkan but window for dialogs --- src/xenia/ui/vulkan/vulkan_context.cc | 2 +- src/xenia/ui/window_gtk.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/xenia/ui/vulkan/vulkan_context.cc b/src/xenia/ui/vulkan/vulkan_context.cc index e39eada39..76101c6b7 100644 --- a/src/xenia/ui/vulkan/vulkan_context.cc +++ b/src/xenia/ui/vulkan/vulkan_context.cc @@ -71,7 +71,7 @@ bool VulkanContext::Initialize() { #elif XE_PLATFORM_LINUX #ifdef GDK_WINDOWING_X11 GtkWidget* window_handle = - static_cast(target_window_->native_handle()); + dynamic_cast(target_window_)->native_window_handle(); xcb_window_t window = gdk_x11_window_get_xid(gtk_widget_get_window(window_handle)); VkXcbSurfaceCreateInfoKHR create_info; diff --git a/src/xenia/ui/window_gtk.h b/src/xenia/ui/window_gtk.h index fba1886a3..27a4c08b0 100644 --- a/src/xenia/ui/window_gtk.h +++ b/src/xenia/ui/window_gtk.h @@ -34,7 +34,8 @@ class GTKWindow : public Window { NativePlatformHandle native_platform_handle() const override { return connection_; } - NativeWindowHandle native_handle() const override { return drawing_area_; } + NativeWindowHandle native_handle() const override { return window_; } + GtkWidget* native_window_handle() const { return drawing_area_; } void EnableMainMenu() override {} void DisableMainMenu() override {} From 271c91e1157ef2e788a1d365a9860348d27204e1 Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Sun, 27 Jan 2019 11:39:21 -0500 Subject: [PATCH 7/9] [ui gtk] Fix init, resize and destroy Prevent Vulkan Swap before display context is assigned. Prevent resize while fullscreen (like in windows impl). Use superclass Resize implementation to reduce code duplication. Remove recursive call to GTKWindow::Close(). Destroy xcb window after superclass Close(). Set hwnd to null after closing on windows implementation. --- .../gpu/vulkan/vulkan_graphics_system.cc | 2 +- src/xenia/ui/window_gtk.cc | 23 +++++++++++-------- src/xenia/ui/window_win.cc | 1 + 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/xenia/gpu/vulkan/vulkan_graphics_system.cc b/src/xenia/gpu/vulkan/vulkan_graphics_system.cc index 0980e7f9e..2a9e16a7a 100644 --- a/src/xenia/gpu/vulkan/vulkan_graphics_system.cc +++ b/src/xenia/gpu/vulkan/vulkan_graphics_system.cc @@ -260,7 +260,7 @@ VulkanGraphicsSystem::CreateCommandProcessor() { } void VulkanGraphicsSystem::Swap(xe::ui::UIEvent* e) { - if (!command_processor_) { + if (!command_processor_ || !display_context_) { return; } diff --git a/src/xenia/ui/window_gtk.cc b/src/xenia/ui/window_gtk.cc index a785af2df..a53aacabb 100644 --- a/src/xenia/ui/window_gtk.cc +++ b/src/xenia/ui/window_gtk.cc @@ -138,8 +138,6 @@ void GTKWindow::OnDestroy() { super::OnDestroy(); } void GTKWindow::OnClose() { if (!closing_ && window_) { closing_ = true; - gtk_widget_destroy(window_); - window_ = nullptr; } super::OnClose(); } @@ -213,19 +211,23 @@ void GTKWindow::set_focus(bool value) { } void GTKWindow::Resize(int32_t width, int32_t height) { - width_ = width; - height_ = height; + if (is_fullscreen()) { + // Cannot resize while in fullscreen. + return; + } gtk_window_resize(GTK_WINDOW(window_), width, height); + super::Resize(width, height); } void GTKWindow::Resize(int32_t left, int32_t top, int32_t right, int32_t bottom) { - int32_t width = right - left; - int32_t height = bottom - top; - width_ = width; - height_ = height; + if (is_fullscreen()) { + // Cannot resize while in fullscreen. + return; + } gtk_window_move(GTK_WINDOW(window_), left, top); - gtk_window_resize(GTK_WINDOW(window_), width, height); + gtk_window_resize(GTK_WINDOW(window_), right - left, bottom - top); + super::Resize(left, top, right, bottom); } void GTKWindow::OnResize(UIEvent* e) { super::OnResize(e); } @@ -240,8 +242,9 @@ void GTKWindow::Close() { return; } closing_ = true; - Close(); OnClose(); + gtk_widget_destroy(window_); + window_ = nullptr; } void GTKWindow::OnMainMenuChange() { diff --git a/src/xenia/ui/window_win.cc b/src/xenia/ui/window_win.cc index 954502364..853f14030 100644 --- a/src/xenia/ui/window_win.cc +++ b/src/xenia/ui/window_win.cc @@ -446,6 +446,7 @@ void Win32Window::Close() { closing_ = true; OnClose(); DestroyWindow(hwnd_); + hwnd_ = 0; } void Win32Window::OnMainMenuChange() { From 0380067105597ae8a1cae16d3223de7364e9eaf4 Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Wed, 21 Aug 2019 23:36:49 +0200 Subject: [PATCH 8/9] [linux vulkan] Remove linking directly with vulkan Using volk means vulkan linking is done at runtime with the dl library. --- src/xenia/app/premake5.lua | 1 - src/xenia/gpu/vulkan/premake5.lua | 2 -- src/xenia/ui/vulkan/premake5.lua | 1 - 3 files changed, 4 deletions(-) diff --git a/src/xenia/app/premake5.lua b/src/xenia/app/premake5.lua index 8d836ff43..fcfacf499 100644 --- a/src/xenia/app/premake5.lua +++ b/src/xenia/app/premake5.lua @@ -74,7 +74,6 @@ project("xenia-app") "X11", "xcb", "X11-xcb", - "vulkan", "SDL2", }) diff --git a/src/xenia/gpu/vulkan/premake5.lua b/src/xenia/gpu/vulkan/premake5.lua index c1437995f..8c5573ae7 100644 --- a/src/xenia/gpu/vulkan/premake5.lua +++ b/src/xenia/gpu/vulkan/premake5.lua @@ -73,7 +73,6 @@ project("xenia-gpu-vulkan-trace-viewer") "xcb", "X11-xcb", "GL", - "vulkan", }) filter("platforms:Windows") @@ -142,7 +141,6 @@ project("xenia-gpu-vulkan-trace-dump") "xcb", "X11-xcb", "GL", - "vulkan", }) filter("platforms:Windows") diff --git a/src/xenia/ui/vulkan/premake5.lua b/src/xenia/ui/vulkan/premake5.lua index d93f98af6..c24c230c5 100644 --- a/src/xenia/ui/vulkan/premake5.lua +++ b/src/xenia/ui/vulkan/premake5.lua @@ -57,5 +57,4 @@ project("xenia-ui-window-vulkan-demo") "xcb", "X11-xcb", "GL", - "vulkan", }) From 005e0e21c13172d6877f1873f3e9f5d4bd583216 Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Wed, 21 Aug 2019 23:41:35 +0200 Subject: [PATCH 9/9] [linux graphics] Remove GLEW OpenGL is not used in the whole stack. All references are removed. --- src/xenia/gpu/vulkan/premake5.lua | 2 -- src/xenia/ui/vulkan/premake5.lua | 1 - 2 files changed, 3 deletions(-) diff --git a/src/xenia/gpu/vulkan/premake5.lua b/src/xenia/gpu/vulkan/premake5.lua index 8c5573ae7..a1eb189d5 100644 --- a/src/xenia/gpu/vulkan/premake5.lua +++ b/src/xenia/gpu/vulkan/premake5.lua @@ -72,7 +72,6 @@ project("xenia-gpu-vulkan-trace-viewer") "X11", "xcb", "X11-xcb", - "GL", }) filter("platforms:Windows") @@ -140,7 +139,6 @@ project("xenia-gpu-vulkan-trace-dump") "X11", "xcb", "X11-xcb", - "GL", }) filter("platforms:Windows") diff --git a/src/xenia/ui/vulkan/premake5.lua b/src/xenia/ui/vulkan/premake5.lua index c24c230c5..6d2137567 100644 --- a/src/xenia/ui/vulkan/premake5.lua +++ b/src/xenia/ui/vulkan/premake5.lua @@ -56,5 +56,4 @@ project("xenia-ui-window-vulkan-demo") "X11", "xcb", "X11-xcb", - "GL", })