The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"


#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <X11/extensions/XShm.h>

#include <Imlib2.h>

#define MWM_HINTS_DECORATIONS   (1L << 1)
#define PROP_MWM_HINTS_ELEMENTS 5
typedef struct {
  uint32_t  flags;
  uint32_t  functions;
  uint32_t  decorations;
  int32_t   input_mode;
  uint32_t  status;
} MWMHints;


/* Bitmap data for invisible pointer */
static unsigned char bm_no_data[] = { 0,0,0,0, 0,0,0,0 };

/* Color for invisible pointer */
static XColor black = { 0L, 0, 0, 0, 0, 0 };

struct x11_fullscreen_s {
	SV* display_str;
	Display * display;
	Window window;
};

typedef struct x11_fullscreen_s* X11_FullScreen;

typedef XEvent* X11_FullScreen_Event;

MODULE = X11::FullScreen	PACKAGE = X11::FullScreen PREFIX=x11_fullscreen_

PROTOTYPES: DISABLE

X11_FullScreen
x11_fullscreen_new(pkg, display_str)
	char *pkg
	SV *display_str
	PREINIT:
		X11_FullScreen xefs;
	CODE:
		Newxz(xefs, 1, struct x11_fullscreen_s);
		xefs->display_str = newSVsv(display_str);
		RETVAL = xefs;
	OUTPUT:
		RETVAL

Display *
x11_fullscreen_display(self)
	X11_FullScreen self
	CODE:
		if (self->display == NULL) {
			croak("Display not initialized");
		}
		RETVAL = self->display;
	OUTPUT:
		RETVAL

Window
x11_fullscreen_window(self)
	X11_FullScreen self
	CODE:
		if ( self->window == 0 ) {
			croak("Window not initialized");
		}
		RETVAL = self->window;
	OUTPUT:
		RETVAL

int
x11_fullscreen_screen(self)
	X11_FullScreen self
	CODE:
		if (self->display == NULL) {
			croak("Display not initialized");
		}
		RETVAL = XDefaultScreen(self->display);
	OUTPUT:
		RETVAL
		
void
x11_fullscreen_show(self)
	X11_FullScreen self
	PREINIT:
		int screen;
		int width;
		int height;
		char* display_str;
		Pixmap bm_no;
		Cursor cursor;
		Atom XA_NO_BORDER;
		MWMHints mwmhints;
		XEvent xev;
		Atom wm_state;
		Atom fullscreen;
		Window window;
	CODE:
		if(! XInitThreads() ) {
			croak("Unable to init threads");
		}
		display_str = SvPV_nolen(self->display_str);
		self->display = XOpenDisplay(display_str);
		if ( self->display == NULL ) {
			croak("Unable to open display");
		}
 		XLockDisplay(self->display);
		screen = XDefaultScreen(self->display);
		width = DisplayWidth(self->display, screen);
		height = DisplayWidth(self->display, screen);
 		window = XCreateSimpleWindow(self->display, XDefaultRootWindow(self->display), 0, 0, width, height, 0, 0, 0);
		XSelectInput(self->display, window, (ExposureMask | ButtonPressMask | KeyPressMask | ButtonMotionMask | StructureNotifyMask | PropertyChangeMask | PointerMotionMask));
  XA_NO_BORDER         = XInternAtom(self->display, "_MOTIF_WM_HINTS", False);
  mwmhints.flags       = MWM_HINTS_DECORATIONS;
  mwmhints.decorations = 0;
  XChangeProperty(self->display, window,
		  XA_NO_BORDER, XA_NO_BORDER, 32, PropModeReplace, (unsigned char *) &mwmhints,
		  PROP_MWM_HINTS_ELEMENTS);
		bm_no = XCreateBitmapFromData(self->display,
				XDefaultRootWindow(self->display),
				bm_no_data,
				8,
				8);
  		cursor = XCreatePixmapCursor(self->display, bm_no, bm_no, &black, &black, 0, 0);
  		XDefineCursor(self->display, window, cursor);
		wm_state = XInternAtom(self->display, "_NET_WM_STATE", False);
		fullscreen = XInternAtom(self->display, "_NET_WM_STATE_FULLSCREEN", False);
		memset(&xev, 0, sizeof(xev));
		xev.type = ClientMessage;
		xev.xclient.window = window;
		xev.xclient.message_type = wm_state;
		xev.xclient.format = 32;
		xev.xclient.data.l[0] = 1;
		xev.xclient.data.l[1] = fullscreen;
		xev.xclient.data.l[2] = 0;
		XSendEvent(self->display, DefaultRootWindow(self->display), False, SubstructureNotifyMask, &xev);	
		XMapRaised(self->display, window);
  		XUnlockDisplay(self->display);
  		self->window = window;		

int
x11_fullscreen_display_width(self)
	X11_FullScreen self
	PREINIT:
		int screen;
	CODE:
		if (self->display == NULL) {
			croak("Display not initialized");
		}
		screen = XDefaultScreen(self->display);
		RETVAL = DisplayWidth(self->display, screen);
	OUTPUT:
		RETVAL


int
x11_fullscreen_display_height(self)
	X11_FullScreen self
	PREINIT:
		int screen;
	CODE:
		if (self->display == NULL) {
			croak("Display not initialized");
		}
		screen = XDefaultScreen(self->display);
		RETVAL = DisplayHeight(self->display, screen);
	OUTPUT:
		RETVAL

double
x11_fullscreen_pixel_aspect(self)
	X11_FullScreen self
	PREINIT:
		double res_h;
		double res_v;
		int screen;
	CODE:
		if (self->display == NULL) {
			croak("Display not initialized");
		}
		screen = XDefaultScreen(self->display);
		res_h = (DisplayWidth(self->display,screen) * 1000 / DisplayWidthMM(self->display, screen));
 		res_v = (DisplayHeight(self->display,screen) * 1000 / DisplayHeightMM(self->display, screen));
		RETVAL = res_v / res_h;
	OUTPUT:
		RETVAL

void
x11_fullscreen_close(self)
	X11_FullScreen self
	CODE:
		if (self->display == NULL) {
			croak("Display not initialized");
		}
  		XLockDisplay(self->display);
  		XUnmapWindow(self->display, self->window);
  		XDestroyWindow(self->display, self->window);
 		XUnlockDisplay(self->display);
 		self->window = 0;


void
x11_fullscreen_sync(self)
	X11_FullScreen self
	CODE:
		if (self->display == NULL) {
			croak("Display not initialized");
		}
		XSync(self->display, False);

void
x11_fullscreen_display_still(self,a_mrl)
	X11_FullScreen self
	char * a_mrl
	INIT:
	  int screen_width = 0;
	  int screen_height = 0;
	  XWindowAttributes windowattr;
	  Imlib_Image image;
	  int image_width = 0;
	  int image_height = 0;
	  int x = 0;
	  int y = 0;
	  float width_ratio = 0.0f;
	  float height_ratio = 0.0f;
	  int width;
	  int height;
	CODE:
		if (self->display == NULL) {
			croak("Display not initialized");
		}
  	  XLockDisplay(self->display);
	  if ( XGetWindowAttributes(self->display, self->window, &windowattr) == 0) { 
	  	croak("Failed to get window attributes"); 
	  } 
	  screen_width = windowattr.width; 
	  screen_height = windowattr.height;
	  imlib_context_set_display(self->display);
	  imlib_context_set_visual(DefaultVisual(self->display,DefaultScreen(self->display)));
	  imlib_context_set_colormap(DefaultColormap(self->display,DefaultScreen(self->display)));
	  imlib_context_set_drawable(self->window);
	  image = imlib_load_image_immediately(a_mrl);
	  if (image == NULL) {
 	   croak("Unable to load image '%s'", a_mrl);
	  }
	  imlib_context_set_image(image);
	  image_width = imlib_image_get_width();
	  image_height = imlib_image_get_height();
	  width_ratio =  (float) screen_width / (float) image_width;
	  height_ratio =  (float) screen_height / (float) image_height;
	  if ( width_ratio < height_ratio ) {
	  	height = round( image_height * width_ratio );
	  	width = screen_width;
	  	y = ( screen_height - height ) / 2;
	  }
	  else {
	  	width = round( image_width * height_ratio );
	  	height = screen_height;
	  	x = ( screen_width - width ) / 2;
	  }
	  imlib_render_image_on_drawable_at_size(x,y,width,height);
	  imlib_free_image();
 	  XUnlockDisplay(self->display);

	

void
x11_fullscreen_clear(self)
	X11_FullScreen self
	CODE:
		if (self->display == NULL) {
			croak("Display not initialized");
		}
		XClearWindow(self->display, self->window);

X11_FullScreen_Event
x11_fullscreen_check_event(self, event_mask=( ExposureMask | VisibilityChangeMask ))
	X11_FullScreen self
	long event_mask
	PREINIT:
		XEvent* event;
	CODE:
		if (self->display == NULL) {
			croak("Display not initialized");
		}
		Newx( event, 1, XEvent );
		if ( ! XCheckWindowEvent(
					self->display,
					self->window,
					event_mask,
					event) ) {
                        Safefree(event);
			XSRETURN_UNDEF;
		}
		else {
			RETVAL = event;
		}
	OUTPUT:
		RETVAL


void
x11_fullscreen_DESTROY(self)
	X11_FullScreen self
	CODE:
		SvREFCNT_dec(self->display_str);
		if (self->display != NULL) {
  			XCloseDisplay(self->display);
  		}
  		Safefree(self);
		

MODULE = X11::FullScreen	PACKAGE = X11::FullScreen::Event	PREFIX=x11_fullscreen_event_

int
x11_fullscreen_event_get_type(event)
	X11_FullScreen_Event event
	CODE:
		RETVAL = event->type;
	OUTPUT:
		RETVAL

void
x11_fullscreen_event_DESTROY(event)
	X11_FullScreen_Event event
	CODE:
		Safefree(event);