 1446a6d7ee
			
		
	
	
		1446a6d7ee
		
			
		
	
	
	
	
		
			
			Update keyboard_layout property whenever guivm's layout changes, instead of only at the start. requires QubesOS/qubes-core-admin#350 references QubesOS/qubes-issues#1396 references QubesOS/qubes-issues#4294
		
			
				
	
	
		
			129 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			129 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # -*- encoding: utf8 -*-
 | |
| #
 | |
| # The Qubes OS Project, http://www.qubes-os.org
 | |
| #
 | |
| # Copyright (C) 2020 Marek Marczykowski-Górecki
 | |
| #                               <marmarek@invisiblethingslab.com>
 | |
| #
 | |
| # This program is free software; you can redistribute it and/or modify
 | |
| # it under the terms of the GNU Lesser General Public License as published by
 | |
| # the Free Software Foundation; either version 2.1 of the License, or
 | |
| # (at your option) any later version.
 | |
| #
 | |
| # This program is distributed in the hope that it will be useful,
 | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| # GNU Lesser General Public License for more details.
 | |
| #
 | |
| # You should have received a copy of the GNU Lesser General Public License along
 | |
| # with this program; if not, see <http://www.gnu.org/licenses/>.
 | |
| """
 | |
| This is a set of helper classes, designed to facilitate importing an X extension
 | |
| that's not supported by default by xcffib.
 | |
| """
 | |
| import io
 | |
| import struct
 | |
| import xcffib
 | |
| 
 | |
| 
 | |
| 
 | |
| class XkbUseExtensionReply(xcffib.Reply):
 | |
|     """Helper class to parse XkbUseExtensionReply
 | |
|     Contains hardcoded values based on X11/XKBproto.h"""
 | |
|     # pylint: disable=too-few-public-methods
 | |
|     def __init__(self, unpacker):
 | |
|         if isinstance(unpacker, xcffib.Protobj):
 | |
|             unpacker = xcffib.MemoryUnpacker(unpacker.pack())
 | |
|         xcffib.Reply.__init__(self, unpacker)
 | |
|         base = unpacker.offset
 | |
|         self.major_version, self.minor_version = unpacker.unpack(
 | |
|             "xx2x4xHH4x4x4x4x")
 | |
|         self.bufsize = unpacker.offset - base
 | |
| 
 | |
| 
 | |
| class XkbUseExtensionCookie(xcffib.Cookie):
 | |
|     """Helper class for use in loading Xkb extension"""
 | |
|     reply_type = XkbUseExtensionReply
 | |
| 
 | |
| 
 | |
| class XkbGetStateReply(xcffib.Reply):
 | |
|     """Helper class to parse XkbGetState; copy&paste from X11/XKBproto.h"""
 | |
|     # pylint: disable=too-few-public-methods
 | |
|     _typedef = """
 | |
|         BYTE    type;
 | |
|         BYTE    deviceID;
 | |
|         CARD16  sequenceNumber B16;
 | |
|         CARD32  length B32;
 | |
|         CARD8   mods;
 | |
|         CARD8   baseMods;
 | |
|         CARD8   latchedMods;
 | |
|         CARD8   lockedMods;
 | |
|         CARD8   group;
 | |
|         CARD8   lockedGroup;
 | |
|         INT16   baseGroup B16;
 | |
|         INT16   latchedGroup B16;
 | |
|         CARD8   compatState;
 | |
|         CARD8   grabMods;
 | |
|         CARD8   compatGrabMods;
 | |
|         CARD8   lookupMods;
 | |
|         CARD8   compatLookupMods;
 | |
|         CARD8   pad1;
 | |
|         CARD16  ptrBtnState B16;
 | |
|         CARD16  pad2 B16;
 | |
|         CARD32  pad3 B32;"""
 | |
|     _type_mapping = {
 | |
|         "BYTE": "B",
 | |
|         "CARD16": "H",
 | |
|         "CARD8": "B",
 | |
|         "CARD32": "I",
 | |
|         "INT16": "h",
 | |
|     }
 | |
| 
 | |
|     def __init__(self, unpacker):
 | |
|         if isinstance(unpacker, xcffib.Protobj):
 | |
|             unpacker = xcffib.MemoryUnpacker(unpacker.pack())
 | |
|         xcffib.Reply.__init__(self, unpacker)
 | |
|         base = unpacker.offset
 | |
| 
 | |
|         # dynamic parse of copy&pasted struct content, for easy re-usability
 | |
|         for line in self._typedef.splitlines():
 | |
|             line = line.strip()
 | |
|             line = line.rstrip(';')
 | |
|             if not line:
 | |
|                 continue
 | |
|             typename, name = line.split()[:2]  # ignore optional third part
 | |
|             setattr(self, name, unpacker.unpack(self._type_mapping[typename]))
 | |
| 
 | |
|         self.bufsize = unpacker.offset - base
 | |
| 
 | |
| 
 | |
| class XkbGetStateCookie(xcffib.Cookie):
 | |
|     """Helper class for use in parsing Xkb GetState"""
 | |
|     reply_type = XkbGetStateReply
 | |
| 
 | |
| 
 | |
| class XkbExtension(xcffib.Extension):
 | |
|     """Helper class to load and use Xkb xcffib extension; needed
 | |
|     because there is not XKB support in xcffib."""
 | |
|     # pylint: disable=invalid-name,missing-function-docstring
 | |
|     def UseExtension(self, is_checked=True):
 | |
|         buf = io.BytesIO()
 | |
|         buf.write(struct.pack("=xx2xHH", 1, 0))
 | |
|         return self.send_request(0, buf, XkbGetStateCookie,
 | |
|                                  is_checked=is_checked)
 | |
| 
 | |
|     def GetState(self, deviceSpec=0x100, is_checked=True):
 | |
|         buf = io.BytesIO()
 | |
|         buf.write(struct.pack("=xx2xHxx", deviceSpec))
 | |
|         return self.send_request(4, buf, XkbGetStateCookie,
 | |
|                                  is_checked=is_checked)
 | |
| 
 | |
| 
 | |
| key = xcffib.ExtensionKey("XKEYBOARD")
 | |
| # this is a lie: there are events and errors types
 | |
| _events = {}
 | |
| _errors = {}
 | |
| 
 | |
| # pylint: disable=protected-access
 | |
| xcffib._add_ext(key, XkbExtension, _events, _errors)
 |