2012-01-14 10:02:30 -05:00
/*
* vim : ts = 4 : sw = 4 : expandtab
*
* i3 - an improved dynamic tiling window manager
* © 2009 - 2012 Michael Stapelberg and contributors ( see also : LICENSE )
*
* commands . c : all command functions ( see commands_parser . c )
*
*/
# include <float.h>
Implement a new parser for commands. (+test)
On the rationale of using a custom parser instead of a lex/yacc one, see this
quote from src/commands_parser.c:
We use a hand-written parser instead of lex/yacc because our commands are
easy for humans, not for computers. Thus, it’s quite hard to specify a
context-free grammar for the commands. A PEG grammar would be easier, but
there’s downsides to every PEG parser generator I have come accross so far.
This parser is basically a state machine which looks for literals or strings
and can push either on a stack. After identifying a literal or string, it
will either transition to the current state, to a different state, or call a
function (like cmd_move()).
Special care has been taken that error messages are useful and the code is
well testable (when compiled with -DTEST_PARSER it will output to stdout
instead of actually calling any function).
During the migration phase (I plan to completely switch to this parser before
4.2 will be released), the new parser will parse every command you send to
i3 and save the resulting call stack. Then, the old parser will parse your
input and actually execute the commands. Afterwards, both call stacks will be
compared and any differences will be logged.
The new parser works with 100% of the test suite and produces identical call
stacks.
2012-01-14 14:53:29 -05:00
# include <stdarg.h>
2012-01-14 10:02:30 -05:00
# include "all.h"
/** When the command did not include match criteria (!), we use the currently
* focused command . Do not confuse this case with a command which included
* criteria but which did not match any windows . This macro has to be called in
* every command .
*/
# define HANDLE_EMPTY_MATCH do { \
if ( match_is_empty ( current_match ) ) { \
owindow * ow = smalloc ( sizeof ( owindow ) ) ; \
ow - > con = focused ; \
TAILQ_INIT ( & owindows ) ; \
TAILQ_INSERT_TAIL ( & owindows , ow , owindows ) ; \
} \
} while ( 0 )
static owindows_head owindows ;
/*
* Returns true if a is definitely greater than b ( using the given epsilon )
*
*/
static bool definitelyGreaterThan ( float a , float b , float epsilon ) {
return ( a - b ) > ( ( fabs ( a ) < fabs ( b ) ? fabs ( b ) : fabs ( a ) ) * epsilon ) ;
}
2012-01-27 17:32:40 -05:00
/*
* Returns an ' output ' corresponding to one of left / right / down / up or a specific
* output name .
*
*/
2012-01-14 10:02:30 -05:00
static Output * get_output_from_string ( Output * current_output , const char * output_str ) {
Output * output ;
if ( strcasecmp ( output_str , " left " ) = = 0 ) {
output = get_output_next ( D_LEFT , current_output ) ;
if ( ! output )
output = get_output_most ( D_RIGHT , current_output ) ;
} else if ( strcasecmp ( output_str , " right " ) = = 0 ) {
output = get_output_next ( D_RIGHT , current_output ) ;
if ( ! output )
output = get_output_most ( D_LEFT , current_output ) ;
} else if ( strcasecmp ( output_str , " up " ) = = 0 ) {
output = get_output_next ( D_UP , current_output ) ;
if ( ! output )
output = get_output_most ( D_DOWN , current_output ) ;
} else if ( strcasecmp ( output_str , " down " ) = = 0 ) {
output = get_output_next ( D_DOWN , current_output ) ;
if ( ! output )
output = get_output_most ( D_UP , current_output ) ;
} else output = get_output_by_name ( output_str ) ;
return output ;
}
2012-01-27 17:11:03 -05:00
// This code is commented out because we might recycle it for popping up error
// messages on parser errors.
#if 0
Implement a new parser for commands. (+test)
On the rationale of using a custom parser instead of a lex/yacc one, see this
quote from src/commands_parser.c:
We use a hand-written parser instead of lex/yacc because our commands are
easy for humans, not for computers. Thus, it’s quite hard to specify a
context-free grammar for the commands. A PEG grammar would be easier, but
there’s downsides to every PEG parser generator I have come accross so far.
This parser is basically a state machine which looks for literals or strings
and can push either on a stack. After identifying a literal or string, it
will either transition to the current state, to a different state, or call a
function (like cmd_move()).
Special care has been taken that error messages are useful and the code is
well testable (when compiled with -DTEST_PARSER it will output to stdout
instead of actually calling any function).
During the migration phase (I plan to completely switch to this parser before
4.2 will be released), the new parser will parse every command you send to
i3 and save the resulting call stack. Then, the old parser will parse your
input and actually execute the commands. Afterwards, both call stacks will be
compared and any differences will be logged.
The new parser works with 100% of the test suite and produces identical call
stacks.
2012-01-14 14:53:29 -05:00
static pid_t migration_pid = - 1 ;
/*
* Handler which will be called when we get a SIGCHLD for the nagbar , meaning
* it exited ( or could not be started , depending on the exit code ) .
*
*/
static void nagbar_exited ( EV_P_ ev_child * watcher , int revents ) {
ev_child_stop ( EV_A_ watcher ) ;
if ( ! WIFEXITED ( watcher - > rstatus ) ) {
fprintf ( stderr , " ERROR: i3-nagbar did not exit normally. \n " ) ;
return ;
}
int exitcode = WEXITSTATUS ( watcher - > rstatus ) ;
printf ( " i3-nagbar process exited with status %d \n " , exitcode ) ;
if ( exitcode = = 2 ) {
fprintf ( stderr , " ERROR: i3-nagbar could not be found. Is it correctly installed on your system? \n " ) ;
}
migration_pid = - 1 ;
}
/* We need ev >= 4 for the following code. Since it is not *that* important (it
* only makes sure that there are no i3 - nagbar instances left behind ) we still
* support old systems with libev 3. */
# if EV_VERSION_MAJOR >= 4
/*
* Cleanup handler . Will be called when i3 exits . Kills i3 - nagbar with signal
* SIGKILL ( 9 ) to make sure there are no left - over i3 - nagbar processes .
*
*/
static void nagbar_cleanup ( EV_P_ ev_cleanup * watcher , int revent ) {
if ( migration_pid ! = - 1 ) {
LOG ( " Sending SIGKILL (9) to i3-nagbar with PID %d \n " , migration_pid ) ;
kill ( migration_pid , SIGKILL ) ;
}
}
# endif
2012-03-31 04:53:04 -04:00
void cmd_MIGRATION_start_nagbar ( void ) {
Implement a new parser for commands. (+test)
On the rationale of using a custom parser instead of a lex/yacc one, see this
quote from src/commands_parser.c:
We use a hand-written parser instead of lex/yacc because our commands are
easy for humans, not for computers. Thus, it’s quite hard to specify a
context-free grammar for the commands. A PEG grammar would be easier, but
there’s downsides to every PEG parser generator I have come accross so far.
This parser is basically a state machine which looks for literals or strings
and can push either on a stack. After identifying a literal or string, it
will either transition to the current state, to a different state, or call a
function (like cmd_move()).
Special care has been taken that error messages are useful and the code is
well testable (when compiled with -DTEST_PARSER it will output to stdout
instead of actually calling any function).
During the migration phase (I plan to completely switch to this parser before
4.2 will be released), the new parser will parse every command you send to
i3 and save the resulting call stack. Then, the old parser will parse your
input and actually execute the commands. Afterwards, both call stacks will be
compared and any differences will be logged.
The new parser works with 100% of the test suite and produces identical call
stacks.
2012-01-14 14:53:29 -05:00
if ( migration_pid ! = - 1 ) {
fprintf ( stderr , " i3-nagbar already running. \n " ) ;
return ;
}
fprintf ( stderr , " Starting i3-nagbar, command parsing differs from expected output. \n " ) ;
ELOG ( " Please report this on IRC or in the bugtracker. Make sure to include the full debug level logfile: \n " ) ;
ELOG ( " i3-dump-log | gzip -9c > /tmp/i3.log.gz \n " ) ;
ELOG ( " FYI: Your i3 version is " I3_VERSION " \n " ) ;
migration_pid = fork ( ) ;
if ( migration_pid = = - 1 ) {
warn ( " Could not fork() " ) ;
return ;
}
/* child */
if ( migration_pid = = 0 ) {
char * pageraction ;
sasprintf ( & pageraction , " i3-sensible-terminal -e i3-sensible-pager \" %s \" " , errorfilename ) ;
char * argv [ ] = {
NULL , /* will be replaced by the executable path */
" -t " ,
" error " ,
" -m " ,
" You found a parsing error. Please, please, please, report it! " ,
" -b " ,
" show errors " ,
pageraction ,
NULL
} ;
exec_i3_utility ( " i3-nagbar " , argv ) ;
}
/* parent */
/* install a child watcher */
ev_child * child = smalloc ( sizeof ( ev_child ) ) ;
ev_child_init ( child , & nagbar_exited , migration_pid , 0 ) ;
ev_child_start ( main_loop , child ) ;
/* We need ev >= 4 for the following code. Since it is not *that* important (it
* only makes sure that there are no i3 - nagbar instances left behind ) we still
* support old systems with libev 3. */
# if EV_VERSION_MAJOR >= 4
/* install a cleanup watcher (will be called when i3 exits and i3-nagbar is
* still running ) */
ev_cleanup * cleanup = smalloc ( sizeof ( ev_cleanup ) ) ;
ev_cleanup_init ( cleanup , nagbar_cleanup ) ;
ev_cleanup_start ( main_loop , cleanup ) ;
# endif
}
2012-01-27 17:11:03 -05:00
# endif
Implement a new parser for commands. (+test)
On the rationale of using a custom parser instead of a lex/yacc one, see this
quote from src/commands_parser.c:
We use a hand-written parser instead of lex/yacc because our commands are
easy for humans, not for computers. Thus, it’s quite hard to specify a
context-free grammar for the commands. A PEG grammar would be easier, but
there’s downsides to every PEG parser generator I have come accross so far.
This parser is basically a state machine which looks for literals or strings
and can push either on a stack. After identifying a literal or string, it
will either transition to the current state, to a different state, or call a
function (like cmd_move()).
Special care has been taken that error messages are useful and the code is
well testable (when compiled with -DTEST_PARSER it will output to stdout
instead of actually calling any function).
During the migration phase (I plan to completely switch to this parser before
4.2 will be released), the new parser will parse every command you send to
i3 and save the resulting call stack. Then, the old parser will parse your
input and actually execute the commands. Afterwards, both call stacks will be
compared and any differences will be logged.
The new parser works with 100% of the test suite and produces identical call
stacks.
2012-01-14 14:53:29 -05:00
/*******************************************************************************
* Criteria functions .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2012-01-27 17:32:40 -05:00
/*
* Initializes the specified ' Match ' data structure and the initial state of
* commands . c for matching target windows of a command .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_criteria_init ( I3_CMD ) {
2012-01-27 16:47:55 -05:00
Con * con ;
owindow * ow ;
2012-01-14 10:02:30 -05:00
DLOG ( " Initializing criteria, current_match = %p \n " , current_match ) ;
match_init ( current_match ) ;
2012-01-27 16:47:55 -05:00
while ( ! TAILQ_EMPTY ( & owindows ) ) {
ow = TAILQ_FIRST ( & owindows ) ;
TAILQ_REMOVE ( & owindows , ow , owindows ) ;
free ( ow ) ;
}
2012-01-14 10:02:30 -05:00
TAILQ_INIT ( & owindows ) ;
/* copy all_cons */
TAILQ_FOREACH ( con , & all_cons , all_cons ) {
2012-01-27 16:47:55 -05:00
ow = smalloc ( sizeof ( owindow ) ) ;
2012-01-14 10:02:30 -05:00
ow - > con = con ;
TAILQ_INSERT_TAIL ( & owindows , ow , owindows ) ;
}
}
2012-01-27 17:32:40 -05:00
/*
* A match specification just finished ( the closing square bracket was found ) ,
* so we filter the list of owindows .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_criteria_match_windows ( I3_CMD ) {
2012-01-14 10:02:30 -05:00
owindow * next , * current ;
DLOG ( " match specification finished, matching... \n " ) ;
/* copy the old list head to iterate through it and start with a fresh
* list which will contain only matching windows */
struct owindows_head old = owindows ;
TAILQ_INIT ( & owindows ) ;
for ( next = TAILQ_FIRST ( & old ) ; next ! = TAILQ_END ( & old ) ; ) {
/* make a copy of the next pointer and advance the pointer to the
* next element as we are going to invalidate the element ’ s
* next / prev pointers by calling TAILQ_INSERT_TAIL later */
current = next ;
next = TAILQ_NEXT ( next , owindows ) ;
DLOG ( " checking if con %p / %s matches \n " , current - > con , current - > con - > name ) ;
if ( current_match - > con_id ! = NULL ) {
if ( current_match - > con_id = = current - > con ) {
DLOG ( " matches container! \n " ) ;
TAILQ_INSERT_TAIL ( & owindows , current , owindows ) ;
}
} else if ( current_match - > mark ! = NULL & & current - > con - > mark ! = NULL & &
regex_matches ( current_match - > mark , current - > con - > mark ) ) {
DLOG ( " match by mark \n " ) ;
TAILQ_INSERT_TAIL ( & owindows , current , owindows ) ;
} else {
if ( current - > con - > window = = NULL )
continue ;
if ( match_matches_window ( current_match , current - > con - > window ) ) {
DLOG ( " matches window! \n " ) ;
TAILQ_INSERT_TAIL ( & owindows , current , owindows ) ;
} else {
DLOG ( " doesnt match \n " ) ;
free ( current ) ;
}
}
}
TAILQ_FOREACH ( current , & owindows , owindows ) {
DLOG ( " matching: %p / %s \n " , current - > con , current - > con - > name ) ;
}
}
2012-01-27 17:32:40 -05:00
/*
* Interprets a ctype = cvalue pair and adds it to the current match
* specification .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_criteria_add ( I3_CMD , char * ctype , char * cvalue ) {
2012-01-14 10:02:30 -05:00
DLOG ( " ctype=*%s*, cvalue=*%s* \n " , ctype , cvalue ) ;
if ( strcmp ( ctype , " class " ) = = 0 ) {
current_match - > class = regex_new ( cvalue ) ;
2012-02-07 17:38:21 -05:00
return ;
2012-01-14 10:02:30 -05:00
}
if ( strcmp ( ctype , " instance " ) = = 0 ) {
current_match - > instance = regex_new ( cvalue ) ;
2012-02-07 17:38:21 -05:00
return ;
2012-01-14 10:02:30 -05:00
}
if ( strcmp ( ctype , " window_role " ) = = 0 ) {
current_match - > role = regex_new ( cvalue ) ;
2012-02-07 17:38:21 -05:00
return ;
2012-01-14 10:02:30 -05:00
}
if ( strcmp ( ctype , " con_id " ) = = 0 ) {
char * end ;
long parsed = strtol ( cvalue , & end , 10 ) ;
if ( parsed = = LONG_MIN | |
parsed = = LONG_MAX | |
parsed < 0 | |
( end & & * end ! = ' \0 ' ) ) {
ELOG ( " Could not parse con id \" %s \" \n " , cvalue ) ;
} else {
current_match - > con_id = ( Con * ) parsed ;
printf ( " id as int = %p \n " , current_match - > con_id ) ;
}
2012-02-07 17:38:21 -05:00
return ;
2012-01-14 10:02:30 -05:00
}
if ( strcmp ( ctype , " id " ) = = 0 ) {
char * end ;
long parsed = strtol ( cvalue , & end , 10 ) ;
if ( parsed = = LONG_MIN | |
parsed = = LONG_MAX | |
parsed < 0 | |
( end & & * end ! = ' \0 ' ) ) {
ELOG ( " Could not parse window id \" %s \" \n " , cvalue ) ;
} else {
current_match - > id = parsed ;
printf ( " window id as int = %d \n " , current_match - > id ) ;
}
2012-02-07 17:38:21 -05:00
return ;
2012-01-14 10:02:30 -05:00
}
if ( strcmp ( ctype , " con_mark " ) = = 0 ) {
current_match - > mark = regex_new ( cvalue ) ;
2012-02-07 17:38:21 -05:00
return ;
2012-01-14 10:02:30 -05:00
}
if ( strcmp ( ctype , " title " ) = = 0 ) {
current_match - > title = regex_new ( cvalue ) ;
2012-02-07 17:38:21 -05:00
return ;
2012-01-14 10:02:30 -05:00
}
2012-01-24 18:00:27 -05:00
if ( strcmp ( ctype , " urgent " ) = = 0 ) {
if ( strcasecmp ( cvalue , " latest " ) = = 0 | |
strcasecmp ( cvalue , " newest " ) = = 0 | |
strcasecmp ( cvalue , " recent " ) = = 0 | |
strcasecmp ( cvalue , " last " ) = = 0 ) {
current_match - > urgent = U_LATEST ;
} else if ( strcasecmp ( cvalue , " oldest " ) = = 0 | |
strcasecmp ( cvalue , " first " ) = = 0 ) {
current_match - > urgent = U_OLDEST ;
}
2012-02-07 17:38:21 -05:00
return ;
2012-01-24 18:00:27 -05:00
}
2012-01-14 10:02:30 -05:00
ELOG ( " Unknown criterion: %s \n " , ctype ) ;
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' move [ window | container ] [ to ] workspace
* next | prev | next_on_output | prev_on_output ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_move_con_to_workspace ( I3_CMD , char * which ) {
2012-01-14 10:02:30 -05:00
owindow * current ;
DLOG ( " which=%s \n " , which ) ;
HANDLE_EMPTY_MATCH ;
/* get the workspace */
Con * ws ;
if ( strcmp ( which , " next " ) = = 0 )
ws = workspace_next ( ) ;
else if ( strcmp ( which , " prev " ) = = 0 )
ws = workspace_prev ( ) ;
else if ( strcmp ( which , " next_on_output " ) = = 0 )
ws = workspace_next_on_output ( ) ;
else if ( strcmp ( which , " prev_on_output " ) = = 0 )
ws = workspace_prev_on_output ( ) ;
else {
ELOG ( " BUG: called with which=%s \n " , which ) ;
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
2012-01-14 10:02:30 -05:00
}
TAILQ_FOREACH ( current , & owindows , owindows ) {
DLOG ( " matching: %p / %s \n " , current - > con , current - > con - > name ) ;
con_move_to_workspace ( current - > con , ws , true , false ) ;
}
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' move [ window | container ] [ to ] workspace < name > ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_move_con_to_workspace_name ( I3_CMD , char * name ) {
2012-01-14 10:02:30 -05:00
if ( strncasecmp ( name , " __i3_ " , strlen ( " __i3_ " ) ) = = 0 ) {
LOG ( " You cannot switch to the i3 internal workspaces. \n " ) ;
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
2012-01-14 10:02:30 -05:00
}
owindow * current ;
/* Error out early to not create a non-existing workspace (in
* workspace_get ( ) ) if we are not actually able to move anything . */
2012-02-07 17:38:21 -05:00
if ( match_is_empty ( current_match ) & & focused - > type = = CT_WORKSPACE ) {
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
}
2012-01-14 10:02:30 -05:00
LOG ( " should move window to workspace %s \n " , name ) ;
/* get the workspace */
Con * ws = workspace_get ( name , NULL ) ;
HANDLE_EMPTY_MATCH ;
TAILQ_FOREACH ( current , & owindows , owindows ) {
DLOG ( " matching: %p / %s \n " , current - > con , current - > con - > name ) ;
con_move_to_workspace ( current - > con , ws , true , false ) ;
}
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-04-08 14:33:46 -04:00
/*
* Implementation of ' move [ window | container ] [ to ] workspace number < number > ' .
*
*/
void cmd_move_con_to_workspace_number ( I3_CMD , char * which ) {
owindow * current ;
/* Error out early to not create a non-existing workspace (in
* workspace_get ( ) ) if we are not actually able to move anything . */
if ( match_is_empty ( current_match ) & & focused - > type = = CT_WORKSPACE ) {
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
}
LOG ( " should move window to workspace with number %d \n " , which ) ;
/* get the workspace */
Con * output , * workspace ;
char * endptr = NULL ;
long parsed_num = strtol ( which , & endptr , 10 ) ;
if ( parsed_num = = LONG_MIN | |
parsed_num = = LONG_MAX | |
parsed_num < 0 | |
* endptr ! = ' \0 ' ) {
LOG ( " Could not parse \" %s \" as a number. \n " , which ) ;
cmd_output - > json_output = sstrdup ( " { \" success \" : false, "
" \" error \" : \" Could not parse number \" } " ) ;
return ;
}
TAILQ_FOREACH ( output , & ( croot - > nodes_head ) , nodes )
GREP_FIRST ( workspace , output_get_content ( output ) ,
child - > num = = parsed_num ) ;
if ( ! workspace ) {
cmd_output - > json_output = sstrdup ( " { \" success \" : false, "
" \" error \" : \" No such workspace \" } " ) ;
return ;
}
HANDLE_EMPTY_MATCH ;
TAILQ_FOREACH ( current , & owindows , owindows ) {
DLOG ( " matching: %p / %s \n " , current - > con , current - > con - > name ) ;
con_move_to_workspace ( current - > con , workspace , true , false ) ;
}
cmd_output - > needs_tree_render = true ;
// XXX: default reply for now, make this a better reply
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
}
2012-04-08 09:59:49 -04:00
static void cmd_resize_floating ( I3_CMD , char * way , char * direction , Con * floating_con , int px ) {
LOG ( " floating resize \n " ) ;
if ( strcmp ( direction , " up " ) = = 0 ) {
floating_con - > rect . y - = px ;
floating_con - > rect . height + = px ;
} else if ( strcmp ( direction , " down " ) = = 0 ) {
floating_con - > rect . height + = px ;
} else if ( strcmp ( direction , " left " ) = = 0 ) {
floating_con - > rect . x - = px ;
floating_con - > rect . width + = px ;
} else {
floating_con - > rect . width + = px ;
}
}
static void cmd_resize_tiling_direction ( I3_CMD , char * way , char * direction , int ppt ) {
LOG ( " tiling resize \n " ) ;
/* get the appropriate current container (skip stacked/tabbed cons) */
Con * current = focused ;
while ( current - > parent - > layout = = L_STACKED | |
current - > parent - > layout = = L_TABBED )
current = current - > parent ;
/* Then further go up until we find one with the matching orientation. */
orientation_t search_orientation =
( strcmp ( direction , " left " ) = = 0 | | strcmp ( direction , " right " ) = = 0 ? HORIZ : VERT ) ;
while ( current - > type ! = CT_WORKSPACE & &
current - > type ! = CT_FLOATING_CON & &
current - > parent - > orientation ! = search_orientation )
current = current - > parent ;
/* get the default percentage */
int children = con_num_children ( current - > parent ) ;
Con * other ;
LOG ( " ins. %d children \n " , children ) ;
double percentage = 1.0 / children ;
LOG ( " default percentage = %f \n " , percentage ) ;
orientation_t orientation = current - > parent - > orientation ;
if ( ( orientation = = HORIZ & &
( strcmp ( direction , " up " ) = = 0 | | strcmp ( direction , " down " ) = = 0 ) ) | |
( orientation = = VERT & &
( strcmp ( direction , " left " ) = = 0 | | strcmp ( direction , " right " ) = = 0 ) ) ) {
LOG ( " You cannot resize in that direction. Your focus is in a %s split container currently. \n " ,
( orientation = = HORIZ ? " horizontal " : " vertical " ) ) ;
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
}
if ( strcmp ( direction , " up " ) = = 0 | | strcmp ( direction , " left " ) = = 0 ) {
other = TAILQ_PREV ( current , nodes_head , nodes ) ;
} else {
other = TAILQ_NEXT ( current , nodes ) ;
}
if ( other = = TAILQ_END ( workspaces ) ) {
LOG ( " No other container in this direction found, cannot resize. \n " ) ;
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
}
LOG ( " other->percent = %f \n " , other - > percent ) ;
LOG ( " current->percent before = %f \n " , current - > percent ) ;
if ( current - > percent = = 0.0 )
current - > percent = percentage ;
if ( other - > percent = = 0.0 )
other - > percent = percentage ;
double new_current_percent = current - > percent + ( ( double ) ppt / 100.0 ) ;
double new_other_percent = other - > percent - ( ( double ) ppt / 100.0 ) ;
LOG ( " new_current_percent = %f \n " , new_current_percent ) ;
LOG ( " new_other_percent = %f \n " , new_other_percent ) ;
/* Ensure that the new percentages are positive and greater than
* 0.05 to have a reasonable minimum size . */
if ( definitelyGreaterThan ( new_current_percent , 0.05 , DBL_EPSILON ) & &
definitelyGreaterThan ( new_other_percent , 0.05 , DBL_EPSILON ) ) {
current - > percent + = ( ( double ) ppt / 100.0 ) ;
other - > percent - = ( ( double ) ppt / 100.0 ) ;
LOG ( " current->percent after = %f \n " , current - > percent ) ;
LOG ( " other->percent after = %f \n " , other - > percent ) ;
} else {
LOG ( " Not resizing, already at minimum size \n " ) ;
}
}
static void cmd_resize_tiling_width_height ( I3_CMD , char * way , char * direction , int ppt ) {
LOG ( " width/height resize \n " ) ;
/* get the appropriate current container (skip stacked/tabbed cons) */
Con * current = focused ;
while ( current - > parent - > layout = = L_STACKED | |
current - > parent - > layout = = L_TABBED )
current = current - > parent ;
/* Then further go up until we find one with the matching orientation. */
orientation_t search_orientation =
( strcmp ( direction , " width " ) = = 0 ? HORIZ : VERT ) ;
while ( current - > type ! = CT_WORKSPACE & &
current - > type ! = CT_FLOATING_CON & &
current - > parent - > orientation ! = search_orientation )
current = current - > parent ;
/* get the default percentage */
int children = con_num_children ( current - > parent ) ;
LOG ( " ins. %d children \n " , children ) ;
double percentage = 1.0 / children ;
LOG ( " default percentage = %f \n " , percentage ) ;
orientation_t orientation = current - > parent - > orientation ;
if ( ( orientation = = HORIZ & &
strcmp ( direction , " height " ) = = 0 ) | |
( orientation = = VERT & &
strcmp ( direction , " width " ) = = 0 ) ) {
LOG ( " You cannot resize in that direction. Your focus is in a %s split container currently. \n " ,
( orientation = = HORIZ ? " horizontal " : " vertical " ) ) ;
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
}
if ( children = = 1 ) {
LOG ( " This is the only container, cannot resize. \n " ) ;
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
}
/* Ensure all the other children have a percentage set. */
Con * child ;
TAILQ_FOREACH ( child , & ( current - > parent - > nodes_head ) , nodes ) {
LOG ( " child->percent = %f (child %p) \n " , child - > percent , child ) ;
if ( child - > percent = = 0.0 )
child - > percent = percentage ;
}
double new_current_percent = current - > percent + ( ( double ) ppt / 100.0 ) ;
double subtract_percent = ( ( double ) ppt / 100.0 ) / ( children - 1 ) ;
LOG ( " new_current_percent = %f \n " , new_current_percent ) ;
LOG ( " subtract_percent = %f \n " , subtract_percent ) ;
/* Ensure that the new percentages are positive and greater than
* 0.05 to have a reasonable minimum size . */
TAILQ_FOREACH ( child , & ( current - > parent - > nodes_head ) , nodes ) {
if ( child = = current )
continue ;
if ( ! definitelyGreaterThan ( child - > percent - subtract_percent , 0.05 , DBL_EPSILON ) ) {
LOG ( " Not resizing, already at minimum size (child %p would end up with a size of %.f \n " , child , child - > percent - subtract_percent ) ;
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
}
}
if ( ! definitelyGreaterThan ( new_current_percent , 0.05 , DBL_EPSILON ) ) {
LOG ( " Not resizing, already at minimum size \n " ) ;
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
}
current - > percent + = ( ( double ) ppt / 100.0 ) ;
LOG ( " current->percent after = %f \n " , current - > percent ) ;
TAILQ_FOREACH ( child , & ( current - > parent - > nodes_head ) , nodes ) {
if ( child = = current )
continue ;
child - > percent - = subtract_percent ;
LOG ( " child->percent after (%p) = %f \n " , child , child - > percent ) ;
}
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' resize grow | shrink < direction > [ < px > px ] [ or < ppt > ppt ] ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_resize ( I3_CMD , char * way , char * direction , char * resize_px , char * resize_ppt ) {
2012-01-14 10:02:30 -05:00
/* resize <grow|shrink> <direction> [<px> px] [or <ppt> ppt] */
DLOG ( " resizing in way %s, direction %s, px %s or ppt %s \n " , way , direction , resize_px , resize_ppt ) ;
// TODO: We could either handle this in the parser itself as a separate token (and make the stack typed) or we need a better way to convert a string to a number with error checking
int px = atoi ( resize_px ) ;
int ppt = atoi ( resize_ppt ) ;
if ( strcmp ( way , " shrink " ) = = 0 ) {
px * = - 1 ;
ppt * = - 1 ;
}
Con * floating_con ;
if ( ( floating_con = con_inside_floating ( focused ) ) ) {
2012-04-08 09:59:49 -04:00
cmd_resize_floating ( current_match , cmd_output , way , direction , floating_con , px ) ;
2012-01-14 10:02:30 -05:00
} else {
2012-04-08 09:59:49 -04:00
if ( strcmp ( direction , " width " ) = = 0 | |
strcmp ( direction , " height " ) = = 0 )
cmd_resize_tiling_width_height ( current_match , cmd_output , way , direction , ppt ) ;
else cmd_resize_tiling_direction ( current_match , cmd_output , way , direction , ppt ) ;
2012-01-14 10:02:30 -05:00
}
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' border normal | none | 1 pixel | toggle ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_border ( I3_CMD , char * border_style_str ) {
2012-01-14 10:02:30 -05:00
DLOG ( " border style should be changed to %s \n " , border_style_str ) ;
owindow * current ;
HANDLE_EMPTY_MATCH ;
TAILQ_FOREACH ( current , & owindows , owindows ) {
DLOG ( " matching: %p / %s \n " , current - > con , current - > con - > name ) ;
int border_style = current - > con - > border_style ;
if ( strcmp ( border_style_str , " toggle " ) = = 0 ) {
border_style + + ;
border_style % = 3 ;
} else {
if ( strcmp ( border_style_str , " normal " ) = = 0 )
border_style = BS_NORMAL ;
else if ( strcmp ( border_style_str , " none " ) = = 0 )
border_style = BS_NONE ;
else if ( strcmp ( border_style_str , " 1pixel " ) = = 0 )
border_style = BS_1PIXEL ;
else {
ELOG ( " BUG: called with border_style=%s \n " , border_style_str ) ;
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
2012-01-14 10:02:30 -05:00
}
}
con_set_border_style ( current - > con , border_style ) ;
}
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' nop < comment > ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_nop ( I3_CMD , char * comment ) {
2012-01-14 10:02:30 -05:00
LOG ( " ------------------------------------------------- \n " ) ;
LOG ( " NOP: %s \n " , comment ) ;
LOG ( " ------------------------------------------------- \n " ) ;
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' append_layout < path > ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_append_layout ( I3_CMD , char * path ) {
2012-01-14 10:02:30 -05:00
LOG ( " Appending layout \" %s \" \n " , path ) ;
tree_append_json ( path ) ;
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' workspace next | prev | next_on_output | prev_on_output ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_workspace ( I3_CMD , char * which ) {
2012-01-14 10:02:30 -05:00
Con * ws ;
DLOG ( " which=%s \n " , which ) ;
if ( strcmp ( which , " next " ) = = 0 )
ws = workspace_next ( ) ;
else if ( strcmp ( which , " prev " ) = = 0 )
ws = workspace_prev ( ) ;
else if ( strcmp ( which , " next_on_output " ) = = 0 )
ws = workspace_next_on_output ( ) ;
else if ( strcmp ( which , " prev_on_output " ) = = 0 )
ws = workspace_prev_on_output ( ) ;
else {
ELOG ( " BUG: called with which=%s \n " , which ) ;
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
2012-01-14 10:02:30 -05:00
}
workspace_show ( ws ) ;
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-04-08 13:17:46 -04:00
/*
* Implementation of ' workspace number < number > '
*
*/
void cmd_workspace_number ( I3_CMD , char * which ) {
Con * output , * workspace ;
char * endptr = NULL ;
long parsed_num = strtol ( which , & endptr , 10 ) ;
if ( parsed_num = = LONG_MIN | |
parsed_num = = LONG_MAX | |
parsed_num < 0 | |
* endptr ! = ' \0 ' ) {
LOG ( " Could not parse \" %s \" as a number. \n " , which ) ;
cmd_output - > json_output = sstrdup ( " { \" success \" : false, "
" \" error \" : \" Could not parse number \" } " ) ;
return ;
}
TAILQ_FOREACH ( output , & ( croot - > nodes_head ) , nodes )
GREP_FIRST ( workspace , output_get_content ( output ) ,
child - > num = = parsed_num ) ;
if ( ! workspace ) {
LOG ( " There is no workspace with number %d. \n " , parsed_num ) ;
cmd_output - > json_output = sstrdup ( " { \" success \" : false, "
" \" error \" : \" No such workspace \" } " ) ;
return ;
}
workspace_show ( workspace ) ;
cmd_output - > needs_tree_render = true ;
// XXX: default reply for now, make this a better reply
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' workspace back_and_forth ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_workspace_back_and_forth ( I3_CMD ) {
2012-01-14 10:02:30 -05:00
workspace_back_and_forth ( ) ;
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' workspace < name > '
*
*/
2012-02-07 17:38:21 -05:00
void cmd_workspace_name ( I3_CMD , char * name ) {
2012-01-14 10:02:30 -05:00
if ( strncasecmp ( name , " __i3_ " , strlen ( " __i3_ " ) ) = = 0 ) {
LOG ( " You cannot switch to the i3 internal workspaces. \n " ) ;
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
2012-01-14 10:02:30 -05:00
}
DLOG ( " should switch to workspace %s \n " , name ) ;
Con * ws = con_get_workspace ( focused ) ;
/* Check if the command wants to switch to the current workspace */
if ( strcmp ( ws - > name , name ) = = 0 ) {
DLOG ( " This workspace is already focused. \n " ) ;
if ( config . workspace_auto_back_and_forth ) {
workspace_back_and_forth ( ) ;
tree_render ( ) ;
}
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
2012-01-14 10:02:30 -05:00
}
workspace_show_by_name ( name ) ;
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' mark < mark > '
*
*/
2012-02-07 17:38:21 -05:00
void cmd_mark ( I3_CMD , char * mark ) {
2012-01-14 10:02:30 -05:00
DLOG ( " Clearing all windows which have that mark first \n " ) ;
Con * con ;
TAILQ_FOREACH ( con , & all_cons , all_cons ) {
if ( con - > mark & & strcmp ( con - > mark , mark ) = = 0 )
FREE ( con - > mark ) ;
}
DLOG ( " marking window with str %s \n " , mark ) ;
owindow * current ;
HANDLE_EMPTY_MATCH ;
TAILQ_FOREACH ( current , & owindows , owindows ) {
DLOG ( " matching: %p / %s \n " , current - > con , current - > con - > name ) ;
current - > con - > mark = sstrdup ( mark ) ;
}
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' mode < string > ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_mode ( I3_CMD , char * mode ) {
2012-01-14 10:02:30 -05:00
DLOG ( " mode=%s \n " , mode ) ;
switch_mode ( mode ) ;
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' move [ window | container ] [ to ] output < str > ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_move_con_to_output ( I3_CMD , char * name ) {
2012-01-14 10:02:30 -05:00
owindow * current ;
DLOG ( " should move window to output %s \n " , name ) ;
HANDLE_EMPTY_MATCH ;
/* get the output */
Output * current_output = NULL ;
Output * output ;
// TODO: fix the handling of criteria
TAILQ_FOREACH ( current , & owindows , owindows )
current_output = get_output_containing ( current - > con - > rect . x , current - > con - > rect . y ) ;
assert ( current_output ! = NULL ) ;
// TODO: clean this up with commands.spec as soon as we switched away from the lex/yacc command parser
if ( strcasecmp ( name , " up " ) = = 0 )
output = get_output_next ( D_UP , current_output ) ;
else if ( strcasecmp ( name , " down " ) = = 0 )
output = get_output_next ( D_DOWN , current_output ) ;
else if ( strcasecmp ( name , " left " ) = = 0 )
output = get_output_next ( D_LEFT , current_output ) ;
else if ( strcasecmp ( name , " right " ) = = 0 )
output = get_output_next ( D_RIGHT , current_output ) ;
else
output = get_output_by_name ( name ) ;
if ( ! output ) {
LOG ( " No such output found. \n " ) ;
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
2012-01-14 10:02:30 -05:00
}
/* get visible workspace on output */
Con * ws = NULL ;
GREP_FIRST ( ws , output_get_content ( output - > con ) , workspace_is_visible ( child ) ) ;
2012-02-07 17:38:21 -05:00
if ( ! ws ) {
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
}
2012-01-14 10:02:30 -05:00
TAILQ_FOREACH ( current , & owindows , owindows ) {
DLOG ( " matching: %p / %s \n " , current - > con , current - > con - > name ) ;
con_move_to_workspace ( current - > con , ws , true , false ) ;
}
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' floating enable | disable | toggle '
*
*/
2012-02-07 17:38:21 -05:00
void cmd_floating ( I3_CMD , char * floating_mode ) {
2012-01-14 10:02:30 -05:00
owindow * current ;
DLOG ( " floating_mode=%s \n " , floating_mode ) ;
HANDLE_EMPTY_MATCH ;
TAILQ_FOREACH ( current , & owindows , owindows ) {
DLOG ( " matching: %p / %s \n " , current - > con , current - > con - > name ) ;
if ( strcmp ( floating_mode , " toggle " ) = = 0 ) {
DLOG ( " should toggle mode \n " ) ;
toggle_floating_mode ( current - > con , false ) ;
} else {
DLOG ( " should switch mode to %s \n " , floating_mode ) ;
if ( strcmp ( floating_mode , " enable " ) = = 0 ) {
floating_enable ( current - > con , false ) ;
} else {
floating_disable ( current - > con , false ) ;
}
}
}
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' move workspace to [ output ] < str > ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_move_workspace_to_output ( I3_CMD , char * name ) {
2012-01-14 10:02:30 -05:00
DLOG ( " should move workspace to output %s \n " , name ) ;
HANDLE_EMPTY_MATCH ;
owindow * current ;
TAILQ_FOREACH ( current , & owindows , owindows ) {
Output * current_output = get_output_containing ( current - > con - > rect . x ,
current - > con - > rect . y ) ;
Output * output = get_output_from_string ( current_output , name ) ;
if ( ! output ) {
LOG ( " No such output \n " ) ;
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
2012-01-14 10:02:30 -05:00
}
Con * content = output_get_content ( output - > con ) ;
LOG ( " got output %p with content %p \n " , output , content ) ;
Con * ws = con_get_workspace ( current - > con ) ;
LOG ( " should move workspace %p / %s \n " , ws , ws - > name ) ;
2012-01-14 17:09:09 -05:00
2012-01-14 10:02:30 -05:00
if ( con_num_children ( ws - > parent ) = = 1 ) {
2012-01-14 17:09:09 -05:00
LOG ( " Creating a new workspace to replace \" %s \" (last on its output). \n " , ws - > name ) ;
/* check if we can find a workspace assigned to this output */
bool used_assignment = false ;
struct Workspace_Assignment * assignment ;
TAILQ_FOREACH ( assignment , & ws_assignments , ws_assignments ) {
if ( strcmp ( assignment - > output , current_output - > name ) ! = 0 )
continue ;
/* check if this workspace is already attached to the tree */
Con * workspace = NULL , * out ;
TAILQ_FOREACH ( out , & ( croot - > nodes_head ) , nodes )
GREP_FIRST ( workspace , output_get_content ( out ) ,
! strcasecmp ( child - > name , assignment - > name ) ) ;
if ( workspace ! = NULL )
continue ;
/* so create the workspace referenced to by this assignment */
LOG ( " Creating workspace from assignment %s. \n " , assignment - > name ) ;
workspace_get ( assignment - > name , NULL ) ;
used_assignment = true ;
break ;
}
/* if we couldn't create the workspace using an assignment, create
* it on the output */
if ( ! used_assignment )
create_workspace_on_output ( current_output , ws - > parent ) ;
/* notify the IPC listeners */
ipc_send_event ( " workspace " , I3_IPC_EVENT_WORKSPACE , " { \" change \" : \" init \" } " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-14 17:09:09 -05:00
2012-01-29 13:22:43 -05:00
/* detach from the old output and attach to the new output */
2012-01-14 10:02:30 -05:00
bool workspace_was_visible = workspace_is_visible ( ws ) ;
Con * old_content = ws - > parent ;
con_detach ( ws ) ;
if ( workspace_was_visible ) {
/* The workspace which we just detached was visible, so focus
* the next one in the focus - stack . */
Con * focus_ws = TAILQ_FIRST ( & ( old_content - > focus_head ) ) ;
LOG ( " workspace was visible, focusing %p / %s now \n " , focus_ws , focus_ws - > name ) ;
workspace_show ( focus_ws ) ;
}
con_attach ( ws , content , false ) ;
2012-01-29 13:22:43 -05:00
/* fix the coordinates of the floating containers */
Con * floating_con ;
TAILQ_FOREACH ( floating_con , & ( ws - > floating_head ) , floating_windows )
floating_fix_coordinates ( floating_con , & ( old_content - > rect ) , & ( content - > rect ) ) ;
2012-01-14 10:02:30 -05:00
ipc_send_event ( " workspace " , I3_IPC_EVENT_WORKSPACE , " { \" change \" : \" move \" } " ) ;
if ( workspace_was_visible ) {
/* Focus the moved workspace on the destination output. */
workspace_show ( ws ) ;
}
}
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' split v | h | vertical | horizontal ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_split ( I3_CMD , char * direction ) {
2012-01-14 10:02:30 -05:00
/* TODO: use matches */
LOG ( " splitting in direction %c \n " , direction [ 0 ] ) ;
tree_split ( focused , ( direction [ 0 ] = = ' v ' ? VERT : HORIZ ) ) ;
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementaiton of ' kill [ window | client ] ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_kill ( I3_CMD , char * kill_mode_str ) {
Implement a new parser for commands. (+test)
On the rationale of using a custom parser instead of a lex/yacc one, see this
quote from src/commands_parser.c:
We use a hand-written parser instead of lex/yacc because our commands are
easy for humans, not for computers. Thus, it’s quite hard to specify a
context-free grammar for the commands. A PEG grammar would be easier, but
there’s downsides to every PEG parser generator I have come accross so far.
This parser is basically a state machine which looks for literals or strings
and can push either on a stack. After identifying a literal or string, it
will either transition to the current state, to a different state, or call a
function (like cmd_move()).
Special care has been taken that error messages are useful and the code is
well testable (when compiled with -DTEST_PARSER it will output to stdout
instead of actually calling any function).
During the migration phase (I plan to completely switch to this parser before
4.2 will be released), the new parser will parse every command you send to
i3 and save the resulting call stack. Then, the old parser will parse your
input and actually execute the commands. Afterwards, both call stacks will be
compared and any differences will be logged.
The new parser works with 100% of the test suite and produces identical call
stacks.
2012-01-14 14:53:29 -05:00
if ( kill_mode_str = = NULL )
kill_mode_str = " window " ;
2012-01-14 10:02:30 -05:00
owindow * current ;
DLOG ( " kill_mode=%s \n " , kill_mode_str ) ;
int kill_mode ;
Implement a new parser for commands. (+test)
On the rationale of using a custom parser instead of a lex/yacc one, see this
quote from src/commands_parser.c:
We use a hand-written parser instead of lex/yacc because our commands are
easy for humans, not for computers. Thus, it’s quite hard to specify a
context-free grammar for the commands. A PEG grammar would be easier, but
there’s downsides to every PEG parser generator I have come accross so far.
This parser is basically a state machine which looks for literals or strings
and can push either on a stack. After identifying a literal or string, it
will either transition to the current state, to a different state, or call a
function (like cmd_move()).
Special care has been taken that error messages are useful and the code is
well testable (when compiled with -DTEST_PARSER it will output to stdout
instead of actually calling any function).
During the migration phase (I plan to completely switch to this parser before
4.2 will be released), the new parser will parse every command you send to
i3 and save the resulting call stack. Then, the old parser will parse your
input and actually execute the commands. Afterwards, both call stacks will be
compared and any differences will be logged.
The new parser works with 100% of the test suite and produces identical call
stacks.
2012-01-14 14:53:29 -05:00
if ( strcmp ( kill_mode_str , " window " ) = = 0 )
2012-01-14 10:02:30 -05:00
kill_mode = KILL_WINDOW ;
else if ( strcmp ( kill_mode_str , " client " ) = = 0 )
kill_mode = KILL_CLIENT ;
else {
ELOG ( " BUG: called with kill_mode=%s \n " , kill_mode_str ) ;
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
2012-01-14 10:02:30 -05:00
}
/* check if the match is empty, not if the result is empty */
if ( match_is_empty ( current_match ) )
tree_close_con ( kill_mode ) ;
else {
TAILQ_FOREACH ( current , & owindows , owindows ) {
DLOG ( " matching: %p / %s \n " , current - > con , current - > con - > name ) ;
tree_close ( current - > con , kill_mode , false , false ) ;
}
}
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' exec [ - - no - startup - id ] < command > ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_exec ( I3_CMD , char * nosn , char * command ) {
2012-01-14 10:02:30 -05:00
bool no_startup_id = ( nosn ! = NULL ) ;
DLOG ( " should execute %s, no_startup_id = %d \n " , command , no_startup_id ) ;
start_application ( command , no_startup_id ) ;
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' focus left | right | up | down ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_focus_direction ( I3_CMD , char * direction ) {
2012-01-14 10:02:30 -05:00
if ( focused & &
focused - > type ! = CT_WORKSPACE & &
focused - > fullscreen_mode ! = CF_NONE ) {
LOG ( " Cannot change focus while in fullscreen mode. \n " ) ;
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
2012-01-14 10:02:30 -05:00
}
DLOG ( " direction = *%s* \n " , direction ) ;
if ( strcmp ( direction , " left " ) = = 0 )
tree_next ( ' p ' , HORIZ ) ;
else if ( strcmp ( direction , " right " ) = = 0 )
tree_next ( ' n ' , HORIZ ) ;
else if ( strcmp ( direction , " up " ) = = 0 )
tree_next ( ' p ' , VERT ) ;
else if ( strcmp ( direction , " down " ) = = 0 )
tree_next ( ' n ' , VERT ) ;
else {
ELOG ( " Invalid focus direction (%s) \n " , direction ) ;
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
2012-01-14 10:02:30 -05:00
}
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' focus tiling | floating | mode_toggle ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_focus_window_mode ( I3_CMD , char * window_mode ) {
2012-01-14 10:02:30 -05:00
if ( focused & &
focused - > type ! = CT_WORKSPACE & &
focused - > fullscreen_mode ! = CF_NONE ) {
LOG ( " Cannot change focus while in fullscreen mode. \n " ) ;
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
2012-01-14 10:02:30 -05:00
}
DLOG ( " window_mode = %s \n " , window_mode ) ;
Con * ws = con_get_workspace ( focused ) ;
Con * current ;
if ( ws ! = NULL ) {
if ( strcmp ( window_mode , " mode_toggle " ) = = 0 ) {
current = TAILQ_FIRST ( & ( ws - > focus_head ) ) ;
if ( current ! = NULL & & current - > type = = CT_FLOATING_CON )
window_mode = " tiling " ;
else window_mode = " floating " ;
}
TAILQ_FOREACH ( current , & ( ws - > focus_head ) , focused ) {
if ( ( strcmp ( window_mode , " floating " ) = = 0 & & current - > type ! = CT_FLOATING_CON ) | |
( strcmp ( window_mode , " tiling " ) = = 0 & & current - > type = = CT_FLOATING_CON ) )
continue ;
con_focus ( con_descend_focused ( current ) ) ;
break ;
}
}
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' focus parent | child ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_focus_level ( I3_CMD , char * level ) {
2012-01-14 10:02:30 -05:00
if ( focused & &
focused - > type ! = CT_WORKSPACE & &
focused - > fullscreen_mode ! = CF_NONE ) {
LOG ( " Cannot change focus while in fullscreen mode. \n " ) ;
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
2012-01-14 10:02:30 -05:00
}
DLOG ( " level = %s \n " , level ) ;
if ( strcmp ( level , " parent " ) = = 0 )
level_up ( ) ;
else level_down ( ) ;
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' focus ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_focus ( I3_CMD ) {
2012-01-14 10:02:30 -05:00
DLOG ( " current_match = %p \n " , current_match ) ;
if ( focused & &
focused - > type ! = CT_WORKSPACE & &
focused - > fullscreen_mode ! = CF_NONE ) {
LOG ( " Cannot change focus while in fullscreen mode. \n " ) ;
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
2012-01-14 10:02:30 -05:00
}
owindow * current ;
if ( match_is_empty ( current_match ) ) {
ELOG ( " You have to specify which window/container should be focused. \n " ) ;
ELOG ( " Example: [class= \" urxvt \" title= \" irssi \" ] focus \n " ) ;
2012-02-07 17:38:21 -05:00
sasprintf ( & ( cmd_output - > json_output ) ,
" { \" success \" :false, \" error \" : \" You have to "
2012-01-14 10:02:30 -05:00
" specify which window/container should be focused \" } " ) ;
2012-02-07 17:38:21 -05:00
return ;
2012-01-14 10:02:30 -05:00
}
int count = 0 ;
TAILQ_FOREACH ( current , & owindows , owindows ) {
Con * ws = con_get_workspace ( current - > con ) ;
/* If no workspace could be found, this was a dock window.
* Just skip it , you cannot focus dock windows . */
if ( ! ws )
continue ;
/* If the container is not on the current workspace,
* workspace_show ( ) will switch to a different workspace and ( if
* enabled ) trigger a mouse pointer warp to the currently focused
* container ( ! ) on the target workspace .
*
* Therefore , before calling workspace_show ( ) , we make sure that
* ' current ' will be focused on the workspace . However , we cannot
* just con_focus ( current ) because then the pointer will not be
* warped at all ( the code thinks we are already there ) .
*
* So we focus ' current ' to make it the currently focused window of
* the target workspace , then revert focus . */
Con * currently_focused = focused ;
con_focus ( current - > con ) ;
con_focus ( currently_focused ) ;
/* Now switch to the workspace, then focus */
workspace_show ( ws ) ;
LOG ( " focusing %p / %s \n " , current - > con , current - > con - > name ) ;
con_focus ( current - > con ) ;
count + + ;
}
if ( count > 1 )
LOG ( " WARNING: Your criteria for the focus command matches %d containers, "
" while only exactly one container can be focused at a time. \n " , count ) ;
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' fullscreen [ global ] ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_fullscreen ( I3_CMD , char * fullscreen_mode ) {
Implement a new parser for commands. (+test)
On the rationale of using a custom parser instead of a lex/yacc one, see this
quote from src/commands_parser.c:
We use a hand-written parser instead of lex/yacc because our commands are
easy for humans, not for computers. Thus, it’s quite hard to specify a
context-free grammar for the commands. A PEG grammar would be easier, but
there’s downsides to every PEG parser generator I have come accross so far.
This parser is basically a state machine which looks for literals or strings
and can push either on a stack. After identifying a literal or string, it
will either transition to the current state, to a different state, or call a
function (like cmd_move()).
Special care has been taken that error messages are useful and the code is
well testable (when compiled with -DTEST_PARSER it will output to stdout
instead of actually calling any function).
During the migration phase (I plan to completely switch to this parser before
4.2 will be released), the new parser will parse every command you send to
i3 and save the resulting call stack. Then, the old parser will parse your
input and actually execute the commands. Afterwards, both call stacks will be
compared and any differences will be logged.
The new parser works with 100% of the test suite and produces identical call
stacks.
2012-01-14 14:53:29 -05:00
if ( fullscreen_mode = = NULL )
fullscreen_mode = " output " ;
2012-01-14 10:02:30 -05:00
DLOG ( " toggling fullscreen, mode = %s \n " , fullscreen_mode ) ;
owindow * current ;
HANDLE_EMPTY_MATCH ;
TAILQ_FOREACH ( current , & owindows , owindows ) {
printf ( " matching: %p / %s \n " , current - > con , current - > con - > name ) ;
Implement a new parser for commands. (+test)
On the rationale of using a custom parser instead of a lex/yacc one, see this
quote from src/commands_parser.c:
We use a hand-written parser instead of lex/yacc because our commands are
easy for humans, not for computers. Thus, it’s quite hard to specify a
context-free grammar for the commands. A PEG grammar would be easier, but
there’s downsides to every PEG parser generator I have come accross so far.
This parser is basically a state machine which looks for literals or strings
and can push either on a stack. After identifying a literal or string, it
will either transition to the current state, to a different state, or call a
function (like cmd_move()).
Special care has been taken that error messages are useful and the code is
well testable (when compiled with -DTEST_PARSER it will output to stdout
instead of actually calling any function).
During the migration phase (I plan to completely switch to this parser before
4.2 will be released), the new parser will parse every command you send to
i3 and save the resulting call stack. Then, the old parser will parse your
input and actually execute the commands. Afterwards, both call stacks will be
compared and any differences will be logged.
The new parser works with 100% of the test suite and produces identical call
stacks.
2012-01-14 14:53:29 -05:00
con_toggle_fullscreen ( current - > con , ( strcmp ( fullscreen_mode , " global " ) = = 0 ? CF_GLOBAL : CF_OUTPUT ) ) ;
2012-01-14 10:02:30 -05:00
}
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' move < direction > [ < pixels > [ px ] ] ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_move_direction ( I3_CMD , char * direction , char * move_px ) {
2012-01-14 10:02:30 -05:00
// TODO: We could either handle this in the parser itself as a separate token (and make the stack typed) or we need a better way to convert a string to a number with error checking
int px = atoi ( move_px ) ;
/* TODO: make 'move' work with criteria. */
DLOG ( " moving in direction %s, px %s \n " , direction , move_px ) ;
if ( con_is_floating ( focused ) ) {
DLOG ( " floating move with %d pixels \n " , px ) ;
Rect newrect = focused - > parent - > rect ;
if ( strcmp ( direction , " left " ) = = 0 ) {
newrect . x - = px ;
} else if ( strcmp ( direction , " right " ) = = 0 ) {
newrect . x + = px ;
} else if ( strcmp ( direction , " up " ) = = 0 ) {
newrect . y - = px ;
} else if ( strcmp ( direction , " down " ) = = 0 ) {
newrect . y + = px ;
}
floating_reposition ( focused - > parent , newrect ) ;
} else {
2012-01-27 17:11:03 -05:00
tree_move ( ( strcmp ( direction , " right " ) = = 0 ? D_RIGHT :
( strcmp ( direction , " left " ) = = 0 ? D_LEFT :
( strcmp ( direction , " up " ) = = 0 ? D_UP :
D_DOWN ) ) ) ) ;
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
}
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' layout default | stacked | stacking | tabbed ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_layout ( I3_CMD , char * layout_str ) {
Implement a new parser for commands. (+test)
On the rationale of using a custom parser instead of a lex/yacc one, see this
quote from src/commands_parser.c:
We use a hand-written parser instead of lex/yacc because our commands are
easy for humans, not for computers. Thus, it’s quite hard to specify a
context-free grammar for the commands. A PEG grammar would be easier, but
there’s downsides to every PEG parser generator I have come accross so far.
This parser is basically a state machine which looks for literals or strings
and can push either on a stack. After identifying a literal or string, it
will either transition to the current state, to a different state, or call a
function (like cmd_move()).
Special care has been taken that error messages are useful and the code is
well testable (when compiled with -DTEST_PARSER it will output to stdout
instead of actually calling any function).
During the migration phase (I plan to completely switch to this parser before
4.2 will be released), the new parser will parse every command you send to
i3 and save the resulting call stack. Then, the old parser will parse your
input and actually execute the commands. Afterwards, both call stacks will be
compared and any differences will be logged.
The new parser works with 100% of the test suite and produces identical call
stacks.
2012-01-14 14:53:29 -05:00
if ( strcmp ( layout_str , " stacking " ) = = 0 )
layout_str = " stacked " ;
2012-01-14 10:02:30 -05:00
DLOG ( " changing layout to %s \n " , layout_str ) ;
owindow * current ;
int layout = ( strcmp ( layout_str , " default " ) = = 0 ? L_DEFAULT :
Implement a new parser for commands. (+test)
On the rationale of using a custom parser instead of a lex/yacc one, see this
quote from src/commands_parser.c:
We use a hand-written parser instead of lex/yacc because our commands are
easy for humans, not for computers. Thus, it’s quite hard to specify a
context-free grammar for the commands. A PEG grammar would be easier, but
there’s downsides to every PEG parser generator I have come accross so far.
This parser is basically a state machine which looks for literals or strings
and can push either on a stack. After identifying a literal or string, it
will either transition to the current state, to a different state, or call a
function (like cmd_move()).
Special care has been taken that error messages are useful and the code is
well testable (when compiled with -DTEST_PARSER it will output to stdout
instead of actually calling any function).
During the migration phase (I plan to completely switch to this parser before
4.2 will be released), the new parser will parse every command you send to
i3 and save the resulting call stack. Then, the old parser will parse your
input and actually execute the commands. Afterwards, both call stacks will be
compared and any differences will be logged.
The new parser works with 100% of the test suite and produces identical call
stacks.
2012-01-14 14:53:29 -05:00
( strcmp ( layout_str , " stacked " ) = = 0 ? L_STACKED :
2012-01-14 10:02:30 -05:00
L_TABBED ) ) ;
/* check if the match is empty, not if the result is empty */
if ( match_is_empty ( current_match ) )
con_set_layout ( focused - > parent , layout ) ;
else {
TAILQ_FOREACH ( current , & owindows , owindows ) {
DLOG ( " matching: %p / %s \n " , current - > con , current - > con - > name ) ;
con_set_layout ( current - > con , layout ) ;
}
}
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementaiton of ' exit ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_exit ( I3_CMD ) {
2012-01-14 10:02:30 -05:00
LOG ( " Exiting due to user command. \n " ) ;
exit ( 0 ) ;
/* unreached */
}
2012-01-27 17:32:40 -05:00
/*
* Implementaiton of ' reload ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_reload ( I3_CMD ) {
2012-01-14 10:02:30 -05:00
LOG ( " reloading \n " ) ;
kill_configerror_nagbar ( false ) ;
load_configuration ( conn , NULL , true ) ;
x_set_i3_atoms ( ) ;
/* Send an IPC event just in case the ws names have changed */
ipc_send_event ( " workspace " , I3_IPC_EVENT_WORKSPACE , " { \" change \" : \" reload \" } " ) ;
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementaiton of ' restart ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_restart ( I3_CMD ) {
2012-01-14 10:02:30 -05:00
LOG ( " restarting i3 \n " ) ;
i3_restart ( false ) ;
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementaiton of ' open ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_open ( I3_CMD ) {
2012-01-14 10:02:30 -05:00
LOG ( " opening new container \n " ) ;
Con * con = tree_open_con ( NULL , NULL ) ;
con_focus ( con ) ;
2012-02-07 17:38:21 -05:00
sasprintf ( & ( cmd_output - > json_output ) ,
" { \" success \" :true, \" id \" :%ld} " , ( long int ) con ) ;
2012-01-14 10:02:30 -05:00
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' focus output < output > ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_focus_output ( I3_CMD , char * name ) {
2012-01-14 10:02:30 -05:00
owindow * current ;
DLOG ( " name = %s \n " , name ) ;
HANDLE_EMPTY_MATCH ;
/* get the output */
Output * current_output = NULL ;
Output * output ;
TAILQ_FOREACH ( current , & owindows , owindows )
current_output = get_output_containing ( current - > con - > rect . x , current - > con - > rect . y ) ;
assert ( current_output ! = NULL ) ;
output = get_output_from_string ( current_output , name ) ;
if ( ! output ) {
LOG ( " No such output found. \n " ) ;
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
2012-01-14 10:02:30 -05:00
}
/* get visible workspace on output */
Con * ws = NULL ;
GREP_FIRST ( ws , output_get_content ( output - > con ) , workspace_is_visible ( child ) ) ;
2012-02-07 17:38:21 -05:00
if ( ! ws ) {
cmd_output - > json_output = sstrdup ( " { \" sucess \" : false} " ) ;
return ;
}
2012-01-14 10:02:30 -05:00
workspace_show ( ws ) ;
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-03-23 08:39:17 -04:00
/*
* Implementation of ' move [ window | container ] [ to ] [ absolute ] position < px > [ px ] < px > [ px ]
*
*/
void cmd_move_window_to_position ( I3_CMD , char * method , char * cx , char * cy ) {
int x = atoi ( cx ) ;
int y = atoi ( cy ) ;
if ( ! con_is_floating ( focused ) ) {
ELOG ( " Cannot change position. The window/container is not floating \n " ) ;
sasprintf ( & ( cmd_output - > json_output ) ,
" { \" success \" :false, \" error \" : \" Cannot change position. "
" The window/container is not floating. \" } " ) ;
return ;
}
if ( strcmp ( method , " absolute " ) = = 0 ) {
focused - > parent - > rect . x = x ;
focused - > parent - > rect . y = y ;
DLOG ( " moving to absolute position %d %d \n " , x , y ) ;
floating_maybe_reassign_ws ( focused - > parent ) ;
cmd_output - > needs_tree_render = true ;
}
if ( strcmp ( method , " position " ) = = 0 ) {
Rect newrect = focused - > parent - > rect ;
DLOG ( " moving to position %d %d \n " , x , y ) ;
newrect . x = x ;
newrect . y = y ;
floating_reposition ( focused - > parent , newrect ) ;
}
// XXX: default reply for now, make this a better reply
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
}
/*
* Implementation of ' move [ window | container ] [ to ] [ absolute ] position center
*
*/
void cmd_move_window_to_center ( I3_CMD , char * method ) {
if ( ! con_is_floating ( focused ) ) {
ELOG ( " Cannot change position. The window/container is not floating \n " ) ;
sasprintf ( & ( cmd_output - > json_output ) ,
" { \" success \" :false, \" error \" : \" Cannot change position. "
" The window/container is not floating. \" } " ) ;
}
if ( strcmp ( method , " absolute " ) = = 0 ) {
Rect * rect = & focused - > parent - > rect ;
DLOG ( " moving to absolute center \n " ) ;
rect - > x = croot - > rect . width / 2 - rect - > width / 2 ;
rect - > y = croot - > rect . height / 2 - rect - > height / 2 ;
floating_maybe_reassign_ws ( focused - > parent ) ;
cmd_output - > needs_tree_render = true ;
}
if ( strcmp ( method , " position " ) = = 0 ) {
Rect * wsrect = & con_get_workspace ( focused ) - > rect ;
Rect newrect = focused - > parent - > rect ;
DLOG ( " moving to center \n " ) ;
newrect . x = wsrect - > width / 2 - newrect . width / 2 ;
newrect . y = wsrect - > height / 2 - newrect . height / 2 ;
floating_reposition ( focused - > parent , newrect ) ;
}
// XXX: default reply for now, make this a better reply
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' move scratchpad ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_move_scratchpad ( I3_CMD ) {
2012-01-14 10:02:30 -05:00
DLOG ( " should move window to scratchpad \n " ) ;
owindow * current ;
HANDLE_EMPTY_MATCH ;
TAILQ_FOREACH ( current , & owindows , owindows ) {
DLOG ( " matching: %p / %s \n " , current - > con , current - > con - > name ) ;
scratchpad_move ( current - > con ) ;
}
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}
2012-01-27 17:32:40 -05:00
/*
* Implementation of ' scratchpad show ' .
*
*/
2012-02-07 17:38:21 -05:00
void cmd_scratchpad_show ( I3_CMD ) {
2012-01-14 10:02:30 -05:00
DLOG ( " should show scratchpad window \n " ) ;
owindow * current ;
if ( match_is_empty ( current_match ) ) {
scratchpad_show ( NULL ) ;
} else {
TAILQ_FOREACH ( current , & owindows , owindows ) {
DLOG ( " matching: %p / %s \n " , current - > con , current - > con - > name ) ;
scratchpad_show ( current - > con ) ;
}
}
2012-02-07 17:38:21 -05:00
cmd_output - > needs_tree_render = true ;
2012-01-14 10:02:30 -05:00
// XXX: default reply for now, make this a better reply
2012-02-07 17:38:21 -05:00
cmd_output - > json_output = sstrdup ( " { \" success \" : true} " ) ;
2012-01-14 10:02:30 -05:00
}