Search code examples
pythonpython-3.xpython-2.xxlib

Python3 segfaults when using ctypes on xlib, python2 works


The following code was scrounged up on the Internet years ago and works quite well in python2. It supplies the current idle time on the X server.

import ctypes, os, subprocess
class XScreenSaverInfo( ctypes.Structure ):
    _fields_ = [("window",     ctypes.c_ulong), ("state",      ctypes.c_int), ("kind",       ctypes.c_int), ("since",      ctypes.c_ulong), ("idle",       ctypes.c_ulong), ("event_mask", ctypes.c_ulong)]

xlib = ctypes.cdll.LoadLibrary("libX11.so.6")
xss = ctypes.cdll.LoadLibrary("libXss.so.1")
display = xlib.XOpenDisplay(os.environ["DISPLAY"])
xss.XScreenSaverAllocInfo.restype = ctypes.POINTER(XScreenSaverInfo)
xssinfo = xss.XScreenSaverAllocInfo()

 xss.XScreenSaverQueryInfo(display, xlib.XDefaultRootWindow(display), xssinfo)

 xssinfo.contents.idle

I can throw this into a python2.7.10 shell and get what I want. However, doing the same on a python3.4.3 shell kicks me out with a segmentation fault in this line

xss.XScreenSaverQueryInfo(display, xlib.XDefaultRootWindow(display), xssinfo)

Is my py3 environment broken? Does py3 do something differently?


Solution

  • There was no significant change in Python 3's ctypes module. However in Python3 os.environ values are unicode strings, contrary to the byte strings of python2 and this causes the segmentation fault. So changing:

    display = xlib.XOpenDisplay(os.environ["DISPLAY"])
    

    To:

    display = xlib.XOpenDisplay(bytes(os.environ["DISPLAY"], 'ascii'))
    

    Fixes the segmentation fault.

    If you want to have code that works both in python 2 and 3 you want to use the encode method:

    display = xlib.XOpenDisplay(os.environ["DISPLAY"].encode('ascii'))