[UI] Implement keycode handling for Linux with additional keys from xenia#2234

This commit is contained in:
Marco Rodolfi 2025-01-17 21:09:29 +01:00 committed by Radosław Gliński
parent 8d841693ff
commit 9703038b71
2 changed files with 163 additions and 20 deletions

View file

@ -2,7 +2,7 @@
****************************************************************************** ******************************************************************************
* Xenia : Xbox 360 Emulator Research Project * * Xenia : Xbox 360 Emulator Research Project *
****************************************************************************** ******************************************************************************
* Copyright 2022 Ben Vanik. All rights reserved. * * Copyright 2025 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. * * Released under the BSD license - see LICENSE in the root for more details. *
****************************************************************************** ******************************************************************************
*/ */
@ -64,7 +64,7 @@ bool GTKWindow::OpenImpl() {
// Add the main menu (even if fullscreen was requested, for the initial layout // Add the main menu (even if fullscreen was requested, for the initial layout
// calculation). // calculation).
const GTKMenuItem* main_menu = static_cast<const GTKMenuItem*>(GetMainMenu()); const auto* main_menu = dynamic_cast<const GTKMenuItem*>(GetMainMenu());
GtkWidget* main_menu_widget = main_menu ? main_menu->handle() : nullptr; GtkWidget* main_menu_widget = main_menu ? main_menu->handle() : nullptr;
if (main_menu_widget) { if (main_menu_widget) {
gtk_box_pack_start(GTK_BOX(box_), main_menu_widget, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(box_), main_menu_widget, FALSE, FALSE, 0);
@ -151,7 +151,7 @@ void GTKWindow::ApplyNewFullscreen() {
// fullscreen being toggled from inside this function. // fullscreen being toggled from inside this function.
WindowDestructionReceiver destruction_receiver(this); WindowDestructionReceiver destruction_receiver(this);
const GTKMenuItem* main_menu = static_cast<const GTKMenuItem*>(GetMainMenu()); const auto* main_menu = dynamic_cast<const GTKMenuItem*>(GetMainMenu());
GtkWidget* main_menu_widget = main_menu ? main_menu->handle() : nullptr; GtkWidget* main_menu_widget = main_menu ? main_menu->handle() : nullptr;
// Changing the menu and the fullscreen state may change the size of the // Changing the menu and the fullscreen state may change the size of the
@ -233,7 +233,7 @@ void GTKWindow::ApplyNewMainMenu(MenuItem* old_main_menu) {
if (old_main_menu) { if (old_main_menu) {
const GTKMenuItem& old_gtk_main_menu = const GTKMenuItem& old_gtk_main_menu =
*static_cast<const GTKMenuItem*>(old_main_menu); *dynamic_cast<const GTKMenuItem*>(old_main_menu);
gtk_container_remove(GTK_CONTAINER(box_), old_gtk_main_menu.handle()); gtk_container_remove(GTK_CONTAINER(box_), old_gtk_main_menu.handle());
if (destruction_receiver.IsWindowDestroyedOrClosed() || IsFullscreen()) { if (destruction_receiver.IsWindowDestroyedOrClosed() || IsFullscreen()) {
if (!destruction_receiver.IsWindowDestroyed()) { if (!destruction_receiver.IsWindowDestroyed()) {
@ -243,8 +243,8 @@ void GTKWindow::ApplyNewMainMenu(MenuItem* old_main_menu) {
} }
} }
const GTKMenuItem* new_main_menu = const auto* new_main_menu =
static_cast<const GTKMenuItem*>(GetMainMenu()); dynamic_cast<const GTKMenuItem*>(GetMainMenu());
if (!new_main_menu) { if (!new_main_menu) {
EndBatchedSizeUpdate(destruction_receiver); EndBatchedSizeUpdate(destruction_receiver);
return; return;
@ -350,6 +350,148 @@ void GTKWindow::EndBatchedSizeUpdate(
} }
} }
VirtualKey GTKWindow::TranslateVirtualKey(guint keyval) {
switch (keyval) {
case GDK_KEY_a:
case GDK_KEY_A:
return VirtualKey::kA;
case GDK_KEY_b:
case GDK_KEY_B:
return VirtualKey::kB;
case GDK_KEY_c:
case GDK_KEY_C:
return VirtualKey::kC;
case GDK_KEY_d:
case GDK_KEY_D:
return VirtualKey::kD;
case GDK_KEY_e:
case GDK_KEY_E:
return VirtualKey::kE;
case GDK_KEY_f:
case GDK_KEY_F:
return VirtualKey::kF;
case GDK_KEY_g:
case GDK_KEY_G:
return VirtualKey::kG;
case GDK_KEY_h:
case GDK_KEY_H:
return VirtualKey::kH;
case GDK_KEY_i:
case GDK_KEY_I:
return VirtualKey::kI;
case GDK_KEY_j:
case GDK_KEY_J:
return VirtualKey::kJ;
case GDK_KEY_k:
case GDK_KEY_K:
return VirtualKey::kK;
case GDK_KEY_l:
case GDK_KEY_L:
return VirtualKey::kL;
case GDK_KEY_m:
case GDK_KEY_M:
return VirtualKey::kM;
case GDK_KEY_n:
case GDK_KEY_N:
return VirtualKey::kN;
case GDK_KEY_o:
case GDK_KEY_O:
return VirtualKey::kO;
case GDK_KEY_p:
case GDK_KEY_P:
return VirtualKey::kP;
case GDK_KEY_q:
case GDK_KEY_Q:
return VirtualKey::kQ;
case GDK_KEY_r:
case GDK_KEY_R:
return VirtualKey::kR;
case GDK_KEY_s:
case GDK_KEY_S:
return VirtualKey::kS;
case GDK_KEY_t:
case GDK_KEY_T:
return VirtualKey::kT;
case GDK_KEY_u:
case GDK_KEY_U:
return VirtualKey::kU;
case GDK_KEY_v:
case GDK_KEY_V:
return VirtualKey::kV;
case GDK_KEY_w:
case GDK_KEY_W:
return VirtualKey::kW;
case GDK_KEY_x:
case GDK_KEY_X:
return VirtualKey::kX;
case GDK_KEY_y:
case GDK_KEY_Y:
return VirtualKey::kY;
case GDK_KEY_z:
case GDK_KEY_Z:
return VirtualKey::kZ;
case GDK_KEY_0:
return VirtualKey::k0;
case GDK_KEY_1:
return VirtualKey::k1;
case GDK_KEY_2:
return VirtualKey::k2;
case GDK_KEY_3:
return VirtualKey::k3;
case GDK_KEY_4:
return VirtualKey::k4;
case GDK_KEY_5:
return VirtualKey::k5;
case GDK_KEY_6:
return VirtualKey::k6;
case GDK_KEY_7:
return VirtualKey::k7;
case GDK_KEY_8:
return VirtualKey::k8;
case GDK_KEY_9:
return VirtualKey::k9;
case GDK_KEY_semicolon:
return VirtualKey::kOem1;
case GDK_KEY_apostrophe:
return VirtualKey::kOem7;
case GDK_KEY_comma:
return VirtualKey::kOemComma;
case GDK_KEY_period:
return VirtualKey::kOemPeriod;
case GDK_KEY_Up:
return VirtualKey::kUp;
case GDK_KEY_Down:
return VirtualKey::kDown;
case GDK_KEY_Left:
return VirtualKey::kLeft;
case GDK_KEY_Right:
return VirtualKey::kRight;
case GDK_KEY_BackSpace:
return VirtualKey::kBack;
case GDK_KEY_Tab:
return VirtualKey::kTab;
case GDK_KEY_Return:
return VirtualKey::kReturn;
case GDK_KEY_Control_L:
return VirtualKey::kLControl;
case GDK_KEY_Control_R:
return VirtualKey::kRControl;
case GDK_KEY_Alt_L:
return VirtualKey::kLMenu;
case GDK_KEY_Alt_R:
return VirtualKey::kRMenu;
case GDK_KEY_Shift_L:
return VirtualKey::kLShift;
case GDK_KEY_Shift_R:
return VirtualKey::kRShift;
case GDK_KEY_space:
return VirtualKey::kSpace;
default:
XELOGW("Unhandled key code: {}", keyval);
return VirtualKey(keyval);
}
}
bool GTKWindow::HandleMouse(GdkEvent* event, bool GTKWindow::HandleMouse(GdkEvent* event,
WindowDestructionReceiver& destruction_receiver) { WindowDestructionReceiver& destruction_receiver) {
MouseEvent::Button button = MouseEvent::Button::kNone; MouseEvent::Button button = MouseEvent::Button::kNone;
@ -432,8 +574,7 @@ bool GTKWindow::HandleKeyboard(
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); uint32_t key_char = gdk_keyval_to_unicode(event->keyval);
// TODO(Triang3l): event->hardware_keycode to VirtualKey translation. KeyEvent e(this, TranslateVirtualKey(event->keyval), 1,
KeyEvent e(this, VirtualKey(event->hardware_keycode), 1,
event->type == GDK_KEY_RELEASE, shift_pressed, ctrl_pressed, event->type == GDK_KEY_RELEASE, shift_pressed, ctrl_pressed,
alt_pressed, super_pressed); alt_pressed, super_pressed);
switch (event->type) { switch (event->type) {
@ -512,7 +653,7 @@ gboolean GTKWindow::WindowEventHandler(GdkEvent* event) {
gboolean GTKWindow::WindowEventHandlerThunk(GtkWidget* widget, GdkEvent* event, gboolean GTKWindow::WindowEventHandlerThunk(GtkWidget* widget, GdkEvent* event,
gpointer user_data) { gpointer user_data) {
GTKWindow* window = reinterpret_cast<GTKWindow*>(user_data); auto* window = static_cast<GTKWindow*>(user_data);
if (!window || widget != window->window_ || if (!window || widget != window->window_ ||
reinterpret_cast<const GdkEventAny*>(event)->window != reinterpret_cast<const GdkEventAny*>(event)->window !=
gtk_widget_get_window(window->window_)) { gtk_widget_get_window(window->window_)) {
@ -558,7 +699,7 @@ gboolean GTKWindow::DrawingAreaEventHandler(GdkEvent* event) {
gboolean GTKWindow::DrawingAreaEventHandlerThunk(GtkWidget* widget, gboolean GTKWindow::DrawingAreaEventHandlerThunk(GtkWidget* widget,
GdkEvent* event, GdkEvent* event,
gpointer user_data) { gpointer user_data) {
GTKWindow* window = reinterpret_cast<GTKWindow*>(user_data); auto* window = static_cast<GTKWindow*>(user_data);
if (!window || widget != window->drawing_area_ || if (!window || widget != window->drawing_area_ ||
reinterpret_cast<const GdkEventAny*>(event)->window != reinterpret_cast<const GdkEventAny*>(event)->window !=
gtk_widget_get_window(window->drawing_area_)) { gtk_widget_get_window(window->drawing_area_)) {
@ -569,7 +710,7 @@ gboolean GTKWindow::DrawingAreaEventHandlerThunk(GtkWidget* widget,
gboolean GTKWindow::DrawHandler(GtkWidget* widget, cairo_t* cr, gboolean GTKWindow::DrawHandler(GtkWidget* widget, cairo_t* cr,
gpointer user_data) { gpointer user_data) {
GTKWindow* window = reinterpret_cast<GTKWindow*>(user_data); auto* window = static_cast<GTKWindow*>(user_data);
if (!window || widget != window->drawing_area_) { if (!window || widget != window->drawing_area_) {
return FALSE; return FALSE;
} }
@ -602,7 +743,7 @@ GTKMenuItem::GTKMenuItem(Type type, const std::string& text,
// TODO(dougvj) Would we ever need to escape underscores? // TODO(dougvj) Would we ever need to escape underscores?
// Replace & with _ for gtk to see the memonic // Replace & with _ for gtk to see the memonic
std::replace(label.begin(), label.end(), '&', '_'); std::replace(label.begin(), label.end(), '&', '_');
const gchar* gtk_label = reinterpret_cast<const gchar*>(label.c_str()); const auto* gtk_label = label.c_str();
switch (type) { switch (type) {
case MenuItem::Type::kNormal: case MenuItem::Type::kNormal:
default: default:
@ -628,7 +769,7 @@ GTKMenuItem::GTKMenuItem(Type type, const std::string& text,
g_object_ref_sink(menu_); g_object_ref_sink(menu_);
if (GTK_IS_MENU_ITEM(menu_)) { if (GTK_IS_MENU_ITEM(menu_)) {
g_signal_connect(menu_, "activate", G_CALLBACK(ActivateHandler), g_signal_connect(menu_, "activate", G_CALLBACK(ActivateHandler),
reinterpret_cast<gpointer>(this)); this);
} }
} }
} }
@ -640,13 +781,13 @@ GTKMenuItem::~GTKMenuItem() {
} }
void GTKMenuItem::OnChildAdded(MenuItem* generic_child_item) { void GTKMenuItem::OnChildAdded(MenuItem* generic_child_item) {
auto child_item = static_cast<GTKMenuItem*>(generic_child_item); auto child_item = dynamic_cast<GTKMenuItem*>(generic_child_item);
GtkWidget* submenu = nullptr; GtkWidget* submenu = nullptr;
switch (child_item->type()) { switch (child_item->type()) {
case MenuItem::Type::kNormal: case Type::kNormal:
// Nothing special. // Nothing special.
break; break;
case MenuItem::Type::kPopup: case Type::kPopup:
if (GTK_IS_MENU_ITEM(menu_)) { if (GTK_IS_MENU_ITEM(menu_)) {
submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_)); submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_));
// Get sub menu and if it doesn't exist create it // Get sub menu and if it doesn't exist create it
@ -659,8 +800,8 @@ void GTKMenuItem::OnChildAdded(MenuItem* generic_child_item) {
gtk_menu_shell_append(GTK_MENU_SHELL(menu_), child_item->handle()); gtk_menu_shell_append(GTK_MENU_SHELL(menu_), child_item->handle());
} }
break; break;
case MenuItem::Type::kSeparator: case Type::kSeparator:
case MenuItem::Type::kString: case Type::kString:
assert(GTK_IS_MENU_ITEM(menu_)); assert(GTK_IS_MENU_ITEM(menu_));
// Get sub menu and if it doesn't exist create it // Get sub menu and if it doesn't exist create it
submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_)); submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_));
@ -673,9 +814,7 @@ void GTKMenuItem::OnChildAdded(MenuItem* generic_child_item) {
} }
} }
// TODO(dougvj)
void GTKMenuItem::OnChildRemoved(MenuItem* generic_child_item) { void GTKMenuItem::OnChildRemoved(MenuItem* generic_child_item) {
assert_always();
} }
} // namespace ui } // namespace ui

View file

@ -59,6 +59,10 @@ class GTKWindow : public Window {
void BeginBatchedSizeUpdate(); void BeginBatchedSizeUpdate();
void EndBatchedSizeUpdate(WindowDestructionReceiver& destruction_receiver); void EndBatchedSizeUpdate(WindowDestructionReceiver& destruction_receiver);
// Translates a gtk virtual key to xenia ui::VirtualKey
static VirtualKey TranslateVirtualKey(guint keyval);
// Handling events related to the whole window. // Handling events related to the whole window.
bool HandleMouse(GdkEvent* event, bool HandleMouse(GdkEvent* event,
WindowDestructionReceiver& destruction_receiver); WindowDestructionReceiver& destruction_receiver);