#define _GNU_SOURCE
#include <dlfcn.h>

#include <stdio.h>
#include <string.h>

#include <X11/Xlib.h>
#include <X11/extensions/xf86vmode.h>
#include <X11/extensions/Xrandr.h>

static Bool triedModeSwitch = False;
static Atom fullScreenAtom = None;

void *(*next_dlopen)(const char *file, int mode);

void *dlopen(const char *file, int mode)
{
	if (!next_dlopen)
		next_dlopen = dlsym(RTLD_NEXT, "dlopen");
	
	// Act as an interposer for these libraries; we _must_ be linked against
	// them for this to work.
	if (file &&
		(strstr(file, "libXxf86vm.so") ||
		 strstr(file, "libXrandr.so") ||
		 strstr(file, "libX11.so")))
	{
		return next_dlopen("libmodeswitch_inhibitor.so.0", mode);
	}
	else
	{
		return next_dlopen(file, mode);
	}
}

Bool XF86VidModeSwitchMode(
	Display *display,
	int screen,
	int zoom)
{
	triedModeSwitch = True;

	return True;
}

Bool XF86VidModeSwitchToMode(
	Display *display,
	int screen,
	XF86VidModeModeInfo *modeline)
{
	triedModeSwitch = True;
	
	return True;
}

Bool XF86VidModeSetViewPort(
	Display *display,
	int screen,
	int x,
	int y)
{
	triedModeSwitch = True;
	
	return True;
}

Status XRRSetCrtcConfig (Display *dpy,
				  XRRScreenResources *resources,
				  RRCrtc crtc,
				  Time timestamp,
				  int x, int y,
				  RRMode mode,
				  Rotation rotation,
				  RROutput *outputs,
				  int noutputs)
{
	// Detect redundancy
	if (XRRGetCrtcInfo(dpy, resources, crtc)->mode != mode)
		triedModeSwitch = True;
	
	return Success;
}

Status XRRSetPanning (Display *dpy,
			   XRRScreenResources *resources,
			   RRCrtc crtc,
			   XRRPanning *panning)
{
	triedModeSwitch = True;
	
	return Success;
}

Status XRRSetScreenConfig (Display *dpy,
						   XRRScreenConfiguration *config,
						   Drawable draw,
						   int size_index,
						   Rotation rotation,
						   Time timestamp)
{
	triedModeSwitch = True;
	
	return Success;
}

Status XRRSetScreenConfigAndRate (Display *dpy,
								  XRRScreenConfiguration *config,
								  Drawable draw,
								  int size_index,
								  Rotation rotation,
								  short rate,
								  Time timestamp)
{
	triedModeSwitch = True;
	
	return Success;
}

void XRRSetScreenSize (Display *dpy, Window window,
				  int width, int height,
				  int mmWidth, int mmHeight)
{
	triedModeSwitch = True;
}

Status (*next_XSendEvent)(Display *display, Window w,
						Bool propagate, long event_mask,
						XEvent *event_send);

Status XSendEvent(Display *display, Window w,
				  Bool propagate, long event_mask,
				  XEvent *event_send)
{
	if (fullScreenAtom == None)
	{
		fullScreenAtom = XInternAtom (display, "_NET_WM_STATE_FULLSCREEN", False);
	}
	
	if (!next_XSendEvent)
		next_XSendEvent = dlsym(RTLD_NEXT, "XSendEvent");

	if (triedModeSwitch && event_send->xclient.data.l[1] == fullScreenAtom)
	{
		// If the app tried to switch modes, it's more than likely already running
		// at a size that is very close to its target; for example Aquaria creates
		// a 640x480 window, tries to switch to that mode, and then still sets the
		// fullscreen bit. Inhibiting that lets the compositor scale the window
		// properly.
		return Success;
	}
	else
	{
		return next_XSendEvent(display, w, propagate, event_mask, event_send);
	}
}

// Tangentially related, but pointer grabs only make it harder for the compositor
// to do its work; in this environment the app in focus effectively achieves the
// same thing as a grab, so it should be fine to just stub that out
int XGrabPointer (Display *display, Window grab_window, Bool owner_events,
				  unsigned int event_mask, int pointer_mode, int keyboard_mode,
				  Window confine_to, Cursor cursor, Time time)
{
	return GrabSuccess;
}
