Files
riverguile/src/load-script.c
Leon Henrik Plickat 153b5e0858 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.
2024-01-07 06:25:56 +01:00

198 lines
5.7 KiB
C

#include <assert.h>
#include <stdio.h>
#include <stdint.h>
#include <libguile.h>
#include <string.h>
#include "riverguile.h"
#include "output.h"
#include "seat.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
* a function pointer as a void pointer, which trips -Wpedantic. Compiling C
* without pedantic errors is not a reasonable option in my opinion, so instead
* we'll have to resort to this hack.
*/
#define scm_c_define_gsubr_fix(NAME, REQ, OPT, RST, FN) \
{ const long int ptr = (long int)FN; scm_c_define_gsubr(NAME, REQ, OPT, RST, (void *)ptr); }
static uint32_t extract_ms_from_idle_key (SCM key)
{
SCM key_split = scm_string_split(
scm_symbol_to_string(key),
scm_to_char_set(scm_from_utf8_string(":"))
);
if ( scm_to_uint32(scm_length(key_split)) != 2 )
goto error;
SCM maybe_number = scm_string_to_number(scm_cadr(key_split), scm_from_int(10));
if ( scm_is_false(maybe_number) == 1 )
goto error;
int32_t ms = scm_to_int32(maybe_number);
if ( ms < 0 )
goto error;
return (uint32_t)ms * 1000;
error:
scm_error_scm(
scm_from_utf8_symbol("wrong-type-arg"),
scm_from_utf8_string("install-handler"),
scm_from_utf8_string("First argument: Excpected 'idle:<positive integer>."),
SCM_BOOL_F,
scm_list_1(key)
);
return 0;
}
static SCM install_handler (SCM key, SCM proc)
{
if ( scm_is_false(scm_symbol_p(key)) == 1 )
{
scm_error_scm(
scm_from_utf8_symbol("wrong-type-arg"),
scm_from_utf8_string("install-handler"),
scm_from_utf8_string("First argument must be a symbol."),
SCM_BOOL_F,
scm_list_1(key)
);
return SCM_UNSPECIFIED;
}
// TODO check if the procedure has the right amount of arguments.
if ( scm_is_false(scm_procedure_p(proc)) == 1 )
{
scm_error_scm(
scm_from_utf8_symbol("wrong-type-arg"),
scm_from_utf8_string("install-handler"),
scm_from_utf8_string("Second argument must be a procedure."),
SCM_BOOL_F,
scm_list_1(proc)
);
return SCM_UNSPECIFIED;
}
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);
if (!seat_add_idle(seat, proc, ms))
{
scm_error_scm(
scm_from_utf8_symbol("memory-allocation-error"),
scm_from_utf8_string("install-handler"),
SCM_BOOL_F,
SCM_BOOL_F,
SCM_BOOL_F
);
return SCM_UNSPECIFIED;
}
}
else
{
scm_error_scm(
// TODO should this be 'misc-error instead?
scm_from_utf8_symbol("out-of-range"),
scm_from_utf8_string("install-handler"),
scm_from_utf8_string("Unknown key: ~A"),
scm_list_1(key),
scm_list_1(key)
);
return SCM_UNSPECIFIED;
}
return SCM_BOOL_T;
}
static void *load_script_inner (void *data)
{
const char *path = (char *)data;
/* scm_primitive_load_path() searches guiles load-path when encountering
* a relative path. That should never happen here though.
*/
assert(path[0] == '/');
return scm_primitive_load_path(scm_from_utf8_string(path));
}
void *load_script (void *data)
{
/* Note: All guile objects are garbage collected. */
scm_c_define_gsubr_fix("install-handler", 2, 0, 0, install_handler);
/* Continuation barrier causes stack unwind on exceptions 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(
load_script_inner, data
);
if ( call_result == NULL )
return (void *)"ERROR: Fatal error while loading layout script.\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;
}