aboutsummaryrefslogblamecommitdiffstats
path: root/libcoopgamma.pyx
blob: 57a53a419a0418915cfff540a31bab1023ad0242 (plain) (tree)
1
2
3
4
5
6
7
8
9
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
                
                                                     


                               
                                             


                                                                                 
                    






















                                                                      
                         
                                             
                           
                                               
                          
                                              
                   
                      
                     
                        
                    



























                                                                           



                                                       
                                                       








































                                                                                        






























                                                                         
































































                                                                                            

 
                                                                                              









                                                                               
                                                                             









                                                                             
                                                                                 






                                         
                                                                               









                                                                               
                                                                                       






                                           
                                                                                     









                                                                                  
                                                                                             






                                           
                                                                                           









                                                                                     
                                                                                                 






                                            
                                                                                               









                                                                                       
                                                                                             






                                           
                                                                                           









                                                                                     
                                                                                           









                                                                                     
                                                                               






                                           
                                                                             









                                                                              
                                                                                   






                                           
                                                                                                 










                                                                                
                                                                                                    









                                                                   
                                                                                                              









                                                                                          
                                                                                               






                                           
                                                                                             









                                                                                      
                                                                                                                










                                                                                        
                                                                                      









                                                                                          
                                                   










                                                                                      
                                                                                    

















                                                                    
                                                                                       











                                                                        
                                                                                          













                                                                        
                                                                                                             













                                                                       
                                                                                                















                                                                 
                                                                     




                                                         
                                                            







                                                                                                            
                                                                          



















                                                                                
                                                                             





                                                               
                                                                                                                       




                                              



                                                                    

   
                                                                                                                          


                                                






                                                                            

   
                                                                                 


















                                                                                               
                                                                                               




                                                                  




                                                                    


                                                                                                             
                                                                                               


                                                                      




                                                                                 


                                                                                                  
                                                                                   

















                                                                                     
                                                                                                                       




                                                              




                                                                    


                                                                                                            
                                                                                          


                                                                  




                                                                                 


                                                                                     
                                                                                                                  
















                                                                             
                                                                                                            
                                                                                          




                                                                   




                                                                                                      

   
                                                                                                                       


                                                                       



                                                                                 

   
                                                                                                                  









                                                                     



                                                                                                  

   

 
                                       












                                                                                             





                                                    
                  

                     
              

 
                                                                       













                                                                                          

                             



                               
        
                              
                                                     



                                                            
                                                   


                                                          
                                   
                                                                                      







                                                     

                     

                       

 
                                                                










                                                                                     




                             
                              
                                                     



                                                            
                                                   


                                                          
                                                        
                                          

                                               



                     


                     
                                                                   











                                                                                                 




                             
                              
                                                     



                                                            
                                                   


                                                          
                                                           
                                          

                                               



                     


                     
                                          







                                                                                    
                                                                          
                    








                                                  
                                                     









                                                                                         
                                                        











                                                                                         

                                               
                                                                              

                 

 
                                                        










                                                                                            
                      
                                     
                        
                   
                                                                             
                    
                               

                                                     

                                                   
                      

                                   




                                                                
            


                                                      
                

 
                                                                 






                                                                                         
                

 
                                                







                                                                                    
                                                                          
                    








                                                  
                                                           









                                                                                                     
                                                              









                                                                                                     



                                                     
                                                                             

                 


              
                                                              










                                                                                            
                      
                                           
                        
                      






                                                                                      
                      

                                   




                                                                      
            


                                                    
                
 
 
                                                                          






                                                            





                                                                                         
       

                             
                                                                                        
















                                                            

 
                                                                            




















                                                                                         
                                              




                                                             
                                                                











                                                                                        
                                                                    









                                                                                          
                                           
                            
                                                                                                      



                                      

                                                                               
                                                                                     
                                      
            
                   


                                
                                                     






                                                                                   
 
                                                                        


                                                


                                                                                      

                                                                                        
                                                                                                       




                                                  
                                                                        



                                                                                       
                                                                              



                                                                                        
                                                                                                       



                                                              







                                                    


              
                                                       














                                                                                        
                                                        
                 









                                                    


              
                                                                                         




                                                                      




                                                                                      

                                                                                        
                                                                                                       
                           
        
                                              





                                                                  

                             
                   


            
                                                                             



                                                                                              
                                                                                   




                                                                                                             
                                                                                                       
                                      
                 




                                                                  

                                                 
                                                   
                                                                                                           








                                                                                                          


              
                                                                        













                                                                                                             
                           
                                                                                        
                                      
                 


                                                        
                                                  



                                                                   
                       

                                                 
                                                   
                                                                                                           








                                                                                                          
                   


              
                                                                               





                                                                  




                                                                                      

                                                                                        
                                                                                                       
                                        
                                                     



                                                         





                                                            
                             
            
                      


            
                                                                        



                                                                                             
                                                                                   




                                                                                                             
                                                                                                       
                                          

                                     
                     


                                                            
                                                              
                       

                                                 
                                                   
                                                                                                           

                                                      


                                                    


                                                    
                                     





                                                                 
                                       





                                                                
                                      





                                                                  
                                       





                                                                   
                                       





                                                                   
                                       





                                                                   





                                                                            
                                                                                           
                                                     

                                                 


              
                                                              
















                                                                                                             

                                     
                     
                                                     



                                                         









                                                            
                                                                  
                           

                                                     
                                                       
                                                                                                               

                                                          


                                                        


                                                        
                                         





                                                                     
                                           





                                                                    
                                          





                                                                      
                                           





                                                                       
                                           





                                                                       
                                           





                                                                       





                                                                                
                                                                                               
                                                         


                                                     
                      


              
                                                                                            












































































































                                                                                          
                                                                               




                                                                       





                                                                                               

                                                                                        
                                                                                                       


                                                       
                                                                             
                                                            
                                                                                 
                     
                   





                                                                                     
        
                                   
                                                                                                  
                                                             











                                                               

                        
                             


            
                                                                        


                                                                           




                                                                                                      

                                                                                        
                                                                                                       

                                                  
                   

                                             
                                               
                                                                                               


               
                                                              
















                                                                                                     
                 

                                                       
                                                                             
                                                            
                                                                                 
                     
                   





                                                                                     
        
                                   
                                                                                                  
                                                             



                                                               
                             



                                              
                                                      
                       

                                                 
                                                   
                                                                                                   
            

                        
                             

               






























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































                                                                                                                 
# -*- python -*-
# See LICENSE file for copyright and license details.
cimport cython

from libc.stddef cimport size_t
from libc.stdlib cimport calloc, malloc, free
from libc.stdint cimport int64_t, uint8_t, uint16_t, uint32_t, uint64_t, intptr_t
from libc.errno cimport errno

import enum as _enum


ctypedef int libcoopgamma_support_t
# Values used to indicate the support for gamma adjustments

ctypedef int libcoopgamma_depth_t
# Values used to tell which datatype is used for the gamma ramp stops
# 
# The values will always be the number of bits for integral types, and
# negative for floating-point types

ctypedef int libcoopgamma_lifespan_t
# Values used to tell when a filter should be removed

ctypedef int libcoopgamma_colourspace_t
# Colourspaces



cdef extern from "include-libcoopgamma.h":

    ctypedef struct libcoopgamma_ramps_t:
    # Gamma ramp structure
        size_t u_red_size
        # The number of stops in the red ramp
        size_t u_green_size
        # The number of stops in the green ramp
        size_t u_blue_size
        # The number of stops in the blue ramp
        void* u_red
        # The red ramp
        void* u_green
        # The green ramp
        void* u_blue
        # The blue ramp

    ctypedef struct libcoopgamma_filter_t:
    # Data set to the coopgamma server to apply, update, or remove a filter
        int64_t priority
        # The priority of the filter, higher priority is applied first.
        # The gamma correction should have priority 0.
        char* crtc
        # The CRTC for which this filter shall be applied
        char* fclass
        # Identifier for the filter
        # 
        # The syntax must be "${PACKAGE_NAME}::${COMMAND_NAME}::${RULE}"
        libcoopgamma_lifespan_t lifespan
        # When shall the filter be removed?
        # 
        # If this member's value is `LIBCOOPGAMMA_REMOVE`,
        # only `.crtc` and `.class` need also be defined
        libcoopgamma_depth_t depth
        # The data type and bit-depth of the ramp stops
        libcoopgamma_ramps_t ramps
        # The gamma ramp adjustments of the filter

    ctypedef struct libcoopgamma_crtc_info_t:
    # Gamma ramp meta information for a CRTC
        int cooperative
        # Is cooperative gamma server running?
        libcoopgamma_depth_t depth
        # Is gamma adjustments supported on the CRTC?
        # If not, `.depth`, `.red_size`, `.green_size`,
        # and `.blue_size` are undefined 
        libcoopgamma_support_t supported
        # The data type and bit-depth of the ramp stops
        size_t red_size
        # The number of stops in the red ramp
        size_t green_size
        # The number of stops in the green ramp
        size_t blue_size
        # The number of stops in the blue ramp
        libcoopgamma_colourspace_t colourspace
        # The monitor's colurspace
        int have_gamut
        # Whether `.red_x`, `.red_y`, `.green_x`, `.green_y`, `.blue_x`, `.blue_y`,
        # `.white_x`, and `.white_y` are set.
        # 
        # If this is true, but the colourspace is not RGB (or sRGB),
        # there is something wrong. Please also check the colourspace.
        unsigned red_x
        # The x-value (CIE xyY) of the monitor's red colour, multiplied by 1024
        unsigned red_y
        # The y-value (CIE xyY) of the monitor's red colour, multiplied by 1024
        unsigned green_x
        # The x-value (CIE xyY) of the monitor's green colour, multiplied by 1024
        unsigned green_y
        # The y-value (CIE xyY) of the monitor's green colour, multiplied by 1024
        unsigned blue_x
        # The x-value (CIE xyY) of the monitor's blue colour, multiplied by 1024
        unsigned blue_y
        # The y-value (CIE xyY) of the monitor's blue colour, multiplied by 1024
        unsigned white_x
        # The x-value (CIE xyY) of the monitor's default white point, multiplied by 1024
        unsigned white_y
        # The y-value (CIE xyY) of the monitor's default white point, multiplied by 1024

    ctypedef struct libcoopgamma_filter_query_t:
    # Data sent to the coopgamma server when requestng the current filter table
        int64_t high_priority
        # Do no return filters with higher priority than this value
        int64_t low_priority
        # Do no return filters with lower priority than this value
        char* crtc
        # The CRTC for which the the current filters shall returned
        int coalesce
        # Whether to coalesce all filters into one gamma ramp triplet

    ctypedef struct libcoopgamma_queried_filter_t:
    # Stripped down version of `libcoopgamma_filter` which only contains
    # the information returned in response to "Command: get-gamma"
        int64_t priority
        # The filter's priority
        char* fclass
        # The filter's class
        libcoopgamma_ramps_t ramps
        # The gamma ramp adjustments of the filter

    ctypedef struct libcoopgamma_filter_table_t:
    # Response type for "Command: get-gamma": a list of applied filters
    # and meta-information that was necessary for decoding the response
        size_t red_size
        # The number of stops in the red ramp
        size_t green_size
        # The number of stops in the green ramp
        size_t blue_size
        # The number of stops in the blue ramp
        size_t filter_count
        # The number of filters
        libcoopgamma_queried_filter_t* filters
        # The filters, should be ordered by priority in descending order,
        # lest there is something wrong with the coopgamma server
        # 
        # If filter coalition was requested, there will be exactly one
        # filter (`.filter_count == 1`) and `.filters->class == NULL`
        # and `.filters->priority` is undefined.
        libcoopgamma_depth_t depth
        # The data type and bit-depth of the ramp stops

    ctypedef struct libcoopgamma_error_t:
    # Error message from coopgamma server
        uint64_t number
        # Error code
        # 
        # If `.custom` is false, 0 indicates success, otherwise,
        # 0 indicates that no error code has been assigned
        int custom
        # Is this a custom error?
        int server_side
        # Did the error occur on the server-side?
        char* description
        # Error message, can be `NULL` if `.custom` is false

    ctypedef struct libcoopgamma_context_t:
    # Library state
    # 
    # Use of this structure is not thread-safe create one instance
    # per thread that uses this structure
        libcoopgamma_error_t error
        # The error of the last failed function call
        # 
        # This member is undefined after successful function call
        int fd
        # File descriptor for the socket
        int have_all_headers
        # Whether `libcoopgamma_synchronise` have read the empty end-of-headers line
        int bad_message
        # Whether `libcoopgamma_synchronise` is reading a corrupt but recoverable message
        int blocking
        # Is communication blocking?
        uint32_t message_id
        # Message ID of the next message
        uint32_t in_response_to
        # The ID of outbound message to which the inbound message being read by
        # `libcoopgamma_synchronise` is a response
        char* outbound
        # Buffer with the outbound message
        size_t outbound_head
        # The write head for `outbound`
        size_t outbound_tail
        # The read head for `outbound`
        size_t outbound_size
        # The allocation size of `outbound`
        char* inbound
        # Buffer with the inbound message
        size_t inbound_head
        # The write head for `inbound`
        size_t inbound_tail
        # The read head for `inbound`
        size_t inbound_size
        # The allocation size of `inbound`
        size_t length
        # The value of 'Length' header in the inbound message
        size_t curline
        # The beginning of the current line that is being read by `libcoopgamma_synchronise`

    ctypedef struct libcoopgamma_async_context_t:
    # Information necessary to identify and parse a response from the server
        uint32_t message_id
        # The value of the 'In response to' header in the waited message
        int coalesce
        # Whether to coalesce all filters into one gamma ramp triplet



cdef extern int libcoopgamma_ramps_initialise_(libcoopgamma_ramps_t* this, size_t width) nogil
'''
Initialise a `libcoopgamma_ramps_t`

`this->red_size`, `this->green_size`, and `this->blue_size` must already be set

@param   this   The record to initialise
@param   width  The `sizeof(*(this->red))`
@return         Zero on success, -1 on error
'''

cdef extern void libcoopgamma_ramps_destroy(libcoopgamma_ramps_t* this) nogil
'''
Release all resources allocated to  a `libcoopgamma_ramps_t`,
the allocation of the record itself is not freed

Always call this function after failed call to `libcoopgamma_ramps_initialise
or failed call to `libcoopgamma_ramps_unmarshal

@param  this  The record to destroy
'''

cdef extern int libcoopgamma_filter_initialise(libcoopgamma_filter_t* this) nogil
'''
Initialise a `libcoopgamma_filter_t`

@param   this  The record to initialise
@return        Zero on success, -1 on err
'''

cdef extern void libcoopgamma_filter_destroy(libcoopgamma_filter_t* this) nogil
'''
Release all resources allocated to  a `libcoopgamma_filter_t`,
the allocation of the record itself is not freed

Always call this function after failed call to `libcoopgamma_filter_initialise`
or failed call to `libcoopgamma_filter_unmarshal`

@param  this  The record to destroy
'''

cdef extern int libcoopgamma_crtc_info_initialise(libcoopgamma_crtc_info_t* this) nogil
'''
Initialise a `libcoopgamma_crtc_info_t`

@param   this  The record to initialise
@return        Zero on success, -1 on error
'''

cdef extern void libcoopgamma_crtc_info_destroy(libcoopgamma_crtc_info_t* this) nogil
'''
Release all resources allocated to  a `libcoopgamma_crtc_info_t`,
the allocation of the record itself is not freed

Always call this function after failed call to `libcoopgamma_crtc_info_initialise`
or failed call to `libcoopgamma_crtc_info_unmarshal

@param  this  The record to destroy
'''

cdef extern int libcoopgamma_filter_query_initialise(libcoopgamma_filter_query_t* this) nogil
'''
Initialise a `libcoopgamma_filter_query_t`

@param   this  The record to initialise
@return        Zero on success, -1 on error
'''

cdef extern void libcoopgamma_filter_query_destroy(libcoopgamma_filter_query_t* this) nogil
'''
Release all resources allocated to  a `libcoopgamma_filter_query_t`,
the allocation of the record itself is not freed

Always call this function after failed call to `libcoopgamma_filter_query_initialise`
or failed call to `libcoopgamma_filter_query_unmarshal`

@param  this  The record to destroy
'''

cdef extern int libcoopgamma_queried_filter_initialise(libcoopgamma_queried_filter_t* this) nogil
'''
Initialise a `libcoopgamma_queried_filter_t`

@param   this  The record to initialise
@return        Zero on success, -1 on error
'''

cdef extern void libcoopgamma_queried_filter_destroy(libcoopgamma_queried_filter_t* this) nogil
'''
Release all resources allocated to  a `libcoopgamma_queried_filter_t`,
the allocation of the record itself is not freed

Always call this function after failed call to `libcoopgamma_queried_filter_initialise`
or failed call to `libcoopgamma_queried_filter_unmarshal

@param  this  The record to destro
'''

cdef extern int libcoopgamma_filter_table_initialise(libcoopgamma_filter_table_t* this) nogil
'''
Initialise a `libcoopgamma_filter_table_t

@param   this  The record to initialise
@return        Zero on success, -1 on error
'''

cdef extern void libcoopgamma_filter_table_destroy(libcoopgamma_filter_table_t* this) nogil
'''
Release all resources allocated to  a `libcoopgamma_filter_table_t`,
the allocation of the record itself is not freed

Always call this function after failed call to `libcoopgamma_filter_table_initialise`
or failed call to `libcoopgamma_filter_table_unmarshal`

@param  this  The record to destroy
'''

cdef extern void libcoopgamma_filter_table_destroy(libcoopgamma_filter_table_t* this) nogil
'''
Release all resources allocated to  a `libcoopgamma_filter_table_t`,
the allocation of the record itself is not freed

Always call this function after failed call to `libcoopgamma_filter_table_initialise`
or failed call to `libcoopgamma_filter_table_unmarshal`

@param  this  The record to destroy
'''

cdef extern int libcoopgamma_error_initialise(libcoopgamma_error_t* this) nogil
'''
Initialise a `libcoopgamma_error_t`

@param   this  The record to initialise
@return        Zero on success, -1 on error
'''

cdef extern void libcoopgamma_error_destroy(libcoopgamma_error_t* this) nogil
'''
Release all resources allocated to  a `libcoopgamma_error_t`,
the allocation of the record itself is not freed

Always call this function after failed call to `libcoopgamma_error_initialise`
or failed call to `libcoopgamma_error_unmarshal`

@param  this  The record to destroy
'''

cdef extern int libcoopgamma_context_initialise(libcoopgamma_context_t* this) nogil
'''
Initialise a `libcoopgamma_context_t`

@param   this  The record to initialise
@return        Zero on success, -1 on error
'''

cdef extern void libcoopgamma_context_destroy(libcoopgamma_context_t* this, int disconnect) nogil
'''
Release all resources allocated to  a `libcoopgamma_context_t`,
the allocation of the record itself is not freed

Always call this function after failed call to `libcoopgamma_context_initialise`
or failed call to `libcoopgamma_context_unmarshal`

@param  this        The record to destroy
@param  disconnect  Disconnect from the server?
'''

cdef extern size_t libcoopgamma_context_marshal(const libcoopgamma_context_t* this, void* buf) nogil
'''
Marshal a `libcoopgamma_context_t` into a buffer

@param   this  The record to marshal
@param   buf   The output buffer, `NULL` to only measure
               how large this buffer has to be
@return        The number of marshalled bytes, or if `buf == NULL`,
               how many bytes would be marshalled if `buf != NULL`
'''

cdef extern int libcoopgamma_context_unmarshal(libcoopgamma_context_t* this, const void* buf, size_t* n) nogil
'''
Unmarshal a `libcoopgamma_context_t` from a buffer

@param   this  The output parameter for unmarshalled record
@param   buf   The buffer with the marshalled record
@param   n     Output parameter for the number of unmarshalled bytes, undefined on failure
@return        `LIBCOOPGAMMA_SUCCESS` (0), `LIBCOOPGAMMA_INCOMPATIBLE_DOWNGRADE`,
               `LIBCOOPGAMMA_INCOMPATIBLE_UPGRADE`, or `LIBCOOPGAMMA_ERRNO_SET`
'''

cdef extern int libcoopgamma_async_context_initialise(libcoopgamma_async_context_t* this) nogil
'''
Initialise a `libcoopgamma_async_context_t`

@param   this  The record to initialise
@return        Zero on success, -1 on error
'''

cdef extern void libcoopgamma_async_context_destroy(libcoopgamma_async_context_t* this) nogil
'''
Release all resources allocated to  a `libcoopgamma_async_context_t`,
the allocation of the record itself is not freed

Always call this function after failed call to `libcoopgamma_async_context_initialise`
or failed call to `libcoopgamma_async_context_unmarshal`

@param  this  The record to destroy
'''

cdef extern size_t libcoopgamma_async_context_marshal(const libcoopgamma_async_context_t* this, void* buf) nogil
'''
Marshal a `libcoopgamma_async_context_t` into a buffer

@param   this  The record to marshal
@param   buf   The output buffer, `NULL` to only measure
               how large this buffer has to be
@return        The number of marshalled bytes, or if `buf == NULL`,
               how many bytes would be marshalled if `buf != NULL`
'''

cdef extern int libcoopgamma_async_context_unmarshal(libcoopgamma_async_context_t* this,
                                                     const void* buf, size_t* n) nogil
'''
Unmarshal a `libcoopgamma_async_context_t` from a buffer

@param   this  The output parameter for unmarshalled record
@param   buf   The buffer with the marshalled record
@param   n     Output parameter for the number of unmarshalled bytes, undefined on failure
@return        `LIBCOOPGAMMA_SUCCESS` (0), `LIBCOOPGAMMA_INCOMPATIBLE_DOWNGRADE`,
               `LIBCOOPGAMMA_INCOMPATIBLE_UPGRADE`, or `LIBCOOPGAMMA_ERRNO_SET`
'''

cdef extern char** libcoopgamma_get_methods() nogil
'''
List all recognised adjustment method

SIGCHLD must not be ignored or blocked

@return  A `NULL`-terminated list of names. You should only free
         the outer pointer, inner pointers are subpointers of the
         outer pointer and cannot be freed. `NULL` on error.
'''

cdef extern int libcoopgamma_get_method_and_site(const char* method, const char* site,
                                                 char** methodp, char** sitep) nogil
'''
Get the adjustment method and site

SIGCHLD must not be ignored or blocked

@param   method   The adjustment method, `NULL` for automatic
@param   site     The site, `NULL` for automatic
@param   methodp  Output pointer for the selected adjustment method,
                  which cannot be `NULL`. It is safe to call
                  this function with this parameter set to `NULL`.
@param   sitep    Output pointer for the selected site, which will
                  be `NULL` the method only supports one site or if
                  `site == NULL` and no site can be selected
                  automatically. It is safe to call this function
                  with this parameter set to `NULL`.
@return           Zero on success, -1 on error
'''

cdef extern char* libcoopgamma_get_pid_file(const char* method, const char* site) nogil
'''
Get the PID file of the coopgamma server

SIGCHLD must not be ignored or blocked

@param   method   The adjustment method, `NULL` for automatic
@param   site     The site, `NULL` for automatic
@return           The pathname of the server's PID file, `NULL` on error
                  or if there server does not use PID files. The later
                  case is detected by checking that `errno` is set to 0.
'''

cdef extern char* libcoopgamma_get_socket_file(const char* method, const char* site) nogil
'''
Get the socket file of the coopgamma server

SIGCHLD must not be ignored or blocked

@param   method   The adjustment method, `NULL` for automatic
@param   site     The site, `NULL` for automatic
@return           The pathname of the server's socket, `NULL` on error
                  or if there server does have its own socket. The later
                  case is detected by checking that `errno` is set to 0,
                  and is the case when communicating with a server in a
                  multi-server display server like mds.
'''

cdef extern int libcoopgamma_connect(const char* method, const char* site, libcoopgamma_context_t* ctx) nogil
'''
Connect to a coopgamma server, and start it if necessary

Use `libcoopgamma_context_destroy` to disconnect

SIGCHLD must not be ignored or blocked

@param   method  The adjustment method, `NULL` for automatic
@param   site    The site, `NULL` for automatic
@param   ctx     The state of the library, must be initialised
@return          Zero on success, -1 on error. On error, `errno` is set
                 to 0 if the server could not be initialised.
'''

cdef extern int libcoopgamma_set_nonblocking(libcoopgamma_context_t* ctx, int nonblocking) nogil
'''
By default communication is blocking, this function
can be used to switch between blocking and nonblocking

After setting the communication to nonblocking,
`libcoopgamma_flush`, `libcoopgamma_synchronise` and
and request-sending functions can fail with EAGAIN and
EWOULDBLOCK. It is safe to continue with `libcoopgamma_flush`
(for `libcoopgamma_flush` it selfand equest-sending functions)
or `libcoopgamma_synchronise` just like EINTR failure.

@param   ctx          The state of the library, must be connected
@param   nonblocking  Nonblocking mode?
@return               Zero on success, -1 on error
'''

cdef extern int libcoopgamma_flush(libcoopgamma_context_t* ctx) nogil
'''
Send all pending outbound data

If this function or another function that sends a request
to the server fails with EINTR, call this function to
complete the transfer. The `async_ctx` parameter will always
be in a properly configured state if a function fails
with EINTR.

@param   ctx  The state of the library, must be connected
@return       Zero on success, -1 on error
'''

cdef extern int libcoopgamma_synchronise(libcoopgamma_context_t* ctx, libcoopgamma_async_context_t* pending,
                                         size_t n, size_t* selected) nogil
'''
Wait for the next message to be received

@param   ctx       The state of the library, must be connected
@param   pending   Information for each pending request
@param   n         The number of elements in `pending`
@param   selected  The index of the element in `pending` which corresponds
                   to the first inbound message, note that this only means
                   that the message is not for any of the other request,
                   if the message is corrupt any of the listed requests can
                   be selected even if it is not for any of the requests.
                   Functions that parse the message will detect such corruption.
@return            Zero on success, -1 on error. If the the message is ignored,
                   which happens if corresponding `libcoopgamma_async_context_t`
                   is not listed, -1 is returned and `errno` is set to 0. If -1
                   is returned, `errno` is set to `ENOTRECOVERABLE` you have
                   received a corrupt message and the context has been tainted
                   beyond recover.
'''

cdef extern void libcoopgamma_skip_message(libcoopgamma_context_t* ctx) nogil
'''
Tell the library that you will not be parsing a receive message

@param  ctx  The state of the library, must be connected
'''

cdef extern int libcoopgamma_get_crtcs_send(libcoopgamma_context_t* ctx, libcoopgamma_async_context_t* async_ctx) nogil
'''
List all available CRTC:s, send request part

Cannot be used before connecting to the server

@param   ctx        The state of the library, must be connected
@param   async_ctx  Information about the request, that is needed to
                    identify and parse the response, is stored here
@return             Zero on success, -1 on error
'''

cdef extern char** libcoopgamma_get_crtcs_recv(libcoopgamma_context_t* ctx, libcoopgamma_async_context_t* async_ctx) nogil
'''
List all available CRTC:s, receive response part

@param   ctx        The state of the library, must be connected
@param   async_ctx  Information about the request
@return             A `NULL`-terminated list of names. You should only free
                    the outer pointer, inner pointers are subpointers of the
                    outer pointer and cannot be freed. `NULL` on error, in
                    which case `ctx->error` (rather than `errno`) is read
                    for information about the error.
'''

cdef extern char** libcoopgamma_get_crtcs_sync(libcoopgamma_context_t* ctx) nogil
'''
List all available CRTC:s, synchronous version

This is a synchronous request function, as such,
you have to ensure that communication is blocking
(default), and that there are not asynchronous
requests waiting, it also means that EINTR:s are
silently ignored and there no wait to cancel the
operation without disconnection from the server

@param   ctx  The state of the library, must be connected
@return       A `NULL`-terminated list of names. You should only free
              the outer pointer, inner pointers are subpointers of the
              outer pointer and cannot be freed. `NULL` on error, in
              which case `ctx->error` (rather than `errno`) is read
              for information about the error.
'''

cdef extern int libcoopgamma_get_gamma_info_send(const char* crtc, libcoopgamma_context_t* ctx,
                                                 libcoopgamma_async_context_t* async_ctx) nogil
'''
Retrieve information about a CRTC:s gamma ramps, send request part

Cannot be used before connecting to the server

@param   crtc       The name of the CRTC
@param   ctx        The state of the library, must be connected
@param   async_ctx  Information about the request, that is needed to
                    identify and parse the response, is stored here
@return             Zero on success, -1 on error
'''

cdef extern int libcoopgamma_get_gamma_info_recv(libcoopgamma_crtc_info_t* info, libcoopgamma_context_t* ctx,
                                                 libcoopgamma_async_context_t* async_ctx) nogil
'''
Retrieve information about a CRTC:s gamma ramps, receive response part

@param   info       Output parameter for the information, must be initialised
@param   ctx        The state of the library, must be connected
@param   async_ctx  Information about the request
@return             Zero on success, -1 on error, in which case `ctx->error`
                    (rather than `errno`) is read for information about the error
'''

cdef extern int libcoopgamma_get_gamma_info_sync(const char* crtc, libcoopgamma_crtc_info_t* info,
                                                 libcoopgamma_context_t* ctx) nogil
'''
Retrieve information about a CRTC:s gamma ramps, synchronous version

This is a synchronous request function, as such,
you have to ensure that communication is blocking
(default), and that there are not asynchronous
requests waiting, it also means that EINTR:s are
silently ignored and there no wait to cancel the
operation without disconnection from the server

@param   crtc   The name of the CRTC
@param   info   Output parameter for the information, must be initialised
@param   ctx    The state of the library, must be connected
@return         Zero on success, -1 on error, in which case `ctx->error`
                (rather than `errno`) is read for information about the error
'''

cdef extern int libcoopgamma_get_gamma_send(const libcoopgamma_filter_query_t* query,
                                            libcoopgamma_context_t* ctx, libcoopgamma_async_context_t* async_ctx) nogil
'''
Retrieve the current gamma ramp adjustments, send request part

Cannot be used before connecting to the server

@param   query      The query to send
@param   ctx        The state of the library, must be connected
@param   async_ctx  Information about the request, that is needed to
                    identify and parse the response, is stored here
@return             Zero on success, -1 on error
'''

cdef extern int libcoopgamma_get_gamma_recv(libcoopgamma_filter_table_t* table, libcoopgamma_context_t* ctx,
                                            libcoopgamma_async_context_t* async_ctx) nogil
'''
Retrieve the current gamma ramp adjustments, receive response part

@param   table      Output for the response, must be initialised
@param   ctx        The state of the library, must be connected
@param   async_ctx  Information about the request
@return             Zero on success, -1 on error, in which case `ctx->error`
                    (rather than `errno`) is read for information about the error
'''

cdef extern int libcoopgamma_get_gamma_sync(const libcoopgamma_filter_query_t* query,
                                            libcoopgamma_filter_table_t* table, libcoopgamma_context_t* ctx) nogil
'''
Retrieve the current gamma ramp adjustments, synchronous version

This is a synchronous request function, as such,
you have to ensure that communication is blocking
(default), and that there are not asynchronous
requests waiting, it also means that EINTR:s are
silently ignored and there no wait to cancel the
operation without disconnection from the server

@param   query  The query to send
@param   table  Output for the response, must be initialised
@param   ctx    The state of the library, must be connected
@return         Zero on success, -1 on error, in which case `ctx->error`
                (rather than `errno`) is read for information about the error
'''

cdef extern int libcoopgamma_set_gamma_send(const libcoopgamma_filter_t* filtr, libcoopgamma_context_t* ctx,
                                            libcoopgamma_async_context_t* async_ctx) nogil
'''
Apply, update, or remove a gamma ramp adjustment, send request part

Cannot be used before connecting to the server

@param   filtr      The filter to apply, update, or remove, gamma ramp meta-data must match the CRTC's
@param   ctx        The state of the library, must be connected
@param   async_ctx  Information about the request, that is needed to
                    identify and parse the response, is stored here
@return             Zero on success, -1 on error
'''

cdef extern int libcoopgamma_set_gamma_recv(libcoopgamma_context_t* ctx, libcoopgamma_async_context_t* async_ctx) nogil
'''
Apply, update, or remove a gamma ramp adjustment, receive response part

@param   ctx        The state of the library, must be connected
@param   async_ctx  Information about the request
@return             Zero on success, -1 on error, in which case `ctx->error`
                    (rather than `errno`) is read for information about the error
'''

cdef extern int libcoopgamma_set_gamma_sync(const libcoopgamma_filter_t* filtr, libcoopgamma_context_t* ctx) nogil
'''
Apply, update, or remove a gamma ramp adjustment, synchronous version

This is a synchronous request function, as such,
you have to ensure that communication is blocking
(default), and that there are not asynchronous
requests waiting, it also means that EINTR:s are
silently ignored and there no wait to cancel the
operation without disconnection from the server

@param   filtr  The filter to apply, update, or remove, gamma ramp meta-data must match the CRTC's
@param   ctx    The state of the library, must be connected
@return         Zero on success, -1 on error, in which case `ctx->error`
                (rather than `errno`) is read for information about the error
'''



def _libcoopgamma_native_get_methods():
    '''
    List all recognised adjustment method
    
    SIGCHLD must not be ignored or blocked
    
    @return  :list<str>|int  Either the value of `errno` (on failure), or (on success) a list
                             of names. You should only free the outer pointer, inner pointers
                             are subpointers of the outer pointer and cannot be freed.
    '''
    cdef char** methods = libcoopgamma_get_methods()
    cdef bytes bs
    if methods is NULL:
        return int(errno)
    try:
        ret = []
        i = 0
        while methods[i] is not NULL:
            bs = methods[i]
            ret.append(bs.decode('utf-8', 'strict'))
            i += 1
    finally:
        free(methods)
    return ret


def _libcoopgamma_native_get_method_and_site(method : str, site : str):
    '''
    Get the adjustment method and site
    
    SIGCHLD must not be ignored or blocked
    
    @param   method:str?         The adjustment method, `None` for automatic
    @param   site:str?           The site, `None` for automatic
    @return  :int|(:str, :str?)  Either the value of `errno` (on failure), or (on success)
                                 the selected adjustment method and the selected the
                                 selected site. If the adjustment method only supports
                                 one site or if `site` is `None` and no site can be
                                 selected automatically, the selected site (the second
                                 element in the returned tuple) will be `None`.
    '''
    cdef char* cmethod = NULL
    cdef char* csite = NULL
    cdef char* rcsmethod = NULL
    cdef char* rcssite = NULL
    cdef bytes rbsmethod
    cdef bytes rbssite
    try:
        if method is not None:
            buf = method.encode('utf-8') + bytes([0])
            cmethod = <char*>malloc(len(buf) * sizeof(char))
            for i in range(len(buf)):
                cmethod[i] = <char>(buf[i])
        if site is not None:
            buf = site.encode('utf-8') + bytes([0])
            csite = <char*>malloc(len(buf) * sizeof(char))
            for i in range(len(buf)):
                csite[i] = <char>(buf[i])
        rmethod, rsite = None, None
        if libcoopgamma_get_method_and_site(cmethod, csite, &rcsmethod, &rcssite) < 0:
            return int(errno)
        rbsmethod = rcsmethod
        rmethod = rbsmethod.decode('utf-8', 'strict')
        if rcssite is not NULL:
            rbssite = rcssite
            rsite = rbssite.decode('utf-8', 'strict')
        return (rmethod, rsite)
    finally:
        free(cmethod)
        free(csite)
        free(rcsmethod)
        free(rcssite)


def _libcoopgamma_native_get_pid_file(method : str, site : str):
    '''
    Get the PID file of the coopgamma server
    
    SIGCHLD must not be ignored or blocked
    
    @param   method:str?  The adjustment method, `None` for automatic
    @param   site:str?    The site, `None` for automatic
    @return  :int|str?    Either the value of `errno` (on failure), or (on success)
                          the pathname of the server's PID file. `None` if the server
                          does not use PID files.
    '''
    cdef char* cmethod = NULL
    cdef char* csite = NULL
    cdef char* path = NULL
    cdef bytes bs
    try:
        if method is not None:
            buf = method.encode('utf-8') + bytes([0])
            cmethod = <char*>malloc(len(buf) * sizeof(char))
            for i in range(len(buf)):
                cmethod[i] = <char>(buf[i])
        if site is not None:
            buf = site.encode('utf-8') + bytes([0])
            csite = <char*>malloc(len(buf) * sizeof(char))
            for i in range(len(buf)):
                csite[i] = <char>(buf[i])
        path = libcoopgamma_get_pid_file(cmethod, csite)
        if path is not NULL or errno == 0:
            bs = path
            return bs.decode('utf-8', 'strict')
    finally:
        free(cmethod)
        free(csite)
        free(path)
    return int(errno)


def _libcoopgamma_native_get_socket_file(method : str, site : str):
    '''
    Get the socket file of the coopgamma server
    
    SIGCHLD must not be ignored or blocked
    
    @param   method:int|str?  The adjustment method, `None` for automatic
    @param   site:str?        The site, `None` for automatic
    @return  :int|str?        Either the value of `errno` (on failure), or (on success)
                              the pathname of the server's socket, `None` if the server does have
                              its own socket, which is the case when communicating with a server
                              in a multi-server display server like mds
    '''
    cdef char* cmethod = NULL
    cdef char* csite = NULL
    cdef char* path = NULL
    cdef bytes bs
    try:
        if method is not None:
            buf = method.encode('utf-8') + bytes([0])
            cmethod = <char*>malloc(len(buf) * sizeof(char))
            for i in range(len(buf)):
                cmethod[i] = <char>(buf[i])
        if site is not None:
            buf = site.encode('utf-8') + bytes([0])
            csite = <char*>malloc(len(buf) * sizeof(char))
            for i in range(len(buf)):
                csite[i] = <char>(buf[i])
        path = libcoopgamma_get_socket_file(cmethod, csite)
        if path is not NULL or errno == 0:
            bs = path
            return bs.decode('utf-8', 'strict')
    finally:
        free(cmethod)
        free(csite)
        free(path)
    return int(errno)


def _libcoopgamma_native_context_create():
    '''
    Create an instance of `libcoopgamma_context_t`
    
    @return  :(:bool, :int)  Element 0: Whether the call was successful
                             Element 1: The value of `errno` on failure, and on
                                        success, the address of the created instance
    '''
    cdef libcoopgamma_context_t* this
    this = <libcoopgamma_context_t*>malloc(sizeof(libcoopgamma_context_t))
    if this is NULL:
        return (False, int(errno))
    if libcoopgamma_context_initialise(this) < 0:
        saved_errno = int(errno)
        libcoopgamma_context_destroy(this, <int>0)
        free(this)
        return (False, saved_errno)
    return (True, int(<intptr_t><void*>this))


def _libcoopgamma_native_context_free(address : int):
    '''
    Disconnect and free an instance of a `libcoopgamma_context_t`
    
    @param  address:int  The address of the `libcoopgamma_context_t` instance
    '''
    cdef libcoopgamma_context_t* this = <libcoopgamma_context_t*><void*><intptr_t>address
    libcoopgamma_context_destroy(this, <int>1)
    free(this)


def _libcoopgamma_native_context_marshal(address : int):
    '''
    Marshal a `libcoopgamma_context_t` into a buffer
    
    @param   address:int  The address of the instance to marshal
    @return  :int|bytes   Either the value of `errno` (on failure), or (on success)
                          a byte representation of the instance
    '''
    cdef libcoopgamma_context_t* this = <libcoopgamma_context_t*><void*><intptr_t>address
    cdef size_t n = libcoopgamma_context_marshal(this, NULL)
    cdef char* buf = <char*>malloc(n)
    if buf is NULL:
        return int(errno)
    try:
        libcoopgamma_context_marshal(this, buf)
        return bytes(int(<int><unsigned char>(buf[i])) for i in range(int(n)))
    finally:
        free(buf)


def _libcoopgamma_native_context_unmarshal(buf : bytes):
    '''
    Unmarshal a `libcoopgamma_context_t` from a buffer
    
    @param   buf:bytes      The buffer with the marshalled instance
    @return  :(:int, :int)  Element 0: 0 = Success
                                       1 = Incompatible downgrade
                                       2 = Incompatible upgrade
                                       -1 = `errno` is returned
                            Element 1: If [0] = 0:  The address of the unmarshalled instance
                                       If [0] = -1: The value of `errno`
    '''
    cdef size_t _n = 0
    cdef libcoopgamma_context_t* this
    cdef char* bs = NULL
    success = False
    this = <libcoopgamma_context_t*>calloc(1, sizeof(libcoopgamma_context_t))
    if this is NULL:
        return (-1, int(errno))
    try:
        if libcoopgamma_context_initialise(this) < 0:
            return (-1, int(errno))
        bs = <char*>malloc(len(buf) * sizeof(char))
        if bs is NULL:
            return (-1, int(errno))
        for i in range(len(buf)):
            bs[i] = <char><unsigned char>(buf[i])
        ret = int(libcoopgamma_context_unmarshal(this, bs, &_n))
        ret = (ret, int(<intptr_t><void*>this))
        success = True
        return ret
    finally:
        if not success:
            libcoopgamma_context_destroy(this, <int>1)
            free(this)
        free(bs)


def _libcoopgamma_native_context_set_fd(address : int, fd : int):
    '''
    Set the field for socket file descriptor in a `libcoopgamma_context_t` 
    
    @param  address:int  The address of the `libcoopgamma_context_t` instance
    @param  fd:int       The file descriptor
    '''
    cdef libcoopgamma_context_t* this = <libcoopgamma_context_t*><void*><intptr_t>address
    this.fd = fd


def _libcoopgamma_native_async_context_create():
    '''
    Create an instance of `libcoopgamma_context_t`
    
    @return  :(:bool, :int)  Element 0: Whether the call was successful
                             Element 1: The value of `errno` on failure, and on
                                        success, the address of the created instance
    '''
    cdef libcoopgamma_context_t* this
    this = <libcoopgamma_context_t*>malloc(sizeof(libcoopgamma_context_t))
    if this is NULL:
        return (False, int(errno))
    if libcoopgamma_context_initialise(this) < 0:
        saved_errno = int(errno)
        libcoopgamma_context_destroy(this, <int>0)
        free(this)
        return (False, saved_errno)
    return (True, int(<intptr_t><void*>this))


def _libcoopgamma_native_async_context_free(address : int):
    '''
    Free an instance of a `libcoopgamma_async_context_t`
    
    @param  address:int  The address of the `libcoopgamma_async_context_t` instance
    '''
    cdef libcoopgamma_async_context_t* this = <libcoopgamma_async_context_t*><void*><intptr_t>address
    libcoopgamma_async_context_destroy(this)
    free(this)


def _libcoopgamma_native_async_context_marshal(address : int):
    '''
    Marshal a `libcoopgamma_async_context_t` into a buffer
    
    @param   address:int  The address of the instance to marshal
    @return  :int|bytes   Either the value of `errno` (on failure), or (on success)
                          a byte representation of the instance
    '''
    cdef libcoopgamma_async_context_t* this = <libcoopgamma_async_context_t*><void*><intptr_t>address
    cdef size_t n = libcoopgamma_async_context_marshal(this, NULL)
    cdef char* buf = <char*>malloc(n)
    try:
        if buf is NULL:
            return int(errno)
        libcoopgamma_async_context_marshal(this, buf)
        ret = bytes(int(<int><unsigned char>(buf[i])) for i in range(int(n)))
    finally:
        free(buf)
    return ret


def _libcoopgamma_native_async_context_unmarshal(buf : bytes):
    '''
    Unmarshal a `libcoopgamma_async_context_t` from a buffer
    
    @param   buf:bytes      The buffer with the marshalled instance
    @return  :(:int, :int)  Element 0: 0 = Success
                                       1 = Incompatible downgrade
                                       2 = Incompatible upgrade
                                       -1 = `errno` is returned
                            Element 1: If [0] = 0:  The address of the unmarshalled instance
                                       If [0] = -1: The value of `errno`
    '''
    cdef size_t _n = 0
    cdef libcoopgamma_async_context_t* this
    cdef char* bs = NULL
    successful = False
    this = <libcoopgamma_async_context_t*>malloc(sizeof(libcoopgamma_async_context_t))
    try:
        if this is NULL:
            return (-1, int(errno))
        if libcoopgamma_async_context_initialise(this) < 0:
            return (-1, int(errno))
        bs = <char*>malloc(len(buf) * sizeof(char))
        if bs is NULL:
            return (-1, int(errno))
        for i in range(len(buf)):
            bs[i] = <char><unsigned char>(buf[i])
        ret = int(libcoopgamma_async_context_unmarshal(this, bs, &_n))
        ret = (ret, int(<intptr_t><void*>this))
        success = True
        return ret
    finally:
        if not success:
            libcoopgamma_async_context_destroy(this)
            free(this)
        free(bs)


def _libcoopgamma_native_connect(method : str, site : str, address : int):
    '''
    Connect to a coopgamma server, and start it if necessary
    
    Use `libcoopgamma_context_destroy` to disconnect
    
    SIGCHLD must not be ignored or blocked
    
    @param   method:str?     The adjustment method, `NULL` for automatic
    @param   site:str?       The site, `NULL` for automatic
    @param   address:int     The address of the state of the library, must be initialised
    @return  :(:bool, :int)  If [0] = `True`: [1] is the socket's file descriptor.
                             If [0] = `False`: [1] is 0 if the server on could not be
                                 initialised, or the value `errno` on other errors
    '''
    cdef char* cmethod = NULL
    cdef char* csite = NULL
    cdef libcoopgamma_context_t* ctx = <libcoopgamma_context_t*><void*><intptr_t>address
    try:
        if method is not None:
            buf = method.encode('utf-8') + bytes([0])
            cmethod = <char*>malloc(len(buf) * sizeof(char))
            for i in range(len(buf)):
                cmethod[i] = <char>(buf[i])
        if site is not None:
            buf = site.encode('utf-8') + bytes([0])
            csite = <char*>malloc(len(buf) * sizeof(char))
            for i in range(len(buf)):
                csite[i] = <char>(buf[i])
        if libcoopgamma_connect(cmethod, csite, ctx) < 0:
            return (False, int(errno))
        return (True, int(ctx.fd))
    finally:
        free(cmethod)
        free(csite)


def _libcoopgamma_native_set_nonblocking(address : int, nonblocking : bool):
    '''
    By default communication is blocking, this function
    can be used to switch between blocking and nonblocking
    
    After setting the communication to nonblocking,
    `libcoopgamma_flush`, `libcoopgamma_synchronise` and
    and request-sending functions can fail with EAGAIN and
    EWOULDBLOCK. It is safe to continue with `libcoopgamma_flush`
    (for `libcoopgamma_flush` it selfand equest-sending functions)
    or `libcoopgamma_synchronise` just like EINTR failure.
    
    @param   address:int       The address of the state of the library, must be connected
    @param   nonblocking:bool  Nonblocking mode?
    @return  :int              Zero on success, the value of `errno` on error
    '''
    cdef libcoopgamma_context_t* ctx = <libcoopgamma_context_t*><void*><intptr_t>address
    if libcoopgamma_set_nonblocking(ctx, <int>(1 if nonblocking else 0)) < 0:
        return int(errno)
    return 0


def _libcoopgamma_native_flush(address : int):
    '''
    Send all pending outbound data
    
    If this function or another function that sends a request
    to the server fails with EINTR, call this function to
    complete the transfer. The `async_ctx` parameter will always
    be in a properly configured state if a function fails
    with EINTR.
    
    @param   address:int  The address of the state of the library, must be connected
    @return  :int         Zero on success, the value of `errno` on error
    '''
    cdef libcoopgamma_context_t* ctx = <libcoopgamma_context_t*><void*><intptr_t>address
    if libcoopgamma_flush(ctx) < 0:
        return int(errno)
    return 0


def _libcoopgamma_native_synchronise(address : int, pending : list):
    '''
    Wait for the next message to be received
    
    @param   address:int        The address of the state of the library, must be connected
    @param   pending:list<int>  Addresses of the information for each pending request
    @return  :(:bool, :int)     If [0] = 0: [1] is the value of `errno`
                                   [0] = 1: [1] is the selected request
    '''
    cdef libcoopgamma_context_t* ctx = <libcoopgamma_context_t*><void*><intptr_t>address
    cdef libcoopgamma_async_context_t* pends
    cdef libcoopgamma_async_context_t* pend
    cdef size_t selected = 0
    pends = <libcoopgamma_async_context_t*>malloc(len(pending) * sizeof(libcoopgamma_async_context_t))
    try:
        if pends is NULL:
            return (False, int(errno))
        for i in range(len(pending)):
            pend = <libcoopgamma_async_context_t*><void*><intptr_t>(pending[i])
            pends[i] = pend[0]
        if libcoopgamma_synchronise(ctx, pends, <size_t>len(pending), &selected) < 0:
            return (False, int(errno))
    finally:
        free(pends)
    return (True, <int>selected)


def _libcoopgamma_native_skip_message(address : int):
    '''
    Tell the library that you will not be parsing a receive message
    
    @param  address:int  The address of the state of the library, must be connected
    '''
    libcoopgamma_skip_message(<libcoopgamma_context_t*><void*><intptr_t>address)


def _libcoopgamma_native_get_crtcs_send(address : int, async_ctx : int):
    '''
    List all available CRTC:s, send request part
    
    @param   address:int    The address of the state of the library, must be connected
    @param   async_ctx:int  The address of the `AsyncContext` for the request
    @return  :int           Zero on success, the value of `errno` on failure
    '''
    cdef libcoopgamma_context_t* ctx = <libcoopgamma_context_t*><void*><intptr_t>address
    cdef libcoopgamma_async_context_t* actx = <libcoopgamma_async_context_t*><void*><intptr_t>async_ctx
    if libcoopgamma_get_crtcs_send(ctx, actx) < 0:
        return int(errno)
    return 0


def _libcoopgamma_native_get_crtcs_recv(address : int, async_ctx : int):
    '''
    List all available CRTC:s, receive response part
    
    @param   address:int     The address of the state of the library, must be connected
    @param   async_ctx:int   The address of the `AsyncContext` for the request
    @return  :int|list<str>  The value of `errno` (on failure), or (on success)
                             a list of the names of the available CRTC:s
    '''
    cdef libcoopgamma_context_t* ctx = <libcoopgamma_context_t*><void*><intptr_t>address
    cdef libcoopgamma_async_context_t* actx = <libcoopgamma_async_context_t*><void*><intptr_t>async_ctx
    cdef char** crtcs = libcoopgamma_get_crtcs_recv(ctx, actx)
    cdef bytes bs
    if crtcs is NULL:
        return int(errno)
    try:
        ret, i = [], 0
        while crtcs[i] is not NULL:
            bs = crtcs[i]
            ret.append(bs.decode('utf-8', 'strict'))
            i += 1
    finally:
        free(crtcs)
    return ret


def _libcoopgamma_native_get_crtcs_sync(address : int):
    '''
    List all available CRTC:s, synchronous version
    
    This is a synchronous request function, as such,
    you have to ensure that communication is blocking
    (default), and that there are not asynchronous
    requests waiting, it also means that EINTR:s are
    silently ignored and there no wait to cancel the
    operation without disconnection from the server
    
    @param   address:int     The address of the state of the library, must be connected
    @return  :int|list<str>  The value of `errno` (on failure), or (on success)
                             a list of the names of the available CRTC:s
    '''
    cdef libcoopgamma_context_t* ctx = <libcoopgamma_context_t*><void*><intptr_t>address
    cdef char** crtcs = libcoopgamma_get_crtcs_sync(ctx)
    cdef bytes bs
    try:
        if crtcs is NULL:
            return int(errno)
        ret, i = [], 0
        while crtcs[i] is not NULL:
            bs = crtcs[i]
            ret.append(bs.decode('utf-8', 'strict'))
            i += 1
    finally:
        free(crtcs)
    return ret


def _libcoopgamma_native_get_gamma_info_send(crtc : str, address : int, async_ctx : int):
    '''
    Retrieve information about a CRTC:s gamma ramps, send request part
    
    Cannot be used before connecting to the server
    
    @param   crtc:crtc      The name of the CRTC
    @param   address:int    The address of the state of the library, must be connected
    @param   async_ctx:int  The address of the information about the request, that is
                            needed to identify and parse the response, is stored here
    @return  :int           Zero on success, the value of `errno` on error
    '''
    cdef libcoopgamma_context_t* ctx = <libcoopgamma_context_t*><void*><intptr_t>address
    cdef libcoopgamma_async_context_t* actx = <libcoopgamma_async_context_t*><void*><intptr_t>async_ctx
    cdef char* ccrtc = NULL
    try:
        bs = crtc.encode('utf-8') + bytes([0])
        ccrtc = <char*>malloc(len(bs) * sizeof(char))
        if ccrtc is NULL:
            return int(errno)
        for i in range(len(bs)):
            ccrtc[i] = <char>(bs[i])
        if libcoopgamma_get_gamma_info_send(ccrtc, ctx, actx) < 0:
            return int(errno)
    finally:
        free(ccrtc)
    return 0


def _libcoopgamma_native_get_gamma_info_recv(address : int, async_ctx : int):
    '''
    Retrieve information about a CRTC:s gamma ramps, receive response part
    
    @param   address:int           The address of the state of the library, must be connected 
    @param   async_ctx:int         The address of the information about the request
    @return  :int|(:bool, :tuple)  The value of `errno` (on failure) or:
                                   Element 0: whether the call was successful
                                   Element 1: tuple with the data for the structure response (possibly error)
    '''
    cdef libcoopgamma_context_t* ctx = <libcoopgamma_context_t*><void*><intptr_t>address
    cdef libcoopgamma_async_context_t* actx = <libcoopgamma_async_context_t*><void*><intptr_t>async_ctx
    cdef libcoopgamma_crtc_info_t info
    cdef bytes bs
    try:
        if libcoopgamma_crtc_info_initialise(&info) < 0:
            return int(errno)
        if libcoopgamma_get_gamma_info_recv(&info, ctx, actx) < 0:
            desc = None
            if ctx.error.description is not NULL:
                bs = ctx.error.description
                desc = bs.decode('utf-8', 'strict')
            ret = (False, (int(ctx.error.number), ctx.error.custom != 0, ctx.error.server_side != 0, desc))
        else:
            ret = (True, (info.cooperative != 0, int(info.depth), int(info.supported), int(info.red_size),
                          int(info.green_size), int(info.blue_size), int(info.colourspace),
                          None if info.have_gamut == 0 else ((info.red_x, info.red_y),
                                                             (info.green_x, info.green_y),
                                                             (info.blue_x, info.blue_y),
                                                             (info.white_x, info.white_y))))
    finally:
        libcoopgamma_crtc_info_destroy(&info)
    return ret


def _libcoopgamma_native_get_gamma_info_sync(crtc : str, address : int):
    '''
    Retrieve information about a CRTC:s gamma ramps, synchronous version
    
    This is a synchronous request function, as such, you have to ensure that
    communication is blocking (default), and that there are not asynchronous
    requests waiting, it also means that EINTR:s are silently ignored and
    there no wait to cancel the operation without disconnection from the server
    
    @param   crtc:str              The name of the CRTC
    @param   address:int           The address of the state of the library, must be connected
    @return  :int|(:bool, :tuple)  The value of `errno` (on failure) or:
                                   Element 0: whether the call was successful
                                   Element 1: tuple with the data for the structure response (possibly error)
    '''
    cdef char* ccrtc = NULL
    cdef libcoopgamma_context_t* ctx = <libcoopgamma_context_t*><void*><intptr_t>address
    cdef libcoopgamma_crtc_info_t info
    cdef bytes bs
    try:
        if libcoopgamma_crtc_info_initialise(&info) < 0:
            return int(errno)
        bscrtc = crtc.encode('utf-8') + bytes([0])
        ccrtc = <char*>malloc(len(bscrtc) * sizeof(bscrtc))
        for i in range(len(bscrtc)):
            ccrtc[i] = <char>(bscrtc[i])
        if libcoopgamma_get_gamma_info_sync(ccrtc, &info, ctx) < 0:
            desc = None
            if ctx.error.description is not NULL:
                bs = ctx.error.description
                desc = bs.decode('utf-8', 'strict')
            ret = (False, (int(ctx.error.number), ctx.error.custom != 0, ctx.error.server_side != 0, desc))
        else:
            ret = (True, (info.cooperative != 0, int(info.depth), int(info.supported), int(info.red_size),
                          int(info.green_size), int(info.blue_size), int(info.colourspace),
                          None if info.have_gamut == 0 else ((info.red_x, info.red_y),
                                                             (info.green_x, info.green_y),
                                                             (info.blue_x, info.blue_y),
                                                             (info.white_x, info.white_y))))
    finally:
        libcoopgamma_crtc_info_destroy(&info)
        free(ccrtc)
    return ret


def _libcoopgamma_native_get_gamma_send(query, address : int, async_ctx : int):
    
    '''
    Retrieve the current gamma ramp adjustments, send request part
    
    Cannot be used before connecting to the server
    
    @param   query:Query    The query to send
    @param   address:int    The address of the state of the library, must be connected
    @param   async_ctx:int  The address of the information about the request, that is
                            needed to identify and parse the response, is stored here
    @return                 Zero on success, the value of `errno` on failure
    '''
    cdef libcoopgamma_context_t* ctx = <libcoopgamma_context_t*><void*><intptr_t>address
    cdef libcoopgamma_async_context_t* actx = <libcoopgamma_async_context_t*><void*><intptr_t>async_ctx
    cdef libcoopgamma_filter_query_t qry
    crtc_bs = query.crtc.encode('utf-8') + bytes([0])
    qry.high_priority = <int64_t>(query.high_priority)
    qry.low_priority = <int64_t>(query.low_priority)
    qry.coalesce = <int>(query.coalesce)
    qry.crtc = <char*>malloc(len(crtc_bs) * sizeof(char))
    try:
        if qry.crtc is NULL:
            return int(errno)
        for i in range(len(crtc_bs)):
            qry.crtc[i] = <char>(crtc_bs[i])
        if libcoopgamma_get_gamma_send(&qry, ctx, actx) < 0:
            return int(errno)
    finally:
        free(qry.crtc)
    return 0
    

def _libcoopgamma_native_get_gamma_recv(address : int, async_ctx : int):
    '''
    Retrieve the current gamma ramp adjustments, receive response part
    
    @param   address:int           The address of the state of the library, must be connected
    @param   async_ctx:int         The address of the information about the request
    @return  :int|(:bool, :tuple)  The value of `errno` (on failure) or:
                                   Element 0: whether the call was successful
                                   Element 1: tuple with the data for the structure response (possibly error)
    '''
    cdef libcoopgamma_context_t* ctx = <libcoopgamma_context_t*><void*><intptr_t>address
    cdef libcoopgamma_async_context_t* actx = <libcoopgamma_async_context_t*><void*><intptr_t>async_ctx
    cdef libcoopgamma_filter_table_t table
    cdef bytes bs
    cdef libcoopgamma_ramps_t* rampsp
    cdef char* fclass
    try:
        if libcoopgamma_filter_table_initialise(&table) < 0:
            return int(errno)
        if libcoopgamma_get_gamma_recv(&table, ctx, actx) < 0:
            desc = None
            if ctx.error.description is not NULL:
                bs = ctx.error.description
                desc = bs.decode('utf-8', 'strict')
            ret = (False, (int(ctx.error.number), ctx.error.custom != 0, ctx.error.server_side != 0, desc))
        else:
            filters = [None] * int(table.filter_count)
            for j in range(int(table.filter_count)):
                rampsp = &(table.filters[j].ramps)
                fclass = table.filters[j].fclass
                red   = [None] * rampsp.u_red_size
                green = [None] * rampsp.u_green_size
                blue  = [None] * rampsp.u_blue_size
                if table.depth == -2:
                    for i in range(rampsp.u_red_size):
                        red[i] = (<double*>(rampsp.u_red))[i]
                    for i in range(rampsp.u_green_size):
                        green[i] = (<double*>(rampsp.u_green))[i]
                    for i in range(rampsp.u_blue_size):
                        blue[i] = (<double*>(rampsp.u_blue))[i]
                elif table.depth == -1:
                    for i in range(rampsp.u_red_size):
                        red[i] = (<float*>(rampsp.u_red))[i]
                    for i in range(rampsp.u_green_size):
                        green[i] = (<float*>(rampsp.u_green))[i]
                    for i in range(rampsp.u_blue_size):
                        blue[i] = (<float*>(rampsp.u_blue))[i]
                elif table.depth == 8:
                    for i in range(rampsp.u_red_size):
                        red[i] = (<uint8_t*>(rampsp.u_red))[i]
                    for i in range(rampsp.u_green_size):
                        green[i] = (<uint8_t*>(rampsp.u_green))[i]
                    for i in range(rampsp.u_blue_size):
                        blue[i] = (<uint8_t*>(rampsp.u_blue))[i]
                elif table.depth == 16:
                    for i in range(rampsp.u_red_size):
                        red[i] = (<uint16_t*>(rampsp.u_red))[i]
                    for i in range(rampsp.u_green_size):
                        green[i] = (<uint16_t*>(rampsp.u_green))[i]
                    for i in range(rampsp.u_blue_size):
                        blue[i] = (<uint16_t*>(rampsp.u_blue))[i]
                elif table.depth == 32:
                    for i in range(rampsp.u_red_size):
                        red[i] = (<uint32_t*>(rampsp.u_red))[i]
                    for i in range(rampsp.u_green_size):
                        green[i] = (<uint32_t*>(rampsp.u_green))[i]
                    for i in range(rampsp.u_blue_size):
                        blue[i] = (<uint32_t*>(rampsp.u_blue))[i]
                elif table.depth == 64:
                    for i in range(rampsp.u_red_size):
                        red[i] = (<uint64_t*>(rampsp.u_red))[i]
                    for i in range(rampsp.u_green_size):
                        green[i] = (<uint64_t*>(rampsp.u_green))[i]
                    for i in range(rampsp.u_blue_size):
                        blue[i] = (<uint64_t*>(rampsp.u_blue))[i]
                ramps = (red, green, blue)
                pclass = None
                if fclass is not NULL:
                    bs = fclass
                    pclass = bs.decode('utf-8', 'strict')
                filters[j] = (int(table.filters[j].priority), pclass, ramps)
            ret = (True, (int(table.red_size), int(table.green_size), int(table.blue_size),
                          int(table.depth), filters))
    finally:
        libcoopgamma_filter_table_destroy(&table)
    return ret


def _libcoopgamma_native_get_gamma_sync(query, address : int):
    '''
    Retrieve the current gamma ramp adjustments, synchronous version
    
    This is a synchronous request function, as such, you have to ensure that
    communication is blocking (default), and that there are not asynchronous
    requests waiting, it also means that EINTR:s are silently ignored and
    there no wait to cancel the operation without disconnection from the server
    
    @param   query:Query           The query to send
    @param   address:int           The address of the state of the library, must be connected
    @return  :int|(:bool, :tuple)  The value of `errno` (on failure) or:
                                   Element 0: whether the call was successful
                                   Element 1: tuple with the data for the structure response (possibly error)
    '''
    cdef libcoopgamma_context_t* ctx = <libcoopgamma_context_t*><void*><intptr_t>address
    cdef libcoopgamma_filter_table_t table
    cdef libcoopgamma_filter_query_t qry
    cdef bytes bs
    cdef libcoopgamma_ramps_t* rampsp
    cdef char* fclass
    crtc_bs = query.crtc.encode('utf-8') + bytes([0])
    qry.high_priority = <int64_t>(query.high_priority)
    qry.low_priority = <int64_t>(query.low_priority)
    qry.coalesce = <int>(query.coalesce)
    qry.crtc = <char*>malloc(len(crtc_bs) * sizeof(char))
    try:
        if qry.crtc is NULL:
            return int(errno)
        for i in range(len(crtc_bs)):
            qry.crtc[i] = <char>(crtc_bs[i])
        if libcoopgamma_filter_table_initialise(&table) < 0:
            saved_errno = int(errno)
            libcoopgamma_filter_table_destroy(&table)
            return saved_errno
        try:
            if libcoopgamma_get_gamma_sync(&qry, &table, ctx) < 0:
                desc = None
                if ctx.error.description is not NULL:
                    bs = ctx.error.description
                    desc = bs.decode('utf-8', 'strict')
                ret = (False, (int(ctx.error.number), ctx.error.custom != 0, ctx.error.server_side != 0, desc))
            else:
                filters = [None] * int(table.filter_count)
                for j in range(int(table.filter_count)):
                    rampsp = &(table.filters[j].ramps)
                    fclass = table.filters[j].fclass
                    red   = [None] * rampsp.u_red_size
                    green = [None] * rampsp.u_green_size
                    blue  = [None] * rampsp.u_blue_size
                    if table.depth == -2:
                        for i in range(rampsp.u_red_size):
                            red[i] = (<double*>(rampsp.u_red))[i]
                        for i in range(rampsp.u_green_size):
                            green[i] = (<double*>(rampsp.u_green))[i]
                        for i in range(rampsp.u_blue_size):
                            blue[i] = (<double*>(rampsp.u_blue))[i]
                    elif table.depth == -1:
                        for i in range(rampsp.u_red_size):
                            red[i] = (<float*>(rampsp.u_red))[i]
                        for i in range(rampsp.u_green_size):
                            green[i] = (<float*>(rampsp.u_green))[i]
                        for i in range(rampsp.u_blue_size):
                            blue[i] = (<float*>(rampsp.u_blue))[i]
                    elif table.depth == 8:
                        for i in range(rampsp.u_red_size):
                            red[i] = (<uint8_t*>(rampsp.u_red))[i]
                        for i in range(rampsp.u_green_size):
                            green[i] = (<uint8_t*>(rampsp.u_green))[i]
                        for i in range(rampsp.u_blue_size):
                            blue[i] = (<uint8_t*>(rampsp.u_blue))[i]
                    elif table.depth == 16:
                        for i in range(rampsp.u_red_size):
                            red[i] = (<uint16_t*>(rampsp.u_red))[i]
                        for i in range(rampsp.u_green_size):
                            green[i] = (<uint16_t*>(rampsp.u_green))[i]
                        for i in range(rampsp.u_blue_size):
                            blue[i] = (<uint16_t*>(rampsp.u_blue))[i]
                    elif table.depth == 32:
                        for i in range(rampsp.u_red_size):
                            red[i] = (<uint32_t*>(rampsp.u_red))[i]
                        for i in range(rampsp.u_green_size):
                            green[i] = (<uint32_t*>(rampsp.u_green))[i]
                        for i in range(rampsp.u_blue_size):
                            blue[i] = (<uint32_t*>(rampsp.u_blue))[i]
                    elif table.depth == 64:
                        for i in range(rampsp.u_red_size):
                            red[i] = (<uint64_t*>(rampsp.u_red))[i]
                        for i in range(rampsp.u_green_size):
                            green[i] = (<uint64_t*>(rampsp.u_green))[i]
                        for i in range(rampsp.u_blue_size):
                            blue[i] = (<uint64_t*>(rampsp.u_blue))[i]
                    ramps = (red, green, blue)
                    pclass = None
                    if fclass is not NULL:
                        bs = fclass
                        pclass = bs.decode('utf-8', 'strict')
                    filters[j] = (int(table.filters[j].priority), pclass, ramps)
                ret = (True, (int(table.red_size), int(table.green_size), int(table.blue_size),
                              int(table.depth), filters))
        finally:
            libcoopgamma_filter_table_destroy(&table)
    finally:
        free(qry.crtc)
    return ret


def _libcoopgamma_native_copy_ramps(intptr_t dest_address, src, libcoopgamma_depth_t depth):
    '''
    Copy a Python ramp-trio into C ramp-trio
    
    @param  dest_address:intptr_t       The address of the C ramp-trio
    @param  src:Ramps                   The Python ramp-trio
    @param  depth:libcoopgamma_depth_t  The data type of the ramp stops
    '''
    cdef libcoopgamma_ramps_t* dest = <libcoopgamma_ramps_t*><void*><intptr_t>dest_address
    cdef uint8_t* r8
    cdef uint8_t* g8
    cdef uint8_t* b8
    cdef uint16_t* r16
    cdef uint16_t* g16
    cdef uint16_t* b16
    cdef uint32_t* r32
    cdef uint32_t* g32
    cdef uint32_t* b32
    cdef uint64_t* r64
    cdef uint64_t* g64
    cdef uint64_t* b64
    cdef float* rf
    cdef float* gf
    cdef float* bf
    cdef double* rd
    cdef double* gd
    cdef double* bd
    cdef size_t rn = <size_t>len(src.red)
    cdef size_t gn = <size_t>len(src.green)
    cdef size_t bn = <size_t>len(src.blue)
    if depth == 8:
        r8 = <uint8_t*>malloc((rn + gn + bn) * sizeof(uint8_t))
        g8 = r8 + rn
        b8 = g8 + gn
        for i in range(int(rn)):
            r8[i] = <uint8_t>(src.red[i])
        for i in range(int(gn)):
            g8[i] = <uint8_t>(src.green[i])
        for i in range(int(bn)):
            b8[i] = <uint8_t>(src.blue[i])
        dest.u_red   = <void*>r8
        dest.u_green = <void*>g8
        dest.u_blue  = <void*>b8
    elif depth == 16:
        r16 = <uint16_t*>malloc((rn + gn + bn) * sizeof(uint16_t))
        g16 = r16 + rn
        b16 = g16 + gn
        for i in range(int(rn)):
            r16[i] = <uint16_t>(src.red[i])
        for i in range(int(gn)):
            g16[i] = <uint16_t>(src.green[i])
        for i in range(int(bn)):
            b16[i] = <uint16_t>(src.blue[i])
        dest.u_red   = <void*>r16
        dest.u_green = <void*>g16
        dest.u_blue  = <void*>b16
    elif depth == 32:
        r32 = <uint32_t*>malloc((rn + gn + bn) * sizeof(uint32_t))
        g32 = r32 + rn
        b32 = g32 + gn
        for i in range(int(rn)):
            r32[i] = <uint32_t>(src.red[i])
        for i in range(int(gn)):
            g32[i] = <uint32_t>(src.green[i])
        for i in range(int(bn)):
            b32[i] = <uint32_t>(src.blue[i])
        dest.u_red   = <void*>r32
        dest.u_green = <void*>g32
        dest.u_blue  = <void*>b32
    elif depth == 64:
        r64 = <uint64_t*>malloc((rn + gn + bn) * sizeof(uint64_t))
        g64 = r64 + rn
        b64 = g64 + gn
        for i in range(int(rn)):
            r64[i] = <uint64_t>(src.red[i])
        for i in range(int(gn)):
            g64[i] = <uint64_t>(src.green[i])
        for i in range(int(bn)):
            b64[i] = <uint64_t>(src.blue[i])
        dest.u_red   = <void*>r64
        dest.u_green = <void*>g64
        dest.u_blue  = <void*>b64
    elif depth == -1:
        rf = <float*>malloc((rn + gn + bn) * sizeof(float))
        gf = rf + rn
        bf = gf + gn
        for i in range(int(rn)):
            rf[i] = <float>(src.red[i])
        for i in range(int(gn)):
            gf[i] = <float>(src.green[i])
        for i in range(int(bn)):
            bf[i] = <float>(src.blue[i])
        dest.u_red   = <void*>rf
        dest.u_green = <void*>gf
        dest.u_blue  = <void*>bf
    else:
        rd = <double*>malloc((rn + gn + bn) * sizeof(double))
        gd = rd + rn
        bd = gd + gn
        for i in range(int(rn)):
            rd[i] = <double>(src.red[i])
        for i in range(int(gn)):
            gd[i] = <double>(src.green[i])
        for i in range(int(bn)):
            bd[i] = <double>(src.blue[i])
        dest.u_red   = <void*>rd
        dest.u_green = <void*>gd
        dest.u_blue  = <void*>bd


def _libcoopgamma_native_set_gamma_send(filtr, address : int, async_ctx : int):
    '''
    Apply, update, or remove a gamma ramp adjustment, send request part
    
    Cannot be used before connecting to the server
    
    @param   filtr:Filter   The filter to apply, update, or remove, gamma ramp
                            meta-data must match the CRTC's
    @param   address:int    The address of the state of the library, must be connected
    @param   async_ctx:int  The address of the information about the request, that is needed to
                            identify and parse the response, is stored here
    @return  :int           Zero on success, the value of `errno` on failure
    '''
    cdef libcoopgamma_context_t* ctx = <libcoopgamma_context_t*><void*><intptr_t>address
    cdef libcoopgamma_async_context_t* actx = <libcoopgamma_async_context_t*><void*><intptr_t>async_ctx
    cdef libcoopgamma_filter_t flr
    crtc_bs = filtr.crtc.encode('utf-8') + bytes([0])
    clss_bs = filtr.fclass.encode('utf-8') + bytes([0])
    flr.priority = <int64_t>(0 if filtr.priority is None else filtr.priority)
    flr.lifespan = <libcoopgamma_lifespan_t>(filtr.lifespan)
    flr.depth = <libcoopgamma_depth_t>(0 if filtr.depth is None else filtr.depth)
    flr.fclass = NULL
    flr.crtc = NULL
    flr.ramps.u_red_size   = len(filtr.ramps.red)   if filtr.ramps is not None else 0
    flr.ramps.u_green_size = len(filtr.ramps.green) if filtr.ramps is not None else 0
    flr.ramps.u_blue_size  = len(filtr.ramps.blue)  if filtr.ramps is not None else 0
    flr.ramps.u_red   = NULL
    flr.ramps.u_green = NULL
    flr.ramps.u_blue  = NULL
    try:
        if filtr.ramps is not None:
            _libcoopgamma_native_copy_ramps(<intptr_t><void*>&(flr.ramps), filtr.ramps, flr.depth)
        flr.crtc = <char*>malloc(len(crtc_bs) * sizeof(char))
        if flr.crtc is NULL:
            return int(errno)
        flr.fclass = <char*>malloc(len(clss_bs) * sizeof(char))
        if flr.fclass is NULL:
            return int(errno)
        for i in range(len(crtc_bs)):
            flr.crtc[i] = <char>(crtc_bs[i])
        for i in range(len(clss_bs)):
            flr.fclass[i] = <char>(clss_bs[i])
        if libcoopgamma_set_gamma_send(&flr, ctx, actx) < 0:
            return int(errno)
    finally:
        free(flr.crtc)
        free(flr.fclass)
        free(flr.ramps.u_red)
    return 0


def _libcoopgamma_native_set_gamma_recv(address : int, async_ctx : int):
    '''
    Apply, update, or remove a gamma ramp adjustment, receive response part
    
    @param   address:int    The address of the state of the library, must be connected
    @param   async_ctx:int  The address of the information about the request
    @return  :tuple?        The value of `errno` (on failure) or:
                            Element 0: whether the call was successful
                            Element 1: tuple with the data for the structure response (possibly error)
    '''
    cdef libcoopgamma_context_t* ctx = <libcoopgamma_context_t*><void*><intptr_t>address
    cdef libcoopgamma_async_context_t* actx = <libcoopgamma_async_context_t*><void*><intptr_t>async_ctx
    cdef bytes bs
    if libcoopgamma_set_gamma_recv(ctx, actx) < 0:
        desc = None
        if ctx.error.description is not NULL:
            bs = ctx.error.description
            desc = bs.decode('utf-8', 'strict')
        return (int(ctx.error.number), ctx.error.custom != 0, ctx.error.server_side != 0, desc)
    return None


def _libcoopgamma_native_set_gamma_sync(filtr, address : int):
    '''
    Apply, update, or remove a gamma ramp adjustment, synchronous version
    
    This is a synchronous request function, as such, you have to ensure that
    communication is blocking (default), and that there are not asynchronous
    requests waiting, it also means that EINTR:s are silently ignored and
    there no wait to cancel the operation without disconnection from the server
    
    @param   filtr:Filter  The filter to apply, update, or remove, gamma ramp
                           meta-data must match the CRTC's
    @param   address:int   The address of the state of the library, must be connected
    @return  :tuple?       The value of `errno` (on failure) or:
                           Element 0: whether the call was successful
                           Element 1: tuple with the data for the structure response (possibly error)
    '''
    cdef libcoopgamma_context_t* ctx = <libcoopgamma_context_t*><void*><intptr_t>address
    cdef libcoopgamma_filter_t flr
    cdef bytes bs
    crtc_bs = filtr.crtc.encode('utf-8') + bytes([0])
    clss_bs = filtr.fclass.encode('utf-8') + bytes([0])
    flr.priority = <int64_t>(0 if filtr.priority is None else filtr.priority)
    flr.lifespan = <libcoopgamma_lifespan_t>(filtr.lifespan)
    flr.depth = <libcoopgamma_depth_t>(0 if filtr.depth is None else filtr.depth)
    flr.fclass = NULL
    flr.crtc = NULL
    flr.ramps.u_red_size   = len(filtr.ramps.red)   if filtr.ramps is not None else 0
    flr.ramps.u_green_size = len(filtr.ramps.green) if filtr.ramps is not None else 0
    flr.ramps.u_blue_size  = len(filtr.ramps.blue)  if filtr.ramps is not None else 0
    flr.ramps.u_red   = NULL
    flr.ramps.u_green = NULL
    flr.ramps.u_blue  = NULL
    try:
        if filtr.ramps is not None:
            _libcoopgamma_native_copy_ramps(<intptr_t><void*>&(flr.ramps), filtr.ramps, flr.depth)
        flr.crtc = <char*>malloc(len(crtc_bs) * sizeof(char))
        if flr.crtc is NULL:
            return int(errno)
        flr.fclass = <char*>malloc(len(clss_bs) * sizeof(char))
        if flr.fclass is NULL:
            return int(errno)
        for i in range(len(crtc_bs)):
            flr.crtc[i] = <char>(crtc_bs[i])
        for i in range(len(clss_bs)):
            flr.fclass[i] = <char>(clss_bs[i])
        if libcoopgamma_set_gamma_sync(&flr, ctx) < 0:
            desc = None
            if ctx.error.description is not NULL:
                bs = ctx.error.description
                desc = bs.decode('utf-8', 'strict')
            return (int(ctx.error.number), ctx.error.custom != 0, ctx.error.server_side != 0, desc)
    finally:
        free(flr.crtc)
        free(flr.fclass)
        free(flr.ramps.u_red)
    return None


class Support(_enum.IntEnum):
    '''
    Values used to indicate the support for gamma adjustments
    
    @value  NO=0     Gamma adjustments are not supported
    @value  MAYBE=1  Don't know whether gamma adjustments are supported
    @value  YES=2    Gamma adjustments are supported
    '''
    NO = 0
    MAYBE = 1
    YES = 2
    
    @staticmethod
    def str(v):
        '''
        Get string representation of a value
        
        @param   v:int  The value, must be value of the enum
        @return  :str   Human-readable string representation of the value
        '''
        if v == Support.NO:
            return 'no'
        if v == Support.YES:
            return 'yes'
        if v == Support.MAYBE:
            return 'maybe'
        raise ValueError()


class Depth(_enum.IntEnum):
    '''
    Values used to tell which datatype is used for the gamma ramp stops
    
    The values will always be the number of bits for integral types, and
    negative for floating-point types
    
    @value  UINT8=8    Unsigned 8-bit integer
    @value  UINT16=16  Unsigned 16-bit integer
    @value  UINT32=32  Unsigned 32-bit integer
    @value  UINT64=64  Unsigned 64-bit integer
    @value  FLOAT<0    Single-precision floating-point
    @value  DOUBLE<0   Double-precision floating-point
    '''
    UINT8 = 8
    UINT16 = 16
    UINT32 = 32
    UINT64 = 64
    FLOAT = -1
    DOUBLE = -2
    
    @staticmethod
    def str(v):
        '''
        Get string representation of a value
        
        @param   v:int  The value, must be value of the enum
        @return  :str   Human-readable string representation of the value
        '''
        if v == Depth.UINT8:
            return '8-bits'
        if v == Depth.UINT16:
            return '16-bits'
        if v == Depth.UINT32:
            return '32-bits'
        if v == Depth.UINT64:
            return '64-bits'
        if v == Depth.FLOAT:
            return 'single-precision floating-point'
        if v == Depth.DOUBLE:
            return 'double-precision floating-point'
        raise ValueError()


class Lifespan(_enum.IntEnum):
    '''
    Values used to tell when a filter should be removed
    
    @value  REMOVE=0         Remove the filter now
    @value  UNTIL_DEATH>0    Remove the filter when disconnecting from the coopgamma server
    @value  UNTIL_REMOVAL>0  Only remove the filter when it is explicitly requested
    '''
    REMOVE = 0
    UNTIL_DEATH = 1
    UNTIL_REMOVAL = 2
    
    @staticmethod
    def str(v):
        '''
        Get string representation of a value
        
        @param   v:int  The value, must be value of the enum
        @return  :str   Human-readable string representation of the value
        '''
        if v == Lifespan.REMOVE:
            return 'remove'
        if v == Lifespan.UNTIL_DEATH:
            return 'until death'
        if v == Lifespan.UNTIL_REMOVAL:
            return 'until removal'
        raise ValueError()


class Colourspace(_enum.IntEnum):
    '''
    Colourspaces
    
    @value  UNKNOWN=0  The colourspace is unknown
    @value  SRGB>0     sRGB (Standard RGB)
    @value  RGB>0      RGB other than sRGB
    @value  NON_RGB>0  Non-RGB multicolour
    @value  GREY>0     Monochrome, greyscale, or some other singlecolour scale
    '''
    UNKNOWN = 0
    SRGB = 1
    RGB = 2
    NON_RGB = 3
    GREY = 4
    
    @staticmethod
    def str(v):
        '''
        Get string representation of a value
        
        @param   v:int  The value, must be value of the enum
        @return  :str   Human-readable string representation of the value
        '''
        if v == Colourspace.UNKNOWN:
            return 'unknown'
        if v == Colourspace.SRGB:
            return 'sRGB'
        if v == Colourspace.RGB:
            return 'RGB'
        if v == Colourspace.NON_RGB:
            return 'non-RGB'
        if v == Colourspace.GREY:
            return 'greyscale'
        raise ValueError()


class Ramps:
    '''
    Gamma ramp structure
    
    @variable  red:list<int>    The red gamma ramp
    @variable  green:list<int>  The green gamma ramp
    @variable  blue:list<int>   The blue gamma ramp
    '''
    def __init__(self, red, green, blue):
        '''
        Constructor
        
        @param  red:int    The number of stops in the red ramp
        @param  green:int  The number of stops in the green ramp
        @param  blue:int   The number of stops in the blue ramp
        
        -- or --
        
        @param  red:list<int>    The red gamma ramp
        @param  green:list<int>  The green gamma ramp
        @param  blue:list<int>   The blue gamma ramp
        '''
        if isinstance(red, list):
            self.red   = list(red)
            self.green = list(green)
            self.blue  = list(blue)
        else:
            self.red   = [0] * red
            self.green = [0] * green
            self.blue  = [0] * blue
    
    def clone(self):
        '''
        Create a copy of the instance
        '''
        return Ramps(self.red, self.green, self.blue)
    
    def __repr__(self):
        '''
        Create a parsable string representation of the instance
        
        @return  :str  Parsable representation of the instance
        '''
        params = (self.red, self.green, self.blue)
        return 'libcoopgamma.Ramps(%s)' % ', '.join(repr(p) for p in params)


class Filter:
    '''
    Data set to the coopgamma server to apply, update, or remove a filter
    
    @variable  priority:int       The priority of the filter, higher priority
                                  is applied first. The gamma correction should
                                  have priority 0. This is a signed 64-bit integer.
    @variable  crtc:str           The CRTC for which this filter shall be applied 
    @variable  fclass:str         Identifier for the filter.
                                  The syntax must be "${PACKAGE_NAME}::${COMMAND_NAME}::${RULE}".
    @variable  lifespan:Lifespan  When shall the filter be removed?
                                  If this member's value is `LIBCOOPGAMMA_REMOVE`,
                                  only `.crtc` and `.class` need also be defined.
    @variable  depth:Depth        The data type and bit-depth of the ramp stops
    @variable  ramps:Ramps        The gamma ramp adjustments of the filter
    '''
    def __init__(self, priority = None, crtc = None, fclass = None, lifespan = None, depth = None, ramps = None):
        '''
        Constructor
        
        @param  priority:int       The priority of the filter, higher priority
                                   is applied first. The gamma correction should
                                   have priority 0. This is a signed 64-bit integer.
        @param  crtc:str           The CRTC for which this filter shall be applied 
        @param  fclass:str         Identifier for the filter.
                                   The syntax must be "${PACKAGE_NAME}::${COMMAND_NAME}::${RULE}".
        @param  lifespan:Lifespan  When shall the filter be removed?
                                   If this member's value is `LIBCOOPGAMMA_REMOVE`,
                                   only `.crtc` and `.class` need also be defined.
        @param  depth:Depth        The data type and bit-depth of the ramp stops
        @param  ramps:Ramps        The gamma ramp adjustments of the filter
        '''
        self.priority = priority
        self.crtc = crtc
        self.fclass = fclass
        self.lifespan = lifespan
        self.depth = depth
        self.ramps = ramps
    
    def clone(self, shallow = False):
        '''
        Create a copy of the instance
        
        @param  shallow:bool  Create a shallow copy?
        '''
        ramps = self.ramps if shallow else self.ramps.clone()
        return Filter(self.priority, self.crtc, self.fclass, self.lifespan, self.depth, ramps)
    
    def __repr__(self):
        '''
        Create a parsable string representation of the instance
        
        @return  :str  Parsable representation of the instance
        '''
        params = (self.priority, self.crtc, self.fclass, self.lifespan, self.depth, self.ramps)
        return 'libcoopgamma.Filter(%s)' % ', '.join(repr(p) for p in params)


class GamutPoint:
    '''
    The x- and y-values (CIE xyY) of a colour
    
    @variable  x_raw:int  The x-value (CIE xyY) multiplied by 1024
    @variable  y_raw:int  The y-value (CIE xyY) multiplied by 1024
    @variable  x:int      The x-value (CIE xyY)
    @variable  y:int      The y-value (CIE xyY)
    '''
    def __init__(self, x, y):
        '''
        Constructor
        
        @param  x:int  The x-value (CIE xyY) multiplied by 1024
        @param  y:int  The y-value (CIE xyY) multiplied by 1024
        '''
        self.x_raw, self.x = x, x / 1024
        self.y_raw, self.y = y, y / 1024
    
    def clone(self):
        '''
        Create a copy of the instance
        '''
        return GamutPoint(self.x_raw, self.y_raw)
    
    def __repr__(self):
        '''
        Create a parsable string representation of the instance
        
        @return  :str  Parsable representation of the instance
        '''
        params = (self.x_raw, self.y_raw)
        return 'libcoopgamma.GamutPoint(%s)' % ', '.join(repr(p) for p in params)
    
    def __str__(self):
        '''
        Get human-readable string representation of instance
        
        @return  :str  Human-readable string representation of instance
        '''
        return '(%f, %f)' % (self.x, self.y)


class Gamut:
    '''
    The values of the monitor's colour stimuli
    
    @variable  red:GamutPoint    The red stimuli
    @variable  green:GamutPoint  The green stimuli
    @variable  blue:GamutPoint   The blue stimuli
    @variable  white:GamutPoint  The default whitepoint
    '''
    def __init__(self, red = None, green = None, blue = None, white = None):
        '''
        Constructor
        
        @param  red:GamutPoint|(int, int)    The red stimuli
        @param  green:GamutPoint|(int, int)  The green stimuli
        @param  blue:GamutPoint|(int, int)   The blue stimuli
        @param  white:GamutPoint|(int, int)  The default whitepoint
        '''
        self.red   = GamutPoint(*red)   if isinstance(red,   tuple) else red
        self.green = GamutPoint(*green) if isinstance(green, tuple) else green
        self.blue  = GamutPoint(*blue)  if isinstance(blue,  tuple) else blue
        self.white = GamutPoint(*white) if isinstance(white, tuple) else white
    
    def clone(self, shallow = True):
        '''
        Create a copy of the instance
        
        @param  shallow:bool  Create a shallow copy?
        '''
        if shallow:
            return Gamut(self.red, self.green, self.blue, self.white)
        return Gamut(self.red.clone(), self.green.clone(), self.blue.clone(), self.white.clone())
    
    def __repr__(self):
        '''
        Create a parsable string representation of the instance
        
        @return  :str  Parsable representation of the instance
        '''
        params = (repr(self.red), repr(self.green), repr(self.blue), repr(self.white))
        return 'libcoopgamma.Gamut(%s)' % ', '.join(repr(p) for p in params)


class CRTCInfo:
    '''
    Gamma ramp meta information for a CRTC
    
    @variable  cooperative:bool         Is cooperative gamma server running?
    @variable  depth:Depth?             The data type and bit-depth of the ramp stops
    @variable  supported:Support        Is gamma adjustments supported on the CRTC?
                                        If not, `depth`, `red_size`, `green_size`,
                                        and `blue_size` are undefined
    @variable  red_size:int?            The number of stops in the red ramp
    @variable  green_size:int?          The number of stops in the green ramp
    @variable  blue_size:int?           The number of stops in the blue ramp
    @variable  colourspace:Colourspace  The monitor's colourspace
    @variable  gamut:Gamut?             Measurements of the monitor's colourspace,
                                        `None` if the this information is unavailable.
                                        If this is not `None`, but the colourspace
                                        is not RGB (or sRGB), there is something
                                        wrong. Always check the colourspace.
    '''
    def __init__(self, cooperative = None, depth = None, supported = None, red_size = None,
                 green_size = None, blue_size = None, colourspace = None, gamut = None):
        '''
        Constructor
        
        @param  cooperative:bool         Is cooperative gamma server running?
        @param  depth:Depth?             The data type and bit-depth of the ramp stops
        @param  supported:Support        Is gamma adjustments supported on the CRTC?
                                         If not, `depth`, `red_size`, `green_size`,
                                         and `blue_size` are undefined
        @param  red_size:int?            The number of stops in the red ramp
        @param  green_size:int?          The number of stops in the green ramp
        @param  blue_size:int?           The number of stops in the blue ramp
        @param  colourspace:Colourspace  The monitor's colourspace
        @param  gamut:Gamut|tuple?       Measurements of the monitor's colourspace,
                                         `None` if the this information is unavailable
        '''
        self.cooperative = cooperative
        self.depth = depth
        self.supported = supported
        self.red_size = red_size
        self.green_size = green_size
        self.blue_size = blue_size
        self.colourspace = colourspace
        if gamut is None or isinstance(gamut, Gamut):
            self.gamut = gamut
        else:
            self.gamut = Gamut(*gamut)
    
    def make_ramps(self):
        '''
        Construct a `Ramps` instances for this CRTC
        
        @return  :Ramps  An instance of `Ramps` with same a number of ramp stops
                         matching this CRTC
        '''
        return Ramps(self.red_size, self.green_size, self.blue_size)
    
    def clone(self, shallow = True):
        '''
        Create a copy of the instance
        
        @param  shallow:bool  Create a shallow copy?
        '''
        return CRTCInfo(self.cooperative, self.depth, self.supported, self.red_size,
                        self.green_size, self.blue_size, self.colourspace,
                        self.gamut if shallow else self.gamut.clone())
    
    def __repr__(self):
        '''
        Create a parsable string representation of the instance
        
        @return  :str  Parsable representation of the instance
        '''
        params = (self.cooperative, self.depth, self.supported, self.red_size,
                  self.green_size, self.blue_size, self.colourspace, self.gamut)
        return 'libcoopgamma.CRTCInfo(%s)' % ', '.join(repr(p) for p in params)


class FilterQuery:
    '''
    Data sent to the coopgamma server when requestng the current filter table
    
    @variable  high_priority:int  Do no return filters with higher priority than
                                  this value. This is a signed 64-bit integer.
    @variable  low_priority:int   Do no return filters with lower priority than
                                  this value. This is a signed 64-bit integer.
    @variable  crtc:str           The CRTC for which the the current filters shall returned
    @variable  coalesce:bool      Whether to coalesce all filters into one gamma ramp triplet
    '''
    def __init__(self, high_priority = 9223372036854775807,
                 low_priority = -9223372036854775808, crtc = None, coalesce = False):
        '''
        Constructor
        
        @param  high_priority:int  Do no return filters with higher priority than
                                   this value. This is a signed 64-bit integer.
        @param  low_priority:int   Do no return filters with lower priority than
                                   this value. This is a signed 64-bit integer.
        @param  crtc:str           The CRTC for which the the current filters shall returned
        @param  coalesce:bool      Whether to coalesce all filters into one gamma ramp triplet
        '''
        self.high_priority = high_priority
        self.low_priority = low_priority
        self.crtc = crtc
        self.coalesce = coalesce
    
    def clone(self):
        '''
        Create a copy of the instance
        '''
        return FilterQuery(self.high_priority, self.low_priority, self.crtc, self.coalesce)
    
    def __repr__(self):
        '''
        Create a parsable string representation of the instance
        
        @return  :str  Parsable representation of the instance
        '''
        params = (self.high_priority, self.low_priority, self.crtc, self.coalesce)
        return 'libcoopgamma.FilterQuery(%s)' % ', '.join(repr(p) for p in params)


class QueriedFilter:
    '''
    Stripped down version of `Filter` which only contains the
    information returned in response to "Command: get-gamma"
    
    @variable  priority:int  The filter's priority. This is a signed 64-bit integer.
    @variable  fclass:str    The filter's class
    @variable  ramps:Ramps   The gamma ramp adjustments of the filter
    '''
    def __init__(self, priority = None, fclass = None, ramps = None):
        '''
        Constructor
        
        @param  priority:int       The filter's priority. This is a signed 64-bit integer.
        @param  fclass:str         The filter's class
        @param  ramps:Ramps|tuple  The gamma ramp adjustments of the filter
        '''
        self.priority = priority
        self.fclass = fclass
        self.ramps = None
        if ramps is not None:
            self.ramps = Ramps(*ramps) if isinstance(ramps, tuple) else ramps
    
    def clone(self, shallow = False):
        '''
        Create a copy of the instance
        
        @param  shallow:bool  Create a sallow copy?
        '''
        ramps = self.ramps if shallow else self.ramps.clone()
        return QueriedFilter(self.priority, self.fclass, ramps)
    
    def __repr__(self):
        '''
        Create a parsable string representation of the instance
        
        @return  :str  Parsable representation of the instance
        '''
        params = (self.priority, self.fclass, self.ramps)
        return 'libcoopgamma.QueriedFilter(%s)' % ', '.join(repr(p) for p in params)


class FilterTable:
    '''
    Response type for "Command: get-gamma": a list of applied filters
    and meta-information that was necessary for decoding the response
    
    @variable  red_size:int                 The number of stops in the red ramp
    @variable  green_size:int               The number of stops in the green ramp
    @variable  blue_size:int                The number of stops in the blue ramp
    @variable  depth:Depth                  The data type and bit-depth of the ramp stops
    @variable  filters:list<QueriedFilter>  The filters, should be ordered by priority
                                            in descending order, lest there is something
                                            wrong with the coopgamma server.
                                            If filter coalition was requested, there will
                                            be exactly one filter and `filters[0].class`
                                            and `filters[0].priority` are undefined.
    '''
    def __init__(self, red_size = None, green_size = None, blue_size = None, depth = None, filters = None):
        '''
        Constructor 
        
        @param  red_size:int                       The number of stops in the red ramp
        @param  green_size:int                     The number of stops in the green ramp
        @param  blue_size:int                      The number of stops in the blue ramp
        @param  depth:Depth                        The data type and bit-depth of the ramp stops
        @param  filters:list<QueriedFilter|tuple>  The filters, should be ordered by priority
                                                   in descending order, lest there is something
                                                   wrong with the coopgamma server.
                                                   If filter coalition was requested, there will
                                                   be exactly one filter and `filters[0].class`
                                                   and `filters[0].priority` are undefined.
        '''
        self.red_size   = red_size
        self.green_size = green_size
        self.blue_size  = blue_size
        self.depth      = depth
        if filters is None:
            self.filters
        else:
            self.filters    = list(QueriedFilter(*f) if isinstance(f, tuple) else f for f in filters)
    
    def make_ramps(self):
        '''
        Construct a `Ramps` instances for this CRTC
        
        @return  :Ramps  An instance of `Ramps` with same a number of ramp stops
                         matching this CRTC
        '''
        return Ramps(self.red_size, self.green_size, self.blue_size)
    
    def clone(self, shallow = False):
        '''
        Create a copy of the instance
        
        @param  shallow:bool  Create a sallow copy?
        '''
        filters = list(self.filters) if shallow else [f.clone() for f in self.filters]
        return FilterTable(self.red_size, self.green_size, self.blue_size, self.depth, filters)
    
    def __repr__(self):
        '''
        Create a parsable string representation of the instance
        
        @return  :str  Parsable representation of the instance
        '''
        params = (self.red_size, self.green_size, self.blue_size, self.depth, self.filters)
        return 'libcoopgamma.FilterTable(%s)' % ', '.join(repr(p) for p in params)


class ErrorReport:
    '''
    Error message from coopgamma server
    
    @variable  number:int        The error code. If `.custom` is false, 0 indicates success,
                                 otherwise, 0 indicates that no error code has been assigned.
    @variable  custom:bool       Is this a custom error?
    @variable  server_side:bool  Did the error occur on the server-side?
    @variable  description:str?  Error message, can be `None` if `custom` is false
    '''
    def __init__(self, number = None, custom = None, server_side = None, description = None):
        '''
        Constructor
        
        @param  number:int        The error code. If `.custom` is false, 0 indicates success,
                                  otherwise, 0 indicates that no error code has been assigned.
        @param  custom:bool       Is this a custom error?
        @param  server_side:bool  Did the error occur on the server-side?
        @param  description:str?  Error message, can be `None` if `custom` is false
        '''
        self.number = number
        self.custom = custom
        self.server_side = server_side
        self.description = description
    
    def clone(self):
        '''
        Create a copy of the instance
        '''
        return ErrorReport(self.number, self.custom, self.server_side, self.description)
    
    def __repr__(self):
        '''
        Create a parsable string representation of the instance
        
        @return  :str  Parsable representation of the instance
        '''
        params = (self.number, self.custom, self.server_side, self.description)
        return 'libcoopgamma.ErrorReport(%s)' % ', '.join(repr(p) for p in params)
    
    @staticmethod
    def create_error(error):
        '''
        Create an instance of a subclass of `Exception`
        
        @param  error  Error information
        '''
        if isinstance(error, int):
            import os
            return OSError(error, os.strerror(error))
        error = ErrorReport(*error)
        if not error.custom and not error.server_side:
            import os
            return OSError(error.number, os.strerror(error.number))
        else:
            return LibcoopgammaError(error)


class LibcoopgammaError(Exception):
    '''
    libcoopgamma error class
    
    @variable  error:ErrorReport  Error information
    '''
    def __init__(self, error):
        '''
        Constructor
        
        @param  error:ErrorReport  Error information
        '''
        self.error = error
    
    def __str__(self):
        '''
        Return description and error number as a string
        
        @return  :str  Description and error number
        '''
        if self.error.description is not None:
            if self.error.number != 0:
                return '%s (#%i)' % (self.error.description, self.error.number)
            else:
                return '%s' % self.error.description
        else:
            return '#%i' % self.error.number
    
    def __repr__(self):
        '''
        Create a parsable string representation of the instance
        
        @return  :str  Parsable representation of the instance
        '''
        return 'libcoopgamma.LibcoopgammaError(%s)' % repr(self.error)


class IncompatibleDowngradeError(Exception):
    '''
    Unmarshalled state from a newer version, that is not supported, of libcoopgamma
    '''
    def __init__(self):
        '''
        Constructor
        '''
        pass
    
    def __str__(self):
        '''
        Return descriptor of the error
        
        @return  :str  Descriptor of the error
        '''
        return 'Incompatible downgrade error'
    
    def __repr__(self):
        '''
        Create a parsable string representation of the instance
        
        @return  :str  Parsable representation of the instance
        '''
        return 'libcoopgamma.IncompatibleDowngradeError()'


class IncompatibleUpgradeError(Exception):
    '''
    Unmarshalled state from an older version, that is no longer supported, of libcoopgamma
    '''
    def __init__(self):
        '''
        Constructor
        '''
        pass
    
    def __str__(self):
        '''
        Return descriptor of the error
        
        @return  :str  Descriptor of the error
        '''
        return 'Incompatible downgrade error'
    
    def __repr__(self):
        '''
        Create a parsable string representation of the instance
        
        @return  :str  Parsable representation of the instance
        '''
        return 'libcoopgamma.IncompatibleUpgradeErrorError()'


class ServerInitialisationError(Exception):
    '''
    coopgamma server failed to initialise, reason unknown
    '''
    def __init__(self):
        '''
        Constructor
        '''
        pass
    
    def __str__(self):
        '''
        Return descriptor of the error
        
        @return  :str  Descriptor of the error
        '''
        return 'Server initialisation error'
    
    def __repr__(self):
        '''
        Create a parsable string representation of the instance
        
        @return  :str  Parsable representation of the instance
        '''
        return 'libcoopgamma.ServerInitialisationError()'


class Context:
    '''
    Library state
    
    Use of this structure is not thread-safe, create
    one instance per thread that uses this structure
    
    @variable  fd:int  File descriptor for the socket
    '''
    def __init__(self, fd = -1, buf = None):
        '''
        Constructor
        
        @param  fd:int      File descriptor for the socket
        @param  buf:bytes?  Buffer to unmarshal
        '''
        self.address = None
        self.fd = fd
        if buf is None:
            (successful, value) = _libcoopgamma_native_context_create()
            if not successful:
                raise ErrorReport.create_error(value)
        else:
            (error, value) = _libcoopgamma_native_context_unmarshal(buf)
            if error < 0:
                raise ErrorReport.create_error(value)
            elif error == 1:
                raise IncompatibleDowngradeError()
            elif error == 2:
                raise IncompatibleUpgradeError()
        self.address = value
    
    def __del__(self):
        '''
        Destructor
        '''
        if self.address is not None:
            _libcoopgamma_native_context_free(self.address)
    
    def __repr__(self):
        '''
        Create a parsable string representation of the instance
        
        @return  :str  Parsable representation of the instance
        '''
        data = _libcoopgamma_native_context_marshal(self.address)
        if isinstance(data, int):
            raise ErrorReport.create_error(data)
        params = (self.fd, data)
        return 'libcoopgamma.Context(%s)' % ', '.join(repr(p) for p in params)
    
    def connect(self, method = None, site = None):
        '''
        Connect to a coopgamma server, and start it if necessary
        
        Use `del` keyword to disconnect
        
        SIGCHLD must not be ignored or blocked
        
        All other methods in this class requires that this
        method has been called successfully
        
        @param  method:int|str?  The adjustment method, `None` for automatic
        @param  site:str?        The site, `None` for automatic
        '''
        if method is not None and isinstance(method, int):
            method = str(method)
        (successful, value) = _libcoopgamma_native_connect(method, site, self.address)
        if not successful:
            if value == 0:
                raise ServerInitialisationError()
            else:
                raise ErrorReport.create_error(value)
        self.fd = value
    
    def detach(self):
        '''
        After calling this function, communication with the
        server is impossible, but the connection is not
        closed and the when the instance is destroyed the
        connection will remain
        '''
        _libcoopgamma_native_context_set_fd(self.address, -1)
    
    def attach(self):
        '''
        Undoes the action of `detach`
        '''
        _libcoopgamma_native_context_set_fd(self.address, self.fd)
    
    def set_nonbreaking(self, nonbreaking):
        '''
        By default communication is blocking, this function
        can be used to switch between blocking and nonblocking
        
        After setting the communication to nonblocking, `flush`, `synchronise` and
        and request-sending functions can fail with EAGAIN and EWOULDBLOCK. It is
        safe to continue with `flush` (for `flush` it selfand equest-sending functions)
        or `synchronise` just like EINTR failure.
        
        @param  nonblocking:bool  Nonblocking mode?
        '''
        error = _libcoopgamma_native_set_nonblocking(self.address, nonbreaking)
        if error != 0:
            raise ErrorReport.create_error(error)
    
    def flush(self):
        '''
        Send all pending outbound data
        
        If this function or another function that sends a request to the server fails
        with EINTR, call this function to complete the transfer. The `async_ctx` parameter
        will always be in a properly configured state if a function fails with EINTR.
        '''
        error = _libcoopgamma_native_flush(self.address)
        if error != 0:
            raise ErrorReport.create_error(error)
    
    def synchronise(self, pending):
        '''
        Wait for the next message to be received
        
        @param   pending:list<AsyncContext>  Information for each pending request
        @return  :int?                       The index of the element in `pending` which corresponds
                                             to the first inbound message, note that this only means
                                             that the message is not for any of the other request,
                                             if the message is corrupt any of the listed requests can
                                             be selected even if it is not for any of the requests.
                                             Functions that parse the message will detect such corruption.
                                             `None` if the message is ignored, which happens if
                                             corresponding AsyncContext is not listed.
        '''
        pending = [p.address for p in pending]
        (successful, value) = _libcoopgamma_native_synchronise(self.address, pending)
        if not successful:
            if value == 0:
                return None
            else:
                raise ErrorReport.create_error(value)
        else:
            return value
    
    def skip_message(self):
        '''
        Tell the library that you will not be parsing a receive message
        '''
        _libcoopgamma_native_skip_message(self.address)
    
    def get_crtcs_send(self, async_ctx):
        '''
        List all available CRTC:s, send request part
        
        @param  async_ctx:AsyncContext  Slot for information about the request that is
                                        needed to identify and parse the response
        '''
        
        error = _libcoopgamma_native_get_crtcs_send(self.address, async_ctx.address)
        if error != 0:
            raise ErrorReport.create_error(error)
    
    def get_crtcs_recv(self, async_ctx):
        '''
        List all available CRTC:s, receive response part
        
        @param   async_ctx:AsyncContext  Information about the request
        @return  :list<str>              A list of names. You should only free the outer
                                         pointer, inner pointers are subpointers of the
                                         outer pointer and cannot be freed.
        '''
        ret = _libcoopgamma_native_get_crtcs_recv(self.address, async_ctx.address)
        if isinstance(ret, int):
            raise ErrorReport.create_error(ret)
        return ret
    
    def get_crtcs_sync(self):
        '''
        List all available CRTC:s, synchronous version
        
        This is a synchronous request function, as such, you have to ensure that
        communication is blocking (default), and that there are not asynchronous
        requests waiting, it also means that EINTR:s are silently ignored and
        there no wait to cancel the operation without disconnection from the server
        
        @return  :list<str>  A list of names. You should only free the outer pointer, inner
                             pointers are subpointers of the outer pointer and cannot be freed.
        '''
        ret = _libcoopgamma_native_get_crtcs_sync(self.address)
        if isinstance(ret, int):
            raise ErrorReport.create_error(ret)
        return ret
    
    def get_gamma_info_send(self, crtc, async_ctx):
        '''
        Retrieve information about a CRTC:s gamma ramps, send request part
        
        @param  crtc:str                The name of the CRT
        @param  async_ctx:AsyncContext  Slot for information about the request that is
                                        needed to identify and parse the response
        '''
        error = _libcoopgamma_native_get_gamma_info_send(crtc, self.address, async_ctx.address)
        if error != 0:
            raise ErrorReport.create_error(error)
    
    def get_gamma_info_recv(self, async_ctx):
        '''
        Retrieve information about a CRTC:s gamma ramps, receive response part
        
        @param   async_ctx:AsyncContext  Information about the request
        @return  :CRTCInfo               Information about the CRTC
        '''
        value = _libcoopgamma_native_get_gamma_info_recv(self.address, async_ctx.address)
        if isinstance(value, int):
            raise ErrorReport.create_error(value)
        (successful, value) = value
        if not successful:
            raise ErrorReport.create_error(value)
        return CRTCInfo(*value)
    
    def get_gamma_info_sync(self, crtc):
        '''
        Retrieve information about a CRTC:s gamma ramps, synchronous version
        
        This is a synchronous request function, as such, you have to ensure that
        communication is blocking (default), and that there are not asynchronous
        requests waiting, it also means that EINTR:s are silently ignored and
        there no wait to cancel the operation without disconnection from the server
        
        @param   crtc:str   The name of the CRT
        @return  :CRTCInfo  Information about the CRTC
        '''
        value = _libcoopgamma_native_get_gamma_info_sync(crtc, self.address)
        if isinstance(value, int):
            raise ErrorReport.create_error(value)
        (successful, value) = value
        if not successful:
            raise ErrorReport.create_error(value)
        return CRTCInfo(*value)
    
    def get_gamma_send(self, query, async_ctx):
        '''
        Retrieve the current gamma ramp adjustments, send request part
        
        @param  query:FilterQuery       The query to send
        @param  async_ctx:AsyncContext  Slot for information about the request that is
                                        needed to identify and parse the response
        '''
        error = _libcoopgamma_native_get_gamma_send(
                                    query, self.address, async_ctx.address)
        if error != 0:
            raise ErrorReport.create_error(error)
    
    def get_gamma_recv(self, async_ctx):
        '''
        Retrieve the current gamma ramp adjustments, receive response part
        
        @param   async_ctx:AsyncContext  Information about the request
        @return  :FilterTable            Filter table
        '''
        value = _libcoopgamma_native_get_gamma_recv(self.address, async_ctx.address)
        if isinstance(value, int):
            raise ErrorReport.create_error(value)
        (successful, value) = value
        if not successful:
            raise ErrorReport.create_error(value)
        return FilterTable(*value)
    
    def get_gamma_sync(self, query):
        '''
        Retrieve the current gamma ramp adjustments, synchronous version
        
        This is a synchronous request function, as such, you have to ensure that
        communication is blocking (default), and that there are not asynchronous
        requests waiting, it also means that EINTR:s are silently ignored and
        there no wait to cancel the operation without disconnection from the server
        
        @param   query:FilterQuery  The query to send
        @return  :FilterTable       Filter table
        '''
        value = _libcoopgamma_native_get_gamma_sync(query, self.address)
        if isinstance(value, int):
            raise ErrorReport.create_error(value)
        (successful, value) = value
        if not successful:
            raise ErrorReport.create_error(value)
        return FilterTable(*value)
    
    def set_gamma_send(self, filtr, async_ctx):
        '''
        Apply, update, or remove a gamma ramp adjustment, send request part
        
        @param  filtr:Filter            The filter to apply, update, or remove, gamma ramp
                                        meta-data must match the CRTC's
        @param  async_ctx:AsyncContext  Slot for information about the request that is
                                        needed to identify and parse the response
        '''
        error = _libcoopgamma_native_set_gamma_send(filtr, self.address, async_ctx.address)
        if error != 0:
            raise ErrorReport.create_error(error)
    
    def set_gamma_recv(self, async_ctx):
        '''
        Apply, update, or remove a gamma ramp adjustment, receive response part
        
        @param  async_ctx:AsyncContext  Information about the request
        '''
        error = _libcoopgamma_native_set_gamma_recv(self.address, async_ctx.address)
        if error is not None:
            raise ErrorReport.create_error(error)
    
    def set_gamma_sync(self, filtr):
        '''
        Apply, update, or remove a gamma ramp adjustment, synchronous version
        
        This is a synchronous request function, as such, you have to ensure that
        communication is blocking (default), and that there are not asynchronous
        requests waiting, it also means that EINTR:s are silently ignored and
        there no wait to cancel the operation without disconnection from the server
        
        @param   filtr:Filter  The filter to apply, update, or remove,
                               gamma ramp meta-data must match the CRTC's
        '''
        error = _libcoopgamma_native_set_gamma_sync(filtr, self.address)
        if error is not None:
            raise ErrorReport.create_error(error)


class AsyncContext:
    '''
    Information necessary to identify and parse a response from the server
    '''
    def __init__(self, buf = None):
        '''
        Constructor
        
        @param  buf:bytes?  Buffer to unmarshal
        '''
        self.address = None
        if buf is None:
            (successful, value) = _libcoopgamma_native_async_context_create()
            if not successful:
                raise ErrorReport.create_error(value)
        else:
            (error, value) = _libcoopgamma_native_async_context_unmarshal(buf)
            if error < 0:
                raise ErrorReport.create_error(value)
            elif error == 1:
                raise IncompatibleDowngradeError()
            elif error == 2:
                raise IncompatibleUpgradeError()
        self.address = value
    
    def __del__(self):
        '''
        Destructor
        '''
        if self.address is not None:
            _libcoopgamma_native_async_context_free(self.address)
    
    def __repr__(self):
        '''
        Create a parsable string representation of the instance
        
        @return  :str  Parsable representation of the instance
        '''
        data = _libcoopgamma_native_async_context_marshal(self.address)
        if isinstance(data, int):
            raise ErrorReport.create_error(data)
        return 'libcoopgamma.AsyncContext(%s)' % repr(data)


def get_methods():
    '''
    List all recognised adjustment method
    
    SIGCHLD must not be ignored or blocked
    
    @return  :list<str>  A list of names. You should only free the outer pointer, inner
                         pointers are subpointers of the outer pointer and cannot be freed.
    '''
    ret = _libcoopgamma_native_get_methods()
    if isinstance(ret, int):
        raise ErrorReport.create_error(ret)
    return ret


def get_method_and_site(method = None, site = None):
    '''
    Get the adjustment method and site
    
    SIGCHLD must not be ignored or blocked
    
    @param   method:int|str?  The adjustment method, `None` for automatic
    @param   site:str?        The site, `None` for automatic
    @return  :(:str, :str?)   The selected adjustment method and the selected the
                              selected site. If the adjustment method only supports
                              one site or if `site` is `None` and no site can be
                              selected automatically, the selected site (the second
                              element in the returned tuple) will be `None`.
    '''
    if method is not None:
        method = str(method)
    ret = _libcoopgamma_native_get_method_and_site(method, site)
    if isinstance(ret, int):
        raise ErrorReport.create_error(ret)
    return ret


def get_pid_file(method = None, site = None):
    '''
    Get the PID file of the coopgamma server
    
    SIGCHLD must not be ignored or blocked
    
    @param   method:int|str?  The adjustment method, `None` for automatic
    @param   site:str?        The site, `None` for automatic
    @return  :str?            The pathname of the server's PID file.
                              `None` if the server does not use PID files.
    '''
    if method is not None:
        method = str(method)
    ret = _libcoopgamma_native_get_pid_file(method, site)
    if ret is not None and isinstance(ret, int):
        raise ErrorReport.create_error(ret)
    return ret


def get_socket_file(method = None, site = None):
    '''
    Get the socket file of the coopgamma server
    
    SIGCHLD must not be ignored or blocked
    
    @param   method:int|str?  The adjustment method, `None` for automatic
    @param   site:str?        The site, `None` for automatic
    @return  :str?            The pathname of the server's socket, `None` if the server does have
                              its own socket, which is the case when communicating with a server
                              in a multi-server display server like mds
    '''
    if method is not None:
        method = str(method)
    ret = _libcoopgamma_native_get_socket_file(method, site)
    if ret is not None and isinstance(ret, int):
        raise ErrorReport.create_error(ret)
    return ret