# Xlib.ext.shape -- SHAPE extension module
#
#    Copyright (C) 2002 Jeffrey Boser <verin@lvcm.com>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,  USA


# Constants to use
#
# Regions of a window
ShapeBounding = 0       # the 'edge' of a shaped window
ShapeClip = 1           # the clipping region
# Shape Operations
ShapeSet = 0            # Set the region unmodified (dest=src)
ShapeUnion = 1          # Add the new region to the old (dest=src|dest)
ShapeIntersect = 2      # Use the intersection (dest=src&dest)
ShapeSubtract = 3       # remove region (dest = dest - intersect)
ShapeInvert = 4         # opposite of subtract (dest = src - intersect)
# Events
ShapeNotifyMask = (1<<0)        #a keypress mask?
ShapeNotify = 0                 #still unsure of these values

# How to Use
# The basic functions that change the shapes of things are:
#   shape_rectangles (uses a set of rectangles as the source)
#       operation, region, ordering, rects
#   shape_mask (uses a bitmap as the source)
#       operation, region, x_offset, y_offset, bitmap
#   shape_combine (uses a window as the source)
#       operation, src_region, dest_region, x_offset, y_offset, src_window
#   shape_offset (moves the region)
#       region, x_offset, y_offset
# The functions to find stuff out (these three return mappings of field/values):
#   shape_query_version (shape extension version)
#       major_version, minor_version
#   shape_query_extents (rectangle boundaries of a window's regions)
#       clip_shaped, clip_x, clip_y, clip_width, clip_height,
#       bounding_shaped, bounding_x, bounding_y, bounding_width, bounding_height
#   shape_input_selected (if the window products shapenotify events)
#       enabled
#   shape_get_rectangles (the rectangles set by shape_rectangles)
#       ordering, rects
# And to turn on shape notify events:
#   shape_select_input
#       enable



from Xlib import X
from Xlib.protocol import rq, structs

extname = 'SHAPE'

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

def query_version(self):
    return QueryVersion(
        display = self.display,
        opcode = self.display.get_extension_major(extname),
        )



class Rectangles(rq.Request):
    _request = rq.Struct(
            rq.Card8('opcode'),
            rq.Opcode(1),
            rq.RequestLength(),
            rq.Card8('operation'),
            rq.Set('region', 1, (ShapeBounding, ShapeClip)),
            rq.Card8('ordering'),
            rq.Pad(1),
            rq.Window('window'),
            rq.Int16('x'),
            rq.Int16('y'),
            rq.List('rectangles', structs.Rectangle),
            )

def rectangles(self, region, operation, ordering, x, y, rectangles):
    Rectangles(
            display = self.display,
            opcode = self.display.get_extension_major(extname),
            operation = operation,
            region = region,
            ordering = ordering,
            window = self.id,
            x = x,
            y = y,
            rectangles = rectangles,
            )



class Mask(rq.Request):
    _request = rq.Struct(
            rq.Card8('opcode'),
            rq.Opcode(2),
            rq.RequestLength(),
            rq.Card8('operation'),
            rq.Set('region', 1, (ShapeBounding, ShapeClip)),
            rq.Pad(2),
            rq.Window('window'),
            rq.Int16('x'),
            rq.Int16('y'),
            rq.Pixmap('source', (X.NONE, )),
            )

def mask(self, operation, region, x, y, source):
    Mask(display = self.display,
         opcode = self.display.get_extension_major(extname),
         window = self.id,
         operation = operation,
         region = region,
         x = x,
         y = y,
         source = source,
         )


class Combine(rq.Request):
    _request = rq.Struct(
            rq.Card8('opcode'),
            rq.Opcode(3),
            rq.RequestLength(),
            rq.Card8('operation'),
            rq.Set('dest_region', 1, (ShapeBounding, ShapeClip)),
            rq.Set('source_region', 1, (ShapeBounding, ShapeClip)),
            rq.Pad(1),
            rq.Window('dest'),
            rq.Int16('x'),
            rq.Int16('y'),
            rq.Window('source'),
            )

def combine(self, operation, region, source, source_region, x, y):
    Combine(
            display = self.display,
            opcode = self.display.get_extension_major(extname),
            operation = operation,
            dest_region = region,
            source_region = source_region,
            dest = self.id,
            x = x,
            y = y,
            source = source,
            )



class Offset(rq.Request):
    _request = rq.Struct(
            rq.Card8('opcode'),
            rq.Opcode(4),
            rq.RequestLength(),
            rq.Set('region', 1, (ShapeBounding, ShapeClip)),
            rq.Pad(3),
            rq.Window('window'),
            rq.Int16('x'),
            rq.Int16('y'),
            )

def offset(self, region, x, y):
    Offset(
            display = self.display,
            opcode = self.display.get_extension_major(extname),
            region = region,
            window = self.id,
            x = x,
            y = y,
            )



class QueryExtents(rq.ReplyRequest):
    _request = rq.Struct(
            rq.Card8('opcode'),
            rq.Opcode(5),
            rq.RequestLength(),
            rq.Window('window'),
            )

    _reply = rq.Struct(
            rq.ReplyCode(),
            rq.Pad(1),
            rq.Card16('sequence_number'),
            rq.ReplyLength(),
            rq.Bool('bounding_shaped'),
            rq.Bool('clip_shaped'),
            rq.Pad(2),
            rq.Int16('bounding_x'),
            rq.Int16('bounding_y'),
            rq.Card16('bounding_width'),
            rq.Card16('bounding_height'),
            rq.Int16('clip_x'),
            rq.Int16('clip_y'),
            rq.Card16('clip_width'),
            rq.Card16('clip_height'),
            rq.Pad(4),
            )

def query_extents(self):
    return QueryExtents(
        display = self.display,
        opcode = self.display.get_extension_major(extname),
        window = self.id,
        )


class SelectInput(rq.Request):
    _request = rq.Struct(
            rq.Card8('opcode'),
            rq.Opcode(6),
            rq.RequestLength(),
            rq.Window('window'),
            rq.Bool('enable'),
            rq.Pad(3),
            )

def select_input(self, enable = 1):
    SelectInput(
            display = self.display,
            opcode = self.display.get_extension_major(extname),
            window = self.id,
            enable = enable,
            )


class InputSelected(rq.ReplyRequest):
    _request = rq.Struct(
            rq.Card8('opcode'),
            rq.Opcode(7),
            rq.RequestLength(),
            rq.Window('window'),
            )

    _reply = rq.Struct(
            rq.ReplyCode(),
            rq.Bool('enabled'),
            rq.Card16('sequence_number'),
            rq.ReplyLength(),
            rq.Pad(24),
            )

def input_selected(self):
    reply = InputSelected(
            display = self.display,
            opcode = self.display.get_extension_major(extname),
            window = self.id,
            )
    return reply.enabled


class GetRectangles(rq.ReplyRequest):
    _request = rq.Struct(
            rq.Card8('opcode'),
            rq.Opcode(8),
            rq.RequestLength(),
            rq.Window('window'),
            rq.Set('region', 1, (ShapeBounding, ShapeClip)),
            rq.Pad(3),
            )

    _reply = rq.Struct(
            rq.ReplyCode(),
            rq.Card8('ordering'),
            rq.Card16('sequence_number'),
            rq.ReplyLength(),
            rq.LengthOf('rectangles', 4),
            rq.Pad(20),
            rq.List('rectangles', structs.Rectangle),
            )

def get_rectangles(self, region):
    return GetRectangles(
        display = self.display,
        opcode = self.display.get_extension_major(extname),
        window = self.id,
        region = region,
        )


class ShapeNotify(rq.Event):
    _code = None
    _fields = rq.Struct( rq.Card8('type'),
                         rq.Set('region', 1, (ShapeBounding, ShapeClip)),
                         rq.Card16('sequence_number'),
                         rq.Window('window'),
                         rq.Int16('x'),
                         rq.Int16('y'),
                         rq.Card16('width'),
                         rq.Card16('height'),
                         rq.Card32('time'),
                         rq.Bool('shaped'),
                         rq.Pad(11),
                         )

def init(disp, info):
    disp.extension_add_method('display', 'shape_query_version', query_version )
    disp.extension_add_method('window', 'shape_rectangles',     rectangles )
    disp.extension_add_method('window', 'shape_mask',           mask )
    disp.extension_add_method('window', 'shape_combine',        combine )
    disp.extension_add_method('window', 'shape_offset',         offset )
    disp.extension_add_method('window', 'shape_query_extents',  query_extents )
    disp.extension_add_method('window', 'shape_select_input',   select_input )
    disp.extension_add_method('window', 'shape_input_selected', input_selected )
    disp.extension_add_method('window', 'shape_get_rectangles', get_rectangles )

    disp.extension_add_event(info.first_event, ShapeNotify)
