# Xlib.ext.randr -- RandR extension module
#
#    Copyright (C) 2006 Mike Meyer <mwm@mired.org>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1
# of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
#    Free Software Foundation, Inc.,
#    59 Temple Place,
#    Suite 330,
#    Boston, MA 02111-1307 USA


"""RandR - provide access to the RandR extension information.

This implementation is based off version 1.5 of the XRandR protocol, and may
not be compatible with other versions.

Version 1.5 of the protocol is documented at:
http://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt

Version 1.3.1 here:
http://www.x.org/releases/X11R7.5/doc/randrproto/randrproto.txt

"""


from Xlib import X
from Xlib.protocol import rq

extname = 'RANDR'


# Event codes #
RRScreenChangeNotify        = 0

# V1.2 additions
RRNotify                    = 1

# RRNotify Subcodes
RRNotify_CrtcChange         = 0
RRNotify_OutputChange       = 1
RRNotify_OutputProperty     = 2


# Event selection bits #
RRScreenChangeNotifyMask    = (1 << 0)

# V1.2 additions
RRCrtcChangeNotifyMask      = (1 << 1)
RROutputChangeNotifyMask    = (1 << 2)
RROutputPropertyNotifyMask  = (1 << 3)


# Constants #
SetConfigSuccess            = 0
SetConfigInvalidConfigTime  = 1
SetConfigInvalidTime        = 2
SetConfigFailed             = 3

# used in the rotation field; rotation and reflection in 0.1 proto.
Rotate_0                    = 1
Rotate_90                   = 2
Rotate_180                  = 4
Rotate_270                  = 8

# new in 1.0 protocol, to allow reflection of screen
Reflect_X                   = 16
Reflect_Y                   = 32

# new in 1.2 protocol
HSyncPositive               = 0x00000001
HSyncNegative               = 0x00000002
VSyncPositive               = 0x00000004
VSyncNegative               = 0x00000008
Interlace                   = 0x00000010
DoubleScan                  = 0x00000020
CSync                       = 0x00000040
CSyncPositive               = 0x00000080
CSyncNegative               = 0x00000100
HSkewPresent                = 0x00000200
BCast                       = 0x00000400
PixelMultiplex              = 0x00000800
DoubleClock                 = 0x00001000
ClockDivideBy2              = 0x00002000

# event types?
Connected                   = 0
Disconnected                = 1
UnknownConnection           = 2

# Conventional RandR output properties
PROPERTY_RANDR_EDID         = "EDID"
PROPERTY_SIGNAL_FORMAT      = "SignalFormat"
PROPERTY_SIGNAL_PROPERTIES  = "SignalProperties"
PROPERTY_CONNECTOR_TYPE     = "ConnectorType"
PROPERTY_CONNECTOR_NUMBER   = "ConnectorNumber"
PROPERTY_COMPATIBILITY_LIST = "CompatibilityList"
PROPERTY_CLONE_LIST         = "CloneList"

# subpixel order - TODO: These constants are part of the RENDER extension and
# should be moved there if/when that extension is added to python-xlib.
SubPixelUnknown             = 0
SubPixelHorizontalRGB       = 1
SubPixelHorizontalBGR       = 2
SubPixelVerticalRGB         = 3
SubPixelVerticalBGR         = 4
SubPixelNone                = 5


# Error Codes #
BadRROutput                 = 0
BadRRCrtc                   = 1
BadRRMode                   = 2

# Error classes #
class BadRROutputError(Exception): pass

class BadRRCrtcError(Exception): pass

class BadRRModeError(Exception): pass

# Data Structures #

RandR_ScreenSizes = rq.Struct(
        rq.Card16('width_in_pixels'),
        rq.Card16('height_in_pixels'),
        rq.Card16('width_in_millimeters'),
        rq.Card16('height_in_millimeters'),
        )


RandR_ModeInfo = rq.Struct(
        rq.Card32('id'),
        rq.Card16('width'),
        rq.Card16('height'),
        rq.Card32('dot_clock'),
        rq.Card16('h_sync_start'),
        rq.Card16('h_sync_end'),
        rq.Card16('h_total'),
        rq.Card16('h_skew'),
        rq.Card16('v_sync_start'),
        rq.Card16('v_sync_end'),
        rq.Card16('v_total'),
        rq.Card16('name_length'),
        rq.Card32('flags'),
        )

RandR_Rates = rq.Struct(
        rq.LengthOf('rates', 2),
        rq.List('rates', rq.Card16Obj)
        )

# TODO: This struct is part of the RENDER extension and should be moved there
# if/when that extension is added to python-xlib.
Render_Transform = rq.Struct(
        rq.Card32('matrix11'), #FIXME: All of these are listed as FIXED in the protocol header.
        rq.Card32('matrix12'),
        rq.Card32('matrix13'),
        rq.Card32('matrix21'),
        rq.Card32('matrix22'),
        rq.Card32('matrix23'),
        rq.Card32('matrix31'),
        rq.Card32('matrix32'),
        rq.Card32('matrix33'),
        )

MonitorInfo = rq.Struct(
    rq.Card32('name'),
    rq.Bool('primary'),
    rq.Bool('automatic'),
    rq.LengthOf('crtcs', 2),
    rq.Int16('x'),
    rq.Int16('y'),
    rq.Card16('width_in_pixels'),
    rq.Card16('height_in_pixels'),
    rq.Card32('width_in_millimeters'),
    rq.Card32('height_in_millimeters'),
    rq.List('crtcs', rq.Card32Obj)
)

# Requests #

class QueryVersion(rq.ReplyRequest):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(0),
        rq.RequestLength(),
        rq.Card32('major_version'),
        rq.Card32('minor_version'),
        )
    _reply = rq.Struct(
        rq.ReplyCode(),
        rq.Pad(1),
        rq.Card16('sequence_number'),
        rq.ReplyLength(),
        rq.Card32('major_version'),
        rq.Card32('minor_version'),
        rq.Pad(16),
        )

def query_version(self):
    """Get the current version of the RandR extension.

    """
    return QueryVersion(
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        major_version=1,
        minor_version=5,
        )


class _1_0SetScreenConfig(rq.ReplyRequest):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(2),
        rq.RequestLength(),
        rq.Drawable('drawable'),
        rq.Card32('timestamp'),
        rq.Card32('config_timestamp'),
        rq.Card16('size_id'),
        rq.Card16('rotation'),
        )
    _reply = rq.Struct(
        rq.ReplyCode(),
        rq.Card8('status'),
        rq.Card16('sequence_number'),
        rq.ReplyLength(),
        rq.Card32('new_timestamp'),
        rq.Card32('new_config_timestamp'),
        rq.Window('root'),
        rq.Card16('subpixel_order'),
        rq.Pad(10),
        )

def _1_0set_screen_config(self, size_id, rotation, config_timestamp, timestamp=X.CurrentTime):
    """Sets the screen to the specified size and rotation.

    """
    return _1_0SetScreenConfig(
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        drawable=self,
        timestamp=timestamp,
        config_timestamp=config_timestamp,
        size_id=size_id,
        rotation=rotation,
        )


class SetScreenConfig(rq.ReplyRequest):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(2),
        rq.RequestLength(),
        rq.Drawable('drawable'),
        rq.Card32('timestamp'),
        rq.Card32('config_timestamp'),
        rq.Card16('size_id'),
        rq.Card16('rotation'),
        rq.Card16('rate'), # added in version 1.1
        rq.Pad(2),
        )
    _reply = rq.Struct(
        rq.ReplyCode(),
        rq.Card8('status'),
        rq.Card16('sequence_number'),
        rq.ReplyLength(),
        rq.Card32('new_timestamp'),
        rq.Card32('new_config_timestamp'),
        rq.Window('root'),
        rq.Card16('subpixel_order'),
        rq.Pad(10),
        )

def set_screen_config(self, size_id, rotation, config_timestamp, rate=0, timestamp=X.CurrentTime):
    """Sets the screen to the specified size, rate, rotation and reflection.

    rate can be 0 to have the server select an appropriate rate.

    """
    return SetScreenConfig(
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        drawable=self,
        timestamp=timestamp,
        config_timestamp=config_timestamp,
        size_id=size_id,
        rotation=rotation,
        rate=rate,
        )


class SelectInput(rq.Request):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(4),
        rq.RequestLength(),
        rq.Window('window'),
        rq.Card16('mask'),
        rq.Pad(2),
        )

def select_input(self, mask):
    return SelectInput(
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        window=self,
        mask=mask,
        )


class GetScreenInfo(rq.ReplyRequest):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(5),
        rq.RequestLength(),
        rq.Window('window'),
        )
    _reply = rq.Struct(
        rq.ReplyCode(),
        rq.Card8('set_of_rotations'),
        rq.Card16('sequence_number'),
        rq.ReplyLength(),
        rq.Window('root'),
        rq.Card32('timestamp'),
        rq.Card32('config_timestamp'),
        rq.LengthOf('sizes', 2),
        rq.Card16('size_id'),
        rq.Card16('rotation'),
        rq.Card16('rate'), # added in version 1.1
        rq.Card16('n_rate_ents'), # XCB's protocol description disagrees with the X headers on this; ignoring.
        rq.Pad(2),
        rq.List('sizes', RandR_ScreenSizes),
        #rq.List('rates', RandR_Rates) #FIXME: Why does uncommenting this cause an error?
        )

def get_screen_info(self):
    """Retrieve information about the current and available configurations for
    the screen associated with this window.

    """
    return GetScreenInfo(
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        window=self,
        )


# version 1.2

class GetScreenSizeRange(rq.ReplyRequest):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(6),
        rq.RequestLength(),
        rq.Window('window'),
        )
    _reply = rq.Struct(
        rq.ReplyCode(),
        rq.Pad(1),
        rq.Card16('sequence_number'),
        rq.ReplyLength(),
        rq.Card16('min_width'),
        rq.Card16('min_height'),
        rq.Card16('max_width'),
        rq.Card16('max_height'),
        rq.Pad(16),
        )

def get_screen_size_range(self):
    """Retrieve the range of possible screen sizes. The screen may be set to
	any size within this range.

    """
    return GetScreenSizeRange(
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        window=self,
        )


class SetScreenSize(rq.Request):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(7),
        rq.RequestLength(),
        rq.Window('window'),
        rq.Card16('width'),
        rq.Card16('height'),
        rq.Card32('width_in_millimeters'),
        rq.Card32('height_in_millimeters'),
        )

def set_screen_size(self, width, height, width_in_millimeters=None, height_in_millimeters=None):
    return SetScreenSize(
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        window=self,
        width=width,
        height=height,
        width_in_millimeters=width_in_millimeters,
        height_in_millimeters=height_in_millimeters,
        )


class GetScreenResources(rq.ReplyRequest):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(8),
        rq.RequestLength(),
        rq.Window('window'),
        )
    _reply = rq.Struct(
        rq.ReplyCode(),
        rq.Pad(1),
        rq.Card16('sequence_number'),
        rq.ReplyLength(),
        rq.Card32('timestamp'),
        rq.Card32('config_timestamp'),
        rq.LengthOf('crtcs', 2),
        rq.LengthOf('outputs', 2),
        rq.LengthOf('modes', 2),
        rq.LengthOf('mode_names', 2),
        rq.Pad(8),
        rq.List('crtcs', rq.Card32Obj),
        rq.List('outputs', rq.Card32Obj),
        rq.List('modes', RandR_ModeInfo),
        rq.String8('mode_names'),
        )

def get_screen_resources(self):
    return GetScreenResources(
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        window=self,
        )


class GetOutputInfo(rq.ReplyRequest):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(9),
        rq.RequestLength(),
        rq.Card32('output'),
        rq.Card32('config_timestamp'),
        )
    _reply = rq.Struct(
        rq.ReplyCode(),
        rq.Card8('status'),
        rq.Card16('sequence_number'),
        rq.ReplyLength(),
        rq.Card32('timestamp'),
        rq.Card32('crtc'),
        rq.Card32('mm_width'),
        rq.Card32('mm_height'),
        rq.Card8('connection'),
        rq.Card8('subpixel_order'),
        rq.LengthOf('crtcs', 2),
        rq.LengthOf('modes', 2),
        rq.Card16('num_preferred'),
        rq.LengthOf('clones', 2),
        rq.LengthOf('name', 2),
        rq.List('crtcs', rq.Card32Obj),
        rq.List('modes', rq.Card32Obj),
        rq.List('clones', rq.Card32Obj),
        rq.String8('name'),
        )

def get_output_info(self, output, config_timestamp):
    return GetOutputInfo(
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        output=output,
        config_timestamp=config_timestamp,
        )


class ListOutputProperties(rq.ReplyRequest):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(10),
        rq.RequestLength(),
        rq.Card32('output'),
        )
    _reply = rq.Struct(
        rq.ReplyCode(),
        rq.Pad(1),
        rq.Card16('sequence_number'),
        rq.ReplyLength(),
        rq.LengthOf('atoms', 2),
        rq.Pad(22),
        rq.List('atoms', rq.Card32Obj),
        )

def list_output_properties(self, output):
    return ListOutputProperties (
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        output=output,
        )


class QueryOutputProperty(rq.ReplyRequest):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(11),
        rq.RequestLength(),
        rq.Card32('output'),
        rq.Card32('property'),
        )
    _reply = rq.Struct(
        rq.ReplyCode(),
        rq.Pad(1),
        rq.Card16('sequence_number'),
        rq.ReplyLength(),
        rq.Bool('pending'),
        rq.Bool('range'),
        rq.Bool('immutable'),
        rq.Pad(21),
        rq.List('valid_values', rq.Card32Obj),
        )

def query_output_property(self, output, property):
    return QueryOutputProperty (
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        output=output,
        property=property,
        )


class ConfigureOutputProperty (rq.Request):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(12),
        rq.RequestLength(),
        rq.Card32('output'),
        rq.Card32('property'),
        rq.Bool('pending'),
        rq.Bool('range'),
        rq.Pad(2),
        rq.List('valid_values', rq.Card32Obj),
        )

def configure_output_property (self, output, property):
    return ConfigureOutputProperty (
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        output=output,
        property=property,
        )


class ChangeOutputProperty(rq.Request):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(13),
        rq.RequestLength(),
        rq.Card32('output'),
        rq.Card32('property'),
        rq.Card32('type'),
        rq.Format('value', 1),
        rq.Card8('mode'),
        rq.Pad(2),
        rq.LengthOf('value', 4),
        rq.PropertyData('value'),
        )

def change_output_property(self, output, property, type, mode, value):
    return ChangeOutputProperty(
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        output=output,
        property=property,
        type=type,
        mode=mode,
        value=value,
        )


class DeleteOutputProperty(rq.Request):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(14),
        rq.RequestLength(),
        rq.Card32('output'),
        rq.Card32('property'),
        )

def delete_output_property(self, output, property):
    return DeleteOutputProperty(
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        output=output,
        property=property,
        )


class GetOutputProperty(rq.ReplyRequest):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(15),
        rq.RequestLength(),
        rq.Card32('output'),
        rq.Card32('property'),
        rq.Card32('type'),
        rq.Card32('long_offset'),
        rq.Card32('long_length'),
        rq.Bool('delete'),
        rq.Bool('pending'),
        rq.Pad(2),
        )
    _reply = rq.Struct(
        rq.ReplyCode(),
        rq.Format('value', 1),
        rq.Card16('sequence_number'),
        rq.ReplyLength(),
        rq.Card32('property_type'),
        rq.Card32('bytes_after'),
        rq.LengthOf('value', 4),
        rq.Pad(12),
        rq.List('value', rq.Card8Obj),
        )

def get_output_property(self, output, property, type, long_offset, long_length, delete=False, pending=False):
    return GetOutputProperty(
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        output=output,
        property=property,
        type=type,
        long_offset=long_offset,
        long_length=long_length,
        delete=delete,
        pending=pending,
        )


class CreateMode(rq.ReplyRequest):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(16),
        rq.RequestLength(),
        rq.Window('window'),
        rq.Object('mode', RandR_ModeInfo),
        rq.String8('name'),
        )
    _reply = rq.Struct(
        rq.ReplyCode(),
        rq.Pad(1),
        rq.Card16('sequence_number'),
        rq.ReplyLength(),
        rq.Card32('mode'),
        rq.Pad(20),
        )

def create_mode(self, mode, name):
    return CreateMode (
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        window=self,
        mode=mode,
        name=name,
        )


class DestroyMode(rq.Request):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(17),
        rq.RequestLength(),
        rq.Card32('mode'),
        )

def destroy_mode(self, mode):
    return DestroyMode(
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        mode=mode,
        )


class AddOutputMode(rq.Request):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(18),
        rq.RequestLength(),
        rq.Card32('output'),
        rq.Card32('mode'),
        )

def add_output_mode(self, output, mode):
    return AddOutputMode(
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        output=output,
        mode=mode,
        )


class DeleteOutputMode(rq.Request):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(19),
        rq.RequestLength(),
        rq.Card32('output'),
        rq.Card32('mode'),
        )

def delete_output_mode(self, output, mode):
    return DeleteOutputMode(
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        output=output,
        mode=mode,
        )


class GetCrtcInfo(rq.ReplyRequest):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(20),
        rq.RequestLength(),
        rq.Card32('crtc'),
        rq.Card32('config_timestamp'),
        )
    _reply = rq.Struct(
        rq.ReplyCode(),
        rq.Card8('status'),
        rq.Card16('sequence_number'),
        rq.ReplyLength(),
        rq.Card32('timestamp'),
        rq.Int16('x'),
        rq.Int16('y'),
        rq.Card16('width'),
        rq.Card16('height'),
        rq.Card32('mode'),
        rq.Card16('rotation'),
        rq.Card16('possible_rotations'),
        rq.LengthOf('outputs', 2),
        rq.LengthOf('possible_outputs', 2),
        rq.List('outputs', rq.Card32Obj),
        rq.List('possible_outputs', rq.Card32Obj),
        )

def get_crtc_info(self, crtc, config_timestamp):
    return GetCrtcInfo (
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        crtc=crtc,
        config_timestamp=config_timestamp,
        )


class SetCrtcConfig(rq.ReplyRequest):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(21),
        rq.RequestLength(),
        rq.Card32('crtc'),
        rq.Card32('timestamp'),
        rq.Card32('config_timestamp'),
        rq.Int16('x'),
        rq.Int16('y'),
        rq.Card32('mode'),
        rq.Card16('rotation'),
        rq.Pad(2),
        rq.List('outputs', rq.Card32Obj),
        )
    _reply = rq.Struct(
        rq.ReplyCode(),
        rq.Card8('status'),
        rq.Card16('sequence_number'),
        rq.ReplyLength(),
        rq.Card32('new_timestamp'),
        rq.Pad(20),
        )

def set_crtc_config(self, crtc, config_timestamp, x, y, mode, rotation, outputs, timestamp=X.CurrentTime):
    return SetCrtcConfig (
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        crtc=crtc,
        config_timestamp=config_timestamp,
        x=x,
        y=y,
        mode=mode,
        rotation=rotation,
        outputs=outputs,
        timestamp=timestamp,
        )


class GetCrtcGammaSize(rq.ReplyRequest):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(22),
        rq.RequestLength(),
        rq.Card32('crtc'),
        )
    _reply = rq.Struct(
        rq.ReplyCode(),
        rq.Card8('status'),
        rq.Card16('sequence_number'),
        rq.ReplyLength(),
        rq.Card16('size'),
        rq.Pad(22),
        )

def get_crtc_gamma_size(self, crtc):
    return GetCrtcGammaSize (
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        crtc=crtc,
        )


class GetCrtcGamma(rq.ReplyRequest):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(23),
        rq.RequestLength(),
        rq.Card32('crtc'),
        )
    _reply = rq.Struct(
        rq.ReplyCode(),
        rq.Card8('status'),
        rq.Card16('sequence_number'),
        rq.ReplyLength(),
        rq.LengthOf(('red', 'green', 'blue'), 2),
        rq.Pad(22),
        rq.List('red', rq.Card16Obj),
        rq.List('green', rq.Card16Obj),
        rq.List('blue', rq.Card16Obj),
        )

def get_crtc_gamma(self, crtc):
    return GetCrtcGamma (
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        crtc=crtc,
        )


class SetCrtcGamma(rq.Request):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(24),
        rq.RequestLength(),
        rq.Card32('crtc'),
        rq.Card16('size'),
        rq.Pad(2),
        rq.List('red', rq.Card16Obj),
        rq.List('green', rq.Card16Obj),
        rq.List('blue', rq.Card16Obj),
        )

def set_crtc_gamma(self, crtc, size, red, green, blue):
    return SetCrtcGamma(
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        crtc=crtc,
        size=size,
        red=red,
        green=green,
        blue=blue,
        )


# version 1.3

class GetScreenResourcesCurrent(rq.ReplyRequest):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(25),
        rq.RequestLength(),
        rq.Window('window'),
        )
    _reply = rq.Struct(
        rq.ReplyCode(),
        rq.Pad(1),
        rq.Card16('sequence_number'),
        rq.ReplyLength(),
        rq.Card32('timestamp'),
        rq.Card32('config_timestamp'),
        rq.LengthOf('crtcs', 2),
        rq.LengthOf('outputs', 2),
        rq.LengthOf('modes', 2),
        rq.LengthOf('names', 2),
        rq.Pad(8),
        rq.List('crtcs', rq.Card32Obj),
        rq.List('outputs', rq.Card32Obj),
        rq.List('modes', RandR_ModeInfo),
        rq.String8('names'),
        )

def get_screen_resources_current(self):
    return GetScreenResourcesCurrent(
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        window=self,
        )


class SetCrtcTransform(rq.Request):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(26),
        rq.RequestLength(),
        rq.Card32('crtc'),
        rq.Object('transform', Render_Transform),
        rq.LengthOf('filter_name', 2),
        rq.Pad(2),
        rq.String8('filter_name'),
        rq.List('filter_params', rq.Card32Obj), #FIXME: The protocol says FIXED? http://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt#n2161
        )

def set_crtc_transform(self, crtc, n_bytes_filter):
    return SetCrtcTransform(
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        crtc=crtc,
        n_bytes_filter=n_bytes_filter,
        )


class GetCrtcTransform(rq.ReplyRequest):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(27),
        rq.RequestLength(),
        rq.Card32('crtc'),
        )
    _reply = rq.Struct(
        rq.ReplyCode(),
        rq.Card8('status'),
        rq.Card16('sequence_number'),
        rq.ReplyLength(),
        rq.Object('pending_transform', Render_Transform),
        rq.Bool('has_transforms'),
        rq.Pad(3),
        rq.Object('current_transform', Render_Transform),
        rq.Pad(4),
        rq.LengthOf('pending_filter_name', 2),
        rq.LengthOf('pending_filter_params', 2),
        rq.LengthOf('current_filter_name', 2),
        rq.LengthOf('current_filter_params', 2),
        rq.String8('pending_filter_name'),
        rq.List('pending_filter_params', rq.Card32Obj), #FIXME: The protocol says FIXED? http://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt#n2161
        rq.String8('current_filter_name'),
        rq.List('current_filter_params', rq.Card32Obj), #FIXME: The protocol says FIXED? http://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt#n2161
        )

def get_crtc_transform(self, crtc):
    return GetCrtcTransform(
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        crtc=crtc,
        )


class GetPanning(rq.ReplyRequest):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(28),
        rq.RequestLength(),
        rq.Card32('crtc'),
        )
    _reply = rq.Struct(
        rq.ReplyCode(),
        rq.Card8('status'),
        rq.Card16('sequence_number'),
        rq.ReplyLength(),
        rq.Card32('timestamp'),
        rq.Card16('left'),
        rq.Card16('top'),
        rq.Card16('width'),
        rq.Card16('height'),
        rq.Card16('track_left'),
        rq.Card16('track_top'),
        rq.Card16('track_width'),
        rq.Card16('track_height'),
        rq.Int16('border_left'),
        rq.Int16('border_top'),
        rq.Int16('border_right'),
        rq.Int16('border_bottom'),
        )

def get_panning(self, crtc):
    return GetPanning (
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        crtc=crtc,
        )


class SetPanning(rq.ReplyRequest):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(29),
        rq.RequestLength(),
        rq.Card32('crtc'),
        rq.Card32('timestamp'),
        rq.Card16('left'),
        rq.Card16('top'),
        rq.Card16('width'),
        rq.Card16('height'),
        rq.Card16('track_left'),
        rq.Card16('track_top'),
        rq.Card16('track_width'),
        rq.Card16('track_height'),
        rq.Int16('border_left'),
        rq.Int16('border_top'),
        rq.Int16('border_right'),
        rq.Int16('border_bottom'),
        )
    _reply = rq.Struct(
        rq.ReplyCode(),
        rq.Card8('status'),
        rq.Card16('sequence_number'),
        rq.ReplyLength(),
        rq.Card32('new_timestamp'),
        rq.Pad(20),
        )

def set_panning(self, crtc, left, top, width, height, track_left, track_top, track_width, track_height, border_left, border_top, border_width, border_height, timestamp=X.CurrentTime):
    return SetPanning (
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        crtc=crtc,
        left=left,
        top=top,
        width=width,
        height=height,
        track_left=track_left,
        track_top=track_top,
        track_width=track_width,
        track_height=track_height,
        border_left=border_left,
        border_top=border_top,
        border_width=border_width,
        border_height=border_height,
        timestamp=timestamp,
        )


class SetOutputPrimary(rq.Request):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(30),
        rq.RequestLength(),
        rq.Window('window'),
        rq.Card32('output'),
        )

def set_output_primary(self, output):
    return SetOutputPrimary(
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        window=self,
        output=output,
        )


class GetOutputPrimary(rq.ReplyRequest):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(31),
        rq.RequestLength(),
        rq.Window('window'),
        )
    _reply = rq.Struct(
        rq.ReplyCode(),
        rq.Pad(1),
        rq.Card16('sequence_number'),
        rq.ReplyLength(),
        rq.Card32('output'),
        rq.Pad(20),
        )

def get_output_primary(self):
    return GetOutputPrimary(
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        window=self,
        )


# Version 1.5 methods 

class GetMonitors(rq.ReplyRequest):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(42),
        rq.RequestLength(),
        rq.Window('window'),
        rq.Bool('is_active'),
        rq.Pad(3)
    )

    _reply = rq.Struct(
        rq.ReplyCode(),
        rq.Pad(1),
        rq.Card16('sequence_number'),
        rq.ReplyLength(),
        rq.Card32('timestamp'),
        rq.LengthOf('monitors', 4),
        rq.Card32('outputs'),
        rq.Pad(12),
        rq.List('monitors', MonitorInfo)
    )


def get_monitors(self, is_active=True):
    return GetMonitors(
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        window=self,
        is_active=is_active
    )

class SetMonitor(rq.Request):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(43),
        rq.RequestLength(),
        rq.Window('window'),
        rq.Object('monitor_info', MonitorInfo)
    )


def set_monitor(self, monitor_info):
    return SetMonitor(
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        window=self,
        monitor_info=monitor_info
    )


class DeleteMonitor(rq.Request):
    _request = rq.Struct(
        rq.Card8('opcode'),
        rq.Opcode(44),
        rq.RequestLength(),
        rq.Window('window'),
        rq.Card32('name')
    )


def delete_monitor(self, name):
    return DeleteMonitor(
        display=self.display,
        opcode=self.display.get_extension_major(extname),
        window=self,
        name=name
    )

# Events #

class ScreenChangeNotify(rq.Event):
    _code = None
    _fields = rq.Struct(
        rq.Card8('type'),
        rq.Card8('rotation'),
        rq.Card16('sequence_number'),
        rq.Card32('timestamp'),
        rq.Card32('config_timestamp'),
        rq.Window('root'),
        rq.Window('window'),
        rq.Card16('size_id'),
        rq.Card16('subpixel_order'),
        rq.Card16('width_in_pixels'),
        rq.Card16('height_in_pixels'),
        rq.Card16('width_in_millimeters'),
        rq.Card16('height_in_millimeters'),
        )


class CrtcChangeNotify(rq.Event):
    _code = None
    _fields = rq.Struct(
        rq.Card8('type'),
        rq.Card8('sub_code'),
        rq.Card16('sequence_number'),
        rq.Card32('timestamp'),
        rq.Window('window'),
        rq.Card32('crtc'),
        rq.Card32('mode'),
        rq.Card16('rotation'),
        rq.Pad(2),
        rq.Int16('x'),
        rq.Int16('y'),
        rq.Card16('width'),
        rq.Card16('height'),
        )


class OutputChangeNotify(rq.Event):
    _code = None
    _fields = rq.Struct(
        rq.Card8('type'),
        rq.Card8('sub_code'),
        rq.Card16('sequence_number'),
        rq.Card32('timestamp'),
        rq.Card32('config_timestamp'),
        rq.Window('window'),
        rq.Card32('output'),
        rq.Card32('crtc'),
        rq.Card32('mode'),
        rq.Card16('rotation'),
        rq.Card8('connection'),
        rq.Card8('subpixel_order'),
        )


class OutputPropertyNotify(rq.Event):
    _code = None
    _fields = rq.Struct(
        rq.Card8('type'),
        rq.Card8('sub_code'),
        rq.Card16('sequence_number'),
        rq.Window('window'),
        rq.Card32('output'),
        rq.Card32('atom'),
        rq.Card32('timestamp'),
        rq.Card8('state'),
        rq.Pad(11),
        )
# Initialization #

def init(disp, info):
    disp.extension_add_method('display', 'xrandr_query_version', query_version)
    disp.extension_add_method('window', 'xrandr_select_input', select_input)
    disp.extension_add_method('window', 'xrandr_get_screen_info', get_screen_info)
    disp.extension_add_method('drawable', 'xrandr_1_0set_screen_config', _1_0set_screen_config)
    disp.extension_add_method('drawable', 'xrandr_set_screen_config', set_screen_config)
    disp.extension_add_method('window', 'xrandr_get_screen_size_range', get_screen_size_range)
    disp.extension_add_method('window', 'xrandr_set_screen_size', set_screen_size)
    disp.extension_add_method('window', 'xrandr_get_screen_resources', get_screen_resources)
    disp.extension_add_method('display', 'xrandr_get_output_info', get_output_info)
    disp.extension_add_method('display', 'xrandr_list_output_properties', list_output_properties)
    disp.extension_add_method('display', 'xrandr_query_output_property', query_output_property)
    disp.extension_add_method('display', 'xrandr_configure_output_property ', configure_output_property )
    disp.extension_add_method('display', 'xrandr_change_output_property', change_output_property)
    disp.extension_add_method('display', 'xrandr_delete_output_property', delete_output_property)
    disp.extension_add_method('display', 'xrandr_get_output_property', get_output_property)
    disp.extension_add_method('window', 'xrandr_create_mode', create_mode)
    disp.extension_add_method('display', 'xrandr_destroy_mode', destroy_mode)
    disp.extension_add_method('display', 'xrandr_add_output_mode', add_output_mode)
    disp.extension_add_method('display', 'xrandr_delete_output_mode', delete_output_mode)
    disp.extension_add_method('display', 'xrandr_get_crtc_info', get_crtc_info)
    disp.extension_add_method('display', 'xrandr_set_crtc_config', set_crtc_config)
    disp.extension_add_method('display', 'xrandr_get_crtc_gamma_size', get_crtc_gamma_size)
    disp.extension_add_method('display', 'xrandr_get_crtc_gamma', get_crtc_gamma)
    disp.extension_add_method('display', 'xrandr_set_crtc_gamma', set_crtc_gamma)
    disp.extension_add_method('window', 'xrandr_get_screen_resources_current', get_screen_resources_current)
    disp.extension_add_method('display', 'xrandr_set_crtc_transform', set_crtc_transform)
    disp.extension_add_method('display', 'xrandr_get_crtc_transform', get_crtc_transform)
    disp.extension_add_method('window', 'xrandr_set_output_primary', set_output_primary)
    disp.extension_add_method('window', 'xrandr_get_output_primary', get_output_primary)
    disp.extension_add_method('display', 'xrandr_get_panning', get_panning)
    disp.extension_add_method('display', 'xrandr_set_panning', set_panning)

    # If the server is running RANDR 1.5+, enable 1.5 compatible methods and events
    version = query_version(disp)
    if version.major_version == 1 and version.minor_version >= 5:
        # version 1.5 compatible
        disp.extension_add_method('window', 'xrandr_get_monitors', get_monitors)
        disp.extension_add_method('window', 'xrandr_set_monitor', set_monitor)
        disp.extension_add_method('window', 'xrandr_delete_monitor', delete_monitor)

        disp.extension_add_event(info.first_event + RRScreenChangeNotify, ScreenChangeNotify)
        # add RRNotify events (1 event code with 3 subcodes)
        disp.extension_add_subevent(info.first_event + RRNotify, RRNotify_CrtcChange, CrtcChangeNotify)
        disp.extension_add_subevent(info.first_event + RRNotify, RRNotify_OutputChange, OutputChangeNotify)
        disp.extension_add_subevent(info.first_event + RRNotify, RRNotify_OutputProperty, OutputPropertyNotify)

        disp.extension_add_error(BadRROutput, BadRROutputError)
        disp.extension_add_error(BadRRCrtc, BadRRCrtcError)
        disp.extension_add_error(BadRRMode, BadRRModeError)
