
/*
 * CINELERRA
 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#ifndef BCSYNCHRONOUS_H
#define BCSYNCHRONOUS_H

#include "arraylist.h"
#include "bcpbuffer.inc"
#include "bcpixmap.inc"
#include "bctexture.inc"
#include "bcwindowbase.inc"
#include "condition.inc"
#include "mutex.inc"
#include "thread.h"
#include "vframe.inc"

#ifdef HAVE_GL
#include <GL/gl.h>
#include <GL/glx.h>
#endif

#include <X11/Xlib.h>

// This takes requests and runs all OpenGL calls in the main thread.
// Past experience showed OpenGL couldn't be run from multiple threads
// reliably even if MakeCurrent was used and only 1 thread at a time did
// anything.

// Also manages texture memory.  Textures are not deleted until the glx_context
// is deleted, so they have to be reused as much as possible.
// Must be run as the main loop of the application.  Other threads must
// call into it to dispatch commands.

// In addition to synchronous operations, it handles global OpenGL variables.

// Users should create a subclass of the command and thread components to
// add specific commands.



class TextureID
{
public:
	TextureID(int window_id, int id, int w, int h, int components);
	int window_id;
	int id;
	int w;
	int h;
	int components;
	BC_WindowBase *window;
	int in_use;
};

class ShaderID
{
public:
	ShaderID(int window_id, unsigned int handle,
		 const char *vert, const char *frag);
	~ShaderID();

// Should really use an MD5 to compare sources but this is easiest.
	char *vert, *frag;
	int window_id;
	unsigned int handle;
};

class PBufferID
{
public:
	PBufferID() {};
#ifdef HAVE_GL
	PBufferID(int window_id,
		GLXPbuffer glx_pbuffer, GLXContext glx_context,
		int w, int h);
	GLXPbuffer glx_pbuffer;
	GLXContext glx_context;
	int window_id;
	int w, h;
	int in_use;
#endif
};

class BC_SynchronousCommand
{
public:
	BC_SynchronousCommand();
	virtual ~BC_SynchronousCommand();

	virtual void copy_from(BC_SynchronousCommand *command);

	Condition *command_done;
	long result;
	int command;

// Commands
	enum
	{
		NONE,
		QUIT,
// Used by garbage collector
		DELETE_WINDOW,
		DELETE_PIXMAP,
		DELETE_DISPLAY,
// subclasses create new commands starting with this enumeration
		LAST_COMMAND
	};

	int colormodel;
	BC_WindowBase *window;

	VFrame *frame;
	VFrame *frame_return;

	int id;
	int w, h;

// For garbage collection
	int window_id;
	Display *display;
	Window win;
#ifdef HAVE_GL
	GLXContext glx_context;
	GLXPixmap glx_pixmap;
	GLXWindow glx_win;
#endif
};

class BC_Synchronous : public Thread
{
public:
	BC_Synchronous();
	virtual ~BC_Synchronous();

	friend class BC_WindowBase;
	friend class VFrame;
	friend class BC_PBuffer;
	friend class BC_Pixmap;
	friend class BC_Texture;

	void quit();
// Must be called after constructor to create inherited objects.
	void create_objects();
	void start();
	void run();

	virtual BC_SynchronousCommand* new_command();
// Handle extra commands not part of the base class.
// Contains a switch statement starting with LAST_COMMAND
	virtual void handle_command(BC_SynchronousCommand *command);


// OpenGL texture removal doesn't work.  Need to store the ID's of all the deleted
// textures in this stack and reuse them.  Also since OpenGL needs synchronous
// commands, be sure this is always called synchronously.
// Called when a texture is created to associate it with the current window.
// Must be called inside synchronous loop.
	void put_texture(int id, int w, int h, int components);
// Search for existing texture matching the parameters and not in use
// and return it.  If -1 is returned, a new texture must be created.
// Must be called inside synchronous loop.
// If someone proves OpenGL can delete texture memory, this function can be
// forced to always return -1.
	int get_texture(int w, int h, int components);
// Release a texture for use by the get_texture call.
// Can be called outside synchronous loop.
	void release_texture(int window_id, int id);

// Get the shader by window_id and vertex/fragment source comparison
	int get_shader(unsigned int *handle, const char *vert, const char *frag);
	void put_shader(unsigned int handle, const char *vert, const char *frag);
	void dump_shader(unsigned int handle);


#ifdef HAVE_GL
// Push a pbuffer when it's created.
// Must be called inside synchronous loop.
	void put_pbuffer(int w, int h,
		GLXPbuffer glx_pbuffer,
		GLXContext glx_context);
// Get the PBuffer by window_id and dimensions if it exists.
// Must be called inside synchronous loop.
	GLXPbuffer get_pbuffer(int w, int h, GLXContext *glx_context);
// Release a pbuffer for use by get_pbuffer.
	void release_pbuffer(int window_id, GLXPbuffer pbuffer);

// Schedule GL pixmap for deletion by the garbage collector.
// Pixmaps don't wait until until the window is deleted but they must be
// deleted before the window is deleted to have the display connection.
	void delete_pixmap(BC_WindowBase *window,
		GLXPixmap pixmap,
		GLXContext context);
#endif



// Called by ~BC_WindowBase to delete OpenGL objects related to the window.
// This function returns immediately instead of waiting for the synchronous
// part to finish.
	void delete_window(BC_WindowBase *window);
	void delete_display(BC_WindowBase *window);

	long send_command(BC_SynchronousCommand *command);
	void send_garbage(BC_SynchronousCommand *command);

	void delete_pixmap_sync(BC_SynchronousCommand *command);
	void delete_window_sync(BC_SynchronousCommand *command);
	void delete_display_sync(BC_SynchronousCommand *command);

// Get the window currently bound to the context.
	BC_WindowBase* get_window();

private:
	void handle_command_base(BC_SynchronousCommand *command);

	Mutex *lock_sync;
	void sync_lock(const char *cp=0);
	void sync_unlock();

	void get_display_sync(Display *display, const char *cp);

	Condition *next_command;
	Mutex *command_lock;

// Must be locked in order of current_window->lock_window, table_lock
// or just table_lock.
	Mutex *table_lock;

// This quits the program when it's 1.
	int done;
// Command stack
	ArrayList<BC_SynchronousCommand*> commands;
	int is_started;
// The window the opengl context is currently bound to.
// Set by BC_WindowBase::enable_opengl.
	BC_WindowBase *current_window;

	ArrayList<ShaderID*> shader_ids;
 	ArrayList<TextureID*> texture_ids;
 	ArrayList<PBufferID*> pbuffer_ids;

// When the context is bound to a pbuffer, this
// signals glCopyTexSubImage2D to use the front buffer.
	int is_pbuffer;
};




#endif
