WM: support the "Hyper" modifier key on Linux

Add support for a 5th modifier key called "hyper",
this is a modifier supported on Wayland & X11 although
other platforms could support an additional modifier too.

Both GNOME and KDE can map CapsLock to Hyper.
Other compositors can use the XKB_DEFAULT_OPTIONS environment variable.

This allows users to have an additional modifier for their own use
that doesn't conflict with other keys.

Ref !136340
This commit is contained in:
Campbell Barton
2025-03-25 23:32:41 +00:00
parent 241f626015
commit 5e2b421996
34 changed files with 258 additions and 30 deletions

View File

@@ -187,6 +187,8 @@ typedef enum {
GHOST_kModifierKeyRightControl,
GHOST_kModifierKeyLeftOS,
GHOST_kModifierKeyRightOS,
GHOST_kModifierKeyLeftHyper,
GHOST_kModifierKeyRightHyper,
GHOST_kModifierKeyNum
} GHOST_TModifierKey;
@@ -444,7 +446,10 @@ typedef enum {
GHOST_kKeyRightAlt,
GHOST_kKeyLeftOS, /* Command key on Apple, Windows key(s) on Windows. */
GHOST_kKeyRightOS,
#define _GHOST_KEY_MODIFIER_MAX GHOST_kKeyRightOS
GHOST_kKeyLeftHyper, /* Additional modifier on Wayland & X11, see !136340. */
GHOST_kKeyRightHyper,
#define _GHOST_KEY_MODIFIER_MAX GHOST_kKeyRightHyper
GHOST_kKeyGrLess, /* German PC only! */
GHOST_kKeyApp, /* Also known as menu key. */

View File

@@ -248,6 +248,8 @@ const char *GHOST_EventPrinter::getKeyString(const GHOST_TKey key) const
CASE_KEY(GHOST_kKeyRightAlt, "RightAlt");
CASE_KEY(GHOST_kKeyLeftOS, "LeftOS");
CASE_KEY(GHOST_kKeyRightOS, "RightOS");
CASE_KEY(GHOST_kKeyLeftHyper, "LeftHyper");
CASE_KEY(GHOST_kKeyRightHyper, "RightHyper");
CASE_KEY(GHOST_kKeyApp, "App");
CASE_KEY(GHOST_kKeyGrLess, "GrLess");
CASE_KEY(GHOST_kKeyCapsLock, "CapsLock");

View File

@@ -48,6 +48,12 @@ GHOST_TKey GHOST_ModifierKeys::getModifierKeyCode(GHOST_TModifierKey mask)
case GHOST_kModifierKeyRightOS:
key = GHOST_kKeyRightOS;
break;
case GHOST_kModifierKeyLeftHyper:
key = GHOST_kKeyLeftHyper;
break;
case GHOST_kModifierKeyRightHyper:
key = GHOST_kKeyRightHyper;
break;
default:
/* Should not happen. */
GHOST_ASSERT(0, "Invalid key!");
@@ -76,6 +82,10 @@ bool GHOST_ModifierKeys::get(GHOST_TModifierKey mask) const
return m_LeftOS;
case GHOST_kModifierKeyRightOS:
return m_RightOS;
case GHOST_kModifierKeyLeftHyper:
return m_LeftHyper;
case GHOST_kModifierKeyRightHyper:
return m_RightHyper;
default:
GHOST_ASSERT(0, "Invalid key!");
return false;
@@ -109,6 +119,12 @@ void GHOST_ModifierKeys::set(GHOST_TModifierKey mask, bool down)
case GHOST_kModifierKeyRightOS:
m_RightOS = down;
break;
case GHOST_kModifierKeyLeftHyper:
m_LeftHyper = down;
break;
case GHOST_kModifierKeyRightHyper:
m_RightHyper = down;
break;
default:
GHOST_ASSERT(0, "Invalid key!");
break;
@@ -125,6 +141,8 @@ void GHOST_ModifierKeys::clear()
m_RightControl = false;
m_LeftOS = false;
m_RightOS = false;
m_LeftHyper = false;
m_RightHyper = false;
}
bool GHOST_ModifierKeys::equals(const GHOST_ModifierKeys &keys) const
@@ -132,5 +150,6 @@ bool GHOST_ModifierKeys::equals(const GHOST_ModifierKeys &keys) const
return (m_LeftShift == keys.m_LeftShift) && (m_RightShift == keys.m_RightShift) &&
(m_LeftAlt == keys.m_LeftAlt) && (m_RightAlt == keys.m_RightAlt) &&
(m_LeftControl == keys.m_LeftControl) && (m_RightControl == keys.m_RightControl) &&
(m_LeftOS == keys.m_LeftOS) && (m_RightOS == keys.m_RightOS);
(m_LeftOS == keys.m_LeftOS) && (m_RightOS == keys.m_RightOS) &&
(m_LeftHyper == keys.m_LeftHyper) && (m_RightHyper == keys.m_RightHyper);
}

View File

@@ -71,4 +71,6 @@ struct GHOST_ModifierKeys {
/** Bit-field that stores the appropriate key state. */
uint8_t m_LeftOS : 1;
uint8_t m_RightOS : 1;
uint8_t m_LeftHyper : 1;
uint8_t m_RightHyper : 1;
};

View File

@@ -332,8 +332,9 @@ enum {
MOD_INDEX_ALT = 1,
MOD_INDEX_CTRL = 2,
MOD_INDEX_OS = 3,
MOD_INDEX_HYPER = 4,
};
#define MOD_INDEX_NUM (MOD_INDEX_OS + 1)
#define MOD_INDEX_NUM (MOD_INDEX_HYPER + 1)
struct GWL_ModifierInfo {
/** Only for printing messages. */
@@ -380,6 +381,15 @@ static const GWL_ModifierInfo g_modifier_info_table[MOD_INDEX_NUM] = {
/*mod_l*/ GHOST_kModifierKeyLeftOS,
/*mod_r*/ GHOST_kModifierKeyRightOS,
},
/*MOD_INDEX_HYPER*/
{
/*display_name*/ "Hyper",
/*xkb_id*/ XKB_VMOD_NAME_HYPER,
/*key_l*/ GHOST_kKeyLeftHyper,
/*key_r*/ GHOST_kKeyRightHyper,
/*mod_l*/ GHOST_kModifierKeyLeftHyper,
/*mod_r*/ GHOST_kModifierKeyRightHyper,
},
};
/** \} */
@@ -2107,6 +2117,8 @@ static GHOST_TKey xkb_map_gkey(const xkb_keysym_t sym)
GXMAP(gkey, XKB_KEY_Alt_R, GHOST_kKeyRightAlt);
GXMAP(gkey, XKB_KEY_Super_L, GHOST_kKeyLeftOS);
GXMAP(gkey, XKB_KEY_Super_R, GHOST_kKeyRightOS);
GXMAP(gkey, XKB_KEY_Hyper_L, GHOST_kKeyLeftHyper);
GXMAP(gkey, XKB_KEY_Hyper_R, GHOST_kKeyRightHyper);
GXMAP(gkey, XKB_KEY_Menu, GHOST_kKeyApp);
GXMAP(gkey, XKB_KEY_Caps_Lock, GHOST_kKeyCapsLock);

View File

@@ -710,6 +710,8 @@ bool GHOST_SystemX11::processEvents(bool waitForEvent)
XK_Alt_R,
XK_Super_L,
XK_Super_R,
XK_Hyper_L,
XK_Hyper_R,
};
for (int i = 0; i < int(ARRAY_SIZE(modifiers)); i++) {
@@ -1104,6 +1106,8 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
case GHOST_kKeyLeftControl:
case GHOST_kKeyLeftOS:
case GHOST_kKeyRightOS:
case GHOST_kKeyLeftHyper:
case GHOST_kKeyRightHyper:
case GHOST_kKey0:
case GHOST_kKey1:
case GHOST_kKey2:
@@ -1666,6 +1670,8 @@ GHOST_TSuccess GHOST_SystemX11::getModifierKeys(GHOST_ModifierKeys &keys) const
const static KeyCode alt_r = XKeysymToKeycode(m_display, XK_Alt_R);
const static KeyCode super_l = XKeysymToKeycode(m_display, XK_Super_L);
const static KeyCode super_r = XKeysymToKeycode(m_display, XK_Super_R);
const static KeyCode hyper_l = XKeysymToKeycode(m_display, XK_Hyper_L);
const static KeyCode hyper_r = XKeysymToKeycode(m_display, XK_Hyper_R);
/* shift */
keys.set(GHOST_kModifierKeyLeftShift,
@@ -1685,6 +1691,11 @@ GHOST_TSuccess GHOST_SystemX11::getModifierKeys(GHOST_ModifierKeys &keys) const
((m_keyboard_vector[super_l >> 3] >> (super_l & 7)) & 1) != 0);
keys.set(GHOST_kModifierKeyRightOS,
((m_keyboard_vector[super_r >> 3] >> (super_r & 7)) & 1) != 0);
/* hyper */
keys.set(GHOST_kModifierKeyLeftHyper,
((m_keyboard_vector[hyper_l >> 3] >> (hyper_l & 7)) & 1) != 0);
keys.set(GHOST_kModifierKeyRightHyper,
((m_keyboard_vector[hyper_r >> 3] >> (hyper_r & 7)) & 1) != 0);
return GHOST_kSuccess;
}
@@ -1917,6 +1928,8 @@ static GHOST_TKey ghost_key_from_keysym(const KeySym key)
GXMAP(type, XK_Alt_R, GHOST_kKeyRightAlt);
GXMAP(type, XK_Super_L, GHOST_kKeyLeftOS);
GXMAP(type, XK_Super_R, GHOST_kKeyRightOS);
GXMAP(type, XK_Hyper_L, GHOST_kKeyLeftHyper);
GXMAP(type, XK_Hyper_R, GHOST_kKeyRightHyper);
GXMAP(type, XK_Insert, GHOST_kKeyInsert);
GXMAP(type, XK_Delete, GHOST_kKeyDelete);

View File

@@ -47,7 +47,7 @@ def kmi_args_as_data(kmi):
if kmi.any:
s.append("\"any\": True")
else:
for attr in ("shift", "ctrl", "alt", "oskey"):
for attr in ("shift", "ctrl", "alt", "oskey", "hyper"):
if mod := getattr(kmi, attr):
s.append(f"\"{attr:s}\": " + ("-1" if mod == -1 else "True"))
if (mod := kmi.key_modifier) and (mod != 'NONE'):

View File

@@ -24,6 +24,7 @@ def generate(context, space_type, *, use_fallback_keys=True, use_reset=True):
("ctrl", False),
("alt", False),
("oskey", False),
("hyper", False),
("key_modifier", 'NONE'),
):
val = getattr(kmi, attr)

View File

@@ -201,6 +201,11 @@ def draw_kmi(display_keymaps, kc, km, kmi, layout, level):
subrow.prop(kmi, "alt_ui", toggle=True)
subrow.prop(kmi, "oskey_ui", text="Cmd", toggle=True)
# On systems that don't support Hyper, only show if it's enabled.
# Otherwise the user may have a key binding that doesn't work and can't be changed.
if _platform_supports_hyper_key() or kmi.hyper == 1:
subrow.prop(kmi, "hyper_ui", text="Hyper", toggle=True)
subrow.prop(kmi, "key_modifier", text="", event=True)
# Operator properties
@@ -218,6 +223,16 @@ _EVENT_TYPES = set()
_EVENT_TYPE_MAP = {}
_EVENT_TYPE_MAP_EXTRA = {}
_HAS_HYPER_KEY = None
def _platform_supports_hyper_key():
global _HAS_HYPER_KEY
if _HAS_HYPER_KEY is None:
from _bpy import _ghost_backend
_HAS_HYPER_KEY = _ghost_backend() in {'WAYLAND', 'X11'}
return _HAS_HYPER_KEY
def draw_filtered(display_keymaps, filter_type, filter_text, layout):
@@ -259,6 +274,7 @@ def draw_filtered(display_keymaps, filter_type, filter_text, layout):
"alt": "alt",
"shift": "shift",
"oskey": "oskey",
"hyper": "hyper",
"any": "any",
# macOS specific modifiers names

View File

@@ -259,7 +259,7 @@ def _fallback_id(text, fallback):
def any_except(*args):
mod = {"ctrl": -1, "alt": -1, "shift": -1, "oskey": -1}
mod = {"ctrl": -1, "alt": -1, "shift": -1, "oskey": -1, "hyper": -1}
for arg in args:
del mod[arg]
return mod

View File

@@ -2610,7 +2610,7 @@ class WM_OT_toolbar_prompt(Operator):
# Pressing entry even again exists, as long as it's not mapped to a key (for convenience).
if event_type == self._init_event_type:
if event_value == 'RELEASE':
if not (event.ctrl or event.alt or event.shift or event.oskey):
if not (event.ctrl or event.alt or event.shift or event.oskey or event.hyper):
context.workspace.status_text_set(None)
return {'CANCELLED'}

View File

@@ -27,7 +27,7 @@
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 10
#define BLENDER_FILE_SUBVERSION 11
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to

View File

@@ -1442,6 +1442,25 @@ void blo_do_versions_userdef(UserDef *userdef)
}
}
if (!USER_VERSION_ATLEAST(405, 11)) {
wmKeyConfigFilterItemParams params{};
params.check_item = true;
params.check_diff_item_add = true;
BKE_keyconfig_pref_filter_items(
userdef,
&params,
[](wmKeyMapItem *kmi, void * /*user_data*/) -> bool {
if (kmi->shift == KM_ANY && kmi->ctrl == KM_ANY && kmi->alt == KM_ANY &&
kmi->oskey == KM_ANY)
{
kmi->hyper = KM_ANY;
}
return false;
},
nullptr);
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a USER_VERSION_ATLEAST check.

View File

@@ -968,6 +968,7 @@ DEF_ICON_COLOR(EVENT_SHIFT)
DEF_ICON_COLOR(EVENT_CTRL)
DEF_ICON_COLOR(EVENT_ALT)
DEF_ICON_COLOR(EVENT_OS)
DEF_ICON_COLOR(EVENT_HYPER)
DEF_ICON_COLOR(EVENT_F1)
DEF_ICON_COLOR(EVENT_F2)

View File

@@ -4105,6 +4105,7 @@ static void ui_but_update_ex(uiBut *but, const bool validate)
kmi_dummy.ctrl = (hotkey_but->modifier_key & KM_CTRL) ? KM_MOD_HELD : KM_NOTHING;
kmi_dummy.alt = (hotkey_but->modifier_key & KM_ALT) ? KM_MOD_HELD : KM_NOTHING;
kmi_dummy.oskey = (hotkey_but->modifier_key & KM_OSKEY) ? KM_MOD_HELD : KM_NOTHING;
kmi_dummy.hyper = (hotkey_but->modifier_key & KM_HYPER) ? KM_MOD_HELD : KM_NOTHING;
but->drawstr = WM_keymap_item_to_string(&kmi_dummy, true).value_or("");
}

View File

@@ -710,6 +710,9 @@ int UI_icon_from_keymap_item(const wmKeyMapItem *kmi, int r_icon_mod[KM_MOD_NUM]
if (kmi->oskey == KM_MOD_HELD) {
r_icon_mod[i++] = ICON_EVENT_OS;
}
if (!ELEM(kmi->hyper, KM_NOTHING, KM_ANY)) {
r_icon_mod[i++] = ICON_EVENT_HYPER;
}
}
return UI_icon_from_event_type(kmi->type, kmi->val);
}
@@ -762,6 +765,7 @@ static void init_event_icons()
INIT_EVENT_ICON(ICON_EVENT_CTRL, EVT_LEFTCTRLKEY, KM_ANY);
INIT_EVENT_ICON(ICON_EVENT_ALT, EVT_LEFTALTKEY, KM_ANY);
INIT_EVENT_ICON(ICON_EVENT_OS, EVT_OSKEY, KM_ANY);
INIT_EVENT_ICON(ICON_EVENT_HYPER, EVT_HYPER, KM_ANY);
INIT_EVENT_ICON(ICON_EVENT_F1, EVT_F1KEY, KM_ANY);
INIT_EVENT_ICON(ICON_EVENT_F2, EVT_F2KEY, KM_ANY);
INIT_EVENT_ICON(ICON_EVENT_F3, EVT_F3KEY, KM_ANY);

View File

@@ -253,6 +253,9 @@ void icon_draw_rect_input(const float x,
icon_draw_rect_input_text(&rect, IFACE_("OS"), aspect, alpha, inverted, ICON_KEY_EMPTY2);
}
}
else if (icon_id == ICON_EVENT_HYPER) {
icon_draw_rect_input_text(&rect, IFACE_("Hyp"), aspect, alpha, inverted, ICON_KEY_EMPTY2);
}
else if (icon_id == ICON_EVENT_DEL) {
icon_draw_rect_input_text(&rect, IFACE_("Del"), aspect, alpha, inverted, ICON_KEY_EMPTY2);
}

View File

@@ -987,6 +987,8 @@ static void ui_keymap_but_cb(bContext * /*C*/, void *but_v, void * /*key_v*/)
&but->rnapoin, "alt", (hotkey_but->modifier_key & KM_ALT) ? KM_MOD_HELD : KM_NOTHING);
RNA_int_set(
&but->rnapoin, "oskey", (hotkey_but->modifier_key & KM_OSKEY) ? KM_MOD_HELD : KM_NOTHING);
RNA_int_set(
&but->rnapoin, "hyper", (hotkey_but->modifier_key & KM_HYPER) ? KM_MOD_HELD : KM_NOTHING);
}
/**

View File

@@ -1129,8 +1129,15 @@ std::optional<std::string> UI_key_event_operator_string(const bContext *C,
}
if ((event_val != KM_NOTHING) && (event_type != KM_NOTHING)) {
return WM_keymap_item_raw_to_string(
KM_NOTHING, KM_NOTHING, KM_NOTHING, KM_NOTHING, 0, event_val, event_type, false);
return WM_keymap_item_raw_to_string(KM_NOTHING,
KM_NOTHING,
KM_NOTHING,
KM_NOTHING,
KM_NOTHING,
0,
event_val,
event_type,
false);
}
return std::nullopt;

View File

@@ -37,7 +37,7 @@ static const wmKeyMapItem *keymap_item_from_enum_item(const wmKeyMap *keymap,
static bool keymap_item_can_collapse(const wmKeyMapItem *kmi_a, const wmKeyMapItem *kmi_b)
{
return (kmi_a->shift == kmi_b->shift && kmi_a->ctrl == kmi_b->ctrl && kmi_a->alt == kmi_b->alt &&
kmi_a->oskey == kmi_b->oskey);
kmi_a->oskey == kmi_b->oskey && kmi_a->hyper == kmi_b->hyper);
}
int uiTemplateStatusBarModalItem(uiLayout *layout,

View File

@@ -965,6 +965,9 @@ void WorkspaceStatus::opmodal(std::string text,
if (kmi->oskey == KM_MOD_HELD) {
ed_workspace_status_item(workspace_, {}, ICON_EVENT_OS, 0.0f, inverted);
}
if (!ELEM(kmi->hyper, KM_NOTHING, KM_ANY)) {
ed_workspace_status_item(workspace_, {}, ICON_EVENT_HYPER, 0.0f, inverted);
}
ed_workspace_status_icon_item(workspace_, icon, inverted);
ed_workspace_status_text_item(workspace_, std::move(text));
}

View File

@@ -454,6 +454,9 @@ struct ViewOpsData_Utility : ViewOpsData {
if (kmi_merge->oskey == KM_MOD_HELD || ELEM(kmi_merge->type, EVT_OSKEY)) {
kmi_cpy->oskey = KM_MOD_HELD;
}
if (kmi_merge->hyper == KM_MOD_HELD || ELEM(kmi_merge->type, EVT_HYPER)) {
kmi_cpy->hyper = KM_MOD_HELD;
}
if (!ISKEYMODIFIER(kmi_merge->type)) {
kmi_cpy->keymodifier = kmi_merge->type;
}

View File

@@ -1995,7 +1995,8 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
(ELEM(kmi->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY) &&
(event->modifier & KM_SHIFT)) ||
(ELEM(kmi->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY) && (event->modifier & KM_ALT)) ||
((kmi->type == EVT_OSKEY) && (event->modifier & KM_OSKEY)))
((kmi->type == EVT_OSKEY) && (event->modifier & KM_OSKEY)) ||
((kmi->type == EVT_HYPER) && (event->modifier & KM_HYPER)))
{
t->modifiers &= ~MOD_NODE_ATTACH;
}

View File

@@ -472,6 +472,10 @@ typedef struct wmKeyMapItem {
int8_t alt;
/** Also known as "Apple", "Windows-Key" or "Super. */
int8_t oskey;
/** See #KM_HYPER for details. */
int8_t hyper;
char _pad0[7];
/** Raw-key modifier. */
short keymodifier;

View File

@@ -283,6 +283,7 @@ const EnumPropertyItem rna_enum_event_type_items[] = {
{EVT_RIGHTSHIFTKEY, "RIGHT_SHIFT", 0, "Right Shift", "ShiftR"},
RNA_ENUM_ITEM_SEPR,
{EVT_OSKEY, "OSKEY", 0, "OS Key", "Cmd"},
{EVT_HYPER, "HYPER", 0, "Hyper", "Hyp"},
{EVT_APPKEY, "APP", 0, "Application", "App"},
{EVT_GRLESSKEY, "GRLESS", 0, "Grless", ""},
{EVT_ESCKEY, "ESC", 0, "Esc", ""},
@@ -1136,7 +1137,9 @@ static bool rna_KeyMapItem_any_get(PointerRNA *ptr)
{
wmKeyMapItem *kmi = (wmKeyMapItem *)ptr->data;
if (kmi->shift == KM_ANY && kmi->ctrl == KM_ANY && kmi->alt == KM_ANY && kmi->oskey == KM_ANY) {
if (kmi->shift == KM_ANY && kmi->ctrl == KM_ANY && kmi->alt == KM_ANY && kmi->oskey == KM_ANY &&
kmi->hyper == KM_ANY)
{
return true;
}
else {
@@ -1149,10 +1152,10 @@ static void rna_KeyMapItem_any_set(PointerRNA *ptr, bool value)
wmKeyMapItem *kmi = (wmKeyMapItem *)ptr->data;
if (value) {
kmi->shift = kmi->ctrl = kmi->alt = kmi->oskey = KM_ANY;
kmi->shift = kmi->ctrl = kmi->alt = kmi->oskey = kmi->hyper = KM_ANY;
}
else {
kmi->shift = kmi->ctrl = kmi->alt = kmi->oskey = KM_NOTHING;
kmi->shift = kmi->ctrl = kmi->alt = kmi->oskey = kmi->hyper = KM_NOTHING;
}
}
@@ -1180,6 +1183,12 @@ static bool rna_KeyMapItem_oskey_get(PointerRNA *ptr)
return kmi->oskey != KM_NOTHING;
}
static bool rna_KeyMapItem_hyper_get(PointerRNA *ptr)
{
wmKeyMapItem *kmi = (wmKeyMapItem *)ptr->data;
return kmi->hyper != KM_NOTHING;
}
static PointerRNA rna_WindowManager_active_keyconfig_get(PointerRNA *ptr)
{
wmWindowManager *wm = static_cast<wmWindowManager *>(ptr->data);
@@ -2435,6 +2444,11 @@ static void rna_def_event(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "OS Key", "True when the Cmd key is held");
prop = RNA_def_property(srna, "hyper", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "modifier", KM_HYPER);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Hyper", "True when the Hyper key is held");
RNA_define_verify_sdna(true); /* not in sdna */
}
@@ -2991,6 +3005,12 @@ static void rna_def_keyconfig(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "OS Key", "Operating system key pressed, -1 for any state");
RNA_def_property_update(prop, 0, "rna_KeyMapItem_update");
prop = RNA_def_property(srna, "hyper", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, nullptr, "hyper");
RNA_def_property_range(prop, KM_ANY, KM_MOD_HELD);
RNA_def_property_ui_text(prop, "Hyper", "Hyper key pressed, -1 for any state");
RNA_def_property_update(prop, 0, "rna_KeyMapItem_update");
/* XXX(@ideasman42): the `*_ui` suffix is only for the UI, may be removed,
* since this is only exposed so the UI can show these settings as toggle-buttons. */
prop = RNA_def_property(srna, "shift_ui", PROP_BOOLEAN, PROP_NONE);
@@ -3021,6 +3041,18 @@ static void rna_def_keyconfig(BlenderRNA *brna)
// RNA_def_property_enum_items(prop, keymap_modifiers_items);
RNA_def_property_ui_text(prop, "OS Key", "Operating system key pressed");
RNA_def_property_update(prop, 0, "rna_KeyMapItem_update");
prop = RNA_def_property(srna, "hyper_ui", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "hyper", 0);
RNA_def_property_boolean_funcs(prop, "rna_KeyMapItem_hyper_get", nullptr);
// RNA_def_property_enum_items(prop, keymap_modifiers_items);
RNA_def_property_ui_text(
prop,
"Hyper",
"Hyper key pressed. "
/* Additional info since this is not so widely known. */
"An additional modifier which can be configured on Linux, typically replacing CapsLock");
RNA_def_property_update(prop, 0, "rna_KeyMapItem_update");
/* End `_ui` modifiers. */
prop = RNA_def_property(srna, "key_modifier", PROP_ENUM, PROP_NONE);

View File

@@ -273,7 +273,8 @@ static int rna_Operator_props_dialog_popup(bContext *C,
cancel_default);
}
static int keymap_item_modifier_flag_from_args(bool any, int shift, int ctrl, int alt, int oskey)
static int keymap_item_modifier_flag_from_args(
bool any, int shift, int ctrl, int alt, int oskey, int hyper)
{
int modifier = 0;
if (any) {
@@ -307,6 +308,13 @@ static int keymap_item_modifier_flag_from_args(bool any, int shift, int ctrl, in
else if (oskey == KM_ANY) {
modifier |= KM_OSKEY_ANY;
}
if (hyper == KM_MOD_HELD) {
modifier |= KM_HYPER;
}
else if (hyper == KM_ANY) {
modifier |= KM_HYPER_ANY;
}
}
return modifier;
}
@@ -321,6 +329,7 @@ static wmKeyMapItem *rna_KeyMap_item_new(wmKeyMap *km,
int ctrl,
int alt,
int oskey,
int hyper,
int keymodifier,
int direction,
bool repeat,
@@ -335,7 +344,7 @@ static wmKeyMapItem *rna_KeyMap_item_new(wmKeyMap *km,
// wmWindowManager *wm = CTX_wm_manager(C);
wmKeyMapItem *kmi = nullptr;
char idname_bl[OP_MAX_TYPENAME];
const int modifier = keymap_item_modifier_flag_from_args(any, shift, ctrl, alt, oskey);
const int modifier = keymap_item_modifier_flag_from_args(any, shift, ctrl, alt, oskey, hyper);
WM_operator_bl_idname(idname_bl, idname);
@@ -395,6 +404,7 @@ static wmKeyMapItem *rna_KeyMap_item_new_modal(wmKeyMap *km,
int ctrl,
int alt,
int oskey,
int hyper,
int keymodifier,
int direction,
bool repeat)
@@ -406,7 +416,7 @@ static wmKeyMapItem *rna_KeyMap_item_new_modal(wmKeyMap *km,
}
wmKeyMapItem *kmi = nullptr;
const int modifier = keymap_item_modifier_flag_from_args(any, shift, ctrl, alt, oskey);
const int modifier = keymap_item_modifier_flag_from_args(any, shift, ctrl, alt, oskey, hyper);
int propvalue = 0;
KeyMapItem_Params params{};
@@ -716,7 +726,8 @@ static wmEvent *rna_Window_event_add_simulate(wmWindow *win,
bool shift,
bool ctrl,
bool alt,
bool oskey)
bool oskey,
bool hyper)
{
if ((G.f & G_FLAG_EVENT_SIMULATE) == 0) {
BKE_report(reports, RPT_ERROR, "Not running with '--enable-event-simulate' enabled");
@@ -775,6 +786,9 @@ static wmEvent *rna_Window_event_add_simulate(wmWindow *win,
if (oskey) {
e.modifier |= KM_OSKEY;
}
if (hyper) {
e.modifier |= KM_HYPER;
}
e.utf8_buf[0] = '\0';
if (unicode != nullptr) {
@@ -862,6 +876,7 @@ void RNA_api_window(StructRNA *srna)
RNA_def_boolean(func, "ctrl", false, "Ctrl", "");
RNA_def_boolean(func, "alt", false, "Alt", "");
RNA_def_boolean(func, "oskey", false, "OS Key", "");
RNA_def_boolean(func, "hyper", false, "Hyper", "");
parm = RNA_def_pointer(func, "event", "Event", "Item", "Added key map item");
RNA_def_function_return(func, parm);
}
@@ -1304,6 +1319,7 @@ void RNA_api_keymapitems(StructRNA *srna)
RNA_def_int(func, "ctrl", KM_NOTHING, KM_ANY, KM_MOD_HELD, "Ctrl", "", KM_ANY, KM_MOD_HELD);
RNA_def_int(func, "alt", KM_NOTHING, KM_ANY, KM_MOD_HELD, "Alt", "", KM_ANY, KM_MOD_HELD);
RNA_def_int(func, "oskey", KM_NOTHING, KM_ANY, KM_MOD_HELD, "OS Key", "", KM_ANY, KM_MOD_HELD);
RNA_def_int(func, "hyper", KM_NOTHING, KM_ANY, KM_MOD_HELD, "Hyper", "", KM_ANY, KM_MOD_HELD);
RNA_def_enum(func, "key_modifier", rna_enum_event_type_items, 0, "Key Modifier", "");
RNA_def_enum(func, "direction", rna_enum_event_direction_items, KM_ANY, "Direction", "");
RNA_def_boolean(func, "repeat", false, "Repeat", "When set, accept key-repeat events");
@@ -1329,6 +1345,7 @@ void RNA_api_keymapitems(StructRNA *srna)
RNA_def_int(func, "ctrl", KM_NOTHING, KM_ANY, KM_MOD_HELD, "Ctrl", "", KM_ANY, KM_MOD_HELD);
RNA_def_int(func, "alt", KM_NOTHING, KM_ANY, KM_MOD_HELD, "Alt", "", KM_ANY, KM_MOD_HELD);
RNA_def_int(func, "oskey", KM_NOTHING, KM_ANY, KM_MOD_HELD, "OS Key", "", KM_ANY, KM_MOD_HELD);
RNA_def_int(func, "hyper", KM_NOTHING, KM_ANY, KM_MOD_HELD, "Hyper", "", KM_ANY, KM_MOD_HELD);
RNA_def_enum(func, "key_modifier", rna_enum_event_type_items, 0, "Key Modifier", "");
RNA_def_enum(func, "direction", rna_enum_event_direction_items, KM_ANY, "Direction", "");
RNA_def_boolean(func, "repeat", false, "Repeat", "When set, accept key-repeat events");

View File

@@ -181,6 +181,7 @@ std::optional<std::string> WM_keymap_item_raw_to_string(int8_t shift,
int8_t ctrl,
int8_t alt,
int8_t oskey,
int8_t hyper,
short keymodifier,
short val,
short type,

View File

@@ -277,16 +277,28 @@ enum {
KM_ALT = (1 << 2),
/** Use for Windows-Key on MS-Windows, Command-key on macOS and Super on Linux. */
KM_OSKEY = (1 << 3),
/**
* An additional modifier available on Unix systems (in addition to "Super").
* Even though standard keyboards don't have a "Hyper" key it is a valid modifier
* on Wayland and X11, where it is possible to map a key (typically CapsLock)
* to be a Hyper modifier, see !136340.
*
* Note that this is currently only supported on Wayland & X11
* but could could be supported on other platforms if desired.
*/
KM_HYPER = (1 << 4),
/* Used for key-map item creation function arguments. */
KM_SHIFT_ANY = (1 << 4),
KM_CTRL_ANY = (1 << 5),
KM_ALT_ANY = (1 << 6),
KM_OSKEY_ANY = (1 << 7),
KM_SHIFT_ANY = (1 << 5),
KM_CTRL_ANY = (1 << 6),
KM_ALT_ANY = (1 << 7),
KM_OSKEY_ANY = (1 << 8),
KM_HYPER_ANY = (1 << 9),
};
/** The number of modifiers #wmKeyMapItem & #wmEvent can use. */
#define KM_MOD_NUM 4
#define KM_MOD_NUM 5
/* `KM_MOD_*` flags for #wmKeyMapItem and `wmEvent.alt/shift/oskey/ctrl`. */
/* Note that #KM_ANY and #KM_NOTHING are used with these defines too. */
@@ -759,7 +771,7 @@ struct wmEvent {
*/
char utf8_buf[6];
/** Modifier states: #KM_SHIFT, #KM_CTRL, #KM_ALT & #KM_OSKEY. */
/** Modifier states: #KM_SHIFT, #KM_CTRL, #KM_ALT, #KM_OSKEY & #KM_HYPER. */
uint8_t modifier;
/** The direction (for #KM_CLICK_DRAG events only). */

View File

@@ -91,6 +91,8 @@ void WM_event_print(const wmEvent *event)
{"CTRL", KM_CTRL},
{"ALT", KM_ALT},
{"OS", KM_OSKEY},
{"HYPER", KM_HYPER},
};
event_ids_from_flag(
modifier_id, sizeof(modifier_id), flag_data, ARRAY_SIZE(flag_data), event->modifier);

View File

@@ -2397,6 +2397,12 @@ BLI_INLINE bool wm_eventmatch(const wmEvent *winevent, const wmKeyMapItem *kmi)
return false;
}
}
if (kmi->hyper != KM_ANY) {
const int8_t hyper = (winevent->modifier & KM_HYPER) ? KM_MOD_HELD : KM_NOTHING;
if ((hyper != kmi->hyper) && (winevent->type != EVT_HYPER)) {
return false;
}
}
/* Only key-map entry with key-modifier is checked,
* means all keys without modifier get handled too. */
@@ -5214,6 +5220,9 @@ static int wm_event_type_from_ghost_key(GHOST_TKey key)
case GHOST_kKeyLeftOS:
case GHOST_kKeyRightOS:
return EVT_OSKEY;
case GHOST_kKeyLeftHyper:
case GHOST_kKeyRightHyper:
return EVT_HYPER;
case GHOST_kKeyLeftAlt:
return EVT_LEFTALTKEY;
case GHOST_kKeyRightAlt:
@@ -6075,6 +6084,10 @@ void wm_event_add_ghostevent(wmWindowManager *wm,
SET_FLAG_FROM_TEST(event.modifier, (event.val == KM_PRESS), KM_OSKEY);
break;
}
case EVT_HYPER: {
SET_FLAG_FROM_TEST(event.modifier, (event.val == KM_PRESS), KM_HYPER);
break;
}
default: {
if (event.val == KM_PRESS) {
if (event.keymodifier == 0) {

View File

@@ -197,7 +197,7 @@ static bool wm_keymap_item_equals(wmKeyMapItem *a, wmKeyMapItem *b)
{
return (wm_keymap_item_equals_result(a, b) && a->type == b->type && a->val == b->val &&
a->shift == b->shift && a->ctrl == b->ctrl && a->alt == b->alt && a->oskey == b->oskey &&
a->keymodifier == b->keymodifier && a->maptype == b->maptype &&
a->hyper == b->hyper && a->keymodifier == b->keymodifier && a->maptype == b->maptype &&
((a->val != KM_CLICK_DRAG) || (a->direction == b->direction)) &&
((ISKEYBOARD(a->type) == 0) ||
(a->flag & KMI_REPEAT_IGNORE) == (b->flag & KMI_REPEAT_IGNORE)));
@@ -515,14 +515,15 @@ static void keymap_event_set(wmKeyMapItem *kmi, const KeyMapItem_Params *params)
kmi->direction = params->direction;
if (params->modifier == KM_ANY) {
kmi->shift = kmi->ctrl = kmi->alt = kmi->oskey = KM_ANY;
kmi->shift = kmi->ctrl = kmi->alt = kmi->oskey = kmi->hyper = KM_ANY;
}
else {
/* Only one of the flags should be set. */
BLI_assert(((params->modifier & (KM_SHIFT | KM_SHIFT_ANY)) != (KM_SHIFT | KM_SHIFT_ANY)) &&
((params->modifier & (KM_CTRL | KM_CTRL_ANY)) != (KM_CTRL | KM_CTRL_ANY)) &&
((params->modifier & (KM_ALT | KM_ALT_ANY)) != (KM_ALT | KM_ALT_ANY)) &&
((params->modifier & (KM_OSKEY | KM_OSKEY_ANY)) != (KM_OSKEY | KM_OSKEY_ANY)));
((params->modifier & (KM_OSKEY | KM_OSKEY_ANY)) != (KM_OSKEY | KM_OSKEY_ANY)) &&
((params->modifier & (KM_HYPER | KM_HYPER_ANY)) != (KM_HYPER | KM_HYPER_ANY)));
kmi->shift = ((params->modifier & KM_SHIFT) ?
KM_MOD_HELD :
@@ -536,6 +537,9 @@ static void keymap_event_set(wmKeyMapItem *kmi, const KeyMapItem_Params *params)
kmi->oskey = ((params->modifier & KM_OSKEY) ?
KM_MOD_HELD :
((params->modifier & KM_OSKEY_ANY) ? KM_ANY : KM_NOTHING));
kmi->hyper = ((params->modifier & KM_HYPER) ?
KM_MOD_HELD :
((params->modifier & KM_HYPER_ANY) ? KM_ANY : KM_NOTHING));
}
}
@@ -1199,6 +1203,7 @@ std::optional<std::string> WM_keymap_item_raw_to_string(const int8_t shift,
const int8_t ctrl,
const int8_t alt,
const int8_t oskey,
const int8_t hyper,
const short keymodifier,
const short val,
const short type,
@@ -1227,6 +1232,10 @@ std::optional<std::string> WM_keymap_item_raw_to_string(const int8_t shift,
result_array.append(WM_key_event_string(EVT_OSKEY, true));
result_array.append(space);
}
if (hyper == KM_MOD_HELD) {
result_array.append(WM_key_event_string(EVT_HYPER, true));
result_array.append(space);
}
if (keymodifier) {
result_array.append(WM_key_event_string(keymodifier, compact));
@@ -1252,8 +1261,15 @@ std::optional<std::string> WM_keymap_item_raw_to_string(const int8_t shift,
std::optional<std::string> WM_keymap_item_to_string(const wmKeyMapItem *kmi, const bool compact)
{
return WM_keymap_item_raw_to_string(
kmi->shift, kmi->ctrl, kmi->alt, kmi->oskey, kmi->keymodifier, kmi->val, kmi->type, compact);
return WM_keymap_item_raw_to_string(kmi->shift,
kmi->ctrl,
kmi->alt,
kmi->oskey,
kmi->hyper,
kmi->keymodifier,
kmi->val,
kmi->type,
compact);
}
std::optional<std::string> WM_modalkeymap_items_to_string(const wmKeyMap *km,
@@ -1736,6 +1752,10 @@ bool WM_keymap_item_compare(const wmKeyMapItem *k1, const wmKeyMapItem *k2)
return false;
}
if (k1->hyper != KM_ANY && k2->hyper != KM_ANY && k1->hyper != k2->hyper) {
return false;
}
if (k1->keymodifier != k2->keymodifier) {
return false;
}
@@ -2072,6 +2092,7 @@ void WM_keymap_item_restore_to_default(wmWindowManager *wm, wmKeyMap *keymap, wm
kmi->ctrl = orig->ctrl;
kmi->alt = orig->alt;
kmi->oskey = orig->oskey;
kmi->hyper = orig->hyper;
kmi->keymodifier = orig->keymodifier;
kmi->maptype = orig->maptype;
kmi->flag = (kmi->flag & ~(KMI_REPEAT_IGNORE | KMI_INACTIVE)) |

View File

@@ -545,6 +545,12 @@ static bool wm_keymap_item_uses_modifier(const wmKeyMapItem *kmi, const int even
return false;
}
}
if (kmi->hyper != KM_ANY) {
if ((kmi->hyper == KM_NOTHING) != ((event_modifier & KM_HYPER) == 0)) {
return false;
}
}
return true;
}

View File

@@ -152,6 +152,9 @@ static const struct {
{KM_OSKEY,
{GHOST_kKeyLeftOS, GHOST_kKeyRightOS},
{GHOST_kModifierKeyLeftOS, GHOST_kModifierKeyRightOS}},
{KM_HYPER,
{GHOST_kKeyLeftHyper, GHOST_kKeyRightHyper},
{GHOST_kModifierKeyLeftHyper, GHOST_kModifierKeyRightHyper}},
};
enum ModSide {

View File

@@ -176,6 +176,9 @@ enum {
/* Menu/App key. */
EVT_APPKEY = 0x00b2, /* 178 */
/** Additional modifier, see: #KM_HYPER for details. */
EVT_HYPER = 0x00b3, /* 179 */
EVT_PADPERIOD = 0x00c7, /* 199 */
EVT_CAPSLOCKKEY = 0x00d3, /* 211 */
@@ -395,7 +398,7 @@ enum {
/** Test whether the event is a modifier key. */
#define ISKEYMODIFIER(event_type) \
(((event_type) >= EVT_LEFTCTRLKEY && (event_type) <= EVT_LEFTSHIFTKEY) || \
(event_type) == EVT_OSKEY)
ELEM((event_type), EVT_OSKEY, EVT_HYPER))
/**
* Test whether the event is any kind: