<commit_before>// fluxbox.cc for Fluxbox Window Manager
// Copyright (c) 2001 - 2002 <NAME> (<EMAIL>)
//
// blackbox.cc for blackbox - an X11 Window manager
// Copyright (c) 1997 - 2000 <NAME> (<EMAIL>)
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.	IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

// $Id: fluxbox.cc,v 1.26 2002/02/04 06:57:26 fluxgen Exp $

//Use some GNU extensions
#ifndef	 _GNU_SOURCE
#define	 _GNU_SOURCE
#endif // _GNU_SOURCE

#ifdef		HAVE_CONFIG_H
#	include "../config.h"
#endif // HAVE_CONFIG_H

#include "i18n.hh"
#include "fluxbox.hh"
#include "Basemenu.hh"
#include "Clientmenu.hh"
#include "Rootmenu.hh"
#include "Screen.hh"

#ifdef SLIT
#include "Slit.hh"
#endif // SLIT

#include "Toolbar.hh"
#include "Window.hh"
#include "Workspace.hh"
#include "Workspacemenu.hh"
#include "StringUtil.hh"
#include "Resource.hh"
#include "XrmDatabaseHelper.hh"

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xresource.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>

#ifdef		SHAPE
#include <X11/extensions/shape.h>
#endif // SHAPE


#ifdef		HAVE_STDIO_H
#	include <stdio.h>
#endif // HAVE_STDIO_H

#ifdef		STDC_HEADERS
#	include <stdlib.h>
#	include <string.h>
#endif // STDC_HEADERS

#ifdef		HAVE_UNISTD_H
#	include <sys/types.h>
#	include <unistd.h>
#endif // HAVE_UNISTD_H

#ifdef		HAVE_SYS_PARAM_H
#	include <sys/param.h>
#endif // HAVE_SYS_PARAM_H

#ifndef	 MAXPATHLEN
#define	 MAXPATHLEN 255
#endif // MAXPATHLEN

#ifdef		HAVE_SYS_SELECT_H
#	include <sys/select.h>
#endif // HAVE_SYS_SELECT_H

#ifdef		HAVE_SIGNAL_H
#	include <signal.h>
#endif // HAVE_SIGNAL_H

#ifdef		HAVE_SYS_SIGNAL_H
#	include <sys/signal.h>
#endif // HAVE_SYS_SIGNAL_H

#ifdef		HAVE_SYS_STAT_H
#	include <sys/types.h>
#	include <sys/stat.h>
#endif // HAVE_SYS_STAT_H

#ifdef		TIME_WITH_SYS_TIME
#	include <sys/time.h>
#	include <time.h>
#else // !TIME_WITH_SYS_TIME
#	ifdef		HAVE_SYS_TIME_H
#		include <sys/time.h>
#	else // !HAVE_SYS_TIME_H
#		include <time.h>
#	endif // HAVE_SYS_TIME_H
#endif // TIME_WITH_SYS_TIME

#ifdef		HAVE_LIBGEN_H
#	include <libgen.h>
#endif // HAVE_LIBGEN_H

#include <iostream>
#include <string>
#include <strstream>
#include <memory>

using namespace std;

#ifndef	 HAVE_BASENAME
static inline char *basename (char *);
static inline char *basename (char *s) {
	char *save = s;

	while (*s) if (*s++ == '/') save = s;

	return save;
}
#endif // HAVE_BASENAME

#define RC_PATH "fluxbox"
#define RC_INIT_FILE "init"


// X event scanner for enter/leave notifies - adapted from twm
typedef struct scanargs {
	Window w;
	Bool leave, inferior, enter;
} scanargs;

static Bool queueScanner(Display *, XEvent *e, char *args) {
	if ((e->type == LeaveNotify) &&
			(e->xcrossing.window == ((scanargs *) args)->w) &&
			(e->xcrossing.mode == NotifyNormal)) {
		((scanargs *) args)->leave = True;
		((scanargs *) args)->inferior = (e->xcrossing.detail == NotifyInferior);
	} else if ((e->type == EnterNotify) &&
			(e->xcrossing.mode == NotifyUngrab))
		((scanargs *) args)->enter = True;

	return False;
}
//-----------------------------------------------------------------
//---- accessors for int, bool, and some enums with Resource ------
//-----------------------------------------------------------------
template<>
void Resource<int>::
setFromString(const char* strval) {
	int val;
	if (sscanf(strval, "%d", &val)==1)
		*this = val;
}

template<>
void Resource<std::string>::
setFromString(const char *strval) {
	*this = strval;
}

template<>
void Resource<bool>::
setFromString(char const *strval) {
	if (strcasecmp(strval, "true")==0)
		*this = true;
	else
		*this = false;
}

template<>
void Resource<Fluxbox::TitlebarList>::
setFromString(char const *strval) {
	vector<std::string> val;
	StringUtil::stringtok(val, strval);
	int size=val.size();
	//clear old values
	m_value.clear();
		
	for (int i=0; i<size; i++) {
		if (strcasecmp(val[i].c_str(), "Maximize")==0)
			m_value.push_back(Fluxbox::MAXIMIZE);
		else if (strcasecmp(val[i].c_str(), "Minimize")==0)
			m_value.push_back(Fluxbox::MINIMIZE);
		else if (strcasecmp(val[i].c_str(), "Shade")==0)
			m_value.push_back(Fluxbox::SHADE);
		else if (strcasecmp(val[i].c_str(), "Stick")==0)
			m_value.push_back(Fluxbox::STICK);
		else if (strcasecmp(val[i].c_str(), "Menu")==0)
			m_value.push_back(Fluxbox::MENU);
		else if (strcasecmp(val[i].c_str(), "Close")==0)
			m_value.push_back(Fluxbox::CLOSE);
	}
}

template<>
void Resource<unsigned int>::
setFromString(const char *strval) {	
	if (sscanf(strval, "%ul", &m_value) != 1)
		setDefaultValue();
}

//-----------------------------------------------------------------
//---- manipulators for int, bool, and some enums with Resource ---
//-----------------------------------------------------------------
template<>
std::string Resource<bool>::
getString() {				
	return std::string(**this == true ? "true" : "false");
}

template<>
std::string Resource<int>::
getString() {
	char strval[256];
	sprintf(strval, "%d", **this);
	return std::string(strval);
}

template<>
std::string Resource<std::string>::
getString() { return **this; }

template<>
std::string Resource<Fluxbox::TitlebarList>::
getString() {
	string retval;
	int size=m_value.size();
	for (int i=0; i<size; i++) {
		switch (m_value[i]) {
			case Fluxbox::SHADE:
				retval.append("Shade");
				break;
			case Fluxbox::MINIMIZE:
				retval.append("Minimize");
				break;
			case Fluxbox::MAXIMIZE:
				retval.append("Maximize");
				break;
			case Fluxbox::CLOSE:
				retval.append("Close");
				break;
			case Fluxbox::STICK:
				retval.append("Stick");
				break;
			case Fluxbox::MENU:
				retval.append("Menu");
				break;
			default:
				break;
		}
		retval.append(" ");
	}

	return retval;
}

template<>
string Resource<unsigned int>::
getString() {
	char tmpstr[128];
	sprintf(tmpstr, "%ul", m_value);
	return string(tmpstr);
}

//static singleton var
Fluxbox *Fluxbox::singleton=0;

//------------ instance ---------------------
//returns singleton object of blackbox class
//since we only need to create one instance of Fluxbox
//-------------------------------------------
Fluxbox *Fluxbox::instance(int m_argc, char **m_argv, char *dpy_name, char *rc) {
	return singleton;
}


Fluxbox::Fluxbox(int m_argc, char **m_argv, char *dpy_name, char *rc)
: BaseDisplay(m_argv[0], dpy_name),
m_resourcemanager(), m_screen_rm(),
m_rc_tabs(m_resourcemanager, true, "session.tabs", "Session.Tabs"),
m_rc_iconbar(m_resourcemanager, true, "session.iconbar", "Session.Iconbar"),
m_rc_colors_per_channel(m_resourcemanager, 4, "session.colorsPerChannel", "Session.ColorsPerChannel"),
m_rc_stylefile(m_resourcemanager, "", "session.styleFile", "Session.StyleFile"),
m_rc_menufile(m_resourcemanager, DEFAULTMENU, "session.menuFile", "Session.MenuFile"),
m_rc_keyfile(m_resourcemanager, DEFAULTKEYSFILE, "session.keyFile", "Session.KeyFile"),
m_rc_titlebar_left(m_resourcemanager, TitlebarList(0), "session.titlebar.left", "Session.Titlebar.Left"),
m_rc_titlebar_right(m_resourcemanager, TitlebarList(0), "session.titlebar.right", "Session.Titlebar.Right"),
m_rc_cache_life(m_resourcemanager, 5, "session.cacheLife", "Session.CacheLife"),
m_rc_cache_max(m_resourcemanager, 200, "session.cacheMax", "Session.CacheMax"),
focused_window(0),
masked_window(0),
no_focus(False),
rc_file(rc),
argv(m_argv), argc(m_argc), 
key(0)
{

	//singleton pointer
	singleton = this;
	BaseDisplay::GrabGuard gg(*this);
	gg.grab();

	if (! XSupportsLocale())
		fprintf(stderr, "X server does not support locale\n");

	if (XSetLocaleModifiers("") == NULL)
		fprintf(stderr, "cannot set locale modifiers\n");

// Set default values to member variables

	resource.auto_raise_delay.tv_sec = resource.auto_raise_delay.tv_usec = 0;
	
	masked = None;
	
	windowSearchList = new LinkedList<WindowSearch>;
	menuSearchList = new LinkedList<MenuSearch>;

#ifdef SLIT
	slitSearchList = new LinkedList<SlitSearch>;
	#ifdef KDE
	//For KDE dock applets
	kwm1_dockwindow = XInternAtom(getXDisplay(), "KWM_DOCKWINDOW", False); //KDE v1.x
	kwm2_dockwindow = XInternAtom(getXDisplay(), "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", False); //KDE v2.x
	#endif //KDE

#endif // SLIT

	toolbarSearchList = new LinkedList<ToolbarSearch>;
	tabSearchList = new LinkedList<TabSearch>;
	groupSearchList = new LinkedList<WindowSearch>;

	menuTimestamps = new LinkedList<MenuTimestamp>;


	#ifdef HAVE_GETPID
	fluxbox_pid = XInternAtom(getXDisplay(), "_BLACKBOX_PID", False);
	#endif // HAVE_GETPID

	screenList = new LinkedList<BScreen>;
	int i;
	load_rc();
	//allocate screens
	for (i = 0; i < getNumberOfScreens(); i++) {
		char scrname[128], altscrname[128];
		sprintf(scrname, "session.screen%d", i);
		sprintf(altscrname, "session.Screen%d", i);
		BScreen *screen = new BScreen(m_screen_rm, this, scrname, altscrname, i);

		if (! screen->isScreenManaged()) {
			delete screen;
			continue;
		}

		screenList->insert(screen);
	}
	
	I18n *i18n = I18n::instance();
	if (! screenList->count()) {
		fprintf(stderr,
			i18n->
				getMessage(
#ifdef		NLS
				blackboxSet, blackboxNoManagableScreens,
#else // !NLS
				 0, 0,
#endif // NLS
				"Fluxbox::Fluxbox: no managable screens found, aborting.\n"));

		throw static_cast<int>(3);
	}

	XSynchronize(getXDisplay(), False);
	XSync(getXDisplay(), False);

	reconfigure_wait = reread_menu_wait = False;

	timer = new BTimer(this, this);
	timer->setTimeout(0);
	timer->fireOnce(True);

	//create keybindings handler and load keys file
	key = new Keys(getXDisplay(), const_cast<char *>((*m_rc_keyfile).c_str()));

	ungrab();
}


Fluxbox::~Fluxbox(void) {
	
	while (screenList->count())
		delete screenList->remove(0);

	while (menuTimestamps->count()) {
		MenuTimestamp *ts = menuTimestamps->remove(0);

		if (ts->filename)
			delete [] ts->filename;

		delete ts;
	}
	
	delete key;
	key = 0;

	delete timer;

	delete screenList;
	delete menuTimestamps;

	delete windowSearchList;
	delete menuSearchList;
	delete toolbarSearchList;
	delete tabSearchList;
	delete groupSearchList;

#ifdef		SLIT
	delete slitSearchList;
#endif // SLIT
}


void Fluxbox::process_event(XEvent *e) {

	if ((masked == e->xany.window) && masked_window &&
			(e->type == MotionNotify)) {
		last_time = e->xmotion.time;
		masked_window->motionNotifyEvent(&e->xmotion);

		return;
	}
	
	switch (e->type) {
	case ButtonPress:
		{
			last_time = e->xbutton.time;

			FluxboxWindow *win = (FluxboxWindow *) 0;
			Basemenu *menu = (Basemenu *) 0;

			#ifdef		SLIT
			Slit *slit = (Slit *) 0;
			#endif // SLIT

			Toolbar *tbar = (Toolbar *) 0;			
			Tab *tab = 0;

			if ((win = searchWindow(e->xbutton.window))) {

				win->buttonPressEvent(&e->xbutton);
				
				if (e->xbutton.button == 1)
					win->installColormap(True);
					
			} else if ((menu = searchMenu(e->xbutton.window))) {
				menu->buttonPressEvent(&e->xbutton);

			#ifdef		SLIT
			} else if ((slit = searchSlit(e->xbutton.window))) {
				slit->buttonPressEvent(&e->xbutton);
			#endif // SLIT

			} else if ((tbar = searchToolbar(e->xbutton.window))) {
				tbar->buttonPressEvent(&e->xbutton);
			} else if ((tab = searchTab(e->xbutton.window))) {
				tab->buttonPressEvent(&e->xbutton);
			} else {
				LinkedListIterator<BScreen> it(screenList);

				for (; it.current(); it++) {
					BScreen *screen = it.current();
				

						//if ( e->xbutton.button == 1 && 
						//	(win = screen->getIconBar()->findWindow(e->xbutton.window)) ) {
						//	win->deiconify();
						
				 if (e->xbutton.window == screen->getRootWindow()) {
					if (e->xbutton.button == 1) {
							if (! screen->isRootColormapInstalled())
							screen->getImageControl()->installRootColormap();

						if (screen->getWorkspacemenu()->isVisible())
							screen->getWorkspacemenu()->hide();

						if (screen->getRootmenu()->isVisible())
							screen->getRootmenu()->hide();
						
					} else if (e->xbutton.button == 2) {
						int mx = e->xbutton.x_root -
							(screen->getWorkspacemenu()->getWidth() / 2);
						int my = e->xbutton.y_root -
							(screen->getWorkspacemenu()->getTitleHeight() / 2);
	
						if (mx < 0) mx = 0;
						if (my < 0) my = 0;

						if (mx + screen->getWorkspacemenu()->getWidth() >
							 screen->getWidth())
								mx = screen->getWidth() -
						screen->getWorkspacemenu()->getWidth() -
						screen->getBorderWidth();

						if (my + screen->getWorkspacemenu()->getHeight() >
									screen->getHeight())
							my = screen->getHeight() -
						screen->getWorkspacemenu()->getHeight() -
						screen->getBorderWidth();

						screen->getWorkspacemenu()->move(mx, my);

						if (! screen->getWorkspacemenu()->isVisible()) {
							screen->getWorkspacemenu()->removeParent();
							screen->getWorkspacemenu()->show();
						}
					} else if (e->xbutton.button == 3) {
						int mx = e->xbutton.x_root -
						 (screen->getRootmenu()->getWidth() / 2);
						int my = e->xbutton.y_root -
						 (screen->getRootmenu()->getTitleHeight() / 2);

						if (mx < 0) mx = 0;
						if (my < 0) my = 0;

						if (mx + screen->getRootmenu()->getWidth() > screen->getWidth())
							mx = screen->getWidth() -
						screen->getRootmenu()->getWidth() -
						screen->getBorderWidth();

						if (my + screen->getRootmenu()->getHeight() >
							screen->getHeight())
							my = screen->getHeight() -
								screen->getRootmenu()->getHeight() -
								screen->getBorderWidth();

						screen->getRootmenu()->move(mx, my);

						if (! screen->getRootmenu()->isVisible()) {
							checkMenu();
							screen->getRootmenu()->show();
						}
						
					} 
			
				}
			}
		}

			break;
	}

	case ButtonRelease:
	{
			last_time = e->xbutton.time;
			FluxboxWindow *win = (FluxboxWindow *) 0;
			Basemenu *menu = (Basemenu *) 0;
			Toolbar *tbar = (Toolbar *) 0;
			Tab *tab = 0;
			if ((win = searchWindow(e->xbutton.window)))
				win->buttonReleaseEvent(&e->xbutton);
			else if ((menu = searchMenu(e->xbutton.window)))
				menu->buttonReleaseEvent(&e->xbutton);
			else if ((tbar = searchToolbar(e->xbutton.window)))
				tbar->buttonReleaseEvent(&e->xbutton);
			else if ((tab = searchTab(e->xbutton.window)))
				tab->buttonReleaseEvent(&e->xbutton);
			break;
		}

		case ConfigureRequest:
	{
		FluxboxWindow *win = (FluxboxWindow *) 0;

		#ifdef SLIT
		Slit *slit = (Slit *) 0;
		#endif // SLIT
		
		if ((win = searchWindow(e->xconfigurerequest.window))) {
			win->configureRequestEvent(&e->xconfigurerequest);

		#ifdef SLIT
		} else if ((slit = searchSlit(e->xconfigurerequest.window))) {
			slit->configureRequestEvent(&e->xconfigurerequest);
		#endif // SLIT

		} else {
			grab();

			if (validateWindow(e->xconfigurerequest.window)) {
				XWindowChanges xwc;

				xwc.x = e->xconfigurerequest.x;
				xwc.y = e->xconfigurerequest.y;
				xwc.width = e->xconfigurerequest.width;
				xwc.height = e->xconfigurerequest.height;
				xwc.border_width = e->xconfigurerequest.border_width;
				xwc.sibling = e->xconfigurerequest.above;
				xwc.stack_mode = e->xconfigurerequest.detail;

				XConfigureWindow(getXDisplay(), e->xconfigurerequest.window,
							e->xconfigurerequest.value_mask, &xwc);
			}

			ungrab();
		}

		break;
	}

	case MapRequest:
	{
		#ifdef		DEBUG
		fprintf(stderr,
			I18n::instance()->
				getMessage(
				#ifdef NLS
				 blackboxSet, blackboxMapRequest,
				#else // !NLS
				 0, 0,
				#endif // NLS
				 "Fluxbox::process_event(): MapRequest for 0x%lx\n"),
							e->xmaprequest.window);
		#endif // DEBUG
		
		#ifdef SLIT
		#ifdef KDE
		//Check and see if client is KDE dock applet.
		//If so add to Slit
		bool iskdedockapp = False;
		Atom ajunk;
		int ijunk;
		unsigned long *data = (unsigned long *) 0, uljunk;

		// Check if KDE v2.x dock applet
		if (XGetWindowProperty(getXDisplay(), e->xmaprequest.window,
				getKWM2DockwindowAtom(), 0l, 1l, False,
				XA_WINDOW, &ajunk, &ijunk, &uljunk,
				&uljunk, (unsigned char **) &data) == Success) {
					
			if (data)
				iskdedockapp = True;
			XFree((char *) data);
	
		}

		// Check if KDE v1.x dock applet
		if (!iskdedockapp) {
			if (XGetWindowProperty(getXDisplay(), e->xmaprequest.window,
					getKWM1DockwindowAtom(), 0l, 1l, False,
					getKWM1DockwindowAtom(), &ajunk, &ijunk, &uljunk,
					&uljunk, (unsigned char **) &data) == Success) {
				iskdedockapp = (data && data[0] != 0);
				XFree((char *) data);
			}
		}

		if (iskdedockapp) {
			XSelectInput(getXDisplay(), e->xmaprequest.window, StructureNotifyMask);
			LinkedListIterator<BScreen> it(screenList);
			for (; it.current() == screenList->last(); it++) {
				BScreen *screen = it.current();
				screen->getSlit()->addClient(e->xmaprequest.window);
			}
			return;
		}
		#endif //KDE
		#endif // SLIT
			
		FluxboxWindow *win = searchWindow(e->xmaprequest.window);

		if (! win) {
			try {
				win = new FluxboxWindow(e->xmaprequest.window);
			} catch (FluxboxWindow::Error error) {
				FluxboxWindow::showError(error);
				delete win;
				win = 0;
			}
		}
			
		if ((win = searchWindow(e->xmaprequest.window)))
					win->mapRequestEvent(&e->xmaprequest);

		break;
	}

	case MapNotify:
	{
		#ifdef DEBUG
		cerr<<__FILE__<<"("<<__LINE__<<"): MapNotify:"<<hex<<e->xmap.window<<endl;
		#endif
		FluxboxWindow *win = searchWindow(e->xmap.window);

		if (win)
			win->mapNotifyEvent(&e->xmap);

		break;
	}

	case UnmapNotify:
	{
			#ifdef DEBUG
			cerr<<__FILE__<<"("<<__LINE__<<"): UnmapNotify:"<<hex<<e->xunmap.window<<endl;
			#endif
			FluxboxWindow *win = (FluxboxWindow *) 0;

			#ifdef SLIT
			Slit *slit = (Slit *) 0;
			#endif // SLIT

			if ((win = searchWindow(e->xunmap.window))) {
				// only process windows with StructureNotify selected 
		     	// (ignore SubstructureNotify)
			//    if (win->windowFor(e->xunmap.window))
					if (win->getClientWindow()!=e->xunmap.window)
						win->unmapNotifyEvent(&e->xunmap);
			#ifdef SLIT
			} else if ((slit = searchSlit(e->xunmap.window))) {
				slit->removeClient(e->xunmap.window);
			#endif // SLIT

			}

			break;
		}
	case CreateNotify:
		break;
	case DestroyNotify:
		{
			FluxboxWindow *win = (FluxboxWindow *) 0;

			#ifdef SLIT
			Slit *slit = 0;
			#endif // SLIT

			if ((win = searchWindow(e->xdestroywindow.window))) {
				if (win->destroyNotifyEvent(&e->xdestroywindow)) {
					delete win;
					win = 0;
				}
			#ifdef SLIT<commit_msg>fixed isTransient in UnmapNotify<commit_after>// fluxbox.cc for Fluxbox Window Manager
// Copyright (c) 2001 - 2002 <NAME> (<EMAIL>)
//
// blackbox.cc for blackbox - an X11 Window manager
// Copyright (c) 1997 - 2000 <NAME> (<EMAIL>.net)
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.	IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

// $Id: fluxbox.cc,v 1.27 2002/02/06 10:17:30 fluxgen Exp $

//Use some GNU extensions
#ifndef	 _GNU_SOURCE
#define	 _GNU_SOURCE
#endif // _GNU_SOURCE

#ifdef		HAVE_CONFIG_H
#	include "../config.h"
#endif // HAVE_CONFIG_H

#include "i18n.hh"
#include "fluxbox.hh"
#include "Basemenu.hh"
#include "Clientmenu.hh"
#include "Rootmenu.hh"
#include "Screen.hh"

#ifdef SLIT
#include "Slit.hh"
#endif // SLIT

#include "Toolbar.hh"
#include "Window.hh"
#include "Workspace.hh"
#include "Workspacemenu.hh"
#include "StringUtil.hh"
#include "Resource.hh"
#include "XrmDatabaseHelper.hh"

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xresource.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>

#ifdef		SHAPE
#include <X11/extensions/shape.h>
#endif // SHAPE


#ifdef		HAVE_STDIO_H
#	include <stdio.h>
#endif // HAVE_STDIO_H

#ifdef		STDC_HEADERS
#	include <stdlib.h>
#	include <string.h>
#endif // STDC_HEADERS

#ifdef		HAVE_UNISTD_H
#	include <sys/types.h>
#	include <unistd.h>
#endif // HAVE_UNISTD_H

#ifdef		HAVE_SYS_PARAM_H
#	include <sys/param.h>
#endif // HAVE_SYS_PARAM_H

#ifndef	 MAXPATHLEN
#define	 MAXPATHLEN 255
#endif // MAXPATHLEN

#ifdef		HAVE_SYS_SELECT_H
#	include <sys/select.h>
#endif // HAVE_SYS_SELECT_H

#ifdef		HAVE_SIGNAL_H
#	include <signal.h>
#endif // HAVE_SIGNAL_H

#ifdef		HAVE_SYS_SIGNAL_H
#	include <sys/signal.h>
#endif // HAVE_SYS_SIGNAL_H

#ifdef		HAVE_SYS_STAT_H
#	include <sys/types.h>
#	include <sys/stat.h>
#endif // HAVE_SYS_STAT_H

#ifdef		TIME_WITH_SYS_TIME
#	include <sys/time.h>
#	include <time.h>
#else // !TIME_WITH_SYS_TIME
#	ifdef		HAVE_SYS_TIME_H
#		include <sys/time.h>
#	else // !HAVE_SYS_TIME_H
#		include <time.h>
#	endif // HAVE_SYS_TIME_H
#endif // TIME_WITH_SYS_TIME

#ifdef		HAVE_LIBGEN_H
#	include <libgen.h>
#endif // HAVE_LIBGEN_H

#include <iostream>
#include <string>
#include <strstream>
#include <memory>

using namespace std;

#ifndef	 HAVE_BASENAME
static inline char *basename (char *);
static inline char *basename (char *s) {
	char *save = s;

	while (*s) if (*s++ == '/') save = s;

	return save;
}
#endif // HAVE_BASENAME

#define RC_PATH "fluxbox"
#define RC_INIT_FILE "init"


// X event scanner for enter/leave notifies - adapted from twm
typedef struct scanargs {
	Window w;
	Bool leave, inferior, enter;
} scanargs;

static Bool queueScanner(Display *, XEvent *e, char *args) {
	if ((e->type == LeaveNotify) &&
			(e->xcrossing.window == ((scanargs *) args)->w) &&
			(e->xcrossing.mode == NotifyNormal)) {
		((scanargs *) args)->leave = True;
		((scanargs *) args)->inferior = (e->xcrossing.detail == NotifyInferior);
	} else if ((e->type == EnterNotify) &&
			(e->xcrossing.mode == NotifyUngrab))
		((scanargs *) args)->enter = True;

	return False;
}
//-----------------------------------------------------------------
//---- accessors for int, bool, and some enums with Resource ------
//-----------------------------------------------------------------
template<>
void Resource<int>::
setFromString(const char* strval) {
	int val;
	if (sscanf(strval, "%d", &val)==1)
		*this = val;
}

template<>
void Resource<std::string>::
setFromString(const char *strval) {
	*this = strval;
}

template<>
void Resource<bool>::
setFromString(char const *strval) {
	if (strcasecmp(strval, "true")==0)
		*this = true;
	else
		*this = false;
}

template<>
void Resource<Fluxbox::TitlebarList>::
setFromString(char const *strval) {
	vector<std::string> val;
	StringUtil::stringtok(val, strval);
	int size=val.size();
	//clear old values
	m_value.clear();
		
	for (int i=0; i<size; i++) {
		if (strcasecmp(val[i].c_str(), "Maximize")==0)
			m_value.push_back(Fluxbox::MAXIMIZE);
		else if (strcasecmp(val[i].c_str(), "Minimize")==0)
			m_value.push_back(Fluxbox::MINIMIZE);
		else if (strcasecmp(val[i].c_str(), "Shade")==0)
			m_value.push_back(Fluxbox::SHADE);
		else if (strcasecmp(val[i].c_str(), "Stick")==0)
			m_value.push_back(Fluxbox::STICK);
		else if (strcasecmp(val[i].c_str(), "Menu")==0)
			m_value.push_back(Fluxbox::MENU);
		else if (strcasecmp(val[i].c_str(), "Close")==0)
			m_value.push_back(Fluxbox::CLOSE);
	}
}

template<>
void Resource<unsigned int>::
setFromString(const char *strval) {	
	if (sscanf(strval, "%ul", &m_value) != 1)
		setDefaultValue();
}

//-----------------------------------------------------------------
//---- manipulators for int, bool, and some enums with Resource ---
//-----------------------------------------------------------------
template<>
std::string Resource<bool>::
getString() {				
	return std::string(**this == true ? "true" : "false");
}

template<>
std::string Resource<int>::
getString() {
	char strval[256];
	sprintf(strval, "%d", **this);
	return std::string(strval);
}

template<>
std::string Resource<std::string>::
getString() { return **this; }

template<>
std::string Resource<Fluxbox::TitlebarList>::
getString() {
	string retval;
	int size=m_value.size();
	for (int i=0; i<size; i++) {
		switch (m_value[i]) {
			case Fluxbox::SHADE:
				retval.append("Shade");
				break;
			case Fluxbox::MINIMIZE:
				retval.append("Minimize");
				break;
			case Fluxbox::MAXIMIZE:
				retval.append("Maximize");
				break;
			case Fluxbox::CLOSE:
				retval.append("Close");
				break;
			case Fluxbox::STICK:
				retval.append("Stick");
				break;
			case Fluxbox::MENU:
				retval.append("Menu");
				break;
			default:
				break;
		}
		retval.append(" ");
	}

	return retval;
}

template<>
string Resource<unsigned int>::
getString() {
	char tmpstr[128];
	sprintf(tmpstr, "%ul", m_value);
	return string(tmpstr);
}

//static singleton var
Fluxbox *Fluxbox::singleton=0;

//------------ instance ---------------------
//returns singleton object of blackbox class
//since we only need to create one instance of Fluxbox
//-------------------------------------------
Fluxbox *Fluxbox::instance(int m_argc, char **m_argv, char *dpy_name, char *rc) {
	return singleton;
}


Fluxbox::Fluxbox(int m_argc, char **m_argv, char *dpy_name, char *rc)
: BaseDisplay(m_argv[0], dpy_name),
m_resourcemanager(), m_screen_rm(),
m_rc_tabs(m_resourcemanager, true, "session.tabs", "Session.Tabs"),
m_rc_iconbar(m_resourcemanager, true, "session.iconbar", "Session.Iconbar"),
m_rc_colors_per_channel(m_resourcemanager, 4, "session.colorsPerChannel", "Session.ColorsPerChannel"),
m_rc_stylefile(m_resourcemanager, "", "session.styleFile", "Session.StyleFile"),
m_rc_menufile(m_resourcemanager, DEFAULTMENU, "session.menuFile", "Session.MenuFile"),
m_rc_keyfile(m_resourcemanager, DEFAULTKEYSFILE, "session.keyFile", "Session.KeyFile"),
m_rc_titlebar_left(m_resourcemanager, TitlebarList(0), "session.titlebar.left", "Session.Titlebar.Left"),
m_rc_titlebar_right(m_resourcemanager, TitlebarList(0), "session.titlebar.right", "Session.Titlebar.Right"),
m_rc_cache_life(m_resourcemanager, 5, "session.cacheLife", "Session.CacheLife"),
m_rc_cache_max(m_resourcemanager, 200, "session.cacheMax", "Session.CacheMax"),
focused_window(0),
masked_window(0),
no_focus(False),
rc_file(rc),
argv(m_argv), argc(m_argc), 
key(0)
{

	//singleton pointer
	singleton = this;
	BaseDisplay::GrabGuard gg(*this);
	gg.grab();

	if (! XSupportsLocale())
		fprintf(stderr, "X server does not support locale\n");

	if (XSetLocaleModifiers("") == NULL)
		fprintf(stderr, "cannot set locale modifiers\n");

// Set default values to member variables

	resource.auto_raise_delay.tv_sec = resource.auto_raise_delay.tv_usec = 0;
	
	masked = None;
	
	windowSearchList = new LinkedList<WindowSearch>;
	menuSearchList = new LinkedList<MenuSearch>;

#ifdef SLIT
	slitSearchList = new LinkedList<SlitSearch>;
	#ifdef KDE
	//For KDE dock applets
	kwm1_dockwindow = XInternAtom(getXDisplay(), "KWM_DOCKWINDOW", False); //KDE v1.x
	kwm2_dockwindow = XInternAtom(getXDisplay(), "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", False); //KDE v2.x
	#endif //KDE

#endif // SLIT

	toolbarSearchList = new LinkedList<ToolbarSearch>;
	tabSearchList = new LinkedList<TabSearch>;
	groupSearchList = new LinkedList<WindowSearch>;

	menuTimestamps = new LinkedList<MenuTimestamp>;


	#ifdef HAVE_GETPID
	fluxbox_pid = XInternAtom(getXDisplay(), "_BLACKBOX_PID", False);
	#endif // HAVE_GETPID

	screenList = new LinkedList<BScreen>;
	int i;
	load_rc();
	//allocate screens
	for (i = 0; i < getNumberOfScreens(); i++) {
		char scrname[128], altscrname[128];
		sprintf(scrname, "session.screen%d", i);
		sprintf(altscrname, "session.Screen%d", i);
		BScreen *screen = new BScreen(m_screen_rm, this, scrname, altscrname, i);

		if (! screen->isScreenManaged()) {
			delete screen;
			continue;
		}

		screenList->insert(screen);
	}
	
	I18n *i18n = I18n::instance();
	if (! screenList->count()) {
		fprintf(stderr,
			i18n->
				getMessage(
#ifdef		NLS
				blackboxSet, blackboxNoManagableScreens,
#else // !NLS
				 0, 0,
#endif // NLS
				"Fluxbox::Fluxbox: no managable screens found, aborting.\n"));

		throw static_cast<int>(3);
	}

	XSynchronize(getXDisplay(), False);
	XSync(getXDisplay(), False);

	reconfigure_wait = reread_menu_wait = False;

	timer = new BTimer(this, this);
	timer->setTimeout(0);
	timer->fireOnce(True);

	//create keybindings handler and load keys file
	key = new Keys(getXDisplay(), const_cast<char *>((*m_rc_keyfile).c_str()));

	ungrab();
}


Fluxbox::~Fluxbox(void) {
	
	while (screenList->count())
		delete screenList->remove(0);

	while (menuTimestamps->count()) {
		MenuTimestamp *ts = menuTimestamps->remove(0);

		if (ts->filename)
			delete [] ts->filename;

		delete ts;
	}
	
	delete key;
	key = 0;

	delete timer;

	delete screenList;
	delete menuTimestamps;

	delete windowSearchList;
	delete menuSearchList;
	delete toolbarSearchList;
	delete tabSearchList;
	delete groupSearchList;

#ifdef		SLIT
	delete slitSearchList;
#endif // SLIT
}


void Fluxbox::process_event(XEvent *e) {

	if ((masked == e->xany.window) && masked_window &&
			(e->type == MotionNotify)) {
		last_time = e->xmotion.time;
		masked_window->motionNotifyEvent(&e->xmotion);

		return;
	}
	
	switch (e->type) {
	case ButtonPress:
		{
			last_time = e->xbutton.time;

			FluxboxWindow *win = (FluxboxWindow *) 0;
			Basemenu *menu = (Basemenu *) 0;

			#ifdef		SLIT
			Slit *slit = (Slit *) 0;
			#endif // SLIT

			Toolbar *tbar = (Toolbar *) 0;			
			Tab *tab = 0;

			if ((win = searchWindow(e->xbutton.window))) {

				win->buttonPressEvent(&e->xbutton);
				
				if (e->xbutton.button == 1)
					win->installColormap(True);
					
			} else if ((menu = searchMenu(e->xbutton.window))) {
				menu->buttonPressEvent(&e->xbutton);

			#ifdef		SLIT
			} else if ((slit = searchSlit(e->xbutton.window))) {
				slit->buttonPressEvent(&e->xbutton);
			#endif // SLIT

			} else if ((tbar = searchToolbar(e->xbutton.window))) {
				tbar->buttonPressEvent(&e->xbutton);
			} else if ((tab = searchTab(e->xbutton.window))) {
				tab->buttonPressEvent(&e->xbutton);
			} else {
				LinkedListIterator<BScreen> it(screenList);

				for (; it.current(); it++) {
					BScreen *screen = it.current();
				

						//if ( e->xbutton.button == 1 && 
						//	(win = screen->getIconBar()->findWindow(e->xbutton.window)) ) {
						//	win->deiconify();
						
				 if (e->xbutton.window == screen->getRootWindow()) {
					if (e->xbutton.button == 1) {
							if (! screen->isRootColormapInstalled())
							screen->getImageControl()->installRootColormap();

						if (screen->getWorkspacemenu()->isVisible())
							screen->getWorkspacemenu()->hide();

						if (screen->getRootmenu()->isVisible())
							screen->getRootmenu()->hide();
						
					} else if (e->xbutton.button == 2) {
						int mx = e->xbutton.x_root -
							(screen->getWorkspacemenu()->getWidth() / 2);
						int my = e->xbutton.y_root -
							(screen->getWorkspacemenu()->getTitleHeight() / 2);
	
						if (mx < 0) mx = 0;
						if (my < 0) my = 0;

						if (mx + screen->getWorkspacemenu()->getWidth() >
							 screen->getWidth())
								mx = screen->getWidth() -
						screen->getWorkspacemenu()->getWidth() -
						screen->getBorderWidth();

						if (my + screen->getWorkspacemenu()->getHeight() >
									screen->getHeight())
							my = screen->getHeight() -
						screen->getWorkspacemenu()->getHeight() -
						screen->getBorderWidth();

						screen->getWorkspacemenu()->move(mx, my);

						if (! screen->getWorkspacemenu()->isVisible()) {
							screen->getWorkspacemenu()->removeParent();
							screen->getWorkspacemenu()->show();
						}
					} else if (e->xbutton.button == 3) {
						int mx = e->xbutton.x_root -
						 (screen->getRootmenu()->getWidth() / 2);
						int my = e->xbutton.y_root -
						 (screen->getRootmenu()->getTitleHeight() / 2);

						if (mx < 0) mx = 0;
						if (my < 0) my = 0;

						if (mx + screen->getRootmenu()->getWidth() > screen->getWidth())
							mx = screen->getWidth() -
						screen->getRootmenu()->getWidth() -
						screen->getBorderWidth();

						if (my + screen->getRootmenu()->getHeight() >
							screen->getHeight())
							my = screen->getHeight() -
								screen->getRootmenu()->getHeight() -
								screen->getBorderWidth();

						screen->getRootmenu()->move(mx, my);

						if (! screen->getRootmenu()->isVisible()) {
							checkMenu();
							screen->getRootmenu()->show();
						}
						
					} 
			
				}
			}
		}

			break;
	}

	case ButtonRelease:
	{
			last_time = e->xbutton.time;
			FluxboxWindow *win = (FluxboxWindow *) 0;
			Basemenu *menu = (Basemenu *) 0;
			Toolbar *tbar = (Toolbar *) 0;
			Tab *tab = 0;
			if ((win = searchWindow(e->xbutton.window)))
				win->buttonReleaseEvent(&e->xbutton);
			else if ((menu = searchMenu(e->xbutton.window)))
				menu->buttonReleaseEvent(&e->xbutton);
			else if ((tbar = searchToolbar(e->xbutton.window)))
				tbar->buttonReleaseEvent(&e->xbutton);
			else if ((tab = searchTab(e->xbutton.window)))
				tab->buttonReleaseEvent(&e->xbutton);
			break;
		}

		case ConfigureRequest:
	{
		FluxboxWindow *win = (FluxboxWindow *) 0;

		#ifdef SLIT
		Slit *slit = (Slit *) 0;
		#endif // SLIT
		
		if ((win = searchWindow(e->xconfigurerequest.window))) {
			win->configureRequestEvent(&e->xconfigurerequest);

		#ifdef SLIT
		} else if ((slit = searchSlit(e->xconfigurerequest.window))) {
			slit->configureRequestEvent(&e->xconfigurerequest);
		#endif // SLIT

		} else {
			grab();

			if (validateWindow(e->xconfigurerequest.window)) {
				XWindowChanges xwc;

				xwc.x = e->xconfigurerequest.x;
				xwc.y = e->xconfigurerequest.y;
				xwc.width = e->xconfigurerequest.width;
				xwc.height = e->xconfigurerequest.height;
				xwc.border_width = e->xconfigurerequest.border_width;
				xwc.sibling = e->xconfigurerequest.above;
				xwc.stack_mode = e->xconfigurerequest.detail;

				XConfigureWindow(getXDisplay(), e->xconfigurerequest.window,
							e->xconfigurerequest.value_mask, &xwc);
			}

			ungrab();
		}

		break;
	}

	case MapRequest:
	{
		#ifdef		DEBUG
		fprintf(stderr,
			I18n::instance()->
				getMessage(
				#ifdef NLS
				 blackboxSet, blackboxMapRequest,
				#else // !NLS
				 0, 0,
				#endif // NLS
				 "Fluxbox::process_event(): MapRequest for 0x%lx\n"),
							e->xmaprequest.window);
		#endif // DEBUG
		
		#ifdef SLIT
		#ifdef KDE
		//Check and see if client is KDE dock applet.
		//If so add to Slit
		bool iskdedockapp = False;
		Atom ajunk;
		int ijunk;
		unsigned long *data = (unsigned long *) 0, uljunk;

		// Check if KDE v2.x dock applet
		if (XGetWindowProperty(getXDisplay(), e->xmaprequest.window,
				getKWM2DockwindowAtom(), 0l, 1l, False,
				XA_WINDOW, &ajunk, &ijunk, &uljunk,
				&uljunk, (unsigned char **) &data) == Success) {
					
			if (data)
				iskdedockapp = True;
			XFree((char *) data);
	
		}

		// Check if KDE v1.x dock applet
		if (!iskdedockapp) {
			if (XGetWindowProperty(getXDisplay(), e->xmaprequest.window,
					getKWM1DockwindowAtom(), 0l, 1l, False,
					getKWM1DockwindowAtom(), &ajunk, &ijunk, &uljunk,
					&uljunk, (unsigned char **) &data) == Success) {
				iskdedockapp = (data && data[0] != 0);
				XFree((char *) data);
			}
		}

		if (iskdedockapp) {
			XSelectInput(getXDisplay(), e->xmaprequest.window, StructureNotifyMask);
			LinkedListIterator<BScreen> it(screenList);
			for (; it.current() == screenList->last(); it++) {
				BScreen *screen = it.current();
				screen->getSlit()->addClient(e->xmaprequest.window);
			}
			return;
		}
		#endif //KDE
		#endif // SLIT
			
		FluxboxWindow *win = searchWindow(e->xmaprequest.window);

		if (! win) {
			try {
				win = new FluxboxWindow(e->xmaprequest.window);
			} catch (FluxboxWindow::Error error) {
				FluxboxWindow::showError(error);
				delete win;
				win = 0;
			}
		}
			
		if ((win = searchWindow(e->xmaprequest.window)))
					win->mapRequestEvent(&e->xmaprequest);

		break;
	}

	case MapNotify:
	{
		#ifdef DEBUG
		cerr<<__FILE__<<"("<<__LINE__<<"): MapNotify:"<<hex<<e->xmap.window<<endl;
		#endif
		FluxboxWindow *win = searchWindow(e->xmap.window);

		if (win)
			win->mapNotifyEvent(&e->xmap);

		break;
	}

	case UnmapNotify:
	{
			#ifdef DEBUG
			cerr<<__FILE__<<"("<<__LINE__<<"): UnmapNotify:"<<hex<<e->xunmap.window<<endl;
			#endif
			FluxboxWindow *win = (FluxboxWindow *) 0;

			#ifdef SLIT
			Slit *slit = (Slit *) 0;
			#endif // SLIT

			if ((win = searchWindow(e->xunmap.window))) {
				// only process windows with StructureNotify selected 
		     	// (ignore SubstructureNotify)
			//    if (win->windowFor(e->xunmap.window))
					if (win->getClientWindow()!=e->xunmap.window ||
						win->isTransient())
						win->unmapNotifyEvent(&e->xunmap);
			#ifdef SLIT
			} else if ((slit = searchSlit(e->xunmap.window))) {
				slit->removeClient(e->xunmap.window);
			#endif // SLIT

			}

			break;
		}
	case CreateNotify:
		break;
	case DestroyNotify:
		{
			FluxboxWindow *win = (FluxboxWindow *) 0;

			#ifdef SLIT
			Slit *slit = 0;
			#endif // SLIT

			if ((win = searchWindow(e->xdestroywindow.window))) {
				if (win->destroyNotifyEvent(&e->xdestroywindow)) {
					delete win;
					win = 0;
				}
			#ifdef SLIT
{'fraction_non_alphanumeric': 0.17314653264237911, 'fraction_numerical': 0.008281034348063852, 'mean_word_length': 3.454988399071926, 'pattern_counts': {'":': 0, '<': 199, '<?xml version=': 0, '>': 502, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2043309', 'n_tokens_mistral': 15799, 'n_tokens_neox': 13471, 'n_words': 2965}