github.com/jmigpin/editor@v1.6.0/driver/xdriver/xinput/kmap.go (about)

     1  package xinput
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"unicode"
     7  
     8  	"github.com/BurntSushi/xgb"
     9  	"github.com/BurntSushi/xgb/xproto"
    10  	"github.com/jmigpin/editor/util/uiutil/event"
    11  )
    12  
    13  // $ man keymaps
    14  // https://tronche.com/gui/x/xlib/input/XGetKeyboardMapping.html
    15  // https://tronche.com/gui/x/xlib/input/keyboard-encoding.html
    16  // http://wiki.linuxquestions.org/wiki/List_of_Keysyms_Recognised_by_Xmodmap
    17  // https://www.x.org/releases/X11R7.7/doc/libX11/i18n/compose/iso8859-2.html
    18  
    19  // Keyboard mapping
    20  type KMap struct {
    21  	si    *xproto.SetupInfo
    22  	reply *xproto.GetKeyboardMappingReply
    23  	conn  *xgb.Conn
    24  }
    25  
    26  func NewKMap(conn *xgb.Conn) (*KMap, error) {
    27  	km := &KMap{conn: conn}
    28  	err := km.ReadTable()
    29  	if err != nil {
    30  		return nil, err
    31  	}
    32  	return km, nil
    33  }
    34  
    35  //----------
    36  
    37  func (km *KMap) ReadTable() error {
    38  	si := xproto.Setup(km.conn)
    39  	count := byte(si.MaxKeycode - si.MinKeycode + 1)
    40  	if count == 0 {
    41  		return fmt.Errorf("count is 0")
    42  	}
    43  	cookie := xproto.GetKeyboardMapping(km.conn, si.MinKeycode, count)
    44  	reply, err := cookie.Reply()
    45  	if err != nil {
    46  		return err
    47  	}
    48  	if reply.KeysymsPerKeycode < 2 {
    49  		return fmt.Errorf("keysyms per keycode < 2")
    50  	}
    51  	km.reply = reply
    52  	km.si = si
    53  
    54  	//log.Printf("%v", km.KeysymTable())
    55  
    56  	return nil
    57  }
    58  
    59  //----------
    60  
    61  func (km *KMap) KeysymTable() string {
    62  	// some symbols are not present, like "~" and "^", and their X11 constant is present instead
    63  	o := "keysym table\n"
    64  	table := km.reply.Keysyms
    65  	width := int(km.reply.KeysymsPerKeycode)
    66  	for y := 0; y*width < len(table); y++ {
    67  		var u []string
    68  		for x := 0; x < width; x++ {
    69  			xks := table[y*width+x]
    70  			evs := translateXKeysymToEventKeySym(xks)
    71  			s2 := fmt.Sprintf("%v", evs)
    72  			if evs == event.KSymNone {
    73  				s2 = "-"
    74  			}
    75  			u = append(u, fmt.Sprintf("(%c,%v)", rune(xks), s2))
    76  		}
    77  		kc := xproto.Keycode(y) + km.si.MinKeycode
    78  		o += fmt.Sprintf("kc=%v: %v\n", kc, strings.Join(u, ", "))
    79  	}
    80  	return o
    81  }
    82  
    83  //----------
    84  
    85  func (km *KMap) keysymRow(keycode xproto.Keycode) []xproto.Keysym {
    86  	y := int(keycode - km.si.MinKeycode)
    87  	width := int(km.reply.KeysymsPerKeycode) // usually ~7
    88  	return km.reply.Keysyms[y*width : y*width+width]
    89  }
    90  
    91  //----------
    92  
    93  func (km *KMap) printKeysyms(keycode xproto.Keycode) {
    94  	keysyms := km.keysymRow(keycode)
    95  	//fmt.Printf("%v\n", keysyms)
    96  
    97  	{
    98  		u := []string{}
    99  		for _, ks := range keysyms {
   100  			u = append(u, string(rune(ks)))
   101  		}
   102  		fmt.Printf("[%v]\n", strings.Join(u, " "))
   103  	}
   104  	{
   105  		u := []string{}
   106  		for _, ks := range keysyms {
   107  			u = append(u, fmt.Sprintf("%x", ks))
   108  		}
   109  		fmt.Printf("[%v]\n", strings.Join(u, " "))
   110  	}
   111  }
   112  
   113  //----------
   114  
   115  func isKeypad(ks xproto.Keysym) bool {
   116  	return (0xFF80 <= ks && ks <= 0xFFBD) ||
   117  		(0x11000000 <= ks && ks <= 0x1100FFFF)
   118  }
   119  
   120  //----------
   121  
   122  // xproto.Keycode is a physical key.
   123  // xproto.Keysym is the encoding of a symbol on the cap of a key.
   124  // A list of keysyms is associated with each keycode.
   125  
   126  func (km *KMap) keysym(krow []xproto.Keysym, m uint16) xproto.Keysym {
   127  	bitIsSet := func(v uint16) bool { return m&v > 0 }
   128  	hasShift := bitIsSet(xproto.KeyButMaskShift)
   129  	hasCaps := bitIsSet(xproto.KeyButMaskLock)
   130  	hasCtrl := bitIsSet(xproto.KeyButMaskControl)
   131  	hasNum := bitIsSet(xproto.KeyButMaskMod2)
   132  	hasAltGr := bitIsSet(xproto.KeyButMaskMod5)
   133  
   134  	// keysym group
   135  	group := 0
   136  	if hasCtrl {
   137  		group = 1
   138  	} else if hasAltGr {
   139  		group = 2
   140  	}
   141  
   142  	// each group has two symbols
   143  	i1 := group * 2
   144  	i2 := i1 + 1
   145  	if i1 >= len(krow) {
   146  		return 0
   147  	}
   148  	if i2 >= len(krow) {
   149  		i2 = i1
   150  	}
   151  	ks1, ks2 := krow[i1], krow[i2]
   152  	if ks2 == 0 {
   153  		ks2 = ks1
   154  	}
   155  
   156  	// keypad
   157  	if hasNum && isKeypad(ks2) {
   158  		if hasShift {
   159  			return ks1
   160  		} else {
   161  			return ks2
   162  		}
   163  	}
   164  
   165  	r1 := rune(ks1)
   166  	hasLower := unicode.IsLower(unicode.ToLower(r1))
   167  
   168  	if hasLower {
   169  		shifted := (hasShift && !hasCaps) || (!hasShift && hasCaps)
   170  		if shifted {
   171  			return ks2
   172  		}
   173  		return ks1
   174  	}
   175  
   176  	if hasShift {
   177  		return ks2
   178  	}
   179  	return ks1
   180  }
   181  
   182  //----------
   183  
   184  func (km *KMap) Lookup(keycode xproto.Keycode, kmods uint16) (event.KeySym, rune) {
   185  	// keysym
   186  	ksRow := km.keysymRow(keycode)            // keycode to keysyms
   187  	xks := km.keysym(ksRow, kmods)            // keysyms to keysym
   188  	eks := translateXKeysymToEventKeySym(xks) // keysym to event.keysym
   189  	// rune
   190  	ru := keySymsRune(xks, eks)
   191  	return eks, ru
   192  }