[modules/xkbgroup] Remove thirdparty xkb, use system xkb
Providing a custom-built xkb module obviously doesn't really work, so use the system one instead. see #177
This commit is contained in:
parent
855012f05d
commit
e0e59b863c
9 changed files with 1 additions and 10410 deletions
|
@ -13,7 +13,7 @@ import bumblebee.input
|
||||||
import bumblebee.output
|
import bumblebee.output
|
||||||
import bumblebee.engine
|
import bumblebee.engine
|
||||||
|
|
||||||
from thirdparty.xkbgroup import *
|
from xkbgroup import *
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
0
thirdparty/__init__.py
vendored
0
thirdparty/__init__.py
vendored
25
thirdparty/xkbgroup/PKG-INFO
vendored
25
thirdparty/xkbgroup/PKG-INFO
vendored
|
@ -1,25 +0,0 @@
|
||||||
Metadata-Version: 1.1
|
|
||||||
Name: xkbgroup
|
|
||||||
Version: 0.1.4.3
|
|
||||||
Summary: Query and change XKB layout state
|
|
||||||
Home-page: https://github.com/hcpl/xkbgroup
|
|
||||||
Author: Nguyen Duc My
|
|
||||||
Author-email: hcpl.prog@gmail.com
|
|
||||||
License: MIT
|
|
||||||
Description: UNKNOWN
|
|
||||||
Platform: UNKNOWN
|
|
||||||
Classifier: Development Status :: 3 - Alpha
|
|
||||||
Classifier: Environment :: X11 Applications
|
|
||||||
Classifier: Intended Audience :: Developers
|
|
||||||
Classifier: License :: OSI Approved :: MIT License
|
|
||||||
Classifier: Operating System :: POSIX
|
|
||||||
Classifier: Operating System :: POSIX :: Linux
|
|
||||||
Classifier: Programming Language :: Python
|
|
||||||
Classifier: Programming Language :: Python :: 3
|
|
||||||
Classifier: Programming Language :: Python :: 3.2
|
|
||||||
Classifier: Programming Language :: Python :: 3.3
|
|
||||||
Classifier: Programming Language :: Python :: 3.4
|
|
||||||
Classifier: Programming Language :: Python :: 3.5
|
|
||||||
Classifier: Programming Language :: Python :: 3.6
|
|
||||||
Classifier: Topic :: Desktop Environment :: Window Managers
|
|
||||||
Classifier: Topic :: Software Development :: Libraries
|
|
243
thirdparty/xkbgroup/README.rst
vendored
243
thirdparty/xkbgroup/README.rst
vendored
|
@ -1,243 +0,0 @@
|
||||||
========
|
|
||||||
xkbgroup
|
|
||||||
========
|
|
||||||
|
|
||||||
.. image:: https://img.shields.io/badge/python-3.2+-blue.svg
|
|
||||||
|
|
||||||
.. image:: https://img.shields.io/pypi/v/xkbgroup.svg
|
|
||||||
:target: https://pypi.python.org/pypi/xkbgroup
|
|
||||||
|
|
||||||
.. image:: https://img.shields.io/badge/license-MIT-blue.svg
|
|
||||||
:target: https://github.com/hcpl/xkbgroup/blob/master/LICENSE
|
|
||||||
|
|
||||||
Use this library to change the keyboard layout through XKB extension (subsystem)
|
|
||||||
of the X server system. Both library and command line script included.
|
|
||||||
|
|
||||||
|
|
||||||
.. contents:: **Table of Contents**
|
|
||||||
|
|
||||||
|
|
||||||
Dependencies
|
|
||||||
------------
|
|
||||||
|
|
||||||
* Python 3.2+
|
|
||||||
* ``libX11.so.6`` shared library which you must have by default if you use
|
|
||||||
X server
|
|
||||||
|
|
||||||
|
|
||||||
Installation
|
|
||||||
------------
|
|
||||||
|
|
||||||
From PyPI package xkbgroup__
|
|
||||||
++++++++++++++++++++++++++++
|
|
||||||
|
|
||||||
__ https://pypi.python.org/pypi/xkbgroup
|
|
||||||
|
|
||||||
.. code-block:: sh
|
|
||||||
|
|
||||||
pip install xkbgroup
|
|
||||||
|
|
||||||
|
|
||||||
Library usage
|
|
||||||
-------------
|
|
||||||
|
|
||||||
.. code-block:: sh
|
|
||||||
|
|
||||||
# Assume we have the following configuration
|
|
||||||
$ setxkbmap -layout us,ru,ua,fr
|
|
||||||
# Change layout once before calling python
|
|
||||||
$ python
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
>>> from xkbgroup import XKeyboard
|
|
||||||
>>> xkb = XKeyboard()
|
|
||||||
>>> xkb.groups_count
|
|
||||||
4
|
|
||||||
>>> xkb.group_num
|
|
||||||
1
|
|
||||||
>>> xkb.group_num = 2
|
|
||||||
>>> xkb.group_num
|
|
||||||
2
|
|
||||||
>>> xkb.group_num -= 2
|
|
||||||
>>> xkb.group_num
|
|
||||||
0
|
|
||||||
>>> xkb.groups_names
|
|
||||||
['English (US)', 'Russian', 'Ukrainian', 'French']
|
|
||||||
>>> xkb.group_name
|
|
||||||
'English (US)'
|
|
||||||
>>> xkb.group_name = 'Ukrainian'
|
|
||||||
>>> xkb.group_name
|
|
||||||
'Ukrainian'
|
|
||||||
>>> xkb.group_num
|
|
||||||
2
|
|
||||||
>>> xkb.groups_symbols
|
|
||||||
['us', 'ru', 'ua', 'fr']
|
|
||||||
>>> xkb.group_symbol
|
|
||||||
'ua'
|
|
||||||
>>> xkb.group_symbol = 'fr'
|
|
||||||
>>> xkb.group_symbol
|
|
||||||
'fr'
|
|
||||||
>>> xkb.groups_variants
|
|
||||||
['', '', '', '']
|
|
||||||
>>> xkb.group_variant
|
|
||||||
''
|
|
||||||
>>> xkb.group_num -= 3
|
|
||||||
>>> xkb.group_variant
|
|
||||||
''
|
|
||||||
>>> xkb.group_num
|
|
||||||
0
|
|
||||||
>>> xkb.group_data
|
|
||||||
GroupData(num=0, name='English (US)', symbol='us', variant='')
|
|
||||||
>>> xkb.groups_data
|
|
||||||
[GroupData(num=0, name='English (US)', symbol='us', variant=''), GroupData(num=1, name=
|
|
||||||
'Russian', symbol='ru', variant=''), GroupData(num=2, name='Ukrainian', symbol='ua', va
|
|
||||||
riant=''), GroupData(num=3, name='French', symbol='fr', variant='')]
|
|
||||||
>>> xkb.format('{num} => {symbol}')
|
|
||||||
'0 => us'
|
|
||||||
>>> xkb.group_num = 1
|
|
||||||
>>> xkb.format('{num} => {symbol}')
|
|
||||||
'1 => ru'
|
|
||||||
>>> xkb.group_num = 3
|
|
||||||
>>> xkb.format('{num}: {symbol} - {name} "{variant}"')
|
|
||||||
'3: fr - French ""'
|
|
||||||
>>> xkb.format('{count}')
|
|
||||||
'4'
|
|
||||||
>>> xkb.format('{names}')
|
|
||||||
"['English (US)', 'Russian', 'Ukrainian', 'French']"
|
|
||||||
>>> xkb.format('{names::}')
|
|
||||||
'English (US)RussianUkrainianFrench'
|
|
||||||
>>> xkb.format('{names:: - }')
|
|
||||||
'English (US) - Russian - Ukrainian - French'
|
|
||||||
>>> xkb.format('{symbols:: - }')
|
|
||||||
'us - ru - ua - fr'
|
|
||||||
>>> xkb.format('{symbols:s: - }')
|
|
||||||
'us - ru - ua - fr'
|
|
||||||
>>> xkb.format('{all_data}')
|
|
||||||
"[GroupData(num=0, name='English (US)', symbol='us', variant=''), GroupData(num=1, name
|
|
||||||
='Russian', symbol='ru', variant=''), GroupData(num=2, name='Ukrainian', symbol='ua', v
|
|
||||||
ariant=''), GroupData(num=3, name='French', symbol='fr', variant='')]"
|
|
||||||
>>> xkb.format('{all_data:{{num}}}')
|
|
||||||
"['0', '1', '2', '3']"
|
|
||||||
>>> xkb.format('{all_data:/* {{name}} */}')
|
|
||||||
"['/* English (US) */', '/* Russian */', '/* Ukrainian */', '/* French */']"
|
|
||||||
>>> xkb.format('{all_data:{{symbol}}:\n}')
|
|
||||||
'us\nru\nua\nfr'
|
|
||||||
>>> print(xkb.format('{all_data:{{symbol}}:\n}'))
|
|
||||||
us
|
|
||||||
ru
|
|
||||||
ua
|
|
||||||
fr
|
|
||||||
>>> print(xkb.format('{all_data:{{num}}\\: {{symbol}} - {{name}} - "{{variant}}":\n}'))
|
|
||||||
0: us - English (US) - ""
|
|
||||||
1: ru - Russian - ""
|
|
||||||
2: ua - Ukrainian - ""
|
|
||||||
3: fr - French - ""
|
|
||||||
>>>
|
|
||||||
|
|
||||||
|
|
||||||
Command line features mapping
|
|
||||||
-----------------------------
|
|
||||||
|
|
||||||
+----------+-------------------------------------+--------------------------------------+
|
|
||||||
| Category | Library | Command line |
|
|
||||||
+==========+=====================================+======================================+
|
|
||||||
| Get | ``xkb.group_num`` | ``xkbgroup get num`` |
|
|
||||||
| +-------------------------------------+--------------------------------------+
|
|
||||||
| | ``xkb.group_name`` | ``xkbgroup get name`` |
|
|
||||||
| +-------------------------------------+--------------------------------------+
|
|
||||||
| | ``xkb.group_symbol`` | ``xkbgroup get symbol`` |
|
|
||||||
| +-------------------------------------+--------------------------------------+
|
|
||||||
| | ``xkb.group_variant`` | ``xkbgroup get variant`` |
|
|
||||||
| +-------------------------------------+--------------------------------------+
|
|
||||||
| | ``xkb.group_data`` | ``xkbgroup get current_data`` |
|
|
||||||
| +-------------------------------------+--------------------------------------+
|
|
||||||
| | ``xkb.groups_count`` | ``xkbgroup get count`` |
|
|
||||||
| +-------------------------------------+--------------------------------------+
|
|
||||||
| | ``xkb.groups_names`` | ``xkbgroup get names`` |
|
|
||||||
| +-------------------------------------+--------------------------------------+
|
|
||||||
| | ``xkb.groups_symbols`` | ``xkbgroup get symbols`` |
|
|
||||||
| +-------------------------------------+--------------------------------------+
|
|
||||||
| | ``xkb.groups_variants`` | ``xkbgroup get variants`` |
|
|
||||||
| +-------------------------------------+--------------------------------------+
|
|
||||||
| | ``xkb.groups_data`` | ``xkbgroup get all_data`` |
|
|
||||||
+----------+-------------------------------------+--------------------------------------+
|
|
||||||
| Set | ``xkb.group_num = 2`` | ``xkbgroup set num 2`` |
|
|
||||||
| +-------------------------------------+--------------------------------------+
|
|
||||||
| | ``xkb.group_name = 'English (US)'`` | ``xkbgroup set name 'English (US)'`` |
|
|
||||||
| +-------------------------------------+--------------------------------------+
|
|
||||||
| | ``xkb.group_symbol = 'fr'`` | ``xkbgroup set symbol fr`` |
|
|
||||||
+----------+-------------------------------------+--------------------------------------+
|
|
||||||
| Format | ``xkb.format('{format_str}')`` | ``xkbgroup format '{format_str}'`` |
|
|
||||||
+----------+-------------------------------------+--------------------------------------+
|
|
||||||
|
|
||||||
|
|
||||||
Naming convention
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
Throughout the whole XKB subsystem the `so-called groups represent actual
|
|
||||||
keyboard layouts`__. This library follows the same convention and names of the
|
|
||||||
API methods start with ``group_`` or ``groups_``.
|
|
||||||
|
|
||||||
__ https://wiki.archlinux.org/index.php/X_KeyBoard_extension#Keycode_translation
|
|
||||||
|
|
||||||
|
|
||||||
Classes
|
|
||||||
-------
|
|
||||||
|
|
||||||
These all reside in ``xkbgroup/core.py``:
|
|
||||||
|
|
||||||
* ``XKeyboard`` — the main class:
|
|
||||||
|
|
||||||
- ``__init__(self, auto_open=True, non_symbols=None)``:
|
|
||||||
|
|
||||||
+ ``auto_open`` — if ``True`` then automatically call ``open_display()``
|
|
||||||
on initialization.
|
|
||||||
+ ``non_symbols`` — either iterable of string non-symbol names or None to
|
|
||||||
use the default set of non-symbol names.
|
|
||||||
- ``open_display()`` — establishes connection with X server and prepares
|
|
||||||
objects necessary to retrieve and send data.
|
|
||||||
- ``close_display()`` — closes connection with X server and cleans up
|
|
||||||
objects created on ``open_display()``.
|
|
||||||
- ``group_*`` — properties for accessing current group data:
|
|
||||||
|
|
||||||
+ ``group_num`` — get/set current group number
|
|
||||||
(e.g. ``0``, ``2``, ``3``).
|
|
||||||
+ ``group_name`` — get/set current group full name
|
|
||||||
(e.g. ``English (US)``, ``Russian``, ``French``).
|
|
||||||
+ ``group_symbol`` — get/set current group symbol
|
|
||||||
(e.g. ``us``, ``ru``, ``fr``).
|
|
||||||
+ ``group_variant`` — get (only) current group variant
|
|
||||||
(e.g. `` ``, ``dos``, ``latin9``).
|
|
||||||
+ ``group_data`` — get (only) all data about the current group.
|
|
||||||
In fact, assembles all previous ``group_*`` values.
|
|
||||||
- ``groups_*`` — properties for querying info about all groups set by
|
|
||||||
``setxkbmap``:
|
|
||||||
|
|
||||||
+ ``groups_count`` — get number of all groups.
|
|
||||||
+ ``groups_names`` — get names of all groups.
|
|
||||||
+ ``groups_symbols`` — get symbols of all groups.
|
|
||||||
+ ``groups_variants`` — get variants of all groups.
|
|
||||||
+ ``groups_data`` — get all data about all groups
|
|
||||||
by assembling all previous ``groups_*`` values.
|
|
||||||
|
|
||||||
- ``format()`` — obtain a formatted output, see `<docs/formatting.rst>`_
|
|
||||||
for details.
|
|
||||||
|
|
||||||
* ``X11Error`` — an exception class, raised for errors on X server issues.
|
|
||||||
|
|
||||||
|
|
||||||
Helper files
|
|
||||||
------------
|
|
||||||
|
|
||||||
There are also complementary files:
|
|
||||||
|
|
||||||
* ``generate_bindings.sh`` — a shell script which generates Python bindings
|
|
||||||
to X server structures, functions and ``#define`` definitions by:
|
|
||||||
|
|
||||||
- converting X11 C headers using ``h2xml`` and ``xml2py``;
|
|
||||||
- creating ``ctypes`` references to functions from ``libX11.so.6`` using
|
|
||||||
``xml2py``.
|
|
||||||
|
|
||||||
* ``xkbgroup/xkb.py`` — the output of the above script, usable for Xlib
|
|
||||||
development under Python.
|
|
14
thirdparty/xkbgroup/__init__.py
vendored
14
thirdparty/xkbgroup/__init__.py
vendored
|
@ -1,14 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
"""
|
|
||||||
xkbgroup
|
|
||||||
~~~~~~~~
|
|
||||||
|
|
||||||
Use this library to change the keyboard layout through XKB extension
|
|
||||||
(subsystem) of the X server system.
|
|
||||||
|
|
||||||
:copyright: (c) 2016 by Nguyen Duc My.
|
|
||||||
:license: MIT, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from .core import XKeyboard, X11Error
|
|
103
thirdparty/xkbgroup/__main__.py
vendored
103
thirdparty/xkbgroup/__main__.py
vendored
|
@ -1,103 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
import codecs
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from argparse import ArgumentParser
|
|
||||||
|
|
||||||
from .core import XKeyboard
|
|
||||||
from .version import print_version
|
|
||||||
|
|
||||||
|
|
||||||
GET_CHOICES = ["num", "name", "symbol", "variant", "current_data",
|
|
||||||
"count", "names", "symbols", "variants", "all_data"]
|
|
||||||
SET_CHOICES = ["num", "name", "symbol"]
|
|
||||||
|
|
||||||
|
|
||||||
DATA_DEFAULT_FORMAT = "{num}: {symbol} - {name} - \"{variant}\""
|
|
||||||
|
|
||||||
def format_group_data(group_data):
|
|
||||||
return DATA_DEFAULT_FORMAT.format(
|
|
||||||
num=group_data.num,
|
|
||||||
name=group_data.name,
|
|
||||||
symbol=group_data.symbol,
|
|
||||||
variant=group_data.variant)
|
|
||||||
|
|
||||||
def xkb_get(args, xkb):
|
|
||||||
attrmap = {
|
|
||||||
"num": "group_num",
|
|
||||||
"name": "group_name",
|
|
||||||
"symbol": "group_symbol",
|
|
||||||
"variant": "group_variant",
|
|
||||||
"current_data": "group_data",
|
|
||||||
"count": "groups_count",
|
|
||||||
"names": "groups_names",
|
|
||||||
"symbols": "groups_symbols",
|
|
||||||
"variants": "groups_variants",
|
|
||||||
"all_data": "groups_data",
|
|
||||||
}
|
|
||||||
|
|
||||||
value = getattr(xkb, attrmap[args.attribute])
|
|
||||||
if args.attribute == "all_data":
|
|
||||||
value = [format_group_data(data) for data in value]
|
|
||||||
elif args.attribute == "current_data":
|
|
||||||
value = format_group_data(value)
|
|
||||||
value = "\n".join(value) if isinstance(value, list) else value
|
|
||||||
|
|
||||||
print(value)
|
|
||||||
|
|
||||||
def xkb_set(args, xkb):
|
|
||||||
value = int(args.value) if args.attribute == "num" else args.value
|
|
||||||
attrmap = {
|
|
||||||
"num": "group_num",
|
|
||||||
"name": "group_name",
|
|
||||||
"symbol": "group_symbol",
|
|
||||||
}
|
|
||||||
|
|
||||||
setattr(xkb, attrmap[args.attribute], value)
|
|
||||||
|
|
||||||
def xkb_format(args, xkb):
|
|
||||||
unescaped_format_str = codecs.decode(args.format_string, "unicode_escape")
|
|
||||||
print(xkb.format(unescaped_format_str), end="")
|
|
||||||
|
|
||||||
|
|
||||||
def create_argument_parser():
|
|
||||||
parser = ArgumentParser()
|
|
||||||
|
|
||||||
parser.add_argument("-V", "--version", action="store_true")
|
|
||||||
subparsers = parser.add_subparsers(title="actions", dest="cmd")
|
|
||||||
|
|
||||||
parser_get = subparsers.add_parser("get")
|
|
||||||
parser_get.set_defaults(func=xkb_get)
|
|
||||||
parser_get.add_argument("attribute", choices=GET_CHOICES)
|
|
||||||
|
|
||||||
parser_set = subparsers.add_parser("set")
|
|
||||||
parser_set.set_defaults(func=xkb_set)
|
|
||||||
parser_set.add_argument("attribute", choices=SET_CHOICES)
|
|
||||||
parser_set.add_argument("value")
|
|
||||||
|
|
||||||
parser_format = subparsers.add_parser("format")
|
|
||||||
parser_format.set_defaults(func=xkb_format)
|
|
||||||
parser_format.add_argument("format_string")
|
|
||||||
|
|
||||||
return parser
|
|
||||||
|
|
||||||
def main():
|
|
||||||
xkb = XKeyboard()
|
|
||||||
progname = os.path.basename(sys.argv[0])
|
|
||||||
|
|
||||||
parser = create_argument_parser()
|
|
||||||
args = parser.parse_args()
|
|
||||||
if args.version:
|
|
||||||
print_version(progname)
|
|
||||||
elif args.cmd:
|
|
||||||
args.func(args, xkb)
|
|
||||||
else:
|
|
||||||
parser.print_usage(sys.stderr)
|
|
||||||
parser.exit(2, "{prog}: error: {message}\n".format(
|
|
||||||
prog=progname,
|
|
||||||
message="the following arguments are required: cmd"))
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
557
thirdparty/xkbgroup/core.py
vendored
557
thirdparty/xkbgroup/core.py
vendored
|
@ -1,557 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
"""
|
|
||||||
xkbgroup.core
|
|
||||||
~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
This module implements the XKeyboard API.
|
|
||||||
|
|
||||||
Classes:
|
|
||||||
* XKeyboard: the main class.
|
|
||||||
|
|
||||||
Exceptions:
|
|
||||||
* X11Error: raised for errors on X server issues.
|
|
||||||
|
|
||||||
:copyright: (c) 2016 by Nguyen Duc My.
|
|
||||||
:license: MIT, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from ctypes import *
|
|
||||||
try:
|
|
||||||
from collections import UserList
|
|
||||||
except ImportError:
|
|
||||||
from UserList import UserList
|
|
||||||
|
|
||||||
from collections import namedtuple
|
|
||||||
|
|
||||||
from .xkb import *
|
|
||||||
|
|
||||||
|
|
||||||
# Error-related utilities
|
|
||||||
|
|
||||||
OPEN_DISPLAY_ERRORS = {
|
|
||||||
XkbOD_BadLibraryVersion: "{libname} uses XKB version {used_major}.{used_minor}\n"
|
|
||||||
"Xlib supports incompatible version {found_major}.{found_minor}",
|
|
||||||
XkbOD_ConnectionRefused: "Cannot open display \"{display_name}\"",
|
|
||||||
XkbOD_BadServerVersion: "{libname} uses XKB version {used_major}.{used_minor}\n"
|
|
||||||
"Server \"{display_name}\" uses incompatible version "
|
|
||||||
"{found_major}.{found_minor}",
|
|
||||||
XkbOD_NonXkbServer: "XKB extension not present on \"{display_name}\"",
|
|
||||||
}
|
|
||||||
|
|
||||||
GET_CONTROLS_ERRORS = {
|
|
||||||
BadAlloc: "Unable to allocate storage",
|
|
||||||
BadImplementation: "Invalid reply from server",
|
|
||||||
BadMatch: "A compatible version of Xkb was not available in the server or "
|
|
||||||
"an argument has correct type and range, but is otherwise invalid",
|
|
||||||
}
|
|
||||||
|
|
||||||
GET_NAMES_ERRORS = {
|
|
||||||
BadAlloc: "Unable to allocate storage",
|
|
||||||
BadImplementation: "Invalid reply from server",
|
|
||||||
BadLength: "The length of a request is shorter or longer than that "
|
|
||||||
"required to minimally contain the arguments",
|
|
||||||
BadMatch: "A compatible version of Xkb was not available in the server or "
|
|
||||||
"an argument has correct type and range, but is otherwise invalid",
|
|
||||||
}
|
|
||||||
|
|
||||||
class X11Error(Exception):
|
|
||||||
"""Exception class, raised for errors on X server issues."""
|
|
||||||
|
|
||||||
def _ensure_type(obj, type):
|
|
||||||
if not isinstance(obj, type):
|
|
||||||
raise ValueError("Wrong value type, must be {}.".format(str(type)))
|
|
||||||
|
|
||||||
|
|
||||||
# Both Python 3.2+ compatible and more neat than assigning to __doc__
|
|
||||||
class GroupData(namedtuple("GroupData", ["num", "name", "symbol", "variant"])):
|
|
||||||
"""Contains all data about the specific group."""
|
|
||||||
|
|
||||||
def __format__(self, format_spec):
|
|
||||||
"""If format_spec is not empty, use it as a format string in
|
|
||||||
format_spec.format(...) with keyword arguments named corresponding to
|
|
||||||
fields. Otherwise just return str(self).
|
|
||||||
|
|
||||||
:param format_spec: format specifier
|
|
||||||
:rtype: str
|
|
||||||
"""
|
|
||||||
if len(format_spec) > 0:
|
|
||||||
return format_spec.format(
|
|
||||||
num=self.num,
|
|
||||||
name=self.name,
|
|
||||||
symbol=self.symbol,
|
|
||||||
variant=self.variant)
|
|
||||||
return str(self)
|
|
||||||
|
|
||||||
class XKeyboard:
|
|
||||||
"""The main class.
|
|
||||||
|
|
||||||
Usage examples:
|
|
||||||
|
|
||||||
# Assume we have the following configuration
|
|
||||||
$ setxkbmap -layout us,ru,ua,fr
|
|
||||||
$ python
|
|
||||||
>>> from xkbgroup import XKeyboard
|
|
||||||
>>> xkb = XKeyboard()
|
|
||||||
>>> xkb.group_num
|
|
||||||
1
|
|
||||||
>>> xkb.group_num = 2
|
|
||||||
>>> xkb.group_num
|
|
||||||
2
|
|
||||||
>>> xkb.group_num -= 2
|
|
||||||
>>> xkb.group_num
|
|
||||||
0
|
|
||||||
>>> xkb.group_name
|
|
||||||
English (US)
|
|
||||||
>>> xkb.group_name = 'Ukrainian'
|
|
||||||
>>> xkb.group_name
|
|
||||||
Ukrainian
|
|
||||||
>>> xkb.group_num
|
|
||||||
2
|
|
||||||
>>> xkb.group_symbol
|
|
||||||
ua
|
|
||||||
>>> xkb.group_symbol = 'fr'
|
|
||||||
>>> xkb.group_symbol
|
|
||||||
fr
|
|
||||||
>>> xkb.group_variant
|
|
||||||
''
|
|
||||||
>>> xkb.group_num -= 3
|
|
||||||
>>> xkb.group_variant
|
|
||||||
''
|
|
||||||
>>> xkb.group_num
|
|
||||||
0
|
|
||||||
>>>
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Fields with default values
|
|
||||||
|
|
||||||
non_symbols = {"pc", "inet", "group", "terminate"}
|
|
||||||
|
|
||||||
|
|
||||||
# Main methods
|
|
||||||
|
|
||||||
def __init__(self, auto_open=True, non_symbols=None):
|
|
||||||
"""
|
|
||||||
:param auto_open: if True automatically call open_display().
|
|
||||||
:param non_symbols: either iterable of string non-symbol names or
|
|
||||||
None to use the default set of non-symbol names.
|
|
||||||
"""
|
|
||||||
if non_symbols:
|
|
||||||
self.non_symbols = non_symbols
|
|
||||||
|
|
||||||
if auto_open:
|
|
||||||
self.open_display()
|
|
||||||
|
|
||||||
def open_display(self):
|
|
||||||
"""Establishes connection with X server and prepares objects
|
|
||||||
necessary to retrieve and send data.
|
|
||||||
"""
|
|
||||||
self.close_display() # Properly finish previous open_display()
|
|
||||||
|
|
||||||
XkbIgnoreExtension(False)
|
|
||||||
|
|
||||||
display_name = None
|
|
||||||
major = c_int(XkbMajorVersion)
|
|
||||||
minor = c_int(XkbMinorVersion)
|
|
||||||
reason = c_int()
|
|
||||||
|
|
||||||
self._display = XkbOpenDisplay(
|
|
||||||
display_name,
|
|
||||||
None, None, byref(major), byref(minor), byref(reason))
|
|
||||||
if not self._display:
|
|
||||||
if reason.value in OPEN_DISPLAY_ERRORS:
|
|
||||||
# Assume POSIX conformance
|
|
||||||
display_name = os.getenv("DISPLAY") or "default"
|
|
||||||
|
|
||||||
raise X11Error(OPEN_DISPLAY_ERRORS[reason.value].format(
|
|
||||||
libname="xkbgroup",
|
|
||||||
used_major=XkbMajorVersion,
|
|
||||||
used_minor=XkbMinorVersion,
|
|
||||||
found_major=major.value,
|
|
||||||
found_minor=minor.value,
|
|
||||||
display_name=display_name)
|
|
||||||
+ ".")
|
|
||||||
else:
|
|
||||||
raise X11Error("Unknown error {} from XkbOpenDisplay.".format(reason.value))
|
|
||||||
|
|
||||||
self._keyboard_description = XkbGetMap(self._display, 0, XkbUseCoreKbd)
|
|
||||||
if not self._keyboard_description:
|
|
||||||
self.close_display()
|
|
||||||
raise X11Error("Failed to get keyboard description.")
|
|
||||||
|
|
||||||
# Controls mask doesn't affect the availability of xkb->ctrls->num_groups anyway
|
|
||||||
# Just use a valid value, and xkb->ctrls->num_groups will be definitely set
|
|
||||||
status = XkbGetControls(self._display, XkbAllControlsMask, self._keyboard_description)
|
|
||||||
if status != Success:
|
|
||||||
self.close_display()
|
|
||||||
raise X11Error(GET_CONTROLS_ERRORS[status] + ".")
|
|
||||||
|
|
||||||
names_mask = XkbSymbolsNameMask | XkbGroupNamesMask
|
|
||||||
status = XkbGetNames(self._display, names_mask, self._keyboard_description)
|
|
||||||
if status != Success:
|
|
||||||
self.close_display()
|
|
||||||
raise X11Error(GET_NAMES_ERRORS[status] + ".")
|
|
||||||
|
|
||||||
def close_display(self):
|
|
||||||
"""Closes connection with X server and cleans up objects
|
|
||||||
created on open_display().
|
|
||||||
"""
|
|
||||||
if hasattr(self, "_keyboard_description") and self._keyboard_description:
|
|
||||||
names_mask = XkbSymbolsNameMask | XkbGroupNamesMask
|
|
||||||
XkbFreeNames(self._keyboard_description, names_mask, True)
|
|
||||||
XkbFreeControls(self._keyboard_description, XkbAllControlsMask, True)
|
|
||||||
XkbFreeClientMap(self._keyboard_description, 0, True)
|
|
||||||
del self._keyboard_description
|
|
||||||
|
|
||||||
if hasattr(self, "_display") and self._display:
|
|
||||||
XCloseDisplay(self._display)
|
|
||||||
del self._display
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
self.close_display()
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
self.open_display()
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __exit__(self, type, value, traceback):
|
|
||||||
self.close_display()
|
|
||||||
|
|
||||||
|
|
||||||
# Properties for all layouts
|
|
||||||
|
|
||||||
@property
|
|
||||||
def groups_data(self):
|
|
||||||
"""All data about all groups (get-only).
|
|
||||||
|
|
||||||
:getter: Returns all data about all groups
|
|
||||||
:type: list of GroupData
|
|
||||||
"""
|
|
||||||
return _ListProxy(GroupData(num, name, symbol, variant)
|
|
||||||
for (num, name, symbol, variant)
|
|
||||||
in zip(range(self.groups_count),
|
|
||||||
self.groups_names,
|
|
||||||
self.groups_symbols,
|
|
||||||
self.groups_variants))
|
|
||||||
|
|
||||||
@property
|
|
||||||
def groups_count(self):
|
|
||||||
"""Number of all groups (get-only).
|
|
||||||
|
|
||||||
:getter: Returns number of all groups
|
|
||||||
:type: int
|
|
||||||
"""
|
|
||||||
if self._keyboard_description.contents.ctrls is not None:
|
|
||||||
return self._keyboard_description.contents.ctrls.contents.num_groups
|
|
||||||
else:
|
|
||||||
groups_source = self._groups_source
|
|
||||||
|
|
||||||
groups_count = 0
|
|
||||||
while (groups_count < XkbNumKbdGroups and
|
|
||||||
groups_source[groups_count] != None_):
|
|
||||||
groups_count += 1
|
|
||||||
|
|
||||||
return groups_count
|
|
||||||
|
|
||||||
@property
|
|
||||||
def groups_names(self):
|
|
||||||
"""Names of all groups (get-only).
|
|
||||||
|
|
||||||
:getter: Returns names of all groups
|
|
||||||
:type: list of str
|
|
||||||
"""
|
|
||||||
return _ListProxy(self._get_group_name_by_num(i) for i in range(self.groups_count))
|
|
||||||
|
|
||||||
@property
|
|
||||||
def groups_symbols(self):
|
|
||||||
"""Symbols of all groups (get-only).
|
|
||||||
|
|
||||||
:getter: Returns symbols of all groups
|
|
||||||
:type: list of str
|
|
||||||
"""
|
|
||||||
return _ListProxy(symdata.symbol for symdata in self._symboldata_list)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def groups_variants(self):
|
|
||||||
"""Variants of all groups (get-only).
|
|
||||||
|
|
||||||
:getter: Returns variants of all groups
|
|
||||||
:type: list of str
|
|
||||||
"""
|
|
||||||
return _ListProxy(symdata.variant or "" for symdata in self._symboldata_list)
|
|
||||||
|
|
||||||
|
|
||||||
# Properties and methods for current layout
|
|
||||||
|
|
||||||
@property
|
|
||||||
def group_data(self):
|
|
||||||
"""All data about the current group (get-only).
|
|
||||||
|
|
||||||
:getter: Returns all data about the current group
|
|
||||||
:type: GroupData
|
|
||||||
"""
|
|
||||||
return GroupData(self.group_num,
|
|
||||||
self.group_name,
|
|
||||||
self.group_symbol,
|
|
||||||
self.group_variant)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def group_num(self):
|
|
||||||
"""Current group number.
|
|
||||||
|
|
||||||
:getter: Returns current group number
|
|
||||||
:setter: Sets current group number
|
|
||||||
:type: int
|
|
||||||
"""
|
|
||||||
xkb_state = XkbStateRec()
|
|
||||||
XkbGetState(self._display, XkbUseCoreKbd, byref(xkb_state))
|
|
||||||
return xkb_state.group
|
|
||||||
|
|
||||||
@group_num.setter
|
|
||||||
def group_num(self, value):
|
|
||||||
_ensure_type(value, int)
|
|
||||||
if XkbLockGroup(self._display, XkbUseCoreKbd, value):
|
|
||||||
XFlush(self._display)
|
|
||||||
else:
|
|
||||||
self.close_display()
|
|
||||||
raise X11Error("Failed to set group number.")
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def group_name(self):
|
|
||||||
"""Current group full name.
|
|
||||||
|
|
||||||
:getter: Returns current group name
|
|
||||||
:setter: Sets current group name
|
|
||||||
:type: str
|
|
||||||
"""
|
|
||||||
return self._get_group_name_by_num(self.group_num)
|
|
||||||
|
|
||||||
@group_name.setter
|
|
||||||
def group_name(self, value):
|
|
||||||
_ensure_type(value, str)
|
|
||||||
groups_names = self.groups_names
|
|
||||||
n_mapping = {groups_names[i]: i for i in range(len(groups_names))}
|
|
||||||
try:
|
|
||||||
self.group_num = n_mapping[value]
|
|
||||||
except KeyError as exc:
|
|
||||||
raise ValueError("Wrong group name.")
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def group_symbol(self):
|
|
||||||
"""Current group symbol.
|
|
||||||
|
|
||||||
:getter: Returns current group symbol
|
|
||||||
:setter: Sets current group symbol
|
|
||||||
:type: str
|
|
||||||
"""
|
|
||||||
s_mapping = {symdata.index: symdata.symbol for symdata in self._symboldata_list}
|
|
||||||
return s_mapping[self.group_num]
|
|
||||||
|
|
||||||
@group_symbol.setter
|
|
||||||
def group_symbol(self, value):
|
|
||||||
_ensure_type(value, str)
|
|
||||||
s_mapping = {symdata.symbol: symdata.index for symdata in self._symboldata_list}
|
|
||||||
try:
|
|
||||||
self.group_num = s_mapping[value]
|
|
||||||
except KeyError as exc:
|
|
||||||
raise ValueError("Wrong group symbol.")
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def group_variant(self):
|
|
||||||
"""Current group variant (get-only).
|
|
||||||
|
|
||||||
:getter: Returns current group variant
|
|
||||||
:type: str
|
|
||||||
"""
|
|
||||||
v_mapping = {symdata.index: symdata.variant for symdata in self._symboldata_list}
|
|
||||||
return v_mapping[self.group_num] or ""
|
|
||||||
|
|
||||||
# Current group variant is a get-only value because variants are associated
|
|
||||||
# with symbols in /usr/share/X11/xkb/rules/evdev.lst and specified at
|
|
||||||
# setxkbmap call time
|
|
||||||
|
|
||||||
|
|
||||||
# Formatting method (for the great goodness!)
|
|
||||||
|
|
||||||
def format(self, format_str):
|
|
||||||
"""Returns a formatted version of format_str.
|
|
||||||
The only named replacement fields supported by this method and
|
|
||||||
their corresponding API calls are:
|
|
||||||
|
|
||||||
* {num} group_num
|
|
||||||
* {name} group_name
|
|
||||||
* {symbol} group_symbol
|
|
||||||
* {variant} group_variant
|
|
||||||
* {current_data} group_data
|
|
||||||
* {nums} groups_nums
|
|
||||||
* {names} groups_names
|
|
||||||
* {symbols} groups_symbols
|
|
||||||
* {variants} groups_variants
|
|
||||||
* {all_data} groups_data
|
|
||||||
|
|
||||||
Passing other replacement fields will result in raising exceptions.
|
|
||||||
|
|
||||||
:param format_str: a new style format string
|
|
||||||
:rtype: str
|
|
||||||
"""
|
|
||||||
return format_str.format(**{
|
|
||||||
"num": self.group_num,
|
|
||||||
"name": self.group_name,
|
|
||||||
"symbol": self.group_symbol,
|
|
||||||
"variant": self.group_variant,
|
|
||||||
"current_data": self.group_data,
|
|
||||||
"count": self.groups_count,
|
|
||||||
"names": self.groups_names,
|
|
||||||
"symbols": self.groups_symbols,
|
|
||||||
"variants": self.groups_variants,
|
|
||||||
"all_data": self.groups_data})
|
|
||||||
|
|
||||||
def __format__(self, format_spec):
|
|
||||||
"""Handle format(xkb, format_spec) as xkb.format(format_spec) if
|
|
||||||
format_spec is not empty. Otherwise just return str(self).
|
|
||||||
|
|
||||||
:param format_spec: format specifier
|
|
||||||
:rtype: str
|
|
||||||
"""
|
|
||||||
if len(format_spec) > 0:
|
|
||||||
return self.format(format_spec)
|
|
||||||
return str(self)
|
|
||||||
|
|
||||||
|
|
||||||
# Private properties and methods
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _groups_source(self):
|
|
||||||
return self._keyboard_description.contents.names.contents.groups
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _symbols_source(self):
|
|
||||||
return self._keyboard_description.contents.names.contents.symbols
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _symboldata_list(self):
|
|
||||||
symbol_str_atom = self._symbols_source
|
|
||||||
if symbol_str_atom != None_:
|
|
||||||
b_symbol_str = XGetAtomName(self._display, symbol_str_atom)
|
|
||||||
return _parse_symbols(b_symbol_str.decode(), self.non_symbols)
|
|
||||||
else:
|
|
||||||
raise X11Error("Failed to get symbol names.")
|
|
||||||
|
|
||||||
def _get_group_name_by_num(self, group_num):
|
|
||||||
cur_group_atom = self._groups_source[group_num]
|
|
||||||
if cur_group_atom != None_:
|
|
||||||
b_group_name = XGetAtomName(self._display, cur_group_atom)
|
|
||||||
return b_group_name.decode() if b_group_name else ""
|
|
||||||
else:
|
|
||||||
raise X11Error("Failed to get group name.")
|
|
||||||
|
|
||||||
|
|
||||||
SymbolData = namedtuple("SymbolData", ["symbol", "variant", "index"])
|
|
||||||
SYMBOL_REGEX = re.compile(r"""
|
|
||||||
(?P<symbol>\w+)
|
|
||||||
(?: \( (?P<variant>\w+) \) )?
|
|
||||||
(?: : (?P<index>\d+) )?
|
|
||||||
""", re.VERBOSE)
|
|
||||||
|
|
||||||
class _Compat_SRE_Pattern:
|
|
||||||
def __init__(self, re_obj):
|
|
||||||
self.re_obj = re_obj
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
return getattr(self.re_obj, name)
|
|
||||||
|
|
||||||
# re_obj.fullmatch is a Python 3.4+ only feature
|
|
||||||
def fullmatch(self, string, pos=None, endpos=None):
|
|
||||||
pos = pos if pos else 0
|
|
||||||
endpos = endpos if endpos else len(string)
|
|
||||||
match = self.re_obj.match(string, pos, endpos)
|
|
||||||
if match and match.span() != (pos, endpos):
|
|
||||||
return None
|
|
||||||
return match
|
|
||||||
|
|
||||||
if sys.version_info < (3, 4):
|
|
||||||
SYMBOL_REGEX = _Compat_SRE_Pattern(SYMBOL_REGEX)
|
|
||||||
|
|
||||||
def _parse_symbols(symbols_str, non_symbols, default_index=0):
|
|
||||||
def get_symboldata(symstr):
|
|
||||||
match = SYMBOL_REGEX.fullmatch(symstr)
|
|
||||||
if match:
|
|
||||||
index = match.group('index')
|
|
||||||
return SymbolData(
|
|
||||||
match.group('symbol'),
|
|
||||||
match.group('variant'),
|
|
||||||
int(index) - 1 if index else default_index)
|
|
||||||
else:
|
|
||||||
raise X11Error("Malformed symbol string: \"{}\"".format(symstr))
|
|
||||||
|
|
||||||
symboldata_list = []
|
|
||||||
for symstr in symbols_str.split('+'):
|
|
||||||
symboldata = get_symboldata(symstr)
|
|
||||||
if symboldata.symbol not in non_symbols:
|
|
||||||
symboldata_list.append(symboldata)
|
|
||||||
|
|
||||||
indices = [symdata.index for symdata in symboldata_list]
|
|
||||||
assert len(indices) == len(set(indices)) # No doubles
|
|
||||||
|
|
||||||
return symboldata_list
|
|
||||||
|
|
||||||
|
|
||||||
_COLON_SEPARATOR_REGEX = re.compile(r"(?<!\\):")
|
|
||||||
|
|
||||||
class _ListProxy(UserList):
|
|
||||||
def __format__(self, format_spec):
|
|
||||||
if len(format_spec) > 0:
|
|
||||||
spec_parts = _COLON_SEPARATOR_REGEX.split(format_spec)
|
|
||||||
spec_parts = [s.replace("\\:", ":") for s in spec_parts]
|
|
||||||
assert len(spec_parts) > 0
|
|
||||||
|
|
||||||
elem_spec = spec_parts[0]
|
|
||||||
elems_formatted = [format(x, elem_spec) for x in self.data]
|
|
||||||
|
|
||||||
if len(spec_parts) == 1:
|
|
||||||
assert len(elem_spec) > 0
|
|
||||||
return str(elems_formatted)
|
|
||||||
elif len(spec_parts) == 2:
|
|
||||||
sep = spec_parts[1]
|
|
||||||
return sep.join(elems_formatted)
|
|
||||||
else:
|
|
||||||
raise ValueError(
|
|
||||||
"Too many specifiers: \"{}\"".format(format_spec))
|
|
||||||
|
|
||||||
return str(self.data)
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["XKeyboard", "GroupData", "X11Error"]
|
|
||||||
|
|
||||||
|
|
||||||
def print_xkeyboard(xkb):
|
|
||||||
print("xkb {")
|
|
||||||
contents = [
|
|
||||||
"%d groups {%s}," % (xkb.groups_count, ", ".join(xkb.groups_names)),
|
|
||||||
"symbols {%s}" % ", ".join(xkb.groups_symbols),
|
|
||||||
"variants {%s}" % ", ".join('"{}"'.format(variant) for variant in xkb.groups_variants),
|
|
||||||
"current group: %s (%d) - %s - \"%s\"" %
|
|
||||||
(xkb.group_symbol, xkb.group_num, xkb.group_name, xkb.group_variant)
|
|
||||||
]
|
|
||||||
print("\n".join("\t" + line for line in contents))
|
|
||||||
print("}")
|
|
||||||
|
|
||||||
def test():
|
|
||||||
with XKeyboard() as xkb:
|
|
||||||
print_xkeyboard(xkb)
|
|
||||||
xkb.group_num += 2
|
|
||||||
print_xkeyboard(xkb)
|
|
||||||
xkb.group_num -= 3
|
|
||||||
print_xkeyboard(xkb)
|
|
||||||
xkb.group_num -= 2
|
|
||||||
print_xkeyboard(xkb)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
test()
|
|
27
thirdparty/xkbgroup/version.py
vendored
27
thirdparty/xkbgroup/version.py
vendored
|
@ -1,27 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
"""
|
|
||||||
xkbgroup.version
|
|
||||||
~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
This module collects all version-related utilities.
|
|
||||||
|
|
||||||
Global variables:
|
|
||||||
* VERSION: current version of this library.
|
|
||||||
|
|
||||||
Functions:
|
|
||||||
* print_version: pretty prints current version.
|
|
||||||
|
|
||||||
:copyright: (c) 2016 by Nguyen Duc My.
|
|
||||||
:license: MIT, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
VERSION = "0.1.4.3"
|
|
||||||
|
|
||||||
def print_version(progname):
|
|
||||||
"""Prints the version along with the specified program name."""
|
|
||||||
print("{} {}".format(progname, VERSION))
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["VERSION", "print_version"]
|
|
9440
thirdparty/xkbgroup/xkb.py
vendored
9440
thirdparty/xkbgroup/xkb.py
vendored
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue