add river-control-unstable-v1 support
This commit is contained in:
6
Makefile
6
Makefile
@@ -21,9 +21,11 @@ OBJ=src/riverguile.o $\
|
||||
src/call-exit-handler.o $\
|
||||
src/load-script.o $\
|
||||
protocol/river-layout-v3.o $\
|
||||
protocol/ext-idle-notify-v1.o
|
||||
protocol/ext-idle-notify-v1.o $\
|
||||
protocol/river-control-unstable-v1.o
|
||||
GEN=protocol/river-layout-v3.c protocol/river-layout-v3.h $\
|
||||
protocol/ext-idle-notify-v1.c protocol/ext-idle-notify-v1.h
|
||||
protocol/ext-idle-notify-v1.c protocol/ext-idle-notify-v1.h $\
|
||||
protocol/river-control-unstable-v1.c protocol/river-control-unstable-v1.h
|
||||
|
||||
all: riverguile
|
||||
|
||||
|
||||
@@ -23,22 +23,60 @@ If certain handlers are installed it will run continously.
|
||||
.P
|
||||
Uppon launch, riverguile tries to load the script from the following paths
|
||||
in the given order:
|
||||
.IP \(bu 2
|
||||
.IP \(bu 2
|
||||
\fBlayout.scm\fR
|
||||
.IP \(bu 2
|
||||
.IP \(bu 2
|
||||
\fB$XDG_CONFIG_HOME/river/layout.scm\fR
|
||||
.IP \(bu 2
|
||||
.IP \(bu 2
|
||||
\fB$HOME/.config/river/layout.scm\fR
|
||||
.IP \(bu 2
|
||||
.IP \(bu 2
|
||||
\fB/etc/riverguile/layout.scm\fR
|
||||
.
|
||||
.
|
||||
.SH COMMANDS
|
||||
.P
|
||||
Riverguile exposes the special procedure
|
||||
\fB(riverctl \fR\fIfirst\fR . \fIrest\fR\fB)\fR, which can be used to send
|
||||
commands to the server in a similar fashion to
|
||||
.BR riverctl (1).
|
||||
All arguments must be strings.
|
||||
.P
|
||||
Here is an example of using this procedure to instruct river to spawn an
|
||||
instance of the
|
||||
.BR foot (1)
|
||||
terminal emulator:
|
||||
.P
|
||||
.RS
|
||||
.EX
|
||||
(\fBriverctl\fR "spawn" "foot")
|
||||
.EE
|
||||
.RE
|
||||
.P
|
||||
To make using this procedure more ergonomic you likely want to wrap it in a
|
||||
macro like this:
|
||||
.P
|
||||
.RS
|
||||
.EX
|
||||
(\fBdefine-syntax\fR R
|
||||
(\fBsyntax-rules\fR ()
|
||||
((R first) (riverctl (\fBsymbol->string\fR 'first)))
|
||||
((R first . rest) (\fBapply\fR riverctl
|
||||
(\fBmap\fR \fBsymbol->string\fR
|
||||
(\fBappend\fR '(first) 'rest))))))
|
||||
|
||||
(R spawn foot)
|
||||
(R spawn \fB#{\fRnotify-send notification!\fB}#\fR)
|
||||
.EE
|
||||
.RE
|
||||
.P
|
||||
This macro can of course be further modified to suit your specific needs.
|
||||
.
|
||||
.
|
||||
.SH EVENT HANDLERS
|
||||
.P
|
||||
In the context of the script, a special procedure
|
||||
\fB(install-handler \fR\fIkey\fR \fIproc\fR\fB)\fR is available, which can be
|
||||
used to install event handlers.
|
||||
Riverguile exposes the special procedure
|
||||
\fB(install-handler \fR\fIkey\fR \fIproc\fR\fB)\fR, which can be used to install
|
||||
event handlers.
|
||||
The parameter \fIkey\fR is a symbol indicating for which event to install
|
||||
the procedure \fIproc\fR.
|
||||
The following keys are currently evailable:
|
||||
|
||||
85
protocol/river-control-unstable-v1.xml
Normal file
85
protocol/river-control-unstable-v1.xml
Normal file
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="river_control_unstable_v1">
|
||||
<copyright>
|
||||
Copyright 2020 The River Developers
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
</copyright>
|
||||
|
||||
<interface name="zriver_control_v1" version="1">
|
||||
<description summary="run compositor commands">
|
||||
This interface allows clients to run compositor commands and receive a
|
||||
success/failure response with output or a failure message respectively.
|
||||
|
||||
Each command is built up in a series of add_argument requests and
|
||||
executed with a run_command request. The first argument is the command
|
||||
to be run.
|
||||
|
||||
A complete list of commands should be made available in the man page of
|
||||
the compositor.
|
||||
</description>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy the river_control object">
|
||||
This request indicates that the client will not use the
|
||||
river_control object any more. Objects that have been created
|
||||
through this instance are not affected.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="add_argument">
|
||||
<description summary="add an argument to the current command">
|
||||
Arguments are stored by the server in the order they were sent until
|
||||
the run_command request is made.
|
||||
</description>
|
||||
<arg name="argument" type="string" summary="the argument to add"/>
|
||||
</request>
|
||||
|
||||
<request name="run_command">
|
||||
<description summary="run the current command">
|
||||
Execute the command built up using the add_argument request for the
|
||||
given seat.
|
||||
</description>
|
||||
<arg name="seat" type="object" interface="wl_seat"/>
|
||||
<arg name="callback" type="new_id" interface="zriver_command_callback_v1"
|
||||
summary="callback object"/>
|
||||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="zriver_command_callback_v1" version="1">
|
||||
<description summary="callback object">
|
||||
This object is created by the run_command request. Exactly one of the
|
||||
success or failure events will be sent. This object will be destroyed
|
||||
by the compositor after one of the events is sent.
|
||||
</description>
|
||||
|
||||
<event name="success" type="destructor">
|
||||
<description summary="command successful">
|
||||
Sent when the command has been successfully received and executed by
|
||||
the compositor. Some commands may produce output, in which case the
|
||||
output argument will be a non-empty string.
|
||||
</description>
|
||||
<arg name="output" type="string" summary="the output of the command"/>
|
||||
</event>
|
||||
|
||||
<event name="failure" type="destructor">
|
||||
<description summary="command failed">
|
||||
Sent when the command could not be carried out. This could be due to
|
||||
sending a non-existent command, no command, not enough arguments, too
|
||||
many arguments, invalid arguments, etc.
|
||||
</description>
|
||||
<arg name="failure_message" type="string"
|
||||
summary="a message explaining why failure occurred"/>
|
||||
</event>
|
||||
</interface>
|
||||
</protocol>
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "output.h"
|
||||
#include "seat.h"
|
||||
|
||||
#include "river-control-unstable-v1.h"
|
||||
|
||||
/**
|
||||
* ISO C forbids casting a function pointer to a void pointer because on some
|
||||
* architectures they have different sizes. However scm_c_define_gsubr() wants
|
||||
@@ -155,6 +157,72 @@ static SCM install_handler (SCM key, SCM proc)
|
||||
return SCM_BOOL_T;
|
||||
}
|
||||
|
||||
static SCM riverctl (SCM first, SCM rest)
|
||||
{
|
||||
if ( context.river_control == NULL )
|
||||
{
|
||||
fputs("ERROR: User script attempts to send river command but river-control-unstable-v1 not available.\n", stderr);
|
||||
fputs("INFO: This error is not fatal, however riverguile will not be able to send any commands to river.\n", stderr);
|
||||
return SCM_BOOL_F;
|
||||
}
|
||||
if ( wl_list_length(&context.seats) == 0 )
|
||||
{
|
||||
fputs("ERROR: User script attempts to send river command but server did not advertise any seats.\n", stderr);
|
||||
fputs("INFO: This error is not fatal, however riverguile will not be able to send any commands to river.\n", stderr);
|
||||
return SCM_BOOL_F;
|
||||
}
|
||||
|
||||
if ( scm_is_string(first) != 1 )
|
||||
goto error;
|
||||
|
||||
assert(scm_is_true(scm_list_p(rest)) == 1);
|
||||
const uint32_t rest_len = scm_to_uint32(scm_length(rest));
|
||||
for (uint32_t i = 0; i < rest_len; i++)
|
||||
if ( scm_is_string(scm_list_ref(rest, scm_from_uint32(i))) != 1 )
|
||||
goto error;
|
||||
|
||||
char *first_owned = scm_to_utf8_stringn(first, 0);
|
||||
zriver_control_v1_add_argument(context.river_control, first_owned);
|
||||
free(first_owned);
|
||||
|
||||
for (uint32_t i = 0; i < rest_len; i++)
|
||||
{
|
||||
char *owned = scm_to_utf8_stringn(scm_list_ref(rest, scm_from_uint32(i)), 0);
|
||||
zriver_control_v1_add_argument(context.river_control, owned);
|
||||
free(owned);
|
||||
}
|
||||
|
||||
/* Just use the first seat. River only supports a single one anyway. */
|
||||
struct Seat *seat;
|
||||
wl_list_for_each(seat, &context.seats, link)
|
||||
break;
|
||||
|
||||
/* While river does tell us about the status of the command, I do not
|
||||
* want to make use of that in riverguile for multiple reasons.
|
||||
* For one, the reporting is of course async, meaning we could not raise
|
||||
* a guile error in response to failed commands, even if we wanted to.
|
||||
* Riverguile could allow to install a handler for the callback, however
|
||||
* I do not sett the use. There are two riverctl commands that return
|
||||
* useful data as a string, however using that for scripting would be
|
||||
* very hacky and I prefer dedicated well designed interfaces. So we
|
||||
* just destroy the callback before it even fires.
|
||||
*/
|
||||
zriver_command_callback_v1_destroy(
|
||||
zriver_control_v1_run_command(context.river_control, seat->wl_seat)
|
||||
);
|
||||
|
||||
return SCM_BOOL_T;
|
||||
error:
|
||||
scm_error_scm(
|
||||
scm_from_utf8_symbol("wrong-type-arg"),
|
||||
scm_from_utf8_string("riverctl"),
|
||||
scm_from_utf8_string("All arguments must be strings."),
|
||||
SCM_BOOL_F,
|
||||
SCM_BOOL_F
|
||||
);
|
||||
return SCM_UNSPECIFIED;
|
||||
}
|
||||
|
||||
static void *load_script_inner (void *data)
|
||||
{
|
||||
const char *path = (char *)data;
|
||||
@@ -172,6 +240,7 @@ void *load_script (void *data)
|
||||
/* Note: All guile objects are garbage collected. */
|
||||
|
||||
scm_c_define_gsubr_fix("install-handler", 2, 0, 0, install_handler);
|
||||
scm_c_define_gsubr_fix("riverctl", 1, 0, 1, riverctl);
|
||||
|
||||
/* Continuation barrier causes stack unwind on exceptions to stop here.
|
||||
* Otherwise the entire stack created by scm_with_guile() would be
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "river-layout-v3.h"
|
||||
#include "ext-idle-notify-v1.h"
|
||||
#include "river-control-unstable-v1.h"
|
||||
|
||||
#include "load-script.h"
|
||||
#include "call-exit-handler.h"
|
||||
@@ -35,6 +36,7 @@ struct Context context = {
|
||||
*/
|
||||
.layout_demand_handler = NULL,
|
||||
.user_command_handler = NULL,
|
||||
.exit_handler = NULL,
|
||||
|
||||
.loop = true,
|
||||
.ret = EXIT_SUCCESS,
|
||||
@@ -51,6 +53,12 @@ static void registry_handle_global (void *data, struct wl_registry *registry,
|
||||
registry, name, &river_layout_manager_v3_interface, 2
|
||||
);
|
||||
}
|
||||
else if ( strcmp(interface, zriver_control_v1_interface.name) == 0 )
|
||||
{
|
||||
context.river_control = wl_registry_bind(
|
||||
registry, name, &zriver_control_v1_interface, 1
|
||||
);
|
||||
}
|
||||
else if ( strcmp(interface, ext_idle_notifier_v1_interface.name) == 0 )
|
||||
{
|
||||
context.idle_notifier = wl_registry_bind(
|
||||
@@ -380,6 +388,8 @@ int main(int argc, char *argv[])
|
||||
ext_idle_notifier_v1_destroy(context.idle_notifier);
|
||||
if ( context.layout_manager != NULL )
|
||||
river_layout_manager_v3_destroy(context.layout_manager);
|
||||
if ( context.river_control != NULL )
|
||||
zriver_control_v1_destroy(context.river_control);
|
||||
if ( context.sync_callback != NULL )
|
||||
wl_callback_destroy(context.sync_callback);
|
||||
if ( context.wl_registry != NULL )
|
||||
|
||||
@@ -35,6 +35,7 @@ struct Context
|
||||
struct wl_callback *sync_callback;
|
||||
struct river_layout_manager_v3 *layout_manager;
|
||||
struct ext_idle_notifier_v1 *idle_notifier;
|
||||
struct zriver_control_v1 *river_control;
|
||||
|
||||
struct wl_list seats;
|
||||
struct wl_list outputs;
|
||||
|
||||
Reference in New Issue
Block a user