mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-09 04:15:52 +00:00
parent
ca850c787f
commit
43a8118d2e
1
tool/xkb2ifcfg/.gitignore
vendored
Normal file
1
tool/xkb2ifcfg/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/xkb2ifcfg
|
36
tool/xkb2ifcfg/Makefile
Normal file
36
tool/xkb2ifcfg/Makefile
Normal file
@ -0,0 +1,36 @@
|
||||
#
|
||||
# Check config
|
||||
#
|
||||
|
||||
ifeq ($(filter clean cleanall, $(MAKECMDGOALS)),)
|
||||
GENODE_DIR := $(realpath $(dir $(MAKEFILE_LIST))/../..)
|
||||
ifneq ($(shell pkg-config --print-errors --errors-to-stdout --exists xkbcommon),)
|
||||
$(error Please install libxkbcommon-dev)
|
||||
endif
|
||||
endif
|
||||
|
||||
#
|
||||
# Build rules
|
||||
#
|
||||
|
||||
TARGET = xkb2ifcfg
|
||||
|
||||
SRC_CC = $(wildcard *.cc)
|
||||
SRC_H = $(wildcard *.h)
|
||||
|
||||
CFLAGS = -Werror -Wall -Wextra -Wno-attributes -std=gnu++17 -ggdb
|
||||
CFLAGS += -I$(GENODE_DIR)/repos/os/include
|
||||
CFLAGS += -I$(GENODE_DIR)/repos/base/include
|
||||
CFLAGS += -I$(GENODE_DIR)/repos/base/include/spec/64bit
|
||||
CFLAGS += -I$(GENODE_DIR)/repos/base/include/spec/x86
|
||||
CFLAGS += -I$(GENODE_DIR)/repos/base/include/spec/x86_64
|
||||
CFLAGS += $(shell pkg-config --cflags --libs xkbcommon)
|
||||
|
||||
$(TARGET): $(SRC_CC) $(SRC_H) Makefile
|
||||
g++ -o $@ $(SRC_CC) $(CFLAGS)
|
||||
|
||||
cleanall clean:
|
||||
rm -f $(TARGET) *~
|
||||
|
||||
|
||||
.PHONY: cleanall clean
|
74
tool/xkb2ifcfg/README
Normal file
74
tool/xkb2ifcfg/README
Normal file
@ -0,0 +1,74 @@
|
||||
XKB to Genode input_filter configuration
|
||||
|
||||
Christian Helmuth
|
||||
|
||||
|
||||
This tool generates Genode input_filter XML configuration files from
|
||||
X11 XKB configuration. It must be built and run on Linux with
|
||||
libxkbcommon development packages installed.
|
||||
|
||||
For pragmatic (and realistic reasons) this tool assumes a standard
|
||||
105-keys PC keyboard. This assumption automatically includes US
|
||||
keyboards with 104 keys. The differences are:
|
||||
|
||||
- PC105 has an additional 105th Less Then / Greater Then key <LSGT>
|
||||
right of Left Shift <LFSH> as additional 105th key
|
||||
- PC105 has the Backslash key <BKSL> left of Return <RTRN>
|
||||
|
||||
xkb2ifcfg supports Shift, AltGr, and Caps Lock modifiers, as well as
|
||||
localized dead-key/compose sequences. The tool does not support
|
||||
latched modifiers.
|
||||
|
||||
|
||||
Build
|
||||
=====
|
||||
|
||||
Just execute 'make' and 'xkb2ifcfg' should be built.
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
! xkb2ifcfg <command> <layout> <variant> <locale>
|
||||
!
|
||||
! Commands
|
||||
!
|
||||
! generate generate input_filter config
|
||||
! dump dump raw XKB keymap
|
||||
! info simple per-key information
|
||||
!
|
||||
! Examples
|
||||
!
|
||||
! xkb2ifcfg generate us "" en_US.UTF-8 > en_US.chargen
|
||||
! xkb2ifcfg info de nodeadkeys de_DE.UTF-8 > de_DE.chargen
|
||||
|
||||
All information is printed on standard output while diagnostic
|
||||
messages are written to standard error.
|
||||
|
||||
Valid 'layout' and 'variant' options can be figured out from the
|
||||
LAYOUTS section in
|
||||
|
||||
! man 7 xkeyboard-config
|
||||
|
||||
'variant' strings are depicted in parentheses after the layout (e.g.,
|
||||
'us(euro)').
|
||||
|
||||
The 'locale' option has the standard locale syntax (see
|
||||
/usr/share/i18n/locales).
|
||||
|
||||
|
||||
Generation of keyboard-layout PDFs (and other examples)
|
||||
=======================================================
|
||||
|
||||
setxkbmap -print -rules evdev -model pc104 -layout us -variant "" -option "" | xkbcomp -xkm - - | xkbprint -color -label name - - | ps2pdf - t.pdf
|
||||
setxkbmap -print -rules evdev -model pc105 -layout de -variant "nodeadkeys" -option "" | xkbcomp -xkm - - | xkbprint -color -label symbols - - | ps2pdf - t.pdf
|
||||
|
||||
setxkbmap -print -model pc104 -option -layout us
|
||||
setxkbmap -print -model pc105 -option -layout de -variant nodeadkeys
|
||||
setxkbmap -print -model pc105 -option -layout ch -variant de_nodeadkeys
|
||||
setxkbmap -print -model pc105 -option -layout ch -variant fr_nodeadkeys
|
||||
|
||||
setxkbmap -print -model pc105 -option -layout gb
|
||||
setxkbmap -print -model pc104 -option -layout us -variant euro
|
||||
setxkbmap -print -model pc104 -option -layout us -variant workman
|
||||
|
43
tool/xkb2ifcfg/genode.cc
Normal file
43
tool/xkb2ifcfg/genode.cc
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* \brief Genode utility support
|
||||
* \author Christian Helmuth <christian.helmuth@genode-labs.com>
|
||||
* \date 2019-07-25
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/console.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
|
||||
void Genode::Console::_out_string(char const *str)
|
||||
{
|
||||
if (!str)
|
||||
_out_string("<NULL>");
|
||||
else
|
||||
while (*str) _out_char(*str++);
|
||||
}
|
||||
|
||||
|
||||
void Genode::Console::printf(const char *format, ...)
|
||||
{
|
||||
va_list list;
|
||||
va_start(list, format);
|
||||
vprintf(format, list);
|
||||
va_end(list);
|
||||
}
|
||||
|
||||
|
||||
void Genode::Console::vprintf(const char *format, va_list list)
|
||||
{
|
||||
Formatted str(format, list);
|
||||
|
||||
_out_string(str.string());
|
||||
}
|
842
tool/xkb2ifcfg/main.cc
Normal file
842
tool/xkb2ifcfg/main.cc
Normal file
@ -0,0 +1,842 @@
|
||||
/*
|
||||
* \brief Libxkbcommon-based keyboard-layout generator
|
||||
* \author Christian Helmuth
|
||||
* \date 2019-07-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Linux includes */
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <xkbcommon/xkbcommon-compose.h>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/xml_generator.h>
|
||||
#include <util/retry.h>
|
||||
#include <util/reconstructible.h>
|
||||
|
||||
#include "xkb_mapping.h"
|
||||
#include "util.h"
|
||||
|
||||
using Genode::Xml_generator;
|
||||
using Genode::Constructible;
|
||||
|
||||
|
||||
static void append_comment(Xml_generator &xml, char const *prefix,
|
||||
char const *comment, char const *suffix)
|
||||
{
|
||||
xml.append(prefix);
|
||||
xml.append("<!-- "); xml.append(comment); xml.append(" -->");
|
||||
xml.append(suffix);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* XML generator that expands to your needs
|
||||
*/
|
||||
class Expanding_xml_buffer
|
||||
{
|
||||
private:
|
||||
|
||||
char const *_name;
|
||||
|
||||
enum { BUFFER_INCREMENT = 1024*1024 };
|
||||
|
||||
size_t _buffer_size { 0 };
|
||||
char *_buffer { nullptr };
|
||||
|
||||
void _increase_buffer()
|
||||
{
|
||||
::free(_buffer);
|
||||
|
||||
_buffer_size += BUFFER_INCREMENT;
|
||||
_buffer = (char *)::calloc(1, _buffer_size);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Expanding_xml_buffer() { _increase_buffer(); }
|
||||
|
||||
~Expanding_xml_buffer() { ::free(_buffer); }
|
||||
|
||||
char const *buffer() const { return _buffer; }
|
||||
|
||||
template <typename FUNC>
|
||||
void generate(char const *name, FUNC const &func)
|
||||
{
|
||||
Genode::retry<Xml_generator::Buffer_exceeded>(
|
||||
[&] () {
|
||||
Xml_generator xml(_buffer, _buffer_size,
|
||||
name, [&] () { func(xml); });
|
||||
},
|
||||
[&] () { _increase_buffer(); }
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static bool keysym_composing(xkb_compose_state *compose_state, xkb_keysym_t sym)
|
||||
{
|
||||
xkb_compose_state_reset(compose_state);
|
||||
xkb_compose_state_feed(compose_state, sym);
|
||||
|
||||
return (XKB_COMPOSE_COMPOSING == xkb_compose_state_get_status(compose_state));
|
||||
}
|
||||
|
||||
|
||||
struct Key_info
|
||||
{
|
||||
enum { COMMENT_CAPACITY = 100 };
|
||||
|
||||
xkb_keysym_t _keysym { XKB_KEY_NoSymbol };
|
||||
bool _composing { false };
|
||||
unsigned _utf32 { 0 };
|
||||
char *_comment { (char *)::calloc(1, COMMENT_CAPACITY) };
|
||||
|
||||
Key_info(xkb_state *state, xkb_compose_state *compose_state, Input::Keycode code)
|
||||
{
|
||||
_keysym = xkb_state_key_get_one_sym(state, Xkb::keycode(code));
|
||||
if (_keysym == XKB_KEY_NoSymbol) return;
|
||||
|
||||
_composing = keysym_composing(compose_state, _keysym);
|
||||
|
||||
if (!_composing) {
|
||||
_utf32 = xkb_state_key_get_utf32(state, Xkb::keycode(code));
|
||||
xkb_state_key_get_utf8(state, Xkb::keycode(code), _comment, COMMENT_CAPACITY);
|
||||
} else {
|
||||
char keysym_str[32];
|
||||
xkb_keysym_get_name(_keysym, keysym_str, sizeof(keysym_str));
|
||||
|
||||
for (Xkb::Dead_keysym &d : Xkb::dead_keysym) {
|
||||
if (d.xkb != _keysym) continue;
|
||||
|
||||
_utf32 = d.utf32;
|
||||
strncat(_comment, keysym_str, COMMENT_CAPACITY - 1);
|
||||
return;
|
||||
}
|
||||
::fprintf(stderr, "no UTF32 mapping found for composing keysym <%s>\n", keysym_str);
|
||||
}
|
||||
}
|
||||
|
||||
~Key_info()
|
||||
{
|
||||
::free(_comment);
|
||||
}
|
||||
|
||||
bool valid() const { return _utf32 != 0; }
|
||||
xkb_keysym_t keysym() const { return _keysym; }
|
||||
bool composing() const { return _composing; }
|
||||
unsigned utf32() const { return _utf32; }
|
||||
|
||||
void attributes(Xml_generator &xml)
|
||||
{
|
||||
xml.attribute("code", Formatted("0x%04x", _utf32).string());
|
||||
}
|
||||
|
||||
void comment(Xml_generator &xml)
|
||||
{
|
||||
append_comment(xml, "\t", _comment, "");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Keysym
|
||||
{
|
||||
bool composing { 0 };
|
||||
xkb_keysym_t keysym { 0 };
|
||||
unsigned utf32 { 0 };
|
||||
};
|
||||
|
||||
|
||||
static bool operator < (Keysym const &a, Keysym const &b)
|
||||
{
|
||||
return a.keysym < b.keysym;
|
||||
}
|
||||
|
||||
|
||||
template <Input::Keycode code>
|
||||
struct Locked
|
||||
{
|
||||
xkb_state *state;
|
||||
|
||||
Locked(xkb_state *state) : state(state)
|
||||
{
|
||||
xkb_state_update_key(state, Xkb::keycode(code), XKB_KEY_DOWN);
|
||||
xkb_state_update_key(state, Xkb::keycode(code), XKB_KEY_UP);
|
||||
}
|
||||
|
||||
~Locked()
|
||||
{
|
||||
xkb_state_update_key(state, Xkb::keycode(code), XKB_KEY_DOWN);
|
||||
xkb_state_update_key(state, Xkb::keycode(code), XKB_KEY_UP);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <Input::Keycode code>
|
||||
struct Pressed
|
||||
{
|
||||
xkb_state *state;
|
||||
|
||||
Pressed(xkb_state *state) : state(state)
|
||||
{
|
||||
xkb_state_update_key(state, Xkb::keycode(code), XKB_KEY_DOWN);
|
||||
}
|
||||
|
||||
~Pressed()
|
||||
{
|
||||
xkb_state_update_key(state, Xkb::keycode(code), XKB_KEY_UP);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Args
|
||||
{
|
||||
struct Invalid_args { };
|
||||
|
||||
enum class Command { GENERATE, DUMP, INFO };
|
||||
|
||||
Command command;
|
||||
char const *layout;
|
||||
char const *variant;
|
||||
char const *locale;
|
||||
|
||||
char const *usage =
|
||||
"usage: xkb2ifcfg <command> <layout> <variant> <locale>\n"
|
||||
"\n"
|
||||
" Commands\n"
|
||||
"\n"
|
||||
" generate generate input_filter config\n"
|
||||
" dump dump raw XKB keymap\n"
|
||||
" info simple per-key information\n"
|
||||
"\n"
|
||||
" Example\n"
|
||||
"\n"
|
||||
" xkb2ifcfg generate us '' en_US.UTF-8\n"
|
||||
" xkb2ifcfg info de nodeadkeys de_DE.UTF-8\n";
|
||||
|
||||
Args(int argc, char **argv)
|
||||
try {
|
||||
if (argc != 5) throw Invalid_args();
|
||||
|
||||
if (!::strcmp("generate", argv[1])) command = Command::GENERATE;
|
||||
else if (!::strcmp("dump", argv[1])) command = Command::DUMP;
|
||||
else if (!::strcmp("info", argv[1])) command = Command::INFO;
|
||||
else throw Invalid_args();
|
||||
|
||||
layout = argv[2];
|
||||
variant = argv[3];
|
||||
locale = argv[4];
|
||||
|
||||
if (!strlen(layout) || !strlen(locale))
|
||||
throw Invalid_args();
|
||||
} catch (...) { ::fputs(usage, stderr); throw; }
|
||||
};
|
||||
|
||||
|
||||
class Main
|
||||
{
|
||||
private:
|
||||
|
||||
struct Map;
|
||||
struct Sequence;
|
||||
|
||||
Args args;
|
||||
|
||||
xkb_context *_context;
|
||||
xkb_rule_names _rmlvo;
|
||||
xkb_keymap *_keymap;
|
||||
xkb_state *_state;
|
||||
xkb_compose_table *_compose_table;
|
||||
xkb_compose_state *_compose_state;
|
||||
|
||||
std::set<Keysym> _keysyms;
|
||||
|
||||
/*
|
||||
* Numpad keys are remapped in input_filter if numlock=off, so we
|
||||
* always assume numlock=on to handle KP1 etc. correctly.
|
||||
*/
|
||||
Constructible<Locked<Input::KEY_NUMLOCK>> _numlock;
|
||||
|
||||
/* utilities */
|
||||
char const * _string(enum xkb_compose_status);
|
||||
char const * _string(enum xkb_compose_feed_result);
|
||||
|
||||
void _keycode_info(xkb_keycode_t);
|
||||
void _keycode_xml_non_printable(Xml_generator &, xkb_keycode_t);
|
||||
void _keycode_xml_control(Xml_generator &, xkb_keycode_t);
|
||||
void _keycode_xml_printable(Xml_generator &, xkb_keycode_t);
|
||||
void _keycode_xml_printable_shift(Xml_generator &, xkb_keycode_t);
|
||||
void _keycode_xml_printable_altgr(Xml_generator &, xkb_keycode_t);
|
||||
void _keycode_xml_printable_capslock(Xml_generator &, xkb_keycode_t);
|
||||
void _keycode_xml_printable_shift_altgr(Xml_generator &, xkb_keycode_t);
|
||||
void _keycode_xml_printable_shift_capslock(Xml_generator &, xkb_keycode_t);
|
||||
void _keycode_xml_printable_altgr_capslock(Xml_generator &, xkb_keycode_t);
|
||||
void _keycode_xml_printable_shift_altgr_capslock(Xml_generator &, xkb_keycode_t);
|
||||
|
||||
int _generate();
|
||||
int _dump();
|
||||
int _info();
|
||||
|
||||
public:
|
||||
|
||||
Main(int argc, char **argv);
|
||||
~Main();
|
||||
|
||||
xkb_keymap * keymap() { return _keymap; }
|
||||
|
||||
int exec();
|
||||
};
|
||||
|
||||
|
||||
struct Main::Map
|
||||
{
|
||||
Main &main;
|
||||
Xml_generator &xml;
|
||||
|
||||
enum class Mod : unsigned {
|
||||
NONE = 0,
|
||||
SHIFT = 0b0001, /* mod1 */
|
||||
CONTROL = 0b0010, /* mod2 */
|
||||
ALTGR = 0b0100, /* mod3 */
|
||||
CAPSLOCK = 0b1000, /* mod4 */
|
||||
SHIFT_ALTGR = SHIFT | ALTGR,
|
||||
SHIFT_CAPSLOCK = SHIFT | CAPSLOCK,
|
||||
ALTGR_CAPSLOCK = ALTGR | CAPSLOCK,
|
||||
SHIFT_ALTGR_CAPSLOCK = SHIFT | ALTGR | CAPSLOCK,
|
||||
} mod;
|
||||
|
||||
static char const * _string(Mod mod)
|
||||
{
|
||||
switch (mod) {
|
||||
case Map::Mod::NONE: return "no modifier";
|
||||
case Map::Mod::SHIFT: return "SHIFT";
|
||||
case Map::Mod::CONTROL: return "CONTROL";
|
||||
case Map::Mod::ALTGR: return "ALTGR";
|
||||
case Map::Mod::CAPSLOCK: return "CAPSLOCK";
|
||||
case Map::Mod::SHIFT_ALTGR: return "SHIFT-ALTGR";
|
||||
case Map::Mod::SHIFT_CAPSLOCK: return "SHIFT-CAPSLOCK";
|
||||
case Map::Mod::ALTGR_CAPSLOCK: return "ALTGR-CAPSLOCK";
|
||||
case Map::Mod::SHIFT_ALTGR_CAPSLOCK: return "SHIFT-ALTGR-CAPSLOCK";
|
||||
}
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
static void _non_printable(xkb_keymap *, xkb_keycode_t keycode, void *data)
|
||||
{
|
||||
Map &m = *reinterpret_cast<Map *>(data);
|
||||
|
||||
if (m.mod != Map::Mod::NONE) {
|
||||
::fprintf(stderr, "%s: mod=%u not supported\n",
|
||||
__PRETTY_FUNCTION__, unsigned(m.mod));
|
||||
return;
|
||||
}
|
||||
|
||||
m.main._keycode_xml_non_printable(m.xml, keycode);
|
||||
}
|
||||
|
||||
static void _control(xkb_keymap *, xkb_keycode_t keycode, void *data)
|
||||
{
|
||||
Map &m = *reinterpret_cast<Map *>(data);
|
||||
|
||||
if (m.mod != Map::Mod::CONTROL) {
|
||||
::fprintf(stderr, "%s: mod=%u not supported\n",
|
||||
__PRETTY_FUNCTION__, unsigned(m.mod));
|
||||
return;
|
||||
}
|
||||
|
||||
m.main._keycode_xml_control(m.xml, keycode);
|
||||
}
|
||||
|
||||
static void _printable(xkb_keymap *, xkb_keycode_t keycode, void *data)
|
||||
{
|
||||
Map &m = *reinterpret_cast<Map *>(data);
|
||||
|
||||
switch (m.mod) {
|
||||
case Map::Mod::NONE:
|
||||
m.main._keycode_xml_printable(m.xml, keycode); break;
|
||||
case Map::Mod::SHIFT:
|
||||
m.main._keycode_xml_printable_shift(m.xml, keycode); break;
|
||||
case Map::Mod::CONTROL:
|
||||
/* not printable */ break;
|
||||
case Map::Mod::ALTGR:
|
||||
m.main._keycode_xml_printable_altgr(m.xml, keycode); break;
|
||||
case Map::Mod::CAPSLOCK:
|
||||
m.main._keycode_xml_printable_capslock(m.xml, keycode); break;
|
||||
case Map::Mod::SHIFT_ALTGR:
|
||||
m.main._keycode_xml_printable_shift_altgr(m.xml, keycode); break;
|
||||
case Map::Mod::SHIFT_CAPSLOCK:
|
||||
m.main._keycode_xml_printable_shift_capslock(m.xml, keycode); break;
|
||||
case Map::Mod::ALTGR_CAPSLOCK:
|
||||
m.main._keycode_xml_printable_altgr_capslock(m.xml, keycode); break;
|
||||
case Map::Mod::SHIFT_ALTGR_CAPSLOCK:
|
||||
m.main._keycode_xml_printable_shift_altgr_capslock(m.xml, keycode); break;
|
||||
}
|
||||
}
|
||||
|
||||
Map(Main &main, Xml_generator &xml, Mod mod)
|
||||
:
|
||||
main(main), xml(xml), mod(mod)
|
||||
{
|
||||
if (mod == Mod::NONE) {
|
||||
/* generate basic character map */
|
||||
xml.node("map", [&] ()
|
||||
{
|
||||
append_comment(xml, "\n\t\t", "printable", "");
|
||||
xkb_keymap_key_for_each(main.keymap(), _printable, this);
|
||||
|
||||
append_comment(xml, "\n\n\t\t", "non-printable", "");
|
||||
xkb_keymap_key_for_each(main.keymap(), _non_printable, this);
|
||||
|
||||
/* FIXME xml.append() as last operation breaks indentation */
|
||||
xml.node("dummy", [] () {});
|
||||
});
|
||||
|
||||
} else if (mod == Mod::CONTROL) {
|
||||
/* generate control character map */
|
||||
append_comment(xml, "\n\n\t", _string(mod), "");
|
||||
xml.node("map", [&] ()
|
||||
{
|
||||
xml.attribute("mod2", true);
|
||||
|
||||
xkb_keymap_key_for_each(main.keymap(), _control, this);
|
||||
|
||||
/* FIXME xml.append() as last operation breaks indentation */
|
||||
xml.node("dummy", [] () {});
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
/* generate characters depending on modifier state */
|
||||
append_comment(xml, "\n\n\t", _string(mod), "");
|
||||
xml.node("map", [&] ()
|
||||
{
|
||||
xml.attribute("mod1", (bool)(unsigned(mod) & unsigned(Mod::SHIFT)));
|
||||
xml.attribute("mod2", false);
|
||||
xml.attribute("mod3", (bool)(unsigned(mod) & unsigned(Mod::ALTGR)));
|
||||
xml.attribute("mod4", (bool)(unsigned(mod) & unsigned(Mod::CAPSLOCK)));
|
||||
|
||||
xkb_keymap_key_for_each(main.keymap(), _printable, this);
|
||||
|
||||
/* FIXME xml.append() as last operation breaks indentation */
|
||||
xml.node("dummy", [] () {});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Main::Sequence
|
||||
{
|
||||
struct Guard
|
||||
{
|
||||
std::vector<Keysym> &seq;
|
||||
|
||||
Guard(std::vector<Keysym> &seq, Keysym keysym) : seq(seq) { seq.push_back(keysym); }
|
||||
~Guard() { seq.pop_back(); }
|
||||
};
|
||||
|
||||
Main &_main;
|
||||
Xml_generator &_xml;
|
||||
|
||||
xkb_compose_state *_state { xkb_compose_state_ref(_main._compose_state) };
|
||||
|
||||
void _generate(std::vector<Keysym> &seq, Keysym keysym)
|
||||
{
|
||||
Guard g(seq, keysym);
|
||||
|
||||
if (seq.size() > 4) {
|
||||
::fprintf(stderr, "dead-key / compose sequence too long (max=4)\n");
|
||||
return;
|
||||
}
|
||||
|
||||
xkb_compose_state_reset(_state);
|
||||
for (Keysym k : seq) xkb_compose_state_feed(_state, k.keysym);
|
||||
|
||||
switch (xkb_compose_state_get_status(_state)) {
|
||||
case XKB_COMPOSE_COMPOSED: {
|
||||
xkb_keysym_t result = xkb_compose_state_get_one_sym(_state);
|
||||
unsigned utf32 = xkb_keysym_to_utf32(result);
|
||||
|
||||
if (utf32 == 0) {
|
||||
::fprintf(stderr, "skipping sequence");
|
||||
for (Keysym k : seq) fprintf(stderr, " U+%05x", k.utf32);
|
||||
::fprintf(stderr, " generating U+%04x\n", utf32);
|
||||
break;
|
||||
}
|
||||
|
||||
_xml.node("sequence", [&] ()
|
||||
{
|
||||
try {
|
||||
_xml.attribute("first", Formatted("0x%04x", seq.at(0).utf32).string());
|
||||
_xml.attribute("second", Formatted("0x%04x", seq.at(1).utf32).string());
|
||||
_xml.attribute("third", Formatted("0x%04x", seq.at(2).utf32).string());
|
||||
_xml.attribute("fourth", Formatted("0x%04x", seq.at(3).utf32).string());
|
||||
} catch (std::out_of_range) { }
|
||||
|
||||
_xml.attribute("code", Formatted("0x%04x", utf32).string());
|
||||
});
|
||||
|
||||
char comment[32];
|
||||
xkb_keysym_to_utf8(result, comment, sizeof(comment));
|
||||
append_comment(_xml, "\t", comment, "");
|
||||
} break;
|
||||
|
||||
case XKB_COMPOSE_COMPOSING:
|
||||
for (Keysym k : _main._keysyms) {
|
||||
_generate(seq, k);
|
||||
}
|
||||
break;
|
||||
|
||||
case XKB_COMPOSE_CANCELLED:
|
||||
case XKB_COMPOSE_NOTHING:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Sequence(Main &main, Xml_generator &xml)
|
||||
:
|
||||
_main(main), _xml(xml)
|
||||
{
|
||||
append_comment(_xml, "\n\n\t", "dead-key / compose sequences", "");
|
||||
for (Keysym k : _main._keysyms) {
|
||||
/* first must be a dead/composing keysym */
|
||||
if (!k.composing) continue;
|
||||
|
||||
std::vector<Keysym> seq;
|
||||
|
||||
_generate(seq, k);
|
||||
}
|
||||
|
||||
/* FIXME xml.append() as last operation breaks indentation */
|
||||
xml.node("dummy", [] () {});
|
||||
}
|
||||
|
||||
~Sequence() { xkb_compose_state_unref(_state); }
|
||||
};
|
||||
|
||||
|
||||
char const * Main::_string(enum xkb_compose_status status)
|
||||
{
|
||||
switch (status) {
|
||||
case XKB_COMPOSE_NOTHING: return "XKB_COMPOSE_NOTHING";
|
||||
case XKB_COMPOSE_COMPOSING: return "XKB_COMPOSE_COMPOSING";
|
||||
case XKB_COMPOSE_COMPOSED: return "XKB_COMPOSE_COMPOSED";
|
||||
case XKB_COMPOSE_CANCELLED: return "XKB_COMPOSE_CANCELLED";
|
||||
}
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
|
||||
char const * Main::_string(enum xkb_compose_feed_result result)
|
||||
{
|
||||
switch (result) {
|
||||
case XKB_COMPOSE_FEED_IGNORED: return "XKB_COMPOSE_FEED_IGNORED";
|
||||
case XKB_COMPOSE_FEED_ACCEPTED: return "XKB_COMPOSE_FEED_ACCEPTED";
|
||||
}
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
|
||||
void Main::_keycode_info(xkb_keycode_t keycode)
|
||||
{
|
||||
for (Xkb::Mapping &m : Xkb::printable) {
|
||||
if (m.xkb != keycode) continue;
|
||||
|
||||
::printf("keycode %3u:", m.xkb);
|
||||
::printf(" %-8s", m.xkb_name);
|
||||
::printf(" %-16s", Input::key_name(m.code));
|
||||
|
||||
unsigned const num_levels = xkb_keymap_num_levels_for_key(_keymap, m.xkb, 0);
|
||||
::printf("\t%u levels { ", num_levels);
|
||||
|
||||
for (unsigned l = 0; l < num_levels; ++l) {
|
||||
::printf(" %u:", l);
|
||||
|
||||
xkb_keysym_t const *syms = nullptr;
|
||||
unsigned const num_syms = xkb_keymap_key_get_syms_by_level(_keymap, m.xkb, 0, l, &syms);
|
||||
|
||||
for (unsigned s = 0; s < num_syms; ++s) {
|
||||
char buffer[7] = { 0, };
|
||||
xkb_keysym_to_utf8(syms[s], buffer, sizeof(buffer));
|
||||
::printf(" %x %s", syms[s], keysym_composing(_compose_state, syms[s])
|
||||
? "COMPOSING!" : buffer);
|
||||
}
|
||||
}
|
||||
|
||||
::printf(" }");
|
||||
::printf("\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Main::_keycode_xml_non_printable(Xml_generator &xml, xkb_keycode_t keycode)
|
||||
{
|
||||
/* non-printable symbols with chargen entry (e.g., ENTER) */
|
||||
for (Xkb::Mapping &m : Xkb::non_printable) {
|
||||
if (m.xkb != keycode) continue;
|
||||
|
||||
xml.node("key", [&] ()
|
||||
{
|
||||
xml.attribute("name", Input::key_name(m.code));
|
||||
xml.attribute("ascii", m.ascii);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Main::_keycode_xml_control(Xml_generator &xml, xkb_keycode_t keycode)
|
||||
{
|
||||
/* chargen entry for control characters (e.g., CTRL-J) */
|
||||
static char const *desc[] {
|
||||
"SOH (start of heading) ",
|
||||
"STX (start of text) ",
|
||||
"ETX (end of text) ",
|
||||
"EOT (end of transmission) ",
|
||||
"ENQ (enquiry) ",
|
||||
"ACK (acknowledge) ",
|
||||
"BEL '\\a' (bell) ",
|
||||
"BS '\\b' (backspace) ",
|
||||
"HT '\\t' (horizontal tab) ",
|
||||
"LF '\\n' (new line) ",
|
||||
"VT '\\v' (vertical tab) ",
|
||||
"FF '\\f' (form feed) ",
|
||||
"CR '\\r' (carriage ret) ",
|
||||
"SO (shift out) ",
|
||||
"SI (shift in) ",
|
||||
"DLE (data link escape) ",
|
||||
"DC1 (device control 1) ",
|
||||
"DC2 (device control 2) ",
|
||||
"DC3 (device control 3) ",
|
||||
"DC4 (device control 4) ",
|
||||
"NAK (negative ack.) ",
|
||||
"SYN (synchronous idle) ",
|
||||
"ETB (end of trans. blk) ",
|
||||
"CAN (cancel) ",
|
||||
"EM (end of medium) ",
|
||||
"SUB (substitute) ",
|
||||
"ESC (escape) ",
|
||||
"FS (file separator) ",
|
||||
"GS (group separator) ",
|
||||
"RS (record separator) ",
|
||||
"US (unit separator) ",
|
||||
};
|
||||
Pressed<Input::KEY_LEFTCTRL> control(_state);
|
||||
for (Xkb::Mapping &m : Xkb::printable) {
|
||||
if (m.xkb != keycode) continue;
|
||||
|
||||
xkb_keysym_t const keysym = xkb_state_key_get_one_sym(_state, keycode);
|
||||
if (keysym == XKB_KEY_NoSymbol) return;
|
||||
|
||||
unsigned const utf32 = xkb_state_key_get_utf32(_state, m.xkb);
|
||||
if (!utf32 || utf32 > 0x1f) return;
|
||||
|
||||
char keysym_str[32];
|
||||
xkb_keysym_get_name(keysym, keysym_str, sizeof(keysym_str));
|
||||
|
||||
xml.node("key", [&] ()
|
||||
{
|
||||
xml.attribute("name", Input::key_name(m.code));
|
||||
xml.attribute("code", Formatted("0x%04x", utf32).string());
|
||||
});
|
||||
append_comment(xml, "\t",
|
||||
Formatted("%s CTRL-%s", desc[utf32-1], keysym_str).string(),
|
||||
"");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Main::_keycode_xml_printable(Xml_generator &xml, xkb_keycode_t keycode)
|
||||
{
|
||||
for (Xkb::Mapping &m : Xkb::printable) {
|
||||
if (m.xkb != keycode) continue;
|
||||
|
||||
Key_info key_info(_state, _compose_state, m.code);
|
||||
if (!key_info.valid()) break;
|
||||
|
||||
xml.node("key", [&] ()
|
||||
{
|
||||
xml.attribute("name", Input::key_name(m.code));
|
||||
key_info.attributes(xml);
|
||||
});
|
||||
key_info.comment(xml);
|
||||
|
||||
Keysym keysym { key_info.composing(), key_info.keysym(), key_info.utf32() };
|
||||
_keysyms.insert(keysym);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Main::_keycode_xml_printable_shift(Xml_generator &xml, xkb_keycode_t keycode)
|
||||
{
|
||||
Pressed<Input::KEY_LEFTSHIFT> shift(_state);
|
||||
_keycode_xml_printable(xml, keycode);
|
||||
}
|
||||
|
||||
|
||||
void Main::_keycode_xml_printable_altgr(Xml_generator &xml, xkb_keycode_t keycode)
|
||||
{
|
||||
Pressed<Input::KEY_RIGHTALT> altgr(_state);
|
||||
_keycode_xml_printable(xml, keycode);
|
||||
}
|
||||
|
||||
|
||||
void Main::_keycode_xml_printable_capslock(Xml_generator &xml, xkb_keycode_t keycode)
|
||||
{
|
||||
Locked<Input::KEY_CAPSLOCK> capslock(_state);
|
||||
_keycode_xml_printable(xml, keycode);
|
||||
}
|
||||
|
||||
|
||||
void Main::_keycode_xml_printable_shift_altgr(Xml_generator &xml, xkb_keycode_t keycode)
|
||||
{
|
||||
Pressed<Input::KEY_LEFTSHIFT> shift(_state);
|
||||
Pressed<Input::KEY_RIGHTALT> altgr(_state);
|
||||
_keycode_xml_printable(xml, keycode);
|
||||
}
|
||||
|
||||
|
||||
void Main::_keycode_xml_printable_shift_capslock(Xml_generator &xml, xkb_keycode_t keycode)
|
||||
{
|
||||
Locked<Input::KEY_CAPSLOCK> capslock(_state);
|
||||
Pressed<Input::KEY_LEFTSHIFT> shift(_state);
|
||||
_keycode_xml_printable(xml, keycode);
|
||||
}
|
||||
|
||||
|
||||
void Main::_keycode_xml_printable_altgr_capslock(Xml_generator &xml, xkb_keycode_t keycode)
|
||||
{
|
||||
Locked<Input::KEY_CAPSLOCK> capslock(_state);
|
||||
Pressed<Input::KEY_RIGHTALT> altgr(_state);
|
||||
_keycode_xml_printable(xml, keycode);
|
||||
}
|
||||
|
||||
|
||||
void Main::_keycode_xml_printable_shift_altgr_capslock(Xml_generator &xml, xkb_keycode_t keycode)
|
||||
{
|
||||
Locked<Input::KEY_CAPSLOCK> capslock(_state);
|
||||
Pressed<Input::KEY_LEFTSHIFT> shift(_state);
|
||||
Pressed<Input::KEY_RIGHTALT> altgr(_state);
|
||||
_keycode_xml_printable(xml, keycode);
|
||||
}
|
||||
|
||||
|
||||
int Main::_generate()
|
||||
{
|
||||
::printf("<!-- %s/%s/%s chargen configuration generated by xkb2ifcfg -->\n",
|
||||
args.layout, args.variant, args.locale);
|
||||
|
||||
Expanding_xml_buffer xml_buffer;
|
||||
|
||||
auto generate_xml = [&] (Xml_generator &xml)
|
||||
{
|
||||
{ Map map { *this, xml, Map::Mod::NONE }; }
|
||||
{ Map map { *this, xml, Map::Mod::SHIFT }; }
|
||||
{ Map map { *this, xml, Map::Mod::CONTROL }; }
|
||||
{ Map map { *this, xml, Map::Mod::ALTGR }; }
|
||||
{ Map map { *this, xml, Map::Mod::CAPSLOCK }; }
|
||||
{ Map map { *this, xml, Map::Mod::SHIFT_ALTGR }; }
|
||||
{ Map map { *this, xml, Map::Mod::SHIFT_CAPSLOCK }; }
|
||||
{ Map map { *this, xml, Map::Mod::ALTGR_CAPSLOCK }; }
|
||||
{ Map map { *this, xml, Map::Mod::SHIFT_ALTGR_CAPSLOCK }; }
|
||||
|
||||
{ Sequence sequence { *this, xml }; }
|
||||
};
|
||||
|
||||
xml_buffer.generate("chargen", generate_xml);
|
||||
|
||||
::puts(xml_buffer.buffer());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int Main::_dump()
|
||||
{
|
||||
::printf("Dump of XKB keymap for %s/%s/%s by xkb2ifcfg\n",
|
||||
args.layout, args.variant, args.locale);
|
||||
::puts(xkb_keymap_get_as_string(_keymap, XKB_KEYMAP_FORMAT_TEXT_V1));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int Main::_info()
|
||||
{
|
||||
::printf("Simple per-key info for %s/%s/%s by xkb2ifcfg\n",
|
||||
args.layout, args.variant, args.locale);
|
||||
|
||||
auto lambda = [] (xkb_keymap *, xkb_keycode_t keycode, void *data)
|
||||
{
|
||||
reinterpret_cast<Main *>(data)->_keycode_info(keycode);
|
||||
};
|
||||
|
||||
xkb_keymap_key_for_each(_keymap, lambda, this);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int Main::exec()
|
||||
{
|
||||
switch (args.command) {
|
||||
case Args::Command::GENERATE: return _generate();
|
||||
case Args::Command::DUMP: return _dump();
|
||||
case Args::Command::INFO: return _info();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
Main::Main(int argc, char **argv) : args(argc, argv)
|
||||
{
|
||||
/* TODO error handling */
|
||||
_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
_rmlvo = { "evdev", "pc105", args.layout, args.variant, "" };
|
||||
_keymap = xkb_keymap_new_from_names(_context, &_rmlvo, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
_state = xkb_state_new(_keymap);
|
||||
_compose_table = xkb_compose_table_new_from_locale(_context, args.locale, XKB_COMPOSE_COMPILE_NO_FLAGS);
|
||||
_compose_state = xkb_compose_state_new(_compose_table, XKB_COMPOSE_STATE_NO_FLAGS);
|
||||
|
||||
_numlock.construct(_state);
|
||||
}
|
||||
|
||||
|
||||
Main::~Main()
|
||||
{
|
||||
_numlock.destruct();
|
||||
|
||||
xkb_compose_state_unref(_compose_state);
|
||||
xkb_compose_table_unref(_compose_table);
|
||||
xkb_state_unref(_state);
|
||||
xkb_keymap_unref(_keymap);
|
||||
xkb_context_unref(_context);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
try {
|
||||
static Main m(argc, argv);
|
||||
|
||||
return m.exec();
|
||||
} catch (...) { return -1; }
|
||||
}
|
48
tool/xkb2ifcfg/util.h
Normal file
48
tool/xkb2ifcfg/util.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* \brief Libxkbcommon-based keyboard-layout generator
|
||||
* \author Christian Helmuth
|
||||
* \date 2019-08-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _UTIL_H_
|
||||
#define _UTIL_H_
|
||||
|
||||
/* Linux includes */
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
|
||||
struct Formatted
|
||||
{
|
||||
char *_string;
|
||||
|
||||
Formatted(char const *format, va_list list)
|
||||
{
|
||||
::vasprintf(&_string, format, list);
|
||||
}
|
||||
|
||||
Formatted(char const *format, ...)
|
||||
{
|
||||
va_list list;
|
||||
va_start(list, format);
|
||||
::vasprintf(&_string, format, list);
|
||||
va_end(list);
|
||||
}
|
||||
|
||||
~Formatted()
|
||||
{
|
||||
::free(_string);
|
||||
}
|
||||
|
||||
char const * string() const { return _string; }
|
||||
};
|
||||
|
||||
|
||||
#endif /* _UTIL_H_ */
|
167
tool/xkb2ifcfg/xkb_mapping.h
Normal file
167
tool/xkb2ifcfg/xkb_mapping.h
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* \brief Libxkbcommon-based keyboard-layout generator
|
||||
* \author Christian Helmuth
|
||||
* \date 2019-07-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _XKB_MAPPING_H_
|
||||
#define _XKB_MAPPING_H_
|
||||
|
||||
/* Linux includes */
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <input/keycodes.h>
|
||||
|
||||
|
||||
namespace Xkb {
|
||||
|
||||
/*
|
||||
* It's a documented fact that 'xkb keycode == evdev keycode + 8'
|
||||
*/
|
||||
inline xkb_keycode_t keycode(Input::Keycode code)
|
||||
{
|
||||
return xkb_keycode_t(unsigned(code) + 8);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lookup table for keys eventually generating characters
|
||||
*/
|
||||
struct Mapping
|
||||
{
|
||||
xkb_keycode_t xkb;
|
||||
char const xkb_name[7];
|
||||
Input::Keycode code;
|
||||
char const ascii { 0 }; /* predefined non-printable */
|
||||
};
|
||||
|
||||
Mapping printable[] = {
|
||||
{ 10, "<AE01>", Input::KEY_1 },
|
||||
{ 11, "<AE02>", Input::KEY_2 },
|
||||
{ 12, "<AE03>", Input::KEY_3 },
|
||||
{ 13, "<AE04>", Input::KEY_4 },
|
||||
{ 14, "<AE05>", Input::KEY_5 },
|
||||
{ 15, "<AE06>", Input::KEY_6 },
|
||||
{ 16, "<AE07>", Input::KEY_7 },
|
||||
{ 17, "<AE08>", Input::KEY_8 },
|
||||
{ 18, "<AE09>", Input::KEY_9 },
|
||||
{ 19, "<AE10>", Input::KEY_0 },
|
||||
{ 20, "<AE11>", Input::KEY_MINUS },
|
||||
{ 21, "<AE12>", Input::KEY_EQUAL },
|
||||
|
||||
{ 24, "<AD01>", Input::KEY_Q },
|
||||
{ 25, "<AD02>", Input::KEY_W },
|
||||
{ 26, "<AD03>", Input::KEY_E },
|
||||
{ 27, "<AD04>", Input::KEY_R },
|
||||
{ 28, "<AD05>", Input::KEY_T },
|
||||
{ 29, "<AD06>", Input::KEY_Y },
|
||||
{ 30, "<AD07>", Input::KEY_U },
|
||||
{ 31, "<AD08>", Input::KEY_I },
|
||||
{ 32, "<AD09>", Input::KEY_O },
|
||||
{ 33, "<AD10>", Input::KEY_P },
|
||||
{ 34, "<AD11>", Input::KEY_LEFTBRACE },
|
||||
{ 35, "<AD12>", Input::KEY_RIGHTBRACE },
|
||||
|
||||
{ 38, "<AC01>", Input::KEY_A },
|
||||
{ 39, "<AC02>", Input::KEY_S },
|
||||
{ 40, "<AC03>", Input::KEY_D },
|
||||
{ 41, "<AC04>", Input::KEY_F },
|
||||
{ 42, "<AC05>", Input::KEY_G },
|
||||
{ 43, "<AC06>", Input::KEY_H },
|
||||
{ 44, "<AC07>", Input::KEY_J },
|
||||
{ 45, "<AC08>", Input::KEY_K },
|
||||
{ 46, "<AC09>", Input::KEY_L },
|
||||
{ 47, "<AC11>", Input::KEY_SEMICOLON },
|
||||
{ 48, "<AC12>", Input::KEY_APOSTROPHE },
|
||||
|
||||
{ 49, "<TLDE>", Input::KEY_GRAVE }, /* left of "1" <AE01> */
|
||||
{ 51, "<BKSL>", Input::KEY_BACKSLASH }, /* left of <RTRN> (pc105) / above <RTRN> (pc104) */
|
||||
|
||||
{ 52, "<AB01>", Input::KEY_Z },
|
||||
{ 53, "<AB02>", Input::KEY_X },
|
||||
{ 54, "<AB03>", Input::KEY_C },
|
||||
{ 55, "<AB04>", Input::KEY_V },
|
||||
{ 56, "<AB05>", Input::KEY_B },
|
||||
{ 57, "<AB06>", Input::KEY_N },
|
||||
{ 58, "<AB07>", Input::KEY_M },
|
||||
{ 59, "<AB08>", Input::KEY_COMMA },
|
||||
{ 60, "<AB09>", Input::KEY_DOT },
|
||||
{ 61, "<AB10>", Input::KEY_SLASH },
|
||||
|
||||
{ 65, "<SPCE>", Input::KEY_SPACE },
|
||||
{ 94, "<LSGT>", Input::KEY_102ND }, /* right of <LFSH> (pc105) */
|
||||
|
||||
{ 63, "<KPMU>", Input::KEY_KPASTERISK },
|
||||
{ 79, "<KP7>", Input::KEY_KP7 },
|
||||
{ 80, "<KP8>", Input::KEY_KP8 },
|
||||
{ 81, "<KP9>", Input::KEY_KP9 },
|
||||
{ 82, "<KPSU>", Input::KEY_KPMINUS },
|
||||
{ 83, "<KP4>", Input::KEY_KP4 },
|
||||
{ 84, "<KP5>", Input::KEY_KP5 },
|
||||
{ 85, "<KP6>", Input::KEY_KP6 },
|
||||
{ 86, "<KPAD>", Input::KEY_KPPLUS },
|
||||
{ 87, "<KP1>", Input::KEY_KP1 },
|
||||
{ 88, "<KP2>", Input::KEY_KP2 },
|
||||
{ 89, "<KP3>", Input::KEY_KP3 },
|
||||
{ 90, "<KP0>", Input::KEY_KP0 },
|
||||
{ 91, "<KPDL>", Input::KEY_KPDOT },
|
||||
{ 106, "<KPDV>", Input::KEY_KPSLASH },
|
||||
};
|
||||
|
||||
Mapping non_printable[] = {
|
||||
{ 9, "<ESC>", Input::KEY_ESC, 27 },
|
||||
{ 22, "<BKSP>", Input::KEY_BACKSPACE, 8 },
|
||||
{ 23, "<TAB>", Input::KEY_TAB, 9 },
|
||||
{ 36, "<RTRN>", Input::KEY_ENTER, 10 }, /* XXX we use newline not carriage return as X11 */
|
||||
{ 104, "<KPEN>", Input::KEY_KPENTER, 10 }, /* XXX we use newline not carriage return as X11 */
|
||||
{ 119, "<DELE>", Input::KEY_DELETE, 127 },
|
||||
};
|
||||
|
||||
struct Dead_keysym
|
||||
{
|
||||
xkb_keysym_t xkb;
|
||||
unsigned utf32;
|
||||
} dead_keysym[] = {
|
||||
{ XKB_KEY_dead_grave, 0x0300 },
|
||||
{ XKB_KEY_dead_acute, 0x0301 },
|
||||
{ XKB_KEY_dead_circumflex, 0x0302 },
|
||||
{ XKB_KEY_dead_tilde, 0x0303 }, /* aliases: dead_perispomeni */
|
||||
{ XKB_KEY_dead_macron, 0x0304 },
|
||||
{ XKB_KEY_dead_breve, 0x0306 },
|
||||
{ XKB_KEY_dead_abovedot, 0x0307 },
|
||||
{ XKB_KEY_dead_diaeresis, 0x0308 },
|
||||
{ XKB_KEY_dead_hook, 0x0309 },
|
||||
{ XKB_KEY_dead_abovering, 0x030A },
|
||||
{ XKB_KEY_dead_doubleacute, 0x030B },
|
||||
{ XKB_KEY_dead_caron, 0x030C },
|
||||
{ XKB_KEY_dead_doublegrave, 0x030F },
|
||||
{ XKB_KEY_dead_invertedbreve, 0x0311 },
|
||||
{ XKB_KEY_dead_abovecomma, 0x0313 }, /* aliases: dead_psili */
|
||||
{ XKB_KEY_dead_abovereversedcomma, 0x0314 }, /* aliases: dead_dasia */
|
||||
{ XKB_KEY_dead_horn, 0x031B },
|
||||
{ XKB_KEY_dead_belowdot, 0x0323 },
|
||||
{ XKB_KEY_dead_belowdiaeresis, 0x0324 },
|
||||
{ XKB_KEY_dead_belowring, 0x0325 },
|
||||
{ XKB_KEY_dead_belowcomma, 0x0326 },
|
||||
{ XKB_KEY_dead_cedilla, 0x0327 },
|
||||
{ XKB_KEY_dead_ogonek, 0x0328 },
|
||||
{ XKB_KEY_dead_belowcircumflex, 0x032d },
|
||||
{ XKB_KEY_dead_belowtilde, 0x0330 },
|
||||
{ XKB_KEY_dead_belowmacron, 0x0331 },
|
||||
{ XKB_KEY_dead_stroke, 0x0338 },
|
||||
{ XKB_KEY_dead_belowbreve, 0x032E },
|
||||
{ XKB_KEY_dead_iota, 0x0345 }, /* aliases: GREEK YPOGEGRAMMENI */
|
||||
// { XKB_KEY_dead_voiced_sound, 0x03 },
|
||||
// { XKB_KEY_dead_semivoiced_sound, 0x03 },
|
||||
// { XKB_KEY_dead_currency, 0x03 },
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _XKB_MAPPING_H_ */
|
Loading…
x
Reference in New Issue
Block a user