#!/usr/bin/perl
#
# EWMHbus
# v0.01
# Author: Thomas Thurman <thomas.thurman@collabora.co.uk>
# Copyright (C) 2010 Collabora Ltd
#
# This is an interface between D-Bus and the EWMH.
#
# This is a proof-of-concept program.  To make it useful it should:
#   1) monitor changes in the window list and update the objects
#      which are exported to the bus
#   2) expose more methods than just "Maximize"
#   3) not use org.marnanel.* as the domain :)
#   4) be written in C; this is in Perl for ease of prototyping
#
# Feedback is welcome.
# 
# 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., 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.

################################################################
#
# This is a class which represents a window.
#

package EWMHBus::Window;

use strict;
use warnings;
use Net::DBus::Exporter qw(org.marnanel.ewmhbus.Window);
use base qw(Net::DBus::Object);
use X11::Protocol;

my $x11 = X11::Protocol->new();

$x11->{event_handler} = sub {
    # we don't much care about incoming events
};

# What are we exporting?

dbus_method("Maximize", [], []);

# Constructor.
sub new {
    my ($class, $service, $id) = @_;
    my $self = $class->SUPER::new($service, "/org/marnanel/ewmhbus/Window/".$id);
    bless $self, $class;
    return $self;
}

# Class method.
# Convenience function to return the values of a window property
# which consists of a sequence of long ints.
sub get_prop {
    my ($win, $atom) = @_;
    my ($p) = $x11->GetProperty($win, $x11->atom($atom),
				'AnyPropertyType', 0, -1, 0);
    return unpack("L*", $p);
}

# Class method.
# Returns the current list of toplevel windows.
sub get_client_list {
    return get_prop($x11->{root}, '_NET_CLIENT_LIST_STACKING');
}

# Exported to D-Bus.
# Maximises this window.
sub Maximize {
    my ($self) = @_;
    my $path = $self->get_object_path();
    my ($id) = $path =~ /\/([0-9a-f]+)$/;

    $x11->SendEvent($x11->{root}, 0,
		    $x11->pack_event_mask('SubstructureNotify',
					  'SubstructureRedirect'),
		    $x11->pack_event(
		        name => 'ClientMessage',
		        window => hex($id),
		        type => $x11->atom('_NET_WM_STATE'),
		        format => 32,
      data => pack('LLLLL',
        1, # add states
        $x11->atom('_NET_WM_STATE_MAXIMIZED_VERT'),
        $x11->atom('_NET_WM_STATE_MAXIMIZED_HORZ'),
        2, # we are a pager
        0,
     )));

    $x11->flush();
}

################################################################
#
# The centre of operations.
#

package main;

use strict;
use warnings;
use Net::DBus;
use Net::DBus::Reactor;

my $bus = Net::DBus->find;
my $service = $bus->export_service("org.marnanel.ewmhbus");
for my $win (EWMHBus::Window::get_client_list()) {
   my $object = EWMHBus::Window->new($service, sprintf("%x", $win));
}

Net::DBus::Reactor->main->run;

################################################################
#
# EOF ewmhbus.pl
