@ -1,6 +1,7 @@
# define _POSIX_C_SOURCE 200809L
# include <json-c/json.h>
# include <linux/input-event-codes.h>
# include <ctype.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
@ -24,24 +25,19 @@ void i3bar_block_unref(struct i3bar_block *block) {
}
}
static bool i3bar_parse_json ( struct status_line * status , const char * text ) {
static void i3bar_parse_json ( struct status_line * status ,
struct json_object * json_array ) {
struct i3bar_block * block , * tmp ;
wl_list_for_each_safe ( block , tmp , & status - > blocks , link ) {
wl_list_remove ( & block - > link ) ;
i3bar_block_unref ( block ) ;
}
json_object * results = json_tokener_parse ( text ) ;
if ( ! results ) {
status_error ( status , " [failed to parse i3bar json] " ) ;
return false ;
}
wlr_log ( WLR_DEBUG , " Got i3bar json: '%s' " , text ) ;
for ( size_t i = 0 ; i < json_object_array_length ( results ) ; + + i ) {
for ( size_t i = 0 ; i < json_object_array_length ( json_array ) ; + + i ) {
json_object * full_text , * short_text , * color , * min_width , * align , * urgent ;
json_object * name , * instance , * separator , * separator_block_width ;
json_object * background , * border , * border_top , * border_bottom ;
json_object * border_left , * border_right , * markup ;
json_object * json = json_object_array_get_idx ( results , i ) ;
json_object * json = json_object_array_get_idx ( json_array , i ) ;
if ( ! json ) {
continue ;
}
@ -110,96 +106,133 @@ static bool i3bar_parse_json(struct status_line *status, const char *text) {
json_object_get_int ( border_right ) : 1 ;
wl_list_insert ( & status - > blocks , & block - > link ) ;
}
json_object_put ( results ) ;
return true ;
}
bool i3bar_handle_readable ( struct status_line * status ) {
struct i3bar_protocol_state * state = & status - > i3bar_state ;
char * cur = & state - > buffer [ state - > buffer_index ] ;
ssize_t n = read ( status - > read_fd , cur ,
state - > buffer_size - state - > buffer_index ) ;
if ( n = = - 1 ) {
status_error ( status , " [failed to read from status command] " ) ;
return false ;
}
while ( ! status - > started ) { // look for opening bracket
for ( size_t c = 0 ; c < status - > buffer_index ; + + c ) {
if ( status - > buffer [ c ] = = ' [ ' ) {
status - > started = true ;
status - > buffer_index - = + + c ;
memmove ( status - > buffer , & status - > buffer [ c ] , status - > buffer_index ) ;
break ;
} else if ( ! isspace ( status - > buffer [ c ] ) ) {
status_error ( status , " [invalid json] " ) ;
return true ;
}
}
if ( status - > started ) {
break ;
}
if ( n = = ( ssize_t ) ( state - > buffer_size - state - > buffer_index ) ) {
state - > buffer_size = state - > buffer_size * 2 ;
char * new_buffer = realloc ( state - > buffer , state - > buffer_size ) ;
if ( ! new_buffer ) {
free ( state - > buffer ) ;
status_error ( status , " [failed to allocate buffer] " ) ;
errno = 0 ;
ssize_t read_bytes = read ( status - > read_fd , status - > buffer , status - > buffer_size ) ;
if ( read_bytes > - 1 ) {
status - > buffer_index = read_bytes ;
} else if ( errno = = EAGAIN ) {
return false ;
} else {
status_error ( status , " [error reading from status command] " ) ;
return true ;
}
state - > current_node + = new_buffer - state - > buffer ;
cur + = new_buffer - state - > buffer ;
state - > buffer = new_buffer ;
}
cur [ n ] = ' \0 ' ;
bool redraw = false ;
while ( * cur ) {
if ( state - > nodes [ state - > depth ] = = JSON_NODE_STRING ) {
if ( ! state - > escape & & * cur = = ' " ' ) {
- - state - > depth ;
struct json_object * last_object = NULL ;
struct json_object * test_object ;
size_t buffer_pos = 0 ;
while ( true ) {
// since the incoming stream is an infinite array
// parsing is split into two parts
// first, attempt to parse the current object, reading more if the
// parser indicates that the current object is incomplete, and failing
// if the parser fails
// second, look for separating comma, ignoring whitespace, failing if
// any other characters are encountered
if ( status - > expecting_comma ) {
for ( ; buffer_pos < status - > buffer_index ; + + buffer_pos ) {
if ( status - > buffer [ buffer_pos ] = = ' , ' ) {
status - > expecting_comma = false ;
+ + buffer_pos ;
break ;
} else if ( ! isspace ( status - > buffer [ buffer_pos ] ) ) {
status_error ( status , " [invalid i3bar json] " ) ;
return true ;
}
}
if ( buffer_pos < status - > buffer_index ) {
continue ; // look for new object without reading more input
}
state - > escape = ! state - > escape & & * cur = = ' \\ ' ;
buffer_pos = status - > buffer_index = 0 ;
} else {
switch ( * cur ) {
case ' [ ' :
+ + state - > depth ;
if ( state - > depth >
sizeof ( state - > nodes ) / sizeof ( state - > nodes [ 0 ] ) ) {
status_error ( status , " [i3bar json too deep] " ) ;
return false ;
}
state - > nodes [ state - > depth ] = JSON_NODE_ARRAY ;
if ( state - > depth = = 1 ) {
state - > current_node = cur ;
test_object = json_tokener_parse_ex ( status - > tokener ,
& status - > buffer [ buffer_pos ] , status - > buffer_index - buffer_pos ) ;
if ( json_tokener_get_error ( status - > tokener ) = = json_tokener_success ) {
if ( json_object_get_type ( test_object ) = = json_type_array ) {
if ( last_object ) {
json_object_put ( last_object ) ;
}
last_object = test_object ;
} else {
json_object_put ( test_object ) ;
}
break ;
case ' ] ' :
if ( state - > nodes [ state - > depth ] ! = JSON_NODE_ARRAY ) {
status_error ( status , " [failed to parse i3bar json] " ) ;
return false ;
buffer_pos + = status - > tokener - > char_offset ;
status - > expecting_comma = true ;
if ( buffer_pos < status - > buffer_index ) {
continue ; // look for comma without reading more input
}
- - state - > depth ;
if ( state - > depth = = 0 ) {
// cur[1] is valid since cur[0] != '\0'
char p = cur [ 1 ] ;
cur [ 1 ] = ' \0 ' ;
redraw = i3bar_parse_json (
status , state - > current_node ) | | redraw ;
cur [ 1 ] = p ;
memmove ( state - > buffer , cur ,
state - > buffer_size - ( cur - state - > buffer ) ) ;
cur = state - > buffer ;
state - > current_node = cur + 1 ;
buffer_pos = status - > buffer_index = 0 ;
} else if ( json_tokener_get_error ( status - > tokener ) = = json_tokener_continue ) {
if ( status - > buffer_index < status - > buffer_size ) {
// move the object to the start of the buffer
status - > buffer_index - = buffer_pos ;
memmove ( status - > buffer , & status - > buffer [ buffer_pos ] ,
status - > buffer_index ) ;
} else {
// expand buffer
status - > buffer_size * = 2 ;
char * new_buffer = realloc ( status - > buffer , status - > buffer_size ) ;
if ( new_buffer ) {
status - > buffer = new_buffer ;
} else {
free ( status - > buffer ) ;
status_error ( status , " [failed to allocate buffer] " ) ;
return true ;
}
}
break ;
case ' " ' :
+ + state - > depth ;
if ( state - > depth >
sizeof ( state - > nodes ) / sizeof ( state - > nodes [ 0 ] ) ) {
status_error ( status , " [i3bar json too deep] " ) ;
return false ;
}
state - > nodes [ state - > depth ] = JSON_NODE_STRING ;
break ;
} else {
status_error ( status , " [failed to parse i3bar json] " ) ;
return true ;
}
}
+ + cur ;
errno = 0 ;
ssize_t read_bytes = read ( status - > read_fd , & status - > buffer [ status - > buffer_index ] ,
status - > buffer_size - status - > buffer_index ) ;
if ( read_bytes > - 1 ) {
status - > buffer_index + = read_bytes ;
} else if ( errno = = EAGAIN ) {
break ;
} else {
status_error ( status , " [error reading from status command] " ) ;
return true ;
}
}
if ( last_object ) {
i3bar_parse_json ( status , last_object ) ;
json_object_put ( last_object ) ;
return true ;
} else {
return false ;
}
state - > buffer_index = cur - state - > buffer ;
return redraw ;
}
enum hotspot_event_handling i3bar_block_send_click ( struct status_line * status ,
struct i3bar_block * block , int x , int y , enum x11_button button ) {
wlr_log ( WLR_DEBUG , " block %s clicked " , block - > name ? block - > name : " (nil) " ) ;
if ( ! block - > name | | ! status - > i3bar_state . click_events ) {
if ( ! block - > name | | ! status - > click_events) {
return HOTSPOT_PROCESS ;
}