Merge pull request #1431 from bwrsandman/linux_windowing
Linux windowing fixes
This commit is contained in:
commit
cb13f0bae6
|
@ -73,7 +73,6 @@ project("xenia-app")
|
||||||
"X11",
|
"X11",
|
||||||
"xcb",
|
"xcb",
|
||||||
"X11-xcb",
|
"X11-xcb",
|
||||||
"vulkan",
|
|
||||||
"SDL2",
|
"SDL2",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -395,9 +395,8 @@ void Value::MulHi(Value* other, bool is_unsigned) {
|
||||||
(uint32_t)other->constant.i32) >>
|
(uint32_t)other->constant.i32) >>
|
||||||
32);
|
32);
|
||||||
} else {
|
} else {
|
||||||
constant.i32 =
|
constant.i32 = (int32_t)(
|
||||||
(int32_t)(((int64_t)constant.i32 * (int64_t)other->constant.i32) >>
|
((int64_t)constant.i32 * (int64_t)other->constant.i32) >> 32);
|
||||||
32);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case INT64_TYPE:
|
case INT64_TYPE:
|
||||||
|
|
|
@ -810,6 +810,6 @@ void RegisterEmitCategoryControl() {
|
||||||
XEREGISTERINSTR(mtmsrd);
|
XEREGISTERINSTR(mtmsrd);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ppc
|
} // namespace cpu
|
||||||
} // namespace cpu
|
} // namespace cpu
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -832,10 +832,9 @@ int XexModule::ReadPEHeaders() {
|
||||||
// offsetof seems to be unable to find OptionalHeader.
|
// offsetof seems to be unable to find OptionalHeader.
|
||||||
#define offsetof1(type, member) ((std::size_t) & (((type*)0)->member))
|
#define offsetof1(type, member) ((std::size_t) & (((type*)0)->member))
|
||||||
#define IMAGE_FIRST_SECTION1(ntheader) \
|
#define IMAGE_FIRST_SECTION1(ntheader) \
|
||||||
((PIMAGE_SECTION_HEADER)((uint8_t*)ntheader + \
|
((PIMAGE_SECTION_HEADER)( \
|
||||||
offsetof1(IMAGE_NT_HEADERS, OptionalHeader) + \
|
(uint8_t*)ntheader + offsetof1(IMAGE_NT_HEADERS, OptionalHeader) + \
|
||||||
((PIMAGE_NT_HEADERS)(ntheader)) \
|
((PIMAGE_NT_HEADERS)(ntheader))->FileHeader.SizeOfOptionalHeader))
|
||||||
->FileHeader.SizeOfOptionalHeader))
|
|
||||||
|
|
||||||
// Quick scan to determine bounds of sections.
|
// Quick scan to determine bounds of sections.
|
||||||
size_t upper_address = 0;
|
size_t upper_address = 0;
|
||||||
|
|
|
@ -70,8 +70,6 @@ project("xenia-gpu-vulkan-trace-viewer")
|
||||||
"X11",
|
"X11",
|
||||||
"xcb",
|
"xcb",
|
||||||
"X11-xcb",
|
"X11-xcb",
|
||||||
"GL",
|
|
||||||
"vulkan",
|
|
||||||
})
|
})
|
||||||
|
|
||||||
filter("platforms:Windows")
|
filter("platforms:Windows")
|
||||||
|
@ -138,8 +136,6 @@ project("xenia-gpu-vulkan-trace-dump")
|
||||||
"X11",
|
"X11",
|
||||||
"xcb",
|
"xcb",
|
||||||
"X11-xcb",
|
"X11-xcb",
|
||||||
"GL",
|
|
||||||
"vulkan",
|
|
||||||
})
|
})
|
||||||
|
|
||||||
filter("platforms:Windows")
|
filter("platforms:Windows")
|
||||||
|
|
|
@ -271,7 +271,7 @@ VulkanGraphicsSystem::CreateCommandProcessor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanGraphicsSystem::Swap(xe::ui::UIEvent* e) {
|
void VulkanGraphicsSystem::Swap(xe::ui::UIEvent* e) {
|
||||||
if (!command_processor_) {
|
if (!command_processor_ || !display_context_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -277,9 +277,8 @@ uint32_t xeXamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index,
|
||||||
auto setting = user_profile->GetSetting(setting_id);
|
auto setting = user_profile->GetSetting(setting_id);
|
||||||
|
|
||||||
std::memset(out_setting, 0, sizeof(X_USER_READ_PROFILE_SETTING));
|
std::memset(out_setting, 0, sizeof(X_USER_READ_PROFILE_SETTING));
|
||||||
out_setting->from = !setting || !setting->is_set ? 0
|
out_setting->from =
|
||||||
: setting->is_title_specific() ? 2
|
!setting || !setting->is_set ? 0 : setting->is_title_specific() ? 2 : 1;
|
||||||
: 1;
|
|
||||||
out_setting->user_index = static_cast<uint32_t>(user_index);
|
out_setting->user_index = static_cast<uint32_t>(user_index);
|
||||||
out_setting->setting_id = setting_id;
|
out_setting->setting_id = setting_id;
|
||||||
|
|
||||||
|
|
|
@ -17,15 +17,6 @@
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
class PostedFn {
|
|
||||||
public:
|
|
||||||
explicit PostedFn(std::function<void()> fn) : fn_(std::move(fn)) {}
|
|
||||||
void Call() { fn_(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::function<void()> fn_;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unique_ptr<Loop> Loop::Create() { return std::make_unique<GTKLoop>(); }
|
std::unique_ptr<Loop> Loop::Create() { return std::make_unique<GTKLoop>(); }
|
||||||
|
|
||||||
GTKLoop::GTKLoop() : thread_id_() {
|
GTKLoop::GTKLoop() : thread_id_() {
|
||||||
|
@ -56,24 +47,33 @@ bool GTKLoop::is_on_loop_thread() {
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean _posted_fn_thunk(gpointer posted_fn) {
|
gboolean _posted_fn_thunk(gpointer posted_fn) {
|
||||||
PostedFn* Fn = reinterpret_cast<PostedFn*>(posted_fn);
|
// convert gpointer back to std::function, call it, then free std::function
|
||||||
Fn->Call();
|
std::function<void()>* f =
|
||||||
|
reinterpret_cast<std::function<void()>*>(posted_fn);
|
||||||
|
std::function<void()>& func = *f;
|
||||||
|
func();
|
||||||
|
delete f;
|
||||||
|
// Tells GDK we don't want to run this again
|
||||||
return G_SOURCE_REMOVE;
|
return G_SOURCE_REMOVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GTKLoop::Post(std::function<void()> fn) {
|
void GTKLoop::Post(std::function<void()> fn) {
|
||||||
assert_true(thread_id_ != std::thread::id());
|
assert_true(thread_id_ != std::thread::id());
|
||||||
gdk_threads_add_idle(_posted_fn_thunk,
|
// converting std::function to a generic pointer for gdk
|
||||||
reinterpret_cast<gpointer>(new PostedFn(std::move(fn))));
|
gdk_threads_add_idle(_posted_fn_thunk, reinterpret_cast<gpointer>(
|
||||||
|
new std::function<void()>(fn)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GTKLoop::PostDelayed(std::function<void()> fn, uint64_t delay_millis) {
|
void GTKLoop::PostDelayed(std::function<void()> fn, uint64_t delay_millis) {
|
||||||
gdk_threads_add_timeout(
|
gdk_threads_add_timeout(
|
||||||
delay_millis, _posted_fn_thunk,
|
delay_millis, _posted_fn_thunk,
|
||||||
reinterpret_cast<gpointer>(new PostedFn(std::move(fn))));
|
reinterpret_cast<gpointer>(new std::function<void()>(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(); }
|
void GTKLoop::AwaitQuit() { quit_fence_.Wait(); }
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,4 @@ project("xenia-ui-window-vulkan-demo")
|
||||||
"X11",
|
"X11",
|
||||||
"xcb",
|
"xcb",
|
||||||
"X11-xcb",
|
"X11-xcb",
|
||||||
"GL",
|
|
||||||
"vulkan",
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
#include "xenia/ui/vulkan/vulkan_util.h"
|
#include "xenia/ui/vulkan/vulkan_util.h"
|
||||||
#include "xenia/ui/window.h"
|
#include "xenia/ui/window.h"
|
||||||
|
|
||||||
#if XE_PLATFORM_LINUX
|
#if XE_PLATFORM_GNU_LINUX
|
||||||
#include "xenia/ui/window_gtk.h"
|
#include "xenia/ui/window_gtk.h"
|
||||||
|
|
||||||
#include <X11/Xlib-xcb.h>
|
#include <X11/Xlib-xcb.h>
|
||||||
|
@ -71,14 +71,10 @@ bool VulkanContext::Initialize() {
|
||||||
status = ifn.vkCreateWin32SurfaceKHR(*provider->instance(), &create_info,
|
status = ifn.vkCreateWin32SurfaceKHR(*provider->instance(), &create_info,
|
||||||
nullptr, &surface);
|
nullptr, &surface);
|
||||||
CheckResult(status, "vkCreateWin32SurfaceKHR");
|
CheckResult(status, "vkCreateWin32SurfaceKHR");
|
||||||
#elif XE_PLATFORM_LINUX
|
#elif XE_PLATFORM_GNU_LINUX
|
||||||
#ifdef GDK_WINDOWING_X11
|
#ifdef GDK_WINDOWING_X11
|
||||||
GtkWidget* window_handle =
|
GtkWidget* window_handle =
|
||||||
static_cast<GtkWidget*>(target_window_->native_handle());
|
dynamic_cast<GTKWindow*>(target_window_)->native_window_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 =
|
xcb_window_t window =
|
||||||
gdk_x11_window_get_xid(gtk_widget_get_window(window_handle));
|
gdk_x11_window_get_xid(gtk_widget_get_window(window_handle));
|
||||||
VkXcbSurfaceCreateInfoKHR create_info;
|
VkXcbSurfaceCreateInfoKHR create_info;
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
#include "xenia/base/platform_win.h"
|
#include "xenia/base/platform_win.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if XE_PLATFORM_LINUX
|
#if XE_PLATFORM_GNU_LINUX
|
||||||
#include "xenia/ui/window_gtk.h"
|
#include "xenia/ui/window_gtk.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -52,11 +52,16 @@ bool VulkanProvider::Initialize() {
|
||||||
instance_ = std::make_unique<VulkanInstance>();
|
instance_ = std::make_unique<VulkanInstance>();
|
||||||
|
|
||||||
// Always enable the swapchain.
|
// Always enable the swapchain.
|
||||||
#if XE_PLATFORM_WIN32
|
#if XE_PLATFORM_GNU_LINUX || XE_PLATFORM_WIN32
|
||||||
instance_->DeclareRequiredExtension("VK_KHR_surface", Version::Make(0, 0, 0),
|
instance_->DeclareRequiredExtension("VK_KHR_surface", Version::Make(0, 0, 0),
|
||||||
false);
|
false);
|
||||||
|
#if XE_PLATFORM_GNU_LINUX
|
||||||
|
instance_->DeclareRequiredExtension("VK_KHR_xcb_surface",
|
||||||
|
Version::Make(0, 0, 0), false);
|
||||||
|
#elif XE_PLATFORM_WIN32
|
||||||
instance_->DeclareRequiredExtension("VK_KHR_win32_surface",
|
instance_->DeclareRequiredExtension("VK_KHR_win32_surface",
|
||||||
Version::Make(0, 0, 0), false);
|
Version::Make(0, 0, 0), false);
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Attempt initialization and device query.
|
// Attempt initialization and device query.
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "xenia/base/delegate.h"
|
#include "xenia/base/delegate.h"
|
||||||
|
#include "xenia/base/platform.h"
|
||||||
#include "xenia/ui/graphics_context.h"
|
#include "xenia/ui/graphics_context.h"
|
||||||
#include "xenia/ui/loop.h"
|
#include "xenia/ui/loop.h"
|
||||||
#include "xenia/ui/menu_item.h"
|
#include "xenia/ui/menu_item.h"
|
||||||
|
@ -172,8 +173,16 @@ class Window {
|
||||||
Loop* loop_ = nullptr;
|
Loop* loop_ = nullptr;
|
||||||
std::unique_ptr<MenuItem> main_menu_;
|
std::unique_ptr<MenuItem> main_menu_;
|
||||||
std::string title_;
|
std::string title_;
|
||||||
|
#ifdef XE_PLATFORM_GNU_LINUX
|
||||||
|
// GTK must have a default value here that isn't 0
|
||||||
|
// TODO(Triang3l): Cleanup and unify this. May need to get the first resize
|
||||||
|
// message on various platforms.
|
||||||
|
int32_t width_ = 1280;
|
||||||
|
int32_t height_ = 720;
|
||||||
|
#else
|
||||||
int32_t width_ = 0;
|
int32_t width_ = 0;
|
||||||
int32_t height_ = 0;
|
int32_t height_ = 0;
|
||||||
|
#endif
|
||||||
bool has_focus_ = true;
|
bool has_focus_ = true;
|
||||||
bool is_cursor_visible_ = true;
|
bool is_cursor_visible_ = true;
|
||||||
bool is_imgui_input_enabled_ = false;
|
bool is_imgui_input_enabled_ = false;
|
||||||
|
|
|
@ -71,7 +71,7 @@ int window_demo_main(const std::vector<std::string>& args) {
|
||||||
std::unique_ptr<GraphicsProvider> graphics_provider;
|
std::unique_ptr<GraphicsProvider> graphics_provider;
|
||||||
loop->PostSynchronous([&window, &graphics_provider]() {
|
loop->PostSynchronous([&window, &graphics_provider]() {
|
||||||
// Create graphics provider and an initial context for the window.
|
// 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).
|
// resources, etc).
|
||||||
graphics_provider = CreateDemoGraphicsProvider(window.get());
|
graphics_provider = CreateDemoGraphicsProvider(window.get());
|
||||||
window->set_context(graphics_provider->CreateContext(window.get()));
|
window->set_context(graphics_provider->CreateContext(window.get()));
|
||||||
|
|
|
@ -7,9 +7,13 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include <X11/Xlib-xcb.h>
|
||||||
|
|
||||||
#include "xenia/base/assert.h"
|
#include "xenia/base/assert.h"
|
||||||
|
#include "xenia/base/clock.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/base/platform_linux.h"
|
#include "xenia/base/platform_linux.h"
|
||||||
#include "xenia/ui/virtual_key.h"
|
#include "xenia/ui/virtual_key.h"
|
||||||
|
@ -18,15 +22,6 @@
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
class FnWrapper {
|
|
||||||
public:
|
|
||||||
explicit FnWrapper(std::function<void()> fn) : fn_(std::move(fn)) {}
|
|
||||||
void Call() { fn_(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::function<void()> fn_;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unique_ptr<Window> Window::Create(Loop* loop, const std::string& title) {
|
std::unique_ptr<Window> Window::Create(Loop* loop, const std::string& title) {
|
||||||
return std::make_unique<GTKWindow>(loop, title);
|
return std::make_unique<GTKWindow>(loop, title);
|
||||||
}
|
}
|
||||||
|
@ -37,14 +32,16 @@ GTKWindow::GTKWindow(Loop* loop, const std::string& title)
|
||||||
GTKWindow::~GTKWindow() {
|
GTKWindow::~GTKWindow() {
|
||||||
OnDestroy();
|
OnDestroy();
|
||||||
if (window_) {
|
if (window_) {
|
||||||
|
if (GTK_IS_WIDGET(window_)) {
|
||||||
gtk_widget_destroy(window_);
|
gtk_widget_destroy(window_);
|
||||||
|
}
|
||||||
window_ = nullptr;
|
window_ = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GTKWindow::Initialize() { return OnCreate(); }
|
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<GTKWindow*>(data);
|
GTKWindow* window = reinterpret_cast<GTKWindow*>(data);
|
||||||
switch (event->type) {
|
switch (event->type) {
|
||||||
case GDK_OWNER_CHANGE:
|
case GDK_OWNER_CHANGE:
|
||||||
|
@ -58,30 +55,80 @@ void gtk_event_handler_(GtkWidget* widget, GdkEvent* event, gpointer data) {
|
||||||
window->HandleKeyboard(&(event->key));
|
window->HandleKeyboard(&(event->key));
|
||||||
break;
|
break;
|
||||||
case GDK_SCROLL:
|
case GDK_SCROLL:
|
||||||
case GDK_BUTTON_PRESS:
|
|
||||||
case GDK_MOTION_NOTIFY:
|
case GDK_MOTION_NOTIFY:
|
||||||
|
case GDK_BUTTON_PRESS:
|
||||||
|
case GDK_BUTTON_RELEASE:
|
||||||
window->HandleMouse(&(event->any));
|
window->HandleMouse(&(event->any));
|
||||||
break;
|
break;
|
||||||
case GDK_FOCUS_CHANGE:
|
case GDK_FOCUS_CHANGE:
|
||||||
window->HandleWindowFocus(&(event->focus_change));
|
window->HandleWindowFocus(&(event->focus_change));
|
||||||
break;
|
break;
|
||||||
case GDK_CONFIGURE:
|
case GDK_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));
|
window->HandleWindowResize(&(event->configure));
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// Do nothing
|
// 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<GTKWindow*>(data);
|
||||||
|
window->HandleWindowPaint();
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean close_callback(GtkWidget* widget, gpointer data) {
|
||||||
|
GTKWindow* window = reinterpret_cast<GTKWindow*>(data);
|
||||||
|
window->Close();
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GTKWindow::Create() {
|
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);
|
window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||||
gtk_window_set_title(GTK_WINDOW(window_), (gchar*)title_.c_str());
|
gtk_window_set_resizable(GTK_WINDOW(window_), true);
|
||||||
gtk_window_set_default_size(GTK_WINDOW(window_), 1280, 720);
|
gtk_window_set_title(GTK_WINDOW(window_), title_.c_str());
|
||||||
gtk_widget_show_all(window_);
|
gtk_window_set_default_size(GTK_WINDOW(window_), width_, height_);
|
||||||
g_signal_connect(G_OBJECT(window_), "destroy", G_CALLBACK(gtk_main_quit),
|
// Drawing area is where we will attach our vulkan/gl context
|
||||||
NULL);
|
drawing_area_ = gtk_drawing_area_new();
|
||||||
g_signal_connect(G_OBJECT(window_), "event", G_CALLBACK(gtk_event_handler_),
|
// tick callback is for the refresh rate of the window
|
||||||
|
gtk_widget_add_tick_callback(drawing_area_, draw_callback,
|
||||||
|
reinterpret_cast<gpointer>(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<gpointer>(this));
|
reinterpret_cast<gpointer>(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<gpointer>(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<gpointer>(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() {
|
bool GTKWindow::OnCreate() {
|
||||||
|
@ -94,8 +141,6 @@ void GTKWindow::OnDestroy() { super::OnDestroy(); }
|
||||||
void GTKWindow::OnClose() {
|
void GTKWindow::OnClose() {
|
||||||
if (!closing_ && window_) {
|
if (!closing_ && window_) {
|
||||||
closing_ = true;
|
closing_ = true;
|
||||||
gtk_widget_destroy(window_);
|
|
||||||
window_ = nullptr;
|
|
||||||
}
|
}
|
||||||
super::OnClose();
|
super::OnClose();
|
||||||
}
|
}
|
||||||
|
@ -123,7 +168,6 @@ void GTKWindow::ToggleFullscreen(bool fullscreen) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fullscreen_ = fullscreen;
|
fullscreen_ = fullscreen;
|
||||||
|
|
||||||
if (fullscreen) {
|
if (fullscreen) {
|
||||||
gtk_window_fullscreen(GTK_WINDOW(window_));
|
gtk_window_fullscreen(GTK_WINDOW(window_));
|
||||||
} else {
|
} else {
|
||||||
|
@ -140,7 +184,6 @@ void GTKWindow::set_bordered(bool enabled) {
|
||||||
// Don't screw with the borders if we're fullscreen.
|
// Don't screw with the borders if we're fullscreen.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
gtk_window_set_decorated(GTK_WINDOW(window_), enabled);
|
gtk_window_set_decorated(GTK_WINDOW(window_), enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,31 +214,30 @@ void GTKWindow::set_focus(bool value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GTKWindow::Resize(int32_t width, int32_t height) {
|
void GTKWindow::Resize(int32_t width, int32_t height) {
|
||||||
|
if (is_fullscreen()) {
|
||||||
|
// Cannot resize while in fullscreen.
|
||||||
|
return;
|
||||||
|
}
|
||||||
gtk_window_resize(GTK_WINDOW(window_), width, height);
|
gtk_window_resize(GTK_WINDOW(window_), width, height);
|
||||||
|
super::Resize(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GTKWindow::Resize(int32_t left, int32_t top, int32_t right,
|
void GTKWindow::Resize(int32_t left, int32_t top, int32_t right,
|
||||||
int32_t bottom) {
|
int32_t bottom) {
|
||||||
// TODO(dougvj) Verify that this is the desired behavior from this call
|
if (is_fullscreen()) {
|
||||||
|
// Cannot resize while in fullscreen.
|
||||||
|
return;
|
||||||
|
}
|
||||||
gtk_window_move(GTK_WINDOW(window_), left, top);
|
gtk_window_move(GTK_WINDOW(window_), left, top);
|
||||||
gtk_window_resize(GTK_WINDOW(window_), left - right, top - bottom);
|
gtk_window_resize(GTK_WINDOW(window_), right - left, bottom - top);
|
||||||
|
super::Resize(left, top, right, bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GTKWindow::OnResize(UIEvent* e) {
|
void GTKWindow::OnResize(UIEvent* e) { super::OnResize(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::Invalidate() {
|
void GTKWindow::Invalidate() {
|
||||||
|
// gtk_widget_queue_draw(drawing_area_);
|
||||||
super::Invalidate();
|
super::Invalidate();
|
||||||
// TODO(dougvj) I am not sure what this is supposed to do
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GTKWindow::Close() {
|
void GTKWindow::Close() {
|
||||||
|
@ -203,20 +245,22 @@ void GTKWindow::Close() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
closing_ = true;
|
closing_ = true;
|
||||||
Close();
|
|
||||||
OnClose();
|
OnClose();
|
||||||
|
gtk_widget_destroy(window_);
|
||||||
|
window_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GTKWindow::OnMainMenuChange() {
|
void GTKWindow::OnMainMenuChange() {
|
||||||
// We need to store the old handle for detachment
|
// We need to store the old handle for detachment
|
||||||
static GtkWidget* box = nullptr;
|
static int count = 0;
|
||||||
auto main_menu = reinterpret_cast<GTKMenuItem*>(main_menu_.get());
|
auto main_menu = reinterpret_cast<GTKMenuItem*>(main_menu_.get());
|
||||||
if (main_menu && !is_fullscreen()) {
|
if (main_menu && main_menu->handle()) {
|
||||||
if (box) gtk_widget_destroy(box);
|
if (!is_fullscreen()) {
|
||||||
GtkWidget* menu = main_menu->handle();
|
gtk_box_pack_start(GTK_BOX(box_), main_menu->handle(), FALSE, FALSE, 0);
|
||||||
box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
|
gtk_widget_show_all(window_);
|
||||||
gtk_box_pack_start(GTK_BOX(box), menu, FALSE, FALSE, 3);
|
} else {
|
||||||
gtk_container_add(GTK_CONTAINER(window_), box);
|
gtk_container_remove(GTK_CONTAINER(box_), main_menu->handle());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,9 +278,22 @@ bool GTKWindow::HandleWindowOwnerChange(GdkEventOwnerChange* event) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GTKWindow::HandleWindowPaint() {
|
||||||
|
auto e = UIEvent(this);
|
||||||
|
OnPaint(&e);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool GTKWindow::HandleWindowResize(GdkEventConfigure* event) {
|
bool GTKWindow::HandleWindowResize(GdkEventConfigure* event) {
|
||||||
if (event->type == GDK_CONFIGURE) {
|
if (event->type == GDK_CONFIGURE) {
|
||||||
|
int32_t width = event->width;
|
||||||
|
int32_t height = event->height;
|
||||||
auto e = UIEvent(this);
|
auto e = UIEvent(this);
|
||||||
|
if (width != width_ || height != height_) {
|
||||||
|
width_ = width;
|
||||||
|
height_ = height;
|
||||||
|
Layout();
|
||||||
|
}
|
||||||
OnResize(&e);
|
OnResize(&e);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -351,6 +408,7 @@ bool GTKWindow::HandleKeyboard(GdkEventKey* event) {
|
||||||
bool ctrl_pressed = modifiers & GDK_CONTROL_MASK;
|
bool ctrl_pressed = modifiers & GDK_CONTROL_MASK;
|
||||||
bool alt_pressed = modifiers & GDK_META_MASK;
|
bool alt_pressed = modifiers & GDK_META_MASK;
|
||||||
bool super_pressed = modifiers & GDK_SUPER_MASK;
|
bool super_pressed = modifiers & GDK_SUPER_MASK;
|
||||||
|
uint32_t key_char = gdk_keyval_to_unicode(event->keyval);
|
||||||
// TODO(Triang3l): event->hardware_keycode to VirtualKey translation.
|
// TODO(Triang3l): event->hardware_keycode to VirtualKey translation.
|
||||||
auto e = KeyEvent(this, VirtualKey(event->hardware_keycode), 1,
|
auto e = KeyEvent(this, VirtualKey(event->hardware_keycode), 1,
|
||||||
event->type == GDK_KEY_RELEASE, shift_pressed, ctrl_pressed,
|
event->type == GDK_KEY_RELEASE, shift_pressed, ctrl_pressed,
|
||||||
|
@ -358,12 +416,13 @@ bool GTKWindow::HandleKeyboard(GdkEventKey* event) {
|
||||||
switch (event->type) {
|
switch (event->type) {
|
||||||
case GDK_KEY_PRESS:
|
case GDK_KEY_PRESS:
|
||||||
OnKeyDown(&e);
|
OnKeyDown(&e);
|
||||||
|
if (key_char > 0) {
|
||||||
|
OnKeyChar(&e);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case GDK_KEY_RELEASE:
|
case GDK_KEY_RELEASE:
|
||||||
OnKeyUp(&e);
|
OnKeyUp(&e);
|
||||||
break;
|
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:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -377,23 +436,35 @@ std::unique_ptr<ui::MenuItem> MenuItem::Create(Type type,
|
||||||
return std::make_unique<GTKMenuItem>(type, text, hotkey, callback);
|
return std::make_unique<GTKMenuItem>(type, text, hotkey, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _menu_activate_callback(GtkWidget* menu, gpointer data) {
|
static void _menu_activate_callback(GtkWidget* gtk_menu, gpointer data) {
|
||||||
auto fn = reinterpret_cast<FnWrapper*>(data);
|
GTKMenuItem* menu = reinterpret_cast<GTKMenuItem*>(data);
|
||||||
fn->Call();
|
menu->Activate();
|
||||||
delete fn;
|
}
|
||||||
|
|
||||||
|
void GTKMenuItem::Activate() {
|
||||||
|
try {
|
||||||
|
callback_();
|
||||||
|
} catch (const std::bad_function_call& e) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GTKMenuItem::GTKMenuItem(Type type, const std::string& text,
|
GTKMenuItem::GTKMenuItem(Type type, const std::string& text,
|
||||||
const std::string& hotkey,
|
const std::string& hotkey,
|
||||||
std::function<void()> callback)
|
std::function<void()> callback)
|
||||||
: MenuItem(type, text, hotkey, std::move(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<const gchar*>(label.c_str());
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MenuItem::Type::kNormal:
|
case MenuItem::Type::kNormal:
|
||||||
default:
|
default:
|
||||||
menu_ = gtk_menu_bar_new();
|
menu_ = gtk_menu_bar_new();
|
||||||
break;
|
break;
|
||||||
case MenuItem::Type::kPopup:
|
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;
|
break;
|
||||||
case MenuItem::Type::kSeparator:
|
case MenuItem::Type::kSeparator:
|
||||||
menu_ = gtk_separator_menu_item_new();
|
menu_ = gtk_separator_menu_item_new();
|
||||||
|
@ -403,12 +474,12 @@ GTKMenuItem::GTKMenuItem(Type type, const std::string& text,
|
||||||
if (!hotkey.empty()) {
|
if (!hotkey.empty()) {
|
||||||
full_name += "\t" + hotkey;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
if (GTK_IS_MENU_ITEM(menu_))
|
if (GTK_IS_MENU_ITEM(menu_))
|
||||||
g_signal_connect(menu_, "activate", G_CALLBACK(_menu_activate_callback),
|
g_signal_connect(menu_, "activate", G_CALLBACK(_menu_activate_callback),
|
||||||
(gpointer) new FnWrapper(callback));
|
(gpointer)this);
|
||||||
}
|
}
|
||||||
|
|
||||||
GTKMenuItem::~GTKMenuItem() {
|
GTKMenuItem::~GTKMenuItem() {
|
||||||
|
@ -456,4 +527,5 @@ void GTKMenuItem::OnChildRemoved(MenuItem* generic_child_item) {
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
|
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
#include <gdk/gdkx.h>
|
#include <gdk/gdkx.h>
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
#include <xcb/xcb.h>
|
||||||
|
|
||||||
#include "xenia/base/platform_linux.h"
|
#include "xenia/base/platform_linux.h"
|
||||||
#include "xenia/ui/menu_item.h"
|
#include "xenia/ui/menu_item.h"
|
||||||
|
@ -31,9 +32,10 @@ class GTKWindow : public Window {
|
||||||
~GTKWindow() override;
|
~GTKWindow() override;
|
||||||
|
|
||||||
NativePlatformHandle native_platform_handle() const override {
|
NativePlatformHandle native_platform_handle() const override {
|
||||||
return nullptr;
|
return connection_;
|
||||||
}
|
}
|
||||||
NativeWindowHandle native_handle() const override { return window_; }
|
NativeWindowHandle native_handle() const override { return window_; }
|
||||||
|
GtkWidget* native_window_handle() const { return drawing_area_; }
|
||||||
|
|
||||||
void EnableMainMenu() override {}
|
void EnableMainMenu() override {}
|
||||||
void DisableMainMenu() override {}
|
void DisableMainMenu() override {}
|
||||||
|
@ -74,14 +76,22 @@ class GTKWindow : public Window {
|
||||||
private:
|
private:
|
||||||
void Create();
|
void Create();
|
||||||
GtkWidget* window_;
|
GtkWidget* window_;
|
||||||
|
GtkWidget* box_;
|
||||||
|
GtkWidget* drawing_area_;
|
||||||
|
xcb_connection_t* connection_;
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
friend void gtk_event_handler_(GtkWidget*, GdkEvent*, gpointer);
|
|
||||||
bool HandleMouse(GdkEventAny* event);
|
bool HandleMouse(GdkEventAny* event);
|
||||||
bool HandleKeyboard(GdkEventKey* event);
|
bool HandleKeyboard(GdkEventKey* event);
|
||||||
bool HandleWindowResize(GdkEventConfigure* event);
|
bool HandleWindowResize(GdkEventConfigure* event);
|
||||||
bool HandleWindowFocus(GdkEventFocus* event);
|
bool HandleWindowFocus(GdkEventFocus* event);
|
||||||
bool HandleWindowVisibility(GdkEventVisibility* event);
|
bool HandleWindowVisibility(GdkEventVisibility* event);
|
||||||
bool HandleWindowOwnerChange(GdkEventOwnerChange* event);
|
bool HandleWindowOwnerChange(GdkEventOwnerChange* event);
|
||||||
|
bool HandleWindowPaint();
|
||||||
|
|
||||||
bool closing_ = false;
|
bool closing_ = false;
|
||||||
bool fullscreen_ = false;
|
bool fullscreen_ = false;
|
||||||
|
@ -95,6 +105,7 @@ class GTKMenuItem : public MenuItem {
|
||||||
|
|
||||||
GtkWidget* handle() { return menu_; }
|
GtkWidget* handle() { return menu_; }
|
||||||
using MenuItem::OnSelected;
|
using MenuItem::OnSelected;
|
||||||
|
void Activate();
|
||||||
|
|
||||||
void EnableMenuItem(Window& window) override {}
|
void EnableMenuItem(Window& window) override {}
|
||||||
void DisableMenuItem(Window& window) override {}
|
void DisableMenuItem(Window& window) override {}
|
||||||
|
|
|
@ -447,6 +447,7 @@ void Win32Window::Close() {
|
||||||
closing_ = true;
|
closing_ = true;
|
||||||
OnClose();
|
OnClose();
|
||||||
DestroyWindow(hwnd_);
|
DestroyWindow(hwnd_);
|
||||||
|
hwnd_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Win32Window::OnMainMenuChange() {
|
void Win32Window::OnMainMenuChange() {
|
||||||
|
|
Loading…
Reference in New Issue