Merge branch 'master' into menu-accelerators

This commit is contained in:
Alex Chernika 2026-05-08 00:20:57 +02:00
commit 634a3464bd
No known key found for this signature in database
GPG key ID: 6029FAD8ABFB076A
12 changed files with 321 additions and 69 deletions

74
NEWS.md
View file

@ -264,9 +264,9 @@ Note to package maintainers: This release requires wayland version >=1.22.90
- Add theme option window.button.hover.bg.color [#3365] @johanmalm
- Implement scrollable window-switcher OSD [#3291] @tokyo4j
- Support the `NextWindow` options listed below [#3271] @tokyo4j
- `<action name="NextWindow" workspace="current|all"/>`
- `<action name="NextWindow" output="all|focused|cursor"/>`
- `<action name="NextWindow" identifier="all|current"/>`
- `<action name="NextWindow" workspace="current|all" />`
- `<action name="NextWindow" output="all|focused|cursor" />`
- `<action name="NextWindow" identifier="all|current" />`
- Add config option `*<desktops><initial>` for setting the active workspace on
startup. [#3265] @5trixs0f
@ -314,7 +314,7 @@ Note to package maintainers: This release requires wayland version >=1.22.90
and get keyboard focus so that they can be operated with the keyboard.
An example use-case is the xfce4-panel applications-menu being opened by
the command xfce4-popup-applicationsmenu. [#3165] @johanmalm
- On popup destory, return focus to whoever had it before the popop [#3165]
- On popup destroy, return focus to whoever had it before the popop [#3165]
@johanmalm @tokyo4j
- Unshade window if selected from client-list-combined-menu [#3345] @Amodio
- Show non-dialog child windows in window-switcher [#3339] @tokyo4j
@ -348,7 +348,7 @@ A big thank you to all involved in this release.
### Added
- Add `<windowSwitcher order="focus|age"/>` to optionally order windows by age
- Add `<windowSwitcher order="focus|age" />` to optionally order windows by age
rather than most recent focus. @mbroemme [#3229]
- Replace `<snapping><range>` with `<snapping><range inner="" outer="">` to
provide more granular control when configuring the size of snapping areas
@ -379,7 +379,7 @@ A big thank you to all involved in this release.
[#3134]
- labnag: add --keyboard-focus option @tokyo4j [#3120]
- Allow window switcher to temporarily unshade windows using config option
`<windowSwitcher unshade="yes|no"/>` @Amodio @Consolatis [#3124]
`<windowSwitcher unshade="yes|no" />` @Amodio @Consolatis [#3124]
- For the 'classic' style window-switcher, add the following theme options:
- `osd.window-switcher.style-classic.item.active.border.color`
- `osd.window-switcher.style-classic.item.active.bg.color`
@ -409,7 +409,7 @@ A big thank you to all involved in this release.
client surface. Fixes a regression in 885919f. @tokyo4j [#3211]
- Set all foreign-toplevel initial states correctly. This is not believed to fix
any particular user-issue, but just feels safer. @jlindgren90 [#3217]
- Update layer-shell client top layer visiblity on unmap instead of destroy
- Update layer-shell client top layer visibility on unmap instead of destroy
because it is possible for fullscreen xwayland windows to be unmapped without
being destroyed, and in this case the top layer visibility needs to be updated
to unhide other layer-shell clients like panels. @jlindgren90 [#3199]
@ -421,7 +421,7 @@ A big thank you to all involved in this release.
@elviosak [#3146] [#3168]
- Work around client-side rounding issues at right/bottom pixel. This fixes an
issue with some clients (notably Qt ones) where cursor coordinates in the
rightmost or bottom fixel are incorrectly rounded up putting them outside the
rightmost or bottom pixel are incorrectly rounded up putting them outside the
surface bounds. The issue has been particularly noticeable with layer-shell
clients like lxqt-panel. @jlindgren90 [#3157] [#2379] [#3099]
Note: This also avoids a similar server-side rounding issue with some
@ -469,7 +469,7 @@ A big thank you to all involved in this release.
when a window is using fullscreen mode. @johanmalm [#3158]
- Call labnag with on-demand keyboard interactivity by default @tokyo4j [#3120]
- Temporarily unshade windows when switching windows. Restore old behaviour with
`<windowSwitcher unshade="no"/>` @Amodio @Consolatis [#3124]
`<windowSwitcher unshade="no" />` @Amodio @Consolatis [#3124]
- In the classic style window-switcher, the default color of the selected window
item has been changed to inherit the border color but with 15% opacity
@tokyo4j [#3118]
@ -488,7 +488,7 @@ A big thank you to all involved in this release.
`<windowSwitcher style="thumbnail">`. @tokyo4j [#2981]
- Add `toggle` option to `GoToDesktop` action. This has the effect of going back
to the last desktop if already on the target. @RainerKuemmerle [#3024]
- Add `<theme maximizedDecoration="titlebar|none"/>` to allow hiding titlebar
- Add `<theme maximizedDecoration="titlebar|none" />` to allow hiding titlebar
when window is maximized. @CosmicFusion @tokyo4j [#3015]
- Use client-send-to-menu as 'Workspace' submenu in built-in client-menu
@johanmalm [#2995]
@ -539,7 +539,7 @@ A big thank you to all involved in this release.
- Change default keybind `W-<arrow>` to combine cardinal directions to support
resizing of windows to fill a quarter of an output. This only affects users
who do not use an `rc.xml` (thereby using default keybinds) or use the
`<keyboard><default/>` option. Previous behavior can be restored by setting
`<keyboard><default />` option. Previous behavior can be restored by setting
`combine="no"` as shown below. [#3081] @tokyo4j
```
@ -638,7 +638,7 @@ window.*.title.bg.colorTo.splitTo:
window rule to enable this. @Consolatis @tokyo4j [#2840]
- Add config option `<core><primarySelection>`. This enables autoscroll
(middle-click to scroll up/down) in Chromium and electron based clients
without inadvertantly pasting the primary clipboard. @johanmalm [#2832]
without inadvertently pasting the primary clipboard. @johanmalm [#2832]
- Bump `xdg_shell` version from 3 to 6 @tokyo4j [#2814]
- Bump `wl_compositor` version from 5 to 6 @tokyo4j [#2812]
- Support tablet tool mouse buttons @jp7677 [#2778]
@ -715,7 +715,7 @@ window.*.title.bg.colorTo.splitTo:
agnostic on choice of launcher.
- `A-<arrow>` for `MoveToEdge` because `Alt-` keybinds should be for clients
to use and this one results in frequent user complaints because it prevents
some common usage patterns like alt-left/right in web browers.
some common usage patterns like alt-left/right in web browsers.
- Change default titlebar menu button from a dot to an arrow @johanmalm [#2844]
- When `dragLock` is set to `yes`, the drag no longer expires after a short
delay (known as `Sticky` mode) as recommended by libinput [#2803]. The timeout
@ -750,7 +750,7 @@ release.
- Localize desktop-entry application names used by the window switcher via
`desktop_entry_name` or the `%n` specifier @tokyo4j [#2653]
- Add `HideCursor` action @jp7677 [#2633]
- Support application icons in window-switcher using `<field content="icon"/>`
- Support application icons in window-switcher using `<field content="icon" />`
and use this by default. @tokyo4j [#2621]
- Support application icons in client-list-combined-menu @tokyo4j [#2617]
- Support the use of the keypad-enter key when using menu. @zeusgoose [#2610]
@ -887,7 +887,7 @@ Notes to package maintainers:
closing a popup did not move the pointer focus to the main toplevel until the
cursor was moved. [#2443]
- Improve algorithm for menu placement with xdg-positioner [#2408]
- Do not forward IME key-release without correspinding key-press to avoid stuck
- Do not forward IME key-release without corresponding key-press to avoid stuck
keys [#2437]
### Changed
@ -949,7 +949,7 @@ Notes to package maintainers:
```xml
<windowRules>
<windowRule identifier="blender" wantAbsorbedModifierReleaseEvents="yes"/>
<windowRule identifier="blender" wantAbsorbedModifierReleaseEvents="yes" />
</windowRules>
```
@ -966,8 +966,8 @@ menu.border.color: #aaaaaa
```xml
<windowSwitcher>
<fields>
<field content="custom" format="%n" width="25%"/>
<field content="title" width="75%"/>
<field content="custom" format="%n" width="25%" />
<field content="title" width="75%" />
</fields>
</windowSwitcher>
```
@ -1097,7 +1097,7 @@ Notes to package maintainers:
- Support the openbox style menus listed below. Written-by: @droc12345
1. `client-list-combined-menu` shows windows across all workspaces. This can
be used with a mouse/key bind using:
`<action name="ShowMenu" menu="client-list-combined-menu"/>` [#2101]
`<action name="ShowMenu" menu="client-list-combined-menu" />` [#2101]
2. `client-send-to` shows all workspaces that the current window can be sent
to. This can additional be used within a client menu using:
`<menu id="client-send-to-menu" label="Send to Workspace..." />` [#2152]
@ -1256,14 +1256,14 @@ have been attributed with a 'Written-by' against each relevant log entry.
```xml
<placement>
<policy>cascade</policy>
<cascadeOffset x="40" y="30"/>
<cascadeOffset x="40" y="30" />
</placement>
```
- Support relative tablet motion. Written-by: @jp7677 [#1962]
```xml
<tabletTool motion="absolute|relative" relativeMotionSensitivity="1.0"/>
<tabletTool motion="absolute|relative" relativeMotionSensitivity="1.0" />
```
### Fixed
@ -1347,7 +1347,7 @@ joint effort by @spl237 and @Consolatis.
- Respect `menu.overlap.x` when using pipemenus. [#1940]
- Do not try to restore windows to very small width/height on unmaximize.
This fixes a bug with Thonny (Python IDE made with Tk). [#1938]
- Conditially set squared server-side decoration (SSD) corners when a view is
- Conditionally set squared server-side decoration (SSD) corners when a view is
tiled. Written-by: @jp7677 [#1926]
- Remember initial direction when starting window-cycling with `PreviousView`.
Also make the toggling of direction when shift is pressed relative to the
@ -1371,7 +1371,7 @@ joint effort by @spl237 and @Consolatis.
Chromium and Steam. [#1861]
- Session-lock: fix flashing & update cursor shape. [#1858]
- Remove tearing-controller listeners on destroy. [#1853]
- Handle invalid `ForEach` and `If` action cofigs. [#1838]
- Handle invalid `ForEach` and `If` action configs. [#1838]
- Delay startup of applications until event loop is ready. This avoids race
conditions when using autostart scripts that trigger a labwc SIGHUP. [#1588]
- With `SendToDesktop` action follow=no option, ensure the topmost window is
@ -1388,7 +1388,7 @@ joint effort by @spl237 and @Consolatis.
- Remove subprojects/seatd.wrap as no longer needed
- Action `MoveToCursor` is deprecated in favour of:
`<action name="AutoPlace" policy="cursor"/>`.
`<action name="AutoPlace" policy="cursor" />`.
## 0.7.2 - 2024-05-10
@ -1405,7 +1405,7 @@ contributions from others as noted in the log.
### Added
- Add `<menu><ignoreButtonReleasePeriod>` to prevent clicks with small movements
from inadvertantly closing a menu or selecting a menu item. This is the
from inadvertently closing a menu or selecting a menu item. This is the
equivalent of `<menu><hideDelay>` on Openbox. [#1760]
- Support drop-shadows (disabled by default) for windows using server-side
decorations. Written-by: @cillian64
@ -1433,7 +1433,7 @@ window.inactive.shadow.color: #00000040
```xml
<action name="ForEach">
<query identifier="foo"/>
<query identifier="foo" />
<then>
<!-- carry out some action on match -->
</then>
@ -1475,7 +1475,7 @@ osd.window-switcher.width: 75%
<snapping>
<overlay>
<enabled>yes|no</enabled>
<delay inner="500" outer="500"/>
<delay inner="500" outer="500" />
</overlay>
</snapping>
```
@ -1638,7 +1638,7 @@ osd.window-switcher.preview.border.color: #ffffff,#00a2ff,#ffffff
```xml
<keybind key="A-Space">
<action name="ShowMenu" menu="client-menu" atCursor="No"/>
<action name="ShowMenu" menu="client-menu" atCursor="No" />
</keybind>
```
@ -1646,7 +1646,7 @@ osd.window-switcher.preview.border.color: #ffffff,#00a2ff,#ffffff
is already used by the action itself). [#1589]
```xml
<action name="MoveToOutput" output="HDMI-A-1"/>
<action name="MoveToOutput" output="HDMI-A-1" />
```
- Do not deactivate window when giving keyboard focus to a non-view
@ -1711,8 +1711,8 @@ osd.window-switcher.preview.border.color: #ffffff,#00a2ff,#ffffff
Written-by: @jp7677
```xml
<touch mapToOutput=""/>
<touch deviceName="" mapToOutput=""/>
<touch mapToOutput="" />
<touch deviceName="" mapToOutput="" />
```
- Add tablet support including:
@ -1925,7 +1925,7 @@ relating to surface focus and keyboard issues, amongst others.
- Allow referencing the current workspace in actions, for example:
```xml
<action name="SendToDesktop" to="current"/>
<action name="SendToDesktop" to="current" />
```
### Fixed
@ -2081,7 +2081,7 @@ relating to surface focus and keyboard issues, amongst others.
```xml
<windowSwitcher>
<fields>
<field content="identifier" width="25%"/>
<field content="identifier" width="25%" />
</fields>
</windowSwithcer>
```
@ -2148,9 +2148,9 @@ relating to surface focus and keyboard issues, amongst others.
```xml
<windowRules>
<windowRule identifier="some-application">
<action name="Maximize"/>
<action name="Maximize" />
</windowRule>
<windowRule identifier="foo*" serverDecoration="yes|no"/>
<windowRule identifier="foo*" serverDecoration="yes|no" />
</windowRules>
```
@ -2235,7 +2235,7 @@ Unless otherwise stated all contributions are by the core-devs
```xml
<keyboard>
<default/>
<default />
<keybind key="A-Left"><action name="None" /></keybind>
<keybind key="A-Right"><action name="None" /></keybind>
</keyboard>
@ -2499,7 +2499,7 @@ reported, tested and fixed issues. Particular mentions go to @bi4k8,
actions to be de-coupled from buttons. As a result, "Drag" and
"DoubleClick" actions previously defined against "TitleBar" should now
come under the "Title" context, for example:
`<mousebind button="Left" action="Drag"><action name="Move"/></mousebind>`
`<mousebind button="Left" action="Drag"><action name="Move" /></mousebind>`
- Remove default alt-escape keybind for Exit because too many people have
exited the compositor by mistake trying to get out of alt-tab cycling
or similar.

View file

@ -175,6 +175,7 @@ this is for compatibility with Openbox.
<adaptiveSync>no</adaptiveSync>
<allowTearing>no</allowTearing>
<autoEnableOutputs>yes</autoEnableOutputs>
<hdr>no</hdr>
<reuseOutputMode>no</reuseOutputMode>
<xwaylandPersistence>no</xwaylandPersistence>
<primarySelection>yes</primarySelection>
@ -240,6 +241,11 @@ this is for compatibility with Openbox.
'pkill kanshi ; wlopm --off \*' resume 'kanshi & wlopm --on \*'
```
*<core><hdr>* [yes|no]
Automatically enable HDR support on outputs when configuring them,
where supported by the particular output device and display. Default
is no.
*<core><reuseOutputMode>* [yes|no]
Try to re-use the existing output mode (resolution / refresh rate).
This may prevent unnecessary screenblank delays when starting labwc

View file

@ -13,6 +13,7 @@
<adaptiveSync>no</adaptiveSync>
<allowTearing>no</allowTearing>
<autoEnableOutputs>yes</autoEnableOutputs>
<hdr>no</hdr>
<reuseOutputMode>no</reuseOutputMode>
<xwaylandPersistence>no</xwaylandPersistence>
<primarySelection>yes</primarySelection>

View file

@ -62,7 +62,11 @@
#define BOUNDED_INT(a) ((a) < INT_MAX && (a) > INT_MIN)
#endif
#define LAB_WLR_VERSION_AT_LEAST(major, minor, micro) \
(WLR_VERSION_NUM >= (((major) << 16) | ((minor) << 8) | (micro)))
#define _LAB_CALC_WLR_VERSION_NUM(major, minor, micro) (((major) << 16) | ((minor) << 8) | (micro))
#define LAB_WLR_VERSION_AT_LEAST(major, minor, micro) ( \
server.wlr_version >= _LAB_CALC_WLR_VERSION_NUM(major, minor, micro))
#define LAB_WLR_VERSION_LOWER(major, minor, micro) (!LAB_WLR_VERSION_AT_LEAST(major, minor, micro))
#endif /* LABWC_MACROS_H */

View file

@ -36,6 +36,12 @@ enum tearing_mode {
LAB_TEARING_FULLSCREEN_FORCED,
};
enum render_bit_depth {
LAB_RENDER_BIT_DEPTH_DEFAULT = 0,
LAB_RENDER_BIT_DEPTH_8,
LAB_RENDER_BIT_DEPTH_10,
};
enum tiling_events_mode {
LAB_TILING_EVENTS_NEVER = 0,
LAB_TILING_EVENTS_REGION = 1 << 0,
@ -74,6 +80,7 @@ struct rcxml {
int gap;
enum adaptive_sync_mode adaptive_sync;
enum tearing_mode allow_tearing;
enum render_bit_depth target_render_depth;
bool auto_enable_outputs;
bool reuse_output_mode;
uint32_t allowed_interfaces;

View file

@ -148,6 +148,8 @@ struct seat {
};
struct server {
uint32_t wlr_version;
struct wl_display *wl_display;
struct wl_event_loop *wl_event_loop; /* Can be used for timer events */

View file

@ -71,6 +71,9 @@ struct wlr_box output_usable_area_in_layout_coords(struct output *output);
void handle_output_power_manager_set_mode(struct wl_listener *listener,
void *data);
void output_enable_adaptive_sync(struct output *output, bool enabled);
void output_enable_hdr(struct output *output, struct wlr_output_state *os,
bool enabled, bool silent);
void output_state_setup_hdr(struct output *output, bool silent);
/**
* Notifies whether a fullscreen view is displayed on the given output.

View file

@ -1080,6 +1080,16 @@ set_tearing_mode(const char *str, enum tearing_mode *variable)
}
}
static void
set_hdr_mode(const char *str, enum render_bit_depth *variable)
{
if (parse_bool(str, -1) == 1) {
*variable = LAB_RENDER_BIT_DEPTH_10;
} else {
*variable = LAB_RENDER_BIT_DEPTH_8;
}
}
/* Returns true if the node's children should also be traversed */
static bool
entry(xmlNode *node, char *nodename, char *content)
@ -1144,6 +1154,8 @@ entry(xmlNode *node, char *nodename, char *content)
set_adaptive_sync_mode(content, &rc.adaptive_sync);
} else if (!strcasecmp(nodename, "allowTearing.core")) {
set_tearing_mode(content, &rc.allow_tearing);
} else if (!strcasecmp(nodename, "Hdr.core")) {
set_hdr_mode(content, &rc.target_render_depth);
} else if (!strcasecmp(nodename, "autoEnableOutputs.core")) {
set_bool(content, &rc.auto_enable_outputs);
} else if (!strcasecmp(nodename, "reuseOutputMode.core")) {
@ -1518,6 +1530,7 @@ rcxml_init(void)
rc.gap = 0;
rc.adaptive_sync = LAB_ADAPTIVE_SYNC_DISABLED;
rc.allow_tearing = LAB_TEARING_DISABLED;
rc.target_render_depth = LAB_RENDER_BIT_DEPTH_DEFAULT;
rc.auto_enable_outputs = true;
rc.reuse_output_mode = false;
rc.allowed_interfaces = UINT32_MAX;

View file

@ -9,6 +9,7 @@
#include "common/mem.h"
#include "common/scene-helpers.h"
#include "config/rcxml.h"
#include "config/types.h"
#include "labwc.h"
#include "node.h"
#include "output.h"
@ -322,6 +323,27 @@ handle_osd_tree_destroy(struct wl_listener *listener, void *data)
free(osd_output);
}
static enum lab_view_criteria
get_view_criteria(struct cycle_filter *filter)
{
enum lab_view_criteria criteria =
LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER
| LAB_VIEW_CRITERIA_NO_DIALOG;
if (filter->workspace == CYCLE_WORKSPACE_CURRENT) {
criteria |= LAB_VIEW_CRITERIA_CURRENT_WORKSPACE;
}
return criteria;
}
static const char *
get_cycle_app_id(struct cycle_filter *filter)
{
if (filter->app_id == CYCLE_APP_ID_CURRENT && server.active_view) {
return server.active_view->app_id;
}
return NULL;
}
static struct wl_list *prev(struct wl_list *elm) { return elm->prev; }
static struct wl_list *next(struct wl_list *elm) { return elm->next; }
@ -331,17 +353,10 @@ cycle_immediate(enum lab_cycle_dir direction, struct cycle_filter filter)
if (wl_list_empty(&server.views)) {
return;
}
enum lab_view_criteria criteria =
LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER
| LAB_VIEW_CRITERIA_NO_DIALOG;
if (filter.workspace == CYCLE_WORKSPACE_CURRENT) {
criteria |= LAB_VIEW_CRITERIA_CURRENT_WORKSPACE;
}
enum lab_view_criteria criteria = get_view_criteria(&filter);
uint64_t cycle_outputs = get_outputs_by_filter(filter.output);
const char *cycle_app_id = NULL;
if (filter.app_id == CYCLE_APP_ID_CURRENT && server.active_view) {
cycle_app_id = server.active_view->app_id;
}
const char *cycle_app_id = get_cycle_app_id(&filter);
struct wl_list *head = &server.views;
struct wl_list *(*iter)(struct wl_list *list);
@ -381,20 +396,9 @@ cycle_immediate(enum lab_cycle_dir direction, struct cycle_filter filter)
static bool
init_cycle(struct cycle_filter filter)
{
enum lab_view_criteria criteria =
LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER
| LAB_VIEW_CRITERIA_NO_DIALOG;
if (filter.workspace == CYCLE_WORKSPACE_CURRENT) {
criteria |= LAB_VIEW_CRITERIA_CURRENT_WORKSPACE;
}
uint64_t cycle_outputs =
get_outputs_by_filter(filter.output);
const char *cycle_app_id = NULL;
if (filter.app_id == CYCLE_APP_ID_CURRENT && server.active_view) {
cycle_app_id = server.active_view->app_id;
}
enum lab_view_criteria criteria = get_view_criteria(&filter);
uint64_t cycle_outputs = get_outputs_by_filter(filter.output);
const char *cycle_app_id = get_cycle_app_id(&filter);
struct view *view;
for_each_view(view, &server.views, criteria) {

View file

@ -4,8 +4,10 @@
#include <pango/pangocairo.h>
#include <signal.h>
#include <unistd.h>
#include <wlr/version.h>
#include "common/fd-util.h"
#include "common/font.h"
#include "common/macros.h"
#include "common/spawn.h"
#include "config/rcxml.h"
#include "config/session.h"
@ -65,12 +67,15 @@ static void
print_version(void)
{
#define FEATURE_ENABLED(feature) (HAVE_##feature ? "+" : "-")
printf("labwc %s (%sxwayland %snls %srsvg %slibsfdo)\n",
printf("labwc %s (%sxwayland %snls %srsvg %slibsfdo) running on wlroots %d.%d.%d\n",
LABWC_VERSION,
FEATURE_ENABLED(XWAYLAND),
FEATURE_ENABLED(NLS),
FEATURE_ENABLED(RSVG),
FEATURE_ENABLED(LIBSFDO)
FEATURE_ENABLED(LIBSFDO),
wlr_version_get_major(),
wlr_version_get_minor(),
wlr_version_get_micro()
);
#undef FEATURE_ENABLED
}
@ -164,6 +169,12 @@ main(int argc, char *argv[])
char *primary_client = NULL;
enum wlr_log_importance verbosity = WLR_ERROR;
server.wlr_version = _LAB_CALC_WLR_VERSION_NUM(
wlr_version_get_major(),
wlr_version_get_minor(),
wlr_version_get_micro()
);
int c;
while (1) {
int index = 0;

View file

@ -24,6 +24,7 @@ output_state_init(struct output *output)
backup_config, output->wlr_output);
wlr_output_head_v1_state_apply(&backup_head->state, &output->pending);
wlr_output_configuration_v1_destroy(backup_config);
}

View file

@ -9,6 +9,7 @@
#define _POSIX_C_SOURCE 200809L
#include "output.h"
#include <assert.h>
#include <drm_fourcc.h>
#include <strings.h>
#include <wlr/backend/wayland.h>
#include <wlr/config.h>
@ -51,6 +52,150 @@
#include <wlr/backend/session.h>
#endif
static uint32_t output_formats_8bit[] = {
/* 32 bpp RGB with 8 bit component width */
DRM_FORMAT_XRGB8888,
DRM_FORMAT_XBGR8888,
DRM_FORMAT_RGBX8888,
DRM_FORMAT_BGRX8888,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_ABGR8888,
DRM_FORMAT_RGBA8888,
DRM_FORMAT_BGRA8888,
/* 24 bpp RGB */
DRM_FORMAT_RGB888,
DRM_FORMAT_BGR888,
};
uint32_t output_formats_10bit[] = {
/* 32 bpp RGB with 10 bit component width */
DRM_FORMAT_XRGB2101010,
DRM_FORMAT_XBGR2101010,
DRM_FORMAT_RGBX1010102,
DRM_FORMAT_BGRX1010102,
DRM_FORMAT_ARGB2101010,
DRM_FORMAT_ABGR2101010,
DRM_FORMAT_RGBA1010102,
DRM_FORMAT_BGRA1010102,
};
static bool
output_set_render_format(struct output *output, uint32_t candidates[], size_t count)
{
for (size_t i = 0; i < count; i++) {
wlr_output_state_set_render_format(&output->pending, candidates[i]);
if (wlr_output_test_state(output->wlr_output, &output->pending)) {
return true;
}
}
return false;
}
static bool
output_format_in_candidates(uint32_t render_format, uint32_t candidates[], size_t count)
{
for (size_t i = 0; i < count; i++) {
if (candidates[i] == render_format) {
return true;
}
}
return false;
}
static enum render_bit_depth
bit_depth_from_format(uint32_t render_format)
{
if (output_format_in_candidates(render_format, output_formats_10bit,
ARRAY_SIZE(output_formats_10bit))) {
return LAB_RENDER_BIT_DEPTH_10;
} else if (output_format_in_candidates(render_format, output_formats_8bit,
ARRAY_SIZE(output_formats_8bit))) {
return LAB_RENDER_BIT_DEPTH_8;
}
return LAB_RENDER_BIT_DEPTH_DEFAULT;
}
static enum render_bit_depth
get_config_render_bit_depth(void)
{
return rc.target_render_depth;
}
static bool
output_supports_hdr(const struct wlr_output *output, const char **unsupported_reason_ptr)
{
const char *unsupported_reason = NULL;
if (!(output->supported_primaries & WLR_COLOR_NAMED_PRIMARIES_BT2020)) {
unsupported_reason = "BT2020 primaries not supported by output";
} else if (!(output->supported_transfer_functions &
WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ)) {
unsupported_reason = "PQ transfer function not supported by output";
} else if (!server.renderer->features.output_color_transform) {
unsupported_reason = "renderer doesn't support output color transforms";
}
if (unsupported_reason_ptr) {
*unsupported_reason_ptr = unsupported_reason;
}
return !unsupported_reason;
}
void
output_state_setup_hdr(struct output *output, bool silent)
{
uint32_t render_format = output->wlr_output->render_format;
const char *unsupported_reason = NULL;
bool hdr_supported = output_supports_hdr(output->wlr_output,
&unsupported_reason);
bool hdr_succeeded = false;
enum render_bit_depth render_bit_depth = get_config_render_bit_depth();
if (render_bit_depth == LAB_RENDER_BIT_DEPTH_DEFAULT) {
render_bit_depth = bit_depth_from_format(render_format);
}
if (!hdr_supported && render_bit_depth == LAB_RENDER_BIT_DEPTH_10) {
if (!silent) {
wlr_log(WLR_INFO, "Cannot enable HDR on output %s: %s",
output->wlr_output->name, unsupported_reason);
}
render_bit_depth = LAB_RENDER_BIT_DEPTH_8;
}
if (render_bit_depth == LAB_RENDER_BIT_DEPTH_10 &&
bit_depth_from_format(render_format) == render_bit_depth) {
/* 10-bit was set successfully before, try to save some
* tests by reusing the format
*/
hdr_succeeded = true;
} else if (render_bit_depth == LAB_RENDER_BIT_DEPTH_10) {
hdr_succeeded = output_set_render_format(output, output_formats_10bit,
ARRAY_SIZE(output_formats_10bit));
if (!hdr_succeeded) {
if (!silent) {
wlr_log(WLR_INFO, "No 10 bit color formats"
" supported, HDR disabled.");
}
if (!output_set_render_format(output, output_formats_8bit,
ARRAY_SIZE(output_formats_8bit))) {
if (!silent) {
wlr_log(WLR_ERROR, "No 8 bit color formats"
" supported either!");
}
}
}
} else {
if (!output_set_render_format(output, output_formats_8bit,
ARRAY_SIZE(output_formats_8bit)) && !silent) {
wlr_log(WLR_ERROR, "No 8 bit color formats supported!");
}
}
output_enable_hdr(output, &output->pending, hdr_succeeded, silent);
}
bool
output_get_tearing_allowance(struct output *output)
{
@ -371,10 +516,7 @@ configure_new_output(struct output *output)
output_enable_adaptive_sync(output, true);
}
output_state_commit(output);
wlr_output_effective_resolution(wlr_output,
&output->usable_area.width, &output->usable_area.height);
output_state_setup_hdr(output, false);
/*
* Wait until wlr_output_layout_add_auto() returns before
@ -384,6 +526,19 @@ configure_new_output(struct output *output)
server.pending_output_layout_change++;
add_output_to_layout(output);
server.pending_output_layout_change--;
/*
* Commit the output this way instead, HDR needs a buffer, and
* this commit must be called after the output is added to the
* layout above.
*/
lab_wlr_scene_output_commit(output->scene_output, &output->pending);
/*
* Collect the effective resolution after the final commit.
*/
wlr_output_effective_resolution(wlr_output,
&output->usable_area.width, &output->usable_area.height);
}
static uint64_t
@ -653,6 +808,7 @@ output_config_apply(struct wlr_output_configuration_v1 *config)
wlr_output_state_set_transform(os, head->state.transform);
output_enable_adaptive_sync(output,
head->state.adaptive_sync_enabled);
output_state_setup_hdr(output, false);
}
if (!output_state_commit(output)) {
/*
@ -666,6 +822,19 @@ output_config_apply(struct wlr_output_configuration_v1 *config)
break;
}
if (output_enabled) {
/*
* The above commit was likely made without an image
* buffer attached. This will break applying HDR color
* transformation, since image descriptions must be
* committed with a buffer attached. Queue the HDR mode
* again if output is enabled, but make it silent,
* since it would have emitted messages already when
* called above.
*/
output_state_setup_hdr(output, true);
}
/*
* Add or remove output from layout only if the commit went
* through. Note that at startup, the output may have already
@ -1136,3 +1305,34 @@ output_set_has_fullscreen_view(struct output *output, bool has_fullscreen_view)
output_enable_adaptive_sync(output, has_fullscreen_view);
output_state_commit(output);
}
void
output_enable_hdr(struct output *output, struct wlr_output_state *os,
bool enabled, bool silent)
{
if (enabled && !output_supports_hdr(output->wlr_output, NULL)) {
enabled = false;
}
if (!enabled) {
if (output->wlr_output->supported_primaries != 0 ||
output->wlr_output->supported_transfer_functions != 0) {
if (!silent) {
wlr_log(WLR_DEBUG, "Disabling HDR on output %s",
output->wlr_output->name);
}
wlr_output_state_set_image_description(os, NULL);
}
return;
}
if (!silent) {
wlr_log(WLR_DEBUG, "Enabling HDR on output %s",
output->wlr_output->name);
}
const struct wlr_output_image_description image_desc = {
.primaries = WLR_COLOR_NAMED_PRIMARIES_BT2020,
.transfer_function = WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ,
};
wlr_output_state_set_image_description(os, &image_desc);
}