river-layout: update to v2
This implements the changes to the river-layout protocol proposed in the previous commit removing river-options.
This commit is contained in:
parent
871fc7c8de
commit
e80b883a47
14 changed files with 275 additions and 61 deletions
|
@ -65,7 +65,7 @@ pub fn build(b: *zbs.Builder) !void {
|
||||||
scanner.addSystemProtocol("unstable/pointer-constraints/pointer-constraints-unstable-v1.xml");
|
scanner.addSystemProtocol("unstable/pointer-constraints/pointer-constraints-unstable-v1.xml");
|
||||||
scanner.addProtocolPath("protocol/river-control-unstable-v1.xml");
|
scanner.addProtocolPath("protocol/river-control-unstable-v1.xml");
|
||||||
scanner.addProtocolPath("protocol/river-status-unstable-v1.xml");
|
scanner.addProtocolPath("protocol/river-status-unstable-v1.xml");
|
||||||
scanner.addProtocolPath("protocol/river-layout-v1.xml");
|
scanner.addProtocolPath("protocol/river-layout-v2.xml");
|
||||||
scanner.addProtocolPath("protocol/wlr-layer-shell-unstable-v1.xml");
|
scanner.addProtocolPath("protocol/wlr-layer-shell-unstable-v1.xml");
|
||||||
scanner.addProtocolPath("protocol/wlr-output-power-management-unstable-v1.xml");
|
scanner.addProtocolPath("protocol/wlr-output-power-management-unstable-v1.xml");
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,8 @@ function __riverctl_completion ()
|
||||||
zoom \
|
zoom \
|
||||||
default-layout \
|
default-layout \
|
||||||
output-layout \
|
output-layout \
|
||||||
|
set-layout-value \
|
||||||
|
mod-layout-value \
|
||||||
set-focused-tags \
|
set-focused-tags \
|
||||||
set-view-tags \
|
set-view-tags \
|
||||||
toggle-focused-tags \
|
toggle-focused-tags \
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
function __fish_riverctl_complete_no_subcommand
|
function __fish_riverctl_complete_no_subcommand
|
||||||
for i in (commandline -opc)
|
for i in (commandline -opc)
|
||||||
if contains -- $i close csd-filter-add exit float-filter-add focus-output focus-view move resize snap send-to-output spawn swap toggle-float toggle-fullscreen zoom default-layout output-layout set-focused-tags set-view-tags toggle-focused-tags toggle-view-tags spawn-tagmask declare-mode enter-mode map map-pointer unmap unmap-pointer attach-mode background-color border-color-focused border-color-unfocused border-width focus-follows-cursor opacity set-repeat xcursor-theme
|
if contains -- $i close csd-filter-add exit float-filter-add focus-output focus-view move resize snap send-to-output spawn swap toggle-float toggle-fullscreen zoom default-layout output-layout set-layout-value mod-layout-value set-focused-tags set-view-tags toggle-focused-tags toggle-view-tags spawn-tagmask declare-mode enter-mode map map-pointer unmap unmap-pointer attach-mode background-color border-color-focused border-color-unfocused border-width focus-follows-cursor opacity set-repeat xcursor-theme
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -25,6 +25,8 @@ complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a toggle-fu
|
||||||
complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a zoom -d 'Bump the focused view to the top of the layout stack'
|
complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a zoom -d 'Bump the focused view to the top of the layout stack'
|
||||||
complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a default-layout -d 'Set the layout namespace to be used by all outputs by default.'
|
complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a default-layout -d 'Set the layout namespace to be used by all outputs by default.'
|
||||||
complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a output-layout -d 'Set the layout namespace of currently focused output.'
|
complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a output-layout -d 'Set the layout namespace of currently focused output.'
|
||||||
|
complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a set-layout-value -d 'Set the value with name _name_ of the layout on the focused output with matching namespace.'
|
||||||
|
complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a mod-layout-value -d 'Modify the value with name _name_ of the layout on the focused output with matching namespace.'
|
||||||
# Tag managements
|
# Tag managements
|
||||||
complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a set-focused-tags -d 'Show views with tags corresponding to the set bits of tags'
|
complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a set-focused-tags -d 'Show views with tags corresponding to the set bits of tags'
|
||||||
complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a set-view-tags -d 'Assign the currently focused view the tags corresponding to the set bits of tags'
|
complete -c riverctl -x -n '__fish_riverctl_complete_no_subcommand' -a set-view-tags -d 'Assign the currently focused view the tags corresponding to the set bits of tags'
|
||||||
|
|
|
@ -31,6 +31,8 @@ _riverctl() {
|
||||||
'zoom:Bump the focused view to the top of the layout stack'
|
'zoom:Bump the focused view to the top of the layout stack'
|
||||||
'default-layout:Set the layout namespace to be used by all outputs by default.'
|
'default-layout:Set the layout namespace to be used by all outputs by default.'
|
||||||
'output-layout:Set the layout namespace of currently focused output.'
|
'output-layout:Set the layout namespace of currently focused output.'
|
||||||
|
'set-layout-value:Set the value with name _name_ of the layout on the focused output with matching namespace.'
|
||||||
|
'mod-layout-value:Modify the value with name _name_ of the layout on the focused output with matching namespace.'
|
||||||
# Tag management
|
# Tag management
|
||||||
'set-focused-tags:Show views with tags corresponding to the set bits of tags'
|
'set-focused-tags:Show views with tags corresponding to the set bits of tags'
|
||||||
'set-view-tags:Assign the currently focused view the tags corresponding to the set bits of tags'
|
'set-view-tags:Assign the currently focused view the tags corresponding to the set bits of tags'
|
||||||
|
|
|
@ -79,6 +79,16 @@ over the Wayland protocol.
|
||||||
Set the layout namespace of currently focused output, overriding
|
Set the layout namespace of currently focused output, overriding
|
||||||
the value set with *default-layout* if any.
|
the value set with *default-layout* if any.
|
||||||
|
|
||||||
|
*set-layout-value* _namespace_ _type_ _name_ _value_
|
||||||
|
Set the value with name _name_ of the layout on the focused output
|
||||||
|
with matching namespace. If there is no matching layout, this command
|
||||||
|
does nothing.
|
||||||
|
|
||||||
|
*mod-layout-value* _namespace_ _type_ _name_ _value_
|
||||||
|
Modify the value with name _name_ of the layout on the focused
|
||||||
|
output with matching namespace. If there is no matching layout,
|
||||||
|
this command does nothing.
|
||||||
|
|
||||||
## TAG MANAGEMENT
|
## TAG MANAGEMENT
|
||||||
|
|
||||||
Tags are similar to workspaces but more flexible. You can assign views multiple
|
Tags are similar to workspaces but more flexible. You can assign views multiple
|
||||||
|
|
|
@ -13,6 +13,17 @@ rivertile - Tiled layout generator for river
|
||||||
*rivertile* is a layout client for river. It provides a simple tiled layout
|
*rivertile* is a layout client for river. It provides a simple tiled layout
|
||||||
split main/secondary stacks.
|
split main/secondary stacks.
|
||||||
|
|
||||||
|
# VALUES
|
||||||
|
|
||||||
|
_main_location_ (string: top, bottom, left, or right)
|
||||||
|
The location of the main area in the layout.
|
||||||
|
|
||||||
|
_main_count_ (int)
|
||||||
|
The number of views in the main area of the layout.
|
||||||
|
|
||||||
|
_main_factor_ (fixed: [0.1, 0.9])
|
||||||
|
The ratio of main area to total layout area.
|
||||||
|
|
||||||
# AUTHORS
|
# AUTHORS
|
||||||
|
|
||||||
Maintained by Isaac Freund <ifreund@ifreund.xyz> who is assisted by open
|
Maintained by Isaac Freund <ifreund@ifreund.xyz> who is assisted by open
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<protocol name="river_layout_v1">
|
<protocol name="river_layout_v2">
|
||||||
<copyright>
|
<copyright>
|
||||||
Copyright 2020-2021 The River Developers
|
Copyright 2020-2021 The River Developers
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
<description summary="let clients propose view positions and dimensions">
|
<description summary="let clients propose view positions and dimensions">
|
||||||
This protocol specifies a way for clients to propose arbitrary positions and
|
This protocol specifies a way for clients to propose arbitrary positions and
|
||||||
dimensions for a set of views on a specific output of a compositor through
|
dimensions for a set of views on a specific output of a compositor through
|
||||||
the river_layout_v1 object.
|
the river_layout_v2 object.
|
||||||
|
|
||||||
This set of views is logically structured as a simple list. Views
|
This set of views is logically structured as a simple list. Views
|
||||||
in this list cannot be individually addressed, instead the order of
|
in this list cannot be individually addressed, instead the order of
|
||||||
|
@ -32,7 +32,7 @@
|
||||||
intentional limitation.
|
intentional limitation.
|
||||||
|
|
||||||
Note that the client may need to handle multiple layout demands per
|
Note that the client may need to handle multiple layout demands per
|
||||||
river_layout_v1 object simultaneously.
|
river_layout_v2 object simultaneously.
|
||||||
|
|
||||||
Warning! The protocol described in this file is currently in the testing
|
Warning! The protocol described in this file is currently in the testing
|
||||||
phase. Backward compatible changes may be added together with the
|
phase. Backward compatible changes may be added together with the
|
||||||
|
@ -40,9 +40,9 @@
|
||||||
only be done by creating a new major version of the extension.
|
only be done by creating a new major version of the extension.
|
||||||
</description>
|
</description>
|
||||||
|
|
||||||
<interface name="river_layout_manager_v1" version="1">
|
<interface name="river_layout_manager_v2" version="1">
|
||||||
<description summary="manage river layout objects">
|
<description summary="manage river layout objects">
|
||||||
A global factory for river_layout_v1 objects.
|
A global factory for river_layout_v2 objects.
|
||||||
</description>
|
</description>
|
||||||
|
|
||||||
<request name="destroy" type="destructor">
|
<request name="destroy" type="destructor">
|
||||||
|
@ -54,26 +54,26 @@
|
||||||
</request>
|
</request>
|
||||||
|
|
||||||
<request name="get_layout">
|
<request name="get_layout">
|
||||||
<description summary="create a river_layout_v1 object">
|
<description summary="create a river_layout_v2 object">
|
||||||
This creates a new river_layout_v1 object for the given wl_output.
|
This creates a new river_layout_v2 object for the given wl_output.
|
||||||
|
|
||||||
All layout related communication is done through this interface.
|
All layout related communication is done through this interface.
|
||||||
|
|
||||||
The namespace is used by the compositor to decide which river_layout_v1
|
The namespace is used by the compositor to decide which river_layout_v2
|
||||||
object will receive layout demands for the output.
|
object will receive layout demands for the output.
|
||||||
|
|
||||||
The namespace is required to be be unique per-output. Furthermore,
|
The namespace is required to be be unique per-output. Furthermore,
|
||||||
two separate clients may not share a namespace on separate outputs. If
|
two separate clients may not share a namespace on separate outputs. If
|
||||||
these conditions are not upheld, the the namespace_in_use event will
|
these conditions are not upheld, the the namespace_in_use event will
|
||||||
be sent directly after creation of the river_layout_v1 object.
|
be sent directly after creation of the river_layout_v2 object.
|
||||||
</description>
|
</description>
|
||||||
<arg name="id" type="new_id" interface="river_layout_v1"/>
|
<arg name="id" type="new_id" interface="river_layout_v2"/>
|
||||||
<arg name="output" type="object" interface="wl_output"/>
|
<arg name="output" type="object" interface="wl_output"/>
|
||||||
<arg name="namespace" type="string" summary="namespace of the layout object"/>
|
<arg name="namespace" type="string" summary="namespace of the layout object"/>
|
||||||
</request>
|
</request>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
||||||
<interface name="river_layout_v1" version="1">
|
<interface name="river_layout_v2" version="1">
|
||||||
<description summary="receive and respond to layout demands">
|
<description summary="receive and respond to layout demands">
|
||||||
This interface allows clients to receive layout demands from the
|
This interface allows clients to receive layout demands from the
|
||||||
compositor for a specific output and subsequently propose positions and
|
compositor for a specific output and subsequently propose positions and
|
||||||
|
@ -88,8 +88,8 @@
|
||||||
</enum>
|
</enum>
|
||||||
|
|
||||||
<request name="destroy" type="destructor">
|
<request name="destroy" type="destructor">
|
||||||
<description summary="destroy the river_layout_v1 object">
|
<description summary="destroy the river_layout_v2 object">
|
||||||
This request indicates that the client will not use the river_layout_v1
|
This request indicates that the client will not use the river_layout_v2
|
||||||
object any more.
|
object any more.
|
||||||
</description>
|
</description>
|
||||||
</request>
|
</request>
|
||||||
|
@ -98,7 +98,7 @@
|
||||||
<description summary="the requested namespace is already in use">
|
<description summary="the requested namespace is already in use">
|
||||||
After this event is sent, all requests aside from the destroy event
|
After this event is sent, all requests aside from the destroy event
|
||||||
will be ignored by the server. If the client wishes to try again with
|
will be ignored by the server. If the client wishes to try again with
|
||||||
a different namespace they must create a new river_layout_v1 object.
|
a different namespace they must create a new river_layout_v2 object.
|
||||||
</description>
|
</description>
|
||||||
</event>
|
</event>
|
||||||
|
|
||||||
|
@ -188,14 +188,69 @@
|
||||||
<arg name="serial" type="uint" summary="serial of layout demand"/>
|
<arg name="serial" type="uint" summary="serial of layout demand"/>
|
||||||
</request>
|
</request>
|
||||||
|
|
||||||
<request name="parameters_changed">
|
<event name="set_int_value">
|
||||||
<description summary="parameters of layout have changed">
|
<description summary="an int value has been set">
|
||||||
The client may use this request to inform the compositor that one or
|
This event indicates that the value of this river_layout_v2 object
|
||||||
muliple of the parameters it uses to generate layouts have changed.
|
with the given name has been set to the given value.
|
||||||
|
|
||||||
If the client is responsible for the current view layout, the compositor
|
This event will be followed by a layout_demand if necessary (i.e. if
|
||||||
may decide to send a new layout demand to update the layout.
|
this layout object is currently being used by the compositor to
|
||||||
|
layout an output)
|
||||||
</description>
|
</description>
|
||||||
</request>
|
<arg name="name" type="string"/>
|
||||||
|
<arg name="value" type="int"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="mod_int_value">
|
||||||
|
<description summary="an int value has been modified">
|
||||||
|
This event indicates that the value of this river_layout_v2 object
|
||||||
|
with the given name has been modifed by the given delta.
|
||||||
|
|
||||||
|
This event will be followed by a layout_demand if necessary (i.e. if
|
||||||
|
this layout object is currently being used by the compositor to
|
||||||
|
layout an output)
|
||||||
|
</description>
|
||||||
|
<arg name="name" type="string"/>
|
||||||
|
<arg name="delta" type="int"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="set_fixed_value">
|
||||||
|
<description summary="a fixed value has been set">
|
||||||
|
This event indicates that the value of this river_layout_v2 object
|
||||||
|
with the given name has been set to the given value.
|
||||||
|
|
||||||
|
This event will be followed by a layout_demand if necessary (i.e. if
|
||||||
|
this layout object is currently being used by the compositor to
|
||||||
|
layout an output)
|
||||||
|
</description>
|
||||||
|
<arg name="name" type="string"/>
|
||||||
|
<arg name="value" type="fixed"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="mod_fixed_value">
|
||||||
|
<description summary="a fixed value has been modified">
|
||||||
|
This event indicates that the value of this river_layout_v2 object
|
||||||
|
with the given name has been modifed by the given delta.
|
||||||
|
|
||||||
|
This event will be followed by a layout_demand if necessary (i.e. if
|
||||||
|
this layout object is currently being used by the compositor to
|
||||||
|
layout an output)
|
||||||
|
</description>
|
||||||
|
<arg name="name" type="string"/>
|
||||||
|
<arg name="delta" type="fixed"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="set_string_value">
|
||||||
|
<description summary="a string value has been set">
|
||||||
|
This event indicates that the value of this river_layout_v2 object
|
||||||
|
with the given name has been set to the given value.
|
||||||
|
|
||||||
|
This event will be followed by a layout_demand if necessary (i.e. if
|
||||||
|
this layout object is currently being used by the compositor to
|
||||||
|
layout an output)
|
||||||
|
</description>
|
||||||
|
<arg name="name" type="string"/>
|
||||||
|
<arg name="value" type="string"/>
|
||||||
|
</event>
|
||||||
</interface>
|
</interface>
|
||||||
</protocol>
|
</protocol>
|
|
@ -35,12 +35,12 @@ const LayoutDemand = @import("LayoutDemand.zig");
|
||||||
|
|
||||||
const log = std.log.scoped(.layout);
|
const log = std.log.scoped(.layout);
|
||||||
|
|
||||||
layout: *river.LayoutV1,
|
layout: *river.LayoutV2,
|
||||||
namespace: []const u8,
|
namespace: []const u8,
|
||||||
output: *Output,
|
output: *Output,
|
||||||
|
|
||||||
pub fn create(client: *wl.Client, version: u32, id: u32, output: *Output, namespace: []const u8) !void {
|
pub fn create(client: *wl.Client, version: u32, id: u32, output: *Output, namespace: []const u8) !void {
|
||||||
const layout = try river.LayoutV1.create(client, version, id);
|
const layout = try river.LayoutV2.create(client, version, id);
|
||||||
|
|
||||||
if (namespaceInUse(namespace, output, client)) {
|
if (namespaceInUse(namespace, output, client)) {
|
||||||
layout.sendNamespaceInUse();
|
layout.sendNamespaceInUse();
|
||||||
|
@ -91,7 +91,7 @@ fn namespaceInUse(namespace: []const u8, output: *Output, client: *wl.Client) bo
|
||||||
|
|
||||||
/// This exists to handle layouts that have been rendered inert (due to the
|
/// This exists to handle layouts that have been rendered inert (due to the
|
||||||
/// namespace already being in use) until the client destroys them.
|
/// namespace already being in use) until the client destroys them.
|
||||||
fn handleRequestInert(layout: *river.LayoutV1, request: river.LayoutV1.Request, _: ?*c_void) void {
|
fn handleRequestInert(layout: *river.LayoutV2, request: river.LayoutV2.Request, _: ?*c_void) void {
|
||||||
if (request == .destroy) layout.destroy();
|
if (request == .destroy) layout.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,14 +128,10 @@ pub fn startLayoutDemand(self: *Self, views: u32) void {
|
||||||
self.output.root.trackLayoutDemands();
|
self.output.root.trackLayoutDemands();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handleRequest(layout: *river.LayoutV1, request: river.LayoutV1.Request, self: *Self) void {
|
fn handleRequest(layout: *river.LayoutV2, request: river.LayoutV2.Request, self: *Self) void {
|
||||||
switch (request) {
|
switch (request) {
|
||||||
.destroy => layout.destroy(),
|
.destroy => layout.destroy(),
|
||||||
|
|
||||||
// Parameters of the layout changed. We only care about this, if the
|
|
||||||
// layout is currently in use, in which case we rearrange the output.
|
|
||||||
.parameters_changed => if (self == self.output.pending.layout) self.output.arrangeViews(),
|
|
||||||
|
|
||||||
// We receive this event when the client wants to push a view dimension proposal
|
// We receive this event when the client wants to push a view dimension proposal
|
||||||
// to the layout demand matching the serial.
|
// to the layout demand matching the serial.
|
||||||
.push_view_dimensions => |req| {
|
.push_view_dimensions => |req| {
|
||||||
|
@ -171,7 +167,7 @@ fn handleRequest(layout: *river.LayoutV1, request: river.LayoutV1.Request, self:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handleDestroy(layout: *river.LayoutV1, self: *Self) void {
|
fn handleDestroy(layout: *river.LayoutV2, self: *Self) void {
|
||||||
log.debug(
|
log.debug(
|
||||||
"destroying layout '{}' on output '{}'",
|
"destroying layout '{}' on output '{}'",
|
||||||
.{ self.namespace, self.output.wlr_output.name },
|
.{ self.namespace, self.output.wlr_output.name },
|
||||||
|
|
|
@ -21,7 +21,6 @@ const std = @import("std");
|
||||||
const wlr = @import("wlroots");
|
const wlr = @import("wlroots");
|
||||||
const wayland = @import("wayland");
|
const wayland = @import("wayland");
|
||||||
const wl = wayland.server.wl;
|
const wl = wayland.server.wl;
|
||||||
const zriver = wayland.server.zriver;
|
|
||||||
|
|
||||||
const util = @import("util.zig");
|
const util = @import("util.zig");
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ server_destroy: wl.Listener(*wl.Server) = wl.Listener(*wl.Server).init(handleSer
|
||||||
|
|
||||||
pub fn init(self: *Self, server: *Server) !void {
|
pub fn init(self: *Self, server: *Server) !void {
|
||||||
self.* = .{
|
self.* = .{
|
||||||
.global = try wl.Global.create(server.wl_server, river.LayoutManagerV1, 1, *Self, self, bind),
|
.global = try wl.Global.create(server.wl_server, river.LayoutManagerV2, 1, *Self, self, bind),
|
||||||
};
|
};
|
||||||
|
|
||||||
server.wl_server.addDestroyListener(&self.server_destroy);
|
server.wl_server.addDestroyListener(&self.server_destroy);
|
||||||
|
@ -49,7 +49,7 @@ fn handleServerDestroy(listener: *wl.Listener(*wl.Server), wl_server: *wl.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bind(client: *wl.Client, self: *Self, version: u32, id: u32) callconv(.C) void {
|
fn bind(client: *wl.Client, self: *Self, version: u32, id: u32) callconv(.C) void {
|
||||||
const layout_manager = river.LayoutManagerV1.create(client, 1, id) catch {
|
const layout_manager = river.LayoutManagerV2.create(client, 1, id) catch {
|
||||||
client.postNoMemory();
|
client.postNoMemory();
|
||||||
log.crit("out of memory", .{});
|
log.crit("out of memory", .{});
|
||||||
return;
|
return;
|
||||||
|
@ -57,7 +57,7 @@ fn bind(client: *wl.Client, self: *Self, version: u32, id: u32) callconv(.C) voi
|
||||||
layout_manager.setHandler(*Self, handleRequest, null, self);
|
layout_manager.setHandler(*Self, handleRequest, null, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handleRequest(layout_manager: *river.LayoutManagerV1, request: river.LayoutManagerV1.Request, self: *Self) void {
|
fn handleRequest(layout_manager: *river.LayoutManagerV2, request: river.LayoutManagerV2.Request, self: *Self) void {
|
||||||
switch (request) {
|
switch (request) {
|
||||||
.destroy => layout_manager.destroy(),
|
.destroy => layout_manager.destroy(),
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,8 @@ drag_icons: std.SinglyLinkedList(DragIcon) = .{},
|
||||||
xwayland_unmanaged_views: if (build_options.xwayland)
|
xwayland_unmanaged_views: if (build_options.xwayland)
|
||||||
std.TailQueue(XwaylandUnmanaged)
|
std.TailQueue(XwaylandUnmanaged)
|
||||||
else
|
else
|
||||||
void = if (build_options.xwayland) .{},
|
void = if (build_options.xwayland)
|
||||||
|
.{},
|
||||||
|
|
||||||
/// Number of layout demands pending before the transaction may be started.
|
/// Number of layout demands pending before the transaction may be started.
|
||||||
pending_layout_demands: u32 = 0,
|
pending_layout_demands: u32 = 0,
|
||||||
|
|
|
@ -59,12 +59,14 @@ const str_to_impl_fn = [_]struct {
|
||||||
.{ .name = "focus-view", .impl = @import("command/focus_view.zig").focusView },
|
.{ .name = "focus-view", .impl = @import("command/focus_view.zig").focusView },
|
||||||
.{ .name = "map", .impl = @import("command/map.zig").map },
|
.{ .name = "map", .impl = @import("command/map.zig").map },
|
||||||
.{ .name = "map-pointer", .impl = @import("command/map.zig").mapPointer },
|
.{ .name = "map-pointer", .impl = @import("command/map.zig").mapPointer },
|
||||||
|
.{ .name = "mod-layout-value", .impl = @import("command/layout.zig").modLayoutValue },
|
||||||
.{ .name = "move", .impl = @import("command/move.zig").move },
|
.{ .name = "move", .impl = @import("command/move.zig").move },
|
||||||
.{ .name = "opacity", .impl = @import("command/opacity.zig").opacity },
|
.{ .name = "opacity", .impl = @import("command/opacity.zig").opacity },
|
||||||
.{ .name = "output-layout", .impl = @import("command/layout.zig").outputLayout },
|
.{ .name = "output-layout", .impl = @import("command/layout.zig").outputLayout },
|
||||||
.{ .name = "resize", .impl = @import("command/move.zig").resize },
|
.{ .name = "resize", .impl = @import("command/move.zig").resize },
|
||||||
.{ .name = "send-to-output", .impl = @import("command/send_to_output.zig").sendToOutput },
|
.{ .name = "send-to-output", .impl = @import("command/send_to_output.zig").sendToOutput },
|
||||||
.{ .name = "set-focused-tags", .impl = @import("command/tags.zig").setFocusedTags },
|
.{ .name = "set-focused-tags", .impl = @import("command/tags.zig").setFocusedTags },
|
||||||
|
.{ .name = "set-layout-value", .impl = @import("command/layout.zig").setLayoutValue },
|
||||||
.{ .name = "set-repeat", .impl = @import("command/set_repeat.zig").setRepeat },
|
.{ .name = "set-repeat", .impl = @import("command/set_repeat.zig").setRepeat },
|
||||||
.{ .name = "set-view-tags", .impl = @import("command/tags.zig").setViewTags },
|
.{ .name = "set-view-tags", .impl = @import("command/tags.zig").setViewTags },
|
||||||
.{ .name = "snap", .impl = @import("command/move.zig").snap },
|
.{ .name = "snap", .impl = @import("command/move.zig").snap },
|
||||||
|
@ -92,6 +94,7 @@ pub const Error = error{
|
||||||
InvalidDirection,
|
InvalidDirection,
|
||||||
InvalidPhysicalDirection,
|
InvalidPhysicalDirection,
|
||||||
InvalidOrientation,
|
InvalidOrientation,
|
||||||
|
InvalidType,
|
||||||
InvalidRgba,
|
InvalidRgba,
|
||||||
InvalidValue,
|
InvalidValue,
|
||||||
UnknownOption,
|
UnknownOption,
|
||||||
|
@ -135,6 +138,7 @@ pub fn errToMsg(err: Error) [:0]const u8 {
|
||||||
Error.InvalidDirection => "invalid direction. Must be 'next' or 'previous'",
|
Error.InvalidDirection => "invalid direction. Must be 'next' or 'previous'",
|
||||||
Error.InvalidPhysicalDirection => "invalid direction. Must be 'up', 'down', 'left' or 'right'",
|
Error.InvalidPhysicalDirection => "invalid direction. Must be 'up', 'down', 'left' or 'right'",
|
||||||
Error.InvalidOrientation => "invalid orientation. Must be 'horizontal', or 'vertical'",
|
Error.InvalidOrientation => "invalid orientation. Must be 'horizontal', or 'vertical'",
|
||||||
|
Error.InvalidType => "invalid type",
|
||||||
Error.InvalidRgba => "invalid color format, must be #RRGGBB or #RRGGBBAA",
|
Error.InvalidRgba => "invalid color format, must be #RRGGBB or #RRGGBBAA",
|
||||||
Error.InvalidValue => "invalid value",
|
Error.InvalidValue => "invalid value",
|
||||||
Error.OutOfMemory => "out of memory",
|
Error.OutOfMemory => "out of memory",
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const mem = std.mem;
|
||||||
|
const wl = @import("wayland").server.wl;
|
||||||
const util = @import("../util.zig");
|
const util = @import("../util.zig");
|
||||||
|
|
||||||
const Error = @import("../command.zig").Error;
|
const Error = @import("../command.zig").Error;
|
||||||
|
@ -52,3 +54,98 @@ pub fn defaultLayout(
|
||||||
if (output.layout_namespace == null) output.handleLayoutNamespaceChange();
|
if (output.layout_namespace == null) output.handleLayoutNamespaceChange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SetType = enum {
|
||||||
|
int,
|
||||||
|
fixed,
|
||||||
|
string,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// riverctl set-layout-value rivertile int main_count 42
|
||||||
|
/// riverctl set-layout-value rivertile fixed main_factor 42.0
|
||||||
|
/// riverctl set-layout-value rivertile string main_location top
|
||||||
|
pub fn setLayoutValue(
|
||||||
|
allocator: *std.mem.Allocator,
|
||||||
|
seat: *Seat,
|
||||||
|
args: []const []const u8,
|
||||||
|
out: *?[]const u8,
|
||||||
|
) Error!void {
|
||||||
|
if (args.len < 5) return Error.NotEnoughArguments;
|
||||||
|
if (args.len > 5) return Error.TooManyArguments;
|
||||||
|
|
||||||
|
const target_namespace = args[1];
|
||||||
|
const kind = std.meta.stringToEnum(SetType, args[2]) orelse return Error.InvalidType;
|
||||||
|
|
||||||
|
const output = seat.focused_output;
|
||||||
|
|
||||||
|
var it = output.layouts.first;
|
||||||
|
const layout = while (it) |node| : (it = node.next) {
|
||||||
|
const layout = &node.data;
|
||||||
|
if (mem.eql(u8, layout.namespace, target_namespace)) break layout;
|
||||||
|
} else return;
|
||||||
|
|
||||||
|
const null_terminated_name = try util.gpa.dupeZ(u8, args[3]);
|
||||||
|
defer util.gpa.free(null_terminated_name);
|
||||||
|
|
||||||
|
switch (kind) {
|
||||||
|
.int => {
|
||||||
|
const value = try std.fmt.parseInt(i32, args[4], 10);
|
||||||
|
layout.layout.sendSetIntValue(null_terminated_name, value);
|
||||||
|
},
|
||||||
|
.fixed => {
|
||||||
|
const value = try std.fmt.parseFloat(f64, args[4]);
|
||||||
|
layout.layout.sendSetFixedValue(null_terminated_name, wl.Fixed.fromDouble(value));
|
||||||
|
},
|
||||||
|
.string => {
|
||||||
|
const null_terminated_value = try util.gpa.dupeZ(u8, args[4]);
|
||||||
|
defer util.gpa.free(null_terminated_value);
|
||||||
|
layout.layout.sendSetStringValue(null_terminated_name, null_terminated_value);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
output.arrangeViews();
|
||||||
|
}
|
||||||
|
|
||||||
|
const ModType = enum {
|
||||||
|
int,
|
||||||
|
fixed,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// riverctl mode-layout-value rivertile int main_count 42
|
||||||
|
/// riverctl set-layout-value rivertile fixed main_factor 42.0
|
||||||
|
pub fn modLayoutValue(
|
||||||
|
allocator: *std.mem.Allocator,
|
||||||
|
seat: *Seat,
|
||||||
|
args: []const []const u8,
|
||||||
|
out: *?[]const u8,
|
||||||
|
) Error!void {
|
||||||
|
if (args.len < 5) return Error.NotEnoughArguments;
|
||||||
|
if (args.len > 5) return Error.TooManyArguments;
|
||||||
|
|
||||||
|
const target_namespace = args[1];
|
||||||
|
const kind = std.meta.stringToEnum(ModType, args[2]) orelse return Error.InvalidType;
|
||||||
|
|
||||||
|
const output = seat.focused_output;
|
||||||
|
|
||||||
|
var it = output.layouts.first;
|
||||||
|
const layout = while (it) |node| : (it = node.next) {
|
||||||
|
const layout = &node.data;
|
||||||
|
if (mem.eql(u8, layout.namespace, target_namespace)) break layout;
|
||||||
|
} else return;
|
||||||
|
|
||||||
|
const null_terminated_name = try util.gpa.dupeZ(u8, args[3]);
|
||||||
|
defer util.gpa.free(null_terminated_name);
|
||||||
|
|
||||||
|
switch (kind) {
|
||||||
|
.int => {
|
||||||
|
const value = try std.fmt.parseInt(i32, args[4], 10);
|
||||||
|
layout.layout.sendModIntValue(null_terminated_name, value);
|
||||||
|
},
|
||||||
|
.fixed => {
|
||||||
|
const value = try std.fmt.parseFloat(f64, args[4]);
|
||||||
|
layout.layout.sendModFixedValue(null_terminated_name, wl.Fixed.fromDouble(value));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
output.arrangeViews();
|
||||||
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
|
const math = std.math;
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
|
|
||||||
const wayland = @import("wayland");
|
const wayland = @import("wayland");
|
||||||
|
@ -51,9 +52,7 @@ const Location = enum {
|
||||||
left,
|
left,
|
||||||
};
|
};
|
||||||
|
|
||||||
const default_main_location: Location = .left;
|
// TODO: expose these as command line options
|
||||||
const default_main_count = 1;
|
|
||||||
const default_main_factor = 0.6;
|
|
||||||
const default_view_padding = 6;
|
const default_view_padding = 6;
|
||||||
const default_outer_padding = 6;
|
const default_outer_padding = 6;
|
||||||
|
|
||||||
|
@ -62,7 +61,7 @@ const gpa = std.heap.c_allocator;
|
||||||
|
|
||||||
const Context = struct {
|
const Context = struct {
|
||||||
initialized: bool = false,
|
initialized: bool = false,
|
||||||
layout_manager: ?*river.LayoutManagerV1 = null,
|
layout_manager: ?*river.LayoutManagerV2 = null,
|
||||||
outputs: std.TailQueue(Output) = .{},
|
outputs: std.TailQueue(Output) = .{},
|
||||||
|
|
||||||
fn addOutput(context: *Context, registry: *wl.Registry, name: u32) !void {
|
fn addOutput(context: *Context, registry: *wl.Registry, name: u32) !void {
|
||||||
|
@ -79,7 +78,11 @@ const Output = struct {
|
||||||
wl_output: *wl.Output,
|
wl_output: *wl.Output,
|
||||||
name: u32,
|
name: u32,
|
||||||
|
|
||||||
layout: *river.LayoutV1 = undefined,
|
main_location: Location = .left,
|
||||||
|
main_count: u32 = 1,
|
||||||
|
main_factor: f64 = 0.6,
|
||||||
|
|
||||||
|
layout: *river.LayoutV2 = undefined,
|
||||||
|
|
||||||
fn init(output: *Output, context: *Context, wl_output: *wl.Output, name: u32) !void {
|
fn init(output: *Output, context: *Context, wl_output: *wl.Output, name: u32) !void {
|
||||||
output.* = .{ .wl_output = wl_output, .name = name };
|
output.* = .{ .wl_output = wl_output, .name = name };
|
||||||
|
@ -97,21 +100,53 @@ const Output = struct {
|
||||||
output.layout.destroy();
|
output.layout.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layoutListener(layout: *river.LayoutV1, event: river.LayoutV1.Event, output: *Output) void {
|
fn layoutListener(layout: *river.LayoutV2, event: river.LayoutV2.Event, output: *Output) void {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
.namespace_in_use => fatal("namespace 'rivertile' already in use.", .{}),
|
.namespace_in_use => fatal("namespace 'rivertile' already in use.", .{}),
|
||||||
|
|
||||||
|
.set_int_value => |ev| {
|
||||||
|
if (mem.eql(u8, mem.span(ev.name), "main_count")) {
|
||||||
|
if (ev.value > 0) output.main_count = @intCast(u32, ev.value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.mod_int_value => |ev| {
|
||||||
|
if (mem.eql(u8, mem.span(ev.name), "main_count")) {
|
||||||
|
const result = @as(i33, output.main_count) + ev.delta;
|
||||||
|
if (result > 0) output.main_count = @intCast(u32, result);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
.set_fixed_value => |ev| {
|
||||||
|
if (mem.eql(u8, mem.span(ev.name), "main_factor")) {
|
||||||
|
output.main_factor = math.clamp(ev.value.toDouble(), 0.1, 0.9);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.mod_fixed_value => |ev| {
|
||||||
|
if (mem.eql(u8, mem.span(ev.name), "main_factor")) {
|
||||||
|
const new_value = ev.delta.toDouble() + output.main_factor;
|
||||||
|
output.main_factor = math.clamp(new_value, 0.1, 0.9);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
.set_string_value => |ev| {
|
||||||
|
if (mem.eql(u8, mem.span(ev.name), "main_location")) {
|
||||||
|
if (std.meta.stringToEnum(Location, mem.span(ev.value))) |new_location| {
|
||||||
|
output.main_location = new_location;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
.layout_demand => |ev| {
|
.layout_demand => |ev| {
|
||||||
const secondary_count = if (ev.view_count > default_main_count)
|
const secondary_count = if (ev.view_count > output.main_count)
|
||||||
ev.view_count - default_main_count
|
ev.view_count - output.main_count
|
||||||
else
|
else
|
||||||
0;
|
0;
|
||||||
|
|
||||||
const usable_width = switch (default_main_location) {
|
const usable_width = switch (output.main_location) {
|
||||||
.left, .right => ev.usable_width - 2 * default_outer_padding,
|
.left, .right => ev.usable_width - 2 * default_outer_padding,
|
||||||
.top, .bottom => ev.usable_height - 2 * default_outer_padding,
|
.top, .bottom => ev.usable_height - 2 * default_outer_padding,
|
||||||
};
|
};
|
||||||
const usable_height = switch (default_main_location) {
|
const usable_height = switch (output.main_location) {
|
||||||
.left, .right => ev.usable_height - 2 * default_outer_padding,
|
.left, .right => ev.usable_height - 2 * default_outer_padding,
|
||||||
.top, .bottom => ev.usable_width - 2 * default_outer_padding,
|
.top, .bottom => ev.usable_width - 2 * default_outer_padding,
|
||||||
};
|
};
|
||||||
|
@ -126,18 +161,18 @@ const Output = struct {
|
||||||
var secondary_height: u32 = undefined;
|
var secondary_height: u32 = undefined;
|
||||||
var secondary_height_rem: u32 = undefined;
|
var secondary_height_rem: u32 = undefined;
|
||||||
|
|
||||||
if (default_main_count > 0 and secondary_count > 0) {
|
if (output.main_count > 0 and secondary_count > 0) {
|
||||||
main_width = @floatToInt(u32, default_main_factor * @intToFloat(f64, usable_width));
|
main_width = @floatToInt(u32, output.main_factor * @intToFloat(f64, usable_width));
|
||||||
main_height = usable_height / default_main_count;
|
main_height = usable_height / output.main_count;
|
||||||
main_height_rem = usable_height % default_main_count;
|
main_height_rem = usable_height % output.main_count;
|
||||||
|
|
||||||
secondary_width = usable_width - main_width;
|
secondary_width = usable_width - main_width;
|
||||||
secondary_height = usable_height / secondary_count;
|
secondary_height = usable_height / secondary_count;
|
||||||
secondary_height_rem = usable_height % secondary_count;
|
secondary_height_rem = usable_height % secondary_count;
|
||||||
} else if (default_main_count > 0) {
|
} else if (output.main_count > 0) {
|
||||||
main_width = usable_width;
|
main_width = usable_width;
|
||||||
main_height = usable_height / default_main_count;
|
main_height = usable_height / output.main_count;
|
||||||
main_height_rem = usable_height % default_main_count;
|
main_height_rem = usable_height % output.main_count;
|
||||||
} else if (secondary_width > 0) {
|
} else if (secondary_width > 0) {
|
||||||
main_width = 0;
|
main_width = 0;
|
||||||
secondary_width = usable_width;
|
secondary_width = usable_width;
|
||||||
|
@ -152,17 +187,17 @@ const Output = struct {
|
||||||
var width: u32 = undefined;
|
var width: u32 = undefined;
|
||||||
var height: u32 = undefined;
|
var height: u32 = undefined;
|
||||||
|
|
||||||
if (i < default_main_count) {
|
if (i < output.main_count) {
|
||||||
x = 0;
|
x = 0;
|
||||||
y = @intCast(i32, (i * main_height) + if (i > 0) main_height_rem else 0);
|
y = @intCast(i32, (i * main_height) + if (i > 0) main_height_rem else 0);
|
||||||
width = main_width;
|
width = main_width;
|
||||||
height = main_height + if (i == 0) main_height_rem else 0;
|
height = main_height + if (i == 0) main_height_rem else 0;
|
||||||
} else {
|
} else {
|
||||||
x = @intCast(i32, main_width);
|
x = @intCast(i32, main_width);
|
||||||
y = @intCast(i32, (i - default_main_count) * secondary_height +
|
y = @intCast(i32, (i - output.main_count) * secondary_height +
|
||||||
if (i > default_main_count) secondary_height_rem else 0);
|
if (i > output.main_count) secondary_height_rem else 0);
|
||||||
width = secondary_width;
|
width = secondary_width;
|
||||||
height = secondary_height + if (i == default_main_count) secondary_height_rem else 0;
|
height = secondary_height + if (i == output.main_count) secondary_height_rem else 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
x += @intCast(i32, default_view_padding);
|
x += @intCast(i32, default_view_padding);
|
||||||
|
@ -170,7 +205,7 @@ const Output = struct {
|
||||||
width -= 2 * default_view_padding;
|
width -= 2 * default_view_padding;
|
||||||
height -= 2 * default_view_padding;
|
height -= 2 * default_view_padding;
|
||||||
|
|
||||||
switch (default_main_location) {
|
switch (output.main_location) {
|
||||||
.left => layout.pushViewDimensions(
|
.left => layout.pushViewDimensions(
|
||||||
ev.serial,
|
ev.serial,
|
||||||
x + @intCast(i32, default_outer_padding),
|
x + @intCast(i32, default_outer_padding),
|
||||||
|
@ -242,8 +277,8 @@ pub fn main() !void {
|
||||||
fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, context: *Context) void {
|
fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, context: *Context) void {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
.global => |global| {
|
.global => |global| {
|
||||||
if (std.cstr.cmp(global.interface, river.LayoutManagerV1.getInterface().name) == 0) {
|
if (std.cstr.cmp(global.interface, river.LayoutManagerV2.getInterface().name) == 0) {
|
||||||
context.layout_manager = registry.bind(global.name, river.LayoutManagerV1, 1) catch return;
|
context.layout_manager = registry.bind(global.name, river.LayoutManagerV2, 1) catch return;
|
||||||
} else if (std.cstr.cmp(global.interface, wl.Output.getInterface().name) == 0) {
|
} else if (std.cstr.cmp(global.interface, wl.Output.getInterface().name) == 0) {
|
||||||
context.addOutput(registry, global.name) catch |err| fatal("failed to bind output: {}", .{err});
|
context.addOutput(registry, global.name) catch |err| fatal("failed to bind output: {}", .{err});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue