# -*- python -*- # Copyright © 2014 Mattias Andrée (maandree@member.fsf.org) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . cimport cython from libc.stdlib cimport malloc, free, realloc from libc.stdint cimport * cdef extern void blueshift_drm_close() ''' Free all resources, but you need to close all connections first ''' cdef extern int blueshift_drm_card_count() ''' Get the number of cards present on the system @return The number of cards present on the system ''' cdef extern int blueshift_drm_open_card(int card_index) ''' Open connection to a graphics card @param card_index The index of the graphics card @return -1 on failure, otherwise an identifier for the connection to the card ''' cdef extern void blueshift_drm_update_card(int connection) ''' Update the resource, required after `blueshift_drm_open_card` @param connection The identifier for the connection to the card ''' cdef extern void blueshift_drm_close_card(int connection) ''' Close connection to the graphics card @param connection The identifier for the connection to the card ''' cdef extern int blueshift_drm_crtc_count(int connection) ''' Return the number of CRTC:s on the opened card @param connection The identifier for the connection to the card @return The number of CRTC:s on the opened card ''' cdef extern int blueshift_drm_connector_count(int connection) ''' Return the number of connectors on the opened card @param connection The identifier for the connection to the card @return The number of connectors on the opened card ''' cdef extern int blueshift_drm_gamma_size(int connection, int crtc_index) ''' Return the size of the gamma ramps on a CRTC @param connection The identifier for the connection to the card @param crtc_index The index of the CRTC @return The size of the gamma ramps on a CRTC ''' cdef extern int blueshift_drm_get_gamma_ramps(int connection, int crtc_index, int gamma_size, uint16_t* red, uint16_t* green, uint16_t* blue) ''' Get the current gamma ramps of a monitor @param connection The identifier for the connection to the card @param crtc_index The index of the CRTC to read from @param gamma_size The size a gamma ramp @param red Storage location for the red gamma ramp @param green Storage location for the green gamma ramp @param blue Storage location for the blue gamma ramp @return Zero on success ''' cdef extern int blueshift_drm_set_gamma_ramps(int connection, int crtc_index, int gamma_size, uint16_t* red, uint16_t* green, uint16_t* blue) ''' Set the gamma ramps of the of a monitor @param connection The identifier for the connection to the card @param crtc_index The index of the CRTC to read from @param gamma_size The size a gamma ramp @param red The red gamma ramp @param green The green gamma ramp @param blue The blue gamma ramp @return Zero on success ''' cdef extern void blueshift_drm_open_connector(int connection, int connector_index) ''' Acquire information about a connector @param connection The identifier for the connection to the card @param connector_index The index of the connector ''' cdef extern void blueshift_drm_close_connector(int connection, int connector_index) ''' Release information about a connector @param connection The identifier for the connection to the card @param connector_index The index of the connector ''' cdef extern int blueshift_drm_get_width(int connection, int connector_index) ''' Get the physical width the monitor connected to a connector @param connection The identifier for the connection to the card @param connector_index The index of the connector @return The physical width of the monitor in millimetres, 0 if unknown or not connected ''' cdef extern int blueshift_drm_get_height(int connection, int connector_index) ''' Get the physical height the monitor connected to a connector @param connection The identifier for the connection to the card @param connector_index The index of the connector @return The physical height of the monitor in millimetres, 0 if unknown or not connected ''' cdef extern int blueshift_drm_is_connected(int connection, int connector_index) ''' Get whether a monitor is connected to a connector @param connection The identifier for the connection to the card @param connector_index The index of the connector @return 1 if there is a connection, 0 otherwise, -1 if unknown ''' cdef extern int blueshift_drm_get_crtc(int connection, int connector_index) ''' Get the index of the CRTC of the monitor connected to a connector @param connection The identifier for the connection to the card @param connector_index The index of the connector @return The index of the CRTC ''' cdef extern int blueshift_drm_get_connector_type_index(int connection, int connector_index) ''' Get the index of the type of a connector @param connection The identifier for the connection to the card @param connector_index The index of the connector @return The connector type by index, 0 for unknown ''' cdef extern const char* blueshift_drm_get_connector_type_name(int connection, int connector_index) ''' Get the name of the type of a connector @param connection The identifier for the connection to the card @param connector_index The index of the connector @return The connector type by name, "Unknown" if not identifiable, "Unrecognised" if Blueshift does not recognise it. ''' cdef extern long int blueshift_drm_get_edid(int connection, int connector_index, char* edid, long int size, int hexadecimal) ''' Get the extended display identification data for the monitor connected to a connector @param connection The identifier for the connection to the card @param connector_index The index of the connector @param edid Storage location for the EDID, it should be 128 bytes, 256 bytes + zero termination if hex @param size The size allocated to `edid` excluding your zero termination @param hexadecimal Whether to convert to hexadecimal representation, this is preferable @return The length of the found value, 0 if none, as if hex is false ''' cdef uint16_t* r_shared ''' Non-threadsafe storage for the red colour curve to be used in native code ''' cdef uint16_t* g_shared ''' Non-threadsafe storage for the green colour curve to be used in native code ''' cdef uint16_t* b_shared ''' Non-threadsafe storage for the blue colour curve to be used in native code ''' r_shared = NULL g_shared = NULL b_shared = NULL def drm_close(): ''' Free all resources, but you need to close all connections first ''' global r_shared, g_shared, b_shared # Deallocate colour curve storage if r_shared is not NULL: free(r_shared) r_shared = NULL if g_shared is not NULL: free(g_shared) g_shared = NULL if b_shared is not NULL: free(b_shared) b_shared = NULL # Close all native resources blueshift_drm_close() def drm_card_count(): ''' Get the number of cards present on the system @return :int The number of cards present on the system ''' return blueshift_drm_card_count() def drm_open_card(int card_index): ''' Open connection to a graphics card @param card_index The index of the graphics card @return :int -1 on failure, otherwise an identifier for the connection to the card ''' return blueshift_drm_open_card(card_index) def drm_update_card(int connection): ''' Update the resource, required after `blueshift_drm_open_card` @param connection The identifier for the connection to the card ''' blueshift_drm_update_card(connection) def drm_close_card(int connection): ''' Close connection to the graphics card @param connection The identifier for the connection to the card ''' blueshift_drm_close_card(connection) def drm_crtc_count(int connection): ''' Return the number of CRTC:s on the opened card @param connection The identifier for the connection to the card @return :int The number of CRTC:s on the opened card ''' return blueshift_drm_crtc_count(connection) def drm_connector_count(int connection): ''' Return the number of connectors on the opened card @param connection The identifier for the connection to the card @return :int The number of connectors on the opened card ''' return blueshift_drm_connector_count(connection) def drm_gamma_size(int connection, int crtc_index): ''' Return the size of the gamma ramps on a CRTC @param connection The identifier for the connection to the card @param crtc_index The index of the CRTC @return :int The size of the gamma ramps on a CRTC ''' return blueshift_drm_gamma_size(connection, crtc_index) def drm_get_gamma_ramps(int connection, int crtc_index, int gamma_size, threadsafe = False): ''' Get the gamma ramps of the of a monitor @param connection The identifier for the connection to the card @param crtc_index The index of the CRTC to read from @param gamma_size The size a gamma ramp @param threadsafe:bool Whether to decrease memory efficiency and performace so multiple threads can use DRM concurrently @return :(r:list, g:list, b:list)? The current red, green and blue colour curves ''' global r_shared, g_shared, b_shared cdef uint16_t* r cdef uint16_t* g cdef uint16_t* b # If not running in thread-safe mode, if not threadsafe: # allocate the shared storage space for colour curves # if not already allocated. if r_shared is NULL: r_shared = malloc(gamma_size * sizeof(uint16_t)) if g_shared is NULL: g_shared = malloc(gamma_size * sizeof(uint16_t)) if b_shared is NULL: b_shared = malloc(gamma_size * sizeof(uint16_t)) # If not thread-safe use those, otherwise allocate ad-hoc ones r = malloc(gamma_size * sizeof(uint16_t)) if threadsafe else r_shared g = malloc(gamma_size * sizeof(uint16_t)) if threadsafe else g_shared b = malloc(gamma_size * sizeof(uint16_t)) if threadsafe else b_shared # Check for out-of-memory error, both for thread-safe and thread-unsafe if (r is NULL) or (g is NULL) or (b is NULL): raise MemoryError() # Get current curves rc = blueshift_drm_get_gamma_ramps(connection, crtc_index, gamma_size, r, g, b) if rc == 0: # If successful: # Move the C native colour curves to Python data structures rc_r, rc_g, rc_b = [], [], [] for i in range(gamma_size): rc_r.append(r[i]) rc_g.append(g[i]) rc_b.append(b[i]) # Then, if running in thread-safe mode, if threadsafe: # deallocate the ad-hoc curve storage. free(r) free(g) free(b) return (rc_r, rc_g, rc_b) else: # On failure, if threadsafe: # deallocate the ad-hoc curve storage # if running in thread-safe mode. free(r) free(g) free(b) return None def drm_set_gamma_ramps(int connection, crtc_indices, int gamma_size, r_curve, g_curve, b_curve, threadsafe = False): ''' Set the gamma ramps of the of a monitor @param connection The identifier for the connection to the card @param crtc_index:list The index of the CRTC to read from @param gamma_size The size a gamma ramp @param r_curve:list The red gamma ramp @param g_curve:list The green gamma ramp @param b_curve:list The blue gamma ramp @param threadsafe:bool Whether to decrease memory efficiency and performace so multiple threads can use DRM concurrently @return :int Zero on success ''' global r_shared, g_shared, b_shared cdef uint16_t* r cdef uint16_t* g cdef uint16_t* b # If not running in thread-safe mode, if not threadsafe: # allocate the shared storage space for colour curves # if not already allocated. if r_shared is NULL: r_shared = malloc(gamma_size * sizeof(uint16_t)) if g_shared is NULL: g_shared = malloc(gamma_size * sizeof(uint16_t)) if b_shared is NULL: b_shared = malloc(gamma_size * sizeof(uint16_t)) # If not thread-safe use those, otherwise allocate ad-hoc ones r = malloc(gamma_size * sizeof(uint16_t)) if threadsafe else r_shared g = malloc(gamma_size * sizeof(uint16_t)) if threadsafe else g_shared b = malloc(gamma_size * sizeof(uint16_t)) if threadsafe else b_shared # Check for out-of-memory error, both for thread-safe and thread-unsafe if (r is NULL) or (g is NULL) or (b is NULL): raise MemoryError() # Convert the Python colour curves to C native format for i in range(gamma_size): r[i] = r_curve[i] & 0xFFFF g[i] = g_curve[i] & 0xFFFF b[i] = b_curve[i] & 0xFFFF rc = 0 # For each selected CRTC, for crtc_index in crtc_indices: # adjust the colour curves. rc |= blueshift_drm_set_gamma_ramps(connection, crtc_index, gamma_size, r, g, b) if threadsafe: # deallocate the ad-hoc curve storage # if running in thread-safe mode free(r) free(g) free(b) return rc def drm_open_connector(int connection, int connector_index): ''' Acquire information about a connector @param connection The identifier for the connection to the card @param connector_index The index of the connector ''' blueshift_drm_open_connector(connection, connector_index) def drm_close_connector(int connection, int connector_index): ''' Release information about a connector @param connection The identifier for the connection to the card @param connector_index The index of the connector ''' blueshift_drm_close_connector(connection, connector_index) def drm_get_width(int connection, int connector_index): ''' Get the physical width the monitor connected to a connector @param connection The identifier for the connection to the card @param connector_index The index of the connector @return :int The physical width of the monitor in millimetres, 0 if unknown or not connected ''' return blueshift_drm_get_width(connection, connector_index) def drm_get_height(int connection, int connector_index): ''' Get the physical height the monitor connected to a connector @param connection The identifier for the connection to the card @param connector_index The index of the connector @return :int The physical height of the monitor in millimetres, 0 if unknown or not connected ''' return blueshift_drm_get_height(connection, connector_index) def drm_is_connected(int connection, int connector_index): ''' Get whether a monitor is connected to a connector @param connection The identifier for the connection to the card @param connector_index The index of the connector @return :int 1 if there is a connection, 0 otherwise, -1 if unknown ''' return blueshift_drm_is_connected(connection, connector_index) def drm_get_crtc(int connection, int connector_index): ''' Get the index of the CRTC of the monitor connected to a connector @param connection The identifier for the connection to the card @param connector_index The index of the connector @return :int The index of the CRTC ''' return blueshift_drm_get_crtc(connection, connector_index) def drm_get_connector_type_index(int connection, int connector_index): ''' Get the index of the type of a connector @param connection The identifier for the connection to the card @param connector_index The index of the connector @return :int The connector type by index, 0 for unknown ''' return blueshift_drm_get_connector_type_index(connection, connector_index) def drm_get_connector_type_name(int connection, int connector_index): ''' Get the name of the type of a connector @param connection The identifier for the connection to the card @param connector_index The index of the connector @return :str The connector type by name, "Unknown" if not identifiable, "Unrecognised" if Blueshift does not recognise it. ''' return (blueshift_drm_get_connector_type_name(connection, connector_index)).decode('utf-8', 'replace') def drm_get_edid(int connection, int connector_index): ''' Get the extended display identification data for the monitor connected to a connector @param connection The identifier for the connection to the card @param connector_index The index of the connector @return :str? The extended display identification data for the monitor ''' global edid_shared cdef long int size cdef long int got cdef char* edid cdef bytes rc # Prototype side of the hexadecimal representation # of the EDID, should be exact size = 256 # Allocate storage space for the EDID, with one # extra character for NUL-termination edid = malloc((size + 1) * sizeof(char)) # Check for out-of-memory error if edid is NULL: raise MemoryError() # Fill the storage space for the EDID, with the # EDID of the monitor connected to the selected # connector, in hexadecimal, representation. got = blueshift_drm_get_edid(connection, connector_index, edid, size, 1) # If the length of the EDID is zero, if got == 0: # the free the storage space, free(edid) # and return that it failed. return None # But if we got an non-zero length, it is of the # EDID's byte-length, not in hexadecimal representation # that is twice as long. if got * 2 > size: # In if that length is larger than we have anticipated, # update to new size (of the hexadecimal representation), size = got * 2 # and reallocate the storage. edid = realloc(edid, (size + 1) * sizeof(char)) # Check for out-of-memory error if edid is NULL: raise MemoryError() # Get the full EDID. got = blueshift_drm_get_edid(connection, connector_index, edid, size, 1) # Check that we got the EDID. There is an unlikely # race condition where the user can have unplugged # the monitor. if got == 0: # If we did not get an EDID, # free the storage for it, free(edid) # and return that it failed. return None # If we got a large EDID yet, if got * 2 > size: # ignore it because it should happen, # EDID:s are 128 bytes long and the risk that # the use plugged in new monitor that did not # have the same EDID format, is super unlikely. # She would have to pause the program or used # a KVM switch between the two readings. # Instead just truncate the EDID to the size # that we expected; it is not fatal. got = size // 2 # NUL-terminate the EDID, edid[got * 2] = 0 # and convert it to bytes so that we can # later convert it to a Python string, rc = edid # and deallocate the C string. free(edid) # Convert the EDID to a Sython string return rc.decode('utf-8', 'replace')