summaryrefslogblamecommitdiffstats
path: root/src/monitor.py
blob: 91472f459a13f0d03a9d50553f9abc6b2db5f8e9 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12

                      
                                                                      

                                                                      
                                                                      





                                                                   
                                              
  
                                                                   

                                                                       
                            
 

          
                 
                   
                       
 
 
 
                    
 
 









                                                                                                                







                                                                                                   
                                                                
                        
 
 
                                                                 
       






                                                                                        















                                                                                                   

 
                                                    
       
                                                                
    



                                                                                                                
       


                                                             
                                                             
                        
 
                                                      
       
                                                                  
    
                                                                                


                                                                                                                
       


                                                               
                                                               
                          
 
                                                  


                                            



                                                                                                                                              
       


                                                           
                                                           
                      
 








                                                                                                                


                                                              
                                                              
                         
 








                                                                                                                


                                                              
                                                              
                         

 
                                              
       
                                                       
    


                                                                                        
       


                                                         
                                                                           
                    
 
                                                
       
                                                         
    
                                                                                                         

                                                                                     
       


                                                           
                                                                             
                      
 
                                            


                                   



                                                                                        
       


                                                       
                                                                         
                  
 

                                               
                                       




                                                                                             


                                                          
                                                                            
                     
 







                                                                                             


                                                          
                                                                            
                     

 
                                                                      

                               
    

                                         
                                         
                                                          
       


                                                                
                                                 
                                                         
               
                                                          
                                                 
                             
                                
                                 

                                    
                                      

                              
                                 

                                                                    
                                     
                                   
                                     
                             
                                         

                                             
                                    
                      
                                      
                      
                                     
                      
                           
 

 








                                 
 
    











                                                                                                        






                                                
                                                                      







                                                      
                                                                     








                                                                                            
                                                                                  







                                                                             
                                                                            
    






                                                                                       
                                                                     









                                                            
    
                                 





                                            
                                  
    
                       




                                                          
                                 
    
                      




                                            
                                
    
                           




                                                                            
                                     
    
                                       





                                                    
                                  
    
                       




                                                           

                                 
 




















                                                        
                                                                          







                                                      
                                                                         
    
                                              






                                                                                            
                                                                                                       







                                                                             
                                                                                
    






                                                                                       
                                                                         
    


                                                      

                                                            


                                                                                      
 





                                                                          

                                                                                                                                           
                                                                      
                                                               
                                                                                   




                       
                              
                                                                                              




                                                      
                                        
                                                                                                        
                                       
                                                                          

                                                                                                      
 
 
                                                


                                                               
                                                                           
                                                                        
                                                                   

                                                                                       
       





                                                                                                   
                  













                                                           
             
                           
 
 






                                                                                       


                                                                      
                                         
                                 
 





                                                                                   


                                                                    
                                    
                               

                          
       
                                                                             
    

                                                                                   


                                                                       
                                       
                                  



                                                                                  
    

                                                                                   


                                                                       
                                       
                                  





                                                             







                                                                                 
 
#!/usr/bin/env python3

# Copyright © 2014, 2015, 2016, 2017  Mattias Andrée (m@maandree.se)
# 
# 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 <http://www.gnu.org/licenses/>.

# This module is deprecated!

import sys

from aux import *
from curve import *
from output import EDID



cached_displays = {}


def get_gamma(crtc = 0, screen = 0, display = None, *, method = None):
    '''
    Gets the current colour curves
    
    @param   crtc:int      The CRTC of the monitor to read from
    @param   screen:int    The screen to which the monitors belong
    @param   display:str?  The display to which to connect, `None` for current display
    @param   method:str?   The adjustment method
    @return  :()→void      Function to invoke to apply the curves that was used when this function was invoked
    '''
    import output
    if not get_gamma.warned:
        get_gamma.warned = True
        print('get_gamma() is deprecated', file = sys.stderr)
    if (method, display) not in cached_displays:
        cached_displays[(method, display)] = output.get_outputs(method = method, display = display)
    crtc = cached_displays[(method, display)].screens[screen].crtcs[crtc]
    ramps = crtc.get_gamma()
    return ramps_to_function(ramps.red, ramps.green, ramps.blue)
get_gamma.warned = False


def set_gamma(*crtcs, screen = 0, display = None, method = None):
    '''
    Applies colour curves
    
    @param  crtcs:*int    The CRT controllers to use, all are used if none are specified
    @param  screen:int    The screen to which the monitors belong
    @param  display:str?  The display to which to connect, `None` for current display
    @param  method:str?   The adjustment method
    '''
    import output
    if not set_gamma.warned:
        set_gamma.warned = True
        print('set_gamma() is deprecated', file = sys.stderr)
    if (method, display) not in cached_displays:
        cached_displays[(method, display)] = output.get_outputs(method = method, display = display)
    screen = cached_displays[(method, display)].screens[screen]
    ramps = output.Ramps(None, depth = -1, size = i_size)
    ramps.red[:]   = r_curve
    ramps.green[:] = g_curve
    ramps.blue[:]  = b_curve
    for crtc in range(len(screen.crtcs)) if len(crtcs) == 0 else crtcs:
        crtc = screen.crtcs[crtc]
        size = (crtc.red_gamma_size, crtc.green_gamma_size, crtc.blue_gamma_size)
        crtc.set_gamma(ramps.copy(depth = crtc.gamma_depth, size = size))
set_gamma.warned = False


def randr_get(crtc = 0, screen = 0, display = None):
    '''
    Gets the current colour curves using the X11 extension RandR
    
    @param   crtc:int      The CRTC of the monitor to read from
    @param   screen:int    The screen to which the monitors belong
    @param   display:str?  The display to which to connect, `None` for current display
    @return  :()→void      Function to invoke to apply the curves that was used when this function was invoked
    '''
    if not randr_get.warned:
        randr_get.warned = True
        print('randr_get() is deprecated', file = sys.stderr)
    return get_gamma(crtc, screen, display, method = 'randr')
randr_get.warned = False

def vidmode_get(crtc = 0, screen = 0, display = None):
    '''
    Gets the current colour curves using the X11 extension VidMode
    
    @param   crtc:int      The CRTC of the monitor to read from, dummy parameter
    @param   screen:int    The screen to which the monitors belong
    @param   display:str?  The display to which to connect, `None` for current display
    @return  :()→void      Function to invoke to apply the curves that was used when this function was invoked
    '''
    if not vidmode_get.warned:
        vidmode_get.warned = True
        print('vidmode_get() is deprecated', file = sys.stderr)
    return get_gamma(crtc, screen, display, method = 'vidmode')
vidmode_get.warned = False

def drm_get(crtc = 0, screen = 0, display = None):
    '''
    Gets the current colour curves using DRM
    
    @param   crtc:int      The CRTC of the monitor to read from
    @param   screen:int    The graphics card to which the monitors belong, named `screen` for compatibility with `randr_get` and `vidmode_get`
    @param   display:str?  Dummy parameter for compatibility with `randr_get` and `vidmode_get`
    @return  :()→void      Function to invoke to apply the curves that was used when this function was invoked
    '''
    if not drm_get.warned:
        drm_get.warned = True
        print('drm_get() is deprecated', file = sys.stderr)
    return get_gamma(crtc, screen, display, method = 'drm')
drm_get.warned = False

def w32gdi_get(crtc = 0, screen = 0, display = None):
    '''
    Gets the current colour curves using W32 GDI
    
    @param   crtc:int      The CRTC of the monitor to read from
    @param   screen:int    Dummy parameter for compatibility with `randr_get`, `vidmode_get` and `drm_get`
    @param   display:str?  Dummy parameter for compatibility with `randr_get` and `vidmode_get`
    @return  :()→void      Function to invoke to apply the curves that was used when this function was invoked
    '''
    if not w32gdi_get.warned:
        w32gdi_get.warned = True
        print('w32gdi_get() is deprecated', file = sys.stderr)
    return get_gamma(crtc, screen, display, method = 'w32gdi')
w32gdi_get.warned = False

def quartz_get(crtc = 0, screen = 0, display = None):
    '''
    Gets the current colour curves using Quartz
    
    @param   crtc:int      The CRTC of the monitor to read from
    @param   screen:int    Dummy parameter for compatibility with `randr_get`, `vidmode_get` and `drm_get`
    @param   display:str?  Dummy parameter for compatibility with `randr_get` and `vidmode_get`
    @return  :()→void      Function to invoke to apply the curves that was used when this function was invoked
    '''
    if not quartz_get.warned:
        quartz_get.warned = True
        print('quartz_get() is deprecated', file = sys.stderr)
    return get_gamma(crtc, screen, display, method = 'quartz')
quartz_get.warned = False


def randr(*crtcs, screen = 0, display = None):
    '''
    Applies colour curves using the X11 extension RandR
    
    @param  crtcs:*int    The CRT controllers to use, all are used if none are specified
    @param  screen:int    The screen to which the monitors belong
    @param  display:str?  The display to which to connect, `None` for current display
    '''
    if not randr.warned:
        randr.warned = True
        print('randr() is deprecated', file = sys.stderr)
    set_gamma(*crtcs, screen = screen, display = display, method = 'randr')
randr.warned = False

def vidmode(*crtcs, screen = 0, display = None):
    '''
    Applies colour curves using the X11 extension VidMode
    
    @param  crtcs:*int    The CRT controllers to use, all are used if none are specified, dummy parameter
    @param  screen:int    The screen to which the monitors belong
    @param  display:str?  The display to which to connect, `None` for current display
    '''
    if not vidmode.warned:
        vidmode.warned = True
        print('vidmode() is deprecated', file = sys.stderr)
    set_gamma(*crtcs, screen = screen, display = display, method = 'vidmode')
vidmode.warned = False

def drm(*crtcs, screen = 0, display = None):
    '''
    Applies colour curves using DRM
    
    @param  crtcs:*int    The CRT controllers to use, all are used if none are specified
    @param  screen:int    The graphics card to which the monitors belong,
                          named `screen` for compatibility with `randr` and `vidmode`
    @param  display:str?  Dummy parameter for compatibility with `randr` and `vidmode`
    '''
    if not drm.warned:
        drm.warned = True
        print('drm() is deprecated', file = sys.stderr)
    set_gamma(*crtcs, screen = screen, display = display, method = 'drm')
drm.warned = False

def w32gdi(*crtcs, screen = 0, display = None):
    '''
    Applies colour curves using W32 GDI
    
    @param  crtcs:*int    The CRT controllers to use, all are used if none are specified
    @param  screen:int    Dummy parameter for compatibility with `randr`, `vidmode` and `drm`
    @param  display:str?  Dummy parameter for compatibility with `randr` and `vidmode`
    '''
    if not w32gdi.warned:
        w32gdi.warned = True
        print('w32gdi() is deprecated', file = sys.stderr)
    set_gamma(*crtcs, screen = screen, display = display, method = 'w32gdi')
w32gdi.warned = False

def quartz(*crtcs, screen = 0, display = None):
    '''
    Applies colour curves using Quartz
    
    @param  crtcs:*int    The CRT controllers to use, all are used if none are specified
    @param  screen:int    Dummy parameter for compatibility with `randr`, `vidmode` and `drm`
    @param  display:str?  Dummy parameter for compatibility with `randr` and `vidmode`
    '''
    if not quartz.warned:
        quartz.warned = True
        print('quartz() is deprecated', file = sys.stderr)
    set_gamma(*crtcs, screen = screen, display = display, method = 'quartz')
quartz.warned = False


def print_curves(*crtcs, screen = 0, display = None, compact = False):
    '''
    Prints the curves to stdout
    
    @param  crtcs:*int    Dummy parameter
    @param  screen:int    Dummy parameter
    @param  display:str?  Dummy parameter
    @param  compact:bool  Whether to print in compact form
    '''
    if not print_curves.warned:
        print_curves.warned = True
        print('print_curves() is deprecated', file = sys.stderr)
    # Convert curves to [0, 0xFFFF] integer lists
    (R_curve, G_curve, B_curve) = translate_to_integers()
    if compact:
        # Print each colour curve with run-length encoding
        for curve in (R_curve, G_curve, B_curve):
            # Print beginning
            print('[', end = '')
            last, count = None, 0
            for i in range(i_size):
                if curve[i] == last:
                    # Count repetition
                    count += 1
                else:
                    # Print value
                    if last is not None:
                        print('%i {%i}, ' % (last, count), end = '')
                    # Store new value
                    last = curve[i]
                    # Restart counter
                    count = 1
            # Print last value and ending
            print('%i {%i}]' % (last, count))
    else:
        # Print the red colour curve
        print(R_curve)
        # Print the green colour curve
        print(G_curve)
        # Print the blue colour curve
        print(B_curve)
print_curves.warned = False



class Screens:
    '''
    Information about all screens
    '''
    def __init__(self):
        '''
        Constructor
        '''
        self.screens = None

    
    def __find(self, f):
        '''
        Find monitors in each screen
        
        @param   f:(Screen)→list<Output>  Function that for one screen find all desired monitors in it
        @return  :list<Output>            All desired monitors
        '''
        rc = []
        for screen in self.screens:
            rc += f(screen)
        return rc
    
    def find_by_crtc(self, index):
        '''
        Find output by CRTC index
        
        @param   index:int?     The CRTC index
        @return  :list<Output>  Matching outputs
        '''
        return self.__find(lambda screen : screen.find_by_crtc(index))
    
    def find_by_name(self, name):
        '''
        Find output by name
        
        @param   name:str       The name of the output
        @return  :list<Output>  Matching outputs
        '''
        return self.__find(lambda screen : screen.find_by_name(name))
    
    def find_by_size(self, widthmm, heigthmm):
        '''
        Find output by physical size
        
        @param   widthmm:int?   The physical width, measured in millimetres, of the monitor
        @param   heightmm:int?  The physical height, measured in millimetres, of the monitor
        @return  :list<Output>  Matching outputs
        '''
        return self.__find(lambda screen : screen.find_by_size(widthmm, heigthmm))
    
    def find_by_connected(self, status):
        '''
        Find output by connection status
        
        @param   status:bool    Whether the output should be connected or not
        @return  :list<Output>  Matching outputs
        '''
        return self.__find(lambda screen : screen.find_by_connected(status))
    
    def find_by_edid(self, edid):
        '''
        Find output by extended display identification data
        
        @param   edid:str?      The extended display identification data of the monitor
        @return  :list<Output>  Matching outputs
        '''
        return self.__find(lambda screen : screen.find_by_edid(edid))

    
    def __contains__(self, screen):
        '''
        Check if a screen is listed
        
        @param   screen:Screen  The screen
        @return  :bool          Whether the screen is listed
        '''
        return screen in self.screens
    
    def __getitem__(self, index):
        '''
        Get a screen by its index
        
        @param   :int     The screen's index
        @return  :Screen  The screen
        '''
        return self.screens[index]
    
    def __iter__(self):
        '''
        Create an interator of the screens
        
        @return  :itr<Screen>  An interator of the screens
        '''
        return iter(self.screens)
    
    def __len__(self):
        '''
        Get the number of screens
        
        @return  :int  The number of screens
        '''
        return len(self.screens)
    
    def __reversed__(self):
        '''
        Get a reversed iterator of the screens
    
        @return  :itr<Screen>  An interator of the screens in reversed order
        '''
        return reversed(self.screens)
    
    def __setitem__(self, index, item):
        '''
        Replace a screen
        
        @param  index:int    The index of the screen
        @param  item:Screen  The screen
        '''
        self.screens[index] = item
    
    def __repr__(self):
        '''
        Get a string representation of the screens
        
        @return  :str  String representation of the screens
        '''
        return repr(self.screens)


class Screen:
    '''
    Screen information
    
    @variable  crtc_count:int       The number of CRTC:s
    @variable  output:list<Output>  List of outputs
    '''
    def __init__(self):
        '''
        Constructor
        '''
        self.crtc_count = 0
        self.outputs = []
    
    def find_by_crtc(self, index):
        '''
        Find output by CRTC index
        
        @param   index:int?     The CRTC index
        @return  :list<Output>  Matching outputs
        '''
        return [output for output in self.outputs if output.crtc == index]
    
    def find_by_name(self, name):
        '''
        Find output by name
        
        @param   name:str       The name of the output
        @return  :list<Output>  Matching outputs
        '''
        return [output for output in self.outputs if output.name == name]
    
    def find_by_size(self, widthmm, heightmm):
        '''
        Find output by physical size
        
        @param   widthmm:int?   The physical width, measured in millimetres, of the monitor
        @param   heightmm:int?  The physical height, measured in millimetres, of the monitor
        @return  :list<Output>  Matching outputs
        '''
        return [out for out in self.outputs if (out.widthmm == widthmm) and (out.heightmm == heightmm)]
    
    def find_by_connected(self, status):
        '''
        Find output by connection status
        
        @param   status:bool    Whether the output should be connected or not
        @return  :list<Output>  Matching outputs
        '''
        return [output for output in self.outputs if output.connected == status]
    
    def find_by_edid(self, edid):
        '''
        Find output by extended display identification data
        
        @param   edid:str?      The extended display identification data of the monitor
        @return  :list<Output>  Matching outputs
        '''
        return [output for output in self.outputs if output.edid == edid]
    
    def __repr__(self):
        '''
        Return a string representation of the instance
        
        @return  :str  String representation of the instance
        '''
        return '[CRTC count: %i, Outputs: %s]' % (self.crtc_count, repr(self.outputs))


class Output:
    '''
    Output information
    
    @variable  name:str        The name of the output
    @variable  connected:bool  Whether the outout is known to be connected
    @variable  widthmm:int?    The physical width of the monitor, measured in millimetres, `None` if unknown, not defined or not connected
    @variable  heigthmm:int?   The physical height of the monitor, measured in millimetres, `None` if unknown, not defined or not connected
    @variable  crtc:int?       The CRTC index, `None` if not connected
    @variable  screen:int?     The screen index, `None` if none
    @variable  edid:str?       Extended display identification data, `None` if none
    '''
    def __init__(self):
        '''
        Constructor
        '''
        self.connected = False
        self.name, self.widthmm, self.heightmm, self.crtc, self.screen, self.edid = [None] * 6
    
    def __repr__(self):
        '''
        Return a string representation of the instance
        '''
        # Select the order of the values
        rc = [self.name, self.connected, self.widthmm, self.heightmm, self.crtc, self.screen, self.edid]
        # Convert the values to strings
        rc = tuple(rc[:1] + [repr(x) for x in rc[1 : -1]] + [str(rc[-1])])
        # Combine the values
        return '[Name: %s, Connected: %s, Width: %s, Height: %s, CRTC: %s, Screen: %s, EDID: %s]' % rc


def list_screens(method = None, display = None):
    '''
    Retrieve informantion about all screens, outputs and CRTC:s
    
    @param   method:str?   The listing method: 'randr' for RandR (under X),
                                               'drm' for DRM (under TTY)
                                               `None` for automatic
    @param   display:str?  The display to use, `None` for the current one
    @return  :Screens      An instance of a datastructure with the relevant information
    '''
    import output
    if not list_screens.warned:
        list_screens.warned = True
        print('list_screens() is deprecated', file = sys.stderr)
    if (method, display) not in cached_displays:
        cached_displays[(method, display)] = output.get_outputs(method = method, display = display)
    rc = Screens()
    rc.screens = cached_displays[(method, display)].screens
    for screen_i, screen in enumerate(rc.screens):
        screen.crtc_count = len(screen.crtcs)
        screen.outputs = [None] * screen.crtc_count
        for crtc_i in range(screen.crtc_count):
            screen.outputs[crtc_i] = output = Output()
            output.screen = screen_i
            output.crtc = crtc_i
            crtc = screen.crtcs[crtc_i]
            output.name = crtc.connector_name
            output.connected = crtc.active
            output.widthmm = crtc.width_mm
            output.heightmm = crtc.height_mm
            output.edid = crtc.edid
    return rc
list_screens.warned = False


def list_screens_randr(display = None):
    '''
    Retrieve informantion about all screens, outputs and CRTC:s, using RandR
    
    @param   display:str?  The display to use, `None` for the current one
    @return  :Screens      An instance of a datastructure with the relevant information
    '''
    if not list_screens_randr.warned:
        list_screens_randr.warned = True
        print('list_screens_randr() is deprecated', file = sys.stderr)
    return list_screens('randr', display)
list_screens_randr.warned = False

def list_screens_drm():
    '''
    Retrieve informantion about all screens, outputs and CRTC:s, using DRM
    
    @return  :Screens  An instance of a datastructure with the relevant information
    '''
    if not list_screens_drm.warned:
        list_screens_drm.warned = True
        print('list_screens_drm() is deprecated', file = sys.stderr)
    return list_screens('drm', None)
list_screens_drm.warned = False

def list_screens_quartz():
    '''
    Retrieve informantion about all screens, outputs and CRTC:s, using Quartz
    
    @return  :Screens  An instance of a datastructure with the relevant information
    '''
    if not list_screens_quartz.warned:
        list_screens_quartz.warned = True
        print('list_screens_quartz() is deprecated', file = sys.stderr)
    return list_screens('quartz', None)
list_screens_quartz.warned = False

def list_screens_w32gdi():
    '''
    Retrieve informantion about all screens, outputs and CRTC:s, using Windows GDI
    
    @return  :Screens  An instance of a datastructure with the relevant information
    '''
    if not list_screens_w32gdi.warned:
        list_screens_w32gdi.warned = True
        print('list_screens_w32gdi() is deprecated', file = sys.stderr)
    return list_screens('w32gdi', None)
list_screens_w32gdi.warned = False


def quartz_restore():
    '''
    Restore all CRTC:s' gamma ramps the settings in ColorSync
    '''
    import output
    if not quartz_restore.warned:
        quartz_restore.warned = True
        print('quartz_restore() is deprecated', file = sys.stderr)
    if ('quartz', None) not in cached_displays:
        cached_displays[('quartz', None)] = output.get_outputs(method = 'quartz')
    cached_displays[('quartz', None)].restore()
quartz_restore.warned = False