github.com/rajveermalviya/gamen@v0.1.2-0.20220930195403-9be15877c1aa/internal/xkbcommon/xkbcommon.go (about)

     1  //go:build linux && !android
     2  
     3  package xkbcommon
     4  
     5  import (
     6  	"errors"
     7  	"fmt"
     8  	"os"
     9  	"unsafe"
    10  )
    11  
    12  /*
    13  
    14  #include <stdlib.h>
    15  
    16  #include <X11/Xlib-xcb.h>
    17  #include <xcb/xkb.h>
    18  
    19  #include <xkbcommon/xkbcommon.h>
    20  #include <xkbcommon/xkbcommon-x11.h>
    21  #include <xkbcommon/xkbcommon-compose.h>
    22  
    23  */
    24  import "C"
    25  
    26  var locale = func() string {
    27  	locale := os.Getenv("LC_ALL")
    28  	if locale == "" {
    29  		locale = os.Getenv("LC_CTYPE")
    30  	}
    31  	if locale == "" {
    32  		locale = os.Getenv("LANG")
    33  	}
    34  	if locale == "" {
    35  		locale = "C"
    36  	}
    37  	return locale
    38  }()
    39  
    40  type Xkb struct {
    41  	l       *xkbcommon_library
    42  	context *C.struct_xkb_context
    43  	keymap  *C.struct_xkb_keymap
    44  	state   *C.struct_xkb_state
    45  
    46  	composeTable *C.struct_xkb_compose_table
    47  	composeState *C.struct_xkb_compose_state
    48  }
    49  
    50  func New() (xkb *Xkb, err error) {
    51  	xkb = &Xkb{}
    52  
    53  	xkb.l, err = open_xkbcommon_library()
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	xkb.context = xkb.l.xkb_context_new(C.XKB_CONTEXT_NO_FLAGS)
    59  	if xkb.context == nil {
    60  		return nil, errors.New("failed to create xkb context")
    61  	}
    62  
    63  	localStr := C.CString(locale)
    64  	defer C.free(unsafe.Pointer(localStr))
    65  
    66  	xkb.composeTable = xkb.l.xkb_compose_table_new_from_locale(xkb.context, localStr, C.XKB_COMPOSE_COMPILE_NO_FLAGS)
    67  	if xkb.composeTable == nil {
    68  		return
    69  	}
    70  
    71  	xkb.composeState = xkb.l.xkb_compose_state_new(xkb.composeTable, C.XKB_COMPOSE_STATE_NO_FLAGS)
    72  	if xkb.composeState == nil {
    73  		return
    74  	}
    75  
    76  	return
    77  }
    78  
    79  type XcbConnection = C.xcb_connection_t
    80  
    81  func NewFromXcb(conn *XcbConnection) (xkb *Xkb, deviceId int32, firstEvent uint8, err error) {
    82  	xkb = &Xkb{}
    83  
    84  	xkb.l, err = open_xkbcommon_library()
    85  	if err != nil {
    86  		return nil, 0, 0, err
    87  	}
    88  
    89  	var firstXkbEvent C.uint8_t
    90  	ret := xkb.l.xkb_x11_setup_xkb_extension(conn,
    91  		C.XKB_X11_MIN_MAJOR_XKB_VERSION,
    92  		C.XKB_X11_MIN_MINOR_XKB_VERSION,
    93  		C.XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS,
    94  		nil, nil, &firstXkbEvent, nil)
    95  	if ret == 0 {
    96  		return nil, 0, 0, errors.New("failed to setup xkb extension")
    97  	}
    98  	firstEvent = uint8(firstXkbEvent)
    99  
   100  	deviceId = int32(xkb.l.xkb_x11_get_core_keyboard_device_id((*C.xcb_connection_t)(conn)))
   101  	if deviceId == -1 {
   102  		return nil, 0, 0, errors.New("unable to find core keyboard device")
   103  	}
   104  
   105  	xkb.context = xkb.l.xkb_context_new(C.XKB_CONTEXT_NO_FLAGS)
   106  	if xkb.context == nil {
   107  		return nil, 0, 0, errors.New("failed to create xkb context")
   108  	}
   109  
   110  	err = xkb.UpdateKeymap(conn, deviceId)
   111  	if err != nil {
   112  		xkb.l.xkb_context_unref(xkb.context)
   113  		return nil, 0, 0, fmt.Errorf("failed to create keymap: %w", err)
   114  	}
   115  
   116  	err = selectXkbEvents(xkb.l, (*C.xcb_connection_t)(conn), deviceId)
   117  	if err != nil {
   118  		xkb.l.xkb_context_unref(xkb.context)
   119  		return nil, 0, 0, fmt.Errorf("failed to select xcb-xkb events: %w", err)
   120  	}
   121  
   122  	localStr := C.CString(locale)
   123  	defer C.free(unsafe.Pointer(localStr))
   124  
   125  	xkb.composeTable = xkb.l.xkb_compose_table_new_from_locale(xkb.context, localStr, C.XKB_COMPOSE_COMPILE_NO_FLAGS)
   126  	if xkb.composeTable == nil {
   127  		return
   128  	}
   129  
   130  	xkb.composeState = xkb.l.xkb_compose_state_new(xkb.composeTable, C.XKB_COMPOSE_STATE_NO_FLAGS)
   131  	if xkb.composeState == nil {
   132  		return
   133  	}
   134  
   135  	return
   136  }
   137  
   138  func selectXkbEvents(l *xkbcommon_library, conn *C.xcb_connection_t, deviceID int32) error {
   139  	requiredEvents := C.XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY |
   140  		C.XCB_XKB_EVENT_TYPE_MAP_NOTIFY |
   141  		C.XCB_XKB_EVENT_TYPE_STATE_NOTIFY
   142  
   143  	requiredNknDetails := C.XCB_XKB_NKN_DETAIL_KEYCODES
   144  
   145  	requiredMapParts := C.XCB_XKB_MAP_PART_KEY_TYPES |
   146  		C.XCB_XKB_MAP_PART_KEY_SYMS |
   147  		C.XCB_XKB_MAP_PART_MODIFIER_MAP |
   148  		C.XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS |
   149  		C.XCB_XKB_MAP_PART_KEY_ACTIONS |
   150  		C.XCB_XKB_MAP_PART_VIRTUAL_MODS |
   151  		C.XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP
   152  
   153  	requiredStateDetails := C.XCB_XKB_STATE_PART_MODIFIER_BASE |
   154  		C.XCB_XKB_STATE_PART_MODIFIER_LATCH |
   155  		C.XCB_XKB_STATE_PART_MODIFIER_LOCK |
   156  		C.XCB_XKB_STATE_PART_GROUP_BASE |
   157  		C.XCB_XKB_STATE_PART_GROUP_LATCH |
   158  		C.XCB_XKB_STATE_PART_GROUP_LOCK
   159  
   160  	details := &C.xcb_xkb_select_events_details_t{
   161  		affectNewKeyboard:  C.uint16_t(requiredNknDetails),
   162  		newKeyboardDetails: C.uint16_t(requiredNknDetails),
   163  		affectState:        C.uint16_t(requiredStateDetails),
   164  		stateDetails:       C.uint16_t(requiredStateDetails),
   165  	}
   166  
   167  	cookie := l.xcb_xkb_select_events_aux_checked(
   168  		conn,
   169  		C.xcb_xkb_device_spec_t(deviceID),
   170  		C.uint16_t(requiredEvents),
   171  		0,
   172  		0,
   173  		C.uint16_t(requiredMapParts),
   174  		C.uint16_t(requiredMapParts),
   175  		details,
   176  	)
   177  
   178  	err := l.xcb_request_check(conn, cookie)
   179  	if err != nil {
   180  		C.free(unsafe.Pointer(err))
   181  		return errors.New("unable to bind events")
   182  	}
   183  	return nil
   184  }
   185  
   186  func (xkb *Xkb) KeymapFromBuffer(buf []byte) error {
   187  	keymap := xkb.l.xkb_keymap_new_from_buffer(
   188  		xkb.context,
   189  		(*C.char)(unsafe.Pointer(&buf[0])),
   190  		C.size_t(len(buf)-1),
   191  		C.XKB_KEYMAP_FORMAT_TEXT_V1,
   192  		C.XKB_KEYMAP_COMPILE_NO_FLAGS,
   193  	)
   194  	if keymap == nil {
   195  		return errors.New("unable to create keymap from buffer")
   196  	}
   197  
   198  	state := xkb.l.xkb_state_new(keymap)
   199  	if state == nil {
   200  		xkb.l.xkb_keymap_unref(keymap)
   201  		return errors.New("unable to create new state")
   202  	}
   203  
   204  	xkb.keymap = keymap
   205  	xkb.state = state
   206  	return nil
   207  }
   208  
   209  func (xkb *Xkb) UpdateKeymap(conn *XcbConnection, deviceID int32) error {
   210  	keymap := xkb.l.xkb_x11_keymap_new_from_device(
   211  		xkb.context,
   212  		conn,
   213  		C.int32_t(deviceID),
   214  		C.XKB_KEYMAP_COMPILE_NO_FLAGS)
   215  	if keymap == nil {
   216  		return errors.New("unable to create keymap from device")
   217  	}
   218  
   219  	state := xkb.l.xkb_x11_state_new_from_device(keymap, conn, C.int32_t(deviceID))
   220  	if state == nil {
   221  		xkb.l.xkb_keymap_unref(keymap)
   222  		return errors.New("unable to create state from device")
   223  	}
   224  
   225  	xkb.keymap = keymap
   226  	xkb.state = state
   227  	return nil
   228  }
   229  
   230  type (
   231  	KeyCode = C.xkb_keycode_t
   232  	KeySym  = C.xkb_keysym_t
   233  )
   234  
   235  func (xkb *Xkb) KeyRepeats(key KeyCode) bool {
   236  	if xkb.keymap == nil {
   237  		return false
   238  	}
   239  	return xkb.l.xkb_keymap_key_repeats(xkb.keymap, key) != 0
   240  }
   241  
   242  func (xkb *Xkb) GetOneSym(key KeyCode) C.xkb_keysym_t {
   243  	return xkb.l.xkb_state_key_get_one_sym(xkb.state, key)
   244  }
   245  
   246  func (xkb *Xkb) GetUtf8(key KeyCode, sym KeySym) string {
   247  	if xkb.composeState == nil {
   248  		size := xkb.l.xkb_state_key_get_utf8(xkb.state, C.xkb_keycode_t(key), nil, 0) + 1
   249  		if size > 1 {
   250  			buf := (*C.char)(C.malloc(C.size_t(size)))
   251  			defer C.free(unsafe.Pointer(buf))
   252  			xkb.l.xkb_state_key_get_utf8(xkb.state, C.xkb_keycode_t(key), buf, C.size_t(size))
   253  			return C.GoString(buf)
   254  		}
   255  		return ""
   256  	}
   257  
   258  	feedResult := xkb.l.xkb_compose_state_feed(xkb.composeState, C.xkb_keysym_t(sym))
   259  	if feedResult == C.XKB_COMPOSE_FEED_ACCEPTED {
   260  		status := xkb.l.xkb_compose_state_get_status(xkb.composeState)
   261  		switch status {
   262  		case C.XKB_COMPOSE_COMPOSED:
   263  			size := xkb.l.xkb_compose_state_get_utf8(xkb.composeState, nil, 0) + 1
   264  			if size > 1 {
   265  				buf := (*C.char)(C.malloc(C.size_t(size)))
   266  				defer C.free(unsafe.Pointer(buf))
   267  				xkb.l.xkb_compose_state_get_utf8(xkb.composeState, buf, C.size_t(size))
   268  				return C.GoString(buf)
   269  			}
   270  		case C.XKB_COMPOSE_NOTHING:
   271  			size := xkb.l.xkb_state_key_get_utf8(xkb.state, C.xkb_keycode_t(key), nil, 0) + 1
   272  			if size > 1 {
   273  				buf := (*C.char)(C.malloc(C.size_t(size)))
   274  				defer C.free(unsafe.Pointer(buf))
   275  				xkb.l.xkb_state_key_get_utf8(xkb.state, C.xkb_keycode_t(key), buf, C.size_t(size))
   276  				return C.GoString(buf)
   277  			}
   278  		}
   279  	}
   280  
   281  	return ""
   282  }
   283  
   284  type (
   285  	ModMask     = C.xkb_mod_mask_t
   286  	LayoutIndex = C.xkb_layout_index_t
   287  )
   288  
   289  func (xkb *Xkb) UpdateMask(
   290  	depressed_mods ModMask,
   291  	latched_mods ModMask,
   292  	locked_mods ModMask,
   293  	depressed_layout LayoutIndex,
   294  	latched_layout LayoutIndex,
   295  	locked_layout LayoutIndex,
   296  ) bool {
   297  	return xkb.l.xkb_state_update_mask(xkb.state,
   298  		depressed_mods,
   299  		latched_mods,
   300  		locked_mods,
   301  		depressed_layout,
   302  		latched_layout,
   303  		locked_layout,
   304  	)&C.XKB_STATE_MODS_EFFECTIVE == 0
   305  }
   306  
   307  var (
   308  	XKB_MOD_NAME_SHIFT = (*C.char)(unsafe.Pointer(&[]byte("Shift\x00")[0]))
   309  	XKB_MOD_NAME_CTRL  = (*C.char)(unsafe.Pointer(&[]byte("Control\x00")[0]))
   310  	XKB_MOD_NAME_ALT   = (*C.char)(unsafe.Pointer(&[]byte("Mod1\x00")[0]))
   311  	XKB_MOD_NAME_LOGO  = (*C.char)(unsafe.Pointer(&[]byte("Mod4\x00")[0]))
   312  )
   313  
   314  func (xkb *Xkb) ModIsShift() bool {
   315  	return xkb.l.xkb_state_mod_name_is_active(xkb.state, XKB_MOD_NAME_SHIFT, C.XKB_STATE_MODS_EFFECTIVE) == 1
   316  }
   317  
   318  func (xkb *Xkb) ModIsCtrl() bool {
   319  	return xkb.l.xkb_state_mod_name_is_active(xkb.state, XKB_MOD_NAME_CTRL, C.XKB_STATE_MODS_EFFECTIVE) == 1
   320  }
   321  
   322  func (xkb *Xkb) ModIsAlt() bool {
   323  	return xkb.l.xkb_state_mod_name_is_active(xkb.state, XKB_MOD_NAME_ALT, C.XKB_STATE_MODS_EFFECTIVE) == 1
   324  }
   325  
   326  func (xkb *Xkb) ModIsLogo() bool {
   327  	return xkb.l.xkb_state_mod_name_is_active(xkb.state, XKB_MOD_NAME_LOGO, C.XKB_STATE_MODS_EFFECTIVE) == 1
   328  }
   329  
   330  func (xkb *Xkb) Destroy() {
   331  	if xkb.state != nil {
   332  		xkb.l.xkb_state_unref(xkb.state)
   333  		xkb.state = nil
   334  	}
   335  	if xkb.keymap != nil {
   336  		xkb.l.xkb_keymap_unref(xkb.keymap)
   337  		xkb.keymap = nil
   338  	}
   339  	if xkb.composeState != nil {
   340  		xkb.l.xkb_compose_state_unref(xkb.composeState)
   341  		xkb.composeState = nil
   342  	}
   343  	if xkb.composeTable != nil {
   344  		xkb.l.xkb_compose_table_unref(xkb.composeTable)
   345  		xkb.composeTable = nil
   346  	}
   347  	if xkb.context != nil {
   348  		xkb.l.xkb_context_unref(xkb.context)
   349  		xkb.context = nil
   350  	}
   351  	if xkb.l != nil {
   352  		xkb.l.close()
   353  		xkb.l = nil
   354  	}
   355  }