Add flexible configuration for launching

* You can now specify links: {terminal} -e bash -e "{ssh-client} {host}"
    * Add test for this code.
This commit is contained in:
Dave Davenport 2014-09-03 13:07:26 +02:00
parent 5f33d506b9
commit 0462811800
14 changed files with 274 additions and 129 deletions

View File

@ -31,6 +31,7 @@ rofi_SOURCES=\
source/script-dialog.c\
source/history.c\
config/config.c\
source/helper.c\
include/rofi.h\
include/run-dialog.h\
include/ssh-dialog.h\
@ -38,7 +39,8 @@ rofi_SOURCES=\
include/script-dialog.h\
include/xrmoptions.h\
include/history.h\
include/textbox.h
include/textbox.h\
include/helper.h
##
# Manpage

View File

@ -35,15 +35,15 @@ Settings config = {
// Set the default window opacity.
// This option only works when running a composite manager.
// -o
.window_opacity = 100,
.window_opacity = 100,
// Border width around the window.
.menu_bw = 1,
.menu_bw = 1,
// The width of the switcher. (0100 in % > 100 in pixels)
.menu_width = 50,
.menu_width = 50,
// Maximum number of options to show.
.menu_lines = 15,
.menu_lines = 15,
// Number of columns
.menu_columns = 1,
.menu_columns = 1,
// Font
.menu_font = "mono12",
// Foreground color
@ -58,6 +58,12 @@ Settings config = {
.menu_bc = "black",
// Terminal to use. (for ssh and open in terminal)
.terminal_emulator = "x-terminal-emulator",
.ssh_client = "ssh",
// Command when executing ssh.
.ssh_command = "{terminal} -e {ssh-client} {host}",
// Command when running
.run_command = "{cmd}",
.run_shell_command = "{terminal} -e {cmd}",
// Key binding
.window_key = "F12",
.run_key = "mod1+F2",
@ -67,10 +73,9 @@ Settings config = {
// Mode of window, list (Vertical) or dmenu like (Horizontal)
.hmode = FALSE,
// Padding of the window.
.padding = 5,
.ssh_set_title = TRUE,
.y_offset = 0,
.x_offset = 0,
.padding = 5,
.y_offset = 0,
.x_offset = 0,
.fixed_num_lines = FALSE,
.disable_history = FALSE,
.levenshtein_sort = FALSE

View File

@ -10,8 +10,9 @@ rofi - A window switcher, run dialog and dmenu replacement
[ -bg *color* ] [ -hlfg *color* ] [ -hlbg *color* ] [ -key *combo* ] [ -dkey *comdo* ] [ -rkey *comdo* ]
[ -terminal *terminal* ] [ -location *position* ] [ -hmode ] [ -fixed-num-lines ] [ -padding *padding* ]
[ -opacity *opacity%* ] [ -display *display* ] [ -bc *color* ] [ -bw *width* ] [ -dmenu [ -p *prompt* ] ]
[ -ssh-set-title *true|false* ] [ -now ] [ -rnow ] [ -snow ] [ -version ] [ -help] [ -dump-xresources ]
[ -disable-history ] [ -levenshtein-sort ] [ -show *mode* ] [ -switcher *mode1,mode2* ] [ -e *message*]
[ -ssh-client *client* ] [ -ssh-command *command* ] [ -now ] [ -rnow ] [ -snow ] [ -version ]
[ -help] [ -dump-xresources ] [ -disable-history ] [ -levenshtein-sort ] [ -show *mode* ] [ -switcher
*mode1,mode2* ] [ -e *message*]
## DESCRIPTION
@ -220,6 +221,26 @@ The default key combinations are:
Not all terminals support this.
Default value is true.
*This command has been deprecated for the ssh-command string*
`-ssh-command` *cmd*
Set the command to execute when starting a ssh session.
`-run-command` *cmd*
Set the command to execute when running an application.
See *PATTERN*.
`-run-shell-command` *cmd*
Set the command to execute when running an application in a shell.
See *PATTERN*.
`-ssh-client` *client*
Override the used ssh client. Default is `ssh`.
`-disable-history`
Disable history
@ -260,6 +281,15 @@ The default key combinations are:
Popup a message dialog (used internally for showing errors) with *message*.
## Pattern
To launch commands (e.g. when using the ssh dialog) the user can enter the used commandline,
the following keys can be used that will be replaced at runtime:
* `{host}`: The host to connect to.
* `{terminal}`: The configured terminal (See -terminal-emulator)
* `{ssh-client}`: The configured ssh client (See -ssh-client)
* `{cmd}`: The command to execute.
## Keybindings

6
include/helper.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef __HELPER_H__
#define __HELPER_H__
int helper_parse_setup ( char * string, char ***output, int *lenght, ... );
#endif // __HELPER_H__

View File

@ -106,6 +106,13 @@ typedef struct _Settings
char * menu_bc;
// Behavior
char * terminal_emulator;
char * ssh_client;
// Command to execute when ssh session is selected.
char * ssh_command;
// Command for executing an application.
char * run_command;
char * run_shell_command;
// Key bindings
char * window_key;
@ -117,7 +124,6 @@ typedef struct _Settings
int y_offset;
int x_offset;
unsigned int ssh_set_title;
unsigned int fixed_num_lines;
unsigned int disable_history;

73
source/helper.c Normal file
View File

@ -0,0 +1,73 @@
#include <glib.h>
#include <rofi.h>
#include <helper.h>
#include <config.h>
/**
* Replace the entries
*/
static gboolean helper_eval_cb ( const GMatchInfo *info,
GString *res,
gpointer data )
{
gchar *match;
gchar *r;
match = g_match_info_fetch ( info, 0 );
r = g_hash_table_lookup ( (GHashTable *) data, match );
if ( r != NULL ) {
g_string_append ( res, r );
g_free ( match );
}
return FALSE;
}
int helper_parse_setup ( char * string, char ***output, int *length, ... )
{
GError *error = NULL;
GHashTable *h;
h = g_hash_table_new ( g_str_hash, g_str_equal );
g_hash_table_insert ( h, "{terminal}", config.terminal_emulator );
g_hash_table_insert ( h, "{ssh-client}", config.ssh_client );
// Add list.
va_list ap;
va_start ( ap, length );
while ( 1 ) {
char * key = va_arg ( ap, char * );
if ( key == NULL ) {
break;
}
char *value = va_arg ( ap, char * );
if ( value == NULL ) {
break;
}
g_hash_table_insert ( h, key, value );
}
va_end ( ap );
// Replace hits within {-\w+}.
GRegex *reg = g_regex_new ( "{[-\\w]+}", 0, 0, NULL );
char *res = g_regex_replace_eval ( reg,
string, -1,
0, 0, helper_eval_cb, h,
NULL );
g_regex_unref ( reg );
g_hash_table_destroy ( h );
if ( g_shell_parse_argv ( res, length, output, &error ) ) {
g_free ( res );
return TRUE;
}
g_free ( res );
if ( error ) {
char *msg = g_strdup_printf ( "Failed to parse: '%s'\nError: '%s'", string,
error->message );
#ifdef error_dialog
error_dialog ( msg );
#endif
g_free ( msg );
// print error.
g_error_free ( error );
}
return FALSE;
}

View File

@ -1070,13 +1070,13 @@ static void menu_calculate_window_and_element_width ( MenuState *state, workarea
state->w -= config.menu_bw * 2;
}
if(state->columns > 0 ) {
if ( state->columns > 0 ) {
state->element_width = state->w - ( 2 * ( config.padding ) );
// Divide by the # columns
state->element_width = ( state->element_width - ( state->columns - 1 ) * LINE_MARGIN ) / state->columns;
if ( config.hmode == TRUE ) {
state->element_width = ( state->w - ( 2 * ( config.padding ) ) - state->max_elements * LINE_MARGIN ) / (
state->max_elements + 1 );
state->max_elements + 1 );
}
}
}
@ -1666,21 +1666,21 @@ MenuReturn menu ( char **lines, unsigned int num_lines, char **input, char *prom
void error_dialog ( char *msg )
{
MenuState state = {
MenuState state = {
.selected_line = NULL,
.retv = MENU_CANCEL,
.prev_key = 0,
.last_button_press = 0,
.last_offset = 0,
.num_lines = 0,
.prev_key = 0,
.last_button_press = 0,
.last_offset = 0,
.num_lines = 0,
.distance = NULL,
.init = FALSE,
.quit = FALSE,
.filtered_lines = 0,
.columns = 0,
.update =TRUE,
.filtered_lines = 0,
.columns = 0,
.update = TRUE,
};
workarea mon;
workarea mon;
// Get active monitor size.
monitor_active ( &mon );
// main window isn't explicitly destroyed in case we switch modes. Reusing it prevents flicker
@ -1691,7 +1691,7 @@ void error_dialog ( char *msg )
menu_calculate_window_and_element_width ( &state, &mon );
state.max_elements = 0;
state.max_elements = 0;
state.text = textbox_create ( main_window, TB_AUTOHEIGHT,
( config.padding ),
@ -1704,7 +1704,7 @@ void error_dialog ( char *msg )
int line_height = textbox_get_height ( state.text );
// resize window vertically to suit
state.h = line_height + ( config.padding ) * 2;
state.h = line_height + ( config.padding ) * 2;
// Move the window to the correct x,y position.
calculate_window_position ( &state, &mon );
@ -1714,8 +1714,7 @@ void error_dialog ( char *msg )
XMapRaised ( display, main_window );
if ( take_keyboard ( main_window ) ) {
while(!state.quit) {
while ( !state.quit ) {
// Update if requested.
if ( state.update ) {
textbox_draw ( state.text );
@ -2154,16 +2153,10 @@ static void parse_cmd_options ( int argc, char ** argv )
config.hmode = TRUE;
}
if ( find_arg ( argc, argv, "-ssh-set-title" ) >= 0 ) {
char *value;
find_arg_str ( argc, argv, "-ssh-set-title", &value );
if ( strcasecmp ( value, "true" ) == 0 ) {
config.ssh_set_title = TRUE;
}
else{
config.ssh_set_title = FALSE;
}
}
find_arg_str ( argc, argv, "-ssh-client", &( config.ssh_client ) );
find_arg_str ( argc, argv, "-ssh-command", &( config.ssh_command ) );
find_arg_str ( argc, argv, "-run-command", &( config.run_command ) );
find_arg_str ( argc, argv, "-run-shell-command", &( config.run_shell_command ) );
// Keybindings
find_arg_str ( argc, argv, "-key", &( config.window_key ) );
@ -2360,15 +2353,15 @@ int main ( int argc, char *argv[] )
display_get_i3_path ( display );
#endif
char *msg = NULL;
if ( find_arg_str ( argc, argv, "-e", &(msg) ) ) {
char *msg = NULL;
if ( find_arg_str ( argc, argv, "-e", &( msg ) ) ) {
textbox_setup (
config.menu_bg, config.menu_fg,
config.menu_hlbg,
config.menu_hlfg );
error_dialog(msg);
config.menu_bg, config.menu_fg,
config.menu_hlbg,
config.menu_hlfg );
error_dialog ( msg );
textbox_cleanup ();
exit (EXIT_SUCCESS);
exit ( EXIT_SUCCESS );
}

View File

@ -39,6 +39,7 @@
#include <errno.h>
#include "rofi.h"
#include "helper.h"
#include "history.h"
#include "run-dialog.h"
#ifdef TIMING
@ -49,29 +50,25 @@
static inline void execsh ( const char *cmd, int run_in_term )
{
char **args = g_malloc_n ( 6, sizeof ( char* ) );
int i = 0;
char **args = NULL;
int argc = 0;
if ( run_in_term ) {
args[i++] = g_strdup ( config.terminal_emulator );
args[i++] = g_strdup ( "-e" );
helper_parse_setup ( config.run_shell_command, &args, &argc, "{cmd}", cmd, NULL );
}
else {
helper_parse_setup ( config.run_command, &args, &argc, "{cmd}", cmd, NULL );
}
args[i++] = g_strdup ( "sh" );
args[i++] = g_strdup ( "-c" );
args[i++] = g_strdup ( cmd );
args[i++] = NULL;
GError *error = NULL;
g_spawn_async ( NULL, args, NULL,
G_SPAWN_SEARCH_PATH,
NULL, NULL, NULL, &error );
if( error != NULL )
{
char *msg = g_strdup_printf("Failed to execute: '%s'\nError: '%s'", cmd,
error->message);
error_dialog(msg);
g_free(msg);
if ( error != NULL ) {
char *msg = g_strdup_printf ( "Failed to execute: '%s'\nError: '%s'", cmd,
error->message );
error_dialog ( msg );
g_free ( msg );
// print error.
g_error_free(error);
g_error_free ( error );
}
// Free the args list.

View File

@ -36,18 +36,17 @@
#include <assert.h>
#include "rofi.h"
#include "script-dialog.h"
#include "helper.h"
pid_t execute_generator ( char * cmd )
{
char **args = g_malloc_n ( 4, sizeof ( char* ) );
args[0] = g_strdup ( "sh" );
args[1] = g_strdup ( "-c" );
args[2] = g_strdup ( cmd );
args[3] = NULL;
char **args = NULL;
int argv = 0;
helper_parse_setup ( config.run_command, &args, &argv, "{cmd}", cmd, NULL );
int fd = -1;
int fd = -1;
GError *error = NULL;
g_spawn_async_with_pipes ( NULL,
args,
@ -59,14 +58,13 @@ pid_t execute_generator ( char * cmd )
NULL, &fd, NULL,
&error );
if( error != NULL )
{
char *msg = g_strdup_printf("Failed to execute: '%s'\nError: '%s'", cmd,
error->message);
error_dialog(msg);
g_free(msg);
if ( error != NULL ) {
char *msg = g_strdup_printf ( "Failed to execute: '%s'\nError: '%s'", cmd,
error->message );
error_dialog ( msg );
g_free ( msg );
// print error.
g_error_free(error);
g_error_free ( error );
}
g_strfreev ( args );
return fd;

View File

@ -38,6 +38,7 @@
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <helper.h>
#include "rofi.h"
#include "history.h"
@ -53,31 +54,22 @@ static inline int execshssh ( const char *host )
/**
* I am not happy about this code, it causes 7 mallocs and frees
*/
char **args = g_malloc_n ( 7, sizeof ( char* ) );
int i = 0;
args[i++] = g_strdup ( config.terminal_emulator );
if ( config.ssh_set_title ) {
args[i++] = g_strdup ( "-T" );
args[i++] = g_strdup_printf ( "ssh %s", host );
}
args[i++] = g_strdup ( "-e" );
args[i++] = g_strdup ( "ssh" );
args[i++] = g_strdup ( host );
args[i++] = NULL;
char **args = NULL;
int argsv = 0;
helper_parse_setup ( config.ssh_command, &args, &argsv, "{host}", host, NULL );
GError *error = NULL;
g_spawn_async ( NULL, args, NULL,
G_SPAWN_SEARCH_PATH,
NULL, NULL, NULL, &error );
if( error != NULL )
{
char *msg = g_strdup_printf("Failed to execute: 'ssh %s'\nError: '%s'", host,
error->message);
error_dialog(msg);
g_free(msg);
if ( error != NULL ) {
char *msg = g_strdup_printf ( "Failed to execute: 'ssh %s'\nError: '%s'", host,
error->message );
error_dialog ( msg );
g_free ( msg );
// print error.
g_error_free(error);
g_error_free ( error );
}
// Free the args list.
g_strfreev ( args );
@ -154,7 +146,7 @@ static char ** get_ssh ( unsigned int *length )
;
}
for ( stop = start; !isspace(buffer[stop]) ; stop++ ) {
for ( stop = start; !isspace ( buffer[stop] ); stop++ ) {
// We do not want to show wildcard entries, as you cannot ssh to them.
if ( buffer[stop] == '?' || buffer[stop] == '*' ) {
stop = start;

View File

@ -321,10 +321,11 @@ void textbox_delete ( textbox *tb, int pos, int dlen )
// move everything after pos+dlen down
char *at = tb->text + pos;
// Move remainder + closing \0
memmove ( at, at + dlen, len - pos-dlen+1 );
if ( tb->cursor >= pos && tb->cursor < (pos+dlen) ) {
tb->cursor = pos;
} else if ( tb->cursor >= (pos+dlen) ) {
memmove ( at, at + dlen, len - pos - dlen + 1 );
if ( tb->cursor >= pos && tb->cursor < ( pos + dlen ) ) {
tb->cursor = pos;
}
else if ( tb->cursor >= ( pos + dlen ) ) {
tb->cursor -= dlen;
}
}

View File

@ -60,52 +60,55 @@ typedef struct
* Currently supports string and number.
*/
static XrmOption xrmOptions[] = {
{ xrm_String, "switchers", { .str = &config.switchers }, NULL },
{ xrm_Number, "opacity", { .num = &config.window_opacity }, NULL },
{ xrm_String, "switchers", { .str = &config.switchers }, NULL },
{ xrm_Number, "opacity", { .num = &config.window_opacity }, NULL },
{ xrm_SNumber, "width", { .snum = &config.menu_width }, NULL },
{ xrm_SNumber, "width", { .snum = &config.menu_width }, NULL },
{ xrm_Number, "lines", { .num = &config.menu_lines }, NULL },
{ xrm_Number, "columns", { .num = &config.menu_columns }, NULL },
{ xrm_Number, "lines", { .num = &config.menu_lines }, NULL },
{ xrm_Number, "columns", { .num = &config.menu_columns }, NULL },
{ xrm_String, "font", { .str = &config.menu_font }, NULL },
{ xrm_String, "font", { .str = &config.menu_font }, NULL },
/* Foreground color */
{ xrm_String, "foreground", { .str = &config.menu_fg }, NULL },
{ xrm_String, "fg", { .str = &config.menu_fg }, NULL },
{ xrm_String, "foreground", { .str = &config.menu_fg }, NULL },
{ xrm_String, "fg", { .str = &config.menu_fg }, NULL },
{ xrm_String, "background", { .str = &config.menu_bg }, NULL },
{ xrm_String, "bg", { .str = &config.menu_bg }, NULL },
{ xrm_String, "background", { .str = &config.menu_bg }, NULL },
{ xrm_String, "bg", { .str = &config.menu_bg }, NULL },
{ xrm_String, "highlightfg", { .str = &config.menu_hlfg }, NULL },
{ xrm_String, "hlfg", { .str = &config.menu_hlfg }, NULL },
{ xrm_String, "highlightfg", { .str = &config.menu_hlfg }, NULL },
{ xrm_String, "hlfg", { .str = &config.menu_hlfg }, NULL },
{ xrm_String, "highlightbg", { .str = &config.menu_hlbg }, NULL },
{ xrm_String, "hlbg", { .str = &config.menu_hlbg }, NULL },
{ xrm_String, "highlightbg", { .str = &config.menu_hlbg }, NULL },
{ xrm_String, "hlbg", { .str = &config.menu_hlbg }, NULL },
{ xrm_String, "bordercolor", { .str = &config.menu_bc }, NULL },
{ xrm_String, "bc", { .str = &config.menu_bc }, NULL },
{ xrm_String, "bordercolor", { .str = &config.menu_bc }, NULL },
{ xrm_String, "bc", { .str = &config.menu_bc }, NULL },
{ xrm_Number, "borderwidth", { .num = &config.menu_bw }, NULL },
{ xrm_Number, "bw", { .num = &config.menu_bw }, NULL },
{ xrm_Number, "borderwidth", { .num = &config.menu_bw }, NULL },
{ xrm_Number, "bw", { .num = &config.menu_bw }, NULL },
{ xrm_Number, "location", { .num = &config.location }, NULL },
{ xrm_Number, "location", { .num = &config.location }, NULL },
{ xrm_Number, "padding", { .num = &config.padding }, NULL },
{ xrm_SNumber, "yoffset", { .snum = &config.y_offset }, NULL },
{ xrm_SNumber, "xoffset", { .snum = &config.x_offset }, NULL },
{ xrm_Boolean, "fixed-num-lines", { .num = &config.fixed_num_lines }, NULL },
{ xrm_Boolean, "hmode", { .num = &config.hmode }, NULL },
{ xrm_Number, "padding", { .num = &config.padding }, NULL },
{ xrm_SNumber, "yoffset", { .snum = &config.y_offset }, NULL },
{ xrm_SNumber, "xoffset", { .snum = &config.x_offset }, NULL },
{ xrm_Boolean, "fixed-num-lines", { .num = &config.fixed_num_lines }, NULL },
{ xrm_Boolean, "hmode", { .num = &config.hmode }, NULL },
{ xrm_String, "terminal", { .str = &config.terminal_emulator }, NULL },
{ xrm_String, "terminal", { .str = &config.terminal_emulator }, NULL },
{ xrm_String, "ssh-client", { .str = &config.ssh_client }, NULL },
{ xrm_String, "ssh-command", { .str = &config.ssh_command }, NULL },
{ xrm_String, "run-command", { .str = &config.run_command }, NULL },
{ xrm_String, "run-shell-command", { .str = &config.run_shell_command }, NULL },
{ xrm_Boolean, "ssh-set-title", { .num = &config.ssh_set_title }, NULL },
{ xrm_Boolean, "disable-history", { .num = &config.disable_history }, NULL },
{ xrm_Boolean, "levenshtein-sort", { .num = &config.levenshtein_sort }, NULL },
{ xrm_Boolean, "disable-history", { .num = &config.disable_history }, NULL },
{ xrm_Boolean, "levenshtein-sort", { .num = &config.levenshtein_sort }, NULL },
/* Key bindings */
{ xrm_String, "key", { .str = &config.window_key }, NULL },
{ xrm_String, "rkey", { .str = &config.run_key }, NULL },
{ xrm_String, "skey", { .str = &config.ssh_key }, NULL },
{ xrm_String, "key", { .str = &config.window_key }, NULL },
{ xrm_String, "rkey", { .str = &config.run_key }, NULL },
{ xrm_String, "skey", { .str = &config.ssh_key }, NULL },
};

View File

@ -1,7 +1,7 @@
##
# Rofi the program
##
bin_PROGRAMS=rofi_test textbox_test
bin_PROGRAMS=rofi_test textbox_test helper_test
LIBS=\
@xft_LIBS@\
@ -33,9 +33,17 @@ textbox_test_SOURCES=\
../include/textbox.h\
textbox-test.c
helper_test_SOURCES=\
helper-test.c\
../config/config.c\
../include/rofi.h\
../source/helper.c\
../include/helper.h
.PHONY: test
test: ${bin_PROGRAMS}
./rofi_test
./helper_test
$(top_srcdir)/test/run_test.sh 123 $(top_builddir)/test/textbox_test $(top_builddir)
$(top_srcdir)/test/run_test.sh 200 $(top_srcdir)/test/run_errormsg_test.sh $(top_builddir)
$(top_srcdir)/test/run_test.sh 201 $(top_srcdir)/test/run_switchdialog_test.sh $(top_builddir)

31
test/helper-test.c Normal file
View File

@ -0,0 +1,31 @@
#include <assert.h>
#include <glib.h>
#include <stdio.h>
#include <helper.h>
#include <string.h>
static int test = 0;
#define TASSERT(a) {\
assert ( a );\
printf("Test %i passed (%s)\n", ++test, #a);\
}
int main ( int argc, char ** argv )
{
char **list = NULL;
int llength = 0;
char * test_str = "{host} {terminal} -e bash -c \"{ssh-client} {host}; echo '{terminal} {host}'\"";
helper_parse_setup( test_str, &list, &llength, "{host}", "chuck",
"{terminal}", "x-terminal-emulator", NULL);
TASSERT ( llength == 6 );
TASSERT ( strcmp(list[0], "chuck") == 0 );
TASSERT ( strcmp(list[1], "x-terminal-emulator") == 0 );
TASSERT ( strcmp(list[2], "-e") == 0 );
TASSERT ( strcmp(list[3], "bash") == 0 );
TASSERT ( strcmp(list[4], "-c") == 0 );
TASSERT ( strcmp(list[5], "ssh chuck; echo 'x-terminal-emulator chuck'") == 0 );
g_strfreev(list);
}