diff --git a/Makefile b/Makefile index bff6a40..915f7ac 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,7 @@ OBJ=src/riverguile.o $\ src/call-user-command-handler.o $\ src/call-idle-handler.o $\ src/call-exit-handler.o $\ + src/call-new-output-handler.o $\ src/load-script.o $\ protocol/river-layout-v3.o $\ protocol/ext-idle-notify-v1.o $\ diff --git a/doc/riverguile.1 b/doc/riverguile.1 index f47cae5..e9676ab 100644 --- a/doc/riverguile.1 +++ b/doc/riverguile.1 @@ -201,10 +201,35 @@ As of now, river only supports a single seat anyway. .RE . .P +\fBnew-output\fR +.RE +.RS +A handler installed under this key will be called everytime a new output appears. +On binding, it is also called for all outputs that already exist at that time. +.P +Installing a new-output handler will cause riverguile to run continously. +.P +The handler procderure must accept a single argument, the global name of the +output (integer). +.P +Here is an example of a simple new-output handler which merely logs new outputs: +.P +.RS +.EX +(\fBinstall-handler\fR 'new-output + (\fBlambda\fR (output) + (\fBdisplay\fR "New output: ") + (\fBdisplay\fR output) + (\fBnewline\fR))) +.EE +.RE +.RE +. +.P \fBexit\fR .RE .RS -This key allows you to installs a handler which is called when riverguile exits. +This key allows you to install a handler which is called when riverguile exits. .P The procedure takes no arguments. .P diff --git a/src/call-new-output-handler.c b/src/call-new-output-handler.c new file mode 100644 index 0000000..b4a0641 --- /dev/null +++ b/src/call-new-output-handler.c @@ -0,0 +1,36 @@ +#include +#include + +#include "output.h" +#include "riverguile.h" +#include "call-new-output-handler.h" + +static void *call_new_output_handler_inner (void *data) +{ + struct Call_new_output_handler_parameters *params = (struct Call_new_output_handler_parameters *)data; + return scm_call_1( + context.new_output_handler, + scm_from_uint32(params->output->name) + ); +} + +void *call_new_output_handler (void *data) +{ + assert(context.new_output_handler != NULL); + assert(scm_is_true(scm_procedure_p(context.new_output_handler)) == 1); + + /* Continuation barrier causes stack unwind on exceptions (i.e. errors + * in the user defined new-output handler) to stop here. Otherwise + * the entire stack created by scm_with_guile() would be unwound. This + * makes responding to exceptions nicer. + */ + SCM call_result = scm_c_with_continuation_barrier( + call_new_output_handler_inner, data + ); + + + if ( call_result == NULL ) + return (void *)"ERROR: An exception occured while calling the user-command handler.\n"; + + return NULL; +} diff --git a/src/call-new-output-handler.h b/src/call-new-output-handler.h new file mode 100644 index 0000000..a7422db --- /dev/null +++ b/src/call-new-output-handler.h @@ -0,0 +1,11 @@ +#ifndef RIVERGUILE_CALL_NEW_OUTPUT_HANDLER_H +#define RIVERGUILE_CALL_NEW_OUTPUT_HANDLER_H + +struct Call_new_output_handler_parameters +{ + struct Output *output; +}; + +void *call_new_output_handler (void *data); + +#endif diff --git a/src/load-script.c b/src/load-script.c index b20ee52..7357efc 100644 --- a/src/load-script.c +++ b/src/load-script.c @@ -9,6 +9,7 @@ #include "seat.h" #include "river-control-unstable-v1.h" +#include "call-new-output-handler.h" /** * ISO C forbids casting a function pointer to a void pointer because on some @@ -77,7 +78,29 @@ static SCM install_handler (SCM key, SCM proc) return SCM_UNSPECIFIED; } - if ( scm_is_eq(scm_from_utf8_symbol("layout-demand"), key) == 1 ) + if ( scm_is_eq(scm_from_utf8_symbol("new-output"), key) == 1 ) + { + context.mode = CONTINOUS; + context.new_output_handler = proc; + + /* Call handler for all already existing outputs. */ + struct Output *output; + wl_list_for_each(output, &context.outputs, link) + { + struct Call_new_output_handler_parameters params = { + .output = output, + }; + + void *res = scm_with_guile(call_new_output_handler, (void *)¶ms); + if ( res != NULL ) + { + fputs(res, stderr); + fputs("INFO: This error is not fatal.\n", stderr); + continue; + } + } + } + else if ( scm_is_eq(scm_from_utf8_symbol("layout-demand"), key) == 1 ) { if ( context.layout_manager == NULL ) { diff --git a/src/output.c b/src/output.c index 7ba2bec..89ae499 100644 --- a/src/output.c +++ b/src/output.c @@ -11,6 +11,7 @@ #include "output.h" #include "call-layout-demand-handler.h" #include "call-user-command-handler.h" +#include "call-new-output-handler.h" static void layout_handle_layout_demand (void *data, struct river_layout_v3 *river_layout_v3, uint32_t view_count, uint32_t width, uint32_t height, uint32_t tags, uint32_t serial) @@ -106,6 +107,21 @@ struct Output *output_create (struct wl_output *wl_output, uint32_t name) output->name = name; output->wl_output = wl_output; + /* If we have a new-output handler installed, send it. */ + if ( context.new_output_handler != NULL ) + { + struct Call_new_output_handler_parameters params = { + .output = output, + }; + + void *res = scm_with_guile(call_new_output_handler, (void *)¶ms); + if ( res != NULL ) + { + fputs(res, stderr); + fputs("INFO: This error is not fatal.\n", stderr); + } + } + /* 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. diff --git a/src/riverguile.c b/src/riverguile.c index dd2a4c2..16cef85 100644 --- a/src/riverguile.c +++ b/src/riverguile.c @@ -34,6 +34,7 @@ struct Context context = { * that is intended or will disappear in the future. Special care must be taken * to never call any scm_* function on these while they are NULL. */ + .new_output_handler = NULL, .layout_demand_handler = NULL, .user_command_handler = NULL, .exit_handler = NULL, diff --git a/src/riverguile.h b/src/riverguile.h index 85684cd..6fd163a 100644 --- a/src/riverguile.h +++ b/src/riverguile.h @@ -22,6 +22,7 @@ enum Riverguile_mode struct Context { + SCM new_output_handler; SCM layout_demand_handler; SCM user_command_handler; SCM exit_handler;