//////////////////////////////////////////////
// NovaWM - Nova Window Manager for X11     //
//////////////////////////////////////////////
// By: Tim Walters                          //
//////////////////////////////////////////////
// Copyright (C) 2001-2002 Tim Walters      //
//////////////////////////////////////////////

////////////////////////////////////////////////////////////////
//This code is released under the terms of the GNU GPL. Refer //
//to the license file included with this source code.         //
////////////////////////////////////////////////////////////////


#include "novawm.h"

#include "icon_hide.xpm"
#include "icon_stretch.xpm"
#include "icon_close.xpm"

Display *display;
int screen;
Window root;
unsigned int mainPixel;
Cursor Arrow;
Cursor MoveCursor;
Cursor ResizeCursor;
Cursor ResizeRightCursor;
Cursor ResizeDownCursor;
GC novawm_gc;
XColor color_grey;
XColor color_blue;
XColor color_red;
XColor color_darkblue;
XColor color_navyblue;
XFontStruct *novawmFont;
XFontStruct *nbFont;
Atom stateAtom;
Atom deleteAtom;
Atom protocolAtom;
Atom motifAtom;
Atom novawmAtom;
Atom takefocusAtom;
Atom changestateAtom;
Atom gnomeSupportAtom;
Pixmap killPixmap;
Pixmap stretchPixmap;
Pixmap hidePixmap;
GC killIconGC;
GC stretchIconGC;
GC hideIconGC;
Window gnomeSupportWindow;
NovaWM_Menu rootMenu;
NovaWM_Menu windowMenu;
NovaWM_Menu listMenu;
NovaWM_Menu novaMenu;

Atom netStateAtom;
Atom netSupportAtom;
Atom netClientListAtom;
Atom netDesktopAtom;
Atom netStateStickyAtom;
Atom netStateShadedAtom;
Atom netStateHiddenAtom;
Atom netStateSkipBarAtom;
Atom netStateFloatingAtom;
Atom netStateFullscreenAtom;
Atom netSupportedAtom;

NovaWM novawm;
Events events;
WinMgr winmgr;
Config cfg;

int focusType;

#ifdef _WIN_GNOME_SUPPORT
Atom winProtocolsAtom;
Atom winProtocols_ClientList;
Atom winProtocols_Workspace;
Atom winProtocols_WorkspaceCount;
Atom winProtocols_Layer;
Atom winProtocols_State;
Atom winProtocols_Hints;
Atom winProtocols_AppState;
Atom winProtocols_ExpandedSize;
#endif

static void
signal_handle (int signal)
{
	switch (signal)
	{
	case SIGTERM:
		printf ("NovaWM: Caught SIGTERM! Shutting down...\n");
		novawm.Shutdown ();
		break;

	case SIGSEGV:
		printf ("NovaWM: Caught SIGSEGV (Segmentation Fault) !! Attempting to quit quickly...\n");
		novawm.Quick_Shutdown ();
		exit (0);
		break;
	}
}

int
ErrorHandler (Display * dsp, XErrorEvent * event)
{
	char text[300];

	if ((event->error_code == BadAccess) && (event->resourceid == root))
	{
		fprintf (stderr,
			 "NovaWM: Another window manager is already running on that display.\n");
		exit (1);
	}
	XGetErrorText (display, event->error_code, text, sizeof (text));

	return 0;
}


char *displayName;

int
main (int argc, char *argv[])
{
	bool badop = true;
	displayName = NULL;
	int timeFormat = 1;
	cfg.disableBar = false;

	printf ("Nova Window Manager\n");
	printf ("Author: Tim Walters\n");
	printf ("E-Mail: realmi2@yahoo.com\n");

	for (int i = 1; i < argc; i++)
	{
		if (strcmp (argv[i], "--version") == 0)
		{
			printf ("NovaWM Version: " VERSION "\n");
			return 0;
		}
		if (strcmp (argv[i], "-version") == 0)
		{
			printf ("NovaWM Version: " VERSION "\n");
			return 0;
		}
		if (strcmp (argv[i], "-display") == 0)
		{
			displayName = argv[i + 1];
			setenv ("DISPLAY", displayName, true);
			badop = false;
		}
		if (strcmp (argv[i], "--display") == 0)
		{
			displayName = argv[i + 1];
			badop = false;
		}
		if (strcmp (argv[i], "-timeformat") == 0)
		{
			timeFormat = atoi (argv[i + 1]);
			badop = false;
		}
		if (strcmp (argv[i], "--timeformat") == 0)
		{
			timeFormat = atoi (argv[i + 1]);
			badop = false;
		}
		if (strcmp (argv[i], "--disable-bar") == 0)
		{
			cfg.disableBar = true;
			badop = false;
		}
		if (strcmp (argv[i], "--help") == 0)
		{
			printf ("Options:\n");
			printf ("\t--version\t\tShows NovaWM Version information.\n");
			printf ("\t--help\t\t\tShows this message.\n");
			printf ("\t--timeformat 1|2\tSets the time format.\n");
			printf ("\t--display displayname\tSets the X Display\n");
			printf ("\t--disable-bar\tDisables NovaBar(Root menus are provided).\n");
			return 0;
		}
		if (badop == true)
		{
			printf ("Bad Option!\n");
			printf ("Options:\n");
			printf ("\t--version\t\tShows NovaWM Version information.\n");
			printf ("\t--help\t\t\tShows this message.\n");
			printf ("\t--timeformat 1|2\tSets the time format.\n");
			printf ("\t--display displayname\tSets the X Display\n");
			printf ("\t--disable-bar\tDisables NovaBar(Root menus are provided).\n");
			return 0;
		}
	}

	focusType = 1;

	void (*signal_handler) (int);
	signal_handler = signal (SIGSEGV, signal_handle);
	signal_handler = signal (SIGTERM, signal_handle);

	//winmgr = new WinMgr;

	cfg.mainColor = COLOR_MAIN;

	cfg.LoadConfig ();

	novawm.X11_Init ();
	novawm.WM_Start ();

//      cfg.disableBar = disableBar;

	events.NovaWM_EventLoop ();

	return (0);
}

void
NovaWM::X11_Init ()
{
	XGCValues gcvalues;
	XColor cdummy;

	display = XOpenDisplay (displayName);
	if (!display)
	{
		printf ("novawm: failed to open display: %s\n", displayName);
		exit (0);
	}

	screen = DefaultScreen (display);
	root = RootWindow (display, screen);

	XSetErrorHandler (ErrorHandler);

	XSetWindowAttributes attributes;
	attributes.event_mask =
		ButtonPressMask | ButtonReleaseMask | SubstructureNotifyMask |
		SubstructureRedirectMask | PropertyChangeMask |
		PointerMotionMask | FocusChangeMask | EnterWindowMask |
		LeaveWindowMask;

	XChangeWindowAttributes (display, root, CWEventMask, &attributes);

	Arrow = XCreateFontCursor (display, XC_left_ptr);
	MoveCursor = XCreateFontCursor (display, XC_fleur);
	XDefineCursor (display, root, Arrow);

	novawmFont = XLoadQueryFont (display, "lucidasans-12");
	if (!novawmFont)
	{
		printf ("Failed to load font!\n");
		novawmFont = XLoadQueryFont (display, "fixed");
	}

	nbFont = XLoadQueryFont (display, "lucidasans-10");


	gcvalues.function = GXcopy;
	gcvalues.foreground = WhitePixel (display, screen);
	gcvalues.line_width = 1;
	gcvalues.font = novawmFont->fid;

	novawm_gc =
		XCreateGC (display, root,
			   GCFunction | GCForeground | GCLineWidth | GCFont,
			   &gcvalues);

	XColor tmpColor;
	XAllocNamedColor (display, DefaultColormap (display, screen),
			  cfg.mainColor.c_str (), &tmpColor, &cdummy);

	mainPixel = tmpColor.pixel;
}

void
NovaWM::WM_Start ()
{
	Window wdummy;
	Window *windows;
	unsigned int nchildren = 0;
	unsigned int i = 0;

	stateAtom = XInternAtom (display, "WM_STATE", false);
	deleteAtom = XInternAtom (display, "WM_DELETE_WINDOW", false);
	protocolAtom = XInternAtom (display, "WM_PROTOCOLS", false);
	motifAtom = XInternAtom (display, "_MOTIF_WM_HINTS", false);
	takefocusAtom = XInternAtom (display, "WM_TAKE_FOCUS", false);
	changestateAtom = XInternAtom (display, "WM_CHANGE_STATE", false);
	novawmAtom = XInternAtom (display, "NOVAWM_HINTS", false);

	netSupportAtom =
		XInternAtom (display, "_NET_SUPPORTING_WM_CHECK", false);
	netStateAtom = XInternAtom (display, "_NET_WM_STATE", false);
	netClientListAtom = XInternAtom (display, "_NET_CLIENT_LIST", false);
	netDesktopAtom = XInternAtom (display, "_NET_WM_DESKTOP", false);
	netStateStickyAtom =
		XInternAtom (display, "_NET_WM_STATE_STICKY", false);
	netStateShadedAtom =
		XInternAtom (display, "_NET_WM_STATE_SHADED", false);
	netStateHiddenAtom =
		XInternAtom (display, "_NET_WM_STATE_HIDDEN", false);
	netStateSkipBarAtom =
		XInternAtom (display, "_NET_WM_STATE_SKIP_TASKBAR", false);
	netStateFloatingAtom =
		XInternAtom (display, "_NET_WM_STATE_FLOATING", false);
	netStateFullscreenAtom =
		XInternAtom (display, "_NET_WM_STATE_FULLSCREEN", false);

#ifdef _WIN_GNOME_SUPPORT
	//GNOME Support
	gnomeSupportAtom =
		XInternAtom (display, "_WIN_SUPPORTING_WM_CHECK", false);
	winProtocolsAtom = XInternAtom (display, "_WIN_PROTOCOLS", false);
	winProtocols_ClientList =
		XInternAtom (display, "_WIN_CLIENT_LIST", false);
	winProtocols_Workspace =
		XInternAtom (display, "_WIN_WORKSPACE", false);
	winProtocols_WorkspaceCount =
		XInternAtom (display, "_WIN_WORKSPACE_COUNT", false);
	winProtocols_State = XInternAtom (display, "_WIN_STATE", false);
	winProtocols_Layer = XInternAtom (display, "_WIN_LAYER", false);
#endif

	//Setup the menus
	//Setup the Root Menu
	rootMenu.AddItem ("xterm");
	rootMenu.AddItem ("?+MENU_SPLIT+?");
	rootMenu.AddItem ("Iconify Window");
	rootMenu.AddItem ("Kill Window");
	rootMenu.AddItem ("?+MENU_SPLIT+?");
	rootMenu.AddItem ("Restart");
	rootMenu.AddItem ("Exit");
	//Setup the Window Menu
	windowMenu.AddItem ("Hide");
	windowMenu.AddItem ("Maximize");
	windowMenu.AddItem ("Resize");
	windowMenu.AddItem ("Kill");
	windowMenu.AddItem ("?+MENU_SPLIT+?");
	windowMenu.AddItem ("Send to Desktop 1");
	windowMenu.AddItem ("Send to Desktop 2");
	windowMenu.AddItem ("Send to Desktop 3");
	windowMenu.AddItem ("Send to Desktop 4");
	windowMenu.AddItem ("Send to Next Desktop");
	windowMenu.AddItem ("Send to Previous Desktop");

	//The NovaBar Menu/NovaMenu looks better with the smaller font
	novaMenu.SetFont (nbFont);
	novaMenu.SetItemHeight (12);

	novaMenu.AddItem ("?+MENU_SPLIT+?");
	novaMenu.AddItem ("Restart");
	novaMenu.AddItem ("Exit");


	winmgr.maxDesktops = 10;
	winmgr.nDesktops = 1;

	winmgr.nClients = 0;

	XSetInputFocus (display, root, RevertToNone, CurrentTime);

	//Grab the previous desktop key
	XGrabKey (display, XKeysymToKeycode (display, XK_bracketleft),
		  ControlMask, root, true, GrabModeAsync, GrabModeAsync);

	//Grab the next desktop key
	XGrabKey (display, XKeysymToKeycode (display, XK_bracketright),
		  ControlMask, root, true, GrabModeAsync, GrabModeAsync);

	winmgr.SetDesktop (0);


	//Create a window with the _WIN_SUPPORTING_WM_CHECK to be a GNOME
	//Compliant Window Manager.
	gnomeSupportWindow =
		XCreateSimpleWindow (display, root, -200, -200, 5, 5, 0, 0,
				     0);

#ifdef _WIN_GNOME_SUPPORT
	XChangeProperty (display, gnomeSupportWindow, gnomeSupportAtom,
			 XA_CARDINAL, 32, PropModeReplace,
			 (unsigned char *) &gnomeSupportWindow, 1);

	XChangeProperty (display, root, gnomeSupportAtom, XA_CARDINAL, 32,
			 PropModeReplace,
			 (unsigned char *) &gnomeSupportWindow, 1);
#endif

	//Do the same for _NET_SUPPORTING_WM_CHECK for ICCCM Compliance
	XChangeProperty (display, root, netSupportAtom, XA_CARDINAL, 32,
			 PropModeReplace,
			 (unsigned char *) &gnomeSupportWindow, 1);

	XChangeProperty (display, netSupportAtom, gnomeSupportAtom,
			 XA_CARDINAL, 32, PropModeReplace,
			 (unsigned char *) &gnomeSupportWindow, 1);

#ifdef _WIN_GNOME_SUPPORT
	Atom winProtocolsList[7];
	winProtocolsList[0] = winProtocols_ClientList;
	winProtocolsList[1] = winProtocols_WorkspaceCount;
	winProtocolsList[2] = winProtocols_WorkspaceCount;

	//Set the _WIN_PROTOCOLS list to the root window (GNOME Support)
	XChangeProperty (display, root, winProtocolsAtom, XA_ATOM, 32,
			 PropModeReplace, (unsigned char *) winProtocolsList,
			 3);

	//Set the number of desktops
	CARD32 desktopNum = (CARD32) winmgr.nDesktops;
	XChangeProperty (display, root,
			 winProtocols_WorkspaceCount,
			 XA_CARDINAL, 32, PropModeReplace,
			 (unsigned char *) &desktopNum, 1);

#endif


	//Load the button icons
	killPixmap = LoadXPM (icon_close_xpm, 16, 16, &killIconGC);
	stretchPixmap = LoadXPM (icon_stretch_xpm, 16, 16, &stretchIconGC);
	hidePixmap = LoadXPM (icon_hide_xpm, 16, 16, &hideIconGC);

	//Reparent all windows
	XQueryTree (display, root, &wdummy, &wdummy, &windows, &nchildren);
	for (i = 0; i < nchildren; i++)
	{
		if (windows[i] && windows[i] != gnomeSupportWindow)
			winmgr.NewWindow (windows[i]);
	}

	XFree (windows);

	CreateBar ();
}

void
NovaWM::Restart ()
{
#ifdef DEBUG
	printf ("DEBUG: Restart()\n");
#endif

//      for (Novawm_Window * DLWindow = winmgr.topWindow; DLWindow;
//           DLWindow = DLWindow->prev)
	for (list < Novawm_Window * >::const_iterator i =
	     winmgr.novawm_window_List.begin ();
	     i != winmgr.novawm_window_List.end (); ++i)
	{
		if ((*i)->hasTitlebar == true)
		{
			XReparentWindow (display, (*i)->window, root, (*i)->x,
					 (*i)->y);
			XDestroyWindow (display, (*i)->titlebar);
		}
		(*i)->Destroy ();
	}

	DestroyBar ();

	XFreeFont (display, novawmFont);

	XFreeGC (display, novawm_gc);

	XFreeCursor (display, Arrow);

	XFreeCursor (display, ResizeCursor);

	XFreeCursor (display, ResizeRightCursor);

	XFreeCursor (display, ResizeDownCursor);

	XFreePixmap (display, killPixmap);

	XFreePixmap (display, stretchPixmap);

	XFreePixmap (display, hidePixmap);

	XSetInputFocus (display, root, RevertToPointerRoot, CurrentTime);

	//Grab the previous desktop key
	XUngrabKey (display, XKeysymToKeycode (display, XK_bracketleft),
		    ControlMask, root);

	//Grab the next desktop key
	XUngrabKey (display, XKeysymToKeycode (display, XK_bracketright),
		    ControlMask, root);

//	delete winmgr;

	XCloseDisplay (display);

	Run ("novawm");
}

void
NovaWM::Quick_Shutdown ()
{
	//If this is not called it seems that you can't give
	//any window input focus after a seg fault
//      XSetInputFocus(display,root,RevertToPointerRoot,CurrentTime); 

	//XCloseDisplay() is not called here because for
	//some reason it can be another source of seg faults
	//which means a call to this function will be looped.

	exit (0);
}

void
NovaWM::Shutdown ()
{
#ifdef DEBUG
	printf ("DEBUG: Shutdown()\n");
#endif

	XSetInputFocus (display, root, RevertToPointerRoot, CurrentTime);

	//for (Novawm_Window * DLWindow = winmgr.topWindow; DLWindow;
	//     DLWindow = DLWindow->prev)
	for (list < Novawm_Window * >::const_iterator i =
	     winmgr.novawm_window_List.begin ();
	     i != winmgr.novawm_window_List.end (); ++i)
	{
		if ((*i)->hasTitlebar == true)
		{
			XReparentWindow (display, (*i)->window, root, (*i)->x,
					 (*i)->y);
			XDestroyWindow (display, (*i)->titlebar);
		}
		(*i)->Destroy ();
	}

#ifdef DEBUG
	printf ("NovaWM DEBUG: Freeing Menu Execution List...\n");
#endif

	cfg.itemExec.clear ();

	DestroyBar ();

	XFreeFont (display, novawmFont);

	XFreeGC (display, novawm_gc);

	XSetInputFocus (display, PointerRoot, RevertToPointerRoot,
			CurrentTime);

	//Grab the previous desktop key
	XUngrabKey (display, XKeysymToKeycode (display, XK_bracketleft),
		    ControlMask, root);

	//Grab the next desktop key
	XUngrabKey (display, XKeysymToKeycode (display, XK_bracketright),
		    ControlMask, root);

//	delete winmgr;

	XCloseDisplay (display);

	exit (0);
}

void
NovaWM::Run (char *command)
{

	pid_t procid = fork ();

	switch (procid)
	{
	case 0:
		execlp ("/bin/sh", "sh", "-c", command, NULL);
		printf ("Failed to execute program.\n");
		exit (1);
	case -1:
		printf ("Failed to fork\n");

	}

}

//Modifies a XPM and replaces Transparent/None pixels with MAIN_COLOR
char **
XPM_FillTransp (char **xpm, int width, int height)
{
	char *newColor = 0;
	newColor = (char *) malloc (16);	//new char;
	if (!newColor)
	{
		printf ("NovaWM CRITICAL ERROR: XPM_FillTransp(): Your system appears to have run out of memory. NovaWM will now exit.\n");
		exit (0);
	}

	snprintf (newColor, 16, " 	c %s", cfg.mainColor.c_str ());

	xpm[1] = newColor;

	return xpm;
}

Pixmap
NovaWM::LoadXPM (char **xpm_data, int width, int height, GC * xpmgc)
{
	GC xpm_gc;
	XGCValues gc_values;

	gc_values.function = GXcopy;
	gc_values.foreground = mainPixel;	//BlackPixel (display, screen);
	gc_values.background = mainPixel;

	printf ("NovaWM: Fixing XPM Icon...\n");
	XPM_FillTransp (xpm_data, width, height);

	Pixmap pmap =
		XCreatePixmap (display, gnomeSupportWindow, width,
			       height, DefaultDepth (display, screen));

	XpmAttributes attributes;
	Pixmap xmask;

	attributes.valuemask = XpmColormap | XpmCloseness;
	attributes.colormap = DefaultColormap (display, screen);
	attributes.closeness = 65535;

	if (XpmCreatePixmapFromData
	    (display, gnomeSupportWindow, xpm_data, &pmap, &xmask,
	     &attributes) != XpmSuccess)
		return 0;

	xpm_gc = XCreateGC (display, pmap,
			    GCFunction | GCForeground | GCBackground,
			    &gc_values);

	XSetFillStyle (display, xpm_gc, FillTiled);

	XSetTile (display, xpm_gc, pmap);

	XSetClipMask (display, xpm_gc, xmask);

	*xpmgc = xpm_gc;

	return pmap;
}

void
NovaWM::SetNetSupported ()
{
#define NET_SUPPORTED_ATOMS 10
	/*Atom supported_Net_Atoms[NET_SUPPORTED_ATOMS] = {
		netSupportedAtom, netClientListAtom,
		net
	};*/
}


syntax highlighted by Code2HTML, v. 0.9.1