#!/usr/bin/env python
#
# examples/get_selection.py -- demonstrate getting selections
# (equivalent to pasting from the clipboard)
#
#	Copyright (C) 2013 Peter Liljenberg <peter.liljenberg@gmail.com>
#
# 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

import binascii
import sys
import os

# Change path so we find Xlib
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))

from Xlib import X, display, Xutil, Xatom


def log(msg, *args):
    sys.stderr.write(msg.format(*args) + '\n')

def error(msg, *args):
    log(msg, *args)
    sys.exit(1)


def main():
    if len(sys.argv) < 2 or len(sys.argv) > 3:
        sys.exit('usage: {0} SELECTION [TYPE]\n\n'
                 'SELECTION is typically PRIMARY, SECONDARY or CLIPBOARD.\n'
                 'If TYPE is omitted, the available types for the selction are listed.'
                 .format(sys.argv[0]))

    d = display.Display()

    sel_name = sys.argv[1]
    sel_atom = d.get_atom(sel_name)

    if len(sys.argv) > 2:
        target_name = sys.argv[2]
        target_atom = d.get_atom(target_name)
    else:
        target_name = 'TARGETS'
        target_atom = d.get_atom('TARGETS')

    # Ask the server who owns this selection, if any
    owner = d.get_selection_owner(sel_atom)

    if owner == X.NONE:
        log('No owner for selection {0}', sel_name)
        return

    log('selection {0} owner: 0x{1:08x} {2}',
        sel_name, owner.id, owner.get_wm_name())

    # Create ourselves a window and a property for the returned data
    w = d.screen().root.create_window(
        0, 0, 10, 10, 0, X.CopyFromParent)
    w.set_wm_name(os.path.basename(sys.argv[0]))

    data_atom = d.get_atom('SEL_DATA')

    # The data_atom should not be set according to ICCCM, and since
    # this is a new window that is already the case here.

    # Ask for the selection.  We shouldn't use X.CurrentTime, but
    # since we don't have an event here we have to.
    w.convert_selection(sel_atom, target_atom, data_atom, X.CurrentTime)

    # Wait for the notification that we got the selection
    while True:
        e = d.next_event()
        if e.type == X.SelectionNotify:
            break

    # Do some sanity checks
    if (e.requestor != w
        or e.selection != sel_atom
        or e.target != target_atom):
        error('SelectionNotify event does not match our request: {0}', e)

    if e.property == X.NONE:
        log('selection lost or conversion to {0} failed',
            target_name)
        return

    if e.property != data_atom:
        error('SelectionNotify event does not match our request: {0}', e)

    # Get the data
    r = w.get_full_property(data_atom, X.AnyPropertyType,
                            sizehint = 10000)

    # Can the data be used directly or read incrementally
    if r.property_type == d.get_atom('INCR'):
        log('reading data incrementally: at least {0} bytes', r.value[0])
        handle_incr(d, w, data_atom, target_name)
    else:
        output_data(d, r, target_name)

    # Tell selection owner that we're done
    w.delete_property(data_atom)


def handle_incr(d, w, data_atom, target_name):
    # This works by us removing the data property, the selection owner
    # getting a notification of that, and then setting the property
    # again with more data.  To notice that, we must listen for
    # PropertyNotify events.
    w.change_attributes(event_mask = X.PropertyChangeMask)

    while True:
        # Delete data property to tell owner to give us more data
        w.delete_property(data_atom)

        # Wait for notification that we got data
        while True:
            e = d.next_event()
            if (e.type == X.PropertyNotify
                and e.state == X.PropertyNewValue
                and e.window == w
                and e.atom == data_atom):
                break

        r = w.get_full_property(data_atom, X.AnyPropertyType,
                                sizehint = 10000)

        # End of data
        if len(r.value) == 0:
            return

        output_data(d, r, target_name)
        # loop around


def output_data(d, r, target_name):
    log('got {0}:{1}, length {2}',
        d.get_atom_name(r.property_type),
        r.format,
        len(r.value))

    if r.format == 8:
        if r.property_type == Xatom.STRING:
            value = r.value.decode('ISO-8859-1')
        elif r.property_type == d.get_atom('UTF8_STRING'):
            value = r.value.decode('UTF-8')
        else:
            value = binascii.hexlify(r.value).decode('ascii')
        sys.stdout.write(value)

    elif r.format == 32 and r.property_type == Xatom.ATOM:
        for v in r.value:
            sys.stdout.write('{0}\n'.format(d.get_atom_name(v)))

    else:
        for v in r.value:
            sys.stdout.write('{0}\n'.format(v))



if __name__ == '__main__':
    main()
