#!/usr/bin/env python

# This file is part of Window-Switch.
# Copyright (c) 2009-2013 Antoine Martin <antoine@nagafix.co.uk>
# Window-Switch is released under the terms of the GNU GPL v3

import os

from winswitch.consts import ROOT_WINDOW_UUID_PROPERTY, ROOT_WINDOW_DESKTOPSESSION_PROPERTY, ROOT_WINDOW_PULSE_SERVER, ROOT_WINDOW_PULSE_CLONE_DEVICE
from winswitch.util.common import is_valid_file, is_valid_dir, csv_list, visible_command, is_our_display
from winswitch.util.process_util import get_command_for_pid, get_output
from winswitch.util.icon_cache import guess_icon_from_name
from winswitch.util.commands_util import XPROP_COMMAND, XSET_COMMAND, XDPYINFO_COMMAND
from winswitch.util.simple_logger import Logger

DEBUG_DISPLAY_ACCESS=False
COMMAND_VIA_PROC = True			#parse /proc to find command name from its pid



class DisplayUtil:

	def __init__(self):
		Logger(self, log_colour=Logger.GREEN)

	def xprop_get(self, display, prop, remove_spaces=True):
		"""
		Retrieve a string property from the root window of the display.
		Either via pygtk, or via xprop_get.
		"""
		value = None
		try:
			if is_our_display(display):
				value = self.get_gdk_root_prop(display, prop)
			else:
				value = self.get_xprop_via_command(display, prop)
			if value is None:
				return	None
			value = str(value)
			if len(value)>0 and value[-1]=="\0":
				value = value[:-1]
			if remove_spaces:
				value = value.replace(" ", "")
		except Exception, e:
			self.serr(None, e, display, prop, remove_spaces)
		return value

	def get_gdk_root_prop(self, display, prop):
		import pygtk
		pygtk.require("2.0")
		import gtk.gdk
		root = gtk.gdk.get_default_root_window()
		value = root.property_get(gtk.gdk.atom_intern(prop, False), gtk.gdk.atom_intern("STRING", False), False)
		if DEBUG_DISPLAY_ACCESS:
			self.sdebug("display=%s, property_get(%s)=%s" % (display, prop, value), root)
		if value is None:
			return	None
		#(type, format, data) = value
		(_, _, data) = value
		return	data

	def get_xprop_via_command(self, display, prop):
		if not XPROP_COMMAND:
			return	None
		cmd = [XPROP_COMMAND, "-display", display, "-root", prop]
		code, out, _ = get_output(cmd)
		if code!=0:
			self.serror("%s returncode=%s" % (cmd, code), display, prop)
			return	None
		self.slog("stdout(%s)=%s" % (cmd, visible_command(out)), display, prop)
		if not out:
			return	None
		eqpos = out.rfind("=")
		if eqpos<=0:
			if out.find("no such atom on any window"):
				self.sdebug("no such atom on any window", display, prop)
				return	None
			self.serror("malformed output, cannot find '=' sign in '%s'" % visible_command(out), display, prop)
			return	None
		value = out[eqpos+1:]
		value = value.replace('"', '').strip()
		return	value

	def xprop_set(self, display, prop, value):
		if not XPROP_COMMAND:
			return	None
		try:
			cmd = [XPROP_COMMAND, "-display", display, "-root", "-f", prop, "8s", "-set", prop, str(value)]
			code, out, _ = get_output(cmd)
			self.slog("output(%s)=%s" % (cmd, out), display, prop)
			return code==0
		except Exception, e:
			self.serror("%s" % e, display)
			return False

	def get_local_display_owner(self, display):
		return	self.xprop_get(display, ROOT_WINDOW_UUID_PROPERTY)

	def get_display_command(self, display, pid):
		cmd = self.xprop_get(display, ROOT_WINDOW_DESKTOPSESSION_PROPERTY)
		if cmd:
			return	cmd
		if COMMAND_VIA_PROC and pid and pid>0:
			return	get_command_for_pid(pid)

	def get_local_pulse_server(self, display):
		"""
		Find the pulseaudio server using its X11 window properties.
		"""
		pulse_prop = self.xprop_get(display, ROOT_WINDOW_PULSE_SERVER, False)
		#this is a list of addresses which can be used seperated by spaces
		pulse_address = pulse_prop
		if pulse_prop:
			addresses = pulse_prop.split(" ")
			self.sdebug("split(%s)=%s" % (pulse_prop, csv_list(addresses)))
			pulse_address = addresses[0]			#default to the first one
			for x in addresses:
				pos = x.find("unix:/")				#prefer unix sockets, but trim the leading cookie
				if pos>=0:
					pulse_address = x[pos:]
					break
		else:
			#I've tried using dbus (doc missing - examples given don't work..)
			#I've also tried with pax11publish (published unusable crap on laptop..)
			#So this is the only way I found (very unclean, look away now)
			import os.path
			dotpulse = os.path.expanduser("~/.pulse")
			if not is_valid_dir(dotpulse):
				self.sdebug("pulseaudio server not detected: directory '%s' not found" % dotpulse, display)
			else:
				self.sdebug("looking for pulse in %s" % dotpulse, display)
				try:
					files = os.listdir(dotpulse)
					for f in files:
						if not f.endswith("runtime"):
							continue
						runtime = os.path.join(dotpulse, f)
						if not is_valid_dir(runtime):
							continue
						self.sdebug("found valid runtime: %s" % runtime, display)
						socket = os.path.join(runtime, "native")
						if not os.path.exists(socket):
							continue
						if not is_valid_file(socket) and not is_valid_dir(socket):
							pulse_address = "unix:%s" % socket
							break
				except Exception, e:
					self.serr("failed to get pulseaudio from %s" % dotpulse, e, display)
		self.slog("pulse server address(%s)=%s" % (pulse_prop, pulse_address), display)
		return	pulse_address


	def get_clone_device(self, display):
		return	self.xprop_get(display, ROOT_WINDOW_PULSE_CLONE_DEVICE, False)

	def get_display_icon_data(self, command):
		icon = guess_icon_from_name(command)
		if icon:
			return	icon
		try:
			from winswitch.ui.icons import getraw
			return getraw("X11")
		except:
			return	None

	def get_display_size(self, display):
		size = None
		if is_our_display(display):
			size = self.get_gdk_root_size()
		if size is None:
			size = self.get_xdpyinfo_display_size(display)
		return	size

	def get_gdk_root_size(self):
		import pygtk
		pygtk.require("2.0")
		import gtk.gdk
		return "%sx%s" % (gtk.gdk.screen_width(), gtk.gdk.screen_height())

	def get_xdpyinfo_display_size(self, display):
		if not XDPYINFO_COMMAND:
			return	None
		try:
			cmd = [XDPYINFO_COMMAND, "-display", display]
			code, out, _ = get_output(cmd)
			if code!=0:
				return	None
		except Exception, e:
			self.serr(None, e, display)
		w = 0
		h = 0
		screen = ""
		for line in out.split("\n"):
			#self.sdebug("xdpyinfo output=%s" % line, display_no)
			parts = line.split(" ")
			nospace = []
			for part in parts:
				if len(part)>0:
					nospace.append(part)
			if len(nospace)>=2:
				if nospace[0] == "screen":
					screen = nospace[1].replace(":", "")
				if nospace[0] == "dimensions:":
					dim = nospace[1].split("x")		#ie: 1440x900 -> [1440,900]
					self.slog("xdpyinfo screen %s: %s" % (screen, str(dim)), display)
					w += int(dim[0])
					h += int(dim[1])
		return	"%sx%s" % (w,h)

	def can_access_local_display(self, display_no):
		cmd = [XSET_COMMAND, "q"]		#use "xset q" to test access to the server
		env = os.environ.copy()
		env["DISPLAY"] = ":%s" % display_no
		code = -1
		try:
			code, out, err = get_output(cmd, env=env)
			self.sdebug("'%s' returncode: %s" % (cmd, code))
			self.sdebug("stdout: %s" % out)
			self.sdebug("stderr: %s" % err)
		except Exception, e:
			self.serror("failed to access display: %s" % e)
		return	code==0

	def take_screenshot(self, ID, display, env, filename, ok_callback, err_callback):
		""" Take a screenshot of the given display, resizing it to a thumbnail stored in filename.
			Then pass the data to ok_callback """
		from winswitch.ui.capture_util import screen_capture
		screen_capture(self.config.screen_capture_command, display, env, filename, ok_callback, err_callback)
