implement ext-idle-notify-v1
This commit is contained in:
8
Makefile
8
Makefile
@@ -14,11 +14,15 @@ LIBS=-lrt $\
|
|||||||
$(shell pkg-config --libs wayland-client) $\
|
$(shell pkg-config --libs wayland-client) $\
|
||||||
$(shell guile-config link)
|
$(shell guile-config link)
|
||||||
OBJ=src/riverguile.o $\
|
OBJ=src/riverguile.o $\
|
||||||
|
src/seat.o src/output.o $\
|
||||||
src/call-layout-demand-handler.o $\
|
src/call-layout-demand-handler.o $\
|
||||||
src/call-user-command-handler.o $\
|
src/call-user-command-handler.o $\
|
||||||
|
src/call-idle-handler.o $\
|
||||||
src/load-script.o $\
|
src/load-script.o $\
|
||||||
protocol/river-layout-v3.o
|
protocol/river-layout-v3.o $\
|
||||||
GEN=protocol/river-layout-v3.c protocol/river-layout-v3.h
|
protocol/ext-idle-notify-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
|
||||||
|
|
||||||
all: riverguile
|
all: riverguile
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,25 @@ The command (string), the currently active tags (integer representing a
|
|||||||
bitfield of size 32) and the global name of the output (integer).
|
bitfield of size 32) and the global name of the output (integer).
|
||||||
This event can be used to change paramters of the layout.
|
This event can be used to change paramters of the layout.
|
||||||
A new layout demand will be send right after this event.
|
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.
|
||||||
.
|
.
|
||||||
.
|
.
|
||||||
.SH EXAMPLE
|
.SH EXAMPLE
|
||||||
|
|||||||
102
protocol/ext-idle-notify-v1.xml
Normal file
102
protocol/ext-idle-notify-v1.xml
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<protocol name="ext_idle_notify_v1">
|
||||||
|
<copyright>
|
||||||
|
Copyright © 2015 Martin Gräßlin
|
||||||
|
Copyright © 2022 Simon Ser
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice (including the next
|
||||||
|
paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<interface name="ext_idle_notifier_v1" version="1">
|
||||||
|
<description summary="idle notification manager">
|
||||||
|
This interface allows clients to monitor user idle status.
|
||||||
|
|
||||||
|
After binding to this global, clients can create ext_idle_notification_v1
|
||||||
|
objects to get notified when the user is idle for a given amount of time.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the manager">
|
||||||
|
Destroy the manager object. All objects created via this interface
|
||||||
|
remain valid.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="get_idle_notification">
|
||||||
|
<description summary="create a notification object">
|
||||||
|
Create a new idle notification object.
|
||||||
|
|
||||||
|
The notification object has a minimum timeout duration and is tied to a
|
||||||
|
seat. The client will be notified if the seat is inactive for at least
|
||||||
|
the provided timeout. See ext_idle_notification_v1 for more details.
|
||||||
|
|
||||||
|
A zero timeout is valid and means the client wants to be notified as
|
||||||
|
soon as possible when the seat is inactive.
|
||||||
|
</description>
|
||||||
|
<arg name="id" type="new_id" interface="ext_idle_notification_v1"/>
|
||||||
|
<arg name="timeout" type="uint" summary="minimum idle timeout in msec"/>
|
||||||
|
<arg name="seat" type="object" interface="wl_seat"/>
|
||||||
|
</request>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="ext_idle_notification_v1" version="1">
|
||||||
|
<description summary="idle notification">
|
||||||
|
This interface is used by the compositor to send idle notification events
|
||||||
|
to clients.
|
||||||
|
|
||||||
|
Initially the notification object is not idle. The notification object
|
||||||
|
becomes idle when no user activity has happened for at least the timeout
|
||||||
|
duration, starting from the creation of the notification object. User
|
||||||
|
activity may include input events or a presence sensor, but is
|
||||||
|
compositor-specific. If an idle inhibitor is active (e.g. another client
|
||||||
|
has created a zwp_idle_inhibitor_v1 on a visible surface), the compositor
|
||||||
|
must not make the notification object idle.
|
||||||
|
|
||||||
|
When the notification object becomes idle, an idled event is sent. When
|
||||||
|
user activity starts again, the notification object stops being idle,
|
||||||
|
a resumed event is sent and the timeout is restarted.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the notification object">
|
||||||
|
Destroy the notification object.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="idled">
|
||||||
|
<description summary="notification object is idle">
|
||||||
|
This event is sent when the notification object becomes idle.
|
||||||
|
|
||||||
|
It's a compositor protocol error to send this event twice without a
|
||||||
|
resumed event in-between.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="resumed">
|
||||||
|
<description summary="notification object is no longer idle">
|
||||||
|
This event is sent when the notification object stops being idle.
|
||||||
|
|
||||||
|
It's a compositor protocol error to send this event twice without an
|
||||||
|
idled event in-between. It's a compositor protocol error to send this
|
||||||
|
event prior to any idled event.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
</interface>
|
||||||
|
</protocol>
|
||||||
36
src/call-idle-handler.c
Normal file
36
src/call-idle-handler.c
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#include <libguile.h>
|
||||||
|
|
||||||
|
#include "seat.h"
|
||||||
|
#include "call-idle-handler.h"
|
||||||
|
|
||||||
|
static void *call_idle_handler_inner (void *data)
|
||||||
|
{
|
||||||
|
struct Call_idle_handler_parameters *params =
|
||||||
|
(struct Call_idle_handler_parameters *)data;
|
||||||
|
|
||||||
|
SCM event;
|
||||||
|
switch (params->event)
|
||||||
|
{
|
||||||
|
case IDLE: event = scm_from_utf8_symbol("idle"); break;
|
||||||
|
case RESUME: event = scm_from_utf8_symbol("resume"); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return scm_call_1(params->idle->handler, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *call_idle_handler (void *data)
|
||||||
|
{
|
||||||
|
/* Continuation barrier causes stack unwind on exceptions (i.e. errors
|
||||||
|
* in the user defined ide 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_idle_handler_inner, data
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( call_result == NULL )
|
||||||
|
return (void *)"ERROR: An exception occured while calling the user-command handler.\n";
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
20
src/call-idle-handler.h
Normal file
20
src/call-idle-handler.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#ifndef RIVERGUILE_CALL_IDLE_HANDLER_H
|
||||||
|
#define RIVERGUILE_CALL_IDLE_HANDLER_H
|
||||||
|
|
||||||
|
#include "seat.h"
|
||||||
|
|
||||||
|
enum Idle_handler_event
|
||||||
|
{
|
||||||
|
IDLE,
|
||||||
|
RESUME,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Call_idle_handler_parameters
|
||||||
|
{
|
||||||
|
struct Idle *idle;
|
||||||
|
enum Idle_handler_event event;
|
||||||
|
};
|
||||||
|
|
||||||
|
void *call_idle_handler (void *data);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,17 +1,16 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <libguile.h>
|
#include <libguile.h>
|
||||||
|
|
||||||
|
#include "riverguile.h"
|
||||||
|
#include "output.h"
|
||||||
#include "river-layout-v3.h"
|
#include "river-layout-v3.h"
|
||||||
|
|
||||||
#include "call-layout-demand-handler.h"
|
#include "call-layout-demand-handler.h"
|
||||||
|
|
||||||
extern SCM layout_demand_handler;
|
|
||||||
|
|
||||||
static void *call_layout_demand_handler_inner (void *data)
|
static void *call_layout_demand_handler_inner (void *data)
|
||||||
{
|
{
|
||||||
struct Call_layout_demand_handler_parameters *params = (struct Call_layout_demand_handler_parameters *)data;
|
struct Call_layout_demand_handler_parameters *params = (struct Call_layout_demand_handler_parameters *)data;
|
||||||
return scm_call_5(
|
return scm_call_5(
|
||||||
layout_demand_handler,
|
context.layout_demand_handler,
|
||||||
scm_from_uint32(params->view_count),
|
scm_from_uint32(params->view_count),
|
||||||
scm_from_uint32(params->width),
|
scm_from_uint32(params->width),
|
||||||
scm_from_uint32(params->height),
|
scm_from_uint32(params->height),
|
||||||
@@ -24,8 +23,8 @@ void *call_layout_demand_handler (void *data)
|
|||||||
{
|
{
|
||||||
struct Call_layout_demand_handler_parameters *params = (struct Call_layout_demand_handler_parameters *)data;
|
struct Call_layout_demand_handler_parameters *params = (struct Call_layout_demand_handler_parameters *)data;
|
||||||
|
|
||||||
assert(layout_demand_handler != NULL);
|
assert(context.layout_demand_handler != NULL);
|
||||||
assert(scm_is_true(scm_procedure_p(layout_demand_handler)) == 1);
|
assert(scm_is_true(scm_procedure_p(context.layout_demand_handler)) == 1);
|
||||||
|
|
||||||
/* Continuation barrier causes stack unwind on exceptions (i.e. errors
|
/* Continuation barrier causes stack unwind on exceptions (i.e. errors
|
||||||
* in the user defined layout demand handler) to stop here. Otherwise
|
* in the user defined layout demand handler) to stop here. Otherwise
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
#include <libguile.h>
|
#include <libguile.h>
|
||||||
|
|
||||||
#include "types.h"
|
|
||||||
|
|
||||||
struct Call_layout_demand_handler_parameters
|
struct Call_layout_demand_handler_parameters
|
||||||
{
|
{
|
||||||
uint32_t view_count, width, height, serial, tags;
|
uint32_t view_count, width, height, serial, tags;
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <libguile.h>
|
#include <libguile.h>
|
||||||
|
|
||||||
#include "types.h"
|
#include "riverguile.h"
|
||||||
|
#include "output.h"
|
||||||
#include "call-user-command-handler.h"
|
#include "call-user-command-handler.h"
|
||||||
|
|
||||||
extern SCM user_command_handler;
|
|
||||||
|
|
||||||
static void *call_user_command_handler_inner (void *data)
|
static void *call_user_command_handler_inner (void *data)
|
||||||
{
|
{
|
||||||
struct Call_user_command_parameters *params = (struct Call_user_command_parameters *)data;
|
struct Call_user_command_parameters *params = (struct Call_user_command_parameters *)data;
|
||||||
return scm_call_3(
|
return scm_call_3(
|
||||||
user_command_handler,
|
context.user_command_handler,
|
||||||
scm_from_utf8_string(params->cmd),
|
scm_from_utf8_string(params->cmd),
|
||||||
scm_from_uint32(params->output->user_command_tags),
|
scm_from_uint32(params->output->user_command_tags),
|
||||||
scm_from_uint32(params->output->name)
|
scm_from_uint32(params->output->name)
|
||||||
@@ -19,8 +18,8 @@ static void *call_user_command_handler_inner (void *data)
|
|||||||
|
|
||||||
void *call_user_command_handler (void *data)
|
void *call_user_command_handler (void *data)
|
||||||
{
|
{
|
||||||
assert(user_command_handler != NULL);
|
assert(context.user_command_handler != NULL);
|
||||||
assert(scm_is_true(scm_procedure_p(user_command_handler)) == 1);
|
assert(scm_is_true(scm_procedure_p(context.user_command_handler)) == 1);
|
||||||
|
|
||||||
/* Continuation barrier causes stack unwind on exceptions (i.e. errors
|
/* Continuation barrier causes stack unwind on exceptions (i.e. errors
|
||||||
* in the user defined user-command handler) to stop here. Otherwise
|
* in the user defined user-command handler) to stop here. Otherwise
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#ifndef RIVERGUILE_CALL_USER_COMMAND_HANDLER_H
|
#ifndef RIVERGUILE_CALL_USER_COMMAND_HANDLER_H
|
||||||
#define RIVERGUILE_CALL_USER_COMMAND_HANDLER_H
|
#define RIVERGUILE_CALL_USER_COMMAND_HANDLER_H
|
||||||
|
|
||||||
#include "types.h"
|
#include "output.h"
|
||||||
|
|
||||||
struct Call_user_command_parameters
|
struct Call_user_command_parameters
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <libguile.h>
|
#include <libguile.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "riverguile.h"
|
||||||
|
#include "seat.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ISO C forbids casting a function pointer to a void pointer because on some
|
* ISO C forbids casting a function pointer to a void pointer because on some
|
||||||
@@ -12,8 +17,35 @@
|
|||||||
#define scm_c_define_gsubr_fix(NAME, REQ, OPT, RST, FN) \
|
#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); }
|
{ const long int ptr = (long int)FN; scm_c_define_gsubr(NAME, REQ, OPT, RST, (void *)ptr); }
|
||||||
|
|
||||||
extern SCM layout_demand_handler;
|
static uint32_t extract_ms_from_idle_key (SCM key)
|
||||||
extern SCM user_command_handler;
|
{
|
||||||
|
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)
|
static SCM install_handler (SCM key, SCM proc)
|
||||||
{
|
{
|
||||||
@@ -42,10 +74,33 @@ static SCM install_handler (SCM key, SCM proc)
|
|||||||
return SCM_UNSPECIFIED;
|
return SCM_UNSPECIFIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scm_is_eq(scm_from_utf8_symbol("layout-demand"), key))
|
if ( scm_is_eq(scm_from_utf8_symbol("layout-demand"), key) == 1 )
|
||||||
layout_demand_handler = proc;
|
context.layout_demand_handler = proc;
|
||||||
else if (scm_is_eq(scm_from_utf8_symbol("user-command"), key))
|
else if ( scm_is_eq(scm_from_utf8_symbol("user-command"), key) == 1)
|
||||||
user_command_handler = proc;
|
context.user_command_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 )
|
||||||
|
{
|
||||||
|
uint32_t ms = extract_ms_from_idle_key(key);
|
||||||
|
|
||||||
|
struct Idle *idle = calloc(1, sizeof(struct Idle));
|
||||||
|
if ( idle == NULL )
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
idle->ms = ms;
|
||||||
|
idle->handler = proc;
|
||||||
|
wl_list_insert(&context.unconfigured_idles, &idle->link);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
scm_error_scm(
|
scm_error_scm(
|
||||||
@@ -91,13 +146,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 ( layout_demand_handler == NULL )
|
if ( context.layout_demand_handler == NULL )
|
||||||
return (void *)"ERROR: No layout demand handler installed.\n";
|
return (void *)"ERROR: No layout demand handler installed.\n";
|
||||||
|
|
||||||
/* Checked in the installer functions. */
|
/* Checked in the installer functions. */
|
||||||
assert(scm_is_true(scm_procedure_p(layout_demand_handler)) == 1);
|
assert(scm_is_true(scm_procedure_p(context.layout_demand_handler)) == 1);
|
||||||
if ( user_command_handler != NULL )
|
if ( context.user_command_handler != NULL )
|
||||||
assert(scm_is_true(scm_procedure_p(user_command_handler)) == 1);
|
assert(scm_is_true(scm_procedure_p(context.user_command_handler)) == 1);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
121
src/output.c
Normal file
121
src/output.c
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <libguile.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "river-layout-v3.h"
|
||||||
|
|
||||||
|
#include "riverguile.h"
|
||||||
|
#include "output.h"
|
||||||
|
#include "call-layout-demand-handler.h"
|
||||||
|
#include "call-user-command-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)
|
||||||
|
{
|
||||||
|
struct Output *output = (struct Output *)data;
|
||||||
|
|
||||||
|
struct Call_layout_demand_handler_parameters params = {
|
||||||
|
.view_count = view_count,
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
.serial = serial,
|
||||||
|
.tags = tags,
|
||||||
|
.output = output,
|
||||||
|
};
|
||||||
|
|
||||||
|
void *res = scm_with_guile(call_layout_demand_handler, (void *)¶ms);
|
||||||
|
if ( res != NULL )
|
||||||
|
{
|
||||||
|
fputs(res, stderr);
|
||||||
|
fputs("INFO: This error is not fatal.\n", stderr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO allow setting layout name from user installed layout demand handler
|
||||||
|
river_layout_v3_commit(output->layout, "(🐦)", serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void layout_handle_user_command (void *data,
|
||||||
|
struct river_layout_v3 *river_layout, const char *command)
|
||||||
|
{
|
||||||
|
if ( context.user_command_handler == NULL )
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct Output *output = (struct Output *)data;
|
||||||
|
|
||||||
|
struct Call_user_command_parameters params = {
|
||||||
|
.cmd = command,
|
||||||
|
.output = output,
|
||||||
|
};
|
||||||
|
|
||||||
|
void *res = scm_with_guile(call_user_command_handler, (void *)¶ms);
|
||||||
|
if ( res != NULL )
|
||||||
|
{
|
||||||
|
fputs(res, stderr);
|
||||||
|
fputs("INFO: This error is not fatal.\n", stderr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void layout_handle_user_command_tags (void *data,
|
||||||
|
struct river_layout_v3 *river_layout, uint32_t tags)
|
||||||
|
{
|
||||||
|
struct Output *output = (struct Output *)data;
|
||||||
|
output->user_command_tags = tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void layout_handle_namespace_in_use (void *data, struct river_layout_v3 *river_layout_v3)
|
||||||
|
{
|
||||||
|
fputs("ERROR: Namespace already in use.\n", stderr);
|
||||||
|
context.ret = EXIT_FAILURE;
|
||||||
|
context.loop = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct river_layout_v3_listener layout_listener = {
|
||||||
|
.namespace_in_use = layout_handle_namespace_in_use,
|
||||||
|
.layout_demand = layout_handle_layout_demand,
|
||||||
|
.user_command = layout_handle_user_command,
|
||||||
|
.user_command_tags = layout_handle_user_command_tags,
|
||||||
|
};
|
||||||
|
|
||||||
|
void output_configure (struct Output *output)
|
||||||
|
{
|
||||||
|
if ( context.layout_manager == NULL )
|
||||||
|
return;
|
||||||
|
if (output->configured)
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
struct Output *output = calloc(1, sizeof(struct Output));
|
||||||
|
if ( output == NULL )
|
||||||
|
{
|
||||||
|
fprintf(stderr, "ERROR: calloc: %s\n", strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
output->name = name;
|
||||||
|
output->wl_output = wl_output;
|
||||||
|
output_configure(output);
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void output_destroy (struct Output *output)
|
||||||
|
{
|
||||||
|
if ( output->layout != NULL )
|
||||||
|
river_layout_v3_destroy(output->layout);
|
||||||
|
assert(output->wl_output != NULL);
|
||||||
|
wl_output_destroy(output->wl_output);
|
||||||
|
free(output);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#ifndef RIVERGUILE_TYPES_H_INCLUDED
|
#ifndef RIVERGUILE_OUTPUT_H
|
||||||
#define RIVERGUILE_TYPES_H_INCLUDED
|
#define RIVERGUILE_OUTPUT_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
@@ -22,4 +22,8 @@ struct Output
|
|||||||
uint32_t user_command_tags;
|
uint32_t user_command_tags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Output *output_create (struct wl_output *wl_output, uint32_t name);
|
||||||
|
void output_destroy (struct Output *output);
|
||||||
|
void output_configure (struct Output *output);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
268
src/riverguile.c
268
src/riverguile.c
@@ -19,168 +19,80 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "river-layout-v3.h"
|
#include "river-layout-v3.h"
|
||||||
|
#include "ext-idle-notify-v1.h"
|
||||||
|
|
||||||
#include "types.h"
|
|
||||||
#include "call-layout-demand-handler.h"
|
|
||||||
#include "call-user-command-handler.h"
|
|
||||||
#include "load-script.h"
|
#include "load-script.h"
|
||||||
|
#include "riverguile.h"
|
||||||
|
#include "output.h"
|
||||||
|
#include "seat.h"
|
||||||
|
|
||||||
bool loop = true;
|
struct Context context = {
|
||||||
int ret = EXIT_SUCCESS;
|
|
||||||
|
|
||||||
jmp_buf skip_main_loop;
|
|
||||||
|
|
||||||
struct wl_display *wl_display = NULL;
|
|
||||||
struct wl_registry *wl_registry = NULL;
|
|
||||||
struct wl_callback *sync_callback = NULL;
|
|
||||||
struct river_layout_manager_v3 *layout_manager = NULL;
|
|
||||||
|
|
||||||
struct wl_list outputs;
|
|
||||||
|
|
||||||
/* Handlers are initially NULL instead of SCM_EOL because I am not sure whether
|
/* Handlers are initially NULL instead of SCM_EOL because I am not sure whether
|
||||||
* scm_null_p() is safe to use outside of a guile context and if yes, whether
|
* scm_null_p() is safe to use outside of a guile context and if yes, whether
|
||||||
* that is intended or will disappear in the future. Special care must be taken
|
* 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.
|
* to never call any scm_* function on these while they are NULL.
|
||||||
*/
|
*/
|
||||||
SCM layout_demand_handler = NULL;
|
.layout_demand_handler = NULL,
|
||||||
SCM user_command_handler = NULL;
|
.user_command_handler = NULL,
|
||||||
|
|
||||||
static void layout_handle_layout_demand (void *data, struct river_layout_v3 *river_layout_v3,
|
.loop = true,
|
||||||
uint32_t view_count, uint32_t width, uint32_t height, uint32_t tags, uint32_t serial)
|
.ret = EXIT_SUCCESS,
|
||||||
{
|
|
||||||
struct Output *output = (struct Output *)data;
|
|
||||||
|
|
||||||
struct Call_layout_demand_handler_parameters params = {
|
|
||||||
.view_count = view_count,
|
|
||||||
.width = width,
|
|
||||||
.height = height,
|
|
||||||
.serial = serial,
|
|
||||||
.tags = tags,
|
|
||||||
.output = output,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void *res = scm_with_guile(call_layout_demand_handler, (void *)¶ms);
|
jmp_buf skip_main_loop;
|
||||||
if ( res != NULL )
|
|
||||||
{
|
|
||||||
fputs(res, stderr);
|
|
||||||
fputs("INFO: This error is not fatal.\n", stderr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO allow setting layout name from user installed layout demand handler
|
|
||||||
river_layout_v3_commit(output->layout, "(🐦)", serial);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void layout_handle_user_command (void *data,
|
|
||||||
struct river_layout_v3 *river_layout, const char *command)
|
|
||||||
{
|
|
||||||
if ( user_command_handler == NULL )
|
|
||||||
return;
|
|
||||||
|
|
||||||
struct Output *output = (struct Output *)data;
|
|
||||||
|
|
||||||
struct Call_user_command_parameters params = {
|
|
||||||
.cmd = command,
|
|
||||||
.output = output,
|
|
||||||
};
|
|
||||||
|
|
||||||
void *res = scm_with_guile(call_user_command_handler, (void *)¶ms);
|
|
||||||
if ( res != NULL )
|
|
||||||
{
|
|
||||||
fputs(res, stderr);
|
|
||||||
fputs("INFO: This error is not fatal.\n", stderr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void layout_handle_user_command_tags (void *data,
|
|
||||||
struct river_layout_v3 *river_layout, uint32_t tags)
|
|
||||||
{
|
|
||||||
struct Output *output = (struct Output *)data;
|
|
||||||
output->user_command_tags = tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void layout_handle_namespace_in_use (void *data, struct river_layout_v3 *river_layout_v3)
|
|
||||||
{
|
|
||||||
fputs("ERROR: Namespace already in use.\n", stderr);
|
|
||||||
ret = EXIT_FAILURE;
|
|
||||||
loop = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct river_layout_v3_listener layout_listener = {
|
|
||||||
.namespace_in_use = layout_handle_namespace_in_use,
|
|
||||||
.layout_demand = layout_handle_layout_demand,
|
|
||||||
.user_command = layout_handle_user_command,
|
|
||||||
.user_command_tags = layout_handle_user_command_tags,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void output_configure (struct Output *output)
|
|
||||||
{
|
|
||||||
if ( layout_manager == NULL )
|
|
||||||
return;
|
|
||||||
if (output->configured)
|
|
||||||
return;
|
|
||||||
|
|
||||||
output->layout = river_layout_manager_v3_get_layout(
|
|
||||||
layout_manager, output->wl_output, "riverguile"
|
|
||||||
);
|
|
||||||
river_layout_v3_add_listener(output->layout, &layout_listener, output);
|
|
||||||
output->configured = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct Output *output_create (struct wl_output *wl_output)
|
|
||||||
{
|
|
||||||
struct Output *output = calloc(1, sizeof(struct Output));
|
|
||||||
if ( output == NULL )
|
|
||||||
{
|
|
||||||
fprintf(stderr, "ERROR: calloc: %s\n", strerror(errno));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
output->wl_output = wl_output;
|
|
||||||
output_configure(output);
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void output_destroy (struct Output *output)
|
|
||||||
{
|
|
||||||
if ( output->layout != NULL )
|
|
||||||
river_layout_v3_destroy(output->layout);
|
|
||||||
assert(output->wl_output != NULL);
|
|
||||||
wl_output_destroy(output->wl_output);
|
|
||||||
free(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void registry_handle_global (void *data, struct wl_registry *registry,
|
static void registry_handle_global (void *data, struct wl_registry *registry,
|
||||||
uint32_t name, const char *interface, uint32_t version)
|
uint32_t name, const char *interface, uint32_t version)
|
||||||
{
|
{
|
||||||
if ( strcmp(interface, river_layout_manager_v3_interface.name) == 0 )
|
if ( strcmp(interface, river_layout_manager_v3_interface.name) == 0 )
|
||||||
{
|
{
|
||||||
layout_manager = wl_registry_bind(
|
context.layout_manager = wl_registry_bind(
|
||||||
registry, name, &river_layout_manager_v3_interface, 2
|
registry, name, &river_layout_manager_v3_interface, 2
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
else if ( strcmp(interface, ext_idle_notifier_v1_interface.name) == 0 )
|
||||||
|
{
|
||||||
|
context.idle_notifier = wl_registry_bind(
|
||||||
|
registry, name, &ext_idle_notifier_v1_interface, 1
|
||||||
|
);
|
||||||
|
}
|
||||||
else if ( strcmp(interface, wl_output_interface.name) == 0 )
|
else if ( strcmp(interface, wl_output_interface.name) == 0 )
|
||||||
{
|
{
|
||||||
struct wl_output *wl_output = wl_registry_bind(
|
struct wl_output *wl_output = wl_registry_bind(
|
||||||
registry, name, &wl_output_interface, 1
|
registry, name, &wl_output_interface, 1
|
||||||
);
|
);
|
||||||
struct Output *output = output_create(wl_output);
|
struct Output *output = output_create(wl_output, name);
|
||||||
if ( output == NULL )
|
if ( output == NULL )
|
||||||
{
|
{
|
||||||
wl_output_destroy(wl_output);
|
wl_output_destroy(wl_output);
|
||||||
ret = EXIT_FAILURE;
|
context.ret = EXIT_FAILURE;
|
||||||
loop = false;
|
context.loop = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
wl_list_insert(&outputs, &output->link);
|
wl_list_insert(&context.outputs, &output->link);
|
||||||
|
}
|
||||||
|
else if ( strcmp(interface, wl_seat_interface.name) == 0 )
|
||||||
|
{
|
||||||
|
struct wl_seat *wl_seat = wl_registry_bind(
|
||||||
|
registry, name, &wl_seat_interface, 1
|
||||||
|
);
|
||||||
|
struct Seat *seat = seat_create(wl_seat, name);
|
||||||
|
if ( seat == NULL )
|
||||||
|
{
|
||||||
|
wl_seat_destroy(wl_seat);
|
||||||
|
context.ret = EXIT_FAILURE;
|
||||||
|
context.loop = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wl_list_insert(&context.seats, &seat->link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void registry_handle_global_remove (void *data, struct wl_registry *registry,
|
static void registry_handle_global_remove (void *data, struct wl_registry *registry,
|
||||||
uint32_t name)
|
uint32_t name)
|
||||||
{
|
{
|
||||||
struct Output *output, *tmp;
|
struct Output *output, *tmp_o;
|
||||||
wl_list_for_each_safe(output, tmp, &outputs, link)
|
wl_list_for_each_safe(output, tmp_o, &context.outputs, link)
|
||||||
{
|
{
|
||||||
if ( output->name != name )
|
if ( output->name != name )
|
||||||
continue;
|
continue;
|
||||||
@@ -188,6 +100,16 @@ static void registry_handle_global_remove (void *data, struct wl_registry *regis
|
|||||||
output_destroy(output);
|
output_destroy(output);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Seat *seat, *tmp_s;
|
||||||
|
wl_list_for_each_safe(seat, tmp_s, &context.seats, link)
|
||||||
|
{
|
||||||
|
if ( seat->name != name )
|
||||||
|
continue;
|
||||||
|
wl_list_remove(&seat->link);
|
||||||
|
seat_destroy(seat);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct wl_registry_listener registry_listener = {
|
static const struct wl_registry_listener registry_listener = {
|
||||||
@@ -198,22 +120,41 @@ static const struct wl_registry_listener registry_listener = {
|
|||||||
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)
|
||||||
{
|
{
|
||||||
wl_callback_destroy(wl_callback);
|
wl_callback_destroy(wl_callback);
|
||||||
sync_callback = NULL;
|
context.sync_callback = NULL;
|
||||||
|
|
||||||
if ( layout_manager == NULL )
|
if ( context.layout_manager == NULL )
|
||||||
{
|
{
|
||||||
fputs("ERROR: Wayland server does not support river-layout-v3.\n", stderr);
|
fputs("ERROR: Wayland server does not support river-layout-v3.\n", stderr);
|
||||||
ret = EXIT_FAILURE;
|
context.ret = EXIT_FAILURE;
|
||||||
loop = false;
|
context.loop = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( context.idle_notifier == 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct Seat *seat;
|
||||||
|
wl_list_for_each(seat, &context.seats, link)
|
||||||
|
break;
|
||||||
|
|
||||||
|
struct Idle *idle, *tmp_i;
|
||||||
|
wl_list_for_each_safe(idle, tmp_i, &context.unconfigured_idles, link)
|
||||||
|
idle_configure(idle, seat);
|
||||||
|
}
|
||||||
|
|
||||||
/* If outputs were registered before the river_layout_manager is
|
/* If outputs were registered before the river_layout_manager is
|
||||||
* available, they won't have a river_layout, so we need to create
|
* available, they won't have a river_layout, so we need to create
|
||||||
* those here.
|
* those here.
|
||||||
*/
|
*/
|
||||||
struct Output *output;
|
struct Output *output;
|
||||||
wl_list_for_each(output, &outputs, link)
|
wl_list_for_each(output, &context.outputs, link)
|
||||||
output_configure(output);
|
output_configure(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,7 +165,7 @@ static const struct wl_callback_listener sync_callback_listener = {
|
|||||||
static void handle_interrupt (int signum)
|
static void handle_interrupt (int signum)
|
||||||
{
|
{
|
||||||
fputs("Killed 💀\n", stderr);
|
fputs("Killed 💀\n", stderr);
|
||||||
loop = false;
|
context.loop = false;
|
||||||
longjmp(skip_main_loop, 1);
|
longjmp(skip_main_loop, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,13 +294,16 @@ int main(int argc, char *argv[])
|
|||||||
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(&outputs);
|
|
||||||
|
wl_list_init(&context.outputs);
|
||||||
|
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();
|
char *path = get_script_path();
|
||||||
if ( path == NULL )
|
if ( path == NULL )
|
||||||
{
|
{
|
||||||
ret = EXIT_FAILURE;
|
context.ret = EXIT_FAILURE;
|
||||||
goto early_exit;
|
goto early_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,8 +311,8 @@ int main(int argc, char *argv[])
|
|||||||
if ( res != NULL )
|
if ( res != NULL )
|
||||||
{
|
{
|
||||||
fputs((char *)res, stderr);
|
fputs((char *)res, stderr);
|
||||||
ret = EXIT_FAILURE;
|
context.ret = EXIT_FAILURE;
|
||||||
loop = false;
|
context.loop = false;
|
||||||
goto early_exit;
|
goto early_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,44 +325,62 @@ int main(int argc, char *argv[])
|
|||||||
if ( display_name == NULL )
|
if ( display_name == NULL )
|
||||||
{
|
{
|
||||||
fputs("ERROR: WAYLAND_DISPLAY is not set.\n", stderr);
|
fputs("ERROR: WAYLAND_DISPLAY is not set.\n", stderr);
|
||||||
ret = EXIT_FAILURE;
|
context.ret = EXIT_FAILURE;
|
||||||
goto early_exit;
|
goto early_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
wl_display = wl_display_connect(display_name);
|
context.wl_display = wl_display_connect(display_name);
|
||||||
if ( wl_display == NULL )
|
if ( context.wl_display == NULL )
|
||||||
{
|
{
|
||||||
fputs("ERROR: Can not connect to wayland display.\n", stderr);
|
fputs("ERROR: Can not connect to wayland display.\n", stderr);
|
||||||
ret = EXIT_FAILURE;
|
context.ret = EXIT_FAILURE;
|
||||||
goto early_exit;
|
goto early_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
wl_registry = wl_display_get_registry(wl_display);
|
context.wl_registry = wl_display_get_registry(context.wl_display);
|
||||||
wl_registry_add_listener(wl_registry, ®istry_listener, NULL);
|
wl_registry_add_listener(context.wl_registry, ®istry_listener, NULL);
|
||||||
|
|
||||||
sync_callback = wl_display_sync(wl_display);
|
context.sync_callback = wl_display_sync(context.wl_display);
|
||||||
wl_callback_add_listener(sync_callback, &sync_callback_listener, NULL);
|
wl_callback_add_listener(context.sync_callback, &sync_callback_listener, NULL);
|
||||||
|
|
||||||
if ( setjmp(skip_main_loop) == 0 )
|
if ( setjmp(skip_main_loop) == 0 )
|
||||||
while ( loop && wl_display_dispatch(wl_display) > 0 );
|
while ( context.loop && wl_display_dispatch(context.wl_display) > 0 );
|
||||||
|
|
||||||
struct Output *output, *tmp;
|
struct Idle *idle, *tmp_i;
|
||||||
wl_list_for_each_safe(output, tmp, &outputs, link)
|
wl_list_for_each_safe(idle, tmp_i, &context.unconfigured_idles, link)
|
||||||
|
{
|
||||||
|
wl_list_remove(&idle->link);
|
||||||
|
idle_destroy(idle);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Output *output, *tmp_o;
|
||||||
|
wl_list_for_each_safe(output, tmp_o, &context.outputs, link)
|
||||||
{
|
{
|
||||||
wl_list_remove(&output->link);
|
wl_list_remove(&output->link);
|
||||||
output_destroy(output);
|
output_destroy(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( sync_callback != NULL )
|
struct Seat *seat, *tmp_s;
|
||||||
wl_callback_destroy(sync_callback);
|
wl_list_for_each_safe(seat, tmp_s, &context.seats, link)
|
||||||
if ( wl_registry != NULL )
|
{
|
||||||
wl_registry_destroy(wl_registry);
|
wl_list_remove(&seat->link);
|
||||||
wl_display_disconnect(wl_display);
|
seat_destroy(seat);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( context.idle_notifier != NULL )
|
||||||
|
ext_idle_notifier_v1_destroy(context.idle_notifier);
|
||||||
|
if ( context.layout_manager != NULL )
|
||||||
|
river_layout_manager_v3_destroy(context.layout_manager);
|
||||||
|
if ( context.sync_callback != NULL )
|
||||||
|
wl_callback_destroy(context.sync_callback);
|
||||||
|
if ( context.wl_registry != NULL )
|
||||||
|
wl_registry_destroy(context.wl_registry);
|
||||||
|
wl_display_disconnect(context.wl_display);
|
||||||
|
|
||||||
early_exit:
|
early_exit:
|
||||||
if ( path != NULL )
|
if ( path != NULL )
|
||||||
free(path);
|
free(path);
|
||||||
|
|
||||||
return ret;
|
return context.ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
33
src/riverguile.h
Normal file
33
src/riverguile.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#ifndef RIVERGUILE_RIVERGUILE_H
|
||||||
|
#define RIVERGUILE_RIVERGUILE_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include <libguile.h>
|
||||||
|
|
||||||
|
#include "seat.h"
|
||||||
|
|
||||||
|
struct Context
|
||||||
|
{
|
||||||
|
SCM layout_demand_handler;
|
||||||
|
SCM user_command_handler;
|
||||||
|
|
||||||
|
bool loop;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
struct wl_display *wl_display;
|
||||||
|
struct wl_registry *wl_registry;
|
||||||
|
struct wl_callback *sync_callback;
|
||||||
|
struct river_layout_manager_v3 *layout_manager;
|
||||||
|
struct ext_idle_notifier_v1 *idle_notifier;
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct Context context;
|
||||||
|
|
||||||
|
#endif
|
||||||
112
src/seat.c
Normal file
112
src/seat.c
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "ext-idle-notify-v1.h"
|
||||||
|
|
||||||
|
#include "riverguile.h"
|
||||||
|
#include "seat.h"
|
||||||
|
#include "call-idle-handler.h"
|
||||||
|
|
||||||
|
static void idle_notification_handle_idled (void *data,
|
||||||
|
struct ext_idle_notification_v1 *ext_idle_notification_v1)
|
||||||
|
{
|
||||||
|
struct Idle *idle = (struct Idle *)data;
|
||||||
|
assert(idle->handler != NULL);
|
||||||
|
|
||||||
|
struct Call_idle_handler_parameters params = {
|
||||||
|
.idle = idle,
|
||||||
|
.event = IDLE,
|
||||||
|
};
|
||||||
|
call_idle_handler(¶ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void idle_notification_handle_resumed (void *data,
|
||||||
|
struct ext_idle_notification_v1 *ext_idle_notification_v1)
|
||||||
|
{
|
||||||
|
struct Idle *idle = (struct Idle *)data;
|
||||||
|
assert(idle->handler != NULL);
|
||||||
|
|
||||||
|
struct Call_idle_handler_parameters params = {
|
||||||
|
.idle = idle,
|
||||||
|
.event = RESUME,
|
||||||
|
};
|
||||||
|
call_idle_handler(¶ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct ext_idle_notification_v1_listener idle_notification_listener = {
|
||||||
|
.idled = idle_notification_handle_idled,
|
||||||
|
.resumed = idle_notification_handle_resumed,
|
||||||
|
};
|
||||||
|
|
||||||
|
void idle_configure (struct Idle *idle, struct Seat *seat)
|
||||||
|
{
|
||||||
|
assert(idle->seat == NULL);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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?
|
||||||
|
// we could also define an 'exit handler
|
||||||
|
if ( idle->idle_notification != NULL )
|
||||||
|
{
|
||||||
|
assert(idle->seat != NULL);
|
||||||
|
ext_idle_notification_v1_destroy(idle->idle_notification);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert(idle->seat == NULL);
|
||||||
|
}
|
||||||
|
free(idle);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Seat *seat_create (struct wl_seat *wl_seat, uint32_t name)
|
||||||
|
{
|
||||||
|
struct Seat *seat = calloc(1, sizeof(struct Seat));
|
||||||
|
if ( seat == NULL )
|
||||||
|
{
|
||||||
|
fprintf(stderr, "ERROR: calloc: %s\n", strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
seat->name = name;
|
||||||
|
seat->wl_seat = wl_seat;
|
||||||
|
|
||||||
|
wl_list_init(&seat->idles);
|
||||||
|
|
||||||
|
return seat;
|
||||||
|
}
|
||||||
|
|
||||||
|
void seat_destroy (struct Seat *seat)
|
||||||
|
{
|
||||||
|
struct Idle *idle, *tmp_i;
|
||||||
|
wl_list_for_each_safe(idle, tmp_i, &seat->idles, link)
|
||||||
|
{
|
||||||
|
wl_list_remove(&idle->link);
|
||||||
|
idle_destroy(idle);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(seat->wl_seat != NULL);
|
||||||
|
wl_seat_destroy(seat->wl_seat);
|
||||||
|
free(seat);
|
||||||
|
}
|
||||||
|
|
||||||
31
src/seat.h
Normal file
31
src/seat.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#ifndef RIVERGUILE_SEAT_H
|
||||||
|
#define RIVERGUILE_SEAT_H
|
||||||
|
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include <libguile.h>
|
||||||
|
|
||||||
|
struct Seat
|
||||||
|
{
|
||||||
|
struct wl_list link;
|
||||||
|
struct wl_seat *wl_seat;
|
||||||
|
uint32_t name;
|
||||||
|
|
||||||
|
struct wl_list idles;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Idle
|
||||||
|
{
|
||||||
|
struct Seat *seat;
|
||||||
|
struct wl_list link;
|
||||||
|
struct ext_idle_notification_v1 *idle_notification;
|
||||||
|
SCM handler;
|
||||||
|
uint32_t ms;
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user