mirror of
				https://gitee.com/jiuyilian/embedded-framework.git
				synced 2025-10-24 18:20:15 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			223 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			223 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /**
 | |
|  * @file xkb.c
 | |
|  *
 | |
|  */
 | |
| 
 | |
| /*********************
 | |
|  *      INCLUDES
 | |
|  *********************/
 | |
| #include "xkb.h"
 | |
| #if USE_XKB
 | |
| 
 | |
| #include <stdbool.h>
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <xkbcommon/xkbcommon.h>
 | |
| 
 | |
| /*********************
 | |
|  *      DEFINES
 | |
|  *********************/
 | |
| 
 | |
| /**********************
 | |
|  *      TYPEDEFS
 | |
|  **********************/
 | |
| 
 | |
| /**********************
 | |
|  *  STATIC PROTOTYPES
 | |
|  **********************/
 | |
| 
 | |
| /**********************
 | |
|  *  STATIC VARIABLES
 | |
|  **********************/
 | |
| static struct xkb_context *context = NULL;
 | |
| static xkb_drv_state_t default_state = { .keymap = NULL, .state = NULL };
 | |
| 
 | |
| /**********************
 | |
|  *      MACROS
 | |
|  **********************/
 | |
| 
 | |
| /**********************
 | |
|  *   GLOBAL FUNCTIONS
 | |
|  **********************/
 | |
| 
 | |
| /**
 | |
|  * Initialise the XKB system using the default driver state. Use this function if you only want
 | |
|  * to connect a single device.
 | |
|  * @return true if the initialisation was successful
 | |
|  */
 | |
| bool xkb_init(void) {
 | |
|   return xkb_init_state(&default_state);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Initialise the XKB system using a specific driver state. Use this function if you want to
 | |
|  * connect multiple devices.
 | |
|  * @param state XKB driver state to use
 | |
|  * @return true if the initialisation was successful
 | |
|  */
 | |
| bool xkb_init_state(xkb_drv_state_t *state) {
 | |
|   if (!context) {
 | |
|     context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
 | |
|     if (!context) {
 | |
|       perror("could not create new XKB context");
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
| #ifdef XKB_KEY_MAP
 | |
|   struct xkb_rule_names names = XKB_KEY_MAP;
 | |
|   return xkb_set_keymap_state(state, names);
 | |
| #else
 | |
|   return false; /* Keymap needs to be set manually using xkb_set_keymap_state to complete initialisation */
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * De-initialise a previously initialised driver state and free any dynamically allocated memory. Use this function if you want to
 | |
|  * reuse an existing driver state.
 | |
|  * @param state XKB driver state to use
 | |
|  */
 | |
| void xkb_deinit_state(xkb_drv_state_t *state) {
 | |
|   if (state->state) {
 | |
|     xkb_state_unref(state->state);
 | |
|     state->state = NULL;
 | |
|   }
 | |
| 
 | |
|   if (state->keymap) {
 | |
|     xkb_keymap_unref(state->keymap);
 | |
|     state->keymap = NULL;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Set a new keymap to be used for processing future key events using the default driver state. Use
 | |
|  * this function if you only want to connect a single device.
 | |
|  * @param names XKB rule names structure (use NULL components for default values)
 | |
|  * @return true if creating the keymap and associated state succeeded
 | |
|  */
 | |
| bool xkb_set_keymap(struct xkb_rule_names names) {
 | |
|   return xkb_set_keymap_state(&default_state, names);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Set a new keymap to be used for processing future key events using a specific driver state. Use
 | |
|  * this function if you want to connect multiple devices.
 | |
|  * @param state XKB driver state to use
 | |
|  * @param names XKB rule names structure (use NULL components for default values)
 | |
|  * @return true if creating the keymap and associated state succeeded
 | |
|  */
 | |
| bool xkb_set_keymap_state(xkb_drv_state_t *state, struct xkb_rule_names names) {
 | |
|   if (state->keymap) {
 | |
|     xkb_keymap_unref(state->keymap);
 | |
|     state->keymap = NULL;
 | |
|   }
 | |
| 
 | |
|   state->keymap = xkb_keymap_new_from_names(context, &names, XKB_KEYMAP_COMPILE_NO_FLAGS);
 | |
|   if (!state->keymap) {
 | |
|     perror("could not create XKB keymap");
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (state->state) {
 | |
|     xkb_state_unref(state->state);
 | |
|     state->state = NULL;
 | |
|   }
 | |
| 
 | |
|   state->state = xkb_state_new(state->keymap);
 | |
|   if (!state->state) {
 | |
|     perror("could not create XKB state");
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Process an evdev scancode using the default driver state. Use this function if you only want to
 | |
|  * connect a single device.
 | |
|  * @param scancode evdev scancode to process
 | |
|  * @param down true if the key was pressed, false if it was releases
 | |
|  * @return the (first) UTF-8 character produced by the event or 0 if no output was produced
 | |
|  */
 | |
| uint32_t xkb_process_key(uint32_t scancode, bool down) {
 | |
|   return xkb_process_key_state(&default_state, scancode, down);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Process an evdev scancode using a specific driver state. Use this function if you want to connect
 | |
|  * multiple devices.
 | |
|  * @param state XKB driver state to use
 | |
|  * @param scancode evdev scancode to process
 | |
|  * @param down true if the key was pressed, false if it was releases
 | |
|  * @return the (first) UTF-8 character produced by the event or 0 if no output was produced
 | |
|  */
 | |
| uint32_t xkb_process_key_state(xkb_drv_state_t *state, uint32_t scancode, bool down) {
 | |
|   /* Offset the evdev scancode by 8, see https://xkbcommon.org/doc/current/xkbcommon_8h.html#ac29aee92124c08d1953910ab28ee1997 */
 | |
|   xkb_keycode_t keycode = scancode + 8;
 | |
| 
 | |
|   uint32_t result = 0;
 | |
| 
 | |
|   switch (xkb_state_key_get_one_sym(state->state, keycode)) {
 | |
|     case XKB_KEY_BackSpace:
 | |
|       result = LV_KEY_BACKSPACE;
 | |
|       break;
 | |
|     case XKB_KEY_Return:
 | |
|     case XKB_KEY_KP_Enter:
 | |
|       result = LV_KEY_ENTER;
 | |
|       break;
 | |
|     case XKB_KEY_Prior:
 | |
|     case XKB_KEY_KP_Prior:
 | |
|       result = LV_KEY_PREV;
 | |
|       break;
 | |
|     case XKB_KEY_Next:
 | |
|     case XKB_KEY_KP_Next:
 | |
|       result = LV_KEY_NEXT;
 | |
|       break;
 | |
|     case XKB_KEY_Up:
 | |
|     case XKB_KEY_KP_Up:
 | |
|       result = LV_KEY_UP;
 | |
|       break;
 | |
|     case XKB_KEY_Left:
 | |
|     case XKB_KEY_KP_Left:
 | |
|       result = LV_KEY_LEFT;
 | |
|       break;
 | |
|     case XKB_KEY_Right:
 | |
|     case XKB_KEY_KP_Right:
 | |
|       result = LV_KEY_RIGHT;
 | |
|       break;
 | |
|     case XKB_KEY_Down:
 | |
|     case XKB_KEY_KP_Down:
 | |
|       result = LV_KEY_DOWN;
 | |
|       break;
 | |
|     case XKB_KEY_Tab:
 | |
|     case XKB_KEY_KP_Tab:
 | |
|       result = LV_KEY_NEXT;
 | |
|       break;
 | |
|     case XKB_KEY_ISO_Left_Tab: /* Sent on SHIFT + TAB */
 | |
|       result = LV_KEY_PREV;
 | |
|       break;
 | |
|     default:
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   if (result == 0) {
 | |
|     char buffer[4] = { 0, 0, 0, 0 };
 | |
|     int size = xkb_state_key_get_utf8(state->state, keycode, NULL, 0) + 1;
 | |
|     if (size > 1) {
 | |
|       xkb_state_key_get_utf8(state->state, keycode, buffer, size);
 | |
|       memcpy(&result, buffer, 4);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   xkb_state_update_key(state->state, keycode, down ? XKB_KEY_DOWN : XKB_KEY_UP);
 | |
| 
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| /**********************
 | |
|  *   STATIC FUNCTIONS
 | |
|  **********************/
 | |
| 
 | |
| #endif /* USE_XKB */
 | 
