/****************************************************************************
 * Copyright (c) 2000,2001,2003 Sasha Vasko <sasha at aftercode.net>
 * Copyright (c) 1999 Ethan Fisher <allanon@crystaltokyo.com>
 *
 * 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.
 *
 * This has been completely rewritten and as the result relicensed under GPL.
 * For historic purposes we keep original creators here :
 *
 * This module is based on Twm, but has been SIGNIFICANTLY modified
 * by Rob Nation
 * by Bo Yang
 * by Frank Fejes
 ****************************************************************************/
/***********************************************************************
 * afterstep function execution code
 ***********************************************************************/

#define LOCAL_DEBUG

#include "../../configure.h"

#include "asinternals.h"

#include <limits.h>
#include <signal.h>

#include "../../libAfterStep/session.h"
#include "../../libAfterStep/moveresize.h"
#include "../../libAfterStep/mylook.h"

static as_function_handler function_handlers[F_FUNCTIONS_NUM] ;

void ExecuteComplexFunction ( ASEvent *event, char *name );

/* list of available handlers : */
void beep_func_handler( FunctionData *data, ASEvent *event, int module );
void moveresize_func_handler( FunctionData *data, ASEvent *event, int module );
void scroll_func_handler( FunctionData *data, ASEvent *event, int module );
void movecursor_func_handler( FunctionData *data, ASEvent *event, int module );
void raiselower_func_handler( FunctionData *data, ASEvent *event, int module );
void setlayer_func_handler( FunctionData *data, ASEvent *event, int module );
void change_desk_func_handler( FunctionData *data, ASEvent *event, int module );
void toggle_status_func_handler( FunctionData *data, ASEvent *event, int module );
void iconify_func_handler( FunctionData *data, ASEvent *event, int module );
void focus_func_handler( FunctionData *data, ASEvent *event, int module );
void paste_selection_func_handler( FunctionData *data, ASEvent *event, int module );
void warp_func_handler( FunctionData *data, ASEvent *event, int module );
void goto_bookmark_func_handler( FunctionData *data, ASEvent *event, int module );
void bookmark_window_func_handler( FunctionData *data, ASEvent *event, int module );
void pin_menu_func_handler( FunctionData *data, ASEvent *event, int module );
void close_func_handler( FunctionData *data, ASEvent *event, int module );
void restart_func_handler( FunctionData *data, ASEvent *event, int module );
void exec_func_handler( FunctionData *data, ASEvent *event, int module );
void exec_in_term_func_handler( FunctionData *data, ASEvent *event, int module );
void change_background_func_handler( FunctionData *data, ASEvent *event, int module );
void change_config_func_handler( FunctionData *data, ASEvent *event, int module );
void change_theme_func_handler( FunctionData *data, ASEvent *event, int module );
void install_file_func_handler( FunctionData *data, ASEvent *event, int module );
void refresh_func_handler( FunctionData *data, ASEvent *event, int module );
void goto_page_func_handler( FunctionData *data, ASEvent *event, int module );
void toggle_page_func_handler( FunctionData *data, ASEvent *event, int module );
void gethelp_func_handler( FunctionData *data, ASEvent *event, int module );
void wait_func_handler( FunctionData *data, ASEvent *event, int module );
void raise_it_func_handler( FunctionData *data, ASEvent *event, int module );
void desk_func_handler( FunctionData *data, ASEvent *event, int module );
void deskviewport_func_handler( FunctionData *data, ASEvent *event, int module );
void module_func_handler( FunctionData *data, ASEvent *event, int module );
void killmodule_func_handler( FunctionData *data, ASEvent *event, int module );
void popup_func_handler( FunctionData *data, ASEvent *event, int module );
void quit_func_handler( FunctionData *data, ASEvent *event, int module );
void windowlist_func_handler( FunctionData *data, ASEvent *event, int module );
void quickrestart_func_handler( FunctionData *data, ASEvent *event, int module );
void send_window_list_func_handler( FunctionData *data, ASEvent *event, int module );
void save_workspace_func_handler( FunctionData *data, ASEvent *event, int module );
void test_func_handler( FunctionData *data, ASEvent *event, int module );
void screenshot_func_handler( FunctionData *data, ASEvent *event, int module );
void swallow_window_func_handler( FunctionData *data, ASEvent *event, int module );


/* handlers initialization function : */
void SetupFunctionHandlers()
{
    memset( &(function_handlers[0]), 0x00, sizeof( function_handlers ) );
    function_handlers[F_BEEP] = beep_func_handler ;

    function_handlers[F_RESIZE] =
	function_handlers[F_MOVE] = moveresize_func_handler ;

#ifndef NO_VIRTUAL
    function_handlers[F_SCROLL] = scroll_func_handler ;
    function_handlers[F_MOVECURSOR] = movecursor_func_handler ;
#endif

    function_handlers[F_RAISE] =
	function_handlers[F_LOWER] =
	function_handlers[F_RAISELOWER]     = raiselower_func_handler;

    function_handlers[F_PUTONTOP] =
	function_handlers[F_PUTONBACK] =
	function_handlers[F_TOGGLELAYER]=
	function_handlers[F_SETLAYER]       = setlayer_func_handler   ;

    function_handlers[F_CHANGE_WINDOWS_DESK]= change_desk_func_handler;

    function_handlers[F_MAXIMIZE]           =
	function_handlers[F_SHADE]          =
	function_handlers[F_STICK]          = toggle_status_func_handler;

    function_handlers[F_ICONIFY]            = iconify_func_handler;

    function_handlers[F_FOCUS]              = focus_func_handler;
    function_handlers[F_CHANGEWINDOW_UP] =
	function_handlers[F_WARP_F] =
	function_handlers[F_CHANGEWINDOW_DOWN]  =
	function_handlers[F_WARP_B]         = warp_func_handler ;

    function_handlers[F_PASTE_SELECTION]    = paste_selection_func_handler ;
    function_handlers[F_GOTO_BOOKMARK]      = goto_bookmark_func_handler ;
    function_handlers[F_BOOKMARK_WINDOW]    = bookmark_window_func_handler ;
    function_handlers[F_PIN_MENU]           = pin_menu_func_handler ;
    function_handlers[F_DESTROY] =
	function_handlers[F_DELETE] =
	function_handlers[F_CLOSE]          = close_func_handler ;
    function_handlers[F_RESTART]            = restart_func_handler ;

    function_handlers[F_EXEC] =
		function_handlers[F_Swallow] =
		function_handlers[F_MaxSwallow] 	= exec_func_handler ;

	function_handlers[F_ExecInTerm]       	= exec_in_term_func_handler ;

    function_handlers[F_CHANGE_BACKGROUND]  = change_background_func_handler;

    function_handlers[F_CHANGE_LOOK] =
	function_handlers[F_CHANGE_FEEL]    =
		function_handlers[F_CHANGE_COLORSCHEME] =
	function_handlers[F_CHANGE_THEME_FILE] = change_config_func_handler ;

	function_handlers[F_CHANGE_THEME]   = change_theme_func_handler ;

	function_handlers[F_INSTALL_LOOK] =
		function_handlers[F_INSTALL_FEEL] =
		function_handlers[F_INSTALL_BACKGROUND] =
		function_handlers[F_INSTALL_FONT] =
		function_handlers[F_INSTALL_ICON] =
		function_handlers[F_INSTALL_TILE] =
		function_handlers[F_INSTALL_THEME_FILE] =
		function_handlers[F_INSTALL_COLORSCHEME] = install_file_func_handler ;



	function_handlers[F_SAVE_WORKSPACE]     = save_workspace_func_handler;

    function_handlers[F_REFRESH]            = refresh_func_handler ;
#ifndef NO_VIRTUAL
    function_handlers[F_GOTO_PAGE]          = goto_page_func_handler ;
    function_handlers[F_TOGGLE_PAGE]        = toggle_page_func_handler ;
#endif
    function_handlers[F_GETHELP]            = gethelp_func_handler ;
    function_handlers[F_WAIT]               = wait_func_handler ;
    function_handlers[F_RAISE_IT]           = raise_it_func_handler ;
    function_handlers[F_DESK]               = desk_func_handler ;
    function_handlers[F_GOTO_DESKVIEWPORT]  = deskviewport_func_handler ;

    function_handlers[F_MODULE] =
	function_handlers[F_SwallowModule] =
	function_handlers[F_MaxSwallowModule]= module_func_handler ;

    function_handlers[F_KILLMODULEBYNAME]   = killmodule_func_handler ;
    function_handlers[F_POPUP]              = popup_func_handler ;
    function_handlers[F_QUIT]               = quit_func_handler ;
#ifndef NO_WINDOWLIST
    function_handlers[F_WINDOWLIST]         = windowlist_func_handler ;
#endif /* ! NO_WINDOWLIST */
    function_handlers[F_QUICKRESTART]       = quickrestart_func_handler ;
    function_handlers[F_SEND_WINDOW_LIST]   = send_window_list_func_handler ;
    function_handlers[F_Test]               = test_func_handler ;

	function_handlers[F_TAKE_WINDOWSHOT]    =
	function_handlers[F_TAKE_FRAMESHOT]    =
		function_handlers[F_TAKE_SCREENSHOT]    = screenshot_func_handler ;
	function_handlers[F_SWALLOW_WINDOW]	= swallow_window_func_handler ;
}

/* complex functions are stored in hash table ComplexFunctions */
ComplexFunction *
get_complex_function( char *name )
{
    return find_complex_func( Scr.Feel.ComplexFunctions, name );
}

/* WE need to implement functions queue, so that ExecuteFunction
 * only places function to run into that queue, and queue gets processed
 * at a later time from the main event loop.
 * This is to prevent nasty recursions when functions are called from
 * functions, etc.
 */
typedef struct ASScheduledFunction
{
    FunctionData fdata ;
    int module ;
    ASEvent event ;
    /* we do not want to keep pointers to data structures here,
     * since they may change by the time function is run : */
    Window client ;
    Window canvas ;

	Bool defered ;
}ASScheduledFunction;

ASBiDirList *FunctionQueue = NULL ;

void destroy_scheduled_function_handler( void *data )
{
    ASScheduledFunction *sf = data ;
    if( sf )
    {
	free_func_data (&(sf->fdata));
	free( sf );
    }
}
#define destroy_scheduled_function(sf)  destroy_scheduled_function_handler((void*)sf)


/***********************************************************************
 *  Procedure:
 *  ExecuteFunction - schedule execution a afterstep built in function
 *  Inputs:
 *      data    - the function to execute
 *      event   - the event that caused the function
 *      module  - number of the module we received function request from
 ***********************************************************************/
void
ExecuteFunctionExt (FunctionData *data, ASEvent *event, int module, Bool defered)
{
	ASScheduledFunction *sf = NULL;

    if( data == NULL )
	return ;
    if( FunctionQueue == NULL )
	FunctionQueue = create_asbidirlist(destroy_scheduled_function_handler);

    sf = safecalloc( 1, sizeof(ASScheduledFunction ) );
    dup_func_data (&(sf->fdata), data);
    sf->module = module ;

    sf->event.event_time = Scr.last_Timestamp ;
    sf->event.scr = ASDefaultScr ;
	sf->defered = defered ;
    if( event )
    {
	sf->event.mask              = event->mask           ;
	sf->event.eclass            = event->eclass         ;
	sf->event.last_time         = event->last_time      ;
	sf->event.typed_last_time   = event->typed_last_time;
	sf->event.event_time        = event->event_time;

	sf->event.w                 = event->w;
	sf->event.context           = event->context;
	sf->event.x                 = event->x ;
		/* may become freed when it comes to running the function - can't keep a pointer ! */
	if( event->client && event->client->magic == MAGIC_ASWINDOW )
	    sf->client              = event->client->w ;
	if( event->widget )
	    sf->canvas              = event->widget->w ;
    }
    append_bidirelem( FunctionQueue, sf );
}

void
ExecuteFunctionForClient(FunctionData *data, Window client)
{
	ASEvent dummy ;
	LOCAL_DEBUG_CALLER_OUT( "client_window(%lX)", client );
    if( data == NULL )
	return ;
	memset( &dummy, 0x00, sizeof( dummy ) );
	dummy.client = window2ASWindow( client );
	dummy.x.type = ButtonRelease ;
	dummy.x.xany.window = client ;
	ExecuteFunctionExt( data, &dummy, -1, (client!=None) /* deffered */ );
}


#undef ExecuteFunction
void
ExecuteFunction (FunctionData *data, ASEvent *event, int module)
#ifdef TRACE_ExecuteFunction
#define ExecuteFunction(d,e,m) trace_ExecuteFunction(d,e,m,__FILE__,__LINE__)
#endif
{
	LOCAL_DEBUG_CALLER_OUT( "event(%d(%s))->window(%lX)->client(%p(%s))->module(%d)",
			 event?event->x.type:-1,
			 event?event_type2name(event->x.type):"n/a",
			 event?(unsigned long)event->w:0,
			 event?event->client:NULL,
			 event?(event->client?ASWIN_NAME(event->client):"none"):"none",
			 module );
    if( data == NULL )
	return ;
	ExecuteFunctionExt( data, event, module, False );
}
/***********************************************************************
 *  Procedure:
 *  DoExecuteFunction - execute an afterstep built in function
 ***********************************************************************/
static Bool
is_interactive_action(FunctionData *data)
{
    if( data->func == F_MOVE || data->func == F_RESIZE )
	return (data->func_val[0] == INVALID_POSITION && data->func_val[1] == INVALID_POSITION);
    return False;
}

static void
DoExecuteFunction ( ASScheduledFunction *sf )
{
    FunctionData *data = &(sf->fdata) ;
    ASEvent *event = &(sf->event);
    register FunctionCode  func = data->func;

#if !defined(LOCAL_DEBUG) || defined(NO_DEBUG_OUTPUT)
    if( get_output_threshold() >= OUTPUT_LEVEL_DEBUG )
#endif
	{
	    print_func_data(__FILE__, "DoExecuteFunction", __LINE__, data);
	}

    if( sf->client != None )
    {
	ASWindow *asw = window2ASWindow( sf->client );
		if( asw == NULL )
		{	                   /* window had died by now - let go on with our lives */
			destroy_scheduled_function(sf);
			return ;
		}

		event->client = asw ;

	if( sf->canvas )
	{
	    ASCanvas  *canvas = NULL ;
	    if( sf->canvas == asw->client_canvas->w )
		canvas = asw->client_canvas ;
	    else if( sf->canvas == asw->frame_canvas->w )
		canvas = asw->frame_canvas ;
	    else if( asw->icon_canvas && sf->canvas == asw->icon_canvas->w )
		canvas = asw->icon_canvas ;
	    else if( asw->icon_title_canvas && sf->canvas == asw->icon_title_canvas->w )
		canvas = asw->icon_title_canvas ;
	    else
	    {
		int i = FRAME_SIDES ;
		while( --i >= 0 )
		    if( asw->frame_sides[i] != NULL &&
			asw->frame_sides[i]->w == sf->canvas )
		    {
			canvas = asw->frame_sides[i];
			break;
		    }
	    }
	    event->widget = canvas ;
	}
    }

    /* Defer Execution may wish to alter this value */
	LOCAL_DEBUG_OUT( "client = %p, IsWindowFunc() = %s", event->client, IsWindowFunc (func)?"Yes":"No" );
    if (IsWindowFunc (func))
	{
		int           do_defer = !(sf->defered), fin_event;

		if( event->x.type == ButtonPress )
	    	fin_event = ButtonRelease ;
		else if ( event->x.type == MotionNotify )
	    	fin_event = (event->x.xmotion.state&AllButtonMask) != 0 ? ButtonRelease : ButtonPress ;
		else
	    	fin_event = ButtonPress ;

		if (data->text != NULL && event->client == NULL && 
			func != F_SWALLOW_WINDOW )/* SWallowWindow has module name as its text ! */
			if (*(data->text) != '\0')
				if ((event->client = pattern2ASWindow (data->text)) != NULL)
				{
					event->w = get_window_frame(event->client);
					do_defer = False ;
				}

		if( event->x.type == KeyPress || event->x.type == KeyRelease )
		{/* keyboard events should never be deferred,
		  * and if no client is selected for window specific function - then it should be ignored */
			if( event->client == NULL )
				func = F_NOP ;
			do_defer = False;
		}

		if( do_defer && (fin_event != ButtonRelease || !is_interactive_action(data)) )
		{
	    	int cursor = ASCUR_Move ;
	    	if (func != F_RESIZE && func != F_MOVE)
				cursor = (func!=F_DESTROY && func!=F_DELETE && func!=F_CLOSE)?ASCUR_Select:ASCUR_Destroy;

	    	if (DeferExecution (event, cursor, fin_event))
				func = F_NOP;
		}

		if( event->client == NULL )
			func = F_NOP;

	}

    if( function_handlers[func] || func == F_FUNCTION )
    {
		data->func = func ;
		if( event->client )
	    	if( !check_allowed_function2( data->func, event->client->hints) )
	    	{
				LOCAL_DEBUG_OUT( "function \"%s\" is not allowed for the specifyed window (mask 0x%lX)", COMPLEX_FUNCTION_NAME(data), ASWIN_FUNC_MASK(event->client));
				func = data->func = F_BEEP ;
	    	}

		if( get_flags( AfterStepState, ASS_WarpingMode ) &&
	    	function_handlers[func] != warp_func_handler )
	    	EndWarping();

		if( func == F_FUNCTION )
		    ExecuteComplexFunction (event, COMPLEX_FUNCTION_NAME(data));
		else
		    function_handlers[func]( data, event, sf->module );
    }
    destroy_scheduled_function(sf);
}

void ExecutePendingFunctions()
{
    ASScheduledFunction *sf ;
    if( FunctionQueue )
	while( (sf = extract_first_bidirelem( FunctionQueue ) ) != NULL )
	    DoExecuteFunction( sf );
}

/***********************************************************************
 *  Procedure:
 *	DeferExecution - defer the execution of a function to the
 *	    next button press if the context is ASC_Root
 *
 *  Inputs:
 *      eventp  - pointer to ASEvent to patch up
 *      cursor  - the cursor to display while waiting
 *      finish_event - ButtonRelease or ButtonPress; tells what kind of event to
 *                     terminate on.
 * Returns:
 *      True    - if we should defer execution of the function
 *      False   - if we can continue as planned
 ***********************************************************************/
int
DeferExecution ( ASEvent *event, int cursor, int finish_event)
{
    Bool res = False ;
LOCAL_DEBUG_CALLER_OUT( "cursor %d, event %d, window 0x%lX, window_name \"%s\", finish event %d",
			cursor, event?event->x.type:-1, event?(unsigned long)event->w:0, event->client?ASWIN_NAME(event->client):"none", finish_event );

/*    if (event->context != C_ROOT && event->context != C_NO_CONTEXT)
	if ( finish_event == ButtonPress ||
	    (finish_event == ButtonRelease && event->x.type != ButtonPress))
	    return False;
*/
    if (!(res = !GrabEm (ASDefaultScr, Scr.Feel.cursors[cursor])))
	{
		ASHintWindow *hint = create_ashint_window( ASDefaultScr,  &(Scr.Look), "Please select target window");
   		WaitEventLoop( event, finish_event, -1, hint );
		destroy_ashint_window( &hint );

		LOCAL_DEBUG_OUT( "window(%lX)->root(%lX)->subwindow(%lX)", event->x.xbutton.window, event->x.xbutton.root, event->x.xbutton.subwindow );
		if (event->client == NULL)
		{
	    	res = True ;
	    	/* since we grabbed cursor we may get clicks over client windows as reported
	     	* relative to the root window, in which case we have to check subwindow to
	     	* see what client was clicked */
	    	if( event->x.xbutton.subwindow != event->w )
	    	{
				event->client = window2ASWindow( event->x.xbutton.subwindow );
				if( event->client != NULL )
				{
		    		res = False ;
		    		event->w = event->x.xbutton.subwindow ;
				}
	    	}
		}
		UngrabEm ();
    }
    if( res )
	XBell (dpy, event->scr->screen);

LOCAL_DEBUG_OUT( "result %d, event %d, window 0x%lX, window_name \"%s\"",
		  res, event?event->x.type:-1, event?(unsigned long)event->w:0, event->client?ASWIN_NAME(event->client):"none" );

    return res;
}

/*****************************************************************************
 *  Executes complex function defined with Function/EndFunction construct :
 ****************************************************************************/
void
ExecuteComplexFunction ( ASEvent *event, char *name )
{
    ComplexFunction *func = NULL;
    int              clicks = 0 ;
    register int     i ;
    Bool             persist = False;
    Bool             need_window = False;
    char             c ;
    static char clicks_upper[MAX_CLICKS_HANDLED+1] = {CLICKS_TRIGGERS_UPPER};
    static char clicks_lower[MAX_CLICKS_HANDLED+1] = {CLICKS_TRIGGERS_LOWER};
    LOCAL_DEBUG_CALLER_OUT( "event %d, window 0x%lX, asw(%p), window_name \"%s\", function name \"%s\"",
			event?event->x.type:-1, event?(unsigned long)event->w:0, event->client, event->client?ASWIN_NAME(event->client):"none", name);


	if( name && (name[0] == IMMEDIATE || name[0] == IMMEDIATE_UPPER) )
		if( name[1] == ':' )
			if( (func = get_complex_function( &(name[2]) ) ) == NULL )
			return ;
	if( func == NULL )
	    if( (func = get_complex_function( name ) ) == NULL )
			return ;

    LOCAL_DEBUG_OUT( "function data found  %p ...\n   proceeding to execution of immidiate items...", func );
    if( event->w == None && event->client != NULL )
	event->w = get_window_frame(event->client);
    /* first running all the Imediate actions : */
    for( i = 0 ; i < func->items_num ; i++ )
    {
	c = func->items[i].name ? *(func->items[i].name): 'i' ;
	if( c == IMMEDIATE || c == IMMEDIATE_UPPER )
	{
			Bool skip = False ;
			if( func->items[i].name &&
				IsExecFunc(func->items[i].func) &&
				func->items[i].name[1] == ':' )
			{
				int k = i ;
				while( ++k < func->items_num )
					if( func->items[k].func == F_WAIT && func->items[k].name )
						if( strcmp( func->items[k].name, func->items[i].name ) == 0 )
						{
							skip = True ;
							break;
						}
				if( skip )
				{
					/* since function names in complex functions have i or I as the first
					 * character to signify that they are immidiate items - we allow semicolon
					 * to separate that special character from the actuall window's name for
					 * readability sake
					 */
					char *pattern_start = &(func->items[i].name[2]) ;
					if( complex_pattern2ASWindow( pattern_start ) == NULL )
						skip = False ; /* window is not up yet - can't skip */
				}
			}
			if( !skip )
				ExecuteFunctionExt (&(func->items[i]), event, -1, True);
	}else
	{
	    persist = True ;
	    if( IsWindowFunc( func->items[i].func ) )
		need_window = True ;
	}
    }
    /* now lets count clicks : */
    LOCAL_DEBUG_OUT( "done with immidiate items - persisting ?: %s", persist?"True":"False" );
    if( !persist )
	return ;

    if (need_window && event->client == NULL )
    {
	if (DeferExecution (event, ASCUR_Select, ButtonPress))
		{
/*            WaitForButtonsUpLoop (); */
			return;
		}
    }
    if (!GrabEm (ASDefaultScr, Scr.Feel.cursors[ASCUR_Select]))
	{
	show_warning( "failed to grab pointer while executing complex function \"%s\"", name );
	XBell (dpy, Scr.screen);
		return;
	}

    clicks = 0 ;
    while( IsClickLoop( event, ButtonReleaseMask, Scr.Feel.ClickTime ) )
    {
	clicks++ ;
	if( !IsClickLoop( event, ButtonPressMask, Scr.Feel.ClickTime ) )
	    break;
	clicks++ ;
    }
    if( clicks <= MAX_CLICKS_HANDLED )
    {
	/* some functions operate on button release instead of
	 * presses. These gets really weird for complex functions ... */
/*        if (event->x.type == ButtonPress)
	    event->x.type = ButtonRelease;
 */
	/* first running all the Imediate actions : */
	for( i = 0 ; i < func->items_num ; i++ )
	    if( func->items[i].name )
	    {
		c = func->items[i].name[0];
		if( c == clicks_upper[clicks] || c == clicks_lower[clicks] )
					ExecuteFunctionExt (&(func->items[i]), event, -1, True);
	    }
    }
/*    WaitForButtonsUpLoop (); */
	UngrabEm ();
    LOCAL_DEBUG_OUT( "at the end : event %d, window 0x%lX, asw(%p), window_name \"%s\", function name \"%s\"",
			event?event->x.type:-1, event?(unsigned long)event->w:0, event->client, event->client?ASWIN_NAME(event->client):"none", name);
}

void
ExecuteBatch ( ComplexFunction *batch )
{
    ASEvent event = { 0 };
    register int     i ;

	LOCAL_DEBUG_CALLER_OUT( "event %p", batch);

	if( batch )
	{
	for( i = 0 ; i < batch->items_num ; i++ )
	{
			int func = batch->items[i].func ;
		if ( IsWindowFunc (func) || function_handlers[func] == NULL )
				continue;

		function_handlers[func]( &(batch->items[i]), &event, -1 );
	}
	}
}


/***************************************************************************************
 *
 * All The Handlers Go Below, Go Below,
 *                              Go Below.
 * All The Handlers Go Below, Go Below, Go Below
 * Ta-da-da-da-da-da-da
 *
 ****************************************************************************************/

void beep_func_handler( FunctionData *data, ASEvent *event, int module )
{
    XBell (dpy, event->scr->screen);
}

void moveresize_func_handler( FunctionData *data, ASEvent *event, int module )
{   /* gotta have a window */
    ASWindow *asw = event->client ;
    if (asw == NULL)
		return;

    if ( !is_interactive_action(data) )
    {
	int new_val1 = 0, new_val2 = 0;
	int x = asw->status->x ;
	int y = asw->status->y ;
	int width = asw->status->width ;
	int height = asw->status->height ;

	new_val1 = APPLY_VALUE_UNIT(Scr.MyDisplayWidth,data->func_val[0],data->unit_val[0]);
	LOCAL_DEBUG_OUT( "val1 = %ld, unit1 = %ld, new_val1 = %d", data->func_val[0],data->unit_val[0], new_val1 );
	new_val2 = APPLY_VALUE_UNIT(Scr.MyDisplayHeight,data->func_val[1],data->unit_val[1]);
	if( data->func == F_MOVE )
	{
	    if( data->func_val[0] != INVALID_POSITION )
		x = new_val1;
	    if( data->func_val[1] != INVALID_POSITION )
		y = new_val2;
	}else
	{
	    if( data->func_val[0] != INVALID_POSITION )
		width = new_val1;
	    if( data->func_val[1] != INVALID_POSITION )
		height = new_val2;
	}
	moveresize_aswindow_wm( asw, x, y, width, height, False );
    }else
    {
	ASMoveResizeData *mvrdata;
	/*release_pressure(); */
	if( data->func == F_MOVE )
	    mvrdata = move_widget_interactively(Scr.RootCanvas,
						asw->frame_canvas,
						event,
						apply_aswindow_move,
						complete_aswindow_move );
	else
	{
	    int side = 0 ;
	    register unsigned long context = (event->context&C_FRAME);

	    if( ASWIN_GET_FLAGS( asw, AS_Shaded ) )
	    {
		XBell (dpy, Scr.screen);
		return;
	    }

	    while( (0x01&context) == 0 && side <= FR_SE)
	    {
		++side ;
		context = context>>1 ;
	    }

			if( side > FR_SE )
			{
				int pointer_x = 0, pointer_y = 0 ;
				ASQueryPointerRootXY( &pointer_x, &pointer_y );
				if( pointer_x > asw->frame_canvas->root_x + asw->frame_canvas->width/2 )
				{
					if( pointer_y > asw->frame_canvas->root_y + asw->frame_canvas->height/2 )
						side = FR_SE ;
					else
						side = FR_NE ;
				}else if( pointer_y > asw->frame_canvas->root_y + asw->frame_canvas->height/2 )
					side = FR_SW ;
				else
					side = FR_NW ;
			}

	    mvrdata = resize_widget_interactively(  Scr.RootCanvas,
						    asw->frame_canvas,
						    event,
						    apply_aswindow_moveresize,
						    complete_aswindow_moveresize,
						    side );
	}
	if( mvrdata )
	{
			mvrdata->move_only = (data->func == F_MOVE) ;
	    raise_scren_panframes( ASDefaultScr );
	    mvrdata->below_sibling = get_lowest_panframe(ASDefaultScr);
	    set_moveresize_restrains( mvrdata, asw->hints, asw->status);
	    mvrdata->grid = make_desktop_grid(Scr.CurrentDesk, AS_LayerDesktop, False, Scr.Vx, Scr.Vy, asw);
	    Scr.moveresize_in_progress = mvrdata ;
	    ASWIN_SET_FLAGS( asw, AS_MoveresizeInProgress );
	}
    }
}


static inline int
make_scroll_pos( int val, int unit, int curr, int max, int size )
{
    int pos = curr ;
    if ( val > -100000 && val < 100000 )
    {
	pos += APPLY_VALUE_UNIT(size,val,unit);
	if( pos < 0 )
	    pos = 0 ;
	if( pos > max )
	    pos = max ;
    }else
    {
	pos += APPLY_VALUE_UNIT(size,val/1000, unit);
	while( pos < 0 )
	    pos += max ;
	while( pos > max )
	    pos -= max ;
    }
    return pos;
}

static inline int
make_edge_scroll( int curr_pos, int curr_view, int view_size, int max_view, int edge_scroll )
{
    int new_view = curr_view;
	edge_scroll = (edge_scroll*view_size)/100 ;
    if( curr_pos >= new_view+view_size - 2 )
    {
	while ( curr_pos >= new_view+view_size - 2)
	    new_view += edge_scroll ;
	if( new_view > max_view )
	    new_view = max_view ;
    }else
    {
	while ( curr_pos < new_view+2)
	    new_view -= edge_scroll ;
	if( new_view < 0 )
	    new_view = 0 ;
    }
    return new_view;
}

void scroll_func_handler( FunctionData *data, ASEvent *event, int module )
{
#ifndef NO_VIRTUAL
    int x, y ;
	register ScreenInfo *scr = event->scr;

    x = make_scroll_pos( data->func_val[0], data->unit_val[0], scr->Vx, scr->VxMax, scr->MyDisplayWidth );
    y = make_scroll_pos( data->func_val[1], data->unit_val[1], scr->Vy, scr->VyMax, scr->MyDisplayHeight );

    MoveViewport ( x, y, True);
#endif
}

void movecursor_func_handler( FunctionData *data, ASEvent *event, int module )
{
    int curr_x, curr_y ;
    int x, y ;
    register ScreenInfo *scr = ASDefaultScr;

    ASQueryPointerRootXY(&curr_x, &curr_y);
    x = make_scroll_pos( data->func_val[0], data->unit_val[0], scr->Vx+curr_x, scr->VxMax+scr->MyDisplayWidth ,scr->MyDisplayWidth );
    y = make_scroll_pos( data->func_val[1], data->unit_val[1], scr->Vy+curr_y, scr->VyMax+scr->MyDisplayHeight,scr->MyDisplayHeight);

#ifndef NO_VIRTUAL
    {
	int new_vx = 0, new_vy = 0;

	new_vx = make_edge_scroll( x, scr->Vx, scr->MyDisplayWidth,  scr->VxMax, scr->Feel.EdgeScrollX );
	new_vy = make_edge_scroll( y, scr->Vy, scr->MyDisplayHeight, scr->VyMax, scr->Feel.EdgeScrollY );
	if( new_vx != scr->Vx || new_vy != scr->Vy )
	    MoveViewport (new_vx, new_vy, True);
    }
#endif
    x -= scr->Vx ;
    y -= scr->Vy ;
    if( x != curr_x || y != curr_y )
	XWarpPointer (dpy, scr->Root, scr->Root, 0, 0, scr->MyDisplayWidth,
		      scr->MyDisplayHeight, x, y);
}

void iconify_func_handler( FunctionData *data, ASEvent *event, int module )
{
	if( event->client )
	{

LOCAL_DEBUG_CALLER_OUT( "function %ld (val0 = %ld), event %d, window 0x%lX, window_name \"%s\", module %d",
			data?data->func:0, data?data->func_val[0]:0, event?event->x.type:-1, event?(unsigned long)event->w:0, event->client?ASWIN_NAME(event->client):"none", module );
	if (ASWIN_GET_FLAGS(event->client, AS_Iconic) )
	{
		if (data->func_val[0] <= 0)
		set_window_wm_state( event->client, False );
	}else if (data->func_val[0] >= 0)
		set_window_wm_state( event->client, True );
	}
}

void raiselower_func_handler( FunctionData *data, ASEvent *event, int module )
{
    if( event->client )
    {
		if( event->client->last_restack_time != CurrentTime &&
	    	event->event_time != CurrentTime &&
	    	event->client->last_restack_time >= event->event_time )
	    	return ;

		restack_window (event->client,None,(data->func==F_RAISE)?Above:
					   	((data->func==F_RAISELOWER)?Opposite:Below));
    }
}

void raise_it_func_handler( FunctionData *data, ASEvent *event, int module )
{
	ASWindow *asw = window2ASWindow( data->func_val[1] );
    activate_aswindow( asw, True, True );
}

void setlayer_func_handler( FunctionData *data, ASEvent *event, int module )
{
    register int func = data->func ;
    int layer = 0 ;

    if( func == F_PUTONTOP )
	layer = AS_LayerTop ;
    else if( func == F_PUTONBACK )
	layer = AS_LayerBack ;
    else
	layer = (func == F_TOGGLELAYER && ASWIN_LAYER(event->client) == data->func_val[1])?data->func_val[1]: data->func_val[0] ;
    change_aswindow_layer( event->client, layer );
}

void change_desk_func_handler( FunctionData *data, ASEvent *event, int module )
{
    if( event->client )
	change_aswindow_desktop( event->client, data->func_val[0] );
}

void toggle_status_func_handler( FunctionData *data, ASEvent *event, int module )
{
    ASFlagType toggle_flags = 0;
    if( data->func == F_STICK )
	toggle_flags = AS_Sticky ;
    else if( data->func == F_MAXIMIZE )
	{
		if( data->func_val[0] > 0 || data->func_val[1] == 0  )
		toggle_flags = AS_MaximizedX ;
		if( data->func_val[1] > 0 || data->func_val[0] == 0 )
			toggle_flags |= AS_MaximizedY ;
		if( event->client )
		{
			event->client->maximize_ratio_x = (data->unit_val[0] == 0 )? data->func_val[0]: (data->func_val[0]*data->unit_val[0]*100)/Scr.MyDisplayWidth ;
			event->client->maximize_ratio_y = (data->unit_val[1] == 0 )? data->func_val[1]: (data->func_val[1]*data->unit_val[1]*100)/Scr.MyDisplayHeight ;
		}
    }else if( data->func == F_SHADE )
	toggle_flags = AS_Shaded ;
    else
	return ;
    toggle_aswindow_status( event->client, toggle_flags );
}

void focus_func_handler( FunctionData *data, ASEvent *event, int module )
{
    activate_aswindow( event->client, True, False );
}

void warp_func_handler( FunctionData *data, ASEvent *event, int module )
{
    register ASWindow *t = NULL;

    if (data->text != NULL )
	if (*(data->text) != '\0')
	    t = pattern2ASWindow (data->text);
    if( t == NULL )
	t = warp_aswindow_list (Scr.Windows, (data->func == F_CHANGEWINDOW_DOWN ||
					      data->func == F_WARP_B));
    if ( t != NULL)
    {
	event->client = t;
	event->w = get_window_frame(t);
	StartWarping(ASDefaultScr);
	warp_to_aswindow(t, (data->func == F_WARP_F || data->func == F_WARP_B));
    }
}

void paste_selection_func_handler( FunctionData *data, ASEvent *event, int module )
{
    PasteSelection (event->scr);
}

void goto_bookmark_func_handler( FunctionData *data, ASEvent *event, int module )
{
	ASWindow *asw = bookmark2ASWindow( data->text );
	if( asw )
		activate_aswindow( asw, True, False );
}

void bookmark_window_func_handler( FunctionData *data, ASEvent *event, int module )
{
	bookmark_aswindow( event->client, data->text );
}

void pin_menu_func_handler( FunctionData *data, ASEvent *event, int module )
{
    ASMenu *menu = NULL;
	char * menu_name = data->text? data->text:data->name ;
    if( menu_name )
		menu = find_asmenu( menu_name );
    else if( event->client && event->client->internal )
    {
		ASMagic *data = event->client->internal->data ;
		if( data->magic == MAGIC_ASMENU )
	    	menu = (ASMenu*)data;
    }
    if( menu == NULL )
		XBell (dpy, event->scr->screen);
    else
		pin_asmenu( menu );
}


void close_func_handler( FunctionData *data, ASEvent *event, int module )
{
	if( event->client )
	{
		Window w = event->client->w ;

	LOCAL_DEBUG_OUT( "window(0x%lX)->protocols(0x%lX)", w, event->client->hints->protocols );
		if ( get_flags(event->client->hints->protocols, AS_DoesWmDeleteWindow) &&
			data->func != F_DESTROY)
	{
		LOCAL_DEBUG_OUT( "sending delete window request to 0x%lX", w );
			send_wm_protocol_request(w, _XA_WM_DELETE_WINDOW, CurrentTime);
	}else
		{
		if( event->client->internal != NULL || validate_drawable(w, NULL, NULL) == None)
		Destroy (event->client, True);
		else if (data->func == F_DELETE )
		XBell (dpy, event->scr->screen);
		else
				XKillClient (dpy, w);
			XSync (dpy, 0);
		}
	}
}

void restart_func_handler( FunctionData *data, ASEvent *event, int module )
{
    Done (True, data->text);
}

void exec_func_handler( FunctionData *data, ASEvent *event, int module )
{
    XGrabPointer( dpy, Scr.Root, True,
			      ButtonPressMask | ButtonReleaseMask,
		  GrabModeAsync, GrabModeAsync, Scr.Root, Scr.Feel.cursors[ASCUR_Wait], CurrentTime);
    XSync (dpy, 0);
    spawn_child( data->text, -1, -1, None, C_NO_CONTEXT, True, False, NULL );
    XUngrabPointer (dpy, CurrentTime);
    XSync (dpy, 0);
}

int find_escaped_chr_pos( const char *str, char c )
{
	int i ; 
	for( i = 0 ; str[i] != '\0' ; ++i ) 
	{
		if( str[i] == '\\' ) 
		{	
			if( str[++i] == '\0' ) 
				break;
		}else if( str[i] == c ) 
			break;
	}		   
	return i;	
}	 

char *parse_term_cmdl( const char *term_name, const char *term_command, const char *cmdl )
{
	int term_name_len, cmdl_len, curr_full, curr_cmdl;
	char *full_cmdl = NULL ; 
	Bool first = True ;
	
	if( term_name == NULL || term_command == NULL || cmdl == NULL ) 
		return NULL;

	LOCAL_DEBUG_OUT( "term_name = \"%s\", term_command = \"%s\", cmdl = \"%s\"", 
					 term_name, term_command, cmdl );
	curr_full = strlen(term_command);
	cmdl_len  = strlen(cmdl);
	term_name_len = strlen(term_name);
	curr_cmdl = 0 ;
	full_cmdl = safemalloc( curr_full+4+cmdl_len+1 );
	
	strcpy( full_cmdl, term_command );

	while( curr_cmdl < cmdl_len ) 
	{
		while( isspace(cmdl[curr_cmdl])) ++curr_cmdl;
		if( mystrncasecmp( &(cmdl[curr_cmdl]), "if(", 3 ) == 0 ) 
		{
			int tmp ;

			curr_cmdl += 3 ;
			tmp = curr_cmdl ; 
			curr_cmdl += find_escaped_chr_pos( &(cmdl[curr_cmdl]), '}' )+1;
			while( isspace(cmdl[tmp]) ) ++tmp ;
			if( mystrncasecmp( &(cmdl[tmp]), term_name, term_name_len ) == 0 )
			{
				tmp += term_name_len ;	  
				while( isspace(cmdl[tmp]) ) ++tmp ;
				if( cmdl[tmp] == ')' )
				{
					++tmp ;
					while( isspace(cmdl[tmp]) ) ++tmp ;
					if( cmdl[tmp] == '{' ) 
						++tmp ;
					while( isspace(cmdl[tmp]) ) ++tmp ;
					full_cmdl[curr_full++] = ' ';
					while( tmp < curr_cmdl-1 ) 
						full_cmdl[curr_full++] = cmdl[tmp++];
				}
			}
		}else	 
		{
			if( first )
			{	
				if( cmdl[curr_cmdl] != '-' ) 	
				{
					if( cmdl[curr_cmdl] != '\0' ) 	  
						sprintf( &(full_cmdl[curr_full]), " -e %s", &(cmdl[curr_cmdl]));
					else
						full_cmdl[curr_full] = '\0' ;
					return full_cmdl;
				}	 
				first = False ;
			}
			
			if( strncmp( &(cmdl[curr_cmdl]), "-e ", 3 ) == 0 ) 
			{
				sprintf( &(full_cmdl[curr_full]), " %s", &(cmdl[curr_cmdl]));
				return full_cmdl;
			}	 
			
			full_cmdl[curr_full++] = ' ';
			while( curr_cmdl < cmdl_len && !isspace(cmdl[curr_cmdl])) 
				full_cmdl[curr_full++] = cmdl[curr_cmdl++];
		}	 
		
	}	 
	full_cmdl[curr_full] = '\0' ;
	
	return full_cmdl;	
}	 

void exec_in_term_func_handler( FunctionData *data, ASEvent *event, int module )
{
	if( Environment->term_command != NULL && data->text != NULL ) 
	{
		char *full_cmdl = NULL;
		char *term_name = strrchr( Environment->term_command, '/' );
		term_name = (term_name == NULL )? Environment->term_command: term_name+1 ;
		full_cmdl = parse_term_cmdl( term_name, Environment->term_command, data->text );
		if( full_cmdl ) 
		{	
			LOCAL_DEBUG_OUT( "full_cmdl = [%s]", full_cmdl );
    		XGrabPointer( dpy, Scr.Root, True,
			      		ButtonPressMask | ButtonReleaseMask,
		  		GrabModeAsync, GrabModeAsync, Scr.Root, Scr.Feel.cursors[ASCUR_Wait], CurrentTime);
    		XSync (dpy, 0);
    		spawn_child( full_cmdl, -1, -1, None, C_NO_CONTEXT, True, False, NULL );
    		XUngrabPointer (dpy, CurrentTime);
			free( full_cmdl );
		}
    	XSync (dpy, 0);
	}
}


static int _as_config_change_recursion = 0 ;
static int _as_config_change_count = 0 ;
static int _as_background_change_count = 0 ;

void
commit_config_change( int func )
{
	if( _as_config_change_recursion <= 1 )
	{
		if( _as_background_change_count  > 0 )
		{
		    MyBackground *new_back = get_desk_back_or_default( Scr.CurrentDesk, False );
		SendPacket( -1, M_NEW_BACKGROUND, 1, 1);
			if( new_back->loaded_im_name )
			{
				free( new_back->loaded_im_name );
				new_back->loaded_im_name = NULL ;
			}
		change_desktop_background( Scr.CurrentDesk );
			_as_background_change_count = 0 ;
	}
		if ( _as_config_change_count > 0 )
	{
		if( func == F_CHANGE_THEME )
				QuickRestart ("theme");
		else if( func == F_CHANGE_COLORSCHEME )
				QuickRestart ("look");
			else
		QuickRestart ((func == F_CHANGE_LOOK)?"look":"feel");
			_as_config_change_count = 0 ;
		}
    }
}

void change_background_func_handler( FunctionData *data, ASEvent *event, int module )
{
    char tmpfile[256], *realfilename ;

	++_as_config_change_recursion;
    XGrabPointer (dpy, Scr.Root, True, ButtonPressMask | ButtonReleaseMask,
		  GrabModeAsync, GrabModeAsync, Scr.Root, Scr.Feel.cursors[ASCUR_Wait], CurrentTime);
    XSync (dpy, 0);

    if (Scr.screen == 0)
	sprintf (tmpfile, BACK_FILE, Scr.CurrentDesk);
    else
	sprintf (tmpfile, BACK_FILE ".scr%ld", Scr.CurrentDesk, Scr.screen);

    realfilename = make_session_data_file(Session, False, 0, tmpfile, NULL );
    cover_desktop();
    display_progress( True, "Copying selected background \"%s\" into \"%s\" ...", data->text, realfilename);
    LOCAL_DEBUG_OUT( "Copying selected background \"%s\" into \"%s\" ...", data->text, realfilename);
    if (CopyFile (data->text, realfilename) == 0)
    {
		++_as_background_change_count ;
		if( Scr.CurrentDesk == 0 )
		update_default_session ( Session, F_CHANGE_BACKGROUND );
	change_desk_session (Session, Scr.CurrentDesk, realfilename, F_CHANGE_BACKGROUND);
	}
	free (realfilename);

	commit_config_change( F_CHANGE_BACKGROUND );
    remove_desktop_cover();

    XUngrabPointer (dpy, CurrentTime);
    XSync (dpy, 0);
	--_as_config_change_recursion ;
}

void change_theme_func_handler( FunctionData *data, ASEvent *event, int module )
{
	++_as_config_change_recursion ;
	if( install_theme_file( data->text ) )
		++_as_config_change_count ;

	/* theme installation may trigger recursive look and feel changes - we
	 * don't want those to cause any restarts or config reloads.
	 */
	commit_config_change( data->func );

	--_as_config_change_recursion ;
}


void change_config_func_handler( FunctionData *data, ASEvent *event, int module )
{

	char *file_template ;
    char tmpfile[256], *realfilename = NULL ;
    int desk = 0 ;

	++_as_config_change_recursion ;
#ifdef DIFFERENTLOOKNFEELFOREACHDESKTOP
    desk = Scr.CurrentDesk ;
#endif
    if (Scr.screen == 0)
    {
		switch( data->func )
		{
			case F_CHANGE_LOOK : file_template = LOOK_FILE ;   break ;
			case F_CHANGE_FEEL : file_template = FEEL_FILE ;   break ;
			case F_CHANGE_COLORSCHEME : file_template = COLORSCHEME_FILE ;   break ;
			default: file_template = THEME_FILE ;   break ;
		}
	sprintf (tmpfile, file_template, desk);
    }else
    {
		switch( data->func )
		{
			case F_CHANGE_LOOK : file_template = LOOK_FILE  ".scr%ld" ;   break ;
			case F_CHANGE_FEEL : file_template = FEEL_FILE  ".scr%ld" ;   break ;
			case F_CHANGE_COLORSCHEME : file_template = COLORSCHEME_FILE  ".scr%ld" ;   break ;
			default: file_template = THEME_FILE  ".scr%ld" ;   break ;
		}
	sprintf (tmpfile, file_template, desk, Scr.screen);
    }

    realfilename = make_session_data_file(Session, False, 0, tmpfile, NULL );

    cover_desktop();
    display_progress( True, "Copying selected config file \"%s\" into \"%s\" ...", data->text, realfilename);

	if( CopyFile (data->text, realfilename) == 0 )
	{
		++_as_config_change_count ;
	if( Scr.CurrentDesk == 0 )
	    update_default_session ( Session, data->func );
	change_desk_session (Session, Scr.CurrentDesk, realfilename, data->func);
	}

	/* theme installation may trigger recursive look and feel changes - we
	 * don't want those to cause any restarts or config reloads.
	 */
	commit_config_change( data->func );
    remove_desktop_cover();

	--_as_config_change_recursion ;
}

void install_file_func_handler( FunctionData *data, ASEvent *event, int module )
{
	char *file = NULL ;
	char *realfilename = NULL ;
	Bool desktop_resource = False ;
	char *dir_name = NULL ;

	switch( data->func )
	{
		case F_INSTALL_LOOK :		dir_name = as_look_dir_name; break;
		case F_INSTALL_FEEL :		dir_name = as_feel_dir_name; break;
		case F_INSTALL_BACKGROUND : dir_name = as_background_dir_name; break;
		case F_INSTALL_FONT :		dir_name = as_font_dir_name; desktop_resource = True; break;
		case F_INSTALL_ICON :		dir_name = as_icon_dir_name; desktop_resource = True; break;
		case F_INSTALL_TILE :		dir_name = as_tile_dir_name; desktop_resource = True; break;
		case F_INSTALL_THEME_FILE : dir_name = as_theme_file_dir_name; break;
		case F_INSTALL_COLORSCHEME : dir_name= as_colorscheme_dir_name; break;
	}
	if( dir_name != NULL )
	{
		parse_file_name( data->text, NULL, &file ) ;

	cover_desktop();
	display_progress( True, "Installing file \"%s\" into \"%s\" ...", data->text, dir_name);

	if( desktop_resource )
		{
			realfilename = make_session_data_file  (Session, False, 0, DESKTOP_DIR, NULL );
	    CheckOrCreate(realfilename);
		    free( realfilename );
		}

		realfilename = make_session_data_file  (Session, False, 0, dir_name, NULL );
	CheckOrCreate(realfilename);
	    free( realfilename );

		realfilename = make_session_data_file  (Session, False, 0, dir_name, file, NULL );
		CopyFile (data->text, realfilename);
		free( realfilename );
		free( file );
	remove_desktop_cover();
	}
}

void install_feel_func_handler( FunctionData *data, ASEvent *event, int module )
{

}

void install_background_func_handler( FunctionData *data, ASEvent *event, int module )
{

}

void install_font_func_handler( FunctionData *data, ASEvent *event, int module )
{

}

void install_icon_func_handler( FunctionData *data, ASEvent *event, int module )
{

}

void install_tile_func_handler( FunctionData *data, ASEvent *event, int module )
{

}

void install_theme_file_func_handler( FunctionData *data, ASEvent *event, int module )
{

}


void save_workspace_func_handler( FunctionData *data, ASEvent *event, int module )
{
    save_aswindow_list( Scr.Windows, data->text?data->text:NULL );
}

void refresh_func_handler( FunctionData *data, ASEvent *event, int module )
{
    XSetWindowAttributes attributes;
	unsigned long valuemask;
	Window        w;

	valuemask = (CWBackPixmap | CWBackingStore | CWOverrideRedirect);
	attributes.background_pixmap = None;
	attributes.backing_store = NotUseful;
	attributes.override_redirect = True;

    w = create_visual_window(Scr.asv, Scr.Root, 0, 0,
			       Scr.MyDisplayWidth, Scr.MyDisplayHeight,
			       0, InputOutput, valuemask, &attributes);

	XMapRaised (dpy, w);
    XSync (dpy, False);
    XDestroyWindow (dpy, w);
    XFlush (dpy);
}

void goto_page_func_handler( FunctionData *data, ASEvent *event, int module )
{
#ifndef NO_VIRTUAL
    int newvx = data->func_val[0]*event->scr->MyDisplayWidth;
    int newvy = data->func_val[1]*event->scr->MyDisplayHeight;
LOCAL_DEBUG_OUT( "val(%ld,%ld)->scr(%d,%d)->newv(%d,%d)", data->func_val[0], data->func_val[1], event->scr->MyDisplayWidth, event->scr->MyDisplayHeight, newvx, newvy );
    MoveViewport ( newvx, newvy, True);
#endif
}

void toggle_page_func_handler( FunctionData *data, ASEvent *event, int module )
{
#ifndef NO_VIRTUAL
    if( get_flags( Scr.Feel.flags, DoHandlePageing ) )
	clear_flags( Scr.Feel.flags, DoHandlePageing );
	else
	set_flags( Scr.Feel.flags, DoHandlePageing );

    SendPacket( -1, M_TOGGLE_PAGING, 1, get_flags( Scr.Feel.flags, DoHandlePageing ));
    check_screen_panframes(ASDefaultScr);
#endif
}

void gethelp_func_handler( FunctionData *data, ASEvent *event, int module )
{
	if (event->client != NULL)
		if (ASWIN_RES_NAME(event->client)!= NULL)
		{
		char         *realfilename = PutHome(HELPCOMMAND);
	    XGrabPointer (dpy, Scr.Root, True,
			  ButtonPressMask | ButtonReleaseMask,
			  GrabModeAsync, GrabModeAsync, Scr.Root, Scr.Feel.cursors[ASCUR_Wait], CurrentTime);
	    XSync (dpy, 0);
	    spawn_child( realfilename, -1, -1, None, C_NO_CONTEXT, True, False, ASWIN_RES_NAME(event->client), NULL);
	    free (realfilename);
	    XUngrabPointer (dpy, CurrentTime);
	    XSync (dpy, 0);
	}
}

void wait_func_handler( FunctionData *data, ASEvent *event, int module )
{
	char *complex_pattern = data->text ;
	if( data->name && data->name[1] == ':' )
		complex_pattern = &(data->name[2]);

	WaitWindowLoop( complex_pattern, -1 );
	LOCAL_DEBUG_OUT( "Wait completed for \"%s\"", complex_pattern );
	XSync (dpy, 0);
}

void desk_func_handler( FunctionData *data, ASEvent *event, int module )
{
    long new_desk ;

    if ( data->func_val[0] != 0 )
		new_desk = Scr.CurrentDesk + data->func_val[0];
	else if( IsValidDesk (data->func_val[1]) )
		new_desk = data->func_val[1];
	else 
		return ;
    ChangeDesks (new_desk);
}

void deskviewport_func_handler( FunctionData *data, ASEvent *event, int module )
{
    unsigned int new_desk1, new_desk2 ;
	int new_vx, new_vy, flags ;
	int new_desk ;

	if( data->text == NULL )
		return ;
	if( parse_geometry (data->text, &new_vx, &new_vy, &new_desk1, &new_desk2, &flags ) == data->text )
		return ;

	if( !get_flags( flags, XValue ) )
		new_vx = Scr.Vx ;

	if( !get_flags( flags, YValue ) )
		new_vy = Scr.Vy ;

	if( get_flags( flags, WidthValue ) )
		new_desk = new_desk1 ;
	else if( get_flags( flags, HeightValue ) )
		new_desk = new_desk2 ;
	else
		new_desk = Scr.CurrentDesk ;

    ChangeDeskAndViewport (new_desk, new_vx, new_vy, False);
}

void module_func_handler( FunctionData *data, ASEvent *event, int module )
{
    UngrabEm ();
    ExecModule (data->text, event->client ? event->client->w : None, event->context);
}

void killmodule_func_handler( FunctionData *data, ASEvent *event, int module )
{
    KillModuleByName (data->text);
}

void popup_func_handler( FunctionData *data, ASEvent *event, int module )
{
    run_menu( data->text?data->text:data->name, event->client?event->client->w:None );
}

void quit_func_handler( FunctionData *data, ASEvent *event, int module )
{
    Done (0, NULL);
}

void windowlist_func_handler( FunctionData *data, ASEvent *event, int module )
{
#ifndef NO_WINDOWLIST
    MenuData *md =  make_desk_winlist_menu( Scr.Windows, data->text == NULL ? event->scr->CurrentDesk: data->func_val[0], Scr.Feel.winlist_sort_order, False );
    if( md != NULL )
	run_menu_data( md );
#endif /* ! NO_WINDOWLIST */
}

void quickrestart_func_handler( FunctionData *data, ASEvent *event, int module )
{
    QuickRestart (data->text);
}

Bool
send_aswindow_data_iter_func(void *data, void *aux_data)
{
    union { void *ptr ; int id; } module_id;
    ASWindow *asw = (ASWindow *)data ;

	module_id.ptr = aux_data ;

    SendConfig (module_id.id, M_CONFIGURE_WINDOW, asw);
    SendString (module_id.id, M_WINDOW_NAME, asw->w,asw->frame, asw, asw->hints->names[0], asw->hints->names_encoding[0]);
    SendString (module_id.id, M_ICON_NAME, asw->w, asw->frame, asw, asw->hints->icon_name, asw->hints->names_encoding[asw->hints->icon_name_idx]);
    SendString (module_id.id, M_RES_CLASS, asw->w, asw->frame, asw, asw->hints->res_class, asw->hints->names_encoding[asw->hints->res_class_idx]);
    SendString (module_id.id, M_RES_NAME,  asw->w, asw->frame, asw, asw->hints->res_name, asw->hints->names_encoding[asw->hints->res_name_idx]);
    return True;
}

void send_window_list_func_handler( FunctionData *data, ASEvent *event, int module )
{
    if (module >= 0)
    {
		union { void *ptr ; int id; } module_id;
		module_id.id = module ;
	SendPacket (module, M_TOGGLE_PAGING, 1, DoHandlePageing);
	SendPacket (module, M_NEW_DESKVIEWPORT, 3, Scr.Vx, Scr.Vy, Scr.CurrentDesk);
	iterate_asbidirlist( Scr.Windows->clients, send_aswindow_data_iter_func, module_id.ptr, NULL, False );
	SendPacket (module, M_END_WINDOWLIST, 0);
	if( IsValidDesk(Scr.CurrentDesk) )
	    restack_window_list( Scr.CurrentDesk, True );
    }
}

void screenshot_func_handler( FunctionData *data, ASEvent *event, int module )
{
	ASImage *im ;
	Window target = None ;
	sleep_a_millisec(300);
	if( event->client && data->func != F_TAKE_SCREENSHOT)
		target = (data->func == F_TAKE_WINDOWSHOT)?event->client->w:event->client->frame;
	im = grab_root_asimage( ASDefaultScr, target, True );
	LOCAL_DEBUG_OUT( "grab_root_image returned %p", im );
	if( im != NULL )
	{
		char *realfilename = NULL;
		Bool replace = True ;
		char *type = NULL ;
		char *compress = NULL ;  /* default compression */
#ifdef DONT_REPLACE_SCREENSHOT_FILES
		replace = False ;
#endif
		if( data->text != NULL )
		{
			realfilename = PutHome(data->text);
			type = strrchr( realfilename, '.' );
			if( type != NULL )
			{
				++type ;
				if( mystrcasecmp(type, "jpg") == 0 || mystrcasecmp(type, "jpeg") == 0 )
					compress = "0" ;
			}
		}
		if( realfilename == NULL )
		{
			char *capture_file_name = DEFAULT_CAPTURE_SCREEN_FILE;
			char *default_template;
			if (data->func == F_TAKE_WINDOWSHOT)
			    capture_file_name = DEFAULT_CAPTURE_WINDOW_FILE;
			else if (data->func == F_TAKE_FRAMESHOT)
			    capture_file_name = DEFAULT_CAPTURE_FRAMEDWINDOW_FILE;
			default_template = safemalloc(strlen(capture_file_name)+100);
			sprintf(default_template, "%s.%lu.png", capture_file_name, time(NULL));
			realfilename = PutHome(default_template);
			free( default_template );
			compress = "9" ;
			type = "png" ;
		}

		if( save_asimage_to_file(realfilename, im, type, compress, NULL, 0, replace) )
			show_warning( "screenshot saved as \"%s\"", realfilename );
		free( realfilename ) ;
	}
}

void swallow_window_func_handler( FunctionData *data, ASEvent *event, int module )
{
	if( event->client ) 
	{	
		if( data->text ) 
			
			
			
			module = FindModuleByName( data->text );
		SendPacket( module, M_SWALLOW_WINDOW, 2, event->client->w, event->client->frame);
 	}	
}	 

void test_func_handler( FunctionData *data, ASEvent *event, int module )
{
	 /* add test command processing here : */
/*         fprintf( stderr, "Testing <do_menu_new( \"Looks\", NULL ) ...\n" ); */
/*         do_menu_new( "Look", NULL, NULL ); */
		 fprintf( stderr, "Testing completed\n" );
}

void
QuickRestart (char *what)
{
    unsigned long what_flags = 0;
    Bool          update_background = False;

	if (what == NULL)
		return;

    if (strcasecmp (what, "all") == 0 || strcasecmp (what, "theme") == 0)
	what_flags = PARSE_EVERYTHING;
    else if (strcasecmp (what, "look&feel") == 0)
	what_flags = PARSE_LOOK_CONFIG|PARSE_FEEL_CONFIG;
    else if (strcasecmp (what, "startmenu") == 0 || strcasecmp (what, "feel") == 0)
	what_flags = PARSE_FEEL_CONFIG;
    else if (strcasecmp (what, "look") == 0)
	what_flags = PARSE_LOOK_CONFIG;
    else if (strcasecmp (what, "base") == 0)
	what_flags = PARSE_BASE_CONFIG;
    else if (strcasecmp (what, "database") == 0)
	what_flags = PARSE_DATABASE_CONFIG;
    else if (strcasecmp (what, "background") == 0)
		update_background = True;

	/* Force reinstall */
    if (what)
	{
	InstallRootColormap();
	GrabEm (ASDefaultScr, Scr.Feel.cursors[ASCUR_Wait]);
	LoadASConfig (Scr.CurrentDesk, what_flags);
	UngrabEm ();
	}

	if (update_background)
	SendPacket( -1, M_NEW_BACKGROUND, 1, 1);
	SendPacket( -1, M_NEW_CONFIG, 1, what_flags);
}
