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
|
# riverguile
|
||||||
|
|
||||||
Layout generator for the [river](https://github.com/riverwm/river) Wayland
|
Scripting layer for the [river](https://github.com/riverwm/river) Wayland
|
||||||
server which uses [Guile Scheme](https://www.gnu.org/software/guile/) for
|
server using [Guile Scheme](https://www.gnu.org/software/guile/).
|
||||||
layouts. Its layout namespace is `riverguile`.
|
|
||||||
|
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
|
Uppon launch, riverguile tries to load a scheme script and checks the following
|
||||||
paths for it in the given order:
|
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.
|
Check the man page for more information.
|
||||||
|
|
||||||
Here is an example `layout.scm` file replicating rivertiles behavior of having
|
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
|
```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 split 0.55)
|
||||||
|
|
||||||
(define (layout:rows n x y w h)
|
(define (layout:rows n x y w h)
|
||||||
|
|||||||
196
doc/riverguile.1
196
doc/riverguile.1
@@ -2,7 +2,7 @@
|
|||||||
.
|
.
|
||||||
.SH NAME
|
.SH NAME
|
||||||
.P
|
.P
|
||||||
riverguile \- scheme powered layout generator for river
|
riverguile \- scheme powered scripting layer for river
|
||||||
.
|
.
|
||||||
.
|
.
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@@ -12,16 +12,17 @@ riverguile \- scheme powered layout generator for river
|
|||||||
.
|
.
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
.P
|
.P
|
||||||
Layout generator for the
|
Scripting layer for the
|
||||||
.BR river (1)
|
.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
|
.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
|
.P
|
||||||
Uppon launch, riverguile tries to eval a scheme script and checks the following
|
Uppon launch, riverguile tries to load the script from the following paths
|
||||||
paths for it in the given order:
|
in the given order:
|
||||||
.IP \(bu 2
|
.IP \(bu 2
|
||||||
\fBlayout.scm\fR
|
\fBlayout.scm\fR
|
||||||
.IP \(bu 2
|
.IP \(bu 2
|
||||||
@@ -30,80 +31,153 @@ paths for it in the given order:
|
|||||||
\fB$HOME/.config/river/layout.scm\fR
|
\fB$HOME/.config/river/layout.scm\fR
|
||||||
.IP \(bu 2
|
.IP \(bu 2
|
||||||
\fB/etc/riverguile/layout.scm\fR
|
\fB/etc/riverguile/layout.scm\fR
|
||||||
.
|
|
||||||
.P
|
.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
|
\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 parameter \fIkey\fR is a symbol indicating for which event to install
|
||||||
the procedure \fIproc\fR.
|
the procedure \fIproc\fR.
|
||||||
|
The following keys are currently evailable:
|
||||||
|
.
|
||||||
.P
|
.P
|
||||||
The key \fBlayout-demand\fR installs a handler for layout demands, which must
|
\fBlayout-demand\fR
|
||||||
accept five required arguments, which are, in order: The amount of views in the
|
.P
|
||||||
layout (integer), the available width (integer), the available height (integer),
|
.RS
|
||||||
the currently active tag set (integer representing a bitfield of size 32)
|
Installing a handler for this key allows the user to provide window layouts.
|
||||||
and the global name of the output the layout is needed for (integer).
|
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
|
The procedure must return a list containing exactly as many lists as there are
|
||||||
views in the layout.
|
views in the layout.
|
||||||
Each of those sublists must contains exactly four numerical values, which are
|
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.
|
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
|
Note that the numerical values do not need to be exact, riverguile takes care
|
||||||
of rounding and casting for you.
|
of rounding and casting for you.
|
||||||
A layout demand handler must be installed, otherwise riverguile will exit.
|
|
||||||
.P
|
.P
|
||||||
The key \fBuser-command\fR install a handler for user commands, which are the
|
Here is an example of a simple layout-demand handler which simply makes all
|
||||||
strings a user can send to layout generators.
|
windows use all available space:
|
||||||
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.
|
|
||||||
.
|
|
||||||
.P
|
.P
|
||||||
.RS
|
.RS
|
||||||
.EX
|
.EX
|
||||||
(install-handler 'user-command (lambda (cmd tags output)
|
(\fBinstall-handler\fR 'layout-demand
|
||||||
(eval-string cmd)))
|
(\fBlambda\fR (view-count width height tags output)
|
||||||
|
(\fBletrec\fR ((iter (\fBlambda\fR (n)
|
||||||
(install-handler 'layout-demand (lambda (view-count width height tags output)
|
|
||||||
(letrec ((iter (lambda (n)
|
|
||||||
(if (eq? 0 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 (1- n)))))))
|
||||||
(iter view-count))))
|
(iter view-count))))
|
||||||
.EE
|
.EE
|
||||||
.RE
|
.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
|
.SH SEE ALSO
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ static void *call_exit_handler_inner (void *data)
|
|||||||
return scm_call_0(context.exit_handler);
|
return scm_call_0(context.exit_handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *call_exit_handler (void)
|
void *call_exit_handler (void *data)
|
||||||
{
|
{
|
||||||
assert(context.exit_handler != NULL);
|
assert(context.exit_handler != NULL);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#ifndef RIVERGUILE_CALL_EXIT_HANDLER_H
|
#ifndef RIVERGUILE_CALL_EXIT_HANDLER_H
|
||||||
#define RIVERGUILE_CALL_EXIT_HANDLER_H
|
#define RIVERGUILE_CALL_EXIT_HANDLER_H
|
||||||
|
|
||||||
void *call_exit_handler (void);
|
void *call_exit_handler (void* data);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "riverguile.h"
|
#include "riverguile.h"
|
||||||
|
#include "output.h"
|
||||||
#include "seat.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 ( 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;
|
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)
|
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;
|
context.user_command_handler = proc;
|
||||||
|
}
|
||||||
else if ( scm_is_eq(scm_from_utf8_symbol("exit"), key) == 1)
|
else if ( scm_is_eq(scm_from_utf8_symbol("exit"), key) == 1)
|
||||||
context.exit_handler = proc;
|
context.exit_handler = proc;
|
||||||
else if ( scm_is_true(scm_string_prefix_p(scm_from_utf8_string("idle:"), scm_symbol_to_string(key),
|
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_from_int(5),
|
||||||
scm_from_int(0), scm_string_length(scm_symbol_to_string(key)))) == 1 )
|
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);
|
uint32_t ms = extract_ms_from_idle_key(key);
|
||||||
|
|
||||||
struct Idle *idle = calloc(1, sizeof(struct Idle));
|
if (!seat_add_idle(seat, proc, ms))
|
||||||
if ( idle == NULL )
|
|
||||||
{
|
{
|
||||||
scm_error_scm(
|
scm_error_scm(
|
||||||
scm_from_utf8_symbol("memory-allocation-error"),
|
scm_from_utf8_symbol("memory-allocation-error"),
|
||||||
@@ -98,10 +138,6 @@ static SCM install_handler (SCM key, SCM proc)
|
|||||||
);
|
);
|
||||||
return SCM_UNSPECIFIED;
|
return SCM_UNSPECIFIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
idle->ms = ms;
|
|
||||||
idle->handler = proc;
|
|
||||||
wl_list_insert(&context.unconfigured_idles, &idle->link);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -148,13 +184,13 @@ void *load_script (void *data)
|
|||||||
if ( call_result == NULL )
|
if ( call_result == NULL )
|
||||||
return (void *)"ERROR: Fatal error while loading layout script.\n";
|
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. */
|
/* Checked in the installer functions. */
|
||||||
|
if ( context.layout_demand_handler != NULL )
|
||||||
assert(scm_is_true(scm_procedure_p(context.layout_demand_handler)) == 1);
|
assert(scm_is_true(scm_procedure_p(context.layout_demand_handler)) == 1);
|
||||||
if ( context.user_command_handler != NULL )
|
if ( context.user_command_handler != NULL )
|
||||||
assert(scm_is_true(scm_procedure_p(context.user_command_handler)) == 1);
|
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;
|
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,
|
.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 )
|
assert(context.layout_manager != NULL);
|
||||||
return;
|
assert(context.layout_demand_handler != NULL);
|
||||||
if (output->configured)
|
|
||||||
return;
|
|
||||||
|
|
||||||
|
if ( output->layout != NULL )
|
||||||
|
return;
|
||||||
output->layout = river_layout_manager_v3_get_layout(
|
output->layout = river_layout_manager_v3_get_layout(
|
||||||
|
|
||||||
context.layout_manager, output->wl_output, "riverguile"
|
context.layout_manager, output->wl_output, "riverguile"
|
||||||
);
|
);
|
||||||
river_layout_v3_add_listener(output->layout, &layout_listener, output);
|
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)
|
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->name = name;
|
||||||
output->wl_output = wl_output;
|
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;
|
return output;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ struct Output
|
|||||||
struct wl_output *wl_output;
|
struct wl_output *wl_output;
|
||||||
struct river_layout_v3 *layout;
|
struct river_layout_v3 *layout;
|
||||||
uint32_t name;
|
uint32_t name;
|
||||||
bool configured;
|
|
||||||
|
|
||||||
/* Tags for the next user command. Due to backwards compatability, the
|
/* Tags for the next user command. Due to backwards compatability, the
|
||||||
* layout protocol sends us the currently active tags for a user command
|
* 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);
|
struct Output *output_create (struct wl_output *wl_output, uint32_t name);
|
||||||
void output_destroy (struct Output *output);
|
void output_destroy (struct Output *output);
|
||||||
void output_configure (struct Output *output);
|
void output_configure_layout (struct Output *output);
|
||||||
|
|
||||||
#endif
|
#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,
|
.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 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);
|
wl_callback_destroy(wl_callback);
|
||||||
context.sync_callback = NULL;
|
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.ret = EXIT_FAILURE;
|
||||||
context.loop = false;
|
context.loop = false;
|
||||||
return;
|
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: 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);
|
||||||
fputs("ERROR: Wayland server does not support ext-idle-notify-v1.\n", stderr);
|
|
||||||
fputs("INFO: This error is not fatal.\n", stderr);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
switch (context.mode)
|
||||||
{
|
{
|
||||||
struct Seat *seat;
|
case ONESHOT:
|
||||||
wl_list_for_each(seat, &context.seats, link)
|
/* 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;
|
break;
|
||||||
|
|
||||||
struct Idle *idle, *tmp_i;
|
case CONTINOUS:
|
||||||
wl_list_for_each_safe(idle, tmp_i, &context.unconfigured_idles, link)
|
fputs("INFO: At least one handler installed: Riverguile will run continously.\n", stderr);
|
||||||
idle_configure(idle, seat);
|
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)
|
static void handle_interrupt (int signum)
|
||||||
{
|
{
|
||||||
fputs("Killed 💀\n", stderr);
|
fputs("INFO: Killed 💀\n", stderr);
|
||||||
context.loop = false;
|
context.loop = false;
|
||||||
longjmp(skip_main_loop, 1);
|
longjmp(skip_main_loop, 1);
|
||||||
}
|
}
|
||||||
@@ -211,7 +223,6 @@ static void handle_error (int signum)
|
|||||||
kill(getpid(), signum);
|
kill(getpid(), signum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static char *formatted_buffer (const char *fmt, ...)
|
static char *formatted_buffer (const char *fmt, ...)
|
||||||
{
|
{
|
||||||
/* Determine length of formatted text. */
|
/* Determine length of formatted text. */
|
||||||
@@ -286,36 +297,29 @@ static char *get_script_path (void)
|
|||||||
if ( path != NULL )
|
if ( path != NULL )
|
||||||
free(path);
|
free(path);
|
||||||
|
|
||||||
fputs("ERROR: No layout script found.\n", stderr);
|
fputs("ERROR: No script found.\n", stderr);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
fputs("INFO: Welcome to riverguile!\n", stderr);
|
||||||
|
|
||||||
signal(SIGSEGV, handle_error);
|
signal(SIGSEGV, handle_error);
|
||||||
signal(SIGFPE, handle_error);
|
signal(SIGFPE, handle_error);
|
||||||
signal(SIGINT, handle_interrupt);
|
signal(SIGINT, handle_interrupt);
|
||||||
|
|
||||||
wl_list_init(&context.outputs);
|
wl_list_init(&context.outputs);
|
||||||
wl_list_init(&context.seats);
|
wl_list_init(&context.seats);
|
||||||
wl_list_init(&context.unconfigured_idles);
|
|
||||||
|
|
||||||
// TODO use argv[1] if present
|
// TODO use argv[1] if present
|
||||||
char *path = get_script_path();
|
context.path = get_script_path();
|
||||||
if ( path == NULL )
|
if ( context.path == NULL )
|
||||||
{
|
{
|
||||||
context.ret = EXIT_FAILURE;
|
context.ret = EXIT_FAILURE;
|
||||||
goto early_exit;
|
goto early_exit;
|
||||||
}
|
}
|
||||||
|
fprintf(stderr, "INFO: Found script: %s\n", context.path);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We query the display name here instead of letting wl_display_connect()
|
/* We query the display name here instead of letting wl_display_connect()
|
||||||
* figure it out itself, because libwayland (for legacy reasons) falls
|
* 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 );
|
while ( context.loop && wl_display_dispatch(context.wl_display) > 0 );
|
||||||
|
|
||||||
if ( context.exit_handler != NULL )
|
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);
|
void *res = scm_with_guile(call_exit_handler, NULL);
|
||||||
idle_destroy(idle);
|
if ( res != NULL )
|
||||||
|
{
|
||||||
|
fputs((char *)res, stderr);
|
||||||
|
context.ret = EXIT_FAILURE;
|
||||||
|
context.loop = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Output *output, *tmp_o;
|
struct Output *output, *tmp_o;
|
||||||
@@ -382,9 +387,10 @@ int main(int argc, char *argv[])
|
|||||||
wl_display_disconnect(context.wl_display);
|
wl_display_disconnect(context.wl_display);
|
||||||
|
|
||||||
early_exit:
|
early_exit:
|
||||||
if ( path != NULL )
|
if ( context.path != NULL )
|
||||||
free(path);
|
free(context.path);
|
||||||
|
|
||||||
|
fputs("INFO: Exiting.\n", stderr);
|
||||||
return context.ret;
|
return context.ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,12 +7,26 @@
|
|||||||
|
|
||||||
#include "seat.h"
|
#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
|
struct Context
|
||||||
{
|
{
|
||||||
SCM layout_demand_handler;
|
SCM layout_demand_handler;
|
||||||
SCM user_command_handler;
|
SCM user_command_handler;
|
||||||
SCM exit_handler;
|
SCM exit_handler;
|
||||||
|
|
||||||
|
enum Riverguile_mode mode;
|
||||||
bool loop;
|
bool loop;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@@ -25,8 +39,7 @@ struct Context
|
|||||||
struct wl_list seats;
|
struct wl_list seats;
|
||||||
struct wl_list outputs;
|
struct wl_list outputs;
|
||||||
|
|
||||||
/* Idles are created before we connect to the server and bind seats. */
|
char *path;
|
||||||
struct wl_list unconfigured_idles;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct Context context;
|
extern struct Context context;
|
||||||
|
|||||||
27
src/seat.c
27
src/seat.c
@@ -1,5 +1,6 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@@ -20,7 +21,9 @@ static void idle_notification_handle_idled (void *data,
|
|||||||
.idle = idle,
|
.idle = idle,
|
||||||
.event = 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,
|
static void idle_notification_handle_resumed (void *data,
|
||||||
@@ -33,7 +36,9 @@ static void idle_notification_handle_resumed (void *data,
|
|||||||
.idle = idle,
|
.idle = idle,
|
||||||
.event = RESUME,
|
.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 = {
|
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,
|
.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(
|
idle->idle_notification = ext_idle_notifier_v1_get_idle_notification(
|
||||||
context.idle_notifier,
|
context.idle_notifier,
|
||||||
idle->ms,
|
idle->ms,
|
||||||
seat->wl_seat
|
seat->wl_seat
|
||||||
);
|
);
|
||||||
|
|
||||||
ext_idle_notification_v1_add_listener(
|
ext_idle_notification_v1_add_listener(
|
||||||
idle->idle_notification,
|
idle->idle_notification,
|
||||||
&idle_notification_listener,
|
&idle_notification_listener,
|
||||||
idle
|
idle
|
||||||
);
|
);
|
||||||
|
|
||||||
wl_list_remove(&idle->link);
|
|
||||||
wl_list_insert(&seat->idles, &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?
|
// 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?
|
// maybe by entering guile mode one more time at exit?
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#ifndef RIVERGUILE_SEAT_H
|
#ifndef RIVERGUILE_SEAT_H
|
||||||
#define RIVERGUILE_SEAT_H
|
#define RIVERGUILE_SEAT_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <libguile.h>
|
#include <libguile.h>
|
||||||
|
|
||||||
@@ -24,8 +25,6 @@ struct Idle
|
|||||||
|
|
||||||
struct Seat *seat_create (struct wl_seat *wl_seat, uint32_t name);
|
struct Seat *seat_create (struct wl_seat *wl_seat, uint32_t name);
|
||||||
void seat_destroy (struct Seat *seat);
|
void seat_destroy (struct Seat *seat);
|
||||||
|
bool seat_add_idle (struct Seat *seat, SCM proc, uint32_t ms);
|
||||||
void idle_configure (struct Idle *idle, struct Seat *seat);
|
|
||||||
void idle_destroy (struct Idle *idle);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user