/* gltest - small OpenGL tearing test program
 * Copyright (C) 2012-2013 Ralf Jung <post@ralfj.de>
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include "glwindow.h"

#include <assert.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <string.h>

GLWindow::~GLWindow()
{
	delete backend;
	if (window)
		XDestroyWindow(display, window);
	XCloseDisplay(display);
}

void GLWindow::create(unsigned int width, unsigned int height)
{
	assert(!window);
	// get visual from backend
	XVisualInfo vinfo_template;
	vinfo_template.visualid = backend->initialize(display);
	int num_vinfo;
	XVisualInfo *vi = XGetVisualInfo(display, VisualIDMask, &vinfo_template, &num_vinfo);
	assert(num_vinfo == 1 && vi != NULL);
	// set winattrs
	XSetWindowAttributes winAttr;
	winAttr.colormap = XCreateColormap(display, RootWindow(display, vi->screen), vi->visual, AllocNone);
	winAttr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask;                                                 
	winAttr.border_pixel = 0;
	// create window
	printf("Initial window size: %dx%d\n", width, height);
	// create a window in window mode
	window = XCreateWindow(display, RootWindow(display, vi->screen),
		0, 0, width, height, 0, vi->depth, InputOutput, vi->visual,
		CWBorderPixel | CWColormap | CWEventMask, &winAttr);
	/* handle wm_delete_events */
	Atom wmDelete = XInternAtom(display, "WM_DELETE_WINDOW", True);
	XMapRaised(display, window);
	XSetWMProtocols(display, window, &wmDelete, 1);
	// done
	this->fullscreen = false;
	this->width = width;
	this->height = height;
	backend->createContext(window);
	initGL();
	resizeGL(width, height);
}

void GLWindow::close()
{
	if (!window) return;
	XEvent ev;
	memset(&ev, 0, sizeof (ev));
	ev.xclient.type = ClientMessage;
	ev.xclient.window = window;
	ev.xclient.message_type = XInternAtom(display, "WM_PROTOCOLS", true);
	ev.xclient.format = 32;
	ev.xclient.data.l[0] = XInternAtom(display, "WM_DELETE_WINDOW", false);
	ev.xclient.data.l[1] = CurrentTime;
	XSendEvent(display, window, False, NoEventMask, &ev);
}

void GLWindow::setFullscreen(bool fullscreen)
{
	assert(window);
	// set fullscreen property
	XEvent e;
	e.xclient.type         = ClientMessage;
	e.xclient.window       = window;
	e.xclient.message_type = XInternAtom(display, "_NET_WM_STATE", true);
	e.xclient.format = 32;
	e.xclient.data.l[0] = 2;    // _NET_WM_STATE_TOGGLE
	e.xclient.data.l[1] = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", fullscreen);
	e.xclient.data.l[2] = 0;    // no second property to toggle
	e.xclient.data.l[3] = 1;
	e.xclient.data.l[4] = 0;
	XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &e);
}

void GLWindow::exec()
{
	while (true)                            
	{                                        
		/* handle the events in the queue */ 
		while (XPending(display) > 0)        
		{
			XEvent event;
			XNextEvent(display, &event);     
			switch (event.type)              
			{
				case Expose:
					renderGL();
					break;
				case ConfigureNotify:
					if (event.xconfigure.width != this->width || event.xconfigure.height != this->height) {
						printf("Window resized to: %dx%d\n", event.xconfigure.width, event.xconfigure.height);
						this->width = event.xconfigure.width;
						this->height = event.xconfigure.height;
						resizeGL(this->width, this->height);
					}
					break;
				case KeyPress:
					handleKeyPress(XLookupKeysym(&event.xkey, 0));
					break;
				case ClientMessage:
					if (strcmp(XGetAtomName(display, event.xclient.message_type), "WM_PROTOCOLS") == 0 &&
						strcmp(XGetAtomName(display, event.xclient.data.l[0]), "WM_DELETE_WINDOW") == 0)
					{
						return;
					}
					break;
				default:
					break;
			}
		}
		renderGL();
	}
}
