/****************************************************************************
 * 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 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 );

/* 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] =
        function_handlers[F_DropExec]       = exec_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 ;
}

/* 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 = &Scr ;
	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 */
    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)
        	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 ) 
			if( event->client != NULL ) 
				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 (&Scr, Scr.Feel.cursors[cursor])))
	{
        WaitEventLoop( event, finish_event, -1 );
        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 (&Scr, 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]);
        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( &Scr );
            mvrdata->below_sibling = get_lowest_panframe(&Scr);
            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 = &Scr;

    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[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->func_val[0] * data->unit_val[0]) / Scr.MyDisplayWidth ;
			event->client->maximize_ratio_y =  (data->func_val[1] * data->unit_val[1]) / 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(&Scr);
        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;
    if( data->name )
        menu = find_asmenu( data->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);
}

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(&Scr);
#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 && !IsValidDesk (data->func_val[0]))
        new_desk = Scr.CurrentDesk + data->func_val[0];
	else
        new_desk = data->func_val[1];

    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->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(500);
	if( event->client && data->func != F_TAKE_SCREENSHOT)
		target = (data->func == F_TAKE_WINDOWSHOT)?event->client->w:event->client->frame;
	im = grab_root_asimage( &Scr, target, True );
	LOCAL_DEBUG_OUT( "grab_root_image returned %p", im );
	if( im != NULL )
	{
		char *realfilename = NULL;
		Bool replace = True ;
		char *type = NULL ;
#ifdef DONT_REPLACE_SCREENSHOT_FILES
		replace = False ;
#endif
		if( data->text != NULL )
		{
			realfilename = PutHome(data->text);
			type = strrchr( realfilename, '.' );
			if( type != NULL )
				++type ;
		}
		if( realfilename == NULL )
		{
			char default_template[128];
			sprintf(&(default_template[0]), DEFAULT_SCREENSHOT_FILE ".%lu.jpg", time(NULL));
			realfilename = PutHome(&(default_template[0]));
		}
        
		if( save_asimage_to_file(realfilename, im, type, "4", NULL, 0, replace) )
			show_warning( "screenshot saved as \"%s\"", realfilename );
		free( realfilename ) ;
	}
}


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 (&Scr, 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);
}


