Skip to content

Commit

Permalink
implement ClientSend and TriggerAction
Browse files Browse the repository at this point in the history
  • Loading branch information
LBCrion committed Apr 13, 2022
1 parent fd45cad commit 77a14b6
Show file tree
Hide file tree
Showing 11 changed files with 217 additions and 24 deletions.
34 changes: 30 additions & 4 deletions doc/sfwbar.1
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,19 @@ conditions on the list must be satisfied. Supported conditions are:
\fBMinimized\fP, \fBMaximized\fP, \fBFocused\fP, \fBFullScreen\fP, \fBIdleInhibit\fP and
\fBUserState\fP
.sp
Actions can be activated upon receipt of a trigger from one of the client type
sources, using TriggerAction top\-level keyword. I.e.
.INDENT 0.0
.INDENT 3.5
.sp
.nf
.ft C
TriggerAction "mytrigger", Exec "MyCommand"
.ft P
.fi
.UNINDENT
.UNINDENT
.sp
Parameters are specified as strings immediately following the relevant action.
I.e. \fBMenu "WindowOps"\fP\&. Some actions apply to a window, if the action is
attached to taskbar button, the action will be applied to a window referenced
Expand Down Expand Up @@ -449,6 +462,11 @@ send a command over Sway IPC applicable to a current window
.B MpdCmd <string>
send a command to Music Player Daemon
.TP
.B ClientSend <string>, <string>
send a string to a client. The string will be written to client\(aqs standard
input for execClient clients or written into a socket for socketClient\(aqs.
The first parameter is the client id, the second is the string to send.
.TP
.B SetMonitor <string>
move bar to a given monitor
.TP
Expand Down Expand Up @@ -570,14 +588,22 @@ Read data from an output of a shell command
.B ExecClient
Read data from an executable, this source will read a bust of data
using it to populate the variables and emit a trigger event once done.
This source accepts two parameters, command to execute and a trigger
to emit.
This source accepts two parameters, command to execute and an id. The
id is used to address the socket via ClientSend and to identify a
trigger emitted upon variable updates.
USE RESPONSIBLY: If a trigger causes the client to receive new data
(i.e. by triggering a ClientSend command that in turn triggers response
from the source, you can end up with an infinite loop.
.TP
.B SocketClient
Read data from a socket, this source will read a bust of data
using it to populate the variables and emit a trigger event once done.
This source accepts two parameters, a socket address and a trigger
to emit.
This source accepts two parameters, a socket address and an id. The
id is used to address the socket via ClientSend and to identify a
trigger emitted upon variable updates.
USE RESPONSIBLY: If a trigger causes the client to receive new data
(i.e. by triggering a ClientSend command that in turn triggers response
from the source, you can end up with an infinite loop.
.TP
.B MpdClient
Read data from Music Player Daemon IPC (data is polled whenever MPD
Expand Down
26 changes: 22 additions & 4 deletions doc/sfwbar.rst
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,11 @@ conditions on the list must be satisfied. Supported conditions are:
``Minimized``, ``Maximized``, ``Focused``, ``FullScreen``, ``IdleInhibit`` and
``UserState``

Actions can be activated upon receipt of a trigger from one of the client type
sources, using TriggerAction top-level keyword. I.e. ::

TriggerAction "mytrigger", Exec "MyCommand"

Parameters are specified as strings immediately following the relevant action.
I.e. ``Menu "WindowOps"``. Some actions apply to a window, if the action is
attached to taskbar button, the action will be applied to a window referenced
Expand Down Expand Up @@ -369,6 +374,11 @@ SwayWinCmd <string>
MpdCmd <string>
send a command to Music Player Daemon

ClientSend <string>, <string>
send a string to a client. The string will be written to client's standard
input for execClient clients or written into a socket for socketClient's.
The first parameter is the client id, the second is the string to send.

SetMonitor <string>
move bar to a given monitor

Expand Down Expand Up @@ -475,14 +485,22 @@ Exec
ExecClient
Read data from an executable, this source will read a bust of data
using it to populate the variables and emit a trigger event once done.
This source accepts two parameters, command to execute and a trigger
to emit.
This source accepts two parameters, command to execute and an id. The
id is used to address the socket via ClientSend and to identify a
trigger emitted upon variable updates.
USE RESPONSIBLY: If a trigger causes the client to receive new data
(i.e. by triggering a ClientSend command that in turn triggers response
from the source, you can end up with an infinite loop.

SocketClient
Read data from a socket, this source will read a bust of data
using it to populate the variables and emit a trigger event once done.
This source accepts two parameters, a socket address and a trigger
to emit.
This source accepts two parameters, a socket address and an id. The
id is used to address the socket via ClientSend and to identify a
trigger emitted upon variable updates.
USE RESPONSIBLY: If a trigger causes the client to receive new data
(i.e. by triggering a ClientSend command that in turn triggers response
from the source, you can end up with an infinite loop.

MpdClient
Read data from Music Player Daemon IPC (data is polled whenever MPD
Expand Down
45 changes: 45 additions & 0 deletions src/action.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <gtk/gtk.h>

static GHashTable *functions;
static GHashTable *trigger_actions;

void action_function_add ( gchar *name, GList *actions )
{
Expand Down Expand Up @@ -147,6 +148,19 @@ guint16 action_state_build ( GtkWidget *widget, struct wt_window *win )
return state;
}

void action_client_send ( struct layout_action *action )
{
struct scan_file *file;

if(!action->addr || !action->command )
return;

file = scanner_file_get ( action->addr );

if(file)
g_io_channel_write_chars(file->out,action->command,-1,NULL,NULL);
}

void action_exec ( GtkWidget *widget, struct layout_action *action,
GdkEvent *event, struct wt_window *win, guint16 *istate )
{
Expand Down Expand Up @@ -251,6 +265,9 @@ void action_exec ( GtkWidget *widget, struct layout_action *action,
if(action->command && widget)
action_set_user_state(widget, action->command);
break;
case G_TOKEN_CLIENTSEND:
action_client_send(action);
break;
case G_TOKEN_FOCUS:
if(win)
wintree_focus(win->uid);
Expand Down Expand Up @@ -284,5 +301,33 @@ void action_free ( struct layout_action *action, GObject *old )
return;

g_free(action->command);
g_free(action->addr);
g_free(action);
}

void action_trigger_add ( struct layout_action *action, gchar *trigger )
{
void *old;

if(!trigger_actions)
trigger_actions = g_hash_table_new((GHashFunc)str_nhash,(GEqualFunc)str_nequal);

old = g_hash_table_lookup(trigger_actions,trigger);
if(old)
{
g_message("Action for trigger '%s' is already defined",trigger);
g_free(trigger);
action_free(action,NULL);
return;
}

g_hash_table_insert(trigger_actions, trigger, action);
}

struct layout_action *action_trigger_lookup ( gchar *trigger )
{
if(!trigger_actions)
return NULL;

return g_hash_table_lookup(trigger_actions,trigger);
}
23 changes: 16 additions & 7 deletions src/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@
gboolean client_event ( GIOChannel *chan, GIOCondition cond, gpointer data )
{
struct scan_file *file = data;
if(!file)
return FALSE;

g_io_channel_write_chars(chan,"\n",1,NULL,NULL);
if(file->scon)
g_io_channel_write_chars(chan,"\n",1,NULL,NULL);

if( cond & G_IO_ERR || cond & G_IO_HUP )
{
Expand Down Expand Up @@ -71,17 +70,20 @@ void client_socket ( struct scan_file *file )
file->scon = scon;

chan = g_io_channel_unix_new(g_socket_get_fd(sock));
g_io_channel_set_flags(chan,G_IO_FLAG_NONBLOCK,NULL);

if(chan)
{
g_io_channel_set_flags(chan,G_IO_FLAG_NONBLOCK,NULL);
g_io_add_watch(chan, G_IO_IN | G_IO_HUP | G_IO_ERR, client_event, file);
file->out = chan;
}
else
g_object_unref(scon);
}

void client_exec ( struct scan_file *file )
{
gint out;
gint in, out, err;
gchar **argv;
gint argc;
GIOChannel *chan;
Expand All @@ -90,13 +92,20 @@ void client_exec ( struct scan_file *file )
return;

if(!g_spawn_async_with_pipes(NULL,argv,NULL,G_SPAWN_SEARCH_PATH,NULL,NULL,NULL,
NULL, &out, NULL, NULL))
&in, &out, &err, NULL))
{
g_strfreev(argv);
return;
}
g_strfreev(argv);

chan = g_io_channel_unix_new(out);
if(chan)
g_io_add_watch(chan, G_IO_IN, client_event, file);
{
g_io_channel_set_flags(chan,G_IO_FLAG_NONBLOCK,NULL);
g_io_add_watch(chan, G_IO_IN | G_IO_HUP | G_IO_ERR, client_event, file);
g_io_add_watch(g_io_channel_unix_new(err), G_IO_IN, client_event, file);
file->out = g_io_channel_unix_new(in);
g_io_channel_set_flags(file->out,G_IO_FLAG_NONBLOCK,NULL);
}
}
67 changes: 66 additions & 1 deletion src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,8 @@ struct scan_file *config_scanner_source ( GScanner *scanner, gint source )
if( !strchr(fname,'*') && !strchr(fname,'?') )
file->flags |= VF_NOGLOB;
file_list = g_list_append(file_list,file);
if(file->trigger)
scanner_file_attach(trigger,file);

while(((gint)g_scanner_peek_next_token(scanner)!='}')&&
( (gint)g_scanner_peek_next_token ( scanner ) != G_TOKEN_EOF ))
Expand Down Expand Up @@ -640,6 +642,7 @@ gboolean config_action ( GScanner *scanner, struct layout_action *action )
case G_TOKEN_SETVALUE:
case G_TOKEN_SETSTYLE:
case G_TOKEN_SETTOOLTIP:
case G_TOKEN_CLIENTSEND:
type = scanner->token;
break;
default:
Expand Down Expand Up @@ -668,8 +671,31 @@ gboolean config_action ( GScanner *scanner, struct layout_action *action )
case G_TOKEN_SETBARSIZE:
case G_TOKEN_SETBARID:
case G_TOKEN_SETEXCLUSIVEZONE:
if(g_scanner_get_next_token(scanner) != G_TOKEN_STRING)
if(!config_expect_token(scanner, G_TOKEN_STRING,
"Missing argument in action"))
return FALSE;
g_scanner_get_next_token(scanner);
g_free(action->command);
action->command = g_strdup(scanner->value.v_string);
break;
case G_TOKEN_CLIENTSEND:
if(!config_expect_token(scanner, G_TOKEN_STRING,
"Missing argument in action"))
return FALSE;
g_scanner_get_next_token(scanner);
g_free(action->addr);
action->addr = g_strdup(scanner->value.v_string);
if(!config_expect_token(scanner, ',' ,
"Missing second argument in action"))
{
g_free(action->addr);
return FALSE;
}
g_scanner_get_next_token(scanner);
if(!config_expect_token(scanner, G_TOKEN_STRING,
"Missing second argument in action"))
return FALSE;
g_scanner_get_next_token(scanner);
g_free(action->command);
action->command = g_strdup(scanner->value.v_string);
break;
Expand Down Expand Up @@ -1249,6 +1275,38 @@ void config_define ( GScanner *scanner )
g_hash_table_insert(defines,ident,value);
}

void config_trigger_action ( GScanner *scanner )
{
gchar *trigger;
struct layout_action *action;

if(!config_expect_token(scanner, G_TOKEN_STRING,
"missing trigger in TriggerAction"))
return;
g_scanner_get_next_token(scanner);
trigger = g_strdup(scanner->value.v_string);
if(!config_expect_token(scanner, ',',
"missing ',' in TriggerAction"))
{
g_free(trigger);
return;
}

g_scanner_get_next_token(scanner);
action = g_malloc0(sizeof(struct layout_action));

if(!config_action(scanner,action))
{
g_free(trigger);
g_free(action);
g_scanner_error(scanner, "TriggerAction: invalid action");
return;
}

action_trigger_add(action,trigger);
config_optional_semicolon(scanner);
}

struct layout_widget *config_parse_toplevel ( GScanner *scanner,
gboolean layout)
{
Expand Down Expand Up @@ -1282,6 +1340,9 @@ struct layout_widget *config_parse_toplevel ( GScanner *scanner,
case G_TOKEN_DEFINE:
config_define(scanner);
break;
case G_TOKEN_TRIGGERACTION:
config_trigger_action(scanner);
break;
case G_TOKEN_FUNCTION:
config_function(scanner);
break;
Expand Down Expand Up @@ -1324,6 +1385,8 @@ struct layout_widget *config_parse_file ( gchar *fname, gchar *data,
g_scanner_scope_add_symbol(scanner,0, "Switcher",
(gpointer)G_TOKEN_SWITCHER );
g_scanner_scope_add_symbol(scanner,0, "Define", (gpointer)G_TOKEN_DEFINE );
g_scanner_scope_add_symbol(scanner,0, "TriggerAction",
(gpointer)G_TOKEN_TRIGGERACTION );
g_scanner_scope_add_symbol(scanner,0, "End", (gpointer)G_TOKEN_END );
g_scanner_scope_add_symbol(scanner,0, "File", (gpointer)G_TOKEN_FILE );
g_scanner_scope_add_symbol(scanner,0, "Exec", (gpointer)G_TOKEN_EXEC );
Expand Down Expand Up @@ -1424,6 +1487,8 @@ struct layout_widget *config_parse_file ( gchar *fname, gchar *data,
(gpointer)G_TOKEN_SETEXCLUSIVEZONE );
g_scanner_scope_add_symbol(scanner,0, "SetBarID",
(gpointer)G_TOKEN_SETBARID );
g_scanner_scope_add_symbol(scanner,0, "ClientSend",
(gpointer)G_TOKEN_CLIENTSEND );
g_scanner_scope_add_symbol(scanner,0, "Item", (gpointer)G_TOKEN_ITEM );
g_scanner_scope_add_symbol(scanner,0, "Separator",
(gpointer)G_TOKEN_SEPARATOR );
Expand Down
2 changes: 2 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ enum {
G_TOKEN_PLACER = G_TOKEN_LAST + 52,
G_TOKEN_SWITCHER = G_TOKEN_LAST + 53,
G_TOKEN_DEFINE = G_TOKEN_LAST + 54,
G_TOKEN_TRIGGERACTION = G_TOKEN_LAST + 55,
G_TOKEN_END = G_TOKEN_LAST + 59,
G_TOKEN_FILE = G_TOKEN_LAST + 61,
G_TOKEN_EXEC = G_TOKEN_LAST + 62,
Expand Down Expand Up @@ -78,6 +79,7 @@ enum {
G_TOKEN_CLOSE = G_TOKEN_LAST + 161,
G_TOKEN_SETEXCLUSIVEZONE = G_TOKEN_LAST + 162,
G_TOKEN_USERSTATE = G_TOKEN_LAST + 163,
G_TOKEN_CLIENTSEND = G_TOKEN_LAST + 164,
G_TOKEN_ITEM = G_TOKEN_LAST + 170,
G_TOKEN_SEPARATOR = G_TOKEN_LAST + 171,
G_TOKEN_SUBMENU = G_TOKEN_LAST + 172,
Expand Down
4 changes: 2 additions & 2 deletions src/expr.c
Original file line number Diff line number Diff line change
Expand Up @@ -563,10 +563,10 @@ gdouble expr_parse_num( GScanner *scanner )
switch((gint)g_scanner_get_next_token ( scanner ))
{
case '&':
val = val && expr_parse_num_compare ( scanner );
val = expr_parse_num_compare ( scanner ) && val;
break;
case '|':
val = val || expr_parse_num_compare ( scanner );
val = expr_parse_num_compare ( scanner ) || val;
break;
}
if(g_scanner_eof(scanner))
Expand Down
Loading

0 comments on commit 77a14b6

Please sign in to comment.