////////////////////////////////////////////////////////////////////////////////
// -------------------------------------------------------------------------- //
//                                                                            //
//                       (C) 2010-2016 Robot Developers                       //
//                       See LICENSE for licensing info                       //
//                                                                            //
// -------------------------------------------------------------------------- //
////////////////////////////////////////////////////////////////////////////////

//----------------------------------------------------------------------------//
// Prefaces                                                                   //
//----------------------------------------------------------------------------//

#include "Window.h"
#include "Process.h"
using std::string;

#include <regex>
using std::regex;
using std::regex_match;

#ifdef ROBOT_OS_LINUX

	#include <X11/Xlib.h>
	#include <X11/Xatom.h>
	#include <cstring>

	// Open default display
	Display* _Robot_Display =
		XOpenDisplay (NULL);
	#define gDisplay _Robot_Display

	#ifndef X_HAVE_UTF8_STRING
		#error It appears that X_HAVE_UTF8_STRING is not defined - \
			   please verify that your version of XLib is supported
	#endif

#endif
#ifdef ROBOT_OS_MAC

	#include <dlfcn.h>
	#include <ApplicationServices/ApplicationServices.h>

	#ifdef MAC_OS_X_VERSION_10_11
		#define kAXValueCGPointType kAXValueTypeCGPoint
		#define kAXValueCGSizeType  kAXValueTypeCGSize
	#endif

#endif
#ifdef ROBOT_OS_WIN

	#define NOMINMAX
	#define WIN32_LEAN_AND_MEAN
	#include <Windows.h>
	using std::wstring;

#endif
ROBOT_NS_BEGIN



//----------------------------------------------------------------------------//
// Locals                                                                     //
//----------------------------------------------------------------------------//

#ifdef ROBOT_OS_LINUX

	//----------------------------------------------------------------------------//
	// Error Handling                                                             //
	//----------------------------------------------------------------------------//

	////////////////////////////////////////////////////////////////////////////////

	typedef int (*XErrorHandler) (Display*, XErrorEvent*);

	////////////////////////////////////////////////////////////////////////////////

	static int XHandleError (Display* dp, XErrorEvent* e) { return 0; }

	////////////////////////////////////////////////////////////////////////////////

	class XDismissErrors
	{
	public:
		////////////////////////////////////////////////////////////////////////////////

		XDismissErrors (void)
		{
			// Save old handler and dismiss errors
			mOld = XSetErrorHandler (XHandleError);
		}

		////////////////////////////////////////////////////////////////////////////////

		~XDismissErrors (void)
		{
			// Flush output buffer
			XSync (gDisplay, False);

			// Reinstate old handler
			XSetErrorHandler (mOld);
		}

	private:
		XErrorHandler mOld;
	};



	//----------------------------------------------------------------------------//
	// Definitions                                                                //
	//----------------------------------------------------------------------------//

	////////////////////////////////////////////////////////////////////////////////

	struct Hints
	{
		unsigned long Flags;
		unsigned long Funcs;
		unsigned long Decorations;
		  signed long Mode;
		unsigned long Stat;
	};

	////////////////////////////////////////////////////////////////////////////////

	static Atom WM_STATE	= None;
	static Atom WM_ABOVE	= None;
	static Atom WM_HIDDEN	= None;
	static Atom WM_HMAX		= None;
	static Atom WM_VMAX		= None;

	static Atom WM_DESKTOP	= None;
	static Atom WM_CURDESK	= None;

	static Atom WM_NAME		= None;
	static Atom WM_UTF8		= None;
	static Atom WM_PID		= None;
	static Atom WM_ACTIVE	= None;
	static Atom WM_HINTS	= None;
	static Atom WM_EXTENTS	= None;

	////////////////////////////////////////////////////////////////////////////////

	static void LoadAtoms (void)
	{
		WM_STATE   = XInternAtom (gDisplay, "_NET_WM_STATE",                True);
		WM_ABOVE   = XInternAtom (gDisplay, "_NET_WM_STATE_ABOVE",          True);
		WM_HIDDEN  = XInternAtom (gDisplay, "_NET_WM_STATE_HIDDEN",         True);
		WM_HMAX    = XInternAtom (gDisplay, "_NET_WM_STATE_MAXIMIZED_HORZ", True);
		WM_VMAX    = XInternAtom (gDisplay, "_NET_WM_STATE_MAXIMIZED_VERT", True);

		WM_DESKTOP = XInternAtom (gDisplay, "_NET_WM_DESKTOP",              True);
		WM_CURDESK = XInternAtom (gDisplay, "_NET_CURRENT_DESKTOP",         True);

		WM_NAME    = XInternAtom (gDisplay, "_NET_WM_NAME",                 True);
		WM_UTF8    = XInternAtom (gDisplay, "UTF8_STRING",                  True);
		WM_PID     = XInternAtom (gDisplay, "_NET_WM_PID",                  True);
		WM_ACTIVE  = XInternAtom (gDisplay, "_NET_ACTIVE_WINDOW",           True);
		WM_HINTS   = XInternAtom (gDisplay, "_MOTIF_WM_HINTS",              True);
		WM_EXTENTS = XInternAtom (gDisplay, "_NET_FRAME_EXTENTS",           True);
	}



	//----------------------------------------------------------------------------//
	// Functions                                                                  //
	//----------------------------------------------------------------------------//

	////////////////////////////////////////////////////////////////////////////////

	static void* GetWindowProperty (::Window win, Atom atom, uint32* items = nullptr)
	{
		// Property variables
		Atom type; int format;
		unsigned long  nItems;
		unsigned long  bAfter;
		unsigned char* result = nullptr;

		// Check the atom
		if (atom != None)
		{
			// Retrieve and validate the specified property
			if (!XGetWindowProperty (gDisplay, win, atom, 0,
				BUFSIZ, False, AnyPropertyType, &type, &format,
				&nItems, &bAfter, &result) && result && nItems)
			{
				// Copy items result
				if (items != nullptr)
					*items = (uint32) nItems;

				return result;
			}
		}

		// Reset the items result if valid
		if (items != nullptr) *items = 0;

		// Free the result if it got allocated
		if (result != nullptr) XFree (result);
		return nullptr;
	}

	////////////////////////////////////////////////////////////////////////////////

	#define STATE_TOPMOST  0
	#define STATE_MINIMIZE 1
	#define STATE_MAXIMIZE 2

	////////////////////////////////////////////////////////////////////////////////

	static bool GetState (::Window win, uint8 setting)
	{
		// Validate every atom that we want to use
		if (WM_STATE  != None && WM_HMAX != None &&
			WM_ABOVE  != None && WM_VMAX != None &&
			WM_HIDDEN != None)
		{
			Atom* atoms; uint32 nItems = 0;
			bool test1 = false, test2 = false;

			// Get the window state property
			atoms = (Atom*) GetWindowProperty
					(win, WM_STATE, &nItems);

			// Check result and iterate items
			if (atoms == nullptr) return false;
			for (uint32 i = 0; i < nItems; ++i)
			{
				switch (setting)
				{
					case STATE_TOPMOST:
						if (atoms[i] == WM_ABOVE)
							test1 = test2 = true;
						break;

					case STATE_MINIMIZE:
						if (atoms[i] == WM_HIDDEN)
							test1 = test2 = true;
						break;

					case STATE_MAXIMIZE:
						if (atoms[i] == WM_HMAX)
							test1 = true;
						if (atoms[i] == WM_VMAX)
							test2 = true;
						break;

					// Unknown setting
					default: break;
				}

				// We're finished iterating
				if (test1 && test2) break;
			}

			XFree (atoms);
			return test1 && test2;
		}

		return false;
	}

	////////////////////////////////////////////////////////////////////////////////

	static void SetState (::Window win, uint8 setting, bool state)
	{
		// Retrieve the screen number
		XWindowAttributes attr = { 0 };
		XGetWindowAttributes (gDisplay, win, &attr);
		int s = XScreenNumberOfScreen (attr.screen);

		// Perform minimize functions
		if (setting == STATE_MINIMIZE)
		{
			if (state) XIconifyWindow (gDisplay, win, s);
			else Window::SetActive (Window ((uintptr) win));
		}

		// Validate every atom that we want to use
		else if (WM_STATE != None && WM_HMAX != None &&
				 WM_ABOVE != None && WM_VMAX != None)
		{
			// Prepare an event
			XClientMessageEvent e = { 0 };
			e.window = win; e.format = 32;
			e.message_type = WM_STATE;
			e.display = gDisplay;
			e.type = ClientMessage;

			switch (setting)
			{
				case STATE_TOPMOST:
					e.data.l[0] = state ? 1 : 0;
					e.data.l[1] = WM_ABOVE; break;

				case STATE_MAXIMIZE:
					e.data.l[0] = state ? 1 : 0;
					e.data.l[1] = WM_HMAX;
					e.data.l[2] = WM_VMAX; break;

				// Unknown setting
				default: return;
			}

			// Send the message
			XSendEvent (gDisplay, XRootWindow (gDisplay, s), False,
				SubstructureNotifyMask | SubstructureRedirectMask,
				(XEvent*) &e);
		}
	}

	////////////////////////////////////////////////////////////////////////////////

	static Bounds GetFrame (::Window win)
	{
		Bounds frame;
		// Retrieve frame bounds
		if (WM_EXTENTS != None)
		{
			long* result; uint32 nItems = 0;
			// Get the window extents property
			result = (long*) GetWindowProperty
					 (win, WM_EXTENTS, &nItems);

			// Verify the results
			if (result != nullptr)
			{
				if (nItems == 4)
				{
					frame = Bounds
						((int32) result[0],  (int32) result[2],
						 (int32) result[0] + (int32) result[1],
						 (int32) result[2] + (int32) result[3]);
				}

				XFree (result);
			}
		}

		return frame;
	}

	////////////////////////////////////////////////////////////////////////////////

	static void SetDesktopForWindow (::Window win)
	{
		// Validate every atom that we want to use
		if (WM_DESKTOP != None && WM_CURDESK != None)
		{
			// Get desktop property
			long* desktop = (long*)
				GetWindowProperty (win, WM_DESKTOP);

			// Check result value
			if (desktop != nullptr)
			{
				// Retrieve the screen number
				XWindowAttributes attr = { 0 };
				XGetWindowAttributes (gDisplay, win, &attr);
				int s = XScreenNumberOfScreen (attr.screen);
				::Window root = XRootWindow (gDisplay, s);

				// Prepare an event
				XClientMessageEvent e = { 0 };
				e.window = root; e.format = 32;
				e.message_type = WM_CURDESK;
				e.display = gDisplay;
				e.type = ClientMessage;
				e.data.l[0] = *desktop;
				e.data.l[1] = CurrentTime;

				// Send the message
				XSendEvent (gDisplay,
					root, False, SubstructureNotifyMask |
					SubstructureRedirectMask, (XEvent*) &e);

				XFree (desktop);
			}
		}
	}

	////////////////////////////////////////////////////////////////////////////////

	struct EnumWindowsData
	{
		regex		Title;	// Window title to look for
		bool		Empty;	// Whether title is empty
		int32		ProcID;	// Window PID to look for
		WindowList*	Result;	// Pointer to the results
	};

	////////////////////////////////////////////////////////////////////////////////

	static void EnumWindows (::Window win, EnumWindowsData* data)
	{
		// Check the window visibility
		Window window; XWindowAttributes attr = { 0 };
		XGetWindowAttributes (gDisplay, win, &attr);
		if (attr.map_state != IsViewable) goto skip;

		// Attempt to associate current window
		if (!window.SetHandle ((uintptr) win))
			goto skip;

		if (data->ProcID != 0 &&
			// Perform process ID filtering
			data->ProcID != window.GetPID())
			goto skip;

		if (data->Empty ||
			// Try and match current window using regex
			regex_match (window.GetTitle(), data->Title))
		{
			// Add current window to result
			data->Result->push_back (window);
		}

	skip:
		::Window root, parent;
		::Window* children;
		unsigned int count;

		// Retrieve the child list
		Status status = XQueryTree
			(gDisplay, win, &root,
			&parent, &children, &count);

		// Check the results of the query
		if (!status || children == nullptr)
			return;

		// Recursively iterate child list
		for (uint32 i = 0; i < count; ++i)
			EnumWindows (children[i], data);

		// Free children
		XFree (children);
	}

#endif
#ifdef ROBOT_OS_MAC

	////////////////////////////////////////////////////////////////////////////////

	static Boolean (*gAXIsProcessTrustedWithOptions) (CFDictionaryRef);

	////////////////////////////////////////////////////////////////////////////////

	static CFStringRef* gkAXTrustedCheckOptionPrompt;

	////////////////////////////////////////////////////////////////////////////////

	// WARNING: Undocumented API, use with caution as it could one day go away
	extern "C" AXError _AXUIElementGetWindow (AXUIElementRef, CGWindowID* out);

	////////////////////////////////////////////////////////////////////////////////

	static AXUIElementRef GetUIElement (CGWindowID win)
	{
		// Window PID
		int32 pid = 0;

		// Create array storing window
		CGWindowID window[1] = { win };
		CFArrayRef wlist = CFArrayCreate (nullptr,
				(const void**) window, 1, nullptr);

		// Get window info
		CFArrayRef info =
			CGWindowListCreateDescriptionFromArray (wlist);
		CFRelease (wlist);

		// Check whether the resulting array is populated
		if (info != nullptr && CFArrayGetCount (info) > 0)
		{
			// Retrieve description from info array
			CFDictionaryRef desc = (CFDictionaryRef)
					CFArrayGetValueAtIndex (info, 0);

			// Get window PID
			CFNumberRef data =
				(CFNumberRef)
				CFDictionaryGetValue (desc, kCGWindowOwnerPID);

			if (data != nullptr)
				CFNumberGetValue (data, kCFNumberIntType, &pid);

			// Return result
			CFRelease (info);
		}

		// Check if PID was retrieved
		if (pid <= 0) return nullptr;

		// Create an accessibility object using retrieved PID
		auto application = AXUIElementCreateApplication (pid);
		if (application == nullptr) return nullptr;

		CFArrayRef windows = nullptr;
		// Get all windows associated with the app
		AXUIElementCopyAttributeValues (application,
			kAXWindowsAttribute, 0, 1024, &windows);

		// Reference to resulting value
		AXUIElementRef result = nullptr;

		if (windows != nullptr)
		{
			auto count = CFArrayGetCount (windows);
			// Loop all windows in the process
			for (CFIndex i = 0; i < count; ++i)
			{
				// Get the element at the index
				auto element = (AXUIElementRef)
					CFArrayGetValueAtIndex (windows, i);

				CGWindowID temp = 0;
				// Use undocumented API to get WindowID
				_AXUIElementGetWindow (element, &temp);

				// Check results
				if (temp == win)
				{
					// Retain element
					CFRetain (element);
					 result = element;
					break;
				}
			}

			CFRelease (windows);
		}

		CFRelease (application);
		return result;
	}

#endif
#ifdef ROBOT_OS_WIN

	////////////////////////////////////////////////////////////////////////////////

	#ifdef UNICODE
		extern  string _UTF8Encode (const wstring& value);
		extern wstring _UTF8Decode (const  string& value);
	#else
		extern  string _UTF8Encode (const  string& value);
		extern  string _UTF8Decode (const  string& value);
	#endif

	////////////////////////////////////////////////////////////////////////////////

	struct EnumWindowsData
	{
		regex		Title;	// Window title to look for
		bool		Empty;	// Whether title is empty
		int32		ProcID;	// Window PID to look for
		WindowList*	Result;	// Pointer to the results
	};

	////////////////////////////////////////////////////////////////////////////////

	static BOOL CALLBACK EnumWindowsProc (HWND hwnd, LPARAM lParam)
	{
		// Verify window visibility
		if (!IsWindowVisible (hwnd))
			return TRUE;

		// Retrieve lParam as EnumWindowsData
		auto data = (EnumWindowsData*) lParam;

		Window window;
		// Attempt to associate current window
		if (!window.SetHandle ((uintptr) hwnd))
			return TRUE;

		if (data->ProcID != 0 &&
			// Perform process ID filtering
			data->ProcID != window.GetPID())
			return TRUE;

		if (data->Empty ||
			// Try and match current window using regex
			regex_match (window.GetTitle(), data->Title))
		{
			// Add current window to result
			data->Result->push_back (window);
		}

		return TRUE;
	}

#endif



//----------------------------------------------------------------------------//
// Classes                                                             Window //
//----------------------------------------------------------------------------//

////////////////////////////////////////////////////////////////////////////////

struct Window::Data
{
#ifdef ROBOT_OS_LINUX

	::Window		XWin;		// Handle to an X11 window

#endif
#ifdef ROBOT_OS_MAC

	CGWindowID		CgID;		// Handle to a CGWindowID
	AXUIElementRef	AxID;		// Handle to a AXUIElementRef

#endif
#ifdef ROBOT_OS_WIN

	HWND			HWnd;		// Handle to a window HWND

#endif
};



//----------------------------------------------------------------------------//
// Constructors                                                        Window //
//----------------------------------------------------------------------------//

////////////////////////////////////////////////////////////////////////////////

Window::Window (uintptr handle) : mData (new Window::Data(), [](Window::Data* data)
{
#ifdef ROBOT_OS_MAC

	// Release the AX element
	if (data->AxID != nullptr)
		CFRelease (data->AxID);

#endif

	// Free data
	delete data;
})
{
#ifdef ROBOT_OS_LINUX

	// If atoms loaded
	if (WM_PID == None)
	{
		// Load all necessary atom properties
		if (gDisplay != nullptr) LoadAtoms();
	}

	mData->XWin = 0;

#endif
#ifdef ROBOT_OS_MAC

	mData->CgID = 0;
	mData->AxID = 0;

#endif
#ifdef ROBOT_OS_WIN

	mData->HWnd = 0;

#endif

	SetHandle (handle);
}



//----------------------------------------------------------------------------//
// Functions                                                           Window //
//----------------------------------------------------------------------------//

////////////////////////////////////////////////////////////////////////////////

bool Window::IsValid (void) const
{
#ifdef ROBOT_OS_LINUX

	if (mData->XWin == 0)
		return false;

	// Check for a valid X-Window display
	if (gDisplay == nullptr) return false;

	// Ignore X errors
	XDismissErrors xe;

	// Get the window PID property
	void* result = GetWindowProperty
			  (mData->XWin, WM_PID);
	if (result == nullptr) return false;

	// Free result and return true
	XFree (result); return true;

#endif
#ifdef ROBOT_OS_MAC

	if (mData->CgID == 0 ||
		mData->AxID == 0)
		return false;

	CFTypeRef r = nullptr;

	// Attempt to get the window role
	if (AXUIElementCopyAttributeValue
		(mData->AxID, kAXRoleAttribute,
		&r) == kAXErrorSuccess && r)
		{ CFRelease (r); return true; }

	return false;

#endif
#ifdef ROBOT_OS_WIN

	if (mData->HWnd == 0)
		return false;

	return IsWindow (mData->HWnd) != 0;

#endif
}

////////////////////////////////////////////////////////////////////////////////

void Window::Close (void)
{
	// Check window validity
	if (!IsValid()) return;

#ifdef ROBOT_OS_LINUX

	// Ignore X errors
	XDismissErrors xe;

	// Close the window
	XDestroyWindow (gDisplay, mData->XWin);

#endif
#ifdef ROBOT_OS_MAC

	AXUIElementRef b = nullptr;

	// Retrieve the close button of this window
	if (AXUIElementCopyAttributeValue (mData->AxID,
		kAXCloseButtonAttribute, (CFTypeRef*) &b)
		== kAXErrorSuccess && b != nullptr)
	{
		// Simulate button press on the close button
		AXUIElementPerformAction (b, kAXPressAction);
		CFRelease (b);
	}

#endif
#ifdef ROBOT_OS_WIN

	PostMessage (mData->HWnd, WM_CLOSE, 0, 0);

#endif
}

////////////////////////////////////////////////////////////////////////////////

bool Window::IsTopMost (void) const
{
	// Check the window validity
	if (!IsValid()) return false;

#ifdef ROBOT_OS_LINUX

	// Ignore X errors
	XDismissErrors xe;
	return GetState (mData->XWin, STATE_TOPMOST);

#endif
#ifdef ROBOT_OS_MAC

	return false; // WARNING: Unavailable

#endif
#ifdef ROBOT_OS_WIN

	return (GetWindowLongPtr (mData->HWnd,
		GWL_EXSTYLE) & WS_EX_TOPMOST) != 0;

#endif
}

////////////////////////////////////////////////////////////////////////////////

bool Window::IsBorderless (void) const
{
	// Check the window validity
	if (!IsValid()) return false;

#ifdef ROBOT_OS_LINUX

	// Ignore X errors
	XDismissErrors xe;

	// Get the window hints property
	void* result = GetWindowProperty
			(mData->XWin, WM_HINTS);

	// Check the result and convert it
	if (result == nullptr) return false;
	auto decorations = reinterpret_cast
		<Hints*> (result)->Decorations;
	XFree (result); return decorations == 0;

#endif
#ifdef ROBOT_OS_MAC

	return false; // WARNING: Unavailable

#endif
#ifdef ROBOT_OS_WIN

	// CAUTION: The implementation  of this function
	// is a bit dodgy and may not always produce the
	// correct results. Run several tests beforehand
	// to see if this function is right for you.

	return (GetWindowLongPtr (mData->HWnd,
		GWL_STYLE) & WS_TILEDWINDOW) == 0;

#endif
}

////////////////////////////////////////////////////////////////////////////////

bool Window::IsMinimized (void) const
{
	// Check the window validity
	if (!IsValid()) return false;

#ifdef ROBOT_OS_LINUX

	// Ignore X errors
	XDismissErrors xe;
	return GetState (mData->XWin, STATE_MINIMIZE);

#endif
#ifdef ROBOT_OS_MAC

	CFBooleanRef data = nullptr;

	// Determine whether the window is minimized
	if (AXUIElementCopyAttributeValue (mData->AxID,
		kAXMinimizedAttribute, (CFTypeRef*) &data)
		== kAXErrorSuccess && data != nullptr)
	{
		// Convert resulting data into a bool
		bool result = CFBooleanGetValue (data);
		CFRelease (data); return result;
	}

	return false;

#endif
#ifdef ROBOT_OS_WIN

	return (GetWindowLongPtr (mData->HWnd,
			GWL_STYLE) & WS_MINIMIZE) != 0;

#endif
}

////////////////////////////////////////////////////////////////////////////////

bool Window::IsMaximized (void) const
{
	// Check the window validity
	if (!IsValid()) return false;

#ifdef ROBOT_OS_LINUX

	// Ignore X errors
	XDismissErrors xe;
	return GetState (mData->XWin, STATE_MAXIMIZE);

#endif
#ifdef ROBOT_OS_MAC

	return false; // WARNING: Unavailable

#endif
#ifdef ROBOT_OS_WIN

	return (GetWindowLongPtr (mData->HWnd,
			GWL_STYLE) & WS_MAXIMIZE) != 0;

#endif
}

////////////////////////////////////////////////////////////////////////////////

void Window::SetTopMost (bool state)
{
	// Check window validity
	if (!IsValid()) return;

#ifdef ROBOT_OS_LINUX

	// Ignore X errors
	XDismissErrors xe;
	SetState (mData->XWin, STATE_TOPMOST, state);

#endif
#ifdef ROBOT_OS_MAC

	// WARNING: Unavailable

#endif
#ifdef ROBOT_OS_WIN

	SetWindowPos (mData->HWnd, state ?
		HWND_TOPMOST : HWND_NOTOPMOST, 0,
		0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);

#endif
}

////////////////////////////////////////////////////////////////////////////////

void Window::SetBorderless (bool state)
{
	// Check window validity
	if (!IsValid()) return;

#ifdef ROBOT_OS_LINUX

	// Ignore X errors
	XDismissErrors xe;

	// Check atom value
	if (WM_HINTS != None)
	{
		// Create hints
		Hints hints = { 0 };
		hints.Flags = 2;
		hints.Decorations =
			state ? 0 : 1;

		// Set hints property with our version
		XChangeProperty (gDisplay, mData->XWin,
			WM_HINTS, WM_HINTS, 32, PropModeReplace,
			(unsigned char*) &hints, 5);
	}

#endif
#ifdef ROBOT_OS_MAC

	// WARNING: Unavailable

#endif
#ifdef ROBOT_OS_WIN

	// CAUTION: The implementation  of this function
	// is a bit dodgy and may not always produce the
	// correct results. Run several tests beforehand
	// to see if this function is right for you.

	LONG_PTR style = GetWindowLongPtr (mData->HWnd, GWL_STYLE);

	if (state)
		SetWindowLongPtr (mData->HWnd, GWL_STYLE, style & ~WS_TILEDWINDOW);
	else
		SetWindowLongPtr (mData->HWnd, GWL_STYLE, style |  WS_TILEDWINDOW);

	// Request recalculation of new window decorations
	SetWindowPos (mData->HWnd, nullptr, 0, 0, 0, 0,
		SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE |
		SWP_NOSIZE | SWP_FRAMECHANGED | SWP_NOACTIVATE);

#endif
}

////////////////////////////////////////////////////////////////////////////////

void Window::SetMinimized (bool state)
{
	// Check window validity
	if (!IsValid()) return;

#ifdef ROBOT_OS_LINUX

	// Ignore X errors
	XDismissErrors xe;
	SetState (mData->XWin, STATE_MINIMIZE, state);

#endif
#ifdef ROBOT_OS_MAC

	AXUIElementSetAttributeValue (mData->AxID,
		kAXMinimizedAttribute, state ?
		kCFBooleanTrue : kCFBooleanFalse);

#endif
#ifdef ROBOT_OS_WIN

	if (state)
		ShowWindow (mData->HWnd, SW_MINIMIZE);

	else if (IsMinimized())
		ShowWindow (mData->HWnd, SW_RESTORE);

#endif
}

////////////////////////////////////////////////////////////////////////////////

void Window::SetMaximized (bool state)
{
	// Check window validity
	if (!IsValid()) return;

#ifdef ROBOT_OS_LINUX

	// Ignore X errors
	XDismissErrors xe;
	SetState (mData->XWin, STATE_MINIMIZE, false);
	SetState (mData->XWin, STATE_MAXIMIZE, state);

#endif
#ifdef ROBOT_OS_MAC

	// WARNING: Unavailable

#endif
#ifdef ROBOT_OS_WIN

	if (IsMinimized())
		ShowWindow (mData->HWnd, SW_RESTORE);

	if (state)
		ShowWindow (mData->HWnd, SW_MAXIMIZE);

	else if (IsMaximized())
		ShowWindow (mData->HWnd, SW_RESTORE);

#endif
}

////////////////////////////////////////////////////////////////////////////////

Process Window::GetProcess (void) const
{
	return Process (GetPID());
}

////////////////////////////////////////////////////////////////////////////////

int32 Window::GetPID (void) const
{
	// Check window validity
	if (!IsValid()) return 0;

#ifdef ROBOT_OS_LINUX

	// Ignore X errors
	XDismissErrors xe;

	// Get the window PID
	long* result = (long*)
		GetWindowProperty
		(mData->XWin, WM_PID);

	// Check result and convert it
	if (result == nullptr) return 0;
	auto pid = (int32) *result;
	XFree (result); return pid;

#endif
#ifdef ROBOT_OS_MAC

	pid_t pid = 0;
	// Attempt to retrieve the window pid
	if (AXUIElementGetPid (mData->AxID, &pid)
			== kAXErrorSuccess) return pid;

	return 0;

#endif
#ifdef ROBOT_OS_WIN

	DWORD  id = 0;
	GetWindowThreadProcessId (mData->HWnd, &id);
	return id;

#endif
}

////////////////////////////////////////////////////////////////////////////////

uintptr Window::GetHandle (void) const
{
#ifdef ROBOT_OS_LINUX

	return (uintptr) mData->XWin;

#endif
#ifdef ROBOT_OS_MAC

	return (uintptr) mData->CgID;

#endif
#ifdef ROBOT_OS_WIN

	return (uintptr) mData->HWnd;

#endif
}

////////////////////////////////////////////////////////////////////////////////

bool Window::SetHandle (uintptr handle)
{
#ifdef ROBOT_OS_LINUX

	mData->XWin = (::Window) handle;

	if (handle == 0)
		return true;

	if (IsValid())
		return true;

	mData->XWin = 0;
	return false;

#endif
#ifdef ROBOT_OS_MAC

	// Release the AX element
	if (mData->AxID != nullptr)
		CFRelease (mData->AxID);

	// Reset both values
	mData->CgID = 0;
	mData->AxID = 0;

	if (handle == 0)
		return true;

	// Retrieve the window element
	auto cgID = (CGWindowID) handle;
	auto axID = GetUIElement (cgID);

	if (axID != nullptr)
	{
		mData->CgID = cgID;
		mData->AxID = axID;
		return true;
	}

	return false;

#endif
#ifdef ROBOT_OS_WIN

	mData->HWnd = (HWND) handle;

	if (handle == 0)
		return true;

	if (IsValid())
		return true;

	mData->HWnd = 0;
	return false;

#endif
}

////////////////////////////////////////////////////////////////////////////////

uintptr Window::GetHandleAx (void) const
{
#ifdef ROBOT_OS_LINUX

	return 0;

#endif
#ifdef ROBOT_OS_MAC

	return (uintptr)
		mData->AxID;

#endif
#ifdef ROBOT_OS_WIN

	return 0;

#endif
}

////////////////////////////////////////////////////////////////////////////////

string Window::GetTitle (void) const
{
	// Check if the window is valid
	if (!IsValid()) return string();

#ifdef ROBOT_OS_LINUX

	void* result;
	// Ignore X errors
	XDismissErrors xe;

	// Get window title (UTF-8)
	result = GetWindowProperty
		(mData->XWin, WM_NAME);

	// Check result value
	if (result != nullptr)
	{
		// Convert result to a string
		string name ((char*) result);
		XFree (result);

		if (!name.empty())
			return name;
	}

	// Get window title (ASCII)
	result = GetWindowProperty
		(mData->XWin, XA_WM_NAME);

	// Check result value
	if (result != nullptr)
	{
		// Convert result to a string
		string name ((char*) result);
		XFree (result); return name;
	}

	return string();

#endif
#ifdef ROBOT_OS_MAC

	CFStringRef data = nullptr;

	// Determine the current title of the window
	if (AXUIElementCopyAttributeValue (mData->AxID,
		kAXTitleAttribute, (CFTypeRef*) &data)
		== kAXErrorSuccess && data != nullptr)
	{
		char conv[512];
		// Convert result to a C-String
		CFStringGetCString (data, conv,
			512, kCFStringEncodingUTF8);
		CFRelease (data);
		return string (conv);
	}

	return string();

#endif
#ifdef ROBOT_OS_WIN

	TCHAR name[512];
	return GetWindowText
		(mData->HWnd, name, 512) > 0 ?
		_UTF8Encode (name) : string();

#endif
}

////////////////////////////////////////////////////////////////////////////////

void Window::SetTitle (const char* title)
{
	// Check window & title validity
	if (!title || !IsValid()) return;

#ifdef ROBOT_OS_LINUX

	// Ignore X errors
	XDismissErrors xe;

	// Convert title into non-const
	uint8* converted = (uint8*) title;
	int32  conLength =  strlen (title);

	// Change ASCII window name
	XChangeProperty (gDisplay,
		mData->XWin, XA_WM_NAME, XA_STRING, 8,
		PropModeReplace, converted, conLength);

	XChangeProperty (gDisplay,
		mData->XWin, XA_WM_ICON_NAME, XA_STRING,
		8, PropModeReplace, converted, conLength);

	// Check every atom that we want to use
	if (WM_NAME != None && WM_UTF8 != None)
	{
		// Change UTF-8 window name
		XChangeProperty (gDisplay,
			mData->XWin, WM_NAME, WM_UTF8,
			8, PropModeReplace, converted,
			conLength);
	}

#endif
#ifdef ROBOT_OS_MAC

	// Try to convert title into string reference
	CFStringRef name = CFStringCreateWithCString
		(nullptr, title, kCFStringEncodingUTF8);

	if (name != nullptr)
	{
		// Set the current title of the window
		AXUIElementSetAttributeValue (mData->AxID,
			kAXTitleAttribute, (CFTypeRef) name);

		CFRelease (name);
	}

#endif
#ifdef ROBOT_OS_WIN

	SetWindowText (mData->HWnd, _UTF8Decode (title).data());

#endif
}

////////////////////////////////////////////////////////////////////////////////

Bounds Window::GetBounds (void) const
{
	// Check if the window is valid
	if (!IsValid()) return Bounds();

#ifdef ROBOT_OS_LINUX

	// Ignore X errors
	XDismissErrors xe;

	Bounds client = GetClient();
	Bounds frame  = GetFrame (mData->XWin);
	return Bounds (client.X - frame.X,
				   client.Y - frame.Y,
				   client.W + frame.W,
				   client.H + frame.H);

#endif
#ifdef ROBOT_OS_MAC

	Bounds bounds;
	AXValueRef axp = nullptr;
	AXValueRef axs = nullptr;

	// Determine the current point of the window
	if (AXUIElementCopyAttributeValue (mData->AxID,
		kAXPositionAttribute, (CFTypeRef*) &axp)
		!= kAXErrorSuccess || axp == nullptr)
		goto exit;

	// Determine the current size of the window
	if (AXUIElementCopyAttributeValue (mData->AxID,
		kAXSizeAttribute, (CFTypeRef*) &axs)
		!= kAXErrorSuccess || axs == nullptr)
		goto exit;

	CGPoint p; CGSize s;
	// Attempt to convert both values into atomic types
	if (AXValueGetValue (axp, kAXValueCGPointType, &p) &&
		AXValueGetValue (axs, kAXValueCGSizeType,  &s))
		bounds = Bounds (p.x, p.y, s.width, s.height);

exit:
	if (axp != nullptr) CFRelease (axp);
	if (axs != nullptr) CFRelease (axs);
	return bounds;

#endif
#ifdef ROBOT_OS_WIN

	RECT rect = { 0 };
	GetWindowRect (mData->HWnd, &rect);

	return Bounds
		(rect.left, rect.top,
		 rect.right  - rect.left,
		 rect.bottom - rect.top);

#endif
}

////////////////////////////////////////////////////////////////////////////////

void Window::SetBounds (const Bounds& bounds)
{
	SetBounds (bounds.X, bounds.Y,
			   bounds.W, bounds.H);
}

////////////////////////////////////////////////////////////////////////////////

void Window::SetBounds (int32 x,
	  int32 y, int32 w, int32 h)
{
	// Check window validity
	if (!IsValid()) return;

#ifdef ROBOT_OS_LINUX

	// Ignore X errors
	XDismissErrors xe;
	Bounds frame = GetFrame (mData->XWin);

	// Prepare win changes
	XWindowChanges changes;
	changes.x = x; changes.y = y;
	changes.width  = w - frame.W;
	changes.height = h - frame.H;

	// Apply new position and size of window
	XConfigureWindow (gDisplay, mData->XWin,
		CWX | CWY | CWWidth | CWHeight, &changes);

#endif
#ifdef ROBOT_OS_MAC

	// Convert bounds to CG values
	CGPoint p = CGPointMake (x, y);
	CGSize  s = CGSizeMake  (w, h);

	// Convert bounds to AX values
	AXValueRef axp = AXValueCreate (kAXValueCGPointType, (const void*) &p);
	AXValueRef axs = AXValueCreate (kAXValueCGSizeType,  (const void*) &s);

	if (axp != nullptr && axs != nullptr)
	{
		// Set the current bounds of the window
		AXUIElementSetAttributeValue (mData->AxID, kAXPositionAttribute, axp);
		AXUIElementSetAttributeValue (mData->AxID, kAXSizeAttribute,     axs);
	}

	if (axp != nullptr) CFRelease (axp);
	if (axs != nullptr) CFRelease (axs);

#endif
#ifdef ROBOT_OS_WIN

	SetWindowPos (mData->HWnd, nullptr, x, y,
		w, h, SWP_NOZORDER | SWP_NOOWNERZORDER);

#endif
}

////////////////////////////////////////////////////////////////////////////////

Bounds Window::GetClient (void) const
{
	// Check if the window is valid
	if (!IsValid()) return Bounds();

#ifdef ROBOT_OS_LINUX

	// Ignore X errors
	XDismissErrors xe;

	// Property variables
	::Window root, parent;
	::Window* children;
	unsigned int count;
	int32 x = 0, y = 0;

	// Check if the window is the root
	XQueryTree (gDisplay, mData->XWin,
		&root, &parent, &children, &count);
	if (children) XFree (children);

	// Retrieve window attributes
	XWindowAttributes attr = { 0 };
	XGetWindowAttributes (gDisplay,
				mData->XWin, &attr);

	// Coordinates must be translated
	if (parent != attr.root) XTranslateCoordinates
		(gDisplay, mData->XWin, attr.root, attr.x,
		 attr.y, &x, &y, &parent);

	// Coordinates can be left alone
	else { x = attr.x; y = attr.y; }

	// Return resulting window bounds
	return Bounds (x, y, attr.width, attr.height);

#endif
#ifdef ROBOT_OS_MAC

	return GetBounds();

#endif
#ifdef ROBOT_OS_WIN

	RECT rect = { 0 };
	GetClientRect (mData->HWnd, &rect);

	POINT point;
	point.x = rect.left;
	point.y = rect.top;

	// Convert the client point to screen
	ClientToScreen (mData->HWnd, &point);
	return Bounds (point.x, point.y,
			rect.right  - rect.left,
			rect.bottom - rect.top);

#endif
}

////////////////////////////////////////////////////////////////////////////////

void Window::SetClient (const Bounds& bounds)
{
	SetClient (bounds.X, bounds.Y,
			   bounds.W, bounds.H);
}

////////////////////////////////////////////////////////////////////////////////

void Window::SetClient (int32 x,
	  int32 y, int32 w, int32 h)
{
	// Check window validity
	if (!IsValid()) return;

#ifdef ROBOT_OS_LINUX

	// Ignore X errors
	XDismissErrors xe;
	Bounds frame = GetFrame (mData->XWin);

	// Prepare win changes
	XWindowChanges changes;
	changes.x = x - frame.X;
	changes.y = y - frame.Y;
	changes.width  = w;
	changes.height = h;

	// Apply new position and size of window
	XConfigureWindow (gDisplay, mData->XWin,
		CWX | CWY | CWWidth | CWHeight, &changes);

#endif
#ifdef ROBOT_OS_MAC

	SetBounds (x, y, w, h);

#endif
#ifdef ROBOT_OS_WIN

	RECT rect;
	rect.left = x; rect.right  = x + w;
	rect.top  = y; rect.bottom = y + h;

	DWORD st = GetWindowLong (mData->HWnd, GWL_STYLE  );
	DWORD ex = GetWindowLong (mData->HWnd, GWL_EXSTYLE);

	// Compute bounds with decorations
	if (!AdjustWindowRectEx (&rect, st,
		GetMenu (mData->HWnd) != nullptr, ex))
		return;

	SetWindowPos (mData->HWnd, nullptr,
		rect.left, rect.top, rect.right -
		rect.left, rect.bottom - rect.top,
		SWP_NOZORDER | SWP_NOOWNERZORDER);

#endif
}

////////////////////////////////////////////////////////////////////////////////

Point Window::MapToClient (const Point& point) const
{
	// Check if the window is valid
	if (!IsValid()) return Point();
	return point - GetClient().GetPoint();
}

////////////////////////////////////////////////////////////////////////////////

Point Window::MapToScreen (const Point& point) const
{
	// Check if the window is valid
	if (!IsValid()) return Point();
	return point + GetClient().GetPoint();
}

////////////////////////////////////////////////////////////////////////////////

Bounds Window::MapToClient (const Bounds& bounds) const
{
	// Check if the window is valid
	if (!IsValid()) return Bounds();
	return Bounds (bounds.GetPoint() - GetClient().GetPoint(), bounds.GetSize());
}

////////////////////////////////////////////////////////////////////////////////

Bounds Window::MapToScreen (const Bounds& bounds) const
{
	// Check if the window is valid
	if (!IsValid()) return Bounds();
	return Bounds (bounds.GetPoint() + GetClient().GetPoint(), bounds.GetSize());
}

////////////////////////////////////////////////////////////////////////////////

WindowList Window::GetList (const char* title)
{
	return GetList (title, 0);
}

////////////////////////////////////////////////////////////////////////////////

Window Window::GetActive (void)
{
#ifdef ROBOT_OS_LINUX

	Window result;
	// Check X-Window display
	if (WM_ACTIVE == None ||
		gDisplay == nullptr)
		return result;

	// Ignore X errors
	XDismissErrors xe;

	// Get the current active window
	void* active = GetWindowProperty
		(XDefaultRootWindow (gDisplay),
		WM_ACTIVE);

	// Check result value
	if (active != nullptr)
	{
		// Extract window from the result
		long window = *((long*) active);
		XFree (active); if (window != 0)
		{
			// Set and return the foreground window
			result.mData->XWin = (::Window) window;
			return result;
		}
	}

	// Use input focus instead
	::Window window = None;
	int revert = RevertToNone;
	XGetInputFocus (gDisplay,
			&window, &revert);

	// Return foreground window
	result.mData->XWin = window;
	return result;

#endif
#ifdef ROBOT_OS_MAC

	Window result;
	// Ignore deprecated warnings
	#pragma clang diagnostic push
	#pragma clang diagnostic ignored "-Wdeprecated-declarations"

	// NOTE: Until Apple actually removes
	// these functions, there's no real
	// reason to switch to the NS* flavor

	ProcessSerialNumber psn; pid_t pid;
	// Attempt to retrieve the front process
	if (GetFrontProcess (&psn      ) != 0 ||
		GetProcessPID   (&psn, &pid) != 0)
		return result;

	#pragma clang diagnostic pop

	// Create accessibility object using focused PID
	auto focused = AXUIElementCreateApplication (pid);
	if (focused == nullptr) return result; // Verify

	AXUIElementRef element;
	// Retrieve the currently focused window
	if (AXUIElementCopyAttributeValue (focused,
		kAXFocusedWindowAttribute, (CFTypeRef*)
		&element) == kAXErrorSuccess && element)
	{
		CGWindowID win = 0;
		// Use undocumented API to get WID
		if (_AXUIElementGetWindow (element,
			&win) == kAXErrorSuccess && win)
		{
			// Manually set internals
			result.mData->CgID = win;
			result.mData->AxID = element;
		}

		// Something went wrong
		else CFRelease (element);
	}

	CFRelease (focused);
	return result;

#endif
#ifdef ROBOT_OS_WIN

	// Attempt to get the foreground
	// window multiple times in case
	// the window has not been fully
	// activated yet.

	uint8 times = 0;
	while (++times < 20)
	{
		HWND handle;
		handle = GetForegroundWindow();
		if (handle != nullptr) return
			Window ((uintptr) handle);
		Sleep (20);
	}

	return Window();

#endif
}

////////////////////////////////////////////////////////////////////////////////

void Window::SetActive (const Window& window)
{
	// Check if the window is valid
	if (!window.IsValid()) return;

#ifdef ROBOT_OS_LINUX

	// Ignore X errors
	XDismissErrors xe;

	// Go to the specified window's desktop
	SetDesktopForWindow (window.mData->XWin);

	// Check the atom value
	if (WM_ACTIVE != None)
	{
		// Retrieve the screen number
		XWindowAttributes attr = { 0 };
		XGetWindowAttributes (gDisplay,
			window.mData->XWin, &attr);
		int s = XScreenNumberOfScreen (attr.screen);

		// Prepare an event
		XClientMessageEvent e = { 0 };
		e.window = window.mData->XWin;
		e.format = 32;
		e.message_type = WM_ACTIVE;
		e.display = gDisplay;
		e.type = ClientMessage;
		e.data.l[0] = 2;
		e.data.l[1] = CurrentTime;

		// Send the message
		XSendEvent (gDisplay, XRootWindow (gDisplay, s), False,
			SubstructureNotifyMask | SubstructureRedirectMask,
			(XEvent*) &e);
	}

	else
	{
		// Attempt to raise the specified window
		XRaiseWindow (gDisplay, window.mData->XWin);

		// Set the specified window's input focus
		XSetInputFocus (gDisplay, window.mData->XWin,
						RevertToParent, CurrentTime);
	}

#endif
#ifdef ROBOT_OS_MAC

	// Attempt to raise the specified window object
	if (AXUIElementPerformAction (window.mData->AxID,
				  kAXRaiseAction) == kAXErrorSuccess)
	{
		pid_t pid = 0;
		// Attempt to retrieve the PID of the window
		if (AXUIElementGetPid (window.mData->AxID, &pid)
					!= kAXErrorSuccess || !pid) return;

		// Ignore deprecated warnings
		#pragma clang diagnostic push
		#pragma clang diagnostic ignored "-Wdeprecated-declarations"

		// NOTE: Until Apple actually removes
		// these functions, there's no real
		// reason to switch to the NS* flavor

		ProcessSerialNumber psn;
		// Attempt to retrieve the process psn
		if (GetProcessForPID (pid, &psn) == 0)
		{
			// Gracefully activate process
			SetFrontProcessWithOptions (&psn,
			kSetFrontProcessFrontWindowOnly);
		}

		#pragma clang diagnostic pop
	}

#endif
#ifdef ROBOT_OS_WIN

	if (window.IsMinimized())
		ShowWindow (window.mData->HWnd, SW_RESTORE);

	SetForegroundWindow (window.mData->HWnd);

#endif
}

////////////////////////////////////////////////////////////////////////////////

bool Window::IsAxEnabled (bool options)
{
#ifdef ROBOT_OS_LINUX

	return true;

#endif
#ifdef ROBOT_OS_MAC

	// Statically load all required functions one time
	static dispatch_once_t once; dispatch_once (&once,
	^{
		// Open the framework
		void* handle = dlopen
			("/System/Library/Frameworks/Application"
			 "Services.framework/ApplicationServices", RTLD_LAZY);

		// Validate the handle
		if (handle != nullptr)
		{
			*(void**) (&gAXIsProcessTrustedWithOptions) =
				dlsym (handle, "AXIsProcessTrustedWithOptions");

			gkAXTrustedCheckOptionPrompt = (CFStringRef*)
				dlsym (handle, "kAXTrustedCheckOptionPrompt");
		}
	});

	// Check for new OSX 10.9 function
	if (gAXIsProcessTrustedWithOptions)
	{
		// Check whether to show prompt
		auto displayPrompt = options ?
			kCFBooleanTrue : kCFBooleanFalse;

		// Convert display prompt value into a dictionary
		const void* k[] = { *gkAXTrustedCheckOptionPrompt };
		const void* v[] = { displayPrompt };
		CFDictionaryRef o = CFDictionaryCreate
			(nullptr, k, v, 1, nullptr, nullptr);

		// Determine whether the process is actually trusted
		bool result = (*gAXIsProcessTrustedWithOptions) (o);

		// Free memory
		CFRelease (o);
		return result;
	}

	else
	{
		// Ignore deprecated warnings
		#pragma clang diagnostic push
		#pragma clang diagnostic ignored "-Wdeprecated-declarations"

		// Check whether we have accessibility access
		return AXAPIEnabled() || AXIsProcessTrusted();
		#pragma clang diagnostic pop
	}

#endif
#ifdef ROBOT_OS_WIN

	return true;

#endif
}

////////////////////////////////////////////////////////////////////////////////

WindowList Window::GetList (const char* title, int32 pid)
{
	WindowList result;
	// Check if the title is empty
	bool empty = title == nullptr;
	regex regexp; if (!empty) {
		// Attempt to use a case-insensitive regex
		try { regexp = regex (title, regex::icase); }
		catch (...) { return result; }
	}

#ifdef ROBOT_OS_LINUX

	// Check if X-Window display is opened
	if (gDisplay == nullptr) return result;

	// Ignore X errors
	XDismissErrors xe;

	// Prepare enum data
	EnumWindowsData data;
	data.Title  = regexp;
	data.Empty  = empty;
	data.ProcID = pid;
	data.Result = &result;

	// Enumerate windows
	EnumWindows (XDefaultRootWindow (gDisplay), &data);
	return result;

#endif
#ifdef ROBOT_OS_MAC

	// Get windows for specified PID
	auto processPID = [&] (int32 pid)
	{
		// Create accessibility object using the process PID
		auto application = AXUIElementCreateApplication (pid);
		if (application == nullptr) return; // Verify result

		CFArrayRef windows = nullptr;
		// Get all windows associated with the app
		AXUIElementCopyAttributeValues (application,
			kAXWindowsAttribute, 0, 1024, &windows);

		if (windows != nullptr)
		{
			auto count = CFArrayGetCount (windows);
			// Loop all windows in the process
			for (CFIndex i = 0; i < count; ++i)
			{
				// Get the element at the index
				auto element = (AXUIElementRef)
					CFArrayGetValueAtIndex (windows, i);

				CGWindowID win = 0; Window window;
				// Use undocumented API to get WindowID
				if (_AXUIElementGetWindow (element, &win)
					!= kAXErrorSuccess || !win) continue;

				// Manually set internals
				window.mData->CgID = win;
				window.mData->AxID = element;
				CFRetain (element);

				// Attempt to associate and match the current window
				if (empty || regex_match (window.GetTitle(), regexp))
				{
					// Append window to result
					result.push_back (window);
				}
			}

			CFRelease (windows);
		}

		CFRelease (application);
	};

	if (pid == 0)
	{
		// Retrieve list of current processes
		ProcessList list = Process::GetList();
		for (uintptr i = 0; i < list.size(); ++i)
			processPID (list[i].GetPID());
	}

	else
	{
		// Assumed valid
		processPID (pid);
	}

	return result;

#endif
#ifdef ROBOT_OS_WIN

	// Prepare enum data
	EnumWindowsData data;
	data.Title  = regexp;
	data.Empty  = empty;
	data.ProcID = pid;
	data.Result = &result;

	// Enumerate windows
	EnumWindows (EnumWindowsProc, (LPARAM) &data);
	return result;

#endif
}



//----------------------------------------------------------------------------//
// Operators                                                           Window //
//----------------------------------------------------------------------------//

////////////////////////////////////////////////////////////////////////////////

bool Window::operator == (const Window& window) const
{
	return GetHandle() == window.GetHandle();
}

////////////////////////////////////////////////////////////////////////////////

bool Window::operator != (const Window& window) const
{
	return GetHandle() != window.GetHandle();
}

////////////////////////////////////////////////////////////////////////////////

bool Window::operator == (uintptr handle) const
{
	return GetHandle() == handle;
}

////////////////////////////////////////////////////////////////////////////////

bool Window::operator != (uintptr handle) const
{
	return GetHandle() != handle;
}

ROBOT_NS_END
