From acc46c4dd25ccf1d033d3b08896fa82fd6b04346 Mon Sep 17 00:00:00 2001 From: Qball Cow <qball@gmpclient.org> Date: Wed, 29 Jan 2014 13:45:03 +0100 Subject: [PATCH] Add a test json mode --- Makefile | 4 +- doc/mpc.json | 1 + include/json-dialog.h | 8 ++ include/simpleswitcher.h | 1 + source/json-dialog.c | 197 +++++++++++++++++++++++++++++++++++++++ source/simpleswitcher.c | 8 ++ 6 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 doc/mpc.json create mode 100644 include/json-dialog.h create mode 100644 source/json-dialog.c diff --git a/Makefile b/Makefile index 8356a08e..373325cf 100644 --- a/Makefile +++ b/Makefile @@ -51,8 +51,8 @@ ifeq (${PKG_CONFIG},${EMPTY}) $(error Failed to find pkg-config. Please install pkg-config) endif -CFLAGS+=$(shell ${PKG_CONFIG} --cflags x11 xinerama xft libxdg-basedir) -LDADD+=$(shell ${PKG_CONFIG} --libs x11 xinerama xft libxdg-basedir) +CFLAGS+=$(shell ${PKG_CONFIG} --cflags x11 xinerama xft libxdg-basedir json) +LDADD+=$(shell ${PKG_CONFIG} --libs x11 xinerama xft libxdg-basedir json) ifeq (${LDADD},${EMPTY}) $(error Failed to find the required dependencies: x11, xinerama, xft) diff --git a/doc/mpc.json b/doc/mpc.json new file mode 100644 index 00000000..322dce83 --- /dev/null +++ b/doc/mpc.json @@ -0,0 +1 @@ +{"prompt": "mpd ","run-in-terminal":false, "commands": { "play": "mpc play", "stop": "mpc stop", "start": "mpc start"}} diff --git a/include/json-dialog.h b/include/json-dialog.h new file mode 100644 index 00000000..21aa52c7 --- /dev/null +++ b/include/json-dialog.h @@ -0,0 +1,8 @@ +#ifndef __JSON_DIALOG_H__ +#define __JSON_DIALOG_H__ + +extern char *json_input_file; + +SwitcherMode json_switcher_dialog ( char **input ); + +#endif diff --git a/include/simpleswitcher.h b/include/simpleswitcher.h index 9609d010..7d79e989 100644 --- a/include/simpleswitcher.h +++ b/include/simpleswitcher.h @@ -25,6 +25,7 @@ typedef enum { PROFILE_DIALOG, #endif NUM_DIALOGS, + JSON_DIALOG, MODE_EXIT, NEXT_DIALOG } SwitcherMode; diff --git a/source/json-dialog.c b/source/json-dialog.c new file mode 100644 index 00000000..152872fc --- /dev/null +++ b/source/json-dialog.c @@ -0,0 +1,197 @@ +/** + * simpleswitcher + * + * MIT/X11 License + * Copyright 2013-2014 Qball Cow <qball@gmpclient.org> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + + +#define _GNU_SOURCE +#include <stdlib.h> +#include <stdio.h> +#include <X11/X.h> + +#include <unistd.h> +#include <signal.h> +#include <strings.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> + +#include "simpleswitcher.h" +#include "mark-dialog.h" +#include <errno.h> +#include <linux/un.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <i3/ipc.h> + +#ifdef TIMING +#include <time.h> +#endif + +#include <json/json.h> + +char *json_input_file = NULL; + +static inline int execsh( const char *cmd ,int run_in_term ) +{ +// use sh for args parsing + if ( run_in_term ) + return execlp( config.terminal_emulator, config.terminal_emulator, "-e", "sh", "-c", cmd, NULL ); + + return execlp( "/bin/sh", "sh", "-c", cmd, NULL ); +} +static void exec_json( const char *cmd, int run_in_term) +{ + if ( !cmd || !cmd[0] ) return; + + printf("execute: %s\n", cmd); + signal( SIGCHLD, catch_exit ); + pid_t pid = fork(); + + if ( !pid ) { + setsid(); + execsh( cmd, run_in_term ); + exit( EXIT_FAILURE ); + } + +} + +typedef struct pmenu { + char *prompt; + int execute_in_term; + unsigned int num_entries; + char **entries; + char **commands; +} pmenu; + +static pmenu *get_json ( ) +{ + pmenu *retv = allocate_clear(sizeof(*retv)); + +#ifdef TIMING + struct timespec start, stop; + clock_gettime( CLOCK_REALTIME, &start ); +#endif + + struct json_object *jo = NULL; + if(json_input_file != NULL) { + jo = json_object_from_file(json_input_file); + } + + // Create error and exit. + if(jo == NULL) { + retv->entries= realloc( retv->entries, ( retv->num_entries+2 )*sizeof( char* ) ); + retv->entries[retv->num_entries] = strdup( "Failed to parse json input file" ); + retv->entries[retv->num_entries+1] = NULL; + retv->commands= realloc( retv->commands, ( retv->num_entries+2 )*sizeof( char* ) ); + retv->commands[retv->num_entries] = strdup( "" ); + retv->commands[retv->num_entries+1] = NULL; + + retv->num_entries++; + return retv; + } + + struct json_object *jo2; + if(json_object_object_get_ex(jo, "prompt", &jo2)) { + retv->prompt = strdup(json_object_get_string(jo2)); + } + if(json_object_object_get_ex(jo, "run-in-terminal", &jo2)) { + retv->execute_in_term = json_object_get_int(jo2); + } + + if(json_object_object_get_ex(jo, "commands", &jo2)) { + json_object_object_foreach(jo2, key, child) { + retv->entries= realloc( retv->entries, ( retv->num_entries+2 )*sizeof( char* ) ); + retv->entries[retv->num_entries] = strdup( key); + retv->entries[retv->num_entries+1] = NULL; + retv->commands= realloc( retv->commands, ( retv->num_entries+2 )*sizeof( char* ) ); + retv->commands[retv->num_entries] = strdup( json_object_get_string(child)); + retv->commands[retv->num_entries+1] = NULL; + + retv->num_entries++; + } + } + json_object_put(jo); + + + +#ifdef TIMING + clock_gettime( CLOCK_REALTIME, &stop ); + + if ( stop.tv_sec != start.tv_sec ) { + stop.tv_nsec += ( stop.tv_sec-start.tv_sec )*1e9; + } + + long diff = stop.tv_nsec-start.tv_nsec; + printf( "Time elapsed: %ld us\n", diff/1000 ); +#endif + return retv; +} + +static int token_match ( char **tokens, const char *input, + __attribute__( ( unused ) )int index, + __attribute__( ( unused ) )void *data ) +{ + int match = 1; + + // Do a tokenized match. + if ( tokens ) for ( int j = 1; match && tokens[j]; j++ ) { + match = ( strcasestr( input, tokens[j] ) != NULL ); + } + + return match; +} + +SwitcherMode json_switcher_dialog ( char **input ) +{ + SwitcherMode retv = MODE_EXIT; + // act as a launcher + pmenu *list = get_json( ); + + if ( list == NULL ) { + return retv; + } + + int shift=0; + int n = menu( list->entries, input, list->prompt, NULL, &shift,token_match, NULL ); + + if ( n == -2 ) { + retv = JSON_DIALOG; + } else if ( n >=0 && list->commands[n] != NULL ) { + exec_json( list->commands[n], list->execute_in_term); + } + + for ( unsigned int i=0; i < list->num_entries; i++ ) { + free( list->entries[i] ); + free( list->commands[i] ); + } + + if(list->entries)free( list->entries ); + if(list->commands)free( list->commands ); + if(list->prompt)free( list->prompt ); + + return retv; +} + diff --git a/source/simpleswitcher.c b/source/simpleswitcher.c index bc076182..7c1e466c 100644 --- a/source/simpleswitcher.c +++ b/source/simpleswitcher.c @@ -66,6 +66,7 @@ #include "ssh-dialog.h" #include "mark-dialog.h" #include "profile-dialog.h" +#include "json-dialog.h" #define LINE_MARGIN 4 @@ -1199,6 +1200,9 @@ void run_switcher( int fmode, SwitcherMode mode ) } #endif // __QC_MODE__ + else if ( mode == JSON_DIALOG ) { + retv = json_switcher_dialog ( &input ); + } if ( retv == NEXT_DIALOG ) { mode = ( mode+1 )%NUM_DIALOGS; @@ -1427,6 +1431,10 @@ int main( int argc, char *argv[] ) } else if ( find_arg( argc, argv, "-mnow" ) >= 0 ) { run_switcher( NOFORK, MARK_DIALOG ); #endif + } else if ( find_arg( argc, argv, "-json" ) >= 0 ) { + find_arg_str( argc, argv, "-json", &json_input_file); + run_switcher( NOFORK, JSON_DIALOG ); + } else { // Daemon mode, Listen to key presses..