I have the following structure:
class KeyboardModifiers(Structure):
_fields_ = [
('left_control', c_bool, 1),
('right_control', c_bool, 1),
('left_shift', c_bool, 1),
('right_shift', c_bool, 1),
('left_alt', c_bool, 1),
('right_alt', c_bool, 1),
('left_meta', c_bool, 1),
('right_meta', c_bool, 1),
('left_super', c_bool, 1),
('right_super', c_bool, 1),
('left_hyper', c_bool, 1),
('right_hyper', c_bool, 1),
]
It represents a structure returned by a C function, the fields are properly set and their values returned, issue comes when setting a field. For example, if I were to do something like:
my_keyboard_mods.left_shift = True
The first 8 fields would add be set to True, similarly with the next 8. What seems to happen is that it sets the value for the whole byte not respecting the bitfield. My question is:
Thanks.
It results from using c_bool
. I'm not getting the whole byte set to 1, but bit set resulting in a non-zero byte results in a value of 1, not 0xFF as described. It may be an OS or implementation detail. For me it seems to normalize the boolean value to 0/1 based on zero/non-zero value of the byte. For your implementation it may be normalizing to 0/FF. FYI: make sure to post the entire code that reproduces the problem next time.
from ctypes import *
class KeyboardModifiers(Structure):
_fields_ = [
('left_control', c_bool, 1),
('right_control', c_bool, 1),
('left_shift', c_bool, 1),
('right_shift', c_bool, 1),
('left_alt', c_bool, 1),
('right_alt', c_bool, 1),
('left_meta', c_bool, 1),
('right_meta', c_bool, 1),
('left_super', c_bool, 1),
('right_super', c_bool, 1),
('left_hyper', c_bool, 1),
('right_hyper', c_bool, 1),
]
k = KeyboardModifiers()
k.left_control = True
k.left_shift = True
k.right_hyper = True
print(bytes(k))
Expected b'\x05\0x08'
but got the following output due to c_bool
implementation treating non-zero bytes as 1:
b'\x01\x01'
Use c_ubyte
or c_ushort
instead. Note that using a type larger than two bytes would make the structure longer than two bytes if size is a concern:
from ctypes import *
class KeyboardModifiers(Structure):
_fields_ = [
('left_control', c_ubyte, 1),
('right_control', c_ubyte, 1),
('left_shift', c_ubyte, 1),
('right_shift', c_ubyte, 1),
('left_alt', c_ubyte, 1),
('right_alt', c_ubyte, 1),
('left_meta', c_ubyte, 1),
('right_meta', c_ubyte, 1),
('left_super', c_ubyte, 1),
('right_super', c_ubyte, 1),
('left_hyper', c_ubyte, 1),
('right_hyper', c_ubyte, 1),
]
k = KeyboardModifiers()
k.left_control = True
k.left_shift = True
k.right_hyper = True
print(bytes(k))
Output:
b'\x05\x08'
Edit: I see now that each field seems to refer to bit 0 of its containing byte, so even though the byte value of the structure only ever has 0 or 1, the values of the field bits do all change to True
when displayed. In any case the fix is the same...don't use c_bool
:
from ctypes import *
class KeyboardModifiers(Structure):
_fields_ = [
('left_control', c_bool, 1),
('right_control', c_bool, 1),
('left_shift', c_bool, 1),
('right_shift', c_bool, 1),
('left_alt', c_bool, 1),
('right_alt', c_bool, 1),
('left_meta', c_bool, 1),
('right_meta', c_bool, 1),
('left_super', c_bool, 1),
('right_super', c_bool, 1),
('left_hyper', c_bool, 1),
('right_hyper', c_bool, 1),
]
def __repr__(self):
return (f'KeyboardModifiers(\n'
f' left_control={self.left_control},\n'
f' right_control={self.right_control},\n'
f' left_shift={self.left_shift},\n'
f' right_shift={self.right_shift},\n'
f' left_alt={self.left_alt},\n'
f' right_alt={self.right_alt},\n'
f' left_meta={self.left_meta},\n'
f' right_meta={self.right_meta},\n'
f' left_super={self.left_super},\n'
f' right_super={self.right_super},\n'
f' left_hyper={self.left_hyper},\n'
f' right_hyper={self.right_hyper})')
k = KeyboardModifiers()
k.left_control = True
print(bytes(k))
print(k)
Output:
b'\x01\x00'
KeyboardModifiers(
left_control=True,
right_control=True,
left_shift=True,
right_shift=True,
left_alt=True,
right_alt=True,
left_meta=True,
right_meta=True,
left_super=False,
right_super=False,
left_hyper=False,
right_hyper=False)