I'm semi-new to ctypes and I'm having trouble with how to use the GetRawInputData function. I'm not sure how to fill in the 3rd argument. Here's how the code looks right now
def get_raw_input(handle):
dw_size = c_uint()
GetRawInputData(handle, RID_INPUT, None, byref(dw_size), sizeof(RawInputHeader))
lpb = LPBYTE(dw_size)
print(lpb.contents)
GetRawInputData(handle, RID_INPUT, lpb, byref(dw_size), sizeof(RawInputHeader))
print(lpb.contents)
return cast(lpb, POINTER(RawInput))
I've defined the module like so:
GetRawInputData = user32.GetRawInputData
GetRawInputData.argtypes = INT, UINT, LPVOID, PUINT, UINT
GetRawInputData.restype = UINT
GetRawInputData.errcheck = errcheck
When get_raw_input is run, the output is one of either of these:
c_byte(48)
c_byte(0)
c_byte(40)
c_byte(1)
There's some example C++ code here, but I'm not sure how that should look in Python.
Here's the full running code (sorry if it's a bit messy):
window.py (file that runs)
from ctypes import *
from ctypes.wintypes import *
from structures import *
from constants import *
from devices import get_raw_input, register_devices
import random
import sys
def errcheck(result,func,args):
if result is None or result == 0:
raise WinError(get_last_error())
return result
user32 = WinDLL('user32', use_last_error=True)
k32 = WinDLL('kernel32', use_last_error=True)
gdi32 = WinDLL('gdi32',use_last_error=True)
RegisterClass = user32.RegisterClassW
RegisterClass.argtypes = POINTER(WndClass),
RegisterClass.restype = ATOM
RegisterClass.errcheck = errcheck
CreateWindowEx = user32.CreateWindowExW
CreateWindowEx.argtypes = DWORD, LPCWSTR, LPCWSTR, DWORD, INT, INT, INT, INT, HWND, HMENU, HINSTANCE, LPVOID
CreateWindowEx.restype = HWND
CreateWindowEx.errcheck = errcheck
GetModuleHandle = k32.GetModuleHandleW
GetModuleHandle.argtypes = LPCWSTR,
GetModuleHandle.restype = HMODULE
GetModuleHandle.errcheck = errcheck
DefWindowProc = user32.DefWindowProcW
DefWindowProc.argtypes = HWND, UINT, WPARAM, LPARAM
DefWindowProc.restype = LRESULT
ShowWindow = user32.ShowWindow
ShowWindow.argtypes = HWND, INT
ShowWindow.restype = BOOL
UpdateWindow = user32.UpdateWindow
UpdateWindow.argtypes = HWND,
UpdateWindow.restype = BOOL
UpdateWindow.errcheck = errcheck
BeginPaint = user32.BeginPaint
BeginPaint.argtypes = HWND, POINTER(PaintStruct)
BeginPaint.restype = HDC
BeginPaint.errcheck = errcheck
FillRect = user32.FillRect
FillRect.argtypes = HDC, POINTER(RECT), HBRUSH
FillRect.restype = INT
FillRect.errcheck = errcheck
EndPaint = user32.EndPaint
EndPaint.argtypes = HWND, POINTER(PaintStruct)
EndPaint.restype = BOOL
EndPaint.errcheck = errcheck
PostQuitMessage = user32.PostQuitMessage
PostQuitMessage.argtypes = INT,
PostQuitMessage.restype = None
TranslateMessage = user32.TranslateMessage
TranslateMessage.argtypes = POINTER(MSG),
TranslateMessage.restype = BOOL
DispatchMessage = user32.DispatchMessageW
DispatchMessage.argtypes = POINTER(MSG),
DispatchMessage.restype = LRESULT
GetClientRect = user32.GetClientRect
GetClientRect.argtypes = HWND, POINTER(RECT)
GetClientRect.restype = BOOL
GetClientRect.errcheck = errcheck
GetMessage = user32.GetMessageW
GetMessage.argtypes = POINTER(MSG), HWND, UINT, UINT
GetMessage.restype = BOOL
DrawText = user32.DrawTextW
DrawText.argtypes = HDC, LPCWSTR, INT, POINTER(RECT), UINT
DrawText.restype = LRESULT
LoadIcon = user32.LoadIconW
LoadIcon.argtypes = HINSTANCE, LPCWSTR
LoadIcon.restype = HICON
LoadIcon.errcheck = errcheck
LoadCursor = user32.LoadCursorW
LoadCursor.argtypes = HINSTANCE, LPCWSTR
LoadCursor.restype = HCURSOR
LoadCursor.errcheck = errcheck
GetStockObject = gdi32.GetStockObject
GetStockObject.argtypes = INT,
GetStockObject.restype = HGDIOBJ
CreateSolidBrush = gdi32.CreateSolidBrush
CreateSolidBrush.argtypes = COLORREF,
CreateSolidBrush.restype = HBRUSH
TextOut = gdi32.TextOutW
TextOut.argtypes = HDC, INT, INT, LPCWSTR, INT
TextOut.restype = INT
SetBkMode = gdi32.SetBkMode
SetBkMode.argtypes = HDC, INT
SetBkMode.restype = INT
SetTextColor = gdi32.SetTextColor
SetTextColor.argtypes = HDC, COLORREF
SetTextColor.restype = COLORREF
def window_callback(handle, message, w_param, l_param):
ps = PaintStruct()
rect = RECT()
if message == WM_PAINT:
hdc = BeginPaint(handle, byref(ps))
GetClientRect(handle, byref(rect))
SetBkMode(hdc, -1)
SetTextColor(hdc, 0xFFFFFF)
FillRect(hdc, byref(ps.rcPaint), CreateSolidBrush(0x0))
DrawText(hdc, 'Testing oawguna', -1, byref(rect), DT_SINGLELINE|DT_CENTER|DT_VCENTER)
EndPaint(handle, byref(ps))
return 0
elif message == WM_INPUT:
raw_input = get_raw_input(l_param)
elif message == WM_DESTROY:
PostQuitMessage(0)
return 0
return DefWindowProc(handle, message, w_param, l_param)
def run():
instance = GetModuleHandle(None)
class_name = 'TestWindow'
wnd = WndClass()
wnd.style = CS_HREDRAW | CS_VREDRAW
wnd.lpfnWndProc = WNDPROC(window_callback)
wnd.hInstace = instance
wnd.hCursor = LoadCursor(None, IDC_ARROW)
wnd.lpszClassName = class_name
RegisterClass(byref(wnd))
handle = CreateWindowEx(
0, class_name, 'Python Window', WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
None, None, instance, None
)
ShowWindow(handle, SW_NORMAL)
UpdateWindow(handle)
register_devices(handle)
msg = MSG()
while GetMessage(byref(msg), None, 0, 0) != 0:
TranslateMessage(byref(msg))
DispatchMessage(byref(msg))
return msg.wParam
if __name__ == "__main__":
sys.exit(run())
devices.py:
from ctypes import *
from ctypes.wintypes import *
from structures import *
from constants import *
def errcheck(result,func,args):
if result is None or result == -1:
raise WinError(get_last_error())
return result
user32 = WinDLL('user32', use_last_error=True)
RegisterRawInputDevices = user32.RegisterRawInputDevices
RegisterRawInputDevices.argtypes = (RawInputDevice * 7), UINT, UINT
RegisterRawInputDevices.restype = BOOL
RegisterRawInputDevices.errcheck = errcheck
GetRawInputData = user32.GetRawInputData
GetRawInputData.argtypes = INT, UINT, LPVOID, PUINT, UINT
GetRawInputData.restype = UINT
GetRawInputData.errcheck = errcheck
def register_devices(hwnd_target=None):
page = 0x01
devices = (RawInputDevice * 7)(
RawInputDevice(page, 0x01, DW_FLAGS, hwnd_target),
RawInputDevice(page, 0x02, DW_FLAGS, hwnd_target),
RawInputDevice(page, 0x04, DW_FLAGS, hwnd_target),
RawInputDevice(page, 0x05, DW_FLAGS, hwnd_target),
RawInputDevice(page, 0x06, DW_FLAGS, hwnd_target),
RawInputDevice(page, 0x07, DW_FLAGS, hwnd_target),
RawInputDevice(page, 0x08, DW_FLAGS, hwnd_target),
)
RegisterRawInputDevices(devices, len(devices), sizeof(devices[0]))
def get_raw_input(handle):
dw_size = c_uint()
GetRawInputData(handle, RID_INPUT, None, byref(dw_size), sizeof(RawInputHeader))
lpb = LPBYTE(dw_size)
print(lpb.contents)
GetRawInputData(handle, RID_INPUT, lpb, byref(dw_size), sizeof(RawInputHeader))
print(lpb.contents)
return cast(lpb, POINTER(RawInput))
constants.py:
from ctypes.wintypes import *
from ctypes import c_int64, WINFUNCTYPE, c_void_p
LRESULT = c_int64
HCURSOR = c_void_p
USAGE_PAGE = 0x01
USAGE = 0x06
DW_FLAGS = 0
DW_TYPE = 2
WM_INPUT = 0x00FF
WM_PAINT = 0x000F
WM_DESTROY = 0x0002
WM_QUI = 0x0012
RIDI_DEVICENAME = 0x20000007
RIDI_DEVICEINFO = 0x2000000b
ERROR_INSUFFICIENT_BUFFER = 0x7A
WS_EX_CLIENTEDGE = 0x00000200
WS_BORDER = 0x00800000
CS_BYTEALIGNCLIENT = 0x1000
CS_HREDRAW = 2
CS_VREDRAW = 1
SW_NORMAL = 1
CW_USEDEFAULT = -2147483648
WS_OVERLAPPED = 0x00000000
WS_CAPTION = 0x00C00000
WS_SYSMENU = 0x00080000
WS_THICKFRAME = 0x00040000
WS_MINIMIZEBOX = 0x00020000
WS_MAXIMIZEBOX = 0x00010000
WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
RID_INPUT = 0x10000003
PM_REMOVE = 0x0001
DT_SINGLELINE = 32
DT_CENTER = 1
DT_VCENTER = 4
IDI_APPLICATION = LPCWSTR(32512)
IDC_ARROW = LPCWSTR(32512)
WHITE_BRUSH = 0
WNDPROC = WINFUNCTYPE(LRESULT, HWND, UINT, WPARAM, LPARAM)
structures.py:
from ctypes import Structure, windll, WINFUNCTYPE, c_int64, Union, c_byte, c_void_p
from ctypes.wintypes import *
from constants import *
HCURSOR = c_void_p
class RawInputDevice(Structure):
_fields_ = [
("usUsagePage", USHORT),
("usUsage", USHORT),
("dwFlags", DWORD),
("hwndTarget", HWND),
]
class RawInputDeviceList(Structure):
_fields_ = [
("hDevice", HANDLE),
("dwType", DWORD),
]
class RIDDeviceInfoHID(Structure):
_fields_ = [
("dwVendorId", DWORD),
("dwProductId", DWORD),
("dwVersionNumber", DWORD),
("usUsagePage", USHORT),
("usUsage", USHORT),
]
class RIDDeviceInfo(Structure):
_fields_ = [
("cbSize", DWORD),
("dwType", DWORD),
("hid", RIDDeviceInfoHID),
]
class WndClass(Structure):
_fields_ = [
("style", UINT),
("lpfnWndProc", WNDPROC),
("cbClsExtra", INT),
("cbWndExtra", INT),
("hInstance", HINSTANCE),
("hIcon", HICON),
("hCursor", HCURSOR),
("hbrBackground", HBRUSH),
("lpszMenuName", LPCWSTR),
("lpszClassName", LPCWSTR),
]
class PaintStruct(Structure):
_fields_ = [
("hdc", HDC),
("fErase", BOOL),
("rcPaint", RECT),
("fRestore", BOOL),
("fIncUpdate", BOOL),
("rgbReserved", BYTE * 32),
]
class RawInputHeader(Structure):
_fields_ = [
("dwType", DWORD),
("dwSize", DWORD),
("hDevice", HANDLE),
("wParam", WPARAM),
]
class RawHID(Structure):
_fields_ = [
("dwSizeHid", DWORD),
("dwCount", DWORD),
("bRawData", BYTE),
]
class MouseStruct(Structure):
_fields_ = [
("usButtonFlags", USHORT),
("usButtonData", USHORT)
]
class MouseUnion(Union):
_fields_ = [
("ulButtons", ULONG),
("data", MouseStruct)
]
class RawMouse(Structure):
_fields_ = [
("usFlags", USHORT),
("data", MouseUnion),
("ulRawButtons", ULONG),
("lLastX", LONG),
("lLastY", LONG),
("ulExtraInformation", ULONG),
]
class RawKeyboard(Structure):
_fields_ = [
("MakeCode", USHORT),
("Flags", USHORT),
("Reserved", USHORT),
("VKey", USHORT),
("Message", UINT),
("ExtraInformation", ULONG),
]
class RawUnion(Union):
_fields_ = [
("mouse", RawMouse),
("keyboard", RawKeyboard),
("hid", RawHID)
]
class RawInput(Structure):
_fields_ = [
("header", RawInputHeader),
("data", RawUnion),
]
Listing [Python.Docs]: ctypes - A foreign function library for Python.
This (a fix) was already covered as part of [SO]: Having trouble using winapi to read input from a device (#CristiFati's answer).
The problem is LPBYTE (POINTER(c_byte)) initialization:
Without parameters, wraps a NULL pointer
With parameter (an integer value) it will store that value in the pointed area (and not use it as size). More, that area will be 064bit large (discovered experimentally) meaning that trying to index outside it triggers Undefined Behavior, even if the program doesn't crash (that is because the area right next isn't claimed by other variables)
To allocate a memory area of a certain size, use create_string_buffer (or an array - but you'll have to cast when passing it to functions expecting pointers).
>>> import ctypes as ct
>>> import ctypes.wintypes as wt
>>>
>>>
>>> pb0 = wt.LPBYTE()
>>> bool(pb0)
False
>>> pb0.contents
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: NULL pointer access
>>>
>>> ll = ct.c_longlong(0x010203040506070809) # Will be truncated (head part - due to little endianness)
>>> pb1 = wt.LPBYTE(ll)
>>> bool(pb1)
True
>>> pb1.contents
c_byte(9)
>>> [pb1[e] for e in range(0, 10)] # #TODO - cfati: will genarate UB !!!
[9, 8, 7, 6, 5, 4, 3, 2, 0, 0]
>>>
>>> buf = ct.create_string_buffer(12)
>>> len(buf)
12
>>> buf[0]
b'\x00'
>>> buf[12]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: invalid index
I'd like to incorporate a way to have my GUI open on a second monitor, if available. I want to add in some type of error handling so if there is a second monitor, use it, else open on the center of the detected display. Can this be done?
I've been using this snippet using the win32 API to enumerate monitors on windows:
import ctypes
user = ctypes.windll.user32
class RECT(ctypes.Structure):
_fields_ = [
('left', ctypes.c_long),
('top', ctypes.c_long),
('right', ctypes.c_long),
('bottom', ctypes.c_long)
]
def dump(self):
return [int(val) for val in (self.left, self.top, self.right, self.bottom)]
class MONITORINFO(ctypes.Structure):
_fields_ = [
('cbSize', ctypes.c_ulong),
('rcMonitor', RECT),
('rcWork', RECT),
('dwFlags', ctypes.c_ulong)
]
def get_monitors():
retval = []
CBFUNC = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_ulong, ctypes.c_ulong, ctypes.POINTER(RECT), ctypes.c_double)
def cb(hMonitor, hdcMonitor, lprcMonitor, dwData):
r = lprcMonitor.contents
#print("cb: %s %s %s %s %s %s %s %s" % (hMonitor, type(hMonitor), hdcMonitor, type(hdcMonitor), lprcMonitor, type(lprcMonitor), dwData, type(dwData)))
data = [hMonitor]
data.append(r.dump())
retval.append(data)
return 1
cbfunc = CBFUNC(cb)
temp = user.EnumDisplayMonitors(0, 0, cbfunc, 0)
#print(temp)
return retval
def monitor_areas():
retval = []
monitors = get_monitors()
for hMonitor, extents in monitors:
data = [hMonitor]
mi = MONITORINFO()
mi.cbSize = ctypes.sizeof(MONITORINFO)
mi.rcMonitor = RECT()
mi.rcWork = RECT()
res = user.GetMonitorInfoA(hMonitor, ctypes.byref(mi))
data = mi.rcMonitor.dump()
# data.append(mi.rcWork.dump())
retval.append(data)
return retval
if __name__ == "__main__":
print(monitor_areas())
On my system that prints
[[0, 0, 3440, 1440], [3440, 0, 5120, 1050]]
In tkinter you can then move your app window to a given location with
appwindow.geometry('+1720+720')
Which puts it 1720px in from the left and 720 down from the top.
Any monitors that are to the left of or above your primary monitor may get negative coordinates, but they should work just fine.
On MacOS, use Appkit:
import AppKit
[(screen.frame().size.width, screen.frame().size.height)
for screen in AppKit.NSScreen.screens()]
will give you a list of tuples containing all screen sizes (if multiple monitors present)
I am trying to capture the screen using only the ctypes modules. Unfortunately I cannot retrieve raw pixel from CGDataProviderCopyData. I need to get an access to raw data:
#!/usr/bin/env python
# coding: utf-8
from sys import maxsize
from ctypes import POINTER, Structure, c_double, byref, c_void_p, c_int32, c_uint32, c_float, cdll
from ctypes.util import find_library
CGFloat = c_double if maxsize > 2 ** 32 else c_float
class CGPoint(Structure):
_fields_ = [('x', CGFloat), ('y', CGFloat)]
class CGSize(Structure):
_fields_ = [('width', CGFloat), ('height', CGFloat)]
class CGRect(Structure):
_fields_ = [('origin', CGPoint), ('size', CGSize)]
# Library
cgs = cdll.LoadLibrary(find_library('CoreGraphics'))
# Argtypes
cgs.CGGetActiveDisplayList.argtypes = [c_uint32, POINTER(c_uint32), POINTER(c_uint32)]
cgs.CGDisplayBounds.argtypes = [c_uint32]
cgs.CGRectStandardize.argtypes = [CGRect]
cgs.CGDisplayRotation.argtypes = [c_uint32]
cgs.CGWindowListCreateImage.argtypes = [CGRect, c_uint32, c_uint32, c_uint32]
cgs.CGImageGetWidth.argtypes = [c_void_p]
cgs.CGImageGetHeight.argtypes = [c_void_p]
cgs.CGImageGetDataProvider.argtypes = [c_void_p]
cgs.CGDataProviderCopyData.argtypes = [c_void_p]
cgs.CGDataProviderRelease.argtypes = [c_void_p]
# Restypes
cgs.CGGetActiveDisplayList.restype = c_int32
cgs.CGDisplayBounds.restype = CGRect
cgs.CGRectStandardize.restype = CGRect
cgs.CGDisplayRotation.restype = c_float
cgs.CGWindowListCreateImage.restype = c_void_p
cgs.CGImageGetWidth.restype = c_uint32
cgs.CGImageGetHeight.restype = c_uint32
cgs.CGImageGetDataProvider.restype = c_void_p
cgs.CGDataProviderCopyData.restype = c_void_p
cgs.CGDataProviderRelease.restype = c_void_p
# Monitors
max_displays = 32
display_count = c_uint32(0)
active_displays = (c_uint32 * max_displays)()
cgs.CGGetActiveDisplayList(max_displays, active_displays, byref(display_count))
for idx in range(display_count.value):
display = active_displays[idx]
rect = cgs.CGDisplayBounds(display)
rect = cgs.CGRectStandardize(rect)
image_ref = cgs.CGWindowListCreateImage(rect, 1, 0, 0)
width = int(cgs.CGImageGetWidth(image_ref))
height = int(cgs.CGImageGetHeight(image_ref))
prov = cgs.CGImageGetDataProvider(image_ref)
data = cgs.CGDataProviderCopyData(prov)
# How to get raw pixels from data`?
MacOS X version 10.11.3.
Python versions 2.7.10 and 2.6.9.
I have no experience with the CoreGraphics API, but looking at the documentation it looks like CGDataProviderCopyData returns a CFDataRef object. The documentat for CFDataRef has a section on "Examining a CFData Object" which describes a CFDataGetLength function and a CFDataGetBytePtr function which return the length of the data and a UInt8*.
https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CGDataProvider/#//apple_ref/c/func/CGDataProviderCopyData
https://developer.apple.com/library/ios/documentation/CoreFoundation/Reference/CFDataRef/index.html
Thanks to Snorfalorpagus, I finally succeed:
#...
data = cgs.CGDataProviderCopyData(prov)
data_ref = cgs.CFDataGetBytePtr(data)
buf_len = cgs.CFDataGetLength()
image_data = cast(data_ref, POINTER(c_ubyte * buf_len))
cgs.CGDataProviderRelease(prov)
# Raw pixels are in image_data.contents
I am using the following code to read an svg:
from ctypes import CDLL, POINTER, Structure, byref, util
from ctypes import c_bool, c_byte, c_void_p, c_int, c_double, c_uint32, c_char_p
class _PycairoContext(Structure):
_fields_ = [("PyObject_HEAD", c_byte * object.__basicsize__),
("ctx", c_void_p),
("base", c_void_p)]
class _RsvgProps(Structure):
_fields_ = [("width", c_int), ("height", c_int),
("em", c_double), ("ex", c_double)]
class _GError(Structure):
_fields_ = [("domain", c_uint32), ("code", c_int), ("message", c_char_p)]
def _load_rsvg(rsvg_lib_path=None, gobject_lib_path=None):
if rsvg_lib_path is None:
rsvg_lib_path = util.find_library('rsvg-2')
if gobject_lib_path is None:
gobject_lib_path = util.find_library('gobject-2.0')
l = CDLL(rsvg_lib_path)
g = CDLL(gobject_lib_path)
g.g_type_init()
l.rsvg_handle_new_from_file.argtypes = [c_char_p, POINTER(POINTER(_GError))]
l.rsvg_handle_new_from_file.restype = c_void_p
l.rsvg_handle_render_cairo.argtypes = [c_void_p, c_void_p]
l.rsvg_handle_render_cairo.restype = c_bool
l.rsvg_handle_get_dimensions.argtypes = [c_void_p, POINTER(_RsvgProps)]
return l
_librsvg = _load_rsvg()
class Handle(object):
def __init__(self, path):
lib = _librsvg
err = POINTER(_GError)()
self.handle = lib.rsvg_handle_new_from_file(path, byref(err))
if self.handle is None:
gerr = err.contents
raise Exception(gerr.message)
self.props = _RsvgProps()
lib.rsvg_handle_get_dimensions(self.handle, byref(self.props))
def render_cairo(self, ctx):
"""Returns True is drawing succeeded."""
z = _PycairoContext.from_address(id(ctx))
return _librsvg.rsvg_handle_render_cairo(self.handle, z.ctx)
(from Error with Python ctypes and librsvg)
I call img = Handle(path) and this leaks memory. I am pretty sure this is due to incorrectly using ctypes & pointers, but I cannot find a way to fix this.
According to the documentation of rsvg_handle_new, free the handle with g_object_unref. Also, if a failed call allocates a GError, after you get the code and message you have to free the error with g_error_free.
from ctypes import *
from ctypes.util import find_library
_gobj = CDLL(find_library("gobject-2.0"))
_glib = CDLL(find_library("glib-2.0"))
class _GError(Structure):
_fields_ = [("domain", c_uint32),
("code", c_int),
("message", c_char_p)]
_GErrorP = POINTER(_GError)
_glib.g_error_free.restype = None
_glib.g_error_free.argtypes = [_GErrorP]
_gobj.g_object_unref.restype = None
_gobj.g_object_unref.argtypes = [c_void_p]
You can free the handle in the __del__ method of the Handle class:
class Handle(object):
_gobj = _gobj # keep a valid ref for module teardown
# ...
def __del__(self):
if self.handle:
self._gobj.g_object_unref(self.handle)
self.handle = None