2024-05-03 22:34:10 +01:00
# include "IKeyboard.hpp"
# include "../defines.hpp"
2024-06-11 17:17:45 +02:00
# include "../helpers/varlist/VarList.hpp"
2024-05-03 22:34:10 +01:00
# include "../managers/input/InputManager.hpp"
2024-07-21 13:09:54 +02:00
# include "../managers/SeatManager.hpp"
# include "../config/ConfigManager.hpp"
# include <sys/mman.h>
# include <aquamarine/input/Input.hpp>
# include <cstring>
2024-05-03 22:34:10 +01:00
2025-01-30 12:30:12 +01:00
using namespace Hyprutils : : OS ;
2024-07-21 13:09:54 +02:00
# define LED_COUNT 3
constexpr static std : : array < const char * , 8 > MODNAMES = {
XKB_MOD_NAME_SHIFT , XKB_MOD_NAME_CAPS , XKB_MOD_NAME_CTRL , XKB_MOD_NAME_ALT , XKB_MOD_NAME_NUM , " Mod3 " , XKB_MOD_NAME_LOGO , " Mod5 " ,
} ;
constexpr static std : : array < const char * , 3 > LEDNAMES = { XKB_LED_NAME_NUM , XKB_LED_NAME_CAPS , XKB_LED_NAME_SCROLL } ;
//
2024-05-03 22:34:10 +01:00
uint32_t IKeyboard : : getCapabilities ( ) {
return HID_INPUT_CAPABILITY_KEYBOARD ;
}
2024-05-05 22:18:10 +01:00
eHIDType IKeyboard : : getType ( ) {
return HID_TYPE_KEYBOARD ;
}
2024-05-03 22:34:10 +01:00
IKeyboard : : ~ IKeyboard ( ) {
2025-04-29 19:51:07 +02:00
m_events . destroy . emit ( ) ;
2024-05-03 22:34:10 +01:00
2024-07-21 13:09:54 +02:00
clearManuallyAllocd ( ) ;
}
void IKeyboard : : clearManuallyAllocd ( ) {
2025-04-29 19:51:07 +02:00
if ( m_xkbStaticState )
xkb_state_unref ( m_xkbStaticState ) ;
2024-07-21 13:09:54 +02:00
2025-04-29 19:51:07 +02:00
if ( m_xkbState )
xkb_state_unref ( m_xkbState ) ;
2024-07-21 13:09:54 +02:00
2025-04-29 19:51:07 +02:00
if ( m_xkbKeymap )
xkb_keymap_unref ( m_xkbKeymap ) ;
2024-07-21 13:09:54 +02:00
2025-04-29 19:51:07 +02:00
if ( m_xkbSymState )
xkb_state_unref ( m_xkbSymState ) ;
2025-01-28 10:15:08 +01:00
2025-04-29 19:51:07 +02:00
m_xkbSymState = nullptr ;
m_xkbKeymap = nullptr ;
m_xkbState = nullptr ;
m_xkbStaticState = nullptr ;
m_xkbKeymapFD . reset ( ) ;
2024-07-21 13:09:54 +02:00
}
void IKeyboard : : setKeymap ( const SStringRuleNames & rules ) {
2025-04-29 19:51:07 +02:00
if ( m_keymapOverridden ) {
2024-07-25 13:02:05 +02:00
Debug : : log ( LOG , " Ignoring setKeymap: keymap is overridden " ) ;
return ;
}
2025-04-29 19:51:07 +02:00
m_currentRules = rules ;
2024-07-21 13:09:54 +02:00
xkb_rule_names XKBRULES = {
. rules = rules . rules . c_str ( ) ,
. model = rules . model . c_str ( ) ,
. layout = rules . layout . c_str ( ) ,
. variant = rules . variant . c_str ( ) ,
. options = rules . options . c_str ( ) ,
} ;
const auto CONTEXT = xkb_context_new ( XKB_CONTEXT_NO_FLAGS ) ;
if ( ! CONTEXT ) {
Debug : : log ( ERR , " setKeymap: CONTEXT null?? " ) ;
2024-05-03 22:34:10 +01:00
return ;
2024-07-21 13:09:54 +02:00
}
clearManuallyAllocd ( ) ;
Debug : : log ( LOG , " Attempting to create a keymap for layout {} with variant {} (rules: {}, model: {}, options: {}) " , rules . layout , rules . variant , rules . rules , rules . model ,
rules . options ) ;
2025-04-29 19:51:07 +02:00
if ( ! m_xkbFilePath . empty ( ) ) {
auto path = absolutePath ( m_xkbFilePath , g_pConfigManager - > m_configCurrentPath ) ;
2024-07-21 13:09:54 +02:00
if ( FILE * const KEYMAPFILE = fopen ( path . c_str ( ) , " r " ) ; ! KEYMAPFILE )
Debug : : log ( ERR , " Cannot open input:kb_file= file for reading " ) ;
else {
2025-04-29 19:51:07 +02:00
m_xkbKeymap = xkb_keymap_new_from_file ( CONTEXT , KEYMAPFILE , XKB_KEYMAP_FORMAT_TEXT_V1 , XKB_KEYMAP_COMPILE_NO_FLAGS ) ;
2024-07-21 13:09:54 +02:00
fclose ( KEYMAPFILE ) ;
}
}
2025-04-29 19:51:07 +02:00
if ( ! m_xkbKeymap )
m_xkbKeymap = xkb_keymap_new_from_names ( CONTEXT , & XKBRULES , XKB_KEYMAP_COMPILE_NO_FLAGS ) ;
2024-07-21 13:09:54 +02:00
2025-04-29 19:51:07 +02:00
if ( ! m_xkbKeymap ) {
2024-07-21 13:09:54 +02:00
g_pConfigManager - > addParseError ( " Invalid keyboard layout passed. ( rules: " + rules . rules + " , model: " + rules . model + " , variant: " + rules . variant +
" , options: " + rules . options + " , layout: " + rules . layout + " ) " ) ;
Debug : : log ( ERR , " Keyboard layout {} with variant {} (rules: {}, model: {}, options: {}) couldn't have been loaded. " , rules . layout , rules . variant , rules . rules , rules . model ,
rules . options ) ;
memset ( & XKBRULES , 0 , sizeof ( XKBRULES ) ) ;
2025-04-29 19:51:07 +02:00
m_currentRules . rules = " " ;
m_currentRules . model = " " ;
m_currentRules . variant = " " ;
m_currentRules . options = " " ;
m_currentRules . layout = " us " ;
2024-07-21 13:09:54 +02:00
2025-04-29 19:51:07 +02:00
m_xkbKeymap = xkb_keymap_new_from_names ( CONTEXT , & XKBRULES , XKB_KEYMAP_COMPILE_NO_FLAGS ) ;
2024-07-21 13:09:54 +02:00
}
2025-04-29 19:51:07 +02:00
updateXKBTranslationState ( m_xkbKeymap ) ;
2024-07-21 13:09:54 +02:00
2025-04-29 19:51:07 +02:00
const auto NUMLOCKON = g_pConfigManager - > getDeviceInt ( m_hlName , " numlock_by_default " , " input:numlock_by_default " ) ;
2024-07-21 13:09:54 +02:00
if ( NUMLOCKON = = 1 ) {
// lock numlock
2025-04-29 19:51:07 +02:00
const auto IDX = xkb_map_mod_get_index ( m_xkbKeymap , XKB_MOD_NAME_NUM ) ;
2024-07-21 13:09:54 +02:00
if ( IDX ! = XKB_MOD_INVALID )
2025-04-29 19:51:07 +02:00
m_modifiersState . locked | = ( uint32_t ) 1 < < IDX ;
2024-07-21 13:09:54 +02:00
2024-08-23 22:35:52 +03:00
// 0 to avoid mods getting stuck if depressed during reload
2025-04-29 19:51:07 +02:00
updateModifiers ( 0 , 0 , m_modifiersState . locked , m_modifiersState . group ) ;
2024-07-21 13:09:54 +02:00
}
2025-04-29 19:51:07 +02:00
for ( size_t i = 0 ; i < std : : min ( LEDNAMES . size ( ) , m_ledIndexes . size ( ) ) ; + + i ) {
m_ledIndexes [ i ] = xkb_map_led_get_index ( m_xkbKeymap , LEDNAMES [ i ] ) ;
Debug : : log ( LOG , " xkb: LED index {} (name {}) got index {} " , i , LEDNAMES [ i ] , m_ledIndexes [ i ] ) ;
2024-07-21 13:09:54 +02:00
}
2025-04-29 19:51:07 +02:00
for ( size_t i = 0 ; i < std : : min ( MODNAMES . size ( ) , m_modIndexes . size ( ) ) ; + + i ) {
m_modIndexes [ i ] = xkb_map_mod_get_index ( m_xkbKeymap , MODNAMES [ i ] ) ;
Debug : : log ( LOG , " xkb: Mod index {} (name {}) got index {} " , i , MODNAMES [ i ] , m_modIndexes [ i ] ) ;
2024-07-21 13:09:54 +02:00
}
2024-05-03 22:34:10 +01:00
2024-07-25 13:02:05 +02:00
updateKeymapFD ( ) ;
xkb_context_unref ( CONTEXT ) ;
g_pSeatManager - > updateActiveKeyboardData ( ) ;
}
void IKeyboard : : updateKeymapFD ( ) {
2025-04-29 19:51:07 +02:00
Debug : : log ( LOG , " Updating keymap fd for keyboard {} " , m_deviceName ) ;
2024-07-25 13:02:05 +02:00
2025-04-29 19:51:07 +02:00
if ( m_xkbKeymapFD . isValid ( ) )
m_xkbKeymapFD . reset ( ) ;
2024-07-25 13:02:05 +02:00
2025-04-29 19:51:07 +02:00
auto cKeymapStr = xkb_keymap_get_as_string ( m_xkbKeymap , XKB_KEYMAP_FORMAT_TEXT_V1 ) ;
m_xkbKeymapString = cKeymapStr ;
2024-07-21 13:09:54 +02:00
free ( cKeymapStr ) ;
2025-01-30 12:30:12 +01:00
CFileDescriptor rw , ro ;
2025-04-29 19:51:07 +02:00
if ( ! allocateSHMFilePair ( m_xkbKeymapString . length ( ) + 1 , rw , ro ) )
2024-07-21 13:09:54 +02:00
Debug : : log ( ERR , " IKeyboard: failed to allocate shm pair for the keymap " ) ;
else {
2025-04-29 19:51:07 +02:00
auto keymapFDDest = mmap ( nullptr , m_xkbKeymapString . length ( ) + 1 , PROT_READ | PROT_WRITE , MAP_SHARED , rw . get ( ) , 0 ) ;
2025-01-30 12:30:12 +01:00
rw . reset ( ) ;
2024-07-21 13:09:54 +02:00
if ( keymapFDDest = = MAP_FAILED ) {
Debug : : log ( ERR , " IKeyboard: failed to mmap a shm pair for the keymap " ) ;
2025-01-30 12:30:12 +01:00
ro . reset ( ) ;
2024-07-21 13:09:54 +02:00
} else {
2025-04-29 19:51:07 +02:00
memcpy ( keymapFDDest , m_xkbKeymapString . c_str ( ) , m_xkbKeymapString . length ( ) ) ;
munmap ( keymapFDDest , m_xkbKeymapString . length ( ) + 1 ) ;
m_xkbKeymapFD = std : : move ( ro ) ;
2024-07-21 13:09:54 +02:00
}
}
2025-04-29 19:51:07 +02:00
Debug : : log ( LOG , " Updated keymap fd to {} " , m_xkbKeymapFD . get ( ) ) ;
2024-05-03 22:34:10 +01:00
}
void IKeyboard : : updateXKBTranslationState ( xkb_keymap * const keymap ) {
2025-04-29 19:51:07 +02:00
if ( m_xkbStaticState )
xkb_state_unref ( m_xkbStaticState ) ;
2024-07-25 13:02:05 +02:00
2025-04-29 19:51:07 +02:00
if ( m_xkbState )
xkb_state_unref ( m_xkbState ) ;
2024-07-21 13:09:54 +02:00
2025-04-29 19:51:07 +02:00
if ( m_xkbSymState )
xkb_state_unref ( m_xkbSymState ) ;
2024-10-08 13:15:53 +01:00
2025-04-29 19:51:07 +02:00
m_xkbState = nullptr ;
m_xkbStaticState = nullptr ;
m_xkbSymState = nullptr ;
2024-05-03 22:34:10 +01:00
if ( keymap ) {
Debug : : log ( LOG , " Updating keyboard {:x}'s translation state from a provided keymap " , ( uintptr_t ) this ) ;
2025-04-29 19:51:07 +02:00
m_xkbStaticState = xkb_state_new ( keymap ) ;
m_xkbState = xkb_state_new ( keymap ) ;
m_xkbSymState = xkb_state_new ( keymap ) ;
2024-05-03 22:34:10 +01:00
return ;
}
2025-04-29 19:51:07 +02:00
const auto KEYMAP = m_xkbKeymap ;
const auto STATE = m_xkbState ;
2024-05-03 22:34:10 +01:00
const auto LAYOUTSNUM = xkb_keymap_num_layouts ( KEYMAP ) ;
const auto PCONTEXT = xkb_context_new ( XKB_CONTEXT_NO_FLAGS ) ;
for ( uint32_t i = 0 ; i < LAYOUTSNUM ; + + i ) {
2024-05-07 10:07:50 -05:00
if ( xkb_state_layout_index_is_active ( STATE , i , XKB_STATE_LAYOUT_EFFECTIVE ) = = 1 ) {
2024-05-03 22:34:10 +01:00
Debug : : log ( LOG , " Updating keyboard {:x}'s translation state from an active index {} " , ( uintptr_t ) this , i ) ;
2025-04-29 19:51:07 +02:00
CVarList keyboardLayouts ( m_currentRules . layout , 0 , ' , ' ) ;
CVarList keyboardModels ( m_currentRules . model , 0 , ' , ' ) ;
CVarList keyboardVariants ( m_currentRules . variant , 0 , ' , ' ) ;
2024-05-03 22:34:10 +01:00
xkb_rule_names rules = { . rules = " " , . model = " " , . layout = " " , . variant = " " , . options = " " } ;
std : : string layout , model , variant ;
layout = keyboardLayouts [ i % keyboardLayouts . size ( ) ] ;
model = keyboardModels [ i % keyboardModels . size ( ) ] ;
variant = keyboardVariants [ i % keyboardVariants . size ( ) ] ;
rules . layout = layout . c_str ( ) ;
rules . model = model . c_str ( ) ;
rules . variant = variant . c_str ( ) ;
auto KEYMAP = xkb_keymap_new_from_names ( PCONTEXT , & rules , XKB_KEYMAP_COMPILE_NO_FLAGS ) ;
if ( ! KEYMAP ) {
Debug : : log ( ERR , " updateXKBTranslationState: keymap failed 1, fallback without model/variant " ) ;
rules . model = " " ;
rules . variant = " " ;
KEYMAP = xkb_keymap_new_from_names ( PCONTEXT , & rules , XKB_KEYMAP_COMPILE_NO_FLAGS ) ;
}
if ( ! KEYMAP ) {
Debug : : log ( ERR , " updateXKBTranslationState: keymap failed 2, fallback to us " ) ;
rules . layout = " us " ;
KEYMAP = xkb_keymap_new_from_names ( PCONTEXT , & rules , XKB_KEYMAP_COMPILE_NO_FLAGS ) ;
}
2025-04-29 19:51:07 +02:00
m_xkbState = xkb_state_new ( KEYMAP ) ;
m_xkbStaticState = xkb_state_new ( KEYMAP ) ;
m_xkbSymState = xkb_state_new ( KEYMAP ) ;
2024-05-03 22:34:10 +01:00
xkb_keymap_unref ( KEYMAP ) ;
xkb_context_unref ( PCONTEXT ) ;
return ;
}
}
Debug : : log ( LOG , " Updating keyboard {:x}'s translation state from an unknown index " , ( uintptr_t ) this ) ;
xkb_rule_names rules = {
2025-04-29 19:51:07 +02:00
. rules = m_currentRules . rules . c_str ( ) ,
. model = m_currentRules . model . c_str ( ) ,
. layout = m_currentRules . layout . c_str ( ) ,
. variant = m_currentRules . variant . c_str ( ) ,
. options = m_currentRules . options . c_str ( ) ,
2024-05-03 22:34:10 +01:00
} ;
const auto NEWKEYMAP = xkb_keymap_new_from_names ( PCONTEXT , & rules , XKB_KEYMAP_COMPILE_NO_FLAGS ) ;
2025-04-29 19:51:07 +02:00
m_xkbState = xkb_state_new ( NEWKEYMAP ) ;
m_xkbStaticState = xkb_state_new ( NEWKEYMAP ) ;
m_xkbSymState = xkb_state_new ( NEWKEYMAP ) ;
2024-05-03 22:34:10 +01:00
xkb_keymap_unref ( NEWKEYMAP ) ;
xkb_context_unref ( PCONTEXT ) ;
}
std : : string IKeyboard : : getActiveLayout ( ) {
2025-04-29 19:51:07 +02:00
const auto KEYMAP = m_xkbKeymap ;
const auto STATE = m_xkbState ;
2024-05-03 22:34:10 +01:00
const auto LAYOUTSNUM = xkb_keymap_num_layouts ( KEYMAP ) ;
for ( uint32_t i = 0 ; i < LAYOUTSNUM ; + + i ) {
2024-05-07 10:07:50 -05:00
if ( xkb_state_layout_index_is_active ( STATE , i , XKB_STATE_LAYOUT_EFFECTIVE ) = = 1 ) {
2024-05-03 22:34:10 +01:00
const auto LAYOUTNAME = xkb_keymap_layout_get_name ( KEYMAP , i ) ;
if ( LAYOUTNAME )
return std : : string ( LAYOUTNAME ) ;
return " error " ;
}
}
return " none " ;
}
2024-07-28 19:46:38 +09:00
std : : optional < uint32_t > IKeyboard : : getLEDs ( ) {
2025-04-29 19:51:07 +02:00
if ( m_xkbState = = nullptr )
2024-07-28 19:46:38 +09:00
return { } ;
2024-05-03 22:34:10 +01:00
uint32_t leds = 0 ;
2025-04-29 19:51:07 +02:00
for ( uint32_t i = 0 ; i < std : : min ( ( size_t ) LED_COUNT , m_ledIndexes . size ( ) ) ; + + i ) {
if ( xkb_state_led_index_is_active ( m_xkbState , m_ledIndexes [ i ] ) )
2024-05-03 22:34:10 +01:00
leds | = ( 1 < < i ) ;
}
2024-07-28 19:46:38 +09:00
return leds ;
}
void IKeyboard : : updateLEDs ( ) {
std : : optional < uint32_t > leds = getLEDs ( ) ;
if ( ! leds . has_value ( ) )
return ;
updateLEDs ( leds . value ( ) ) ;
2024-05-15 00:14:43 +09:00
}
void IKeyboard : : updateLEDs ( uint32_t leds ) {
2025-04-29 19:51:07 +02:00
if ( ! m_xkbState )
2024-05-15 00:14:43 +09:00
return ;
2025-04-29 19:51:07 +02:00
if ( isVirtual ( ) & & g_pInputManager - > shouldIgnoreVirtualKeyboard ( m_self . lock ( ) ) )
2024-05-03 22:34:10 +01:00
return ;
2024-07-21 13:09:54 +02:00
if ( ! aq ( ) )
return ;
aq ( ) - > updateLEDs ( leds ) ;
}
uint32_t IKeyboard : : getModifiers ( ) {
2025-04-29 19:51:07 +02:00
uint32_t modMask = m_modifiersState . depressed | m_modifiersState . latched ;
2024-07-21 13:09:54 +02:00
uint32_t mods = 0 ;
2025-04-29 19:51:07 +02:00
for ( size_t i = 0 ; i < m_modIndexes . size ( ) ; + + i ) {
if ( m_modIndexes [ i ] = = XKB_MOD_INVALID )
2024-07-21 13:09:54 +02:00
continue ;
2025-04-29 19:51:07 +02:00
if ( ! ( modMask & ( 1 < < m_modIndexes [ i ] ) ) )
2024-07-21 13:09:54 +02:00
continue ;
mods | = ( 1 < < i ) ;
}
return mods ;
}
void IKeyboard : : updateModifiers ( uint32_t depressed , uint32_t latched , uint32_t locked , uint32_t group ) {
2025-04-29 19:51:07 +02:00
if ( ! m_xkbState )
2024-07-21 13:09:54 +02:00
return ;
2025-04-29 19:51:07 +02:00
xkb_state_update_mask ( m_xkbState , depressed , latched , locked , 0 , 0 , group ) ;
2024-07-21 13:09:54 +02:00
2025-04-29 19:51:07 +02:00
if ( m_xkbSymState )
xkb_state_update_mask ( m_xkbSymState , 0 , 0 , 0 , 0 , 0 , group ) ;
2024-10-08 13:15:53 +01:00
2024-07-21 13:09:54 +02:00
if ( ! updateModifiersState ( ) )
return ;
2025-04-29 19:51:07 +02:00
m_keyboardEvents . modifiers . emit ( SModifiersEvent {
. depressed = m_modifiersState . depressed ,
. latched = m_modifiersState . latched ,
. locked = m_modifiersState . locked ,
. group = m_modifiersState . group ,
2024-07-27 18:43:45 +03:00
} ) ;
2024-07-21 13:09:54 +02:00
updateLEDs ( ) ;
}
bool IKeyboard : : updateModifiersState ( ) {
2025-04-29 19:51:07 +02:00
if ( ! m_xkbState )
2024-07-21 13:09:54 +02:00
return false ;
2025-04-29 19:51:07 +02:00
auto depressed = xkb_state_serialize_mods ( m_xkbState , XKB_STATE_MODS_DEPRESSED ) ;
auto latched = xkb_state_serialize_mods ( m_xkbState , XKB_STATE_MODS_LATCHED ) ;
auto locked = xkb_state_serialize_mods ( m_xkbState , XKB_STATE_MODS_LOCKED ) ;
auto group = xkb_state_serialize_layout ( m_xkbState , XKB_STATE_LAYOUT_EFFECTIVE ) ;
2024-07-21 13:09:54 +02:00
2025-04-29 19:51:07 +02:00
if ( depressed = = m_modifiersState . depressed & & latched = = m_modifiersState . latched & & locked = = m_modifiersState . locked & & group = = m_modifiersState . group )
2024-07-21 13:09:54 +02:00
return false ;
2025-04-29 19:51:07 +02:00
m_modifiersState . depressed = depressed ;
m_modifiersState . latched = latched ;
m_modifiersState . locked = locked ;
m_modifiersState . group = group ;
2024-07-21 13:09:54 +02:00
return true ;
}
void IKeyboard : : updateXkbStateWithKey ( uint32_t xkbKey , bool pressed ) {
2025-05-30 18:25:59 +05:00
const auto contains = std : : ranges : : find ( m_pressedXKB , xkbKey ) ! = m_pressedXKB . end ( ) ;
2024-07-21 13:09:54 +02:00
if ( contains & & pressed )
return ;
if ( ! contains & & ! pressed )
return ;
if ( contains )
2025-04-29 19:51:07 +02:00
std : : erase ( m_pressedXKB , xkbKey ) ;
2024-07-21 13:09:54 +02:00
else
2025-04-29 19:51:07 +02:00
m_pressedXKB . emplace_back ( xkbKey ) ;
2024-07-21 13:09:54 +02:00
2025-04-29 19:51:07 +02:00
xkb_state_update_key ( m_xkbState , xkbKey , pressed ? XKB_KEY_DOWN : XKB_KEY_UP ) ;
2024-07-21 13:09:54 +02:00
if ( updateModifiersState ( ) ) {
2025-04-29 19:51:07 +02:00
if ( m_xkbSymState )
xkb_state_update_mask ( m_xkbSymState , 0 , 0 , 0 , 0 , 0 , m_modifiersState . group ) ;
m_keyboardEvents . modifiers . emit ( SModifiersEvent {
. depressed = m_modifiersState . depressed ,
. latched = m_modifiersState . latched ,
. locked = m_modifiersState . locked ,
. group = m_modifiersState . group ,
2024-07-21 13:09:54 +02:00
} ) ;
}
2024-05-03 22:34:10 +01:00
}