OO-ify the Switchers.

This commit is contained in:
QC 2015-03-27 20:28:53 +01:00
parent f52b697dd4
commit b21368c005
14 changed files with 571 additions and 423 deletions

View File

@ -12,6 +12,7 @@
- Improve rendering of boxes (fixed height and margins)
- Fix modi switcher boxes size+layout.
- Reduce work on redraws (do not always calculate new size/position), set text, etc.
- OO-ify the switchers.
Cleanup:
- Do not lug argc,argv around everywhere.

View File

@ -1,14 +1,5 @@
#ifndef __RUN_DIALOG_H__
#define __RUN_DIALOG_H__
/**
* @param input Pointer to the user-input string.
* @param data Custom data pointer for callback.
*
* Run the run dialog
*
* @returns SwitcherMode selected by user.
*/
SwitcherMode run_switcher_dialog ( char **input, void *data );
extern Switcher run_mode;
#endif

View File

@ -1,27 +1,6 @@
#ifndef __SCRIPT_DIALOG_H__
#define __SCRIPT_DIALOG_H__
/**
* Structure holds the arguments for the script_switcher.
*/
typedef struct
{
/** Prompt to display. */
char *name;
/** The script */
char *script_path;
} ScriptOptions;
/**
* @param input Pointer to the user-input string.
* @param data Custom data pointer for callback.
*
* Run the script dialog
*
* @returns SwitcherMode selected by user.
*/
SwitcherMode script_switcher_dialog ( char **input, void *data );
/**
* @param str The input string to parse
@ -31,12 +10,5 @@ SwitcherMode script_switcher_dialog ( char **input, void *data );
*
* @returns NULL when it fails, a newly allocated ScriptOptions when successful.
*/
ScriptOptions *script_switcher_parse_setup ( const char *str );
/**
* @param sw Handle to the ScriptOption
*
* Free the ScriptOptions block.
*/
void script_switcher_free_options ( void *data );
Switcher *script_switcher_parse_setup ( const char *str );
#endif

View File

@ -1,14 +1,5 @@
#ifndef __SSH_DIALOG_H__
#define __SSH_DIALOG_H__
/**
* @param input Pointer to the user-input string.
* @param data Custom data pointer for callback.
*
* Run the ssh dialog
*
* @returns SwitcherMode selected by user.
*/
SwitcherMode ssh_switcher_dialog ( char **input, void *data );
extern Switcher ssh_mode;
#endif

View File

@ -1,6 +1,5 @@
#ifndef __WINDOW_DIALOG_H__
#define __WINDOW_DIALOG_H__
SwitcherMode run_switcher_window ( char **input, G_GNUC_UNUSED void *data );
extern Switcher window_mode;
#endif // __WINDOW_DIALOG_H__

View File

@ -1,6 +1,6 @@
#ifndef __HELPER_H__
#define __HELPER_H__
#include "rofi.h"
/**
* @param string The input string.
* @param output Pointer to 2 dimensional array with parsed string.
@ -105,7 +105,7 @@ int find_arg ( const char * const key );
*/
int token_match ( char **tokens, const char *input, int case_sensitive,
__attribute__( ( unused ) ) int index,
__attribute__( ( unused ) ) void *data );
__attribute__( ( unused ) ) Switcher * data );
/**
* @param cmd The command to execute.
@ -115,7 +115,7 @@ int token_match ( char **tokens, const char *input, int case_sensitive,
*
* @returns a valid file descriptor on success, or -1 on failure.
*/
int execute_generator ( char * cmd ) __attribute__( ( nonnull ) );
int execute_generator ( const char * cmd ) __attribute__( ( nonnull ) );
/**
* @param pidfile The pidfile to create.

View File

@ -2,6 +2,7 @@
#define __SIMPLESWITCHER_H__
#include <X11/X.h>
#include <glib.h>
#include <textbox.h>
/**
@ -9,7 +10,7 @@
*/
extern const char *cache_dir;
typedef struct _Switcher Switcher;
/**
* Enum used to sum the possible states of ROFI.
*/
@ -34,7 +35,7 @@ typedef enum
* @returns SwitcherMode
*/
typedef SwitcherMode ( *switcher_callback )( char **input, void *data );
typedef void ( *switcher_callback_free_data )( void *data );
typedef void ( *switcher_free )( Switcher *data );
/**
* State returned by the rofi window.
@ -42,19 +43,21 @@ typedef void ( *switcher_callback_free_data )( void *data );
typedef enum
{
/** Entry is selected. */
MENU_OK = 0,
MENU_OK = 0x1,
/** User canceled the operation. (e.g. pressed escape) */
MENU_CANCEL = -1,
MENU_CANCEL = 0x2,
/** User requested a mode switch */
MENU_NEXT = -2,
MENU_NEXT = 0x4,
/** Custom (non-matched) input was entered. */
MENU_CUSTOM_INPUT = -3,
MENU_CUSTOM_INPUT = 0x8,
/** User wanted to delete entry from history. */
MENU_ENTRY_DELETE = -4,
MENU_ENTRY_DELETE = 0x10,
/** User wants to jump to another switcher. */
MENU_QUICK_SWITCH = -5,
MENU_QUICK_SWITCH = 0x20,
/** Go to the previous menu. */
MENU_PREVIOUS = -6
MENU_PREVIOUS = 0x40,
/** Modifiers */
MENU_SHIFT = 0x1000
} MenuReturn;
@ -69,7 +72,7 @@ typedef enum
*
* @returns 1 when it matches, 0 if not.
*/
typedef int ( *menu_match_cb )( char **tokens, const char *input, int case_sensitive, int index, void *data );
typedef int ( *menu_match_cb )( char **tokens, const char *input, int case_sensitive, int index, Switcher *data );
/**
* @param lines An array of strings to display.
@ -87,9 +90,9 @@ typedef int ( *menu_match_cb )( char **tokens, const char *input, int case_sensi
* @returns The command issued (see MenuReturn)
*/
MenuReturn menu ( char **lines, unsigned int num_lines, char **input, char *prompt,
Time *time, int *shift,
Time *time,
menu_match_cb mmc, void *mmc_data,
int *selected_line, int sorting ) __attribute__ ( ( nonnull ( 1, 3, 4, 9 ) ) );
int *selected_line, int sorting ) __attribute__ ( ( nonnull ( 1, 3, 4, 8 ) ) );
/**
* @param sig The caught signal
*
@ -212,4 +215,42 @@ extern Settings config;
*/
void error_dialog ( const char *msg );
/**
* Structure defining a switcher.
* It consists of a name, callback and if enabled
* a textbox for the sidebar-mode.
*/
struct _Switcher
{
// Name (max 31 char long)
char name[32];
// Textbox used in the sidebar-mode.
textbox *tb;
// Keybindings (keysym and modmask)
char * keycfg;
char * keystr;
KeySym keysym;
unsigned int modmask;
/**
* A switcher normally consists of the following parts:
*/
void ( *init )( struct _Switcher *sw );
char ** ( *get_data )( unsigned int *length, struct _Switcher *pd );
int ( *match )( char **tokens, const char *input, int case_sensitive, int index, struct _Switcher *data );
SwitcherMode ( *result )( int menu_retv, char **input, unsigned int selected_line, struct _Switcher *pd );
void ( *destroy )( struct _Switcher *pd );
// Token match.
menu_match_cb token_match;
// Pointer to private data.
void *private_data;
// Extra fields for script
void *ed;
// Free SWitcher
switcher_free free;
};
#endif

View File

@ -83,13 +83,12 @@ int dmenu_switcher_dialog ( char **input )
find_arg_int ( "-l", &selected_line );
do {
int shift = 0;
int mretv = menu ( list, length, input, dmenu_prompt, NULL, &shift,
int mretv = menu ( list, length, input, dmenu_prompt, NULL,
token_match, NULL, &selected_line, FALSE );
// We normally do not want to restart the loop.
restart = FALSE;
if ( mretv == MENU_OK && list[selected_line] != NULL ) {
if ( ( mretv & MENU_OK ) && list[selected_line] != NULL ) {
if ( number_mode ) {
fprintf ( stdout, "%d", selected_line );
}
@ -98,20 +97,20 @@ int dmenu_switcher_dialog ( char **input )
}
fputc ( '\n', stdout );
fflush ( stdout );
if ( shift ) {
if ( ( mretv & MENU_SHIFT ) ) {
restart = TRUE;
// Move to next line.
selected_line = MIN ( selected_line + 1, length - 1 );
}
retv = TRUE;
}
else if ( mretv == MENU_CUSTOM_INPUT && *input != NULL && *input[0] != '\0' ) {
else if ( ( mretv & MENU_CUSTOM_INPUT ) && *input != NULL && *input[0] != '\0' ) {
if ( !number_mode ) {
fputs ( *input, stdout );
fputc ( '\n', stdout );
fflush ( stdout );
}
if ( shift ) {
if ( ( mretv & MENU_SHIFT ) ) {
restart = TRUE;
// Move to next line.
selected_line = MIN ( selected_line + 1, length - 1 );

View File

@ -152,9 +152,8 @@ static char ** get_apps_external ( char **retv, unsigned int *length, unsigned i
}
// No duplicate, add it.
retv = g_realloc ( retv, ( ( *length ) + 2 ) * sizeof ( char* ) );
retv[( *length )] = g_strdup ( buffer );
retv[( *length ) + 1] = NULL;
retv = g_realloc ( retv, ( ( *length ) + 2 ) * sizeof ( char* ) );
retv[( *length )] = g_strdup ( buffer );
( *length )++;
@ -162,14 +161,16 @@ static char ** get_apps_external ( char **retv, unsigned int *length, unsigned i
fclose ( inp );
}
}
retv[( *length ) ] = NULL;
return retv;
}
/**
* Internal spider used to get list of executables.
*/
static char ** get_apps ( char **retv, unsigned int *length )
static char ** get_apps ( unsigned int *length )
{
char **retv = NULL;
unsigned int num_favorites = 0;
char *path;
@ -263,47 +264,85 @@ static char ** get_apps ( char **retv, unsigned int *length )
return retv;
}
SwitcherMode run_switcher_dialog ( char **input, G_GNUC_UNUSED void *data )
typedef struct _RunModePrivateData
{
int shift = 0;
int selected_line = 0;
SwitcherMode retv = MODE_EXIT;
// act as a launcher
unsigned int cmd_list_length = 0;
char **cmd_list = NULL;
cmd_list = get_apps ( cmd_list, &cmd_list_length );
unsigned int id;
char **cmd_list;
unsigned int cmd_list_length;
} RunModePrivateData;
if ( cmd_list == NULL ) {
cmd_list = g_malloc_n ( 2, sizeof ( char * ) );
cmd_list[0] = g_strdup ( "No applications found" );
cmd_list[1] = NULL;
static void run_mode_init ( Switcher *sw )
{
if ( sw->private_data == NULL ) {
RunModePrivateData *pd = g_malloc0 ( sizeof ( *pd ) );
sw->private_data = (void *) pd;
}
}
int mretv = menu ( cmd_list, cmd_list_length, input, "run:",
NULL, &shift, token_match, NULL, &selected_line,
config.levenshtein_sort );
static char ** run_mode_get_data ( unsigned int *length, Switcher *sw )
{
RunModePrivateData *rmpd = (RunModePrivateData *) sw->private_data;
if ( rmpd->cmd_list == NULL ) {
rmpd->cmd_list_length = 0;
rmpd->cmd_list = get_apps ( &( rmpd->cmd_list_length ) );
}
*length = rmpd->cmd_list_length;
return rmpd->cmd_list;
}
if ( mretv == MENU_NEXT ) {
static SwitcherMode run_mode_result ( int mretv, char **input, unsigned int selected_line, Switcher *sw )
{
RunModePrivateData *rmpd = (RunModePrivateData *) sw->private_data;
SwitcherMode retv = MODE_EXIT;
int shift = ( ( mretv & MENU_SHIFT ) == MENU_SHIFT );
if ( mretv & MENU_NEXT ) {
retv = NEXT_DIALOG;
}
else if ( mretv == MENU_PREVIOUS ) {
else if ( mretv & MENU_PREVIOUS ) {
retv = PREVIOUS_DIALOG;
}
else if ( mretv == MENU_QUICK_SWITCH ) {
else if ( mretv & MENU_QUICK_SWITCH ) {
retv = selected_line;
}
else if ( mretv == MENU_OK && cmd_list[selected_line] != NULL ) {
exec_cmd ( cmd_list[selected_line], shift );
else if ( ( mretv & MENU_OK ) && rmpd->cmd_list[selected_line] != NULL ) {
exec_cmd ( rmpd->cmd_list[selected_line], shift );
}
else if ( mretv == MENU_CUSTOM_INPUT && *input != NULL && *input[0] != '\0' ) {
else if ( ( mretv & MENU_CUSTOM_INPUT ) && *input != NULL && *input[0] != '\0' ) {
exec_cmd ( *input, shift );
}
else if ( mretv == MENU_ENTRY_DELETE && cmd_list[selected_line] ) {
delete_entry ( cmd_list[selected_line] );
else if ( ( mretv & MENU_ENTRY_DELETE ) && rmpd->cmd_list[selected_line] ) {
delete_entry ( rmpd->cmd_list[selected_line] );
retv = RELOAD_DIALOG;
}
g_strfreev ( cmd_list );
return retv;
}
static void run_mode_destroy ( Switcher *sw )
{
RunModePrivateData *rmpd = (RunModePrivateData *) sw->private_data;
if ( rmpd != NULL ) {
g_strfreev ( rmpd->cmd_list );
g_free ( rmpd );
sw->private_data = NULL;
}
}
Switcher run_mode =
{
.name = "run",
.tb = NULL,
.keycfg = NULL,
.keystr = NULL,
.modmask = AnyModifier,
.init = run_mode_init,
.get_data = run_mode_get_data,
.result = run_mode_result,
.destroy = run_mode_destroy,
.token_match = token_match,
.private_data = NULL,
.free = NULL
};

View File

@ -42,7 +42,7 @@
static char **get_script_output ( char *command, unsigned int *length )
static char **get_script_output ( const char *command, unsigned int *length )
{
char **retv = NULL;
@ -70,101 +70,131 @@ static char **get_script_output ( char *command, unsigned int *length )
return retv;
}
static char **execute_executor ( ScriptOptions *options, const char *result, unsigned int *length )
static char **execute_executor ( Switcher *sw, const char *result, unsigned int *length )
{
char **retv = NULL;
char *arg = g_shell_quote ( result );
char *command = g_strdup_printf ( "%s %s", options->script_path, arg );
char *command = g_strdup_printf ( "%s %s", (const char *) sw->ed, arg );
retv = get_script_output ( command, length );
g_free ( command );
g_free ( arg );
return retv;
}
SwitcherMode script_switcher_dialog ( char **input, void *data )
static void script_switcher_free ( Switcher *sw )
{
ScriptOptions *options = (ScriptOptions *) data;
assert ( options != NULL );
int selected_line = 0;
SwitcherMode retv = MODE_EXIT;
unsigned int length = 0;
char **list = get_script_output ( options->script_path, &length );
char *prompt = g_strdup_printf ( "%s:", options->name );
do {
unsigned int new_length = 0;
char **new_list = NULL;
int mretv = menu ( list, length, input, prompt, NULL, NULL,
token_match, NULL, &selected_line, FALSE );
if ( mretv == MENU_NEXT ) {
retv = NEXT_DIALOG;
}
else if ( mretv == MENU_PREVIOUS ) {
retv = PREVIOUS_DIALOG;
}
else if ( mretv == MENU_QUICK_SWITCH ) {
retv = selected_line;
}
else if ( mretv == MENU_OK && list[selected_line] != NULL ) {
new_list = execute_executor ( options, list[selected_line], &new_length );
}
else if ( mretv == MENU_CUSTOM_INPUT && *input != NULL && *input[0] != '\0' ) {
new_list = execute_executor ( options, *input, &new_length );
}
// Free old list.
g_strfreev ( list );
list = NULL;
// If a new list was generated, use that an loop around.
if ( new_list != NULL ) {
list = new_list;
length = new_length;
g_free ( *input );
*input = NULL;
}
} while ( list != NULL );
g_free ( prompt );
return retv;
}
void script_switcher_free_options ( void *data )
{
ScriptOptions *sw = (ScriptOptions *) data;
if ( sw == NULL ) {
return;
}
g_free ( sw->name );
g_free ( sw->script_path );
g_free ( sw->ed );
g_free ( sw );
}
ScriptOptions *script_switcher_parse_setup ( const char *str )
typedef struct _ScriptModePrivateData
{
ScriptOptions *sw = g_malloc0 ( sizeof ( *sw ) );
char *endp = NULL;
char *parse = g_strdup ( str );
unsigned int index = 0;
unsigned int id;
char **cmd_list;
unsigned int cmd_list_length;
} ScriptModePrivateData;
static void script_mode_init ( Switcher *sw )
{
if ( sw->private_data == NULL ) {
ScriptModePrivateData *pd = g_malloc0 ( sizeof ( *pd ) );
sw->private_data = (void *) pd;
}
}
static char ** script_mode_get_data ( unsigned int *length, Switcher *sw )
{
ScriptModePrivateData *rmpd = (ScriptModePrivateData *) sw->private_data;
if ( rmpd->cmd_list == NULL ) {
rmpd->cmd_list_length = 0;
rmpd->cmd_list = get_script_output ( (const char *) sw->ed, &( rmpd->cmd_list_length ) );
}
*length = rmpd->cmd_list_length;
return rmpd->cmd_list;
}
static SwitcherMode script_mode_result ( int mretv, char **input, unsigned int selected_line, Switcher *sw )
{
ScriptModePrivateData *rmpd = (ScriptModePrivateData *) sw->private_data;
SwitcherMode retv = MODE_EXIT;
char **new_list = NULL;
unsigned int new_length = 0;
if ( ( mretv & MENU_NEXT ) ) {
retv = NEXT_DIALOG;
}
else if ( ( mretv & MENU_PREVIOUS ) ) {
retv = PREVIOUS_DIALOG;
}
else if ( ( mretv & MENU_QUICK_SWITCH ) ) {
retv = selected_line;
}
else if ( ( mretv & MENU_OK ) && rmpd->cmd_list[selected_line] != NULL ) {
new_list = execute_executor ( sw, rmpd->cmd_list[selected_line], &new_length );
}
else if ( ( mretv & MENU_CUSTOM_INPUT ) && *input != NULL && *input[0] != '\0' ) {
new_list = execute_executor ( sw, *input, &new_length );
}
// If a new list was generated, use that an loop around.
if ( new_list != NULL ) {
g_strfreev ( rmpd->cmd_list );
rmpd->cmd_list = new_list;
rmpd->cmd_list_length = new_length;
g_free ( *input );
*input = NULL;
retv = RELOAD_DIALOG;
}
return retv;
}
static void script_mode_destroy ( Switcher *sw )
{
ScriptModePrivateData *rmpd = (ScriptModePrivateData *) sw->private_data;
if ( rmpd != NULL ) {
g_strfreev ( rmpd->cmd_list );
g_free ( rmpd );
sw->private_data = NULL;
}
}
Switcher *script_switcher_parse_setup ( const char *str )
{
Switcher *sw = g_malloc0 ( sizeof ( *sw ) );
char *endp = NULL;
char *parse = g_strdup ( str );
unsigned int index = 0;
for ( char *token = strtok_r ( parse, ":", &endp ); token != NULL; token = strtok_r ( NULL, ":", &endp ) ) {
if ( index == 0 ) {
sw->name = g_strdup ( token );
g_strlcpy ( sw->name, token, 32 );
}
else if ( index == 1 ) {
sw->script_path = g_strdup ( token );
sw->ed = (void *) g_strdup ( token );
}
index++;
}
g_free ( parse );
if ( index == 2 ) {
sw->free = script_switcher_free;
sw->keysym = None;
sw->modmask = AnyModifier;
sw->init = script_mode_init;
sw->get_data = script_mode_get_data;
sw->result = script_mode_result;
sw->destroy = script_mode_destroy;
sw->token_match = token_match;
return sw;
}
fprintf ( stderr, "The script command '%s' has %u options, but needs 2: <name>:<script>.\n",
str, index );
script_switcher_free_options ( sw );
script_switcher_free ( sw );
return NULL;
}

View File

@ -109,11 +109,11 @@ static int ssh_sort_func ( const void *a, const void *b )
const char *bstr = *( const char * const * ) b;
return g_utf8_collate ( astr, bstr );
}
static char ** get_ssh ( unsigned int *length )
static char ** get_ssh ( unsigned int *length )
{
char **retv = NULL;
unsigned int num_favorites = 0;
char *path;
char **retv = NULL;
if ( getenv ( "HOME" ) == NULL ) {
return NULL;
@ -191,47 +191,80 @@ static char ** get_ssh ( unsigned int *length )
return retv;
}
SwitcherMode ssh_switcher_dialog ( char **input, G_GNUC_UNUSED void *data )
typedef struct _SSHModePrivateData
{
SwitcherMode retv = MODE_EXIT;
// act as a launcher
unsigned int cmd_list_length = 0;
char **cmd_list = get_ssh ( &cmd_list_length );
unsigned int id;
char **cmd_list;
unsigned int cmd_list_length;
} SSHModePrivateData;
if ( cmd_list == NULL ) {
cmd_list = g_malloc_n ( 2, sizeof ( char * ) );
cmd_list[0] = g_strdup ( "No ssh hosts found" );
cmd_list[1] = NULL;
static void ssh_mode_init ( Switcher *sw )
{
if ( sw->private_data == NULL ) {
SSHModePrivateData *pd = g_malloc0 ( sizeof ( *pd ) );
sw->private_data = (void *) pd;
}
}
int shift = 0;
int selected_line = 0;
int mretv = menu ( cmd_list, cmd_list_length, input, "ssh:",
NULL, &shift, token_match, NULL, &selected_line,
config.levenshtein_sort );
if ( mretv == MENU_NEXT ) {
static char ** ssh_mode_get_data ( unsigned int *length, Switcher *sw )
{
SSHModePrivateData *rmpd = (SSHModePrivateData *) sw->private_data;
if ( rmpd->cmd_list == NULL ) {
rmpd->cmd_list_length = 0;
rmpd->cmd_list = get_ssh ( &( rmpd->cmd_list_length ) );
}
*length = rmpd->cmd_list_length;
return rmpd->cmd_list;
}
static SwitcherMode ssh_mode_result ( int mretv, char **input, unsigned int selected_line, Switcher *sw )
{
SwitcherMode retv = MODE_EXIT;
SSHModePrivateData *rmpd = (SSHModePrivateData *) sw->private_data;
if ( mretv & MENU_NEXT ) {
retv = NEXT_DIALOG;
}
else if ( mretv == MENU_PREVIOUS ) {
else if ( mretv & MENU_PREVIOUS ) {
retv = PREVIOUS_DIALOG;
}
else if ( mretv == MENU_QUICK_SWITCH ) {
else if ( mretv & MENU_QUICK_SWITCH ) {
retv = selected_line;
}
else if ( mretv == MENU_OK && cmd_list[selected_line] != NULL ) {
exec_ssh ( cmd_list[selected_line] );
else if ( ( mretv & MENU_OK ) && rmpd->cmd_list[selected_line] != NULL ) {
exec_ssh ( rmpd->cmd_list[selected_line] );
}
else if ( mretv == MENU_CUSTOM_INPUT && *input != NULL && *input[0] != '\0' ) {
else if ( ( mretv & MENU_CUSTOM_INPUT ) && *input != NULL && *input[0] != '\0' ) {
exec_ssh ( *input );
}
else if ( mretv == MENU_ENTRY_DELETE && cmd_list[selected_line] ) {
delete_ssh ( cmd_list[selected_line] );
else if ( ( mretv & MENU_ENTRY_DELETE ) && rmpd->cmd_list[selected_line] ) {
delete_ssh ( rmpd->cmd_list[selected_line] );
// Stay
retv = RELOAD_DIALOG;
}
g_strfreev ( cmd_list );
return retv;
}
static void ssh_mode_destroy ( Switcher *sw )
{
SSHModePrivateData *rmpd = (SSHModePrivateData *) sw->private_data;
if ( rmpd != NULL ) {
g_strfreev ( rmpd->cmd_list );
g_free ( rmpd );
sw->private_data = NULL;
}
}
Switcher ssh_mode =
{
.name = "ssh",
.tb = NULL,
.keycfg = NULL,
.keystr = NULL,
.modmask = AnyModifier,
.init = ssh_mode_init,
.get_data = ssh_mode_get_data,
.result = ssh_mode_result,
.destroy = ssh_mode_destroy,
.token_match = token_match,
.private_data = NULL,
.free = NULL
};

View File

@ -278,14 +278,23 @@ static client* window_client ( Display *display, Window win )
return c;
}
typedef struct _SwitcherModePrivateData
{
unsigned int id;
char **cmd_list;
unsigned int cmd_list_length;
winlist *ids;
int config_i3_mode;
int init;
} SwitcherModePrivateData;
static int window_match ( char **tokens, __attribute__( ( unused ) ) const char *input,
int case_sensitive, int index, void *data )
int case_sensitive, int index, Switcher *sw )
{
int match = 1;
winlist *ids = ( winlist * ) data;
client *c = window_client ( display, ids->array[index] );
SwitcherModePrivateData *rmpd = (SwitcherModePrivateData *) sw->private_data;
int match = 1;
winlist *ids = ( winlist * ) rmpd->ids;
client *c = window_client ( display, ids->array[index] );
if ( tokens ) {
// Create collate keys.
@ -324,149 +333,186 @@ static int window_match ( char **tokens, __attribute__( ( unused ) ) const char
return match;
}
SwitcherMode run_switcher_window ( char **input, G_GNUC_UNUSED void *data )
static void window_mode_init ( Switcher *sw )
{
Screen *screen = DefaultScreenOfDisplay ( display );
Window root = RootWindow ( display, XScreenNumberOfScreen ( screen ) );
SwitcherMode retv = MODE_EXIT;
// find window list
Atom type;
int nwins;
Window wins[100];
int count = 0;
Window curr_win_id = 0;
// Create cache
x11_cache_create ();
// Check for i3
int config_i3_mode = i3_support_initialize ( display );
// Get the active window so we can highlight this.
if ( !( window_get_prop ( display, root, netatoms[_NET_ACTIVE_WINDOW], &type,
&count, &curr_win_id, sizeof ( Window ) )
&& type == XA_WINDOW && count > 0 ) ) {
curr_win_id = 0;
if ( sw->private_data == NULL ) {
SwitcherModePrivateData *pd = g_malloc0 ( sizeof ( *pd ) );
sw->private_data = (void *) pd;
pd->init = FALSE;
}
}
if ( window_get_prop ( display, root, netatoms[_NET_CLIENT_LIST_STACKING],
&type, &nwins, wins, 100 * sizeof ( Window ) )
&& type == XA_WINDOW ) {
char pattern[50];
int i;
unsigned int classfield = 0;
unsigned long desktops = 0;
// windows we actually display. May be slightly different to _NET_CLIENT_LIST_STACKING
// if we happen to have a window destroyed while we're working...
winlist *ids = winlist_new ();
static char ** window_mode_get_data ( unsigned int *length, Switcher *sw )
{
SwitcherModePrivateData *pd = (SwitcherModePrivateData *) sw->private_data;
if ( !pd->init ) {
Screen *screen = DefaultScreenOfDisplay ( display );
Window root = RootWindow ( display, XScreenNumberOfScreen ( screen ) );
// find window list
Atom type;
int nwins;
Window wins[100];
int count = 0;
Window curr_win_id = 0;
// Create cache
x11_cache_create ();
// Check for i3
pd->config_i3_mode = i3_support_initialize ( display );
// calc widths of fields
for ( i = nwins - 1; i > -1; i-- ) {
client *c;
if ( ( c = window_client ( display, wins[i] ) )
&& !c->xattr.override_redirect
&& !client_has_state ( c, netatoms[_NET_WM_STATE_SKIP_PAGER] )
&& !client_has_state ( c, netatoms[_NET_WM_STATE_SKIP_TASKBAR] ) ) {
classfield = MAX ( classfield, strlen ( c->class ) );
// In i3 mode, skip the i3bar completely.
if ( config_i3_mode && strstr ( c->class, "i3bar" ) != NULL ) {
continue;
}
if ( c->window == curr_win_id ) {
c->active = TRUE;
}
winlist_append ( ids, c->window, NULL );
}
// Get the active window so we can highlight this.
if ( !( window_get_prop ( display, root, netatoms[_NET_ACTIVE_WINDOW], &type,
&count, &curr_win_id, sizeof ( Window ) )
&& type == XA_WINDOW && count > 0 ) ) {
curr_win_id = 0;
}
// Create pattern for printing the line.
if ( !window_get_cardinal_prop ( display, root, netatoms[_NET_NUMBER_OF_DESKTOPS], &desktops, 1 ) ) {
desktops = 1;
}
if ( config_i3_mode ) {
sprintf ( pattern, "%%-%ds %%s", MAX ( 5, classfield ) );
}
else{
sprintf ( pattern, "%%-%ds %%-%ds %%s", desktops < 10 ? 1 : 2, MAX ( 5, classfield ) );
}
char **list = g_malloc0_n ( ( ids->len + 1 ), sizeof ( char* ) );
unsigned int lines = 0;
if ( window_get_prop ( display, root, netatoms[_NET_CLIENT_LIST_STACKING],
&type, &nwins, wins, 100 * sizeof ( Window ) )
&& type == XA_WINDOW ) {
char pattern[50];
int i;
unsigned int classfield = 0;
unsigned long desktops = 0;
// windows we actually display. May be slightly different to _NET_CLIENT_LIST_STACKING
// if we happen to have a window destroyed while we're working...
pd->ids = winlist_new ();
// build the actual list
for ( i = 0; i < ( ids->len ); i++ ) {
Window w = ids->array[i];
client *c;
if ( ( c = window_client ( display, w ) ) ) {
// final line format
unsigned long wmdesktop;
char desktop[5];
desktop[0] = 0;
char *line = g_malloc ( strlen ( c->title ) + strlen ( c->class ) + classfield + 50 );
if ( !config_i3_mode ) {
// find client's desktop. This is zero-based, so we adjust by since most
// normal people don't think like this :-)
if ( !window_get_cardinal_prop ( display, c->window, netatoms[_NET_WM_DESKTOP], &wmdesktop, 1 ) ) {
wmdesktop = 0xFFFFFFFF;
// calc widths of fields
for ( i = nwins - 1; i > -1; i-- ) {
client *c;
if ( ( c = window_client ( display, wins[i] ) )
&& !c->xattr.override_redirect
&& !client_has_state ( c, netatoms[_NET_WM_STATE_SKIP_PAGER] )
&& !client_has_state ( c, netatoms[_NET_WM_STATE_SKIP_TASKBAR] ) ) {
classfield = MAX ( classfield, strlen ( c->class ) );
// In i3 mode, skip the i3bar completely.
if ( pd->config_i3_mode && strstr ( c->class, "i3bar" ) != NULL ) {
continue;
}
if ( wmdesktop < 0xFFFFFFFF ) {
sprintf ( desktop, "%d", (int) wmdesktop + 1 );
if ( c->window == curr_win_id ) {
c->active = TRUE;
}
sprintf ( line, pattern, desktop, c->class, c->title );
winlist_append ( pd->ids, c->window, NULL );
}
else{
sprintf ( line, pattern, c->class, c->title );
}
list[lines++] = line;
}
}
Time time;
int selected_line = 0;
MenuReturn mretv = menu ( list, lines, input, "window:", &time, NULL,
window_match, ids, &selected_line, config.levenshtein_sort );
if ( mretv == MENU_NEXT ) {
retv = NEXT_DIALOG;
}
else if ( mretv == MENU_PREVIOUS ) {
retv = PREVIOUS_DIALOG;
}
else if ( mretv == MENU_QUICK_SWITCH ) {
retv = selected_line;
}
else if ( ( mretv == MENU_OK || mretv == MENU_CUSTOM_INPUT ) && list[selected_line] ) {
if ( config_i3_mode ) {
// Hack for i3.
i3_support_focus_window ( ids->array[selected_line] );
// Create pattern for printing the line.
if ( !window_get_cardinal_prop ( display, root, netatoms[_NET_NUMBER_OF_DESKTOPS], &desktops, 1 ) ) {
desktops = 1;
}
if ( pd->config_i3_mode ) {
sprintf ( pattern, "%%-%ds %%s", MAX ( 5, classfield ) );
}
else{
// Change to the desktop of the selected window/client.
// TODO: get rid of strtol
window_send_message ( display, root, root, netatoms[_NET_CURRENT_DESKTOP],
strtol ( list[selected_line], NULL, 10 ) - 1,
SubstructureNotifyMask | SubstructureRedirectMask, time );
XSync ( display, False );
sprintf ( pattern, "%%-%ds %%-%ds %%s", desktops < 10 ? 1 : 2, MAX ( 5, classfield ) );
}
pd->cmd_list = g_malloc0_n ( ( pd->ids->len + 1 ), sizeof ( char* ) );
window_send_message ( display, root, ids->array[selected_line],
netatoms[_NET_ACTIVE_WINDOW], 2, // 2 = pager
SubstructureNotifyMask | SubstructureRedirectMask, time );
// build the actual list
for ( i = 0; i < ( pd->ids->len ); i++ ) {
Window w = pd->ids->array[i];
client *c;
if ( ( c = window_client ( display, w ) ) ) {
// final line format
unsigned long wmdesktop;
char desktop[5];
desktop[0] = 0;
char *line = g_malloc ( strlen ( c->title ) + strlen ( c->class ) + classfield + 50 );
if ( !pd->config_i3_mode ) {
// find client's desktop. This is zero-based, so we adjust by since most
// normal people don't think like this :-)
if ( !window_get_cardinal_prop ( display, c->window, netatoms[_NET_WM_DESKTOP], &wmdesktop, 1 ) ) {
wmdesktop = 0xFFFFFFFF;
}
if ( wmdesktop < 0xFFFFFFFF ) {
sprintf ( desktop, "%d", (int) wmdesktop + 1 );
}
sprintf ( line, pattern, desktop, c->class, c->title );
}
else{
sprintf ( line, pattern, c->class, c->title );
}
pd->cmd_list[pd->cmd_list_length++] = line;
}
}
}
g_strfreev ( list );
winlist_free ( ids );
pd->init = TRUE;
}
*length = pd->cmd_list_length;
return pd->cmd_list;
}
static SwitcherMode window_mode_result ( int mretv, G_GNUC_UNUSED char **input, unsigned int selected_line, Switcher *sw )
{
SwitcherModePrivateData *rmpd = (SwitcherModePrivateData *) sw->private_data;
SwitcherMode retv = MODE_EXIT;
if ( mretv & MENU_NEXT ) {
retv = NEXT_DIALOG;
}
else if ( mretv & MENU_PREVIOUS ) {
retv = PREVIOUS_DIALOG;
}
else if ( mretv == MENU_QUICK_SWITCH ) {
retv = selected_line;
}
else if ( ( mretv & ( MENU_OK | MENU_CUSTOM_INPUT ) ) && rmpd->cmd_list[selected_line] ) {
if ( rmpd->config_i3_mode ) {
// Hack for i3.
i3_support_focus_window ( rmpd->ids->array[selected_line] );
}
else{
Screen *screen = DefaultScreenOfDisplay ( display );
Window root = RootWindow ( display, XScreenNumberOfScreen ( screen ) );
// Change to the desktop of the selected window/client.
// TODO: get rid of strtol
window_send_message ( display, root, root, netatoms[_NET_CURRENT_DESKTOP],
strtol ( rmpd->cmd_list[selected_line], NULL, 10 ) - 1,
SubstructureNotifyMask | SubstructureRedirectMask, 0 );
XSync ( display, False );
i3_support_free_internals ();
x11_cache_free ();
window_send_message ( display, root, rmpd->ids->array[selected_line],
netatoms[_NET_ACTIVE_WINDOW], 2, // 2 = pager
SubstructureNotifyMask | SubstructureRedirectMask, 0 );
}
}
return retv;
}
static void window_mode_destroy ( Switcher *sw )
{
SwitcherModePrivateData *rmpd = (SwitcherModePrivateData *) sw->private_data;
if ( rmpd != NULL ) {
g_strfreev ( rmpd->cmd_list );
winlist_free ( rmpd->ids );
i3_support_free_internals ();
x11_cache_free ();
g_free ( rmpd );
sw->private_data = NULL;
}
}
Switcher window_mode =
{
.name = "window",
.tb = NULL,
.keycfg = NULL,
.keystr = NULL,
.modmask = AnyModifier,
.init = window_mode_init,
.get_data = window_mode_get_data,
.result = window_mode_result,
.destroy = window_mode_destroy,
.token_match = window_match,
.private_data = NULL,
.free = NULL
};

View File

@ -318,7 +318,7 @@ int find_arg_char ( const char * const key, char *val )
*/
int token_match ( char **tokens, const char *input, int case_sensitive,
__attribute__( ( unused ) ) int index,
__attribute__( ( unused ) ) void *data )
__attribute__( ( unused ) ) Switcher *data )
{
int match = 1;
char *compk = token_collate_key ( input, case_sensitive );
@ -333,7 +333,7 @@ int token_match ( char **tokens, const char *input, int case_sensitive,
return match;
}
int execute_generator ( char * cmd )
int execute_generator ( const char * cmd )
{
char **args = NULL;
int argv = 0;

View File

@ -58,6 +58,10 @@
#define LINE_MARGIN 3
// TEMP
SwitcherMode switcher_run ( char **input, Switcher *sw );
typedef enum _MainLoopEvent
{
ML_XEVENT,
@ -73,31 +77,9 @@ char *display_str = NULL;
const char *netatom_names[] = { EWMH_ATOMS ( ATOM_CHAR ) };
Atom netatoms[NUM_NETATOMS];
/**
* Structure defining a switcher.
* It consists of a name, callback and if enabled
* a textbox for the sidebar-mode.
*/
typedef struct _Switcher
{
// Name (max 31 char long)
char name[32];
// Switcher callback.
switcher_callback cb;
// Callback data.
void *cb_data;
switcher_callback_free_data cb_data_free;
// Textbox used in the sidebar-mode.
textbox *tb;
// Keybindings (keysym and modmask)
char * keycfg;
char * keystr;
KeySym keysym;
unsigned int modmask;
} Switcher;
// Array of switchers.
Switcher *switchers = NULL;
Switcher **switchers = NULL;
// Number of switchers.
unsigned int num_switchers = 0;
// Current selected switcher.
@ -113,7 +95,7 @@ unsigned int curr_switcher = 0;
static int switcher_get ( const char *name )
{
for ( unsigned int i = 0; i < num_switchers; i++ ) {
if ( strcmp ( switchers[i].name, name ) == 0 ) {
if ( strcmp ( switchers[i]->name, name ) == 0 ) {
return i;
}
}
@ -561,9 +543,9 @@ static int locate_switcher ( KeySym key, unsigned int modstate )
// ignore annoying modifiers
unsigned int modstate_filtered = modstate & ~( LockMask | NumlockMask );
for ( unsigned int i = 0; i < num_switchers; i++ ) {
if ( switchers[i].keystr != NULL ) {
if ( ( modstate_filtered == switchers[i].modmask ) &&
switchers[i].keysym == key ) {
if ( switchers[i]->keystr != NULL ) {
if ( ( modstate_filtered == switchers[i]->modmask ) &&
switchers[i]->keysym == key ) {
return i;
}
}
@ -708,7 +690,7 @@ static void menu_mouse_navigation ( MenuState *state, XButtonEvent *xbe )
}
else {
for ( unsigned int i = 0; config.sidebar_mode == TRUE && i < num_switchers; i++ ) {
if ( switchers[i].tb->window == ( xbe->window ) ) {
if ( switchers[i]->tb->window == ( xbe->window ) ) {
*( state->selected_line ) = i;
state->retv = MENU_QUICK_SWITCH;
state->quit = TRUE;
@ -905,7 +887,7 @@ static void menu_update ( MenuState *state )
state->w - ( ( config.padding ) ) - 1,
state->h - state->line_height - ( config.padding ) - LINE_MARGIN );
for ( unsigned int j = 0; j < num_switchers; j++ ) {
textbox_draw ( switchers[j].tb );
textbox_draw ( switchers[j]->tb );
}
}
@ -941,8 +923,9 @@ static void menu_paste ( MenuState *state, XSelectionEvent *xse )
}
MenuReturn menu ( char **lines, unsigned int num_lines, char **input, char *prompt, Time *time,
int *shift, menu_match_cb mmc, void *mmc_data, int *selected_line, int sorting )
menu_match_cb mmc, void *mmc_data, int *selected_line, int sorting )
{
int shift = FALSE;
MenuState state = {
.selected_line = selected_line,
.retv = MENU_CANCEL,
@ -1078,11 +1061,11 @@ MenuReturn menu ( char **lines, unsigned int num_lines, char **input, char *prom
if ( config.sidebar_mode == TRUE ) {
int width = ( state.w - ( 2 * ( config.padding ) + ( num_switchers - 1 ) * LINE_MARGIN ) ) / num_switchers;
for ( unsigned int j = 0; j < num_switchers; j++ ) {
switchers[j].tb = textbox_create ( main_window, &vinfo, map, TB_CENTER,
config.padding + j * ( width + LINE_MARGIN ),
state.h - state.line_height - config.padding,
width, state.line_height, ( j == curr_switcher ) ? HIGHLIGHT : NORMAL, switchers[j].name );
textbox_show ( switchers[j].tb );
switchers[j]->tb = textbox_create ( main_window, &vinfo, map, TB_CENTER,
config.padding + j * ( width + LINE_MARGIN ),
state.h - state.line_height - config.padding,
width, state.line_height, ( j == curr_switcher ) ? HIGHLIGHT : NORMAL, switchers[j]->name );
textbox_show ( switchers[j]->tb );
}
}
@ -1243,9 +1226,7 @@ MenuReturn menu ( char **lines, unsigned int num_lines, char **input, char *prom
int rc = textbox_keypress ( state.text, &ev );
// Row is accepted.
if ( rc < 0 ) {
if ( shift != NULL ) {
( *shift ) = ( ( ev.xkey.state & ShiftMask ) == ShiftMask );
}
shift = ( ( ev.xkey.state & ShiftMask ) == ShiftMask );
// If a valid item is selected, return that..
if ( state.selected < state.filtered_lines && state.filtered[state.selected] != NULL ) {
@ -1290,12 +1271,16 @@ MenuReturn menu ( char **lines, unsigned int num_lines, char **input, char *prom
int retv = state.retv;
menu_free_state ( &state );
if ( shift ) {
retv |= MENU_SHIFT;
}
// Free the switcher boxes.
// When state is free'ed we should no longer need these.
if ( config.sidebar_mode == TRUE ) {
for ( unsigned int j = 0; j < num_switchers; j++ ) {
textbox_free ( switchers[j].tb );
switchers[j].tb = NULL;
textbox_free ( switchers[j]->tb );
switchers[j]->tb = NULL;
}
}
@ -1449,36 +1434,40 @@ static void run_switcher ( int do_fork, SwitcherMode mode )
config.menu_hlbg,
config.menu_hlfg );
// Otherwise check if requested mode is enabled.
if ( switchers[mode].cb != NULL ) {
char *input = NULL;
do {
SwitcherMode retv;
char *input = NULL;
for ( unsigned int i = 0; i < num_switchers; i++ ) {
switchers[i]->init ( switchers[i] );
}
do {
SwitcherMode retv;
curr_switcher = mode;
retv = switchers[mode].cb ( &input, switchers[mode].cb_data );
// Find next enabled
if ( retv == NEXT_DIALOG ) {
mode = ( mode + 1 ) % num_switchers;
}
else if ( retv == PREVIOUS_DIALOG ) {
if ( mode == 0 ) {
mode = num_switchers - 1;
}
else {
mode = ( mode - 1 ) % num_switchers;
}
}
else if ( retv == RELOAD_DIALOG ) {
// do nothing.
}
else if ( retv < MODE_EXIT ) {
mode = ( retv ) % num_switchers;
curr_switcher = mode;
retv = switcher_run ( &input, switchers[mode] );
// Find next enabled
if ( retv == NEXT_DIALOG ) {
mode = ( mode + 1 ) % num_switchers;
}
else if ( retv == PREVIOUS_DIALOG ) {
if ( mode == 0 ) {
mode = num_switchers - 1;
}
else {
mode = retv;
mode = ( mode - 1 ) % num_switchers;
}
} while ( mode != MODE_EXIT );
g_free ( input );
}
else if ( retv == RELOAD_DIALOG ) {
// do nothing.
}
else if ( retv < MODE_EXIT ) {
mode = ( retv ) % num_switchers;
}
else {
mode = retv;
}
} while ( mode != MODE_EXIT );
g_free ( input );
for ( unsigned int i = 0; i < num_switchers; i++ ) {
switchers[i]->destroy ( switchers[i] );
}
// Cleanup font setup.
@ -1546,14 +1535,14 @@ static void cleanup ()
// Cleaning up memory allocated by the Xresources file.
config_xresource_free ();
for ( unsigned int i = 0; i < num_switchers; i++ ) {
// only used for script dialog.
if ( switchers[i].cb_data_free != NULL ) {
switchers[i].cb_data_free ( switchers[i].cb_data );
}
// Switcher keystr is free'ed when needed by config system.
if ( switchers[i].keycfg != NULL ) {
g_free ( switchers[i].keycfg );
switchers[i].keycfg = NULL;
if ( switchers[i]->keycfg != NULL ) {
g_free ( switchers[i]->keycfg );
switchers[i]->keycfg = NULL;
}
// only used for script dialog.
if ( switchers[i]->free != NULL ) {
switchers[i]->free ( switchers[i] );
}
}
g_free ( switchers );
@ -1576,42 +1565,29 @@ static void setup_switchers ( void )
token != NULL;
token = strtok_r ( NULL, ",", &savept ) ) {
// Resize and add entry.
switchers = (Switcher *) g_realloc ( switchers,
sizeof ( Switcher ) * ( num_switchers + 1 ) );
switchers[num_switchers].cb_data = NULL;
switchers[num_switchers].cb_data_free = NULL;
switchers[num_switchers].keystr = NULL;
switchers[num_switchers].keycfg = NULL;
switchers[num_switchers].keysym = None;
switchers[num_switchers].modmask = AnyModifier;
switchers = (Switcher * *) g_realloc ( switchers,
sizeof ( Switcher* ) * ( num_switchers + 1 ) );
// Window switcher.
if ( strcasecmp ( token, "window" ) == 0 ) {
g_strlcpy ( switchers[num_switchers].name, "window", 32 );
switchers[num_switchers].cb = run_switcher_window;
switchers[num_switchers] = &window_mode;
num_switchers++;
}
// SSh dialog
else if ( strcasecmp ( token, "ssh" ) == 0 ) {
g_strlcpy ( switchers[num_switchers].name, "ssh", 32 );
switchers[num_switchers].cb = ssh_switcher_dialog;
switchers[num_switchers] = &ssh_mode;
num_switchers++;
}
// Run dialog
else if ( strcasecmp ( token, "run" ) == 0 ) {
g_strlcpy ( switchers[num_switchers].name, "run", 32 );
switchers[num_switchers].cb = run_switcher_dialog;
switchers[num_switchers] = &run_mode;
num_switchers++;
}
else {
// If not build in, use custom switchers.
ScriptOptions *sw = script_switcher_parse_setup ( token );
Switcher *sw = script_switcher_parse_setup ( token );
if ( sw != NULL ) {
// Resize and add entry.
g_strlcpy ( switchers[num_switchers].name, sw->name, 32 );
switchers[num_switchers].cb = script_switcher_dialog;
switchers[num_switchers].cb_data = sw;
switchers[num_switchers].cb_data_free = script_switcher_free_options;
switchers[num_switchers] = sw;
num_switchers++;
}
else{
@ -1627,10 +1603,10 @@ static void setup_switchers ( void )
// We cannot do this in main loop, as we create pointer to string,
// and re-alloc moves that pointer.
for ( unsigned int i = 0; i < num_switchers; i++ ) {
switchers[i].keycfg = g_strdup_printf ( "key-%s", switchers[i].name );
switchers[i]->keycfg = g_strdup_printf ( "key-%s", switchers[i]->name );
config_parser_add_option ( xrm_String,
switchers[i].keycfg,
(void * *) &( switchers[i].keystr ) );
switchers[i]->keycfg,
(void * *) &( switchers[i]->keystr ) );
}
}
@ -1809,9 +1785,9 @@ int main ( int argc, char *argv[] )
else{
// Daemon mode, Listen to key presses..
for ( unsigned int i = 0; i < num_switchers; i++ ) {
if ( switchers[i].keystr != NULL ) {
x11_parse_key ( switchers[i].keystr, &( switchers[i].modmask ), &( switchers[i].keysym ) );
x11_grab_key ( display, switchers[i].modmask, switchers[i].keysym );
if ( switchers[i]->keystr != NULL ) {
x11_parse_key ( switchers[i]->keystr, &( switchers[i]->modmask ), &( switchers[i]->keysym ) );
x11_grab_key ( display, switchers[i]->modmask, switchers[i]->keysym );
}
}
// Setup handler for sighup (reload config)
@ -1843,3 +1819,33 @@ int main ( int argc, char *argv[] )
return EXIT_SUCCESS;
}
SwitcherMode switcher_run ( char **input, Switcher *sw )
{
int selected_line = 0;
SwitcherMode retv = MODE_EXIT;
unsigned int cmd_list_length = 0;
char **cmd_list = NULL;
cmd_list = sw->get_data ( &cmd_list_length, sw );
if ( cmd_list == NULL ) {
cmd_list = g_malloc_n ( 2, sizeof ( char * ) );
cmd_list[0] = g_strdup ( "No applications found" );
cmd_list[1] = NULL;
}
char *prompt = g_strdup_printf ( "%s:", sw->name );
int mretv = menu ( cmd_list, cmd_list_length, input, prompt,
NULL, sw->token_match, sw, &selected_line,
config.levenshtein_sort );
retv = sw->result ( mretv, input, selected_line, sw );
g_free ( prompt );
return retv;
}