diff --git a/include/sway/commands.h b/include/sway/commands.h
index 27058587..d9b07d79 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -104,6 +104,7 @@ struct sway_container *container_find_resize_parent(struct sway_container *con,
sway_cmd cmd_exec_validate;
sway_cmd cmd_exec_process;
+sway_cmd cmd_action;
sway_cmd cmd_assign;
sway_cmd cmd_bar;
sway_cmd cmd_bindcode;
diff --git a/include/sway/server.h b/include/sway/server.h
index b2eb2c36..39a8f557 100644
--- a/include/sway/server.h
+++ b/include/sway/server.h
@@ -113,6 +113,11 @@ struct sway_server {
struct wl_listener request_set_cursor_shape;
+ struct wlr_action_binder_v1 *action_binder;
+ struct wl_listener action_binder_bind;
+ struct wl_listener action_binder_unbind;
+ struct wl_listener action_binder_destroy;
+
struct wl_list pending_launcher_ctxs; // launcher_ctx::link
// The timeout for transactions, after which a transaction is applied
@@ -174,6 +179,9 @@ void xdg_activation_v1_handle_request_activate(struct wl_listener *listener,
void *data);
void xdg_activation_v1_handle_new_token(struct wl_listener *listener,
void *data);
+void action_binder_v1_bind(struct wl_listener *listener, void *data);
+void action_binder_v1_unbind(struct wl_listener *listener, void *data);
+void action_binder_v1_delete(struct wl_listener *listener, void *data);
void set_rr_scheduling(void);
diff --git a/protocols/ext-action-binder-v1.xml b/protocols/ext-action-binder-v1.xml
new file mode 100644
index 00000000..a32c4638
--- /dev/null
+++ b/protocols/ext-action-binder-v1.xml
@@ -0,0 +1,148 @@
+
+
+
+ Copyright © 2015-2017 Quentin “Sardem FF7” Glidic, 2023 Anna "navi" Figueiredo Gomes
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that copyright notice and this permission
+ notice appear in supporting documentation, and that the name of
+ the copyright holders not be used in advertising or publicity
+ pertaining to distribution of the software without specific,
+ written prior permission. The copyright holders make no
+ representations about the suitability of this software for any
+ purpose. It is provided "as is" without express or implied
+ warranty.
+
+ THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ THIS SOFTWARE.
+
+
+
+
+ This interface is designed to allow any application to bind
+ an action.
+
+ An action is an arbitrary couple of a namespace and a name describing the
+ wanted behaviour. These two strings are not meant to be user-visible.
+ Some namespaces are well-known and shared by applications while each
+ application can have its own namespaces for internal actions.
+ It is possible to have the same action in several namespaces, e.g. to
+ allow application-specific bindings in addition to global actions.
+
+ It is left to the compositor to determine which client will get events.
+ The choice can be based on policy, heuristic, user configuration, or any
+ other mechanism that may be relevant.
+ Here are some examples of dispatching choice: all applications, last
+ focused, user-defined preference order, latest fullscreened application.
+
+ This interface is exposed as global
+
+
+
+
+ The client no longer wants to receive events for any action.
+
+
+
+
+
+
+
+
+
+
+
+
+ The client no longer wants to receive events for this binding.
+
+
+
+
+
+ Sets the namespace:name of the binding. This a kind of action.
+
+
+
+
+
+
+
+ This description may be used by the compositor to render a ui for bindings.
+
+
+
+
+
+
+ The trigger is a suggestion to the compositor, and the action should not rely
+ to being set to that specific trigger.
+ The client does not know which trigger was actually set, but when a binding is
+ bound, it recieves from the compositor a human readable string describing the trigger,
+ if any, so it could show it in a ui.
+
+
+
+
+
+
+ Bind an action to the object. this is a one-time request.
+ After calling bind, either the "bound" or "rejected" event is sent.
+ Subsequent calls to bind should be ignored.
+ If no action has been set for the binding, the error "invalid_action" is raised.
+
+
+
+
+
+
+
+
+
+ After the compositor processes a bind request, if the action was
+ bound to this binding, it calls this event to notify the client of the result.
+
+
+
+
+
+
+ After the compositor processes a bind request, if the binding was
+ rejected, it calls this event to notify the client of the result.
+ This event may be sent after a binding was bound, should the compositor
+ want to remove the binding.
+ After this event, the binding is destroyed and can't be used anymore.
+
+
+
+
+
+ Depending on the user configuration, an action can be either one-off or
+ sustained. The client must handle all the three event types and either make
+ sense of them or ignore them properly.
+
+
+
+
+
+
+
+
+ This event is sent when actions are triggered.
+ If a binding would trigger both triggered and started events, the
+ started event must be sent first.
+
+
+
+
+
diff --git a/protocols/meson.build b/protocols/meson.build
index 81edb584..3143fd0f 100644
--- a/protocols/meson.build
+++ b/protocols/meson.build
@@ -14,6 +14,7 @@ protocols = [
wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml',
wl_protocol_dir / 'staging/content-type/content-type-v1.xml',
wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml',
+ 'ext-action-binder-v1.xml',
'wlr-layer-shell-unstable-v1.xml',
'idle.xml',
'wlr-output-power-management-unstable-v1.xml',
diff --git a/sway/action-binder-v1.c b/sway/action-binder-v1.c
new file mode 100644
index 00000000..bf6ffef5
--- /dev/null
+++ b/sway/action-binder-v1.c
@@ -0,0 +1,15 @@
+#include
+
+void action_binder_v1_bind(struct wl_listener *listener, void *data) {
+ struct wlr_action_binder_v1_state *state = data;
+ struct wlr_action_binding_v1 *binding = NULL, *tmp = NULL;
+ wl_list_for_each_safe(binding, tmp, &state->bind_queue, link) {
+ wlr_action_binder_v1_bind(binding, "");
+ }
+}
+
+void action_binder_v1_unbind(struct wl_listener *listener, void *data) {
+}
+
+void action_binder_v1_delete(struct wl_listener *listener, void *data) {
+}
diff --git a/sway/commands.c b/sway/commands.c
index 8d003dfa..68879af7 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -112,6 +112,7 @@ static const struct cmd_handler config_handlers[] = {
/* Runtime-only commands. Keep alphabetized */
static const struct cmd_handler command_handlers[] = {
+ { "action", cmd_action },
{ "border", cmd_border },
{ "create_output", cmd_create_output },
{ "exit", cmd_exit },
diff --git a/sway/commands/action.c b/sway/commands/action.c
new file mode 100644
index 00000000..96cb3b59
--- /dev/null
+++ b/sway/commands/action.c
@@ -0,0 +1,18 @@
+#include "log.h"
+#include
+#include "ext-action-binder-v1-protocol.h"
+#include "sway/commands.h"
+#include "sway/server.h"
+#include "sway/input/input-manager.h"
+
+struct cmd_results *cmd_action(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "action", EXPECTED_EQUAL_TO, 2))) {
+ return error;
+ }
+
+ wlr_action_binder_v1_trigger(server.action_binder, argv[0], argv[1],
+ EXT_ACTION_BINDING_V1_TRIGGER_TYPE_ONE_SHOT);
+
+ return cmd_results_new(CMD_SUCCESS, NULL);
+}
diff --git a/sway/meson.build b/sway/meson.build
index d937e425..b24d8fbc 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -1,4 +1,5 @@
sway_sources = files(
+ 'action-binder-v1.c',
'commands.c',
'config.c',
'criteria.c',
@@ -41,6 +42,7 @@ sway_sources = files(
'config/seat.c',
'config/input.c',
+ 'commands/action.c',
'commands/assign.c',
'commands/bar.c',
'commands/bind.c',
diff --git a/sway/server.c b/sway/server.c
index 684b1dbd..573a018b 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -350,6 +350,17 @@ bool server_init(struct sway_server *server) {
wl_list_init(&server->pending_launcher_ctxs);
+ server->action_binder = wlr_action_binder_v1_create(server->wl_display);
+ server->action_binder_bind.notify = action_binder_v1_bind;
+ wl_signal_add(&server->action_binder->events.bind,
+ &server->action_binder_bind);
+ server->action_binder_unbind.notify = action_binder_v1_unbind;
+ wl_signal_add(&server->action_binder->events.unbind,
+ &server->action_binder_unbind);
+ server->action_binder_destroy.notify = action_binder_v1_delete;
+ wl_signal_add(&server->action_binder->events.destroy,
+ &server->action_binder_destroy);
+
// Avoid using "wayland-0" as display socket
char name_candidate[16];
for (unsigned int i = 1; i <= 32; ++i) {