load script after initial registry burst
This makes the logic of installing idle handlers and layout handlers a bit cleaner and allows us to send wayland request from the script in the future.
This commit is contained in:
16
README.md
16
README.md
@@ -1,8 +1,10 @@
|
||||
# riverguile
|
||||
|
||||
Layout generator for the [river](https://github.com/riverwm/river) Wayland
|
||||
server which uses [Guile Scheme](https://www.gnu.org/software/guile/) for
|
||||
layouts. Its layout namespace is `riverguile`.
|
||||
Scripting layer for the [river](https://github.com/riverwm/river) Wayland
|
||||
server using [Guile Scheme](https://www.gnu.org/software/guile/).
|
||||
|
||||
Send commands to river and install handlers for various events, inclusing
|
||||
layout events.
|
||||
|
||||
Uppon launch, riverguile tries to load a scheme script and checks the following
|
||||
paths for it in the given order:
|
||||
@@ -17,9 +19,15 @@ is available, which can be used to install handlers to certain events.
|
||||
Check the man page for more information.
|
||||
|
||||
Here is an example `layout.scm` file replicating rivertiles behavior of having
|
||||
a main window and a stack of windows aside:
|
||||
a main window and a stack of windows aside while also automatically locking
|
||||
the screen after five minutes of inactivity.
|
||||
|
||||
```scheme
|
||||
(install-handler 'idle:300 (lambda (event)
|
||||
(cond ((eq? event 'idle) (system "swaylock &")
|
||||
(system "backlight.sh set-to 40"))
|
||||
((eq? event 'resume) (system "backlight.sh set-to 100")))))
|
||||
|
||||
(define split 0.55)
|
||||
|
||||
(define (layout:rows n x y w h)
|
||||
|
||||
196
doc/riverguile.1
196
doc/riverguile.1
@@ -2,7 +2,7 @@
|
||||
.
|
||||
.SH NAME
|
||||
.P
|
||||
riverguile \- scheme powered layout generator for river
|
||||
riverguile \- scheme powered scripting layer for river
|
||||
.
|
||||
.
|
||||
.SH SYNOPSIS
|
||||
@@ -12,16 +12,17 @@ riverguile \- scheme powered layout generator for river
|
||||
.
|
||||
.SH DESCRIPTION
|
||||
.P
|
||||
Layout generator for the
|
||||
Scripting layer for the
|
||||
.BR river (1)
|
||||
Wayland server which allows users to define layouts using guile scheme functions.
|
||||
.
|
||||
Wayland server.
|
||||
Allows the user to send commands to the Wayland server (probably river) and
|
||||
install handlers for events from a scheme script.
|
||||
.P
|
||||
The layout namespace is \fBriverguile\fB.
|
||||
.
|
||||
By default, riverguile will exit after the script has been loaded and evaluated.
|
||||
If certain handlers are installed it will run continously.
|
||||
.P
|
||||
Uppon launch, riverguile tries to eval a scheme script and checks the following
|
||||
paths for it in the given order:
|
||||
Uppon launch, riverguile tries to load the script from the following paths
|
||||
in the given order:
|
||||
.IP \(bu 2
|
||||
\fBlayout.scm\fR
|
||||
.IP \(bu 2
|
||||
@@ -30,80 +31,153 @@ paths for it in the given order:
|
||||
\fB$HOME/.config/river/layout.scm\fR
|
||||
.IP \(bu 2
|
||||
\fB/etc/riverguile/layout.scm\fR
|
||||
.
|
||||
.P
|
||||
In the context of this script, a special procedure
|
||||
.
|
||||
.
|
||||
.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 handlers to certain events.
|
||||
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:
|
||||
.
|
||||
.P
|
||||
The key \fBlayout-demand\fR installs a handler for layout demands, which must
|
||||
accept five required arguments, which are, in order: The amount of views in the
|
||||
layout (integer), the available width (integer), the available height (integer),
|
||||
the currently active tag set (integer representing a bitfield of size 32)
|
||||
and the global name of the output the layout is needed for (integer).
|
||||
\fBlayout-demand\fR
|
||||
.P
|
||||
.RS
|
||||
Installing a handler for this key allows the user to provide window layouts.
|
||||
All limitations of the river-layout-v3 protocol apply.
|
||||
The server will trigger this event when a new layout is required ("demanded").
|
||||
.P
|
||||
Installing a layout-demand handler will cause riverguile to run continously.
|
||||
.P
|
||||
The handler procedure must accept five required arguments, which are, in order:
|
||||
The amount of views in the layout (integer), the available width (integer), the
|
||||
available height (integer), the currently active tag set (integer representing a
|
||||
bitfield of size 32) and the global name of the output the layout is needed for
|
||||
(integer).
|
||||
The procedure must return a list containing exactly as many lists as there are
|
||||
views in the layout.
|
||||
Each of those sublists must contains exactly four numerical values, which are
|
||||
the x and y coordinates of the window as well as its width and height.
|
||||
Window positions and dimensions get applied to rivers window list top to bottom.
|
||||
Window positions and dimensions get applied to the window list top to bottom.
|
||||
.P
|
||||
Note that the numerical values do not need to be exact, riverguile takes care
|
||||
of rounding and casting for you.
|
||||
A layout demand handler must be installed, otherwise riverguile will exit.
|
||||
.P
|
||||
The key \fBuser-command\fR install a handler for user commands, which are the
|
||||
strings a user can send to layout generators.
|
||||
This handler procedure must accept three arguments, which are, in order:
|
||||
The command (string), the currently active tags (integer representing a
|
||||
bitfield of size 32) and the global name of the output (integer).
|
||||
This event can be used to change paramters of the layout.
|
||||
A new layout demand will be send right after this event.
|
||||
.P
|
||||
The key \fBidle:X\fR installs a handler which is called when the system has been
|
||||
idle for \fIX\fR seconds.
|
||||
As an example, use \fBidle:300\fR if you wish to be notified of your system
|
||||
being idle for five minutes.
|
||||
Idle in this case relates to user interaction;
|
||||
The system being idle for five minutes usually means there has been no input
|
||||
from the user in five minutes.
|
||||
Idle status however can also be influenced by other factors, for example by
|
||||
other programs inhibiting it (like video players), special sensors (user
|
||||
presence sensors) and is generally server-specific policy.
|
||||
The handler procedure must accept one argument, a symbol indicating the type
|
||||
of idle event.
|
||||
This symbol is either \fBidle\fR, indicating the system has been idle for the
|
||||
configured amount of time, or \fBresume\fR, indicating that the time intervall
|
||||
of the system being idle is over.
|
||||
Multiple idle handlers can be installed.
|
||||
Note: All idle events relate to the first advetised seat.
|
||||
As of now, river only supports a single seat anyway.
|
||||
.P
|
||||
The key \fBexit\fR installs a handler which is called when riverguile exits.
|
||||
The procedure takes no arguments.
|
||||
.
|
||||
.
|
||||
.SH EXAMPLE
|
||||
.P
|
||||
This is an example configuration, which installs a layout that simply assigns
|
||||
each window all usable space and a user command handler that tries to evaluate
|
||||
all send commands as scheme code.
|
||||
.
|
||||
Here is an example of a simple layout-demand handler which simply makes all
|
||||
windows use all available space:
|
||||
.P
|
||||
.RS
|
||||
.EX
|
||||
(install-handler 'user-command (lambda (cmd tags output)
|
||||
(eval-string cmd)))
|
||||
|
||||
(install-handler 'layout-demand (lambda (view-count width height tags output)
|
||||
(letrec ((iter (lambda (n)
|
||||
(\fBinstall-handler\fR 'layout-demand
|
||||
(\fBlambda\fR (view-count width height tags output)
|
||||
(\fBletrec\fR ((iter (\fBlambda\fR (n)
|
||||
(if (eq? 0 n)
|
||||
'()
|
||||
(append (list (list 0 0 width height))
|
||||
(\fBappend\fR (\fBlist\fR (\fBlist\fR 0 0 width height))
|
||||
(iter (1- n)))))))
|
||||
(iter view-count))))
|
||||
.EE
|
||||
.RE
|
||||
.RE
|
||||
.
|
||||
.P
|
||||
\fBuser-command\fR
|
||||
.P
|
||||
.RS
|
||||
User commands are intended to send commands to layout generators, allowing the
|
||||
user to update parameters of the layout live.
|
||||
Of course, nothing is stopping you from (ab-)using this event to trigger
|
||||
arbitrary scheme code on keypresses or on outside events, or from simply not
|
||||
using it at all.
|
||||
After a user-command has been received, the server can will trigger a
|
||||
layout-demand if there are visible windows.
|
||||
.P
|
||||
Installing a user-command handler will \fInot\fR cause riverguile to run continously.
|
||||
This event is an extension to the layout-demand event and as such it is invalid
|
||||
to install a user-command handler without also installing a layout-demant
|
||||
handler.
|
||||
.P
|
||||
The handler procedure must accept three arguments, which are, in order:
|
||||
The command (string), the currently active tags (integer representing a
|
||||
bitfield of size 32) and the global name of the output (integer).
|
||||
.P
|
||||
Here is an example of a simple user-command handler which simply evaluates the
|
||||
string as scheme code:
|
||||
.P
|
||||
.RS
|
||||
.EX
|
||||
(\fBinstall-handler\fR 'user-command
|
||||
(\fBlambda\fR (cmd tags output)
|
||||
(\fBeval-string\fR cmd)))
|
||||
.EE
|
||||
.RE
|
||||
.P
|
||||
Note that this is not necessarily good practice, but serves as a decent example.
|
||||
.RE
|
||||
.
|
||||
.P
|
||||
\fBidle:X\fR
|
||||
.P
|
||||
.RS
|
||||
A handler installed for this key will be triggered after the system has been
|
||||
idle for \fIX\fR seconds and once more once the system is no longer idle.
|
||||
.P
|
||||
Installing a layout-demand handler will cause riverguile to run continously.
|
||||
Multiple idle handlers can be installed.
|
||||
.P
|
||||
Idle state is server policy and may depend on a multitude of factors, but
|
||||
usually maps directly to the usage activity of input devices by the user.
|
||||
Certain programs may inhibit idle state, like for example video players.
|
||||
.P
|
||||
The handler procedure must accept one argument, a symbol indicating the type
|
||||
of idle event.
|
||||
This symbol is either \fBidle\fR, indicating the system has been idle for the
|
||||
configured amount of time, or \fBresume\fR, indicating that the system is no
|
||||
longer idle.
|
||||
.P
|
||||
Here is an example which will dim the screen after two minutes of inactiviy
|
||||
and lock it after fice:
|
||||
.P
|
||||
.RS
|
||||
.EX
|
||||
(\fBinstall-handler\fR 'idle:120
|
||||
(\fBlambda\fR (event)
|
||||
(\fBcond\fR ((\fBeq?\fR event 'idle) (\fBsystem\fR "light -S 20"))
|
||||
((\fBeq?\fR event 'resume) (\fBsystem\fR "light -S 100")))))
|
||||
|
||||
(\fBinstall-handler\fR 'idle:300
|
||||
(\fBlambda\fR (event)
|
||||
(if (\fBeq?\fR event 'idle) (\fBsystem\fR "swaylock &"))))
|
||||
.EE
|
||||
.RE
|
||||
.P
|
||||
Note: All idle events relate to the first advetised seat.
|
||||
As of now, river only supports a single seat anyway.
|
||||
.RE
|
||||
.
|
||||
.P
|
||||
\fBexit\fR
|
||||
.RE
|
||||
.RS
|
||||
This key allows you to installs a handler which is called when riverguile exits.
|
||||
.P
|
||||
The procedure takes no arguments.
|
||||
.P
|
||||
Here is an example which adds a message to the system log on exit:
|
||||
.P
|
||||
.RS
|
||||
.EX
|
||||
(\fBinstall-handler\fR 'exit
|
||||
(\fBlambda\fR ()
|
||||
(\fBsystem\fR "logger 'goodbye from riverguile'")))
|
||||
.EE
|
||||
.RE
|
||||
.RE
|
||||
|
||||
.
|
||||
.
|
||||
.SH SEE ALSO
|
||||
|
||||
@@ -9,7 +9,7 @@ static void *call_exit_handler_inner (void *data)
|
||||
return scm_call_0(context.exit_handler);
|
||||
}
|
||||
|
||||
void *call_exit_handler (void)
|
||||
void *call_exit_handler (void *data)
|
||||
{
|
||||
assert(context.exit_handler != NULL);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#ifndef RIVERGUILE_CALL_EXIT_HANDLER_H
|
||||
#define RIVERGUILE_CALL_EXIT_HANDLER_H
|
||||
|
||||
void *call_exit_handler (void);
|
||||
void *call_exit_handler (void* data);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "riverguile.h"
|
||||
#include "output.h"
|
||||
#include "seat.h"
|
||||
|
||||
/**
|
||||
@@ -75,19 +76,58 @@ static SCM install_handler (SCM key, SCM proc)
|
||||
}
|
||||
|
||||
if ( scm_is_eq(scm_from_utf8_symbol("layout-demand"), key) == 1 )
|
||||
{
|
||||
if ( context.layout_manager == NULL )
|
||||
{
|
||||
fputs("ERROR: Trying to install layout-demand handler but server does not support river-layout-v3.\n", stderr);
|
||||
fputs("INFO: This error is not fatal, but means riverguile will not provide any layout.\n", stderr);
|
||||
return SCM_BOOL_F;
|
||||
}
|
||||
|
||||
context.mode = CONTINOUS;
|
||||
context.layout_demand_handler = proc;
|
||||
|
||||
/* Configure all outputs to expose layouts. */
|
||||
struct Output *output;
|
||||
wl_list_for_each(output, &context.outputs, link)
|
||||
output_configure_layout(output);
|
||||
}
|
||||
else if ( scm_is_eq(scm_from_utf8_symbol("user-command"), key) == 1)
|
||||
{
|
||||
/* No need to check if the interface exists since it's only
|
||||
* used when a layout-demand handler is configured.
|
||||
*/
|
||||
context.user_command_handler = proc;
|
||||
}
|
||||
else if ( scm_is_eq(scm_from_utf8_symbol("exit"), key) == 1)
|
||||
context.exit_handler = proc;
|
||||
else if ( scm_is_true(scm_string_prefix_p(scm_from_utf8_string("idle:"), scm_symbol_to_string(key),
|
||||
scm_from_int(0), scm_from_int(5),
|
||||
scm_from_int(0), scm_string_length(scm_symbol_to_string(key)))) == 1 )
|
||||
{
|
||||
if ( context.idle_notifier == NULL )
|
||||
{
|
||||
fputs("ERROR: Trying to install idle handler but server does not support ext-idle-notify-v1.\n", stderr);
|
||||
fputs("INFO: This error is not fatal, but means riverguile will not be able to call any idle handler.\n", stderr);
|
||||
return SCM_BOOL_F;
|
||||
}
|
||||
if ( wl_list_length(&context.seats) == 0 )
|
||||
{
|
||||
fputs("ERROR: Trying to install idle handler but server did not advertise any seats.\n", stderr);
|
||||
fputs("INFO: This error is not fatal, but means riverguile will not be able to call any idle handler.\n", stderr);
|
||||
return SCM_BOOL_F;
|
||||
}
|
||||
|
||||
context.mode = CONTINOUS;
|
||||
|
||||
/* Just use the first seat. River only supports a single one anyway. */
|
||||
struct Seat *seat;
|
||||
wl_list_for_each(seat, &context.seats, link)
|
||||
break;
|
||||
|
||||
uint32_t ms = extract_ms_from_idle_key(key);
|
||||
|
||||
struct Idle *idle = calloc(1, sizeof(struct Idle));
|
||||
if ( idle == NULL )
|
||||
if (!seat_add_idle(seat, proc, ms))
|
||||
{
|
||||
scm_error_scm(
|
||||
scm_from_utf8_symbol("memory-allocation-error"),
|
||||
@@ -98,10 +138,6 @@ static SCM install_handler (SCM key, SCM proc)
|
||||
);
|
||||
return SCM_UNSPECIFIED;
|
||||
}
|
||||
|
||||
idle->ms = ms;
|
||||
idle->handler = proc;
|
||||
wl_list_insert(&context.unconfigured_idles, &idle->link);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -148,13 +184,13 @@ void *load_script (void *data)
|
||||
if ( call_result == NULL )
|
||||
return (void *)"ERROR: Fatal error while loading layout script.\n";
|
||||
|
||||
if ( context.layout_demand_handler == NULL )
|
||||
return (void *)"ERROR: No layout demand handler installed.\n";
|
||||
|
||||
/* Checked in the installer functions. */
|
||||
if ( context.layout_demand_handler != NULL )
|
||||
assert(scm_is_true(scm_procedure_p(context.layout_demand_handler)) == 1);
|
||||
if ( context.user_command_handler != NULL )
|
||||
assert(scm_is_true(scm_procedure_p(context.user_command_handler)) == 1);
|
||||
if ( context.exit_handler != NULL )
|
||||
assert(scm_is_true(scm_procedure_p(context.exit_handler)) == 1);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
20
src/output.c
20
src/output.c
@@ -80,18 +80,18 @@ static const struct river_layout_v3_listener layout_listener = {
|
||||
.user_command_tags = layout_handle_user_command_tags,
|
||||
};
|
||||
|
||||
void output_configure (struct Output *output)
|
||||
void output_configure_layout (struct Output *output)
|
||||
{
|
||||
if ( context.layout_manager == NULL )
|
||||
return;
|
||||
if (output->configured)
|
||||
return;
|
||||
assert(context.layout_manager != NULL);
|
||||
assert(context.layout_demand_handler != NULL);
|
||||
|
||||
if ( output->layout != NULL )
|
||||
return;
|
||||
output->layout = river_layout_manager_v3_get_layout(
|
||||
|
||||
context.layout_manager, output->wl_output, "riverguile"
|
||||
);
|
||||
river_layout_v3_add_listener(output->layout, &layout_listener, output);
|
||||
output->configured = true;
|
||||
}
|
||||
|
||||
struct Output *output_create (struct wl_output *wl_output, uint32_t name)
|
||||
@@ -105,7 +105,13 @@ struct Output *output_create (struct wl_output *wl_output, uint32_t name)
|
||||
|
||||
output->name = name;
|
||||
output->wl_output = wl_output;
|
||||
output_configure(output);
|
||||
|
||||
/* Only bind layout if we need it. Outputs advertised in the initial
|
||||
* registry burst, before the script is loaded, will always skip this,
|
||||
* however it is necessary for outputs added later.
|
||||
*/
|
||||
if ( context.layout_manager != NULL && context.layout_demand_handler != NULL )
|
||||
output_configure_layout(output);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ struct Output
|
||||
struct wl_output *wl_output;
|
||||
struct river_layout_v3 *layout;
|
||||
uint32_t name;
|
||||
bool configured;
|
||||
|
||||
/* Tags for the next user command. Due to backwards compatability, the
|
||||
* layout protocol sends us the currently active tags for a user command
|
||||
@@ -24,6 +23,6 @@ struct Output
|
||||
|
||||
struct Output *output_create (struct wl_output *wl_output, uint32_t name);
|
||||
void output_destroy (struct Output *output);
|
||||
void output_configure (struct Output *output);
|
||||
void output_configure_layout (struct Output *output);
|
||||
|
||||
#endif
|
||||
|
||||
104
src/riverguile.c
104
src/riverguile.c
@@ -118,54 +118,66 @@ static const struct wl_registry_listener registry_listener = {
|
||||
.global_remove = registry_handle_global_remove,
|
||||
};
|
||||
|
||||
static void sync_handle_done (void *, struct wl_callback *, uint32_t);
|
||||
static const struct wl_callback_listener sync_callback_listener = {
|
||||
.done = sync_handle_done,
|
||||
};
|
||||
|
||||
static void sync_handle_done (void *data, struct wl_callback *wl_callback, uint32_t other_data)
|
||||
{
|
||||
static int i = 0;
|
||||
if ( i == 1 )
|
||||
{
|
||||
assert(context.mode == ONESHOT);
|
||||
context.loop = false;
|
||||
return;
|
||||
}
|
||||
|
||||
wl_callback_destroy(wl_callback);
|
||||
context.sync_callback = NULL;
|
||||
|
||||
if ( context.layout_manager == NULL )
|
||||
/* Load the script after connecting to the server and binding interfaces
|
||||
* to allow calling Wayland requests from it.
|
||||
*/
|
||||
assert(context.path != NULL);
|
||||
void *res = scm_with_guile(load_script, (void *)context.path);
|
||||
if ( res != NULL )
|
||||
{
|
||||
fputs("ERROR: Wayland server does not support river-layout-v3.\n", stderr);
|
||||
fputs((char *)res, stderr);
|
||||
context.ret = EXIT_FAILURE;
|
||||
context.loop = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( context.idle_notifier == NULL )
|
||||
if ( context.layout_demand_handler == NULL
|
||||
&& context.user_command_handler != NULL )
|
||||
{
|
||||
if ( wl_list_length(&context.unconfigured_idles) > 0 )
|
||||
{
|
||||
fputs("ERROR: Wayland server does not support ext-idle-notify-v1.\n", stderr);
|
||||
fputs("INFO: This error is not fatal.\n", stderr);
|
||||
fputs("ERROR: Installing user-command handler without installing a layout-demand handler is not allowed.\n", stderr);
|
||||
fputs("INFO: This error is not fatal, but means riverguile will not provide any layout.\n", stderr);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
switch (context.mode)
|
||||
{
|
||||
struct Seat *seat;
|
||||
wl_list_for_each(seat, &context.seats, link)
|
||||
case ONESHOT:
|
||||
/* Oneshot mode. Sync again so we are sure that all commands
|
||||
* have been send, then exit.
|
||||
*/
|
||||
assert(i == 0);
|
||||
i++;
|
||||
context.sync_callback = wl_display_sync(context.wl_display);
|
||||
wl_callback_add_listener(context.sync_callback, &sync_callback_listener, NULL);
|
||||
fputs("INFO: No handlers installed: Riverguile will exit.\n", stderr);
|
||||
break;
|
||||
|
||||
struct Idle *idle, *tmp_i;
|
||||
wl_list_for_each_safe(idle, tmp_i, &context.unconfigured_idles, link)
|
||||
idle_configure(idle, seat);
|
||||
case CONTINOUS:
|
||||
fputs("INFO: At least one handler installed: Riverguile will run continously.\n", stderr);
|
||||
break;
|
||||
}
|
||||
|
||||
/* If outputs were registered before the river_layout_manager is
|
||||
* available, they won't have a river_layout, so we need to create
|
||||
* those here.
|
||||
*/
|
||||
struct Output *output;
|
||||
wl_list_for_each(output, &context.outputs, link)
|
||||
output_configure(output);
|
||||
}
|
||||
|
||||
static const struct wl_callback_listener sync_callback_listener = {
|
||||
.done = sync_handle_done,
|
||||
};
|
||||
|
||||
static void handle_interrupt (int signum)
|
||||
{
|
||||
fputs("Killed 💀\n", stderr);
|
||||
fputs("INFO: Killed 💀\n", stderr);
|
||||
context.loop = false;
|
||||
longjmp(skip_main_loop, 1);
|
||||
}
|
||||
@@ -211,7 +223,6 @@ static void handle_error (int signum)
|
||||
kill(getpid(), signum);
|
||||
}
|
||||
|
||||
|
||||
static char *formatted_buffer (const char *fmt, ...)
|
||||
{
|
||||
/* Determine length of formatted text. */
|
||||
@@ -286,36 +297,29 @@ static char *get_script_path (void)
|
||||
if ( path != NULL )
|
||||
free(path);
|
||||
|
||||
fputs("ERROR: No layout script found.\n", stderr);
|
||||
fputs("ERROR: No script found.\n", stderr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
fputs("INFO: Welcome to riverguile!\n", stderr);
|
||||
|
||||
signal(SIGSEGV, handle_error);
|
||||
signal(SIGFPE, handle_error);
|
||||
signal(SIGINT, handle_interrupt);
|
||||
|
||||
wl_list_init(&context.outputs);
|
||||
wl_list_init(&context.seats);
|
||||
wl_list_init(&context.unconfigured_idles);
|
||||
|
||||
// TODO use argv[1] if present
|
||||
char *path = get_script_path();
|
||||
if ( path == NULL )
|
||||
context.path = get_script_path();
|
||||
if ( context.path == NULL )
|
||||
{
|
||||
context.ret = EXIT_FAILURE;
|
||||
goto early_exit;
|
||||
}
|
||||
|
||||
void *res = scm_with_guile(load_script, (void *)path);
|
||||
if ( res != NULL )
|
||||
{
|
||||
fputs((char *)res, stderr);
|
||||
context.ret = EXIT_FAILURE;
|
||||
context.loop = false;
|
||||
goto early_exit;
|
||||
}
|
||||
fprintf(stderr, "INFO: Found script: %s\n", context.path);
|
||||
|
||||
/* We query the display name here instead of letting wl_display_connect()
|
||||
* figure it out itself, because libwayland (for legacy reasons) falls
|
||||
@@ -348,13 +352,14 @@ int main(int argc, char *argv[])
|
||||
while ( context.loop && wl_display_dispatch(context.wl_display) > 0 );
|
||||
|
||||
if ( context.exit_handler != NULL )
|
||||
call_exit_handler();
|
||||
|
||||
struct Idle *idle, *tmp_i;
|
||||
wl_list_for_each_safe(idle, tmp_i, &context.unconfigured_idles, link)
|
||||
{
|
||||
wl_list_remove(&idle->link);
|
||||
idle_destroy(idle);
|
||||
void *res = scm_with_guile(call_exit_handler, NULL);
|
||||
if ( res != NULL )
|
||||
{
|
||||
fputs((char *)res, stderr);
|
||||
context.ret = EXIT_FAILURE;
|
||||
context.loop = false;
|
||||
}
|
||||
}
|
||||
|
||||
struct Output *output, *tmp_o;
|
||||
@@ -382,9 +387,10 @@ int main(int argc, char *argv[])
|
||||
wl_display_disconnect(context.wl_display);
|
||||
|
||||
early_exit:
|
||||
if ( path != NULL )
|
||||
free(path);
|
||||
if ( context.path != NULL )
|
||||
free(context.path);
|
||||
|
||||
fputs("INFO: Exiting.\n", stderr);
|
||||
return context.ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,12 +7,26 @@
|
||||
|
||||
#include "seat.h"
|
||||
|
||||
enum Riverguile_mode
|
||||
{
|
||||
/* Riverguile is used only for configuring river and will exit after
|
||||
* the init script has been loaded.
|
||||
*/
|
||||
ONESHOT = 0,
|
||||
|
||||
/* Riverguile need to run continous, because we have handlers installed
|
||||
* f.e. for layouts or idle.
|
||||
*/
|
||||
CONTINOUS,
|
||||
};
|
||||
|
||||
struct Context
|
||||
{
|
||||
SCM layout_demand_handler;
|
||||
SCM user_command_handler;
|
||||
SCM exit_handler;
|
||||
|
||||
enum Riverguile_mode mode;
|
||||
bool loop;
|
||||
int ret;
|
||||
|
||||
@@ -25,8 +39,7 @@ struct Context
|
||||
struct wl_list seats;
|
||||
struct wl_list outputs;
|
||||
|
||||
/* Idles are created before we connect to the server and bind seats. */
|
||||
struct wl_list unconfigured_idles;
|
||||
char *path;
|
||||
};
|
||||
|
||||
extern struct Context context;
|
||||
|
||||
27
src/seat.c
27
src/seat.c
@@ -1,5 +1,6 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
@@ -20,7 +21,9 @@ static void idle_notification_handle_idled (void *data,
|
||||
.idle = idle,
|
||||
.event = IDLE,
|
||||
};
|
||||
call_idle_handler(¶ms);
|
||||
void *res = scm_with_guile(call_idle_handler, ¶ms);
|
||||
if ( res != NULL )
|
||||
fputs((char *)res, stderr);
|
||||
}
|
||||
|
||||
static void idle_notification_handle_resumed (void *data,
|
||||
@@ -33,7 +36,9 @@ static void idle_notification_handle_resumed (void *data,
|
||||
.idle = idle,
|
||||
.event = RESUME,
|
||||
};
|
||||
call_idle_handler(¶ms);
|
||||
void *res = scm_with_guile(call_idle_handler, ¶ms);
|
||||
if ( res != NULL )
|
||||
fputs((char *)res, stderr);
|
||||
}
|
||||
|
||||
static const struct ext_idle_notification_v1_listener idle_notification_listener = {
|
||||
@@ -41,28 +46,34 @@ static const struct ext_idle_notification_v1_listener idle_notification_listener
|
||||
.resumed = idle_notification_handle_resumed,
|
||||
};
|
||||
|
||||
void idle_configure (struct Idle *idle, struct Seat *seat)
|
||||
bool seat_add_idle (struct Seat *seat, SCM proc, uint32_t ms)
|
||||
{
|
||||
assert(idle->seat == NULL);
|
||||
assert(context.idle_notifier != NULL);
|
||||
|
||||
struct Idle *idle = calloc(1, sizeof(struct Idle));
|
||||
if ( idle == NULL )
|
||||
return false;
|
||||
idle->ms = ms;
|
||||
idle->seat = seat;
|
||||
idle->handler = proc;
|
||||
|
||||
idle->idle_notification = ext_idle_notifier_v1_get_idle_notification(
|
||||
context.idle_notifier,
|
||||
idle->ms,
|
||||
seat->wl_seat
|
||||
);
|
||||
|
||||
ext_idle_notification_v1_add_listener(
|
||||
idle->idle_notification,
|
||||
&idle_notification_listener,
|
||||
idle
|
||||
);
|
||||
|
||||
wl_list_remove(&idle->link);
|
||||
wl_list_insert(&seat->idles, &idle->link);
|
||||
idle->seat = seat;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void idle_destroy (struct Idle *idle)
|
||||
static void idle_destroy (struct Idle *idle)
|
||||
{
|
||||
// TODO XXX how do we tell guile it is allowed to clean up the handler?
|
||||
// maybe by entering guile mode one more time at exit?
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef RIVERGUILE_SEAT_H
|
||||
#define RIVERGUILE_SEAT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <wayland-client.h>
|
||||
#include <libguile.h>
|
||||
|
||||
@@ -24,8 +25,6 @@ struct Idle
|
||||
|
||||
struct Seat *seat_create (struct wl_seat *wl_seat, uint32_t name);
|
||||
void seat_destroy (struct Seat *seat);
|
||||
|
||||
void idle_configure (struct Idle *idle, struct Seat *seat);
|
||||
void idle_destroy (struct Idle *idle);
|
||||
bool seat_add_idle (struct Seat *seat, SCM proc, uint32_t ms);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user